summaryrefslogtreecommitdiff
path: root/systems/knuth
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
committerNorbert Preining <norbert@preining.info>2019-09-02 13:46:59 +0900
commite0c6872cf40896c7be36b11dcc744620f10adf1d (patch)
tree60335e10d2f4354b0674ec22d7b53f0f8abee672 /systems/knuth
Initial commit
Diffstat (limited to 'systems/knuth')
l---------systems/knuth/dist/cm1
-rw-r--r--systems/knuth/dist/errata/cm85.bug1906
-rw-r--r--systems/knuth/dist/errata/errata.eight610
-rw-r--r--systems/knuth/dist/errata/errata.eleven317
-rw-r--r--systems/knuth/dist/errata/errata.five1108
-rw-r--r--systems/knuth/dist/errata/errata.four1052
-rw-r--r--systems/knuth/dist/errata/errata.nine235
-rw-r--r--systems/knuth/dist/errata/errata.one751
-rw-r--r--systems/knuth/dist/errata/errata.seven821
-rw-r--r--systems/knuth/dist/errata/errata.six528
-rw-r--r--systems/knuth/dist/errata/errata.ten1289
-rw-r--r--systems/knuth/dist/errata/errata.tex1700
-rw-r--r--systems/knuth/dist/errata/errata.three1898
-rw-r--r--systems/knuth/dist/errata/errata.two491
-rw-r--r--systems/knuth/dist/errata/errorlog.tex2935
-rw-r--r--systems/knuth/dist/errata/logmac.tex107
-rw-r--r--systems/knuth/dist/errata/mf84.bug2336
-rw-r--r--systems/knuth/dist/errata/tex82.bug9103
-rw-r--r--systems/knuth/dist/etc/vftovp.web2373
-rw-r--r--systems/knuth/dist/etc/vptovf.web3181
-rw-r--r--systems/knuth/dist/lib/3test.mf53
-rw-r--r--systems/knuth/dist/lib/6test.mf55
-rw-r--r--systems/knuth/dist/lib/cmbase.mft76
-rw-r--r--systems/knuth/dist/lib/expr.mf5
-rw-r--r--systems/knuth/dist/lib/grayf.mf82
-rw-r--r--systems/knuth/dist/lib/hyphen.tex4469
-rw-r--r--systems/knuth/dist/lib/io.mf23
-rw-r--r--systems/knuth/dist/lib/logo.mf127
-rw-r--r--systems/knuth/dist/lib/logo10.mf10
-rw-r--r--systems/knuth/dist/lib/logo8.mf10
-rw-r--r--systems/knuth/dist/lib/logo9.mf10
-rw-r--r--systems/knuth/dist/lib/logobf10.mf10
-rw-r--r--systems/knuth/dist/lib/logosl10.mf3
-rw-r--r--systems/knuth/dist/lib/manfnt.mf788
-rw-r--r--systems/knuth/dist/lib/manmac.tex715
-rw-r--r--systems/knuth/dist/lib/mftmac.tex55
-rw-r--r--systems/knuth/dist/lib/null.mf3
-rw-r--r--systems/knuth/dist/lib/null.tex3
-rw-r--r--systems/knuth/dist/lib/plain.mf595
-rw-r--r--systems/knuth/dist/lib/plain.mft26
-rw-r--r--systems/knuth/dist/lib/plain.tex1241
-rw-r--r--systems/knuth/dist/lib/rtest.mf13
-rw-r--r--systems/knuth/dist/lib/slant.mf32
-rw-r--r--systems/knuth/dist/lib/story.tex18
-rw-r--r--systems/knuth/dist/lib/test.mf29
-rw-r--r--systems/knuth/dist/lib/testfont.tex208
-rw-r--r--systems/knuth/dist/lib/waits.mf168
-rw-r--r--systems/knuth/dist/lib/webmac.tex206
-rw-r--r--systems/knuth/dist/lib/ztest.mf13
-rw-r--r--systems/knuth/dist/mf/mf.web23135
-rw-r--r--systems/knuth/dist/mf/mfbook.tex20160
-rw-r--r--systems/knuth/dist/mf/trap.fot59
-rw-r--r--systems/knuth/dist/mf/trap.log4266
-rw-r--r--systems/knuth/dist/mf/trap.mf170
-rw-r--r--systems/knuth/dist/mf/trap.pl490
-rw-r--r--systems/knuth/dist/mf/trap.typ210
-rw-r--r--systems/knuth/dist/mf/trapin.log179
-rw-r--r--systems/knuth/dist/mf/trapman.tex400
-rw-r--r--systems/knuth/dist/mfware/gftodvi.web4393
-rw-r--r--systems/knuth/dist/mfware/gftopk.web2193
-rw-r--r--systems/knuth/dist/mfware/gftype.web1394
-rw-r--r--systems/knuth/dist/mfware/mft.web1980
-rw-r--r--systems/knuth/dist/tex/glue.web421
-rw-r--r--systems/knuth/dist/tex/tex.web24985
-rw-r--r--systems/knuth/dist/tex/texbook.tex28136
-rw-r--r--systems/knuth/dist/tex/trip.fot83
-rw-r--r--systems/knuth/dist/tex/trip.log7304
-rw-r--r--systems/knuth/dist/tex/trip.pl833
-rw-r--r--systems/knuth/dist/tex/trip.tex448
-rw-r--r--systems/knuth/dist/tex/trip.tfmbin0 -> 3116 bytes
-rw-r--r--systems/knuth/dist/tex/trip.typ1214
-rw-r--r--systems/knuth/dist/tex/tripin.log465
-rw-r--r--systems/knuth/dist/tex/tripman.tex413
-rw-r--r--systems/knuth/dist/tex/tripos.tex3
-rw-r--r--systems/knuth/dist/texware/dvitype.web2506
-rw-r--r--systems/knuth/dist/texware/pltotf.web2518
-rw-r--r--systems/knuth/dist/texware/pooltype.web430
-rw-r--r--systems/knuth/dist/texware/tftopl.web1596
-rw-r--r--systems/knuth/dist/web/tangle.web3325
-rw-r--r--systems/knuth/dist/web/weave.web4904
-rw-r--r--systems/knuth/dist/web/webman.tex1672
-rw-r--r--systems/knuth/local/GPCtypes.h108
-rw-r--r--systems/knuth/local/Makefile12
-rw-r--r--systems/knuth/local/Makefile-sparc12
-rw-r--r--systems/knuth/local/bdemo_ext.c81
-rw-r--r--systems/knuth/local/bdemo_ext.c-sparc84
-rw-r--r--systems/knuth/local/bdemo_ext.h15
-rw-r--r--systems/knuth/local/bdemo_ext.h-sparc11
-rw-r--r--systems/knuth/local/binarydemo.pbin0 -> 1549 bytes
-rw-r--r--systems/knuth/local/binarydemo.p-sparc48
-rw-r--r--systems/knuth/local/cm/bible12.mf75
-rw-r--r--systems/knuth/local/cm/cccsc10.mf110
-rw-r--r--systems/knuth/local/cm/ccmi10.mf75
-rw-r--r--systems/knuth/local/cm/ccmic9.mf75
-rw-r--r--systems/knuth/local/cm/ccr10.mf75
-rw-r--r--systems/knuth/local/cm/ccr5.mf75
-rw-r--r--systems/knuth/local/cm/ccr6.mf75
-rw-r--r--systems/knuth/local/cm/ccr7.mf75
-rw-r--r--systems/knuth/local/cm/ccr8.mf75
-rw-r--r--systems/knuth/local/cm/ccr9.mf75
-rw-r--r--systems/knuth/local/cm/ccsl10.mf75
-rw-r--r--systems/knuth/local/cm/ccsl9.mf75
-rw-r--r--systems/knuth/local/cm/ccslc9.mf75
-rw-r--r--systems/knuth/local/cm/ccti10.mf75
-rw-r--r--systems/knuth/local/cm/cmbxcd10.mf75
-rw-r--r--systems/knuth/local/cm/cmbxti12.mf75
-rw-r--r--systems/knuth/local/cm/cmbxti7.mf75
-rw-r--r--systems/knuth/local/cm/cmcscsl10.mf110
-rw-r--r--systems/knuth/local/cm/cmfibs8.mf75
-rw-r--r--systems/knuth/local/cm/cmitt12.mf75
-rw-r--r--systems/knuth/local/cm/cmitt9.mf75
-rw-r--r--systems/knuth/local/cm/cmman.mf855
-rw-r--r--systems/knuth/local/cm/cmntex10.mf78
-rw-r--r--systems/knuth/local/cm/cmntt10.mf78
-rw-r--r--systems/knuth/local/cm/cmsl6.mf75
-rw-r--r--systems/knuth/local/cm/cmsltt9.mf75
-rw-r--r--systems/knuth/local/cm/cmssbxo10.mf75
-rw-r--r--systems/knuth/local/cm/cmsslu30.mf75
-rw-r--r--systems/knuth/local/cm/cmssu30.mf75
-rw-r--r--systems/knuth/local/cm/cmsytt10.mf97
-rw-r--r--systems/knuth/local/cm/cmtim.mf78
-rw-r--r--systems/knuth/local/cm/cmvtti10.mf75
-rw-r--r--systems/knuth/local/cm/diam12.mf18
-rw-r--r--systems/knuth/local/cm/ebigop.mf912
-rw-r--r--systems/knuth/local/cm/gen.mf91
-rw-r--r--systems/knuth/local/cm/gen10.mf14
-rw-r--r--systems/knuth/local/cm/gen8.mf13
-rw-r--r--systems/knuth/local/cm/gen9.mf13
-rw-r--r--systems/knuth/local/cm/odigs.mf11
-rw-r--r--systems/knuth/local/etc-sparc/Makefile41
-rw-r--r--systems/knuth/local/etc-sparc/vftovp.ch330
-rw-r--r--systems/knuth/local/etc-sparc/vptovf.ch142
-rw-r--r--systems/knuth/local/etc/Makefile46
-rw-r--r--systems/knuth/local/etc/dvityext.h17
-rw-r--r--systems/knuth/local/etc/texwarext.h5
-rw-r--r--systems/knuth/local/etc/vftovp.ch227
-rw-r--r--systems/knuth/local/etc/vftovp.p807
-rw-r--r--systems/knuth/local/etc/vptovf.ch108
-rw-r--r--systems/knuth/local/etc/vptovf.p1112
-rw-r--r--systems/knuth/local/h00vars.h143
-rw-r--r--systems/knuth/local/lib/3list-small.tex71
-rw-r--r--systems/knuth/local/lib/3list.tex71
-rw-r--r--systems/knuth/local/lib/6list.tex75
-rw-r--r--systems/knuth/local/lib/8list.tex79
-rw-r--r--systems/knuth/local/lib/amsmodes.mf286
-rw-r--r--systems/knuth/local/lib/amsppt.sty1133
-rw-r--r--systems/knuth/local/lib/amssym.def108
-rw-r--r--systems/knuth/local/lib/amssym.tex279
-rw-r--r--systems/knuth/local/lib/amstex.tex1797
-rw-r--r--systems/knuth/local/lib/bigwlist.tex54
-rw-r--r--systems/knuth/local/lib/black.mf2
-rw-r--r--systems/knuth/local/lib/blackaps.mf10
-rw-r--r--systems/knuth/local/lib/blackdvi.tex96
-rw-r--r--systems/knuth/local/lib/blackimagen.mf9
-rw-r--r--systems/knuth/local/lib/blacklino.mf10
-rw-r--r--systems/knuth/local/lib/blacklj.mf9
-rw-r--r--systems/knuth/local/lib/colordvi.tex139
-rw-r--r--systems/knuth/local/lib/ctwimac.tex476
-rw-r--r--systems/knuth/local/lib/cwebmac.tex545
-rw-r--r--systems/knuth/local/lib/cyracc.def273
-rw-r--r--systems/knuth/local/lib/domino.mf100
-rw-r--r--systems/knuth/local/lib/e.mft33
-rw-r--r--systems/knuth/local/lib/epsf.tex215
-rw-r--r--systems/knuth/local/lib/fontchart.tex89
-rw-r--r--systems/knuth/local/lib/gkpmac.tex890
-rw-r--r--systems/knuth/local/lib/gray.mf3
-rw-r--r--systems/knuth/local/lib/grayaps.mf9
-rw-r--r--systems/knuth/local/lib/grayimagen.mf10
-rw-r--r--systems/knuth/local/lib/grayimagen3.mf7
-rw-r--r--systems/knuth/local/lib/grayimagenlight.mf12
-rw-r--r--systems/knuth/local/lib/graylj.mf10
-rw-r--r--systems/knuth/local/lib/lcircle.mf550
-rw-r--r--systems/knuth/local/lib/lcircle10.mf7
-rw-r--r--systems/knuth/local/lib/lcirclew10.mf6
-rw-r--r--systems/knuth/local/lib/letter.tex119
-rw-r--r--systems/knuth/local/lib/line.mf552
-rw-r--r--systems/knuth/local/lib/line10.mf11
-rw-r--r--systems/knuth/local/lib/linew10.mf11
-rw-r--r--systems/knuth/local/lib/list-latin.tex94
-rw-r--r--systems/knuth/local/lib/list.tex47
-rw-r--r--systems/knuth/local/lib/llist.tex67
-rw-r--r--systems/knuth/local/lib/local.mf122
-rw-r--r--systems/knuth/local/lib/local.tex8
-rw-r--r--systems/knuth/local/lib/logod10.mf10
-rw-r--r--systems/knuth/local/lib/logosl9.mf3
-rw-r--r--systems/knuth/local/lib/mfman.mf2481
-rw-r--r--systems/knuth/local/lib/mproof.tex28
-rw-r--r--systems/knuth/local/lib/oneone.mf10
-rw-r--r--systems/knuth/local/lib/picmac.tex178
-rw-r--r--systems/knuth/local/lib/proofmac.tex353
-rw-r--r--systems/knuth/local/lib/random.mf198
-rw-r--r--systems/knuth/local/lib/rotate.tex43
-rw-r--r--systems/knuth/local/lib/slantaps4.mf11
-rw-r--r--systems/knuth/local/lib/slantimagen4.mf11
-rw-r--r--systems/knuth/local/lib/slantimagen6.mf11
-rw-r--r--systems/knuth/local/lib/slantlino4.mf11
-rw-r--r--systems/knuth/local/lib/slantlj4.mf11
-rw-r--r--systems/knuth/local/lib/slist.tex49
-rw-r--r--systems/knuth/local/lib/snfont.mf165
-rw-r--r--systems/knuth/local/lib/tinywlist.tex53
-rw-r--r--systems/knuth/local/lib/twimac.tex400
-rw-r--r--systems/knuth/local/lib/unicode.tex25
-rw-r--r--systems/knuth/local/lib/wlist.tex53
-rw-r--r--systems/knuth/local/lib/xepsf.tex219
-rw-r--r--systems/knuth/local/man1/dvitype.137
-rw-r--r--systems/knuth/local/man1/gftodvi.183
-rw-r--r--systems/knuth/local/man1/gftopk.137
-rw-r--r--systems/knuth/local/man1/gftype.135
-rw-r--r--systems/knuth/local/man1/mf.1448
-rw-r--r--systems/knuth/local/man1/mft.154
-rw-r--r--systems/knuth/local/man1/pktype.134
-rw-r--r--systems/knuth/local/man1/pltotf.129
-rw-r--r--systems/knuth/local/man1/pooltype.123
-rw-r--r--systems/knuth/local/man1/tangle.1107
-rw-r--r--systems/knuth/local/man1/tex.1219
-rw-r--r--systems/knuth/local/man1/tftopl.131
-rw-r--r--systems/knuth/local/man1/vftovp.126
-rw-r--r--systems/knuth/local/man1/vptovf.126
-rw-r--r--systems/knuth/local/man1/weave.1107
-rw-r--r--systems/knuth/local/man1/web.1107
-rw-r--r--systems/knuth/local/mf-sparc/Makefile73
-rw-r--r--systems/knuth/local/mf-sparc/ext.c457
-rw-r--r--systems/knuth/local/mf-sparc/ext.h38
-rw-r--r--systems/knuth/local/mf-sparc/gftype.in2
-rw-r--r--systems/knuth/local/mf-sparc/ini_to_trap38
-rw-r--r--systems/knuth/local/mf-sparc/inimf.ch1505
-rw-r--r--systems/knuth/local/mf-sparc/mf.pool824
-rw-r--r--systems/knuth/local/mf-sparc/mf_arith.c142
-rw-r--r--systems/knuth/local/mf-sparc/mf_sunwin.c90
-rw-r--r--systems/knuth/local/mf-sparc/mf_window.h15
-rw-r--r--systems/knuth/local/mf-sparc/plain.basebin0 -> 56640 bytes
-rw-r--r--systems/knuth/local/mf-sparc/plain.log16
-rw-r--r--systems/knuth/local/mf-sparc/trap1.in3
-rw-r--r--systems/knuth/local/mf-sparc/trap2.in1
-rw-r--r--systems/knuth/local/mf/Makefile74
-rw-r--r--systems/knuth/local/mf/ext.c457
-rw-r--r--systems/knuth/local/mf/ext.h38
-rw-r--r--systems/knuth/local/mf/gftype.in2
-rw-r--r--systems/knuth/local/mf/ini_to_trap38
-rw-r--r--systems/knuth/local/mf/inimf.ch1505
-rw-r--r--systems/knuth/local/mf/mf.pool824
-rw-r--r--systems/knuth/local/mf/mf_arith.c142
-rw-r--r--systems/knuth/local/mf/mf_sunwin.c90
-rw-r--r--systems/knuth/local/mf/mf_window.h15
-rw-r--r--systems/knuth/local/mf/plain.basebin0 -> 56640 bytes
-rw-r--r--systems/knuth/local/mf/plain.log16
-rw-r--r--systems/knuth/local/mf/trap1.in3
-rw-r--r--systems/knuth/local/mf/trap2.in1
-rw-r--r--systems/knuth/local/mfpaths.h21
-rw-r--r--systems/knuth/local/mfware-sparc/Makefile54
-rw-r--r--systems/knuth/local/mfware-sparc/gf2dvi.ch836
-rw-r--r--systems/knuth/local/mfware-sparc/gftodvi.ch757
-rw-r--r--systems/knuth/local/mfware-sparc/gftopk.ch367
-rw-r--r--systems/knuth/local/mfware-sparc/gftopk_ext.c43
-rw-r--r--systems/knuth/local/mfware-sparc/gftopk_ext.h6
-rw-r--r--systems/knuth/local/mfware-sparc/gftype.ch430
-rw-r--r--systems/knuth/local/mfware-sparc/mft.ch534
-rw-r--r--systems/knuth/local/mfware-sparc/mft_ext.c249
-rw-r--r--systems/knuth/local/mfware-sparc/mft_ext.h16
-rw-r--r--systems/knuth/local/mfware-sparc/pktype.ch241
-rw-r--r--systems/knuth/local/mfware-sparc/pktype.web1158
-rw-r--r--systems/knuth/local/mfware/Makefile55
-rw-r--r--systems/knuth/local/mfware/gf2dvi.ch834
-rw-r--r--systems/knuth/local/mfware/gftopk.ch367
-rw-r--r--systems/knuth/local/mfware/gftopk_ext.c43
-rw-r--r--systems/knuth/local/mfware/gftopk_ext.h6
-rw-r--r--systems/knuth/local/mfware/gftype.ch430
-rw-r--r--systems/knuth/local/mfware/mft.ch534
-rw-r--r--systems/knuth/local/mfware/mft_ext.c249
-rw-r--r--systems/knuth/local/mfware/mft_ext.h16
-rw-r--r--systems/knuth/local/mfware/pktype.ch241
-rw-r--r--systems/knuth/local/mfware/pktype.web1158
-rw-r--r--systems/knuth/local/tex-sparc/Makefile74
-rw-r--r--systems/knuth/local/tex-sparc/dvitype.in5
-rw-r--r--systems/knuth/local/tex-sparc/ext.c468
-rw-r--r--systems/knuth/local/tex-sparc/ext.h32
-rw-r--r--systems/knuth/local/tex-sparc/ini_to_trip41
-rw-r--r--systems/knuth/local/tex-sparc/ini_to_vir23
-rw-r--r--systems/knuth/local/tex-sparc/initex.ch1419
-rw-r--r--systems/knuth/local/tex-sparc/plain.fmtbin0 -> 166564 bytes
-rw-r--r--systems/knuth/local/tex-sparc/plain.log103
-rw-r--r--systems/knuth/local/tex-sparc/tex.pool1040
-rw-r--r--systems/knuth/local/tex-sparc/trip1.in2
-rw-r--r--systems/knuth/local/tex-sparc/trip2.in1
-rw-r--r--systems/knuth/local/tex/Makefile75
-rw-r--r--systems/knuth/local/tex/dvitype.in5
-rw-r--r--systems/knuth/local/tex/ext.c228
-rw-r--r--systems/knuth/local/tex/ext.h27
-rw-r--r--systems/knuth/local/tex/ini_to_trip41
-rw-r--r--systems/knuth/local/tex/ini_to_vir23
-rw-r--r--systems/knuth/local/tex/initex.ch982
-rw-r--r--systems/knuth/local/tex/plain.fmtbin0 -> 166564 bytes
-rw-r--r--systems/knuth/local/tex/plain.log103
-rw-r--r--systems/knuth/local/tex/tex.pool1040
-rw-r--r--systems/knuth/local/tex/trip1.in2
-rw-r--r--systems/knuth/local/tex/trip2.in1
-rw-r--r--systems/knuth/local/texpaths.h26
-rw-r--r--systems/knuth/local/texware-sparc/Makefile39
-rw-r--r--systems/knuth/local/texware-sparc/access.c19
-rw-r--r--systems/knuth/local/texware-sparc/dvityext.c150
-rw-r--r--systems/knuth/local/texware-sparc/dvityext.h11
-rw-r--r--systems/knuth/local/texware-sparc/dvitype.ch497
-rw-r--r--systems/knuth/local/texware-sparc/pltotf.ch135
-rw-r--r--systems/knuth/local/texware-sparc/pooltype.ch70
-rw-r--r--systems/knuth/local/texware-sparc/tftopl.ch206
-rw-r--r--systems/knuth/local/texware/Makefile40
-rw-r--r--systems/knuth/local/texware/access.c17
-rw-r--r--systems/knuth/local/texware/dvityext.c176
-rw-r--r--systems/knuth/local/texware/dvityext.h17
-rw-r--r--systems/knuth/local/texware/dvitype.ch468
-rw-r--r--systems/knuth/local/texware/dvitype.p604
-rw-r--r--systems/knuth/local/texware/pltotf.ch118
-rw-r--r--systems/knuth/local/texware/pltotf.p830
-rw-r--r--systems/knuth/local/texware/pooltype.ch92
-rw-r--r--systems/knuth/local/texware/pooltype.p57
-rw-r--r--systems/knuth/local/texware/texwarext.h5
-rw-r--r--systems/knuth/local/texware/tftopl.ch128
-rw-r--r--systems/knuth/local/texware/tftopl.p504
-rw-r--r--systems/knuth/local/texware/tftopl.pool2
-rw-r--r--systems/knuth/local/web-sparc/Makefile33
-rw-r--r--systems/knuth/local/web-sparc/tangext.c109
-rw-r--r--systems/knuth/local/web-sparc/tangext.h8
-rw-r--r--systems/knuth/local/web-sparc/tangle.ch655
-rw-r--r--systems/knuth/local/web-sparc/tangle.p814
-rw-r--r--systems/knuth/local/web-sparc/weave.ch591
-rw-r--r--systems/knuth/local/web-sparc/weavext.c107
-rw-r--r--systems/knuth/local/web/Makefile34
-rw-r--r--systems/knuth/local/web/tangext.c106
-rw-r--r--systems/knuth/local/web/tangext.h13
-rw-r--r--systems/knuth/local/web/tangle.ch654
-rw-r--r--systems/knuth/local/web/tangle.p821
-rw-r--r--systems/knuth/local/web/tangle.p-edited816
-rw-r--r--systems/knuth/local/web/tangle.p1814
-rw-r--r--systems/knuth/local/web/weave.ch598
-rw-r--r--systems/knuth/local/web/weave.p1603
-rw-r--r--systems/knuth/local/web/weavext.c107
336 files changed, 236878 insertions, 0 deletions
diff --git a/systems/knuth/dist/cm b/systems/knuth/dist/cm
new file mode 120000
index 0000000000..9c7a442e35
--- /dev/null
+++ b/systems/knuth/dist/cm
@@ -0,0 +1 @@
+../../../fonts/cm/mf \ No newline at end of file
diff --git a/systems/knuth/dist/errata/cm85.bug b/systems/knuth/dist/errata/cm85.bug
new file mode 100644
index 0000000000..d8cbea6442
--- /dev/null
+++ b/systems/knuth/dist/errata/cm85.bug
@@ -0,0 +1,1906 @@
+This file is a log of changes made to the "new generation" of Computer Modern
+fonts, after the first output proofs were ready. I had a great deal of
+advice from Neenie Billawala, Matthew Carter, and Richard Southall while
+I was making these revisions.
+
+Entries are in chronological order; thus the most recent news appears
+at the bottom of the file.
+
+-------------------------------------------------------------------------------
+ Changes based on the proofsheets of April 26, 1985
+ACCENT.MF:
+ lower the idots, umlauts, and tilde (MC)
+ Polish ell cross: longer stroke in bold
+CMBASE.MF:
+COMLIG.MF:
+ double quotes too close together in bold (MC)
+CSCSPU.MF:
+ SS made from two S's
+GREEKU.MF:
+ Gamma: .25u longer arm
+ Lambda: too narrow (MC,NB)
+ Xi: 1u wider; shorten middle serifs (MC)
+ top and bottom strokes to be arms (NB)
+ Psi: more super; thin the lower curve (MC)
+ add points 3' and 7' for crisp sans
+ Omega: redo the bottom, curl 0 becomes vertical (MC)
+ thin from hair to vair at points 3 and 5
+PUNCT.MF:
+ Monospace period, comma, colon, semicolon made heavier (MC)
+ROMAN.MF:
+ ligs=2 gets ff, fi, etc.; ligs=1 gets only --, '', etc.
+ II kerning introduced
+ kern between a and ' in csc
+ROMAND.MF:
+ 0: med_curve decreased to curve; y2l and y4l raised
+ 1: cap_bar changed to bar in the top stroke
+ sans-serif serifs also use bar as the slab height
+ 2: lighten cap_curve and cap_stem slightly (RS)
+ move point 3 down by .5vair (MC)
+ 4: thin the diagonal still more (NB)
+ bar line moves up one pixel when there's a serif below it
+ 6: move bulb to the right (MC)
+ make the lower left curve more_super (RS,NB)
+ 9: make the upper right curve more_super (RS,NB)
+ROMANL.MF:
+ a: bulb positioned by its center rather than the left (MC)
+ c: right terminals moved closer to the edge (MC,NB)
+ more pull on the inside left (RS)
+ e: less left sidebar in monospace (MC)
+ bold versions to be wider (MC)
+ more pull on inside left (MC,RS)
+ g: lower edge of loop goes one pixel lower
+ varg: point 6 raised above baseline (MC)
+ y0 calculated so that it works when x7l=stem_edge
+ lower arc goes one pixel lower
+ i: monospace version gets more left sidebar (MC)
+ dot moves slightly right (NB)
+ stem'' increased to stem' (MC,RS,NB)
+ slab replaces tiny in correction for dot-too-close
+ j: diminish monospace sidebars (MC)
+ shift slightly to right (MC)
+ k: serif right_jut decreased .8 to .6
+ bug fixed in alpha2 (had y1 not y11)(MC,RS,NB)
+ decreased diagonal weights
+ "if abs(angle(z3-z4)-45)<2:y4:=y3-(x3-x4);fi" rejected; moves y4 down too much
+ n: omit pull inside the arch (MC,RS)
+ introduce stem_shift, shifts this character and others slightly right (MC)
+ o: raise y2l and y4l (RS)
+ s: reduced sidebearing in sans (MC)
+ increased thickness at edges (.35 to .5) (RS)
+ changed to super_arc, and made it more_super (RS)
+ raised the top of upper barb to equal top of the character (RS)
+ removed typo (`tiny' for `fine')
+ reduced flare in upper terminal of sans, and rounded it properly
+ t: width to grow in bold case
+ height made independent of bar_height
+ slightly longer bar (MC)
+ v: decreased left stem (RS)
+ w: decreased stems (MC,RS)
+ monospace version drops middle from 2/3 to .6 and lengthens inner serifs
+ x: decreased left stem
+ y: decreased left stem
+ z: lighter diagonal, longer upper arm (MC,RS,NB)
+ROMANP.MF:
+ $: tau eliminated
+ROMANU.MF:
+ A: decrease right stem (MC)
+ make the bar thinner (MC,NB)
+ bar position based on y0 rather than bar_height
+ C: use cap_band instead of vair' at top and bottom (RS)
+ correct typo: x2l to be reset, not y2l
+ E: middle arm .35u longer, but beak reduced from .7 to .6 (MC)
+ F: top arm .25u longer; middle arm as E (MC)
+ G: same as C; also the bar is raised one pixel
+ J: slightly longer upper left serif
+ K: lower diagonal thinned and moved outward
+ M: monospace version raised still more in middle
+ N: diagonal thinned (MC,RS)
+ O: y2l and y4l raised
+ R: tail should move to the right (MC,RS,NB)
+ bold case gets wider (RS,NB)
+ S: changed as s (RS)
+ V: took stem_corr off the left stem (MC)
+ W: monospace version takes center down from 2/3 to .6, lengthens inner jut
+ X: took stem_corr off the left stem (MC)
+ Y: took stem_corr off the left stem (MC)
+ Z: diagonal made lighter in sans (MC)
+ upper arm made longer (MC,RS)
+ROMLIG.MF:
+ equalize stem weights (RS,NB)
+ROMSPL.MF:
+ ae, oe: incorporate changes of a and e and o
+ ss: more stem weight, less curve weight, less bulb (MC,NB)
+ dotless i,j: see i,j
+ROMSPU.MF:
+ AE: middle arm changes as E (MC)
+ if hefty and monospace, avoid the serif on middle arm
+ bars not aligned if hefty
+ if monospace, middle stem moves to right
+ OE: middle arm changes as in AE (MC)
+ middle stem lightened
+ROMSUB.MF:
+ blankspace: not so close to sides
+SYM.MF:
+CMR10.MF etc:
+ bar_height raised in the quote and bold fonts (MC,NB)
+ comma_depth in cmtt10 increased from 40 to 50 (MC)
+ cap_notch_cut decreased in cmsc10, cmsx10
+ apex_o decreased in cmr10, cmb10, cmbx10, cmbx5, cmcsc10, cmtt10
+ o decreased in cmb10
+ cap_jut decreased from 39 to 34 in cmb10; cap_serif_fit increased 5 to 6
+ cmbx5 gets smaller beak, fudge, cap_ess; more apex_corr
+GENERAL:
+ "robustness" added when points like x8r of "a" are calculated
+ (this affects a,b,d,varg,j,p,q,t,u,dj,ae,6,9,f_stroke)
+ "robustness" also added to avoid curl at end of paths
+ (this affects c,e,s,C,G,S,1,5,ae,oe,breve,parens)
+ "term" subroutine adds robustness for sans-serif terminals
+ (this affects C,G,J,S,f_stroke,j,ss,ae,oe,2,3,5,6,9,$,&,?,@)
+ code revised to use "pos" instead of "penpos" when possible
+ Changes based on the proofsheets of May and June, 1985
+ROMAND.MF:
+ 1: use o instead of apex_o
+ 4: bar line height shouldn't depend on bar_height
+ 5: sans-serif lower terminal position raised; depends on upper left edge
+ bug in arm at top (0 for 90) made all hefty arms too small
+ 9: disappears at link
+ROMANU.MF:
+ B: middle bar positioned by its middle, not its bottom (RS)
+ serifed lobes thickened slightly at bottom (RS)
+ C: lower terminal is tilted the other way
+ C,G: upper terminal in sans-serif is tilted slightly
+ upper barb is darkened
+ J: tilt the lower terminal
+ K: lighten the main diagonal (serif case) (RS)
+ lighten the upper diagonal (sans-serif case) (RS)
+ M: remove apex_corr (RS)
+ N: lighten the diagonal
+ P: too light below counter (RS)
+ Q: sharpen upper right corner of sans-serif tail (RS)
+ top of tail thickened and moved slightly left
+ S: less super at upper left and lower right (MC)
+ barb to go below baseline
+ barbs darkened
+ add weight at top, bottom in sans_serif version (RS)
+ tilt the lower terminal like the upper one
+ top weight needs to be maxed with fine.breadth
+ W: top middle stems allowed to overlap in serifed case
+ second inner serif made no longer than left outer serif (MC)
+ ceiling must become floor, to ensure that x5r>=x4r-mid_corr
+ Z: diagonal still too heavy, because previous change was ineffective
+ beaks made darker
+ better positioning of y2 and y3 when tiny is zero
+CSCSPU.MF:
+ J,SS: changes from J and S
+ROMANL.MF:
+ a: inappropriate uses of "fine" are removed
+ bh introduced to guard against "wild" bar heights
+ thin_join introduced at point 9
+ robustness logic changed so that x8l-x8r<=u
+ c: too much weight constrast between strokes in sans_serif (RS)
+ top terminal lowered to match a
+ bottom terminal tilted the other way
+ d: fine.breadth should have been thin_join as in b, p, q
+ e: 1+xpart changed to xpart
+ bottom terminal tilted the other way
+ g: sans-serif ear to use z0l and z0r
+ h,m,n: left inner serifs stay at jut length
+ i,l: mod_width for better fit at lowres
+ j: reposition stem for better fit (use .5w+2.5u, not w-2.5u)
+ remove bug in "pos" change
+ k: upper diagonal serif lengthened from 1.2jut to 1.4jut (RS)
+ diagonals made still lighter
+ r: thin_join introduced
+ s: barb to go below baseline
+ barbs darkened
+ add weight at top, bottom in sans_serif version (RS)
+ tilt the lower terminal like the upper one (RS)
+ top weight needs to be maxed with fine.breadth
+ t: bar extended still more at right (RS)
+ v: decreased the width by .25width_adj#
+ w: overlap allowed as in W
+ second inner serif made no longer than left outer serif (MC)
+ ceiling must become floor, to ensure that x5r>=x4r-mid_corr
+ x: decreased the width by .5width_adj# (RS)
+ y: sans-serif tail curls up too far (RS)
+ z: beaks too light; decrease the diagonal to compensate (RS)
+ better positioning of y2 and y3 when tiny is zero
+ROMSPL.MF:
+ dotless i and j: as i and j
+ ae and oe: bottom terminal tilted the other way
+ ae: corrected as a
+ROMSPU.MF:
+ AE: x12 based on x1l rather than x1, works better in bold cases (RS)
+ OE: left curve not to be pulled
+GREEKU.MF:
+ Phi and Psi: middle parts must keep away from the serifs
+ROMANP.MF:
+ $: strengthened the terminals in sans-serif case, and tilted them
+ &: opened the eye by moving point 8 left .5u (RS)
+ ?: tilted the sans-serif terminal and moved it down a little more
+PUNCT.MF:
+ %: move the diagonal a bit left at the top
+ ( and ): 3.5( changed to 3(.
+ [ and ]: round changed to ceiling and floor, avoids disappearing brackets
+ @: inner a placed by its edges instead of its stem centers (RS)
+ITALL.MF:
+ f: lower bulb moved left; .5 changed to .6 between bulb and stem
+ k: bug in lower right stroke (missing e's)
+ l: bug in italic correction for math fitting (u not u#)
+ m: avoid hooks in monospace version
+ p: lengthed left serif at bottom from .5jut to .75jut
+ x: bulbs moved .25u further out; .5 used between bulb and stem
+ w: avoid hook in monospace version
+ITALIG.MF:
+ all five: changed like f
+ fi: made 1u less wide
+ fl,ffl: made .5u less wide, bulb now touches the l stem
+ ffi: made .75u less wide
+GREEKL.MF:
+ beta, gamma, zeta, xi, omega, varepsilon: +eps at the tight turns
+ gamma: final diagonal should be more slanted (MC)
+ zeta: as tall as xi, shallower diagonal slant (MC)
+ kappa: bug in lower right stroke (missing e's)
+ xi: raise the bottom diagonal and distort arc slightly (MC)
+ chi: introduce slight curve in main diagonal stroke
+ varphi: too wide at right half bowl (MC)
+ITALMS.MF:
+ wp: bulb smaller, bottom a little more open
+ITALD.MF:
+ 5: changed like 5 in ROMAND
+OLDDIG.MF:
+ 2: top a bit narrower, bottom a bit wider (MC)
+ 5: changed like 5 in ROMAND
+SYM.MF:
+ plus-minus: shift depends on u not asc_height
+SYMBOL.MF:
+ minus-plus: shift depends on u not asc_height
+ up-and-down arrows (characters '154 and '155): taller and deeper
+ clubsuit: wider
+ spadesuit: y1+1 changed to y1+.75 (also in clubsuit)
+ROMAN.MF:
+ eliminated kerning between L and O,C,G,Q,U (also in TEXTIT and CSC)
+MATHEX.MF:
+ font_x_height needed to be set, for positioning of accents
+CMB10.MF:
+ dishing is too much (on all bold fonts)
+CMR5.MF:
+ letter_fit decreased 10 to 5
+CMBX5.MF:
+ fudge should be 1 (cap_stem 8=:7 in aps mode was too much)
+ decrease letter_fit drastically; this means f-ligatures must come back
+ lowercase parameters increased by 2/36
+CMBI10.MF:
+ lowercase serifs too short. (same change to all the italic fonts)
+CMSX10.MF:
+ decrease `ess' (RS)
+CMTC10.MF:
+ fudge is too much in lowercase
+CMCC10.MF:
+ lowercase more extended
+ much less letterspace in caps
+ increase lower.cap_curve, decrease lower.o
+CMSQI8.MF:
+ slope shouldn't be so much (RS)
+ increase `ess' (RS)
+
+GENERAL:
+ serif-abutment corrections changed to +1 instead of +2
+ vround used for vertical rounding
+
+CMBASE.MF:
+ new way to set shrink_fit (0,1,2) for monotonicity with letter_fit
+ letter_fit not included in mono_charwd
+ fine must be positive (else we lose barbs)
+ thin_join must be positive (else h_stroke gives a bad pos)
+ Changes based on the `version 0' test fonts of July 12
+[not showing hundreds of changes to the `typography' of the programs]
+
+Driver files in general:
+Frequency info (from MANUAL.TEX[tex,dek]) was used to order the ligtables
+font_xheight changed to font_x_height (a change to PLAIN.MF)
+
+Program files in general:
+ use `hround' instead of `round' wherever granularity might be wanted
+ less_rounded was taken out; autorounded occasionally put in
+ arm($,$$,...), the value of y$$ was adjusted by eps (lower) or -eps (upper)
+
+Parameter files in general:
+ new 12pt and 17+pt fonts; more sans serif fonts
+ new comment at bottom of all parameter files: `switch to the driver file'
+
+ACCENT.MF:
+ ^: eps subtracted from y1 (helps avoid degenerate equations in ultralowres)
+ '`: eps added to y1
+
+ROMAN.MF:
+ kern entry for gj had the wrong sign
+ kerns for v removed in sans serif
+ new kern for aj in serif case
+ new kern for ar in sans serif
+
+ROMAND.MF:
+ 3: pos3 needed to be maxed with fine.breadth
+ y1r,y9r needed to be adjusted by eps
+ 6,9: keep x1 on the correct side of x2 at lowlowres
+ (this change also affects OLDDIG, ITALD, and ITALMS partial sign)
+
+ROMANP.MF:
+ $: less_tense bulbs
+
+ROMSPL.MF:
+ ss: ensure x5 not > x4l at low resolutions
+
+ROMLIG.MF:
+ itc shouldn't include the slant...
+
+COMLIG.MF:
+ em dash should be one em wide (add letter_fit# to the adjustments)
+
+BIGOP.MF:
+ \displaystyle coproduct sign had 1.2bracket instead of 1.8bracket
+ charlist syntax changed from commas to colons
+
+BIGDEL.MF:
+ extensible double arrow: position wasn't computed as in top and bottom
+ charlist syntax changed from commas to colons
+
+BIGACC.MF:
+ charlist syntax changed from commas to colons
+
+ITALL.MF:
+ c: point 1 made definitely greater than point 2, for lowlowres
+ q: x4 increased by eps, for lowlowres path intersection
+ r: x4 based on w, not 5u
+ s: max(ess,fine.breadth)
+ add eps and 2eps for lowlowres case (cmmi6 at 100/in)
+
+ITALMS.MF:
+ wp: x6r must not be less than x1r at lowlowres
+
+TSETSL.MF:
+ gamma: last time's bugfix from GREEKL needs to be here too
+
+GREEKL.MF:
+ omega: add 2eps to x4-x6
+ lambda: make sure that y3>y4 at lowlowres
+ zeta: make sure that x2>x0 at lowlowres
+ varsigma: make sure that x1>x2 at lowlowres
+
+CMFIB8.MF:
+ decreased serif_fit, cap_serif_fit; increased curve, cap_curve
+
+CMBASE.MF:
+ l,r made newinternals
+ serif darkness and skew switches now done with boolean variables
+ comma and ammoc: 2eps addedto/subtractedfrom position of x@2
+ mod_width now called change_width
+ ess and cap_ess initialized to preserve ratios with stem and cap_stem
+ mode="string" had to be handled properly
+ ^ and ! changed to t_ and o_, as per new PLAIN conventions
+ make cmchar "outer"
+ t_ is now `relaxed' if it is useless
+ soft is now softjoin in PLAIN (we must set join_radius:=u)
+ eps is now in PLAIN
+ vair is vrounded but at least 1; also slab, etc.
+ cal nibs, yscaled by cap_hair not vair
+ autorounding and smoothing turned off
+
+ROMANL.MF:
+ a: `footed' version is .25u tighter at the right
+ b,d,varg,p,q: x4l position relative to .5(w-serif_fit) not 5u
+ e: testpath uses .r not .l (because of cmss10)
+ f: added 1 at right for monospace lowres versions
+ varg: robust sans-serif terminal at tail
+ m: missing `round' in r adjustment caused noninteger chardx
+ r: x4 based on w, not 5.25u
+ s: watch out that ess isn't so small that badpos error occurs (also ROMANU)
+ barbs made longer; based on middle stroke not just h
+ t: sans-serif terminal made more robust in extreme cases
+ w: in non-monospace hefty serif case, the middle point comes to .8h not h
+ y: robust sans-serif terminal at tail
+
+ROMANU.MF:
+ E: good.y used in y9
+
+PUNCT.MF:
+ []: side_thickness needed to be maxed with crisp.breadth
+ +1 or -1 makes lowres versions more distinctive
+ #: tricky part lost at low resolution
+
+SYMBOL.MF:
+ angle brackets: +1 and -1 to make lowres versions more distinctive
+ aleph: eps added so that y5 will be well defined at lowlowres
+
+SYM.MF:
+ integral sign: bulbs need to be in right direction at lowlowres
+
+CALU.MF:
+ A: apex moved slightly down
+ D: less flat at top, slightly lower lobe
+ F: u less right sidebar
+ H: soft corner at upper left
+ I: middle stroke more like J, upper bar a little further right
+ K: introduce t_ in case of aspect_ratio
+ L: darker
+ M: lighter first diagonal; clean the joins
+ N: lighter diagonal; clean bottom right; .5u less right sidebar
+ O: shortened the inner stroke
+ P: stem comes lower
+ Q: tail not so long at left
+ R: not so flat at top; diagonal less tense; tail less high
+ T: narrower, also changed like I, .5u less right sidebar,
+ heavier, not so high at right
+ U: less extreme curve at lower left
+ V: cleaned up at bottom; lighter first diagonal; slightly wider
+ W: changed like V, but double
+ X: new design, less rigid
+ Y: new design based more on V than U
+ Z: tighter at right, heavier at top and bottom
+
+CMSS10.MF:
+ decreased notch_cut and bar_height
+
+%Note: The following can presumably be deleted; I removed it from CMBASE because
+% I don't think it is used any more:
+vardef parallel_pos(expr d,u,v,w) = % point at distance $d$ from $u$,
+ % on the line through $w$ that's parallel to |u..v|
+ pair p_; p_=w+whatever*(u-v)=u+whatever*(u-v) rotated 90;
+ numeric d_; d_=length(u-p_); % distance from $w$ to |u..v|
+ if d_>=d: p_ else: p_+(d+-+d_)*unitvector(v-u) fi enddef;
+ Changes subsequent to `Version 1' as released on October 7, 1985.
+
+1. thin_join should be subject to blacker:
+@x in CMBASE.MF, the font_setup routine
+ define_whole_pixels(letter_fit,thin_join,fine,crisp,tiny);
+ define_whole_vertical_pixels(body_height,asc_height,
+ cap_height,fig_height,x_height,comma_depth,desc_depth,serif_drop);
+ define_whole_blacker_pixels(hair,stem,curve,flare,
+@y
+ define_whole_pixels(letter_fit,fine,crisp,tiny);
+ define_whole_vertical_pixels(body_height,asc_height,
+ cap_height,fig_height,x_height,comma_depth,desc_depth,serif_drop);
+ define_whole_blacker_pixels(thin_join,hair,stem,curve,flare,
+@z
+@x
+ if thin_join=0: thin_join:=1; fi
+@y
+@z
+[Note, December 11: My first correction also introduced the statement
+ if fine<thin_join: fine:=thin_join; fi
+in place of
+ if fine=0: fine:=1; fi
+but I can't understand why I would do such a stupid thing!]
+
+2. A new subroutine needed in CMBASE.MF (for cases with aspect_ratio<>1):
+vardef Vround primary y = y_:=vround y;
+ if y_<min_Vround: min_Vround else: y_ fi enddef;
+newinternal y_,min_Vround;
+
+@x related changes to the font_setup routine (in CMBASE.MF):
+ forsuffixes $=vair,bar,slab,cap_bar,cap_band:
+ $:=vround($.#*hppp+blacker); if $<=1: $:=1; fi endfor
+@y
+ define_whole_vertical_blacker_pixels(vair,bar,slab,cap_bar,cap_band);
+@z
+@x
+ vair':=max(1,vround(vair+vair_corr));
+ vstem:=max(1,vround .8[vair,stem]);
+ cap_vstem:=max(1,vround .8[vair,cap_stem]);
+@y
+ vair':=vround(vair+vair_corr);
+ vstem:=vround .8[vair,stem]; cap_vstem:=vround .8[vair,cap_stem];
+@z
+@x
+ pickup pencircle scaled rule_thickness; rule.nib:=savepen;
+@y
+ min_Vround:=max(fine.breadth,crisp.breadth,tiny.breadth);
+ forsuffixes $=vair,bar,slab,cap_bar,cap_band,vair',vstem,cap_vstem:
+ if $<min_Vround: $:=min_Vround; fi endfor
+ pickup pencircle scaled rule_thickness; rule.nib:=savepen;
+@z
+
+The Vround routine is substituted for vround in the programs for
+ Macron, line 6 (in ACCENT.MF)
+ Variant epsilon, line 5 (in GREEKL.MF)
+ Sigma, line 6 (in GREEKU.MF)
+ Omega, line 14 (in GREEKU.MF)
+ 2, line 6 (in OLDDIG.MF and ROMAND.MF)
+ 7, lines 6 and 8 (in OLDDIG.MF and ROMAND.MF)
+ g, lines 6 and 8 (in ROMANL.MF)
+ s, line 6 (in ROMANL.MF)
+ y, line 9 (in ROMANL.MF)
+ z, line 6 (in ROMANL.MF)
+ &, line 39 (in ROMANP.MF)
+ ?, line 18 (in ROMANP.MF)
+ Spanish open ?, line 17 (in ROMANP.MF)
+ Z, line 6 (in ROMANU.MF)
+ smile and frown, line 4 (in ROMMS.MF)
+
+3. Typo in program for oe ligature.
+@x in file ROMSPL.MF
+else: left_curve=max(tiny.breadth,hround(curve-2stem_corr));
+@y
+else: left_curve=max(fine.breadth,hround(curve-2stem_corr));
+@z
+
+4. Forgotten update to program for SS.
+@x in file CSCSPU.MF
+pickup fine.nib; pos2(max(fine.breadth,s_slab-vround vair_corr),-90);
+pos0(cap_ess,theta); pos7(s_slab,-90);
+x2+x7=2x0=w; x7-x2=if serifs: u else: 0 fi; top y2l=h+o; bot y7r=-o;
+y0=.52h; lft x3l=hround u; rt x6r=hround(w-u);
+x3r-x3l=x6r-x6l=hround .5[s_slab,cap_ess] -fine;
+@y
+numeric ess'; ess'=max(fine.breadth,cap_ess);
+pickup fine.nib; pos2(max(fine.breadth,s_slab-vround vair_corr),-90);
+pos0(ess',theta); pos7(s_slab,-90);
+x2+x7=2x0=w; x7-x2=if serifs: u else: 0 fi; top y2l=h+o; bot y7r=-o;
+y0=.52h; lft x3l=hround u; rt x6r=hround(w-u);
+x3r-x3l=x6r-x6l=hround .5[s_slab,ess']-fine;
+@z
+
+5. Hairline in `k' was overcorrected. (Pavel Curtis)
+@x in file ROMANL.MF
+stem3=max(tiny.breadth,hround(fudged.hair-4stem_corr));
+@y
+stem3=max(tiny.breadth,hround(fudged.hair if hefty:-\\4stem_corr fi));
+@z
+
+6. Too much kerning in "We", etc., in sans-serif fonts
+@x in file ROMAN.MF and TEXSET.MF
+ ligtable "F": "V": "W": "o" kern kk#, "e" kern kk#, "u" kern kk#,
+ "r" kern kk#, "a" kern kk#, "A" kern kkk#,
+@y
+ ligtable "F": "V": "W": if serifs: "o" kern kk#, "e" kern kk#, "u" kern kk#,
+ "r" kern kk#, "a" kern kk#, "A" kern kkk#,
+ else: "o" kern k#, "e" kern k#, "u" kern k#,
+ "r" kern k#, "a" kern k#, "A" kern kk#, fi
+@z
+@x in file TITLE.MF
+ ligtable "F": "V": "W": "A" kern kkk#,
+@y
+ ligtable "F": "V": "W": "A" kern if serifs: kkk# else: kk#\\fi,
+@z
+@x in file CSC.MF
+ ligtable "F": "V": "W": "a" kern kkk#, "A" kern kkk#,
+@y
+ ligtable "F": "V": "W": if serifs: "a" kern kkk#, "A" kern kkk#,
+ else: "a" kern kk#, "A" kern kk#, fi
+@z
+@x in file CSC.MF
+ ligtable "f": "v": "w": "a" kern kkk#,
+@y
+ ligtable "f": "v": "w": "a" kern if serifs: kkk# else: kk#\\fi,
+@z
+
+7. dish serifs need to be more robust when dishing is very small.
+@x in file CMBASE.MF
+ if y$<y$$: dish_out=bot y$; dish_in=dish_out+dish;
+ else: dish_out=top y$; dish_in=dish_out-dish; fi
+ erase fill\\(x@1,dish_out)..(x$,dish_in){right}..(x@@1,dish_out)--cycle;
+@y
+ if y$<y$$: dish_out=bot y$; dish_in=dish_out+dish; let rev_=reverse;
+ else: dish_out=top y$; dish_in=dish_out-dish; let rev_=relax; fi
+ erase fill rev_
+ ((x@1,dish_out)..(x$,dish_in){right}..(x@@1,dish_out)--cycle);
+@z
+
+8. more robustness in case thin_join is too big
+@x in file ROMANL.MF, program for "b"
+pos4(vair,90); pos5(curve,0);
+pos6(vair,-90); penpos7(x3l-x3r,-180);
+rt x3l=1/3[rt x2,edge]; y3=1/8[bar_height,x_height];
+@y
+pos4(vair,90); pos5(curve,0); pos6(vair,-90); penpos7(x3l-x3r,-180);
+rt x3l=max(rt x3l-(lft x3r-tiny.lft x2l),1/3[rt x2,edge]);
+y3=1/8[bar_height,x_height];
+@z
+@x ibid, program for "d"
+pos4(vair,90); pos5(curve,180);
+pos6(vair,270); penpos7(x3r-x3l,360);
+lft x3l=1/3[lft x2,edge]; y3=1/8[bar_height,x_height];
+@y
+pos4(vair,90); pos5(curve,180); pos6(vair,270); penpos7(x3r-x3l,360);
+lft x3l=min(lft x3l-(rt x3r-tiny.rt x2r),1/3[lft x2,edge]);
+y3=1/8[bar_height,x_height];
+@z
+@x ibid, program for variant "g"
+pos4(vair,90); pos5(curve,180);
+pos6(vair,270); penpos7(x3r-x3l,360);
+lft x3l=2/3[lft x2,edge]; y3=bar_height;
+@y
+pos4(vair,90); pos5(curve,180); pos6(vair,270); penpos7(x3r-x3l,360);
+lft x3l=min(lft x3l-(rt x3r-tiny.rt x2r),2/3[lft x2,edge]); y3=bar_height;
+@z
+@x ibid, program for "p"
+rt x3l=1/3[rt x2,edge]; y3=1/8[bar_height,x_height];
+@y
+rt x3l=max(rt x3l-(lft x3r-tiny.lft x2l), 1/3[rt x2,edge]);
+y3=1/8[bar_height,x_height];
+@z
+@x ibid, program for "q"
+lft x3l=2/3[lft x2,edge]; y3=bar_height;
+@y
+lft x3l=min(lft x3l-(rt x3r-tiny.rt x2r),2/3[lft x2,edge]); y3=bar_height;
+@z
+@x ibid, proram for "q"
+x6l=x4l-.2u; bot y6r=-oo;
+lft x7l=1/3[lft x2,edge]; y7=min(y3,y6+y4-y3+.6vair);
+@y
+x6l=x4l-.2u; bot y6r=-oo; y7=min(y3,y6+y4-y3+.6vair);
+lft x7l=min(lft x7l-(rt x7r-tiny.rt x2r),1/3[lft x2,edge]);
+@z
+
+9. More robust "c" and "e" and "C"
+@x in file ROMANL.MF, program for "c"
+ (x,y4l)=whatever[z4r,z5l]; x4l:=x;
+@y
+ (x,y4l)=whatever[z4r,z5l]; x4l:=min(x,x4l+.5u);
+@z
+@x ibid, program for "e"
+ (x,y4l)=whatever[z4r,z5]; x4l:=x;
+@y
+ (x,y4l)=whatever[z4r,z5]; x4l:=min(x,x4l+.5u);
+@z
+@x in file ROMSPL.MF, programs for "ae" and "oe"
+ (x,y14l)=whatever[z14r,z15]; x14l:=x;
+@y
+ (x,y14l)=whatever[z14r,z15]; x14l:=min(x,x14l+.5u);
+@z
+@x in file ROMANU.MF, programs for "C" and "G"
+ (x2l',y2l)=whatever[z2r,z1l]; x2l:=x2l';
+ (x4l',y4l)=whatever[z4r,z5l]; x4l:=x4l';
+@y
+ (x2l',y2l)=whatever[z2r,z1l]; x2l:=min(x2l',x2l+.5u);
+ (x4l',y4l)=whatever[z4r,z5l]; x4l:=min(x4l',x4l+.5u);
+@z
+
+10. More robust foot in "a"
+@x in file ROMANL.MF
+ filldraw stroke z5'e---z10e...z11e{right}; % foot
+@y
+ pos12(shaved_stem,0); x11=x12; top y12=slab+eps;
+ filldraw z5'l---z10l...z11l{right}--z11r
+ --z12r{left}...z10r+.75(z12-z11)---z5'r--cycle; % foot
+@z
+
+11. Barbs less sharp
+@x in file ROMANU.MF, programs for "C" and "G"
+ x6=x1r; top y6=h+o; x1r-x1'=2cap_curve-fine; y1'=y1;
+ path upper_arc; upper_arc=z1{x2-x1,10(y2-y1)}..z2{left};
+ numeric t; t=xpart(upper_arc intersectiontimes (z6--z1'));
+ filldraw z1r--z6--subpath(t,0) of upper_arc--cycle; % barb
+@y [actually "G" had something similar but not identical]
+ pos6(.3[fine.breadth,cap_hair],0); x6r=x1r; top y6=h+o;
+ x1r-x1'=2cap_curve-fine; y1'=y1;
+ path upper_arc; upper_arc=z1{x2-x1,10(y2-y1)}..z2{left};
+ numeric t; t=xpart(upper_arc intersectiontimes (z6l--z1'));
+ filldraw z1r--z6r--z6l--subpath(t,0) of upper_arc--cycle; % barb
+@z
+@x ibid, program for "S"
+ x10=x1l; top y10=top y2l; x9=x8r; bot y9=bot y7r;
+ x1l-x1'=x8'-x8r=1.6cap_curve-fine; y1'=y1; y8'=y8;
+ numeric t; t=xpart(upper_arc intersectiontimes(z10--z1'));
+ filldraw z1l--z10--subpath(t,0) of upper_arc--cycle; % upper barb
+ t:=xpart(lower_arc intersectiontimes(z9--z8'));
+ filldraw z8r--z9--subpath(t,1) of lower_arc--cycle; % lower barb
+@y
+ pos10(.3[fine.breadth,cap_hair],0); pos9(.3[fine.breadth,cap_hair],0);
+ x10r=x1l; top y10=top y2l; x9l=x8r; bot y9=bot y7r;
+ x1l-x1'=x8'-x8r=1.6cap_curve-fine; y1'=y1; y8'=y8;
+ numeric t; t=xpart(upper_arc intersectiontimes(z10l--z1'));
+ filldraw z1l--z10r--z10l--subpath(t,0) of upper_arc--cycle; % upper barb
+ t:=xpart(lower_arc intersectiontimes(z9r--z8'));
+ filldraw z8r--z9l--z9r--subpath(t,1) of lower_arc--cycle; % lower barb
+@z [the same change goes into file CSCSPU.MF, the program for two S's]
+@x in file ROMANL.MF, program for "s"
+ x10=x1l; top y10=top y2l; x9=x8r; bot y9=bot y7r;
+ x1l-x1'=x8'-x8r=1.6curve-fine; y1'=y1; y8'=y8;
+ numeric t; t=xpart(upper_arc intersectiontimes(z10--z1'));
+ filldraw z1l--z10--subpath(t,0) of upper_arc--cycle; % upper barb
+ t:=xpart(lower_arc intersectiontimes(z9--z8'));
+ filldraw z8r--z9--subpath(t,1) of lower_arc--cycle; % lower barb
+@y
+ pos10(.3[fine.breadth,cap_hair],0); pos9(.3[fine.breadth,cap_hair],0);
+ x10r=x1l; top y10=top y2l; x9l=x8r; bot y9=bot y7r;
+ x1l-x1'=x8'-x8r=1.6cap_curve-fine; y1'=y1; y8'=y8;
+ numeric t; t=xpart(upper_arc intersectiontimes(z10l--z1'));
+ filldraw z1l--z10r--z10l--subpath(t,0) of upper_arc--cycle; % upper barb
+ t:=xpart(lower_arc intersectiontimes(z9r--z8'));
+ filldraw z8r--z9l--z9r--subpath(t,1) of lower_arc--cycle; % lower barb
+@z
+
+12. Italic ampersand more robust
+@x in file ITALP
+ & pulled_arc.e(9,10)...{dir(theta+90)}z11e; % bowls, loop, and stem
+@y
+ & {{interim superness:=more_super; pulled_arc.e(9,10)}}
+ ..tension .9 and 1..{dir(theta+100)}z11e; % bowls, loop, and stem
+@z
+
+13. h_stroke more robust (esp in case of typewriter-style m)
+@x in file CMBASE
+ pickup fine.nib; pos@0(thin_join,180);
+@y
+ penpos@0(min(rt x$r-lft x$l,thin_join)-fine,180); pickup fine.nib;
+@z
+
+14. eliminate dishing in conflict with arm.
+Put a new subroutine into CMBASE.MF:
+def nodish_serif(suffix $,$$,@)(expr left_darkness,left_jut)
+ (suffix @@)(expr right_darkness,right_jut) suffix modifier =
+ serif($,$$,@,left_darkness,-left_jut) modifier;
+ serif($,$$,@@,right_darkness,right_jut) modifier; enddef;
+
+Now use this instead of dish_serif in the following places:
+GREEKU: Gamma, upper serif
+ Pi, both upper serifs (fix erroneous comments!)
+SYMBOL: Amalgamation, both lower serifs (fix erroneous comments!)
+ROMANU: B, both serifs
+ D, both serifs
+ E, both serifs
+ F, upper serif
+ L, lower serif
+ P, upper serif
+ R, upper serif
+ T, upper bracketing
+ROMSPU: AE, upper and lower middle serifs
+
+15. Change #2 wasn't quite good enough!
+@x in CMBASE
+ min_Vround:=max(fine.breadth,crisp.breadth,tiny.breadth);
+@y
+ min_Vround:=max(fine.breadth,crisp.breadth,tiny.breadth);
+ if min_Vround<vround min_Vround: min_Vround:=vround min_Vround; fi
+ if flare<vround flare: flare:=vround flare; fi
+@z
+
+16. body_depth# is now called paren_depth#, since body_depth is
+computed differently. (This is a cosmetic change only; it affects
+files CMBASE, PUNCT, SYM, SYMBOL, and ROMMS.)
+
+17. more robust Sigma
+@x in GREEKU, page 9
+top y1=h; bot y2=h-slab; bot y4=0; x3l-x1l=4/11(w-2u); y3=.5h;
+@y
+top y1=h; bot y2=h-slab-eps; bot y4=0; x3l-x1l=4/11(w-2u); y3=.5h;
+@z
+@x in BIGOP, page 7
+top_arm_thickness=rule_thickness;
+@y
+top_arm_thickness=Vround rule_thickness;
+@z
+@x in BIGOP, page 7 (this change needed twice)
+top y1=0; bot y2=-top_arm_thickness; bot y4=-d; y3=-.5d;
+@y
+top y1=0; bot y2=-top_arm_thickness-eps; bot y4=-d; y3=-.5d;
+@z
+
+18. yet another refinement of change #2
+@x in CMBASE
+ forsuffixes $=vair,bar,slab,cap_bar,cap_band,vair',vstem,cap_vstem:
+@y
+ forsuffixes $=vair,bar,slab,cap_bar,cap_band,vair',vstem,cap_vstem,bold:
+@z
+
+19. slightly more robust `k'
+@x in ROMANL
+top y3=x_height; rt x3r=hround(r-letter_fit-.7u-right_jut);
+bot y6=0; rt x6r=hround(r-letter_fit-.3u-right_jut);
+@y
+top y3=x_height; rt x3r=hround(r-letter_fit-.7u-right_jut)+eps;
+bot y6=0; rt x6r=hround(r-letter_fit-.3u-right_jut)+eps;
+@z
+
+20. small parameter changes for consistency
+@x in CMBX6
+dish#:=.9/36pt#; % amount erased at top or bottom of serifs
+@y
+dish#:=.8/36pt#; % amount erased at top or bottom of serifs
+@z
+@x in CMBX5
+dish#:=.75/36pt#; % amount erased at top or bottom of serifs
+@y
+dish#:=.7/36pt#; % amount erased at top or bottom of serifs
+@z
+@x in CMSS9 and CMSSI9 [these two lines are not consecutive in the files!]
+notch_cut#:=23/36pt#; % maximum breadth above or below notches
+cap_notch_cut#:=29/36pt#; % max breadth above/below uppercase notches
+@y
+notch_cut#:=17/36pt#; % maximum breadth above or below notches
+cap_notch_cut#:=24/36pt#; % max breadth above/below uppercase notches
+@z
+@x in CMSS8 and CMSSI8 [these two lines are not consecutive in the files!]
+notch_cut#:=21/36pt#; % maximum breadth above or below notches
+cap_notch_cut#:=27/36pt#; % max breadth above/below uppercase notches
+@y
+notch_cut#:=16/36pt#; % maximum breadth above or below notches
+cap_notch_cut#:=22/36pt#; % max breadth above/below uppercase notches
+@z
+@x in CMTI9
+cap_hair#:=10/36pt#; % uppercase hairline breadth
+@y
+cap_hair#:=11/36pt#; % uppercase hairline breadth
+@z
+@x in CMTI8
+cap_hair#:=9.5/36pt#; % uppercase hairline breadth
+@y
+cap_hair#:=11/36pt#; % uppercase hairline breadth
+@z
+@x in CMTI7
+cap_hair#:=9/36pt#; % uppercase hairline breadth
+@y
+cap_hair#:=10.5/36pt#; % uppercase hairline breadth
+@z
+@x in CMTCSC10
+lower.cap_notch_cut#:=24pt#; % max breadth above/below uppercase notches
+@y
+lower.cap_notch_cut#:=24/36pt#; % max breadth above/below uppercase notches
+@z
+
+21. New parameter file CMFI10 was added; font_identifier is "CMFI".
+Also changed the name `cmit10' to `cmitt10'; font_identifier is "CMITT".
+
+22. Still more changes for robustness (based on resolution h120 x v108).
+@x in ROMAND and ITALD, "5"
+bot y5=vround(.53h-vair); top y6r=vround .61803h+o;
+@y
+bot y5=vround(.53h-vair); top y6r=max(vround .61803h+o,top y6r+y5+eps-y6l);
+@z
+@x in OLDDIG, "5"
+bot y5=vround(.53[-d,h]-vair); top y6r=(vround .61803[-d,h])+o;
+@y
+bot y5=vround(.53[-d,h]-vair);
+top y6r=max((vround .61803[-d,h])+o,top y6r+y5+eps-y6l);
+@z
+@x in ROMANU, "C"
+ bot y1=vround max(.6h,x_height-.5vair); y5=good.y .95(h-y1);
+@y
+ bot y1=min(vround max(.6h,x_height-.5vair),bot y2l-eps);
+ y5=max(good.y .95(h-y1),y4l+eps);
+@z
+@x in ROMANU, "G"
+ bot y1=vround max(.6h,x_height-.5vair);
+@y
+ bot y1=min(vround max(.6h,x_height-.5vair),bot y2l-eps);
+@z
+@x in GREEKL page 6 and TSETSL page 8
+y1=.9h; top y2l=h+oo; top y8r=x_height+oo; y4=y8;
+@y
+top y2l=h+oo; y1=min(.9h,y2r-eps); top y8r=x_height+oo; y4=y8;
+@z
+@x in GREEKL page 16
+x6=x7=w-2u; x8=.5w+.5u; y6=-.3d; y8=-3/4d-oo; bot y7r=-d-oo;
+@y
+x6=x7=w-2u; x8=.5w+.5u; y6=-.3d; bot y7r=-d-oo; y8=max(-3/4d-oo,y7l);
+@z
+@x in ITALL page 12, ITALMS page 6, ITALSP page 4
+lft x5r=hround-.5u; x4=1/3(w-u);
+@y
+x4=1/3(w-u); lft x5r=min(hround-.5u,lft x5r+x4-x5l-eps);
+@z
+@x in ROMANL, "g"
+x11=x13=.5w; bot y13r=-d-oo-1; x14=w-x12; z10'l=z10l;
+@y
+x11=x13=max(.5w,x10+eps); bot y13r=-d-oo-1; x14=w-x12; z10'l=z10l;
+@z
+@x in ROMANL, "s"
+ filldraw stroke z1e..tension.9..{left}z2e; % upper arc and terminal
+ filldraw stroke z7e{left}..z8e; fi % lower arc and terminal
+@y
+ filldraw stroke term.e(2,1,right,.9,4); % upper arc and terminal
+ filldraw stroke term.e(7,8,left,1,4); fi % lower arc and terminal
+@z
+@x in ROMANL, "c"
+ filldraw stroke pulled_super_arc.e(2,3)(.7superpull)
+ & pulled_super_arc.e(3,4)(.5superpull)
+ ..tension .9 and 1..z5e; fi % arc and lower terminal
+@y
+ forsuffixes e=l,r: path p.e; p.e=z4e{right}..tension .9 and 1..z5e;
+ if angle direction 1 of p.e>75:
+ p.e:=z4e{right}..tension atleast.9 and 1..{dir 75}z5e; fi endfor
+ filldraw stroke pulled_super_arc.e(2,3)(.7superpull)
+ & pulled_super_arc.e(3,4)(.5superpull) & p.e; fi % arc and lower terminal
+@z
+@x in ROMSPL, page 5
+lft x8r=hround(stem_edge+.5u+1); x7=max(x8l+eps,.4[lft x8r,x6]);
+@y
+lft x8r=min(hround(stem_edge+.5u+1),lft x8r+x6r-2eps-x8l);
+x7=max(x8l+eps,.4[lft x8r,x6]);
+@z
+@x in ROMAND and OLDDIG, "2"
+ lft x1r=hround .75u; bot y1l=vround .7h; y1r:=good.y y1r; x1l:=good.x x1l;
+@y
+ lft x1r=hround .75u; bot y1l=vround .7h; y1r:=good.y y1r+eps; x1l:=good.x x1l;
+@z
+@x in SYMBOL, page 42 (Fraktur R)
+z=(z20{up}...{right}z21)intersectionpoint(z22l--z22l+(h,0) rotated theta);
+@y
+path p; p=z20{up}...(z21--(w,y21));
+z=p intersectionpoint(z22l--z22l+(h,0) rotated theta);
+@z
+
+23. Another consequence of change 2:
+"vround" should be "Vround" in the BIGDEL programs for extensible arrows
+(five times).
+Also five times in the BIGOP programs for summation, product, coproduct.
+
+24. Cosmetic change to bring CMBASE in line with PLAIN.
+@x in CMBASE.MF
+def pickup secondary q =
+ if numeric q: currentpen:=pen_[q];
+ pen_top:=pen_top_[q]; pen_bot:=pen_bot_[q];
+ pen_lft:=pen_lft_[q]; pen_rt:=pen_rt_[q];
+ currentpen_path:=pen_path_[q];
+ if known breadth_[q]: currentbreadth:=breadth_[q]; fi
+ else: currentpen:=q yscaled aspect_ratio;
+ pen_top:=(ypart penoffset left of currentpen)_o_;
+ pen_bot:=(ypart penoffset right of currentpen)_o_;
+ pen_lft:=xpart penoffset down of currentpen;
+ pen_rt:=xpart penoffset up of currentpen;
+ path currentpen_path;
+ fi enddef;
+@y
+def numeric_pickup_ primary q =
+ currentpen:=pen_[q];
+ pen_lft:=pen_lft_[q]; pen_rt:=pen_rt_[q];
+ pen_top:=pen_top_[q]; pen_bot:=pen_bot_[q];
+ currentpen_path:=pen_path_[q];
+ if known breadth_[q]: currentbreadth:=breadth_[q]; fi enddef;
+@z
+
+25. false italic correction in lambda of texset fonts (Feb 25, 1986)
+@x in TEXSET
+mode_setup; font_setup;
+@y
+mode_setup; font_setup; mono_charic#:=0;
+@z
+@x ibid
+slant:=mono_charic#:=0; % the remaining characters will not be slanted
+@y
+slant:=0; % the remaining characters will not be slanted
+@z
+[now remove the occurrences of `charic:=0;' in TSETSL, as they are unnecessary]
+
+*** The above changes are incorporated into the published book,
+*** Computer Modern Typefaces; this is "Version 2" (released March 5, 1986).
+ Changes subsequent to `Version 2' as published in C&T, Volume E:
+
+@x in GREEKU
+numeric shaved_stem; shaved_stem=hround .9[vair,.85cap_stem];
+@y
+numeric shaved_stem; shaved_stem=hround .9[vair,.85cap_stem];
+if shaved_stem<crisp.breadth: shaved_stem:=crisp.breadth; fi
+@z
+
+@x in CMSS9 [this affects the TFM file to a small extent!]
+fig_height#:=236/36pt#; % height of numerals
+@y
+fig_height#:=212/36pt#; % height of numerals
+@z
+
+@x in CMSSI9 [this affects the TFM file in ten hts and eleven italcorrs]
+fig_height#:=236/36pt#; % height of numerals
+@y
+fig_height#:=212/36pt#; % height of numerals
+@z
+
+@x in CMR17 [no change to TFM file]
+curve#:=41/36pt#; % lowercase curve breadth
+@y
+curve#:=40/36pt#; % lowercase curve breadth
+@z
+@x
+cap_stem#:=40/36pt#; % uppercase stem breadth
+cap_curve#:=48/36pt#; % uppercase curve breadth
+@y
+cap_stem#:=41/36pt#; % uppercase stem breadth
+cap_curve#:=47/36pt#; % uppercase curve breadth
+@z
+@x
+serif_drop#:=17/36pt#; % vertical drop of sloped serifs
+@y
+serif_drop#:=7/36pt#; % vertical drop of sloped serifs
+@z
+
+@x in ROMAND [this fixes the `disappearing hairline' in some lowres 8's]
+ lower_side=hround(.5[hair,stem]+stem_corr);
+@y
+ lower_side=hround(.5[hair,stem]+stem_corr);
+ if lower_side>1.2upper_side: upper_side:=lower_side; fi
+@z
+% Note: the SAME change should also be made in files ITALD and OLDDIG
+
+@x in ITALL [this fixes italic ell especially at low resolutions]
+top y1=h; x1=x2; filldraw stroke z1e--z2e; % stem
+@y
+top y1=h; x1=x2; filldraw stroke z1e--z2'e; % stem
+@z
+
+@x in SYM, the plus-or-minus character
+x1=x2=.5w; lft x3=lft=x5=hround u-eps; x4=x6=w-x3;
+@y
+x1=x2=.5w; lft x3=lft x5=hround u-eps; x4=x6=w-x3;
+@z actually the code worked but it was "infelicitous"
+
+@x in SYMBOL, the minus-or-plus character
+x1=x2=.5w; lft x3=lft=x5=hround u-eps; x4=x6=w-x3;
+@y
+x1=x2=.5w; lft x3=lft x5=hround u-eps; x4=x6=w-x3;
+@z actually the code worked but it was "infelicitous"
+
+@x in ROMANU, letter J [fixes a bug if dish=0 and crisp<tiny and serifs]
+ bulb(3,4,5); % bulb
+@y
+ pickup tiny.nib; bulb(3,4,5); % bulb
+@z
+
+@x in ROMANL, letter w [makes notch_cut more useful]
+ else: fill diag_end(6r,5r,1,1,5l,6l)--.5[z5l,z6l]
+ --.5[z5r,z6r]--cycle;% middle stem
+@y
+ else: fill diag_end(6r,5r,1,1,5l,6l)--.9[z5l,z6l]
+ ..{z5-z6}.1[z5r,z6r]--cycle; % middle stem
+@z the same change applies also to letter W in ROMANU
+
+@x in CMBASE, makes lowres types (especially TT) look better
+ define_blacker_pixels(notch_cut,cap_notch_cut);
+@y
+ define_blacker_pixels(notch_cut,cap_notch_cut);
+ forsuffixes $=notch_cut,cap_notch_cut: if $<3: $:=3; fi endfor
+@z
+
+@x in BIGOP, the \displaystyle coproduct sign
+lft x11=hround u; x1l-x11=x2l-x12=x22-x2r=hround cap_jut;
+@y
+lft x11=hround u; x1l-x11=x2l-x12=x22-x2r=hround 1.6cap_jut;
+@z
+
+@x in ROMANL, the letter m
+lft x1l=hround(2.5u-.5stem); x1l=x1'l=x2l=x2'l;
+lft x3l=hround(.5w-.5stem); x5-x3=x3-x1;
+if not monospace: r:=hround(x5+x1)-l; fi % change width for better fit
+@y
+lft x1l=hround(2.5u-.5stem); x1l=x1'l=x2l=x2'l; % stem, sic
+lft x3l=hround(.5w-.5mfudged.stem); x5-x3=x3-x1;
+if not monospace: r:=hround(x5+x1)+r-w; fi % change width for better fit
+@z
+
+@x a new routine for CMBASE, following change_width
+@y
+def center_on(expr x) = if not monospace: % change width for symmetric fit
+ r:=r+2x-w; w:=2x; fi enddef;
+@z
+@x in SYMBOL, the elementary division operator
+x3-.5dot_size=hround(.5w-.5dot_size); w:=r:=2x3;
+@y
+x3-.5dot_size=hround(.5w-.5dot_size); center_on(x3);
+@z
+Similarly, whenever the construction "w:=r:=2x*" appears, change it to
+"center_on(x*)". This happens in the programs for elementary division operator
+(as noted above), large triangle, large inverted triangle, lattice top,
+lattice bottom, dagger mark, double dagger mark, club/diamond/heart/spade suit
+(all in SYMBOL), plus the diamond operator and universal quantifier in SYM.
+The following additional change needs to be made in the programs
+for lattice top and lattice bottom:
+@x
+x1=x2=good.x .5w; center_on(x1); lft x3=hround u; x4=r-x3;
+@y
+x1=x2=good.x .5w; center_on(x1); lft x3=hround u; x4=w-x3;
+@z
+
+@x in SYMBOL, at end of zero-width slash
+labels(1,2); zero_width; endchar;
+@y
+labels(5,6); zero_width; endchar;
+@z
+
+@x in ROMANU, the letter Q (this change simply labels point 8 on proofs)
+math_fit(-.3cap_height#*slant-.5u#,ic#); penlabels(1,2,3,4,5,6,7); endchar;
+@y
+math_fit(-.3cap_height#*slant-.5u#,ic#);
+penlabels(1,2,3,4,5,6,7,8); endchar;
+@z
+
+@x in ROMANL, the letter i (this change by Jonathan Kew makes the dot rounder)
+if serifs: x3r=max(x1r,x1+.5(dot_diam-tiny)-.2jut) else: x3=x1-.5 fi;
+@y
+if serifs: x3r=max(x1r,hround(x1+.5dot_diam-.2jut)-.5tiny)
+else: x3=x1-.5 fi;
+@z
+
+-----------Improvements made in January 1992
+[No change to TFM files]
+@x in cmbase (improve lowres as suggested by John Hobby)
+def normal_adjust_fit(expr left_adjustment,right_adjustment) =
+ l:=-hround(left_adjustment*hppp)-letter_fit;
+ interim xoffset:=-l;
+ charwd:=charwd+2letter_fit#+left_adjustment+right_adjustment;
+ r:=l+hround(charwd*hppp)-shrink_fit;
+ w:=r-hround(right_adjustment*hppp)-letter_fit;
+ enddef;
+
+def mono_adjust_fit(expr left_adjustment,right_adjustment) =
+ numeric expansion_factor;
+ mono_charwd#=2letter_fit#
+ +expansion_factor*(charwd+left_adjustment+right_adjustment);
+ forsuffixes $=u,jut,cap_jut,beak_jut,apex_corr:
+ $:=$.#*expansion_factor*hppp; endfor
+ l:=-hround(left_adjustment*expansion_factor*hppp)-letter_fit;
+ interim xoffset:=-l;
+ r:=l+mono_charwd-shrink_fit;
+ w:=r-hround(right_adjustment*expansion_factor*hppp)-letter_fit;
+ charwd:=mono_charwd#; charic:=mono_charic#;
+ enddef;
+@y
+def do_expansion(expr expansion_factor) =
+ forsuffixes $=u,jut,cap_jut,beak_jut,apex_corr:
+ $:=$.#*expansion_factor*hppp; endfor
+enddef;
+
+def normal_adjust_fit(expr left_adjustment,right_adjustment) =
+ numeric charwd_in; charwd_in=charwd;
+ l:=-hround(left_adjustment*hppp)-letter_fit;
+ interim xoffset:=-l;
+ charwd:=charwd+2letter_fit#+left_adjustment+right_adjustment;
+ r:=l+hround(charwd*hppp)-shrink_fit;
+ w:=r-hround(right_adjustment*hppp)-letter_fit;
+ do_expansion(w/(charwd_in*hppp));
+ enddef;
+
+def mono_adjust_fit(expr left_adjustment,right_adjustment) =
+ numeric charwd_in; charwd_in=charwd;
+ numeric expansion_factor;
+ mono_charwd#=2letter_fit#
+ +expansion_factor*(charwd+left_adjustment+right_adjustment);
+ l:=-hround(left_adjustment*expansion_factor*hppp)-letter_fit;
+ interim xoffset:=-l;
+ r:=l+mono_charwd-shrink_fit;
+ w:=r-hround(right_adjustment*expansion_factor*hppp)-letter_fit;
+ charwd:=mono_charwd#; charic:=mono_charic#;
+ do_expansion(w/(charwd_in*hppp));
+ enddef;
+@z
+@x in SYM, Downward arrow
+pos3(bar,90); pos4(bar,90);
+lft x1l=hround(.5w-.5rule_thickness); y1+.5rule_thickness=h;
+x0=x1=x2; bot y0=-d; x0-x3=x4-x0=3u+eps;
+y3=y4=y0+.24asc_height+eps;
+pos5(bar,angle(z4-z0)); z5l=z0;
+pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+@y (make arrowheads heavier, as suggested by Zapf)
+pos3(rule_thickness,90); pos4(rule_thickness,90);
+lft x1l=hround(.5w-.5rule_thickness); y1+.5rule_thickness=h;
+x0=x1=x2; bot y0=-d; x0-x3=x4-x0=if monospace:3u else:4u fi+eps;
+y3=y4=y0+if monospace:.24 else:.36 fi asc_height+eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.2[.5[z3,z4],z0];
+@z
+@x in SYM, Upward arrow
+pos3(bar,90); pos4(bar,90);
+lft x1l=hround(.5w-.5rule_thickness); y1-.5rule_thickness=-d;
+x0=x1=x2; top y0=h; x0-x3=x4-x0=3u+eps;
+y3=y4=y0-.24asc_height-eps;
+pos5(bar,angle(z4-z0)); z5l=z0;
+pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+@y
+pos3(rule_thickness,90); pos4(rule_thickness,90);
+lft x1l=hround(.5w-.5rule_thickness); y1-.5rule_thickness=-d;
+x0=x1=x2; top y0=h; x0-x3=x4-x0=if monospace:3u else:4u fi+eps;
+y3=y4=y0-if monospace:.24 else:.36 fi asc_height-eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.2[.5[z3,z4],z0];
+@z
+@x in SYM, Leftward arrow
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y3-y0=y0-y4=.24asc_height+eps; x3=x4=x0+3u+eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+@y
+pos1(rule_thickness,90); pos2(rule_thickness,90);
+pos3(rule_thickness,0); pos4(rule_thickness,0);
+y0=y1=y2=math_axis; x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y3-y0=y0-y4=if monospace:.24 else:.36 fi asc_height+eps;
+x3=x4=x0+if monospace:3u else:4u fi+eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.2[.5[z3,z4],z0];
+@z
+@x in SYM, Rightward arrow
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y3-y0=y0-y4=.24asc_height+eps; x3=x4=x0-4u-eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+@y
+pos1(rule_thickness,90); pos2(rule_thickness,90);
+pos3(rule_thickness,0); pos4(rule_thickness,0);
+y0=y1=y2=math_axis; x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y3-y0=y0-y4=if monospace:.24 else:.36 fi asc_height+eps;
+x3=x4=x0-if monospace:3u else:4u fi-eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.2[.5[z3,z4],z0];
+@z
+@x in SYM, Left-and-right arrow
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis if monospace:+vround.3asc_height fi; lft x0=hround u;
+if monospace: x1+.5rule_thickness=hround(w-u) else: x1=.5w fi;
+y3-y0=y0-y4=.24asc_height+eps; x3=x4=x0+3u+eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+@y
+pos1(rule_thickness,90); pos2(rule_thickness,90);
+pos3(rule_thickness,0); pos4(rule_thickness,0);
+y0=y1=y2=math_axis if monospace:+vround.3asc_height fi; lft x0=hround u;
+if monospace: x1+.5rule_thickness=hround(w-u) else: x1=.5w fi;
+y3-y0=y0-y4=if monospace:.24 else:.36 fi asc_height+eps;
+x3=x4=x0+if monospace:3u else:4u fi+eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.2[.5[z3,z4],z0];
+@z
+@x ibid
+pos11(rule_thickness,90); pos12(rule_thickness,90); pos13(bar,0); pos14(bar,0);
+y10=y11=y12=math_axis if monospace:-vround.3asc_height fi;
+rt x10=hround(w-u);
+if monospace: x11-.5rule_thickness=hround u else: x11=.5w fi;
+y13-y10=y10-y14=.24asc_height+eps; x13=x14=x10-3u-eps;
+pos15(bar,angle(z14-z10)); z15l=z10; pos16(bar,angle(z13-z10)); z16l=z10;
+z19=.381966[.5[z13,z14],z10];
+@y
+pos11(rule_thickness,90); pos12(rule_thickness,90);
+pos13(rule_thickness,0); pos14(rule_thickness,0);
+y10=y11=y12=math_axis if monospace:-vround.3asc_height fi;
+rt x10=hround(w-u);
+if monospace: x11-.5rule_thickness=hround u else: x11=.5w fi;
+y13-y10=y10-y14=if monospace:.24 else:.36 fi asc_height+eps;
+x13=x14=x10-if monospace:3u else:4u fi-eps;
+pos15(rule_thickness,angle(z14-z10)); z15l=z10;
+pos16(rule_thickness,angle(z13-z10)); z16l=z10;
+z19=.2[.5[z13,z14],z10];
+@z
+@x in SYMBOL, Up-and-down arrow
+pos3(bar,90); pos4(bar,90);
+lft x1l=hround(.5w-.5rule_thickness); y1=.5[-d,h];
+x0=x1=x2; bot y0=-d-o; x0-x3=x4-x0=3u+eps;
+y3=y4=y0+.24asc_height+eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+@y
+pos3(rule_thickness,90); pos4(rule_thickness,90);
+lft x1l=hround(.5w-.5rule_thickness); y1=.5[-d,h];
+x0=x1=x2; bot y0=-d-o; x0-x3=x4-x0=4u+eps;
+y3=y4=y0+.36asc_height+eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.2[.5[z3,z4],z0];
+@z
+@x ibid
+pos13(bar,90); pos14(bar,90);
+x10=x11=x12; top y10=h+o; x10-x13=x14-x10=3u+eps;
+y13=y14=y10-.36asc_height-eps;
+pos15(bar,angle(z14-z10)); z15l=z10;
+pos16(bar,angle(z13-z10)); z16l=z10;
+z19=.381966[.5[z13,z14],z10];
+@y
+pos13(rule_thickness,90); pos14(rule_thickness,90);
+x10=x11=x12; top y10=h+o; x10-x13=x14-x10=4u+eps;
+y13=y14=y10-.24asc_height-eps;
+pos15(rule_thickness,angle(z14-z10)); z15l=z10;
+pos16(rule_thickness,angle(z13-z10)); z16l=z10;
+z19=.2[.5[z13,z14],z10];
+@z
+@x in SYMBOL, Northeast arrow
+pos3(bar,theta); pos4(bar,theta);
+y3=y0; x4=x0; x0-x3=y0-y4=delta+eps;
+pos5(bar,-90); z5l=z0; pos6(bar,-180); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+@y
+pos3(rule_thickness,theta); pos4(rule_thickness,theta);
+z3-z0=(-4u,.36asc_height) rotated theta;
+z4-z0=(-4u,-.36asc_height) rotated theta;
+pos5(rule_thickness,-90); z5l=z0; pos6(rule_thickness,-180); z6l=z0;
+z9=.2[.5[z3,z4],z0];
+@z
+@x in SYMBOL, Southeast arrow
+pos3(bar,theta); pos4(bar,theta);
+x3=x0; y4=y0; y3-y0=x0-x4=delta+eps;
+pos5(bar,180); z5l=z0; pos6(bar,90); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+@y
+pos3(rule_thickness,theta); pos4(rule_thickness,theta);
+z3-z0=(-4u,.36asc_height) rotated theta;
+z4-z0=(-4u,-.36asc_height) rotated theta;
+pos5(rule_thickness,180); z5l=z0; pos6(rule_thickness,90); z6l=z0;
+z9=.2[.5[z3,z4],z0];
+@z
+@x in SYMBOL, Northwest arrow
+pos3(bar,-180+theta); pos4(bar,-180+theta);
+x3=x0; y4=y0; x4-x0=y0-y3=delta+eps;
+pos5(bar,0); z5l=z0; pos6(bar,-90); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+@y
+pos3(rule_thickness,-180+theta); pos4(rule_thickness,-180+theta);
+z4-z0=(4u,.36asc_height) rotated theta;
+z3-z0=(4u,-.36asc_height) rotated theta;
+pos5(rule_thickness,0); z5l=z0; pos6(rule_thickness,-90); z6l=z0;
+z9=.2[.5[z3,z4],z0];
+@z
+@x in SYMBOL, Southwest arrow
+pos3(bar,-180+theta); pos4(bar,-180+theta);
+y3=y0; x4=x0; x3-x0=y4-y0=delta+eps;
+pos5(bar,90); z5l=z0; pos6(bar,0); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+@y
+pos3(rule_thickness,-180+theta); pos4(rule_thickness,-180+theta);
+z4-z0=(4u,.36asc_height) rotated theta;
+z3-z0=(4u,-.36asc_height) rotated theta;
+pos5(rule_thickness,90); z5l=z0; pos6(rule_thickness,0); z6l=z0;
+z9=.2[.5[z3,z4],z0];
+@z
+@x in ROMMS, Leftward top half arrow
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y3-y0=y0-y4=.24asc_height+eps; x3=x4=x0+3u+eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+@y
+pos1(rule_thickness,90); pos2(rule_thickness,90);
+pos3(rule_thickness,0); pos4(rule_thickness,0);
+y0=y1=y2=math_axis; x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0+4u+eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+@z
+@x in ROMMS, Leftward bottom half arrow
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y3-y0=y0-y4=.24asc_height+eps; x3=x4=x0+3u+eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+@y
+pos1(rule_thickness,90); pos2(rule_thickness,90);
+pos3(rule_thickness,0); pos4(rule_thickness,0);
+y0=y1=y2=math_axis; x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0+4u+eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+@z
+@x in ROMMS, Rightward top half arrow
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y3-y0=y0-y4=.24asc_height+eps; x3=x4=x0-3u-eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+@y
+pos1(rule_thickness,90); pos2(rule_thickness,90);
+pos3(rule_thickness,0); pos4(rule_thickness,0);
+y0=y1=y2=math_axis; x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0-4u-eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+@z
+@x in ROMMS, Rightward bottom half arrow
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y3-y0=y0-y4=.asc_height+eps; x3=x4=x0-3u-eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+@y
+pos1(rule_thickness,90); pos2(rule_thickness,90);
+pos3(rule_thickness,0); pos4(rule_thickness,0);
+y0=y1=y2=math_axis; x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0-4u-eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+@z
+@x in CALU, Calligraphic F
+top y1=top y6=h; z2=.5[z3,z1]+bend;
+bot y3=-o; y4=.1h; y5=y2; y7=.9h;
+draw flex(z1,z2,z3) softjoin (z3...{x4-x3,5(y4-y3)}z4); % stem
+draw z1-flourish_change{up}...z1---z6...{down}z7; % upper bar
+@y
+top y1=top y6=h; z2=.5[z3,z1]+1.2bend;
+bot y3=-o; y4=.1h; y5=y2; y7=.9h;
+draw flex(z1,z2,z3) softjoin (z3...{x4-x3,5(y4-y3)}z4); % stem
+draw z1-flourish_change{up}...(z1-(u,0))---z6...{down}z7; % upper bar
+@z
+@x in CALU, Calligraphic H
+lft x1=lft x3=0; rt x4=rt x6=.8w; rt x9=w;
+top y1=top y4=h; bot y3=-o; bot y6=bot_flourish_line; y9=y6+.1h;
+z2=.6[z3,z1]+bend; z5=.4[z6,z4]-2bend;
+path p[]; p1=flex(z1,z2,z3); p2=flex(z4,z5,z6);
+p3=(-w,.55h)--(2w,.55h);
+lft z7=p3 intersectionpoint p1; rt z8=p3 intersectionpoint p2;
+draw (z1-flourish_change{up}....z1-(u,0)---z1) softjoin p1; % left stem
+@y
+lft x1=lft x3=.5u; rt x4=rt x6=.8w-.6u; rt x9=w;
+top y1=top y4=h; bot y3=-.06h; bot y6=bot_flourish_line; y9=y6+.1h;
+z2=.6[z3,z1]+bend; z5=.4[z6,z4]-bend;
+path p[]; p1=flex(z1,z2,z3); p2=flex(z4,z5,z6);
+p3=(-w,.45h)--(2w,.45h);
+rt z7+2bend=p3 intersectionpoint p1; rt z8=p3 intersectionpoint p2;
+draw (z1-flourish_change-bend{curl2}....z1-(u,0)---z1) softjoin p1; % left stem
+@z
+@x in CALU, Calligraphic I
+lft x0=0; x1=.9w; x2=x4=.5w; x5=.2w; x6=.75w; rt x7=w;
+@y
+lft x0=0; x1=.9w; x2=x4=.5w; x5=.2w; x6=.8w; rt x7=1.05w;
+@z
+@x in CALU, Calligraphic T
+x1=x3=.5w; lft x4=0; x5=w-x6=.25w; rt x7=w;
+x1=.47w; x3=.5w; lft x4=0; x5=w-x6=.25w; rt x7=1.05w;
+z2=.5[z3,z1]+bend;
+x1-x8=x9-x1=2u; y8=y9=y3;
+z0=1/3[z1,z6];
+draw z0{left}...z2{down}...{left}z8; % stem
+draw z8--z9; % foot
+draw z4{up}...z5{right}...z6{right}...{up}z7; % arms
+math_fit(.5u#-5/7h#*slant,-u#); labels(1,2,3,4,5,6,7,8,9); endchar;
+@y
+x1=.47w; x3=.5w; lft x4=0; x5=.25w; x6=.85w; rt x7=1.05w;
+top y1=h; bot y3=-.1h; y4=3/4h; top y5=top y6=h; top y7=1.05h;
+z2=.3[z3,z1]+bend;
+top y0=y1; x0=x2;
+z8=(2u,.2h);
+draw z0---z2...z3; % stem
+draw z4{curl 2}...z5{right}...z6{right}...{up}z7; % arms
+math_fit(.5u#-5/7h#*slant,-u#); labels(1,2,3,4,5,6,7); endchar;
+@z
+@x in GREEKL, lowercase delta
+pos1(hair,-180); pos2(vair,-90);
+numeric theta; theta=angle(18u,-h);
+pos3(stem,theta+90); pos4(stem,theta+90); pos5(1/4[hair,stem],20);
+pos6(vair,-90); pos7(curve,-180); pos8(vair,-270);
+rt x1l=hround(w-2u+.5hair); x2=.5w; x3r=3u; rt x5r=hround(w-u);
+x4=x6=x8=.5w+.5u; lft x7r=hround(1.5u-.5curve);
+top y2l=h+oo; y1=min(.9h,y2r-eps); top y8r=x_height+oo; y4=y8;
+z4-z3=whatever*(18u,-h); y5=y7=.5[y6,y8]; bot y6=-oo;
+filldraw stroke z1e{x2-x1,3(y2-y1)}...z2e{left}...z3e---z4e
+ ....z5e{down}...pulled_arc.e(6,7) & pulled_arc.e(7,8); % hook and bowl
+math_fit(-.3x_height#*slant+.5curve#-u#,.7x_height#*slant-.5u#);
+penlabels(1,2,3,4,5,6,7,8); endchar;
+@y
+x0=-u; y0=1.1h;
+numeric light_flare; light_flare=2/3[vair,flare];
+x1=w-2u-.5light_flare; y1=h-.5light_flare;
+numeric theta; theta=angle (z1-z0);
+pos1(light_flare,theta-90); pos2(.2[vair,light_flare],-90); pos3(vair,theta);
+x2=x3+u; y2=h;
+x4=x6=.5w+.5u; top y8r=x_height+oo; z4=z8;
+pos6(vair,-90); pos7(stem,-180); pos8(vair,-270);
+pos4(stem,angle(z4-z0)+90); pos5(stem,30);
+z3=.5[.5[z1,z4],z0];
+y5+.1x_height=y7=.5[y6,y8]; bot y6=-oo;
+lft x7r=hround(1.4u-.5stem); rt x5r=hround(w-u);
+filldraw stroke z1e{z0-z1e}....z2e....z3e{(z0-z1)rotated 90}
+ ...z4e{z4e-.8[z4,z0]}
+ ....z5e{down}...pulled_arc.e(6,7) & pulled_arc.e(7,8); % hook and bowl
+filldraw z1r{z1r-z0}...z1l{z0-z1l}--cycle; % bulb
+math_fit(-.3x_height#*slant+.5curve#-u#,.7x_height#*slant-.5u#);
+penlabels(0,1,2,3,4,5,6,7,8); endchar;
+@z
+@x in TSETSL, lowercase delta for extended ASCII
+pos1(hair,-180); pos2(vair,-90);
+numeric theta; theta=angle(18u,-h);
+pos3(stem,theta+90); pos4(stem,theta+90); pos5(1/4[hair,stem],20);
+pos6(vair,-90); pos7(curve,-180); pos8(vair,-270);
+rt x1l=hround(w-2u+.5hair); x2=.5w; x3r=3u; rt x5r=hround(w-u);
+x4=x6=x8=.5w+.5u; lft x7r=hround(1.5u-.5curve);
+top y2l=h+oo; y1=min(.9h,y2r-eps); top y8r=x_height+oo; y4=y8;
+z4-z3=whatever*(18u,-h); y5=y7=.5[y6,y8]; bot y6=-oo;
+filldraw stroke z1e{x2-x1,3(y2-y1)}...z2e{left}...z3e---z4e
+ ....z5e{down}...pulled_arc.e(6,7) & pulled_arc.e(7,8); % hook and bowl
+penlabels(1,2,3,4,5,6,7,8); endchar;
+@y this next line differs from GREEKL, but the other lines agree
+x0=-u; y0=1.05h;
+numeric light_flare; light_flare=2/3[vair,flare];
+x1=w-2u-.5light_flare; y1=h-.5light_flare;
+numeric theta; theta=angle (z1-z0);
+pos1(light_flare,theta-90); pos2(.2[vair,light_flare],-90); pos3(vair,theta);
+x2=x3+u; y2=h;
+x4=x6=.5w+.5u; top y8r=x_height+oo; z4=z8;
+pos6(vair,-90); pos7(stem,-180); pos8(vair,-270);
+pos4(stem,angle(z4-z0)+90); pos5(stem,30);
+z3=.5[.5[z1,z4],z0];
+y5+.1x_height=y7=.5[y6,y8]; bot y6=-oo;
+lft x7r=hround(1.4u-.5stem); rt x5r=hround(w-u);
+filldraw stroke z1e{z0-z1e}....z2e....z3e{(z0-z1)rotated 90}
+ ...z4e{z4e-.8[z4,z0]}
+ ....z5e{down}...pulled_arc.e(6,7) & pulled_arc.e(7,8); % hook and bowl
+filldraw z1r{z1r-z0}...z1l{z0-z1l}--cycle; % bulb
+penlabels(0,1,2,3,4,5,6,7,8); endchar;
+@z
+
+----------- Changes made after the fourth printing (1993) of Volume E
+
+@x in CSCSPU, letter J [fixes a bug if dish=0 and crisp<tiny and serifs]
+ bulb(3,4,5); % bulb
+@y
+ pickup tiny.nib; bulb(3,4,5); % bulb
+@z
+
+@x in BIGDEL, two harmless bugs caught by Robert Hunt
+"Extensible vertical arrow--extension module";
+@y
+cmchar "Extensible vertical arrow--extension module";
+@z
+@x
+"Extensible double vertical arrow--extension module";
+@y
+cmchar "Extensible double vertical arrow--extension module";
+@z
+
+@x in GREEKU, letter Phi (hi-res glitch found by Robert Hunt)
+lft x1l=lft x2l=hround(.5w-.5cap_stem); top y1=h; bot y2=0;
+@y
+lft x1l=lft x2l=hround(.5w-.5shaved_stem); top y1=h; bot y2=0;
+@z
+@x in GREEKU, letter Psi similarly
+lft x1l=lft x2l=hround(.5w-.5cap_stem); top y1=h; bot y2=0;
+@y
+lft x1l=lft x2l=hround(.5w-.5shaved_stem); top y1=h; bot y2=0;
+@z
+
+@x in BIGOP, teststyle integral sign
+x5=.5[x4,x6]; x4-x6=1.2u; lft x5r=hround(.5w-.5stem);
+@y
+x5=.5[x4,x6]; x4-x6=1.2u; lft x5r=hround(.5w-.5curve);
+@z
+@x same, displaystyle integral sign
+x5=.5[x4,x6]; x4-x6=4.8u; lft x5r=hround(.5w-.5stem);
+@y
+x5=.5[x4,x6]; x4-x6=4.8u; lft x5r=hround(.5w-.5max_size);
+@z
+@x same, textstyle contour integral sign
+x5=.5[x4,x6]; x4-x6=1.2u; lft x5r=hround(.5w-.5stem);
+@y
+x5=.5[x4,x6]; x4-x6=1.2u; lft x5r=hround(.5w-.5curve);
+@z
+@x same, displaystyle contour integral sign
+x5=.5[x4,x6]; x4-x6=4.8u; lft x5r=hround(.5w-.5stem);
+@y
+x5=.5[x4,x6]; x4-x6=4.8u; lft x5r=hround(.5w-.5max_size);
+@z
+
+@x in ITALMS, the partial differential sign
+path p; p=pulled_super_arc.l(3,4)(pull);
+@y
+path p; {{interim superness:=more_super; p=pulled_super_arc.l(3,4)(pull)}};
+@z
+
+@x in SYMBOL, the elementary division operator
+beginarithchar(oct"004"); pickup rule.nib;
+x3-.5dot_size=hround(.5w-.5dot_size); center_on(x3);
+y3+.5dot_size=vround(math_axis+math_spread[.5x_height,.6x_height]+.5dot_size);
+@y note that pickup rule.nib leaves currentbreadth unchanged
+beginarithchar(oct"004"); pickup fine.nib; pickup rule.nib;
+numeric del; del=dot_size-currentbreadth; % currentbreadth=fine
+x3-.5del=good.x(.5w-.5del); center_on(x3);
+y3+.5del=good.y(math_axis+math_spread[.5x_height,.6x_height]+.5del);
+@z
+
+@x in CMBASE, compute_spread can force even spreads for better rounding
+ spread:=ceiling(spread#*hppp)+eps; enddef;
+@y
+ spread:=2ceiling(spread#*hppp/2)+eps; enddef;
+@z
+
+@x in CMBASE, better diag_ratio routine avoids overflow in hi-res chars
+ numeric a_,b_; b_=b/y; a_=a*a-b_*b_;
+ (a*(c++y*sqrt a_)-b_*c)/a_/y enddef;
+@y
+ numeric a_,b_,c_; b_=b/y; c_=c/y; a_=a*a-b_*b_;
+ (a*(c_++sqrt a_)-b_*c_)/a_ enddef;
+@z
+
+@x in ROMAND, numeral 3, allow hi-res cminch to run without overflow (MacKay)
+x4=1/3[x5,x3l]; z4=z5+whatever*(150u,h);
+@y
+x4=1/3[x5,x3l]; z4=z5+whatever*(15u,.1h);
+@z
+
+@x in ITALP, the Sterling sign (bug caught by Yannis Haralambous)
+lft x6r=hround u; x7=3u; x8=w-3.5u; rt x9r=hround(w-u);
+@y
+lft x6r=hround u; x7=3u; x8=w-3.5u; rt x9l=hround(w-u);
+@z
+
+@x in GREEKL, lowercase omega, I made minor changes for new edition of TAOCP:
+y1+.5hair=h; x1=x2+.75u; pos1(hair,angle(2(x1-x2),y1-y2)+90);
+@y
+y1+.5hair=h; x1=x2+.75u; pos1(hair+dw,angle(2(x1-x2),y1-y2)+90);
+@z
+@x
+x3=.5[x2,x4]; x7+.25u=.5[x6,x8]; rt x8r=hround(w-.5u);
+@y
+x3=.5[x2,x4]; x7-.25u=.5[x6,x8]; rt x8r=hround(w-.5u);
+@z
+
+@x in GREEKL, lowercase beta, I made minor changes for new edition of TAOCP:
+pos3(stem,0); pos4(vair,-90); pos5(hair,-180);
+pos6(vair,-270); pos7(curve,-360); pos8(vair,-450); pos9(hair,-540);
+x0=x1=x9; lft x0l=hround(1.5u-.5hair); x2=x4=x6=x8=.5w+.25u;
+rt x3r=hround(w-1.5u); rt x7r=hround(w-1.5u+.5curve); rt x5l=hround(x4-u);
+bot y0=-d; y1=top y6r=x_height; top y2r=h+oo; y3=.5[y2,y4];
+y5=.5[y4,y6]; top y6r-bot y4r=vstem+eps; bot y8=-oo; y7=y9=.5[y6,y8];
+@y (this change also is made in TSETSL)
+pos3(.8[hair,stem],0); pos4(vair,-90); pos5(hair,-180);
+pos6(vair,-270); pos7(stem,-360); pos8(vair,-450); pos9(hair,-540);
+x0=x1=x9; lft x0l=hround(1.5u-.5hair); x2=x4=x6=x8=.5w-.25u;
+rt x3r=hround(w-1.75u); rt x7r=hround(w-u); rt x5l=hround(x4-u);
+bot y0=-d; y1=top y6r=x_height; top y2r=h+oo; y3=.5[y2,y4];
+y5=.5[y4,y6]; top y6r-bot y4r=vstem+eps; bot y8=-oo; y7=y9=.55[y6,y8];
+@z
+
+@x in ROMANU, less overshoot on uppercase sans serif C (for Vol 2 of TAOCP)
+ top y1r=vround .95h+o; top y2r=h+o; y3=.5h;
+ bot y4r=-o; bot y5r=vround .08h-o; y5l:=good.y y5l; x5l:=good.x x5l;
+@y
+ top y1r=vround .95h+oo; top y2r=h+oo; y3=.5h;
+ bot y4r=-oo; bot y5r=vround .08h-oo; y5l:=good.y y5l; x5l:=good.x x5l;
+@z
+
+@x in ROMANU, likewise for G
+ top y1r=vround .93h+o; top y2r=h+o; y3=.5h;
+ bot y4r=-o; bot y5r=vround .07h-o;
+@y
+ top y1r=vround .93h+oo; top y2r=h+oo; y3=.5h;
+ bot y4r=-oo; bot y5r=vround .07h-oo;
+@z
+
+@x in SYMBOL, spade suit, a cosmetic change that doesn't affect any fonts:
+labels(1,2,3,5,6,10,11,12,13,14,15); endchar;
+@y
+labels(1,2,3,5,6,7,8,9,10,11,12,13,14,15); endchar;
+@z
+
+@x in CMBASE, sloped_serif.l never used $$ except rarely in letter p
+ y@0=max(y@2l-bracket,y$$)-eps;
+@y and in letter p, it was wrong to do so! (but not in any normal font)
+ y@0=y@2l-bracket-eps;
+@z
+@x sloped_serif.r never used $$ in any reasonable way at all
+ y@0=min(y@2l+bracket,y$$)+eps;
+@y
+ y@0=y@2l+bracket+eps;
+@z
+
+in ITALD, programs for 6 and 9: same change as above for partial derivative
+in OLDDIG, programs for 6 and 9: same change as above for partial derivative
+in ROMAND, programs for 6 and 9: same change as above for partial derivative
+in TSETSL, program for partial derivative: same change as above
+
+@x in ROMANL, slight change to cmss10 and cmssbx10 `g' improves meta-ness
+loop_top=Vround .77[vair,fudged.stem];
+@y
+loop_top=if serifs: Vround .77[vair,fudged.stem] else: vair fi;
+@z
+
+@x in CMBASE, recent change shouldn't clobber a_, b_, and c_ of plain.mf
+ numeric a_,b_,c_; b_=b/y; c_=c/y; a_=a*a-b_*b_;
+ (a*(c_++sqrt a_)-b_*c_)/a_ enddef;
+@y
+ numeric aa_,bb_,cc_; bb_=b/y; cc_=c/y; aa_=a*a-bb_*bb_;
+ (a*(cc_++sqrt aa_)-bb_*cc_)/aa_ enddef;
+@z
+
+@x in ITALMS, Arrow (vector) accent
+lft x1=hround .5u; x2=w-x1; y1=y2=good.x .7[x_height,asc_height];
+@y a correction of almost purely academic interest
+lft x1=hround .5u; x2=w-x1; y1=y2=good.y .7[x_height,asc_height];
+@z
+
+the following patches make the arrows more robust, but essentially unchanged
+@x in SYM, Downward arrow
+ --z2r---z1r..z1l---z2l--subpath (t,0) of\\(z3r{z9-z3}..z5r)
+@y
+ ---z1r..z1l---subpath (t,0) of\\(z3r{z9-z3}..z5r)
+@z
+@x in SYM, Upward arrow
+ --z2r---z1r..z1l---z2l--subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@y
+ ---z1r..z1l---subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@z
+@x in SYM, Leftward arrow
+ --z2l---z1l..z1r---z2r--subpath (t,0) of\\(z3r{z9-z3}..z5r)
+@y
+ ---z1l..z1r---subpath (t,0) of\\(z3r{z9-z3}..z5r)
+@z
+@x in SYM, Rightward arrow
+ --z2l---z1l..z1r---z2r--subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@y
+ ---z1l..z1r---subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@z
+@x in SYM, Left-and-right arrow
+ --z2l---z1l..z1r---z2r--subpath (t,0) of\\(z3r{z9-z3}..z5r)
+@y
+ ---z1l..z1r---subpath (t,0) of\\(z3r{z9-z3}..z5r)
+@z
+@x ibid
+ --z12l---z11l..z11r---z12r--subpath (t,0) of\\(z13l{z19-z13}..z15r)
+@y
+ ---z11l..z11r---subpath (t,0) of\\(z13l{z19-z13}..z15r)
+@z
+@x in SYMBOL, Up-and-down arrow
+ --z2r---z1r..z1l---z2l--subpath (t,0) of\\(z3r{z9-z3}..z5r)
+@y
+ ---z1r..z1l---subpath (t,0) of\\(z3r{z9-z3}..z5r)
+@z
+@x ibid
+ --z12r---z11r..z11l---z12l--subpath (t,0) of\\(z13l{z19-z13}..z15r)
+@y
+ ---z11r..z11l---subpath (t,0) of\\(z13l{z19-z13}..z15r)
+@z
+@x in SYMBOL, Northeast arrow
+ --z2l---z1l..z1r---z2r--subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@y
+ ---z1l..z1r---subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@z
+@x in SYMBOL, Southeast arrow
+ --z2l---z1l..z1r---z2r
+ --subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@y
+ ---z1l..z1r---subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@z
+@x in SYMBOL, Northwest arrow
+ --z2l---z1l..z1r---z2r--subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@y
+ ---z1l..z1r---subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@z
+@x in SYMBOL, Southwest arrow
+ --z2l---z1l..z1r---z2r
+ --subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@y
+ ---z1l..z1r---subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@z
+@x in ROMMS, Leftward top half arrow
+filldraw z0--(x0,y2l)---z1l..z1r---z2r
+ ..subpath (t,0) of\\(z3r..{2(x0-x3),y0-y3}z5r)
+@y
+filldraw z0--(x0,y2l)--z1l{right}..{left}z1r
+ --subpath (t,0) of\\(z3r..{2(x0-x3),y0-y3}z5r)
+@z
+@x in ROMMS, Leftward bottom half arrow
+filldraw z0{2(x4-x0),y4-y0}..z4l
+ --subpath (0,t) of\\(z4r..{2(x0-x4),y0-y4}z6r)
+@y
+ --z1l{right}..{left}z1r--(x0,y2r)--cycle; % arrowhead and stem
+@z
+@x in ROMMS, Rightward top half arrow
+filldraw z0--(x0,y2l)---z1l..z1r---z2r
+ ..subpath (t,0) of\\(z3l..{2(x0-x3),y0-y3}z5r)
+@y
+filldraw z0--(x0,y2l)--z1l{left}..{right}z1r
+ --subpath (t,0) of\\(z3l..{2(x0-x3),y0-y3}z5r)
+@z
+@x in ROMMS, Rightward bottom half arrow
+ ..z2l---z1l..z1r---(x0,y2r)--cycle; % arrowhead and stem
+@y
+ --z1l{left}..{right}z1r--(x0,y2r)--cycle; % arrowhead and stem
+@z
+@x in BIGDEL, Extensible vertical arrow--top
+ --z2r---z1r--z1l---z2l--subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@y
+ --z1r--z1l--subpath (t,0) of\\(z3l{z9-z3}..z5r)
+@z
+@x in BIGDEL, Extensible vertical arrow--bottom
+ --z2r---z1r--z1l---z2l--subpath (t,0) of\\(z3r{z9-z3}..z5r)
+@y
+ --z1r--z1l--subpath (t,0) of\\(z3r{z9-z3}..z5r)
+@z
+
+@x in ROMLIG, the ligature fi
+ x13=x11-.5; top y14r=min(2x_height,h+1); top y11=x_height;
+@y make the center of the dot consistent with ordinary i, in sans-serif fonts
+ x13=x11-.5; top y14r=min(10/7x_height+.5bulb_diam,h)+1; top y11=x_height;
+@z
+@x in ROMLIG, the ligature ffi
+ x23=x21-.5; top y24r=min(2x_height,h+1); top y21=x_height;
+@y same corrections as to fi
+ x23=x21-.5; top y24r=min(10/7x_height+.5bulb_diam,h)+1; top y21=x_height;
+@z
+
+@x in SYMBOL, the circle operator
+lft x6=hround u; x2=w-x6; top y8=h; y2=math_axis;
+@y this correction makes it a circle even at small size, low resolution
+lft x6=hround u; x2=w-x6; top y8=h; y8-y4=x2-x6;
+@z
+@x in SYMBOL, the bullet
+lft x6=hround u; x2=w-x6; top y8=h; y2=math_axis; circle_points;
+@y likewise
+lft x6=hround u; x2=w-x6; top y8=h; y8-y4=x2-x6; circle_points;
+@z
+
+@x in ROMAND, the numeral 2
+ ..z3e{down}.. z4e---z5e--z6e; % stroke
+@y it's more robust to avoid --- next to -- (in case of tiny loops)
+ ..z3e{down}..{z5e-z4e}z4e--z5e--z6e; % stroke
+@z
+@x in OLDDIG, oldstyle numeral 2
+ ..z3e{down}.. z4e---z5e--z6e; % stroke
+@y likewise
+ ..z3e{down}..{z5e-z4e}z4e--z5e--z6e; % stroke
+@z
+
+@x in CSC, after the second font_setup
+extra_endchar:=extra_endchar&"charcode:=charcode+code_offset";
+@y
+extra_endchar:=extra_endchar&"charcode:=charcode+code_offset;";
+@z
+
+@x in ACCENT, in program for Breve
+numeric mid_thickness; mid_thickness=vround 1/3[vair,stem];
+@y
+numeric mid_thickness; mid_thickness=Vround 1/3[vair,stem];
+@z
+
+@x in CMBASE, a more robust arm routine fixes the 7 in cmbx5,6,7,8
+ filldraw z$$l{z@1-z$$l}...darkness[z@1,.5[z@2,z$$l] ]...z@2
+ ---z$l--z$r--z@0--z$$r--cycle; % arm and beak
+@y
+ path p_; p_= z$$l{z@1-z$$l}...darkness[z@1,.5[z@2,z$$l] ]...z@2
+ ---z$l--z$r--z@0--z$$r--cycle;
+ if (y$$>y$) <> (ypart precontrol 1 of p_ > ypart postcontrol 1 of p_):
+ p_:=z$$l{z@1-z$$l}...darkness[z@1,.5[z@2,z$$l] ]
+ ---z$l--z$r--z@0--z$$r--cycle; fi
+ filldraw p_; % arm and beak
+@z
+
+@x in CALU, I decided in July 2005 that I really wanted a less swashy F
+top y1=top y6=h; z2=.5[z3,z1]+1.2bend;
+bot y3=-o; y4=.1h; y5=y2; y7=.9h;
+draw flex(z1,z2,z3) softjoin (z3...{x4-x3,5(y4-y3)}z4); % stem
+draw z1-flourish_change{up}...(z1-(u,0))---z6...{down}z7; % upper bar
+@y
+top y1=top y6=h; z2=.5[z3,z1]+bend;
+bot y3=-o; y4=.1h; y5=y2; y7=.9h;
+draw flex(z1,z2,z3) softjoin (z3...{x4-x3,5(y4-y3)}z4); % stem
+draw z1-flourish_change+(0,.15asc_height){up}...{right}(z1-(2u,0))
+ ---z6...{down}z7; % upper bar
+@z
+
+@x in SYMBOL, two points of Hardy's asymptotic equivalence sign were unlabeled
+labels(1,2,3,4); endchar;
+@y
+labels(1,2,3,4,5,6); endchar;
+@z
+
+@x in GREEKL, correct a tiny notch that can show up at hires (Charles Duan)
+filldraw z1l--z2l--z--z1r--cycle; % stem
+@y
+filldraw z1l--z2l...(x3,y2l)...z--z1r--cycle; % stem
+@z
+
+@x in GREEKL, make almost-invisible semantic corrections to beta
+x0=x1=x9; lft x0l=hround(1.5u-.5hair); x2=x4=x6=x8=.5w-.25u;
+@y
+x0=x1=x9; lft x0r=hround(1.5u-.5hair); x2=x4=x6=x8=.5w-.25u;
+@z
+@x
+y5=.5[y4,y6]; top y6r-bot y4r=vstem+eps; bot y8=-oo; y7=y9=.55[y6,y8];
+@y
+y5=.5[y4,y6]; top y6r-bot y4r=vstem+eps; bot y8r=-oo; y7=y9=.55[y6,y8];
+@z
+@x and also to delta
+y5+.1x_height=y7=.5[y6,y8]; bot y6=-oo;
+@y
+y5+.1x_height=y7=.5[y6,y8]; bot y6r=-oo;
+@z
+@x and also to phi
+top y1=x_height+oo; y2=y4=.5[y1,y3]; bot y3=-oo;
+@y
+top y1r=x_height+oo; y2=y4=.5[y1,y3]; bot y3r=-oo;
+@z
+
+-----------Here I draw the line with respect to further changes
+
+(I sincerely believe there won't be any more!)
+
+I absolutely guarantee that the TFM files will never change again.
+(Otherwise I would consider zeroing the depth of italic 7,
+which I admit is strange... we can live with it.)
diff --git a/systems/knuth/dist/errata/errata.eight b/systems/knuth/dist/errata/errata.eight
new file mode 100644
index 0000000000..f7ceafd9f6
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.eight
@@ -0,0 +1,610 @@
+% Bugs (sigh) in Computers \& Typesetting
+
+\input manmac
+\font\sltt=cmsltt10
+\font\niness=cmss9
+\font\ninessi=cmssi9
+\proofmodefalse
+\raggedbottom
+\output{\hsize=29pc \onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\today{\number\day\
+ \ifcase\month\or
+ Jan\or Feb\or Mar\or Apr\or May\or Jun\or
+ Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi
+ \ \number\year}
+
+\def\cutpar{{\parfillskip=0pt\par}}
+
+\def\rhead{Bugs in {\tensl Computers \& Typesetting, 1992}}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\buginvol#1(#2) \par{\bigbreak\penalty-1000\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Volume #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\slMF{{\manual 89:;}\-{\manual <=>:}} % slant the logo
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\newcount\nn
+\newdimen\nsize \newdimen\msize \newdimen\ninept \ninept=9pt
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+
+\tenpoint
+\noindent This is a list of all corrections made to {\sl Computers \&
+Typesetting\/} between 15 March 1992 and the publication of the final
+printed versions of those books.
+Corrections made to the softcover version of {\sl The \TeX book\/} are
+the same as corrections to Volume~A\null. Corrections to the softcover
+version of {\sl The \slMF\kern1ptbook\/} are the same as corrections
+to Volume~C\null. Changes to Volume~B refer to the fourth printing
+(1991), which differs markedly from earlier printings because it includes
+all the revisions for \TeX3.0. Changes to Volume~D refer to the third
+printing (1991), which differs markedly from earlier printings because
+it includes all the revisions for \MF\kern1pt2.0. Changes to the mini-indexes
+and master indexes of Volumes B and~D are not shown here unless they are
+not obviously derivable from what has been shown.
+\looseness=-1
+
+ % volume A
+
+\bugonpage A23, line 14 (9/1/92)
+
+\tenpoint\noindent
+a command and you type `|tex|' or `|run| |tex|' or something like that.)
+
+\bugonpage A53, line 23 (7/7/92)
+
+\tenpoint\noindent
+{\sl scientiarum imperialis petropolitan\ae\/}
+became {\sl Akademi\t\i a Nauk SSSR, Doklady}.
+
+\bugonpage A146, line 2 from the bottom (2/25/93)
+
+|$\bigl|\|| |\||x|\||-|\||y|\|| \bigr|\||$|\hskip1.25in
+ $\bigl\vert\vert x\vert-\vert y\vert\bigr\vert$
+
+\bugonpage A149, lines 3--5 (2/25/93)
+
+\noindent
+example, we used |\bigl| and |\bigr| to produce $\bigl\vert\vert x\vert-
+\vert y\vert\bigr\vert$ in one of the previous illustrations; |\left| and
+|\right| don't make things any bigger than necessary, so
+`|$\left|\||\left|\||x\right|\||-\left|\||y\right|\||\right|\||$|'
+yields only `$\left\vert
+ \left\vert x\right\vert -\left\vert y\right\vert \right\vert$'.\cutpar
+
+\bugonpage A158, line 18 from the bottom (2/25/93)
+
+\ninepoint
+are four possibilities for each of these fields.
+A field can be
+
+\bugonpage A282, line 9 from the bottom (7/8/92)
+
+\ninepoint\noindent
+category~4) are intercepted by the alignment
+process, en route to \TeX's stomach, so\cutpar
+
+\bugonpage A293, new paragraph after line 15 (4/9/92)
+
+\ninepoint
+\textindent{$\bull$}|\unhbox|\<8-bit number>, |\unhcopy|\<8-bit number>.\enskip
+The specified box register must be void. Nothing happens.
+
+\bugonpage A309, line 23 (7/7/92)
+
+\ninepoint\noindent
+|petropolitan\ae\/} became {\sl Akademi\t\i a Nauk SSSR, Doklady}.|
+
+\bugonpage A320, line 11 (1/26/93)
+
+\ninepoint
+\ansno17.12:
+|$\bigl(x+f(x)\bigr) \big/ \bigl(x-f(x)\bigr)$|. \ Notice especially the\cutpar
+
+\bugonpage A349, second line from the bottom (7/8/92)
+
+\ninepoint\noindent
+expand to a ^\<number> en route to \TeX's
+``stomach''; |\multiply| wouldn't work, because\cutpar
+
+\bugonpage A358, bottom line (2/3/93)
+
+\ninepoint\noindent
+it is easy to define ^|\ldots| and ^|\cdots| macros that
+give the proper spacing in most\cutpar
+
+\bugonpage A370, lines 28 and 29 (9/1/92)
+
+\def\Russiantt#1{{\tt\hbox to.5em{\hss\eighttt\char#1\hss}}}
+\ninepoint\noindent
+example, if \TeX\ is implemented for
+a purely ^{Cyrillic} ^^{Russian} keyboard, the letter `\Russiantt5' should be
+assigned to code \oct{160} and `\Russiantt{`T}' to code \oct{164}, so that
+`\Russiantt5\Russiantt{`T}' still means `|pt|'; or else control\cutpar
+
+\bugonpage A377, lines 17--24 (5/4/92)
+
+\ninepoint
+\begintt
+\def\sanswitch{\let\n@xt\endsanity \ifx\next\endsanity
+ \else\ifcat\noexpand\next\stoken\aftergroup\space\let\n@xt=\eat
+ \else\ifcat\noexpand\next\bgroup\aftergroup{\let\n@xt=\eat
+ \else\ifcat\noexpand\next\egroup\aftergroup}\let\n@xt=\eat
+ \else\let\n@xt=\copytok\fi\fi\fi\fi \n@xt}
+\def\eat{\afterassignment\sanitize \let\next= }
+\long\def\copytok#1{\ifcat\noexpand#1\relax\aftergroup\noexpand\fi
+ \ifcat\noexpand#1\noexpand~\aftergroup\noexpand\fi
+\endtt
+
+\bugonpage A455, line 25 (2/26/93)
+
+\ninepoint\noindent
+rent language'' is set equal to |\language|. Whenever a
+character is added to the cur-\cutpar
+
+\bugonpage A459, second line of entry for ampersand (3/22/92)
+
+\eightpoint\indent\qquad
+{\it231--248}, $\underline{282}$, {\it339}, 344, 385--386, 428.
+
+\bugonpage A461, right column (2/19/93)
+
+\eightpoint
+\newbox\astbox \setbox\astbox=\hbox to0pt{\hss\lower1pt\hbox{*}}
+\def\prim#1{\par\indent\copy\astbox{\tt\char`\\#1}}
+\prim{chardef}, 44, 121, 155, 210, 214, 215, 271,\par
+\indent\qquad $\underline{277}$, 336, {\it343}, {\it345}, {\it356}, 452.\par
+\indent\<chardef token>, $\underline{271}$, 283, 286, 289.
+
+\bugonpage A467, left column (2/25/93)
+
+\eightpoint
+Greek, 127--128, 137, 156, 164, 319,\par
+\indent\qquad 358, 430, 434.
+
+\bugonpage A470, left column (2/25/93)
+
+\eightpoint
+margins, {\sl see\/} |\hoffset|, |\hsize|, |\narrower|.
+
+\bugonpage A471, left column (2/19/93)
+
+\eightpoint
+\prim{mathchardef}, 155, 199, 214, 215, 271,\par
+\indent\qquad 277, 289, 336, {\it358}, {\it394}.\par
+\indent\<mathchardef token>, $\underline{271}$, 289.
+
+\bugonpage A474, right column (3/22/92)
+
+\eightpoint
+pound sterling, 54, {\it339}, 428.
+
+\bugonpage A477, right column (3/22/92)
+
+\eightpoint
+sterling, 54, {\it339}, 428.
+
+\bugonpage A480, left column (4/9/92)
+
+\eightpoint
+\prim{unhbox}, 120, 283, $\underline{285}$, 293, {\it354}, {\it356},
+ {\it399}.\par
+\prim{unhcopy}, 120, 283, $\underline{285}$, 293, {\it353}.
+
+\bugonpage A481, left column (2/25/93)
+
+\eightpoint
+whatsits, 95, 110, 157, 226--229, 455.
+
+\bugonpage A483, lines 15--21 (2/25/93)
+
+{\tt P.O. Box 869\par
+Santa Barbara, CA 93102-0869 USA.\par
+|}|}
+\smallskip\noindent
+Don't delay, write today! That number again is
+
+\smallskip
+{\obeylines
+\TeX\ Users Group
+P.O. Box 869
+Santa Barbara, CA 93102-0869 USA.
+}
+
+ % volume B
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage B2, line 10 from the bottom (2/27/93)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{banner}\equiv\hbox{\tt\char'23}$%
+{\tt This\]is\]TeX,\]Version\]3.1415\char'23}\quad
+$\{\,$printed when \TeX\ starts$\,\}$
+
+\bugonpage B89, line 12 (2/27/93)
+
+\tenpoint
+\noindent\hskip10pt
+In horizontal mode, the \\{prev\_graf} field is used for initial language data.
+
+\bugonpage B89, line 20 (2/27/93)
+
+\ninepoint
+\noindent\hskip20pt
+\\{pg\_field}, \\{ml\_field}: \\{integer}; \ \\{aux\_field}: \\{memory\_word};
+\par
+\noindent [Also delete the definitions of \\{lhmin} and \\{rhmin}, lines
+32 and 33.]
+
+\bugonpage B90, line 13 (2/27/93)
+
+\ninepoint
+\noindent\hskip10pt
+$\\{prev\_depth}\gets\\{ignore\_depth}$; \ $\\{mode\_line}\gets0$; \
+$\\{prev\_graf}\gets0$;
+
+\bugonpage B91, top three lines (2/27/93)
+
+\ninepoint
+\noindent\hskip20pt
+{\bf if\/} $m=\\{hmode}$ {\bf then if\/} $\\{nest}[p].\\{pg\_field}\ne
+ \oct{40600000}$ {\bf then}\par\noindent\hskip40pt
+{\bf begin} \\{print}({\tt\char`\"\](language\char`\"}); \
+ \\{print\_int}(\\{nest}[$p$].\\{pg\_field} {\bf mod} \oct{200000});\par
+\noindent\hskip40pt
+ \\{print}({\tt\char`\":hyphenmin\char`\"}); \
+ \\{print\_int}(\\{nest}[$p$].\\{pg\_field} {\bf div} \oct{20000000}); \
+ \\{print\_char}({\tt\char`\",\char`\"});\par\noindent\hskip40pt
+\\{print\_int}((\\{nest}[$p$].\\{pg\_field} {\bf div} \oct{200000})
+ {\bf mod} \oct{100}); \ \\{print\_char}({\tt\char`\")\char`\"});
+
+\bugonpage B344, lines 21 and 22 (2/27/93)
+
+\tenpoint\noindent\hskip10pt
+This code assumes that a \\{glue\_node} and a \\{penalty\_node} occupy the
+same number of \\{mem}~words.
+
+\bugonpage B344, line 30 (2/27/93)
+
+\ninepoint\noindent\hskip10pt
+$\\{link}(\\{tail})\gets\\{new\_param\_glue}(\\{par\_fill\_skip\_code})$; \
+ $\\{init\_cur\_lang}\gets\\{prev\_graf}$ {\bf mod} \oct{200000};\par
+\noindent\hskip10pt
+$\\{init\_l\_hyf}\gets\\{prev\_graf}$ {\bf div} \oct{20000000}; \
+$\\{init\_r\_hyf}\gets(\\{prev\_graf}$ {\bf div} \oct{200000}) {\bf mod}
+ \oct{100}; \
+\\{pop\_nest};
+
+\bugonpage B353, line 4 (2/27/93)
+
+\tenpoint\noindent\hskip10pt
+Kern nodes do not disappear at a line break unless they are \\{explicit}.
+
+\bugonpage B353, lines 15 and 16 (2/27/93)
+
+\ninepoint\noindent\hskip20pt
+\\{math\_node}: $\\{break\_width}[1]\gets\\{break\_width}[1]-\\{width}(s)$;\par
+\noindent\hskip20pt
+\\{kern\_node}: {\bf if\/} $\\{subtype}(s)\ne\\{explicit}$
+ {\bf then goto} \\{done}\par\noindent\hskip30pt
+{\bf else} $\\{break\_width}[1]\gets\\{break\_width}[1]-\\{width}(s)$;
+
+\bugonpage B354, lines 6 and 7 (2/27/93)
+
+\tenpoint\noindent
+will be the background
+plus $l_1$, so the length from \\{cur\_p} to \\{cur\_p} should be
+$\gamma+l_0+l_1-l$.
+If the post-break text of the discretionary is empty, a break may also
+discard~$q$; in that unusual case we subtract the length of~$q$ and any
+other nodes that will be discarded after the discretionary break.
+
+\bugonpage B354, line 18 (2/27/93)
+
+\ninepoint\noindent\hskip20pt
+{\bf begin} $\langle\,$Add the width of node $s$ to \\{break\_width}{\sevenrm
+ \kern.5em842}$\,\rangle$;
+
+\bugonpage B354, line 22 (2/27/93)
+
+\ninepoint\noindent\hskip10pt
+{\bf if\/} $\\{post\_break}(\\{cur\_p})=\\{null}$ {\bf then}
+ $s\gets\\{link}(v)$;\quad$\{\,$nodes may be discardable after the break$\,\}$
+
+\bugonpage B355, top line (2/27/93)
+
+\ninepoint\noindent
+{\tenbf842.\quad}$\langle\,$Add the width of node $s$ to
+ \\{break\_width}{\sevenrm\kern.5em842}$\,\rangle\equiv$
+
+\bugonpage B355, lines 9--14 (2/27/93)
+
+\ninepoint\noindent\hskip20pt
+$\\{hlist\_node},\\{vlist\_node},\\{rule\_node},\\{kern\_node}$:
+ $\\{break\_width}[1]\gets\\{break\_width}[1]+\\{width}(s)$;\par
+\noindent\hskip20pt
+{\bf othercases} \\{confusion}({\tt\char`\"disc2\char`\"})\par
+\noindent\hskip20pt
+{\bf endcases}
+
+\bugonpage B364, line 10 (2/27/93)
+
+\tenpoint\noindent
+a glue node, penalty node, explicit kern node, or math node.
+
+\bugonpage B366, line 11 from the bottom (2/27/93)
+
+\ninepoint\noindent
+\\{kern\_node}: {\bf if\/} $\\{subtype}(\\{cur\_p})=\\{explicit}$
+ {\bf then} \\{kern\_break}\par\noindent\hskip10pt
+{\bf else} $\\{act\_width}\gets\\{act\_width}+\\{width}(\\{cur\_p})$;
+
+\bugonpage B367, line 21 (2/27/93)
+
+\ninepoint\noindent\hskip20pt
+{\bf else if\/} \\{precedes\_break}(\\{prev\_p}) {\bf then}
+ $\\{try\_break}(0,\\{unhyphenated})$\par\noindent\hskip20pt
+{\bf else if\/} $(\\{type}(\\{prev\_p})=\\{kern\_node})\land
+ (\\{subtype}(\\{prev\_p})\ne\\{explicit})$ {\bf then}
+ $\\{try\_break}(0,\\{unhyphenated})$;
+
+\bugonpage B372, lines 12 and 13 (2/27/93)
+
+\ninepoint\noindent\hskip20pt
+{\bf if\/} $\\{type}(q)=\\{kern\_node}$ {\bf then}\par\noindent\hskip30pt
+{\bf if\/} $\\{subtype}(q)\ne\\{explicit}$ {\bf then goto} \\{done1};
+
+\bugonpage B376, line 3 from the bottom (2/27/93)
+
+\ninepoint\noindent\hskip10pt
+$\\{cur\_lang}\gets\\{init\_cur\_lang}$; \
+$\\{l\_hyf}\gets\\{init\_l\_hyf}$; \
+$\\{r\_hyf}\gets\\{init\_r\_hyf}$;
+
+\bugonpage B377, lines 11 and 12 (2/27/93)
+
+\ninepoint\noindent
+$\\{cur\_lang},\\{init\_cur\_lang}$: \\{ASCII\_code};\quad
+ $\{\,$current hyphenation table of interest$\,\}$\par\noindent
+$\\{l\_hyf},\\{r\_hyf},\\{init\_l\_hyf},\\{init\_r\_hyf}$: \\{integer};\quad
+ $\{\,$limits on fragment sizes$\,\}$
+
+\bugonpage B378, line 5 from the bottom, overriding earlier change (2/27/93)
+
+\ninepoint\noindent\hskip30pt
+{\bf else if} $(\\{type}(s)=\\{kern\_node})\land(\\{subtype}(s)=\\{normal})$
+ {\bf then}\par\noindent\hskip50pt
+{\bf begin} $\\{hb}\gets s$; \ $\\{hyf\_bchar}\gets\\{font\_bchar}[\\{hf}]$; \
+ {\bf end}\par\noindent\hskip40pt
+{\bf else goto} \\{done3};
+
+
+\bugonpage B394, lines 12 and 13 (2/27/93)
+
+\ninepoint\noindent\hskip10pt
+{\bf var} $n$: $0\to64$;\quad$\{\,$length of current word; not always
+ a \\{small\_number}$\,\}$\par\noindent\hskip20pt
+$j$: $0\to64$;\quad$\{\,$an index into \\{hc}$\,\}$
+
+\bugonpage B404, line 21 (2/27/93)
+
+\ninepoint\noindent\hskip10pt
+{\bf var} $k,l$: $0\to64$;\quad$\{\,$indices into \\{hc} and \\{hyf};
+ not always in \\{small\_number} range$\,\}$
+
+\bugonpage B460, lines 21 and 22 (2/27/93)
+
+\ninepoint\noindent\hskip10pt
+\\{push\_nest}; \ $\\{mode}\gets\\{hmode}$; \ $\\{space\_factor}\gets1000$; \
+ \\{set\_cur\_lang}; \ $\\{clang}\gets\\{cur\_lang}$;\par\noindent\hskip10pt
+$\\{prev\_graf}\gets(\\{norm\_min}(\\{left\_hyphen\_min})\ast\oct{100}+
+ \\{norm\_min}(\\{right\_hyphen\_min}))\ast\oct{200000}+\\{cur\_lang}$;
+
+\bugonpage B492, line 6 from the bottom (2/27/93)
+
+\ninepoint\noindent\hskip10pt
+\\{unsave}; \ $\\{prev\_graf}\gets\\{prev\_graf}+3$;\par\noindent\hskip10pt
+\\{push\_nest}; \ $\\{mode}\gets\\{hmode}$; \ $\\{space\_factor}\gets1000$; \
+ \\{set\_cur\_lang}; \ $\\{clang}\gets\\{cur\_lang}$;\par\noindent\hskip10pt
+$\\{prev\_graf}\gets(\\{norm\_min}(\\{left\_hyphen\_min})\ast\oct{100}+
+ \\{norm\_min}(\\{right\_hyphen\_min}))\ast\oct{200000}+\\{cur\_lang}$;
+
+ % volume C
+\hsize=29pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+
+\bugonpage C151, line 11 from the bottom (6/26/93)
+
+\ninepoint\indent
+\qquad scaled $1.42(1+\max(-\\{pen\_lft},\\{pen\_rt},\\{pen\_top},
+ -\\{pen\_bot}))$
+
+\bugonpage C262, line 15 (6/26/93)
+
+\ninepoint\noindent
+|string base_name, base_version; base_name="plain"; base_version="2.71";|
+
+\bugonpage C262, line 29 (6/26/93)
+
+\ninepoint\noindent
+|def |^|gobble|| primary g = enddef; def |^|killtext|| text t = enddef;|
+
+\bugonpage C271, bottom line (6/26/93)
+
+\ninepoint\noindent
+| culldraw p enddef;|
+
+\bugonpage C272, three new lines for top of page (6/26/93)
+
+{\ninepoint\parindent=0pt
+|def |^|culldraw|| expr p = addto pic_ doublepath p.t_ withpen currentpen;|\par
+| cull pic_ dropping(-infinity,0) withweight default_wt_;|\par
+| addto_currentpicture also pic_; pic_:=nullpicture; killtext enddef;|\par
+}
+
+\bugonpage C272, replacement for former line 5 (6/26/93)
+
+\ninepoint\noindent
+| (cut_ scaled (1+max(-pen_lft,pen_rt,pen_top,-pen_bot))|
+
+\bugonpage C296, line 24 (2/3/93)
+
+\ninepoint\noindent
+the definition of |rp| is changed to `|]..tension 4..|',
+and if `|scaled|~|5pt|' is inserted\cutpar
+
+\bugonpage C299, line 3 (5/15/92)
+
+\ninepoint\noindent
+a Bernshte{\u\i}n polynomial of order $n-1$.)
+
+\bugonpage C347, left column (5/15/92)
+
+\eightpoint
+Bernshte{\u\i}n, Serge{\u\i} \thinspace Natanovich, 14.
+
+\bugonpage C348, left column (6/26/93)
+
+\eightpoint
+|culldraw|, {\it271}, $\underline{272}$.
+
+\bugonpage C350, left column (6/26/93)
+
+\eightpoint
+exponential, {\sl see\/} |mexp|.
+
+\bugonpage C352, left column (6/26/93)
+
+\eightpoint
+|killtext|, $\underline{262}$, {\it 272}.
+
+\bugonpage C352, right column (6/26/93)
+
+\eightpoint
+logarithm, {\sl see\/} |mlog|.
+
+\bugonpage C361, lines 14 and 15 (2/25/93)
+
+P.O. Box 869\par
+Santa Barbara, CA 93102-0869 USA.
+
+
+ % Volume D
+\hsize=29pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage Dxiv, line 13 (4/19/96)
+
+\noindent preprocessor converts these into numeric constants that are
+256 or more. This\cutpar
+
+\bugonpage Dxiv, line $-1$ (4/19/96)
+
+\ninepoint\noindent This file contains one line per string, starting with
+string number 256, then number 257,\cutpar
+
+\bugonpage Dxv, lines 10 and 11 (4/19/96)
+
+\ninepoint\noindent
+In this case, occurrences of |""| in the |WEB| program will be replaced by
+256; occurrences of |"This longer string"| will be replaced by 257.
+The symbol |@$| stands for the numeric\cutpar
+
+\hsize=35pc
+\bugonpage D2, line $-17$ (8/7/98)
+
+\def\RQ/{{\char'23}} % right quote in a string
+\ninepoint\noindent
+{\bf define} $\\{banner}\equiv\hbox{\tt\RQ/This\]is\]METAFONT,\]%
+ Version\]2.7182\RQ/}$\quad$\{\,$printed when \MF\ starts$\,\}$
+
+\bugonpage D50, line 26 (8/7/98)
+
+\ninepoint\noindent
+\quad{\bf if\/} $b>0$ {\bf then}
+
+\bugonpage D138, line 14 from the bottom (3/6/95)
+
+\tenpoint
+\textindent{$2'$)} Let $Z_k^{(j+1)}={1\over2}(Z_k^{(j)}+Z_{k+1}^{(j)})$, for
+$1\le k\le n-j$, for $1\le j<n$.
+
+\bugonpage D190, D191, D194, D195 (6/26/93)
+
+\noindent[Several changes to the code in sections 415, 416, 424, and 425
+were made to \MF\ version 2.71 in July~1991, too numerous to mention here.
+They are documented in file {\tt mf84.bug} as bug number 560. We also
+delete lines 4 and~5 of page D194.]
+
+\bugonpage D216, line 10 from the bottom (7/15/92)
+
+\tenpoint\noindent
+will be offset by $w_1$ or $w_2$, unless its slope drops to zero
+en route to the eighth octant; in the latter\cutpar
+
+\bugonpage D289, lines 9 and 10 (6/26/93)
+
+\ninepoint\noindent\hskip20pt
+$p\gets\\{dep\_list}(p)$; \ $r\gets\\{inf\_val}$;\par\noindent\hskip20pt
+{\bf repeat if\/} $\\{value}(\\{info}(p))\ge\\{value}(r)$ {\bf then}
+
+\bugonpage D296, lines 8 and 9 from the bottom (9/13/98)
+
+\ninepoint\noindent[Delete these spurious lines.]
+
+\bugonpage D297, mini-index (6/6/98)
+
+\eightpoint the meaning of {\it loc\/} should be `macro'
+
+\bugonpage D310, line 7 (8/7/98)
+
+\ninepoint\noindent
+\quad{\bf if\/} $(\\{loc}=k+1)\land(\\{length}(\\{buffer}[k])=1)$
+ {\bf then} $\\{cur\_mod}:=\\{buffer}[k]$
+
+\bugonpage D363, lines 10 and 11 (3/1/95)
+
+\ninepoint\noindent
+\quad {\bf begin if\/} $(\\{max\_c}[\\{dependent}]$ {\bf div}
+ $\oct{10000}\ge\\{max\_c}[\\{proto\_dependent}])$ {\bf then}
+ $t\gets\\{dependent}$
+
+\bugonpage D512, line 13 (11/23/98)
+
+\ninepoint\noindent
+\quad \\{print\_int}(\\{round\_unscaled}(\\{internal}[\\{year}])); \
+ \\{print\_char}(|"."|);
+
+\bugonpage D518, insert new material between lines 7 and 8 (3/20/95)
+
+\ninepoint\noindent
+\quad {\bf while} $\\{input\_ptr}>0$ {\bf do}\par\noindent
+\qquad {\bf if\/} \\{token\_state} {\bf then} \\{end\_token\_list}
+ {\bf else} \\{end\_file\_reading};\par\noindent
+\quad {\bf while} $\\{loop\_ptr}\ne\\{null}$ {\bf do} \\{stop\_iteration};
+
+\bugonpage D518, line 18 (3/20/95)
+
+\ninepoint\noindent
+\qquad $\\{loop\_ptr}\gets\\{cond\_ptr}$; \
+ $\\{cond\_ptr}\gets\\{link}(\\{cond\_ptr})$; \
+ $\\{free\_node}(\\{loop\_ptr},\\{if\_node\_size})$;
+
+\bugonpage D546, left column (4/11/96)
+
+\eightpoint
+Stern, Moritz Abraham: \ 526.
+
+ % volume E
+\hsize=29pc
+\def\dashto{\mathrel{\hbox{-\kern-.05em}\mkern3.9mu\hbox{-\kern-.05em}}}
+
+
+ \bye
+
diff --git a/systems/knuth/dist/errata/errata.eleven b/systems/knuth/dist/errata/errata.eleven
new file mode 100644
index 0000000000..64d9f8ab45
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.eleven
@@ -0,0 +1,317 @@
+% Bugs (sigh) in Computers \& Typesetting
+
+\input manmac
+\def\curl{\mathop{\rm curl}}
+\def\cycle{{\rm cycle}}\indent
+\def\dashto{\mathrel{\hbox{-\thinspace-\kern-.05em}}}
+\font\sltt=cmsltt10
+\font\niness=cmss9
+\font\ninessi=cmssi9
+\proofmodefalse
+\raggedbottom
+\output{\hsize=29pc \onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\today{\number\day\
+ \ifcase\month\or
+ Jan\or Feb\or Mar\or Apr\or May\or Jun\or
+ Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi
+ \ \number\year}
+
+\def\cutpar{{\parfillskip=0pt\par}}
+
+\def\rhead{Bugs in {\tensl Computers \& Typesetting, 2001}}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\buginvol#1(#2) \par{\bigbreak\penalty-1000\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Volume #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\slMF{{\manual 89:;}\-{\manual <=>:}} % slant the logo
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\newcount\nn
+\newdimen\nsize \newdimen\msize \newdimen\ninept \ninept=9pt
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+
+\tenpoint
+\noindent This is a list of all substantial corrections made to {\sl Computers
+\& Typesetting\/} between the first ``Millennium edition'' of 2000 and the
+second such edition, which appeared late in 2001.
+(More precisely, it lists errors to the 16th, 7th, 6th, 4th, and 5th
+printings of Volumes A, B, C, D, and E, respectively, that were corrected
+in the 17th, 8th, 7th, 5th, and 6th printings.)
+Changes to the mini-indexes
+and master indexes of Volumes B, D, and~E are not shown here unless they are
+not obviously derivable from what has been shown.
+\looseness=-1
+
+ % volume A
+
+\bugonpage A16, line 7 from the bottom (06/30/01)
+
+\font\magnifiedfiverm=cmr5 at 10pt
+\tenpoint\indent
+Ten-point type is different from%
+ \magnifiedfiverm\ magnif{}ied f{}ive-point type.
+
+\bugonpage A17, line 7 (06/30/01)
+
+\ninepoint\indent
+fications that grow in geometric ratios---something like equal-tempered
+tuning\cutpar
+
+\bugonpage A51, lines 18--20 (06/30/01)
+
+\tenpoint\kern-6pt\noindent
+$$\openup1pt\halign{\indent#\hfil\cr
+|ff| yields ff\thinspace;$\!$\quad |fi| yields fi\thinspace;$\!$\quad
+|fl| yields fl\thinspace;$\!$\quad
+|ffi| yields ffi\thinspace;$\!$\quad |ffl| yields ffl\thinspace;\cr
+|``| yields``\thinspace;\qquad |''| yields ''\thinspace;\qquad
+|!||`| yields !`\thinspace;\qquad |?||`| yields ?`\thinspace;\cr
+|--| yields --\thinspace;\qquad |---| yields ---\thinspace.\cr}$$
+
+\bugonpage A52, line 7 from the bottom (06/30/01)
+
+\tenpoint\kern-6pt\noindent
+$$\halign{\indent\hbox to 50pt{#\hfil}&\hbox to 35pt{#\hfil}&#\hfil\cr
+|\ae,\AE|&\ae,\thinspace\AE&(Latin ligature and Scandinavian letter AE)\cr}$$
+
+\bugonpage A71, line 15 (06/30/01)
+
+\tenpoint\indent
+One of the interesting things that can happen when glue stretches and\cutpar
+
+\bugonpage A180, line 20 (06/30/01)
+
+\ninepoint\indent
+Challenge number 5:\enspace
+$\qquad\tenmath k=1.38065\times10^{-16}\rm\,erg\,K^{-1}$.
+
+\bugonpage A254, line 12 from the bottom becomes two lines (04/09/01)
+
+\ninepoint\noindent
+\begintt
+\output={\unvbox255
+ \ifnum\outputpenalty<10000 \penalty\outputpenalty\fi}
+\endtt
+
+\bugonpage A292, lines 13--16 (06/30/01)
+
+\def\s{\hskip0pt plus1pt}
+\ninepoint\textindent{$\bull$}|\mathchoice|\s
+$\langle$filler$\rangle$\s|{|\s$\langle$math mode material$\rangle$\s|}|\s
+$\langle$filler$\rangle$\s|{|\s$\langle$math mode material$\rangle$\s|}|\break
+$\langle$filler$\rangle$|{|$\langle$math mode material$\rangle$|}|
+$\langle$filler$\rangle$|{|$\langle$math mode material$\rangle$|}|.
+Four math lists, which are defined as in the
+second alternative of a \<math field>, are
+recorded in a ``choice item'' that is appended to the current list.
+
+\bugonpage A306, line 7 (06/30/01)
+
+\ninepoint\noindent
+instead of a shelf{\kern0pt}ful.
+In fact, the latter idea---to
+insert an italic correction---is prefer-\cutpar
+
+\bugonpage A323, line 12 from the bottom (06/30/01)
+
+\ninepoint\noindent
+\hbox to\parindent{\bf\hss18.31.\enspace}%
+|$k=1.38065\times10^{-16}\rm\,erg\,K^{-1}$|.
+
+\bugonpage A451, line 15 (01/30/01)
+
+\tenpoint\noindent
+{\sl Connecticut Yankee\/} come out with only nine or
+ten bad hyphens:
+
+\bugonpage A451, line 23 (01/30/01)
+
+\tenpoint\noindent\begintt
+ mo-er-der-mohren-mut-ter-mar-mor-mon-u-menten-macher.
+\endtt
+
+\bugonpage A454, lines 23--30 (06/30/01)
+
+\begingroup
+\hyphenpenalty=-1000 \pretolerance=-1 \tolerance=1000
+\doublehyphendemerits=-100000 \finalhyphendemerits=-100000
+\ddanger If a suitable starting letter is found, let it be in font~$f$.
+Hyphenation is abandoned unless the |\hyphenchar| of~$f$ is a number
+between 0 and~255, inclusive.
+If this test is passed, \TeX\ continues to scan forward
+until coming to something that's not one of the following three
+``admissible items'': (1)~a character in font~$f$ whose |\lccode|
+is nonzero; (2)~a ligature formed entirely from characters of type~(1);
+(3)~an implicit kern. The first inadmissible item terminates this part of
+the process; the trial word consists of all the letters found in admissible
+items. Notice that all of these letters are in font~$f$.
+\par\endgroup
+
+\bugonpage A461, right column (07/08/01)
+
+\eightpoint\noindent
+\llap{*}|\char|, {\it 43--45}, 76, 86, 155, 283, $\underline{286}$,
+
+\bugonpage A466, left column (07/09/01)
+
+\eightpoint\noindent
+\llap{*}|\floatingpenalty|, $\underline{123}$--$\underline{124}$,
+ 272, 281, {\it 363}.
+
+\bugonpage A473, left column (06/30/01)
+
+\eightpoint\noindent
+orphans, {\sl see\/} widow words.
+
+ % volume B
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\hsize=35pc
+
+\bugonpage B8, line 2 (05/04/01)
+
+\tenpoint\noindent
+statements will be meaningful. We insert the label
+`\\{exit}' just before the `{\bf end}' of a procedure in\cutpar
+
+\bugonpage B30, line $-4$ (05/04/01)
+
+\ninepoint\noindent
+\quad{\bf begin} \\{update\_terminal};\quad$\{\,$now the user sees
+ the prompt for sure$\,\}$
+
+\bugonpage B84, lines 22 and 27 (05/04/01)
+
+\ninepoint\noindent
+\quad$\\{ignore}=9$\quad$\{\,$characters to ignore ( |^^@| )$\,\}$\par
+\noindent
+\quad$\\{active\_char}=13$\quad$\{\,$characters that invoke
+ macros ( |~| )$\,\}$
+
+\bugonpage B280, lines 23 and 24 (04/08/01)
+
+\tenpoint\noindent
+or unset nodes; in particular, each mlist item appears in the
+variable-size part of \\{mem}, so the \\{type} field is always present.
+
+\bugonpage B382, line 6 (01/01/01)
+
+\tenpoint\noindent
+between `fl' and `y', then $m=2$, $t=2$, and $y_1$ will
+be a ligature node for `fl' followed by an\cutpar
+
+\bugonpage B386, line 11 (04/08/01)
+
+\ninepoint\noindent
+\quad$\\{qi}(2),\\{qi}(6)$: {\bf begin} $\\{cur\_r}\gets\\{rem\_byte}(q)$;%
+ \quad$\{\,$\||=:|, \||=:>|$\,\}$
+
+\bugonpage B475, line 12 (07/01/01)
+
+\ninepoint\noindent
+\qquad{\bf end};\quad$\{\,$now we are in vertical mode,
+ working on the list that will contain the display$\,\}$
+
+ % volume C
+\hsize=29pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\ddashto{\mathrel{\hbox{-\thinspace-\thinspace-\kern-.05em}}}
+\def\tension{\mathop{\rm tension}}
+\def\controls{\mathop{\rm controls}}
+\def\and{\,{\rm and}\,}
+
+\bugonpage C204, line 3 from the bottom (07/08/01)
+
+\ninepoint\noindent
+slightly. If $\\{autorounding}>1$,
+you get even more changes: Paths are perturbed slightly\cutpar
+
+\bugonpage C238, lines 9 and 8 from the bottom (07/08/01)
+
+\ninepoint\noindent
+tance is ${\rm length}(z_4-z_1)$. But there's a slicker solution:
+Just calculate
+$$\hbox{abs ypart$((z_1-z_2)\mathbin{\rm rotated}-{\rm angle}(z_3-z_2))$.}$$
+
+\bugonpage C313, bottom line (06/30/01)
+
+\rightline{\eightss--- LA ROCHEFOUCAULD, {\eightssi Maximes\/}\enspace(1665)}
+
+\bugonpage C352, left column (06/30/01)
+
+\eightpoint\noindent
+La Rochefoucauld, Fran\c cois VI, 313.
+
+\bugonpage C357, right column (07/08/01)
+
+\eightpoint\noindent
+\llap{*}|true|, 55, {\it64\/}--{\it65}, 170, 210.
+
+ % Volume D
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\hsize=35pc
+
+\bugonpage D8, line 2 (05/04/01)
+
+\tenpoint\noindent
+statements will be meaningful. We insert the label
+`\\{exit}' just before the `{\bf end}' of a procedure in\cutpar
+
+\bugonpage D28, line $-8$ (05/04/01)
+
+\ninepoint\noindent
+\quad{\bf begin} \\{update\_terminal};\quad$\{\,$now the user sees
+ the prompt for sure$\,\}$
+
+\bugonpage D101, line 21 (07/08/01)
+
+\ninepoint\noindent
+\quad{\bf define} $\\{subscr\_head\_loc}(\hbox{\tt\#})\equiv\hbox{\tt\#}+1$
+\quad$\{\,$where \\{value}, \\{subscr\_head}, and \\{attr\_head} are$\,\}$
+
+\bugonpage D180, lines 22 and 23 (01/26/01)
+
+\tenpoint\noindent
+$(y,-x)$ will appear in node~$p$. Similarly, a fourth-octant
+transformation will have been applied after the transition, so
+we will have $\\{x\_coord}(q)=\hbox{$-x$}$ and $\\{y\_coord}(q)=y$.
+
+\bugonpage D196, lines 7 and 8 (01/26/01)
+
+\tenpoint\noindent
+where $x'(t)\ge0$ we have $\\{right\_type}=%
+\\{first\_octant}$ or $\\{right\_type}=\\{eighth\_octant}$; in regions where
+$x'(t)\le0$,
+we have $\\{right\_type}=\\{fifth\_octant}$ or $\\{right\_type}=\\{fourth%
+\_octant}$.
+
+\bugonpage D511, line 17 (07/03/01)
+
+\tenpoint\noindent
+from appearing again.
+
+ % volume E
+\hsize=29pc
+
+\newbox\shorthyf \setbox\shorthyf=\hbox{-\kern-.05em}
+\mathchardef\period=`\.
+{\catcode`\-=\active \global\def-{\copy\shorthyf\mkern3.9mu}
+ \catcode`\.=\active \global\def.{\period\mkern3mu}}
+\def\8#1{\mathrel{\mathcode`\.="8000 \mathcode`\-="8000
+ #1\unkern}} % `..' and `--'
+
+\bugonpage E9, line 9 (07/03/01)
+
+\tenpoint\indent
+|[92] [123] [124]) ) )|
+
+ \bye
+
+
diff --git a/systems/knuth/dist/errata/errata.five b/systems/knuth/dist/errata/errata.five
new file mode 100644
index 0000000000..4e2982f109
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.five
@@ -0,0 +1,1108 @@
+% Bugs (sigh) in Computers \& Typesetting
+
+\input manmac
+\font\sltt=cmsltt10
+\font\niness=cmss9
+\font\ninessi=cmssi9
+\proofmodefalse
+\raggedbottom
+\output{\hsize=29pc \onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\today{\number\day\
+ \ifcase\month\or
+ Jan\or Feb\or Mar\or Apr\or May\or Jun\or
+ Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi
+ \ \number\year}
+
+\def\cutpar{{\parfillskip=0pt\par}}
+
+\def\rhead{Bugs in {\tensl Computers \& Typesetting, 1989}}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\buginvol#1(#2) \par{\bigbreak\penalty-1000\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Volume #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\slMF{{\manual 89:;}\-{\manual <=>:}} % slant the logo
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\newcount\nn
+\newdimen\nsize \newdimen\msize \newdimen\ninept \ninept=9pt
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+
+\tenpoint
+\noindent This is a list of all corrections made to {\sl Computers \&
+Typesetting}, Volumes \hbox{A--E}, between 20 February 1989 and
+30 September 1989 (when \TeX\ Version 3.0 and \MF\ Version 2.0 were
+fully defined). Corrections made to
+the softcover version of {\sl The \TeX book\/} are the same as corrections to
+Volume~A\null. Corrections to the softcover version of {\sl The
+\slMF\kern1ptbook\/} are the same as corrections to Volume~C\null.
+Some of these corrections have already been made in reprintings
+of the books. Several minor changes
+to Volumes A~and~C are not shown here because they simply
+make room for the more substantive changes needed to describe the new
+features of \TeX\ Version 3.0 and \MF\ Version 2.0. Hundreds of
+changes will soon be made to Volumes B~and~D because of the upgrades
+to \TeX\ and \MF\/; it will unfortunately be impossible to document all of those
+changes. Therefore, readers who need up-to-date information on the \TeX\ and
+\MF\ programs should refer to the |WEB| source files until new
+printings of Volumes B~and~D are issued.
+
+ % volume A
+
+\buginvol A, in general (9/23/89)
+
+\ninepoint\noindent
+[Change `127' to `255' and `128' to `256' in contexts referring to character
+codes. This happens on pages 37(twice), 39, 41, 43, 44(twice), 48, 93, 154,
+277, 305(twice), 308(twice), 313, and 343. Also change `7-bit' to `8-bit' on
+pages 214 and 277.]
+
+\bugonpage A23, line 16 (9/23/89)
+
+|This is TeX, Version 3.0 (preloaded format=plain 89.7.15)|
+
+\bugonpage A34, new copy for bottom of page (9/23/89)
+
+\ddanger If you use \TeX\ format packages designed by others, your
+error messages may involve many inscrutable two-line levels of macro
+context. By setting ^|\errorcontextlines||=0| at the beginning of your file,
+you can reduce the amount of information that is reported;
+\TeX\ will show only the top and bottom pairs of context lines
+together with up to |\errorcontextlines| additional two-line items. \ (If
+anything has thereby been omitted, you'll also see `|...|'.) \ Chances
+are good that you can spot the source of an error even when most of a
+large context has been suppressed; if not, you can say
+`|I\errorcontextlines=100\oops|' and try again. \ (That will usually
+give you an undefined control sequence error and plenty of context.) \
+Plain \TeX\ sets |\errorcontextlines=5|.
+
+\bugonpage A45, lines 9--15 (9/23/89)
+
+\ninepoint\noindent
+|^^| has an internal code between 64 and 127, \TeX\
+subtracts 64 from the code; if the code is between 0 and 63, \TeX\
+adds~64. Hence code 127 can be typed |^^?|, and
+the dangerous bend sign can be obtained by saying
+|{\manual^^?}|. However, you must change the category code of character
+127 before using it, since this character ordinarily has category~15
+(^{invalid}); say, e.g., |\catcode`\^^?=12|.
+^^{double hat} ^^{hat hat}
+The |^^| notation is different from |\char|, because |^^| combinations are
+like single characters; for example, it would not be permissible to say
+|\catcode`\char127|, but |^^| symbols can even be used as letters within
+control words.
+
+\bugonpage A45, new copy before line 20 (9/23/89)
+
+\danger There's also a special convention in which |^^| is
+followed by {\sl two\/} ``lowercase hexadecimal digits,'' |0|--|9| or |a|--|f|.
+With this convention, all 256 characters are obtainable in a uniform
+way, from |^^00| to |^^ff|. Character 127 is |^^7f|.
+
+\noindent
+[Also remove one of the two dangerous bend signs on line 20.]
+
+\bugonpage A45, bottom paragraph and footnote (9/23/89)
+
+\ddanger People who install \TeX\ systems for use with non-American alphabets
+can make \TeX\ conform to any desired standard. For example, suppose
+you have a ^{Norwegian keyboard} containing the letter {\tt\ae}, which
+^^{Scandinavian letters} ^^{foreign languages}
+comes in as code~241 (say). Your local format package should define
+|\catcode`|{\tt\ae}|=11|; then you could have control sequences like
+|\s|{\tt\ae}|rtrykk|. Your \TeX\ input files could be made readable by
+American installations of \TeX\ that don't have your keyboard, by
+substituting |^^f1| for character~241. \ (For example, the stated control
+sequence would appear as |\s^^f1rtrykk| in the file; your American
+friends should also be provided with the format that you used, with its
+|\catcode`^^f1=11|.) \ Of course you should also arrange your fonts
+so that \TeX's character 241 will print as {\ae}; and you should
+change \TeX's hyphenation algorithm so that it will do correct
+Norwegian hyphenation. The main point is that such changes are not
+extremely difficult; nothing in the design of \TeX\ limits it to the
+American alphabet. Fine printing is obtained by fine tuning to the
+language or languages being used.
+^^{keyboards, non-ASCII}
+
+\ddanger European languages can also be accommodated effectively with
+only a limited character set.
+For example, let's consider Norwegian again, but suppose that\parfillskip=0pt
+
+\noindent [Now continue with the text on line 11 of page 46.]
+
+\bugonpage A47, lines 9--21 (9/23/89)
+
+\ddanger If \TeX\ sees a superscript character (category 7) in any state,
+and if that character is followed by another identical character, and if
+those two equal characters are followed by a character of code
+$c<128$, then they
+are deleted and 64 is added~to or subtracted from the code~$c$.
+\ (Thus, |^^A| is
+replaced by a single character whose code is~1, etc., as explained earlier.) \
+However, if the two superscript characters are immediately followed by two
+of the lowercase hexadecimal digits |0123456789abcdef|, the
+four-character sequence is replaced by a single character having the
+specified hexadecimal code.
+The replacement is carried out also if such a trio or quartet of
+characters is encountered during steps (b) or~(c) of the control-sequence-name
+scanning procedure described above. After the replacement is made, \TeX\
+begins again as if the new character had been present all the time.
+If a superscript character is not the first of such a trio or quartet, it is
+handled by the following rule.
+
+\ddanger If \TeX\ sees a character of categories 1, 2, 3, 4, 6, 8, 11, 12,
+or~13,
+or a character of category~7 that is not the first of a special
+sequence as just
+described, it converts the character to a token by attaching the category
+code, and goes into state~$M$. This is the normal case; almost every
+nonblank character is handled by this rule.
+
+\bugonpage A48, line 15 (9/23/89)
+
+\ninepoint\noindent
+the input line
+`| $x^2$~ \TeX ^^62^^6|'\thinspace?
+
+\bugonpage A54, third line from the bottom (9/23/89)
+
+\ninepoint\noindent
+For example, a well-designed \TeX\ font for ^{French}
+might well treat accents as lig-\cutpar
+
+\bugonpage A76, lines 3--5 from the bottom (9/23/89)
+
+\ninepoint\noindent
+\TeX\ does not assign any value to
+|\sfcode'042|.
+
+\bugonpage A107, new copy for top of page (9/23/89)
+
+\ddanger If you want to avoid overfull boxes at all costs without
+trying to fix them manually, you might be tempted to set
+|tolerance=10000|; this allows arbitrarily bad lines to be acceptable
+in tough situations. But infinite tolerance is a bad idea, because
+\TeX\ doesn't distinguish between terribly bad and preposterously
+horrible lines. Indeed, a tolerance of 10000 encourages \TeX\ to
+concentrate all the badness in one place, making one truly unsightly
+line instead of two moderately bad ones, because a single
+``write-off'' produces fewest total demerits according to the rules.
+There's a much better way to get the desired effect: \TeX\ has a
+parameter called ^|\emergencystretch| that is added to the assumed
+stretchability of every line when badness and demerits are computed,
+in cases where overfull boxes are otherwise unavoidable. If
+|\emergencystretch| is positive, \TeX\ will make a third pass over a
+paragraph before choosing the line breaks, when the first passes did
+not find a way to satisfy the ^|\pretolerance| and ^|\tolerance|.
+The effect of\/ |\emergencystretch| is to scale down the badnesses so
+that large infinities are distinguishable from smaller ones. By
+setting |\emergencystretch| high enough (based on |\hsize|) you can be
+sure that the |\tolerance| is never exceeded; hence overfull boxes
+will never occur unless the line-breaking task is truly impossible.
+
+\bugonpage A116, lines 11--15 (6/7/89)
+
+\danger If you have two or more |\topinsert| or |\pageinsert| commands in
+quick succession, \TeX\ may need to carry them over to several subsequent
+pages; but they will retain their relative order when they are
+carried over. For example, suppose you have pages that are nine inches
+tall, and suppose you have already specified 4~inches of text for some
+page, say page~25. Then suppose you make seven topinserts in a row, of%
+{\parfillskip=0pt\endgraf}\endgroup
+
+\bugonpage A125, lines 13--29 (9/23/89)
+
+\ddanger \looseness=-1
+When the best page break is finally chosen, \TeX\ removes everything after
+the chosen breakpoint from the bottom of the ``current page,'' and puts it
+all back at the top of the ``recent contributions.'' The
+chosen breakpoint itself is placed at the very top of the recent contributions.
+If it is a penalty item, the value of the penalty is recorded in
+^|\outputpenalty| and the penalty in the contribution list is changed
+to $10000$; otherwise |\outputpenalty| is set to 10000.
+The insertions that remain on the current page are of three kinds: For
+each class~$n$ there are unsplit insertions, followed possibly by
+a single split insertion, followed possibly by others. If
+^|\holdinginserts|$\null>0$, all insertions remain in place (so that
+they might be contributed again); otherwise they are all removed from
+the current page list as follows: The unsplit insertions
+are appended to |\box|$\,n$, with no interline glue between them. \
+(^{Struts} should be used, as in the |\vfootnote| macro of
+Appendix~B\null.) \
+If a split insertion is present, it is effectively |\vsplit| to the size
+that was computed previously in Step~4; the top part is treated as an
+unsplit insertion, and the remainder (if any) is converted to an insertion
+as if it had not been split. This remainder, followed by any other floating
+insertions of the same class, is held
+over in a separate place. \ (They will show up on the ``current page'' if
+^|\showlists| is used while an ^|\output| routine is active; the total
+number of such insertions appears in ^|\insertpenalties| during an
+|\output| routine.) %\
+Finally, the remaining items before the best break on the current page are put
+together in a |\vbox|\parfillskip=0pt
+
+\bugonpage A131, line 12 (9/22/89)
+
+\ninepoint\noindent
+work fine; but sometimes you want to have uniformity
+between different members of a\cutpar
+
+\bugonpage A155, lines 3--5 (9/23/89)
+
+\ninepoint\noindent
+when it encounters a character that
+is given explicitly as ^|\char|\<number>.
+
+\bugonpage A214, lines 19--24 (9/23/89)
+
+\ninepoint
+\textindent\bull |\the|\<special register>, where \<special register> is
+one of the integer quantities ^|\prevgraf|, ^|\deadcycles|, ^|\insertpenalties|,
+^|\inputlineno|, ^|\badness|,
+or ^|\parshape| (denoting only the number of lines of\/ |\parshape|); or
+one of the dimensions ^|\pagetotal|, ^|\pagegoal|, ^|\pagestretch|,
+^|\pagefilstretch|, ^|\pagefillstretch|, ^|\pagefilllstretch|, ^|\pageshrink|,
+^|\pagedepth|. In horizontal modes you can also refer to a special integer,
+|\the\spacefactor|; in vertical modes there's a special dimension,
+|\the\prevdepth|.
+
+\bugonpage A229, new copy after line 11 (9/23/89)
+
+\ddanger \TeX\ will report the badness of glue setting in a box if
+you ask for the numeric quantity
+^|\badness| after making a box. For example, you might say
+\begintt
+\setbox0=\line{\trialtexta}
+\ifnum\badness>250 \setbox0=\line{\trialtextb}\fi
+\endtt
+The badness is between 0 and 10000
+unless the box is overfull, when |\badness=1000000|.
+
+\bugonpage A271, lines 17--20 (9/23/89)
+
+\ninepoint
+\beginsyntax
+ \alt<countdef token>\alt^|\count|<8-bit number>\alt<codename><8-bit number>
+ \alt<chardef token>\alt<mathchardef token>\alt^|\parshape|\alt^|\inputlineno|
+ \alt^|\hyphenchar|<font>\alt^|\skewchar|<font>\alt^|\badness|
+\endsyntax
+
+\bugonpage A272, lines 3--4 (9/23/89)
+
+\ninepoint\noindent
+value is between 0~and $2^8-1=255$; a ^\<4-bit number> is similar.
+
+\bugonpage A273, insert after lines 11, 20, 21, 21, 38 (9/23/89)
+
+\ninepoint
+\begindisplay
+|\holdinginserts|\quad(positive if insertions remain dormant in output box)\cr
+|\language|\quad(the current set of hyphenation rules)\cr
+|\lefthyphenmin|\quad(smallest fragment at beginning of hyphenated word)\cr
+|\righthyphenmin|\quad(smallest fragment at end of hyphenated word)\cr
+|\errorcontextlines|\quad(maximum extra context shown when errors occur)\cr
+\enddisplay
+
+\bugonpage A274, insert after line 4 (9/23/89)
+
+\ninepoint\indent
+|\emergencystretch|\quad(reduces badnesses on final pass of line-breaking)
+
+\bugonpage A275, line 13 (9/23/89)
+
+\ninepoint\noindent
+That makes a total of 103 parameters of all five kinds.
+
+\bugonpage A283, line 14 (9/23/89)
+
+\ninepoint
+\beginsyntax
+ \alt^|\noboundary|\alt^|\unhbox|\alt^|\unhcopy|\alt^|\valign|\alt^|\vrule|
+\endsyntax
+
+\bugonpage A286, lines 3--12 from the bottom (9/23/89)
+
+\ninepoint
+\textindent{$\bull$}%
+\<letter>, \<otherchar>, \kern-1pt^|\char|\<8-bit number>, \<chardef token>,
+\kern-1pt^|\noboundary|.\enskip
+The most common commands of all are the character commands that tell
+\TeX\ to append a character to the current horizontal
+list, using the current font.
+If two or more commands of this type occur in succession, \TeX\ processes
+them all as a unit, converting to ligatures and/or
+inserting kerns as directed by the font information. \ (Ligatures and
+kerns may be influenced by invisible ``boundary'' characters at the left
+and right, unless |\noboundary| appears.) \ Each character
+command adjusts ^|\spacefactor|, using
+the ^|\sfcode| table as described in Chapter~12.
+In unrestricted horizontal mode, a
+`|\discretionary{}{}{}|' item is appended after a character whose code is
+the ^|\hyphenchar| of its font, or after a ligature formed from a sequence
+that ends with such a character.
+
+\bugonpage A287, insert after line 19 (9/23/89)
+
+\ninepoint
+\textindent{$\bull$}%
+|\setlanguage|\<number>.\enskip See the conclusion of Appendix H.
+
+\bugonpage A289, lines 9--14 from the bottom (9/23/89)
+
+\ninepoint\noindent
+$2^{15}-1$. This is done by replacing the character number by its
+^|\mathcode| value. If the
+|\mathcode| value turns out to be $32768=\null$\hex{8000}, however,
+ the \<character>
+is replaced by an ^{active character} token having the original character
+code (0 to~255); \TeX\ forgets the original \<character> and expands this
+active character according to the rules of Chapter~20.
+
+\bugonpage A290, insert before 13th line from bottom (9/23/89)
+
+\ninepoint
+\textindent{$\bull$}%
+|\noboundary|. This command is redundant and therefore has no
+effect; boundary ligatures are automatically disabled in math modes.
+
+\bugonpage A296, line 16 from the bottom (9/22/89)
+
+\noindent[There should be a `|^|' just above the `|3|' in the line below.
+This was mistakenly dropped by the printer some time during 1985; it was
+correct in the first two printings and it has always been correct inside the
+computer!]
+
+\bugonpage A309, lines 3--5 (9/23/89)
+
+\ninepoint\noindent
+\hbox to\parindent{\bf\hss8.4.\enspace}\ignorespaces
+|$|$_{3}$ |x|$_{11}$ |^|$_7$ |2|$_{12}$ |$|$_{3}$ |~|$_{13}$ \]$_{10}$
+\cstok{TeX} |b|$_{12}$ |v|$_{12}$ \]$_{10}$. The final space comes from the
+\<return> placed at the end of the line. Code |^^6| yields |v| only
+when not followed by |0|--|9| or |a|--|f|.
+The initial space is ignored, because state~$N$
+governs the beginning of the line.
+
+\bugonpage A314, line 27 (9/23/89)
+
+\ninepoint\noindent
+The English word `eighteen' might deserve similar treatment.
+\TeX's hyphenation algorithm will not make such spelling changes automatically.
+
+\bugonpage A318, line 19 (3/3/89)
+
+\ninepoint
+|\def\clearnotenumber{\notenumber=0\relax}|
+
+\bugonpage A330, line 3 (8/25/89)
+
+\ninepoint\noindent
+\hbox to\parindent{\bf\hss20.10.\enspace}\ignorespaces
+|\def\overpaid{{\count0=\balance|
+
+\bugonpage A336, lines 4--8 from the bottom (9/23/89)
+
+\ninepoint\noindent
+badness rating of a
+box is at most 10000, except that the |\badness| of
+an overfull box is 1000000. |INITEX| initializes |\tolerance| to
+10000, thereby making all line breaks feasible. Penalties of 10000 or more
+prohibit breaks; penalties of $-10000$ or less make breaks mandatory. The
+cost of a page break is 100000, if the badness is 10000 and if the
+associated penalties are less than 10000 in magnitude (see Chapter~15).
+
+\bugonpage A337, lines 2--16 (9/23/89)
+
+\ninepoint\noindent
+ifies characters whose codes differ by~64
+from the codes of |?|, |@|, |A|; this convention applies only to
+characters with ASCII codes less than~128. There are 256 possible characters,
+hence 256 entries in each of the |\catcode|, |\mathcode|,
+|\lccode|, |\uccode|, |\sfcode|, and |\delcode| tables. All
+|\lccode|, |\uccode|, and |\char| values
+must be less than~256. A font has at most 256 characters. There are
+256~|\box| registers, 256~|\count| registers, 256~|\dimen| registers,
+256~|\skip| registers, 256~|\muskip| registers, 256~|\toks| registers,
+256~hyphenation tables.
+The ``at size'' of a font must be less than~$2048\pt$, i.e.,~$2^{11}\pt$.
+Math delimiters are encoded by multiplying the math~code of the ``small
+character'' by~$2^{12}$. The magnitude of
+a~\<dimen> value must be less than~$16384\pt$, i.e.,~$2^{14}\pt$;
+similarly, the \<factor> in a~\<fil dimen> must be less than~$2^{14}$.
+A~|\mathchar| or |\spacefactor| or |\sfcode| value must be less than~$2^{15}$;
+a~|\mathcode| or |\mag| value must be less than or equal to~$2^{15}$,
+and $2^{15}$ denotes an ``active'' math character. There
+are $2^{16}\rm\,sp$ per~pt. A~|\delcode| value
+must be less than~$2^{24}$; a~|\delimiter|, less than $2^{27}$.
+The |\end| command sometimes contributes
+a penalty of $-2^{30}$ to the current page. A~\<dimen> must be less than
+$2^{30}\rm\,sp$ in absolute value; a~\<number> must be
+less than $2^{31}$ in absolute value.
+
+\bugonpage A348, line 12 from the bottom (9/23/89)
+
+\ninepoint\noindent
+^|\showboxbreadth||=5 |^|\showboxdepth||=3 |^|\errorcontextlines||=5|
+
+\bugonpage A364, insert before line 18 from the bottom (9/23/89)
+
+\ninepoint\noindent
+^|\lefthyphenmin||=2 |^|\righthyphenmin||=3 % disallow x- or -xx breaks|
+
+\bugonpage A364, line 5 from the bottom (9/23/89)
+
+\ninepoint\noindent
+|\def|^|\fmtname||{plain}\def\fmtversion{3.0} % identifies the current format|
+
+\bugonpage A369, insert before line 5 from the bottom (9/23/89)
+
+\ninepoint
+Modern keyboards allow 256 codes to be input, not just 128; so \TeX\
+represents characters internally as numbers in the range 0--255 (i.e.,
+\oct{000}--\oct{377}, or \hex{00}--\hex{FF}). Implementations of \TeX\
+differ in which characters they will accept in input files and which
+they will transmit to output files; these subsets can be specified
+independently. A completely permissive version of \TeX\ allows full
+256-character input and output; other versions might ignore all
+but the visible characters of ASCII; still other versions might
+distinguish the tab character (code \oct{011}) from a space on input,
+but might output each tab as a sequence of three characters |^^I|.
+
+\bugonpage A370, lines 3--7 (9/23/89)
+
+\ninepoint\noindent
+close as possible to the ASCII conventions.
+\ (b)~Make sure that codes \oct{041}--\oct{046}, \oct{060}--\oct{071},
+\oct{141}--\oct{146}, and \oct{160}--\oct{171} are present and that
+each unrepresentable
+internal code $<\null$\oct{200} leads to a representable code when \oct{100} is
+added or subtracted; then all 256 codes can be input and output.
+\ (c)~Cooperate with everyone else who shares
+the same constraints, so that you all adopt the same policy.
+\ (See Appendix~J for information about the \TeX\ Users Group.)
+
+\bugonpage A370, bottom line (9/23/89)
+
+\ninepoint\noindent
+doesn't matter if these symbols have their plain
+\TeX\ meanings or not. \ (6)~There is a special convention for
+representing characters 0--255 in the hexadecimal forms
+|^^00|--|^^ff|, explained in Chapter~8. This convention is always
+acceptable as input, when |^| is any character of catcode~7. Text
+output is produced with this convention only when representing
+characters of code $\ge128$ that a \TeX\ installer has chosen not to
+output directly.
+
+\bugonpage A385, line 8 (5/14/89)
+
+\ninepoint
+|\def\beginbox{\setbox0=\hbox\bgroup}|
+
+\bugonpage A400, line 18 from the bottom (9/23/89)
+
+\ninepoint\noindent
+page prematurely if you want to pass a signal. \ (Set
+^|\holdinginserts| positive to pass a signal when the contents of\/
+|\box255| will be sent back through the page builder again, if any
+insertions are present.)
+
+\bugonpage A419, lines 4--6 (9/23/89)
+
+\ninepoint\noindent
+shortened or lengthened anyway;
+book preparation with \TeX, as with type, encourages interaction between
+humans and machines.) \
+The lines of the quotations are set ^{flush right} by using
+^|\obeylines| together with a stretchable ^|\leftskip|:
+
+\bugonpage A444, lines 21--26 (9/23/89)
+
+\ninepoint\noindent
+following one, using the specified family and the current size, then
+insert the ligature character and continue as specified by the font;
+two characters may collapse into one, or a new character may appear.
+Otherwise if the font information
+shows a kern between the current symbol and the next, insert a kern item
+after the current Ord atom and move to the next item after that.
+Otherwise (i.e., if no ligature or kern is specified between the present
+text symbol and the following character), go to Rule~17.
+
+\bugonpage A453, lines 12--14 from the bottom (9/23/89)
+
+\begingroup
+\hyphenpenalty=-1000 \pretolerance=-1 \tolerance=1000
+\doublehyphendemerits=-100000 \finalhyphendemerits=-100000
+\ninepoint\noindent
+Exception: The character
+`|.|'~is treated as if it were a \<letter> of code~0
+when it appears in a pattern. Code~0 (which obviously cannot match a nonzero
+|\lccode|) is used by \TeX\ to represent the
+left or right edge of a word when it is being hyphenated.
+
+\endgroup
+
+\bugonpage A454, lines 7--15 from the bottom (9/23/89)
+
+\begingroup
+\hyphenpenalty=-1000 \pretolerance=-1 \tolerance=1000
+\doublehyphendemerits=-100000 \finalhyphendemerits=-100000
+\ddanger If a trial word $l_1\ldots l_n$ has been found by this process,
+hyphenation will still be abandoned unless $n\ge\lambda+\rho$, where
+$\lambda=\max(1,\hbox{|\lefthyphenmin|})$ and
+$\rho=\max(1,\hbox{|\righthyphenmin|})$.
+\ (Plain \TeX\ takes $\lambda=2$ and $\rho=3$.) \ Furthermore, the items
+immediately following the trial word must consist of zero or more
+characters, ligatures, and implicit kerns, followed immediately by
+either glue or an explicit kern or a penalty item or a whatsit or an
+item of vertical mode material from ^|\mark|, ^|\insert|, or ^|\vadjust|.
+Thus, a box or rule or math formula or discretionary following too closely
+upon the trial word will inhibit hyphenation. (Since \TeX\ inserts
+empty discretionaries after ^{explicit hyphens}, these rules imply that
+already-hyphenated compound words will not be further hyphenated by
+the algorithm.)
+
+\endgroup
+
+\bugonpage A455, new copy after line 13 (9/23/89)
+
+\begingroup
+\hyphenpenalty=-1000 \pretolerance=-1 \tolerance=1000
+\doublehyphendemerits=-100000 \finalhyphendemerits=-100000
+\ddanger \looseness=-1
+So far we have assumed that \TeX\ knows only one style of
+hyphenation at a time; but in fact \TeX\ can remember up to 256
+distinct sets of rules, if you have enough memory in your computer. An
+integer parameter called ^|\language| selects the rules actually used;
+every ^|\hyphenation| and ^|\patterns| specification appends new rules
+to those previously given for the current value of\/ |\language|.
+\ (If\/ |\language| is negative or greater than 255, \TeX\ acts as if
+|\language|$\null=0$.) \ All |\patterns| for all languages must be
+given before a paragraph is typeset, if |INITEX| is used for
+typesetting.
+
+\ddanger \TeX\ is able to work with several languages in the same
+paragraph, because it operates as follows. At the beginning of a
+paragraph the ``current language'' is defined to be~0. Whenever a
+character is added to the current paragraph (i.e., in unrestricted
+horizontal mode), the current language is compared to |\language|; if
+they differ, the current language is reset and a whatsit node
+specifying the new current language is inserted before the character.
+Thus, if you say `|\def\french{\language1...}|' and `|mix| |{\french
+franc/ais}| |with| |English|', \TeX\ will put whatsits before the |f|
+and the~|w|; hence it will use language~1 rules when hyphenating
+|franc/ais|, after which it will revert to language~0. You can insert
+the whatsit yourself (even in restricted horizontal mode) by saying
+^|\setlanguage|\<number>; this changes the current language but it
+does not change |\language|.
+
+\endgroup
+
+\bugonpage A459, right column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\badness|, 214, {\it229}, 271.
+
+\bugonpage A461, right column (9/23/89)
+
+\eightpoint
+caron, {\sl see\/} h\'a\v cek.
+
+\bugonpage A464, line 10 (5/15/89)
+
+\eightpoint
+displays, 87, 103, {\it139--145}, {\it166--167},
+
+\bugonpage A464, right column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\emergencystretch|, $\underline{107}$, 274.
+
+\bugonpage A465, left column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\errorcontextlines|, $\underline{34}$, 273, {\it348}.
+
+\bugonpage A466, entry for `fractions' (9/23/89)
+
+\eightpoint[Add page 332 to this entry.]
+
+\bugonpage A466, entry for `French'' (9/23/89)
+
+\eightpoint[Add page 455 to this entry.]
+
+\bugonpage A467, entry for `hexadecimal' (9/23/89)
+
+\eightpoint[Add pages 45, 47--48 to this entry.]
+
+\bugonpage A467, right column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\holdinginserts|, $\underline{125}$, 273, 400.
+
+\bugonpage A467, bottom line (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\hyphenation|, 277, {\it419}, $\underline{452}$--$\underline{453}$, 455.
+
+\bugonpage A468, right column (9/23/89)
+
+\eightpoint
+infinite badness, 97, 107, 111, 229, 317.
+
+\bugonpage A468, right column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\inputlineno|, 214, 271.
+
+\bugonpage A469, entry for kerns (9/23/89)
+
+\eightpoint[Add pages 286 and 444 to this entry.]
+
+\bugonpage A469, left column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\language| (hyphenation method), 273, $\underline{455}$.
+
+\bugonpage A469, right column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\lefthyphenmin|, 273, {\it364}, $\underline{454}$.
+
+\bugonpage A470, entry for ligatures (9/23/89)
+
+\eightpoint[Add pages 286 and 444 to this entry.]
+
+\bugonpage A472, left column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\noboundary|, 283, $\underline{286}$, 290.
+
+\bugonpage A473, right column (9/23/89)
+
+\eightpoint
+overfull boxes, 27--30, 94, 229, 238,\par
+\indent\qquad 302--303, 307, 400.\par
+\indent\quad avoiding, 107.
+
+\bugonpage A474, left column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\patterns|, 277, $\underline{453}$, 455.
+
+\bugonpage A476, left column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\righthyphenmin|, 273, {\it364}, $\underline{454}$.
+
+\bugonpage A476, right column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\setlanguage|, 287, $\underline{455}$.
+
+\bugonpage A476, right column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\showboxbreadth|, 273, $\underline{302}$, 303, {\it348}.\par
+\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\showboxdepth|, 79, 273, $\underline{302}$, 303, {\it348}.
+
+\bugonpage A479, left column (9/23/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\tolerance|, {\it29--30}, 91, 94, $\underline{96}$, 107, 272,\par
+\indent\qquad{\it317}, {\it333}, {\it342}, {\it348}, {\it364}, {\it451}.
+
+\bugonpage A481, right column, last six entries (9/23/89)
+
+\def\frac#1/#2{\leavevmode\kern.1em
+ \raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em
+ /\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}%
+\eightpoint
+\frac1/2, 67, 332.\par
+1/2, in unslashed form, 141, 186.\par
+\<4-bit number>, $\underline{271}$.\par
+\<8-bit number>, $\underline{271}$, 276--278.\par
+\<15-bit number>, $\underline{271}$, 277, 289, 291.\par
+\<27-bit number>, $\underline{271}$, 289, 291.\par
+
+\bugonpage A483, lines 15 and 21 (9/23/89)
+
+\noindent[Delete these two lines, as TUG's address is no longer c/o AMS.]
+
+ % volume B
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage Bvii, top two lines (4/21/89)
+
+{\hsize=29pc
+\tenpoint\noindent
+{\it {\sltt WEB} documentation for four utility programs that are
+often used in conjunction with \TeX: {\sltt POOLtype}, {\sltt TFtoPL},
+{\sltt PLtoTF}, and {\sltt DVItype}.}
+\par}
+
+\bugonpage B2, line 32 (6/20/89)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{banner}\equiv\hbox{\tt\char'23}$%
+{\tt This\]is\]TeX,\]Version\]2.991\char'23}\quad
+$\{\,$printed when \TeX\ starts$\,\}$
+
+\bugonpage B118, lines 2--4 (3/2/89)
+
+\ninepoint\noindent\hskip10pt
+{\bf begin if\/} $\\{cur\_level}>\\{level\_one}$ {\bf then}\par
+\noindent\hskip20pt{\bf begin} \\{check\_full\_save\_stack}; \
+ $\\{save\_type}(\\{save\_ptr})\gets\\{insert\_token}$;\par
+\noindent\hskip20pt$\\{save\_level}(\\{save\_ptr})\gets\\{level\_zero}$; \
+ $\\{save\_index}(\\{save\_ptr})\gets t$; \ \\{incr}(\\{save\_ptr});\par
+\noindent\hskip20pt{\bf end};
+
+\bugonpage B182, line 13 becomes two lines (6/20/89)
+
+\ninepoint\noindent
+$k,\\{kk}$: \\{small\_number};\quad
+ $\{\,$number of digits in a decimal fraction$\,\}$\par\noindent
+$p,q$: \\{pointer};\quad
+ $\{\,$top of decimal digit stack$\,\}$
+
+\bugonpage B182, line 15 from the bottom (6/20/89)
+
+\ninepoint\noindent\hskip10pt
+{\bf begin} $k\gets0$; $p\gets\\{null}$; \\{get\_token};\quad
+ $\{\,$\\{point\_token} is being re-scanned$\,\}$
+
+\bugonpage B182, line 11 from the bottom (6/20/89)
+
+\ninepoint\noindent\hskip30pt
+{\bf begin} $q\gets\\{get\_avail}$; $\\{link}(q)\gets p$;
+ $\\{info}(q)\gets\\{cur\_tok}-\\{zero\_token}$;
+ $p\gets q$; $\\{incr}(k)$;
+
+\bugonpage B182, line 8 from the bottom (6/20/89)
+
+\ninepoint\noindent
+\\{done1}: {\bf for} $\\{kk}\gets k$ {\bf downto} 1 {\bf do}\par
+\noindent\hskip20pt
+{\bf begin} $\\{dig}[kk-1]\gets\\{info}(p)$; $q\gets p$; $p\gets\\{link}(p)$;
+ $\\{free\_avail}(q)$;\par
+\noindent\hskip20pt{\bf end};\par
+\noindent\hskip10pt$f\gets\\{round\_decimals}(k)$;
+
+\bugonpage B332, lines 11 and 12 from the bottom (4/8/89)
+
+\ninepoint
+\noindent\hskip10pt
+{\bf begin if\/} $\\{cur\_align}=\\{null}$ {\bf then}
+ \\{confusion}({\tt\char'23endv\char'23});\par\noindent\hskip10pt
+$q\gets\\{link}(\\{cur\_align})$; \ {\bf if\/} $q=\\{null}$ {\bf then}
+ \\{confusion}({\tt\char'23endv\char'23});
+
+\bugonpage B466, line 5 becomes three lines (6/7/89)
+
+\ninepoint
+\noindent
+$\\{mmode}+\\{halign}$: {\bf if\/} \\{privileged} {\bf then}\par\noindent
+\hskip20pt{\bf if\/} $\\{cur\_group}=\\{math\_shift\_group}$
+ {\bf then} \\{init\_align}\par\noindent\hskip20pt
+{\bf else} \\{off\_save};
+
+\bugonpage B518, line 25 (8/31/89)
+
+\ninepoint\noindent\hskip10pt
+$\\{undump}(\\{lo\_mem\_stat\_max}+1)(\\{lo\_mem\_max})(\\{rover})$; \
+$p\gets\\{mem\_bot}$; \ $q\gets\\{rover}$;
+
+ % volume C
+\hsize=29pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+
+\buginvol C, in general (9/23/89)
+
+\ninepoint\noindent
+[Change `127' to `255' and `128' to `256' in contexts referring to character
+codes. This happens on pages 188(thrice) and 251.]
+
+\bugonpage C91, lines 12 and 13 (8/31/89)
+
+\begintt
+\mode=cheapo; input newface
+\endtt
+and the same file should also produce a high-resolution font if we start with
+
+\bugonpage C204, line 4 (8/18/89)
+
+\ninepoint\noindent
+so that
+\\{currenttransform} multiplies all $y$~coordinates by
+\\{aspect\_ratio}, when paths are\cutpar
+
+\bugonpage C212, lines 24--27 (9/30/89)
+
+\ninepoint
+\begindisplay
+{\it boundarychar}\quad&the right boundary character for ligatures and kerns\cr
+\enddisplay
+All of these quantities are numeric. They are initially zero at the
+start of a job, except for {\it year\/}, {\it month\/},
+ {\it day\/}, and {\it time\/}, which
+are initialized to the time the run began; furthermore, {\it boundarychar\/} is
+initially~$-1$. A {\it granularity\/} of zero is equivalent to
+ $\hbox{\it granularity\/}=1$.
+A preloaded base file like plain \MF\ will usually give nonzero values to
+several other internal quantities on this list.
+
+\bugonpage C259, lines 16 and 17 from the bottom (5/14/89)
+
+\tenpoint
+\noindent
+|screenchars|; \ |screenstrokes|; \ |imagerules|; \ |gfcorners|; \
+|nodisplays|;\hfil\break
+|notransforms|; \ |input| \<filename>.
+
+\bugonpage C282, the three lines following the chart (9/30/89)
+
+\tenpoint\noindent
+\MF\ can also be configured to accept any or all of the character codes
+128--255.
+However, \MF\ programs that make use of anything in addition to the 95
+standard ASCII characters cannot be expected to run on other systems, so
+the use of extended character sets is discouraged.
+
+\bugonpage C316, bottom 14 lines and top 30 of page C317 (9/30/89)
+
+\ninepoint
+Ligature information and kerning information is specified in short
+``^{ligtable programs}'' of a particularly simple form. Here's an example
+that illustrates most of the features (although it is not a serious
+example of typographic practice):
+\beginlines
+^|ligtable|| "f": "f" =: oct"013", "i" |\||=: oct"020", skipto 1;|
+|ligtable "o": "b": "p": "e" kern .5u#, "o" kern .5u#, "x" kern-.5u#,|
+| 1:: "!" kern u#;|
+\endlines
+This sequence of instructions can be paraphrased as follows:
+\smallskip
+\hangindent 3pc
+Dear \TeX, when you're typesetting an~`f' with this font, and when the
+following character also belongs to this font, look at it closely because
+you might need to do something special: If that following character is
+another~`f', replace the two f's by character code |oct"013"|
+[namely `\char'13'\kern.5pt];
+if it's an `i', retain the `f' but replace the `i' by character code
+|oct"020"| [a dotless `\char'20'\kern.5pt];
+otherwise skip down to label `|1::|' for further instructions.
+When you're typesetting an `o' or~`b' or~`p', if the next input to \TeX\ is
+`e' or~`o', add a half unit
+of space between the letters; if it's an `x', subtract a half unit; if it's an
+exclamation point, add a full unit. The last instruction applies also
+to exclamation points following~`f' (because of the label `|1::|').
+\smallskip\noindent
+When a character code appears in front of a colon, the colon ``labels''
+the starting place for that character's ligature and kerning program,
+which continues to the end of the ligtable statement. A double colon denotes
+a ``local label''; a |skipto| instruction advances to the next matching local
+label, which must appear before 128 ligtable steps intervene. The special
+label \|\||:| can be used to initiate ligtable instructions for an invisible
+``left boundary character'' that is implicitly present just before every
+word; an invisible ``right boundary character'' equal to {\it boundarychar\/} is
+also implicitly present just after every word, if {\it boundarychar\/}
+ lies between
+0 and~255.
+
+The general syntax for ligtable programs is pretty easy to guess from
+these examples, but we ought to exhibit it for completeness:
+\beginsyntax \chardef\\=`\|
+<ligtable command>\is[ligtable]<ligtable program><optional skip>
+<ligtable program>\is<ligtable step>\alt<ligtable program>[,]<ligtable step>
+<optional skip>\is[,] [skipto]<code>\alt<empty>
+<ligtable step>\is<code><ligature op><code>
+ \alt<code>[kern]<numeric expression>
+ \alt<label><ligtable step>
+<ligature op>\is[=:]\alt[\\=:]\alt[\\=:>]\alt[=:\\]\alt[=:\\>]%
+ \alt[\\=:\\]\alt[\\=:\\>]\alt[\\=:\\>>]
+<label>\is<code>[:]\alt<code>[::]\alt[\\\\:]
+<code>\is<numeric expression>\alt<string expression>
+\endsyntax
+A \<code> should have a numeric value between 0 and 255, inclusive,
+after having been rounded to the nearest integer; or it should be a
+string of length~1, in which case it denotes the corresponding
+^{ASCII} code (Appendix~C\null). For example, |"A"| and |64.61| both
+specify the code value 65. Vertical bars to the left or right of `|=:|'
+tell \TeX\ to retain the original left and/or right character that invoked a
+ligature. Additional `|>|' signs tell \TeX\ to advance its focus of attention
+instead of doing any further ligtable operations at the current
+character position.
+
+\bugonpage C338, lines 21 and 22 (9/30/89)
+
+\ninepoint\noindent
+and 127--255 have to be specified with the `|#|' option,
+on non-fancy installations of \TeX,
+and so does code 35 (which is the ASCII code of `|#|' itself).
+
+\bugonpage C346, left column, after line 14 (9/30/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+\||=:|, {\it316}, $\underline{317}$.\par
+\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+\||=:>|, $\underline{317}$.\par
+\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|=:|\|, $\underline{317}$.\par
+\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|=:|\||>|, $\underline{317}$.\par
+\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+\||=:|\|, $\underline{317}$.\par
+\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+\||=:|\||>|, $\underline{317}$.\par
+\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+\||=:|\||>>|, $\underline{317}$.\par
+
+\bugonpage C346, left column, after line 31 (9/30/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|::| (local label), $\underline{317}$.\par
+\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+\|\||:| (left boundary label), $\underline{317}$.\par
+
+\bugonpage C347, left column (9/30/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|boundarychar|, 212, 317.
+
+\bugonpage C352, left column (9/30/89)
+
+\eightpoint[Change `\<ligature replacement>' to `\<ligature op>'.]
+
+\bugonpage C354, left column (9/30/89)
+
+\eightpoint
+\<optional skip>, 217.
+
+\bugonpage C356, left column (9/30/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|skipto|, {\it316}, $\underline{317}$.
+
+ % Volume D
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage Dvi, bottom two lines, and top lines of page vii (4/21/89)
+
+{\hsize=29pc \tenpoint
+\textindent\bull ``\MF\/ware'' by Donald~E. Knuth, Tomas~G. Rokicki, and
+Ar\-thur~L. Samuel, Stanford Computer Science Report 1255 (Stanford,
+California, April 1989), 207~pp. \ {\it The {\sltt WEB} programs for
+four utility programs that are often used in conjunction with
+\slMF\kern1pt: {\sltt GFtype}, {\sltt GFtoPK}, {\sltt GFtoDVI},
+and {\sltt MFT}.}
+\par}
+
+\bugonpage D63, line 9 (8/31/89)
+
+\tenpoint\noindent
+\\{mem}, so we
+allow pointers to assume any \\{halfword} value. The minimum memory
+index represents\cutpar
+
+\bugonpage D63, line 28 (8/31/89)
+
+\tenpoint\centerline{$\\{null}=\\{mem\_min}<\\{lo\_mem\_max}<
+\\{hi\_mem\_min}<\\{mem\_top}\le\\{mem\_end}\le\\{mem\_max}$.}
+
+\bugonpage D67, in the July 1987 printing (4/7/89)
+
+\ninepoint\noindent
+[Delete line 7, which has a redundant `{\bf if\/} $r=p$ {\bf then}';
+ move line 8 to the left 10 points for alignment; and restore the following
+ line (which was deleted by mistake after line 8):
+
+ \noindent\hskip10pt
+ $\\{node\_size}(p)\gets q-p$\quad$\{\,$reset the size in case it grew$\,\}$
+
+ \noindent
+ These corrections are needed only in the reprinting made July, 1987.]
+
+\bugonpage D228, in the July 1987 printing (4/7/89)
+
+\ninepoint\noindent
+[Delete lines 14--15, which were inserted erroneously from a previous errata
+list; and restore the following lines (which were deleted by mistake):
+
+\noindent\hskip20pt
+{\bf begin} \\{double}(\\{max\_coef}); \
+\\{double}(\\{x0}); \
+\\{double}(\\{x1}); \
+\\{double}(\\{x2});\par\noindent\hskip20pt
+\\{double}(\\{y0}); \
+\\{double}(\\{y1}); \
+\\{double}(\\{y2});\par\noindent\hskip20pt
+{\bf end}
+
+\noindent
+These corrections are needed only in the reprinting made July, 1987.]
+
+\bugonpage D248, in the July 1987 printing (4/7/89)
+
+\ninepoint\noindent
+[Delete line 16, which begins with `$d\gets\\{take\_fraction}$';
+and restore the following line (which was deleted by mistake after line 22):
+
+\noindent\hskip10pt
+{\bf if\/} $d<\\{alpha}$ {\bf then} $d\gets\\{alpha}$
+
+\noindent
+These corrections are needed only in the reprinting made July, 1987.]
+
+\bugonpage D389, line 10 (6/20/89)
+
+\ninepoint\noindent\hskip20pt
+\\{help1}({\tt\char'23
+The\]expression\]above\]should\]have\]been\]a\]number\]>=3/4.\char'23});
+
+\bugonpage D504, line 25 (8/31/89)
+
+\ninepoint\noindent\hskip10pt
+$\\{undump}(\\{lo\_mem\_stat\_max}+1)(\\{lo\_mem\_max})(\\{rover})$; \
+$p\gets\\{mem\_min}$; \ $q\gets\\{rover}$;
+
+\bugonpage D510, in the July 1987 printing (4/7/89)
+
+\ninepoint\noindent
+[Move the 7th-to-last line, which begins with `\\{internal}[\\{fontmaking}]',
+one line down, and indent it to the right by 10 more points.
+This correction is needed only in the reprinting made July, 1987.]
+
+ % volume E
+\hsize=29pc
+\def\dashto{\mathrel{\hbox{-\kern-.05em}\mkern3.9mu\hbox{-\kern-.05em}}}
+
+\bugonpage Exiii, bottom four lines (5/5/89)
+
+{\hsize=29pc \tenpoint
+\textindent\bull ``Metamarks: Preliminary studies for a Pandora's Box of
+shapes'' by Neenie Billawala,
+Stanford Computer Science Report 1256 (Stanford,
+California, May 1989), 132~pp. \ {\it Lavishly illustrated studies in
+parameter variation, leading to the design of a new family of
+typefaces called Pandora.}
+\par}
+
+\bugonpage E401, bottom line (5/16/89)
+
+\ninepoint\noindent
+{\bf \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}%
+math\_fit}$(-.3\\{cap\_height}\0*\\{slant}-.5u\0,\\{ic}\0)$;\par\noindent
+{\bf penlabels}$(1,2,3,4,5,6,7,8)$; {\bf endchar\/};
+\smallskip\noindent
+[some points and labels are missing at the tip of the tail on page 400]
+ \bye
+ Now here are some that I will make soon!
+
diff --git a/systems/knuth/dist/errata/errata.four b/systems/knuth/dist/errata/errata.four
new file mode 100644
index 0000000000..11d0435e74
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.four
@@ -0,0 +1,1052 @@
+% Bugs (sigh) in Computers \& Typesetting
+
+\input manmac
+\font\sltt=cmsltt10
+\font\niness=cmss9
+\font\ninessi=cmssi9
+\proofmodefalse
+\raggedbottom
+\output{\hsize=29pc \onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\today{\number\day\
+ \ifcase\month\or
+ Jan\or Feb\or Mar\or Apr\or May\or Jun\or
+ Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi
+ \ \number\year}
+
+\def\cutpar{{\parfillskip=0pt\par}}
+
+\def\rhead{Bugs in {\tensl Computers \& Typesetting, 1987--1988}}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\buginvol#1(#2) \par{\bigbreak\penalty-1000\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Volume #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\slMF{{\manual 89:;}\-{\manual <=>:}} % slant the logo
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\newcount\nn
+\newdimen\nsize \newdimen\msize \newdimen\ninept \ninept=9pt
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+
+\noindent This is a list of all corrections made to {\sl Computers \&
+Typesetting}, Volumes A--E\null, between
+16 June 1987 and 20 February 1989. Corrections made to
+the softcover version of {\sl The \TeX book} are the same as corrections to
+Volume~A\null. Corrections to the softcover version of {\sl The
+\slMF\kern1ptbook\/} are the same as corrections to Volume~C\null.
+Some of these corrections have already been made in reprintings
+of the books. Some of these corrections affect the indexes and
+mini-indexes of Volumes B~and~D in ways not shown here. Corrections
+made up to 15 June 1987 appear in other files.
+ % volume A
+
+\bugonpage A159, line 22 (2/15/88)
+
+\ninepoint\noindent
+`|\nolimits|' if the normal |\displaylimits|
+convention has been overridden; a Rad\cutpar
+
+\bugonpage A213, lines 34--35 (12/23/87)
+
+\ninepoint\noindent
+text will be a single control sequence token, defined to be like |\relax| if
+its meaning is currently undefined.
+
+\bugonpage A299, line 30 (7/6/88)
+
+\ninepoint\indent\tt
+Fatal format file error; I'm stymied.
+
+\bugonpage A326, line 12 (12/12/87)
+
+\ninepoint\noindent
+its natural width. The |\hbox| version also invokes |\everymath|.
+
+\bugonpage A359, line 2 (11/6/88)
+
+\ninepoint\noindent
+|\mathchardef\ldotp="613A\mathchardef\cdotp="6201\mathchardef\colon="603A|
+
+\bugonpage A359, lines 35--38 (5/24/88)
+
+\ninepoint\noindent
+|\def\updownarrow{\delimiter"326C33F } \def\arrowvert{\delimiter"033C000 }|%
+ \par\noindent
+|\def\Updownarrow{\delimiter"326D377 } \def\Arrowvert{\delimiter"033D000 } |%
+ \par\noindent
+|\def\vert{\delimiter"026A30C } \def\Vert{\delimiter"026B30D } |%
+ \par\noindent
+|\def\backslash{\delimiter"026E30F } \def\bracevert{\delimiter"033E000 }|
+
+\bugonpage A364, line 35 (11/6/88)
+
+\ninepoint\noindent
+|\def\fmtname{plain}\def\fmtversion{2.94} % identifies the current format|
+
+\bugonpage A379, line 15 (10/12/87)
+
+\ninepoint
+|\def\deleterightmost#1{\edef#1{\expandafter\xyzzy#1\xyzzy}}|
+
+\bugonpage A383, lines 7--15 from the bottom (1/4/89)
+
+\begintt
+ 209 strings out of 1685
+ 1659 string characters out of 17636
+ 27618 words of memory out of 52821
+ 1172 multiletter control sequences out of 2500
+\endtt
+Consequently there was plenty of room for more macros: $52821-27618=
+25203$ unused cells of main memory, $2500-1172=1328$ of name memory,
+$1685-209=1476$ of string memory, and $17636-1659=15977$ of character memory.
+But a fairly large \TeX\ was being used, and only the macros of
+Appendices B and~E were loaded; in other circumstances it might have
+been necessary to conserve space.
+
+\bugonpage A454, lines 23--29 (8/13/87)
+
+\begingroup
+\hyphenpenalty=-1000 \pretolerance=-1 \tolerance=1000
+\doublehyphendemerits=-100000 \finalhyphendemerits=-100000
+\ddanger If a suitable starting letter is found, let it be in font~$f$.
+Hyphenation is abandoned unless the |\hyphenchar| of~$f$ is between
+0 and~255, and unless a character of that number exists in the font.
+If this test is passed, \TeX\ continues to scan forward
+until coming to something that's not one of the following three
+``admissible items'': (1)~a character in font~$f$ whose |\lccode|
+is nonzero; (2)~a ligature formed entirely from characters of type~(1);
+(3)~an implicit kern. The first inadmissible item terminates this part of
+the process; the trial word consists of all the letters found in admissible
+items. Notice that all of these letters are in font~$f$.
+
+\endgroup % end the special hyphenation conventions
+
+\bugonpage A458, left column, line 19 (2/15/88)
+
+\eightpoint\indent
+|\|\| ( $\Vert$ ), {\it146--147}, {\it171}, $\underline{361}$, 435, 438.
+
+\bugonpage A462, left column, line 7 (10/9/87)
+
+\eightpoint\indent\qquad 152, 178, $\underline{360}$.
+
+\bugonpage A463, left column (4/17/88)
+
+\eightpoint\indent
+\hbox to0pt{\hss\lower1pt\hbox{*}}|\day|, 273, 349, {\it406}.
+
+\bugonpage A464, left column, under Displays (12/8/88)
+
+\eightpoint\indent\quad
+non-centered, 186, 326, 375--376, 420--421.
+
+\bugonpage A465, entry for {\tt\char`\\everymath} (12/12/87)
+
+\eightpoint\indent[Include also a reference to page 326.]
+
+\bugonpage A465, right column (7/6/88)
+
+\eightpoint\indent{\tt Fatal format file error}, 299.
+
+\bugonpage A473, entry for `page builder' (8/13/87)
+
+\eightpoint\indent\quad when exercised, 122, 280--283, 286--287.
+
+\bugonpage A474, left column (12/27/88)
+
+\eightpoint\indent
+\hbox to0pt{\hss\lower1pt\hbox{*}}|\parshape|, 101--102, 214, 271, 277, 283,
+
+\bugonpage A480, right column (2/15/88)
+
+\eightpoint\indent|\vdots| ( $\vdots$ ), {\it177}, $\underline{359}$.
+
+\bugonpage A481, right column (7/3/87)
+
+\eightpoint|\z@|, $\underline{347}$, 348.\par
+|\z@skip|, $\underline{347}$, 348.
+ % volume B
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage B2, line 32 (2/20/89)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{banner}\equiv\hbox{\tt\char'23}$%
+{\tt This\]is\]TeX,\]Version\]2.97\char'23}\quad
+$\{\,$printed when \TeX\ starts$\,\}$
+
+\bugonpage B38, lines 7--9 from the bottom (11/6/88)
+
+\tenpoint\noindent[Delete this paragraph; it is being moved to page B214.]
+
+\bugonpage B38, line 5 from the bottom (12/14/88)
+
+\ninepoint\noindent\kern10pt
+{\bf begin if\/} \\{log\_opened} {\bf then} $\\{selector}\gets\\{term\_and\_log}$
+
+\bugonpage B39, line 5 (12/14/88)
+
+\ninepoint\noindent\kern50pt
+{\bf if\/} \\{log\_opened} {\bf then} \\{error};
+
+\bugonpage B52, line 5 (8/13/87)
+
+\tenpoint\noindent
+cannot be done, i.e., if $\\{hi\_mem\_min}=\\{lo\_mem\_max}+1$,
+we have to quit.
+
+\bugonpage B54, lines 34--35 (7/9/88)
+
+\ninepoint\noindent\kern10pt
+{\bf begin if\/} $\\{hi\_mem\_min}-\\{lo\_mem\_max}\ge1998$
+ {\bf then} $t\gets\\{lo\_mem\_max}+1000$\par\noindent\kern10pt
+{\bf else} $t\gets\\{lo\_mem\_max}+1+(\\{hi\_mem\_min}-\\{lo\_mem\_max})
+\,\mathbin{\bf div}\,2$;\quad\kern-4pt
+$\{\,\\{lo\_mem\_max}+2\le t<\\{hi\_mem\_min}\,\}$
+
+\bugonpage B108, new line after line 8 (5/24/88)
+
+\ninepoint\noindent\kern20pt
+$d$: \\{integer};\quad
+$\{\,$number of characters in incomplete current string$\,\}$
+
+\bugonpage B108, lines 31--33 (5/24/88)
+
+\ninepoint\noindent\kern10pt
+$\\{str\_room}(l)$; $d\gets\\{cur\_length}$;\par\noindent\kern10pt
+{\bf while} $\\{pool\_ptr}>\\{str\_start}[\\{str\_ptr}]$ {\bf do}
+ \par\noindent\kern20pt
+{\bf begin} \\{decr}(\\{pool\_ptr});
+ $\\{str\_pool}[\\{pool\_ptr}+l]\gets\\{str\_pool}[\\{pool\_ptr}]$;
+ \par\noindent\kern20pt
+{\bf end};\quad$\{\,$move current string up to make room for another$\,\}$
+\par\noindent\kern10pt
+{\bf for} $k\gets j$ {\bf to} $j+l-1$ {\bf do} $\\{append\_char}(\\{buffer}[k])$;
+\par\noindent\kern10pt
+$\\{text}(p)\gets\\{make\_string}$; $\\{pool\_ptr}\gets\\{pool\_ptr}+d$;
+
+\bugonpage B115, line 12 (4/28/88)
+
+\ninepoint\noindent\hskip10pt
+$\\{group\_code}=0\to\\{max\_group\_code}$;\quad
+ $\{\,$\\{save\_level} for a level boundary$\,\}$
+
+\bugonpage B141, line 19 (4/28/88)
+
+\ninepoint\noindent
+\\{par\_token}: \\{halfword};\quad
+ $\{\,$token representing `|\par|'$\,\}$
+
+\bugonpage B150, line 24 (4/28/88)
+
+\tenpoint\noindent{\bf 358.\quad}%
+The present point in the program is reached only when the \\{expand}
+routine has inserted\cutpar
+
+\bugonpage B151, mini-index (4/28/88)
+
+\eightpoint\noindent
+Delete the entry for `\\{no\_expand}'; replace it by:
+
+\indent\\{expand}: {\bf procedure}, \S366.
+
+\bugonpage B154, lines 25, 29, 34 respectively (9/20/87)
+
+\ninepoint\noindent\hskip20pt
+$\\{cvl\_backup},\\{radix\_backup},\\{co\_backup}$: \\{small\_number};\quad
+ $\{\,$to save \\{cur\_val\_level}, etc.$\,\}$\par\noindent\hskip10pt
+$\\{co\_backup}\gets\\{cur\_order}$;
+ $\\{backup\_backup}\gets\\{link}(\\{backup\_head})$;\par\noindent\hskip10pt
+$\\{cur\_order}\gets\\{co\_backup}$;
+ $\\{link}(\\{backup\_head})\gets\\{backup\_backup}$;
+
+\bugonpage B155, new entry for mini-index (9/20/87)
+
+\eightpoint\indent
+\\{cur\_order}: \\{glue\_ord}, \S447.
+
+\bugonpage B156, line 28 (12/23/87)
+
+\ninepoint\noindent\hskip20pt
+{\bf begin }$\\{eq\_define}(\\{cur\_cs},\\{relax},256)$;
+
+\bugonpage B157, mini-index (12/23/87)
+
+\eightpoint\noindent
+Delete the entries for `\\{eqtb}' and `\\{frozen\_relax}'; replace them
+by the following:
+
+\indent\\{eq\_define}: {\bf procedure}, \S227.\par
+$\\{relax}=0$, \S207.\par
+
+\bugonpage B162, lines 12--14 (4/30/88)
+
+\ninepoint\noindent\hskip10pt
+{\bf repeat} $\\{link}(\\{temp\_head})\gets\\{null}$;\par\noindent\hskip20pt
+{\bf if\/} $(\\{info}(r)>\\{match\_token}+127)\lor
+ (\\{info}(r)<\\{match\_token})$ {\bf then}
+ $s\gets\\{null}$\par\noindent\hskip20pt
+{\bf else begin} $\\{match\_chr}\gets\\{info}(r)-\\{match\_token}$; \
+ $s\gets\\{link}(r)$; \ $r\gets s$; \ $p\gets\\{temp\_head}$; \
+ $m\gets 0$;
+
+\bugonpage B177, bottom line before mini-index (7/13/88)
+
+\ninepoint\noindent\hskip10pt
+$\\{cur\_val}\gets0$; \
+ $\\{cur\_val\_level}\gets\\{int\_val}$; \
+ $\\{radix}\gets0$; \
+ $\\{cur\_order}\gets0$;
+
+\bugonpage B181, line 31 (4/28/88)
+
+\ninepoint\noindent
+[Change `$x$ units per sp' to `$x$ sp per unit'! This change also
+should be made on line~1 of page B183 and line $-8$ of page B590.]
+
+\bugonpage B188, line 8 (5/25/88)
+
+\ninepoint\noindent
+{\bf function} $\\{str\_toks}(b:\\{pool\_pointer})$: \\{pointer};\quad
+ $\{\,$changes the string \\{str\_pool}$[b\to\\{pool\_ptr}]$ to a token list$\,\}$
+
+\bugonpage B188, line 13 (5/25/88)
+
+\ninepoint\noindent\kern10pt
+{\bf begin} \\{str\_room}(1); $p\gets\\{temp\_head}$;
+ $\\{link}(p)\gets\\{null}$; $k\gets b$;
+
+\bugonpage B188, line 20 (5/25/88)
+
+\ninepoint\noindent\kern10pt
+$\\{pool\_ptr}\gets b$; $\\{str\_toks}\gets p$;
+
+\bugonpage B188, new line after line 28 (5/25/88)
+
+\ninepoint\noindent\kern20pt
+$b$: \\{pool\_pointer};\quad$\{\,$base of temporary string$\,\}$
+
+\bugonpage B188, line 31 (5/25/88)
+
+\ninepoint\noindent\kern10pt
+{\bf else begin} $\\{old\_setting}\gets\\{selector}$;
+ $\\{selector}\gets\\{new\_string}$; $b\gets\\{pool\_ptr}$;
+
+\bugonpage B188, line 41 (5/25/88)
+
+\ninepoint\noindent\kern20pt
+$\\{selector}\gets\\{old\_setting}$; $\\{the\_toks}\gets\\{str\_toks}(b)$;
+
+\bugonpage B190, lines 16--18 (5/25/88)
+
+\ninepoint\noindent\kern20pt
+$b$: \\{pool\_pointer};\quad$\{\,$base of temporary string$\,\}$\par
+\noindent\kern10pt
+{\bf begin} $c\gets\\{cur\_chr}$;
+ $\langle\,$Scan the argument for command $c${\eightrm\kern.5em471}$\,\rangle$;
+\par\noindent\kern10pt
+$\\{old\_setting}\gets\\{selector}$;
+$\\{selector}\gets\\{new\_string}$; $b\gets\\{pool\_ptr}$;
+$\langle\,$Print the result of command~$c${\eightrm\kern.5em472}$\,\rangle$;
+\par\noindent\kern10pt
+$\\{selector}\gets\\{old\_setting}$;
+$\\{link}(\\{garbage})\gets\\{str\_toks}(b)$;
+$\\{ins\_list}(\\{link}(\\{temp\_head}))$;
+
+\bugonpage B210, line 36 (5/25/88)
+
+\ninepoint\noindent\kern10pt
+{\bf begin if} $(\\{pool\_ptr}+\\{name\_length}>\\{pool\_size})
+ \lor(\\{str\_ptr}=\\{max\_strings})\lor(\\{cur\_length}>0)$ {\bf then}
+
+\bugonpage B211, new line of code before the mini-index (12/14/88)
+
+\ninepoint\noindent
+\\{log\_opened}: \\{boolean};\quad$\{\,$has the transcript file been opened?$\,\}$
+
+\bugonpage B212, line 5 (12/14/88)
+
+\ninepoint\noindent\kern10pt
+$\\{job\_name}\gets0$; \ $\\{name\_in\_progress}\gets\\{false}$; \
+$\\{log\_opened}\gets\\{false}$;
+
+\bugonpage B213, line 24 (12/14/88)
+
+\ninepoint\noindent\kern10pt
+$\\{log\_name}\gets\\{a\_make\_name\_string}(\\{log\_file})$; \
+$\\{selector}\gets\\{log\_only}$; \
+$\\{log\_opened}\gets\\{true}$;
+
+\bugonpage B214, lines 2 and 3 (12/14/88)
+
+\tenpoint\noindent
+messages or even to \\{show\_context}.
+The \\{prompt\_file\_name} routine can result in a \\{fatal\_error},
+but the \\{error}
+routine will not be invoked because \\{log\_opened} will be false.
+\par\noindent\hskip10pt
+The normal idea of \\{batch\_mode} is that nothing at all should be written
+on the terminal. However, in the unusual case that
+no log file could be opened, we make an exception and allow
+an explanatory message to be seen.
+
+\bugonpage B214, lines 7--11 reduce to a single line (12/14/88)
+
+\ninepoint\noindent\hskip10pt
+{\bf begin} $\\{selector}\gets\\{term\_only}$;
+
+\bugonpage B224, second-last line (4/28/87)
+
+\ninepoint\noindent
+\\{done}: {\bf if} \\{file\_opened} {\bf then} \\{b\_close}(\\{tfm\_file});\par
+\noindent\hskip10pt $\\{read\_font\_info}\gets g$;
+
+\bugonpage B229, lines 6--8 (11/17/87)
+
+\tenpoint\noindent
+than $2^{27}$.
+If $z<2^{23}$, the individual multiplications $b\cdot z$,
+$c\cdot z$, $d\cdot z$ cannot overflow; otherwise we will divide $z$ by 2,
+4, 8, or 16, to obtain a multiplier less than $2^{23}$, and we can
+compensate for this later. If $z$ has thereby been replaced by
+$z^\prime=z/2^e$, let $\beta=2^{4-e}$; we shall compute
+
+\bugonpage B229, lines 11--12 (11/17/87)
+
+\tenpoint\noindent
+if $a=0$, or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$.
+This calculation must be done exactly, in order to guarantee portability
+of \TeX\ between computers.
+
+\bugonpage B230, lines 2--5 (11/17/87)
+
+\ninepoint
+\noindent\hskip10pt{\bf begin} $\\{alpha}\gets16$;\par
+\noindent\hskip10pt{\bf while} $z\ge\oct{40000000}$ {\bf do}\par
+\noindent\hskip20pt{\bf begin} $z\gets z\ {\bf div}\ 2$; \
+ $\\{alpha}\gets\\{alpha}+\\{alpha}$; \ {\bf end};\par
+\noindent\hskip10pt$\\{beta}\gets256\ {\bf div}\ \\{alpha}$; \
+ $\\{alpha}\gets\\{alpha}\ast z$;\par
+
+\bugonpage B245, new entry for mini-index (8/7/87)
+
+\eightpoint\indent
+\\{cur\_s}: \\{integer}, \S616.
+
+\bugonpage B254, line 29 (8/7/87)
+
+\ninepoint\noindent
+\\{cur\_s}: \\{integer};\quad
+$\{\,$current depth of output box nesting, initially $-1\,\}$
+
+\bugonpage B254, line 31 (8/7/87)
+
+\ninepoint\noindent
+[Remove the statement `$\\{cur\_s}\gets-1$;' and put it on page B244 at the
+end of line 31.]
+
+\bugonpage B259, line 13 (11/9/87)
+
+\ninepoint\noindent\hskip20pt
+{\bf begin }$\\{rule\_wd}\gets\\{rule\_wd}+10$;\quad
+$\{\,$compensate for floating-point rounding$\,\}$\par\noindent\hskip20pt
+$\\{edge}\gets\\{cur\_h}+\\{rule\_wd}$; $\\{lx}\gets0$;
+$\langle\,$Let \\{cur\_h} be the position of the first box, and set
+
+\bugonpage B259, line 17 (11/9/87)
+
+\ninepoint\noindent\hskip20pt
+$\\{cur\_h}\gets\\{edge}-10$; {\bf goto} \\{next\_p};
+
+\bugonpage B263, line 21 (11/9/87)
+
+\ninepoint\noindent\hskip20pt
+{\bf begin }$\\{rule\_ht}\gets\\{rule\_ht}+10$;\quad
+$\{\,$compensate for floating-point rounding$\,\}$\par\noindent\hskip20pt
+$\\{edge}\gets\\{cur\_v}+\\{rule\_ht}$; $\\{lx}\gets0$;
+$\langle\,$Let \\{cur\_v} be the position of the first box, and set
+
+\bugonpage B263, line 25 (11/9/87)
+
+\ninepoint\noindent\hskip20pt
+$\\{cur\_v}\gets\\{edge}-10$; {\bf goto} \\{next\_p};
+
+\bugonpage B266, line 8 (8/7/87)
+
+\ninepoint\noindent\hskip10pt
+\\{dvi\_out}(\\{eop}); \\{incr}(\\{total\_pages}); $\\{cur\_s}\gets-1$;
+
+\bugonpage B266, new code between lines 31 and 32 (8/7/87)
+
+\ninepoint
+\noindent\hskip10pt{\bf while} $\\{cur\_s}>-1$ {\bf do}\par
+\noindent\hskip20pt{\bf begin if} $\\{cur\_s}>0$ {\bf then}
+ \\{dvi\_out}(\\{pop})\par
+\noindent\hskip20pt{\bf else begin} \\{dvi\_out}(\\{eop});
+ \\{incr}(\\{total\_pages})\par
+\noindent\hskip30pt{\bf end};\par
+\noindent\hskip20pt\\{decr}(\\{cur\_s});\par
+\noindent\hskip20pt{\bf end};\par
+
+\bugonpage B285, line 21 (4/28/88)
+
+\noindent\tenpoint
+is subsidiary to the \\{nucleus} field of some noad; the dot is replaced by
+`|_|' or `|^|' or `|/|' or `|\|' if $p$ is\cutpar
+
+\bugonpage B338, second-last line (8/19/87)
+
+\ninepoint\noindent\kern10pt
+$q\gets\\{link}(\\{head})$; $s\gets\\{head}$;
+
+\bugonpage B339, line 4 (8/19/87)
+
+\ninepoint\noindent\kern20pt
+$s\gets q$; $q\gets\\{link}(q)$;
+
+\bugonpage B339, new code to insert after line 10 (8/19/87)
+
+\ninepoint
+\noindent\kern10pt{\bf if} $o\ne0$ {\bf then}\par
+\noindent\kern20pt{\bf begin} $r\gets\\{link}(q)$; $\\{link}(q)\gets\\{null}$;
+ $q\gets\\{hpack}(q,\\{natural})$;\par
+\noindent\kern20pt$\\{shift\_amount}(q)\gets o$; $\\{link}(q)\gets r$;
+ $\\{link}(s)\gets q$;\par
+\noindent\kern20pt{\bf end};\par
+\noindent[These new lines also imply changes to the index that aren't
+ shown in this errata list.]
+
+\bugonpage B387, line 2 (5/24/88)
+
+\tenpoint\noindent
+is quite short. In the following code we set \\{hc}$[\\{hn}+2]$ to the
+impossible value 128, in order to\cutpar
+
+\bugonpage B387, line 8 (5/24/88)
+
+\ninepoint\noindent\kern10pt
+$\\{hc}[0]\gets127$; $\\{hc}[\\{hn}+1]\gets127$;
+ $\\{hc}[\\{hn}+2]\gets128$;\quad$\{\,$insert delimiters$\,\}$
+
+\bugonpage B390, lines 17--18 (5/24/88)
+
+\ninepoint\noindent\kern10pt
+$\langle\,$Enter as many hyphenation exceptions as are listed, until coming
+ to a right brace; then {\bf return\eightrm\kern.5em961}$\,\rangle$;
+
+\smallskip[The same change applies to lines 20--21, and
+to page~582.]
+
+\bugonpage B396, new line after line 34 (5/24/88)
+
+\ninepoint\noindent\kern10pt
+$\\{trie\_link}(\\{trie\_size})\gets0$;
+$\\{trie\_back}(0)\gets\\{trie\_size}$;\quad
+$\{\,$wrap around$\,\}$
+
+\bugonpage B396, bottom line (12/12/87)
+
+\ninepoint\noindent\hskip10pt
+$\\{trie\_link}(0)\gets0$; $\\{trie\_char}(0)\gets0$;
+ $\\{trie\_op}(0)\gets\\{min\_quarterword}$;
+
+\bugonpage B397, lines 15--17 (5/24/88)
+
+\ninepoint\noindent\kern10pt
+{\bf begin} $c\gets\\{trie\_c}[p]$;\par\noindent\kern10pt
+{\bf if} $c<\\{trie\_min}$ {\bf then} $\\{trie\_min}\gets c$;\par\noindent\kern10pt
+{\bf if} $\\{trie\_min}=0$ {\bf then} $z\gets\\{trie\_link}(\\{trie\_size})$\par
+\noindent\kern10pt
+{\bf else} $z\gets\\{trie\_link}(\\{trie\_min}-1)$;\quad
+$\{\,$get the first conceivably good hole$\,\}$
+
+\bugonpage B400, lines 3--4 (5/24/88)
+
+\ninepoint\noindent
+$\langle\,$Enter all of the patterns into a linked trie, until coming
+ to a right brace{\eightrm\kern.5em961}$\,\rangle\equiv$
+
+\smallskip[The same change applies to page B399, lines 29--30, and
+to page~582.]
+
+\bugonpage B402, line 10 (5/24/88)
+
+\ninepoint\noindent\kern10pt
+$r\gets\\{trie\_size}$;\quad
+$\{\,$finally, we will zero out the holes$\,\}$
+
+\bugonpage B406, line 9 from the bottom (1/23/89)
+
+\ninepoint\noindent\kern30pt
+$\\{shrink\_order}(r)\gets\\{normal}$; \ $\\{delete\_glue\_ref}(q)$; \
+$\\{glue\_ptr}(p)\gets r$; \ $q\gets r$;
+
+\bugonpage B417, line 10 (1/23/89)
+
+\ninepoint\noindent\kern10pt
+$q\gets\\{new\_skip\_param}(\\{top\_skip\_code})$; \quad
+$\{\,$now $\\{temp\_ptr}=\\{glue\_ptr}(q)\,\}$
+
+\bugonpage B418, line 14 (1/23/89)
+
+\ninepoint\noindent\kern30pt
+$\\{shrink\_order}(r)\gets\\{normal}$; \ $\\{delete\_glue\_ref}(q)$; \
+$\\{glue\_ptr}(p)\gets r$; \ $q\gets r$;
+
+\bugonpage B507, line 13 (12/14/88)
+
+\ninepoint\noindent\kern10pt
+{\bf if\/} \\{log\_opened} {\bf then} $\\{selector}\gets\\{selector}+2$;
+
+\bugonpage B527, line 21 (12/14/88)
+
+\ninepoint\noindent\kern10pt
+{\bf if\/} \\{log\_opened} {\bf then}
+
+\bugonpage B528, line 5 (12/14/88)
+
+\ninepoint\noindent\kern10pt
+{\bf if\/} \\{log\_opened} {\bf then}
+
+\bugonpage B547, right column (9/20/87)
+
+\eightpoint
+\leftline{\\{co\_backup}:\quad $\underline{366}$.}
+
+\bugonpage B548, right column (9/20/87)
+
+\eightpoint
+\leftline{\\{cur\_order}:\quad 366, $\underline{447}$, 448, 454, 462.}
+
+\bugonpage B548, right column (8/7/87)
+
+\eightpoint
+\leftline{\\{cur\_s}:\quad 593, $\underline{616}$, 619, 629, 640, 642.}
+
+\bugonpage B551, both columns (12/23/87)
+
+\eightpoint[Remove `372' from \\{eqtb} and put it into \\{eq\_define}.]
+
+\bugonpage B552, left column (4/28/88)
+
+\eightpoint[Insert `358' into \\{expand}.]
+
+\bugonpage B554, left column (12/23/87)
+
+\eightpoint[Remove `372' from \\{frozen\_relax}.]
+
+\bugonpage B559, new entry (12/14/88)
+
+\eightpoint\noindent
+\\{log\_opened}, 92--93, $\underline{527}$, 528, 534--535, 1265, 1333--1334.
+
+\bugonpage B559, right column (8/13/87)
+
+\eightpoint[Delete the entry for \\{low\_mem\_max}.]
+
+\bugonpage B562, left column (4/28/88)
+
+\eightpoint[Remove `358' from \\{no\_expand}.]
+
+\bugonpage B565, left column (8/7/87)
+
+\eightpoint
+\leftline{\\{pop}:\quad 584--585, $\underline{586}$, 590, 601, 608, 642.}
+
+\bugonpage B567, left column (12/23/87)
+
+\eightpoint[Insert `372' into \\{relax}.]
+
+\bugonpage B568, left column (4/28/88)
+
+\eightpoint[Move `269' from \\{save\_index} to \\{save\_level}.]
+ % volume C
+\hsize=29pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+
+\bugonpage C26, bottom line (7/18/87)
+
+\tenpoint\noindent
+What angle corresponds to the direction North-Northwest?
+
+\bugonpage C107, line 13 (10/7/87)
+
+{\bf pickup penrazor} xscaled \\{heavyline}
+ rotated (angle$(z_{32}-z_{31})+90$);
+
+\bugonpage C164, line 10 (4/27/88)
+
+\ninepoint\indent
+\quad $y_{\$c}=\\{top}\,y_{\$l}$; \ $y_{\$d}=y_{\$r}$; \
+ $x_{\$c}=x_{\$l}-\\{left\_jut}$; \ $x_{\$d}=x_{\$r}+\\{right\_jut}$;
+
+\bugonpage C175, line 23 (1/11/88)
+
+\ninepoint\noindent
+expand into a sequence of tokens. \
+(The language {\eightrm{SIMULA67}} demonstrated that it is\cutpar
+
+\bugonpage C241, line 11 (5/25/88)
+
+\ninepoint\indent
+{\bf numeric} $\\{ht}\0,\\{dp}\0$; \
+ $\\{ht}\0=\\{body\_height}\0$; \
+ $.5[\\{ht}\0,-\\{dp}\0]=\\{axis}\0$;
+
+\bugonpage C248, line 21 becomes two lines (1/24/89)
+
+\ninepoint\noindent
+which might not
+be numerically stable in the presence of rounding errors.)
+Another case, not really desirable, is $\\{left\_jut}=\\{right\_jut}=0$.
+
+\bugonpage C262, line 15 (12/23/88)
+
+\ninepoint\noindent
+|string base_name, base_version; base_name="plain"; base_version="1.7";|
+
+\bugonpage C271, line 12 (1/4/89)
+
+\ninepoint\noindent
+the user and \MF's primitive picture commands.
+First, some important program\cutpar
+
+\bugonpage C271, line 4 from the bottom (12/23/88)
+
+\ninepoint\noindent
+|def |^|cutdraw|| expr p = % caution: you may need autorounding=0|
+
+\bugonpage C272, lines 5 and 6 (12/23/88)
+
+\ninepoint\noindent
+| (cut_ scaled (1+max(pen_lft,pen_rt,pen_top,pen_bot))|\par\noindent
+| rotated theta shifted z)t_;|
+
+\bugonpage C273, lines 20 and 22 (9/26/88)
+
+\ninepoint\noindent
+| (z_+(0,pen_top))t_=round((z+(0,pen_top))t_); z_ enddef;|\par\noindent
+| (z_+(0,pen_bot))t_=round((z+(0,pen_bot))t_); z_ enddef;|
+
+\bugonpage C290, line 6 from the bottom (12/23/88)
+
+\ninepoint\noindent
+(2)~A throwaway variable,
+`\\{whatever}', nullifies an unwanted equation at the beginning\cutpar
+
+\bugonpage C331, just below the illustration (7/18/87)
+
+\ninepoint\noindent
+Such a pattern is, of course, rather unlikely to occur in a |gf| file,
+but |GFtoDVI| would\cutpar
+
+\bugonpage C337, line 11 (4/28/88)
+
+\ninepoint
+An online ``menu'' of the available test routines will be typed at your
+terminal\cutpar
+
+\bugonpage C346, entry for {\tt autorounding} (12/23/88)
+
+\eightpoint\indent\hskip20pt
+212, {\it262}, {\it264}, 271--272.
+
+\bugonpage C350, left column (7/6/88)
+
+\eightpoint\indent
+|Fatal| |base| |file| |error|, 226.
+
+\bugonpage C356, left column (1/11/88)
+
+\eightpoint
+SIMULA67 language, 175.
+
+\bugonpage C358, right column (2/15/88)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|yoffset|, 212, $\underline{220}$, 315, 324.
+ % Volume D
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage D2, line 27 (12/14/88)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{banner}\equiv\hbox{\tt\char'23}$%
+{\tt This\]is\]METAFONT,\]Version\]1.7\char'23}\quad
+$\{\,$printed when \MF\ starts$\,\}$
+
+\bugonpage D36, lines 3--5 (11/6/88)
+
+\tenpoint\noindent[Delete this paragraph; it is being moved to page D349.]
+
+\bugonpage D36, line 7 (12/14/88)
+
+\ninepoint\noindent\kern10pt
+{\bf begin if\/} \\{log\_opened} {\bf then} $\\{selector}\gets\\{term\_and\_log}$
+
+\bugonpage D36, line 16 (12/14/88)
+
+\ninepoint\noindent\kern50pt
+{\bf if\/} \\{log\_opened} {\bf then} \\{error};
+
+\bugonpage D66, lines 34--35 (7/9/88)
+
+\ninepoint\noindent\kern10pt
+{\bf begin if\/} $\\{hi\_mem\_min}-\\{lo\_mem\_max}\ge1998$
+ {\bf then} $t\gets\\{lo\_mem\_max}+1000$\par\noindent\kern10pt
+{\bf else} $t\gets\\{lo\_mem\_max}+1+(\\{hi\_mem\_min}-\\{lo\_mem\_max})
+\,\mathbin{\bf div}\,2$;\quad\kern-4pt
+$\{\,\\{lo\_mem\_max}+2\le t<\\{hi\_mem\_min}\,\}$
+
+\bugonpage D347, new line of code after line 5 (12/14/88)
+
+\ninepoint\noindent
+\\{log\_opened}: \\{boolean};\quad$\{\,$has the transcript file been opened?$\,\}$
+
+\bugonpage D347, line 11 (12/14/88)
+
+\ninepoint\noindent\kern10pt
+$\\{job\_name}\gets0$; \
+$\\{log\_opened}\gets\\{false}$;
+
+\bugonpage D348, line 4 from the bottom (12/14/88)
+
+\ninepoint\noindent\kern10pt
+$\\{log\_name}\gets\\{a\_make\_name\_string}(\\{log\_file})$; \
+$\\{selector}\gets\\{log\_only}$; \
+$\\{log\_opened}\gets\\{true}$;
+
+\bugonpage D349, lines 6 and 7 (12/14/88)
+
+\tenpoint\noindent
+print error messages or even to \\{show\_context}.
+The \\{prompt\_file\_name} routine can result in a \\{fatal\_error},
+but the \\{error}
+routine will not be invoked because \\{log\_opened} will be false.
+\par\noindent\hskip10pt
+The normal idea of \\{batch\_mode} is that nothing at all should be written
+on the terminal. However, in the unusual case that
+no log file could be opened, we make an exception and allow
+an explanatory message to be seen.
+
+\bugonpage D349, lines 11--15 reduce to a single line (12/14/88)
+
+\ninepoint\noindent\hskip10pt
+{\bf begin} $\\{selector}\gets\\{term\_only}$;
+
+\bugonpage D420, bottom line (5/25/88)
+
+\ninepoint\noindent\kern30pt
+{\bf if\/} \\{txx} {\bf mod} $\\{unity}=0$ {\bf then}
+
+\bugonpage D441, delete line 2 and change line 12 as follows (5/25/88)
+
+\ninepoint\noindent
+\\{done}: {\bf if} $\\{eq\_type}(x)\ne\\{tag\_token}$ {\bf then}
+ $\\{clear\_symbol}(x,\\{false})$;\par\noindent\kern10pt
+{\bf if} $\\{equiv}(x)=\\{null}$ {\bf then} $\\{new\_root}(x)$;
+\par\noindent\kern10pt
+$\\{scan\_declared\_variable}\gets h$;
+
+\bugonpage D444, line 8 from the bottom (12/14/88)
+
+\ninepoint\noindent\kern10pt
+{\bf if\/} \\{log\_opened} {\bf then} $\\{selector}\gets\\{selector}+2$;
+
+\bugonpage D510, line 14 (12/14/88)
+
+\ninepoint\noindent\kern10pt
+{\bf if\/} \\{log\_opened} {\bf then}
+
+\bugonpage D511, line 11 (12/14/88)
+
+\ninepoint\noindent\kern10pt
+{\bf if\/} \\{log\_opened} {\bf then}
+
+\bugonpage D530, new entry (12/14/88)
+
+\eightpoint\noindent
+\\{log\_opened}, 87--88, $\underline{782}$, 783, 788--789, 1023, 1205, 1208.
+
+\bugonpage D545, left column (10/31/87)
+
+\eightpoint
+\leftline{{\bf zscaled} primitive:\quad $\underline{893}$.}
+\leftline{Zabala Salelles, Ignacio Andres:\quad 812.}
+
+ % volume E
+\hsize=29pc
+\def\dashto{\mathrel{\hbox{-\kern-.05em}\mkern3.9mu\hbox{-\kern-.05em}}}
+
+\bugonpage E32, second-last line (9/20/87)
+
+\tenpoint\noindent
+after which comes `\\{math\_axis}\0; {\bf generate} |mathsy|' (which we
+won't bother to\cutpar
+
+\bugonpage E111, line 29 (10/16/88)
+
+\ninepoint\noindent
+$\\{lft}\,x_{11}={\rm hround}\,u$; \
+ $x_{1l}-x_{11}=x_{2l}-x_{12}=x_{22}-x_{2r}={\rm hround}\,1.6\\{cap\_jut}$;
+
+\bugonpage E285, bottom line (12/1/87)
+
+\rightline{\eightssi Due to Technical Developments\/\enspace\eightss(1968)}
+
+\bugonpage E333, lines 9--11 (1/9/89)
+
+\ninepoint\noindent
+$\\{lft}\,x_{1l}={\rm hround}(2.5u-.5\\{mfudged.stem})$; \
+ $x_{1l}=x_{1'l}=x_{2l}=x_{2'l}$;\par\noindent
+$\\{lft}\,x_{3l}={\rm hround}(.5w-.5\\{mfudged.stem})$; \
+ $x_5-x_3=x_3-x_1$;\par
+\line{{\bf if\/} not \\{monospace}:
+ $r:={\rm hround}(x_5+x_1)+r-w$; {\bf fi}\hfill
+ \% change width for better fit}
+
+\bugonpage E353, lines 38--39 (8/12/87)
+
+\ninepoint
+\leftline{\kern10pt{\bf else}: {\bf fill} \\{diag\_end}$(6r,5r,1,1,5l,6l)
+ \dashto.9[z_{5l},z_{6l}]$}
+\line{\kern30pt$.\,.\,\{z_5-z_6\}\,.1[z_{5r},z_{6r}]\dashto\rm cycle$;\hfil
+ \% middle stem}
+
+\bugonpage E387, line 13 (8/12/87)
+
+\ninepoint
+\line{\kern10pt{\bf pickup} \\{tiny}.\\{nib}; \ \\{bulb}$(3,4,5)$;\hfil\% bulb}
+
+\bugonpage E413, lines 37--38 (8/12/87)
+
+\ninepoint
+\leftline{\kern10pt{\bf else}: {\bf fill} \\{diag\_end}$(6r,5r,1,1,5l,6l)
+ \dashto.9[z_{5l},z_{6l}]$}
+\line{\kern30pt$.\,.\,\{z_5-z_6\}\,.1[z_{5r},z_{6r}]\dashto\rm cycle$;\hfil
+ \% middle stem}
+
+\bugonpage E459, line 24 (8/7/87)
+
+\ninepoint\noindent[Delete the `$=$' sign between `\\{lft}' and `$x_5$'.]
+
+\bugonpage E471, line 5 (12/11/88)
+
+\ninepoint\noindent
+$x_2=\\{good}.x\,.5w$; \ $\\{center\_on}(x_2)$;
+
+\bugonpage E471, insert two lines below the rule at bottom of page (12/11/88)
+
+\ninepoint
+\line{{\bf def\/}
+\\{center\_on}({\bf expr} $x) =\null$
+{\bf if\/} not \\{monospace}:\hfill \% change width for symmetric fit}
+\leftline{\kern10pt
+ $r:=r+2x-w$; \ $w:=2x$; \ {\bf fi} {\bf enddef};}
+
+\bugonpage E477, line 20 (12/11/87)
+
+\ninepoint\noindent
+$x_4=x_8=\\{good}.x\,.5w$; \ $\\{center\_on}(x_4)$; \
+$x_2=w-x_6=\\{good}.x(x_4+a)$;
+
+\bugonpage E483, third line of elementary division operator (12/11/88)
+
+\ninepoint\noindent
+$x_3-.5\\{dot\_size}={\rm hround}(.5w-.5\\{dot\_size})$; \
+$\\{center\_on}(x_3)$;
+
+\bugonpage E485, line 4 (8/7/87)
+
+\ninepoint\noindent[Delete the `$=$' sign between `\\{lft}' and `$x_5$'.]
+
+\bugonpage E487, line 17 (8/4/88)
+
+\ninepoint\line{%
+{\bf fill} \\{fullcircle} scaled$\,(\\{bold}+3.8\\{dw}+\\{eps})\,$%
+ shifted$\,(.5[z_4,z_8])$;\hfill\% dot}
+\smallskip\noindent[Also remove page 487 from the index entry for
+\\{dot\_size}, and add it to the entries for \\{bold} and \\{dw}.]
+
+\bugonpage E515, lines 5 and 12 (12/11/88)
+
+\ninepoint\noindent
+$.5[x_1,x_2]=x_3=\\{good}.x\,.5w$; \
+$\\{center\_on}(x_3)$; \ $\\{lft}\,x_1={\rm hround}(.5w-u*{\rm sqrt}48)$;
+
+\bugonpage E515, line 21 (1/23/89)
+
+\ninepoint\noindent
+{\bf labels}$(5,6)$; \ \\{zero\_width}; \ {\bf endchar\/};
+\smallskip\noindent[Also put labels `{\tt5}' and `{\tt6}' on the
+upper right figure, page E514.]
+
+\bugonpage E521, lines 4 and 14 (12/12/88)
+
+\ninepoint\noindent
+$x_1=x_2=\\{good}.x\,.5w$; \
+$\\{center\_on}(x_1)$; \ $\\{lft}\,x_3={\rm hround}\,u$; \ $x_4=w-x_3$;
+
+\bugonpage E537, line 6 (12/11/88)
+
+\ninepoint\noindent
+$x_1=x_2=x_3=x_4$; \
+$x_1-.5\\{stem}={\rm hround}(.5w-.5\\{stem})$; \
+$\\{center\_on}(x_1)$;
+
+\bugonpage E537, line 19 (12/11/88)
+
+\ninepoint\noindent
+$x_1=x_2=x_3$; \
+$x_1-.5\\{stem}={\rm hround}(.5w-.5\\{stem})$; \
+$\\{center\_on}(x_1)$;
+
+\bugonpage E539, line 4 (12/11/88)
+
+\ninepoint\noindent
+$x_1=x_4=x_{30}=x_{33}=\\{good}.x\,.5w$; \ $\\{center\_on}(x_1)$;
+
+\bugonpage E539, line 21 (12/11/88)
+
+\ninepoint\noindent
+$x_1=x_4=\\{good}.x\,.5w$; \ $\\{center\_on}(x_1)$;
+
+\bugonpage E541, line 4 (12/11/88)
+
+\ninepoint\noindent
+$x_1=x_5=\\{good}.x\,.5w$; \ $\\{center\_on}(x_1)$;
+
+\bugonpage E541, line 17 (12/11/88)
+
+\ninepoint\noindent
+$x_1=x_{10}=\\{good}.x\,.5w$; \ $\\{center\_on}(x_1)$;
+
+\bugonpage E550, new line after line 23 (8/15/87)
+
+\ninepoint\noindent\kern10pt
+{\bf forsuffixes} $\hbox{\$}=\\{notch\_cut},\\{cap\_notch\_cut}$:
+ {\bf if\/} $\hbox{\$}<3$: $\hbox{\$}:=3$; {\bf fi endfor}
+\smallskip\noindent
+[To make room for this, combine lines 38 and 39 into a single line.]
+
+\bugonpage E550, line 29 (7/9/88)
+
+\ninepoint\noindent\hskip10pt
+{\bf \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}%
+define\_whole\_vertical\_blacker\_pixels}$(\\{vair},\\{bar},\\{slab},
+ \\{cap\_bar},\\{cap\_band})$;
+
+\bugonpage E572, new entry at bottom (12/11/88)
+
+\eightpoint\noindent
+\\{center\_on}, $\underline{471}$, 477, 483, 515, 521, 537--541.
+ \bye
+ Now here are some that I will make soon!
+
diff --git a/systems/knuth/dist/errata/errata.nine b/systems/knuth/dist/errata/errata.nine
new file mode 100644
index 0000000000..fe9a20adad
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.nine
@@ -0,0 +1,235 @@
+% Bugs (sigh) in Computers \& Typesetting
+
+\input manmac
+\font\sltt=cmsltt10
+\font\niness=cmss9
+\font\ninessi=cmssi9
+\proofmodefalse
+\raggedbottom
+\output{\hsize=29pc \onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\today{\number\day\
+ \ifcase\month\or
+ Jan\or Feb\or Mar\or Apr\or May\or Jun\or
+ Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi
+ \ \number\year}
+
+\def\cutpar{{\parfillskip=0pt\par}}
+
+\def\rhead{Bugs in {\tensl Computers \& Typesetting A, 1996}}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\buginvol#1(#2) \par{\bigbreak\penalty-1000\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Volume #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\slMF{{\manual 89:;}\-{\manual <=>:}} % slant the logo
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\newcount\nn
+\newdimen\nsize \newdimen\msize \newdimen\ninept \ninept=9pt
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+
+\tenpoint
+\noindent This is a list of all corrections made to {\sl Computers \&
+Typesetting}, Volume~A (also known as {\sl The \TeX book}),
+between 1992 and the publication of the final printed
+version of that book in September 1996.
+\looseness=-1
+
+ % volume A
+
+\bugonpage A31, line 8 (3/6/95)
+
+\tenpoint\noindent
+\TeX\ begins its error messages with `|!|', and it shows what it was
+reading at the\cutpar
+
+\bugonpage A46, line 8 (1/22/95)
+
+\ninepoint\noindent
+out for
+the occasional times when the adjacent characters |aa|, |ae|, and |o/|
+should not be\cutpar
+
+\bugonpage A49, top (9/1/96)
+
+\eightpoint\noindent
+[The copy on pages 45 and 46 was significantly shortened
+in the seventeenth printing (October 1989 --- see {\tt errata.five}),
+and exercise 8.7 moved back to page 48 as a result. But the printer
+was not asked to change page 49; hence exercise 8.7 appeared twice,
+on pages 48 {\it and\/}~49, during a six-year period.]
+
+\bugonpage A282, lines 11 and 12 (4/18/96)
+
+\ninepoint\noindent
+unadorned \<box> command, except that the new box
+being appended to the vertical list is also shifted left or right by the
+specified amount.
+
+\bugonpage A285, lines 15--17 from the bottom (4/18/96)
+
+\ninepoint
+\textindent{$\bull$}|\raise|\<dimen>\<box>, |\lower|\<dimen>\<box>.\enskip
+This acts just like an unadorned \<box> command, except that the new box
+being appended to the horizontal list is also shifted up or down by the
+specified amount.
+
+\bugonpage A290, lines 4--6 from the bottom (4/18/96)
+
+\ninepoint
+\textindent{$\bull$}|\raise|\<dimen>\<box>, |\lower|\<dimen>\<box>.\enskip
+This acts just like an unadorned \<box> command, except that the new box
+being put into the nucleus is also shifted up or down by the specified amount.
+
+\bugonpage A331, bottom two lines (6/25/93)
+
+\ninepoint\noindent
+if you know that the
+enclosing box is sufficiently small; and |\leaders\vrule\vfill| works fine in
+vertical mode.
+
+\bugonpage A354, lines 19--22 (3/5/95)
+
+\ninepoint\noindent
+|\def\sett@b{\ifx\next\+\def\nxt{\afterassignment\s@tt@b\let\nxt}%|\par
+\noindent
+| \else\let\nxt=\s@tcols\fi|\par\noindent
+| \let\next=\relax \nxt} % turn off \outerness|\par\noindent
+|\def\s@tt@b{\let\nxt=\relax \us@false\m@ketabbox}|
+
+\bugonpage A356, lines 13--20 from the bottom (3/5/95)
+
+\ninepoint
+{\parindent=0pt
+|\def\oalign#1{\leavevmode\vtop{\baselineskip0pt \lineskip.25ex|\par
+| \ialign{##\crcr#1\crcr}}} \def\o@lign{\lineskiplimit=0pt \oalign}|\par
+|\def\ooalign{\lineskiplimit=-\maxdimen \oalign} % chars over each other|\par
+|\def\sh@ft#1{\dimen0=.00#1ex \multiply\dimen0 by\fontdimen1\font|\par
+| \kern-.0156\dimen0} % compensate for slant in lowered accents|\par
+|\def|^|\d||#1{{\o@lign{\relax#1\crcr\hidewidth\sh@ft{10}.\hidewidth}}}|\par
+|\def|^|\b||#1{{\o@lign{\relax#1\crcr\hidewidth\sh@ft{29}%|\par
+| \vbox to.2ex{\hbox{\char'26}\vss}\hidewidth}}}|\par
+}
+
+\bugonpage A357, lines 7--12 (8/1/95)
+
+\ninepoint\noindent
+|\def|^|\rightarrowfill||{$\m@th \smash- \mkern-7mu|\par\noindent
+| \cleaders\hbox{$\mkern-2mu \smash- \mkern-2mu$}\hfill|\par\noindent
+| \mkern-7mu \mathord\rightarrow$}|\par\noindent
+|\def|^|\leftarrowfill|%
+ |{$\m@th \mathord\leftarrow \mkern-7mu|\par\noindent
+| \cleaders\hbox{$\mkern-2mu \smash- \mkern-2mu$}\hfill|\par\noindent
+| \mkern-7mu \smash-$}|
+
+\bugonpage A357, lines 16--20 (6/25/93)
+
+\ninepoint
+{\parindent=0pt
+| \setbox0=\hbox{$\braceld$}%|\par
+| \bracelu\leaders\vrule height\ht0 depth0pt\hfill\bracerd|\par
+| \braceld\leaders\vrule height\ht0 depth0pt\hfill\braceru$}|\par
+|\def|^|\downbracefill||{$\m@th|\par
+| \setbox0=\hbox{$\braceld$}%|\par
+| \braceld\leaders\vrule height\ht0 depth0pt\hfill\braceru|\par
+| \bracelu\leaders\vrule height\ht0 depth0pt\hfill\bracerd$}|\par
+\smallskip[Also delete lines 21 and 22, as the usage is no longer restricted.]
+}
+
+\bugonpage A359, line 25 (3/5/95)
+
+\ninepoint\noindent
+|\def|^|\skew||#1#2#3{{\muskip0=#1mu \mkern.5\muskip0|\par\noindent
+| #2{\mkern-.5\muskip0{#3}\mkern.5\muskip0}\mkern-.5\muskip0}{}}|
+
+\bugonpage A360, line 5 from the bottom (3/5/95)
+
+\ninepoint\noindent
+|\def\@vereq#1#2{\lower.5pt\vbox{\lineskiplimit\maxdimen \lineskip-.5pt|
+
+\bugonpage A361, lines 19 and 20 (3/5/95)
+
+\ninepoint\noindent
+|\def|^|\bmod||{\nonscript\mskip-\medmuskip \mkern5mu|\par\noindent
+| |^|\mathbin||{\rm mod} \penalty900 \mkern5mu \nonscript\mskip-\medmuskip}|
+
+\bugonpage A362, lines 14--18 (3/5/95)
+
+\ninepoint\noindent
+| |^|\everycr||{\noalign{\ifdt@p \global\dt@pfalse|%
+ | \ifdim\prevdepth>-1000pt|\par\noindent
+| \vskip-\lineskiplimit \vskip\normallineskiplimit \fi|\par\noindent
+| \else \penalty|^|\interdisplaylinepenalty|| \fi}}}|\par\noindent
+|\def\@lign{\tabskip=0pt\everycr={}} % restore inside \displ@y|\par\noindent
+|\def|^|\displaylines||#1{\displ@y \tabskip=0pt|
+
+\bugonpage A363, lines 8--9 from the bottom (12/8/89)
+
+{\ninepoint\parindent=0pt
+| \if@mid \dimen@=\ht0 \advance\dimen@ by\dp\z@ |%
+ |\advance\dimen@ by12\p@|\parbreak%
+| \advance\dimen@ by\pagetotal \advance\dimen@ by-\pageshrink|
+\par}
+
+\bugonpage A364, line 5 from the bottom (4/11/96)
+
+\ninepoint\noindent
+|\def\fmtname{plain}|\par\noindent
+|\def\fmtversion{3.141592} % identifies the current format|
+
+\bugonpage A374, line 3 (3/7/95)
+
+\ninepoint\indent
+|\begingroup\aftergroup\def\aftergroup\asts\aftergroup{|
+
+\bugonpage A410, line 23 (4/18/96)
+
+\ninepoint\noindent
+| Tema con variazione \(su un tema differente)|
+\smallskip\eightpoint\noindent
+(This implies a corresponding correction to page 411.)
+
+\bugonpage A451, line $16$ (8/8/93)
+
+\ninepoint\noindent
+But when plain \TeX\ is tried on the name of a famous ^{Welsh} village,
+
+\bugonpage A462, right column (3/5/95)
+
+\def\cong{\mathrel{\mathpalette\OvOreq\sim}} % congruence sign, index version
+\def\OvOreq#1#2{\lower.5pt\vbox{\baselineskip0pt\lineskip-.0pt
+ \ialign{$#1\hfil##\hfil$\crcr#2\crcr=\crcr}}}
+\eightpoint
+|\cong| ( $\cong$ ), 151, $\underline{360}$, 436.
+
+\bugonpage A463, right column (6/25/93)
+
+\eightpoint
+direct sum, {\sl see\/} |\oplus|.
+
+\bugonpage A464, left column (6/25/93)
+
+\eightpoint
+|\downbracefill| (\hbox to 4em{\downbracefill}), 225--226, $\underline{357}$.
+
+\bugonpage A483, lines 15--21 (4/29/97)
+
+\tenpoint
+\begintt
+email: {\tt TUG@tug.org}
+internet: {\tt http://www.tug.org/}
+}
+\endtt
+Don't delay, subscribe today! That address again is
+
+\smallskip
+{\obeylines
+\TeX\ Users Group
+email: {\tt TUG\char`\@ tug.org}
+internet: {\tt http://www.tug.org/}
+}
+
+\bye
diff --git a/systems/knuth/dist/errata/errata.one b/systems/knuth/dist/errata/errata.one
new file mode 100644
index 0000000000..471971958c
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.one
@@ -0,0 +1,751 @@
+% Bugs (sigh) in The TeXbook
+
+\input manmac
+\proofmodefalse
+\raggedbottom
+\output{\onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\rhead{Bugs in {\sl The \TeX book}, first printing}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule\line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule\nobreak\medskip}
+
+\noindent This is a list of all corrections made to {\sl The \TeX book\/}
+between the first and second printings. If your copy says `{\sl\kern-1pt Second
+printing (October 1984)\/}' on the copyright page, you've already got
+all of these things corrected. Otherwise, you're a lucky owner of the
+rare first edition; read on.
+
+\bugonpage 29, lines 31--32 (8/25/84)
+
+The underfull box that \TeX\ produces in the 1.5-inch case is really bad;
+with such narrow limits, an occasional wide space is unavoidable. But try
+
+\bugonpage 54, lines 5--6 (4/20/84)
+
+{\parfillskip=0pt
+\ddanger Appendix B shows that plain \TeX\ handles most of the accents
+by using \TeX's ^|\accent| primitive. For example, |\'#1| is equivalent
+to |{\accent19 #1}|, where\par}
+
+\bugonpage 63, seven lines below the first illustration (2/27/84)
+
+\line{points, a width of 5.5555 points, and a depth of zero;
+the letter `g' has a height}
+
+\bugonpage 72, line 35 (2/28/84)
+
+\ninepoint\noindent
+from |0pt|, but |0.00001filll| is infinitely greater than |16383.99999fill|.
+
+\bugonpage 79, line 12 (2/28/84)
+
+\ninepoint\indent
+|\hbox(6.25+1.94444)x312.0, glue set 0.5783, shifted 36.0 []|
+
+\bugonpage 98, line 24 (4/13/84)
+
+\ninepoint
+\line{and |\finalhyphendemerits=5000|. Demerits are in units of
+ ``badness squared,'' so the}
+
+\bugonpage 101, lines 29--30 (3/13/84)
+
+{\parfillskip=0pt
+\danger It's possible to control the length of lines in a much more general
+way, if simple changes to |\leftskip| and |\rightskip| aren't
+flexible enough for your\par}
+
+\bugonpage 113, bottom two lines (3/13/84)
+
+{\parfillskip=0pt
+\ddanger Notice that the first ``|%| line'' of our example says |t=10.0|;
+this is a consequence of another parameter, called ^|\topskip|. Glue
+disappears at a page break, but\par}
+
+\bugonpage 124, eighth-last line (8/25/84)
+
+\ninepoint
+{\parfillskip=0pt\noindent
+discarded, |\box100| will
+be void after the |\vsplit|. And if\/ |\box100| was void before the\par}
+
+\bugonpage 131, display in exercise 16.8 (3/16/84)
+
+\indent
+|If$ x = y$, then $x$ is equal to $y.$|
+
+\bugonpage 170, table in middle of the page (2/12/84)
+
+\ninepoint
+$$\baselineskip0pt\lineskip0pt
+\halign to\hsize
+ {\strut\hbox to\parindent{\it#\hfil}& % for the legend "Left atom"
+ #\hfil\quad& % for the row labels
+ #\hfil\tabskip 0pt plus 10pt& % for the rule at the left
+ \hbox to 25pt{\tt\hss#\hss}& % for column 1
+ \hbox to 25pt{\tt\hss#\hss}& % for column 2
+ \hbox to 25pt{\tt\hss#\hss}& % for column 3
+ \hbox to 25pt{\tt\hss#\hss}& % for column 4
+ \hbox to 25pt{\tt\hss#\hss}& % for column 5
+ \hbox to 25pt{\tt\hss#\hss}& % for column 6
+ \hbox to 25pt{\tt\hss#\hss}& % for column 7
+ \hbox to 25pt{\tt\hss#\hss}& % for column 8
+ #\hfil\tabskip0pt\cr % for the rule at the right
+\noalign{\vskip-6pt} % it just happens that there's extra white space
+&&&&\multispan7\hss\it Right atom\hss\cr
+\noalign{\vskip3pt}
+&&&\rm Ord&\rm Op&\rm Bin&\rm Rel&\rm Open&\rm Close&\rm Punct&\rm Inner\cr
+\noalign{\vskip2pt}
+\omit&&\multispan{10}\leaders\hrule\hfil\cr
+\omit\vbox to 2pt{}&&\vrule&&&&&&&&&\vrule\cr
+&Ord&\vrule&0&1&(2)&(3)&0&0&0&(1)&\vrule\cr
+&Op&\vrule&1&1&*&(3)&0&0&0&(1)&\vrule\cr
+&Bin&\vrule&(2)&(2)&*&*&(2)&*&*&(2)&\vrule\cr
+Left&Rel&\vrule&(3)&(3)&*&0&(3)&0&0&(3)&\vrule\cr
+atom&Open&\vrule&0&0&*&0&0&0&0&0&\vrule\cr
+&Close&\vrule&0&1&(2)&(3)&0&0&0&(1)&\vrule\cr
+&Punct&\vrule&(1)&(1)&*&(1)&(1)&(1)&(1)&(1)&\vrule\cr
+&Inner&\vrule&(1)&1&(2)&(3)&(1)&0&(1)&(1)&\vrule\cr
+\omit\vbox to 2pt{}&&\vrule&&&&&&&&&\vrule\cr
+\omit&&\multispan{10}\leaders\hrule\hfil\cr}$$
+
+\bugonpage 173, line 11 (1/2/84)
+
+\indent|Clearly $a_i<b_i$ for~$i=1, 2, \ldots, n$.|
+
+\bugonpage 176, bottom two lines (7/20/84)
+
+\def\chapno{ 18} \exno=23 % for exercise 18.24!
+\dangerexercise Typeset the display \ \lower12pt\null
+$\tenpoint\smash{\displaystyle
+\left\lgroup\matrix{a&b&c\cr d&e&f\cr}\right\rgroup
+ \left\lgroup\matrix{u&x\cr v&y\cr w&z\cr}\right\rgroup
+}$, \
+using ^|\lgroup| and ^|\rgroup|.
+
+\bugonpage 189, line 18 (2/13/84)
+
+\ninepoint\noindent
+{\parfillskip=0pt
+when there is an overlap.] \ If $e=0$ and if there is an |\leqno|,
+the equation number is\par}
+
+\bugonpage 204, line 31 (2/13/84)
+
+\ninepoint\noindent
+of\/ |\a| is delimited by a left brace.
+
+\bugonpage 212, line 23 (7/8/84)
+
+\ninepoint\noindent
+it equals~2.) \
+Similarly, ^|\tracingmacros||=2| will trace |\output|, |\everypar|, etc.
+
+\bugonpage 216, first five lines (8/25/84)
+
+\ddanger Expanded definitions that are made with |\edef| or |\xdef| continue
+to expand tokens until only unexpandable tokens remain, except that
+token lists produced by `^|\the|' are not expanded further. Furthermore
+a token following `^|\noexpand|' will not be expanded, since its ability
+to expand has been nullified. These two operations can be used to control
+^^{expansion, avoiding} what gets expanded and what doesn't.
+
+\bugonpage 219, simplification of line 18 (2/15/84)
+
+\ninepoint\indent
+| \advance\count0 by\count2 \hexdigit}}|
+
+\bugonpage 223, lines 3--4 (3/13/84)
+
+{\parfillskip=0pt
+\ddanger Chapters 24 to 26 present summaries of all \TeX's operations
+in all modes, and when those summaries mention a `\<box>' they mean one
+of the seven\par}
+
+\bugonpage 242, line 29 (1/2/84)
+
+\ninepoint\noindent
+{\parfillskip=0pt
+a relation, the solution is to insert `|{}|' ^^{lbrace rbrace}
+at the beginning of the right-hand formula; \TeX
+\par}
+
+\bugonpage 245, line 24 (2/15/84)
+
+\ninepoint\noindent
+of a box that spans columns $i$ through~$j$,
+hence the glue in such a box might shrink.
+
+\bugonpage 248, the fourth dangerous bend (2/15/84)
+
+{\parfillskip=0pt
+\ddanger You have to be careful with the use of |&| and ^|\span| and ^|\cr|,
+because these tokens are intercepted by \TeX's scanner even when it is
+not expanding macros.\par}
+
+\bugonpage 249, lines 20--26 (2/15/84)
+
+\ninepoint\noindent
+line (see Chapter~9).
+If you don't want a~|\cr| at the end of a certain line,
+just type `|%|' and the corresponding |\cr|
+will be ``commented out.'' ^^{percent} \ (This special mode doesn't
+work with ^|\+| lines, since |\+| is a macro whose argument is delimited
+by the token `|\cr|', not simply by a token that has the same meaning
+as~|\cr|. ^^{delimited arguments} But you can redefine |\+| to overcome
+this hurdle, if you want to. For example, define a macro |\alternateplus|
+that is just like |\+| except that its argument is delimited by the active
+character |^^M|; then include the command `|\let\+=\alternateplus|' as
+part of\/ |\obeylines|.)
+
+\bugonpage 253, lines 28--32 (4/25/84)
+
+\ninepoint\noindent
+vertical list at what it thinks is the best place, and at such times
+it enters internal vertical mode and begins to read the commands in the
+current |\output| routine. When the output routine begins, ^|\box255|
+contains the page that \TeX\ has completed; the output routine is supposed to
+do something with this vbox. When the output routine ends, the list of
+items that it has constructed in internal vertical mode is placed just%
+{\parfillskip=0pt\par}
+
+\bugonpage 254, lines 1--13 (3/13/84)
+
+\ddanger \TeX's primitive command |\shipout|\<box> is what actually
+causes output. It sends the contents of the box to the |dvi| file,
+which is \TeX's main output file; after \TeX\ has finished,
+the ^|dvi| file will contain a compact device-independent encoding of
+instructions that specify exactly what should be printed. When a
+box is shipped out, \TeX\ displays the values of\/ |\count0| through
+|\count9| on your terminal, ^^|\count0| as explained in Chapter~15; these
+ten counters are also recorded in the |dvi| file, where they can be used
+to identify the page. All of the ^|\openout|, ^|\closeout|, and ^|\write|
+commands that appear inside of the \<box> are performed in their natural
+order as that box is being shipped out. Since a |\write| command
+expands macros, as explained in Chapter~21, \TeX's scanning mechanism
+might detect syntax errors while a |\shipout| is in progress. If
+^|\tracingoutput| is nonzero at the time of a |\shipout|, the contents
+of the \<box> being shipped are written into your log file in symbolic
+form. You can say |\shipout| anywhere, not only in an output routine.
+
+\bugonpage 255, line 33 (4/25/84)
+
+\ninepoint\indent
+|\nointerlineskip|
+
+
+\bugonpage 256, starting with line $-17$ (11/1/83)
+
+\ninepoint
+\textindent{6)} Finally, the ^|\dosupereject| macro is designed to clear
+out any insertions that have been held over, whether they are illustrations
+or footnotes or both: ^^|\insertpenalties| ^^|\supereject|
+\begintt
+\ifnum\insertpenalties>0
+ \line{} \kern-\topskip \nobreak
+ \vfill\supereject\fi
+\endtt
+The mysterious negative ^|\kern| here cancels out the natural space of the
+^|\topskip| glue that goes above the empty |\line|; that empty line box
+prevents the ^|\vfill| from disappearing into a page break. The vertical
+list that results from |\dosupereject| is placed on \TeX's list of things
+to put out next, just after the straggling insertions have been
+reconsidered as explained in Chapter~15. Hence another super-eject will
+occur, and the process will continue until no insertions remain.
+
+\bugonpage 262, line 14 (2/12/84)
+
+\ninepoint\indent
+|\def\endindex{\mark{}\break\endgroup}|
+
+\bugonpage 262, lines 34 and 35 (2/12/84)
+
+\ninepoint\noindent
+if\/ |\next| is `|\endindex|',
+the next commands executed will be `|\vfill|\allowbreak
+|\mark{}|\allowbreak|\break|\allowbreak|\endgroup|';
+otherwise the line will be treated as a main entry.
+
+\bugonpage 269, line 23 becomes two lines (8/25/84)
+
+\ninepoint\noindent
+tokens like |+|$_{12}$;
+(3)~keywords like \[pt]; (4)~control sequence names like |\dimen|;
+or (5)~the special symbols |{|, |}|, |$|.
+
+\bugonpage 274, line 24 (2/15/84)
+
+\ninepoint\indent
+|\lineskip|\quad(interline glue if\/ |\baselineskip| isn't feasible)
+
+\bugonpage 289, slight clarification on lines 39--41 (3/10/84)
+
+\ninepoint
+A \<math character> defines a 15-bit number either by specifying it
+directly with ^|\mathchar| or in a previous ^|\mathchardef|, or by
+specifying a 27-bit |\delimiter| value; in the latter case, the least
+significant 12~bits are discarded.
+
+
+\bugonpage 307, a slightly more explicit answer (11/3/83)
+
+\ninepoint\noindent
+\hbox to\parindent{\bf\hss6.3.\enspace}%
+It represents the heavy bar that shows up in
+your output. \ (This bar wouldn't be present if\/ ^|\overfullrule| had been
+set to |0pt|, nor is it present in an underfull box.)
+
+\bugonpage 313, first four lines (3/13/84)
+
+{\ninepoint\parfillskip=0pt
+\ansno12.17:
+ You get `A' at the extreme left and `puzzle.\null' at the extreme right,
+because the space between words has the only stretchability that is finite;
+the infinite stretchability cancels out. \ (In this case, \TeX's rule
+about ^{infinite glue} differs from what you would get in the limit if the
+value of $1\,{\rm fil}$ were finite but getting larger and larger.
+The true\par}
+
+\bugonpage 315, first three lines (3/13/84)
+
+\ninepoint
+\ansno14.14:
+ Just say |\parfillskip|\stretch|=|\stretch|\parindent|. Of course,
+\TeX\ will not be able to find appropriate line breaks unless each
+paragraph is sufficiently long or sufficiently lucky; but with an
+appropriate text, your output will be immaculately
+symmetrical.{\parfillskip=\parindent\par}
+
+\bugonpage 324, line 16 (2/15/84)
+
+\ninepoint\noindent
+\hbox to\parindent{\bf\hss18.41.\enspace}%
+|$$\{\underbrace{\overbrace{\mathstrut a,\ldots,a}|
+
+\bugonpage 324, first line of answer 18.44 (4/11/84)
+
+\ninepoint
+\ansno18.44:
+ |$$\mathop{{\sum}'}_{x\in A}f(x)\mathrel{\mathop=^{\rm def}}|
+
+\bugonpage 333, beginning of the final paragraph (12/19/83)
+
+\ninepoint
+{\sl Note:\/} The stated preamble solves the problem and demonstrates
+that \TeX's line-breaking capability can be used within tables. But this
+particular table is not really a good example of the use of\/ |\halign|,
+because \TeX\ could typeset it directly, using ^|\everypar| in an
+appropriate manner to set up the hanging indentation, and using |\par|
+instead of\/ |\cr|. For example, one could say
+
+\bugonpage 341, the bottom line was left out! (2/9/84)
+
+\line{Footline\quad\dotfill\quad Page 1009}
+
+\bugonpage 345, top three lines (1/26/84)
+
+\ninepoint{\noindent\parfillskip=0pt
+A mathcode is relevant only when the corresponding category code is
+11 or~12; therefore many of these codes will rarely be looked at. For
+example, the math code for |^^M| specifies the character |\oplus|,
+but it's hard to imagine a user who would want |^^M|\par}
+
+\bugonpage 345, line 31 (2/29/84)
+
+\ninepoint\noindent
+|\delcode`\<="26830A \delcode`\\="26E30F \delcode`\>="26930B|
+
+\bugonpage 347, lines 1 and 2 (3/16/84)
+
+\ninepoint\noindent
+|\count18=3 % this counter allocates math families 4, 5, 6, ...|\hfil\break
+|\count19=255 % this counter allocates insertions 254, 253, 252, ...|
+
+\bugonpage 350, line 9 from the bottom (3/16/84)
+
+\ninepoint\noindent
+font, whose information does not have to be loaded again.
+
+\bugonpage 354, line 5 (6/7/84)
+
+\ninepoint\noindent
+|\def\ialign{\everycr={}\tabskip=0pt \halign} % initialized \halign|
+
+\bugonpage 355, lines 19--21 (7/3/84)
+
+\ninepoint\noindent
+subdivision in a document; to use it, you say
+`|\beginsection|\<section title>' followed by a blank line (or~|\par|).
+The macro first emits glue and penalties, designed to start a new page
+if the present page is nearly full; then it makes a ^|\bigskip| and
+puts the section{\parfillskip=0pt\par}
+
+\bugonpage 355, lines 27--29 (7/3/84)
+
+\ninepoint\noindent
+|\outer\def\beginsection#1\par{\vskip0pt plus.3\vsize\penalty-250|
+\par\noindent
+| \vskip0pt plus-.3\vsize\bigskip\vskip\parskip|
+\par\noindent
+| \message{#1}\leftline{\bf#1}\nobreak\smallskip\noindent}|
+
+\bugonpage 355, line 37 (4/24/84)
+
+\ninepoint\noindent
+|\outer\def\proclaim #1. #2\par{\medbreak|
+
+\bugonpage 356, seven lines from the bottom (4/11/84)
+
+\ninepoint\noindent
+|\def|^|\TeX||{T\kern-.1667em \lower.5ex\hbox{E}\kern-.125em X}|
+
+\bugonpage 359, starting with line 2 (11/16/83)
+
+\ninepoint
+\beginlines
+|\mathchardef\ldotp="602E\mathchardef\cdotp="6201\mathchardef\colon="603A|
+|\def\ldots{\mathinner{\ldotp\ldotp\ldotp}}|
+|\def\cdots{\mathinner{\cdotp\cdotp\cdotp}}|
+|\def\vdots{\vbox{\baselineskip=4pt \lineskiplimit=0pt|
+| \kern6pt \hbox{.}\hbox{.}\hbox{.}}}|
+|\def\ddots{\mathinner{\mskip1mu\raise7pt\vbox{\kern7pt\hbox{.}}\mskip2mu|
+| \raise4pt\hbox{.}\mskip2mu\raise1pt\hbox{.}\mskip1mu}}|
+\endlines
+
+\bugonpage 359, starting with line 19 (11/3/83)
+
+{\ninepoint\parindent=0pt
+|\def|^|\overbrace|%
+ |#1{\mathop{\vbox{\ialign{##\crcr\noalign{\kern3pt}|\parbreak%
+| \downbracefill\crcr\noalign{\kern3pt\nointerlineskip}|\parbreak%
+| $\hfil\displaystyle{#1}\hfil$\crcr}}}|^|\limits||}|
+
+|\def|^|\underbrace||#1{\mathop{\vtop{\ialign{##\crcr|\parbreak%
+| $\hfil\displaystyle{#1}\hfil$\crcr|%
+ |\noalign{\kern3pt\nointerlineskip}|\parbreak%
+| \upbracefill\crcr\noalign{\kern3pt}}}}\limits}|
+}
+
+\bugonpage 359, seventh line from the bottom (2/29/84)
+
+\ninepoint\noindent
+|\def\backslash{\delimiter"026E30F } \def\bracevert{\delimiter"000033E }|
+
+\bugonpage 361, line 3 (8/17/84)
+
+\ninepoint\noindent
+
+|\def\buildrel#1\over#2{\mathrel{\mathop{\null#2}\limits^{#1}}}|
+
+\bugonpage 363, line 10 (4/26/84)
+
+\ninepoint\noindent
+| \ifhmode\edef\@sf{\spacefactor=\the\spacefactor}\/\fi|
+
+\bugonpage 364, starting with line 10 (11/1/83)
+
+{\ninepoint\parindent=0pt
+|\def\dosupereject{\ifnum\insertpenalties>0 % something is being held over|%
+\parbreak
+| \line{}\kern-\topskip\nobreak\vfill\supereject\fi}|
+}
+
+\bugonpage 364, line 28 (7/8/84)
+
+\ninepoint\noindent
+| \tracingmacros=2 \tracingparagraphs=1 \tracingrestores=1 |
+
+\bugonpage 370, line 7 (3/16/84)
+
+\ninepoint\noindent
+information about the \TeX\ Users Group.)
+
+\bugonpage 374, line 23 (7/8/84)
+
+\ninepoint
+\line{log file when |\tracingmacros=2| and
+ |\tracingcommands=2|. One of the important ways}
+
+\bugonpage 379, line 1 (1/12/84)
+
+\ninepoint\noindent
+A particular item can be selected by its position number from the left:
+
+\bugonpage 381, line 6 (2/12/84)
+
+\ninepoint\indent
+|\newcount\lineno % the number of file lines listed|
+
+\bugonpage 381, lines 24 and 25 (12/15/83)
+
+\ninepoint
+{\parfillskip=0pt
+Instead of listing a file verbatim, you might want to define a |\verbatim|
+macro such that `|\verbatim{$this$|{\tt\ is }|{\it!}}|' yields
+`|$this$|{\tt\ is }|{\it!}|'. It's somewhat\par}
+
+\bugonpage 385, lines 22 and 23 (1/12/84)
+
+\ninepoint\noindent
+macro, a parameter, or a token list
+variable; (b)~when \TeX\ must determine whether the token
+|&|~or ^|\span| ^^{ampersand}
+or ^|\cr| or~^|\crcr| is the end of an entry within an ^{alignment}.
+
+\bugonpage 387, two paragraphs in right column (1/18/84)
+
+\setbox0=\vbox{
+\eightpoint
+\tolerance=9999
+\hbadness=2300
+\finalhyphendemerits=3000000
+\doublehyphendemerits=1000000
+\parskip=1pt
+\parindent=1.5em
+\frenchspacing
+\hsize=166.8125pt
+\def\\#1{\raise.5pt\hbox{$\scriptscriptstyle
+ \ifx#1`\langle\!\langle\else\rangle\!\rangle\fi$}% Spanish quote marks
+ \ifx#1`\nobreak\hskip0pt \fi} % allow hyphenation
+\item{A.} Exactamente. Pero los profesores son tan conservadores
+que temer\'\i an espantar al tipo de estudiante \\`apisonadora\\'
+que hace lo que le proponen para casa, obe\-dien\-te\-mente y de forma
+mec\'anica. Adem\'as, no creo que les gustase el trabajo adicional
+de calificar respuestas a preguntas abiertas.
+
+\item{}La forma tradicional es dejar la parte creativa para los cursos
+altos. Durante diecisiete a\~nos o m\'as se ense\~na al es\-tu\-diante a
+aprobar, luego de golpe, cerca de la graduaci\'on, se le pide que haga
+algo original.
+
+}\rightline{\box0}
+
+\bugonpage 395, lines 21 and 22 (1/12/84)
+
+\ninepoint\noindent
+{\parfillskip=0pt
+Notice that the macros need to do their own checking for ligatures, and
+they also take appropriate actions when a paragraph begins with an opening
+quote. Since |\kern|\par}
+
+\bugonpage 399, line 1 (1/10/84)
+
+\ninepoint
+{\parfillskip=0pt
+Inside the output routine, |\box\footins| will now be a vbox of hboxes, and
+\par}
+
+\bugonpage 399, line 9 (2/28/84)
+
+\ninepoint\indent
+|.\hbox(7.6359+0.0)x269.62617 []|
+
+\bugonpage 407, line 4 (6/10/84)
+
+\ninepoint\noindent
+|\beginlinemode| and |\beginparmode| are defined to initiate these
+modes; and another%
+{\parfillskip=0pt\par}
+
+\bugonpage 408, line 15 (12/14/83)
+
+\noindent
+| P. O. Box 1009, Haga Alto, CA 94321 USA}|
+
+\smallskip\noindent\ninepoint
+[Also change the ZIP code in the return address on the envelope
+illustrated at the bottom of page 405.]
+
+\bugonpage 409, line 5 (2/18/84)
+
+\ninepoint\noindent
+|\font\twelveit=cmti10 at 12pt % (a cheap substitute for cmti12)|
+
+\bugonpage 417, last six lines (8/25/84)
+
+\ninepoint\noindent
+^|\parskip|
+of |0pt| |plus|~|.8pt| between adjacent entries, and since there is room for
+more than 50 lines per column; therefore the |manmac| balancing routine tries
+to make both the top and bottom baselines agree at the end of the index.
+In applications where the glue is not so flexible it would be more
+appropriate to let the right-hand column be a little short; the best
+way to do this is probably to replace the command `|\unvbox3|' by
+`|\dimen2=|^|\dp||3| |\unvbox3| |\kern-\dimen2| ^|\vfil|'.
+
+\bugonpage 422, lines 24--26 (2/9/84)
+
+\ninepoint\noindent
+(The last two lines use |\d@nger| and |\dd@nger|, which are non-|\outer|
+equivalents of\/ |\danger| and |\ddanger|; such duplication is necessary
+because control sequences of type ^|\outer| cannot appear within a |\def|.)
+
+\bugonpage 428, in the table of sixteen basic fonts (12/19/83)
+
+\ninepoint\noindent
+[The special fonts called |cmi10| and |cmi7| and |cmi5| should really be
+called |cmmi10| and |cmmi7| and |cmmi5|.]
+
+\bugonpage 433, last eight lines (8/17/84)
+
+\noindent
+explained in Appendix~G\null. If you want to increase
+the number of parameters past the number that actually appear in a font's
+metric information file, you can assign new values immediately after that font
+has been loaded. For example, if some font |\ff| with seven parameters
+has just entered \TeX's memory, the command |\fontdimen13\ff=5pt| will set
+parameter number~13 to $5\pt$; the intervening parameters, numbers 8--12,
+will be set to zero. You can even give more than seven parameters to
+^|\nullfont|, provided that you assign the values before any actual fonts
+have been loaded.
+
+\bugonpage 445, line 6 (11/11/83)
+
+\ninepoint
+\line{if $(a-{1\over2}\theta)-
+\bigl(h(z)-v\bigr)<\varphi$, increase~$v$ by the difference. Finally
+construct a vbox of}
+
+\bugonpage 449, line 12 (1/18/84)
+
+\line{immediately clear why the `n' should
+be attached to the `e' in one case but not}
+
+\bugonpage 459, left column, line 2 (1/18/84)
+
+\eightpoint
+al-Khw\^arizm\^\i, abu Ja`far Mu\d{h}ammad
+
+\bugonpage 460, index entry for Beethoven (8/16/84)
+
+\eightpoint
+Change `von' to `van'.
+
+\bugonpage 461, third line in left column (8/25/84)
+
+\eightpoint The entry for |\box255| should not be indented.
+
+\bugonpage 461, index entry for boxed material (8/2/84)
+
+\eightpoint Add `{\it 420}'.
+
+\bugonpage 462, index entry for {\tt\char`\\colon} (11/16/83)
+
+\eightpoint Add page \underbar{359} to this list.
+
+\bugonpage 462, right column, third-last line (5/21/84)
+
+\eightpoint\indent
+[Change `crochets' to `crotchets'; then move this entry down two lines.]
+
+\bugonpage 463, right column, line 16 (5/20/84)
+
+\eightpoint\indent
+design size, 16--17, 213.
+
+\bugonpage 464, index entry for {\tt\char`\\dump} (1/10/84)
+
+\eightpoint Add page {\it 344\/} to this list.
+
+\bugonpage 464, right column, line 5 (1/5/84)
+
+\eightpoint
+Dvo\v r\'ak, Anton\'\i n Leopold, 409.
+
+\bugonpage 464, index entry for {\tt\char`\\end} (8/25/84)
+
+\eightpoint Page number 264 should be underlined.
+
+\bugonpage 465, index entry for {\tt\char`\\everydisplay} (8/25/84)
+
+\eightpoint Add page {\it 326\/} to this list.
+
+\bugonpage 465, index entry for {\tt\char`\\filbreak} (7/3/84)
+
+\eightpoint Delete the reference to page number 355.
+
+\bugonpage 466, index entry for {\tt\char`\\footnote} (4/26/84)
+
+\eightpoint Page number 363 should be underlined.
+
+\bugonpage 467, index entry for {\tt\char`\\hidewidth} (7/3/84)
+
+\eightpoint Page number 354 should be underlined.
+
+\bugonpage 468, index entry for insertions (8/25/84)
+
+\eightpoint Add pages 115--117, 122--125 to this list.
+
+\bugonpage 469, index entry for {\tt\char`\\kern} (11/1/83)
+
+\eightpoint Add page {\it 256\/} to this list.
+
+\bugonpage 470, index entry for {\tt\char`\\limits} (11/3/83)
+
+\eightpoint Add page {\it 359\/} to this list.
+
+\bugonpage 472, right column, lines 10--11 (7/9/84)
+
+{\eightpoint
+\indent
+|\normalbaselines|\kern1pt,
+ {\it 325}, 349, $\underline{351}$, {\it 414--415}.\par
+\baselineskip=9.9pt
+\indent
+|\normalbaselineskip|\kern1pt,
+ $\underline{349}$, {\it 414--415}.\par
+}
+
+\bugonpage 472, index entry for {\tt\char`\\null} (7/3/84)
+
+\eightpoint Page number 351 should be underlined.
+
+\bugonpage 472, right column, line 28 (1/3/84)
+
+\eightpoint\indent
+\hbox to0pt{\hss\lower1pt\hbox{*}}|\nullfont|, 14, 153, 271, 433.
+
+\bugonpage 476, a new index entry (8/25/84)
+
+\eightpoint\indent
+shifted output, {\sl see\/} |\hoffset|, |\voffset|.
+
+\bugonpage 476, index entry for shriek (8/25/84)
+
+\eightpoint It should not be capitalized.
+
+\bugonpage 478, index entry for \'Swierczkowski (9/15/84)
+
+\eightpoint
+The middle name should be `S\l awomir'.
+
+\bugonpage 479, last seven lines in the left column (8/23/84)
+
+\eightpoint
+{\baselineskip=9.9pt
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\tracingmacros|, $\underline{205}$, $\underline{212}$, 273, {\it329}.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\tracingonline|, 121, 212, 273, $\underline{303}$.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\tracingoutput|, $\underline{254}$, 273, {\it301--302}.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\tracingpages|, {\it112--114}, 124, 273, $\underline{303}$.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\tracingparagraphs|, {\it98--99}, 273, $\underline{303}$.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\tracingrestores|, 273, $\underline{301}$, $\underline{303}$.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\tracingstats|, 273, $\underline{300}$, $\underline{303}$, {\it383}.
+}
+
+\bugonpage 479, index entry for underlined text (8/2/84)
+
+\eightpoint Add `{\sl see also\/} |\underbar|'.
+
+\bugonpage 480, index entry for {\tt\char`\\vbox} (11/1/83)
+
+\eightpoint Delete page 256 from this list.
+
+\bye
+
diff --git a/systems/knuth/dist/errata/errata.seven b/systems/knuth/dist/errata/errata.seven
new file mode 100644
index 0000000000..0866189da5
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.seven
@@ -0,0 +1,821 @@
+% Bugs (sigh) in Computers \& Typesetting
+
+\input manmac
+\font\sltt=cmsltt10
+\font\niness=cmss9
+\font\ninessi=cmssi9
+\proofmodefalse
+\raggedbottom
+\output{\hsize=29pc \onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\today{\number\day\
+ \ifcase\month\or
+ Jan\or Feb\or Mar\or Apr\or May\or Jun\or
+ Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi
+ \ \number\year}
+
+\def\cutpar{{\parfillskip=0pt\par}}
+
+\def\rhead{Bugs in {\tensl Computers \& Typesetting, 1991}}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\buginvol#1(#2) \par{\bigbreak\penalty-1000\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Volume #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\slMF{{\manual 89:;}\-{\manual <=>:}} % slant the logo
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\newcount\nn
+\newdimen\nsize \newdimen\msize \newdimen\ninept \ninept=9pt
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+
+\tenpoint
+\noindent This is a list of all corrections made to {\sl Computers \&
+Typesetting}, Volumes A,~B, C, and D\null, between 1 January 1991 and
+15 March 1992.
+Corrections made to the softcover version of {\sl The \TeX book\/} are
+the same as corrections to Volume~A\null. Corrections to the softcover
+version of {\sl The \slMF\kern1ptbook\/} are the same as corrections
+to Volume~C\null. Some of the corrections below have already been made in
+reprintings of the books. Changes to Volume~B refer to the fourth printing
+(1991), which differs markedly from earlier printings because it includes
+all the revisions for \TeX3.0. Changes to Volume~D refer to the third
+printing (1991), which differs markedly from earlier printings because
+it includes all the revisions for \MF\kern1pt2.0. Changes to the mini-indexes
+and master indexes of Volumes B and~D are not shown here unless they are
+not obviously derivable from what has been shown.
+Dozens of changes, too many to list
+here, have been made to Volume~E because of recent upgrades to the
+Computer Modern font source files.
+Those changes, which affect only the digitization at
+low resolution and the appearance of lowercase delta and
+some characters in the math symbols
+fonts (but not the {\tt TFM} files), are documented at
+the end of file {\tt cm85.bug}.
+\looseness=-1
+
+ % volume A
+
+\bugonpage A96, lines 9--11 (9/18/91)
+
+\ninepoint
+Some ^{German} words traditionally change their spelling
+when they are split between lines. For example, `backen' becomes `bak-ken'
+and `Bettuch' becomes `Bett-tuch'. How can you instruct \TeX\ to produce
+such effects?
+
+\bugonpage A178, line 17 (11/19/91)
+
+\ninepoint
+If you say `|\phantom{|\<subformula>|}|' in any formula, plain
+\TeX\ will do its\cutpar
+
+\bugonpage A286, bottom two lines and continuing into A287 (11/21/91)
+
+\ninepoint\noindent
+stands for zero or more \<assignment>
+commands other than |\setbox|.
+If the assignments are not followed by a \<character>, where
+\<character> stands for any of the commands just discussed in the previous
+paragraph, \TeX\ treats |\accent| as if it were |\char|, except that
+the space factor is set to 1000. Otherwise the character that follows
+the assignment is accented by the character that corresponds to the
+\<8-bit number>. \ (The purpose of the intervening assignments is to
+allow the accenter and accentee to be in different fonts.) \ If the
+accent must be moved up or down, it is put into an hbox that is
+raised or lowered. Then the accent is effectively superposed on the
+character by means of kerns, in such a way that the width of the accent
+does not influence the width of the resulting horizontal list.
+Finally, \TeX\ sets |\spacefactor=1000|.
+
+\bugonpage A291, lines 6--8 (11/21/91)
+
+\ninepoint\noindent
+`|}|' may be followed by
+optional \<assignment> commands other than |\setbox|,
+after which `|$$|'~must conclude
+the display. \TeX\ will insert the |\abovedisplayskip| and
+|\belowdisplayskip| glue before and after the result of the alignment.
+
+\bugonpage A293, line 14 (9/18/91)
+
+\ninepoint\noindent
+explained in Appendix~G\null. \TeX\
+scans \<one optional space> after completing a displayed formula; this is
+usually the implicit space at the end of a line in the input file.
+
+\bugonpage A311, bottom four lines (9/18/91)
+
+\ninepoint
+\ansno12.7: 1000, except: 999 after |O|, |B|, |S|, |D|, and |J|; 1250 after the
+comma; 3000 after the exclamation point, the right-quote marks, and the
+periods. If a period had come just after the |B| (i.e., if the text had
+said `|B. Sally|'), the space factor after that period would have
+been~1000, not~3000.
+
+\bugonpage A314, lines 16--18 from the bottom (1/10/92)
+
+\ninepoint
+\ansno14.8: |ba\ck/en| and |Be\ttt/uch|, where the macros |\ck/| and |\ttt/|
+are defined by
+\begintt
+\def\ck/{\discretionary{k-}{k}{ck}}
+\def\ttt/{tt\discretionary{-}{t}{}}
+\endtt
+
+\bugonpage A354, line 8 (9/18/91)
+
+\ninepoint\noindent
+|\def\multispan#1{\omit\mscount=#1\relax\loop\ifnum\mscount>1 \sp@n\repeat}|%
+\kern-10pt\null
+
+\bugonpage A356, line 11 from the bottom (9/23/91)
+
+\ninepoint\noindent
+| \else{\ooalign{\unhbox0\crcr\hidewidth\char'30\hidewidth}}\fi}|
+
+\bugonpage A358, line 8 from the bottom (9/18/91)
+
+\ninepoint\noindent
+|\mathchardef\mapstochar="3237 \def\mapsto{\mapstochar\rightarrow}|
+
+\bugonpage A359, line 13 (11/4/91)
+
+\ninepoint\noindent
+|\def\overrightarrow#1{\vbox{\m@th\ialign{##\crcr|
+
+\bugonpage A359, line 16 (11/4/91)
+
+\ninepoint\noindent
+|\def\overleftarrow#1{\vbox{\m@th\ialign{##\crcr|
+
+\bugonpage A359, line 19 (11/4/91)
+
+\ninepoint\noindent
+|\def\overbrace#1{\mathop{\vbox{\m@th\ialign{##\crcr\noalign{\kern3pt}|
+
+\bugonpage A359, line 22 (11/4/91)
+
+\ninepoint\noindent
+|\def\underbrace#1{\mathop{\vtop{\m@th\ialign{##\crcr|
+
+\bugonpage A359, lines 7--14 from the bottom (1/11/92)
+
+\ninepoint\noindent
+|\def\lgroup{\delimiter"462833A } \def\rgroup{\delimiter"562933B }|%
+\par\noindent
+|\def\lmoustache{\delimiter"437A340 } \def\rmoustache{\delimiter"537B341 }|%
+ \kern-2pt\null\par\noindent
+|\def\uparrow{\delimiter"3222378 } \def\Uparrow{\delimiter"322A37E }|%
+\par\noindent
+|\def\downarrow{\delimiter"3223379 } \def\Downarrow{\delimiter"322B37F }|%
+\par\noindent
+|\def\updownarrow{\delimiter"326C33F } \def\arrowvert{\delimiter"026A33C }|%
+\par\noindent
+|\def\Updownarrow{\delimiter"326D377 } \def\Arrowvert{\delimiter"026B33D } |%
+\par\noindent
+|\def\vert{\delimiter"026A30C } \def\Vert{\delimiter"026B30D } |%
+\par\noindent
+|\def\backslash{\delimiter"026E30F } \def\bracevert{\delimiter"077C33E }|
+
+\bugonpage A360, line 13 (11/19/91)
+
+\ninepoint\noindent
+|\phantom|, |\smash|, |\root|, and other
+operations. (Actually |\phantom| and |\smash| are not perfect: They
+assume that the current style is uncramped.)
+
+\bugonpage A360, line 2 from the bottom (11/4/91)
+
+\ninepoint\noindent
+|\def\c@ncel#1#2{\m@th\ooalign{$\hfil#1\mkern1mu/\hfil$\crcr$#1#2$}}|
+
+\bugonpage A361, top line (11/4/91)
+
+\ninepoint\noindent
+|\def\rlh@#1{\vcenter{\m@th\hbox{\ooalign{\raise2pt|
+
+\bugonpage A364, line 5 from the bottom (11/4/91)
+
+\ninepoint\noindent
+|\def|^|\fmtname||{plain}\def\fmtversion{3.141}|
+
+\bugonpage A377, the bottom 17 lines (9/18/91)
+
+\eightpoint\noindent\hangindent\parindent\hangafter-3
+story: Macro |\stest| decides whether or not
+a given token list register begins with
+a \<space token> as defined in Chapter~24. If so, the macro
+decides whether the token is explicit and/or funny and/or active.
+\begintt
+\newif\ifspace \newif\iffunny \newif\ifexplicit \newif\ifactive
+\def\stest#1{\funnyfalse \expandafter\s\the#1! \stest}
+\def\s{\global\explicitfalse \global\activefalse \futurelet\next\ss}
+\def\ss{\ifcat\noexpand\next\stoken\let\nxt\sx\else\let\nxt\ns\fi\nxt}
+\def\sx{\spacetrue\ifx\next\stoken\let\nxt\sss\else\let\nxt=\ssss\fi\nxt}
+\long\def\sss#1 #2\stest{\def\next{#1}%
+ \ifx\next\empty \global\explicittrue \else\testactive#1\s\fi}
+\long\def\ssss#1#2\stest{\funnytrue{\escapechar=\if*#1`?\else`*\fi\relax
+ \if#1\string#1\uccode`#1=`~ % we assume that ~ is an active character
+ \uppercase{\ifcat\noexpand#1}\noexpand~\global\activetrue
+ \else\global\explicittrue\fi
+ \else\testactive#1\s\fi}}
+\long\def\ns#1\stest{\spacefalse}
+\long\def\testactive#1#2\s{\expandafter\tact\string#1\s\tact}
+\long\def\tact#1#2\tact{\def\next{#2}\ifx\next\xs\global\activetrue
+ \else\ifx\next\empty \global\activetrue\fi\fi} \def\xs{\s}
+\endtt
+
+\bugonpage A444, lines 15--26 (3/26/91)
+
+\ninepoint
+\textindent{\bf14.}If the current item is an Ord atom,
+go directly to Rule~17 unless
+all of the following are true: The nucleus is a symbol; the subscript
+and superscript are both empty; the very next item in the math list is an
+atom of type Ord, Op, Bin, Rel, Open, Close, or Punct; and the nucleus of the
+next item is a symbol whose family is the same as the family in the present
+Ord atom. In such cases the present symbol is marked as a text symbol.
+If the font information shows a ligature between this symbol and the
+following one, using the specified family and the current size, then
+insert the ligature character and continue as specified by the font;
+in this process, two characters may collapse into a single Ord
+text symbol, and/or new Ord text characters may appear. If the font information
+shows a kern between the current symbol and the next, insert a kern item
+following the current atom.
+As soon as an Ord atom has been fully processed for ligatures and kerns,
+go to Rule~17.
+
+\bugonpage A446, lines 5 and 6 from the bottom (1/13/92)
+
+\ninepoint\noindent
+are used to change the current style just as
+in the first pass, so that both passes have the same value of~$C$ when
+they work on any particular atom.
+
+\bugonpage A447, in the parameter usage table (1/13/92)
+
+\ninepoint\noindent[Delete the entry for `$\sigma_2$'; the entry for
+`$\sigma_{17}$' moves down to the bottom of the left column.]
+
+\bugonpage A447, line 2 after the parameter usage table (1/13/92)
+
+\ninepoint\noindent
+to parameters in arbitrary families:
+Rule~17 uses |\fontdimen| parameter~2 (space) to de-\cutpar
+
+\bugonpage A467, entry for {\tt\char`\\hss} (9/18/91)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt\char`\\hss}, 71--72, {\it82--83}, 233, 283, 285, 290, 442.
+
+\bugonpage A467, new subentry under hyphenation (9/18/91)
+
+\eightpoint\indent\quad
+suppressing, 93, 414, 424, 454.
+
+\bugonpage A476, right column (11/21/91)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt\char`\\setbox}, 66--67, 77, 81, $\underline{120}$, 276, 279, 286,\par
+\indent\qquad 291, {\it386--392}.
+
+ % volume B
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage B2, line 10 from the bottom (1/11/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{banner}\equiv\hbox{\tt\char'23}$%
+{\tt This\]is\]TeX,\]Version\]3.141\char'23}\quad
+$\{\,$printed when \TeX\ starts$\,\}$
+
+\bugonpage B18, lines 21 and 22 (10/12/91)
+
+\tenpoint\noindent
+must have an \\{xchr} equivalent in the local
+character set. (This restriction applies only to preloaded strings,
+not to those generated dynamically by the user.)
+
+\bugonpage B26, new line before fourth line from bottom (1/24/92)
+
+\ninepoint\noindent\hskip20pt
+\\{nl}: \\{integer};\quad$\{\,$new-line character to restore$\,\}$
+
+\bugonpage B26, bottom line and top 3 lines of B27 (1/24/92)
+
+\ninepoint\noindent\hskip30pt
+{\bf else begin if\/} $\\{selector}>\\{pseudo}$ {\bf then}\par
+\noindent\hskip50pt
+{\bf begin} \\{print\_char}($s$); \ {\bf return};\quad
+ $\{\,$internal strings are not expanded$\,\}$\par\noindent\hskip50pt
+{\bf end};\par\noindent\hskip40pt
+{\bf if\/} ($\langle\,$Character $s$ is the current new-line
+ character{\sevenrm\kern.5em244}$\,\rangle$) {\bf then}\par\noindent\hskip50pt
+{\bf if\/} $\\{selector}<\\{pseudo}$ {\bf then}\par\noindent\hskip60pt
+{\bf begin} \\{print\_ln}; \ {\bf return}; \
+{\bf end};\par\noindent\hskip40pt
+$\\{nl}\gets\\{new\_line\_char}$; \ $\\{new\_line\_char}\gets-1$;\quad
+ $\{\,$temporarily disable new-line character$\,\}$\par\noindent\hskip40pt
+$j\gets\\{str\_start}[s]$;\par\noindent\hskip40pt
+{\bf while} $j<\\{str\_start}[s+1]$ {\bf do}\par\noindent\hskip50pt
+{\bf begin} $\\{print\_char}(\\{so}(\\{str\_pool}[j]))$; \ $\\{incr}(j)$; \
+{\bf end};\par\noindent\hskip40pt
+$\\{new\_line\_char}\gets\\{nl}$; \ {\bf return};\par\noindent\hskip40pt
+{\bf end};
+
+\bugonpage B27, lines 9 and 10 (9/19/91)
+
+\tenpoint\noindent
+{\bf 60.\quad}%
+Control sequence names, file names, and strings constructed with
+{\tt\char`\\string} might contain \\{ASCII\_code} values that can't
+be printed using \\{print\_char}. Therefore we use
+\\{slow\_print} for them:
+
+\bugonpage B27, lines 13--26 (1/24/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf var} $j$: \\{pool\_pointer};\quad$\{\,$current character code
+ position$\,\}$\par\noindent\hskip10pt
+{\bf begin if\/} $(s\ge\\{str\_ptr})\lor(s<256)$ {\bf then} \\{print}(s)
+\par\noindent\hskip10pt {\bf else begin} $j\gets\\{str\_start}[s]$;\par
+\noindent\hskip20pt{\bf while} $j<\\{str\_start}[s+1]$ {\bf do}\par
+\noindent\hskip30pt{\bf begin} $\\{print}(\\{so}(\\{str\_pool}[j]))$; \
+ $\\{incr}(j)$;\par\noindent\hskip30pt
+{\bf end};\par\noindent\hskip20pt
+{\bf end};\par\noindent\hskip10pt
+{\bf end};
+
+\bugonpage B28, line 8 (9/19/91)
+
+\ninepoint\noindent\hskip10pt
+{\bf else begin} \\{slow\_print}(\\{format\_ident}); \ \\{print\_ln};
+
+\bugonpage B33, line 3 (1/11/92)
+
+\tenpoint\noindent
+recursively. A similar interlock is provided by \\{set\_box\_allowed}.
+
+\bugonpage B33, new line to come after line 14 (1/11/92)
+
+\ninepoint\noindent
+\\{set\_box\_allowed}: \\{boolean};\quad
+ $\{\,$is it safe to do a |\setbox| assignment?$\,\}$
+
+\bugonpage B33, new line to come after line 20 (1/11/92)
+
+\ninepoint\noindent\hskip10pt
+$\\{set\_box\_allowed}\gets\\{true}$;
+
+\bugonpage B36, line 12 (9/19/91)
+
+\ninepoint\noindent\hskip30pt
+{\bf begin} \\{print\_nl}({\tt\char`\"You\]want\]to\]edit\]file\]\char`\"});
+ \ \\{slow\_print}(\\{input\_stack}[\\{base\_ptr}].\\{name\_field});
+
+\bugonpage B46, lines 9 and 10 (5/24/91)
+
+\tenpoint\noindent
+arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the
+routines cited there must be modified to allow negative glue ratios.)
+
+\bugonpage B47, lines 2 and 3 (5/24/91)
+
+\tenpoint\noindent
+structures on a \\{memory\_word}, which contains either a (signed) integer,
+possibly scaled, or a (signed) \\{glue\_ratio}, or a small number of
+fields that are one half or one quarter of the size used\cutpar
+
+\bugonpage B177, lines 10 and 11 (9/19/91)
+
+\ninepoint\noindent\hskip20pt
+{\bf begin} \\{print\_err}({\tt\char`\"Bad\]mathchar\char`\"});\par
+\noindent\hskip20pt
+\\{help2}({\tt\char`\"A\]mathchar\]number\]must\]be\]between\]0\]%
+ and\]32767.\char`\"})
+
+\bugonpage B196, new lines after line 11 (1/13/92)
+
+\ninepoint\noindent\hskip20pt
+{\bf if\/} $\\{align\_state}<1000000$ {\bf then}\quad
+ $\{\,$unmatched `|}|' aborts the line$\,\}$\par\noindent\hskip30pt
+{\bf begin repeat} \\{get\_token}; \ {\bf until} $\\{cur\_tok}=0$;\par
+\noindent\hskip30pt$\\{align\_state}\gets1000000$; \ {\bf goto} \\{done};\par
+\noindent\hskip30pt{\bf end};
+
+\bugonpage B208, line 21 (9/19/91)
+
+\ninepoint\noindent\hskip10pt
+{\bf begin} \\{slow\_print}($a$); \ \\{slow\_print}($n$); \
+ \\{slow\_print}($e$);
+
+\bugonpage B214, line 14 (9/19/91)
+
+\ninepoint\noindent\hskip10pt
+{\bf begin} \\{wlog}(\\{banner}); \\{slow\_print}(\\{format\_ident}); \
+ \\{print}({\tt\char`\"\]\]\char`\"}); \
+ \\{print\_int}(\\{day}); \
+ \\{print\_char}({\tt\char`\"\]\char`\"});
+
+\bugonpage B214, line 2 from the bottom (9/19/91)
+
+\ninepoint\noindent\hskip10pt
+\\{print\_char}({\tt\char`\"(\char`\"}); \
+\\{incr}(\\{open\_parens}); \
+\\{slow\_print}(\\{name}); \
+\\{update\_terminal}; \
+$\\{state}\gets\\{new\_line}$;
+
+\bugonpage B234, line 22 (9/19/91)
+
+\ninepoint\noindent\hskip20pt
+\\{print}({\tt\char`\"\]in\]font\]\char`\"}); \
+\\{slow\_print}(\\{font\_name}[$f$]); \
+\\{print\_char}({\tt\char`\"!\char`\"}); \
+\\{end\_diagnostic}(\\{false});
+
+\bugonpage B267, lines 7 and 8 (9/19/91)
+
+\ninepoint\noindent\hskip20pt
+\\{print\_nl}({\tt\char`\"Output\]written\]on\]\char`\"}); \
+\\{slow\_print}(\\{output\_file\_name});\par\noindent\hskip20pt
+\\{print}({\tt\char`\"\](\char`\"}); \
+\\{print\_int}(\\{total\_pages}); \
+\\{print}({\tt\char`\"\]page\char`\"});
+
+\bugonpage B296, new lines after line 8 of section 716 (1/11/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf if\/} $f<0$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} \\{decr}($n$); \ $f\gets f+\oct{200000}$;\par\noindent\hskip20pt
+{\bf end};
+
+\bugonpage B297, new lines after line 7 of section 717 (1/11/92)
+
+\ninepoint\noindent\hskip20pt
+{\bf if\/} $f<0$ {\bf then}\par\noindent\hskip30pt
+{\bf begin} \\{decr}($n$); \ $f\gets f+\oct{200000}$;\par\noindent\hskip30pt
+{\bf end};
+
+\bugonpage B348, bottom two lines (1/3/92)
+
+\tenpoint\noindent\hskip10pt
+Up to three passes might be made through the paragraph in an attempt to find at
+least one set of feasible breakpoints. On the first pass, we have
+$\\{threshold}=\\{pretolerance}$ and $\\{second\_pass}=$\cutpar
+
+\bugonpage B364, line 20 (1/3/92)
+
+\tenpoint\noindent
+{\bf 863.\quad}
+The `{\bf loop}' in the following code is performed at most
+thrice per call of \\{line\_break}, since\cutpar
+
+\bugonpage B377, insert new line after line 12 (9/19/91)
+
+\ninepoint\noindent
+\\{hyf\_bchar}:\enspace\\{halfword};\quad
+ $\{\,$boundary character after $c_n\,\}$
+
+\bugonpage B378, line 12 from the bottom (9/19/91)
+
+\ninepoint\noindent\hskip30pt
+$\\{hyf\_bchar}\gets\\{character}(s)$; \
+$c\gets\\{qo}(\\{hyf\_bchar})$;
+
+\bugonpage B378, line 9 from the bottom (1/10/92)
+
+\ninepoint\noindent\hskip30pt
+$\\{hb}\gets s$; \ \\{incr}(\\{hn}); \ $\\{hu}[\\{hn}]\gets c$; \
+$\\{hc}[\\{hn}]\gets\\{lc\_code}(c)$; \
+$\\{hyf\_bchar}\gets\\{non\_char}$;
+
+\bugonpage B378, line 5 from the bottom (9/19/91)
+
+\ninepoint\noindent\hskip30pt
+{\bf else if} $(\\{type}(s)=\\{kern\_node})\land(\\{subtype}(s)=\\{normal})$
+ {\bf then} $\\{hb}\gets s$\par\noindent\hskip40pt
+ {\bf else goto} \\{done3};
+
+\bugonpage B379, line 6 (9/19/91)
+
+\ninepoint\noindent\hskip10pt
+$j\gets\\{hn}$; \ $q\gets\\{lig\_ptr}(s)$; \
+{\bf if\/} $q>\\{null}$ {\bf then} $\\{hyf\_bchar}\gets\\{character}(q)$;
+
+\bugonpage B379, new line between lines 14 and 15 (1/10/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf if\/} $\\{odd}(\\{subtype}(s))$ {\bf then}
+$\\{hyf\_bchar}\gets\\{font\_bchar}[\\{hf}]$ {\bf else}
+$\\{hyf\_bchar}\gets\\{non\_char}$;
+
+\bugonpage B379, line 19 (9/19/91)
+
+\ninepoint\noindent\hskip10pt
+{\bf if\/} $\\{hn}<\\{l\_hyf}+\\{r\_hyf}$ {\bf then goto} \\{done1}; \
+\quad$\{\,$\\{l\_hyf} and \\{r\_hyf} are always $\ge1\,\}$
+
+\bugonpage B380, lines 9--11 from the bottom reduce to a single line (1/10/92)
+
+\ninepoint\noindent\hskip10pt
+$q\gets\\{link}(\\{hb})$; \ $\\{link}(\\{hb})\gets\\{null}$; \
+$r\gets\\{link}(\\{ha})$; \ $\\{link}(\\{ha})\gets\\{null}$; \
+$\\{bchar}\gets\\{hyf\_bchar}$;
+
+\bugonpage B436, lines 9 and 10 (3/15/92)
+
+\tenpoint
+$$\\{cur\_r}=\cases{\\{character}(\\{lig\_stack}),&if $\\{lig\_stack}>
+ \\{null}$;\cr
+ \\{font\_bchar}[\\{cur\_font}],&otherwise;\cr}$$
+except when $\\{character}(\\{lig\_stack})=\\{font\_false\_bchar}
+ [\\{cur\_font}]$. Several additional global variables are needed.
+
+\bugonpage B438, line 13 from the bottom (3/15/92)
+
+\ninepoint\noindent\hskip10pt
+$\\{cur\_q}\gets\\{tail}$; \ $\\{cur\_l}\gets\\{character}(\\{lig\_stack})$;
+
+\bugonpage B507, line 6 of section 1241 (1/11/92)
+
+\ninepoint\noindent\hskip10pt
+\\{scan\_optional\_equals};\par\noindent\hskip10pt
+{\bf if\/} \\{set\_box\_allowed} {\bf then}
+$\\{scan\_box}(\\{box\_flag}+n)$\par\noindent\hskip10pt
+{\bf else begin} \\{print\_err}({\tt\char`\"Improper\]\char`\"});
+\ \\{print\_esc}({\tt\char`\"setbox\char`\"});\par\noindent\hskip20pt
+\\{help2}({\tt\char`\"Sorry,\]\char`\\setbox\]is\]not\]allowed\]after\]%
+\char`\\halign\]in\]a\]display,\char`\"})\par\noindent\hskip20pt
+({\tt\char`\"or\]between\]\char`\\accent\]%
+and\]an\]accented\]character.\char`\"});
+\ \\{error};\par\noindent\hskip20pt
+{\bf end};
+
+\bugonpage B511, new line inserted after line 3 (1/24/92)
+
+\ninepoint\noindent\hskip20pt
+\\{flushable\_string}: \\{str\_number};\quad
+ $\{\,$string not yet referenced$\,\}$
+
+\bugonpage B512, new line inserted after line 3 of section 1260 (1/24/92)
+
+\ninepoint\noindent\hskip10pt
+$\\{flushable\_string}\gets\\{str\_ptr}-1$;
+
+\bugonpage B512, the former line 6 of section 1260 (1/24/92)
+
+\ninepoint\noindent\hskip30pt
+{\bf begin if\/} $\\{cur\_name}=\\{flushable\_string}$ {\bf then}
+\par\noindent\hskip40pt{\bf begin} \\{flush\_string}; \
+ $\\{cur\_name}\gets\\{font\_name}[f]$; \ {\bf end};
+\par\noindent\hskip30pt{\bf if\/} $s>0$ {\bf then}
+
+\bugonpage B512, line 10 from the bottom (9/19/91)
+
+\ninepoint\noindent
+\\{set\_font}:\enspace{\bf begin}
+\\{print}({\tt\char`\"select\]font\]\char`\"}); \
+\\{slow\_print}(\\{font\_name}[\\{chr\_code}]);
+
+\bugonpage B514, line 9 (1/11/92)
+
+\ninepoint\noindent\hskip20pt
+$\\{set\_box\_allowed}\gets\\{false}$; \
+\\{prefixed\_command}; \
+$\\{set\_box\_allowed}\gets\\{true}$;
+
+\bugonpage B515, line 19 (9/19/91)
+
+\ninepoint\noindent\hskip10pt
+\\{slow\_print}($s$); \ \\{update\_terminal};
+
+\bugonpage B516, line 2 (9/19/91)
+
+\ninepoint\noindent\hskip10pt
+{\bf begin} \\{print\_err}({\tt\char`\"\char`\"}); \
+ \\{slow\_print}($s$);
+
+\bugonpage B531, lines 19 and 20 (9/19/91)
+
+\ninepoint\noindent\hskip10pt
+\\{print\_nl}({\tt\char`\"Beginning\]to\]dump\]on\]file\]\char`\"}; \
+\\{slow\_print}(\\{w\_make\_name\_string}(\\{fmt\_file})); \
+\\{flush\_string};\par\noindent\hskip10pt
+\\{print\_nl}({\tt\char`\"\char`\"}); \
+\\{slow\_print}(\\{format\_ident})
+
+\bugonpage B533, line 29 (9/19/91)
+
+\ninepoint\noindent\hskip30pt
+{\bf begin} \\{print\_nl}({\tt\char`\"Transcript\]written\]on\]\char`\"}); \
+\\{slow\_print}(\\{log\_name}); \
+\\{print\_char}({\tt\char`\".\char`\"});
+
+\bugonpage B538, line 13 (9/19/91)
+
+\ninepoint\noindent
+10:\enspace\\{slow\_print}($n$);
+
+\bugonpage B577, left column (12/23/91)
+
+\eightpoint\noindent[Add 798 to the index entries for `system dependencies'.]
+
+ % volume C
+\hsize=29pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+
+\bugonpage C262, line 15 (3/26/91)
+
+\ninepoint\noindent
+|string base_name, base_version; base_name="plain"; base_version="2.7";|
+
+\bugonpage C271, line 17 from the bottom (3/26/91)
+
+\ninepoint\noindent
+| currentpen_path shifted (z.t_) withpen penspeck enddef;|
+
+\bugonpage C347, Bront''e entry (1/29/91)
+
+\eightpoint\noindent
+[The accent was clobbered; her name should, of course, be Bront\"e.
+Fix the entries for D\"urer, M\"obius, and Stravinsky in the same way.]
+
+\bugonpage C348, left column (1/11/92)
+
+\eightpoint\indent
+compound statement, $\underline{155}$, 217.
+
+\bugonpage C353, right column (1/11/92)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt numeric}, 55, $\underline{56}$, {\it65}, 88.
+
+\bugonpage C354, miscellaneous entries in both columns (1/11/92)
+
+\eightpoint
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt openwindow}, $\underline{191}$--$\underline{193}$, 220, {\it277},
+ {\it312--313}.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt or}, {\it65}, $\underline{170}$, 210, 237, 288--289.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt pair}, 55, $\underline{56}$, 65.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt path}, 55, $\underline{56}$, 171.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt pen}, 55, $\underline{56}$, {\it65}, 170.
+\par\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt picture}, 55, $\underline{56}$, {\it114}.
+
+\bugonpage C356, right column (1/11/92)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt string}, 55, $\underline{56}$, 69.
+
+\bugonpage C357, right column (1/11/92)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt transform}, 55, $\underline{56}$, 57, 141--143, {\it160}, 266.
+
+ % Volume D
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage D2, last line of section 2 (1/24/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{banner}\equiv\hbox{\tt\char'23}$%
+{\tt This\]is\]METAFONT,\]Version\]2.71\char'23}\quad
+$\{\,$printed when \MF\ starts$\,\}$
+
+\bugonpage D102, line 15 from the bottom (11/1/91)
+
+\tenpoint\noindent
+Then
+$\\{eq\_type}(h(x))=\\{tag\_token}$ and $\\{equiv}(h(x))=p$,
+where $p$~is a two-word value
+node with\cutpar
+
+\bugonpage D188, lines 16 and 17 (1/24/92)
+
+\tenpoint\noindent
+errors. Our subroutines also obey the identity $t[a,b]+t[b,a]=a+b$.
+
+\bugonpage D190, new copy before bottom four lines (1/24/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf if\/} $\\{x\_coord}(r)<\\{x\_coord}(\\{pp})$ {\bf then}
+ $\\{x\_coord}(r)\gets\\{x\_coord}(\\{pp})$\par\noindent\hskip10pt
+{\bf else if\/} $\\{x\_coord}(r)>\\{dest\_x}$ {\bf then}
+ $\\{x\_coord}(r)\gets\\{dest\_x}$;\par\noindent\hskip10pt
+{\bf if\/} $\\{left\_x}(r)>\\{x\_coord}(r)$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} $\\{left\_x}(r)\gets\\{x\_coord}(r)$; \
+{\bf if\/} $\\{right\_x}(\\{pp})>\\{x\_coord}(r)$ {\bf then}
+$\\{right\_x}(\\{pp})\gets\\{x\_coord}(r)$; \ {\bf end};\par\noindent\hskip10pt
+{\bf if\/} $\\{right\_x}(r)<\\{x\_coord}(r)$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} $\\{right\_x}(r)\gets\\{x\_coord}(r)$; \
+{\bf if\/} $\\{left\_x}(\\{qq})<\\{x\_coord}(r)$ {\bf then}
+$\\{left\_x}(\\{qq})\gets\\{x\_coord}(r)$; \ {\bf end};
+
+\bugonpage D191, new copy before bottom two lines of section 416 (1/24/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf if\/} $\\{x\_coord}(s)<\\{x\_coord}(r)$ {\bf then}
+ $\\{x\_coord}(s)\gets\\{x\_coord}(r)$\par\noindent\hskip10pt
+{\bf else if\/} $\\{x\_coord}(s)>\\{dest\_x}$ {\bf then}
+ $\\{x\_coord}(s)\gets\\{dest\_x}$;\par\noindent\hskip10pt
+{\bf if\/} $\\{left\_x}(s)>\\{x\_coord}(s)$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} $\\{left\_x}(s)\gets\\{x\_coord}(s)$; \
+{\bf if\/} $\\{right\_x}(r)>\\{x\_coord}(s)$ {\bf then}
+$\\{right\_x}(r)\gets\\{x\_coord}(s)$; \ {\bf end};\par\noindent\hskip10pt
+{\bf if\/} $\\{right\_x}(s)<\\{x\_coord}(s)$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} $\\{right\_x}(s)\gets\\{x\_coord}(s)$; \
+{\bf if\/} $\\{left\_x}(\\{qq})<\\{x\_coord}(s)$ {\bf then}
+$\\{left\_x}(\\{qq})\gets\\{x\_coord}(s)$; \ {\bf end};
+
+\bugonpage D194, lines 4 and 5 (1/24/92)
+
+\tenpoint\noindent[Delete those two lines; I no longer believe that the
+assertion has been proved (although it might be true).]
+
+\bugonpage D194, lines 7--13 of section 424 (1/24/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf if\/} $\\{y\_coord}(r)<\\{y\_coord}(p)$ {\bf then}
+ $\\{y\_coord}(r)\gets\\{y\_coord}(p)$\par\noindent\hskip10pt
+{\bf else if\/} $\\{y\_coord}(r)>\\{dest\_y}$ {\bf then}
+ $\\{y\_coord}(r)\gets\\{dest\_y}$;\par\noindent\hskip10pt
+{\bf if\/} $\\{x\_coord}(p)+\\{y\_coord}(r)>\\{dest\_x}+\\{dest\_y}$
+{\bf then} $\\{y\_coord}(r)\gets\\{dest\_x}+\\{dest\_y}-\\{x\_coord}(p)$;\par
+\noindent\hskip10pt
+{\bf if\/} $\\{left\_y}(r)>\\{y\_coord}(r)$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} $\\{left\_y}(r)\gets\\{y\_coord}(r)$; \
+{\bf if\/} $\\{right\_y}(p)>\\{y\_coord}(r)$ {\bf then}
+$\\{right\_y}(p)\gets\\{y\_coord}(r)$; \ {\bf end};\par\noindent\hskip10pt
+{\bf if\/} $\\{right\_y}(r)<\\{y\_coord}(r)$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} $\\{right\_y}(r)\gets\\{y\_coord}(r)$; \
+{\bf if\/} $\\{left\_y}(q)<\\{y\_coord}(r)$ {\bf then}
+$\\{left\_y}(q)\gets\\{y\_coord}(r)$; \ {\bf end};
+
+\bugonpage D194, lines 8--11 from the bottom (1/24/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf if\/} $\\{right\_y}(r)<\\{y\_coord}(r)$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} $\\{right\_y}(r)\gets\\{y\_coord}(r)$; \
+{\bf if\/} $\\{left\_y}(q)<\\{y\_coord}(r)$ {\bf then}
+$\\{left\_y}(q)\gets\\{y\_coord}(r)$; \ {\bf end};
+
+\bugonpage D195, lines 3--9 of section 425 (1/24/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf if\/} $\\{y\_coord}(s)<\\{y\_coord}(r)$ {\bf then}
+ $\\{y\_coord}(s)\gets\\{y\_coord}(r)$\par\noindent\hskip10pt
+{\bf else if\/} $\\{y\_coord}(s)>\\{dest\_y}$ {\bf then}
+ $\\{y\_coord}(s)\gets\\{dest\_y}$;\par\noindent\hskip10pt
+{\bf if\/} $\\{x\_coord}(r)+\\{y\_coord}(s)>\\{dest\_x}+\\{dest\_y}$
+{\bf then} $\\{y\_coord}(s)\gets\\{dest\_x}+\\{dest\_y}-\\{x\_coord}(r)$;\par
+\noindent\hskip10pt
+{\bf if\/} $\\{left\_y}(s)>\\{y\_coord}(s)$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} $\\{left\_y}(s)\gets\\{y\_coord}(s)$; \
+{\bf if\/} $\\{right\_y}(r)>\\{y\_coord}(s)$ {\bf then}
+$\\{right\_y}(r)\gets\\{y\_coord}(s)$; \ {\bf end};\par\noindent\hskip10pt
+{\bf if\/} $\\{right\_y}(s)<\\{y\_coord}(s)$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} $\\{right\_y}(s)\gets\\{y\_coord}(s)$; \
+{\bf if\/} $\\{left\_y}(q)<\\{y\_coord}(s)$ {\bf then}
+$\\{left\_y}(q)\gets\\{y\_coord}(s)$; \ {\bf end};
+
+\bugonpage D195, lines 3--7 from the bottom if section 425 (1/24/92)
+
+\ninepoint\noindent\hskip10pt
+{\bf if\/} $\\{right\_y}(s)<\\{y\_coord}(s)$ {\bf then}\par\noindent\hskip20pt
+{\bf begin} $\\{right\_y}(s)\gets\\{y\_coord}(s)$; \
+{\bf if\/} $\\{left\_y}(q)<\\{y\_coord}(s)$ {\bf then}
+$\\{left\_y}(q)\gets\\{y\_coord}(s)$; \ {\bf end};
+
+\bugonpage D289, lines 9 and 10 (11/1/91)
+
+\ninepoint\noindent\hskip20pt
+$p\gets\\{dep\_list}(p)$; \ $r\gets\\{inf\_val}$;\par\noindent\hskip20pt
+{\bf repeat if\/} $\\{value}(\\{info}(p))\ge\\{value}(r)$ {\bf then}\par
+
+\bugonpage D486, line 18 (11/1/91)
+
+\tenpoint\noindent
+The \\{label\_loc}
+and \\{label\_char} arrays have been set up to record all the
+starting addresses; we have\cutpar
+
+ % volume E
+\hsize=29pc
+\def\dashto{\mathrel{\hbox{-\kern-.05em}\mkern3.9mu\hbox{-\kern-.05em}}}
+
+
+ \bye
+
diff --git a/systems/knuth/dist/errata/errata.six b/systems/knuth/dist/errata/errata.six
new file mode 100644
index 0000000000..ad7444d860
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.six
@@ -0,0 +1,528 @@
+% Bugs (sigh) in Computers \& Typesetting
+
+\input manmac
+\font\sltt=cmsltt10
+\font\niness=cmss9
+\font\ninessi=cmssi9
+\proofmodefalse
+\raggedbottom
+\output{\hsize=29pc \onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\today{\number\day\
+ \ifcase\month\or
+ Jan\or Feb\or Mar\or Apr\or May\or Jun\or
+ Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi
+ \ \number\year}
+
+\def\cutpar{{\parfillskip=0pt\par}}
+
+\def\rhead{Bugs in {\tensl Computers \& Typesetting, 1990}}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\buginvol#1(#2) \par{\bigbreak\penalty-1000\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Volume #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\slMF{{\manual 89:;}\-{\manual <=>:}} % slant the logo
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\newcount\nn
+\newdimen\nsize \newdimen\msize \newdimen\ninept \ninept=9pt
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+
+\tenpoint
+\noindent This is a list of all corrections made to {\sl Computers \&
+Typesetting}, Volumes A,~C, and E\null, between 30 September 1989 (when
+the revisions for \TeX\ Version 3.0 and \MF\ Version 2.0 were made) and
+December 31, 1990.
+Corrections made to the softcover version of {\sl The \TeX book\/} are
+the same as corrections to Volume~A\null. Corrections to the softcover
+version of {\sl The \slMF\kern1ptbook\/} are the same as corrections
+to Volume~C\null. Some of the corrections below have already been made in
+reprintings of the books. Hundreds of changes, too many to list here,
+have been made to Volumes B~and~D because of the upgrades to \TeX\ and
+\MF\null. Readers who need up-to-date information on the \TeX\ and
+\MF\ programs should refer to the |WEB| source files until new
+printings of Volumes B~and~D are issued. \looseness=-1
+
+ % volume A
+
+\bugonpage A99, line 4 from the bottom (2/22/90)
+
+\ninepoint\indent
+to be chosen because there was no feasible
+way to keep total demerits small.
+
+\bugonpage A124, lines 18--21 (9/5/90)
+
+\ninepoint\noindent
+Floating insertions can be accommodated
+as a special case of split insertions, by making each floating topinsert
+start with a small penalty, and by having
+zero as the associated |\floatingpenalty|; non-floating insertions
+like footnotes are accommodated by associating larger penalties with
+split insertions (see Appendix~B).
+
+\bugonpage A137, lines 2 and 3 from the bottom (11/9/90)
+
+{\eightssi
+\rightline{and you shouldn't even be reading this manual,}
+\rightline{which is undoubtedly all English to you.}
+}
+
+\bugonpage A141, line 15 from the bottom (10/18/90)
+
+\tenpoint\noindent
+Thus if you type `|$1\over2$|' (in a text) you get $1\over2$, namely style
+$S$ over style~$S'$;\cutpar
+
+\bugonpage A156, line 2 (11/18/89)
+
+\ninepoint
+Commands like |\mathchardef\alpha="010B| are used in
+Appendix~B to define\cutpar
+
+\bugonpage A165, lines 2--3 (8/13/90)
+
+\ninepoint
+ Type the formula $\bf\bar x^{\rm T}Mx={\rm0}\iff x=0$,
+using as few keystrokes as possible.
+\ (The first `0' is roman, the second is bold. The superscript `T' is roman.)
+
+\bugonpage A171, lines 24--26 (3/13/90)
+
+\ninepoint\noindent
+formula produces a result exactly equivalent to
+`|\left(|\<subformula>|\right)|', when the \<subformula> doesn't end
+with Punct, except that the {delimiters} are forced to
+be of the |\big| size regardless of the height and depth of the subformula.
+
+\bugonpage A193, lines 16--18 (12/2/89)
+
+\ninepoint\noindent
+line if you insert
+`^|\noalign||{|^|\break||}|'
+after the |\cr| for that line. You can prohibit {\sl all\/} breaks
+in an |\eqalignno| if you set ^|\interdisplaylinepenalty||=10000|; or you
+can enclose the whole works in a ^|\vbox|:
+
+\bugonpage A233, bottom 9 lines, and top three on next page (12/2/89)
+
+\danger The |\+| macro in Appendix~B works
+by putting the \<text> for each column that's followed by~|&|
+into an hbox as follows:
+\begindisplay
+|\hbox to |\<column width>|{|\<text>|\hss}|
+\enddisplay
+The ^|\hss| means that the text is normally flush left, and that it can
+extend to the right of its box. Since |\hfill| is ``more infinite'' than
+|\hss| in its ability to stretch, it has the effect of right-justifying or
+centering as stated above. Note that |\hfill| doesn't shrink, but |\hss|
+does; if the text doesn't fit in its column, it will stick out at the right.
+You could cancel the shrinkability of |\hss| by adding ^|\hfilneg|; then
+an oversize text would produce an overfull box.
+You could also center some text by putting `|\hss|' before it and just
+`|&|' after it; in that case the text would be allowed to extend to the
+left and right of its column.
+ The last column of a |\+|~line (i.e., the column entry that is
+followed by |\cr|) is treated differently: The
+\<text> is simply put into an hbox with its natural~width.\looseness=-1
+
+\bugonpage A254, line 5 from the bottom (10/5/89)
+
+\ninepoint\noindent
+|\vsize| hasn't changed, and if all insertions have been
+held in place, the same page break\cutpar
+
+\bugonpage A286, lines 30--32 (3/13/90)
+
+\ninepoint\noindent
+reading and expanding this \cstok{par}
+token, \TeX\ will see the \<vertical command> token again. \ (The current
+meaning of the control sequence ^|\par| will be used; \cstok{par} might no
+longer stand for \TeX's |\par| primitive.)
+
+\bugonpage A290, lines 12--13 (3/24/90)
+
+\ninepoint\noindent
+simply a single Ord atom without subscripts or superscripts,
+or an Acc whose nucleus is an Ord, the
+enclosing braces are effectively removed.
+
+\bugonpage A317, line 17 (5/17/90)
+
+\ninepoint
+|\pretolerance=9999 \tolerance=9999 \parindent=0pt|
+
+\bugonpage A321, lines 16--17 (8/13/90)
+
+\ninepoint\noindent
+\hbox to\parindent{\bf\hss18.6.\enspace}\ignorespaces
+|$\bf\bar x^{\rm T}Mx={\rm0}\iff x=0$|. \ (If you typed a space between
+|\rm| and~|0|, you wasted a keystroke; but don't feel guilty about it.)
+
+\bugonpage A340, nonblank line 11 (3/13/90)
+
+\tenpoint\noindent
+|\topglue 1in % This makes an inch of blank space (1in=2.54cm).|
+
+\bugonpage A342, line 6 (3/13/90)
+
+\tenpoint\noindent
+|\topglue| but not |\hglue|. It does not
+illustrate |\raggedright| setting of para-\cutpar
+
+\bugonpage A346, lines 20--21 (12/3/89)
+
+\ninepoint\noindent
+streams used by ^|\read| and ^|\write|, to math
+^{families} used by ^|\fam|, to sets of hyphenation rules used by
+^|\language|, and to insertions (which require
+^|\box|, ^|\count|, ^|\dimen|, and ^|\skip| registers all having the
+same number).
+
+\bugonpage A346, line 20 from the bottom (12/3/89)
+
+\ninepoint\noindent
+manent value. These macros use registers
+|\count10| through |\count20| to hold the\cutpar
+
+\bugonpage A346, lines 8--13 from the bottom (12/3/89)
+
+\ninepoint\noindent
+number was allocated. The inside story of how allocation is actually performed
+should be irrelevant when the allocation macros are used at a higher level;
+you mustn't assume that |plain.tex| really does allocation in any
+particular way.
+\beginlines
+|\count10=22 % this counter allocates \count registers 23, 24, 25, ...|
+\endgroup
+
+\bugonpage A347, lines 2--5 (12/3/89)
+
+\ninepoint{\parindent=0pt
+|\count19=0 % this counter allocates language codes 1, 2, 3, ...|\parbreak
+|\count20=255 % this counter allocates insertions 254, 253, 252, ...|\parbreak
+|\countdef\insc@unt=20 % nickname for the insertion counter|\parbreak
+|\countdef\allocationnumber=21 % the most recent allocation|\parbreak
+|\countdef|^|\m@ne||=22 \m@ne=-1 % a handy constant|\par}
+
+\bugonpage A347, new line after former line 17 (12/3/89)
+
+\ninepoint\noindent
+|\outer\def|^|\newlanguage||{\alloc@9\language\chardef\@cclvi}|
+
+\bugonpage A352, new line before line 6 from the bottom (3/13/90)
+
+\ninepoint\noindent
+|\def\topglue{\nointerlineskip \vglue-\topskip \vglue} % for top of page|
+
+\bugonpage A355, line 8 from the bottom (12/3/89)
+
+\ninepoint\noindent
+| \noindent{\bf#1.\enspace}{\sl#2\par}%|
+
+\bugonpage A363, lines 8--9 from the bottom (12/8/89)
+
+{\ninepoint\parindent=0pt
+| \if@mid \dimen@=\ht0 \advance\dimen@ by\dp\z@ |%
+ |\advance\dimen@ by12\p@|\parbreak%
+| \advance\dimen@ by\pagetotal \advance\dimen@ by-\pageshrink|
+\par}
+
+\bugonpage A375, line 27 (10/30/89)
+
+\ninepoint\noindent
+depending on whether or not |\t|~contains
+an asterisk. \ (Do you see why?) \ And here's\cutpar
+
+\bugonpage A393, lines 3--5 from the bottom (12/3/89)
+
+\ninepoint
+|\hskip-.17em plus-3em minus.11em|\par
+|\vadjust{}\penalty10000|\par
+|\leaders\copy\dbox\hskip3.3\wd\dbox plus1fil minus.3\wd\dbox|
+
+\bugonpage A444, line 4 (3/13/90)
+
+\ninepoint\noindent
+Shift box~$x$ down by ${1\over2}\bigl(h(x)-d(x)\bigr)
+-a$, where $a=\sigma_{22}$, so that the operator character\cutpar
+
+\bugonpage A450, line 8 (12/3/89)
+
+\def\\#1{$_{\kern\scriptspace#1}$}
+\ninepoint\indent
+{\qquad\tt\\0h\\0e\\0n\\5a\\0t\\0 \\1n\\0a\\0 \\0n\\2a\\0t\\0
+ \\1t\\0i\\0o\\0 \\2i\\0o\\0 \\0o\\2n\\0}
+
+\bugonpage A450, line 14 (12/3/89)
+
+\ninepoint
+{\tt.\\0h\\0y$_3$p\\0h\\0e\\2n\\5a\\4t\\2i\\0o\\2n\\0.}
+
+\bugonpage A450, lines 19 and 20 (12/3/89)
+
+\ninepoint\vskip-10pt
+\begindisplay
+\tt\\0o\\2n\\0
+\\0o\\0n\\1c\\0
+\\1c\\0a\\0
+\\1n\\0a\\0
+\\0n\\2a\\0t\\0
+\\1t\\0i\\0o\\0
+\\2i\\0o\\0
+\\0o\\2n\\0
+\enddisplay
+and this yields `{\tt\\0c\\0o\\2n\\1c\\0a\\0t\\0e\\1n\\2a\\1t\\2i\\0o\\2n\\0}',
+i.e., `|con-cate-na-tion|'.
+
+\bugonpage A455, last lines before the quotes (11/30/89)
+
+\ninepoint\noindent
+sit yourself (even in restricted horizontal mode) by saying
+^|\setlanguage|\<number>;\break
+ this changes the current language but it
+does not change |\language|. Each what-\break
+sit records the current
+|\lefthyphenmin| and |\righthyphenmin|.
+
+\bugonpage A467, right column (12/3/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\hfilneg|, 72, 100, 233, 283, 285, 290, 397.
+
+\bugonpage A468, right column (12/2/89)
+
+\eightpoint
+|\interdisplaylinepenalty|, {\it193}, 349, 362.
+
+\bugonpage A469, left column (12/3/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\language| (hyphenation method), 273, 346, $\underline{455}$.
+
+\bugonpage A469, right column (10/30/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\lefthyphenmin|, 273, {\it364}, $\underline{454}$, 455.
+
+\bugonpage A472, left column (12/3/89)
+
+\eightpoint
+|\newlanguage|, 346, $\underline{347}$.
+
+\bugonpage A476, left column (10/30/89)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+|\righthyphenmin|, 273, {\it364}, $\underline{454}$, 455.
+
+\bugonpage A479, new entry (3/13/90)
+
+\eightpoint
+|\topglue|, {\it340}, $\underline{352}$.
+
+\bugonpage A480, right column (3/13/90)
+
+\eightpoint
+|\vglue|, $\underline{352}$, {\it408}.
+
+\bugonpage A483, the Providence lines (10/8/89)
+
+\noindent[Change the first one to
+\begintt
+Providence RI 02940\kern.05em-9506, USA.
+\endtt
+Then the second one will be
+\begindisplay
+Providence RI 02940\kern.05em-9506, USA.
+\enddisplay
+The second line will also appear on page C361.]
+
+ % volume B
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+
+ % volume C
+\hsize=29pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+
+\bugonpage C11, replacement for second quotation at bottom of page (9/27/90)
+
+\begingroup
+ \eightpoint \let\tt=\ninett
+ \baselineskip 10pt
+ \parfillskip \z@
+ \interlinepenalty 10000
+ \leftskip \z@ plus 40pc minus \parindent
+ \let\rm=\eightss \let\sl=\eightssi
+ \everypar{\sl}
+ \def\par{\ifhmode\/\endgraf\fi}\obeylines
+To anyone who has lived in a modern American city (except Boston)
+at least one of the underlying ideas of ^{Descartes}' analytic geometry
+will seem ridiculously evident. Yet, as remarked,
+it took mathematicians all of two thousand years
+to arrive at this simple thing.
+\author ERIC TEMPLE ^{BELL}, {\sl Mathematics: Queen and Servant of %
+ Science\/} (1951) % p123
+
+\endgroup
+
+\bugonpage C220, top line (3/13/90)
+
+\ninepoint\noindent
+modes you get into by hitting
+`|S|', `|R|', or `|Q|', respectively, in response to error messages\cutpar
+
+\bugonpage C252, line 16 (3/13/90)
+
+\ninepoint\indent
+| for i:=1 upto n_windows: display blankpicture inwindow i; endfor|
+
+\bugonpage C262, lines 19--21 (11/9/90)
+
+\ninepoint\noindent
+for commonly occurring idioms.
+For example, `{\bf stop} |"hello"|' displays `|hello|' on the terminal and waits
+until \<return> is typed.
+\beginlines
+|def |^|upto|| = step 1 until enddef; def |^|downto|| = step -1 until enddef;|
+\endgroup
+
+\bugonpage C264, lines 4--6 from the bottom (3/24/90)
+
+\ninepoint\noindent
+|vardef |^|counterclockwise|| primary c =|\par\noindent
+| if turningcheck>0:|\par\noindent
+| interim |^|autorounding||:=0;|\par\noindent
+| if |^|turningnumber|| c <= 0: reverse fi fi c enddef;|
+
+\bugonpage C306, line 6 (3/13/90)
+
+\ninepoint\noindent
+| ligtable "'": "'" =: oct"042", % close quotes|
+
+\bugonpage C309, second line from bottom (11/18/89)
+
+\ninepoint\noindent
+| define_whole_vertical_blacker_pixels(vair,slab,| $\cdots$ |);|
+
+\bugonpage C315, line 9 from the bottom (1/2/90)
+
+\ninepoint\noindent
+units of printer's points):
+
+\bugonpage C329, line 25 (12/29/90)
+
+\ninepoint\noindent
+which can be used to specify a nonstandard file area
+or directory name for the gray\cutpar
+
+\bugonpage C337, line 4 from the bottom (1/7/90)
+
+\ninepoint\noindent
+|\def\startfont{\font\testfont=\fontname \spaceskip=0pt|
+
+\bugonpage C347, left column (9/27/90)
+
+\eightpoint\noindent
+Bell, Eric Temple, 11.
+
+\bugonpage C349, left column (9/27/90)
+
+\eightpoint\noindent
+Descartes, Ren\'e, 6, 11, 19.
+
+\bugonpage C356, right column (9/27/90)
+
+\eightpoint\noindent
+[remove the entry for Rex Stout.]
+
+\bugonpage C358, right column (9/27/90)
+
+\eightpoint\noindent
+[remove the entry for Nero Wolfe.]
+
+ % Volume D
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+ % volume E
+\hsize=29pc
+\def\dashto{\mathrel{\hbox{-\kern-.05em}\mkern3.9mu\hbox{-\kern-.05em}}}
+
+\bugonpage Exiii, replacement for last four lines (4/30/90)
+
+\textindent{\bull}``AMS Euler---A new typeface for mathematics''
+ by Donald~E. Knuth
+and Hermann Zapf, {\sl Scholarly Publishing\/ \bf21} (1989), 131--157.
+\ {\it The story of a design project that helps bridge the gulf between
+mathematics and art.}
+
+\smallskip
+\textindent{\bull}``Meta-Marks:
+ Preliminary studies for a Pandora's Box of shapes''
+by Neenie Billawala, Stanford Computer Science report 1259 (Stanford,
+California, July 1989), 132~pp.
+\ {\it Lavishly illus\-trated studies in parameter variation,
+leading to the design of a new typeface called Pandora.}
+
+
+
+\bugonpage E325, line 13 (3/13/90)
+
+\ninepoint\noindent
+{\bf if} \\{serifs}: $x_{3r}=\max(x_{1r},{\rm hround}(x_1+.5\\{dot\_diam}
+ -.2\\{jut})-.5\\{tiny})$\par\noindent
+{\bf else}: $x_3=x_1-.5$ {\bf fi};
+
+\bugonpage E483, line 4 (3/13/90)
+
+\ninepoint
+\rightline{\% Character codes \oct{000}--\oct{100} and \oct{133}--\oct{177}
+ are generated.}
+
+\bugonpage E544, line 5 (3/13/90)
+
+\ninepoint\noindent
+\hskip 3em\vdots\hskip2em\raise2pt\hbox{(the rest of the
+ program for `$\gamma$' in |greekl| comes here)}
+
+\bugonpage E557, line 9 (3/13/90)
+
+\ninepoint
+\rightline{\sl`Nevermore---Ah nevermore.'\thinspace''}
+
+\bugonpage E558, line 21 (3/13/90)
+
+\eightpoint\noindent
+|Clasp a rare and radiant maiden whom the angels name Lenore."|
+
+\bugonpage E570, lines 27--28 look better with proper skewchars (3/13/90)
+
+\begingroup
+\tenpoint\bf
+\textfont0=\tenbf \scriptfont0=\sevenbf
+\font\boldi=cmmib10 \font\boldsy=cmbsy10
+\skewchar\boldi='177 \skewchar\boldsy='60
+\textfont1=\boldi
+\textfont2=\boldsy
+\noindent Here's some bold 10-point math:
+${\hat A}^\Gamma_0
++{\check B}^\Delta_1
+-{\tilde C}^\Theta_2
+\times{\acute D}^\Lambda_3
+/{\grave E}^\Xi_4
+\oplus{\dot F}^\Pi_5
+\ominus{\ddot G}^\Sigma_6
+\otimes{\breve H}^\Phi_7
+\oslash{\bar I}^\Psi_8
+\odot{\vec J}^{\,\Omega}_9$.
+\endgroup
+
+ \bye
+
diff --git a/systems/knuth/dist/errata/errata.ten b/systems/knuth/dist/errata/errata.ten
new file mode 100644
index 0000000000..9efa2a3e50
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.ten
@@ -0,0 +1,1289 @@
+% Bugs (sigh) in Computers \& Typesetting
+
+\input manmac
+\def\curl{\mathop{\rm curl}}
+\def\cycle{{\rm cycle}}\indent
+\def\dashto{\mathrel{\hbox{-\thinspace-\kern-.05em}}}
+\font\sltt=cmsltt10
+\font\niness=cmss9
+\font\ninessi=cmssi9
+\proofmodefalse
+\raggedbottom
+\output{\hsize=29pc \onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\today{\number\day\
+ \ifcase\month\or
+ Jan\or Feb\or Mar\or Apr\or May\or Jun\or
+ Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi
+ \ \number\year}
+
+\def\cutpar{{\parfillskip=0pt\par}}
+
+\def\rhead{Bugs in {\tensl Computers \& Typesetting, 2000}}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\buginvol#1(#2) \par{\bigbreak\penalty-1000\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Volume #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\slMF{{\manual 89:;}\-{\manual <=>:}} % slant the logo
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\newcount\nn
+\newdimen\nsize \newdimen\msize \newdimen\ninept \ninept=9pt
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+
+\tenpoint
+\noindent This is a list of all substantial corrections made to {\sl Computers
+\& Typesetting\/} from the mid-1990s until the first ``Millennium edition'' was
+published at the end of the year 2000.
+Corrections made to the softcover version of {\sl The \TeX book\/} are
+the same as corrections to Volume~A\null. Corrections to the softcover
+version of {\sl The \slMF\kern1ptbook\/} are the same as corrections
+to Volume~C\null. Changes to the mini-indexes
+and master indexes of Volumes B, D, and~E are not shown here unless they are
+not obviously derivable from what has been shown.
+\looseness=-1
+
+ % volume A
+
+\bugonpage A3, line 14 {(in certain printings only)} (9/6/00)
+
+\tenpoint\noindent
+that
+looks like {\tt\char'15} or {\tt\char'23}.
+
+\bugonpage A8, lines 14 and 15 (9/6/00)
+
+\tenpoint\noindent
+that is not to be ignored. Notice that |\|\] is a control
+sequence of the second kind, namely a control symbol, since there is a
+single nonletter (\]) following\cutpar
+
+\bugonpage A43, line $-17$ (8/4/98)
+
+\tenpoint\noindent
+into your manuscript, if the |b|-key on your keyboard is broken. \
+(An optional\cutpar
+
+\bugonpage A88, lines 14, 16, 18, and 21 (8/12/00)
+
+\tenpoint\noindent
+[Insert two blank spaces between `{\tt blank space}' and `{\tt\char`\}}']
+
+\bugonpage A96, lines 9 and 10 (8/6/98)
+
+\ninepoint\indent
+Before 1998, some ^{German} words changed their spelling
+when split between lines. For example, `backen' became `bak-ken'
+and `Bettuch' sometimes became `Bett-\cutpar
+
+\bugonpage A107, line 2 (8/5/98)
+
+\ninepoint
+\line{\indent\spaceskip=.4em minus.35em
+ually, you might be tempted to set
+|\tolerance=10000|; this allows arbitrarily bad}
+
+\bugonpage A115, line $-19$ (8/5/98)
+
+\ninepoint\noindent
+If there's no room for such an insertion on this page, \TeX\
+will insert it at the top of\cutpar
+
+\bugonpage A119, line 15 (8/5/98)
+
+\ninepoint\noindent
+of\/ |\dimen3|, assuming that |\dimen3| is positive.
+
+\bugonpage A182, middle line of the displayed commutative diagram (12/3/99)
+
+\ninepoint
+\def\mapright#1{\smash{
+ \mathop{\longrightarrow}\limits^{#1}}}
+$$\matrix{
+ 0&\mapright{}&{\cal O}_C&\mapright\pi&
+ \pi_*{\cal O}_D&\mapright\delta&
+ R^1f_*{\cal O}_V(-D)&\mapright{}&0\cr}$$
+
+\bugonpage A233, line $-2$ (8/5/98)
+
+\ninepoint\noindent
+could avoid this by adding |\hskip| |0pt| |minus-1fil|; then
+an oversize text would\cutpar
+
+\bugonpage A277, line 1 (8/5/98)
+
+\ninepoint\indent
+\<code assignment>\is\<codename>\<8-bit number>\<equals>\<number>
+
+\bugonpage A277, line $-11$ (8/5/98)
+
+\ninepoint\noindent
+[Move this line, which defines \<at clause>, up to the top of the page.]
+
+\bugonpage A289, line 24 (2/3/97)
+
+\ninepoint\indent
+\<math field>\is\<filler>\<math symbol>\alt\<filler>|{|\<math mode material>|}|
+
+\bugonpage A309, line 3 (8/12/97)
+
+\ninepoint
+\ansno8.4:
+|$|$_{3}$ |x|$_{11}$ |^|$_7$ |2|$_{12}$ |$|$_{3}$ |~|$_{13}$ \]$_{10}$
+\cstok{TeX} |b|$_{11}$ |v|$_{11}$ \]$_{10}$. The final space comes from
+the\cutpar
+
+\bugonpage A313, line 24 (9/19/00)
+
+\ninepoint\noindent
+stands for `|\par\vfill...|', so the next three commands are
+
+\bugonpage A313, line 27 (9/19/00)
+
+\ninepoint\indent
+|{vertical mode: \par}|
+
+\bugonpage A318, lines 12 and 13 (8/5/98)
+
+\ninepoint\noindent\hbox to\parindent{\hfil\bf15.8.\enspace}%
+|\advance\dimen2 by\ifnum\dimen2<0 -\fi.5\dimen3|\parbreak
+|\divide\dimen2 by\dimen3 \multiply\dimen2 by\dimen3|
+
+\bugonpage A325, line 22 (12/3/99)
+
+\ninepoint
+| 0&\mapright{}&{\cal O}_C&\mapright\pi&|
+
+\bugonpage A337, line 3 from the bottom (9/6/00)
+
+\rightline{\eightss DONALD E. KNUTH, {\eightssi The \TeX book\/} (1984)}
+
+\bugonpage A348, lines 14--16 (8/6/98)
+
+\ninepoint\noindent
+| \def\@if#1{true}{\let#1=\iftrue}%|\par\noindent
+| \expandafter\expandafter\expandafter|\par\noindent
+| \def\@if#1{false}{\let#1=\iffalse}%|\par\noindent
+
+\bugonpage A356, line 21 (8/6/98)
+
+\ninepoint\noindent
+|\def\AA{\leavevmode\setbox0=\hbox{!}\dimen@=\ht0 \advance\dimen@ by-1ex|
+
+\bugonpage A356, lines 9--21 from the bottom (8/6/98)
+
+\ninepoint
+{\parindent=0pt
+|\def\S{\mathhexbox278} \def\P{\mathhexbox27B} \def\Orb{\mathhexbox20D}|\par
+\smallskip
+|\def\oalign#1{\leavevmode\vtop{\baselineskip0pt \lineskip.25ex|\par
+| \ialign{##\crcr#1\crcr}}} \def\o@lign{\lineskiplimit=0pt \oalign}|\par
+|\def\ooalign{\lineskiplimit=-\maxdimen \oalign} % chars over each other|\par
+|{\catcode`p=12 \catcode`t=12 \gdef\\#1pt{#1}} \let\getf@ctor=\\|\par
+|\def\sh@ft#1{\dimen@=#1 \kern\expandafter\getf@ctor\the\fontdimen1\font|\par
+| \dimen@} % kern by #1 times the current slant|\par
+|\def\d#1{{\o@lign{\relax#1\crcr\hidewidth\sh@ft{-1ex}.\hidewidth}}}|\par
+|\def\b#1{{\o@lign{\relax#1\crcr\hidewidth\sh@ft{-3ex}%|\par
+| \vbox to.2ex{\hbox{\char'26}\vss}\hidewidth}}}|\par
+|\def\c#1{{\setbox0=\hbox{#1}\ifdim\ht0=1ex \accent'30 #1%|\par
+| \else\ooalign{\unhbox0\crcr\hidewidth\char'30\hidewidth}\fi}}|\par
+|\def\copyright{{\ooalign{\hfil\raise.07ex\hbox{c}\hfil\crcr\Orb}}}|\par
+}
+
+\bugonpage A364, line 9 (8/9/98)
+
+\ninepoint\noindent
+|\def\makefootline{\baselineskip=24pt \lineskiplimit=0pt|\par\noindent
+| \line{\the\footline}}|
+
+
+\bugonpage A364, line 4 from the bottom (8/6/98)
+
+\ninepoint\noindent
+|\def\fmtversion{3.1415926} % identifies the current format|
+
+\bugonpage A447, bottom line (6/3/98)
+
+\rightline{\eightss--- JOHN SMITH, {\eightssi The Printer's Grammar\/}\enspace
+ (1755)}
+
+\bugonpage A450, lines 11--13 (4/12/98)
+
+\def\\#1{$_{\kern\scriptspace#1}$}%
+\tenpoint\noindent
+between `|e|' and `|n|' there are five relevant values
+in this case (2~from {\tt\\0h\\0e\\2n\\0},
+0~from {\tt\\0h\\0e\\0n\\0a\\4},
+0~from {\tt\\0h\\0e\\0n\\5a\\0t\\0},
+1~from {\tt\\1n\\0a\\0},
+and 0~from {\tt\\0n\\2a\\0t\\0}); the maximum of these is~2.
+The result of all the maximizations is
+
+\bugonpage A453, line 6 (8/5/98)
+
+\ninepoint\noindent
+tion dictionary, except that plain \TeX\ blocks hyphens after the
+very first letter or be-\cutpar
+
+\bugonpage A458, left column (9/6/00)
+
+\eightpoint
+\leavevmode{\tt\rlap<\char`\_}, 45, 135, 368--369; {\sl see also\/} |\le|.\par
+\leavevmode{\tt\rlap/=}, 45, 135, 368--369; {\sl see also\/} |\ne|.\par
+\leavevmode{\tt\rlap>\char`\_}, 45, 135, 368--369; {\sl see also\/} |\ge|.
+
+\bugonpage A458, right column (7/5/99)
+
+\eightpoint
+{\tt\char'13} and {\tt\char'14}, 135, 343, 368--369, 429;\par
+al-Khw\^arizm\^\i, abu `Abd All\^ah Mu\d{h}ammad ibn M\^us\^a, 53.
+
+\bugonpage A464, right column (8/6/98)
+
+\eightpoint
+\newbox\astbox \setbox\astbox=\hbox to0pt{\hss\lower1pt\hbox{*}}
+\def\prim#1{\par\indent\copy\astbox{\tt\char`\\#1}}
+\prim{edef}, {\it215--216}, 275, {\it328}, {\it373--374}.
+
+\bugonpage A466, right column (8/8/98)
+
+\eightpoint
+|\getfactor|, {\it356}, $\underline{375}$, {\it398}.
+
+\bugonpage A467, right column (8/5/98)
+
+\eightpoint
+\prim{hfilneg}, 72, 100, 283, 285, 290, 397.
+
+\bugonpage A469, left column (8/5/98)
+
+\eightpoint
+italic type, 13--14, 100, 127, 165, 409, 428, 430.
+
+\bugonpage A469--A477, passim (5/13/98)
+
+\eightpoint\noindent
+Add page 272 to the index entries for |\lastskip|, |\pagedepth|,
+|\pagefilllstretch|,\hfil\break |\pagefillstretch|, |\pagefilstretch|,
+|\pagegoal|, |\pageshrink|, |\pagestretch|, |\pagetotal|,\hfil\break
+|\parshape|, |\prevdepth|, and |\spacefactor|.\par
+\smallskip\noindent Also change `369' to `370' in the index entries for
+|\lbrack|, |\lq|, |\rbrack|, |\rq|, |\sb|, and |\sp|.\par
+\smallskip\noindent Also change `Luckombe, Philip' to `Smith, John'.\par
+
+\bugonpage A472, right column (8/6/98)
+
+\eightpoint
+\prim{noexpand}, $\underline{209}$, $\underline{213}$, 215, 216,
+ {\it377}, {\it424}.
+
+\bugonpage A473, left column (8/6/98)
+
+\eightpoint
+|\Orb| ( \Orb\ ), $\underline{356}$.
+
+ % volume B
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage Bix, line 16 (1/16/00)
+
+\tenpoint\textindent{\bull}``Word hy-phen-a-tion by com-put-er''
+by Franklin Mark Liang, Stan-\cutpar
+
+\bugonpage Bxiv, line 13 (4/19/96)
+
+\tenpoint\noindent preprocessor converts these into numeric constants that are
+256 or more. This\cutpar
+
+\bugonpage Bxiv, line $-1$ (4/19/96)
+
+\ninepoint\noindent This file contains one line per string, starting with
+string number 256, then number 257,\cutpar
+
+\bugonpage Bxv, lines 10 and 11 (4/19/96)
+
+\ninepoint\noindent
+In this case, occurrences of |""| in the |WEB| program will be replaced by
+256; occurrences of |"This longer string"| will be replaced by 257.
+The symbol |@$| stands for the numeric\cutpar
+
+\hsize=35pc
+\bugonpage B2, line $-10$ (3/8/95)
+
+\def\RQ/{{\char'23}} % right quote in a string
+\ninepoint\noindent\quad
+{\bf define} $\\{banner}\equiv\hbox{\tt\RQ/This\]is\]TeX,\]%
+ Version\]3.14159\RQ/}$\quad$\{\,$printed when \TeX\ starts$\,\}$
+
+\bugonpage B169, line 13 (9/22/95)
+
+\tenpoint\noindent
+something in a ``muskip'' register, or to one of the
+three parameters \hbox{\tt\char`\\thinmuskip},
+\hbox{\tt\char`\\medmuskip},\cutpar
+
+\bugonpage B221, line 9 (3/4/95)
+
+\ninepoint\noindent
+\quad{\bf define} $\\{non\_address}=0$\quad
+$\{\,$a spurious \\{bchar\_label}$\,\}$
+
+\bugonpage B221, line 17 (3/4/95)
+
+\ninepoint\noindent
+\\{font\_params}: {\bf array}[\\{internal\_font\_number}] {\bf of}
+ \\{font\_index};\quad$\{\,$how many font parameters are present$\,\}$
+
+\bugonpage B256, insert new line 12 before the bottom (3/7/95)
+
+\ninepoint\noindent\qquad
+\\{glue\_temp}: \\{real};\quad $\{\,$glue value before rounding$\,\}$
+
+\bugonpage B258, line 11 before the bottom becomes four lines (3/7/95)
+
+\ninepoint\noindent
+{\tenbf 625.}\quad{\bf define} $\\{billion}\equiv
+ \\{float\_constant}(1000000000)$\par\noindent
+{\bf define} $\\{vet\_glue}(\hbox{\tt\char`\#})\equiv
+ \\{glue\_temp}\gets\hbox{\tt\char`\#}$;\par\noindent
+\qquad{\bf if\/} $\\{glue\_temp}>\\{billion}$
+ {\bf then} $\\{glue\_temp}\gets\\{billion}$\par\noindent
+\qquad{\bf else if\/} $\\{glue\_temp}<-\\{billion}$
+ {\bf then} $\\{glue\_temp}\gets-\\{billion}$\smallskip\noindent
+$\langle\,$Move right or output leaders{\sevenrm\kern.5em625}$\,\rangle\equiv$
+
+\bugonpage B258, lines 3--6 from the bottom (3/7/95)
+
+\ninepoint\noindent
+\qquad\qquad{\bf begin} $\\{vet\_glue}(\\{float}(\\{glue\_set}
+ (\\{this\_box}))*\\{stretch}(g))$;\par\noindent
+\qquad\qquad$\\{rule\_wd}\gets\\{rule\_wd}+
+ \\{round}(\\{glue\_temp})$;\par\noindent
+\qquad\qquad{\bf end};\par\noindent
+\qquad\quad{\bf end}\par\noindent
+\qquad{\bf else if\/} $\\{shrink\_order}(g)=\\{g\_order}$
+ {\bf then}\par\noindent
+\qquad\quad{\bf begin} $\\{vet\_glue}(\\{float}(\\{glue\_set}
+ (\\{this\_box}))*\\{shrink}(g))$;\par\noindent
+\qquad\quad$\\{rule\_wd}\gets\\{rule\_wd}-\\{round}(\\{glue\_temp})$;
+
+\bugonpage B260, line 13 from the bottom (6/26/93)
+
+\ninepoint
+\noindent\hskip10pt
+$\\{doing\_leaders}\gets\\{outer\_doing\_leaders}$; \
+$\\{dvi\_v}\gets\\{save\_v}$; \
+$\\{dvi\_h}\gets\\{save\_h}$; \
+$\\{cur\_v}\gets\\{base\_line}$;
+
+\bugonpage B261, insert new line after line 7 (3/7/95)
+
+\ninepoint\noindent\qquad
+\\{glue\_temp}: \\{real};\quad $\{\,$glue value before rounding$\,\}$
+
+\bugonpage B262, lines 3--6 from the bottom (3/7/95)
+
+\ninepoint\noindent
+\qquad\qquad{\bf begin} $\\{vet\_glue}(\\{float}(\\{glue\_set}
+ (\\{this\_box}))*\\{stretch}(g))$;\par\noindent
+\qquad\qquad$\\{rule\_ht}\gets\\{rule\_ht}+
+ \\{round}(\\{glue\_temp})$;\par\noindent
+\qquad\qquad{\bf end};\par\noindent
+\qquad\quad{\bf end}\par\noindent
+\qquad{\bf else if\/} $\\{shrink\_order}(g)=\\{g\_order}$
+ {\bf then}\par\noindent
+\qquad\quad{\bf begin} $\\{vet\_glue}(\\{float}(\\{glue\_set}
+ (\\{this\_box}))*\\{shrink}(g))$;\par\noindent
+\qquad\quad$\\{rule\_ht}\gets\\{rule\_ht}-\\{round}(\\{glue\_temp})$;
+
+\bugonpage B264, line 22 (6/26/93)
+
+\ninepoint
+\noindent\hskip10pt
+$\\{doing\_leaders}\gets\\{outer\_doing\_leaders}$; \
+$\\{dvi\_v}\gets\\{save\_v}$; \
+$\\{dvi\_h}\gets\\{save\_h}$; \
+$\\{cur\_h}\gets\\{left\_edge}$;
+
+\bugonpage B297, line 11 (3/7/95)
+
+\ninepoint\noindent
+\qquad $\\{width}(p)\gets\\{mu\_mult}(\\{width}(p))$; \
+ $\\{subtype}(p)\gets\\{explicit}$;
+
+\bugonpage B309, line 7 (9/22/95)
+
+\ninepoint\noindent
+\qquad {\bf if} $\\{cur\_style}<\\{text\_style}$ {\bf then}
+ \quad$\{\,$display style$\,\}$
+
+\bugonpage B356, line $-5$ (3/4/95)
+
+\tenpoint\noindent
+$\\{hang\_after}=1$, and $\\{hang\_indent}=0$.
+Note that if $\\{hang\_indent}=0$, the value of $\\{hang\_after}$ is\cutpar
+
+\bugonpage B388, bottom line (3/4/95)
+
+\ninepoint\noindent
+\quad {\bf if\/} $\\{bchar\_label}[\\{hf}]\ne\\{non\_address}$ {\bf then}\quad
+ $\{\,$put left boundary at beginning of new line$\,\}$
+
+\bugonpage B406, line 10 (5/1/98)
+
+\ninepoint\noindent
+\qquad$q\gets p$;\quad$\{\,$now node $q$ represents $p_1\ldots p_{l-1}\,\}$
+
+\bugonpage B503, line 12 (3/4/95)
+
+\tenpoint\noindent
+of the following procedure. (Exception: The tabskip
+glue isn't trapped while preambles are being scanned.)
+
+\bugonpage B529, line 12 (3/4/95)
+
+\ninepoint\noindent\quad
+\\{undump}(0)($\\{fmem\_ptr}-1$)(\\{bchar\_label}[$k$]);\par\noindent\quad
+\\{undump}(\\{min\_quarterword})(\\{non\_char})(\\{font\_bchar}[$k$]);
+
+\bugonpage B531, line 2 (11/23/98)
+
+\tenpoint\noindent
+from appearing again.
+
+\bugonpage B531, line 14 (11/23/98)
+
+\ninepoint\noindent
+\quad \\{print\_int}(\\{year}); \ \\{print\_char}(|"."|); \
+ \\{print\_int}(\\{month}); \ \\{print\_char}(|"."|); \
+ \\{print\_int}(\\{day});
+
+\bugonpage B534, insert new material between lines $-16$ and $-15$ (3/20/95)
+
+\ninepoint\noindent
+\quad {\bf while} $\\{input\_ptr}>0$ {\bf do}\par\noindent
+\qquad {\bf if\/} $\\{state}=\\{token\_list}$ {\bf then} \\{end\_token\_list}
+ {\bf else} \\{end\_file\_reading};
+
+\bugonpage B534, line $-2$ (3/20/95)
+
+\ninepoint\noindent
+\qquad $\\{temp\_ptr}\gets\\{cond\_ptr}$; \
+ $\\{cond\_ptr}\gets\\{link}(\\{cond\_ptr})$; \
+ $\\{free\_node}(\\{temp\_ptr},\\{if\_node\_size})$;
+
+\bugonpage B535, line 9 (3/20/95)
+
+\ninepoint\noindent
+\qquad {\bf begin init for} $c\gets\\{top\_mark\_code}$ {\bf to}
+ \\{split\_bot\_mark\_code} {\bf do}\par\noindent
+\qquad\qquad {\bf if\/} $\\{cur\_mark}[c]\ne\\{null}$ {\bf then}
+ $\\{delete\_token\_ref}(\\{cur\_mark}[c])$;\par\noindent
+\qquad\quad \\{store\_fmt\_file}; \ {\bf return}; \ {\bf tini}
+
+\bugonpage B581, Zabala entry (8/19/00)
+
+\eightpoint\noindent
+Zabala Salelles, Ignacio Andr\'es:\quad 2.
+
+ % volume C
+\hsize=29pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+
+\bugonpage C17, lines 12 and 13 (9/6/00)
+
+\tenpoint
+{\bf draw} $z_4\{\curl0\}\to z_2\{z_3-z_4\}\to\{\curl0\}\,z_3$;\par
+{\bf draw} $z_4\{\curl2\}\to z_2\{z_3-z_4\}\to\{\curl2\}\,z_3$
+
+\bugonpage C23, line $-7$ (8/5/98)
+
+\ninepoint\indent
+$x_1=ss=w-x_5$;\quad$y_3-y_1=\\{ygap}$
+
+\bugonpage C69, line 17 (9/6/00)
+
+\ninepoint\noindent
+|"abra"|, while
+$p_1$ is `$(0,0)\to(3,3)$' and $p_2$ is `$(0,0)\to(3,3)\to\cycle$'.
+
+\bugonpage C94, line $-11$ (3/4/95)
+
+\ninepoint\noindent
+put
+are assumed to have square pixels. But if, for
+example, the {\bf mode\_def} sets\cutpar
+
+\bugonpage C107, line 15 (3/4/95)
+
+\ninepoint
+{\bf labels}$(1a,1b,2a,2b,3a,3b,4a,4b,\,${\bf range} 1 {\bf thru} 36);
+ \ {\bf endchar\/};
+
+\bugonpage C123, lines 21 and 22 (12/19/95)
+
+\chardef\circc=34 % rotated quartercircle
+\def\chapno{ 14} \exno=2 % for exercise 14.3!
+\dangerexercise
+Use a {\sl rotated\/} quarter-circle to produce `{\manual\circc}\kern1pt'
+in font position `{\tt c}'.
+
+\bugonpage C129, lines 6--17 (8/5/98)
+
+\ninepoint
+\beginsyntax
+<path primary>\is<pair primary>\alt<path variable>
+ \alt[(]<path expression>[)]
+ \alt[reverse]<path primary>
+ \alt[subpath]<pair expression>[of]<path primary>
+<path secondary>\is<pair secondary>\alt<path primary>
+ \alt<path secondary><transformer>
+<path tertiary>\is<pair tertiary>\alt<path secondary>
+<path expression>\is<pair expression>\alt<path tertiary>
+ \alt<path subexpression><direction specifier>
+ \alt<path subexpression><path join>[cycle]
+<path subexpression>\is<path expression>
+ \alt<path subexpression><path join><path tertiary>
+\endsyntax
+
+\bugonpage C134, line 8 (3/4/95)
+
+\ninepoint\noindent
+of~$p$; if $t\le 0$, precontrol~$t$ of~$p$ is~$z_0$.
+In particular, if $t$ is an integer, postcontrol~$t$ of~$p$\cutpar
+
+\bugonpage C139, illustration (8/5/98)
+
+\eightpoint\noindent
+[Remove the labels {\tt 2r}, {\tt 2}, and {\tt 2l} below their dots.]
+
+\bugonpage C143, top two lines (3/4/95)
+
+\danger In order to have some transform variables to work with, it's necessary
+to `^{hide}' some declarations and commands before giving the next |expr|s:
+
+\bugonpage C147, lines 14, 16, and 19 (9/6/00)
+
+\noindent
+[Change `{\bf savepen}' to `{\it savepen}'.]
+
+\bugonpage C147, line 2 from the bottom (9/6/00)
+
+\ninepoint\noindent
+{\manual lmnj}'s {\bf penrazor} stands for
+`{\bf makepen} $\bigl((-.5,0)\dashto(.5,0)\dashto \cycle\bigr)$',
+and {\bf pensquare}\cutpar
+
+\bugonpage C171, line 19 (8/5/98)
+
+\ninepoint\noindent
+(\<path tertiary>) and
+(\<pair tertiary>). A pair expression is not considered to\cutpar
+
+\bugonpage C172, line 14 (8/5/98)
+
+\tenpoint\noindent
+been evaluated and changed to numeric tokens before being
+substituted for~$s$.\cutpar
+
+\bugonpage C175, line 23 (1/11/88)
+
+\ninepoint\noindent
+expand into a sequence of tokens. \
+(The language {\eightrm{SIMULA67}} demonstrated that it is\cutpar
+
+\bugonpage C206, minor changes to lines $-19$ to $-5$ (3/4/95)
+
+\vbox to70mm{\ninepoint
+\beginlines \advance\hsize.71pt
+|Path at line 15, before subdivision into octants:|
+|(1.53745,9.05345)..controls (1.53745,4.00511) and (5.75409,-0.00049)|
+| ..(10.85147,-0.00049)..controls (16.2217,-0.00049) and (20.46255,4.51297)|%
+ \kern.5em\null
+| ..(20.46255,9.94655)..controls (20.46255,14.99713) and (16.23842,19.00049)|
+ \kern-.71pt
+| ..(11.13652,19.00049)..controls (5.77066,19.00049) and (1.53745,14.48491)|%
+ \kern.5em\null
+| ..cycle|
+\smallskip
+|Cycle spec at line 15, after subdivision:|
+|(1.53745,9.05345) % beginning in octant `SSE'|
+| ..controls (1.53745,6.58786) and (2.54324,4.371)|
+| ..(4.16621,2.74803) % segment 0|
+|% entering octant `ESE'|
+| ..controls (5.8663,1.04794) and (8.24362,-0.00049)|
+| ..(10.85147,-0.00049) % segment 0|
+|% entering octant `ENE'|
+\endlines
+$\ldots$ and so on; there are lots more numbers! What does this all mean?
+Well, the first segment of the curve, from $(1.53745,9.05345)$ to
+$(10.85147,-0.00049)$,
+has been\cutpar}
+
+\bugonpage C207, minor changes to lines 1--23 (3/4/95)
+
+\ninepoint
+\begingroup\let\HRULE=\hrule \def\hrule{\HRULE width\hsize}
+\beginlines
+|Cycle spec at line 15, after subdivision and autorounding:|
+|(2,9.05348) % beginning in octant `SSE'|
+| ..controls (2,6.50526) and (3.02194,4.22272)|
+| ..(4.6577,2.58696) % segment 0|
+|% entering octant `ESE'|
+| ..controls (6.2624,0.98225) and (8.45786,0)|
+| ..(10.85873,0) % segment 0|
+|% entering octant `ENE'|
+\endlines
+Point $(1.53745,9.05345)$, where there was a vertical tangent, has been
+rounded to $(2,9.05348)$; point $(10.85147,-.00049)$, where there was
+\vadjust{\goodbreak}%
+a horizontal tangent, has been rounded to $(10.85873,0)$; the intermediate
+control points have been adjusted accordingly. \ (Rounding of $x$~coordinates
+has been done separately from $y$~coordinates.) \ Finally, with
+$\\{autorounding}=2$, additional adjustments are made so that the
+$45^\circ$ transition point will occur at what \MF\ thinks is a good spot:
+\beginlines
+|Cycle spec at line 15, after subdivision and double autorounding:|
+|(2,9.05348) % beginning in octant `SSE'|
+| ..controls (2,6.6761) and (3.07103,4.42897)|
+| ..(4.78537,2.71463) % segment 0|
+|% entering octant `ESE'|
+| ..controls (6.46927,1.03073) and (8.62749,0)|
+| ..(10.85873,0) % segment 0|
+|% entering octant `ENE'|
+\endlines
+(Notice that $4.78537+2.71463=7.50000$; when the slope
+is~$-1$ at a transition point\cutpar
+\endgroup
+
+\bugonpage C210, line $-7$ (8/5/98)
+
+\ninepoint\indent\qquad\alt\<numeric token primary>
+
+\bugonpage C210, line $-2$ (8/5/98)
+
+\ninepoint\indent\<numeric token primary>\is\<numeric token>\thinspace
+{\tt/}\thinspace\<numeric token>
+
+\bugonpage C211, line 16 (8/5/98)
+
+\ninepoint\indent\qquad\alt\<numeric token primary not followed by
+{\tt+} or {\tt-} or a numeric token>
+
+\bugonpage C213, lines 17--27 (8/5/98)
+
+\ninepoint
+\beginsyntax
+<path primary>\is<pair primary>\alt<path variable>\alt<path argument>
+ \alt[(]<path expression>[)]
+ \alt[begingroup]<statement list><path expression>[endgroup]
+ \alt[makepath]<pen primary>\alt[makepath]<future pen primary>
+ \alt[reverse]<path primary>
+ \alt[subpath]<pair expression>[of]<path primary>
+<path secondary>\is<pair secondary>\alt<path primary>
+ \alt<path secondary><transformer>
+<path tertiary>\is<pair tertiary>\alt<path secondary>
+<path subexpression>\is<path expression>
+ \alt<path subexpression><path join><path tertiary>
+\endsyntax
+
+\bugonpage C213, line $-4$ (8/5/98)
+
+\ninepoint\indent
+\<path expression>\is\<pair expression>\alt\<path tertiary>
+\endsyntax
+
+\bugonpage C234, line 6 (9/6/00)
+
+\ninepoint\noindent
+line~$z_1\to z_5$ that bisects $z_4\to z_2$, so it starts out in a
+south-by-southwesterly direction;\cutpar
+
+\bugonpage C246, line 5 of answer 14.15 (8/5/98)
+
+\ninepoint\indent
+\qquad/ length(postcontrol $t$ of $p$ $-$ point $t$ of $p$) {\bf enddef\/};
+
+\bugonpage C246, line 10 of answer 14.15 (8/5/98)
+
+\ninepoint\indent
+\qquad/ length(precontrol $t$ of $p$ $-$ point $t$ of $p$) {\bf enddef\/};
+
+\bugonpage C252, line $-6$ (8/5/98)
+
+\ninepoint\noindent
+$h+o$ and $\\{bot}\,y_4=-o$, so nothing needs to be done there.
+We should, however, say
+
+\bugonpage C257, large display on line 5 (3/4/95)
+
+\def\bb{$\,\left\{\vcenter\bgroup\halign\bgroup\hfil##\hfil\cr}
+\def\ee{\crcr\egroup\egroup\right\}\,$}
+\tenpoint\noindent
+\bb|boolean|\cr|numeric|\cr|pair|\cr|path|\cr
+|pen|\cr|picture|\cr|string|\cr|transform|\ee\<expression>; \
+\bb\<boolean>\cr\<numeric>\cr\<pair>\cr\<string>\cr\<transform>\ee
+ \bb|<|\cr|<=|\cr|=|\cr|<>|\cr|>=|\cr|>|\ee
+ \bb\<boolean>\cr\<numeric>\cr\<pair>\cr\<string>\cr\<transform>\ee;
+
+\bugonpage C261, line $-15$ (8/5/98)
+
+\tenpoint\textindent\bull {\it Hacks:\/} \ |gobble|, |gobbled|, |killtext|; \
+|capsule_def|; \ |numtok|.
+
+\bugonpage C286, line 15 (8/5/98)
+
+\ninepoint\noindent
+isn't entirely expanded by {\bf expandafter\/}; only \MF's first
+step in loop expansion\cutpar
+
+\bugonpage C299, line 2 (12/6/99)
+
+\ninepoint\noindent
+\begindisplay
+$\displaystyle t[u_1,\ldots,u_n]\;=\;\sum_{k=1}^n{n-1\choose k-1}
+ (1-t)^{n-k}t^{k-1}u_k,$
+\enddisplay
+
+\bugonpage C299, swap lines 11 and 12 (8/5/98)
+
+\ninepoint\begintt
+def lbrack = hide(delimiters []) lookahead [ enddef;
+let [[[ = [; let ]]] = ]; let [ = lbrack;
+\endtt
+
+\bugonpage C306, line 1 (11/4/98)
+
+\ninepoint\noindent
+| ligtable oct"013": "i" =: oct"016", "l" =: oct"017", % ffi and ffl|
+
+\bugonpage C311, line 2 (8/5/98)
+
+\ninepoint\noindent
+$\\{fine}:=4-\\{eps}$, and $\\{breadth\_}[1]:=4-\\{eps}$.
+\ (A small amount~\\{eps} has been subtracted\cutpar
+
+\bugonpage C323, line $-3$ (8/5/98)
+
+\ninepoint\noindent
+statement occurs,
+the special string `|"title "|\thinspace\&\thinspace\<title>'
+is output. \ (This is how the\cutpar
+
+\bugonpage C332, lines 22--24 (8/5/98)
+
+\ninepoint
+\item{}be replicated so that the final
+proofs will be \\{rep} times bigger than usual, and the pattern will be clipped
+slightly at the edges so that discrete pixels can be seen plainly.
+
+
+\bugonpage C341, line 23 (10/10/96)
+
+\ninepoint
+\noindent|\def\:{\setbox0=\hbox{\noboundary\char\n\noboundary}%|
+
+\bugonpage C346, left column (9/6/00)
+
+\eightpoint
+|...| (bounded join), 18--19, 127, 248, $\underline{262}$.\par
+|...| (truncation of displayed context), 44.
+
+\bugonpage C346, and throughout the index (3/7/95)
+
+\eightpoint\noindent
+(Many index entries for rules of syntax in chapters 25--26
+should have been underlined)
+
+\bugonpage C350, left column (4/24/00)
+
+\eightpoint\noindent
+Evetts, Leonard Charles, 153.
+
+\bugonpage C351, right column (9/22/97)
+
+\eightpoint\noindent
+\llap{\lower1pt\hbox{*}}%
+|intersectiontimes|, $\underline{136}$, {\it178}, 213, {\it265}, {\it294},
+{\it298}.
+
+\bugonpage C353, right column (8/5/98)
+
+\eightpoint\noindent
+\<numeric token atom>, {\it delete this entry}.\par\noindent
+\<numeric token primary>, 72, $\underline{210}$.
+
+\bugonpage C354, left column (7/26/98)
+
+\eightpoint\noindent
+Orwell, George (= Blair, Eric Arthur), 85.
+
+\bugonpage C355, right column (3/7/95)
+
+\eightpoint\noindent
+|rt|, {\it23}, {\it77}, 80, {\it103}, 147, 151, $\underline{273}$.
+
+\bugonpage C361, lines 14--15 (4/29/97)
+
+\tenpoint
+\begintt
+email: {\tt TUG@tug.org}
+internet: {\tt http://www.tug.org/}
+}
+\endtt
+
+\bugonpage C361, bottom five lines (4/29/97)
+
+\begingroup \def\TeX{T\kern-.2em\lower.5ex\hbox{E}\kern-.000em X}
+\baselineskip10pt
+\rightline{\eightssi Don't delay, subscribe today! That address again is}
+\rightline{\eightssi \TeX\ Users Group}
+\rightline{\eightssi email: \eighttt TUG\char`\@ tug.org}
+\rightline{\eightssi internet: \eighttt http://www.tug.org/}
+\smallskip
+\rightline{\eightss DONALD E. KNUTH, {\eightssi The \TeX book\/} (1996)}
+\endgroup
+
+ % Volume D
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage Dix, line ix (8/19/00)
+
+\tenpoint
+\textindent{\bull}``Interfacing with graphic objects'' by
+Ignacio Andr\'es Zabala Salelles,\cutpar
+
+\hsize=35pc
+
+\bugonpage D71, line 11 of section 178 (9/13/00)
+
+\ninepoint
+\noindent\quad\qquad\qquad$\{$ previous \\{mem\_end}, \\{lo\_mem\_max},
+ and \\{hi\_mem\_min} $\}$
+
+\bugonpage D132, line 6 of section 291 (9/13/00)
+
+\tenpoint\noindent
+$$=v_n+w_n\theta_0-u_n\bigl(v_1+w_1\theta_0-u_1(v_2+\cdots
+-u_{n-2}(v_{n-1}+w_{n-1}\theta_0-u_{n-1}\theta_0)\ldots{})\bigr),$$
+
+\bugonpage D213, line 7 (9/14/00)
+
+\tenpoint\noindent
+$(-y+\epsilon,
+x+y+\epsilon\delta)$. We should therefore round as if our skewed coordinates
+were $(x+\epsilon+\epsilon\delta,y-\epsilon)$\cutpar
+
+\bugonpage D349, line 4 of section 784 (9/14/00)
+
+\ninepoint\noindent
+{\bf procedure} \\{pack\_job\_name}($s\;$: \\{str\_number});\quad
+ $\{$ $s={}$|".log"|, |".gf"|, |".tfm"|, or \\{base\_extension} $\}$
+
+\bugonpage D451, line 11 (9/14/00)
+
+\tenpoint\noindent
+{\bf 1040}.\qquad The value of \\{cur\_mod} controls the \\{verbosity} in
+the \\{print\_exp} routine: If it's \\{show\_code},\cutpar
+
+\bugonpage D464, bottom line (9/14/00)
+
+\ninepoint\noindent
+\\{long\_help\_seen}: \\{boolean};\quad$\{$ has the long {\bf errmessage}
+ help been used? $\}$
+
+\bugonpage D551, Zabala entry (8/19/00)
+
+\eightpoint\noindent
+Zabala Salelles, Ignacio Andr\'es:\quad 812.
+
+ % volume E
+\hsize=29pc
+\bugonpage Exiii, lines 3 and 4 from the bottom (7/17/98)
+
+\textindent{\bull}``Metamarks:
+Preliminary studies for a Pandora's Box of shapes''
+by Neenie Billawala, Stanford Computer Science report 1256 (Stanford,
+California,\cutpar
+
+\def\dashto{\mathrel{\hbox{-\kern-.05em}\mkern3.9mu\hbox{-\kern-.05em}}}
+\def\ddashto{\mathrel{\hbox{-\kern-.05em}\mkern3.9mu
+ \hbox{-\kern-.05em}\mkern3.9mu\hbox{-\kern-.05em}}}
+
+\bugonpage E87, bottom line (6/4/98)
+
+\rightline{\eightss--- JOHN SMITH, {\eightssi The Printer's Grammar\/}\enspace
+ (1755)} % p129
+
+\bugonpage E95, line 16 (8/8/98)
+
+\ninepoint\noindent
+\quad$\dashto z_{1r}\dashto z_{1l}\dashto{}$%
+ {\bf subpath} $(t,0)$ {\bf of\/} $(z_{3l}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E95, line 11 from the bottom (8/8/98)
+
+\ninepoint\noindent
+\quad$\dashto z_{1r}\dashto z_{1l}\dashto{}$%
+ {\bf subpath} $(t,0)$ {\bf of\/} $(z_{3r}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E95, line 8 from the bottom (3/6/95)
+
+\ninepoint\noindent
+{\bf cmchar} |"Extensible vertical arrow--extension module"|;
+
+\bugonpage E97, line 8 from the bottom (3/6/95)
+
+\ninepoint\noindent
+{\bf cmchar} |"Extensible double vertical arrow--extension module"|;
+
+\bugonpage E113, line 9 (3/6/95)
+
+\ninepoint\noindent
+$x_5=.5[x_4,x_6]$; \ $x_4-x_6=1.2u$; \ $\\{lft}\,x_{5r}=\hbox{hround}
+ (.5w-.5\\{curve})$;
+
+\bugonpage E113, line 10 from the bottom (3/6/95)
+
+\ninepoint\noindent
+$x_5=.5[x_4,x_6]$; \ $x_4-x_6=4.8u$; \ $\\{lft}\,x_{5r}=\hbox{hround}
+ (.5w-.5\\{max\_size})$;
+
+\bugonpage E115, line 9 (3/6/95)
+
+\ninepoint\noindent
+$x_5=.5[x_4,x_6]$; \ $x_4-x_6=1.2u$; \ $\\{lft}\,x_{5r}=\hbox{hround}
+ (.5w-.5\\{curve})$;
+
+\bugonpage E115, line 12 from the bottom (3/6/95)
+
+\ninepoint\noindent
+$x_5=.5[x_4,x_6]$; \ $x_4-x_6=4.8u$; \ $\\{lft}\,x_{5r}=\hbox{hround}
+ (.5w-.5\\{max\_size})$;
+
+\bugonpage E147, lines 11--14 from the bottom (7/7/97)
+
+\ninepoint\noindent
+$\\{pos}_3(.8[\\{hair},\\{stem}],0)$; \ $\\{pos}_4(\\{vair},-90)$; \
+ $\\{pos}_5(\\{hair},-180)$;\par\noindent
+$\\{pos}_6(\\{vair},-270)$; \ ${pos}_7(\\{stem},-360)$; \
+ $\\{pos}_8(\\{vair},-450)$; \ ${pos}_9(\\{hair},-540)$;\par\noindent
+$x_0=x_1=x_9$; \ $\\{lft}\,x_{0l}=\hbox{hround}(1.5u-.5\\{hair})$; \
+ $x_2=x_4=x_6=x_8=.5w-.25u$;\par\noindent
+$\\{rt}\,x_{3r}=\hbox{hround}(w-1.75u)$; \ $\\{rt}\,x_{7r}=\hbox{hround}(w-u)$;
+
+\bugonpage E147, line 8 from the bottom (7/7/97)
+
+\ninepoint\noindent
+$y_5=.5[y_4,y_6]$; \ $\\{top}\,y_{6r}-\\{bot}\,y_{4r}=\\{vstem}+\\{eps}$; \
+$\\{bot}\,y_8=-\\{oo}$; \ $y_7=y_9=.55[y_6,y_8]$;
+
+\bugonpage E165, line 6 (2/8/97)
+
+\ninepoint\noindent
+$y_1+.5\\{hair}=h$; \ $x_1=x_2+.75u$; \
+ $\\{pos}_1(\\{hair}+\\{dw},\hbox{angle}(2(x_1-x_2),y_1-y_2)+90)$;
+
+\bugonpage E165, line 10 (2/8/97)
+
+\ninepoint\noindent
+$x_3=.5[x_2,x_4]$; \ $x_7-.25u=.5[x_6,x_8]$; \
+ $\\{rt}\,x_{8r}=\hbox{hround}(w-.5u)$;
+
+\bugonpage E187, line 9 (3/6/95)
+
+\ninepoint\noindent
+$\\{lft}\, x_{1l}=\\{lft}\, x_{2l}=\hbox{hround}(.5w-.5\\{shaved\_stem})$; \
+ $\\{top}\, y_1=h$; \ $\\{bot}\, y_2=0$;
+
+\bugonpage E189, line 8 (3/6/95)
+
+\ninepoint\noindent
+$\\{lft}\, x_{1l}=\\{lft}\, x_{2l}=\hbox{hround}(.5w-.5\\{shaved\_stem})$; \
+ $\\{top}\, y_1=h$; \ $\\{bot}\, y_2=0$;
+
+\bugonpage E233, line 21 (3/6/95)
+
+\ninepoint\noindent
+{\bf path} $p$; \
+$\{\{${\bf interim} $\\{superness}:=\\{more\_super}$; \
+ $p=\\{pulled\_super\_arc}_l(3,4)(\\{pull})\}\}$;
+
+\bugonpage E237, line 5 (8/6/98)
+
+\ninepoint\noindent
+$\\{lft}\,x_1={\rm hround}\,.5u$; \ $x_2=w-x_1$; \
+ $y_1=y_2=\\{good}.y\,.7[\\{x\_height},\\{asc\_height}]$;
+
+\bugonpage E239, line 7 from the bottom (3/6/95)
+
+\ninepoint\noindent
+$\\{lft}\,x_{6r}=\hbox{hround}\, u$; \
+$x_7=3u$; \ $x_8=w-3.5u$; \ $\\{rt}\,x_{9l}=\hbox{hround}(w-u)$;
+
+\bugonpage E253, line 2 from the bottom (8/9/98)
+
+\ninepoint
+\line{\quad$.\,.\,z_{3e}\{\\{down}\}\,.\,.\,\{z_{5l}-z_{4l}\}z_{4e}
+ \dashto z_{5e}\dashto z_{6e}$;\hfil \% stroke}
+
+\bugonpage E263, line 21 (5/10/98)
+
+\ninepoint\noindent
+{\bf path} $p$; \
+$\{\{${\bf interim} $\\{superness}:=\\{more\_super}$; \
+ $p=\\{pulled\_super\_arc}_l(3,4)(\\{pull})\}\}$;
+
+\bugonpage E289, line 2 from the bottom (8/9/98)
+
+\ninepoint
+\line{\quad$.\,.\,z_{3e}\{\\{down}\}\,.\,.\,\{z_{5l}-z_{4l}\}z_{4e}
+ \dashto z_{5e}\dashto z_{6e}$;\hfil \% stroke}
+
+\bugonpage E291, line 18 (3/6/95)
+
+\def\frac#1/#2{\leavevmode\kern.1em
+ \raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em
+ /\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}
+\ninepoint\noindent
+$x_4=\frac1/3[x_5,x_{3l}]$; \ $z_4=z_5+\\{whatever}*(15u,.1h)$;
+
+\bugonpage E297, line 17 (5/10/98)
+
+\ninepoint\noindent
+{\bf path} $p$; \
+$\{\{${\bf interim} $\\{superness}:=\\{more\_super}$; \
+ $p=\\{pulled\_super\_arc}_l(3,4)(\\{pull})\}\}$;
+
+\bugonpage E303, line 17 (5/10/98)
+
+\ninepoint\noindent
+{\bf path} $p$; \
+$\{\{${\bf interim} $\\{superness}:=\\{more\_super}$; \
+ $p=\\{pulled\_super\_arc}_l(3,4)(\\{pull})\}\}$;
+
+\bugonpage E309, line 7 from the bottom (5/8/98)
+
+\ninepoint\noindent
+\quad$y_{@0}=y_{@2l}-\\{bracket}-\\{eps}$;
+
+\bugonpage E313, line 7 from the bottom (5/8/98)
+
+\ninepoint\noindent
+\quad$y_{@0}=y_{@2l}+\\{bracket}+\\{eps}$;
+
+\bugonpage E319, line 8 (5/11/98)
+
+\ninepoint\noindent
+$\\{loop\_top}=$ {\bf if\/} \\{serifs}:
+ Vround .77$[\\{vair},\\{fudged.stem}]$ {\bf else}: \\{vair} {\bf fi};
+
+\bugonpage E373, lines 5 and 6 from the bottom (7/13/97)
+
+\ninepoint\noindent
+\quad$\\{top}\,y_{1r}=\hbox{vround}\, .95h+\\{oo}$; \
+$\\{top}\,y_{2r}=h+\\{oo}$; \ $y_3=.5h$;\par\noindent
+\quad$\\{bot}\,y_{4r}=-\\{oo}$; \
+$\\{bot}\,y_{5r}=\hbox{vround}\,.08h-\\{oo}$; \
+$y_{5l}:=\\{good}.y\, y_{5l}$; \ $x_{5l}:=\\{good}.x\,x_{5l}$;
+
+\bugonpage E381, lines 11 and 12 from the bottom (7/13/97)
+
+\ninepoint\noindent
+\quad$\\{top}\,y_{1r}=\hbox{vround}\, .93h+\\{oo}$; \
+$\\{top}\,y_{2r}=h+\\{oo}$; \ $y_3=.5h$;\par\noindent
+\quad$\\{bot}\,y_{4r}=-\\{oo}$; \
+$\\{bot}\,y_{5r}=\hbox{vround}\,.07h-\\{oo}$;
+
+\bugonpage E389, bottom two lines (8/7/98)
+
+\ninepoint\noindent
+\quad {\bf numeric} $\\{aa\_},\\{bb\_},\\{cc\_}$;
+ \ $\\{bb\_}=b/y$; \ $\\{cc\_}=c/y$;
+ \ $\\{aa\_}=a*a-\\{bb\_}*\\{bb\_}$;\par\noindent
+\quad $(a*(\\{cc\_}\mathbin{++}\hbox{sqrt}\,\\{aa\_})-\\{bb\_}*\\{cc\_})/\\{aa\_}$
+ {\bf enddef\/};
+
+\bugonpage E423, line 17 (8/8/98)
+
+\ninepoint\noindent\quad
+$x_{13}=x_{11}-.5$; \ $\\{top}\,y_{14r}=\min(\frac10/7\\{x\_height}+
+ .5\\{bulb\_diam},h)+1$; \ $\\{top}\,y_{11}=\\{x\_height}$;
+
+\bugonpage E427, line 21 (8/8/98)
+
+\ninepoint\noindent\quad
+$x_{23}=x_{21}-.5$; \ $\\{top}\,y_{24r}=\min(\frac10/7\\{x\_height}+
+ .5\\{bulb\_diam},h)+1$; \ $\\{top}\,y_{21}=\\{x\_height}$;
+
+\bugonpage E431, lines 18 and 19 (8/8/98)
+
+\ninepoint\noindent
+{\bf filldraw} $z_0\dashto(x_0,y_{2l})\dashto z_{1l}\{\\{right}\}\,.\,.\,
+ \{\\{left}\}z_{1r}$\par
+\noindent\quad$\dashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3r}\,.\,.\,\{2(x_0-x_3),y_0-y_3\}z_{5r})$
+
+\bugonpage E431, line 2 from the bottom (8/8/98)
+
+\ninepoint\line{\quad
+$\dashto z_{1l}\{\\{right}\}\,.\,.\,\{\\{left}\}z_{1r}\dashto
+ (x_0,y_{2r})\dashto{}$cycle;\hfill\% arrowhead and stem}
+
+\bugonpage E433, lines 13 and 14 (8/8/98)
+
+\ninepoint\noindent
+{\bf filldraw} $z_0\dashto(x_0,y_{2l})\dashto z_{1l}\{\\{left}\}\,.\,.\,
+ \{\\{right}\}z_{1r}$\par
+\noindent\quad$\dashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3l}\,.\,.\,\{2(x_0-x_3),y_0-y_3\}z_{5r})$
+
+\bugonpage E433, line 2 from the bottom (8/8/98)
+
+\ninepoint\line{\quad
+$\dashto z_{1l}\{\\{left}\}\,.\,.\,\{\\{right}\}z_{1r}\dashto
+ (x_0,y_{2r})\dashto{}$cycle;\hfill\% arrowhead and stem}
+
+\bugonpage E463, line 15 (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{1r}\,.\,.\,z_{1l}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3r}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E463, line 3 from the bottom (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{1r}\,.\,.\,z_{1l}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3l}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E465, line 16 (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{1l}\,.\,.\,z_{1r}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3r}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E465, line 3 from the bottom (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{1l}\,.\,.\,z_{1r}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3l}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E467, line 18 (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{1l}\,.\,.\,z_{1r}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3r}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E467, line 3 from the bottom (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{11l}\,.\,.\,z_{12r}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{13l}\{z_{19}-z_{13}\}\,.\,.\,z_{15r})$
+
+\bugonpage E483, lines 12--14 from the bottom (3/6/95)
+
+\ninepoint\noindent
+{\bf beginarithchar}(oct$\,$|"004"|); \
+{\bf pickup} \\{fine}.\\{nib}; \
+{\bf pickup} \\{rule}.\\{nib};\par
+\line{{\bf numeric} \\{del};
+ \ $\\{del}=\\{dot\_size}-\\{currentbreadth}$;\hfill
+ \% $\\{currentbreadth}=\\{fine}$}
+\noindent
+$x_3-.5\\{del}=\\{good}.x(.5w-.5\\{del})$;
+ \ $\\{center\_on}(x_3)$;\par\noindent
+$y_3+.5del=\\{good}.y(\\{math\_axis}+\\{math\_spread}[.5\\{x\_height}
+ ,.6\\{x\_height}]+.5\\{del})$;
+
+\bugonpage E485, bottom line (6/4/98)
+
+\rightline{\eightss--- JOHN SMITH, {\eightssi The Printer's Grammar\/}\enspace
+ (1755)} % p80
+
+\bugonpage E489, line 4 (8/8/98)
+
+\ninepoint\noindent
+$\\{lft}\,x_6={\rm hround}\,u$; \ $x_2=w-x_6$; \ $\\{top}\,y_8=h$; \
+ $y_8-y_4=x_2-x_6$;
+
+\bugonpage E489, line 10 (8/8/98)
+
+\ninepoint\noindent
+$\\{lft}\,x_6={\rm hround}\,u$; \ $x_2=w-x_6$; \ $\\{top}\,y_8=h$; \
+ $y_8-y_4=x_2-x_6$; \ \\{circle\_points};
+
+\bugonpage E491, line 3 from the bottom (3/6/95)
+
+\def\SH{\raise.7ex\hbox{$\scriptstyle\#$}} % sharp sign for sharped units
+\ninepoint\noindent
+\quad $\\{spread}:=2\hbox{ceiling}(\\{spread}\SH*\\{hppp}/2)+\\{eps}$;
+ \ {\bf enddef\/};
+
+\bugonpage E507, line 15 (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{1r}\,.\,.\,z_{1l}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3r}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E507, line 3 from the bottom (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{11r}\,.\,.\,z_{11l}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{13l}\{z_{19}-z_{13}\}\,.\,.\,z_{15r})$
+
+\bugonpage E509, line 17 (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{1l}\,.\,.\,z_{1r}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3l}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E509, lines 3 and 4 from the bottom (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{1l}\,.\,.\,z_{1r}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3l}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E511, line 17 (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{1l}\,.\,.\,z_{1r}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3l}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E511, lines 3 and 4 from the bottom (8/8/98)
+
+\ninepoint\noindent\quad
+$\ddashto z_{1l}\,.\,.\,z_{1r}\ddashto{}${\bf subpath} $(t,0)$ {\bf of\/}
+ $(z_{3l}\{z_9-z_3\}\,.\,.\,z_{5r})$
+
+\bugonpage E541, bottom line (2/27/97)
+
+{\bf labels}\kern.05em$(1,2,3,5,6,7,8,9,10,11,12,13,14,15)$; \ {\bf endchar};
+
+\bugonpage E568, the example of {\tt cmtex8} (4/18/96)
+
+\eightpoint\indent
+(The word `{\tt logician}' should not be hyphenated.)
+
+\bugonpage E574, left column (3/6/95)
+
+\eightpoint\indent
+\\{currentbreadth}, 483, $\underline{545}$, 546.
+
+\bugonpage E575, right column (9/10/98)
+
+Holmes, Kris Ann, vi, vii.
+
+\bugonpage E576, right column (6/4/98)
+
+\eightpoint\noindent Delete the entry for Luckombe
+
+\bugonpage E579, left column (6/4/98)
+
+\eightpoint Smith, John, 87, 485.
+
+ \bye
diff --git a/systems/knuth/dist/errata/errata.tex b/systems/knuth/dist/errata/errata.tex
new file mode 100644
index 0000000000..c53b83902b
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.tex
@@ -0,0 +1,1700 @@
+% Bugs (sigh) in Computers \& Typesetting --- the most recent errata
+
+\input manmac
+\def\.#1{\hbox{\tt#1}}
+\font\sltt=cmsltt10
+\font\niness=cmss9
+\font\ninessi=cmssi9
+\proofmodefalse
+\raggedbottom
+\output{\hsize=29pc \onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\today{\number\day\
+ \ifcase\month\or
+ Jan\or Feb\or Mar\or Apr\or May\or Jun\or
+ Jul\or Aug\or Sep\or Oct\or Nov\or Dec\fi
+ \ \number\year}
+
+\def\cutpar{{\parfillskip=0pt\endgraf}}
+
+\def\rhead{Bugs in {\tensl Computers \& Typesetting as of \today}}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\buginvol#1(#2) \par{\bigbreak\penalty-1000\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Volume #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\slMF{{\manual 89:;}\-{\manual <=>:}} % slant the logo
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\newcount\nn
+\newdimen\nsize \newdimen\msize \newdimen\ninept \ninept=9pt
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+
+\tenpoint
+\noindent This is a list of all substantial corrections made to {\sl Computers
+\& Typesetting\/} since the publication of the second ``Millennium Edition''
+at the close of the year 2001. (More precisely, it lists errors corrected
+since the 16th printing of Volume~A, the 7th printing
+of Volume~B, the 6th printing of Volume~C, the 4th printing of Volume~D,
+and the 5th printing of Volume~E.)
+Corrections made to the softcover version of {\sl The \TeX book\/},
+beginning with its 32nd printing, are
+the same as corrections to Volume~A\null. Corrections to the softcover
+version of {\sl The \slMF\kern1ptbook}, beginning with its 11th printing,
+are the same as corrections to Volume~C\null. Changes to the mini-indexes
+and master indexes of Volumes B, D, and~E are not shown here unless they are
+not obviously derivable from what has been shown. Some (or all) of these
+errors have been corrected in the most recent printings.
+\looseness=-1
+
+ % volume A
+
+\bugonpage A7, line 4 from the bottom (01/15/04)
+
+\noindent
+since control sequences of the second kind always have exactly one
+symbol after\cutpar
+
+\bugonpage A123, line 7 from the bottom (02/27/08)
+
+\ninepoint\noindent
+that it won't make the natural height-plus-depth
+of\/ |\box|$\,n$ surpass |\dimen|$\,n$, when it~is\cutpar
+
+\bugonpage A124, lines 12 and 13 (02/27/08)
+
+\ninepoint\noindent
+means that \TeX\ has tried to split an |\insert254| to height $180.2\pt$;
+the natural height-plus-depth of the best such split is $175.3\pt$,
+and the penalty for breaking there is~100.)
+
+\bugonpage A153, line 7 (01/03/14)
+
+\ninepoint\noindent
+of three fonts: one for text size, one for
+script size, and one for scriptscript size. The\cutpar
+
+\bugonpage A206, lines 12--17 (05/21/07)
+
+\ninepoint\noindent
+or alignment template
+is also considered to be |\outer| in this sense; for example, a
+file shouldn't end in the middle of a definition. If you are designing a
+format for others to use, you can help them detect errors before too much
+harm is done, by using |\outer| with all control sequences that should
+appear only at ``quiet times'' within a document. For example, Appendix~B
+defines |\proclaim| to be |\outer|, since a user shouldn't be stating a
+theorem as part of a definition or argument or preamble.
+
+
+\bugonpage A216, line 3 from the bottom (12/20/07)
+
+\ninepoint\indent|\openin|\<number>|=|\<file name>
+
+\bugonpage A290, lines 25--26 (02/24/08)
+
+\ninepoint\textindent{$\bull$}
+\<leaders>\<box or rule>\<horizontal skip>.\enskip
+Here \<horizontal skip> refers to one of the first five glue-appending
+commands just mentioned; the formal syntax for \<leaders>\cutpar
+
+\bugonpage A292, line 15 (12/02/02)
+
+\ninepoint\noindent
+are defined as in the
+second alternative of a \<math field>, are
+recorded in a ``choice\cutpar
+
+\bugonpage A308, lines 25 and 26 (06/17/02)
+
+\ninepoint
+\begintt
+\def\appendroman#1#2#3{\expandafter\def\expandafter#1\expandafter
+ {\csname\expandafter\gobble\string#2\romannumeral#3\endcsname}}
+\endtt
+
+\bugonpage A311, line 14 (12/02/02)
+
+\ninepoint\indent
+|\def\\{\if\space\next\ % assume that \next is unexpandable|
+
+\bugonpage A311, line 17 (12/29/07)
+
+\ninepoint\indent
+| \leavevmode\copy0\kern-\wd0\makelightbox}|
+
+\bugonpage A318, lines 24 and 25 (10/01/03)
+
+\ninepoint\noindent
+\hbox to\parindent{\bf\hss15.13.\enspace}%
+Yes, in severe circumstances. (1)~Previous footnotes might
+ have left no room for any more footnotes on the page.
+(2)~If |\vadjust{\eject}| occurs on the same line\cutpar
+
+\bugonpage A364, lines 12--15 from the bottom (02/29/08)
+
+\ninepoint\noindent
+|\def\loggingall{\tracingcommands=2 \tracingstats=2|\par\noindent
+| \tracingpages=1 \tracingoutput=1 \tracinglostchars=1 |\par\noindent
+| \tracingmacros=2 \tracingparagraphs=1 \tracingrestores=1 |\par\noindent
+| \showboxbreadth=\maxdimen \showboxdepth=\maxdimen}|\par
+\noindent
+|\def\tracingall{\tracingonline=1 \loggingall}|
+
+\bugonpage A364, line 5 from the bottom (02/29/08)
+
+\ninepoint\noindent
+|\def\fmtversion{3.141592653} % identifies the current format|
+
+\bugonpage A373, lines 4 and 5 from the bottom (01/02/14)
+
+\ninepoint\noindent
+And here's another solution (which may be faster, because
+token list registers can be expanded more quickly than macros
+on some implementations, using |\the|):
+
+\bugonpage A373, line 2 from the bottom (01/02/14)
+
+\ninepoint\indent
+|\loop \ifnum\m>0 \t=\expandafter{\the\t*}\advance\m-1 \repeat|
+
+\bugonpage A399, line 18, through what used to be page A400, line 14 (02/26/08)
+
+\ninepoint
+Finally, the reformatting of\/ |\box\footins| can be achieved easily with
+an elegant technique suggested by David Kastrup, using the following
+\TeX\ code within the |\output| routine:
+\begindisplay
+|\def\makefootnoteparagraph{\unvbox\footins|\cr
+| \baselineskip=\footnotebaselineskip \removehboxes}|\cr
+|\def\removehboxes{\unskip\setbox0=\lastbox|\cr
+| \ifhbox0{\removehboxes}\unhbox0 \else\noindent \fi}|\cr
+\enddisplay
+The key idea here is |\removehboxes|, a macro that has the magical ability to
+take a vertical box such as `|\vbox{\box1\box2\box3\removehboxes}|' and
+transform it into
+`|\vbox{\noindent\unhbox1\unhbox2\unhbox3}|'\kern-1pt,
+if\/ |\box1|, \kern-2pt|\box2|, and
+|\box3| are hboxes. Notice how |\removehboxes| introduces braces so that
+\TeX's {save stack} will hold all of the hboxes before they are unboxed. Each
+level of recursion in this routine uses one cell of input stack space and
+three cells of save stack space; thus, it is generally safe to do more than
+100 footnotes without exceeding \TeX's capacity.
+
+In our application there is no interline glue within |\box\footins|,
+so the |\unskip| command could be deleted from |\removehboxes|.
+
+Incidentally, the |\unskip| and |\lastbox| operations have running
+times of the approximate form
+$a+mb$, where $m$~is the number of items {efficiency} on the
+list preceding the glue or box that is removed. Hence |\removehboxes| has a
+running time of order $n^2$ when it removes $n$~boxes.
+But the constant~$b$ is so small that
+for practical purposes it's possible to think of\/ |\unskip| and
+|\lastbox| as almost instantaneous.
+
+\bugonpage A416, lines 18--22 (06/08/07)
+
+\ninepoint\noindent\beginlines
+|\def\leftheadline{\hbox to \pagewidth{\spaceskip=0pt|
+| \vbox to 10pt{}% strut to position the baseline|
+| \llap{\tenbf\folio\kern1pc}% folio to left of text|
+| \tenit\rhead\hfil}} % running head flush left|
+|\def\rightheadline{\hbox to \pagewidth{\spaceskip=0pt\vbox to 10pt{}%|
+\endlines
+
+\bugonpage A418, line 8 from the bottom (12/13/11)
+
+\ninepoint\noindent\beginlines
+| \def\\{#3} \advance\hsize by -18mm|
+\endlines
+
+\bugonpage A418, line 3 from the bottom (12/13/11)
+
+\ninepoint\noindent\beginlines
+| \halign{\line{\titlefont\hss##}\\#4\unskip\\}|
+\endlines
+
+\bugonpage A442, lines 7 and 8 from the bottom (01/03/14)
+
+\def\rule#1.{\smallskip\textindent{\bf#1.}\ignorespaces}
+\ninepoint\textindent{\bf 3.}%
+If the current item is a style change, set $C$ to the specified
+style and move on to the next item.
+
+\bugonpage A450, lines 14--16 from the bottom (12/19/02)
+
+\begingroup\def\\#1{$_{\kern\scriptspace#1}$}
+\indent\qquad{\tt s\\1tic
+ \\1exp x\\3p pi\\3a \\2i\\1a i\\2al \\2id \\1do \\1ci \\2io ou\\2 \\2us}
+\medskip\noindent
+(where subscripts that aren't shown are zero), and this yields
+$$\centerline{%
+\tt.\\0s\\0u\\1p\\0e\\0r\\1c\\0a\\0l\\1i\\0f\\0r\\0a\\0g\\1i\\0l\\4i%
+\\0s\\1t\\2i\\0c\\1e\\0x\\3p\\2i\\3a\\0l\\2i\\1d\\0o\\1c\\2i\\0o\\2u\\2s\\0.}$$
+\endgroup
+
+\bugonpage A458, left column (01/11/07)
+
+\eightpoint\noindent
+|\\|, 38, {\it356}, {\it378}, {\it418}.
+
+\bugonpage A459, left column (03/17/06)
+
+\eightpoint\noindent
+angle brackets ( $\langle\,\rangle$ ), 59, {\it146--147}, 150,~156,\par
+\noindent\qquad $\underline{268}$, 420, 437;
+ {\sl see also\/} |\langle|, |\rangle|.
+
+\bugonpage A461, left column (02/24/08)
+
+\eightpoint\noindent
+|\boxit|, 223, 331.
+
+\bugonpage A468, right column (02/26/08)
+
+\eightpoint\noindent
+interline glue, 78--79, $\underline{80}$, 104, 105, 125, 221,\par
+\noindent\qquad 245, 263, 281--282, 335, 352, 399, 409.
+
+\bugonpage A469, left column (02/26/08)
+
+\eightpoint\noindent
+Kastrup, David Friedrich, 399.
+
+\bugonpage A470, left column (01/21/03)
+
+\eightpoint\noindent
+|\loggingall|, $\underline{364}$.
+
+\bugonpage A477, right column (06/08/07)
+
+\eightpoint\noindent
+\llap{*}|\spaceskip|, 76, 274, {\it317}, {\it356}, {\it416}, 429.
+
+\bugonpage A479, right column (09/11/07)
+
+\eightpoint\noindent
+|\undefined|, 350, 384.
+
+\bugonpage A483, line 5 from the bottom (11/18/03)
+
+\eightpoint
+\rightline{\eightss--- HIERONYMUS HORNSCHUCH, %
+ {\eightrm'}$O\mkern-1mu\rho\mkern1mu\vartheta o\mkern1mu %
+ \tau\upsilon\pi o\gamma\mkern-1mu %
+ \rho\alpha\phi\acute\iota\alpha\varsigma$\enspace(1608)}
+
+ % volume B
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage Bv, page number change (12/27/11)
+
+\eightpoint\noindent[For consistency with Volumes A, C, and E, the
+preface now begins on page v instead of page~vii. This change was first
+made in the ninth printing.]
+
+\bugonpage Bv {(formerly Bvii)}, bottom two lines (01/06/14)
+
+\eightpoint\noindent
+all of those changes.
+I~now believe that the final bug was discovered on 14 September 2008
+and removed in version 3.14159265.
+The finder's fee has converged to \$327.68.
+
+\bugonpage Bxiii {(formerly Bxv)}, line $-7$ (12/27/11)
+
+\ninepoint\noindent
+Format specs have no effect on the corresponding Pascal program, but they
+do influence\cutpar
+
+\hsize=35pc
+
+\bugonpage B2, line 10 from the bottom (01/02/14)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{banner}\equiv\hbox{\tt\char'23}$%
+{\tt This\]is\]TeX,\]Version\]3.14159265\char'23}\quad
+$\{\,$printed when \TeX\ starts$\,\}$
+
+\bugonpage B3, new paragraph to follow line 9 (12/20/02)
+
+\tenpoint\noindent\quad
+Incidentally, Pascal's standard \\{round} function can be problematical,
+because it disagrees with the IEEE floating-point standard.
+Many implementors have
+therefore chosen to substitute their own home-grown rounding procedure.
+
+\bugonpage B21, lines 33 and 34 (09/11/07)
+
+\def\Oct#1{\hbox{\rm\char'23\kern-.2em\it#1\/\kern.05em}} % octal constant
+\tenpoint\noindent
+$[\Oct{41}\to\Oct{46},\Oct{60}%
+\to\Oct{71},\Oct{136},\Oct{141}\to\Oct{146},\Oct{160}\to\Oct{171}]$ must be printable.
+Thus, at least 81 printable characters are needed.
+
+\bugonpage B109, line 16 (01/06/14)
+
+\ninepoint\noindent
+\qquad\qquad{\bf begin} $\\{print\_esc}(\.{"csname"})$;
+$\\{print\_esc}(\.{"endcsname"})$;
+$\\{print\_char}(\.{"\ "})$;
+{\bf end}
+
+\bugonpage B114, line 25 (09/11/07)
+
+\def\#{\hbox{\tt\char`\#}} % parameter sign
+\ninepoint\noindent
+{\bf define} $\\{save\_index}(\#)\equiv\\{save\_stack}[\#].\\{hh}.\\{rh}$\quad
+ $\{\,$\\{eqtb} location or token or \\{save\_stack} location$\,\}$
+
+\bugonpage B139, line 20 (12/19/02)
+
+\ninepoint\noindent
+\quad{\bf begin while} $(\\{state}=\\{token\_list}) \land
+ (\\{loc}=\\{null}) \land (\\{token\_type}\ne\\{v\_template})$ {\bf do}\par
+\noindent\qquad\\{end\_token\_list};\quad$\{\,$conserve stack space$\,\}$
+
+\bugonpage B144, line 14 (09/11/07)
+
+\ninepoint\noindent
+\quad\\{cat}: $0\to\\{max\_char\_code}$;\quad$\{\,$\\{cat\_code}(\\{cur\_char}), usually$\,\}$
+
+\bugonpage B153, lines 2 and 3 (09/11/07)
+
+\tenpoint\noindent
+In fact, these three procedures account for almost every use of \\{get\_next}.
+
+\bugonpage B161, line 19 (12/19/02)
+
+\ninepoint\noindent
+\quad{\bf while} $(\\{state}=\\{token\_list}) \land
+ (\\{loc}=\\{null}) \land (\\{token\_type}\ne\\{v\_template})$ {\bf do}\par
+\noindent\qquad\\{end\_token\_list};\quad$\{\,$conserve stack space$\,\}$
+
+\bugonpage B163, line 29 (12/19/02)
+
+\ninepoint\noindent
+\quad$\\{long\_state}\gets\\{call}$;
+$\\{cur\_tok}\gets\\{par\_token}$;
+$\\{ins\_error}$;
+{\bf goto} \\{continue};
+
+\bugonpage B172, lines 2--6 from the bottom (09/11/07)
+
+\ninepoint\noindent
+{\bf else if\/} $m=\\{vmode}$ {\bf then} \\{scanned\_result}(\\{prev\_depth})(\\{dimen\_val})\par\noindent
+\quad{\bf else} \\{scanned\_result}(\\{space\_factor})(\\{int\_val})
+
+\bugonpage B178, line 4 (09/11/07)
+
+\ninepoint\noindent
+\quad$\\{cur\_val}\gets0$;
+$\\{cur\_val\_level}\gets\\{int\_val}$;
+$\\{radix}\gets0$;
+$\\{cur\_order}\gets\\{normal}$;
+
+\bugonpage B184, line 9 from the bottom (04/18/07)
+
+\tenpoint\noindent
+and denominator sum to 32768 or less.
+According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$;\kern-6.6pt\cutpar
+
+\bugonpage B206, line 14 (10/30/02)
+
+\tenpoint\noindent
+used input files like \.{webmac.tex}.
+
+\bugonpage B206, new paragraph to follow line 22 (12/20/02)
+
+\tenpoint\noindent\quad
+The following procedures don't allow spaces to be part of
+file names; but some users seem to like names that are spaced-out.
+System-dependent changes to allow such things should probably
+be made with reluctance, and only when an entire file name that
+includes spaces is ``quoted'' somehow.
+
+\bugonpage B227, new line to precede line 23 (09/11/07)
+
+\ninepoint\noindent
+{\bf if} $(\\{nw}=0)\lor(\\{nh}=0)\lor(\\{nd}=0)\lor(\\{ni}=0)$
+ {\bf then} \\{abort};
+
+\bugonpage B256, line 25 (12/20/02)
+
+\ninepoint\noindent
+\qquad\\{cur\_glue}: \\{real};\quad$\{\,$glue seen so far$\,\}$\par\noindent
+\qquad\\{cur\_g}: \\{scaled};\quad$\{\,$rounded
+ equivalent of \\{cur\_glue} times the glue ratio$\,\}$\par\noindent
+\quad{\bf begin} $\\{cur\_g}\gets0$;
+ $\\{cur\_glue}\gets\\{float\_constant}(0)$;\par\noindent
+\quad$\\{this\_box}\gets\\{temp\_ptr}$;
+ $\\{g\_order}\gets\\{glue\_order}(\\{this\_box})$;
+ $\\{g\_sign}\gets\\{glue\_sign}(\\{this\_box})$;
+
+\bugonpage B258, line 5 from the bottom (12/20/02)
+
+\ninepoint\noindent
+\quad{\bf begin} $g\gets\\{glue\_ptr}(p)$;
+ $\\{rule\_wd}\gets\\{width}(g)-\\{cur\_g}$;
+
+\bugonpage B258, bottom line (12/20/02)
+
+\ninepoint\noindent
+\qquad\qquad{\bf begin} $\\{cur\_glue}\gets\\{cur\_glue}+\\{stretch}(g)$;
+ $\\{vet\_glue}(\\{float}(\\{glue\_set}(\\{this\_box}))*\\{cur\_glue})$;\par
+\noindent\qquad\qquad$\\{cur\_g}\gets\\{round}(\\{glue\_temp})$;
+
+\bugonpage B259, line 4 (12/20/02)
+
+\ninepoint\noindent
+\qquad\qquad{\bf begin} $\\{cur\_glue}\gets\\{cur\_glue}-\\{shrink}(g)$;
+ $\\{vet\_glue}(\\{float}(\\{glue\_set}(\\{this\_box}))*\\{cur\_glue})$;\par
+\noindent\qquad\qquad$\\{cur\_g}\gets\\{round}(\\{glue\_temp})$;
+
+\bugonpage B259, new line to precede old line 7 (12/20/02)
+
+\ninepoint\noindent
+\quad$\\{rule\_wd}\gets\\{rule\_wd}+\\{cur\_g}$;
+
+\bugonpage B260, line 21 (12/19/02)
+
+\ninepoint\noindent
+\qquad{\bf else begin} $\\{lx}\gets\\{lr}$ {\bf div} $(\\{lq}+1)$;
+
+\bugonpage B261, line 9 (12/20/02)
+
+\ninepoint\noindent
+\qquad\\{cur\_glue}: \\{real};\quad$\{\,$glue seen so far$\,\}$\par\noindent
+\qquad\\{cur\_g}: \\{scaled};\quad$\{\,$rounded
+ equivalent of \\{cur\_glue} times the glue ratio$\,\}$\par\noindent
+\quad{\bf begin} $\\{cur\_g}\gets0$;
+ $\\{cur\_glue}\gets\\{float\_constant}(0)$;\par\noindent
+\quad$\\{this\_box}\gets\\{temp\_ptr}$;
+ $\\{g\_order}\gets\\{glue\_order}(\\{this\_box})$;
+ $\\{g\_sign}\gets\\{glue\_sign}(\\{this\_box})$;
+
+\bugonpage B262, line 10 from the bottom (12/20/02)
+
+\ninepoint\noindent
+\quad{\bf begin} $g\gets\\{glue\_ptr}(p)$;
+ $\\{rule\_ht}\gets\\{width}(g)-\\{cur\_g}$;
+
+\bugonpage B262, line 6 from the bottom (12/20/02)
+
+\ninepoint\noindent
+\qquad\qquad{\bf begin} $\\{cur\_glue}\gets\\{cur\_glue}+\\{stretch}(g)$;
+ $\\{vet\_glue}(\\{float}(\\{glue\_set}(\\{this\_box}))*\\{cur\_glue})$;\par
+\noindent\qquad\qquad$\\{cur\_g}\gets\\{round}(\\{glue\_temp})$;
+
+\bugonpage B262, line 2 from the bottom (12/20/02)
+
+\ninepoint\noindent
+\qquad\qquad{\bf begin} $\\{cur\_glue}\gets\\{cur\_glue}-\\{shrink}(g)$;
+ $\\{vet\_glue}(\\{float}(\\{glue\_set}(\\{this\_box}))*\\{cur\_glue})$;\par
+\noindent\qquad\qquad$\\{cur\_g}\gets\\{round}(\\{glue\_temp})$;
+
+\bugonpage B263, new line to precede old line 2 (12/20/02)
+
+\ninepoint\noindent
+\quad$\\{rule\_ht}\gets\\{rule\_ht}+\\{cur\_g}$;
+
+\bugonpage B264, line 10 (12/19/02)
+
+\ninepoint\noindent
+\qquad{\bf else begin} $\\{lx}\gets\\{lr}$ {\bf div} $(\\{lq}+1)$;
+
+\bugonpage B266, line 29 (09/11/07)
+
+\tenpoint\noindent
+$\\{total\_pages}\ge65536$, the \.{DVI} file will lie. And if
+$\\{max\_push}\ge65536$, the user deserves whatever chaos might ensue.
+
+\bugonpage B279, line 19 (09/11/07)
+
+\ninepoint\noindent
+\qquad\\{p}: \\{pointer};\quad$\{\,$a new glue node$\,\}$
+
+\bugonpage B288, lines 18--20 (09/11/07)
+
+\ninepoint\noindent
+\\{left\_noad}: {\bf begin} \\{print\_esc}(\.{"left"});
+ \\{print\_delimiter}(\\{delimiter}($p$));\par\noindent
+\quad{\bf end};\par\noindent
+\\{right\_noad}: {\bf begin} \\{print\_esc}(\.{"right"});
+ \\{print\_delimiter}(\\{delimiter}($p$));
+
+\bugonpage B290, line 12 (09/11/07)
+
+\ninepoint\noindent
+\quad{\bf begin if\/} $s=\\{text\_size}$ {\bf then}
+ \\{print\_esc}(\.{"textfont"});
+
+\bugonpage B299, line 9 (12/20/02)
+
+\ninepoint\noindent
+\qquad\qquad\quad{\bf if\/} $\\{type}(r)=\\{kern\_node}$ {\bf then}
+ \quad$\{\,$unneeded italic correction$\,\}$
+
+\bugonpage B332, line 6 (12/19/02)
+
+\tenpoint\noindent
+is being scanned, or when no alignment preamble is active.
+
+\bugonpage B332, line 8 (12/19/02)
+
+\ninepoint\noindent
+\quad{\bf begin if\/} $(\\{scanner\_status}=\\{aligning}) \lor
+ (\\{cur\_align}=\\{null})$ {\bf then}
+
+\bugonpage B336, line 11 from the bottom (10/13/03)
+
+\tenpoint\noindent
+$j-i+\\{min\_quarterword}$ in their
+\\{link} fields. The values of $w_{ii}$ were initialized to
+\\{null\_flag},\cutpar
+
+\bugonpage B342, lines 5 and 6 (09/11/07)
+
+\tenpoint
+In restricted horizontal mode, the \\{clang} part of \\{aux} is undefined;
+an over-cautious Pascal runtime system may complain about this.
+
+\bugonpage B416, line 22 (02/29/08)
+
+\ninepoint\noindent
+\qquad\qquad{\bf if\/} $\\{count}(t)=1000$ {\bf then} $t\gets\\{height}(r)$
+\par\noindent
+\qquad\qquad{\bf else} $t\gets\\{x\_over\_n}(\\{height}(r),1000)*\\{count}(t)$;
+\par\noindent
+\qquad\qquad\\{print\_scaled}$(t)$
+
+\bugonpage B438, lines 1--3 (09/11/07)
+
+\tenpoint\noindent
+{\bf1035.\quad}If \\{link}(\\{cur\_q}) is nonnull when \\{wrapup} is invoked,
+\\{cur\_q} points to
+the list of characters that were consumed while building the ligature
+character~\\{cur\_l}.
+
+\bugonpage B438, lines 19 and 20 (09/11/07)
+
+\ninepoint\noindent
+\qquad\qquad\qquad{\bf begin if\/} $\\{link}(\\{cur\_q})>\\{null}$ {\bf then}
+\par\noindent
+\qquad\qquad\qquad\quad{\bf if\/} $\\{character}(\\{tail})=\\{qi}(
+ \\{hyphen\_char}[\\{main\_f}])$ {\bf then} $\\{ins\_disc}\gets\\{true}$;
+
+\bugonpage B438, line 4 from the bottom (09/11/07)
+
+\ninepoint\noindent
+\quad$\\{link}(\\{tail})\gets\\{lig\_stack}$;
+$\\{tail}\gets\\{lig\_stack}$\quad$\{\,$\\{main\_loop\_lookahead} is next$\,\}$
+
+\bugonpage B439, line 3 (09/11/07)
+
+\ninepoint\noindent
+\quad{\bf if\/} $\\{main\_p}>\\{null}$ {\bf then}
+ \\{tail\_append}(\\{main\_p});\quad$\{\,$append a single character$\,\}$
+
+\bugonpage B440, new line to follow line 9 (09/11/07)
+
+\ninepoint\noindent
+\quad{\bf if\/} $\\{cur\_r}=\\{non\_char}$ {\bf then goto}
+ \\{main\_loop\_wrapup};
+
+\bugonpage B452, line 18 (28/03/11)
+
+\noindent\\{hmode}, where the latter two are used to denote
+ \.{\char`\\vbox} and \.{\char`\\hbox}, respectively.
+
+\bugonpage B455, lines 3 and 4 (09/11/07)
+
+\ninepoint\noindent
+\quad{\bf if\/} $((\\{cur\_cmd}=\\{hskip})\land(\\{abs}(\\{mode})\ne\\{vmode}))
+ \lor ((\\{cur\_cmd}=\\{vskip})\land(\\{abs}(\\{mode})=\\{vmode}))$ {\bf then}
+
+\bugonpage B472, new paragraph to follow line 10 (12/20/02)
+
+\tenpoint\noindent\quad
+A devious user might force an \\{endv} command to occur just about anywhere;
+we must defeat such hacks.
+
+\bugonpage B472, replacement for what used to be line 13 (12/20/02)
+
+\ninepoint\noindent
+\quad{\bf begin} $\\{base\_ptr}\gets\\{input\_ptr}$;
+ $\\{input\_stack}[\\{base\_ptr}]\gets\\{cur\_input}$;\par\noindent
+\quad{\bf while} $(\\{input\_stack}[\\{base\_ptr}].\\{index\_field}\ne
+ \\{v\_template}) \land{}$\par\noindent
+\quad\qquad $(\\{input\_stack}[\\{base\_ptr}].\\{loc\_field}=
+ \\{null}) \land {}$\par\noindent
+\quad\qquad $(\\{input\_stack}[\\{base\_ptr}].\\{state\_field}=
+ \\{token\_list})$ {\bf do} \\{decr}(\\{base\_ptr});\par\noindent
+\quad{\bf if\/} $(\\{input\_stack}[\\{base\_ptr}].\\{index\_field}\ne
+ \\{v\_template}) \lor {}$\par\noindent
+\quad\qquad $(\\{input\_stack}[\\{base\_ptr}].\\{loc\_field}\ne
+ \\{null}) \lor {}$\par\noindent
+\quad\qquad $(\\{input\_stack}[\\{base\_ptr}].\\{state\_field}\ne
+ \\{token\_list})$ {\bf then}\par\noindent
+\qquad\\{fatal\_error}(\hbox{\tt\char'23(interwoven\]alignment\]preambles\]%
+ are\]not\]allowed)\char'23});\par\noindent
+\quad{\bf if\/} $\\{cur\_group}=\\{align\_group}$ {\bf then}
+
+\bugonpage B505, line 19 (09/11/07)
+
+\ninepoint\noindent
+\qquad(\.{"since\]the\]result\]is\]out\]of\]range."});\par\noindent
+\qquad{\bf if\/} $p\ge\\{glue\_val}$ {\bf then}
+ \\{delete\_glue\_ref}(\\{cur\_val});\par\noindent
+\qquad\\{error}; {\bf return};
+
+\bugonpage B506, line 1 (10/13/03)
+
+\tenpoint\noindent
+{\bf 1237.\quad}Here we use the fact that the consecutive codes
+$\\{int\_val}\to\\{mu\_val}$ and
+$\\{assign\_int}\to$\cutpar
+
+\bugonpage B520, line 8 (06/25/04)
+
+\tenpoint\noindent
+says,
+for example, `\.{(preloaded format=plain 1982.11.19)}', showing the year,
+month, and day\cutpar
+
+\bugonpage B535, new line to follow line 11 (09/11/07)
+
+\ninepoint\noindent
+\qquad{\bf if\/} $\\{last\_glue}\ne\\{max\_halfword}$ {\bf then}
+ \\{delete\_glue\_ref}(\\{last\_glue});
+
+\bugonpage B578, new entry (06/04/06)
+
+\eightpoint\noindent
+Trabb Pardo, Luis Isidoro, 2.
+
+ % volume C
+\hsize=29pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\ddashto{\mathrel{\hbox{-\thinspace-\thinspace-\kern-.05em}}}
+\def\tension{\mathop{\rm tension}}
+\def\controls{\mathop{\rm controls}}
+\def\and{\,{\rm and}\,}
+
+\bugonpage Cxi, line 4 (05/20/07)
+
+\line{\hbox to\parindent{\bf\hbox to 1em{\hss27}\hss}%
+ \rm Recovery from Errors\leaders\hbox to 20pt{\kern13pt.\hss}\hfil
+ \hbox to 2em{\hss223}}
+
+\bugonpage C11, line 11 (10/11/01)
+
+\noindent
+the area below the bar to the area above it equal to
+$(\sqrt5+1)/2\approx1.61803$, the\cutpar
+
+\bugonpage C29, illustration for exercise 4.11 (09/09/01)
+
+\noindent
+[points 2 and 5 should not be labeled twice]
+
+\bugonpage C32, line 5 from the bottom (01/04/14)
+
+\noindent
+\hbox to\parindent{\hfil\sevenrm20\ \ }%
+| penpos1(stem,15); penpos2(.9stem,12); penpos3(stem,10);|
+
+\bugonpage C36, line 5 from the bottom (01/05/14)
+
+\noindent
+line~12, where it
+says `|x1l|', not `|x11|' or~`|xll|'); be sure to distinguish between\cutpar
+
+\bugonpage C55, lines 5 and 6 (01/05/14)
+
+\ninepoint\noindent
+suffixed or subscripted.
+Thus, the syntax rule for \<variable>
+should actually be replaced by a slightly more complicated pair of rules:
+
+
+\bugonpage C129, line 16 (02/21/08)
+
+\ninepoint\beginsyntax
+<path subexpression>\is<path expression not ending with direction specifier>\kern-5pt\null
+\endsyntax
+
+\bugonpage C130, lines 13--15 from the bottom (09/13/03)
+
+\ninepoint\noindent
+point but not after it, the
+nonempty one is duplicated in a similar way. A~basic path join
+`$\to\controls u\and v\to$' specifies explicit control points that
+override any direction specifiers that may immediately surround it.
+
+\bugonpage C137, lines 5--7 from the bottom (02/21/08)
+
+\danger Let's conclude this chapter by applying what we've learned about
+paths to a real-life example. The {\sl Journal of Algorithms\/} was
+published for many years by Academic Press, and its cover page carried the
+following logo, which was designed\cutpar
+\enddanger
+
+\bugonpage C137, bottom two lines (02/21/08)
+
+\ninepoint\noindent
+A \MF\ program to produce this logo made it possible for the editors
+of the journal to use it on letterheads in their correspondence.
+Here is one way to do that job,\cutpar
+% actually the MS now says "to write that program," and the
+% correction therefore extends to page C138
+
+\bugonpage C156, line 15 from the bottom (09/09/01)
+
+\ninepoint\noindent
+be the values they had upon entry to the group.)
+
+\bugonpage C159, lines 12--15 (12/01/06)
+
+\begintt
+def --- = ..tension infinity.. enddef;
+\endtt
+it makes `$z_1\ddashto z_2$' become
+`$z_1\to\tension\\{infinity}\to z_2$'.
+The {replacement text} can be any sequence of tokens not including
+`{\bf enddef}\kern1pt'; or it can include entire subdefinitions like
+`{\bf def}~$\ldots$~{\bf enddef}\kern1pt', according to certain rules
+that we shall explain later.
+
+\bugonpage C171, line 16 from the bottom (06/18/02)
+
+\tenpoint
+\<loop>\is\<loop header>|:|\<loop text> {\tt endfor}
+
+\bugonpage C179, line 7 from the bottom (09/09/01)
+
+\ninepoint\noindent
+next time \MF\ gets to the end of an input line, it will stop reading
+from the\cutpar
+
+\bugonpage C180, lines 14--16 (04/25/03)
+
+\ninepoint\noindent
+digits should be a
+file name that works in essentially the same way on all installations of
+\MF\kern-.03em\null. Uppercase letters are considered to be distinct from their
+lowercase counterparts, on many systems.
+
+\bugonpage C180, new line to be inserted 4 from the bottom (06/25/04)
+
+\ninepoint\item\bull
+When \MF\ is reading the symbolic tokens to be saved by {\bf save}.
+
+\bugonpage C203, line 12 from the bottom (04/25/03)
+
+\ninepoint\hbox to 237pt{point~3 at the right of the triangle
+might digitize into a}
+
+\bugonpage C213, line 26 (02/21/08)
+
+\ninepoint\beginsyntax
+<path subexpression>\is<path expression not ending with direction specifier>\kern-5pt\null
+\endsyntax
+
+\bugonpage C226, line 23 (02/21/08)
+
+\ninepoint\noindent following nineteen things will be mentioned:
+
+\bugonpage C226, new line to be second from the bottom (02/21/08)
+
+\ninepoint\indent|independent variables|\qquad(distinct numeric variables)
+
+\bugonpage C236, line 7 from the bottom (01/05/14)
+
+\ninepoint\noindent
+\hbox to\parindent{\hss\bf 7.4.\enspace}False.
+After `|newinternal x;|' you can't say `|x|\<tag>' in a \<suffix list>.
+
+\bugonpage C246, line 12 (02/21/08)
+
+\ninepoint\noindent
+is performed whenever \MF\ uses the last two alternatives
+in the definition\cutpar
+
+\bugonpage C250, lines 13 and 14 (02/19/08)
+
+\ninepoint\noindent\hbox to\parindent{\bf\hss19.3.\enspace}%
+Yes, if and only if $n-{1\over2}$ is a nonnegative even integer.
+\ (Because ambiguous values are rounded upwards.)
+
+\bugonpage C250, line 12 from the bottom (04/25/03)
+
+\ninepoint\noindent
+following \<boolean primary>.)
+
+\bugonpage C286, line 25 (09/09/01)
+
+\ninepoint\noindent
+problem; it would simply have put |ENDFOR| into the
+replacement text of |asts|, because\cutpar
+
+\bugonpage C289, line 7 (09/09/01)
+
+\vskip-6pt\ninepoint\begintt
+if if pair x: x>(0,0) else: false fi: A else: B fi.
+\endtt
+
+\bugonpage C292, line 10 from the bottom (09/09/01)
+
+\ninepoint\noindent
+be known by saying `{\bf if\/} known $p-q$: $p=q$ {\bf else}:~{\bf false}
+{\bf fi}'; transforms could be handled\cutpar
+
+\bugonpage C293, line 5 from the bottom (04/25/03)
+
+\ninepoint\noindent
+given angle~$\phi$. We can consider
+the common angle~$\theta$ of $z_{1r}-z_{1l}$ and $z_{0r}-z_{0l}$ to be\cutpar
+
+\bugonpage C315, line 15 from the bottom (04/25/03)
+
+\ninepoint\noindent
+`b' was shipped out.) \ The second letter,~`o', is placed
+in a second little box adjacent\cutpar
+
+\bugonpage C325, bottom line (02/29/08)
+
+\rightline{\eightss--- CAROLUS LINN\AE US,
+ {\eightssi Philosophia Botanica\/}\enspace(1751)}
+
+\bugonpage C332, line 4 from the bottom (04/25/03)
+
+\ninepoint\noindent
+(The proofsheet resolution will be 50 pixels per inch, because {\it cheapo\/}
+has 200 pixels per\cutpar
+
+\bugonpage C346, left column (06/18/02)
+
+\eightpoint\noindent
+\llap{*}|:|, 169, 171, 317--319.
+
+\bugonpage C346, right column (07/09/01)
+
+\eightpoint\noindent
+\llap{*}|angle|, {\it29}, {\it67}, $\underline{72}$, {\it107},
+{\it135}, 211, {\it238}.
+
+\bugonpage C346, right column (10/04/04)
+
+\eightpoint\noindent
+arccosine, arcsine, arctangent, {\sl see\/} |angle|.
+
+\bugonpage C351, right column (02/21/08)
+
+\eightpoint\noindent
+independent variables, $\underline{81}$--$\underline{83}$, 88, 224, 226.
+
+\bugonpage C352, right column (02/29/08)
+
+\eightpoint\noindent
+Linn\'e, Carl von (= Linn\ae us, Carolus), 325.
+
+\bugonpage C355, right column (02/29/08)
+
+\eightpoint\noindent
+\llap{*}|save|, $\underline{155}$--$\underline{156}$, {\it160}, 173,
+ {\it178}, 180, 218,\par
+\noindent\qquad{\it236}, {\it244}, {\it296}, 299.
+
+ % Volume D
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\bugonpage Dv, page number change (12/27/11)
+
+\eightpoint\noindent[For consistency with Volumes A, C, and E, the
+preface now begins on page v instead of page~vii. This change was first
+made in the sixth printing.]
+
+\bugonpage Dv {(formerly Dvii)}, bottom two lines (01/06/14)
+
+\eightpoint\noindent
+corporates all of those changes.
+I~now believe that the final bug was discovered on 03~June 2008,
+and removed in version 2.7182818.
+The finder's fee has converged to \$327.68.
+
+\bugonpage Dxiii {(formerly Dxv)}, line $-7$ (12/27/11)
+
+\ninepoint\noindent
+Format specs have no effect on the corresponding Pascal program, but they
+do influence\cutpar
+
+\hsize=35pc
+\parindent=1em
+
+\bugonpage D2, line $-17$ (01/03/14)
+
+\ninepoint\noindent
+{\bf define} $\\{banner}\equiv\hbox{\tt\char'23}$%
+{\tt This\]is\]METAFONT,\]Version\]2.7182818\char'23}\quad
+$\{\,$printed when \MF\ starts$\,\}$
+
+\bugonpage D2, lines 4 and 5 from the bottom (12/23/02)
+
+\tenpoint\noindent
+types; there are no `{\bf var}' parameters, except in the case of files
+or in the system-dependent \\{paint\_row} procedure;
+there are no tag fields on variant records; there are no \\{real} variables;
+no procedures are declared local to other procedures.)
+
+\bugonpage D16, new paragraph to follow line 26 (06/25/04)
+
+The first line is special also because it may be read before \MF\ has
+input a base file. In such cases, normal error messages cannot yet
+be given. The following code uses concepts that will be explained later.
+(If the Pascal compiler does not support non-local {\bf goto}, the
+statement `{\bf goto} \\{final\_end}' should be replaced by something that
+quietly terminates the program.)
+
+\bugonpage D22, line 26 (09/11/07)
+
+\noindent
+ASCII codes $[\Oct{60}\to\Oct{71},%
+\Oct{136},\Oct{141}\to\Oct{146}]$
+must be printable.
+
+\bugonpage D31, line 29 (06/25/04)
+
+\noindent
+This is the only nontrivial {\bf goto} statement in the
+whole program. It is used when there is no\cutpar
+
+\bugonpage D42, replacement for lines 8--13 (12/23/02)
+
+\tenpoint\noindent\quad
+Notice that if 64-bit integer arithmetic were available,
+we could simply compute $(2^{29}*p+\nobreak q)\allowbreak\,
+\hbox{\bf div}\,(2*q)$.
+But when we are restricted to Pascal's 32-bit arithmetic we
+must either resort to multiple-precision maneuvering
+or use a simple but slow iteration. The multiple-precision technique
+would be about three times faster than the code adopted here, but it
+would be comparatively long and tricky, involving about sixteen
+additional multiplications and divisions.
+
+\bugonpage D43, line 20 (12/23/02)
+
+\tenpoint\noindent
+language or 64-bit substitute is advisable.
+
+\bugonpage D44, lines 24--26 (12/23/02)
+
+\tenpoint\noindent\quad
+Once again it is a good idea to use 64-bit arithmetic if
+possible; otherwise \\{take\_scaled} will use more than 2\% of the running time
+when the Computer Modern fonts are being generated.
+
+\bugonpage D58, line 16 from the bottom (06/25/04)
+
+\ninepoint\noindent
+\hskip5em{\bf if\/} $\\{j\_random}=0$ {\bf then} \\{new\_randoms}
+ {\bf else} \\{decr}(\\{j\_random})
+
+\bugonpage D63, line 21 (06/25/04)
+
+Locations of \\{mem} between \\{mem\_min} and \\{mem\_top} may be dumped as
+part
+of preloaded base\cutpar
+
+\bugonpage D75, line 13 (06/25/04)
+
+\ninepoint\noindent
+\quad{\bf define} $\\{fi\_or\_else}=2$\quad$\{\,$delimiters for conditionals
+ {\bf elseif\/}, {\bf else}, {\bf fi})$\,\}$
+
+\bugonpage D76, line 5 (06/25/04)
+
+\ninepoint\noindent
+\quad{\bf define} $\\{type\_name}=30$\quad$\{\,$declare a type
+ ({\bf numeric}, {\bf pair}, etc.)$\,\}$
+
+\bugonpage D77, line 16 (06/25/04)
+
+\ninepoint\noindent
+\quad{\bf define} $\\{lig\_kern\_token}=76$\quad$\{\,$the operators
+ `{\bf kern}' and `\.{=:}' and `\.{=:\char'174}', etc.$\,\}$
+
+\bugonpage D98, bottom two lines (06/25/04)
+
+\noindent
+They consist of zero or more parameter tokens followed
+by a code for the type of macro.
+
+\bugonpage D101, line 3 (06/25/04)
+
+\noindent
+\MF\ user assigns a type to a variable like \.{x20a.b} by saying, for
+example, `\.{boolean} \.{x[]a.b}'.
+
+\bugonpage D102, lines 10--16 (06/25/04)
+
+\noindent
+variable that is relevant
+when no attributes are attached to the parent. The \\{attr\_head} node
+has the fields of either
+a value node, a subscript node, or an attribute node, depending on what
+the parent would be if it were not structured; but the subscript and
+attribute fields are ignored, so it effectively contains only the data of
+a value node. The \\{link} field in this special node points to an attribute
+node whose \\{attr\_loc} field is zero; the latter node represents a collective
+subscript `\.{[]}' attached to the parent, and its \\{link} field points to
+the first non-special attribute node (or to \\{end\_attr} if there are none).
+
+\bugonpage D102, lines 7 and 8 from the bottom (06/25/04)
+
+\noindent
+$\\{subscr\_head}(\\{q1})=\\{qq1}$;
+\\{qq} is a three-word ``attribute-as-value'' node with
+$\\{type}(\\{qq})=\break\\{numeric\_type}$
+(assuming that \.{x5} is numeric, because \\{qq} represents `\.{x[]}'
+with no further\break
+ attributes), $\\{name\_type}(\\{qq})=\\{structured\_root}$,
+$\\{attr\_loc}(\\{qq})=0$, $\\{parent}(\\{qq})=p$,\cutpar
+
+\bugonpage D103, line 6 (06/25/04)
+
+\noindent
+The value of variable \.{x20b}
+appears in node~$\\{qqq2}=\\{link}(\\{qqq1})$, as you can well imagine.
+Similarly, the value of `\.{x.a}' appears in node $\\{q2}=\\{link}(\\{q1})$,
+where $\\{attr\_loc}(\\{q2})=h(a)$ and $\\{parent}(\\{q2})=p$.
+
+\bugonpage D114, line 12 (06/25/04)
+
+\noindent
+\qquad Such save stack entries are generated by \&{save} commands.
+
+\bugonpage D120, line 3 (06/25/04)
+
+\ninepoint\noindent
+[delete the line `The code here \dots', since the
+ code {\it doesn't\/} use the stated fact]
+
+\bugonpage D126, line 10 (06/25/04)
+
+\tenpoint\noindent
+If $\theta_0$ is supposed to have a given value $E_0$, we simply
+define $C_0=1$, $D_0=0$, and $R_0=E_0$.\cutpar
+
+\bugonpage D138, line 11 from the bottom (10/26/06)
+
+\tenpoint\noindent
+for the bisected interval are $z'_0=z_0$
+and $z''_0=z_0+(Z'_1+Z'_2+\cdots+Z'_n)/2^{l+1}$.
+
+\bugonpage D142, line 3 (06/25/04)
+
+\tenpoint\noindent
+out to hold if and only if $x_0\le x_1$ and $x_2\le x_3$, and either
+$x_1\le x_2$ or $(x_1-x_2)^2\le(x_1-x_0)(x_3-x_2)$.\cutpar
+
+\bugonpage D142, line 8 (10/26/06)
+
+\tenpoint\noindent\quad
+For example, if we start with $(x_1-x_0,x_2-x_1,x_3-x_2)=
+(X_1,X_2,X_3)=(7,-16,39)$, the\cutpar
+
+\bugonpage D142, lines 21--23 (06/25/04)
+
+\tenpoint\noindent
+monotonic
+cubic, then $B(x_0,x_1,x_2,x_3;{1\over2})$ is always between
+$.06[x_0,x_3]$ and $.94[x_0,x_3]$; and it is impossible for $\bar x$
+to be within~$\epsilon$ of such a number. Contradiction!
+(The constant .06 is actually $(2-\sqrt3\,)/4$; the worst case
+occurs for polynomials like $B(0,2-\sqrt3,1-\sqrt3,3;t)$.)
+
+\bugonpage D177, line 18 (06/25/04)
+
+\ninepoint\noindent
+$\\{cur\_x},\\{cur\_y}$: \\{scaled};\quad$\{\,$outputs of \\{skew},
+ \\{unskew}, and a few other routines$\,\}$
+
+\bugonpage D182, lines 27--29 (06/25/04)
+
+\noindent
+{\bf399.\quad}If
+ the segment numbers on the cycle are $t_1$, $t_2$, \dots, $t_m$,
+and if $m\le\\{max\_quarterword}$,
+we have $t_{k-1}\le t_k$ except for at most one value of~$k$. If there are
+no exceptions, $f$ will point to $t_1$; otherwise it will point to the
+exceptional~$t_k$.
+
+\bugonpage D184, line 18 (12/21/02)
+
+\ninepoint\noindent
+\qquad\\{chopped}: \\{integer};\quad
+$\{\,$positive if data truncated, negative if data dangerously large$\,\}$
+
+\bugonpage D184, line 25 (12/21/02)
+
+\ninepoint\noindent
+\quad{\bf if\/} $(\\{internal}[\\{autorounding}]>0)\land(\\{chopped}=0)$
+ {\bf then} \\{xy\_round};
+
+\bugonpage D184, line 27 (12/21/02)
+
+\ninepoint\noindent
+\quad{\bf if\/} $(\\{internal}[\\{autorounding}]>\\{unity})\land
+ (\\{chopped}=0)$ {\bf then} \\{diag\_round};
+
+\bugonpage D184, line 32 (12/21/02)
+
+\ninepoint\noindent
+\qquad{\bf if\/} $(\\{internal}[\\{autorounding}]\le0)\lor(\\{chopped}\ne0)$
+ {\bf then} \\{print\_spec}({\tt\char`",\]after\]subdivision\char`"})
+
+\bugonpage D185, lines 15--19 (12/21/02)
+
+\ninepoint\noindent
+\quad{\bf define} \\{procrustes}({\tt\char`#})${}\equiv{}${\bf if\/}
+ $\\{abs}(\.\#)\ge\\{dmax}$ {\bf then}\par\noindent
+\qquad\qquad\quad{\bf if\/} $\\{abs}(\.\#)>\\{max\_allowed}$ {\bf then}\par
+\noindent\qquad\qquad\qquad{\bf begin} $\\{chopped}\gets1$;\par\noindent
+\qquad\qquad\qquad{\bf if\/} $\.\#>0$ {\bf then} $\.\#\gets\\{max\_allowed}$
+ {\bf else} $\.\#\gets-\\{max\_allowed}$;\par\noindent
+\qquad\qquad\qquad{\bf end}\par\noindent
+\qquad\qquad\quad{\bf else if\/} $\\{chopped}=0$ {\bf then}
+ $\\{chopped}\gets-1$
+
+\bugonpage D185, old line 22 (12/21/02)
+
+\ninepoint\noindent
+\quad$p\gets\\{cur\_spec}$; $k\gets1$; $\\{chopped}\gets0$;
+ $\\{dmax}\gets\\{half}(\\{max\_allowed})$;
+
+\bugonpage D185, old line 28 (12/21/02)
+
+\ninepoint\noindent
+\quad{\bf if\/} $\\{chopped}>0$ {\bf then}
+
+\bugonpage D196, lines 3--8 (06/25/04)
+
+The first job is to fix things so that $x(t)$ plus the horizontal
+pen offset is an integer multiple of the
+current ``granularity'' when the derivative $x'(t)$ crosses through zero.
+The given cyclic path contains regions where $x'(t)\ge0$ and regions
+where $x'(t)\le0$. The \\{quadrant\_subdivide} routine is called into action
+before any of the path coordinates have been skewed, but some of them
+may have been negated. In regions where $x'(t)\ge0$ we have $\\{right\_type}=%
+\\{first\_octant}$ or $\\{right\_type}=\\{eighth\_octant}$; in regions where
+$x'(t)\le0$,
+we have $\\{right\_type}=\\{fifth\_octant}$ or $\\{right\_type}=\\{fourth%
+\_octant}$.
+
+\bugonpage D196, lines 15 and 16 (06/25/04)
+
+\noindent
+current pen might be unsymmetric in such a way that $x$ coordinates
+should round dif-\break ferently in different parts of the curve.
+These considerations imply that round$(x_0)$\cutpar
+
+\bugonpage D200, line 4 (06/25/04)
+
+\noindent
+and that there are similar ways to address other important offsets.\par
+\smallskip\ninepoint
+[Also delete the definitions of \\{north\_south\_edge}, etc.,
+on lines 11--15; those definitions are never used.]
+
+\bugonpage D212, line 18 (06/25/04)
+
+\noindent
+at $(x_0,y_0)$ and ends at $(x_1,y_1)$, it's possible to
+prove (by induction on the length of the truncated\cutpar
+
+\bugonpage D216, bottom line (06/25/04)
+
+\noindent
+we list it twice (with coordinates
+interchanged, so as to make the second octant look like\cutpar
+
+\bugonpage D217, lines 2--10 (06/25/04)
+
+\noindent
+$$\tabskip\centering
+\halign to\hsize{$\hfil#\;\mapsto\;{}$\tabskip=0pt&
+$#\hfil$&\quad in the #\hfil\tabskip\centering\cr
+w_2\;w_2\;w_2&(-5,6)\;(-5,6)\;(-5,6)\cr
+\noalign{\vskip\belowdisplayskip
+\vbox{\noindent\strut as the list of transformed and skewed offsets to use
+when curves that travel in the second octant. Similarly, we will have\strut}
+\vskip\abovedisplayskip}
+w_2\;w_2\;w_2&(7,-6)\;(7,-6)\;(7,-6)&third;\cr
+w_2\;w_2\;w_3\;w_3&(-7,1)\;(-7,1)\;(-3,2)\;(-3,2)&fourth;\cr
+w_3\;w_3\;w_3&(3,-2)\;(3,-2)\;(3,-2)&fifth;\cr
+w_3\;w_3\;w_0\;w_0&(-3,1)\;(-3,1)\;(1,0)\;(1,0)&sixth;\cr
+w_0\;w_0\;w_0&(1,0)\;(1,0)\;(1,0)&seventh;\cr
+w_0\;w_0\;w_0&(-1,1)\;(-1,1)\;(-1,1)&eighth.\cr}$$
+
+\bugonpage D218, lines 2 and 3 (06/25/04)
+
+\noindent
+count followed by pointers to the eight offset lists, followed
+by an indication of the pen's range of values.
+
+\bugonpage D218, line 15 (06/25/04)
+
+The \\{link} field of a pen header node should be \\{null} if and only if
+the pen is a single point.
+
+\bugonpage D227, line 11 (06/25/04)
+
+\noindent
+\\{endpoint}. The cubics all have
+monotone-nondecreasing $x(t)$ and $y(t)$.
+
+\bugonpage D228, lines 4--7 from the bottom (06/25/04)
+
+\noindent
+In odd-numbered octants, the numerator and denominator of this fraction
+will be nonnegative; in even-numbered octants they will both be nonpositive.
+Furthermore we always have $0=s_0\le s_1\le\cdots\le s_n=\infty$. The goal of
+\\{offset\_prep} is to find an offset index~$k$ to associate with
+each cubic, such that the slope $s(t)$ of the cubic satisfies
+
+\bugonpage D231, line 7 (06/25/04)
+
+\ninepoint\noindent
+\quad{\bf if\/} $\\{abs}(\\{du})\ge\\{abs}(\\{dv})$ {\bf then}\quad $\{\,
+ s_{k-1}\le1$ or $s_k\le1\,\}$
+
+\bugonpage D231, line 16 (06/25/04)
+
+\noindent
+and return towards $s_{k-1}$ or $s_k$,
+respectively, yielding another solution of $(*)$.
+
+\bugonpage D246, line 4 from the bottom (06/25/04)
+
+\noindent
+dinate fields. Hence, for example,
+the point $\bigl($$\\{x\_coord}(p)-\\{left\_v}(q),\\{y\_coord}(p)+%
+\\{right\_u}(p)$$\bigr)$
+also\cutpar
+
+\bugonpage D248, lines 14 and 15 (01/06/14)
+
+\noindent
+the $x$-axis at the point
+$\bigl((a^2-b^2)\sin\theta\cos\theta/\rho\bigr)+i\rho$, where
+\vadjust{\vskip1pt}%
+$\rho=\sqrt{(a\sin\theta)^2+(b\cos\theta)^2}$. It reaches
+furthest to the right of~the $y$-axis at the point
+$\sigma+i(a^2-b^2)\sin\theta\cos\theta/\sigma$, where
+$\sigma=$\cutpar
+
+\bugonpage D248, line 24 (06/25/04)
+
+\ninepoint\noindent
+\qquad{\bf else begin} $\\{beta}\gets\\{minor\_axis}$;
+ $\\{gamma}\gets\\{major\_axis}$;
+ $\\{theta}\gets0$;
+
+\bugonpage D251, line 1 (01/06/14)
+
+\noindent
+{\bf536.\quad}Only the coordinates need to be copied, not the class numbers
+and other stuff. At this point either $\\{link}(p)$ or
+$\\{link}(\\{link}(p))$ is \\{null}.
+
+\bugonpage D251, line 10 (01/06/14)
+
+\ninepoint\noindent
+\\{done1}: {\bf if\/} $(\\{link}(p)\ne\\{null})$ {\bf then}
+$\\{free\_node}(\\{link}(p),\\{knot\_node\_size})$;\hfil\break
+\null\quad$\\{link}(p)\gets s$;
+$\\{beta}\gets-\\{y\_coord}(h)$;
+
+\bugonpage D256, line 2 from the bottom (06/25/04)
+
+\noindent
+we have $2^lu_{\min}=2^lu_0+U_{\min}$, etc.; the condition for overlap
+reduces to
+
+\bugonpage D261, line 5 (06/25/04)
+
+\ninepoint\noindent
+\\{tol}: \\{integer};\quad$\{\,$bound on the uncertainty in the overlap test$\,\}$
+
+\bugonpage D262, lines 26 and 27 (06/25/04)
+
+\ninepoint\noindent
+\qquad\quad$\\{uv}\gets\\{uv}+\\{int\_packets}$;\quad$\{\,$switch
+ from \\{l\_packets} to \\{r\_packets}$\,\}$\par\noindent
+\qquad\quad$\\{decr}(\\{cur\_tt})$;
+ $\\{xy}\gets\\{xy}-\\{int\_packets}$;\quad$\{\,$switch
+ from \\{r\_packets} to \\{l\_packets}$\,\}$
+
+\bugonpage D262, line 11 from the bottom (06/25/04)
+
+\ninepoint\noindent
+\qquad$\\{xy}\gets\\{xy}+\\{int\_packets}$;\quad$\{\,$switch
+ from \\{l\_packets} to \\{r\_packets}$\,\}$
+
+\bugonpage D274, line 15 from the bottom (06/25/04)
+
+\ninepoint\noindent
+\qquad\qquad{\bf begin if\/} $\\{serial\_no}>\\{el\_gordo}-\\{s\_scale}$
+ {\bf then}\par\noindent
+\qquad\qquad\quad$\\{overflow}(\.{"independent\]variables"},
+ \\{serial\_no}\mathbin{\hbox{\bf div}}\\{s\_scale})$;\par\noindent
+\qquad\qquad$\\{type}(\#)\gets\\{independent}$;
+ $\\{serial\_no}\gets\\{serial\_no}+\\{s\_scale}$;
+ $\\{value}(\#)\gets\\{serial\_no}$;
+
+\bugonpage D309, line 21 (06/25/04)
+
+\noindent
+{\bf670.\quad}We go to \\{restart} instead of to \\{switch},
+because we might enter \\{token\_state} after the error\cutpar
+
+\bugonpage D314, line 6 from the bottom (06/25/04)
+
+\noindent
+\\{macro\_def} or \\{iteration}).
+
+\bugonpage D330, line 1 (06/25/04)
+
+\noindent
+{\bf728.\quad}A {\bf suffix} or {\bf text} parameter will have been scanned as
+a token list pointed to by \\{cur\_exp},\cutpar
+
+\bugonpage D354, lines 15 and 16 from the bottom (06/25/04)
+
+\noindent\hangindent 3em
+$\\{cur\_type}=\\{unknown\_boolean}$ means that \\{cur\_exp} points to a
+capsule node that is in
+a ring of equivalent booleans whose value has not yet been defined.
+
+\bugonpage D354, lines 11 and 12 from the bottom (06/25/04)
+
+\noindent\hangindent 3em
+$\\{cur\_type}=\\{unknown\_string}$ means that \\{cur\_exp} points to a
+capsule node that is in
+a ring of equivalent strings whose value has not yet been defined.
+
+\bugonpage D354, lines 7 and 8 from the bottom (06/25/04)
+
+\noindent\hangindent 3em
+$\\{cur\_type}=\\{unknown\_pen}$ means that \\{cur\_exp} points to a
+capsule node that is in
+a ring of equivalent pens whose value has not yet been defined.
+
+\bugonpage D355, lines 1 and 2 (06/25/04)
+
+\noindent\hangindent 3em
+$\\{cur\_type}=\\{unknown\_path}$ means that \\{cur\_exp} points to a
+capsule node that is in
+a ring of equivalent paths whose value has not yet been defined.
+
+\bugonpage D355, lines 5 and 6 (06/25/04)
+
+\noindent\hangindent 3em
+$\\{cur\_type}=\\{unknown\_picture}$ means that \\{cur\_exp} points to a
+capsule node that is in
+a ring of equivalent pictures whose value has not yet been defined.
+
+\bugonpage D355, lines 21 and 22 (06/25/04)
+
+\noindent
+$\\{cur\_type}=\\{token\_list}$ means that \\{cur\_exp} points to a linked list
+of tokens.
+
+\bugonpage D356, lines 2--3 (06/25/04)
+
+\noindent
+nodes have $\\{name\_type}=\\{capsule}$,
+and their \\{type} field is one of the possibilities for \\{cur\_type}
+listed above.
+Also $\\{link}\le\\{void}$ in capsules that aren't part of a token list.
+
+\bugonpage D368, line 13 (06/25/04)
+
+\ninepoint\noindent
+\qquad\\{my\_var\_flag}: $0\to\\{max\_command\_code}$;\quad$\{\,$initial
+ value of \\{var\_flag}$\,\}$
+
+\bugonpage D378, line 9 from the bottom (06/25/04)
+
+\ninepoint\noindent
+\qquad\quad{\bf begin} $\\{cur\_type}\gets\\{known}$;
+ $\\{cur\_exp}\gets0$;
+ $\\{free\_node}(q,\\{dep\_node\_size})$;
+
+\bugonpage D380, line 12 (06/25/04)
+
+\ninepoint\noindent
+\qquad\qquad{\bf begin} $\\{type}(r)\gets\\{known}$;
+ $\\{value}(r)\gets0$;
+ $\\{free\_node}(p,\\{dep\_node\_size})$;
+
+\bugonpage D390, lines 2 and 3 (06/25/04)
+
+\noindent
+by a previous operation. We must maintain
+the value of $\\{right\_type}(q)$ in cases such as\break
+`|..\\{curl2\}z\{0,0\}..|'.
+
+\bugonpage D437, line 1 (06/25/04)
+
+\noindent
+{\bf996.\quad}And \\{do\_assignment} is similar to \\{do\_equation}:
+
+\bugonpage D439, line 10 becomes two lines (06/25/04)
+
+\ninepoint\noindent
+\qquad{\bf begin} $\\{nonlinear\_eq}(v,\\{cur\_exp},\\{false})$;
+ $\\{cur\_type}\gets t$;
+ {\bf goto} \\{done};
+
+\bugonpage D443, line 11 (06/25/04)
+
+\ninepoint\noindent
+\\{done}: {\bf if\/} $\\{eq\_type}(x)\mathbin{\hbox{\bf mod}}\\{outer\_tag}
+ \ne\\{tag\_token}$ {\bf then}
+ $\\{clear\_symbol}(x,\\{false})$;
+
+\bugonpage D452, line 9 (06/25/04)
+
+\noindent
+though they don't necessarily correspond to primitive tokens.
+
+\bugonpage D476, line 12 from the bottom (06/25/04)
+
+\ninepoint\noindent
+\quad{\bf if\/} $\\{nl}-\\{skip\_table}[c]>128$ {\bf then}
+
+\bugonpage D483, line 7 (06/25/04)
+
+\ninepoint\noindent
+\quad$\\{max\_tfm\_dimen}\gets16\ast\\{internal}[\\{design\_size}]-1
+ -\\{internal}[\\{design\_size}]\mathbin{\hbox{\bf div}}\Oct{10000000}$;
+
+\bugonpage D483, lines 15--17 (06/25/04)
+
+\ninepoint\noindent
+\qquad{\bf if\/} $x>0$ {\bf then}
+ $x\gets\\{max\_tfm\_dimen}$ {\bf else}
+ $x\gets-\\{max\_tfm\_dimen}$;\par\noindent
+\qquad{\bf end};\par\noindent
+\quad$x\gets\\{make\_scaled}(x\ast16,\\{internal}[\\{design\_size}])$;
+
+\bugonpage D496, line 2 (06/25/04)
+
+\noindent
+a pointer to
+an edge structure. Its mission is to describe the positive pixels
+in \.{GF} form,\cutpar
+
+\bugonpage D500, line 16 (06/25/04)
+
+\ninepoint\noindent
+\quad$\\{selector}\gets\\{old\_setting}$;
+ $\\{gf\_out}(\\{cur\_length})$;
+ $\\{gf\_string}(0,\\{make\_string})$;
+ $\\{decr}(\\{str\_ptr})$;
+
+\bugonpage D506, lines 8--10 (06/25/04)
+
+\noindent
+\MF\ it says,
+for example, `\.{(preloaded base=plain 1984.2.29)}', showing the year,
+month, and day that the base file was created. We have $\\{base\_ident}=0$
+before \MF's tables are loaded.
+
+\bugonpage D514, line 14 from the bottom (06/25/04)
+
+\noindent
+\.{CMMF}, should also be provided for commonly used bases such as \.{cmbase}.
+
+ % volume E
+\hsize=29pc
+
+\newbox\shorthyf \setbox\shorthyf=\hbox{-\kern-.05em}
+\mathchardef\period=`\.
+{\catcode`\-=\active \global\def-{\copy\shorthyf\mkern3.9mu}
+ \catcode`\.=\active \global\def.{\period\mkern3mu}}
+\def\8#1{\mathrel{\mathcode`\.="8000 \mathcode`\-="8000
+ #1\unkern}} % `..' and `--'
+
+\bugonpage E1, line 3 (01/06/06)
+
+\tenpoint\noindent
+Zillions of alphabets can be generated by the programs in this book.
+All\cutpar
+
+\bugonpage E6, lines 16--19 (12/29/04)
+
+\textindent\bull
+ {\it square\_dots\/} tells whether dots should be square, not rounded;\smallskip
+\textindent\bull
+ {\it hefty\/} tells whether weight-reducing strategies should be used;\smallskip
+\textindent\bull\hangindent\parindent
+ {\it monospace\/} tells whether the characters should all be forced to
+ have the same width;
+
+\bugonpage E7, line 11 (12/21/02)
+
+\ninepoint\indent
+\\{hair}, \\{vair}, \\{stem}, \\{curve}, \\{ess}, \\{flare}, \\{dot\_size},
+ \\{bar}, \\{slab},
+
+\bugonpage E7, line 14 (12/21/02)
+
+\ninepoint\indent
+\\{crisp}, \\{tiny}, \\{fine};
+\medskip\noindent
+and \\{thin\_join} should not be less than \\{fine}.
+
+\bugonpage E19, line 19 (11/07/01)
+
+\tenpoint
+\line{\\{cap\_notch\_cut}\hskip 0pt plus1.5fil46/36\hfil31/36\hfil25/36\hfil
+24/36\hfil22/36\hskip0pt plus3fil25/36}
+
+\bugonpage E41, line 8 (12/21/02)
+
+\ninepoint\noindent\mathchardef\AM="2026 % ampersand
+\quad$\\{extra\_endchar}\gets\\{extra\_endchar}\AM
+ \.{\char`"charcode:=charcode+code\_offset;"}$;
+
+\bugonpage E53, line 7 (12/21/02)
+
+\def\frac#1/#2{\leavevmode\kern.1em
+ \raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em
+ /\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}
+\ninepoint\noindent
+{\bf numeric} \\{mid\_thickness};
+ $\\{mid\_thickness}={\rm Vround}$ \frac1/3[$\\{vair},\\{stem}$];
+
+\bugonpage E125, line 6 from the bottom (07/10/05)
+
+\ninepoint\noindent
+$\\{top}\,y_1=\\{top}\,y_6=h$; $z_2=.5[z_3,z_1]+\\{bend}$;
+
+\bugonpage E125, line 3 from the bottom (07/10/05)
+
+\ninepoint\noindent
+{\bf draw} $z_1-\\{flourish\_change}\{\\{up}\}+(0,.15\\{asc\_height})
+ \{\\{up}\}$\par
+\line{\quad$\8{...}\{\\{right}\}(z_1+(2u,0))\8{---}z_6\8{...}\{\\{down}\}z_7$;
+ \hfil\% upper bar}
+
+\bugonpage E146, also pages 164 and 540 (02/08/03)
+
+\eightpoint\noindent
+[The labels on the new illustrations of beta, omega, and spadesuit
+are too large, and the resolution of the shapes is too small.]
+
+\bugonpage E147, line 11 from the bottom (04/23/04)
+
+\ninepoint\noindent
+$x_0=x_1=x_9$; $\\{lft}\,x_{0r}={\rm hround}(1.5u-.5\\{hair})$;
+$x_2=x_4=x_6=x_8=.5w-.25u$;
+
+\bugonpage E147, line 8 from the bottom (04/23/04)
+
+\ninepoint\noindent
+$y_5=.5[y_4,y_6]$; $\\{top}\,y_{6r}-\\{bot}\,y_{4r}=\\{vstem}+\\{eps}$;
+ $\\{bot}\,y_{8r}=-\\{oo}$; $y_7=y_9=.55[y_6,y_8]$;
+
+\bugonpage E149, line 8 from the bottom (04/23/04)
+
+\ninepoint\noindent
+$y_5+.1\\{x\_height}=y_7=.5[y_6,y_8]$; $\\{bot}\,y_{6r}=-\\{oo}$;
+
+\bugonpage E157, line 11 (02/29/08)
+
+\ninepoint\noindent
+\line{{\bf filldraw} $z_{1l}\8{--}z_{2l}\8{...}(x_3,y_{2l})\8{...}z\8{--}
+ z_{1r}\8{--}\rm cycle$;\hfil\% stem}
+
+\bugonpage E161, line 7 from the bottom (04/23/04)
+
+\ninepoint\noindent
+$\\{top}\,y_{1r}=\\{x\_height}+\\{oo}$; $y_2=y_4=.5[y_1,y_3]$;
+ $\\{bot}\,y_{3r}=-\\{oo}$;
+
+\bugonpage E209, line 3 (12/29/04)
+
+\ninepoint
+\rightline{\% This lowercase italic alphabet was prepared by D. E. Knuth
+ in December, 1979,}
+
+\bugonpage E377, lines 3 and 4 from the bottom (12/22/02)
+
+\ninepoint\noindent
+\qquad {\bf path} \\{p\_}; $\\{p\_}=z_{\$\$l}\{z_{@1}-z_{\$\$l}\}\8{...}
+ \\{darkness}[z_{@1},.5[z_{@2},z_{\$\$l}]]\8{...}z_{@2}$\par\noindent
+\qquad\quad$\8{---}z_{\$l}\8{--}z_{\$r}\8{--}z_{@0}\8{--}z_{\$\$r}\8{--}%
+ {\rm cycle}$;\par\noindent
+\qquad{\bf if\/} $(y_{\$\$}>y_\$) \ne ({\rm ypart}\,\hbox{\bf precontrol}\,1
+ \,\hbox{\bf of\/}\,\\{p\_} > {\rm ypart}\,\hbox{\bf postcontrol}\,1\,
+ \,\hbox{\bf of\/}\,\\{p\_})$:\par\noindent
+\qquad\quad$\\{p\_}=z_{\$\$l}\{z_{@1}-z_{\$\$l}\}\8{...}
+ \\{darkness}[z_{@1},.5[z_{@2},z_{\$\$l}]]$\par\noindent
+\qquad\qquad$\8{---}z_{\$l}\8{--}z_{\$r}\8{--}z_{@0}\8{--}z_{\$\$r}\8{--}%
+ {\rm cycle}$;\ {\bf fi}\par\noindent
+\line{\qquad {\bf filldraw} \\{p\_};\hfil \% arm and beak}
+
+\bugonpage E379, lines 17 and 18 become one line (01/06/14)
+
+\ninepoint\noindent
+{\bf else}: $\\{rt}\,x_{6r}={\rm hround}(w-1.5u)$;
+$y_{6}=y_{5l}+\\{eps};$ {\bf fi}
+
+\bugonpage E379, bottom line of the program (01/06/14)
+
+\def\SH{\raise.7ex\hbox{$\scriptstyle\#$}} % sharp sign for sharped units
+\ninepoint\noindent
+{\bf math\_fit}$(0,\\{ic}\SH-2.5u\SH)$;
+{\bf penlabels}$(0,1,2,3,4,5,6,7)$;
+{\bf endchar};
+
+\bugonpage E489, bottom line (06/25/04)
+
+\ninepoint\noindent
+{\bf labels}$(1,2,3,4,5,6)$; {\bf endchar};\hfil\break
+[Labels `\.5' and `\.6' should also be added to
+ the lower illustration on page E488.]
+
+\bugonpage E545, line 11 from the bottom (12/29/04)
+
+\parindent=36pt
+The most important general routine in |cmbase| is probably the {\it pos}\cutpar
+
+\bugonpage E551, line 3 from the bottom (12/29/04)
+
+\noindent quantities needed in the |calu|
+programs are also established at this time.
+
+
+\bugonpage E577, right column (12/23/02)
+
+\eightpoint\noindent
+\\{p\_}\kern1pt, 305, 377.\par\noindent
+{\bf padded}, 103--111, 117--121, $\underline{549}$.
+
+\bugonpage E578, left column (12/23/02)
+
+\eightpoint\noindent
+{\bf postcontrol}, 347, 377.\par\noindent
+{\bf precontrol}, 347, 377.
+
+ \bye
diff --git a/systems/knuth/dist/errata/errata.three b/systems/knuth/dist/errata/errata.three
new file mode 100644
index 0000000000..5f00f73b22
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.three
@@ -0,0 +1,1898 @@
+% Bugs (sigh) in Computers \& Typesetting
+
+\input manmac
+\font\sltt=cmsltt10
+\font\niness=cmss9
+\font\ninessi=cmssi9
+\proofmodefalse
+\raggedbottom
+\output{\hsize=29pc \onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\cutpar{{\parfillskip=0pt\par}}
+
+\def\rhead{Bugs in {\tensl Computers \& Typesetting}}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\buginvol#1(#2) \par{\bigbreak\penalty-1000\tenpoint
+ \hrule width\hsize
+ \line{\lower3.5pt\vbox to13pt{}Volume #1\hfil(#2)}\hrule width\hsize
+ \nobreak\medskip}
+\def\slMF{{\manual 89:;}\-{\manual <=>:}} % slant the logo
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\newcount\nn
+\newdimen\nsize \newdimen\msize \newdimen\ninept \ninept=9pt
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+
+\noindent This is a list of all corrections made to {\sl Computers \&
+Typesetting}, Volumes A--E\null, between the date of publication
+(May, 1986) and 15~June 1987.
+It also includes corrections made to
+the softcover version of {\sl The \TeX book}, beginning with the
+sixth printing (January 1986); these are the same as corrections to
+Volume~A\null. Corrections to the softcover version of {\sl The
+\slMF\kern1ptbook\/} are the same as corrections to Volume~C\null.
+ % volume A
+\bugonpage A7, fourth line from the bottom (6/28/86)
+
+\tenpoint\line{%
+since control sequences of the second kind always have exactly one
+symbol after}
+
+\bugonpage A35, second-last line (1/31/87)
+
+\rightline{\eightssi He may run who reads.}
+\smallskip
+\rightline{\eightss--- HABAKKUK 2\thinspace:\thinspace2 (c.~600 B.C.)}
+\smallskip
+\rightline{\eightssi He that runs may read.}
+
+\bugonpage A43, lines 8--9 (8/23/86)
+
+\tenpoint\noindent
+of Appendix B\null, which defines |%| to be a special kind of symbol so that you
+can use it for comments, defines the control sequence |\%| to mean
+a percent sign.
+
+\bugonpage A45, lines 10--13 (8/23/86)
+
+\ninepoint\noindent
+\TeX\ adds~64. Hence
+code 127 can be typed |^^?|, and
+the dangerous bend sign can be obtained by saying
+|{\manual^^?}|. However, you must change the category code of character
+127 before using it, since this character ordinarily has category~15
+(^{invalid}); say, e.g., |\catcode`\^^?=12|.
+The |^^| notation is different from |\char|, because |^^|\cutpar
+
+\bugonpage A76, line 7 (8/23/86)
+
+\ninepoint
+\noindent
+and extra space; for example, these quantities are
+$3.33333\pt$, $1.66666\pt$, $1.11111\pt$,\cutpar
+
+\bugonpage A83, bottom line (5/19/87)
+
+\tenpoint\noindent[This line should be flush right.]
+
+\bugonpage A111, 7th-last line, right-hand column (2/15/87)
+
+\ninepoint
+if $b=10000$ and $-10000<p<10000$ and $q<10000$;
+
+\bugonpage A117, second-last line (6/10/87)
+
+\eightpoint
+marks; sometimes also |$\|\||$| ($\Vert$).
+You can say, e.g., `|\footnote\dag{...}|'.
+
+\bugonpage A124, lines 6--11 (2/26/87)
+
+\begingroup \def\n{\thinspace$n$}
+\ninepoint\noindent
+of insertion; an additional `|\penalty-10000|' item is assumed
+to be present at the end of the vertical list, to ensure that a legal
+breakpoint exists.) \ Let $u$ be the natural height plus depth of that
+least-cost box, and let $r$ be the penalty associated with the optimum
+breakpoint. Decrease $g$ by~$uf$, and increase $q$ by~$r$. \ (If
+|\tracingpages||=1|, the log file should now get a cryptic message that says
+`|% split|\n\ |to| $v$|,|$u$ |p=|$r$'. For~example,
+\begintt
+% split254 to 180.2,175.3 p=100
+\endtt
+\endgroup
+
+\bugonpage A158, lines 6--8 (2/20/87)
+
+\ninepoint\noindent the
+second atom, which has subscript~$i$; the superscripts are empty except for the
+last atom, whose superscript is~$\overline{n+1}$. This superscript is
+itself a math list consisting of one atom, whose nucleus is~$n+1$; and that
+nucleus is a math list consisting of three atoms.
+
+\bugonpage A171, line 20 (1/26/86)
+
+\ninepoint\line{%
+will be surrounded by more space than there would be
+if that subformula were enclosed}
+
+\bugonpage A176, line 1 (8/23/86)
+
+\ninepoint
+You can insert `|\noalign||{|$\langle$vertical mode
+material$\rangle$|}|' just after any \kern-1pt|\cr| within\cutpar
+
+\bugonpage A248, line 17 (6/17/86)
+
+\ninepoint
+`|&|' or `|\span|' or `|\cr|', it needs some way to decide which
+alignment is involved.\cutpar
+
+\bugonpage A249, line 20 (6/17/86)
+
+\ninepoint\noindent
+line (see Chapter~8).
+If you don't want a~|\cr| at the end of a certain line,
+just type\cutpar
+
+\bugonpage A276, line 19 (1/27/86)
+
+\ninepoint\vskip-3pt
+\beginsyntax
+ \alt^|\font|<control sequence><equals><file name><at clause>
+ \alt<global assignment>
+\endsyntax
+[The bottom line of p.~276 will now move to the top of p.~277.]
+
+\bugonpage A277, lines 31--32 (1/27/86)
+
+\ninepoint
+\beginsyntax
+<font assignment>\is^|\fontdimen|<number><font><equals><dimen>
+\endsyntax
+
+\bugonpage A286, sixth-last line (4/28/87)
+
+\ninepoint\noindent
+|\sfcode| table as described in Chapter~12; characters numbered 128
+to~255 set the\cutpar
+
+\bugonpage A287, line 19 (2/15/87)
+
+\ninepoint
+\textindent{$\bull$}|\-|.\enskip
+This ``discretionary hyphen'' command is defined in Appendix H.
+
+\bugonpage A292, lines 9--10 (2/15/87)
+
+\ninepoint
+\textindent{$\bull$}|\-|.\enskip
+This command is usually equivalent to `|\discretionary{-}{}{}|'; the `|-|' is
+therefore interpreted as a ^{hyphen}, not as a minus sign.
+\ (See Appendix~H.)
+
+\bugonpage A308, lines 25--26 (6/1/87)
+
+\ninepoint\indent
+|\def\appendroman#1#2#3{\edef#1{\csname|\parbreak
+| \expandafter\gobble\string#2\romannumeral#3\endcsname}}|
+
+\bugonpage A312, lines 10--14 (8/23/86)
+
+\ninepoint
+\ansno12.11: The interline glue will be zero, and the natural height is
+$1+1-3+2=1\pt$ (because the depth of\/ |\box2| isn't included in the natural
+height); so the glue will ultimately become |\vskip-1pt| when it's set.
+Thus, |\box3| is $3\pt$ high, $2\pt$ deep, $4\pt$ wide. Its reference
+point coincides with that of\/ |\box2|; to get to the reference point
+of\/ |\box1| you go up $2\pt$ and right $3\pt$.
+
+\bugonpage A312, line 21 (8/23/86)
+
+\ninepoint\noindent
+up $4\pt$ to get to the upper left corner of
+|\box4|; then down $-1.6\pt$, i.e., up $1.6\pt$, to\cutpar
+
+\bugonpage A319, line 20 (31/3/87)
+
+\ninepoint\noindent
+make ordinary periods act like |\cdot| symbols: Just define
+|\mathcode`.| to be |"0201|,\cutpar
+
+\bugonpage A328, lines 18--19 (5/14/87)
+
+\ninepoint\noindent
+not performed
+while the expansion is taking place, and the control sequences following
+|\def| are expanded; so the result is an infinite string
+\begintt
+A\def A\def A\def A\def A\def A\def A\def A\def A...
+\endtt
+
+\bugonpage A329, lines 14--15 (8/23/86)
+
+\ninepoint
+\ansno20.5: The |##| feature is indispensable when the replacement text of
+a definition contains other definitions. For example, consider
+
+\bugonpage A356, lines 6--7 (1/30/87)
+
+\ninepoint\noindent
+| \spaceskip=.3333em \xspaceskip=.5em\relax}|\hfil\break
+|\def\ttraggedright{\tt\rightskip=0pt plus2em\relax}|
+
+\bugonpage A356, line 33 (6/1/87)
+
+\ninepoint\noindent
+| \vbox to.2ex{\hbox{\char'26}\vss}\hidewidth}}|
+
+\bugonpage A357, tenth-last line (10/13/86)
+
+\ninepoint\noindent
+|\let\sp=^ \let\sb=_ {\catcode`\_=\active \global\let_=\_}|
+
+\bugonpage A357, third-last and second-last lines (2/17/87)
+
+\ninepoint\noindent
+|\def\pr@m@s{\ifx'\next\let\nxt\pr@@@s \else\ifx^\next\let\nxt\pr@@@t|%
+\hfil\break\strut
+| \else\let\nxt\egroup\fi\fi \nxt}|
+
+\bugonpage A364, fifth-last line (1/30/87)
+
+\ninepoint\noindent
+|\def\fmtname{plain}\def\fmtversion{2.3} % identifies the current format|
+
+\bugonpage A368, bottom line (2/26/86)
+
+\ninepoint
+\line{that includes the symbols
+{\tentex\char'30},~{\tentex\char1}, {\tentex\char'32}, {\tentex\char'34},
+and~{\tentex\char'35}, and he finds that this makes it much more}
+
+\bugonpage A396, line 13 (8/23/86)
+
+\ninepoint
+| \hyphenpenalty=10000 \exhyphenpenalty=10000|
+
+\bugonpage A414, line 10 (3/4/86)
+
+\ninepoint\noindent
+|\font\titlefont=cmssdc10 at 40pt % titles in chapter openings|
+
+\bugonpage A427, line 7 (2/23/86)
+
+\ninepoint\noindent
+the author's book
+{\sl Computer Modern Typefaces}.)
+
+\bugonpage A428, lines 18--20 (6/15/87)
+
+\tenpoint\noindent
+The first eight of these all have essentially the same layout;
+but |cmr5| needs no ligatures, and many of the symbols of |cmti10|
+have different shapes.
+For example, the ^{ampersand} becomes an `^{E.T.}', and the
+^{dollar} changes to ^{pound} ^{sterling}:
+
+\bugonpage A434, lines 25--28 (8/17/86)
+
+\tenpoint\noindent
+from |\nu|~($\nu$). Similarly,
+|\varsigma|~($\varsigma$) should not be confused with |\zeta|~($\zeta$).
+It turns out that |\varsigma| and |\upsilon| are almost never used in
+math formulas; they are included in plain \TeX\ primarily because they are
+sometimes needed in short Greek citations (cf.~Appendix~J).
+
+\bugonpage A447, line 32 (6/1/87)
+
+\ninepoint\noindent
+ters
+also affect mathematical typesetting:
+dimension parameters
+ \hbox{|\delimitershortfall|}\cutpar
+
+\bugonpage A455, new paragraph to follow line 9 (2/15/87)
+
+\begingroup
+\hyphenpenalty=-1000 \pretolerance=-1 \tolerance=1000
+\doublehyphendemerits=-100000 \finalhyphendemerits=-100000
+\ddanger The control sequence ^|\-| is equivalent to
+|\discretionary{\char|$\,h$|}{}{}|, where $h$ is the
+^|\hyphenchar| of the current font, provided that $h$ lies
+between 0 and~255. Otherwise |\-| is equivalent to |\discretionary{}{}{}|.
+
+\endgroup % end the special hyphenation conventions
+
+\bugonpage A457, left column, fifth-last line (2/17/87)
+
+\eightpoint\indent\qquad 155, 201, {\it 305}, 324, $\underline{357}$, 394--395;
+
+\bugonpage A458, left column, line 6 (2/15/87)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt\char`\\-} (discretionary hyphen), 95, 283, 287,\par
+\indent\qquad 292, $\underline{455}$.
+
+\bugonpage A458, left column, near the bottom (5/19/87)
+
+\eightpoint {\tt!} (exclamation point), 51,
+{\it 72}, 73, 75, {\it 169}.
+\nobreak\medskip\noindent[This saves a line that otherwise would make
+the index too long on page 481!]
+
+\bugonpage A458, right column, line 10 (11/27/86)
+
+\eightpoint {\tt\char`\~}
+(tilde), 38, 51, 343, $\underline{353}$; {\sl see also\/} ties.
+
+\bugonpage A458, right column (6/14/87)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt\char`\\accent} (general accent), 9, 54, 86, 283, $\underline{286}$.
+
+\bugonpage A461, entry for boxes (3/16/87)
+
+\eightpoint boxes, 63--67, 77--83, 221--229.
+
+\bugonpage A461, entry for {\tt\char`\\centering} (1/28/86)
+
+\eightpoint {\tt\char`\\centering}, $\underline{347}$, 348, 362.
+
+\bugonpage A462, entry for \<code assignment> (1/27/86)
+
+\eightpoint \<code assignment>, $\underline{277}$.
+
+\bugonpage A464, left column, line 3 (2/15/87)
+
+\eightpoint
+discretionary hyphens, 28, 95--96, 453, $\underline{455}$.
+
+\bugonpage A465, right column, line 8 (5/3/87)
+
+\eightpoint
+expansion of expandable tokens, 212--216, 238,
+
+\bugonpage A466, entry for {\tt\char`\\font}, second line (1/27/86)
+
+\eightpoint \indent\qquad 271, $\underline{276}$.
+
+\bugonpage A466, new entry (2/3/87)
+
+\eightpoint \indent\<fontdef token>, $\underline{271}$.
+
+\bugonpage A467, entry for {\tt\char`\\hideskip} (1/28/86)
+
+\eightpoint {\tt\char`\\hideskip}, $\underline{347}$, 348, 354.
+
+\bugonpage A468, left column line 2 (2/15/87)
+
+\eightpoint\indent\qquad 351, 395, {\it 414}, 454, 455.
+
+\bugonpage A470, entry for {\tt manfnt} (1/15/86)
+
+\eightpoint {\tt manfnt}, 44, 408, 414.
+
+\bugonpage A471, entry for {\tt\char`\\medbreak} (10/13/86)
+
+\eightpoint {\tt\char`\\medbreak}, 111, 113, $\underline{353}$,
+ {\it355}, {\it419}, {\it422}.
+
+\bugonpage A471, entry for {\tt\char`\\moveright} (2/27/87)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt\char`\\moveright}, 80--81, {\it 221}, $\underline{282}$.
+
+\bugonpage A471, entry for Mozart, second line (3/19/86)
+
+\eightpoint \indent\qquad Gottlieb (= Theophilus = Amadeus), 409.
+
+\bugonpage A472, the entry for {\tt\char`\\not} (2/12/87)
+
+\eightpoint\noindent
+[The overprinting here is intentional, since {\tt\char`\\not} is a
+character of width zero. More than a dozen people have reported this
+as an error, but it is not!]
+
+\bugonpage A477, entry for {\tt\char`\\span} (5/3/87)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt\char`\\span}, 215, 238, $\underline{243}$, {\it244}, $\underline{245}$,
+248, 249,\par
+\indent\qquad 282, {\it330}, 385.
+
+\bugonpage A479, entry for ties, second line (11/27/86)
+
+\eightpoint \indent\qquad {\it173}, 353, {\it404}.
+
+\bugonpage A480, changes to various entries (6/14/87)
+
+\eightpoint
+\newbox\astbox \setbox\astbox=\hbox to0pt{\hss\lower1pt\hbox{*}}
+\def\prim#1{\par\indent\copy\astbox{\tt\char`\\#1}}
+\prim{underline}, {\it130--131}, 141, 291, $\underline{443}$.
+\prim{unhbox}, 120, 283, $\underline{285}$, {\it354}, {\it356}, {\it361},
+ {\it399}.
+\prim{unhcopy}, 120, 283, $\underline{285}$, {\it353}.
+\prim{unkern}, $\underline{280}$.
+\prim{unpenalty}, $\underline{280}$.
+\prim{unskip}, 222--223, $\underline{280}$, 286, {\it313}, {\it392},
+ {\it418--419}.
+\prim{unvbox}, 120, 254, $\underline{282}$, 286, {\it354}, {\it361},
+ {\it363}, {\it364}, {\it392}, {\it399}, {\it417}.
+\prim{unvcopy}, 120, $\underline{282}$, 286, {\it361}.
+\prim{vadjust}, 95, 105, 109, 110, 117, 259, $\underline{281}$, 393, 454.
+\prim{valign}, 249, 283, $\underline{285}$--$\underline{286}$, 302,
+ {\it335}, {\it397}.
+\prim{vcenter}, 150--151, 159, 170, 193, 222, 242,
+\prim{vfil}, 71, $\underline{72}$, 111, 256, 281, 286, 417.
+\prim{vfill}, 24, 25, 71, $\underline{72}$, 256--257, 281, 286.
+\prim{vfilneg}, $\underline{72}$, 111, 281, 286.\par
+|\voidb@x|, $\underline{347}$, 348.
+
+\bugonpage A481, left column (6/14/87)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt\char`\\vss}, 71, $\underline{72}$, {\it 255}, 281, 286.
+
+ % volume B
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\buginvol B, in general (7/28/86)
+
+\tenpoint\noindent
+[A number of entries were mistakenly omitted from the mini-indexes
+on the right-hand pages. Here is a combined list of all the missing
+items; you can mount it inside the back cover, say, as a secondary mini-index
+when the first one fails\dots\ ]
+
+\nobreak\medskip
+\setbox0=\vbox{\eightpoint \hsize=11pc \catcode`\_=\active \let_=\_
+ \rightskip=0pt plus 100pt minus 10pt
+ \pretolerance 10000
+ \hyphenpenalty 10000 \exhyphenpenalty 10000
+ \noindent\vbox to1pt{}\par % 1pt = \topskip - \ninept
+ \def\&#1{\hbox{\bf#1\/}} % boldface type for reserved words
+ \obeylines
+ \def\makeref #1 #2 #3#4
+ {\nn=#2 \hangindent=1em \noindent\\{#1}%
+ \if#3:: \else\unhcopy\eqbox \fi#4, \S\number\nn.\par}
+ \makeref active_base 222 =$1$
+ \makeref aux 213 =macro
+ \makeref begin_name 515 :\&{procedure}
+ \makeref big_switch 1030 =$60$
+ \makeref choice_node 689 =$15$
+ \makeref cur_boundary 271 :$0\to \\{save\_size}$
+ \makeref cur_c 724 :\\{quarterword}
+ \makeref cur_group 271 :\\{group\_code}
+ \makeref cur_i 724 :\\{four\_quarters}
+ \makeref cur_level 271 :\\{quarterword}
+ \makeref do_extension 1348 :\&{procedure}
+ \makeref dvi_buf 595 :\&{array}
+ \makeref dvi_gone 595 :\\{integer}
+ \makeref dvi_limit 595 :\\{dvi\_index}
+ \makeref dvi_offset 595 :\\{integer}
+ \makeref dvi_ptr 595 :\\{dvi\_index}
+ \makeref end_graf 1096 :\&{procedure}
+ \makeref error 82 :\&{procedure}
+ \makeref error_stop_mode 73 =$3$
+ \makeref font_base 12 =$0$
+ \makeref font_info 549 :\&{array}
+ \makeref get_token 365 :\&{procedure}
+ \makeref glue_base 222 =$2626$
+ \makeref half_buf 595 :\\{dvi\_index}
+ \makeref handle_right_brace 1068 :\&{procedure}
+ \makeref hash_base 222 =$258$
+ \makeref head 213 =macro
+ \makeref hyf_distance 921 :\&{array}
+ \makeref hyf_next 921 :\&{array}
+ \makeref hyf_num 921 :\&{array}
+ \makeref index 302 =macro
+ \makeref inf 448 :\\{boolean}
+ \makeref init_col 788 :\&{procedure}
+ \makeref init_span 787 :\&{procedure}
+ \makeref input_ln 31 :\&{function}
+ \makeref interaction 73 :$0\to 3$
+ \makeref limit 302 =macro
+ \makeref line_width 830 :\\{scaled}
+ \makeref macro_call 389 :\&{procedure}
+ \makeref main_control 1030 :\&{procedure}
+ \makeref mem 116 :\&{array}
+ \makeref mem_bot 12 =$0$
+ \makeref mem_end 118 :\\{pointer}
+ \makeref mem_top 12 =macro
+ \makeref mlist_to_hlist 726 :\&{procedure}
+ \makeref mode 213 =macro
+ \makeref mode_line 213 =macro
+ \makeref more_name 516 :\&{function}
+ \makeref mu 448 :\\{boolean}
+ \makeref name 302 =macro
+ \makeref nest 213 :\&{array}
+ \makeref off_save 1064 :\&{procedure}
+ \makeref open_log_file 534 :\&{procedure}
+ \makeref output_active 989 :\\{boolean}
+ \makeref p 498 :\\{pointer}
+ \makeref param_stack 308 :\&{array}
+ \makeref pool_file 50 :\\{alpha\_file}
+ \makeref pool_ptr 39 :\\{pool\_pointer}
+ \makeref prefixed_command 1211 :\&{procedure}
+ \makeref prev_depth 213 =macro
+ \makeref prev_graf 213 =macro
+ \makeref prev_prev_r 830 :\\{pointer}
+ \makeref print_err 73 =macro
+ \makeref r 960 :\\{trie\_pointer}
+ \makeref reconstitute 906 :\&{function}
+ \makeref resume_after_display 1200 :\&{procedure}
+ \makeref save_ptr 271 :$0\to \\{save\_size}$
+ \makeref save_stack 271 :\&{array}
+ \makeref scan_dimen 448 :\&{procedure}
+ \makeref scan_math 1151 :\&{procedure}
+ \makeref short_display 174 :\&{procedure}
+ \makeref show_node_list 182 :\&{procedure}
+ \makeref start 302 =macro
+ \makeref state 302 =macro
+ \makeref str_pool 39 :\&{packed}\ \&{array}
+ \makeref str_ptr 39 :\\{str\_number}
+ \makeref str_start 39 :\&{array}
+ \makeref tail 213 =macro
+ \makeref trap_zero_glue 1229 :\&{procedure}
+ \makeref trie 921 :\&{array}
+ \makeref trie_char 921 =macro
+ \makeref trie_link 921 =macro
+ \makeref trie_op 921 =macro
+ \makeref vlist_out 629 :\&{procedure}
+ \makeref write_loc 1345 :\\{pointer}
+ }
+\hbox{\nsize=\ht0 \advance\nsize-\topskip
+ \divide\nsize by 3 \divide\nsize by\ninept
+ \multiply\nsize by\ninept \advance\nsize\topskip
+ \vsplit0 to\nsize \kern1pc
+ \msize=\ht0 \advance\msize-\topskip
+ \divide\msize by 2 \divide\msize by\ninept
+ \multiply\msize by\ninept \advance\msize\topskip
+ \vbox to\nsize{\vsplit0 to\msize\vss}\kern1pc
+ \vbox to\nsize{\box0\vss}}
+
+\buginvol B, in general (4/6/87)
+
+\tenpoint\noindent[The percent signs in all the comments (for example,
+on pages 7 and 50) are in the wrong font! Change `{\tt\%}' to `\%'.]
+
+\bugonpage Bvi, bottom line, and top line of next page (10/12/86)
+
+{\hsize=29pc
+\tenpoint\noindent
+puter Science Report 1097 (Stanford, California, April 1986), 146~pp.
+\ {\it The {\sltt WEB} programs for four utility programs that are
+often used with \TeX: {\sltt POOLtype}, {\sltt TFtoPL},
+{\sltt PLtoTF}, and {\sltt DVItype}.}
+\par}
+
+\bugonpage B2, line 32 (4/22/87)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{banner}\equiv\hbox{\tt\char'23}$%
+{\tt This\]is\]TeX,\]Version\]2.2\char'23}\quad
+$\{\,$printed when \TeX\ starts$\,\}$
+
+\bugonpage B7, new line after line 25 (1/28/87)
+
+\ninepoint\noindent\hskip10pt
+{\bf if} $\\{max\_in\_open}\ge128$ {\bf then} $\\{bad}\gets6$;
+
+\bugonpage B13, first three lines (4/7/87)
+
+\tenpoint\noindent
+The `\\{name}' parameter, which is of type `{\bf packed array
+$[\langle\\{any}\rangle]$ of \\{char}}', stands for the name of
+the external file that is being opened for input or output.
+Blank spaces that might appear in \\{name} are ignored.
+
+\bugonpage B14, line 30 (4/7/87)
+
+\tenpoint\noindent
+{\bf 31.\quad}%
+The \\{input\_ln} function brings the next line of input from the specified
+file into available\cutpar
+
+\bugonpage B18, line 30 (5/22/86)
+
+\ninepoint\noindent
+\\{str\_ptr}: \\{str\_number};\quad
+$\{\,$number of the current string being created$\,\}$
+
+\bugonpage B21, first line of mini-index, right column (6/14/87)
+
+\eightpoint
+\indent\\{pool\_name}\unhcopy\eqbox|"string"|, \S11.
+
+\bugonpage B34, lines 5--6 (6/14/87)
+
+\tenpoint\noindent
+to delete a token, and/or if some fatal error
+occurs while \TeX\ is trying to fix a non-fatal one. But such recursion
+is never more than two levels deep.
+
+\bugonpage B55, lines 12--13 (4/21/87)
+
+\ninepoint\noindent\hskip10pt
+{\bf if} $r=p$ {\bf then if} $\\{rlink}(p)\ne p$ {\bf then}
+ $\langle\,$Allocate entire node $p$ and {\bf goto} \\{found}%
+ {\sevenrm\kern.5em129}$\,\rangle$;
+
+\bugonpage B57, lines 25--28 (6/14/87)
+
+\tenpoint\noindent
+The first of these has $\\{font}=\\{font\_base}$, and its \\{link}
+points to the second;
+the second identifies the font and the character dimensions.
+The saving feature about oriental characters is that most of them have
+the same box dimensions. The \\{character} field of the first \\{char\_node}
+is a ``\\{charext}'' that distinguishes between graphic symbols whose
+dimensions are identical for typesetting purposes. (See the \MF\ manual.)
+Such an extension of \TeX\ would not be difficult; further details are
+left to the reader.
+
+\bugonpage B58, second line of section 136 (7/23/86)
+
+\tenpoint\noindent
+the values corresponding to `|\hbox{}|'. The \\{subtype} field is set to
+\\{min\_quarterword}, since that's\cutpar
+
+\bugonpage B66, lines 2--8 (4/21/87)
+
+\tenpoint\noindent
+location is
+more efficient than dynamic allocation when we can get away with it. For
+example, locations \\{mem\_bot} to $\\{mem\_bot}+3$ are always used to store the
+specification for glue that is `\hbox{\tt 0pt plus 0pt minus 0pt}'. The
+following macro definitions accomplish the static allocation by giving
+symbolic names to the fixed positions. Static variable-size nodes appear
+in locations \\{mem\_bot} through \\{lo\_mem\_stat\_max}, and static
+single-word nodes appear in locations \\{hi\_mem\_stat\_min} through
+\\{mem\_top}, inclusive. It is harmless to let \\{lig\_trick} and
+\\{garbage} share the same location of \\{mem}.
+
+\bugonpage B67, line 23 (4/13/87)
+
+\ninepoint\noindent\hskip30pt
+$\{\,$previous \\{mem\_end}, \\{lo\_mem\_max}, and \\{hi\_mem\_min}$\,\}$
+
+\bugonpage B71, line 17 (4/15/87)
+
+\ninepoint\noindent\hskip10pt
+{\bf begin while} $p>\\{mem\_min}$ {\bf do}
+
+\smallskip\eightpoint\noindent[Now \\{null} can be removed from the mini-index.]
+
+\bugonpage B74, line 24 (4/15/87)
+
+\ninepoint\noindent
+{\bf procedure} \\{show\_node\_list}($p\;{:}\;\\{integer}$);\quad
+$\{\,$prints a node list symbolically$\,\}$
+
+\bugonpage B74, line 33 (4/15/87)
+
+\ninepoint\noindent\hskip10pt
+{\bf while} $p>\\{mem\_min}$ {\bf do}
+
+\bugonpage B84, line 12 (2/15/87)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{relax}=0$\quad$\{\,$do nothing ( {\tt\char`\\relax} )$\,\}$
+
+\bugonpage B86, third line of section 210 (8/23/86)
+
+\tenpoint\noindent
+that their special nature is easily discernible.
+The ``expandable'' commands come first.
+
+\bugonpage B88, line 23 (5/22/86)
+
+\ninepoint\noindent
+{\bf procedure\/}\ $\\{print\_mode}(m:\\{integer})$;\quad
+$\{\,$prints the mode represented by $m\,\}$
+
+\bugonpage B93, lines 3--4 (8/17/86)
+
+{\tenpoint\parindent=1em
+In the first region we have 128 equivalents for ``active characters'' that
+act as control sequences, followed by 128 equivalents for single-character
+control sequences.
+\par}
+
+\bugonpage B130, ninth-last line (5/7/87)
+
+\tenpoint\noindent
+This variable has six possible values:
+
+\bugonpage B151, line 9 (4/22/87)
+
+\ninepoint\noindent\hskip20pt
+{\bf begin if} $(\\{end\_line\_char}<0)\lor(\\{end\_line\_char}>127)$
+ {\bf then} \\{incr}(\\{limit});\par\noindent\hskip20pt
+{\bf if} $\\{limit}=\\{start}$ {\bf then}\quad
+ $\{\,$previous line was empty$\,\}$
+
+\bugonpage B160, lines 17--20 (7/28/86)
+
+\tenpoint\noindent
+{\bf 389.\quad}%
+After parameter scanning is complete, the parameters are moved to the
+\\{param\_stack}. Then the macro body is fed to the scanner; in other words,
+\\{macro\_call} places the defined text of the control sequence at the
+top of\/ \TeX's input stack, so that \\{get\_next} will proceed to read it
+next.
+
+\bugonpage B200, top line (5/5/87)
+
+\tenpoint\noindent{\bf 495.\quad}%
+ When we begin to process a new {\tt\char`\\if}, we set
+$\\{if\_limit}\gets\\{if\_code}$; then
+if\/ {\tt\char`\\or} or {\tt\char`\\else} or {\tt\char`\\fi}\cutpar
+
+\bugonpage B217, lines 15--16 (6/14/87)
+
+\tenpoint\noindent
+|DVI| format.
+
+\bugonpage B224, lines 4--7 of section 560 (10/22/86)
+
+\tenpoint\noindent
+name and area strings \\{nom} and \\{aire}, and the
+``at'' size~$s$. If $s$~is negative, it's the negative of a scale factor
+to be applied to the design size; $s=-1000$ is the normal case.
+Otherwise $s$ will be substituted for the design size; in this
+case, $s$ must be positive and less than $2048\rm\,pt$
+(i.e., it must be less than $2^{27}$ when considered as an integer).
+
+\bugonpage B224, second-last line (4/28/87)
+
+\ninepoint\noindent
+\\{done}: {\bf if} \\{file\_opened} {\bf then} \\{b\_close}(\\{tfm\_file});\par
+\noindent\hskip10pt $\\{read\_font\_info}\gets g$;
+
+\bugonpage B255, mini-index at the bottom (4/15/87)
+
+\eightpoint
+$\\{mag}=\rm macro$, \S236.
+
+\bugonpage B257, lines 11--13 (6/14/87)
+
+\ninepoint
+\noindent\hskip20pt{\bf if} $c\ge\\{qi}(128)$ {\bf then}
+ \\{dvi\_out}(\\{set1});\par
+\noindent\hskip20pt\\{dvi\_out}(\\{qo}($c$));
+
+\bugonpage B260, lines 7--8 (4/15/87)
+
+\tenpoint\noindent\hskip10pt
+In the case of \\{c\_leaders} (centered leaders), we want to increase \\{cur\_h}
+by half of the excess space not occupied by the leaders; and in the
+case of \\{x\_leaders} (expanded leaders) we increase\cutpar
+
+\bugonpage B267, mini-index at the bottom (4/15/87)
+
+\eightpoint
+\\{cur\_s}: \\{integer}, \S616.
+$\\{mag}=\rm macro$, \S236.
+$\\{pop}=142$, \S586.
+
+\bugonpage B271, line 10 (8/23/86)
+
+\tenpoint\noindent
+which will be ignored in the calculations
+because it is a highly negative number.
+
+\bugonpage B285, lines 23 and 24 (5/4/87)
+
+\tenpoint\noindent
+the current string would be `{\tt.\char`\^.\char`\_/}'
+if $p$ points to the \\{ord\_noad} for $x$ in the (ridiculous) formula
+`{\tt\char`\\sqrt\char`\{a\char`\^\char`\{\char`\\mathinner\char`\{%
+b\char`\_\char`\{c\char`\\over x+y\char`\}\char`\}\char`\}\char`\}\char`\$}'.
+
+\bugonpage B296, lines 3--5 (5/8/87)
+
+\tenpoint\noindent
+box~$b$ and
+changes it so that the new box is centered in a box of width~$w$.
+The centering is done by putting {\tt\char`\\hss} glue at the left and right
+of the list inside $b$, then packaging the new box; thus, the
+actual box might not really be centered, if it already contains
+infinite glue.
+
+\bugonpage B346, line 19 (5/19/87)
+
+\ninepoint\noindent
+\\{pass\_number}: \\{halfword};\quad
+$\{\,$the number of passive nodes allocated on this pass$\,\}$
+
+\bugonpage B350, lines 36 and 37 (1/28/87)
+
+\ninepoint\noindent
+$v$: \\{pointer};\quad
+$\{\,$points to a glue specification or a node ahead of \\{cur\_p}$\,\}$
+\par\noindent
+$t$: \\{integer};\quad
+$\{\,$node count, if \\{cur\_p} is a discretionary node$\,\}$
+
+\bugonpage B353, lines 8--22 (1/28/87)
+
+\ninepoint
+\noindent\hskip10pt$s\gets\\{cur\_p}$;\par
+\noindent\hskip10pt{\bf if} $\\{break\_type}>\\{unhyphenated}$ {\bf then}
+ {\bf if} $\\{cur\_p}\ne\\{null}$ {\bf then}\par
+\noindent\hskip30pt$\langle\,$Compute the discretionary
+ \\{break\_width} values{\sevenrm\kern.5em840}$\,\rangle$;\par
+\noindent\hskip10pt{\bf while} $s\ne\\{null}$ {\bf do}\par
+\noindent\hskip30pt\vdots\hskip30pt [as before, but indented one less notch]\par
+\noindent\hskip10pt{\bf end};
+
+\bugonpage B354, line 6 (1/28/87)
+
+\tenpoint\noindent
+will be the background plus $l_1$, so the length from \\{cur\_p} to \\{cur\_p}
+should be $\gamma+l_0+l_1-l$,
+minus the length of nodes that will be discarded after the discretionary break.
+
+\bugonpage B354, lines 12--18 (1/28/87)
+
+\ninepoint
+\noindent\hskip10pt{\bf begin} $t\gets\\{replace\_count}(\\{cur\_p})$;\kern5pt
+ $v\gets\\{cur\_p}$;\kern5pt $s\gets\\{post\_break}(\\{cur\_p})$;\par
+\noindent\hskip10pt{\bf while} $t>0$ {\bf do}\par
+\noindent\hskip20pt{\bf begin} $\\{decr}(t)$;\kern5pt
+ $v\gets\\{link}(v)$;\kern5pt
+ $\langle\,$Subtract the width of node $v$ from \\{break\_width}%
+ {\sevenrm\kern.5em841}$\,\rangle$;\par
+\noindent\hskip20pt{\bf end};\par
+\noindent\hskip10pt{\bf while} $s\ne\\{null}$ {\bf do}\par
+\noindent\hskip20pt{\bf begin} $\langle\,$Add the width of
+ node $s$ to \\{break\_width} and increase $t$, unless it's
+ discardable{\sevenrm\kern.5em842}$\,\rangle$;\par
+
+\bugonpage B354, new line after line 21 (1/28/87)
+
+\ninepoint\noindent\hskip10pt
+{\bf if} $t=0$ {\bf then} $s\gets\\{link}(v)$;\quad
+ $\{\,$more nodes may also be discardable after the break$\,\}$
+
+\bugonpage B354, lines 26--34 (1/28/87)
+
+\ninepoint\noindent
+[Change `$s$' to `$v$' throughout this section (8 times).]
+
+\bugonpage B354, line 9 from the bottom (1/28/87)
+
+\tenpoint\noindent{\bf 842.\quad}%
+\ninepoint$\langle\,$Add the width of
+ node $s$ to \\{break\_width} and increase $t$, unless it's
+ discardable{\sevenrm\kern.5em842}$\,\rangle\equiv$
+
+\bugonpage B355, lines 1--3 (1/28/87)
+
+\ninepoint
+\noindent\hskip20pt$\\{hlist\_node},\\{vlist\_node},\\{rule\_node}$:
+ $\\{break\_width}[1]\gets\\{break\_width}[1]+\\{width}(s)$;\par
+\noindent\hskip20pt\\{kern\_node}: {\bf if} $(t=0)\land
+ (\\{subtype}(s)\ne\\{acc\_kern})$ {\bf then}
+ $t\gets-1$\quad$\{\,$discardable$\,\}$\par
+\noindent\hskip30pt{\bf else} $\\{break\_width}[1]\gets
+ \\{break\_width}[1]+\\{width}(s)$;\par
+\noindent\hskip20pt{\bf othercases}
+ \\{confusion}({\tt\char'42 disc2\char'42})\par
+\noindent\hskip20pt{\bf endcases};\par
+\noindent\hskip10pt$\\{incr}(t)$
+
+\bugonpage B355, patches to mini-index at bottom (1/28/87)
+
+\eightpoint
+$\\{acc\_kern}=2$, \S155.\par
+$\\{incr}=\rm macro$, \S16.\par
+$t$: \\{integer}, \S830.\par
+$v$: \\{pointer}, \S830.
+
+\bugonpage B372, lines 12--14 (1/28/87)
+
+\ninepoint
+\noindent\hskip40pt$\langle\,$Change discretionary to compulsory
+ and set $\\{disc\_break}\gets\\{true}${\sevenrm\kern.5em882}$\,\rangle$\par
+\noindent\hskip30pt{\bf else if\/} $(\\{type}(q)=\\{math\_node})\lor
+ (\\{type}(q)=\\{kern\_node})$ {\bf then} $\\{width}(q)\gets0$;
+
+\bugonpage B380, fifth-last line (5/7/87)
+
+\begingroup\tenpoint\noindent\def\!{\kern-1pt}\def\.#1{\hbox{\tt#1}}
+\.b and \.c, the two patterns with and without hyphenation are
+$\.a\,\.b\,\.-\,\.{c\!d}\,\.{e\!f}$ and $\.a\,\.{b\!c}\,\.{d\!e}\,\.f$.
+Thus the\cutpar\endgroup
+
+\bugonpage B386, lines 2--4 (5/21/87)
+
+\tenpoint\noindent
+hyphenation,
+\TeX\ first looks to see if it is in the user's exception dictionary. If not,
+hyphens are inserted based on patterns that appear within the given word,
+using an algorithm due to Frank~M. Liang.
+
+\bugonpage B397, line 28 (5/21/87)
+
+\tenpoint\noindent
+$h=z-c$. It follows that location \\{trie\_max} will
+never be occupied in \\{trie}, and we will have\cutpar
+
+\bugonpage B415, the mini-index (4/6/87)
+
+\eightpoint\noindent[Delete the spurious entry for `$c$'.]
+
+\bugonpage B419, mini-index entry for \\{c} (4/6/87)
+
+\eightpoint $c$: \\{integer}, \S994.
+
+\bugonpage B422, line 24 (8/23/86)
+
+\ninepoint\noindent
+\hskip20pt\\{prev\_p}: \\{pointer};\quad
+$\{\,$predecessor of $p\,\}$
+
+\bugonpage B435, line 16 (10/12/86)
+
+\ninepoint\noindent
+\hskip20pt$\\{width}(p)\gets\\{font\_info}[k].\\{sc}$;\quad
+$\{\,$that's \\{space}$(f)\,\}$\par\noindent
+\hskip20pt$\\{stretch}(p)\gets\\{font\_info}[k+1].\\{sc}$;\quad
+$\{\,$and \\{space\_stretch}$(f)\,\}$\par\noindent
+\hskip20pt$\\{shrink}(p)\gets\\{font\_info}[k+2].\\{sc}$;\quad
+$\{\,$and \\{space\_shrink}$(f)\,\}$\par
+\smallskip\eightpoint\noindent
+[And the mini-index gets three new entries:
+$\\{space}=macro$, \S558.
+$\\{space\_shrink}=macro$, \S558.
+$\\{space\_stretch}=macro$, \S558.]
+
+\bugonpage B495, lines 18 and 19 (2/15/87)
+
+\ninepoint\noindent
+[delete these lines, since the cases cannot occur]
+
+\bugonpage B510, line 8 (12/15/86)
+
+\ninepoint\noindent\hskip30pt
+({\tt"Pretend\]that\]you're\]Hercule\]Poirot:\]Examine\]all\]clues,"})
+
+\bugonpage B527, new line to follow line 13 (6/17/86)
+
+{\tenpoint\parindent=1em
+This program doesn't bother to close the input files that may still be open.
+\par}
+
+\bugonpage B534, fourth-last line (5/4/87)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{write\_stream}(\hbox{\tt\char`\#})\equiv\\{info}(
+ \hbox{\tt\char`\#}+1)$\quad $\{\,$stream number (0 to 17)$\,\}$
+
+\bugonpage B544, left column (1/28/87)
+
+\eightpoint
+\leftline{\\{acc\_kern}:\quad$\underline{155}$, 191, 837, 842, 879, 1125.}
+
+\bugonpage B546, entry for \\{c} (4/6/87)
+
+\eightpoint\noindent[Add a reference to section $\underline{994}$.]
+
+\bugonpage B547, left column (4/7/87)
+
+\eightpoint
+\leftline{\\{char}:\quad 19, 26--27, 520, 534.}
+
+\bugonpage B547, left column (6/14/87)
+
+\eightpoint
+\leftline{Chinese characters:\quad 134, 585.}
+
+\bugonpage B553, entry for \\{font\_base} (6/14/87)
+
+\eightpoint\noindent[Insert a reference to section 134.]
+
+\bugonpage B555, right column, new entry (10/25/86)
+
+\eightpoint
+\leftline{{\tt Huge page...},\quad 641.}
+
+\bugonpage B556, entry for \\{incr} (1/28/87)
+
+\eightpoint\noindent[Add a reference to section 842.]
+
+\bugonpage B557, entry for \\{is\_char\_node} (1/28/87)
+
+\eightpoint\noindent[Delete the reference to section 881.]
+
+\bugonpage B557, right column (6/14/87)
+
+\eightpoint
+\leftline{Japanese characters:\quad 134, 585.}
+
+\bugonpage B560, right column (1/28/87)
+
+\eightpoint
+\leftline{\\{max\_in\_open}:\quad$\underline{11}$, 14, 304, 328.}
+
+\bugonpage B561, left column, line 10 (4/15/87)
+
+\eightpoint
+\leftline{\qquad 169--172, 174, 178, 182, 1249, 1312, 1334.}
+
+\bugonpage B561, left column (5/1/87)
+
+\eightpoint
+\leftline{{\tt Missing font identifier}:\quad 577.}
+
+\bugonpage B563, left column, line 2 (4/15/87)
+
+\eightpoint
+\leftline{\qquad 136, 145, 149--154, 164, 168--169, 175--176, 182,}
+
+\bugonpage B563, right column (6/14/87)
+
+\eightpoint
+\leftline{oriental characters:\quad 134, 585.}
+
+\bugonpage B569, right column, in appropriate places (10/12/86)
+
+\eightpoint
+\leftline{\\{space}:\quad 547, $\underline{558}$, 752, 755, 1042.}
+\leftline{\\{space\_shrink}:\quad 547, $\underline{558}$, 1042.}
+\leftline{\\{space\_stretch}:\quad 547, $\underline{558}$, 1042.}
+
+\bugonpage B570, third-last line (1/28/87)
+
+\eightpoint\noindent\qquad
+ 786, 795, 809, 819--820, 822, 837, 842--844, 866,
+
+\bugonpage B571, right column (10/25/86)
+
+\eightpoint
+\leftline{{\tt The following...deleted},\quad 641, 992, 1121.}
+
+\bugonpage B571, right column (4/7/87)
+
+\eightpoint
+\leftline{\\{text\_char}:\quad $\underline{19}$, 20, 25, 47.}
+
+\bugonpage B573, right column (5/1/87)
+
+\eightpoint\noindent
+[Delete the entry for `{\tt Undefined font code}'.]
+
+\bugonpage B576, line 2 (1/28/87)
+
+\ninepoint\noindent
+$\langle\,$Add the width of
+ node $s$ to \\{break\_width} and increase $t$, unless it's
+ discardable{\sevenrm\kern.5em842}$\,\rangle$\par
+\noindent\qquad {\eightpoint Used in section 840.}
+
+\bugonpage B591, line 6 from the bottom (1/28/87)
+
+\ninepoint\noindent
+ $\langle\,$Subtract the width of node $v$ from \\{break\_width}%
+ {\sevenrm\kern.5em841}$\,\rangle$\quad
+ {\eightpoint Used in section 840.}
+
+ % volume C
+\hsize=29pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+
+\bugonpage C14, top two lines (3/16/87)
+
+\danger The recursive midpoint rule for curve-drawing was discovered in 1959
+by Paul de Casteljau, who showed that the curve could be described
+algebraically by the remarkably simple formula
+
+\bugonpage C54, sixth-last to fourth-last lines (10/13/86)
+
+\ninepoint Jonathan H. Quick (a student) used `|a.plus1|' as the name
+of a variable at the beginning of his program; later he said `|let|
+|plus=+|'. How could he refer to the variable `|a.plus1|' after that?
+
+\bugonpage C76, line 14 (10/13/86)
+
+\tenpoint
+\newdimen\longesteq
+\setbox0=\hbox{\indent$z_{12}-z_{11}=z_{14}-z_{13}$\quad}
+\longesteq=\wd0
+\noindent\hbox to \longesteq{\indent
+ $x_4=w-.01\\{in}$\hfil}%
+Point 4 should be one-hundredth of an inch inside\cutpar
+
+\bugonpage C103, line 12 (10/12/86)
+
+\tenpoint
+$\\{ht}\0=\\{body\_height}\0$; \ $.5[\\{ht}\0,-\\{dp}\0]=\\{axis}\0$;
+
+\bugonpage C105, line 13 (10/13/86)
+
+\ninepoint
+The vertical line just to the right of the italic left parenthesis
+shows the italic\cutpar
+
+\bugonpage C113, lines 20--27 (8/23/86)
+
+{\catcode`\@=\active
+\def@#1@{\begingroup\def\_{\kern.04em
+ \vbox{\hrule width.3em height .6pt}\kern.08em}%
+ \ifmmode\mathop{\bf#1}\else\hbox{\bf#1\/}\fi\endgroup}
+\danger The command `@erase@ @fill@ $c$' is an abbreviation for
+`@cullit@; @unfill@~$c$; @cullit@'; this zeros out the pixel values inside
+the cyclic path~$c$, and sets other pixel values to~1 if they were positive
+before erasing took place. \ (It works because the initial @cullit@ makes
+all the values 0 or~1, then the @unfill@ changes the values inside~$c$
+to 0 or negative. The final @cullit@ gets rid of the negative values,
+so that they won't detract from future filling and drawing.) \ You can
+also use `@draw@', `@filldraw@', or `@drawdot@' with `@erase@'; for example,
+`@erase@ @draw@~$p$' is an abbreviation for `@cullit@; @undraw@~$p$;
+@cullit@', which uses the currently-picked-up pen as if it were an
+eraser applied to path~$p$.
+
+}
+
+\bugonpage C124, line 9 (6/17/86)
+
+\eightpoint
+\noindent\hskip1.8in
+$\\{branch}_2=\\{flex}((30,570),(10,590),(-1,616))$
+
+\bugonpage C130, 3rd-last line (9/25/86)
+
+\ninepoint\noindent
+{\sl Geometry\/ \bf 1} (1986), 123--140]: Given a sequence
+
+\bugonpage C144, sixth line of the program (8/23/86)
+
+\ninepoint\noindent\hbox to\parindent{\hfil\sevenrm6\ \ \ }%
+$y_2=.1h$; \ $\\{top}\,y_3=.4h$;
+
+\bugonpage C148, the line before the illustration (11/27/86)
+
+\ninepoint\noindent
+are polygons with 32 and 40 sides, respectively:
+
+\smallskip\noindent
+[New illustrations are needed here, since \MF\ version 1.3 improves
+the accuracy of pen polygons.]
+
+\bugonpage C149, 7th line after the illustration (10/24/86)
+
+\ninepoint
+\line{$(200,y+100\pm\alpha)$, where
+$\alpha=\sqrt5/4\approx0.559$. If we digitize these outlines and fill the}
+
+\bugonpage C178, second-last line (8/23/86)
+
+\ninepoint\noindent
+(If $t_3=t_1$~transum~$t_2$, then
+$z$~transformed~$t_3=z$~transformed~$t_1+z$~transformed~$t_2$,\cutpar
+
+\bugonpage C198, fifth-last and fourth-last lines (10/13/86)
+
+\ninepoint\vskip-3pt
+\begindisplay
+$\\{top}\,y_2={\rm round}(\\{top}\,\beta)$.
+\enddisplay
+Such operations occur frequently in practice, so plain \MF\ provides
+convenient\cutpar
+
+\bugonpage C212, lines 9--11 from the bottom (8/23/86)
+
+\ninepoint
+\qquad
+ \alt\[point]\<numeric expression>\[of]\<path primary>\continuerule
+ \alt\[precontrol]\<numeric expression>\[of]\<path primary>\continuerule
+ \alt\[postcontrol]\<numeric expression>\[of]\<path primary>
+
+\bugonpage C233, lines 13--14 (2/15/87)
+
+\ninepoint\noindent
+one column of white
+pixels, if the character is $2a$ pixels wide, because the right edge of
+black pixels is specified here to have the $x$~coordinate $2a-1$.
+
+\bugonpage C247, lines 23--25 (11/27/86)
+
+\ninepoint
+\ansno 16.2:
+ `{\bf pencircle} scaled 1.06060' is the diamond but
+`{\bf pencircle} scaled 1.06061' is~the square. \ (This assumes that
+$\\{fillin}=0$. If, for example, $\\{fillin}=.1$, the change doesn't
+occur until the diameter is 1.20204.) \ The next change is at diameter
+1.5, which\cutpar
+
+\bugonpage C262, lines 1--4 (7/28/86)
+
+\ninepoint
+When we come to macros whose use has not yet been explained---for
+example, somehow |softjoin| and |stop| never made it
+into Chapters 1 through~27---we shall consider them from a user's
+viewpoint. But most of the comments that follow are addressed to a
+potential base-file designer.
+
+\bugonpage C266, line 16 (8/17/86)
+
+\ninepoint\noindent
+variables; they have the side effect of changing the variable's value.
+
+\bugonpage C276, line 26 (6/23/86)
+
+\ninepoint
+\noindent
+| if charic<>0: r((w+charic*hppp,h.o_),(w+charic*hppp,.5h.o_)); fi|
+
+\bugonpage C286, lines 24--26 (10/13/86)
+
+\ninepoint\noindent
+but \MF\ won't let you. And even if this had worked, it wouldn't have
+solved the problem; it would simply have put |ENDFOR| into the
+replacement text of |ast|, because expansion is inhibited when the
+replacement text is being read.
+
+\bugonpage C290, line 1 (8/23/86)
+
+\ninepoint \noindent{\it 2.\enspace Fortuitous loops.\enspace}%
+The `^{max}' and `^{min}' macros in Appendix~B make use of the fact\cutpar
+
+\bugonpage C298, third-last line (8/23/86)
+
+\ninepoint
+$t[\,u_1,\ldots,u_n]\;=\;t\bigl[t[u_1,\ldots,u_{n-1}],t[u_2,\ldots,u_n]\,\bigr]$
+
+\bugonpage C304, 14th-last line (2/15/87)
+
+\ninepoint\noindent
+[replace this `|\smallskip|' by a |\smallskip| between lines!]
+
+\bugonpage C307, fifth-last line (12/7/86)
+
+\ninepoint
+{\def\_{\kern.04em
+ \vbox{\hrule width.3em height .6pt}\kern.08em}%
+\bf adjust\_fit}(\<left sidebearing adjustment>,\thinspace
+ \<right sidebearing adjustment>);
+
+\bugonpage C312, line 34 (10/12/86)
+
+\ninepoint\noindent
+|params[2] = "sans_params"; fontname[2] = "cmssbx10";|
+
+\bugonpage C316, lines 19--21 (8/17/86)
+
+\ninepoint\noindent
+example,
+`|(some| |charht| |values| |had| |to| |be| |adjusted| |by| |as| |much|
+|as| |0.12pt)|' means that~you had too many different nonzero heights, but
+\MF\ found a way to reduce the number to at most~15 by changing some of
+them; none of them had to be\cutpar
+
+\bugonpage C319, line 3 (8/23/86)
+
+\ninepoint\noindent
+specified by saying, e.g.,
+
+\bugonpage C321, line 6 (7/28/86)
+
+\ninepoint\noindent
+| special "identifier " & font_identifier_;|
+
+\bugonpage C334, line 2 (6/23/86)
+
+\ninepoint\noindent
+| currentpicture := currentpicture shifted-(1,1); pix := currentpicture;|
+
+\bugonpage C339, tenth-last line (2/4/87)
+
+\ninepoint\noindent
+| Jackie K\=aren {\L}au\.ra Mar{\'\i}a N\H{a}ta{\l}{\u\i}e {\O}ctave|
+
+\bugonpage C343, second-last line (8/23/86)
+
+\rightline{\eightssi
+the precise needs of a precise but limited intellectual goal.}
+
+\bugonpage C346, 2nd line of entry for `{\tt;}' (1/12/87)
+
+\eightpoint
+\qquad 217, 223--224, 263, 312.
+
+\bugonpage C348, line 6 (6/17/86)
+
+\eightpoint
+concatenation, of paths, {\eightit 70--71}, {\eightit 123}, 127,
+
+\bugonpage C348, just before `debugging' (3/16/87)
+
+\eightpoint
+de Casteljau, Paul de Faget, 14.
+
+\bugonpage C348, right column (3/16/87)
+
+\eightpoint\noindent
+[The entry for `|define_whole_vertical_blacker_pixels|' should be moved up
+before the entry for `|define_whole_vertical_pixels|'.]
+
+\bugonpage C352, left column (6/1/87)
+
+\eightpoint\indent\hbox to0pt{\hss\lower1pt\hbox{*}}%
+{\tt kern}, {\it 97}, {\it 316}, $\underline{317}$.
+
+\bugonpage C352, right column (3/8/87)
+
+\eightpoint\noindent
+[The entry for `|lowres|' belongs before the entry for `|lowres_fix|'.]
+
+\bugonpage C353, left column (3/8/87)
+
+\eightpoint\noindent
+[The entries for `|mode|' and `\<mode command>' belong before the entry
+for `|mode_def|'.]
+
+\bugonpage C353, entry for {\tt mode\char`\_def} (8/17/86)
+
+\eightpoint
+{\tt mode\char`\_def}, 94, 189, $\underline{\smash{\hbox{\it 270}}}$,
+{\it 278--279}.
+
+\bugonpage C355, right column (4/15/86)
+
+\eightpoint\noindent
+[The entry for `{\tt rulepen}' belongs before the entry for `rules'.]
+
+\bugonpage C355, right column (8/5/86)
+
+\eightpoint
+{\tt screenstrokes}, 191, $\underline{277}$.
+
+\bugonpage C355, 2nd line of entry for `semicolons' (1/12/87)
+
+\eightpoint
+\qquad 217, 223--224, 263, 312.
+
+\bugonpage C356, full names for the Stanfords (4/10/86)
+
+\eightpoint
+Stanford, Amasa Leland, 340.
+
+Stanford, Jane Elizabeth Lathrop, 340.
+
+ % Volume D
+\hsize=35pc
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+
+\buginvol D, in general (7/28/86)
+
+\tenpoint\noindent
+[A number of entries were mistakenly omitted from the mini-indexes
+on the right-hand pages. Here is a combined list of all the missing
+items; you can mount it inside the back cover, say, as a secondary mini-index
+when the first one fails\dots\ ]
+
+\nobreak\medskip
+\setbox0=\vbox{\eightpoint \hsize=11pc \catcode`\_=\active \let_=\_
+ \rightskip=0pt plus 100pt minus 10pt
+ \pretolerance 10000
+ \hyphenpenalty 10000 \exhyphenpenalty 10000
+ \noindent\vbox to1pt{}\par % 1pt = \topskip - \ninept
+ \def\&#1{\hbox{\bf#1\/}} % boldface type for reserved words
+ \obeylines
+ \def\makeref #1 #2 #3#4
+ {\nn=#2 \hangindent=1em \noindent\\{#1}%
+ \if#3:: \else\unhcopy\eqbox \fi#4, \S\number\nn.\par}
+ \makeref add_or_subtract 930 :\&{procedure}
+ \makeref after 427 :\&{array}
+ \makeref arg_list 720 :\\{pointer}
+ \makeref b 580 :\\{pixel\_color}
+ \makeref bad_exp 824 :\&{procedure}
+ \makeref before 427 :\&{array}
+ \makeref begin_name 770 :\&{procedure}
+ \makeref bilin1 968 :\&{procedure}
+ \makeref binary_mac 863 :\&{procedure}
+ \makeref blank_rectangle 567 :\&{procedure}
+ \makeref boc_c 1162 :\\{integer}
+ \makeref boc_p 1162 :\\{integer}
+ \makeref cf 298 :\\{fraction}
+ \makeref clockwise 453 :\\{boolean}
+ \makeref ct 298 :\\{fraction}
+ \makeref cubic_intersection 556 :\&{procedure}
+ \makeref cur_pen 403 :\\{pointer}
+ \makeref cur_rounding_ptr 427 :$0\to \\{max\_wiggle}$
+ \makeref cur_spec 403 :\\{pointer}
+ \makeref cur_x 389 :\\{scaled}
+ \makeref cur_y 389 :\\{scaled}
+ \makeref dely 557 :\\{integer}
+ \makeref dep_finish 935 :\&{procedure}
+ \makeref dep_list 587 =macro
+ \makeref dimen_head 1125 :\&{array}
+ \makeref dx 495 :\\{integer}
+ \makeref dy 495 :\\{integer}
+ \makeref d1 464 :$0\to 1$
+ \makeref end_name 772 :\&{procedure}
+ \makeref eqtb 201 :\&{array}
+ \makeref error_stop_mode 68 =$3$
+ \makeref firm_up_the_line 682 :\&{procedure}
+ \makeref get_next 667 :\&{procedure}
+ \makeref gf_buf 1152 :\&{array}
+ \makeref gf_offset 1152 :\\{integer}
+ \makeref gf_ptr 1152 :\\{gf\_index}
+ \makeref halfword 156 =$\\{min\_halfword}\to \\{max\_halfword}$
+ \makeref hash 201 :\&{array}
+ \makeref index 629 =macro
+ \makeref input_ln 30 :\&{function}
+ \makeref interaction 68 :$0\to 3$
+ \makeref j 357 :$0\to \\{move\_size}$
+ \makeref known_pair 872 :\&{procedure}
+ \makeref limit 629 =macro
+ \makeref m_spread 357 :\\{integer}
+ \makeref materialize_pen 865 :\&{procedure}
+ \makeref max_allowed 403 :\\{scaled}
+ \makeref max_c 813 :\&{array}
+ \makeref max_link 813 :\&{array}
+ \makeref max_tfm_dimen 1130 :\\{scaled}
+ \makeref mem_top 12 =macro
+ \makeref mem 159 :\&{array}
+ \makeref memory_word 156 =\&{record}
+ \makeref more_name 771 :\&{function}
+ \makeref m1 464 :\\{integer}
+ \makeref n 580 :\\{screen\_col}
+ \makeref n_sin_cos 145 :\&{procedure}
+ \makeref name 629 =macro
+ \makeref negate_dep_list 904 :\&{procedure}
+ \makeref new_knot 871 :\&{function}
+ \makeref node_to_round 427 :\&{array}
+ \makeref n1 464 :\\{integer}
+ \makeref octant_dir 395 :\&{array}
+ \makeref o1 453 :\\{small\_number}
+ \makeref o2 453 :\\{small\_number}
+ \makeref paint_row 568 :\&{procedure}
+ \makeref param 1096 :\&{array}
+ \makeref param_stack 633 :\&{array}
+ \makeref path_length 916 :\&{function}
+ \makeref perturbation 1119 :\\{scaled}
+ \makeref phi 542 :\\{angle}
+ \makeref pool_ptr 38 :\\{pool\_pointer}
+ \makeref post_head 843 :\\{pointer}
+ \makeref pre_head 843 :\\{pointer}
+ \makeref print_err 68 =macro
+ \makeref print_macro_name 722 :\&{procedure}
+ \makeref quarterword 156 =$0\to 255$
+ \makeref recycle_value 809 :\&{procedure}
+ \makeref row_transition 579 :\\{trans\_spec}
+ \makeref scan_text_arg 730 :\&{procedure}
+ \makeref scroll_mode 68 =$2$
+ \makeref set_controls 299 :\&{procedure}
+ \makeref sf 298 :\\{fraction}
+ \makeref show_context 635 :\&{procedure}
+ \makeref sorted 325 =macro
+ \makeref st 298 :\\{fraction}
+ \makeref start 629 =macro
+ \makeref start_sym 1077 :\\{halfword}
+ \makeref str_pool 38 :\&{packed}\ \&{array}
+ \makeref str_ptr 38 :\\{str\_number}
+ \makeref str_start 38 :\&{array}
+ \makeref take_part 910 :\&{procedure}
+ \makeref tfm_changed 1130 :\\{integer}
+ \makeref tol 557 :\\{integer}
+ \makeref tt 843 :\\{small\_number}
+ \makeref tx 954 :\\{scaled}
+ \makeref txx 954 :\\{scaled}
+ \makeref txy 954 :\\{scaled}
+ \makeref ty 954 :\\{scaled}
+ \makeref tyx 954 :\\{scaled}
+ \makeref tyy 954 :\\{scaled}
+ \makeref unsorted 325 =macro
+ \makeref uv 557 :$0\to \\{bistack\_size}$
+ \makeref xy 557 :$0\to \\{bistack\_size}$
+ \makeref x1 542 :\\{scaled}
+ \makeref x2 542 :\\{scaled}
+ \makeref x3 542 :\\{scaled}
+ \makeref y1 542 :\\{scaled}
+ \makeref y2 542 :\\{scaled}
+ \makeref y3 542 :\\{scaled}
+ }
+\hbox{\nsize=\ht0 \advance\nsize-\topskip
+ \divide\nsize by 3 \divide\nsize by\ninept
+ \multiply\nsize by\ninept \advance\nsize\topskip
+ \vsplit0 to\nsize \kern1pc
+ \msize=\ht0 \advance\msize-\topskip
+ \divide\msize by 2 \divide\msize by\ninept
+ \multiply\msize by\ninept \advance\msize\topskip
+ \vbox to\nsize{\vsplit0 to\msize\vss}\kern1pc
+ \vbox to\nsize{\box0\vss}}
+
+\buginvol D, in general (4/6/87)
+
+\tenpoint\noindent[The percent signs in all the comments (for example,
+on pages 7 and 42) are in the wrong font! Change `{\tt\%}' to `\%'.]
+
+\bugonpage Dvii, line 9 (9/25/86)
+
+{\tenpoint\noindent\hsize=29pc
+{\sl Discrete and Computational Geometry\/ \bf1} (1986), 123--140.
+\ \it Develops the theory\cutpar}
+
+\bugonpage D2, line 27 (6/17/86)
+
+\ninepoint\noindent\hskip10pt
+{\bf define} $\\{banner}\equiv\hbox{\tt\char'23}$%
+{\tt This\]is\]METAFONT,\]Version\]1.3\char'23}\quad
+$\{\,$printed when \MF\ starts$\,\}$
+
+\bugonpage D18, line 30 (5/22/86)
+
+\ninepoint\noindent
+\\{str\_ptr}: \\{str\_number};\quad
+$\{\,$number of the current string being created$\,\}$
+
+\bugonpage D23, second line of mini-index, right column (6/14/87)
+
+\eightpoint
+\indent\\{pool\_name}\unhcopy\eqbox|"string"|, \S11.
+
+\bugonpage D30, lines 33--34 (6/14/87)
+
+\tenpoint\noindent
+to delete a token, and/or if some fatal error
+occurs while \MF\ is trying to fix a non-fatal one. But such recursion
+is never more than two levels deep.
+
+\bugonpage D63, lines 13--14 (5/5/87)
+
+\ninepoint\noindent
+[These two lines can be eliminated, since the variable \\{temp\_ptr}
+is no longer used! If you delete them, also remove \S158 from the
+list of sections where global variables are declared (pages D7 and D552),
+and remove \\{temp\_ptr} from the index on page D540.]
+
+\bugonpage D66, line 6 (5/22/86)
+
+\ninepoint\noindent
+{\bf function\/}\ $\\{get\_node}(s:\\{integer})$: \\{pointer};\quad
+$\{\,$variable-size node allocation$\,\}$
+
+\bugonpage D66, lines 31--32 (3/16/86)
+
+\tenpoint\noindent
+controlled
+growth helps to keep the \\{mem} usage consecutive when \MF\ is
+implemented on ``virtual memory'' systems.
+
+\bugonpage D67, lines 7--8 (4/21/87)
+
+\ninepoint\noindent\hskip10pt
+{\bf if} $r=p$ {\bf then if} $\\{rlink}(p)\ne p$ {\bf then}
+ $\langle\,$Allocate entire node $p$ and {\bf goto} \\{found}%
+ {\sevenrm\kern.5em171}$\,\rangle$;
+
+\bugonpage D86, second line of section 198 (2/27/87)
+
+\noindent
+Individual class numbers have no semantic
+or syntactic significance, except in a few instances\cutpar
+
+\bugonpage D101, line 2 (3/16/86)
+
+\tenpoint\line{%
+like `{\tt x}', or they can
+combine the structural properties of arrays and records, like `{\tt x20a.b}'.
+A}
+
+\bugonpage D102, line 24 (3/16/86)
+
+\tenpoint\line{\kern10pt
+In other words, variables have a hierarchical structure that includes
+enough threads running}
+
+\bugonpage D127, line 10 (5/5/87)
+
+\ninepoint\noindent
+[Variable $r$ can be eliminated, since it is not
+used in this procedure! If you delete it, also remove $\underline{280}$
+from the corresponding index entry on page D536.]
+
+\bugonpage D129, line 15 (5/5/87)
+
+\ninepoint\noindent
+[This line can be eliminated, since \\{sine} and \\{cosine} are not
+used in this procedure! If you delete them, also remove $\underline{284}$
+from the corresponding index entries on pages D538 and D521.]
+
+\bugonpage D142, line 23 (4/24/87)
+
+\tenpoint\noindent
+$(7-\sqrt{28}\,)/12$; the worst case
+occurs for polynomials like $B(0,28-4\sqrt{28},14-5\sqrt{28},42;t)$.)
+
+\bugonpage D178, third-last line (7/30/86)
+
+\tenpoint\line{\quad
+The following code maintains the invariant relations
+$0\le \\{x0}<\max(\\{x1},\\{x1}+\\{x2})$, $\vert\\{x1}\vert<2^{30}$,}
+
+\bugonpage D228, line 13 (7/30/86)
+
+\ninepoint\noindent\kern10pt
+{\bf while} $\\{max\_coef}<\\{fraction\_half}$ {\bf do}
+
+\smallskip\eightpoint\noindent
+The mini-index at the bottom of the next page should also receive the following
+new entry:
+\smallskip\indent
+$\\{fraction\_half}={\rm macro}$, \S105.
+
+\bugonpage D228, 10th-last line (5/5/87)
+
+\ninepoint\noindent\hskip20pt
+{\bf begin} $\\{right\_type}(p)\gets k$;
+\smallskip
+\noindent[Also eliminate `$q,$' seven lines above this, and delete
+$\underline{497}$ from the index entry for \\{q} on page D536.]
+
+\bugonpage D248, lines 16--21 (11/27/86)
+
+\ninepoint\noindent\kern10pt
+$\\{alpha}\gets\\{abs}(u)$;\kern5pt
+ $\\{beta}\gets\\{abs}(v)$;\par\noindent\kern10pt
+{\bf if} $\\{alpha}<\\{beta}$ {\bf then}\par\noindent\kern20pt
+{\bf begin} $\\{alpha}\gets\\{abs}(v)$;\kern5pt
+ $\\{beta}\gets\\{abs}(u)$;\kern5pt
+{\bf end};\quad$\{\,$now $\alpha=\max(\vert u\vert,\vert v\vert)$,
+ $\beta=\min(\vert u\vert,\vert v\vert)\,\}$\par\noindent\kern10pt
+{\bf if} $\\{internal}[\\{fillin}]\ne0$ {\bf then}\par\noindent\kern20pt
+$d\gets d-\\{take\_fraction}(\\{internal}[\\{fillin}],
+ \\{make\_fraction}(\\{beta}+\\{beta},\\{delta}))$;\par\noindent\kern10pt
+$d\gets\\{take\_fraction}((d+4)\;{\bf div}\;8,\\{delta})$;\kern5pt
+$\\{alpha}\gets\\{alpha}\;{\bf div}\;\\{half\_unit}$;
+
+\bugonpage D263, line 20 (3/16/86)
+
+\tenpoint\noindent
+instead of \\{false}, the other routines will simply log the fact
+that they have been called; they won't\cutpar
+
+\bugonpage D268, line 2 (4/28/87)
+
+\tenpoint\noindent
+Given the number~$k$ of an open window, the pixels of positive
+weight in \\{cur\_edges} will be shown\cutpar
+
+\bugonpage D301, line 6 of section 652 (5/5/87)
+
+\ninepoint\noindent
+[This line can be eliminated, since variable $s$ is not
+used in this procedure! If you delete it, also remove $\underline{652}$
+from the corresponding index entry on page D537; remove 652 from
+the index entries for \\{param\_size} and \\{param\_start} on page D534;
+and remove \\{param\_size} from the mini-index on page D301.]
+
+\bugonpage D376, lines 17 and 18 (11/14/86)
+
+\tenpoint\noindent
+[these two mysterious lines should be deleted]
+
+\bugonpage D380, line 11 (5/5/87)
+
+\ninepoint\noindent
+[Variables $q$ and $r$ can be eliminated, since they are not
+used in this procedure! If you delete them, also remove $\underline{862}$
+from the corresponding index entries on page D536.]
+
+\bugonpage D429, line 14 (5/5/87)
+
+\ninepoint\noindent\hskip10pt
+{\bf begin} $p\gets\\{cur\_exp}$;
+\smallskip
+\noindent[Also eliminate line 12, and delete $\underline{985}$ from the
+index entry for \\{vv} on page D543.]
+
+\bugonpage D455, line 5 (5/5/87)
+
+\ninepoint\noindent
+[This line can be eliminated, since variable $t$ is not
+used in this procedure! If you delete it, also remove $\underline{1059}$
+from the corresponding index entry on page D540; remove 1059 from
+the index entries for \\{small\_number} and \\{with\_option} on pages D539
+and D544; and remove \\{with\_option} from the mini-index on page D455.]
+
+\bugonpage D463, line 10 (12/15/86)
+
+\ninepoint\noindent\hskip30pt
+({\tt"Pretend\]that\]you're\]Miss\]Marple:\]Examine\]all\]clues,"})
+
+\bugonpage D465, lines 17--18 (6/14/87)
+
+\tenpoint\noindent
+[Delete these two lines.]
+
+\bugonpage D474, 5th-last line (3/16/86)
+
+\tenpoint\noindent
+depths, or italic corrections) are sorted;
+then the list of sorted values is perturbed, if necessary.
+
+\bugonpage D481, line 12 (6/17/86)
+
+\ninepoint\noindent\hskip10pt
+\\{print\_nl}({\tt\char`\"Font\]metrics\]written\]on\]\char`\"});\kern5pt
+\\{print}(\\{metric\_file\_name});\kern5pt
+\\{print\_char}({\tt\char`\".\char`\"});
+
+\noindent\hskip10pt\\{b\_close}(\\{tfm\_file})
+
+\smallskip\eightpoint\noindent
+The mini-index at the bottom of this page should also receive the following
+new entry:
+\smallskip\indent
+\\{print\_char}: {\bf procedure}, \S58.
+
+\bugonpage D510, new line to follow line 5 (6/17/86)
+
+{\tenpoint\parindent=1em
+This program doesn't bother to close the input files that may still be open.
+\par}
+
+\bugonpage D510, just before the fifth-last line (8/5/86)
+
+\ninepoint\noindent\hskip30pt$\\{internal}[\\{fontmaking}]\gets0$;\quad
+$\{\,$avoid loop in case of fatal error$\,\}$
+
+\bugonpage D520, right column (6/14/87)
+
+\eightpoint
+\leftline{Chinese characters:\quad 1147.}
+
+\bugonpage D526, left column, lines 1--2 (7/30/86)
+
+\eightpoint
+\leftline{\indent\\{fraction\_half}:\quad
+ $\underline{105}$, 111, 152, 288, 408, 496, 543,}
+\leftline{\indent\qquad 1098, 1128, 1141.}
+
+\bugonpage D526, left column, lines 6--7 (7/30/86)
+
+\eightpoint
+\leftline{\indent\qquad 478, 497, 499, 503, 530, 540, 547, 549, 599, 603,}
+\leftline{\indent\qquad 612, 615, 815--816, 917, 1169--1170.}
+
+\bugonpage D528, right column (6/14/87)
+
+\eightpoint
+\leftline{Japanese characters:\quad 1147.}
+
+\bugonpage D530, right column, line 45 (7/30/86)
+
+\eightpoint
+\leftline{\indent\\{max}:\quad$\underline{539}$, 543.}
+
+\bugonpage D533, right column (6/14/87)
+
+\eightpoint
+\leftline{oriental characters:\quad 1147.}
+
+\bugonpage D535, right column, line 27 (6/17/86)
+
+\eightpoint
+\leftline{\indent\qquad 1134, 1163--1165, 1182, 1194, 1200, 1205, 1213.}
+
+\bugonpage D547, bottom two lines (11/27/86)
+
+\ninepoint\noindent
+[These lines, and the top two on the next page, should move down
+so that they appear in alphabetical order just before `Compute
+test coefficients'.]
+ % volume E
+\hsize=29pc
+\def\dashto{\mathrel{\hbox{-\kern-.05em}\mkern3.9mu\hbox{-\kern-.05em}}}
+
+\bugonpage Exiii, lines 1--2 (7/28/86)
+
+\tenpoint\noindent
+February 11--13, 1984), 49.
+\ {\it An example meta-character of the Devanagari alphabet, worked out
+``online'' with the help of Matthew Carter.}
+
+\bugonpage Exiii, line 6 (7/28/86)
+
+\tenpoint\noindent
+{\it and western alphabets work also for Devanagari and Tamil.}
+
+\bugonpage E12, lines 15 and 19 (7/23/86)
+
+\tenpoint\noindent[change `17.32' to `17.28' in both places]
+
+\bugonpage E12, third-last line (12/18/86)
+
+\tenpoint\noindent[change `41' to `40']
+
+\bugonpage E13, lines 3, 4, and 20 (12/18/86)
+
+\tenpoint\noindent[change `40' to `41', `48' to `47', `17' to `7']
+
+\bugonpage E18, line 20 (7/23/86)
+
+\tenpoint\noindent[change `17.32' to `17.28']
+
+\bugonpage E18, line 29 (12/9/86)
+
+\tenpoint\noindent[change `236' to `212' in the {\tt cmss9} column]
+
+\bugonpage E170, top illustration (11/2/86)
+
+\tenpoint\noindent[There should be no ``dish'' or depression in the
+vicinity of point {\tt 3r}; the top edge of the character should be
+straight. This error appears also in the other uses of `\\{no\_dish\_serif}'
+throughout the book, since the illustrations were made before
+`\\{no\_dish\_serif}' was added to the program. See page
+E180~(twice at the top), E370~(twice), E374~(twice), E376~(twice), E378~(top),
+E390~(bottom), E398~(top), E402~(top), E406~(top), E453~(twice).]
+
+\bugonpage E179, new line to be inserted after line 6 (10/13/86)
+
+\ninepoint\noindent
+{\bf if} $\\{shaved\_stem}<\\{crisp}.\\{breadth}$:
+ $\\{shaved\_stem}:=\\{crisp}.\\{breadth}$; {\bf fi}
+
+\bugonpage E219, line 29 (6/2/87)
+
+\ninepoint\line{\\{top} $y_1=h$; \ $x_1=x_2$; \
+ {\bf filldraw stroke} $z_{1e}\dashto z_{2'e}$;\hfil\% stem}
+
+\bugonpage E279, seventh line from the bottom (7/20/86)
+
+\rightline{\eightssi that delicious but restrained humor which
+ her readers found so irresistible.}
+
+\bugonpage E301, new line to be inserted after line 28 (5/15/87)
+
+\ninepoint\noindent
+\quad{\bf if} $\\{lower\_side}>1.2\\{upper\_side}$:
+ $\\{upper\_side}:=\\{lower\_side}$; {\bf fi}
+
+\bugonpage E554, bottom half of page (12/18/86)
+
+\ninepoint\noindent[The letters will change slightly because of the
+corrections to {\tt cmr17} noted on pages 12 and 13.]
+
+\bugonpage E561, line 3 (12/9/86)
+
+\ninepoint\noindent[The numerals should be `\thinspace
+{\niness 0123456789}\thinspace' (i.e., 2/3 point less tall)
+because of the correction made to page 18.]
+
+\bugonpage E562, line 9 (12/9/86)
+
+\ninepoint\noindent[The numerals should be `\thinspace
+{\ninessi 0123456789\/}\thinspace' (i.e., 2/3 point less tall)
+because of the correction made to page 18.]
+
+\bugonpage E572, entry for {\it breadth} (10/13/86)
+
+\eightpoint
+{\it breadth}, 59, 75, 79, 91, 93, 179, 225, 233,
+
+\bugonpage E573, entry for {\tt cmcsc10} (8/17/86)
+
+\eightpoint
+{\tt cmcsc10}, $\underline{30}$--$\underline{31}$, 567.
+
+\bugonpage E576, tenth-last line (5/15/87)
+
+\eightpoint
+{\bf lowres\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em
+ fix}, 550.
+ \bye
+ Now here are some that I will make soon!
+
diff --git a/systems/knuth/dist/errata/errata.two b/systems/knuth/dist/errata/errata.two
new file mode 100644
index 0000000000..74dde87726
--- /dev/null
+++ b/systems/knuth/dist/errata/errata.two
@@ -0,0 +1,491 @@
+% More bugs (sigh) in The TeXbook
+
+\input manmac
+\proofmodefalse
+\raggedbottom
+\output{\onepageout{\unvbox255\kern-\dimen@ \vfil}}
+
+\def\rhead{Bugs in {\tensl The \TeX book}, second printing}
+\def\bugonpage#1(#2) \par{\bigbreak\tenpoint
+ \hrule\line{\lower3.5pt\vbox to13pt{}Page #1\hfil(#2)}\hrule\nobreak\medskip}
+
+\noindent This is a list of all corrections made to {\sl The \TeX book\/}
+since the second printing. If your copy doesn't say `{\sl\kern-1pt Second
+printing (October 1984)\/}' on the copyright page, you should also look at
+the previous bug list. In fact, the most important corrections to the
+first printing were discovered first, so they've already been~made.
+
+\bugonpage 23, line 16 (10/13/84)
+
+\tenpoint\indent
+|This is TeX, Version 1.0 (preloaded format=plain 83.7.15)|
+
+\bugonpage 33, line 32 (10/21/84)
+
+The bottom line shows how far \TeX\ has gotten until now in the
+|story|{\parfillskip=0pt\par}
+
+\bugonpage 41, lines 7 and 8 (10/8/85)
+
+\ddanger The twin operations ^|\uppercase||{|\<token list>|}| and
+^|\lowercase||{|\<token list>|}| go through a given token list and convert
+all of the character tokens to their\parfillskip=0pt\enddanger
+
+\bugonpage 57, line 17 (1/6/86)
+
+|dd|\quad didot point ($\rm1157\,dd=1238\,pt$)
+
+\bugonpage 61, lines 17--19 (12/18/85)
+
+\ninepoint\noindent
+|depth|, |em|, |ex|,
+|fil|, |height|, |in|, |l|, |minus|, |mm|, |mu|, |pc|, |plus|,
+|pt|, |scaled|, |sp|, |spread|, |to|, |true|, |width|. ^^{reserved words}
+\ (See Appendix~I for references to the contexts in which each of these is
+recognized as a keyword.)
+
+\bugonpage 67, append a new exercise (1/19/85)
+
+\exno=5 \def\chapno{11}
+\gdef\frac#1/#2{\leavevmode\kern.1em
+ \raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em
+ /\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}
+
+\ddangerexercise Construct a |\frac| macro such that `|\frac1/2|' yields
+`\frac1/2'.
+
+\bugonpage 130, line 15 (4/17/85)
+
+\beginmathdemo
+|$y'''_3+g'^2$|&y'''_3+g'{}^2\cr
+\endmathdemo
+
+\bugonpage 170, line 5 (5/28/85)
+
+\ninepoint
+\line{tall, unslanted
+letter; and so on. But two of the examples involve corrections that were}
+
+\bugonpage 194, lines 13--15 should be centered better (10/22/84)
+
+\ninepoint
+$$\displaylines{\hfill x\equiv x;\hfill\llap{(1)}\cr
+ \hfill\hbox{if}\quad x\equiv y\quad\hbox{then}\quad
+ y\equiv x;\hfill\llap{(2)}\cr
+ \hfill\hbox{if}\quad x\equiv y\quad\hbox{and}\quad
+ y\equiv z\quad\hbox{then}\quad
+ x\equiv z.\hfill\llap{(3)}\cr}$$
+
+\bugonpage 215, lines 9 and 10 from the bottom (12/23/84)
+
+\ninepoint\noindent
+general format
+is the same as for |\def| and |\gdef|, but \TeX\ blindly expands the tokens
+of the replacement text according to the expansion rules above. For
+example, consider
+
+\bugonpage 233, lines 15--19 (1/19/85)
+
+\medskip
+\settabs\+\indent&10\frac1/2 lbs.\qquad&\it Servings\qquad&\cr
+\+&\negthinspace\it Weight&\it Servings&
+ {\it Approximate Cooking Time\/}*\cr
+\smallskip
+\+&8 lbs.&6&1 hour and 50 to 55 minutes\cr
+\+&9 lbs.&7 to 8&About 2 hours\cr
+\+&9\frac1/2 lbs.&8 to 9&2 hours and 10 to 15 minutes\cr
+\+&10\frac1/2 lbs.&9 to 10&2 hours and 15 to 20 minutes\cr
+
+\bugonpage 236, lines 18--21 (1/19/85)
+
+\ninepoint
+$$\vbox{\openup2pt
+\halign{\hfil\bf#&\quad\hfil\it#\hfil&\quad\hfil#\hfil&
+ \quad\hfil#\hfil&\quad#\hfil\cr
+Squab&Poussin&2&\frac3/4 to 1&Broil, Grill, Roast\cr
+Broiler&Poulet Nouveau&2 to 3&1\frac1/2 to 2\frac1/2&Broil, Grill, Roast\cr
+Fryer&Poulet Reine&3 to 5&2 to 3&Fry, Saut\'e, Roast\cr
+Roaster&Poularde&5\frac1/2 to 9&Over 3&Roast, Poach, Fricassee\cr}}$$
+[This change should also be made at the bottom of page 237.]
+
+\bugonpage 236, fifth-last line (1/19/85)
+
+\ninepoint
+| Squab&Poussin&2&\frac3/4 to 1&Broil, Grill, Roast\cr|
+
+\bugonpage 237, line 25 (10/10/84)
+
+\ninepoint\noindent
+saying `^|\tabskip||=|\<glue>'. For example,
+let's do the poultry table again, but with the{\parfillskip=0pt\par}
+
+\bugonpage 265, bottom line (11/6/85)
+
+[insert a comma after `{\eightss LEONTIEF}'.]
+
+\bugonpage 271, line 8 (11/12/85)
+
+\ninepoint
+\beginsyntax
+<fil unit>\is[fil]\alt<fil unit>[l]
+\endsyntax
+
+\bugonpage 280, lines 7 and 8 (1/8/85)
+
+\ninepoint\noindent
+\<4-bit number>.\enskip
+The specified output stream is opened or closed, for use in |\write|
+commands, as explained in Chapter~21.
+
+\bugonpage 300, lines 5--10 [changed for version 1.3] (11/25/84)
+
+\ninepoint\noindent
+what part of \TeX's memory has become overloaded;
+one of the following fourteen things will be mentioned:
+\begindisplay
+|number of strings|\qquad(names of control sequences and files)\cr
+|pool size|\qquad(the characters in such names)\cr
+|main memory size|\qquad(boxes, glue, breakpoints, token lists,
+ characters, etc.)\cr
+\enddisplay
+
+\bugonpage 300, lines 23--29 [changed for version 1.3] (11/25/84)
+
+\danger If you have a job that doesn't overflow \TeX's capacity, yet
+you want to see just how closely you have approached the limits,
+just set ^|\tracingstats| to a positive value before the end of your
+job. The log file will then conclude with a report on your actual
+usage of the first eleven things named above (i.e., the number of strings,
+\dots, the save size), in that order. ^^{stack positions}
+Furthermore, if you set |\tracingstats| equal to 2~or~more, \TeX\
+will show its current memory usage whenever it
+does a ^|\shipout| command. Such statistics are broken into two
+parts; `|490&5950|' means, for example, that 490 words are being used
+for ``large'' things like boxes, glue, and
+breakpoints, while 5950 words are being used for ``small'' things like
+tokens and characters.
+
+\bugonpage 302, line 14 (10/8/85)
+
+\begintt
+.\tenrm || (ligature ---)
+\endtt
+
+\bugonpage 305, line 26 (12/24/84)
+
+\ninepoint\noindent
+sentable as |^^M|. Asking \TeX\ to |\show\^^M|
+\looseness-1
+produces the response `|>| |\^^M=macro:->\|\]|.|'.
+
+\bugonpage 306, line 10 (7/1/85)
+
+\ninepoint\noindent
+no ``^{explicit kerns},'' and an italic correction is an
+explicit kern.) \ But the italic correction may be too much (especially in an
+italic font); |shelf{|^|\kern||0pt}ful| is often best.
+
+\bugonpage 308, line 25 (3/25/85)
+
+\ninepoint\indent
+|\def\appendroman#1#2#3{\edef#1{\def\noexpand#1{\csname|
+
+\bugonpage 311, insert a new answer (1/19/85)
+
+\ninepoint
+\ansno11.6:
+ |\def\frac#1/#2{\leavevmode\kern.1em|\parbreak
+|\raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em|\parbreak
+|/\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}|
+
+\smallskip\noindent[This causes answer 12.8 to move to page 312;
+answer 12.16 also moves to page 313.]
+
+\bugonpage 320, lines 17--20 (8/10/85)
+
+\ninepoint
+\ansno17.16:
+ |\def\sqr#1#2{{\vcenter{\vbox{\hrule height.#2pt|\parbreak
+ | \hbox{\vrule width.#2pt height#1pt \kern#1pt|\parbreak
+ | \vrule width.#2pt}|\parbreak
+ | \hrule height.#2pt}}}}|
+
+\bugonpage 327, lines 26--33 (10/22/84)
+
+\ninepoint
+\ansno19.16:
+ |$$\displaylines{\hfill x\equiv x;\hfill\llap{(1)}\cr|\parbreak
+ | \hfill\hbox{if}\quad x\equiv y\quad\hbox{then}\quad|\parbreak
+ | y\equiv x;\hfill\llap{(2)}\cr|\parbreak
+ | \hfill\hbox{if}\quad x\equiv y\quad\hbox{and}\quad|\parbreak
+ | y\equiv z\quad\hbox{then}\quad|\parbreak
+ | x\equiv z.\hfill\llap{(3)}\cr}$$|\par\medskip\noindent
+There's also a trickier solution, which begins with
+\begintt
+$$\displaylines{x\equiv x;\hfil\llap{(1)}\hfilneg\cr
+\endtt
+
+\bugonpage 330, line 29 (11/15/85)
+
+\ninepoint\indent
+|\edef\next#1#2{\def#1{\b#2\d}} \next\a\c|
+
+\bugonpage 332, lines 17--24 (1/19/85)
+
+\ninepoint
+\begintt
+\settabs\+\indent&10\frac1/2 lbs.\qquad&\it Servings\qquad&\cr
+\+&\negthinspace\it Weight&\it Servings&
+ {\it Approximate Cooking Time\/}*\cr
+\smallskip
+\+&8 lbs.&6&1 hour and 50 to 55 minutes\cr
+\+&9 lbs.&7 to 8&About 2 hours\cr
+\+&9\frac1/2 lbs.&8 to 9&2 hours and 10 to 15 minutes\cr
+\+&10\frac1/2 lbs.&9 to 10&2 hours and 15 to 20 minutes\cr
+\endtt
+
+\bugonpage 332, lines 33--35 (1/19/85)
+
+\ninepoint\noindent
+proofs. \ (You weren't supposed to think of this,
+but it has to be mentioned.) \ See exercise 11.\fracexno\ for the `|\frac|'
+macro; it's better to say `\frac1/2' than `$1\over2$', in a cookbook.\par
+Another way to treat this table would be to display it in a vbox, instead
+of including a first column whose sole purpose is to specify indentation.
+
+\bugonpage 337, line 28 (11/12/85)
+
+\ninepoint\noindent |\nextnumber|. Quick should put `|\relax|'
+at the end of his macro. \ (The {keywords} |l|,{\parfillskip=0pt\par}
+
+\bugonpage 357, lines 35 and 36 (1/8/85)
+
+\ninepoint\noindent
+|\def\*{\discretionary{\thinspace\the\textfont2\char2}{}{}}|
+
+\bugonpage 357, last two lines (4/17/85)
+
+\ninepoint\noindent
+|\def\pr@m@s{\ifx'\next\let\next\pr@@@s|%
+ | \else\ifx^\next\let\next\pr@@@t|\par\noindent
+| \else\let\next\egroup\fi\fi \next}|\par\noindent
+|\def\pr@@@s#1{\prim@s} \def\pr@@@t#1#2{#2\egroup}|
+
+\bugonpage 358, lines 8--12 (1/23/85)
+
+\ninepoint\noindent
+|\def\hbar{{\mathchar'26\mkern-9muh}}|\hfil\break\null
+|\def\surd{{\mathchar"1270}}|\hfil\break\null
+|\def\angle{{\vbox{\ialign{$\m@th\scriptstyle##$\crcr|\hfil\break\null
+| \not\mathrel{\mkern14mu}\crcr \noalign{\nointerlineskip}|\hfil\break\null
+| \mkern2.5mu\leaders\hrule height.34pt\hfill\mkern2.5mu\crcr}}}}|
+
+\bugonpage 359, lines 7--8 (1/22/85)
+
+\ninepoint\noindent
+|\def\ddots{\mathinner{\mkern1mu\raise7pt\vbox{\kern7pt\hbox{.}}\mkern2mu|%
+\hfil\break\null
+| \raise4pt\hbox{.}\mkern2mu\raise1pt\hbox{.}\mkern1mu}}|
+
+\bugonpage 360, line 22 (1/22/85)
+
+\ninepoint\noindent
+| \mkern5mu \raise.6\dimen@\copy\rootbox \mkern-10mu \box0}|
+
+\bugonpage 361, line 3 (3/27/85)
+
+\ninepoint\noindent
+|\def\buildrel#1\over#2{\mathrel{\mathop{\kern0pt #2}\limits^{#1}}}|
+
+\bugonpage 361, lines 19--20 (1/22/85)
+
+\ninepoint\noindent
+|\def\bmod{\mskip-\medmuskip \mkern5mu|\hfil\break\null
+| \mathbin{\rm mod} \penalty900 \mkern5mu \mskip-\medmuskip}|
+
+\bugonpage 361, line 27 (5/1/85)
+
+\ninepoint\noindent
+|\def\matrix#1{\null\,\vcenter{\normalbaselines\m@th|
+
+\bugonpage 361, bottom line (5/1/85)
+
+\ninepoint\noindent
+| \null\;\vbox{\kern\ht1\box2}\endgroup}|
+
+\bugonpage 362, line 9 (5/1/85)
+
+\ninepoint\noindent
+|\def\eqalign#1{\null\,\vcenter{\openup1\jot \m@th|
+
+\bugonpage 362, lines 17--29 (8/10/85)
+
+{\parindent=0pt\ninepoint
+|\def\@lign{\tabskip=0pt\everycr={}} % restore inside \displ@y|\par
+|\def|^|\displaylines||#1{\displ@y|\parbreak%
+| \halign{\hbox to\displaywidth{|%
+ |$\hfil\@lign\displaystyle##\hfil$}\crcr|\parbreak%
+| #1\crcr}}|
+\smallbreak
+|\def|^|\eqalignno||#1{\displ@y \tabskip=|^|\centering|\parbreak%
+| \halign to\displaywidth{\hfil$\@lign\displaystyle{##}$\tabskip=0pt|\parbreak%
+| &$\@lign\displaystyle{{}##}$\hfil\tabskip=\centering|\parbreak%
+| &\llap{$\@lign##$}\tabskip=0pt\crcr|\parbreak%
+| #1\crcr}}|\par
+|\def|^|\leqalignno||#1{\displ@y \tabskip=\centering|\parbreak%
+| \halign to\displaywidth{\hfil$\@lign\displaystyle{##}$\tabskip=0pt|\parbreak%
+| &$\@lign\displaystyle{{}##}$\hfil\tabskip=\centering|\parbreak%
+| &\kern-\displaywidth\rlap{$\@lign##$}\tabskip=\displaywidth\crcr|\parbreak%
+| #1\crcr}}|
+\par}
+
+\bugonpage 363, line 9 (5/12/85)
+
+\ninepoint\noindent
+|\def\footnote#1{\let\@sf=\empty % parameter #2 (the text) is read later|
+
+\bugonpage 364, line 3 (3/23/85)
+
+\ninepoint\noindent
+|\def\plainoutput{\shipout\vbox{\makeheadline\pagebody\makefootline}%|
+
+\bugonpage 364, fifth-last line (9/15/85)
+
+\ninepoint\noindent
+|\def\fmtname{plain}\def\fmtversion{2.0} % identifies the current format|
+
+\bugonpage 399, eighth-last line (2/11/85)
+
+\ninepoint\noindent
+| \baselineskip=\footnotebaselineskip\noindent\unhbox0\par}|
+
+\bugonpage 401, line 5 (1/29/85)
+
+\ninepoint\noindent
+{|\fontdimen| parameters to qualify as a math symbol font).
+(2)~Set all the font identifiers\parfillskip=0pt\par}
+
+\bugonpage 414, line 10 (12/17/84)
+
+\ninepoint\noindent
+|\font\titlefont=cmssdc40 % titles in chapter openings|
+
+%\bugonpage 420, line 9 (10/3/85)
+%
+%\ninepoint\noindent
+%|\def\AmSTeX{$\cal A\kern-.1667em\lower.5ex\hbox{$\cal M$}\kern-.075em|
+% that change comes under `font data', explained away below
+
+\bugonpage 444, bottom line (1/10/85)
+
+\ninepoint\noindent
+depth $d(z)+v$, consisting
+of box~$x$ followed by an appropriate kern followed by box~$z$.
+
+\bugonpage 461, entry for character codes (11/6/85)
+
+\eightpoint
+Add `{\sl see also\/} category codes'.
+
+\bugonpage 463, entries for {\tt dd}, Didot, and didot (1/6/86)
+
+\eightpoint
+Remove the circumflex accents.
+
+\bugonpage 466, left column (1/19/85)
+
+\eightpoint
+fractions, 67, 139--143, 152, 170, 179,\par
+\qquad 186, 444--445.\par
+\quad huge, 196.\par
+\quad slashed form, 67, 139--140, 233, 236.
+
+\bugonpage 467, index entry for {\tt\char`\\hsize} (6/14/85)
+
+\eightpoint
+Add a reference to page {\it 60}.
+
+\bugonpage 469, index entry for {\tt\char`\\kern} (7/1/85)
+
+\eightpoint
+Add a reference to page {\it 306}.
+
+\bugonpage 469, index entry for kerns (7/1/85)
+
+\eightpoint
+Add a reference to page 306.
+
+\bugonpage 469, new entry (11/12/85)
+
+\eightpoint
+|l| after |fil|, $\underline{271}$, 337.
+
+\bugonpage 469, second line on the right (9/13/85)
+
+\eightpoint
+%\def\LaTeX{L\kern-.25em\raise.7ex\hbox{a}\kern-.05em\TeX} % old style
+{L\kern -.36em\raise.6ex\hbox{\sixrm A}\kern-.15em\TeX}, 137.
+
+\bugonpage 470, index entries for {\tt\char`\\longleftarrow}
+ thru {\tt\char`\\Longrightarrow} (10/5/84)
+
+\eightpoint
+The references to page 358 should be underlined (seven times).
+
+\bugonpage 475, index entry for punctuation in formulas (4/29/85)
+
+\eightpoint
+Add a reference to page 161.
+
+\bugonpage 476, index entry for {\tt\char`\\scriptspace} (8/10/85)
+
+\eightpoint
+Change `445' to `445--446'.
+
+\bugonpage 478, first and last lines (10/11/84)
+
+\noindent Delete the last line in the right-hand column
+(since it appears on page 479), and add the following line
+at the top of the left-hand column (since it was dropped by mistake
+from the second printing):
+
+\smallskip
+\eightpoint styles of math formatting, 140--141, 441--447.
+
+\bugonpage 478, new entry after tabbing (5/28/85)
+
+\eightpoint
+tables, {\sl see\/} alignments, tabbing.
+
+\bugonpage 478, tabskip entries (3/25/85)
+
+\eightpoint\noindent Instead of `237--239' and `237--238' it should say
+`$\underline{237}$--$\underline{239}$' twice.
+
+\bugonpage 481, the entry for {\tt\char`\\widetilde} (9/23/85)
+
+\eightpoint
+Page 359 should be underlined.
+
+\bugonpage 483, lines 16--17 (1/19/85)
+
+|P.O. Box 9506|\parbreak
+|Providence RI 02940-9506, USA.|
+
+\bugonpage 483, lines 22--23 (1/19/85)
+
+P.O. Box 9506\par
+Providence RI 02940-9506, USA.
+
+\bigskip
+\hrule
+\bigskip\noindent\tenrm
+Note: The next printing will use the ``real'' Computer Modern fonts
+instead of the ``almost'' Computer Modern fonts. Therefore many of
+the line breaks will be slightly different. Also, the font-related
+numerical data on pages 27, 29, 66, 75, 76, 79, 88, 98, 99, 112, 113, 310,
+314, 396, 399, 409, 420, and 459 will be different. However, these
+differences need not be listed here, because the old book was correct with
+respect to the old fonts.
+
+\bye
diff --git a/systems/knuth/dist/errata/errorlog.tex b/systems/knuth/dist/errata/errorlog.tex
new file mode 100644
index 0000000000..49bb6c0450
--- /dev/null
+++ b/systems/knuth/dist/errata/errorlog.tex
@@ -0,0 +1,2935 @@
+% Appendix to the Errors of TeX paper (updated)
+% Section numbers now adjusted to TeX 3.0 equivalents
+% NB: tab marks are significant in this file, they signal continuation lines
+%\magnification=\magstephalf
+%\pageno=35
+\input logmac
+* 10 Mar 1978
+L1. Rename a few external variables to make their first six letters unique.
+D2. Initialize \\{escape_char} to $-1$, not 0 [it will be set to the
+ first character input]. @240
+L3. Fix bug: The test `$\\{id}<\O{200}$' was supposed to distinguish one-letter
+ identifiers from longer (packed) ones, but negative values of
+ \\{id} also pass this test. @356
+B4. Fix bug: I wrote `{\bf while} $\alpha\land(\beta\lor\gamma)$' when I meant
+ `{\bf while} $(\alpha\land\beta)\lor\gamma$'. @259
+R5. Initialize the input routines in |INITEX| [at this time a short,
+ separate program not under user control], in case errors occur. @1337
+E6. Don't initialize \\{mem} in |INITEX|, it wastes time. @164
+B7. Change `\\{new_line}' [which denotes a lexical scanning state] to
+ `\\{next_line}' [which denotes
+ \\{carriage_return} and \\{line_feed}] in print commands.
+F8. Include additional test `$\\{mem}[p] \ne 0\; \land$' in \\{check_mem}. @168
+M9. Fix inconsistency between the \\{eq_level} conventions of \\{macro_def} and
+ \\{eq_define}. @277
+# About six hours of debugging time today.
+# |INITEX| appears to work,
+ and the test routine got through \\{start_input},
+ \\{chcode} [the \TeX78 command for assigning a \\{cat_code}],
+ \\{get_next}, and \\{back_input} the first time.
+* 11 Mar 1978
+I10. Insert space before `|(|' on terminal when opening a new file. @537
+F11. Put `$p\gets \\{link}(p)$' into the loop of \\{show_token_list},
+ so that it doesn't loop forever. @292
+L12. Shift the last item found by \\{scan_toks} into the \\{info} field.
+ [With |SAIL| all packing of fields was done by arithmetic operations,
+ not by the compiler.] @474
+B13\>12. Fix the previous bugfix: I shifted by the wrong amount. @474
+I14. Add a feature that prints a warning when the end of a file page occurs within
+ a macro definition or call. [System dependent.] @336
+# Unintended bugs in my test routine [a format intended eventually to typeset
+ {\sl The Art of Computer Programming\/}] helped check out the
+ error recovery mechanisms.
+ For example, I had `|\lft{#}|' instead of `|\lft{##}|' inside a macro,
+ and three cases of improper |{|~and~|}| nesting.
+F15. Add the forgotten case `\\{set_font}:' to \\{eq_destroy}. @275
+C16. Change |\require| to |\input|. @376
+F17. Add code for the case $\\{cur_cmd}=0$ [later known as the case
+ `$\,t\ge\\{cs_token_flag}$'] when scanning a tokenlist. @357
+# That's the first ``big'' error I've spotted so far.
+I18. Introduce a `|d|' option in the error routine, to facilitate debugging. @84
+L19. Assign a floating-point constant \\{ignore_depth} to \\{prev_depth},
+ instead of assigning the integer constant \\{flag}
+ [since \\{prev_depth} is type \\{real} in \TeX78]. @215
+I20. Improve the readability and spacing of \\{show_node_list} output. @182,187
+F21. Set the variable $v$ before using the {\bf case} construction in
+ \\{show_node_list}, because there's one case where $v$
+ didn't receive a value [as part of the field unpacking]. @182
+# About seven hours today.
+* 12 Mar 1978
+# One hour to enter yesterday's corrections and recompile.
+# At this point \TeX\ correctly located further unintended syntax errors
+ in |acphdr| [the test file].
+I22. Insert \\{debug_help} into \\{succumb}, giving a chance to look at memory
+ before the system dies. @93
+D23. Use \\{eq_destroy} wherever necessary in \\{unsave}. @283
+L24. Change `$t \gets (t-1)$ mod 8' to `$t \gets (t-1)$ land~7' in \\{id_name},
+ since |SAIL| has $-1$~mod~$8 = -1$. [At this time, \\{id_name} is a
+ routine that unpacks control sequence names, according to a scheme
+ that will become obsolete after change \#422.]
+S25. Remove the space that appears at end of paragraph.
+ (I hadn't anticipated that.) @816
+L26. Throw away unwanted \\{line_feed} after getting a \\{carriage_return}
+ in response to \\{in_chr_w} [a system routine for
+ input from the terminal]. @83
+B27. Delete spurious call to \\{flush_list} in \\{end_token_list}. @324
+# Why did I make such a silly mistake?
+F28. Fix bug in \\{get_x_token}: I forgot to say `\\{macro_call}' (which is the
+ main point of that routine)! @380
+# While tracking that bug down, I found out incidentally that kerning is okay.
+# Also \TeX\ correctly caught an error |0p| for |0pt|.
+L29. Fix bug in \\{scan_spec} ({\bf while} instead of {\bf repeat}). @404
+M30. Make the table entries for |\hfill| and |\hskip| consistent with the
+ program conventions. @1058
+L31. Disable unforeseen coercion: When \\{scan_spec} put \\{hsize} on
+ \\{save_stack}, the value changed from \\{real} to \\{integer}. @645
+I32. Use `|*|' instead of `|-1.0|' for running dimensions
+ of rules in \\{show_node_list}. @176
+D33. Clear \\{mem}[\\{head}] to null in \\{push_nest} [in \TeX82, this
+ will be done by \\{get_avail}]. @216
+# A vrule link got clobbered because I forgot to do this.
+I34. Translate ASCII control codes to special form when displaying them. @48,68
+# Ligatures work, but \\{show_node_list} showed them funny.
+F35. Remember to clear parameters off \\{save_stack} in \\{package} routine. @1086
+# About eight hours today.
+* 13 Mar 1978
+D36. Introduce a new variable \\{hang_first} [later the sign of \\{hang_after}]. @849
+E37\>36. Simplify the new code, realizing that if $\\{hang_indent}=0$ then
+ \\{hang_first} is irrelevant. @848
+# Time sharing is very slow today, so I'm mostly reading technical reports while
+ waiting\/ {\bf three hours} for compiler, editor, and loading routine.
+# I'm not counting this as debugging time!
+# (Came back in the evening.)
+P38. Spruce up the comments in the \\{line_break} routine, which appears to be
+ almost working. @813
+D39. Rethink the setting of \\{best_line}; it's 1 too high in many cases.
+ [The final line of a paragraph was handled in a treacherous way.] @874
+D40. Compute proper initialization for
+ \\{prev_depth} when beginning an |\hbox|
+ with a paragraph inside. [This refers to a special `paragraph box'
+ construction, used when an hbox of specified size becomes overfull;
+ \TeX78 doesn't have the concept of internal vertical mode.] @1083
+D41. Also initialize \\{tail} in that case. @1083
+M42. Also put the result of line-breaking into the correct list.
+T43. Fix a typo in the \\{free_node} routine (`\\{link}' not `\\{llink}');
+ by strange chance it had been harmless until today. @130
+F44. Fix bug: \\{post_line_break} forgot to set \\{adjust_tail}. @889
+D45. Update \\{act_width} properly when looking for end of word while
+ line breaking. @866
+B46. Repair the ``tricky'' part of \\{get_node}: I used the \\{info} field
+ when I meant to say \\{llink}. @127
+# Now the |\corners| macro of |acphdr| works! [See |\setcornerrules|
+ in {\sl The \TeX book}, page 417.]
+D47. Reset \\{contrib_tail} properly in \\{build_page}. @995
+T48. Fix typo (|-| for |+|) in computation of \\{page_total}. @1004
+S49. Change the page-breaking logic: \TeX\ reached \\{fire_up} with
+ $\\{best_page_break}=\\{null}$ in one case,
+ since the badness was too bad. @1005
+M50. Perform the operation \\{delete_token_ref}(\\{top_mark}) only when
+ $\\{top_mark}\ne0$. @1012
+F51. Make \\{scan_toks} omit the initial |{| of an |\output| routine. @473
+I52. Insert a comma to make memory usage statistics look better. @639
+# About seven good hours of debugging today.
+# Tomorrow will be first-output day (I hope).
+* 14 Mar 1978
+# (Came in evening after sleeping most of day, to get computer at better time.)
+# (Some day we will have personal computers and will live more normally.)
+# 8:30pm, began to enter corrections to yesterday's problems.
+I53. Issue an error message for non-character in filename or in font name. @771
+I54. Display `|...|' for omitted stuff in \\{show_context} routine. @643
+L55. Watch out for the |SAIL| syntax `$\alpha+\beta\mathbin{\rm lsh}\gamma$';
+ it doesn't shift $\alpha+\beta$ left (only $\beta$). @464
+# That error was very hard to track down; it created a spurious link field
+ and sent $\\{hash}[0]=\null$|\beta| to the scanner!
+# I could have found this bug an hour sooner if I had looked at the correct
+ stack entries for \\{name} and \\{token_type}.
+D56. Show the correct page number when tracing pages before output is shipped. @638
+F57. Remember to nullify a box after using it. @1079
+I58. Issue an error message if\/ |\box255| isn't consumed by the output routine. @1015
+# I'm having trouble with the |BAIL| debugger;
+ it makes an illegal memory reference and dies,
+ when single-stepping past the entry to recursive procedures
+ \\{hlist_out} and \\{vlist_out}. So I have to reload and be careful
+ to go thru these procedures at high speed.
+P59. Fix bug in comment (memory parameter description said $\ge$ not $\le$). @11
+B60. Fix typo in definition of rule output (said $x,y$ not $\\{x0},\\{y0}$).
+ [This part of the code went away when |DVI| files were introduced.]
+B61. Correct the embarrassing bug in shellsort, where I said `$\le\\{str}[k]$'
+ not `$\le t$'.
+ [The first \TeX\ had to sort all output by vertical position on page.]
+M62. Make \\{start_input} set up \\{job_name} in the form needed by \\{shipout};
+ it uses obsolete conventions. @532,537
+L63. Insert |(| and |)| into the |SAIL| macro definition of \\{new_string}.
+ [This macro was for pre-|DVI| output.]
+M64. Unscramble the parameters of \\{out_rule}:
+ The declaration was $(\\{x0},\\{y0},\\{x1},\\{y1})$
+ while the call was $(\\{x0},\\{x1},\\{y0},\\{y1})$.
+# 4:30am, \TeX's first page is successfully output!
+# (It was `|\titlepage\setcpage1\corners\eject\end|'.)
+* 15 Mar 1978
+# 10:30pm. Today I'm instrumenting the line-breaking routine and putting it
+ through a bunch of tests.
+# (The inserted instrumentation had bugs that won't be mentioned here.)
+C65. Don't abort the job when \\{eq_destroy} redefines a
+ \TeX\ control sequence. @275
+# The first word of a paragraph won't be hyphenated \dots\ so be it!
+T66. Fix the typo in \\{line_break} that spoils the test for
+ `letters in the same font'. @896
+# The effect of that typo was to suppress all hyphenation attempts.
+B67\>25. Replace the space at paragraph end by fillglue, not by zero. @816
+L68. Pack the hyphen character properly into its node. @582
+T69. Fix a typo (`{\tentex\char'30}' for `|+|')
+ in the computation of \\{break_width}. @838
+M70. Change the |\end| maneuver; the present code doesn't end the job,
+ since I forgot that \\{back_input} uses \\{cur_tok}. @1054
+A71. Add a parameter to \\{try_break}, since the width is different at
+ a discretionary hyphen. [This problem will be solved differently in \TeX82,
+ when discretionaries become much more general.] @840
+F72. Bypass kern nodes in pre-hyphenation. @896
+F73. Supply code for the forgotten case `$<$ |"a"|' in pre-hyphenation.
+ [This case was later generalized to a test of \\{lc_code}.] @897
+B74. Change $\\{mem}[q]$ to $\\{prev_break}(q)$ in the
+ reverse-linking loop. @878
+# (Such blunders. Am I getting feeble-minded?)
+A75. Introduce special logic for \\{eject_penalty}; I was wrong
+ to think that forced ejection was exactly like an
+ infinitely negative penalty. @851
+A76. Use $(1+b)^2-p^2$ when computing demerits with $p<0$. @859
+# 6:30am. The line-breaking algorithm appears to be working fine
+ and efficiently.
+ On small measures (about 20 characters per line), it gives overfull boxes
+ instead of spaced out ones. Surprising but satisfactory.
+* 16 Mar 1978
+# 9pm. The plan for tonight is to test page breaking and more paragraphing.
+G77. Insert `|\topskip|' glue at beginning of page. @1001
+I78. Add `|\pausing|' feature. @363
+M79. Fix discrepancy: In \\{make_accent} I called \\{vpackage}
+ with a pointer to the first list item, but \\{vpackage} itself assumes
+ that the parameter is a pointer to that pointer. [The
+ \\{vpackage} of \TeX82 will be different.] @668
+# I checked for other lapses like that. Result: 14 calls OK, 12 NG.
+M80. Create a new temporary list head location, \\{hold_head}, since there's
+ a case where \\{vpackage} is improperly called with
+ parameter \\{temp_head}. [At this time \\{vpackage} uses \\{temp_head}
+ to make a list of all insertions found.] @1014
+# 11:30pm. The machine is tied up again.
+F81. Write code to handle charnodes in vlists; I forgot that I'd decided
+ to allow them. [Later I prohibited them again!] @669
+F82. Combine the page lists before pruning off glue
+ in \\{fire_up}; otherwise the pruning doesn't go far enough. @1017
+B83\>25. Fix typo where line-breaking starts: `\\{fill_glue}' should be
+ `\\{new_glue}(\\{fill_glue})'. @816
+I84. Add |/q| to |xspool| command (cosmetic change). [This changes a system
+ command that causes \TeX\ output to be printed on the Xerox Graphics
+ Printer (XGP), the progenitor of future laser printers;
+ the |/q| option says that the queue of printing requests should be
+ displayed on the user's terminal.] @642
+E85. Don't write a form feed after the last page of output. @642
+# To fix this, I reorganized \\{ship_out}, and it became simpler.
+T86. Correct a typo (`{\tentex\char'30}' for `|+|') in the \\{vlist_node} case
+ within \\{hlist_out}. [The output routines were quite different
+ at this time, because output went directly to the XGP.] @622
+I87. Change the message `|completed page|' to `|Completed for page|'. @638
+B88\>48. Fix yet another typo in the computation of \\{page_total}: My original
+ code said $\\{stretch}(p)$ instead of $\\{stretch}(q)$ (terrible). @1004
+P89. Document the dirty trick about \\{bot_mark}'s reference count.
+ [That trick is, fortunately, no longer useful.] @1016
+A90. Rethink the algorithm for contributing an insertion: The original code
+ tests for a page break after incrementing the totals but
+ before the \\{contrib_list} is updated. [\TeX78 handles insertions
+ in a hardwired manner that will be greatly generalized in \TeX82.]
+F91. Fix \\{get_node} again:
+ After the variable memory overflows, control falls through to \\{found}
+ instead of going to the \\{overflow} call. @125
+# I spent several hours tracking down that data structure bug!
+B92. Change \\{new_line} to \\{next_line} in yet another print command (see \#7).
+A93\>75. Amend the line-breaking algorithm:
+ |\break| in paragraph doesn't work with really bad breaks. @851
+# A problem to be diagnosed tomorrow: Each time I run the test program,
+ the amount of memory in use grows by 13 cells not returned.
+# Seven hours tonight.
+* 17 Mar 1978
+G94. Introduce \\{dead_cycles} to keep |\end| active until \\{ship_out}
+ occurs. @1054
+E95. Don't call \\{line_break} with an empty list. @1096
+S96. Take proper account of the (infinite) fillglue when computing
+ the width of a paragraph line preceding a display. @1146
+M97. Add a new parameter to \\{hpack} so that \\{line_break} won't be called
+ at the wrong time.
+ [This is for the soon-to-be-obsolete feature described in \#40.]
+L98. Give a warning message if there's an |\hfill| in the middle of a paragraph;
+ fillglue upsets the line breaker,
+ because floating-point calculations don't have sufficient accuracy. @868
+# I spent an hour looking for another bug in \TeX, but the following one was in \MF:
+ The \\{xgp_height} data in fonts had been supplied wrong.
+# It took two hours to recompile 32 fonts with proto-\MF.
+R99. Make \\{show_node_list} and \\{show_token_list}
+ more robust in the presence of software bugs. @182
+D100\>97. Do not remove nodes with \\{eject_penalty},
+ when the new parameter to \\{hpack} is \\{true}.
+E101\>97. Put a fast exit into \\{hpack}; e.g., at glue nodes, test `{\bf if}
+ \\{paragraphing} $\land$ $\langle$current width is large$\rangle$'.
+# 2am. I have to go to bed ``early'' tonight.
+* 18 Mar 1978
+# 3:30pm. (Saturday)
+I102. Add a parameter to \\{check_mem} (to suppress display unless needed). @167
+G103. Introduce a user-settable parameter |\maxdepth|, and
+ pass it as a parameter to \\{vpackage}. @668
+# I realized the need for |\maxdepth| while fixing insertions (see \#90).
+G104. Introduce a user-settable parameter for \\{line_break}: The constant
+ 2.0 in my original algorithm becomes |\jpar|
+ [later |\tolerance|], to be set like |\tracing|. @828
+D105. Reclaim the \\{eject_penalty} nodes removed during line-breaking. @879
+# (Those were the 13 extra nodes reported on Thursday.)
+# The \\{init_align} procedure worked right the first time!
+# Also \\{init_row}, \\{init_col}. But then\thinspace\dots
+S106. Rethink the command codes: \\{endv} in a token list has too high a
+ code for the assumptions of \\{get_next}. @207
+A107. Add a \\{prev_cmd} variable for processing delimited macro parameters;
+ the original algorithm loses track of braces.
+ [The rules will change slightly in \TeX82, and
+ \\{rbrace_ptr} will take on a similar function.] @400
+S108. Make the \\{get_next} routine intercept |&| and |\cr| tokens. @342
+# I'd thought I could just put |&| and |\cr| into
+ \\{big_switch} [i.e., in the stomach of \TeX, not the eyes];
+ that was a great big mistake.
+R109. Make more error checks on \\{endv}; e.g., it must not occur in
+ a macro definition or call. @780
+S110\>108. No, rethink alignments again; the new program still fails! @768
+# For the first time I can glimpse the
+ hairiness of alignment in general (e.g., `|\halign{\u#\v&...|'
+ when |\u| and |\v| are defined to include |&|'s and possible
+ alignments themselves).
+# I think there's a ``simple'' solution, by considering only whether
+ an alignment is currently active [in \S342].
+# 11:30pm. Went to bed.
+* 19 Mar 1978
+# Woke up with ``better'' idea on how to handle |&| and |\cr|.
+# (Namely, to consider a special kind of\/ |\def| whose parameters don't interrupt
+ on |&|'s and |\cr|'s.)
+# But replaced this by a much better idea (to introduce \\{align_state}).
+# 11pm. Began to use computer. Performed major surgery
+ (inserting \\{align_state} and updating the associated routines
+ and documentation).
+D111. Pop the alignment stacks in \\{fin_align}. @800
+T112\>110. Fix a (newly inserted) typo in \\{show_context}. @314
+D113\>110. Set \\{align_state} false when a live |&| or |\cr| is found.
+ [Originally \\{align_state} was of type \\{boolean}.] @789
+I114. Insert |\cr| when `|}|' occurs prematurely in an alignment. @1132
+M115. Remember to record \\{glue_stretch} when packaging an unset node. @796
+# I had a mistake in |acphdr| definition of\/ |\quoteformat|; also extra spaces.
+# My first test programs, used before today, were contrived to test macro
+ expansion, line-breaking, and page layout.
+# Next I'm using a test program based on Volume 2.
+C116. Make carriage-return, space, and tab equivalent for macro matching. @348
+F117. Omit the reference count node when displaying a mark. @176
+B118. Correct a silly slip: I wrote `\\{type_displacement}' instead of
+ `\\{value_displacement}' when packing data in a penalty node. @158
+M119. Don't go to \\{build_page} after seeing |\noindent|; \TeX\ isn't
+ ready for that.
+ [In the original program, this was an instance of a bad {\bf goto}.] @1091
+# I had undesired spaces coming thru the scanner in my macro definitions
+ of\/ |\tenpoint| [see {\sl The \TeX book}, page 414].
+# 4am. \TeX\ now knows enough to typeset page 1 of Volume 2!
+# Also it did its first ``math formula'' (namely `|$X$|') without crucial error.
+# (Except that the italic correction was missing for some reason.)
+D120. Remember to decrement \\{cur_level} in \\{fin_align}. [The routines will
+ eventually become more general and use \\{unsave} here.] @800
+D121. Remember to increment \\{cur_level} in error corrections by
+ \\{handle_right_brace}. [A better procedure will be adopted later.] @1069
+T122. Fix a typo: (`|{|' instead of `|}|') in error message for $\\{mmode}+
+ \\{math_shift}$. @1065
+R123\>99. Make \\{show_noad_list} more robust and more like the new
+ \\{show_node_list}. [The routines will be combined in \TeX82.] @690
+L124. Fix a typo in \\{char_box}: should say \\{font_info_real}.
+ [In \TeX78 a single array
+ is used for both \\{real} and \\{integer}; in \TeX82 things will be
+ \\{scaled}.] @554
+B125. Fix typos in the definitions of \\{default_rule_thickness} and
+ \\{big_op_spacing}; they shouldn't start at \\{mathex}(7). @701
+B126. Reverse the \\{before} and \\{after} conventions in math nodes. @1196
+# I had them backwards; this turned
+ hyphenation on just before math, and off just after it!
+# Seven and a half hours debugging today.
+ Got through the test program a little more.
+ But \TeX\ blew up on `|$Y+1$|'; tomorrow I hope to find out why.
+* 20 Mar 1978
+# 8pm. I decided to work next on a super-hairy formula.
+C127. Change `|\ascii|' to `|\cc|' (character code). [This name will change again
+ later, to `|\char|'.] @265
+E128. Don't bother to store a penalty node at the beginning of |$$| when
+ the paragraph-so-far fits on a single line, since such a penalty
+ has already been stored. [These conventions will change later, and
+ the |\predisplaypenalty| will always be stored.] @1203
+S129. Avoid reference to \\{tail} in \\{build_page},
+ if $\\{nest_ptr}>0$. @995
+B130. Correct a silly
+ slip in \\{math_comp} (the exact opposite of what I did in \#118). @1158
+B131. Rectify my mental lapse in \\{make_fraction}; I said \\{nucleus} instead of
+ \\{thickness}. @743
+F132. Mask off the math class when scanning delimiters. @1160
+C133. Allow an optional space after |\def{...}|. [This decision
+ will be retracted later.] @473
+# My test example is so complicated it causes the semantic stacks to overflow!
+L134. Don't test for no pages output by looking at the channel status. @642
+T135. Fix typo in definition of\/ |\mathop| (\\{open_noad} not \\{op_noad}). @1156
+A136. Rewrite \\{fin_mlist}, because `|\left(...\above...\right)|' doesn't
+ parse correctly; the |\left| goes into the numerator,
+ the |\right| into the denominator. @1184
+B137. Correct the use of \\{depth_threshold} in \\{print_subsidiary_data}:
+ Simple fields get shown while others look empty. @692
+I138\>78. Return the carriage before showing the first line of a new file
+ when pausing. @538
+M139. Fix bug: The call \\{show_noad_list}(\\{mem}[|..|]) should be
+ \\{show_noad_list}(|..|), in the \\{incompleat_noad} case
+ of \\{show_activities}. @219
+# 3am. The whole messy formula has been parsed correctly into a tree.
+# The easy part is done, now comes the harder part.
+F140. Don't shift single characters down in \\{make_op}. @749
+D141. Make \\{clean_box} return a box (as its name implies), not an hlist. @720
+# Font info still isn't quite right, it has the wrong value of \\{quad}.
+A142. Retain the italic correction when doing \\{rebox};
+ can make $\\{glue_set}\ne0$ a flag for this. [A better
+ solution will be adopted later.] @715
+B143. Fix the bug that makes \\{rebox} bomb out: $\\{value}(p)$ should be
+ $\\{value}(\\{mem}[p])$. @715
+# 6am; ten hours today. \TeX\ didn't do |$\pi\over2$| correctly, but was close.
+# I found that the \\{rebox} problem (\#142) went away when I fixed the
+ \\{clean_box} problem (\#141); but I
+ will leave the extra stuff about $\\{glue_set} \ne 0$
+ in the program anyway, just for weird cases.
+E144. Omit extra levels of boxing when possible in \\{clean_box}. @721
+# (To do this, I need to face the \\{rebox} problem anyway.)
+* 21 Mar 1978
+# 10pm. The computer is rather heavily loaded tonight.
+F145. Don't forget \\{thickness} when making a square-root sign (see \#131).
+ [The rule thickness will later be derived from the character height.] @737
+L146. Define $p$ local to the \\{make_fraction} routine. @743
+# Unwittingly using the global $p$ was a disaster.
+I147. Don't show the amount of \\{glue_set} when it's zero. @186
+D148\>142. Make \\{glue_set} nonzero in the result of \\{var_delimiter}. @706
+F149. Fix bug: The \\{math_glue} function didn't return any result. @716
+T150. Fix typo in \\{char_box} ($c$ not $w$); this caused a subscripted~$P$ to come
+ out the same width as an unsubscripted~$P$. [Later changes in the rules
+ will move this computation to \S755.] @709
+P151\>144. Revise \\{clean_box} to do operations that are needed
+ often because of the \\{rebox} change. @720
+D152. Use the new \\{clean_box} to avoid a bug in |\sqrt{\raise...}|. @737
+Q153. Change the definition of\/ |\not| so that it's a relation
+ (which will butt against the following relation). [All math
+ symbols and Greek letters are defined in |INITEX| at this
+ time, not in a changeable format definition.]
+I154. Give error message `{\tt Large delimiter must be
+ in mathex font}', instead of calling \\{confusion},
+ since the error can occur.
+ [This particular error is impossible in \TeX82.] @706
+F155. Change the use of $p$ in \\{var_delimiter}; it isn't always set when I say
+ {\bf goto} \\{found}. @706
+# Another font problem now surfaced: The mathex meta-font didn't compute \TeX\ info
+ in a machine-independent way.
+ (It took two hours to correct this.)
+D156. Don't forget to set $\\{type}(b)$ in all relevant cases of
+ \\{var_delimiter}. @708
+B157. Use the correct sign convention for \\{shift_amount} in \\{hpackage}. @653
+F158. Always kern by \\{delta} when there's no superscript. @755
+M159. Declare \\{space_table} to be $[0\,.\,.\,6,0\,.\,.\,6]$
+ not $[0\,.\,.\,7,0\,.\,.\,7]$; otherwise its entries are
+ pre\-loaded into the wrong positions. [The \\{space_table} in \TeX78 is
+ $7\times7$; it will become $8\times8$ in \TeX82,
+ represented as a string called \\{math_spacing}.] @764
+C160. Use a negative value, not zero, to represent a null delimiter.
+ [Actually zero will come back again later.] @685
+C161\>127. Change |\cc| to |\char|. @265
+L162. Don't use tricky subtraction on packed data when changing $q$ to
+ an \\{ord_noad} in \\{mlist_to_hlist}; subtraction isn't always safe. @729
+B163. Fix two typos in the \\{space_table} (|*| for |0|). @764
+F164. Initialize \\{cur_size} everywhere (I forgot it in two places). @703
+A165. Reset \\{op_noad} before resetting \\{bin_noad}. @728
+F166. Treat $\\{display_style}+\\{cramped}$ the same as \\{display_style}
+ inside \\{make_op}. @749
+B167. Shift the character correctly in the non-|\displaystyle| case of
+ \\{make_op}. @749
+# Still another font problem: The italic corrections are wrong because
+ the corresponding array was declared \\{real} in proto-\MF\ (and
+ italic corrections were used in nonstandard way in |mathex|).
+B168. Use \\{depth} instead of \\{height} in \\{var_delimiter}.
+ [Later, both were used.] @714
+Q169. Skew the accents according to the font \\{slant}. [Soon retracted.] @741
+# At this point I think nearly all the math routines have been exercised.
+# Tomorrow they should work!
+# Eight hours debugging today.
+* 22 Mar 1978
+# (Wednesday, but actually Thursday:
+ I began at midnight because I was proofreading a paper.)
+# I checked out the font access tables, slowly (i.e., all the |\mathcode|
+ and special-character name entries were catalogued).
+Q170\>169. Do {\bf not\/} consider slants after all in the math accent
+ routine, since slanted math letters are put differently into fonts. @741
+B171. Don't use $q$ for two different things simultaneously
+ in \\{make_math_accent}. @738
+F172. Fix bug in \\{compact_list} (I forgot to advance the loop variable).
+ [This procedure became unnecessary in \TeX82.]
+M173. Avoid conflict between \\{var_delimiter} and \\{mlist_to_hlist}, which
+ want to use \\{temp_head} simultaneously. @713
+T174. Fix bad typo in \\{overbar} routine ($b$ for $p$). @705
+# Finally \TeX\ got to \\{after_math} after dealing with that
+ hairy formula\thinspace\dots
+T175. Fix another bad typo: $p$ for $b$ this time. @1199
+L176. Insert more parentheses (twice) because of `|lsh|' precedence
+ in |SAIL|. @1199
+M177\>36. Use the new hanging-indentation conventions when formatting displayed
+ equations. @1199
+Q178. Recompute penalties so that break is allowed after \\{punct_noad}s. @761
+F179. Center the large delimiters vertically. @749
+Q180. Round all rule sizes (up) before drawing them. @589
+Q181. Provide more space over $x$ in $\sqrt x$, and more space atop vincula.
+ @705,737
+Q182. Make large delimiters large enough to cover formula height
+ (important for subscripts, superscripts). @762
+I183. Insert |/ntn=33| on XGP prompt message so that complex math
+ won't blow the device driver. [See \#84.]
+P184\>161. Update the comment about the meaning of\/ |\char|, since it
+ can be used in math mode. @208
+# Six hours today.
+* 23 Mar 1978
+# 11pm, Maundy Thursday.
+C185\>104. Make |\tracing| and |\jpar| follow block structure. @283
+# It took me two hours to enter yesterday's corrections,
+ because the changes were so numerous.
+M186. Fix bad call on \\{begin_token_list} when marks are to be scanned. @396
+# Now the formula looks like it should, modulo problems in fonts.
+Q187. Prevent an exponent from going below baseline + xheight/4. @758
+B188. Change \\{quad} to \\{math_quad} when finishing a display
+ (several places). @1199
+A189. Don't use \\{append_to_vlist} when putting an |\eqno| box on a separate
+ line, because the page shouldn't break at glue there.
+ [Later, the \\{append} will be used but preceded by an
+ infinite penalty.] @1205
+S190. Increase the input \\{stack_size}; \TeX\ may need to back up a lot. @11
+A191. Don't assume that $p$ always points to a glue node when
+ a page is broken. @1017
+A192. Use \\{epsilon} in \\{scan_spec} (I had used a different
+ small constant). [This was a kludge to avoid the extra
+ parameter later called \\{exactly} or \\{additional}.] @645
+R193. Introduce a new procedure \\{scan_positive_length}, to prevent
+ negative or zero lengths in \\{scan_rule_spec}.
+ [This restrictive rule will be ``overruled'' later.] @463
+B194. Fix ridiculous bug in the leaders routine of \\{vlist_out}:
+ I had the initialization {\sl inside\/} the loop! @635
+L195. Eliminate confusion between the two temp variables named $h$;
+ one is \\{real} and the other is \\{integer}. @629
+F196. Include forgotten case (\\{leader_node}) in \\{hlist_out}.
+ [Type \\{leader_node} will be absorbed
+ into \\{glue_node} in \TeX82.] @622
+F197. Don't forget to compute \\{x0} in variable horizontal rules. @624
+# Seven and a half hours today.
+# \TeX\ seems to be ready to tackle my test file based on Volume 2.
+S198. Calculate \\{y00} in horizontal rules as an integer number of pixels
+ from the baseline, so that the baseline doesn't jump. @589
+* 25 Mar 1978
+# 2am Saturday. (Might as well drop Friday.)
+D199\>185. Make \\{def_code} consistent with the new |\tracing| conventions.
+ [Many tracing options are packed into a single
+ parameter called |\tracing|.] @1233
+R200\>185. Don't allow users to change nonexistent things like |\catcode1000|. @1232
+F201\>110. Reset \\{align_state} at beginning of \\{init_align}. @774
+F202. Don't forget \\{scan_left_brace} after |\noalign|. @785
+F203\>110. Set $\\{cur_cs}\gets 0$ in \\{get_next}, after |\cr|
+ causes a switch to the $\langle v_j\rangle$ template. @342
+# Ouch, that was a big bad bug, which took me three hours to find
+ (since I thought \TeX's low-level scanning mechanism was working).
+# Note to myself: I\/ {\bf knew\/} it would be cleaner to define \\{get_next}
+ so that it sets \\{cur_cs} to zero every time it begins [i.e.,
+ in \S341, where this change will in fact be made in \TeX82].
+ But I had avoided this on grounds of efficiency in the inner loop.
+ Well, now I have earned this tiny bit of efficiency.
+D204. Prohibit the first word of an unavailable node from becoming negative.
+ [The storage allocator of \TeX78 uses
+ a negative value to signify a node that is available, just as
+ `$\\{link}=\\{max_halfword}$' will signal availability in \TeX82.] @124
+# That was another bad one, it's not my night.
+# At least I'm developing more subtle diagnostic techniques.
+M205. Remember to un-negate the top \\{save_stack} entry
+ when \\{handle_right_brace} finishes an \\{insert_group}.
+ [This routine was completely revised in \TeX82.] @1100
+F206\>185. Initialize |\jpar| [i.e., |\tolerance|]. @240
+B207. Correct the display of insertion nodes by \\{show_node_list}. @188
+R208. Prevent \\{show_token_list} from generating really long strings
+ when in a loop. @292
+D209. Increase the reference count of \\{bot_mark} when
+ \\{vpackage} finds it. [This was later the job of \\{fire_up}.] @1016
+M210. Remember that the tokenlist for a mark ends with a |}|. @1101
+D211. Don't let \\{vpackage} lose the top insert. (It fails when
+ the very first item is a |\topinsert|.) @1014
+A212. And when that stupid code is corrected, make it handle
+ insertions first-in-first-out. @1018
+# Seven hours today.
+* 26 Mar 1978
+# Easter Sunday, will work till sunrise.
+I213. Add an `|i|' feature to the \\{error} recovery routine. @87
+I214. Include a prompt. @87
+C215. Ignore space after |\noalign{...}|. @1133
+# Otherwise, things are going well tonight;
+ I'm finding more bugs in my test program than in \TeX.
+# The `|i|' feature is proving to be very helpful.
+# I increased the size of \\{mem}
+ (now $\\{lo_mem_max}=3500$, $\\{mem_max}=10000$).
+# In fact I just needed to increase it again
+ (now $\\{lo_mem_max}=4500$, $\\{mem_max}=11000$).
+R216. Make |INITEX| output \\{mem_top} for consistency checking. @1307
+A217. Calculate the size of delimiters by considering the enclosed formula's
+ distance from the axis, not from the baseline. @762
+# I'm having trouble with a |SAIL| compiler bug; I must rearrange the program,
+ more or less at random, until it
+ compiles correctly. I hope the bug isn't more severe than it appears.
+D218\>210. Don't put a new group on \\{save_stack} if a null mark is expanded.
+ [\TeX82 will remove the `|}|' from the mark text.] @386
+# I had to redo the typewriter-style font since its width tables were wrong.
+# And I increased low-memory size again to 5500, then 6500.
+# Finally the entire test program was \TeX ed. Happy Easter! Six hours today.
+* 27 Mar 1978
+# Beginning at 2:30am.
+A219. Move |\vcenter| processing to the first pass of \\{mlist_to_hlist};
+ otherwise the height, depth, subscripts, etc., are way off. @733
+C220. Omit space after closing |$$|. @1200
+# Spacing is wrong in the formula $Y_1+\cdots+Y_k$; I have to rethink the use
+ of three dots.
+G221. Make conditional thin space available to user as |\|{\tentex\char'34}.
+ [Later will retract this.] @226
+Q222. Introduce |\dispaskip| and |\dispbskip| [later called
+ |\abovedisplayshortskip| and |\belowdisplayshortskip|]. @226
+# Reminder: I need to test line-breaking with embedded math formulas.
+I223. Make sure that $\\{interaction}\ne\\{error_stop_mode}$
+ in the `|Whoa|' error [\\{fatal_error}]. @93
+B224. Fix a big mistake in the \\{style_node} routine (which points to a glue spec,
+ not to glue itself); somehow this didn't cause trouble yesterday.
+ [In \TeX78, style nodes double as placeholders for math glue
+ like thin spaces.] @732
+C225. Make |\fntfam| obey group structure. [\TeX78's |\fntfam| operation
+ is a combination of \TeX82's
+ |\textfont|, |\scriptfont|, and |\scriptscriptfont|.] @1234
+# At this point the test routine for Volume 2 works perfectly.
+# But I will change the page width in order to check harder cases.
+Q226\>178. Disable automatic line breaks after punctuation in math
+ (e.g., consider $f(x,y)$). @761
+S227. Represent italic corrections as boxes, not glue, so that they won't be broken.
+ [The |\kern| command doesn't exist yet.] @1113
+# Eight hours today.
+B228. Fix a bug that just clobbered the memory: Call \\{free_avail}, not
+ \\{free_node}, in the \\{ins_node} case of \\{vpackage}.
+ [This logic will change completely in \TeX82.] @1019
+* 29 Mar 1978
+# (Wednesday) Again beginning at 2:30am.
+Q229. Put still more space above and below fraction lines in displayed
+ formulas. @746
+G230\>189. Install an infinite penalty feature, which positively suppresses breaks;
+ use it in displayed formulas whose |\eqno| doesn't fit. @1205
+F231. Call \\{build_page} after finishing a display; and don't
+ go to the |\noindent| routine because of the next remark. @1200
+S232. Put |\parskip| glue just before a paragraph, not just after
+ (since it interferes with a penalty after). @1091
+# Although the test program gives correct output, it generates 46 locations
+ of variable-size memory and 280 of one-word memory that are not freed.
+F233. Recycle the ulists and vlists in \\{fin_align}. @801
+M234\>25. Fix bug when deleting space at end of paragraph:
+ \\{delete_glue_ref}(\\{cur_node}) not
+ \\{delete_glue_ref}(\\{value}(\\{cur_node})). @816
+# There's also a more mysterious type of uncollected garbage,
+ a \\{fraction_noad} corresponding to
+ |$p\choose$|, an \\{incompleat_noad} not completed.
+# Couldn't find that one, so I recompiled with \#233 and \#234 corrected.
+# Now it gains just 10 locations of variable-size memory and 7 of the other kind.
+I235. Extend \\{search_mem} to search \\{eqtb} also. @255
+D236\>143. Fix bug in \\{rebox} when $\\{list_ptr}(b)=0$. @715
+# The seven one-word nodes were generated by this bug;
+ \\{rebox}\kern-1pt\ put them onto a linked list starting with
+ $\\{mem}[0]$, growing at the far end!
+D237. Remember to complete each \\{incompleat_noad}. @1184
+# This solved the other mystery. I had never noticed that my test output
+ was actually wrong: |$p\choose k$| came out as `$k$'.
+# After these corrections, the test routine worked\thinspace\dots\ I feel
+ that \TeX\ is now pretty well debugged
+ (except perhaps for error recovery)---it's time to celebrate!
+* 1 Apr 1978
+I238. Don't quit after file lookup fails. @530
+* 2 Apr 1978
+P239. Add \\{\TeX_font_area}, so that it's easier to change the
+ default library area associated with a device. @514
+* 3 Apr 1978
+L240. Insert parentheses again, to cope with the precedence of\/ |lsh|
+ when packing data. (See \#55 and \#176.) @1114
+# I had never tried $\\{hmode}+\\{discretionary}$ before!
+M241. Remember that \\{back_error} requires \\{cur_tok} to be set.
+ (Problem can arise during error recovery on parameter |#|$n$
+ with $n$ out of range.) @476
+* 4 Apr 1978
+I242. Add a deletion feature to the \\{error} routine. @88
+* 5 Apr 1978
+Q243. Reset \\{space_factor} after |\/| [this was later rescinded]
+ and after math in text. @1196
+* 10 Apr 1978
+G244\>104. Replace |\jpar| and |\tracing| by a new primitive |\chpar| for parameters.
+ It allows a user to change those quantities as well as the penalties for
+ hyphens, relations, binary ops, widows. @209
+* 14 May 1978
+# Beginning to typeset a real book (Volume 2, second edition), not just a test.
+Q245. Make math in text end with spacing as if it were
+ followed by punctuation. [This rule will soon be rescinded.] @760
+F246. Insert |\times| into the hash table; I left it out by mistake.
+ [It will eventually move into |plain.tex|.]
+C247. Change the names of Scandinavian accents from |\o|, |\oslash|,
+ |\Oslash| to |\a|, |\o|, |\O|. [This will also move to |plain|.]
+* 17 May 1978
+B248. Fix a silly bug that hasn't been tweaked until today:
+ `|\halign to size|' [obsolete in \TeX82] used \\{vsize}
+ instead of \\{hsize}. @645
+* 19 May 1978
+G249. Add a |\topbaseline| feature [later called |\topskip|]. @1001
+Q250\>245. Subtract the math spacing change of May 14. @760
+A251. Skip past blanks in the \\{scan_math} procedure. [This blank-skipping
+ will eventually go into \\{scan_left_brace}.] @403
+I252. Introduce a \\{missing_brace} routine [later generalized] to improve
+ error recovery in $\\{mmode}+\\{math_shift}$, when the top
+ of \\{save_stack} isn't a \\{math_shift_group}. @1065
+Q253. Adjust the math spacing between closing parentheses and
+ Ord, Op, Open, Punct. @764
+Q254. Make the underline go further under. @735
+S255\>96. Compute the proper natural width when a displayed equation follows
+ a paragraph whose fillglue has been deleted by \\{line_break}. @1146
+* 20 May 1978
+A256. Fix the spurious value of \\{prev_depth} inside alignments. @775
+S257. Consider (and defeat) the following scenario:
+ The u and v lists are built in \\{init_align} using \\{temp_head};
+ then while scanning `|\tabskip 2pt\rt{...}|' the macro |\rt| is expanded,
+ clobbering \\{temp_head}. @779
+# That bug was more subtle than usual.
+Q258. Add the parameter \\{num3}, so that the positioning of\/ |\atop|
+ can be different from that for fractions. @700
+Q259. Add new parameters \\{delim1} and \\{delim2}, so that
+ |\comb| can use fixed size delimiters, not computed as with |\left|. @748
+* 22 May 1978
+C260\>221. Change |\|{\tentex\char'34} to |\|{\tentex\char'35} and introduce
+ |\|{\tentex\char'34} as the negative of\/ |\|{\tentex\char'35}.
+ [Later obsolete.] @226
+L261. Fix the display of negative penalty nodes; \\{show_node_list} is confused
+ when a negative value has been packed into the middle of a word. @194
+# Memory overflow just occurred with $\\{lo_mem_max}=7500$ and $\\{mem_max}=16384$.
+ So I have to go to 15-bit pointers. (A problem on 32-bit machines?)
+* 23 May 1978
+Q262. Add a new parameter \\{big_op_spacing5}, for
+ extra space above and below limits of big displayed operators. @751
+F263. Initialize \\{incompleat_noad} in |$$\halign{...}$$|. @775
+# That was another heretofore-untested operation. How much of the code
+ has not yet been exercised?
+F264\>238. Close the file when doing lookup-failure recovery. @27
+I265. Improve the error recovery for `|Extra &|'. @792
+R266. The top piece must be calculated mod 128 in \\{var_delimiter},
+ to guarantee a valid subscript range. [Obsolete in \TeX82.] @546
+B267\>252. Fix a blunder in new \\{missing_brace} code. @1065
+B268\>262. Fix a blunder in new code for limits on display operators. @751
+* 26 May 1978
+Q269. Don't insert a new penalty after an explicit penalty in math mode. @767
+# The hash table overflowed; I ought to make it much bigger.
+R270\>110. Avoid possible bad memory references in alignment
+ when there is erroneous input after |\cr|. [Instead of
+ \\{extra_info}, the value of \\{cur_align} in \TeX78 is negated,
+ because we need only distinguish |\cr| from |&|.] @789
+S271. Make the dimension parameters like |\hsize| all global, so that they can
+ be set in the |\output| routine. @279
+# This led to major simplifications, also to major surgery.
+# [But it was a kludgy decision, overruled in \TeX82.]
+D272\>94. Don't forget to set the type of the new null box in the |\end| routine. @1054
+* 27 May 1978
+# The data overflowed memory again, both low and high, doing Section 3.3.2.
+R273\>184. Mask off extra bits of\/ |\char| in math mode,
+ to avoid bad memory references. @1151
+B274. Zero out the negative |\medmuskip| in script styles. @732
+* 29 May 1978
+S275. Be prepared to handle an undefined control sequence during \\{get_x_token}.
+ (Can fix this by brute force, using \\{get_token} instead of \\{get_next}.)
+ @380
+D276. Correct the superscript shift when a single character is raised. @758
+R277\>184. Mask off all but 7 bits in |\char| routine,
+ to avoid space-factor index out of range. @435
+# More memory capacity overflows.
+E278\>22. Fix \TeX's overflow stop so that I don't have to wait for loading of the
+ |BAIL| debug routines. [System dependent.] @93
+F279. Remember to adjust the page number
+ when a file page ends in mid-macro. [System dependent.] @306
+* 5 Jun 1978
+R280. Make sure that the arguments of positioning commands don't overflow
+ their field size. @610
+I281. Report the excess amount when giving an overfull box warning. @666,677
+* 7 Jun 1978
+Q282. Use $\ge$ instead of $>$ as termination criterion in \\{var_delimiter}. @714
+R283. Disallow |\eject| in math mode. [In \TeX78, |\eject| is
+ distinct from |\break|; in horizontal mode it includes
+ \TeX82's `|\vadjust{\break}|'.] @1102
+Q284. Don't put too much clearance above |\sqrt| in text style. @737
+* 9 Jun 1978
+G285\>110. Make \\{align_state} an integer variable, not \\{boolean},
+ so that |\eqalign| can be within another |\eqalign|. @309
+C286. A |\mark| should expand its input. @1101
+* 10 Jun 1978
+E287. Provide for preloading of fonts. @1320
+L288. Close the output file before switching to edit the input file with
+ the `|e|' option. @84
+E289. Return adjustments found by \\{hpack} to free storage if
+ they're not used. [Later, \\{hpack} will detach them only
+ when they're used.] @655
+Q290. Strive for consistency between \\{make_under} and \\{make_over}.
+ @735
+* 18 Jun 1978
+B291\>236. Fix a serious error in \\{rebox}
+ (`$b$' instead of `$\\{list_ptr}(b)$'). @715
+# Strange that such a bug would now surface for the first time!
+C292. Remove |\deg| from |INITEX|, since macros suffice.
+Q293. Add an extra hyphenation penalty for two hyphenated lines in a row. @859
+* 19 Jun 1978
+S294. Introduce the `\\{no_new_control_sequence}' switch. Among other things,
+ this will prevent an undefined control sequence following \\{scan_math}
+ from clobbering the save stack. @259
+* 20 Jun 1978
+L295. Change the badness test
+ `$\\{glue}\le0.0$' to `$\\{glue}\le0.0001$'. [\TeX82 will avoid such
+ problems by calculating badness without floating point arithmetic.] @99
+R296. Force \\{badness} to be at most $10^{19}$. @108
+I297. Add \\{end_template} for better error recovery in alignments. @375
+E298\>287. Make |INITEX| more like the real \TeX; my simple scheme for
+ font preloading was no good because it left thousands of `dead'
+ words in memory. @8
+E299. Economize disk space by using internal arrays in load
+ modules that aren't being reinitialized. [System dependent.]
+E300. Move the declaration of \\{mem} to the semantics module, so that the
+ object code will be more efficient. [System dependent. The code of
+ \TeX78 was divided into separately compiled modules for syntax,
+ semantics, output, extensions, and general organization.]
+* 21 Jun 1978
+# Today I'm working on the user manual.
+C301. Disallow |\input| except in vertical mode. [I will change this in
+ \TeX82, treating |\input| as a case of expansion.] @378
+I302. Add error recovery for \\{endv} and \\{par_end} occurring in math mode.
+ @1047
+G303. Generalize |\ifT| to |\if T|. @506
+* 22 Jun 1978
+F304. Preload the |\bullet| [later done by |plain.tex|].
+D305\>256. Get the correct \\{prev_depth} at the beginning of an alignment. @775
+C306. Change |\eject| so that it ejects only once. @1000
+* 14 Jul 1978
+I307. Look in standard area if a file isn't found in the user's area. @537
+I308. Echo all online inputs in the transcript file. @71
+* 19 Jul 1978
+Q309. Equalize spacing when only one of numerator/denominator is big. @745
+Q310. Prevent subscript from getting too high above baseline. @757
+R311. Avoid infinite loop when stack overflows: \\{push_input} should say
+ `{\bf if} $\\{input_ptr}\ge\\{stack_size}\;\land\;\\{interaction}=
+ \\{error_stop_mode}$'. @321
+* 22 Jul 1978
+C312. Make |\quad| meaningful outside math mode. (All fonts must
+ be generated again!) @558
+I313. Show the nesting level at the end of \\{show_activities}.
+ [But I decided not to do this in \TeX82.] @218
+C314. Put in |\>| [namely, |\mskip\medmuskip|;
+ \TeX78 already has |\|{\tentex\char'35}, for conditional |\thinmuskip|,
+ as well as the negative amounts |\<|,~|\|{\tentex\char'34}].
+ Change the name of vector accent from |\>| to |\b|. [Math spacing
+ operators will become much more general in \TeX82.] @716
+* 25 Jul 1978
+Q315\>94. Give the correct |\hsize| and |\vsize| to the null boxes created
+ at |\end|. @1054
+A316\>94. And don't ``append'' them. [Later this was changed, so
+ that it would work better with generalized output routines.] @1054
+I317\>297. Remove the control sequence |\endv|,
+ since error recovery is now better. @375
+I318. Define another mode of tracing: It says `|OK|' and stops after
+ |\showlists|. @1298
+Q319\>244. Give better defaults to parameters. [Later done by |plain.tex|.] @209
+I320. Allow more bits in the packed representation of\/ |\showboxdepth|. @238
+I321. Scan past delimiters and/or dimensions when recovering from
+ ambiguous fractions. @1183
+R322. Reduce accent numbers modulo 128 or 512, depending on the
+ mode. @1165
+I323. Include a warning, `|(\end occurred on level ...)|'. @1335
+* 28 Jul 1978
+# (I'm writing Chapter 27 of the manual: `Recovery From Errors'.)
+I324. Improve the error message in \\{scan_digit}. [This procedure will change
+ its name to \\{scan_eight_bit_int}, when the number of registers
+ increases from 10 to~256.] @433
+I325. Don't report overfull boxes if they're less than .1~point
+ over. @666,677
+I326. Give the user extra chances to define the font, if \\{read_font_info}
+ is unsuccessful. @560
+I327. Change default recovery for bad parameter number from |#1| to |##|, since
+ |#1| won't always work and since |##| is probably intended. @479
+I328. Omit the |"Negative?"| message on things like \\{scan_char_num}. @435
+I329. Improve error recovery when a large delimiter isn't in family~3.
+ [Obsolete.]
+I330. Give a more appropriate error message when the input is `|$\right|'. @1192
+# Currently \TeX\ says `|Missing $|'!
+I331. Call \\{back_input} before the error message in \\{back_error},
+ not afterwards. @327
+* 1 Aug 1978
+I332. Give an appropriate warning when there's no input file and the user
+ types `|e|'. @84
+L333. Increase the system pushdownlist size so that the manual will compile.
+ [Procedures \\{hlist_out} and \\{vlist_out} can recurse deeply.]
+# Yesterday I distributed 45 preliminary copies of the manual; today I
+ took out the ``debugging hooks'' and put \TeX\ up as a system program.
+* 2 Aug 1978
+# I'm typing Volume 2 again (currently in Section 4.2.2). Culture shock!
+G334. Introduce a |\ragged| parameter, to indicate a degree of raggedness.
+ [Previously, ragged-right setting was performed when the
+ |\tolerance|/100 was odd! Eventually a better approach,
+ with |\rightskip| and such things, will be discovered.] @886
+Q335. Omit the `widow penalty' in one-line paragraphs. @890
+* 5 Aug 1978
+G336. Generalize |\pageno| to |\count|\<digit>. @236
+D337\>285. Update \\{align_state} when recovering from `|Missing {|' and
+ `|Extra }|' errors. @1069,1127
+I338. Show ``runaway'' tokens, making it easier to pinpoint an error. @306
+* 22 Aug 1978
+G339. Add |\predisplaypenalty|. @1203
+I340. Clarify error messages; they should indicate when something has been
+ inserted, etc. @1064
+* 23 Aug 1978
+I341\>114. Substitute `|Extra }|' for the losing `|Missing \cr|' error message. @1069
+I342\>213. Go past online insertions in \\{show_context}. @311
+Q343. Exact no penalty for breaking one line before a display. @1145
+I344\>338. Check for runaways at end of file. @362
+I345. Give error message when a macro argument begins with |}|. @395
+* 24 Aug 1978
+L346\>213. Remove extra line-feed in \\{show_context} after printing insertions.
+ [System dependent.] @318
+* 25 Aug 1978
+Q347. Leave no glue at top of page, even after |\eject|. @997
+* 27 Aug 1978
+P348. Adopt Guy Steele's new version of the \TeX\ source files. [He has
+ recently made a copy and modified it by introducing
+ compile-time switches for MIT conventions as an alternative to SUAI.
+ This is the first time that \TeX\ is being ported to another site;
+ additional switches for PARC, TENEX, TOPS10, and TOPS20 will be
+ added later, using the Steele style.]
+* 1 Sep 1978
+Q349. Don't pass over leader nodes in the \\{try_break} background
+ computation. [At this time, leaders have not yet been
+ unified with glue.] @837
+Q350\>82. Prune away all penalties at the top of a page. @997
+* 4 Sep 1978
+I351\>338. Include `|\|' in error message about a runaway argument. @306
+* 8 Sep 1978
+# I just remade all the fonts, with increased ligature field size.
+B352\>350. Insert a necessary {\bf goto} statement in the first branch of
+ the new penalty routine within \\{build_page}. @997
+* 30 Sep 1978
+M353\>338. Make the token list for runaway arguments meaningful outside
+ of \\{macro_call}. (I just had a runaway argument ending with
+ `|\lcm|', which turned out to be the
+ control sequence in hashtable location 0.) @371
+R354. Avoid infinite loop when recovering from |$$| in
+ restricted horizontal mode. @1138
+L355. Fix two hyphenation bugs related to |-ages|, |-ers|.
+ [A completely new algorithm for hyphenation will go into \TeX82.]
+Q356. Add |-est| to hyphenation routine;
+ also disable |puz-zled| and |rat-tled|, etc.
+* 4 Oct 1978
+G357. Add new primitive |\vtop|. @1087
+Q358. Treat implicit kerns properly after discretionary hyphens have been
+ inserted. @914
+* 4 Nov 1978
+Q359. Forget the half quad originally required at left and right when
+ centering displayed equations without equation numbers. @1202
+* 11 Nov 1978
+R360. Don't let the postamble come out empty. [This could occur if no fonts were
+ selected.] @642
+* 15 Nov 1978
+C361. Allow optional space after digit in \\{scan_int} routine. @444
+* 17 Nov 1978
+R362. Make the \\{check_mem} procedure slightly more robust. @167
+* 20 Nov 1978
+C363. Make the |\par| in a |\def| match the |\par| that comes automatically with
+ a blank line. (Suggested by Terry Winograd.) @351
+G364. Add new parameter |\mathsurround| for spacing before and after
+ math in text. @1196
+G365. Extend |\advance| to allow increase by other than unity. [At this
+ time it applies only to the ten |\count| registers, and it is
+ called |\advcount|.] @1238
+* 25 Nov 1978
+G366. Add a new primitive: |\unskip|. @1105
+G367. Add new primitives |\uppercase| and |\lowercase|. @1288
+* 28 Nov 1978
+M368\>338. Don't let |\mark| and \\{macro_call} interfere with each other's
+ \\{scanner_status}. @306
+M369. Omit extra |}| after \\{show_node_list} shows a |\mark|,
+ since the right brace is already there. (See \#210.) @176
+G370. Add a new primitive suggested by Terry Winograd: |\xdef|. @1218
+* 29 Nov 1978
+S371. Delete a space following |\else{...}| also in the
+ false case. [\TeX78 uses braces, not |\fi|, for conditionals.]
+D372\>320. Make |\tracing| set |\showboxbreadth| as advertised. @198
+F373. Account properly for kerns in width calculations of \\{line_break}.
+ @866
+Q374\>364. Delete a \\{math_node} at the beginning of a line. @148
+A375\>339. Guarantee that |\predisplaypenalty=10000| will suppress page breaking
+ before a display. @1005
+* 6 Dec 1978
+L376. Change the file opening statement to allow lines up to 150 characters long.
+ [System dependent.]
+* 16 Jan 1979
+F377\>365. Initialize \\{negative} properly in the |\advance| routine
+ with a |\count| as argument. @440
+* 20 Jan 1979
+R378. Try to keep complex, buggy preambles of alignments from
+ crashing the program. @789
+* 17 Feb 1979
+I379\>376. Give more detailed information when warning about a
+ long line being broken. [System dependent;
+ the buffer size in \TeX78 is very limited.]
+L380. Declare $p$ local to \\{try_break}, for the ``rare'' case code.
+ [My original program included the following comment:
+ ``This case can arise only in weird circumstances due to
+ changing line lengths, and the code may in fact never be executed.''
+ Later Michael Plass will discover that variable line lengths require an
+ entirely different algorithm, using \\{last_special_line}.]
+ @847
+F381\>334. Don't omit the raggedness correction when the
+ last line of paragraph has to shrink. [Obsolete in \TeX82.]
+* 22 Feb 1979
+F382\>363. Don't forget to return from \\{get_x_token} after finding |\par|. @351
+Q383. Add a new parameter: |\lineskiplimit|. @679
+C384. Change the syntactic sugar:
+ `|\hbox par|' replaces `|\hjust to ...{overfull}|'. [This vastly improves on
+ the old idea (see \#40), but there still is no internal vertical mode.]
+C385. Introduce new names |\hbox| and |\vbox| for |\hjust| and |\vjust|. @1071
+G386. Add a new condition: |\ifpos|. [It will later be
+ generalized to |\ifnum| and |\ifdim|.] @513
+G387. Add |vu| and |\varunit|. [\TeX82 will eventually allow arbitrary
+ internal dimensions as units of measure.] @453
+G388\>312. Add an |em| unit. @455
+C389. Legalize |\hbox spread |\<negative dimension> [since \\{scan_spec}
+ no longer uses the sign as a flag]. @645
+* 10 Mar 1979
+C390\>370. Make \\{scan_toks} expand |\count| during |\xdef|. [This will
+ change later when |\the| and |\number| are introduced.] @367
+* 23 Mar 1979
+Q391. Put only 100000\thinspace pt stretch at the end of a paragraph instead of
+ 10000000000\thinspace pt.
+ [In \TeX78, ``infinite'' glue is actually finite but large; in the
+ language of \TeX82 we would say that |\parfillskip|, which is
+ not yet user-settable, is being changed to be like |\hfil| instead of
+ like |\hfill|.] @816
+Q392. Treat the last line of a paragraph more consistently with
+ the other lines (e.g., when |\hfil| appears in mid-paragraph), by
+ effectively inserting \\{inf_penalty} at the end. @816
+* 31 Mar 1979
+S393. Ensure that penalty nodes aren't wiped out, in weird cases where
+ breaks occur at penalties that normally disappear. @879
+* 27 Apr 1979
+A394. Correct the page number count when files begin with an empty page.
+ [System dependent.]
+G395. Allow the \\{math_code} table to be changeable via |\chcode|.
+ [In \TeX82, |\chcode| will split into |\mathcode| and |\catcode|.] @1232
+I396\>332. Don't accept `|e|' after an error message if not
+ inputting from a file. @84
+* 29 May 1979
+F397. Don't call \\{end_file_reading} if you haven't already invoked
+ \\{begin_file_reading}; this could happen when trying to
+ recover from an error in \\{start_input}.@537
+* 7 Jun 1979
+A398\>306. Be sure to eject two pages,
+ when |\eject| comes just at the time another break is preferable
+ (e.g., when the page has just become too full). @1005
+* 27 Jun 1979
+I399\>354. Don't say `|You can't do that in math mode|' when the user
+ says `|$$|' in restricted horizontal mode! @1138
+* 30 Jun 1979
+G400. Add |wd|, |dp|, |ht| dimension units. @455
+I401\>307. Don't try the system area for file names whose area is explicitly
+ indicated. @537
+* 1 Jul 1979
+G402. Allow letters as (ASCII) numbers [without the |`| marker introduced
+ later]. @442
+* 2 Jul 1979
+F403. Fix a |\gdef| bug: If the control sequence was never defined before
+ [this later became the \\{restore_zero} option],
+ don't remove it at group end. @282
+* 16 Jul 1979
+I404\>320. Update \\{show_noad_list} to be like \\{show_node_list}.
+ [The two routines, originally separate, will be merged in \TeX82.] @238
+* 18 Jul 1979
+G405. Extend capacity from 32 fonts to 64 fonts if desired. @134
+Q406. Add new \\{extra_space} parameter to all text fonts (requested
+ by Frances Yao). @558
+F407. Make each \\{node_noad} print properly in \\{show_noad_list}. @183
+Q408. Make |\jpar| allow any break if it is 1000000 or more.
+ [In \TeX82, a |\tolerance| of 10000 or more allows any break.] @851
+* 23 Jul 1979
+E409. Introduce new primitives |\hfil|, |\vfil|, |\hfilneg|, |\vfilneg|. @1058
+G410. Add |\ifmmode|. @501
+G411. Add |\firstmark|. @1012,1016
+C412. Allow break at leaders (horizontal mode only). @149
+* 25 Jul 1979
+I413\>213. Revise \\{error} so that online insertions work properly
+ after end-of-file errors. @336
+B414\>411. Change `{\bf if} $\\{first_mark}\ne0$' to
+ `{\bf if} $\\{first_mark}\ge0$' [because $-1$
+ is used to indicate `not yet given a value']. @1012
+* 28 Jul 1979
+C415\>370. Stop |\xdef| from expanding control sequences after |\def|'s.
+ [This decision will be rescinded later, after several more years of
+ experience with macro expansion will suggest better ways to cure
+ the problem.] @366
+I416. Change symbolic printout for control symbols. [System dependent.] @49
+L417\>308. Avoid linefeeds in the transcript file. [System dependent.]
+C418\>370. Expand |\topmark|, etc., in |\xdef|. @366
+* 4 Aug 1979
+B419\>413. Fix an error introduced recently: |\par| was suddenly
+ omitted at end of page. [System dependent.]
+* 11 Aug 1979
+P420. Change error messages that use |SAIL| characters not in
+ standard ASCII. @360
+* 28 Aug 1979
+D421\>411. Move the command `$\\{first_mark}\gets-1$' from \\{vpackage}
+ to \\{fire_up}. @1012
+S422\>403. Correct a serious |\gdef| bug:
+ Control sequences don't obey a last-in-first-out
+ discipline, so \TeX\ loses things from the hash table when deleting a
+ control sequence. @259
+# To fix this, I either need to restrict \TeX\ (so that
+ |\gdef| can be used inside a group only for control sequences already
+ defined on the outer level) or need to change the hash table algorithm.
+ Although all applications of \TeX\ known to me will agree to the
+ former restriction, I've chosen the latter alternative, because it gives me
+ a chance to improve the language: Control sequences
+ of arbitrary length will now be recognized.
+D423. Make sure that \\{unsave} cannot call \\{eq_destroy}
+ with a value from the upper part of \\{eqtb}. @282
+# I noticed this long-standing bug while fixing \#422. It had very
+ low probability of causing damage (e.g., it required a certain field
+ of a floating-point number to have a certain value), but it would have
+ been devastating on the day it first showed up!
+* 29 Aug 1979
+F424. Call \\{eq_destroy} when a control sequence is |\gdef|'ed
+ after being |\def|'ed. @283
+F425\>418. Treat the first token consistently
+ when |\topmark| and its cousins are expanded in \\{scan_toks}. @477
+# Now I've checked things pretty carefully and I think \TeX\ is ``fully debugged.''
+* 25 Jan 1980
+I426\>338. Display runaway alignment preambles. @306
+G427. Introduce active characters (one-stroke control sequences).
+ [I don't yet go
+ all the way: The meanings of `|x|' and `|\x|' have to be identical.] @344
+* 7 Feb 1980
+F428\>314. Fix a glaring omission: Op space |\>| was
+ never implemented in math mode! @716
+* 25 Feb 1980
+G429. Add a new dimension `|ex|' (for units of xheight). @455
+* 3 Mar 1980
+C430\>427. Allow the control sequence |\:| to be redefined
+ [it was the `select font' operator];
+ this allows the character |:| to be active. [Obsolete.]
+* 23 Mar 1980
+# An extend-\TeX-for-the-eighties party:
+G431. Add a new |\copy| feature. @204
+G432. Add a new |\unbox| feature. @1110
+G433. Add a new |\open| feature [later |\openout|]. @1351
+G434. Add a new |\send| feature [later |\write|]. @1352
+G435. Add a new |\leqno| feature, requested by MDS. @1204
+G436. Add a new |\ifdimen| feature [later |\ifdim|]. @513
+C437. Make |\|\<space> in vertical mode begin a paragraph. @1090
+G438. Add a new |\font| feature [replacing the silly previous convention that
+ a font must be defined when it is first selected]. @1256
+G439. Add new |\parval| and |\codeval| features [later
+ |\the|\thinspace\<whatever>\thinspace]. @413
+C440\>427. Don't let active characters gobble the following space. @344
+G441\>208. Add a new parameter to govern amount of token list dumped. [Obsolete.] @295
+G442. Add a new |\linebreak| feature [later replaced by |\break|]. @831
+* 25 Mar 1980
+# (Still working on the above, also thought of more.)
+G443. Add a new |\mskip| feature. @716
+G444. Add a new |\newname| feature (soon changed to |\let|). @1221
+G445\>430. Allow any control sequence to be redefined. @275
+I446. Send the output to the user's current file area, even when
+ input comes from elsewhere. @532
+* 27 Mar 1980
+Q447. Compute the xheight for accents in math mode from
+ family~1, not family~3. [Obsolete.]
+* 28 Mar 1980
+Q448. Increase minimum clearance between subscript and superscript. @759
+* 29 Mar 1980
+Q449\>222. When a display follows a display, the second should have the
+ `shortskip' glue. @1146
+* 4 Apr 1980
+A450\>445. Look at current token meanings when trying to recognize
+ |\tabskip| in alignment preambles. @782
+* 23 Apr 1980
+I451. Estimate the length of printed output, for the new
+ priority feature on our XGP device driver. [System dependent.]
+C452\>434. Break long |\send| lines
+ into pieces so that the file can be read in again. [System dependent.]
+* 19 May 1980
+Q453\>182. Don't make |\left| and |\right| delimiters too large; they
+ need to be only 90\% of the enclosed size. [This eventually
+ became |\delimiterfactor|.] @762
+* 21 May 1980
+G454. Add a new |\pagebreak| feature [later |\vadjust{\break}|]. @655
+* 13 Jun 1980
+# Today I'm beginning to overhaul the line-breaking routine,
+ and I'll also install miscellaneous goodies.
+G455. Allow a radical sign to be in different font positions. @737
+E456. Clear empty tokenlists off input stacks to allow deeper recursions
+ (suggested by Jim Boyce's macros for chess positions). @325
+G457. Make |\spaceskip| and |\parfillskip| changeable. @1228
+G458. Add a new parameter |\rfudge| (per request of Zippel) [later |\mag|]. @288
+G459. Add a new parameter |\loose| [later |\looseness|];
+ now parameters are allowed to take negative values. @875
+E460. Remove the variable \\{just_par}. [Obsolete; it was the \\{real} equivalent
+ of an \\{integer}].
+* 14 Jun 1980
+Q461. Install new line-breaking routines, including |\parshape|.
+ (These major changes are introduced as Michael Plass
+ and I write our article.) @813
+G462. Add a new parameter |\exhyf| [later |\exhyphenpenalty|]. @869
+* 16 Jun 1980
+S463\>444. Change conventions in \\{eqtb} so that glue is distinguishable
+ from other equivalents. @275
+A464\>444. Don't expand |\b| in |\xdef{\d\b{...}}| after |\let\d=\def|.
+ [Obsolete.]
+D465\>444. Avoid creating dead storage when doing \\{unsave} in certain regions. @275
+* 17 Jun 1980
+C466. Allow negative dimensions in rules. @138
+* 19 Jun 1980
+B467\>463. Make the new test for glue at the outer level of \\{show_eqtb}. @252
+* 27 Jun 1980
+Q468\>453. Don't let |\left| and |\right| become too small for big matrices.
+ [This eventually became |\delimitershortfall|.] @762
+* 3 Aug 1980
+Q469. Don't move extra-wide, numbered equations flush left unless
+ they begin with glue. @1202
+* 15 Sep 1980
+M470\>461. Say `$\ge\\{fz}$' instead of `$>\\{fz}$' in the pre-hyphenation
+ routine; I'd forgotten my definition of \\{fz} [a variable used to test
+ for a sequence of lowercase letters in the same font]. @897
+R471\>395. Check the range of the index in |\chcode| before saving the old value.
+ @1232
+* 18 Sep 1980
+D472\>457. Don't forget to increase the reference count to |\parfillskip|,
+ or it will mysteriously vanish. @816
+* 19 Sep 1980
+C473\>412. Make leaders break like glue in both horizontal and vertical modes.
+ @149
+Q474\>364. Make |\mathsurround| break properly at left and right end of lines.
+ @879
+* 13 Oct 1980
+I475\>461. Remove spurious overfull boxes generated when the looseness criterion
+ fails. [Obsolete.]
+A476\>461. Redesign the iteration for looseness; breakpoints were not chosen
+ optimally. @875
+E477\>461. Avoid storing a lot of breakpoints when they are dominated by
+ others. @836
+B478\>366. Don't say `\\{cur_node}' when you mean `\\{mem}[\\{cur_node}]'. @1105
+Q479\>461. Prefer the oldest break to the youngest break
+ when two break nodes have the same total demerits. @836
+L480\>461. Don't make badness too big for floating-point calculations,
+ when forced to make an overfull box. [Obsolete.]
+* 10 Dec 1980
+R481. Make it impossible to get unmatched `|}|' in a delimited macro
+ argument. @392
+G482. Add new |\topsep| and |\botsep| features. [These are \TeX78's
+ way to put space at the edge of inserts,
+ replaced in \TeX82 by the |\skip| register corresponding
+ to an |\insert| class.] @1009
+* 6 Jan 1981
+P483. Install new routines for reading the font metrics, using Ramshaw's
+ |TFM| files instead of\/ |TFX| files. @539
+I484. Abort after reporting 100 errors, if not pausing on errors. @82
+G485. Add new |\spacefactor| and |\specskip| and |\skip| primitives.
+ [At this time we write `|\specskip3=10pt|' and `|\skip3|' for what
+ will become `|\skip3=10pt|' and `|\hskip\skip3|' in \TeX82.] @1060
+G486\>366. |\unskip| is now allowed in internal vertical mode. @1105
+* 26 Jan 1981
+B487\>482. Don't say `\\{mem}[$q$]' when you mean `$q$'. (See \#143 and \#478.) @1009
+* 27 Feb 1981
+I488\>417. Put some linefeeds back into the transcript file, in order
+ to prevent overprinting in listings. [System dependent.]
+G489. Add a new |\dpenalty| feature [later |\postdisplaypenalty|]. @1205
+G490. Add the dimension |cc| for European users. @458
+C491. Make \\{scan_keyword} match uppercase letters as alternatives
+ to lowercase ones (suggested by Barbara Beeton's experiments
+ with |\uppercase|). @407
+I492. Add nonstop mode so that overnight batch processing is possible. @73
+* 2 Mar 1981
+S493\>422. Fix a still more serious |\gdef| bug: The generality
+ of\/ |\gdef| almost makes it a crime to
+ forget {\sl any\/} control sequence names, ever! (The previous bug was
+ only the tip of an iceberg.) @259
+I494. Issue warning message at the end of a file page if nesting level isn't zero.
+ [System dependent.]
+* 5 Mar 1981
+I495. Keep track of maximum memory usage, for statistical
+ reporting. [Obsolete.] @125
+Q496\>350. Prune away glue and penalties at top of page after marks, sends,
+ inserts. @1000
+G497. Allow |\mark| in horizontal mode. [Later it will
+ be |\vadjust{\mark...}|.] @655
+C498. Allow optional space before a required left brace, e.g., |\if AA {...}|.
+ [See \#251.] @403
+I499. Issue an incomplete |\if| error, to help catch a bad |\if|. @336
+* 17 Mar 1981
+I500\>494. Omit the warning message at end of a file page unless
+ the nesting level has changed on that page. [System dependent.]
+Q501\>310. Fix the spacing when there is a very tall subscript with
+ a superscript. @759
+* 20 Mar 1981
+S502\>371. Make space-eating after |\else| fully consistent between the
+ true and false cases. [Obsolete.]
+* 24 Mar 1981
+B503\>496. Change \\{glue_spec_size} to \\{ins_spec_size}
+ in \\{vpackage} [where insertions are done]. [Obsolete.]
+* 5 Apr 1981
+B504\>501. Fix a typo (`|+|' instead of `|-|') in the new subscript code;
+ this shifted certain subscripts down instead of up. @759
+* 18 Apr 1981
+G505. Make leaders with rules of specified size act like variable rules.
+ @626,635
+* 29 Apr 1981
+A506\>461. Don't consider $\\{badness}>\\{threshold}$ at a line |\break|
+ except in an emergency. @854
+* 13 Jul 1981
+C507\>402. Allow other characters as numbers. @442
+R508\>294. Avoid dead storage if a \\{no_new_control_sequence} error occurs.
+ [Obsolete.] @259
+G509. Add a new |\ifx| feature. @507
+G510. Add new features |\xleaders| and |\cleaders|. @626,635
+* 14 Jul 1981
+S511\>507. Amend the new code for constants;
+ the `|.|' in `|.5|' is thought to mean \O{056}! @442
+L512\>507. And fix an egregious blunder in that code: New commands at the
+ end of a procedure are ignored when earlier statements
+ exit via {\bf return}. @442
+* 4 Aug 1981
+P513. Accept alphabetic codes for all online error recovery options,
+ instead of insisting on control codes like line feed or form feed.
+ [The original error-recovery codes were suggested by the
+ conventions of the |SAIL| compiler.] @84
+G514. Add a new |\thebox| feature [later |\lastbox|]. @1079
+* 7 Aug 1981
+G515. Add |fil|, |fill|, and |filll| as units for glue stretching
+ or shrinking. @454
+I516. Suppress the overfull box error when shrinkage amount is negative. @664
+* 9 Aug 1981
+Q517. Let unset boxes inherit the size of their parent in alignments. @810
+* 12 Apr 1982
+F518. Make |INITEX| dump out the \\{font_dsize} array needed by
+ the new |DVI| output module. @1322
+* 1 May 1982
+S519\>151. Fix \\{clean_box} so that \\{mlist_to_hlist} cannot make
+ $\\{link}(q)=0$ and $\\{type}(q)=\\{glue_node}$. @720
+# [That was the historic final change to \TeX78. All subsequent entries
+ in this log refer to \TeX82.]
+* 15 Jul 1982
+# Finished draft of test program and began debugging about 1430
+ [2:30\thinspace pm]. Taking my time.
+BX1. Change \\{eqtb}[\\{cur_font}] to \\{eqtb}[\\{cur_font_loc}]. @232
+# Not logging changes to the exposition.
+# Compile time is about 2 minutes CPU, times 5 for time-sharing;
+ add another half minute for linking and loading.
+# Hash table and \\{get_next} seem to be working, with no changes needed!
+# Time out 1630--1815 for Jill's birthday party.
+LX2. Insert {\bf begin} \dots~{\bf end} around \\{dump_int} macro. @1305
+IX3. Print two blank spaces before date in \\{open_log_file}. @536
+AX4. Update $x$ and \\{var_used} outside the {\bf for} loop. @1311
+BX5. Change {\bf if} $=$ to {\bf if} $\ne$ as loop exit condition. @1315,1316
+# The |TRIP| test should preload more fonts.
+LX6. Insert {\bf begin} \dots~{\bf end} around statistics output. @1334
+FX7. Must \\{get_x_token} when scanning a number. @445
+IX8. Interactive \\{debug_help} needs to print a newline. @1338
+FX9. Include \\{ignore_spaces} and \\{math_accent} in \\{print_cmd_char} cases. @266
+IX10. Don't call \\{confusion} when \\{print_cmd_char} sees unknown code. @298
+# Compiler bug causes stack overflow. Retiring for the night at 2145.
+* 16 Jul 1982
+# Starting at 0700; DRF has fixed the compiler.
+RX11. Allow arbitrary integer parameters in diagnostic print routines. @237,699
+TX12. Say \\{cur_tok}, not \\{cur_val}, when you mean \\{cur_tok}. @440
+FX13. Make |\pause| effective also on first line of a file. @538
+FX14. Show context after online deletion. @88
+IX15. Bypass reference count when \\{debug_help} shows a token list. @1339
+BX16. Change `{\bf case} $p$' to `{\bf case} \\{type}($p$)'. @1000
+FX17. Fix timing of \\{print_ln} when scrolling help messages. @90
+PX18. Make \\{other_char} the default category for ASCII control codes too. @232
+AX19. Use special scanning method for font number in \\{new_font}. @1257
+# Eating lunch, 1020--1035, while the machine slowly recompiles everything.
+FX20. Don't forget to increase $k$ in the {\bf while} loop. @355
+DX21. Adjust \\{limit} properly after line changed when pausing. @363
+FX22. Remember to return a value in \\{new_spec} and \\{new_penalty}. @151,158
+# Now stepping through \\{line_break} in simple case.
+AX23. Don't prune unwanted nodes if $\\{cur_p}=\\{null}$. @877
+FX24. Print a closing parenthesis when displaying glue nodes. @189
+SX25. Use \\{last}, not \\{limit}, in \\{term_input}; else error prompt causes
+ trouble when $\\{state}=\\{token_list}$. @71,87
+DX26. Set $\\{first}\gets\\{limit}+1$ after \\{init_terminal}. @331
+DX27. Make sure \\{set_trick_count} is always performed. @317
+* 17 Jul 1982
+IX28. Add new diagnostic feature |\tracingcommands|. @299,1031
+# Debugging of system-dependent code not shown in this log.
+# Tangling \TeX\ now takes 1.75 minutes; about 75K bytes, 108K tokens.
+# Redundant semicolon sends Pascal compiler into infinite loop!
+FX29. Initialize $\\{passive}\gets\\{null}$. @864
+AX30. Fix pseudoprinting when the line is empty. @318
+EX31. Merge adjacent free areas of dynamic memory before dumping. @131
+FX32. Print the word |mode| in \\{print_mode}. @211
+IX33. Improve message and help in case of weird error. @415
+GX34. Allow optional space after |\def| and similar constructions. @473
+MX35. Declare \\{alpha} to be integer in \\{read_font_info}. @560
+AX36. Fix timing of \\{back_input} in \\{scan_dimen}. @448
+IX37. Back up after missing number error. @446
+IX38. Show the `|at|' size that is considered improper. @1259
+IX39. Streamline the dialog in \\{debug_help}. @1338
+MX40. Take output of \\{the_toks} from the advertised place. @467,1297
+TX41. Say \\{trie_fix}($q$), not \\{trie_fix}($p$). @959
+SX42. Decrease low limit of \\{error_count} to $-1$. @76
+* 18 Jul 1982
+FX43. Clear initial reference count of macro definitions. @473
+AX44. Fix timing of \\{back_input} in \\{scan_glue}. @461
+BX45. Use \\{cur_val_level}, not \\{cur_val}, when checking levels. @461
+BX46. Multiply |fil| units by $2^{16}$ for correct scaling. @454
+BX47. Don't confuse \\{glue_base} with \\{skip_base}. @1237
+AX48. Fix \\{print_scaled} so that 0.01 doesn't come out |0.1|. @103
+IX49\>X28. Show mode changes when tracing commands. @299
+LX50. Don't say {\bf if} $(n=0)\lor(\,\ldots\;\hbox{\bf div}\;n)$ in Pascal. @105
+BX51. Don't confuse \\{box_flag} with \\{box_code}. @1075
+SX52. Reset \\{offset} on \\{print_ln} even in \\{no_print} mode. @57
+SX53. Fix restarting of interrupts after \\{big_switch}. @1031
+LX54. Don't loop {\bf for} $k\gets a$ {\bf to} $b-1$ when $b=0$ if $k$ is
+ declared nonnegative.
+IX55. Put `|=|' sign into the \\{format_ident}. @1328
+SX56. Allow $r$ to be any integer in \\{get_node}. @125
+IX57. Don't put the output of \\{print_file_name} in quotes. @518
+IX58. Say `|dumped|' after dumping. @1311
+EX59. Eliminate unnecessary initialization code. @1332
+LX60. Get the file reading started right when beginning to undump. @1308
+IX61. Give forlorn message if format file can't be loaded. @1303
+FX62. Assign value to \\{cur_val} after glue arithmetic. @1239,1240
+* 19 Jul 1982
+LX63. Don't say {\bf if} $p>\\{max}\lor\\{free}[p]$ in Pascal. @169
+AX64. Fix memory-undump logic; loops are out of phase with input. @1312
+BX65. Undump \\{hyph_word}[$j$], not \\{hyph_word}[$k$]. @1325
+# At last |trip.fmt| loads without bombing out.
+PX66. Remove assignment of array to array, not allowed by IBM Pascal
+ (Susan Plass). @167
+PX67. Simplify an expression that's too big for IBM Pascal (Susan Plass). @1009
+AX68. Go to \\{contribute}, not \\{done}, after insertions. @1000
+IX69. Decrease \\{depth_threshold} if there's not enough string space. @198
+IX70. Show rules as `{\tt\char`\|}' in short displays. @175
+IX71. Don't show null glue in short displays. @175
+SX72. Set $\\{job_name}\gets0$ as part of output initialization. @528
+SX73. Don't complain of infinite shrinkage on |0pt minus 0fil|. @825,976,1009
+IX74. Use different prompt at beginning when accepting a file name. @37
+FX75. Reset \\{last_glue} on nonglue nodes. @996
+BX76. Remember to call \\{error} after printing |OK|. @1293
+LX77. Insert {\bf begin} \dots{ \bf end} around program text of section. @1025
+FX78. Define the |\shipout| primitive. @1071,1073
+AX79. Introduce \\{write_loc} analogous to \\{par_loc}. @1344,1371
+FX80. Dump and undump \\{par_loc} and \\{write_loc}. @1313,1314
+FX81. Allow \\{the} in \\{scan_the} [later \\{scan_something_internal}]. @413
+EX82. Interchange command codes $\\{the}:=:\\{number}$ [later \\{convert}]. @210
+BX83. Don't confuse \\{breadth} with \\{depth}. @236
+IX84. Add string printing feature to \\{debug_help}. @1339
+FX85. Set $\\{state}\gets\\{mid_line}$ in \\{begin_file_reading}. @328
+# Time out 2105--2200 to pick up Jenny from driving lesson.
+LX86. Keep $c\le127$ when deleting 99 tokens. @88
+SX87. Don't check for \\{str_room} error when $\\{selector}=\\{new_string}$. @1328
+* 21 Jul 1982
+IX88. Gather more statistics: String usage, font info, hyphen exceptions, stacks. @1334
+DX89\>X79. Initialize \\{write_loc} from \\{cur_val}, not from \\{cs_ptr}. @1344
+FX90. Remember to pack file name for |\open|. @1374
+SX91\>X34. Defuse |\outer| test before scanning optional space after `|}|'. @473
+SX92. Don't allow \\{prepare_mag} to cause errors after \\{jump_out}. @84
+# The first page of |DVI| output is {\sl perfect\/}! Pause to play piano.
+BX93. Don't confuse \\{dimen_base} with \\{scaled_base}. @1237
+DX94. Initialize $\\{link}(\\{page_head})\gets\\{null}$ when beginning a page. @991
+SX95. Correct \\{cur_height} more often, since \\{max_depth} might be negative. @972,973
+AX96. Calculate page dimensions properly after vertical kerns. @973
+SX97. Install new \\{page_contents} logic to handle interaction between
+ insertions and |\topskip|. @987,1008
+SX98. Allow \\{top_skip} glue to be a valid breakpoint. @1001
+FX99. Don't forget to count \\{dyn_used} in inner loop [erroneous analysis
+ retracted later]. @1034
+FX100. Set $p\gets q$ after migration step. @655
+FX101. Clear \\{prev_graf} to zero at start of paragraph. @1091
+LX102. Put parens into negated \\{leader_flag} macro. @1078
+# Time out 1720--1920 for John's birthday dinner.
+DX103. Scale \\{best_height} when adjusting \\{page_goal}. @1010
+AX104. Simplify logic of split insertions; three states become two. @981,1019,1020
+MX105. Don't omit first character when showing a macro argument. @400
+RX106. Prevent clobberage if macros have too many parameters. @390
+* 22 Jul 1982
+IX107. Tell how many |DVI| bytes were output. @642
+IX108\>X88. Adjust for singular or plural statistics. @1320,1334
+SX109\>X98. Consider \\{page_head} a glue node, to inhibit unwanted break. @988
+DX110. Introduce \\{new_skip_param} to keep reference counts updated. @679,969,1001
+DX111. Record the correct size of new insertion after it's split. @1010
+DX112. Use \\{free_node}, not \\{flush_node_list}, when recycling insertion
+ nodes. @1022
+SX113. Make online insertions work after \\{get_next} is interrupted. @87,324,343
+BX114. Print newline on interruption stop. @98
+SX115\>X34. Put space before \\{end_write_token}. @1371
+BX116. Don't confuse \\{glue_order} with \\{stretch_order}. @838
+TX117. Set $\\{max_dimen}\gets\hbox{\it'7777777777\/}$, not {\it'777777777}. @421
+MX118. Make |\global\textfont| legal. @1211
+BX119. Fetch |\the\textfont| with \\{equiv}, not \\{fam_font}. @415
+BX120. Call $\\{new_ligature}(f,l,\ldots)$ not $(f,c,\ldots)$. @1035
+RX121. Make \\{show_box} work on random garbage. @174,177
+DX122. Count reference to \\{zero_glue} from \\{cond_math_glue}. @1171
+* 23 Jul 1982
+RX123. Allow $\\{avail}=\\{null}$ when undumping. @1312
+BX124\>X110. Set $\\{width}(\\{glue_ptr}(p))$, not $\\{width}(p)$. @679
+AX125. Put \\{begin_file_reading} inside the \\{start_input} loop. @537
+BX126\>X116. Don't confuse \\{glue_stretch} with \\{stretch_order} [the previous
+ fix went only half\-way]. @838
+BX127. Negate $x$ when calculating badness of shrinkage. @667,678
+LX128\>X121. Remove dangling {\bf else} that parses wrong. @174
+IX129\>X28. Print newline before |{|, not after |}|, when tracing commands. @299
+IX130. Remove colon from overfull box messages. @663,675
+DX131\>X97. Compute \\{page_goal} properly when the first box arrives after
+ inserts. @1001
+BX132. Don't confuse \\{page_size} [now \\{page_goal}] with \\{best_size}. @1017
+AX133. Put heldover insertions at front of contribution list. @1023
+SX134\>X88. Output stats before closing |DVI| file, since the latter
+ decreases \\{font_ptr}. @1333
+TX135. Don't call a |\vbox| an |\hbox|. @674
+TX136. Fix misplaced {\bf end} caused by editing error. @675
+* 24 Jul 1982
+DX137. Don't \\{eq_destroy} any paragraph shape when \\{par_shape} is null. @275
+IX138. Omit blank before |\message| at the beginning of a line. @1280
+AX139\>X104. Don't try to split an insertion when \\{best_node} isn't the
+ split one. @1021
+LX140. Correct another case of `{\bf if} $x\land y$' misunderstood by Pascal.
+ @1021
+IX141. Provide diagnostic info about insertions via |\showlists|. @986
+IX142. Add `|inside a group|' to clarify a warning message. @1335
+IX143. Report \\{prev_depth} on a separate line in |\showlists|. @219
+SX144. Back up input to avoid unexpected clobberage of \\{cur_tok}. @1090
+# Wow what a bug: \\{new_graf} calls \\{build_page}, which invokes the
+ output routine, after which `\/{\bf goto} \\{reswitch}' is a {\sl disaster}.
+DX145. Add insertion glue to \\{page_so_far} instead of subtracting it from
+ the goal. @1009
+IX146. Put extra blank line before overfull box warning. @660
+CX147. Define break at kern consistently between horizontal and vertical lists. @973,1000
+DX148. Renumber so that math nodes are nondiscardable. @147
+TX149. Correct the \\{char_kern} macro: |#|, not $f$. @557
+FX150. Decrease $l$ after reconstituting discretionary break. @916
+EX151. Simplify the hyphen routine, knowing that $\\{link}(s)=\\{null}$. @918
+FX152. Initialize $r\gets q$ in discretionary destruction routine. @883
+* 25 Jul 1982
+AX153. Don't add interline penalty after last line of paragraph. @890
+IX154. Adjust spacing in diagnostic messages. @245
+DX155. Avoid simultaneous use of \\{temp_head} by \\{prune_page_top} and the
+ page builder. @1017
+FX156. Clear the \\{post_break} field of simple discretionaries. @916
+AX157. Split \\{offset} into independent variables \\{term_offset},
+ \\{file_offset}. @54,57,58
+# Am freezing current program as version $-0.25$; a week of TUG lectures
+ begins tomorrow.
+* 5 Aug 1982
+IX158. The `|.err|' file should be `|.log|' instead. @534
+GX159. Allow |\special| strings to contain more than 256 bytes. @585,1368
+MX160\>X99. Undo ``correction'' to a non-bug. @1034
+IX161. Suggest |\&| in help message for unexpected |&|. @1128
+GX162. Make `|E|' a standard option for exiting. @84
+GX163. Restore the use of dead cycles \`a la \TeX78. @1024,1054
+# The previous six changes were suggested during discussions with \TeX82 class.
+SX164. Fix global variable conflict between \\{open_log_file} and \\{new_font}. @1257
+CX165. Allow optional `|=|' when assign to font parameter. @1253
+FX166. Set \\{cur_val} after increasing the number of font parameters. @580
+DX167. Set \\{hash_brace} when matching `|{|'. @476
+IX168\>X88. Clarify meaning of statistics printed. @1334
+GX169. Change |DVI| format to include design size. @602,1260
+DX170. Introduce \\{def_ref} for \\{runaway} messages. @306
+MX171. Restore \\{cur_cs} before calling \\{scan_toks}. @1226
+AX172\>X157. Update \\{print_nl} to dual offset conventions. @62
+AX173\>X163. Move endgame logic inside \\{main_control}, because the output
+ routine becomes active. @1054
+* 6 Aug 1982
+CX174. Allow |INITEX| to load format files. @1337
+EX175. Conserve input stack space by deleting finished token lists. @325
+IX176\>X74. Print the opening `|**|' in the transcript file. @534
+# Now ready to try breaking new ground in |TRIP|.
+SX177. Preserve \\{align_state} from tokens deleted online. @88
+DX178. Set $t$ in all branches of \\{scan_toks}. @473
+SX179. Change `$\\{cur_cmd}\le\\{right_brace}$' to `$\\{cur_tok}\le
+ \\{right_brace_limit}$'; otherwise |\relax| gets through. @477
+CX180. Allow optional |=| when setting |\spacefactor|; disallow zero. @1243
+BX181. Change \\{vpackage}(\\{head}) to \\{vpackage}(\\{link}(\\{head})). @796
+# Shades of 1978!
+LX182. Insert missing {\bf begin} \dots{ \bf end}. @798
+* 7 Aug 1982
+LX183. Keep \\{trie_max} declared in non-|INITEX| [later rescinded]. @950
+DX184. Watch out for empty token list when copying. @466
+DX185. Free unused reference count slot when defining |\everypar|. @1226
+GX186\>X163. Introduce |\maxdeadcycles|. @1012
+# I believe the \\{line_break} routine has passed its test perfectly.
+SX187. Don't put discretionary after |-| inside a discretionary. @1039
+CX188. Change `|\minus|' to `|\minusthe|' [this feature retracted later]. @413
+BX189. Change \\{cur_p} to $r$ (three places). @875
+AX190. Increase range of \\{hc} to \\{halfword}; otherwise end-of-word mark
+ might match a vacant entry in the trie. @892
+FX191. Initialize $b$ in \\{shift_case} routine. @1288
+EX192. Don't back up if a space follows a decimal fraction. @452
+BX193. Don't confuse \\{glue_base} with \\{dimen_base}. @1145
+LX194. Guard against anomalous floating-point values in glue display. @186
+* 8 Aug 1982
+SX195. Avoid infinite loop when |\outer| leads to runaways. @339
+# I worked on that problem about two hours before fixing it.
+IX196. Move final \\{debug_help} to \\{succumb}, except in batch mode. @93
+RX197. Insert kern after spanned box, to defeat access to floating point. @808
+* 9 Aug 1982
+CX198. Include |\leftskip| and |\rightskip| in displayed equations [rescinded
+ later]. @1199
+IX199. Trace line-break computations if $\\{tracing_stats}>2$. @846,856
+DX200. Keep \\{prev_p} up to date when passing a string. @867
+# Now stepping through math stuff; a lot is working.
+DX201. Set $\\{link}(p)\gets z$ when making a fraction. @747
+CX202. Don't reset space factor when beginning |\valign|. @775
+IX203. Don't show glue setting if $\\{glue_sign}=\\{normal}$. @186
+DX204. Clear \\{glue_stretch} and \\{glue_shrink} when creating an unset box. @801
+BX205. Do \\{vpack} in hmode and vice versa when aligning. @804
+AX206. Remove fallacious call to \\{confusion} after alignment in display. @1206
+AX207. Don't test $\\{mode}=\\{vmode}$ in display, test $\\{nest_ptr}=1$. @1145
+FX208. Show an \\{inner_noad} as well as the other types. @690,696
+DX209. Renumber \\{mu_glue} and \\{cond_math_glue} so that the glue display logic
+ works. @149
+BX210. Don't confuse \\{cur_size} with \\{cur_style}. @703
+FX211. Advance $p\gets q$ during second pass over mlist. @761
+IX212. Add helpful hint about |\tracingonline=1|. @1293
+TX213. Delete spurious statement left from sloppy editing. @710
+DX214. Change the subtype when \\{mskip} becomes \\{hskip}. @732
+FX215. Don't forget to use the remainder when computing math glue. @716
+IX216\>X199. Improve paragraph diagnostics using \\{short_display}. @857
+IX217\>X199. Introduce \\{artificial_badness} for better diagnostics. @854,856
+* 11 Aug 1982
+AX218. Introduce \\{char_box} subroutine so that \\{var_delimiter} adds italic
+ correction. @709
+SX219. Save font and char in local variables of \\{make_math_accent}, since
+ it can be recursive. @738
+FX220. Call \\{error} after decrying an invalid character. @346
+* 12 Aug 1982
+IX221\>X199. Install new format for showing break nodes. @846
+# I have been testing \\{line_break} and I think it's working fine.
+BX222. Change $q$ to $p$, in order to catch empty alignments. @812,1206
+SX223. Disallow third part of discretionary in math mode. @1120
+DX224. Don't change \\{tail} if discretionary third part is empty. @1120
+IX225. Say |nonscript|, not |non_script|. @189
+SX226. Inhibit math if |\scriptfont3| is improper. @1195
+IX227\>X199. Introduce {\tt\@}|firstpass| and {\tt\@}|secondpass| comments. @863
+TX228. Change $p$ to $r$ when you mean $r$. @1204
+IX229\>X108. Say |page|, not |pages|, if there's only |1|. @642
+IX230. Insert space before |[]| on truncated |\showlists|. @182
+* 28 Aug 1982
+# Back from vacation after having looked at hardcopy listing of |TRIP| test.
+GX231. Allow |dm| as a unit [later |.5dm1| will be |.5\dimen1|]. @455
+IX232\>X108. Singularize |prevgraf 1 lines|. @219
+IX233. Omit trailing zero count registers when showing completed page numbers. @638
+LX234. Avoid clobbering $a$ by introducing a new local variable $t$. @986
+DX235\>X216. Fix diagnostic printing of discretionaries. @858
+IX236. Don't show unset stretch/shrink that's zero. @185
+CX237\>X198. Make |\halign| in displays consistent with other displays. (Namely,
+ ignore |\leftskip| and |\rightskip| in nonaligned displays;
+ respect the paragraph shape in aligned displays.) @800,1199
+IX238. Parenthesize `|If you're confused ...|'. @403
+IX239. Say `|\fraction|', not `|\xabovex|'. @697
+FX240. Remember to {\bf return} when you should. @1153
+PX241. Use absolute value to make sure {\bf div} is unambiguous. @737
+BX242\>X218. Don't confuse depth with height. @709
+BX243. Use $\\{delta}-\\{height}$, not $\\{height}-\\{delta}$. @736
+BX244. Increase \\{shift_down} to increase the clearance. @745
+IX245. Don't back up after improper use of |\the|. @428
+IX246. Don't give |0pt| as the default result when looking for \\{tok_val}. @428
+FX247. Initialize \\{second_indent} in the easy case. @848
+FX248. Package the equation number. @1204
+QX249. Don't resort to $v\gets\\{max_dimen}$ when glue doesn't stretch or
+ shrink. @1148
+IX250. Insert newline before showing current |\botmark|.
+FX251. Call \\{error} after giving error message. @784
+AX252. Change implementation of |\number|; it should \\{scan_int}, not
+ something internal. @471
+PX253. Introduce symbolic constants like \\{format_area_length}. @524
+PX254. Change \\{quit} to \\{jump_out}, since some compilers treat \\{quit}
+ as a reserved word. @81
+LX255. Add more parentheses to get proper parsing. @1260
+IX256. Say |please| in order to be friendly (or at least polite). @360
+BX257. Don't confuse \\{cur_vcmd} with \\{cur_chr}. @508
+IX258. Use |&| instead of |!| to specify a preloaded format. @1337
+TX259\>X177. Correct |s3| to |s4|. @88
+GX260. Introduce new primitive |\mathchardef|, to save space and time. @1224
+CX261. Use the |[]| convention for noads as well as nodes. @692
+TX262. Correct spelling in call to \\{primitive}: |\xatopx| should be |\xoverx|
+ [later renamed, thank goodness]. @1178
+* 30 Aug 1982
+RX263. Don't fetch \\{link}(\\{null}) in malformed list. @175
+SX264. Initialize \\{align_state} at a better time so that \\{align_peek}
+ doesn't see |&| or |\span|. @785,791
+SX265. Outlaw preamble interfering with $\\{align_state}=0$. @789
+CX266. Add level of grouping to alignment to tabskip locality. @774
+FX267. Check \\{align_state} when scanning $\langle u_j\rangle$. @783
+AX268. Move `\\{unsave}; \\{new_save_level}' from \\{main_control} into
+ \\{fin_col}. @791,1131
+SX269\>X180. Remember \\{cur_chr} when you're looking for optional `|=|'. @1243
+TX270. Change $q$ to $r$ [in code now obsolete]. @804
+IX271. Disable interrupts during \\{back_error} so that help messages aren't
+ clobbered. @327
+SX272. Introduce \\{slow_print} for printing control sequences. @60
+IX273. Initialize \\{del_code}(|"."|$)\gets0$ for error recovery. @240
+SX274. Call \\{end_file_reading} before calling \\{check_outer_validity}. @362
+IX275. Don't delete an extra `|}|' when |\par| will help find a runaway. @395
+* 31 Aug 1982
+BX276. Don't confuse \\{thin_muskip} with \\{thin_muskip_code}. @413
+FX277\>X266. Recover from error if new \\{align_group} ends abnormally. @1132
+IX278. Recover from error if |\par| occurs when $\\{align_state}<0$. @1094
+CX279. Make |\hskip\the\thinmuskip| and |\mskip\the\baselineskip| erroneous. @413
+GX280. Add |\muskip| and |\setmuskip| analogs to |skip| and |\setskip|. @413,1228
+EX281. Don't output |pop| right after |push|. @601
+# The |TRIP| test looks right; now to test for wasted memory.
+# When memory should be empty I find $\\{dyn_used}=18$, $\\{var_used}=267$.
+* 1 Sep 1982
+# Made special |MEMTEX| program, designed to track all memory allocation.
+DX282. Delete reference to \\{last_glue} when a page is packaged. @1017
+IX283. Include \\{save_stack} in the \\{search_mem} debugging routine. @285
+CX284. Disallow |\vfill| in restricted horizontal mode. @1095
+# Most of the memory locations I thought were wasted were actually in good use.
+# Total 192 hours (approx) debugging time so far since July 15.
+* 2 Sep 1982
+# Now looking at all zero counts in profile and extending |TRIP|.
+EX285. Simplify the creation of |vtop| boxes. @1087
+CX286. Set $\\{space_factor}\gets1000$ after |\hbox|. @1076
+PX287. Introduce preamble into |DVI| format. @617
+SX288. Give special \\{chr_code} to |\relax|. @265
+IX289. Don't show `|(null)|' when token list is null, just show nothing. @295
+EX290. Delete the procedure \\{write_name_string}, which is never used.
+CX291. Rename |\xabovex| to |\abovewithdelims|; do the same for
+ |\xatopx| and |\xoverx|. @1178
+QX292. Improve \\{clean_box} so that it recognizes cleanliness better. @720
+IX293. Report a |Missing delimiter| more meaningfully. @1161
+RX294. Give \\{endv_token} a \\{chr} code of 128 so that it will end a file name. @289
+RX295. Test present of math fonts {\sl after\/} parsing an mlist, not
+ before. @1138,1195
+IX296. Omit `|recent contributions|' and/or `|current page|' when they are
+ empty. @218,986
+IX297. Display what \TeX\ has deleted after improper discretionary list
+ has arisen. @1121
+IX298. Show what math character was undefined. @723
+IX299. Improve the |Incompatible magnification| error; break it into two lines. @288
+DX300. Put new cases into \\{flush_node_list}, to recycle mlist noads. @698
+* 6 Sep 1982
+FX301\>X300. Insert a necessary `{\bf goto} \\{done}' in that new material. @698
+# It took two hours to diagnose that {\bf goto} problem.
+TX302\>X295. Change `|2|' to `|3|' in help message for extension fonts. @1195
+IX303. Add a special note if material is being held over for the next output. @986
+RX304. Divide before multiplying in \\{make_left_right}, to avoid overflow. @762
+IX305. Introduce the \\{box_error} routine. @992
+FX306. Clear \\{arith_error} after overflow has been reported. @460
+BX307\>X249. Don't confuse \\{stretch} with \\{glue_stretch}. @1148
+DX308. Set $\\{glue_sign}\gets\\{normal}$ when packaging with glue ratio
+ zero. @558,664
+AX309. Test for overflow before attaching the sign. @448
+# That all worked! Now trying |min_quarterword| negative.
+LX310. Take absolute value before applying {\bf mod} in \\{new_trie_op}. @944
+LX311. Say $\\{qi}(c)$, not $c$, when testing |TFM| flags [now obsolete]. @573
+LX312. Initialize \\{token_ref_count}(\\{def_ref}) to \\{null}, not zero. @473
+LX313. Change the type of \\{vsplit} parameter $n$ from \\{quarterword} to
+ \\{eight_bits}. @977
+LX314. Initialize \\{null-delimiter} different form \\{null_character}. @685
+LX315. Insert \\{qi} twice in \\{scan_delimiter}. @1160
+FX316. Insert \\{qi} in \\{scan_math}. @1151
+FX317. Insert \\{qo} in \\{fetch}. @722,723
+FX318. Insert \\{qi} in \\{set_math_char}. @1155
+FX319. Insert \\{qi} in \\{math_ac}. @1165
+FX320. Insert \\{qo} in \\{mlist_to_hlist}. @755
+IX321. Use brackets around 8-bit characters in \\{print_ASCII}. @68
+IX322. Include \\{hyph_list} in the \\{search_mem} debugging routine. @933
+# Now compiling non-|INITEX| to try an industrial-strength version.
+LX323. Add {\bf return} to \\{final_cleanup}, because some Pascal compilers
+ insist that each label be used. @1335
+DX324. Compute \\{par_token} when undumping. @1314
+* 11 Sep 1982
+IX325. Emit newline before file name, if near end of line. @537
+CX326. Define |\ifx| for arbitrary tokens. @507
+* 12 Sep 1982
+IX327. Don't ask users to type |x| twice before exiting. @84
+GX328. Install new features |\openin|, |\read|, |\ifeof|, |\closein|; rename
+ existing |\open|, |\send|, |\close| to be |\openout|, |\write|,
+ |\closeout|. @209,313,1275
+GX329. Install new feature |\expandafter|. @368
+PX330. Change the default file area from `|<TeX>|' to `|TeXinputs:|'. @574
+* 13 Sep 1982
+GX331. Install new feature |\string|. @472
+AX332. Remove spurious space printed by \\{sprint_cs}. @263
+# All tests passed now! But when I played with the system I found another bug
+ (undetected by |TRIP|):
+FX333. Set $r\gets s$ after matching macro parameter tokens. @397
+* 16 Sep 1982
+IX334\>X199. Introduce serial numbers in line-break records, improving
+ readability and independence. @846
+IX335. Don't abort when \\{file_name_size} is exceeded. @519
+* 17 Sep 1982
+IX336. Remove unwanted period from font capacity message. @567
+* 18 Sep 1982
+GX337\>X329. Make |\expandafter| more powerful by moving it from semantics
+ to syntax [i.e., from stomach to mouth]. @368
+* 19 Sep 1982
+IX338. Improve error recovery for `|Missing number|'. @415
+* 22 Sep 1982
+QX339. Suppress italic correction between letters in math mode except in math
+ fonts. @752
+* 24 Sep 1982
+PX340. Define $\\{null}=\\{mem_bot}$, not \\{min_halfword}, because there's
+ a reference to \\{link}(\\{null}) in \\{try_break}. @115
+DX341. Initialize $\\{str_start}[0]\gets0$. @47
+IX342. Avoid blank space at beginning of line. @638
+DX343. Set type of new box in math mode to \\{ord_noad}, not \\{inner_noad}. @1076
+* 28 Sep 1982
+# Here are the first changes made to the preliminary listing of \TeX82 that was
+ published by the \TeX\ project earlier this month.
+F520. Insert the missing cases \\{letter} and \\{other_char} after
+ \\{x_token} looks ahead. @1038
+C521. Change `|\pause|' to `|\pausing|'. @236
+D522. Reset \\{overfull_rule} when determining tabskip glue. @804
+A523. Fix the logic for scanning |\ifcase| [in obsolete syntax---everything
+ is still done with braces since `|\fi|' doesn't exist yet]. @509
+* 30 Sep 1982
+I524. Change |"0.0"| to |"?.?"| (suggested by DRF). @186
+* 2 Oct 1982
+Q525. Use conditional thin spacing next to `Inner' noads. @764
+Q526. Make thick spaces conditional. @766
+* 4 Oct 1982
+P527. Increase \\{trie_size} from 7000 to 8000, because of Frank Liang's improved
+ (but longer) hyphenation patterns. @11
+* 6 Oct 1982
+F528\>X330. Change the string lengths to match the new \\{\TeX_format_default}. @520
+# Version 0 of \TeX\ is being released today!
+* 8 Oct 1982
+B529. Fix a blunder: I decreased $h$ mod a quarterword when it should have
+ been decreased mod \\{trie_op_hash_size} (HWT). @944
+* 9 Oct 1982
+P530\>X258. Fix a typo (`|!|' not `|&|') in the |WEB| documentation. @524
+F531. Remember to call \\{initialize} if a different format was preloaded
+ (Max D{\'\i}az). @1337
+# Version 0.1 incorporates the above changes.
+* 12 Oct 1982
+G532. Add the `|\immediate|' feature, by popular request. @1375
+# Version 0.2 incorporates this (somewhat extensive) change.
+* 13 Oct 1982
+P533. Introduce new |WEB| macros so that \\{glue_ratio} is more easily changed. @109
+# I began writing {\sl The \TeX book\/} today: edited the old preface and
+ searched in the library for quotations.
+* 14 Oct 1982
+B534. Change the type of \\{hd} to \\{eight_bits}; it's
+ not a \\{quarterword} (HWT). @649
+S535\>X281. Revise the optimization of\/ |DVI| commands: It's not always safe to eliminate
+ \\{pop} when the preceding byte is \\{push}, since |DVI| commands
+ have variable length! (Embarrassing oversight caught by DRF.) @601
+* 15 Oct 1982
+C536. Test `$\\{prev_depth}>\\{ignore_depth}$', not `$\ne$'. @679
+# Version 0.3 incorporates the above changes.
+* 16 Oct 1982
+P537. Omit definition of \\{align_size}; it's never used (Bill Scherlis). @11
+I538. Inhibit error messages when packaging box 255. @1017
+* 21 Oct 1982
+A539\>X145. Subtract $\\{width}(q)$ from \\{page_goal}, don't add it to
+ $\\{page_so_far}[1]$. @1009
+# Version 0.4 incorporates the above changes.
+* 22 Oct 1982
+P540. Increase the amount of lower (variable-size) memory from 12000 to 13000,
+ since the \TeX\ program listing now needs about 11500. [At this time
+ there still is a fixed boundary between upper and lower memory.] @12
+G541. Add a new parameter |\boxmaxdepth|. @1086
+# Version 0.5 incorporates the above changes.
+* 26 Oct 1982
+B542. Fix an off-by-one error caught by Gabi Kuper and HWT.
+ (I forgot `${}+1$'). @1317
+B543. Fix the spacing of displayed control sequences: \\{print_cs} should base its
+ decision on $\\{cat_code}(p-\\{single_base})$, not $\\{cat_code}(p)$. @262
+# The |TRIP| test detected this bug, but I didn't notice.
+* 27 Oct 1982
+S544. Set \\{math_type} before saying $\\{fetch}(\\{nucleus}(q))$, since
+ fetching can have a side effect. @752
+* 28 Oct 1982
+G545. Install a major change: Fonts now have identifiers instead of code letters.
+ Eliminate the `|\:|' primitive, and give corresponding new
+ features to `|\the|'. @209
+# Actually I began making these changes on October 26, but I needed two days
+ to debug them and to put Humpty Dumpty together again.
+# At this time I'm also drafting macros for typesetting {\sl The \TeX book}.
+# The above changes have been incorporated into Version 0.6.
+* 30 Oct 1982
+# After years of searching, I've finally found a definitive definition of
+ the printer's point; and (unfortunately) my previous conjecture was
+ wrong. The truth is that $\rm83\,pc=35\,cm$, exactly;
+ so I am changing \TeX\ to conform.
+C546. Revise unit definitions for the `real' printer's point. @458,617
+# Version 0.7 incorporates the above.
+* 1 Nov 1982
+# Oops! Retract error \#546, and retract \TeX\ Version 0.7;
+ the source of my information about points was flaky after all.
+ My original suppositions were correct,
+ as confirmed by NBS Circular 570.
+* 4 Nov 1982
+C547. Revise the definition of |dd|, conforming to the definitive value
+ shown me by Chuck Bigelow. @458
+R548\>545. Introduce ``frozen'' copies of
+ font identifiers, to be returned by |\the\font|, so that font
+ manipulation is more robust. @1257
+* 5 Nov 1982
+D549. Reset \\{looseness} and paragraph shape when beginning a |\vbox|. @1083
+* 6 Nov 1982
+D550. De-update \\{align_state} when braces are in constants. @442
+I551\>X294. Improve error recovery for bad alignments. @1127
+# Today I wrapped up Chapters 4 and 5.
+* 8 Nov 1982
+G552. Give more power to |\let|: the right-hand side needn't be a control
+ sequence. @1221
+I553. Amend \\{show_context} to say
+ `$(\\{base_ptr}=\\{input_ptr})\lor{}$'; otherwise undefined control
+ sequences can be invisible in unusual cases (John Hobby). @312
+A554. Compute demerits more suitably by adding a penalty squared, instead of
+ adding penalties before squaring. @859
+# Previously a slightly loose hyphenated line followed by a decent line
+ was considered worse than a decent hyphenated line followed by
+ a quite loose line.
+* 10 Nov 1982
+E555. Save a bit of buffer space by declaring \\{pool_file} only in
+ |INITEX|. @50
+* 11 Nov 1982
+I556. Introduce a new context indicator to clarify \TeX's scanning state:
+ A special type
+ called \\{backed_up} is distinguished from other kinds of \\{inserted}
+ lists; it is called `recently read' or `to be read again', while
+ others are called `inserted'. @314
+I557. Append a comment, `|treated as zero|', to the missing-number message. @446
+I558. Ignore the settings of\/ |\hfuzz| or |\vfuzz| if\/ |\hbadness| or |\vbadness|
+ is less than 100. @666,677
+* 13 Nov 1982
+# Major surgery on the program is planned for today, because of new ideas
+ suggested by correspondence with MDS and other macro writers.
+G559. Introduce a new |\tokens| register; this will be useful and easy to add,
+ since \TeX\ already can handle |\everypar| and |\output|. @1227
+C560\>X34. Change \\{get_x_token} to \\{get_token} when scanning an optional
+ space; then a construction like
+ |\def\foo{...}\foo| won't complain that |\foo| is undefined. @443
+# This change was retracted when it was being debugged, because it could
+ cause \\{endv} to abort the job. Then it was re-established again
+ when I found that \\{endv} needed to be more robust anyway.
+ [But it was eventually rescinded again.]
+G561. Make |\span| mean `expand' in a preamble. @782
+E562. Use three separate {\bf if\/} tests instead of
+ `$\land$' in the inner loop of \\{get_next}, to gain efficiency. @342
+R563. Introduce \\{get_r_token} so that assignments have uniform error
+ messages and so that frozen equivalents cannot be changed. @1215
+# I gave a few variables more mnemonic names as I made these changes.
+C564. Move conditional statements from the semantics (`stomach') part of
+ \TeX\ to the syntax (`mouth') part, by introducing `|\fi|'.
+ Also introduce |\csname| and |\endcsname|. @372,489\hbox{--}500
+# This makes macros much more predictable and logical, but it is by far
+ the most drastic change ever made to \TeX. The program began to come
+ back to life only after three days of solid hacking.
+# Several other things were cleaned up as part of this change
+ because it is now more natural to handle them differently.
+ For example, a null control sequence has now become more logical.
+# The result of all this is called Version 0.8.
+* 18 Nov 1982
+# Today I resumed writing Chapter 8. Tomorrow I'm $\,2^{14}$ days old!
+* 21 Nov 1982
+F565. Declare $c$ as a local variable for hyphenation (DRF). @912
+E566. Omit the ``first pass'' and try hyphenations immediately,
+ if\/ |\pretolerance| is negative (suggested by DRF). @863
+R567. Don't ship out incredibly huge pages; they might foul up
+ |DVI| files. @641
+* 2 Dec 1982
+G568. Add new features |\everymath| and |\everydisplay|. @1139,1145
+G569. Add a new feature |\futurelet|. @1221
+# The changes above have been incorporated into Version 0.9 of TeX.
+* 7 Dec 1982
+G570. Add a new |\endinput| primitive (suggested by FY). @362,378
+* 8 Dec 1982
+I571. Try \\{off_save}, if\/ |\par| occurs in restricted horizontal mode.
+ (This avoids embarrassment if \TeX\ says `type a command or
+ say |\end|', then when you type |\end| it says you can't!)
+ [However, I soon retracted this change.] @1094
+* 21 Dec 1982
+A572. Redefine |\relax| so that its \\{chr} field exceeds 127. (This facilitates
+ the test for end in \\{scan_file_name}.) @265
+F573\>566. Call \\{begin_diagnostic} when omitting
+ the first pass of line breaking. @863
+A574. Fix the logic of glue scanning: In |\hskip-1pt plus2pt| the minus should
+ apply only to the |1pt|. @461
+* 23 Dec 1982
+I575. Renumber the decimal codes in paragraph statistics for loose and tight
+ lines; they were ordered backwards. @817
+C576. Treat a paragraph that ends with leaders like a paragraph that
+ ends with glue. @816
+C577. Allow commas as alternates to radix points, for Europeans. @438
+C578. Change |\hangindent| to a normal dimension parameter. [It had been
+ a combination of\/ |\hangindent| and |\hangafter|, with special
+ syntax.] @247
+G579. Make |\prevgraf| accessible to users. @422,1244
+G580. Split |\clubpenalty| off from |\widowpenalty|. @890
+# I'm typing Chapter 14 while making these changes.
+* 24 Dec 1982
+S581. Use \\{back_input} instead of {\bf goto} \\{reswitch} when
+ inserting |\par|, because |\par| may have changed. @1095
+* 25 Dec 1982
+# It's 10pm after a very Merry Christmas!
+I582\>X328. Don't prompt for a new file name if\/ |\openin| doesn't find a file. @1275
+G583. Add a new |\jobname| primitive. @472
+I584. Give the user a way to delete the dollar sign, when \TeX\ decides
+ to insert one. @1047
+C585. Allow optional equals after |\parshape|, and implement |\the\parshape|.
+ @423,1248
+* 26 Dec 1982
+I586. Add an \\{if_line_field} to the condition stack entries,
+ so that more informative error messages can be given. @489
+D587\>549. Introduce a \\{normal_paragraph} procedure, since initialization is needed
+ also within |\insert|, |\vadjust|, |\valign|, |\output|. @1070
+* 27 Dec 1982
+G588. Give users access to |\pagetotal| and |\pagegoal|. (Analogous to
+ \#679 and \#585, but simpler.) @1245
+I589\>X199. Introduce |\tracingpages|, allowing users to see
+ page-optimization calculations. Also split |\tracingparagraphs| off from
+ |\tracingstats|. @987,1005,1011
+# The changes above have been incorporated into Version 0.91 of \TeX.
+* 31 Dec 1982
+P590. Break the \\{build_page} procedure into two parts, by extracting
+ the section now called \\{fire_up}. [This is necessary because
+ some Pascal compilers, notably for IBM mainframes, cannot
+ deal with large procedures.] @1012
+S591\>564. Make |\ifodd1\else| legal by introducing \\{if_code}. @489
+Q592. Improve alignments when columns don't occur: Don't append null boxes
+ for columns missing before |\cr|, and zero out the tabskip
+ glue after nonpresent columns. @802
+I593. Make the error message about overfull alignment more intelligible.
+ @801,804
+# The changes above have been incorporated into Version 0.92 of \TeX82,
+ which was the last version of 1982,
+ completed at 11:59pm on December 31.
+* 3 Jan 1983
+# Today I'm beginning to write Chapter 15, and
+ planning the |\output| routine of |plain.tex|.
+C594\>X186. Change the logic of \\{its_all_over}; use \\{max_dead_cycles}
+ here too, instead of the fixed constant~100. @1054
+F595\>X34. Don't forget to \\{pop_nest} when an insert is empty. Also disallow
+ optional space after |\insert|$\,n\,$|{...}|. @1100
+* 4 Jan 1983
+C596\>541. Use the |\boxmaxdepth| that's declared inside a |\vbox| when packaging it.
+ @1086
+C597. Rename |\groupbegin| and |\groupend| as |\begingroup| and
+ |\endgroup|. @265
+G598\>594. Make |\deadcycles| accessible to users. @1246
+Q599. Base the split insertions on natural height plus depth, not
+ on \\{delta}. @1010
+# The changes above have been incorporated into Version 0.93.
+* 6 Jan 1983
+D600. Add \\{push_math} to handle a case where I forgot
+ to clear \\{incompleat_noad}. (This long-standing bug was
+ unearthed today by Phyllis Winkler.) @1136
+G601\>588. Add |\pageshrink|, etc., too. @1245
+G602. Introduce new parameters |\floatingpenalty|, |\insertpenalties|. Also
+ adopt a new internal representation of insertion nodes,
+ so that |\floatingpenalty|, |\splittopskip| and |\splitmaxdepth|
+ can be stored with each insertion. @140,1008
+* 7 Jan 1983
+Q603. Improve the rules for entering \\{new_line}, in particular
+ when the end-of-line character is active. @343
+* 9 Jan 1983
+Q604. Distinguish between implicit and explicit kerns. @155,896
+C605. Change the name |\ignorespace| to |\ignorespaces|. @265
+C606\>560. Don't omit a blank space after |\def|, |\message|, |\mark|, etc.;
+ the previous hodge-podge of rules is impossible to learn. @473
+# The above changes appear in Version 0.94.
+* 12 Jan 1983
+# Beginning to write the chapters on math today.
+G607. Add a new feature: active characters in math mode. @1151
+* 15 Jan 1983
+A608. Fix a surprise bug: `|$1-$|' treated the |-| as binary. @729
+D609. Initialize \\{space_factor} inside discretionaries. @1117
+* 16 Jan 1983
+F610. Fix an incredibly embarrassing bug: I forgot to update \\{spotless} in the
+ \\{error} routine!
+# While fixing this, I decided to change
+ \\{spotless} to a more general \\{history} variable, as suggested
+ by IBMers who want a return code. @76,82,1335
+I611. Replace two calls of \\{confusion} by attempts at error recovery, in places
+ where `|This can't happen|' could actually happen. @1027,1372
+* 18 Jan 1983
+R612. Introduce the \\{normalize_selector} routine
+ to protect against startup anomalies
+ when the transcript file isn't open. Also make \\{open_log_file}
+ terminate in some cases. @92,535
+R613\>591. Insert |\relax|, not a blank space, to cure infinite
+ loop like |\ifeof\fi| (LL). @510
+G614. Change the old |\limitswitch| to |\limits|, |\nolimits|, and
+ |\displaylimits|. Incidentally, this fixes a bug in the former
+ positioning of integral signs. @682,749
+C615. Give a |\char| in math mode its inherited |\mathcode|. @1151
+Q616\>525. Make underline, overline, radical, vcenter, accent noads and |{...}|
+ all revert to type Ord instead of type Inner. Introduce a new primitive
+ |\mathinner|. (This fixes the spacing, which
+ got worse in some ways after change \#525.) @761
+# I'm working on Appendix G today.
+* 19 Jan 1983
+G617. Introduce a |\mathchoice| primitive. @1174
+C618. Move |\input| from the stomach to the mouth. @378
+C619\>X260. Introduce |\chardef|, analogous to |\mathchardef|. @1038,1224
+G620. Change |\unbox| to |\unhbox| and |\unvbox|; also add |\unhcopy|. @1110
+C621. Consider |\spacefactor|, |\pagetotal|, etc., as part of
+ \\{prefixed_command}, even though they are always global. @1211
+* 20 Jan 1983
+C622. Switch modes when |\hrule| occurs in horizontal mode or |\vrule|
+ in vertical. @1090,1094
+G623. Add a new |\globaldefs| feature. @1211
+* 21 Jan 1983
+E624. Optimize the code, in places where it's important (based on
+ frequency counts of \TeX\ usage
+ accumulated during the past week): Introduce \\{fast_get_avail}
+ and \\{fast_store_new_token}; reduce procedure-call overhead
+ in \\{begin_token_list}, \\{end_token_list}, \\{back_input},
+ \\{flush_node_list}; change some tests from `{\bf if} $a\land b$' to
+ `{\bf if}~$a$ {\bf then if}~$b$'. @122,371
+* 22 Jan 1983
+E625. Save space in math lists: Don't insert penalties within restricted
+ horizontal mode; simplify trivial boxes. @721,1196
+S626. Fix a surprising oversight in the \\{rebox} routine:
+ Ensure that $b$ isn't a vbox. @715
+C627\>545. Make |\nullfont| a primitive, so that \\{cur_font} always
+ has a value. (This is a dramatic improvement over \TeX78, where a
+ missing font was a fatal error called `|Whoa|'!) @552
+* 24 Jan 1983
+I628\>586. List all incomplete |\if|'s when the job ends. @1335
+* 29 Jan 1983
+C629\>552. Change initialization of \\{align_state} so that |\halign\bgroup|
+ works. @777
+* 30 Jan 1983
+D630\>625. Be sure to test `\\{is_char_node}($q$)' when checking for a trivial
+ box. @721
+# By extraordinary coincidence, this bug was caught when somebody
+ used font number~11 ($=\\{kern_node}$) in the second character
+ of a list of length~2!
+I631\>X168. Improve format for stats at end of run, as suggested by DRF. @1334
+# The changes above have been incorporated into Version 0.95.
+C632. Don't ignore the space after a control symbol (except `|\ |'). @354
+P633. Remove all trailing spaces at the right of input lines,
+ so that there's perfect compatibility with IBM systems that
+ extend short lines with spaces. @31
+* 3 Feb 1983
+I634. Assume that a \\{math_accent} was intended, after giving an error
+ message in the case $\\{mmode}+\\{accent}$. @1165
+G635. Add new primitives |\iftrue| and |\iffalse|. @488
+* 6 Feb 1983
+A636\>X304. Improve the accuracy of fixed-point arithmetic when
+ calculating sizes for |\left| and |\right|. (I had started by
+ dividing \\{delimiter_factor}, not \\{delta1}, by 500.) @762
+* 12 Feb 1983
+C637. Change the name |\delimiterlimit| to |\delimitershortfall|. @248
+C638. Make |\abovewithdelims..| equivalent to |\above|; change the
+ order of operands so that delimiters precede the dimension. @1182
+C639\>607. Remove the kludgy math codes introduced earlier;
+ make |\fam| a normal integer parameter and allow |\mathcode|
+ to equal $2^{15}$. @1233
+R640. Don't let |\spacefactor| become more than $2^{15}$. @1233,1243
+# I finished drafting Chapter 17 today.
+* 14 Feb 1983
+I641\>639. Replace octal output (\\{print_octal}) by hexadecimal (\\{print_hex})
+ so that math codes are clearer. @67
+F642\>619. Don't forget \\{char_given} in the \\{math_accent} routine. @1124
+* 17 Feb 1983
+C643\>622. Switch modes when |\halign| occurs in horizontal mode, or |\valign|
+ in vertical mode. @1090,1094
+* 18 Feb 1983
+I644. Add a new feature |\tracingrestores|. This requires a new procedure called
+ \\{show_eqtb}, whose code can be
+ interspersed with the \\{eqtb} definitions. @252
+* 25 Feb 1983
+I645\>622. Suggest using |\leaders| when the user tries a horizontal rule
+ in restricted horizontal mode. @1095
+* 27 Feb 1983
+I646. Specify the range of source lines, when giving warning messages
+ for underfull or overfull boxes in alignments. @662,675
+# Why did it take me all day to type the middle part of Chapter 18?
+* 4 Mar 1983
+G647. Introduce a new feature |\xcr| (suggested by LL). [Changed later to
+ `|\crcr|'.] @785
+I648\>631. Subtract out \TeX's own string requirements from the stats. @1334
+* 6 Mar 1983
+G649. Add new features |\everyhbox| and |\everyvbox|. @1083,1167
+* 9 Mar 1983
+R650\>X295. Avoid accessing \\{math_quad} when the symbol fonts aren't known
+ to be present. @1199
+P651\>533. Introduce \\{float} and \\{unfloat} macros to aid portability (HWT). @109
+C652. Introduce new names |\abovedisplayskip| and |\belowdisplayskip| for
+ the old |\dispskip|; also |\abovedisplayshortskip| and
+ |\belowdisplayshortskip| for the old |\dispaskip| and |\dispbskip|. @226
+* 10 Mar 1983
+C653. Unbundle |\romannumeral| from |\number| (suggested by FY). @468
+* 12 Mar 1983
+C654. Ignore leading spaces in \\{scan_keyword}. @407
+* 14 Mar 1983
+E655\>631. Use \\{write} and \\{write_ln} directly when printing stats. @1334
+* 16 Mar 1983
+Q656\>602. Refine the page-break cost function (introducing `\\{deplorable}',
+ which is not quite `\\{awful_bad}'), after suggestion by LL. @974,1005
+# The changes above have been incorporated into Version 0.96.
+* 18 Mar 1983
+G657. Add a new feature |\everyjob| suggested by FY. @1030
+* 19 Mar 1983
+I658. Don't treat left braces specially when showing macros. @294
+C659. Ignore blanks that would otherwise become undelimited arguments. @393
+* 21 Mar 1983
+F660\>X280. Make |\lastskip| handle \\{mu_glue} as well as ordinary glue. @424
+C661\>561. Expand only one level in a preamble |\span|. @782
+* 22 Mar 1983
+C662. Let a single |#| suffice in |\tokens|, |\message|, etc. (The previous rule,
+ in which |##| was always required as in macros, was a loser especially
+ in |\write| where you had to say |####|!) @477
+C663\>X328. Require the keyword `|to|' in |\read|. (This will avoid the common error
+ of an incomplete constant when no space appears
+ before the |\cs|.) Also allow terminal
+ I/O as a default when a stream number is out of range. @482,1225,1370
+* 26 Mar 1983
+C664. Replace |\ifeven|\<countnumber> by |\ifodd|\<number>, for better
+ consistency of language. @504
+S665\>564. Introduce the \\{change_if_limit}, to overcome a big surprise
+ bug relating to |\if\if aabc\fi|. @497
+# Such examples show that \\{cur_if} might not be current,
+ in my original implementation.
+* 28 Mar 1983
+G666\>X326. Tolerate non-characters as arguments to |\if| and |\ifcat|. @506
+C667. Change `|absent|' to `|void|', a better word. @487
+C668. Clear the \\{shift_amount} in |\lastbox|, since I don't
+ want to figure out what it means in all cases. @1081
+* 29 Mar 1983
+I669. Wake up the terminal before giving an error message. (This
+ means a special \\{print_err} procedure is introduced.)
+ (Suggested by DRF.) @34,73
+* 1 Apr 1983
+# Today I finished Chapter 21 (boxes) and began to draft Chapter 22 (alignments).
+G670. Allow periodic preambles in alignments. @793
+C671. Make |\leaders| line up according to the smallest
+ enclosing box. @627,636
+Q672. Allow hyphenation after whatsits (e.g., after items for an index). @896
+* 2 Apr 1983
+Q673. Call \\{build_page} when |\par| occurs in vertical mode. @1094
+C674. Clear \\{aux} in \\{init_row}, for tidyness. @786
+* 4 Apr 1983
+C675. Let digits switch families in math mode. @232
+* 7 Apr 1983
+Q676\>602. Refine the test for not splitting an insertion. @1008
+* 8 Apr 1983
+C677\>647. Rename |\xcr| as |\crcr|, at LL's request. @780
+* 9 Apr 1983
+# Took a day off and had a chance to help print a sample page on
+ a 150-year-old letterpress in Murphys, California.
+* 11 Apr 1983
+I678. Recover more sensibly after a runaway preamble. @339
+* 12 Apr 1983
+C679\>X328. Make |\read| span several input lines, if necessary to get
+ balanced braces. @482
+* 14 Apr 1983
+S680. Fix a subtle bug found by JS: \S882 can make $q$ a \\{char_node},
+ so we need to test `{\bf if} $\lnot\\{is_char_node}(q)$'.
+ [Actually I discovered much later that the real bug was
+ to omit `{\bf else}' at this point.] @881
+* 15 Apr 1983
+C681. Make |\uppercase| and |\lowercase| apply to all characters, regardless
+ of category. @1289
+# 7:30am. After working all night, I completed a draft of the manual thru
+ Chapter 22, for distribution to volunteer readers.
+# 5pm. The changes above have been incorporated into Version 0.97.
+* 17 Apr 1983
+R682. Change `\\{small_number}' to `$0\,.\,.\,65$' in the
+ hyphenation routine (DRF). @901
+I683. Flush patterns in the input when the user tries |\patterns|
+ outside of\/ |INITEX| (suggested by DRF). @1252
+# Tomorrow I fly to England, where I'll lecture and write a paper about
+ `Literate Programming' [{\sl Comp.~J. \bf27} (1984), 97--111].
+* 14 May 1983
+I684\>663. Improve the behavior of\/ |\read| from terminal
+ (suggested by Todd Allen at Yale).
+ [I'd forgotten to implement the extended stream numbers in \#663.
+ Also, the prompt is now omitted if $n<0$.] @484
+* 18 May 1983
+I685. Restrict |\write| $n$ to the transcript file only, if $n<0$. @1350
+C686\>X188. Unify the syntax for registers and internal quantities. (Remove
+ primitives called `|\insthe|' and `|\minusthe|'; rename
+ \\{scan_the} to \\{scan_something_internal}, and change its
+ interface accordingly; clean up command codes generally.) @209,413
+G687. Introduce new parameters |\hoffset|, |\voffset|. @617
+* 24 May 1983
+G688. Introduce a new parameter |\everycr| (suggested by MDS). @774,799
+# Many macro writers and preliminary-manual readers have been requesting
+ new features; I'll try to keep the language as concise and consistent
+ as possible.
+* 25 May 1983
+G689. Introduce |\countdef|, |\dimendef|, etc.\ (suggested by DRF long ago,
+ easy now in view of \#686). @1224
+G690. Introduce |\advance|, |\multiply|, |\divide| (suggested by FY). @1240
+G691. Introduce |\hyphenchar|; this requires a new command
+ \\{assign_font_int}, plus minor changes to about 15 modules. @915
+G692. Introduce |\skewchar| (easy because of \#691). @741
+G693. Introduce |\noexpand|. (I had difficulty thinking of how to
+ implement this one!) @358,369
+G694. Introduce |\meaning|. @296
+G695\>X231. Remove `|dm|' and `|vu|'; allow the more general `|.5\hsize|'. @455
+C696. Change `|\texinfo| $f$ $n$' to `|\fontdimen| $n$ $f$'. @578
+* 27 May 1983
+G697. Add a new feature |\afterassignment| (suggested by ARK). @1269
+C698\>619. Adjust the timing so that commands like `|\chardef\xx=5\xx|'
+ behave sensibly. @1224
+* 28 May 1983
+C699. Ignore `|\relax|' as if it were a space, in math mode and in a few other
+ places where |\relax| would otherwise be erroneous. @404
+Q700. Improve |\mathaccent| spacing with respect to subscripts
+ and superscripts (suggested by HWT). @742
+* 30 May 1983
+C701\>598. Terminate a job only when $\\{dead_cycles}=0$. @1054
+# The changes above constitute Version 0.98.
+* 3 Jun 1983
+# I finished the draft of Chapter 23 (output routines) today.
+G702. Allow |\mark| and |\insert| and |\vadjust| in restricted
+ horizontal mode, and also in math mode. (This is a comparatively
+ big change, triggered by the fact that |\mark| in a display
+ presently causes \TeX\ to crash with `|This can't happen|'!)
+ The global variable \\{adjust_tail} is introduced. @796,888,1085
+* 6 Jun 1983
+G703\>695. Replace (and generalize) the previous uses of\/ |ht|, |wd|, and |dp|
+ in dimensions by introducing the new control sequences
+ |\ht|, |\wd|, and |\dp|. @1247
+I704. Display sub-parts of noads with the symbols |^| and |_| instead of
+ |(| and~|[|. @696
+C705\>694. Allow |A..F| in hex constants to be \\{other_char} as well as
+ \\{letter}. @445
+* 7 Jun 1983
+E706\>654. Remove an instance of \<Scan optional space>, since it's now
+ redundant. @457
+C707. Legalize |\mkern\thinmuskip| and |\mkern5\thinmuskip|. @456
+C708. Clean up the treatment of optional spaces in numerical specifications. @455
+# A construction like |2.5\space\space\dimen0| was previously
+ valid after `|plus|' or `|minus|' only!
+# I'm obviously working on Chapter~24 today.
+C709\>545. Allow `|\font|' as a \<font identifier> for the current font. @577
+C710\>623. Don't make |\gdef| global when $\\{global_defs}<0$. @1218
+E711. Produce \\{zero_glue} as the outcome of
+ |\advance\spaceskip by-\spaceskip|. @1229
+I712. Make |\show| do something appropriate for every possible token. @1294
+G713\>559. Replace the (single) |\tokens| parameter by an array of
+ 256 token registers. @230
+C714. Allow |\indent| in math mode; also make |\valign| in math mode produce the
+ `|Missing|~|$|' error. @1046,1093
+E715. Remove redundant code: There's no need to check \\{cur_group}
+ or call \\{off_save} when starting alignments or equation numbers
+ in displays. @1130,1142
+* 8 Jun 1983
+C716. Disallow |\openout-1| and |\closeout-1|. @1350
+C717. Disallow |\lastbox| in math mode. @1080
+* 9 Jun 1983
+I718. Call \\{back_error}, not \\{error}, when |\leaders| aren't followed by
+ proper glue. @1078
+D719. Initialize for a possible paragraph, after |\noalign|
+ in a |\valign|. @785
+* 10 Jun 1983
+C720\>708. Expand the optional space after an ASCII constant. @442
+* 12 Jun 1983
+C721. Set $\\{space_factor}\gets1000$ after a rule or a
+ constructed accent. @1056,1123
+* 14 Jun 1983
+D722. Correct a serious blunder: Set $\\{disc_width}\gets0$ before testing
+ if $s$ is null (caught by JS). @869
+# This is a real bug that existed since the beginning!
+ It showed up on page~37 of the
+ Version~0 |TRIP| manual, but I didn't notice the problem.
+C723\>708. Make optional spaces after \<dimen> like those after \<number>. @448
+C724\>568. Insert \\{every_display} before calling \\{build_page}. @1145
+I725\>648. Report \TeX's capacity on overflow errors in a way that's
+ fully consistent with other statistical reports. @42
+* 17 Jun 1983
+C726. Make all |\tracing| decisions on the basis of $\ge$ versus $<$,
+ not $\ne$ versus~$=$. @581
+# Today I finished the draft of Chapter 27 (the last chapter)!
+# The changes above were released as Version 0.99 on June 19, 1983.
+* 20 Jun 1983
+C727. Set |\catcode`\%=14| in |INITEX|. @232
+C728\>587. Call \\{normal_paragraph} when |\par| occurs in vertical mode. @1094
+# Once again I'm retiring about 8am and awaking about 4pm.
+* 21 Jun 1983
+C729\>558. Don't append an overfull rule solely because of\/ |\hbadness|. @666
+R730. Don't allow the glue-ratio of shrinking to be less than~$-1$. @810,811
+* 22 Jun 1983
+B731\>653. Declare the parameter to \\{print_roman_int} to be of type \\{integer},
+ instead of \\{nonnegative_integer} (found by Debby Clark). @69
+C732\>690. Make the keyword `|by|' optional (suggested by LL). @1236
+* 24 Jun 1983
+I733. Say `|preloaded|' when announcing \\{format_ident}. @1328
+* 25 Jun 1983
+R734. Add extra boxes and
+ glue to the output of alignment. [This thwarts possible attempts
+ at trickery by which system-dependent glue set values
+ computed by |\span| could have gotten into \TeX's registers by
+ things like |\valign| and |\vsplit|. It also has the advantage
+ of perfect accuracy in alignment of vertical rules.] @809
+C735. Make leaders affect the height or width of the enclosing boxes. @656,671
+# Today I'm mainly installing a much-improved format for change files
+ in |WEB| programs (suggested by DRF).
+* 28 Jun 1983
+C736. Permit |\unskip| in vertical mode when we know that it does nothing. @1106
+* 1 Jul 1983
+E737\>700. Avoid redundant boxes when things like `|{\bf A}|' occur in math. @1186
+G738. Add a `|scaled|' feature to |\font| input. @1258
+D739\>700. Remember to correct \\{delta} when an accented box changes. @742
+* 2 Jul 1983
+R740.Introduce \\{bypass_eoln}, to remove anomalous behavior on input files
+ of length~1. (Suggested by DRF after the problem was discovered by LL). @31
+* 4 Jul 1983
+G741. Allow codes like |^^b| as well as |^^B|. @352,355
+G742. Introduce new parameters |\escapechar|, |\endlinechar|, |\defaulthyphenchar|,
+ and |\defaultskewchar|, to make \TeX\ less dependent on the
+ character set. (This affects many modules, since a lot of
+ error messages must be broken up so that they use \\{print_esc}.)
+* 7 Jul 1983
+P743. Use a system-dependent function \\{erstat} when opening or closing
+ files (suggested by DRF). @27
+* 11 Jul 1983
+# The computer is back up after more than 50 hours down time
+ (due to air conditioning failure).
+I744. Show total glue in the output of\/ |\tracingpages|. @985
+R745. Guard against insertion into an hbox. @993
+C746. Legalize the assignment \<tokenvar>=\<tokenvar>. @1227
+I747. Introduce a new parameter |\errhelp|. @1283
+F748\>623. Don't forget to check \\{global_defs} when |\tabskip| is changed. @782
+* 12 Jul 1983
+C749. Allow an |\outer| macro to appear after |\string|, |\noexpand|,
+ and |\meaning| (Todd Allen). @369,471
+C750. Make `|\the|' an expandable control sequence (i.e., move it
+ from the stomach to the throat); this cleans up
+ several annoying glitches. @367
+C751\>620. Allow |\unhbox| and |\unhcopy| in math mode if the box is void. @1110
+* 13 Jul 1983
+# I lectured for four hours at the TUG meeting today after very little sleep!
+* 16 Jul 1983
+# The following were suggested by TUG meeting discussions.
+L752. Round the value of \\{default_rule} more properly: It should be 26215. @463
+Q753\>700. Fix |\mathaccent| again; it's still not right!
+ The final height should be the maximum of
+ the height of accented letter without superscript and
+ the height of unaccented letter with superscript. @742
+G754. Add a new feature |\newlinechar|. @59
+G755. Allow boxes and rules in discretionaries (suggested by somebody
+ from Hewlett-Packard). @1121
+I756\>X28. Show all token expansions, not just macros,
+ when |\tracingcommands|. @367
+C757. Allow |\char| in a |\hyphenation| list. @935
+G758. Introduce a new feature |\aftergroup|; it can be implemented with
+ \\{save_stack}. @326
+C759. Run the running dimensions to alignment boundaries (suggested
+ by ARK). @806
+* 17 Jul 1983
+R760. Zero out \\{hyf} values at the edges, so that
+ weird pattern data cannot lead to Pascal range checks. @965
+R761\>X190. Decrease the |hc| codes for hyphenation, so that code~127 cannot
+ possibly be matched. @937,962
+C762\>672. Allow whatsits after hyphenatable words. @899
+C763\>604. Represent an italic correction as an explicit kern. @1113
+* 18 Jul 1983
+C764. Allow lowercase letters in file names. @519
+I765. Change the message `|No output file|' to: `|No pages of output|'. @642
+I766. Confirm that a quiet mode is being entered, when error
+ interaction ends with |Q|, |R|, or |S| (suggested by ARK). @86
+# Version 0.999 was finally installed today; a new program listing has
+ been printed.
+# From now on, I plan to keep all section numbers unchanged.
+# I'm done writing Appendix~H; beginning to revise Chapter~20.
+* 25 Jul 1983
+C767\>663. Allow space after `|to|' in the |\read| command (FY). @1215
+# To bed at 1pm today.
+* 27 Jul 1983
+S768\>665. Stack the current type of\/ |\if|; this precaution is necessary
+ in general (FY). @498
+# To bed at 2pm today.
+* 29 Jul 1983
+E769. Avoid putting a control sequence in the hash table
+ when it occurs after |\ifx|. (Requested by Math Reviews people.) @507
+# Finished a version of {\sl The \TeX book\/} lacking only Appendices
+ D, E, and~I, for distribution to interested readers.
+# To bed at 10:30pm, planning to arise regularly at 6am for a change.
+* 31 Jul 1983
+I770\>766. Call \\{update_terminal} when going quiet (HWT). @86
+* 1 Aug 1983
+C771. Don't put an empty line at the end of an |\input| file! (This simplifies
+ the rules and the program, and also gets around a bug that occurred
+ at the end of files with $\\{end_line_char}<0$.) @362
+# The changes above went into Version 0.9999, which was widely distributed.
+* 16 Aug 1983
+B772\>665. Rectify a ridiculous gaffe: I initialized $q$ every time the loop
+ of \\{change_if_limit} was performed! (Found by FY.) @497
+I773\>648. Distinguish `|string|' from `|strings|' when reporting statistics. @1334
+A774. Introduce \\{lx}, to correct a bug in |\xleader| computations
+ (found by FY). @627
+* 20 Aug 1983
+F775. Don't forget to apply |\/| to ligatures. @1113
+# Today I began to read all previous issues of {\sl TUGboat}, in preparation for
+ Appendix~D.
+* 27 Aug 1983
+I776. Add debugging hack number~16, to help catch subtle data structure bugs. @1339
+E777. Remove redundant setting and resetting of \\{name_in_progress}. @531
+S778\>618. Suppress |\input| during a font size spec; otherwise
+ \\{cur_name} is clobbered (found by MDS). @1258
+G779. Introduce new conditionals |\ifhbox| and |\ifvbox|. @505
+* 29 Aug 1983
+D780\>750. Test for an empty list, if emptiness will mess up the data structure.
+ (Found by Todd Allen.) @478
+E781\>624. Use \\{fast_store_new_token} in another place for efficiency. @466
+I782. Say `|has only|' instead of `|has|'. @579
+# These changes yield Version 0.99999, used only at Stanford.
+* 30 Aug 1983
+C783. Make funny blank spaces showable. @298
+* 31 Aug 1983
+C784\>754. Make |\newlinechar| affect \\{print_char}, not just \\{print}. @58
+* 4 Sep 1983
+G785. Add new features |\lastkern|, |\lastpenalty|, |\unkern|, |\unpenalty|.
+ @424,996,1105
+# OK, Appendix D is finished!!
+# The above changes have been installed in Version 0.999999.
+* 17 Sep 1983
+P786\>548. Don't bother making duplicate font identifiers; that
+ was overkill, not really needed. @1258
+# Will this be the historic last change to \TeX?
+* 18 Sep 1983
+I787. Correct a minor inconsistency, `|display|' not `|displayed|'. @211
+* 20 Sep 1983
+C788\>604. Treat the kerns inserted for accents as explicit kerns. @1125
+* 26 Sep 1983
+I789. Change `|log|' to `|transcript|' in several messages. @535,1335
+# The index was finished today; I mailed the entire {\sl \TeX book\/} to
+ Massachusetts for final proofreading before publication.
+* 1 Oct 1983
+D790. Prevent uninitialized trie positions in case of overflow
+ (found by Bernd Schulze). @944
+* 7 Oct 1983
+# Henceforth our weekly `\TeX\ lunch' meetings will be called `\MF\ lunch'.
+# DRF begins to produce {\sl The \TeX book\/} on our APS phototypesetter.
+* 14 Oct 1983
+P791\>633. Ignore spaces at the ends of lines also in |TEX.POOL| (found
+ by DRF). @52
+D792\>610. Initialize the \\{history} variable at \\{start_here} (DRF). @1332
+* 18 Oct 1983
+I793. Extend \\{runaway} to catch runaway text (suggested by FY). @306
+D794. Reset \\{cur_cs} after \\{back_input}, not after scanning the
+ `|=|' (found by FY). @1226
+* 24 Oct 1983
+I795\>638. Change the error recovery for bad delimiters, in accordance with
+ the changed syntax. (Found by Barry Smith.) @1183
+* 9 Nov 1983
+E796. Optimize the code a bit more, based on empirical frequency data gathered
+ during September and October: In \S45, use the fact that the
+ result is almost always true. In \S380, delete `{\bf while}
+ \\{true} {\bf do}' since many compilers implement that badly.
+ Rewrite \S852 to avoid calling \\{badness} in the most common case.
+ @45,380,852
+* 3 Dec 1983
+F797. Don't forget to call |error| after the message has been
+ given (noticed by Gabi Kuper). @500
+# Version 1.0 released today incorporates all of the above.
+* 9 Dec 1983
+# Dinner party with 36 guests to celebrate \TeX's coming of age.
+* 2 Feb 1984
+S798\>786. Reinstall |\font| precautions that I thought were unnecessary.
+ I overlooked many problematic possibilities, like
+ `|{\font\a=x| |\global\a}| |\the\font|'
+ and `|\font\a=x| |\font\b=x| |\let\b=\undefined| |\the\a|', etc.
+ (Found by Mike Urban.) The new remedy involves removal of the
+ \\{font_ident} array and putting the identifiers into a frozen
+ part of the hash table; so there's a sprinkling of corrections
+ in lots of modules. But basically the change is quite conservative,
+ so it shouldn't spawn any new bugs (it says here). @222,267,1257
+* 9 Feb 1984
+S799. Remove the possibility of double interrupt, in a scenario
+ found by Clint Cuzzo. @1031
+* 12 Feb 1984
+Q800. Improve spacing in a formula like |$(A,<)$|. @764
+* 13 Feb 1984
+A801. Avoid a bad {\bf goto}, as diagnosed by Clint Cuzzo and George O'Connor.
+ (Must not go directly to \\{switch}.) @346
+E802. Conserve string pool space by not storing file name in two
+ guises (suggested by DRF). @537
+* 26 Feb 1984
+I803. Make scaled output look cleaner by printing fewer decimals whenever
+ this involves no loss of accuracy. (Suggested by \MF\ development.) @103
+* 2 Mar 1984
+R804. Maintain 17-digit accuracy, not 16; now
+ constants like `|.00000762939453126pt|' will round correctly. @452
+* 16 Mar 1984
+R805. Plug a loophole that permitted recursion in \\{get_next}, by disallowing
+ deletions in \\{check_outer_validity}. @336
+* 24 Mar 1984
+I806. Open the terminal before trying to wake it up, when the program starts bad.
+ @1332
+* 27 Mar 1984
+R807. Check that $k<63$, to avoid the
+ |\patterns{xxx...xxxdxxxdxxx}| anomaly found by Jacques D\'esarm\'enien. @962
+* 11 Apr 1984
+F808. Supply code for the missing case \\{adjust_node} in \\{copy_node_list}. @206
+# Yoicks, how could serious bugs like that have escaped detection?
+* 11 Jun 1984
+D809\>627. Initialize \\{char_base}, etc., for \\{null_font}.
+ (Found by Nick Briggs.) @552
+R810. Clear the \\{buffer} array initially (Briggs). @331
+* 21 Jun 1984
+C811. Look ahead for ligature or kern after a |\chardef|'d item
+ (D\'esarm\'enien). @1038
+* 4 Jul 1984
+R812. Make the quarterword constraint explicit with a
+ new `\\{bad}' case (19). @111
+* 7 Jul 1984
+E813. Optimize \\{firm_up_the_line} slightly,
+ to be consistent with the \MF\ program. @363
+* 8 Jul 1984
+I814. Give additional diagnostics when |\tracingmacros>1|. @323
+# The changes above were incorporated in Version 1.1, released July 9, 1984.
+* 27 Jul 1984
+I815. Say `|see| |the| |transcript| |file|' after handling offline |\show|
+ commands. (Suggested by \MF.) @1298
+* 20 Oct 1984
+I816. Allow `|0|' in response to error prompts. @84
+# Those two changes led to Version 1.2.
+* 25 Nov 1984
+R817. Don't forget to check for \\{null} before looking at subfields
+ of a node. (This was ``dirty Pascal,'' with two quarterword 0's
+ read as a halfword.) @846
+R818. Ditto in another place! @939
+E819. Remove the fixed-at-compile-time
+ partition between lower and upper memory. @116,125,162
+# This major change in memory management
+ completes Version 1.3, which was published in preliminary
+ looseleaf form as `\TeX: The Program'.
+* 20 Dec 1984
+R820. Keep the \\{node_size} field from overflowing if the lower part of memory
+ is too large. @125
+# That was another bug in existence from the beginning!
+* 5 Jan 1985
+I821. Improve the missing-format-file error (DRF). @524
+* 7 Jan 1985
+I822. Update the terminal right away so that the welcoming message will
+ appear as soon as possible (DRF). @61
+* 23 Jan 1985
+I823. Convey more uncertainty in the
+ help message at times of \\{confusion}. @95
+I824\>610. Improve the \\{history} logic in the \\{warning_issued} case. @245
+* 18 Feb 1985
+P825\>810. Stick to standard Pascal: Don't
+ use \\{first} in a {\bf for} loop. [Some procedures ``threaten'' it
+ globally, according to British Standard 6192, section 6.8.3.9.]
+ (Pointed out by CET.) @331
+* 11 Apr 1985
+S826. Prevent nonexistent characters from being output by
+ unusual combinations of ligatures and hyphenation. @915
+* 15 Apr 1985
+L827\>819. Compute memory usage correctly in |INITEX|; the previous number
+ was wrong because of a |WEB| text macro without parentheses (DRF). @164
+* 16 Apr 1985
+E828. Speed up \\{flush_list} by not calling \\{free_avail} (DRF). @123
+* 17 Apr 1985
+A829\>788. Introduce a special kind of kern for accent positioning;
+ it must not disappear after a line break. @837,879,1125
+* 18 Apr 1985
+R830\>755. Prevent |\lastbox| and |\unkern| from removing discretionary replacements.
+ @1081,1105
+# That completes Version 1.4.
+* 26 Apr 1985
+C831. Don't try \\{\TeX_area} if a nonstandard file area has been specified
+ (DRF). @537
+# That was \#401 in \TeX78; I never learn!
+* 30 Apr 1985
+C832\>754. Eliminate the limitation on |\write| length; the reason for it
+ has disappeared (Nancy Tuma). @1370
+* 8 May 1985
+D833\>819. Allocate two words for the head of the \\{active} list (CET). @162
+* 11 May 1985
+I834. Change \\{wterm} to \\{wterm_ln} after a bad beginning (Bill Gropp). @1332
+E835\>806. Don't open the terminal twice (CET). @1332
+* 22 May 1985
+R836. Test for \\{batch_mode} after trying to open the transcript file,
+ not before (DRF). @92
+R837. Be prepared for string pool overflow while reading the command line!
+ (This bug was first found in \MF, when it could occur more easily.) @525
+* 7 Aug 1985
+A838. Fix a bug in |\edef\foo{\iffalse\fi\the\toks0}|: \TeX\ should stay in the
+ loop when expanding non-|\the|. (Found by Dan Brotsky.) @478
+# The above changes were incorporated in Version 1.5.
+* 27 Nov 1985
+C839\>764. Make `|plain|' a lowercase name, for consistency with the manual. @521
+I840\>669. Wake up the terminal for |\show| commands. @1294,1297
+# The above changes were incorporated in Version 2.0, which was published
+ as Volume~B of the {\sl Computers \& Typesetting\/} series.
+* 15 Dec 1986
+I841. Punctuate the Poirot help message more carefully. @1283
+* 28 Jan 1987
+R842. Make sure that \\{max_in_open} doesn't exceed 127 (DRF). @14
+D843\>680. Don't allow a |\kern| to be clobbered at the end of a pre-break list
+ when a discretionary break is taken. (A missing `{\bf else}' was the
+ source of the error, diagnosed incorrectly before.) @881
+D844. Take account of discarded nodes when computing the background width
+ after a discretionary. @840
+# That was the first really serious bug detected for more than 17 months! I found it
+ while experimenting with right-to-left extensions.
+# Version 2.1 was released on January 26, 1987.
+* 5 Feb 1987
+E845. Remove cases in \\{shorthand_def} that cannot occur (found by Pat Monardo). @1224
+* 14 Apr 1987
+R846. Improve robustness of data structure display
+ when debugging (Ronaldo Am\'a). @174,182
+* 21 Apr 1987
+E847. Make the storage allocation algorithm more elegant and efficient. @127
+* 22 Apr 1987
+A848\>742. Calculate the empty-line condition properly when \\{end_line_char} is
+ absent. @360
+# The previous three changes were found while I was teaching a class based
+ on Volume~B; they led to Version 2.2.
+* 28 Apr 1987
+E849. Avoid closing a file when \TeX\ knows that it isn't open (JS). @560
+* 3 Aug 1987
+S850. Clean up unfinished output if it's necessary to
+ \\{jump_out} (Klaus Gunterman). @642
+# That makes Version 2.3; subsequent version numbers won't be logged here.
+* 19 Aug 1987
+A851. Indent rules properly in cases like\hfil\break
+ |\hangindent=1pt$$\halign{...\cr\noalign{\hrule}}$$|. @806
+* 20 Aug 1987
+S852. Introduce \\{co_backup} because of cases like
+ |\hskip 0pt plus 1fil\ifdim| (Alan Guth). @366
+* 9 Nov 1987
+S853. Change the calculation for number of leader boxes, so that
+ it won't be too sensitive to roundoff error near exact multiples
+ (M. F. Bridgland). @626
+* 17 Nov 1987
+A854. Replace my stupid algorithm
+ for fixed-point multiplication of negatives (WGS). @572
+* 12 Dec 1987
+B855. Fix a typo in the initialization of hyphenation tables (PB). @952
+# That error was almost completely harmless, thus undetectable,
+ except if some |\lccode| is~1 and no |\patterns| are given.
+* 23 Dec 1987
+S856\>564. Be more cautious when ``relaxing'' a previously undefined |\csname|;
+ you might be inside a group (CET). @372
+* 20 Apr 1988
+S857. Make sure \\{temp_head} is well-formed whenever it can be
+ printed in a ``runaway'' message: Consider constructions like
+ |\outer\def\a0{}\a\a| (Silvio Levy). @391
+* 24 Apr 1988
+S858\>618. Avoid conflicting use of the string pool in constructions like
+ |\def\\#1{}\input a\\\z| (Robert Messer). @260
+* 10 May 1988
+R859. Amend the |\patterns| data structure when $\\{trie_min}=0$
+ (PB). @951,953
+* 25 May 1988
+R860. Guarantee that \\{trie_pointer} cannot be out of range. @923
+S861\>618. Avoid additional bugs like \#858 in constructions
+ like |\input a\romannumeral1|, etc. @464,465,470
+R862\>618. Prevent similar string pool confusion that could occur
+ during the processing of |**\input\romannumeral6|. @525
+* 19 Jun 1988
+S863\>819. Prevent a negative dividend from rounding upward,
+ causing a loop (CET). @126
+E864\>819. Adopt a smoother allocation strategy when
+ memory is nearly gone (CET). @126
+* 20 Jun 1988
+D865\>852. Initialize \\{cur_order}, now that it's being backed up
+ (Tsunetoshi Hayashi). @439
+* 6 Nov 1988
+S866\>612. Disable \\{fatal_error} in \\{prompt_input}, so that
+ \\{open_log_file} can use it safely (Tim Morgan). @71
+S867\>836. Force terminal output whenever \\{open_log_file} fails. @535
+* 14 Dec 1988
+P868\>866. Restore \\{fatal_error} in \\{prompt_input}, but don't
+ let it be unsafe for \\{open_log_file}. @92,534
+* 23 Jan 1989
+D869. Give $q$ a legal value when recovering from ``infinite shrinkage''
+ error. @976,1004
+* 17 Feb 1989
+D870\>758. Avoid spurious error message for |\aftergroup\relax\dump| by avoiding
+ inaccessible |\aftergroup| tokens (FM and Rainer Sch\"opf). @280
+* 20 Mar 1989
+R871. Don't refer to \\{link}(\\{null}) even when it ``can't happen''
+ (PB). @791
+* 7 Jun 1989
+S872. Avoid confusion from |$$\begingroup\halign{#\cr}$$| (FM). @1130
+* 20 Jun 1989
+S873. Put fraction digits into dynamic memory, not the global \\{dig} array,
+ because of constructions like |.5\ifdim.6| (FM). @452
+* 17 Jul 1989
+S874. Prevent embarrassing attempts to report errors before the string
+ mechanism has been fully initialized, for example when the
+ command line exceeds the buffer size (WGS). @31
+* 16 Aug 1989
+M875. Allow integer products to be 31 bits long (FM). @105
+* 31 Aug 1989
+C876\>441. Increase the number of tokens shown by \\{token_show}
+ (J. Lavagnino). @295
+S877. Avoid confusion from |$$\begingroup\eqno$$| (FM). @1140
+# The recent TUG meeting turned out to be an extend-\TeX-for-the-nineties
+ party! I agreed that some extensions for non-English languages
+ ought to be made while I still knew how to do them. (In other words,
+ I broke my firm commitment to keeping \TeX\ completely stable;
+ but in this case nobody objected.) The following eleven changes
+ were coded during the month of September.
+* 30 Sep 1989
+G878. Install major change allowing general 8-bit code input. @38,352
+G879. Install major change allowing multiple hyphenation tables
+ (M. Ferguson). @923
+G880. Introduce new parameters |\lefthyphenmin| and |\righthyphenmin|. @923
+G881. Introduce major new ligature capabilities including implicit
+ boundary characters. @908,1037
+G882. Install new |\inputlineno| feature suggested by MDS. @424
+G883. Install new |\holdinginserts| feature suggested by FM. @1014
+G884. Install new |\badness| feature. @424,664
+G885. Install new |\emergencystretch| feature. @863
+G886. Install new |\errorcontextlines| feature suggested by FM. @311
+S887. Recover from anomaly when hyphenation |char_warning| clobbers
+ |old_setting|. @863
+P888. Make it easier to change the format extension (Don Hosek). @520,1328
+* 16 Oct 1989
+R889. Avoid range check in null font with |bc=256| (PB). @565
+* 22 Nov 1989
+S890\>856. Prevent \\{save_stack} conflicts in
+ |{\hbox\expandafter{\csname\endcsname}}| and\kern-3pt\break
+ similar constructions (WGS). @645,1117
+S891\>858. System-dependent parts of file names must be addressed relatively,
+ not absolutely (FM and Rainer Sch\"opf). @516,517
+* 3 Dec 1989
+G892\>880. Allow different hyphenmins in the same paragraph (M. Ferguson). @1376
+S893. Distinguish |\par| from characters on |\if| tests. (MVL). @334
+S894\>378. Alignments need to be more robust against malicious
+ attacks (MVL). @782
+C895. Don't let kerns in discretionaries disappear at breaks (MVL). @869
+Q896\>881. Make the new hyphenation reconstruction procedure less cautious,
+ so that it doesn't lose hyphens found by the old method. @914
+* 11 Dec 1989
+D897\>879. Make an undumped trie dumpable again (PB). @1325
+* 18 Dec 1989
+G898\>588. Allow access to page totals in |\output| routines
+ (FM and Chris Rowley). @421
+* 22 Jan 1990
+R899\>611. Recognize more cases of unbalanced |\output| (CET). @1026
+* 29 Jan 1990
+S900\>758. Make |\aftergroup| work properly after |\eqno| (Michael Downes).
+ @1194
+* 1 Feb 1990
+S901\>878. Fix one more case of \\{end_line_char_inactive} (WGS). @360
+* 22 Feb 1990
+R902. Don't lose the last active node when total demerits are very high (FM).
+ @836,854
+* 13 Mar 1990
+D903. Doublecheck math fonts after making equation number (MVL). @1194
+D904. Don't forget to rule out charnodes before testing \\{type} (MVL).
+ @805,1202
+* 23 Mar 1990
+F905\>881. Don't change the font of punctuation preceding a hyphenated word
+ (Scott C. Allendorf). @903
+I906. Balance the parentheses shown on the terminal during normal runs. @1335
+E907. Optimize |\ifx\p\q| after |\let\p=\q| (MVL). @508
+S908. Treat migration properly in displays (MVL). @1199,1205
+# We're now up to Version 3.0; I sincerely hope all bugs have been found.
+* 11 May 1990
+F909\>881. Initialize |\nullfont| ligature parameters (Lance Carnes). @552
+* 22 July 1990
+S910\>579. Treat |\prevgraf| as zero within |\write| (Bogus\l aw Jackowski).
+ @422
+* 26 July 1990
+S911. Report `|l.1|' when first line of file overflows buffer
+ (George Russell). @538
+* 5 December 1990
+S912\>878. Translate unprintable characters in font identifiers (WGS). @63
+* 28 December 1990
+R913. Avoid range check when there are 65536 or more pages (Eberhard Mattes).
+ @642
+* 20 September 1991
+I914\>878. Improve error message for |\mathchar| out of range. @436
+S915\>878. Retain unprintable internal strings in 8-bit form (FM). @59
+S916\>881. Retain right punctuation context for ligature reconstruction
+ (problem found by Brian Hamilton Kelly). @903
+* 10 January 1992
+S917\>881. Also avoid producing a double kern at boundary (CET). @897
+S918. Disallow |\setbox| where it doesn't work (Robert Hunt). @1241,1270
+S919. Robustify |\mskip| and |\mkern| in presence of negative quad (WGS).
+ @716,717
+S920\>679. Defend against `|}{|' in |\read| (Michael Downes). @483
+E921\>798. Save string memory if font occurs repeatedly (Bogus\l aw Jackowski).
+ @1260
+S922\>784. Don't let |\newlinechar| interrupt unprintable expansion (Bernd
+ Raichle). @59,60
+* 7 February 1992
+D923\>881. Restore |cur_l| properly when boundary character doesn't exist
+ (Mattes and Raichle). @1036
+* 17 July 1992
+C924\>892. Use current language at beginning of horizontal mode (Rainer
+ Sch\"opf and CET). @1091,1200
+* 17 December 1992
+R925\>879. Avoid (harmless) range errors (Philip Taylor and CET). @934,960
+* 25 February 1993
+C926\>881. Protect kerns inserted by boundary characters (William Baxter).
+ @837,866
+S927\>917. Don't let boundary kern disappear after hyphenation. @897
+* 26 June 1993
+R928\>668. Avoid potential future bug (Peter Breitenlohner). @628,637
+* 17 December 1993
+S929\>881. Boundary character representation shouldn't depend on the font
+ memory size (Berthold Horn). @549,1323
+* 10 March 1994
+R930. Huge font parameter number may exceed array bound (CET). @549
+* 4 September 1994
+F931\>926. Math kerns are explicit (Walter Carlip). @717
+R932. Avoid overflow on huge real-to-integer conversion. @625,634
+* 19 March 1995
+R933. Avoid spurious reference counts in format files (PB). @1335
+* 23 November 1998
+R934. Make sure that TeX is certifiably Y2K-safe. @1328
+* 12 July 1999
+A935\>255. |\xleaders| often drops the final box (Hiroshi Nakashima). @627,636
+* 30 August 2001
+R936. Don't allow a million unbalanced braces followed by |\cr|
+ outside of any alignment (Ralf Roth). @789
+R937. Don't allow end-template except at end of template (Roth). @325,1131
+* 30 September 2002
+Q938. Improve rounding of glue during output (M. F. Bridgland). @625,634
+* 18 March 2008
+F939. Forgot to goto the proper error recovery (DRF). @395
+E940. Avoid a case of dirty Pascal and speed up inner loop. @1035
+R941. Forget reference to last_glue in format file (DRF). @1335
+R942. Glue reference not updated after overflow error (DRF). @1236
+B943. Muglue in \leaders had wrong units; now disallowed (DRF). @1078
+E944. Speedup of ligatures and kerns at end of a word (DRF). @1039
+R945. Another sanity check of TFM files for security (DRF). @365
+I946. Give better size data for unmagnified insertions. @986
+* 6 January 2014
+C947. Space after \csname\endcsname to match other spaces. (Oleg Bulatov) @262
+\relax
+\bye
+
+Up to a point it is better to let the snags [bugs] be there
+than to spend such time in design that there are none
+(how many decades would this course take?).
+-- A M Turing
+ Proposals for Development in the Mathematics Division
+ of an Automatic Computing Engine (ACE)
+ Report E882, Executive Committee, National Physical Laboratory (NPL)
+ 1945; reprinted in April 1972 as NPL report Com Sci 57
+ page 18
+ quoted by Carpenter and Doran in The Computer Journal 20 (1977), 273.
+
+DRF David Fuchs
+HWT Howard Trickey
+FY Frank Yellin
+LL Leslie Lamport
+JS Jim Sterken
+ARK Arthur Keller
+CET Chris Thompson
+FM Frank Mittelbach (added subsequent to publication of paper)
+PB Peter Breitenlohner (ditto)
+WGS Wayne G. Sullivan (ditto)
+MVL Marc van Leeuwen (ditto)
+MDS Michael D. Spivak (ditto)
diff --git a/systems/knuth/dist/errata/logmac.tex b/systems/knuth/dist/errata/logmac.tex
new file mode 100644
index 0000000000..8719efc497
--- /dev/null
+++ b/systems/knuth/dist/errata/logmac.tex
@@ -0,0 +1,107 @@
+% macros for the appendix to "Errors of TeX" paper
+\input manmac
+\voffset=-.3in
+\catcode`\^=7 % disable the indexing stuff
+\font\sltt=cmsltt9
+\font\logosl=logosl9
+\ninepoint
+\vsize=48pc \pageheight=\vsize
+\hsize=33.5pc
+
+\def\\{\bgroup\catcode`_=\active\identifier}
+{\catcode`_=\active \global\let_=\_}
+\def\identifier#1{\hbox{\it#1\/\kern.05em}\egroup} % italic type for identifiers
+
+\newdimen\codesize \setbox0=\hbox{\enspace M} \codesize=\wd0
+\setbox0=\hbox{\thinspace\S1111\kern\codesize} \rightskip=\wd0
+\newdimen\itemnosize \setbox0=\hbox{\bf999} \itemnosize=\wd0
+\setbox0=\hbox{$999\mapsto{}$\kern\itemnosize\enspace} \parindent=\wd0
+\newdimen\datemar \setbox0=\hbox{\bf 15 Mar 1977}
+\datemar=\hsize \advance\datemar-\wd0 \divide\datemar2
+\newdimen\hangamount \hangamount=\parindent \advance\hangamount 1em
+\everypar{\global\hangindent=\hangamount}
+\parfillskip=-\rightskip
+\finalhyphendemerits=0
+\interlinepenalty=100
+\smallskipamount=3pt plus 2pt
+\def\smallbreak{\fin\penalty-50\smallskip}
+
+\newbox\predec % for predecessor "mapsto" info
+\newbox\code % for the code letter
+\newbox\texsec % for TeX section numbers
+\newbox\cbox % the symbol used on commentlines
+\newbox\dbox % empty box used after datelines
+\setbox\dbox=\hbox to\datemar{}
+\setbox\cbox=\hbox to\itemnosize{\hfil$\bullet$}
+
+\newif\ifnothing \newif\ifnobreak
+\def\fin{\ifnothing\nothingfalse\else\endgroup
+ \unskip\nobreak\hfil\penalty500\enspace\null\nobreak\hfil\box\texsec
+ \box\code\endgraf\ifnobreak\nobreakfalse\nobreak\fi\fi}
+
+\catcode`\ =12 % tabs are ordinary characters
+\catcode`@=\active % @ signs denote a TeX section reference
+{\obeylines \gdef@#1
+ {\global\setbox\texsec=\hbox{\S$#1$}\lookahead}}
+\chardef\@=`\@
+{\catcode`X=\active \gdef X{\X}}
+\def\X{{\eightbf X}}
+\def\lightX{\hbox{\eightrm X}}
+\def\>#1\relax{\global\setbox\predec=\hbox{\let\X=\lightX
+ $#1\mapsto{}$}} % previous bug ref
+
+\def\checkvalid#1{\expandafter\ifx\csname#1!\endcsname\okay\else\invalidcode\fi}
+\def\okay{\okay}
+\def\makevalid#1{\expandafter\let\csname#1!\endcsname=\okay}
+\makevalid A % algorithm
+\makevalid B % blunder
+\makevalid C % cleanup
+\makevalid D % data structure
+\makevalid E % efficiency
+\makevalid F % forgotten case
+\makevalid G % generalization
+\makevalid I % interaction
+\makevalid L % language
+\makevalid M % mismatch
+\makevalid P % program organization
+\makevalid Q % quality
+\makevalid R % robustness
+\makevalid S % surprise
+\makevalid T % typo
+
+\def\lookahead{\futurelet\next\looky} % we'll do this at end of every line
+\def\looky{\if\next \let\cont\continuationline % tab mark
+ \else\if\next*\let\cont\dateline % asterisk
+ \else\if\next##\let\cont\commentline % sharp sign
+ \else\if\next\relax\let\cont\endit % \relax (gets us out)
+ \else\let\cont\dataline\fi\fi\fi\fi\cont} % otherwise should be a code letter
+\def\continuationline { } % insert space if a tab mark starts the next line
+\def\dateline* {\smallbreak\begingroup\bf\hfill\nobreaktrue
+ \global\setbox\code=\copy\dbox}
+\def\dataline#1{\fin\checkvalid#1%
+ \global\setbox\code=\hbox to\codesize{\enspace
+ \hskip0pt plus 3fil\rm#1\hskip0pt plus1fil}%
+ \begingroup\rm\catcode`X=\active\itemnumber}
+\def\itemnumber#1.{\catcode`X=11\setbox0=\hbox to\itemnosize{\bf\hss#1\relax}%
+ \setbox2=\hbox{\bf#1\relax}\ifdim\wd2>\wd0 \setbox0=\box2 \fi
+ \textindent{\hfil\box\predec\box0}}
+\def\commentline{\fin\afterassignment\begincomment\let\next}
+\def\begincomment{\begingroup\let\tt=\sltt
+ \let\MF=\slMF\sl\textindent{\hfil\copy\cbox}}
+
+\def\leftheadline{\hbox to \hsize{%
+ \vbox to 10pt{}% strut to position the baseline
+ {\tenrm\folio\kern1pc}% folio to left of text
+ \hfil\eightrm D. \ E. \ KNUTH\hfil% running head
+ }}
+\def\rightheadline{\hbox to \hsize{%
+ \vbox to 10pt{}% strut to position the baseline
+ \hfil\eightrm THE \ ERRORS \ OF \ \TeX\hfil% running head
+ {\kern1pc\tenrm\folio}% folio to right of text
+ }}
+
+\def\O#1{\hbox{\rm\char'23\kern-.2em\it#1\/\kern.05em}} % octal constant
+\def\slMF{{\logosl META}\-{\logosl FONT}}
+
+\def\endit{\fin\catcode`\^^M=5\let\par=\endgraf}
+\let\par=\lookahead \obeylines \nothingtrue
diff --git a/systems/knuth/dist/errata/mf84.bug b/systems/knuth/dist/errata/mf84.bug
new file mode 100644
index 0000000000..0ce8ccbd74
--- /dev/null
+++ b/systems/knuth/dist/errata/mf84.bug
@@ -0,0 +1,2336 @@
+This file has been updated periodically ever since METAFONT84 was born.
+What you are about to read is "authentic source material" from the
+early days before the program converged. Module numbers on the first
+entries may bear little relation to those in Volume D.
+
+Entries are in chronological order; thus the most recent news appears
+at the bottom of the file.
+
+-------------------------------------------------------------------------------
+
+(rough list of all bugs found in MF after it passed syntax check)
+Starting with Version -100.0: [Mar 27, first run, about 00:30]
+1. fix_date_and_time was badly patched in INIMF.
+2. cur_tok function forgot "cur_tok:=p" at the end; PASCAL doesn't catch that.
+3. "multiletter control sequences" msgs should say "symbolic tokens".
+Version -99.0: [still Mar 27, done while waiting for WEAVE/TeX output]
+4. do_statement should initialize cur_type:=vacuous.
+5. find_variable has misplaced p:=equiv(p).
+6. one-character primitives shouldn't decrease str_ptr.
+7. clear_symbol had "q" at end where I meant to say "p".
+8. help message for extra token flushing improved.
+9. "buffer[k]" should be "buffer[j]" for 1-character id_lookup.
+10. def_delims forgot to get_next afterwards.
+11. type_command at beginning of statement wasn't diverted.
+12. menu always came out after help message.
+13. forgot to return after getting num/str token from token list.
+ Version -98.0: [March 28]
+14. scan_declared_variable flushes too much.
+15. ordered pairs: one level of indirection was forgotten.
+16. type declarations must add "unknown" bit.
+17. semicolon+ added to do_statement cases.
+18. base_ident should get max_str_ref.
+19. "internal[...]" missing in date for base_ident.
+20. do_binary(p,c) should be do_binary(p,d) after continue_path
+21. in scan_def after get_clear_symbol I need to get_next.
+22. get_next needed at end of scan_def.
+23. get_next needed at end of mode_command processing.
+24. randomseed missing error typo: `;' for `:='.
+25. print_nl("") at beginning of show.
+26. refcounts on macro calls were incremented twice.
+27. forgot to advance loc after getting num/str token from token list.
+28. "/" has the wrong command code.
+29. get_x_next needed after, e.g., "1/2" in scan_primary.
+30. take_fraction should be make_fraction in call of frac_mult.
+31. v:=dep_list(v) forgotten in dep+dep branch of add_or_subtract.
+32. q:=link(q) etc misplaced and omitted in p_plus_fq and p_plus_q.
+33. dep_finish recycled a dependency list twice.
+34. call of dep_finish should use null not cur_exp.
+35. decided that "3 3" shouldn't equal 9.
+[also put in new code for the addto command]
+ Version -97.0: [March 29]
+36. forget to set q:=qq in scan_expression.
+37. first line of print_path: forgot print_ln.
+38. typo in set_controls: right_y should be left_y.
+39. toss_knot_list forgot that it has a circular list to toss.
+40. print_exp shows big nodes backwards.
+41. angle brackets didn't look good in print_exp; took them out.
+42. left_brace must be between min_tertiary and max_tertiary.
+43. prev_dep should point back to tail, not head, of previous list.
+ (that error was in encapsulate and <Copy the big node>)
+44. nonlinear_eq should leave node p vacant, sometimes.
+45. conditional routine forgot that get_boolean starts with get_x_next.
+46. forgot get_x_next in processing of else.
+47. str_ref not initialized by make_string.
+48. param_start not initialized properly.
+49. "%EXPR" didn't look good in token list.
+50. "{loop}" not necessary in forever loops; there's already enough.
+51. macro name didn't come out right when tracing macros.
+52. macro parameters weren't shown when tracing macros.
+53. quote marks missing when displaying strings.
+54. parameter names on macro call trace should look nicer.
+55. statement beginning with bad token: error message needed.
+56. `->' desired in context lines of macro.
+57. forgot q:=loop_list_loc(s) in begin_iteration.
+[also put in new code for shipout]
+ Version -96.0: [March 30]
+58. forgot loop_type(s):=null in begin_iteration.
+59. forgot value(p):=null after type(p):=type(pp) in find_variable.
+60. forgot to delete_pen_ref after filling a contour.
+61. check that it's a cycle before calling make_spec.
+62. "if tracing_stats>0" should be "if internal[tracing_stats]>0"!
+63. forgot get_next after showdependencies.
+64. forgot link(r):=p at end of dependency list addition.
+65. extra `get_x_next' in <Scan a direction...>.
+66. typos in last half of <Remove open types...>: mind the p's and q's.
+67. take_fraction should be make_fraction in simple case of two givens.
+68. in print_exp of unknown type, stop if v=null.
+69. showtoken forgot to stop; also should be consistent with TeX.
+70. "continue" label needed in error routine, after all.
+71. id_lookup should treat length-one identifiers by simpler method.
+ (in particular, this allows it to find unprintable ones!)
+72. need psi[n+1]:=psi[1] in cyclic case of make_choices.
+73. tension/controls take primaries as arguments, not expressions.
+74. print_spec didn't initialize octant.
+75. print_spec: break lines into two, since they are quite long.
+76. smooth_moves not called in fill_spec.
+77. typo < for > in edge_prep.
+78. edge tracing misses half the corners, and forgot to set trace_y.
+79. edge_prep needs extra logic when rover=head.
+80. make_good should compute cur_d based on good values, not original values;
+ but we sometimes need the other alternative too.
+81. valid_sum: the test was backwards.
+82. print_weight blunder: ho(q) should be ho(info(q)).
+83. forgot init_gf in shipout.
+84. typo in <Finish the GF file>: if total_chars<>1 then print("s").
+85. initializations of ww, prev_w, and m were forgotten in ship_out.
+86. typos in initialization of prev_m and gf_min_x.
+87. forgot to update prev_n in ship_out.
+88. relax becomes "expandable".
+89. internal[x]>1 should be internal[x]>unity!
+90. forgot to restore normal scanner_status in pass_text.
+91. (end occurred when if...) was inevitable.
+[also put in new code for openwindow, display]
+ Version -95.0: [April 1]
+92. forgot to unscale the window numbers in that new code.
+93. had w<0 test instead of w<=0 in <Record a possible transition...>.
+94. prev_w:=w should be done in all cases of <Record a possible...>.
+95. forgot get_x_next after scanning `1/2'.
+96. in do_assignment, after recycle_value(p) I need to set type(p):=something.
+97. omit `.' after the equivalent in showtoken.
+98. in <Copy the big node p>, I said "until q=p"; I meant "until q=value(p)".
+99. print_macro_name said "info(link(a))"; meant "text(info(info(link(a))))".
+100. forgot get_x_next after macro_call in scan_primary.
+101. scan_primary installed dependent components badly (data structure error).
+102. new_structure failed in case of generic_subscript (data structure error).
+103. param_type case of print_cmd_mod had bad if/else nesting (language error).
+104. null_path forgot to return a value (language error).
+105. p_with_x_becoming_q had typos (q and f for x).
+106. <Copy the big node...> should end with cur_exp:=t, not r.
+107. subscripted definitions: out of sync; scan_primary loop needs revision.
+108. secondarydef went through scan_def instead of make_op_def.
+109. make_op_def forgot the final get_next.
+110. make_op_def also forgot to insert the general_macro preface.
+111. showvar shows too much of macro variables.
+ Version -94.0: [April 4]
+112. single_dependency needs to take account of the scale factor.
+113. add_or_subtract starts out with wrong v value. [bug 31 was fixed wrong]
+114. print_exp needs a case for t=independent.
+115. also numeric_type, and case v=null of pair_type, transform_type.
+116. end_token_list thought identity_macro and insertions had ref counts.
+117. disp_var should display also in case of generic subscripts.
+118. find_var should put value(pp):=null when it sets type(pp):=numeric_type.
+119. yet another problem in <Copy the big...>: copy_dep_list(dep_list(q)).
+120. scan_primary should flush_node_list(q) before make_exp_copy(p).
+121. make_scaled arguments reversed in p_over_v.
+[also added code for new internals: smoothing, autorounding]
+[also added code for showstats]
+ Version -90.0: [April 5]
+122. Should check range of variables when they become known.
+123. <Exclaim about...> should include begin...end (WEB language error).
+124. forever should be followed by a colon.
+125. dangling else problem in make_eq: "abc"="abc" considered false.
+126. showstats, showtoken should read the semicolon before stopping.
+127. print_variable_name balked at, e.g., xpart of a capsule.
+ Version -89.0: [April 6]
+128. after &foo, shouldn't assume there's an input file present.
+129. forever forgot get_next.
+130. pair=path and path=pair should coerce the pair to be a path.
+131. similarly in the operand to `addto'.
+[added new code for cull command]
+[minor revision of internal quantities; e.g., labeling is out, fontmaking is in]
+ Version -88.0: [April 7]
+132. "string" operator is renamed "str".
+133. min/max coordinates added to boc command.
+134. labeling_command removed; it will be implemented in macros.
+135. info(p) should be attr_loc(p) in flush_variable.
+136. make_spec didn't realize the need for xh and yh.
+137. gf_new_row parameters had sign reversed.
+138. the "numeric" case was left out of recycle_value.
+[added new code for special/numspecial]
+ Version -87.0: [April 13]
+139. q:=qq should apply also after <Splice...>, in scan_expr.
+140. param_size: better defined than a constant.
+141. surprise: back_input fails when cur_cmd=expr_arg, as it changes limit.
+142. <Subdivide...second time wrt x'-y'> forgot to change right_type(r).
+143. show <path> shows the whole path.
+144. expr like 0..1 leads to <Determine the path join...> with q undefined.
+145. typos in <Decide...clockwise>, clobbered dx2 and used right_type(p) twice.
+146. need to adjust expr_arg when macro is inside a macro!
+147. expr_arg forbidden to be in a text argument.
+148. frozen_relax added to prevent premature termination.
+[I took out the code used for debugging the screen interface]
+[added "cycle", a unary operator taking path to boolean]
+[added the code for concatenation and for str and for xpart..yypart]
+ Version -86.0: [April 25]
+149. not_scolded_yet was never set false after scolding.
+150. Missing "q:=s" at the end of <Make variable q known>.
+151. In @<Worry...@>, add get_next after back_error.
+152. New xpart routine didn't recycle other components of the big node.
+[added the "hard_times" case of multiplication]
+[added the complete set of boolean expressions]
+[added the eight transformations, applied to three kinds of expressions]
+[added the save command]
+[added the interim command]
+[added the let command]
+[added the pen operations]
+153. Incomplete help message on "Not a variable".
+154. forgot begin...end in the last section of materialize_pen.
+155. blunder <Insert a new line...>: length(p,s,q) should be length(s,q,r).
+156. <Construct the offset...>: forgot begin...end here too.
+157. "(newly created)" shd be " (newly created)".
+158. path_trans: forgot to unstash the capsule p.
+159. set_up_trans: transform_type should be transformed_by, in one place.
+160. <Install sines...>: had p instead of q, in several places.
+161. edges_trans: an edges variable isn't in a capsule.
+ Version -85.0: [May 2]
+162. begingroup ... endgroup should be allowed as compound statement.
+163. pausing shouldn't clobber "showit". (WAITS version only)
+164. Improved help message for isolated expressions.
+165. Bad error recovery after "Not a variable" in showvar.
+166. Typo in clear_symbol: name_type(p) instead of name_type(q).
+167. "%" and ";" to be left out of file names being input.
+168. dep_finish used v for two purposes; wrong declared type.
+169. autorounding 2 suppresses correction when the derivative isn't critical.
+170. tab characters should be changed to spaces on input. (WAITS version)
+171. harmful goto: Forgot to |return| after path_trans and edge_trans.
+[added ord, hex, oct, length, char, decimal, and jobname]
+ Version -80.0: [May 4]
+172. showvar still screwed up.
+173. don't clobber screen for prompt_input (WAITS only).
+174. length of non-cyclic path is one too high.
+175. <Evoke an error message...> evoked the wrong message.
+176. better error message needed on `Equation cannot be performed'.
+177. ">> " inserted before expression displays.
+178. <Finish linking...> should dup_offset also when n=0.
+179. pencircle was a factor of 2 too big.
+180. cosine of 180 should be -1 exactly.
+181. ord "" should be -1.
+182. shipout should allow expressions as well as variables.
+183. "mod outer_tag" needed in various tests on eq_type.
+184. merge_edges forgot to adjust for changes in m_offsets.
+185. typo, ww for w in print_pen.
+186. typo, "outer" for "errhelp" in print_cmd_mod (message case).
+[added "also"]
+[added "message", "errmessage", "errhelp"]
+[added "outer", "inner"]
+[added "readterminal", "readstring"]
+ Version -79.0: [May 6]
+187. read_toks forgot to include the general_macro token.
+188. offset_prep should treat n=0 like n=1.
+189. cusp transitions (from change 169) must be suppressed in envelope case.
+190. <Record a line segment...> shouldn't change xp or yp.
+191. <Find the initial slope...> was used too late.
+192. <Find the index...> had a redundant test (a relic of old code).
+193. <Compute test coeff...> wastefully made the same fraction three times.
+194. doublepath <cycle> should split the cycle.
+195. `if k=0' in fin_offset_prep should have been `if k=1'.
+196. `done:' misplaced in <Make the envelope moves...>.
+197. also move[move_ptr] should be initialized to 1 there, not 0.
+198. skew is used with p=q in fill_envelope.
+199. it's no use tracing subdivision for offsets, since everything is skewed.
+200. in <Record a line...>, round_unscaled should be floor_unscaled.
+201. unskewing: wrong formula (x was .5 too large).
+202. elliptical pens: better correction for the case gamma<=abs(alpha).
+203. dual env_move was updated badly, in both <Transfer...> and <Record...>.
+204. dual env_move to move: subscripts were off by 1.
+205. <Insert...dually...> should set ww:=knil(link(h)), not link(h).
+[added ++]
+ Version -70.0: [May 9]
+206. suppressed cusp rounding in envelope case too.
+207. `show' should use `>> ' when it shows an expression.
+208. streamlined the test for bad lambda and mu in |skew|.
+209. after linear_eq, must check if type(cur_exp)=known.
+210. print_spec surprise: contours might never travel toward the first octant!
+211. t0,t1,t2 sign was reversed in <Complete the offset splitting...>.
+ Version -69.0: [May 10]
+212. "showvar" changed to "showvariable".
+213. typo: r for r_delim in <Scan a text parameter...>.
+214. "newinternal" feature added.
+215. <Record a line...dually...> needs to update envmove at end of loop.
+216. smooth_moves shouldn't affect the extreme points.
+217. floor(unity*(n-1)/n) is unity when n is large; need to be more careful.
+218. major improvements to rounding, based on new theory of (x+eps,y+eps^2).
+219. pen offsets made symmetrical about the origin.
+220. p:=half(p+q) overflow problem in take_fraction, take_scaled.
+221. cull_edges now updates the max/min values too.
+222. make_spec needs to be more bullet-proof to rounding errors; added `dest's.
+223. xmult/ymult/zmult changed to xscaled/yscaled/zscaled.
+224. elliptical pens improved by temporarily allowing zero-length lines.
+[added edge transposition algorithm]
+ Version -67.0: [May 17]
+225. copy can encounter a vacuous expression, e.g. in a parameter.
+226. xy_swap_edges mustn't change w when inserting a large weight.
+227. in #222, had typo pp for r in <split ... second time ... x'-y'>.
+228. in #222, incorrectly assumed local max = global max of cubic.
+ Version -66.0: [May 19]
+229. bad_binary(c,p) should have been bad_binary(p,c).
+230. enabled comparison of unknown strings.
+231. fixed ring_delete to avoid unexpected scolding.
+232. should store character width info even when proofing<0.
+[added all the TFM commands and output routines]
+[added the `substring' operation]
+[added `point', `precontrol', `postcontrol'; deleted `direction']
+ Version -65.0: [May 24]
+233. Change error message `(t)point(u)' to `point(t)of(u)'.
+234. newinternal to allow a list of tokens.
+235. In <Feed...>, should check for overflow before begin_token_list.
+236. scan_def didn't allow for the case warning_info=null.
+ Version -60.0: [May 25]
+237. increased param_size.
+238. added <boolean><relation><boolean>.
+239. <Remove dead cubics> stopped too early if the first cubic was dead.
+240. bad_unary, bad_binary should distinguish pair from unknown pair.
+241. yet another "m<env_move[move_ptr]" test needs to be inserted.
+242. newinternal names need to go in the dump file.
+243. delete_mac_ref(v) should be delete_mac_ref(value(p)) in recycle_value.
+244. constant in <Decide...clockwise> was scaled instead of fraction.
+ Version -59.0: [May 27]
+245. break up initialize routine into two parts (cf init_prim in TeX)
+ Version -58.0: [May 28]
+246. do_ship_out should scan_expression, not scan_primary.
+247. offset_prep might clobber node q, so we need to search for q again.
+248. pair "1" = true! (blunder in type_test)
+249. allow strings x in `def f(expr x)=def g=x enddef enddef'.
+250. allow exprs in text parameters, in sufficiently easy cases.
+251. check right delimiters in macro_call.
+252. "Bad window number" message gave unscaled value.
+253. turnaround feature improves automatic rounding slightly.
+254. primaries changed to expressions in window and cull commands.
+255. scan_expression should make (0,0){0,0} a path, not a future path.
+256. showvariable and showtoken to allow lists; all shows made consistent.
+257. new internal quantity "windingcheck".
+258. delete warning message about edges never used.
+259. vardef: group the righthand side.
+260. show commands stop once each.
+261. cur_type should be vacuous after equation or assignment on outer level.
+ Version -57.0: [June 4]
+262. added xyzzy diagnostic about effect of autorounding.
+263. make_moves now puts xi_corr,eta_corr into halving.
+[otherwise "draw (0,0)..(9,-.00002)" and "draw (0,-.00002)..(9,0)" fail badly!]
+ Version -56.0: [June 6]
+264. smooth_moves changed completely; now is LR symmetric, cleaner.
+265. changed "unknown" to "known".
+266. pair to path conversion now done on length, makepen, reverse, etc.
+267. cycle now defined on arguments of any type.
+268. (re 218) sign was flipped in ceiling correction of <insert downward...>.
+[added union, subpath, penoffset, directiontime, intersectiontimes]
+
+Note: the first two draw statements in PENS.mf show an interesting effect.
+With autorounding, the curves are noticeably better, esp the small one.
+But there are two "holes", because of tiny discrepancies that I believe
+are unavoidable.
+Added later: Actually these are avoided by the new smooth_moves, but
+certain others will not be.
+ Version -50.0 [June 30]
+269. smoothing to be shut off at the retrograde transitions
+270. more care needed in the rootfinding part of find_direction_time.
+271. improved error recovery after "Isolated expression".
+ Version -40.0 [July 3] --- the first version to be "complete"!
+272. trivial change in <Exit...if the derivative $B(x_1,x_2,x_3;t)$...>.
+273. check that halfwords are large enough to hold edge/weight data.
+274. `path reverse (0,1)' should be true.
+275. xy_swap_edges might empty the structure; then x_reflect will fail!
+276. fix_offset was being done too often, due to improper range test.
+277. parameters come out even when tracingonline=0.
+278. step-until wasted r and didn't check syntax carefully.
+279. <Scan a group...> had typo: `line' for `group_line'.
+280. "You can't dump in a group" error was impossible.
+281. need better ETC cutoff in showmacro.
+282. print_exp needs to handle independents, because it's used by disp_var.
+283. suppress `#### xpart %CAPSULEnnnn' printouts.
+[added code for everyjob; this version wasn't complete after all!]
+ Version -30.0 [July 9]
+284. need better error/help after ` if s1=s2' when the strings are unknown.
+285. removed autorounding>2; it was leading to more glitches than it was worth.
+286. envelope filling of a cycle to be done by two calls of fill_envelope.
+287. "null" changed to "vacuous".
+288. cyclic path inserted in another path should retain its last arc.
+289. introduce several dozen "put_get" errors for improved recovery.
+290. suppress "with autorounding" when autorounding hasn't been set.
+291. {loop value} came out even when tracingonline=0.
+292. show commands should wake up the terminal before showing their output.
+293. forgot error message in `if true else'.
+ Version -20.0 [July 14]
+294. need to catch pens that are too large.
+295. make_eq dassn't change lhs (because this clobbers the memory links later).
+296. string usage is reported too high by showstats.
+297. bilin1 and add_mult_dep can call p_plus_fq with type(q)=proto_dependent.
+298. <Transform a known big node> forgot to recycle pp.
+299. in change number 286, the weight of the two paths should be the same.
+300. print_ln sometimes changed to print_nl("") in order to avoid blank lines.
+301. xyzzy to be combined with tracingedges.
+302. good3 entries should be 1 and 2 (not 1 and 0), to give desired symmetry.
+ Version -10.0 [July 17]
+303. had to add `new_if_limit' to `conditional'.
+304. `else' can be incomplete as well as `if' and `elseif'.
+305. new help "You can't redefine a parameter."
+306. print_edges should use cur_edges, because print_weight does.
+307. cull shouldn't round; it should use ordinary inequalities.
+308. obliterated variable left `0' token hanging in scan_primary.
+309. forgot recycle_value(p) in <Evoke...>.
+310. forgot to check improper culling amounts.
+311. `on' to be changed to `inwindow'.
+312. >=max_tfm_dimen should be simply >.
+ Version -9.0 [July 19]
+313. max_tfm_dimen needs to be decreased by design_size div 2^21.
+314. merge test data reporting into the screen display routines.
+315. use @# in printout at time of macro expansion tracing.
+316. bad recovery after "Incomplete string" error.
+317. @ should be usable as a parameter.
+318. spacing bad on `missing of' and `too many parameters' errors messages.
+319. keep track of largest str_ptr and pool_ptr values; showstats on two lines.
+ Version -8.0 [July 26] [introduced new test routines to study memory usage]
+320. make_pen doesn't recycle the knots
+321. flush_below often says free_node(2) on node of size 3.
+322. make_eq should recycle and free; do_equation shouldn't
+323. re #298 and #309: should also free the value nodes!
+324. the replacement text of a bad_var definition should be recycled.
+325. token lists left unrecycled in error recovery of addto, cull, display.
+ Version -7.0 [July 27]
+326. showall to put "(see the transcript file)" after >> if not tracingonline.
+327. remove redundant code: hex in print_the_digs; second l=1 in idlookup.
+328. procedures never called: b_open_in; round_scaled.
+329. need to zero cur_mod after back_expr in <Put the left bracket...>.
+330. <Print the coefficient...> now catches also numbers that round to 1.0.
+331. known_pair logic was obsolete; now it is massively simpler.
+332. fix_check_sum now zeroes the negative bytes; this simplifies other code.
+333. show_dependencies not to show parts of capsules.
+334. forgot to set fix_needed:=false!
+335. move fix_needed from add_or_subtract to dep_finish.
+336. test fix_needed in bilin1 and add_mult_dep.
+337. after back_expr in <Scan a mediation...>, forgot to unstash p.
+ Version 0.0 [July 28] [Hurray! The preliminary TRAP test is fully passed.]
+338. corrected blunder in definition of trans_spec type. (found by Karney)
+339. less extreme starting values of gf_min_x and its cousins.
+340. uninitialized left_curl was possible. (found by John Hobby)
+341. In <Reduce...straight line...>, need to test sign before the `div 3' ops.
+342. In pyth_add, remove `+2' when dividing by 4, to prevent overflow.
+343. "Not a variable" changed to "Not a suitable variable".
+344. known_pair forgot to reset cur_type.
+345. round and floor procedures need to work in the presence of huge arguments.
+346. major revision to allow independent variables to disappear silently:
+346a. delete null_path and <Declare the null value routines>.
+346b. delete the hard part of ring_delete, and delete <Scold...for using...>.
+346c. stash/unstash/flush_cur_exp routines to allow independent type.
+346d. print_exp to allow a ring that contains only capsules.
+346e. <Zero out...> becomes <Recycle an independent variable> (major new code).
+346f. stash_in needs code to handle the case cur_type=independent.
+346g. negation needs code to handle the case cur_type=independent.
+346h. take_part needs code to handle the case cur_type=independent.
+346i. do_binary needs code to handle either or both operands independent.
+346j. frac_mult needs code to handle the case cur_type=independent.
+346k. make_eq and try_eq too.
+346l. linear_eq needs to check if cur_exp=x and cur_type=independent.
+346m. linear_eq displays new dependency only when lhs is interesting.
+347. minor improvement for coding style/brevity: the "interesting" function.
+348. test `if t<independent' had been left out of print_exp (was dirty PASCAL).
+349. (begingroup save x;x+1000 endgroup,0) caused subtle bug during unsave.
+350. print_type(known) changed to "known numeric" for consistency.
+351. In <Change...to proto_dependent>, didn't watch for coefficients zeroed.
+352. check_mem now checks also the links in the dependency lists.
+353. null_pen initialization belongs in <Initialize tab...>, not <Set init...>.
+354. fix_dependencies has to worry about coefficients dropping to zero.
+355. add tracing to frac_mult.
+356. need cur_sym>0 at the time of end_read_terminal. (found by S Robinson)
+ Version 0.1 [August 30]
+357. Don't complain about winding_number<=0 when doing a doublepath.
+358. Need to test for max_coef=0 in offset_prep.
+359. Cusp detection should allow for more rounding error.
+360. Major change to path syntax, allowing {dir}..{dir} path joins.
+360a. `Fragments' disappear; new_fragment becomes new_knot.
+360b. print_path has simpler conventions.
+360c. future_path type disappears.
+360d. left_brace renumbered and removed from scan_tertiary.
+360e. some scan_tertiary code moves to new subroutine scan_direction.
+360f. scan_expression has a bunch of new code.
+361. scan_expression shouldn't have returned after cycle_hit.
+362. abort_path, scrap_knot logic removed from scan_expression.
+ Version 0.2 [September 12]
+363. new operator: makepath <pen primary>.
+364. define point/precontrol/postcontrol at endpoints and beyond. (JDH)
+365. union replaced by +; negation and subtraction allowed on edges.
+366. another new operator: totalweight <edges primary>. (JDH's idea)
+367. undelimited parameters may be preceded by an optional = or :=.
+368. base_area_length was wrong. (affects CH files)(found by Pavel)
+369. internal names to be allowable in suffixes.
+370. label not_found was misplaced in do_add_to. (found by PasMESA translator)
+371. progress report should be given at time of shipout.
+372. procedures make_choices, make_spec,fill_envelope split into shorter pieces.
+373. locals of cubic_intersection made global so that people could split it.
+ Version 0.3 [September 27]
+374. allow <future pen> ::= <pen> <transform>
+375. equation that's off by .0001 shouldn't be called inconsistent.
+376. bug in skew: must compute x_coord(q) and y_coord(q) exactly.
+377. right/left/up/down to be assigned to octants 1/5/2/6; was 1/4/2/7.
+378. boundedness to be allowed with any tension, and one-sidedly.
+379. eliminate coordinate nodes in path lists by passing angles instead (JDH).
+380. known_pair to free cur_exp after error msgs that might showdependencies.
+381. scan_primary sometimes accessed num before it had a value (found by Pavel).
+382. invalid characters should cause error message (suggested by PMB).
+ Version 0.4 [October 12]
+383. allow `0' in response to error prompts.
+384. put loop value on error context lines.
+385. change `ord' to `ASCII'.
+386. scantokens <string primary> to be permitted; readterminal goes away.
+387. expandafter <token> to be permitted.
+388. improved overflow conventions on make_fraction, make_scaled.
+389. variable=<value over 4096> should get its own error message.
+390. nice_pair first argument can be any integer.
+391. new feature, shortshow commands that don't pause.
+392. "harmless" bug in negate_edges, the sorted list can get out of sorts.
+393. ni too skimpy (15 instead of 63). (JDH)
+394. loop_ptr not initialized. (found by Paul Richards)
+395. bug in <Find the approximate type...>, q=null should goto done1. (Richards)
+396. empty_edges macro added for readability.
+397. x_reflect_edges might as well fix the offset as it goes.
+398. blunder: frozen_bad_vardef had the wrong eq_type.
+399. "name" changed to "tag".
+400. check_arith inserted after division of numeric tokens.
+401. stop_bit was not initialized in <Compile a ligature/kern...>.
+402. char 127 to be a string of length 1; slow_print added (therefore).
+403. pyth_sub added to the repertoire.
+404. minor change to calls on "confusion" in make_fraction, make_scaled. (JJW)
+ Version 0.5 [November 9]
+405. 4095.999999 should be erroneous.
+406. length <pair> to give the absolute value; argd changed to "angle".
+407. get_next changed to get_x_next wherever possible.
+408. z1..z2{curl 0} should be equivalent to z1..{curl 0}z2.
+409. scantokens "" should be fast.
+410. allow undelimited suffix and text parameters.
+411. warningcheck to allow large variable values passed over silently.
+412. "edges" changed to "picture".
+413. "winding" changed to "turning".
+414. <If consecutive knots...> had typo, "p" for "q".
+415. elliptical pens should have axis symmetry.
+416. slow_add introduced to catch arithmetic overflow on addition.
+417. add a turningnumber primitive.
+418. no carriage return between ">>" and "Edge structure...".
+419. install new memory management routines (from TeX version 1.3).
+420. generalized cull command. (suggested by Bruce Leban)
+421. bad goto: `x..' reports that x is vacuous, loses value of x.
+422. shortshows replaced by an internal variable "showstopping".
+423. major change, capsule tokens: lots of code and restrictions gone! (JDH)
+424. substring(a,b), subpath(a,b) to reverse the result when a>b.
+425. new GF format: z goes away. (suggested by ALS)
+426. another GF mod: chardw replaced by chardx/chardy, which are scaled. (PMB)
+427. make `&' have the same precedence always (not different for strings).
+428. wrong error message for 2"s" (bug introduced in change 369).
+429. variable l in show_context has to be declared type integer.
+430. bug in chop_path when both limits are large.
+ Version 0.6 [December 10]
+431. xoffset and yoffset to be added by shipout. [this affects GF comments]
+432. wake_up_terminal, not t_open_out, before "Ouch..." (Scott Robinson)
+433. undelimited suffix parameters to allow an optional delimiter, disallow =.
+434. showall... to be eliminated; tracingcapsules added.
+435. allow undelimited parameters after delimited ones.
+436. nullpen test in addto should test for any one-point pen.
+437. granularity parameter to affect autorounding (Bruce Leban)
+[this leads to major improvements suggested by JDH; lots of code changes!]
+[basically, make_spec is now responsible for autorounding; fill_spec just fills]
+438. node_size overflow problem must be prevented. (Paul Richards)
+439. allow unary + with a picture.
+440. nullpen should transform quickly to itself, if not shifted.
+441. print_edges should skip over null rows, if it doesn't already.
+442. open_base_file improved (following TeX's change number 302).
+443. output initialization improved (following TeX's change number 303).
+444. operators shouldn't fail because future pen isn't a pen. (JDH)
+445. intersection routine to quit in hard cases. (DRF)
+446. angle routine was off by epsilon on negative arguments. (Karney)
+447. octant assignment should look ahead further in ambiguous cases. (JDH)
+448. <Compute the incoming...> forgot that right_type(r)=endpoint could happen.
+449. turning_number calculated directly, not times 8.
+450. floor_scaled made more efficient by trading subtraction for multiplication.
+451. doublepath autorounding to compromise between east and west. (JDH)
+452. codes of picture_type and path_type interchanged, to simplify a "case".
+ Version 0.7 [January 17, 1985]
+453. def to allow both := and =. (NB)
+454. "keeping" and "dropping" instead of "including" and "excluding". (HWT)
+455. (see the transcript...): why, when tracingonline>0?
+456. dead cubics need to be removed also before xy_round. (NB)
+457. [re 433] I said get_x_next twice; a blunder.
+ Version 0.75 [January 23, 1985]
+458. Abbreviated boc command makes GF format more attractive for lowres. (GusF)
+ Version 0.76 [February 2]
+459. known and unknown might as well both be primitive.
+460. ab_vs_cd(a-d,b,c-b,d) simplifies to ab_vs_cd(a,b,c,d). (JDH)
+461. (from change 435) <Absorb undelimited...> must use k, not n.
+462. (from change 458) max_new_row added, since 165 wasn't updated to 164.
+ Version 0.77 [February 16]
+463. abbreviated version of tracingspecs is desired for turning number errors.
+464. improved octant number in pathological cases of quadrant_subdivide (JDH)
+465. xoffset,yoffset communicated to GF file when proofing>0.
+466. Poirot changed to Marple.
+467. reverse<cycle> starts the reversed path at the wrong time.
+468. Extra help given after `undefined coordinate' errors.
+469. Keep segment numbers from overflowing max_halfword. (JDH)
+470. Ditto for pen offset slope numbers.
+471. n_magic had min_halfword with wrong sign. (John Johnson)
+ Version 0.80 [April 1]
+472. In errhelp, % means <return>, %% means %. (BPL)
+473. tracing of envelopes should give symbolic octant names.
+474. (-x mod y) should be ((-x) mod y) in good_val.
+475. crossingpoint could overflow on 32-bit machines. (BPL)
+476. Improvements to abbreviated format of number 463.
+ Version 0.81 [April 4]
+477. `I hide(showdependencies)' in hint to become `I ???'.
+478. print_strange shouldn't wake up the terminal in scroll mode. (cf 463)
+479. flush_list procedure made a bit faster. (DRF)
+480. bounded velocities to be multiplied by .999 for safety.
+481. Detect error in declaration that tries to extend a vardef.
+482. print_strange puts turns in parentheses (cf 463).
+483. make_name_string shouldn't ever cause string space overflow.
+484. allow <picture primary> xscaled m yscaled n.
+485. checksum should consider values relative to designsize. (DRF)
+[I also increased max_strings to 2000.]
+
+[Note: Neenie found a case where missing pixels can occur, due to
+inconsistent rounding between left-to-right and right-to-left subdivision:
+pickup pencircle; draw
+(0,126)..controls (130.29451,126) and (288,126.00002)
+ ..(288,36)..controls (288,143.99998) and (132.97438,144)
+ ..(0,144)..controls (0,138) and (0,132)
+ ..cycle;
+The "right" way to fix this would be to develop a make_spec routine for
+non-cycles, and to copy a reversed spec, so that double paths would be
+guaranteed self-inverse. However, my later experiments showed that
+autorounding wasn't nearly as useful as I had expected it to be, hence I
+decided not to waste time beefing up this routine; no satisfactory
+automatic approach to rounding occurs to me.]
+ Version 0.90 [May 10]
+486. 180-degree turns to be clockwise for half of doublepath cycles.
+487. introduce fillin parameter.
+488. `numeric_type' moved before `known', so that print_exp works (Richards).
+489. numeric_type as unary op should include independent in its range (cf 346).
+ Version 0.91 [May 13]
+490. show_token_list gets null_tally parameter.
+491. if bad>0, change the code as in TeX changes 315, 316.
+492. batchmode change as in TeX 317.
+493. "and double autorounding" to be traced, if appropriate.
+494. introduce tracingrestores.
+ *`restoring a bad variable'?
+495. put decimal number into gf extension
+496. mediation put into scan_primary, so that round .5[a,b] works properly.
+497. cubic_intersection now has |tol| to compensate for rounding errors.
+498. find_direction_time needs to worry about degenerate case x3=y3=0.
+499. crossing_point(a,b,0) now yields fraction_one if a,b>0.
+500. diag_round shouldn't insist that b-a is even (see below).
+501. macro_call will show_macro with much larger limit.
+502. TFM file statistics are logged too.
+
+* Here's an example (found by Neenie) explaining bugfix 500:
+autorounding:=2; draw
+(7.03159,-9.31831)..controls (12.14732,-9.4386) and (17.4299,-6.82948)
+ ..(21.60132,0)..controls (27.47417,9.61507) and (37.87402,15.58647)
+ ..(43.74687,25.20154)..controls (51.16223,37.34201) and (57.31,50.171)
+ ..(63.45776,63.00002) withpen pencircle;
+The missing pixels are due to the following curious circumstances: (1) the
+middle cubic splits into three, when subdividing into octants; (2) the reverse
+curve splits too but with slightly different rounding, off by 1; (3) the old
+diag_round routine, when it ensures that b-a is even, computes aa=a+1 on
+one side of the curve, but aa=a-1 on the other side, thus making the
+rounding "safe" on only one side. (Namely, on one side we have consecutive
+values
+ b=1317666 a=1310720
+ b=1329617 a=1310721
+and on the other side,
+ b=-1329618 a=-1310720
+ b=-1317667 a=-1310721.)
+
+* Here's an example (by JDH) showing the necessary of bugfix 497:
+path p[];
+p1 = (1,0)..controls (1,0.10493) and (0.98347,0.20921)
+ ..(0.95105,0.30902)..controls (0.91862,0.40881) and (0.8707,0.50288)
+ ..(0.80902,0.58778);
+p2 = (0,0)..controls (0.63403,0.20601) and (1.26807,0.41203)
+ ..(1.9021,0.61804);
+show p1 intersectiontimes p2; % (-1,-1), without `tol'
+pickup pencircle;
+currenttransform := identity scaled 150;
+draw p1; draw p2;
+showit; % but they clearly intersect
+ Version 0.95 [Aug 12]
+503. `Se' changed to `SSE', etc. (suggested by RFS)
+504. Inconsisting diagonal rounding was introduced in bugfix 500.
+
+[the following lines from @<Determine the before...@> in diag_round:
+ if aa>bb then cc:=dd-half(aa-bb-1)@+else cc:=dd-half(aa-bb+1)
+ if a>b then c:=d-half(a-b-1)@+else c:=d-half(a-b+1)
+were replaced respectively by:
+ if right_type(p)>switch_x_and_y then cc:=dd-half(aa-bb+1)
+ else cc:=dd-half(aa-bb-1)
+ if right_type(p)>switch_x_and_y then c:=d-half(a-b-1)
+ else c:=d-half(a-b+1)
+in order to make the rounding consistent on adjacent octants;
+the following command caused `this can't happen':
+fill (33,7)..controls (32.63094,5.70827) and (32.06163,3.716)
+ ..(31.59488,3.36667)..controls (31.10509,3.00009) and (29.14824,3)
+ ..(27.85318,3)..controls (27.85286,3) and (23.84952,3)
+ ..(23.8492,3)..controls (23.73282,2) and (23.61646,1)
+ ..(23.50008,0)..controls (26.66672,0) and (29.83336,0)
+ ..(33,0)..controls (33.66667,2.33333) and (34.33333,4.66667)
+ ..(35,7)..controls (34.33333,7) and (33.66667,7)
+ ..cycle;]
+ Version 0.96 [Aug 17]
+505. allow comparison of transforms. [test by trying t=t when t is new...]
+506. {restoring...} shouldn't get through when tracingonline=0.
+507. better error message needed on `interim alpha:=0', `interim 0:=0'.
+508. make_op_def should put parameters in the other order.
+509. <Worry about...> should say get_x_next, not get_next.
+510. unif_rand to allow negative x, save on error messages.
+511. tidy up chop_string when b<0 and/or a>l.
+512. randomseed is automatically logged.
+513. change 495 messed up if gf file couldn't be opened. (found by DRF)
+[In INIMF.CH, 3-character extensions now have 0--9 or A--Z as leading digit.]
+ Version 0.97 [Sep 5]
+514. a[b,c] to be tried only if a is numeric type.
+515. simplified the code for length_op(string_type).
+516. citations of `Chapter xx' now replaced by correct references.
+517. <Log the subfile sizes...> to be in stat...tats version only.
+518. remove extra space after <insert> and <scantokens>.
+519. offsets should be included in the tracingoutput output.
+520. a vacuous expression should be considered known.
+521. charlist syntax changed from commas to colons.
+[also the WAITS version command-line reader was fixed to handle `^^W']
+ Version 0.99 [Sep 24]
+522. vardef's begingroup...endgroup should be accessible.
+523. allow an empty expression in for loops.
+ Version 0.999 [Oct 1]
+524. "charfam" changed to "charext".
+525. variable tt makes get_node slightly faster. (PROFILE suggests this)
+526. cur_cmd<=max should be tested before cur_cmd>=min. (PROFILE)
+527. fast_get_avail used in cur_tok and scan_primary. (PROFILE)
+ Version 0.9999 [Oct 27]
+528. Major change to representation of independent variables
+ ("serial number" added so that MF's equation solving doesn't depend
+ on vagaries of storage allocation)
+ [module number change: new module between old 585 and 586]
+ [in terms of new numbering, modules 232, 585--587, 589, 594, 597,
+ 601, 608, 610, 816--817, 855, 1198--1199 were affected]
+[I also reordered some of the cases in print_op, module 189,
+so that the program could be condensed in published version]
+ Version 0.99999 [Nov 13]
+529. forgot to decrease three_l in change 497!
+530. cubic_intersection to give approximation if max_patience exceeded.
+[the following example, found by NB, motivated this change:
+ show (
+ (36.00104,26.99951)..controls (36.00131,26.99979) and (36.00146,27.00017)
+ ..(36.00146,27.00055)
+ )intersectiontimes(
+ (36,27.00055)..controls (45.17148,21.12364) and (55.83588,18)
+ ..(66.72873,18)
+ );]
+ Version 0.999999 [Dec 2]
+531. hold_head needed (scan_toks calls cur_tok calls copy_edges). (D. Kosower)
+ Version 1.0 [January 4, 1986]
+532. Optimization in module 1174, suggested by LER (3 Feb 86)
+@x
+ if delta=0 then gf_out(skip0)
+ else begin gf_out(skip1); gf_out(delta);
+ end
+@y
+ begin gf_out(skip1); gf_out(delta);
+ end
+@z
+*** Late copies of version 1.0 include this correction;
+in particular, the program listing in Volume D was updated (with scissors)!
+ Changes subsequent to `Version 1.0' as published in C&T, Volume D:
+533. Inconsistent punctuation in user messages (found by Karl Berry, June 86)
+@x module 1134
+print_nl("Font metrics written on "); print(metric_file_name);
+@y
+print_nl("Font metrics written on "); print(metric_file_name); print_char(".");
+@z (that was installed in version 1.1)
+
+534. Possible arithmetic overflows (found by Klaus Guntermann, July 86)
+@x module 496
+while max_coef<fraction_one do
+@y
+while max_coef<fraction_half do
+@z (that was installed in version 1.2)
+
+535. Possible loop in nonstopmode (found by Chris Thompson, July 86)
+@x module 1206
+ @<Finish the \.{TFM} file@>;
+@y
+ internal[fontmaking]:=0; {avoid loop in case of fatal error}
+ @<Finish the \.{TFM} file@>;
+@z (that too was installed in version 1.2)
+
+536. Double rounding error should be avoided in make_ellipse (JDH, 22 Nov 86)
+@x module 533
+d:=take_fraction(d,delta);
+alpha:=abs(u); beta:=abs(v);
+if alpha<beta then
+ begin delta:=alpha; alpha:=beta; beta:=delta;
+ end; {now $\alpha=\max(\vert u\vert,\vert v\vert)$,
+ $\beta=\min(\vert u\vert,\vert v\vert)$}
+if internal[fillin]<>0 then d:=d-take_fraction(internal[fillin],beta+beta);
+d:=(d+4) div 8; alpha:=alpha div half_unit;
+@y
+alpha:=abs(u); beta:=abs(v);
+if alpha<beta then
+ begin alpha:=abs(v); beta:=abs(u);
+ end; {now $\alpha=\max(\vert u\vert,\vert v\vert)$,
+ $\beta=\min(\vert u\vert,\vert v\vert)$}
+if internal[fillin]<>0 then
+ d:=d-take_fraction(internal[fillin],make_fraction(beta+beta,delta));
+d:=take_fraction((d+4) div 8,delta); alpha:=alpha div half_unit;
+@z (That was the reason for version 1.3)
+
+537. Trivial change to a help message (version number is still 1.3).
+@x module 1086
+ ("Pretend that you're Miss Marple, examine all clues,")@/
+@y
+ ("Pretend that you're Miss Marple: Examine all clues,")@/
+@z
+
+538. Storage allocation can be more elegant and efficient (4/21/87)
+@x module 169
+if r=p then if ((rlink(p)<>rover) or (llink(p)<>rover)) then
+@y
+if r=p then if rlink(p)<>p then
+@z
+
+539. Unused variables can be eliminated. (Found by John Sauter, 5/5/87)
+@x module 158
+@<Glob...@>=
+@!temp_ptr:pointer; {a pointer variable for occasional emergency use}
+
+@y (I used it only in my change file!)
+@z
+@x module 280
+@!r,@!s,@!t:pointer; {registers for list traversal}
+@y
+@!s,@!t:pointer; {registers for list traversal}
+@z
+@x module 284
+@!sine,@!cosine:fraction; {trig functions of various angles}
+@y (the declarations in module 281 are correct, but in 284 they're superfluous)
+@z
+@x module 497
+var @!q,@!ww:pointer; {for list manipulation}
+@!du,@!dv:scaled; {for slope calculation}
+@!t0,@!t1,@!t2:integer; {test coefficients}
+@!t:fraction; {place where the derivative passes a critical slope}
+@!s:fraction; {slope or reciprocal slope}
+@!v:integer; {intermediate value for updating |x0..y2|}
+begin loop
+ begin q:=link(p); right_type(p):=k;
+@y
+var @!ww:pointer; {for list manipulation}
+@!du,@!dv:scaled; {for slope calculation}
+@!t0,@!t1,@!t2:integer; {test coefficients}
+@!t:fraction; {place where the derivative passes a critical slope}
+@!s:fraction; {slope or reciprocal slope}
+@!v:integer; {intermediate value for updating |x0..y2|}
+begin loop
+ begin right_type(p):=k;
+@z
+@x module 652
+@!s:0..param_size; {value of |param_start| on the current level}
+@y who knows why that line was there?
+@z
+@x module 862
+var @!p,@!q,@!r:pointer; {for list manipulation}
+@y
+var @!p:pointer; {for list manipulation}
+@z
+@x module 985
+@!vv:scaled; {initial value of |v|}
+@!q:pointer; {successor of |p|}
+begin vv:=v; p:=cur_exp;@/
+@y
+@!q:pointer; {successor of |p|}
+begin p:=cur_exp;@/
+@z
+@x module 1059
+@!t:small_number; {variant of |with_option|}
+@y
+@z
+
+540. Typo suppresses an error detection (Chris Thompson, 2May88)
+@x module 963
+ if txy mod unity=0 then if tyy mod unity=0 then
+@y
+ if txx mod unity=0 then if tyy mod unity=0 then
+@z
+
+541. get_x_token can lose a scanned declared variable (Chris Thompson, 4May88)
+@x module 1011
+if equiv(x)=null then new_root(x);
+@y
+@z
+@x module 1011
+done:scan_declared_variable:=h;
+@y
+done: if eq_type(x)<>tag_token then clear_symbol(x,false);
+if equiv(x)=null then new_root(x);
+scan_declared_variable:=h;
+@z
+
+542. Avoid negative divisor rounding upward (Chris Thompson, fixed 19Jun88)
+@x module 168
+else t:=(lo_mem_max+hi_mem_min+2) div 2; {|lo_mem_max+2<=t<hi_mem_min|}
+@y
+else t:=lo_mem_max+1+(hi_mem_min-lo_mem_max)div 2; {|lo_mem_max+2<=t<hi_mem_min|}
+@z
+
+543. Better strategy when near memory overflow (Chris Thompson)
+@x module 168
+begin if lo_mem_max+1000<hi_mem_min then t:=lo_mem_max+1000
+@y
+begin if hi_mem_min-lo_mem_max>=1998 then t:=lo_mem_max+1000
+@z
+
+544. Avoid fatal_error after terminal eof (Tim Morgan, reported 25Oct88)
+@x module 66 [serious problem occurred if this was called in open_log_file]
+if not input_ln(term_in,true) then fatal_error("End of file on the terminal!");
+@y
+if not input_ln(term_in,true) then t_open_in;
+@z
+
+545. Force terminal output when open_log_file aborts (6Nov88)
+@x module 789
+ begin print_err("I can't write on file `");
+@y
+ begin selector:=term_only; print_err("I can't write on file `");
+@z
+
+546. By popular request, undo #544 and fix the bug a more complex way.
+@x module 66 [this undoes change #544]
+if not input_ln(term_in,true) then t_open_in;
+@y
+if not input_ln(term_in,true) then fatal_error("End of file on the terminal!");
+@z
+@x module 87 [now we get to the new stuff]
+begin if job_name>0 then selector:=term_and_log
+@y
+begin if log_opened then selector:=term_and_log
+@z
+@x module 88
+ error;
+@y
+ if log_opened then error;
+@z
+@x module 782
+@!job_name:str_number; {principal file name}
+@y
+@!job_name:str_number; {principal file name}
+@!log_opened:boolean; {has the transcript file been opened?}
+@z
+@x module 783
+@<Initialize the output...@>=job_name:=0;
+@y
+@<Initialize the output...@>=job_name:=0; log_opened:=false;
+@z
+@x module 788
+selector:=log_only;
+@y
+selector:=log_only; log_opened:=true;
+@z
+@x module 789
+begin if interaction<scroll_mode then {bypass |fatal_error|}
+ begin selector:=term_only; print_err("I can't write on file `");
+@.I can't write on file x@>
+ print_file_name(cur_name,cur_area,cur_ext); print("'.");@/
+ job_name:=0; history:=fatal_error_stop; jump_out;
+ end; {abort the program without a log file}
+@y
+begin selector:=term_only;
+@z
+@x module 1023 [this change is optional, but it's a slight improvement]
+ if job_name<>0 then selector:=selector+2;
+@y
+ if log_opened then selector:=selector+2;
+@z
+@x module 1205
+if job_name>0 then
+@y
+if log_opened then
+@z
+@x module 1208
+if job_name>0 then {the log file is open}
+@y
+if log_opened then
+@z
+
+547. String startup problems corresponding to TeX change 355 (17 Jul 89)
+@x module 30 (Warning: This affects most change files!)
+ overflow("buffer size",buf_size);
+@:METAFONT capacity exceeded buffer size}{\quad buffer size@>
+@y
+ @<Report overflow of the input buffer, and abort@>;
+@z
+@x module 34
+consist of the remainder of the command line, after the part that invoked \MF.
+@y
+consist of the remainder of the command line, after the part that invoked \MF.
+
+The first line is special also because it may be read before \MF\ has
+input a base file. In such cases, normal error messages cannot yet
+be given. The following code uses concepts that will be explained later.
+
+@<Report overflow of the input buffer, and abort@>=
+if base_ident=0 then
+ begin write_ln(term_out,'Buffer size exceeded!'); goto final_end;
+@.Buffer size exceeded@>
+ end
+else begin cur_input.loc_field:=first; cur_input.limit_field:=last-1;
+ overflow("buffer size",buf_size);
+@:METAFONT capacity exceeded buffer size}{\quad buffer size@>
+ end
+@z
+@x module 1193
+k:=pool_ptr-4; undump_four_ASCII
+@y
+k:=pool_ptr-4; undump_four_ASCII;
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;@/
+max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr
+@z
+@x module 1204
+tini@/
+@y
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;@/
+max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr; fix_date_and_time;
+tini@/
+@z
+@x module 1204
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;@/
+max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr;@/
+@y
+@z
+
+548. Major changes to allow 8-bit input, cf. TeX82 #359 (11 Sep 89).
+@x module 18
+@!ASCII_code=0..127; {seven-bit numbers}
+@y
+@!ASCII_code=0..255; {eight-bit numbers}
+@z
+@x module 19
+@d last_text_char=127 {ordinal number of the largest element of |text_char|}
+
+@<Local variables for init...@>=
+@!i:0..last_text_char;
+@y
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Local variables for init...@>=
+@!i:integer;
+@z
+@x module 21
+xchr[0]:=' '; xchr[@'177]:=' ';
+ {ASCII codes 0 and |@'177| do not appear in text}
+@y
+@z
+@x module 22
+for i:=1 to @'37 do xchr[i]:=' ';
+@y changing ' ' to chr(i) here will allow all 8-bit characters to get in
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+@z
+@x module 23
+for i:=1 to @'176 do xord[xchr[i]]:=i;
+@y
+for i:=@'200 to @'377 do xord[xchr[i]]:=i;
+for i:=0 to @'176 do xord[xchr[i]]:=i;
+@z
+@x module 37
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+
+@ @<Glob...@>=
+@!str_pool:packed array[pool_pointer] of ASCII_code; {the characters}
+@y [OK to make si(#)==#-128 and so(#)==#+128 (without parens) in change files]
+Some \PASCAL\ compilers won't pack integers into a single byte unless the
+integers lie in the range |-128..127|. To accommodate such systems
+we access the string pool only via macros that can easily be redefined.
+
+@d si(#) == # {convert from |ASCII_code| to |packed_ASCII_code|}
+@d so(#) == # {convert from |packed_ASCII_code| to |ASCII_code|}
+
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+@!packed_ASCII_code = 0..255; {elements of |str_pool| array}
+
+@ @<Glob...@>=
+@!str_pool:packed array[pool_pointer] of packed_ASCII_code; {the characters}
+@z
+@x module 41
+begin str_pool[pool_ptr]:=#; incr(pool_ptr);
+@y
+begin str_pool[pool_ptr]:=si(#); incr(pool_ptr);
+@z
+@x module 45
+ begin if str_pool[j]<>buffer[k] then
+@y
+ begin if so(str_pool[j])<>buffer[k] then
+@z
+@x module 47
+var k,@!l:0..127; {small indices or counters}
+@y
+var k,@!l:0..255; {small indices or counters}
+@z
+@x module 47
+@<Make the first 128 strings@>;
+@y
+@<Make the first 256 strings@>;
+@z
+@x module 48
+@ @<Make the first 128...@>=
+for k:=0 to 127 do
+ begin if (@<Character |k| cannot be printed@>) then
+ begin append_char("^"); append_char("^");
+ if k<@'100 then append_char(k+@'100)
+ else append_char(k-@'100);
+@y
+@ @d app_lc_hex(#)==l:=#;
+ if l<10 then append_char(l+"0)@+else append_char(l-10+"a")
+
+@<Make the first 256...@>=
+for k:=0 to 255 do
+ begin if (@<Character |k| cannot be printed@>) then
+ begin append_char("^"); append_char("^");
+ if k<@'100 then append_char(k+@'100)
+ else if k<@'200 then append_char(k-@'100)
+ else begin app_lc_hex(k div 16); app_lc_hex(k mod 16);
+ end;
+@z
+@x module 59
+ begin print_char(str_pool[j]); incr(j);
+@y
+ begin print_char(so(str_pool[j])); incr(j);
+@z
+@x module 60
+ begin print(str_pool[j]); incr(j);
+@y
+ begin print(so(str_pool[j])); incr(j);
+@z
+@x module 85
+ begin if str_pool[j]<>"%" then print(str_pool[j])
+ else if j+1=str_start[err_help+1] then print_ln
+ else if str_pool[j+1]<>"%" then print_ln
+@y
+ begin if str_pool[j]<>si("%") then print(so(str_pool[j]))
+ else if j+1=str_start[err_help+1] then print_ln
+ else if str_pool[j+1]<>si("%") then print_ln
+@z
+@x module 199
+char_class[127]:=invalid_class;
+@y
+for k:=127 to 255 do char_class[k]:=invalid_class;
+@z
+@x module 200
+@d hash_is_full == (hash_used=1) {test if all positions are occupied}
+@d eq_type(#) == eqtb[#].lh {the current ``meaning'' of a symbolic token}
+@d equiv(#) == eqtb[#].rh {parametric part of a token's meaning}
+@d hash_base=129 {hashing actually starts here}
+@y [incidentally I fixed a bug re hash overflow here]
+@d eq_type(#) == eqtb[#].lh {the current ``meaning'' of a symbolic token}
+@d equiv(#) == eqtb[#].rh {parametric part of a token's meaning}
+@d hash_base=257 {hashing actually starts here}
+@d hash_is_full == (hash_used=hash_base) {are all positions occupied?}
+@z
+@x module 210
+for j:=0 to l-1 do buffer[j]:=str_pool[k+j];
+cur_sym:=id_lookup(0,l);@/
+if s>=128 then {we don't want to have the string twice}
+@y
+for j:=0 to l-1 do buffer[j]:=so(str_pool[k+j]);
+cur_sym:=id_lookup(0,l);@/
+if s>=256 then {we don't want to have the string twice}
+@z
+@x module 223
+begin c:=char_class[str_pool[str_start[r]]];
+@y
+begin c:=char_class[so(str_pool[str_start[r]])];
+@z
+@x module 717
+ begin buffer[first]:=str_pool[j]; incr(j); incr(first);
+@y
+ begin buffer[first]:=so(str_pool[j]); incr(j); incr(first);
+@z
+@x module 774
+for j:=str_start[a] to str_start[a+1]-1 do append_to_name(str_pool[j]);
+for j:=str_start[n] to str_start[n+1]-1 do append_to_name(str_pool[j]);
+for j:=str_start[e] to str_start[e+1]-1 do append_to_name(str_pool[j]);
+@y
+for j:=str_start[a] to str_start[a+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[n] to str_start[n+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[e] to str_start[e+1]-1 do append_to_name(so(str_pool[j]));
+@z
+@x module 912
+ else begin cur_exp:=round_unscaled(cur_exp) mod 128; cur_type:=string_type;
+ if cur_exp<0 then cur_exp:=cur_exp+128;
+@y
+ else begin cur_exp:=round_unscaled(cur_exp) mod 256; cur_type:=string_type;
+ if cur_exp<0 then cur_exp:=cur_exp+256;
+@z
+@x module 913
+ else n:=str_pool[str_start[cur_exp]]
+@y
+ else n:=so(str_pool[str_start[cur_exp]])
+@z
+@x ibid
+ begin m:=str_pool[k];
+@y
+ begin m:=so(str_pool[k]);
+@z
+@x module 976
+for k:=str_start[a] to str_start[a+1]-1 do append_char(str_pool[k]);
+for k:=str_start[b] to str_start[b+1]-1 do append_char(str_pool[k]);
+@y
+for k:=str_start[a] to str_start[a+1]-1 do append_char(so(str_pool[k]));
+for k:=str_start[b] to str_start[b+1]-1 do append_char(so(str_pool[k]));
+@z
+@x module 977
+ for k:=str_start[s]+b-1 downto str_start[s]+a do append_char(str_pool[k])
+else for k:=str_start[s]+a to str_start[s]+b-1 do append_char(str_pool[k]);
+@y
+ for k:=str_start[s]+b-1 downto str_start[s]+a do append_char(so(str_pool[k]))
+else for k:=str_start[s]+a to str_start[s]+b-1 do append_char(so(str_pool[k]));
+@z
+@x module 1103
+ begin c:=str_pool[str_start[cur_exp]]; goto found;
+@y
+ begin c:=so(str_pool[str_start[cur_exp]]); goto found;
+@z
+@x module 1160
+ for k:=str_start[s] to str_start[s+1]-1 do gf_out(str_pool[k]);
+ end;
+if t<>0 then for k:=str_start[t] to str_start[t+1]-1 do gf_out(str_pool[k]);
+@y
+ for k:=str_start[s] to str_start[s+1]-1 do gf_out(so(str_pool[k]));
+ end;
+if t<>0 then for k:=str_start[t] to str_start[t+1]-1 do gf_out(so(str_pool[k]));
+@z
+@x module 1192
+ w.b0:=str_pool[k]; w.b1:=str_pool[k+1];
+ w.b2:=str_pool[k+2]; w.b3:=str_pool[k+3];
+@y [often qi(so(x))=x, but not e.g. when "quarterwords" are two bytes]
+ w.b0:=qi(so(str_pool[k])); w.b1:=qi(so(str_pool[k+1]));
+ w.b2:=qi(so(str_pool[k+2])); w.b3:=qi(so(str_pool[k+3]));
+@z
+@x module 1193
+ str_pool[k]:=w.b0; str_pool[k+1]:=w.b1;
+ str_pool[k+2]:=w.b2; str_pool[k+3]:=w.b3
+@y
+ str_pool[k]:=si(qo(w.b0)); str_pool[k+1]:=si(qo(w.b1));
+ str_pool[k+2]:=si(qo(w.b2)); str_pool[k+3]:=si(qo(w.b3))
+@z
+
+549. Make ".base" more easily switchable (see TeX82 log #369).
+@x module 775 gets a new definition
+@y
+@d base_extension=".base" {the extension, as a \.{WEB} constant}
+@z now replace ".base" by base_extension in modules 784 and 1200.
+
+550. "This can't happen" happened because of nonmonotonic rounding
+ (bug found by Mark Eklof, fixed 7 Oct 89)
+@x module 411
+left_x(r):=x_coord(r);
+@y
+left_x(r):=x_coord(r);
+if right_x(p)>x_coord(r) then right_x(p):=x_coord(r);
+ {we always have |x_coord(p)<=right_x(p)|}
+@z
+@x ibid
+else if x_coord(r)>dest_x then x_coord(r):=dest_x;
+@y
+else begin if x_coord(r)>dest_x then
+ begin x_coord(r):=dest_x; left_x(r):=-x_coord(r); right_x(r):=x_coord(r);
+ end;
+ if left_x(q)>dest_x then left_x(q):=dest_x
+ else if left_x(q)<x_coord(r) then left_x(q):=x_coord(r);
+ end;
+@z
+@x module 412
+left_x(s):=x_coord(s);
+negate(x_coord(s)); right_x(s):=x_coord(s);
+negate(left_x(q));
+@y
+left_x(s):=x_coord(s); {now |x_coord(r)=right_x(r)<=left_x(s)|}
+if left_x(q)<dest_x then left_x(q):=-dest_x
+else if left_x(q)>x_coord(s) then left_x(q):=-x_coord(s)
+else negate(left_x(q));
+negate(x_coord(s)); right_x(s):=x_coord(s);
+@z
+@x module 415
+if x_coord(r)>dest_x then x_coord(r):=dest_x
+else if x_coord(r)<x_coord(pp) then x_coord(r):=x_coord(pp);
+if y_coord(r)<y_coord(pp) then y_coord(r):=y_coord(pp);
+left_y(r):=y_coord(r);
+@y
+if y_coord(r)<y_coord(pp) then y_coord(r):=y_coord(pp);
+left_y(r):=y_coord(r);
+if right_y(pp)>y_coord(r) then right_y(pp):=y_coord(r);
+ {we always have |y_coord(pp)<=right_y(pp)|}
+@z
+@x ibid
+else if y_coord(r)>dest_y then y_coord(r):=dest_y;
+@y
+else begin if y_coord(r)>dest_y then
+ begin y_coord(r):=dest_y; left_y(r):=-y_coord(r); right_y(r):=y_coord(r);
+ end;
+ if left_y(qq)>dest_y then left_y(qq):=dest_y
+ else if left_y(qq)<y_coord(r) then left_y(qq):=y_coord(r);
+ end;
+@z
+@x module 416
+if x_coord(s)>dest_x then x_coord(s):=dest_x
+else if x_coord(s)<x_coord(r) then x_coord(s):=x_coord(r);
+if y_coord(s)<dest_y then y_coord(s):=dest_y;
+if y_coord(s)<y_coord(r) then y_coord(s):=y_coord(r);
+right_type(s):=right_type(pp);
+left_y(s):=y_coord(s);
+negate(y_coord(s)); right_y(s):=y_coord(s);
+negate(left_y(qq));
+@y
+if y_coord(s)<dest_y then y_coord(s):=dest_y;
+if y_coord(s)<y_coord(r) then y_coord(s):=y_coord(r);
+right_type(s):=right_type(pp);
+left_y(s):=y_coord(s); {now |y_coord(r)=right_y(r)<=left_y(s)|}
+if left_y(qq)<dest_y then left_y(qq):=-dest_y
+else if left_y(qq)>y_coord(s) then left_y(qq):=-y_coord(s)
+else negate(left_y(qq));
+negate(y_coord(s)); right_y(s):=y_coord(s);
+@z
+@x module 424
+if y_coord(r)>dest_y then y_coord(r):=dest_y
+else if y_coord(r)<y_coord(p) then y_coord(r):=y_coord(p);
+if x_coord(r)<x_coord(p) then x_coord(r):=x_coord(p);
+left_x(r):=x_coord(r);@/
+y_coord(r):=y_coord(r)+x_coord(r); negate(x_coord(r));@/
+right_x(r):=x_coord(r); right_y(r):=right_y(r)-right_x(r);@/
+left_y(q):=left_y(q)+left_x(q); negate(left_x(q));@/
+dest_y:=dest_y+dest_x; negate(dest_x);
+@y
+if x_coord(p)+y_coord(r)>dest_x+dest_y then
+ begin y_coord(r):=dest_x+dest_y-x_coord(p);
+ if left_y(r)>y_coord(r) then
+ begin left_y(r):=y_coord(r);
+ if right_y(p)>y_coord(r) then right_y(p):=y_coord(r);
+ end;
+ end;
+if x_coord(r)<x_coord(p) then x_coord(r):=x_coord(p)
+else if x_coord(r)+y_coord(r)>dest_x+dest_y then
+ x_coord(r):=dest_x+dest_y-y_coord(r);
+left_x(r):=x_coord(r);
+if right_x(p)>x_coord(r) then right_x(p):=x_coord(r);
+ {we always have |x_coord(p)<=right_x(p)|}
+y_coord(r):=y_coord(r)+x_coord(r); right_y(r):=right_y(r)+x_coord(r);@/
+negate(x_coord(r)); right_x(r):=x_coord(r);@/
+left_y(q):=left_y(q)+left_x(q); negate(left_x(q));@/
+dest_y:=dest_y+dest_x; negate(dest_x);
+if right_y(r)>dest_y then right_y(r):=dest_y;
+if left_y(q)>dest_y then left_y(q):=dest_y
+else if left_y(q)<y_coord(r) then left_y(q):=y_coord(r);
+if right_y(r)>left_y(q) then right_y(r):=left_y(q);
+@z
+@x ibid
+else if x_coord(r)>dest_x then x_coord(r):=dest_x
+@y
+else begin if x_coord(r)>dest_x then
+ begin x_coord(r):=dest_x; left_x(r):=-x_coord(r); right_x(r):=x_coord(r);
+ end;
+ if left_x(q)>dest_x then left_x(q):=dest_x
+ else if left_x(q)<x_coord(r) then left_x(q):=x_coord(r);
+ end;
+@z
+@x module 425 is entirely replaced
+@y by the following code:
+@ @<Subdivide the cubic a second time with respect to $x'-y'$@>=
+begin split_cubic(r,t,dest_x,dest_y); s:=link(r);@/
+if x_coord(r)+y_coord(s)>dest_x+dest_y then
+ begin y_coord(s):=dest_x+dest_y-x_coord(r);
+ if left_y(s)>y_coord(s) then
+ begin left_y(s):=y_coord(s);
+ if right_y(r)>y_coord(s) then right_y(r):=y_coord(s);
+ end;
+ end;
+if x_coord(s)+y_coord(s)>dest_x+dest_y then x_coord(s):=dest_x+dest_y-y_coord(s)
+else begin if x_coord(s)<dest_x then x_coord(s):=dest_x;
+ if x_coord(s)<x_coord(r) then x_coord(s):=x_coord(r);
+ end;
+right_type(s):=right_type(p);
+left_x(s):=x_coord(s); {now |x_coord(r)=right_x(r)<=left_x(s)|}
+if left_x(q)<dest_x then
+ begin left_y(q):=left_y(q)+dest_x; left_x(q):=-dest_x;@+end
+else if left_x(q)>x_coord(s) then
+ begin left_y(q):=left_y(q)+x_coord(s); left_x(q):=-x_coord(s);@+end
+else begin left_y(q):=left_y(q)+left_x(q); negate(left_x(q));@+end;
+y_coord(s):=y_coord(s)+x_coord(s); right_y(s):=right_y(s)+x_coord(s);@/
+negate(x_coord(s)); right_x(s):=x_coord(s);@/
+dest_y:=dest_y+dest_x;
+if right_y(s)>dest_y then right_y(s):=dest_y;
+if left_y(q)>dest_y then left_y(q):=dest_y
+else if left_y(q)<y_coord(s) then left_y(q):=y_coord(s);
+if right_y(s)>left_y(q) then right_y(s):=left_y(q);
+end
+@z
+
+551. Major change for extended ligatures.
+@x module 11 (this may affect change files)
+@!lig_table_size=300; {maximum number of ligature/kern steps}
+@y
+@!lig_table_size=5000; {maximum number of ligature/kern steps, must be
+ at least 255 and at most 32510}
+@!max_kerns=500; {maximum number of distinct kern amounts}
+@z
+@x module 14 gets a new line of code
+@y
+if(lig_table_size<255)or(lig_table_size>32510)then bad:=7;
+@z
+@x module 186
+@d lig_kern_token=76 {the operators `\&{kern}' and `\.{=:}'}
+@d assignment=77 {the operator `\.{:=}'}
+@d colon=78 {the operator `\.:'}
+@#
+@d comma=79 {the operator `\.,'}
+@d end_of_statement==cur_cmd>comma
+@d semicolon=80 {the operator `\.;', must be |comma+1|}
+@d end_group=81 {end a group (\&{endgroup}), must be |semicolon+1|}
+@d stop=82 {end a job (\&{end}, \&{dump}), must be |end_group+1|}
+@y
+@d lig_kern_token=76
+ {the operators `\&{kern}' and `\.{=:}' and `\.{=:\char'174}, etc.}
+@d assignment=77 {the operator `\.{:=}'}
+@d skip_to=78 {the operation `\&{skipto}'}
+@d bchar_label=79 {the operator `\.{\char'174\char'174:}'}
+@d double_colon=80 {the operator `\.{::}'}
+@d colon=81 {the operator `\.:'}
+@#
+@d comma=82 {the operator `\.,', must be |colon+1|}
+@d end_of_statement==cur_cmd>comma
+@d semicolon=83 {the operator `\.;', must be |comma+1|}
+@d end_group=84 {end a group (\&{endgroup}), must be |semicolon+1|}
+@d stop=85 {end a job (\&{end}, \&{dump}), must be |end_group+1|}
+@z
+@x module 190
+@d max_given_internal=warning_check
+@y
+@d boundary_char=41 {the right boundary character for ligatures}
+@d max_given_internal=41
+@z
+@x module 192 gets two new lines of code
+@y
+primitive("boundarychar",internal_quantity,boundary_char);@/
+@!@:boundary_char_}{\&{boundarychar} primitive@>
+@z
+@x and module 193 gets one too
+@y
+int_name[boundary_char]:="boundarychar";
+@z
+@x and module 211 gets several
+@y (to be inserted in appropriate places)
+primitive("::",double_colon,0);
+@!@::: }{\.{::} primitive@>
+primitive("||:",bchar_label,0);
+@!@:::: }{\.{\char'174\char'174:} primitive@>
+primitive("skipto",skip_to,0);@/
+@!@:skip_to_}{\&{skipto} primitive@>
+@z
+@x as does module 212
+@y
+bchar_label:print("||:");
+double_colon:print("::");
+skip_to:print("skipto");
+@z
+@x module 1093 has new text (see TeX change 362 for module 545) and also this:
+@d stop_bit(#)==lig_kern[#].b0
+@d next_char(#)==lig_kern[#].b1
+@d op_bit(#)==lig_kern[#].b2
+@y
+@d skip_byte(#)==lig_kern[#].b0
+@d next_char(#)==lig_kern[#].b1
+@d op_byte(#)==lig_kern[#].b2
+@z
+@x module 1096 gets a new definition
+@y
+@d undefined_label==lig_table_size {an undefined local label}
+@z
+@x ...and some changed declarations
+@!char_remainder:array[eight_bits] of eight_bits; {the |remainder| byte}
+@!header_byte:array[1..header_size] of -1..255;
+ {bytes of the \.{TFM} header, or $-1$ if unset}
+@!lig_kern:array[0..lig_table_size] of four_quarters; {the ligature/kern table}
+@!nl:0..lig_table_size; {the number of ligature/kern steps so far}
+@!kern:array[eight_bits] of scaled; {distinct kerning amounts}
+@!nk:0..256; {the number of distinct kerns so far}
+@y
+@!char_remainder:array[eight_bits] of 0..lig_table_size; {the |remainder| byte}
+@!header_byte:array[1..header_size] of -1..255;
+ {bytes of the \.{TFM} header, or $-1$ if unset}
+@!lig_kern:array[0..lig_table_size] of four_quarters; {the ligature/kern table}
+@!nl:0..32767-256; {the number of ligature/kern steps so far}
+@!kern:array[0..max_kerns] of scaled; {distinct kerning amounts}
+@!nk:0..max_kerns; {the number of distinct kerns so far}
+@z
+@x ...and some new declarations
+@y
+@!skip_table:array[eight_bits] of 0..lig_table_size; {local label status}
+@!lk_started:boolean; {has there been a lig/kern step in this command yet?}
+@!bchar:integer; {right boundary character}
+@!bch_label:0..lig_table_size; {left boundary starting location}
+@!ll,@!lll:0..lig_table_size; {registers used for lig/kern processing}
+@!label_loc:array[0..256] of -1..lig_table_size; {lig/kern starting addresses}
+@!label_char:array[1..256] of eight_bits; {characters for |label_loc|}
+@!label_ptr:0..256; {highest position occupied in |label_loc|}
+@z
+@x module 1097
+ end;
+for k:=1 to header_size do header_byte[k]:=-1;
+bc:=255; ec:=0; nl:=0; nk:=0; ne:=0; np:=0;
+@y
+ skip_table[k]:=undefined_label;
+ end;
+for k:=1 to header_size do header_byte[k]:=-1;
+bc:=255; ec:=0; nl:=0; nk:=0; ne:=0; np:=0;@/
+internal[boundary_char]:=-unity;
+bch_label:=undefined_label;@/
+label_loc[0]:=-1; label_ptr:=0;
+@z
+@x module 1104
+procedure set_tag(@!c:eight_bits;@!t:small_number;@!r:eight_bits);
+begin if char_tag[c]=no_tag then
+ begin char_tag[c]:=t; char_remainder[c]:=r;
+@y
+procedure set_tag(@!c:halfword;@!t:small_number;@!r:halfword);
+begin if char_tag[c]=no_tag then
+ begin char_tag[c]:=t; char_remainder[c]:=r;
+ if t=lig_tag then
+ begin incr(label_ptr); label_loc[label_ptr]:=r; label_char[label_ptr]:=c;
+ end;
+@z
+@x module 1105
+if (c>" ")and(c<128) then print(c)
+@y
+if (c>" ")and(c<127) then print(c)
+else if c=256 then print("||")
+@z
+@x module 1106
+label continue;
+var @!c,@!cc:eight_bits; {character codes}
+@!k:0..256; {index into the |kern| array}
+@y
+label continue,done;
+var @!c,@!cc:0..256; {character codes}
+@!k:0..max_kerns; {index into the |kern| array}
+@z
+% also the previous code of module 1107 is inserted into 1106
+@x modules 1107--1111 are completely replaced
+@y by the following new code:
+@ @<Store a list of ligature/kern steps@>=
+begin lk_started:=false;
+continue: get_x_next;
+if(cur_cmd=skip_to)and lk_started then
+ @<Process a |skip_to| command and |goto done|@>;
+if cur_cmd=bchar_label then
+ begin c:=256; cur_cmd:=colon;@+end
+else begin back_input; c:=get_code;@+end;
+if(cur_cmd=colon)or(cur_cmd=double_colon)then
+ @<Record a label in a lig/kern subprogram and |goto continue|@>;
+if cur_cmd=lig_kern_token then @<Compile a ligature/kern command@>
+else begin print_err("Illegal ligtable step");
+@.Illegal ligtable step@>
+ help1("I was looking for `=:' or `kern' here.");
+ back_error; next_char(nl):=qi(0); op_byte(nl):=qi(0); rem_byte(nl):=qi(0);@/
+ skip_byte(nl):=stop_flag+1; {this specifies an unconditional stop}
+ end;
+if nl=lig_table_size then overflow("ligtable size",lig_table_size);
+@:METAFONT capacity exceeded ligtable size}{\quad ligtable size@>
+incr(nl);
+if cur_cmd=comma then goto continue;
+if skip_byte(nl-1)<stop_flag then skip_byte(nl-1):=stop_flag;
+done:end
+
+@ @<Put each...@>=
+primitive("=:",lig_kern_token,0);
+@!@:=:_}{\.{=:} primitive@>
+primitive("=:|",lig_kern_token,1);
+@!@:=:/_}{\.{=:\char'174} primitive@>
+primitive("=:|>",lig_kern_token,5);
+@!@:=:/>_}{\.{=:\char'174>} primitive@>
+primitive("|=:",lig_kern_token,2);
+@!@:=:/_}{\.{\char'174=:} primitive@>
+primitive("|=:>",lig_kern_token,6);
+@!@:=:/>_}{\.{\char'174=:>} primitive@>
+primitive("|=:|",lig_kern_token,3);
+@!@:=:/_}{\.{\char'174=:\char'174} primitive@>
+primitive("|=:|>",lig_kern_token,7);
+@!@:=:/>_}{\.{\char'174=:\char'174>} primitive@>
+primitive("|=:|>>",lig_kern_token,11);
+@!@:=:/>_}{\.{\char'174=:\char'174>>} primitive@>
+primitive("kern",lig_kern_token,128);
+@!@:kern_}{\&{kern} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+lig_kern_token: case m of
+0:print("=:");
+1:print("=:|");
+2:print("|=:");
+3:print("|=:|");
+5:print("=:|>");
+6:print("|=:>");
+7:print("|=:|>");
+11:print("|=:|>>");
+othercases print("kern")
+endcases;
+
+@ Local labels are implemented by maintaining the |skip_table| array,
+where |skip_table[c]| is either |undefined_label| or the address of the
+most recent lig/kern instruction that skips to local label~|c|. In the
+latter case, the |skip_byte| in that instruction will (temporarily)
+be zero if there were no prior skips to this label, or it will be the
+distance to the prior skip.
+
+We may need to cancel skips that span more than 127 lig/kern steps.
+
+@d cancel_skips(#)==ll:=#;
+ repeat lll:=qo(skip_byte(ll)); skip_byte(ll):=stop_flag; ll:=ll-lll;
+ until lll=0
+@d skip_error(#)==begin print_err("Too far to skip");
+@.Too far to skip@>
+ help1("At most 127 lig/kern steps can separate skipto1 from 1::.");
+ error; cancel_skips(#);
+ end
+
+@<Process a |skip_to| command and |goto done|@>=
+begin c:=get_code;
+if nl-skip_table[c]>128 then {|skip_table[c]<<nl<=undefined_label|}
+ begin skip_error(skip_table[c]); skip_table[c]:=undefined_label;
+ end;
+if skip_table[c]=undefined_label then skip_byte(nl-1):=qi(0)
+else skip_byte(nl-1):=qi(nl-skip_table[c]-1);
+skip_table[c]:=nl-1; goto done;
+end
+
+@ @<Record a label in a lig/kern subprogram and |goto continue|@>=
+begin if cur_cmd=colon then
+ if c=256 then bch_label:=nl
+ else set_tag(c,lig_tag,nl)
+else if skip_table[c]<undefined_label then
+ begin ll:=skip_table[c]; skip_table[c]:=undefined_label;
+ repeat lll:=qo(skip_byte(ll));
+ if nl-ll>128 then
+ begin skip_error(ll); goto continue;
+ end;
+ skip_byte(ll):=qi(nl-ll-1); ll:=ll-lll;
+ until lll=0;
+ end;
+goto continue;
+end
+@x module 1112
+next_char(nl):=qi(c); op_bit(nl):=qi(cur_mod); stop_bit(nl):=qi(0);
+if cur_mod=0 then rem_byte(nl):=qi(get_code)
+@y
+begin next_char(nl):=qi(c); skip_byte(nl):=qi(0);
+if cur_mod<128 then {ligature op}
+ begin op_byte(nl):=qi(cur_mod); rem_byte(nl):=qi(get_code);
+ end
+@z
+@x ibid
+ begin if nk=256 then overflow("kern",256);
+@:METAFONT capacity exceeded kern}{\quad kern@>
+ incr(nk);
+ end;
+ rem_byte(nl):=qi(k);
+ end
+@y
+ begin if nk=max_kerns then overflow("kern",max_kerns);
+@:METAFONT capacity exceeded kern}{\quad kern@>
+ incr(nk);
+ end;
+ op_byte(nl):=kern_flag+(k div 256);
+ rem_byte(nl):=qi((k mod 256));
+ end;
+lk_started:=true;
+end
+@z
+@x module 1135
+tfm_two(6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np);
+ {this is the total number of file words that will be output}
+tfm_two(lh); tfm_two(bc); tfm_two(ec); tfm_two(nw); tfm_two(nh);
+tfm_two(nd); tfm_two(ni); tfm_two(nl); tfm_two(nk); tfm_two(ne); tfm_two(np);
+@y
+@<Compute the ligature/kern program offset and implant the
+ left boundary label@>;
+tfm_two(6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+lk_offset+nk+ne+np);
+ {this is the total number of file words that will be output}
+tfm_two(lh); tfm_two(bc); tfm_two(ec); tfm_two(nw); tfm_two(nh);
+tfm_two(nd); tfm_two(ni); tfm_two(nl+lk_offset); tfm_two(nk); tfm_two(ne);
+tfm_two(np);
+@z
+% modules 1137&1138 are combined into a single module
+% and so are modules 1140--1141, to make room for two new modules.
+@x module 1136 is moved to after old module 1141
+@y and changed to the following code:
+@ @<Log the subfile sizes of the \.{TFM} file@>=
+if bch_label<undefined_label then decr(nl);
+wlog_ln('(You used ',nw:1,'w,',@| nh:1,'h,',@| nd:1,'d,',@| ni:1,'i,',@|
+ nl:1,'l,',@| nk:1,'k,',@| ne:1,'e,',@|
+ np:1,'p metric file positions');
+wlog_ln(' out of ',@| '256w,16h,16d,64i,',@|
+ lig_table_size:1,'l,',max_kerns:1,'k,256e,',@|
+ max_font_dimen:1,'p)');
+end
+@z
+@x module 1139 is completely replaced
+@y by the following three modules:
+@ We need to output special instructions at the beginning of the
+|lig_kern| array in order to specify the right boundary character
+and/or to handle starting addresses that exceed 255. The |label_loc|
+and |label_char| arrays have been set up to record all the
+starting addesses; we have $-1=|label_loc|[0]<|label_loc|[1]\le\cdots
+\le|label_loc|[|label_ptr]|$.
+
+@<Compute the ligature/kern program offset...@>=
+bchar:=round_unscaled(internal[boundary_char]);
+if(bchar<0)or(bchar>255)then
+ begin bchar:=-1; lk_started:=false; lk_offset:=0;@+end
+else begin lk_started:=true; lk_offset:=1;@+end;
+@<Find the minimum |lk_offset| and adjust all remainders@>;
+if bch_label<undefined_label then
+ begin skip_byte(nl):=qi(255); next_char(nl):=qi(0);
+ op_byte(nl):=qi(((bch_label+lk_offset)div 256));
+ rem_byte(nl):=qi(((bch_label+lk_offset)mod 256));
+ incr(nl); {possibly |nl=lig_table_size+1|}
+ end
+
+@ @<Find the minimum |lk_offset|...@>=
+k:=label_ptr; {pointer to the largest unallocated label}
+if label_loc[k]+lk_offset>255 then
+ begin lk_offset:=0; lk_started:=false; {location 0 can do double duty}
+ repeat char_remainder[label_char[k]]:=lk_offset;
+ while label_loc[k-1]=label_loc[k] do
+ begin decr(k); char_remainder[label_char[k]]:=lk_offset;
+ end;
+ incr(lk_offset); decr(k);
+ until lk_offset+label_loc[k]<256;
+ {N.B.: |lk_offset=256| satisfies this when |k=0|}
+ end;
+if lk_offset>0 then
+ while k>0 do
+ begin char_remainder[label_char[k]]
+ :=char_remainder[label_char[k]]+lk_offset;
+ decr(k);
+ end
+
+@ @<Output the ligature/kern program@>=
+for k:=0 to 255 do if skip_table[k]<undefined_label then
+ begin print_nl("(local label "); print_int(k); print(":: was missing)");
+@.local label l:: was missing@>
+ cancel_skips(skip_table[k]);
+ end;
+if lk_started then {|lk_offset=1| for the special |bchar|}
+ begin tfm_out(255); tfm_out(bchar); tfm_two(0);
+ end
+else for k:=1 to lk_offset do {output the redirection specs}
+ begin ll:=label_loc[label_ptr];
+ if bchar<0 then
+ begin tfm_out(254); tfm_out(0);
+ end
+ else begin tfm_out(255); tfm_out(bchar);
+ end;
+ tfm_two(ll+lk_offset);
+ repeat decr(label_ptr);
+ until label_loc[label_ptr]<ll;
+ end;
+for k:=0 to nl-1 do tfm_qqqq(lig_kern[k]);
+for k:=0 to nk-1 do tfm_four(dimen_out(kern[k]))
+@z
+@x module 1205 gets a new declaration
+@y
+@!lk_offset:0..256; {extra words inserted at beginning of |lig_kern| array}
+@z
+
+552. Improved the covering/shortening routines. (18 Dec 89)
+@x module 1119 gets a new global variable
+@y
+@!excess:integer; {the list is this much too long}
+@z
+@x module 1120 gives it a value
+begin if min_cover(0)<=m then threshold:=0
+@y
+begin excess:=min_cover(0)-m;
+if excess<=0 then threshold:=0
+@z
+@x and module 1122 uses it
+begin repeat p:=link(p); info(p):=m;
+@y
+begin repeat p:=link(p); info(p):=m;
+decr(excess);@+if excess=0 then d:=0;
+@z
+@x and module 1122 also avoids overflow/ambiguous halving
+v:=half(l+value(p));
+@y
+v:=l+half(value(p)-l);
+@z
+
+553. Global variable used prematurely in do_add_to (Alan Jeffrey, 9 Feb 90)
+@x module 403
+@!cur_path_type:double_path_code..also_code; {likewise}
+@y
+@!cur_path_type:double_path_code..contour_code; {likewise}
+@z
+@x module 1059
+begin get_x_next; var_flag:=thing_to_add; scan_primary;
+if cur_type<>token_list then
+ @<Abandon edges command because there's no variable@>
+else begin lhs:=cur_exp; cur_path_type:=cur_mod;@/
+ cur_type:=vacuous; get_x_next; scan_expression;
+ if cur_path_type=also_code then @<Augment some edges by others@>
+@y
+@!add_to_type:double_path_code..also_code; {modifier of \&{addto}}
+begin get_x_next; var_flag:=thing_to_add; scan_primary;
+if cur_type<>token_list then
+ @<Abandon edges command because there's no variable@>
+else begin lhs:=cur_exp; add_to_type:=cur_mod;@/
+ cur_type:=vacuous; get_x_next; scan_expression;
+ if add_to_type=also_code then @<Augment some edges by others@>
+@z
+@x module 1064
+else begin lhs:=null;
+@y
+else begin lhs:=null; cur_path_type:=add_to_type;
+@z
+
+554. Balance the parens showing on the terminal (for Lispers).
+@x module 631
+@!in_open : 0..max_in_open; {the number of lines in the buffer, less one}
+@y
+@!in_open : 0..max_in_open; {the number of lines in the buffer, less one}
+@!open_parens : 0..max_in_open; {the number of open text files}
+@z
+@x module 657
+in_open:=0; max_buf_stack:=0;
+@y
+in_open:=0; open_parens:=0; max_buf_stack:=0;
+@z
+@x module 681
+ begin print_char(")"); force_eof:=false;
+ update_terminal; {show user that file has been read}
+@y
+ begin print_char(")"); decr(open_parens);
+ update_terminal; {show user that file has been read}
+ force_eof:=false;
+@z
+@x module 793
+print_char("("); print(name); update_terminal;
+@y
+print_char("("); incr(open_parens); print(name); update_terminal;
+@z
+@x module 1209
+if job_name=0 then open_log_file;
+@y
+if job_name=0 then open_log_file;
+while open_parens>0 do
+ begin print(" )"); decr(open_parens);
+ end;
+@z
+
+-----------Here I draw the line with respect to further changes
+
+555. Don't try system area if an area was given (see tex82.bug number 312;
+found by Jonathan Kew, May 1990)
+@x module 793
+ pack_file_name(cur_name,MF_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+@y
+ if cur_area="" then
+ begin pack_file_name(cur_name,MF_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+ end;
+@z
+
+556. Report correct line number when buffer overflows (CET, Jul 90).
+@x module 794
+begin if not input_ln(cur_file,false) then do_nothing;
+firm_up_the_line;
+buffer[limit]:="%"; first:=limit+1; loc:=start; line:=1;
+@y
+begin line:=1;
+if input_ln(cur_file,false) then do_nothing;
+firm_up_the_line;
+buffer[limit]:="%"; first:=limit+1; loc:=start;
+@z
+
+557. TeX82 bug 308, corrected in 1985, is also in MF! (Lutz Birkhahn, May91)
+@x module 176
+var_used:=lo_mem_stat_max+1-mem_min; dyn_used:=mem_top+1-hi_mem_stat_min;
+@y
+var_used:=lo_mem_stat_max+1-mem_min; dyn_used:=mem_top+1-hi_mem_min;
+@z
+
+558. Allow unprintable file names, as in TeX change 396 (19 Sep 91)
+(Much of this is redundant except for people who make nonportable
+versions that allow other 8-bit codes to be in variable names---and for
+those people only if they allow input of more codes than they can print!
+Still, it seems best to make the programs for TeX and MF as alike as possible.)
+@x module 59
+j:=str_start[s];
+while j<str_start[s+1] do
+ begin print_char(so(str_pool[j])); incr(j);
+ end;
+@y
+if (s<256)and(selector>pseudo) then print_char(s)
+else begin j:=str_start[s];
+ while j<str_start[s+1] do
+ begin print_char(so(str_pool[j])); incr(j);
+ end;
+ end;
+@z
+@x module 60
+j:=str_start[s];
+while j<str_start[s+1] do
+ begin print(so(str_pool[j])); incr(j);
+ end;
+@y
+if (s<256)and(selector>pseudo) then print_char(s)
+else begin j:=str_start[s];
+ while j<str_start[s+1] do
+ begin print(so(str_pool[j])); incr(j);
+ end;
+ end;
+@z
+@x module 61
+else begin print(base_ident); print_ln;
+@y
+else begin slow_print(base_ident); print_ln;
+@z
+@x module 79
+ print(input_stack[file_ptr].name_field);
+@y
+ slow_print(input_stack[file_ptr].name_field);
+@z
+@x module 223
+print(r);
+@y
+slow_print(r);
+@z
+@x module 254
+ print(int_name[q-(hash_end)]); print_char("=");
+@y
+ slow_print(int_name[q-(hash_end)]); print_char("=");
+@z
+@x module 254 again
+ print(text(q)); print_char("}");
+@y
+ slow_print(text(q)); print_char("}");
+@z
+@x module 638
+ if name<>null then print(text(name))
+@y
+ if name<>null then slow_print(text(name))
+@z
+@x module 664
+ if scanner_status=op_defining then print(text(warning_info))
+@y
+ if scanner_status=op_defining then slow_print(text(warning_info))
+@z
+@x module 664 again
+loop_defining: begin print("the text of a "); print(text(warning_info));
+@y
+loop_defining: begin print("the text of a "); slow_print(text(warning_info));
+@z
+@x module 722
+begin if n<>null then print(text(n))
+@y
+begin if n<>null then slow_print(text(n))
+@z
+@x module 722 again
+ if p=null then print(text(info(info(link(a)))))
+@y
+ if p=null then slow_print(text(info(info(link(a)))))
+@z
+@x module 725
+ print_nl(" Missing `"); print(text(r_delim));
+@y
+ print_nl(" Missing `"); slow_print(text(r_delim));
+@z
+@x module 773
+begin print(a); print(n); print(e);
+@y
+begin slow_print(a); slow_print(n); slow_print(e);
+@z
+@x module 790
+print(base_ident); print(" ");
+@y
+slow_print(base_ident); print(" ");
+@z
+@x module 793
+print_char("("); incr(open_parens); print(name); update_terminal;
+@y
+print_char("("); incr(open_parens); slow_print(name); update_terminal;
+@z
+@x module 998
+if info(lhs)>hash_end then print(int_name[info(lhs)-(hash_end)])
+@y
+if info(lhs)>hash_end then slow_print(int_name[info(lhs)-(hash_end)])
+@z
+@x module 999
+ print(int_name[info(lhs)-(hash_end)]);
+@y
+ slow_print(int_name[info(lhs)-(hash_end)]);
+@z
+@x module 1032
+else begin print_err("The token `"); print(text(r_delim));
+@y
+else begin print_err("The token `"); slow_print(text(r_delim));
+@z
+@x module 1034
+ else print(text(cur_sym));
+@y
+ else slow_print(text(cur_sym));
+@z
+@x module 1041
+else begin print(text(cur_sym)); print_char("=");
+@y
+else begin slow_print(text(cur_sym)); print_char("=");
+@z
+@x module 1042
+else begin print_char(""""); print(cur_mod); print_char("""");
+@y
+else begin print_char(""""); slow_print(cur_mod); print_char("""");
+@z
+@x module 1043
+ print("t delimiter that matches "); print(text(m));
+@y
+ print("t delimiter that matches "); slow_print(text(m));
+@z
+@x module 1043 again
+internal_quantity:print(int_name[m]);
+@y
+internal_quantity:slow_print(int_name[m]);
+@z
+@x module 1134
+print_nl("Font metrics written on "); print(metric_file_name); print_char(".");
+@y
+print_nl("Font metrics written on "); slow_print(metric_file_name);
+print_char(".");
+@z
+@x module 1182
+print_nl("Output written on "); print(output_file_name);
+@y
+print_nl("Output written on "); slow_print(output_file_name);
+@z
+@x module 1200
+print(w_make_name_string(base_file)); flush_string(str_ptr-1);
+print_nl(base_ident)
+@y
+slow_print(w_make_name_string(base_file)); flush_string(str_ptr-1);
+print_nl(""); slow_print(base_ident)
+@z
+@x module 1205
+ print(log_name); print_char(".");
+@y
+ slow_print(log_name); print_char(".");
+@z
+@x module 1213
+10: print(n);
+@y
+10: slow_print(n);
+@z
+
+------ The third printing of Volume D (October 1991) incorporated the above.
+
+559. Debugging routine never used since change 528; needed update then (JDH).
+@x module 617
+ p:=dep_list(p); r:=hi_mem_min;
+ repeat if info(p)>=r then
+@y
+ p:=dep_list(p); r:=inf_val;
+ repeat if value(info(p))>=value(r) then
+@z
+
+560. Corrections to blunders in change 550 (noted by B Jackowski, Jul 91).
+@x module 415 can use more paranoia even though no problems have occurred yet
+negate(left_y(qq)); negate(dest_y);@/
+@y
+negate(left_y(qq)); negate(dest_y);@/
+if x_coord(r)<x_coord(pp) then x_coord(r):=x_coord(pp)
+else if x_coord(r)>dest_x then x_coord(r):=dest_x;
+if left_x(r)>x_coord(r) then
+ begin left_x(r):=x_coord(r);
+ if right_x(pp)>x_coord(r) then right_x(pp):=x_coord(r);
+ end;
+if right_x(r)<x_coord(r) then
+ begin right_x(r):=x_coord(r);
+ if left_x(qq)<x_coord(r) then left_x(qq):=x_coord(r);
+ end;
+@z
+@x module 416 similarly
+negate(y_coord(s)); right_y(s):=y_coord(s);
+@y
+negate(y_coord(s)); right_y(s):=y_coord(s);
+if x_coord(s)<x_coord(r) then x_coord(s):=x_coord(r)
+else if x_coord(s)>dest_x then x_coord(s):=dest_x;
+if left_x(s)>x_coord(s) then
+ begin left_x(s):=x_coord(s);
+ if right_x(r)>x_coord(s) then right_x(r):=x_coord(s);
+ end;
+if right_x(s)<x_coord(s) then
+ begin right_x(s):=x_coord(s);
+ if left_x(qq)<x_coord(s) then left_x(qq):=x_coord(s);
+ end;
+@z
+@x module 424 similarly
+if x_coord(p)+y_coord(r)>dest_x+dest_y then
+ begin y_coord(r):=dest_x+dest_y-x_coord(p);
+ if left_y(r)>y_coord(r) then
+ begin left_y(r):=y_coord(r);
+ if right_y(p)>y_coord(r) then right_y(p):=y_coord(r);
+ end;
+ end;
+@y
+if y_coord(r)<y_coord(p) then y_coord(r):=y_coord(p)
+else if y_coord(r)>dest_y then y_coord(r):=dest_y;
+if x_coord(p)+y_coord(r)>dest_x+dest_y then
+ y_coord(r):=dest_x+dest_y-x_coord(p);
+if left_y(r)>y_coord(r) then
+ begin left_y(r):=y_coord(r);
+ if right_y(p)>y_coord(r) then right_y(p):=y_coord(r);
+ end;
+if right_y(r)<y_coord(r) then
+ begin right_y(r):=y_coord(r);
+ if left_y(q)<y_coord(r) then left_y(q):=y_coord(r);
+ end;
+@z
+@x in module 424, here's the serious bug introduced in change #550.
+if right_y(r)>dest_y then right_y(r):=dest_y;
+if left_y(q)>dest_y then left_y(q):=dest_y
+else if left_y(q)<y_coord(r) then left_y(q):=y_coord(r);
+if right_y(r)>left_y(q) then right_y(r):=left_y(q);
+@y
+if right_y(r)<y_coord(r) then
+ begin right_y(r):=y_coord(r);
+ if left_y(q)<y_coord(r) then left_y(q):=y_coord(r);
+ end;
+@z
+@x module 425 is analogous
+if x_coord(r)+y_coord(s)>dest_x+dest_y then
+ begin y_coord(s):=dest_x+dest_y-x_coord(r);
+ if left_y(s)>y_coord(s) then
+ begin left_y(s):=y_coord(s);
+ if right_y(r)>y_coord(s) then right_y(r):=y_coord(s);
+ end;
+ end;
+@y
+if y_coord(s)<y_coord(r) then y_coord(s):=y_coord(r)
+else if y_coord(s)>dest_y then y_coord(s):=dest_y;
+if x_coord(r)+y_coord(s)>dest_x+dest_y then
+ y_coord(s):=dest_x+dest_y-x_coord(r);
+if left_y(s)>y_coord(s) then
+ begin left_y(s):=y_coord(s);
+ if right_y(r)>y_coord(s) then right_y(r):=y_coord(s);
+ end;
+if right_y(s)<y_coord(s) then
+ begin right_y(s):=y_coord(s);
+ if left_y(q)<y_coord(s) then left_y(q):=y_coord(s);
+ end;
+@z
+@x module 425, the analogous bug
+dest_y:=dest_y+dest_x;
+if right_y(s)>dest_y then right_y(s):=dest_y;
+if left_y(q)>dest_y then left_y(q):=dest_y
+else if left_y(q)<y_coord(s) then left_y(q):=y_coord(s);
+if right_y(s)>left_y(q) then right_y(s):=left_y(q);
+@y
+if right_y(s)<y_coord(s) then
+ begin right_y(s):=y_coord(s);
+ if left_y(q)<y_coord(s) then left_y(q):=y_coord(s);
+ end;
+@z
+
+561. serious longstanding bug caused overflows and bad rounding (1 Mar 95)
+@x module 815
+begin if (max_c[dependent]>=fraction_one)or@|
+ (max_c[dependent] div @'10000 >= max_c[proto_dependent]) then
+@y
+begin if (max_c[dependent] div @'10000 >=
+ max_c[proto_dependent]) then
+@z
+
+562. final cleanup should not retain spurious reference counts (20 Mar 95)
+@x module 1209
+while open_parens>0 do
+@y
+while input_ptr>0 do
+ if token_state then end_token_list@+else end_file_reading;
+while loop_ptr<>null do stop_iteration;
+while open_parens>0 do
+@z
+@x module 1209
+ cur_if:=name_type(cond_ptr); cond_ptr:=link(cond_ptr);
+@y
+ cur_if:=name_type(cond_ptr); loop_ptr:=cond_ptr;
+ cond_ptr:=link(cond_ptr); free_node(loop_ptr,if_node_size);
+@z
+
+563. unprintable strings of length 1 need to be truly length 1 (27 Nov 95,
+pointed out by Ulrik Vieth)
+@x module 671
+ if loc=k+1 then cur_mod:=buffer[k]
+@y
+ if (loc=k+1) and (length(buffer[k])=1) then cur_mod:=buffer[k]
+@z
+
+564. minor improvement to pythagorean addition (22 Jan 97)
+@x module 124
+if a>0 then
+@y
+if b>0 then
+@z
+
+565. we need not raise hackles about two-digit years (23 Nov 98)
+@x module 1200
+print_int(round_unscaled(internal[year]) mod 100); print_char(".");
+@y
+print_int(round_unscaled(internal[year])); print_char(".");
+@z
+
+566. "this can't happen" can happen if coordinates are half vast
+ (Julian Gilbey, January 2001)
+@x module 402
+@!chopped:boolean; {have we truncated any of the data?}
+@y
+@!chopped:integer; {positive if data truncated,
+ negative if data dangerously large}
+@z
+@x module 402
+if internal[autorounding]>0 then xy_round;
+octant_subdivide; {complete the subdivision}
+if internal[autorounding]>unity then diag_round;
+@<Remove dead cubics@>;
+@<Insert octant boundaries and compute the turning number@>;
+while left_type(cur_spec)<>endpoint do cur_spec:=link(cur_spec);
+if tracing>0 then
+ if internal[autorounding]<=0 then print_spec(", after subdivision")
+@y
+if (internal[autorounding]>0)and(chopped=0) then xy_round;
+octant_subdivide; {complete the subdivision}
+if (internal[autorounding]>unity)and(chopped=0) then diag_round;
+@<Remove dead cubics@>;
+@<Insert octant boundaries and compute the turning number@>;
+while left_type(cur_spec)<>endpoint do cur_spec:=link(cur_spec);
+if tracing>0 then
+ if (internal[autorounding]<=0)or(chopped<>0) then
+ print_spec(", after subdivision")
+@z
+@x module 404
+@d procrustes(#)==if abs(#)>max_allowed then
+ begin chopped:=true;
+ if #>0 then #:=max_allowed@+else #:=-max_allowed;
+ end
+
+@<Truncate the values of all coordinates that exceed...@>=
+p:=cur_spec; k:=1; chopped:=false;
+@y
+@d procrustes(#)==if abs(#)>=dmax then
+ if abs(#)>max_allowed then
+ begin chopped:=1;
+ if #>0 then #:=max_allowed@+else #:=-max_allowed;
+ end
+ else if chopped=0 then chopped:=-1
+
+@<Truncate the values of all coordinates that exceed...@>=
+p:=cur_spec; k:=1; chopped:=0; dmax:=half(max_allowed);
+@z
+@x module 404
+if chopped then
+@y
+if chopped>0 then
+@z
+
+567. I forgot to update curtype in e.g. boolean b[]; b1=true=b2;
+ (Thorsten Dahlheimer, June 2004)
+@x 1003
+ begin nonlinear_eq(v,cur_exp,false); goto done;
+@y
+ begin nonlinear_eq(v,cur_exp,false); unstash_cur_exp(cur_exp); goto done;
+@z
+
+568. Wrong handling of extreme TFM values (T. Dahlheimer, June 2004)
+@x 1128
+max_tfm_dimen:=16*internal[design_size]-internal[design_size] div @'10000000;
+@y
+max_tfm_dimen:=16*internal[design_size]-1-internal[design_size] div @'10000000;
+@z
+@x 1129
+ if x>0 then x:=three_bytes-1@+else x:=1-three_bytes;
+ end
+else x:=make_scaled(x*16,internal[design_size]);
+@y
+ if x>0 then x:=max_tfm_param@+else x:=-max_tfm_param;
+ end;
+x:=make_scaled(x*16,internal[design_size]);
+@z
+
+569. Bug in make_ellipse when theta=180 (T. Dahlheimer, June 2004)
+@x 530
+ else begin beta:=minor_axis; gamma:=major_axis;
+@y
+ else begin beta:=minor_axis; gamma:=major_axis; theta:=0;
+@z
+
+570. Forgot outer_tag in scan_declared variable (T. Dahlheimer, June 2004)
+@x 1011
+done: if eq_type(x)<>tag_token then clear_symbol(x,false);
+@y
+done: if eq_type(x) mod outer_tag<>tag_token then clear_symbol(x,false);
+@z
+
+571. Apparent bug if init_gf called at incredibly unlikely time (T. Dahlheimer,
+June 2004); not REALLY a bug, because str_ptr cannot equal max_strings here
+(because this was checked by end_name, and area_delimiter=0 there because
+cur_area="" in the output file name); but anyway I'll make a patch to be clean
+@x 1163
+str_start[str_ptr+1]:=pool_ptr; gf_string(0,str_ptr);
+@y
+gf_string(0,make_string); decr(str_ptr);
+@z
+
+572. Memory leak in make_ellipse on symmetric pens that don't have
+a point on the x-axis (Eberhard Mattes, 03 June 2008)
+@x module 536
+done1: link(p):=s; beta:=-y_coord(h);
+@y
+done1: if (link(p)<>null) then free_node(link(p),knot_node_size);
+link(p):=s; beta:=-y_coord(h);
+@z
+
+-------------
+999. The absolutely final change (to be made after my death)
+@x module 2
+@d banner=='This is METAFONT, Version 2.7182818' {printed when \MF\ starts}
+@y
+@d banner=='This is METAFONT, Version $e$' {printed when \MF\ starts}
+@z
+When this change is made, the corresponding line should be changed in
+Volume D, and also on page 31 of The METAFONTbook.
+My last will and testament for METAFONT is that no further changes be made
+under any circumstances. Improved systems should not be called simply
+`METAFONT'; that name, unqualified, should refer only to the program for which
+I have taken personal responsibility. -- Don Knuth
diff --git a/systems/knuth/dist/errata/tex82.bug b/systems/knuth/dist/errata/tex82.bug
new file mode 100644
index 0000000000..7d7a8c34ba
--- /dev/null
+++ b/systems/knuth/dist/errata/tex82.bug
@@ -0,0 +1,9103 @@
+This file has been updated periodically ever since TeX82 was born;
+it has been summarized in "The errors of TeX," Software Practice &
+Experience, July 1989. Entries are in chronological order;
+thus the most recent news (including all bugfixes made since
+that article was published) appears at the bottom of the file.
+
+[Add 519 to these entry numbers to get the corresponding number in
+the published article. The article also translates all module
+numbers to their final form; what you are about to read is
+"authentic source material" from the early days before TeX converged.]
+
+-------------------------------------------------------------------------------
+
+First updates to the TeX82 listing published in September, 1982.
+(These changes were included in the original Version 0 of TeX, but they
+were discovered after the listing went to press.)
+
+1. Module 943, line 6 (bug discovered 9/28)
+change "if cur_cmd=char_num then" to
+ if (cur_cmd=letter) or (cur_cmd=other_char) then r:=qi(cur_chr)
+ else if cur_cmd=char_num then
+
+2. "pause" changed to "pausing" and "pause_code" to "pausing code", throughout.
+
+3. Module 719, lines 8 and 11 (bug discovered 9/28)
+insert "rule_save:=overfull_rule; overfull_rule:=0;" after "save_ptr-2;"
+insert "overfull_rule:=rule_save;" before "q:=p+list_offset;"
+and insert a declaration of "overfull_rule: scaled" in module 716.
+
+4. Module 1128, lines 6 and following (bug discovered 9/28)
+change "while n<>0 do" to "loop"
+change "goto done" to
+ begin scan_left_brace; new_save_level(false_group);
+ goto done;
+ end
+change "... return 1130>;" to
+ ... return 1130>
+ else if n=0 then
+ begin new_save_level(case_group); goto done;
+ end;
+and change "done: ... (false_group);" to "done:".
+
+5. Module 182, line 10 (suggestion by DRF on 9/30)
+change "0.0" to "?.?"
+
+6. Module 682, new definition of math_spacing (decision of 10/2)
+"0234000122*4000133**3**344*0400400*000000234000111*4111112341011"
+
+7. Module 684, new code for case "4" (decision of 10/2)
+"4": if cur_style<script_style then x:=thick_mu_skip_code else x:=0;
+[Also in module 682 line 7, say "...a conditional thick space (\nonscript..."]
+
+8. Module 11, trie_size changed from 7000 to 8000 because of new
+improved (but longer) hyphenation patterns (10/4)
+
+9. Module 453, forgot to change this when 454 changed (noted by DRF 10/6)
+format_default_length=20 (not 22)
+format_area_length=11 (not 13)
+
+ Changes to TEX.WEB made after Version 0 was released in October, 1982.
+
+Since copies of TEX.WEB are not supposed to be edited, there are
+two ways to make your version of TeX bug-free:
+ 1. Get a new copy of TEX.WEB.
+ 2. Put the corrections into your change file(s).
+Some people will find (1) easier than (2), except the main TeX sources at
+SCORE won't be updated quite as fast as the sources at SAIL (which have to
+be translated into ascii before they are sent to the outside world).
+Actually (2) will be quite easy, unless the list of changes becomes quite
+long, so it is the recommended procedure. In case (2) it would be useful
+to include a comment like "this is fix number xx" (using the numbering
+scheme in this file), so that the change could readily be deleted at some
+future time.
+
+10. Module 857 line -5 (bug discovered 10/8/82 by HWT)
+change it to: if h>0 then decr(h) else h:=trie_op_hash_size;
+
+11. Module 457 line 3 (typo discovered 10/9/82)
+change `\.!' to `\.\&'
+
+12. Module 1245 line 8 (bug fixed 10/9/82, discovered by MMD)
+insert the following between "begin" and "if":
+if format_ident<>0 then initialize; {erase preloaded format}
+
+** Version 0.1 incorporates the above changes.
+
+13. This is an extension to the language, put in to satisfy people who
+objected to the fact that \write (and \openout and \closeout) only
+caused action after being deferred to the next \shipout. Some applications
+call for immediate output, hence a new feature: \immediate followed by
+\openout or \write or \closeout causes the output action to take place
+without delay. For example, \immediate\write{x} is equivalent to
+\shipout\vbox{\write{x}} except that the latter also puts an empty
+page into the DVI file.
+
+The extension requires the following new code:
+13a. Insert `\immediate' after `\closeout' in module 1248.
+13b. Define immediate_code=4 and include the following in module 1252:
+ primitive("immediate",extension,immediate_code);
+13c. Include the following in module 1254:
+ immediate_code:print_esc("immediate");
+13d. And, in module 1256:
+ immediate_code:@<Implement \.{\\immediate}@>;
+13e. Finally, there's a new module inserted after old module 1280.
+Here is the WEB coding for this module:
+
+@ The presence of `\.{\\immediate}' causes the |do_extension| procedure
+to descend to one level of recursion. Nothing happens unless \.{\\immediate}
+is followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'.
+@^recursion@>
+
+@<Implement \.{\\immediate}@>=
+begin get_nc_token;
+if (cur_cmd=extension)and(cur_chr<=close_node) then
+ begin p:=tail; do_extension; {append a whatsit node}
+ out_what(tail); {do the action immediately}
+ flush_node_list(tail); tail:=p; link(p):=null;
+ end
+else back_input;
+end;
+
+** Version 0.2 incorporates the above changes.
+
+14. Like change 11, this one doesn't affect the program, it just improves
+the documentation: Insert the following definitions in module 106:
+define set_glue_ratio_zero(#) == #:=0.0 {assign representation of zero ratio}
+define set_glue_ratio_one(#) == #:=1.0 {assign representation of unit ratio}
+
+These macros are now introduced in a dozen or so future modules, thereby
+eliminating most of the system-dependent changes needed elsewhere for ratios.
+(Note: I also changed 0 to 0.0 in two places of module 182, where a glue_ratio
+comparision was being made.)
+
+15. Change of module 576 (discovered by HWT, 10/14/82)
+"hd:quarterword" should be "hd:eight_bits".
+(The same error occurs in module 522, but in that module the remedy is
+simply to delete the declaration of hd, since this variable is no longer used.)
+
+16. A most embarrassing bug (discovered by DRF, 10/14/82)
+Replace module 531 by:
+
+@ A mild optimization of the output is performed by the |dvi_pop|
+routine, which issues a |pop| unless it is possible to cancel a
+`|push| |pop|' pair. The parameter to |dvi_pop| is the byte address
+following the old |push| that matches the new |pop|.
+
+@p procedure dvi_pop(@!l:integer);
+begin if (l=dvi_offset+dvi_ptr)and(dvi_ptr>0) then decr(dvi_ptr)
+else dvi_out(pop);
+end;
+
+Now we need to make a few changes to subsequent modules:
+16a. In 549, after "incr(cur_s)", insert
+ if cur_s>0 then dvi_out(push);
+and before "decr(cur_s)", insert
+ if cur_s>0 then dvi_pop(save_loc);
+
+16b. Delete "dvi_out(push);" and "dvi_pop;" from modules 553, 558, 562, 567.
+
+16c. Change module 559 just as in 16a.
+
+17. Module 605, line 6 (discovered 10/15/82)
+The test should be "prev_depth>ignore_depth"
+
+** Version 0.3 incorporates the above changes.
+
+18. Module 11 (noticed by WLS, 10/16/82)
+Delete the definition of align_size (it's harmless but never used)
+
+19. (This change and the next cause major changes to the TRIP output;
+file TRIP.LOG and its relatives are being kept up to date on area [tug,dek].)
+The change avoids error messages when vpackage is called during output.
+Such messages can occur when there was no error, because the page is being
+boxed without the \skip glue from its insertions; so they should be omitted.
+The user who really wants such messages can still get them by saying
+"\setbox255=\vbox to 1ht255{\unbox255}".
+In module 903, insert the followng before the declaration of "wait":
+ save_vbadness:integer; {saved value of |vbadness|}
+ save_vfuzz: scaled; {saved value of |vfuzz|}
+Then in module 924, insert
+ save_vbadness:=vbadness; vbadness:=inf_bad;
+ save_vfuzz:=vfuzz; vfuzz:=max_dimen; {inhibit error messages}
+before the call on vpackage, and
+ vbadness:=save_vbadness; vfuzz:=save_vfuzz;
+after.
+
+20. Module 917 computes |page_size| improperly. (Noticed 10/21/82)
+Delete the statement "page_so_far[1]:=page_so_far[1]+width(q);"
+and change the preceding statement to:
+ page_size:=page_size-h-width(q);
+(The comment about page_so_far in the first paragraph of module 895 is
+correct; I mistakenly introduced a bug in module 917 some months after
+writing the first draft of the code, believing that I was making the
+algorithm more elegant or something.)
+
+** Version 0.4 incorporates the above changes.
+
+21. Since TeX82 applied to (the "woven" documentation) TEX.TEX uses
+about 11500 words of variable-size memory, I'm increasing hi_mem_size
+(in module 12) from 12000 to 13000. Actually, I recommend using considerably
+larger values for mem_max and hi_mem_size, whenever possible.
+
+22. Addition of the \boxmaxdepth parameter (10/22/82):
+This involves renumbering hfuzz_code through dimen_pars, in module 234,
+to numbers 9 through 18; inserting the lines
+ @d box_max_depth_code=8 {maximum depth of explicit vboxes}
+ @d box_max_depth==dimen_par(box_max_depth_code)
+ box_max_depth_code:print_esc("boxmaxdepth");
+to module 234 and
+ primitive("boxmaxdepth",assign_dimen,box_max_depth_code);
+to module 235; and changing the call on vpack in module 996 to
+ vpackage(link(head),saved(2),saved(1),box_max_depth);
+
+** Version 0.5 incorporates the above changes.
+
+23. Module 1224, line 4 (bug found by GMK/HWT on 10/26/82)
+change "(k+x>eqtb_size)" to "(k+x>eqtb_size+1)"
+
+24. Module 247, line 13 (bug found 10/26/82)
+(bug was reflected in TRIP.LOG but not noticed)
+change "ch_code(p)" to "ch_code(p-single_base)"
+
+25. Module 670, line 18 (bug found 10/27/82)
+interchange the statements "fetch(...)" and "math_type(...):=..."
+(since the fetch routine sometimes has a side-effect of changing math_type)
+ A major change (Version 0.6) made on October 28:
+The following list of changes counts as "number 26" on the list.
+Fonts now have identifiers instead of code numbers; the "\:" primitive
+has disappeared; and there are associated new features for "\the".
+
+a. In module 11, delete bad_font_code.
+b. In modules 170 and 172, delete print_esc(":"), and change
+ `print_int(font_code' to `sprint_cs(font_ident'.
+c. In module 205, the comment for set_font is revised.
+d. In module 217, there are now five locations for control sequences
+ that are perpetually defined; undefined_control_sequence
+ is therefore defined to be frozen_control_sequence+5.
+e. In module 248, sprint_cs is now included among <Basic printing procedures>.
+f. Delete the primitive ":" in modules 250 and 251.
+g. Add to module 376: <Declare procedures that scan font-related stuff>
+h. In module 377, seven levels are now distinguished; we define
+ font_val=4, ident_val=5, tok_val=6.
+i. In module 380, delete the previous cases for def_family and set_font,
+ and the following takes the place of case assign_toks:
+ assign_toks,def_family,set_font,def_font: <Fetch a token list or
+ font number or font identifier, provided that level=tok_val>;
+j. Module 382 (which now has a new name) has this new ending:
+ else if cur_cmd=assign_toks then scanned_result(equiv(m))(tok_val)
+ else <Fetch a font number or a font identifier>
+k. Module 389 becomes
+ <Fetch a font number or a font identifier>=
+ if cur_cmd=set_font then scanned_result(cur_chr)(font_val)
+ else if cur_cmd=def_font then
+ scanned_result(font_ident[cur_font])(ident_val)
+ else begin scan_four_bit_int;
+ scanned_result(font_ident[equiv(m+cur_val)])(ident_val);
+ end
+l. In module 393, the relation `<>tok_val' becomes `<=mu_val'.
+m. In module 426, the relation `=tok_val' becomes '>=ident_val'.
+ Also add a new case to the case statement:
+ font_val: begin print(font_name[cur_val]);
+ if font_size[cur_val]<>font_dsize[cur_val] then
+ begin print(" at "); print_scaled(font_size[cur_val]);
+ print("pt");
+ end;
+ end;
+n. The body of module 427 becomes:
+ begin p:=temp_head; link(p):=null;
+ if cur_val_level=ident_val then store_new_token(cs_token_flag+cur_val)
+ else if cur_val<>null then
+ begin r:=link(cur_val); {do not copy the reference count}
+ while r<>null do
+ begin store_new_token(info(r)); r:=link(r);
+ end;
+ end;
+ the_toks:=p;
+ end
+o. In module 480, delete user_font_code; there's a new comment:
+ When the user defines \font\f, say, TeX assigns an internal number
+ to the user's font \f. For example, if this internal number is 13,
+ we will have font_ident[13]=p and equiv(p)=13, where p is the eqtb
+ location of the control sequence \f.
+p. In module 481, delete the declaration of font_number, and
+ replace the declaration of font_code by
+ font_ident:array[internal_font_number] of pointer;
+q. New stuff in module 483 (also delete references to font_number, font_code):
+ define bad_font_ident=frozen_control_sequence+4 {denotes a null font}
+ font_name[undefined_font]:="nullfont";
+ font_ident[undefined_font]:=bad_font_ident;
+ text(bad_font_ident):="nullfont"; eq_level(bad_font_ident):=level_one;
+ eq_type(bad_font_ident):=set_font;
+ equiv(bad_font_ident):=undefined_font;
+r. In module 490, parameter u is new of type pointer, and the
+ read_font_info subroutine is changed to a function that
+ returns an internal_font_number. There's a new local variable
+ g:internal_font_namber; {the number to return}
+ and we set g:=undefined_font immediately upon entering read_font_info.
+ Also set read_font_info:=g just before exiting.
+s. In module 491, print_int(u) becomes sprint_cs(u).
+t. Delete the statements involving font_code and font_number in module 506,
+ and set g:=f at the end of that module.
+u. The body of module 507 becomes:
+ <Declare procedures that scan font-related stuff>=
+ procedure scan_font_ident;
+ var f:internal_font_number;
+ begin <Get the next non-blank non-call...>;
+ if cur_cmd=set_font then f:=cur_chr
+ else begin print_nl("! Missing font identifier");
+ help2("I was looking for a control sequence whose")
+ ("current meaning has been defined by \font.");
+ back_error; f:=undefined_font;
+ end;
+ end;
+v. New beginning of module 508:
+ The following routine is used to implement `\texinfo f n'.
+ The boolean parameter writing is set true if the calling program
+ intends to change the parameter value.
+ <Declare procedures that scan font-related stuff>=
+and "scan_font_number" is changed to "scan_font_ident".
+w. In module 509, "print_int(font_code" becomes "sprint_cs(font_ident".
+ Also "font code is defined" becomes "\font is loaded" in the help.
+x. In module 940, the first line of help is changed to
+ "You have to specify a font identifier,"
+y. The set_font case in module 1138 reduces to
+ set_font: define(cur_font_loc,data,cur_chr);
+z. In module 1153, delete all the complicated stuff starting with "scan_int"
+ and substitute simply this:
+ scan_font_ident; define(p,data,cur_val);
+ end;
+aa. Change new_font to new_font(a) in module 1169, and add parameter
+ (a:small_number)
+ in module 1170. Also add the label common_ending, and declare
+ variable u to have type pointer. The code beginning with "scan_int"
+ is changed to the following:
+ get_token;
+ if cs_ptr=0 then
+ <Complain about the missing control sequence and return>;
+ u:=cs_ptr; scan_optional_equals; scan_file_name;
+ <Scan the ``at'' size specification>;
+ <If this font has already been loaded, set f to the internal
+ font number and goto common_ending>;
+ f:=read_font_info(u,cur_name,cur_area,s);
+ common_ending: define(u,set_font,f); font_ident[f]:=u;
+ exit:end;
+bb. New module 1172:
+ When the user gives a new identifier to a font that was previously
+ loaded, the new value becomes the font_ident of record.
+ Font names `xyz' and `XYZ' are considered to be different.
+ <If this font has already been loaded...>=
+ for f:=font_base+1 to font_ptr do
+ if [the test previously in module 1174] then goto common_ending
+cc. New module 1173:
+ <Complain about the missing control sequence and return>=
+ begin print_nl("! A font identifier must be a control sequence");
+ help2("You should say, e.g., `\font\f=fontfilename'.")
+ ("(I'm going to ignore the \font command you just gave.)");
+ back_error; return;
+ end
+dd. New module 1174:
+ <Cases of print_cmd_chr...>=
+ set_font:begin print("select font "); print(font_name[chr_code]);
+ if font_size[chr_code]<>font_dsize[chr_code] then
+ begin print(" at "); print_scaled(font_size[chr_code]);
+ print("pt");
+ end;
+ end;
+ee. Delete the comment at the beginning of module 1227, and delete the
+ loop for k:=0 to bad_font_code-1, and delete dump_int(undefined_font).
+ff. Delete the repeat loop in module 1228.
+gg. Change font_code to font_ident in module 1229, and change
+ print_int(font_code to sprint_int(font_ident.
+hh. In module 1230,
+ begin undump(single_base)(undefined_control_sequence)(font_ident[k]);
+ undump_qqqq(font_check[k]); [and continue as before]
+
+The changes above have been incorporated into Version 0.6.
+ The ill-fated Version 0.7
+
+27. (Here's a change that I retracted shortly after making it, since I
+discovered that the source was flaky after all; and I also found a reliable
+source [NBS Circular 570] confirming my original information. I include
+the note here only for historical purposes...)
+
+"After years of searching, I've finally found a definitive definition of
+the printer's point; and (unfortunately) my previous conjecture was wrong.
+The truth is that 83pc=35cm, exactly; so I am changing TeX to conform.
+This means some changes in the comments of modules 101 and 517, and the
+following changes to the program:
+
+"27a. In modules 547 and 571, the appropriate DVI numbers are now:
+ dvi_four(109375); dvi_four(2039808); {conversion ratio for sp}
+"27b. The guts of module 418 become:
+ if scan_keyword("in") then set_conversion(63246)(875)
+ else if scan_keyword("pc") then set_conversion(12)(1)
+ else if scan_keyword("cm") then set_conversion(996)(35)
+ else if scan_keyword("mm") then set_conversion(498)(175)
+ else if scan_keyword("bp") then set_conversion(10541)(10500)
+ else if scan_keyword("dd") then set_conversion(996)(931)
+ else if scan_keyword("cc") then set_conversion(11952)(931)
+ else..."
+
+*** Version 0.7 of TeX incorporated the changes above (82/10/30);
+this version was withdrawn on 82/11/2.
+ Changes subsequent to Version 0.6
+
+28. The experience with #27 did lead me to one improvement, thanks
+to Chuck Bigelow, with respect to Didot points. (11/4/82)
+Add the following comment to module 418:
+ According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$;
+ this agrees well with the value $\rm1000.333/,mm$ cited by Bosshard
+ in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980).
+And change two lines of the code there as follows:
+ else if scan_keyword("dd") then set_conversion(1238)(1157)
+ else if scan_keyword("cc") then set_conversion(14856)(1157)
+
+29. The new font material is made more robust by ensuring that \the\font
+always returns a pointer to a control sequence whose command code is
+set_font. (Change 11/4/82)
+
+29a. The code changed in 26aa is changed again, to the following:
+ <If the next token isn't a control sequence, issue a complaint and
+ return; otherwise set u:=cs_ptr, and set hash_used to the
+ location of a ``frozen'' copy of the new font identifier>;
+ define(u,set_font,undefined_font);
+ scan_optional_equals; scan_file_name;
+ <Scan the ``at'' size specification>;
+ <If this font has already been loaded, set f to the internal
+ font number and goto common_ending>;
+ f:=read_font_info(u,cur_name,cur_area,s);
+ common_ending: equiv(u):=f; geq_define(hash_used,set_font,f);
+ font_ident[f]:=hash_used;
+ exit:end;
+29b. Instead of 26cc, here's the new module 1173:
+ We reserve a special control sequence for the font identifier;
+ this one cannot be redefined by the user, so it is safe to
+ return it as a value of \the\font.
+ <If the next token isn't a control sequence...>=
+ get_token;
+ if cs_ptr<hash_base then
+ begin print_nl(
+ "! A font identifier must be a multiletter control sequence");
+ help2("You should say, e.g., `\font\ffn=fontfilename'.")
+ ("(I'm going to ignore the \font command you just gave.)");
+ back_error; return;
+ end;
+ repeat if hash_is_full then overflow("hash size",hash_size);
+ decr(hash_used);
+ until text(hash_used)=0; {search for an empty location in hash}
+ u:=cs_ptr; text(hash_used):=text(u); {copy the name}
+ stat incr(cs_count); tats
+
+30. Module 993, replace lines 5 to 7 (11/5/82):
+else begin if k=vmode then new_save_level(vbox_group)
+ else begin new_save_level(vtop_group); k:=vmode;
+ end;
+ if looseness<>0 then eq_word_define(int_base+looseness_code,0);
+ if hanging_indent<>0 then
+ eq_word_define(dimen_base+hanging_indent_code,0);
+ if par_shape_ptr<>null then eq_define(par_shape_loc,shape_ref,null);
+ end;
+
+31. Module 403, replace line 5 (11/6/82):
+if cur_tok<cs_token_flag then
+ begin cur_val:=cur_chr;
+ if cur_cmd<=right_brace then
+ if cur_cmd=right_brace then incr(align_state)
+ else decr(align_state);
+ end
+
+32. Improvements to error recovery for bad alignments (11/6/82):
+In module 1039, replace "begin align_error; goto reswitch; end" by "align_error"
+In module 1040, delete "cur_cmd:=left_brace;" and "cur_cmd:=right_brace;"
+ and change "error" to "back_error"
+In module 1041, delete "cur_cmd:=relax;"
+
+33. More power to \let (11/8/82):
+33a. Module 1141 becomes:
+ <Assignments>+=
+ let: begin get_token;
+ if cs_ptr<>0 then <Carry out the \let operation>
+ else begin print_nl
+ ("! You can use \let only with control sequences");
+ help2("I'm not \let-ting anything change here,")
+ ("since I can only do things like `\let\a=b'.");
+ back_error;
+ end;
+ end;
+33b. Module 1142 becomes:
+ <Carry out the \let...>=
+ begin p:=cs_ptr;
+ repeat get_token;
+ until cur_cmd<>spacer;
+ if cur_tok=other_token+"=" then
+ begin get_token;
+ if cur_cmd=spacer then get_token;
+ end;
+ if cur_cmd>=call then add_token_ref(cur_chr);
+ define(p,cur_cmd,cur_chr);
+ end
+
+34. This change helps you see an undefined control sequence
+in certain unusual cases (discovered by JDH, 11/8/82):
+Add the clause "(base_ptr=input_ptr) or" to module 294, line 2.
+
+35. Here's an improvement in the formula for demerits; previously
+more weight was given to minimizing bad spacing on lines with penalties,
+so that (slightly loose hyphenated line)(OK line) was considered worse
+than (OK hyphenated line)(quite loose line). (fixed 11/8/82)
+Change lines 2--7 of module 772 to the following:
+ d:=line_penalty+b; d:=d*d;
+ if pi<>0 then
+ if pi>0 then d:=d+pi*pi
+ else if pi>eject_penalty then d:=d-pi*pi;
+
+36. Minor change to save buffer space in non-INITEX (11/10/82):
+Enclose the declaration of pool_file in module 50 by init...tini.
+
+37. Minor improvement in format for the context of error messages (11/13/82):
+37a. Module 289, type "inserted" split into "backed_up=3", "inserted=4"
+and the other type numbers increase: "macro=5", etc.
+37b. In module 296, backed_up: if loc=null then print_nl("<recently read>")
+ else print_nl("<to be read again> ");
+ inserted: print_nl("<inserted text> ");
+37c. Minor changes to comments in modules 289 and 293.
+37d. Change "inserted" to "backed_up" in module 294.
+37e. Add definition "back_list(#)==begin_token_list(#,backed_up)" in module 305.
+37f. In module 306, first test should be ">=backed_up", second "<=inserted".
+37g. Change ins_list to back_list in modules 307, 318, 375, 1195.
+37h. Add definition to module 308:
+ ins_error==begin back_error; token_type:=inserted; end
+37i. Change back_error to ins_error in modules 362, 1040, 1046.
+37j. Change "<to be read again>" to "<inserted text>" in module 973 help.
+
+38. Module 407, the error message is changed (11/11/82) to:
+"! Missing number, treated as zero".
+
+39. Fix anomaly when hbadness or vbadness is small (11/11/82):
+Module 593, line 2, "if (-x-total_shrink[normal]>hfuzz) or (hbadness<100)"
+Module 603, line 2, "if (-x-total_shrink[normal]>vfuzz) or (vbadness<100)"
+
+40. Added the \tokens primitive (11/13/82):
+In module 223, defined tokens_loc and tokens (analogous to every_par).
+In module 225, defined the primitive.
+In module 1146, changed the comment to include tokens_loc as a possibility.
+
+41. Change get_nc_token to get_token in module 374 (11/13/82).
+(In particular \def\foo{...}\foo won't say "undefined c.s." now!)
+(This change later retracted during debugging; it was found that
+"endv" aborts the job, so this cure was worse than the disease. Then
+it was re-established as part of the major change to conditionals,
+because endv needed to be more robust anyway.)
+
+42. In module 699, we make \span expand in a preamble (11/13/82).
+Change lines -7 through -3 to:
+ begin get_nc_token; goto reswitch;
+ end;
+
+43. Modules 321 and 322 have been altered for better efficiency (11/12/82)
+and better exposition; the three conditions of module 322 are now
+in separate if tests.
+
+44. Frozen control sequences are now unredefinable. (11/16/82)
+A new procedure get_r_token has been introduced to give uniform error
+messages for \def, \let, \read, \font, \mathchardef, and to make them
+incapable of changing the frozen equivalents.
+
+Incidentally, get_nc_token is changed to get_x_token; and ch_code
+becomes cat_code.
+ A major change (Version 0.8) made on November 14--16:
+Conditional statements are taken from semantics to syntax.
+
+This change, which counts as number 45 on the list, made it necessary
+to renumber the modules. And it was such a drastic change, the
+differences can only be sketched here, using the old module numbers
+for reference. A variety of other things were cleaned up because this
+change made it more natural for them to be handled differently.
+
+45a. Module 158, inserted a permanently empty token list called null_list.
+45b. Module 204, deleted if_test, case_branch, else_code, convert;
+ inserted end_cs_name.
+45c. Module 206, inserted if_test, fi_or_else, cs_name, convert, and
+ end_template (the latter comes after long_outer_call).
+45d. Module 217, there are now nine frozen control sequences; also null_cs.
+45e. Modules 247 and 248, must be able to print a null control sequence.
+45f. Module 268 (leave_transparent_group) disappears; comments in the
+ previous modules also are appropriately simplified.
+45g. Module 273, delete endv_token.
+45h. Modules 277 and 278, delete references to endv (it no longer appears).
+45i. After module 281, a procedure show_cur_cmd_chr takes the
+ code for tracing commands from module 937.
+45j. Module 317 now forbids \outer control sequences when skipping,
+ and inserts \fi in front of them, for error recovery.
+45k. Module 334 now uses null_cs if carriage-return is an escape.
+45l. Module 343 no longer has endv test.
+45m. Module 344, major extensions to expand_calls make it several modules
+ longer. It saves global variables like cur_val. It changes
+ end_template to endv. It processes cs_name and convert.
+ It processes if_test by calling the "conditional" procedure.
+ It processes fi_or_else by checking their legality, and (if legal)
+ updating the if stack.
+45n. Module 373 (pass_block) disappears.
+45o. After module 400, radix and cur_val and cur_val_level are initialized.
+45p. Module 428 changes so that conv_toks is a procedure rather than a
+ function; this procedure is invoked by expand_calls.
+45q. Modules 436 and 1032, delete reference to conv_toks.
+45r. After module 443, the entire part 48 moves to this part of the program,
+ and the parts are renumbered accordingly. \case is changed to
+ \ifcase, and it resides under the if_test command.
+ New routines are inserted to maintain a linked stack that records
+ the current state of conditionals. There's a procedure
+ pass_text that skips text while looking for \fi or \else or \or
+ on level zero with respect to \if...\fi nesting. The previous
+ routines for skip control via handle_right_brace are eliminated,
+ and the new ones are somewhat simpler. The new \ifcat is mostly
+ combined with the old \if; they no longer ignore spaces.
+ The scanner_status is set to normal while processing \ifx.
+ \ifx will consider \a to equal 0 after \let\a=0.
+45s. Modules 701, 707: endv_token is replaced by a token for a frozen
+ control sequence whose command code end_template makes it behave
+ like an outer_call. After expand_calls, it is converted to another
+ frozen control sequence, whose command code is endv. This two-step
+ facilitates error recovery, instead of giving a fatal error stop.
+45t. Module 874, remove "pass_block(1); goto done".
+45u. After module 1047, routine for end_cs_name prints an error message.
+45v. Module 1049, get_nc_token becomes get_token. (else $\ifmmode fails)
+45w. Module 1202 gets another case (end_template).
+45x. Module 1243, new warning is printed if the if stack isn't empty.
+ Changes after version 0.8
+
+46. Declare c : ascii_code in module 825 (noted by DRF, 11/21/82)
+
+47. Module 776 lines 4-6 (suggested by DRF, 11/21/82)
+threshold:=pretolerance;
+if threshold>=0 then
+ begin stat if tracing_stats>2 then
+ begin begin_diagnostic; print_nl("@@firstpass");
+ end; tats
+ second_pass:=false;
+ end
+else begin threshold:=tolerance; second_pass:=true;
+ end;
+
+48. Protection for DVI files (added 11/16/82)
+Replace lines 2 and 3 of module 570 by:
+ <Update the values of max_h and max_v; but if the page is too large,
+ goto done>;
+Move the statement "dead_cycles:=0" from module 570 to module 568.
+Declare "label done;" in module 568.
+And add the following new module after 570:
+
+@ Sometimes the user will generate a huge page because other error messages
+are being ignored. Such pages are not output to the \.{dvi} file, since they
+may confuse the printing software.
+
+@<Update the values of |max_h| and |max_v|; but if the page is too large...@>=
+if (height(p)>max_dimen)or(depth(p)>max_dimen)or(width(p)>max_dimen) then
+ begin print_nl("! Huge page cannot be shipped out");
+ help2("The page just created is more than 18 feet tall or")@/
+ ("more than 18 feet wide, so I suspect something went wrong.");
+ error;
+ if tracing_output=0 then
+ begin begin_diagnostic;
+ print_nl("The following box has been deleted:");
+ show_box(p);
+ end_diagnostic(true);
+ end;
+ goto done;
+ end;
+if height(p)+depth(p)>max_v then max_v:=height(p)+depth(p);
+if width(p)>max_h then max_h:=width(p)
+
+49. New features \everymath and \everydisplay (12/2/82).
+Make changes analogous to those for "\tokens" (see change 40).
+Put the following just before the end of module 1050:
+ if every_math<>null then begin_token_list(every_math,every_math_text);
+And put the following just before the end of module 1056:
+ if every_display<>null then
+ begin_token_list(every_display,every_display_text);
+
+50. New feature \futurelet (12/2/82):
+Module 1141 changes again. (Also, "let" and "futurelet" share cmd code "let".)
+let: begin n:=cur_chr;
+ get_r_token; p:=cs_ptr;
+ if n=normal then
+ begin repeat get_token;
+ until cur_cmd<>spacer;
+ if cur_tok=other_token+"=" then
+ begin get_token;
+ if cur_cmd=spacer then get_token;
+ end;
+ end
+ else begin get_token; q:=cur_tok; get_token; back_input;
+ cur_tok:=q; back_input; {look ahead, then back up}
+ end; {|back_input| doesn't affect |cur_cmd|, |cur_chr|}
+and continue with "if cur_cmd>=call", as before.
+
+*** The changes above have been incorporated into version 0.9 of TeX.
+ Changes after version 0.9
+
+51. new \endinput primitive (suggested by FY, 12/7/82):
+\input and \endinput both use the same command code. The cases in module
+955 are deleted and replaced by those in module 965 (which disappears),
+because \input is now allowed in any mode. The code for any_mode(input)
+is "if cur_chr=0 then start_input else force_eof:=true", and it moves from
+module 952 to just before module 1197. The global variable force_eof
+is initially false, and module 340 becomes (after first:=start;)
+ if not force_eof then
+ begin if input_ln.... else force_eof:=true;
+ end;
+ if force_eof then begin print_char(")"); force_eof:=false;...
+
+52. Module 1005, line 4 (12/8/82)
+change "if align_state<0" to "if (mode<0)or(align_state<0)"
+(This avoids embarrassing case where TeX says "type a command or say \end"
+but when you type \end it says "You can't use \end in restricted horiz mode".)
+
+53. Patch to the new code for \csname (12/21/82)
+After eq_type(cs_ptr):=relax, also say equiv(cs_ptr):=256.
+(This corrects a bug that would appear only if \csname occurs right
+after a file name.)
+
+54. Change 47 introduced a bug when tracingonline=0 (12/20/82)
+when omitting firstpass, also do "if tracing_stats>2 then begin_diagnostic;"
+
+55. \hskip -1pt plus 2pt was parsed as \hskip -(1pt plus 2pt)! (12/20/82)
+In module 421, before line -4, insert the following:
+ if negative then
+ begin cur_val:=-cur_val; negative:=false;
+ end;
+But then realize that "negative" is always false on line -2; simplify.
+
+56. Cosmetic change to paragraph statistics (12/23/82)
+tight_fit..very_loose_fit codes have been renumbered very_loose_fit..tight_fit.
+
+57. Module 729 changes to make TeX language more consistent (12/23/82):
+else if type(tail)<>glue_node then tail_append(new_penalty(inf_penalty))
+else begin type(tail):=penalty_node; delete_glue_ref(glue_ptr(tail));
+ flush_node_list(leader_ptr(tail)); penalty(tail):=inf_penalty;
+
+58. Commas are allowed as alternates to radix points. (12/23/82)
+define continental_point_token=other_token+"," {decimal point, Eurostyle}
+in module 400, and insert the following twice in 409:
+ if cur_tok=continental_point_token then cur_tok:=point_token;
+
+59. \hangindent becomes a normal parameter. (12/23/82)
+This simplifies the code in obvious ways; for example, module 1148 disappears.
+The command code hang_indent goes away too; what was previously called
+hanging_indent is then renamed hang_indent.
+
+60. \prevgraf becomes accessible. (12/23/82)
+This involves renaming the "after" field "pg_field" in the nest array;
+making a new command code set_prev_graf;
+including the following loop into scan_the:
+ nest[nest_ptr]:=cur_list; p:=nest_ptr;
+ while abs(nest[p].mode_field)<>vmode do decr(p);
+ scanned_result(nest[p].pg_field)(int_val);
+and including the following active procedure for any_mode(set_prev_graf):
+ procedure change_prev_graf;
+ var p:0..nest_size; {index into |nest|}
+ begin nest[nest_ptr]:=cur_list; p:=nest_ptr;
+ while abs(nest[p].mode_field)<>vmode do decr(p);
+ scan_optional_equals; scan_int;
+ if cur_val<0 then
+ begin print_nl("! Bad \prevgraf");
+ help1("I allow only nonnegative values here.");
+ int_error(cur_val);
+ end
+ else begin nest[p].pg_field:=cur_val; cur_list:=nest[nest_ptr];
+ end;
+ end;
+
+61. \clubpenalty is split off from \widowpenalty. (12/23/82)
+
+62. Bad bug in module 1005 (12/24/82)
+(must not go to reswitch if \par is a macro!)
+Instead of setting cur_cmd and cur_chr and goto reswitch, just back_input
+and set token_type:=inserted.
+
+63. \openin not to prompt if file not present (12/25/82)
+Change lines -4 and -3 of module 1183 to:
+ if a_open_in(read_file[n]) then read_open[n]:=just_open
+ else read_open[n]:=closed;
+
+64. New \jobname primitive (12/25/82)
+It is added to the "convert" command in the obvious way.
+In conv_toks, the relevant code is
+ if job_name=0 then open_log_file
+before "selector" is changed and
+ print(job_name)
+after.
+
+65. Better error recovery for math-only things (12/25/82):
+In module 952, don't goto reswitch after insert_dollar_sign.
+In module 954, first back_input, then set cur_tok, and
+don't bother to set cur_chr or cur_cmd; ins_error instead of back_error.
+
+66. Module 1163, line 3, insert "scan_optional_equals" (12/25/82).
+Also make \the\parshape allowed.
+
+67. The location where an \if begins is stacked (12/26/82)
+so that a better error message can be given for \end while \if is incomplete.
+This means two-word nodes instead of one-word nodes in the if stack.
+
+68. Change 30 is extended to \insert, \vadjust, \valign, \output (12/26/82)
+The one-time-only paragraph parameters are now cleared by a
+subroutine called normal_paragraph; hang_after is also set to 1.
+The essential change being made now is to call normal_paragraph in modules
+704, 932, 1009, and 1076.
+
+69. \pagetotal and \pagegoal are added (12/27/82)
+The changes are analogous to, but simpler than, those for \prevgraf.
+
+70. Tracing of page-optimization calculations (12/27/82)
+A bunch of print commands are added to modules 897, 914, and 918,
+activated if tracing_pages>0. Also, \tracingparagraphs is separated
+from \tracingstats.
+
+**** The changes above have been incorporated into version 0.91 of TeX
+
+71. The build_page procedure is broken in two parts (Dec 31, 1982)
+by making module 919 into a procedure called fire_up.
+
+72. \ifeven1\else is made legal by introducing if_code (Dec 31, 1982)
+This improves part of the code in change 45; if_limit has a specific
+value that recovers automatically from a former syntax error.
+
+73. Improvement to alignments when columns don't occur (Dec 31, 1982)
+Delete module 711; and where it was used in module 708, say this:
+ begin fin_col:=true; return;
+ end;
+Also, in module 717, replace the statement "width(q):=0" by
+"<Nullify width(q) and the tabskip glue following the current column>"
+where that new module has the following code:
+ begin width(q):=0; r:=link(q); s:=glue_ptr(r);
+ if s<>zero_glue then
+ begin add_glue_rel(zero_glue); delete_glue_ref(s);
+ glue_ptr(r):=zero_glue;
+ end;
+ end
+
+74. Better error message in overfull alignment (Dec 31, 1982)
+In module 717, don't set both height and width; set the height zero.
+Then in module 719, switch width to height if necessary. (People didn't
+understand the previous error messages, and I couldn't blame them.)
+
+*** The changes above have been incorporated into version 0.92 of TeX82
+*** (which was the last version of 1982, completed 11:59pm on December 31)
+ The first changes after 1982
+
+75. Modules 961 and 962 should be one module. (Jan 3, 1983)
+Also, the absolute constant 100 is replaced, and the test becomes
+ if ((page_head=page_tail)and(head=tail)and(dead_cycles=0))or
+ (dead_cycles>max_dead_cycles) then
+
+76. Surprise bug: module 1010. (Jan 3, 1983)
+The case "if head<>tail" needs an else clause: else pop_nest.
+Also remove the "<Scan an optional space>" in that module.
+
+77. Improvement to change 22 (Jan 4, 1983)
+In module 996, use box_max_depth from inside the \vbox:
+ @!d:scaled; {maximum depth}
+ begin d:=box_max_depth; unsave;... vpackage(...,d);
+
+78. \groupbegin and \groupend changed to \begingroup and \endgroup (Jan 4, 83)
+
+79. \deadcycles made accessible (Jan 4, 83)
+
+80. New calculations for split insertions (Jan 4, 83)
+In module 918, we now work with natural width, and add in the page depth too:
+The line "else w:=x_over_n..." is changed to
+ else begin w:=page_goal-page_total-page_depth;
+ if count(n)<>1000 then w:=x_over_n(w,count(n))*1000;
+ end;
+(Note that cur_page_height and cur_page_depth and page_size have been
+renamed page_total, page_depth, and page_goal, in accordance with new syntax.)
+
+*** The changes above have been incorporated into Version 0.93
+
+81. Old bug finally unearthed by PHY (Jan 6, 1983)
+insert "incompleat_node:=null;" after "push_nest;" in module 1097.
+
+82. Extension of change 69: \pageshrink, etc. added (Jan 6, 1983)
+
+83. \floatingpenalty and \insertpenalties added (Jan 6, 1983)
+Also, the insertion nodes now have a new format, so that the values of
+\floatingpenalty, \splittopskip, and \splitmaxdepth can be stored with
+each insertion; this requires the obvious changes in several places:
+a) Module 136, ins_node has fields float,depth,height,ins_ptr,split_top_ptr
+b) Module 184, these fields are displayed
+c) Module 198, ins_node case, also delete_glue_ref(split_top_ptr(p))
+d) Module 202, ins_node case, also add_glue_ref(split_top_ptr(p))
+e) Modules 226-228, new integer parameter \floatingpenalty
+f) Module 883, add parameter d to vert_break subroutine
+g) Module 885, use d instead of split_max_depth
+h) Module 890, argument split_max_depth to call of vert_break
+i) Module 894, change width to height (better name)
+j) Module 900, initialize insert_penalties here, not in 901
+k) Module 914, cost to be awful_bad if insert_penalties>=10000
+l) Module 916, add float(p) to insert_penalties if type(r)<>inserting
+m) Module 916, subtract page_depth from delta
+n) Module 918, subtract page_shrink from delta
+o) Module 918, argument depth(p) to call of vert_break
+p) Module 921, insert_penalties:=0, save split_top_skip before calling 925
+q) Module 921, restore split_top_skip before calling 924
+r) Module 928, set split_top_skip:=split_top_ptr(p) before prune_page_top
+s) Module 929, after q:=p, incr(insert_penalties)
+t) Module 929, before free_node, delete_glue_ref(split_top_ptr(p))
+u) Module 933, clear insert_penalties before calling 934
+v) Module 978, more local variables needed
+w) Module 1010, build the newfangled ins_node
+
+84. Scanner goes to new_line when <return> is category 13 (Jan 7, 1983)
+Insert state:=new_line in module 323 just before the reference to module 339;
+and delete "state:=new_line;" from modules 328 and 330 (and their names).
+
+85. Distinguish between user \kern and font \kern (Jan 9, 1983)
+The nontrivial parts of this are to change "type(s)<>kern_node" to
+"(type(s)<>kern_node)or(subtype(s)<>normal)" in modules 809 and 810;
+and to say "kern_node: if subtype(s)=explicit then goto done4" in module 812.
+The \kern primitive now has "explicit" instead of "normal" in 967.
+
+86. "ignorespace" becomes "ignorespaces" (Jan 9, 1983)
+
+87. Don't omit a blank space after \def, \message, \mark, etc. (Jan 9, 1983)
+<Scan an optional space> is removed from modules 431, 848, 874.
+"info(r):=space_token;" and "link(r):=get_avail; r:=link(r);" removed from 1278.
+
+*** The above changes appear in Version 0.94.
+ Version 0.95
+
+88. New active characters in math mode (Jan 12, 1983)
+In module 1062, add a label "restart" and change lines -3 and -2 as follows:
+fam(p):=(c div 256) mod 16;
+if c>=var_code then
+ if fam(p)<8 then fam(p):=cur_fam
+ else begin <Convert c to an active character whose equivalent is
+ ready to be scanned next>; goto restart;
+ end;
+In module 1064, the body of the procedure becomes
+begin if c>='4000 then <Convert c ...> else <the former body>; end
+And there's a new module:
+<Convert c...>=
+begin cs_ptr:=(c mod 128)+active_base;
+cur_cmd:=eq_type(cs_ptr); cur_chr:=equiv(cs_ptr); x_token; back_input;
+end
+
+89. Surprise bug: $1-$ treated the - as binary (Jan 15, 1983)
+New module <Convert a final bin_noad to an ord_noad>=
+ if r_type=bin_noad then type(r):=ord_noad
+is called in module 649 before the call of 678, and in module 651
+in the place where that code already appears.
+
+90. Another oversight (Jan 15, 1983)
+Add "space_factor:=1000;" after "push_nest" in modules 1027, 1029
+
+91. And a more embarrassing one (Jan 16, 1983)
+I forgot "spotless:=false;" at the beginning of the procedure in module 80.
+But while fixing this, I decided to make it more general since IBMers want
+a return code at the end of the job. So there's a history variable that has
+four values: spotless, warning_issued, error_message_issued, fatal_error_stop.
+a) module 75: declare history, define spotless etc.
+b) module 76: initialize history:=spotless
+c) module 80: if history<error_mess... then history:=error_mess...
+d) 80 and 90: history:=fatal_error_stop before jump_out
+e) 92: if history<error_message_issued then
+f) 232: if history=spotless then history:=warning_issued
+g) 1243: if history=warning_issued then print_nl("(see the log...)")
+
+92. "This can't happen" could happen, so there's new error recovery (Jan 16, 83)
+In module 933, replace the call on confusion by a call of the following module:
+ @ @<Recover from an unbalanced output routine@>=
+ begin print_nl("! Unbalanced \output routine");
+ help2("Your sneaky output routine has fewer real {'s than }'s.")@/
+ ("I can't handle that very well; good luck."); error;
+ repeat get_token;
+ until loc=null;
+ end
+In module 1278, replace the call on confusion by a call of:
+ @ @<Recover from an unbalanced write command@>=
+ begin print_nl("! Unbalanced \write command");
+ help2("On this page there's a \write with fewer real {'s than }'s.")@/
+ ("I can't handle that very well; good luck."); error;
+ repeat get_token;
+ until cur_tok=end_write_token;
+ end
+
+93. String overflow clobbered the log file (Jan 18, 1983)
+Also, "confusion" before log file open would cause problems. Also start_input
+calling open_log_file calling prompt_file_name calling fatal_error!
+To fix these anomalies, open_log_file no longer calls prompt_file_name,
+if interaction<scrollmode; instead, it terminates after printing what went
+wrong. Also, the fatal_error and overflow and confusion procedures call
+the following new subroutine:
+procedure normalize_selector;
+begin if job_name>0 then selector:=term_and_log else selector:=term_only;
+if interaction=batch_mode then decr(selector);
+if job_name=0 then open_log_file;
+end;
+
+94. \ifeof\fi loops infinitely (Jan 18, discovered by Lamport)
+Change 72 converted such a \fi to <space>\fi. Now it is converted to \relax\fi.
+
+95. \limitswitch changed to \displaylimits et al. (Jan 18, 1983)
+[Incidentally, this fixes a bug in the former positioning of \int\limitswitch]
+a) module 608: subtype of Op can be normal, limits, or no_limits.
+b) 620: display the subtype if not normal
+c) 667: new logic decides limits before looking at the operand (and the operand
+ is now called the nucleus); the italic correction is removed only if
+ it should not be put back
+d) 1069: subtype(tail):=cur_chr
+
+96. Minor changes to math in unusual cases (Jan 19, 1983)
+a:delete "if height(y)<=0 then height(y):=default_rule_thickness" in module 658
+b:move "if thickness(q)=default..." from module 664 to module 661
+c:delete module 1081, that error message isn't worth the bother
+d:in module 1062, char_num case, c:=math_code(cur_val) (if cur_val<128)
+e:in module 1064, a similar change
+
+97. Bad spacing from change 6 is corrected (Jan 19, 1983)
+underline, overline, radical, vcenter, and accent noads now revert to type
+Ord instead of type Inner.
+{...} produces type Ord also.
+There's a new primitive \mathinner.
+The new_noad function now produces an ord_noad (change its calls accordingly).
+And the default is changed in module 679 to t:=ord_noad; the fraction_noad
+case sets t:=inner_noad, and the (inner_noad,ord_noad) cases swap places.
+
+98. New \mathchoice primitive (Jan 19, 1983)
+a) module 204: new command
+b) 254: math_choice_group
+c) 614: style node three words long, so a choice node can be converted to it
+d) 614a: choice node has four subfields: display_mlist, text_mlits, etc.
+e) 1081a: routines to build a choice_node like 1027-1029 build discretionaries
+
+99. \input moves to syntax from semantics (Jan 19, 1983)
+a) 204, 206: renumber commands
+b) When input is to be expanded, if name_in_progress then insert_relax
+c) 459,464: name_in_pr:=true; begin_name;...end_name; name_in_pr:=false;
+d) 406,461: declare name_in_progress, a global boolean initially false
+
+100. \chardef joins \mathchardef (Jan 19, 1983)
+a) 204: math_only becomes math_given; add new command char_given
+b) 205: new command char_def
+c) 250,251: new primitive char_def
+d) 380: char_given,math_given yield integer after \the
+e) 401: char_given,math_given yield integer in context of integer
+f) 936,943: char_given treated like other_char
+g) 1062,1064: same, if cur_chr<128, else assume math_code(x)=x
+h) 1143,1144: char_def is analogous to math_char_def
+
+101. \unbox becomes \unhbox,\unvbox; also add \unhcopy (Jan 19, 1983)
+Module 1020 changes in the obvious way.
+
+102. \spacefactor, \pagetotal, etc. move to prefixed_command (Jan 20, 1983)
+
+103. \hrule in horizontal mode, \vrule in vertical mode: switch modes (Jan 20)
+
+104. \globaldefs parameter, affects prefixed_command (Jan 20, 1983)
+
+105. After looking at frequency counts, some optimizations made (Jan 21, 1983)
+a) "fast_get_avail" and "fast_store_new_token" introduced to speed up the loops
+in modules 360 and 431.
+b) some procedure call overhead eliminated in begin_token_list, end_token_list,
+back_input, and flush_node_list.
+c) a few if tests changed from "if a and b then" to "if a then if b then".
+
+106. Changes for space efficiency in math constructions (Jan 22, 1983)
+a) In module 1102, mlist_penalties:=(mode>0)
+b) The following code is inserted before "free_node" in module 639 (rebox):
+ if (is_char_node(p))and(link(p)=null) then
+ begin f:=font(p); v:=char_width(f)(char_info(f)(character(p)));
+ if v<>width(b) then link(p):=new_kern(width(b)-v);
+ end;
+c) A new module is called just before clean_box exits:
+<Simplify a trivial box>=
+q:=list_ptr(x);
+if is_char_node(q) then
+ begin r:=link(q);
+ if r<>null then if link(r)=null then if type(r)=kern_node then
+ begin free_node(r,small_node_size); link(q):=null;
+ end;
+ end
+
+107. Oversight in rebox routine (module 639) corrected (Jan 22, 1983)
+if type(b)=vlist_node then b:=hpack(b,natural);
+
+108. Module 217 clobbers eqtb[bad_font_ident] set in change 26q (Jan 22, 1983)
+Decided to fix this by making \nullfont a primitive.
+This means the procedure missing_font can be deleted, and the test for
+undefined font can be removed from the inner loop.
+(This reflects a rather dramatic change from TeX80, where
+a missing font was a fatal "Whoa" error!)
+Note: I thought I could delete module 646, but realized that it still provides
+a useful error message. The dump/undump routines (cf. change 26ee) now
+dump the null_font information too, as its parameters can be changed.
+
+109. End of program now lists all incomplete \ifs (Jan 24, 1983)
+
+110. Alignment preamble setup to allow \halign\lb (Jan 29, 1983)
+The statement "align_state:=-1000000;" is inserted near the beginning
+of module 695 (and the comment about align_state=-999999 is deleted).
+The constant -999999 is changed to -1000000 in modules 700 and 701.
+
+111. Forgot to test is_char_node(r) (Jan 30, 1983)
+in the <Simplify a trivial box> code of change 106c.
+By coincidence, this was caught since somebody used font number 11
+in the second character of a list of length 2!
+
+112. Improved format for stats at end of run (suggested by DRF, Jan 30, 1983)
+Module 1242 changes; nothing subtle.
+
+**** The changes above have been incorporated into version 0.95
+ Version 0.96
+
+113. space after one-symbol control sequences NOT to be ignored unless
+the catcode of that symbol is letter or spacer. (Jan 30, 1983)
+The name of module 334 changes slightly; the state is set based
+on cat_code(cur_chr), shortly after label start_cs.
+A new variable cat is introduced and set to cat_code(cur_chr) in modules
+334 and 336.
+
+114. trailing spaces removed on all lines of input (Jan 30, 1983)
+last_nonblank is added to input_ln routine (module 31)
+
+115. mmode+accent gives error but assumes math_accent (Feb 3, 1983)
+This goes into math_ac procedure (module 1075); module 1035 is eliminated.
+
+116. \iftrue and \iffalse (Feb 5, 1983)
+Simple addition of two new conditions.
+
+117. Bad calculation of size for \left and \right (Feb 6, 1983)
+In module 680, the first assignment to delta should be
+ delta:=(delta1 div 500)*delimiter_factor.
+
+118. \delimitershortfall (new name) replaces \delimiterlimit. (Feb 12)
+
+119. \abovewithdelims.. to be equivalent to \above (Feb 12)
+Module 1089 revised so that the delimiters are scanned before the dimension.
+
+120. Remove the kludgy math codes introduced in change 88 (Feb 12)
+\fam becomes a normal integer parameter
+and \mathcode stored with min_halfword added
+and \mathcode allowed to be 32768
+and current family is substituted only when it's in range.
+Also the initialization of mathcode for letters now specifies family 1.
+(This implies a dozen or so obvious revisions.)
+
+121. Bug in module 1152: max spacefactor is 32767 (Feb 12)
+also module 1013 gets a restricted range
+
+122. Octal output to be replaced by hexadecimal. (Feb 14)
+
+123. Forgot to include char_given in module 1037, re change 100 (Feb 14)
+
+124. vmode+valign,hmode+halign made legal transitions (Feb 17)
+
+125. \tracingrestores (Feb 18)
+This involves a lot of new, rather tedious code that's interspersed with the
+eqtb definitions (the body of a procedure called show_eqtb).
+
+126. New error message for hmode+hrule in -hmode. (Feb 25)
+head_for_vmode now suggests \leaders in this case. [improves change 103]
+
+127. under/overfull boxes in alignment: Better message. (Feb 27)
+par_begin_line becomes pack_begin_line, and it is set (negative) in
+module 719, read in modules 590 and 601.
+
+128. New \xcr feature. (suggested by Lamport comments, Mar 4)
+align_peek (module 702) ignores it, otherwise it acts like ordinary \cr.
+
+129. In stats, subtract out TeX's own string requirements (Mar 4)
+(cf. change 112)
+init_pool_ptr and init_str_ptr variables added in obvious way
+(declared in module 39, set in module 1240, used in module 1242)
+
+130. \everyhbox and \everyvbox. (Mar 6)
+The modifications are obvious; for example, module 1076 gets the statement
+ if every_vbox<>null then begin_token_list(every_vbox,every_vbox_text);
+and a similar pair of statements goes into module 993.
+
+131. Precaution in module 1105 error recovery (March 9, 1983)
+Change the first test to "if (a=null)or danger", in order to
+avoid accessing math_quad when the symbol fonts aren't known to be present.
+
+132. Installed float and unfloat to aid portability (suggested by HWT, 3/7/83)
+
+133. \dispskip becomes \abovedisplayskip and \belowdisplayskip (3/9/83)
+Also \dispaskip becomes \abovedisplayshortskip; \dispbskip similar.
+
+134. \romannumeral separated from \number (suggested by FY, March 10)
+Obvious changes; print_roman_int now accepts negative input but gives
+no output in such cases (the test in module 68 becomes "if n<=0 then return").
+
+135. scan_keyword to ignore leading spaces (Mar 12)
+In module 375, change "else begin" to
+"else if (cur_cmd<>spacer)or(p<>backup_head) then begin".
+
+136. Update to change 112 (Mar 14)
+Module 1242 now uses write and write_ln directly (saves space and time).
+
+137. Another change to page-break cost (suggested by Lamport, March 16)
+Cf. change 83k above; the relevant lines of module 914 now read
+ if b<awful_bad then
+ if pi<=eject_penalty then c:=pi
+ else if b<inf_bad then c:=b+pi+insert_penalties
+ else c:=deplorable
+ else c:=b;
+ if insert_penalties>=10000 then c:=awful_bad;
+and a similar change (but without insert_penalties) occurs in module 887.
+
+**** The changes above have been incorporated into version 0.96 (March 16, 1983)
+ Version 0.97
+
+138. \everyjob (suggested by FY, March 18)
+Module 936: main_control inserts every_job as its first action.
+
+139. Improved printout of macro definitions (March 19)
+In module 278, simply treat left_brace like right_brace.
+(This corresponds to the way the manual describes parameter matching.)
+
+140. Omit blanks as non-delimited parameters (March 19)
+In module 360, replace
+ else store_new_token(cur_tok);
+by
+ else @<Store the current token, but |goto continue| if it is
+ a blank space that would become an undelimited parameter@>;
+Then define @<Store the current token...@> to be:
+begin if cur_tok=space_token then
+ if info(r)<=end_match_token then
+ if info(r)>=match_token then goto continue;
+store_new_token(cur_tok);
+end
+
+141. Minor patch in module 386, catches mu_glue (March 21)
+begin cur_val:=zero_glue; cur_val_level:=glue_val;
+if not is_char_node(tail)and(mode<>0) then
+ begin if type(tail)=glue_node then
+ begin cur_val:=glue_ptr(tail);
+ if subtype(tail)=mu_glue then cur_val_level:=mu_val;
+ end;
+ end
+else if (mode=vmode)and(tail=head)and(last_page_glue<>max_halfword) then
+ cur_val:=last_page_glue;
+end
+
+142. Patch in module 699, \span expands only one level (March 21)
+(See previous change #42.) The "get_x_token" in the code
+while (cur_chr=span_code)and(cur_cmd=tab_mark) do get_x_token;
+should be replaced by:
+ begin get_token;
+ if cur_cmd>max_command then
+ begin expand_calls; get_token;
+ end;
+ end
+
+143. Single # in \tokens, \message, etc. (March 22)
+(The previous rule was really bad in connection with \uppercase,
+or with \write when #'s had to be given four times!)
+Module 437 should be prefaced with "if macro_def then".
+And get_token should become get_x_token there, if xpand is true.
+
+144. Keyword "to" required in \read. (March 22)
+This will avoid the common error of a missing space before the \cs.
+Also the stream number can be out of range, for terminal input.
+The stream number in a \write can be out of range too, for terminal output.
+Modules 1145 and 1258 and 1280, etc., change in the obvious ways.
+
+145. \ifeven<countnumber> replaced by \ifodd<number> (March 26)
+This makes the language more consistent.
+
+146. Big surprise bug relating to \if\if aabc\fi (March 26)
+(related to change 45; the possibility that cur_if might not be the
+correct one when the conditional is evaluated was discovered today)
+The main change is to add the change_if_limit procedure, and to add
+the variable save_cond_ptr and the code that now depends on it.
+
+147. \if and \ifcat should tolerate primitives (March 28)
+
+148. "absent" becomes "void", a better word (March 28)
+
+149. Module 990: \lastbox to clear the shift_amount (March 28)
+since I don't want to figure out what it means in all cases (\vsplit, etc)
+
+150. print_err("...") takes the place of print_nl("! ...") (DRF, March 29)
+And wake_up_terminal is introduced in module 34, and used in modules
+37, 70, 341, 441, 457, 463, 1210, 1240, 1246.
+
+151. \halign extended to periodic preambles (April 1)
+Modules 688--690: cur_loop introduced, stacked/unstacked
+Module 695: cur_loop initialized
+Module 700: cur_loop set up
+Module 708--709: cur_loop used and advanced
+
+152. \leaders to align by the smallest enclosing box. (April 1)
+Module 549: new local variable left_edge, initialized to cur_h
+Module 557: cur_h:=left_edge+leader_wd*((cur_h-left_edge) div leader_wd)
+Module 559: new local variable top_edge, initialized to cur_v-height(this_box)
+Module 566: analogous to 557
+
+153. hyphenation after whatsits is OK (April 1)
+In module 809, skip past whatsits
+
+154. \par in vertical mode should build_page (April 2)
+vmode+par_end moves from 952 to 1005
+
+155. Clear aux to zero in module 703 (April 2)
+
+156. Digits will switch families (April 4)
+(initialize their math codes differently in module 225)
+
+157. Refinement to correction 83m (April 7)
+the test for not splitting should be
+ if ((h<=0)or(h<=delta))and(height(p)+height(r)<=dimen(n)) then
+
+158. (re 128) \xcr renamed to \crcr, at Lamport's request. (April 8)
+
+159. Better error recovery in runaway preamble (April 11)
+Module 319, aligning: set align_state:=-1000000
+
+160. \read to get balanced braces (April 12)
+Module 440 changed to look more like 431.
+Module 443 gives error message if the \read goes off end of file.
+Module 319 removes \outer from forbidden control sequence that is \read.
+
+161. Bug found by Jim Sterken (April 14)
+Module 798 can make q a char_node, so module 794 needs this patch:
+ if not is_char_node(q) then
+to be inserted just before "if (type(q)=math_node)..."
+
+162. \uppercase and \lowercase to apply to all characters (April 15)
+module 1196 changes; also I put active_base before single_base in eqtb
+
+**** The changes above have been incorporated into version 0.97 (April 16, 1983)
+**** except the part of 144 about \read-1 was forgotten and put in 0.98 later
+ Version 0.98
+
+163. change small_number to 0..65 in module 814 (found by DRF, April 17)
+
+164. improved error recovery in module 1166 (suggested by DRF, April 17)
+after error: repeat get_token; until cur_cmd=right_brace; {flush the patterns}
+
+165. improved \read from terminal (suggested by Todd Allen at Yale, May 1)
+I had forgotten to implement the extended stream numbers in change 144.
+Also, the prompt is now omitted if n<0.
+
+166. \write n writes only to the log file, if n<0. (May 18)
+
+167. Unified syntax for parameters and registers. (May 18)
+a) Command code changes: def_font moved to before prefix;
+ register eliminated; set_register renamed register and
+ moved (with adv_register...div_register) to before prefix;
+ min_internal and max_internal defined.
+b) scan_the renamed scan_something_internal, and it acts on cur_tok.
+c) scan_int, scan_dimen, scan_glue simplified accordingly; instead of
+ testing for cur_cmd=the or cur_cmd=register, they now test
+ cur_cmd versus min_internal and max_internal.
+d) ins_the is removed.
+e) \minusthe is removed. Consequently "the_toks" needs no parameter.
+
+168. new parameters \hoffset, \voffset (May 18)
+
+169. \everycr (suggested by Spivak, installed May 24)
+
+170. \countdef, \dimendef, etc. (suggested by DRF long ago, installed May 25)
+Straightforward change to internal representation of assign_int and similar
+commands, so that the chr part is now a pointer to the eqtb location.
+
+171. \advance, \multiply, \divide (suggested by FY, installed May 25)
+
+172. \hyphenchar (May 25)
+A new command assign_font_int is introduced, and incorporated into
+scan_something_internal and prefixed_command in the usual way.
+The hyphen_char array entries are initialized in modules 483 and 506,
+allocated in 482, used in modules 809 [is hyphenation to be suppressed?],
+828 [insert the hyphen character], 946 [insert discretionary node
+following hyphen], 1027 [implementation of \-].
+hyphen_char[k] is dumped and undumped in modules 1229 and 1230.
+
+173. \skewchar (May 25)
+Loaded and fetched with the same command code as \hyphenchar.
+Module 659 gets a dozen more lines of program, to compute the skew.
+
+174. \noexpand (May 25)
+This involves: introduction of no_expand and dont_expand command codes;
+frozen_dont_expand as an internal marker; small change to get_next
+when that marker is sensed; change to processing of \if and \ifcat;
+implementation of no_expand in the expand_calls subroutine.
+I also changed the name of expand_calls to "expand".
+
+175. \meaning (May 25)
+This adds one case to the "convert" command.
+print_meaning is a new subroutine, using code that was in show_whatever.
+
+176. "dm" and "vu" are out, ".5\hsize" is in (May 25)
+Straightforward changes to module 416.
+
+177. \texinfo f n becomes \fontdimen n f (May 25)
+
+178. \afterassignment (suggested by ARK, May 27)
+
+179. \chardef\xx=5\xx shouldn't say that \xx is undefined (May 27)
+
+180. \relax to be ignored like spaces in math mode (May 28)
+and in a few other places: <Get next non-blank non-relax non-call token>
+is now used in modules 379, 988, 994, 1062, 1070, 1134, 1179
+(i.e., scan_left_brace, scan_box, scan_math, scan_delimiter, prefixed_command,
+do_assignments, and just after \leaders).
+
+181. improve \mathaccent wrt sub/superscripts (sugg by HWT, May 30)
+
+182. its_all_over: remove dead_cycles>max_dead_cycles (May 30)
+Modules 961 and 962 are combined and simplified.
+
+*** The changes above were installed into version 0.98 on May 31, 1983 ***
+ Version 0.99
+
+183. \mark and \insert and \vadjust allowed in restricted hmode (Jun 3)
+also in math; this is a comparatively big change
+[at present, \mark in a display causes TeX to crash with "can't happen"!]
+modules 652 and 679: mark_node,ins_node,adjust_node now permitted
+modules 576 and 578: t becomes a global variable adjust_tail
+modules 802 and 1110: new calling sequence to get the adjustments
+modules 254, 986, 993, 995, 996: adjusted_hbox_group for the new kind of hbox
+modules 698--690: cur_head and cur_tail added to alignment stack
+modules 703, 712, 715: adjustments gathered and appended during \halign
+
+184. \ht, \wd, and \dp (Jun 6)
+
+185. When displaying noads, use ^ and _ instead of ( and [ (Jun 6)
+
+186. A..F in hex constants could be otherchar as well as letter (Jun 6)
+
+187. Remove <scan optional space> from module 417 (Jun 7)
+(it was redundant code)
+
+188. \mkern .5\thinmuskip and \mkern\thinmuskip should be legal (Jun 7)
+
+189. 2.5\space\space\dimen0 should work (Jun 7)
+previously it worked after "plus" or "minus" only!
+
+190. <font identifier> to allow also \font for current font (Jun 7)
+"if cur_cmd=def_font then f:=cur_font else" added to module 507
+
+191. \gdef not to be global when \globaldefs<0 (Jun 7)
+"and (global_defs>=0)" added to module 1139
+
+192. \advance\spaceskip by-\spaceskip should yield zero_glue (Jun 7)
+the procedure trap_zero_glue is culled from module 1147
+
+193. \show should work with any token (Jun 7)
+
+194. \tokens to become 256 registers (Jun 8)
+\toks and \toksdef added in the straightforward way
+(this affects mainly eqtb, scan_something_internal, prefixed_command)
+
+195. allow \indent in math mode (Jun 8)
+also, \valign in math mode to give missing $ error
+modules 1001 and 1043 disappear; 1004 is generalized slightly.
+
+196. remove redundant code (Jun 8)
+In module 1044, there's no need to check cur_group and call off_save.
+Similarly in module 1053.
+
+197. new_write_whatsit shouldn't allow \openout-1, \closeout-1 (Jun 8)
+(simple change to module 1258)
+
+198. \lastbox should give error in math mode (Jun 8)
+(simple change to module 990)
+
+199. \leaders not followed by proper glue should be back_error (Jun 9)
+[I made the change; TRIP should test this error message!]
+
+200. Module 702, correction to beginning of \noalign (Jun 9)
+if mode=-vmode then normal_paragraph;
+
+201. After alphabetic constant, expand the optional space (Jun 10)
+
+202. Set space_factor:=1000 after rule or constructing an accent (Jun 12)
+That's in modules 964 and 1036.
+
+203. blunder in module 783 (caught by Jim Sterken, fixed Jun 14)
+disc_width:=0 needs to be set before testing if s=null
+(A real bug that existed since the beginning! It showed up on page 37
+ of the September 1982 TRIP manual; my hand-checking was incomplete...)
+
+204. Change to optional spaces after <dimen> (Jun 14)
+The optional space will now be analogous to that after <number>.
+
+205. Fix conflict between \output and \everydisplay (Jun 14)
+In change 49, I should have inserted every_display before calling build_page.
+
+206. Overflow errors to be consistent with statistics reporting (Jun 17)
+(See change 129.)
+
+207. \tracing switches to be all positive vs nonpositive (Jun 17)
+
+*** The changes above were installed into version 0.99 on June 19, 1983 ***
+ Version 0.999
+
+208. \catcode`\%=14 to be done by INITEX (Jun 20)
+
+209. \par in vmode to clear parshape etc. (Jun 21)
+Module 1005, which contains vmode+par_end, now calls normal_paragraph.
+
+210. Improvement to change 39 (Jun 21)
+Module 593, overfull_rule not appended if solely due to hbadness
+
+211. Alignment bug allows glue_set to be less than -1! (Jun 21)
+Modules 723 and 724 need to be patched.
+
+212. Correction to change 134 (found by Debby Clark, Jun 22)
+Module 68: n to be declared integer, not nonnegative_integer.
+
+213. "by" to be optional (suggested by Lamport, Jun 22)
+Module 1158 disappears
+
+214. Module 1235, slight change in format_ident message (Jun 24)
+
+215. New measures needed to thwart trickery (Jun 25)
+Glue set values computed by \span could have been brought into
+TeX's registers via, e.g., \valign and \vsplit; so the
+"kern" idea of module 722 is insufficient and should be abandoned.
+Extra boxes and glue are added; this has additional virtue
+of perfect accuracy in alignment of vertical rules.
+The main change is to introduce a new module after 722, in which s and t
+are updated for spans, and to eliminate the "equivalent kern" code from 719
+(the corresponding glue calculations are now done in the new module).
+
+216. leaders to affect height/width of their boxes (Jun 25)
+Module 583 splits into two parts (one for hpack, one for vpack).
+
+217. \unskip permitted a little more often (Jun 28)
+Module 1018 reports error only if last_page_glue<max_halfword.
+
+218. {\bf A} in math: not to make a box (Jul 1)
+Module 1093 changes as follows: info(saved(0)):=fin_mlist(null) becomes
+p:=fin_mlist(null); info(saved(0)):=p; if <simple case> then <simplify>.
+
+219. "scaled" feature added to \font input.
+
+220. \mathaccent still not working right? (Jul 2)
+Change 181, I forgot to correct delta when box x changed.
+
+221. Remove confusion possible on file of length 1 (found by Lamport)
+Correction suggested by DRF, July 2: introduce bypass_eoln
+
+222. Allow things like ^^b. (July 4)
+This simplifies modules 332 and 335; also affects 48 and 49.
+And (surprise) 58.
+
+223. \escapechar \defaulthyphenchar \defaultskewchar \endlinechar (July 4)
+to make TeX less dependent on the character set
+A lot of error messages are now broken up so that they use print_esc
+
+224. erstat added for file opening/closing (by DRF, July 7)
+That's module 27.
+
+225. \tracingpages output to show total glue too (July 11)
+Added a procedure called print_page_totals, used also by show_activities.
+Broke modules 214 and 896 into two modules each.
+
+226. Guard against insertion into an hbox (July 11)
+A new procedure ensure_vbox is called in modules 917 and 925.
+
+227. <tokenvar>=<tokenvar> allowed (July 11)
+
+228. \errhelp parameter added (July 11)
+
+229. get_preamble_token should look at global_defs (July 11)
+Module 699 is patched.
+
+*** The above changes installed in preliminary version 0.999 on July 12, 1983,
+but I decided later in the day to do a few more things:
+
+230. \string, \noexpand, \meaning to allow \outer (Todd Allen, July 12)
+
+231. \the to be an expandable control sequence (July 12)
+Several things in the language are cleaned up:
+a) \the\tenrm replaced by \fontname\tenrm [\fontname<font>]
+b) when expanding edef, etc., result of \the still expanded only only level
+c) expansion after \def not inhibited, since \noexpand is now present
+d) \the\the disallowed.
+
+232. \unhbox and \unhcopy allowed in math mode if the box is void (July 12)
+Module 1020 is extended to handle this case.
+
+233. Value of default_rule was incorrectly rounded (July 16)
+Module 424: default_rule=26214 {.39999 pt}
+
+234. \mathaccent still not right! (July 16)
+I need to make the final height the max of (height of accented letter w/o
+superscript, height of unaccented letter w superscript).
+
+235. \newlinechar parameter (July 16)
+simple change to print subroutine
+
+236. boxes and rules to be allowed in discretionaries (sugg by HP man, July 16)
+in fact this simply requires omitting of the prohibition (in module 1031)
+and a few more cases equivalent to kern_node in modules 754, 755, 784.
+
+237. \tracingcommands to show all expandable tokens (July 16)
+
+238. \char to be allowed in \hyphenation list (July 16)
+Module 848 changes in the obvious way.
+
+239. \aftergroup (July 16)
+A new save_type.
+
+240. Curtail running dimensions inside alignments (sugg by ARK, July 16)
+Module 720.
+
+241. Strange pattern data could cause PASCAL error (found July 17)
+Put "if hc[1]=127 then hyf[0]:=0;" at beginning of module 878.
+
+242. hc codes for hyphenation are one lower (July 17)
+e.g., hc[...]:=lc_code(...)-1. This makes code 127 impossible to match.
+
+243. whatsits also allowed after hyphenatable words (July 17)
+Module 812.
+
+244. \/ makes an explicit kern (July 17)
+"subtype(tail):=explicit" in module 1023.
+
+*** Version 0.999 installed July 17, 1983.
+But later in the day I decided to do a few more things:
+
+245. Lowercase letters allowed in file names (July 18)
+In module 452, omit the conversion to uppercase.
+
+246. "No output file" becomes "No pages of output." (July 18)
+Module 571.
+
+247. QRS on error leads to confirmation message (sugg by ARK, July 18)
+Module 84.
+
+*** The real version 0.999 finally installed July 18.
+ * Version 1.0
+
+(Changes made after the Version 0.999 listing of TeX82)
+(Henceforth the "final" module numbers are used)
+
+248. Module 1215, allow space in \read n to \cs (by FY, July 25, 1983)
+@x patch in get_r_token routine
+begin restart: get_token;
+@y
+begin restart: repeat get_token;
+until cur_tok<>space_token;
+@z
+
+249. Module 498, we must stack the current if type (FY, July 27)
+@x patch in conditional routine
+begin @<Push the condition stack@>;@+save_cond_ptr:=cond_ptr;@/
+@y
+@!this_if:small_number; {type of this conditional}
+begin @<Push the condition stack@>;@+save_cond_ptr:=cond_ptr;this_if:=cur_chr;@/
+@z
+Also replace cur_if by this_if in modules 501, 503, 506. The following
+patches do only what is necessary to make things work:
+@x
+ print_cmd_chr(if_test,cur_if);
+@y
+ print_cmd_chr(if_test,this_if);
+@z
+@x
+if cur_if=if_int_code then scan_int@+else scan_normal_dimen;
+@y
+if this_if=if_int_code then scan_int@+else scan_normal_dimen;
+@z
+@x
+if cur_if=if_char_code then b:=(n=cur_chr)@+else b:=(m=cur_cmd);
+@y
+if this_if=if_char_code then b:=(n=cur_chr)@+else b:=(m=cur_cmd);
+@z
+
+250. Module 507, \ifx need not put a control sequence in hash table (July 29)
+@x
+get_token; n:=cs_ptr; p:=cur_cmd; q:=cur_chr;
+get_token; if cur_cmd<>p then b:=false
+@y
+get_next; n:=cs_ptr; p:=cur_cmd; q:=cur_chr;
+get_next; if cur_cmd<>p then b:=false
+@z
+
+251. Module 86, message is lost (noticed by HWT, July 31)
+@x
+print("..."); print_ln; return;
+@y
+print("..."); print_ln; update_terminal; return;
+@z
+
+252. Don't put empty at end of \input file! (Aug 1)
+[This simplifies the rules and the program, and also gets around a bug
+that occurred at the end of files with \endlinechar<0.]
+@x Module 362:
+@ An empty line is inserted at the end of the file, if the last line
+wasn't already empty, because |input_ln| sets |last:=first| when
+it discovers an |eof|.
+@^empty line at end of file@>
+
+@<Read next line of file into |buffer|, or
+ |goto restart| if the file has ended@>=
+begin incr(line); first:=start;
+if not force_eof then
+ begin if input_ln(cur_file,true) then {not end of file}
+ firm_up_the_line {this sets |limit|}
+ else if limit<>start then firm_up_the_line
+ {if |pausing|, the user can add more lines}
+ else force_eof:=true;
+@y
+@ @<Read next line of file into |buffer|, or
+ |goto restart| if the file has ended@>=
+begin incr(line); first:=start;
+if not force_eof then
+ begin if input_ln(cur_file,true) then {not end of file}
+ firm_up_the_line {this sets |limit|}
+ else force_eof:=true;
+@z
+
+*** The changes above went into Version 0.9999, which was widely distributed
+
+253. Ridiculous blunder made in change 146 (found by FY, August 16)
+@x Correction to module 497
+else loop@+begin q:=cond_ptr;
+ if link(q)=p then
+ begin type(p):=l; return;
+ end;
+ if q=null then confusion("if");
+@:this can't happen if}{\quad if@>
+ q:=link(q);
+@y
+else begin q:=cond_ptr;
+ loop@+ begin if q=null then confusion("if");
+@:this can't happen if}{\quad if@>
+ if link(q)=p then
+ begin type(q):=l; return;
+ end;
+ q:=link(q);
+ end;
+@z
+
+254. Minor amendment to stat(s) printing (cf. change 129) (August 16)
+@x in module 1334
+ wlog_ln(' ',str_ptr-init_str_ptr:1,' strings out of ',
+ max_strings-init_str_ptr:1);@/
+@y
+ wlog(' ',str_ptr-init_str_ptr:1,' string');
+ if str_ptr<>init_str_ptr+1 then wlog('s');
+ wlog_ln(' out of ',
+@z
+
+255. Bug in \xleader computations (found by FY, August 18)
+@x in module 592
+@!lq,@!lr:integer; {quantities used in calculations for leaders}
+@y
+@!lq,@!lr,@!lx:integer; {quantities used in calculations for leaders}
+@z
+@x in module 626
+ begin edge:=cur_h+rule_wd;
+ @<Let |cur_h| be the position of the first box, and set |leader_wd|
+ to the spacing between corresponding parts of boxes@>;
+ while cur_h+leader_wd<=edge do
+ @<Output a leader box at |cur_h|,
+ then advance |cur_h| by |leader_wd|@>;
+@y
+ begin edge:=cur_h+rule_wd; lx:=0;
+ @<Let |cur_h| be the position of the first box, and set |leader_wd+lx|
+ to the spacing between corresponding parts of boxes@>;
+ while cur_h+leader_wd<=edge do
+ @<Output a leader box at |cur_h|,
+ then advance |cur_h| by |leader_wd+lx|@>;
+@z
+@x in module 627
+ leader_wd:=leader_wd+lx;
+@y
+@z
+@x in module 628
+cur_h:=save_h+leader_wd;
+@y
+cur_h:=save_h+leader_wd+lx;
+@z
+@x in module 635
+ begin edge:=cur_v+rule_ht;
+ @<Let |cur_v| be the position of the first box, and set |leader_ht|
+ to the spacing between corresponding parts of boxes@>;
+ while cur_v+leader_ht<=edge do
+ @<Output a leader box at |cur_v|,
+ then advance |cur_v| by |leader_ht|@>;
+@y
+ begin edge:=cur_v+rule_ht; lx:=0;
+ @<Let |cur_v| be the position of the first box, and set |leader_ht+lx|
+ to the spacing between corresponding parts of boxes@>;
+ while cur_v+leader_ht<=edge do
+ @<Output a leader box at |cur_v|,
+ then advance |cur_v| by |leader_ht+lx|@>;
+@z
+@x in module 636
+ leader_ht:=leader_ht+lx;
+@y
+@z
+@x in module 637
+cur_v:=save_v-height(leader_box)+leader_ht;
+@y
+cur_v:=save_v-height(leader_box)+leader_ht+lx;
+@z
+Also insert the following in modules 619 and 629:
+@!lx:scaled; {extra space between leader boxes}
+
+256. \/ should apply to ligatures! (August 20)
+@x in module 1113
+var f:internal_font_number; {the font in the |char_node|}
+begin if is_char_node(tail)and(tail<>head) then
+ begin f:=font(tail);
+ tail_append(new_kern(char_italic(f)(char_info(f)(character(tail)))));
+@y
+label exit;
+var p:pointer; {|char_node| at the tail of the current list}
+@!f:internal_font_number; {the font in the |char_node|}
+begin if tail<>head then
+ begin if is_char_node(tail) then p:=tail
+ else if type(tail)=ligature_node then p:=lig_char(tail)
+ else return;
+ f:=font(p);
+ tail_append(new_kern(char_italic(f)(char_info(f)(character(p)))));
+@z
+@x later in that same module
+end;
+@y
+exit: end;
+@z
+
+257. Another debugging hack. (August 27)
+@x module 1339
+15: begin font_in_short_display:=null_font; short_display(n);
+ end;
+@y
+15: begin font_in_short_display:=null_font; short_display(n);
+ end;
+16: panicking:=not panicking;
+@z
+
+258. Redundant code eliminated (August 27)
+Module 531 needn't set and reset name_in_progress [but it's harmless].
+
+259. Bug: \input shouldn't occur during font size spec (Spivak; fixed August 27)
+@x module 1258
+@ @<Scan the font size specification@>=
+@y
+@ @<Scan the font size specification@>=
+name_in_progress:=true; {this keeps |cur_name| from being changed}
+@z
+@x module 1258
+else s:=-1000
+@y
+else s:=-1000;
+name_in_progress:=false
+@z
+
+260. \ifhbox and \ifvbox introduced. (August 27)
+@x module 487
+@d ifx_code=10 { `\.{\\ifx}' }
+@d if_eof_code=11 { `\.{\\ifeof}' }
+@d if_true_code=12 { `\.{\\iftrue}' }
+@d if_false_code=13 { `\.{\\iffalse}' }
+@d if_case_code=14 { `\.{\\ifcase}' }
+@y
+@d if_hbox_code=10 { `\.{\\ifhbox}' }
+@d if_vbox_code=11 { `\.{\\ifvbox}' }
+@d ifx_code=12 { `\.{\\ifx}' }
+@d if_eof_code=13 { `\.{\\ifeof}' }
+@d if_true_code=14 { `\.{\\iftrue}' }
+@d if_false_code=15 { `\.{\\iffalse}' }
+@d if_case_code=16 { `\.{\\ifcase}' }
+@z
+@x
+primitive("ifx",if_test,ifx_code);
+@y
+primitive("ifhbox",if_test,if_hbox_code);
+@!@:if_hbox_}{\.{\\ifhbox} primitive@>
+primitive("ifvbox",if_test,if_vbox_code);
+@!@:if_vbox_}{\.{\\ifvbox} primitive@>
+primitive("ifx",if_test,ifx_code);
+@z
+@x module 488
+ ifx_code:print_esc("ifx");
+@y
+ if_hbox_code:print_esc("ifhbox");
+ if_vbox_code:print_esc("ifvbox");
+ ifx_code:print_esc("ifx");
+@z
+@x module 501
+if_void_code: @<Test if a box is void@>;
+@y
+if_void_code, if_hbox_code, if_vbox_code: @<Test box register status@>;
+@z
+@x module 505
+@ @<Test if a box is void@>=
+begin scan_eight_bit_int; b:=(box(cur_val)=null);
+end
+@y
+@ @<Test box register status@>=
+begin scan_eight_bit_int; p:=box(cur_val);
+if this_if=if_void_code then b:=(p=null)
+else if p=null then b:=false
+else if this_if=if_hbox_code then b:=(type(p)=hlist_node)
+else b:=(type(p)=vlist_node);
+end
+
+261. Serious data structure error (found by Todd Allen, August 29)
+@x module 478 (an error introduced in change 231)
+ q:=the_toks; link(p):=link(temp_head); p:=q;
+@y
+ q:=the_toks;
+ if link(temp_head)<>null then
+ begin link(p):=link(temp_head); p:=q;
+ end;
+@z
+
+262. Minor patch for efficiency (August 29)
+@x module 466
+ begin store_new_token(info(r)); r:=link(r);
+@y
+ begin fast_store_new_token(info(r)); r:=link(r);
+@z
+
+263. Minor patch to error message (August 29)
+@module 579
+ print(" has "); print_int(font_params[f]);
+ print(" fontdimen parameters");
+@.Font x has n fontdimen...@>
+@y
+ print(" has only "); print_int(font_params[f]);
+ print(" fontdimen parameters");
+@.Font x has only...@>
+@z
+
+*** The changes above comprise version 0.99999, used only at SAIL (August 29)
+
+264. funny blank spaces are showable (August 30)
+@x module 298
+spacer: print("blank space");
+@y
+spacer: chr_cmd("blank space ");
+@z
+
+265. \newlinechar [change 235] should affect print_char too (August 31)
+@x module 58
+procedure print_char(@!c:ascii_code); {prints a single character}
+begin case selector of
+term_and_log: begin wterm(xchr[c]); write(log_file,xchr[c]);
+ incr(term_offset); incr(file_offset);
+ if term_offset=max_print_line then
+ begin wterm_cr; term_offset:=0;
+ end;
+ if file_offset=max_print_line then
+ begin wlog_cr; file_offset:=0;
+ end;
+ end;
+log_only: begin write(log_file,xchr[c]); incr(file_offset);
+ if file_offset=max_print_line then print_ln;
+ end;
+term_only: begin wterm(xchr[c]); incr(term_offset);
+ if term_offset=max_print_line then print_ln;
+ end;
+no_print: do_nothing;
+pseudo: if tally<trick_count then trick_buf[tally mod error_line]:=c;
+new_string: begin if pool_ptr<pool_size then append_char(c);
+ end; {drop characters if the string space is full}
+othercases write(write_file[selector],xchr[c])
+endcases;@/
+incr(tally);
+end;
+@y
+procedure print_char(@!s:ascii_code); {prints a single character}
+label exit;
+begin if @<Character |s| is the current new-line character@> then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+case selector of
+term_and_log: begin wterm(xchr[s]); wlog(xchr[s]);
+ incr(term_offset); incr(file_offset);
+ if term_offset=max_print_line then
+ begin wterm_cr; term_offset:=0;
+ end;
+ if file_offset=max_print_line then
+ begin wlog_cr; file_offset:=0;
+ end;
+ end;
+log_only: begin wlog(xchr[s]); incr(file_offset);
+ if file_offset=max_print_line then print_ln;
+ end;
+term_only: begin wterm(xchr[s]); incr(term_offset);
+ if term_offset=max_print_line then print_ln;
+ end;
+no_print: do_nothing;
+pseudo: if tally<trick_count then trick_buf[tally mod error_line]:=s;
+new_string: begin if pool_ptr<pool_size then append_char(s);
+ end; {drop characters if the string space is full}
+othercases write(write_file[selector],xchr[s])
+endcases;@/
+incr(tally);
+exit:end;
+
+266. \lastkern,\lastpenalty,\unkern,\unpenalty (September 4)
+@x module 208
+@d unskip=25 {nullify glue ( \.{\\unskip} )}
+@y
+@d remove_item=25 {nullify last item ( \.{\\unpenalty},
+ \.{\\unkern}, \.{\\unskip} )}
+@z
+@x module 208
+@d last_skip=69 {most recent glue ( \.{\\lastskip} )}
+@y
+@d last_item=69 {most recent item ( \.{\\lastpenalty},
+ \.{\\lastkern}, \.{\\lastskip} )}
+@z
+@x module 265
+primitive("lastskip",last_skip,0);@/
+@!@:last_skip_}{\.{\\lastskip} primitive@>
+@y
+@z
+@x module 265
+primitive("the",the,0);
+@!@:the_}{\.{\\the} primitive@>
+primitive("toks",toks_register,0);
+@!@:toks_}{\.{\\toks} primitive@>
+primitive("unskip",unskip,0);@/
+@!@:unskip_}{\.{\\unskip} primitive@>
+@y
+primitive("the",the,0);@/
+@!@:the_}{\.{\\the} primitive@>
+primitive("toks",toks_register,0);@/
+@!@:toks_}{\.{\\toks} primitive@>
+@z
+@x module 266
+last_skip: print_esc("lastskip");
+@y
+@z
+@x module 266
+unskip: print_esc("unskip");
+@y
+@z
+@x module 413
+last_skip: @<Fetch the glue in the current node, if any@>;
+@y
+last_item: @<Fetch an item in the current node, if appropriate@>;
+@z
+@x module 416 [I also changed the comment]
+primitive("dp",set_box_dimen,depth_offset);
+@!@:dp_}{\.{\\dp} primitive@>
+@y
+primitive("dp",set_box_dimen,depth_offset);
+@!@:dp_}{\.{\\dp} primitive@>
+primitive("lastpenalty",last_item,int_val);
+@!@:last_penalty_}{\.{\\lastpenalty} primitive@>
+primitive("lastkern",last_item,dimen_val);
+@!@:last_kern_}{\.{\\lastkern} primitive@>
+primitive("lastskip",last_item,glue_val);
+@!@:last_skip_}{\.{\\lastskip} primitive@>
+@z
+@x module 417
+else print_esc("dp");
+@y
+else print_esc("dp");
+last_item: if chr_code=int_val then print_esc("lastpenalty")
+else if chr_code=dimen_val then print_esc("lastkern")
+else print_esc("lastskip");
+@z
+@x module 424
+@ Here is where \.{\\lastskip} is implemented. The reference count will be
+updated later.
+@:last_skip_}{\.{\\lastskip} primitive@>
+
+@<Fetch the glue in the current node, if any@>=
+begin cur_val:=zero_glue; cur_val_level:=glue_val;
+if not is_char_node(tail)and(mode<>0) then
+ begin if type(tail)=glue_node then
+ begin cur_val:=glue_ptr(tail);
+ if subtype(tail)=mu_glue then cur_val_level:=mu_val;
+ end;
+ end
+else if (mode=vmode)and(tail=head)and(last_page_glue<>max_halfword) then
+ cur_val:=last_page_glue;
+end
+@y
+@ Here is where \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\lastskip} are
+implemented. The reference count for \.{\\lastskip} will be updated later.
+
+@<Fetch an item in the current node...@>=
+begin if cur_chr=glue_val then cur_val:=zero_glue@+else cur_val:=0;
+cur_val_level:=cur_chr;
+if not is_char_node(tail)and(mode<>0) then
+ case cur_chr of
+ int_val: if type(tail)=penalty_node then cur_val:=penalty(tail);
+ dimen_val: if type(tail)=kern_node then cur_val:=width(tail);
+ glue_val: if type(tail)=glue_node then
+ begin cur_val:=glue_ptr(tail);
+ if subtype(tail)=mu_glue then cur_val_level:=mu_val;
+ end;
+ end {there are no other cases}
+else if (mode=vmode)and(tail=head) then
+ case cur_chr of
+ int_val: cur_val:=last_penalty;
+ dimen_val: cur_val:=last_kern;
+ glue_val: if last_glue<>max_halfword then cur_val:=last_glue;
+ end; {there are no other cases}
+end
+@z
+@x module 982 [also the comment changes]
+@!last_page_glue:pointer; {used to implement \.{\\lastskip}}
+@y
+@!last_glue:pointer; {used to implement \.{\\lastskip}}
+@!last_penalty:integer; {used to implement \.{\\lastpenalty}}
+@!last_kern:scaled; {used to implement \.{\\lastkern}}
+@z
+@x module 991
+last_page_glue:=max_halfword;
+@y
+last_glue:=max_halfword; last_penalty:=0; last_kern:=0;
+@z
+@x module 994
+@<Update the value of |last_page_glue|@>;
+@y
+@<Update the values of |last_glue|, |last_penalty|, and |last_kern|@>;
+@z
+@x module 996
+@ @<Update the value of |last_page_glue|@>=
+if last_page_glue<>max_halfword then delete_glue_ref(last_page_glue);
+if type(p)=glue_node then
+ begin last_page_glue:=glue_ptr(p); add_glue_ref(last_page_glue);
+ end
+else last_page_glue:=max_halfword
+@y
+@ @<Update the values of |last_glue|...@>=
+if last_glue<>max_halfword then delete_glue_ref(last_glue);
+last_penalty:=0; last_kern:=0;
+if type(p)=glue_node then
+ begin last_glue:=glue_ptr(p); add_glue_ref(last_glue);
+ end
+else begin last_glue:=max_halfword;
+ if type(p)=penalty_node then last_penalty:=penalty(p)
+ else if type(p)=kern_node then last_kern:=width(p);
+ end
+@z
+@x module 1017
+if last_page_glue<>max_halfword then delete_glue_ref(last_page_glue);
+@<Start a new current page@>; {this sets |last_page_glue:=max_halfword|}
+@y
+if last_glue<>max_halfword then delete_glue_ref(last_glue);
+@<Start a new current page@>; {this sets |last_glue:=max_halfword|}
+@z
+@x module 1048
+vmode+vmove,hmode+hmove,mmode+hmove,any_mode(last_skip),
+@y
+vmode+vmove,hmode+hmove,mmode+hmove,any_mode(last_item),
+@z
+@x module 1104
+any_mode(unskip): delete_skip;
+@y
+any_mode(remove_item): delete_last;
+@z
+@x module 1105
+procedure delete_skip;
+var p:pointer; {runs through the current list}
+begin if (mode=vmode)and(tail=head) then
+ @<Apologize for inability to do \.{\\unskip} now,
+ unless the previous node was not glue@>
+else begin if not is_char_node(tail) then if type(tail)=glue_node then
+@y
+procedure delete_last;
+var p:pointer; {runs through the current list}
+begin if (mode=vmode)and(tail=head) then
+ @<Apologize for inability to do the operation now,
+ unless \.{\\unskip} follows non-glue@>
+else begin if not is_char_node(tail) then if type(tail)=cur_chr then
+@z
+@x module 1106
+@ @<Apologize for inability to do \.{\\unskip}...@>=
+begin if last_page_glue<>max_halfword then
+ begin you_cant;
+ help2("Sorry...I'm usually unable to take things from the current")@/
+ ("page. Try `I\vskip-\lastskip' instead."); error;
+@y
+@ @<Apologize for inability to do the operation...@>=
+begin if (cur_chr<>glue_node)or(last_glue<>max_halfword) then
+ begin you_cant;
+ help2("Sorry...I'm usually unable to take things from the current")@/
+ ("page. Try `I\vskip-\lastskip' instead.");
+ if cur_chr=kern_node then help_line[0]:=
+ ("page. Try `I\kern-\lastkern' instead.")
+ else if cur_chr<>glue_node then help_line[0]:=@|
+ ("page. Perhaps you can make the output routine do it.");
+ error;
+@z
+@x module 1107
+primitive("unhbox",un_hbox,box_code);@/
+@y
+primitive("unpenalty",remove_item,penalty_node);@/
+@!@:un_penalty_}{\.{\\unpenalty} primitive@>
+primitive("unkern",remove_item,kern_node);@/
+@!@:un_kern_}{\.{\\unkern} primitive@>
+primitive("unskip",remove_item,glue_node);@/
+@!@:un_skip_}{\.{\\unskip} primitive@>
+primitive("unhbox",un_hbox,box_code);@/
+@z
+@x module 1108
+un_hbox: if chr_code=copy_code then print_esc("unhcopy")
+@y
+remove_item: if chr_code=glue_node then print_esc("unskip")
+ else if chr_code=kern_node then print_esc("unkern")
+ else print_esc("unpenalty");
+un_hbox: if chr_code=copy_code then print_esc("unhcopy")
+@z
+
+*** The above changes installed in version 0.999999 (September 5, 1983)
+
+267. Undo change 29: it was overkill and not needed (Sep 17)
+@x module 1257
+label exit, common_ending;
+var u:pointer; {user's font identifier}
+@!s:scaled; {stated ``at'' size, or negative of scaled magnification}
+@!f:internal_font_number; {runs through existing fonts}
+begin if job_name=0 then open_log_file;
+ {avoid confusing \.{texput} with the font name}
+@<If the next token isn't a suitable control sequence, issue a complaint
+ and |return|; otherwise set |u:=cs_ptr|, and set |hash_used| to the
+ location of a ``frozen'' copy of the new font identifier@>;
+define(u,set_font,null_font); scan_optional_equals; scan_file_name;
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal
+ font number and |goto common_ending|@>;
+f:=read_font_info(u,cur_name,cur_area,s);
+common_ending: equiv(u):=f; geq_define(hash_used,set_font,f);
+font_ident[f]:=hash_used;
+exit:end;
+@y
+label common_ending;
+var u:pointer; {user's font identifier}
+@!s:scaled; {stated ``at'' size, or negative of scaled magnification}
+@!f:internal_font_number; {runs through existing fonts}
+begin if job_name=0 then open_log_file;
+ {avoid confusing \.{texput} with the font name}
+get_r_token; u:=cs_ptr;
+define(u,set_font,null_font); scan_optional_equals; scan_file_name;
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal
+ font number and |goto common_ending|@>;
+f:=read_font_info(u,cur_name,cur_area,s);
+common_ending: equiv(u):=f; font_ident[f]:=u;
+end;
+@z
+@x module 1260
+@ We reserve a special control sequence for the font identifier; this one
+cannot be redefined by the user, so it is safe to return it as a value of
+\.{\\the\\font}.
+
+@<If the next token isn't a suitable control sequence...@>=
+get_r_token;
+if cs_ptr<hash_base then
+ begin print_err("A font identifier must be a multiletter control sequence");
+@.A font identifier...@>
+ help2("You should say, e.g., `\font\ffn=fontfilename'.")@/
+ ("(I'm going to ignore the \font command you just gave.)");
+ back_error; return;
+ end;
+repeat if hash_is_full then overflow("hash size",hash_size);
+@:TeX capacity exceeded hash size}{\quad hash size@>
+decr(hash_used);
+until text(hash_used)=0; {search for an empty location in |hash|}
+u:=cs_ptr; text(hash_used):=text(u); {copy the name}
+@!stat incr(cs_count);@+tats@;@/
+@y
+@z
+Note: Since module 1260 has disappeared, module 1258 has been split into two.
+
+268. Minor change to diagnostic output format (September 18)
+@x module 211 [print_mode]
+ 2:print("displayed math");
+@y
+ 2:print("display math");
+@z
+
+269. Kerns inserted for accents must be explicit (September 20)
+@x module 1123
+@!p,@!q: pointer; {character and box nodes}
+@y
+@!p,@!q,@!r:pointer; {character, box, and kern nodes}
+@z
+@x module 1125
+link(tail):=new_kern(delta); link(link(tail)):=p;
+link(p):=new_kern(-a-delta); tail:=link(p); p:=q;
+@y
+r:=new_kern(delta); subtype(r):=explicit; link(tail):=r; link(r):=p;
+tail:=new_kern(-a-delta); subtype(tail):=explicit; link(p):=tail; p:=q;
+@z
+
+270. "log" changed to "transcript" in a few output messages (Sep 26)
+@x module 535
+prompt_file_name("log file name",".log");
+@y
+prompt_file_name("transcript file name",".log");
+@z
+@x module 1293
+ ("lists on your terminal as well as on the log file.");
+@y
+ ("lists on your terminal as well as in the transcript file.");
+@z
+@x module 1335
+ print_nl("(see the log file for additional information)");
+@.see the log file...@>
+@y
+ print_nl("(see the transcript file for additional information)");
+@.see the transcript file...@>
+@z
+
+271. Uninitialized variable bug (found by Bernd Schulze, 1 Oct 83)
+@x module 944
+ begin if trie_op_ptr>=max_quarterword-1 then {overflow}
+ begin trie_op_ptr:=max_quarterword;
+ new_trie_op:=min_quarterword; return;
+@y we allow one more trie op (OK since trie_op_hash_size is big enough)
+ begin if trie_op_ptr=max_quarterword then {overflow}
+ begin new_trie_op:=min_quarterword; return;
+@z
+
+272. Spaces at end of lines ignored also in TEX.POOL (by DRF, 14 Oct 83)
+@x module 52
+ begin if eoln(pool_file) then bad_pool('! TEX.POOL line too short.');
+@.TEX.POOL line too short@>
+ read(pool_file,m); append_char(xord[m]);
+@y
+ begin if eoln(pool_file) then m:=' '@+else read(pool_file,m);
+ append_char(xord[m]);
+@z
+
+273. |history| updates (by DRF, 14 Oct 83)
+@x module 77
+deletions_allowed:=true; history:=spotless; error_count:=0;
+@y
+deletions_allowed:=true; error_count:=0; {|history| is initialized elsewhere}
+@z
+@x module 1332
+@p begin {@!|start_here|}
+@y
+@p begin {@!|start_here|}
+history:=fatal_error_stop; {in case we quit during initialization}
+@z
+@x ibid.
+main_control; {come to life}
+@y
+history:=spotless; {ready to go!}
+main_control; {come to life}
+@z
+
+274. improved "runaway" messages (suggested by FY, October 18)
+@x module 305 (the introductory comment is changed too)
+@d aligning=4 {|scanner_status| when reading an alignment preamble}
+
+@<Glob...@>=
+@!scanner_status : normal..aligning; {can a subfile end now?}
+@y
+@d aligning=4 {|scanner_status| when reading an alignment preamble}
+@d absorbing=5 {|scanner_status| when reading a balanced text}
+
+@<Glob...@>=
+@!scanner_status : normal..absorbing; {can a subfile end now?}
+@z
+@x module 306 (string space is also now conserved slightly)
+ defining: begin print("definition?"); p:=def_ref;
+ end;
+ matching: begin print("argument?"); p:=temp_head;
+ end;
+ aligning: begin print("preamble?"); p:=hold_head;
+ end;
+ end; {there are no other cases}
+ print_ln; show_token_list(link(p),null,error_line-10);
+@y
+ defining: begin print("definition"); p:=def_ref;
+ end;
+ matching: begin print("argument"); p:=temp_head;
+ end;
+ aligning: begin print("preamble"); p:=hold_head;
+ end;
+ absorbing: begin print("text"); p:=def_ref;
+ end;
+ end; {there are no other cases}
+ print_char("?");print_ln; show_token_list(link(p),null,error_line-10);
+@x module 339 (I also changed the module name)
+end; {there are no other cases}
+@y
+absorbing:begin print("text"); info(p):=right_brace_token+"}";
+ end;
+end; {there are no other cases}
+@z
+@x module 473
+begin scanner_status:=defining; warning_index:=cs_ptr;
+def_ref:=get_avail; info(def_ref):=null;
+@y
+begin if macro_def then scanner_status:=defining
+@+else scanner_status:=absorbing;
+warning_index:=cs_ptr; def_ref:=get_avail; info(def_ref):=null;
+@z
+
+275. similar, but this corrects a real bug (found by FY, October 18)
+@x module 1226
+ cs_ptr:=q;
+ @<Get the next non-blank non-relax non-call token@>;
+ if cur_cmd<>left_brace then @<If the righthand side is a token parameter
+ or token register, finish the assignment and |goto done|@>;
+ back_input; q:=scan_toks(false,false);
+@y
+ @<Get the next non-blank non-relax non-call token@>;
+ if cur_cmd<>left_brace then @<If the righthand side is a token parameter
+ or token register, finish the assignment and |goto done|@>;
+ back_input; cs_ptr:=q; q:=scan_toks(false,false);
+@z
+
+276. Change #119 should have changed module 1090 too (by Barry Smith, Oct 24)
+@x module 1183 [which was module 1090 at the time of change 119]
+begin if c mod delimited_code=above_code then scan_normal_dimen;
+if c>=delimited_code then
+ begin scan_delimiter(garbage,false); scan_delimiter(garbage,false);
+ end;
+@y
+begin if c>=delimited_code then
+ begin scan_delimiter(garbage,false); scan_delimiter(garbage,false);
+ end;
+if c mod delimited_code=above_code then scan_normal_dimen;
+@z
+
+277. Changes for efficiency, based on empirical frequency data (Nov 9)
+@x module 45 [since the result is true almost always]
+begin j:=str_start[s]; result:=false;
+while j<str_start[s+1] do
+ begin if str_pool[j]<>buffer[k] then goto not_found;
+@y
+begin j:=str_start[s];
+while j<str_start[s+1] do
+ begin if str_pool[j]<>buffer[k] then
+ begin result:=false; goto not_found;
+ end;
+@z
+@x module 380 [many compilers don't handle "while true do" very well]
+label done;
+begin loop begin get_next;
+@^inner loop@>
+ if cur_cmd<=max_command then goto done;
+ if cur_cmd>=call then
+ if cur_cmd<end_template then macro_call
+ else begin cs_ptr:=frozen_endv; cur_cmd:=endv;
+ goto done; {|cur_chr=null_list|}
+ end
+ else expand;
+ end;
+@y
+label restart,done;
+begin restart: get_next;
+@^inner loop@>
+if cur_cmd<=max_command then goto done;
+if cur_cmd>=call then
+ if cur_cmd<end_template then macro_call
+ else begin cs_ptr:=frozen_endv; cur_cmd:=endv;
+ goto done; {|cur_chr=null_list|}
+ end
+else expand;
+goto restart;
+@z
+@x module 829 [preparation for change to module 852]
+label exit,done,continue,deactivate;
+@y
+label exit,done,done1,continue,deactivate;
+@z
+@x module 830 [ditto]
+@!save_link:pointer; {temporarily holds value of |link(cur_p)|}
+@y
+@!save_link:pointer; {temporarily holds value of |link(cur_p)|}
+@!shortfall:scaled; {used in badness calculations}
+@z
+@x modue 851 [ditto]
+if cur_active_width[1]<line_width then
+@y
+shortfall:=line_width-cur_active_width[1]; {we're this much too short}
+if shortfall>0 then
+@z
+@x module 852 [avoid calling |badness| in most common case]
+else begin b:=badness(line_width-cur_active_width[1],cur_active_width[2]);
+@y
+else begin if shortfall>7230584 then if cur_active_width[2]<1663497 then
+ begin b:=inf_bad; fit_class:=very_loose_fit; goto done1;
+ end;
+ b:=badness(shortfall,cur_active_width[2]);
+@z
+@x module 852, continued
+ else fit_class:=decent_fit;
+@y
+ else fit_class:=decent_fit;
+ done1:
+@z
+@x module 853 [using |shortfall|, since we now have it]
+begin if cur_active_width[1]-line_width>cur_active_width[6] then
+ b:=inf_bad+1
+else b:=badness(cur_active_width[1]-line_width,cur_active_width[6]);
+@y
+begin if -shortfall>cur_active_width[6] then b:=inf_bad+1
+else b:=badness(-shortfall,cur_active_width[6]);
+@z
+
+278. Forgotten |error| call (noticed by Gabi Kuper, December 3, 1983)
+@x module 500
+ help1("I'm ignoring this; it doesn't match any \if.");
+@y
+ help1("I'm ignoring this; it doesn't match any \if.");
+ error;
+@z
+
+*** Version 1.0 released on December 3, 1983 incorporates all of the above.
+ Version 1.1
+
+279. Problem with change 267 (found by Mike Urban, received 2 Feb 84)
+(I had overlooked many problems, e.g. `{\font\a=x \global\a}\the\font'
+and `\font\a=x \font\b=x \let\b=\undefined \the\a', etc.
+The remedy involves removal of the font_ident array, so there's a
+sprinkling of corrections in lots of modules. But basically the change
+is quite conservative, so it shouldn't spawn any new bugs (it says here).)
+
+@x module 174 (removing a reference to |font_ident|)
+ else sprint_cs(font_ident[font(p)]);
+@y
+ else @<Print the font identifier for |font(p)|@>;
+@z
+@x module 176 (removing a reference to |font_ident|)
+ else sprint_cs(font_ident[font(p)]);
+@y
+ else @<Print the font identifier for |font(p)|@>;
+@z
+@x module 222 (redefining and expanding the `frozen' area)
+@d frozen_null_font=frozen_control_sequence+9 {permanent `\.{\\nullfont}'}
+@d frozen_dont_expand=frozen_control_sequence+10
+ {permanent `\.{\\notexpanded:}'}
+@d undefined_control_sequence=frozen_control_sequence+11 {dummy location}
+@y
+@d frozen_dont_expand=frozen_control_sequence+9
+ {permanent `\.{\\notexpanded:}'}
+@d frozen_null_font=frozen_control_sequence+10
+ {permanent `\.{\\nullfont}'}
+@d font_id_base=frozen_null_font-font_base
+ {begins table of 257 permanent font identifiers}
+@d undefined_control_sequence=frozen_null_font+257 {dummy location}
+@z
+@x module 234 (removing a reference to |font_ident|, awkwardly)
+print_char("="); sprint_cs(font_ident[equiv(n)]);
+@y
+print_char("=");@/
+print_esc(hash[font_id_base+equiv(n)].rh);
+ {that's |text(font_id_base+equiv(n))|}
+@z
+@x module 256 (adding a new macro, font_id_text)
+@d hash_is_full == (hash_used=hash_base) {test if all positions are occupied}
+@y
+@d hash_is_full == (hash_used=hash_base) {test if all positions are occupied}
+@d font_id_text(#) == text(font_id_base+#) {a frozen font identifier's name}
+@z
+@x module 262 (relaxing a former restriction)
+else if (text(p)<128)or(text(p)>=str_ptr) then print_esc("NONEXISTENT.")
+@y
+else if (text(p)<0)or(text(p)>=str_ptr) then print_esc("NONEXISTENT.")
+@z
+@x module 267 (pick up the changes from 174 and 176)
+@!@^Single-character primitives@>
+@y
+@!@^Single-character primitives@>
+
+Meanwhile, this is a convenient place to catch up on something we were unable
+to do before the hash table was defined:
+
+@<Print the font identifier for |font(p)|@>=
+print_esc(font_id_text(font(p)))
+@z
+@x module 415 (removing another reference to font_ident)
+ scanned_result(font_ident[cur_val])(ident_val);
+@y
+ scanned_result(font_id_base+cur_val)(ident_val);
+@z
+@x module 548 (change to the comment only)
+to the user's font~\.{\\f}. For example, if this internal number is 13,
+we will have |font_ident[13]=p| and |equiv(p)=13|, where |p| is the |eqtb|
+location of the control sequence~\.{\\f}.
+@y
+to the user's font~\.{\\f}. Adding this number to |font_id_base| gives the
+|eqtb| location of a ``frozen'' control sequence that will always select
+the font.
+@z
+@x module 549 (deleting the declaration of font_ident)
+@!font_ident:array[internal_font_number] of pointer; {the most recent user
+ font identifier corresponding to an internal font number}
+@y
+@z
+@x module 552 (deleting an unnecessary initialization of font_ident)
+font_ident[null_font]:=frozen_null_font;
+@y
+@z
+@x module 579 (removing another reference to font_ident)
+ begin print_err("Font "); sprint_cs(font_ident[f]);
+@y
+ begin print_err("Font "); print_esc(font_id_text(f));
+@z
+@x module 1257 (this is the real change!)
+begin if job_name=0 then open_log_file;
+ {avoid confusing \.{texput} with the font name}
+get_r_token; u:=cs_ptr;
+define(u,set_font,null_font); scan_optional_equals; scan_file_name;
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal
+ font number and |goto common_ending|@>;
+f:=read_font_info(u,cur_name,cur_area,s);
+common_ending: equiv(u):=f; font_ident[f]:=u;
+@y
+@!t:str_number; {name for the frozen font identifier}
+@!old_setting:0..max_selector; {holds |selector| setting}
+begin if job_name=0 then open_log_file;
+ {avoid confusing \.{texput} with the font name}
+get_r_token; u:=cs_ptr;
+if u>=hash_base then t:=text(u)
+else if u>=single_base then
+ if u=null_cs then t:="FONT"@+else t:=u-single_base
+else begin old_setting:=selector; selector:=new_string;
+ print("FONT"); print(u-active_base); selector:=old_setting;
+@.FONTx@>
+ str_room(1); t:=make_string;
+ end;
+define(u,set_font,null_font); scan_optional_equals; scan_file_name;
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal
+ font number and |goto common_ending|@>;
+f:=read_font_info(u,cur_name,cur_area,s);
+common_ending: equiv(u):=f; eqtb[font_id_base+f]:=eqtb[u]; font_id_text(f):=t;
+@z
+@x module 1260 (change to the comment only)
+the new value becomes the |font_ident| of record. Font names `\.{xyz}' and
+@y
+the new name becomes the font identifier of record. Font names `\.{xyz}' and
+@z
+@x module 1322 (no need to dump font_ident)
+begin dump_int(font_ident[k]);
+dump_qqqq(font_check[k]);
+@y
+begin dump_qqqq(font_check[k]);
+@z
+@x module 1322 (or to print from it)
+print_nl("\font"); sprint_cs(font_ident[k]); print_char("=");
+@y
+print_nl("\font"); print_esc(font_id_text(k)); print_char("=");
+@z
+@x module 1323 (or to undump it)
+begin undump(active_base)(undefined_control_sequence)(font_ident[k]);
+undump_qqqq(font_check[k]);@/
+@y
+begin undump_qqqq(font_check[k]);@/
+@z
+
+280. Double interrupt possibility (found by Clint Cuzzo, received 9 Feb 84)
+@x module 1031
+if interrupt>0 then if OK_to_interrupt then
+ begin back_input; pause_for_instructions; goto big_switch;
+@y
+if interrupt<>0 then if OK_to_interrupt then
+ begin back_input; check_interrupt; goto big_switch;
+@z
+
+281. Improve spacing in $(A,<)$. (12 Feb 84)
+@x module 764
+"0234000122*4000133**3**344*0400400*000000234000111*4111112341011"
+@y
+"0234000122*4000133**3**344*0400400*000000234000111*1111112341011"
+@z
+
+282. Bad goto! (diagnosis by Clint Cuzzo and George O'Connor, recd 13 Feb 84)
+@x module 344 (here we simply change the name of module 346)
+any_state_plus(invalid_char): @<Decry the invalid character and
+ |goto switch|@>;
+@y
+any_state_plus(invalid_char): @<Decry the invalid character and
+ |goto restart|@>;
+@z
+@x module 346 (because clear_for_error_prompt might make state=token_list)
+goto switch;
+@y
+goto restart;
+@z
+
+283. String pool economy (suggested by DRF, 13 Feb 84)
+@x module 537
+@<Read the first line of the new file@>;
+@y
+if name=str_ptr-1 then {we can conserve string pool space now}
+ begin flush_string; name:=cur_name;
+ end;
+@<Read the first line of the new file@>;
+@z
+
+284. Nicer scaled output (suggested by METAFONT development, 26 Feb 84)
+@x module 103
+be reproduced exactly. [{\sl Proof:\/} If round$(x)=\lfloor
+x+{1\over2}\rfloor$ and if $\alpha<1$, it is not difficult to verify that
+round$(\alpha\,\hbox{round}( \alpha^{-1}n))=n$ for all integers |n|. In
+our case $\alpha=2^{16}/10^5$.]
+
+@p procedure print_scaled(@!s:scaled); {prints scaled real, rounded to five
+ digits}
+begin if s<0 then
+ begin print_char("-"); negate(s); {print the sign, if negative}
+ end;
+print_int(s div unity); {print the integer part}
+s:=((s mod unity) * 3125 + 1024) div 2048;
+ {now |0<=s<100000| is the fraction part}
+print_char(".");
+repeat print_char("0"+(s div 10000)); s:=10*(s mod 10000);
+until s=0;
+end;
+@y
+be reproduced exactly; the ``simplest'' such decimal number is output,
+but there is always at least one digit following the decimal point.
+
+The invariant relation in the \&{repeat} loop is that a sequence of
+decimal digits yet to be printed will yield the original number if and only if
+they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
+We can stop if and only if $f=0$ satisfies this condition; the loop will
+terminate before $s$ can possibly become zero.
+
+@p procedure print_scaled(@!s:scaled); {prints scaled real, rounded to five
+ digits}
+var delta:scaled; {amount of allowable inaccuracy}
+begin if s<0 then
+ begin print_char("-"); negate(s); {print the sign, if negative}
+ end;
+print_int(s div unity); {print the integer part}
+print_char(".");
+s:=10*(s mod unity)+5; delta:=10;
+repeat if delta>unity then s:=s+@'100000-(delta div 2); {round the last digit}
+print_char("0"+(s div unity)); s:=10*(s mod unity); delta:=delta*10;
+until s<=delta;
+end;
+@z
+
+*** Those six changes account for version 1.1 (February 29, 1984).
+*** And we put the following change in too, at the last minute:
+
+285. ".00000762939453126pt" didn't round correctly! (March 2, 1984)
+@x module 452
+ if k<16 then {digits for |k>=16| cannot affect the result}
+@y (Note, I also changed the comment in module 102: 16 -> 17)
+ if k<17 then {digits for |k>=17| cannot affect the result}
+@z
+
+*** Note: I changed "cs_ptr" to "cur_cs", since the name change is more
+indicative of the fact that this variable makes a trio with cur_cmd
+and cur_chr. The TEX.WEB source changed 78 times, as a result, but
+none of the author's change files were affected.
+(But note: "save_cs_ptr" was not changed to "save_cur_cs", because
+of a name conflict.)
+
+*** And the following two were slipped in at the last second:
+
+286. loophole permitted get_next recursion (March 16, 1984)
+@x module 336
+ begin @<Back up an outer control sequence so that it can be reread@>;
+@y
+ begin deletions_allowed:=false;
+ @<Back up an outer control sequence so that it can be reread@>;
+@z
+@x ibid
+ end;
+@y
+ deletions_allowed:=true;
+ end;
+@z [the changes to deletions_allowed in module 338 can now be removed]
+
+287. terminal not open when the program starts bad (March 24, 1984)
+@x module 1332
+initialize; {set global variables to their starting values}
+@<Check the ``constant'' values...@>@;
+if bad>0 then
+ begin wake_up_terminal;
+ wterm('Ouch---my internal constants have been',
+ ' clobbered!---case ',bad:1);
+@.Ouch...clobbered@>
+ goto final_end;
+ end;
+@y
+@<Check the ``constant'' values...@>@;
+if bad>0 then
+ begin t_open_out;
+ wterm('Ouch---my internal constants have been',
+ ' clobbered!---case ',bad:1);
+@.Ouch...clobbered@>
+ goto final_end;
+ end;
+initialize; {set global variables to their starting values}
+@z
+
+*** Note: "Cosmetic" changes were also made in module 363 ("print_ln"
+moved to after "wake_up_terminal") and in some commentary.
+
+*** Another last-minute correction:
+288. \patterns{xxx...xxxdxxxdxxx} anomaly found by JRD (Mar 27, 1984)
+@x module 962
+else begin hyf[k]:=cur_chr-"0"; digit_sensed:=true;
+@y
+else begin hyf[k]:=cur_chr-"0";
+ if k<63 then digit_sensed:=true;
+@z
+
+*** Yoicks, yet ANOTHER:
+289. a whole case of copy_node_list forgotten (Apr 11, 1984)
+@x module 206
+othercases confusion("copying")
+@y
+adjust_node: begin r:=get_node(small_node_size);
+ adjust_ptr(r):=copy_node_list(adjust_ptr(p));
+ end; {|words=1=small_node_size-1|}
+othercases confusion("copying")
+@z
+
+*** And "finally"...
+290. uninitialized variables could be accessed (found by Nick Briggs, June 11)
+@x module 552
+font_size[null_font]:=0; font_dsize[null_font]:=0;
+@y
+font_size[null_font]:=0; font_dsize[null_font]:=0;
+char_base[null_font]:=0; width_base[null_font]:=0;
+height_base[null_font]:=0; depth_base[null_font]:=0;
+italic_base[null_font]:=0; lig_kern_base[null_font]:=0;
+kern_base[null_font]:=0; exten_base[null_font]:=0;
+@z (actual values are immaterial except for debugging safeguards)
+
+291. same, cf. modules 355 and 389 (Nick Briggs, June 11)
+@x module 331
+scanner_status:=normal; first:=1;
+@y
+for first:=0 to buf_size do buffer[first]:=0;
+scanner_status:=normal; warning_index:=null; first:=1;
+@z (error was harmless except that it might trigger debugging checks)
+
+292. missing ligature/kern (found by JRD, June 21)
+@x module 1036
+ if (cur_cmd=letter)or(cur_cmd=other_char) then r:=qi(cur_chr)
+@y
+ if (cur_cmd=letter)or(cur_cmd=other_char)or(cur_cmd=char_given) then
+ r:=qi(cur_chr)
+@z
+
+293. quarterword constraint is made explicit (July 4)
+@x module 111
+if (buf_size>max_halfword) then bad:=18;
+@y
+if buf_size>max_halfword then bad:=18;
+if max_quarterword-min_quarterword<255 then bad:=19;
+@z
+
+294. trivial optimization made for consistency with MF (July 7)
+@x module 363
+if (pausing>0)and(interaction>nonstop_mode) then
+@y
+if pausing>0 then if interaction>nonstop_mode then
+@z
+
+295. \tracingmacros>1 for more diagnostics (July 8)
+@x module 323
+ if t=macro then param_start:=param_ptr@+else loc:=link(p);
+@y
+ if t=macro then param_start:=param_ptr
+ else begin loc:=link(p);
+ if tracing_macros>1 then
+ begin begin_diagnostic; print_nl("");
+ case t of
+ mark_text:print_esc("mark");
+ write_text:print_esc("write");
+ othercases print_cmd_chr(assign_toks,t-output_text+output_routine_loc)
+ endcases;@/
+ print("->"); token_show(p); end_diagnostic(false);
+ end;
+ end;
+@z
+
+*** The changes above were incorporated in Version 1.1, released July 9, 1984.
+ Version 1.2
+
+296. "see the transcript file" on offline show commands. (July 27, 1984)
+@x module 1293
+@ @d show_error==@;@/
+ if interaction<error_stop_mode then
+ begin help0; decr(error_count);
+ end
+ else if tracing_online>0 then
+ begin@t@>@;@/
+ help3("This isn't an error message; I'm just \showing something.")
+ @t\4\4@>@/
+ ("Type `I\show...' to show more (e.g., \show\cs,")@/
+ ("\showthe\count10, \showbox255, \showlists).");
+ end
+ else begin@t@>@;@/
+ help5("This isn't an error message; I'm just \showing something.")
+ @t\4\4@>@/
+ ("Type `I\show...' to show more (e.g., \show\cs,")@/
+ ("\showthe\count10, \showbox255, \showlists).")@/
+ ("And type `I\tracingonline=1\show...' to show boxes and")@/
+ ("lists on your terminal as well as in the transcript file.");
+ end;
+ error
+
+@<Declare act...@>=
+procedure show_whatever;
+var p:pointer; {tail of a token list to show}
+begin case cur_chr of
+show_code: @<Show the current meaning of a token@>;
+show_box_code: @<Show the current contents of a box@>;
+show_the_code: @<Show the current value of some parameter or register@>;
+othercases @<Show the current semantic nest@>
+endcases;
+end;
+@y
+@ @<Declare act...@>=
+procedure show_whatever;
+label common_ending;
+var p:pointer; {tail of a token list to show}
+begin case cur_chr of
+show_lists: begin begin_diagnostic; show_activities;
+ end;
+show_box_code: @<Show the current contents of a box@>;
+show_code: @<Show the current meaning of a token, then |goto common_ending|@>;
+othercases @<Show the current value of some parameter or register,
+ then |goto common_ending|@>
+endcases;@/
+@<Complete a potentially long \.{\\show} command@>;
+common_ending: if interaction<error_stop_mode then
+ begin help0; decr(error_count);
+ end
+else if tracing_online>0 then
+ begin@t@>@;@/
+ help3("This isn't an error message; I'm just \showing something.")@/
+ ("Type `I\show...' to show more (e.g., \show\cs,")@/
+ ("\showthe\count10, \showbox255, \showlists).");
+ end
+else begin@t@>@;@/
+ help5("This isn't an error message; I'm just \showing something.")
+ ("Type `I\show...' to show more (e.g., \show\cs,")@/
+ ("\showthe\count10, \showbox255, \showlists).")@/
+ ("And type `I\tracingonline=1\show...' to show boxes and")@/
+ ("lists on your terminal as well as in the transcript file.");
+ end;
+error;
+end;
+@z
+@x module 1294
+@ @<Show the current meaning of a token@>=
+begin get_token; print_nl("> ");
+if cur_cs<>0 then
+ begin sprint_cs(cur_cs); print_char("=");
+ end;
+print_meaning; show_error;
+@y
+@ @<Show the current meaning of a token...@>=
+begin get_token; print_nl("> ");
+if cur_cs<>0 then
+ begin sprint_cs(cur_cs); print_char("=");
+ end;
+print_meaning; goto common_ending;
+@z
+@x module 1296
+end_diagnostic(true);
+print_err("OK");
+@.OK@>
+show_error;
+@y
+@z
+@x module 1297
+flush_list(link(temp_head)); show_error;
+@y
+flush_list(link(temp_head)); goto common_ending;
+@z
+@x module 1298
+@ @<Show the current semantic nest@>=
+begin begin_diagnostic; show_activities; end_diagnostic(true);
+print_err("OK");
+show_error;
+@.OK@>
+end
+@y
+@ @<Complete a potentially long \.{\\show} command@>=
+end_diagnostic(true); print_err("OK");
+@.OK@>
+if selector=term_and_log then if tracing_online<=0 then
+ begin selector:=term_only; print(" (see the transcript file)");
+ selector:=term_and_log;
+ end
+@z
+
+297. allow `0' in response to error prompts (October 20, 1984)
+@x module 84
+"1","2","3","4","5","6","7","8","9": if deletions_allowed then
+@y
+"0","1","2","3","4","5","6","7","8","9": if deletions_allowed then
+@z
+ Versions 1.3 and 1.4
+
+298. Dirty PASCAL (two quarterword 0's were read as a halfword) (25 Nov 84)
+@x module 846
+print(" -> @@@@"); print_int(serial(prev_break(passive)));
+@y
+print(" -> @@@@");
+if prev_break(passive)=null then print_char("0")
+else print_int(serial(prev_break(passive)));
+@z
+
+299. Ditto! (25 Nov 84)
+@x module 934
+label reswitch, exit, found, not_found;
+@y
+label reswitch, exit, found, not_found, done;
+@z
+@x module 939
+while info(p)>n-3 do {eliminate hyphens \TeX\ doesn't like}
+ begin q:=link(p); free_avail(p); p:=q;
+ end;
+@<Insert the \(p)pair |(s,p)| into the exception table@>;
+@y
+loop@+ begin if p=null then goto done;
+ if info(p)<n-2 then goto done;
+ q:=link(p); free_avail(p); p:=q; {eliminate hyphens that \TeX\ doesn't like}
+ end;
+done: @<Insert the \(p)pair |(s,p)| into the exception table@>;
+@z
+
+300. Major change to memory management (25 Nov 84)
+[The following changes have two main merits: (1) TeX should run faster on
+virtual-memory systems when the jobs are small; (2) it should be possible
+to use the same production version of TeX both for jobs with lots of macros
+and few boxes as well as for jobs with lots of boxes and few macros, because
+the boundary line between the two types of storage is now chosen dynamically.]
+[The user is unaffected, except that there's only one "memory size" overflow
+error now instead of two.]
+[The TRIP test is now changed slightly: The correct settings of memory
+size parameters are now mem_min=mem_bot=1, mem_top=mem_max=3000.]
+
+@x module 11
+@!mem_max=30000; {greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|}
+@y
+@!mem_max=30000; {greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|}
+@!mem_min=0; {smallest index in \TeX's internal |mem| array;
+ must be |min_halfword| or more;
+ must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|}
+@z
+@x module 12
+@d mem_base=0 {smallest index in the |mem| array; must not be less
+ than |min_halfword|}
+@d hi_mem_base=13000 {smallest index in the single-word area of |mem|;
+ must be substantially larger than |mem_base| and smaller than |mem_max|}
+@y
+@d mem_bot=0 {smallest index in the |mem| array dumped by \.{INITEX};
+ must not be less than |mem_min|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INITEX};
+ must be substantially larger than |mem_bot|
+ and not greater than |mem_max|}
+@z
+@x module 12
+ about |(mem_max-hi_mem_base)/6|, but 2100 is already quite generous}
+@y
+ about |(mem_max-mem_min)/10|, but 2100 is already quite generous}
+@z
+@x module 14
+if (hi_mem_base<mem_base+100)or(hi_mem_base+100>mem_max) then bad:=4;
+@y
+if mem_bot+1100>mem_top then bad:=4;
+@z
+@x module 111
+if (min_quarterword>0)or(max_quarterword<127) then bad:=11;
+@y
+init if (mem_min<>mem_bot)or(mem_max<>mem_top) then bad:=10;@+tini@;@/
+if (mem_min>mem_bot)or(mem_max<mem_top) then bad:=10;
+if (min_quarterword>0)or(max_quarterword<127) then bad:=11;
+@z
+@x module 111
+if (mem_base<min_halfword)or(mem_max>=max_halfword) then bad:=14;
+@y
+if (mem_min<min_halfword)or(mem_max>=max_halfword) then bad:=14;
+@z
+@x module 115
+value represents a null pointer.
+
+@d pointer==halfword {a flag or a location in |mem| or |eqtb|}
+@d null==mem_base {the null pointer}
+@y
+value represents a null pointer. \TeX\ does not assume that |mem[null]| exists.
+
+@d pointer==halfword {a flag or a location in |mem| or |eqtb|}
+@d null==min_halfword {the null pointer}
+@z
+@x module 116
+@ The |mem| array is divided once and for all into two regions that are
+allocated separately. Locations less than |hi_mem_base| are used for storing
+@y
+@ The |mem| array is divided into two regions that are allocated separately,
+but the dividing line between these two regions is not fixed; they grow
+together until finding their ``natural'' size in a particular job.
+Locations less than or equal to |lo_mem_max| are used for storing
+@z
+@x module 116
+relevant size when a node is freed. The remaining region of |mem| is
+allocated in single words using a conventional \.{AVAIL} stack.
+
+Incidentally, it would be feasible to construct implementations of \TeX\ that
+are based on 16-bit words instead of 32-bit words, for machines having
+comparatively small memories. In such cases it might be desirable to have
+two parallel arrays for the upper part of memory, called say \\{mem\_link}|[p]|
+and \\{mem\_info}|[p]|,
+since the single-word region in the present implementation
+consists entirely of |memory_word| items of type |two_halves|.
+@^small computers@>
+@y
+relevant size when a node is freed. Locations greater than or equal to
+|hi_mem_min| are used for storing one-word records; a conventional
+\.{AVAIL} stack is used for allocation in this region.
+
+Locations of |mem| between |mem_bot| and |mem_top| may be dumped as part
+of preloaded format files, by the \.{INITEX} preprocessor.
+@.INITEX@>
+Production versions of \TeX\ may extend the memory at both ends in order to
+provide more space; locations between |mem_min| and |mem_bot| are always
+used for variable-size nodes, and locations between |mem_top| and |mem_max|
+are always used for single-word nodes.
+
+The key pointers that govern |mem| allocation have a prescribed order:
+$$\hbox{|null<=mem_min<=mem_bot<lo_mem_max<
+ hi_mem_min<mem_top<=mem_end<=mem_max|.}$$
+@z
+@x module 116
+@!mem : array[mem_base..mem_max] of memory_word; {the big dynamic storage area}
+@y
+@!mem : array[mem_min..mem_max] of memory_word; {the big dynamic storage area}
+@!lo_mem_max : pointer; {the largest location of variable-size memory in use}
+@!hi_mem_min : pointer; {the smallest location of one-word memory in use}
+@z
+@x module 117
+@!max_var_used : integer; {how much memory was in use}
+@y
+@z
+@x module 118
+occur between |hi_mem_base| and |mem_end|, inclusive, are of type
+@y
+occur between |hi_mem_min| and |mem_end|, inclusive, are of type
+@z
+@x module 120
+else begin runaway; {if memory is exhausted, display possible runaway text}
+ overflow("macro memory size",mem_max+1-hi_mem_base);
+ {quit; all one-word nodes are busy}
+@:TeX capacity exceeded macro memory size}{\quad macro memory size@>
+ end;
+@y
+else begin decr(hi_mem_min); p:=hi_mem_min;
+ if hi_mem_min<=lo_mem_max then
+ begin runaway; {if memory is exhausted, display possible runaway text}
+ overflow("main memory size",mem_max+1-mem_min);
+ {quit; all one-word nodes are busy}
+@:TeX capacity exceeded main memory size}{\quad main memory size@>
+ end;
+ end;
+@z
+@x module 125
+label found,exit;
+@y
+label found,exit,restart;
+@z
+@x module 125
+begin p:=rover; {start at some free node in the ring}
+@y
+begin restart: p:=rover; {start at some free node in the ring}
+@z
+@x module 125
+overflow("box memory size",hi_mem_base-mem_base);
+ {sorry, nothing satisfactory is left}
+@:TeX capacity exceeded box memory size}{\quad box memory size@>
+found: link(r):=null; {this node is now nonempty}
+@!stat var_used:=var_used+s; {maintain usage statistics}
+if var_used>max_var_used then max_var_used:=var_used;
+@y
+if lo_mem_max+2<hi_mem_min then
+ @<Grow more variable-size memory and |goto restart|@>;
+overflow("main memory size",mem_max+1-mem_min);
+ {sorry, nothing satisfactory is left}
+@:TeX capacity exceeded main memory size}{\quad main memory size@>
+found: link(r):=null; {this node is now nonempty}
+@!stat var_used:=var_used+s; {maintain usage statistics}
+@z
+@x modules 126--127
+@ Empirical tests show that the routine in this section performs a
+|remove_node| operation about 0.75 times per allocation, on the average,
+after which it finds that |r>p+1| about 95\% of the time.
+
+@<Try to allocate...@>=
+q:=p+node_size(p); {find the physical successor}
+@^inner loop@>
+while is_empty(q) do @<Merge node |p| with node |q|@>;
+r:=q-s;
+if r>p+1 then @<Allocate from the top of node |p| and |goto found|@>;
+if r=p then if ((rlink(p)<>rover) or (llink(p)<>rover)) then
+ @<Allocate entire node |p| and |goto found|@>;
+node_size(p):=q-p {reset the size in case it grew}
+
+@ @<Merge node |p| with node |q|@>=
+begin t:=rlink(q);
+@^inner loop@>
+if q=rover then rover:=t;
+llink(t):=llink(q); rlink(llink(q)):=t;@/
+q:=q+node_size(q);
+end
+@y
+@ The lower part of |mem| grows by 1000 words at a time, unless
+we are very close to going under. When it grows, we simply link
+a new node into the available-space list. This method of controlled
+growth helps to keep the |mem| usage consecutive when \TeX\ is
+implemented on ``virtual memory'' systems.
+@^virtual memory@>
+
+@<Grow more variable-size memory and |goto restart|@>=
+begin if lo_mem_max+1000<hi_mem_min then t:=lo_mem_max+1000
+else t:=(lo_mem_max+hi_mem_min+2) div 2; {|lo_mem_max+2<=t<hi_mem_min|}
+p:=llink(rover); q:=lo_mem_max; rlink(p):=q; llink(rover):=q;@/
+rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/
+lo_mem_max:=t; link(lo_mem_max):=null; info(lo_mem_max):=null;
+rover:=q; goto restart;
+end
+
+@ Empirical tests show that the routine in this section performs a
+node-merging operation about 0.75 times per allocation, on the average,
+after which it finds that |r>p+1| about 95\% of the time.
+
+@<Try to allocate...@>=
+q:=p+node_size(p); {find the physical successor}
+@^inner loop@>
+while is_empty(q) do {merge node |p| with node |q|}
+ begin t:=rlink(q);
+ if q=rover then rover:=t;
+ llink(t):=llink(q); rlink(llink(q)):=t;@/
+ q:=q+node_size(q);
+ end;
+r:=q-s;
+if r>p+1 then @<Allocate from the top of node |p| and |goto found|@>;
+if r=p then if ((rlink(p)<>rover) or (llink(p)<>rover)) then
+ @<Allocate entire node |p| and |goto found|@>;
+node_size(p):=q-p {reset the size in case it grew}
+@z
+% module 129 no longer refers to remove_node; I don't recall when it changed!
+@x module 134
+@d is_char_node(#) == (#>hi_mem_base)
+@y
+@d is_char_node(#) == (#>=hi_mem_min)
+@z
+@x module 162
+example, locations |mem_base| to |mem_base+3| are always used to store the
+specification for glue that is `\.{0pt plus 0pt minus 0pt}'. The
+following macro definitions accomplish the static allocation by giving
+symbolic names to the fixed positions. Dynamic allocation of variable-size
+nodes is restricted to locations |first_mem| through |(hi_mem_base-1)|,
+and single-word nodes are dynamically allocated in locations |second_mem|
+through |mem_max|, inclusive. It is harmless to let |lig_trick|, |garbage|,
+and |backup_head| share the same location of |mem|.
+
+@d zero_glue==mem_base {specification for \.{0pt plus 0pt minus 0pt}}
+@y
+example, locations |mem_bot| to |mem_bot+3| are always used to store the
+specification for glue that is `\.{0pt plus 0pt minus 0pt}'. The
+following macro definitions accomplish the static allocation by giving
+symbolic names to the fixed positions. Static variable-size nodes appear
+in locations |mem_bot| through |lo_mem_stat_max|, and static single-word nodes
+appear in locations |hi_mem_stat_min| through |mem_top|, inclusive. It is
+harmless to let |lig_trick|, |garbage|, and |backup_head| share the same
+location of |mem|.
+
+@d zero_glue==mem_bot {specification for \.{0pt plus 0pt minus 0pt}}
+@z
+@x module 162
+@d first_mem==fil_neg_glue+glue_spec_size {first dynamically allocatable word
+ in the variable-size |mem|}
+@#
+@d page_ins_head==hi_mem_base {list of insertion data for current page}
+@d contrib_head==hi_mem_base+1 {vlist of items not yet on current page}
+@d page_head==hi_mem_base+2 {vlist for current page}
+@d temp_head==hi_mem_base+3 {head of a temporary list of some kind}
+@d hold_head==hi_mem_base+4 {head of a temporary list of another kind}
+@d adjust_head==hi_mem_base+5 {head of adjustment list returned by |hpack|}
+@d active==hi_mem_base+6 {head of active list in |line_break|, needs two words}
+@d align_head==hi_mem_base+8 {head of preamble list for alignments}
+@d end_span==hi_mem_base+9 {tail of spanned-width lists}
+@d omit_template==hi_mem_base+10 {a constant token list}
+@d null_list==hi_mem_base+11 {permanently empty list}
+@d lig_trick==hi_mem_base+12 {a ligature masquerading as a |char_node|}
+@d garbage==hi_mem_base+12 {used for scrap information}
+@d backup_head==hi_mem_base+13 {head of token list built by |scan_keyword|}
+@d second_mem==hi_mem_base+14 {first dynamically allocatable word in
+ the one-word |mem|}
+@y
+@d lo_mem_stat_max==fil_neg_glue+glue_spec_size-1 {largest statically
+ allocated word in the variable-size |mem|}
+@#
+@d page_ins_head==mem_top {list of insertion data for current page}
+@d contrib_head==mem_top-1 {vlist of items not yet on current page}
+@d page_head==mem_top-2 {vlist for current page}
+@d temp_head==mem_top-3 {head of a temporary list of some kind}
+@d hold_head==mem_top-4 {head of a temporary list of another kind}
+@d adjust_head==mem_top-5 {head of adjustment list returned by |hpack|}
+@d active==mem_top-6 {head of active list in |line_break|, needs two words}
+@d align_head==mem_top-8 {head of preamble list for alignments}
+@d end_span==mem_top-9 {tail of spanned-width lists}
+@d omit_template==mem_top-10 {a constant token list}
+@d null_list==mem_top-11 {permanently empty list}
+@d lig_trick==mem_top-12 {a ligature masquerading as a |char_node|}
+@d garbage==mem_top-12 {used for scrap information}
+@d backup_head==mem_top-13 {head of token list built by |scan_keyword|}
+@d hi_mem_stat_min==mem_top-13 {smallest statically allocated word in
+ the one-word |mem|}
+@z
+@x module 164
+for k:=mem_base+1 to first_mem-1 do mem[k].sc:=0;
+ {all glue dimensions are zeroed}
+@^data structure assumptions@>
+k:=mem_base;@+while k<first_mem do {set first words of glue specifications}
+@y
+for k:=mem_bot+1 to lo_mem_stat_max do mem[k].sc:=0;
+ {all glue dimensions are zeroed}
+@^data structure assumptions@>
+k:=mem_bot;@+while k<=lo_mem_stat_max do
+ {set first words of glue specifications}
+@z
+@x module 164
+rover:=first_mem; link(rover):=empty_flag; {now initialize the dynamic memory}
+node_size(rover):=hi_mem_base-rover; {which is one big available node}
+llink(rover):=rover; rlink(rover):=rover;@/
+link(hi_mem_base):=null; info(hi_mem_base):=null;
+for k:=hi_mem_base+1 to second_mem-1 do
+ mem[k]:=mem[hi_mem_base];{clear list heads}
+@<Initialize the special list heads and constant nodes@>;
+avail:=null; mem_end:=second_mem-1; {initialize the one-word memory}
+var_used:=first_mem-mem_base; dyn_used:=second_mem-hi_mem_base;
+max_var_used:=var_used; {initialize statistics}
+@y
+rover:=lo_mem_stat_max+1;
+link(rover):=empty_flag; {now initialize the dynamic memory}
+node_size(rover):=1000; {which is a 1000-word available node}
+llink(rover):=rover; rlink(rover):=rover;@/
+lo_mem_max:=rover+1000; link(lo_mem_max):=null; info(lo_mem_max):=null;@/
+for k:=hi_mem_stat_min to mem_top do
+ mem[k]:=mem[lo_mem_max]; {clear list heads}
+@<Initialize the special list heads and constant nodes@>;
+avail:=null; mem_end:=mem_top;
+hi_mem_min:=hi_mem_stat_min; {initialize the one-word memory}
+var_used:=lo_mem_stat_max+1-mem_bot; dyn_used:=mem_top+1-hi_mem_stat_min;
+ {initialize statistics}
+@z
+@x module 165
+@t\hskip1em@>@!was_mem_end: pointer; {previous |mem_end|}
+@y
+@t\hskip1em@>@!was_mem_end,@!was_lo_max,@!was_hi_min: pointer;
+ {previous |mem_end|, |lo_mem_max|,and |hi_mem_min|}
+@z
+@x module 166
+@!debug was_mem_end:=mem_base; {indicate that everything was previously free}
+@y
+@!debug was_mem_end:=mem_min; {indicate that everything was previously free}
+was_lo_max:=mem_min; was_hi_min:=mem_max;
+@z
+@x module 167
+begin for p:=mem_base to mem_end do free[p]:=false; {you can probably
+ do this faster}
+@y
+begin for p:=mem_min to lo_mem_max do free[p]:=false; {you can probably
+ do this faster}
+for p:=hi_mem_min to mem_end do free[p]:=false; {ditto}
+@z
+@x module 167
+for p:=0 to mem_end do was_free[p]:=free[p]; {|was_free:=free| might be faster}
+was_mem_end:=mem_end;
+@y
+for p:=mem_min to lo_mem_max do was_free[p]:=free[p];
+for p:=hi_mem_min to mem_end do was_free[p]:=free[p];
+ {|was_free:=free| might be faster}
+was_mem_end:=mem_end; was_lo_max:=lo_mem_max; was_hi_min:=hi_mem_min;
+@z
+@x module 168
+ begin if (p>mem_end)or(p<second_mem) then clobbered:=true
+@y
+ begin if (p>mem_end)or(p<hi_mem_min) then clobbered:=true
+@z
+@x module 169
+repeat if (p>=hi_mem_base)or(p<first_mem) then clobbered:=true
+ else if (rlink(p)>=hi_mem_base)or(rlink(p)<first_mem) then clobbered:=true
+ else if not(is_empty(p))or(node_size(p)<2)or@|
+ (p+node_size(p)>hi_mem_base)or@| (llink(rlink(p))<>p) then clobbered:=true;
+@y
+repeat if (p>=lo_mem_max)or(p<mem_min) then clobbered:=true
+ else if (rlink(p)>=lo_mem_max)or(rlink(p)<mem_min) then clobbered:=true
+ else if not(is_empty(p))or(node_size(p)<2)or@|
+ (p+node_size(p)>lo_mem_max)or@| (llink(rlink(p))<>p) then clobbered:=true;
+@z
+@x module 170
+p:=mem_base;
+while p<=hi_mem_base do {node |p| should not be empty}
+@y
+p:=mem_min;
+while p<=lo_mem_max do {node |p| should not be empty}
+@z
+@x module 170
+ while (p<=hi_mem_base) and not free[p] do incr(p);
+ while (p<=hi_mem_base) and free[p] do incr(p);
+@y
+ while (p<=lo_mem_max) and not free[p] do incr(p);
+ while (p<=lo_mem_max) and free[p] do incr(p);
+@z
+@x module 171
+for p:=mem_base to mem_end do
+ if not free[p] and ((p>was_mem_end) or was_free[p]) then
+@y
+for p:=mem_min to lo_mem_max do
+ if not free[p] and ((p>was_lo_max) or was_free[p]) then
+ begin print_char(" "); print_int(p);
+ end;
+for p:=hi_mem_min to mem_end do
+ if not free[p] and
+ ((p<was_hi_min) or (p>was_mem_end) or was_free[p]) then
+@z
+@x module 172
+begin for q:=mem_base to mem_end do
+@y
+begin for q:=mem_min to lo_mem_max do
+@z
+@x module 172
+@<Search |eqtb| for equivalents equal to |p|@>;
+@y
+for q:=hi_mem_min to mem_end do
+ begin if link(q)=p then
+ begin print_nl("LINK("); print_int(q); print_char(")");
+ end;
+ if info(q)=p then
+ begin print_nl("INFO("); print_int(q); print_char(")");
+ end;
+ end;
+@<Search |eqtb| for equivalents equal to |p|@>;
+@z
+@x module 176
+if (p<hi_mem_base)or(p>mem_end) then print_esc("CLOBBERED.")
+@y
+if (p<hi_mem_min)or(p>mem_end) then print_esc("CLOBBERED.")
+@z
+@x module 178
+begin if (p<mem_base)or(p>=hi_mem_base) then print_char("*")
+@y
+begin if (p<mem_min)or(p>=lo_mem_max) then print_char("*")
+@z
+@x module 293
+if (p<hi_mem_base) or (p>mem_end) then
+@y
+if (p<hi_mem_min) or (p>mem_end) then
+@z
+@x module 639
+ print_int(dyn_used); print("; max so far: ");
+ print_int(max_var_used); print_char("&");
+ print_int(mem_end+1-hi_mem_base); print_ln;
+@y
+ print_int(dyn_used); print("; still untouched: ");
+ print_int(hi_mem_min-lo_mem_max-1); print_ln;
+@z
+@x module 857
+begin save_link:=link(cur_p); {this is OK even if |cur_p=null|}
+link(cur_p):=null; print_nl(""); short_display(link(printed_node));
+link(cur_p):=save_link; printed_node:=cur_p;
+@y
+begin print_nl("");
+if cur_p=null then short_display(link(printed_node))
+else begin save_link:=link(cur_p);
+ link(cur_p):=null; print_nl(""); short_display(link(printed_node));
+ link(cur_p):=save_link;
+ end;
+printed_node:=cur_p;
+@z
+@x module 1249
+if 2*max_halfword<hi_mem_base-mem_base then bad:=41;
+@y
+if 2*max_halfword<mem_top-mem_min then bad:=41;
+@z
+@x module 1307
+dump_int(hi_mem_base);@/
+@y
+dump_int(mem_bot);@/
+dump_int(mem_top);@/
+@z
+@x module 1308
+if x<>hi_mem_base then goto bad_fmt;
+@y
+if x<>mem_bot then goto bad_fmt;
+undump_int(x);
+if x<>mem_top then goto bad_fmt;
+@z
+@x module 1311
+dump_int(rover);
+p:=mem_base; q:=rover; x:=0;
+@y
+dump_int(lo_mem_max); dump_int(rover);
+p:=mem_bot; q:=rover; x:=0;
+@z
+@x module 1311
+var_used:=var_used+hi_mem_base-p; dyn_used:=mem_end+1-hi_mem_base;@/
+dump_int(mem_end); dump_int(avail);
+for k:=p to mem_end do dump_wd(mem[k]);
+x:=x+mem_end+1-p;
+@y
+var_used:=var_used+lo_mem_max-p; dyn_used:=mem_end+1-hi_mem_min;@/
+for k:=p to lo_mem_max do dump_wd(mem[k]);
+x:=x+lo_mem_max+1-p;
+dump_int(hi_mem_min); dump_int(avail);
+for k:=hi_mem_min to mem_end do dump_wd(mem[k]);
+x:=x+mem_end+1-hi_mem_min;
+@z
+@x module 1312
+undump(min_halfword)(hi_mem_base)(rover);
+p:=mem_base; q:=rover; x:=0;
+repeat for k:=p to q+1 do undump_wd(mem[k]);
+p:=q+node_size(q);
+if (p>hi_mem_base)or((q>=rlink(q))and(rlink(q)<>rover)) then goto bad_fmt;
+q:=rlink(q);
+until q=rover;
+undump_size(hi_mem_base)(mem_max)('mem max')(mem_end);
+undump(null)(mem_end)(avail);
+for k:=p to mem_end do undump_wd(mem[k]);
+undump_int(var_used); undump_int(dyn_used);
+max_var_used:=var_used
+@y
+undump(lo_mem_stat_max+1000)(hi_mem_stat_min-1)(lo_mem_max);
+undump(lo_mem_stat_max+1)(lo_mem_max)(rover);
+p:=mem_bot; q:=rover; x:=0;
+repeat for k:=p to q+1 do undump_wd(mem[k]);
+p:=q+node_size(q);
+if (p>lo_mem_max)or((q>=rlink(q))and(rlink(q)<>rover)) then goto bad_fmt;
+q:=rlink(q);
+until q=rover;
+for k:=p to lo_mem_max do undump_wd(mem[k]);
+if mem_min<mem_bot-2 then {make more low memory available}
+ begin p:=llink(rover); q:=mem_min+1;
+ link(mem_min):=null; info(mem_min):=null; {we don't use the bottom word}
+ rlink(p):=q; llink(rover):=q;@/
+ rlink(q):=rover; llink(q):=p; link(q):=empty_flag;
+ node_size(q):=mem_bot-q;
+ end;
+undump(lo_mem_max+1)(hi_mem_stat_min)(hi_mem_min);
+undump(null)(mem_top)(avail); mem_end:=mem_top;
+for k:=hi_mem_min to mem_end do undump_wd(mem[k]);
+undump_int(var_used); undump_int(dyn_used)
+@z
+@x module 1323
+undump(min_halfword)(hi_mem_base)(font_glue[k]);
+@y
+undump(min_halfword)(lo_mem_max)(font_glue[k]);
+@z
+@x module 1334
+ wlog_ln(' ',max_var_used:1,'&',mem_end+1-hi_mem_base:1,@|
+ ' words of memory out of ',
+ hi_mem_base-mem_base:1,'&',mem_max+1-hi_mem_base:1);@/
+@y
+ wlog_ln(' ',lo_mem_max-mem_min+mem_end-hi_mem_min+2:1,@|
+ ' words of memory out of ',mem_end+1-mem_min:1);@/
+@z that's the end of change number 300!
+
+301. node_size field could overflow if low mem was too large (Dec 20, 1984)
+[This bug existed from the beginning, if hi_mem_base-first_mem>max_halfword!]
+@x module 111
+if (mem_min<min_halfword)or(mem_max>=max_halfword) then bad:=14;
+@y
+if (mem_min<min_halfword)or(mem_max>=max_halfword)or@|
+ (mem_bot-mem_min>max_halfword+1) then bad:=14;
+@z
+@x module 125
+@!t:pointer; {temporary register}
+@y
+@!t:integer; {temporary register}
+@z
+@x module 125
+if lo_mem_max+2<hi_mem_min then
+@y
+if lo_mem_max+2<hi_mem_min then if lo_mem_max+2<=mem_bot+max_halfword then
+@z
+@x module 126
+rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/
+@y
+if t>mem_bot+max_halfword then t:=mem_bot+max_halfword;
+rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/
+@z
+
+302. Improvement to missing-format-file error (DRF, 5 Jan 85)
+@x module 524
+var j:0..buf_size; {the first space after the file name}
+begin if buffer[loc]="&" then
+ begin incr(loc); j:=loc; buffer[last]:=" ";
+ while buffer[j]<>" " do incr(j);
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(fmt_file) then
+ begin loc:=j; goto found;
+ end;@/
+ pack_buffered_name(format_area_length,loc,j-1);
+ {now try the system format file area}
+ if w_open_in(fmt_file) then
+ begin loc:=j; goto found;
+ end;
+ wake_up_terminal;
+ wterm_ln('Sorry, I can''t find that format;',' will try PLAIN.');
+@.Sorry, I can't find...@>
+ end;
+ {now pull out all the stops: try for the system \.{PLAIN} file}
+pack_buffered_name(format_default_length-format_ext_length,1,0);
+if not w_open_in(fmt_file) then
+ begin wake_up_terminal;
+ wterm_ln('I can''t find the PLAIN format file!');
+@.I can't find PLAIN...@>
+@.PLAIN@>
+ open_fmt_file:=false; return;
+ end;
+found:open_fmt_file:=true;
+@y
+var j:0..buf_size; {the first space after the format file name}
+begin j:=loc;
+if buffer[loc]="&" then
+ begin incr(loc); j:=loc; buffer[last]:=" ";
+ while buffer[j]<>" " do incr(j);
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(fmt_file) then goto found;
+ pack_buffered_name(format_area_length,loc,j-1);
+ {now try the system format file area}
+ if w_open_in(fmt_file) then goto found;
+ wake_up_terminal;
+ wterm_ln('Sorry, I can''t find that format;',' will try PLAIN.');
+@.Sorry, I can't find...@>
+ update_terminal;
+ end;
+ {now pull out all the stops: try for the system \.{PLAIN} file}
+pack_buffered_name(format_default_length-format_ext_length,1,0);
+if not w_open_in(fmt_file) then
+ begin wake_up_terminal;
+ wterm_ln('I can''t find the PLAIN format file!');
+@.I can't find PLAIN...@>
+@.PLAIN@>
+ open_fmt_file:=false; return;
+ end;
+found:loc:=j; open_fmt_file:=true;
+@z
+
+303. Welcoming message appears sooner (DRF, Jan 7)
+@x module 61
+else begin print(format_ident); print_ln;
+ end;
+@y
+else begin print(format_ident); print_ln;
+ end;
+update_terminal;
+@z
+
+304. Minor change to help message in "confusion" (Jan 23)
+@x module 95
+ help2("One of your earlier faux pas has wounded me deeply,")@/
+ ("so I'm barely conscious. Please fix it and try again.");
+@y
+ help2("One of your faux pas seems to have wounded me deeply...")@/
+ ("in fact, I'm barely conscious. Please fix it and try again.");
+@z
+
+305. Improved logic for `(see the transcript file...)' (Jan 23)
+@x module 245
+if history=spotless then history:=warning_issued;
+if (tracing_online<=0)and(selector=term_and_log) then decr(selector);
+@y
+if (tracing_online<=0)and(selector=term_and_log) then
+ begin decr(selector);
+ if history=spotless then history:=warning_issued;
+ end;
+@z
+
+306. Change 291 violated standard PASCAL (CET1, Feb 18)
+@x module 331
+for first:=0 to buf_size do buffer[first]:=0;
+@y since some procedures `threaten' |first| globally [BS/6192 sec 6.8.3.9]
+first:=buf_size; repeat buffer[first]:=0; decr(first); until first=0;
+@z
+
+307. Nonexistent character could be output via ligature hyphenation (April 11)
+@x module 582 (slight inefficiency removed while correcting this)
+begin if (font_bc[f]<=c)and(font_ec[f]>=c) then
+@y
+begin if font_bc[f]<=c then if font_ec[f]>=c then
+@z
+@x module 892 (likewise)
+@!hu:array[1..63] of ASCII_code; {like |hc|, before conversion to lowercase}
+@y
+@!hu:array[1..63] of ASCII_code; {like |hc|, before conversion to lowercase}
+@!hyf_char:integer; {hyphen character of the relevant font}
+@z
+@x module 896 (likewise)
+done2: if hyphen_char[hf]<0 then goto done1;
+if hyphen_char[hf]>255 then goto done1;
+@y
+done2: hyf_char:=hyphen_char[hf];
+if hyf_char<0 then goto done1;
+if hyf_char>255 then goto done1;
+@z
+@x module 912 and another local variable
+@!c:ASCII_code; {character temporarily replaced by a hyphen}
+@y
+@!c:ASCII_code; {character temporarily replaced by a hyphen}
+@!hyf_node:pointer; {the hyphen, if it exists}
+@z
+@x module 915 (this is the real bugfix)
+minor_tail:=null; c:=hu[i+1]; hu[i+1]:=hyphen_char[hf];
+repeat l:=reconstitute(l+1,i+1);
+if minor_tail=null then pre_break(r):=link(hold_head)
+else link(minor_tail):=link(hold_head);
+minor_tail:=link(hold_head);
+if link(minor_tail)<>null then minor_tail:=link(minor_tail);
+until l>i;
+hu[i+1]:=c; {restore the character in the hyphen position}
+decr(l); hyf[l]:=0
+@y
+minor_tail:=null; hyf_node:=new_character(hf,hyf_char);
+if hyf_node<>null then
+ begin incr(i); c:=hu[i]; hu[i]:=hyf_char;
+ end;
+repeat l:=reconstitute(l+1,i);
+if minor_tail=null then pre_break(r):=link(hold_head)
+else link(minor_tail):=link(hold_head);
+minor_tail:=link(hold_head);
+if link(minor_tail)<>null then minor_tail:=link(minor_tail);
+until l=i;
+if hyf_node<>null then
+ begin hu[i]:=c; {restore the character in the hyphen position}
+ free_avail(hyf_node); decr(i); l:=i;
+ end;
+hyf[i]:=0
+@z
+@x module 918
+begin r:=new_disc; pre_break(r):=new_character(hf,hyphen_char[hf]);
+@y
+begin r:=new_disc; pre_break(r):=new_character(hf,hyf_char);
+@z
+
+308. Improper memory usage computed by INITEX in change 300. (DRF, April 15)
+@x module 162
+@d hi_mem_stat_min==mem_top-13 {smallest statically allocated word in
+ the one-word |mem|}
+@y
+@d hi_mem_stat_min==mem_top-13 {smallest statically allocated word in
+ the one-word |mem|}
+@d hi_mem_stat_usage=14 {the number of one-word nodes always present}
+@z
+@x module 164 (text macro without parentheses strikes again!)
+var_used:=lo_mem_stat_max+1-mem_bot; dyn_used:=mem_top+1-hi_mem_stat_min;
+@y
+var_used:=lo_mem_stat_max+1-mem_bot; dyn_used:=hi_mem_stat_usage;
+@z
+
+309. A faster |flush_list| routine for the inner loop. (DRF, April 16)
+@x module 123
+var q:pointer; {the successor of node |p|}
+begin while p<>null do
+ begin q:=link(p); free_avail(p); p:=q;
+ end;
+@y
+var @!q,@!r:pointer; {list traversers}
+begin if p<>null then
+ begin r:=p;
+ repeat q:=r; r:=link(r); @!stat decr(dyn_used);@+tats@/
+ until r=null; {now |q| is the last node on the list}
+ link(q):=avail; avail:=p;
+ end;
+@z
+
+310. Kern for accent positioning could disappear after a line break (April 17)
+(Change 269 fixed only part of the problem!)
+@x module 155
+@d explicit=1 {|subtype| of kern nodes from \.{\\kern} and \.{\\/} and accents}
+@y
+@d explicit=1 {|subtype| of kern nodes from \.{\\kern} and \.{\\/}}
+@d acc_kern=2 {|subtype| of kern nodes from accents}
+@z
+@x module 191
+if subtype(p)<=explicit then
+ begin print_esc("kern");
+ if subtype(p)=explicit then print_char(" ");
+ print_scaled(width(p));
+@y
+if subtype(p)<>mu_glue then
+ begin print_esc("kern");
+ if subtype(p)<>normal then print_char(" ");
+ print_scaled(width(p));
+ if subtype(p)=acc_kern then print(" (for accent)");
+@.for accent@>
+@z
+@x module 837
+ math_node,kern_node: break_width[1]:=break_width[1]-width(s);
+@y
+ math_node,kern_node: if subtype(s)=acc_kern then goto done
+ else break_width[1]:=break_width[1]-width(s);
+@z
+@x module 879
+ if non_discardable(q) then goto done1;
+@y
+ if non_discardable(q) then goto done1;
+ if subtype(q)=acc_kern then if type(q)=kern_node then goto done1;
+@z
+@x module 1125
+r:=new_kern(delta); subtype(r):=explicit; link(tail):=r; link(r):=p;
+tail:=new_kern(-a-delta); subtype(tail):=explicit; link(p):=tail; p:=q;
+@y
+r:=new_kern(delta); subtype(r):=acc_kern; link(tail):=r; link(r):=p;
+tail:=new_kern(-a-delta); subtype(tail):=acc_kern; link(p):=tail; p:=q;
+@z
+
+311. \lastbox and \unkern must not remove discretionary replacements (April 18)
+(Change 236 introduced this bug!)
+@x module 1079
+label exit;
+var p:pointer; {runs through the current list}
+@y
+label exit, done;
+var @!p,@!q:pointer; {run through the current list}
+@!m:quarterword; {the length of a replacement list}
+@z
+@x modules 1080 and 1081
+ @<Apologize for inability to do \.{\\lastbox} now@>
+else begin if not is_char_node(tail) then
+ if (type(tail)=hlist_node)or(type(tail)=vlist_node) then
+ begin p:=head; cur_box:=tail; shift_amount(cur_box):=0;
+ while link(p)<>tail do p:=link(p);
+ tail:=p; link(p):=null;
+ end;
+ end;
+end
+
+@ @<Apologize for inability to do \.{\\lastbox} now@>=
+begin you_cant;
+help2("Sorry...I'm usually unable to take things from the current")@/
+ ("page. This \lastbox will therefore be void."); error;
+end
+@y
+ begin you_cant;
+ help2("Sorry...I usually can't take things from the current page.")@/
+ ("This \lastbox will therefore be void."); error;
+ end
+else begin if not is_char_node(tail) then
+ if (type(tail)=hlist_node)or(type(tail)=vlist_node) then
+ @<Remove the last box, unless it's part of a discretionary@>;
+ end;
+end
+
+@ @<Remove the last box...@>=
+begin q:=head;
+repeat p:=q;
+if not is_char_node(q) then if type(q)=disc_node then
+ begin for m:=1 to replace_count(q) do p:=link(p);
+ if p=tail then goto done;
+ end;
+q:=link(p);
+until q=tail;
+cur_box:=tail; shift_amount(cur_box):=0;
+tail:=p; link(p):=null;
+done: end
+@z
+@x module 1105
+var p:pointer; {runs through the current list}
+@y
+label exit;
+var @!p,@!q:pointer; {run through the current list}
+@!m:quarterword; {the length of a replacement list}
+@z
+@x module 1105
+ begin p:=head;
+ while link(p)<>tail do p:=link(p);
+ link(p):=null; flush_node_list(tail); tail:=p;
+ end;
+ end;
+end;
+@y
+ begin q:=head;
+ repeat p:=q;
+ if not is_char_node(q) then if type(q)=disc_node then
+ begin for m:=1 to replace_count(q) do p:=link(p);
+ if p=tail then return;
+ end;
+ q:=link(p);
+ until q=tail;
+ link(p):=null; flush_node_list(tail); tail:=p;
+ end;
+ end;
+exit:end;
+@z
+@x module 1106
+ help2("Sorry...I'm usually unable to take things from the current")@/
+ ("page. Try `I\vskip-\lastskip' instead.");
+ if cur_chr=kern_node then help_line[0]:=
+ ("page. Try `I\kern-\lastkern' instead.")
+ else if cur_chr<>glue_node then help_line[0]:=@|
+ ("page. Perhaps you can make the output routine do it.");
+@y
+ help2("Sorry...I usually can't take things from the current page.")@/
+ ("Try `I\vskip-\lastskip' instead.");
+ if cur_chr=kern_node then help_line[0]:=
+ ("Try `I\kern-\lastkern' instead.")
+ else if cur_chr<>glue_node then help_line[0]:=@|
+ ("Perhaps you can make the output routine do it.");
+@z
+@x module 1120
+if n<128 then replace_count(tail):=n
+@y
+if n<=max_quarterword then replace_count(tail):=n
+@z
+
+
+*** Note by DEK, 18 Apr 1985
+(The code for version 1.3 has been published by Addison-Wesley under the
+title "TeX: The Program". It was phototypeset on our APS, but does not
+contain changes > 300. Some of those changes have been made in versions
+of TeX called 1.3; but the exact definition of version 1.3 is somewhat
+vague. Version 1.4 was released on April 18, 1985, after
+making change 311. At present I believe Version 1.4 will be identical
+to Version 2.0 except perhaps for things like index entries in the
+documentation and slightly revised help messages. Version 2.0 will
+coincide with the publication of Volume 1 of Computers and Typesetting,
+which will come out when METAFONT and Computer Modern are both finished.)
+ Version 1.5
+312. if nonstandard area specified, don't try TEX_area too. (DRF, 26 Apr 85)
+@x module 537
+ pack_file_name(cur_name,TEX_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+@y
+ if cur_area="" then
+ begin pack_file_name(cur_name,TEX_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+ end;
+@z
+
+313. length of \write need no longer be limited (cf 235). (NBT, 30 Apr 85)
+@x module 1370
+show_token_list(link(def_ref),null,buf_size-10); print_ln;
+@y
+show_token_list(link(def_ref),null,10000000); print_ln;
+@z
+
+314. bug introduced in #300, just lucky it didn't cause trouble (CET1, 8 May 85)
+@x module 162
+@d active==mem_top-6 {head of active list in |line_break|, needs two words}
+@y
+@d active==mem_top-7 {head of active list in |line_break|, needs two words}
+@z
+
+315. Missing eoln after last typeout (Gropp, 11 May 85)
+@x module 1332
+ wterm('Ouch---my internal constants have been',
+@y
+ wterm_ln('Ouch---my internal constants have been',
+@z
+
+316. Redundancy (CET1, 17 May 85)
+[Change 287 was apparently introduced because of a faulty assumption,
+although its change to the calling of "initialize" was valid.]
+@x module 1332
+ begin t_open_out;
+ wterm_ln('Ouch---my internal constants have been',
+ ' clobbered!---case ',bad:1);
+@y
+ begin wterm_ln('Ouch---my internal constants have been clobbered!',
+ '---case ',bad:1);
+@z
+
+317. batchmode to see if log file couldn't be opened (DRF, 22 May 85)
+@x module 92
+if interaction=batch_mode then decr(selector);
+if job_name=0 then open_log_file;
+@y
+if job_name=0 then open_log_file;
+if interaction=batch_mode then decr(selector);
+@z
+
+318. If the string pool overflows while reading the command line... (22 May 85)
+[This bug was first found in METAFONT, when it could occur more easily]
+@x module 525
+begin str_room(name_length);
+for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+make_name_string:=make_string;
+@y
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings) then
+ make_name_string:="?"
+else begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+@z
+
+319. Bug in \edef\foo{\iffalse\fi\the\toks0} (found by Dan Brotsky, 7 Aug 85)
+@x module 478
+ if cur_cmd<>the then goto done2;
+ q:=the_toks;
+ if link(temp_head)<>null then
+ begin link(p):=link(temp_head); p:=q;
+ end;
+@y
+ if cur_cmd<=max_command then goto done2;
+ if cur_cmd<>the then expand
+ else begin q:=the_toks;
+ if link(temp_head)<>null then
+ begin link(p):=link(temp_head); p:=q;
+ end;
+ end;
+@z
+
+*** Version 1.5 was released on August 7, 1985.
+
+320. Lowercase `plain' for consistency with the manual
+@x module 521
+TEX_format_default:='TeXformats:PLAIN.fmt';
+@.TeXformats@>
+@.PLAIN@>
+@y
+TEX_format_default:='TeXformats:plain.fmt';
+@.TeXformats@>
+@.plain@>
+@z (index entries also changed to lowercase in modules 524, 1331)
+
+321. Terminal woken up by \show and \showthe (November 27, 1985)
+@x module 1294
+begin get_token; print_nl("> ");
+@y
+begin get_token;
+if interaction=error_stop_mode then wake_up_terminal;
+print_nl("> ");
+@z
+@x module 1297
+begin p:=the_toks; print_nl("> "); token_show(temp_head);
+@y
+begin p:=the_toks;
+if interaction=error_stop_mode then wake_up_terminal;
+print_nl("> "); token_show(temp_head);
+@z
+ Changes subsequent to `Version 2.0' as published in C&T, Volume B:
+
+322. Trivial change to a help message.
+@x module 1283
+ ("Pretend that you're Hercule Poirot, examine all clues,")@/
+@y
+ ("Pretend that you're Hercule Poirot: Examine all clues,")@/
+@z
+
+323. Precaution since |index| can be as high as |max_in_open|. (DRF)
+@x module 14
+if hash_prime>hash_size then bad:=5;
+@y
+if hash_prime>hash_size then bad:=5;
+if max_in_open>=128 then bad:=6;
+@z
+
+324. \kern clobbered at end of pre-break list in discretionary break.
+@x module 881
+ |disc_break:=true|@>;
+ if not is_char_node(q) then
+ if (type(q)=math_node)or(type(q)=kern_node) then width(q):=0;
+@y
+ |disc_break:=true|@>
+ else if (type(q)=math_node)or(type(q)=kern_node) then width(q):=0;
+@z
+
+325. width after discretionary didn't take account of discarded nodes.
+@x module 830 (slight redefinition)
+@!t:quarterword; {replacement count, if |cur_p| is a discretionary node}
+@y
+@!t:integer; {node count, if |cur_p| is a discretionary node}
+@z
+@x module 837 (slight rearrangement)
+if (break_type=unhyphenated)or(cur_p=null) then
+ begin s:=cur_p;
+ while s<>null do
+ begin if is_char_node(s) then goto done;
+ case type(s) of
+ glue_node:@<Subtract glue from |break_width|@>;
+ penalty_node: do_nothing;
+ math_node,kern_node: if subtype(s)=acc_kern then goto done
+ else break_width[1]:=break_width[1]-width(s);
+ othercases goto done
+ endcases;@/
+ s:=link(s);
+ end;
+ end
+else @<Compute the discretionary |break_width| values@>;
+@y
+s:=cur_p;
+if break_type>unhyphenated then if cur_p<>null then
+ @<Compute the discretionary |break_width| values@>;
+while s<>null do
+ begin if is_char_node(s) then goto done;
+ case type(s) of
+ glue_node:@<Subtract glue from |break_width|@>;
+ penalty_node: do_nothing;
+ math_node,kern_node: if subtype(s)=acc_kern then goto done
+ else break_width[1]:=break_width[1]-width(s);
+ othercases goto done
+ endcases;@/
+ s:=link(s);
+ end;
+@z
+@x module 840 (this is the main change to fix the bug)
+begin t:=replace_count(cur_p); s:=cur_p;
+while t>0 do
+ begin decr(t); s:=link(s);
+ @<Subtract the width of node |s| from |break_width|@>;
+ end;
+s:=post_break(cur_p);
+while s<>null do
+ begin @<Add the width of node |s| to |break_width|@>;
+@y
+begin t:=replace_count(cur_p); v:=cur_p; s:=post_break(cur_p);
+while t>0 do
+ begin decr(t); v:=link(v);
+ @<Subtract the width of node |v| from |break_width|@>;
+ end;
+while s<>null do
+ begin @<Add the width of node |s| to |break_width| and increase |t|,
+ unless it's discardable@>;
+@z
+@x module 840, continued
+break_width[1]:=break_width[1]+disc_width;
+@y
+break_width[1]:=break_width[1]+disc_width;
+if t=0 then s:=link(v); {more nodes may also be discardable after the break}
+@z
+@x module 841 (here we just change |s| to |v|)
+@<Subtract the width of node |s|...@>=
+if is_char_node(s) then
+ begin f:=font(s);
+ break_width[1]:=break_width[1]-char_width(f)(char_info(f)(character(s)));
+ end
+else case type(s) of
+ ligature_node: begin f:=font(lig_char(s));@/
+ break_width[1]:=@|break_width[1]-
+ char_width(f)(char_info(f)(character(lig_char(s))));
+ end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]-width(s);
+@y
+@<Subtract the width of node |v|...@>=
+if is_char_node(v) then
+ begin f:=font(v);
+ break_width[1]:=break_width[1]-char_width(f)(char_info(f)(character(v)));
+ end
+else case type(v) of
+ ligature_node: begin f:=font(lig_char(v));@/
+ break_width[1]:=@|break_width[1]-
+ char_width(f)(char_info(f)(character(lig_char(v))));
+ end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]-width(v);
+@z
+@x module 842
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]+width(s);
+ othercases confusion("disc2")
+@:this can't happen disc2}{\quad disc2@>
+ endcases
+@y
+ hlist_node,vlist_node,rule_node:break_width[1]:=break_width[1]+width(s);
+ kern_node: if (t=0)and(subtype(s)<>acc_kern) then t:=-1 {discardable}
+ else break_width[1]:=break_width[1]+width(s);
+ othercases confusion("disc2")
+@:this can't happen disc2}{\quad disc2@>
+ endcases;
+incr(t)
+@z
+
+*** Version 2.1 was released on January 26, 1987.
+
+326. Removal of redundant code (found by Pat Monardo, 5 Feb 87)
+@x module 1224 [this change need not be made, since it has no effect]
+ char_def_code: define(p,char_given,cur_val);
+ math_char_def_code: define(p,math_given,cur_val);
+@y the cases cannot occur, so we simply don't list them
+@z
+
+327. More robustness is needed when debugging (Ronaldo Am\'a, 14 Apr 87)
+@x module 174
+begin while p>null do
+@y maybe mem_min>null (but nodes shouldn't be put in location mem_min exactly)
+begin while p>mem_min do
+@z
+@x module 182
+@p procedure show_node_list(@!p:pointer); {prints a node list symbolically}
+@y
+@p procedure show_node_list(@!p:integer); {prints a node list symbolically}
+@z
+@x module 182, continued
+while p>null do
+@y
+while p>mem_min do
+@z
+
+328. Storage allocation can be more elegant and efficient (4/21/87)
+@x module 127
+if r=p then if ((rlink(p)<>rover) or (llink(p)<>rover)) then
+@y
+if r=p then if rlink(p)<>p then
+@z
+
+329. Miscalculation of empty-line condition (April 22, 1987)
+@x module 360
+ begin if limit=start then {previous line was empty}
+@y
+ begin if (end_line_char<0)or(end_line_char>127) then incr(limit);
+ if limit=start then {previous line was empty}
+@z
+
+330. A case where we don't have to assume system bookkeeping (April 28, 1987)
+@x module 560
+done: b_close(tfm_file); read_font_info:=g;
+@y [suggested by Jim Sterken]
+done: if file_opened then b_close(tfm_file);
+read_font_info:=g;
+@z [this was not a bug, according to the comment in module 28]
+
+331. jump_out must fix unfinished output (Found by Klaus Gunterman, 3 Aug 87)
+@x module 593
+doing_leaders:=false; dead_cycles:=0;
+@y
+doing_leaders:=false; dead_cycles:=0; cur_s:=-1;
+@z
+@x module 617
+cur_s:=-1; ensure_dvi_open;
+@y
+ensure_dvi_open;
+@z
+@x module 640
+dvi_out(eop); incr(total_pages);
+@y
+dvi_out(eop); incr(total_pages); cur_s:=-1;
+@z
+@x module 642
+if total_pages=0 then print_nl("No pages of output.")
+@y
+while cur_s>-1 do
+ begin if cur_s>0 then dvi_out(pop)
+ else begin dvi_out(eop); incr(total_pages);
+ end;
+ decr(cur_s);
+ end;
+if total_pages=0 then print_nl("No pages of output.")
+@z
+
+332. \hangindent=1pt$$\halign{...\cr\noalign{\hrule}}$$ problem (19 Aug 87)
+@x module 805
+q:=link(head);
+@y
+q:=link(head); s:=head;
+@z
+@x module 805, continued
+ q:=link(q);
+@y
+ s:=q; q:=link(q);
+@z
+@x module 806
+if is_running(depth(q)) then depth(q):=depth(p);
+@y
+if is_running(depth(q)) then depth(q):=depth(p);
+if o<>0 then
+ begin r:=link(q); link(q):=null; q:=hpack(q,natural);
+ shift_amount(q):=o; link(q):=r; link(s):=q;
+ end;
+@z
+
+333. \hskip 0pt plus 1fil\ifdim problem (found by Alan Guth, 20 Aug 87)
+@x module 366
+@!cvl_backup,@!radix_backup:small_number; {to save |cur_val_level| and |radix|}
+@y
+@!cvl_backup,@!radix_backup,@!co_backup:small_number;
+ {to save |cur_val_level|, etc.}
+@z
+@x
+backup_backup:=link(backup_head);
+@y
+co_backup:=cur_order; backup_backup:=link(backup_head);
+@z
+@x
+link(backup_head):=backup_backup;
+@y
+cur_order:=co_backup; link(backup_head):=backup_backup;
+@z
+
+334. leaders too sensitive near exact multiples (M. F. Bridgland, 9 Nov 87)
+@x module 626
+ begin edge:=cur_h+rule_wd; lx:=0;
+@y
+ begin rule_wd:=rule_wd+10; {compensate for floating-point rounding}
+ edge:=cur_h+rule_wd; lx:=0;
+@z
+@x ibid
+ cur_h:=edge; goto next_p;
+@y
+ cur_h:=edge-10; goto next_p;
+@z
+@x module 635
+ begin edge:=cur_v+rule_ht; lx:=0;
+@y
+ begin rule_ht:=rule_ht+10; {compensate for floating-point rounding}
+ edge:=cur_v+rule_ht; lx:=0;
+@z
+@x ibid
+ cur_v:=edge; goto next_p;
+@y
+ cur_v:=edge-10; goto next_p;
+@z
+
+335. Glitch in fixed-point multiplication of negatives (W.G. Sullivan, 17Nov87)
+@x module 572
+begin alpha:=16*z; beta:=16;
+while z>=@'40000000 do
+ begin z:=z div 2; beta:=beta div 2;
+ end;
+@y
+begin alpha:=16;
+while z>=@'40000000 do
+ begin z:=z div 2; alpha:=alpha+alpha;
+ end;
+beta:=256 div alpha; alpha:=alpha*z;
+@z
+
+336. If there are no \patterns and some \lccode is 1 (Breitenlohner, 12Dec87)
+@x module 952
+trie_link(0):=0; trie_char(0):=0; trie_op(0):=0;
+@y
+trie_link(0):=0; trie_char(0):=0; trie_op(0):=min_quarterword;
+@z
+
+337. \csname might encounter undefined_cs in a group (Chris Thompson, 23Dec87)
+@x module 372
+ begin eqtb[cur_cs]:=eqtb[frozen_relax];
+@y
+ begin eq_define(cur_cs,relax,256);
+@z
+
+338. \outer\def\a0{}\a\a shows temp_head list garbage (Silvio Levy, 20Apr88)
+@x module 391
+repeat if (info(r)>match_token+127)or(info(r)<match_token) then s:=null
+else begin match_chr:=info(r)-match_token; s:=link(r); r:=s;
+ p:=temp_head; link(p):=null; m:=0;
+@y
+repeat link(temp_head):=null;
+if (info(r)>match_token+127)or(info(r)<match_token) then s:=null
+else begin match_chr:=info(r)-match_token; s:=link(r); r:=s;
+ p:=temp_head; m:=0;
+@z
+
+339. \def\\#1{}\input a\\\z failed (Robert Messer, 24Apr88)
+@x module 259
+var h:integer; {hash code}
+@y
+var h:integer; {hash code}
+@!d:integer; {number of characters in incomplete current string}
+@z
+@x module 260
+str_room(l);
+for k:=j to j+l-1 do append_char(buffer[k]);
+text(p):=make_string;
+@y
+str_room(l); d:=cur_length;
+while pool_ptr>str_start[str_ptr] do
+ begin decr(pool_ptr); str_pool[pool_ptr+l]:=str_pool[pool_ptr];
+ end; {move current string up to make room for another}
+for k:=j to j+l-1 do append_char(buffer[k]);
+text(p):=make_string; pool_ptr:=pool_ptr+d;
+@z
+
+340. Make patterns work when trie_min=0 (Peter Breitenlohner, 10May88)
+@x module 951
+trie_max:=128; trie_min:=128; trie_link(0):=1; trie_taken[0]:=false;
+@y
+trie_max:=128; trie_min:=128; trie_link(0):=1; trie_taken[0]:=false;
+trie_link(trie_size):=0; trie_back(0):=trie_size; {wrap around}
+@z
+@x module 953
+begin c:=trie_c[p]; {we have |c>0|}
+if c<trie_min then trie_min:=c;
+z:=trie_link(trie_min-1); {get the first conceivably good hole}
+@y
+begin c:=trie_c[p];
+if c<trie_min then trie_min:=c;
+if trie_min=0 then z:=trie_link(trie_size)
+else z:=trie_link(trie_min-1); {get the first conceivably good hole}
+@z
+@x module 966
+r:=0; {finally, we will zero out the holes}
+@y
+r:=trie_size; {finally, we will zero out the holes}
+@z
+
+341. Avoid possible trie_pointer out of range (24May88)
+@x module 923
+hc[0]:=127; hc[hn+1]:=127; hc[hn+2]:=256; {insert delimiters}
+@y
+hc[0]:=127; hc[hn+1]:=127; hc[hn+2]:=128; {insert delimiters}
+@z
+
+342. \input a\romannumeral1 etc.; similar to bug 339. (25May88)
+@x module 464
+@p function str_toks:pointer; {changes the current string to a token list}
+@y
+@p function str_toks(@!b:pool_pointer):pointer;
+ {changes the string |str_pool[b..pool_ptr]| to a token list}
+@z
+@x module 464, continued
+p:=temp_head; link(p):=null; k:=str_start[str_ptr];
+@y
+p:=temp_head; link(p):=null; k:=b;
+@z
+@x module 464, concluded
+pool_ptr:=str_start[str_ptr]; str_toks:=p;
+@y
+pool_ptr:=b; str_toks:=p;
+@z
+@x module 465
+begin get_x_token; scan_something_internal(tok_val,false);
+if cur_val_level>=ident_val then @<Copy the token list@>
+else begin old_setting:=selector; selector:=new_string;
+@y
+@!b:pool_pointer; {base of temporary string}
+begin get_x_token; scan_something_internal(tok_val,false);
+if cur_val_level>=ident_val then @<Copy the token list@>
+else begin old_setting:=selector; selector:=new_string; b:=pool_ptr;
+@z
+@x module 465, continued
+ selector:=old_setting; the_toks:=str_toks;
+@y
+ selector:=old_setting; the_toks:=str_toks(b);
+@z
+@x module 470
+begin c:=cur_chr; @<Scan the argument for command |c|@>;
+old_setting:=selector; selector:=new_string;
+@<Print the result of command |c|@>;
+selector:=old_setting; link(garbage):=str_toks; ins_list(link(temp_head));
+@y
+@!b:pool_pointer; {base of temporary string}
+begin c:=cur_chr; @<Scan the argument for command |c|@>;
+old_setting:=selector; selector:=new_string; b:=pool_ptr;
+@<Print the result of command |c|@>;
+selector:=old_setting; link(garbage):=str_toks(b); ins_list(link(temp_head));
+@z
+
+343. **\input\romannumeral6 confusion bypassed (25May88)
+@x module 525
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings) then
+@y
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings)or
+ (cur_length>0) then
+@z
+
+344. Avoid negative dividend rounding upward (Chris Thompson, fixed 19Jun88)
+@x module 126
+else t:=(lo_mem_max+hi_mem_min+2) div 2; {|lo_mem_max+2<=t<hi_mem_min|}
+@y
+else t:=lo_mem_max+1+(hi_mem_min-lo_mem_max)div 2; {|lo_mem_max+2<=t<hi_mem_min|}
+@z
+
+345. Better strategy when near memory overflow (Chris Thompson)
+@x module 126
+begin if lo_mem_max+1000<hi_mem_min then t:=lo_mem_max+1000
+@y
+begin if hi_mem_min-lo_mem_max>=1998 then t:=lo_mem_max+1000
+@z
+
+346. An omission from change 333 (Tsunetoshi Hayashi, reported 20June88)
+@x module 439
+cur_val:=0; cur_val_level:=int_val; radix:=0;
+@y
+cur_val:=0; cur_val_level:=int_val; radix:=0; cur_order:=0;
+@z
+
+347. Avoid fatal_error after terminal eof (Tim Morgan, reported 25Oct88)
+@x module 71 [serious problem occurred if this was called in open_log_file]
+if not input_ln(term_in,true) then fatal_error("End of file on the terminal!");
+@y
+if not input_ln(term_in,true) then t_open_in;
+@z
+
+348. Force terminal output when open_log_file aborts (6Nov88)
+@x module 535
+ begin print_err("I can't write on file `");
+@y
+ begin selector:=term_only; print_err("I can't write on file `");
+@z
+
+349. By popular request, undo #347 and fix the bug a more complex way.
+@x module 71 [this undoes change #347]
+if not input_ln(term_in,true) then t_open_in;
+@y
+if not input_ln(term_in,true) then fatal_error("End of file on the terminal!");
+@z
+@x module 92 [now we get to the new stuff]
+begin if job_name>0 then selector:=term_and_log
+@y
+begin if log_opened then selector:=term_and_log
+@z
+@x module 93
+ error;
+@y
+ if log_opened then error;
+@z
+@x module 527
+@!job_name:str_number; {principal file name}
+@y
+@!job_name:str_number; {principal file name}
+@!log_opened:boolean; {has the transcript file been opened?}
+@z
+@x module 528
+@<Initialize the output...@>=job_name:=0; name_in_progress:=false;
+@y
+@<Initialize the output...@>=
+job_name:=0; name_in_progress:=false; log_opened:=false;
+@z
+@x module 534
+selector:=log_only;
+@y
+selector:=log_only; log_opened:=true;
+@z
+@x module 535
+begin if interaction<scroll_mode then {bypass |fatal_error|}
+ begin selector:=term_only; print_err("I can't write on file `");
+@.I can't write on file x@>
+ print_file_name(cur_name,cur_area,cur_ext); print("'.");@/
+ job_name:=0; history:=fatal_error_stop; jump_out;
+ end; {abort the program without a log file}
+@y
+begin selector:=term_only;
+@z
+@x module 1265 [this change is optional, but it's a slight improvement]
+if job_name<>0 then selector:=selector+2;
+@y
+if log_opened then selector:=selector+2;
+@z
+@x module 1333
+if job_name>0 then
+@y
+if log_opened then
+@z
+@x module 1334
+if job_name>0 then {the log file is open}
+@y
+if log_opened then
+@z
+
+350. Forgotten update in error recovery (Breitenlohner, 20 Jan 89)
+@x module 976
+ glue_ptr(p):=r;
+@y
+ glue_ptr(p):=r; q:=r;
+@z
+@x module 1004
+ glue_ptr(p):=r;
+@y
+ glue_ptr(p):=r; q:=r;
+@z
+
+351. Avoid error \aftergroup\relax\dump (Mittelbach&Schoepf, 17 Feb 89)
+@x module 280
+begin check_full_save_stack;
+save_type(save_ptr):=insert_token; save_level(save_ptr):=level_zero;
+save_index(save_ptr):=t; incr(save_ptr);
+@y
+begin if cur_level>level_one then
+ begin check_full_save_stack;
+ save_type(save_ptr):=insert_token; save_level(save_ptr):=level_zero;
+ save_index(save_ptr):=t; incr(save_ptr);
+ end;
+@z
+
+352. Cosmetic change to validate comment in 115 (Breitenlohner, 20 Mar 89)
+@x module 791
+begin q:=link(cur_align);
+if (cur_align=null)or(q=null) then confusion("endv");
+@y
+begin if cur_align=null then confusion("endv");
+q:=link(cur_align);@+if q=null then confusion("endv");
+@z
+
+353. $$\begingroup\halign{#\cr}$$ caused confusion (Mittelbach, 7 Jun 89)
+@x module 1130
+mmode+halign: if privileged then init_align;
+@y
+mmode+halign: if privileged then
+ if cur_group=math_shift_group then init_align
+ else off_save;
+@z
+
+354. Conflict in global array dig with .5\ifdim.6 (Mittelbach, 20 Jun 89)
+@x module 450
+@!k:small_number; {number of digits in a decimal fraction}
+@y
+@!k,@!kk:small_number; {number of digits in a decimal fraction}
+@!p,@!q:pointer; {top of decimal digit stack}
+@z
+@x module 452
+begin k:=0; get_token; {|point_token| is being re-scanned}
+loop@+ begin get_x_token;
+ if (cur_tok>zero_token+9)or(cur_tok<zero_token) then goto done1;
+ if k<17 then {digits for |k>=17| cannot affect the result}
+ begin dig[k]:=cur_tok-zero_token; incr(k);
+ end;
+ end;
+done1: f:=round_decimals(k);
+@y
+begin k:=0; p:=null; get_token; {|point_token| is being re-scanned}
+loop@+ begin get_x_token;
+ if (cur_tok>zero_token+9)or(cur_tok<zero_token) then goto done1;
+ if k<17 then {digits for |k>=17| cannot affect the result}
+ begin q:=get_avail; link(q):=p; info(q):=cur_tok-zero_token;
+ p:=q; incr(k);
+ end;
+ end;
+done1: for kk:=k downto 1 do
+ begin dig[kk-1]:=info(p); q:=p; p:=link(p); free_avail(q);
+ end;
+f:=round_decimals(k);
+@z
+
+355. String startup problems (Wayne Sullivan, 17 Jul 89)
+@x module 31 (Warning: This affects most change files!)
+ overflow("buffer size",buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+@y
+ @<Report overflow of the input buffer, and abort@>;
+@z
+@x module 35
+consist of the remainder of the command line, after the part that invoked
+\TeX.
+@y
+consist of the remainder of the command line, after the part that invoked
+\TeX.
+
+The first line is special also because it may be read before \TeX\ has
+input a format file. In such cases, normal error messages cannot yet
+be given. The following code uses concepts that will be explained later.
+
+@<Report overflow of the input buffer, and abort@>=
+if format_ident=0 then
+ begin write_ln(term_out,'Buffer size exceeded!'); goto final_end;
+@.Buffer size exceeded@>
+ end
+else begin cur_input.loc_field:=first; cur_input.limit_field:=last-1;
+ overflow("buffer size",buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ end
+@z
+@x module 1310
+k:=pool_ptr-4; undump_four_ASCII
+@y
+k:=pool_ptr-4; undump_four_ASCII;
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr
+@z
+@x module 1332
+tini@/
+@y
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr; fix_date_and_time;
+tini@/
+@z
+@x module 1332
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;@/
+@y
+@z
+
+356. Allow integer products to be 31 bits long (Mittelbach, 16 Aug 89)
+@x module 105
+@p function nx_plus_y(@!n:integer;@!x,@!y:scaled):scaled;
+begin if n<0 then
+ begin negate(x); negate(n);
+ end;
+if n=0 then nx_plus_y:=y
+else if ((x<=(@'7777777777-y) div n)and(-x<=(@'7777777777+y) div n)) then
+ nx_plus_y:=n*x+y
+else begin arith_error:=true; nx_plus_y:=0;
+ end;
+end;
+@y
+@d nx_plus_y(#)==mult_and_add(#,@'7777777777)
+@d mult_integers(#)==mult_and_add(#,0,@'17777777777)
+
+@p function mult_and_add(@!n:integer;@!x,@!y,@!max_answer:scaled):scaled;
+begin if n<0 then
+ begin negate(x); negate(n);
+ end;
+if n=0 then mult_and_add:=y
+else if ((x<=(max_answer-y) div n)and(-x<=(max_answer+y) div n)) then
+ mult_and_add:=n*x+y
+else begin arith_error:=true; mult_and_add:=0;
+ end;
+end;
+@z
+@x module 1240
+ if q=multiply then cur_val:=nx_plus_y(eqtb[l].int,cur_val,0)
+@y
+ if q=multiply then
+ if p=int_val then cur_val:=mult_integers(eqtb[l].int,cur_val)
+ else cur_val:=nx_plus_y(eqtb[l].int,cur_val,0)
+@z
+
+357. Raise the maximum number of tokens normally shown (pointed out by
+John Lavagnino in TeXhax on 19 May 89; corrected on 31 Aug 89).
+@x module 295
+begin if p<>null then show_token_list(link(p),null,1000);
+@y
+begin if p<>null then show_token_list(link(p),null,10000000);
+@z
+@x module 1370 (this part of the change is cosmetic only)
+show_token_list(link(def_ref),null,10000000); print_ln;
+@y
+token_show(def_ref); print_ln;
+@z
+
+358. Like 353, but with $$\begingroup\eqno$$ (Mittelbach, 25 Jul 89)
+@x module 1140
+mmode+eq_no: if privileged then start_eq_no;
+@y
+mmode+eq_no: if privileged then
+ if cur_group=math_shift_group then start_eq_no
+ else off_save;
+@z
+
+359. Major extension to 8-bit character input. (These changes, and a few that
+follow, were discussed during the 10th TUG meeting and installed during
+the week of 10 Sep 1989. They bring the implementation of TeX82 up to date with
+international developments and experience, and I believe they mark the
+final development of this program into a completely stable system.)
+Several changes to the documentation (not the program) are omitted here.
+@x module 18
+@!ASCII_code=0..127; {seven-bit numbers}
+@y
+@!ASCII_code=0..255; {eight-bit numbers}
+@z
+@x module 19
+@d last_text_char=127 {ordinal number of the largest element of |text_char|}
+
+@<Local variables for init...@>=
+@!i:0..last_text_char;
+@y
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Local variables for init...@>=
+@!i:integer;
+@z
+@x module 21
+xchr[0]:=' '; xchr[@'177]:=' ';
+ {ASCII codes 0 and |@'177| do not appear in text}
+@y
+@z
+@x module 23
+for i:=1 to @'37 do xchr[i]:=' ';
+@y changing ' ' to chr(i) here will allow all 8-bit characters to get in
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+@z
+@x module 24
+for i:=1 to @'176 do xord[xchr[i]]:=i;
+@y
+for i:=@'200 to @'377 do xord[xchr[i]]:=i;
+for i:=0 to @'176 do xord[xchr[i]]:=i;
+@z
+@x module 38
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+
+@ @<Glob...@>=
+@!str_pool:packed array[pool_pointer] of ASCII_code; {the characters}
+@y [OK to make si(#)==#-128 and so(#)==#+128 (without parens) in change files]
+Some \PASCAL\ compilers won't pack integers into a single byte unless the
+integers lie in the range |-128..127|. To accommodate such systems
+we access the string pool only via macros that can easily be redefined.
+
+@d si(#) == # {convert from |ASCII_code| to |packed_ASCII_code|}
+@d so(#) == # {convert from |packed_ASCII_code| to |ASCII_code|}
+
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+@!packed_ASCII_code = 0..255; {elements of |str_pool| array}
+
+@ @<Glob...@>=
+@!str_pool:packed array[pool_pointer] of packed_ASCII_code; {the characters}
+@z
+@x module 42
+begin str_pool[pool_ptr]:=#; incr(pool_ptr);
+@y
+begin str_pool[pool_ptr]:=si(#); incr(pool_ptr);
+@z
+@x module 45
+ begin if str_pool[j]<>buffer[k] then
+@y
+ begin if so(str_pool[j])<>buffer[k] then
+@z
+@x module 47
+var k,@!l:0..127; {small indices or counters}
+@y
+var k,@!l:0..255; {small indices or counters}
+@z
+@x module 47
+@<Make the first 128 strings@>;
+@y
+@<Make the first 256 strings@>;
+@z
+@x module 48
+@ @<Make the first 128...@>=
+for k:=0 to 127 do
+ begin if (@<Character |k| cannot be printed@>) then
+ begin append_char("^"); append_char("^");
+ if k<@'100 then append_char(k+@'100)
+ else append_char(k-@'100);
+@y
+@ @d app_lc_hex(#)==l:=#;
+ if l<10 then append_char(l+"0)@+else append_char(l-10+"a")
+
+@<Make the first 256...@>=
+for k:=0 to 255 do
+ begin if (@<Character |k| cannot be printed@>) then
+ begin append_char("^"); append_char("^");
+ if k<@'100 then append_char(k+@'100)
+ else if k<@'200 then append_char(k-@'100)
+ else begin app_lc_hex(k div 16); app_lc_hex(k mod 16);
+ end;
+@z
+@x module 59
+ begin print_char(str_pool[j]); incr(j);
+@y
+ begin print_char(so(str_pool[j])); incr(j);
+@z
+@x modules 59 and 60
+else if s<128 then
+@y
+else if s<256 then
+@z
+@x module 60
+ begin print(str_pool[j]); incr(j);
+@y
+ begin print(so(str_pool[j])); incr(j);
+@z
+@x module 63
+if c>=0 then if c<128 then print(c);
+@y
+if c>=0 then if c<256 then print(c);
+@z
+@x module 68
+@ In certain situations, \TeX\ prints either a standard visible ASCII
+character or its hexadecimal ASCII code.
+
+@p procedure print_ASCII(@!c:integer); {prints a character or its code}
+begin if (c>=0) and (c<=127) then print(c)
+else begin print_char("[");
+ if c<0 then print_int(c)@+else print_hex(c);
+ print_char("]");
+ end;
+end;
+@y
+@ Old versions of \TeX\ needed a procedure called |print_ASCII| whose function
+is now subsumed by |print|. We retain the old name here as a possible aid to
+future software arch\ae ologists.
+
+@d print_ASCII == print
+@z
+@x module 69
+ begin print_char(str_pool[j]); n:=n-v;
+ end;
+ if n<=0 then return; {nonpositive input produces no output}
+ k:=j+2; u:=v div (str_pool[k-1]-"0");
+ if str_pool[k-1]="2" then
+ begin k:=k+2; u:=u div (str_pool[k-1]-"0");
+ end;
+ if n+u>=v then
+ begin print_char(str_pool[k]); n:=n+u;
+ end
+ else begin j:=j+2; v:=v div (str_pool[j-1]-"0");
+@y
+ begin print_char(so(str_pool[j])); n:=n-v;
+ end;
+ if n<=0 then return; {nonpositive input produces no output}
+ k:=j+2; u:=v div (so(str_pool[k-1])-"0");
+ if str_pool[k-1]=si("2") then
+ begin k:=k+2; u:=u div (so(str_pool[k-1])-"0");
+ end;
+ if n+u>=v then
+ begin print_char(so(str_pool[k])); n:=n+u;
+ end
+ else begin j:=j+2; v:=v div (so(str_pool[j-1])-"0");
+@z
+@x module 70
+ begin print_char(str_pool[j]); incr(j);
+@y
+ begin print_char(so(str_pool[j])); incr(j);
+@z
+@x module 222
+@d single_base=active_base+128 {equivalents of one-letter control sequences}
+@d null_cs=single_base+128 {equivalent of \.{\\csname\\endcsname}}
+@y
+@d single_base=active_base+256 {equivalents of one-character control sequences}
+@d null_cs=single_base+256 {equivalent of \.{\\csname\\endcsname}}
+@z
+@x module 230
+ {table of 128 command codes (the ``catcodes'')}
+@d lc_code_base=cat_code_base+128 {table of 128 lowercase mappings}
+@d uc_code_base=lc_code_base+128 {table of 128 uppercase mappings}
+@d sf_code_base=uc_code_base+128 {table of 128 spacefactor mappings}
+@d math_code_base=sf_code_base+128 {table of 128 math mode mappings}
+@d int_base=math_code_base+128 {beginning of region 5}
+@y
+ {table of 256 command codes (the ``catcodes'')}
+@d lc_code_base=cat_code_base+256 {table of 256 lowercase mappings}
+@d uc_code_base=lc_code_base+256 {table of 256 uppercase mappings}
+@d sf_code_base=uc_code_base+256 {table of 256 spacefactor mappings}
+@d math_code_base=sf_code_base+256 {table of 256 math mode mappings}
+@d int_base=math_code_base+256 {beginning of region 5}
+@z
+@x module 232
+for k:=0 to 127 do
+@y
+for k:=0 to 255 do
+@z
+@x module 236
+@d del_code_base=count_base+256 {128 delimiter code mappings}
+@d dimen_base=del_code_base+128 {beginning of region 6}
+@y
+@d del_code_base=count_base+256 {256 delimiter code mappings}
+@d dimen_base=del_code_base+256 {beginning of region 6}
+@z
+@x module 240
+for k:=0 to 127 do del_code(k):=-1;
+@y
+for k:=0 to 255 do del_code(k):=-1;
+@z
+@x module 264
+begin if s<128 then cur_val:=s+single_base
+@y
+begin if s<256 then cur_val:=s+single_base
+@z
+@x ibid
+ for j:=0 to l-1 do buffer[j]:=str_pool[k+j];
+@y
+ for j:=0 to l-1 do buffer[j]:=so(str_pool[k+j]);
+@z
+@x module 289
+@d cs_token_flag==@'10000 {amount added to the |eqtb| location in a
+ token that stands for a control sequence; is a multiple of~256}
+@y
+@d cs_token_flag==@'7777 {amount added to the |eqtb| location in a
+ token that stands for a control sequence; is a multiple of~256, less~1}
+@z
+@x module 293
+ if (info(p)<0)or(c>127) then print_esc("BAD.")
+@y
+ if info(p)<0 then print_esc("BAD.")
+@z
+@x module 341
+@!cat:0..15; {|cat_code(cur_chr)|, usually}
+@y
+@!cat:0..15; {|cat_code(cur_chr)|, usually}
+@!c,@!cc:ASCII_code; {constituents of a possible expanded code}
+@!d:2..3; {number of excess characters in an expanded code}
+@z
+@x module 352
+@<If this |sup_mark| starts a control character...@>=
+begin if (cur_chr=buffer[loc])and(loc<limit) then
+ begin if buffer[loc+1]<@'100 then cur_chr:=buffer[loc+1]+@'100
+ else cur_chr:=buffer[loc+1]-@'100;
+ loc:=loc+2; goto reswitch;
+ end;
+@y
+@ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
+
+@d is_hex(#)==(((#>="0")and(#<="9"))or((#>="a")and(#<="f")))
+@d hex_to_cur_chr==
+ if c<="9" then cur_chr:=c-"0" @+else cur_chr:=c-"a"+10;
+ if cc<="9" then cur_chr:=16*cur_chr+cc-"0"
+ else cur_chr:=16*cur_chr+cc-"a"+10
+
+@<If this |sup_mark| starts an expanded character...@>=
+begin if cur_chr=buffer[loc] then if loc<limit then
+ begin c:=buffer[loc+1]; @+if c<@'200 then {yes we have an expanded char}
+ begin loc:=loc+2;
+ if is_hex(c) then if loc<=limit then
+ begin cc:=buffer[loc]; @+if is_hex(cc) then
+ begin incr(loc); hex_to_cur_chr; goto reswitch;
+ end;
+ end;
+ if c<@'100 then cur_chr:=c+@'100 @+else cur_chr:=c-@'100;
+ goto reswitch;
+ end;
+ end;
+@z
+@x module 355
+ begin cur_chr:=buffer[k+1];
+ if cur_chr<@'100 then buffer[k-1]:=cur_chr+@'100
+ else buffer[k-1]:=cur_chr-@'100;
+ limit:=limit-2; first:=first-2;
+ while k<=limit do
+ begin buffer[k]:=buffer[k+2]; incr(k);
+ end;
+ goto start_cs;
+ end;
+@y
+begin c:=buffer[k+1]; @+if c<@'200 then {yes, one is indeed present}
+ begin d:=2;
+ if is_hex(c) then @+if k+2<=limit then
+ begin cc:=buffer[k+2]; @+if is_hex(cc) then incr(d);
+ end;
+ if d>2 then
+ begin hex_to_cur_chr; buffer[k-1]:=cur_chr;
+ end
+ else if c<@'100 then buffer[k-1]:=c+@'100
+ else buffer[k-1]:=c-@'100;
+ limit:=limit-d; first:=first-d;
+ while k<=limit do
+ begin buffer[k]:=buffer[k+d]; incr(k);
+ end;
+ goto start_cs;
+ end;
+ end;
+@z
+@x module 360
+There is one more branch.
+@y
+There is one more branch.
+
+@d end_line_char_inactive == (end_line_char<0)or(end_line_char>255)
+@z
+@x ibid
+ if (end_line_char<0)or(end_line_char>127) then decr(limit)
+@y
+ if end_line_char_inactive then decr(limit)
+@z
+@x modules 362 and 483 and 538 and 1337
+if (end_line_char<0)or(end_line_char>127) then decr(limit)
+@y
+if end_line_char_inactive then decr(limit)
+@z
+@x module 391
+if (info(r)>match_token+127)or(info(r)<match_token) then s:=null
+@y
+if (info(r)>match_token+255)or(info(r)<match_token) then s:=null
+@z
+@x module 407
+ ((cur_chr=str_pool[k])or(cur_chr=str_pool[k]-"a"+"A")) then
+@y
+ ((cur_chr=so(str_pool[k]))or(cur_chr=so(str_pool[k])-"a"+"A")) then
+@z
+@x module 414
+begin scan_seven_bit_int;
+@y
+begin scan_char_num;
+@z
+@x module 432
+|scan_something_internal|:
+
+@<Declare procedures that scan restricted classes of integers@>=
+procedure scan_seven_bit_int;
+begin scan_int;
+if (cur_val<0)or(cur_val>127) then
+ begin print_err("Bad character code");
+@.Bad character code@>
+ help2("The numeric code for a character must be between 0 and 127.")@/
+ ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
+ end;
+end;
+@y
+|scan_something_internal|.
+@z (and I'll interchange modules 434 and 435)
+@x module 442
+if cur_val>127 then
+@y
+if cur_val>255 then
+@z
+@x module 464
+ begin t:=str_pool[k];
+@y
+ begin t:=so(str_pool[k]);
+@z
+@x module 506 (correction needed twice)
+if (cur_cmd>active_char)or(cur_chr>127) then
+@y
+if (cur_cmd>active_char)or(cur_chr>255) then
+@z
+@x module 519
+for j:=str_start[a] to str_start[a+1]-1 do append_to_name(str_pool[j]);
+for j:=str_start[n] to str_start[n+1]-1 do append_to_name(str_pool[j]);
+for j:=str_start[e] to str_start[e+1]-1 do append_to_name(str_pool[j]);
+@y
+for j:=str_start[a] to str_start[a+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[n] to str_start[n+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[e] to str_start[e+1]-1 do append_to_name(so(str_pool[j]));
+@z
+@x module 526
+if (cur_cmd>active_char)or(cur_chr>127) then
+@y
+if (cur_cmd>active_char)or(cur_chr>255) then
+@z
+@x module 603 (change to be made twice)
+ dvi_out(str_pool[k]);
+@y
+ dvi_out(so(str_pool[k]));
+@z
+@x module 617
+ for s:=str_start[str_ptr] to pool_ptr-1 do dvi_out(str_pool[s]);
+@y
+ for s:=str_start[str_ptr] to pool_ptr-1 do dvi_out(so(str_pool[s]));
+@z
+@x module 766
+ begin case str_pool[r_type*8+t+magic_offset] of
+@y
+ begin case so(str_pool[r_type*8+t+magic_offset]) of
+@z
+@x module 780
+@d span_code=128 {distinct from any character}
+@d cr_code=129 {distinct from |span_code| and from any character}
+@y
+@d span_code=256 {distinct from any character}
+@d cr_code=257 {distinct from |span_code| and from any character}
+@z
+@x module 815
+label done,done1,done2,done3,done4;
+@y
+label done,done1,done2,done3,done4,continue;
+@z
+@x module 896
+ else if (type(s)=kern_node)and(subtype(s)=normal) then c:=128
+ else if type(s)=whatsit_node then c:=128
+ else goto done1;
+ if c<128 then if lc_code(c)<>0 then
+ if (lc_code(c)=c)or(uc_hyph>0) then goto done2
+ else goto done1;
+ s:=link(s);
+@y
+ else if (type(s)=kern_node)and(subtype(s)=normal) then goto continue
+ else if type(s)=whatsit_node then goto continue
+ else goto done1;
+ if lc_code(c)<>0 then
+ if (lc_code(c)=c)or(uc_hyph>0) then goto done2
+ else goto done1;
+continue: s:=link(s);
+@z
+@x module 897
+ if c>=128 then goto done3;
+ if (lc_code(c)=0)or(hn=63) then goto done3;
+ hb:=s; incr(hn); hu[hn]:=c; hc[hn]:=lc_code(c)-1;
+@y
+ if lc_code(c)=0 then goto done3;
+ if hn=63 then goto done3;
+ hb:=s; incr(hn); hu[hn]:=c; hc[hn]:=lc_code(c);
+@z
+@x module 898
+if c>=128 then goto done3;
+if (lc_code(c)=0)or(j=63) then goto done3;
+incr(j); hu[j]:=c; hc[j]:=lc_code(c)-1;@/
+@y
+if lc_code(c)=0 then goto done3;
+if j=63 then goto done3;
+incr(j); hu[j]:=c; hc[j]:=lc_code(c);@/
+@z
+@x module 923
+hc[0]:=127; hc[hn+1]:=127; hc[hn+2]:=128; {insert delimiters}
+@y
+hc[0]:=0; hc[hn+1]:=0; hc[hn+2]:=256; {insert delimiters}
+@z
+@x ibid
+ while hc[l]=trie_char(z) do
+@y
+ while hc[l]=qo(trie_char(z)) do
+@z
+@x module 931
+ repeat if str_pool[u]<hc[j] then goto not_found;
+ if str_pool[u]>hc[j] then goto done;
+@y
+ repeat if so(str_pool[u])<hc[j] then goto not_found;
+ if so(str_pool[u])>hc[j] then goto done;
+@z
+@x module 937
+else begin if (cur_chr>127)or(lc_code(cur_chr)=0) then
+@y
+else begin if lc_code(cur_chr)=0 then
+@z
+@x ibid
+ begin incr(n); hc[n]:=lc_code(cur_chr)-1;
+@y
+ begin incr(n); hc[n]:=lc_code(cur_chr);
+@z
+@x module 945
+@!init @!trie_c:packed array[trie_pointer] of ASCII_code; {characters to match}
+@y
+@!init @!trie_c:packed array[trie_pointer] of packed_ASCII_code;
+ {characters to match}
+@z
+% In modules 951--954, change "127" to "255" and "128" to "256"
+@x module 952
+trie_link(0):=0; trie_char(0):=0; trie_op(0):=min_quarterword;
+@y
+trie_link(0):=0; trie_char(0):=min_quarterword; trie_op(0):=min_quarterword;
+@z
+@x module 953
+begin c:=trie_c[p];
+@y
+begin c:=so(trie_c[p]);
+@z
+@x module 955
+ begin if trie_link(h+trie_c[q])=0 then goto not_found;
+@y
+ begin if trie_link(h+so(trie_c[q]))=0 then goto not_found;
+@z
+@x module 956
+repeat z:=h+trie_c[q]; trie_back(trie_link(z)):=trie_back(z);
+@y
+repeat z:=h+so(trie_c[q]); trie_back(trie_link(z)):=trie_back(z);
+@z
+@x module 959
+ begin q:=trie_l[p]; c:=trie_c[p];
+ trie_link(z+c):=trie_ref[q]; trie_char(z+c):=c; trie_op(z+c):=trie_o[p];
+@y
+ begin q:=trie_l[p]; c:=so(trie_c[p]);
+ trie_link(z+c):=trie_ref[q]; trie_char(z+c):=qi(c); trie_op(z+c):=trie_o[p];
+@z
+@x module 962
+ begin if cur_chr="." then cur_chr:=128 {edge-of-word delimiter}
+ else begin cur_chr:=lc_code(cur_chr);
+ if cur_chr=0 then
+ begin print_err("Nonletter");
+@.Nonletter@>
+ help1("(See Appendix H.)"); error; cur_chr:=128;
+ end;
+ end;
+ if k<63 then
+ begin incr(k); hc[k]:=cur_chr-1; hyf[k]:=0; digit_sensed:=false;
+ end;
+ end
+else begin hyf[k]:=cur_chr-"0";
+ if k<63 then digit_sensed:=true;
+ end
+@y
+ begin if cur_chr="." then cur_chr:=0 {edge-of-word delimiter}
+ else begin cur_chr:=lc_code(cur_chr);
+ if cur_chr=0 then
+ begin print_err("Nonletter");
+@.Nonletter@>
+ help1("(See Appendix H.)"); error;
+ end;
+ end;
+ if k<63 then
+ begin incr(k); hc[k]:=cur_chr; hyf[k]:=0; digit_sensed:=false;
+ end;
+ end
+ else if k<63 then
+ begin hyf[k]:=cur_chr-"0"; digit_sensed:=true;
+ end;
+ end
+@z
+@x module 963
+ while (p>0)and(c>trie_c[p]) do
+@y
+ while (p>0)and(c>so(trie_c[p])) do
+@z
+@x ibid
+ if (p=0)or(c<trie_c[p]) then
+@y
+ if (p=0)or(c<so(trie_c[p])) then
+@z
+@x module 964
+trie_c[p]:=c; trie_o[p]:=min_quarterword;
+@y
+trie_c[p]:=si(c); trie_o[p]:=min_quarterword;
+@z
+@x module 965
+if hc[1]=127 then hyf[0]:=0;
+if hc[k]=127 then hyf[k]:=0;
+@y
+if hc[1]=0 then hyf[0]:=0;
+if hc[k]=0 then hyf[k]:=0;
+@z
+@x module 966
+h.rh:=0; h.b0:=min_quarterword; h.b1:=0; {|trie_link:=0|,
+ |trie_op:=min_quarterword|, |trie_char:=0|}
+@y
+h.rh:=0; h.b0:=min_quarterword; h.b1:=min_quarterword; {|trie_link:=0|,
+ |trie_op:=min_quarterword|, |trie_char:=qi(0)|}
+@z
+@x module 1034
+if c<128 then
+@y
+@z
+@x ibid
+else space_factor:=1000
+@y
+@z
+@x module 1151
+letter,other_char,char_given: if cur_chr>=128 then c:=cur_chr
+ else begin c:=ho(math_code(cur_chr));
+@y
+letter,other_char,char_given: begin c:=ho(math_code(cur_chr));
+@z
+@x module 1154
+mmode+letter,mmode+other_char,mmode+char_given: if cur_chr<128 then
+ set_math_char(ho(math_code(cur_chr)))
+ else set_math_char(cur_chr);
+mmode+char_num: begin scan_char_num; cur_chr:=cur_val;
+ if cur_chr<128 then set_math_char(ho(math_code(cur_chr)))
+ else set_math_char(cur_chr);
+@y
+mmode+letter,mmode+other_char,mmode+char_given:
+ set_math_char(ho(math_code(cur_chr)));
+mmode+char_num: begin scan_char_num; cur_chr:=cur_val;
+ set_math_char(ho(math_code(cur_chr)));
+@z
+@x module 1232
+ p:=cur_chr; scan_seven_bit_int; p:=p+cur_val; scan_optional_equals;
+@y
+ p:=cur_chr; scan_char_num; p:=p+cur_val; scan_optional_equals;
+@z
+@x module 1233
+else n:=127
+@y
+else n:=255
+@z
+@x module 1289
+ begin if t>=cs_token_flag then t:=t-active_base;
+ c:=t mod 256;
+ if c<128 then if equiv(b+c)<>0 then t:=256*(t div 256)+equiv(b+c);
+ if t>=cs_token_flag then info(p):=t+active_base
+ else info(p):=t;
+@y
+ begin c:=t mod 256;
+ if equiv(b+c)<>0 then info(p):=t-c+equiv(b+c);
+@z
+@x module 1309
+ w.b0:=str_pool[k]; w.b1:=str_pool[k+1];
+ w.b2:=str_pool[k+2]; w.b3:=str_pool[k+3];
+@y [often qi(so(x))=x, but not e.g. when "quarterwords" are two bytes]
+ w.b0:=qi(so(str_pool[k])); w.b1:=qi(so(str_pool[k+1]));
+ w.b2:=qi(so(str_pool[k+2])); w.b3:=qi(so(str_pool[k+3]));
+@z
+@x module 1310
+ str_pool[k]:=w.b0; str_pool[k+1]:=w.b1;
+ str_pool[k+2]:=w.b2; str_pool[k+3]:=w.b3
+@y
+ str_pool[k]:=si(qo(w.b0)); str_pool[k+1]:=si(qo(w.b1));
+ str_pool[k+2]:=si(qo(w.b2)); str_pool[k+3]:=si(qo(w.b3))
+@z
+@x module 1368
+for k:=str_start[str_ptr] to pool_ptr-1 do dvi_out(str_pool[k]);
+@y
+for k:=str_start[str_ptr] to pool_ptr-1 do dvi_out(so(str_pool[k]));
+@z
+
+360. Major change to allow multiple hyphenation tables.
+I've combined this with the code for a minor change:
+361. New parameters \lefthyphenmin and \righthyphenmin.
+@x module 11 (affects most change files!)
+@!trie_size=8000; {space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX}
+@y
+@!trie_size=8000; {space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX}
+@!trie_op_size=500; {space for ``opcodes'' in the hyphenation patterns}
+@z
+@x module 212
+known as |space_factor|; it holds the current space factor used in spacing
+calculations. In math mode, |aux| is also known as |incompleat_noad|; if
+@y
+known as |space_factor| and |clang|; it holds the current space factor used in
+spacing calculations, and the current language used for hyphenation.
+(The value of |clang| is undefined in restricted horizontal mode.)
+In math mode, |aux| is also known as |incompleat_noad|; if
+@z
+@x ibid
+ @!pg_field,@!aux_field,@!ml_field: integer;
+@y
+ @!pg_field,@!ml_field: integer;
+ @!aux_field: memory_word;
+@z
+@x module 213
+@d prev_depth==aux {the name of |aux| in vertical mode}
+@d space_factor==aux {the name of |aux| in horizontal mode}
+@d incompleat_noad==aux {the name of |aux| in math mode}
+@y
+@d prev_depth==aux.sc {the name of |aux| in vertical mode}
+@d space_factor==aux.hh.lh {part of |aux| in horizontal mode}
+@d clang==aux.hh.rh {the other part of |aux| in horizontal mode}
+@d incompleat_noad==aux.int {the name of |aux| in math mode}
+@z
+@x module 218
+@!a:integer; {auxiliary}
+@y
+@!a:memory_word; {auxiliary}
+@z
+@x module 219
+ if a<=ignore_depth then print("ignored")
+ else print_scaled(a);
+ if nest[p].pg_field<>0 then
+ begin print(", prevgraf ");
+ print_int(nest[p].pg_field); print(" line");
+ if nest[p].pg_field<>1 then print_char("s");
+ end;
+ end;
+1: begin print_nl("spacefactor "); print_int(a);
+ end;
+2: if a<>null then
+ begin print("this will be denominator of:"); show_box(a);
+@y
+ if a.sc<=ignore_depth then print("ignored")
+ else print_scaled(a.sc);
+ if nest[p].pg_field<>0 then
+ begin print(", prevgraf ");
+ print_int(nest[p].pg_field); print(" line");
+ if nest[p].pg_field<>1 then print_char("s");
+ end;
+ end;
+1: begin print_nl("spacefactor "); print_int(a.hh.lh);
+ if m>0 then if a.hh.rh>0 then
+ begin print(", current language "); print_int(a.hh.rh);
+ end;
+ end;
+2: if a.int<>null then
+ begin print("this will be denominator of:"); show_box(a.int);
+@z
+@x modules 236--238
+@d int_pars=50 {total number of integer parameters}
+@y
+@d language_code=50 {current hyphenation table}
+@d left_hyphen_min_code=51 {minimum left hyphenation fragment size}
+@d right_hyphen_min_code=52 {minimum right hyphenation fragment size}
+@d int_pars=53 {total number of integer parameters}
+@z and define/print these new parameters appropriately (mimicking new_line_char)
+@x module 418
+else begin cur_val:=aux;
+ if m=vmode then cur_val_level:=dimen_val@+else cur_val_level:=int_val;
+ end
+@y
+else if m=vmode then
+ begin cur_val:=prev_depth; cur_val_level:=dimen_val;
+ end
+else begin cur_val:=space_factor; cur_val_level:=int_val;
+ end
+@z
+@x module 775
+ begin mode:=-vmode; prev_depth:=nest[nest_ptr-2].aux_field;
+@y
+ begin mode:=-vmode; prev_depth:=nest[nest_ptr-2].aux_field.sc;
+@z
+@x module 786
+begin push_nest; mode:=(-hmode-vmode)-mode; aux:=0;
+@y
+begin push_nest; mode:=(-hmode-vmode)-mode;
+if mode=-hmode then space_factor:=0 @+else prev_depth:=0;
+@z
+@x module 863
+loop@+ begin @<Create an active breakpoint representing the beginning of
+ the paragraph@>;
+@y
+loop@+ begin if threshold>inf_bad then threshold:=inf_bad;
+ if second_pass then @<Initialize for hyphenating a paragraph@>;
+ @<Create an active breakpoint representing the beginning of the paragraph@>;
+@z
+@x module 891
+passes on only about 5 per cent of the paragraphs.
+@y
+passes on only about 5 per cent of the paragraphs.
+
+@<Initialize for hyphenating...@>=
+begin @!init if trie_not_ready then init_trie; @+tini@;@/
+l_hyf:=left_hyphen_min-1;@+if l_hyf<0 then l_hyf:=0;
+r_hyf:=right_hyphen_min-1;@+if r_hyf<0 then r_hyf:=0;
+min_hyf:=l_hyf+r_hyf+2; cur_lang:=0;
+end
+@z
+@x module 892
+@!hyf_char:integer; {hyphen character of the relevant font}
+@y
+@!hyf_char:integer; {hyphen character of the relevant font}
+@!cur_lang:ASCII_code; {current hyphenation table of interest}
+@!l_hyf,@!r_hyf,@!min_hyf:integer; {limits on fragment sizes}
+@z
+@x module 894
+begin s:=link(cur_p);
+@y
+begin if min_hyf>63 then goto done1;
+s:=link(cur_p);
+@z
+@x module 896
+ else if type(s)=whatsit_node then goto continue
+@y
+ else if type(s)=whatsit_node then
+ begin @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>;
+ goto continue;
+ end
+@z
+@x module 899 (title of this module also changes appropriately)
+if hn<5 then goto done1;
+@y
+if hn<min_hyf then goto done1;
+@z
+@x module 902
+for j:=2 to hn-3 do if odd(hyf[j]) then goto found1;
+@y
+for j:=l_hyf+1 to hn-r_hyf-1 do if odd(hyf[j]) then goto found1;
+@z
+@x module 920
+$p_1\ldots p_k$ by setting |@t$z_1$@>:=@t$p_1$@>| and then, for |1<i<=k|,
+@y
+$p_1\ldots p_k$ by letting $z_0$ be one greater than the relevant language index
+and then, for |1<=i<=k|,
+@z
+@x ibid
+the letters in |hc[(l-k+1)..l@,]|, we perform all of the required operations
+for this pattern by carrying out the following little program: Set
+|v:=trie_op(@t$z_k$@>)|. Then set |hyf[l-hyf_distance[v]]:=@tmax@>(
+hyf[l-hyf_distance[v]], hyf_num[v])|, and |v:=hyf_next[v]|; repeat, if
+necessary, until |v=min_quarterword|.
+@y
+the letters in |hc[(l-k+1)..l@,]| of language |t|,
+we perform all of the required operations
+for this pattern by carrying out the following little program: Set
+|v:=trie_op(@t$z_k$@>)|. Then set |v:=v+op_start[t]|,
+|hyf[l-hyf_distance[v]]:=@tmax@>(hyf[l-hyf_distance[v]], hyf_num[v])|,
+and |v:=hyf_next[v]|; repeat, if necessary, until |v=min_quarterword|.
+@z
+@x module 921
+@!hyf_distance:array[quarterword] of small_number; {position |k-j| of $n_j$}
+@!hyf_num:array[quarterword] of small_number; {value of $n_j$}
+@!hyf_next:array[quarterword] of quarterword; {continuation of this |trie_op|}
+@y
+@!hyf_distance:array[1..trie_op_size] of small_number; {position |k-j| of $n_j$}
+@!hyf_num:array[1..trie_op_size] of small_number; {value of $n_j$}
+@!hyf_next:array[1..trie_op_size] of quarterword; {continuation code}
+@!op_start:array[ASCII_code] of 0..trie_op_size; {offset for current language}
+@z
+@x module 922
+@!v:quarterword; {an index into |hyf_distance|, etc.}
+@y
+@!v:integer; {an index into |hyf_distance|, etc.}
+@z
+@x module 923
+for j:=0 to hn-2 do
+ begin z:=hc[j]; l:=j;
+@y
+if trie_char(cur_lang+1)<>qi(cur_lang) then return; {no patterns for |cur_lang|}
+for j:=0 to hn-r_hyf do
+ begin z:=trie_link(cur_lang+1)+hc[j]; l:=j;
+@z
+@x ibid
+found: hyf[1]:=0; hyf[hn-2]:=0; hyf[hn-1]:=0; hyf[hn]:=0
+@y
+found: for j:=0 to l_hyf do hyf[j]:=0;
+for j:=0 to r_hyf do hyf[hn-j]:=0
+@z
+@x module 924
+repeat i:=l-hyf_distance[v];
+@y
+repeat v:=v+op_start[cur_lang]; i:=l-hyf_distance[v];
+@z
+@x module 930
+h:=hc[1];
+@y
+h:=hc[1]; incr(hn); hc[hn]:=cur_lang;
+@z
+@x ibid
+not_found:
+@y
+not_found: decr(hn)
+@z
+@x module 931
+ goto found;
+@y
+ decr(hn); goto found;
+@z
+@x module 934
+@p procedure new_hyph_exceptions; {enters new exceptions}
+label reswitch, exit, found, not_found, done;
+@y
+@d set_cur_lang==if language<=0 then cur_lang:=0
+ else if language>255 then cur_lang:=0
+ else cur_lang:=language
+
+@p procedure new_hyph_exceptions; {enters new exceptions}
+label reswitch, exit, found, not_found;
+@z
+@x ibid
+begin scan_left_brace; {a left brace must follow \.{\\hyphenation}}
+@y
+begin scan_left_brace; {a left brace must follow \.{\\hyphenation}}
+set_cur_lang;
+@z
+@x module 935
+ spacer,right_brace: begin if n>4 then @<Enter a hyphenation exception@>;
+@y
+ spacer,right_brace: begin if n>1 then @<Enter a hyphenation exception@>;
+@z
+@x module 938
+begin if n>1 then
+@y
+begin if n<63 then
+@z
+@x module 939
+begin str_room(n); h:=0;
+@y
+begin incr(n); hc[n]:=cur_lang; str_room(n); h:=0;
+@z
+@x ibid
+loop@+ begin if p=null then goto done;
+ if info(p)<n-2 then goto done;
+ q:=link(p); free_avail(p); p:=q; {eliminate hyphens that \TeX\ doesn't like}
+ end;
+done: @<Insert the \(p)pair |(s,p)| into the exception table@>;
+@y
+@<Insert the \(p)pair |(s,p)| into the exception table@>;
+@z
+@x module 942
+@p@!init @<Declare procedures for preprocessing hyphenation patterns@>@;
+@y
+@<Declare subprocedures for |line_break|@>=
+@!init @<Declare procedures for preprocessing hyphenation patterns@>@;
+@z
+@x module 943
+@d quarterword_diff=max_quarterword-min_quarterword
+@d trie_op_hash_size=quarterword_diff+quarterword_diff {double}
+
+@<Glob...@>=
+@!init@! trie_op_hash:array[0..trie_op_hash_size] of quarterword;
+ {trie op codes for triples}
+tini@;@/
+@t\hskip1em@>@!trie_op_ptr:quarterword; {highest |trie_op| assigned}
+@y
+@<Glob...@>=
+@!init@! trie_op_hash:array[-trie_op_size..trie_op_size] of 0..trie_op_size;
+ {trie op codes for quadruples}
+@!trie_used:array[ASCII_code] of quarterword;
+ {largest opcode used so far for this language}
+@!trie_op_lang:array[1..trie_op_size] of ASCII_code;
+ {language part of a hashed quadruple}
+@!trie_op_val:array[1..trie_op_size] of quarterword;
+ {opcode corresponding to a hashed quadruple}
+@!trie_op_ptr:0..trie_op_size; {number of stored ops so far}
+tini
+@z
+@x module 944 should be entirely replaced
+@y by the following code:
+@ It's tempting to remove the |overflow| stops in the following procedure;
+|new_trie_op| could return |min_quarterword| (thereby simply ignoring
+part of a hyphenation pattern) instead of aborting the job. However, that would
+lead to different hyphenation results on different installations of \TeX\
+using the same patterns. The |overflow| stops are necessary for portability
+of patterns.
+
+@<Declare procedures for preprocessing hyph...@>=
+function new_trie_op(@!d,@!n:small_number;@!v:quarterword):quarterword;
+label exit;
+var h:-trie_op_size..trie_op_size; {trial hash location}
+@!u:quarterword; {trial op code}
+@!l:0..trie_op_size; {pointer to stored data}
+begin h:=abs(n+313*d+361*v+1009*cur_lang) mod (trie_op_size+trie_op_size)
+ - trie_op_size;
+loop@+ begin l:=trie_op_hash[h];
+ if l=0 then {empty position found for a new op}
+ begin if trie_op_ptr=trie_op_size then
+ overflow("pattern memory ops",trie_op_size);
+ u:=trie_used[cur_lang];
+ if u=max_quarterword then
+ overflow("pattern memory ops per language",
+ max_quarterword-min_quarterword);
+ incr(trie_op_ptr); incr(u); trie_used[cur_lang]:=u;
+ hyf_distance[trie_op_ptr]:=d;
+ hyf_num[trie_op_ptr]:=n; hyf_next[trie_op_ptr]:=v;
+ trie_op_lang[trie_op_ptr]:=cur_lang; trie_op_hash[h]:=trie_op_ptr;
+ trie_op_val[trie_op_ptr]:=u; new_trie_op:=u; return;
+ end;
+ if (hyf_distance[l]=d)and(hyf_num[l]=n)and(hyf_next[l]=v)
+ and(trie_op_lang[l]=cur_lang) then
+ begin new_trie_op:=trie_op_val[l]; return;
+ end;
+ if h>-trie_op_size then decr(h)@+else h:=trie_op_size;
+ end;
+exit:end;
+@z
+@x module 945: move the code from this module into 946, and introduce new code:
+@y
+@ After |new_trie_op| has compressed the necessary opcode information,
+plenty of information is available to unscramble the data into the
+final form needed by our hyphenation algorithm.
+
+@<Sort the hyphenation op tables into proper order@>=
+op_start[0]:=-min_quarterword;
+for j:=1 to 255 do op_start[j]:=op_start[j-1]+qo(trie_used[j-1]);
+for j:=1 to trie_op_ptr do
+ trie_op_hash[j]:=op_start[trie_op_lang[j]]+trie_op_val[j]; {destination}
+for j:=1 to trie_op_ptr do while trie_op_hash[j]>j do
+ begin k:=trie_op_hash[j];@/
+ t:=hyf_distance[k]; hyf_distance[k]:=hyf_distance[j]; hyf_distance[j]:=t;@/
+ t:=hyf_num[k]; hyf_num[k]:=hyf_num[j]; hyf_num[j]:=t;@/
+ t:=hyf_next[k]; hyf_next[k]:=hyf_next[j]; hyf_next[j]:=t;@/
+ trie_op_hash[j]:=trie_op_hash[k]; trie_op_hash[k]:=k;
+ end
+@z
+@x module 949: Move this code to just after the new module 945!
+mentioned so far, let's write a procedure that does the initialization.
+
+@<Declare procedures for preprocessing hyph...@>=
+procedure init_pattern_memory; {gets ready to build a linked trie}
+var h:0..trie_op_hash_size; {an index into |trie_op_hash|}
+@!p:trie_pointer; {an index into |trie_hash|}
+begin for h:=0 to trie_op_hash_size do trie_op_hash[h]:=min_quarterword;
+trie_op_ptr:=min_quarterword; trie_root:=0; trie_c[0]:=0; trie_ptr:=0;
+for p:=0 to trie_size do trie_hash[p]:=0;
+end;
+@y
+mentioned so far, let's write down the code that gets them started.
+
+@<Initialize table entries...@>=
+for k:=-trie_op_size to trie_op_size do trie_op_hash[k]:=0;
+for k:=0 to 255 do trie_used[k]:=min_quarterword;
+trie_op_ptr:=0;
+@z
+@x module 950
+@t\hskip1em@>@!trie_min:trie_pointer;
+ {all locations |<=trie_min| are vacant in |trie|}
+tini@;@/
+@t\hskip1em@>@!trie_max:trie_pointer; {largest location used in |trie|}
+@y
+@t\hskip10pt@>@!trie_min:array[ASCII_code] of trie_pointer;
+ {the first possible slot for each character}
+@t\hskip10pt@>@!trie_max:trie_pointer; {largest location used in |trie|}
+@t\hskip10pt@>@!trie_not_ready:boolean; {is the trie still in linked form?}
+tini
+@z
+@x modules 951 and 952
+@ Here is how these data structures are initialized.
+
+@<Declare procedures for preprocessing hyph...@>=
+procedure init_trie_memory; {gets ready to pack into |trie|}
+var p:trie_pointer; {index into |trie_ref|, |trie|, |trie_taken|}
+begin for p:=0 to trie_ptr do trie_ref[p]:=0;
+trie_max:=256; trie_min:=256; trie_link(0):=1; trie_taken[0]:=false;
+trie_link(trie_size):=0; trie_back(0):=trie_size; {wrap around}
+for p:=1 to 256 do
+ begin trie_back(p):=p-1; trie_link(p):=p+1; trie_taken[p]:=false;
+ end;
+end;
+
+@ Each time \.{\\patterns} appears, it overrides any patterns that were
+entered earlier, so the arrays are not initialized until \TeX\ sees
+\.{\\patterns}. However, some of the global variables must be
+initialized when \.{INITEX} is loaded, in case the user never mentions
+any \.{\\patterns}.
+
+@<Initialize table entries...@>=
+trie_op_ptr:=min_quarterword;@/
+trie_link(0):=0; trie_char(0):=min_quarterword; trie_op(0):=min_quarterword;
+for k:=1 to 255 do trie[k]:=trie[0];
+trie_max:=255;
+@y
+@ Each time \.{\\patterns} appears, it contributes further patterns to
+the future trie, which will be built only when hyphenation is attempted or
+when a format file is dumped. The boolean variable |trie_not_ready|
+will change to |false| when the trie is compressed; this will disable
+further patterns.
+
+@<Initialize table entries...@>=
+trie_not_ready:=true; trie_root:=0; trie_c[0]:=si(0); trie_ptr:=0;
+
+@ Here is how the trie-compression data structures are initialized.
+If storage is tight, it would be possible to overlap |trie_op_hash|,
+|trie_op_lang|, and |trie_op_val| with |trie|, |trie_hash|, and |trie_taken|,
+because we finish with the former just before we need the latter.
+
+@<Get ready to compress the trie@>=
+@<Sort the hyphenation...@>;
+for p:=0 to trie_size do trie_hash[p]:=0;
+trie_root:=compress_trie(trie_root); {identify equivalent subtries}
+for p:=0 to trie_ptr do trie_ref[p]:=0;
+for p:=0 to 255 do trie_min[p]:=p+1;
+trie_link(0):=1; trie_max:=0
+@z
+@x module 953
+begin c:=so(trie_c[p]);
+if c<trie_min then trie_min:=c;
+if trie_min=0 then z:=trie_link(trie_size)
+else z:=trie_link(trie_min-1); {get the first conceivably good hole}
+loop@+ begin if z<c then goto not_found;
+ h:=z-c;@/
+@y
+@!l,@!r:trie_pointer; {left and right neighbors}
+@!ll:1..256; {upper limit of |trie_min| updating}
+begin c:=so(trie_c[p]);
+z:=trie_min[c]; {get the first conceivably good hole}
+loop@+ begin h:=z-c;@/
+@z
+@x module 956
+repeat z:=h+so(trie_c[q]); trie_back(trie_link(z)):=trie_back(z);
+trie_link(trie_back(z)):=trie_link(z); trie_link(z):=0; q:=trie_r[q];
+@y
+repeat z:=h+so(trie_c[q]); l:=trie_back(z); r:=trie_link(z);
+trie_back(r):=l; trie_link(l):=r; trie_link(z):=0;
+if l<256 then
+ begin if z<256 then ll:=z @+else ll:=256;
+ repeat trie_min[l]:=r; incr(l);
+ until l=ll;
+ end;
+q:=trie_r[q];
+@z
+@x module 958 is entirely replaced
+@y by the following new code:
+@ When the whole trie has been allocated into the sequential table, we
+must go through it once again so that |trie| contains the correct
+information. Null pointers in the linked trie will be represented by the
+value~0, which properly implements an ``empty'' family.
+
+@<Move the data into |trie|@>=
+h.rh:=0; h.b0:=min_quarterword; h.b1:=min_quarterword; {|trie_link:=0|,
+ |trie_op:=min_quarterword|, |trie_char:=qi(0)|}
+if trie_root=0 then {no patterns were given}
+ begin for r:=0 to 256 do trie[r]:=h;
+ trie_max:=256;
+ end
+else begin trie_fix(trie_root); {this fixes the non-holes in |trie|}
+ r:=0; {now we will zero out all the holes}
+ repeat s:=trie_link(r); trie[r]:=h; r:=s;
+ until r>trie_max;
+ end;
+trie_char(0):=qi("?"); {make |trie_char(c)<>c| for all |c|}
+@z
+@x module 960
+@!r,@!s:trie_pointer; {used to clean up the packed |trie|}
+@!h:two_halves; {template used to zero out |trie|'s holes}
+begin scan_left_brace; {a left brace must follow \.{\\patterns}}
+init_pattern_memory;@/
+@<Enter all of the patterns into a linked trie, until coming to a right
+ brace@>;
+trie_root:=compress_trie(trie_root); {compress the trie}
+@<Pack the trie@>;
+end;
+@y
+begin if trie_not_ready then
+ begin set_cur_lang; scan_left_brace; {a left brace must follow \.{\\patterns}}
+ @<Enter all of the patterns into a linked trie, until coming to a right
+ brace@>;
+ end
+else begin print_err("Too late for "); print_esc("patterns");
+ help1("All patterns must be given before typesetting begins.");
+ error; link(garbage):=scan_toks(false,false); flush_list(def_ref);
+ end;
+end;
+@z
+@x module 963
+q:=0;
+while l<k do
+ begin incr(l); c:=hc[l]; p:=trie_l[q]; first_child:=true;
+@y
+q:=0; hc[0]:=cur_lang;
+while l<=k do
+ begin c:=hc[l]; incr(l); p:=trie_l[q]; first_child:=true;
+@z
+@x module 966 now says that the first call ``takes'' location 1,
+@y and the Pascal code in this module is entirely replaced by the following:
+@<Declare procedures for preprocessing hyphenation patterns@>=
+procedure init_trie;
+var @!p:trie_pointer; {pointer for initialization}
+@!j,@!k,@!t:integer; {all-purpose registers for initialization}
+@!r,@!s:trie_pointer; {used to clean up the packed |trie|}
+@!h:two_halves; {template used to zero out |trie|'s holes}
+begin @<Get ready to compress the trie@>;
+if trie_root<>0 then
+ begin first_fit(trie_root); trie_pack(trie_root);
+ end;
+@<Move the data into |trie|@>;
+trie_not_ready:=false;
+end;
+@z
+@x module 1033 gets new code before main_loop_1
+@y
+if mode>0 then if language<>clang then fix_language;
+@z
+@x modules 1091 and 1200
+push_nest; mode:=hmode; space_factor:=1000;
+@y
+push_nest; mode:=hmode; space_factor:=1000; clang:=0;
+@z
+@x module 1324
+for k:=0 to trie_max do dump_hh(trie[k]);
+dump_int(trie_op_ptr);
+for k:=min_quarterword+1 to trie_op_ptr do
+ begin dump_int(hyf_distance[k]);
+ dump_int(hyf_num[k]);
+ dump_int(hyf_next[k]);
+ end;
+print_ln; print_int(hyph_count); print(" hyphenation exception");
+if hyph_count<>1 then print_char("s");
+print_nl("Hyphenation trie of length "); print_int(trie_max);
+@.Hyphenation trie...@>
+print(" has "); print_int(qo(trie_op_ptr)); print(" op");
+if trie_op_ptr<>min_quarterword+1 then print_char("s")
+@y
+print_ln; print_int(hyph_count); print(" hyphenation exception");
+if hyph_count<>1 then print_char("s");
+if trie_not_ready then init_trie;
+dump_int(trie_max);
+for k:=0 to trie_max do dump_hh(trie[k]);
+dump_int(trie_op_ptr);
+for k:=1 to trie_op_ptr do
+ begin dump_int(hyf_distance[k]);
+ dump_int(hyf_num[k]);
+ dump_int(hyf_next[k]);
+ end;
+print_nl("Hyphenation trie of length "); print_int(trie_max);
+@.Hyphenation trie...@>
+print(" has "); print_int(trie_op_ptr); print(" op");
+if trie_op_ptr<>1 then print_char("s");
+print(" out of "); print_int(trie_op_size);
+for k:=255 downto 0 do if trie_used[k]>min_quarterword then
+ begin print_nl(" "); print_int(qo(trie_used[k]));
+ print(" for language "); print_int(k);
+ dump_int(k); dump_int(qo(trie_used[k]));
+ end
+@z
+@x module 1325
+undump_size(0)(trie_size)('trie size')(trie_max);
+for k:=0 to trie_max do undump_hh(trie[k]);
+undump(min_quarterword)(max_quarterword)(trie_op_ptr);
+for k:=min_quarterword+1 to trie_op_ptr do
+ begin undump(0)(63)(hyf_distance[k]); {a |small_number|}
+ undump(0)(63)(hyf_num[k]);
+ undump(min_quarterword)(max_quarterword)(hyf_next[k]);
+ end
+@y
+undump_size(0)(trie_size)('trie size')(j); {|trie_max|}
+for k:=0 to j do undump_hh(trie[k]);
+undump_size(0)(trie_op_size)('trie op size')(j); {|trie_op_ptr|}
+for k:=1 to j do
+ begin undump(0)(63)(hyf_distance[k]); {a |small_number|}
+ undump(0)(63)(hyf_num[k]);
+ undump(min_quarterword)(max_quarterword)(hyf_next[k]);
+ end;
+k:=256;
+while j>0 do
+ begin undump(0)(k-1)(k); undump(1)(j)(x); j:=j-x; op_start[k]:=qo(j);
+ end;
+@!init trie_not_ready:=false @+tini
+@z
+@x module 1341 gets two new definitions
+@y
+@d language_node=4 {|subtype| in whatsits that change the current language}
+@d stored_language(#)==mem[#+1].int {language number, in the range |0..255|}
+@z
+@x module 1344 gets a new definition and a new Pascal statement
+@y
+@d set_language_code=5 {command modifier for \.{\\setlanguage}}
+primitive("setlanguage",extension,set_language_code);@/
+@!@:set_language_}{\.{\\setlanguage} primitive@>
+@z
+@x module 1346 gets a new case
+@y
+ set_language_code:print_esc("setlanguage");
+@z
+@x module 1348 gets a new case
+@y
+set_language_code:@<Implement \.{\\setlanguage}@>;
+@z
+@x module 1356 gets a new case
+@y
+language_node:begin print_esc("setlanguage");
+ print_int(stored_language(p));
+ end;
+@z
+% in modules 1357 and 1358, change "close_node" to "close_node,language_node".
+@x module 1362 becomes two modules
+@ @<Advance \(p)past a whatsit node in the |line_break| loop@>=do_nothing
+@y
+@ @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>=
+if subtype(cur_p)=language_node then cur_lang:=stored_language(cur_p)
+
+@ @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>=
+if subtype(s)=language_node then cur_lang:=stored_language(s)
+@z and old module 1367 is moved to just before the old module 1378
+@x module 1373 gets a new case
+@y
+language_node:do_nothing;
+@z
+@x new modules before the system-dependent changes (i.e. before the old 1376)
+@y
+@ @<Implement \.{\\setlanguage}@>=
+if abs(mode)<>hmode then report_illegal_case
+else begin new_whatsit(language_node,small_node_size);
+ scan_int;
+ if cur_val<=0 then clang:=0
+ else if cur_val>255 then clang:=0
+ else clang:=cur_val;
+ stored_language(tail):=clang;
+ end
+
+@ Finally, we need a subroutine that comes into play when a character of
+a non-|clang| language is being appended to the current paragraph.
+
+@<Declare action...@>=
+procedure fix_language;
+var @!l:ASCII_code; {the new current language}
+begin if language<=0 then l:=0
+else if language>255 then l:=0
+else l:=language;
+if l<>clang then
+ begin new_whatsit(language_node,small_node_size);
+ stored_language(tail):=l; clang:=l;
+ end;
+end;
+@z
+
+362. Major extension to ligature capability.
+@x module 143
+a linked list of character nodes for those characters.
+@y
+a linked list of character nodes for all original characters that have been
+deleted. (This list might be empty if the characters that generated the
+ligature were retained in other nodes.)
+
+The |subtype| field is 0, plus 2 and/or 1 if the original source of the
+ligature included implicit left and/or right boundaries.
+@z
+@x in module 144 add the following procedure to the existing code
+@y
+function new_lig_item(@!c:quarterword):pointer;
+var p:pointer; {the new node}
+begin p:=get_node(small_node_size); character(p):=c; lig_ptr(p):=null;
+new_lig_item:=p;
+end;
+@z
+@x module 193
+font_in_short_display:=font(lig_char(p));
+short_display(lig_ptr(p)); print_char(")");
+@y
+if subtype(p)>1 then print_char("|");
+font_in_short_display:=font(lig_char(p)); short_display(lig_ptr(p));
+if odd(subtype(p)) then print_char("|");
+print_char(")");
+end
+@z
+@x modules 208 and 209
+@d radical=65 {square root and similar signs ( \.{\\radical} )}
+@y
+@d no_boundary=65 {suppress boundary ligatures ( \.{\\noboundary} )}
+@d radical=66 {square root and similar signs ( \.{\\radical} )}
+@z and so on, adding 1 to each definition until getting to max_command=100
+@x module 265 gets a new statement
+@y
+primitive("noboundary",no_boundary,0);@/
+@!@:no_boundary_}{\.{\\noboundary} primitive@>
+@z
+@x module 266 gets a new case
+@y
+no_boundary:print_esc("noboundary");
+@z
+@x module 545 is entirely replaced
+@y by the following new specifications:
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word in this array is a
+|@!lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+ step if the byte is 128 or more, otherwise the next step is obtained by
+ skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+ then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+ a kern step otherwise.\par
+\hang fourth byte: |remainder|.\par
+\yskip\noindent
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|remainder| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+If the very first instruction of the |lig_kern| array has |skip_byte=255|,
+the |next_char| byte is the so-called right boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte=255|,
+there is a special ligature/kerning program for a left boundary character,
+beginning at location |256*op_byte+remainder|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte>128|, the program actually begins in location
+|256*op_byte+remainder|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location |<=255|.
+
+Any instruction with |skip_byte>128| in the |lig_kern| array must have
+|256*op_byte+remainder<nl|. If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+command is performed.
+
+@d stop_flag=qi(128) {value indicating `\.{STOP}' in a lig/kern program}
+@d kern_flag=qi(128) {op code for a kern step}
+@d skip_byte(#)==#.b0
+@d next_char(#)==#.b1
+@d op_byte(#)==#.b2
+@d rem_byte(#)==#.b3
+@z
+@x module 549
+@<Glob...@>=
+@y
+@d non_char==qi(256) {a |halfword| code that can't match a real character}
+@d non_address==font_mem_size {a spurious |font_index|}
+
+@<Glob...@>=
+@z
+@x module 548 gets a new type definition
+@y
+@!font_index=0..font_mem_size;
+@z
+@x module 549
+ {current \.{\\skewchar} values}
+@y
+ {current \.{\\skewchar} values}
+@!bchar_label:array[internal_font_number] of font_index;
+ {start of |lig_kern| program for left boundary character,
+ |non_address| if there is none}
+@!font_bchar:array[internal_font_number] of min_quarterword..non_char;
+ {right boundary character, |non_char| if there is none}
+@!font_false_bchar:array[internal_font_number] of min_quarterword..non_char;
+ {|font_bchar| if it doesn't exist in the font, otherwise |non_char|}
+@z
+@x module 557
+@d char_kern_end(#)==rem_byte(#)].sc
+@y NOTE: Optimize kern_base_offset in your change file! It's a constant.
+@d char_kern_end(#)==256*op_byte(#)+rem_byte(#)].sc
+@d kern_base_offset==256*(kern_flag)
+@d lig_kern_restart_end(#)==256*op_byte(#)+rem_byte(#)+32768-kern_base_offset
+@d lig_kern_restart(#)==lig_kern_base[#]+lig_kern_restart_end
+@z
+@x module 560
+@!z:scaled; {the design size or the ``at'' size}
+@y
+@!bch_label:integer; {left boundary start location, or infinity}
+@!bchar:0..256; {right boundary character, or 256}
+@!z:scaled; {the design size or the ``at'' size}
+@z
+@x module 566
+kern_base[f]:=lig_kern_base[f]+nl;
+exten_base[f]:=kern_base[f]+nk;
+@y
+kern_base[f]:=lig_kern_base[f]+nl-kern_base_offset;
+exten_base[f]:=kern_base[f]+kern_base_offset+nk;
+@z
+@x module 573: The entire module is replaced
+@y by the following code.
+@ @d check_existence(#)==@t@>@;@/
+ begin check_byte_range(#);
+ qw:=char_info(f)(#); {N.B.: not |qi(#)|}
+ if not char_exists(qw) then abort;
+ end
+
+@<Read ligature/kern program@>=
+bch_label:=@'77777; bchar:=256;
+if nl>0 then
+ begin for k:=lig_kern_base[f] to kern_base[f]+kern_base_offset-1 do
+ begin store_four_quarters(font_info[k].qqqq);
+ if a>128 then
+ begin if 256*c+d>=nl then abort;
+ if a=255 then if k=lig_kern_base[f] then bchar:=b;
+ end
+ else begin if b<>bchar then check_existence(b);
+ if c<128 then check_existence(d) {check ligature}
+ else if 256*(c-128)+d>=nk then abort; {check kern}
+ if a<128 then if k-lig_kern_base[f]+a+1>=nl then abort;
+ end;
+ end;
+ if a=255 then bch_label:=256*c+d;
+ end;
+for k:=kern_base[f]+kern_base_offset to exten_base[f]-1 do
+ store_scaled(font_info[k].sc);
+@z
+@x module 574 (actually a bugfix)
+ if a<>0 then check_byte_range(a);
+ if b<>0 then check_byte_range(b);
+ if c<>0 then check_byte_range(c);
+ check_byte_range(d);
+@y
+ if a<>0 then check_existence(a);
+ if b<>0 then check_existence(b);
+ if c<>0 then check_existence(c);
+ check_existence(d);
+@z
+@x module 576
+font_name[f]:=nom;
+@y
+if bch_label<nl then bchar_label[f]:=bch_label+lig_kern_base[f]
+else bchar_label[f]:=non_address;
+font_bchar[f]:=qi(bchar);
+font_false_bchar[f]:=qi(bchar);
+if bchar<=ec then if bchar>=bc then
+ begin qw:=char_info(f)(bchar); {N.B.: not |qi(bchar)|}
+ if char_exists(qw) then font_false_bchar[f]:=non_char;
+ end;
+font_name[f]:=nom;
+@z
+@x module 708 (slight but optional optimization)
+continue: if (qo(y)>=font_bc[g])and(qo(y)<=font_ec[g]) then
+ begin q:=char_info(g)(y);
+@y
+if (qo(y)>=font_bc[g])and(qo(y)<=font_ec[g]) then
+ begin continue: q:=char_info(g)(y);
+@z
+@x module 740 (another bugfix)
+ i:=char_info(f)(y);
+@y
+ i:=char_info(f)(y);
+ if not char_exists(i) then goto done;
+@z
+@x module 741
+ repeat cur_i:=font_info[a].qqqq;
+ if qo(next_char(cur_i))=skew_char[cur_f] then
+ begin if op_bit(cur_i)>=kern_flag then
+ s:=char_kern(cur_f)(cur_i);
+ goto done1;
+ end;
+ incr(a);
+ until stop_bit(cur_i)>=stop_flag;
+@y
+ cur_i:=font_info[a].qqqq;
+ if skip_byte(cur_i)>stop_flag then
+ begin a:=256*(qo(op_byte(cur_i)))+rem_byte(cur_i);
+ cur_i:=font_info[a].qqqq;
+ end;
+ loop begin if qo(next_char(cur_i))=skew_char[cur_f] then
+ begin if op_byte(cur_i)>=kern_flag then
+ if skip_byte(cur_i)<=stop_flag then s:=char_kern(cur_f)(cur_i);
+ goto done1;
+ end;
+ if skip_byte(cur_i)>=stop_flag then goto done1
+ else a:=a+qo(skip_byte(cur_i))+1;
+ cur_i:=font_info[a].qqqq;
+ end;
+@z
+@x module 749 (yet another bugfix of the same type!)
+@!p,@!v,@!x,@!y,@!z:pointer; {temporary registers for box construction}
+@y
+@!p,@!v,@!x,@!y,@!z:pointer; {temporary registers for box construction}
+@!c:quarterword;@+@!i:four_quarters; {registers for character examination}
+@z
+@x ibid
+ begin cur_c:=rem_byte(cur_i); character(nucleus(q)):=cur_c;
+ cur_i:=char_info(cur_f)(cur_c);
+@y
+ begin c:=rem_byte(cur_i); i:=char_info(cur_f)(c);
+ if char_exists(i) then
+ begin cur_c:=c; cur_i:=i; character(nucleus(q)):=c;
+ end;
+@z
+@x module 752
+@!p:pointer; {temporary register for list manipulation}
+@y
+@!p,@!r:pointer; {temporary registers for list manipulation}
+@z
+@x ibid
+ repeat cur_i:=font_info[a].qqqq;@/
+ @<If instruction |cur_i| is a kern with |cur_c|,
+ attach the kern after |q| and |return|;
+ or if it is a ligature with |cur_c|, combine
+ noads |q| and |p| and |goto restart|@>;
+ incr(a);
+ until stop_bit(cur_i)>=stop_flag;
+@y
+ cur_i:=font_info[a].qqqq;
+ if skip_byte(cur_i)>stop_flag then
+ begin a:=lig_kern_restart(cur_f)(cur_i);
+ cur_i:=font_info[a].qqqq;
+ end;
+ loop@+ begin @<If instruction |cur_i| is a kern with |cur_c|, attach
+ the kern after~|q|; or if it is a ligature with |cur_c|, combine
+ noads |q| and~|p| appropriately; then |return| if the cursor has
+ moved past a noad, or |goto restart|@>;
+ if skip_byte(cur_i)>=stop_flag then return;
+ a:=a+qo(skip_byte(cur_i))+1;
+ cur_i:=font_info[a].qqqq;
+ end;
+@z
+@x The entire code of module 753 is revised
+@y and should be replaced by the following:
+@ Note that a ligature between an |ord_noad| and another kind of noad
+is replaced by an |ord_noad|, when the two noads collapse into one.
+But we could make a parenthesis (say) change shape when it follows
+certain letters. Presumably a font designer will define such
+ligatures only when this convention makes sense.
+
+\chardef\?='174 % vertical line to indicate character retention
+
+@<If instruction |cur_i| is a kern with |cur_c|, ...@>=
+if next_char(cur_i)=cur_c then if skip_byte(cur_i)<=stop_flag then
+ if op_byte(cur_i)>=kern_flag then
+ begin p:=new_kern(char_kern(cur_f)(cur_i));
+ link(p):=link(q); link(q):=p; return;
+ end
+ else begin check_interrupt; {allow a way out of infinite ligature loop}
+ case op_byte(cur_i) of
+ qi(1),qi(5): character(nucleus(q)):=rem_byte(cur_i); {\.{=:\?}, \.{=:\?>}}
+ qi(2),qi(6): character(nucleus(p)):=rem_byte(cur_i); {\.{\?=:}, \.{\?=:>}}
+ qi(3),qi(7),qi(11):begin r:=new_noad; {\.{\?=:\?}, \.{\?=:\?>}, \.{\?=:\?>>}}
+ character(nucleus(r)):=rem_byte(cur_i);
+ fam(nucleus(r)):=fam(nucleus(q));@/
+ link(q):=r; link(r):=p;
+ if op_byte(cur_i)<qi(11) then math_type(nucleus(r)):=math_char
+ else math_type(nucleus(r)):=math_text_char; {prevent combination}
+ end;
+ othercases begin link(q):=link(p);
+ character(nucleus(q)):=rem_byte(cur_i); {\.{=:}}
+ mem[subscr(q)]:=mem[subscr(p)]; mem[supscr(q)]:=mem[supscr(p)];@/
+ free_node(p,noad_size);
+ end
+ endcases;
+ if op_byte(cur_i)>qi(3) then return;
+ math_type(nucleus(q)):=math_char; goto restart;
+ end
+@z
+@x module 862
+@!q,@!r,@!s:pointer; {miscellaneous nodes of temporary interest}
+@y
+@!q,@!r,@!s,@!prev_s:pointer; {miscellaneous nodes of temporary interest}
+@z
+@x module 892
+nodes $p_a$ and~$p_b$ in the description above are placed into variables
+@y
+nodes $p_{a-1}$ and~$p_b$ in the description above are placed into variables
+@z
+@x ibid
+@!hc:array[0..65] of halfword; {word to be hyphenated}
+@y
+@!hc:array[0..65] of 0..256; {word to be hyphenated}
+@z
+@x ibid
+@!hu:array[1..63] of ASCII_code; {like |hc|, before conversion to lowercase}
+@y
+@!hu:array[0..63] of 0..256; {like |hc|, before conversion to lowercase}
+@z
+@x module 894
+s:=link(cur_p);
+@y
+prev_s:=cur_p; s:=link(prev_s);
+@z
+@x module 895
+label done,found,not_found,found1,exit;
+@y
+label common_ending,done,found,found1,not_found,not_found+1,exit;
+@z
+@x module 896
+ else if type(s)=ligature_node then
+ begin q:=lig_ptr(s); c:=qo(character(q)); hf:=font(q);
+ end
+@y
+ else if type(s)=ligature_node then
+ if lig_ptr(s)=null then goto continue
+ else begin q:=lig_ptr(s); c:=qo(character(q)); hf:=font(q);
+ end
+@z
+@x ibid
+continue: s:=link(s);
+@y
+continue: prev_s:=s; s:=link(prev_s);
+@z
+@x ibid
+ha:=s
+@y
+ha:=prev_s
+@z
+@x module 898
+begin j:=hn; q:=lig_ptr(s);
+if font(q)<>hf then goto done3;
+repeat c:=qo(character(q));
+if lc_code(c)=0 then goto done3;
+if j=63 then goto done3;
+incr(j); hu[j]:=c; hc[j]:=lc_code(c);@/
+q:=link(q);
+until q=null;
+@y
+begin if font(lig_char(s))<>hf then goto done3;
+j:=hn; q:=lig_ptr(s);
+while q>null do
+ begin c:=qo(character(q));
+ if lc_code(c)=0 then goto done3;
+ if j=63 then goto done3;
+ incr(j); hu[j]:=c; hc[j]:=lc_code(c);@/
+ q:=link(q);
+ end;
+@z
+@x module 900 gets three new global variables
+@y
+@!init_list:pointer; {list of punctuation characters preceding the word}
+@!init_lig:boolean; {does |init_list| represent a ligature?}
+@!init_lft:boolean; {if so, did the ligature involve a left boundary?}
+@z
+@x module 901
+@!q,@!r,@!s:pointer; {temporary registers for list manipulation}
+@y
+@!p,@!q,@!r,@!s:pointer; {temporary registers for list manipulation}
+@!bchar:halfword; {right boundary character of hyphenated word, or |non_char|}
+@z
+@x module 903 should be entirely replaced
+@y by the following:
+@ If hyphens are in fact going to be inserted, \TeX\ first deletes the
+subsequence of nodes between |ha| and~|hb|. An attempt is made to
+preserve the effect that implicit boundary characters and punctuation marks
+had on ligatures inside the hyphenated word, by storing a left boundary or
+preceding character in |hu[0]| and by storing a possible right boundary
+in |bchar|. We set |j:=0| if |hu[0]| is to be part of the reconstruction;
+otherwise |j:=1|.
+The variable |s| will point to the tail of the current hlist, and
+|q| will point to the node following |hb|, so that
+things can be hooked up after we reconstitute the hyphenated word.
+
+@<Replace nodes |ha..hb| by a sequence of nodes...@>=
+q:=link(hb); link(hb):=null; r:=link(ha); link(ha):=null; bchar:=non_char;
+if type(hb)=ligature_node then if odd(subtype(hb)) then
+ bchar:=font_bchar[hf];
+if is_char_node(ha) then
+ begin init_list:=ha; init_lig:=false; hu[0]:=qo(character(ha));
+ end
+else if type(ha)=ligature_node then
+ begin init_list:=lig_ptr(ha); init_lig:=true; init_lft:=(subtype(ha)>1);
+ hu[0]:=qo(character(lig_char(ha)));
+ if init_list=null then if init_lft then
+ begin hu[0]:=256; init_lig:=false;
+ end; {in this case a ligature will be reconstructed from scratch}
+ free_node(ha,small_node_size);
+ end
+else goto not_found+1; {no punctuation found}
+s:=cur_p; {we have |cur_p<>ha| because |type(cur_p)=glue_node|}
+while link(s)<>ha do s:=link(s);
+j:=0; goto common_ending;
+not_found+1: j:=1; s:=ha; init_list:=null;
+if not is_char_node(r) then if type(r)=ligature_node then
+ if subtype(r)>1 then
+ begin j:=0; hu[0]:=256; init_lig:=false;
+ end;
+common_ending: flush_node_list(r);
+@<Reconstitute nodes for the hyphenated word, inserting discretionary hyphens@>;
+flush_list(init_list)
+@z
+@x modules 905--911 are entirely replaced
+@y by the following new code:
+Still further complications arise in the presence of ligatures that do not
+delete the original characters. When punctuation precedes the word being
+hyphenated, \TeX's method is not perfect under all possible scenarios,
+because punctuation marks and letters can propagate information back and forth.
+For example, suppose the original pre-hyphenation pair
+\.{*a} changes to \.{*y} via a \.{\?=:} ligature, which changes to \.{xy}
+via a \.{=:\?} ligature; if $p_{a-1}=\.x$ and $p_a=\.y$, the reconstitution
+procedure isn't smart enough to obtain \.{xy} again. In such cases the
+font designer should include a ligature that goes from \.{xa} to \.{xy}.
+
+@ The processing is facilitated by a subroutine called |reconstitute|. Given
+a string of characters $x_j\ldots x_n$, there is a smallest index $m\ge j$
+such that the ``translation'' of $x_j\ldots x_n$ by ligatures and kerning
+has the form $y_1\ldots y_t$ followed by the translation of $x_{m+1}\ldots x_n$,
+where $y_1\ldots y_t$ is some nonempty sequence of character, ligature, and
+kern nodes. We call $x_j\ldots x_m$ a ``cut prefix'' of $x_j\ldots x_n$.
+For example, if $x_1x_2x_3=\.{fly}$, and if the font contains `fl' as a
+ligature and a kern between `fl' and `y', then $m=2$, $y=2$, and $y_1$ will
+be a ligature node for `fl' followed by an appropriate kern node~$y_2$.
+In the most common case, $x_j$~forms no ligature with $x_{j+1}$ and we
+simply have $m=j$, $y_1=x_j$. If $m<n$ we can repeat the procedure on
+$x_{m+1}\ldots x_n$ until the entire translation has been found.
+
+The |reconstitute| function returns the integer $m$ and puts the nodes
+$y_1\ldots y_t$ into a linked list starting at |link(hold_head)|,
+getting the input $x_j\ldots x_n$ from the |hu| array. If $x_j=256$,
+we consider $x_j$ to be an implicit left boundary character; in this
+case |j| must be strictly less than~|n|. There is a
+parameter |bchar|, which is either 256 or an implicit right boundary character
+assumed to be present just following~$x_n$. (The value |hu[n+1]| is never
+explicitly examined, but the algorithm imagines that |bchar| is there.)
+
+If there exists an index |k| in the range $j\le k\le m$ such that |hyf[k]|
+is odd and such that the result of reconstitute would have been different
+if $x_{k+1}$ had been |hchar|, then |reconstitute| sets |hyphen_passed|
+to the smallest such~|k|. Otherwise it sets |hyphen_passed| to zero.
+
+A special convention is used in the case |j=0|: Then we assume that the
+translation of |hu[0]| appears in a special list of charnodes starting at
+|init_list|; moreover, if |init_lig| is |true|, then |hu[0]| will be
+a ligature character, involving a left boundary if |init_lft| is |true|.
+This facility is provided for cases when a hyphenated
+word is preceded by punctuation (like single or double quotes) that might
+affect the translation of the beginning of the word.
+
+@<Glob...@>=
+@!hyphen_passed:small_number; {first hyphen in a ligature, if any}
+
+@ @<Declare the function called |reconstitute|@>=
+function reconstitute(@!j,@!n:small_number;@!bchar,@!hchar:halfword):
+ small_number;
+label continue,done;
+var @!p:pointer; {temporary register for list manipulation}
+@!t:pointer; {a node being appended to}
+@!q:four_quarters; {character information or a lig/kern instruction}
+@!cur_rh:halfword; {hyphen character for ligature testing}
+@!test_char:halfword; {hyphen or other character for ligature testing}
+@!w:scaled; {amount of kerning}
+@!k:font_index; {position of current lig/kern instruction}
+begin hyphen_passed:=0; t:=hold_head; w:=0; link(hold_head):=null;
+ {at this point |ligature_present=lft_hit=rt_hit=false|}
+@<Set up data structures with the cursor following position |j|@>;
+continue:@<If there's a ligature or kern at the cursor position, update the data
+ structures, possibly advancing~|j|; continue until the cursor moves@>;
+@<Append a ligature and/or kern to the translation;
+ |goto continue| if the stack of inserted ligatures is nonempty@>;
+reconstitute:=j;
+end;
+
+@ The reconstitution procedure shares many of the global data structures
+by which \TeX\ has processed the words before they were hyphenated.
+There is an implied ``cursor'' between characters |cur_l| and |cur_r|;
+these characters will be tested for possible ligature activity. If
+|ligature_present| then |cur_l| is a ligature character formed from the
+original characters following |cur_q| in the current translation list.
+There is a ``ligature stack'' between the cursor and character |j+1|,
+consisting of pseudo-ligature nodes linked together by their |link| fields.
+This stack is normally empty unless a ligature command has created a new
+character that will need to be processed later. A pseudo-ligature is
+a special node having a |character| field that represents a potential
+ligature and a |lig_ptr| field that points to a |char_node| or is |null|.
+We have
+$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack>null|;\cr
+ |qi(hu[j+1])|,&if |lig_stack=null| and |j<n|;\cr
+ bchar,&if |lig_stack=null| and |j=n|.\cr}$$
+
+@<Glob...@>=
+@!cur_l,@!cur_r:halfword; {characters before and after the cursor}
+@!cur_q:pointer; {where a ligature should be detached}
+@!lig_stack:pointer; {unfinished business to the right of the cursor}
+@!ligature_present:boolean; {should a ligature node be made for |cur_l|?}
+@!lft_hit,@!rt_hit:boolean; {did we hit a ligature with a boundary character?}
+
+@ @d append_charnode_to_t(#)== begin link(t):=get_avail; t:=link(t);
+ font(t):=hf; character(t):=#;
+ end
+@d set_cur_r==begin if j<n then cur_r:=qi(hu[j+1])@+else cur_r:=bchar;
+ if odd(hyf[j]) then cur_rh:=hchar@+else cur_rh:=non_char;
+ end
+
+@<Set up data structures with the cursor following position |j|@>=
+cur_l:=qi(hu[j]); cur_q:=t;
+if j=0 then
+ begin ligature_present:=init_lig; p:=init_list;
+ if ligature_present then lft_hit:=init_lft;
+ while p>null do
+ begin append_charnode_to_t(character(p)); p:=link(p);
+ end;
+ end
+else if cur_l<non_char then append_charnode_to_t(cur_l);
+lig_stack:=null; set_cur_r
+
+@ We may want to look at the lig/kern program twice, once for a hyphen
+and once for a normal letter. (The hyphen might appear after the letter
+in the program, so we'd better not try to look for both at once.)
+
+@<If there's a ligature or kern at the cursor position, update...@>=
+if cur_l=non_char then
+ begin k:=bchar_label[hf];
+ if k=non_address then goto done@+else q:=font_info[k].qqqq;
+ end
+else begin q:=char_info(hf)(cur_l);
+ if char_tag(q)<>lig_tag then goto done;
+ k:=lig_kern_start(hf)(q); q:=font_info[k].qqqq;
+ if skip_byte(q)>stop_flag then
+ begin k:=lig_kern_restart(hf)(q); q:=font_info[k].qqqq;
+ end;
+ end; {now |k| is the starting address of the lig/kern program}
+if cur_rh<non_char then test_char:=cur_rh@+else test_char:=cur_r;
+loop@+begin if next_char(q)=test_char then if skip_byte(q)<=stop_flag then
+ if cur_rh<non_char then
+ begin hyphen_passed:=j; hchar:=non_char; cur_rh:=non_char;
+ goto continue;
+ end
+ else begin if hchar<non_char then if odd(hyf[j]) then
+ begin hyphen_passed:=j; hchar:=non_char;
+ end;
+ if op_byte(q)<kern_flag then
+ @<Carry out a ligature replacement, updating the cursor structure
+ and possibly advancing~|j|; |goto continue| if the cursor doesn't
+ advance, otherwise |goto done|@>;
+ w:=char_kern(hf)(q); goto done; {this kern will be inserted below}
+ end;
+ if skip_byte(q)>=stop_flag then
+ if cur_rh=non_char then goto done
+ else begin cur_rh:=non_char; goto continue;
+ end;
+ k:=k+qo(skip_byte(q))+1; q:=font_info[k].qqqq;
+ end;
+done:
+
+@ @d wrap_lig(#)==if ligature_present then
+ begin p:=new_ligature(hf,cur_l,link(cur_q));
+ if lft_hit then
+ begin subtype(p):=2; lft_hit:=false;
+ end;
+ if # then if lig_stack=null then
+ begin incr(subtype(p)); rt_hit:=false;
+ end;
+ link(cur_q):=p; t:=p; ligature_present:=false;
+ end
+@d pop_lig_stack==begin if lig_ptr(lig_stack)>null then
+ begin link(t):=lig_ptr(lig_stack); {this is a charnode for |hu[j+1]|}
+ t:=link(t); incr(j);
+ end;
+ p:=lig_stack; lig_stack:=link(p); free_node(p,small_node_size);
+ if lig_stack=null then set_cur_r@+else cur_r:=character(lig_stack);
+ end {if |lig_stack| isn't |null| we have |cur_rh=non_char|}
+
+@<Append a ligature and/or kern to the translation...@>=
+wrap_lig(rt_hit);
+if w<>0 then
+ begin link(t):=new_kern(w); t:=link(t); w:=0;
+ end;
+if lig_stack>null then
+ begin cur_q:=t; cur_l:=character(lig_stack); ligature_present:=true;
+ pop_lig_stack; goto continue;
+ end
+
+@ @<Carry out a ligature replacement, updating the cursor structure...@>=
+begin if cur_l=non_char then lft_hit:=true;
+if j=n then if lig_stack=null then rt_hit:=true;
+check_interrupt; {allow a way out in case there's an infinite ligature loop}
+case op_byte(q) of
+qi(1),qi(5):begin cur_l:=rem_byte(q); {\.{=:\?}, \.{=:\?>}}
+ ligature_present:=true;
+ end;
+qi(2),qi(6):begin cur_r:=rem_byte(q); {\.{\?=:}. \.{\?=:>}}
+ if lig_stack>null then character(lig_stack):=cur_r
+ else begin lig_stack:=new_lig_item(cur_r);
+ if j=n then bchar:=non_char
+ else begin p:=get_avail; lig_ptr(lig_stack):=p;
+ character(p):=qi(hu[j+1]); font(p):=hf;
+ end;
+ end;
+ end;
+qi(3):begin cur_r:=rem_byte(q); {\.{\?=:\?}}
+ p:=lig_stack; lig_stack:=new_lig_item(cur_r); link(lig_stack):=p;
+ end;
+qi(7),qi(11):begin wrap_lig(false); {\.{\?=:\?>}, \.{\?=:\?>>}}
+ cur_q:=t; cur_l:=rem_byte(q); ligature_present:=true;
+ end;
+othercases begin cur_l:=rem_byte(q); ligature_present:=true; {\.{=:}}
+ if lig_stack>null then pop_lig_stack
+ else if j=n then goto done
+ else begin append_charnode_to_t(cur_r); incr(j); set_cur_r;
+ end;
+ end
+endcases;
+if op_byte(q)>qi(4) then if op_byte(q)<>qi(7) then goto done;
+goto continue;
+end
+@z
+@x module 912
+@!c:ASCII_code; {character temporarily replaced by a hyphen}
+@y
+@!c:ASCII_code; {character temporarily replaced by a hyphen}
+@!c_loc:0..63; {where that character came from}
+@!r_count:integer; {replacement count for discretionary}
+@z
+@x modules 913--918 are to be completely replaced
+@y by the following code:
+@ When the following code is performed, |hyf[0]| and |hyf[hn]| will be zero.
+
+@<Reconstitute nodes for the hyphenated word...@>=
+repeat l:=j; j:=reconstitute(j,hn,bchar,qi(hyf_char))+1;
+if hyphen_passed=0 then
+ begin link(s):=link(hold_head);
+ while link(s)>null do s:=link(s);
+ if odd(hyf[j-1]) then
+ begin l:=j; hyphen_passed:=j-1; link(hold_head):=null;
+ end;
+ end;
+if hyphen_passed>0 then
+ @<Create and append a discretionary node as an alternative to the
+ unhyphenated word, and continue to develop both branches until they
+ become equivalent@>;
+until j>hn;
+link(s):=q
+
+@ @d advance_major_tail==begin major_tail:=link(major_tail); incr(r_count);
+ end
+
+@<Create and append a discretionary node as an alternative...@>=
+begin r:=get_node(small_node_size);
+link(r):=link(hold_head); type(r):=disc_node;
+major_tail:=r; r_count:=0;
+while link(major_tail)>null do advance_major_tail;
+i:=hyphen_passed;
+@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>;
+@<Put the \(c)characters |hu[i+1..@,]| into |post_break(r)|, appending to this
+ list and to |major_tail| until synchronization has been achieved@>;
+@<Move pointer |s| to the end of the current list, and set |replace_count(r)|
+ appropriately@>;
+end
+
+@ The new hyphen might combine with the previous character via ligature
+or kern. At this point we have |l-1<=i<=j| and |i<hn|.
+
+@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>=
+minor_tail:=null; pre_break(r):=null; hyf_node:=new_character(hf,hyf_char);
+if hyf_node<>null then
+ begin incr(i); c:=hu[i]; hu[i]:=hyf_char; free_avail(hyf_node);
+ end;
+while l<=i do
+ begin l:=reconstitute(l,i,font_bchar[hf],non_char)+1;
+ if link(hold_head)>null then
+ begin if minor_tail=null then pre_break(r):=link(hold_head)
+ else link(minor_tail):=link(hold_head);
+ minor_tail:=link(hold_head);
+ while link(minor_tail)>null do minor_tail:=link(minor_tail);
+ end;
+ end;
+if hyf_node<>null then
+ begin hu[i]:=c; {restore the character in the hyphen position}
+ l:=i; decr(i);
+ end
+
+@ The synchronization algorithm begins with |l=i+1<=j|.
+
+@<Put the \(c)characters |hu[i+1..@,]| into |post_break(r)|...@>=
+minor_tail:=null; post_break(r):=null; c_loc:=0;
+if bchar_label[hf]<non_address then {put left boundary at beginning of new line}
+ begin decr(l); c:=hu[l]; c_loc:=l; hu[l]:=256;
+ end;
+while l<j do
+ begin repeat l:=reconstitute(l,hn,bchar,non_char)+1;
+ if c_loc>0 then
+ begin hu[c_loc]:=c; c_loc:=0;
+ end;
+ if link(hold_head)>null then
+ begin if minor_tail=null then post_break(r):=link(hold_head)
+ else link(minor_tail):=link(hold_head);
+ minor_tail:=link(hold_head);
+ while link(minor_tail)>null do minor_tail:=link(minor_tail);
+ end;
+ until l>=j;
+ while l>j do
+ @<Append characters of |hu[j..@,]| to |major_tail|, advancing~|j|@>;
+ end
+
+@ @<Append characters of |hu[j..@,]|...@>=
+begin j:=reconstitute(j,hn,bchar,non_char)+1;
+link(major_tail):=link(hold_head);
+while link(major_tail)>null do advance_major_tail;
+end
+
+@ Ligature insertion can cause a word to grow exponentially in size. Therefore
+we must test the size of |r_count| here, even though the hyphenated text
+was at most 63 characters long.
+
+@<Move pointer |s| to the end of the current list...@>=
+if r_count>127 then {we have to forget the discretionary hyphen}
+ begin link(s):=link(r); link(r):=null; flush_node_list(r);
+ end
+else begin link(s):=r; replace_count(r):=r_count;
+ end;
+s:=major_tail
+@z
+@x module 1030
+@d main_loop=70 {go here to typeset |cur_chr| in the current font}
+@d main_loop_1=71 {like |main_loop|, but |(f,c)| = current font and char}
+@d main_loop_2=72 {like |main_loop_1|, but |c| is known to be in range}
+@d main_loop_3=73 {like |main_loop_2|, but several variables are set up}
+@d append_normal_space=74 {go here to append a normal space between words}
+@y
+@d main_loop=70 {go here to typeset a string of consecutive characters}
+@d main_loop_wrapup=80 {go here to finish a character or ligature}
+@d main_loop_move=90 {go here to advance the ligature cursor}
+@d main_loop_move_lig=95 {same, when advancing past a generated ligature}
+@d main_loop_lookahead=100 {go here to bring in another character, if any}
+@d main_lig_loop=110 {go here to check for ligatures or kerning}
+@d append_normal_space=120 {go here to append a normal space between words}
+@z
+@x ibid
+label big_switch,reswitch,main_loop,main_loop_1,main_loop_2,main_loop_3,
+@y
+label big_switch,reswitch,main_loop,main_loop_wrapup,
+ main_loop_move,main_loop_move+1,main_loop_move+2,main_loop_move_lig,
+ main_loop_lookahead,main_loop_lookahead+1,
+ main_lig_loop,main_lig_loop+1,main_lig_loop+2,
+@z
+@x ibid
+@<Local variables for the inner loop of |main_control|@>@;
+@y
+@z
+@x ibid
+hmode+char_num: begin scan_char_num; cur_chr:=cur_val; goto main_loop;
+ end;
+@y
+hmode+char_num: begin scan_char_num; cur_chr:=cur_val; goto main_loop;@+end;
+hmode+no_boundary: begin get_x_token;
+ if (cur_cmd=letter)or(cur_cmd=other_char)or(cur_cmd=char_given)or
+ (cur_cmd=char_num) then cancel_boundary:=true;
+ goto reswitch;
+ end;
+@z
+@x modules 1032--1040 are all to be replaced
+@y by the following code:
+@ The following part of the program was first written in a structured
+manner, according to the philosophy that ``premature optimization is
+the root of all evil.'' Then it was rearranged into pieces of
+spaghetti so that the most common actions could proceed with little or
+no redundancy.
+
+The original unoptimized form of this algorithm resembles the
+|reconstitute| procedure, which was described earlier in connection with
+hyphenation. Again we have an implied ``cursor`` between characters
+|cur_l| and |cur_r|. The main difference is that the |lig_stack| can now
+contain a charnode as well as pseudo-ligatures; that stack is now
+usually nonempty, because the next character of input (if any) has been
+appended to it. In |main_control| we have
+$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack>null|;\cr
+ |font_bchar[cur_font]|,&otherwise.\cr}$$
+Several additional global variables are needed.
+
+@<Glob...@>=
+@!main_f:internal_font_number; {the current font}
+@!main_i:four_quarters; {character information bytes for |cur_l|}
+@!main_j:four_quarters; {ligature/kern command}
+@!main_k:font_index; {index into |font_info|}
+@!main_p:pointer; {temporary register for list manipulation}
+@!main_s:integer; {space factor value}
+@!bchar:halfword; {right boundary character of current font, or |non_char|}
+@!false_bchar:halfword; {nonexistent character matching |bchar|, or |non_char|}
+@!cancel_boundary:boolean; {should the left boundary be ignored?}
+@!ins_disc:boolean; {should we insert a discretionary node?}
+
+@ The boolean variables of the main loop are normally false, and always reset
+to false before the loop is left. That saves us the extra work of initializing
+each time.
+
+@<Set init...@>=
+ligature_present:=false; cancel_boundary:=false; lft_hit:=false; rt_hit:=false;
+ins_disc:=false;
+
+@ We leave |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we
+set it to |sf_code(cur_chr)|, except that the space factor never changes
+from a value less than 1000 to a value exceeding 1000. The most common
+case is |sf_code(cur_chr)=1000|, so we want that case to be fast.
+
+The overall structure of the main loop is presented here. Some program labels
+are inside the individual sections.
+
+@d adjust_space_factor==@t@>@;@/
+ main_s:=sf_code(cur_chr);
+ if main_s=1000 then space_factor:=1000
+ else if main_s<1000 then
+ begin if main_s>0 then space_factor:=main_s;
+ end
+ else if space_factor<1000 then space_factor:=1000
+ else space_factor:=main_s
+
+@<Append character |cur_chr|...@>=
+adjust_space_factor;@/
+main_f:=cur_font;
+bchar:=font_bchar[main_f]; false_bchar:=font_false_bchar[main_f];
+if mode>0 then if language<>clang then fix_language;
+fast_get_avail(lig_stack); font(lig_stack):=main_f; cur_l:=qi(cur_chr);
+character(lig_stack):=cur_l;@/
+cur_q:=tail;
+if cancel_boundary then
+ begin cancel_boundary:=false; main_k:=non_address;
+ end
+else main_k:=bchar_label[main_f];
+if main_k=non_address then goto main_loop_move+2; {no left boundary processing}
+cur_r:=cur_l; cur_l:=non_char;
+goto main_lig_loop+1; {begin with cursor after left boundary}
+@#
+main_loop_wrapup:@<Make a ligature node, if |ligature_present|;
+ insert a null discretionary, if appropriate@>;
+main_loop_move:@<If the cursor is immediately followed by the right boundary,
+ |goto reswitch|; if it's followed by an invalid character, |goto big_switch|;
+ otherwise move the cursor one step to the right and |goto main_lig_loop|@>;
+main_loop_lookahead:@<Look ahead for another character, or leave |lig_stack|
+ empty if there's none there@>;
+main_lig_loop:@<If there's a ligature/kern command relevant to |cur_l| and
+ |cur_r|, adjust the text appropriately; exit to |main_loop_wrapup|@>;
+main_loop_move_lig:@<Move the cursor past a pseudo-ligature, then
+ |goto main_loop_lookahead| or |main_lig_loop|@>
+
+@ If the current horizontal list is empty, the reference to |character(tail)|
+here is not strictly legal, since |tail| will be a node freshly returned by
+|get_avail|. But this should cause no problem on most implementations, and we
+do want the inner loop to be fast.
+@^dirty Pascal@>
+
+A discretionary break is not inserted for an explicit hyphen when we are in
+restricted horizontal mode. In particular, this avoids putting discretionary
+nodes inside of other discretionaries.
+
+@d pack_lig(#)== {the parameter is either |rt_hit| or |false|}
+ begin main_p:=new_ligature(main_f,cur_l,link(cur_q));
+ if lft_hit then
+ begin subtype(main_p):=2; lft_hit:=false;
+ end;
+ if # then if lig_stack=null then
+ begin incr(subtype(main_p)); rt_hit:=false;
+ end;
+ link(cur_q):=main_p; tail:=main_p; ligature_present:=false;
+ end
+
+@d wrapup(#)==if cur_l<non_char then
+ begin if character(tail)=qi(hyphen_char[main_f]) then if link(cur_q)>null then
+ ins_disc:=true;
+ if ligature_present then pack_lig(#);
+ if ins_disc then
+ begin ins_disc:=false;
+ if mode>0 then tail_append(new_disc);
+ end;
+ end
+
+@<Make a ligature node, if |ligature_present|;...@>=
+wrapup(rt_hit)
+
+@ @<If the cursor is immediately followed by the right boundary...@>=
+if lig_stack=null then goto reswitch;
+cur_q:=tail; cur_l:=cur_r; {or |character(lig_stack)|}
+main_loop_move+1:if not is_char_node(lig_stack) then goto main_loop_move_lig;
+main_loop_move+2:if(cur_chr<font_bc[main_f])or(cur_chr>font_ec[main_f]) then
+ begin char_warning(main_f,cur_chr); free_avail(lig_stack); goto big_switch;
+ end;
+main_i:=char_info(main_f)(cur_l);
+if not char_exists(main_i) then
+ begin char_warning(main_f,cur_chr); free_avail(lig_stack); goto big_switch;
+ end;
+tail_append(lig_stack) {|main_loop_lookahead| is next}
+
+@ Here we are at |main_loop_move_lig|.
+When we begin this code we have |cur_l=character(lig_stack)| and |cur_q=tail|.
+
+@<Move the cursor past a pseudo-ligature...@>=
+main_p:=lig_ptr(lig_stack);
+if main_p>null then tail_append(main_p);
+temp_ptr:=lig_stack; lig_stack:=link(temp_ptr);
+free_node(temp_ptr,small_node_size);
+main_i:=char_info(main_f)(cur_l); ligature_present:=true;
+if lig_stack=null then
+ if main_p>null then goto main_loop_lookahead
+ else cur_r:=bchar
+else cur_r:=character(lig_stack);
+goto main_lig_loop
+
+@ The result of \.{\\char} can participate in a ligature or kern, so we must
+look ahead for it.
+
+@<Look ahead for another character...@>=
+get_next; {set only |cur_cmd| and |cur_chr|, for speed}
+if cur_cmd=letter then goto main_loop_lookahead+1;
+if cur_cmd=other_char then goto main_loop_lookahead+1;
+if cur_cmd=char_given then goto main_loop_lookahead+1;
+x_token; {now expand and set |cur_cmd|, |cur_chr|, |cur_tok|}
+if cur_cmd=letter then goto main_loop_lookahead+1;
+if cur_cmd=other_char then goto main_loop_lookahead+1;
+if cur_cmd=char_given then goto main_loop_lookahead+1;
+if cur_cmd=char_num then
+ begin scan_char_num; cur_chr:=cur_val; goto main_loop_lookahead+1;
+ end;
+if cur_cmd=no_boundary then bchar:=non_char;
+cur_r:=bchar; lig_stack:=null; goto main_lig_loop;
+main_loop_lookahead+1: adjust_space_factor;
+fast_get_avail(lig_stack); font(lig_stack):=main_f;
+cur_r:=qi(cur_chr); character(lig_stack):=cur_r;
+if cur_r=false_bchar then cur_r:=non_char {this prevents spurious ligatures}
+
+@ Even though comparatively few characters have a lig/kern program, several
+of the instructions here count as part of \TeX's inner loop, since a
+potentially long sequential search must be performed. For example, tests with
+Computer Modern Roman showed that about 40 per cent of all characters
+actually encountered in practice had a lig/kern program, and that about four
+lig/kern commands were investigated for every such character.
+
+At the beginning of this code we have |main_i=char_info(main_f)(cur_l)|.
+
+@<If there's a ligature/kern command...@>=
+if char_tag(main_i)<>lig_tag then goto main_loop_wrapup;
+main_k:=lig_kern_start(main_f)(main_i); main_j:=font_info[main_k].qqqq;
+if skip_byte(main_j)<=stop_flag then goto main_lig_loop+2;
+main_k:=lig_kern_restart(main_f)(main_j);
+main_lig_loop+1:main_j:=font_info[main_k].qqqq;
+main_lig_loop+2:if next_char(main_j)=cur_r then
+ if skip_byte(main_j)<=stop_flag then
+ @<Do ligature or kern command, returning to |main_lig_loop|
+ or |main_loop_wrapup| or |main_loop_move|@>;
+if skip_byte(main_j)=qi(0) then incr(main_k)
+else begin if skip_byte(main_j)>=stop_flag then goto main_loop_wrapup;
+ main_k:=main_k+qo(skip_byte(main_j))+1;
+ end;
+goto main_lig_loop+1
+
+@ When a ligature or kern instruction matches a character, we know from
+|read_font_info| that the character exists in the font, even though we
+haven't verified its existence in the normal way.
+
+This section could be made into a subroutine, if the code inside
+|main_control| needs to be shortened.
+
+\chardef\?='174 % vertical line to indicate character retention
+
+@<Do ligature or kern command...@>=
+begin if op_byte(main_j)>=kern_flag then
+ begin wrapup(rt_hit);
+ tail_append(new_kern(char_kern(main_f)(main_j))); goto main_loop_move;
+ end;
+if cur_l=non_char then lft_hit:=true
+else if lig_stack=null then rt_hit:=true;
+check_interrupt; {allow a way out in case there's an infinite ligature loop}
+case op_byte(main_j) of
+qi(1),qi(5):begin cur_l:=rem_byte(main_j); {\.{=:\?}, \.{=:\?>}}
+ main_i:=char_info(main_f)(cur_l); ligature_present:=true;
+ end;
+qi(2),qi(6):begin cur_r:=rem_byte(main_j); {\.{\?=:}, \.{\?=:>}}
+ if lig_stack=null then {right boundary character is being consumed}
+ begin lig_stack:=new_lig_item(cur_r); bchar:=non_char;
+ end
+ else if is_char_node(lig_stack) then {|link(lig_stack)=null|}
+ begin main_p:=lig_stack; lig_stack:=new_lig_item(cur_r);
+ lig_ptr(lig_stack):=main_p;
+ end
+ else character(lig_stack):=cur_r;
+ end;
+qi(3):begin cur_r:=rem_byte(main_j); {\.{\?=:\?}}
+ main_p:=lig_stack; lig_stack:=new_lig_item(cur_r);
+ link(lig_stack):=main_p;
+ end;
+qi(7),qi(11):begin wrapup(false); {\.{\?=:\?>}, \.{\?=:\?>>}}
+ cur_q:=tail; cur_l:=rem_byte(main_j);
+ main_i:=char_info(main_f)(cur_l); ligature_present:=true;
+ end;
+othercases begin cur_l:=rem_byte(main_j); ligature_present:=true; {\.{=:}}
+ if lig_stack=null then goto main_loop_wrapup
+ else goto main_loop_move+1;
+ end
+endcases;
+if op_byte(main_j)>qi(4) then
+ if op_byte(main_j)<>qi(7) then goto main_loop_wrapup;
+if cur_l<non_char then goto main_lig_loop;
+main_k:=bchar_label[main_f]; goto main_lig_loop+1;
+end
+@z
+% HINTS for making main_control shorter if you need to conserve space:
+% (1) make pack_lig(#) a procedure call instead of a macro call
+% (2) make the code of @<Do ligature...@> a function that returns one of
+% four values; you goto a label based on the value returned.
+@x module 1042
+begin p:=font_glue[cur_font];
+if p=null then
+ begin f:=cur_font; p:=new_spec(zero_glue); k:=param_base[f]+space_code;
+ width(p):=font_info[k].sc; {that's |space(f)|}
+ stretch(p):=font_info[k+1].sc; {and |space_stretch(f)|}
+ shrink(p):=font_info[k+2].sc; {and |space_shrink(f)|}
+ font_glue[f]:=p;
+@y
+begin main_p:=font_glue[cur_font];
+if main_p=null then
+ begin main_p:=new_spec(zero_glue); main_k:=param_base[cur_font]+space_code;
+ width(main_p):=font_info[main_k].sc; {that's |space(cur_font)|}
+ stretch(main_p):=font_info[main_k+1].sc; {and |space_stretch(cur_font)|}
+ shrink(main_p):=font_info[main_k+2].sc; {and |space_shrink(cur_font)|}
+ font_glue[cur_font]:=main_p;
+@z
+@x module 1045
+any_mode(relax),vmode+spacer,mmode+spacer,mmode+no_boundary:do_nothing;
+@y
+any_mode(relax),vmode+spacer,mmode+spacer:do_nothing;
+@z
+@x module 1090
+ vmode+ex_space:@t@>@;@/
+@y
+ vmode+ex_space,vmode+no_boundary:@t@>@;@/
+@z
+@x module 1322
+dump_int(font_glue[k]);@/
+@y
+dump_int(font_glue[k]);@/
+dump_int(bchar_label[k]);
+dump_int(font_bchar[k]);
+dump_int(font_false_bchar[k]);@/
+@z
+@x module 1323
+undump(min_halfword)(lo_mem_max)(font_glue[k]);@/
+@y
+undump(min_halfword)(lo_mem_max)(font_glue[k]);@/
+undump(0)(font_mem_size)(bchar_label[k]);
+undump(min_quarterword)(non_char)(font_bchar[k]);
+undump(min_quarterword)(non_char)(font_false_bchar[k]);
+@z
+
+363. New \inputlineno feature desired by Spivak
+@x module 416 gets a new definition and a new statement
+@y
+@d input_line_no_code=glue_val+1 {code for \.{\\inputlineno}}
+primitive("inputlineno",last_item,input_line_no_code);
+@!@:input_line_no_}{\.{\\inputlineno} primitive@>
+@z and module 417 changes in the obvious way
+@x module 424 gets new code at the beginning
+@y
+if cur_chr>glue_val then
+ begin cur_val:=line; cur_val_level:=int_val;
+ end
+else
+@z
+
+364. New feature \holdinginserts suggested by Mittelbach.
+@x modules 236--238 get a new integer parameter
+@d int_pars=53 {total number of integer parameters}
+@y
+@d holding_inserts_code=53 {do not remove insertion nodes from \.{\\box255}}
+@d int_pars=54 {total number of integer parameters}
+@z and appropriate further lines are added to match all the other parameters
+@x module 1014
+@<Prepare all the boxes involved in insertions to act as queues@>;
+q:=hold_head; link(q):=null; prev_p:=page_head; p:=link(prev_p);
+while p<>best_page_break do
+ begin if type(p)=ins_node then @<Either insert the material
+ specified by node |p| into the appropriate box, or
+ hold it for the next page; also delete node |p| from
+ the current page@>
+@y
+if holding_inserts<=0 then
+ @<Prepare all the boxes involved in insertions to act as queues@>;
+q:=hold_head; link(q):=null; prev_p:=page_head; p:=link(prev_p);
+while p<>best_page_break do
+ begin if type(p)=ins_node then
+ begin if holding_inserts<=0 then
+ @<Either insert the material specified by node |p| into the
+ appropriate box, or hold it for the next page;
+ also delete node |p| from the current page@>;
+ end
+@z
+% also insert begin...end around the Pascal code of module 1018
+
+365. New \badness feature (which I'd been resisting for years)
+@x module 416 gets a new definition and a new statement
+@y
+@d badness_code=glue_val+2 {code for \.{\\badness}}
+primitive("badness",last_item,badness_code);
+@!@:badness_}{\.{\\badness} primitive@>
+@z and module 417 changes in the obvious way
+@x module 424 (the new code just added in #363)
+ begin cur_val:=line; cur_val_level:=int_val;
+@y
+ begin if cur_chr=input_line_no_code then cur_val:=line
+ else cur_val:=last_badness; {|cur_chr=badness_code|}
+ cur_val_level:=int_val;
+@z
+@x module 646 gets a new global variable
+@y
+@!last_badness:integer; {badness of the most recently packaged box}
+@z
+@x module 648
+@ @<Set init...@>=adjust_tail:=null;
+@y
+@ @<Set init...@>=adjust_tail:=null; last_badness:=0;
+@z
+@x modules 649 and 668
+@!b:integer; {badness of the new box}
+begin r:=get_node(box_node_size); type(r):=hlist_node;
+@y (except it's vlist_node in 668)
+begin last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node;
+@z (except it's vlist_node in 668)
+% change b to last_badness in modules 660&674 (4 times), 667&678 (3 times)
+@x modules 658 and 673
+if (hbadness<inf_bad)and(o=normal)and(list_ptr(r)<>null) then
+@y (except it's vbadness in 673)
+if o=normal then if list_ptr(r)<>null then
+@z
+@x modules 664 and 676
+ begin set_glue_ratio_one(glue_set(r)); {this is the maximum shrinkage}
+@y
+ begin last_badness:=1000000;
+ set_glue_ratio_one(glue_set(r)); {use the maximum shrinkage}
+@z
+@x ibid
+else if (hbadness<100)and(o=normal)and(list_ptr(r)<>null) then
+@y (except it's vbadness in 676)
+else if o=normal then if list_ptr(r)<>null then
+@z
+
+366. New \emergencystretch feature
+@x modules 247--248 get a new dimen parameter
+@d dimen_pars=20 {total number of dimension parameters}
+@y
+@d emergency_stretch_code=20 {reduces badnesses on final pass of line-breaking}
+@d dimen_pars=21 {total number of dimension parameters}
+@z and appropriate further lines are added to match all the other parameters
+@x module 828
+@!second_pass:boolean; {is this our second attempt to break this paragraph?}
+@y
+@!second_pass:boolean; {is this our second attempt to break this paragraph?}
+@!final_pass:boolean; {is this our final attempt to break this paragraph?}
+@z
+% change second_pass to final_pass in modules 854 and 873
+@x module 863
+ second_pass:=false;
+ end
+else begin threshold:=tolerance; second_pass:=true;
+@y
+ second_pass:=false; final_pass:=false;
+ end
+else begin threshold:=tolerance; second_pass:=true;
+ final_pass:=(emergency_stretch<=0);
+@z
+@x ibid
+ @!stat if tracing_paragraphs>0 then print_nl("@@secondpass");@;@+tats@/
+ threshold:=tolerance; second_pass:=true; {if at first you don't
+ succeed, \dots}
+@y
+ if not second_pass then
+ begin@!stat if tracing_paragraphs>0 then print_nl("@@secondpass");@;@+tats@/
+ threshold:=tolerance; second_pass:=true; final_pass:=(emergency_stretch<=0);
+ end {if at first you don't succeed, \dots}
+ else begin @!stat if tracing_paragraphs>0 then
+ print_nl("@@finalpass");@;@+tats@/
+ background[2]:=background[2]+emergency_stretch; final_pass:=true;
+ end;
+@z
+
+367. New \errorcontextlines feature suggested by a TUG participant
+@x modules 236--238 get a new integer parameter
+@d int_pars=54 {total number of integer parameters}
+@y
+@d error_context_lines_code=54 {maximum intermediate line pairs shown}
+@d int_pars=55 {total number of integer parameters}
+@z and appropriate further lines are added to match all the other parameters
+@x module 311
+@<Local variables for formatting calculations@>@/
+begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
+ {store current state}
+loop@+begin cur_input:=input_stack[base_ptr]; {enter into the context}
+ @<Display the current context@>;
+ if (state<>token_list) then
+ if (name>17) or (base_ptr=0) then goto done;
+@y
+@!nn:integer; {number of contexts shown so far, less one}
+@!bottom_line:boolean; {have we reached the final context to be shown?}
+@<Local variables for formatting calculations@>@/
+begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
+ {store current state}
+nn:=-1; bottom_line:=false;
+loop@+begin cur_input:=input_stack[base_ptr]; {enter into the context}
+ if (state<>token_list) then
+ if (name>17) or (base_ptr=0) then bottom_line:=true;
+ if (base_ptr=input_ptr)or bottom_line or(nn<error_context_lines) then
+ @<Display the current context@>
+ else if nn=error_context_lines then
+ begin print_nl("..."); incr(nn); {omitted if |error_context_lines<0|}
+ end;
+ if bottom_line then goto done;
+@z
+% also insert begin...end around the Pascal code of module 312
+% and add the statement "incr(nn)" to that module
+
+368. char_warning inside hyphenation could clobber old_setting
+@x module 863
+done: @!stat if tracing_paragraphs>0 then end_diagnostic(true);@;@+tats@/
+@y
+done: @!stat if tracing_paragraphs>0 then
+ begin end_diagnostic(true); normalize_selector;
+ end;@+tats@/
+@z
+
+369. Make ".fmt" more easily switchable (Don Hosek).
+@x module 520 gets a new definition
+@y
+@d format_extension=".fmt" {the extension, as a \.{WEB} constant}
+@z now replace ".fmt" by format_extension in modules 529 and 1328.
+
+370. Possible range check on weird nullfont (Breitenlohner, 16 Oct 89).
+@x module 565
+if (bc>ec+1)or(ec>255) then abort;
+@y
+if (bc>ec+1)or(ec>255) then abort;
+if bc>255 then {|bc=256| and |ec=255|}
+ begin bc:=1; ec:=0;
+ end;
+@z
+
+371. Prevent save_stack conflicts, e.g. in {\hbox\expandafter{\csname
+ \endcsname}} (found by Sullivan).
+@x module 645
+@p procedure scan_spec; {scans a box specification and left brace}
+label found;
+begin if scan_keyword("to") then saved(0):=exactly
+@.to@>
+else if scan_keyword("spread") then saved(0):=additional
+@.spread@>
+else begin saved(0):=additional; saved(1):=0;
+ goto found;
+ end;
+scan_normal_dimen; saved(1):=cur_val;
+found: save_ptr:=save_ptr+2; scan_left_brace;
+@y
+@p procedure scan_spec(@!c:group_code;@!three_codes:boolean);
+ {scans a box specification and left brace}
+label found;
+var @!s:integer; {temporarily saved value}
+@!spec_code:exactly..additional;
+begin if three_codes then s:=saved(0);
+if scan_keyword("to") then spec_code:=exactly
+@.to@>
+else if scan_keyword("spread") then spec_code:=additional
+@.spread@>
+else begin spec_code:=additional; cur_val:=0;
+ goto found;
+ end;
+scan_normal_dimen;
+found: if three_codes then
+ begin saved(0):=s; incr(save_ptr);
+ end;
+saved(0):=spec_code; saved(1):=cur_val; save_ptr:=save_ptr+2;
+new_save_level(c); scan_left_brace;
+@z
+@x module 774
+scan_spec; new_save_level(align_group);@/
+@y
+scan_spec(align_group,false);@/
+@z
+@x module 1073
+ if t=0 then saved(0):=cur_val@+else saved(0):=-cur_val;
+ scan_box;
+ end;
+any_mode(leader_ship): begin saved(0):=leader_flag-a_leaders+cur_chr; scan_box;
+ end;
+any_mode(make_box): begin saved(0):=0; begin_box;
+ end;
+@y
+ if t=0 then scan_box(cur_val)@+else scan_box(-cur_val);
+ end;
+any_mode(leader_ship): scan_box(leader_flag-a_leaders+cur_chr);
+any_mode(make_box): begin_box(0);
+@z
+@x module 1075
+procedure box_end;
+var p:pointer; {|ord_noad| for new box in math mode}
+begin if saved(0)<box_flag then @<Append box |cur_box| to the current list,
+ shifted by |saved(0)|@>
+else if saved(0)<ship_out_flag then @<Store \(c)|cur_box| in a box register@>
+else if cur_box<>null then
+ if saved(0)>ship_out_flag then @<Append a new leader node that
+ uses |cur_box|@>
+@y
+procedure box_end(@!box_context:integer);
+var p:pointer; {|ord_noad| for new box in math mode}
+begin if box_context<box_flag then @<Append box |cur_box| to the current list,
+ shifted by |box_context|@>
+else if box_context<ship_out_flag then @<Store \(c)|cur_box| in a box register@>
+else if cur_box<>null then
+ if box_context>ship_out_flag then @<Append a new leader node that
+ uses |cur_box|@>
+@z
+@x module 1076
+ begin shift_amount(cur_box):=saved(0);
+@y
+ begin shift_amount(cur_box):=box_context;
+@z
+@x module 1077
+if saved(0)<box_flag+256 then
+ eq_define(box_base-box_flag+saved(0),box_ref,cur_box)
+else geq_define(box_base-box_flag-256+saved(0),box_ref,cur_box)
+@y
+if box_context<box_flag+256 then
+ eq_define(box_base-box_flag+box_context,box_ref,cur_box)
+else geq_define(box_base-box_flag-256+box_context,box_ref,cur_box)
+@z
+@x module 1078
+ begin append_glue; subtype(tail):=saved(0)-(leader_flag-a_leaders);
+@y
+ begin append_glue; subtype(tail):=box_context-(leader_flag-a_leaders);
+@z
+@x module 1079
+procedure begin_box;
+@y
+procedure begin_box(@!box_context:integer);
+@z
+@x ibid
+box_end; {in simple cases, we use the box immediately}
+@y
+box_end(box_context); {in simple cases, we use the box immediately}
+@z
+@x module 1083
+incr(save_ptr); scan_spec;
+if k=hmode then
+ if (saved(-3)<box_flag)and(abs(mode)=vmode) then
+ new_save_level(adjusted_hbox_group)
+ else new_save_level(hbox_group)
+else begin if k=vmode then new_save_level(vbox_group)
+ else begin new_save_level(vtop_group); k:=vmode;
+@y
+saved(0):=box_context;
+if k=hmode then
+ if (box_context<box_flag)and(abs(mode)=vmode) then
+ scan_spec(adjusted_hbox_group,true)
+ else scan_spec(hbox_group,true)
+else begin if k=vmode then scan_spec(vbox_group,true)
+ else begin scan_spec(vtop_group,true); k:=vmode;
+@z
+@x module 1084
+procedure scan_box; {the next input should specify a box or perhaps a rule}
+begin @<Get the next non-blank non-relax...@>;
+if cur_cmd=make_box then begin_box
+else if (saved(0)>=leader_flag)and((cur_cmd=hrule)or(cur_cmd=vrule)) then
+ begin cur_box:=scan_rule_spec; box_end;
+@y
+procedure scan_box(@!box_context:integer);
+ {the next input should specify a box or perhaps a rule}
+begin @<Get the next non-blank non-relax...@>;
+if cur_cmd=make_box then begin_box(box_context)
+else if (box_context>=leader_flag)and((cur_cmd=hrule)or(cur_cmd=vrule)) then
+ begin cur_box:=scan_rule_spec; box_end(box_context);
+@z
+@x module 1086
+pop_nest; box_end;
+@y
+pop_nest; box_end(saved(0));
+@z
+@x module 1117
+else begin incr(save_ptr); saved(-1):=0; scan_left_brace;
+ new_save_level(disc_group); push_nest; mode:=-hmode; space_factor:=1000;
+@y
+else begin incr(save_ptr); saved(-1):=0; new_save_level(disc_group);
+ scan_left_brace; push_nest; mode:=-hmode; space_factor:=1000;
+@z
+@x module 1119
+incr(saved(-1)); scan_left_brace; new_save_level(disc_group);
+@y
+incr(saved(-1)); new_save_level(disc_group); scan_left_brace;
+@z
+@x module 1167
+mmode+vcenter: begin scan_spec; new_save_level(vcenter_group); normal_paragraph;
+@y
+mmode+vcenter: begin scan_spec(vcenter_group,false); normal_paragraph;
+@z
+@x module 1172
+scan_left_brace; push_math(math_choice_group);
+@y
+push_math(math_choice_group); scan_left_brace;
+@z
+@x module 1174
+incr(saved(-1)); scan_left_brace; push_math(math_choice_group);
+@y
+incr(saved(-1)); push_math(math_choice_group); scan_left_brace;
+@z
+@x module 1241
+ if global then saved(0):=box_flag+256+cur_val
+ else saved(0):=box_flag+cur_val;
+ scan_optional_equals; scan_box;
+@y
+ if global then n:=256+cur_val@+else n:=cur_val;
+ scan_optional_equals; scan_box(box_flag+n);
+@z
+
+372. Bugfix 339 didn't go far enough (found by Sch\"opf and Mittelbach).
+@x module 516 [NOTE: THIS AFFECTS ALMOST ALL CHANGE FILES!]
+@ And here's the second.
+@^system dependencies@>
+
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin if (c=">")or(c=":") then
+ begin area_delimiter:=pool_ptr; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
+ str_room(1); append_char(c); {contribute |c| to the current string}
+@y
+@ And here's the second. The string pool might change as the file name is
+being scanned, since a new \.{\\csname} might be entered; therefore we keep
+|area_delimiter| and |ext_delimiter| relative to the beginning of the current
+string, instead of assigning an absolute address like |pool_ptr| to them.
+@^system dependencies@>
+
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin str_room(1); append_char(c); {contribute |c| to the current string}
+ if (c=">")or(c=":") then
+ begin area_delimiter:=cur_length; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=cur_length;
+@z
+@x module 517 [NOTE: THIS DOES TOO, AND SO DOES THE NEXT!]
+else begin cur_area:=str_ptr; incr(str_ptr);
+ str_start[str_ptr]:=area_delimiter+1;
+@y
+else begin cur_area:=str_ptr;
+ str_start[str_ptr+1]:=str_start[str_ptr]+area_delimiter; incr(str_ptr);
+@z
+@x ibid
+else begin cur_name:=str_ptr; incr(str_ptr);
+ str_start[str_ptr]:=ext_delimiter; cur_ext:=make_string;
+@y
+else begin cur_name:=str_ptr;
+ str_start[str_ptr+1]:=str_start[str_ptr]+ext_delimiter-area_delimiter-1;
+ incr(str_ptr); cur_ext:=make_string;
+@z
+
+373. Allow multiple hyphenmins in the same paragraph (Mike Ferguson).
+@x module 212, two new fields for list_state_record
+@y
+ @!lhm_field,@!rhm_field: quarterword;
+@z
+@x module 213, two new macros to access those fields
+@y
+@d lhmin==cur_list.lhm_field {\.{\\lefthyphenmin} at start of paragraph}
+@d rhmin==cur_list.rhm_field {\.{\\righthyphenmin} at start of paragraph}
+@z
+@x module 215, two new initializations
+@y
+lhmin:=0; rhmin:=0;
+@z
+@x module 218
+ if nest[p].ml_field<0 then print(" (\output routine)");
+@y
+ if m=hmode then@+if(nest[p].lhm_field<>2)or(nest[p].rhm_field<>3)then
+ begin print(" (hyphenmin "); print_int(nest[p].lhm_field); print_char(",");
+ print_int(nest[p].rhm_field); print_char(")");
+ end;
+ if nest[p].ml_field<0 then print(" (\output routine)");
+@z
+@x module 891
+l_hyf:=left_hyphen_min-1;@+if l_hyf<0 then l_hyf:=0;
+r_hyf:=right_hyphen_min-1;@+if r_hyf<0 then r_hyf:=0;
+min_hyf:=l_hyf+r_hyf+2; cur_lang:=0;
+@y
+l_hyf:=lhmin; r_hyf:=rhmin; cur_lang:=0;
+@z
+@x module 892
+@!l_hyf,@!r_hyf,@!min_hyf:integer; {limits on fragment sizes}
+@y
+@!l_hyf,@!r_hyf:integer; {limits on fragment sizes}
+@z
+@x module 894
+begin if min_hyf>63 then goto done1;
+prev_s:=cur_p; s:=link(prev_s);
+if s<>null then
+ begin @<Skip to node |ha|, or |goto done1| if no hyphenation
+ should be attempted@>;
+ @<Skip to node |hb|, putting letters into |hu| and |hc|@>;
+ @<Check that the nodes following |hb| permit hyphenation and that at least
+ |min_hyf| letters have been found, otherwise |goto done1|@>;
+@y
+begin prev_s:=cur_p; s:=link(prev_s);
+if s<>null then
+ begin @<Skip to node |ha|, or |goto done1| if no hyphenation
+ should be attempted@>;
+ if l_hyf+r_hyf>63 then goto done1;
+ @<Skip to node |hb|, putting letters into |hu| and |hc|@>;
+ @<Check that the nodes following |hb| permit hyphenation and that at least
+ |l_hyf+r_hyf| letters have been found, otherwise |goto done1|@>;
+@z
+@x module 899
+if hn<min_hyf then goto done1;
+@y
+if hn<l_hyf+r_hyf then goto done1;
+@z
+@x module 902
+for j:=l_hyf+1 to hn-r_hyf-1 do if odd(hyf[j]) then goto found1;
+@y
+for j:=l_hyf to hn-r_hyf do if odd(hyf[j]) then goto found1;
+@z
+@x module 923
+for j:=0 to hn-r_hyf do
+@y
+for j:=0 to hn-r_hyf+1 do
+@z
+@x ibid
+found: for j:=0 to l_hyf do hyf[j]:=0;
+for j:=0 to r_hyf do hyf[hn-j]:=0
+@y
+found: for j:=0 to l_hyf-1 do hyf[j]:=0;
+for j:=0 to r_hyf-1 do hyf[hn-j]:=0
+@z
+@x module 1091 begins with a new subroutine
+@y
+function norm_min(@!h:integer):small_number;
+begin if h<=0 then norm_min:=1@+else if h>=63 then norm_min:=63@+
+else norm_min:=h;
+end;
+@z
+@x module 1091 then uses the new subroutine
+push_nest; mode:=hmode; space_factor:=1000; clang:=0;
+@y
+lhmin:=norm_min(left_hyphen_min); rhmin:=norm_min(right_hyphen_min);
+push_nest; mode:=hmode; space_factor:=1000; clang:=0;
+@z
+@x module 1341
+@d stored_language(#)==mem[#+1].int {language number, in the range |0..255|}
+@y
+@d what_lang(#)==link(#+1) {language number, in the range |0..255|}
+@d what_lhm(#)==type(#+1) {minimum left fragment, in the range |1..63|}
+@d what_rhm(#)==subtype(#+1) {minimum right fragment, in the range |1..63|}
+@z
+@x module 1356
+ print_int(stored_language(p));
+@y
+ print_int(what_lang(p)); print(" (hyphenmin ");
+ print_int(what_lhm(p)); print_char(",");
+ print_int(what_rhm(p)); print_char(")");
+@z
+@x modules 1362 and 1363
+@ @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>=
+if subtype(cur_p)=language_node then cur_lang:=stored_language(cur_p)
+
+@ @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>=
+if subtype(s)=language_node then cur_lang:=stored_language(s)
+@y
+@ @d adv_past(#)==@+if subtype(#)=language_node then
+ begin cur_lang:=what_lang(#); l_hyf:=what_lhm(#); r_hyf:=what_rhm(#);@+end
+
+@<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>=@+
+adv_past(cur_p)
+
+@ @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>=@+
+adv_past(s)
+@z
+@x module 1376
+ stored_language(tail):=l; clang:=l;
+@y
+ what_lang(tail):=l; clang:=l;@/
+ what_lhm(tail):=norm_min(left_hyphen_min);
+ what_rhm(tail):=norm_min(right_hyphen_min);
+@z
+@x module 1377
+ stored_language(tail):=clang;
+@y
+ what_lang(tail):=clang;
+ what_lhm(tail):=norm_min(left_hyphen_min);
+ what_rhm(tail):=norm_min(right_hyphen_min);
+@z
+
+374. Make \par and end_template definitely non-character (Marc van Leeuwen)
+@x module 14 gets a new line
+@y
+if mem_top<256+11 then bad:=7; {we will want |null_list>255|}
+@z
+@x module 334
+primitive("par",par_end,0); par_loc:=cur_val; par_token:=cs_token_flag+par_loc;
+@y
+primitive("par",par_end,256); {cf. |scan_file_name|}
+par_loc:=cur_val; par_token:=cs_token_flag+par_loc;
+@z
+
+375. Alignments must be more robust to prevent crashes (Marc van Leeuwen)
+@x module 324
+else if token_type=u_template then align_state:=0;
+@y
+else if token_type=u_template then
+ if align_state>500000 then align_state:=0
+ else fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+@z
+@x module 782
+if (cur_cmd=assign_glue)and(cur_chr=glue_base+tab_skip_code) then
+@y
+if cur_cmd=endv then
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+if (cur_cmd=assign_glue)and(cur_chr=glue_base+tab_skip_code) then
+@z
+@x module 791
+p:=link(q);
+@y
+if align_state<500000 then
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+p:=link(q);
+@z
+
+376. Avoid kern removal in discretionary breaks (Marc van Leeuwen)
+@x module 815
+label done,done1,done2,done3,done4,continue;
+@y
+label done,done1,done2,done3,done4,done5,continue;
+@z
+@x module 866
+glue_node: begin @<If node |cur_p| is a legal breakpoint, call |try_break|@>;
+ @<Update the active widths by including the glue in |glue_ptr(cur_p)|@>;
+@y
+glue_node: begin @<If node |cur_p| is a legal breakpoint, call |try_break|;
+ then update the active widths by including the glue in |glue_ptr(cur_p)|@>;
+@z
+@x ibid
+disc_node: @<Try to break after a discretionary fragment@>;
+@y
+disc_node: @<Try to break after a discretionary fragment, then |goto done5|@>;
+@z
+@x ibid
+end
+@y
+done5:end
+@z
+Combine modules 868 and 869 into a single module, with a semicolon between.
+@x module 870 (which becomes module 869) gets ... in its title
+@y and the following new code just before its final "end":
+r:=replace_count(cur_p); s:=link(cur_p);
+while r>0 do
+ begin @<Add the width of node |s| to |act_width|@>;
+ decr(r); s:=link(s);
+ end;
+prev_p:=cur_p; cur_p:=s; goto done5;
+@z
+@x Now module 871 becomes 870, and we add a new (very similar) module 871:
+@y
+@ @<Add the width of node |s| to |act_width|@>=
+if is_char_node(s) then
+ begin f:=font(s);
+ act_width:=act_width+char_width(f)(char_info(f)(character(s)));
+ end
+else case type(s) of
+ ligature_node: begin f:=font(lig_char(s));
+ act_width:=act_width+
+ char_width(f)(char_info(f)(character(lig_char(s))));
+ end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ act_width:=act_width+width(s);
+ othercases confusion("disc4")
+@:this can't happen disc4}{\quad disc4@>
+ endcases
+@z
+@x module 877
+@!disc_break:boolean; {was the current break at a discretionary node?}
+@y
+@!disc_break:boolean; {was the current break at a discretionary node?}
+@!post_disc_break:boolean; {and did it have a nonempty post-break part?}
+@z
+@x ibid
+if cur_p<>null then @<Prune unwanted nodes at the beginning of the next line@>;
+@y
+if cur_p<>null then if not post_disc_break then
+ @<Prune unwanted nodes at the beginning of the next line@>;
+@z
+@x module 881
+q:=cur_break(cur_p); disc_break:=false;
+@y
+q:=cur_break(cur_p); disc_break:=false; post_disc_break:=false;
+@z
+@x module 883
+ if not is_char_node(s) then if next_break(cur_p)<>null then
+ if cur_break(next_break(cur_p))=s then s:=r;
+@y that subtle bug is no longer possible so we can remove the code
+@z
+@x module 884
+link(s):=r; r:=post_break(q); post_break(q):=null;
+@y
+link(s):=r; r:=post_break(q); post_break(q):=null; post_disc_break:=true;
+@z
+
+377. Pick up a few discretionary hyphens that were lost because of
+the new (cautious) algorithm.
+@x module 914
+@ @d advance_major_tail==begin major_tail:=link(major_tail); incr(r_count);
+ end
+
+@<Create and append a discretionary node as an alternative...@>=
+begin r:=get_node(small_node_size);
+@y
+@ In this repeat loop we will insert another discretionary if |hyf[j-1]| is
+odd, after both branches of the previous discretionary end at position |j-1|.
+Strictly speaking, we aren't justified in doing this, because we don't know
+that a hyphen after |j-1| is truly independent of those branches. But in almost
+all applications we would rather not lose a potentially valuable hyphenation
+point. (Consider the word `difficult', where the letter `c' is in position |j|.)
+
+@d advance_major_tail==begin major_tail:=link(major_tail); incr(r_count);
+ end
+
+@<Create and append a discretionary node as an alternative...@>=
+repeat r:=get_node(small_node_size);
+@z
+@x ibid
+i:=hyphen_passed;
+@y
+i:=hyphen_passed; hyf[i]:=0;
+@z
+@x ibid
+end
+@y
+hyphen_passed:=j-1; link(hold_head):=null;
+until not odd(hyf[j-1])
+@z
+
+378. Undumped trie must also be dumpable again (Breitenlohner, 11 Dec 89)
+@x module 1325
+undump_size(0)(trie_size)('trie size')(j); {|trie_max|}
+for k:=0 to j do undump_hh(trie[k]);
+undump_size(0)(trie_op_size)('trie op size')(j); {|trie_op_ptr|}
+@y
+undump_size(0)(trie_size)('trie size')(j); @+init trie_max:=j;@+tini
+for k:=0 to j do undump_hh(trie[k]);
+undump_size(0)(trie_op_size)('trie op size')(j); @+init trie_op_ptr:=j;@+tini
+@z
+@x ibid
+k:=256;
+while j>0 do
+ begin undump(0)(k-1)(k); undump(1)(j)(x); j:=j-x; op_start[k]:=qo(j);
+@y
+init for k:=0 to 255 do trie_used[k]:=min_quarterword;@+tini
+k:=256;
+while j>0 do
+ begin undump(0)(k-1)(k); undump(1)(j)(x);@+init trie_used[k]:=qi(x);@+tini
+ j:=j-x; op_start[k]:=qo(j);
+@z
+
+379. Allow output routine to access page totals. (Suggested by Frank
+Mittelbach and Chris Rowley, December 1989)
+@x module 421
+begin if page_contents=empty then
+@y
+begin if (page_contents=empty) and (not output_active) then
+@z
+
+380. Slightly more robust recovery and detection of \output anomalies.
+(Suggested by Chris Thompson, provoked by George Russell, January 1990)
+@x module 1026
+begin if loc<>null then @<Recover from an unbalanced output routine@>;
+@y
+begin if (loc<>null) or
+ ((token_type<>output_text)and(token_type<>backed_up)) then
+ @<Recover from an unbalanced output routine@>;
+@z
+@x module 1027
+help2("Your sneaky output routine has fewer real {'s than }'s.")@/
+@y
+help2("Your sneaky output routine has problematic {'s and/or }'s.")@/
+@z
+
+381. \eqno\aftergroup*$$ should yield * after the $$ (Michael Downes, 29 Jan 90)
+@x module 1194
+ begin cur_mlist:=p; cur_style:=text_style; mlist_penalties:=false;
+@y
+ begin @<Check that another \.\$ follows@>;
+ cur_mlist:=p; cur_style:=text_style; mlist_penalties:=false;
+@z
+@x ibid
+else begin @<Check that another \.\$ follows@>;
+@y
+else begin if a=null then @<Check that another \.\$ follows@>;
+@z
+
+382. Missed a case in the 8-bit change (Wayne Sullivan, 1 Feb 1990)
+@x module 360
+ begin if (end_line_char<0)or(end_line_char>127) then incr(limit);
+@y
+ begin if end_line_char_inactive then incr(limit);
+@z
+
+383. Linebreaking needs to be more robust when total demerits get
+very high (Frank Mittelbach, 22 Feb 1990)
+@x module 830
+@!artificial_badness:boolean; {has |b| been forced to zero?}
+@y
+@!artificial_demerits:boolean; {has |d| been forced to zero?}
+@z
+@x module 833 (this change is cosmetic only)
+@!minimal_demerits:array[very_loose_fit..tight_fit] of scaled; {best total
+ demerits known for current line class and position, given the fitness}
+@!minimum_demerits:scaled; {best total demerits known for current line class
+@y
+@!minimal_demerits:array[very_loose_fit..tight_fit] of integer; {best total
+ demerits known for current line class and position, given the fitness}
+@!minimum_demerits:integer; {best total demerits known for current line class
+@z
+@x module 836 here we want to ensure that total_demerits < awful_bad
+minimum_demerits:=minimum_demerits+abs(adj_demerits);
+@y
+if abs(adj_demerits)>=awful_bad-minimum_demerits then
+ minimum_demerits:=awful_bad-1
+else minimum_demerits:=minimum_demerits+abs(adj_demerits);
+@z
+@x module 851
+begin @!stat artificial_badness:=false;@+tats@/
+@y
+begin artificial_demerits:=false;@/
+@z
+@x module 854
+ begin b:=0; {set badness zero, this break is forced}
+ @!stat artificial_badness:=true;@+tats
+ end
+@y
+ artificial_demerits:=true {set demerits zero, this break is forced}
+@z
+@x module 855
+@<Compute the demerits, |d|, from |r| to |cur_p|@>;
+@y
+if artificial_demerits then d:=0
+else @<Compute the demerits, |d|, from |r| to |cur_p|@>;
+@z
+@x module 856
+if artificial_badness then print_char("*")@+else print_int(b);
+@.*\relax@>
+print(" p="); print_int(pi);
+print(" d="); print_int(d);
+@y
+if b>inf_bad then print_char("*")@+else print_int(b);
+@.*\relax@>
+print(" p="); print_int(pi); print(" d=");
+if artificial_demerits then print_char("*")@+else print_int(d);
+@z
+@x module 859
+d:=line_penalty+b; d:=d*d;
+@y
+begin d:=line_penalty+b;
+if abs(d)>=10000 then d:=100000000@+else d:=d*d;
+@z
+@x ibid
+if abs(fit_class-fitness(r))>1 then d:=d+adj_demerits
+@y
+if abs(fit_class-fitness(r))>1 then d:=d+adj_demerits;
+end
+@z
+
+384. Math fonts may disappear outside of \eqno (Marc van Leeuwen).
+@x module 1194
+ if danger then flush_math;
+@y
+ danger:=false;
+ @<Check that the necessary fonts for math symbols are present;
+ if not, flush the current math lists and set |danger:=true|@>;
+@z
+
+385. Forgot to rule out charnode when testing for node type (Marc van Leeuwen).
+@x module 805
+ begin if type(q)=unset_node then
+ @<Set the unset box |q| and the unset boxes in it@>
+ else if type(q)=rule_node then
+ @<Make the running dimensions in rule |q| extend to the
+ boundaries of the alignment@>;
+@y
+ begin if not is_char_node(q) then
+ if type(q)=unset_node then
+ @<Set the unset box |q| and the unset boxes in it@>
+ else if type(q)=rule_node then
+ @<Make the running dimensions in rule |q| extend to the
+ boundaries of the alignment@>;
+@z
+@x module 903
+if type(hb)=ligature_node then if odd(subtype(hb)) then
+@y
+if not is_char_node(hb) then
+ if type(hb)=ligature_node then if odd(subtype(hb)) then
+@z
+@x module 1202
+ if p<>null then if type(p)=glue_node then d:=0;
+@y
+ if p<>null then if not is_char_node(p) then if type(p)=glue_node then d:=0;
+@z
+
+386. Don't change the font of punctuation before a hyphenated word
+ (Scott Allendorf, reported 7 Mar 90)
+@x module 895
+label common_ending,done,found,found1,not_found,not_found+1,exit;
+@y
+label common_ending,done,found,found1,found2,not_found,exit;
+@z
+@x module 903
+if is_char_node(ha) then
+ begin init_list:=ha; init_lig:=false; hu[0]:=qo(character(ha));
+ end
+else if type(ha)=ligature_node then
+ begin init_list:=lig_ptr(ha); init_lig:=true; init_lft:=(subtype(ha)>1);
+ hu[0]:=qo(character(lig_char(ha)));
+ if init_list=null then if init_lft then
+ begin hu[0]:=256; init_lig:=false;
+ end; {in this case a ligature will be reconstructed from scratch}
+ free_node(ha,small_node_size);
+ end
+else goto not_found+1; {no punctuation found}
+s:=cur_p; {we have |cur_p<>ha| because |type(cur_p)=glue_node|}
+while link(s)<>ha do s:=link(s);
+j:=0; goto common_ending;
+not_found+1: j:=1; s:=ha; init_list:=null;
+if not is_char_node(r) then if type(r)=ligature_node then
+ if subtype(r)>1 then
+ begin j:=0; hu[0]:=256; init_lig:=false;
+ end;
+@y
+if is_char_node(ha) then
+ if font(ha)<>hf then goto found2
+ else begin init_list:=ha; init_lig:=false; hu[0]:=qo(character(ha));
+ end
+else if type(ha)=ligature_node then
+ if font(lig_char(ha))<>hf then goto found2
+ else begin init_list:=lig_ptr(ha); init_lig:=true; init_lft:=(subtype(ha)>1);
+ hu[0]:=qo(character(lig_char(ha)));
+ if init_list=null then if init_lft then
+ begin hu[0]:=256; init_lig:=false;
+ end; {in this case a ligature will be reconstructed from scratch}
+ free_node(ha,small_node_size);
+ end
+else begin {no punctuation found; look for left boundary}
+ if not is_char_node(r) then if type(r)=ligature_node then
+ if subtype(r)>1 then goto found2;
+ j:=1; s:=ha; init_list:=null; goto common_ending;
+ end;
+s:=cur_p; {we have |cur_p<>ha| because |type(cur_p)=glue_node|}
+while link(s)<>ha do s:=link(s);
+j:=0; goto common_ending;
+found2: s:=ha; j:=0; hu[0]:=256; init_lig:=false; init_list:=null;
+@z
+
+387. Balance the parens showing on the terminal (for Lispers).
+@x module 304
+@!in_open : 0..max_in_open; {the number of lines in the buffer, less one}
+@y
+@!in_open : 0..max_in_open; {the number of lines in the buffer, less one}
+@!open_parens : 0..max_in_open; {the number of open text files}
+@z
+@x module 331
+in_open:=0; max_buf_stack:=0;
+@y
+in_open:=0; open_parens:=0; max_buf_stack:=0;
+@z
+@x module 362
+ begin print_char(")"); force_eof:=false;
+ update_terminal; {show user that file has been read}
+@y
+ begin print_char(")"); decr(open_parens);
+ update_terminal; {show user that file has been read}
+ force_eof:=false;
+@z
+@x module 537
+print_char("("); print(name); update_terminal; state:=new_line;
+@y
+print_char("("); incr(open_parens); print(name); update_terminal;
+state:=new_line;
+@z
+@x module 1334
+if job_name=0 then open_log_file;
+@y
+if job_name=0 then open_log_file;
+while open_parens>0 do
+ begin print(" )"); decr(open_parens);
+ end;
+@z
+
+388. Optimize \ifx\p\q after \let\p=\q (Marc van Leeuwen says that
+ AmS-TeX uses this a lot).
+@x module 508
+ while (p<>null)and(q<>null) do
+ if info(p)<>info(q) then p:=null
+ else begin p:=link(p); q:=link(q);
+ end;
+b:=((p=null)and(q=null));
+@y
+if p=q then b:=true
+else begin while (p<>null)and(q<>null) do
+ if info(p)<>info(q) then p:=null
+ else begin p:=link(p); q:=link(q);
+ end;
+ b:=((p=null)and(q=null));
+ end;
+@z
+@x module 538 (while we're at it, might as well optimize this too)
+begin if not input_ln(cur_file,false) then do_nothing;
+@y
+begin if input_ln(cur_file,false) then do_nothing;
+@z
+@x module 1020 (and this)
+else begin wait:=false; s:=ins_ptr(p);
+ link(last_ins_ptr(r)):=s; s:=last_ins_ptr(r);
+@y
+else begin wait:=false; s:=last_ins_ptr(r); link(s):=ins_ptr(p);
+@z
+
+389. Treat migration properly in displays (Marc van Leeuwen).
+@x module 1199 (a bad bug that really has bitten)
+adjust_tail:=adjust_head; b:=hpack(p,natural);
+@y
+adjust_tail:=adjust_head; b:=hpack(p,natural); p:=list_ptr(b);
+@z
+@x module 1204
+shift_amount(b):=s+d; append_to_vlist(b);
+if t<>adjust_head then
+ begin link(tail):=link(adjust_head); tail:=t;
+ end
+@y
+shift_amount(b):=s+d; append_to_vlist(b)
+@z
+@x module 1205
+ tail_append(new_penalty(post_display_penalty));
+ end
+else begin tail_append(new_penalty(post_display_penalty));
+ tail_append(new_param_glue(g2));
+ end
+@y
+ g2:=0;
+ end;
+if t<>adjust_head then {migrating material comes after equation number}
+ begin link(tail):=link(adjust_head); tail:=t;
+ end;
+tail_append(new_penalty(post_display_penalty));
+if g2>0 then tail_append(new_param_glue(g2))
+@z
+
+-----------Here I draw the line with respect to further changes
+
+390. Uninitialized nullfont parameters (found by Lance Carnes, 11 May 90).
+@x module 552
+hyphen_char[null_font]:="-"; skew_char[null_font]:=-1;
+@y
+hyphen_char[null_font]:="-"; skew_char[null_font]:=-1;
+bchar_label[null_font]:=non_address;
+font_bchar[null_font]:=non_char; font_false_bchar[null_font]:=non_char;
+@z
+
+391. Disable \write{\the\prevgraf} (B. Jackowski, July 1990).
+@x module 422
+begin nest[nest_ptr]:=cur_list; p:=nest_ptr;
+while abs(nest[p].mode_field)<>vmode do decr(p);
+scanned_result(nest[p].pg_field)(int_val);
+end
+@y
+if mode=0 then scanned_result(0)(int_val) {|prev_graf=0| within \.{\\write}}
+else begin nest[nest_ptr]:=cur_list; p:=nest_ptr;
+ while abs(nest[p].mode_field)<>vmode do decr(p);
+ scanned_result(nest[p].pg_field)(int_val);
+ end
+@z
+
+392. Report correct line number when buffer overflows (George Russell).
+@x module 538
+begin if input_ln(cur_file,false) then do_nothing;
+firm_up_the_line;
+if end_line_char_inactive then decr(limit)
+else buffer[limit]:=end_line_char;
+first:=limit+1; loc:=start; line:=1;
+@y
+begin line:=1;
+if input_ln(cur_file,false) then do_nothing;
+firm_up_the_line;
+if end_line_char_inactive then decr(limit)
+else buffer[limit]:=end_line_char;
+first:=limit+1; loc:=start;
+@z
+
+-------------
+Note: When making change 376, I forgot to delete the redundant
+code in module 883, and I should also have changed the name of that
+module. These cosmetic changes (and some changes to the comments)
+were made in version 3.14, in addition to the following two changes.
+-------------
+
+393. Show unprintable characters in font id's (Wayne Sullivan, Dec 1990)
+@x module 63
+print(s);
+@y
+slow_print(s);
+@z
+@x module 262 can now be spruced up
+else begin print_esc(""); slow_print(text(p));
+@y
+else begin print_esc(text(p));
+@z
+@x and module 263 likewise
+else begin print_esc(""); slow_print(text(p));
+ end;
+@y
+else print_esc(text(p));
+@z
+
+394. Avoid range check if total_pages>65535 (Eberhard Mattes, Dec 1990)
+@x module 642
+ dvi_out(total_pages div 256); dvi_out(total_pages mod 256);@/
+@y
+ dvi_out((total_pages div 256) mod 256); dvi_out(total_pages mod 256);@/
+@z
+
+395. Less confusing error message (18 Sep 91)
+@x module 436
+ begin print_err("Bad math code");
+@.Bad math code@>
+ help2("A numeric math code must be between 0 and 32767.")@/
+@y (because a \mathcode can be 32768, but not a \mathchar)
+ begin print_err("Bad mathchar");
+@.Bad mathchar@>
+ help2("A mathchar number must be between 0 and 32767.")@/
+@z
+
+396. Internal strings should not be expanded even if unprintable;
+i.e., selector=new_string => print(c)==print_char(c), print(s)==slow_print(s).
+This change completes what I began with number 393 (18 Sep 91).
+@x modules 59 and 60 (same change to both)
+ else if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+@y
+ else if selector>pseudo then
+ begin print_char(s); return; {internal strings are not expanded}
+ end
+ else if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+@z
+@x module 61
+else begin print(format_ident); print_ln;
+@y
+else begin slow_print(format_ident); print_ln;
+@z
+@x module 84
+ print(input_stack[base_ptr].name_field);
+@y
+ slow_print(input_stack[base_ptr].name_field);
+@z
+@x module 518
+begin print(a); print(n); print(e);
+@y
+begin slow_print(a); slow_print(n); slow_print(e);
+@z
+@x module 536
+print(format_ident); print(" ");
+@y
+slow_print(format_ident); print(" ");
+@z
+@x module 537
+print_char("("); incr(open_parens); print(name); update_terminal;
+@y
+print_char("("); incr(open_parens); slow_print(name); update_terminal;
+@z
+@x module 581
+ print(font_name[f]); print_char("!"); end_diagnostic(false);
+@y
+ slow_print(font_name[f]); print_char("!"); end_diagnostic(false);
+@z
+@x module 642
+ print_nl("Output written on "); print(output_file_name);
+@y
+ print_nl("Output written on "); slow_print(output_file_name);
+@z
+@x module 1261
+set_font:begin print("select font "); print(font_name[chr_code]);
+@y
+set_font:begin print("select font "); slow_print(font_name[chr_code]);
+@z
+@x module 1280
+print(s); update_terminal;
+@y
+slow_print(s); update_terminal;
+@z
+@x module 1283
+begin print_err(s);
+@y
+begin print_err(""); slow_print(s);
+@z
+@x module 1328
+print(w_make_name_string(fmt_file)); flush_string;
+print_nl(format_ident)
+@y
+slow_print(w_make_name_string(fmt_file)); flush_string;
+print_nl(""); slow_print(format_ident)
+@z
+@x module 1333
+ print(log_name); print_char(".");
+@y
+ slow_print(log_name); print_char(".");
+@z
+@x module 1339
+10: print(n);
+@y
+10: slow_print(n);
+@z
+
+397. Retain more right context for reconstructing ligatures (19 Sep 91)
+@x module 892
+@!l_hyf,@!r_hyf:integer; {limits on fragment sizes}
+@y
+@!l_hyf,@!r_hyf:integer; {limits on fragment sizes}
+@!hyf_bchar:halfword; {boundary character after $c_n$}
+@z
+@x module 897
+ c:=qo(character(s));
+@y
+ hyf_bchar:=character(s); c:=qo(hyf_bchar);
+@z
+@x module 897 again
+ s:=link(s);
+@y
+ s:=link(s); hyf_bchar:=non_char;
+@z
+@x module 898
+j:=hn; q:=lig_ptr(s);
+@y
+j:=hn; q:=lig_ptr(s);@+if q>null then hyf_bchar:=character(q);
+@z
+@x module 903
+q:=link(hb); link(hb):=null; r:=link(ha); link(ha):=null; bchar:=non_char;
+@y
+q:=link(hb); link(hb):=null; r:=link(ha); link(ha):=null; bchar:=hyf_bchar;
+@z
+
+398. Patch the previous code, which could cause double kerning (CET1, 29 Sep 91)
+@x module 897
+ hb:=s; incr(hn); hu[hn]:=c; hc[hn]:=lc_code(c);
+ end
+ else if type(s)=ligature_node then
+ @<Move the characters of a ligature node to |hu| and |hc|;
+ but |goto done3| if they are not all letters@>
+ else if (type(s)<>kern_node)or(subtype(s)<>normal) then goto done3;
+ s:=link(s); hyf_bchar:=non_char;
+@y
+ hb:=s; incr(hn); hu[hn]:=c; hc[hn]:=lc_code(c); hyf_bchar:=non_char;
+ end
+ else if type(s)=ligature_node then
+ @<Move the characters of a ligature node to |hu| and |hc|;
+ but |goto done3| if they are not all letters@>
+ else if (type(s)=kern_node)and(subtype(s)=normal) then hb:=s
+ else goto done3;
+ s:=link(s);
+@z
+@x module 898
+hb:=s; hn:=j;
+@y
+hb:=s; hn:=j;
+if odd(subtype(s)) then hyf_bchar:=font_bchar[hf]@+else hyf_bchar:=non_char;
+@z
+@x module 903
+if not is_char_node(hb) then
+ if type(hb)=ligature_node then if odd(subtype(hb)) then
+ bchar:=font_bchar[hf];
+@y
+@z
+
+399. Disallow \setbox in do_assignments (problem found by Robert Hunt, Nov 91)
+@x module 76
+@!deletions_allowed:boolean; {is it safe for |error| to call |get_token|?}
+@y
+@!deletions_allowed:boolean; {is it safe for |error| to call |get_token|?}
+@!set_box_allowed:boolean; {is it safe to do a \.{\\setbox} assignment?}
+@z
+@x module 77
+deletions_allowed:=true; error_count:=0; {|history| is initialized elsewhere}
+@y
+deletions_allowed:=true; set_box_allowed:=true;
+error_count:=0; {|history| is initialized elsewhere}
+@z
+@x module 1241
+ scan_optional_equals; scan_box(box_flag+n);
+@y
+ if set_box_allowed then scan_box(box_flag+n)
+ else begin print_err("Improper "); print_esc("setbox");
+@.Improper \\setbox@>
+ help2("Sorry, \setbox is not allowed after \halign in a display,")@/
+ ("or between \accent and an accented character."); error;
+ end;
+@z
+@x module 1270
+ prefixed_command;
+@y
+ set_box_allowed:=false; prefixed_command; set_box_allowed:=true;
+@z
+
+400. Defend against negative cur_mu (Wayne Sullivan, Nov 91)
+@x module 716
+begin n:=x_over_n(m,@'200000); f:=remainder;@/
+@y
+begin n:=x_over_n(m,@'200000); f:=remainder;@/
+if f<0 then
+ begin decr(n); f:=f+@'200000;
+ end;
+@z
+@x module 717
+ begin n:=x_over_n(m,@'200000); f:=remainder;@/
+@y
+ begin n:=x_over_n(m,@'200000); f:=remainder;@/
+ if f<0 then
+ begin decr(n); f:=f+@'200000;
+ end;
+@z
+
+401. Defend against `|}{|' in |\read| (Michael Downes).
+@x module 483
+ store_new_token(cur_tok);
+@y
+ if align_state<1000000 then {unmatched `\.\}' aborts the line}
+ begin repeat get_token; until cur_tok=0;
+ align_state:=1000000; goto done;
+ end;
+ store_new_token(cur_tok);
+@z
+
+402. Economize string storage under multiple \font defs (B. Jackowski)
+@x module 1257
+@!old_setting:0..max_selector; {holds |selector| setting}
+@y
+@!old_setting:0..max_selector; {holds |selector| setting}
+@!flushable_string:str_number; {string not yet referenced}
+@z
+@x module 1260
+for f:=font_base+1 to font_ptr do
+ if str_eq_str(font_name[f],cur_name)and str_eq_str(font_area[f],cur_area) then
+ begin if s>0 then
+@y
+flushable_string:=str_ptr-1;
+for f:=font_base+1 to font_ptr do
+ if str_eq_str(font_name[f],cur_name)and str_eq_str(font_area[f],cur_area) then
+ begin if cur_name=flushable_string then
+ begin flush_string; cur_name:=font_name[f];
+ end;
+ if s>0 then
+@z
+
+403. Don't look for \newlinechar within unprintable chars (B. Raichle, Nov 91)
+@x module 59
+var j:pool_pointer; {current character code position}
+@y
+var j:pool_pointer; {current character code position}
+@!nl:integer; {new-line character to restore}
+@z
+@x
+ else if selector>pseudo then
+ begin print_char(s); return; {internal strings are not expanded}
+ end
+ else if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+@y
+ else begin if selector>pseudo then
+ begin print_char(s); return; {internal strings are not expanded}
+ end;
+ if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+ nl:=new_line_char; new_line_char:=-1;
+ {temporarily disable new-line character}
+ j:=str_start[s];
+ while j<str_start[s+1] do
+ begin print_char(so(str_pool[j])); incr(j);
+ end;
+ new_line_char:=nl; return;
+ end;
+@z
+@x module 60
+label exit;
+var j:pool_pointer; {current character code position}
+begin if s>=str_ptr then s:="???" {this can't happen}
+@.???@>
+else if s<256 then
+ if s<0 then s:="???" {can't happen}
+ else if selector>pseudo then
+ begin print_char(s); return; {internal strings are not expanded}
+ end
+ else if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+j:=str_start[s];
+while j<str_start[s+1] do
+ begin print(so(str_pool[j])); incr(j);
+ end;
+exit:end;
+label exit;
+var j:pool_pointer; {current character code position}
+begin if s>=str_ptr then s:="???" {this can't happen}
+@.???@>
+else if s<256 then
+ if s<0 then s:="???" {can't happen}
+ else if selector>pseudo then
+ begin print_char(s); return; {internal strings are not expanded}
+ end
+ else if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+j:=str_start[s];
+while j<str_start[s+1] do
+ begin print(so(str_pool[j])); incr(j);
+ end;
+exit:end;
+@y
+var j:pool_pointer; {current character code position}
+begin if (s>=str_ptr) or (s<256) then print(s)
+else begin j:=str_start[s];
+ while j<str_start[s+1] do
+ begin print(so(str_pool[j])); incr(j);
+ end;
+ end;
+end;
+@z
+
+404. False boundary character logic was incorrect (found by Eberhard
+ Mattes and Bernd Raichle, 6 Feb 92)
+@x module 1036
+cur_q:=tail; cur_l:=cur_r; {or |character(lig_stack)|}
+@y the following is needed since cur_r might have been changed to non_char
+cur_q:=tail; cur_l:=character(lig_stack);
+@z
+
+405. New paragraph and resume-after-display should continue current
+ \language (Sch\"opf and Thompson, 17 July 92)
+@@@ Undo the changes to modules 212, 213, 215 in change number 373.
+@x module 218
+ if m=hmode then@+if(nest[p].lhm_field<>2)or(nest[p].rhm_field<>3)then
+ begin print(" (hyphenmin "); print_int(nest[p].lhm_field); print_char(",");
+ print_int(nest[p].rhm_field); print_char(")");
+ end;
+@y
+ if m=hmode then if (nest[p].pg_field <> @'40600000) then
+ begin print(" (language"); print_int(nest[p].pg_field mod @'200000);
+ print(":hyphenmin"); print_int(nest[p].pg_field div @'20000000);
+ print_char(","); print_int((nest[p].pg_field div @'200000) mod @'100);
+ print_char(")");
+ end;
+@z
+@x module 816
+pop_nest;
+@y prev_graf now carries the language parameters at start of horizontal mode
+init_cur_lang:=prev_graf mod @'200000;
+init_l_hyf:=prev_graf div @'20000000;
+init_r_hyf:=(prev_graf div @'200000) mod @'100;
+pop_nest;
+@z
+@x module 891
+l_hyf:=lhmin; r_hyf:=rhmin; cur_lang:=0;
+@y
+cur_lang:=init_cur_lang; l_hyf:=init_l_hyf; r_hyf:=init_r_hyf;
+@z
+@x module 892
+@!cur_lang:ASCII_code; {current hyphenation table of interest}
+@!l_hyf,@!r_hyf:integer; {limits on fragment sizes}
+@y
+@!cur_lang,@!init_cur_lang:ASCII_code; {current hyphenation table of interest}
+@!l_hyf,@!r_hyf,@!init_l_hyf,@!init_r_hyf:integer; {limits on fragment sizes}
+@z
+@x module 1091
+lhmin:=norm_min(left_hyphen_min); rhmin:=norm_min(right_hyphen_min);
+push_nest; mode:=hmode; space_factor:=1000; clang:=0;@/
+@y
+push_nest; mode:=hmode; space_factor:=1000; set_cur_lang; clang:=cur_lang;
+prev_graf:=(norm_min(left_hyphen_min)*@'100+norm_min(right_hyphen_min))
+ *@'200000+cur_lang;
+@z
+@x module 1200
+push_nest; mode:=hmode; space_factor:=1000; clang:=0;
+@y
+push_nest; mode:=hmode; space_factor:=1000; set_cur_lang; clang:=cur_lang;
+prev_graf:=(norm_min(left_hyphen_min)*@'100+norm_min(right_hyphen_min))
+ *@'200000+cur_lang;
+@z
+
+406. Avoid (harmless) range errors (Philip Taylor and CET1, 17 Dec 92).
+@x module 934
+var n:small_number; {length of current word}
+@!j:small_number; {an index into |hc|}
+@y
+var n:0..64; {length of current word; not always a |small_number|}
+@!j:0..64; {an index into |hc|}
+@z
+@x module 960
+var k,@!l:small_number; {indices into |hc| and |hyf|}
+@y
+var k,@!l:0..64; {indices into |hc| and |hyf|;
+ not always in |small_number| range}
+@z
+
+407. Kerns inserted via boundary chars should not disappear (William E. Baxter,
+ 25 Feb 93)
+@x module 837
+Kern nodes for accents are treated specially: They do not
+disappear at a line break.
+@y
+Kern nodes do not disappear at a line break unless they are |explicit|.
+@z
+@x ibid
+ math_node,kern_node: if subtype(s)=acc_kern then goto done
+ else break_width[1]:=break_width[1]-width(s);
+@y
+ math_node: break_width[1]:=break_width[1]-width(s);
+ kern_node: if subtype(s)<>explicit then goto done
+ else break_width[1]:=break_width[1]-width(s);
+@z
+@x module 839
+plus $l_1$, so the length from |cur_p| to |cur_p| should be $\gamma+l_0+l_1-l$,
+minus the length of nodes that will be discarded after the discretionary break.
+@y (this change and the next two should have been made long ago, with #376!)
+plus $l_1$, so the length from |cur_p| to |cur_p| should be $\gamma+l_0+l_1-l$.
+If the post-break text of the discretionary is empty, a break may also
+discard~|q|; in that unusual case we subtract the length of~|q| and any
+other nodes that will be discarded after the discretionary break.
+@z
+@x module 840
+ begin @<Add the width of node |s| to |break_width| and increase |t|,
+ unless it's discardable@>;
+ s:=link(s);
+ end;
+break_width[1]:=break_width[1]+disc_width;
+if t=0 then s:=link(v); {more nodes may also be discardable after the break}
+@y
+ begin @<Add the width of node |s| to |break_width|@>;
+ s:=link(s);
+ end;
+break_width[1]:=break_width[1]+disc_width;
+if post_break(cur_p)=null then s:=link(v);
+ {nodes may be discardable after the break}
+@z
+@x module 842 (which now has a new title)
+ hlist_node,vlist_node,rule_node:break_width[1]:=break_width[1]+width(s);
+ kern_node: if (t=0)and(subtype(s)<>acc_kern) then t:=-1 {discardable}
+ othercases confusion("disc2")
+@:this can't happen disc2}{\quad disc2@>
+ endcases;
+incr(t)
+@y
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]+width(s);
+ othercases confusion("disc2")
+@:this can't happen disc2}{\quad disc2@>
+ endcases
+@z
+@x module 866
+kern_node: kern_break;
+@y
+kern_node: if subtype(cur_p)=explicit then kern_break
+ else act_width:=act_width+width(cur_p);
+@z
+@x module 868
+ else if precedes_break(prev_p) then try_break(0,unhyphenated);
+@y
+ else if precedes_break(prev_p) then try_break(0,unhyphenated)
+ else if (type(prev_p)=kern_node)and(subtype(prev_p)<>explicit) then
+ try_break(0,unhyphenated);
+@z
+@x module 879
+ if subtype(q)=acc_kern then if type(q)=kern_node then goto done1;
+@y
+ if type(q)=kern_node then if subtype(q)<>explicit then goto done1;
+@z
+
+408. Prevent boundary kern from disappearing after hyphenation (25 Feb 93)
+@x module 897
+ else if (type(s)=kern_node)and(subtype(s)=normal) then hb:=s
+@y
+ else if (type(s)=kern_node)and(subtype(s)=normal) then
+ begin hb:=s;
+ hyf_bchar:=font_bchar[hf];
+ end
+@z
+
+409. Avoid potential future bug [P Breitenlohner] (26 Jun 93)
+@x module 628 [shift_amount(leader_box)=0 now but maybe not in extensions]
+dvi_v:=save_v; dvi_h:=save_h; cur_v:=save_v;
+@y
+dvi_v:=save_v; dvi_h:=save_h; cur_v:=base_line;
+@z
+@x module 637
+dvi_v:=save_v; dvi_h:=save_h; cur_h:=save_h;
+@y
+dvi_v:=save_v; dvi_h:=save_h; cur_h:=left_edge;
+@z
+
+410. Mustn't let |non-address| depend on |font_mem_size|
+@x module 549
+@d non_address==font_mem_size {a spurious |font_index|}
+@y
+@d non_address=0 {a spurious |bchar_label|}
+@z
+@x module 916
+if bchar_label[hf]<non_address then {put left boundary at beginning of new line}
+@y
+if bchar_label[hf]<>non_address then {put left boundary at beginning of new line}
+@z
+@x module 1323, this will detect obsolete format files
+undump(0)(font_mem_size)(bchar_label[k]);
+@y
+undump(0)(fmem_ptr-1)(bchar_label[k]);
+@z
+
+411. Allow large |font_mem_size|, many font parameters.
+@x module 549
+@!font_params:array[internal_font_number] of halfword; {how many font
+@y
+@!font_params:array[internal_font_number] of font_index; {how many font
+@z
+
+412. Math kerns are explicit; this matters, after change 405 (W Carlip, Sep 94)
+@x module 717
+ width(p):=mu_mult(width(p)); subtype(p):=normal;
+@y
+ width(p):=mu_mult(width(p)); subtype(p):=explicit;
+@z
+
+413. Avoid overflow on real-to-integer conversion
+@x module 619
+@!edge:scaled; {left edge of sub-box, or right edge of leader space}
+@y
+@!edge:scaled; {left edge of sub-box, or right edge of leader space}
+@!glue_temp:real; {glue value before rounding}
+@z
+@x module 625
+@ @<Move right or output leaders@>=
+begin g:=glue_ptr(p); rule_wd:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ rule_wd:=rule_wd+round(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ end
+ else begin if shrink_order(g)=g_order then
+ rule_wd:=rule_wd-round(float(glue_set(this_box))*shrink(g));
+@y
+@ @d vet_glue(#)== glue_temp:=#;
+ if glue_temp>float_constant(1000000000) then
+ glue_temp:=float_constant(1000000000)
+ else if glue_temp<-float_constant(1000000000) then
+ glue_temp:=-float_constant(1000000000)
+
+@<Move right or output leaders@>=
+begin g:=glue_ptr(p); rule_wd:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ rule_wd:=rule_wd+round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*shrink(g));
+ rule_wd:=rule_wd-round(glue_temp);
+@z
+@x module 629
+@!edge:scaled; {bottom boundary of leader space}
+@y
+@!edge:scaled; {bottom boundary of leader space}
+@!glue_temp:real; {glue value before rounding}
+@z
+@x module 634
+ rule_ht:=rule_ht+round(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ end
+ else begin if shrink_order(g)=g_order then
+ rule_ht:=rule_ht-round(float(glue_set(this_box))*shrink(g));
+@y
+ begin vet_glue(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ rule_ht:=rule_ht+round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*shrink(g));
+ rule_ht:=rule_ht-round(glue_temp);
+@z
+
+414. final cleanup should not retain spurious reference counts (20 Mar 95)
+@x module 1335
+while open_parens>0 do
+@y
+while input_ptr>0 do
+ if state=token_list then end_token_list@+else end_file_reading;
+while open_parens>0 do
+@z
+@x module 1335, continued
+ cur_if:=subtype(cond_ptr); cond_ptr:=link(cond_ptr);
+@y
+ cur_if:=subtype(cond_ptr); temp_ptr:=cond_ptr;
+ cond_ptr:=link(cond_ptr); free_node(temp_ptr,if_node_size);
+@z
+@x module 1335, concluded
+ begin @!init store_fmt_file; return;@+tini@/
+@y
+ begin @!init for c:=top_mark_code to split_bot_mark_code do
+ if cur_mark[c]<>null then delete_token_ref(cur_mark[c]);
+ store_fmt_file; return;@+tini@/
+@z
+
+415. we need not raise hassles about two-digit years (23 Nov 98)
+@x module 1328
+print_int(year mod 100); print_char(".");
+@y
+print_int(year); print_char(".");
+@z
+
+416. \xleaders often drops the final box (Hiroshi Nakashima, 12 July 99)
+@x modules 627 and 636
+ else begin lx:=(2*lr+lq+1) div (2*lq+2); {round|(lr/(lq+1))|}
+@y
+ else begin lx:=lr div (lq+1);
+@z
+
+417. disallow 100000 unbalanced }s outside of alignment (Ralf Roth, 30 Aug 01)
+@x module 789
+to another alignment is being scanned.
+
+@<Insert the \(v)\<v_j>...@>=
+begin if scanner_status=aligning then
+@y
+to another alignment is being scanned, or when no alignment preamble is active.
+
+@<Insert the \(v)\<v_j>...@>=
+begin if (scanner_status=aligning) or (cur_align=null) then
+@z
+
+418. don't allow end-template except at end of template (Roth, 30 Aug 01)
+@x module 325
+while (state=token_list)and(loc=null) do end_token_list; {conserve stack space}
+@y
+while (state=token_list)and(loc=null)and(token_type<>v_template) do
+ end_token_list; {conserve stack space}
+@z
+@x module 390
+while (state=token_list)and(loc=null) do end_token_list; {conserve stack space}
+@y
+while (state=token_list)and(loc=null)and(token_type<>v_template) do
+ end_token_list; {conserve stack space}
+@z
+@x module 1131
+during an entire alignment, until |fin_align| removes it.
+
+@<Declare act...@>=
+procedure do_endv;
+begin if cur_group=align_group then
+@y
+during an entire alignment, until |fin_align| removes it.
+
+A devious user might force an |endv| command to occur just about anywhere;
+we must defeat such hacks.
+
+@<Declare act...@>=
+procedure do_endv;
+begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
+while (input_stack[base_ptr].index_field<>v_template) and
+ (input_stack[base_ptr].loc_field=null) and
+ (input_stack[base_ptr].state_field=token_list) do decr(base_ptr);
+if (input_stack[base_ptr].index_field<>v_template) or
+ (input_stack[base_ptr].loc_field<>null) or
+ (input_stack[base_ptr].state_field<>token_list) then
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+ if cur_group=align_group then
+@z
+
+419. Improve rounding of glue during output.
+@x module 3
+@!@^dirty \PASCAL@>
+@y
+@!@^dirty \PASCAL@>
+
+Incidentally, \PASCAL's standard |round| function can be problematical,
+because it disagrees with the IEEE floating-point standard.
+Many implementors have
+therefore chosen to substitute their own home-grown rounding procedure.
+@z
+@x module 619
+begin this_box:=temp_ptr; g_order:=glue_order(this_box);
+@y
+@!cur_glue:real; {glue seen so far}
+@!cur_g:scaled; {rounded equivalent of |cur_glue| times the glue ratio}
+begin cur_g:=0; cur_glue:=float_constant(0);
+this_box:=temp_ptr; g_order:=glue_order(this_box);
+@z
+@x module 625
+begin g:=glue_ptr(p); rule_wd:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ rule_wd:=rule_wd+round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*shrink(g));
+ rule_wd:=rule_wd-round(glue_temp);
+ end;
+ end;
+@y
+begin g:=glue_ptr(p); rule_wd:=width(g)-cur_g;
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin cur_glue:=cur_glue+stretch(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+@^real multiplication@>
+ cur_g:=round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin cur_glue:=cur_glue-shrink(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+ cur_g:=round(glue_temp);
+ end;
+ end;
+rule_wd:=rule_wd+cur_g;
+@z
+@x module 629
+begin this_box:=temp_ptr; g_order:=glue_order(this_box);
+@y
+@!cur_glue:real; {glue seen so far}
+@!cur_g:scaled; {rounded equivalent of |cur_glue| times the glue ratio}
+begin cur_g:=0; cur_glue:=float_constant(0);
+this_box:=temp_ptr; g_order:=glue_order(this_box);
+@z
+@x module 634
+begin g:=glue_ptr(p); rule_ht:=width(g);
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*stretch(g));
+@^real multiplication@>
+ rule_ht:=rule_ht+round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin vet_glue(float(glue_set(this_box))*shrink(g));
+ rule_ht:=rule_ht-round(glue_temp);
+ end;
+ end;
+@y
+begin g:=glue_ptr(p); rule_ht:=width(g)-cur_g;
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin cur_glue:=cur_glue+stretch(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+@^real multiplication@>
+ cur_g:=round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin cur_glue:=cur_glue-shrink(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+ cur_g:=round(glue_temp);
+ end;
+ end;
+rule_ht:=rule_ht+cur_g;
+@z
+
+420. Error recovery hindered by missing goto statement (reported by
+David Fuchs, September 2007)
+
+@x module 395
+incr(align_state); long_state:=call; cur_tok:=par_token; ins_error;
+@y
+incr(align_state); long_state:=call; cur_tok:=par_token; ins_error;
+goto continue;
+@z
+
+421. Slight improvement to inner loop speed also eliminates a case
+of "dirty Pascal"
+
+@x module 1035
+ begin if character(tail)=qi(hyphen_char[main_f]) then if link(cur_q)>null then
+@y
+ begin if link(cur_q)>null then
+ if character(tail)=qi(hyphen_char[main_f]) then ins_disc:=true;
+@z
+
+422. Amendment to bugfix 414 (reported by David Fuchs, September 2007)
+
+@x module 1335
+ store_fmt_file; return;@+tini@/
+@y
+ if last_glue<>max_halfword then delete_glue_ref(last_glue);
+ store_fmt_file; return;@+tini@/
+@z
+
+423. Remove leaking glue reference after error (DRF, September 2007)
+
+@x module 1236
+ help2("I can't carry out that multiplication or division,")@/
+ ("since the result is out of range.");
+@y
+ help2("I can't carry out that multiplication or division,")@/
+ ("since the result is out of range.");
+ if p>=glue_val then delete_glue_ref(cur_val);
+@z
+
+424. Leaders with muglue is henceforth disallowed (indeed, David Fuchs
+observed that it has never worked, except in 180pt fonts!)
+
+@x module 1078
+if ((cur_cmd=hskip)and(abs(mode)<>vmode))or@|
+ ((cur_cmd=vskip)and(abs(mode)=vmode))or@|
+ ((cur_cmd=mskip)and(abs(mode)=mmode)) then
+@y
+if ((cur_cmd=hskip)and(abs(mode)<>vmode))or@|
+ ((cur_cmd=vskip)and(abs(mode)=vmode)) then
+@z
+
+425. An optimization suggested by Fuchs that I couldn't resist, because
+it improves the inner loop whenever the last letter of a word
+has a lig/kern program. Also another twiddle for the inner loop.
+
+@x module 1039
+if char_tag(main_i)<>lig_tag then goto main_loop_wrapup;
+@y
+if char_tag(main_i)<>lig_tag then goto main_loop_wrapup;
+if cur_r=non_char then goto main_loop_wrapup;
+@z
+@x module 1036
+tail_append(lig_stack) {|main_loop_lookahead| is next}
+@y
+link(tail):=lig_stack; tail:=lig_stack {|main_loop_lookahead| is next}
+@z
+
+426. For robustness, enforce a restriction checked by TFtoPL (suggested
+by DRF)
+
+@x module 365
+if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then abort;
+@y
+if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then abort;
+if (nw=0)or(nh=0)or(nd=0)or(ni=0) then abort;
+@z
+
+427. Tell more precisely the effective size of 1:1 insertions (DEK, 27 Feb 08)
+
+@x module 986
+ t:=x_over_n(height(r),1000)*count(t); print_scaled(t);
+@y
+ if count(t)=1000 then t:=height(r)
+ else t:=x_over_n(height(r),1000)*count(t);
+ print_scaled(t);
+@z
+
+428. \csname\endcsname should be followed by space in print_cs, just as all
+other multiletter control sequences are (found by Oleg Bulatov, 14 Sep 2008)
+
+@x module 262
+ begin print_esc("csname"); print_esc("endcsname");
+@y
+ begin print_esc("csname"); print_esc("endcsname"); print_char(" ");
+@z
+
+-----------
+999. The absolutely final change (to be made after my death)
+@x module 2
+@d banner=='This is TeX, Version 3.14159265' {printed when \TeX\ starts}
+@y
+@d banner=='This is TeX, Version $\pi$' {printed when \TeX\ starts}
+@z
+When this change is made, the corresponding line should be changed in
+Volume B, and also on page 23 of The TeXbook.
+My last will and testament for TeX is that no further changes be made
+under any circumstances. Improved systems should not be called simply `TeX';
+that name, unqualified, should refer only to the program for which I have
+taken personal responsibility. -- Don Knuth
+
+ * Possibly nice ideas that will not be implemented
+
+. classes of marks analogous to classes of insertions
+
+. \showcontext to show the current location without stopping for error
+
+. \show commands to be less like errors
+
+. \everyeof to insert tokens before an \input file ends
+ (strange example: \everyeof{\noexpand} will allow things
+ like \xdef\a{\input foo}!)
+
+. generalize \leftskip and \rightskip to token lists (problems with
+ displayed math then)
+
+. generalize \widowline and \clubline to go further into a paragraph
+
+. \lastbox to remove and box a charnode if one is there
+
+. \lastbox to set a new quantity \lastboxshift, to preserve info now lost
+
+. \posttolerance for third pass of line breaking
+
+* Design errors that are too late to fix
+
+. additional parameters should be in symbol fonts to govern the space between
+ rules and text in \over, \sqrt, etc.
+
+. multilingual typesetting doesn't work properly when the \lccode changes
+ within a paragraph
+
+. large accents cannot be brought close to small letters in math mode
+
+. \hyphenation is not sensitive to \language
+
+* Bad ideas that will not be implemented
+
+. several people want to be able to remove arbitrary elements of lists,
+ but that must never be done because some of those elements (e.g. kerns
+ for accents) depend on floating point arithmetic
+
+. if anybody wants letter spacing desperately they should put it in their own
+ private version (e.g. generalize the hpack routine) and NOT call it TeX.
diff --git a/systems/knuth/dist/etc/vftovp.web b/systems/knuth/dist/etc/vftovp.web
new file mode 100644
index 0000000000..cf0ba73326
--- /dev/null
+++ b/systems/knuth/dist/etc/vftovp.web
@@ -0,0 +1,2373 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Version 1 was implemented in December 1989.
+% Version 1.1 fixed problems of strict Pascal (April 1990).
+% Version 1.2 fixed various bugs found by Peter Breitenlohner (September 1990).
+% Version 1.3 made out_as_fix tell the truth in overflow cases (Dec 2002).
+% Version 1.4 reports out-of-range characters (Breitenlohner, Jan 2014),
+% and tests nl>lig_size not 4*lig_size (C. M. Connelly, Melissa O'Neill).
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+
+\def\title{VF\lowercase{to}VP}
+\def\contentspagenumber{101}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont VFtoVP} processor}
+ \vskip 15pt
+ \centerline{(Version 1.4, January 2014)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this program
+ was supported in part by the National Science
+ Foundation and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+The \.{VFtoVP} utility program converts a virtual font (``\.{VF}'') file
+and its associated \TeX\ font metric (``\.{TFM}'')
+file into an equivalent virtual-property-list (``\.{VPL}'') file. It also
+makes a thorough check of the given files, using algorithms that are
+essentially the same as those used by
+\.{DVI} device drivers and by \TeX. Thus if \TeX\ or a \.{DVI} driver
+complains that a \.{TFM} or \.{VF}
+file is ``bad,'' this program will pinpoint the source or sources of
+badness. A \.{VPL} file output by this program can be edited with
+a normal text editor, and the result can be converted back to \.{VF} and \.{TFM}
+format using the companion program \.{VPtoVF}.
+
+\indent\.{VFtoVP} is an extended version of the program \.{TFtoPL}, which
+is part of the standard \TeX ware library.
+The idea of a virtual font was inspired by the work of David R. Fuchs
+@^Fuchs, David Raymond@>
+who designed a similar set of conventions in 1984 while developing a
+device driver for ArborText, Inc. He wrote a somewhat similar program
+called \.{AMFtoXPL}.
+
+The |banner| string defined here should be changed whenever \.{VFtoVP}
+gets modified.
+
+@d banner=='This is VFtoVP, Version 1.4' {printed when the program starts}
+
+@ This program is written entirely in standard \PASCAL, except that
+it occasionally has lower case letters in strings that are output.
+Such letters can be converted to upper case if necessary. The input is read
+from |vf_file| and |tfm_file|; the output is written on |vpl_file|.
+Error messages and
+other remarks are written on the |output| file, which the user may
+choose to assign to the terminal if the system permits it.
+@^system dependencies@>
+
+The term |print| is used instead of |write| when this program writes on
+the |output| file, so that all such output can be easily deflected.
+
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+
+@p program VFtoVP(@!vf_file,@!tfm_file,@!vpl_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var @!k:integer; {all-purpose index for initialization}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ If the program has to stop prematurely, it goes to the
+`|final_end|'.
+
+@d final_end=9999 {label for the end of it all}
+
+@<Labels...@>=final_end;
+
+@ The following parameters can be changed at compile time to extend or
+reduce \.{VFtoVP}'s capacity.
+
+@<Constants...@>=
+@!tfm_size=30000; {maximum length of |tfm| data, in bytes}
+@!vf_size=10000; {maximum length of |vf| data, in bytes}
+@!max_fonts=300; {maximum number of local fonts in the |vf| file}
+@!lig_size=5000; {maximum length of |lig_kern| program, in words}
+@!hash_size=5003; {preferably a prime number, a bit larger than the number
+ of character pairs in lig/kern steps}
+@!name_length=50; {a file name shouldn't be longer than this}
+@!max_stack=50; {maximum depth of \.{DVI} stack in character packets}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d do_nothing == {empty statement}
+@d exit=10 {go here to leave a procedure}
+@d not_found=45 {go here when you've found nothing}
+@d return==goto exit {terminate a procedure call}
+@f return==nil
+
+@<Types...@>=
+@!byte=0..255; {unsigned eight-bit quantity}
+
+@* Virtual fonts. The idea behind \.{VF} files is that a general
+interface mechanism is needed to switch between the myriad font
+layouts provided by different suppliers of typesetting equipment.
+Without such a mechanism, people must go to great lengths writing
+inscrutable macros whenever they want to use typesetting conventions
+based on one font layout in connection with actual fonts that have
+another layout. This puts an extra burden on the typesetting system,
+interfering with the other things it needs to do (like kerning,
+hyphenation, and ligature formation).
+
+These difficulties go away when we have a ``virtual font,''
+i.e., a font that exists in a logical sense but not a physical sense.
+A typesetting system like \TeX\ can do its job without knowing where the
+actual characters come from; a device driver can then do its job by
+letting a \.{VF} file tell what actual characters correspond to the
+characters \TeX\ imagined were present. The actual characters
+can be shifted and/or magnified and/or combined with other characters
+from many different fonts. A virtual font can even make use of characters
+from virtual fonts, including itself.
+
+Virtual fonts also allow convenient character substitutions for proofreading
+purposes, when fonts designed for one output device are unavailable on another.
+
+@ A \.{VF} file is organized as a stream of 8-bit bytes, using conventions
+borrowed from \.{DVI} and \.{PK} files. Thus, a device driver that knows
+about \.{DVI} and \.{PK} format will already
+contain most of the mechanisms necessary to process \.{VF} files.
+We shall assume that \.{DVI} format is understood; the conventions in the
+\.{DVI} documentation (see, for example, {\sl \TeX: The Program}, part 31)
+are adopted here to define \.{VF} format.
+
+A preamble
+appears at the beginning, followed by a sequence of character definitions,
+followed by a postamble. More precisely, the first byte of every \.{VF} file
+must be the first byte of the following ``preamble command'':
+
+\yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]| |cs[4]| |ds[4]|.
+Here |i| is the identification byte of \.{VF}, currently 202. The string
+|x| is merely a comment, usually indicating the source of the \.{VF} file.
+Parameters |cs| and |ds| are respectively the check sum and the design size
+of the virtual font; they should match the first two words in the header of
+the \.{TFM} file, as described below.
+
+\yskip
+After the |pre| command, the preamble continues with font definitions;
+every font needed to specify ``actual'' characters in later
+\\{set\_char} commands is defined here. The font definitions are
+exactly the same in \.{VF} files as they are in \.{DVI} files, except
+that the scaled size |s| is relative and the design size |d| is absolute:
+
+\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<256|.
+
+\yskip\hang|@!fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<65536|.
+
+\yskip\hang|@!fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|@!fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
+
+\yskip\noindent
+These font numbers |k| are ``local''; they have no relation to font numbers
+defined in the \.{DVI} file that uses this virtual font. The dimension~|s|,
+which represents the scaled size of the local font being defined,
+is a |fix_word| relative to the design size of the virtual font.
+Thus if the local font is to be used at the same size
+as the design size of the virtual font itself, |s| will be the
+integer value $2^{20}$. The value of |s| must be positive and less than
+$2^{24}$ (thus less than 16 when considered as a |fix_word|).
+The dimension~|d| is a |fix_word| in units of printer's points; hence it
+is identical to the design size found in the corresponding \.{TFM} file.
+
+@d id_byte=202
+
+@<Glob...@>=
+@!vf_file:packed file of byte;
+
+@ The preamble is followed by zero or more character packets, where each
+character packet begins with a byte that is $<243$. Character packets have
+two formats, one long and one short:
+
+\yskip\hang|long_char| 242 |pl[4]| |cc[4]| |tfm[4]| |dvi[pl]|. This long form
+specifies a virtual character in the general case.
+
+\yskip\hang|short_char0..short_char241|
+|pl[1]| |cc[1]| |tfm[3]| |dvi[pl]|. This short form specifies a
+virtual character in the common case
+when |0<=pl<242| and |0<=cc<256| and $0\le|tfm|<2^{24}$.
+
+\yskip\noindent
+Here |pl| denotes the packet length following the |tfm| value; |cc| is
+the character code; and |tfm| is the character width copied from the
+\.{TFM} file for this virtual font. There should be at most one character
+packet having any given |cc| code.
+
+The |dvi| bytes are a sequence of complete \.{DVI} commands, properly
+nested with respect to |push| and |pop|. All \.{DVI} operations are
+permitted except |bop|, |eop|, and commands with opcodes |>=243|.
+Font selection commands (|fnt_num0| through |fnt4|) must refer to fonts
+defined in the preamble.
+
+Dimensions that appear in the \.{DVI} instructions are analogous to
+|fix_word| quantities; i.e., they are integer multiples of $2^{-20}$ times
+the design size of the virtual font. For example, if the virtual font
+has design size $10\,$pt, the \.{DVI} command to move down $5\,$pt
+would be a \\{down} instruction with parameter $2^{19}$. The virtual font
+itself might be used at a different size, say $12\,$pt; then that
+\\{down} instruction would move down $6\,$pt instead. Each dimension
+must be less than $2^{24}$ in absolute value.
+
+Device drivers processing \.{VF} files treat the sequences of |dvi| bytes
+as subroutines or macros, implicitly enclosing them with |push| and |pop|.
+Each subroutine begins with |w=x=y=z=0|, and with current font~|f| the
+number of the first-defined in the preamble (undefined if there's no
+such font). After the |dvi| commands have been
+performed, the |h| and~|v| position registers of \.{DVI} format and the
+current font~|f| are restored to their former values;
+then, if the subroutine has been invoked by a \\{set\_char} or \\{set}
+command, |h|~is increased by the \.{TFM} width
+(properly scaled)---just as if a simple character had been typeset.
+
+@d long_char=242 {\.{VF} command for general character packet}
+@d set_char_0=0 {\.{DVI} command to typeset character 0 and move right}
+@d set1=128 {typeset a character and move right}
+@d set_rule=132 {typeset a rule and move right}
+@d put1=133 {typeset a character}
+@d put_rule=137 {typeset a rule}
+@d nop=138 {no operation}
+@d push=141 {save the current positions}
+@d pop=142 {restore previous positions}
+@d right1=143 {move right}
+@d w0=147 {move right by |w|}
+@d w1=148 {move right and set |w|}
+@d x0=152 {move right by |x|}
+@d x1=153 {move right and set |x|}
+@d down1=157 {move down}
+@d y0=161 {move down by |y|}
+@d y1=162 {move down and set |y|}
+@d z0=166 {move down by |z|}
+@d z1=167 {move down and set |z|}
+@d fnt_num_0=171 {set current font to 0}
+@d fnt1=235 {set current font}
+@d xxx1=239 {extension to \.{DVI} primitives}
+@d xxx4=242 {potentially long extension to \.{DVI} primitives}
+@d fnt_def1=243 {define the meaning of a font number}
+@d pre=247 {preamble}
+@d post=248 {postamble beginning}
+@d improper_DVI_for_VF==139,140,243,244,245,246,247,248,249,250,251,252,
+ 253,254,255
+
+@ The character packets are followed by a trivial postamble, consisting of
+one or more bytes all equal to |post| (248). The total number of bytes
+in the file should be a multiple of~4.
+
+@* Font metric data.
+The idea behind \.{TFM} files is that typesetting routines like \TeX\
+need a compact way to store the relevant information about several
+dozen fonts, and computer centers need a compact way to store the
+relevant information about several hundred fonts. \.{TFM} files are
+compact, and most of the information they contain is highly relevant,
+so they provide a solution to the problem.
+
+The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
+Since the number of bytes is always a multiple of 4, we could
+also regard the file as a sequence of 32-bit words; but \TeX\ uses the
+byte interpretation, and so does \.{VFtoVP}. Note that the bytes
+are considered to be unsigned numbers.
+
+@<Glob...@>=
+@!tfm_file:packed file of byte;
+
+@ On some systems you may have to do something special to read a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+reset(tfm_file); reset(vf_file);
+
+@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
+integers that give the lengths of the various subsequent portions
+of the file. These twelve integers are, in order:
+$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
+|@!lf|&length of the entire file, in words;\cr
+|@!lh|&length of the header data, in words;\cr
+|@!bc|&smallest character code in the font;\cr
+|@!ec|&largest character code in the font;\cr
+|@!nw|&number of words in the width table;\cr
+|@!nh|&number of words in the height table;\cr
+|@!nd|&number of words in the depth table;\cr
+|@!ni|&number of words in the italic correction table;\cr
+|@!nl|&number of words in the lig/kern table;\cr
+|@!nk|&number of words in the kern table;\cr
+|@!ne|&number of words in the extensible character table;\cr
+|@!np|&number of font parameter words.\cr}}$$
+They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
+|ne<=256|, and
+$$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
+Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
+and as few as 0 characters (if |bc=ec+1|).
+
+Incidentally, when two or more 8-bit bytes are combined to form an integer of
+16 or more bits, the most significant bytes appear first in the file.
+This is called BigEndian order.
+
+@<Glob...@>=
+@!lf,@!lh,@!bc,@!ec,@!nw,@!nh,@!nd,@!ni,@!nl,@!nk,@!ne,@!np:0..@'77777;
+ {subfile sizes}
+
+@ The rest of the \.{TFM} file may be regarded as a sequence of ten data
+arrays having the informal specification
+$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
+\vbox{\halign{\hfil\\{#}&$\,:\,$\arr#\hfil\cr
+header&|[0..lh-1]stuff|\cr
+char\_info&|[bc..ec]char_info_word|\cr
+width&|[0..nw-1]fix_word|\cr
+height&|[0..nh-1]fix_word|\cr
+depth&|[0..nd-1]fix_word|\cr
+italic&|[0..ni-1]fix_word|\cr
+lig\_kern&|[0..nl-1]lig_kern_command|\cr
+kern&|[0..nk-1]fix_word|\cr
+exten&|[0..ne-1]extensible_recipe|\cr
+param&|[1..np]fix_word|\cr}}$$
+The most important data type used here is a |@!fix_word|, which is
+a 32-bit representation of a binary fraction. A |fix_word| is a signed
+quantity, with the two's complement of the entire word used to represent
+negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
+binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
+the smallest is $-2048$. We will see below, however, that all but one of
+the |fix_word| values will lie between $-16$ and $+16$.
+
+@ The first data array is a block of header information, which contains
+general facts about the font. The header must contain at least two words,
+and for \.{TFM} files to be used with Xerox printing software it must
+contain at least 18 words, allocated as described below. When different
+kinds of devices need to be interfaced, it may be necessary to add further
+words to the header block.
+
+\yskip\hang|header[0]| is a 32-bit check sum that \TeX\ will copy into the
+\.{DVI} output file whenever it uses the font. Later on when the \.{DVI}
+file is printed, possibly on another computer, the actual font that gets
+used is supposed to have a check sum that agrees with the one in the
+\.{TFM} file used by \TeX. In this way, users will be warned about
+potential incompatibilities. (However, if the check sum is zero in either
+the font file or the \.{TFM} file, no check is made.) The actual relation
+between this check sum and the rest of the \.{TFM} file is not important;
+the check sum is simply an identification number with the property that
+incompatible fonts almost always have distinct check sums.
+@^check sum@>
+
+\yskip\hang|header[1]| is a |fix_word| containing the design size of the
+font, in units of \TeX\ points (7227 \TeX\ points = 254 cm). This number
+must be at least 1.0; it is fairly arbitrary, but usually the design size
+is 10.0 for a ``10 point'' font, i.e., a font that was designed to look
+best at a 10-point size, whatever that really means. When a \TeX\ user
+asks for a font `\.{at} $\delta$ \.{pt}', the effect is to override the
+design size and replace it by $\delta$, and to multiply the $x$ and~$y$
+coordinates of the points in the font image by a factor of $\delta$
+divided by the design size. {\sl All other dimensions in the\/\ \.{TFM}
+file are |fix_word|\kern-1pt\ numbers in design-size units.} Thus, for example,
+the value of |param[6]|, one \.{em} or \.{\\quad}, is often the |fix_word|
+value $2^{20}=1.0$, since many fonts have a design size equal to one em.
+The other dimensions must be less than 16 design-size units in absolute
+value; thus, |header[1]| and |param[1]| are the only |fix_word| entries in
+the whole \.{TFM} file whose first byte might be something besides 0 or
+255. @^design size@>
+
+\yskip\hang|header[2..11]|, if present, contains 40 bytes that identify
+the character coding scheme. The first byte, which must be between 0 and
+39, is the number of subsequent ASCII bytes actually relevant in this
+string, which is intended to specify what character-code-to-symbol
+convention is present in the font. Examples are \.{ASCII} for standard
+ASCII, \.{TeX text} for fonts like \.{cmr10} and \.{cmti9}, \.{TeX math
+extension} for \.{cmex10}, \.{XEROX text} for Xerox fonts, \.{GRAPHIC} for
+special-purpose non-alphabetic fonts, \.{UNSPECIFIED} for the default case
+when there is no information. Parentheses should not appear in this name.
+(Such a string is said to be in {\mc BCPL} format.)
+@^coding scheme@>
+
+\yskip\hang|header[12..16]|, if present, contains 20 bytes that name the
+font family (e.g., \.{CMR} or \.{HELVETICA}), in {\mc BCPL} format.
+This field is also known as the ``font identifier.''
+@^family name@>
+@^font identifier@>
+
+\yskip\hang|header[17]|, if present, contains a first byte called the
+|seven_bit_safe_flag|, then two bytes that are ignored, and a fourth byte
+called the |face|. If the value of the fourth byte is less than 18, it has
+the following interpretation as a ``weight, slope, and expansion'': Add 0
+or 2 or 4 (for medium or bold or light) to 0 or 1 (for roman or italic) to
+0 or 6 or 12 (for regular or condensed or extended). For example, 13 is
+0+1+12, so it represents medium italic extended. A three-letter code
+(e.g., \.{MIE}) can be used for such |face| data.
+
+\yskip\hang|header[18..@twhatever@>]| might also be present; the individual
+words are simply called |header[18]|, |header[19]|, etc., at the moment.
+
+@ Next comes the |char_info| array, which contains one |char_info_word|
+per character. Each |char_info_word| contains six fields packed into
+four bytes as follows.
+
+\yskip\hang first byte: |width_index| (8 bits)\par
+\hang second byte: |height_index| (4 bits) times 16, plus |depth_index|
+ (4~bits)\par
+\hang third byte: |italic_index| (6 bits) times 4, plus |tag|
+ (2~bits)\par
+\hang fourth byte: |remainder| (8 bits)\par
+\yskip\noindent
+The actual width of a character is |width[width_index]|, in design-size
+units; this is a device for compressing information, since many characters
+have the same width. Since it is quite common for many characters
+to have the same height, depth, or italic correction, the \.{TFM} format
+imposes a limit of 16 different heights, 16 different depths, and
+64 different italic corrections.
+
+Incidentally, the relation |width[0]=height[0]=depth[0]=italic[0]=0|
+should always hold, so that an index of zero implies a value of zero.
+The |width_index| should never be zero unless the character does
+not exist in the font, since a character is valid if and only if it lies
+between |bc| and |ec| and has a nonzero |width_index|.
+
+@ The |tag| field in a |char_info_word| has four values that explain how to
+interpret the |remainder| field.
+
+\yskip\hang|tag=0| (|no_tag|) means that |remainder| is unused.\par
+\hang|tag=1| (|lig_tag|) means that this character has a ligature/kerning
+program starting at |lig_kern[remainder]|.\par
+\hang|tag=2| (|list_tag|) means that this character is part of a chain of
+characters of ascending sizes, and not the largest in the chain. The
+|remainder| field gives the character code of the next larger character.\par
+\hang|tag=3| (|ext_tag|) means that this character code represents an
+extensible character, i.e., a character that is built up of smaller pieces
+so that it can be made arbitrarily large. The pieces are specified in
+|exten[remainder]|.\par
+
+@d no_tag=0 {vanilla character}
+@d lig_tag=1 {character has a ligature/kerning program}
+@d list_tag=2 {character has a successor in a charlist}
+@d ext_tag=3 {character is extensible}
+
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word is a
+|lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+ step if the byte is 128 or more, otherwise the next step is obtained by
+ skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+ then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+ a kern step otherwise.\par
+\hang fourth byte: |remainder|.\par
+\yskip\noindent
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|remainder| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+Notice that if $a=0$ and $b=1$, the current character is unchanged; if
+$a=b$ and $c=1$, the current character is changed but the next character is
+unchanged. \.{VFtoVP} will check to see that infinite loops are avoided.
+
+If the very first instruction of the |lig_kern| array has |skip_byte=255|,
+the |next_char| byte is the so-called right boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte=255|,
+there is a special ligature/kerning program for a left boundary character,
+beginning at location |256*op_byte+remainder|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte>128|, the program actually begins in location
+|256*op_byte+remainder|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location |<=255|.
+
+Any instruction with |skip_byte>128| in the |lig_kern| array must have
+|256*op_byte+remainder<nl|. If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+command is performed.
+
+@d stop_flag=128 {value indicating `\.{STOP}' in a lig/kern program}
+@d kern_flag=128 {op code for a kern step}
+
+@ Extensible characters are specified by an |extensible_recipe|,
+which consists of four bytes called |top|, |mid|,
+|bot|, and |rep| (in this order). These bytes are the character codes
+of individual pieces used to build up a large symbol.
+If |top|, |mid|, or |bot| are zero,
+they are not present in the built-up result. For example, an extensible
+vertical line is like an extensible bracket, except that the top and
+bottom pieces are missing.
+
+
+@ The final portion of a \.{TFM} file is the |param| array, which is another
+sequence of |fix_word| values.
+
+\yskip\hang|param[1]=@!slant| is the amount of italic slant, which is used
+to help position accents. For example, |slant=.25| means that when you go
+up one unit, you also go .25 units to the right. The |slant| is a pure
+number; it's the only |fix_word| other than the design size itself that is
+not scaled by the design size.
+
+\hang|param[2]=space| is the normal spacing between words in text.
+Note that character |" "| in the font need not have anything to do with
+blank spaces.
+
+\hang|param[3]=space_stretch| is the amount of glue stretching between words.
+
+\hang|param[4]=space_shrink| is the amount of glue shrinking between words.
+
+\hang|param[5]=x_height| is the height of letters for which accents don't
+have to be raised or lowered.
+
+\hang|param[6]=quad| is the size of one em in the font.
+
+\hang|param[7]=extra_space| is the amount added to |param[2]| at the
+ends of sentences.
+
+When the character coding scheme is \.{TeX math symbols}, the font is
+supposed to have 15 additional parameters called |num1|, |num2|, |num3|,
+|denom1|, |denom2|, |sup1|, |sup2|, |sup3|, |sub1|, |sub2|, |supdrop|,
+|subdrop|, |delim1|, |delim2|, and |axis_height|, respectively. When the
+character coding scheme is \.{TeX math extension}, the font is supposed to
+have six additional parameters called |default_rule_thickness| and
+|big_op_spacing1| through |big_op_spacing5|.
+
+@ So that is what \.{TFM} files hold. The next question is, ``What about
+\.{VPL} files?'' A complete answer to that question appears in the
+documentation of the companion program, \.{VPtoVF}, so it will not
+be repeated here. Suffice it to say that a \.{VPL} file is an ordinary
+\PASCAL\ text file, and that the output of \.{VFtoVP} uses only a
+subset of the possible constructions that might appear in a \.{VPL} file.
+Furthermore, hardly anybody really wants to look at the formal
+definition of \.{VPL} format, because it is almost self-explanatory when
+you see an example or two.
+
+@<Glob...@>=
+@!vpl_file:text;
+
+@ @<Set init...@>=
+rewrite(vpl_file);
+
+@* Unpacking the TFM file.
+The first thing \.{VFtoVP} does is read the entire |tfm_file| into an array of
+bytes, |tfm[0..(4*lf-1)]|.
+
+@<Types...@>=
+@!index=0..tfm_size; {address of a byte in |tfm|}
+
+@ @<Glob...@>=
+@!tfm:array [-1000..tfm_size] of byte; {the \.{TFM} input data all goes here}
+ {the negative addresses avoid range checks for invalid characters}
+
+@ The input may, of course, be all screwed up and not a \.{TFM} file
+at all. So we begin cautiously.
+
+@d abort(#)==begin print_ln(#);
+ print_ln('Sorry, but I can''t go on; are you sure this is a TFM?');
+ goto final_end;
+ end
+
+@<Read the whole \.{TFM} file@>=
+read(tfm_file,tfm[0]);
+if tfm[0]>127 then abort('The first byte of the input file exceeds 127!');
+@.The first byte...@>
+if eof(tfm_file) then abort('The input file is only one byte long!');
+@.The input...one byte long@>
+read(tfm_file,tfm[1]); lf:=tfm[0]*@'400+tfm[1];
+if lf=0 then
+ abort('The file claims to have length zero, but that''s impossible!');
+@.The file claims...@>
+if 4*lf-1>tfm_size then abort('The file is bigger than I can handle!');
+@.The file is bigger...@>
+for tfm_ptr:=2 to 4*lf-1 do
+ begin if eof(tfm_file) then
+ abort('The file has fewer bytes than it claims!');
+@.The file has fewer bytes...@>
+ read(tfm_file,tfm[tfm_ptr]);
+ end;
+if not eof(tfm_file) then
+ begin print_ln('There''s some extra junk at the end of the TFM file,');
+@.There's some extra junk...@>
+ print_ln('but I''ll proceed as if it weren''t there.');
+ end
+
+@ After the file has been read successfully, we look at the subfile sizes
+to see if they check out.
+
+@d eval_two_bytes(#)==begin if tfm[tfm_ptr]>127 then
+ abort('One of the subfile sizes is negative!');
+@.One of the subfile sizes...@>
+ #:=tfm[tfm_ptr]*@'400+tfm[tfm_ptr+1];
+ tfm_ptr:=tfm_ptr+2;
+ end
+
+@<Set subfile sizes |lh|, |bc|, \dots, |np|@>=
+begin tfm_ptr:=2;@/
+eval_two_bytes(lh);
+eval_two_bytes(bc);
+eval_two_bytes(ec);
+eval_two_bytes(nw);
+eval_two_bytes(nh);
+eval_two_bytes(nd);
+eval_two_bytes(ni);
+eval_two_bytes(nl);
+eval_two_bytes(nk);
+eval_two_bytes(ne);
+eval_two_bytes(np);
+if lh<2 then abort('The header length is only ',lh:1,'!');
+@.The header length...@>
+if nl>lig_size then
+ abort('The lig/kern program is longer than I can handle!');
+@.The lig/kern program...@>
+if (bc>ec+1)or(ec>255) then abort('The character code range ',
+@.The character code range...@>
+ bc:1,'..',ec:1,' is illegal!');
+if (nw=0)or(nh=0)or(nd=0)or(ni=0) then
+ abort('Incomplete subfiles for character dimensions!');
+@.Incomplete subfiles...@>
+if ne>256 then abort('There are ',ne:1,' extensible recipes!');
+@.There are ... recipes@>
+if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then
+ abort('Subfile sizes don''t add up to the stated total!');
+@.Subfile sizes don't add up...@>
+end
+
+@ Once the input data successfully passes these basic checks,
+\.{VFtoVP} believes that it is a \.{TFM} file, and the conversion
+to \.{VPL} format will take place. Access to the various subfiles
+is facilitated by computing the following base addresses. For example,
+the |char_info| for character |c| will start in location
+|4*(char_base+c)| of the |tfm| array.
+
+@<Globals...@>=
+@!char_base,@!width_base,@!height_base,@!depth_base,@!italic_base,
+@!lig_kern_base,@!kern_base,@!exten_base,@!param_base:integer;
+ {base addresses for the subfiles}
+
+@ @<Compute the base addresses@>=
+begin char_base:=6+lh-bc;
+width_base:=char_base+ec+1;
+height_base:=width_base+nw;
+depth_base:=height_base+nh;
+italic_base:=depth_base+nd;
+lig_kern_base:=italic_base+ni;
+kern_base:=lig_kern_base+nl;
+exten_base:=kern_base+nk;
+param_base:=exten_base+ne-1;
+end
+
+@ Of course we want to define macros that suppress the detail of how the
+font information is actually encoded. Each word will be referred to by
+the |tfm| index of its first byte. For example, if |c| is a character
+code between |bc| and |ec|, then |tfm[char_info(c)]| will be the
+first byte of its |char_info|, i.e., the |width_index|; furthermore
+|width(c)| will point to the |fix_word| for |c|'s width.
+
+@d check_sum=24
+@d design_size=check_sum+4
+@d scheme=design_size+4
+@d family=scheme+40
+@d random_word=family+20
+@d char_info(#)==4*(char_base+#)
+@d width_index(#)==tfm[char_info(#)]
+@d nonexistent(#)==((#<bc)or(#>ec)or(width_index(#)=0))
+@d height_index(#)==(tfm[char_info(#)+1] div 16)
+@d depth_index(#)==(tfm[char_info(#)+1] mod 16)
+@d italic_index(#)==(tfm[char_info(#)+2] div 4)
+@d tag(#)==(tfm[char_info(#)+2] mod 4)
+@d reset_tag(#)==tfm[char_info(#)+2]:=4*italic_index(#)+no_tag
+@d remainder(#)==tfm[char_info(#)+3]
+@d width(#)==4*(width_base+width_index(#))
+@d height(#)==4*(height_base+height_index(#))
+@d depth(#)==4*(depth_base+depth_index(#))
+@d italic(#)==4*(italic_base+italic_index(#))
+@d exten(#)==4*(exten_base+remainder(#))
+@d lig_step(#)==4*(lig_kern_base+(#))
+@d kern(#)==4*(kern_base+#) {here \#\ is an index, not a character}
+@d param(#)==4*(param_base+#) {likewise}
+
+@ One of the things we would like to do is take cognizance of fonts whose
+character coding scheme is \.{TeX math symbols} or \.{TeX math extension};
+we will set the |font_type| variable to one of the three choices
+|vanilla|, |mathsy|, or |mathex|.
+
+@d vanilla=0 {not a special scheme}
+@d mathsy=1 {\.{TeX math symbols} scheme}
+@d mathex=2 {\.{TeX math extension} scheme}
+
+@<Glob...@>=
+@!font_type:vanilla..mathex; {is this font special?}
+
+@* Unpacking the VF file.
+Once the \.{TFM} file has been brought into memory, \.{VFtoVP} completes
+the input phase by reading the \.{VF} information into another array of bytes.
+In this case we don't store all the data; we check the redundant bytes
+for consistency with their \.{TFM} counterparts, and we partially decode
+the packets.
+
+@<Glob...@>=
+@!vf:array[0..vf_size] of byte; {the \.{VF} input data goes here}
+@!font_number:array[0..max_fonts] of integer; {local font numbers}
+@!font_start,@!font_chars:array[0..max_fonts] of 0..vf_size; {font info}
+@!font_ptr:0..max_fonts; {number of local fonts}
+@!packet_start,@!packet_end:array[byte] of 0..vf_size;
+ {character packet boundaries}
+@!packet_found:boolean; {at least one packet has appeared}
+@!temp_byte:byte;@+@!count:integer; {registers for simple calculations}
+@!real_dsize:real; {the design size, converted to floating point}
+@!pl:integer; {packet length}
+@!vf_ptr:0..vf_size; {first unused location in |vf|}
+@!vf_count:integer; {number of bytes read from |vf_file|}
+
+@ Again we cautiously verify that we've been given decent data.
+
+@d read_vf(#)==read(vf_file,#)
+@d vf_abort(#)==
+ begin print_ln(#);
+ print_ln('Sorry, but I can''t go on; are you sure this is a VF?');
+ goto final_end;
+ end
+
+@<Read the whole \.{VF} file@>=
+read_vf(temp_byte);
+if temp_byte<>pre then vf_abort('The first byte isn''t `pre''!');
+@.The first byte...@>
+@<Read the preamble command@>;
+@<Read and store the font definitions and character packets@>;
+@<Read and verify the postamble@>
+
+@ @d vf_store(#)==@t@>@;@/
+ if vf_ptr+#>=vf_size then vf_abort('The file is bigger than I can handle!');
+@.The file is bigger...@>
+ for k:=vf_ptr to vf_ptr+#-1 do
+ begin if eof(vf_file) then vf_abort('The file ended prematurely!');
+@.The file ended prematurely@>
+ read_vf(vf[k]);
+ end;
+ vf_count:=vf_count+#; vf_ptr:=vf_ptr+#
+
+@<Read the preamble command@>=
+if eof(vf_file) then vf_abort('The input file is only one byte long!');
+@.The input...one byte long@>
+read_vf(temp_byte);
+if temp_byte<>id_byte then vf_abort('Wrong VF version number in second byte!');
+@.Wrong VF version...@>
+if eof(vf_file) then vf_abort('The input file is only two bytes long!');
+read_vf(temp_byte); {read the length of introductory comment}
+vf_count:=11; vf_ptr:=0; vf_store(temp_byte);
+for k:=0 to vf_ptr-1 do print(xchr[vf[k]]);
+print_ln(' '); count:=0;
+for k:=0 to 7 do
+ begin if eof(vf_file) then vf_abort('The file ended prematurely!');
+@.The file ended prematurely@>
+ read_vf(temp_byte);
+ if temp_byte=tfm[check_sum+k] then incr(count);
+ end;
+real_dsize:=(((tfm[design_size]*256+tfm[design_size+1])*256+tfm[design_size+2])
+ *256+tfm[design_size+3])/@'4000000;
+if count<>8 then
+ begin print_ln('Check sum and/or design size mismatch.');
+@.Check sum...mismatch@>
+ print_ln('Data from TFM file will be assumed correct.');
+ end
+
+@ @<Read and store the font definitions and character packets@>=
+for k:=0 to 255 do packet_start[k]:=vf_size;
+font_ptr:=0; packet_found:=false; font_start[0]:=vf_ptr;
+repeat if eof(vf_file) then
+ begin print_ln('File ended without a postamble!'); temp_byte:=post;
+@.File ended without a postamble@>
+ end
+else begin read_vf(temp_byte); incr(vf_count);
+ if temp_byte<>post then
+ if temp_byte>long_char then @<Read and store a font definition@>
+ else @<Read and store a character packet@>;
+ end;
+until temp_byte=post
+
+@ @<Read and verify the postamble@>=
+while (temp_byte=post)and not eof(vf_file) do
+ begin read_vf(temp_byte); incr(vf_count);
+ end;
+if not eof(vf_file) then
+ begin print_ln('There''s some extra junk at the end of the VF file.');
+@.There's some extra junk...@>
+ print_ln('I''ll proceed as if it weren''t there.');
+ end;
+if vf_count mod 4 <> 0 then
+ print_ln('VF data not a multiple of 4 bytes')
+@.VF data not a multiple of 4 bytes@>
+
+@ @<Read and store a font definition@>=
+begin if packet_found or(temp_byte>=pre) then
+ vf_abort('Illegal byte ',temp_byte:1,' at beginning of character packet!');
+@.Illegal byte...@>
+font_number[font_ptr]:=vf_read(temp_byte-fnt_def1+1);
+if font_ptr=max_fonts then vf_abort('I can''t handle that many fonts!');
+@.I can't handle that many fonts@>
+vf_store(14); {|c[4]| |s[4]| |d[4]| |a[1]| |l[1]|}
+if vf[vf_ptr-10]>0 then {|s| is negative or exceeds $2^{24}-1$}
+ vf_abort('Mapped font size is too big!');
+@.Mapped font size...big@>
+a:=vf[vf_ptr-2]; l:=vf[vf_ptr-1]; vf_store(a+l); {|n[a+l]|}
+@<Print the name of the local font@>;
+@<Read the local font's \.{TFM} file and record the characters it contains@>;
+incr(font_ptr); font_start[font_ptr]:=vf_ptr;
+end
+
+@ The font area may need to be separated from the font name on some systems.
+Here we simply reproduce the font area and font name (with no space
+or punctuation between them).
+@^system dependencies@>
+
+@<Print the name...@>=
+print('MAPFONT ',font_ptr:1,': ');
+for k:=font_start[font_ptr]+14 to vf_ptr-1 do print(xchr[vf[k]]);
+k:=font_start[font_ptr]+5;
+print_ln(' at ',(((vf[k]*256+vf[k+1])*256+vf[k+2])/@'4000000)*real_dsize:2:2,
+ 'pt')
+
+@ Now we must read in another \.{TFM} file. But this time we needn't be so
+careful, because we merely want to discover which characters are present.
+The next few sections of the program are copied pretty much verbatim from
+\.{DVItype}, so that system-dependent modifications can be copied from existing
+software.
+
+It turns out to be convenient to read four bytes at a time, when we are
+inputting from the local \.{TFM} files. The input goes into global variables
+|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
+the fourth.
+
+@<Glob...@>=
+@!a:integer; {length of the area/directory spec}
+@!l:integer; {length of the font name proper}
+@!cur_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+@!b0,@!b1,@!b2,@!b3: byte; {four bytes input at once}
+@!font_lh:0..@'77777; {header length of current local font}
+@!font_bc,@!font_ec:0..@'77777; {character range of current local font}
+
+@ The |read_tfm_word| procedure sets |b0| through |b3| to the next
+four bytes in the current \.{TFM} file.
+@^system dependencies@>
+
+@d read_tfm(#)==if eof(tfm_file) then #:=0@+else read(tfm_file,#)
+
+@p procedure read_tfm_word;
+begin read_tfm(b0); read_tfm(b1);
+read_tfm(b2); read_tfm(b3);
+end;
+
+@ We use the |vf| array to store a list of all valid characters in the
+local font, beginning at location |font_chars[f]|.
+
+@<Read the local font's \.{TFM} file...@>=
+font_chars[font_ptr]:=vf_ptr;
+@<Move font name into the |cur_name| string@>;
+reset(tfm_file,cur_name);
+@^system dependencies@>
+if eof(tfm_file) then
+ print_ln('---not loaded, TFM file can''t be opened!')
+@.TFM file can\'t be opened@>
+else begin font_bc:=0; font_ec:=256; {will cause error if not modified soon}
+ read_tfm_word;
+ if b2<128 then
+ begin font_lh:=b2*256+b3; read_tfm_word;
+ if (b0<128) and (b2<128) then
+ begin font_bc:=b0*256+b1; font_ec:=b2*256+b3;
+ end;
+ end;
+ if font_bc<=font_ec then
+ if font_ec>255 then print_ln('---not loaded, bad TFM file!')
+@.bad TFM file@>
+ else begin for k:=0 to 3+font_lh do
+ begin read_tfm_word;
+ if k=4 then @<Check the check sum@>;
+ if k=5 then @<Check the design size@>;
+ end;
+ for k:=font_bc to font_ec do
+ begin read_tfm_word;
+ if b0>0 then {character |k| exists in the font}
+ begin vf[vf_ptr]:=k; incr(vf_ptr);
+ if vf_ptr=vf_size then vf_abort('I''m out of VF memory!');
+@.I'm out of VF memory@>
+ end;
+ end;
+ end;
+ if eof(tfm_file) then
+ print_ln('---trouble is brewing, TFM file ended too soon!');
+@.trouble is brewing...@>
+ end;
+incr(vf_ptr) {leave space for character search later}
+
+@ @<Check the check sum@>=
+if b0+b1+b2+b3>0 then
+ if(b0<>vf[font_start[font_ptr]])or@|
+ (b1<>vf[font_start[font_ptr]+1])or@|
+ (b2<>vf[font_start[font_ptr]+2])or@|
+ (b3<>vf[font_start[font_ptr]+3]) then
+ begin print_ln('Check sum in VF file being replaced by TFM check sum');
+@.Check sum...replaced...@>
+ vf[font_start[font_ptr]]:=b0;
+ vf[font_start[font_ptr]+1]:=b1;
+ vf[font_start[font_ptr]+2]:=b2;
+ vf[font_start[font_ptr]+3]:=b3;
+ end
+
+@ @<Check the design size@>=
+if(b0<>vf[font_start[font_ptr]+8])or@|
+ (b1<>vf[font_start[font_ptr]+9])or@|
+ (b2<>vf[font_start[font_ptr]+10])or@|
+ (b3<>vf[font_start[font_ptr]+11]) then
+ begin print_ln('Design size in VF file being replaced by TFM design size');
+@.Design size...replaced...@>
+ vf[font_start[font_ptr]+8]:=b0;
+ vf[font_start[font_ptr]+9]:=b1;
+ vf[font_start[font_ptr]+10]:=b2;
+ vf[font_start[font_ptr]+11]:=b3;
+ end
+
+@ If no font directory has been specified, \.{DVI}-reading software
+is supposed to use the default font directory, which is a
+system-dependent place where the standard fonts are kept.
+The string variable |default_directory| contains the name of this area.
+@^system dependencies@>
+
+@d default_directory_name=='TeXfonts:' {change this to the correct name}
+@d default_directory_name_length=9 {change this to the correct length}
+
+@<Glob...@>=
+@!default_directory:packed array[1..default_directory_name_length] of char;
+
+@ @<Set init...@>=
+default_directory:=default_directory_name;
+
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font. This usually means that we need to
+prepend the name of the default directory, and
+to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
+to upper case, since |cur_name| is a \PASCAL\ string.
+@^system dependencies@>
+
+@<Move font name into the |cur_name| string@>=
+for k:=1 to name_length do cur_name[k]:=' ';
+if a=0 then
+ begin for k:=1 to default_directory_name_length do
+ cur_name[k]:=default_directory[k];
+ r:=default_directory_name_length;
+ end
+else r:=0;
+for k:=font_start[font_ptr]+14 to vf_ptr-1 do
+ begin incr(r);
+ if r+4>name_length then vf_abort('Font name too long for me!');
+@.Font name too long for me@>
+ if (vf[k]>="a")and(vf[k]<="z") then
+ cur_name[r]:=xchr[vf[k]-@'40]
+ else cur_name[r]:=xchr[vf[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='T'; cur_name[r+3]:='F'; cur_name[r+4]:='M'
+
+
+@ It's convenient to have a subroutine
+that reads a |k|-byte number from |vf_file|.
+
+@d get_vf(#)==if eof(vf_file) then #:=0 @+else read_vf(#)
+
+@p function vf_read(@!k:integer):integer; {actually |1<=k<=4|}
+var @!b:byte; {input byte}
+@!a:integer; {accumulator}
+begin vf_count:=vf_count+k; get_vf(b); a:=b;
+if k=4 then if b>=128 then a:=a-256; {4-byte numbers are signed}
+while k>1 do
+ begin get_vf(b);
+ a:=256*a+b; decr(k);
+ end;
+vf_read:=a;
+end;
+
+@ The \.{VF} format supports arbitrary 4-byte character codes,
+but \.{VPL} format presently does not.
+Therefore we give up if the character code is
+not between 0 and~255.
+
+After more experience is gained with present-day \.{VPL} files, the
+best way to extend them to arbitrary character codes will become clear;
+the extensions to \.{VFtoVP} and \.{VPtoVF} should not be difficult.
+
+@<Read and store a character packet@>=
+begin if temp_byte=long_char then
+ begin pl:=vf_read(4); c:=vf_read(4); count:=vf_read(4);
+ {|pl[4]| |cc[4]| |tfm[4]|}
+ end
+else begin pl:=temp_byte; c:=vf_read(1); count:=vf_read(3);
+ {|pl[1]| |cc[1]| |tfm[3]|}
+ end;
+if nonexistent(c) then vf_abort('Character ',c:1,' does not exist!');
+@.Character c does not exist@>
+if packet_start[c]<vf_size then
+ print_ln('Discarding earlier packet for character ',c:1);
+@.Discarding earlier packet...@>
+if count<>tfm_width(c) then
+ print_ln('Incorrect TFM width for character ',c:1,' in VF file');
+@.Incorrect TFM width...@>
+if pl<0 then vf_abort('Negative packet length!');
+@.Negative packet length@>
+packet_start[c]:=vf_ptr; vf_store(pl); packet_end[c]:=vf_ptr-1;
+packet_found:=true;
+end
+
+@ The preceding code requires a simple subroutine that evaluates \.{TFM} data.
+
+@p function tfm_width(@!c:byte):integer;
+var @!a:integer; {accumulator}
+@!k:index; {index into |tfm|}
+begin k:=width(c); {we assume that character |c| exists}
+a:=tfm[k];
+if a>=128 then a:=a-256;
+tfm_width:=((256*a+tfm[k+1])*256+tfm[k+2])*256+tfm[k+3];
+end;
+
+@* Basic output subroutines.
+Let us now define some procedures that will reduce the rest of \.{VFtoVP}'s
+work to a triviality.
+
+First of all, it is convenient to have an abbreviation for output to the
+\.{VPL} file:
+
+@d out(#)==write(vpl_file,#)
+
+@ In order to stick to standard \PASCAL, we use an |xchr| array to do
+appropriate conversion of ASCII codes. Three other little strings are
+used to produce |face| codes like \.{MIE}.
+
+@<Glob...@>=
+@!ASCII_04,@!ASCII_10,@!ASCII_14: packed array [1..32] of char;
+ {strings for output in the user's external character set}
+@!xchr:packed array [0..255] of char;
+@!MBL_string,@!RI_string,@!RCE_string:packed array [1..3] of char;
+ {handy string constants for |face| codes}
+
+@ @<Set init...@>=
+ASCII_04:=' !"#$%&''()*+,-./0123456789:;<=>?';@/
+ASCII_10:='@@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_';@/
+ASCII_14:='`abcdefghijklmnopqrstuvwxyz{|}~?';@/
+for k:=0 to 255 do xchr[k]:='?';
+for k:=0 to @'37 do
+ begin xchr[k+@'40]:=ASCII_04[k+1];
+ xchr[k+@'100]:=ASCII_10[k+1];
+ xchr[k+@'140]:=ASCII_14[k+1];
+ end;
+MBL_string:='MBL'; RI_string:='RI '; RCE_string:='RCE';
+
+@ The array |dig| will hold a sequence of digits to be output.
+
+@<Glob...@>=
+@!dig:array[0..11] of 0..9;
+
+@ Here, in fact, are two procedures that output |dig[j-1]|$\,\ldots\,$|dig[0]|,
+given $j>0$.
+
+@p procedure out_digs(j:integer); {outputs |j| digits}
+begin repeat decr(j); out(dig[j]:1);
+until j=0;
+end;
+@#
+procedure print_digs(j:integer); {prints |j| digits}
+begin repeat decr(j); print(dig[j]:1);
+until j=0;
+end;
+
+@ The |print_octal| procedure indicates how |print_digs| can be used.
+Since this procedure is used only to print character codes, it always
+produces three digits.
+
+@p procedure print_octal(c:byte); {prints octal value of |c|}
+var j:0..2; {index into |dig|}
+begin print(''''); {an apostrophe indicates the octal notation}
+for j:=0 to 2 do
+ begin dig[j]:=c mod 8; c:=c div 8;
+ end;
+print_digs(3);
+end;
+
+@ A \.{VPL} file has nested parentheses, and we want to format the output
+so that its structure is clear. The |level| variable keeps track of the
+depth of nesting.
+
+@<Glob...@>=
+@!level:0..5;
+
+@ @<Set init...@>=
+level:=0;
+
+@ Three simple procedures suffice to produce the desired structure in the
+output.
+
+@p procedure out_ln; {finishes one line, indents the next}
+var l:0..5;
+begin write_ln(vpl_file);
+for l:=1 to level do out(' ');
+end;
+@#
+procedure left; {outputs a left parenthesis}
+begin incr(level); out('(');
+end;
+@#
+procedure right; {outputs a right parenthesis and finishes a line}
+begin decr(level); out(')'); out_ln;
+end;
+
+@ The value associated with a property can be output in a variety of
+ways. For example, we might want to output a {\mc BCPL} string that
+begins in |tfm[k]|:
+
+@p procedure out_BCPL(@!k:index); {outputs a string, preceded by a blank space}
+var l:0..39; {the number of bytes remaining}
+begin out(' '); l:=tfm[k];
+while l>0 do
+ begin incr(k); decr(l); out(xchr[tfm[k]]);
+ end;
+end;
+
+@ The property value might also be a sequence of |l| bytes, beginning
+in |tfm[k]|, that we would like to output in octal notation.
+The following procedure assumes that |l<=4|, but larger values of |l|
+could be handled easily by enlarging the |dig| array and increasing
+the upper bounds on |b| and |j|.
+
+@p procedure out_octal(@!k,@!l:index); {outputs |l| bytes in octal}
+var a:0..@'1777; {accumulator for bits not yet output}
+@!b:0..32; {the number of significant bits in |a|}
+@!j:0..11; {the number of digits of output}
+begin out(' O '); {specify octal format}
+a:=0; b:=0; j:=0;
+while l>0 do @<Reduce \(1)|l| by one, preserving the invariants@>;
+while (a>0)or(j=0) do
+ begin dig[j]:=a mod 8; a:=a div 8; incr(j);
+ end;
+out_digs(j);
+end;
+
+@ @<Reduce \(1)|l|...@>=
+begin decr(l);
+if tfm[k+l]<>0 then
+ begin while b>2 do
+ begin dig[j]:=a mod 8; a:=a div 8; b:=b-3; incr(j);
+ end;
+ case b of
+ 0: a:=tfm[k+l];
+ 1:a:=a+2*tfm[k+l];
+ 2:a:=a+4*tfm[k+l];
+ end;
+ end;
+b:=b+8;
+end
+
+@ The property value may be a character, which is output in octal
+unless it is a letter or a digit.
+@^system dependencies@>
+
+@p procedure out_char(@!c:byte); {outputs a character}
+begin if font_type>vanilla then
+ begin tfm[0]:=c; out_octal(0,1)
+ end
+else if ((c>="0")and(c<="9"))or@|
+ ((c>="A")and(c<="Z"))or@|
+ ((c>="a")and(c<="z")) then out(' C ',xchr[c])
+else begin tfm[0]:=c; out_octal(0,1);
+ end;
+end;
+
+@ The property value might be a ``face'' byte, which is output in the
+curious code mentioned earlier, provided that it is less than 18.
+
+@p procedure out_face(@!k:index); {outputs a |face|}
+var s:0..1; {the slope}
+@!b:0..8; {the weight and expansion}
+begin if tfm[k]>=18 then out_octal(k,1)
+else begin out(' F '); {specify face-code format}
+ s:=tfm[k] mod 2; b:=tfm[k] div 2;
+ out(MBL_string[1+(b mod 3)]);
+ out(RI_string[1+s]);
+ out(RCE_string[1+(b div 3)]);
+ end;
+end;
+
+@ And finally, the value might be a |fix_word|, which is output in
+decimal notation with just enough decimal places for \.{VPtoVF}
+to recover every bit of the given |fix_word|.
+
+All of the numbers involved in the intermediate calculations of
+this procedure will be nonnegative and less than $10\cdot2^{24}$.
+
+@p procedure out_fix(@!k:index); {outputs a |fix_word|}
+var a:0..@'7777; {accumulator for the integer part}
+@!f:integer; {accumulator for the fraction part}
+@!j:0..12; {index into |dig|}
+@!delta:integer; {amount if allowable inaccuracy}
+begin out(' R '); {specify real format}
+a:=(tfm[k]*16)+(tfm[k+1] div 16);
+f:=((tfm[k+1] mod 16)*@'400+tfm[k+2])*@'400+tfm[k+3];
+if a>@'3777 then @<Reduce \(2)negative to positive@>;
+@<Output the integer part, |a|, in decimal notation@>;
+@<Output the fraction part, $|f|/2^{20}$, in decimal notation@>;
+end;
+
+@ The following code outputs at least one digit even if |a=0|.
+
+@<Output the integer...@>=
+begin j:=0;
+repeat dig[j]:=a mod 10; a:=a div 10; incr(j);
+until a=0;
+out_digs(j);
+end
+
+@ And the following code outputs at least one digit to the right
+of the decimal point.
+
+@<Output the fraction...@>=
+begin out('.'); f:=10*f+5; delta:=10;
+repeat if delta>@'4000000 then f:=f+@'2000000-(delta div 2);
+out(f div @'4000000:1); f:=10*(f mod @'4000000); delta:=delta*10;
+until f<=delta;
+end;
+
+@ @<Reduce \(2)negative to positive@>=
+begin out('-'); a:=@'10000-a;
+if f>0 then
+ begin f:=@'4000000-f; decr(a);
+ end;
+end
+
+@* Outputting the TFM info.
+\TeX\ checks the information of a \.{TFM} file for validity as the
+file is being read in, so that no further checks will be needed when
+typesetting is going on. And when it finds something wrong, it justs
+calls the file ``bad,'' without identifying the nature of the problem,
+since \.{TFM} files are supposed to be good almost all of the time.
+
+Of course, a bad file shows up every now and again, and that's where
+\.{VFtoVP} comes in. This program wants to catch at least as many errors as
+\TeX\ does, and to give informative error messages besides.
+All of the errors are corrected, so that the \.{VPL} output will
+be correct (unless, of course, the \.{TFM} file was so loused up
+that no attempt is being made to fathom it).
+
+@ Just before each character is processed, its code is printed in octal
+notation. Up to eight such codes appear on a line; so we have a variable
+to keep track of how many are currently there. We also keep track of
+whether or not any errors have had to be corrected.
+
+@<Glob...@>=
+@!chars_on_line:0..8; {the number of characters printed on the current line}
+@!perfect:boolean; {was the file free of errors?}
+
+@ @<Set init...@>=
+chars_on_line:=0;@/
+perfect:=true; {innocent until proved guilty}
+
+@ Error messages are given with the help of the |bad| and |range_error|
+and |bad_char| macros:
+
+@d bad(#)==begin perfect:=false; if chars_on_line>0 then print_ln(' ');
+ chars_on_line:=0; print_ln('Bad TFM file: ',#);
+ end
+@.Bad TFM file@>
+@d range_error(#)==begin perfect:=false; print_ln(' ');
+ print(#,' index for character ');
+ print_octal(c); print_ln(' is too large;');
+ print_ln('so I reset it to zero.');
+ end
+@d bad_char_tail(#)==print_octal(#); print_ln('.');
+ end
+@d bad_char(#)==begin perfect:=false; if chars_on_line>0 then print_ln(' ');
+ chars_on_line:=0; print('Bad TFM file: ',#,' nonexistent character ');
+ bad_char_tail
+@d correct_bad_char_tail(#)==print_octal(tfm[#]); print_ln('.'); tfm[#]:=bc;
+ end
+@d correct_bad_char(#)== begin perfect:=false;
+ if chars_on_line>0 then print_ln(' ');
+ chars_on_line:=0; print('Bad TFM file: ',#,' nonexistent character ');
+ correct_bad_char_tail
+
+@<Glob...@>=
+@!i:0..@'77777; {an index to words of a subfile}
+@!c:0..256; {a random character}
+@!d:0..3; {byte number in a word}
+@!k:index; {a random index}
+@!r:0..65535; {a random two-byte value}
+
+@ There are a lot of simple things to do, and they have to be done one
+at a time, so we might as well get down to business. The first things
+that \.{VFtoVP} will put into the \.{VPL} file appear in the header part.
+
+@<Do the header@>=
+begin font_type:=vanilla;
+if lh>=12 then
+ begin @<Set the true |font_type|@>;
+ if lh>=17 then
+ begin @<Output the family name@>;
+ if lh>=18 then @<Output the rest of the header@>;
+ end;
+ @<Output the character coding scheme@>;
+ end;
+@<Output the design size@>;
+@<Output the check sum@>;
+@<Output the |seven_bit_safe_flag|@>;
+end
+
+@ @<Output the check sum@>=
+left; out('CHECKSUM'); out_octal(check_sum,4);
+right
+
+@ Incorrect design sizes are changed to 10 points.
+
+@d bad_design(#)==begin bad('Design size ',#,'!');
+@.Design size wrong@>
+ print_ln('I''ve set it to 10 points.');
+ out(' D 10');
+ end
+
+@ @<Output the design size@>=
+left; out('DESIGNSIZE');
+if tfm[design_size]>127 then bad_design('negative')
+else if (tfm[design_size]=0)and(tfm[design_size+1]<16) then
+ bad_design('too small')
+else out_fix(design_size);
+right;
+out('(COMMENT DESIGNSIZE IS IN POINTS)'); out_ln;
+out('(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)'); out_ln
+@.DESIGNSIZE IS IN POINTS@>
+
+@ Since we have to check two different {\mc BCPL} strings for validity,
+we might as well write a subroutine to make the check.
+
+@p procedure check_BCPL(@!k,@!l:index); {checks a string of length |<l|}
+var j:index; {runs through the string}
+@!c:byte; {character being checked}
+begin if tfm[k]>=l then
+ begin bad('String is too long; I''ve shortened it drastically.');
+@.String is too long...@>
+ tfm[k]:=1;
+ end;
+for j:=k+1 to k+tfm[k] do
+ begin c:=tfm[j];
+ if (c="(")or(c=")") then
+ begin bad('Parenthesis in string has been changed to slash.');
+@.Parenthesis...changed to slash@>
+ tfm[j]:="/";
+ end
+ else if (c<" ")or(c>"~") then
+ begin bad('Nonstandard ASCII code has been blotted out.');
+@.Nonstandard ASCII code...@>
+ tfm[j]:="?";
+ end
+ else if (c>="a")and(c<="z") then tfm[j]:=c+"A"-"a"; {upper-casify letters}
+ end;
+end;
+
+@ The |font_type| starts out |vanilla|; possibly we need to reset it.
+
+@<Set the true |font_type|@>=
+begin check_BCPL(scheme,40);
+if (tfm[scheme]>=11)and@|(tfm[scheme+1]="T")and@|
+ (tfm[scheme+2]="E")and@|(tfm[scheme+3]="X")and@|
+ (tfm[scheme+4]=" ")and@|(tfm[scheme+5]="M")and@|
+ (tfm[scheme+6]="A")and@|(tfm[scheme+7]="T")and@|
+ (tfm[scheme+8]="H")and@|(tfm[scheme+9]=" ") then
+ begin if (tfm[scheme+10]="S")and(tfm[scheme+11]="Y") then font_type:=mathsy
+ else if (tfm[scheme+10]="E")and(tfm[scheme+11]="X") then font_type:=mathex;
+ end;
+end
+
+@ @<Output the character coding scheme@>=
+left; out('CODINGSCHEME');
+out_BCPL(scheme);
+right
+
+@ @<Output the family name@>=
+left; out('FAMILY');
+check_BCPL(family,20);
+out_BCPL(family);
+right
+
+@ @<Output the rest of the header@>=
+begin left; out('FACE'); out_face(random_word+3); right;
+for i:=18 to lh-1 do
+ begin left; out('HEADER D ',i:1);
+ out_octal(check_sum+4*i,@,4); right;
+ end;
+end
+
+@ This program does not check to see if the |seven_bit_safe_flag| has the
+correct setting, i.e., if it really reflects the seven-bit-safety of
+the \.{TFM} file; the stated value is merely put into the \.{VPL} file.
+The \.{VPtoVF} program will store a correct value and give a warning
+message if a file falsely claims to be safe.
+
+@<Output the |seven_bit_safe_flag|@>=
+if (lh>17) and (tfm[random_word]>127) then
+ begin left; out('SEVENBITSAFEFLAG TRUE'); right;
+ end
+
+@ The next thing to take care of is the list of parameters.
+
+@<Do the parameters@>=
+if np>0 then
+ begin left; out('FONTDIMEN'); out_ln;
+ for i:=1 to np do @<Check and output the $i$th parameter@>;
+ right;
+ end;
+@<Check to see if |np| is complete for this font type@>;
+
+@ @<Check to see if |np|...@>=
+if (font_type=mathsy)and(np<>22) then
+ print_ln('Unusual number of fontdimen parameters for a math symbols font (',
+@.Unusual number of fontdimen...@>
+ np:1,' not 22).')
+else if (font_type=mathex)and(np<>13) then
+ print_ln('Unusual number of fontdimen parameters for an extension font (',
+ np:1,' not 13).')
+
+@ All |fix_word| values except the design size and the first parameter
+will be checked to make sure that they are less than 16.0 in magnitude,
+using the |check_fix| macro:
+
+@d check_fix_tail(#)==bad(#,' ',i:1,' is too big;');
+ print_ln('I have set it to zero.');
+ end
+@d check_fix(#)==if (tfm[#]>0)and(tfm[#]<255) then
+ begin tfm[#]:=0; tfm[(#)+1]:=0; tfm[(#)+2]:=0; tfm[(#)+3]:=0;
+ check_fix_tail
+
+@<Check and output the $i$th parameter@>=
+begin left;
+if i=1 then out('SLANT') {this parameter is not checked}
+else begin check_fix(param(i))('Parameter');@/
+@.Parameter n is too big@>
+ @<Output the name of parameter $i$@>;
+ end;
+out_fix(param(i)); right;
+end
+
+@ @<Output the name...@>=
+if i<=7 then case i of
+ 2:out('SPACE');@+3:out('STRETCH');@+4:out('SHRINK');
+ 5:out('XHEIGHT');@+6:out('QUAD');@+7:out('EXTRASPACE')@+end
+else if (i<=22)and(font_type=mathsy) then case i of
+ 8:out('NUM1');@+9:out('NUM2');@+10:out('NUM3');
+ 11:out('DENOM1');@+12:out('DENOM2');
+ 13:out('SUP1');@+14:out('SUP2');@+15:out('SUP3');
+ 16:out('SUB1');@+17:out('SUB2');
+ 18:out('SUPDROP');@+19:out('SUBDROP');
+ 20:out('DELIM1');@+21:out('DELIM2');
+ 22:out('AXISHEIGHT')@+end
+else if (i<=13)and(font_type=mathex) then
+ if i=8 then out('DEFAULTRULETHICKNESS')
+ else out('BIGOPSPACING',i-8:1)
+else out('PARAMETER D ',i:1)
+
+@ We need to check the range of all the remaining |fix_word| values,
+and to make sure that |width[0]=0|, etc.
+
+@d nonzero_fix(#)==(tfm[#]>0)or(tfm[#+1]>0)or(tfm[#+2]>0)or(tfm[#+3]>0)
+
+@<Check the |fix_word| entries@>=
+if nonzero_fix(4*width_base) then bad('width[0] should be zero.');
+@.should be zero@>
+if nonzero_fix(4*height_base) then bad('height[0] should be zero.');
+if nonzero_fix(4*depth_base) then bad('depth[0] should be zero.');
+if nonzero_fix(4*italic_base) then bad('italic[0] should be zero.');
+for i:=0 to nw-1 do check_fix(4*(width_base+i))('Width');
+@.Width n is too big@>
+for i:=0 to nh-1 do check_fix(4*(height_base+i))('Height');
+@.Height n is too big@>
+for i:=0 to nd-1 do check_fix(4*(depth_base+i))('Depth');
+@.Depth n is too big@>
+for i:=0 to ni-1 do check_fix(4*(italic_base+i))('Italic correction');
+@.Italic correction n is too big@>
+if nk>0 then for i:=0 to nk-1 do check_fix(kern(i))('Kern');
+@.Kern n is too big@>
+
+@ The ligature/kerning program comes next. Before we can put it out in
+\.{VPL} format, we need to make a table of ``labels'' that will be inserted
+into the program. For each character |c| whose |tag| is |lig_tag| and
+whose starting address is |r|, we will store the pair |(c,r)| in the
+|label_table| array. If there's a boundary-char program starting at~|r|,
+we also store the pair |(256,r)|.
+This array is sorted by its second components, using the
+simple method of straight insertion.
+
+@<Glob...@>=
+@!label_table:array[0..258] of record@t@>@/@!cc:0..256;@!rr:0..lig_size;end;
+@!label_ptr: 0..257; {the largest entry in |label_table|}
+@!sort_ptr:0..257; {index into |label_table|}
+@!boundary_char:0..256; {boundary character, or 256 if none}
+@!bchar_label:0..@'77777; {beginning of boundary character program}
+
+@ @<Set init...@>=
+boundary_char:=256; bchar_label:=@'77777;@/
+label_ptr:=0; label_table[0].rr:=0; {a sentinel appears at the bottom}
+
+@ We'll also identify and remove inaccessible program steps, using the
+|activity| array.
+
+@d unreachable=0 {a program step not known to be reachable}
+@d pass_through=1 {a program step passed through on initialization}
+@d accessible=2 {a program step that can be relevant}
+
+@<Glob...@>=
+@!activity:array[0..lig_size] of unreachable..accessible;
+@!ai,@!acti:0..lig_size; {indices into |activity|}
+
+@ @<Do the ligatures and kerns@>=
+if nl>0 then
+ begin for ai:=0 to nl-1 do activity[ai]:=unreachable;
+ @<Check for a boundary char@>;
+ end;
+@<Build the label table@>;
+if nl>0 then
+ begin left; out('LIGTABLE'); out_ln;@/
+ @<Compute the |activity| array@>;
+ @<Output and correct the ligature/kern program@>;
+ right;
+ @<Check for ligature cycles@>;
+ end
+
+@ We build the label table even when |nl=0|, because this catches errors
+that would not otherwise be detected.
+
+@<Build...@>=
+for c:=bc to ec do if tag(c)=lig_tag then
+ begin r:=remainder(c);
+ if r<nl then
+ begin if tfm[lig_step(r)]>stop_flag then
+ begin r:=256*tfm[lig_step(r)+2]+tfm[lig_step(r)+3];
+ if r<nl then if activity[remainder(c)]=unreachable then
+ activity[remainder(c)]:=pass_through;
+ end;
+ end;
+ if r>=nl then
+ begin perfect:=false; print_ln(' ');
+ print('Ligature/kern starting index for character '); print_octal(c);
+ print_ln(' is too large;'); print_ln('so I removed it.'); reset_tag(c);
+@.Ligature/kern starting index...@>
+ end
+ else @<Insert |(c,r)| into |label_table|@>;
+ end;
+label_table[label_ptr+1].rr:=lig_size; {put ``infinite'' sentinel at the end}
+
+@ @<Insert |(c,r)|...@>=
+begin sort_ptr:=label_ptr; {there's a hole at position |sort_ptr+1|}
+while label_table[sort_ptr].rr>r do
+ begin label_table[sort_ptr+1]:=label_table[sort_ptr];
+ decr(sort_ptr); {move the hole}
+ end;
+label_table[sort_ptr+1].cc:=c;
+label_table[sort_ptr+1].rr:=r; {fill the hole}
+incr(label_ptr); activity[r]:=accessible;
+end
+
+@ @<Check for a bound...@>=
+if tfm[lig_step(0)]=255 then
+ begin left; out('BOUNDARYCHAR');
+ boundary_char:=tfm[lig_step(0)+1]; out_char(boundary_char); right;
+ activity[0]:=pass_through;
+ end;
+if tfm[lig_step(nl-1)]=255 then
+ begin r:=256*tfm[lig_step(nl-1)+2]+tfm[lig_step(nl-1)+3];
+ if r>=nl then
+ begin perfect:=false; print_ln(' ');
+ print('Ligature/kern starting index for boundarychar is too large;');
+ print_ln('so I removed it.');
+@.Ligature/kern starting index...@>
+ end
+ else begin label_ptr:=1; label_table[1].cc:=256; label_table[1].rr:=r;
+ bchar_label:=r; activity[r]:=accessible;
+ end;
+ activity[nl-1]:=pass_through;
+ end
+
+@ @<Compute the |activity| array@>=
+for ai:=0 to nl-1 do if activity[ai]=accessible then
+ begin r:=tfm[lig_step(ai)];
+ if r<stop_flag then
+ begin r:=r+ai+1;
+ if r>=nl then
+ begin bad('Ligature/kern step ',ai:1,' skips too far;');
+@.Lig...skips too far@>
+ print_ln('I made it stop.'); tfm[lig_step(ai)]:=stop_flag;
+ end
+ else activity[r]:=accessible;
+ end;
+ end
+
+@ We ignore |pass_through| items, which don't need to be mentioned in
+the \.{VPL} file.
+
+@<Output and correct the ligature...@>=
+sort_ptr:=1; {point to the next label that will be needed}
+for acti:=0 to nl-1 do if activity[acti]<>pass_through then
+ begin i:=acti; @<Take care of commenting out unreachable steps@>;
+ @<Output any labels for step $i$@>;
+ @<Output step $i$ of the ligature/kern program@>;
+ end;
+if level=2 then right {the final step was unreachable}
+
+@ @<Output any labels...@>=
+while i=label_table[sort_ptr].rr do
+ begin left; out('LABEL');
+ if label_table[sort_ptr].cc=256 then out(' BOUNDARYCHAR')
+ else out_char(label_table[sort_ptr].cc);
+ right; incr(sort_ptr);
+ end
+
+@ @<Take care of commenting out...@>=
+if activity[i]=unreachable then
+ begin if level=1 then
+ begin left; out('COMMENT THIS PART OF THE PROGRAM IS NEVER USED!'); out_ln;
+ end
+ end
+else if level=2 then right
+
+@ @<Output step $i$...@>=
+begin k:=lig_step(i);
+if tfm[k]>stop_flag then
+ begin if 256*tfm[k+2]+tfm[k+3]>=nl then
+ bad('Ligature unconditional stop command address is too big.');
+@.Ligature unconditional stop...@>
+ end
+else if tfm[k+2]>=kern_flag then @<Output a kern step@>
+else @<Output a ligature step@>;
+if tfm[k]>0 then
+ if level=1 then @<Output either \.{SKIP} or \.{STOP}@>;
+end
+
+@ The \.{SKIP} command is a bit tricky, because we will be omitting all
+inaccessible commands.
+
+@<Output either...@>=
+begin if tfm[k]>=stop_flag then out('(STOP)')
+else begin count:=0;
+ for ai:=i+1 to i+tfm[k] do if activity[ai]=accessible then incr(count);
+ out('(SKIP D ',count:1,')'); {possibly $count=0$, so who cares}
+ end;
+out_ln;
+end
+
+@ @<Output a kern step@>=
+begin if nonexistent(tfm[k+1]) then if tfm[k+1]<>boundary_char then
+ correct_bad_char('Kern step for')(k+1);
+@.Kern step for nonexistent...@>
+left; out('KRN'); out_char(tfm[k+1]);
+r:=256*(tfm[k+2]-kern_flag)+tfm[k+3];
+if r>=nk then
+ begin bad('Kern index too large.');
+@.Kern index too large@>
+ out(' R 0.0');
+ end
+else out_fix(kern(r));
+right;
+end
+
+@ @<Output a ligature step@>=
+begin if nonexistent(tfm[k+1]) then if tfm[k+1]<>boundary_char then
+ correct_bad_char('Ligature step for')(k+1);
+@.Ligature step for nonexistent...@>
+if nonexistent(tfm[k+3]) then
+ correct_bad_char('Ligature step produces the')(k+3);
+@.Ligature step produces...@>
+left; r:=tfm[k+2];
+if (r=4)or((r>7)and(r<>11)) then
+ begin print_ln('Ligature step with nonstandard code changed to LIG');
+ r:=0; tfm[k+2]:=0;
+ end;
+if r mod 4>1 then out('/');
+out('LIG');
+if odd(r) then out('/');
+while r>3 do
+ begin out('>'); r:=r-4;
+ end;
+out_char(tfm[k+1]); out_char(tfm[k+3]); right;
+end
+
+@ The last thing on \.{VFtoVP}'s agenda is to go through the
+list of |char_info| and spew out the information about each individual
+character.
+
+@<Do the characters@>=
+sort_ptr:=0; {this will suppress `\.{STOP}' lines in ligature comments}
+for c:=bc to ec do if width_index(c)>0 then
+ begin if chars_on_line=8 then
+ begin print_ln(' '); chars_on_line:=1;
+ end
+ else begin if chars_on_line>0 then print(' ');
+ incr(chars_on_line);
+ end;
+ print_octal(c); {progress report}
+ left; out('CHARACTER'); out_char(c); out_ln;
+ @<Output the character's width@>;
+ if height_index(c)>0 then @<Output the character's height@>;
+ if depth_index(c)>0 then @<Output the character's depth@>;
+ if italic_index(c)>0 then @<Output the italic correction@>;
+ case tag(c) of
+ no_tag: do_nothing;
+ lig_tag: @<Output the applicable part of the ligature/kern
+ program as a comment@>;
+ list_tag: @<Output the character link unless there is a problem@>;
+ ext_tag: @<Output an extensible character recipe@>;
+ end;@/
+ if not do_map(c) then goto final_end;
+ right;
+ end
+
+@ @<Output the character's width@>=
+begin left; out('CHARWD');
+if width_index(c)>=nw then range_error('Width')
+else out_fix(width(c));
+right;
+end
+
+@ @<Output the character's height@>=
+if height_index(c)>=nh then range_error('Height')
+@.Height index for char...@>
+else begin left; out('CHARHT'); out_fix(height(c)); right;
+ end
+
+@ @<Output the character's depth@>=
+if depth_index(c)>=nd then range_error('Depth')
+@.Depth index for char@>
+else begin left; out('CHARDP'); out_fix(depth(c)); right;
+ end
+
+@ @<Output the italic correction@>=
+if italic_index(c)>=ni then range_error('Italic correction')
+@.Italic correction index for char...@>
+else begin left; out('CHARIC'); out_fix(italic(c)); right;
+ end
+
+@ @<Output the applicable part of the ligature...@>=
+begin left; out('COMMENT'); out_ln;@/
+i:=remainder(c); r:=lig_step(i);
+if tfm[r]>stop_flag then i:=256*tfm[r+2]+tfm[r+3];
+repeat @<Output step...@>;
+if tfm[k]>=stop_flag then i:=nl
+else i:=i+1+tfm[k];
+until i>=nl;
+right;
+end
+
+@ We want to make sure that there is no cycle of characters linked together
+by |list_tag| entries, since such a cycle would get \TeX\ into an endless
+loop. If such a cycle exists, the routine here detects it when processing
+the largest character code in the cycle.
+
+@<Output the character link unless there is a problem@>=
+begin r:=remainder(c);
+if nonexistent(r) then
+ begin bad_char('Character list link to')(r); reset_tag(c);
+@.Character list link...@>
+ end
+else begin while (r<c)and(tag(r)=list_tag) do r:=remainder(r);
+ if r=c then
+ begin bad('Cycle in a character list!');
+@.Cycle in a character list@>
+ print('Character '); print_octal(c);
+ print_ln(' now ends the list.');
+ reset_tag(c);
+ end
+ else begin left; out('NEXTLARGER'); out_char(remainder(c));
+ right;
+ end;
+ end;
+end
+
+@ @<Output an extensible character recipe@>=
+if remainder(c)>=ne then
+ begin range_error('Extensible'); reset_tag(c);
+@.Extensible index for char@>
+ end
+else begin left; out('VARCHAR'); out_ln;
+ @<Output the extensible pieces that exist@>;
+ right;
+ end
+
+@ @<Output the extensible pieces that...@>=
+for k:=0 to 3 do if (k=3)or(tfm[exten(c)+k]>0) then
+ begin left;
+ case k of
+ 0:out('TOP');@+1:out('MID');@+2:out('BOT');@+3:out('REP')@+end;
+ if nonexistent(tfm[exten(c)+k]) then out_char(c)
+ else out_char(tfm[exten(c)+k]);
+ right;
+ end
+
+@ Some of the extensible recipes may not actually be used, but \TeX\ will
+complain about them anyway if they refer to nonexistent characters.
+Therefore \.{VFtoVP} must check them too.
+
+@<Check the extensible recipes@>=
+if ne>0 then for c:=0 to ne-1 do for d:=0 to 3 do
+ begin k:=4*(exten_base+c)+d;
+ if (tfm[k]>0)or(d=3) then
+ begin if nonexistent(tfm[k]) then
+ begin bad_char('Extensible recipe involves the')(tfm[k]);
+@.Extensible recipe involves...@>
+ if d<3 then tfm[k]:=0;
+ end;
+ end;
+ end
+
+@* Checking for ligature loops.
+We have programmed almost everything but the most interesting calculation of
+all, which has been saved for last as a special treat. \TeX's extended ligature
+mechanism allows unwary users to specify sequences of ligature replacements
+that never terminate. For example, the pair of commands
+$$\.{(/LIG $x$ $y$) (/LIG $y$ $x$)}$$
+alternately replaces character $x$ by character $y$ and vice versa. A similar
+loop occurs if \.{(LIG/ $z$ $y$)} occurs in the program for $x$ and
+ \.{(LIG/ $z$ $x$)} occurs in the program for $y$.
+
+More complicated loops are also possible. For example, suppose the ligature
+programs for $x$ and $y$ are
+$$\vcenter{\halign{#\hfil\cr
+\.{(LABEL $x$)(/LIG/ $z$ $w$)(/LIG/> $w$ $y$)} \dots,\cr
+\.{(LABEL $y$)(LIG $w$ $x$)} \dots;\cr}}$$
+then the adjacent characters $xz$ change to $xwz$, $xywz$, $xxz$, $xxwz$,
+\dots, ad infinitum.
+
+@ To detect such loops, \.{VFtoVP} attempts to evaluate the function
+$f(x,y)$ for all character pairs $x$ and~$y$, where $f$ is defined as
+follows: If the current character is $x$ and the next character is
+$y$, we say the ``cursor'' is between $x$ and $y$; when the cursor
+first moves past $y$, the character immediately to its left is
+$f(x,y)$. This function is defined if and only if no infinite loop is
+generated when the cursor is between $x$ and~$y$.
+
+The function $f(x,y)$ can be defined recursively. It turns out that all pairs
+$(x,y)$ belong to one of five classes. The simplest class has $f(x,y)=y$; this
+happens if there's no ligature between $x$ and $y$, or in the cases
+\.{LIG/>} and \.{/LIG/>>}. Another simple class arises when there's a
+\.{LIG} or \.{/LIG>} between $x$ and~$y$, generating the character~$z$;
+then $f(x,y)=z$. Otherwise we always have $f(x,y)$ equal to
+either $f(x,z)$ or $f(z,y)$ or $f(f(x,z),y)$, where $z$ is the inserted
+ligature character.
+
+The first two of these classes can be merged; we can also consider
+$(x,y)$ to belong to the simple class when $f(x,y)$ has been evaluated.
+For technical reasons we allow $x$ to be 256 (for the boundary character
+at the left) or 257 (in cases when an error has been detected).
+
+For each pair $(x,y)$ having a ligature program step, we store
+$(x,y)$ in a hash table from which the values $z$ and $class$ can be read.
+
+@d simple=0 {$f(x,y)=z$}
+@d left_z=1 {$f(x,y)=f(z,y)$}
+@d right_z=2 {$f(x,y)=f(x,z)$}
+@d both_z=3 {$f(x,y)=f(f(x,z),y)$}
+@d pending=4 {$f(x,y)$ is being evaluated}
+
+@<Glob...@>=
+@!hash:array[0..hash_size] of 0..66048; {$256x+y+1$ for $x\le257$ and $y\le255$}
+@!class:array[0..hash_size] of simple..pending;
+@!lig_z:array[0..hash_size] of 0..257;
+@!hash_ptr:0..hash_size; {the number of nonzero entries in |hash|}
+@!hash_list:array[0..hash_size] of 0..hash_size; {list of those nonzero entries}
+@!h,@!hh:0..hash_size; {indices into the hash table}
+@!x_lig_cycle,@!y_lig_cycle:0..256; {problematic ligature pair}
+
+@ @<Check for ligature cycles@>=
+hash_ptr:=0; y_lig_cycle:=256;
+for hh:=0 to hash_size do hash[hh]:=0; {clear the hash table}
+for c:=bc to ec do if tag(c)=lig_tag then
+ begin i:=remainder(c);
+ if tfm[lig_step(i)]>stop_flag then
+ i:=256*tfm[lig_step(i)+2]+tfm[lig_step(i)+3];
+ @<Enter data for character $c$ starting at location |i| in the hash table@>;
+ end;
+if bchar_label<nl then
+ begin c:=256; i:=bchar_label;
+ @<Enter data for character $c$ starting at location |i| in the hash table@>;
+ end;
+if hash_ptr=hash_size then
+ begin print_ln('Sorry, I haven''t room for so many ligature/kern pairs!');
+@.Sorry, I haven't room...@>
+ goto final_end;
+ end;
+for hh:=1 to hash_ptr do
+ begin r:=hash_list[hh];
+ if class[r]>simple then {make sure $f$ is defined}
+ r:=f(r,(hash[r]-1)div 256,(hash[r]-1)mod 256);
+ end;
+if y_lig_cycle<256 then
+ begin print('Infinite ligature loop starting with ');
+@.Infinite ligature loop...@>
+ if x_lig_cycle=256 then print('boundary')@+else print_octal(x_lig_cycle);
+ print(' and '); print_octal(y_lig_cycle); print_ln('!');
+ out('(INFINITE LIGATURE LOOP MUST BE BROKEN!)'); goto final_end;
+ end
+
+@ @<Enter data for character $c$...@>=
+repeat hash_input; k:=tfm[lig_step(i)];
+if k>=stop_flag then i:=nl
+else i:=i+1+k;
+until i>=nl
+
+@ We use an ``ordered hash table'' with linear probing, because such a table
+is efficient when the lookup of a random key tends to be unsuccessful.
+
+@p procedure hash_input; {enter data for character |c| and command |i|}
+label exit;
+var @!cc:simple..both_z; {class of data being entered}
+@!zz:0..255; {function value or ligature character being entered}
+@!y:0..255; {the character after the cursor}
+@!key:integer; {value to be stored in |hash|}
+@!t:integer; {temporary register for swapping}
+begin if hash_ptr=hash_size then return;
+@<Compute the command parameters |y|, |cc|, and |zz|@>;
+key:=256*c+y+1; h:=(1009*key) mod hash_size;
+while hash[h]>0 do
+ begin if hash[h]<=key then
+ begin if hash[h]=key then return; {unused ligature command}
+ t:=hash[h]; hash[h]:=key; key:=t; {do ordered-hash-table insertion}
+ t:=class[h]; class[h]:=cc; cc:=t; {namely, do a swap}
+ t:=lig_z[h]; lig_z[h]:=zz; zz:=t;
+ end;
+ if h>0 then decr(h)@+else h:=hash_size;
+ end;
+hash[h]:=key; class[h]:=cc; lig_z[h]:=zz;
+incr(hash_ptr); hash_list[hash_ptr]:=h;
+exit:end;
+
+@ We must store kern commands as well as ligature commands, because the former
+might make the latter inapplicable.
+
+@<Compute the command param...@>=
+k:=lig_step(i); y:=tfm[k+1]; t:=tfm[k+2]; cc:=simple; zz:=tfm[k+3];
+if t>=kern_flag then zz:=y
+else begin case t of
+ 0,6:do_nothing; {\.{LIG},\.{/LIG>}}
+ 5,11:zz:=y; {\.{LIG/>}, \.{/LIG/>>}}
+ 1,7:cc:=left_z; {\.{LIG/}, \.{/LIG/>}}
+ 2:cc:=right_z; {\.{/LIG}}
+ 3:cc:=both_z; {\.{/LIG/}}
+ end; {there are no other cases}
+ end
+
+@ Evaluation of $f(x,y)$ is handled by two mutually recursive procedures.
+Kind of a neat algorithm, generalizing a depth-first search.
+
+@p function f(@!h,@!x,@!y:index):index; forward;@t\2@>
+ {compute $f$ for arguments known to be in |hash[h]|}
+function eval(@!x,@!y:index):index; {compute $f(x,y)$ with hashtable lookup}
+var @!key:integer; {value sought in hash table}
+begin key:=256*x+y+1; h:=(1009*key) mod hash_size;
+while hash[h]>key do
+ if h>0 then decr(h)@+else h:=hash_size;
+if hash[h]<key then eval:=y {not in ordered hash table}
+else eval:=f(h,x,y);
+end;
+
+@ Pascal's beastly convention for |forward| declarations prevents us from
+saying |function f(h,x,y:index):index| here.
+
+@p function f;
+begin case class[h] of
+simple: do_nothing;
+left_z: begin class[h]:=pending; lig_z[h]:=eval(lig_z[h],y); class[h]:=simple;
+ end;
+right_z: begin class[h]:=pending; lig_z[h]:=eval(x,lig_z[h]); class[h]:=simple;
+ end;
+both_z: begin class[h]:=pending; lig_z[h]:=eval(eval(x,lig_z[h]),y);
+ class[h]:=simple;
+ end;
+pending: begin x_lig_cycle:=x; y_lig_cycle:=y; lig_z[h]:=257; class[h]:=simple;
+ end; {the value 257 will break all cycles, since it's not in |hash|}
+end; {there are no other cases}
+f:=lig_z[h];
+end;
+
+@* Outputting the VF info.
+The routines we've used for output from the |tfm| array have counterparts
+for output from |vf|. One difference is that the string outputs from |vf|
+need to be checked for balanced parentheses. The |string_balance| routine
+tests the string of length~|l| that starts at location~|k|.
+
+@p function string_balance(@!k,@!l:integer):boolean;
+label not_found,exit;
+var @!j,@!bal:integer;
+begin if l>0 then if vf[k]=" " then goto not_found;
+ {a leading blank is considered unbalanced}
+bal:=0;
+for j:=k to k+l-1 do
+ begin if (vf[j]<" ")or(vf[j]>=127) then goto not_found;
+ if vf[j]="(" then incr(bal)
+ else if vf[j]=")" then
+ if bal=0 then goto not_found else decr(bal);
+ end;
+if bal>0 then goto not_found;
+string_balance:=true; return;
+not_found:string_balance:=false;
+exit:end;
+
+@ @d bad_vf(#)==begin perfect:=false; if chars_on_line>0 then print_ln(' ');
+ chars_on_line:=0; print_ln('Bad VF file: ',#);
+ end
+@.Bad VF file@>
+
+@<Do the virtual font title@>=
+if string_balance(0,font_start[0]) then
+ begin left; out('VTITLE ');
+ for k:=0 to font_start[0]-1 do out(xchr[vf[k]]);
+ right;
+ end
+else bad_vf('Title is not a balanced ASCII string')
+@.Title is not balanced@>
+
+@ We can re-use some code by moving |fix_word| data to |tfm|, using the
+fact that the design size has already been output.
+
+@p procedure out_as_fix(@!x:integer);
+var @!k:1..3;
+begin if abs(x)>=@'100000000 then
+ begin bad_vf('Oversize dimension has been reset to zero.');
+@.Oversize dimension...@>
+ x:=0;
+ end;
+if x>=0 then tfm[design_size]:=0
+else begin tfm[design_size]:=255; x:=x+@'100000000;
+ end;
+for k:=3 downto 1 do
+ begin tfm[design_size+k]:=x mod 256; x:=x div 256;
+ end;
+out_fix(design_size);
+end;
+
+@ @<Do the local fonts@>=
+for f:=0 to font_ptr-1 do
+ begin left; out('MAPFONT D ',f:1); out_ln;
+ @<Output the font area and name@>;
+ for k:=0 to 11 do tfm[k]:=vf[font_start[f]+k];
+ if tfm[0]+tfm[1]+tfm[2]+tfm[3]>0 then
+ begin left; out('FONTCHECKSUM'); out_octal(0,4); right;
+ end;
+ left; out('FONTAT'); out_fix(4); right;
+ left; out('FONTDSIZE'); out_fix(8); right; right;
+ end
+
+@ @<Output the font area and name@>=
+a:=vf[font_start[f]+12]; l:=vf[font_start[f]+13];
+if a>0 then
+ if not string_balance(font_start[f]+14,a) then
+ bad_vf('Improper font area will be ignored')
+@.Improper font area@>
+ else begin left; out('FONTAREA ');
+ for k:=font_start[f]+14 to font_start[f]+a+13 do out(xchr[vf[k]]);
+ right;
+ end;
+if (l=0)or not string_balance(font_start[f]+14+a,l) then
+ bad_vf('Improper font name will be ignored')
+@.Improper font name@>
+else begin left; out('FONTNAME ');
+ for k:=font_start[f]+14+a to font_start[f]+a+l+13 do out(xchr[vf[k]]);
+ right;
+ end
+
+@ Now we get to the interesting part of \.{VF} output, where \.{DVI}
+commands are translated into symbolic form. The \.{VPL} language is a subset
+of \.{DVI}, so we sometimes need to output semantic equivalents of
+the commands instead of producing a literal translation. This causes a
+small but tolerable loss of efficiency. We need to simulate the stack
+used by \.{DVI}-reading software.
+
+@<Glob...@>=
+@!top:0..max_stack; {\.{DVI} stack pointer}
+@!wstack,@!xstack,@!ystack,@!zstack:array[0..max_stack] of integer;
+ {stacked values of \.{DVI} registers |w|, |x|, |y|, |z|}
+@!vf_limit:0..vf_size; {the current packet ends here}
+@!o:byte; {the current opcode}
+
+@ @<Do the packet for character |c|@>=
+if packet_start[c]=vf_size then
+ bad_vf('Missing packet for character ',c:1)
+@.Missing packet@>
+else begin left; out('MAP'); out_ln;
+ top:=0; wstack[0]:=0; xstack[0]:=0; ystack[0]:=0; zstack[0]:=0;
+ vf_ptr:=packet_start[c]; vf_limit:=packet_end[c]+1; f:=0;
+ while vf_ptr<vf_limit do
+ begin o:=vf[vf_ptr]; incr(vf_ptr);
+ case o of
+ @<Cases of \.{DVI} instructions that can appear in character packets@>@;
+ improper_DVI_for_VF: bad_vf('Illegal DVI code ',o:1,' will be ignored');
+ end; {there are no other cases}
+ end;
+ if top>0 then
+ begin bad_vf('More pushes than pops!');
+@.More pushes than pops@>
+ repeat out('(POP)'); decr(top);@+until top=0;
+ end;
+ right;
+ end
+
+@ A procedure called |get_bytes| helps fetch the parameters of \.{DVI} commands.
+
+@p function get_bytes(@!k:integer;@!signed:boolean):integer;
+var @!a:integer; {accumulator}
+begin if vf_ptr+k>vf_limit then
+ begin bad_vf('Packet ended prematurely'); k:=vf_limit-vf_ptr;
+ end;
+a:=vf[vf_ptr];
+if (k=4) or signed then
+ if a>=128 then a:=a-256;
+incr(vf_ptr);
+while k>1 do
+ begin a:=a*256+vf[vf_ptr]; incr(vf_ptr); decr(k);
+ end;
+get_bytes:=a;
+end;
+
+@ Let's look at the simplest cases first, in order to get some experience.
+
+@d four_cases(#)==#,#+1,#+2,#+3
+@d eight_cases(#)==four_cases(#),four_cases(#+4)
+@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
+@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
+@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)
+
+@<Cases...@>=
+nop:do_nothing;
+push:begin if top=max_stack then
+ begin print_ln('Stack overflow!'); goto final_end;
+@.Stack overflow@>
+ end;
+ incr(top); wstack[top]:=wstack[top-1]; xstack[top]:=xstack[top-1];
+ ystack[top]:=ystack[top-1]; zstack[top]:=zstack[top-1]; out('(PUSH)');
+ out_ln;
+ end;
+pop:if top=0 then bad_vf('More pops than pushes!')
+@.More pops than pushes@>
+ else begin decr(top); out('(POP)'); out_ln;
+ end;
+set_rule,put_rule:begin if o=put_rule then out('(PUSH)');
+ left; out('SETRULE'); out_as_fix(get_bytes(4,true));
+ out_as_fix(get_bytes(4,true));
+ if o=put_rule then out(')(POP');
+ right;
+ end;
+
+@ Horizontal and vertical motions become \.{RIGHT} and \.{DOWN} in \.{VPL}
+lingo.
+
+@<Cases...@>=
+four_cases(right1):begin out('(MOVERIGHT');
+ out_as_fix(get_bytes(o-right1+1,true));
+ out(')'); out_ln;@+end;
+w0,four_cases(w1):begin if o<>w0 then wstack[top]:=get_bytes(o-w1+1,true);
+ out('(MOVERIGHT'); out_as_fix(wstack[top]); out(')'); out_ln;@+end;
+x0,four_cases(x1):begin if o<>x0 then xstack[top]:=get_bytes(o-x1+1,true);
+ out('(MOVERIGHT'); out_as_fix(xstack[top]); out(')'); out_ln;@+end;
+four_cases(down1):begin out('(MOVEDOWN'); out_as_fix(get_bytes(o-down1+1,true));
+ out(')'); out_ln;@+end;
+y0,four_cases(y1):begin if o<>y0 then ystack[top]:=get_bytes(o-y1+1,true);
+ out('(MOVEDOWN'); out_as_fix(ystack[top]); out(')'); out_ln;@+end;
+z0,four_cases(z1):begin if o<>z0 then zstack[top]:=get_bytes(o-z1+1,true);
+ out('(MOVEDOWN'); out_as_fix(zstack[top]); out(')'); out_ln;@+end;
+
+@ Variable |f| always refers to the current font. If |f=font_ptr|, it's
+a font that hasn't been defined (so its characters will be ignored).
+
+@<Cases...@>=
+sixty_four_cases(fnt_num_0),four_cases(fnt1):begin f:=0;
+ if o>=fnt1 then font_number[font_ptr]:=get_bytes(o-fnt1+1,false)
+ else font_number[font_ptr]:=o-fnt_num_0;
+ while font_number[f]<>font_number[font_ptr] do incr(f);
+ if f=font_ptr then bad_vf('Undeclared font selected')
+@.Undeclared font selected@>
+ else begin out('(SELECTFONT D ',f:1,')'); out_ln;
+ end;
+ end;
+
+@ Before we typeset a character we make sure that it exists.
+
+@<Cases...@>=
+sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64),
+ four_cases(set1),four_cases(put1):begin if o>=set1 then
+ if o>=put1 then k:=get_bytes(o-put1+1,false)
+ else k:=get_bytes(o-set1+1,false)
+ else k:=o;
+ c:=k;
+ if (k<0)or(k>255) then
+ bad_vf('Character ',k:1,' is out of range and will be ignored')
+ else if f=font_ptr then
+ bad_vf('Character ',c:1,' in undeclared font will be ignored')
+@.Character...will be ignored@>
+ else begin vf[font_start[f+1]-1]:=c; {store |c| in the ``hole'' we left}
+ k:=font_chars[f];@+while vf[k]<>c do incr(k);
+ if k=font_start[f+1]-1 then
+ bad_vf('Character ',c:1,' in font ',f:1,' will be ignored')
+ else begin if o>=put1 then out('(PUSH)');
+ left; out('SETCHAR'); out_char(c);
+ if o>=put1 then out(')(POP');
+ right;
+ end;
+ end;
+ end;
+
+@ The ``special'' commands are the only ones remaining to be dealt with.
+We use a hexadecimal
+output in the general case, if a simple string would be inadequate.
+
+@d out_hex(#)==begin a:=#;
+ if a<10 then out(a:1)
+ else out(xchr[a-10+"A"]);
+ end
+
+@<Cases...@>=
+four_cases(xxx1):begin k:=get_bytes(o-xxx1+1,false);
+ if k<0 then bad_vf('String of negative length!')
+ else begin left;
+ if k+vf_ptr>vf_limit then
+ begin bad_vf('Special command truncated to packet length');
+ k:=vf_limit-vf_ptr;
+ end;
+ if (k>64)or not string_balance(vf_ptr,k) then
+ begin out('SPECIALHEX ');
+ while k>0 do
+ begin if k mod 32=0 then out_ln
+ else if k mod 4=0 then out(' ');
+ out_hex(vf[vf_ptr] div 16); out_hex(vf[vf_ptr] mod 16);
+ incr(vf_ptr); decr(k);
+ end;
+ end
+ else begin out('SPECIAL ');
+ while k>0 do
+ begin out(xchr[vf[vf_ptr]]); incr(vf_ptr); decr(k);
+ end;
+ end;
+ right;
+ end;
+ end;
+
+@* The main program.
+The routines sketched out so far need to be packaged into separate procedures,
+on some systems, since some \PASCAL\ compilers place a strict limit on the
+size of a routine. The packaging is done here in an attempt to avoid some
+system-dependent changes.
+
+First come the |vf_input| and |organize| procedures, which read the input data
+and get ready for subsequent events. If something goes wrong, the routines
+return |false|.
+
+@p function vf_input:boolean;
+label final_end, exit;
+var vf_ptr:0..vf_size; {an index into |vf|}
+@!k:integer; {all-purpose index}
+@!c:integer; {character code}
+begin @<Read the whole \.{VF} file@>;
+vf_input:=true; return;
+final_end: vf_input:=false;
+exit: end;
+@#
+function organize:boolean;
+label final_end, exit;
+var tfm_ptr:index; {an index into |tfm|}
+begin @<Read the whole \.{TFM} file@>;
+@<Set subfile sizes |lh|, |bc|, \dots, |np|@>;
+@<Compute the base addresses@>;
+organize:=vf_input; return;
+final_end: organize:=false;
+exit: end;
+
+@ Next we do the simple things.
+
+@p procedure do_simple_things;
+var i:0..@'77777; {an index to words of a subfile}
+@!f:0..vf_size; {local font number}
+@!k:integer; {all-purpose index}
+begin @<Do the virtual font title@>;
+@<Do the header@>;
+@<Do the parameters@>;
+@<Do the local fonts@>;
+@<Check the |fix_word| entries@>;
+end;
+
+@ And then there's a routine for individual characters.
+
+@p function do_map(@!c:byte):boolean;
+label final_end,exit;
+var @!k:integer;
+@!f:0..vf_size; {current font number}
+begin @<Do the packet for character |c|@>;
+do_map:=true; return;
+final_end: do_map:=false;
+exit:end;
+@#
+function do_characters:boolean;
+label final_end, exit;
+var @!c:byte; {character being done}
+@!k:index; {a random index}
+@!ai:0..lig_size; {index into |activity|}
+begin @<Do the characters@>;@/
+do_characters:=true; return;
+final_end: do_characters:=false;
+exit:end;
+
+@ Here is where \.{VFtoVP} begins and ends.
+@p begin initialize;@/
+if not organize then goto final_end;
+do_simple_things;@/
+@<Do the ligatures and kerns@>;
+@<Check the extensible recipes@>;
+if not do_characters then goto final_end;
+print_ln('.');@/
+if level<>0 then print_ln('This program isn''t working!');
+@.This program isn't working@>
+if not perfect then
+ begin out('(COMMENT THE TFM AND/OR VF FILE WAS BAD, ');
+ out('SO THE DATA HAS BEEN CHANGED!)');
+ write_ln(vpl_file);
+ end;
+@.THE TFM AND/OR VF FILE WAS BAD...@>
+final_end:end.
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{VFtoVP} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/systems/knuth/dist/etc/vptovf.web b/systems/knuth/dist/etc/vptovf.web
new file mode 100644
index 0000000000..03b057e88b
--- /dev/null
+++ b/systems/knuth/dist/etc/vptovf.web
@@ -0,0 +1,3181 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Version 1 was implemented in December 1989.
+% Version 1.1 fixed some for-loop indices for stricter Pascal (April 1990).
+% Version 1.2 fixed `nonexistent char 0' bug, and a bit more (September 1990).
+% Version 1.3 has more robust `out_scaled' (March 1991).
+% Version 1.4 (March 1995) initialized lk_step_ended (Armin K\"ollner).
+% Version 1.5 (August 1998) corrected vf_fix(0) (Wayne Sullivan).
+% Version 1.6 (January 2014) corrected possible end-of-line glitch (Ken Nakano),
+% and get_fix now treats -- as + (Peter Breitenlohner).
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+\font\logo=logo10 % for the METAFONT logo
+\def\MF{{\logo METAFONT}}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+
+\def\title{VP\lowercase{to}VF}
+\def\contentspagenumber{201}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont VPtoVF} processor}
+ \vskip 15pt
+ \centerline{(Version 1.6, January 2014)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this program
+ was supported in part by the National Science
+ Foundation and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+The \.{VPtoVF} utility program converts virtual-property-list (``\.{VPL}'')
+files into an equivalent pair of files called a virtual font (``\.{VF}'') file
+and a \TeX\ font metric (``\.{TFM}'') file. It also makes a thorough check
+of the given \.{VPL} file, so that the \.{VF} file should be acceptable to
+device drivers and the \.{TFM} file should be acceptable to \TeX.
+
+\indent\.{VPtoVF} is an extended version of the program \.{PLtoTF}, which
+is part of the standard \TeX ware library.
+The idea of a virtual font was inspired by the work of David R. Fuchs
+@^Fuchs, David Raymond@>
+who designed a similar set of conventions in 1984 while developing a
+device driver for ArborText, Inc. He wrote a somewhat similar program
+called \.{PLFONT}.
+
+The |banner| string defined here should be changed whenever \.{VPtoVF}
+gets modified.
+
+@d banner=='This is VPtoVF, Version 1.6' {printed when the program starts}
+
+@ This program is written entirely in standard \PASCAL, except that
+it has to do some slightly system-dependent character code conversion
+on input. Furthermore, lower case letters are used in error messages;
+they could be converted to upper case if necessary. The input is read
+from |vpl_file|, and the output is written on |vf_file| and |tfm_file|;
+error messages and
+other remarks are written on the |output| file, which the user may
+choose to assign to the terminal if the system permits it.
+@^system dependencies@>
+
+The term |print| is used instead of |write| when this program writes on
+the |output| file, so that all such output can be easily deflected.
+
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+
+@p program VPtoVF(@!vpl_file,@!vf_file,@!tfm_file,@!output);
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var @<Local variables for initialization@>@/
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ The following parameters can be changed at compile time to extend or
+reduce \.{VPtoVF}'s capacity.
+
+@<Constants...@>=
+@!buf_size=60; {length of lines displayed in error messages}
+@!max_header_bytes=100; {four times the maximum number of words allowed in
+ the \.{TFM} file header block, must be 1024 or less}
+@!vf_size=10000; {maximum length of |vf| data, in bytes}
+@!max_stack=100; {maximum depth of simulated \.{DVI} stack}
+@!max_param_words=30; {the maximum number of \.{fontdimen} parameters allowed}
+@!max_lig_steps=5000;
+ {maximum length of ligature program, must be at most $32767-257=32510$}
+@!max_kerns=500; {the maximum number of distinct kern values}
+@!hash_size=5003; {preferably a prime number, a bit larger than the number
+ of character pairs in lig/kern steps}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d do_nothing == {empty statement}
+
+@* Property list description of font metric data.
+The idea behind \.{VPL} files is that precise details about fonts, i.e., the
+facts that are needed by typesetting routines like \TeX, sometimes have to
+be supplied by hand. The nested property-list format provides a reasonably
+convenient way to do this.
+
+A good deal of computation is necessary to parse and process a
+\.{VPL} file, so it would be inappropriate for \TeX\ itself to do this
+every time it loads a font. \TeX\ deals only with the compact descriptions
+of font metric data that appear in \.{TFM} files. Such data is so compact,
+however, it is almost impossible for anybody but a computer to read it.
+
+Device drivers also need a compact way to describe mappings from \TeX's idea
+of a font to the actual characters a device can produce. They can do this
+conveniently when given a packed sequence of bytes called a \.{VF} file.
+
+The purpose of \.{VPtoVF} is to convert from a human-oriented file of text
+to computer-oriented files of binary numbers. There's a companion program,
+\.{VFtoVP}, which goes the other way.
+
+@<Glob...@>=
+@!vpl_file:text;
+
+@ @<Set init...@>=
+reset(vpl_file);
+
+@ A \.{VPL} file is like a \.{PL} file with a few extra features, so we
+can begin to define it by reviewing the definition of \.{PL} files. The
+material in the next few sections is copied from the program \.{PLtoTF}.
+
+A \.{PL} file is a list of entries of the form
+$$\.{(PROPERTYNAME VALUE)}$$
+where the property name is one of a finite set of names understood by
+this program, and the value may itself in turn be a property list.
+The idea is best understood by looking at an example, so let's consider
+a fragment of the \.{PL} file for a hypothetical font.
+$$\vbox{\halign{\.{#}\hfil\cr
+(FAMILY NOVA)\cr
+(FACE F MIE)\cr
+(CODINGSCHEME ASCII)\cr
+(DESIGNSIZE D 10)\cr
+(DESIGNUNITS D 18)\cr
+(COMMENT A COMMENT IS IGNORED)\cr
+(COMMENT (EXCEPT THIS ONE ISN'T))\cr
+(COMMENT (ACTUALLY IT IS, EVEN THOUGH\cr
+\qquad\qquad IT SAYS IT ISN'T))\cr
+(FONTDIMEN\cr
+\qquad (SLANT R -.25)\cr
+\qquad (SPACE D 6)\cr
+\qquad (SHRINK D 2)\cr
+\qquad (STRETCH D 3)\cr
+\qquad (XHEIGHT R 10.55)\cr
+\qquad (QUAD D 18)\cr
+\qquad )\cr
+(LIGTABLE\cr
+\qquad (LABEL C f)\cr
+\qquad (LIG C f O 200)\cr
+\qquad (SKIP D 1)\cr
+\qquad (LABEL O 200)\cr
+\qquad (LIG C i O 201)\cr
+\qquad (KRN O 51 R 1.5)\cr
+\qquad (/LIG C ? C f)\cr
+\qquad (STOP)\cr
+\qquad )\cr
+(CHARACTER C f\cr
+\qquad (CHARWD D 6)\cr
+\qquad (CHARHT R 13.5)\cr
+\qquad (CHARIC R 1.5)\cr
+\qquad )\cr}}$$
+This example says that the font whose metric information is being described
+belongs to the hypothetical
+\.{NOVA} family; its face code is medium italic extended;
+and the characters appear in ASCII code positions. The design size is 10 points,
+and all other sizes in this \.{PL} file are given in units such that 18 units
+equals the design size. The font is slanted with a slope of $-.25$ (hence the
+letters actually slant backward---perhaps that is why the family name is
+\.{NOVA}). The normal space between words is 6 units (i.e., one third of
+the 18-unit design size), with glue that shrinks by 2 units or stretches by 3.
+The letters for which accents don't need to be raised or lowered are 10.55
+units high, and one em equals 18 units.
+
+The example ligature table is a bit trickier. It specifies that the
+letter \.f followed by another \.f is changed to code @'200, while
+code @'200 followed by \.i is changed to @'201; presumably codes @'200
+and @'201 represent the ligatures `ff' and `ffi'. Moreover, in both cases
+\.f and @'200, if the following character is the code @'51 (which is a
+right parenthesis), an additional 1.5 units of space should be inserted
+before the @'51. (The `\.{SKIP}~\.D~\.1' skips over one \.{LIG} or
+\.{KRN} command, which in this case is the second \.{LIG}; in this way
+two different ligature/kern programs can come together.)
+Finally, if either \.f or @'200 is followed by a question mark,
+the question mark is replaced by \.f and the ligature program is
+started over. (Thus, the character pair `\.{f?}' would actually become
+the ligature `ff', and `\.{ff?}' or `\.{f?f}' would become `fff'. To
+avoid this restart procedure, the \.{/LIG} command could be replaced
+by \.{/LIG>}; then `\.{f?}' would become `f\kern0ptf' and `\.{f?f}'
+would become `f\kern0ptff'.)
+
+Character \.f itself is 6 units wide and 13.5 units tall, in this example.
+Its depth is zero (since \.{CHARDP} is not given), and its italic correction
+is 1.5 units.
+
+@ The example above illustrates most of the features found in \.{PL} files.
+Note that some property names, like \.{FAMILY} or \.{COMMENT}, take a
+string as their value; this string continues until the first unmatched
+right parenthesis. But most property names, like \.{DESIGNSIZE} and \.{SLANT}
+and \.{LABEL}, take a number as their value. This number can be expressed in
+a variety of ways, indicated by a prefixed code; \.D stands for decimal,
+\.H for hexadecimal, \.O for octal, \.R for real, \.C for character, and
+\.F for ``face.'' Other property names, like \.{LIG}, take two numbers as
+their value. And still other names, like \.{FONTDIMEN} and \.{LIGTABLE} and
+\.{CHARACTER}, have more complicated values that involve property lists.
+
+A property name is supposed to be used only in an appropriate property
+list. For example, \.{CHARWD} shouldn't occur on the outer level or
+within \.{FONTDIMEN}.
+
+The individual property-and-value pairs in a property list can appear in
+any order. For instance, `\.{SHRINK}' precedes `\.{STRETCH}' in the example
+above, although the \.{TFM} file always puts the stretch parameter first.
+One could even give the information about characters like `\.f' before
+specifying the number of units in the design size, or before specifying the
+ligature and kerning table. However, the \.{LIGTABLE} itself is an exception
+to this rule; the individual elements of the \.{LIGTABLE} property list
+can be reordered only to a certain extent without changing the meaning
+of that table.
+
+If property-and-value pairs are omitted, a default value is used. For example,
+we have already noted that the default for \.{CHARDP} is zero. The default
+for {\sl every\/} numeric value is, in fact, zero, unless otherwise stated
+below.
+
+If the same property name is used more than once, \.{VPtoVF} will not notice
+the discrepancy; it simply uses the final value given. Once again, however, the
+\.{LIGTABLE} is an exception to this rule; \.{VPtoVF} will complain if there
+is more than one label for some character. And of course many of the
+entries in the \.{LIGTABLE} property list have the same property name.
+
+@ A \.{VPL} file also includes information about how to create each character,
+by typesetting characters from other fonts and/or by drawing lines, etc.
+Such information is the value of the `\.{MAP}' property, which can be
+illustrated as follows:
+$$\vbox{\halign{\.{#}\hfil\cr
+(MAPFONT D 0 (FONTNAME Times-Roman))\cr
+(MAPFONT D 1 (FONTNAME Symbol))\cr
+(MAPFONT D 2 (FONTNAME cmr10)(FONTAT D 20))\cr
+(CHARACTER O 0 (MAP (SELECTFONT D 1)(SETCHAR C G)))\cr
+(CHARACTER O 76 (MAP (SETCHAR O 277)))\cr
+(CHARACTER D 197 (MAP\cr
+\qquad(PUSH)(SETCHAR C A)(POP)\cr
+\qquad(MOVEUP R 0.937)(MOVERIGHT R 1.5)(SETCHAR O 312)))\cr
+(CHARACTER O 200 (MAP (MOVEDOWN R 2.1)(SETRULE R 1 R 8)))\cr
+(CHARACTER O 201 (MAP\cr
+\qquad (SPECIAL ps: /SaveGray currentgray def .5 setgray)\cr
+\qquad (SELECTFONT D 2)(SETCHAR C A)\cr
+\qquad (SPECIAL ps: SaveGray setgray)))\cr
+}}$$
+(These specifications appear in addition to the conventional \.{PL}
+information. The \.{MAP} attribute can be mixed in with other attributes
+like \.{CHARWD} or it can be given separately.)
+
+In this example, the virtual font is composed of characters that can be
+fabricated from three actual fonts, `\.{Times-Roman}',
+`\.{Symbol}', and `\.{cmr10} \.{at} \.{20\\u}' (where \.{\\u}
+is the unit size in this \.{VPL} file). Character |@'0| is typeset as
+a `G' from the symbol font. Character |@'76| is typeset as character |@'277|
+from the ordinary Times font. (If no other font is selected, font
+number~0 is the default. If no \.{MAP} attribute is given, the default map
+is a character of the same number in the default font.)
+
+Character 197 (decimal) is more interesting: First an A is typeset (in the
+default font Times), and this is enclosed by \.{PUSH} and \.{POP} so that
+the original position is restored. Then the accent character |@'312| is
+typeset, after moving up .937 units and right 1.5 units.
+
+To typeset character |@'200| in this virtual font, we move down 2.1 units,
+then typeset a rule that is 1 unit high and 8 units wide.
+
+Finally, to typeset character |@'201|, we do something that requires a
+special ability to interpret PostScript commands; this example
+sets the PostScript ``color'' to 50\char`\%\ gray and typesets an `A'
+from \.{cmr10} \.{at} \.{20\\u} in that color.
+
+In general, the \.{MAP} attribute of a virtual character can be any sequence
+of typesetting commands that might appear in a page of a \.{DVI} file.
+A single character might map into an entire page.
+
+@ But instead of relying on a hypothetical example, let's consider a complete
+grammar for \.{VPL} files, beginning with the (unchanged) grammatical rules
+for \.{PL} files. At the outer level, the following property names
+are valid in any \.{PL} file:
+
+\yskip\hang\.{CHECKSUM} (four-byte value). The value, which should be a
+nonnegative integer less than $2^{32}$, is used to identify a particular
+version of a font; it should match the check sum value stored with the font
+itself. An explicit check sum of zero is used to bypass
+check sum testing. If no checksum is specified in the \.{VPL} file,
+\.{VPtoVF} will compute the checksum that \MF\ would compute from the
+same data.
+
+\yskip\hang\.{DESIGNSIZE} (numeric value, default is 10). The value, which
+should be a real number in the range |1.0<=x<2048|, represents the default
+amount by which all quantities will be scaled if the font is not loaded
+with an `\.{at}' specification. For example, if one says
+`\.{\\font\\A=cmr10 at 15pt}' in \TeX\ language, the design size in the \.{TFM}
+file is ignored and effectively replaced by 15 points; but if one simply
+says `\.{\\font\\A=cmr10}' the stated design size is used. This quantity is
+always in units of printer's points.
+
+\yskip\hang\.{DESIGNUNITS} (numeric value, default is 1). The value
+should be a positive real number; it says how many units equals the design
+size (or the eventual `\.{at}' size, if the font is being scaled). For
+example, suppose you have a font that has been digitized with 600 pixels per
+em, and the design size is one em; then you could say `\.{(DESIGNUNITS R 600)}'
+if you wanted to give all of your measurements in units of pixels.
+
+\yskip\hang\.{CODINGSCHEME} (string value, default is `\.{UNSPECIFIED}').
+The string should not contain parentheses, and its length must be less than 40.
+It identifies the correspondence between the numeric codes and font characters.
+(\TeX\ ignores this information, but other software programs make use of it.)
+
+\yskip\hang\.{FAMILY} (string value, default is `\.{UNSPECIFIED}').
+The string should not contain parentheses, and its length must be less than 20.
+It identifies the name of the family to which this font belongs, e.g.,
+`\.{HELVETICA}'. (\TeX\ ignores this information; but it is needed, for
+example, when converting \.{DVI} files to \.{PRESS} files for Xerox
+equipment.)
+
+\yskip\hang\.{FACE} (one-byte value). This number, which must lie between
+0 and 255 inclusive, is a subsidiary ident\-ifi\-ca\-tion of the font within its
+family. For example, bold italic condensed fonts might have the same family name
+as light roman extended fonts, differing only in their face byte. (\TeX\
+ignores this information; but it is needed, for example, when converting
+\.{DVI} files to \.{PRESS} files for Xerox equipment.)
+
+\yskip\hang\.{SEVENBITSAFEFLAG} (string value, default is `\.{FALSE}'). The
+value should start with either `\.T' (true) or `\.F' (false). If true, character
+codes less than 128 cannot lead to codes of 128 or more via ligatures or
+charlists or extensible characters. (\TeX82 ignores this flag, but older
+versions of \TeX\ would only accept \.{TFM} files that were seven-bit safe.)
+\.{VPtoVF} computes the correct value of this flag and gives an error message
+only if a claimed ``true'' value is incorrect.
+
+\yskip\hang\.{HEADER} (a one-byte value followed by a four-byte value).
+The one-byte value should be between 18 and a maximum limit that can be
+raised or lowered depending on the compile-time setting of |max_header_bytes|.
+The four-byte value goes into the header word whose index is the one-byte
+value; for example, to set |header[18]:=1|, one may write
+`\.{(HEADER D 18 O 1)}'. This notation is used for header information that
+is presently unnamed. (\TeX\ ignores it.)
+
+\yskip\hang\.{FONTDIMEN} (property list value). See below for the names
+allowed in this property list.
+
+\yskip\hang\.{LIGTABLE} (property list value). See below for the rules
+about this special kind of property list.
+
+\yskip\hang\.{BOUNDARYCHAR} (one-byte value). If this character appears in
+a \.{LIGTABLE} command, it matches ``end of word'' as well as itself.
+If no boundary character is given and no \.{LABEL} \.{BOUNDARYCHAR} occurs
+within \.{LIGTABLE}, word boundaries will not affect ligatures or kerning.
+
+\yskip\hang\.{CHARACTER}. The value is a one-byte integer followed by
+a property list. The integer represents the number of a character that is
+present in the font; the property list of a character is defined below.
+The default is an empty property list.
+
+@ Numeric property list values can be given in various forms identified by
+a prefixed letter.
+
+\yskip\hang\.C denotes an ASCII character, which should be a standard visible
+character that is not a parenthesis. The numeric value will therefore be
+between @'41 and @'176 but not @'50 or @'51.
+
+\yskip\hang\.D denotes an unsigned decimal integer, which must be
+less than $2^{32}$, i.e., at most `\.{D 4294967295}'.
+
+\yskip\hang\.F denotes a three-letter Xerox face code; the admissible codes
+are \.{MRR}, \.{MIR}, \.{BRR}, \.{BIR}, \.{LRR}, \.{LIR}, \.{MRC}, \.{MIC},
+\.{BRC}, \.{BIC}, \.{LRC}, \.{LIC}, \.{MRE}, \.{MIE}, \.{BRE}, \.{BIE},
+\.{LRE}, and \.{LIE}, denoting the integers 0 to 17, respectively.
+
+\yskip\hang\.O denotes an unsigned octal integer, which must be less than
+$2^{32}$, i.e., at most `\.{O 37777777777}'.
+
+\yskip\hang\.H denotes an unsigned hexadecimal integer, which must be less than
+$2^{32}$, i.e., at most `\.{H FFFFFFFF}'.
+
+\yskip\hang\.R denotes a real number in decimal notation, optionally preceded
+by a `\.+' or `\.-' sign, and optionally including a decimal point. The
+absolute value must be less than 2048.
+
+@ The property names allowed in a \.{FONTDIMEN} property list correspond to
+various \TeX\ parameters, each of which has a (real) numeric value. All
+of the parameters except \.{SLANT} are in design units. The admissible
+names are \.{SLANT}, \.{SPACE}, \.{STRETCH}, \.{SHRINK}, \.{XHEIGHT},
+\.{QUAD}, \.{EXTRASPACE}, \.{NUM1}, \.{NUM2}, \.{NUM3}, \.{DENOM1},
+\.{DENOM2}, \.{SUP1}, \.{SUP2}, \.{SUP3}, \.{SUB1}, \.{SUB2}, \.{SUPDROP},
+\.{SUBDROP}, \.{DELIM1}, \.{DELIM2}, and \.{AXISHEIGHT}, for parameters
+1~to~22. The alternate names \.{DEFAULTRULETHICKNESS},
+\.{BIGOPSPACING1}, \.{BIGOPSPACING2}, \.{BIGOPSPACING3},
+\.{BIGOPSPACING4}, and \.{BIGOPSPACING5}, may also be used for parameters
+8 to 13.
+
+The notation `\.{PARAMETER} $n$' provides another way to specify the
+$n$th parameter; for example, `\.{(PARAMETER} \.{D 1 R -.25)}' is another way
+to specify that the \.{SLANT} is $-0.25$. The value of $n$ must be positive
+and less than |max_param_words|.
+
+@ The elements of a \.{CHARACTER} property list can be of six different types.
+
+\yskip\hang\.{CHARWD} (real value) denotes the character's width in
+design units.
+
+\yskip\hang\.{CHARHT} (real value) denotes the character's height in
+design units.
+
+\yskip\hang\.{CHARDP} (real value) denotes the character's depth in
+design units.
+
+\yskip\hang\.{CHARIC} (real value) denotes the character's italic correction in
+design units.
+
+\yskip\hang\.{NEXTLARGER} (one-byte value), specifies the character that
+follows the present one in a ``charlist.'' The value must be the number of a
+character in the font, and there must be no infinite cycles of supposedly
+larger and larger characters.
+
+\yskip\hang\.{VARCHAR} (property list value), specifies an extensible character.
+This option and \.{NEXTLARGER} are mutually exclusive; i.e., they cannot
+both be used within the same \.{CHARACTER} list.
+
+\yskip\noindent
+The elements of a \.{VARCHAR} property list are either \.{TOP}, \.{MID},
+\.{BOT} or \.{REP}; the values are integers, which must be zero or the number
+of a character in the font. A zero value for \.{TOP}, \.{MID}, or \.{BOT} means
+that the corresponding piece of the extensible character is absent. A nonzero
+value, or a \.{REP} value of zero, denotes the character code used to make
+up the top, middle, bottom, or replicated piece of an extensible character.
+
+@ A \.{LIGTABLE} property list contains elements of four kinds, specifying a
+program in a simple command language that \TeX\ uses for ligatures and kerns.
+If several \.{LIGTABLE} lists appear, they are effectively concatenated into
+a single list.
+
+\yskip\hang\.{LABEL} (one-byte value) means that the program for the
+stated character value starts here. The integer must be the number of a
+character in the font; its \.{CHARACTER} property list must not have a
+\.{NEXTLARGER} or \.{VARCHAR} field. At least one \.{LIG} or \.{KRN} step
+must follow.
+
+\yskip\hang\.{LABEL} \.{BOUNDARYCHAR} means that the program for
+beginning-of-word ligatures starts here.
+
+\yskip\hang\.{LIG} (two one-byte values). The instruction `\.{(LIG} $c$ $r$\.)'
+means, ``If the next character is $c$, then insert character~$r$ and
+possibly delete the current character and/or~$c$;
+otherwise go on to the next instruction.''
+Characters $r$ and $c$ must be present in the font. \.{LIG} may be immediately
+preceded or followed by a slash, and then immediately followed by \.>
+characters not exceeding the number of slashes. Thus there are eight
+possible forms:
+$$\hbox to .8\hsize{\.{LIG}\hfil\.{/LIG}\hfil\.{/LIG>}\hfil
+\.{LIG/}\hfil\.{LIG/>}\hfil\.{/LIG/}\hfil\.{/LIG/>}\hfil\.{/LIG/>>}}$$
+The slashes specify retention of the left or right original character; the
+\.> signs specify passing over the result without further ligature processing.
+
+\yskip\hang\.{KRN} (a one-byte value and a real value). The instruction
+`\.{(KRN} $c$ $r$\.)' means, ``If the next character is $c$, then insert
+a blank space of width $r$ between the current character character and $c$;
+otherwise go on to the next intruction.'' The value of $r$, which is in
+design units, is often negative. Character code $c$ must exist
+in the font.
+
+\yskip\hang\.{STOP} (no value). This instruction ends a ligature/kern program.
+It must follow either a \.{LIG} or \.{KRN} instruction, not a \.{LABEL}
+or \.{STOP} or \.{SKIP}.
+
+\yskip\hang\.{SKIP} (value in the range |0..127|). This instruction specifies
+continuation of a ligature/kern program after the specified number of \.{LIG}
+or \.{KRN} steps has been skipped over. The number of subsequent \.{LIG} and
+\.{KRN} instructions must therefore exceed this specified amount.
+
+@ In addition to all these possibilities, the property name \.{COMMENT} is
+allowed in any property list. Such comments are ignored.
+
+@ So that is what \.{PL} files hold. In a \.{VPL} file additional
+properties are recognized; two of these are valid on the outermost level:
+
+\yskip\hang\.{VTITLE} (string value, default is empty). The value will be
+reproduced at the beginning of the \.{VF} file (and printed on the terminal
+by \.{VFtoVP} when it examines that file).
+
+\yskip\hang\.{MAPFONT}. The value is a nonnegative integer followed by
+a property list. The integer represents an identifying number for fonts
+used in \.{MAP} attributes. The property list, which identifies the font and
+relative size, is defined below.
+
+\yskip\noindent
+And one additional ``virtual property'' is valid within a \.{CHARACTER}:
+
+\yskip\hang\.{MAP}. The value is a property list consisting of typesetting
+commands. Default is the single command \.{SETCHAR}~$c$, where $c$ is
+the current character number.
+
+@ The elements of a \.{MAPFONT} property list can be of the following types.
+
+\yskip\hang\.{FONTNAME} (string value, default is \.{NULL}).
+This is the font's identifying name.
+
+\yskip\hang\.{FONTAREA} (string value, default is empty). If the font appears
+in a nonstandard directory, according to local conventions, the directory
+name is given here. (This is system dependent, just as in \.{DVI} files.)
+
+\yskip\hang\.{FONTCHECKSUM} (four-byte value, default is zero). This value,
+which should be a nonnegative integer less than $2^{32}$, can be used to
+check that the font being referred to matches the intended font. If nonzero,
+it should equal the \.{CHECKSUM} parameter in that font.
+
+\yskip\hang\.{FONTAT} (numeric value, default is the \.{DESIGNUNITS} of the
+present virtual font). This value is relative to the design units of
+the present virtual font, hence it will be scaled when the virtual
+font is magnified or reduced. It represents the value that will
+effectively replace the design size of the font being referred to,
+so that all characters will be scaled appropriately.
+
+\yskip\hang\.{FONTDSIZE} (numeric value, default is 10). This value is
+absolute, in units of printer's points. It should equal the \.{DESIGNSIZE}
+parameter in the font being referred to.
+
+\yskip\noindent
+If any of the
+string values contain parentheses, the parentheses must be balanced. Leading
+blanks are removed from the strings, but trailing blanks are not.
+
+@ Finally, the elements of a \.{MAP} property list are an ordered sequence
+of typesetting commands chosen from among the following:
+
+\yskip\hang\.{SELECTFONT} (four-byte integer value). The value must be the
+number of a previously defined \.{MAPFONT}. This font (or more precisely, the
+final font that is mapped to that code number, if two \.{MAPFONT} properties
+happen to specify the same code) will be used in subsequent \.{SETCHAR}
+instructions until overridden by another \.{SELECTFONT}. The first-specified
+\.{MAPFONT} is implicitly selected before the first \.{SELECTFONT} in every
+character's map.
+
+\yskip\hang\.{SETCHAR} (one-byte integer value). There must be a character of
+this number in the currently selected font. (\.{VPtoVF} doesn't check that
+the character is valid, but \.{VFtoVP} does.) That character is typeset at the
+current position, and the typesetter moves right by the \.{CHARWD} in
+that character's \.{TFM} file.
+
+\yskip\hang\.{SETRULE} (two real values). The first value specifies height,
+the second specifies width, in design units. If both height and width are
+positive, a rule is typeset at the current position. Then the typesetter
+moves right, by the specified width.
+
+\yskip\hang\.{MOVERIGHT}, \.{MOVELEFT}, \.{MOVEUP}, \.{MOVEDOWN} (real
+value). The typesetter moves its current position
+by the number of design units specified.
+
+\yskip\hang\.{PUSH}. The current typesetter position is remembered, to
+be restored on a subsequent \.{POP}.
+
+\yskip\hang\.{POP}. The current typesetter position is reset to where it
+was on the most recent unmatched \.{PUSH}. The \.{PUSH} and \.{POP}
+commands in any \.{MAP} must be properly nested like balanced parentheses.
+
+\yskip\hang\.{SPECIAL} (string value). The subsequent characters, starting
+with the first nonblank and ending just before the first `\.)' that has no
+matching `\.(', are interpreted according to local conventions with the
+same system-dependent meaning as a `special' (\\{xxx}) command
+in a \.{DVI} file.
+
+\yskip\hang\.{SPECIALHEX} (hexadecimal string value). The subsequent
+nonblank characters before the next `\.)' must consist entirely of
+hexadecimal digits, and they must contain an even number of such digits.
+Each pair of hex digits specifies a byte, and this string of bytes is
+treated just as the value of a \.{SPECIAL}. (This convention permits
+arbitrary byte strings to be represented in an ordinary text file.)
+
+@ Virtual font mapping is a recursive process, like macro expansion.
+Thus, a \.{MAPFONT} might
+specify another virtual font, whose characters are themselves mapped to
+other fonts. As an example of this possibility, consider the
+following curious file called \.{recurse.vpl}, which defines a
+virtual font that is self-contained and self-referential:
+$$\vbox{\halign{\.{#}\cr
+(VTITLE Example of recursion)\cr
+(MAPFONT D 0 (FONTNAME recurse)(FONTAT D 2))\cr
+(CHARACTER C A (CHARWD D 1)(CHARHT D 1)(MAP (SETRULE D 1 D 1)))\cr
+(CHARACTER C B (CHARWD D 2)(CHARHT D 2)(MAP (SETCHAR C A)))\cr
+(CHARACTER C C (CHARWD D 4)(CHARHT D 4)(MAP (SETCHAR C B)))\cr
+}}$$
+The design size is 10 points (the default), hence the character \.A
+in font \.{recurse} is a $10\times10$ point black square. Character \.B
+is typeset as character \.A in \.{recurse} {scaled} {2000}, hence it
+is a $20\times20$ point black square. And character \.C is typeset as
+character \.{B} in \.{recurse} {scaled} {2000}, hence its size is
+$40\times40$.
+
+Users are responsible for making sure that infinite recursion doesn't happen.
+
+@ So that is what \.{VPL} files hold. From these rules,
+you can guess (correctly) that \.{VPtoVF} operates in four main stages.
+First it assigns the default values to all properties; then it scans
+through the \.{VPL} file, changing property values as new ones are seen; then
+it checks the information and corrects any problems; and finally it outputs
+the \.{VF} and \.{TFM} files.
+
+@ The next question is, ``What are \.{VF} and
+\.{TFM} files?'' A complete answer to that question appears in the
+documentation of the companion programs, \.{VFtoVP} and
+\.{TFtoPL}, so the details will not
+be repeated here. Suffice it to say that a \.{VF} or
+\.{TFM} file stores all of the
+relevant font information in a sequence of 8-bit bytes. The number of
+bytes is always a multiple of 4, so we could regard the files
+as sequences of 32-bit words; but \TeX\ uses the byte interpretation,
+and so does \.{VPtoVF}. Note that the bytes are considered to be unsigned
+numbers.
+
+@<Glob...@>=
+@!vf_file:packed file of 0..255;
+@!tfm_file:packed file of 0..255;
+
+@ On some systems you may have to do something special to write a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(vf_file); rewrite(tfm_file);
+
+@* Basic input routines.
+For the purposes of this program, a |byte| is an unsigned eight-bit quantity,
+and an |ASCII_code| is an integer between @'40 and @'177. Such ASCII codes
+correspond to one-character constants like \.{"A"} in \.{WEB} language.
+
+@<Types...@>=
+@!byte=0..255; {unsigned eight-bit quantity}
+@!ASCII_code=@'40..@'177; {standard ASCII code numbers}
+
+@ One of the things \.{VPtoVF} has to do is convert characters of strings
+to ASCII form, since that is the code used for the family name and the
+coding scheme in a \.{TFM} file. An array |xord| is used to do the
+conversion from |char|; the method below should work with little or no change
+on most \PASCAL\ systems.
+@^system dependencies@>
+
+@d first_ord=0 {ordinal number of the smallest element of |char|}
+@d last_ord=127 {ordinal number of the largest element of |char|}
+
+@<Global...@>=
+@!xord:array[char] of ASCII_code; {conversion table}
+
+@ @<Local variables for init...@>=
+@!k:integer; {all-purpose initialization index}
+
+@ Characters that should not appear in \.{VPL} files (except in comments)
+are mapped into @'177.
+
+@d invalid_code=@'177 {code deserving an error message}
+
+@<Set init...@>=
+for k:=first_ord to last_ord do xord[chr(k)]:=invalid_code;
+xord[' ']:=" "; xord['!']:="!"; xord['"']:=""""; xord['#']:="#";
+xord['$']:="$"; xord['%']:="%"; xord['&']:="&"; xord['''']:="'";
+xord['(']:="("; xord[')']:=")"; xord['*']:="*"; xord['+']:="+"; xord[',']:=",";
+xord['-']:="-"; xord['.']:="."; xord['/']:="/"; xord['0']:="0"; xord['1']:="1";
+xord['2']:="2"; xord['3']:="3"; xord['4']:="4"; xord['5']:="5"; xord['6']:="6";
+xord['7']:="7"; xord['8']:="8"; xord['9']:="9"; xord[':']:=":"; xord[';']:=";";
+xord['<']:="<"; xord['=']:="="; xord['>']:=">"; xord['?']:="?";
+xord['@@']:="@@"; xord['A']:="A"; xord['B']:="B"; xord['C']:="C";
+xord['D']:="D"; xord['E']:="E"; xord['F']:="F"; xord['G']:="G"; xord['H']:="H";
+xord['I']:="I"; xord['J']:="J"; xord['K']:="K"; xord['L']:="L"; xord['M']:="M";
+xord['N']:="N"; xord['O']:="O"; xord['P']:="P"; xord['Q']:="Q"; xord['R']:="R";
+xord['S']:="S"; xord['T']:="T"; xord['U']:="U"; xord['V']:="V"; xord['W']:="W";
+xord['X']:="X"; xord['Y']:="Y"; xord['Z']:="Z"; xord['[']:="["; xord['\']:="\";
+xord[']']:="]"; xord['^']:="^"; xord['_']:="_"; xord['`']:="`"; xord['a']:="a";
+xord['b']:="b"; xord['c']:="c"; xord['d']:="d"; xord['e']:="e"; xord['f']:="f";
+xord['g']:="g"; xord['h']:="h"; xord['i']:="i"; xord['j']:="j"; xord['k']:="k";
+xord['l']:="l"; xord['m']:="m"; xord['n']:="n"; xord['o']:="o"; xord['p']:="p";
+xord['q']:="q"; xord['r']:="r"; xord['s']:="s"; xord['t']:="t"; xord['u']:="u";
+xord['v']:="v"; xord['w']:="w"; xord['x']:="x"; xord['y']:="y"; xord['z']:="z";
+xord['{']:="{"; xord['|']:="|"; xord['}']:="}"; xord['~']:="~";
+
+@ In order to help catch errors of badly nested parentheses, \.{VPtoVF}
+assumes that the user will begin each line with a number of blank spaces equal
+to some constant times the number of open parentheses at the beginning of
+that line. However, the program doesn't know in advance what the constant
+is, nor does it want to print an error message on every line for a user
+who has followed no consistent pattern of indentation.
+
+Therefore the following strategy is adopted: If the user has been consistent
+with indentation for ten or more lines, an indentation error will be
+reported. The constant of indentation is reset on every line that should
+have nonzero indentation.
+
+@<Glob...@>=
+@!line:integer; {the number of the current line}
+@!good_indent:integer; {the number of lines since the last bad indentation}
+@!indent: integer; {the number of spaces per open parenthesis, zero if unknown}
+@!level: integer; {the current number of open parentheses}
+
+@ @<Set init...@>=
+line:=0; good_indent:=0; indent:=0; level:=0;
+
+@ The input need not really be broken into lines of any maximum length, and
+we could read it character by character without any buffering. But we shall
+place it into a small buffer so that offending lines can be displayed in error
+messages.
+
+@<Glob...@>=
+@!left_ln,@!right_ln:boolean; {are the left and right ends of the buffer
+ at end-of-line marks?}
+@!limit:0..buf_size; {position of the last character present in the buffer}
+@!loc:0..buf_size; {position of the last character read in the buffer}
+@!buffer:array[1..buf_size] of char;
+@!input_has_ended:boolean; {there is no more input to read}
+
+@ @<Set init...@>=
+limit:=0; loc:=0; left_ln:=true; right_ln:=true; input_has_ended:=false;
+
+@ Just before each \.{CHARACTER} property list is evaluated, the character
+code is printed in octal notation. Up to eight such codes appear on a line;
+so we have a variable to keep track of how many are currently there.
+
+@<Glob...@>=
+@!chars_on_line:0..8; {the number of characters printed on the current line}
+
+@ @<Set init...@>=
+chars_on_line:=0;
+
+@ The following routine prints an error message and an indication of
+where the error was detected. The error message should not include any
+final punctuation, since this procedure supplies its own.
+
+@d err_print(#)==begin if chars_on_line>0 then print_ln(' ');
+ print(#); show_error_context;
+ end
+
+@p procedure show_error_context; {prints the current scanner location}
+var k:0..buf_size; {an index into |buffer|}
+begin print_ln(' (line ',line:1,').');
+if not left_ln then print('...');
+for k:=1 to loc do print(buffer[k]); {print the characters already scanned}
+print_ln(' ');
+if not left_ln then print(' ');
+for k:=1 to loc do print(' '); {space out the second line}
+for k:=loc+1 to limit do print(buffer[k]); {print the characters yet unseen}
+if right_ln then print_ln(' ')@+else print_ln('...');
+chars_on_line:=0;
+end;
+
+@ Here is a procedure that does the right thing when we are done
+reading the present contents of the buffer. It keeps |buffer[buf_size]|
+empty, in order to avoid range errors on certain \PASCAL\ compilers.
+
+An infinite sequence of right parentheses is placed at the end of the
+file, so that the program is sure to get out of whatever level of nesting
+it is in.
+
+On some systems it is desirable to modify this code so that tab marks
+in the buffer are replaced by blank spaces. (Simply setting
+|xord[chr(@'11)]:=" "| would not work; for example, two-line
+error messages would not come out properly aligned.)
+@^system dependencies@>
+
+@p procedure fill_buffer;
+begin left_ln:=right_ln; limit:=0; loc:=0;
+if left_ln then
+ begin if line>0 then read_ln(vpl_file);
+ incr(line);
+ end;
+if eof(vpl_file) then
+ begin limit:=1; buffer[1]:=')'; right_ln:=false; input_has_ended:=true;
+ end
+else begin while (limit<buf_size-2)and(not eoln(vpl_file)) do
+ begin incr(limit); read(vpl_file,buffer[limit]);
+ end;
+ buffer[limit+1]:=' '; right_ln:=eoln(vpl_file);
+ if right_ln then begin incr(limit); buffer[limit+1]:=' ';
+ end;
+ if left_ln then @<Set |loc| to the number of leading blanks in
+ the buffer, and check the indentation@>;
+ end;
+end;
+
+@ The interesting part about |fill_buffer| is the part that learns what
+indentation conventions the user is following, if any.
+
+@d bad_indent(#)==begin if good_indent>=10 then err_print(#);
+ good_indent:=0; indent:=0;
+ end
+
+@<Set |loc|...@>=
+begin while (loc<limit)and(buffer[loc+1]=' ') do incr(loc);
+if loc<limit then
+ begin if level=0 then
+ if loc=0 then incr(good_indent)
+ else bad_indent('Warning: Indented line occurred at level zero')
+@.Warning: Indented line...@>
+ else if indent=0 then
+ if loc mod level=0 then
+ begin indent:=loc div level; good_indent:=1;
+ end
+ else good_indent:=0
+ else if indent*level=loc then incr(good_indent)
+ else bad_indent('Warning: Inconsistent indentation; ',
+@.Warning: Inconsistent indentation...@>
+ 'you are at parenthesis level ',level:1);
+ end;
+end
+
+@* Basic scanning routines.
+The global variable |cur_char| holds the ASCII code corresponding to the
+character most recently read from the input buffer, or to a character that
+has been substituted for the real one.
+
+@<Global...@>=
+@!cur_char:ASCII_code; {we have just read this}
+
+@ Here is a procedure that sets |cur_char| to an ASCII code for the
+next character of input, if that character is a letter or digit or slash
+or \.>. Otherwise
+it sets |cur_char:=" "|, and the input system will be poised to reread the
+character that was rejected, whether or not it was a space.
+Lower case letters are converted to upper case.
+
+@p procedure get_keyword_char;
+begin while (loc=limit)and(not right_ln) do fill_buffer;
+if loc=limit then cur_char:=" " {end-of-line counts as a delimiter}
+else begin cur_char:=xord[buffer[loc+1]];
+ if cur_char>="a" then cur_char:=cur_char-@'40;
+ if ((cur_char>="0")and(cur_char<="9")) then incr(loc)
+ else if ((cur_char>="A")and(cur_char<="Z")) then incr(loc)
+ else if cur_char="/" then incr(loc)
+ else if cur_char=">" then incr(loc)
+ else cur_char:=" ";
+ end;
+end;
+
+@ The following procedure sets |cur_char| to the next character code,
+and converts lower case to upper case. If the character is a left or
+right parenthesis, it will not be ``digested''; the character will
+be read again and again, until the calling routine does something
+like `|incr(loc)|' to get past it. Such special treatment of parentheses
+insures that the structural information they contain won't be lost in
+the midst of other error recovery operations.
+
+@d backup==begin if (cur_char>")")or(cur_char<"(") then decr(loc);
+ end {undoes the effect of |get_next|}
+
+@p procedure get_next; {sets |cur_char| to next, balks at parentheses}
+begin while loc=limit do fill_buffer;
+incr(loc); cur_char:=xord[buffer[loc]];
+if cur_char>="a" then
+ if cur_char<="z" then cur_char:=cur_char-@'40 {uppercasify}
+ else begin if cur_char=invalid_code then
+ begin err_print('Illegal character in the file');
+@.Illegal character...@>
+ cur_char:="?";
+ end;
+ end
+else if (cur_char<=")")and(cur_char>="(") then decr(loc);
+end;
+
+@ Here's a procedure that scans a hexadecimal digit or a right parenthesis.
+
+@p function get_hex:byte;
+var @!a:integer; {partial result}
+begin repeat get_next;
+until cur_char<>" ";
+a:=cur_char-")";
+if a>0 then
+ begin a:=cur_char-"0";
+ if cur_char>"9" then
+ if cur_char<"A" then a:=-1 else a:=cur_char-"A"+10;
+ end;
+if (a<0)or(a>15) then
+ begin err_print('Illegal hexadecimal digit'); get_hex:=0;
+@.Illegal hexadecimal digit@>
+ end
+else get_hex:=a;
+end;
+
+@ The next procedure is used to ignore the text of a comment, or to pass over
+erroneous material. As such, it has the privilege of passing parentheses.
+It stops after the first right parenthesis that drops the level below
+the level in force when the procedure was called.
+
+@p procedure skip_to_end_of_item;
+var l:integer; {initial value of |level|}
+begin l:=level;
+while level>=l do
+ begin while loc=limit do fill_buffer;
+ incr(loc);
+ if buffer[loc]=')' then decr(level)
+ else if buffer[loc]='(' then incr(level);
+ end;
+if input_has_ended then err_print('File ended unexpectedly: No closing ")"');
+@.File ended unexpectedly...@>
+cur_char:=" "; {now the right parenthesis has been read and digested}
+end;
+
+@ A similar procedure copies the bytes remaining in an item. The copied bytes
+go into an array |vf| that we'll declare later. Leading blanks are ignored.
+
+@d vf_store(#)==
+ begin vf[vf_ptr]:=#;
+ if vf_ptr=vf_size then err_print('I''m out of memory---increase my vfsize!')
+@.I'm out of memory...@>
+ else incr(vf_ptr);
+ end
+
+@p procedure copy_to_end_of_item;
+label 30;
+var l:integer; {initial value of |level|}
+@!nonblank_found:boolean; {have we seen a nonblank character yet?}
+begin l:=level; nonblank_found:=false;
+while true do
+ begin while loc=limit do fill_buffer;
+ if buffer[loc+1]=')' then
+ if level=l then goto 30@+else decr(level);
+ incr(loc);
+ if buffer[loc]='(' then incr(level);
+ if buffer[loc]<>' ' then nonblank_found:=true;
+ if nonblank_found then
+ if xord[buffer[loc]]=invalid_code then
+ begin err_print('Illegal character in the file');
+@.Illegal character...@>
+ vf_store("?");
+ end
+ else vf_store(xord[buffer[loc]]);
+ end;
+30:end;
+
+@ Sometimes we merely want to skip past characters in the input until we
+reach a left or a right parenthesis. For example, we do this whenever we
+have finished scanning a property value and we hope that a right parenthesis
+is next (except for possible blank spaces).
+
+@d skip_to_paren==repeat get_next@;@+ until (cur_char="(")or(cur_char=")")
+@d skip_error(#)==begin err_print(#); skip_to_paren;
+ end {this gets to the right parenthesis if something goes wrong}
+@d flush_error(#)==begin err_print(#); skip_to_end_of_item;
+ end {this gets past the right parenthesis if something goes wrong}
+
+@ After a property value has been scanned, we want to move just past the
+right parenthesis that should come next in the input (except for possible
+blank spaces).
+
+@p procedure finish_the_property; {do this when the value has been scanned}
+begin while cur_char=" " do get_next;
+if cur_char<>")" then err_print('Junk after property value will be ignored');
+@.Junk after property value...@>
+skip_to_end_of_item;
+end;
+
+@* Scanning property names.
+We have to figure out the meaning of names that appear in the \.{VPL} file,
+by looking them up in a dictionary of known keywords. Keyword number $n$
+appears in locations |start[n]| through |start[n+1]-1| of an array called
+|dictionary|.
+
+@d max_name_index=100 {upper bound on the number of keywords}
+@d max_letters=666 {upper bound on the total length of all keywords}
+
+@<Global...@>=
+@!start:array[1..max_name_index] of 0..max_letters;
+@!dictionary:array[0..max_letters] of ASCII_code;
+@!start_ptr:0..max_name_index; {the first available place in |start|}
+@!dict_ptr:0..max_letters; {the first available place in |dictionary|}
+
+@ @<Set init...@>=
+start_ptr:=1; start[1]:=0; dict_ptr:=0;
+
+@ When we are looking for a name, we put it into the |cur_name| array.
+When we have found it, the corresponding |start| index will go into
+the global variable |name_ptr|.
+
+@d longest_name=20 {length of \.{DEFAULTRULETHICKNESS}}
+
+@<Glob...@>=
+@!cur_name:array[1..longest_name] of ASCII_code; {a name to look up}
+@!name_length:0..longest_name; {its length}
+@!name_ptr:0..max_name_index; {its ordinal number in the dictionary}
+
+@ A conventional hash table with linear probing (cf.\ Algorithm 6.4L
+in {\sl The Art of Computer Pro\-gram\-ming\/}) is used for the dictionary
+operations. If |nhash[h]=0|, the table position is empty, otherwise |nhash[h]|
+points into the |start| array.
+
+@d hash_prime=141 {size of the hash table}
+
+@<Glob...@>=
+@!nhash:array[0..hash_prime-1] of 0..max_name_index;
+@!cur_hash:0..hash_prime-1; {current position in the hash table}
+
+@ @<Local...@>=
+@!h:0..hash_prime-1; {runs through the hash table}
+
+@ @<Set init...@>=
+for h:=0 to hash_prime-1 do nhash[h]:=0;
+
+@ Since there is no chance of the hash table overflowing, the procedure
+is very simple. After |lookup| has done its work, |cur_hash| will point
+to the place where the given name was found, or where it should be inserted.
+
+@p procedure lookup; {finds |cur_name| in the dictionary}
+var k:0..longest_name; {index into |cur_name|}
+@!j:0..max_letters; {index into |dictionary|}
+@!not_found:boolean; {clumsy thing necessary to avoid |goto| statement}
+begin @<Compute the hash code, |cur_hash|, for |cur_name|@>;
+not_found:=true;
+while not_found do
+ begin if cur_hash=0 then cur_hash:=hash_prime-1@+else decr(cur_hash);
+ if nhash[cur_hash]=0 then not_found:=false
+ else begin j:=start[nhash[cur_hash]];
+ if start[nhash[cur_hash]+1]=j+name_length then
+ begin not_found:=false;
+ for k:=1 to name_length do
+ if dictionary[j+k-1]<>cur_name[k] then not_found:=true;
+ end;
+ end;
+ end;
+name_ptr:=nhash[cur_hash];
+end;
+
+@ @<Compute the hash...@>=
+cur_hash:=cur_name[1];
+for k:=2 to name_length do
+ cur_hash:=(cur_hash+cur_hash+cur_name[k]) mod hash_prime
+
+@ The ``meaning'' of the keyword that begins at |start[k]| in the
+dictionary is kept in |equiv[k]|. The numeric |equiv| codes are given
+symbolic meanings by the following definitions.
+
+@d comment_code=0
+@d check_sum_code=1
+@d design_size_code=2
+@d design_units_code=3
+@d coding_scheme_code=4
+@d family_code=5
+@d face_code=6
+@d seven_bit_safe_flag_code=7
+@d header_code= 8
+@d font_dimen_code=9
+@d lig_table_code=10
+@d boundary_char_code=11
+@d virtual_title_code=12
+@d map_font_code=13
+@d character_code=14
+@d font_name_code=20
+@d font_area_code=21
+@d font_checksum_code=22
+@d font_at_code=23
+@d font_dsize_code=24
+@d parameter_code=30
+@d char_info_code=60
+@d width=1
+@d height=2
+@d depth=3
+@d italic=4
+@d char_wd_code=char_info_code+width
+@d char_ht_code=char_info_code+height
+@d char_dp_code=char_info_code+depth
+@d char_ic_code=char_info_code+italic
+@d next_larger_code=65
+@d map_code=66
+@d var_char_code=67
+@d select_font_code=80
+@d set_char_code=81
+@d set_rule_code=82
+@d move_right_code=83
+@d move_down_code=85
+@d push_code=87
+@d pop_code=88
+@d special_code=89
+@d special_hex_code=90
+@d label_code=100
+@d stop_code=101
+@d skip_code=102
+@d krn_code=103
+@d lig_code=104
+
+@<Glo...@>=
+@!equiv:array[0..max_name_index] of byte;
+@!cur_code:byte; {equivalent most recently found in |equiv|}
+
+@ We have to get the keywords into the hash table and into the dictionary in
+the first place (sigh). The procedure that does this has the desired
+|equiv| code as a parameter. In order to facilitate \.{WEB} macro writing
+for the initialization, the keyword being initialized is placed into the
+last positions of |cur_name|, instead of the first positions.
+
+@p procedure enter_name(v:byte); {|cur_name| goes into the dictionary}
+var k:0..longest_name;
+begin for k:=1 to name_length do
+ cur_name[k]:=cur_name[k+longest_name-name_length];
+{now the name has been shifted into the correct position}
+lookup; {this sets |cur_hash| to the proper insertion place}
+nhash[cur_hash]:=start_ptr; equiv[start_ptr]:=v;
+for k:=1 to name_length do
+ begin dictionary[dict_ptr]:=cur_name[k]; incr(dict_ptr);
+ end;
+incr(start_ptr); start[start_ptr]:=dict_ptr;
+end;
+
+@ Here are the macros to load a name of up to 20 letters into the
+dictionary. For example, the macro |load5| is used for five-letter keywords.
+
+@d tail(#)==enter_name(#)
+@d t20(#)==cur_name[20]:=#;tail
+@d t19(#)==cur_name[19]:=#;t20
+@d t18(#)==cur_name[18]:=#;t19
+@d t17(#)==cur_name[17]:=#;t18
+@d t16(#)==cur_name[16]:=#;t17
+@d t15(#)==cur_name[15]:=#;t16
+@d t14(#)==cur_name[14]:=#;t15
+@d t13(#)==cur_name[13]:=#;t14
+@d t12(#)==cur_name[12]:=#;t13
+@d t11(#)==cur_name[11]:=#;t12
+@d t10(#)==cur_name[10]:=#;t11
+@d t9(#)==cur_name[9]:=#;t10
+@d t8(#)==cur_name[8]:=#;t9
+@d t7(#)==cur_name[7]:=#;t8
+@d t6(#)==cur_name[6]:=#;t7
+@d t5(#)==cur_name[5]:=#;t6
+@d t4(#)==cur_name[4]:=#;t5
+@d t3(#)==cur_name[3]:=#;t4
+@d t2(#)==cur_name[2]:=#;t3
+@d t1(#)==cur_name[1]:=#;t2
+@d load3==name_length:=3;t18
+@d load4==name_length:=4;t17
+@d load5==name_length:=5;t16
+@d load6==name_length:=6;t15
+@d load7==name_length:=7;t14
+@d load8==name_length:=8;t13
+@d load9==name_length:=9;t12
+@d load10==name_length:=10;t11
+@d load11==name_length:=11;t10
+@d load12==name_length:=12;t9
+@d load13==name_length:=13;t8
+@d load14==name_length:=14;t7
+@d load15==name_length:=15;t6
+@d load16==name_length:=16;t5
+@d load17==name_length:=17;t4
+@d load18==name_length:=18;t3
+@d load19==name_length:=19;t2
+@d load20==name_length:=20;t1
+
+@ (Thank goodness for keyboard macros in the text editor used to create this
+\.{WEB} file.)
+
+@<Enter all the \.{PL} names and their equivalents,
+ except the parameter names@>=
+equiv[0]:=comment_code; {this is used after unknown keywords}
+load8("C")("H")("E")("C")("K")("S")("U")("M")(check_sum_code);@/
+load10("D")("E")("S")("I")("G")("N")("S")("I")("Z")("E")(design_size_code);@/
+load11("D")("E")("S")("I")("G")("N")
+ ("U")("N")("I")("T")("S")(design_units_code);@/
+load12("C")("O")("D")("I")("N")("G")
+ ("S")("C")("H")("E")("M")("E")(coding_scheme_code);@/
+load6("F")("A")("M")("I")("L")("Y")(family_code);@/
+load4("F")("A")("C")("E")(face_code);@/
+load16("S")("E")("V")("E")("N")("B")("I")("T")@/@t\hskip2em@>
+ ("S")("A")("F")("E")("F")("L")("A")("G")(seven_bit_safe_flag_code);@/
+load6("H")("E")("A")("D")("E")("R")(header_code);@/
+load9("F")("O")("N")("T")("D")("I")("M")("E")("N")(font_dimen_code);@/
+load8("L")("I")("G")("T")("A")("B")("L")("E")(lig_table_code);@/
+load12("B")("O")("U")("N")("D")("A")("R")("Y")("C")("H")("A")("R")
+ (boundary_char_code);@/
+load9("C")("H")("A")("R")("A")("C")("T")("E")("R")(character_code);@/
+load9("P")("A")("R")("A")("M")("E")("T")("E")("R")(parameter_code);@/
+load6("C")("H")("A")("R")("W")("D")(char_wd_code);@/
+load6("C")("H")("A")("R")("H")("T")(char_ht_code);@/
+load6("C")("H")("A")("R")("D")("P")(char_dp_code);@/
+load6("C")("H")("A")("R")("I")("C")(char_ic_code);@/
+load10("N")("E")("X")("T")("L")("A")("R")("G")("E")("R")(next_larger_code);@/
+load7("V")("A")("R")("C")("H")("A")("R")(var_char_code);@/
+load3("T")("O")("P")(var_char_code+1);@/
+load3("M")("I")("D")(var_char_code+2);@/
+load3("B")("O")("T")(var_char_code+3);@/
+load3("R")("E")("P")(var_char_code+4);@/
+load3("E")("X")("T")(var_char_code+4); {compatibility with older \.{PL} format}
+load7("C")("O")("M")("M")("E")("N")("T")(comment_code);@/
+load5("L")("A")("B")("E")("L")(label_code);@/
+load4("S")("T")("O")("P")(stop_code);@/
+load4("S")("K")("I")("P")(skip_code);@/
+load3("K")("R")("N")(krn_code);@/
+load3("L")("I")("G")(lig_code);@/
+load4("/")("L")("I")("G")(lig_code+2);@/
+load5("/")("L")("I")("G")(">")(lig_code+6);@/
+load4("L")("I")("G")("/")(lig_code+1);@/
+load5("L")("I")("G")("/")(">")(lig_code+5);@/
+load5("/")("L")("I")("G")("/")(lig_code+3);@/
+load6("/")("L")("I")("G")("/")(">")(lig_code+7);@/
+load7("/")("L")("I")("G")("/")(">")(">")(lig_code+11);@/
+
+@ \.{VPL} files may contain the following in addition to the \.{PL} names.
+
+@<Enter all the \.{VPL} names@>=
+load6("V")("T")("I")("T")("L")("E")(virtual_title_code);@/
+load7("M")("A")("P")("F")("O")("N")("T")(map_font_code);@/
+load3("M")("A")("P")(map_code);@/
+load8("F")("O")("N")("T")("N")("A")("M")("E")(font_name_code);@/
+load8("F")("O")("N")("T")("A")("R")("E")("A")(font_area_code);@/
+load12("F")("O")("N")("T")
+ ("C")("H")("E")("C")("K")("S")("U")("M")(font_checksum_code);@/
+load6("F")("O")("N")("T")("A")("T")(font_at_code);@/
+load9("F")("O")("N")("T")("D")("S")("I")("Z")("E")(font_dsize_code);@/
+load10("S")("E")("L")("E")("C")("T")("F")("O")("N")("T")(select_font_code);@/
+load7("S")("E")("T")("C")("H")("A")("R")(set_char_code);@/
+load7("S")("E")("T")("R")("U")("L")("E")(set_rule_code);@/
+load9("M")("O")("V")("E")("R")("I")("G")("H")("T")(move_right_code);@/
+load8("M")("O")("V")("E")("L")("E")("F")("T")(move_right_code+1);@/
+load8("M")("O")("V")("E")("D")("O")("W")("N")(move_down_code);@/
+load6("M")("O")("V")("E")("U")("P")(move_down_code+1);@/
+load4("P")("U")("S")("H")(push_code);@/
+load3("P")("O")("P")(pop_code);@/
+load7("S")("P")("E")("C")("I")("A")("L")(special_code);@/
+load10("S")("P")("E")("C")("I")("A")("L")("H")("E")("X")(special_hex_code);@/
+
+@ @<Enter the parameter names@>=
+load5("S")("L")("A")("N")("T")(parameter_code+1);@/
+load5("S")("P")("A")("C")("E")(parameter_code+2);@/
+load7("S")("T")("R")("E")("T")("C")("H")(parameter_code+3);@/
+load6("S")("H")("R")("I")("N")("K")(parameter_code+4);@/
+load7("X")("H")("E")("I")("G")("H")("T")(parameter_code+5);@/
+load4("Q")("U")("A")("D")(parameter_code+6);@/
+load10("E")("X")("T")("R")("A")("S")("P")("A")("C")("E")(parameter_code+7);@/
+load4("N")("U")("M")("1")(parameter_code+8);@/
+load4("N")("U")("M")("2")(parameter_code+9);@/
+load4("N")("U")("M")("3")(parameter_code+10);@/
+load6("D")("E")("N")("O")("M")("1")(parameter_code+11);@/
+load6("D")("E")("N")("O")("M")("2")(parameter_code+12);@/
+load4("S")("U")("P")("1")(parameter_code+13);@/
+load4("S")("U")("P")("2")(parameter_code+14);@/
+load4("S")("U")("P")("3")(parameter_code+15);@/
+load4("S")("U")("B")("1")(parameter_code+16);@/
+load4("S")("U")("B")("2")(parameter_code+17);@/
+load7("S")("U")("P")("D")("R")("O")("P")(parameter_code+18);@/
+load7("S")("U")("B")("D")("R")("O")("P")(parameter_code+19);@/
+load6("D")("E")("L")("I")("M")("1")(parameter_code+20);@/
+load6("D")("E")("L")("I")("M")("2")(parameter_code+21);@/
+load10("A")("X")("I")("S")("H")("E")("I")("G")("H")("T")(parameter_code+22);@/
+load20("D")("E")("F")("A")("U")("L")("T")("R")("U")("L")("E")@/@t\hskip2em@>
+ ("T")("H")("I")("C")("K")("N")("E")("S")("S")(parameter_code+8);@/
+load13("B")("I")("G")("O")("P")
+ ("S")("P")("A")("C")("I")("N")("G")("1")(parameter_code+9);@/
+load13("B")("I")("G")("O")("P")
+ ("S")("P")("A")("C")("I")("N")("G")("2")(parameter_code+10);@/
+load13("B")("I")("G")("O")("P")
+ ("S")("P")("A")("C")("I")("N")("G")("3")(parameter_code+11);@/
+load13("B")("I")("G")("O")("P")
+ ("S")("P")("A")("C")("I")("N")("G")("4")(parameter_code+12);@/
+load13("B")("I")("G")("O")("P")
+ ("S")("P")("A")("C")("I")("N")("G")("5")(parameter_code+13);@/
+
+@ When a left parenthesis has been scanned, the following routine
+is used to interpret the keyword that follows, and to store the
+equivalent value in |cur_code|.
+
+@p procedure get_name;
+begin incr(loc); incr(level); {pass the left parenthesis}
+cur_char:=" ";
+while cur_char=" " do get_next;
+if (cur_char>")")or(cur_char<"(") then decr(loc); {back up one character}
+name_length:=0; get_keyword_char; {prepare to scan the name}
+while cur_char<>" " do
+ begin if name_length=longest_name then cur_name[1]:="X" {force error}
+ else incr(name_length);
+ cur_name[name_length]:=cur_char;
+ get_keyword_char;
+ end;
+lookup;
+if name_ptr=0 then err_print('Sorry, I don''t know that property name');
+@.Sorry, I don't know...@>
+cur_code:=equiv[name_ptr];
+end;
+
+@* Scanning numeric data.
+The next thing we need is a trio of subroutines to read the one-byte,
+four-byte, and real numbers that may appear as property values.
+These subroutines are careful to stick to numbers between $-2^{31}$
+and $2^{31}-1$, inclusive, so that a computer with two's complement
+32-bit arithmetic will not be interrupted by overflow.
+
+@ The first number scanner, which returns a one-byte value, surely has
+no problems of arithmetic overflow.
+
+@p function get_byte:byte; {scans a one-byte property value}
+var acc:integer; {an accumulator}
+@!t:ASCII_code; {the type of value to be scanned}
+begin repeat get_next;
+until cur_char<>" "; {skip the blanks before the type code}
+t:=cur_char; acc:=0;
+repeat get_next;
+until cur_char<>" "; {skip the blanks after the type code}
+if t="C" then @<Scan an ASCII character code@>
+else if t="D" then @<Scan a small decimal number@>
+else if t="O" then @<Scan a small octal number@>
+else if t="H" then @<Scan a small hexadecimal number@>
+else if t="F" then @<Scan a face code@>
+else skip_error('You need "C" or "D" or "O" or "H" or "F" here');
+@.You need "C" or "D" ...here@>
+cur_char:=" "; get_byte:=acc;
+end;
+
+@ The |get_next| routine converts lower case to upper case, but it leaves
+the character in the buffer, so we can unconvert it.
+
+@<Scan an ASCII...@>=
+if (cur_char>=@'41)and(cur_char<=@'176)and
+ ((cur_char<"(")or(cur_char>")")) then
+ acc:=xord[buffer[loc]]
+else skip_error('"C" value must be standard ASCII and not a paren')
+@:C value}\.{"C" value must be...@>
+
+@ @<Scan a small dec...@>=
+begin while (cur_char>="0")and(cur_char<="9") do
+ begin acc:=acc*10+cur_char-"0";
+ if acc>255 then
+ begin skip_error('This value shouldn''t exceed 255');
+@.This value shouldn't...@>
+ acc:=0; cur_char:=" ";
+ end
+ else get_next;
+ end;
+backup;
+end
+
+@ @<Scan a small oct...@>=
+begin while (cur_char>="0")and(cur_char<="7") do
+ begin acc:=acc*8+cur_char-"0";
+ if acc>255 then
+ begin skip_error('This value shouldn''t exceed ''377');
+@.This value shouldn't...@>
+ acc:=0; cur_char:=" ";
+ end
+ else get_next;
+ end;
+backup;
+end
+
+@ @<Scan a small hex...@>=
+begin while ((cur_char>="0")and(cur_char<="9"))or
+ ((cur_char>="A")and(cur_char<="F")) do
+ begin if cur_char>="A" then cur_char:=cur_char+"0"+10-"A";
+ acc:=acc*16+cur_char-"0";
+ if acc>255 then
+ begin skip_error('This value shouldn''t exceed "FF');
+@.This value shouldn't...@>
+ acc:=0; cur_char:=" ";
+ end
+ else get_next;
+ end;
+backup;
+end
+
+@ @<Scan a face...@>=
+begin if cur_char="B" then acc:=2
+else if cur_char="L" then acc:=4
+else if cur_char<>"M" then acc:=18;
+get_next;
+if cur_char="I" then incr(acc)
+else if cur_char<>"R" then acc:=18;
+get_next;
+if cur_char="C" then acc:=acc+6
+else if cur_char="E" then acc:=acc+12
+else if cur_char<>"R" then acc:=18;
+if acc>=18 then
+ begin skip_error('Illegal face code, I changed it to MRR');
+@.Illegal face code...@>
+ acc:=0;
+ end;
+end
+
+@ The routine that scans a four-byte value puts its output into |cur_bytes|,
+which is a record containing (yes, you guessed it) four bytes.
+
+@<Types...@>=
+@!four_bytes=record @!b0:byte;@+@!b1:byte;@+@!b2:byte;@+@!b3:byte;@+end;
+
+@ @d c0==cur_bytes.b0
+@d c1==cur_bytes.b1
+@d c2==cur_bytes.b2
+@d c3==cur_bytes.b3
+
+@<Glob...@>=
+@!cur_bytes:four_bytes; {a four-byte accumulator}
+@!zero_bytes:four_bytes; {four bytes all zero}
+
+@ @<Set init...@>=
+zero_bytes.b0:=0; zero_bytes.b1:=0; zero_bytes.b2:=0; zero_bytes.b3:=0;
+
+@ Since the |get_four_bytes| routine is used very infrequently, no attempt
+has been made to make it fast; we only want it to work.
+
+@p procedure get_four_bytes; {scans an unsigned constant and sets |four_bytes|}
+var c:integer; {local two-byte accumulator}
+@!r:integer; {radix}
+begin repeat get_next;
+until cur_char<>" "; {skip the blanks before the type code}
+r:=0; cur_bytes:=zero_bytes; {start with the accumulator zero}
+if cur_char="H" then r:=16
+else if cur_char="O" then r:=8
+else if cur_char="D" then r:=10
+else skip_error('Decimal ("D"), octal ("O"), or hex ("H") value needed here');
+@.Decimal ("D"), octal ("O"), or hex...@>
+if r>0 then
+ begin repeat get_next;
+ until cur_char<>" "; {skip the blanks after the type code}
+ while ((cur_char>="0")and(cur_char<="9"))or@|
+ ((cur_char>="A")and(cur_char<="F")) do
+ @<Multiply by |r|, add |cur_char-"0"|, and |get_next|@>;
+ end;
+end;
+
+@ @<Multiply by |r|...@>=
+begin if cur_char>="A" then cur_char:=cur_char+"0"+10-"A";
+if cur_char>="0"+r then skip_error('Illegal digit')
+@.Illegal digit@>
+else begin c:=c3*r+cur_char-"0"; c3:=c mod 256;@/
+ c:=c2*r+c div 256; c2:=c mod 256;@/
+ c:=c1*r+c div 256; c1:=c mod 256;@/
+ c:=c0*r+c div 256;
+ if c<256 then c0:=c
+ else begin cur_bytes:=zero_bytes;
+ if r=8 then
+ skip_error('Sorry, the maximum octal value is O 37777777777')
+@.Sorry, the maximum...@>
+ else if r=10 then
+ skip_error('Sorry, the maximum decimal value is D 4294967295')
+ else skip_error('Sorry, the maximum hex value is H FFFFFFFF');
+ end;
+ get_next;
+ end;
+end
+
+@ The remaining scanning routine is the most interesting. It scans a real
+constant and returns the nearest |fix_word| approximation to that constant.
+A |fix_word| is a 32-bit integer that represents a real value that
+has been multiplied by $2^{20}$. Since \.{VPtoVF} restricts the magnitude
+of reals to 2048, the |fix_word| will have a magnitude less than $2^{31}$.
+
+@d unity==@'4000000 {$2^{20}$, the |fix_word| 1.0}
+
+@<Types...@>=
+@!fix_word=integer; {a scaled real value with 20 bits of fraction}
+
+@ When a real value is desired, we might as well treat `\.D' and `\.R'
+formats as if they were identical.
+
+@p function get_fix:fix_word; {scans a real property value}
+var negative:boolean; {was there a minus sign?}
+@!acc:integer; {an accumulator}
+@!int_part:integer; {the integer part}
+@!j:0..7; {the number of decimal places stored}
+begin repeat get_next;
+until cur_char<>" "; {skip the blanks before the type code}
+negative:=false; acc:=0; {start with the accumulators zero}
+if (cur_char<>"R")and(cur_char<>"D") then
+ skip_error('An "R" or "D" value is needed here')
+@.An "R" or "D" ... needed here@>
+else begin @<Scan the blanks and/or signs after the type code@>;
+ while (cur_char>="0") and (cur_char<="9") do
+ @<Multiply by 10, add |cur_char-"0"|, and |get_next|@>;
+ int_part:=acc; acc:=0;
+ if cur_char="." then @<Scan the fraction part and put it in |acc|@>;
+ if (acc>=unity)and(int_part=2047) then
+ skip_error('Real constants must be less than 2048')
+@.Real constants must be...@>
+ else acc:=int_part*unity+acc;
+ end;
+if negative then get_fix:=-acc@+else get_fix:=acc;
+end;
+
+@ @<Scan the blanks...@>=
+repeat get_next;
+if cur_char="-" then
+ begin cur_char:=" "; negative:=not negative;
+ end
+else if cur_char="+" then cur_char:=" ";
+until cur_char<>" "
+
+@ @<Multiply by 10...@>=
+begin acc:=acc*10+cur_char-"0";
+if acc>=2048 then
+ begin skip_error('Real constants must be less than 2048');
+@.Real constants must be...@>
+ acc:=0; cur_char:=" ";
+ end
+else get_next;
+end
+
+@ To scan the fraction $.d_1d_2\ldots\,$, we keep track of up to seven
+of the digits $d_j$. A correct result is obtained if we first compute
+$f^\prime=\lfloor 2^{21}(d_1\ldots d_j)/10^j\rfloor$, after which
+$f=\lfloor(f^\prime+1)/2\rfloor$. It is possible to have $f=1.0$.
+
+@<Glob...@>=
+@!fraction_digits:array[1..7] of integer; {$2^{21}$ times $d_j$}
+
+@ @<Scan the frac...@>=
+begin j:=0; get_next;
+while (cur_char>="0")and(cur_char<="9") do
+ begin if j<7 then
+ begin incr(j); fraction_digits[j]:=@'10000000*(cur_char-"0");
+ end;
+ get_next;
+ end;
+acc:=0;
+while j>0 do
+ begin acc:=fraction_digits[j]+(acc div 10); decr(j);
+ end;
+acc:=(acc+10) div 20;
+end
+
+@* Storing the property values.
+When property values have been found, they are squirreled away in a bunch
+of arrays. The header information is unpacked into bytes in an array
+called |header_bytes|. The ligature/kerning program is stored in an array
+of type |four_bytes|.
+Another |four_bytes| array holds the specifications of extensible characters.
+The kerns and parameters are stored in separate arrays of |fix_word| values.
+Virtual font data goes into an array |vf| of single-byte values.
+
+We maintain information about at most 256 local fonts. (If this is inadequate,
+several arrays need to be made longer and we need to output font definitions
+that go beyond |fnt1| and |fnt_def1| in the \.{VF} file.)
+
+Instead of storing the design size in the header array, we will keep it
+in a |fix_word| variable until the last minute. The number of units in the
+design size is also kept in a |fix_word|.
+
+@<Glob...@>=
+@!header_bytes:array[header_index] of byte; {the header block}
+@!header_ptr:header_index; {the number of header bytes in use}
+@!design_size:fix_word; {the design size}
+@!design_units:fix_word; {reciprocal of the scaling factor}
+@!frozen_du:boolean; {have we used |design_units| irrevocably?}
+@!seven_bit_safe_flag:boolean; {does the file claim to be seven-bit-safe?}
+@!lig_kern:array[0..max_lig_steps] of four_bytes; {the ligature program}
+@!nl:0..32767; {the number of ligature/kern instructions so far}
+@!min_nl:0..32767; {the final value of |nl| must be at least this}
+@!kern:array[0..max_kerns] of fix_word; {the distinct kerning amounts}
+@!nk:0..max_kerns; {the number of entries of |kern|}
+@!exten:array[0..255] of four_bytes; {extensible character specs}
+@!ne:0..256; {the number of extensible characters}
+@!param:array[1..max_param_words] of fix_word; {\.{FONTDIMEN} parameters}
+@!np:0..max_param_words; {the largest parameter set nonzero}
+@!check_sum_specified:boolean; {did the user name the check sum?}
+@!bchar:0..256; {the right boundary character, or 256 if unspecified}
+@!vf:array[0..vf_size] of byte; {stored bytes for \.{VF} file}
+@!vf_ptr:0..vf_size; {first unused location in |vf|}
+@!vtitle_start:0..vf_size; {starting location of \.{VTITLE} string}
+@!vtitle_length:byte; {length of \.{VTITLE} string}
+@!packet_start:array[byte] of 0..vf_size;
+ {beginning location of character packet}
+@!packet_length:array[byte] of integer; {length of character packet}
+@!font_ptr:0..256; {number of distinct local fonts seen}
+@!cur_font:0..256; {number of the current local font}
+@!fname_start:array[byte] of 0..vf_size; {beginning of local font name}
+@!fname_length:array[byte] of byte; {length of local font name}
+@!farea_start:array[byte] of 0..vf_size; {beginning of local font area}
+@!farea_length:array[byte] of byte; {length of local font area}
+@!font_checksum:array[byte] of four_bytes; {local font checksum}
+@!font_number:array[0..256] of four_bytes; {local font id number}
+@!font_at:array[byte] of fix_word; {local font ``at size''}
+@!font_dsize:array[byte] of fix_word; {local font design size}
+
+@ @<Types...@>=
+@!header_index=0..max_header_bytes;
+@!indx=0..@'77777;
+
+@ @<Local...@>=
+@!d:header_index; {an index into |header_bytes|}
+
+@ We start by setting up the default values.
+
+@d check_sum_loc=0
+@d design_size_loc=4
+@d coding_scheme_loc=8
+@d family_loc=coding_scheme_loc+40
+@d seven_flag_loc=family_loc+20
+@d face_loc=seven_flag_loc+3
+
+@<Set init...@>=
+for d:=0 to 18*4-1 do header_bytes[d]:=0;
+header_bytes[8]:=11; header_bytes[9]:="U";
+header_bytes[10]:="N";
+header_bytes[11]:="S";
+header_bytes[12]:="P";
+header_bytes[13]:="E";
+header_bytes[14]:="C";
+header_bytes[15]:="I";
+header_bytes[16]:="F";
+header_bytes[17]:="I";
+header_bytes[18]:="E";
+header_bytes[19]:="D";
+@.UNSPECIFIED@>
+for d:=family_loc to family_loc+11 do header_bytes[d]:=header_bytes[d-40];
+design_size:=10*unity; design_units:=unity; frozen_du:=false;
+seven_bit_safe_flag:=false;@/
+header_ptr:=18*4; nl:=0; min_nl:=0; nk:=0; ne:=0; np:=0;@/
+check_sum_specified:=false; bchar:=256;@/
+vf_ptr:=0; vtitle_start:=0; vtitle_length:=0; font_ptr:=0;
+for k:=0 to 255 do packet_start[k]:=vf_size;
+for k:=0 to 127 do packet_length[k]:=1;
+for k:=128 to 255 do packet_length[k]:=2;
+
+@ Most of the dimensions, however, go into the |memory| array. There are
+at most 257 widths, 257 heights, 257 depths, and 257 italic corrections,
+since the value 0 is required but it need not be used. So |memory| has room
+for 1028 entries, each of which is a |fix_word|. An auxiliary table called
+|link| is used to link these words together in linear lists, so that
+sorting and other operations can be done conveniently.
+
+We also add four ``list head'' words to the |memory| and |link| arrays;
+these are in locations |width| through |italic|, i.e., 1 through 4.
+For example, |link[height]| points to the smallest element in
+the sorted list of distinct heights that have appeared so far, and
+|memory[height]| is the number of distinct heights.
+
+@d mem_size=1028+4 {number of nonzero memory addresses}
+
+@<Types...@>=
+@!pointer=0..mem_size; {an index into memory}
+
+@ The arrays |char_wd|, |char_ht|, |char_dp|, and |char_ic| contain
+pointers to the |memory| array entries where the corresponding dimensions
+appear. Two other arrays, |char_tag| and |char_remainder|, hold
+the other information that \.{TFM} files pack into a |char_info_word|.
+
+@d no_tag=0 {vanilla character}
+@d lig_tag=1 {character has a ligature/kerning program}
+@d list_tag=2 {character has a successor in a charlist}
+@d ext_tag=3 {character is extensible}
+@d bchar_label==char_remainder[256]
+ {beginning of ligature program for left boundary}
+
+@<Glob...@>=
+@!memory:array[pointer] of fix_word; {character dimensions and kerns}
+@!mem_ptr:pointer; {largest |memory| word in use}
+@!link:array[pointer] of pointer; {to make lists of |memory| items}
+@!char_wd:array[byte] of pointer; {pointers to the widths}
+@!char_ht:array[byte] of pointer; {pointers to the heights}
+@!char_dp:array[byte] of pointer; {pointers to the depths}
+@!char_ic:array[byte] of pointer; {pointers to italic corrections}
+@!char_tag:array[byte] of no_tag..ext_tag; {character tags}
+@!char_remainder:array[0..256] of 0..65535; {pointers to ligature labels,
+ next larger characters, or extensible characters}
+
+@ @<Local...@>=
+@!c:byte; {runs through all character codes}
+
+@ @<Set init...@>=
+bchar_label:=@'77777;
+for c:=0 to 255 do
+ begin char_wd[c]:=0; char_ht[c]:=0; char_dp[c]:=0; char_ic[c]:=0;@/
+ char_tag[c]:=no_tag; char_remainder[c]:=0;
+ end;
+memory[0]:=@'17777777777; {an ``infinite'' element at the end of the lists}
+memory[width]:=0; link[width]:=0; {width list is empty}
+memory[height]:=0; link[height]:=0; {height list is empty}
+memory[depth]:=0; link[depth]:=0; {depth list is empty}
+memory[italic]:=0; link[italic]:=0; {italic list is empty}
+mem_ptr:=italic;
+
+@ As an example of these data structures, let us consider the simple
+routine that inserts a potentially new element into one of the dimension
+lists. The first parameter indicates the list head (i.e., |h=width| for
+the width list, etc.); the second parameter is the value that is to be
+inserted into the list if it is not already present. The procedure
+returns the value of the location where the dimension appears in |memory|.
+The fact that |memory[0]| is larger than any legal dimension makes the
+algorithm particularly short.
+
+We do have to handle two somewhat subtle situations. A width of zero must be
+put into the list, so that a zero-width character in the font will not appear
+to be nonexistent (i.e., so that its |char_wd| index will not be zero), but
+this does not need to be done for heights, depths, or italic corrections.
+Furthermore, it is necessary to test for memory overflow even though we
+have provided room for the maximum number of different dimensions in any
+legal font, since the \.{VPL} file might foolishly give any number of
+different sizes to the same character.
+
+@p function sort_in(@!h:pointer;@!d:fix_word):pointer; {inserts into list}
+var p:pointer; {the current node of interest}
+begin if (d=0)and(h<>width) then sort_in:=0
+else begin p:=h;
+ while d>=memory[link[p]] do p:=link[p];
+ if (d=memory[p])and(p<>h) then sort_in:=p
+ else if mem_ptr=mem_size then
+ begin err_print('Memory overflow: more than 1028 widths, etc');
+@.Memory overflow...@>
+ print_ln('Congratulations! It''s hard to make this error.');
+ sort_in:=p;
+ end
+ else begin incr(mem_ptr); memory[mem_ptr]:=d;
+ link[mem_ptr]:=link[p]; link[p]:=mem_ptr; incr(memory[h]);
+ sort_in:=mem_ptr;
+ end;
+ end;
+end;
+
+@ When these lists of dimensions are eventually written to the \.{TFM}
+file, we may have to do some rounding of values, because the \.{TFM} file
+allows at most 256 widths, 16 heights, 16 depths, and 64 italic
+corrections. The following procedure takes a given list head |h| and a
+given dimension |d|, and returns the minimum $m$ such that the elements of
+the list can be covered by $m$ intervals of width $d$. It also sets
+|next_d| to the smallest value $d^\prime>d$ such that the covering found
+by this procedure would be different. In particular, if $d=0$ it computes
+the number of elements of the list, and sets |next_d| to the smallest
+distance between two list elements. (The covering by intervals of width
+|next_d| is not guaranteed to have fewer than $m$ elements, but in practice
+this seems to happen most of the time.)
+
+@<Glob...@>=
+@!next_d:fix_word; {the next larger interval that is worth trying}
+
+@ Once again we can make good use of the fact that |memory[0]| is ``infinite.''
+
+@p function min_cover(@!h:pointer;@!d:fix_word):integer;
+var p:pointer; {the current node of interest}
+@!l:fix_word; {the least element covered by the current interval}
+@!m:integer; {the current size of the cover being generated}
+begin m:=0; p:=link[h]; next_d:=memory[0];
+while p<>0 do
+ begin incr(m); l:=memory[p];
+ while memory[link[p]]<=l+d do p:=link[p];
+ p:=link[p];
+ if memory[p]-l<next_d then next_d:=memory[p]-l;
+ end;
+min_cover:=m;
+end;
+
+@ The following procedure uses |min_cover| to determine the smallest $d$
+such that a given list can be covered with at most a given number of
+intervals.
+
+@p function shorten(@!h:pointer;m:integer):fix_word; {finds best way to round}
+var d:fix_word; {the current trial interval length}
+@!k:integer; {the size of a minimum cover}
+begin if memory[h]>m then
+ begin excess:=memory[h]-m;
+ k:=min_cover(h,0); d:=next_d; {now the answer is at least |d|}
+ repeat d:=d+d; k:=min_cover(h,d);
+ until k<=m; {first we ascend rapidly until finding the range}
+ d:=d div 2; k:=min_cover(h,d); {now we run through the feasible steps}
+ while k>m do
+ begin d:=next_d; k:=min_cover(h,d);
+ end;
+ shorten:=d;
+ end
+else shorten:=0;
+end;
+
+@ When we are nearly ready to output the \.{TFM} file, we will set
+|index[p]:=k| if the dimension in |memory[p]| is being rounded to the
+|k|th element of its list.
+
+@<Glob...@>=
+@!index:array[pointer] of byte;
+@!excess:byte; {number of words to remove, if list is being shortened}
+
+@ Here is the procedure that sets the |index| values. It also shortens
+the list so that there is only one element per covering interval;
+the remaining elements are the midpoints of their clusters.
+
+@p procedure set_indices(@!h:pointer;@!d:fix_word); {reduces and indexes a list}
+var p:pointer; {the current node of interest}
+@!q:pointer; {trails one step behind |p|}
+@!m:byte; {index number of nodes in the current interval}
+@!l:fix_word; {least value in the current interval}
+begin q:=h; p:=link[q]; m:=0;
+while p<>0 do
+ begin incr(m); l:=memory[p]; index[p]:=m;
+ while memory[link[p]]<=l+d do
+ begin p:=link[p]; index[p]:=m; decr(excess);
+ if excess=0 then d:=0;
+ end;
+ link[q]:=p; memory[p]:=l+(memory[p]-l) div 2; q:=p; p:=link[p];
+ end;
+memory[h]:=m;
+end;
+
+@* The input phase.
+We're ready now to read and parse the \.{VPL} file, storing property
+values as we go.
+
+@<Glob...@>=
+@!c:byte; {the current character or byte being processed}
+@!x:fix_word; {current dimension of interest}
+@!k:integer; {general-purpose index}
+
+@ @<Read all the input@>=
+cur_char:=" ";
+repeat while cur_char=" " do get_next;
+if cur_char="(" then @<Read a font property value@>
+else if (cur_char=")")and not input_has_ended then
+ begin err_print('Extra right parenthesis');
+ incr(loc); cur_char:=" ";
+ end
+@.Extra right parenthesis@>
+else if not input_has_ended then junk_error;
+until input_has_ended
+
+@ The |junk_error| routine just referred to is called when something
+appears in the forbidden area between properties of a property list.
+
+@p procedure junk_error; {gets past no man's land}
+begin err_print('There''s junk here that is not in parentheses');
+@.There's junk here...@>
+skip_to_paren;
+end;
+
+@ For each font property, we are supposed to read the data from the
+left parenthesis that is the current value of |cur_char| to the right
+parenthesis that matches it in the input. The main complication is
+to recover with reasonable grace from various error conditions that might arise.
+
+@<Read a font property value@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if cur_code>character_code then
+ flush_error('This property name doesn''t belong on the outer level')
+@.This property name doesn't belong...@>
+else begin @<Read the font property value specified by |cur_code|@>;
+ finish_the_property;
+ end;
+end
+
+@ @<Read the font property value spec...@>=
+case cur_code of
+check_sum_code: begin check_sum_specified:=true; read_four_bytes(check_sum_loc);
+ end;
+design_size_code: @<Read the design size@>;
+design_units_code: @<Read the design units@>;
+coding_scheme_code: read_BCPL(coding_scheme_loc,40);
+family_code: read_BCPL(family_loc,20);
+face_code:header_bytes[face_loc]:=get_byte;
+seven_bit_safe_flag_code: @<Read the seven-bit-safe flag@>;
+header_code: @<Read an indexed header word@>;
+font_dimen_code: @<Read font parameter list@>;
+lig_table_code: read_lig_kern;
+boundary_char_code: bchar:=get_byte;
+virtual_title_code: begin vtitle_start:=vf_ptr; copy_to_end_of_item;
+ if vf_ptr>vtitle_start+255 then
+ begin err_print('VTITLE clipped to 255 characters'); vtitle_length:=255;
+@.VTITLE clipped...@>
+ end
+ else vtitle_length:=vf_ptr-vtitle_start;
+ end;
+map_font_code:@<Read a local font list@>;
+character_code: read_char_info;
+end
+
+@ The |case| statement just given makes use of three subroutines that we
+haven't defined yet. The first of these puts a 32-bit octal quantity
+into four specified bytes of the header block.
+
+@p procedure read_four_bytes(l:header_index);
+begin get_four_bytes;
+header_bytes[l]:=c0;
+header_bytes[l+1]:=c1;
+header_bytes[l+2]:=c2;
+header_bytes[l+3]:=c3;
+end;
+
+@ The second little procedure is used to scan a string and to store it in
+the ``{\mc BCPL} format'' required by \.{TFM} files. The string is supposed
+to contain at most |n| bytes, including the first byte (which holds the
+length of the rest of the string).
+
+@p procedure read_BCPL(l:header_index;n:byte);
+var k:header_index;
+begin k:=l;
+while cur_char=" " do get_next;
+while (cur_char<>"(")and(cur_char<>")") do
+ begin if k<l+n then incr(k);
+ if k<l+n then header_bytes[k]:=cur_char;
+ get_next;
+ end;
+if k=l+n then
+ begin err_print('String is too long; its first ',n-1:1,
+@.String is too long...@>
+ ' characters will be kept'); decr(k);
+ end;
+header_bytes[l]:=k-l;
+while k<l+n-1 do {tidy up the remaining bytes by setting them to nulls}
+ begin incr(k); header_bytes[k]:=0;
+ end;
+end;
+
+@ @<Read the design size@>=
+begin next_d:=get_fix;
+if next_d<unity then
+ err_print('The design size must be at least 1')
+@.The design size must...@>
+else design_size:=next_d;
+end
+
+@ @<Read the design units@>=
+begin next_d:=get_fix;
+if next_d<=0 then
+ err_print('The number of units per design size must be positive')
+@.The number of units...@>
+else if frozen_du then
+ err_print('Sorry, it''s too late to change the design units')
+@.Sorry, it's too late...@>
+else design_units:=next_d;
+end
+
+@ @<Read the seven-bit-safe...@>=
+begin while cur_char=" " do get_next;
+if cur_char="T" then seven_bit_safe_flag:=true
+else if cur_char="F" then seven_bit_safe_flag:=false
+else err_print('The flag value should be "TRUE" or "FALSE"');
+@.The flag value should be...@>
+skip_to_paren;
+end
+
+@ @<Read an indexed header word@>=
+begin c:=get_byte;
+if c<18 then skip_error('HEADER indices should be 18 or more')
+@.HEADER indices...@>
+else if 4*c+4>max_header_bytes then
+ skip_error('This HEADER index is too big for my present table size')
+@.This HEADER index is too big...@>
+else begin while header_ptr<4*c+4 do
+ begin header_bytes[header_ptr]:=0; incr(header_ptr);
+ end;
+ read_four_bytes(4*c);
+ end;
+end
+
+@ The remaining kinds of font property values that need to be read are
+those that involve property lists on higher levels. Each of these has a
+loop similar to the one that was used at level zero. Then we put the
+right parenthesis back so that `|finish_the_property|' will be happy;
+there is probably a more elegant way to do this.
+
+@d finish_inner_property_list==begin decr(loc); incr(level); cur_char:=")";
+ end
+
+@<Read font parameter list@>=
+begin while level=1 do
+ begin while cur_char=" " do get_next;
+ if cur_char="(" then @<Read a parameter value@>
+ else if cur_char=")" then skip_to_end_of_item
+ else junk_error;
+ end;
+finish_inner_property_list;
+end
+
+@ @<Read a parameter value@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if (cur_code<parameter_code)or(cur_code>=char_wd_code) then
+ flush_error('This property name doesn''t belong in a FONTDIMEN list')
+@.This property name doesn't belong...@>
+else begin if cur_code=parameter_code then c:=get_byte
+ else c:=cur_code-parameter_code;
+ if c=0 then flush_error('PARAMETER index must not be zero')
+@.PARAMETER index must not...@>
+ else if c>max_param_words then
+ flush_error('This PARAMETER index is too big for my present table size')
+@.This PARAMETER index is too big...@>
+ else begin while np<c do
+ begin incr(np); param[np]:=0;
+ end;
+ param[c]:=get_fix;
+ finish_the_property;
+ end;
+ end;
+end
+
+@ @d numbers_differ==(font_number[cur_font].b3<>font_number[font_ptr].b3)or@|
+(font_number[cur_font].b2<>font_number[font_ptr].b2)or@|
+(font_number[cur_font].b1<>font_number[font_ptr].b1)or@|
+(font_number[cur_font].b0<>font_number[font_ptr].b0)
+
+@<Read a local font list@>=
+begin get_four_bytes; font_number[font_ptr]:=cur_bytes; cur_font:=0;
+while numbers_differ do incr(cur_font);
+if cur_font=font_ptr then {it's a new font number}
+ if font_ptr<256 then @<Initialize a new local font@>
+ else err_print('I can handle only 256 different mapfonts');
+@.I can handle only 256...@>
+if cur_font=font_ptr then skip_to_end_of_item
+else while level=1 do
+ begin while cur_char=" " do get_next;
+ if cur_char="(" then @<Read a local font property@>
+ else if cur_char=")" then skip_to_end_of_item
+ else junk_error;
+ end;
+finish_inner_property_list;
+end
+
+@ @<Initialize a new local font@>=
+begin incr(font_ptr);
+fname_start[cur_font]:=vf_size; fname_length[cur_font]:=4; {\.{NULL}}
+farea_start[cur_font]:=vf_size; farea_length[cur_font]:=0;
+font_checksum[cur_font]:=zero_bytes;
+font_at[cur_font]:=@'4000000; {denotes design size of this virtual font}
+font_dsize[cur_font]:=@'50000000; {the |fix_word| for 10}
+end
+
+@ @<Read a local font property@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if (cur_code<font_name_code)or(cur_code>font_dsize_code) then
+ flush_error('This property name doesn''t belong in a MAPFONT list')
+@.This property name doesn't belong...@>
+else begin case cur_code of
+ font_name_code:@<Read a local font name@>;
+ font_area_code:@<Read a local font area@>;
+ font_checksum_code:begin get_four_bytes; font_checksum[cur_font]:=cur_bytes;
+ end;
+ font_at_code: begin frozen_du:=true;
+ if design_units=unity then font_at[cur_font]:=get_fix
+ else font_at[cur_font]:=round((get_fix/design_units)*1048576.0);
+ end;
+ font_dsize_code:font_dsize[cur_font]:=get_fix;
+ end; {there are no other cases}
+ finish_the_property;
+ end;
+end
+
+@ @<Read a local font name@>=
+begin fname_start[cur_font]:=vf_ptr; copy_to_end_of_item;
+if vf_ptr>fname_start[cur_font]+255 then
+ begin err_print('FONTNAME clipped to 255 characters');
+@.FONTNAME clipped...@>
+ fname_length[cur_font]:=255;
+ end
+else fname_length[cur_font]:=vf_ptr-fname_start[cur_font];
+end
+
+@ @<Read a local font area@>=
+begin farea_start[cur_font]:=vf_ptr; copy_to_end_of_item;
+if vf_ptr>farea_start[cur_font]+255 then
+ begin err_print('FONTAREA clipped to 255 characters');
+@.FONTAREA clipped...@>
+ farea_length[cur_font]:=255;
+ end
+else farea_length[cur_font]:=vf_ptr-farea_start[cur_font];
+end
+
+@ @<Read ligature/kern list@>=
+begin lk_step_ended:=false;
+while level=1 do
+ begin while cur_char=" " do get_next;
+ if cur_char="(" then @<Read a ligature/kern command@>
+ else if cur_char=")" then skip_to_end_of_item
+ else junk_error;
+ end;
+finish_inner_property_list;
+end
+
+@ @<Read a ligature/kern command@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if cur_code<label_code then
+ flush_error('This property name doesn''t belong in a LIGTABLE list')
+@.This property name doesn't belong...@>
+else begin case cur_code of
+ label_code:@<Read a label step@>;
+ stop_code:@<Read a stop step@>;
+ skip_code:@<Read a skip step@>;
+ krn_code:@<Read a kerning step@>;
+ lig_code,lig_code+1,lig_code+2,lig_code+3,lig_code+5,lig_code+6,lig_code+7,
+ lig_code+11:@<Read a ligature step@>;
+ end; {there are no other cases |>=label_code|}
+ finish_the_property;
+ end;
+end
+
+@ When a character is about to be tagged, we call the following
+procedure so that an error message is given in case of multiple tags.
+
+@p procedure check_tag(c:byte); {print error if |c| already tagged}
+begin case char_tag[c] of
+no_tag: do_nothing;
+lig_tag: err_print('This character already appeared in a LIGTABLE LABEL');
+@.This character already...@>
+list_tag: err_print('This character already has a NEXTLARGER spec');
+ext_tag: err_print('This character already has a VARCHAR spec');
+end;
+end;
+
+@ @<Read a label step@>=
+begin while cur_char=" " do get_next;
+if cur_char="B" then
+ begin bchar_label:=nl; skip_to_paren; {\.{LABEL BOUNDARYCHAR}}
+ end
+else begin backup; c:=get_byte;
+ check_tag(c); char_tag[c]:=lig_tag; char_remainder[c]:=nl;
+ end;
+if min_nl<=nl then min_nl:=nl+1;
+lk_step_ended:=false;
+end
+
+@ @d stop_flag=128 {value indicating `\.{STOP}' in a lig/kern program}
+@d kern_flag=128 {op code for a kern step}
+
+@<Globals...@>=
+@!lk_step_ended:boolean;
+ {was the last \.{LIGTABLE} property \.{LIG} or \.{KRN}?}
+@!krn_ptr:0..max_kerns; {an index into |kern|}
+
+@ @<Read a stop step@>=
+if not lk_step_ended then
+ err_print('STOP must follow LIG or KRN')
+@.STOP must follow LIG or KRN@>
+else begin lig_kern[nl-1].b0:=stop_flag; lk_step_ended:=false;
+ end
+
+@ @<Read a skip step@>=
+if not lk_step_ended then
+ err_print('SKIP must follow LIG or KRN')
+@.SKIP must follow LIG or KRN@>
+else begin c:=get_byte;
+ if c>=128 then err_print('Maximum SKIP amount is 127')
+@.Maximum SKIP amount...@>
+ else if nl+c>=max_lig_steps then
+ err_print('Sorry, LIGTABLE too long for me to handle')
+@.Sorry, LIGTABLE too long...@>
+ else begin lig_kern[nl-1].b0:=c;
+ if min_nl<=nl+c then min_nl:=nl+c+1;
+ end;
+ lk_step_ended:=false;
+ end
+
+@ @<Read a ligature step@>=
+begin lig_kern[nl].b0:=0;
+lig_kern[nl].b2:=cur_code-lig_code;
+lig_kern[nl].b1:=get_byte;
+lig_kern[nl].b3:=get_byte;
+if nl>=max_lig_steps-1 then
+ err_print('Sorry, LIGTABLE too long for me to handle')
+@.Sorry, LIGTABLE too long...@>
+else incr(nl);
+lk_step_ended:=true;
+end
+
+@ @<Read a kerning step@>=
+begin lig_kern[nl].b0:=0; lig_kern[nl].b1:=get_byte;
+kern[nk]:=get_fix; krn_ptr:=0;
+while kern[krn_ptr]<>kern[nk] do incr(krn_ptr);
+if krn_ptr=nk then
+ begin if nk<max_kerns then incr(nk)
+ else begin err_print('Sorry, too many different kerns for me to handle');
+@.Sorry, too many different kerns...@>
+ decr(krn_ptr);
+ end;
+ end;
+lig_kern[nl].b2:=kern_flag+(krn_ptr div 256);
+lig_kern[nl].b3:=krn_ptr mod 256;
+if nl>=max_lig_steps-1 then
+ err_print('Sorry, LIGTABLE too long for me to handle')
+@.Sorry, LIGTABLE too long...@>
+else incr(nl);
+lk_step_ended:=true;
+end
+
+@ Finally we come to the part of \.{VPtoVF}'s input mechanism
+that is used most, the processing of individual character data.
+
+@<Read character info list@>=
+begin c:=get_byte; {read the character code that is being specified}
+@<Print |c| in octal notation@>;
+while level=1 do
+ begin while cur_char=" " do get_next;
+ if cur_char="(" then @<Read a character property@>
+ else if cur_char=")" then skip_to_end_of_item
+ else junk_error;
+ end;
+if char_wd[c]=0 then char_wd[c]:=sort_in(width,0); {legitimatize |c|}
+finish_inner_property_list;
+end
+
+@ @<Read a character prop...@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if (cur_code<char_wd_code)or(cur_code>var_char_code) then
+ flush_error('This property name doesn''t belong in a CHARACTER list')
+@.This property name doesn't belong...@>
+else begin case cur_code of
+ char_wd_code:char_wd[c]:=sort_in(width,get_fix);
+ char_ht_code:char_ht[c]:=sort_in(height,get_fix);
+ char_dp_code:char_dp[c]:=sort_in(depth,get_fix);
+ char_ic_code:char_ic[c]:=sort_in(italic,get_fix);
+ next_larger_code:begin check_tag(c); char_tag[c]:=list_tag;
+ char_remainder[c]:=get_byte;
+ end;
+ map_code:read_packet(c);
+ var_char_code:@<Read an extensible recipe for |c|@>;
+ end;@/
+ finish_the_property;
+ end;
+end
+
+@ @<Read an extensible r...@>=
+begin if ne=256 then
+ err_print('At most 256 VARCHAR specs are allowed')
+@.At most 256 VARCHAR specs...@>
+else begin check_tag(c); char_tag[c]:=ext_tag; char_remainder[c]:=ne;@/
+ exten[ne]:=zero_bytes;
+ while level=2 do
+ begin while cur_char=" " do get_next;
+ if cur_char="(" then @<Read an extensible piece@>
+ else if cur_char=")" then skip_to_end_of_item
+ else junk_error;
+ end;
+ incr(ne);
+ finish_inner_property_list;
+ end;
+end
+
+@ @<Read an extensible p...@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if (cur_code<var_char_code+1)or(cur_code>var_char_code+4) then
+ flush_error('This property name doesn''t belong in a VARCHAR list')
+@.This property name doesn't belong...@>
+else begin case cur_code-(var_char_code+1) of
+ 0:exten[ne].b0:=get_byte;
+ 1:exten[ne].b1:=get_byte;
+ 2:exten[ne].b2:=get_byte;
+ 3:exten[ne].b3:=get_byte;
+ end;@/
+ finish_the_property;
+ end;
+end
+
+@* Assembling the mappings.
+Each \.{MAP} property is a sequence of \.{DVI} instructions, for which
+we need to know some of the opcodes.
+
+@d set_char_0=0 {\.{DVI} command to typeset character 0 and move right}
+@d set1=128 {typeset a character and move right}
+@d set_rule=132 {typeset a rule and move right}
+@d push=141 {save the current positions}
+@d pop=142 {restore previous positions}
+@d right1=143 {move right}
+@d w0=147 {move right by |w|}
+@d w1=148 {move right and set |w|}
+@d x0=152 {move right by |x|}
+@d x1=153 {move right and set |x|}
+@d down1=157 {move down}
+@d y0=161 {move down by |y|}
+@d y1=162 {move down and set |y|}
+@d z0=166 {move down by |z|}
+@d z1=167 {move down and set |z|}
+@d fnt_num_0=171 {set current font to 0}
+@d fnt1=235 {set current font}
+@d xxx1=239 {extension to \.{DVI} primitives}
+@d xxx4=242 {potentially long extension to \.{DVI} primitives}
+@d fnt_def1=243 {define the meaning of a font number}
+@d pre=247 {preamble}
+@d post=248 {postamble beginning}
+
+@ We keep stacks of movement values, in order to optimize the \.{DVI} code
+in simple cases.
+
+@<Glob...@>=
+@!hstack:array[0..max_stack] of 0..2; {number of known horizontal movements}
+@!vstack:array[0..max_stack] of 0..2; {number of known vertical movements}
+@!wstack,@!xstack,@!ystack,@!zstack:array[0..max_stack] of fix_word;
+@!stack_ptr:0..max_stack;
+
+@ The packet is built by straightforward assembly of \.{DVI} instructions.
+
+@p @<Declare the |vf_fix| procedure@>@;@/
+procedure read_packet(@!c:byte);
+var @!cc:byte; {character being typeset}
+@!x:fix_word; {movement}
+@!h,@!v:0..2; {top of |hstack| and |vstack|}
+@!special_start:0..vf_size; {location of |xxx1| command}
+@!k:0..vf_size; {loop index}
+begin packet_start[c]:=vf_ptr; stack_ptr:=0; h:=0; v:=0;
+cur_font:=0;
+while level=2 do
+ begin while cur_char=" " do get_next;
+ if cur_char="(" then @<Read and assemble a list of \.{DVI} commands@>
+ else if cur_char=")" then skip_to_end_of_item
+ else junk_error;
+ end;
+while stack_ptr>0 do
+ begin err_print('Missing POP supplied');
+@.Missing POP supplied@>
+ vf_store(pop); decr(stack_ptr);
+ end;
+packet_length[c]:=vf_ptr-packet_start[c];
+finish_inner_property_list;
+end;
+
+@ @<Read and assemble a list of \.{DVI}...@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if (cur_code<select_font_code)or(cur_code>special_hex_code) then
+ flush_error('This property name doesn''t belong in a MAP list')
+@.This property name doesn't belong...@>
+else begin case cur_code of
+ select_font_code:@<Assemble a font selection@>;
+ set_char_code:@<Assemble a typesetting instruction@>;
+ set_rule_code:@<Assemble a rulesetting instruction@>;
+ move_right_code,move_right_code+1:@<Assemble a horizontal movement@>;
+ move_down_code,move_down_code+1:@<Assemble a vertical movement@>;
+ push_code:@<Assemble a stack push@>;
+ pop_code:@<Assemble a stack pop@>;
+ special_code,special_hex_code:@<Assemble a special command@>;
+ end;@/
+ finish_the_property;
+ end;
+end
+
+@ @<Assemble a font selection@>=
+begin get_four_bytes; font_number[font_ptr]:=cur_bytes;
+cur_font:=0;
+while numbers_differ do incr(cur_font);
+if cur_font=font_ptr then err_print('Undefined MAPFONT cannot be selected')
+@.Undefined MAPFONT...@>
+else if cur_font<64 then vf_store(fnt_num_0+cur_font)
+else begin vf_store(fnt1); vf_store(cur_font);
+ end;
+end
+
+@ @<Assemble a typesetting instruction@>=
+if cur_font=font_ptr then
+ err_print('Character cannot be typeset in undefined font')
+@.Character cannot be typeset...@>
+else begin cc:=get_byte;
+ if cc>=128 then vf_store(set1);
+ vf_store(cc);
+ end
+
+@ Here's a procedure that converts a |fix_word| to a sequence of
+\.{DVI} bytes.
+
+@<Declare the |vf_fix|...@>=
+procedure vf_fix(@!opcode:byte;@!x:fix_word);
+var negative:boolean;
+@!k:0..4; {number of bytes to typeset}
+@!t:integer; {threshold}
+begin frozen_du:=true;
+if design_units<>unity then x:=round((x/design_units)*1048576.0);
+if x>=0 then negative:=false
+else begin negative:=true; x:=-1-x;@+end;
+if opcode=0 then
+ begin k:=4; t:=@'100000000;@+end
+else begin t:=127; k:=1;
+ while x>t do
+ begin t:=256*t+255; incr(k);
+ end;
+ vf_store(opcode+k-1); t:=t div 128 +1;
+ end;
+repeat if negative then
+ begin vf_store(255-(x div t)); negative:=false;
+ x:=(x div t)*t+t-1-x;
+ end
+else vf_store((x div t) mod 256);
+decr(k); t:=t div 256;
+until k=0;
+end;
+
+@ @<Assemble a rulesetting instruction@>=
+begin vf_store(set_rule); vf_fix(0,get_fix); vf_fix(0,get_fix);
+end
+
+@ @<Assemble a horizontal movement@>=
+begin if cur_code=move_right_code then x:=get_fix@+else x:=-get_fix;
+if h=0 then
+ begin wstack[stack_ptr]:=x; h:=1; vf_fix(w1,x);@+end
+else if x=wstack[stack_ptr] then vf_store(w0)
+else if h=1 then
+ begin xstack[stack_ptr]:=x; h:=2; vf_fix(x1,x);@+end
+else if x=xstack[stack_ptr] then vf_store(x0)
+else vf_fix(right1,x);
+end
+
+@ @<Assemble a vertical movement@>=
+begin if cur_code=move_down_code then x:=get_fix@+else x:=-get_fix;
+if v=0 then
+ begin ystack[stack_ptr]:=x; v:=1; vf_fix(y1,x);@+end
+else if x=ystack[stack_ptr] then vf_store(y0)
+else if v=1 then
+ begin zstack[stack_ptr]:=x; v:=2; vf_fix(z1,x);@+end
+else if x=zstack[stack_ptr] then vf_store(z0)
+else vf_fix(down1,x);
+end
+
+@ @<Assemble a stack push@>=
+if stack_ptr=max_stack then {too pushy}
+ err_print('Don''t push so much---stack is full!')
+@.Don't push so much...@>
+else begin vf_store(push); hstack[stack_ptr]:=h; vstack[stack_ptr]:=v;
+ incr(stack_ptr); h:=0; v:=0;
+ end
+
+@ @<Assemble a stack pop@>=
+if stack_ptr=0 then
+ err_print('Empty stack cannot be popped')
+@.Empty stack...@>
+else begin vf_store(pop); decr(stack_ptr);
+ h:=hstack[stack_ptr]; v:=vstack[stack_ptr];
+ end
+
+@ @<Assemble a special command@>=
+begin vf_store(xxx1); vf_store(0); {dummy length}
+special_start:=vf_ptr;
+if cur_code=special_code then copy_to_end_of_item
+else begin repeat x:=get_hex;
+ if cur_char>")" then vf_store(x*16+get_hex);
+ until cur_char<=")";
+ end;
+if vf_ptr-special_start>255 then @<Convert |xxx1| command to |xxx4|@>
+else vf[special_start-1]:=vf_ptr-special_start;
+end
+
+@ @<Convert |xxx1|...@>=
+if vf_ptr+3>vf_size then
+ begin err_print('Special command being clipped---no room left!');
+@.Special command being clipped...@>
+ vf_ptr:=special_start+255; vf[special_start-1]:=255;
+ end
+else begin for k:=vf_ptr downto special_start do vf[k+3]:=vf[k];
+ x:=vf_ptr-special_start; vf_ptr:=vf_ptr+3;
+ vf[special_start-2]:=xxx4;
+ vf[special_start-1]:=x div @'100000000;
+ vf[special_start]:=(x div @'200000) mod 256;
+ vf[special_start+1]:=(x div @'400) mod 256;
+ vf[special_start+2]:=x mod 256;
+ end
+
+@ The input routine is now complete except for the following code,
+which prints a progress report as the file is being read.
+
+@p procedure print_octal(c:byte); {prints three octal digits}
+begin print('''',(c div 64):1,((c div 8) mod 8):1,(c mod 8):1);
+end;
+
+@ @<Print |c| in octal...@>=
+begin if chars_on_line=8 then
+ begin print_ln(' '); chars_on_line:=1;
+ end
+else begin if chars_on_line>0 then print(' ');
+ incr(chars_on_line);
+ end;
+print_octal(c); {progress report}
+end
+
+@* The checking and massaging phase.
+Once the whole \.{VPL} file has been read in, we must check it for consistency
+and correct any errors. This process consists mainly of running through
+the characters that exist and seeing if they refer to characters that
+don't exist. We also compute the true value of |seven_unsafe|; we make sure
+that the charlists and ligature programs contain no loops; and we
+shorten the lists of widths, heights, depths, and italic corrections,
+if necessary, to keep from exceeding the required maximum sizes.
+
+@<Glob...@>=
+@!seven_unsafe:boolean; {do seven-bit characters generate eight-bit ones?}
+
+@ @<Correct and check the information@>=
+if nl>0 then @<Make sure the ligature/kerning program ends appropriately@>;
+seven_unsafe:=false;
+for c:=0 to 255 do if char_wd[c]<>0 then
+ @<For all characters |g| generated by |c|,
+ make sure that |char_wd[g]| is nonzero, and
+ set |seven_unsafe| if |c<128<=g|@>;
+if bchar_label<@'77777 then
+ begin c:=256; @<Check ligature program of |c|@>;
+ end;
+if seven_bit_safe_flag and seven_unsafe then
+ print_ln('The font is not really seven-bit-safe!');
+@.The font is not...safe@>
+@<Check for infinite ligature loops@>;
+@<Doublecheck the lig/kern commands and the extensible recipes@>;
+for c:=0 to 255 do
+ @<Make sure that |c| is not the largest element of a charlist cycle@>;
+@<Put the width, height, depth, and italic lists into final form@>
+
+@ The checking that we need in several places is accomplished by three
+macros that are only slightly tricky.
+
+@d existence_tail(#)==begin char_wd[g]:=sort_in(width,0);
+ print(#,' '); print_octal(c);
+ print_ln(' had no CHARACTER spec.');
+ end;
+ end
+@d check_existence_and_safety(#)==begin g:=#;
+ if (g>=128)and(c<128) then seven_unsafe:=true;
+ if char_wd[g]=0 then existence_tail
+@d check_existence(#)==begin g:=#;
+ if char_wd[g]=0 then existence_tail
+
+@<For all characters |g| generated by |c|...@>=
+case char_tag[c] of
+no_tag: do_nothing;
+lig_tag: @<Check ligature program of |c|@>;
+list_tag: check_existence_and_safety(char_remainder[c])
+ ('The character NEXTLARGER than');
+@.The character NEXTLARGER...@>
+ext_tag:@<Check the pieces of |exten[c]|@>;
+end
+
+@ @<Check the pieces...@>=
+begin if exten[char_remainder[c]].b0>0 then
+ check_existence_and_safety(exten[char_remainder[c]].b0)
+ ('TOP piece of character');
+@.TOP piece of character...@>
+if exten[char_remainder[c]].b1>0 then
+ check_existence_and_safety(exten[char_remainder[c]].b1)
+ ('MID piece of character');
+@.MID piece of character...@>
+if exten[char_remainder[c]].b2>0 then
+ check_existence_and_safety(exten[char_remainder[c]].b2)
+ ('BOT piece of character');
+@.BOT piece of character...@>
+check_existence_and_safety(exten[char_remainder[c]].b3)
+ ('REP piece of character');
+@.REP piece of character...@>
+end
+
+@ @<Make sure that |c| is not the largest element of a charlist cycle@>=
+if char_tag[c]=list_tag then
+ begin g:=char_remainder[c];
+ while (g<c)and(char_tag[g]=list_tag) do g:=char_remainder[g];
+ if g=c then
+ begin char_tag[c]:=no_tag;
+ print('A cycle of NEXTLARGER characters has been broken at ');
+@.A cycle of NEXTLARGER...@>
+ print_octal(c); print_ln('.');
+ end;
+ end
+
+@ @<Glob...@>=
+@!delta:fix_word; {size of the intervals needed for rounding}
+
+@ @d round_message(#)==if delta>0 then print_ln('I had to round some ',
+@.I had to round...@>
+ #,'s by ',(((delta+1) div 2)/@'4000000):1:7,' units.')
+
+@<Put the width, height, depth, and italic lists into final form@>=
+delta:=shorten(width,255); set_indices(width,delta); round_message('width');@/
+delta:=shorten(height,15); set_indices(height,delta); round_message('height');@/
+delta:=shorten(depth,15); set_indices(depth,delta); round_message('depth');@/
+delta:=shorten(italic,63); set_indices(italic,delta);
+ round_message('italic correction');
+
+@ @d clear_lig_kern_entry== {make an unconditional \.{STOP}}
+ lig_kern[nl].b0:=255; lig_kern[nl].b1:=0;
+ lig_kern[nl].b2:=0; lig_kern[nl].b3:=0
+
+@<Make sure the ligature/kerning program ends...@>=
+begin if bchar_label<@'77777 then {make room for it}
+ begin clear_lig_kern_entry; incr(nl);
+ end; {|bchar_label| will be stored later}
+while min_nl>nl do
+ begin clear_lig_kern_entry; incr(nl);
+ end;
+if lig_kern[nl-1].b0=0 then lig_kern[nl-1].b0:=stop_flag;
+end
+
+@ It's not trivial to check for infinite loops generated by repeated
+insertion of ligature characters. But fortunately there is a nice
+algorithm for such testing, copied here from the program \.{TFtoPL}
+where it is explained further.
+
+@d simple=0 {$f(x,y)=z$}
+@d left_z=1 {$f(x,y)=f(z,y)$}
+@d right_z=2 {$f(x,y)=f(x,z)$}
+@d both_z=3 {$f(x,y)=f(f(x,z),y)$}
+@d pending=4 {$f(x,y)$ is being evaluated}
+
+
+@ @<Glo...@>=
+@!lig_ptr:0..max_lig_steps; {an index into |lig_kern|}
+@!hash:array[0..hash_size] of 0..66048; {$256x+y+1$ for $x\le257$ and $y\le255$}
+@!class:array[0..hash_size] of simple..pending;
+@!lig_z:array[0..hash_size] of 0..257;
+@!hash_ptr:0..hash_size; {the number of nonzero entries in |hash|}
+@!hash_list:array[0..hash_size] of 0..hash_size; {list of those nonzero entries}
+@!h,@!hh:0..hash_size; {indices into the hash table}
+@!tt:indx; {temporary register}
+@!x_lig_cycle,@!y_lig_cycle:0..256; {problematic ligature pair}
+
+@ @<Set init...@>=
+hash_ptr:=0; y_lig_cycle:=256;
+for k:=0 to hash_size do hash[k]:=0;
+
+@ @d lig_exam==lig_kern[lig_ptr].b1
+@d lig_gen==lig_kern[lig_ptr].b3
+
+@<Check lig...@>=
+begin lig_ptr:=char_remainder[c];
+repeat if hash_input(lig_ptr,c) then
+ begin if lig_kern[lig_ptr].b2<kern_flag then
+ begin if lig_exam<>bchar then
+ check_existence(lig_exam)('LIG character examined by');
+@.LIG character examined...@>
+ check_existence(lig_gen)('LIG character generated by');
+@.LIG character generated...@>
+ if lig_gen>=128 then if(c<128)or(c=256) then
+ if(lig_exam<128)or(lig_exam=bchar) then seven_unsafe:=true;
+ end
+ else if lig_exam<>bchar then
+ check_existence(lig_exam)('KRN character examined by');
+@.KRN character examined...@>
+ end;
+if lig_kern[lig_ptr].b0>=stop_flag then lig_ptr:=nl
+else lig_ptr:=lig_ptr+1+lig_kern[lig_ptr].b0;
+until lig_ptr>=nl;
+end
+
+@ The |hash_input| procedure is copied from \.{TFtoPL}, but it is made
+into a boolean function that returns |false| if the ligature command
+was masked by a previous one.
+
+@p function hash_input(@!p,@!c:indx):boolean;
+ {enter data for character |c| and command in location |p|, unless it isn't new}
+label 30; {go here for a quick exit}
+var @!cc:simple..both_z; {class of data being entered}
+@!zz:0..255; {function value or ligature character being entered}
+@!y:0..255; {the character after the cursor}
+@!key:integer; {value to be stored in |hash|}
+@!t:integer; {temporary register for swapping}
+begin if hash_ptr=hash_size then
+ begin hash_input:=false; goto 30;@+end;
+@<Compute the command parameters |y|, |cc|, and |zz|@>;
+key:=256*c+y+1; h:=(1009*key) mod hash_size;
+while hash[h]>0 do
+ begin if hash[h]<=key then
+ begin if hash[h]=key then
+ begin hash_input:=false; goto 30; {unused ligature command}
+ end;
+ t:=hash[h]; hash[h]:=key; key:=t; {do ordered-hash-table insertion}
+ t:=class[h]; class[h]:=cc; cc:=t; {namely, do a swap}
+ t:=lig_z[h]; lig_z[h]:=zz; zz:=t;
+ end;
+ if h>0 then decr(h)@+else h:=hash_size;
+ end;
+hash[h]:=key; class[h]:=cc; lig_z[h]:=zz;
+incr(hash_ptr); hash_list[hash_ptr]:=h;
+hash_input:=true;
+30:end;
+
+@ @<Compute the command param...@>=
+y:=lig_kern[p].b1; t:=lig_kern[p].b2; cc:=simple;
+zz:=lig_kern[p].b3;
+if t>=kern_flag then zz:=y
+else begin case t of
+ 0,6:do_nothing; {\.{LIG},\.{/LIG>}}
+ 5,11:zz:=y; {\.{LIG/>}, \.{/LIG/>>}}
+ 1,7:cc:=left_z; {\.{LIG/}, \.{/LIG/>}}
+ 2:cc:=right_z; {\.{/LIG}}
+ 3:cc:=both_z; {\.{/LIG/}}
+ end; {there are no other cases}
+ end
+
+@ (More good stuff from \.{TFtoPL}.)
+
+@p function f(@!h,@!x,@!y:indx):indx; forward;@t\2@>
+ {compute $f$ for arguments known to be in |hash[h]|}
+function eval(@!x,@!y:indx):indx; {compute $f(x,y)$ with hashtable lookup}
+var @!key:integer; {value sought in hash table}
+begin key:=256*x+y+1; h:=(1009*key) mod hash_size;
+while hash[h]>key do
+ if h>0 then decr(h)@+else h:=hash_size;
+if hash[h]<key then eval:=y {not in ordered hash table}
+else eval:=f(h,x,y);
+end;
+
+@ Pascal's beastly convention for |forward| declarations prevents us from
+saying |function f(h,x,y:indx):indx| here.
+
+@p function f;
+begin case class[h] of
+simple: do_nothing;
+left_z: begin class[h]:=pending; lig_z[h]:=eval(lig_z[h],y); class[h]:=simple;
+ end;
+right_z: begin class[h]:=pending; lig_z[h]:=eval(x,lig_z[h]); class[h]:=simple;
+ end;
+both_z: begin class[h]:=pending; lig_z[h]:=eval(eval(x,lig_z[h]),y);
+ class[h]:=simple;
+ end;
+pending: begin x_lig_cycle:=x; y_lig_cycle:=y; lig_z[h]:=257; class[h]:=simple;
+ end; {the value 257 will break all cycles, since it's not in |hash|}
+end; {there are no other cases}
+f:=lig_z[h];
+end;
+
+@ @<Check for infinite...@>=
+if hash_ptr<hash_size then for hh:=1 to hash_ptr do
+ begin tt:=hash_list[hh];
+ if class[tt]>simple then {make sure $f$ is well defined}
+ tt:=f(tt,(hash[tt]-1)div 256,(hash[tt]-1)mod 256);
+ end;
+if(hash_ptr=hash_size)or(y_lig_cycle<256) then
+ begin if hash_ptr<hash_size then
+ begin print('Infinite ligature loop starting with ');
+@.Infinite ligature loop...@>
+ if x_lig_cycle=256 then print('boundary')@+else print_octal(x_lig_cycle);
+ print(' and '); print_octal(y_lig_cycle); print_ln('!');
+ end
+ else print_ln('Sorry, I haven''t room for so many ligature/kern pairs!');
+@.Sorry, I haven't room...@>
+ print_ln('All ligatures will be cleared.');
+ for c:=0 to 255 do if char_tag[c]=lig_tag then
+ begin char_tag[c]:=no_tag; char_remainder[c]:=0;
+ end;
+ nl:=0; bchar:=256; bchar_label:=@'77777;
+ end
+
+@ The lig/kern program may still contain references to nonexistent characters,
+if parts of that program are never used. Similarly, there may be extensible
+characters that are never used, because they were overridden by
+\.{NEXTLARGER}, say. This would produce an invalid \.{TFM} file; so we
+must fix such errors.
+
+@d double_check_tail(#)==@t\1@>if char_wd[0]=0
+ then char_wd[0]:=sort_in(width,0);
+ print('Unused ',#,' refers to nonexistent character ');
+ print_octal(c); print_ln('!');
+ end;
+ end
+@d double_check_lig(#)==begin c:=lig_kern[lig_ptr].#;
+ if char_wd[c]=0 then if c<>bchar then
+ begin lig_kern[lig_ptr].#:=0; double_check_tail
+@d double_check_ext(#)==begin c:=exten[g].#;
+ if c>0 then if char_wd[c]=0 then
+ begin exten[g].#:=0; double_check_tail
+@d double_check_rep(#)==begin c:=exten[g].#;
+ if char_wd[c]=0 then
+ begin exten[g].#:=0; double_check_tail
+
+@<Doublecheck...@>=
+if nl>0 then for lig_ptr:=0 to nl-1 do
+ if lig_kern[lig_ptr].b2<kern_flag then
+ begin if lig_kern[lig_ptr].b0<255 then
+ begin double_check_lig(b1)('LIG step'); double_check_lig(b3)('LIG step');
+ end;
+ end
+ else double_check_lig(b1)('KRN step');
+@.Unused LIG step...@>
+@.Unused KRN step...@>
+if ne>0 then for g:=0 to ne-1 do
+ begin double_check_ext(b0)('VARCHAR TOP');
+ double_check_ext(b1)('VARCHAR MID');
+ double_check_ext(b2)('VARCHAR BOT');
+ double_check_rep(b3)('VARCHAR REP');
+@.Unused VARCHAR...@>
+ end
+
+@* The TFM output phase.
+Now that we know how to get all of the font data correctly stored in
+\.{VPtoVF}'s memory, it only remains to write the answers out.
+
+First of all, it is convenient to have an abbreviation for output to the
+\.{TFM} file:
+
+@d out(#)==write(tfm_file,#)
+
+@ The general plan for producing \.{TFM} files is long but simple:
+
+@<Do the \.{TFM} output@>=
+@<Compute the twelve subfile sizes@>;
+@<Output the twelve subfile sizes@>;
+@<Output the header block@>;
+@<Output the character info@>;
+@<Output the dimensions themselves@>;
+@<Output the ligature/kern program@>;
+@<Output the extensible character recipes@>;
+@<Output the parameters@>
+
+@ A \.{TFM} file begins with 12 numbers that tell how big its subfiles are.
+We already know most of these numbers; for example, the number of distinct
+widths is |memory[width]+1|, where the $+1$ accounts for the zero width that
+is always supposed to be present. But we still should compute the beginning
+and ending character codes (|bc| and |ec|), the number of header words (|lh|),
+and the total number of words in the \.{TFM} file (|lf|).
+
+@<Gl...@>=
+@!bc:byte; {the smallest character code in the font}
+@!ec:byte; {the largest character code in the font}
+@!lh:byte; {the number of words in the header block}
+@!lf:0..32767; {the number of words in the entire \.{TFM} file}
+@!not_found:boolean; {has a font character been found?}
+@!temp_width:fix_word; {width being used to compute a check sum}
+
+@ It might turn out that no characters exist at all. But \.{VPtoVF} keeps
+going and writes the \.{TFM} anyway. In this case |ec| will be~0 and |bc|
+will be~1.
+
+@<Compute the twelve...@>=
+lh:=header_ptr div 4;@/
+not_found:=true; bc:=0;
+while not_found do
+ if (char_wd[bc]>0)or(bc=255) then not_found:=false
+ else incr(bc);
+not_found:=true; ec:=255;
+while not_found do
+ if (char_wd[ec]>0)or(ec=0) then not_found:=false
+ else decr(ec);
+if bc>ec then bc:=1;
+incr(memory[width]); incr(memory[height]); incr(memory[depth]);
+incr(memory[italic]);@/
+@<Compute the ligature/kern program offset@>;
+lf:=6+lh+(ec-bc+1)+memory[width]+memory[height]+memory[depth]+
+memory[italic]+nl+lk_offset+nk+ne+np;
+
+@ @d out_size(#)==out((#) div 256); out((#) mod 256)
+
+@<Output the twelve subfile sizes@>=
+out_size(lf); out_size(lh); out_size(bc); out_size(ec);
+out_size(memory[width]); out_size(memory[height]);
+out_size(memory[depth]); out_size(memory[italic]);
+out_size(nl+lk_offset); out_size(nk); out_size(ne); out_size(np);
+
+@ The routines that follow need a few temporary variables of different types.
+
+@<Gl...@>=
+@!j:0..max_header_bytes; {index into |header_bytes|}
+@!p:pointer; {index into |memory|}
+@!q:width..italic; {runs through the list heads for dimensions}
+@!par_ptr:0..max_param_words; {runs through the parameters}
+
+@ The header block follows the subfile sizes. The necessary information all
+appears in |header_bytes|, except that the design size and the seven-bit-safe
+flag must still be set.
+
+@<Output the header block@>=
+if not check_sum_specified then @<Compute the check sum@>;
+header_bytes[design_size_loc]:=design_size div @'100000000;
+ {this works since |design_size>0|}
+header_bytes[design_size_loc+1]:=(design_size div @'200000) mod 256;
+header_bytes[design_size_loc+2]:=(design_size div 256) mod 256;
+header_bytes[design_size_loc+3]:=design_size mod 256;
+if not seven_unsafe then header_bytes[seven_flag_loc]:=128;
+for j:=0 to header_ptr-1 do out(header_bytes[j]);
+
+@ @<Compute the check sum@>=
+begin c0:=bc; c1:=ec; c2:=bc; c3:=ec;
+for c:=bc to ec do if char_wd[c]>0 then
+ begin temp_width:=memory[char_wd[c]];
+ if design_units<>unity then
+ temp_width:=round((temp_width/design_units)*1048576.0);
+ temp_width:=temp_width + (c+4)*@'20000000; {this should be positive}
+ c0:=(c0+c0+temp_width) mod 255;
+ c1:=(c1+c1+temp_width) mod 253;
+ c2:=(c2+c2+temp_width) mod 251;
+ c3:=(c3+c3+temp_width) mod 247;
+ end;
+header_bytes[check_sum_loc]:=c0;
+header_bytes[check_sum_loc+1]:=c1;
+header_bytes[check_sum_loc+2]:=c2;
+header_bytes[check_sum_loc+3]:=c3;
+end
+
+@ The next block contains packed |char_info|.
+
+@<Output the character info@>=
+index[0]:=0;
+for c:=bc to ec do
+ begin out(index[char_wd[c]]);
+ out(index[char_ht[c]]*16+index[char_dp[c]]);
+ out(index[char_ic[c]]*4+char_tag[c]);
+ out(char_remainder[c]);
+ end
+
+@ When a scaled quantity is output, we may need to divide it by |design_units|.
+The following subroutine takes care of this, using floating point arithmetic
+only if |design_units<>1.0|.
+
+@p procedure out_scaled(x:fix_word); {outputs a scaled |fix_word|}
+var @!n:byte; {the first byte after the sign}
+@!m:0..65535; {the two least significant bytes}
+begin if abs(x/design_units)>=16.0 then
+ begin print_ln('The relative dimension ',x/@'4000000:1:3,
+ ' is too large.');
+@.The relative dimension...@>
+ print(' (Must be less than 16*designsize');
+ if design_units<>unity then print(' =',design_units/@'200000:1:3,
+ ' designunits');
+ print_ln(')'); x:=0;
+ end;
+if design_units<>unity then x:=round((x/design_units)*1048576.0);
+if x<0 then
+ begin out(255); x:=x+@'100000000;
+ if x<=0 then x:=1;
+ end
+else begin out(0);
+ if x>=@'100000000 then x:=@'77777777;
+ end;
+n:=x div @'200000; m:=x mod @'200000;
+out(n); out(m div 256); out(m mod 256);
+end;
+
+@ We have output the packed indices for individual characters.
+The scaled widths, heights, depths, and italic corrections are next.
+
+@<Output the dimensions themselves@>=
+for q:=width to italic do
+ begin out(0); out(0); out(0); out(0); {output the zero word}
+ p:=link[q]; {head of list}
+ while p>0 do
+ begin out_scaled(memory[p]);
+ p:=link[p];
+ end;
+ end;
+
+@ One embarrassing problem remains: The ligature/kern program might be very
+long, but the starting addresses in |char_remainder| can be at most~255.
+Therefore we need to output some indirect address information; we want to
+compute |lk_offset| so that addition of |lk_offset| to all remainders makes
+all but |lk_offset| distinct remainders less than~256.
+
+For this we need a sorted table of all relevant remainders.
+
+@<Glob...@>=
+@!label_table:array[0..256] of record
+ @!rr: -1..@'77777; {sorted label values}
+ @!cc: byte; {associated characters}
+ end;
+@!label_ptr:0..256; {index of highest entry in |label_table|}
+@!sort_ptr:0..256; {index into |label_table|}
+@!lk_offset:0..256; {smallest offset value that might work}
+@!t:0..@'77777; {label value that is being redirected}
+@!extra_loc_needed:boolean; {do we need a special word for |bchar|?}
+
+@ @<Compute the ligature/kern program offset@>=
+@<Insert all labels into |label_table|@>;
+if bchar<256 then
+ begin extra_loc_needed:=true; lk_offset:=1;
+ end
+else begin extra_loc_needed:=false; lk_offset:=0;
+ end;
+@<Find the minimum |lk_offset| and adjust all remainders@>;
+if bchar_label<@'77777 then
+ begin lig_kern[nl-1].b2:=(bchar_label+lk_offset)div 256;
+ lig_kern[nl-1].b3:=(bchar_label+lk_offset)mod 256;
+ end
+
+@ @<Insert all labels...@>=
+label_ptr:=0; label_table[0].rr:=-1; {sentinel}
+for c:=bc to ec do if char_tag[c]=lig_tag then
+ begin sort_ptr:=label_ptr; {there's a hole at position |sort_ptr+1|}
+ while label_table[sort_ptr].rr>char_remainder[c] do
+ begin label_table[sort_ptr+1]:=label_table[sort_ptr];
+ decr(sort_ptr); {move the hole}
+ end;
+ label_table[sort_ptr+1].cc:=c;
+ label_table[sort_ptr+1].rr:=char_remainder[c];
+ incr(label_ptr);
+ end
+
+@ @<Find the minimum |lk_offset| and adjust all remainders@>=
+begin sort_ptr:=label_ptr; {the largest unallocated label}
+if label_table[sort_ptr].rr+lk_offset > 255 then
+ begin lk_offset:=0; extra_loc_needed:=false; {location 0 can do double duty}
+ repeat char_remainder[label_table[sort_ptr].cc]:=lk_offset;
+ while label_table[sort_ptr-1].rr=label_table[sort_ptr].rr do
+ begin decr(sort_ptr); char_remainder[label_table[sort_ptr].cc]:=lk_offset;
+ end;
+ incr(lk_offset); decr(sort_ptr);
+ until lk_offset+label_table[sort_ptr].rr<256;
+ {N.B.: |lk_offset=256| satisfies this when |sort_ptr=0|}
+ end;
+if lk_offset>0 then while sort_ptr>0 do
+ begin char_remainder[label_table[sort_ptr].cc]:=
+ char_remainder[label_table[sort_ptr].cc]+lk_offset;
+ decr(sort_ptr);
+ end;
+end
+
+@ @<Output the ligature/kern program@>=
+if extra_loc_needed then {|lk_offset=1|}
+ begin out(255); out(bchar); out(0); out(0);
+ end
+else for sort_ptr:=1 to lk_offset do {output the redirection specs}
+ begin t:=label_table[label_ptr].rr;
+ if bchar<256 then
+ begin out(255); out(bchar);
+ end
+ else begin out(254); out(0);
+ end;
+ out_size(t+lk_offset);
+ repeat decr(label_ptr); until label_table[label_ptr].rr<t;
+ end;
+if nl>0 then for lig_ptr:=0 to nl-1 do
+ begin out(lig_kern[lig_ptr].b0);
+ out(lig_kern[lig_ptr].b1);
+ out(lig_kern[lig_ptr].b2);
+ out(lig_kern[lig_ptr].b3);
+ end;
+if nk>0 then for krn_ptr:=0 to nk-1 do out_scaled(kern[krn_ptr])
+
+@ @<Output the extensible character recipes@>=
+if ne>0 then for c:=0 to ne-1 do
+ begin out(exten[c].b0);
+ out(exten[c].b1);
+ out(exten[c].b2);
+ out(exten[c].b3);
+ end;
+
+@ For our grand finale, we wind everything up by outputting the parameters.
+
+@<Output the parameters@>=
+for par_ptr:=1 to np do
+ begin if par_ptr=1 then
+ @<Output the slant (|param[1]|) without scaling@>
+ else out_scaled(param[par_ptr]);
+ end
+
+@ @<Output the slant...@>=
+begin if param[1]<0 then
+ begin param[1]:=param[1]+@'10000000000;
+ out((param[1] div @'100000000)+256-64);
+ end
+else out(param[1] div @'100000000);
+out((param[1] div @'200000) mod 256);
+out((param[1] div 256) mod 256);
+out(param[1] mod 256);
+end
+
+@* The VF output phase.
+Output to |vf_file| is considerably simpler.
+
+@d id_byte=202 {current version of \.{VF} format}
+@d vout(#)==write(vf_file,#)
+
+@<Glob...@>=
+@!vcount:integer; {number of bytes written to |vf_file|}
+
+@ We need a routine to output integers as four bytes. Negative values
+will never be less than $-2^{24}$.
+
+@p procedure vout_int(@!x:integer);
+begin if x>=0 then vout(x div @'100000000)
+else begin vout(255); x:=x+@'100000000;
+ end;
+vout((x div @'200000) mod 256);
+vout((x div @'400) mod 256); vout(x mod 256);
+end;
+
+@ @<Do the \.{VF} output@>=
+vout(pre); vout(id_byte); vout(vtitle_length);
+for k:=0 to vtitle_length-1 do vout(vf[vtitle_start+k]);
+for k:=check_sum_loc to design_size_loc+3 do vout(header_bytes[k]);
+vcount:=vtitle_length+11;
+for cur_font:=0 to font_ptr-1 do @<Output a local font definition@>;
+for c:=bc to ec do if char_wd[c]>0 then
+ @<Output a packet for character |c|@>;
+repeat vout(post); incr(vcount);
+until vcount mod 4 = 0
+
+@ @<Output a local font definition@>=
+begin vout(fnt_def1); vout(cur_font);@/
+vout(font_checksum[cur_font].b0);
+vout(font_checksum[cur_font].b1);
+vout(font_checksum[cur_font].b2);
+vout(font_checksum[cur_font].b3);
+vout_int(font_at[cur_font]);
+vout_int(font_dsize[cur_font]);
+vout(farea_length[cur_font]);
+vout(fname_length[cur_font]);
+for k:=0 to farea_length[cur_font]-1 do vout(vf[farea_start[cur_font]+k]);
+if fname_start[cur_font]=vf_size then
+ begin vout("N"); vout("U"); vout("L"); vout("L");
+ end
+else for k:=0 to fname_length[cur_font]-1 do vout(vf[fname_start[cur_font]+k]);
+vcount:=vcount+12+farea_length[cur_font]+fname_length[cur_font];
+end
+
+@ @<Output a packet for character |c|@>=
+begin x:=memory[char_wd[c]];
+if design_units<>unity then x:=round((x/design_units)*1048576.0);
+if (packet_length[c]>241)or(x<0)or(x>=@'100000000) then
+ begin vout(242); vout_int(packet_length[c]); vout_int(c); vout_int(x);
+ vcount:=vcount+13+packet_length[c];
+ end
+else begin vout(packet_length[c]); vout(c); vout(x div @'200000);
+ vout((x div @'400) mod 256); vout(x mod 256);
+ vcount:=vcount+5+packet_length[c];
+ end;
+if packet_start[c]=vf_size then
+ begin if c>=128 then vout(set1);
+ vout(c);
+ end
+else for k:=0 to packet_length[c]-1 do vout(vf[packet_start[c]+k]);
+end
+
+@* The main program.
+The routines sketched out so far need to be packaged into separate procedures,
+on some systems, since some \PASCAL\ compilers place a strict limit on the
+size of a routine. The packaging is done here in an attempt to avoid some
+system-dependent changes.
+
+@p procedure param_enter;
+begin @<Enter the parameter names@>;
+end;
+@#
+procedure vpl_enter;
+begin @<Enter all the \.{VPL} names@>;
+end;
+@#
+procedure name_enter; {enter all names and their equivalents}
+begin @<Enter all the \.{PL} names...@>;
+vpl_enter; param_enter;
+end;
+@#
+procedure read_lig_kern;
+var @!krn_ptr:0..max_kerns; {an index into |kern|}
+@!c:byte; {runs through all character codes}
+begin @<Read ligature/kern list@>;
+end;
+@#
+procedure read_char_info;
+var @!c:byte; {the char}
+begin @<Read character info list@>;
+end;
+@#
+procedure read_input;
+var @!c:byte; {header or parameter index}
+begin @<Read all the input@>;
+end;
+@#
+procedure corr_and_check;
+var @!c:0..256; {runs through all character codes}
+@!hh:0..hash_size; {an index into |hash_list|}
+@!lig_ptr:0..max_lig_steps; {an index into |lig_kern|}
+@!g:byte; {a character generated by the current character |c|}
+begin @<Correct and check the information@>
+end;
+@#
+procedure vf_output;
+var @!c:byte; {runs through all character codes}
+@!cur_font:0..256; {runs through all local fonts}
+@!k:integer; {loop index}
+begin @<Do the \.{VF} output@>;
+end;
+
+@ Here is where \.{VPtoVF} begins and ends.
+
+@p begin initialize;@/
+name_enter;@/
+read_input; print_ln('.');@/
+corr_and_check;@/
+@<Do the \.{TFM} output@>;
+vf_output;
+end.
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{VPtoVF} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/systems/knuth/dist/lib/3test.mf b/systems/knuth/dist/lib/3test.mf
new file mode 100644
index 0000000000..8a62fb1fdd
--- /dev/null
+++ b/systems/knuth/dist/lib/3test.mf
@@ -0,0 +1,53 @@
+% This tries all characters on TEST.MF using three different sets of parameters
+% (appropriate for italic styles)
+if unknown cmbase: input cmbase fi % omit if CMMF can be assumed
+mag=.5;
+mode_setup; let mode_setup=\;
+
+boolean running;
+def abort = hide(scrollmode; running:=false) enddef;
+def pause=stop "done with char "&decimal charcode&". " enddef;
+let iff=always_iff;
+def ligtable text t=enddef;
+def charlist text t=enddef;
+def extensible text t=enddef;
+
+string currenttitle;
+let semi=;; let echar=endchar; let endchar=enddef;
+def cmchar expr s = currenttitle:=s;
+ let;=testchar semi quote def chartext=enddef;
+def testchar = semi let;=semi;
+ % now `chartext echar' will generate the character to be tested
+ running:=true; errorstopmode;
+ for k=1 upto 6:
+ if running: if known params[k]: scantokens params[k]; font_setup;
+ currentwindow:=k;
+ currenttitle & ", " & fontname[k];
+ chartext echar; fi fi endfor
+ pause; enddef;
+
+string params[],fontname[];
+params[1]="ital_params"; fontname[1]="cmti10";
+params[2]="bold_ital_params"; fontname[2]="cmbxti10";
+params[3]="tt_params"; fontname[3]="cmmi10";
+
+w_rows=floor 1/2 screen_rows; w_cols=floor 1/3 screen_cols;
+def open(expr k,i,j)=
+ openwindow k from ((i-1)*w_rows,(j-1)*w_cols) to (i*w_rows,j*w_cols)
+ at (-10,140) enddef;
+def openit =
+ open(1,1,1); open(2,1,2); open(3,1,3);
+ open(4,2,1); open(5,2,2); open(6,2,3); enddef;
+
+begingroup delimiters begintext generate;
+ def makedef(expr s)(text t) =
+ expandafter def scantokens s = t enddef; flushtext enddef;
+ def flushtext suffix t = enddef;
+ for k=1 upto 6: if known params[k]:
+ makedef(params[k])
+ expandafter expandafter expandafter begintext
+ scantokens ("input "&fontname[k]); fi endfor
+endgroup;
+
+input test
+end
diff --git a/systems/knuth/dist/lib/6test.mf b/systems/knuth/dist/lib/6test.mf
new file mode 100644
index 0000000000..9c66d556fd
--- /dev/null
+++ b/systems/knuth/dist/lib/6test.mf
@@ -0,0 +1,55 @@
+% This tries all characters on TEST.MF using six different sets of parameters
+if unknown cmbase: input cmbase fi % omit if CMMF can be assumed
+mag=.5;
+mode_setup; let mode_setup=\;
+
+boolean running;
+def abort = hide(scrollmode; running:=false) enddef;
+def pause=stop "done with char "&decimal charcode&". " enddef;
+let iff=always_iff;
+def ligtable text t=enddef;
+def charlist text t=enddef;
+def extensible text t=enddef;
+
+string currenttitle;
+let semi=;; let echar=endchar; let endchar=enddef;
+def cmchar expr s = currenttitle:=s;
+ let;=testchar semi quote def chartext=enddef;
+def testchar = semi let;=semi;
+ % now `chartext echar' will generate the character to be tested
+ running:=true; errorstopmode;
+ for k=1 upto 6:
+ if running: if known params[k]: scantokens params[k]; font_setup;
+ currentwindow:=k;
+ currenttitle & ", " & fontname[k];
+ chartext echar; fi fi endfor
+ pause; enddef;
+
+string params[],fontname[];
+params[1]="roman_params"; fontname[1]="cmr10";
+params[2]="sans_params"; fontname[2]="cmssbx10";
+params[3]="tt_params"; fontname[3]="cmtt10";
+params[4]="bold_params"; fontname[4]="cmb10";
+params[5]="quote_params"; fontname[5]="cmssqi8";
+params[6]="ital_params"; fontname[6]="cmti10";
+
+w_rows=floor 1/2 screen_rows; w_cols=floor 1/3 screen_cols;
+def open(expr k,i,j)=
+ openwindow k from ((i-1)*w_rows,(j-1)*w_cols) to (i*w_rows,j*w_cols)
+ at (-10,140) enddef;
+def openit =
+ open(1,1,1); open(2,1,2); open(3,1,3);
+ open(4,2,1); open(5,2,2); open(6,2,3); enddef;
+
+begingroup delimiters begintext generate;
+ def makedef(expr s)(text t) =
+ expandafter def scantokens s = t enddef; flushtext enddef;
+ def flushtext suffix t = enddef;
+ for k=1 upto 6: if known params[k]:
+ makedef(params[k])
+ expandafter expandafter expandafter begintext
+ scantokens ("input "&fontname[k]); fi endfor
+endgroup;
+
+input test;
+bye
diff --git a/systems/knuth/dist/lib/cmbase.mft b/systems/knuth/dist/lib/cmbase.mft
new file mode 100644
index 0000000000..3a08ded31d
--- /dev/null
+++ b/systems/knuth/dist/lib/cmbase.mft
@@ -0,0 +1,76 @@
+%%%% MFT commands for the Computer Modern base
+%%% } ( ) ]] {{ }} ! ^ %%%% tokens that need no special formatting
+%%% step upto downto %%%% boldface binary operators
+%%% addto fill unfill draw undraw %%%% boldface unary operators
+%%% addto filldraw unfilldraw drawdot undrawdot erase pickup
+%%% addto exitunless stop incr decr proofrulethickness screenrule
+%%% addto define_pixels define_whole_pixels define_whole_vertical_pixels
+%%% addto define_blacker_pixels define_whole_blacker_pixels
+%%% addto define_corrected_pixels lowres_fix proofoffset penstroke
+%%% addto beginchar beginarithchar italcorr font_size font_slant
+%%% addto font_normal_space font_normal_stretch font_normal_shrink font_quad
+%%% addto font_x_height font_extra_space font_identifier font_coding_scheme
+%%% addto cmchar iff generate adjust_fit math_fit labels penlabels
+%%% addto stroke circ_stroke padded
+%%% enddef endchar %%%% boldface closing
+%%% true relax mode_setup font_setup %%%% boldface nullary operators
+%%% true clearit shipit cullit openit showit clearxy clearpen
+%%% true nodisplay notransforms screenchars screenstrokes imagerules
+%%% .. ... .... -- --- %%%% path operators made of dots and dashes
+%%% length flex abs dir %%%% unary operators to be in roman type
+%%% length unitvector inverse ceiling hround vround Vround counterclockwise
+%%% length tensepath byte reflectedabout rotatedaround magstep max min
+%%% and mod dotprod intersectionpoint softjoin %%%% binary operators to be roman
+%%% ++ ** %%%% binary operators made of two special characters
+%%% penoffset goodval direction directionpoint %%%% operators that take "of"
+%%% pausing tolerance pixels_per_inch blacker o_correction %%%% internals
+%%% pausing screen_rows screen_cols currentwindow displaying
+%%% pausing pen_top pen_bot pen_lft pen_rt shrink_fit rt lft top bot
+%%% good crisp fine tiny rule light_rule cal light_cal med_cal heavy_cal
+%%% good term fudged mfudged sloped_serif tilted med_tilted
+%%% pausing slant fudge math_spread superness superpull beak_darkness ligs
+%%% input generate
+%% \outer\def^^L{\par\vfill\eject} % obeypages
+%%
+%% % nine-point type:
+%% \catcode`@=11 % borrow the private macros of PLAIN (with care)
+%% \def\ninebig#1{{\hbox{$\textfont0=\tenrm\textfont2=\tensy
+%% \left#1\vbox to7.25pt{}\right.\n@space$}}}
+%% \catcode`@=12 % now @ is a nonletter again
+%% \font\ninerm=cmr9 \font\sixrm=cmr6
+%% \font\ninei=cmmi9 \font\sixi=cmmi6
+%% \skewchar\ninei='177 \skewchar\sixi='177
+%% \font\ninesy=cmsy9 \font\sixsy=cmsy6
+%% \skewchar\ninesy='60 \skewchar\sixsy='60
+%% \font\nineit=cmti9
+%% \font\ninesl=cmsl9
+%% \font\ninebf=cmbx9 \font\sixbf=cmbx6
+%% \font\ninett=cmtt9 \hyphenchar\ninett=-1
+%% \font\ninetex=cmtex9 \hyphenchar\ninetex=-1
+%% \def\rm{\fam0\ninerm}
+%% \textfont0=\ninerm \scriptfont0=\sixrm \scriptscriptfont0=\fiverm
+%% \textfont1=\ninei \scriptfont1=\sixi \scriptscriptfont1=\fivei
+%% \textfont2=\ninesy \scriptfont2=\sixsy \scriptscriptfont2=\fivesy
+%% \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+%% \def\it{\fam\itfam\nineit}
+%% \textfont\itfam=\nineit
+%% \def\sl{\fam\slfam\ninesl}
+%% \textfont\slfam=\ninesl
+%% \def\bf{\fam\bffam
+%% \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}%
+%% \ninebf}
+%% \textfont\bffam=\ninebf \scriptfont\bffam=\sixbf
+%% \scriptscriptfont\bffam=\fivebf
+%% \def\tt{\fam\ttfam\ninett}
+%% \textfont\ttfam=\ninett
+%% \def\finstring"#1"{\ninetex"#1"\egroup}
+%% \baselineskip=11pt
+%% \def\MF{{\manual hijk}\-{\manual lmnj}}
+%% \let\big=\ninebig
+%% \setbox\strutbox=\hbox{\vrule height8pt depth3pt width0pt}
+%% \rm
+%% \setbox\shorthyf=\hbox{-\kern-.05em}
+%% \hsize=29pc % this is the size of pages in the Computer Modern book
+%% \vsize=44pc % likewise
+%%
+%%%% \mag=\magstep1 %%%% for magnified proofs
diff --git a/systems/knuth/dist/lib/expr.mf b/systems/knuth/dist/lib/expr.mf
new file mode 100644
index 0000000000..fe05a01322
--- /dev/null
+++ b/systems/knuth/dist/lib/expr.mf
@@ -0,0 +1,5 @@
+string s[]; s1="abra";
+path p[]; p1=(0,0)..(3,3); p2=(0,0)..(3,3)..cycle;
+tracingonline:=1; scrollmode;
+forever: message "gimme an expr: "; s0:=readstring;
+show scantokens s0; endfor
diff --git a/systems/knuth/dist/lib/grayf.mf b/systems/knuth/dist/lib/grayf.mf
new file mode 100644
index 0000000000..1a34e18fd1
--- /dev/null
+++ b/systems/knuth/dist/lib/grayf.mf
@@ -0,0 +1,82 @@
+% More-or-less general gray font generator
+% See Appendix H of The METAFONTbook for how to use it
+
+forsuffixes m = mag,rep:
+ if unknown m: m := 1;
+ elseif (m<1) or (m<>floor m):
+ errmessage "Sorry, " & str m & " must be a positive integer";
+ m := 1; fi endfor
+
+mg := mag; mag := 1; mode_setup;
+if mg>1: hppp := hppp*mg; vppp := vppp*mg;
+ extra_endchar:=
+ "if charcode>0:currentpicture:=currentpicture scaled mg;fi"
+ & extra_endchar; fi;
+
+if picture pix_picture: rep := 1;
+ cull pix_picture keeping (1,infinity);
+else: for z=(0,2),(1,0),(2,3),(3,1):
+ fill unitsquare shifted z; endfor
+ if not boolean lightweight: addto currentpicture also
+ currentpicture rotated 90 xscaled -1; fi
+ if unknown scale: scale := max(1,round(pixels_per_inch/300)); fi
+ pix_wd := pix_ht := 4scale;
+ if rep>1: picture pix;
+ currentpicture := currentpicture shifted-(1,1); pix := currentpicture;
+ for r=1 upto rep-1: addto currentpicture also pix shifted(4r,0); endfor
+ cullit; pix := currentpicture;
+ for r=1 upto rep-1: addto currentpicture also pix shifted(0,4r); endfor
+ unfill unitsquare xscaled 4rep yscaled 2 shifted-(1,1);
+ unfill unitsquare yscaled 4rep xscaled 2 shifted-(1,1); cullit; fi
+ picture pix_picture; pix_picture := currentpicture scaled scale;
+ pix_wd := pix_ht := 4scale*rep; fi
+
+def # = *72.27/pixels_per_inch enddef;
+if unknown dotsize: dotsize := 2.5pix_wd/rep; fi
+
+beginchar(0,1.2dotsize#,1.2dotsize#,0);
+fill fullcircle scaled dotsize scaled mg; endchar;
+
+numeric a[]; newinternal b,k;
+def next_binary =
+ k := 0; forever: if k>b: a[incr b] := 0; fi
+ exitif a[k]=0; a[k] := 0; k := k+1; endfor
+ a[k] := 1 enddef;
+def next_special_binary =
+ if a[0]=1: for k=0 upto b: a[k] := 0; endfor a[incr b]
+ else: k := 0; forever: exitif a[incr k]=1; endfor
+ a[k-1] fi := 1 enddef;
+
+def make_char =
+ clearit; next_binary;
+ for k=0 upto b: if a[k]=1:
+ addto currentpicture also pix_picture shifted(0,-k*pix_ht); fi endfor
+ charcode := charcode+1; chardp := b*charht;
+ scantokens extra_endchar; shipout currentpicture enddef;
+
+charwd := pix_wd#; charht := pix_ht#; chardx := pix_wd*mg;
+b := -1;
+
+if boolean large_pixels:
+ for k=1 upto 7: make_char; charlist k:k+120; endfor
+ charcode := 120; b := -1;
+ addto pix_picture also pix_picture shifted (chardx,0);
+ charwd := 2charwd; chardx := 2chardx;
+ for k=1 upto 7: make_char; endfor
+else: for k=1 upto 63: make_char; endfor
+ let next_binary = next_special_binary;
+ for k=64 upto 120: make_char; endfor
+ for k=121,122: charcode := k;
+ addto currentpicture also currentpicture shifted (chardx,0);
+ charwd := 2charwd; chardx := 2chardx;
+ scantokens extra_endchar; shipout currentpicture; endfor
+ charlist 120:121:122; fi
+
+font_coding_scheme "GFGRAY";
+font_size 8(pix_wd#);
+font_normal_space pix_wd#;
+font_x_height pix_ht#;
+font_quad pix_wd#;
+fontdimen 8: if known rulethickness: rulethickness
+ else: pix_wd#/(2rep) fi;
+bye.
diff --git a/systems/knuth/dist/lib/hyphen.tex b/systems/knuth/dist/lib/hyphen.tex
new file mode 100644
index 0000000000..86e113cf4c
--- /dev/null
+++ b/systems/knuth/dist/lib/hyphen.tex
@@ -0,0 +1,4469 @@
+% The Plain TeX hyphenation tables [NOT TO BE CHANGED IN ANY WAY!]
+% Unlimited copying and redistribution of this file are permitted as long
+% as this file is not modified. Modifications are permitted, but only if
+% the resulting file is not named hyphen.tex.
+\patterns{ % just type <return> if you're not using INITEX
+.ach4
+.ad4der
+.af1t
+.al3t
+.am5at
+.an5c
+.ang4
+.ani5m
+.ant4
+.an3te
+.anti5s
+.ar5s
+.ar4tie
+.ar4ty
+.as3c
+.as1p
+.as1s
+.aster5
+.atom5
+.au1d
+.av4i
+.awn4
+.ba4g
+.ba5na
+.bas4e
+.ber4
+.be5ra
+.be3sm
+.be5sto
+.bri2
+.but4ti
+.cam4pe
+.can5c
+.capa5b
+.car5ol
+.ca4t
+.ce4la
+.ch4
+.chill5i
+.ci2
+.cit5r
+.co3e
+.co4r
+.cor5ner
+.de4moi
+.de3o
+.de3ra
+.de3ri
+.des4c
+.dictio5
+.do4t
+.du4c
+.dumb5
+.earth5
+.eas3i
+.eb4
+.eer4
+.eg2
+.el5d
+.el3em
+.enam3
+.en3g
+.en3s
+.eq5ui5t
+.er4ri
+.es3
+.eu3
+.eye5
+.fes3
+.for5mer
+.ga2
+.ge2
+.gen3t4
+.ge5og
+.gi5a
+.gi4b
+.go4r
+.hand5i
+.han5k
+.he2
+.hero5i
+.hes3
+.het3
+.hi3b
+.hi3er
+.hon5ey
+.hon3o
+.hov5
+.id4l
+.idol3
+.im3m
+.im5pin
+.in1
+.in3ci
+.ine2
+.in2k
+.in3s
+.ir5r
+.is4i
+.ju3r
+.la4cy
+.la4m
+.lat5er
+.lath5
+.le2
+.leg5e
+.len4
+.lep5
+.lev1
+.li4g
+.lig5a
+.li2n
+.li3o
+.li4t
+.mag5a5
+.mal5o
+.man5a
+.mar5ti
+.me2
+.mer3c
+.me5ter
+.mis1
+.mist5i
+.mon3e
+.mo3ro
+.mu5ta
+.muta5b
+.ni4c
+.od2
+.odd5
+.of5te
+.or5ato
+.or3c
+.or1d
+.or3t
+.os3
+.os4tl
+.oth3
+.out3
+.ped5al
+.pe5te
+.pe5tit
+.pi4e
+.pio5n
+.pi2t
+.pre3m
+.ra4c
+.ran4t
+.ratio5na
+.ree2
+.re5mit
+.res2
+.re5stat
+.ri4g
+.rit5u
+.ro4q
+.ros5t
+.row5d
+.ru4d
+.sci3e
+.self5
+.sell5
+.se2n
+.se5rie
+.sh2
+.si2
+.sing4
+.st4
+.sta5bl
+.sy2
+.ta4
+.te4
+.ten5an
+.th2
+.ti2
+.til4
+.tim5o5
+.ting4
+.tin5k
+.ton4a
+.to4p
+.top5i
+.tou5s
+.trib5ut
+.un1a
+.un3ce
+.under5
+.un1e
+.un5k
+.un5o
+.un3u
+.up3
+.ure3
+.us5a
+.ven4de
+.ve5ra
+.wil5i
+.ye4
+4ab.
+a5bal
+a5ban
+abe2
+ab5erd
+abi5a
+ab5it5ab
+ab5lat
+ab5o5liz
+4abr
+ab5rog
+ab3ul
+a4car
+ac5ard
+ac5aro
+a5ceou
+ac1er
+a5chet
+4a2ci
+a3cie
+ac1in
+a3cio
+ac5rob
+act5if
+ac3ul
+ac4um
+a2d
+ad4din
+ad5er.
+2adi
+a3dia
+ad3ica
+adi4er
+a3dio
+a3dit
+a5diu
+ad4le
+ad3ow
+ad5ran
+ad4su
+4adu
+a3duc
+ad5um
+ae4r
+aeri4e
+a2f
+aff4
+a4gab
+aga4n
+ag5ell
+age4o
+4ageu
+ag1i
+4ag4l
+ag1n
+a2go
+3agog
+ag3oni
+a5guer
+ag5ul
+a4gy
+a3ha
+a3he
+ah4l
+a3ho
+ai2
+a5ia
+a3ic.
+ai5ly
+a4i4n
+ain5in
+ain5o
+ait5en
+a1j
+ak1en
+al5ab
+al3ad
+a4lar
+4aldi
+2ale
+al3end
+a4lenti
+a5le5o
+al1i
+al4ia.
+ali4e
+al5lev
+4allic
+4alm
+a5log.
+a4ly.
+4alys
+5a5lyst
+5alyt
+3alyz
+4ama
+am5ab
+am3ag
+ama5ra
+am5asc
+a4matis
+a4m5ato
+am5era
+am3ic
+am5if
+am5ily
+am1in
+ami4no
+a2mo
+a5mon
+amor5i
+amp5en
+a2n
+an3age
+3analy
+a3nar
+an3arc
+anar4i
+a3nati
+4and
+ande4s
+an3dis
+an1dl
+an4dow
+a5nee
+a3nen
+an5est.
+a3neu
+2ang
+ang5ie
+an1gl
+a4n1ic
+a3nies
+an3i3f
+an4ime
+a5nimi
+a5nine
+an3io
+a3nip
+an3ish
+an3it
+a3niu
+an4kli
+5anniz
+ano4
+an5ot
+anoth5
+an2sa
+an4sco
+an4sn
+an2sp
+ans3po
+an4st
+an4sur
+antal4
+an4tie
+4anto
+an2tr
+an4tw
+an3ua
+an3ul
+a5nur
+4ao
+apar4
+ap5at
+ap5ero
+a3pher
+4aphi
+a4pilla
+ap5illar
+ap3in
+ap3ita
+a3pitu
+a2pl
+apoc5
+ap5ola
+apor5i
+apos3t
+aps5es
+a3pu
+aque5
+2a2r
+ar3act
+a5rade
+ar5adis
+ar3al
+a5ramete
+aran4g
+ara3p
+ar4at
+a5ratio
+ar5ativ
+a5rau
+ar5av4
+araw4
+arbal4
+ar4chan
+ar5dine
+ar4dr
+ar5eas
+a3ree
+ar3ent
+a5ress
+ar4fi
+ar4fl
+ar1i
+ar5ial
+ar3ian
+a3riet
+ar4im
+ar5inat
+ar3io
+ar2iz
+ar2mi
+ar5o5d
+a5roni
+a3roo
+ar2p
+ar3q
+arre4
+ar4sa
+ar2sh
+4as.
+as4ab
+as3ant
+ashi4
+a5sia.
+a3sib
+a3sic
+5a5si4t
+ask3i
+as4l
+a4soc
+as5ph
+as4sh
+as3ten
+as1tr
+asur5a
+a2ta
+at3abl
+at5ac
+at3alo
+at5ap
+ate5c
+at5ech
+at3ego
+at3en.
+at3era
+ater5n
+a5terna
+at3est
+at5ev
+4ath
+ath5em
+a5then
+at4ho
+ath5om
+4ati.
+a5tia
+at5i5b
+at1ic
+at3if
+ation5ar
+at3itu
+a4tog
+a2tom
+at5omiz
+a4top
+a4tos
+a1tr
+at5rop
+at4sk
+at4tag
+at5te
+at4th
+a2tu
+at5ua
+at5ue
+at3ul
+at3ura
+a2ty
+au4b
+augh3
+au3gu
+au4l2
+aun5d
+au3r
+au5sib
+aut5en
+au1th
+a2va
+av3ag
+a5van
+ave4no
+av3era
+av5ern
+av5ery
+av1i
+avi4er
+av3ig
+av5oc
+a1vor
+3away
+aw3i
+aw4ly
+aws4
+ax4ic
+ax4id
+ay5al
+aye4
+ays4
+azi4er
+azz5i
+5ba.
+bad5ger
+ba4ge
+bal1a
+ban5dag
+ban4e
+ban3i
+barbi5
+bari4a
+bas4si
+1bat
+ba4z
+2b1b
+b2be
+b3ber
+bbi4na
+4b1d
+4be.
+beak4
+beat3
+4be2d
+be3da
+be3de
+be3di
+be3gi
+be5gu
+1bel
+be1li
+be3lo
+4be5m
+be5nig
+be5nu
+4bes4
+be3sp
+be5str
+3bet
+bet5iz
+be5tr
+be3tw
+be3w
+be5yo
+2bf
+4b3h
+bi2b
+bi4d
+3bie
+bi5en
+bi4er
+2b3if
+1bil
+bi3liz
+bina5r4
+bin4d
+bi5net
+bi3ogr
+bi5ou
+bi2t
+3bi3tio
+bi3tr
+3bit5ua
+b5itz
+b1j
+bk4
+b2l2
+blath5
+b4le.
+blen4
+5blesp
+b3lis
+b4lo
+blun4t
+4b1m
+4b3n
+bne5g
+3bod
+bod3i
+bo4e
+bol3ic
+bom4bi
+bon4a
+bon5at
+3boo
+5bor.
+4b1ora
+bor5d
+5bore
+5bori
+5bos4
+b5ota
+both5
+bo4to
+bound3
+4bp
+4brit
+broth3
+2b5s2
+bsor4
+2bt
+bt4l
+b4to
+b3tr
+buf4fer
+bu4ga
+bu3li
+bumi4
+bu4n
+bunt4i
+bu3re
+bus5ie
+buss4e
+5bust
+4buta
+3butio
+b5uto
+b1v
+4b5w
+5by.
+bys4
+1ca
+cab3in
+ca1bl
+cach4
+ca5den
+4cag4
+2c5ah
+ca3lat
+cal4la
+call5in
+4calo
+can5d
+can4e
+can4ic
+can5is
+can3iz
+can4ty
+cany4
+ca5per
+car5om
+cast5er
+cas5tig
+4casy
+ca4th
+4cativ
+cav5al
+c3c
+ccha5
+cci4a
+ccompa5
+ccon4
+ccou3t
+2ce.
+4ced.
+4ceden
+3cei
+5cel.
+3cell
+1cen
+3cenc
+2cen4e
+4ceni
+3cent
+3cep
+ce5ram
+4cesa
+3cessi
+ces5si5b
+ces5t
+cet4
+c5e4ta
+cew4
+2ch
+4ch.
+4ch3ab
+5chanic
+ch5a5nis
+che2
+cheap3
+4ched
+che5lo
+3chemi
+ch5ene
+ch3er.
+ch3ers
+4ch1in
+5chine.
+ch5iness
+5chini
+5chio
+3chit
+chi2z
+3cho2
+ch4ti
+1ci
+3cia
+ci2a5b
+cia5r
+ci5c
+4cier
+5cific.
+4cii
+ci4la
+3cili
+2cim
+2cin
+c4ina
+3cinat
+cin3em
+c1ing
+c5ing.
+5cino
+cion4
+4cipe
+ci3ph
+4cipic
+4cista
+4cisti
+2c1it
+cit3iz
+5ciz
+ck1
+ck3i
+1c4l4
+4clar
+c5laratio
+5clare
+cle4m
+4clic
+clim4
+cly4
+c5n
+1co
+co5ag
+coe2
+2cog
+co4gr
+coi4
+co3inc
+col5i
+5colo
+col3or
+com5er
+con4a
+c4one
+con3g
+con5t
+co3pa
+cop3ic
+co4pl
+4corb
+coro3n
+cos4e
+cov1
+cove4
+cow5a
+coz5e
+co5zi
+c1q
+cras5t
+5crat.
+5cratic
+cre3at
+5cred
+4c3reta
+cre4v
+cri2
+cri5f
+c4rin
+cris4
+5criti
+cro4pl
+crop5o
+cros4e
+cru4d
+4c3s2
+2c1t
+cta4b
+ct5ang
+c5tant
+c2te
+c3ter
+c4ticu
+ctim3i
+ctu4r
+c4tw
+cud5
+c4uf
+c4ui
+cu5ity
+5culi
+cul4tis
+3cultu
+cu2ma
+c3ume
+cu4mi
+3cun
+cu3pi
+cu5py
+cur5a4b
+cu5ria
+1cus
+cuss4i
+3c4ut
+cu4tie
+4c5utiv
+4cutr
+1cy
+cze4
+1d2a
+5da.
+2d3a4b
+dach4
+4daf
+2dag
+da2m2
+dan3g
+dard5
+dark5
+4dary
+3dat
+4dativ
+4dato
+5dav4
+dav5e
+5day
+d1b
+d5c
+d1d4
+2de.
+deaf5
+deb5it
+de4bon
+decan4
+de4cil
+de5com
+2d1ed
+4dee.
+de5if
+deli4e
+del5i5q
+de5lo
+d4em
+5dem.
+3demic
+dem5ic.
+de5mil
+de4mons
+demor5
+1den
+de4nar
+de3no
+denti5f
+de3nu
+de1p
+de3pa
+depi4
+de2pu
+d3eq
+d4erh
+5derm
+dern5iz
+der5s
+des2
+d2es.
+de1sc
+de2s5o
+des3ti
+de3str
+de4su
+de1t
+de2to
+de1v
+dev3il
+4dey
+4d1f
+d4ga
+d3ge4t
+dg1i
+d2gy
+d1h2
+5di.
+1d4i3a
+dia5b
+di4cam
+d4ice
+3dict
+3did
+5di3en
+d1if
+di3ge
+di4lato
+d1in
+1dina
+3dine.
+5dini
+di5niz
+1dio
+dio5g
+di4pl
+dir2
+di1re
+dirt5i
+dis1
+5disi
+d4is3t
+d2iti
+1di1v
+d1j
+d5k2
+4d5la
+3dle.
+3dled
+3dles.
+4dless
+2d3lo
+4d5lu
+2dly
+d1m
+4d1n4
+1do
+3do.
+do5de
+5doe
+2d5of
+d4og
+do4la
+doli4
+do5lor
+dom5iz
+do3nat
+doni4
+doo3d
+dop4p
+d4or
+3dos
+4d5out
+do4v
+3dox
+d1p
+1dr
+drag5on
+4drai
+dre4
+drea5r
+5dren
+dri4b
+dril4
+dro4p
+4drow
+5drupli
+4dry
+2d1s2
+ds4p
+d4sw
+d4sy
+d2th
+1du
+d1u1a
+du2c
+d1uca
+duc5er
+4duct.
+4ducts
+du5el
+du4g
+d3ule
+dum4be
+du4n
+4dup
+du4pe
+d1v
+d1w
+d2y
+5dyn
+dy4se
+dys5p
+e1a4b
+e3act
+ead1
+ead5ie
+ea4ge
+ea5ger
+ea4l
+eal5er
+eal3ou
+eam3er
+e5and
+ear3a
+ear4c
+ear5es
+ear4ic
+ear4il
+ear5k
+ear2t
+eart3e
+ea5sp
+e3ass
+east3
+ea2t
+eat5en
+eath3i
+e5atif
+e4a3tu
+ea2v
+eav3en
+eav5i
+eav5o
+2e1b
+e4bel.
+e4bels
+e4ben
+e4bit
+e3br
+e4cad
+ecan5c
+ecca5
+e1ce
+ec5essa
+ec2i
+e4cib
+ec5ificat
+ec5ifie
+ec5ify
+ec3im
+eci4t
+e5cite
+e4clam
+e4clus
+e2col
+e4comm
+e4compe
+e4conc
+e2cor
+ec3ora
+eco5ro
+e1cr
+e4crem
+ec4tan
+ec4te
+e1cu
+e4cul
+ec3ula
+2e2da
+4ed3d
+e4d1er
+ede4s
+4edi
+e3dia
+ed3ib
+ed3ica
+ed3im
+ed1it
+edi5z
+4edo
+e4dol
+edon2
+e4dri
+e4dul
+ed5ulo
+ee2c
+eed3i
+ee2f
+eel3i
+ee4ly
+ee2m
+ee4na
+ee4p1
+ee2s4
+eest4
+ee4ty
+e5ex
+e1f
+e4f3ere
+1eff
+e4fic
+5efici
+efil4
+e3fine
+ef5i5nite
+3efit
+efor5es
+e4fuse.
+4egal
+eger4
+eg5ib
+eg4ic
+eg5ing
+e5git5
+eg5n
+e4go.
+e4gos
+eg1ul
+e5gur
+5egy
+e1h4
+eher4
+ei2
+e5ic
+ei5d
+eig2
+ei5gl
+e3imb
+e3inf
+e1ing
+e5inst
+eir4d
+eit3e
+ei3th
+e5ity
+e1j
+e4jud
+ej5udi
+eki4n
+ek4la
+e1la
+e4la.
+e4lac
+elan4d
+el5ativ
+e4law
+elaxa4
+e3lea
+el5ebra
+5elec
+e4led
+el3ega
+e5len
+e4l1er
+e1les
+el2f
+el2i
+e3libe
+e4l5ic.
+el3ica
+e3lier
+el5igib
+e5lim
+e4l3ing
+e3lio
+e2lis
+el5ish
+e3liv3
+4ella
+el4lab
+ello4
+e5loc
+el5og
+el3op.
+el2sh
+el4ta
+e5lud
+el5ug
+e4mac
+e4mag
+e5man
+em5ana
+em5b
+e1me
+e2mel
+e4met
+em3ica
+emi4e
+em5igra
+em1in2
+em5ine
+em3i3ni
+e4mis
+em5ish
+e5miss
+em3iz
+5emniz
+emo4g
+emoni5o
+em3pi
+e4mul
+em5ula
+emu3n
+e3my
+en5amo
+e4nant
+ench4er
+en3dic
+e5nea
+e5nee
+en3em
+en5ero
+en5esi
+en5est
+en3etr
+e3new
+en5ics
+e5nie
+e5nil
+e3nio
+en3ish
+en3it
+e5niu
+5eniz
+4enn
+4eno
+eno4g
+e4nos
+en3ov
+en4sw
+ent5age
+4enthes
+en3ua
+en5uf
+e3ny.
+4en3z
+e5of
+eo2g
+e4oi4
+e3ol
+eop3ar
+e1or
+eo3re
+eo5rol
+eos4
+e4ot
+eo4to
+e5out
+e5ow
+e2pa
+e3pai
+ep5anc
+e5pel
+e3pent
+ep5etitio
+ephe4
+e4pli
+e1po
+e4prec
+ep5reca
+e4pred
+ep3reh
+e3pro
+e4prob
+ep4sh
+ep5ti5b
+e4put
+ep5uta
+e1q
+equi3l
+e4q3ui3s
+er1a
+era4b
+4erand
+er3ar
+4erati.
+2erb
+er4bl
+er3ch
+er4che
+2ere.
+e3real
+ere5co
+ere3in
+er5el.
+er3emo
+er5ena
+er5ence
+4erene
+er3ent
+ere4q
+er5ess
+er3est
+eret4
+er1h
+er1i
+e1ria4
+5erick
+e3rien
+eri4er
+er3ine
+e1rio
+4erit
+er4iu
+eri4v
+e4riva
+er3m4
+er4nis
+4ernit
+5erniz
+er3no
+2ero
+er5ob
+e5roc
+ero4r
+er1ou
+er1s
+er3set
+ert3er
+4ertl
+er3tw
+4eru
+eru4t
+5erwau
+e1s4a
+e4sage.
+e4sages
+es2c
+e2sca
+es5can
+e3scr
+es5cu
+e1s2e
+e2sec
+es5ecr
+es5enc
+e4sert.
+e4serts
+e4serva
+4esh
+e3sha
+esh5en
+e1si
+e2sic
+e2sid
+es5iden
+es5igna
+e2s5im
+es4i4n
+esis4te
+esi4u
+e5skin
+es4mi
+e2sol
+es3olu
+e2son
+es5ona
+e1sp
+es3per
+es5pira
+es4pre
+2ess
+es4si4b
+estan4
+es3tig
+es5tim
+4es2to
+e3ston
+2estr
+e5stro
+estruc5
+e2sur
+es5urr
+es4w
+eta4b
+eten4d
+e3teo
+ethod3
+et1ic
+e5tide
+etin4
+eti4no
+e5tir
+e5titio
+et5itiv
+4etn
+et5ona
+e3tra
+e3tre
+et3ric
+et5rif
+et3rog
+et5ros
+et3ua
+et5ym
+et5z
+4eu
+e5un
+e3up
+eu3ro
+eus4
+eute4
+euti5l
+eu5tr
+eva2p5
+e2vas
+ev5ast
+e5vea
+ev3ell
+evel3o
+e5veng
+even4i
+ev1er
+e5verb
+e1vi
+ev3id
+evi4l
+e4vin
+evi4v
+e5voc
+e5vu
+e1wa
+e4wag
+e5wee
+e3wh
+ewil5
+ew3ing
+e3wit
+1exp
+5eyc
+5eye.
+eys4
+1fa
+fa3bl
+fab3r
+fa4ce
+4fag
+fain4
+fall5e
+4fa4ma
+fam5is
+5far
+far5th
+fa3ta
+fa3the
+4fato
+fault5
+4f5b
+4fd
+4fe.
+feas4
+feath3
+fe4b
+4feca
+5fect
+2fed
+fe3li
+fe4mo
+fen2d
+fend5e
+fer1
+5ferr
+fev4
+4f1f
+f4fes
+f4fie
+f5fin.
+f2f5is
+f4fly
+f2fy
+4fh
+1fi
+fi3a
+2f3ic.
+4f3ical
+f3ican
+4ficate
+f3icen
+fi3cer
+fic4i
+5ficia
+5ficie
+4fics
+fi3cu
+fi5del
+fight5
+fil5i
+fill5in
+4fily
+2fin
+5fina
+fin2d5
+fi2ne
+f1in3g
+fin4n
+fis4ti
+f4l2
+f5less
+flin4
+flo3re
+f2ly5
+4fm
+4fn
+1fo
+5fon
+fon4de
+fon4t
+fo2r
+fo5rat
+for5ay
+fore5t
+for4i
+fort5a
+fos5
+4f5p
+fra4t
+f5rea
+fres5c
+fri2
+fril4
+frol5
+2f3s
+2ft
+f4to
+f2ty
+3fu
+fu5el
+4fug
+fu4min
+fu5ne
+fu3ri
+fusi4
+fus4s
+4futa
+1fy
+1ga
+gaf4
+5gal.
+3gali
+ga3lo
+2gam
+ga5met
+g5amo
+gan5is
+ga3niz
+gani5za
+4gano
+gar5n4
+gass4
+gath3
+4gativ
+4gaz
+g3b
+gd4
+2ge.
+2ged
+geez4
+gel4in
+ge5lis
+ge5liz
+4gely
+1gen
+ge4nat
+ge5niz
+4geno
+4geny
+1geo
+ge3om
+g4ery
+5gesi
+geth5
+4geto
+ge4ty
+ge4v
+4g1g2
+g2ge
+g3ger
+gglu5
+ggo4
+gh3in
+gh5out
+gh4to
+5gi.
+1gi4a
+gia5r
+g1ic
+5gicia
+g4ico
+gien5
+5gies.
+gil4
+g3imen
+3g4in.
+gin5ge
+5g4ins
+5gio
+3gir
+gir4l
+g3isl
+gi4u
+5giv
+3giz
+gl2
+gla4
+glad5i
+5glas
+1gle
+gli4b
+g3lig
+3glo
+glo3r
+g1m
+g4my
+gn4a
+g4na.
+gnet4t
+g1ni
+g2nin
+g4nio
+g1no
+g4non
+1go
+3go.
+gob5
+5goe
+3g4o4g
+go3is
+gon2
+4g3o3na
+gondo5
+go3ni
+5goo
+go5riz
+gor5ou
+5gos.
+gov1
+g3p
+1gr
+4grada
+g4rai
+gran2
+5graph.
+g5rapher
+5graphic
+4graphy
+4gray
+gre4n
+4gress.
+4grit
+g4ro
+gruf4
+gs2
+g5ste
+gth3
+gu4a
+3guard
+2gue
+5gui5t
+3gun
+3gus
+4gu4t
+g3w
+1gy
+2g5y3n
+gy5ra
+h3ab4l
+hach4
+hae4m
+hae4t
+h5agu
+ha3la
+hala3m
+ha4m
+han4ci
+han4cy
+5hand.
+han4g
+hang5er
+hang5o
+h5a5niz
+han4k
+han4te
+hap3l
+hap5t
+ha3ran
+ha5ras
+har2d
+hard3e
+har4le
+harp5en
+har5ter
+has5s
+haun4
+5haz
+haz3a
+h1b
+1head
+3hear
+he4can
+h5ecat
+h4ed
+he5do5
+he3l4i
+hel4lis
+hel4ly
+h5elo
+hem4p
+he2n
+hena4
+hen5at
+heo5r
+hep5
+h4era
+hera3p
+her4ba
+here5a
+h3ern
+h5erou
+h3ery
+h1es
+he2s5p
+he4t
+het4ed
+heu4
+h1f
+h1h
+hi5an
+hi4co
+high5
+h4il2
+himer4
+h4ina
+hion4e
+hi4p
+hir4l
+hi3ro
+hir4p
+hir4r
+his3el
+his4s
+hith5er
+hi2v
+4hk
+4h1l4
+hlan4
+h2lo
+hlo3ri
+4h1m
+hmet4
+2h1n
+h5odiz
+h5ods
+ho4g
+hoge4
+hol5ar
+3hol4e
+ho4ma
+home3
+hon4a
+ho5ny
+3hood
+hoon4
+hor5at
+ho5ris
+hort3e
+ho5ru
+hos4e
+ho5sen
+hos1p
+1hous
+house3
+hov5el
+4h5p
+4hr4
+hree5
+hro5niz
+hro3po
+4h1s2
+h4sh
+h4tar
+ht1en
+ht5es
+h4ty
+hu4g
+hu4min
+hun5ke
+hun4t
+hus3t4
+hu4t
+h1w
+h4wart
+hy3pe
+hy3ph
+hy2s
+2i1a
+i2al
+iam4
+iam5ete
+i2an
+4ianc
+ian3i
+4ian4t
+ia5pe
+iass4
+i4ativ
+ia4tric
+i4atu
+ibe4
+ib3era
+ib5ert
+ib5ia
+ib3in
+ib5it.
+ib5ite
+i1bl
+ib3li
+i5bo
+i1br
+i2b5ri
+i5bun
+4icam
+5icap
+4icar
+i4car.
+i4cara
+icas5
+i4cay
+iccu4
+4iceo
+4ich
+2ici
+i5cid
+ic5ina
+i2cip
+ic3ipa
+i4cly
+i2c5oc
+4i1cr
+5icra
+i4cry
+ic4te
+ictu2
+ic4t3ua
+ic3ula
+ic4um
+ic5uo
+i3cur
+2id
+i4dai
+id5anc
+id5d
+ide3al
+ide4s
+i2di
+id5ian
+idi4ar
+i5die
+id3io
+idi5ou
+id1it
+id5iu
+i3dle
+i4dom
+id3ow
+i4dr
+i2du
+id5uo
+2ie4
+ied4e
+5ie5ga
+ield3
+ien5a4
+ien4e
+i5enn
+i3enti
+i1er.
+i3esc
+i1est
+i3et
+4if.
+if5ero
+iff5en
+if4fr
+4ific.
+i3fie
+i3fl
+4ift
+2ig
+iga5b
+ig3era
+ight3i
+4igi
+i3gib
+ig3il
+ig3in
+ig3it
+i4g4l
+i2go
+ig3or
+ig5ot
+i5gre
+igu5i
+ig1ur
+i3h
+4i5i4
+i3j
+4ik
+i1la
+il3a4b
+i4lade
+i2l5am
+ila5ra
+i3leg
+il1er
+ilev4
+il5f
+il1i
+il3ia
+il2ib
+il3io
+il4ist
+2ilit
+il2iz
+ill5ab
+4iln
+il3oq
+il4ty
+il5ur
+il3v
+i4mag
+im3age
+ima5ry
+imenta5r
+4imet
+im1i
+im5ida
+imi5le
+i5mini
+4imit
+im4ni
+i3mon
+i2mu
+im3ula
+2in.
+i4n3au
+4inav
+incel4
+in3cer
+4ind
+in5dling
+2ine
+i3nee
+iner4ar
+i5ness
+4inga
+4inge
+in5gen
+4ingi
+in5gling
+4ingo
+4ingu
+2ini
+i5ni.
+i4nia
+in3io
+in1is
+i5nite.
+5initio
+in3ity
+4ink
+4inl
+2inn
+2i1no
+i4no4c
+ino4s
+i4not
+2ins
+in3se
+insur5a
+2int.
+2in4th
+in1u
+i5nus
+4iny
+2io
+4io.
+ioge4
+io2gr
+i1ol
+io4m
+ion3at
+ion4ery
+ion3i
+io5ph
+ior3i
+i4os
+io5th
+i5oti
+io4to
+i4our
+2ip
+ipe4
+iphras4
+ip3i
+ip4ic
+ip4re4
+ip3ul
+i3qua
+iq5uef
+iq3uid
+iq3ui3t
+4ir
+i1ra
+ira4b
+i4rac
+ird5e
+ire4de
+i4ref
+i4rel4
+i4res
+ir5gi
+ir1i
+iri5de
+ir4is
+iri3tu
+5i5r2iz
+ir4min
+iro4g
+5iron.
+ir5ul
+2is.
+is5ag
+is3ar
+isas5
+2is1c
+is3ch
+4ise
+is3er
+3isf
+is5han
+is3hon
+ish5op
+is3ib
+isi4d
+i5sis
+is5itiv
+4is4k
+islan4
+4isms
+i2so
+iso5mer
+is1p
+is2pi
+is4py
+4is1s
+is4sal
+issen4
+is4ses
+is4ta.
+is1te
+is1ti
+ist4ly
+4istral
+i2su
+is5us
+4ita.
+ita4bi
+i4tag
+4ita5m
+i3tan
+i3tat
+2ite
+it3era
+i5teri
+it4es
+2ith
+i1ti
+4itia
+4i2tic
+it3ica
+5i5tick
+it3ig
+it5ill
+i2tim
+2itio
+4itis
+i4tism
+i2t5o5m
+4iton
+i4tram
+it5ry
+4itt
+it3uat
+i5tud
+it3ul
+4itz.
+i1u
+2iv
+iv3ell
+iv3en.
+i4v3er.
+i4vers.
+iv5il.
+iv5io
+iv1it
+i5vore
+iv3o3ro
+i4v3ot
+4i5w
+ix4o
+4iy
+4izar
+izi4
+5izont
+5ja
+jac4q
+ja4p
+1je
+jer5s
+4jestie
+4jesty
+jew3
+jo4p
+5judg
+3ka.
+k3ab
+k5ag
+kais4
+kal4
+k1b
+k2ed
+1kee
+ke4g
+ke5li
+k3en4d
+k1er
+kes4
+k3est.
+ke4ty
+k3f
+kh4
+k1i
+5ki.
+5k2ic
+k4ill
+kilo5
+k4im
+k4in.
+kin4de
+k5iness
+kin4g
+ki4p
+kis4
+k5ish
+kk4
+k1l
+4kley
+4kly
+k1m
+k5nes
+1k2no
+ko5r
+kosh4
+k3ou
+kro5n
+4k1s2
+k4sc
+ks4l
+k4sy
+k5t
+k1w
+lab3ic
+l4abo
+laci4
+l4ade
+la3dy
+lag4n
+lam3o
+3land
+lan4dl
+lan5et
+lan4te
+lar4g
+lar3i
+las4e
+la5tan
+4lateli
+4lativ
+4lav
+la4v4a
+2l1b
+lbin4
+4l1c2
+lce4
+l3ci
+2ld
+l2de
+ld4ere
+ld4eri
+ldi4
+ld5is
+l3dr
+l4dri
+le2a
+le4bi
+left5
+5leg.
+5legg
+le4mat
+lem5atic
+4len.
+3lenc
+5lene.
+1lent
+le3ph
+le4pr
+lera5b
+ler4e
+3lerg
+3l4eri
+l4ero
+les2
+le5sco
+5lesq
+3less
+5less.
+l3eva
+lev4er.
+lev4era
+lev4ers
+3ley
+4leye
+2lf
+l5fr
+4l1g4
+l5ga
+lgar3
+l4ges
+lgo3
+2l3h
+li4ag
+li2am
+liar5iz
+li4as
+li4ato
+li5bi
+5licio
+li4cor
+4lics
+4lict.
+l4icu
+l3icy
+l3ida
+lid5er
+3lidi
+lif3er
+l4iff
+li4fl
+5ligate
+3ligh
+li4gra
+3lik
+4l4i4l
+lim4bl
+lim3i
+li4mo
+l4im4p
+l4ina
+1l4ine
+lin3ea
+lin3i
+link5er
+li5og
+4l4iq
+lis4p
+l1it
+l2it.
+5litica
+l5i5tics
+liv3er
+l1iz
+4lj
+lka3
+l3kal
+lka4t
+l1l
+l4law
+l2le
+l5lea
+l3lec
+l3leg
+l3lel
+l3le4n
+l3le4t
+ll2i
+l2lin4
+l5lina
+ll4o
+lloqui5
+ll5out
+l5low
+2lm
+l5met
+lm3ing
+l4mod
+lmon4
+2l1n2
+3lo.
+lob5al
+lo4ci
+4lof
+3logic
+l5ogo
+3logu
+lom3er
+5long
+lon4i
+l3o3niz
+lood5
+5lope.
+lop3i
+l3opm
+lora4
+lo4rato
+lo5rie
+lor5ou
+5los.
+los5et
+5losophiz
+5losophy
+los4t
+lo4ta
+loun5d
+2lout
+4lov
+2lp
+lpa5b
+l3pha
+l5phi
+lp5ing
+l3pit
+l4pl
+l5pr
+4l1r
+2l1s2
+l4sc
+l2se
+l4sie
+4lt
+lt5ag
+ltane5
+l1te
+lten4
+ltera4
+lth3i
+l5ties.
+ltis4
+l1tr
+ltu2
+ltur3a
+lu5a
+lu3br
+luch4
+lu3ci
+lu3en
+luf4
+lu5id
+lu4ma
+5lumi
+l5umn.
+5lumnia
+lu3o
+luo3r
+4lup
+luss4
+lus3te
+1lut
+l5ven
+l5vet4
+2l1w
+1ly
+4lya
+4lyb
+ly5me
+ly3no
+2lys4
+l5yse
+1ma
+2mab
+ma2ca
+ma5chine
+ma4cl
+mag5in
+5magn
+2mah
+maid5
+4mald
+ma3lig
+ma5lin
+mal4li
+mal4ty
+5mania
+man5is
+man3iz
+4map
+ma5rine.
+ma5riz
+mar4ly
+mar3v
+ma5sce
+mas4e
+mas1t
+5mate
+math3
+ma3tis
+4matiza
+4m1b
+mba4t5
+m5bil
+m4b3ing
+mbi4v
+4m5c
+4me.
+2med
+4med.
+5media
+me3die
+m5e5dy
+me2g
+mel5on
+mel4t
+me2m
+mem1o3
+1men
+men4a
+men5ac
+men4de
+4mene
+men4i
+mens4
+mensu5
+3ment
+men4te
+me5on
+m5ersa
+2mes
+3mesti
+me4ta
+met3al
+me1te
+me5thi
+m4etr
+5metric
+me5trie
+me3try
+me4v
+4m1f
+2mh
+5mi.
+mi3a
+mid4a
+mid4g
+mig4
+3milia
+m5i5lie
+m4ill
+min4a
+3mind
+m5inee
+m4ingl
+min5gli
+m5ingly
+min4t
+m4inu
+miot4
+m2is
+mis4er.
+mis5l
+mis4ti
+m5istry
+4mith
+m2iz
+4mk
+4m1l
+m1m
+mma5ry
+4m1n
+mn4a
+m4nin
+mn4o
+1mo
+4mocr
+5mocratiz
+mo2d1
+mo4go
+mois2
+moi5se
+4mok
+mo5lest
+mo3me
+mon5et
+mon5ge
+moni3a
+mon4ism
+mon4ist
+mo3niz
+monol4
+mo3ny.
+mo2r
+4mora.
+mos2
+mo5sey
+mo3sp
+moth3
+m5ouf
+3mous
+mo2v
+4m1p
+mpara5
+mpa5rab
+mpar5i
+m3pet
+mphas4
+m2pi
+mpi4a
+mp5ies
+m4p1in
+m5pir
+mp5is
+mpo3ri
+mpos5ite
+m4pous
+mpov5
+mp4tr
+m2py
+4m3r
+4m1s2
+m4sh
+m5si
+4mt
+1mu
+mula5r4
+5mult
+multi3
+3mum
+mun2
+4mup
+mu4u
+4mw
+1na
+2n1a2b
+n4abu
+4nac.
+na4ca
+n5act
+nag5er.
+nak4
+na4li
+na5lia
+4nalt
+na5mit
+n2an
+nanci4
+nan4it
+nank4
+nar3c
+4nare
+nar3i
+nar4l
+n5arm
+n4as
+nas4c
+nas5ti
+n2at
+na3tal
+nato5miz
+n2au
+nau3se
+3naut
+nav4e
+4n1b4
+ncar5
+n4ces.
+n3cha
+n5cheo
+n5chil
+n3chis
+nc1in
+nc4it
+ncour5a
+n1cr
+n1cu
+n4dai
+n5dan
+n1de
+nd5est.
+ndi4b
+n5d2if
+n1dit
+n3diz
+n5duc
+ndu4r
+nd2we
+2ne.
+n3ear
+ne2b
+neb3u
+ne2c
+5neck
+2ned
+ne4gat
+neg5ativ
+5nege
+ne4la
+nel5iz
+ne5mi
+ne4mo
+1nen
+4nene
+3neo
+ne4po
+ne2q
+n1er
+nera5b
+n4erar
+n2ere
+n4er5i
+ner4r
+1nes
+2nes.
+4nesp
+2nest
+4nesw
+3netic
+ne4v
+n5eve
+ne4w
+n3f
+n4gab
+n3gel
+nge4n4e
+n5gere
+n3geri
+ng5ha
+n3gib
+ng1in
+n5git
+n4gla
+ngov4
+ng5sh
+n1gu
+n4gum
+n2gy
+4n1h4
+nha4
+nhab3
+nhe4
+3n4ia
+ni3an
+ni4ap
+ni3ba
+ni4bl
+ni4d
+ni5di
+ni4er
+ni2fi
+ni5ficat
+n5igr
+nik4
+n1im
+ni3miz
+n1in
+5nine.
+nin4g
+ni4o
+5nis.
+nis4ta
+n2it
+n4ith
+3nitio
+n3itor
+ni3tr
+n1j
+4nk2
+n5kero
+n3ket
+nk3in
+n1kl
+4n1l
+n5m
+nme4
+nmet4
+4n1n2
+nne4
+nni3al
+nni4v
+nob4l
+no3ble
+n5ocl
+4n3o2d
+3noe
+4nog
+noge4
+nois5i
+no5l4i
+5nologis
+3nomic
+n5o5miz
+no4mo
+no3my
+no4n
+non4ag
+non5i
+n5oniz
+4nop
+5nop5o5li
+nor5ab
+no4rary
+4nosc
+nos4e
+nos5t
+no5ta
+1nou
+3noun
+nov3el3
+nowl3
+n1p4
+npi4
+npre4c
+n1q
+n1r
+nru4
+2n1s2
+ns5ab
+nsati4
+ns4c
+n2se
+n4s3es
+nsid1
+nsig4
+n2sl
+ns3m
+n4soc
+ns4pe
+n5spi
+nsta5bl
+n1t
+nta4b
+nter3s
+nt2i
+n5tib
+nti4er
+nti2f
+n3tine
+n4t3ing
+nti4p
+ntrol5li
+nt4s
+ntu3me
+nu1a
+nu4d
+nu5en
+nuf4fe
+n3uin
+3nu3it
+n4um
+nu1me
+n5umi
+3nu4n
+n3uo
+nu3tr
+n1v2
+n1w4
+nym4
+nyp4
+4nz
+n3za
+4oa
+oad3
+o5a5les
+oard3
+oas4e
+oast5e
+oat5i
+ob3a3b
+o5bar
+obe4l
+o1bi
+o2bin
+ob5ing
+o3br
+ob3ul
+o1ce
+och4
+o3chet
+ocif3
+o4cil
+o4clam
+o4cod
+oc3rac
+oc5ratiz
+ocre3
+5ocrit
+octor5a
+oc3ula
+o5cure
+od5ded
+od3ic
+odi3o
+o2do4
+odor3
+od5uct.
+od5ucts
+o4el
+o5eng
+o3er
+oe4ta
+o3ev
+o2fi
+of5ite
+ofit4t
+o2g5a5r
+og5ativ
+o4gato
+o1ge
+o5gene
+o5geo
+o4ger
+o3gie
+1o1gis
+og3it
+o4gl
+o5g2ly
+3ogniz
+o4gro
+ogu5i
+1ogy
+2ogyn
+o1h2
+ohab5
+oi2
+oic3es
+oi3der
+oiff4
+oig4
+oi5let
+o3ing
+oint5er
+o5ism
+oi5son
+oist5en
+oi3ter
+o5j
+2ok
+o3ken
+ok5ie
+o1la
+o4lan
+olass4
+ol2d
+old1e
+ol3er
+o3lesc
+o3let
+ol4fi
+ol2i
+o3lia
+o3lice
+ol5id.
+o3li4f
+o5lil
+ol3ing
+o5lio
+o5lis.
+ol3ish
+o5lite
+o5litio
+o5liv
+olli4e
+ol5ogiz
+olo4r
+ol5pl
+ol2t
+ol3ub
+ol3ume
+ol3un
+o5lus
+ol2v
+o2ly
+om5ah
+oma5l
+om5atiz
+om2be
+om4bl
+o2me
+om3ena
+om5erse
+o4met
+om5etry
+o3mia
+om3ic.
+om3ica
+o5mid
+om1in
+o5mini
+5ommend
+omo4ge
+o4mon
+om3pi
+ompro5
+o2n
+on1a
+on4ac
+o3nan
+on1c
+3oncil
+2ond
+on5do
+o3nen
+on5est
+on4gu
+on1ic
+o3nio
+on1is
+o5niu
+on3key
+on4odi
+on3omy
+on3s
+onspi4
+onspir5a
+onsu4
+onten4
+on3t4i
+ontif5
+on5um
+onva5
+oo2
+ood5e
+ood5i
+oo4k
+oop3i
+o3ord
+oost5
+o2pa
+ope5d
+op1er
+3opera
+4operag
+2oph
+o5phan
+o5pher
+op3ing
+o3pit
+o5pon
+o4posi
+o1pr
+op1u
+opy5
+o1q
+o1ra
+o5ra.
+o4r3ag
+or5aliz
+or5ange
+ore5a
+o5real
+or3ei
+ore5sh
+or5est.
+orew4
+or4gu
+4o5ria
+or3ica
+o5ril
+or1in
+o1rio
+or3ity
+o3riu
+or2mi
+orn2e
+o5rof
+or3oug
+or5pe
+3orrh
+or4se
+ors5en
+orst4
+or3thi
+or3thy
+or4ty
+o5rum
+o1ry
+os3al
+os2c
+os4ce
+o3scop
+4oscopi
+o5scr
+os4i4e
+os5itiv
+os3ito
+os3ity
+osi4u
+os4l
+o2so
+os4pa
+os4po
+os2ta
+o5stati
+os5til
+os5tit
+o4tan
+otele4g
+ot3er.
+ot5ers
+o4tes
+4oth
+oth5esi
+oth3i4
+ot3ic.
+ot5ica
+o3tice
+o3tif
+o3tis
+oto5s
+ou2
+ou3bl
+ouch5i
+ou5et
+ou4l
+ounc5er
+oun2d
+ou5v
+ov4en
+over4ne
+over3s
+ov4ert
+o3vis
+oviti4
+o5v4ol
+ow3der
+ow3el
+ow5est
+ow1i
+own5i
+o4wo
+oy1a
+1pa
+pa4ca
+pa4ce
+pac4t
+p4ad
+5pagan
+p3agat
+p4ai
+pain4
+p4al
+pan4a
+pan3el
+pan4ty
+pa3ny
+pa1p
+pa4pu
+para5bl
+par5age
+par5di
+3pare
+par5el
+p4a4ri
+par4is
+pa2te
+pa5ter
+5pathic
+pa5thy
+pa4tric
+pav4
+3pay
+4p1b
+pd4
+4pe.
+3pe4a
+pear4l
+pe2c
+2p2ed
+3pede
+3pedi
+pedia4
+ped4ic
+p4ee
+pee4d
+pek4
+pe4la
+peli4e
+pe4nan
+p4enc
+pen4th
+pe5on
+p4era.
+pera5bl
+p4erag
+p4eri
+peri5st
+per4mal
+perme5
+p4ern
+per3o
+per3ti
+pe5ru
+per1v
+pe2t
+pe5ten
+pe5tiz
+4pf
+4pg
+4ph.
+phar5i
+phe3no
+ph4er
+ph4es.
+ph1ic
+5phie
+ph5ing
+5phisti
+3phiz
+ph2l
+3phob
+3phone
+5phoni
+pho4r
+4phs
+ph3t
+5phu
+1phy
+pi3a
+pian4
+pi4cie
+pi4cy
+p4id
+p5ida
+pi3de
+5pidi
+3piec
+pi3en
+pi4grap
+pi3lo
+pi2n
+p4in.
+pind4
+p4ino
+3pi1o
+pion4
+p3ith
+pi5tha
+pi2tu
+2p3k2
+1p2l2
+3plan
+plas5t
+pli3a
+pli5er
+4plig
+pli4n
+ploi4
+plu4m
+plum4b
+4p1m
+2p3n
+po4c
+5pod.
+po5em
+po3et5
+5po4g
+poin2
+5point
+poly5t
+po4ni
+po4p
+1p4or
+po4ry
+1pos
+pos1s
+p4ot
+po4ta
+5poun
+4p1p
+ppa5ra
+p2pe
+p4ped
+p5pel
+p3pen
+p3per
+p3pet
+ppo5site
+pr2
+pray4e
+5preci
+pre5co
+pre3em
+pref5ac
+pre4la
+pre3r
+p3rese
+3press
+pre5ten
+pre3v
+5pri4e
+prin4t3
+pri4s
+pris3o
+p3roca
+prof5it
+pro3l
+pros3e
+pro1t
+2p1s2
+p2se
+ps4h
+p4sib
+2p1t
+pt5a4b
+p2te
+p2th
+pti3m
+ptu4r
+p4tw
+pub3
+pue4
+puf4
+pul3c
+pu4m
+pu2n
+pur4r
+5pus
+pu2t
+5pute
+put3er
+pu3tr
+put4ted
+put4tin
+p3w
+qu2
+qua5v
+2que.
+3quer
+3quet
+2rab
+ra3bi
+rach4e
+r5acl
+raf5fi
+raf4t
+r2ai
+ra4lo
+ram3et
+r2ami
+rane5o
+ran4ge
+r4ani
+ra5no
+rap3er
+3raphy
+rar5c
+rare4
+rar5ef
+4raril
+r2as
+ration4
+rau4t
+ra5vai
+rav3el
+ra5zie
+r1b
+r4bab
+r4bag
+rbi2
+rbi4f
+r2bin
+r5bine
+rb5ing.
+rb4o
+r1c
+r2ce
+rcen4
+r3cha
+rch4er
+r4ci4b
+rc4it
+rcum3
+r4dal
+rd2i
+rdi4a
+rdi4er
+rdin4
+rd3ing
+2re.
+re1al
+re3an
+re5arr
+5reav
+re4aw
+r5ebrat
+rec5oll
+rec5ompe
+re4cre
+2r2ed
+re1de
+re3dis
+red5it
+re4fac
+re2fe
+re5fer.
+re3fi
+re4fy
+reg3is
+re5it
+re1li
+re5lu
+r4en4ta
+ren4te
+re1o
+re5pin
+re4posi
+re1pu
+r1er4
+r4eri
+rero4
+re5ru
+r4es.
+re4spi
+ress5ib
+res2t
+re5stal
+re3str
+re4ter
+re4ti4z
+re3tri
+reu2
+re5uti
+rev2
+re4val
+rev3el
+r5ev5er.
+re5vers
+re5vert
+re5vil
+rev5olu
+re4wh
+r1f
+rfu4
+r4fy
+rg2
+rg3er
+r3get
+r3gic
+rgi4n
+rg3ing
+r5gis
+r5git
+r1gl
+rgo4n
+r3gu
+rh4
+4rh.
+4rhal
+ri3a
+ria4b
+ri4ag
+r4ib
+rib3a
+ric5as
+r4ice
+4rici
+5ricid
+ri4cie
+r4ico
+rid5er
+ri3enc
+ri3ent
+ri1er
+ri5et
+rig5an
+5rigi
+ril3iz
+5riman
+rim5i
+3rimo
+rim4pe
+r2ina
+5rina.
+rin4d
+rin4e
+rin4g
+ri1o
+5riph
+riph5e
+ri2pl
+rip5lic
+r4iq
+r2is
+r4is.
+ris4c
+r3ish
+ris4p
+ri3ta3b
+r5ited.
+rit5er.
+rit5ers
+rit3ic
+ri2tu
+rit5ur
+riv5el
+riv3et
+riv3i
+r3j
+r3ket
+rk4le
+rk4lin
+r1l
+rle4
+r2led
+r4lig
+r4lis
+rl5ish
+r3lo4
+r1m
+rma5c
+r2me
+r3men
+rm5ers
+rm3ing
+r4ming.
+r4mio
+r3mit
+r4my
+r4nar
+r3nel
+r4ner
+r5net
+r3ney
+r5nic
+r1nis4
+r3nit
+r3niv
+rno4
+r4nou
+r3nu
+rob3l
+r2oc
+ro3cr
+ro4e
+ro1fe
+ro5fil
+rok2
+ro5ker
+5role.
+rom5ete
+rom4i
+rom4p
+ron4al
+ron4e
+ro5n4is
+ron4ta
+1room
+5root
+ro3pel
+rop3ic
+ror3i
+ro5ro
+ros5per
+ros4s
+ro4the
+ro4ty
+ro4va
+rov5el
+rox5
+r1p
+r4pea
+r5pent
+rp5er.
+r3pet
+rp4h4
+rp3ing
+r3po
+r1r4
+rre4c
+rre4f
+r4reo
+rre4st
+rri4o
+rri4v
+rron4
+rros4
+rrys4
+4rs2
+r1sa
+rsa5ti
+rs4c
+r2se
+r3sec
+rse4cr
+rs5er.
+rs3es
+rse5v2
+r1sh
+r5sha
+r1si
+r4si4b
+rson3
+r1sp
+r5sw
+rtach4
+r4tag
+r3teb
+rten4d
+rte5o
+r1ti
+rt5ib
+rti4d
+r4tier
+r3tig
+rtil3i
+rtil4l
+r4tily
+r4tist
+r4tiv
+r3tri
+rtroph4
+rt4sh
+ru3a
+ru3e4l
+ru3en
+ru4gl
+ru3in
+rum3pl
+ru2n
+runk5
+run4ty
+r5usc
+ruti5n
+rv4e
+rvel4i
+r3ven
+rv5er.
+r5vest
+r3vey
+r3vic
+rvi4v
+r3vo
+r1w
+ry4c
+5rynge
+ry3t
+sa2
+2s1ab
+5sack
+sac3ri
+s3act
+5sai
+salar4
+sal4m
+sa5lo
+sal4t
+3sanc
+san4de
+s1ap
+sa5ta
+5sa3tio
+sat3u
+sau4
+sa5vor
+5saw
+4s5b
+scan4t5
+sca4p
+scav5
+s4ced
+4scei
+s4ces
+sch2
+s4cho
+3s4cie
+5scin4d
+scle5
+s4cli
+scof4
+4scopy
+scour5a
+s1cu
+4s5d
+4se.
+se4a
+seas4
+sea5w
+se2c3o
+3sect
+4s4ed
+se4d4e
+s5edl
+se2g
+seg3r
+5sei
+se1le
+5self
+5selv
+4seme
+se4mol
+sen5at
+4senc
+sen4d
+s5ened
+sen5g
+s5enin
+4sentd
+4sentl
+sep3a3
+4s1er.
+s4erl
+ser4o
+4servo
+s1e4s
+se5sh
+ses5t
+5se5um
+5sev
+sev3en
+sew4i
+5sex
+4s3f
+2s3g
+s2h
+2sh.
+sh1er
+5shev
+sh1in
+sh3io
+3ship
+shiv5
+sho4
+sh5old
+shon3
+shor4
+short5
+4shw
+si1b
+s5icc
+3side.
+5sides
+5sidi
+si5diz
+4signa
+sil4e
+4sily
+2s1in
+s2ina
+5sine.
+s3ing
+1sio
+5sion
+sion5a
+si2r
+sir5a
+1sis
+3sitio
+5siu
+1siv
+5siz
+sk2
+4ske
+s3ket
+sk5ine
+sk5ing
+s1l2
+s3lat
+s2le
+slith5
+2s1m
+s3ma
+small3
+sman3
+smel4
+s5men
+5smith
+smol5d4
+s1n4
+1so
+so4ce
+soft3
+so4lab
+sol3d2
+so3lic
+5solv
+3som
+3s4on.
+sona4
+son4g
+s4op
+5sophic
+s5ophiz
+s5ophy
+sor5c
+sor5d
+4sov
+so5vi
+2spa
+5spai
+spa4n
+spen4d
+2s5peo
+2sper
+s2phe
+3spher
+spho5
+spil4
+sp5ing
+4spio
+s4ply
+s4pon
+spor4
+4spot
+squal4l
+s1r
+2ss
+s1sa
+ssas3
+s2s5c
+s3sel
+s5seng
+s4ses.
+s5set
+s1si
+s4sie
+ssi4er
+ss5ily
+s4sl
+ss4li
+s4sn
+sspend4
+ss2t
+ssur5a
+ss5w
+2st.
+s2tag
+s2tal
+stam4i
+5stand
+s4ta4p
+5stat.
+s4ted
+stern5i
+s5tero
+ste2w
+stew5a
+s3the
+st2i
+s4ti.
+s5tia
+s1tic
+5stick
+s4tie
+s3tif
+st3ing
+5stir
+s1tle
+5stock
+stom3a
+5stone
+s4top
+3store
+st4r
+s4trad
+5stratu
+s4tray
+s4trid
+4stry
+4st3w
+s2ty
+1su
+su1al
+su4b3
+su2g3
+su5is
+suit3
+s4ul
+su2m
+sum3i
+su2n
+su2r
+4sv
+sw2
+4swo
+s4y
+4syc
+3syl
+syn5o
+sy5rin
+1ta
+3ta.
+2tab
+ta5bles
+5taboliz
+4taci
+ta5do
+4taf4
+tai5lo
+ta2l
+ta5la
+tal5en
+tal3i
+4talk
+tal4lis
+ta5log
+ta5mo
+tan4de
+tanta3
+ta5per
+ta5pl
+tar4a
+4tarc
+4tare
+ta3riz
+tas4e
+ta5sy
+4tatic
+ta4tur
+taun4
+tav4
+2taw
+tax4is
+2t1b
+4tc
+t4ch
+tch5et
+4t1d
+4te.
+tead4i
+4teat
+tece4
+5tect
+2t1ed
+te5di
+1tee
+teg4
+te5ger
+te5gi
+3tel.
+teli4
+5tels
+te2ma2
+tem3at
+3tenan
+3tenc
+3tend
+4tenes
+1tent
+ten4tag
+1teo
+te4p
+te5pe
+ter3c
+5ter3d
+1teri
+ter5ies
+ter3is
+teri5za
+5ternit
+ter5v
+4tes.
+4tess
+t3ess.
+teth5e
+3teu
+3tex
+4tey
+2t1f
+4t1g
+2th.
+than4
+th2e
+4thea
+th3eas
+the5at
+the3is
+3thet
+th5ic.
+th5ica
+4thil
+5think
+4thl
+th5ode
+5thodic
+4thoo
+thor5it
+tho5riz
+2ths
+1tia
+ti4ab
+ti4ato
+2ti2b
+4tick
+t4ico
+t4ic1u
+5tidi
+3tien
+tif2
+ti5fy
+2tig
+5tigu
+till5in
+1tim
+4timp
+tim5ul
+2t1in
+t2ina
+3tine.
+3tini
+1tio
+ti5oc
+tion5ee
+5tiq
+ti3sa
+3tise
+tis4m
+ti5so
+tis4p
+5tistica
+ti3tl
+ti4u
+1tiv
+tiv4a
+1tiz
+ti3za
+ti3zen
+2tl
+t5la
+tlan4
+3tle.
+3tled
+3tles.
+t5let.
+t5lo
+4t1m
+tme4
+2t1n2
+1to
+to3b
+to5crat
+4todo
+2tof
+to2gr
+to5ic
+to2ma
+tom4b
+to3my
+ton4ali
+to3nat
+4tono
+4tony
+to2ra
+to3rie
+tor5iz
+tos2
+5tour
+4tout
+to3war
+4t1p
+1tra
+tra3b
+tra5ch
+traci4
+trac4it
+trac4te
+tras4
+tra5ven
+trav5es5
+tre5f
+tre4m
+trem5i
+5tria
+tri5ces
+5tricia
+4trics
+2trim
+tri4v
+tro5mi
+tron5i
+4trony
+tro5phe
+tro3sp
+tro3v
+tru5i
+trus4
+4t1s2
+t4sc
+tsh4
+t4sw
+4t3t2
+t4tes
+t5to
+ttu4
+1tu
+tu1a
+tu3ar
+tu4bi
+tud2
+4tue
+4tuf4
+5tu3i
+3tum
+tu4nis
+2t3up.
+3ture
+5turi
+tur3is
+tur5o
+tu5ry
+3tus
+4tv
+tw4
+4t1wa
+twis4
+4two
+1ty
+4tya
+2tyl
+type3
+ty5ph
+4tz
+tz4e
+4uab
+uac4
+ua5na
+uan4i
+uar5ant
+uar2d
+uar3i
+uar3t
+u1at
+uav4
+ub4e
+u4bel
+u3ber
+u4bero
+u1b4i
+u4b5ing
+u3ble.
+u3ca
+uci4b
+uc4it
+ucle3
+u3cr
+u3cu
+u4cy
+ud5d
+ud3er
+ud5est
+udev4
+u1dic
+ud3ied
+ud3ies
+ud5is
+u5dit
+u4don
+ud4si
+u4du
+u4ene
+uens4
+uen4te
+uer4il
+3ufa
+u3fl
+ugh3en
+ug5in
+2ui2
+uil5iz
+ui4n
+u1ing
+uir4m
+uita4
+uiv3
+uiv4er.
+u5j
+4uk
+u1la
+ula5b
+u5lati
+ulch4
+5ulche
+ul3der
+ul4e
+u1len
+ul4gi
+ul2i
+u5lia
+ul3ing
+ul5ish
+ul4lar
+ul4li4b
+ul4lis
+4ul3m
+u1l4o
+4uls
+uls5es
+ul1ti
+ultra3
+4ultu
+u3lu
+ul5ul
+ul5v
+um5ab
+um4bi
+um4bly
+u1mi
+u4m3ing
+umor5o
+um2p
+unat4
+u2ne
+un4er
+u1ni
+un4im
+u2nin
+un5ish
+uni3v
+un3s4
+un4sw
+unt3ab
+un4ter.
+un4tes
+unu4
+un5y
+un5z
+u4ors
+u5os
+u1ou
+u1pe
+uper5s
+u5pia
+up3ing
+u3pl
+up3p
+upport5
+upt5ib
+uptu4
+u1ra
+4ura.
+u4rag
+u4ras
+ur4be
+urc4
+ur1d
+ure5at
+ur4fer
+ur4fr
+u3rif
+uri4fic
+ur1in
+u3rio
+u1rit
+ur3iz
+ur2l
+url5ing.
+ur4no
+uros4
+ur4pe
+ur4pi
+urs5er
+ur5tes
+ur3the
+urti4
+ur4tie
+u3ru
+2us
+u5sad
+u5san
+us4ap
+usc2
+us3ci
+use5a
+u5sia
+u3sic
+us4lin
+us1p
+us5sl
+us5tere
+us1tr
+u2su
+usur4
+uta4b
+u3tat
+4ute.
+4utel
+4uten
+uten4i
+4u1t2i
+uti5liz
+u3tine
+ut3ing
+ution5a
+u4tis
+5u5tiz
+u4t1l
+ut5of
+uto5g
+uto5matic
+u5ton
+u4tou
+uts4
+u3u
+uu4m
+u1v2
+uxu3
+uz4e
+1va
+5va.
+2v1a4b
+vac5il
+vac3u
+vag4
+va4ge
+va5lie
+val5o
+val1u
+va5mo
+va5niz
+va5pi
+var5ied
+3vat
+4ve.
+4ved
+veg3
+v3el.
+vel3li
+ve4lo
+v4ely
+ven3om
+v5enue
+v4erd
+5vere.
+v4erel
+v3eren
+ver5enc
+v4eres
+ver3ie
+vermi4n
+3verse
+ver3th
+v4e2s
+4ves.
+ves4te
+ve4te
+vet3er
+ve4ty
+vi5ali
+5vian
+5vide.
+5vided
+4v3iden
+5vides
+5vidi
+v3if
+vi5gn
+vik4
+2vil
+5vilit
+v3i3liz
+v1in
+4vi4na
+v2inc
+vin5d
+4ving
+vio3l
+v3io4r
+vi1ou
+vi4p
+vi5ro
+vis3it
+vi3so
+vi3su
+4viti
+vit3r
+4vity
+3viv
+5vo.
+voi4
+3vok
+vo4la
+v5ole
+5volt
+3volv
+vom5i
+vor5ab
+vori4
+vo4ry
+vo4ta
+4votee
+4vv4
+v4y
+w5abl
+2wac
+wa5ger
+wag5o
+wait5
+w5al.
+wam4
+war4t
+was4t
+wa1te
+wa5ver
+w1b
+wea5rie
+weath3
+wed4n
+weet3
+wee5v
+wel4l
+w1er
+west3
+w3ev
+whi4
+wi2
+wil2
+will5in
+win4de
+win4g
+wir4
+3wise
+with3
+wiz5
+w4k
+wl4es
+wl3in
+w4no
+1wo2
+wom1
+wo5ven
+w5p
+wra4
+wri4
+writa4
+w3sh
+ws4l
+ws4pe
+w5s4t
+4wt
+wy4
+x1a
+xac5e
+x4ago
+xam3
+x4ap
+xas5
+x3c2
+x1e
+xe4cuto
+x2ed
+xer4i
+xe5ro
+x1h
+xhi2
+xhil5
+xhu4
+x3i
+xi5a
+xi5c
+xi5di
+x4ime
+xi5miz
+x3o
+x4ob
+x3p
+xpan4d
+xpecto5
+xpe3d
+x1t2
+x3ti
+x1u
+xu3a
+xx4
+y5ac
+3yar4
+y5at
+y1b
+y1c
+y2ce
+yc5er
+y3ch
+ych4e
+ycom4
+ycot4
+y1d
+y5ee
+y1er
+y4erf
+yes4
+ye4t
+y5gi
+4y3h
+y1i
+y3la
+ylla5bl
+y3lo
+y5lu
+ymbol5
+yme4
+ympa3
+yn3chr
+yn5d
+yn5g
+yn5ic
+5ynx
+y1o4
+yo5d
+y4o5g
+yom4
+yo5net
+y4ons
+y4os
+y4ped
+yper5
+yp3i
+y3po
+y4poc
+yp2ta
+y5pu
+yra5m
+yr5ia
+y3ro
+yr4r
+ys4c
+y3s2e
+ys3ica
+ys3io
+3ysis
+y4so
+yss4
+ys1t
+ys3ta
+ysur4
+y3thin
+yt3ic
+y1w
+za1
+z5a2b
+zar2
+4zb
+2ze
+ze4n
+ze4p
+z1er
+ze3ro
+zet4
+2z1i
+z4il
+z4is
+5zl
+4zm
+1zo
+zo4m
+zo5ol
+zte4
+4z1z2
+z4zy
+}
+\hyphenation{ % Do NOT make any alterations to this list! --- DEK
+as-so-ciate
+as-so-ciates
+dec-li-na-tion
+oblig-a-tory
+phil-an-thropic
+present
+presents
+project
+projects
+reci-procity
+re-cog-ni-zance
+ref-or-ma-tion
+ret-ri-bu-tion
+ta-ble
+}
diff --git a/systems/knuth/dist/lib/io.mf b/systems/knuth/dist/lib/io.mf
new file mode 100644
index 0000000000..58a8be68db
--- /dev/null
+++ b/systems/knuth/dist/lib/io.mf
@@ -0,0 +1,23 @@
+mode_setup;
+ em#:=10pt#; cap#:=7pt#;
+ thin#:=1/3pt#; thick#:=5/6pt#;
+ o#:=1/5pt#;
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick);
+define_corrected_pixels(o);
+ curve_sidebar=round 1/18em;
+beginchar("O",0.8em#,cap#,0); "The letter O";
+ penpos1(thick,10); penpos2(.1[thin,thick],90-10);
+ penpos3(thick,180+10); penpos4(thin,270-10);
+ x1l=w-x3l=curve_sidebar; x2=x4=.5w;
+ y1=.49h; y2l=-o; y3=.51h; y4l=h+o;
+ penstroke z1e{down}..z2e{right}
+ ..z3e{up}..z4e{left}..cycle;
+ penlabels(1,2,3,4); endchar;
+def test_I(expr code,trial_stem,trial_width) =
+ stem#:=trial_stem*pt#; define_blacker_pixels(stem);
+ beginchar(code,trial_width*em#,cap#,0); "The letter I";
+ penpos1(stem,15); penpos2(.9stem,12); penpos3(stem,10);
+ x1=x2=x3=.5w; y1=h; y2=.55h; y3=0; x2l:=1/6[x2l,x2];
+ penstroke z1e..z2e{down}..z3e;
+ penlabels(1,2,3); endchar; enddef;
diff --git a/systems/knuth/dist/lib/logo.mf b/systems/knuth/dist/lib/logo.mf
new file mode 100644
index 0000000000..2a308ab917
--- /dev/null
+++ b/systems/knuth/dist/lib/logo.mf
@@ -0,0 +1,127 @@
+% Routines for the METAFONT logo, as found in The METAFONTbook
+% Extended to characters P and S for John Hobby's METAPOST
+% (logo10.mf is a typical parameter file)
+
+mode_setup;
+if unknown slant: slant:=0 else: currenttransform:=
+ identity slanted slant yscaled aspect_ratio fi;
+
+ygap#:=(ht#/13.5u#)*xgap#; % vertical adjustment
+ho#:=o#; % horizontal overshoot
+leftstemloc#:=2.5u#+s#; % position of left stem
+barheight#:=.45ht#; % height of bar lines
+py#:=.9px#; % vertical pen thickness
+
+define_pixels(s,u);
+define_whole_pixels(xgap);
+define_whole_vertical_pixels(ygap);
+define_blacker_pixels(px,py);
+pickup pencircle xscaled px yscaled py;
+logo_pen:=savepen;
+define_good_x_pixels(leftstemloc);
+define_good_y_pixels(barheight);
+define_corrected_pixels(o);
+define_horizontal_corrected_pixels(ho);
+
+def beginlogochar(expr code, unit_width) =
+ beginchar(code,unit_width*u#+2s#,ht#,0);
+ pickup logo_pen enddef;
+
+def super_half(suffix i,j,k) =
+ draw z.i{0,y.j-y.i}
+ ... (.8[x.j,x.i],.8[y.i,y.j]){z.j-z.i}
+ ... z.j{x.k-x.i,0}
+ ... (.8[x.j,x.k],.8[y.k,y.j]){z.k-z.j}
+ ... z.k{0,y.k-y.j} enddef;
+
+def super_crescent(suffix i,j,k) =
+ draw z.i{x.j-x.i,0}
+ ... (.8[x.i,x.j],.8[y.j,y.i]){z.j-z.i}
+ ... z.j{0,y.k-y.i}
+ ... (.8[x.k,x.j],.8[y.j,y.k]){z.k-z.j}
+ ... z.k{x.k-x.j,0} enddef;
+
+beginlogochar("M",18);
+x1=x2=leftstemloc; x4=x5=w-x1; x3=w-x3;
+y1=y5; y2=y4; bot y1=-o;
+top y2=h+o; y3=y1+ygap;
+draw z1--z2--z3--z4--z5;
+labels(1,2,3,4,5); endchar;
+
+beginlogochar("E",14);
+x1=x2=x3=leftstemloc;
+x4=x6=w-x1+ho; x5=x4-xgap;
+y1=y6; y2=y5; y3=y4;
+bot y1=0; top y3=h; y2=barheight;
+draw z6--z1--z3--z4; draw z2--z5;
+labels(1,2,3,4,5,6); endchar;
+
+beginlogochar("T",13);
+italcorr ht#*slant + .5u#;
+if .5w<>good.x .5w: change_width; fi
+lft x1=-eps; x2=w-x1; x3=x4=.5w;
+y1=y2=y3; top y1=h; bot y4=-o;
+draw z1--z2; draw z3--z4;
+labels(1,2,3,4); endchar;
+
+beginlogochar("A",15);
+x1=.5w; x2=x4=leftstemloc; x3=x5=w-x2;
+top y1=h+o; y2=y3=barheight;
+bot y4=bot y5=-o;
+draw z4--z2--z3--z5; super_half(2,1,3);
+labels(1,2,3,4,5); endchar;
+
+beginlogochar("F",14);
+x1=x2=x3=leftstemloc;
+x4=w-x1+ho; x5=x4-xgap;
+y2=y5; y3=y4; bot y1=-o;
+top y3=h; y2=barheight;
+draw z1--z3--z4; draw z2--z5;
+labels(1,2,3,4,5); endchar;
+
+beginlogochar("P",14);
+x1=x2=x3=leftstemloc;
+x4=x5=.618[x1,w-x1];
+y2=y5; y3=y4; bot y1=-o;
+top y3=h; y2=barheight;
+draw z1--z3--z4; draw z2--z5;
+x6=.5[w-x1,lft w]; y6=.5[y4,y5];
+super_crescent(4,6,5);
+labels(1,2,3,4,5,6); endchar;
+
+beginlogochar("S",14);
+x3=x8=leftstemloc;
+x1=w-x3;
+x2=x4=.382[x3,x1];
+x5=x7=.9[x3,x1];
+x6=.5[x1,w];
+top y1=h; y4=barheight; bot y8=0;
+y1=y2; y3=.6[y2,y4]; y4=y5; y6=.3[y5,y7]; y7=y8;
+draw z1--z2; draw z4--z5; draw z7--z8;
+super_crescent(2,3,4);
+super_crescent(5,6,7);
+labels(1,2,3,4,5,6,7,8); endchar;
+
+beginlogochar("O",15);
+x1=x4=.5w; top y1=h+o; bot y4=-o;
+x2=w-x3=good.x(1.5u+s); y2=y3=barheight;
+super_half(2,1,3); super_half(2,4,3);
+labels(1,2,3,4); endchar;
+
+beginlogochar("N",15);
+x1=x2=leftstemloc; x3=x4=x5=w-x1;
+bot y1=bot y4=-o;
+top y2=top y5=h+o; y3=y4+ygap;
+draw z1--z2--z3; draw z4--z5;
+labels(1,2,3,4,5); endchar;
+
+ligtable "T": "A" kern -.5u#;
+ligtable "F": "O" kern -u#;
+ligtable "P": "O" kern u#;
+
+font_quad:=18u#+2s#;
+font_normal_space:=6u#+2s#;
+font_normal_stretch:=3u#;
+font_normal_shrink:=2u#;
+font_identifier:="MFLOGO" if slant<>0: & "SL" fi;
+font_coding_scheme:="AEFMNOPST only";
diff --git a/systems/knuth/dist/lib/logo10.mf b/systems/knuth/dist/lib/logo10.mf
new file mode 100644
index 0000000000..2b89c710d1
--- /dev/null
+++ b/systems/knuth/dist/lib/logo10.mf
@@ -0,0 +1,10 @@
+% 10-point METAFONT logo
+font_size 10pt#; % the "design size" of this font
+ht#:=6pt#; % height of characters
+xgap#:=0.6pt#; % horizontal adjustment
+u#:=4/9pt#; % unit width
+s#:=0; % extra space at the left and the right
+o#:=1/9pt#; % overshoot
+px#:=2/3pt#; % horizontal thickness of pen
+input logo % now generate the font
+bye % and stop.
diff --git a/systems/knuth/dist/lib/logo8.mf b/systems/knuth/dist/lib/logo8.mf
new file mode 100644
index 0000000000..59b37c072b
--- /dev/null
+++ b/systems/knuth/dist/lib/logo8.mf
@@ -0,0 +1,10 @@
+% 8-point METAFONT logo
+font_size 8pt#; % the "design size" of this font
+ht#:=.8*6pt#; % height of characters
+xgap#:=.8*0.6pt#; % horizontal adjustment
+u#:=.82*4/9pt#; % unit width
+s#:=.2pt#; % extra space at the left and the right
+o#:=1/12pt#; % overshoot
+px#:=.8*2/3pt#; % horizontal thickness of pen
+input logo % now generate the font
+bye % and stop.
diff --git a/systems/knuth/dist/lib/logo9.mf b/systems/knuth/dist/lib/logo9.mf
new file mode 100644
index 0000000000..49ea7e99f7
--- /dev/null
+++ b/systems/knuth/dist/lib/logo9.mf
@@ -0,0 +1,10 @@
+% 9-point METAFONT logo
+font_size 9pt#; % the "design size" of this font
+ht#:=.9*6pt#; % height of characters
+xgap#:=.9*0.6pt#; % horizontal adjustment
+u#:=.91*4/9pt#; % unit width
+s#:=.08pt#; % extra space at the left and the right
+o#:=1/10pt#; % overshoot
+px#:=.9*2/3pt#; % horizontal thickness of pen
+input logo % now generate the font
+bye % and stop.
diff --git a/systems/knuth/dist/lib/logobf10.mf b/systems/knuth/dist/lib/logobf10.mf
new file mode 100644
index 0000000000..f943053ff5
--- /dev/null
+++ b/systems/knuth/dist/lib/logobf10.mf
@@ -0,0 +1,10 @@
+% 10-point boldface METAFONT logo
+font_size 10pt#; % the "design size" of this font
+ht#:=6pt#; % height of characters
+xgap#:=23/20*0.6pt#; % horizontal adjustment
+u#:=23/20*4/9pt#; % unit width
+s#:=.1pt#; % extra space at the left and the right
+o#:=1/9pt#; % overshoot
+px#:=44/36pt#; % horizontal thickness of pen
+input logo % now generate the font
+bye % and stop.
diff --git a/systems/knuth/dist/lib/logosl10.mf b/systems/knuth/dist/lib/logosl10.mf
new file mode 100644
index 0000000000..741fdb9fa2
--- /dev/null
+++ b/systems/knuth/dist/lib/logosl10.mf
@@ -0,0 +1,3 @@
+% 10-point slanted METAFONT logo
+slant := 1/4;
+input logo10
diff --git a/systems/knuth/dist/lib/manfnt.mf b/systems/knuth/dist/lib/manfnt.mf
new file mode 100644
index 0000000000..0c85e0864b
--- /dev/null
+++ b/systems/knuth/dist/lib/manfnt.mf
@@ -0,0 +1,788 @@
+% Special font for the TeX and METAFONT manuals
+% This file was prepared "hastily" and hasn't been "polished".
+% In particular, some characters won't work when aspect_ratio<>1.
+
+font_size 10pt#;
+em#:=10pt#; ht#:=250/36pt#;
+mode_setup;
+
+font_identifier "MANFNT";
+font_coding_scheme "PI";
+
+message "Hex symbols";
+width1#=.4pt#*(ht#/(100/36pt#));
+width2#=.6pt#*(ht#/(100/36pt#));
+define_blacker_pixels(width1,width2);
+
+beginchar(1,2ht#,ht#,0); "Original hex"; % \hexa
+pickup pencircle scaled width1;
+w:=2good.x .5w;
+x1=x4=good.x 0; x2=x5=.5w; x3=x6=good.x w;
+y1=y2=y3=good.y h; y4=y5=y6=good.y 0;
+draw z1..z6; draw z2..z5; draw z3..z4;
+for k=1 upto 6: drawdot z[k]; endfor
+labels (1,2,3,4,5,6); endchar;
+
+beginchar(2,2ht#,ht#,0); "Top/bot adjusted hex"; % \hexb
+pickup pencircle scaled width1;
+w:=2good.x .5w;
+x1=x4=good.x 0; x2=x5=.5w; x3=x6=good.x w;
+top y1=h; y1=y2=y3; bot y4=0; y4=y5=y6;
+draw z1..z6; draw z2..z5; draw z3..z4;
+for k=1 upto 6: drawdot z[k]; endfor
+labels (1,2,3,4,5,6); endchar;
+
+beginchar(3,2ht#,ht#,0); "Bold top/bot adjusted hex"; % \hexc
+pickup pencircle scaled width2;
+w:=2good.x .5w;
+x1=x4=good.x 0; x2=x5=.5w; x3=x6=good.x w;
+top y1=h; y1=y2=y3; bot y4=0; y4=y5=y6;
+draw z1..z6; draw z2..z5; draw z3..z4;
+for k=1 upto 6: drawdot z[k]; endfor
+labels (1,2,3,4,5,6); endchar;
+
+beginchar(4,2ht#,ht#,0); "Bold top/bot/lft/rt adjusted hex"; % \hexd
+pickup pencircle scaled width2;
+w:=2good.x .5w;
+lft x1=0; x1=x4; x2=x5=.5w; rt x3=w-1; x3=x6;
+top y1=h; y1=y2=y3; bot y4=0; y4=y5=y6;
+draw z1..z6; draw z2..z5; draw z3..z4;
+for k=1 upto 6: drawdot z[k]; endfor
+labels (1,2,3,4,5,6); endchar;
+
+beginchar(5,2ht#,ht#,0); "Hex corrected for darkness in the center"; % \hexe
+pickup pencircle scaled width1;
+w:=2good.x .5w;
+top z1=(0,h); top z2=(.5w,h); top z3=(w,h);
+bot z4=(0,0); bot z5=(.5w,0); bot z6=(w,0);
+for k=1 upto 6: drawdot z[k]; endfor
+z1'=.25[z1,z6]; z6'=.75[z1,z6]; theta1:=angle(z6-z1)+90;
+z3'=.25[z3,z4]; z4'=.75[z3,z4]; theta3:=angle(z4-z3)+90;
+z7=z8=.5[z1,z6];
+penpos1'(width1,theta1); penpos6'(width1,theta1); penpos7(.6width1,theta1);
+penpos3'(width1,theta3); penpos4'(width1,theta3); penpos8(.6width1,theta3);
+draw z1..z1'; penstroke z1'e{z6'-z1'}..z7e..{z6'-z1'}z6'e; draw z6'..z6;
+draw z2..z5;
+draw z3..z3'; penstroke z3'e{z4'-z3'}..z8e..{z4'-z3'}z4'e; draw z4'..z4;
+penlabels(1,1',7,2,3,3',8,4,4',5,6,6'); endchar;
+
+message "Stick A's";
+b#:=250/36pt#; a#:=150/36pt#; s#:=30/36pt#;
+standardwidth#:=.4pt#;
+darkwidth#:=.8pt#; lightwidth#:=.2pt#;
+define_blacker_pixels(standardwidth,darkwidth,lightwidth);
+define_pixels(b,a,s);
+def stick_A(expr alpha)(text pentype)=
+beginchar (incr(charcode),s#+a#+s#,b#,0);
+pickup pentype;
+bot z1=(good.x s,0); z5=z1+(a,0);
+z3=(1/2[x1,x5],good.y b);
+z2=alpha[z1,z3]; z4=alpha[z5,z3];
+draw z1..z3; draw z3..z5; draw z2..z4;
+drawdot z1; drawdot z5; drawdot z3;
+labels(1,2,3,4,5); endchar; enddef;
+charcode:=5; % the first A will be number 6
+
+stick_A((3-sqrt5)/2, % (area above bar / area below) = golden ratio % \Aa
+ pencircle scaled standardwidth);
+
+for alpha=.2 step .05 until .5+10epsilon:
+ stick_A(alpha,pencircle scaled standardwidth); endfor % chars 7..13
+
+beginchar(14,s#+a#+s#,b#,0);
+pickup pencircle scaled standardwidth;
+bot z1=(good.x s,0); z5=z1+(a,0);
+z3=(1/2[x1,x5],good.y b);
+z4-z2=whatever*dir20; 1/2[y2,y4]=2/3[y3,y1];
+z2=whatever[z1,z3]; z4=whatever[z3,z5];
+draw z1..z3; draw z3..z5; draw z2..z4;
+drawdot z1; drawdot z5; drawdot z3;
+labels(1,2,3,4,5); endchar;
+
+stick_A((3-sqrt5)/2,pencircle xscaled darkwidth yscaled lightwidth); % \Ab=15
+stick_A((3-sqrt5)/2,
+ pencircle xscaled darkwidth yscaled lightwidth rotated 30); % \Ac=16
+
+message "Kidney beans";
+standardwidth#:=.4pt#;
+darkwidth#:=.8pt#; lightwidth#:=.2pt#;
+define_blacker_pixels(standardwidth,darkwidth,lightwidth);
+b#:=100/36pt#;
+define_pixels(b);
+def bean(text pentype)=
+beginchar (incr(charcode),2b#,1.5b#,0);
+pickup pentype;
+z1=(0,b); z2=(b,b); z3=(2b,b);
+z4=(0,0); z5=(b,0); z6=(2b,0);
+draw z5..z4..z1..z3..z6..cycle;
+labels(1,2,3,4,5,6); endchar; enddef;
+charcode:=16; % the first bean will be number 17
+
+bean(pencircle scaled standardwidth); % \beana=17
+bean(pencircle scaled darkwidth); % \beanb=18
+bean(pencircle xscaled darkwidth yscaled lightwidth); % \beanc=19
+bean(pencircle xscaled darkwidth yscaled lightwidth rotated 30); % \beand=20
+
+message "Pen nibs";
+bigthickwidth#:=8pt#; littlethickwidth#:=2pt#;
+define_blacker_pixels(bigthickwidth,littlethickwidth);
+
+beginchar(21,bigthickwidth#,0,0); % \niba
+pickup pencircle xscaled bigthickwidth yscaled littlethickwidth;
+lft z1=(0,good.y 0);
+drawdot z1;
+labels(1); endchar;
+
+beginchar(22,bigthickwidth#,0,0); % \nibb
+pickup pencircle xscaled bigthickwidth yscaled littlethickwidth rotated 30;
+lft z1=(0,good.y 0);
+drawdot z1;
+labels(1); endchar;
+
+beginchar(23,littlethickwidth#,0,0); % \nibc
+pickup pencircle yscaled bigthickwidth xscaled littlethickwidth;
+lft z1=(0,good.y 0);
+drawdot z1;
+labels(1); endchar;
+
+message "Ionian letters";
+em#:=10pt#; cap#:=7pt#;
+thin#:=1/3pt#; thick#:=5/6pt#;
+slab#:=.8pt#;
+stem#:=.9pt#;
+o#:=1/5pt#;
+
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick,slab,stem);
+define_corrected_pixels(o);
+
+curve_sidebar=round 1/18em;
+
+%vardef pentaper@#(expr lt,rt) = % dropped from PLAIN
+% x@#l:=lt[x@#l,x@#]; x@#r:=rt[x@#r,x@#];
+% y@#l:=lt[y@#l,y@#]; y@#r:=rt[y@#r,y@#] enddef;
+
+
+% ill-fated example wasn't attractive enough by itself:
+%bar:=slab;
+%beginchar(24,0.6em#,cap#,0); % "The letter T"; \IOT=24
+%penpos1(bar,70);
+%penpos2(bar,35);
+%penpos4(bar,35);
+%penpos5(bar,55);
+%x1=0; y1r=h; y2r=y4r=.2[y1r,y1]; y5l=.3[y1l,y1];
+%x2l=1/3[x1l,x5l]; x4l=2/3[x1l,x5l]; x5=w;
+%penpos6(stem,20); pentaper6(.1,0);
+%penpos3(x6r-x6l,0);
+%penpos7(stem,10);
+%y3=y2; x3=.5w;
+%x6r=x3r; y6=2/3h;
+%x7=x6; y7=0;
+%penstroke z1e{curl infinity}..z2e..z4e..{curl infinity}z5e;
+%penstroke z3e..z6e{down}..z7e;
+%labels.top(1r,2l,2,2r,4l,4,4r,6);
+%labels.lft(1,1l,6l);
+%labels.rt(5,5r,5l,6r);
+%labels.bot(3l,3,3r,7,7r,7l);
+%endchar;
+
+beginchar(25,5/9em#,cap#,0); % "The letter S"; /IOS=25
+penpos1(slab,70);
+penpos2(.5slab,80);
+penpos3(.5[slab,thick],200);
+penpos5(.5[slab,thick],210);
+penpos6(.7slab,80);
+penpos7(.25[slab,thick],72);
+x1=x5; y1r=.94h+o;
+x2=x4=x6=.5w; y2r=h+o; y4=.54h; y6l=-o;
+x3r=.04em; y3=.5[y4,y2];
+x5l=w-.03em; y5=.5[y4,y6];
+.5[x7l,x7]=.04em; y7l=.12h-o;
+path trial; trial=z3{down}..z4..{down}z5;
+pair dz; dz=direction 1 of trial;
+penpos4(thick,angle dz-90);
+penstroke z1e..z2e{left}..z3e{down}..z4e{dz}..z5e{down}..z6e{left}..z7e;
+labels.lft(2,3r,5r,6,7);
+labels.rt(1,3l,4,5l);
+labels.top(1r,2r,3,4l,5,6r,7r);
+labels.bot(1l,2l,4r,6l,7l);
+endchar;
+
+beginchar(26,0.8em#,cap#,0); % "The letter O"; \IOO=26
+ penpos1(thick,10); penpos2(.1[thin,thick],90-10);
+ penpos3(thick,180+10); penpos4(thin,270-10);
+ x1l=w-x3l=curve_sidebar; x2=x4=.5w;
+ y1=.49h; y2l=-o; y3=.51h; y4l=h+o;
+ penstroke z1e{down}..z2e{right}..z3e{up}..z4e{left}..cycle;
+ penlabels(1,2,3,4); endchar;
+
+beginchar(27,.35em#,cap#,0); % "The letter I"; \IOI=27
+ penpos1(stem,15); penpos2(.9stem,12); penpos3(stem,10);
+ x1=x2=x3=.5w; y1=h; y2=.55h; y3=0; x2l:=1/6[x2l,x2];
+ penstroke z1e..z2e{down}..z3e;
+ penlabels(1,2,3); endchar;
+
+%ligtable 24: 24 kern .05em#, 26 kern -.05em#; % TT and TO are adjusted
+
+message "Cubes and such";
+s#:=5pt#; define_pixels(s); % side of the square
+beginchar(28,5/3s#,4/3s#,0); % "Possible cube"; \cubea=28
+z1=(-epsilon,-epsilon); z2=(s+epsilon,-epsilon);
+ z3=(-epsilon,s+epsilon); z4=(s+epsilon,s+epsilon);
+for k=1 upto 4: z[k+4]=z[k]+(2/3s,1/3s); endfor
+pickup pencircle scaled .4pt;
+draw z5--z6--z8--z7--cycle;
+pickup pencircle scaled 1.6pt;
+erase draw z2--z4--z3;
+pickup pencircle scaled .4pt;
+draw z1--z2--z4--z3--cycle;
+for k=1 upto 4: draw z[k]--z[k+4]; endfor
+labels.top(1,2,5,6);
+labels.bot(3,4,7,8);
+endchar;
+
+beginchar(29,5/3s#,4/3s#,0); % "Impossible cube"; \cubeb=29
+z1=(-epsilon,-epsilon); z2=(s+epsilon,-epsilon);
+ z3=(-epsilon,s+epsilon); z4=(s+epsilon,s+epsilon);
+for k=1 upto 4: z[k+4]=z[k]+(2/3s,1/3s); endfor
+pickup pencircle scaled .4pt;
+draw z3--z4; draw z5--z6;
+cullit; pickup pencircle scaled 1.6pt;
+undraw z7--1/2[z7,z5];
+ undraw z2--1/2[z2,z4];
+cullit; pickup pencircle scaled .4pt;
+draw z3--z1--z2--z4;
+ draw z5--z7--z8--z6;
+for k=1 upto 4: draw z[k]--z[k+4]; endfor
+labels.top(1,2,5,6);
+labels.bot(3,4,7,8);
+endchar;
+
+beginchar(30,10pt#,7pt#,2pt#); % bicentennial star
+pair center; center=(.5w,2pt);
+numeric radius; radius=5pt;
+for k=0 upto 4: z[k]=center+(radius,0) rotated(90+360/5k); endfor
+def :: = ..tension 5.. enddef;
+path star; star=z0::z2::z4::z1::z3::cycle;
+pickup pencircle scaled .4pt; draw star;
+cullit; pickup pencircle scaled 1.6pt;
+for k=0 upto 4: undraw subpath(k+.55,k+.7) of star; endfor
+cullit; pickup pencircle scaled .4pt;
+for k=0 upto 4: draw subpath(k+.47,k+.8) of star; endfor
+labels(0,1,2,3,4); endchar;
+
+beginchar(31,2/3*11pt#,.5*11pt#,0); % \oneu=31
+pickup pencircle scaled 3/4pt yscaled 1/3 rotated 30;
+save ww; ww=3/2w;
+save t; transform t;
+t=identity rotatedaround((.5ww,h),-90);
+x2=.35ww; x3=good.x .6ww;
+y2=good.y .2h; top y3=round .8h+eps;
+save p; path p; p=z2{right}...{up}z3;
+top z1=point .5 of p transformed t;
+draw (z1...p);
+labels(1,2,3); endchar;
+
+beginchar(38,11pt#,11pt#,0); % \fouru=38
+pickup pencircle scaled 3/4pt yscaled 1/3 rotated 30;
+save t; transform t;
+t=identity rotatedaround((.5w,.5h),-90);
+x2=.35w; x3=good.x .6w;
+y2=good.y .1h; top y3=round .4h+eps;
+save p; path p; p=z2{right}...{up}z3;
+top z1=point .5 of p transformed t;
+draw (z1...p);
+addto currentpicture also currentpicture transformed t;
+addto currentpicture also currentpicture transformed(t transformed t);
+labels.bot(1,3); labels.rt(2); endchar;
+
+beginchar(39,11pt#,11pt#,0); % \fourc=39
+pickup pencircle scaled 3/4pt yscaled 1/3 rotated 30;
+save t; transform t;
+t=identity rotatedaround((.5w,.5h),-90);
+x2=.35w; x3=good.x .6w;
+y2=good.y .1h; top y3=round .4h+eps;
+save p; path p; p=z2{right}...{up}z3;
+top z1=point .5 of p transformed t;
+draw (z1...p) rotatedaround((.5w,.5h),-45)
+ withpen pencircle scaled 3/4pt yscaled 1/3 rotated-15;
+addto currentpicture also currentpicture transformed t;
+addto currentpicture also currentpicture transformed(t transformed t);
+labels.bot(1,3); labels.rt(2); endchar;
+
+message "Circle variations";
+
+beginchar(32,5pt#,5pt#,0); "Quarter circle"; % \circa
+pickup pencircle scaled (.4pt+blacker);
+draw quartercircle scaled 10pt; endchar;
+
+beginchar(33,5pt#,5pt#,0); "Filled quarter circle"; % \circb
+fill ((0,0)--quartercircle--cycle) scaled 10pt; endchar;
+
+beginchar(34,5pt#,5pt#,0); "Rotated quarter circle"; % \circc
+pickup pencircle scaled (.4pt+blacker);
+draw quartercircle rotated 90 scaled 10pt shifted (5pt,0); endchar;
+
+beginchar(35,5pt#*sqrt2,5pt#,0); "Cone"; % \circd
+pickup pencircle scaled (.4pt+blacker);
+draw ((0,0)--quartercircle--cycle)
+ rotated 45 scaled 10pt shifted (.5w,0); endchar;
+
+beginchar(36,10pt#,7.5pt#,2.5pt#); "Concentric circles"; % \circe
+pickup pencircle scaled (.4pt+blacker);
+for D=.2w,.6w,w:
+ draw fullcircle scaled D shifted (.5w,.5[-d,h]);
+ endfor endchar;
+
+beginchar(37,10pt#,7.5pt#,2.5pt#); "Concentric diamonds"; % \circf
+pickup pencircle scaled (.4pt+blacker);
+for D=.2w,.6w,w:
+ draw unitsquare shifted -(.5,.5) rotated 45
+ scaled (D/sqrt2) shifted (.5w,.5[-d,h]);
+ endfor endchar;
+
+message "Miscellaneous symbols"; % mostly from the old manfnt
+
+beginchar(oct"171",15pt#,7.5pt#,2.5pt#); % arrow for errata lists
+curve#:=30/36pt#; dcurve#:=60/36pt#; bar#:=25/36pt#;
+define_whole_blacker_pixels(curve,dcurve,bar);
+penpos1(curve,90); penpos2(curve,90); penpos3(dcurve,90);
+penpos4(dcurve,90); penpos5(curve,90); penpos6(curve,90);
+y1r=vround(y1r-(y1-.5[-d,h])); y2=y5=y6=y1=y0; y4l=-d; y3r=h;
+x1=0; x2=.1w; x3=.2w; x4=.55w; x5=.65w;
+x2l:=x2l+.25curve; x5r:=x5r-.25curve;
+penstroke z1e--z2e--z3e--z4e--z5e; % crooked arrow
+penpos13(bar,0); penpos14(bar,0); x0=w;
+y13-y0=y0-y14=.24h+eps; x13=x14=7/8w-eps;
+penpos15(.5bar,angle(z14-z0)); z15l=z0; penpos16(.5bar,angle(z13-z0)); z16l=z0;
+z19=.381966[.5[z13,z14],z0];
+numeric t; path p; p=z14l{z19-z14}..z16r;
+t=xpart(p intersectiontimes((0,y6l)--(w,y6l))); x6=xpart point t of p;
+filldraw z0..{z14-z19}z14r--subpath (0,t) of\\(z14l{z19-z14}..z16r)
+ --z6l--z5l--z5r--z6r--subpath (t,0) of\\(z13l{z19-z13}..z15r)
+ --z13r{z19-z13}..z0 & cycle; % arrowhead and link
+penlabels(range 1 thru 19); endchar;
+
+beginchar(oct"170", 5pt#, 6.25pt#, 0); "Triangle for exercises";
+x1=x2=w-x3=round .43pt; y3=.5+floor.5h;
+z1-z2=(z3-z2) rotated 60;
+y1:=.5sqrt3+round(y1-.5sqrt3); y2:=h-y1;
+fill z1--z2--z3--cycle;
+labels(1,2,3); endchar;
+
+beginchar("6",256/36pt#,250/36pt#,0); % upper triangle for Addison-Wesley logo
+x1=round 5/36pt; x2=round .5w; x2-x1=x3-x2=(y2-y1)/(sqrt 3);
+y1=y3=0; fill z1--z2--z3--cycle; labels(1,2,3); endchar;
+
+beginchar("7",256/36pt#,250/36pt#,0); % lower triangle for Addison-Wesley logo
+x1=round 5/36pt; x2=round .5w; x2-x1=x3-x2=(y1-y2)/(sqrt 3);
+y1=y3; y2=0; fill z1--z2--z3--cycle; labels(1,2,3); endchar;
+
+def font_setup=
+define_pixels(u,tiny,axis,hheight,border);
+define_blacker_pixels(hair,thin,thick,rulethickness);
+pickup if tiny<.5: nullpen else: pencircle scaled tiny fi;
+tinypen:=savepen;
+currenttransform:=identity yscaled aspect_ratio slanted slant;
+enddef;
+
+% typical cmr10 parameters
+u#:=20/36pt#;
+tiny#:=0;
+axis#:=90/36pt#;
+hheight#:=250/36pt#;
+border#:=20/36pt#;
+hair#:=8/36pt#;
+thin#:=9/36pt#;
+thick#:=25/36pt#;
+fixwidth:=0;
+slant:=0;
+rulethickness#:=.4pt#;
+
+baselinedistance#:=11pt#;
+heavyline#:=50/36pt#;
+font_setup;
+define_whole_pixels(baselinedistance);
+define_blacker_pixels(heavyline);
+
+beginchar(127,25u#,hheight#+border#,0); "Dangerous bend sign";
+pickup pencircle scaled rulethickness;
+top y1=25/27h; lft x4=0;
+x1+x1=x1a+x1b=x4b+x2a=x4+x2=x4a+x2b=x3b+x3a=x3+x3=w;
+x4a=x4b=x4+u; x3b=x1a=x1-2u;
+y4+y4=y4a+y4b=y3b+y1a=y3+y1=y3a+y1b=y2b+y2a=y2+y2=0;
+y1a=y1b=y1-2/27h; y4b=y2a=y4+4/27h;
+draw z1a..z1..z1b---z2a..z2..z2b---
+ z3a..z3..z3b---z4a..z4..z4b---cycle; % signboard
+x10=x11=x12=x13=good.x(.5w-u); x14=x15=x16=x17=w-x10;
+y10=y14=28/27h+epsilon; bot y13=-baselinedistance;
+z11=(z10..z13) intersectionpoint (z1a{z1a-z4b}..z1{right});
+y15=y11; y16=y12=-y11; y17=y20=y21=y13;
+draw z11--z10--z14--z15; draw z12--z13; draw z16--z17; % signpost
+x20=w-x21; x21-x20=16u; draw z20--z21; % ground level
+x38=w-x31; x38-x31=8u; x32=x34=x38; x31=x35=x37;
+y31=-y38=12/27h; y32=-y37=9/27h; y34=-y35=3/27h;
+pickup pencircle scaled heavyline;
+draw z32{z32-z31}..z34---z35..z37{z38-z37}; % the dangerous bend
+pickup penrazor xscaled heavyline rotated (angle(z32-z31)+90);
+draw z31--z32; draw z37--z38; % upper and lower bars
+labels(1,1a,1b,2,2a,2b,3,3a,3b,4,4a,4b,10,11,12,13,14,15,16,17,20,21,
+ 31,32,33,34,35,36,37,38);
+picture dbend; dbend=currentpicture;
+endchar;
+beginchar(126,25u#,hheight#+border#,0); "Lefthanded ditto";
+currentpicture:=dbend reflectedabout((.5w,0),(.5w,h));
+endchar;
+beginchar(0,25u#,hheight#+border#,0); "Reverse video ditto";
+fill (0,-11pt)--(w,-11pt)--(w,h)--(0,h)--cycle;
+addto currentpicture also -dbend;
+endchar;
+
+beginchar(oct"140",3.75pt#,7pt#,4/3pt#); "special 1/7 for a quotation";
+currenttransform:=currenttransform slanted(2/7/aspect_ratio);
+y3=y4=.5[h,-d]=.5[y2,y5]; y5=y6;
+x1=x2=x7=.5w; x5+x6=x3+x4=w; x5=x3+1/6pt;
+pickup pencircle xscaled .5pt yscaled .35pt;
+bot y2=top y3+pt; top y1=h; bot y7=-d; lft x3=0; % y2 moved up 1/3pt (Feb 5)
+draw z1..z2; draw z3..z4;
+draw z5--z6..{down}z7;
+labels(1,2,3,4,5,6,7);
+endchar;
+currenttransform:=currenttransform slanted(-2/7/aspect_ratio);
+
+% Circumscribed circles
+pickup pencircle scaled(.4pt+blacker); rpen:=savepen;
+def begincircle(expr code,units,depth,asp,superness)=
+beginchar(code,units*20/36pt#,0,0); pickup rpen;
+aa:=.5(w++depth*pt/asp);
+x2=x4=.5[x1,x3]=.5w; y1=y3=.5[y2,y4]=-.5round(depth*pt);
+x1=good.x(x2+aa); y2=good.y(y1+asp*aa);
+draw superellipse(z1,z2,z3,z4,superness); labels(1,2,3,4) enddef;
+
+"Ellipse for floating-point mod operator"; % also for pseudo coin top
+begincircle(oct"130",29,5,.5,.75786); endchar;
+
+"Edge and bottom of pseudo coin";
+begincircle(oct"133",29,5,.5,.75786); charht:=2pt#;
+erase fill rt z1--(rt x1,top y2)--(lft x3,top y2)--lft z3--cycle;
+x0=x1; x5=x3; y0=y5=y1+2pt; draw z0--z1; draw z3--z5; labels(0,5); endchar;
+
+"Circle to enclose two digits";
+begincircle(oct"131",19,7.5,1,.75786); endchar;
+
+"Circle to enclose an exponent of 2";
+begincircle(oct"132",9,6,1,1/sqrt2); endchar;
+
+% Now we do the \qc font from TeXbook Appendix D
+pickup pencircle scaled ceiling pt; ptpen:=savepen;
+
+beginchar("a",10pt#,5.5pt#,4.5pt#); pickup ptpen; % W to S
+bot y1=lft x2=0; x2=.5[x1,x1+w]; y1=.5[y2,y2+h+d];
+draw z1{right}..{down}z2; labels(1,2); endchar;
+
+beginchar("b",10pt#,5.5pt#,4.5pt#); pickup ptpen; % W to N
+bot y1=lft x2=0; x2=.5[x1,x1+w]; y1=.5[y2,y2-h-d];
+draw z1{right}..{up}z2; labels(1,2); endchar;
+
+beginchar("c",10pt#,5.5pt#,4.5pt#); pickup ptpen; % E to N
+bot y1=lft x2=0; x2=.5[x1,x1-w]; y1=.5[y2,y2-h-d];
+draw z1{left}..{up}z2; labels(1,2); endchar;
+
+beginchar("d",10pt#,5.5pt#,4.5pt#); pickup ptpen; % E to S
+bot y1=lft x2=0; x2=.5[x1,x1-w]; y1=.5[y2,y2+h+d];
+draw z1{left}..{down}z2; labels(1,2); endchar;
+
+fontdimen 8: pt#; % rule thickness in \qc
+
+message "METAFONT logo";
+
+def arc(suffix i,j) =
+ (z.i{0,y.j-y.i}..
+ (beta[x.i,x.j],beta[y.j,y.i]){z.j-z.i}..
+ z.j{x.j-x.i,0}) enddef;
+
+def double_arc(suffix i,j,k) =
+ arc(i,j) & reverse arc(k,j) enddef;
+
+alpha:=.45; % controls bar location and similar things
+beta:=.2; % controls squareness of bowls
+
+def setparameters(text parameter_equations) =
+ string code.M,code.E,code.T,code.A,code.F,code.O,code.N;
+ numeric h#, xx#, yy#, o#, s#, u#, px#, py#;
+ numeric leftstemloc, barheight;
+ parameter_equations;
+ yy#=(h#/13.5u#)*xx#; define_whole_pixels(xx,yy);
+ define_pixels(s,u);
+ define_corrected_pixels(o);
+ py#=.9px#; define_blacker_pixels(px,py);
+ pickup pencircle xscaled px yscaled py;
+ logo_pen:=savepen;
+ leftstemloc:=good.x((2.5u#+s#)*pt);
+ barheight:=good.y(alpha*h#*pt);
+ ligtable code.T: code.A kern -.5u#;
+ ligtable code.F: code.O kern -u#;
+enddef;
+
+def beginlogochar(suffix c)(expr n) = % code c; width is n units plus sidebars
+ beginchar(code.c,n*u#+2s#,h#,0); pickup logo_pen enddef;
+
+def do_it =
+beginlogochar(M,18);
+x1=x2=leftstemloc; x4=x5=w-x1; x3=w-x3;
+y1=y5; y2=y4; bot.y1=-o; top y2=h+o; y3=y1+yy;
+draw z1--z2--z3--z4--z5;
+labels(1,2,3,4,5);
+endchar;
+
+beginlogochar(E,14);
+x1=x2=x3=leftstemloc; x4=x6=w-x1+o; x5=x4-xx;
+y1=y6; y2=y5; y3=y4; bot.y1=0; top.y3=h; y2=barheight;
+draw z6--z1--z3--z4; draw z2..z5;
+labels(1,2,3,4,5,6);
+endchar;
+
+beginlogochar(T,13);
+italcorr h#*slant+.5u#;
+if odd(w-px): w:=w+1; fi % allows a symmetric stem
+lft x1=-epsilon; x2=w-x1; x3=x4=.5w; % I tried lft x1=s-o; not as good
+y1=y2=y3; top.y1=h; bot.y4=-o;
+draw z1..z2; draw z3..z4;
+labels(1,2,3,4);
+endchar;
+
+beginlogochar(A,15); % changed from 16 on Oct 16!
+x1+x4=x2+x3=2x5=w; x1=x2=leftstemloc;
+y1=y4; y2=y3; bot.y1=-o; top.y5=h+o; y2=barheight;
+draw double_arc(2,5,3); draw z1--z2--z3--z4;
+labels(1,2,3,4,5);
+endchar;
+
+beginlogochar(F,14);
+x1=x2=x3=leftstemloc; x4=w-x1+o; x5=x4-xx;
+y2=y5; y3=y4; bot.y1=-o; top.y3=h; y2=barheight;
+draw z1--z3--z4; draw z2..z5;
+labels(1,2,3,4,5);
+endchar;
+
+beginlogochar(O,15);
+x1+x2=x3+x4=w; x1=good.x(1.5u+s); x3=x4;
+bot.y3=-o; top.y4=h+o; y1=y2=barheight;
+draw double_arc(1,4,2); draw double_arc(2,3,1);
+labels(1,2,3,4);
+endchar;
+
+beginlogochar(N,15);
+x1+x4=x1+x3=x2+x5=w; x1=x2=leftstemloc;
+y1=y4=y3-yy; bot.y1=-o; y2=y5; top.y5=h+o;
+draw z1--z2--z3; draw z4..z5;
+labels(1,2,3,4,5);
+endchar;
+enddef; % the end of "do_it"
+
+% ten-point size
+message " ten point";
+setparameters(h#=6; % height of characters, in pt
+slant:=0;
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ px#=2/3; % horizontal thickness of pen, in pt
+ code.M="M"; code.E="E"; code.T="T"; code.A="A";
+ code.F="F"; code.O="O"; code.N="N")
+do_it;
+
+currenttransform:=currenttransform slanted(.25/aspect_ratio);
+% ten-point size, slanted: 89:;<=>: = METAFONT!
+message " slanted ten point";
+setparameters(h#=6; % height of characters, in pt
+slant:=.25;
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ px#=2/3; % horizontal thickness of pen, in pt
+ code.M="8"; code.E="9"; code.T=":"; code.A=";";
+ code.F="<"; code.O="="; code.N=">")
+do_it;
+currenttransform:=currenttransform slanted(-.25/aspect_ratio);
+
+% nine-point size: hijklmnj = METAFONT!
+message " nine point";
+setparameters(h#=.9*6; % height of characters, in pt
+slant:=0;
+ xx#=.9*0.6; % extra length of certain features, in pt
+ u#=.91*4/9; % unit width, in pt
+ s#=.08; % extra sidebar, in pt
+ o#=1/10; % overshoot of curves, in pt
+ px#=.9*2/3; % horizontal thickness of pen, in pt
+ code.M="h"; code.E="i"; code.T="j"; code.A="k";
+ code.F="l"; code.O="m"; code.N="n")
+do_it;
+font_quad 18u#+2s#; % we take spacing from the 9-point logo font
+font_normal_space 6u#+2s#;
+font_normal_stretch 3u#;
+font_normal_shrink 2u#;
+
+% eight-point size: opqrstuq = METAFONT!
+message " eight point";
+setparameters(h#=.8*6; % height of characters, in pt
+slant:=0;
+ xx#=.8*0.6; % extra length of certain features, in pt
+ u#=.82*4/9; % unit width, in pt
+ s#=.2; % extra sidebar, in pt
+ o#=1/12; % overshoot of curves, in pt
+ px#=.8*2/3; % horizontal thickness of pen, in pt
+ code.M="o"; code.E="p"; code.T="q"; code.A="r";
+ code.F="s"; code.O="t"; code.N="u")
+do_it;
+
+% eight-point size, magnified 10/8: /0123451 = METAFONT!
+message " magnified eight point";
+setparameters(h#=.8*6*10/8; % height of characters, in pt
+slant:=0;
+ xx#=.8*0.6*10/8; % extra length of certain features, in pt
+ u#=.82*4/9*10/8; % unit width, in pt
+ s#=.2*10/8; % extra sidebar, in pt
+ o#=1/12*10/8; % overshoot of curves, in pt
+ px#=.8*2/3*10/8; % horizontal thickness of pen, in pt
+ code.M="/"; code.E="0"; code.T="1"; code.A="2";
+ code.F="3"; code.O="4"; code.N="5")
+do_it;
+
+currenttransform:=currenttransform slanted(2/7/aspect_ratio);
+% eight-point size, slanted: \]^_efg^ = METAFONT!
+message " slanted eight point";
+slant:=2/7;
+setparameters(h#=.8*6; % height of characters, in pt
+ xx#=.8*0.6; % extra length of certain features, in pt
+ u#=.82*4/9; % unit width, in pt
+ s#=.2; % extra sidebar, in pt
+ o#=1/12; % overshoot of curves, in pt
+ px#=.8*2/3; % horizontal thickness of pen, in pt
+ code.M="\"; code.E="]"; code.T="^"; code.A="_";
+ code.F="e"; code.O="f"; code.N="g")
+do_it;
+currenttransform:=currenttransform slanted(-2/7/aspect_ratio);
+
+% special size for the title page: ()*+,-.* = METAFONT!
+message " title page size";
+setparameters(h#=250/9; % height of characters, in pt (same as cmssc40)
+slant:=0;
+ xx#=2.9; % extra length of certain features, in pt
+ u#=(1+sqrt5)/2; % unit width, in pt
+ s#=.2; % extra sidebar, in pt
+ o#=4/9; % overshoot of curves, in pt
+ px#=4; % horizontal thickness of pen, in pt
+ code.M="("; code.E=")"; code.T="*"; code.A="+";
+ code.F=","; code.O="-"; code.N=".")
+do_it;
+
+currenttransform:=currenttransform slanted(-1/9/aspect_ratio);
+% backslanted skinny bold: BCDGHIJD = METAFONT!
+message " weird skinny bold condensed";
+setparameters(h#=25; % height of characters, in pt
+slant:=-1/9; % leftward bias
+ xx#=1.5; % extra length of certain features, in pt
+ u#=3/9; % unit width, in pt
+ s#=1/3; % extra sidebar, in pt
+ o#=2/9; % overshoot of curves, in pt
+ px#=1; % horizontal thickness of pen, in pt
+ code.M="B"; code.E="C"; code.T="D"; code.A="G";
+ code.F="H"; code.O="I"; code.N="J")
+do_it;
+currenttransform:=currenttransform slanted(+1/9/aspect_ratio);
+
+% ultrawide light: KLUVWvwU = METAFONT!
+message " weird light extended";
+setparameters(h#=6; % height of characters, in pt
+slant:=0;
+ xx#=2; % extra length of certain features, in pt
+ u#=4/3; % unit width, in pt
+ s#=-2/3; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ px#=1/3; % horizontal thickness of pen, in pt
+ code.M="K"; code.E="L"; code.T="U"; code.A="V";
+ code.F="W"; code.O="v"; code.N="w")
+do_it;
+
+message "Pixel symbols";
+
+beginchar("P",722.7/722.909pt#,722.7/722.909pt#,0); % 1pt pixel
+pickup pencircle scaled .2pt;
+top lft z1=(0,.8h); top rt z2=(.8w,.8h);
+bot lft z3=(0,0); bot rt z4=(.8w,0);
+draw z1--z4; draw z2--z3; endchar;
+
+beginchar("Q",722.7/722.909pt#,722.7/722.909pt#,0); % 1pt nonpixel
+pickup pencircle scaled .2pt;
+drawdot(.4w,.4h); endchar;
+
+beginchar("R",3*722.7/722.909pt#,3*722.7/722.909pt#,0); % 3pt pixel
+pickup pencircle scaled .3pt;
+interim autorounding:=0;
+top lft z1=(0,.9h); top z2=(.45w,.9h); top rt z3=(.9w,.9h);
+lft z4=(0,.45h); rt z5=(.9w,.45h);
+bot lft z6=(0,0); bot z7=(.45w,0); bot rt z8=(.9w,0);
+draw z1+(-50epsilon,50epsilon)--z8+(50epsilon,-50epsilon);
+draw z2+(0,50epsilon)--z7-(0,50epsilon);
+draw z3+(50epsilon,50epsilon)--z6-(50epsilon,50epsilon);
+draw z4-(50epsilon,0)--z5+(50epsilon,0); endchar;
+
+beginchar("S",3*722.7/722.909pt#,3*722.7/722.909pt#,0); % 3pt nonpixel
+pickup pencircle scaled .3pt;
+drawdot(.45w,.45h); endchar;
+
+message "Gothic d";
+
+beginchar("?",4pt#,8pt#,0);
+ % taken from NB, but my changes may have spoiled it
+stem_length#:=10/6.7pt#; define_blacker_pixels(stem_length);
+xheight:=6pt;
+penpos1 (2*stem_length,30); % normal points
+penpos2 (2*stem_length,30); % normal points
+penpos3 (2*stem_length,30); % normal points
+penpos4 (2*stem_length,30); % normal points
+
+x1l=x2=0; y1r=xheight; y2l=0;
+ penpos40(stem_length,30); z40r=round z1;
+ penpos41(stem_length,40); x40r=x41r=x42r; y41=.5[y40,y42];
+ penpos42(stem_length,30); y42l=y2;
+fill z40r--(x40r,y42l)--z42l...z41l{up}...z40l--cycle; % left stem
+
+x3=x4r=w; y3=y1; y4=y2;
+ penpos50(stem_length,30); z50l=round z3l;
+ penpos51(stem_length,40); x50l=x51l=x52l; y51=.5[y50,y52];
+ penpos52(stem_length,30); y52l=y4;
+fill z52l--(x52l,y50r)--z50r...z51r{down}...z52r--cycle; % right stem
+
+ penpos10(stem_length,30); z10=z42;
+ penpos11(stem_length,25); x11=.5[x10,x12]; y10l=y11=y12r;
+ penpos12(stem_length,30); x12r=x4; % redundant: y12l=0;
+ penpos14(stem_length*(1+cosd30),30); z14l=z12l;
+ penpos15(.07stem_length,120); z15l=z14r;
+fill z10l...z11l{z12-z10}...z12l--z14r
+ --z15r{z14l-z14r}...z11r{z10-z12}...z10r--cycle; % lower join
+
+numeric ch; ch=10; % Neenie's version was more like ch=7.5 (steeper)
+numeric s_length[];
+ s_length0*cosd(30-ch)=stem_length*cosd 30;
+ s_length1*cosd(40-ch)=stem_length*cosd 40;
+pair corner; corner=z50r;
+vardef f(expr theta)=
+ save x,y; penpos60(s_length0,theta); y60r=h; x60l=0;
+ angle(z60r-corner)<theta+60+ch enddef;
+numeric theta; theta=solve f(90,0);
+ penpos60(s_length0,theta); y60r=h; x60l=0;
+ penpos61(s_length1,theta+10); z61l=.5[z60l,z62l];
+ penpos62(s_length0,theta); z62r=corner;
+fill z60l--((z60l--z62l)intersectionpoint((x4,0)--(x4,h)))
+ --z62r...z61r{z60-z62}...z60r--cycle; % diagonal
+
+charwd:=charwd+1.03277pt#; w:= w + round(1.03277pt);
+penlabels(1,2,3,4,10,11,12,13,14,15,40,41,42,50,51,52,60,61,62);
+interim xoffset:=.46687pt; interim yoffset:=-.3pt;
+endchar;
+
+bye
diff --git a/systems/knuth/dist/lib/manmac.tex b/systems/knuth/dist/lib/manmac.tex
new file mode 100644
index 0000000000..ac0c375612
--- /dev/null
+++ b/systems/knuth/dist/lib/manmac.tex
@@ -0,0 +1,715 @@
+% Macros for The TeXbook
+
+\catcode`@=11 % borrow the private macros of PLAIN (with care)
+
+\font\tentex=cmtex10
+
+\font\inchhigh=cminch
+\font\titlefont=cmssdc10 at 40pt
+
+\font\ninerm=cmr9
+\font\eightrm=cmr8
+\font\sixrm=cmr6
+
+\font\ninei=cmmi9
+\font\eighti=cmmi8
+\font\sixi=cmmi6
+\skewchar\ninei='177 \skewchar\eighti='177 \skewchar\sixi='177
+
+\font\ninesy=cmsy9
+\font\eightsy=cmsy8
+\font\sixsy=cmsy6
+\skewchar\ninesy='60 \skewchar\eightsy='60 \skewchar\sixsy='60
+
+\font\eightss=cmssq8
+
+\font\eightssi=cmssqi8
+
+\font\ninebf=cmbx9
+\font\eightbf=cmbx8
+\font\sixbf=cmbx6
+
+\font\ninett=cmtt9
+\font\eighttt=cmtt8
+
+\hyphenchar\tentt=-1 % inhibit hyphenation in typewriter type
+\hyphenchar\ninett=-1
+\hyphenchar\eighttt=-1
+
+\font\ninesl=cmsl9
+\font\eightsl=cmsl8
+
+\font\nineit=cmti9
+\font\eightit=cmti8
+
+\font\tenu=cmu10 % unslanted text italic
+\font\magnifiedfiverm=cmr5 at 10pt
+\font\manual=manfnt % font used for the METAFONT logo, etc.
+\font\cmman=cmman % font used for miscellaneous Computer Modern variations
+
+\newskip\ttglue
+\def\tenpoint{\def\rm{\fam0\tenrm}%
+ \textfont0=\tenrm \scriptfont0=\sevenrm \scriptscriptfont0=\fiverm
+ \textfont1=\teni \scriptfont1=\seveni \scriptscriptfont1=\fivei
+ \textfont2=\tensy \scriptfont2=\sevensy \scriptscriptfont2=\fivesy
+ \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+ \def\it{\fam\itfam\tenit}%
+ \textfont\itfam=\tenit
+ \def\sl{\fam\slfam\tensl}%
+ \textfont\slfam=\tensl
+ \def\bf{\fam\bffam\tenbf}%
+ \textfont\bffam=\tenbf \scriptfont\bffam=\sevenbf
+ \scriptscriptfont\bffam=\fivebf
+ \def\tt{\fam\ttfam\tentt}%
+ \textfont\ttfam=\tentt
+ \tt \ttglue=.5em plus.25em minus.15em
+ \normalbaselineskip=12pt
+ \def\MF{{\manual META}\-{\manual FONT}}%
+ \let\sc=\eightrm
+ \let\big=\tenbig
+ \setbox\strutbox=\hbox{\vrule height8.5pt depth3.5pt width\z@}%
+ \normalbaselines\rm}
+
+\def\ninepoint{\def\rm{\fam0\ninerm}%
+ \textfont0=\ninerm \scriptfont0=\sixrm \scriptscriptfont0=\fiverm
+ \textfont1=\ninei \scriptfont1=\sixi \scriptscriptfont1=\fivei
+ \textfont2=\ninesy \scriptfont2=\sixsy \scriptscriptfont2=\fivesy
+ \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+ \def\it{\fam\itfam\nineit}%
+ \textfont\itfam=\nineit
+ \def\sl{\fam\slfam\ninesl}%
+ \textfont\slfam=\ninesl
+ \def\bf{\fam\bffam\ninebf}%
+ \textfont\bffam=\ninebf \scriptfont\bffam=\sixbf
+ \scriptscriptfont\bffam=\fivebf
+ \def\tt{\fam\ttfam\ninett}%
+ \textfont\ttfam=\ninett
+ \tt \ttglue=.5em plus.25em minus.15em
+ \normalbaselineskip=11pt
+ \def\MF{{\manual hijk}\-{\manual lmnj}}%
+ \let\sc=\sevenrm
+ \let\big=\ninebig
+ \setbox\strutbox=\hbox{\vrule height8pt depth3pt width\z@}%
+ \normalbaselines\rm}
+
+\def\eightpoint{\def\rm{\fam0\eightrm}%
+ \textfont0=\eightrm \scriptfont0=\sixrm \scriptscriptfont0=\fiverm
+ \textfont1=\eighti \scriptfont1=\sixi \scriptscriptfont1=\fivei
+ \textfont2=\eightsy \scriptfont2=\sixsy \scriptscriptfont2=\fivesy
+ \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+ \def\it{\fam\itfam\eightit}%
+ \textfont\itfam=\eightit
+ \def\sl{\fam\slfam\eightsl}%
+ \textfont\slfam=\eightsl
+ \def\bf{\fam\bffam\eightbf}%
+ \textfont\bffam=\eightbf \scriptfont\bffam=\sixbf
+ \scriptscriptfont\bffam=\fivebf
+ \def\tt{\fam\ttfam\eighttt}%
+ \textfont\ttfam=\eighttt
+ \tt \ttglue=.5em plus.25em minus.15em
+ \normalbaselineskip=9pt
+ \def\MF{{\manual opqr}\-{\manual stuq}}%
+ \let\sc=\sixrm
+ \let\big=\eightbig
+ \setbox\strutbox=\hbox{\vrule height7pt depth2pt width\z@}%
+ \normalbaselines\rm}
+
+\def\tenmath{\tenpoint\fam-1 } % use after $ in ninepoint sections
+\def\tenbig#1{{\hbox{$\left#1\vbox to8.5pt{}\right.\n@space$}}}
+\def\ninebig#1{{\hbox{$\textfont0=\tenrm\textfont2=\tensy
+ \left#1\vbox to7.25pt{}\right.\n@space$}}}
+\def\eightbig#1{{\hbox{$\textfont0=\ninerm\textfont2=\ninesy
+ \left#1\vbox to6.5pt{}\right.\n@space$}}}
+
+% Page layout
+\newdimen\pagewidth \newdimen\pageheight \newdimen\ruleht
+\hsize=29pc \vsize=44pc \maxdepth=2.2pt \parindent=3pc
+\pagewidth=\hsize \pageheight=\vsize \ruleht=.5pt
+\abovedisplayskip=6pt plus 3pt minus 1pt
+\belowdisplayskip=6pt plus 3pt minus 1pt
+\abovedisplayshortskip=0pt plus 3pt
+\belowdisplayshortskip=4pt plus 3pt
+
+%\newinsert\footins
+\def\footnote#1{\edef\@sf{\spacefactor\the\spacefactor}#1\@sf
+ \insert\footins\bgroup\eightpoint
+ \interlinepenalty100 \let\par=\endgraf
+ \leftskip=\z@skip \rightskip=\z@skip
+ \splittopskip=10pt plus 1pt minus 1pt \floatingpenalty=20000
+ \smallskip\item{#1}\bgroup\strut\aftergroup\@foot\let\next}
+\skip\footins=12pt plus 2pt minus 4pt % space added when footnote is present
+%\count\footins=1000 % footnote magnification factor (1 to 1)
+\dimen\footins=30pc % maximum footnotes per page
+
+\newinsert\margin
+\dimen\margin=\maxdimen
+%\count\margin=0 \skip\margin=0pt % marginal inserts take up no space
+
+\newif\iftitle
+\def\titlepage{\global\titletrue} % for pages without headlines
+\def\rhead{} % \rhead contains the running headline
+
+\def\leftheadline{\hbox to \pagewidth{\spaceskip=0pt
+ \vbox to 10pt{}% strut to position the baseline
+ \llap{\tenbf\folio\kern1pc}% folio to left of text
+ \tenit\rhead\hfil% running head flush left
+ }}
+\def\rightheadline{\hbox to \pagewidth{\spaceskip=0pt
+ \vbox to 10pt{}% strut to position the baseline
+ \hfil\tenit\rhead\/% running head flush right
+ \rlap{\kern1pc\tenbf\folio}% folio to right of text
+ }}
+
+\def\onepageout#1{\shipout\vbox{ % here we define one page of output
+ \offinterlineskip % butt the boxes together
+ \vbox to 3pc{ % this part goes on top of the 44pc pages
+ \iftitle % the next is used for title pages
+ \global\titlefalse % reset the titlepage switch
+ \setcornerrules % for camera alignment
+ \else\ifodd\pageno \rightheadline\else\leftheadline\fi\fi
+ \vfill} % this completes the \vbox to 3pc
+ \vbox to \pageheight{
+ \ifvoid\margin\else % marginal info is present
+ \rlap{\kern31pc\vbox to\z@{\kern4pt\box\margin \vss}}\fi
+ #1 % now insert the main information
+ \ifvoid\footins\else % footnote info is present
+ \vskip\skip\footins \kern-3pt
+ \hrule height\ruleht width\pagewidth \kern-\ruleht \kern3pt
+ \unvbox\footins\fi
+ \boxmaxdepth=\maxdepth
+ } % this completes the \vbox to \pageheight
+ }
+ \advancepageno}
+
+\def\setcornerrules{\hbox to \pagewidth{\vrule width 1pc height\ruleht
+ \hfil \vrule width 1pc}
+ \hbox to \pagewidth{\llap{\sevenrm(page \folio)\kern1pc}%
+ \vrule height1pc width\ruleht depth\z@
+ \hfil \vrule width\ruleht depth\z@}}
+
+\output{\onepageout{\unvbox255}}
+
+\newbox\partialpage
+\def\begindoublecolumns{\begingroup
+ \output={\global\setbox\partialpage=\vbox{\unvbox255\bigskip}}\eject
+ \output={\doublecolumnout} \hsize=14pc \vsize=89pc}
+\def\enddoublecolumns{\output={\balancecolumns}\eject
+ \endgroup \pagegoal=\vsize}
+
+\def\doublecolumnout{\splittopskip=\topskip \splitmaxdepth=\maxdepth
+ \dimen@=44pc \advance\dimen@ by-\ht\partialpage
+ \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
+ \onepageout\pagesofar
+ \unvbox255 \penalty\outputpenalty}
+\def\pagesofar{\unvbox\partialpage
+ \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}}
+\def\balancecolumns{\setbox0=\vbox{\unvbox255} \dimen@=\ht0
+ \advance\dimen@ by\topskip \advance\dimen@ by-\baselineskip
+ \divide\dimen@ by2 \splittopskip=\topskip
+ {\vbadness=10000 \loop \global\setbox3=\copy0
+ \global\setbox1=\vsplit3 to\dimen@
+ \ifdim\ht3>\dimen@ \global\advance\dimen@ by1pt \repeat}
+ \setbox0=\vbox to\dimen@{\unvbox1}
+ \setbox2=\vbox to\dimen@{\unvbox3}
+ \pagesofar}
+
+% To produce only a subset of pages, put the page numbers on separate
+% lines in a file called pages.tex
+\let\Shipout=\shipout
+\newread\pages \newcount\nextpage \openin\pages=pages
+\def\getnextpage{\ifeof\pages\else
+ {\endlinechar=-1\read\pages to\next
+ \ifx\next\empty % in this case we should have eof now
+ \else\global\nextpage=\next\fi}\fi}
+\ifeof\pages\else\message{OK, I'll ship only the requested pages!}
+ \getnextpage\fi
+\def\shipout{\ifeof\pages\let\next=\Shipout
+ \else\ifnum\pageno=\nextpage\getnextpage\let\next=\Shipout
+ \else\let\next=\Tosspage\fi\fi \next}
+\newbox\garbage \def\Tosspage{\deadcycles=0\setbox\garbage=}
+
+% Chapter formatting
+% The preface and table of contents are formatted in place, not here
+
+\newcount\exno % for the number of exercises in the current chapter
+\newcount\subsecno % for the number of subsections in the current chapter
+
+\def\beginchapter#1 #2#3. #4\par{\global\exno=0
+ \subsecno=0
+ \def\chapno{#2#3}
+ \ifodd\pageno
+ \errmessage{You had too much text on that last page; I'm backing up}
+ \advance\pageno by-1 \fi
+ \titlepage
+ \def\\{ } % \\'s in the title will be treated as spaces
+ \message{#1 #2#3:} % show the chapter title on the terminal
+ \def\MF{{\manual 89:;<=>:}} % slant the logo
+ \xdef\rhead{#1 #2#3: #4\unskip}
+ {\def\TeX{T\kern-.2em\lower.5ex\hbox{E}\kern-.06em X}
+ \def\MF{{\vbox to30pt{}\manual ()*+,-.*}}
+ \def\\{#3} \advance\hsize by -18mm
+ \ifx\empty\\ \rightline{\inchhigh #2\kern-.04em}
+ \else\rightline{\inchhigh #2\kern-.06em#3\kern-.04em}\fi
+ \vskip 1.75pc
+ \baselineskip 36pt \lineskiplimit \titlelsl \lineskip 12pt
+ \let\\=\cr % now the \\'s are line dividers
+ \halign{\line{\titlefont\hss##}\\#4\unskip\\}
+ \vfill\eject} % output the chapter title page
+ \tenpoint
+ \noindent\ignorespaces} % the first paragraph of a chapter is not indented
+\newdimen\titlelsl \titlelsl=1pt
+
+\outer\def\endchapter{\ifodd\pageno \else\vfill\eject\null\fi
+ \begingroup\bigskip\vfill % beginning of the quotes
+ \def\eject{\endgroup\eject}
+ \def\par{\ifhmode\/\endgraf\fi}\obeylines
+ \def\TeX{T\kern-.2em\lower.5ex\hbox{E}\kern-.000em X}
+ \def\MF{{\manual opqr}\-{\manual stuq}}
+ \eightpoint \let\tt=\ninett
+ \baselineskip 10pt
+ \parfillskip \z@
+ \interlinepenalty 10000
+ \leftskip \z@ plus 40pc minus \parindent
+ \let\rm=\eightss \let\sl=\eightssi
+ \everypar{\sl}}
+\def\author#1(#2){\smallskip\noindent\rm--- #1\unskip\enspace(#2)}
+
+\def\dbend{{\manual\char127}} % dangerous bend sign
+\def\d@nger{\medbreak\begingroup\clubpenalty=10000
+ \def\par{\endgraf\endgroup\medbreak} \noindent\hang\hangafter=-2
+ \hbox to0pt{\hskip-\hangindent\dbend\hfill}\ninepoint}
+\outer\def\danger{\d@nger}
+\def\dd@nger{\medbreak\begingroup\clubpenalty=10000
+ \def\par{\endgraf\endgroup\medbreak} \noindent\hang\hangafter=-2
+ \hbox to0pt{\hskip-\hangindent\dbend\kern1pt\dbend\hfill}\ninepoint}
+\outer\def\ddanger{\dd@nger}
+\def\enddanger{\endgraf\endgroup} % omits the \medbreak
+
+\outer\def\subsection#1. {\medbreak\advance\subsecno by 1
+ \noindent{\it \the\subsecno.\enspace#1.\enspace}}
+\def\ansno#1.#2:{\medbreak\noindent
+ \hbox to\parindent{\bf\hss#1.#2.\enspace}\ignorespaces}
+
+% Composition macros
+\hyphenation{man-u-script man-u-scripts ap-pen-dix xscaled}
+
+\def\AmSTeX{$\cal A\kern-.1667em\lower.5ex\hbox{$\cal M$}\kern-.075em
+ S$-\TeX}
+\def\bull{\vrule height .9ex width .8ex depth -.1ex } % square bullet
+\def\SS{{\it SS}} % scriptscript style
+\def\|{\leavevmode\hbox{\tt\char`\|}} % vertical line
+\def\dn{\leavevmode\hbox{\tt\char'14}} % downward arrow
+\def\up{\leavevmode\hbox{\tt\char'13}} % upward arrow
+\def\]{\leavevmode\hbox{\tt\char`\ }} % visible space
+
+\def\pt{\,{\rm pt}} % units of points, in math formulas
+\def\em{\,{\rm em}} % units of ems, in math formulas
+\def\<#1>{\leavevmode\hbox{$\langle$#1\/$\rangle$}} % syntactic quantity
+\def\oct#1{\hbox{\rm\'{}\kern-.2em\it#1\/\kern.05em}} % octal constant
+\def\hex#1{\hbox{\rm\H{}\tt#1}} % hexadecimal constant
+\def\cstok#1{\leavevmode\thinspace\hbox{\vrule\vtop{\vbox{\hrule\kern1pt
+ \hbox{\vphantom{\tt/}\thinspace{\tt#1}\thinspace}}
+ \kern1pt\hrule}\vrule}\thinspace} % control sequence token
+
+{\obeyspaces\gdef {\ }}
+\def\parbreak{\hfil\break\indent\strut}
+\def\stretch{\nobreak\hskip0pt plus2pt\relax}
+
+% macros for non-centered displays
+\outer\def\begindisplay{\obeylines\startdisplay}
+{\obeylines\gdef\startdisplay#1
+ {\catcode`\^^M=5$$#1\halign\bgroup\indent##\hfil&&\qquad##\hfil\cr}}
+\outer\def\enddisplay{\crcr\egroup$$}
+
+% (the following \begin...\end-type macros do not appear in Appendix E)
+% macros for demonstrating math constructions
+\outer\def\beginmathdemo{$$\advance\baselineskip by2pt
+ \halign\bgroup\indent\hbox to 160pt{##\hfil}&$##$\hfil\cr\noalign{\vskip-2pt}}
+\outer\def\begindisplaymathdemo {$$\advance\baselineskip by15pt
+ \halign\bgroup\indent\hbox to 160pt{##\hfil}&$\displaystyle{##}$\hfil\cr
+ \noalign{\vskip-15pt}}
+\outer\def\beginlongmathdemo{$$\advance\baselineskip by2pt
+ \halign\bgroup\indent\hbox to 210pt{##\hfil}&$##$\hfil\cr\noalign{\vskip-2pt}}
+\outer\def\beginlongdisplaymathdemo {$$\advance\baselineskip by15pt
+ \halign\bgroup\indent\hbox to 210pt{##\hfil}&$\displaystyle{##}$\hfil\cr
+ \noalign{\vskip-15pt}}
+\outer\def\endmathdemo{\egroup$$}
+
+% macros for font tables
+\def\oddline#1{\cr
+ \noalign{\nointerlineskip}
+ \multispan{19}\hrulefill&
+ \setbox0=\hbox{\lower 2.3pt\hbox{\hex{#1x}}}\smash{\box0}\cr
+ \noalign{\nointerlineskip}}
+\def\evenline{\cr\noalign{\hrule}}
+\def\chartstrut{\lower4.5pt\vbox to14pt{}}
+\def\beginchart#1{$$\postdisplaypenalty=-10000 \global\count@=0 #1
+ \halign to\hsize\bgroup
+ \chartstrut##\tabskip0pt plus10pt&
+ &\hfil##\hfil&\vrule##\cr
+ \lower6.5pt\null
+ &&&\oct0&&\oct1&&\oct2&&\oct3&&\oct4&&\oct5&&\oct6&&\oct7&\evenline}
+\def\endchart{\raise11.5pt\null&&&\hex 8&&\hex 9&&\hex A&&\hex B&
+ &\hex C&&\hex D&&\hex E&&\hex F&\cr\egroup$$}
+\def\:{\setbox0=\hbox{\char\count@}%
+ \ifdim\ht0>7.5pt\reposition
+ \else\ifdim\dp0>2.5pt\reposition\fi\fi
+ \box0\global\advance\count@ by1 }
+\def\reposition{\setbox0=\hbox{$\vcenter{\kern2pt\box0\kern2pt}$}}
+\def\normalchart{%
+ &\oct{00x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline0
+ &\oct{01x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+ &\oct{02x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline1
+ &\oct{03x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+ &\oct{04x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline2
+ &\oct{05x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+ &\oct{06x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline3
+ &\oct{07x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+ &\oct{10x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline4
+ &\oct{11x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+ &\oct{12x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline5
+ &\oct{13x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+ &\oct{14x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline6
+ &\oct{15x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+ &\oct{16x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline7
+ &\oct{17x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline}
+
+% (now Appendix E resumes again)
+% macros for verbatim scanning
+\chardef\other=12
+\def\ttverbatim{\begingroup
+ \catcode`\\=\other
+ \catcode`\{=\other
+ \catcode`\}=\other
+ \catcode`\$=\other
+ \catcode`\&=\other
+ \catcode`\#=\other
+ \catcode`\%=\other
+ \catcode`\~=\other
+ \catcode`\_=\other
+ \catcode`\^=\other
+ \obeyspaces \obeylines \tt}
+
+\outer\def\begintt{$$\let\par=\endgraf \ttverbatim \parskip=\z@
+ \catcode`\|=0 \rightskip-5pc \ttfinish}
+{\catcode`\|=0 |catcode`|\=\other % | is temporary escape character
+ |obeylines % end of line is active
+ |gdef|ttfinish#1^^M#2\endtt{#1|vbox{#2}|endgroup$$}}
+
+\catcode`\|=\active
+{\obeylines \gdef|{\ttverbatim \spaceskip\ttglue \let^^M=\ \let|=\endgroup}}
+
+% macros for syntax rules (again, not in Appendix E)
+\def\[#1]{\silenttrue\xref|#1|\thinspace{\tt#1}\thinspace} % keyword in syntax
+\def\beginsyntax{\endgraf\nobreak\medskip
+ \begingroup \catcode`<=13 \catcode`[=13
+ \let\par=\endsyntaxline \obeylines}
+\def\endsyntaxline{\futurelet\next\syntaxswitch}
+\def\syntaxswitch{\ifx\next\<\let\next=\syntaxrule
+ \else\ifx\next\endsyntax\let\next=\endgroup
+ \else\let\next=\continuerule\fi\fi \next}
+\def\continuerule{\hfil\break\indent\qquad}
+\def\endsyntax{\medbreak\noindent}
+{\catcode`<=13 \catcode`[=13
+ \global\let<=\< \global\let[=\[
+ \gdef\syntaxrule<#1>{\endgraf\indent\silentfalse\xref\<#1>}}
+\def\is{\ $\longrightarrow$ }
+\def\alt{\ $\vert$ }
+
+% macros to demarcate lines quoted from TeX source files
+\def\beginlines{\par\begingroup\nobreak\medskip\parindent\z@ \obeylines
+ \hrule\kern1pt\nobreak \everypar{\strut}}
+\def\endlines{\kern1pt\hrule\endgroup\medbreak\noindent}
+\def\weakendlines{\kern1pt\hrule\endgroup\medskip\noindent}
+\def\finalendlines{\kern1pt\hrule\endgroup\medbreak}
+
+\outer\def\exercise{\medbreak
+ \global\advance\exno by 1
+ \noindent\llap{\manual\char'170\rm\kern.15em}% triangle in margin
+ {\ninebf EXERCISE \bf\chapno.\the\exno}\par\nobreak\noindent}
+\def\dexercise{\global\advance\exno by 1
+ \llap{\manual\char'170\rm\kern.15em}% triangle in indented space
+ {\eightbf EXERCISE \bf\chapno.\the\exno}\hfil\break}
+\outer\def\dangerexercise{\d@nger \dexercise}
+\outer\def\ddangerexercise{\dd@nger \dexercise}
+
+\newwrite\ans
+\immediate\openout\ans=answers % file for answers to exercises
+\outer\def\answer{\par\medbreak
+ \immediate\write\ans{}
+ \immediate\write\ans{\string\ansno\chapno.\the\exno:}
+ \copytoblankline}
+\def\copytoblankline{\begingroup\setupcopy\copyans}
+\def\setupcopy{\def\do##1{\catcode`##1=\other}\dospecials
+ \catcode`\|=\other \obeylines}
+{\obeylines \gdef\copyans#1
+ {\def\next{#1}%
+ \ifx\next\empty\let\next=\endgroup %
+ \else\immediate\write\ans{\next} \let\next=\copyans\fi\next}}
+
+% Editorial notes: some things to watch for.
+
+% f |\ and f ^|\ => insert \/ [e.g., if\/ |\hbox|...]
+% appendi => check for \null [e.g., Appendix~B\null.]
+% ly- => the hyphen is probably omittable
+% ''. and '', => transpose to .'' and ,''
+% dgement => dgment
+% in MFbook: f@' and \MF, and \MF.
+
+% Macros for drawing figures (not in Appendix E)
+\def\hidehrule#1#2{\kern-#1\hrule height#1 depth#2 \kern-#2 }
+\def\hidevrule#1#2{\kern-#1{\dimen0=#1
+ \advance\dimen0 by#2\vrule width\dimen0}\kern-#2 }
+% \makeblankbox puts rules at the edges of a blank box
+% whose dimensions are those of \box0 (assuming nonnegative wd,ht,dp)
+% #1 is rule thickness outside, #2 is rule thickness inside
+\def\makeblankbox#1#2{\hbox{\lower\dp0\vbox{\hidehrule{#1}{#2}%
+ \kern-#1% overlap the rules at the corners
+ \hbox to\wd0{\hidevrule{#1}{#2}%
+ \raise\ht0\vbox to #1{}% set the vrule height
+ \lower\dp0\vtop to #1{}% set the vrule depth
+ \hfil\hidevrule{#2}{#1}}%
+ \kern-#1\hidehrule{#2}{#1}}}}
+\def\maketypebox{\makeblankbox{0pt}{1pt}}
+\def\makelightbox{\makeblankbox{.2pt}{.2pt}}
+
+% \box\bigdot is a null box with a bullet at its reference point
+\newbox\bigdot \newbox\smalldot
+\setbox0=\hbox{$\vcenter{}$} % \ht0 is the axis height
+\setbox1=\hbox to\z@{$\hss\bullet\hss$} % bullet is centered on the axis
+\setbox\bigdot=\vbox to\z@{\kern-\ht1 \kern\ht0 \box1 \vss}
+\setbox1=\hbox to\z@{$\hss\cdot\hss$} % cdot is centered on the axis
+\setbox\smalldot=\vbox to\z@{\kern-\ht1 \kern\ht0 \box1 \vss}
+
+% \arrows makes things like <--- text --->
+\def\arrows#1#2{% #1=width, #2=text
+ {\setbox0=\hbox{$\mkern-2mu\mathord-\mkern-2mu$}
+ \hbox to #1{\kern-.055556em$\leftarrow\mkern-6mu$%
+ \cleaders\copy0\hfil
+ \kern.4em #2\kern.4em
+ \cleaders\copy0\hfil
+ $\mkern-6mu\rightarrow$\kern-.055556em}}}
+
+% \samplebox makes the outline of a box, with big dot at reference point
+\def\samplebox#1#2#3#4{% #1=ht, #2=dp, #3=wd, #4=text
+ {\setbox0=\vtop{\vbox to #1{\hbox to #3{}\vss}
+ \nointerlineskip
+ \vbox to #2{}}% now \box0 has the desired ht, dp, and wd
+ \hbox{\copy\bigdot
+ \vrule height.2pt depth.2pt width#3%
+ \kern-#3%
+ \makelightbox
+ \kern-#3%
+ \raise#1\vbox{\hbox to #3{\hss#4\hss}
+ \kern 3pt}}}}
+
+% \sampleglue makes glue between sample boxes
+\newdimen\varunit
+\varunit=\hsize \advance\varunit by-2\parindent
+\divide\varunit by 58 % illustrations in Chapter 12
+\def\sampleglue#1#2{% #1=width, #2=text
+ \vtop{\hbox to #1{\xleaders\hbox to .5\varunit{\hss\copy\smalldot\hss}\hfil}
+ \kern3pt
+ \tabskip \z@ plus 1fil
+ \halign to #1{\hfil##\cr#2\cr}}}
+
+% Indexing macros
+\newif\ifproofmode
+\proofmodetrue % this should be false when making camera-ready copy
+\newwrite\inx
+\immediate\openout\inx=index % file for index reminders
+\newif\ifsilent
+\def\specialhat{\ifmmode\def\next{^}\else\let\next=\beginxref\fi\next}
+\def\beginxref{\futurelet\next\beginxrefswitch}
+\def\beginxrefswitch{\ifx\next\specialhat\let\next=\silentxref
+ \else\silentfalse\let\next=\xref\fi \next}
+\catcode`\^=\active \let ^=\specialhat
+\def\silentxref^{\silenttrue\xref}
+
+\def\marginstyle{\vrule height6pt depth2pt width\z@ \sevenrm}
+
+\chardef\bslash=`\\
+\def\xref{\futurelet\next\xrefswitch}
+\def\xrefswitch{\begingroup
+ \ifx\next|\aftergroup\vxref % case 1 or 2, |arg| or |\arg|
+ \else\ifx\next\<\aftergroup\anglexref % case 3, "\<arg>" means angle brackets
+ \else\aftergroup\normalxref \fi\fi\endgroup} % case 0, "{arg}"
+\def\vxref|{\catcode`\\=\active \futurelet\next\vxrefswitch}
+\def\vxrefswitch#1|{\catcode`\\=0
+ \ifx\next\empty\def\xreftype{2}%
+ \def\next{{\tt\bslash\text}}% type 2, |\arg|
+ \else\def\xreftype{1}\def\next{{\tt\text}}\fi % type 1, |arg|
+ \edef\text{#1}\makexref}
+{\catcode`\|=0 \catcode`\\=\active |gdef\{}}
+\def\anglexref\<#1>{\def\xreftype{3}\def\text{#1}%
+ \def\next{\<\text>}\makexref}
+\def\normalxref#1{\def\xreftype{0}\def\text{#1}\let\next=\text\makexref}
+\def\makexref{\ifproofmode\insert\margin{\hbox{\marginstyle\text}}%
+ \xdef\writeit{\write\inx{\text\space!\xreftype\space
+ \noexpand\number\pageno.}}\writeit
+ \else\ifhmode\kern\z@\fi\fi
+ \ifsilent\ignorespaces\else\next\fi}
+% the \insert (which is done in proofmode only) suppresses hyphenation,
+% so the \kern\z@ is put in to give the same effect in non-proofmode.
+
+% Internal cross references that may change
+\def\sesame{61} % page number for Sesame Street quote
+\def\bmiexno{20} % exercise number for bold math italic
+\def\punishexno{1} % exercise number for `punishment'
+\def\fracexno{6} % exercise number for `\frac'
+\def\vshippage{31} % error message from `\vship'
+\def\storypage{24} % listing of story.tex
+\def\metaT{4} % exercise number for T of METAFONT
+\def\xwhat{2} % exercise number for x3:=whatever
+\def\Xwhat{2} % exercise number for whatever itself
+
+\def\checkequals#1#2{\ifnum#1=#2\else
+ \errmessage{Redefine \string#1 to be \the#2}\fi}
+
+% Things for The METAFONTbook only
+\ifx\MFmanual\!\else\endinput\fi
+
+\def\!{\kern-.03em\relax}
+
+\def\frac#1/#2{\leavevmode\kern.1em
+ \raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em
+ /\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}
+
+\outer\def\displayfig #1 (#2){$$\advance\abovedisplayskip by 3pt
+ \leftline{\indent\figbox{#1}{3in}{#2}\vbox}$$}
+\def\rightfig #1 (#2 x #3) ^#4 {% #2 wide and #3 deep, raised #4
+ \strut\vadjust{\setbox0=\vbox to 0pt{\vss
+ \hbox to\pagewidth{\hfil
+ \raise #4\figbox{#1}{#2}{#3}\vtop \quad}}
+ \dp0=0pt \box0}}
+\def\figbox#1#2#3#4{#4to#3{ % makes a box #2 wide and #3 deep
+ \ifproofmode\kern0pt\hrule\vfill
+ \hsize=#2 \baselineskip 6pt \fiverm\noindent\raggedright
+ (Figure #1 will be inserted here; too bad you can't see it now.)
+ \endgraf\vfill\hrule
+ \else\vfill\hbox to#2{}\fi}}
+
+\def\endsyntax{\begingroup\let\par=\endgraf\medbreak\endgroup\noindent}
+
+\let\BEGINCHAPTER=\beginchapter
+\def\beginchapter{\titlelsl=1pt \BEGINCHAPTER}
+\def\beginChapter{\titlelsl=2pt \BEGINCHAPTER}
+
+\def\decreasehsize #1 {\advance\hsize-#1}
+\def\restorehsize{\hsize=\pagewidth}
+
+\catcode`\@=\active
+\catcode`\"=\active
+\def\ttverbatim{\begingroup \catcode`\@=\other \catcode`\"=\other
+ \catcode`\\=\other
+ \catcode`\{=\other
+ \catcode`\}=\other
+ \catcode`\$=\other
+ \catcode`\&=\other
+ \catcode`\#=\other
+ \catcode`\%=\other
+ \catcode`\~=\other
+ \catcode`\_=\other
+ \catcode`\^=\other
+ \obeyspaces \obeylines \tt}
+\def\setupcopy{\def\do##1{\catcode`##1=\other}\dospecials
+ \catcode`\|=\other \catcode`\@=\other \catcode`\"=\other \obeylines}
+\def\_{\leavevmode \kern.06em \vbox{\hrule width.3em}}
+\def@#1@{\begingroup\def\_{\kern.04em
+ \vbox{\hrule width.3em height .6pt}\kern.08em}%
+ \ifmmode\mathop{\bf#1}\else\hbox{\bf#1\/}\fi\endgroup}
+\def"#1"{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\xrefswitch{\begingroup
+ \ifx\next|\aftergroup\vxref % case 1, |arg| or |\arg|
+ \else\ifx\next@\aftergroup\boldxref % case 2, "@arg@" means boldface
+ \else\ifx\next"\aftergroup\italxref % case 4, ""arg"" means boldface
+ \else\ifx\next\<\aftergroup\anglexref % case 3, "\<arg>" means angle brackets
+ \else\aftergroup\normalxref \fi\fi\fi\fi\endgroup} % case 0, "{arg}"
+\def\boldxref@#1@{\def\xreftype{2}\def\text{#1}%
+ \def\next{@\text@}\makexref}
+\def\italxref"#1"{\def\xreftype{4}\def\text{#1}%
+ \def\next{"\text"}\makexref}
+
+\def\pyth+{\mathbin{++}}
+\def\0{\raise.7ex\hbox{$\scriptstyle\#$}}
+\def\to{\mathrel{\ldotp\ldotp}}
+\def\dashto{\mathrel{\hbox{-\thinspace-\kern-.05em}}}
+\def\ddashto{\mathrel{\hbox{-\thinspace-\thinspace-\kern-.05em}}}
+\def\round{\mathop{\rm round}}
+\def\angle{\mathop{\rm angle}}
+\def\rmsqrt{\mathop{\rm sqrt}}
+\def\reverse{\mathop{\rm reverse}}
+\def\curl{\mathop{\rm curl}}
+\def\tension{\mathop{\rm tension}}
+\def\atleast{\mathop{\rm atleast}}
+\def\controls{\mathop{\rm controls}}
+\def\and{\,{\rm and}\,}
+\def\cycle{{\rm cycle}}
+\def\pickup{@pickup@ \thinspace}
+\def\penpos#1{\hbox{\it penpos}_{#1}}
+\def\pentaper#1{\hbox{\it pentaper}_{#1}}
+
+\chardef\hexa=1 % first hex
+\chardef\hexb=2 % top and bot adjusted
+\chardef\hexc=3 % same, bold
+\chardef\hexd=4 % same, confined to box
+\chardef\hexe=5 % penstroked hex
+\chardef\Aa=6 % stick-figure A, golden ratio
+\def\sevenAs{\char7\char8\char9\char10\char11\char12\char13} % same, variants
+\chardef\Az=14 % same, with crooked bar
+\chardef\Ab=15 % \Aa with rectilinear elliptical pen
+\chardef\Ac=16 % same, with the ellipse tilted
+\chardef\beana=17 % kidney bean, default pen
+\chardef\beanb=18 % same, twice as bold
+\chardef\beanc=19 % same, rectilinear elliptical pen
+\chardef\beand=20 % same, with the ellipse tilted
+\chardef\niba=21 % 10x rectilinear ellipse
+\chardef\nibb=22 % same, with the ellipse tilted
+\chardef\nibc=23 % same, 90 degrees titled
+\chardef\IOT=24 % Ionian T
+\chardef\IOS=25 % Ionian S
+\chardef\IOO=26 % Ionian O
+\chardef\IOI=27 % Ionian I
+\chardef\cubea=28 % possible cube
+\chardef\cubeb=29 % impossible cube
+\chardef\bicentennial=30 % star with overlapping strokes
+\chardef\oneu=31 % 1/4 of uuuu ornament
+\chardef\circa=32 % quartercircle
+\chardef\circb=33 % filled quartercircle
+\chardef\circc=34 % rotated quartercircle
+\chardef\circd=35 % cone
+\chardef\circe=36 % concentric circles
+\chardef\circf=37 % concentric diamonds
+\chardef\fouru=38 % uuuu ornament
+\chardef\fourc=39 % same, rotated
+\chardef\seventh='140 % 1/7, to go with cmssqi8
+
+\newdimen\apspix
+\apspix=31448sp % 8 APS pixels = 52413.64sp, and I'm taking 60% of this
+% to crude approximation, there are about 2\apspix per pt
+\newdimen\blankpix \newdimen\Blankpix
+\setbox0=\hbox{\manual P} \blankpix=\wd0 % approximately 1pt blank pixel
+\setbox0=\hbox{\manual R} \Blankpix=\wd0 % approximately 3pt blank pixel
+
+\def\leftheadline{\hbox to \pagewidth{%
+ \vbox to 10pt{}% strut to position the baseline
+ \llap{\tenbf\folio\kern1pc}% folio to left of text
+ \def\MF{{\manual 89:;<=>:}}% slanted 10pt
+ \tenit\rhead\hfil% running head flush left
+ }}
+\def\rightheadline{\hbox to \pagewidth{%
+ \vbox to 10pt{}% strut to position the baseline
+ \def\MF{{\manual 89:;<=>:}}% slanted 10pt
+ \hfil\tenit\rhead\/% running head flush right
+ \rlap{\kern1pc\tenbf\folio}% folio to right of text
+ }}
+\def\ttok#1{\leavevmode\thinspace\hbox{\vrule\vtop{\vbox{\hrule\kern1pt
+ \hbox{\vphantom{\tt(j}\thinspace{\tt#1}\thinspace}}
+ \kern1pt\hrule}\vrule}\thinspace} % token
+
+\newdimen\tinypix \setbox0=\hbox{\sixrm0} \tinypix=5pt
+\newdimen\pixcorr \pixcorr=\tinypix \advance\pixcorr by-\wd0
+\def\pixpat#1#2#3#4{\vcenter{\sixrm\baselineskip=\tinypix
+ \hbox{#1\kern\pixcorr#2}\hbox{#3\kern\pixcorr#4}}}
+
+\font\rand=random
diff --git a/systems/knuth/dist/lib/mftmac.tex b/systems/knuth/dist/lib/mftmac.tex
new file mode 100644
index 0000000000..3243e74d68
--- /dev/null
+++ b/systems/knuth/dist/lib/mftmac.tex
@@ -0,0 +1,55 @@
+% special macros for use with MFT output
+
+\font\tenlogo=logo10 % font used for the METAFONT logo
+\font\tentex=cmtex10 \hyphenchar\tentex=-1 % font used for strings
+\font\sevenit=cmti7 \scriptfont\itfam=\sevenit
+\def\MF{{\tenlogo META}\-{\tenlogo FONT}}
+
+\parindent=0pt
+\thinmuskip=5mu
+\thickmuskip=6mu plus 6mu
+\mathcode`\|="326A
+
+\def\\#1{{\it#1}} % italic type for identifiers
+\def\0#1#2#3{\hbox{\rm\'{}\kern-.2em\it#1#2#3\/\kern.05em}} % octal constant
+\def\1#1{\mathop{\hbox{\rm#1}}} % operator, in roman type
+\def\2#1{\mathop{\hbox{\bf#1\/\kern.05em}}} % operator, in bold type
+\def\3#1{\,\mathclose{\hbox{\bf#1\/}}} % `fi' and `endgroup'
+\def\4#1{\mathbin{\hbox{\bf#1\/}}} % `step' and `at'
+\def\5#1{\hbox{\bf#1\/}} % `true' and `nullpicture'
+\def\6#1{\mathbin{\rm#1}} % `++' and `scaled'
+\def\7{\hbox\bgroup\nocats\frenchspacing\finstring} % string token
+\def\8#1{\mathrel{\mathcode`\.="8000 \mathcode`\-="8000
+ #1\unkern}} % `..' and `--'
+\def\9{\hfill$\%} % comment separator
+\def\?#1{\mathopen{#1}\;} % `:', `::', and `||:'
+\def\frac#1/#2{\leavevmode\kern.1em
+ \raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em
+ /\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}
+
+\mathchardef\AM="2026 % ampersand
+\let\BL=\medskip % space for empty line
+\mathchardef\BS="026E % backslash
+\mathchardef\HA="0222 % hat ("005E not as good)
+\def\PS{\mathbin{+{-}+}} % pythagorean subtraction
+\def\SH{\raise.7ex\hbox{$\scriptstyle\#$}} % sharp sign for sharped units
+\mathchardef\TI="007E % tilde
+
+\chardef\other=12
+\def\nocats{\catcode`\\=\other \catcode`\{=\other
+ \catcode`\}=\other \catcode`\$=\other \catcode`\&=\other
+ \catcode`\#=\other \catcode`\%=\other \catcode`\~=\other
+ \catcode`\_=\other \catcode`\^=\other}
+\def\finstring"#1"{\tentex"#1"\egroup}
+
+\newbox\shorthyf \setbox\shorthyf=\hbox{-\kern-.05em}
+\mathchardef\period=`\.
+{\catcode`\-=\active \global\def-{\copy\shorthyf\mkern3.9mu}
+ \catcode`\.=\active \global\def.{\period\mkern3mu}}
+
+\def\bf{\fam\bffam
+ \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}%
+ \tenbf}
+
+\def\join#1${} % say %%\join in .mf file to join lines together
+\def\]{\hskip0pt plus 1filll\ } % say % comment\] to get comment flush left
diff --git a/systems/knuth/dist/lib/null.mf b/systems/knuth/dist/lib/null.mf
new file mode 100644
index 0000000000..9828fbb16f
--- /dev/null
+++ b/systems/knuth/dist/lib/null.mf
@@ -0,0 +1,3 @@
+% This is an empty METAFONT source file. Why do you want it? Because if
+% mf says `Please type another input file name:' and if you don't have one
+% in mind, you can just say `null' and METAFONT will get out of its loop.
diff --git a/systems/knuth/dist/lib/null.tex b/systems/knuth/dist/lib/null.tex
new file mode 100644
index 0000000000..b53b0f4ff1
--- /dev/null
+++ b/systems/knuth/dist/lib/null.tex
@@ -0,0 +1,3 @@
+% This is an empty TeX source file. Why do you want it? Because if TeX says
+% `Please type another input file name:' and if you don't have one in mind,
+% you can just say `null' and TeX will get out of its loop.
diff --git a/systems/knuth/dist/lib/plain.mf b/systems/knuth/dist/lib/plain.mf
new file mode 100644
index 0000000000..0a3369afc9
--- /dev/null
+++ b/systems/knuth/dist/lib/plain.mf
@@ -0,0 +1,595 @@
+% This is the plain METAFONT base that's described in The METAFONTbook.
+% N.B.: Please change "base_version" whenever this file is modified!
+% And don't modify the file unless you change its name:
+% Everybody's "plain.mf" file should be the same, worldwide.
+
+% Unlimited copying and redistribution of this file are permitted as long
+% as this file is not modified. Modifications are permitted, but only if
+% the resulting file is not named plain.mf.
+
+string base_name, base_version; base_name="plain"; base_version="2.71";
+
+message "Preloading the plain base, version "&base_version&": preliminaries,";
+
+delimiters (); % this makes parentheses behave like parentheses
+def upto = step 1 until enddef; % syntactic sugar
+def downto = step -1 until enddef;
+def exitunless expr c = exitif not c enddef;
+let relax = \; % ignore the word `relax', as in TeX
+let \\ = \; % double relaxation is like single
+def ]] = ] ] enddef; % right brackets should be loners
+def -- = {curl 1}..{curl 1} enddef;
+def --- = .. tension infinity .. enddef;
+def ... = .. tension atleast 1 .. enddef;
+
+def gobble primary g = enddef; def killtext text t = enddef;
+primarydef g gobbled gg = enddef;
+def hide(text t) = exitif numeric begingroup t;endgroup; enddef;
+def ??? = hide(interim showstopping:=1; showdependencies) enddef;
+def stop expr s = message s; gobble readstring enddef;
+
+smoothing:=1; autorounding:=2; % this adjusts curves to the raster
+turningcheck:=2; % this will warn about a "strange path"
+granularity:=1; % this says that pixels are pixels
+
+def interact = % sets up to make "show" commands stop
+ hide(showstopping:=1; tracingonline:=1) enddef;
+
+def loggingall = % puts tracing info into the log
+ tracingcommands:=3; tracingedges:=2; tracingtitles:=1; tracingequations:=1;
+ tracingcapsules:=1; tracingspecs:=1; tracingpens:=1; tracingchoices:=1;
+ tracingstats:=1; tracingoutput:=1; tracingmacros:=1; tracingrestores:=1;
+ enddef;
+
+def tracingall = % turns on every form of tracing
+ tracingonline:=1; showstopping:=1; loggingall enddef;
+
+def tracingnone = % turns off every form of tracing
+ tracingcommands:=0; tracingonline:=0; showstopping:=0;
+ tracingedges:=0; tracingtitles:=0; tracingequations:=0;
+ tracingcapsules:=0; tracingspecs:=0; tracingpens:=0; tracingchoices:=0;
+ tracingstats:=0; tracingoutput:=0; tracingmacros:=0; tracingrestores:=0;
+ enddef;
+
+message " basic constants and mathematical macros,";
+
+% numeric constants
+newinternal eps,epsilon,infinity,_;
+eps := .00049; % this is a pretty small positive number
+epsilon := 1/256/256; % but this is the smallest
+infinity := 4095.99998; % and this is the largest
+_ := -1; % internal constant to make macros unreadable but shorter
+
+% pair constants
+pair right,left,up,down,origin;
+origin=(0,0); up=-down=(0,1); right=-left=(1,0);
+
+% path constants
+path quartercircle,halfcircle,fullcircle,unitsquare;
+quartercircle=(right{up}..(right+up)/sqrt2..up{left}) scaled .5;
+halfcircle=quartercircle & quartercircle rotated 90;
+fullcircle=halfcircle & halfcircle rotated 180 & cycle;
+unitsquare=(0,0)--(1,0)--(1,1)--(0,1)--cycle;
+
+% transform constants
+transform identity;
+for z=origin,right,up: z transformed identity = z; endfor
+
+% picture constants
+picture blankpicture,unitpixel;
+blankpicture=nullpicture; % `display blankpicture...'
+unitpixel=nullpicture; addto unitpixel contour unitsquare;
+
+% string constants
+string ditto; ditto = char 34; % ASCII double-quote mark
+
+% pen constants
+def capsule_def(suffix s) primary u = def s = u enddef enddef;
+capsule_def(pensquare) makepen(unitsquare shifted -(.5,.5));
+capsule_def(penrazor) makepen((-.5,0)--(.5,0)--cycle);
+pen penspeck; penspeck=pensquare scaled eps;
+
+% nullary operators
+vardef whatever = save ?; ? enddef;
+
+% unary operators
+let abs = length;
+
+vardef round primary u =
+ if numeric u: floor(u+.5)
+ elseif pair u: (hround xpart u, vround ypart u)
+ else: u fi enddef;
+
+vardef hround primary x = floor(x+.5) enddef;
+vardef vround primary y = floor(y.o_+.5)_o_ enddef;
+
+vardef ceiling primary x = -floor(-x) enddef;
+
+vardef byte primary s =
+ if string s: ASCII fi s enddef;
+
+vardef dir primary d = right rotated d enddef;
+
+vardef unitvector primary z = z/abs z enddef;
+
+vardef inverse primary T =
+ transform T_; T_ transformed T = identity; T_ enddef;
+
+vardef counterclockwise primary c =
+ if turningcheck>0:
+ interim autorounding:=0;
+ if turningnumber c <= 0: reverse fi fi c enddef;
+
+vardef tensepath expr r =
+ for k=0 upto length r - 1: point k of r --- endfor
+ if cycle r: cycle else: point infinity of r fi enddef;
+
+% binary operators
+
+primarydef x mod y = (x-y*floor(x/y)) enddef;
+primarydef x div y = floor(x/y) enddef;
+primarydef w dotprod z = (xpart w * xpart z + ypart w * ypart z) enddef;
+
+primarydef x**y = if y=2: x*x else: takepower y of x fi enddef;
+def takepower expr y of x =
+ if x>0: mexp(y*mlog x)
+ elseif (x=0) and (y>0): 0
+ else: 1
+ if y=floor y:
+ if y>=0: for n=1 upto y: *x endfor
+ else: for n=_ downto y: /x endfor
+ fi
+ else: hide(errmessage "Undefined power: " & decimal x&"**"&decimal y)
+ fi fi enddef;
+
+vardef direction expr t of p =
+ postcontrol t of p - precontrol t of p enddef;
+
+vardef directionpoint expr z of p =
+ a_:=directiontime z of p;
+ if a_<0: errmessage("The direction doesn't occur"); fi
+ point a_ of p enddef;
+
+secondarydef p intersectionpoint q =
+ begingroup save x_,y_; (x_,y_)=p intersectiontimes q;
+ if x_<0: errmessage("The paths don't intersect"); origin
+ else: .5[point x_ of p, point y_ of q] fi endgroup
+enddef;
+
+tertiarydef p softjoin q =
+ begingroup c_:=fullcircle scaled 2join_radius shifted point 0 of q;
+ a_:=ypart(c_ intersectiontimes p); b_:=ypart(c_ intersectiontimes q);
+ if a_<0:point 0 of p{direction 0 of p} else: subpath(0,a_) of p fi
+ ... if b_<0:{direction infinity of q}point infinity of q
+ else: subpath(b_,infinity) of q fi endgroup enddef;
+newinternal join_radius,a_,b_; path c_;
+
+% special operators
+vardef incr suffix $ = $:=$+1; $ enddef;
+vardef decr suffix $ = $:=$-1; $ enddef;
+
+def reflectedabout(expr w,z) = % reflects about the line w..z
+ transformed
+ begingroup transform T_;
+ w transformed T_ = w; z transformed T_ = z;
+ xxpart T_ = -yypart T_; xypart T_ = yxpart T_; % T_ is a reflection
+ T_ endgroup enddef;
+
+def rotatedaround(expr z, d) = % rotates d degrees around z
+ shifted -z rotated d shifted z enddef;
+let rotatedabout = rotatedaround; % for roundabout people
+
+vardef min(expr u)(text t) = % t is a list of numerics, pairs, or strings
+ save u_; setu_ u; for uu = t: if uu<u_: u_:=uu; fi endfor
+ u_ enddef;
+
+vardef max(expr u)(text t) = % t is a list of numerics, pairs, or strings
+ save u_; setu_ u; for uu = t: if uu>u_: u_:=uu; fi endfor
+ u_ enddef;
+
+def setu_ primary u =
+ if pair u: pair u_ elseif string u: string u_ fi;
+ u_=u enddef;
+
+def flex(text t) = % t is a list of pairs
+ hide(n_:=0; for z=t: z_[incr n_]:=z; endfor
+ dz_:=z_[n_]-z_1)
+ z_1 for k=2 upto n_-1: ...z_[k]{dz_} endfor ...z_[n_] enddef;
+newinternal n_; pair z_[],dz_;
+
+def superellipse(expr r,t,l,b,s)=
+ r{up}...(s[xpart t,xpart r],s[ypart r,ypart t]){t-r}...
+ t{left}...(s[xpart t,xpart l],s[ypart l,ypart t]){l-t}...
+ l{down}...(s[xpart b,xpart l],s[ypart l,ypart b]){b-l}...
+ b{right}...(s[xpart b,xpart r],s[ypart r,ypart b]){r-b}...cycle enddef;
+
+vardef interpath(expr a,p,q) =
+ for t=0 upto length p-1: a[point t of p, point t of q]
+ ..controls a[postcontrol t of p, postcontrol t of q]
+ and a[precontrol t+1 of p, precontrol t+1 of q] .. endfor
+ if cycle p: cycle
+ else: a[point infinity of p, point infinity of q] fi enddef;
+
+vardef solve@#(expr true_x,false_x)= % @#(true_x)=true, @#(false_x)=false
+ tx_:=true_x; fx_:=false_x;
+ forever: x_:=.5[tx_,fx_]; exitif abs(tx_-fx_)<=tolerance;
+ if @#(x_): tx_ else: fx_ fi :=x_; endfor
+ x_ enddef; % now x_ is near where @# changes from true to false
+newinternal tolerance, tx_,fx_,x_; tolerance:=.1;
+
+message " macros for converting from device-independent units to pixels,";
+
+def fix_units = % define the conversion factors, given pixels_per_inch
+ mm:=pixels_per_inch/25.4; cm:=pixels_per_inch/2.54;
+ pt:=pixels_per_inch/72.27; pc:=pixels_per_inch/6.0225;
+ dd:=1238/1157pt; cc:=12dd;
+ bp:=pixels_per_inch/72; in:=pixels_per_inch;
+ hppp:=pt; % horizontal pixels per point
+ vppp:=aspect_ratio*hppp; % vertical pixels per point
+ enddef;
+
+mm#=2.84528; pt#=1; dd#=1.07001; bp#=1.00375;
+cm#=28.45276; pc#=12; cc#=12.84010; in#=72.27;
+
+newinternal pixels_per_inch; % the given resolution
+newinternal blacker, o_correction; % device-oriented corrections
+
+def define_pixels(text t) =
+ forsuffixes $=t: $:=$.#*hppp; endfor enddef;
+def define_whole_pixels(text t) =
+ forsuffixes $=t: $:=hround($.#*hppp); endfor enddef;
+def define_whole_vertical_pixels(text t) =
+ forsuffixes $=t: $:=vround($.#*hppp); endfor enddef;
+def define_good_x_pixels(text t) =
+ forsuffixes $=t: $:=good.x($.#*hppp); endfor enddef;
+def define_good_y_pixels(text t) =
+ forsuffixes $=t: $:=good.y($.#*hppp); endfor enddef;
+def define_blacker_pixels(text t) =
+ forsuffixes $=t: $:=$.#*hppp+blacker; endfor enddef;
+def define_whole_blacker_pixels(text t) =
+ forsuffixes $=t: $:=hround($.#*hppp+blacker);
+ if $<=0: $:=1; fi endfor enddef;
+def define_whole_vertical_blacker_pixels(text t) =
+ forsuffixes $=t: $:=vround($.#*hppp+blacker);
+ if $<=0: $:=1_o_; fi endfor enddef;
+def define_corrected_pixels(text t) =
+ forsuffixes $=t: $:=vround($.#*hppp*o_correction)+eps; endfor enddef;
+def define_horizontal_corrected_pixels(text t) =
+ forsuffixes $=t: $:=hround($.#*hppp*o_correction)+eps; endfor enddef;
+
+def lowres_fix(text t) expr ratio =
+ begingroup save min,max,first;
+ forsuffixes $=t: if unknown min: min=max=first=$; min#=max#=$.#;
+ elseif $.#<min#: min:=$; min#:=$.#;
+ elseif $.#>max#: max:=$; max#:=$.#; fi endfor
+ if max/min>ratio*max#/min#: forsuffixes $=t: $:=first; endfor fi
+ endgroup enddef;
+
+message " macros and tables for various modes of operation,";
+
+def mode_setup =
+ warningcheck:=0;
+ if unknown mode: mode=proof; fi
+ numeric aspect_ratio; transform currenttransform;
+ scantokens if string mode:("input "&mode) else: mode_name[mode] fi;
+ if unknown mag: mag=1; fi
+ if unknown aspect_ratio: aspect_ratio=1; fi
+ displaying:=proofing;
+ pixels_per_inch:=pixels_per_inch*mag;
+ if aspect_ratio=1: let o_=\; let _o_=\
+ else: def o_=*aspect_ratio enddef; def _o_=/aspect_ratio enddef fi;
+ fix_units;
+ scantokens extra_setup; % the user's special last-minute adjustments
+ currenttransform:=
+ if unknown currenttransform: identity else: currenttransform fi
+ yscaled aspect_ratio;
+ clearit;
+ pickup pencircle scaled (.4pt+blacker);
+ warningcheck:=1; enddef;
+def smode = string mode; mode enddef;
+string extra_setup, mode_name[];
+extra_setup=""; % usually there's nothing special to do
+newinternal displaying; % if positive, endchar will `showit'
+
+vardef magstep primary m = mexp(46.67432m) enddef;
+
+def mode_def suffix $ =
+ $:=incr number_of_modes;
+ mode_name[$]:=str$ & "_";
+ expandafter quote def scantokens mode_name[$] enddef;
+newinternal number_of_modes;
+
+% proof mode: for initial design of characters
+mode_def proof =
+ proofing:=2; % yes, we're making full proofs
+ fontmaking:=0; % no, we're not making a font
+ tracingtitles:=1; % yes, show titles online
+ pixels_per_inch:=2601.72; % that's 36 pixels per pt
+ blacker:=0; % no additional blackness
+ fillin:=0; % no compensation for fillin
+ o_correction:=1; % no reduction in overshoot
+ enddef;
+
+% smoke mode: for label-free proofs to mount on the wall
+mode_def smoke =
+ proof_; % same as proof mode, except:
+ proofing:=1; % yes, we're making unlabeled proofs
+ extra_setup:=extra_setup&"grayfont black"; % with solid black pixels
+ let makebox=maketicks; % make the boxes less obtrusive
+ enddef;
+
+% lowres mode: for certain devices that print 200 pixels per inch
+mode_def lowres =
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles at all
+ pixels_per_inch:=200; % that's the meaning of lowres
+ blacker:=.65; % make pens a bit blacker
+ fillin:=.2; % compensate for diagonal fillin
+ o_correction:=.4; % but don't overshoot as much
+ enddef;
+
+localfont:=lowres; % the mode most commonly used to make fonts
+
+% It is customary to input another file to supplement the PLAIN base.
+% This supplementary file adds analogous modes, corresponding to
+% local output devices, and it redefines `localfont' as appropriate.
+% The values of screen_rows and screen_cols should also be updated.
+% The auxiliary file should set base_version:=base_version&"/localname".
+% Remember that the present file PLAIN.MF should not be changed;
+% all local changes should be confined to a separate file.
+
+message " macros for drawing and filling,";
+
+pen currentpen;
+path currentpen_path;
+picture currentpicture;
+transform currenttransform;
+def t_ = transformed currenttransform enddef;
+
+def fill expr c = addto_currentpicture contour c.t_ enddef;
+def addto_currentpicture = addto currentpicture enddef;
+def draw expr p =
+ addto_currentpicture doublepath p.t_ withpen currentpen enddef;
+def filldraw expr c = fill counterclockwise c withpen currentpen enddef;
+def drawdot expr z = if unknown currentpen_path: def_pen_path_ fi
+ addto_currentpicture contour
+ currentpen_path shifted (z.t_) withpen penspeck enddef;
+def def_pen_path_ =
+ hide(currentpen_path=tensepath makepath currentpen) enddef;
+
+def unfill expr c = fill c withweight _ enddef;
+def undraw expr p = draw p withweight _ enddef;
+def unfilldraw expr c = filldraw c withweight _ enddef;
+def undrawdot expr z = drawdot z withweight _ enddef;
+def erase text t = begingroup interim default_wt_:=_;
+ cullit; t withweight _; cullit; endgroup enddef;
+newinternal default_wt_; default_wt_:=1;
+
+def cutdraw expr p = % caution: you may need autorounding=0
+ cutoff(point 0 of p, 180+angle direction 0 of p);
+ cutoff(point infinity of p, angle direction infinity of p);
+ culldraw p enddef;
+def culldraw expr p = addto pic_ doublepath p.t_ withpen currentpen;
+ cull pic_ dropping(-infinity,0) withweight default_wt_;
+ addto_currentpicture also pic_; pic_:=nullpicture; killtext enddef;
+vardef cutoff(expr z,theta) =
+ interim autorounding := 0; interim smoothing := 0;
+ addto pic_ doublepath z.t_ withpen currentpen;
+ addto pic_ contour
+ (cut_ scaled (1+max(-pen_lft,pen_rt,pen_top,-pen_bot))
+ rotated theta shifted z)t_;
+ cull pic_ keeping (2,2) withweight -default_wt_;
+ addto currentpicture also pic_;
+ pic_:=nullpicture enddef;
+picture pic_; pic_:=nullpicture;
+path cut_; cut_ = ((0,_)--(1,_)--(1,1)--(0,1)--cycle) scaled 1.42;
+
+def pickup secondary q =
+ if numeric q: numeric_pickup_ else: pen_pickup_ fi q enddef;
+def numeric_pickup_ primary q =
+ if unknown pen_[q]: errmessage "Unknown pen"; clearpen
+ else: currentpen:=pen_[q];
+ pen_lft:=pen_lft_[q];
+ pen_rt:=pen_rt_[q];
+ pen_top:=pen_top_[q];
+ pen_bot:=pen_bot_[q];
+ currentpen_path:=pen_path_[q] fi; enddef;
+def pen_pickup_ primary q =
+ currentpen:=q yscaled aspect_ratio;
+ pen_lft:=xpart penoffset down of currentpen;
+ pen_rt:=xpart penoffset up of currentpen;
+ pen_top:=(ypart penoffset left of currentpen)_o_;
+ pen_bot:=(ypart penoffset right of currentpen)_o_;
+ path currentpen_path; enddef;
+newinternal pen_lft,pen_rt,pen_top,pen_bot,pen_count_;
+
+vardef savepen = pen_[incr pen_count_]=currentpen;
+ pen_lft_[pen_count_]=pen_lft;
+ pen_rt_[pen_count_]=pen_rt;
+ pen_top_[pen_count_]=pen_top;
+ pen_bot_[pen_count_]=pen_bot;
+ pen_path_[pen_count_]=currentpen_path;
+ pen_count_ enddef;
+
+def clearpen = currentpen:=nullpen;
+ pen_lft:=pen_rt:=pen_top:=pen_bot:=0;
+ path currentpen_path;
+ enddef;
+def clear_pen_memory =
+ pen_count_:=0;
+ numeric pen_lft_[],pen_rt_[],pen_top_[],pen_bot_[];
+ pen currentpen,pen_[];
+ path currentpen_path, pen_path_[];
+ enddef;
+
+vardef lft primary x = x + if pair x: (pen_lft,0) else: pen_lft fi enddef;
+vardef rt primary x = x + if pair x: (pen_rt,0) else: pen_rt fi enddef;
+vardef top primary y = y + if pair y: (0,pen_top) else: pen_top fi enddef;
+vardef bot primary y = y + if pair y: (0,pen_bot) else: pen_bot fi enddef;
+vardef good.x primary x = hround(x+pen_lft)-pen_lft enddef;
+vardef good.y primary y = vround(y+pen_top)-pen_top enddef;
+vardef good.lft primary z = save z_; pair z_;
+ (z_+(pen_lft,0))t_=round((z+(pen_lft,0))t_); z_ enddef;
+vardef good.rt primary z = save z_; pair z_;
+ (z_+(pen_rt,0))t_=round((z+(pen_rt,0))t_); z_ enddef;
+vardef good.top primary z = save z_; pair z_;
+ (z_+(0,pen_top))t_=round((z+(0,pen_top))t_); z_ enddef;
+vardef good.bot primary z = save z_; pair z_;
+ (z_+(0,pen_bot))t_=round((z+(0,pen_bot))t_); z_ enddef;
+
+vardef penpos@#(expr b,d) =
+ (x@#r-x@#l,y@#r-y@#l)=(b,0) rotated d;
+ x@#=.5(x@#l+x@#r); y@#=.5(y@#l+y@#r) enddef;
+
+def penstroke text t =
+ forsuffixes e = l,r: path_.e:=t; endfor
+ if cycle path_.l: cyclestroke_
+ else: fill path_.l -- reverse path_.r -- cycle fi enddef;
+def cyclestroke_ =
+ begingroup interim turningcheck:=0;
+ addto pic_ contour path_.l.t_ withweight 1;
+ addto pic_ contour path_.r.t_ withweight _;
+ cull pic_ dropping origin withweight default_wt_;
+ addto_currentpicture also pic_;
+ pic_:=nullpicture endgroup enddef;
+path path_.l,path_.r;
+
+message " macros for proof labels and rules,";
+
+vardef makelabel@#(expr s,z) = % puts string s at point z
+ if known z: special lcode_@# & s;
+ numspecial xpart(z.t_); numspecial ypart(z.t_) fi enddef;
+string lcode_,lcode_.top,lcode_.lft,lcode_.rt,lcode_.bot,
+ lcode_.top.nodot,lcode_.lft.nodot,lcode_.rt.nodot,lcode_.bot.nodot;
+lcode_.top=" 1"; lcode_.lft=" 2"; lcode_.rt=" 3"; lcode_.bot=" 4";
+lcode_=" 0"; % change to " /" to avoid listing in overflow column
+lcode_.top.nodot=" 5"; lcode_.lft.nodot=" 6";
+lcode_.rt.nodot=" 7"; lcode_.bot.nodot=" 8";
+
+vardef labels@#(text t) =
+ if proofing>1: forsuffixes $=t:
+ makelabel@#(str$,z$); endfor
+ fi enddef;
+vardef penlabels@#(text t) =
+ if proofing>1: forsuffixes $$=l,,r: forsuffixes $=t:
+ makelabel@#(str$.$$,z$.$$); endfor endfor
+ fi enddef;
+
+def range expr x = numtok[x] enddef;
+def numtok suffix x=x enddef;
+tertiarydef m thru n =
+ m for x=m+1 step 1 until n: , numtok[x] endfor enddef;
+
+def proofrule(expr w,z) =
+ special "rule"; numspecial xpart w; numspecial ypart w;
+ numspecial xpart z; numspecial ypart z enddef;
+def screenrule(expr w,z) =
+ addto currentpicture doublepath w--z withpen rulepen enddef;
+pen rulepen; rulepen = pensquare scaled 2;
+
+def makegrid(text xlist,ylist) =
+ xmin_ := min(xlist); xmax_ := max(xlist);
+ ymin_ := min(ylist); ymax_ := max(ylist);
+ for x=xlist: proofrule((x,ymin_), (x,ymax_)); endfor
+ for y=ylist: proofrule((xmin_,y), (xmax_,y)); endfor
+ enddef;
+
+vardef titlefont suffix $ = special "titlefont "&str$ enddef;
+vardef labelfont suffix $ = special "labelfont "&str$ enddef;
+vardef grayfont suffix $ = special "grayfont "&str$ enddef;
+vardef slantfont suffix $ = special "slantfont "&str$ enddef;
+def proofoffset primary z = % shifts proof output by z
+ special "offset"; numspecial xpart z; numspecial ypart z enddef;
+vardef proofrulethickness expr x =
+ special "rulethickness"; numspecial x enddef;
+
+message " macros for character and font administration,";
+
+def beginchar(expr c,w_sharp,h_sharp,d_sharp) =
+ begingroup
+ charcode:=if known c: byte c else: 0 fi;
+ charwd:=w_sharp; charht:=h_sharp; chardp:=d_sharp;
+ w:=hround(charwd*hppp); h:=vround(charht*hppp); d:=vround(chardp*hppp);
+ charic:=0; clearxy; clearit; clearpen; scantokens extra_beginchar;
+ enddef;
+
+def italcorr expr x_sharp = if x_sharp>0: charic:=x_sharp fi enddef;
+
+def change_width =
+ w:=w if w>charwd*hppp:- else:+ fi 1 enddef;
+
+def endchar =
+ scantokens extra_endchar;
+ if proofing>0: makebox(proofrule); fi
+ chardx:=w; % desired width of the character in pixels
+ shipit;
+ if displaying>0: makebox(screenrule); showit; fi
+ endgroup enddef;
+
+string extra_beginchar, extra_endchar;
+extra_beginchar=extra_endchar="";
+
+def makebox(text r) =
+ for y=0,h.o_,-d.o_: r((0,y),(w,y)); endfor % horizontals
+ for x=0,w: r((x,-d.o_),(x,h.o_)); endfor % verticals
+ if charic<>0: r((w+charic*hppp,h.o_),(w+charic*hppp,.5h.o_)); fi
+ enddef;
+
+def maketicks(text r) =
+ for y=0,h.o_,-d.o_: r((0,y),(10,y)); r((w-10,y),(w,y)); endfor
+ for x=0,w: r((x,10-d.o_),(x,-d.o_)); r((x,h.o_-10),(x,h.o_)); endfor
+ if charic<>0: r((w+charic*hppp,h.o_-10),(w+charic*hppp,h.o_)); fi
+ enddef;
+
+def font_size expr x = designsize:=x enddef;
+def font_slant expr x = fontdimen 1: x enddef;
+def font_normal_space expr x = fontdimen 2: x enddef;
+def font_normal_stretch expr x = fontdimen 3: x enddef;
+def font_normal_shrink expr x = fontdimen 4: x enddef;
+def font_x_height expr x = fontdimen 5: x enddef;
+def font_quad expr x = fontdimen 6: x enddef;
+def font_extra_space expr x = fontdimen 7: x enddef;
+
+def font_identifier expr x = font_identifier_:=x enddef;
+def font_coding_scheme expr x = font_coding_scheme_:=x enddef;
+string font_identifier_, font_coding_scheme_;
+font_identifier_=font_coding_scheme_="UNSPECIFIED";
+
+message "and a few last-minute items.";
+
+vardef z@#=(x@#,y@#) enddef;
+
+newinternal screen_rows, screen_cols, currentwindow;
+screen_rows:=400; % these values should be corrected,
+screen_cols:=500; % by reading in a separate file after PLAIN.MF
+
+def openit = openwindow currentwindow
+ from origin to (screen_rows,screen_cols) at (-50,300) enddef;
+def showit = openit; let showit=showit_; showit enddef; % first time only
+def showit_ = display currentpicture inwindow currentwindow enddef;
+
+def clearxy = save x,y enddef;
+def clearit = currentpicture:=nullpicture enddef;
+def shipit = shipout currentpicture enddef;
+def cullit = cull currentpicture dropping (-infinity,0) enddef;
+
+def screenchars = % endchar should `showit'
+ extra_endchar:=extra_endchar&"showit;" enddef;
+def screenstrokes = % every stroke should `showit'
+ def addto_currentpicture text t=
+ addto currentpicture t; showit enddef; enddef;
+def imagerules = % a box should be part of the character image
+ extra_beginchar:=extra_beginchar & "makebox(screenrule);" enddef;
+def gfcorners = % `maketicks' should send rules to the GF file
+ extra_setup:=extra_setup & "let makebox=maketicks;proofing:=1;" enddef;
+def nodisplays = % endchar shouldn't `showit'
+ extra_setup:=extra_setup & "displaying:=0;" enddef;
+def notransforms = % currenttransform should not be used
+ let t_ = \ enddef;
+
+let bye = end; outer end,bye;
+
+clear_pen_memory; % initialize the `savepen' mechanism
+mode_setup; % establish proof mode as the default
+numeric mode,mag; % but leave mode and mag undefined
diff --git a/systems/knuth/dist/lib/plain.mft b/systems/knuth/dist/lib/plain.mft
new file mode 100644
index 0000000000..06fa93e89e
--- /dev/null
+++ b/systems/knuth/dist/lib/plain.mft
@@ -0,0 +1,26 @@
+%%%% MFT commands for the PLAIN base
+%%% } ( ) ]] ! ^ %%%% tokens that need no special formatting
+%%% step upto downto %%%% boldface binary operators
+%%% addto fill unfill draw undraw %%%% boldface unary operators
+%%% addto filldraw unfilldraw drawdot undrawdot erase pickup
+%%% addto exitunless stop incr decr proofrulethickness screenrule
+%%% addto define_pixels define_whole_pixels define_whole_vertical_pixels
+%%% addto define_blacker_pixels define_whole_blacker_pixels
+%%% addto define_corrected_pixels lowres_fix proofoffset penstroke
+%%% addto beginchar italcorr font_size font_slant labels
+%%% addto font_normal_space font_normal_stretch font_normal_shrink font_quad
+%%% addto font_x_height font_extra_space font_identifier font_coding_scheme
+%%% enddef endchar %%%% boldface closing
+%%% true relax mode_setup %%%% boldface nullary operators
+%%% true clearit shipit cullit openit showit clearxy clearpen
+%%% true nodisplay notransforms screenchars screenstrokes imagerules
+%%% .. ... -- --- %%%% path operators made of dots and dashes
+%%% length flex abs dir %%%% unary operators to be in roman type
+%%% length unitvector inverse ceiling round hround vround counterclockwise
+%%% length tensepath byte reflectedabout rotatedaround magstep max min
+%%% and mod dotprod intersectionpoint softjoin %%%% binary operators to be roman
+%%% ++ ** %%%% binary operators made of two special characters
+%%% penoffset goodval direction directionpoint %%%% operators that take "of"
+%%% pausing tolerance pixels_per_inch blacker o_correction %%%% internals
+%%% pausing screen_rows screen_cols currentwindow displaying
+%%% pausing pen_top pen_bot pen_lft pen_rt rt lft top bot
diff --git a/systems/knuth/dist/lib/plain.tex b/systems/knuth/dist/lib/plain.tex
new file mode 100644
index 0000000000..2b72027dff
--- /dev/null
+++ b/systems/knuth/dist/lib/plain.tex
@@ -0,0 +1,1241 @@
+% This is the plain TeX format that's described in The TeXbook.
+% N.B.: A version number is defined at the very end of this file;
+% please change that number whenever the file is modified!
+% And don't modify the file unless you change its name:
+% Everybody's "plain.tex" file should be the same, worldwide.
+
+% Unlimited copying and redistribution of this file are permitted as long
+% as this file is not modified. Modifications are permitted, but only if
+% the resulting file is not named plain.tex.
+
+\catcode`\{=1 % left brace is begin-group character
+\catcode`\}=2 % right brace is end-group character
+\catcode`\$=3 % dollar sign is math shift
+\catcode`\&=4 % ampersand is alignment tab
+\catcode`\#=6 % hash mark is macro parameter character
+\catcode`\^=7 \catcode`\^^K=7 % circumflex and uparrow are for superscripts
+\catcode`\_=8 \catcode`\^^A=8 % underline and downarrow are for subscripts
+\catcode`\^^I=10 % ascii tab is a blank space
+\chardef\active=13 \catcode`\~=\active % tilde is active
+\catcode`\^^L=\active \outer\def^^L{\par} % ascii form-feed is "\outer\par"
+
+\message{Preloading the plain format: codes,}
+
+% We had to define the \catcodes right away, before the message line,
+% since \message uses the { and } characters.
+% When INITEX (the TeX initializer) starts up,
+% it has defined the following \catcode values:
+% \catcode`\^^@=9 % ascii null is ignored
+% \catcode`\^^M=5 % ascii return is end-line
+% \catcode`\\=0 % backslash is TeX escape character
+% \catcode`\%=14 % percent sign is comment character
+% \catcode`\ =10 % ascii space is blank space
+% \catcode`\^^?=15 % ascii delete is invalid
+% \catcode`\A=11 ... \catcode`\Z=11 % uppercase letters
+% \catcode`\a=11 ... \catcode`\z=11 % lowercase letters
+% all others are type 12 (other)
+
+% Here is a list of the characters that have been specially catcoded:
+\def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&%
+ \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~}
+% (not counting ascii null, tab, linefeed, formfeed, return, delete)
+% Each symbol in the list is preceded by \do, which can be defined
+% if you want to do something to every item in the list.
+
+% We make @ signs act like letters, temporarily, to avoid conflict
+% between user names and internal control sequences of plain format.
+\catcode`@=11
+
+% INITEX sets up \mathcode x=x, for x=0..255, except that
+% \mathcode x=x+"7100, for x = `A to `Z and `a to `z;
+% \mathcode x=x+"7000, for x = `0 to `9.
+% The following changes define internal codes as recommended
+% in Appendix C of The TeXbook:
+\mathcode`\^^@="2201 % \cdot
+\mathcode`\^^A="3223 % \downarrow
+\mathcode`\^^B="010B % \alpha
+\mathcode`\^^C="010C % \beta
+\mathcode`\^^D="225E % \land
+\mathcode`\^^E="023A % \lnot
+\mathcode`\^^F="3232 % \in
+\mathcode`\^^G="0119 % \pi
+\mathcode`\^^H="0115 % \lambda
+\mathcode`\^^I="010D % \gamma
+\mathcode`\^^J="010E % \delta
+\mathcode`\^^K="3222 % \uparrow
+\mathcode`\^^L="2206 % \pm
+\mathcode`\^^M="2208 % \oplus
+\mathcode`\^^N="0231 % \infty
+\mathcode`\^^O="0140 % \partial
+\mathcode`\^^P="321A % \subset
+\mathcode`\^^Q="321B % \supset
+\mathcode`\^^R="225C % \cap
+\mathcode`\^^S="225B % \cup
+\mathcode`\^^T="0238 % \forall
+\mathcode`\^^U="0239 % \exists
+\mathcode`\^^V="220A % \otimes
+\mathcode`\^^W="3224 % \leftrightarrow
+\mathcode`\^^X="3220 % \leftarrow
+\mathcode`\^^Y="3221 % \rightarrow
+\mathcode`\^^Z="8000 % \ne
+\mathcode`\^^[="2205 % \diamond
+\mathcode`\^^\="3214 % \le
+\mathcode`\^^]="3215 % \ge
+\mathcode`\^^^="3211 % \equiv
+\mathcode`\^^_="225F % \lor
+\mathcode`\ ="8000 % \space
+\mathcode`\!="5021
+\mathcode`\'="8000 % ^\prime
+\mathcode`\(="4028
+\mathcode`\)="5029
+\mathcode`\*="2203 % \ast
+\mathcode`\+="202B
+\mathcode`\,="613B
+\mathcode`\-="2200
+\mathcode`\.="013A
+\mathcode`\/="013D
+\mathcode`\:="303A
+\mathcode`\;="603B
+\mathcode`\<="313C
+\mathcode`\=="303D
+\mathcode`\>="313E
+\mathcode`\?="503F
+\mathcode`\[="405B
+\mathcode`\\="026E % \backslash
+\mathcode`\]="505D
+\mathcode`\_="8000 % \_
+\mathcode`\{="4266
+\mathcode`\|="026A
+\mathcode`\}="5267
+\mathcode`\^^?="1273 % \smallint
+
+% INITEX sets \uccode`x=`X and \uccode `X=`X for all letters x,
+% and \lccode`x=`x, \lccode`X=`x; all other values are zero.
+% No changes to those tables are needed in plain TeX format.
+
+% INITEX sets \sfcode x=1000 for all x, except that \sfcode`X=999
+% for uppercase letters. The following changes are needed:
+\sfcode`\)=0 \sfcode`\'=0 \sfcode`\]=0
+% The \nonfrenchspacing macro will make further changes to \sfcode values.
+
+% Finally, INITEX sets all \delcode values to -1, except \delcode`.=0
+\delcode`\(="028300
+\delcode`\)="029301
+\delcode`\[="05B302
+\delcode`\]="05D303
+\delcode`\<="26830A
+\delcode`\>="26930B
+\delcode`\/="02F30E
+\delcode`\|="26A30C
+\delcode`\\="26E30F
+% N.B. { and } should NOT get delcodes; otherwise parameter grouping fails!
+
+% To make the plain macros more efficient in time and space,
+% several constant values are declared here as control sequences.
+% If they were changed, anything could happen; so they are private symbols.
+\chardef\@ne=1
+\chardef\tw@=2
+\chardef\thr@@=3
+\chardef\sixt@@n=16
+\chardef\@cclv=255
+\mathchardef\@cclvi=256
+\mathchardef\@m=1000
+\mathchardef\@M=10000
+\mathchardef\@MM=20000
+
+% Allocation of registers
+
+% Here are macros for the automatic allocation of \count, \box, \dimen,
+% \skip, \muskip, and \toks registers, as well as \read and \write
+% stream numbers, \fam codes, \language codes, and \insert numbers.
+
+\message{registers,}
+
+% When a register is used only temporarily, it need not be allocated;
+% grouping can be used, making the value previously in the register return
+% after the close of the group. The main use of these macros is for
+% registers that are defined by one macro and used by others, possibly at
+% different nesting levels. All such registers should be defined through
+% these macros; otherwise conflicts may occur, especially when two or more
+% macro packages are being used at once.
+
+% The following counters are reserved:
+% 0 to 9 page numbering
+% 10 count allocation
+% 11 dimen allocation
+% 12 skip allocation
+% 13 muskip allocation
+% 14 box allocation
+% 15 toks allocation
+% 16 read file allocation
+% 17 write file allocation
+% 18 math family allocation
+% 19 language allocation
+% 20 insert allocation
+% 21 the most recently allocated number
+% 22 constant -1
+% New counters are allocated starting with 23, 24, etc. Other registers are
+% allocated starting with 10. This leaves 0 through 9 for the user to play
+% with safely, except that counts 0 to 9 are considered to be the page and
+% subpage numbers (since they are displayed during output). In this scheme,
+% \count 10 always contains the number of the highest-numbered counter that
+% has been allocated, \count 14 the highest-numbered box, etc.
+% Inserts are given numbers 254, 253, etc., since they require a \count,
+% \dimen, \skip, and \box all with the same number; \count 20 contains the
+% lowest-numbered insert that has been allocated. Of course, \box255 is
+% reserved for \output; \count255, \dimen255, and \skip255 can be used freely.
+
+% It is recommended that macro designers always use
+% \global assignments with respect to registers numbered 1, 3, 5, 7, 9, and
+% always non-\global assignments with respect to registers 0, 2, 4, 6, 8, 255.
+% This will prevent ``save stack buildup'' that might otherwise occur.
+
+\count10=22 % allocates \count registers 23, 24, ...
+\count11=9 % allocates \dimen registers 10, 11, ...
+\count12=9 % allocates \skip registers 10, 11, ...
+\count13=9 % allocates \muskip registers 10, 11, ...
+\count14=9 % allocates \box registers 10, 11, ...
+\count15=9 % allocates \toks registers 10, 11, ...
+\count16=-1 % allocates input streams 0, 1, ...
+\count17=-1 % allocates output streams 0, 1, ...
+\count18=3 % allocates math families 4, 5, ...
+\count19=0 % allocates \language codes 1, 2, ...
+\count20=255 % allocates insertions 254, 253, ...
+\countdef\insc@unt=20 % the insertion counter
+\countdef\allocationnumber=21 % the most recent allocation
+\countdef\m@ne=22 \m@ne=-1 % a handy constant
+\def\wlog{\immediate\write\m@ne} % write on log file (only)
+
+% Here are abbreviations for the names of scratch registers
+% that don't need to be allocated.
+
+\countdef\count@=255
+\dimendef\dimen@=0
+\dimendef\dimen@i=1 % global only
+\dimendef\dimen@ii=2
+\skipdef\skip@=0
+\toksdef\toks@=0
+
+% Now, we define \newcount, \newbox, etc. so that you can say \newcount\foo
+% and \foo will be defined (with \countdef) to be the next counter.
+% To find out which counter \foo is, you can look at \allocationnumber.
+% Since there's no \boxdef command, \chardef is used to define a \newbox,
+% \newinsert, \newfam, and so on.
+
+\outer\def\newcount{\alloc@0\count\countdef\insc@unt}
+\outer\def\newdimen{\alloc@1\dimen\dimendef\insc@unt}
+\outer\def\newskip{\alloc@2\skip\skipdef\insc@unt}
+\outer\def\newmuskip{\alloc@3\muskip\muskipdef\@cclvi}
+\outer\def\newbox{\alloc@4\box\chardef\insc@unt}
+\let\newtoks=\relax % we do this to allow plain.tex to be read in twice
+\outer\def\newhelp#1#2{\newtoks#1#1\expandafter{\csname#2\endcsname}}
+\outer\def\newtoks{\alloc@5\toks\toksdef\@cclvi}
+\outer\def\newread{\alloc@6\read\chardef\sixt@@n}
+\outer\def\newwrite{\alloc@7\write\chardef\sixt@@n}
+\outer\def\newfam{\alloc@8\fam\chardef\sixt@@n}
+\outer\def\newlanguage{\alloc@9\language\chardef\@cclvi}
+\def\alloc@#1#2#3#4#5{\global\advance\count1#1by\@ne
+ \ch@ck#1#4#2% make sure there's still room
+ \allocationnumber=\count1#1%
+ \global#3#5=\allocationnumber
+ \wlog{\string#5=\string#2\the\allocationnumber}}
+\outer\def\newinsert#1{\global\advance\insc@unt by\m@ne
+ \ch@ck0\insc@unt\count
+ \ch@ck1\insc@unt\dimen
+ \ch@ck2\insc@unt\skip
+ \ch@ck4\insc@unt\box
+ \allocationnumber=\insc@unt
+ \global\chardef#1=\allocationnumber
+ \wlog{\string#1=\string\insert\the\allocationnumber}}
+\def\ch@ck#1#2#3{\ifnum\count1#1<#2%
+ \else\errmessage{No room for a new #3}\fi}
+
+% Here are some examples of allocation.
+\newdimen\maxdimen \maxdimen=16383.99999pt % the largest legal <dimen>
+\newskip\hideskip \hideskip=-1000pt plus 1fill % negative but can grow
+\newskip\centering \centering=0pt plus 1000pt minus 1000pt
+\newdimen\p@ \p@=1pt % this saves macro space and time
+\newdimen\z@ \z@=0pt % can be used both for 0pt and 0
+\newskip\z@skip \z@skip=0pt plus0pt minus0pt
+\newbox\voidb@x % permanently void box register
+
+% And here's a different sort of allocation:
+% For example, \newif\iffoo creates \footrue, \foofalse to go with \iffoo.
+\outer\def\newif#1{\count@\escapechar \escapechar\m@ne
+ \expandafter\expandafter\expandafter
+ \def\@if#1{true}{\let#1=\iftrue}%
+ \expandafter\expandafter\expandafter
+ \def\@if#1{false}{\let#1=\iffalse}%
+ \@if#1{false}\escapechar\count@} % the condition starts out false
+\def\@if#1#2{\csname\expandafter\if@\string#1#2\endcsname}
+{\uccode`1=`i \uccode`2=`f \uppercase{\gdef\if@12{}}} % `if' is required
+
+% Assign initial values to TeX's parameters
+
+\message{parameters,}
+
+% All of TeX's numeric parameters are listed here,
+% but the code is commented out if no special value needs to be set.
+% INITEX makes all parameters zero except where noted.
+
+\pretolerance=100
+\tolerance=200 % INITEX sets this to 10000
+\hbadness=1000
+\vbadness=1000
+\linepenalty=10
+\hyphenpenalty=50
+\exhyphenpenalty=50
+\binoppenalty=700
+\relpenalty=500
+\clubpenalty=150
+\widowpenalty=150
+\displaywidowpenalty=50
+\brokenpenalty=100
+\predisplaypenalty=10000
+% \postdisplaypenalty=0
+% \interlinepenalty=0
+% \floatingpenalty=0, set during \insert
+% \outputpenalty=0, set before TeX enters \output
+\doublehyphendemerits=10000
+\finalhyphendemerits=5000
+\adjdemerits=10000
+% \looseness=0, cleared by TeX after each paragraph
+% \pausing=0
+% \holdinginserts=0
+% \tracingonline=0
+% \tracingmacros=0
+% \tracingstats=0
+% \tracingparagraphs=0
+% \tracingpages=0
+% \tracingoutput=0
+\tracinglostchars=1
+% \tracingcommands=0
+% \tracingrestores=0
+% \language=0
+\uchyph=1
+% \lefthyphenmin=2 \righthyphenmin=3 set below
+% \globaldefs=0
+% \maxdeadcycles=25 % INITEX does this
+% \hangafter=1 % INITEX does this, also TeX after each paragraph
+% \fam=0
+% \mag=1000 % INITEX does this
+% \escapechar=`\\ % INITEX does this
+\defaulthyphenchar=`\-
+\defaultskewchar=-1
+% \endlinechar=`\^^M % INITEX does this
+\newlinechar=-1
+\delimiterfactor=901
+% \time=now % TeX does this at beginning of job
+% \day=now % TeX does this at beginning of job
+% \month=now % TeX does this at beginning of job
+% \year=now % TeX does this at beginning of job
+\showboxbreadth=5
+\showboxdepth=3
+\errorcontextlines=5
+
+\hfuzz=0.1pt
+\vfuzz=0.1pt
+\overfullrule=5pt
+\hsize=6.5in
+\vsize=8.9in
+\maxdepth=4pt
+\splitmaxdepth=\maxdimen
+\boxmaxdepth=\maxdimen
+% \lineskiplimit=0pt, changed by \normalbaselines
+\delimitershortfall=5pt
+\nulldelimiterspace=1.2pt
+\scriptspace=0.5pt
+% \mathsurround=0pt
+% \predisplaysize=0pt, set before TeX enters $$
+% \displaywidth=0pt, set before TeX enters $$
+% \displayindent=0pt, set before TeX enters $$
+\parindent=20pt
+% \hangindent=0pt, zeroed by TeX after each paragraph
+% \hoffset=0pt
+% \voffset=0pt
+
+% \baselineskip=0pt, changed by \normalbaselines
+% \lineskip=0pt, changed by \normalbaselines
+\parskip=0pt plus 1pt
+\abovedisplayskip=12pt plus 3pt minus 9pt
+\abovedisplayshortskip=0pt plus 3pt
+\belowdisplayskip=12pt plus 3pt minus 9pt
+\belowdisplayshortskip=7pt plus 3pt minus 4pt
+% \leftskip=0pt
+% \rightskip=0pt
+\topskip=10pt
+\splittopskip=10pt
+% \tabskip=0pt
+% \spaceskip=0pt
+% \xspaceskip=0pt
+\parfillskip=0pt plus 1fil
+
+\thinmuskip=3mu
+\medmuskip=4mu plus 2mu minus 4mu
+\thickmuskip=5mu plus 5mu
+
+% We also define special registers that function like parameters:
+\newskip\smallskipamount \smallskipamount=3pt plus 1pt minus 1pt
+\newskip\medskipamount \medskipamount=6pt plus 2pt minus 2pt
+\newskip\bigskipamount \bigskipamount=12pt plus 4pt minus 4pt
+\newskip\normalbaselineskip \normalbaselineskip=12pt
+\newskip\normallineskip \normallineskip=1pt
+\newdimen\normallineskiplimit \normallineskiplimit=0pt
+\newdimen\jot \jot=3pt
+\newcount\interdisplaylinepenalty \interdisplaylinepenalty=100
+\newcount\interfootnotelinepenalty \interfootnotelinepenalty=100
+
+% Definitions for preloaded fonts
+
+\def\magstephalf{1095 }
+\def\magstep#1{\ifcase#1 \@m\or 1200\or 1440\or 1728\or 2074\or 2488\fi\relax}
+
+% Fonts assigned to \preloaded are not part of "plain TeX",
+% but they are preloaded so that other format packages can use them.
+% For example, if another set of macros says "\font\ninerm=cmr9",
+% TeX will not have to reload the font metric information for cmr9.
+
+\message{fonts,}
+
+\font\tenrm=cmr10 % roman text
+\font\preloaded=cmr9
+\font\preloaded=cmr8
+\font\sevenrm=cmr7
+\font\preloaded=cmr6
+\font\fiverm=cmr5
+
+\font\teni=cmmi10 % math italic
+\font\preloaded=cmmi9
+\font\preloaded=cmmi8
+\font\seveni=cmmi7
+\font\preloaded=cmmi6
+\font\fivei=cmmi5
+
+\font\tensy=cmsy10 % math symbols
+\font\preloaded=cmsy9
+\font\preloaded=cmsy8
+\font\sevensy=cmsy7
+\font\preloaded=cmsy6
+\font\fivesy=cmsy5
+
+\font\tenex=cmex10 % math extension
+
+\font\preloaded=cmss10 % sans serif
+\font\preloaded=cmssq8
+
+\font\preloaded=cmssi10 % sans serif italic
+\font\preloaded=cmssqi8
+
+\font\tenbf=cmbx10 % boldface extended
+\font\preloaded=cmbx9
+\font\preloaded=cmbx8
+\font\sevenbf=cmbx7
+\font\preloaded=cmbx6
+\font\fivebf=cmbx5
+
+\font\tentt=cmtt10 % typewriter
+\font\preloaded=cmtt9
+\font\preloaded=cmtt8
+
+\font\preloaded=cmsltt10 % slanted typewriter
+
+\font\tensl=cmsl10 % slanted roman
+\font\preloaded=cmsl9
+\font\preloaded=cmsl8
+
+\font\tenit=cmti10 % text italic
+\font\preloaded=cmti9
+\font\preloaded=cmti8
+\font\preloaded=cmti7
+
+\message{more fonts,}
+\font\preloaded=cmu10 % unslanted text italic
+
+\font\preloaded=cmmib10 % bold math italic
+\font\preloaded=cmbsy10 % bold math symbols
+
+\font\preloaded=cmcsc10 % caps and small caps
+
+\font\preloaded=cmssbx10 % sans serif bold extended
+
+\font\preloaded=cmdunh10 % Dunhill style
+
+\font\preloaded=cmr7 scaled \magstep4 % for titles
+\font\preloaded=cmtt10 scaled \magstep2
+\font\preloaded=cmssbx10 scaled \magstep2
+
+\font\preloaded=manfnt % METAFONT logo and dragon curve and special symbols
+
+% Additional \preloaded fonts can be specified here.
+% (And those that were \preloaded above can be eliminated.)
+
+\let\preloaded=\undefined % preloaded fonts must be declared anew later.
+
+\skewchar\teni='177 \skewchar\seveni='177 \skewchar\fivei='177
+\skewchar\tensy='60 \skewchar\sevensy='60 \skewchar\fivesy='60
+
+\textfont0=\tenrm \scriptfont0=\sevenrm \scriptscriptfont0=\fiverm
+\def\rm{\fam\z@\tenrm}
+\textfont1=\teni \scriptfont1=\seveni \scriptscriptfont1=\fivei
+\def\mit{\fam\@ne} \def\oldstyle{\fam\@ne\teni}
+\textfont2=\tensy \scriptfont2=\sevensy \scriptscriptfont2=\fivesy
+\def\cal{\fam\tw@}
+\textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+\newfam\itfam \def\it{\fam\itfam\tenit} % \it is family 4
+\textfont\itfam=\tenit
+\newfam\slfam \def\sl{\fam\slfam\tensl} % \sl is family 5
+\textfont\slfam=\tensl
+\newfam\bffam \def\bf{\fam\bffam\tenbf} % \bf is family 6
+\textfont\bffam=\tenbf \scriptfont\bffam=\sevenbf
+\scriptscriptfont\bffam=\fivebf
+\newfam\ttfam \def\tt{\fam\ttfam\tentt} % \tt is family 7
+\textfont\ttfam=\tentt
+
+% Macros for setting ordinary text
+\message{macros,}
+
+\def\frenchspacing{\sfcode`\.\@m \sfcode`\?\@m \sfcode`\!\@m
+ \sfcode`\:\@m \sfcode`\;\@m \sfcode`\,\@m}
+\def\nonfrenchspacing{\sfcode`\.3000\sfcode`\?3000\sfcode`\!3000%
+ \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 }
+
+\def\normalbaselines{\lineskip\normallineskip
+ \baselineskip\normalbaselineskip \lineskiplimit\normallineskiplimit}
+
+\def\^^M{\ } % control <return> = control <space>
+\def\^^I{\ } % same for <tab>
+
+\def\lq{`} \def\rq{'}
+\def\lbrack{[} \def\rbrack{]}
+
+\let\endgraf=\par \let\endline=\cr
+
+\def\space{ }
+\def\empty{}
+\def\null{\hbox{}}
+
+\let\bgroup={ \let\egroup=}
+
+% In \obeylines, we say `\let^^M=\par' instead of `\def^^M{\par}'
+% since this allows, for example, `\let\par=\cr \obeylines \halign{...'
+{\catcode`\^^M=\active % these lines must end with %
+ \gdef\obeylines{\catcode`\^^M\active \let^^M\par}%
+ \global\let^^M\par} % this is in case ^^M appears in a \write
+\def\obeyspaces{\catcode`\ \active}
+{\obeyspaces\global\let =\space}
+
+\def\loop#1\repeat{\def\body{#1}\iterate}
+\def\iterate{\body \let\next\iterate \else\let\next\relax\fi \next}
+\let\repeat=\fi % this makes \loop...\if...\repeat skippable
+
+\def\thinspace{\kern .16667em }
+\def\negthinspace{\kern-.16667em }
+\def\enspace{\kern.5em }
+
+\def\enskip{\hskip.5em\relax}
+\def\quad{\hskip1em\relax}
+\def\qquad{\hskip2em\relax}
+
+\def\smallskip{\vskip\smallskipamount}
+\def\medskip{\vskip\medskipamount}
+\def\bigskip{\vskip\bigskipamount}
+
+\def\nointerlineskip{\prevdepth-1000\p@}
+\def\offinterlineskip{\baselineskip-1000\p@
+ \lineskip\z@ \lineskiplimit\maxdimen}
+
+\def\topglue{\nointerlineskip\vglue-\topskip\vglue} % for top of page
+\def\vglue{\afterassignment\vgl@\skip@=}
+\def\vgl@{\par \dimen@\prevdepth \hrule height\z@
+ \nobreak\vskip\skip@ \prevdepth\dimen@}
+\def\hglue{\afterassignment\hgl@\skip@=}
+\def\hgl@{\leavevmode \count@\spacefactor \vrule width\z@
+ \nobreak\hskip\skip@ \spacefactor\count@}
+
+\def~{\penalty\@M \ } % tie
+\def\slash{/\penalty\exhyphenpenalty} % a `/' that acts like a `-'
+
+\def\break{\penalty-\@M}
+\def\nobreak{\penalty \@M}
+\def\allowbreak{\penalty \z@}
+
+\def\filbreak{\par\vfil\penalty-200\vfilneg}
+\def\goodbreak{\par\penalty-500 }
+\def\eject{\par\break}
+\def\supereject{\par\penalty-\@MM}
+
+\def\removelastskip{\ifdim\lastskip=\z@\else\vskip-\lastskip\fi}
+\def\smallbreak{\par\ifdim\lastskip<\smallskipamount
+ \removelastskip\penalty-50\smallskip\fi}
+\def\medbreak{\par\ifdim\lastskip<\medskipamount
+ \removelastskip\penalty-100\medskip\fi}
+\def\bigbreak{\par\ifdim\lastskip<\bigskipamount
+ \removelastskip\penalty-200\bigskip\fi}
+
+\def\line{\hbox to\hsize}
+\def\leftline#1{\line{#1\hss}}
+\def\rightline#1{\line{\hss#1}}
+\def\centerline#1{\line{\hss#1\hss}}
+
+\def\rlap#1{\hbox to\z@{#1\hss}}
+\def\llap#1{\hbox to\z@{\hss#1}}
+
+\def\m@th{\mathsurround\z@}
+\def\underbar#1{$\setbox\z@\hbox{#1}\dp\z@\z@
+ \m@th \underline{\box\z@}$}
+
+\newbox\strutbox
+\setbox\strutbox=\hbox{\vrule height8.5pt depth3.5pt width\z@}
+\def\strut{\relax\ifmmode\copy\strutbox\else\unhcopy\strutbox\fi}
+
+\def\hidewidth{\hskip\hideskip} % for alignment entries that can stick out
+\def\ialign{\everycr{}\tabskip\z@skip\halign} % initialized \halign
+\newcount\mscount
+\def\multispan#1{\omit \mscount#1\relax
+ \loop\ifnum\mscount>\@ne \sp@n\repeat}
+\def\sp@n{\span\omit\advance\mscount\m@ne}
+
+\newif\ifus@ \newif\if@cr
+\newbox\tabs \newbox\tabsyet \newbox\tabsdone
+
+\def\cleartabs{\global\setbox\tabsyet\null \setbox\tabs\null}
+\def\settabs{\setbox\tabs\null \futurelet\next\sett@b}
+\let\+=\relax % in case this file is being read in twice
+\def\sett@b{\ifx\next\+\def\nxt{\afterassignment\s@tt@b\let\nxt}%
+ \else\let\nxt\s@tcols\fi \let\next\relax \nxt}
+\def\s@tt@b{\let\nxt\relax \us@false\m@ketabbox}
+\def\tabalign{\us@true\m@ketabbox} % non-\outer version of \+
+\outer\def\+{\tabalign}
+\def\s@tcols#1\columns{\count@#1\dimen@\hsize
+ \loop\ifnum\count@>\z@ \@nother \repeat}
+\def\@nother{\dimen@ii\dimen@ \divide\dimen@ii\count@
+ \setbox\tabs\hbox{\hbox to\dimen@ii{}\unhbox\tabs}%
+ \advance\dimen@-\dimen@ii \advance\count@\m@ne}
+
+\def\m@ketabbox{\begingroup
+ \global\setbox\tabsyet\copy\tabs
+ \global\setbox\tabsdone\null
+ \def\cr{\@crtrue\crcr\egroup\egroup
+ \ifus@\unvbox\z@\lastbox\fi\endgroup
+ \setbox\tabs\hbox{\unhbox\tabsyet\unhbox\tabsdone}}%
+ \setbox\z@\vbox\bgroup\@crfalse
+ \ialign\bgroup&\t@bbox##\t@bb@x\crcr}
+
+\def\t@bbox{\setbox\z@\hbox\bgroup}
+\def\t@bb@x{\if@cr\egroup % now \box\z@ holds the column
+ \else\hss\egroup \global\setbox\tabsyet\hbox{\unhbox\tabsyet
+ \global\setbox\@ne\lastbox}% now \box\@ne holds its size
+ \ifvoid\@ne\global\setbox\@ne\hbox to\wd\z@{}%
+ \else\setbox\z@\hbox to\wd\@ne{\unhbox\z@}\fi
+ \global\setbox\tabsdone\hbox{\box\@ne\unhbox\tabsdone}\fi
+ \box\z@}
+
+\def\hang{\hangindent\parindent}
+\def\textindent#1{\indent\llap{#1\enspace}\ignorespaces}
+\def\item{\par\hang\textindent}
+\def\itemitem{\par\indent \hangindent2\parindent \textindent}
+\def\narrower{\advance\leftskip\parindent
+ \advance\rightskip\parindent}
+
+\outer\def\beginsection#1\par{\vskip\z@ plus.3\vsize\penalty-250
+ \vskip\z@ plus-.3\vsize\bigskip\vskip\parskip
+ \message{#1}\leftline{\bf#1}\nobreak\smallskip\noindent}
+\outer\def\proclaim #1. #2\par{\medbreak
+ \noindent{\bf#1.\enspace}{\sl#2\par}%
+ \ifdim\lastskip<\medskipamount \removelastskip\penalty55\medskip\fi}
+
+\def\raggedright{\rightskip\z@ plus2em \spaceskip.3333em \xspaceskip.5em\relax}
+\def\ttraggedright{\tt\rightskip\z@ plus2em\relax} % for use with \tt only
+
+\chardef\%=`\%
+\chardef\&=`\&
+\chardef\#=`\#
+\chardef\$=`\$
+\chardef\ss="19
+\chardef\ae="1A
+\chardef\oe="1B
+\chardef\o="1C
+\chardef\AE="1D
+\chardef\OE="1E
+\chardef\O="1F
+\chardef\i="10 \chardef\j="11 % dotless letters
+\def\aa{\accent23a}
+\def\l{\char32l}
+\def\L{\leavevmode\setbox0\hbox{L}\hbox to\wd0{\hss\char32L}}
+
+\def\leavevmode{\unhbox\voidb@x} % begins a paragraph, if necessary
+\def\_{\leavevmode \kern.06em \vbox{\hrule width.3em}}
+\def\AA{\leavevmode\setbox0\hbox{!}\dimen@\ht0\advance\dimen@-1ex%
+ \rlap{\raise.67\dimen@\hbox{\char'27}}A}
+
+\def\mathhexbox#1#2#3{\leavevmode
+ \hbox{$\m@th \mathchar"#1#2#3$}}
+\def\dag{\mathhexbox279}
+\def\ddag{\mathhexbox27A}
+\def\S{\mathhexbox278}
+\def\P{\mathhexbox27B}
+\def\Orb{\mathhexbox20D}
+
+\def\oalign#1{\leavevmode\vtop{\baselineskip\z@skip \lineskip.25ex%
+ \ialign{##\crcr#1\crcr}}} \def\o@lign{\lineskiplimit\z@ \oalign}
+\def\ooalign{\lineskiplimit-\maxdimen \oalign} % chars over each other
+{\catcode`p=12 \catcode`t=12 \gdef\\#1pt{#1}} \let\getf@ctor=\\
+\def\sh@ft#1{\dimen@#1\kern\expandafter\getf@ctor\the\fontdimen1\font
+ \dimen@} % kern by #1 times the current slant
+\def\d#1{{\o@lign{\relax#1\crcr\hidewidth\sh@ft{-1ex}.\hidewidth}}}
+\def\b#1{{\o@lign{\relax#1\crcr\hidewidth\sh@ft{-3ex}%
+ \vbox to.2ex{\hbox{\char22}\vss}\hidewidth}}}
+\def\c#1{{\setbox\z@\hbox{#1}\ifdim\ht\z@=1ex\accent24 #1%
+ \else\ooalign{\unhbox\z@\crcr\hidewidth\char24\hidewidth}\fi}}
+\def\copyright{{\ooalign{\hfil\raise.07ex\hbox{c}\hfil\crcr\Orb}}}
+
+\def\dots{\relax\ifmmode\ldots\else$\m@th\ldots\,$\fi}
+\def\TeX{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125emX}
+
+\def\`#1{{\accent18 #1}}
+\def\'#1{{\accent19 #1}}
+\def\v#1{{\accent20 #1}} \let\^^_=\v
+\def\u#1{{\accent21 #1}} \let\^^S=\u
+\def\=#1{{\accent22 #1}}
+\def\^#1{{\accent94 #1}} \let\^^D=\^
+\def\.#1{{\accent95 #1}}
+\def\H#1{{\accent"7D #1}}
+\def\~#1{{\accent"7E #1}}
+\def\"#1{{\accent"7F #1}}
+\def\t#1{{\edef\next{\the\font}\the\textfont1\accent"7F\next#1}}
+
+\def\hrulefill{\leaders\hrule\hfill}
+\def\dotfill{\cleaders\hbox{$\m@th \mkern1.5mu.\mkern1.5mu$}\hfill}
+\def\rightarrowfill{$\m@th\smash-\mkern-7mu%
+ \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill
+ \mkern-7mu\mathord\rightarrow$}
+\def\leftarrowfill{$\m@th\mathord\leftarrow\mkern-7mu%
+ \cleaders\hbox{$\mkern-2mu\smash-\mkern-2mu$}\hfill
+ \mkern-7mu\smash-$}
+\mathchardef\braceld="37A \mathchardef\bracerd="37B
+\mathchardef\bracelu="37C \mathchardef\braceru="37D
+\def\downbracefill{$\m@th \setbox\z@\hbox{$\braceld$}%
+ \braceld\leaders\vrule height\ht\z@ depth\z@\hfill\braceru
+ \bracelu\leaders\vrule height\ht\z@ depth\z@\hfill\bracerd$}
+\def\upbracefill{$\m@th \setbox\z@\hbox{$\braceld$}%
+ \bracelu\leaders\vrule height\ht\z@ depth\z@\hfill\bracerd
+ \braceld\leaders\vrule height\ht\z@ depth\z@\hfill\braceru$}
+
+\outer\def\bye{\par\vfill\supereject\end}
+
+% Macros for math setting
+\message{math definitions,}
+
+\let\sp=^ \let\sb=_
+\def\,{\mskip\thinmuskip}
+\def\>{\mskip\medmuskip}
+\def\;{\mskip\thickmuskip}
+\def\!{\mskip-\thinmuskip}
+\def\*{\discretionary{\thinspace\the\textfont2\char2}{}{}}
+{\catcode`\'=\active \gdef'{^\bgroup\prim@s}}
+\def\prim@s{\prime\futurelet\next\pr@m@s}
+\def\pr@m@s{\ifx'\next\let\nxt\pr@@@s \else\ifx^\next\let\nxt\pr@@@t
+ \else\let\nxt\egroup\fi\fi \nxt}
+\def\pr@@@s#1{\prim@s} \def\pr@@@t#1#2{#2\egroup}
+{\catcode`\^^Z=\active \gdef^^Z{\not=}} % ^^Z is like \ne in math
+
+{\catcode`\_=\active \global\let_=\_} % _ in math is either subscript or \_
+
+\mathchardef\alpha="010B
+\mathchardef\beta="010C
+\mathchardef\gamma="010D
+\mathchardef\delta="010E
+\mathchardef\epsilon="010F
+\mathchardef\zeta="0110
+\mathchardef\eta="0111
+\mathchardef\theta="0112
+\mathchardef\iota="0113
+\mathchardef\kappa="0114
+\mathchardef\lambda="0115
+\mathchardef\mu="0116
+\mathchardef\nu="0117
+\mathchardef\xi="0118
+\mathchardef\pi="0119
+\mathchardef\rho="011A
+\mathchardef\sigma="011B
+\mathchardef\tau="011C
+\mathchardef\upsilon="011D
+\mathchardef\phi="011E
+\mathchardef\chi="011F
+\mathchardef\psi="0120
+\mathchardef\omega="0121
+\mathchardef\varepsilon="0122
+\mathchardef\vartheta="0123
+\mathchardef\varpi="0124
+\mathchardef\varrho="0125
+\mathchardef\varsigma="0126
+\mathchardef\varphi="0127
+\mathchardef\Gamma="7000
+\mathchardef\Delta="7001
+\mathchardef\Theta="7002
+\mathchardef\Lambda="7003
+\mathchardef\Xi="7004
+\mathchardef\Pi="7005
+\mathchardef\Sigma="7006
+\mathchardef\Upsilon="7007
+\mathchardef\Phi="7008
+\mathchardef\Psi="7009
+\mathchardef\Omega="700A
+
+\mathchardef\aleph="0240
+\def\hbar{{\mathchar'26\mkern-9muh}}
+\mathchardef\imath="017B
+\mathchardef\jmath="017C
+\mathchardef\ell="0160
+\mathchardef\wp="017D
+\mathchardef\Re="023C
+\mathchardef\Im="023D
+\mathchardef\partial="0140
+\mathchardef\infty="0231
+\mathchardef\prime="0230
+\mathchardef\emptyset="023B
+\mathchardef\nabla="0272
+\def\surd{{\mathchar"1270}}
+\mathchardef\top="023E
+\mathchardef\bot="023F
+\def\angle{{\vbox{\ialign{$\m@th\scriptstyle##$\crcr
+ \not\mathrel{\mkern14mu}\crcr
+ \noalign{\nointerlineskip}
+ \mkern2.5mu\leaders\hrule height.34pt\hfill\mkern2.5mu\crcr}}}}
+\mathchardef\triangle="0234
+\mathchardef\forall="0238
+\mathchardef\exists="0239
+\mathchardef\neg="023A \let\lnot=\neg
+\mathchardef\flat="015B
+\mathchardef\natural="015C
+\mathchardef\sharp="015D
+\mathchardef\clubsuit="027C
+\mathchardef\diamondsuit="027D
+\mathchardef\heartsuit="027E
+\mathchardef\spadesuit="027F
+
+\mathchardef\coprod="1360
+\mathchardef\bigvee="1357
+\mathchardef\bigwedge="1356
+\mathchardef\biguplus="1355
+\mathchardef\bigcap="1354
+\mathchardef\bigcup="1353
+\mathchardef\intop="1352 \def\int{\intop\nolimits}
+\mathchardef\prod="1351
+\mathchardef\sum="1350
+\mathchardef\bigotimes="134E
+\mathchardef\bigoplus="134C
+\mathchardef\bigodot="134A
+\mathchardef\ointop="1348 \def\oint{\ointop\nolimits}
+\mathchardef\bigsqcup="1346
+\mathchardef\smallint="1273
+
+\mathchardef\triangleleft="212F
+\mathchardef\triangleright="212E
+\mathchardef\bigtriangleup="2234
+\mathchardef\bigtriangledown="2235
+\mathchardef\wedge="225E \let\land=\wedge
+\mathchardef\vee="225F \let\lor=\vee
+\mathchardef\cap="225C
+\mathchardef\cup="225B
+\mathchardef\ddagger="227A
+\mathchardef\dagger="2279
+\mathchardef\sqcap="2275
+\mathchardef\sqcup="2274
+\mathchardef\uplus="225D
+\mathchardef\amalg="2271
+\mathchardef\diamond="2205
+\mathchardef\bullet="220F
+\mathchardef\wr="226F
+\mathchardef\div="2204
+\mathchardef\odot="220C
+\mathchardef\oslash="220B
+\mathchardef\otimes="220A
+\mathchardef\ominus="2209
+\mathchardef\oplus="2208
+\mathchardef\mp="2207
+\mathchardef\pm="2206
+\mathchardef\circ="220E
+\mathchardef\bigcirc="220D
+\mathchardef\setminus="226E % for set difference A\setminus B
+\mathchardef\cdot="2201
+\mathchardef\ast="2203
+\mathchardef\times="2202
+\mathchardef\star="213F
+
+\mathchardef\propto="322F
+\mathchardef\sqsubseteq="3276
+\mathchardef\sqsupseteq="3277
+\mathchardef\parallel="326B
+\mathchardef\mid="326A
+\mathchardef\dashv="3261
+\mathchardef\vdash="3260
+\mathchardef\nearrow="3225
+\mathchardef\searrow="3226
+\mathchardef\nwarrow="322D
+\mathchardef\swarrow="322E
+\mathchardef\Leftrightarrow="322C
+\mathchardef\Leftarrow="3228
+\mathchardef\Rightarrow="3229
+\def\neq{\not=} \let\ne=\neq
+\mathchardef\leq="3214 \let\le=\leq
+\mathchardef\geq="3215 \let\ge=\geq
+\mathchardef\succ="321F
+\mathchardef\prec="321E
+\mathchardef\approx="3219
+\mathchardef\succeq="3217
+\mathchardef\preceq="3216
+\mathchardef\supset="321B
+\mathchardef\subset="321A
+\mathchardef\supseteq="3213
+\mathchardef\subseteq="3212
+\mathchardef\in="3232
+\mathchardef\ni="3233 \let\owns=\ni
+\mathchardef\gg="321D
+\mathchardef\ll="321C
+\mathchardef\not="3236
+\mathchardef\leftrightarrow="3224
+\mathchardef\leftarrow="3220 \let\gets=\leftarrow
+\mathchardef\rightarrow="3221 \let\to=\rightarrow
+\mathchardef\mapstochar="3237 \def\mapsto{\mapstochar\rightarrow}
+\mathchardef\sim="3218
+\mathchardef\simeq="3227
+\mathchardef\perp="323F
+\mathchardef\equiv="3211
+\mathchardef\asymp="3210
+\mathchardef\smile="315E
+\mathchardef\frown="315F
+\mathchardef\leftharpoonup="3128
+\mathchardef\leftharpoondown="3129
+\mathchardef\rightharpoonup="312A
+\mathchardef\rightharpoondown="312B
+
+\def\joinrel{\mathrel{\mkern-3mu}}
+\def\relbar{\mathrel{\smash-}} % \smash, because - has the same height as +
+\def\Relbar{\mathrel=}
+\mathchardef\lhook="312C \def\hookrightarrow{\lhook\joinrel\rightarrow}
+\mathchardef\rhook="312D \def\hookleftarrow{\leftarrow\joinrel\rhook}
+\def\bowtie{\mathrel\triangleright\joinrel\mathrel\triangleleft}
+\def\models{\mathrel|\joinrel=}
+\def\Longrightarrow{\Relbar\joinrel\Rightarrow}
+\def\longrightarrow{\relbar\joinrel\rightarrow}
+\def\longleftarrow{\leftarrow\joinrel\relbar}
+\def\Longleftarrow{\Leftarrow\joinrel\Relbar}
+\def\longmapsto{\mapstochar\longrightarrow}
+\def\longleftrightarrow{\leftarrow\joinrel\rightarrow}
+\def\Longleftrightarrow{\Leftarrow\joinrel\Rightarrow}
+\def\iff{\;\Longleftrightarrow\;}
+
+\mathchardef\ldotp="613A % ldot as a punctuation mark
+\mathchardef\cdotp="6201 % cdot as a punctuation mark
+\mathchardef\colon="603A % colon as a punctuation mark
+\def\ldots{\mathinner{\ldotp\ldotp\ldotp}}
+\def\cdots{\mathinner{\cdotp\cdotp\cdotp}}
+\def\vdots{\vbox{\baselineskip4\p@ \lineskiplimit\z@
+ \kern6\p@\hbox{.}\hbox{.}\hbox{.}}}
+\def\ddots{\mathinner{\mkern1mu\raise7\p@\vbox{\kern7\p@\hbox{.}}\mkern2mu
+ \raise4\p@\hbox{.}\mkern2mu\raise\p@\hbox{.}\mkern1mu}}
+
+\def\acute{\mathaccent"7013 }
+\def\grave{\mathaccent"7012 }
+\def\ddot{\mathaccent"707F }
+\def\tilde{\mathaccent"707E }
+\def\bar{\mathaccent"7016 }
+\def\breve{\mathaccent"7015 }
+\def\check{\mathaccent"7014 }
+\def\hat{\mathaccent"705E }
+\def\vec{\mathaccent"017E }
+\def\dot{\mathaccent"705F }
+\def\widetilde{\mathaccent"0365 }
+\def\widehat{\mathaccent"0362 }
+\def\overrightarrow#1{\vbox{\m@th\ialign{##\crcr
+ \rightarrowfill\crcr\noalign{\kern-\p@\nointerlineskip}
+ $\hfil\displaystyle{#1}\hfil$\crcr}}}
+\def\overleftarrow#1{\vbox{\m@th\ialign{##\crcr
+ \leftarrowfill\crcr\noalign{\kern-\p@\nointerlineskip}
+ $\hfil\displaystyle{#1}\hfil$\crcr}}}
+\def\overbrace#1{\mathop{\vbox{\m@th\ialign{##\crcr\noalign{\kern3\p@}
+ \downbracefill\crcr\noalign{\kern3\p@\nointerlineskip}
+ $\hfil\displaystyle{#1}\hfil$\crcr}}}\limits}
+\def\underbrace#1{\mathop{\vtop{\m@th\ialign{##\crcr
+ $\hfil\displaystyle{#1}\hfil$\crcr\noalign{\kern3\p@\nointerlineskip}
+ \upbracefill\crcr\noalign{\kern3\p@}}}}\limits}
+\def\skew#1#2#3{{\muskip\z@#1mu\divide\muskip\z@\tw@ \mkern\muskip\z@
+ #2{\mkern-\muskip\z@{#3}\mkern\muskip\z@}\mkern-\muskip\z@}{}}
+
+\def\lmoustache{\delimiter"437A340 } % top from (, bottom from )
+\def\rmoustache{\delimiter"537B341 } % top from ), bottom from (
+\def\lgroup{\delimiter"462833A } % extensible ( with sharper tips
+\def\rgroup{\delimiter"562933B } % extensible ) with sharper tips
+\def\arrowvert{\delimiter"26A33C } % arrow without arrowheads
+\def\Arrowvert{\delimiter"26B33D } % double arrow without arrowheads
+\def\bracevert{\delimiter"77C33E } % the vertical bar that extends braces
+\def\Vert{\delimiter"26B30D } \let\|=\Vert
+\def\vert{\delimiter"26A30C }
+\def\uparrow{\delimiter"3222378 }
+\def\downarrow{\delimiter"3223379 }
+\def\updownarrow{\delimiter"326C33F }
+\def\Uparrow{\delimiter"322A37E }
+\def\Downarrow{\delimiter"322B37F }
+\def\Updownarrow{\delimiter"326D377 }
+\def\backslash{\delimiter"26E30F } % for double coset G\backslash H
+\def\rangle{\delimiter"526930B }
+\def\langle{\delimiter"426830A }
+\def\rbrace{\delimiter"5267309 } \let\}=\rbrace
+\def\lbrace{\delimiter"4266308 } \let\{=\lbrace
+\def\rceil{\delimiter"5265307 }
+\def\lceil{\delimiter"4264306 }
+\def\rfloor{\delimiter"5263305 }
+\def\lfloor{\delimiter"4262304 }
+
+\def\bigl{\mathopen\big}
+\def\bigm{\mathrel\big}
+\def\bigr{\mathclose\big}
+\def\Bigl{\mathopen\Big}
+\def\Bigm{\mathrel\Big}
+\def\Bigr{\mathclose\Big}
+\def\biggl{\mathopen\bigg}
+\def\biggm{\mathrel\bigg}
+\def\biggr{\mathclose\bigg}
+\def\Biggl{\mathopen\Bigg}
+\def\Biggm{\mathrel\Bigg}
+\def\Biggr{\mathclose\Bigg}
+\def\big#1{{\hbox{$\left#1\vbox to8.5\p@{}\right.\n@space$}}}
+\def\Big#1{{\hbox{$\left#1\vbox to11.5\p@{}\right.\n@space$}}}
+\def\bigg#1{{\hbox{$\left#1\vbox to14.5\p@{}\right.\n@space$}}}
+\def\Bigg#1{{\hbox{$\left#1\vbox to17.5\p@{}\right.\n@space$}}}
+\def\n@space{\nulldelimiterspace\z@ \m@th}
+
+\def\choose{\atopwithdelims()}
+\def\brack{\atopwithdelims[]}
+\def\brace{\atopwithdelims\{\}}
+
+\def\sqrt{\radical"270370 }
+
+\def\mathpalette#1#2{\mathchoice{#1\displaystyle{#2}}%
+ {#1\textstyle{#2}}{#1\scriptstyle{#2}}{#1\scriptscriptstyle{#2}}}
+\newbox\rootbox
+\def\root#1\of{\setbox\rootbox
+ \hbox{$\m@th\scriptscriptstyle{#1}$}\mathpalette\r@@t}
+\def\r@@t#1#2{\setbox\z@\hbox{$\m@th#1\sqrt{#2}$}\dimen@\ht\z@
+ \advance\dimen@-\dp\z@
+ \mkern5mu\raise.6\dimen@\copy\rootbox \mkern-10mu\box\z@}
+\newif\ifv@ \newif\ifh@
+\def\vphantom{\v@true\h@false\ph@nt}
+\def\hphantom{\v@false\h@true\ph@nt}
+\def\phantom{\v@true\h@true\ph@nt}
+\def\ph@nt{\ifmmode\def\next{\mathpalette\mathph@nt}%
+ \else\let\next\makeph@nt\fi\next}
+\def\makeph@nt#1{\setbox\z@\hbox{#1}\finph@nt}
+\def\mathph@nt#1#2{\setbox\z@\hbox{$\m@th#1{#2}$}\finph@nt}
+\def\finph@nt{\setbox\tw@\null
+ \ifv@ \ht\tw@\ht\z@ \dp\tw@\dp\z@\fi
+ \ifh@ \wd\tw@\wd\z@\fi \box\tw@}
+\def\mathstrut{\vphantom(}
+\def\smash{\relax % \relax, in case this comes first in \halign
+ \ifmmode\def\next{\mathpalette\mathsm@sh}\else\let\next\makesm@sh
+ \fi\next}
+\def\makesm@sh#1{\setbox\z@\hbox{#1}\finsm@sh}
+\def\mathsm@sh#1#2{\setbox\z@\hbox{$\m@th#1{#2}$}\finsm@sh}
+\def\finsm@sh{\ht\z@\z@ \dp\z@\z@ \box\z@}
+
+\def\cong{\mathrel{\mathpalette\@vereq\sim}} % congruence sign
+\def\@vereq#1#2{\lower.5\p@\vbox{\lineskiplimit\maxdimen\lineskip-.5\p@
+ \ialign{$\m@th#1\hfil##\hfil$\crcr#2\crcr=\crcr}}}
+\def\notin{\mathrel{\mathpalette\c@ncel\in}}
+\def\c@ncel#1#2{\m@th\ooalign{$\hfil#1\mkern1mu/\hfil$\crcr$#1#2$}}
+\def\rightleftharpoons{\mathrel{\mathpalette\rlh@{}}}
+\def\rlh@#1{\vcenter{\m@th\hbox{\ooalign{\raise2pt
+ \hbox{$#1\rightharpoonup$}\crcr
+ $#1\leftharpoondown$}}}}
+\def\buildrel#1\over#2{\mathrel{\mathop{\kern\z@#2}\limits^{#1}}}
+\def\doteq{\buildrel\textstyle.\over=}
+
+\def\log{\mathop{\rm log}\nolimits}
+\def\lg{\mathop{\rm lg}\nolimits}
+\def\ln{\mathop{\rm ln}\nolimits}
+\def\lim{\mathop{\rm lim}}
+\def\limsup{\mathop{\rm lim\,sup}}
+\def\liminf{\mathop{\rm lim\,inf}}
+\def\sin{\mathop{\rm sin}\nolimits}
+\def\arcsin{\mathop{\rm arcsin}\nolimits}
+\def\sinh{\mathop{\rm sinh}\nolimits}
+\def\cos{\mathop{\rm cos}\nolimits}
+\def\arccos{\mathop{\rm arccos}\nolimits}
+\def\cosh{\mathop{\rm cosh}\nolimits}
+\def\tan{\mathop{\rm tan}\nolimits}
+\def\arctan{\mathop{\rm arctan}\nolimits}
+\def\tanh{\mathop{\rm tanh}\nolimits}
+\def\cot{\mathop{\rm cot}\nolimits}
+\def\coth{\mathop{\rm coth}\nolimits}
+\def\sec{\mathop{\rm sec}\nolimits}
+\def\csc{\mathop{\rm csc}\nolimits}
+\def\max{\mathop{\rm max}}
+\def\min{\mathop{\rm min}}
+\def\sup{\mathop{\rm sup}}
+\def\inf{\mathop{\rm inf}}
+\def\arg{\mathop{\rm arg}\nolimits}
+\def\ker{\mathop{\rm ker}\nolimits}
+\def\dim{\mathop{\rm dim}\nolimits}
+\def\hom{\mathop{\rm hom}\nolimits}
+\def\det{\mathop{\rm det}}
+\def\exp{\mathop{\rm exp}\nolimits}
+\def\Pr{\mathop{\rm Pr}}
+\def\gcd{\mathop{\rm gcd}}
+\def\deg{\mathop{\rm deg}\nolimits}
+
+\def\bmod{\nonscript\mskip-\medmuskip\mkern5mu
+ \mathbin{\rm mod}\penalty900\mkern5mu\nonscript\mskip-\medmuskip}
+\def\pmod#1{\allowbreak\mkern18mu({\rm mod}\,\,#1)}
+
+\def\cases#1{\left\{\,\vcenter{\normalbaselines\m@th
+ \ialign{$##\hfil$&\quad##\hfil\crcr#1\crcr}}\right.}
+\def\matrix#1{\null\,\vcenter{\normalbaselines\m@th
+ \ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr
+ \mathstrut\crcr\noalign{\kern-\baselineskip}
+ #1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}\,}
+\def\pmatrix#1{\left(\matrix{#1}\right)}
+\newdimen\p@renwd
+\setbox0=\hbox{\tenex B} \p@renwd=\wd0 % width of the big left (
+\def\bordermatrix#1{\begingroup \m@th
+ \setbox\z@\vbox{\def\cr{\crcr\noalign{\kern2\p@\global\let\cr\endline}}%
+ \ialign{$##$\hfil\kern2\p@\kern\p@renwd&\thinspace\hfil$##$\hfil
+ &&\quad\hfil$##$\hfil\crcr
+ \omit\strut\hfil\crcr\noalign{\kern-\baselineskip}%
+ #1\crcr\omit\strut\cr}}%
+ \setbox\tw@\vbox{\unvcopy\z@\global\setbox\@ne\lastbox}%
+ \setbox\tw@\hbox{\unhbox\@ne\unskip\global\setbox\@ne\lastbox}%
+ \setbox\tw@\hbox{$\kern\wd\@ne\kern-\p@renwd\left(\kern-\wd\@ne
+ \global\setbox\@ne\vbox{\box\@ne\kern2\p@}%
+ \vcenter{\kern-\ht\@ne\unvbox\z@\kern-\baselineskip}\,\right)$}%
+ \null\;\vbox{\kern\ht\@ne\box\tw@}\endgroup}
+
+\def\openup{\afterassignment\@penup\dimen@=}
+\def\@penup{\advance\lineskip\dimen@
+ \advance\baselineskip\dimen@
+ \advance\lineskiplimit\dimen@}
+\def\eqalign#1{\null\,\vcenter{\openup\jot\m@th
+ \ialign{\strut\hfil$\displaystyle{##}$&$\displaystyle{{}##}$\hfil
+ \crcr#1\crcr}}\,}
+\newif\ifdt@p
+\def\displ@y{\global\dt@ptrue\openup\jot\m@th
+ \everycr{\noalign{\ifdt@p \global\dt@pfalse \ifdim\prevdepth>-1000\p@
+ \vskip-\lineskiplimit \vskip\normallineskiplimit \fi
+ \else \penalty\interdisplaylinepenalty \fi}}}
+\def\@lign{\tabskip\z@skip\everycr{}} % restore inside \displ@y
+\def\displaylines#1{\displ@y \tabskip\z@skip
+ \halign{\hbox to\displaywidth{$\@lign\hfil\displaystyle##\hfil$}\crcr
+ #1\crcr}}
+\def\eqalignno#1{\displ@y \tabskip\centering
+ \halign to\displaywidth{\hfil$\@lign\displaystyle{##}$\tabskip\z@skip
+ &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering
+ &\llap{$\@lign##$}\tabskip\z@skip\crcr
+ #1\crcr}}
+\def\leqalignno#1{\displ@y \tabskip\centering
+ \halign to\displaywidth{\hfil$\@lign\displaystyle{##}$\tabskip\z@skip
+ &$\@lign\displaystyle{{}##}$\hfil\tabskip\centering
+ &\kern-\displaywidth\rlap{$\@lign##$}\tabskip\displaywidth\crcr
+ #1\crcr}}
+
+% Definitions related to output
+
+\message{output routines,}
+
+\countdef\pageno=0 \pageno=1 % first page is number 1
+\newtoks\headline \headline={\hfil} % headline is normally blank
+\newtoks\footline \footline={\hss\tenrm\folio\hss}
+ % footline is normally a centered page number in font \tenrm
+\newif\ifr@ggedbottom
+\def\raggedbottom{\topskip 10\p@ plus60\p@ \r@ggedbottomtrue}
+\def\normalbottom{\topskip 10\p@ \r@ggedbottomfalse} % undoes \raggedbottom
+\def\folio{\ifnum\pageno<\z@ \romannumeral-\pageno \else\number\pageno \fi}
+\def\nopagenumbers{\footline{\hfil}} % blank out the footline
+\def\advancepageno{\ifnum\pageno<\z@ \global\advance\pageno\m@ne
+ \else\global\advance\pageno\@ne \fi} % increase |pageno|
+
+\newinsert\footins
+\def\footnote#1{\let\@sf\empty % parameter #2 (the text) is read later
+ \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
+ #1\@sf\vfootnote{#1}}
+\def\vfootnote#1{\insert\footins\bgroup
+ \interlinepenalty\interfootnotelinepenalty
+ \splittopskip\ht\strutbox % top baseline for broken footnotes
+ \splitmaxdepth\dp\strutbox \floatingpenalty\@MM
+ \leftskip\z@skip \rightskip\z@skip \spaceskip\z@skip \xspaceskip\z@skip
+ \textindent{#1}\footstrut\futurelet\next\fo@t}
+\def\fo@t{\ifcat\bgroup\noexpand\next \let\next\f@@t
+ \else\let\next\f@t\fi \next}
+\def\f@@t{\bgroup\aftergroup\@foot\let\next}
+\def\f@t#1{#1\@foot}
+\def\@foot{\strut\egroup}
+\def\footstrut{\vbox to\splittopskip{}}
+\skip\footins=\bigskipamount % space added when footnote is present
+\count\footins=1000 % footnote magnification factor (1 to 1)
+\dimen\footins=8in % maximum footnotes per page
+
+\newinsert\topins
+\newif\ifp@ge \newif\if@mid
+\def\topinsert{\@midfalse\p@gefalse\@ins}
+\def\midinsert{\@midtrue\@ins}
+\def\pageinsert{\@midfalse\p@getrue\@ins}
+\skip\topins=\z@skip % no space added when a topinsert is present
+\count\topins=1000 % magnification factor (1 to 1)
+\dimen\topins=\maxdimen % no limit per page
+\def\@ins{\par\begingroup\setbox\z@\vbox\bgroup} % start a \vbox
+\def\endinsert{\egroup % finish the \vbox
+ \if@mid \dimen@\ht\z@ \advance\dimen@\dp\z@ \advance\dimen@12\p@
+ \advance\dimen@\pagetotal \advance\dimen@-\pageshrink
+ \ifdim\dimen@>\pagegoal\@midfalse\p@gefalse\fi\fi
+ \if@mid \bigskip\box\z@\bigbreak
+ \else\insert\topins{\penalty100 % floating insertion
+ \splittopskip\z@skip
+ \splitmaxdepth\maxdimen \floatingpenalty\z@
+ \ifp@ge \dimen@\dp\z@
+ \vbox to\vsize{\unvbox\z@\kern-\dimen@}% depth is zero
+ \else \box\z@\nobreak\bigskip\fi}\fi\endgroup}
+
+\output{\plainoutput}
+\def\plainoutput{\shipout\vbox{\makeheadline\pagebody\makefootline}%
+ \advancepageno
+ \ifnum\outputpenalty>-\@MM \else\dosupereject\fi}
+\def\pagebody{\vbox to\vsize{\boxmaxdepth\maxdepth \pagecontents}}
+\def\makeheadline{\vbox to\z@{\vskip-22.5\p@
+ \line{\vbox to8.5\p@{}\the\headline}\vss}\nointerlineskip}
+\def\makefootline{\baselineskip24\p@\lineskiplimit\z@\line{\the\footline}}
+\def\dosupereject{\ifnum\insertpenalties>\z@ % something is being held over
+ \line{}\kern-\topskip\nobreak\vfill\supereject\fi}
+
+\def\pagecontents{\ifvoid\topins\else\unvbox\topins\fi
+ \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}
+\def\footnoterule{\kern-3\p@
+ \hrule width 2truein \kern 2.6\p@} % the \hrule is .4pt high
+
+% Hyphenation, miscellaneous macros, and initial values for standard layout
+\message{hyphenation}
+
+\lefthyphenmin=2 \righthyphenmin=3 % disallow x- or -xx breaks
+\input hyphen
+
+\def\magnification{\afterassignment\m@g\count@}
+\def\m@g{\mag\count@
+ \hsize6.5truein\vsize8.9truein\dimen\footins8truein}
+
+\def\loggingall{\tracingcommands\tw@\tracingstats\tw@
+ \tracingpages\@ne\tracingoutput\@ne\tracinglostchars\@ne
+ \tracingmacros\tw@\tracingparagraphs\@ne\tracingrestores\@ne
+ \showboxbreadth\maxdimen\showboxdepth\maxdimen}
+\def\tracingall{\tracingonline\@ne\loggingall}
+
+\def\showhyphens#1{\setbox0\vbox{\parfillskip\z@skip\hsize\maxdimen\tenrm
+ \pretolerance\m@ne\tolerance\m@ne\hbadness0\showboxdepth0\ #1}}
+
+\normalbaselines\rm % select roman font
+\nonfrenchspacing % punctuation affects the spacing
+\catcode`@=12 % at signs are no longer letters
+
+\def\fmtname{plain}\def\fmtversion{3.141592653} % identifies the current format
diff --git a/systems/knuth/dist/lib/rtest.mf b/systems/knuth/dist/lib/rtest.mf
new file mode 100644
index 0000000000..f5e449aa7d
--- /dev/null
+++ b/systems/knuth/dist/lib/rtest.mf
@@ -0,0 +1,13 @@
+% try all characters on TEST.MF using the parameters of cmr10
+if unknown cmbase: input cmbase fi % omit if CMMF can be assumed
+mode_setup;
+
+def generate suffix t= enddef;
+input cmr10; font_setup;
+
+let echar=endchar;
+def endchar= echar; stop "done with char "&decimal charcode&". " enddef;
+let iff=always_iff;
+
+input test;
+end
diff --git a/systems/knuth/dist/lib/slant.mf b/systems/knuth/dist/lib/slant.mf
new file mode 100644
index 0000000000..3763222bce
--- /dev/null
+++ b/systems/knuth/dist/lib/slant.mf
@@ -0,0 +1,32 @@
+% more-or-less general slant font generator for GFtoDVI
+
+% the calling file should set the font_identifier and
+% n = number of characters
+% s = slant ratio
+% r# = rule thickness (in sharp units)
+% u = vertical unit (in pixels)
+
+if unknown mag: mag := 1;
+elseif (mag<1) or (mag<>floor mag):
+ errmessage "Sorry, mag must be a positive integer"; mag := 1; fi
+
+mg := mag; mag := 1; mode_setup; u# := u*72.27/pixels_per_inch;
+pixels_per_inch := pixels_per_inch*mg; fix_units;
+
+define_whole_pixels(u); define_blacker_pixels(r);
+pickup pencircle scaled r; ruler := savepen;
+
+for k=1 upto n:
+ beginchar(k,k*u#*s,n*u#,0);
+ pickup ruler; draw origin--(k*u*s,k*u);
+ unfill (lft-1,bot -1)--(rt 1,bot -1)
+ --(rt 1,0)--(lft-1,0)--cycle;
+ unfill ((lft -1,0)--(rt 1,0)
+ --(rt 1,top 1)--(lft -1,top 1)--cycle) shifted (k*u*s,k*u);
+ endchar; endfor
+
+font_size 16pt#;
+font_slant s;
+fontdimen 8: r#;
+font_coding_scheme "GFSLANT";
+bye.
diff --git a/systems/knuth/dist/lib/story.tex b/systems/knuth/dist/lib/story.tex
new file mode 100644
index 0000000000..fcbaa4151a
--- /dev/null
+++ b/systems/knuth/dist/lib/story.tex
@@ -0,0 +1,18 @@
+\hrule
+\vskip 1in
+\centerline{\bf A SHORT STORY}
+\vskip 6pt
+\centerline{\sl by A. U. Thor} % !`?`?! (modified)
+\vskip .5cm
+Once upon a time, in a distant
+ galaxy called \"O\"o\c c,
+there lived a computer
+named R.~J. Drofnats.
+
+Mr.~Drofnats---or ``R. J.,'' as
+he preferred to be called---% error has been fixed!
+was happiest when he was at work
+typesetting beautiful documents.
+\vskip 1in
+\hrule
+\vfill\eject
diff --git a/systems/knuth/dist/lib/test.mf b/systems/knuth/dist/lib/test.mf
new file mode 100644
index 0000000000..c2d81f0302
--- /dev/null
+++ b/systems/knuth/dist/lib/test.mf
@@ -0,0 +1,29 @@
+
+cmchar "Lowercase thorn";
+beginchar(oct"015",10u#+serif_fit#,asc_height#,desc_depth#);
+italcorr .5x_height#*slant+min(.5curve#-.85u#,-.1u#);
+adjust_fit(serif_fit#,0);
+pickup tiny.nib; pos1(stem',0); pos2(stem,0);
+pos0'(stem',0); pos0(stem,0); z0l=z0'l; x0'=x1; x0=x2;
+lft x1l=hround(2.5u-.5stem'); top y1=h;
+numeric edge; edge=rt x2r;
+pickup fine.nib; pos3(if hefty:thin_join else: hair fi,180);
+pos4(vair',90); pos5(curve,0); pos6(vair,-90);
+rt x3l=1/3[rt x2,edge]; y3=1/8[bar_height,x_height];
+x4l=w-.5(w-serif_fit)+.5u; top y4r=x_height+oo;
+rt x5r=hround min(w-1.35u+.5curve,w-.6u); y5=.5x_height;
+x6=x3; bot y6r=-oo;
+(x,y4r)=whatever[z3l,z4l]; x4r:=min(x,.5[x5r,x4]);
+filldraw stroke z3e{up}...{right}z4e&super_arc.e(4,5)
+ &z5e{down}...{5(x6-x5),y6-y5}z6e; % bowl
+y0=ypart(((edge,h)--(edge,0))intersectionpoint(z3l{up}...{right}z4l));
+pickup tiny.nib; bot y2=-d;
+filldraw stroke z1e--z0'e--z0e--z2e; % stem
+pickup crisp.nib; pos8(hair,0); pos7'(stem,0);
+z7'=z2; x8l=x7'l; bot y8=0;
+filldraw stroke z7'e--z8e; % point
+if serifs: sloped_serif.l(1,0',a,1/3,jut,serif_drop); % upper serif
+ dish_serif(2,0,b,1/3,jut,c,1/3,0); fi % lower serif
+penlabels(0,1,2,3,4,5,6,8); endchar;
+
+
diff --git a/systems/knuth/dist/lib/testfont.tex b/systems/knuth/dist/lib/testfont.tex
new file mode 100644
index 0000000000..f7d146f867
--- /dev/null
+++ b/systems/knuth/dist/lib/testfont.tex
@@ -0,0 +1,208 @@
+% A testbed for font evaluation (see The METAFONTbook, Appendix H)
+
+\tracinglostchars=0
+\tolerance=1000
+\raggedbottom
+\nopagenumbers
+\parindent=0pt
+\newlinechar=`@
+\hyphenpenalty=200
+\doublehyphendemerits=30000
+\hyphenation{prom-i-nent}
+
+\newcount\m \newcount\n \newcount\p \newdimen\dim
+\chardef\other=12
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\def\hours{\n=\time \divide\n 60
+ \m=-\n \multiply\m 60 \advance\m \time
+ \twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\def\init{\message{@Name of the font to test = }
+ \read-1 to\fontname \startfont
+ \message{Now type a test command (\string\help\space for help):}}
+\def\startfont{\font\testfont=\fontname \spaceskip=0pt
+ \leftline{\sevenrm Test of \fontname\unskip\ on \today\ at \hours}
+ \medskip
+ \testfont \setbaselineskip
+ \ifdim\fontdimen6\testfont<10pt \rightskip=0pt plus 20pt
+ \else\rightskip=0pt plus 2em \fi
+ \spaceskip=\fontdimen2\testfont % space between words (\raggedright)
+ \xspaceskip=\fontdimen2\testfont \advance\xspaceskip by\fontdimen7\testfont}
+
+{\catcode`\|=0 \catcode`\\=\other
+|gdef|help{|message{%
+\init switches to another font;@%
+\end or \bye finishes the run;@%
+\table prints the font layout in tabular format;@%
+\text prints a sample text, assuming TeX text font conventions;@%
+\sample combines \table and \text;@%
+\mixture mixes a background character with a series of others;@%
+\alternation interleaves a background character with a series;@%
+\alphabet prints all lowercase letters within a given background;@%
+\ALPHABET prints all uppercase letters within a given background;@%
+\series prints a series of letters within a given background;@%
+\lowers prints a comprehensive test of lowercase;@%
+\uppers prints a comprehensive test of uppercase;@%
+\digits prints a comprehensive test of numerals;@%
+\math prints a comprehensive test of TeX math italic;@%
+\names prints a text that mixes upper and lower case;@%
+\punct prints a punctuation test;@%
+\bigtest combines many of the above routines;@%
+\help repeats this message;@%
+and you can use ordinary TeX commands (e.g., to \input a file).}}}
+
+\def\setbaselineskip{\setbox0=\hbox{\n=0
+\loop\char\n \ifnum \n<255 \advance\n 1 \repeat}
+\baselineskip=6pt \advance\baselineskip\ht0 \advance\baselineskip\dp0 }
+
+\def\setchar#1{{\escapechar-1\message{\string#1 character = }%
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \read-1 to\next
+ \expandafter\finsetchar\next\next#1}}
+\def\finsetchar#1#2\next#3{\global\chardef#3=`#1
+ \ifnum #3=`\# \global\chardef#3=#2 \fi}
+\def\promptthree{\setchar\background
+ \setchar\starting \setchar\ending}
+
+\def\mixture{\promptthree \domix\mixpattern}
+\def\alternation{\promptthree \domix\altpattern}
+\def\mixpattern{\0\1\0\0\1\1\0\0\0\1\1\1\0\1}
+\def\altpattern{\0\1\0\1\0\1\0\1\0\1\0\1\0\1\0\1\0}
+\def\domix#1{\par\chardef\0=\background \n=\starting
+ \loop \chardef\1=\n #1\endgraf
+ \ifnum \n<\ending \advance\n 1 \repeat}
+
+\def\!{\discretionary{\background}{\background}{\background}}
+\def\series{\promptthree \!\doseries\starting\ending\par}
+\def\doseries#1#2{\n=#1\loop\char\n\!\ifnum\n<#2\advance\n 1 \repeat}
+\def\complower{\!\doseries{`a}{`z}\doseries{'31}{'34}\par}
+\def\compupper{\!\doseries{`A}{`Z}\doseries{'35}{'37}\par}
+\def\compdigs{\!\doseries{`0}{`9}\par}
+\def\alphabet{\setchar\background\complower}
+\def\ALPHABET{\setchar\background\compupper}
+
+\def\lowers{\docomprehensive\complower{`a}{`z}{'31}{'34}}
+\def\uppers{\docomprehensive\compupper{`A}{`Z}{'35}{'37}}
+\def\digits{\docomprehensive\compdigs{`0}{`4}{`5}{`9}}
+\def\docomprehensive#1#2#3#4#5{\par\chardef\background=#2
+ \loop{#1} \ifnum\background<#3\m=\background\advance\m 1
+ \chardef\background=\m \repeat \chardef\background=#4
+ \loop{#1} \ifnum\background<#5\m=\background\advance\m 1
+ \chardef\background=\m \repeat}
+
+\def\names{ {\AA}ngel\aa\ Beatrice Claire
+ Diana \'Erica Fran\c{c}oise Ginette H\'el\`ene Iris
+ Jackie K\=aren {\L}au\.ra Mar{\'\i}a N\H{a}ta{\l}{\u\i}e {\O}ctave
+ Pauline Qu\^eneau Roxanne Sabine T\~a{\'\j}a Ur\v{s}ula
+ Vivian Wendy Xanthippe Yv{\o}nne Z\"azilie\par}
+\def\punct{\par\dopunct{min}\dopunct{pig}\dopunct{hid}
+ \dopunct{HIE}\dopunct{TIP}\dopunct{fluff}
+ \$1,234.56 + 7/8 = 9\% @ \#0\par}
+\def\dopunct#1{#1,\ #1:\ #1;\ `#1'\ ?`#1?\ !`#1!\ (#1)\ [#1]\ #1*\ #1.\par}
+
+\def\bigtest{\sample
+ hamburgefonstiv HAMBURGEFONSTIV\par
+ \names \punct \lowers \uppers \digits}
+
+\def\math{\textfont1=\testfont \skewchar\testfont=\skewtrial
+ \mathchardef\Gamma="100 \mathchardef\Delta="101
+ \mathchardef\Theta="102 \mathchardef\Lambda="103 \mathchardef\Xi="104
+ \mathchardef\Pi="105 \mathchardef\Sigma="106 \mathchardef\Upsilon="107
+ \mathchardef\Phi="108 \mathchardef\Psi="109 \mathchardef\Omega="10A
+ \def\ii{i} \def\jj{j}
+ \def\\##1{|##1|+}\mathtrial
+ \def\\##1{##1_2+}\mathtrial
+ \def\\##1{##1^2+}\mathtrial
+ \def\\##1{##1/2+}\mathtrial
+ \def\\##1{2/##1+}\mathtrial
+ \def\\##1{##1,{}+}\mathtrial
+ \def\\##1{d##1+}\mathtrial
+ \let\ii=\imath \let\jj=\jmath \def\\##1{\hat##1+}\mathtrial}
+\newcount\skewtrial \skewtrial='177
+\def\mathtrial{$\\A \\B \\C \\D \\E \\F \\G \\H \\I \\J \\K \\L \\M \\N \\O
+ \\P \\Q \\R \\S \\T \\U \\V \\W \\X \\Y \\Z \\a \\b \\c \\d \\e \\f \\g
+ \\h \\\ii \\\jj \\k \\l \\m \\n \\o \\p \\q \\r \\s \\t \\u \\v \\w \\x \\y
+ \\z \\\alpha \\\beta \\\gamma \\\delta \\\epsilon \\\zeta \\\eta \\\theta
+ \\\iota \\\kappa \\\lambda \\\mu \\\nu \\\xi \\\pi \\\rho \\\sigma \\\tau
+ \\\upsilon \\\phi \\\chi \\\psi \\\omega \\\vartheta \\\varpi \\\varphi
+ \\\Gamma \\\Delta \\\Theta \\\Lambda \\\Xi \\\Pi \\\Sigma \\\Upsilon
+ \\\Phi \\\Psi \\\Omega \\\partial \\\ell \\\wp$\par}
+\def\mathsy{\begingroup\skewtrial='060 % for math symbol font tests
+ \def\mathtrial{$\\A \\B \\C \\D \\E \\F \\G \\H \\I \\J \\K \\L
+ \\M \\N \\O \\P \\Q \\R \\S \\T \\U \\V \\W \\X \\Y \\Z$\par}
+ \math\endgroup}
+
+\def\oct#1{\hbox{\rm\'{}\kern-.2em\it#1\/\kern.05em}} % octal constant
+\def\hex#1{\hbox{\rm\H{}\tt#1}} % hexadecimal constant
+\def\setdigs#1"#2{\gdef\h{#2}% \h=hex prefix; \0\1=corresponding octal
+ \m=\n \divide\m by 64 \xdef\0{\the\m}%
+ \multiply\m by-64 \advance\m by\n \divide\m by 8 \xdef\1{\the\m}}
+\def\testrow{\setbox0=\hbox{\penalty 1\def\\{\char"\h}%
+ \\0\\1\\2\\3\\4\\5\\6\\7\\8\\9\\A\\B\\C\\D\\E\\F%
+ \global\p=\lastpenalty}} % \p=1 if none of the characters exist
+\def\oddline{\cr
+ \noalign{\nointerlineskip}
+ \multispan{19}\hrulefill&
+ \setbox0=\hbox{\lower 2.3pt\hbox{\hex{\h x}}}\smash{\box0}\cr
+ \noalign{\nointerlineskip}}
+\newif\ifskipping
+\def\evenline{\loop\skippingfalse
+ \ifnum\n<256 \m=\n \divide\m 16 \chardef\next=\m
+ \expandafter\setdigs\meaning\next \testrow
+ \ifnum\p=1 \skippingtrue \fi\fi
+ \ifskipping \global\advance\n 16 \repeat
+ \ifnum\n=256 \let\next=\endchart\else\let\next=\morechart\fi
+ \next}
+\def\morechart{\cr\noalign{\hrule\penalty5000}
+ \chartline \oddline \m=\1 \advance\m 1 \xdef\1{\the\m}
+ \chartline \evenline}
+\def\chartline{&\oct{\0\1x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&}
+\def\chartstrut{\lower4.5pt\vbox to14pt{}}
+\def\table{$$\global\n=0
+ \halign to\hsize\bgroup
+ \chartstrut##\tabskip0pt plus10pt&
+ &\hfil##\hfil&\vrule##\cr
+ \lower6.5pt\null
+ &&&\oct0&&\oct1&&\oct2&&\oct3&&\oct4&&\oct5&&\oct6&&\oct7&\evenline}
+\def\endchart{\cr\noalign{\hrule}
+ \raise11.5pt\null&&&\hex 8&&\hex 9&&\hex A&&\hex B&
+ &\hex C&&\hex D&&\hex E&&\hex F&\cr\egroup$$\par}
+\def\:{\setbox0=\hbox{\noboundary\char\n\noboundary}%
+ \ifdim\ht0>7.5pt\reposition
+ \else\ifdim\dp0>2.5pt\reposition\fi\fi
+ \box0\global\advance\n 1 }
+\def\reposition{\setbox0=\vbox{\kern2pt\box0}\dim=\dp0
+ \advance\dim 2pt \dp0=\dim}
+\def\centerlargechars{
+ \def\reposition{\setbox0=\hbox{$\vcenter{\kern2pt\box0\kern2pt}$}}}
+
+\def\text{{\advance\baselineskip-4pt
+\setbox0=\hbox{abcdefghijklmnopqrstuvwxyz}
+\ifdim\hsize>2\wd0 \ifdim 15pc>2\wd0 \hsize=15pc \else \hsize=2\wd0 \fi\fi
+On November 14, 1885, Senator \& Mrs.~Leland Stanford called
+together at their San Francisco mansion the 24~prominent men who had
+been chosen as the first trustees of The Leland Stanford Junior University.
+They handed to the board the Founding Grant of the University, which they
+had executed three days before. This document---with various amendments,
+legislative acts, and court decrees---remains as the University's charter.
+In bold, sweeping language it stipulates that the objectives of the University
+are ``to qualify students for personal success and direct usefulness in life;
+and to promote the publick welfare by exercising an influence in behalf of
+humanity and civilization, teaching the blessings of liberty regulated by
+law, and inculcating love and reverence for the great principles of
+government as derived from the inalienable rights of man to life, liberty,
+and the pursuit of happiness.'' \moretext
+(!`THE DAZED BROWN FOX QUICKLY GAVE 12345--67890 JUMPS!)\par}}
+\def\moretext{?`But aren't Kafka's Schlo{\ss} and {\AE}sop's {\OE}uvres
+often na{\"\i}ve vis-\`a-vis the d{\ae}monic ph{\oe}nix's official r\^ole
+in fluffy souffl\'es? }
+\def\omitaccents{\let\moretext=\relax}
+
+\def\sample{\table\text}
+
+\ifx\noinit!\else\init\fi
diff --git a/systems/knuth/dist/lib/waits.mf b/systems/knuth/dist/lib/waits.mf
new file mode 100644
index 0000000000..4d23e0a00d
--- /dev/null
+++ b/systems/knuth/dist/lib/waits.mf
@@ -0,0 +1,168 @@
+% This file can be loaded after PLAIN.MF.
+% It introduces conventions that are commonly used at WAITS.
+base_version:=base_version&"/WAITS";
+
+% The WAITS implementation allows an extended character set;
+% characters :=, <=, <>, and >= are put into the class with <, =, >, :, and |;
+% character ^^L (form feed) is in a class by itself, and is like an "outer" \.
+% Files containing these special characters should not be expected to work
+% elsewhere unless the following replacements are made:
+ let <= = <=;
+ let <> = <>;
+ let >= = >=;
+ let := = :=;
+
+% Say "boise_proof" on the command line, to get proofmode on boise
+def boise_proof = lcode_:=" /";
+ extra_setup:=extra_setup&"grayfont grbois;" enddef;
+
+screen_rows:=400; screen_cols:=480;
+
+% Here are conventions for local output devices:
+
+mode_def dover = % dover mode: for the Xerox Dover
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=384;
+ blacker:=1.2; % make pens somewhat blacker
+ fillin:=0; % no compensation for fillin
+ o_correction:=.6; % but don't overshoot as much
+ enddef;
+
+mode_def imagen = % imagen mode: for the Imagen 8/300 (Canon engine)
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=300;
+ blacker:=0; % Canon engine is black enough
+ fillin:=.2; % and it tends to fill in diagonals
+ o_correction:=.6; %
+ enddef;
+
+mode_def qms = % qms mode: for the QMS (Xerox engine)
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=300;
+ blacker:=.75; % (this value is conjectural)
+ fillin:=0; % (ditto)
+ o_correction:=.5; % (ditto)
+ enddef;
+
+mode_def aps = % aps mode: for the Autologic APS-Micro5
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=1; % yes, show titles online
+ pixels_per_inch:=722.909; % that's roughly 10 per pt
+ blacker:=.2; % make pens a teeny bit blacker
+ fillin:=.2; % but compensate for diagonal fillin
+ o_correction:=1; % and keep the full overshoot
+ enddef;
+
+mode_def crs = % crs mode: for the Alphatype CRS
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=1; % yes, show titles online
+ begingroup interim warningcheck:=0; % (we need to go past 4096)
+ pixels_per_inch:=4000+4000/3 endgroup; % HIGH res
+ blacker:=4; % make pens a lot blacker
+ fillin:=0; % but don't compensate for fillin
+ o_correction:=1; % and keep the full overshoot
+ enddef;
+
+mode_def boise = % boise mode: for the HP 2680A
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=180;
+ blacker:=.55; % make pens a bit blacker
+ fillin:=.1; % and compensate for diagonal fillin
+ o_correction:=.3; % but don't overshoot much
+ enddef;
+
+mode_def DD = % DD mode: for the DataDisc (usually with mag=magstep 2=1.44)
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=70; % really lowres
+ blacker:=0; % don't make the pens any blacker
+ fillin:=0; % and don't compensate for fillin
+ o_correction:=.2; % but suppress most overshoots
+ enddef;
+
+mode_def canon = % canon mode: for the old Imagen (Canon LBP10)
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=240;
+ blacker:=.2; % make pens a wee bit blacker
+ fillin:=.2; % and compensate for fillin
+ o_correction:=.4; % but don't overshoot as much
+ enddef;
+
+mode_def newDD = % newDD mode: for DataDisc with special aspect ratio
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=70; % really lowres
+ blacker:=0; % don't make the pens any blacker
+ fillin:=0; % and don't compensate for fillin
+ o_correction:=.2; % but suppress most overshoots
+ aspect_ratio:=4/3; % this is the new feature...
+ enddef;
+
+mode_def cg = % cg mode: for the Compugraphic 8600
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=1; % yes, show titles online
+ pixels_per_inch:=1301.5; % a bit less than 20 per pt
+ blacker:=.2; % make pens a teeny bit blacker
+ fillin:=.2; % but compensate for diagonal fillin
+ o_correction:=1; % and keep the full overshoot
+ aspect_ratio:=1569/1301.5; % strange, no?
+ enddef;
+
+mode_def epson = % Epson
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=240; % lowres
+ blacker:=0; % don't make the pens any blacker
+ fillin:=0; % and don't compensate for fillin
+ o_correction:=.2; % but suppress most overshoots
+ aspect_ratio:=9/10; % 216 dots/inch vertical
+ enddef;
+
+
+localfont:=imagen;
+
+% Finally, here are macros for Xerox-world font info:
+
+def font_family expr s = % string s names the font family, e.g., "CMR"
+ headerbyte 49: BCPL_string(s,20);
+ special "identifier "&s enddef;
+def coding_scheme expr s = % string s names the scheme, e.g. "TEX TEXT"
+ headerbyte 9: BCPL_string(s,40);
+ special "codingscheme "&s enddef;
+def font_face_byte expr x = % integer x gives the family member number,
+ headerbyte 72: x; % which should be between 0 and 255
+ special "fontfacebyte"; numspecial x enddef;
+
+def BCPL_string(expr s,n)= % string s becomes an n-byte BCPL string
+ for l:=if length(s)>=n: n-1 else: length(s) fi: l
+ for k:=1 upto l: , substring (k-1,k) of s endfor
+ for k:=l+2 upto n: , 0 endfor endfor enddef;
+
+Xerox_world:=1; % users can say `if known Xerox_world:...fi'
+
+inner end;
+def bye=
+ if fontmaking>0: font_family font_identifier_;
+ coding_scheme font_coding_scheme_;
+ font_face_byte max(0,254-round 2designsize); fi
+ tracingstats:=1; % at least for now we want this
+ end
+ enddef;
+
+outer bye,end;
diff --git a/systems/knuth/dist/lib/webmac.tex b/systems/knuth/dist/lib/webmac.tex
new file mode 100644
index 0000000000..5ca707bb9b
--- /dev/null
+++ b/systems/knuth/dist/lib/webmac.tex
@@ -0,0 +1,206 @@
+% standard macros for WEB listings (in addition to PLAIN.TEX)
+\xdef\fmtversion{\fmtversion+WEBMAC4.1} % identifies current set of macros
+\parskip 0pt % no stretch between paragraphs
+\parindent 1em % for paragraphs and for the first line of Pascal text
+
+\font\eightrm=cmr8 \let\sc=\eightrm % NOT a caps-and-small-caps font!
+\let\mainfont=\tenrm
+\font\titlefont=cmr7 scaled\magstep4 % title on the contents page
+\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title
+\font\tentex=cmtex10 % TeX extended character set (used in strings)
+\fontdimen7\tentex=0pt % no extra space after punctuation
+
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\|#1{\hbox{$#1$}} % one-letter identifiers look a bit better this way
+\def\&#1{\hbox{\bf#1\/}} % boldface type for reserved words
+\def\.#1{\hbox{\tentex % typewriter type for strings
+ \let\\=\BS % backslash in a string
+ \let\'=\RQ % right quote in a string
+ \let\`=\LQ % left quote in a string
+ \let\{=\LB % left brace in a string
+ \let\}=\RB % right brace in a string
+ \let\~=\TL % tilde in a string
+ \let\ =\SP % space in a string
+ \let\_=\UL % underline in a string
+ \let\&=\AM % ampersand in a string
+ #1}}
+\def\#{\hbox{\tt\char`\#}} % parameter sign
+\def\${\hbox{\tt\char`\$}} % dollar sign
+\def\%{\hbox{\tt\char`\%}} % percent sign
+\def\^{\ifmmode\mathchar"222 \else\char`^ \fi} % pointer or hat
+% circumflex accents can be obtained from \^^D instead of \^
+\def\AT!{@} % at sign for control text
+
+\chardef\AM=`\& % ampersand character in a string
+\chardef\BS=`\\ % backslash in a string
+\chardef\LB=`\{ % left brace in a string
+\def\LQ{{\tt\char'22}} % left quote in a string
+\chardef\RB=`\} % right brace in a string
+\def\RQ{{\tt\char'23}} % right quote in a string
+\def\SP{{\tt\char`\ }} % (visible) space in a string
+\chardef\TL=`\~ % tilde in a string
+\chardef\UL=`\_ % underline character in a string
+
+\newbox\bak \setbox\bak=\hbox to -1em{} % backspace one em
+\newbox\bakk\setbox\bakk=\hbox to -2em{} % backspace two ems
+
+\newcount\ind % current indentation in ems
+\def\1{\global\advance\ind by1\hangindent\ind em} % indent one more notch
+\def\2{\global\advance\ind by-1} % indent one less notch
+\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement
+\def\4{\copy\bak} % backspace one notch
+\def\5{\hfil\penalty-1\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break
+\def\6{\ifmmode\else\par % forced break
+ \hangindent\ind em\noindent\kern\ind em\copy\bakk\ignorespaces\fi}
+\def\7{\Y\6} % forced break and a little extra space
+
+\let\yskip=\smallskip
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+\def\note#1#2.{\Y\noindent{\hangindent2em\baselineskip10pt\eightrm#1~#2.\par}}
+\def\lapstar{\rlap{*}}
+\def\startsection{\Q\noindent{\let\*=\lapstar\bf\modstar.\quad}}
+\def\defin#1{\global\advance\ind by 2 \1\&{#1 }} % begin `define' or `format'
+\def\A{\note{See also section}} % crossref for doubly defined section name
+\def\As{\note{See also sections}} % crossref for multiply defined section name
+\def\B{\mathopen{\.{@\{}}} % begin controlled comment
+\def\C#1{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % Pascal comments
+ \XX\hfil\penalty-1\hfilneg\quad$\{\,$#1$\,\}$\XX}
+\def\D{\defin{define}} % macro definition
+\def\E{\cdot10^} % exponent in floating point constant
+\def\ET{ and~} % conjunction between two section numbers
+\def\ETs{, and~} % conjunction between the last two of several section numbers
+\def\F{\defin{format}} % format definition
+\let\G=\ge % greater than or equal sign
+\def\H#1{\hbox{\rm\char"7D\tt#1}} % hexadecimal constant
+\let\I=\ne % unequal sign
+\def\J{\.{@\&}} % TANGLE's join operation
+\let\K=\gets % left arrow
+\let\L=\le % less than or equal sign
+\outer\def\M#1.{\MN#1.\ifon\vfil\penalty-100\vfilneg % beginning of section
+ \vskip12ptminus3pt\startsection\ignorespaces}
+\outer\def\N#1.#2.{\MN#1.\vfil\eject % beginning of starred section
+ \def\rhead{\uppercase{\ignorespaces#2}} % define running headline
+ \message{*\modno} % progress report
+ \edef\next{\write\cont{\Z{#2}{\modno}{\the\pageno}}}\next % to contents file
+ \ifon\startsection{\bf\ignorespaces#2.\quad}\ignorespaces}
+\def\MN#1.{\par % common code for \M, \N
+ {\xdef\modstar{#1}\let\*=\empty\xdef\modno{#1}}
+ \ifx\modno\modstar \onmaybe \else\ontrue \fi \mark{\modno}}
+\def\O#1{\hbox{\rm\char'23\kern-.2em\it#1\/\kern.05em}} % octal constant
+\def\P{\rightskip=0pt plus 100pt minus 10pt % go into Pascal mode
+ \sfcode`;=3000
+ \pretolerance 10000
+ \hyphenpenalty 10000 \exhyphenpenalty 10000
+ \global\ind=2 \1\ \unskip}
+\def\Q{\rightskip=0pt % get out of Pascal mode
+ \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 }
+\let\R=\lnot % logical not
+\let\S=\equiv % equivalence sign
+\def\T{\mathclose{\.{@\}}}} % terminate controlled comment
+\def\U{\note{This code is used in section}} % crossref for use of a section
+\def\Us{\note{This code is used in sections}} % crossref for uses of a section
+\let\V=\lor % logical or
+\let\W=\land % logical and
+\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % section name
+ \XX$\langle\,$#2{\eightrm\kern.5em#1}$\,\rangle$\XX}
+\def\Y{\par\yskip}
+\let\Z=\let % now you can \send the control sequence \Z
+\def\){\hbox{\.{@\$}}} % sign for string pool check sum
+\def\]{\hbox{\.{@\\}}} % sign for forced line break
+\def\=#1{\kern2pt\hbox{\vrule\vtop{\vbox{\hrule
+ \hbox{\strut\kern2pt\.{#1}\kern2pt}}
+ \hrule}\vrule}\kern2pt} % verbatim string
+\let\~=\ignorespaces
+\let\*=*
+
+\def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue
+\newif\ifon \newif\iftitle \newif\ifpagesaved
+\def\lheader{\mainfont\the\pageno\eightrm\qquad\rhead\hfill\title\qquad
+ \tensy x\mainfont\topmark} % top line on left-hand pages
+\def\rheader{\tensy x\mainfont\topmark\eightrm\qquad\title\hfill\rhead
+ \qquad\mainfont\the\pageno} % top line on right-hand pages
+\def\page{\box255 }
+\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi
+ \shipout\vbox{
+ \vbox to\fullpageheight{
+ \iftitle\global\titlefalse
+ \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi
+ \vfill#1}} % parameter #1 is the page itself
+ \global\advance\pageno by1}
+
+\def\rhead{\.{WEB} OUTPUT} % this running head is reset by starred sections
+\def\title{} % an optional title can be set by the user
+\def\topofcontents{\centerline{\titlefont\title}
+ \vfill} % this material will start the table of contents page
+\def\botofcontents{\vfill} % this material will end the table of contents page
+\def\contentspagenumber{0} % default page number for table of contents
+\newdimen\pagewidth \pagewidth=6.5in % the width of each page
+\newdimen\pageheight \pageheight=8.7in % the height of each page
+\newdimen\fullpageheight \fullpageheight=9in % page height including headlines
+\newdimen\pageshift \pageshift=0in % shift righthand pages wrt lefthand ones
+\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein
+ \fullpageheight=9truein\setpage}
+\def\setpage{\hsize\pagewidth\vsize\pageheight} % use after changing page size
+\def\contentsfile{CONTENTS} % file that gets table of contents info
+\def\readcontents{\input CONTENTS}
+
+\newwrite\cont
+\output{\setbox0=\page % the first page is garbage
+ \openout\cont=\contentsfile
+ \global\output{\normaloutput\page\lheader\rheader}}
+\setpage
+\vbox to \vsize{} % the first \topmark won't be null
+
+\def\ch{\note{The following sections were changed by the change file:}
+ \let\*=\relax}
+\newbox\sbox % saved box preceding the index
+\newbox\lbox % lefthand column in the index
+\def\inx{\par\vskip6pt plus 1fil % we are beginning the index
+ \write\cont{} % ensure that the contents file isn't empty
+ \closeout\cont % the contents information has been fully gathered
+ \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi
+ \global\setbox\sbox=\page \global\pagesavedtrue}
+ \pagesavedfalse \eject % eject the page-so-far and predecessors
+ \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box
+ \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height
+ \hsize=.5\pagewidth \advance\hsize by -10pt
+ % column width for the index (20pt between cols)
+ \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
+ \def\lr{L} % this tells whether the left or right column is next
+ \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
+ \else\normaloutput{\vbox to\pageheight{\box\sbox\vss
+ \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader
+ \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi}
+ \message{Index:}
+ \parskip 0pt plus .5pt
+ \outer\def\:##1, {\par\hangindent2em\noindent##1:\kern1em} % index entry
+ \let\ttentry=\. \def\.##1{\ttentry{##1\kern.2em}} % give \tt a little room
+ \def\[##1]{$\underline{##1}$} % underlined index item
+ \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar
+ \hyphenpenalty 10000 \parindent0pt}
+\def\fin{\par\vfill\eject % this is done when we are ending the index
+ \ifpagesaved\null\vfill\eject\fi % output a null index column
+ \if L\lr\else\null\vfill\eject\fi % finish the current page
+ \parfillskip 0pt plus 1fil
+ \def\rhead{NAMES OF THE SECTIONS}
+ \message{Section names:}
+ \output{\normaloutput\page\lheader\rheader}
+ \setpage
+ \def\note##1##2.{\hfil\penalty-1\hfilneg\quad{\eightrm##1~##2.}}
+ \linepenalty=10 % try to conserve lines
+ \def\U{\note{Used in section}} % crossref for use of a section
+ \def\Us{\note{Used in sections}} % crossref for uses of a section
+ \def\:{\par\hangindent 2em}\let\*=*\let\.=\ttentry}
+\def\con{\par\vfill\eject % finish the section names
+ \rightskip 0pt \hyphenpenalty 50 \tolerance 200
+ \setpage
+ \output{\normaloutput\page\lheader\rheader}
+ \titletrue % prepare to output the table of contents
+ \pageno=\contentspagenumber \def\rhead{TABLE OF CONTENTS}
+ \message{Table of contents:}
+ \topofcontents
+ \line{\hfil Section\hbox to3em{\hss Page}}
+ \def\Z##1##2##3{\line{\ignorespaces##1
+ \leaders\hbox to .5em{.\hfil}\hfil\ ##2\hbox to3em{\hss##3}}}
+ \readcontents\relax % read the contents info
+ \botofcontents \end} % print the contents page(s) and terminate
diff --git a/systems/knuth/dist/lib/ztest.mf b/systems/knuth/dist/lib/ztest.mf
new file mode 100644
index 0000000000..35d1489e27
--- /dev/null
+++ b/systems/knuth/dist/lib/ztest.mf
@@ -0,0 +1,13 @@
+% try all characters on TEST.MF using the parameters of an unknown font
+if unknown cmbase: input cmbase fi % omit if CMMF can be assumed
+mode_setup;
+
+def generate suffix t= enddef;
+input z; font_setup;
+
+let echar=endchar;
+def endchar= echar; stop "done with char "&decimal charcode&". " enddef;
+let iff=always_iff;
+
+input test;
+end
diff --git a/systems/knuth/dist/mf/mf.web b/systems/knuth/dist/mf/mf.web
new file mode 100644
index 0000000000..ca3cb2518d
--- /dev/null
+++ b/systems/knuth/dist/mf/mf.web
@@ -0,0 +1,23135 @@
+% This program is copyright (C) 1984 by D. E. Knuth; all rights are reserved.
+% Copying of this file is authorized only if (1) you are D. E. Knuth, or if
+% (2) you make absolutely no changes to your copy. (The WEB system provides
+% for alterations via an auxiliary file; the master file should stay intact.)
+% In other words, METAFONT is under essentially the same ground rules as TeX.
+
+% TeX is a trademark of the American Mathematical Society.
+% METAFONT is a trademark of Addison-Wesley Publishing Company.
+
+% Version 0 was completed on July 28, 1984.
+% Version 1 was completed on January 4, 1986; it corresponds to "Volume D".
+% Version 1.1 trivially corrected the punctuation in one message (June 1986).
+% Version 1.2 corrected an arithmetic overflow problem (July 1986).
+% Version 1.3 improved rounding when elliptical pens are made (November 1986).
+% Version 1.4 corrected scan_declared_variable timing (May 1988).
+% Version 1.5 fixed negative halving in allocator when mem_min<0 (June 1988).
+% Version 1.6 kept open_log_file from calling fatal_error (November 1988).
+% Version 1.7 solved that problem a better way (December 1988).
+% Version 1.8 introduced major changes for 8-bit extensions (September 1989).
+% Version 1.9 improved skimping and was edited for style (December 1989).
+% Version 2.0 fixed bug in addto; released with TeX version 3.0 (March 1990).
+% Version 2.7 made consistent with TeX version 3.1 (September 1990).
+% Version 2.71 fixed bug in draw, allowed unprintable filenames (March 1992).
+% Version 2.718 fixed bug in <Choose a dependent...> (March 1995).
+% Version 2.7182 fixed bugs related to "<unprintable char>" (August 1996).
+% Version 2.71828 suppressed autorounding in dangerous cases (June 2003).
+% Version 2.718281 was a general cleanup with minor fixes (February 2008).
+% Version 2.7182818 was similar (January 2014).
+
+% A reward of $327.68 will be paid to the first finder of any remaining bug.
+
+% Although considerable effort has been expended to make the METAFONT program
+% correct and reliable, no warranty is implied; the author disclaims any
+% obligation or liability for damages, including but not limited to
+% special, indirect, or consequential damages arising out of or in
+% connection with the use or performance of this software. This work has
+% been a ``labor of love'' and the author hopes that users enjoy it.
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\noindent\ignorespaces}
+\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+\def\ph{\hbox{Pascal-H}}
+\def\psqrt#1{\sqrt{\mathstrut#1}}
+\def\k{_{k+1}}
+\def\pct!{{\char`\%}} % percent sign in ordinary text
+\font\tenlogo=logo10 % font used for the METAFONT logo
+\font\logos=logosl10
+\font\eightlogo=logo8
+\def\MF{{\tenlogo META}\-{\tenlogo FONT}}
+\def\<#1>{$\langle#1\rangle$}
+\def\section{\mathhexbox278}
+\let\swap=\leftrightarrow
+\def\round{\mathop{\rm round}\nolimits}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
+
+\outer\def\N#1. \[#2]#3.{\MN#1.\vfil\eject % begin starred section
+ \def\rhead{PART #2:\uppercase{#3}} % define running headline
+ \message{*\modno} % progress report
+ \edef\next{\write\cont{\Z{\?#2]#3}{\modno}{\the\pageno}}}\next
+ \ifon\startsection{\bf\ignorespaces#3.\quad}\ignorespaces}
+\let\?=\relax % we want to be able to \write a \?
+
+\def\title{{\eightlogo METAFONT}}
+\def\topofcontents{\hsize 5.5in
+ \vglue -30pt plus 1fil minus 1.5in
+ \def\?##1]{\hbox to 1in{\hfil##1.\ }}
+ }
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+\pageno=3
+\def\glob{13} % this should be the section number of "<Global...>"
+\def\gglob{20, 26} % this should be the next two sections of "<Global...>"
+
+@* \[1] Introduction.
+This is \MF, a font compiler intended to produce typefaces of high quality.
+The \PASCAL\ program that follows is the definition of \MF84, a standard
+@:PASCAL}{\PASCAL@>
+@!@:METAFONT84}{\MF84@>
+version of \MF\ that is designed to be highly portable so that identical output
+will be obtainable on a great variety of computers. The conventions
+of \MF84 are the same as those of \TeX82.
+
+The main purpose of the following program is to explain the algorithms of \MF\
+as clearly as possible. As a result, the program will not necessarily be very
+efficient when a particular \PASCAL\ compiler has translated it into a
+particular machine language. However, the program has been written so that it
+can be tuned to run efficiently in a wide variety of operating environments
+by making comparatively few changes. Such flexibility is possible because
+the documentation that follows is written in the \.{WEB} language, which is
+at a higher level than \PASCAL; the preprocessing step that converts \.{WEB}
+to \PASCAL\ is able to introduce most of the necessary refinements.
+Semi-automatic translation to other languages is also feasible, because the
+program below does not make extensive use of features that are peculiar to
+\PASCAL.
+
+A large piece of software like \MF\ has inherent complexity that cannot
+be reduced below a certain level of difficulty, although each individual
+part is fairly simple by itself. The \.{WEB} language is intended to make
+the algorithms as readable as possible, by reflecting the way the
+individual program pieces fit together and by providing the
+cross-references that connect different parts. Detailed comments about
+what is going on, and about why things were done in certain ways, have
+been liberally sprinkled throughout the program. These comments explain
+features of the implementation, but they rarely attempt to explain the
+\MF\ language itself, since the reader is supposed to be familiar with
+{\sl The {\logos METAFONT\/}book}.
+@.WEB@>
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+
+@ The present implementation has a long ancestry, beginning in the spring
+of~1977, when its author wrote a prototype set of subroutines and macros
+@^Knuth, Donald Ervin@>
+that were used to develop the first Computer Modern fonts.
+This original proto-\MF\ required the user to recompile a {\mc SAIL} program
+whenever any character was changed, because it was not a ``language'' for
+font design; the language was {\mc SAIL}. After several hundred characters
+had been designed in that way, the author developed an interpretable language
+called \MF, in which it was possible to express the Computer Modern programs
+less cryptically. A complete \MF\ processor was designed and coded by the
+author in 1979. This program, written in {\mc SAIL}, was adapted for use
+with a variety of typesetting equipment and display terminals by Leo Guibas,
+Lyle Ramshaw, and David Fuchs.
+@^Guibas, Leonidas Ioannis@>
+@^Ramshaw, Lyle Harold@>
+@^Fuchs, David Raymond@>
+Major improvements to the design of Computer Modern fonts were made in the
+spring of 1982, after which it became clear that a new language would
+better express the needs of letterform designers. Therefore an entirely
+new \MF\ language and system were developed in 1984; the present system
+retains the name and some of the spirit of \MF79, but all of the details
+have changed.
+
+No doubt there still is plenty of room for improvement, but the author
+is firmly committed to keeping \MF84 ``frozen'' from now on; stability
+and reliability are to be its main virtues.
+
+On the other hand, the \.{WEB} description can be extended without changing
+the core of \MF84 itself, and the program has been designed so that such
+extensions are not extremely difficult to make.
+The |banner| string defined here should be changed whenever \MF\
+undergoes any modifications, so that it will be clear which version of
+\MF\ might be the guilty party when a problem arises.
+@^extensions to \MF@>
+@^system dependencies@>
+
+If this program is changed, the resulting system should not be called
+`\MF\kern.5pt'; the official name `\MF\kern.5pt' by itself is reserved
+for software systems that are fully compatible with each other.
+A special test suite called the ``\.{TRAP} test'' is available for
+helping to determine whether an implementation deserves to be
+known as `\MF\kern.5pt' [cf.~Stanford Computer Science report CS1095,
+January 1986].
+
+@d banner=='This is METAFONT, Version 2.7182818' {printed when \MF\ starts}
+
+@ Different \PASCAL s have slightly different conventions, and the present
+@!@:PASCAL H}{\ph@>
+program expresses \MF\ in terms of the \PASCAL\ that was
+available to the author in 1984. Constructions that apply to
+this particular compiler, which we shall call \ph, should help the
+reader see how to make an appropriate interface for other systems
+if necessary. (\ph\ is Charles Hedrick's modification of a compiler
+@^Hedrick, Charles Locke@>
+for the DECsystem-10 that was originally developed at the University of
+Hamburg; cf.\ {\sl SOFTWARE---Practice \AM\ Experience \bf6} (1976),
+29--42. The \MF\ program below is intended to be adaptable, without
+extensive changes, to most other versions of \PASCAL, so it does not fully
+use the admirable features of \ph. Indeed, a conscious effort has been
+made here to avoid using several idiosyncratic features of standard
+\PASCAL\ itself, so that most of the code can be translated mechanically
+into other high-level languages. For example, the `\&{with}' and `\\{new}'
+features are not used, nor are pointer types, set types, or enumerated
+scalar types; there are no `\&{var}' parameters, except in the case of files
+or in the system-dependent |paint_row| procedure;
+there are no tag fields on variant records; there are no |real| variables;
+no procedures are declared local to other procedures.)
+
+The portions of this program that involve system-dependent code, where
+changes might be necessary because of differences between \PASCAL\ compilers
+and/or differences between
+operating systems, can be identified by looking at the sections whose
+numbers are listed under `system dependencies' in the index. Furthermore,
+the index entries for `dirty \PASCAL' list all places where the restrictions
+of \PASCAL\ have not been followed perfectly, for one reason or another.
+@!@^system dependencies@>
+@!@^dirty \PASCAL@>
+
+@ The program begins with a normal \PASCAL\ program heading, whose
+components will be filled in later, using the conventions of \.{WEB}.
+@.WEB@>
+For example, the portion of the program called `\X\glob:Global
+variables\X' below will be replaced by a sequence of variable declarations
+that starts in $\section\glob$ of this documentation. In this way, we are able
+to define each individual global variable when we are prepared to
+understand what it means; we do not have to define all of the globals at
+once. Cross references in $\section\glob$, where it says ``See also
+sections \gglob, \dots,'' also make it possible to look at the set of
+all global variables, if desired. Similar remarks apply to the other
+portions of the program heading.
+
+Actually the heading shown here is not quite normal: The |program| line
+does not mention any |output| file, because \ph\ would ask the \MF\ user
+to specify a file name if |output| were specified here.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@f mtype==type {`\&{mtype}' will be equivalent to `\&{type}'}
+@f type==true {but `|type|' will not be treated as a reserved word}
+
+@p @t\4@>@<Compiler directives@>@/
+program MF; {all file names are defined dynamically}
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+mtype @<Types in the outer block@>@/
+var @<Global variables@>@/
+@#
+procedure initialize; {this procedure gets things started properly}
+ var @<Local variables for initialization@>@/
+ begin @<Set initial values of key variables@>@/
+ end;@#
+@t\4@>@<Basic printing procedures@>@/
+@t\4@>@<Error handling procedures@>@/
+
+@ The overall \MF\ program begins with the heading just shown, after which
+comes a bunch of procedure declarations and function declarations.
+Finally we will get to the main program, which begins with the
+comment `|start_here|'. If you want to skip down to the
+main program now, you can look up `|start_here|' in the index.
+But the author suggests that the best way to understand this program
+is to follow pretty much the order of \MF's components as they appear in the
+\.{WEB} description you are now reading, since the present ordering is
+intended to combine the advantages of the ``bottom up'' and ``top down''
+approaches to the problem of understanding a somewhat complicated system.
+
+@ Three labels must be declared in the main program, so we give them
+symbolic names.
+
+@d start_of_MF=1 {go here when \MF's variables are initialized}
+@d end_of_MF=9998 {go here to close files and terminate gracefully}
+@d final_end=9999 {this label marks the ending of the program}
+
+@<Labels in the out...@>=
+start_of_MF@t\hskip-2pt@>, end_of_MF@t\hskip-2pt@>,@,final_end;
+ {key control points}
+
+@ Some of the code below is intended to be used only when diagnosing the
+strange behavior that sometimes occurs when \MF\ is being installed or
+when system wizards are fooling around with \MF\ without quite knowing
+what they are doing. Such code will not normally be compiled; it is
+delimited by the codewords `$|debug|\ldots|gubed|$', with apologies
+to people who wish to preserve the purity of English.
+
+Similarly, there is some conditional code delimited by
+`$|stat|\ldots|tats|$' that is intended for use when statistics are to be
+kept about \MF's memory usage. The |stat| $\ldots$ |tats| code also
+implements special diagnostic information that is printed when
+$\\{tracingedges}>1$.
+@^debugging@>
+
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@f debug==begin
+@f gubed==end
+@#
+@d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering
+ usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering
+ usage statistics}
+@f stat==begin
+@f tats==end
+
+@ This program has two important variations: (1) There is a long and slow
+version called \.{INIMF}, which does the extra calculations needed to
+@.INIMF@>
+initialize \MF's internal tables; and (2)~there is a shorter and faster
+production version, which cuts the initialization to a bare minimum.
+Parts of the program that are needed in (1) but not in (2) are delimited by
+the codewords `$|init|\ldots|tini|$'.
+
+@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
+@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
+@f init==begin
+@f tini==end
+
+@ If the first character of a \PASCAL\ comment is a dollar sign,
+\ph\ treats the comment as a list of ``compiler directives'' that will
+affect the translation of this program into machine language. The
+directives shown below specify full checking and inclusion of the \PASCAL\
+debugger when \MF\ is being debugged, but they cause range checking and other
+redundant code to be eliminated when the production system is being generated.
+Arithmetic overflow will be detected in all cases.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+@^overflow in arithmetic@>
+
+@<Compiler directives@>=
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+
+@ This \MF\ implementation conforms to the rules of the {\sl Pascal User
+@:PASCAL}{\PASCAL@>
+@^system dependencies@>
+Manual} published by Jensen and Wirth in 1975, except where system-dependent
+@^Wirth, Niklaus@>
+@^Jensen, Kathleen@>
+code is necessary to make a useful system program, and except in another
+respect where such conformity would unnecessarily obscure the meaning
+and clutter up the code: We assume that |case| statements may include a
+default case that applies if no matching label is found. Thus, we shall use
+constructions like
+$$\vbox{\halign{\ignorespaces#\hfil\cr
+|case x of|\cr
+1: $\langle\,$code for $x=1\,\rangle$;\cr
+3: $\langle\,$code for $x=3\,\rangle$;\cr
+|othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr
+|endcases|\cr}}$$
+since most \PASCAL\ compilers have plugged this hole in the language by
+incorporating some sort of default mechanism. For example, the \ph\
+compiler allows `|others|:' as a default label, and other \PASCAL s allow
+syntaxes like `\&{else}' or `\&{otherwise}' or `\\{otherwise}:', etc. The
+definitions of |othercases| and |endcases| should be changed to agree with
+local conventions. Note that no semicolon appears before |endcases| in
+this program, so the definition of |endcases| should include a semicolon
+if the compiler wants one. (Of course, if no default mechanism is
+available, the |case| statements of \MF\ will have to be laboriously
+extended by listing all remaining cases. People who are stuck with such
+\PASCAL s have, in fact, done this, successfully but not happily!)
+@:PASCAL H}{\ph@>
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The following parameters can be changed at compile time to extend or
+reduce \MF's capacity. They may have different values in \.{INIMF} and
+in production versions of \MF.
+@.INIMF@>
+@^system dependencies@>
+
+@<Constants...@>=
+@!mem_max=30000; {greatest index in \MF's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INIMF}, otherwise |>=mem_top|}
+@!max_internal=100; {maximum number of internal quantities}
+@!buf_size=500; {maximum number of characters simultaneously present in
+ current lines of open files; must not exceed |max_halfword|}
+@!error_line=72; {width of context lines on terminal error messages}
+@!half_error_line=42; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!screen_width=768; {number of pixels in each row of screen display}
+@!screen_depth=1024; {number of pixels in each column of screen display}
+@!stack_size=30; {maximum number of simultaneous input sources}
+@!max_strings=2000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=8000; {the minimum number of characters that should be
+ available for the user's identifier names and strings,
+ after \MF's own error messages are stored}
+@!pool_size=32000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all identifiers;
+ must exceed |string_vacancies| by the total
+ length of \MF's own strings, which is currently about 22000}
+@!move_size=5000; {space for storing moves in a single octant}
+@!max_wiggle=300; {number of autorounded points per cycle}
+@!gf_buf_size=800; {size of the output buffer, must be a multiple of 8}
+@!file_name_size=40; {file names shouldn't be longer than this}
+@!pool_name='MFbases:MF.POOL ';
+ {string of length |file_name_size|; tells where the string pool appears}
+@.MFbases@>
+@!path_size=300; {maximum number of knots between breakpoints of a path}
+@!bistack_size=785; {size of stack for bisection algorithms;
+ should probably be left at this value}
+@!header_size=100; {maximum number of \.{TFM} header words, times~4}
+@!lig_table_size=5000; {maximum number of ligature/kern steps, must be
+ at least 255 and at most 32510}
+@!max_kerns=500; {maximum number of distinct kern amounts}
+@!max_font_dimen=50; {maximum number of \&{fontdimen} parameters}
+
+@ Like the preceding parameters, the following quantities can be changed
+at compile time to extend or reduce \MF's capacity. But if they are changed,
+it is necessary to rerun the initialization program \.{INIMF}
+@.INIMF@>
+to generate new tables for the production \MF\ program.
+One can't simply make helter-skelter changes to the following constants,
+since certain rather complex initialization
+numbers are computed from them. They are defined here using
+\.{WEB} macros, instead of being put into \PASCAL's |const| list, in order to
+emphasize this distinction.
+
+@d mem_min=0 {smallest index in the |mem| array, must not be less
+ than |min_halfword|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INIMF};
+ must be substantially larger than |mem_min|
+ and not greater than |mem_max|}
+@d hash_size=2100 {maximum number of symbolic tokens,
+ must be less than |max_halfword-3*param_size|}
+@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
+@d max_in_open=6 {maximum number of input files and error insertions that
+ can be going on simultaneously}
+@d param_size=150 {maximum number of simultaneous macro parameters}
+@^system dependencies@>
+
+@ In case somebody has inadvertently made bad settings of the ``constants,''
+\MF\ checks them using a global variable called |bad|.
+
+This is the first of many sections of \MF\ where global variables are
+defined.
+
+@<Glob...@>=
+@!bad:integer; {is some ``constant'' wrong?}
+
+@ Later on we will say `\ignorespaces|if mem_max>=max_halfword then bad:=10|',
+or something similar. (We can't do that until |max_halfword| has been defined.)
+
+@<Check the ``constant'' values for consistency@>=
+bad:=0;
+if (half_error_line<30)or(half_error_line>error_line-15) then bad:=1;
+if max_print_line<60 then bad:=2;
+if gf_buf_size mod 8<>0 then bad:=3;
+if mem_min+1100>mem_top then bad:=4;
+if hash_prime>hash_size then bad:=5;
+if header_size mod 4 <> 0 then bad:=6;
+if(lig_table_size<255)or(lig_table_size>32510)then bad:=7;
+
+@ Labels are given symbolic names by the following definitions, so that
+occasional |goto| statements will be meaningful. We insert the label
+`|exit|' just before the `\ignorespaces|end|\unskip' of a procedure in
+which we have used the `|return|' statement defined below; the label
+`|restart|' is occasionally used at the very beginning of a procedure; and
+the label `|reswitch|' is occasionally used just prior to a |case|
+statement in which some cases change the conditions and we wish to branch
+to the newly applicable case. Loops that are set up with the |loop|
+construction defined below are commonly exited by going to `|done|' or to
+`|found|' or to `|not_found|', and they are sometimes repeated by going to
+`|continue|'. If two or more parts of a subroutine start differently but
+end up the same, the shared code may be gathered together at
+`|common_ending|'.
+
+Incidentally, this program never declares a label that isn't actually used,
+because some fussy \PASCAL\ compilers will complain about redundant labels.
+
+@d exit=10 {go here to leave a procedure}
+@d restart=20 {go here to start a procedure again}
+@d reswitch=21 {go here to start a case statement again}
+@d continue=22 {go here to resume a loop}
+@d done=30 {go here to exit a loop}
+@d done1=31 {like |done|, when there is more than one loop}
+@d done2=32 {for exiting the second loop in a long block}
+@d done3=33 {for exiting the third loop in a very long block}
+@d done4=34 {for exiting the fourth loop in an extremely long block}
+@d done5=35 {for exiting the fifth loop in an immense block}
+@d done6=36 {for exiting the sixth loop in a block}
+@d found=40 {go here when you've found it}
+@d found1=41 {like |found|, when there's more than one per routine}
+@d found2=42 {like |found|, when there's more than two per routine}
+@d not_found=45 {go here when you've found nothing}
+@d common_ending=50 {go here when you want to merge with another branch}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d negate(#) == #:=-# {change the sign of a variable}
+@d double(#) == #:=#+# {multiply a variable by two}
+@d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
+@f loop == xclause
+ {\.{WEB}'s |xclause| acts like `\ignorespaces|while true do|\unskip'}
+@d do_nothing == {empty statement}
+@d return == goto exit {terminate a procedure call}
+@f return == nil {\.{WEB} will henceforth say |return| instead of \\{return}}
+
+@* \[2] The character set.
+In order to make \MF\ readily portable to a wide variety of
+computers, all of its input text is converted to an internal eight-bit
+code that includes standard ASCII, the ``American Standard Code for
+Information Interchange.'' This 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 to a
+text file.
+@^ASCII code@>
+
+Such an internal code is relevant to users of \MF\ only with respect to
+the \&{char} and \&{ASCII} operations, and the comparison of strings.
+
+@ Characters of text that have been converted to \MF's internal form
+are said to be of type |ASCII_code|, which is a subrange of the integers.
+
+@<Types...@>=
+@!ASCII_code=0..255; {eight-bit numbers}
+
+@ 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, especially in a program for font design;
+so the present specification of \MF\ has been written under the assumption
+that the \PASCAL\ compiler and run-time system permit the use of text files
+with more than 64 distinguishable characters. More precisely, we assume that
+the character set contains at least the letters and symbols associated
+with ASCII codes @'40 through @'176; all of these characters are now
+available on most computer terminals.
+
+Since we are dealing with more characters than were present in the first
+\PASCAL\ compilers, we have to decide what to call the associated data
+type. Some \PASCAL s use the original name |char| for the
+characters in text files, even though there now are more than 64 such
+characters, while other \PASCAL s consider |char| to be a 64-element
+subrange of a larger data type that has some other name.
+
+In order to accommodate this difference, we shall use the name |text_char|
+to stand for the data type of the characters that are converted to and
+from |ASCII_code| when they are input and output. We shall also assume
+that |text_char| consists of the elements |chr(first_text_char)| through
+|chr(last_text_char)|, inclusive. The following definitions should be
+adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Local variables for init...@>=
+@!i:integer;
+
+@ The \MF\ processor converts 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.
+
+@<Glob...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+
+@ Since we are assuming that our \PASCAL\ system 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 the standard part of the |xchr| array
+properly, without needing any system-dependent changes. On the other
+hand, it is possible to implement \MF\ with less complete character
+sets, and in such cases it will be necessary to change something here.
+@^system dependencies@>
+
+@<Set init...@>=
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';@/
+
+@ The ASCII code is ``standard'' only to a certain extent, since many
+computer installations have found it advantageous to have ready access
+to more than 94 printing characters. If \MF\ is being used
+on a garden-variety \PASCAL\ for which only standard ASCII
+codes will appear in the input and output files, it doesn't really matter
+what codes are specified in |xchr[0..@'37]|, but the safest policy is to
+blank everything out by using the code shown below.
+
+However, other settings of |xchr| will make \MF\ more friendly on
+computers that have an extended character set, so that users can type things
+like `\.^^Z' instead of `\.{<>}'.
+People with extended character sets can
+assign codes arbitrarily, giving an |xchr| equivalent to whatever
+characters the users of \MF\ are allowed to have in their input files.
+Appropriate changes to \MF's |char_class| table should then be made.
+(Unlike \TeX, each installation of \MF\ has a fixed assignment of category
+codes, called the |char_class|.) Such changes make portability of programs
+more difficult, so they should be introduced cautiously if at all.
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]|
+where |i<j<@'177|, the value of |xord[xchr[i]]| will turn out to be
+|j| or more; hence, standard ASCII code numbers will be used instead of
+codes below @'40 in case there is a coincidence.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=@'177;
+for i:=@'200 to @'377 do xord[xchr[i]]:=i;
+for i:=0 to @'176 do xord[xchr[i]]:=i;
+
+@* \[3] Input and output.
+The bane of portability is the fact that different operating systems treat
+input and output quite differently, perhaps because computer scientists
+have not given sufficient attention to this problem. People have felt somehow
+that input and output are not part of ``real'' programming. Well, it is true
+that some kinds of programming are more fun than others. With existing
+input/output conventions being so diverse and so messy, the only sources of
+joy in such parts of the code are the rare occasions when one can find a
+way to make the program a little less bad than it might have been. We have
+two choices, either to attack I/O now and get it over with, or to postpone
+I/O until near the end. Neither prospect is very attractive, so let's
+get it over with.
+
+The basic operations we need to do are (1)~inputting and outputting of
+text, to or from a file or the user's terminal; (2)~inputting and
+outputting of eight-bit bytes, to or from a file; (3)~instructing the
+operating system to initiate (``open'') or to terminate (``close'') input or
+output from a specified file; (4)~testing whether the end of an input
+file has been reached; (5)~display of bits on the user's screen.
+The bit-display operation will be discussed in a later section; we shall
+deal here only with more traditional kinds of I/O.
+
+\MF\ needs to deal with two kinds of files.
+We shall use the term |alpha_file| for a file that contains textual data,
+and the term |byte_file| for a file that contains eight-bit binary information.
+These two types turn out to be the same on many computers, but
+sometimes there is a significant distinction, so we shall be careful to
+distinguish between them. Standard protocols for transferring
+such files from computer to computer, via high-speed networks, are
+now becoming available to more and more communities of users.
+
+The program actually makes use also of a third kind of file, called a
+|word_file|, when dumping and reloading base information for its own
+initialization. We shall define a word file later; but it will be possible
+for us to specify simple operations on word files before they are defined.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!alpha_file=packed file of text_char; {files that contain textual data}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+
+@ Most of what we need to do with respect to input and output can be handled
+by the I/O facilities that are standard in \PASCAL, i.e., the routines
+called |get|, |put|, |eof|, and so on. But
+standard \PASCAL\ does not allow file variables to be associated with file
+names that are determined at run time, so it cannot be used to implement
+\MF; some sort of extension to \PASCAL's ordinary |reset| and |rewrite|
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \MF\ can open a file whose external name is specified by
+|name_of_file|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file:packed array[1..file_name_size] of char;@;@/
+ {on some systems this may be a \&{record} variable}
+@!name_length:0..file_name_size;@/{this many characters are actually
+ relevant in |name_of_file| (the rest are blank)}
+
+@ The \ph\ compiler with which the present version of \MF\ was prepared has
+extended the rules of \PASCAL\ in a very convenient way. To open file~|f|,
+we can write
+$$\vbox{\halign{#\hfil\qquad&#\hfil\cr
+|reset(f,@t\\{name}@>,'/O')|&for input;\cr
+|rewrite(f,@t\\{name}@>,'/O')|&for output.\cr}}$$
+The `\\{name}' parameter, which is of type `\ignorespaces|packed
+array[@t\<\\{any}>@>] of text_char|', stands for the name of
+the external file that is being opened for input or output.
+Blank spaces that might appear in \\{name} are ignored.
+
+The `\.{/O}' parameter tells the operating system not to issue its own
+error messages if something goes wrong. If a file of the specified name
+cannot be found, or if such a file cannot be opened for some other reason
+(e.g., someone may already be trying to write the same file), we will have
+|@!erstat(f)<>0| after an unsuccessful |reset| or |rewrite|. This allows
+\MF\ to undertake appropriate corrective action.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+\MF's file-opening procedures return |false| if no file identified by
+|name_of_file| could be opened.
+
+@d reset_OK(#)==erstat(#)=0
+@d rewrite_OK(#)==erstat(#)=0
+
+@p function a_open_in(var @!f:alpha_file):boolean;
+ {open a text file for input}
+begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f);
+end;
+@#
+function a_open_out(var @!f:alpha_file):boolean;
+ {open a text file for output}
+begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f);
+end;
+@#
+function b_open_out(var @!f:byte_file):boolean;
+ {open a binary file for output}
+begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f);
+end;
+@#
+function w_open_in(var @!f:word_file):boolean;
+ {open a word file for input}
+begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f);
+end;
+@#
+function w_open_out(var @!f:word_file):boolean;
+ {open a word file for output}
+begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f);
+end;
+
+@ Files can be closed with the \ph\ routine `|close(f)|', which
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+should be used when all input or output with respect to |f| has been completed.
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+@p procedure a_close(var @!f:alpha_file); {close a text file}
+begin close(f);
+end;
+@#
+procedure b_close(var @!f:byte_file); {close a binary file}
+begin close(f);
+end;
+@#
+procedure w_close(var @!f:word_file); {close a word file}
+begin close(f);
+end;
+
+@ Binary input and output are done with \PASCAL's ordinary |get| and |put|
+procedures, so we don't have to make any other special arrangements for
+binary~I/O. Text output is also easy to do with standard \PASCAL\ routines.
+The treatment of text input is more difficult, however, because
+of the necessary translation to |ASCII_code| values.
+\MF's conventions should be efficient, and they should
+blend nicely with the user's operating environment.
+
+@ Input from text files is read one line at a time, using a routine called
+|input_ln|. This function is defined in terms of global variables called
+|buffer|, |first|, and |last| that will be described in detail later; for
+now, it suffices for us to know that |buffer| is an array of |ASCII_code|
+values, and that |first| and |last| are indices into this array
+representing the beginning and ending of a line of text.
+
+@<Glob...@>=
+@!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read}
+@!first:0..buf_size; {the first unused position in |buffer|}
+@!last:0..buf_size; {end of the line just input to |buffer|}
+@!max_buf_stack:0..buf_size; {largest index used in |buffer|}
+
+@ The |input_ln| function brings the next line of input from the specified
+field into available positions of the buffer array and returns the value
+|true|, unless the file has already been entirely read, in which case it
+returns |false| and sets |last:=first|. In general, the |ASCII_code|
+numbers that represent the next line of the file are input into
+|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the
+global variable |last| is set equal to |first| plus the length of the
+line. Trailing blanks are removed from the line; thus, either |last=first|
+(in which case the line was entirely blank) or |buffer[last-1]<>" "|.
+@^inner loop@>
+
+An overflow error is given, however, if the normal actions of |input_ln|
+would make |last>=buf_size|; this is done so that other parts of \MF\
+can safely look at the contents of |buffer[last+1]| without overstepping
+the bounds of the |buffer| array. Upon entry to |input_ln|, the condition
+|first<buf_size| will always hold, so that there is always room for an
+``empty'' line.
+
+The variable |max_buf_stack|, which is used to keep track of how large
+the |buf_size| parameter must be to accommodate the present job, is
+also kept up to date by |input_ln|.
+
+If the |bypass_eoln| parameter is |true|, |input_ln| will do a |get|
+before looking at the first character of the line; this skips over
+an |eoln| that was in |f^|. The procedure does not do a |get| when it
+reaches the end of the line; therefore it can be used to acquire input
+from the user's terminal as well as from ordinary text files.
+
+Standard \PASCAL\ says that a file should have |eoln| immediately
+before |eof|, but \MF\ needs only a weaker restriction: If |eof|
+occurs in the middle of a line, the system function |eoln| should return
+a |true| result (even though |f^| will be undefined).
+
+@p function input_ln(var @!f:alpha_file;@!bypass_eoln:boolean):boolean;
+ {inputs the next line or returns |false|}
+var @!last_nonblank:0..buf_size; {|last| with trailing blanks removed}
+begin if bypass_eoln then if not eof(f) then get(f);
+ {input the first character of the line into |f^|}
+last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
+if eof(f) then input_ln:=false
+else begin last_nonblank:=first;
+ while not eoln(f) do
+ begin if last>=max_buf_stack then
+ begin max_buf_stack:=last+1;
+ if max_buf_stack=buf_size then
+ @<Report overflow of the input buffer, and abort@>;
+ end;
+ buffer[last]:=xord[f^]; get(f); incr(last);
+ if buffer[last-1]<>" " then last_nonblank:=last;
+ end;
+ last:=last_nonblank; input_ln:=true;
+ end;
+end;
+
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file variable is called |term_in|, and when it
+is considered an output file the file variable is |term_out|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!term_in:alpha_file; {the terminal as an input file}
+@!term_out:alpha_file; {the terminal as an output file}
+
+@ Here is how to open the terminal files
+in \ph. The `\.{/I}' switch suppresses the first |get|.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d t_open_in==reset(term_in,'TTY:','/O/I') {open the terminal for text input}
+@d t_open_out==rewrite(term_out,'TTY:','/O')
+ {open the terminal for text output}
+
+@ Sometimes it is necessary to synchronize the input/output mixture that
+happens on the user's terminal, and three system-dependent
+procedures are used for this
+purpose. The first of these, |update_terminal|, 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.
+The second, |clear_terminal|, is called when we wish to cancel any
+input that the user may have typed ahead (since we are about to
+issue an unexpected error message). The third, |wake_up_terminal|,
+is supposed to revive the terminal if the user has disabled it by
+some instruction to the operating system. The following macros show how
+these operations can be specified in \ph:
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@d clear_terminal == break_in(term_in,true) {clear the terminal input buffer}
+@d wake_up_terminal == do_nothing {cancel the user's cancellation of output}
+
+@ We need a special routine to read the first line of \MF\ input from
+the user's terminal. This line is different because it is read before we
+have opened the transcript file; there is sort of a ``chicken and
+egg'' problem here. If the user types `\.{input cmr10}' on the first
+line, or if some macro invoked by that line does such an \.{input},
+the transcript file will be named `\.{cmr10.log}'; but if no \.{input}
+commands are performed during the first line of terminal input, the transcript
+file will acquire its default name `\.{mfput.log}'. (The transcript file
+will not contain error messages generated by the first line before the
+first \.{input} command.)
+@.mfput@>
+
+The first line is even more special if we are lucky enough to have an operating
+system that treats \MF\ differently from a run-of-the-mill \PASCAL\ object
+program. It's nice to let the user start running a \MF\ job by typing
+a command line like `\.{MF cmr10}'; in such a case, \MF\ will operate
+as if the first line of input were `\.{cmr10}', i.e., the first line will
+consist of the remainder of the command line, after the part that invoked \MF.
+
+The first line is special also because it may be read before \MF\ has
+input a base file. In such cases, normal error messages cannot yet
+be given. The following code uses concepts that will be explained later.
+(If the \PASCAL\ compiler does not support non-local |@!goto|\unskip, the
+@^system dependencies@>
+statement `|goto final_end|' should be replaced by something that
+quietly terminates the program.)
+
+@<Report overflow of the input buffer, and abort@>=
+if base_ident=0 then
+ begin write_ln(term_out,'Buffer size exceeded!'); goto final_end;
+@.Buffer size exceeded@>
+ end
+else begin cur_input.loc_field:=first; cur_input.limit_field:=last-1;
+ overflow("buffer size",buf_size);
+@:METAFONT capacity exceeded buffer size}{\quad buffer size@>
+ end
+
+@ Different systems have different ways to get started. But regardless of
+what conventions are adopted, the routine that initializes the terminal
+should satisfy the following specifications:
+
+\yskip\textindent{1)}It should open file |term_in| for input from the
+ terminal. (The file |term_out| will already be open for output to the
+ terminal.)
+
+\textindent{2)}If the user has given a command line, this line should be
+ considered the first line of terminal input. Otherwise the
+ user should be prompted with `\.{**}', and the first line of input
+ should be whatever is typed in response.
+
+\textindent{3)}The first line of input, which might or might not be a
+ command line, should appear in locations |first| to |last-1| of the
+ |buffer| array.
+
+\textindent{4)}The global variable |loc| should be set so that the
+ character to be read next by \MF\ is in |buffer[loc]|. This
+ character should not be blank, and we should have |loc<last|.
+
+\yskip\noindent(It may be necessary to prompt the user several times
+before a non-blank line comes in. The prompt is `\.{**}' instead of the
+later `\.*' because the meaning is slightly different: `\.{input}' need
+not be typed immediately after~`\.{**}'.)
+
+@d loc==cur_input.loc_field {location of first unread character in |buffer|}
+
+@ The following program does the required initialization
+without retrieving a possible command line.
+It should be clear how to modify this routine to deal with command lines,
+if the system permits them.
+@^system dependencies@>
+
+@p function init_terminal:boolean; {gets the terminal input started}
+label exit;
+begin t_open_in;
+loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal;
+@.**@>
+ if not input_ln(term_in,true) then {this shouldn't happen}
+ begin write_ln(term_out);
+ write(term_out,'! End of file on the terminal... why?');
+@.End of file on the terminal@>
+ init_terminal:=false; return;
+ end;
+ loc:=first;
+ while (loc<last)and(buffer[loc]=" ") do incr(loc);
+ if loc<last then
+ begin init_terminal:=true;
+ return; {return unless the line was all blank}
+ end;
+ write_ln(term_out,'Please type the name of your input file.');
+ end;
+exit:end;
+
+@* \[4] String handling.
+Symbolic token names and diagnostic messages are variable-length strings
+of eight-bit characters. Since \PASCAL\ does not have a well-developed string
+mechanism, \MF\ does all of its string processing by homegrown methods.
+
+Elaborate facilities for dynamic strings are not needed, so all of the
+necessary operations can be handled with a simple data structure.
+The array |str_pool| contains all of the (eight-bit) ASCII codes in all
+of the strings, and the array |str_start| contains indices of the starting
+points of each string. Strings are referred to by integer numbers, so that
+string number |s| comprises the characters |str_pool[j]| for
+|str_start[s]<=j<str_start[s+1]|. Additional integer variables
+|pool_ptr| and |str_ptr| indicate the number of entries used so far
+in |str_pool| and |str_start|, respectively; locations
+|str_pool[pool_ptr]| and |str_start[str_ptr]| are
+ready for the next string to be allocated.
+
+String numbers 0 to 255 are reserved for strings that correspond to single
+ASCII characters. This is in accordance with the conventions of \.{WEB},
+@.WEB@>
+which converts single-character strings into the ASCII code number of the
+single character involved, while it converts other strings into integers
+and builds a string pool file. Thus, when the string constant \.{"."} appears
+in the program below, \.{WEB} converts it into the integer 46, which is the
+ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"}
+into some integer greater than~255. String number 46 will presumably be the
+single character `\..'\thinspace; but some ASCII codes have no standard visible
+representation, and \MF\ may need to be able to print an arbitrary
+ASCII character, so the first 256 strings are used to specify exactly what
+should be printed for each of the 256 possibilities.
+
+Elements of the |str_pool| array must be ASCII codes that can actually be
+printed; i.e., they must have an |xchr| equivalent in the local
+character set. (This restriction applies only to preloaded strings,
+not to those generated dynamically by the user.)
+
+Some \PASCAL\ compilers won't pack integers into a single byte unless the
+integers lie in the range |-128..127|. To accommodate such systems
+we access the string pool only via macros that can easily be redefined.
+@^system dependencies@>
+
+@d si(#) == # {convert from |ASCII_code| to |packed_ASCII_code|}
+@d so(#) == # {convert from |packed_ASCII_code| to |ASCII_code|}
+
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+@!packed_ASCII_code = 0..255; {elements of |str_pool| array}
+
+@ @<Glob...@>=
+@!str_pool:packed array[pool_pointer] of packed_ASCII_code; {the characters}
+@!str_start : array[str_number] of pool_pointer; {the starting pointers}
+@!pool_ptr : pool_pointer; {first unused position in |str_pool|}
+@!str_ptr : str_number; {number of the current string being created}
+@!init_pool_ptr : pool_pointer; {the starting value of |pool_ptr|}
+@!init_str_ptr : str_number; {the starting value of |str_ptr|}
+@!max_pool_ptr : pool_pointer; {the maximum so far of |pool_ptr|}
+@!max_str_ptr : str_number; {the maximum so far of |str_ptr|}
+
+@ Several of the elementary string operations are performed using \.{WEB}
+macros instead of \PASCAL\ procedures, because many of the
+operations are done quite frequently and we want to avoid the
+overhead of procedure calls. For example, here is
+a simple macro that computes the length of a string.
+@.WEB@>
+
+@d length(#)==(str_start[#+1]-str_start[#]) {the number of characters
+ in string number \#}
+
+@ The length of the current string is called |cur_length|:
+
+@d cur_length == (pool_ptr - str_start[str_ptr])
+
+@ Strings are created by appending character codes to |str_pool|.
+The |append_char| macro, defined here, does not check to see if the
+value of |pool_ptr| has gotten too high; this test is supposed to be
+made before |append_char| is used.
+
+To test if there is room to append |l| more characters to |str_pool|,
+we shall write |str_room(l)|, which aborts \MF\ and gives an
+apologetic error message if there isn't enough room.
+
+@d append_char(#) == {put |ASCII_code| \# at the end of |str_pool|}
+begin str_pool[pool_ptr]:=si(#); incr(pool_ptr);
+end
+@d str_room(#) == {make sure that the pool hasn't overflowed}
+ begin if pool_ptr+# > max_pool_ptr then
+ begin if pool_ptr+# > pool_size then
+ overflow("pool size",pool_size-init_pool_ptr);
+@:METAFONT capacity exceeded pool size}{\quad pool size@>
+ max_pool_ptr:=pool_ptr+#;
+ end;
+ end
+
+@ \MF's string expressions are implemented in a brute-force way: Every
+new string or substring that is needed is simply copied into the string pool.
+
+Such a scheme can be justified because string expressions aren't a big
+deal in \MF\ applications; strings rarely need to be saved from one
+statement to the next. But it would waste space needlessly if we didn't
+try to reclaim the space of strings that are going to be used only once.
+
+Therefore a simple reference count mechanism is provided: If there are
+@^reference counts@>
+no references to a certain string from elsewhere in the program, and
+if there are no references to any strings created subsequent to it,
+then the string space will be reclaimed.
+
+The number of references to string number |s| will be |str_ref[s]|. The
+special value |str_ref[s]=max_str_ref=127| is used to denote an unknown
+positive number of references; such strings will never be recycled. If
+a string is ever referred to more than 126 times, simultaneously, we
+put it in this category. Hence a single byte suffices to store each |str_ref|.
+
+@d max_str_ref=127 {``infinite'' number of references}
+@d add_str_ref(#)==begin if str_ref[#]<max_str_ref then incr(str_ref[#]);
+ end
+
+@<Glob...@>=
+@!str_ref:array[str_number] of 0..max_str_ref;
+
+@ Here's what we do when a string reference disappears:
+
+@d delete_str_ref(#)== begin if str_ref[#]<max_str_ref then
+ if str_ref[#]>1 then decr(str_ref[#])@+else flush_string(#);
+ end
+
+@<Declare the procedure called |flush_string|@>=
+procedure flush_string(@!s:str_number);
+begin if s<str_ptr-1 then str_ref[s]:=0
+else repeat decr(str_ptr);
+ until str_ref[str_ptr-1]<>0;
+pool_ptr:=str_start[str_ptr];
+end;
+
+@ Once a sequence of characters has been appended to |str_pool|, it
+officially becomes a string when the function |make_string| is called.
+This function returns the identification number of the new string as its
+value.
+
+@p function make_string : str_number; {current string enters the pool}
+begin if str_ptr=max_str_ptr then
+ begin if str_ptr=max_strings then
+ overflow("number of strings",max_strings-init_str_ptr);
+@:METAFONT capacity exceeded number of strings}{\quad number of strings@>
+ incr(max_str_ptr);
+ end;
+str_ref[str_ptr]:=1; incr(str_ptr); str_start[str_ptr]:=pool_ptr;
+make_string:=str_ptr-1;
+end;
+
+@ The following subroutine compares string |s| with another string of the
+same length that appears in |buffer| starting at position |k|;
+the result is |true| if and only if the strings are equal.
+
+@p function str_eq_buf(@!s:str_number;@!k:integer):boolean;
+ {test equality of strings}
+label not_found; {loop exit}
+var @!j: pool_pointer; {running index}
+@!result: boolean; {result of comparison}
+begin j:=str_start[s];
+while j<str_start[s+1] do
+ begin if so(str_pool[j])<>buffer[k] then
+ begin result:=false; goto not_found;
+ end;
+ incr(j); incr(k);
+ end;
+result:=true;
+not_found: str_eq_buf:=result;
+end;
+
+@ Here is a similar routine, but it compares two strings in the string pool,
+and it does not assume that they have the same length. If the first string
+is lexicographically greater than, less than, or equal to the second,
+the result is respectively positive, negative, or zero.
+
+@p function str_vs_str(@!s,@!t:str_number):integer;
+ {test equality of strings}
+label exit;
+var @!j,@!k: pool_pointer; {running indices}
+@!ls,@!lt:integer; {lengths}
+@!l:integer; {length remaining to test}
+begin ls:=length(s); lt:=length(t);
+if ls<=lt then l:=ls@+else l:=lt;
+j:=str_start[s]; k:=str_start[t];
+while l>0 do
+ begin if str_pool[j]<>str_pool[k] then
+ begin str_vs_str:=str_pool[j]-str_pool[k]; return;
+ end;
+ incr(j); incr(k); decr(l);
+ end;
+str_vs_str:=ls-lt;
+exit:end;
+
+@ The initial values of |str_pool|, |str_start|, |pool_ptr|,
+and |str_ptr| are computed by the \.{INIMF} program, based in part
+on the information that \.{WEB} has output while processing \MF.
+@.INIMF@>
+@^string pool@>
+
+@p @!init function get_strings_started:boolean; {initializes the string pool,
+ but returns |false| if something goes wrong}
+label done,exit;
+var @!k,@!l:0..255; {small indices or counters}
+@!m,@!n:text_char; {characters input from |pool_file|}
+@!g:str_number; {garbage}
+@!a:integer; {accumulator for check sum}
+@!c:boolean; {check sum has been checked}
+begin pool_ptr:=0; str_ptr:=0; max_pool_ptr:=0; max_str_ptr:=0; str_start[0]:=0;
+@<Make the first 256 strings@>;
+@<Read the other strings from the \.{MF.POOL} file and return |true|,
+ or give an error message and return |false|@>;
+exit:end;
+tini
+
+@ @d app_lc_hex(#)==l:=#;
+ if l<10 then append_char(l+"0")@+else append_char(l-10+"a")
+
+@<Make the first 256...@>=
+for k:=0 to 255 do
+ begin if (@<Character |k| cannot be printed@>) then
+ begin append_char("^"); append_char("^");
+ if k<@'100 then append_char(k+@'100)
+ else if k<@'200 then append_char(k-@'100)
+ else begin app_lc_hex(k div 16); app_lc_hex(k mod 16);
+ end;
+ end
+ else append_char(k);
+ g:=make_string; str_ref[g]:=max_str_ref;
+ end
+
+@ The first 128 strings will contain 95 standard ASCII characters, and the
+other 33 characters will be printed in three-symbol form like `\.{\^\^A}'
+unless a system-dependent change is made here. Installations that have
+an extended character set, where for example |xchr[@'32]=@t\.{\'^^Z\'}@>|,
+would like string @'32 to be the single character @'32 instead of the
+three characters @'136, @'136, @'132 (\.{\^\^Z}). On the other hand,
+even people with an extended character set will want to represent string
+@'15 by \.{\^\^M}, since @'15 is ASCII's ``carriage return'' code; the idea is
+to produce visible strings instead of tabs or line-feeds or carriage-returns
+or bell-rings or characters that are treated anomalously in text files.
+
+Unprintable characters of codes 128--255 are, similarly, rendered
+\.{\^\^80}--\.{\^\^ff}.
+
+The boolean expression defined here should be |true| unless \MF\ internal
+code number~|k| corresponds to a non-troublesome visible symbol in the
+local character set.
+If character |k| cannot be printed, and |k<@'200|, then character |k+@'100| or
+|k-@'100| must be printable; moreover, ASCII codes
+|[@'60..@'71, @'136, @'141..@'146]|
+must be printable.
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Character |k| cannot be printed@>=
+ (k<" ")or(k>"~")
+
+@ When the \.{WEB} system program called \.{TANGLE} processes the \.{MF.WEB}
+description that you are now reading, it outputs the \PASCAL\ program
+\.{MF.PAS} and also a string pool file called \.{MF.POOL}. The \.{INIMF}
+@.WEB@>@.INIMF@>
+program reads the latter file, where each string appears as a two-digit decimal
+length followed by the string itself, and the information is recorded in
+\MF's string memory.
+
+@<Glob...@>=
+@!init @!pool_file:alpha_file; {the string-pool file output by \.{TANGLE}}
+tini
+
+@ @d bad_pool(#)==begin wake_up_terminal; write_ln(term_out,#);
+ a_close(pool_file); get_strings_started:=false; return;
+ end
+@<Read the other strings...@>=
+name_of_file:=pool_name; {we needn't set |name_length|}
+if a_open_in(pool_file) then
+ begin c:=false;
+ repeat @<Read one string, but return |false| if the
+ string memory space is getting too tight for comfort@>;
+ until c;
+ a_close(pool_file); get_strings_started:=true;
+ end
+else bad_pool('! I can''t read MF.POOL.')
+@.I can't read MF.POOL@>
+
+@ @<Read one string...@>=
+begin if eof(pool_file) then bad_pool('! MF.POOL has no check sum.');
+@.MF.POOL has no check sum@>
+read(pool_file,m,n); {read two digits of string length}
+if m='*' then @<Check the pool check sum@>
+else begin if (xord[m]<"0")or(xord[m]>"9")or@|
+ (xord[n]<"0")or(xord[n]>"9") then
+ bad_pool('! MF.POOL line doesn''t begin with two digits.');
+@.MF.POOL line doesn't...@>
+ l:=xord[m]*10+xord[n]-"0"*11; {compute the length}
+ if pool_ptr+l+string_vacancies>pool_size then
+ bad_pool('! You have to increase POOLSIZE.');
+@.You have to increase POOLSIZE@>
+ for k:=1 to l do
+ begin if eoln(pool_file) then m:=' '@+else read(pool_file,m);
+ append_char(xord[m]);
+ end;
+ read_ln(pool_file); g:=make_string; str_ref[g]:=max_str_ref;
+ end;
+end
+
+@ The \.{WEB} operation \.{@@\$} denotes the value that should be at the
+end of this \.{MF.POOL} file; any other value means that the wrong pool
+file has been loaded.
+@^check sum@>
+
+@<Check the pool check sum@>=
+begin a:=0; k:=1;
+loop@+ begin if (xord[n]<"0")or(xord[n]>"9") then
+ bad_pool('! MF.POOL check sum doesn''t have nine digits.');
+@.MF.POOL check sum...@>
+ a:=10*a+xord[n]-"0";
+ if k=9 then goto done;
+ incr(k); read(pool_file,n);
+ end;
+done: if a<>@$ then bad_pool('! MF.POOL doesn''t match; TANGLE me again.');
+@.MF.POOL doesn't match@>
+c:=true;
+end
+
+@* \[5] On-line and off-line printing.
+Messages that are sent to a user's terminal and to the transcript-log file
+are produced by several `|print|' procedures. These procedures will
+direct their output to a variety of places, based on the setting of
+the global variable |selector|, which has the following possible
+values:
+
+\yskip
+\hang |term_and_log|, the normal setting, prints on the terminal and on the
+ transcript file.
+
+\hang |log_only|, prints only on the transcript file.
+
+\hang |term_only|, prints only on the terminal.
+
+\hang |no_print|, doesn't print at all. This is used only in rare cases
+ before the transcript file is open.
+
+\hang |pseudo|, puts output into a cyclic buffer that is used
+ by the |show_context| routine; when we get to that routine we shall discuss
+ the reasoning behind this curious mode.
+
+\hang |new_string|, appends the output to the current string in the
+ string pool.
+
+\yskip
+\noindent The symbolic names `|term_and_log|', etc., have been assigned
+numeric codes that satisfy the convenient relations |no_print+1=term_only|,
+|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|.
+
+Three additional global variables, |tally| and |term_offset| and
+|file_offset|, record the number of characters that have been printed
+since they were most recently cleared to zero. We use |tally| to record
+the length of (possibly very long) stretches of printing; |term_offset|
+and |file_offset|, on the other hand, keep track of how many characters
+have appeared so far on the current line that has been output to the
+terminal or to the transcript file, respectively.
+
+@d no_print=0 {|selector| setting that makes data disappear}
+@d term_only=1 {printing is destined for the terminal only}
+@d log_only=2 {printing is destined for the transcript file only}
+@d term_and_log=3 {normal |selector| setting}
+@d pseudo=4 {special |selector| setting for |show_context|}
+@d new_string=5 {printing is deflected to the string pool}
+@d max_selector=5 {highest selector setting}
+
+@<Glob...@>=
+@!log_file : alpha_file; {transcript of \MF\ session}
+@!selector : 0..max_selector; {where to print a message}
+@!dig : array[0..22] of 0..15; {digits in a number being output}
+@!tally : integer; {the number of characters recently printed}
+@!term_offset : 0..max_print_line;
+ {the number of characters on the current terminal line}
+@!file_offset : 0..max_print_line;
+ {the number of characters on the current file line}
+@!trick_buf:array[0..error_line] of ASCII_code; {circular buffer for
+ pseudoprinting}
+@!trick_count: integer; {threshold for pseudoprinting, explained later}
+@!first_count: integer; {another variable for pseudoprinting}
+
+@ @<Initialize the output routines@>=
+selector:=term_only; tally:=0; term_offset:=0; file_offset:=0;
+
+@ Macro abbreviations for output to the terminal and to the log file are
+defined here for convenience. Some systems need special conventions
+for terminal output, and it is possible to adhere to those conventions
+by changing |wterm|, |wterm_ln|, and |wterm_cr| here.
+@^system dependencies@>
+
+@d wterm(#)==write(term_out,#)
+@d wterm_ln(#)==write_ln(term_out,#)
+@d wterm_cr==write_ln(term_out)
+@d wlog(#)==write(log_file,#)
+@d wlog_ln(#)==write_ln(log_file,#)
+@d wlog_cr==write_ln(log_file)
+
+@ To end a line of text output, we call |print_ln|.
+
+@<Basic print...@>=
+procedure print_ln; {prints an end-of-line}
+begin case selector of
+term_and_log: begin wterm_cr; wlog_cr;
+ term_offset:=0; file_offset:=0;
+ end;
+log_only: begin wlog_cr; file_offset:=0;
+ end;
+term_only: begin wterm_cr; term_offset:=0;
+ end;
+no_print,pseudo,new_string: do_nothing;
+end; {there are no other cases}
+end; {note that |tally| is not affected}
+
+@ The |print_char| procedure sends one character to the desired destination,
+using the |xchr| array to map it into an external character compatible with
+|input_ln|. All printing comes through |print_ln| or |print_char|.
+
+@<Basic printing...@>=
+procedure print_char(@!s:ASCII_code); {prints a single character}
+begin case selector of
+term_and_log: begin wterm(xchr[s]); wlog(xchr[s]);
+ incr(term_offset); incr(file_offset);
+ if term_offset=max_print_line then
+ begin wterm_cr; term_offset:=0;
+ end;
+ if file_offset=max_print_line then
+ begin wlog_cr; file_offset:=0;
+ end;
+ end;
+log_only: begin wlog(xchr[s]); incr(file_offset);
+ if file_offset=max_print_line then print_ln;
+ end;
+term_only: begin wterm(xchr[s]); incr(term_offset);
+ if term_offset=max_print_line then print_ln;
+ end;
+no_print: do_nothing;
+pseudo: if tally<trick_count then trick_buf[tally mod error_line]:=s;
+new_string: begin if pool_ptr<pool_size then append_char(s);
+ end; {we drop characters if the string space is full}
+end; {there are no other cases}
+incr(tally);
+end;
+
+@ An entire string is output by calling |print|. Note that if we are outputting
+the single standard ASCII character \.c, we could call |print("c")|, since
+|"c"=99| is the number of a single-character string, as explained above. But
+|print_char("c")| is quicker, so \MF\ goes directly to the |print_char|
+routine when it knows that this is safe. (The present implementation
+assumes that it is always safe to print a visible ASCII character.)
+@^system dependencies@>
+
+@<Basic print...@>=
+procedure print(@!s:integer); {prints string |s|}
+var @!j:pool_pointer; {current character code position}
+begin if (s<0)or(s>=str_ptr) then s:="???"; {this can't happen}
+@.???@>
+if (s<256)and(selector>pseudo) then print_char(s)
+else begin j:=str_start[s];
+ while j<str_start[s+1] do
+ begin print_char(so(str_pool[j])); incr(j);
+ end;
+ end;
+end;
+
+@ Sometimes it's necessary to print a string whose characters
+may not be visible ASCII codes. In that case |slow_print| is used.
+
+@<Basic print...@>=
+procedure slow_print(@!s:integer); {prints string |s|}
+var @!j:pool_pointer; {current character code position}
+begin if (s<0)or(s>=str_ptr) then s:="???"; {this can't happen}
+@.???@>
+if (s<256)and(selector>pseudo) then print_char(s)
+else begin j:=str_start[s];
+ while j<str_start[s+1] do
+ begin print(so(str_pool[j])); incr(j);
+ end;
+ end;
+end;
+
+@ Here is the very first thing that \MF\ prints: a headline that identifies
+the version number and base name. The |term_offset| variable is temporarily
+incorrect, but the discrepancy is not serious since we assume that the banner
+and base identifier together will occupy at most |max_print_line|
+character positions.
+
+@<Initialize the output...@>=
+wterm(banner);
+if base_ident=0 then wterm_ln(' (no base preloaded)')
+else begin slow_print(base_ident); print_ln;
+ end;
+update_terminal;
+
+@ The procedure |print_nl| is like |print|, but it makes sure that the
+string appears at the beginning of a new line.
+
+@<Basic print...@>=
+procedure print_nl(@!s:str_number); {prints string |s| at beginning of line}
+begin if ((term_offset>0)and(odd(selector)))or@|
+ ((file_offset>0)and(selector>=log_only)) then print_ln;
+print(s);
+end;
+
+@ An array of digits in the range |0..9| is printed by |print_the_digs|.
+
+@<Basic print...@>=
+procedure print_the_digs(@!k:eight_bits);
+ {prints |dig[k-1]|$\,\ldots\,$|dig[0]|}
+begin while k>0 do
+ begin decr(k); print_char("0"+dig[k]);
+ end;
+end;
+
+@ The following procedure, which prints out the decimal representation of a
+given integer |n|, has been written carefully so that it works properly
+if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
+to negative arguments, since such operations are not implemented consistently
+by all \PASCAL\ compilers.
+
+@<Basic print...@>=
+procedure print_int(@!n:integer); {prints an integer in decimal form}
+var k:0..23; {index to current digit; we assume that $|n|<10^{23}$}
+@!m:integer; {used to negate |n| in possibly dangerous cases}
+begin k:=0;
+if n<0 then
+ begin print_char("-");
+ if n>-100000000 then negate(n)
+ else begin m:=-1-n; n:=m div 10; m:=(m mod 10)+1; k:=1;
+ if m<10 then dig[0]:=m
+ else begin dig[0]:=0; incr(n);
+ end;
+ end;
+ end;
+repeat dig[k]:=n mod 10; n:=n div 10; incr(k);
+until n=0;
+print_the_digs(k);
+end;
+
+@ \MF\ also makes use of a trivial procedure to print two digits. The
+following subroutine is usually called with a parameter in the range |0<=n<=99|.
+
+@p procedure print_dd(@!n:integer); {prints two least significant digits}
+begin n:=abs(n) mod 100; print_char("0"+(n div 10));
+print_char("0"+(n mod 10));
+end;
+
+@ Here is a procedure that asks the user to type a line of input,
+assuming that the |selector| setting is either |term_only| or |term_and_log|.
+The input is placed into locations |first| through |last-1| of the
+|buffer| array, and echoed on the transcript file if appropriate.
+
+This procedure is never called when |interaction<scroll_mode|.
+
+@d prompt_input(#)==begin wake_up_terminal; print(#); term_input;
+ end {prints a string and gets a line of input}
+
+@p procedure term_input; {gets a line from the terminal}
+var @!k:0..buf_size; {index into |buffer|}
+begin update_terminal; {now the user sees the prompt for sure}
+if not input_ln(term_in,true) then fatal_error("End of file on the terminal!");
+@.End of file on the terminal@>
+term_offset:=0; {the user's line ended with \<\rm return>}
+decr(selector); {prepare to echo the input}
+if last<>first then for k:=first to last-1 do print(buffer[k]);
+print_ln; buffer[last]:="%"; incr(selector); {restore previous status}
+end;
+
+@* \[6] Reporting errors.
+When something anomalous is detected, \MF\ typically does something like this:
+$$\vbox{\halign{#\hfil\cr
+|print_err("Something anomalous has been detected");|\cr
+|help3("This is the first line of my offer to help.")|\cr
+|("This is the second line. I'm trying to")|\cr
+|("explain the best way for you to proceed.");|\cr
+|error;|\cr}}$$
+A two-line help message would be given using |help2|, etc.; these informal
+helps should use simple vocabulary that complements the words used in the
+official error message that was printed. (Outside the U.S.A., the help
+messages should preferably be translated into the local vernacular. Each
+line of help is at most 60 characters long, in the present implementation,
+so that |max_print_line| will not be exceeded.)
+
+The |print_err| procedure supplies a `\.!' before the official message,
+and makes sure that the terminal is awake if a stop is going to occur.
+The |error| procedure supplies a `\..' after the official message, then it
+shows the location of the error; and if |interaction=error_stop_mode|,
+it also enters into a dialog with the user, during which time the help
+message may be printed.
+@^system dependencies@>
+
+@ The global variable |interaction| has four settings, representing increasing
+amounts of user interaction:
+
+@d batch_mode=0 {omits all stops and omits terminal output}
+@d nonstop_mode=1 {omits all stops}
+@d scroll_mode=2 {omits error stops}
+@d error_stop_mode=3 {stops at every opportunity to interact}
+@d print_err(#)==begin if interaction=error_stop_mode then wake_up_terminal;
+ print_nl("! "); print(#);
+@.!\relax@>
+ end
+
+@<Glob...@>=
+@!interaction:batch_mode..error_stop_mode; {current level of interaction}
+
+@ @<Set init...@>=interaction:=error_stop_mode;
+
+@ \MF\ is careful not to call |error| when the print |selector| setting
+might be unusual. The only possible values of |selector| at the time of
+error messages are
+
+\yskip\hang|no_print| (when |interaction=batch_mode|
+ and |log_file| not yet open);
+
+\hang|term_only| (when |interaction>batch_mode| and |log_file| not yet open);
+
+\hang|log_only| (when |interaction=batch_mode| and |log_file| is open);
+
+\hang|term_and_log| (when |interaction>batch_mode| and |log_file| is open).
+
+@<Initialize the print |selector| based on |interaction|@>=
+if interaction=batch_mode then selector:=no_print@+else selector:=term_only
+
+@ A global variable |deletions_allowed| is set |false| if the |get_next|
+routine is active when |error| is called; this ensures that |get_next|
+will never be called recursively.
+@^recursion@>
+
+The global variable |history| records the worst level of error that
+has been detected. It has four possible values: |spotless|, |warning_issued|,
+|error_message_issued|, and |fatal_error_stop|.
+
+Another global variable, |error_count|, is increased by one when an
+|error| occurs without an interactive dialog, and it is reset to zero at
+the end of every statement. If |error_count| reaches 100, \MF\ decides
+that there is no point in continuing further.
+
+@d spotless=0 {|history| value when nothing has been amiss yet}
+@d warning_issued=1 {|history| value when |begin_diagnostic| has been called}
+@d error_message_issued=2 {|history| value when |error| has been called}
+@d fatal_error_stop=3 {|history| value when termination was premature}
+
+@<Glob...@>=
+@!deletions_allowed:boolean; {is it safe for |error| to call |get_next|?}
+@!history:spotless..fatal_error_stop; {has the source input been clean so far?}
+@!error_count:-1..100; {the number of scrolled errors since the
+ last statement ended}
+
+@ The value of |history| is initially |fatal_error_stop|, but it will
+be changed to |spotless| if \MF\ survives the initialization process.
+
+@<Set init...@>=
+deletions_allowed:=true; error_count:=0; {|history| is initialized elsewhere}
+
+@ Since errors can be detected almost anywhere in \MF, we want to declare the
+error procedures near the beginning of the program. But the error procedures
+in turn use some other procedures, which need to be declared |forward|
+before we get to |error| itself.
+
+It is possible for |error| to be called recursively if some error arises
+when |get_next| is being used to delete a token, and/or if some fatal error
+occurs while \MF\ is trying to fix a non-fatal one. But such recursion
+@^recursion@>
+is never more than two levels deep.
+
+@<Error handling...@>=
+procedure@?normalize_selector; forward;@t\2@>@/
+procedure@?get_next; forward;@t\2@>@/
+procedure@?term_input; forward;@t\2@>@/
+procedure@?show_context; forward;@t\2@>@/
+procedure@?begin_file_reading; forward;@t\2@>@/
+procedure@?open_log_file; forward;@t\2@>@/
+procedure@?close_files_and_terminate; forward;@t\2@>@/
+procedure@?clear_for_error_prompt; forward;@t\2@>@/
+@t\4\hskip-\fontdimen2\font@>@;@+@!debug@+procedure@?debug_help;
+ forward;@;@+gubed@;@/
+@t\4@>@<Declare the procedure called |flush_string|@>
+
+@ Individual lines of help are recorded in the array |help_line|, which
+contains entries in positions |0..(help_ptr-1)|. They should be printed
+in reverse order, i.e., with |help_line[0]| appearing last.
+
+@d hlp1(#)==help_line[0]:=#;@+end
+@d hlp2(#)==help_line[1]:=#; hlp1
+@d hlp3(#)==help_line[2]:=#; hlp2
+@d hlp4(#)==help_line[3]:=#; hlp3
+@d hlp5(#)==help_line[4]:=#; hlp4
+@d hlp6(#)==help_line[5]:=#; hlp5
+@d help0==help_ptr:=0 {sometimes there might be no help}
+@d help1==@+begin help_ptr:=1; hlp1 {use this with one help line}
+@d help2==@+begin help_ptr:=2; hlp2 {use this with two help lines}
+@d help3==@+begin help_ptr:=3; hlp3 {use this with three help lines}
+@d help4==@+begin help_ptr:=4; hlp4 {use this with four help lines}
+@d help5==@+begin help_ptr:=5; hlp5 {use this with five help lines}
+@d help6==@+begin help_ptr:=6; hlp6 {use this with six help lines}
+
+@<Glob...@>=
+@!help_line:array[0..5] of str_number; {helps for the next |error|}
+@!help_ptr:0..6; {the number of help lines present}
+@!use_err_help:boolean; {should the |err_help| string be shown?}
+@!err_help:str_number; {a string set up by \&{errhelp}}
+
+@ @<Set init...@>=
+help_ptr:=0; use_err_help:=false; err_help:=0;
+
+@ The |jump_out| procedure just cuts across all active procedure levels and
+goes to |end_of_MF|. This is the only nontrivial |@!goto| statement in the
+whole program. It is used when there is no recovery from a particular error.
+
+Some \PASCAL\ compilers do not implement non-local |goto| statements.
+@^system dependencies@>
+In such cases the body of |jump_out| should simply be
+`|close_files_and_terminate|;\thinspace' followed by a call on some system
+procedure that quietly terminates the program.
+
+@<Error hand...@>=
+procedure jump_out;
+begin goto end_of_MF;
+end;
+
+@ Here now is the general |error| routine.
+
+@<Error hand...@>=
+procedure error; {completes the job of error reporting}
+label continue,exit;
+var @!c:ASCII_code; {what the user types}
+@!s1,@!s2,@!s3:integer; {used to save global variables when deleting tokens}
+@!j:pool_pointer; {character position being printed}
+begin if history<error_message_issued then history:=error_message_issued;
+print_char("."); show_context;
+if interaction=error_stop_mode then @<Get user's advice and |return|@>;
+incr(error_count);
+if error_count=100 then
+ begin print_nl("(That makes 100 errors; please try again.)");
+@.That makes 100 errors...@>
+ history:=fatal_error_stop; jump_out;
+ end;
+@<Put help message on the transcript file@>;
+exit:end;
+
+@ @<Get user's advice...@>=
+loop@+begin continue: clear_for_error_prompt; prompt_input("? ");
+@.?\relax@>
+ if last=first then return;
+ c:=buffer[first];
+ if c>="a" then c:=c+"A"-"a"; {convert to uppercase}
+ @<Interpret code |c| and |return| if done@>;
+ end
+
+@ It is desirable to provide an `\.E' option here that gives the user
+an easy way to return from \MF\ to the system editor, with the offending
+line ready to be edited. But such an extension requires some system
+wizardry, so the present implementation simply types out the name of the
+file that should be
+edited and the relevant line number.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines haven't
+been commented~out.
+@^debugging@>
+
+@<Interpret code |c| and |return| if done@>=
+case c of
+"0","1","2","3","4","5","6","7","8","9": if deletions_allowed then
+ @<Delete |c-"0"| tokens and |goto continue|@>;
+@t\4\4@>@;@+@!debug "D":begin debug_help;goto continue;@+end;@+gubed@/
+"E": if file_ptr>0 then
+ begin print_nl("You want to edit file ");
+@.You want to edit file x@>
+ slow_print(input_stack[file_ptr].name_field);
+ print(" at line "); print_int(line);@/
+ interaction:=scroll_mode; jump_out;
+ end;
+"H": @<Print the help information and |goto continue|@>;
+"I":@<Introduce new material from the terminal and |return|@>;
+"Q","R","S":@<Change the interaction level and |return|@>;
+"X":begin interaction:=scroll_mode; jump_out;
+ end;
+othercases do_nothing
+endcases;@/
+@<Print the menu of available options@>
+
+@ @<Print the menu...@>=
+begin print("Type <return> to proceed, S to scroll future error messages,");@/
+@.Type <return> to proceed...@>
+print_nl("R to run without stopping, Q to run quietly,");@/
+print_nl("I to insert something, ");
+if file_ptr>0 then print("E to edit your file,");
+if deletions_allowed then
+ print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
+print_nl("H for help, X to quit.");
+end
+
+@ Here the author of \MF\ apologizes for making use of the numerical
+relation between |"Q"|, |"R"|, |"S"|, and the desired interaction settings
+|batch_mode|, |nonstop_mode|, |scroll_mode|.
+@^Knuth, Donald Ervin@>
+
+@<Change the interaction...@>=
+begin error_count:=0; interaction:=batch_mode+c-"Q";
+print("OK, entering ");
+case c of
+"Q":begin print("batchmode"); decr(selector);
+ end;
+"R":print("nonstopmode");
+"S":print("scrollmode");
+end; {there are no other cases}
+print("..."); print_ln; update_terminal; return;
+end
+
+@ When the following code is executed, |buffer[(first+1)..(last-1)]| may
+contain the material inserted by the user; otherwise another prompt will
+be given. In order to understand this part of the program fully, you need
+to be familiar with \MF's input stacks.
+
+@<Introduce new material...@>=
+begin begin_file_reading; {enter a new syntactic level for terminal input}
+if last>first+1 then
+ begin loc:=first+1; buffer[first]:=" ";
+ end
+else begin prompt_input("insert>"); loc:=first;
+@.insert>@>
+ end;
+first:=last+1; cur_input.limit_field:=last; return;
+end
+
+@ We allow deletion of up to 99 tokens at a time.
+
+@<Delete |c-"0"| tokens...@>=
+begin s1:=cur_cmd; s2:=cur_mod; s3:=cur_sym; OK_to_interrupt:=false;
+if (last>first+1) and (buffer[first+1]>="0")and(buffer[first+1]<="9") then
+ c:=c*10+buffer[first+1]-"0"*11
+else c:=c-"0";
+while c>0 do
+ begin get_next; {one-level recursive call of |error| is possible}
+ @<Decrease the string reference count, if the current token is a string@>;
+ decr(c);
+ end;
+cur_cmd:=s1; cur_mod:=s2; cur_sym:=s3; OK_to_interrupt:=true;
+help2("I have just deleted some text, as you asked.")@/
+("You can now delete more, or insert, or whatever.");
+show_context; goto continue;
+end
+
+@ @<Print the help info...@>=
+begin if use_err_help then
+ begin @<Print the string |err_help|, possibly on several lines@>;
+ use_err_help:=false;
+ end
+else begin if help_ptr=0 then
+ help2("Sorry, I don't know how to help in this situation.")@/
+ @t\kern1em@>("Maybe you should try asking a human?");
+ repeat decr(help_ptr); print(help_line[help_ptr]); print_ln;
+ until help_ptr=0;
+ end;
+help4("Sorry, I already gave what help I could...")@/
+ ("Maybe you should try asking a human?")@/
+ ("An error might have occurred before I noticed any problems.")@/
+ ("``If all else fails, read the instructions.''");@/
+goto continue;
+end
+
+@ @<Print the string |err_help|, possibly on several lines@>=
+j:=str_start[err_help];
+while j<str_start[err_help+1] do
+ begin if str_pool[j]<>si("%") then print(so(str_pool[j]))
+ else if j+1=str_start[err_help+1] then print_ln
+ else if str_pool[j+1]<>si("%") then print_ln
+ else begin incr(j); print_char("%");
+ end;
+ incr(j);
+ end
+
+@ @<Put help message on the transcript file@>=
+if interaction>batch_mode then decr(selector); {avoid terminal output}
+if use_err_help then
+ begin print_nl("");
+ @<Print the string |err_help|, possibly on several lines@>;
+ end
+else while help_ptr>0 do
+ begin decr(help_ptr); print_nl(help_line[help_ptr]);
+ end;
+print_ln;
+if interaction>batch_mode then incr(selector); {re-enable terminal output}
+print_ln
+
+@ In anomalous cases, the print selector might be in an unknown state;
+the following subroutine is called to fix things just enough to keep
+running a bit longer.
+
+@p procedure normalize_selector;
+begin if log_opened then selector:=term_and_log
+else selector:=term_only;
+if job_name=0 then open_log_file;
+if interaction=batch_mode then decr(selector);
+end;
+
+@ The following procedure prints \MF's last words before dying.
+
+@d succumb==begin if interaction=error_stop_mode then
+ interaction:=scroll_mode; {no more interaction}
+ if log_opened then error;
+ @!debug if interaction>batch_mode then debug_help;@;@+gubed@;@/
+ history:=fatal_error_stop; jump_out; {irrecoverable error}
+ end
+
+@<Error hand...@>=
+procedure fatal_error(@!s:str_number); {prints |s|, and that's it}
+begin normalize_selector;@/
+print_err("Emergency stop"); help1(s); succumb;
+@.Emergency stop@>
+end;
+
+@ Here is the most dreaded error message.
+
+@<Error hand...@>=
+procedure overflow(@!s:str_number;@!n:integer); {stop due to finiteness}
+begin normalize_selector;
+print_err("METAFONT capacity exceeded, sorry [");
+@.METAFONT capacity exceeded ...@>
+print(s); print_char("="); print_int(n); print_char("]");
+help2("If you really absolutely need more capacity,")@/
+ ("you can ask a wizard to enlarge me.");
+succumb;
+end;
+
+@ The program might sometime run completely amok, at which point there is
+no choice but to stop. If no previous error has been detected, that's bad
+news; a message is printed that is really intended for the \MF\
+maintenance person instead of the user (unless the user has been
+particularly diabolical). The index entries for `this can't happen' may
+help to pinpoint the problem.
+@^dry rot@>
+
+@<Error hand...@>=
+procedure confusion(@!s:str_number);
+ {consistency check violated; |s| tells where}
+begin normalize_selector;
+if history<error_message_issued then
+ begin print_err("This can't happen ("); print(s); print_char(")");
+@.This can't happen@>
+ help1("I'm broken. Please show this to someone who can fix can fix");
+ end
+else begin print_err("I can't go on meeting you like this");
+@.I can't go on...@>
+ help2("One of your faux pas seems to have wounded me deeply...")@/
+ ("in fact, I'm barely conscious. Please fix it and try again.");
+ end;
+succumb;
+end;
+
+@ Users occasionally want to interrupt \MF\ while it's running.
+If the \PASCAL\ runtime system allows this, one can implement
+a routine that sets the global variable |interrupt| to some nonzero value
+when such an interrupt is signalled. Otherwise there is probably at least
+a way to make |interrupt| nonzero using the \PASCAL\ debugger.
+@^system dependencies@>
+@^debugging@>
+
+@d check_interrupt==begin if interrupt<>0 then pause_for_instructions;
+ end
+
+@<Global...@>=
+@!interrupt:integer; {should \MF\ pause for instructions?}
+@!OK_to_interrupt:boolean; {should interrupts be observed?}
+
+@ @<Set init...@>=
+interrupt:=0; OK_to_interrupt:=true;
+
+@ When an interrupt has been detected, the program goes into its
+highest interaction level and lets the user have the full flexibility of
+the |error| routine. \MF\ checks for interrupts only at times when it is
+safe to do this.
+
+@p procedure pause_for_instructions;
+begin if OK_to_interrupt then
+ begin interaction:=error_stop_mode;
+ if (selector=log_only)or(selector=no_print) then
+ incr(selector);
+ print_err("Interruption");
+@.Interruption@>
+ help3("You rang?")@/
+ ("Try to insert some instructions for me (e.g.,`I show x'),")@/
+ ("unless you just want to quit by typing `X'.");
+ deletions_allowed:=false; error; deletions_allowed:=true;
+ interrupt:=0;
+ end;
+end;
+
+@ Many of \MF's error messages state that a missing token has been
+inserted behind the scenes. We can save string space and program space
+by putting this common code into a subroutine.
+
+@p procedure missing_err(@!s:str_number);
+begin print_err("Missing `"); print(s); print("' has been inserted");
+@.Missing...inserted@>
+end;
+
+@* \[7] Arithmetic with scaled numbers.
+The principal computations performed by \MF\ are done entirely in terms of
+integers less than $2^{31}$ in magnitude; thus, the arithmetic specified in this
+program can be carried out in exactly the same way on a wide variety of
+computers, including some small ones.
+@^small computers@>
+
+But \PASCAL\ does not define the @!|div|
+operation in the case of negative dividends; for example, the result of
+|(-2*n-1) div 2| is |-(n+1)| on some computers and |-n| on others.
+There are two principal types of arithmetic: ``translation-preserving,''
+in which the identity |(a+q*b)div b=(a div b)+q| is valid; and
+``negation-preserving,'' in which |(-a)div b=-(a div b)|. This leads to
+two \MF s, which can produce different results, although the differences
+should be negligible when the language is being used properly.
+The \TeX\ processor has been defined carefully so that both varieties
+of arithmetic will produce identical output, but it would be too
+inefficient to constrain \MF\ in a similar way.
+
+@d el_gordo == @'17777777777 {$2^{31}-1$, the largest value that \MF\ likes}
+
+@ One of \MF's most common operations is the calculation of
+$\lfloor{a+b\over2}\rfloor$,
+the midpoint of two given integers |a| and~|b|. The only decent way to do
+this in \PASCAL\ is to write `|(a+b) div 2|'; but on most machines it is
+far more efficient to calculate `|(a+b)| right shifted one bit'.
+
+Therefore the midpoint operation will always be denoted by `|half(a+b)|'
+in this program. If \MF\ is being implemented with languages that permit
+binary shifting, the |half| macro should be changed to make this operation
+as efficient as possible.
+
+@d half(#)==(#) div 2
+
+@ A single computation might use several subroutine calls, and it is
+desirable to avoid producing multiple error messages in case of arithmetic
+overflow. So the routines below set the global variable |arith_error| to |true|
+instead of reporting errors directly to the user.
+@^overflow in arithmetic@>
+
+@<Glob...@>=
+@!arith_error:boolean; {has arithmetic overflow occurred recently?}
+
+@ @<Set init...@>=
+arith_error:=false;
+
+@ At crucial points the program will say |check_arith|, to test if
+an arithmetic error has been detected.
+
+@d check_arith==begin if arith_error then clear_arith;@+end
+
+@p procedure clear_arith;
+begin print_err("Arithmetic overflow");
+@.Arithmetic overflow@>
+help4("Uh, oh. A little while ago one of the quantities that I was")@/
+ ("computing got too large, so I'm afraid your answers will be")@/
+ ("somewhat askew. You'll probably have to adopt different")@/
+ ("tactics next time. But I shall try to carry on anyway.");
+error; arith_error:=false;
+end;
+
+@ Addition is not always checked to make sure that it doesn't overflow,
+but in places where overflow isn't too unlikely the |slow_add| routine
+is used.
+
+@p function slow_add(@!x,@!y:integer):integer;
+begin if x>=0 then
+ if y<=el_gordo-x then slow_add:=x+y
+ else begin arith_error:=true; slow_add:=el_gordo;
+ end
+else if -y<=el_gordo+x then slow_add:=x+y
+ else begin arith_error:=true; slow_add:=-el_gordo;
+ end;
+end;
+
+@ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples
+of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit
+positions from the right end of a binary computer word.
+
+@d quarter_unit == @'40000 {$2^{14}$, represents 0.250000}
+@d half_unit == @'100000 {$2^{15}$, represents 0.50000}
+@d three_quarter_unit == @'140000 {$3\cdot2^{14}$, represents 0.75000}
+@d unity == @'200000 {$2^{16}$, represents 1.00000}
+@d two == @'400000 {$2^{17}$, represents 2.00000}
+@d three == @'600000 {$2^{17}+2^{16}$, represents 3.00000}
+
+@<Types...@>=
+@!scaled = integer; {this type is used for scaled integers}
+@!small_number=0..63; {this type is self-explanatory}
+
+@ The following function is used to create a scaled integer from a given decimal
+fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is
+given in |dig[i]|, and the calculation produces a correctly rounded result.
+
+@p function round_decimals(@!k:small_number) : scaled;
+ {converts a decimal fraction}
+var @!a:integer; {the accumulator}
+begin a:=0;
+while k>0 do
+ begin decr(k); a:=(a+dig[k]*two) div 10;
+ end;
+round_decimals:=half(a+1);
+end;
+
+@ Conversely, here is a procedure analogous to |print_int|. If the output
+of this procedure is subsequently read by \MF\ and converted by the
+|round_decimals| routine above, it turns out that the original value will
+be reproduced exactly. A decimal point is printed only if the value is
+not an integer. If there is more than one way to print the result with
+the optimum number of digits following the decimal point, the closest
+possible value is given.
+
+The invariant relation in the \&{repeat} loop is that a sequence of
+decimal digits yet to be printed will yield the original number if and only if
+they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
+We can stop if and only if $f=0$ satisfies this condition; the loop will
+terminate before $s$ can possibly become zero.
+
+@<Basic printing...@>=
+procedure print_scaled(@!s:scaled); {prints scaled real, rounded to five
+ digits}
+var @!delta:scaled; {amount of allowable inaccuracy}
+begin if s<0 then
+ begin print_char("-"); negate(s); {print the sign, if negative}
+ end;
+print_int(s div unity); {print the integer part}
+s:=10*(s mod unity)+5;
+if s<>5 then
+ begin delta:=10; print_char(".");
+ repeat if delta>unity then
+ s:=s+@'100000-(delta div 2); {round the final digit}
+ print_char("0"+(s div unity)); s:=10*(s mod unity); delta:=delta*10;
+ until s<=delta;
+ end;
+end;
+
+@ We often want to print two scaled quantities in parentheses,
+separated by a comma.
+
+@<Basic printing...@>=
+procedure print_two(@!x,@!y:scaled); {prints `|(x,y)|'}
+begin print_char("("); print_scaled(x); print_char(","); print_scaled(y);
+print_char(")");
+end;
+
+@ The |scaled| quantities in \MF\ programs are generally supposed to be
+less than $2^{12}$ in absolute value, so \MF\ does much of its internal
+arithmetic with 28~significant bits of precision. A |fraction| denotes
+a scaled integer whose binary point is assumed to be 28 bit positions
+from the right.
+
+@d fraction_half==@'1000000000 {$2^{27}$, represents 0.50000000}
+@d fraction_one==@'2000000000 {$2^{28}$, represents 1.00000000}
+@d fraction_two==@'4000000000 {$2^{29}$, represents 2.00000000}
+@d fraction_three==@'6000000000 {$3\cdot2^{28}$, represents 3.00000000}
+@d fraction_four==@'10000000000 {$2^{30}$, represents 4.00000000}
+
+@<Types...@>=
+@!fraction=integer; {this type is used for scaled fractions}
+
+@ In fact, the two sorts of scaling discussed above aren't quite
+sufficient; \MF\ has yet another, used internally to keep track of angles
+in units of $2^{-20}$ degrees.
+
+@d forty_five_deg==@'264000000 {$45\cdot2^{20}$, represents $45^\circ$}
+@d ninety_deg==@'550000000 {$90\cdot2^{20}$, represents $90^\circ$}
+@d one_eighty_deg==@'1320000000 {$180\cdot2^{20}$, represents $180^\circ$}
+@d three_sixty_deg==@'2640000000 {$360\cdot2^{20}$, represents $360^\circ$}
+
+@<Types...@>=
+@!angle=integer; {this type is used for scaled angles}
+
+@ The |make_fraction| routine produces the |fraction| equivalent of
+|p/q|, given integers |p| and~|q|; it computes the integer
+$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are
+positive. If |p| and |q| are both of the same scaled type |t|,
+the ``type relation'' |make_fraction(t,t)=fraction| is valid;
+and it's also possible to use the subroutine ``backwards,'' using
+the relation |make_fraction(t,fraction)=t| between scaled types.
+
+If the result would have magnitude $2^{31}$ or more, |make_fraction|
+sets |arith_error:=true|. Most of \MF's internal computations have
+been designed to avoid this sort of error.
+
+Notice that if 64-bit integer arithmetic were available,
+we could simply compute |(@t$(2^{29}$@>*p+q)div (2*q)|.
+But when we are restricted to \PASCAL's 32-bit arithmetic we
+must either resort to multiple-precision maneuvering
+or use a simple but slow iteration. The multiple-precision technique
+would be about three times faster than the code adopted here, but it
+would be comparatively long and tricky, involving about sixteen
+additional multiplications and divisions.
+
+This operation is part of \MF's ``inner loop''; indeed, it will
+consume nearly 10\pct! of the running time (exclusive of input and output)
+if the code below is left unchanged. A machine-dependent recoding
+will therefore make \MF\ run faster. The present implementation
+is highly portable, but slow; it avoids multiplication and division
+except in the initial stage. System wizards should be careful to
+replace it with a routine that is guaranteed to produce identical
+results in all cases.
+@^system dependencies@>
+
+As noted below, a few more routines should also be replaced by machine-dependent
+code, for efficiency. But when a procedure is not part of the ``inner loop,''
+such changes aren't advisable; simplicity and robustness are
+preferable to trickery, unless the cost is too high.
+@^inner loop@>
+
+@p function make_fraction(@!p,@!q:integer):fraction;
+var @!f:integer; {the fraction bits, with a leading 1 bit}
+@!n:integer; {the integer part of $\vert p/q\vert$}
+@!negative:boolean; {should the result be negated?}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin if p>=0 then negative:=false
+else begin negate(p); negative:=true;
+ end;
+if q<=0 then
+ begin debug if q=0 then confusion("/");@;@+gubed@;@/
+@:this can't happen /}{\quad \./@>
+ negate(q); negative:=not negative;
+ end;
+n:=p div q; p:=p mod q;
+if n>=8 then
+ begin arith_error:=true;
+ if negative then make_fraction:=-el_gordo@+else make_fraction:=el_gordo;
+ end
+else begin n:=(n-1)*fraction_one;
+ @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>;
+ if negative then make_fraction:=-(f+n)@+else make_fraction:=f+n;
+ end;
+end;
+
+@ The |repeat| loop here preserves the following invariant relations
+between |f|, |p|, and~|q|:
+(i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and
+$p_0$ is the original value of~$p$.
+
+Notice that the computation specifies
+|(p-q)+p| instead of |(p+p)-q|, because the latter could overflow.
+Let us hope that optimizing compilers do not miss this point; a
+special variable |be_careful| is used to emphasize the necessary
+order of computation. Optimizing compilers should keep |be_careful|
+in a register, not store it in memory.
+@^inner loop@>
+
+@<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>=
+f:=1;
+repeat be_careful:=p-q; p:=be_careful+p;
+if p>=0 then f:=f+f+1
+else begin double(f); p:=p+q;
+ end;
+until f>=fraction_one;
+be_careful:=p-q;
+if be_careful+p>=0 then incr(f)
+
+@ The dual of |make_fraction| is |take_fraction|, which multiplies a
+given integer~|q| by a fraction~|f|. When the operands are positive, it
+computes $p=\lfloor qf/2^{28}+{1\over2}\rfloor$, a symmetric function
+of |q| and~|f|.
+
+This routine is even more ``inner loopy'' than |make_fraction|;
+the present implementation consumes almost 20\pct! of \MF's computation
+time during typical jobs, so a machine-language or 64-bit
+substitute is advisable.
+@^inner loop@> @^system dependencies@>
+
+@p function take_fraction(@!q:integer;@!f:fraction):integer;
+var @!p:integer; {the fraction so far}
+@!negative:boolean; {should the result be negated?}
+@!n:integer; {additional multiple of $q$}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin @<Reduce to the case that |f>=0| and |q>=0|@>;
+if f<fraction_one then n:=0
+else begin n:=f div fraction_one; f:=f mod fraction_one;
+ if q<=el_gordo div n then n:=n*q
+ else begin arith_error:=true; n:=el_gordo;
+ end;
+ end;
+f:=f+fraction_one;
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>;
+be_careful:=n-el_gordo;
+if be_careful+p>0 then
+ begin arith_error:=true; n:=el_gordo-p;
+ end;
+if negative then take_fraction:=-(n+p)
+else take_fraction:=n+p;
+end;
+
+@ @<Reduce to the case that |f>=0| and |q>=0|@>=
+if f>=0 then negative:=false
+else begin negate(f); negative:=true;
+ end;
+if q<0 then
+ begin negate(q); negative:=not negative;
+ end;
+
+@ The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor
+=\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and
+$f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$.
+@^inner loop@>
+
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>=
+p:=fraction_half; {that's $2^{27}$; the invariants hold now with $k=28$}
+if q<fraction_four then
+ repeat if odd(f) then p:=half(p+q)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+else repeat if odd(f) then p:=p+half(q-p)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+
+
+@ When we want to multiply something by a |scaled| quantity, we use a scheme
+analogous to |take_fraction| but with a different scaling.
+Given positive operands, |take_scaled|
+computes the quantity $p=\lfloor qf/2^{16}+{1\over2}\rfloor$.
+
+Once again it is a good idea to use 64-bit arithmetic if
+possible; otherwise |take_scaled| will use more than 2\pct! of the running time
+when the Computer Modern fonts are being generated.
+@^inner loop@>
+
+@p function take_scaled(@!q:integer;@!f:scaled):integer;
+var @!p:integer; {the fraction so far}
+@!negative:boolean; {should the result be negated?}
+@!n:integer; {additional multiple of $q$}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin @<Reduce to the case that |f>=0| and |q>=0|@>;
+if f<unity then n:=0
+else begin n:=f div unity; f:=f mod unity;
+ if q<=el_gordo div n then n:=n*q
+ else begin arith_error:=true; n:=el_gordo;
+ end;
+ end;
+f:=f+unity;
+@<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>;
+be_careful:=n-el_gordo;
+if be_careful+p>0 then
+ begin arith_error:=true; n:=el_gordo-p;
+ end;
+if negative then take_scaled:=-(n+p)
+else take_scaled:=n+p;
+end;
+
+@ @<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>=
+p:=half_unit; {that's $2^{15}$; the invariants hold now with $k=16$}
+@^inner loop@>
+if q<fraction_four then
+ repeat if odd(f) then p:=half(p+q)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+else repeat if odd(f) then p:=p+half(q-p)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+
+@ For completeness, there's also |make_scaled|, which computes a
+quotient as a |scaled| number instead of as a |fraction|.
+In other words, the result is $\lfloor2^{16}p/q+{1\over2}\rfloor$, if the
+operands are positive. \ (This procedure is not used especially often,
+so it is not part of \MF's inner loop.)
+
+@p function make_scaled(@!p,@!q:integer):scaled;
+var @!f:integer; {the fraction bits, with a leading 1 bit}
+@!n:integer; {the integer part of $\vert p/q\vert$}
+@!negative:boolean; {should the result be negated?}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin if p>=0 then negative:=false
+else begin negate(p); negative:=true;
+ end;
+if q<=0 then
+ begin debug if q=0 then confusion("/");@+gubed@;@/
+@:this can't happen /}{\quad \./@>
+ negate(q); negative:=not negative;
+ end;
+n:=p div q; p:=p mod q;
+if n>=@'100000 then
+ begin arith_error:=true;
+ if negative then make_scaled:=-el_gordo@+else make_scaled:=el_gordo;
+ end
+else begin n:=(n-1)*unity;
+ @<Compute $f=\lfloor 2^{16}(1+p/q)+{1\over2}\rfloor$@>;
+ if negative then make_scaled:=-(f+n)@+else make_scaled:=f+n;
+ end;
+end;
+
+@ @<Compute $f=\lfloor 2^{16}(1+p/q)+{1\over2}\rfloor$@>=
+f:=1;
+repeat be_careful:=p-q; p:=be_careful+p;
+if p>=0 then f:=f+f+1
+else begin double(f); p:=p+q;
+ end;
+until f>=unity;
+be_careful:=p-q;
+if be_careful+p>=0 then incr(f)
+
+@ Here is a typical example of how the routines above can be used.
+It computes the function
+$${1\over3\tau}f(\theta,\phi)=
+{\tau^{-1}\bigl(2+\sqrt2\,(\sin\theta-{1\over16}\sin\phi)
+ (\sin\phi-{1\over16}\sin\theta)(\cos\theta-\cos\phi)\bigr)\over
+3\,\bigl(1+{1\over2}(\sqrt5-1)\cos\theta+{1\over2}(3-\sqrt5\,)\cos\phi\bigr)},$$
+where $\tau$ is a |scaled| ``tension'' parameter. This is \MF's magic
+fudge factor for placing the first control point of a curve that starts
+at an angle $\theta$ and ends at an angle $\phi$ from the straight path.
+(Actually, if the stated quantity exceeds 4, \MF\ reduces it to~4.)
+
+The trigonometric quantity to be multiplied by $\sqrt2$ is less than $\sqrt2$.
+(It's a sum of eight terms whose absolute values can be bounded using
+relations such as $\sin\theta\cos\theta\L{1\over2}$.) Thus the numerator
+is positive; and since the tension $\tau$ is constrained to be at least
+$3\over4$, the numerator is less than $16\over3$. The denominator is
+nonnegative and at most~6. Hence the fixed-point calculations below
+are guaranteed to stay within the bounds of a 32-bit computer word.
+
+The angles $\theta$ and $\phi$ are given implicitly in terms of |fraction|
+arguments |st|, |ct|, |sf|, and |cf|, representing $\sin\theta$, $\cos\theta$,
+$\sin\phi$, and $\cos\phi$, respectively.
+
+@p function velocity(@!st,@!ct,@!sf,@!cf:fraction;@!t:scaled):fraction;
+var @!acc,@!num,@!denom:integer; {registers for intermediate calculations}
+begin acc:=take_fraction(st-(sf div 16), sf-(st div 16));
+acc:=take_fraction(acc,ct-cf);
+num:=fraction_two+take_fraction(acc,379625062);
+ {$2^{28}\sqrt2\approx379625062.497$}
+denom:=fraction_three+take_fraction(ct,497706707)+take_fraction(cf,307599661);
+ {$3\cdot2^{27}\cdot(\sqrt5-1)\approx497706706.78$ and
+ $3\cdot2^{27}\cdot(3-\sqrt5\,)\approx307599661.22$}
+if t<>unity then num:=make_scaled(num,t);
+ {|make_scaled(fraction,scaled)=fraction|}
+if num div 4>=denom then velocity:=fraction_four
+else velocity:=make_fraction(num,denom);
+end;
+
+@ The following somewhat different subroutine tests rigorously if $ab$ is
+greater than, equal to, or less than~$cd$,
+given integers $(a,b,c,d)$. In most cases a quick decision is reached.
+The result is $+1$, 0, or~$-1$ in the three respective cases.
+
+@d return_sign(#)==begin ab_vs_cd:=#; return;
+ end
+
+@p function ab_vs_cd(@!a,b,c,d:integer):integer;
+label exit;
+var @!q,@!r:integer; {temporary registers}
+begin @<Reduce to the case that |a,c>=0|, |b,d>0|@>;
+loop@+ begin q := a div d; r := c div b;
+ if q<>r then
+ if q>r then return_sign(1)@+else return_sign(-1);
+ q := a mod d; r := c mod b;
+ if r=0 then
+ if q=0 then return_sign(0)@+else return_sign(1);
+ if q=0 then return_sign(-1);
+ a:=b; b:=q; c:=d; d:=r;
+ end; {now |a>d>0| and |c>b>0|}
+exit:end;
+
+@ @<Reduce to the case that |a...@>=
+if a<0 then
+ begin negate(a); negate(b);
+ end;
+if c<0 then
+ begin negate(c); negate(d);
+ end;
+if d<=0 then
+ begin if b>=0 then
+ if ((a=0)or(b=0))and((c=0)or(d=0)) then return_sign(0)
+ else return_sign(1);
+ if d=0 then
+ if a=0 then return_sign(0)@+else return_sign(-1);
+ q:=a; a:=c; c:=q; q:=-b; b:=-d; d:=q;
+ end
+else if b<=0 then
+ begin if b<0 then if a>0 then return_sign(-1);
+ if c=0 then return_sign(0) else return_sign(-1);
+ end
+
+@ We conclude this set of elementary routines with some simple rounding
+and truncation operations that are coded in a machine-independent fashion.
+The routines are slightly complicated because we want them to work
+without overflow whenever $-2^{31}\L x<2^{31}$.
+
+@p function floor_scaled(@!x:scaled):scaled;
+ {$2^{16}\lfloor x/2^{16}\rfloor$}
+var @!be_careful:integer; {temporary register}
+begin if x>=0 then floor_scaled:=x-(x mod unity)
+else begin be_careful:=x+1;
+ floor_scaled:=x+((-be_careful) mod unity)+1-unity;
+ end;
+end;
+@#
+function floor_unscaled(@!x:scaled):integer;
+ {$\lfloor x/2^{16}\rfloor$}
+var @!be_careful:integer; {temporary register}
+begin if x>=0 then floor_unscaled:=x div unity
+else begin be_careful:=x+1; floor_unscaled:=-(1+((-be_careful) div unity));
+ end;
+end;
+@#
+function round_unscaled(@!x:scaled):integer;
+ {$\lfloor x/2^{16}+.5\rfloor$}
+var @!be_careful:integer; {temporary register}
+begin if x>=half_unit then round_unscaled:=1+((x-half_unit) div unity)
+else if x>=-half_unit then round_unscaled:=0
+else begin be_careful:=x+1;
+ round_unscaled:=-(1+((-be_careful-half_unit) div unity));
+ end;
+end;
+@#
+function round_fraction(@!x:fraction):scaled;
+ {$\lfloor x/2^{12}+.5\rfloor$}
+var @!be_careful:integer; {temporary register}
+begin if x>=2048 then round_fraction:=1+((x-2048) div 4096)
+else if x>=-2048 then round_fraction:=0
+else begin be_careful:=x+1;
+ round_fraction:=-(1+((-be_careful-2048) div 4096));
+ end;
+end;
+
+@* \[8] Algebraic and transcendental functions.
+\MF\ computes all of the necessary special functions from scratch, without
+relying on |real| arithmetic or system subroutines for sines, cosines, etc.
+
+@ To get the square root of a |scaled| number |x|, we want to calculate
+$s=\lfloor 2^8\!\sqrt x +{1\over2}\rfloor$. If $x>0$, this is the unique
+integer such that $2^{16}x-s\L s^2<2^{16}x+s$. The following subroutine
+determines $s$ by an iterative method that maintains the invariant
+relations $x=2^{46-2k}x_0\bmod 2^{30}$, $0<y=\lfloor 2^{16-2k}x_0\rfloor
+-s^2+s\L q=2s$, where $x_0$ is the initial value of $x$. The value of~$y$
+might, however, be zero at the start of the first iteration.
+
+@p function square_rt(@!x:scaled):scaled;
+var @!k:small_number; {iteration control counter}
+@!y,@!q:integer; {registers for intermediate calculations}
+begin if x<=0 then @<Handle square root of zero or negative argument@>
+else begin k:=23; q:=2;
+ while x<fraction_two do {i.e., |while x<@t$2^{29}$@>|\unskip}
+ begin decr(k); x:=x+x+x+x;
+ end;
+ if x<fraction_four then y:=0
+ else begin x:=x-fraction_four; y:=1;
+ end;
+ repeat @<Decrease |k| by 1, maintaining the invariant
+ relations between |x|, |y|, and~|q|@>;
+ until k=0;
+ square_rt:=half(q);
+ end;
+end;
+
+@ @<Handle square root of zero...@>=
+begin if x<0 then
+ begin print_err("Square root of ");
+@.Square root...replaced by 0@>
+ print_scaled(x); print(" has been replaced by 0");
+ help2("Since I don't take square roots of negative numbers,")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+ error;
+ end;
+square_rt:=0;
+end
+
+@ @<Decrease |k| by 1, maintaining...@>=
+double(x); double(y);
+if x>=fraction_four then {note that |fraction_four=@t$2^{30}$@>|}
+ begin x:=x-fraction_four; incr(y);
+ end;
+double(x); y:=y+y-q; double(q);
+if x>=fraction_four then
+ begin x:=x-fraction_four; incr(y);
+ end;
+if y>q then
+ begin y:=y-q; q:=q+2;
+ end
+else if y<=0 then
+ begin q:=q-2; y:=y+q;
+ end;
+decr(k)
+
+@ Pythagorean addition $\psqrt{a^2+b^2}$ is implemented by an elegant
+iterative scheme due to Cleve Moler and Donald Morrison [{\sl IBM Journal
+@^Moler, Cleve Barry@>
+@^Morrison, Donald Ross@>
+of Research and Development\/ \bf27} (1983), 577--581]. It modifies |a| and~|b|
+in such a way that their Pythagorean sum remains invariant, while the
+smaller argument decreases.
+
+@p function pyth_add(@!a,@!b:integer):integer;
+label done;
+var @!r:fraction; {register used to transform |a| and |b|}
+@!big:boolean; {is the result dangerously near $2^{31}$?}
+begin a:=abs(a); b:=abs(b);
+if a<b then
+ begin r:=b; b:=a; a:=r;
+ end; {now |0<=b<=a|}
+if b>0 then
+ begin if a<fraction_two then big:=false
+ else begin a:=a div 4; b:=b div 4; big:=true;
+ end; {we reduced the precision to avoid arithmetic overflow}
+ @<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>;
+ if big then
+ if a<fraction_two then a:=a+a+a+a
+ else begin arith_error:=true; a:=el_gordo;
+ end;
+ end;
+pyth_add:=a;
+end;
+
+@ The key idea here is to reflect the vector $(a,b)$ about the
+line through $(a,b/2)$.
+
+@<Replace |a| by an approximation to $\psqrt{a^2+b^2}$@>=
+loop@+ begin r:=make_fraction(b,a);
+ r:=take_fraction(r,r); {now $r\approx b^2/a^2$}
+ if r=0 then goto done;
+ r:=make_fraction(r,fraction_four+r);
+ a:=a+take_fraction(a+a,r); b:=take_fraction(b,r);
+ end;
+done:
+
+@ Here is a similar algorithm for $\psqrt{a^2-b^2}$.
+It converges slowly when $b$ is near $a$, but otherwise it works fine.
+
+@p function pyth_sub(@!a,@!b:integer):integer;
+label done;
+var @!r:fraction; {register used to transform |a| and |b|}
+@!big:boolean; {is the input dangerously near $2^{31}$?}
+begin a:=abs(a); b:=abs(b);
+if a<=b then @<Handle erroneous |pyth_sub| and set |a:=0|@>
+else begin if a<fraction_four then big:=false
+ else begin a:=half(a); b:=half(b); big:=true;
+ end;
+ @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>;
+ if big then a:=a+a;
+ end;
+pyth_sub:=a;
+end;
+
+@ @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>=
+loop@+ begin r:=make_fraction(b,a);
+ r:=take_fraction(r,r); {now $r\approx b^2/a^2$}
+ if r=0 then goto done;
+ r:=make_fraction(r,fraction_four-r);
+ a:=a-take_fraction(a+a,r); b:=take_fraction(b,r);
+ end;
+done:
+
+@ @<Handle erroneous |pyth_sub| and set |a:=0|@>=
+begin if a<b then
+ begin print_err("Pythagorean subtraction "); print_scaled(a);
+ print("+-+"); print_scaled(b); print(" has been replaced by 0");
+@.Pythagorean...@>
+ help2("Since I don't take square roots of negative numbers,")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+ error;
+ end;
+a:=0;
+end
+
+@ The subroutines for logarithm and exponential involve two tables.
+The first is simple: |two_to_the[k]| equals $2^k$. The second involves
+a bit more calculation, which the author claims to have done correctly:
+|spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)=
+2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the
+nearest integer.
+
+@<Glob...@>=
+@!two_to_the:array[0..30] of integer; {powers of two}
+@!spec_log:array[1..28] of integer; {special logarithms}
+
+@ @<Local variables for initialization@>=
+@!k:integer; {all-purpose loop index}
+
+@ @<Set init...@>=
+two_to_the[0]:=1;
+for k:=1 to 30 do two_to_the[k]:=2*two_to_the[k-1];
+spec_log[1]:=93032640;
+spec_log[2]:=38612034;
+spec_log[3]:=17922280;
+spec_log[4]:=8662214;
+spec_log[5]:=4261238;
+spec_log[6]:=2113709;
+spec_log[7]:=1052693;
+spec_log[8]:=525315;
+spec_log[9]:=262400;
+spec_log[10]:=131136;
+spec_log[11]:=65552;
+spec_log[12]:=32772;
+spec_log[13]:=16385;
+for k:=14 to 27 do spec_log[k]:=two_to_the[27-k];
+spec_log[28]:=1;
+
+@ Here is the routine that calculates $2^8$ times the natural logarithm
+of a |scaled| quantity; it is an integer approximation to $2^{24}\ln(x/2^{16})$,
+when |x| is a given positive integer.
+
+The method is based on exercise 1.2.2--25 in {\sl The Art of Computer
+Programming\/}: During the main iteration we have $1\L 2^{-30}x<1/(1-2^{1-k})$,
+and the logarithm of $2^{30}x$ remains to be added to an accumulator
+register called~$y$. Three auxiliary bits of accuracy are retained in~$y$
+during the calculation, and sixteen auxiliary bits to extend |y| are
+kept in~|z| during the initial argument reduction. (We add
+$100\cdot2^{16}=6553600$ to~|z| and subtract 100 from~|y| so that |z| will
+not become negative; also, the actual amount subtracted from~|y| is~96,
+not~100, because we want to add~4 for rounding before the final division by~8.)
+
+@p function m_log(@!x:scaled):scaled;
+var @!y,@!z:integer; {auxiliary registers}
+@!k:integer; {iteration counter}
+begin if x<=0 then @<Handle non-positive logarithm@>
+else begin y:=1302456956+4-100; {$14\times2^{27}\ln2\approx1302456956.421063$}
+ z:=27595+6553600; {and $2^{16}\times .421063\approx 27595$}
+ while x<fraction_four do
+ begin double(x); y:=y-93032639; z:=z-48782;
+ end; {$2^{27}\ln2\approx 93032639.74436163$
+ and $2^{16}\times.74436163\approx 48782$}
+ y:=y+(z div unity); k:=2;
+ while x>fraction_four+4 do
+ @<Increase |k| until |x| can be multiplied by a
+ factor of $2^{-k}$, and adjust $y$ accordingly@>;
+ m_log:=y div 8;
+ end;
+end;
+
+@ @<Increase |k| until |x| can...@>=
+begin z:=((x-1) div two_to_the[k])+1; {$z=\lceil x/2^k\rceil$}
+while x<fraction_four+z do
+ begin z:=half(z+1); k:=k+1;
+ end;
+y:=y+spec_log[k]; x:=x-z;
+end
+
+@ @<Handle non-positive logarithm@>=
+begin print_err("Logarithm of ");
+@.Logarithm...replaced by 0@>
+print_scaled(x); print(" has been replaced by 0");
+help2("Since I don't take logs of non-positive numbers,")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+error; m_log:=0;
+end
+
+@ Conversely, the exponential routine calculates $\exp(x/2^8)$,
+when |x| is |scaled|. The result is an integer approximation to
+$2^{16}\exp(x/2^{24})$, when |x| is regarded as an integer.
+
+@p function m_exp(@!x:scaled):scaled;
+var @!k:small_number; {loop control index}
+@!y,@!z:integer; {auxiliary registers}
+begin if x>174436200 then
+ {$2^{24}\ln((2^{31}-1)/2^{16})\approx 174436199.51$}
+ begin arith_error:=true; m_exp:=el_gordo;
+ end
+else if x<-197694359 then m_exp:=0
+ {$2^{24}\ln(2^{-1}/2^{16})\approx-197694359.45$}
+else begin if x<=0 then
+ begin z:=-8*x; y:=@'4000000; {$y=2^{20}$}
+ end
+ else begin if x<=127919879 then z:=1023359037-8*x
+ {$2^{27}\ln((2^{31}-1)/2^{20})\approx 1023359037.125$}
+ else z:=8*(174436200-x); {|z| is always nonnegative}
+ y:=el_gordo;
+ end;
+ @<Multiply |y| by $\exp(-z/2^{27})$@>;
+ if x<=127919879 then m_exp:=(y+8) div 16@+else m_exp:=y;
+ end;
+end;
+
+@ The idea here is that subtracting |spec_log[k]| from |z| corresponds
+to multiplying |y| by $1-2^{-k}$.
+
+A subtle point (which had to be checked) was that if $x=127919879$, the
+value of~|y| will decrease so that |y+8| doesn't overflow. In fact,
+$z$ will be 5 in this case, and |y| will decrease by~64 when |k=25|
+and by~16 when |k=27|.
+
+@<Multiply |y| by...@>=
+k:=1;
+while z>0 do
+ begin while z>=spec_log[k] do
+ begin z:=z-spec_log[k];
+ y:=y-1-((y-two_to_the[k-1]) div two_to_the[k]);
+ end;
+ incr(k);
+ end
+
+@ The trigonometric subroutines use an auxiliary table such that
+|spec_atan[k]| contains an approximation to the |angle| whose tangent
+is~$1/2^k$.
+
+@<Glob...@>=
+@!spec_atan:array[1..26] of angle; {$\arctan2^{-k}$ times $2^{20}\cdot180/\pi$}
+
+@ @<Set init...@>=
+spec_atan[1]:=27855475;
+spec_atan[2]:=14718068;
+spec_atan[3]:=7471121;
+spec_atan[4]:=3750058;
+spec_atan[5]:=1876857;
+spec_atan[6]:=938658;
+spec_atan[7]:=469357;
+spec_atan[8]:=234682;
+spec_atan[9]:=117342;
+spec_atan[10]:=58671;
+spec_atan[11]:=29335;
+spec_atan[12]:=14668;
+spec_atan[13]:=7334;
+spec_atan[14]:=3667;
+spec_atan[15]:=1833;
+spec_atan[16]:=917;
+spec_atan[17]:=458;
+spec_atan[18]:=229;
+spec_atan[19]:=115;
+spec_atan[20]:=57;
+spec_atan[21]:=29;
+spec_atan[22]:=14;
+spec_atan[23]:=7;
+spec_atan[24]:=4;
+spec_atan[25]:=2;
+spec_atan[26]:=1;
+
+@ Given integers |x| and |y|, not both zero, the |n_arg| function
+returns the |angle| whose tangent points in the direction $(x,y)$.
+This subroutine first determines the correct octant, then solves the
+problem for |0<=y<=x|, then converts the result appropriately to
+return an answer in the range |-one_eighty_deg<=@t$\theta$@><=one_eighty_deg|.
+(The answer is |+one_eighty_deg| if |y=0| and |x<0|, but an answer of
+|-one_eighty_deg| is possible if, for example, |y=-1| and $x=-2^{30}$.)
+
+The octants are represented in a ``Gray code,'' since that turns out
+to be computationally simplest.
+
+@d negate_x=1
+@d negate_y=2
+@d switch_x_and_y=4
+@d first_octant=1
+@d second_octant=first_octant+switch_x_and_y
+@d third_octant=first_octant+switch_x_and_y+negate_x
+@d fourth_octant=first_octant+negate_x
+@d fifth_octant=first_octant+negate_x+negate_y
+@d sixth_octant=first_octant+switch_x_and_y+negate_x+negate_y
+@d seventh_octant=first_octant+switch_x_and_y+negate_y
+@d eighth_octant=first_octant+negate_y
+
+@p function n_arg(@!x,@!y:integer):angle;
+var @!z:angle; {auxiliary register}
+@!t:integer; {temporary storage}
+@!k:small_number; {loop counter}
+@!octant:first_octant..sixth_octant; {octant code}
+begin if x>=0 then octant:=first_octant
+else begin negate(x); octant:=first_octant+negate_x;
+ end;
+if y<0 then
+ begin negate(y); octant:=octant+negate_y;
+ end;
+if x<y then
+ begin t:=y; y:=x; x:=t; octant:=octant+switch_x_and_y;
+ end;
+if x=0 then @<Handle undefined arg@>
+else begin @<Set variable |z| to the arg of $(x,y)$@>;
+ @<Return an appropriate answer based on |z| and |octant|@>;
+ end;
+end;
+
+@ @<Handle undefined arg@>=
+begin print_err("angle(0,0) is taken as zero");
+@.angle(0,0)...zero@>
+help2("The `angle' between two identical points is undefined.")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+error; n_arg:=0;
+end
+
+@ @<Return an appropriate answer...@>=
+case octant of
+first_octant:n_arg:=z;
+second_octant:n_arg:=ninety_deg-z;
+third_octant:n_arg:=ninety_deg+z;
+fourth_octant:n_arg:=one_eighty_deg-z;
+fifth_octant:n_arg:=z-one_eighty_deg;
+sixth_octant:n_arg:=-z-ninety_deg;
+seventh_octant:n_arg:=z-ninety_deg;
+eighth_octant:n_arg:=-z;
+end {there are no other cases}
+
+@ At this point we have |x>=y>=0|, and |x>0|. The numbers are scaled up
+or down until $2^{28}\L x<2^{29}$, so that accurate fixed-point calculations
+will be made.
+
+@<Set variable |z| to the arg...@>=
+while x>=fraction_two do
+ begin x:=half(x); y:=half(y);
+ end;
+z:=0;
+if y>0 then
+ begin while x<fraction_one do
+ begin double(x); double(y);
+ end;
+ @<Increase |z| to the arg of $(x,y)$@>;
+ end
+
+@ During the calculations of this section, variables |x| and~|y|
+represent actual coordinates $(x,2^{-k}y)$. We will maintain the
+condition |x>=y|, so that the tangent will be at most $2^{-k}$.
+If $x<2y$, the tangent is greater than $2^{-k-1}$. The transformation
+$(a,b)\mapsto(a+b\tan\phi,b-a\tan\phi)$ replaces $(a,b)$ by
+coordinates whose angle has decreased by~$\phi$; in the special case
+$a=x$, $b=2^{-k}y$, and $\tan\phi=2^{-k-1}$, this operation reduces
+to the particularly simple iteration shown here. [Cf.~John E. Meggitt,
+@^Meggitt, John E.@>
+{\sl IBM Journal of Research and Development\/ \bf6} (1962), 210--226.]
+
+The initial value of |x| will be multiplied by at most
+$(1+{1\over2})(1+{1\over8})(1+{1\over32})\cdots\approx 1.7584$; hence
+there is no chance of integer overflow.
+
+@<Increase |z|...@>=
+k:=0;
+repeat double(y); incr(k);
+if y>x then
+ begin z:=z+spec_atan[k]; t:=x; x:=x+(y div two_to_the[k+k]); y:=y-t;
+ end;
+until k=15;
+repeat double(y); incr(k);
+if y>x then
+ begin z:=z+spec_atan[k]; y:=y-x;
+ end;
+until k=26
+
+@ Conversely, the |n_sin_cos| routine takes an |angle| and produces the sine
+and cosine of that angle. The results of this routine are
+stored in global integer variables |n_sin| and |n_cos|.
+
+@<Glob...@>=
+@!n_sin,@!n_cos:fraction; {results computed by |n_sin_cos|}
+
+@ Given an integer |z| that is $2^{20}$ times an angle $\theta$ in degrees,
+the purpose of |n_sin_cos(z)| is to set
+|x=@t$r\cos\theta$@>| and |y=@t$r\sin\theta$@>| (approximately),
+for some rather large number~|r|. The maximum of |x| and |y|
+will be between $2^{28}$ and $2^{30}$, so that there will be hardly
+any loss of accuracy. Then |x| and~|y| are divided by~|r|.
+
+@p procedure n_sin_cos(@!z:angle); {computes a multiple of the sine and cosine}
+var @!k:small_number; {loop control variable}
+@!q:0..7; {specifies the quadrant}
+@!r:fraction; {magnitude of |(x,y)|}
+@!x,@!y,@!t:integer; {temporary registers}
+begin while z<0 do z:=z+three_sixty_deg;
+z:=z mod three_sixty_deg; {now |0<=z<three_sixty_deg|}
+q:=z div forty_five_deg; z:=z mod forty_five_deg;
+x:=fraction_one; y:=x;
+if not odd(q) then z:=forty_five_deg-z;
+@<Subtract angle |z| from |(x,y)|@>;
+@<Convert |(x,y)| to the octant determined by~|q|@>;
+r:=pyth_add(x,y); n_cos:=make_fraction(x,r); n_sin:=make_fraction(y,r);
+end;
+
+@ In this case the octants are numbered sequentially.
+
+@<Convert |(x,...@>=
+case q of
+0:do_nothing;
+1:begin t:=x; x:=y; y:=t;
+ end;
+2:begin t:=x; x:=-y; y:=t;
+ end;
+3:negate(x);
+4:begin negate(x); negate(y);
+ end;
+5:begin t:=x; x:=-y; y:=-t;
+ end;
+6:begin t:=x; x:=y; y:=-t;
+ end;
+7:negate(y);
+end {there are no other cases}
+
+@ The main iteration of |n_sin_cos| is similar to that of |n_arg| but
+applied in reverse. The values of |spec_atan[k]| decrease slowly enough
+that this loop is guaranteed to terminate before the (nonexistent) value
+|spec_atan[27]| would be required.
+
+@<Subtract angle |z|...@>=
+k:=1;
+while z>0 do
+ begin if z>=spec_atan[k] then
+ begin z:=z-spec_atan[k]; t:=x;@/
+ x:=t+y div two_to_the[k];
+ y:=y-t div two_to_the[k];
+ end;
+ incr(k);
+ end;
+if y<0 then y:=0 {this precaution may never be needed}
+
+@ And now let's complete our collection of numeric utility routines
+by considering random number generation.
+\MF\ generates pseudo-random numbers with the additive scheme recommended
+in Section 3.6 of {\sl The Art of Computer Programming}; however, the
+results are random fractions between 0 and |fraction_one-1|, inclusive.
+
+There's an auxiliary array |randoms| that contains 55 pseudo-random
+fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-24})\bmod 2^{28}$,
+we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|.
+The global variable |j_random| tells which element has most recently
+been consumed.
+
+@<Glob...@>=
+@!randoms:array[0..54] of fraction; {the last 55 random values generated}
+@!j_random:0..54; {the number of unused |randoms|}
+
+@ To consume a random fraction, the program below will say `|next_random|'
+and then it will fetch |randoms[j_random]|. The |next_random| macro
+actually accesses the numbers backwards; blocks of 55~$x$'s are
+essentially being ``flipped.'' But that doesn't make them less random.
+
+@d next_random==if j_random=0 then new_randoms
+ else decr(j_random)
+
+@p procedure new_randoms;
+var @!k:0..54; {index into |randoms|}
+@!x:fraction; {accumulator}
+begin for k:=0 to 23 do
+ begin x:=randoms[k]-randoms[k+31];
+ if x<0 then x:=x+fraction_one;
+ randoms[k]:=x;
+ end;
+for k:=24 to 54 do
+ begin x:=randoms[k]-randoms[k-24];
+ if x<0 then x:=x+fraction_one;
+ randoms[k]:=x;
+ end;
+j_random:=54;
+end;
+
+@ To initialize the |randoms| table, we call the following routine.
+
+@p procedure init_randoms(@!seed:scaled);
+var @!j,@!jj,@!k:fraction; {more or less random integers}
+@!i:0..54; {index into |randoms|}
+begin j:=abs(seed);
+while j>=fraction_one do j:=half(j);
+k:=1;
+for i:=0 to 54 do
+ begin jj:=k; k:=j-k; j:=jj;
+ if k<0 then k:=k+fraction_one;
+ randoms[(i*21)mod 55]:=j;
+ end;
+new_randoms; new_randoms; new_randoms; {``warm up'' the array}
+end;
+
+@ To produce a uniform random number in the range |0<=u<x| or |0>=u>x|
+or |0=u=x|, given a |scaled| value~|x|, we proceed as shown here.
+
+Note that the call of |take_fraction| will produce the values 0 and~|x|
+with about half the probability that it will produce any other particular
+values between 0 and~|x|, because it rounds its answers.
+
+@p function unif_rand(@!x:scaled):scaled;
+var @!y:scaled; {trial value}
+begin next_random; y:=take_fraction(abs(x),randoms[j_random]);
+if y=abs(x) then unif_rand:=0
+else if x>0 then unif_rand:=y
+else unif_rand:=-y;
+end;
+
+@ Finally, a normal deviate with mean zero and unit standard deviation
+can readily be obtained with the ratio method (Algorithm 3.4.1R in
+{\sl The Art of Computer Programming\/}).
+
+@p function norm_rand:scaled;
+var @!x,@!u,@!l:integer; {what the book would call $2^{16}X$, $2^{28}U$,
+ and $-2^{24}\ln U$}
+begin repeat
+ repeat next_random;
+ x:=take_fraction(112429,randoms[j_random]-fraction_half);
+ {$2^{16}\sqrt{8/e}\approx 112428.82793$}
+ next_random; u:=randoms[j_random];
+ until abs(x)<u;
+x:=make_fraction(x,u);
+l:=139548960-m_log(u); {$2^{24}\cdot12\ln2\approx139548959.6165$}
+until ab_vs_cd(1024,l,x,x)>=0;
+norm_rand:=x;
+end;
+
+@* \[9] Packed data.
+In order to make efficient use of storage space, \MF\ bases its major data
+structures on a |memory_word|, which contains either a (signed) integer,
+possibly scaled, or a small number of fields that are one half or one
+quarter of the size used for storing integers.
+
+If |x| is a variable of type |memory_word|, it contains up to four
+fields that can be referred to as follows:
+$$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr
+|x|&.|int|&(an |integer|)\cr
+|x|&.|sc|\qquad&(a |scaled| integer)\cr
+|x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr
+|x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword
+ field)\cr
+|x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt
+ &\qquad\qquad\qquad(four quarterword fields)\cr}}$$
+This is somewhat cumbersome to write, and not very readable either, but
+macros will be used to make the notation shorter and more transparent.
+The \PASCAL\ code below gives a formal definition of |memory_word| and
+its subsidiary types, using packed variant records. \MF\ makes no
+assumptions about the relative positions of the fields within a word.
+
+Since we are assuming 32-bit integers, a halfword must contain at least
+16 bits, and a quarterword must contain at least 8 bits.
+@^system dependencies@>
+But it doesn't hurt to have more bits; for example, with enough 36-bit
+words you might be able to have |mem_max| as large as 262142.
+
+N.B.: Valuable memory space will be dreadfully wasted unless \MF\ is compiled
+by a \PASCAL\ that packs all of the |memory_word| variants into
+the space of a single integer. Some \PASCAL\ compilers will pack an
+integer whose subrange is `|0..255|' into an eight-bit field, but others
+insist on allocating space for an additional sign bit; on such systems you
+can get 256 values into a quarterword only if the subrange is `|-128..127|'.
+
+The present implementation tries to accommodate as many variations as possible,
+so it makes few assumptions. If integers having the subrange
+`|min_quarterword..max_quarterword|' can be packed into a quarterword,
+and if integers having the subrange `|min_halfword..max_halfword|'
+can be packed into a halfword, everything should work satisfactorily.
+
+It is usually most efficient to have |min_quarterword=min_halfword=0|,
+so one should try to achieve this unless it causes a severe problem.
+The values defined here are recommended for most 32-bit computers.
+
+@d min_quarterword=0 {smallest allowable value in a |quarterword|}
+@d max_quarterword=255 {largest allowable value in a |quarterword|}
+@d min_halfword==0 {smallest allowable value in a |halfword|}
+@d max_halfword==65535 {largest allowable value in a |halfword|}
+
+@ Here are the inequalities that the quarterword and halfword values
+must satisfy (or rather, the inequalities that they mustn't satisfy):
+
+@<Check the ``constant''...@>=
+init if mem_max<>mem_top then bad:=10;@+tini@;@/
+if mem_max<mem_top then bad:=10;
+if (min_quarterword>0)or(max_quarterword<127) then bad:=11;
+if (min_halfword>0)or(max_halfword<32767) then bad:=12;
+if (min_quarterword<min_halfword)or@|
+ (max_quarterword>max_halfword) then bad:=13;
+if (mem_min<min_halfword)or(mem_max>=max_halfword) then bad:=14;
+if max_strings>max_halfword then bad:=15;
+if buf_size>max_halfword then bad:=16;
+if (max_quarterword-min_quarterword<255)or@|
+ (max_halfword-min_halfword<65535) then bad:=17;
+
+@ The operation of subtracting |min_halfword| occurs rather frequently in
+\MF, so it is convenient to abbreviate this operation by using the macro
+|ho| defined here. \MF\ will run faster with respect to compilers that
+don't optimize the expression `|x-0|', if this macro is simplified in the
+obvious way when |min_halfword=0|. Similarly, |qi| and |qo| are used for
+input to and output from quarterwords.
+@^system dependencies@>
+
+@d ho(#)==#-min_halfword
+ {to take a sixteen-bit item from a halfword}
+@d qo(#)==#-min_quarterword {to read eight bits from a quarterword}
+@d qi(#)==#+min_quarterword {to store eight bits in a quarterword}
+
+@ The reader should study the following definitions closely:
+@^system dependencies@>
+
+@d sc==int {|scaled| data is equivalent to |integer|}
+
+@<Types...@>=
+@!quarterword = min_quarterword..max_quarterword; {1/4 of a word}
+@!halfword=min_halfword..max_halfword; {1/2 of a word}
+@!two_choices = 1..2; {used when there are two variants in a record}
+@!three_choices = 1..3; {used when there are three variants in a record}
+@!two_halves = packed record@;@/
+ @!rh:halfword;
+ case two_choices of
+ 1: (@!lh:halfword);
+ 2: (@!b0:quarterword; @!b1:quarterword);
+ end;
+@!four_quarters = packed record@;@/
+ @!b0:quarterword;
+ @!b1:quarterword;
+ @!b2:quarterword;
+ @!b3:quarterword;
+ end;
+@!memory_word = record@;@/
+ case three_choices of
+ 1: (@!int:integer);
+ 2: (@!hh:two_halves);
+ 3: (@!qqqq:four_quarters);
+ end;
+@!word_file = file of memory_word;
+
+@ When debugging, we may want to print a |memory_word| without knowing
+what type it is; so we print it in all modes.
+@^dirty \PASCAL@>@^debugging@>
+
+@p @!debug procedure print_word(@!w:memory_word);
+ {prints |w| in all ways}
+begin print_int(w.int); print_char(" ");@/
+print_scaled(w.sc); print_char(" "); print_scaled(w.sc div @'10000); print_ln;@/
+print_int(w.hh.lh); print_char("="); print_int(w.hh.b0); print_char(":");
+print_int(w.hh.b1); print_char(";"); print_int(w.hh.rh); print_char(" ");@/
+print_int(w.qqqq.b0); print_char(":"); print_int(w.qqqq.b1); print_char(":");
+print_int(w.qqqq.b2); print_char(":"); print_int(w.qqqq.b3);
+end;
+gubed
+
+@* \[10] Dynamic memory allocation.
+The \MF\ system does nearly all of its own memory allocation, so that it
+can readily be transported into environments that do not have automatic
+facilities for strings, garbage collection, etc., and so that it can be in
+control of what error messages the user receives. The dynamic storage
+requirements of \MF\ are handled by providing a large array |mem| in
+which consecutive blocks of words are used as nodes by the \MF\ routines.
+
+Pointer variables are indices into this array, or into another array
+called |eqtb| that will be explained later. A pointer variable might
+also be a special flag that lies outside the bounds of |mem|, so we
+allow pointers to assume any |halfword| value. The minimum memory
+index represents a null pointer.
+
+@d pointer==halfword {a flag or a location in |mem| or |eqtb|}
+@d null==mem_min {the null pointer}
+
+@ The |mem| array is divided into two regions that are allocated separately,
+but the dividing line between these two regions is not fixed; they grow
+together until finding their ``natural'' size in a particular job.
+Locations less than or equal to |lo_mem_max| are used for storing
+variable-length records consisting of two or more words each. This region
+is maintained using an algorithm similar to the one described in exercise
+2.5--19 of {\sl The Art of Computer Programming}. However, no size field
+appears in the allocated nodes; the program is responsible for knowing the
+relevant size when a node is freed. Locations greater than or equal to
+|hi_mem_min| are used for storing one-word records; a conventional
+\.{AVAIL} stack is used for allocation in this region.
+
+Locations of |mem| between |mem_min| and |mem_top| may be dumped as part
+of preloaded base files, by the \.{INIMF} preprocessor.
+@.INIMF@>
+Production versions of \MF\ may extend the memory at the top end in order to
+provide more space; these locations, between |mem_top| and |mem_max|,
+are always used for single-word nodes.
+
+The key pointers that govern |mem| allocation have a prescribed order:
+$$\hbox{|null=mem_min<lo_mem_max<hi_mem_min<mem_top<=mem_end<=mem_max|.}$$
+
+@<Glob...@>=
+@!mem : array[mem_min..mem_max] of memory_word; {the big dynamic storage area}
+@!lo_mem_max : pointer; {the largest location of variable-size memory in use}
+@!hi_mem_min : pointer; {the smallest location of one-word memory in use}
+
+@ Users who wish to study the memory requirements of specific applications can
+use optional special features that keep track of current and
+maximum memory usage. When code between the delimiters |@!stat| $\ldots$
+|tats| is not ``commented out,'' \MF\ will run a bit slower but it will
+report these statistics when |tracing_stats| is positive.
+
+@<Glob...@>=
+@!var_used, @!dyn_used : integer; {how much memory is in use}
+
+@ Let's consider the one-word memory region first, since it's the
+simplest. The pointer variable |mem_end| holds the highest-numbered location
+of |mem| that has ever been used. The free locations of |mem| that
+occur between |hi_mem_min| and |mem_end|, inclusive, are of type
+|two_halves|, and we write |info(p)| and |link(p)| for the |lh|
+and |rh| fields of |mem[p]| when it is of this type. The single-word
+free locations form a linked list
+$$|avail|,\;\hbox{|link(avail)|},\;\hbox{|link(link(avail))|},\;\ldots$$
+terminated by |null|.
+
+@d link(#) == mem[#].hh.rh {the |link| field of a memory word}
+@d info(#) == mem[#].hh.lh {the |info| field of a memory word}
+
+@<Glob...@>=
+@!avail : pointer; {head of the list of available one-word nodes}
+@!mem_end : pointer; {the last one-word node used in |mem|}
+
+@ If one-word memory is exhausted, it might mean that the user has forgotten
+a token like `\&{enddef}' or `\&{endfor}'. We will define some procedures
+later that try to help pinpoint the trouble.
+
+@p @t\4@>@<Declare the procedure called |show_token_list|@>@;
+@t\4@>@<Declare the procedure called |runaway|@>
+
+@ The function |get_avail| returns a pointer to a new one-word node whose
+|link| field is null. However, \MF\ will halt if there is no more room left.
+@^inner loop@>
+
+@p function get_avail : pointer; {single-word node allocation}
+var @!p:pointer; {the new node being got}
+begin p:=avail; {get top location in the |avail| stack}
+if p<>null then avail:=link(avail) {and pop it off}
+else if mem_end<mem_max then {or go into virgin territory}
+ begin incr(mem_end); p:=mem_end;
+ end
+else begin decr(hi_mem_min); p:=hi_mem_min;
+ if hi_mem_min<=lo_mem_max then
+ begin runaway; {if memory is exhausted, display possible runaway text}
+ overflow("main memory size",mem_max+1-mem_min);
+ {quit; all one-word nodes are busy}
+@:METAFONT capacity exceeded main memory size}{\quad main memory size@>
+ end;
+ end;
+link(p):=null; {provide an oft-desired initialization of the new node}
+@!stat incr(dyn_used);@+tats@;{maintain statistics}
+get_avail:=p;
+end;
+
+@ Conversely, a one-word node is recycled by calling |free_avail|.
+
+@d free_avail(#)== {single-word node liberation}
+ begin link(#):=avail; avail:=#;
+ @!stat decr(dyn_used);@+tats@/
+ end
+
+@ There's also a |fast_get_avail| routine, which saves the procedure-call
+overhead at the expense of extra programming. This macro is used in
+the places that would otherwise account for the most calls of |get_avail|.
+@^inner loop@>
+
+@d fast_get_avail(#)==@t@>@;@/
+ begin #:=avail; {avoid |get_avail| if possible, to save time}
+ if #=null then #:=get_avail
+ else begin avail:=link(#); link(#):=null;
+ @!stat incr(dyn_used);@+tats@/
+ end;
+ end
+
+@ The available-space list that keeps track of the variable-size portion
+of |mem| is a nonempty, doubly-linked circular list of empty nodes,
+pointed to by the roving pointer |rover|.
+
+Each empty node has size 2 or more; the first word contains the special
+value |max_halfword| in its |link| field and the size in its |info| field;
+the second word contains the two pointers for double linking.
+
+Each nonempty node also has size 2 or more. Its first word is of type
+|two_halves|\kern-1pt, and its |link| field is never equal to |max_halfword|.
+Otherwise there is complete flexibility with respect to the contents
+of its other fields and its other words.
+
+(We require |mem_max<max_halfword| because terrible things can happen
+when |max_halfword| appears in the |link| field of a nonempty node.)
+
+@d empty_flag == max_halfword {the |link| of an empty variable-size node}
+@d is_empty(#) == (link(#)=empty_flag) {tests for empty node}
+@d node_size == info {the size field in empty variable-size nodes}
+@d llink(#) == info(#+1) {left link in doubly-linked list of empty nodes}
+@d rlink(#) == link(#+1) {right link in doubly-linked list of empty nodes}
+
+@<Glob...@>=
+@!rover : pointer; {points to some node in the list of empties}
+
+@ A call to |get_node| with argument |s| returns a pointer to a new node
+of size~|s|, which must be 2~or more. The |link| field of the first word
+of this new node is set to null. An overflow stop occurs if no suitable
+space exists.
+
+If |get_node| is called with $s=2^{30}$, it simply merges adjacent free
+areas and returns the value |max_halfword|.
+
+@p function get_node(@!s:integer):pointer; {variable-size node allocation}
+label found,exit,restart;
+var @!p:pointer; {the node currently under inspection}
+@!q:pointer; {the node physically after node |p|}
+@!r:integer; {the newly allocated node, or a candidate for this honor}
+@!t,@!tt:integer; {temporary registers}
+@^inner loop@>
+begin restart: p:=rover; {start at some free node in the ring}
+repeat @<Try to allocate within node |p| and its physical successors,
+ and |goto found| if allocation was possible@>;
+p:=rlink(p); {move to the next node in the ring}
+until p=rover; {repeat until the whole list has been traversed}
+if s=@'10000000000 then
+ begin get_node:=max_halfword; return;
+ end;
+if lo_mem_max+2<hi_mem_min then if lo_mem_max+2<=mem_min+max_halfword then
+ @<Grow more variable-size memory and |goto restart|@>;
+overflow("main memory size",mem_max+1-mem_min);
+ {sorry, nothing satisfactory is left}
+@:METAFONT capacity exceeded main memory size}{\quad main memory size@>
+found: link(r):=null; {this node is now nonempty}
+@!stat var_used:=var_used+s; {maintain usage statistics}
+tats@;@/
+get_node:=r;
+exit:end;
+
+@ The lower part of |mem| grows by 1000 words at a time, unless
+we are very close to going under. When it grows, we simply link
+a new node into the available-space list. This method of controlled
+growth helps to keep the |mem| usage consecutive when \MF\ is
+implemented on ``virtual memory'' systems.
+@^virtual memory@>
+
+@<Grow more variable-size memory and |goto restart|@>=
+begin if hi_mem_min-lo_mem_max>=1998 then t:=lo_mem_max+1000
+else t:=lo_mem_max+1+(hi_mem_min-lo_mem_max) div 2;
+ {|lo_mem_max+2<=t<hi_mem_min|}
+if t>mem_min+max_halfword then t:=mem_min+max_halfword;
+p:=llink(rover); q:=lo_mem_max; rlink(p):=q; llink(rover):=q;@/
+rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/
+lo_mem_max:=t; link(lo_mem_max):=null; info(lo_mem_max):=null;
+rover:=q; goto restart;
+end
+
+@ @<Try to allocate...@>=
+q:=p+node_size(p); {find the physical successor}
+while is_empty(q) do {merge node |p| with node |q|}
+ begin t:=rlink(q); tt:=llink(q);
+@^inner loop@>
+ if q=rover then rover:=t;
+ llink(t):=tt; rlink(tt):=t;@/
+ q:=q+node_size(q);
+ end;
+r:=q-s;
+if r>p+1 then @<Allocate from the top of node |p| and |goto found|@>;
+if r=p then if rlink(p)<>p then
+ @<Allocate entire node |p| and |goto found|@>;
+node_size(p):=q-p {reset the size in case it grew}
+
+@ @<Allocate from the top...@>=
+begin node_size(p):=r-p; {store the remaining size}
+rover:=p; {start searching here next time}
+goto found;
+end
+
+@ Here we delete node |p| from the ring, and let |rover| rove around.
+
+@<Allocate entire...@>=
+begin rover:=rlink(p); t:=llink(p);
+llink(rover):=t; rlink(t):=rover;
+goto found;
+end
+
+@ Conversely, when some variable-size node |p| of size |s| is no longer needed,
+the operation |free_node(p,s)| will make its words available, by inserting
+|p| as a new empty node just before where |rover| now points.
+
+@p procedure free_node(@!p:pointer; @!s:halfword); {variable-size node
+ liberation}
+var @!q:pointer; {|llink(rover)|}
+begin node_size(p):=s; link(p):=empty_flag;
+@^inner loop@>
+q:=llink(rover); llink(p):=q; rlink(p):=rover; {set both links}
+llink(rover):=p; rlink(q):=p; {insert |p| into the ring}
+@!stat var_used:=var_used-s;@+tats@;{maintain statistics}
+end;
+
+@ Just before \.{INIMF} writes out the memory, it sorts the doubly linked
+available space list. The list is probably very short at such times, so a
+simple insertion sort is used. The smallest available location will be
+pointed to by |rover|, the next-smallest by |rlink(rover)|, etc.
+
+@p @!init procedure sort_avail; {sorts the available variable-size nodes
+ by location}
+var @!p,@!q,@!r: pointer; {indices into |mem|}
+@!old_rover:pointer; {initial |rover| setting}
+begin p:=get_node(@'10000000000); {merge adjacent free areas}
+p:=rlink(rover); rlink(rover):=max_halfword; old_rover:=rover;
+while p<>old_rover do @<Sort |p| into the list starting at |rover|
+ and advance |p| to |rlink(p)|@>;
+p:=rover;
+while rlink(p)<>max_halfword do
+ begin llink(rlink(p)):=p; p:=rlink(p);
+ end;
+rlink(p):=rover; llink(rover):=p;
+end;
+tini
+
+@ The following |while| loop is guaranteed to
+terminate, since the list that starts at
+|rover| ends with |max_halfword| during the sorting procedure.
+
+@<Sort |p|...@>=
+if p<rover then
+ begin q:=p; p:=rlink(q); rlink(q):=rover; rover:=q;
+ end
+else begin q:=rover;
+ while rlink(q)<p do q:=rlink(q);
+ r:=rlink(p); rlink(p):=rlink(q); rlink(q):=p; p:=r;
+ end
+
+@* \[11] Memory layout.
+Some areas of |mem| are dedicated to fixed usage, since static allocation is
+more efficient than dynamic allocation when we can get away with it. For
+example, locations |mem_min| to |mem_min+2| are always used to store the
+specification for null pen coordinates that are `$(0,0)$'. The
+following macro definitions accomplish the static allocation by giving
+symbolic names to the fixed positions. Static variable-size nodes appear
+in locations |mem_min| through |lo_mem_stat_max|, and static single-word nodes
+appear in locations |hi_mem_stat_min| through |mem_top|, inclusive.
+
+@d null_coords==mem_min {specification for pen offsets of $(0,0)$}
+@d null_pen==null_coords+3 {we will define |coord_node_size=3|}
+@d dep_head==null_pen+10 {and |pen_node_size=10|}
+@d zero_val==dep_head+2 {two words for a permanently zero value}
+@d temp_val==zero_val+2 {two words for a temporary value node}
+@d end_attr==temp_val {we use |end_attr+2| only}
+@d inf_val==end_attr+2 {and |inf_val+1| only}
+@d bad_vardef==inf_val+2 {two words for \&{vardef} error recovery}
+@d lo_mem_stat_max==bad_vardef+1 {largest statically
+ allocated word in the variable-size |mem|}
+@#
+@d sentinel==mem_top {end of sorted lists}
+@d temp_head==mem_top-1 {head of a temporary list of some kind}
+@d hold_head==mem_top-2 {head of a temporary list of another kind}
+@d hi_mem_stat_min==mem_top-2 {smallest statically allocated word in
+ the one-word |mem|}
+
+@ The following code gets the dynamic part of |mem| off to a good start,
+when \MF\ is initializing itself the slow way.
+
+@<Initialize table entries (done by \.{INIMF} only)@>=
+rover:=lo_mem_stat_max+1; {initialize the dynamic memory}
+link(rover):=empty_flag;
+node_size(rover):=1000; {which is a 1000-word available node}
+llink(rover):=rover; rlink(rover):=rover;@/
+lo_mem_max:=rover+1000; link(lo_mem_max):=null; info(lo_mem_max):=null;@/
+for k:=hi_mem_stat_min to mem_top do
+ mem[k]:=mem[lo_mem_max]; {clear list heads}
+avail:=null; mem_end:=mem_top;
+hi_mem_min:=hi_mem_stat_min; {initialize the one-word memory}
+var_used:=lo_mem_stat_max+1-mem_min; dyn_used:=mem_top+1-hi_mem_min;
+ {initialize statistics}
+
+@ The procedure |flush_list(p)| frees an entire linked list of one-word
+nodes that starts at a given position, until coming to |sentinel| or a
+pointer that is not in the one-word region. Another procedure,
+|flush_node_list|, frees an entire linked list of one-word and two-word
+nodes, until coming to a |null| pointer.
+@^inner loop@>
+
+@p procedure flush_list(@!p:pointer); {makes list of single-word nodes
+ available}
+label done;
+var @!q,@!r:pointer; {list traversers}
+begin if p>=hi_mem_min then if p<>sentinel then
+ begin r:=p;
+ repeat q:=r; r:=link(r); @!stat decr(dyn_used);@+tats@/
+ if r<hi_mem_min then goto done;
+ until r=sentinel;
+ done: {now |q| is the last node on the list}
+ link(q):=avail; avail:=p;
+ end;
+end;
+@#
+procedure flush_node_list(@!p:pointer);
+var @!q:pointer; {the node being recycled}
+begin while p<>null do
+ begin q:=p; p:=link(p);
+ if q<hi_mem_min then free_node(q,2)@+else free_avail(q);
+ end;
+end;
+
+@ If \MF\ is extended improperly, the |mem| array might get screwed up.
+For example, some pointers might be wrong, or some ``dead'' nodes might not
+have been freed when the last reference to them disappeared. Procedures
+|check_mem| and |search_mem| are available to help diagnose such
+problems. These procedures make use of two arrays called |free| and
+|was_free| that are present only if \MF's debugging routines have
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+
+@<Glob...@>=
+@!debug @!free: packed array [mem_min..mem_max] of boolean; {free cells}
+@t\hskip1em@>@!was_free: packed array [mem_min..mem_max] of boolean;
+ {previously free cells}
+@t\hskip1em@>@!was_mem_end,@!was_lo_max,@!was_hi_min: pointer;
+ {previous |mem_end|, |lo_mem_max|, and |hi_mem_min|}
+@t\hskip1em@>@!panicking:boolean; {do we want to check memory constantly?}
+gubed
+
+@ @<Set initial...@>=
+@!debug was_mem_end:=mem_min; {indicate that everything was previously free}
+was_lo_max:=mem_min; was_hi_min:=mem_max;
+panicking:=false;
+gubed
+
+@ Procedure |check_mem| makes sure that the available space lists of
+|mem| are well formed, and it optionally prints out all locations
+that are reserved now but were free the last time this procedure was called.
+
+@p @!debug procedure check_mem(@!print_locs : boolean);
+label done1,done2; {loop exits}
+var @!p,@!q,@!r:pointer; {current locations of interest in |mem|}
+@!clobbered:boolean; {is something amiss?}
+begin for p:=mem_min to lo_mem_max do free[p]:=false; {you can probably
+ do this faster}
+for p:=hi_mem_min to mem_end do free[p]:=false; {ditto}
+@<Check single-word |avail| list@>;
+@<Check variable-size |avail| list@>;
+@<Check flags of unavailable nodes@>;
+@<Check the list of linear dependencies@>;
+if print_locs then @<Print newly busy locations@>;
+for p:=mem_min to lo_mem_max do was_free[p]:=free[p];
+for p:=hi_mem_min to mem_end do was_free[p]:=free[p];
+ {|was_free:=free| might be faster}
+was_mem_end:=mem_end; was_lo_max:=lo_mem_max; was_hi_min:=hi_mem_min;
+end;
+gubed
+
+@ @<Check single-word...@>=
+p:=avail; q:=null; clobbered:=false;
+while p<>null do
+ begin if (p>mem_end)or(p<hi_mem_min) then clobbered:=true
+ else if free[p] then clobbered:=true;
+ if clobbered then
+ begin print_nl("AVAIL list clobbered at ");
+@.AVAIL list clobbered...@>
+ print_int(q); goto done1;
+ end;
+ free[p]:=true; q:=p; p:=link(q);
+ end;
+done1:
+
+@ @<Check variable-size...@>=
+p:=rover; q:=null; clobbered:=false;
+repeat if (p>=lo_mem_max)or(p<mem_min) then clobbered:=true
+ else if (rlink(p)>=lo_mem_max)or(rlink(p)<mem_min) then clobbered:=true
+ else if not(is_empty(p))or(node_size(p)<2)or@|
+ (p+node_size(p)>lo_mem_max)or@| (llink(rlink(p))<>p) then clobbered:=true;
+ if clobbered then
+ begin print_nl("Double-AVAIL list clobbered at ");
+@.Double-AVAIL list clobbered...@>
+ print_int(q); goto done2;
+ end;
+for q:=p to p+node_size(p)-1 do {mark all locations free}
+ begin if free[q] then
+ begin print_nl("Doubly free location at ");
+@.Doubly free location...@>
+ print_int(q); goto done2;
+ end;
+ free[q]:=true;
+ end;
+q:=p; p:=rlink(p);
+until p=rover;
+done2:
+
+@ @<Check flags...@>=
+p:=mem_min;
+while p<=lo_mem_max do {node |p| should not be empty}
+ begin if is_empty(p) then
+ begin print_nl("Bad flag at "); print_int(p);
+@.Bad flag...@>
+ end;
+ while (p<=lo_mem_max) and not free[p] do incr(p);
+ while (p<=lo_mem_max) and free[p] do incr(p);
+ end
+
+@ @<Print newly busy...@>=
+begin print_nl("New busy locs:");
+@.New busy locs@>
+for p:=mem_min to lo_mem_max do
+ if not free[p] and ((p>was_lo_max) or was_free[p]) then
+ begin print_char(" "); print_int(p);
+ end;
+for p:=hi_mem_min to mem_end do
+ if not free[p] and
+ ((p<was_hi_min) or (p>was_mem_end) or was_free[p]) then
+ begin print_char(" "); print_int(p);
+ end;
+end
+
+@ The |search_mem| procedure attempts to answer the question ``Who points
+to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem|
+that might not be of type |two_halves|. Strictly speaking, this is
+@^dirty \PASCAL@>
+undefined in \PASCAL, and it can lead to ``false drops'' (words that seem to
+point to |p| purely by coincidence). But for debugging purposes, we want
+to rule out the places that do {\sl not\/} point to |p|, so a few false
+drops are tolerable.
+
+@p @!debug procedure search_mem(@!p:pointer); {look for pointers to |p|}
+var @!q:integer; {current position being searched}
+begin for q:=mem_min to lo_mem_max do
+ begin if link(q)=p then
+ begin print_nl("LINK("); print_int(q); print_char(")");
+ end;
+ if info(q)=p then
+ begin print_nl("INFO("); print_int(q); print_char(")");
+ end;
+ end;
+for q:=hi_mem_min to mem_end do
+ begin if link(q)=p then
+ begin print_nl("LINK("); print_int(q); print_char(")");
+ end;
+ if info(q)=p then
+ begin print_nl("INFO("); print_int(q); print_char(")");
+ end;
+ end;
+@<Search |eqtb| for equivalents equal to |p|@>;
+end;
+gubed
+
+@* \[12] The command codes.
+Before we can go much further, we need to define symbolic names for the internal
+code numbers that represent the various commands obeyed by \MF. These codes
+are somewhat arbitrary, but not completely so. For example,
+some codes have been made adjacent so that |case| statements in the
+program need not consider cases that are widely spaced, or so that |case|
+statements can be replaced by |if| statements. A command can begin an
+expression if and only if its code lies between |min_primary_command| and
+|max_primary_command|, inclusive. The first token of a statement that doesn't
+begin with an expression has a command code between |min_command| and
+|max_statement_command|, inclusive. The ordering of the highest-numbered
+commands (|comma<semicolon<end_group<stop|) is crucial for the parsing
+and error-recovery methods of this program.
+
+At any rate, here is the list, for future reference.
+
+@d if_test=1 {conditional text (\&{if})}
+@d fi_or_else=2 {delimiters for conditionals (\&{elseif}, \&{else}, \&{fi})}
+@d input=3 {input a source file (\&{input}, \&{endinput})}
+@d iteration=4 {iterate (\&{for}, \&{forsuffixes}, \&{forever}, \&{endfor})}
+@d repeat_loop=5 {special command substituted for \&{endfor}}
+@d exit_test=6 {premature exit from a loop (\&{exitif})}
+@d relax=7 {do nothing (\.{\char`\\})}
+@d scan_tokens=8 {put a string into the input buffer}
+@d expand_after=9 {look ahead one token}
+@d defined_macro=10 {a macro defined by the user}
+@d min_command=defined_macro+1
+@d display_command=11 {online graphic output (\&{display})}
+@d save_command=12 {save a list of tokens (\&{save})}
+@d interim_command=13 {save an internal quantity (\&{interim})}
+@d let_command=14 {redefine a symbolic token (\&{let})}
+@d new_internal=15 {define a new internal quantity (\&{newinternal})}
+@d macro_def=16 {define a macro (\&{def}, \&{vardef}, etc.)}
+@d ship_out_command=17 {output a character (\&{shipout})}
+@d add_to_command=18 {add to edges (\&{addto})}
+@d cull_command=19 {cull and normalize edges (\&{cull})}
+@d tfm_command=20 {command for font metric info (\&{ligtable}, etc.)}
+@d protection_command=21 {set protection flag (\&{outer}, \&{inner})}
+@d show_command=22 {diagnostic output (\&{show}, \&{showvariable}, etc.)}
+@d mode_command=23 {set interaction level (\&{batchmode}, etc.)}
+@d random_seed=24 {initialize random number generator (\&{randomseed})}
+@d message_command=25 {communicate to user (\&{message}, \&{errmessage})}
+@d every_job_command=26 {designate a starting token (\&{everyjob})}
+@d delimiters=27 {define a pair of delimiters (\&{delimiters})}
+@d open_window=28 {define a window on the screen (\&{openwindow})}
+@d special_command=29 {output special info (\&{special}, \&{numspecial})}
+@d type_name=30 {declare a type (\&{numeric}, \&{pair}, etc.)}
+@d max_statement_command=type_name
+@d min_primary_command=type_name
+@d left_delimiter=31 {the left delimiter of a matching pair}
+@d begin_group=32 {beginning of a group (\&{begingroup})}
+@d nullary=33 {an operator without arguments (e.g., \&{normaldeviate})}
+@d unary=34 {an operator with one argument (e.g., \&{sqrt})}
+@d str_op=35 {convert a suffix to a string (\&{str})}
+@d cycle=36 {close a cyclic path (\&{cycle})}
+@d primary_binary=37 {binary operation taking `\&{of}' (e.g., \&{point})}
+@d capsule_token=38 {a value that has been put into a token list}
+@d string_token=39 {a string constant (e.g., |"hello"|)}
+@d internal_quantity=40 {internal numeric parameter (e.g., \&{pausing})}
+@d min_suffix_token=internal_quantity
+@d tag_token=41 {a symbolic token without a primitive meaning}
+@d numeric_token=42 {a numeric constant (e.g., \.{3.14159})}
+@d max_suffix_token=numeric_token
+@d plus_or_minus=43 {either `\.+' or `\.-'}
+@d max_primary_command=plus_or_minus {should also be |numeric_token+1|}
+@d min_tertiary_command=plus_or_minus
+@d tertiary_secondary_macro=44 {a macro defined by \&{secondarydef}}
+@d tertiary_binary=45 {an operator at the tertiary level (e.g., `\.{++}')}
+@d max_tertiary_command=tertiary_binary
+@d left_brace=46 {the operator `\.{\char`\{}'}
+@d min_expression_command=left_brace
+@d path_join=47 {the operator `\.{..}'}
+@d ampersand=48 {the operator `\.\&'}
+@d expression_tertiary_macro=49 {a macro defined by \&{tertiarydef}}
+@d expression_binary=50 {an operator at the expression level (e.g., `\.<')}
+@d equals=51 {the operator `\.='}
+@d max_expression_command=equals
+@d and_command=52 {the operator `\&{and}'}
+@d min_secondary_command=and_command
+@d secondary_primary_macro=53 {a macro defined by \&{primarydef}}
+@d slash=54 {the operator `\./'}
+@d secondary_binary=55 {an operator at the binary level (e.g., \&{shifted})}
+@d max_secondary_command=secondary_binary
+@d param_type=56 {type of parameter (\&{primary}, \&{expr}, \&{suffix}, etc.)}
+@d controls=57 {specify control points explicitly (\&{controls})}
+@d tension=58 {specify tension between knots (\&{tension})}
+@d at_least=59 {bounded tension value (\&{atleast})}
+@d curl_command=60 {specify curl at an end knot (\&{curl})}
+@d macro_special=61 {special macro operators (\&{quote}, \.{\#\AT!}, etc.)}
+@d right_delimiter=62 {the right delimiter of a matching pair}
+@d left_bracket=63 {the operator `\.['}
+@d right_bracket=64 {the operator `\.]'}
+@d right_brace=65 {the operator `\.{\char`\}}'}
+@d with_option=66 {option for filling (\&{withpen}, \&{withweight})}
+@d cull_op=67 {the operator `\&{keeping}' or `\&{dropping}'}
+@d thing_to_add=68
+ {variant of \&{addto} (\&{contour}, \&{doublepath}, \&{also})}
+@d of_token=69 {the operator `\&{of}'}
+@d from_token=70 {the operator `\&{from}'}
+@d to_token=71 {the operator `\&{to}'}
+@d at_token=72 {the operator `\&{at}'}
+@d in_window=73 {the operator `\&{inwindow}'}
+@d step_token=74 {the operator `\&{step}'}
+@d until_token=75 {the operator `\&{until}'}
+@d lig_kern_token=76
+ {the operators `\&{kern}' and `\.{=:}' and `\.{=:\char'174}', etc.}
+@d assignment=77 {the operator `\.{:=}'}
+@d skip_to=78 {the operation `\&{skipto}'}
+@d bchar_label=79 {the operator `\.{\char'174\char'174:}'}
+@d double_colon=80 {the operator `\.{::}'}
+@d colon=81 {the operator `\.:'}
+@#
+@d comma=82 {the operator `\.,', must be |colon+1|}
+@d end_of_statement==cur_cmd>comma
+@d semicolon=83 {the operator `\.;', must be |comma+1|}
+@d end_group=84 {end a group (\&{endgroup}), must be |semicolon+1|}
+@d stop=85 {end a job (\&{end}, \&{dump}), must be |end_group+1|}
+@d max_command_code=stop
+@d outer_tag=max_command_code+1 {protection code added to command code}
+
+@<Types...@>=
+@!command_code=1..max_command_code;
+
+@ Variables and capsules in \MF\ have a variety of ``types,''
+distinguished by the following code numbers:
+
+@d undefined=0 {no type has been declared}
+@d unknown_tag=1 {this constant is added to certain type codes below}
+@d vacuous=1 {no expression was present}
+@d boolean_type=2 {\&{boolean} with a known value}
+@d unknown_boolean=boolean_type+unknown_tag
+@d string_type=4 {\&{string} with a known value}
+@d unknown_string=string_type+unknown_tag
+@d pen_type=6 {\&{pen} with a known value}
+@d unknown_pen=pen_type+unknown_tag
+@d future_pen=8 {subexpression that will become a \&{pen} at a higher level}
+@d path_type=9 {\&{path} with a known value}
+@d unknown_path=path_type+unknown_tag
+@d picture_type=11 {\&{picture} with a known value}
+@d unknown_picture=picture_type+unknown_tag
+@d transform_type=13 {\&{transform} variable or capsule}
+@d pair_type=14 {\&{pair} variable or capsule}
+@d numeric_type=15 {variable that has been declared \&{numeric} but not used}
+@d known=16 {\&{numeric} with a known value}
+@d dependent=17 {a linear combination with |fraction| coefficients}
+@d proto_dependent=18 {a linear combination with |scaled| coefficients}
+@d independent=19 {\&{numeric} with unknown value}
+@d token_list=20 {variable name or suffix argument or text argument}
+@d structured=21 {variable with subscripts and attributes}
+@d unsuffixed_macro=22 {variable defined with \&{vardef} but no \.{\AT!\#}}
+@d suffixed_macro=23 {variable defined with \&{vardef} and \.{\AT!\#}}
+@#
+@d unknown_types==unknown_boolean,unknown_string,
+ unknown_pen,unknown_picture,unknown_path
+
+@<Basic printing procedures@>=
+procedure print_type(@!t:small_number);
+begin case t of
+vacuous:print("vacuous");
+boolean_type:print("boolean");
+unknown_boolean:print("unknown boolean");
+string_type:print("string");
+unknown_string:print("unknown string");
+pen_type:print("pen");
+unknown_pen:print("unknown pen");
+future_pen:print("future pen");
+path_type:print("path");
+unknown_path:print("unknown path");
+picture_type:print("picture");
+unknown_picture:print("unknown picture");
+transform_type:print("transform");
+pair_type:print("pair");
+known:print("known numeric");
+dependent:print("dependent");
+proto_dependent:print("proto-dependent");
+numeric_type:print("numeric");
+independent:print("independent");
+token_list:print("token list");
+structured:print("structured");
+unsuffixed_macro:print("unsuffixed macro");
+suffixed_macro:print("suffixed macro");
+othercases print("undefined")
+endcases;
+end;
+
+@ Values inside \MF\ are stored in two-word nodes that have a |name_type|
+as well as a |type|. The possibilities for |name_type| are defined
+here; they will be explained in more detail later.
+
+@d root=0 {|name_type| at the top level of a variable}
+@d saved_root=1 {same, when the variable has been saved}
+@d structured_root=2 {|name_type| where a |structured| branch occurs}
+@d subscr=3 {|name_type| in a subscript node}
+@d attr=4 {|name_type| in an attribute node}
+@d x_part_sector=5 {|name_type| in the \&{xpart} of a node}
+@d y_part_sector=6 {|name_type| in the \&{ypart} of a node}
+@d xx_part_sector=7 {|name_type| in the \&{xxpart} of a node}
+@d xy_part_sector=8 {|name_type| in the \&{xypart} of a node}
+@d yx_part_sector=9 {|name_type| in the \&{yxpart} of a node}
+@d yy_part_sector=10 {|name_type| in the \&{yypart} of a node}
+@d capsule=11 {|name_type| in stashed-away subexpressions}
+@d token=12 {|name_type| in a numeric token or string token}
+
+@ Primitive operations that produce values have a secondary identification
+code in addition to their command code; it's something like genera and species.
+For example, `\.*' has the command code |primary_binary|, and its
+secondary identification is |times|. The secondary codes start at 30 so that
+they don't overlap with the type codes; some type codes (e.g., |string_type|)
+are used as operators as well as type identifications.
+
+@d true_code=30 {operation code for \.{true}}
+@d false_code=31 {operation code for \.{false}}
+@d null_picture_code=32 {operation code for \.{nullpicture}}
+@d null_pen_code=33 {operation code for \.{nullpen}}
+@d job_name_op=34 {operation code for \.{jobname}}
+@d read_string_op=35 {operation code for \.{readstring}}
+@d pen_circle=36 {operation code for \.{pencircle}}
+@d normal_deviate=37 {operation code for \.{normaldeviate}}
+@d odd_op=38 {operation code for \.{odd}}
+@d known_op=39 {operation code for \.{known}}
+@d unknown_op=40 {operation code for \.{unknown}}
+@d not_op=41 {operation code for \.{not}}
+@d decimal=42 {operation code for \.{decimal}}
+@d reverse=43 {operation code for \.{reverse}}
+@d make_path_op=44 {operation code for \.{makepath}}
+@d make_pen_op=45 {operation code for \.{makepen}}
+@d total_weight_op=46 {operation code for \.{totalweight}}
+@d oct_op=47 {operation code for \.{oct}}
+@d hex_op=48 {operation code for \.{hex}}
+@d ASCII_op=49 {operation code for \.{ASCII}}
+@d char_op=50 {operation code for \.{char}}
+@d length_op=51 {operation code for \.{length}}
+@d turning_op=52 {operation code for \.{turningnumber}}
+@d x_part=53 {operation code for \.{xpart}}
+@d y_part=54 {operation code for \.{ypart}}
+@d xx_part=55 {operation code for \.{xxpart}}
+@d xy_part=56 {operation code for \.{xypart}}
+@d yx_part=57 {operation code for \.{yxpart}}
+@d yy_part=58 {operation code for \.{yypart}}
+@d sqrt_op=59 {operation code for \.{sqrt}}
+@d m_exp_op=60 {operation code for \.{mexp}}
+@d m_log_op=61 {operation code for \.{mlog}}
+@d sin_d_op=62 {operation code for \.{sind}}
+@d cos_d_op=63 {operation code for \.{cosd}}
+@d floor_op=64 {operation code for \.{floor}}
+@d uniform_deviate=65 {operation code for \.{uniformdeviate}}
+@d char_exists_op=66 {operation code for \.{charexists}}
+@d angle_op=67 {operation code for \.{angle}}
+@d cycle_op=68 {operation code for \.{cycle}}
+@d plus=69 {operation code for \.+}
+@d minus=70 {operation code for \.-}
+@d times=71 {operation code for \.*}
+@d over=72 {operation code for \./}
+@d pythag_add=73 {operation code for \.{++}}
+@d pythag_sub=74 {operation code for \.{+-+}}
+@d or_op=75 {operation code for \.{or}}
+@d and_op=76 {operation code for \.{and}}
+@d less_than=77 {operation code for \.<}
+@d less_or_equal=78 {operation code for \.{<=}}
+@d greater_than=79 {operation code for \.>}
+@d greater_or_equal=80 {operation code for \.{>=}}
+@d equal_to=81 {operation code for \.=}
+@d unequal_to=82 {operation code for \.{<>}}
+@d concatenate=83 {operation code for \.\&}
+@d rotated_by=84 {operation code for \.{rotated}}
+@d slanted_by=85 {operation code for \.{slanted}}
+@d scaled_by=86 {operation code for \.{scaled}}
+@d shifted_by=87 {operation code for \.{shifted}}
+@d transformed_by=88 {operation code for \.{transformed}}
+@d x_scaled=89 {operation code for \.{xscaled}}
+@d y_scaled=90 {operation code for \.{yscaled}}
+@d z_scaled=91 {operation code for \.{zscaled}}
+@d intersect=92 {operation code for \.{intersectiontimes}}
+@d double_dot=93 {operation code for improper \.{..}}
+@d substring_of=94 {operation code for \.{substring}}
+@d min_of=substring_of
+@d subpath_of=95 {operation code for \.{subpath}}
+@d direction_time_of=96 {operation code for \.{directiontime}}
+@d point_of=97 {operation code for \.{point}}
+@d precontrol_of=98 {operation code for \.{precontrol}}
+@d postcontrol_of=99 {operation code for \.{postcontrol}}
+@d pen_offset_of=100 {operation code for \.{penoffset}}
+
+@p procedure print_op(@!c:quarterword);
+begin if c<=numeric_type then print_type(c)
+else case c of
+true_code:print("true");
+false_code:print("false");
+null_picture_code:print("nullpicture");
+null_pen_code:print("nullpen");
+job_name_op:print("jobname");
+read_string_op:print("readstring");
+pen_circle:print("pencircle");
+normal_deviate:print("normaldeviate");
+odd_op:print("odd");
+known_op:print("known");
+unknown_op:print("unknown");
+not_op:print("not");
+decimal:print("decimal");
+reverse:print("reverse");
+make_path_op:print("makepath");
+make_pen_op:print("makepen");
+total_weight_op:print("totalweight");
+oct_op:print("oct");
+hex_op:print("hex");
+ASCII_op:print("ASCII");
+char_op:print("char");
+length_op:print("length");
+turning_op:print("turningnumber");
+x_part:print("xpart");
+y_part:print("ypart");
+xx_part:print("xxpart");
+xy_part:print("xypart");
+yx_part:print("yxpart");
+yy_part:print("yypart");
+sqrt_op:print("sqrt");
+m_exp_op:print("mexp");
+m_log_op:print("mlog");
+sin_d_op:print("sind");
+cos_d_op:print("cosd");
+floor_op:print("floor");
+uniform_deviate:print("uniformdeviate");
+char_exists_op:print("charexists");
+angle_op:print("angle");
+cycle_op:print("cycle");
+plus:print_char("+");
+minus:print_char("-");
+times:print_char("*");
+over:print_char("/");
+pythag_add:print("++");
+pythag_sub:print("+-+");
+or_op:print("or");
+and_op:print("and");
+less_than:print_char("<");
+less_or_equal:print("<=");
+greater_than:print_char(">");
+greater_or_equal:print(">=");
+equal_to:print_char("=");
+unequal_to:print("<>");
+concatenate:print("&");
+rotated_by:print("rotated");
+slanted_by:print("slanted");
+scaled_by:print("scaled");
+shifted_by:print("shifted");
+transformed_by:print("transformed");
+x_scaled:print("xscaled");
+y_scaled:print("yscaled");
+z_scaled:print("zscaled");
+intersect:print("intersectiontimes");
+substring_of:print("substring");
+subpath_of:print("subpath");
+direction_time_of:print("directiontime");
+point_of:print("point");
+precontrol_of:print("precontrol");
+postcontrol_of:print("postcontrol");
+pen_offset_of:print("penoffset");
+othercases print("..")
+endcases;
+end;
+
+@ \MF\ also has a bunch of internal parameters that a user might want to
+fuss with. Every such parameter has an identifying code number, defined here.
+
+@d tracing_titles=1 {show titles online when they appear}
+@d tracing_equations=2 {show each variable when it becomes known}
+@d tracing_capsules=3 {show capsules too}
+@d tracing_choices=4 {show the control points chosen for paths}
+@d tracing_specs=5 {show subdivision of paths into octants before digitizing}
+@d tracing_pens=6 {show details of pens that are made}
+@d tracing_commands=7 {show commands and operations before they are performed}
+@d tracing_restores=8 {show when a variable or internal is restored}
+@d tracing_macros=9 {show macros before they are expanded}
+@d tracing_edges=10 {show digitized edges as they are computed}
+@d tracing_output=11 {show digitized edges as they are output}
+@d tracing_stats=12 {show memory usage at end of job}
+@d tracing_online=13 {show long diagnostics on terminal and in the log file}
+@d year=14 {the current year (e.g., 1984)}
+@d month=15 {the current month (e.g., 3 $\equiv$ March)}
+@d day=16 {the current day of the month}
+@d time=17 {the number of minutes past midnight when this job started}
+@d char_code=18 {the number of the next character to be output}
+@d char_ext=19 {the extension code of the next character to be output}
+@d char_wd=20 {the width of the next character to be output}
+@d char_ht=21 {the height of the next character to be output}
+@d char_dp=22 {the depth of the next character to be output}
+@d char_ic=23 {the italic correction of the next character to be output}
+@d char_dx=24 {the device's $x$ movement for the next character, in pixels}
+@d char_dy=25 {the device's $y$ movement for the next character, in pixels}
+@d design_size=26 {the unit of measure used for |char_wd..char_ic|, in points}
+@d hppp=27 {the number of horizontal pixels per point}
+@d vppp=28 {the number of vertical pixels per point}
+@d x_offset=29 {horizontal displacement of shipped-out characters}
+@d y_offset=30 {vertical displacement of shipped-out characters}
+@d pausing=31 {positive to display lines on the terminal before they are read}
+@d showstopping=32 {positive to stop after each \&{show} command}
+@d fontmaking=33 {positive if font metric output is to be produced}
+@d proofing=34 {positive for proof mode, negative to suppress output}
+@d smoothing=35 {positive if moves are to be ``smoothed''}
+@d autorounding=36 {controls path modification to ``good'' points}
+@d granularity=37 {autorounding uses this pixel size}
+@d fillin=38 {extra darkness of diagonal lines}
+@d turning_check=39 {controls reorientation of clockwise paths}
+@d warning_check=40 {controls error message when variable value is large}
+@d boundary_char=41 {the right boundary character for ligatures}
+@d max_given_internal=41
+
+@<Glob...@>=
+@!internal:array[1..max_internal] of scaled;
+ {the values of internal quantities}
+@!int_name:array[1..max_internal] of str_number;
+ {their names}
+@!int_ptr:max_given_internal..max_internal;
+ {the maximum internal quantity defined so far}
+
+@ @<Set init...@>=
+for k:=1 to max_given_internal do internal[k]:=0;
+int_ptr:=max_given_internal;
+
+@ The symbolic names for internal quantities are put into \MF's hash table
+by using a routine called |primitive|, which will be defined later. Let us
+enter them now, so that we don't have to list all those names again
+anywhere else.
+
+@<Put each of \MF's primitives into the hash table@>=
+primitive("tracingtitles",internal_quantity,tracing_titles);@/
+@!@:tracingtitles_}{\&{tracingtitles} primitive@>
+primitive("tracingequations",internal_quantity,tracing_equations);@/
+@!@:tracing_equations_}{\&{tracingequations} primitive@>
+primitive("tracingcapsules",internal_quantity,tracing_capsules);@/
+@!@:tracing_capsules_}{\&{tracingcapsules} primitive@>
+primitive("tracingchoices",internal_quantity,tracing_choices);@/
+@!@:tracing_choices_}{\&{tracingchoices} primitive@>
+primitive("tracingspecs",internal_quantity,tracing_specs);@/
+@!@:tracing_specs_}{\&{tracingspecs} primitive@>
+primitive("tracingpens",internal_quantity,tracing_pens);@/
+@!@:tracing_pens_}{\&{tracingpens} primitive@>
+primitive("tracingcommands",internal_quantity,tracing_commands);@/
+@!@:tracing_commands_}{\&{tracingcommands} primitive@>
+primitive("tracingrestores",internal_quantity,tracing_restores);@/
+@!@:tracing_restores_}{\&{tracingrestores} primitive@>
+primitive("tracingmacros",internal_quantity,tracing_macros);@/
+@!@:tracing_macros_}{\&{tracingmacros} primitive@>
+primitive("tracingedges",internal_quantity,tracing_edges);@/
+@!@:tracing_edges_}{\&{tracingedges} primitive@>
+primitive("tracingoutput",internal_quantity,tracing_output);@/
+@!@:tracing_output_}{\&{tracingoutput} primitive@>
+primitive("tracingstats",internal_quantity,tracing_stats);@/
+@!@:tracing_stats_}{\&{tracingstats} primitive@>
+primitive("tracingonline",internal_quantity,tracing_online);@/
+@!@:tracing_online_}{\&{tracingonline} primitive@>
+primitive("year",internal_quantity,year);@/
+@!@:year_}{\&{year} primitive@>
+primitive("month",internal_quantity,month);@/
+@!@:month_}{\&{month} primitive@>
+primitive("day",internal_quantity,day);@/
+@!@:day_}{\&{day} primitive@>
+primitive("time",internal_quantity,time);@/
+@!@:time_}{\&{time} primitive@>
+primitive("charcode",internal_quantity,char_code);@/
+@!@:char_code_}{\&{charcode} primitive@>
+primitive("charext",internal_quantity,char_ext);@/
+@!@:char_ext_}{\&{charext} primitive@>
+primitive("charwd",internal_quantity,char_wd);@/
+@!@:char_wd_}{\&{charwd} primitive@>
+primitive("charht",internal_quantity,char_ht);@/
+@!@:char_ht_}{\&{charht} primitive@>
+primitive("chardp",internal_quantity,char_dp);@/
+@!@:char_dp_}{\&{chardp} primitive@>
+primitive("charic",internal_quantity,char_ic);@/
+@!@:char_ic_}{\&{charic} primitive@>
+primitive("chardx",internal_quantity,char_dx);@/
+@!@:char_dx_}{\&{chardx} primitive@>
+primitive("chardy",internal_quantity,char_dy);@/
+@!@:char_dy_}{\&{chardy} primitive@>
+primitive("designsize",internal_quantity,design_size);@/
+@!@:design_size_}{\&{designsize} primitive@>
+primitive("hppp",internal_quantity,hppp);@/
+@!@:hppp_}{\&{hppp} primitive@>
+primitive("vppp",internal_quantity,vppp);@/
+@!@:vppp_}{\&{vppp} primitive@>
+primitive("xoffset",internal_quantity,x_offset);@/
+@!@:x_offset_}{\&{xoffset} primitive@>
+primitive("yoffset",internal_quantity,y_offset);@/
+@!@:y_offset_}{\&{yoffset} primitive@>
+primitive("pausing",internal_quantity,pausing);@/
+@!@:pausing_}{\&{pausing} primitive@>
+primitive("showstopping",internal_quantity,showstopping);@/
+@!@:showstopping_}{\&{showstopping} primitive@>
+primitive("fontmaking",internal_quantity,fontmaking);@/
+@!@:fontmaking_}{\&{fontmaking} primitive@>
+primitive("proofing",internal_quantity,proofing);@/
+@!@:proofing_}{\&{proofing} primitive@>
+primitive("smoothing",internal_quantity,smoothing);@/
+@!@:smoothing_}{\&{smoothing} primitive@>
+primitive("autorounding",internal_quantity,autorounding);@/
+@!@:autorounding_}{\&{autorounding} primitive@>
+primitive("granularity",internal_quantity,granularity);@/
+@!@:granularity_}{\&{granularity} primitive@>
+primitive("fillin",internal_quantity,fillin);@/
+@!@:fillin_}{\&{fillin} primitive@>
+primitive("turningcheck",internal_quantity,turning_check);@/
+@!@:turning_check_}{\&{turningcheck} primitive@>
+primitive("warningcheck",internal_quantity,warning_check);@/
+@!@:warning_check_}{\&{warningcheck} primitive@>
+primitive("boundarychar",internal_quantity,boundary_char);@/
+@!@:boundary_char_}{\&{boundarychar} primitive@>
+
+@ Well, we do have to list the names one more time, for use in symbolic
+printouts.
+
+@<Initialize table...@>=
+int_name[tracing_titles]:="tracingtitles";
+int_name[tracing_equations]:="tracingequations";
+int_name[tracing_capsules]:="tracingcapsules";
+int_name[tracing_choices]:="tracingchoices";
+int_name[tracing_specs]:="tracingspecs";
+int_name[tracing_pens]:="tracingpens";
+int_name[tracing_commands]:="tracingcommands";
+int_name[tracing_restores]:="tracingrestores";
+int_name[tracing_macros]:="tracingmacros";
+int_name[tracing_edges]:="tracingedges";
+int_name[tracing_output]:="tracingoutput";
+int_name[tracing_stats]:="tracingstats";
+int_name[tracing_online]:="tracingonline";
+int_name[year]:="year";
+int_name[month]:="month";
+int_name[day]:="day";
+int_name[time]:="time";
+int_name[char_code]:="charcode";
+int_name[char_ext]:="charext";
+int_name[char_wd]:="charwd";
+int_name[char_ht]:="charht";
+int_name[char_dp]:="chardp";
+int_name[char_ic]:="charic";
+int_name[char_dx]:="chardx";
+int_name[char_dy]:="chardy";
+int_name[design_size]:="designsize";
+int_name[hppp]:="hppp";
+int_name[vppp]:="vppp";
+int_name[x_offset]:="xoffset";
+int_name[y_offset]:="yoffset";
+int_name[pausing]:="pausing";
+int_name[showstopping]:="showstopping";
+int_name[fontmaking]:="fontmaking";
+int_name[proofing]:="proofing";
+int_name[smoothing]:="smoothing";
+int_name[autorounding]:="autorounding";
+int_name[granularity]:="granularity";
+int_name[fillin]:="fillin";
+int_name[turning_check]:="turningcheck";
+int_name[warning_check]:="warningcheck";
+int_name[boundary_char]:="boundarychar";
+
+@ The following procedure, which is called just before \MF\ initializes its
+input and output, establishes the initial values of the date and time.
+@^system dependencies@>
+Since standard \PASCAL\ cannot provide such information, something special
+is needed. The program here simply specifies July 4, 1776, at noon; but
+users probably want a better approximation to the truth.
+
+Note that the values are |scaled| integers. Hence \MF\ can no longer
+be used after the year 32767.
+
+@p procedure fix_date_and_time;
+begin internal[time]:=12*60*unity; {minutes since midnight}
+internal[day]:=4*unity; {fourth day of the month}
+internal[month]:=7*unity; {seventh month of the year}
+internal[year]:=1776*unity; {Anno Domini}
+end;
+
+@ \MF\ is occasionally supposed to print diagnostic information that
+goes only into the transcript file, unless |tracing_online| is positive.
+Now that we have defined |tracing_online| we can define
+two routines that adjust the destination of print commands:
+
+@<Basic printing...@>=
+procedure begin_diagnostic; {prepare to do some tracing}
+begin old_setting:=selector;
+if(internal[tracing_online]<=0)and(selector=term_and_log) then
+ begin decr(selector);
+ if history=spotless then history:=warning_issued;
+ end;
+end;
+@#
+procedure end_diagnostic(@!blank_line:boolean);
+ {restore proper conditions after tracing}
+begin print_nl("");
+if blank_line then print_ln;
+selector:=old_setting;
+end;
+
+@ Of course we had better declare another global variable, if the previous
+routines are going to work.
+
+@<Glob...@>=
+@!old_setting:0..max_selector;
+
+@ We will occasionally use |begin_diagnostic| in connection with line-number
+printing, as follows. (The parameter |s| is typically |"Path"| or
+|"Cycle spec"|, etc.)
+
+@<Basic printing...@>=
+procedure print_diagnostic(@!s,@!t:str_number;@!nuline:boolean);
+begin begin_diagnostic;
+if nuline then print_nl(s)@+else print(s);
+print(" at line "); print_int(line);
+print(t); print_char(":");
+end;
+
+@ The 256 |ASCII_code| characters are grouped into classes by means of
+the |char_class| table. Individual class numbers have no semantic
+or syntactic significance, except in a few instances defined here.
+There's also |max_class|, which can be used as a basis for additional
+class numbers in nonstandard extensions of \MF.
+
+@d digit_class=0 {the class number of \.{0123456789}}
+@d period_class=1 {the class number of `\..'}
+@d space_class=2 {the class number of spaces and nonstandard characters}
+@d percent_class=3 {the class number of `\.\%'}
+@d string_class=4 {the class number of `\."'}
+@d right_paren_class=8 {the class number of `\.)'}
+@d isolated_classes==5,6,7,8 {characters that make length-one tokens only}
+@d letter_class=9 {letters and the underline character}
+@d left_bracket_class=17 {`\.['}
+@d right_bracket_class=18 {`\.]'}
+@d invalid_class=20 {bad character in the input}
+@d max_class=20 {the largest class number}
+
+@<Glob...@>=
+@!char_class:array[ASCII_code] of 0..max_class; {the class numbers}
+
+@ If changes are made to accommodate non-ASCII character sets, they should
+follow the guidelines in Appendix~C of {\sl The {\logos METAFONT\/}book}.
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+@^system dependencies@>
+
+@<Set init...@>=
+for k:="0" to "9" do char_class[k]:=digit_class;
+char_class["."]:=period_class;
+char_class[" "]:=space_class;
+char_class["%"]:=percent_class;
+char_class[""""]:=string_class;@/
+char_class[","]:=5;
+char_class[";"]:=6;
+char_class["("]:=7;
+char_class[")"]:=right_paren_class;
+for k:="A" to "Z" do char_class[k]:=letter_class;
+for k:="a" to "z" do char_class[k]:=letter_class;
+char_class["_"]:=letter_class;@/
+char_class["<"]:=10;
+char_class["="]:=10;
+char_class[">"]:=10;
+char_class[":"]:=10;
+char_class["|"]:=10;@/
+char_class["`"]:=11;
+char_class["'"]:=11;@/
+char_class["+"]:=12;
+char_class["-"]:=12;@/
+char_class["/"]:=13;
+char_class["*"]:=13;
+char_class["\"]:=13;@/
+char_class["!"]:=14;
+char_class["?"]:=14;@/
+char_class["#"]:=15;
+char_class["&"]:=15;
+char_class["@@"]:=15;
+char_class["$"]:=15;@/
+char_class["^"]:=16;
+char_class["~"]:=16;@/
+char_class["["]:=left_bracket_class;
+char_class["]"]:=right_bracket_class;@/
+char_class["{"]:=19;
+char_class["}"]:=19;@/
+for k:=0 to " "-1 do char_class[k]:=invalid_class;
+for k:=127 to 255 do char_class[k]:=invalid_class;
+
+@* \[13] The hash table.
+Symbolic tokens are stored and retrieved by means of a fairly standard hash
+table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C
+in {\sl The Art of Computer Programming\/}). Once a symbolic token enters the
+table, it is never removed.
+
+The actual sequence of characters forming a symbolic token is
+stored in the |str_pool| array together with all the other strings. An
+auxiliary array |hash| consists of items with two halfword fields per
+word. The first of these, called |next(p)|, points to the next identifier
+belonging to the same coalesced list as the identifier corresponding to~|p|;
+and the other, called |text(p)|, points to the |str_start| entry for
+|p|'s identifier. If position~|p| of the hash table is empty, we have
+|text(p)=0|; if position |p| is either empty or the end of a coalesced
+hash list, we have |next(p)=0|.
+
+An auxiliary pointer variable called |hash_used| is maintained in such a
+way that all locations |p>=hash_used| are nonempty. The global variable
+|st_count| tells how many symbolic tokens have been defined, if statistics
+are being kept.
+
+The first 256 locations of |hash| are reserved for symbols of length one.
+
+There's a parallel array called |eqtb| that contains the current equivalent
+values of each symbolic token. The entries of this array consist of
+two halfwords called |eq_type| (a command code) and |equiv| (a secondary
+piece of information that qualifies the |eq_type|).
+
+@d next(#) == hash[#].lh {link for coalesced lists}
+@d text(#) == hash[#].rh {string number for symbolic token name}
+@d eq_type(#) == eqtb[#].lh {the current ``meaning'' of a symbolic token}
+@d equiv(#) == eqtb[#].rh {parametric part of a token's meaning}
+@d hash_base=257 {hashing actually starts here}
+@d hash_is_full == (hash_used=hash_base) {are all positions occupied?}
+
+@<Glob...@>=
+@!hash_used:pointer; {allocation pointer for |hash|}
+@!st_count:integer; {total number of known identifiers}
+
+@ Certain entries in the hash table are ``frozen'' and not redefinable,
+since they are used in error recovery.
+
+@d hash_top==hash_base+hash_size {the first location of the frozen area}
+@d frozen_inaccessible==hash_top {|hash| location to protect the frozen area}
+@d frozen_repeat_loop==hash_top+1 {|hash| location of a loop-repeat token}
+@d frozen_right_delimiter==hash_top+2 {|hash| location of a permanent `\.)'}
+@d frozen_left_bracket==hash_top+3 {|hash| location of a permanent `\.['}
+@d frozen_slash==hash_top+4 {|hash| location of a permanent `\./'}
+@d frozen_colon==hash_top+5 {|hash| location of a permanent `\.:'}
+@d frozen_semicolon==hash_top+6 {|hash| location of a permanent `\.;'}
+@d frozen_end_for==hash_top+7 {|hash| location of a permanent \&{endfor}}
+@d frozen_end_def==hash_top+8 {|hash| location of a permanent \&{enddef}}
+@d frozen_fi==hash_top+9 {|hash| location of a permanent \&{fi}}
+@d frozen_end_group==hash_top+10
+ {|hash| location of a permanent `\.{endgroup}'}
+@d frozen_bad_vardef==hash_top+11 {|hash| location of `\.{a bad variable}'}
+@d frozen_undefined==hash_top+12 {|hash| location that never gets defined}
+@d hash_end==hash_top+12 {the actual size of the |hash| and |eqtb| arrays}
+
+@<Glob...@>=
+@!hash: array[1..hash_end] of two_halves; {the hash table}
+@!eqtb: array[1..hash_end] of two_halves; {the equivalents}
+
+@ @<Set init...@>=
+next(1):=0; text(1):=0; eq_type(1):=tag_token; equiv(1):=null;
+for k:=2 to hash_end do
+ begin hash[k]:=hash[1]; eqtb[k]:=eqtb[1];
+ end;
+
+@ @<Initialize table entries...@>=
+hash_used:=frozen_inaccessible; {nothing is used}
+st_count:=0;@/
+text(frozen_bad_vardef):="a bad variable";
+text(frozen_fi):="fi";
+text(frozen_end_group):="endgroup";
+text(frozen_end_def):="enddef";
+text(frozen_end_for):="endfor";@/
+text(frozen_semicolon):=";";
+text(frozen_colon):=":";
+text(frozen_slash):="/";
+text(frozen_left_bracket):="[";
+text(frozen_right_delimiter):=")";@/
+text(frozen_inaccessible):=" INACCESSIBLE";@/
+eq_type(frozen_right_delimiter):=right_delimiter;
+
+@ @<Check the ``constant'' values...@>=
+if hash_end+max_internal>max_halfword then bad:=21;
+
+@ Here is the subroutine that searches the hash table for an identifier
+that matches a given string of length~|l| appearing in |buffer[j..
+(j+l-1)]|. If the identifier is not found, it is inserted; hence it
+will always be found, and the corresponding hash table address
+will be returned.
+
+@p function id_lookup(@!j,@!l:integer):pointer; {search the hash table}
+label found; {go here when you've found it}
+var @!h:integer; {hash code}
+@!p:pointer; {index in |hash| array}
+@!k:pointer; {index in |buffer| array}
+begin if l=1 then @<Treat special case of length 1 and |goto found|@>;
+@<Compute the hash code |h|@>;
+p:=h+hash_base; {we start searching here; note that |0<=h<hash_prime|}
+loop@+ begin if text(p)>0 then if length(text(p))=l then
+ if str_eq_buf(text(p),j) then goto found;
+ if next(p)=0 then
+ @<Insert a new symbolic token after |p|, then
+ make |p| point to it and |goto found|@>;
+ p:=next(p);
+ end;
+found: id_lookup:=p;
+end;
+
+@ @<Treat special case of length 1...@>=
+begin p:=buffer[j]+1; text(p):=p-1; goto found;
+end
+
+@ @<Insert a new symbolic...@>=
+begin if text(p)>0 then
+ begin repeat if hash_is_full then
+ overflow("hash size",hash_size);
+@:METAFONT capacity exceeded hash size}{\quad hash size@>
+ decr(hash_used);
+ until text(hash_used)=0; {search for an empty location in |hash|}
+ next(p):=hash_used; p:=hash_used;
+ end;
+str_room(l);
+for k:=j to j+l-1 do append_char(buffer[k]);
+text(p):=make_string; str_ref[text(p)]:=max_str_ref;
+@!stat incr(st_count);@+tats@;@/
+goto found;
+end
+
+@ The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it
+should be a prime number. The theory of hashing tells us to expect fewer
+than two table probes, on the average, when the search is successful.
+[See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.]
+@^Vitter, Jeffrey Scott@>
+
+@<Compute the hash code |h|@>=
+h:=buffer[j];
+for k:=j+1 to j+l-1 do
+ begin h:=h+h+buffer[k];
+ while h>=hash_prime do h:=h-hash_prime;
+ end
+
+@ @<Search |eqtb| for equivalents equal to |p|@>=
+for q:=1 to hash_end do
+ begin if equiv(q)=p then
+ begin print_nl("EQUIV("); print_int(q); print_char(")");
+ end;
+ end
+
+@ We need to put \MF's ``primitive'' symbolic tokens into the hash
+table, together with their command code (which will be the |eq_type|)
+and an operand (which will be the |equiv|). The |primitive| procedure
+does this, in a way that no \MF\ user can. The global value |cur_sym|
+contains the new |eqtb| pointer after |primitive| has acted.
+
+@p @!init procedure primitive(@!s:str_number;@!c:halfword;@!o:halfword);
+var @!k:pool_pointer; {index into |str_pool|}
+@!j:small_number; {index into |buffer|}
+@!l:small_number; {length of the string}
+begin k:=str_start[s]; l:=str_start[s+1]-k;
+ {we will move |s| into the (empty) |buffer|}
+for j:=0 to l-1 do buffer[j]:=so(str_pool[k+j]);
+cur_sym:=id_lookup(0,l);@/
+if s>=256 then {we don't want to have the string twice}
+ begin flush_string(str_ptr-1); text(cur_sym):=s;
+ end;
+eq_type(cur_sym):=c; equiv(cur_sym):=o;
+end;
+tini
+
+@ Many of \MF's primitives need no |equiv|, since they are identifiable
+by their |eq_type| alone. These primitives are loaded into the hash table
+as follows:
+
+@<Put each of \MF's primitives into the hash table@>=
+primitive("..",path_join,0);@/
+@!@:.._}{\.{..} primitive@>
+primitive("[",left_bracket,0); eqtb[frozen_left_bracket]:=eqtb[cur_sym];@/
+@!@:[ }{\.{[} primitive@>
+primitive("]",right_bracket,0);@/
+@!@:] }{\.{]} primitive@>
+primitive("}",right_brace,0);@/
+@!@:]]}{\.{\char`\}} primitive@>
+primitive("{",left_brace,0);@/
+@!@:][}{\.{\char`\{} primitive@>
+primitive(":",colon,0); eqtb[frozen_colon]:=eqtb[cur_sym];@/
+@!@:: }{\.{:} primitive@>
+primitive("::",double_colon,0);@/
+@!@::: }{\.{::} primitive@>
+primitive("||:",bchar_label,0);@/
+@!@:::: }{\.{\char'174\char'174:} primitive@>
+primitive(":=",assignment,0);@/
+@!@::=_}{\.{:=} primitive@>
+primitive(",",comma,0);@/
+@!@:, }{\., primitive@>
+primitive(";",semicolon,0); eqtb[frozen_semicolon]:=eqtb[cur_sym];@/
+@!@:; }{\.; primitive@>
+primitive("\",relax,0);@/
+@!@:]]\\}{\.{\char`\\} primitive@>
+@#
+primitive("addto",add_to_command,0);@/
+@!@:add_to_}{\&{addto} primitive@>
+primitive("at",at_token,0);@/
+@!@:at_}{\&{at} primitive@>
+primitive("atleast",at_least,0);@/
+@!@:at_least_}{\&{atleast} primitive@>
+primitive("begingroup",begin_group,0); bg_loc:=cur_sym;@/
+@!@:begin_group_}{\&{begingroup} primitive@>
+primitive("controls",controls,0);@/
+@!@:controls_}{\&{controls} primitive@>
+primitive("cull",cull_command,0);@/
+@!@:cull_}{\&{cull} primitive@>
+primitive("curl",curl_command,0);@/
+@!@:curl_}{\&{curl} primitive@>
+primitive("delimiters",delimiters,0);@/
+@!@:delimiters_}{\&{delimiters} primitive@>
+primitive("display",display_command,0);@/
+@!@:display_}{\&{display} primitive@>
+primitive("endgroup",end_group,0);
+ eqtb[frozen_end_group]:=eqtb[cur_sym]; eg_loc:=cur_sym;@/
+@!@:endgroup_}{\&{endgroup} primitive@>
+primitive("everyjob",every_job_command,0);@/
+@!@:every_job_}{\&{everyjob} primitive@>
+primitive("exitif",exit_test,0);@/
+@!@:exit_if_}{\&{exitif} primitive@>
+primitive("expandafter",expand_after,0);@/
+@!@:expand_after_}{\&{expandafter} primitive@>
+primitive("from",from_token,0);@/
+@!@:from_}{\&{from} primitive@>
+primitive("inwindow",in_window,0);@/
+@!@:in_window_}{\&{inwindow} primitive@>
+primitive("interim",interim_command,0);@/
+@!@:interim_}{\&{interim} primitive@>
+primitive("let",let_command,0);@/
+@!@:let_}{\&{let} primitive@>
+primitive("newinternal",new_internal,0);@/
+@!@:new_internal_}{\&{newinternal} primitive@>
+primitive("of",of_token,0);@/
+@!@:of_}{\&{of} primitive@>
+primitive("openwindow",open_window,0);@/
+@!@:open_window_}{\&{openwindow} primitive@>
+primitive("randomseed",random_seed,0);@/
+@!@:random_seed_}{\&{randomseed} primitive@>
+primitive("save",save_command,0);@/
+@!@:save_}{\&{save} primitive@>
+primitive("scantokens",scan_tokens,0);@/
+@!@:scan_tokens_}{\&{scantokens} primitive@>
+primitive("shipout",ship_out_command,0);@/
+@!@:ship_out_}{\&{shipout} primitive@>
+primitive("skipto",skip_to,0);@/
+@!@:skip_to_}{\&{skipto} primitive@>
+primitive("step",step_token,0);@/
+@!@:step_}{\&{step} primitive@>
+primitive("str",str_op,0);@/
+@!@:str_}{\&{str} primitive@>
+primitive("tension",tension,0);@/
+@!@:tension_}{\&{tension} primitive@>
+primitive("to",to_token,0);@/
+@!@:to_}{\&{to} primitive@>
+primitive("until",until_token,0);@/
+@!@:until_}{\&{until} primitive@>
+
+@ Each primitive has a corresponding inverse, so that it is possible to
+display the cryptic numeric contents of |eqtb| in symbolic form.
+Every call of |primitive| in this program is therefore accompanied by some
+straightforward code that forms part of the |print_cmd_mod| routine
+explained below.
+
+@<Cases of |print_cmd_mod| for symbolic printing of primitives@>=
+add_to_command:print("addto");
+assignment:print(":=");
+at_least:print("atleast");
+at_token:print("at");
+bchar_label:print("||:");
+begin_group:print("begingroup");
+colon:print(":");
+comma:print(",");
+controls:print("controls");
+cull_command:print("cull");
+curl_command:print("curl");
+delimiters:print("delimiters");
+display_command:print("display");
+double_colon:print("::");
+end_group:print("endgroup");
+every_job_command:print("everyjob");
+exit_test:print("exitif");
+expand_after:print("expandafter");
+from_token:print("from");
+in_window:print("inwindow");
+interim_command:print("interim");
+left_brace:print("{");
+left_bracket:print("[");
+let_command:print("let");
+new_internal:print("newinternal");
+of_token:print("of");
+open_window:print("openwindow");
+path_join:print("..");
+random_seed:print("randomseed");
+relax:print_char("\");
+right_brace:print("}");
+right_bracket:print("]");
+save_command:print("save");
+scan_tokens:print("scantokens");
+semicolon:print(";");
+ship_out_command:print("shipout");
+skip_to:print("skipto");
+step_token:print("step");
+str_op:print("str");
+tension:print("tension");
+to_token:print("to");
+until_token:print("until");
+
+@ We will deal with the other primitives later, at some point in the program
+where their |eq_type| and |equiv| values are more meaningful. For example,
+the primitives for macro definitions will be loaded when we consider the
+routines that define macros.
+It is easy to find where each particular
+primitive was treated by looking in the index at the end; for example, the
+section where |"def"| entered |eqtb| is listed under `\&{def} primitive'.
+
+@* \[14] Token lists.
+A \MF\ token is either symbolic or numeric or a string, or it denotes
+a macro parameter or capsule; so there are five corresponding ways to encode it
+@^token@>
+internally: (1)~A symbolic token whose hash code is~|p|
+is represented by the number |p|, in the |info| field of a single-word
+node in~|mem|. (2)~A numeric token whose |scaled| value is~|v| is
+represented in a two-word node of~|mem|; the |type| field is |known|,
+the |name_type| field is |token|, and the |value| field holds~|v|.
+The fact that this token appears in a two-word node rather than a
+one-word node is, of course, clear from the node address.
+(3)~A string token is also represented in a two-word node; the |type|
+field is |string_type|, the |name_type| field is |token|, and the
+|value| field holds the corresponding |str_number|. (4)~Capsules have
+|name_type=capsule|, and their |type| and |value| fields represent
+arbitrary values (in ways to be explained later). (5)~Macro parameters
+are like symbolic tokens in that they appear in |info| fields of
+one-word nodes. The $k$th parameter is represented by |expr_base+k| if it
+is of type \&{expr}, or by |suffix_base+k| if it is of type \&{suffix}, or
+by |text_base+k| if it is of type \&{text}. (Here |0<=k<param_size|.)
+Actual values of these parameters are kept in a separate stack, as we will
+see later. The constants |expr_base|, |suffix_base|, and |text_base| are,
+of course, chosen so that there will be no confusion between symbolic
+tokens and parameters of various types.
+
+It turns out that |value(null)=0|, because |null=null_coords|;
+we will make use of this coincidence later.
+
+Incidentally, while we're speaking of coincidences, we might note that
+the `\\{type}' field of a node has nothing to do with ``type'' in a
+printer's sense. It's curious that the same word is used in such different ways.
+
+@d type(#) == mem[#].hh.b0 {identifies what kind of value this is}
+@d name_type(#) == mem[#].hh.b1 {a clue to the name of this value}
+@d token_node_size=2 {the number of words in a large token node}
+@d value_loc(#)==#+1 {the word that contains the |value| field}
+@d value(#)==mem[value_loc(#)].int {the value stored in a large token node}
+@d expr_base==hash_end+1 {code for the zeroth \&{expr} parameter}
+@d suffix_base==expr_base+param_size {code for the zeroth \&{suffix} parameter}
+@d text_base==suffix_base+param_size {code for the zeroth \&{text} parameter}
+
+@<Check the ``constant''...@>=
+if text_base+param_size>max_halfword then bad:=22;
+
+@ A numeric token is created by the following trivial routine.
+
+@p function new_num_tok(@!v:scaled):pointer;
+var @!p:pointer; {the new node}
+begin p:=get_node(token_node_size); value(p):=v;
+type(p):=known; name_type(p):=token; new_num_tok:=p;
+end;
+
+@ A token list is a singly linked list of nodes in |mem|, where
+each node contains a token and a link. Here's a subroutine that gets rid
+of a token list when it is no longer needed.
+
+@p procedure@?token_recycle; forward;@t\2@>@;@/
+procedure flush_token_list(@!p:pointer);
+var @!q:pointer; {the node being recycled}
+begin while p<>null do
+ begin q:=p; p:=link(p);
+ if q>=hi_mem_min then free_avail(q)
+ else begin case type(q) of
+ vacuous,boolean_type,known:do_nothing;
+ string_type:delete_str_ref(value(q));
+ unknown_types,pen_type,path_type,future_pen,picture_type,
+ pair_type,transform_type,dependent,proto_dependent,independent:
+ begin g_pointer:=q; token_recycle;
+ end;
+ othercases confusion("token")
+@:this can't happen token}{\quad token@>
+ endcases;@/
+ free_node(q,token_node_size);
+ end;
+ end;
+end;
+
+@ The procedure |show_token_list|, which prints a symbolic form of
+the token list that starts at a given node |p|, illustrates these
+conventions. The token list being displayed should not begin with a reference
+count. However, the procedure is intended to be fairly robust, so that if the
+memory links are awry or if |p| is not really a pointer to a token list,
+almost nothing catastrophic can happen.
+
+An additional parameter |q| is also given; this parameter is either null
+or it points to a node in the token list where a certain magic computation
+takes place that will be explained later. (Basically, |q| is non-null when
+we are printing the two-line context information at the time of an error
+message; |q| marks the place corresponding to where the second line
+should begin.)
+
+The generation will stop, and `\.{\char`\ ETC.}' will be printed, if the length
+of printing exceeds a given limit~|l|; the length of printing upon entry is
+assumed to be a given amount called |null_tally|. (Note that
+|show_token_list| sometimes uses itself recursively to print
+variable names within a capsule.)
+@^recursion@>
+
+Unusual entries are printed in the form of all-caps tokens
+preceded by a space, e.g., `\.{\char`\ BAD}'.
+
+@<Declare the procedure called |show_token_list|@>=
+procedure@?print_capsule; forward; @t\2@>@;@/
+procedure show_token_list(@!p,@!q:integer;@!l,@!null_tally:integer);
+label exit;
+var @!class,@!c:small_number; {the |char_class| of previous and new tokens}
+@!r,@!v:integer; {temporary registers}
+begin class:=percent_class;
+tally:=null_tally;
+while (p<>null) and (tally<l) do
+ begin if p=q then @<Do magic computation@>;
+ @<Display token |p| and set |c| to its class;
+ but |return| if there are problems@>;
+ class:=c; p:=link(p);
+ end;
+if p<>null then print(" ETC.");
+@.ETC@>
+exit:
+end;
+
+@ @<Display token |p| and set |c| to its class...@>=
+c:=letter_class; {the default}
+if (p<mem_min)or(p>mem_end) then
+ begin print(" CLOBBERED"); return;
+@.CLOBBERED@>
+ end;
+if p<hi_mem_min then @<Display two-word token@>
+else begin r:=info(p);
+ if r>=expr_base then @<Display a parameter token@>
+ else if r<1 then
+ if r=0 then @<Display a collective subscript@>
+ else print(" IMPOSSIBLE")
+@.IMPOSSIBLE@>
+ else begin r:=text(r);
+ if (r<0)or(r>=str_ptr) then print(" NONEXISTENT")
+@.NONEXISTENT@>
+ else @<Print string |r| as a symbolic token
+ and set |c| to its class@>;
+ end;
+ end
+
+@ @<Display two-word token@>=
+if name_type(p)=token then
+ if type(p)=known then @<Display a numeric token@>
+ else if type(p)<>string_type then print(" BAD")
+@.BAD@>
+ else begin print_char(""""); slow_print(value(p)); print_char("""");
+ c:=string_class;
+ end
+else if (name_type(p)<>capsule)or(type(p)<vacuous)or(type(p)>independent) then
+ print(" BAD")
+else begin g_pointer:=p; print_capsule; c:=right_paren_class;
+ end
+
+@ @<Display a numeric token@>=
+begin if class=digit_class then print_char(" ");
+v:=value(p);
+if v<0 then
+ begin if class=left_bracket_class then print_char(" ");
+ print_char("["); print_scaled(v); print_char("]");
+ c:=right_bracket_class;
+ end
+else begin print_scaled(v); c:=digit_class;
+ end;
+end
+
+@ Strictly speaking, a genuine token will never have |info(p)=0|.
+But we will see later (in the |print_variable_name| routine) that
+it is convenient to let |info(p)=0| stand for `\.{[]}'.
+
+@<Display a collective subscript@>=
+begin if class=left_bracket_class then print_char(" ");
+print("[]"); c:=right_bracket_class;
+end
+
+@ @<Display a parameter token@>=
+begin if r<suffix_base then
+ begin print("(EXPR"); r:=r-(expr_base);
+@.EXPR@>
+ end
+else if r<text_base then
+ begin print("(SUFFIX"); r:=r-(suffix_base);
+@.SUFFIX@>
+ end
+else begin print("(TEXT"); r:=r-(text_base);
+@.TEXT@>
+ end;
+print_int(r); print_char(")"); c:=right_paren_class;
+end
+
+@ @<Print string |r| as a symbolic token...@>=
+begin c:=char_class[so(str_pool[str_start[r]])];
+if c=class then
+ case c of
+ letter_class:print_char(".");
+ isolated_classes:do_nothing;
+ othercases print_char(" ")
+ endcases;
+slow_print(r);
+end
+
+@ The following procedures have been declared |forward| with no parameters,
+because the author dislikes \PASCAL's convention about |forward| procedures
+with parameters. It was necessary to do something, because |show_token_list|
+is recursive (although the recursion is limited to one level), and because
+|flush_token_list| is syntactically (but not semantically) recursive.
+@^recursion@>
+
+@<Declare miscellaneous procedures that were declared |forward|@>=
+procedure print_capsule;
+begin print_char("("); print_exp(g_pointer,0); print_char(")");
+end;
+@#
+procedure token_recycle;
+begin recycle_value(g_pointer);
+end;
+
+@ @<Glob...@>=
+@!g_pointer:pointer; {(global) parameter to the |forward| procedures}
+
+@ Macro definitions are kept in \MF's memory in the form of token lists
+that have a few extra one-word nodes at the beginning.
+
+The first node contains a reference count that is used to tell when the
+list is no longer needed. To emphasize the fact that a reference count is
+present, we shall refer to the |info| field of this special node as the
+|ref_count| field.
+@^reference counts@>
+
+The next node or nodes after the reference count serve to describe the
+formal parameters. They consist of zero or more parameter tokens followed
+by a code for the type of macro.
+
+@d ref_count==info {reference count preceding a macro definition or pen header}
+@d add_mac_ref(#)==incr(ref_count(#)) {make a new reference to a macro list}
+@d general_macro=0 {preface to a macro defined with a parameter list}
+@d primary_macro=1 {preface to a macro with a \&{primary} parameter}
+@d secondary_macro=2 {preface to a macro with a \&{secondary} parameter}
+@d tertiary_macro=3 {preface to a macro with a \&{tertiary} parameter}
+@d expr_macro=4 {preface to a macro with an undelimited \&{expr} parameter}
+@d of_macro=5 {preface to a macro with
+ undelimited `\&{expr} |x| \&{of}~|y|' parameters}
+@d suffix_macro=6 {preface to a macro with an undelimited \&{suffix} parameter}
+@d text_macro=7 {preface to a macro with an undelimited \&{text} parameter}
+
+@p procedure delete_mac_ref(@!p:pointer);
+ {|p| points to the reference count of a macro list that is
+ losing one reference}
+begin if ref_count(p)=null then flush_token_list(p)
+else decr(ref_count(p));
+end;
+
+@ The following subroutine displays a macro, given a pointer to its
+reference count.
+
+@p @t\4@>@<Declare the procedure called |print_cmd_mod|@>@;
+procedure show_macro(@!p:pointer;@!q,@!l:integer);
+label exit;
+var @!r:pointer; {temporary storage}
+begin p:=link(p); {bypass the reference count}
+while info(p)>text_macro do
+ begin r:=link(p); link(p):=null;
+ show_token_list(p,null,l,0); link(p):=r; p:=r;
+ if l>0 then l:=l-tally@+else return;
+ end; {control printing of `\.{ETC.}'}
+@.ETC@>
+tally:=0;
+case info(p) of
+general_macro:print("->");
+@.->@>
+primary_macro,secondary_macro,tertiary_macro:begin print_char("<");
+ print_cmd_mod(param_type,info(p)); print(">->");
+ end;
+expr_macro:print("<expr>->");
+of_macro:print("<expr>of<primary>->");
+suffix_macro:print("<suffix>->");
+text_macro:print("<text>->");
+end; {there are no other cases}
+show_token_list(link(p),q,l-tally,0);
+exit:end;
+
+@* \[15] Data structures for variables.
+The variables of \MF\ programs can be simple, like `\.x', or they can
+combine the structural properties of arrays and records, like `\.{x20a.b}'.
+A \MF\ user assigns a type to a variable like \.{x20a.b} by saying, for
+example, `\.{boolean} \.{x[]a.b}'. It's time for us to study how such
+things are represented inside of the computer.
+
+Each variable value occupies two consecutive words, either in a two-word
+node called a value node, or as a two-word subfield of a larger node. One
+of those two words is called the |value| field; it is an integer,
+containing either a |scaled| numeric value or the representation of some
+other type of quantity. (It might also be subdivided into halfwords, in
+which case it is referred to by other names instead of |value|.) The other
+word is broken into subfields called |type|, |name_type|, and |link|. The
+|type| field is a quarterword that specifies the variable's type, and
+|name_type| is a quarterword from which \MF\ can reconstruct the
+variable's name (sometimes by using the |link| field as well). Thus, only
+1.25 words are actually devoted to the value itself; the other
+three-quarters of a word are overhead, but they aren't wasted because they
+allow \MF\ to deal with sparse arrays and to provide meaningful diagnostics.
+
+In this section we shall be concerned only with the structural aspects of
+variables, not their values. Later parts of the program will change the
+|type| and |value| fields, but we shall treat those fields as black boxes
+whose contents should not be touched.
+
+However, if the |type| field is |structured|, there is no |value| field,
+and the second word is broken into two pointer fields called |attr_head|
+and |subscr_head|. Those fields point to additional nodes that
+contain structural information, as we shall see.
+
+@d subscr_head_loc(#) == #+1 {where |value|, |subscr_head|, and |attr_head| are}
+@d attr_head(#) == info(subscr_head_loc(#)) {pointer to attribute info}
+@d subscr_head(#) == link(subscr_head_loc(#)) {pointer to subscript info}
+@d value_node_size=2 {the number of words in a value node}
+
+@ An attribute node is three words long. Two of these words contain |type|
+and |value| fields as described above, and the third word contains
+additional information: There is an |attr_loc| field, which contains the
+hash address of the token that names this attribute; and there's also a
+|parent| field, which points to the value node of |structured| type at the
+next higher level (i.e., at the level to which this attribute is
+subsidiary). The |name_type| in an attribute node is `|attr|'. The
+|link| field points to the next attribute with the same parent; these are
+arranged in increasing order, so that |attr_loc(link(p))>attr_loc(p)|. The
+final attribute node links to the constant |end_attr|, whose |attr_loc|
+field is greater than any legal hash address. The |attr_head| in the
+parent points to a node whose |name_type| is |structured_root|; this
+node represents the null attribute, i.e., the variable that is relevant
+when no attributes are attached to the parent. The |attr_head| node
+has the fields of either
+a value node, a subscript node, or an attribute node, depending on what
+the parent would be if it were not structured; but the subscript and
+attribute fields are ignored, so it effectively contains only the data of
+a value node. The |link| field in this special node points to an attribute
+node whose |attr_loc| field is zero; the latter node represents a collective
+subscript `\.{[]}' attached to the parent, and its |link| field points to
+the first non-special attribute node (or to |end_attr| if there are none).
+
+A subscript node likewise occupies three words, with |type| and |value| fields
+plus extra information; its |name_type| is |subscr|. In this case the
+third word is called the |subscript| field, which is a |scaled| integer.
+The |link| field points to the subscript node with the next larger
+subscript, if any; otherwise the |link| points to the attribute node
+for collective subscripts at this level. We have seen that the latter node
+contains an upward pointer, so that the parent can be deduced.
+
+The |name_type| in a parent-less value node is |root|, and the |link|
+is the hash address of the token that names this value.
+
+In other words, variables have a hierarchical structure that includes
+enough threads running around so that the program is able to move easily
+between siblings, parents, and children. An example should be helpful:
+(The reader is advised to draw a picture while reading the following
+description, since that will help to firm up the ideas.)
+Suppose that `\.x' and `\.{x.a}' and `\.{x[]b}' and `\.{x5}'
+and `\.{x20b}' have been mentioned in a user's program, where
+\.{x[]b} has been declared to be of \&{boolean} type. Let |h(x)|, |h(a)|,
+and |h(b)| be the hash addresses of \.x, \.a, and~\.b. Then
+|eq_type(h(x))=tag_token| and |equiv(h(x))=p|, where |p|~is a two-word value
+node with |name_type(p)=root| and |link(p)=h(x)|. We have |type(p)=structured|,
+|attr_head(p)=q|, and |subscr_head(p)=r|, where |q| points to a value
+node and |r| to a subscript node. (Are you still following this? Use
+a pencil to draw a diagram.) The lone variable `\.x' is represented by
+|type(q)| and |value(q)|; furthermore
+|name_type(q)=structured_root| and |link(q)=q1|, where |q1| points
+to an attribute node representing `\.{x[]}'. Thus |name_type(q1)=attr|,
+|attr_loc(q1)=collective_subscript=0|, |parent(q1)=p|,
+|type(q1)=structured|, |attr_head(q1)=qq|, and |subscr_head(q1)=qq1|;
+|qq| is a three-word ``attribute-as-value'' node with |type(qq)=numeric_type|
+(assuming that \.{x5} is numeric, because |qq| represents `\.{x[]}'
+with no further attributes), |name_type(qq)=structured_root|,
+|attr_loc(qq)=0|, |parent(qq)=p|, and
+|link(qq)=qq1|. (Now pay attention to the next part.) Node |qq1| is
+an attribute node representing `\.{x[][]}', which has never yet
+occurred; its |type| field is |undefined|, and its |value| field is
+undefined. We have |name_type(qq1)=attr|, |attr_loc(qq1)=collective_subscript|,
+|parent(qq1)=q1|, and |link(qq1)=qq2|. Since |qq2| represents
+`\.{x[]b}', |type(qq2)=unknown_boolean|; also |attr_loc(qq2)=h(b)|,
+|parent(qq2)=q1|, |name_type(qq2)=attr|, |link(qq2)=end_attr|.
+(Maybe colored lines will help untangle your picture.)
+ Node |r| is a subscript node with |type| and |value|
+representing `\.{x5}'; |name_type(r)=subscr|, |subscript(r)=5.0|,
+and |link(r)=r1| is another subscript node. To complete the picture,
+see if you can guess what |link(r1)| is; give up? It's~|q1|.
+Furthermore |subscript(r1)=20.0|, |name_type(r1)=subscr|,
+|type(r1)=structured|, |attr_head(r1)=qqq|, |subscr_head(r1)=qqq1|,
+and we finish things off with three more nodes
+|qqq|, |qqq1|, and |qqq2| hung onto~|r1|. (Perhaps you should start again
+with a larger sheet of paper.) The value of variable `\.{x20b}'
+appears in node~|qqq2=link(qqq1)|, as you can well imagine.
+Similarly, the value of `\.{x.a}' appears in node |q2=link(q1)|, where
+|attr_loc(q2)=h(a)| and |parent(q2)=p|.
+
+If the example in the previous paragraph doesn't make things crystal
+clear, a glance at some of the simpler subroutines below will reveal how
+things work out in practice.
+
+The only really unusual thing about these conventions is the use of
+collective subscript attributes. The idea is to avoid repeating a lot of
+type information when many elements of an array are identical macros
+(for which distinct values need not be stored) or when they don't have
+all of the possible attributes. Branches of the structure below collective
+subscript attributes do not carry actual values except for macro identifiers;
+branches of the structure below subscript nodes do not carry significant
+information in their collective subscript attributes.
+
+@d attr_loc_loc(#)==#+2 {where the |attr_loc| and |parent| fields are}
+@d attr_loc(#)==info(attr_loc_loc(#)) {hash address of this attribute}
+@d parent(#)==link(attr_loc_loc(#)) {pointer to |structured| variable}
+@d subscript_loc(#)==#+2 {where the |subscript| field lives}
+@d subscript(#)==mem[subscript_loc(#)].sc {subscript of this variable}
+@d attr_node_size=3 {the number of words in an attribute node}
+@d subscr_node_size=3 {the number of words in a subscript node}
+@d collective_subscript=0 {code for the attribute `\.{[]}'}
+
+@<Initialize table...@>=
+attr_loc(end_attr):=hash_end+1; parent(end_attr):=null;
+
+@ Variables of type \&{pair} will have values that point to four-word
+nodes containing two numeric values. The first of these values has
+|name_type=x_part_sector| and the second has |name_type=y_part_sector|;
+the |link| in the first points back to the node whose |value| points
+to this four-word node.
+
+Variables of type \&{transform} are similar, but in this case their
+|value| points to a 12-word node containing six values, identified by
+|x_part_sector|, |y_part_sector|, |xx_part_sector|, |xy_part_sector|,
+|yx_part_sector|, and |yy_part_sector|.
+
+When an entire structured variable is saved, the |root| indication
+is temporarily replaced by |saved_root|.
+
+Some variables have no name; they just are used for temporary storage
+while expressions are being evaluated. We call them {\sl capsules}.
+
+@d x_part_loc(#)==# {where the \&{xpart} is found in a pair or transform node}
+@d y_part_loc(#)==#+2 {where the \&{ypart} is found in a pair or transform node}
+@d xx_part_loc(#)==#+4 {where the \&{xxpart} is found in a transform node}
+@d xy_part_loc(#)==#+6 {where the \&{xypart} is found in a transform node}
+@d yx_part_loc(#)==#+8 {where the \&{yxpart} is found in a transform node}
+@d yy_part_loc(#)==#+10 {where the \&{yypart} is found in a transform node}
+@#
+@d pair_node_size=4 {the number of words in a pair node}
+@d transform_node_size=12 {the number of words in a transform node}
+
+@<Glob...@>=
+@!big_node_size:array[transform_type..pair_type] of small_number;
+
+@ The |big_node_size| array simply contains two constants that \MF\
+occasionally needs to know.
+
+@<Set init...@>=
+big_node_size[transform_type]:=transform_node_size;
+big_node_size[pair_type]:=pair_node_size;
+
+@ If |type(p)=pair_type| or |transform_type| and if |value(p)=null|, the
+procedure call |init_big_node(p)| will allocate a pair or transform node
+for~|p|. The individual parts of such nodes are initially of type
+|independent|.
+
+@p procedure init_big_node(@!p:pointer);
+var @!q:pointer; {the new node}
+@!s:small_number; {its size}
+begin s:=big_node_size[type(p)]; q:=get_node(s);
+repeat s:=s-2; @<Make variable |q+s| newly independent@>;
+name_type(q+s):=half(s)+x_part_sector; link(q+s):=null;
+until s=0;
+link(q):=p; value(p):=q;
+end;
+
+@ The |id_transform| function creates a capsule for the
+identity transformation.
+
+@p function id_transform:pointer;
+var @!p,@!q,@!r:pointer; {list manipulation registers}
+begin p:=get_node(value_node_size); type(p):=transform_type;
+name_type(p):=capsule; value(p):=null; init_big_node(p); q:=value(p);
+r:=q+transform_node_size;
+repeat r:=r-2;
+type(r):=known; value(r):=0;
+until r=q;
+value(xx_part_loc(q)):=unity; value(yy_part_loc(q)):=unity;
+id_transform:=p;
+end;
+
+@ Tokens are of type |tag_token| when they first appear, but they point
+to |null| until they are first used as the root of a variable.
+The following subroutine establishes the root node on such grand occasions.
+
+@p procedure new_root(@!x:pointer);
+var @!p:pointer; {the new node}
+begin p:=get_node(value_node_size); type(p):=undefined; name_type(p):=root;
+link(p):=x; equiv(x):=p;
+end;
+
+@ These conventions for variable representation are illustrated by the
+|print_variable_name| routine, which displays the full name of a
+variable given only a pointer to its two-word value packet.
+
+@p procedure print_variable_name(@!p:pointer);
+label found,exit;
+var @!q:pointer; {a token list that will name the variable's suffix}
+@!r:pointer; {temporary for token list creation}
+begin while name_type(p)>=x_part_sector do
+ @<Preface the output with a part specifier; |return| in the
+ case of a capsule@>;
+q:=null;
+while name_type(p)>saved_root do
+ @<Ascend one level, pushing a token onto list |q|
+ and replacing |p| by its parent@>;
+r:=get_avail; info(r):=link(p); link(r):=q;
+if name_type(p)=saved_root then print("(SAVED)");
+@.SAVED@>
+show_token_list(r,null,el_gordo,tally); flush_token_list(r);
+exit:end;
+
+@ @<Ascend one level, pushing a token onto list |q|...@>=
+begin if name_type(p)=subscr then
+ begin r:=new_num_tok(subscript(p));
+ repeat p:=link(p);
+ until name_type(p)=attr;
+ end
+else if name_type(p)=structured_root then
+ begin p:=link(p); goto found;
+ end
+else begin if name_type(p)<>attr then confusion("var");
+@:this can't happen var}{\quad var@>
+ r:=get_avail; info(r):=attr_loc(p);
+ end;
+link(r):=q; q:=r;
+found: p:=parent(p);
+end
+
+@ @<Preface the output with a part specifier...@>=
+begin case name_type(p) of
+x_part_sector: print_char("x");
+y_part_sector: print_char("y");
+xx_part_sector: print("xx");
+xy_part_sector: print("xy");
+yx_part_sector: print("yx");
+yy_part_sector: print("yy");
+capsule: begin print("%CAPSULE"); print_int(p-null); return;
+@.CAPSULE@>
+ end;
+end; {there are no other cases}
+print("part "); p:=link(p-2*(name_type(p)-x_part_sector));
+end
+
+@ The |interesting| function returns |true| if a given variable is not
+in a capsule, or if the user wants to trace capsules.
+
+@p function interesting(@!p:pointer):boolean;
+var @!t:small_number; {a |name_type|}
+begin if internal[tracing_capsules]>0 then interesting:=true
+else begin t:=name_type(p);
+ if t>=x_part_sector then if t<>capsule then
+ t:=name_type(link(p-2*(t-x_part_sector)));
+ interesting:=(t<>capsule);
+ end;
+end;
+
+@ Now here is a subroutine that converts an unstructured type into an
+equivalent structured type, by inserting a |structured| node that is
+capable of growing. This operation is done only when |name_type(p)=root|,
+|subscr|, or |attr|.
+
+The procedure returns a pointer to the new node that has taken node~|p|'s
+place in the structure. Node~|p| itself does not move, nor are its
+|value| or |type| fields changed in any way.
+
+@p function new_structure(@!p:pointer):pointer;
+var @!q,@!r:pointer; {list manipulation registers}
+begin case name_type(p) of
+root: begin q:=link(p); r:=get_node(value_node_size); equiv(q):=r;
+ end;
+subscr: @<Link a new subscript node |r| in place of node |p|@>;
+attr: @<Link a new attribute node |r| in place of node |p|@>;
+othercases confusion("struct")
+@:this can't happen struct}{\quad struct@>
+endcases;@/
+link(r):=link(p); type(r):=structured; name_type(r):=name_type(p);
+attr_head(r):=p; name_type(p):=structured_root;@/
+q:=get_node(attr_node_size); link(p):=q; subscr_head(r):=q;
+parent(q):=r; type(q):=undefined; name_type(q):=attr; link(q):=end_attr;
+attr_loc(q):=collective_subscript; new_structure:=r;
+end;
+
+@ @<Link a new subscript node |r| in place of node |p|@>=
+begin q:=p;
+repeat q:=link(q);
+until name_type(q)=attr;
+q:=parent(q); r:=subscr_head_loc(q); {|link(r)=subscr_head(q)|}
+repeat q:=r; r:=link(r);
+until r=p;
+r:=get_node(subscr_node_size);
+link(q):=r; subscript(r):=subscript(p);
+end
+
+@ If the attribute is |collective_subscript|, there are two pointers to
+node~|p|, so we must change both of them.
+
+@<Link a new attribute node |r| in place of node |p|@>=
+begin q:=parent(p); r:=attr_head(q);
+repeat q:=r; r:=link(r);
+until r=p;
+r:=get_node(attr_node_size); link(q):=r;@/
+mem[attr_loc_loc(r)]:=mem[attr_loc_loc(p)]; {copy |attr_loc| and |parent|}
+if attr_loc(p)=collective_subscript then
+ begin q:=subscr_head_loc(parent(p));
+ while link(q)<>p do q:=link(q);
+ link(q):=r;
+ end;
+end
+
+@ The |find_variable| routine is given a pointer~|t| to a nonempty token
+list of suffixes; it returns a pointer to the corresponding two-word
+value. For example, if |t| points to token \.x followed by a numeric
+token containing the value~7, |find_variable| finds where the value of
+\.{x7} is stored in memory. This may seem a simple task, and it
+usually is, except when \.{x7} has never been referenced before.
+Indeed, \.x may never have even been subscripted before; complexities
+arise with respect to updating the collective subscript information.
+
+If a macro type is detected anywhere along path~|t|, or if the first
+item on |t| isn't a |tag_token|, the value |null| is returned.
+Otherwise |p| will be a non-null pointer to a node such that
+|undefined<type(p)<structured|.
+
+@d abort_find==begin find_variable:=null; return;@+end
+
+@p function find_variable(@!t:pointer):pointer;
+label exit;
+var @!p,@!q,@!r,@!s:pointer; {nodes in the ``value'' line}
+@!pp,@!qq,@!rr,@!ss:pointer; {nodes in the ``collective'' line}
+@!n:integer; {subscript or attribute}
+@!save_word:memory_word; {temporary storage for a word of |mem|}
+@^inner loop@>
+begin p:=info(t); t:=link(t);
+if eq_type(p) mod outer_tag<>tag_token then abort_find;
+if equiv(p)=null then new_root(p);
+p:=equiv(p); pp:=p;
+while t<>null do
+ begin @<Make sure that both nodes |p| and |pp| are of |structured| type@>;
+ if t<hi_mem_min then
+ @<Descend one level for the subscript |value(t)|@>
+ else @<Descend one level for the attribute |info(t)|@>;
+ t:=link(t);
+ end;
+if type(pp)>=structured then
+ if type(pp)=structured then pp:=attr_head(pp)@+else abort_find;
+if type(p)=structured then p:=attr_head(p);
+if type(p)=undefined then
+ begin if type(pp)=undefined then
+ begin type(pp):=numeric_type; value(pp):=null;
+ end;
+ type(p):=type(pp); value(p):=null;
+ end;
+find_variable:=p;
+exit:end;
+
+@ Although |pp| and |p| begin together, they diverge when a subscript occurs;
+|pp|~stays in the collective line while |p|~goes through actual subscript
+values.
+
+@<Make sure that both nodes |p| and |pp|...@>=
+if type(pp)<>structured then
+ begin if type(pp)>structured then abort_find;
+ ss:=new_structure(pp);
+ if p=pp then p:=ss;
+ pp:=ss;
+ end; {now |type(pp)=structured|}
+if type(p)<>structured then {it cannot be |>structured|}
+ p:=new_structure(p) {now |type(p)=structured|}
+
+@ We want this part of the program to be reasonably fast, in case there are
+@^inner loop@>
+lots of subscripts at the same level of the data structure. Therefore
+we store an ``infinite'' value in the word that appears at the end of the
+subscript list, even though that word isn't part of a subscript node.
+
+@<Descend one level for the subscript |value(t)|@>=
+begin n:=value(t);
+pp:=link(attr_head(pp)); {now |attr_loc(pp)=collective_subscript|}
+q:=link(attr_head(p)); save_word:=mem[subscript_loc(q)];
+subscript(q):=el_gordo; s:=subscr_head_loc(p); {|link(s)=subscr_head(p)|}
+repeat r:=s; s:=link(s);
+until n<=subscript(s);
+if n=subscript(s) then p:=s
+else begin p:=get_node(subscr_node_size); link(r):=p; link(p):=s;
+ subscript(p):=n; name_type(p):=subscr; type(p):=undefined;
+ end;
+mem[subscript_loc(q)]:=save_word;
+end
+
+@ @<Descend one level for the attribute |info(t)|@>=
+begin n:=info(t);
+ss:=attr_head(pp);
+repeat rr:=ss; ss:=link(ss);
+until n<=attr_loc(ss);
+if n<attr_loc(ss) then
+ begin qq:=get_node(attr_node_size); link(rr):=qq; link(qq):=ss;
+ attr_loc(qq):=n; name_type(qq):=attr; type(qq):=undefined;
+ parent(qq):=pp; ss:=qq;
+ end;
+if p=pp then
+ begin p:=ss; pp:=ss;
+ end
+else begin pp:=ss; s:=attr_head(p);
+ repeat r:=s; s:=link(s);
+ until n<=attr_loc(s);
+ if n=attr_loc(s) then p:=s
+ else begin q:=get_node(attr_node_size); link(r):=q; link(q):=s;
+ attr_loc(q):=n; name_type(q):=attr; type(q):=undefined;
+ parent(q):=p; p:=q;
+ end;
+ end;
+end
+
+@ Variables lose their former values when they appear in a type declaration,
+or when they are defined to be macros or \&{let} equal to something else.
+A subroutine will be defined later that recycles the storage associated
+with any particular |type| or |value|; our goal now is to study a higher
+level process called |flush_variable|, which selectively frees parts of a
+variable structure.
+
+This routine has some complexity because of examples such as
+`\hbox{\tt numeric x[]a[]b}',
+which recycles all variables of the form \.{x[i]a[j]b} (and no others), while
+`\hbox{\tt vardef x[]a[]=...}'
+discards all variables of the form \.{x[i]a[j]} followed by an arbitrary
+suffix, except for the collective node \.{x[]a[]} itself. The obvious way
+to handle such examples is to use recursion; so that's what we~do.
+@^recursion@>
+
+Parameter |p| points to the root information of the variable;
+parameter |t| points to a list of one-word nodes that represent
+suffixes, with |info=collective_subscript| for subscripts.
+
+@p @t\4@>@<Declare subroutines for printing expressions@>@;@/
+@t\4@>@<Declare basic dependency-list subroutines@>@;
+@t\4@>@<Declare the recycling subroutines@>@;
+@t\4@>@<Declare the procedure called |flush_cur_exp|@>@;
+@t\4@>@<Declare the procedure called |flush_below_variable|@>@;
+procedure flush_variable(@!p,@!t:pointer;@!discard_suffixes:boolean);
+label exit;
+var @!q,@!r:pointer; {list manipulation}
+@!n:halfword; {attribute to match}
+begin while t<>null do
+ begin if type(p)<>structured then return;
+ n:=info(t); t:=link(t);
+ if n=collective_subscript then
+ begin r:=subscr_head_loc(p); q:=link(r); {|q=subscr_head(p)|}
+ while name_type(q)=subscr do
+ begin flush_variable(q,t,discard_suffixes);
+ if t=null then
+ if type(q)=structured then r:=q
+ else begin link(r):=link(q); free_node(q,subscr_node_size);
+ end
+ else r:=q;
+ q:=link(r);
+ end;
+ end;
+ p:=attr_head(p);
+ repeat r:=p; p:=link(p);
+ until attr_loc(p)>=n;
+ if attr_loc(p)<>n then return;
+ end;
+if discard_suffixes then flush_below_variable(p)
+else begin if type(p)=structured then p:=attr_head(p);
+ recycle_value(p);
+ end;
+exit:end;
+
+@ The next procedure is simpler; it wipes out everything but |p| itself,
+which becomes undefined.
+
+@<Declare the procedure called |flush_below_variable|@>=
+procedure flush_below_variable(@!p:pointer);
+var @!q,@!r:pointer; {list manipulation registers}
+begin if type(p)<>structured then
+ recycle_value(p) {this sets |type(p)=undefined|}
+else begin q:=subscr_head(p);
+ while name_type(q)=subscr do
+ begin flush_below_variable(q); r:=q; q:=link(q);
+ free_node(r,subscr_node_size);
+ end;
+ r:=attr_head(p); q:=link(r); recycle_value(r);
+ if name_type(p)<=saved_root then free_node(r,value_node_size)
+ else free_node(r,subscr_node_size);
+ {we assume that |subscr_node_size=attr_node_size|}
+ repeat flush_below_variable(q); r:=q; q:=link(q); free_node(r,attr_node_size);
+ until q=end_attr;
+ type(p):=undefined;
+ end;
+end;
+
+@ Just before assigning a new value to a variable, we will recycle the
+old value and make the old value undefined. The |und_type| routine
+determines what type of undefined value should be given, based on
+the current type before recycling.
+
+@p function und_type(@!p:pointer):small_number;
+begin case type(p) of
+undefined,vacuous:und_type:=undefined;
+boolean_type,unknown_boolean:und_type:=unknown_boolean;
+string_type,unknown_string:und_type:=unknown_string;
+pen_type,unknown_pen,future_pen:und_type:=unknown_pen;
+path_type,unknown_path:und_type:=unknown_path;
+picture_type,unknown_picture:und_type:=unknown_picture;
+transform_type,pair_type,numeric_type:und_type:=type(p);
+known,dependent,proto_dependent,independent:und_type:=numeric_type;
+end; {there are no other cases}
+end;
+
+@ The |clear_symbol| routine is used when we want to redefine the equivalent
+of a symbolic token. It must remove any variable structure or macro
+definition that is currently attached to that symbol. If the |saving|
+parameter is true, a subsidiary structure is saved instead of destroyed.
+
+@p procedure clear_symbol(@!p:pointer;@!saving:boolean);
+var @!q:pointer; {|equiv(p)|}
+begin q:=equiv(p);
+case eq_type(p) mod outer_tag of
+defined_macro,secondary_primary_macro,tertiary_secondary_macro,
+ expression_tertiary_macro: if not saving then delete_mac_ref(q);
+tag_token:if q<>null then
+ if saving then name_type(q):=saved_root
+ else begin flush_below_variable(q); free_node(q,value_node_size);
+ end;@;
+othercases do_nothing
+endcases;@/
+eqtb[p]:=eqtb[frozen_undefined];
+end;
+
+@* \[16] Saving and restoring equivalents.
+The nested structure provided by \&{begingroup} and \&{endgroup}
+allows |eqtb| entries to be saved and restored, so that temporary changes
+can be made without difficulty. When the user requests a current value to
+be saved, \MF\ puts that value into its ``save stack.'' An appearance of
+\&{endgroup} ultimately causes the old values to be removed from the save
+stack and put back in their former places.
+
+The save stack is a linked list containing three kinds of entries,
+distinguished by their |info| fields. If |p| points to a saved item,
+then
+
+\smallskip\hang
+|info(p)=0| stands for a group boundary; each \&{begingroup} contributes
+such an item to the save stack and each \&{endgroup} cuts back the stack
+until the most recent such entry has been removed.
+
+\smallskip\hang
+|info(p)=q|, where |1<=q<=hash_end|, means that |mem[p+1]| holds the former
+contents of |eqtb[q]|. Such save stack entries are generated by \&{save}
+commands.
+
+\smallskip\hang
+|info(p)=hash_end+q|, where |q>0|, means that |value(p)| is a |scaled|
+integer to be restored to internal parameter number~|q|. Such entries
+are generated by \&{interim} commands.
+
+\smallskip\noindent
+The global variable |save_ptr| points to the top item on the save stack.
+
+@d save_node_size=2 {number of words per non-boundary save-stack node}
+@d saved_equiv(#)==mem[#+1].hh {where an |eqtb| entry gets saved}
+@d save_boundary_item(#)==begin #:=get_avail; info(#):=0;
+ link(#):=save_ptr; save_ptr:=#;
+ end
+
+@<Glob...@>=@!save_ptr:pointer; {the most recently saved item}
+
+@ @<Set init...@>=save_ptr:=null;
+
+@ The |save_variable| routine is given a hash address |q|; it salts this
+address in the save stack, together with its current equivalent,
+then makes token~|q| behave as though it were brand new.
+
+Nothing is stacked when |save_ptr=null|, however; there's no way to remove
+things from the stack when the program is not inside a group, so there's
+no point in wasting the space.
+
+@p procedure save_variable(@!q:pointer);
+var @!p:pointer; {temporary register}
+begin if save_ptr<>null then
+ begin p:=get_node(save_node_size); info(p):=q; link(p):=save_ptr;
+ saved_equiv(p):=eqtb[q]; save_ptr:=p;
+ end;
+clear_symbol(q,(save_ptr<>null));
+end;
+
+@ Similarly, |save_internal| is given the location |q| of an internal
+quantity like |tracing_pens|. It creates a save stack entry of the
+third kind.
+
+@p procedure save_internal(@!q:halfword);
+var @!p:pointer; {new item for the save stack}
+begin if save_ptr<>null then
+ begin p:=get_node(save_node_size); info(p):=hash_end+q;
+ link(p):=save_ptr; value(p):=internal[q]; save_ptr:=p;
+ end;
+end;
+
+@ At the end of a group, the |unsave| routine restores all of the saved
+equivalents in reverse order. This routine will be called only when there
+is at least one boundary item on the save stack.
+
+@p procedure unsave;
+var @!q:pointer; {index to saved item}
+@!p:pointer; {temporary register}
+begin while info(save_ptr)<>0 do
+ begin q:=info(save_ptr);
+ if q>hash_end then
+ begin if internal[tracing_restores]>0 then
+ begin begin_diagnostic; print_nl("{restoring ");
+ slow_print(int_name[q-(hash_end)]); print_char("=");
+ print_scaled(value(save_ptr)); print_char("}");
+ end_diagnostic(false);
+ end;
+ internal[q-(hash_end)]:=value(save_ptr);
+ end
+ else begin if internal[tracing_restores]>0 then
+ begin begin_diagnostic; print_nl("{restoring ");
+ slow_print(text(q)); print_char("}");
+ end_diagnostic(false);
+ end;
+ clear_symbol(q,false);
+ eqtb[q]:=saved_equiv(save_ptr);
+ if eq_type(q) mod outer_tag=tag_token then
+ begin p:=equiv(q);
+ if p<>null then name_type(p):=root;
+ end;
+ end;
+ p:=link(save_ptr); free_node(save_ptr,save_node_size); save_ptr:=p;
+ end;
+p:=link(save_ptr); free_avail(save_ptr); save_ptr:=p;
+end;
+
+@* \[17] Data structures for paths.
+When a \MF\ user specifies a path, \MF\ will create a list of knots
+and control points for the associated cubic spline curves. If the
+knots are $z_0$, $z_1$, \dots, $z_n$, there are control points
+$z_k^+$ and $z_{k+1}^-$ such that the cubic splines between knots
+$z_k$ and $z_{k+1}$ are defined by B\'ezier's formula
+@:Bezier}{B\'ezier, Pierre Etienne@>
+$$\eqalign{z(t)&=B(z_k,z_k^+,z_{k+1}^-,z_{k+1};t)\cr
+&=(1-t)^3z_k+3(1-t)^2tz_k^++3(1-t)t^2z_{k+1}^-+t^3z_{k+1}\cr}$$
+for |0<=t<=1|.
+
+There is a 7-word node for each knot $z_k$, containing one word of
+control information and six words for the |x| and |y| coordinates
+of $z_k^-$ and $z_k$ and~$z_k^+$. The control information appears
+in the |left_type| and |right_type| fields, which each occupy
+a quarter of the first word in the node; they specify properties
+of the curve as it enters and leaves the knot. There's also a
+halfword |link| field, which points to the following knot.
+
+If the path is a closed contour, knots 0 and |n| are identical;
+i.e., the |link| in knot |n-1| points to knot~0. But if the path
+is not closed, the |left_type| of knot~0 and the |right_type| of knot~|n|
+are equal to |endpoint|. In the latter case the |link| in knot~|n| points
+to knot~0, and the control points $z_0^-$ and $z_n^+$ are not used.
+
+@d left_type(#) == mem[#].hh.b0 {characterizes the path entering this knot}
+@d right_type(#) == mem[#].hh.b1 {characterizes the path leaving this knot}
+@d endpoint=0 {|left_type| at path beginning and |right_type| at path end}
+@d x_coord(#) == mem[#+1].sc {the |x| coordinate of this knot}
+@d y_coord(#) == mem[#+2].sc {the |y| coordinate of this knot}
+@d left_x(#) == mem[#+3].sc {the |x| coordinate of previous control point}
+@d left_y(#) == mem[#+4].sc {the |y| coordinate of previous control point}
+@d right_x(#) == mem[#+5].sc {the |x| coordinate of next control point}
+@d right_y(#) == mem[#+6].sc {the |y| coordinate of next control point}
+@d knot_node_size=7 {number of words in a knot node}
+
+@ Before the B\'ezier control points have been calculated, the memory
+space they will ultimately occupy is taken up by information that can be
+used to compute them. There are four cases:
+
+\yskip
+\textindent{$\bullet$} If |right_type=open|, the curve should leave
+the knot in the same direction it entered; \MF\ will figure out a
+suitable direction.
+
+\yskip
+\textindent{$\bullet$} If |right_type=curl|, the curve should leave the
+knot in a direction depending on the angle at which it enters the next
+knot and on the curl parameter stored in |right_curl|.
+
+\yskip
+\textindent{$\bullet$} If |right_type=given|, the curve should leave the
+knot in a nonzero direction stored as an |angle| in |right_given|.
+
+\yskip
+\textindent{$\bullet$} If |right_type=explicit|, the B\'ezier control
+point for leaving this knot has already been computed; it is in the
+|right_x| and |right_y| fields.
+
+\yskip\noindent
+The rules for |left_type| are similar, but they refer to the curve entering
+the knot, and to \\{left} fields instead of \\{right} fields.
+
+Non-|explicit| control points will be chosen based on ``tension'' parameters
+in the |left_tension| and |right_tension| fields. The
+`\&{atleast}' option is represented by negative tension values.
+@:at_least_}{\&{atleast} primitive@>
+
+For example, the \MF\ path specification
+$$\.{z0..z1..tension atleast 1..\{curl 2\}z2..z3\{-1,-2\}..tension
+ 3 and 4..p},$$
+where \.p is the path `\.{z4..controls z45 and z54..z5}', will be represented
+by the six knots
+\def\lodash{\hbox to 1.1em{\thinspace\hrulefill\thinspace}}
+$$\vbox{\halign{#\hfil&&\qquad#\hfil\cr
+|left_type|&\\{left} info&|x_coord,y_coord|&|right_type|&\\{right} info\cr
+\noalign{\yskip}
+|endpoint|&\lodash$,\,$\lodash&$x_0,y_0$&|curl|&$1.0,1.0$\cr
+|open|&\lodash$,1.0$&$x_1,y_1$&|open|&\lodash$,-1.0$\cr
+|curl|&$2.0,-1.0$&$x_2,y_2$&|curl|&$2.0,1.0$\cr
+|given|&$d,1.0$&$x_3,y_3$&|given|&$d,3.0$\cr
+|open|&\lodash$,4.0$&$x_4,y_4$&|explicit|&$x_{45},y_{45}$\cr
+|explicit|&$x_{54},y_{54}$&$x_5,y_5$&|endpoint|&\lodash$,\,$\lodash\cr}}$$
+Here |d| is the |angle| obtained by calling |n_arg(-unity,-two)|.
+Of course, this example is more complicated than anything a normal user
+would ever write.
+
+These types must satisfy certain restrictions because of the form of \MF's
+path syntax:
+(i)~|open| type never appears in the same node together with |endpoint|,
+|given|, or |curl|.
+(ii)~The |right_type| of a node is |explicit| if and only if the
+|left_type| of the following node is |explicit|.
+(iii)~|endpoint| types occur only at the ends, as mentioned above.
+
+@d left_curl==left_x {curl information when entering this knot}
+@d left_given==left_x {given direction when entering this knot}
+@d left_tension==left_y {tension information when entering this knot}
+@d right_curl==right_x {curl information when leaving this knot}
+@d right_given==right_x {given direction when leaving this knot}
+@d right_tension==right_y {tension information when leaving this knot}
+@d explicit=1 {|left_type| or |right_type| when control points are known}
+@d given=2 {|left_type| or |right_type| when a direction is given}
+@d curl=3 {|left_type| or |right_type| when a curl is desired}
+@d open=4 {|left_type| or |right_type| when \MF\ should choose the direction}
+
+@ Here is a diagnostic routine that prints a given knot list
+in symbolic form. It illustrates the conventions discussed above,
+and checks for anomalies that might arise while \MF\ is being debugged.
+
+@<Declare subroutines for printing expressions@>=
+procedure print_path(@!h:pointer;@!s:str_number;@!nuline:boolean);
+label done,done1;
+var @!p,@!q:pointer; {for list traversal}
+begin print_diagnostic("Path",s,nuline); print_ln;
+@.Path at line...@>
+p:=h;
+repeat q:=link(p);
+if (p=null)or(q=null) then
+ begin print_nl("???"); goto done; {this won't happen}
+@.???@>
+ end;
+@<Print information for adjacent knots |p| and |q|@>;
+p:=q;
+if (p<>h)or(left_type(h)<>endpoint) then
+ @<Print two dots, followed by |given| or |curl| if present@>;
+until p=h;
+if left_type(h)<>endpoint then print("cycle");
+done:end_diagnostic(true);
+end;
+
+@ @<Print information for adjacent knots...@>=
+print_two(x_coord(p),y_coord(p));
+case right_type(p) of
+endpoint: begin if left_type(p)=open then print("{open?}"); {can't happen}
+@.open?@>
+ if (left_type(q)<>endpoint)or(q<>h) then q:=null; {force an error}
+ goto done1;
+ end;
+explicit: @<Print control points between |p| and |q|, then |goto done1|@>;
+open: @<Print information for a curve that begins |open|@>;
+curl,given: @<Print information for a curve that begins |curl| or |given|@>;
+othercases print("???") {can't happen}
+@.???@>
+endcases;@/
+if left_type(q)<=explicit then print("..control?") {can't happen}
+@.control?@>
+else if (right_tension(p)<>unity)or(left_tension(q)<>unity) then
+ @<Print tension between |p| and |q|@>;
+done1:
+
+@ Since |n_sin_cos| produces |fraction| results, which we will print as if they
+were |scaled|, the magnitude of a |given| direction vector will be~4096.
+
+@<Print two dots...@>=
+begin print_nl(" ..");
+if left_type(p)=given then
+ begin n_sin_cos(left_given(p)); print_char("{");
+ print_scaled(n_cos); print_char(",");
+ print_scaled(n_sin); print_char("}");
+ end
+else if left_type(p)=curl then
+ begin print("{curl "); print_scaled(left_curl(p)); print_char("}");
+ end;
+end
+
+@ @<Print tension between |p| and |q|@>=
+begin print("..tension ");
+if right_tension(p)<0 then print("atleast");
+print_scaled(abs(right_tension(p)));
+if right_tension(p)<>left_tension(q) then
+ begin print(" and ");
+ if left_tension(q)<0 then print("atleast");
+ print_scaled(abs(left_tension(q)));
+ end;
+end
+
+@ @<Print control points between |p| and |q|, then |goto done1|@>=
+begin print("..controls "); print_two(right_x(p),right_y(p)); print(" and ");
+if left_type(q)<>explicit then print("??") {can't happen}
+@.??@>
+else print_two(left_x(q),left_y(q));
+goto done1;
+end
+
+@ @<Print information for a curve that begins |open|@>=
+if (left_type(p)<>explicit)and(left_type(p)<>open) then
+ print("{open?}") {can't happen}
+@.open?@>
+
+@ A curl of 1 is shown explicitly, so that the user sees clearly that
+\MF's default curl is present.
+
+@<Print information for a curve that begins |curl|...@>=
+begin if left_type(p)=open then print("??"); {can't happen}
+@.??@>
+if right_type(p)=curl then
+ begin print("{curl "); print_scaled(right_curl(p));
+ end
+else begin n_sin_cos(right_given(p)); print_char("{");
+ print_scaled(n_cos); print_char(","); print_scaled(n_sin);
+ end;
+print_char("}");
+end
+
+@ If we want to duplicate a knot node, we can say |copy_knot|:
+
+@p function copy_knot(@!p:pointer):pointer;
+var @!q:pointer; {the copy}
+@!k:0..knot_node_size-1; {runs through the words of a knot node}
+begin q:=get_node(knot_node_size);
+for k:=0 to knot_node_size-1 do mem[q+k]:=mem[p+k];
+copy_knot:=q;
+end;
+
+@ The |copy_path| routine makes a clone of a given path.
+
+@p function copy_path(@!p:pointer):pointer;
+label exit;
+var @!q,@!pp,@!qq:pointer; {for list manipulation}
+begin q:=get_node(knot_node_size); {this will correspond to |p|}
+qq:=q; pp:=p;
+loop@+ begin left_type(qq):=left_type(pp);
+ right_type(qq):=right_type(pp);@/
+ x_coord(qq):=x_coord(pp); y_coord(qq):=y_coord(pp);@/
+ left_x(qq):=left_x(pp); left_y(qq):=left_y(pp);@/
+ right_x(qq):=right_x(pp); right_y(qq):=right_y(pp);@/
+ if link(pp)=p then
+ begin link(qq):=q; copy_path:=q; return;
+ end;
+ link(qq):=get_node(knot_node_size); qq:=link(qq); pp:=link(pp);
+ end;
+exit:end;
+
+@ Similarly, there's a way to copy the {\sl reverse\/} of a path. This procedure
+returns a pointer to the first node of the copy, if the path is a cycle,
+but to the final node of a non-cyclic copy. The global
+variable |path_tail| will point to the final node of the original path;
+this trick makes it easier to implement `\&{doublepath}'.
+
+All node types are assumed to be |endpoint| or |explicit| only.
+
+@p function htap_ypoc(@!p:pointer):pointer;
+label exit;
+var @!q,@!pp,@!qq,@!rr:pointer; {for list manipulation}
+begin q:=get_node(knot_node_size); {this will correspond to |p|}
+qq:=q; pp:=p;
+loop@+ begin right_type(qq):=left_type(pp); left_type(qq):=right_type(pp);@/
+ x_coord(qq):=x_coord(pp); y_coord(qq):=y_coord(pp);@/
+ right_x(qq):=left_x(pp); right_y(qq):=left_y(pp);@/
+ left_x(qq):=right_x(pp); left_y(qq):=right_y(pp);@/
+ if link(pp)=p then
+ begin link(q):=qq; path_tail:=pp; htap_ypoc:=q; return;
+ end;
+ rr:=get_node(knot_node_size); link(rr):=qq; qq:=rr; pp:=link(pp);
+ end;
+exit:end;
+
+@ @<Glob...@>=
+@!path_tail:pointer; {the node that links to the beginning of a path}
+
+@ When a cyclic list of knot nodes is no longer needed, it can be recycled by
+calling the following subroutine.
+
+@<Declare the recycling subroutines@>=
+procedure toss_knot_list(@!p:pointer);
+var @!q:pointer; {the node being freed}
+@!r:pointer; {the next node}
+begin q:=p;
+repeat r:=link(q); free_node(q,knot_node_size); q:=r;
+until q=p;
+end;
+
+@* \[18] Choosing control points.
+Now we must actually delve into one of \MF's more difficult routines,
+the |make_choices| procedure that chooses angles and control points for
+the splines of a curve when the user has not specified them explicitly.
+The parameter to |make_choices| points to a list of knots and
+path information, as described above.
+
+A path decomposes into independent segments at ``breakpoint'' knots,
+which are knots whose left and right angles are both prespecified in
+some way (i.e., their |left_type| and |right_type| aren't both open).
+
+@p @t\4@>@<Declare the procedure called |solve_choices|@>@;
+procedure make_choices(@!knots:pointer);
+label done;
+var @!h:pointer; {the first breakpoint}
+@!p,@!q:pointer; {consecutive breakpoints being processed}
+@<Other local variables for |make_choices|@>@;
+begin check_arith; {make sure that |arith_error=false|}
+if internal[tracing_choices]>0 then
+ print_path(knots,", before choices",true);
+@<If consecutive knots are equal, join them explicitly@>;
+@<Find the first breakpoint, |h|, on the path;
+ insert an artificial breakpoint if the path is an unbroken cycle@>;
+p:=h;
+repeat @<Fill in the control points between |p| and the next breakpoint,
+ then advance |p| to that breakpoint@>;
+until p=h;
+if internal[tracing_choices]>0 then
+ print_path(knots,", after choices",true);
+if arith_error then @<Report an unexpected problem during the choice-making@>;
+end;
+
+@ @<Report an unexpected problem during the choice...@>=
+begin print_err("Some number got too big");
+@.Some number got too big@>
+help2("The path that I just computed is out of range.")@/
+ ("So it will probably look funny. Proceed, for a laugh.");
+put_get_error; arith_error:=false;
+end
+
+@ Two knots in a row with the same coordinates will always be joined
+by an explicit ``curve'' whose control points are identical with the
+knots.
+
+@<If consecutive knots are equal, join them explicitly@>=
+p:=knots;
+repeat q:=link(p);
+if x_coord(p)=x_coord(q) then if y_coord(p)=y_coord(q) then
+ if right_type(p)>explicit then
+ begin right_type(p):=explicit;
+ if left_type(p)=open then
+ begin left_type(p):=curl; left_curl(p):=unity;
+ end;
+ left_type(q):=explicit;
+ if right_type(q)=open then
+ begin right_type(q):=curl; right_curl(q):=unity;
+ end;
+ right_x(p):=x_coord(p); left_x(q):=x_coord(p);@/
+ right_y(p):=y_coord(p); left_y(q):=y_coord(p);
+ end;
+p:=q;
+until p=knots
+
+@ If there are no breakpoints, it is necessary to compute the direction
+angles around an entire cycle. In this case the |left_type| of the first
+node is temporarily changed to |end_cycle|.
+
+@d end_cycle=open+1
+
+@<Find the first breakpoint, |h|, on the path...@>=
+h:=knots;
+loop@+ begin if left_type(h)<>open then goto done;
+ if right_type(h)<>open then goto done;
+ h:=link(h);
+ if h=knots then
+ begin left_type(h):=end_cycle; goto done;
+ end;
+ end;
+done:
+
+@ If |right_type(p)<given| and |q=link(p)|, we must have
+|right_type(p)=left_type(q)=explicit| or |endpoint|.
+
+@<Fill in the control points between |p| and the next breakpoint...@>=
+q:=link(p);
+if right_type(p)>=given then
+ begin while (left_type(q)=open)and(right_type(q)=open) do q:=link(q);
+ @<Fill in the control information between
+ consecutive breakpoints |p| and |q|@>;
+ end;
+p:=q
+
+@ Before we can go further into the way choices are made, we need to
+consider the underlying theory. The basic ideas implemented in |make_choices|
+are due to John Hobby, who introduced the notion of ``mock curvature''
+@^Hobby, John Douglas@>
+at a knot. Angles are chosen so that they preserve mock curvature when
+a knot is passed, and this has been found to produce excellent results.
+
+It is convenient to introduce some notations that simplify the necessary
+formulas. Let $d_{k,k+1}=\vert z\k-z_k\vert$ be the (nonzero) distance
+between knots |k| and |k+1|; and let
+$${z\k-z_k\over z_k-z_{k-1}}={d_{k,k+1}\over d_{k-1,k}}e^{i\psi_k}$$
+so that a polygonal line from $z_{k-1}$ to $z_k$ to $z\k$ turns left
+through an angle of~$\psi_k$. We assume that $\vert\psi_k\vert\L180^\circ$.
+The control points for the spline from $z_k$ to $z\k$ will be denoted by
+$$\eqalign{z_k^+&=z_k+
+ \textstyle{1\over3}\rho_k e^{i\theta_k}(z\k-z_k),\cr
+ z\k^-&=z\k-
+ \textstyle{1\over3}\sigma\k e^{-i\phi\k}(z\k-z_k),\cr}$$
+where $\rho_k$ and $\sigma\k$ are nonnegative ``velocity ratios'' at the
+beginning and end of the curve, while $\theta_k$ and $\phi\k$ are the
+corresponding ``offset angles.'' These angles satisfy the condition
+$$\theta_k+\phi_k+\psi_k=0,\eqno(*)$$
+whenever the curve leaves an intermediate knot~|k| in the direction that
+it enters.
+
+@ Let $\alpha_k$ and $\beta\k$ be the reciprocals of the ``tension'' of
+the curve at its beginning and ending points. This means that
+$\rho_k=\alpha_k f(\theta_k,\phi\k)$ and $\sigma\k=\beta\k f(\phi\k,\theta_k)$,
+where $f(\theta,\phi)$ is \MF's standard velocity function defined in
+the |velocity| subroutine. The cubic spline $B(z_k^{\phantom+},z_k^+,
+z\k^-,z\k^{\phantom+};t)$
+has curvature
+@^curvature@>
+$${2\sigma\k\sin(\theta_k+\phi\k)-6\sin\theta_k\over\rho_k^2d_{k,k+1}}
+\qquad{\rm and}\qquad
+{2\rho_k\sin(\theta_k+\phi\k)-6\sin\phi\k\over\sigma\k^2d_{k,k+1}}$$
+at |t=0| and |t=1|, respectively. The mock curvature is the linear
+@^mock curvature@>
+approximation to this true curvature that arises in the limit for
+small $\theta_k$ and~$\phi\k$, if second-order terms are discarded.
+The standard velocity function satisfies
+$$f(\theta,\phi)=1+O(\theta^2+\theta\phi+\phi^2);$$
+hence the mock curvatures are respectively
+$${2\beta\k(\theta_k+\phi\k)-6\theta_k\over\alpha_k^2d_{k,k+1}}
+\qquad{\rm and}\qquad
+{2\alpha_k(\theta_k+\phi\k)-6\phi\k\over\beta\k^2d_{k,k+1}}.\eqno(**)$$
+
+@ The turning angles $\psi_k$ are given, and equation $(*)$ above
+determines $\phi_k$ when $\theta_k$ is known, so the task of
+angle selection is essentially to choose appropriate values for each
+$\theta_k$. When equation~$(*)$ is used to eliminate $\phi$~variables
+from $(**)$, we obtain a system of linear equations of the form
+$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$
+where
+$$A_k={\alpha_{k-1}\over\beta_k^2d_{k-1,k}},
+\qquad B_k={3-\alpha_{k-1}\over\beta_k^2d_{k-1,k}},
+\qquad C_k={3-\beta\k\over\alpha_k^2d_{k,k+1}},
+\qquad D_k={\beta\k\over\alpha_k^2d_{k,k+1}}.$$
+The tensions are always $3\over4$ or more, hence each $\alpha$ and~$\beta$
+will be at most $4\over3$. It follows that $B_k\G{5\over4}A_k$ and
+$C_k\G{5\over4}D_k$; hence the equations are diagonally dominant;
+hence they have a unique solution. Moreover, in most cases the tensions
+are equal to~1, so that $B_k=2A_k$ and $C_k=2D_k$. This makes the
+solution numerically stable, and there is an exponential damping
+effect: The data at knot $k\pm j$ affects the angle at knot~$k$ by
+a factor of~$O(2^{-j})$.
+
+@ However, we still must consider the angles at the starting and ending
+knots of a non-cyclic path. These angles might be given explicitly, or
+they might be specified implicitly in terms of an amount of ``curl.''
+
+Let's assume that angles need to be determined for a non-cyclic path
+starting at $z_0$ and ending at~$z_n$. Then equations of the form
+$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta_{k+1}=R_k$$
+have been given for $0<k<n$, and it will be convenient to introduce
+equations of the same form for $k=0$ and $k=n$, where
+$$A_0=B_0=C_n=D_n=0.$$
+If $\theta_0$ is supposed to have a given value $E_0$, we simply
+define $C_0=1$, $D_0=0$, and $R_0=E_0$. Otherwise a curl
+parameter, $\gamma_0$, has been specified at~$z_0$; this means
+that the mock curvature at $z_0$ should be $\gamma_0$ times the
+mock curvature at $z_1$; i.e.,
+$${2\beta_1(\theta_0+\phi_1)-6\theta_0\over\alpha_0^2d_{01}}
+=\gamma_0{2\alpha_0(\theta_0+\phi_1)-6\phi_1\over\beta_1^2d_{01}}.$$
+This equation simplifies to
+$$(\alpha_0\chi_0+3-\beta_1)\theta_0+
+ \bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\theta_1=
+ -\bigl((3-\alpha_0)\chi_0+\beta_1\bigr)\psi_1,$$
+where $\chi_0=\alpha_0^2\gamma_0/\beta_1^2$; so we can set $C_0=
+\chi_0\alpha_0+3-\beta_1$, $D_0=(3-\alpha_0)\chi_0+\beta_1$, $R_0=-D_0\psi_1$.
+It can be shown that $C_0>0$ and $C_0B_1-A_1D_0>0$ when $\gamma_0\G0$,
+hence the linear equations remain nonsingular.
+
+Similar considerations apply at the right end, when the final angle $\phi_n$
+may or may not need to be determined. It is convenient to let $\psi_n=0$,
+hence $\theta_n=-\phi_n$. We either have an explicit equation $\theta_n=E_n$,
+or we have
+$$\bigl((3-\beta_n)\chi_n+\alpha_{n-1}\bigr)\theta_{n-1}+
+(\beta_n\chi_n+3-\alpha_{n-1})\theta_n=0,\qquad
+ \chi_n={\beta_n^2\gamma_n\over\alpha_{n-1}^2}.$$
+
+When |make_choices| chooses angles, it must compute the coefficients of
+these linear equations, then solve the equations. To compute the coefficients,
+it is necessary to compute arctangents of the given turning angles~$\psi_k$.
+When the equations are solved, the chosen directions $\theta_k$ are put
+back into the form of control points by essentially computing sines and
+cosines.
+
+@ OK, we are ready to make the hard choices of |make_choices|.
+Most of the work is relegated to an auxiliary procedure
+called |solve_choices|, which has been introduced to keep
+|make_choices| from being extremely long.
+
+@<Fill in the control information between...@>=
+@<Calculate the turning angles $\psi_k$ and the distances $d_{k,k+1}$;
+ set $n$ to the length of the path@>;
+@<Remove |open| types at the breakpoints@>;
+solve_choices(p,q,n)
+
+@ It's convenient to precompute quantities that will be needed several
+times later. The values of |delta_x[k]| and |delta_y[k]| will be the
+coordinates of $z\k-z_k$, and the magnitude of this vector will be
+|delta[k]=@t$d_{k,k+1}$@>|. The path angle $\psi_k$ between $z_k-z_{k-1}$
+and $z\k-z_k$ will be stored in |psi[k]|.
+
+@<Glob...@>=
+@!delta_x,@!delta_y,@!delta:array[0..path_size] of scaled; {knot differences}
+@!psi:array[1..path_size] of angle; {turning angles}
+
+@ @<Other local variables for |make_choices|@>=
+@!k,@!n:0..path_size; {current and final knot numbers}
+@!s,@!t:pointer; {registers for list traversal}
+@!delx,@!dely:scaled; {directions where |open| meets |explicit|}
+@!sine,@!cosine:fraction; {trig functions of various angles}
+
+@ @<Calculate the turning angles...@>=
+k:=0; s:=p; n:=path_size;
+repeat t:=link(s);
+delta_x[k]:=x_coord(t)-x_coord(s);
+delta_y[k]:=y_coord(t)-y_coord(s);
+delta[k]:=pyth_add(delta_x[k],delta_y[k]);
+if k>0 then
+ begin sine:=make_fraction(delta_y[k-1],delta[k-1]);
+ cosine:=make_fraction(delta_x[k-1],delta[k-1]);
+ psi[k]:=n_arg(take_fraction(delta_x[k],cosine)+
+ take_fraction(delta_y[k],sine),
+ take_fraction(delta_y[k],cosine)-
+ take_fraction(delta_x[k],sine));
+ end;
+@:METAFONT capacity exceeded path size}{\quad path size@>
+incr(k); s:=t;
+if k=path_size then overflow("path size",path_size);
+if s=q then n:=k;
+until (k>=n)and(left_type(s)<>end_cycle);
+if k=n then psi[n]:=0@+else psi[k]:=psi[1]
+
+@ When we get to this point of the code, |right_type(p)| is either
+|given| or |curl| or |open|. If it is |open|, we must have
+|left_type(p)=end_cycle| or |left_type(p)=explicit|. In the latter
+case, the |open| type is converted to |given|; however, if the
+velocity coming into this knot is zero, the |open| type is
+converted to a |curl|, since we don't know the incoming direction.
+
+Similarly, |left_type(q)| is either |given| or |curl| or |open| or
+|end_cycle|. The |open| possibility is reduced either to |given| or to |curl|.
+
+@<Remove |open| types at the breakpoints@>=
+if left_type(q)=open then
+ begin delx:=right_x(q)-x_coord(q); dely:=right_y(q)-y_coord(q);
+ if (delx=0)and(dely=0) then
+ begin left_type(q):=curl; left_curl(q):=unity;
+ end
+ else begin left_type(q):=given; left_given(q):=n_arg(delx,dely);
+ end;
+ end;
+if (right_type(p)=open)and(left_type(p)=explicit) then
+ begin delx:=x_coord(p)-left_x(p); dely:=y_coord(p)-left_y(p);
+ if (delx=0)and(dely=0) then
+ begin right_type(p):=curl; right_curl(p):=unity;
+ end
+ else begin right_type(p):=given; right_given(p):=n_arg(delx,dely);
+ end;
+ end
+
+@ Linear equations need to be solved whenever |n>1|; and also when |n=1|
+and exactly one of the breakpoints involves a curl. The simplest case occurs
+when |n=1| and there is a curl at both breakpoints; then we simply draw
+a straight line.
+
+But before coding up the simple cases, we might as well face the general case,
+since we must deal with it sooner or later, and since the general case
+is likely to give some insight into the way simple cases can be handled best.
+
+When there is no cycle, the linear equations to be solved form a tri-diagonal
+system, and we can apply the standard technique of Gaussian elimination
+to convert that system to a sequence of equations of the form
+$$\theta_0+u_0\theta_1=v_0,\quad
+\theta_1+u_1\theta_2=v_1,\quad\ldots,\quad
+\theta_{n-1}+u_{n-1}\theta_n=v_{n-1},\quad
+\theta_n=v_n.$$
+It is possible to do this diagonalization while generating the equations.
+Once $\theta_n$ is known, it is easy to determine $\theta_{n-1}$, \dots,
+$\theta_1$, $\theta_0$; thus, the equations will be solved.
+
+The procedure is slightly more complex when there is a cycle, but the
+basic idea will be nearly the same. In the cyclic case the right-hand
+sides will be $v_k+w_k\theta_0$ instead of simply $v_k$, and we will start
+the process off with $u_0=v_0=0$, $w_0=1$. The final equation will be not
+$\theta_n=v_n$ but $\theta_n+u_n\theta_1=v_n+w_n\theta_0$; an appropriate
+ending routine will take account of the fact that $\theta_n=\theta_0$ and
+eliminate the $w$'s from the system, after which the solution can be
+obtained as before.
+
+When $u_k$, $v_k$, and $w_k$ are being computed, the three pointer
+variables |r|, |s|,~|t| will point respectively to knots |k-1|, |k|,
+and~|k+1|. The $u$'s and $w$'s are scaled by $2^{28}$, i.e., they are
+of type |fraction|; the $\theta$'s and $v$'s are of type |angle|.
+
+@<Glob...@>=
+@!theta:array[0..path_size] of angle; {values of $\theta_k$}
+@!uu:array[0..path_size] of fraction; {values of $u_k$}
+@!vv:array[0..path_size] of angle; {values of $v_k$}
+@!ww:array[0..path_size] of fraction; {values of $w_k$}
+
+@ Our immediate problem is to get the ball rolling by setting up the
+first equation or by realizing that no equations are needed, and to fit
+this initialization into a framework suitable for the overall computation.
+
+@<Declare the procedure called |solve_choices|@>=
+@t\4@>@<Declare subroutines needed by |solve_choices|@>@;
+procedure solve_choices(@!p,@!q:pointer;@!n:halfword);
+label found,exit;
+var @!k:0..path_size; {current knot number}
+@!r,@!s,@!t:pointer; {registers for list traversal}
+@<Other local variables for |solve_choices|@>@;
+begin k:=0; s:=p;
+loop@+ begin t:=link(s);
+ if k=0 then @<Get the linear equations started; or |return|
+ with the control points in place, if linear equations
+ needn't be solved@>
+ else case left_type(s) of
+ end_cycle,open:@<Set up equation to match mock curvatures
+ at $z_k$; then |goto found| with $\theta_n$
+ adjusted to equal $\theta_0$, if a cycle has ended@>;
+ curl:@<Set up equation for a curl at $\theta_n$
+ and |goto found|@>;
+ given:@<Calculate the given value of $\theta_n$
+ and |goto found|@>;
+ end; {there are no other cases}
+ r:=s; s:=t; incr(k);
+ end;
+found:@<Finish choosing angles and assigning control points@>;
+exit:end;
+
+@ On the first time through the loop, we have |k=0| and |r| is not yet
+defined. The first linear equation, if any, will have $A_0=B_0=0$.
+
+@<Get the linear equations started...@>=
+case right_type(s) of
+given: if left_type(t)=given then @<Reduce to simple case of two givens
+ and |return|@>
+ else @<Set up the equation for a given value of $\theta_0$@>;
+curl: if left_type(t)=curl then @<Reduce to simple case of straight line
+ and |return|@>
+ else @<Set up the equation for a curl at $\theta_0$@>;
+open: begin uu[0]:=0; vv[0]:=0; ww[0]:=fraction_one;
+ end; {this begins a cycle}
+end {there are no other cases}
+
+@ The general equation that specifies equality of mock curvature at $z_k$ is
+$$A_k\theta_{k-1}+(B_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k,$$
+as derived above. We want to combine this with the already-derived equation
+$\theta_{k-1}+u_{k-1}\theta_k=v_{k-1}+w_{k-1}\theta_0$ in order to obtain
+a new equation
+$\theta_k+u_k\theta\k=v_k+w_k\theta_0$. This can be done by dividing the
+equation
+$$(B_k-u_{k-1}A_k+C_k)\theta_k+D_k\theta\k=-B_k\psi_k-D_k\psi\k-A_kv_{k-1}
+ -A_kw_{k-1}\theta_0$$
+by $B_k-u_{k-1}A_k+C_k$. The trick is to do this carefully with
+fixed-point arithmetic, avoiding the chance of overflow while retaining
+suitable precision.
+
+The calculations will be performed in several registers that
+provide temporary storage for intermediate quantities.
+
+@<Other local variables for |solve_choices|@>=
+@!aa,@!bb,@!cc,@!ff,@!acc:fraction; {temporary registers}
+@!dd,@!ee:scaled; {likewise, but |scaled|}
+@!lt,@!rt:scaled; {tension values}
+
+@ @<Set up equation to match mock curvatures...@>=
+begin @<Calculate the values $\\{aa}=A_k/B_k$, $\\{bb}=D_k/C_k$,
+ $\\{dd}=(3-\alpha_{k-1})d_{k,k+1}$, $\\{ee}=(3-\beta\k)d_{k-1,k}$,
+ and $\\{cc}=(B_k-u_{k-1}A_k)/B_k$@>;
+@<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>;
+uu[k]:=take_fraction(ff,bb);
+@<Calculate the values of $v_k$ and $w_k$@>;
+if left_type(s)=end_cycle then
+ @<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>;
+end
+
+@ Since tension values are never less than 3/4, the values |aa| and
+|bb| computed here are never more than 4/5.
+
+@<Calculate the values $\\{aa}=...@>=
+if abs(right_tension(r))=unity then
+ begin aa:=fraction_half; dd:=2*delta[k];
+ end
+else begin aa:=make_fraction(unity,3*abs(right_tension(r))-unity);
+ dd:=take_fraction(delta[k],
+ fraction_three-make_fraction(unity,abs(right_tension(r))));
+ end;
+if abs(left_tension(t))=unity then
+ begin bb:=fraction_half; ee:=2*delta[k-1];
+ end
+else begin bb:=make_fraction(unity,3*abs(left_tension(t))-unity);
+ ee:=take_fraction(delta[k-1],
+ fraction_three-make_fraction(unity,abs(left_tension(t))));
+ end;
+cc:=fraction_one-take_fraction(uu[k-1],aa)
+
+@ The ratio to be calculated in this step can be written in the form
+$$\beta_k^2\cdot\\{ee}\over\beta_k^2\cdot\\{ee}+\alpha_k^2\cdot
+ \\{cc}\cdot\\{dd},$$
+because of the quantities just calculated. The values of |dd| and |ee|
+will not be needed after this step has been performed.
+
+@<Calculate the ratio $\\{ff}=C_k/(C_k+B_k-u_{k-1}A_k)$@>=
+dd:=take_fraction(dd,cc); lt:=abs(left_tension(s)); rt:=abs(right_tension(s));
+if lt<>rt then {$\beta_k^{-1}\ne\alpha_k^{-1}$}
+ if lt<rt then
+ begin ff:=make_fraction(lt,rt);
+ ff:=take_fraction(ff,ff); {$\alpha_k^2/\beta_k^2$}
+ dd:=take_fraction(dd,ff);
+ end
+ else begin ff:=make_fraction(rt,lt);
+ ff:=take_fraction(ff,ff); {$\beta_k^2/\alpha_k^2$}
+ ee:=take_fraction(ee,ff);
+ end;
+ff:=make_fraction(ee,ee+dd)
+
+@ The value of $u_{k-1}$ will be |<=1| except when $k=1$ and the previous
+equation was specified by a curl. In that case we must use a special
+method of computation to prevent overflow.
+
+Fortunately, the calculations turn out to be even simpler in this ``hard''
+case. The curl equation makes $w_0=0$ and $v_0=-u_0\psi_1$, hence
+$-B_1\psi_1-A_1v_0=-(B_1-u_0A_1)\psi_1=-\\{cc}\cdot B_1\psi_1$.
+
+@<Calculate the values of $v_k$ and $w_k$@>=
+acc:=-take_fraction(psi[k+1],uu[k]);
+if right_type(r)=curl then
+ begin ww[k]:=0;
+ vv[k]:=acc-take_fraction(psi[1],fraction_one-ff);
+ end
+else begin ff:=make_fraction(fraction_one-ff,cc); {this is
+ $B_k/(C_k+B_k-u_{k-1}A_k)<5$}
+ acc:=acc-take_fraction(psi[k],ff);
+ ff:=take_fraction(ff,aa); {this is $A_k/(C_k+B_k-u_{k-1}A_k)$}
+ vv[k]:=acc-take_fraction(vv[k-1],ff);
+ if ww[k-1]=0 then ww[k]:=0
+ else ww[k]:=-take_fraction(ww[k-1],ff);
+ end
+
+@ When a complete cycle has been traversed, we have $\theta_k+u_k\theta\k=
+v_k+w_k\theta_0$, for |1<=k<=n|. We would like to determine the value of
+$\theta_n$ and reduce the system to the form $\theta_k+u_k\theta\k=v_k$
+for |0<=k<n|, so that the cyclic case can be finished up just as if there
+were no cycle.
+
+The idea in the following code is to observe that
+$$\eqalign{\theta_n&=v_n+w_n\theta_0-u_n\theta_1=\cdots\cr
+&=v_n+w_n\theta_0-u_n\bigl(v_1+w_1\theta_0-u_1(v_2+\cdots
+ -u_{n-2}(v_{n-1}+w_{n-1}\theta_0-u_{n-1}\theta_0)\ldots{})\bigr),\cr}$$
+so we can solve for $\theta_n=\theta_0$.
+
+@<Adjust $\theta_n$ to equal $\theta_0$ and |goto found|@>=
+begin aa:=0; bb:=fraction_one; {we have |k=n|}
+repeat decr(k);
+if k=0 then k:=n;
+aa:=vv[k]-take_fraction(aa,uu[k]);
+bb:=ww[k]-take_fraction(bb,uu[k]);
+until k=n; {now $\theta_n=\\{aa}+\\{bb}\cdot\theta_n$}
+aa:=make_fraction(aa,fraction_one-bb);
+theta[n]:=aa; vv[0]:=aa;
+for k:=1 to n-1 do vv[k]:=vv[k]+take_fraction(aa,ww[k]);
+goto found;
+end
+
+@ @d reduce_angle(#)==if abs(#)>one_eighty_deg then
+ if #>0 then #:=#-three_sixty_deg@+else #:=#+three_sixty_deg
+
+@<Calculate the given value of $\theta_n$...@>=
+begin theta[n]:=left_given(s)-n_arg(delta_x[n-1],delta_y[n-1]);
+reduce_angle(theta[n]);
+goto found;
+end
+
+@ @<Set up the equation for a given value of $\theta_0$@>=
+begin vv[0]:=right_given(s)-n_arg(delta_x[0],delta_y[0]);
+reduce_angle(vv[0]);
+uu[0]:=0; ww[0]:=0;
+end
+
+@ @<Set up the equation for a curl at $\theta_0$@>=
+begin cc:=right_curl(s); lt:=abs(left_tension(t)); rt:=abs(right_tension(s));
+if (rt=unity)and(lt=unity) then
+ uu[0]:=make_fraction(cc+cc+unity,cc+two)
+else uu[0]:=curl_ratio(cc,rt,lt);
+vv[0]:=-take_fraction(psi[1],uu[0]); ww[0]:=0;
+end
+
+@ @<Set up equation for a curl at $\theta_n$...@>=
+begin cc:=left_curl(s); lt:=abs(left_tension(s)); rt:=abs(right_tension(r));
+if (rt=unity)and(lt=unity) then
+ ff:=make_fraction(cc+cc+unity,cc+two)
+else ff:=curl_ratio(cc,lt,rt);
+theta[n]:=-make_fraction(take_fraction(vv[n-1],ff),
+ fraction_one-take_fraction(ff,uu[n-1]));
+goto found;
+end
+
+@ The |curl_ratio| subroutine has three arguments, which our previous notation
+encourages us to call $\gamma$, $\alpha^{-1}$, and $\beta^{-1}$. It is
+a somewhat tedious program to calculate
+$${(3-\alpha)\alpha^2\gamma+\beta^3\over
+ \alpha^3\gamma+(3-\beta)\beta^2},$$
+with the result reduced to 4 if it exceeds 4. (This reduction of curl
+is necessary only if the curl and tension are both large.)
+The values of $\alpha$ and $\beta$ will be at most~4/3.
+
+@<Declare subroutines needed by |solve_choices|@>=
+function curl_ratio(@!gamma,@!a_tension,@!b_tension:scaled):fraction;
+var @!alpha,@!beta,@!num,@!denom,@!ff:fraction; {registers}
+begin alpha:=make_fraction(unity,a_tension);
+beta:=make_fraction(unity,b_tension);@/
+if alpha<=beta then
+ begin ff:=make_fraction(alpha,beta); ff:=take_fraction(ff,ff);
+ gamma:=take_fraction(gamma,ff);@/
+ beta:=beta div @'10000; {convert |fraction| to |scaled|}
+ denom:=take_fraction(gamma,alpha)+three-beta;
+ num:=take_fraction(gamma,fraction_three-alpha)+beta;
+ end
+else begin ff:=make_fraction(beta,alpha); ff:=take_fraction(ff,ff);
+ beta:=take_fraction(beta,ff) div @'10000; {convert |fraction| to |scaled|}
+ denom:=take_fraction(gamma,alpha)+(ff div 1365)-beta;
+ {$1365\approx 2^{12}/3$}
+ num:=take_fraction(gamma,fraction_three-alpha)+beta;
+ end;
+if num>=denom+denom+denom+denom then curl_ratio:=fraction_four
+else curl_ratio:=make_fraction(num,denom);
+end;
+
+@ We're in the home stretch now.
+
+@<Finish choosing angles and assigning control points@>=
+for k:=n-1 downto 0 do theta[k]:=vv[k]-take_fraction(theta[k+1],uu[k]);
+s:=p; k:=0;
+repeat t:=link(s);@/
+n_sin_cos(theta[k]); st:=n_sin; ct:=n_cos;@/
+n_sin_cos(-psi[k+1]-theta[k+1]); sf:=n_sin; cf:=n_cos;@/
+set_controls(s,t,k);@/
+incr(k); s:=t;
+until k=n
+
+@ The |set_controls| routine actually puts the control points into
+a pair of consecutive nodes |p| and~|q|. Global variables are used to
+record the values of $\sin\theta$, $\cos\theta$, $\sin\phi$, and
+$\cos\phi$ needed in this calculation.
+
+@<Glob...@>=
+@!st,@!ct,@!sf,@!cf:fraction; {sines and cosines}
+
+@ @<Declare subroutines needed by |solve_choices|@>=
+procedure set_controls(@!p,@!q:pointer;@!k:integer);
+var @!rr,@!ss:fraction; {velocities, divided by thrice the tension}
+@!lt,@!rt:scaled; {tensions}
+@!sine:fraction; {$\sin(\theta+\phi)$}
+begin lt:=abs(left_tension(q)); rt:=abs(right_tension(p));
+rr:=velocity(st,ct,sf,cf,rt);
+ss:=velocity(sf,cf,st,ct,lt);
+if (right_tension(p)<0)or(left_tension(q)<0) then @<Decrease the velocities,
+ if necessary, to stay inside the bounding triangle@>;
+right_x(p):=x_coord(p)+take_fraction(
+ take_fraction(delta_x[k],ct)-take_fraction(delta_y[k],st),rr);
+right_y(p):=y_coord(p)+take_fraction(
+ take_fraction(delta_y[k],ct)+take_fraction(delta_x[k],st),rr);
+left_x(q):=x_coord(q)-take_fraction(
+ take_fraction(delta_x[k],cf)+take_fraction(delta_y[k],sf),ss);
+left_y(q):=y_coord(q)-take_fraction(
+ take_fraction(delta_y[k],cf)-take_fraction(delta_x[k],sf),ss);
+right_type(p):=explicit; left_type(q):=explicit;
+end;
+
+@ The boundedness conditions $\\{rr}\L\sin\phi\,/\sin(\theta+\phi)$ and
+$\\{ss}\L\sin\theta\,/\sin(\theta+\phi)$ are to be enforced if $\sin\theta$,
+$\sin\phi$, and $\sin(\theta+\phi)$ all have the same sign. Otherwise
+there is no ``bounding triangle.''
+
+@<Decrease the velocities, if necessary...@>=
+if((st>=0)and(sf>=0))or((st<=0)and(sf<=0)) then
+ begin sine:=take_fraction(abs(st),cf)+take_fraction(abs(sf),ct);
+ if sine>0 then
+ begin sine:=take_fraction(sine,fraction_one+unity); {safety factor}
+ if right_tension(p)<0 then
+ if ab_vs_cd(abs(sf),fraction_one,rr,sine)<0 then
+ rr:=make_fraction(abs(sf),sine);
+ if left_tension(q)<0 then
+ if ab_vs_cd(abs(st),fraction_one,ss,sine)<0 then
+ ss:=make_fraction(abs(st),sine);
+ end;
+ end
+
+@ Only the simple cases remain to be handled.
+
+@<Reduce to simple case of two givens and |return|@>=
+begin aa:=n_arg(delta_x[0],delta_y[0]);@/
+n_sin_cos(right_given(p)-aa); ct:=n_cos; st:=n_sin;@/
+n_sin_cos(left_given(q)-aa); cf:=n_cos; sf:=-n_sin;@/
+set_controls(p,q,0); return;
+end
+
+@ @<Reduce to simple case of straight line and |return|@>=
+begin right_type(p):=explicit; left_type(q):=explicit;
+lt:=abs(left_tension(q)); rt:=abs(right_tension(p));
+if rt=unity then
+ begin if delta_x[0]>=0 then right_x(p):=x_coord(p)+((delta_x[0]+1) div 3)
+ else right_x(p):=x_coord(p)+((delta_x[0]-1) div 3);
+ if delta_y[0]>=0 then right_y(p):=y_coord(p)+((delta_y[0]+1) div 3)
+ else right_y(p):=y_coord(p)+((delta_y[0]-1) div 3);
+ end
+else begin ff:=make_fraction(unity,3*rt); {$\alpha/3$}
+ right_x(p):=x_coord(p)+take_fraction(delta_x[0],ff);
+ right_y(p):=y_coord(p)+take_fraction(delta_y[0],ff);
+ end;
+if lt=unity then
+ begin if delta_x[0]>=0 then left_x(q):=x_coord(q)-((delta_x[0]+1) div 3)
+ else left_x(q):=x_coord(q)-((delta_x[0]-1) div 3);
+ if delta_y[0]>=0 then left_y(q):=y_coord(q)-((delta_y[0]+1) div 3)
+ else left_y(q):=y_coord(q)-((delta_y[0]-1) div 3);
+ end
+else begin ff:=make_fraction(unity,3*lt); {$\beta/3$}
+ left_x(q):=x_coord(q)-take_fraction(delta_x[0],ff);
+ left_y(q):=y_coord(q)-take_fraction(delta_y[0],ff);
+ end;
+return;
+end
+
+@* \[19] Generating discrete moves.
+The purpose of the next part of \MF\ is to compute discrete approximations
+to curves described as parametric polynomial functions $z(t)$.
+We shall start with the low level first, because an efficient ``engine''
+is needed to support the high-level constructions.
+
+Most of the subroutines are based on variations of a single theme,
+namely the idea of {\sl bisection}. Given a Bernshte{\u\i}n polynomial
+@^Bernshte{\u\i}n, Serge{\u\i} Natanovich@>
+$$B(z_0,z_1,\ldots,z_n;t)=\sum_k{n\choose k}t^k(1-t)^{n-k}z_k,$$
+we can conveniently bisect its range as follows:
+
+\smallskip
+\textindent{1)} Let $z_k^{(0)}=z_k$, for |0<=k<=n|.
+
+\smallskip
+\textindent{2)} Let $z_k^{(j+1)}={1\over2}(z_k^{(j)}+z\k^{(j)})$, for
+|0<=k<n-j|, for |0<=j<n|.
+
+\smallskip\noindent
+Then
+$$B(z_0,z_1,\ldots,z_n;t)=B(z_0^{(0)},z_0^{(1)},\ldots,z_0^{(n)};2t)
+ =B(z_0^{(n)},z_1^{(n-1)},\ldots,z_n^{(0)};2t-1).$$
+This formula gives us the coefficients of polynomials to use over the ranges
+$0\L t\L{1\over2}$ and ${1\over2}\L t\L1$.
+
+In our applications it will usually be possible to work indirectly with
+numbers that allow us to deduce relevant properties of the polynomials
+without actually computing the polynomial values. We will deal with
+coefficients $Z_k=2^l(z_k-z_{k-1})$ for |1<=k<=n|, instead of
+the actual numbers $z_0$, $z_1$, \dots,~$z_n$, and the value of~|l| will
+increase by~1 at each bisection step. This technique reduces the
+amount of calculation needed for bisection and also increases the
+accuracy of evaluation (since one bit of precision is gained at each
+bisection). Indeed, the bisection process now becomes one level shorter:
+
+\smallskip
+\textindent{$1'$)} Let $Z_k^{(1)}=Z_k$, for |1<=k<=n|.
+
+\smallskip
+\textindent{$2'$)} Let $Z_k^{(j+1)}={1\over2}(Z_k^{(j)}+Z\k^{(j)})$, for
+|1<=k<=n-j|, for |1<=j<n|.
+
+\smallskip\noindent
+The relevant coefficients $(Z'_1,\ldots,Z'_n)$ and $(Z''_1,\ldots,Z''_n)$
+for the two subintervals after bisection are respectively
+$(Z_1^{(1)},Z_1^{(2)},\ldots,Z_1^{(n)})$ and
+$(Z_1^{(n)},Z_2^{(n-1)},\ldots,Z_n^{(1)})$.
+And the values of $z_0$ appropriate for the bisected interval are $z'_0=z_0$
+and $z''_0=z_0+(Z'_1+Z'_2+\cdots+Z'_n)/2^{l+1}$.
+
+Step $2'$ involves division by~2, which introduces computational errors
+of at most $1\over2$ at each step; thus after $l$~levels of bisection the
+integers $Z_k$ will differ from their true values by at most $(n-1)l/2$.
+This error rate is quite acceptable, considering that we have $l$~more
+bits of precision in the $Z$'s by comparison with the~$z$'s. Note also
+that the $Z$'s remain bounded; there's no danger of integer overflow, even
+though we have the identity $Z_k=2^l(z_k-z_{k-1})$ for arbitrarily large~$l$.
+
+In fact, we can show not only that the $Z$'s remain bounded, but also that
+they become nearly equal, since they are control points for a polynomial
+of one less degree. If $\vert Z\k-Z_k\vert\L M$ initially, it is possible
+to prove that $\vert Z\k-Z_k\vert\L\lceil M/2^l\rceil$ after $l$~levels
+of bisection, even in the presence of rounding errors. Here's the
+proof [cf.~Lane and Riesenfeld, {\sl IEEE Trans.\ on Pattern Analysis
+@^Lane, Jeffrey Michael@>
+@^Riesenfeld, Richard Franklin@>
+and Machine Intelligence\/ \bf PAMI-2} (1980), 35--46]: Assuming that
+$\vert Z\k-Z_k\vert\L M$ before bisection, we want to prove that
+$\vert Z\k-Z_k\vert\L\lceil M/2\rceil$ afterward. First we show that
+$\vert Z\k^{(j)}-Z_k^{(j)}\vert\L M$ for all $j$ and~$k$, by induction
+on~$j$; this follows from the fact that
+$$\bigl\vert\\{half}(a+b)-\\{half}(b+c)\bigr\vert\L
+ \max\bigl(\vert a-b\vert,\vert b-c\vert\bigr)$$
+holds for both of the rounding rules $\\{half}(x)=\lfloor x/2\rfloor$
+and $\\{half}(x)={\rm sign}(x)\lfloor\vert x/2\vert\rfloor$.
+(If $\vert a-b\vert$ and $\vert b-c\vert$ are equal, then
+$a+b$ and $b+c$ are both even or both odd. The rounding errors either
+cancel or round the numbers toward each other; hence
+$$\eqalign{\bigl\vert\\{half}(a+b)-\\{half}(b+c)\bigr\vert
+&\L\textstyle\bigl\vert{1\over2}(a+b)-{1\over2}(b+c)\bigr\vert\cr
+&=\textstyle\bigl\vert{1\over2}(a-b)+{1\over2}(b-c)\bigr\vert
+\L\max\bigl(\vert a-b\vert,\vert b-c\vert\bigr),\cr}$$
+as required. A simpler argument applies if $\vert a-b\vert$ and
+$\vert b-c\vert$ are unequal.) Now it is easy to see that
+$\vert Z_1^{(j+1)}-Z_1^{(j)}\vert\L\bigl\lfloor{1\over2}
+\vert Z_2^{(j)}-Z_1^{(j)}\vert+{1\over2}\bigr\rfloor
+\L\bigl\lfloor{1\over2}(M+1)\bigr\rfloor=\lceil M/2\rceil$.
+
+Another interesting fact about bisection is the identity
+$$Z_1'+\cdots+Z_n'+Z_1''+\cdots+Z_n''=2(Z_1+\cdots+Z_n+E),$$
+where $E$ is the sum of the rounding errors in all of the halving
+operations ($\vert E\vert\L n(n-1)/4$).
+
+@ We will later reduce the problem of digitizing a complex cubic
+$z(t)=B(z_0,z_1,z_2,z_3;t)$ to the following simpler problem:
+Given two real cubics
+$x(t)=B(x_0,x_1,x_2,x_3;t)$
+and $y(t)=B(y_0,y_1,y_2,y_3;t)$ that are monotone nondecreasing,
+determine the set of integer points
+$$P=\bigl\{\bigl(\lfloor x(t)\rfloor,\lfloor y(t)\rfloor\bigr)
+\bigm\vert 0\L t\L 1\bigr\}.$$
+Well, the problem isn't actually quite so clean as this; when the path
+goes very near an integer point $(a,b)$, computational errors may
+make us think that $P$ contains $(a-1,b)$ while in reality it should
+contain $(a,b-1)$. Furthermore, if the path goes {\sl exactly\/}
+through the integer points $(a-1,b-1)$ and
+$(a,b)$, we will want $P$ to contain one
+of the two points $(a-1,b)$ or $(a,b-1)$, so that $P$ can be described
+entirely by ``rook moves'' upwards or to the right; no diagonal
+moves from $(a-1,b-1)$ to~$(a,b)$ will be allowed.
+
+Thus, the set $P$ we wish to compute will merely be an approximation
+to the set described in the formula above. It will consist of
+$\lfloor x(1)\rfloor-\lfloor x(0)\rfloor$ rightward moves and
+$\lfloor y(1)\rfloor-\lfloor y(0)\rfloor$ upward moves, intermixed
+in some order. Our job will be to figure out a suitable order.
+
+The following recursive strategy suggests itself, when we recall that
+$x(0)=x_0$, $x(1)=x_3$, $y(0)=y_0$, and $y(1)=y_3$:
+
+\smallskip
+If $\lfloor x_0\rfloor=\lfloor x_3\rfloor$ then take
+$\lfloor y_3\rfloor-\lfloor y_0\rfloor$ steps up.
+
+Otherwise if $\lfloor y_0\rfloor=\lfloor y_3\rfloor$ then take
+$\lfloor x_3\rfloor-\lfloor x_0\rfloor$ steps to the right.
+
+Otherwise bisect the current cubics and repeat the process on both halves.
+
+\yskip\noindent
+This intuitively appealing formulation does not quite solve the problem,
+because it may never terminate. For example, it's not hard to see that
+no steps will {\sl ever\/} be taken if $(x_0,x_1,x_2,x_3)=(y_0,y_1,y_2,y_3)$!
+However, we can surmount this difficulty with a bit of care; so let's
+proceed to flesh out the algorithm as stated, before worrying about
+such details.
+
+The bisect-and-double strategy discussed above suggests that we represent
+$(x_0,x_1,x_2,x_3)$ by $(X_1,X_2,X_3)$, where $X_k=2^l(x_k-x_{k-1})$
+for some~$l$. Initially $l=16$, since the $x$'s are |scaled|.
+In order to deal with other aspects of the algorithm we will want to
+maintain also the quantities $m=\lfloor x_3\rfloor-\lfloor x_0\rfloor$
+and $R=2^l(x_0\bmod 1)$. Similarly,
+$(y_0,y_1,y_2,y_3)$ will be represented by $(Y_1,Y_2,Y_3)$,
+$n=\lfloor y_3\rfloor-\lfloor y_0\rfloor$,
+and $S=2^l(y_0\bmod 1)$. The algorithm now takes the following form:
+
+\smallskip
+If $m=0$ then take $n$ steps up.
+
+Otherwise if $n=0$ then take $m$ steps to the right.
+
+Otherwise bisect the current cubics and repeat the process on both halves.
+
+\smallskip\noindent
+The bisection process for $(X_1,X_2,X_3,m,R,l)$ reduces, in essence,
+to the following formulas:
+$$\vbox{\halign{$#\hfil$\cr
+X_2'=\\{half}(X_1+X_2),\quad
+X_2''=\\{half}(X_2+X_3),\quad
+X_3'=\\{half}(X_2'+X_2''),\cr
+X_1'=X_1,\quad
+X_1''=X_3',\quad
+X_3''=X_3,\cr
+R'=2R,\quad
+T=X_1'+X_2'+X_3'+R',\quad
+R''=T\bmod 2^{l+1},\cr
+m'=\lfloor T/2^{l+1}\rfloor,\quad
+m''=m-m'.\cr}}$$
+
+@ When $m=n=1$, the computation can be speeded up because we simply
+need to decide between two alternatives, (up,\thinspace right)
+versus (right,\thinspace up). There appears to be no simple, direct
+way to make the correct decision by looking at the values of
+$(X_1,X_2,X_3,R)$ and
+$(Y_1,Y_2,Y_3,S)$; but we can streamline the bisection process, and
+we can use the fact that only one of the two descendants needs to
+be examined after each bisection. Furthermore, we observed earlier
+that after several levels of bisection the $X$'s and $Y$'s will be nearly
+equal; so we will be justified in assuming that the curve is essentially a
+straight line. (This, incidentally, solves the problem of infinite
+recursion mentioned earlier.)
+
+It is possible to show that
+$$m=\bigl\lfloor(X_1+X_2+X_3+R+E)\,/\,2^l\bigr\rfloor,$$
+where $E$ is an accumulated rounding error that is at most
+$3\cdot(2^{l-16}-1)$ in absolute value. We will make sure that
+the $X$'s are less than $2^{28}$; hence when $l=30$ we must
+have |m<=1|. This proves that the special case $m=n=1$ is
+bound to be reached by the time $l=30$. Furthermore $l=30$ is
+a suitable time to make the straight line approximation,
+if the recursion hasn't already died out, because the maximum
+difference between $X$'s will then be $<2^{14}$; this corresponds
+to an error of $<1$ with respect to the original scaling.
+(Stating this another way, each bisection makes the curve two bits
+closer to a straight line, hence 14 bisections are sufficient for
+28-bit accuracy.)
+
+In the case of a straight line, the curve goes first right, then up,
+if and only if $(T-2^l)(2^l-S)>(U-2^l)(2^l-R)$, where
+$T=X_1+X_2+X_3+R$ and $U=Y_1+Y_2+Y_3+S$. For the actual curve
+essentially runs from $(R/2^l,S/2^l)$ to $(T/2^l,U/2^l)$, and
+we are testing whether or not $(1,1)$ is above the straight
+line connecting these two points. (This formula assumes that $(1,1)$
+is not exactly on the line.)
+
+@ We have glossed over the problem of tie-breaking in ambiguous
+cases when the cubic curve passes exactly through integer points.
+\MF\ finesses this problem by assuming that coordinates
+$(x,y)$ actually stand for slightly perturbed values $(x+\xi,y+\eta)$,
+where $\xi$ and~$\eta$ are infinitesimals whose signs will determine
+what to do when $x$ and/or~$y$ are exact integers. The quantities
+$\lfloor x\rfloor$ and~$\lfloor y\rfloor$ in the formulas above
+should actually read $\lfloor x+\xi\rfloor$ and $\lfloor y+\eta\rfloor$.
+
+If $x$ is a |scaled| value, we have $\lfloor x+\xi\rfloor=\lfloor x\rfloor$
+if $\xi>0$, and $\lfloor x+\xi\rfloor=\lfloor x-2^{-16}\rfloor$ if
+$\xi<0$. It is convenient to represent $\xi$ by the integer |xi_corr|,
+defined to be 0~if $\xi>0$ and 1~if $\xi<0$; then, for example, the
+integer $\lfloor x+\xi\rfloor$ can be computed as
+|floor_unscaled(x-xi_corr)|. Similarly, $\eta$ is conveniently
+represented by~|eta_corr|.
+
+In our applications the sign of $\xi-\eta$ will always be the same as
+the sign of $\xi$. Therefore it turns out that the rule for straight
+lines, as stated above, should be modified as follows in the case of
+ties: The line goes first right, then up, if and only if
+$(T-2^l)(2^l-S)+\xi>(U-2^l)(2^l-R)$. And this relation holds iff
+$|ab_vs_cd|(T-2^l,2^l-S,U-2^l,2^l-R)-|xi_corr|\ge0$.
+
+These conventions for rounding are symmetrical, in the sense that the
+digitized moves obtained from $(x_0,x_1,x_2,x_3,y_0,y_1,y_2,y_3,\xi,\eta)$
+will be exactly complementary to the moves that would be obtained from
+$(-x_3,-x_2,-x_1,-x_0,-y_3,-y_2,-y_1,-y_0,-\xi,-\eta)$, if arithmetic
+is exact. However, truncation errors in the bisection process might
+upset the symmetry. We can restore much of the lost symmetry by adding
+|xi_corr| or |eta_corr| when halving the data.
+
+@ One further possibility needs to be mentioned: The algorithm
+will be applied only to cubic polynomials $B(x_0,x_1,x_2,x_3;t)$ that
+are nondecreasing as $t$~varies from 0 to~1; this condition turns
+out to hold if and only if $x_0\L x_1$ and $x_2\L x_3$, and either
+$x_1\L x_2$ or $(x_1-x_2)^2\L(x_1-x_0)(x_3-x_2)$. If bisection were
+carried out with perfect accuracy, these relations would remain
+invariant. But rounding errors can creep in, hence the bisection
+algorithm can produce non-monotonic subproblems from monotonic
+initial conditions. This leads to the potential danger that $m$ or~$n$
+could become negative in the algorithm described above.
+
+For example, if we start with $(x_1-x_0,x_2-x_1,x_3-x_2)=
+(X_1,X_2,X_3)=(7,-16,39)$, the corresponding polynomial is
+monotonic, because $16^2<7\cdot39$. But the bisection algorithm
+produces the left descendant $(7,-5,3)$, which is nonmonotonic;
+its right descendant is~$(0,-1,3)$.
+
+\def\xt{{\tilde x}}
+Fortunately we can prove that such rounding errors will never cause
+the algorithm to make a tragic mistake. At every stage we are working
+with numbers corresponding to a cubic polynomial $B(\xt_0,
+\xt_1,\xt_2,\xt_3)$ that approximates some
+monotonic polynomial $B(x_0,x_1,x_2,x_3)$. The accumulated errors are
+controlled so that $\vert x_k-\xt_k\vert<\epsilon=3\cdot2^{-16}$.
+If bisection is done at some stage of the recursion, we have
+$m=\lfloor\xt_3\rfloor-\lfloor\xt_0\rfloor>0$, and the algorithm
+computes a bisection value $\bar x$ such that $m'=\lfloor\bar x\rfloor-
+\lfloor\xt_0\rfloor$
+and $m''=\lfloor\xt_3\rfloor-\lfloor\bar x\rfloor$. We want to prove
+that neither $m'$ nor $m''$ can be negative. Since $\bar x$ is an
+approximation to a value in the interval $[x_0,x_3]$, we have
+$\bar x>x_0-\epsilon$ and $\bar x<x_3+\epsilon$, hence $\bar x>
+\xt_0-2\epsilon$ and $\bar x<\xt_3+2\epsilon$.
+If $m'$ is negative we must have $\xt_0\bmod 1<2\epsilon$;
+if $m''$ is negative we must have $\xt_3\bmod 1>1-2\epsilon$.
+In either case the condition $\lfloor\xt_3\rfloor-\lfloor\xt_0\rfloor>0$
+implies that $\xt_3-\xt_0>1-2\epsilon$, hence $x_3-x_0>1-4\epsilon$.
+But it can be shown that if $B(x_0,x_1,x_2,x_3;t)$ is a monotonic
+cubic, then $B(x_0,x_1,x_2,x_3;{1\over2})$ is always between
+$.06[x_0,x_3]$ and $.94[x_0,x_3]$; and it is impossible for $\bar x$
+to be within~$\epsilon$ of such a number. Contradiction!
+(The constant .06 is actually $(2-\sqrt3\,)/4$; the worst case
+occurs for polynomials like $B(0,2-\sqrt3,1-\sqrt3,3;t)$.)
+
+@ OK, now that a long theoretical preamble has justified the
+bisection-and-doubling algorithm, we are ready to proceed with
+its actual coding. But we still haven't discussed the
+form of the output.
+
+For reasons to be discussed later, we shall find it convenient to
+record the output as follows: Moving one step up is represented by
+appending a `1' to a list; moving one step right is represented by
+adding unity to the element at the end of the list. Thus, for example,
+the net effect of ``(up, right, right, up, right)'' is to append
+$(3,2)$.
+
+The list is kept in a global array called |move|. Before starting the
+algorithm, \MF\ should check that $\\{move\_ptr}+\lfloor y_3\rfloor
+-\lfloor y_0\rfloor\L\\{move\_size}$, so that the list won't exceed
+the bounds of this array.
+
+@<Glob...@>=
+@!move:array[0..move_size] of integer; {the recorded moves}
+@!move_ptr:0..move_size; {the number of items in the |move| list}
+
+@ When bisection occurs, we ``push'' the subproblem corresponding
+to the right-hand subinterval onto the |bisect_stack| while
+we continue to work on the left-hand subinterval. Thus, the |bisect_stack|
+will hold $(X_1,X_2,X_3,R,m,Y_1,Y_2,Y_3,S,n,l)$ values for
+subproblems yet to be tackled.
+
+At most 15 subproblems will be on the stack at once (namely, for
+$l=15$,~16, \dots,~29); but the stack is bigger than this, because
+it is used also for more complicated bisection algorithms.
+
+@d stack_x1==bisect_stack[bisect_ptr] {stacked value of $X_1$}
+@d stack_x2==bisect_stack[bisect_ptr+1] {stacked value of $X_2$}
+@d stack_x3==bisect_stack[bisect_ptr+2] {stacked value of $X_3$}
+@d stack_r==bisect_stack[bisect_ptr+3] {stacked value of $R$}
+@d stack_m==bisect_stack[bisect_ptr+4] {stacked value of $m$}
+@d stack_y1==bisect_stack[bisect_ptr+5] {stacked value of $Y_1$}
+@d stack_y2==bisect_stack[bisect_ptr+6] {stacked value of $Y_2$}
+@d stack_y3==bisect_stack[bisect_ptr+7] {stacked value of $Y_3$}
+@d stack_s==bisect_stack[bisect_ptr+8] {stacked value of $S$}
+@d stack_n==bisect_stack[bisect_ptr+9] {stacked value of $n$}
+@d stack_l==bisect_stack[bisect_ptr+10] {stacked value of $l$}
+@d move_increment=11 {number of items pushed by |make_moves|}
+
+@<Glob...@>=
+@!bisect_stack:array[0..bistack_size] of integer;
+@!bisect_ptr:0..bistack_size;
+
+@ @<Check the ``constant'' values...@>=
+if 15*move_increment>bistack_size then bad:=31;
+
+@ The |make_moves| subroutine is given |scaled| values $(x_0,x_1,x_2,x_3)$
+and $(y_0,y_1,y_2,y_3)$ that represent monotone-nondecreasing polynomials;
+it makes $\lfloor x_3+\xi\rfloor-\lfloor x_0+\xi\rfloor$ rightward moves
+and $\lfloor y_3+\eta\rfloor-\lfloor y_0+\eta\rfloor$ upward moves, as
+explained earlier. (Here $\lfloor x+\xi\rfloor$ actually stands for
+$\lfloor x/2^{16}-|xi_corr|\rfloor$, if $x$ is regarded as an integer
+without scaling.) The unscaled integers $x_k$ and~$y_k$ should be less
+than $2^{28}$ in magnitude.
+
+It is assumed that $|move_ptr| + \lfloor y_3+\eta\rfloor -
+\lfloor y_0+\eta\rfloor < |move_size|$ when this procedure is called,
+so that the capacity of the |move| array will not be exceeded.
+
+The variables |r| and |s| in this procedure stand respectively for
+$R-|xi_corr|$ and $S-|eta_corr|$ in the theory discussed above.
+
+@p procedure make_moves(@!xx0,@!xx1,@!xx2,@!xx3,@!yy0,@!yy1,@!yy2,@!yy3:
+ scaled;@!xi_corr,@!eta_corr:small_number);
+label continue, done, exit;
+var @!x1,@!x2,@!x3,@!m,@!r,@!y1,@!y2,@!y3,@!n,@!s,@!l:integer;
+ {bisection variables explained above}
+@!q,@!t,@!u,@!x2a,@!x3a,@!y2a,@!y3a:integer; {additional temporary registers}
+begin if (xx3<xx0)or(yy3<yy0) then confusion("m");
+@:this can't happen m}{\quad m@>
+l:=16; bisect_ptr:=0;@/
+x1:=xx1-xx0; x2:=xx2-xx1; x3:=xx3-xx2;
+if xx0>=xi_corr then r:=(xx0-xi_corr) mod unity
+else r:=unity-1-((-xx0+xi_corr-1) mod unity);
+m:=(xx3-xx0+r) div unity;@/
+y1:=yy1-yy0; y2:=yy2-yy1; y3:=yy3-yy2;
+if yy0>=eta_corr then s:=(yy0-eta_corr) mod unity
+else s:=unity-1-((-yy0+eta_corr-1) mod unity);
+n:=(yy3-yy0+s) div unity;@/
+if (xx3-xx0>=fraction_one)or(yy3-yy0>=fraction_one) then
+ @<Divide the variables by two, to avoid overflow problems@>;
+loop@+ begin continue:@<Make moves for current subinterval;
+ if bisection is necessary, push the second subinterval
+ onto the stack, and |goto continue| in order to handle
+ the first subinterval@>;
+ if bisect_ptr=0 then return;
+ @<Remove a subproblem for |make_moves| from the stack@>;
+ end;
+exit: end;
+
+@ @<Remove a subproblem for |make_moves| from the stack@>=
+bisect_ptr:=bisect_ptr-move_increment;@/
+x1:=stack_x1; x2:=stack_x2; x3:=stack_x3; r:=stack_r; m:=stack_m;@/
+y1:=stack_y1; y2:=stack_y2; y3:=stack_y3; s:=stack_s; n:=stack_n;@/
+l:=stack_l
+
+@ Our variables |(x1,x2,x3)| correspond to $(X_1,X_2,X_3)$ in the notation
+of the theory developed above. We need to keep them less than $2^{28}$
+in order to avoid integer overflow in weird circumstances.
+For example, data like $x_0=-2^{28}+2^{16}-1$ and $x_1=x_2=x_3=2^{28}-1$
+would otherwise be problematical. Hence this part of the code is
+needed, if only to thwart malicious users.
+
+@<Divide the variables by two, to avoid overflow problems@>=
+begin x1:=half(x1+xi_corr); x2:=half(x2+xi_corr); x3:=half(x3+xi_corr);
+r:=half(r+xi_corr);@/
+y1:=half(y1+eta_corr); y2:=half(y2+eta_corr); y3:=half(y3+eta_corr);
+s:=half(s+eta_corr);@/
+l:=15;
+end
+
+@ @<Make moves...@>=
+if m=0 then @<Move upward |n| steps@>
+else if n=0 then @<Move to the right |m| steps@>
+else if m+n=2 then @<Make one move of each kind@>
+else begin incr(l); stack_l:=l;@/
+ stack_x3:=x3; stack_x2:=half(x2+x3+xi_corr); x2:=half(x1+x2+xi_corr);
+ x3:=half(x2+stack_x2+xi_corr); stack_x1:=x3;@/
+ r:=r+r+xi_corr; t:=x1+x2+x3+r;@/
+ q:=t div two_to_the[l]; stack_r:=t mod two_to_the[l];@/
+ stack_m:=m-q; m:=q;@/
+ stack_y3:=y3; stack_y2:=half(y2+y3+eta_corr); y2:=half(y1+y2+eta_corr);
+ y3:=half(y2+stack_y2+eta_corr); stack_y1:=y3;@/
+ s:=s+s+eta_corr; u:=y1+y2+y3+s;@/
+ q:=u div two_to_the[l]; stack_s:=u mod two_to_the[l];@/
+ stack_n:=n-q; n:=q;@/
+ bisect_ptr:=bisect_ptr+move_increment; goto continue;
+ end
+
+@ @<Move upward |n| steps@>=
+while n>0 do
+ begin incr(move_ptr); move[move_ptr]:=1; decr(n);
+ end
+
+@ @<Move to the right |m| steps@>=
+move[move_ptr]:=move[move_ptr]+m
+
+@ @<Make one move of each kind@>=
+begin r:=two_to_the[l]-r; s:=two_to_the[l]-s;@/
+while l<30 do
+ begin x3a:=x3; x2a:=half(x2+x3+xi_corr); x2:=half(x1+x2+xi_corr);
+ x3:=half(x2+x2a+xi_corr);
+ t:=x1+x2+x3; r:=r+r-xi_corr;@/
+ y3a:=y3; y2a:=half(y2+y3+eta_corr); y2:=half(y1+y2+eta_corr);
+ y3:=half(y2+y2a+eta_corr);
+ u:=y1+y2+y3; s:=s+s-eta_corr;@/
+ if t<r then if u<s then @<Switch to the right subinterval@>
+ else begin @<Move up then right@>; goto done;
+ end
+ else if u<s then
+ begin @<Move right then up@>; goto done;
+ end;
+ incr(l);
+ end;
+r:=r-xi_corr; s:=s-eta_corr;
+if ab_vs_cd(x1+x2+x3,s,y1+y2+y3,r)-xi_corr>=0 then @<Move right then up@>
+ else @<Move up then right@>;
+done:
+end
+
+@ @<Switch to the right subinterval@>=
+begin x1:=x3; x2:=x2a; x3:=x3a; r:=r-t;
+y1:=y3; y2:=y2a; y3:=y3a; s:=s-u;
+end
+
+@ @<Move right then up@>=
+begin incr(move[move_ptr]); incr(move_ptr); move[move_ptr]:=1;
+end
+
+@ @<Move up then right@>=
+begin incr(move_ptr); move[move_ptr]:=2;
+end
+
+@ After |make_moves| has acted, possibly for several curves that move toward
+the same octant, a ``smoothing'' operation might be done on the |move| array.
+This removes optical glitches that can arise even when the curve has been
+digitized without rounding errors.
+
+The smoothing process replaces the integers $a_0\ldots a_n$ in
+|move[b..t]| by ``smoothed'' integers $a_0'\ldots a_n'$ defined as
+follows:
+$$a_k'=a_k+\delta\k-\delta_k;\qquad
+\delta_k=\cases{+1,&if $1<k<n$ and $a_{k-2}\G a_{k-1}\ll a_k\G a\k$;\cr
+-1,&if $1<k<n$ and $a_{k-2}\L a_{k-1}\gg a_k\L a\k$;\cr
+0,&otherwise.\cr}$$
+Here $a\ll b$ means that $a\L b-2$, and $a\gg b$ means that $a\G b+2$.
+
+The smoothing operation is symmetric in the sense that, if $a_0\ldots a_n$
+smoothes to $a_0'\ldots a_n'$, then the reverse sequence $a_n\ldots a_0$
+smoothes to $a_n'\ldots a_0'$; also the complementary sequence
+$(m-a_0)\ldots(m-a_n)$ smoothes to $(m-a_0')\ldots(m-a_n')$.
+We have $a_0'+\cdots+a_n'=a_0+\cdots+a_n$ because $\delta_0=\delta_{n+1}=0$.
+
+@p procedure smooth_moves(@!b,@!t:integer);
+var@!k:1..move_size; {index into |move|}
+@!a,@!aa,@!aaa:integer; {original values of |move[k],move[k-1],move[k-2]|}
+begin if t-b>=3 then
+ begin k:=b+2; aa:=move[k-1]; aaa:=move[k-2];
+ repeat a:=move[k];
+ if abs(a-aa)>1 then
+ @<Increase and decrease |move[k-1]| and |move[k]| by $\delta_k$@>;
+ incr(k); aaa:=aa; aa:=a;
+ until k=t;
+ end;
+end;
+
+@ @<Increase and decrease |move[k-1]| and |move[k]| by $\delta_k$@>=
+if a>aa then
+ begin if aaa>=aa then if a>=move[k+1] then
+ begin incr(move[k-1]); move[k]:=a-1;
+ end;
+ end
+else begin if aaa<=aa then if a<=move[k+1] then
+ begin decr(move[k-1]); move[k]:=a+1;
+ end;
+ end
+
+@* \[20] Edge structures.
+Now we come to \MF's internal scheme for representing what the user can
+actually ``see,'' the edges between pixels. Each pixel has an integer
+weight, obtained by summing the weights on all edges to its left. \MF\
+represents only the nonzero edge weights, since most of the edges are
+weightless; in this way, the data storage requirements grow only linearly
+with respect to the number of pixels per point, even though two-dimensional
+data is being represented. (Well, the actual dependence on the underlying
+resolution is order $n\log n$, but the the $\log n$ factor is buried in our
+implicit restriction on the maximum raster size.) The sum of all edge
+weights in each row should be zero.
+
+The data structure for edge weights must be compact and flexible,
+yet it should support efficient updating and display operations. We
+want to be able to have many different edge structures in memory at
+once, and we want the computer to be able to translate them, reflect them,
+and/or merge them together with relative ease.
+
+\MF's solution to this problem requires one single-word node per
+nonzero edge weight, plus one two-word node for each row in a contiguous
+set of rows. There's also a header node that provides global information
+about the entire structure.
+
+@ Let's consider the edge-weight nodes first. The |info| field of such
+nodes contains both an $m$~value and a weight~$w$, in the form
+$8m+w+c$, where $c$ is a constant that depends on data found in the header.
+We shall consider $c$ in detail later; for now, it's best just to think
+of it as a way to compensate for the fact that $m$ and~$w$ can be negative,
+together with the fact that an |info| field must have a value between
+|min_halfword| and |max_halfword|. The $m$ value is an unscaled $x$~coordinate,
+so it satisfies $\vert m\vert<
+4096$; the $w$ value is always in the range $1\L\vert w\vert\L3$. We can
+unpack the data in the |info| field by fetching |ho(info(p))=
+info(p)-min_halfword| and dividing this nonnegative number by~8;
+the constant~$c$ will be chosen so that the remainder of this division
+is $4+w$. Thus, for example, a remainder of~3 will correspond to
+the edge weight $w=-1$.
+
+Every row of an edge structure contains two lists of such edge-weight
+nodes, called the |sorted| and |unsorted| lists, linked together by their
+|link| fields in the normal way. The difference between them is that we
+always have |info(p)<=info(link(p))| in the |sorted| list, but there's no
+such restriction on the elements of the |unsorted| list. The reason for
+this distinction is that it would take unnecessarily long to maintain
+edge-weight lists in sorted order while they're being updated; but when we
+need to process an entire row from left to right in order of the
+$m$~values, it's fairly easy and quick to sort a short list of unsorted
+elements and to merge them into place among their sorted cohorts.
+Furthermore, the fact that the |unsorted| list is empty can sometimes be
+used to good advantage, because it allows us to conclude that a particular
+row has not changed since the last time we sorted it.
+
+The final |link| of the |sorted| list will be |sentinel|, which points to
+a special one-word node whose |info| field is essentially infinite; this
+facilitates the sorting and merging operations. The final |link| of the
+|unsorted| list will be either |null| or |void|, where |void=null+1|
+is used to avoid redisplaying data that has not changed:
+A |void| value is stored at the head of the
+unsorted list whenever the corresponding row has been displayed.
+
+@d zero_w=4
+@d void==null+1
+
+@<Initialize table entries...@>=
+info(sentinel):=max_halfword; {|link(sentinel)=null|}
+
+@ The rows themselves are represented by row header nodes that
+contain four link fields. Two of these four, |sorted| and |unsorted|,
+point to the first items of the edge-weight lists just mentioned.
+The other two, |link| and |knil|, point to the headers of the two
+adjacent rows. If |p| points to the header for row number~|n|, then
+|link(p)| points up to the header for row~|n+1|, and |knil(p)| points
+down to the header for row~|n-1|. This double linking makes it
+convenient to move through consecutive rows either upward or downward;
+as usual, we have |link(knil(p))=knil(link(p))=p| for all row headers~|p|.
+
+The row associated with a given value of |n| contains weights for
+edges that run between the lattice points |(m,n)| and |(m,n+1)|.
+
+@d knil==info {inverse of the |link| field, in a doubly linked list}
+@d sorted_loc(#)==#+1 {where the |sorted| link field resides}
+@d sorted(#)==link(sorted_loc(#)) {beginning of the list of sorted edge weights}
+@d unsorted(#)==info(#+1) {beginning of the list of unsorted edge weights}
+@d row_node_size=2 {number of words in a row header node}
+
+@ The main header node |h| for an edge structure has |link| and |knil|
+fields that link it above the topmost row and below the bottommost row.
+It also has fields called |m_min|, |m_max|, |n_min|, and |n_max| that
+bound the current extent of the edge data: All |m| values in edge-weight
+nodes should lie between |m_min(h)-4096| and |m_max(h)-4096|, inclusive.
+Furthermore the topmost row header, pointed to by |knil(h)|,
+is for row number |n_max(h)-4096|; the bottommost row header, pointed to by
+|link(h)|, is for row number |n_min(h)-4096|.
+
+The offset constant |c| that's used in all of the edge-weight data is
+represented implicitly in |m_offset(h)|; its actual value is
+$$\hbox{|c=min_halfword+zero_w+8*m_offset(h)|.}$$
+Notice that it's possible to shift an entire edge structure by an
+amount $(\Delta m,\Delta n)$ by adding $\Delta n$ to |n_min(h)| and |n_max(h)|,
+adding $\Delta m$ to |m_min(h)| and |m_max(h)|, and subtracting
+$\Delta m$ from |m_offset(h)|;
+none of the other edge data needs to be modified. Initially the |m_offset|
+field is~4096, but it will change if the user requests such a shift.
+The contents of these five fields should always be positive and less than
+8192; |n_max| should, in fact, be less than 8191. Furthermore
+|m_min+m_offset-4096| and |m_max+m_offset-4096| must also lie strictly
+between 0 and 8192, so that the |info| fields of edge-weight nodes will
+fit in a halfword.
+
+The header node of an edge structure also contains two somewhat unusual
+fields that are called |last_window(h)| and |last_window_time(h)|. When this
+structure is displayed in window~|k| of the user's screen, after that
+window has been updated |t| times, \MF\ sets |last_window(h):=k| and
+|last_window_time(h):=t|; it also sets |unsorted(p):=void| for all row
+headers~|p|, after merging any existing unsorted weights with the sorted
+ones. A subsequent display in the same window will be able to avoid
+redisplaying rows whose |unsorted| list is still |void|, if the window
+hasn't been used for something else in the meantime.
+
+A pointer to the row header of row |n_pos(h)-4096| is provided in
+|n_rover(h)|. Most of the algorithms that update an edge structure
+are able to get by without random row references; they usually
+access rows that are neighbors of each other or of the current |n_pos| row.
+Exception: If |link(h)=h| (so that the edge structure contains
+no rows), we have |n_rover(h)=h|, and |n_pos(h)| is irrelevant.
+
+@d zero_field=4096 {amount added to coordinates to make them positive}
+@d n_min(#)==info(#+1) {minimum row number present, plus |zero_field|}
+@d n_max(#)==link(#+1) {maximum row number present, plus |zero_field|}
+@d m_min(#)==info(#+2) {minimum column number present, plus |zero_field|}
+@d m_max(#)==link(#+2) {maximum column number present, plus |zero_field|}
+@d m_offset(#)==info(#+3) {translation of $m$ data in edge-weight nodes}
+@d last_window(#)==link(#+3) {the last display went into this window}
+@d last_window_time(#)==mem[#+4].int {after this many window updates}
+@d n_pos(#)==info(#+5) {the row currently in |n_rover|, plus |zero_field|}
+@d n_rover(#)==link(#+5) {a row recently referenced}
+@d edge_header_size=6 {number of words in an edge-structure header}
+@d valid_range(#)==(abs(#-4096)<4096) {is |#| strictly between 0 and 8192?}
+@d empty_edges(#)==link(#)=# {are there no rows in this edge header?}
+
+@p procedure init_edges(@!h:pointer); {initialize an edge header to null values}
+begin knil(h):=h; link(h):=h;@/
+n_min(h):=zero_field+4095; n_max(h):=zero_field-4095;
+m_min(h):=zero_field+4095; m_max(h):=zero_field-4095;
+m_offset(h):=zero_field;@/
+last_window(h):=0; last_window_time(h):=0;@/
+n_rover(h):=h; n_pos(h):=0;@/
+end;
+
+@ When a lot of work is being done on a particular edge structure, we plant
+a pointer to its main header in the global variable |cur_edges|.
+This saves us from having to pass this pointer as a parameter over and
+over again between subroutines.
+
+Similarly, |cur_wt| is a global weight that is being used by several
+procedures at once.
+
+@<Glob...@>=
+@!cur_edges:pointer; {the edge structure of current interest}
+@!cur_wt:integer; {the edge weight of current interest}
+
+@ The |fix_offset| routine goes through all the edge-weight nodes of
+|cur_edges| and adds a constant to their |info| fields, so that
+|m_offset(cur_edges)| can be brought back to |zero_field|. (This
+is necessary only in unusual cases when the offset has gotten too
+large or too small.)
+
+@p procedure fix_offset;
+var @!p,@!q:pointer; {list traversers}
+@!delta:integer; {the amount of change}
+begin delta:=8*(m_offset(cur_edges)-zero_field);
+m_offset(cur_edges):=zero_field;
+q:=link(cur_edges);
+while q<>cur_edges do
+ begin p:=sorted(q);
+ while p<>sentinel do
+ begin info(p):=info(p)-delta; p:=link(p);
+ end;
+ p:=unsorted(q);
+ while p>void do
+ begin info(p):=info(p)-delta; p:=link(p);
+ end;
+ q:=link(q);
+ end;
+end;
+
+@ The |edge_prep| routine makes the |cur_edges| structure ready to
+accept new data whose coordinates satisfy |ml<=m<=mr| and |nl<=n<=nr-1|,
+assuming that |-4096<ml<=mr<4096| and |-4096<nl<=nr<4096|. It makes
+appropriate adjustments to |m_min|, |m_max|, |n_min|, and |n_max|,
+adding new empty rows if necessary.
+
+@p procedure edge_prep(@!ml,@!mr,@!nl,@!nr:integer);
+var @!delta:halfword; {amount of change}
+@!p,@!q:pointer; {for list manipulation}
+begin ml:=ml+zero_field; mr:=mr+zero_field;
+nl:=nl+zero_field; nr:=nr-1+zero_field;@/
+if ml<m_min(cur_edges) then m_min(cur_edges):=ml;
+if mr>m_max(cur_edges) then m_max(cur_edges):=mr;
+if not valid_range(m_min(cur_edges)+m_offset(cur_edges)-zero_field) or@|
+ not valid_range(m_max(cur_edges)+m_offset(cur_edges)-zero_field) then
+ fix_offset;
+if empty_edges(cur_edges) then {there are no rows}
+ begin n_min(cur_edges):=nr+1; n_max(cur_edges):=nr;
+ end;
+if nl<n_min(cur_edges) then
+ @<Insert exactly |n_min(cur_edges)-nl| empty rows at the bottom@>;
+if nr>n_max(cur_edges) then
+ @<Insert exactly |nr-n_max(cur_edges)| empty rows at the top@>;
+end;
+
+@ @<Insert exactly |n_min(cur_edges)-nl| empty rows at the bottom@>=
+begin delta:=n_min(cur_edges)-nl; n_min(cur_edges):=nl;
+p:=link(cur_edges);
+repeat q:=get_node(row_node_size); sorted(q):=sentinel; unsorted(q):=void;
+knil(p):=q; link(q):=p; p:=q; decr(delta);
+until delta=0;
+knil(p):=cur_edges; link(cur_edges):=p;
+if n_rover(cur_edges)=cur_edges then n_pos(cur_edges):=nl-1;
+end
+
+@ @<Insert exactly |nr-n_max(cur_edges)| empty rows at the top@>=
+begin delta:=nr-n_max(cur_edges); n_max(cur_edges):=nr;
+p:=knil(cur_edges);
+repeat q:=get_node(row_node_size); sorted(q):=sentinel; unsorted(q):=void;
+link(p):=q; knil(q):=p; p:=q; decr(delta);
+until delta=0;
+link(p):=cur_edges; knil(cur_edges):=p;
+if n_rover(cur_edges)=cur_edges then n_pos(cur_edges):=nr+1;
+end
+
+@ The |print_edges| subroutine gives a symbolic rendition of an edge
+structure, for use in `\&{show}' commands. A rather terse output
+format has been chosen since edge structures can grow quite large.
+
+@<Declare subroutines for printing expressions@>=
+@t\4@>@<Declare the procedure called |print_weight|@>@;@/
+procedure print_edges(@!s:str_number;@!nuline:boolean;@!x_off,@!y_off:integer);
+var @!p,@!q,@!r:pointer; {for list traversal}
+@!n:integer; {row number}
+begin print_diagnostic("Edge structure",s,nuline);
+p:=knil(cur_edges); n:=n_max(cur_edges)-zero_field;
+while p<>cur_edges do
+ begin q:=unsorted(p); r:=sorted(p);
+ if(q>void)or(r<>sentinel) then
+ begin print_nl("row "); print_int(n+y_off); print_char(":");
+ while q>void do
+ begin print_weight(q,x_off); q:=link(q);
+ end;
+ print(" |");
+ while r<>sentinel do
+ begin print_weight(r,x_off); r:=link(r);
+ end;
+ end;
+ p:=knil(p); decr(n);
+ end;
+end_diagnostic(true);
+end;
+
+@ @<Declare the procedure called |print_weight|@>=
+procedure print_weight(@!q:pointer;@!x_off:integer);
+var @!w,@!m:integer; {unpacked weight and coordinate}
+@!d:integer; {temporary data register}
+begin d:=ho(info(q)); w:=d mod 8; m:=(d div 8)-m_offset(cur_edges);
+if file_offset>max_print_line-9 then print_nl(" ")
+else print_char(" ");
+print_int(m+x_off);
+while w>zero_w do
+ begin print_char("+"); decr(w);
+ end;
+while w<zero_w do
+ begin print_char("-"); incr(w);
+ end;
+end;
+
+@ Here's a trivial subroutine that copies an edge structure. (Let's hope
+that the given structure isn't too gigantic.)
+
+@p function copy_edges(@!h:pointer):pointer;
+var @!p,@!r:pointer; {variables that traverse the given structure}
+@!hh,@!pp,@!qq,@!rr,@!ss:pointer; {variables that traverse the new structure}
+begin hh:=get_node(edge_header_size);
+mem[hh+1]:=mem[h+1]; mem[hh+2]:=mem[h+2];
+mem[hh+3]:=mem[h+3]; mem[hh+4]:=mem[h+4]; {we've now copied |n_min|, |n_max|,
+ |m_min|, |m_max|, |m_offset|, |last_window|, and |last_window_time|}
+n_pos(hh):=n_max(hh)+1;n_rover(hh):=hh;@/
+p:=link(h); qq:=hh;
+while p<>h do
+ begin pp:=get_node(row_node_size); link(qq):=pp; knil(pp):=qq;
+ @<Copy both |sorted| and |unsorted| lists of |p| to |pp|@>;
+ p:=link(p); qq:=pp;
+ end;
+link(qq):=hh; knil(hh):=qq;
+copy_edges:=hh;
+end;
+
+@ @<Copy both |sorted| and |unsorted|...@>=
+r:=sorted(p); rr:=sorted_loc(pp); {|link(rr)=sorted(pp)|}
+while r<>sentinel do
+ begin ss:=get_avail; link(rr):=ss; rr:=ss; info(rr):=info(r);@/
+ r:=link(r);
+ end;
+link(rr):=sentinel;@/
+r:=unsorted(p); rr:=temp_head;
+while r>void do
+ begin ss:=get_avail; link(rr):=ss; rr:=ss; info(rr):=info(r);@/
+ r:=link(r);
+ end;
+link(rr):=r; unsorted(pp):=link(temp_head)
+
+@ Another trivial routine flips |cur_edges| about the |x|-axis
+(i.e., negates all the |y| coordinates), assuming that at least
+one row is present.
+
+@p procedure y_reflect_edges;
+var @!p,@!q,@!r:pointer; {list manipulation registers}
+begin p:=n_min(cur_edges);
+n_min(cur_edges):=zero_field+zero_field-1-n_max(cur_edges);
+n_max(cur_edges):=zero_field+zero_field-1-p;
+n_pos(cur_edges):=zero_field+zero_field-1-n_pos(cur_edges);@/
+p:=link(cur_edges); q:=cur_edges; {we assume that |p<>q|}
+repeat r:=link(p); link(p):=q; knil(q):=p; q:=p; p:=r;
+until q=cur_edges;
+last_window_time(cur_edges):=0;
+end;
+
+@ It's somewhat more difficult, yet not too hard, to reflect about the |y|-axis.
+
+@p procedure x_reflect_edges;
+var @!p,@!q,@!r,@!s:pointer; {list manipulation registers}
+@!m:integer; {|info| fields will be reflected with respect to this number}
+begin p:=m_min(cur_edges);
+m_min(cur_edges):=zero_field+zero_field-m_max(cur_edges);
+m_max(cur_edges):=zero_field+zero_field-p;
+m:=(zero_field+m_offset(cur_edges))*8+zero_w+min_halfword+zero_w+min_halfword;
+m_offset(cur_edges):=zero_field;
+p:=link(cur_edges);
+repeat @<Reflect the edge-and-weight data in |sorted(p)|@>;
+@<Reflect the edge-and-weight data in |unsorted(p)|@>;
+p:=link(p);
+until p=cur_edges;
+last_window_time(cur_edges):=0;
+end;
+
+@ We want to change the sign of the weight as we change the sign of the
+|x|~coordinate. Fortunately, it's easier to do this than to negate
+one without the other.
+
+@<Reflect the edge-and-weight data in |unsorted(p)|@>=
+q:=unsorted(p);
+while q>void do
+ begin info(q):=m-info(q); q:=link(q);
+ end
+
+@ Reversing the order of a linked list is best thought of as the process of
+popping nodes off one stack and pushing them on another. In this case we
+pop from stack~|q| and push to stack~|r|.
+
+@<Reflect the edge-and-weight data in |sorted(p)|@>=
+q:=sorted(p); r:=sentinel;
+while q<>sentinel do
+ begin s:=link(q); link(q):=r; r:=q; info(r):=m-info(q); q:=s;
+ end;
+sorted(p):=r
+
+@ Now let's multiply all the $y$~coordinates of a nonempty edge structure
+by a small integer $s>1$:
+
+@p procedure y_scale_edges(@!s:integer);
+var @!p,@!q,@!pp,@!r,@!rr,@!ss:pointer; {list manipulation registers}
+@!t:integer; {replication counter}
+begin if (s*(n_max(cur_edges)+1-zero_field)>=4096) or@|
+ (s*(n_min(cur_edges)-zero_field)<=-4096) then
+ begin print_err("Scaled picture would be too big");
+@.Scaled picture...big@>
+ help3("I can't yscale the picture as requested---it would")@/
+ ("make some coordinates too large or too small.")@/
+ ("Proceed, and I'll omit the transformation.");
+ put_get_error;
+ end
+else begin n_max(cur_edges):=s*(n_max(cur_edges)+1-zero_field)-1+zero_field;
+ n_min(cur_edges):=s*(n_min(cur_edges)-zero_field)+zero_field;
+ @<Replicate every row exactly $s$ times@>;
+ last_window_time(cur_edges):=0;
+ end;
+end;
+
+@ @<Replicate...@>=
+p:=cur_edges;
+repeat q:=p; p:=link(p);
+for t:=2 to s do
+ begin pp:=get_node(row_node_size); link(q):=pp; knil(p):=pp;
+ link(pp):=p; knil(pp):=q; q:=pp;
+ @<Copy both |sorted| and |unsorted|...@>;
+ end;
+until link(p)=cur_edges
+
+@ Scaling the $x$~coordinates is, of course, our next task.
+
+@p procedure x_scale_edges(@!s:integer);
+var @!p,@!q:pointer; {list manipulation registers}
+@!t:0..65535; {unpacked |info| field}
+@!w:0..7; {unpacked weight}
+@!delta:integer; {amount added to scaled |info|}
+begin if (s*(m_max(cur_edges)-zero_field)>=4096) or@|
+ (s*(m_min(cur_edges)-zero_field)<=-4096) then
+ begin print_err("Scaled picture would be too big");
+@.Scaled picture...big@>
+ help3("I can't xscale the picture as requested---it would")@/
+ ("make some coordinates too large or too small.")@/
+ ("Proceed, and I'll omit the transformation.");
+ put_get_error;
+ end
+else if (m_max(cur_edges)<>zero_field)or(m_min(cur_edges)<>zero_field) then
+ begin m_max(cur_edges):=s*(m_max(cur_edges)-zero_field)+zero_field;
+ m_min(cur_edges):=s*(m_min(cur_edges)-zero_field)+zero_field;
+ delta:=8*(zero_field-s*m_offset(cur_edges))+min_halfword;
+ m_offset(cur_edges):=zero_field;@/
+ @<Scale the $x$~coordinates of each row by $s$@>;
+ last_window_time(cur_edges):=0;
+ end;
+end;
+
+@ The multiplications cannot overflow because we know that |s<4096|.
+
+@<Scale the $x$~coordinates of each row by $s$@>=
+q:=link(cur_edges);
+repeat p:=sorted(q);
+while p<>sentinel do
+ begin t:=ho(info(p)); w:=t mod 8; info(p):=(t-w)*s+w+delta; p:=link(p);
+ end;
+p:=unsorted(q);
+while p>void do
+ begin t:=ho(info(p)); w:=t mod 8; info(p):=(t-w)*s+w+delta; p:=link(p);
+ end;
+q:=link(q);
+until q=cur_edges
+
+@ Here is a routine that changes the signs of all the weights, without
+changing anything else.
+
+@p procedure negate_edges(@!h:pointer);
+label done;
+var @!p,@!q,@!r,@!s,@!t,@!u:pointer; {structure traversers}
+begin p:=link(h);
+while p<>h do
+ begin q:=unsorted(p);
+ while q>void do
+ begin info(q):=8-2*((ho(info(q))) mod 8)+info(q); q:=link(q);
+ end;
+ q:=sorted(p);
+ if q<>sentinel then
+ begin repeat info(q):=8-2*((ho(info(q))) mod 8)+info(q); q:=link(q);
+ until q=sentinel;
+ @<Put the list |sorted(p)| back into sort@>;
+ end;
+ p:=link(p);
+ end;
+last_window_time(h):=0;
+end;
+
+@ \MF\ would work even if the code in this section were omitted, because
+a list of edge-and-weight data that is sorted only by
+|m| but not~|w| turns out to be good enough for correct operation.
+However, the author decided not to make the program even trickier than
+it is already, since |negate_edges| isn't needed very often.
+The simpler-to-state condition, ``keep the |sorted| list fully sorted,''
+is therefore being preserved at the cost of extra computation.
+
+@<Put the list |sorted(p)|...@>=
+u:=sorted_loc(p); q:=link(u); r:=q; s:=link(r); {|q=sorted(p)|}
+loop@+ if info(s)>info(r) then
+ begin link(u):=q;
+ if s=sentinel then goto done;
+ u:=r; q:=s; r:=q; s:=link(r);
+ end
+ else begin t:=s; s:=link(t); link(t):=q; q:=t;
+ end;
+done: link(r):=sentinel
+
+@ The |unsorted| edges of a row are merged into the |sorted| ones by
+a subroutine called |sort_edges|. It uses simple insertion sort,
+followed by a merge, because the unsorted list is supposedly quite short.
+However, the unsorted list is assumed to be nonempty.
+
+@p procedure sort_edges(@!h:pointer); {|h| is a row header}
+label done;
+var @!k:halfword; {key register that we compare to |info(q)|}
+@!p,@!q,@!r,@!s:pointer;
+begin r:=unsorted(h); unsorted(h):=null;
+p:=link(r); link(r):=sentinel; link(temp_head):=r;
+while p>void do {sort node |p| into the list that starts at |temp_head|}
+ begin k:=info(p); q:=temp_head;
+ repeat r:=q; q:=link(r);
+ until k<=info(q);
+ link(r):=p; r:=link(p); link(p):=q; p:=r;
+ end;
+@<Merge the |temp_head| list into |sorted(h)|@>;
+end;
+
+@ In this step we use the fact that |sorted(h)=link(sorted_loc(h))|.
+
+@<Merge the |temp_head| list into |sorted(h)|@>=
+begin r:=sorted_loc(h); q:=link(r); p:=link(temp_head);
+loop@+ begin k:=info(p);
+ while k>info(q) do
+ begin r:=q; q:=link(r);
+ end;
+ link(r):=p; s:=link(p); link(p):=q;
+ if s=sentinel then goto done;
+ r:=p; p:=s;
+ end;
+done:end
+
+@ The |cull_edges| procedure ``optimizes'' an edge structure by making all
+the pixel weights either |w_out| or~|w_in|. The weight will be~|w_in| after the
+operation if and only if it was in the closed interval |[w_lo,w_hi]|
+before, where |w_lo<=w_hi|. Either |w_out| or |w_in| is zero, while the other is
+$\pm1$, $\pm2$, or $\pm3$. The parameters will be such that zero-weight
+pixels will remain of weight zero. (This is fortunate,
+because there are infinitely many of them.)
+
+The procedure also computes the tightest possible bounds on the resulting
+data, by updating |m_min|, |m_max|, |n_min|, and~|n_max|.
+
+@p procedure cull_edges(@!w_lo,@!w_hi,@!w_out,@!w_in:integer);
+label done;
+var @!p,@!q,@!r,@!s:pointer; {for list manipulation}
+@!w:integer; {new weight after culling}
+@!d:integer; {data register for unpacking}
+@!m:integer; {the previous column number, including |m_offset|}
+@!mm:integer; {the next column number, including |m_offset|}
+@!ww:integer; {accumulated weight before culling}
+@!prev_w:integer; {value of |w| before column |m|}
+@!n,@!min_n,@!max_n:pointer; {current and extreme row numbers}
+@!min_d,@!max_d:pointer; {extremes of the new edge-and-weight data}
+begin min_d:=max_halfword; max_d:=min_halfword;
+min_n:=max_halfword; max_n:=min_halfword;@/
+p:=link(cur_edges); n:=n_min(cur_edges);
+while p<>cur_edges do
+ begin if unsorted(p)>void then sort_edges(p);
+ if sorted(p)<>sentinel then
+ @<Cull superfluous edge-weight entries from |sorted(p)|@>;
+ p:=link(p); incr(n);
+ end;
+@<Delete empty rows at the top and/or bottom;
+ update the boundary values in the header@>;
+last_window_time(cur_edges):=0;
+end;
+
+@ The entire |sorted| list is returned to available memory in this step;
+a new list is built starting (temporarily) at |temp_head|.
+Since several edges can occur at the same column, we need to be looking
+ahead of where the actual culling takes place. This means that it's
+slightly tricky to get the iteration started and stopped.
+
+@<Cull superfluous...@>=
+begin r:=temp_head; q:=sorted(p); ww:=0; m:=1000000; prev_w:=0;
+loop@+ begin if q=sentinel then mm:=1000000
+ else begin d:=ho(info(q)); mm:=d div 8; ww:=ww+(d mod 8)-zero_w;
+ end;
+ if mm>m then
+ begin @<Insert an edge-weight for edge |m|, if the new pixel
+ weight has changed@>;
+ if q=sentinel then goto done;
+ end;
+ m:=mm;
+ if ww>=w_lo then if ww<=w_hi then w:=w_in
+ else w:=w_out
+ else w:=w_out;
+ s:=link(q); free_avail(q); q:=s;
+ end;
+done: link(r):=sentinel; sorted(p):=link(temp_head);
+if r<>temp_head then @<Update the max/min amounts@>;
+end
+
+@ @<Insert an edge-weight for edge |m|, if...@>=
+if w<>prev_w then
+ begin s:=get_avail; link(r):=s;
+ info(s):=8*m+min_halfword+zero_w+w-prev_w;
+ r:=s; prev_w:=w;
+ end
+
+@ @<Update the max/min amounts@>=
+begin if min_n=max_halfword then min_n:=n;
+max_n:=n;
+if min_d>info(link(temp_head)) then min_d:=info(link(temp_head));
+if max_d<info(r) then max_d:=info(r);
+end
+
+@ @<Delete empty rows at the top and/or bottom...@>=
+if min_n>max_n then @<Delete all the row headers@>
+else begin n:=n_min(cur_edges); n_min(cur_edges):=min_n;
+ while min_n>n do
+ begin p:=link(cur_edges); link(cur_edges):=link(p);
+ knil(link(p)):=cur_edges;
+ free_node(p,row_node_size); incr(n);
+ end;
+ n:=n_max(cur_edges); n_max(cur_edges):=max_n;
+ n_pos(cur_edges):=max_n+1; n_rover(cur_edges):=cur_edges;
+ while max_n<n do
+ begin p:=knil(cur_edges); knil(cur_edges):=knil(p);
+ link(knil(p)):=cur_edges;
+ free_node(p,row_node_size); decr(n);
+ end;
+ m_min(cur_edges):=((ho(min_d)) div 8)-m_offset(cur_edges)+zero_field;
+ m_max(cur_edges):=((ho(max_d)) div 8)-m_offset(cur_edges)+zero_field;
+ end
+
+@ We get here if the edges have been entirely culled away.
+
+@<Delete all the row headers@>=
+begin p:=link(cur_edges);
+while p<>cur_edges do
+ begin q:=link(p); free_node(p,row_node_size); p:=q;
+ end;
+init_edges(cur_edges);
+end
+
+
+@ The last and most difficult routine for transforming an edge structure---and
+the most interesting one!---is |xy_swap_edges|, which interchanges the
+r\^^Doles of rows and columns. Its task can be viewed as the job of
+creating an edge structure that contains only horizontal edges, linked
+together in columns, given an edge structure that contains only
+vertical edges linked together in rows; we must do this without changing
+the implied pixel weights.
+
+Given any two adjacent rows of an edge structure, it is not difficult to
+determine the horizontal edges that lie ``between'' them: We simply look
+for vertically adjacent pixels that have different weight, and insert
+a horizontal edge containing the difference in weights. Every horizontal
+edge determined in this way should be put into an appropriate linked
+list. Since random access to these linked lists is desirable, we use
+the |move| array to hold the list heads. If we work through the given
+edge structure from top to bottom, the constructed lists will not need
+to be sorted, since they will already be in order.
+
+The following algorithm makes use of some ideas suggested by John Hobby.
+@^Hobby, John Douglas@>
+It assumes that the edge structure is non-null, i.e., that |link(cur_edges)
+<>cur_edges|, hence |m_max(cur_edges)>=m_min(cur_edges)|.
+
+@p procedure xy_swap_edges; {interchange |x| and |y| in |cur_edges|}
+label done;
+var @!m_magic,@!n_magic:integer; {special values that account for offsets}
+@!p,@!q,@!r,@!s:pointer; {pointers that traverse the given structure}
+@<Other local variables for |xy_swap_edges|@>@;
+begin @<Initialize the array of new edge list heads@>;
+@<Insert blank rows at the top and bottom, and set |p| to the new top row@>;
+@<Compute the magic offset values@>;
+repeat q:=knil(p);@+if unsorted(q)>void then sort_edges(q);
+@<Insert the horizontal edges defined by adjacent rows |p,q|,
+ and destroy row~|p|@>;
+p:=q; n_magic:=n_magic-8;
+until knil(p)=cur_edges;
+free_node(p,row_node_size); {now all original rows have been recycled}
+@<Adjust the header to reflect the new edges@>;
+end;
+
+@ Here we don't bother to keep the |link| entries up to date, since the
+procedure looks only at the |knil| fields as it destroys the former
+edge structure.
+
+@<Insert blank rows at the top and bottom...@>=
+p:=get_node(row_node_size); sorted(p):=sentinel; unsorted(p):=null;@/
+knil(p):=cur_edges; knil(link(cur_edges)):=p; {the new bottom row}
+p:=get_node(row_node_size); sorted(p):=sentinel;
+knil(p):=knil(cur_edges); {the new top row}
+
+@ The new lists will become |sorted| lists later, so we initialize
+empty lists to |sentinel|.
+
+@<Initialize the array of new edge list heads@>=
+m_spread:=m_max(cur_edges)-m_min(cur_edges); {this is |>=0| by assumption}
+if m_spread>move_size then overflow("move table size",move_size);
+@:METAFONT capacity exceeded move table size}{\quad move table size@>
+for j:=0 to m_spread do move[j]:=sentinel
+
+@ @<Other local variables for |xy_swap_edges|@>=
+@!m_spread:integer; {the difference between |m_max| and |m_min|}
+@!j,@!jj:0..move_size; {indices into |move|}
+@!m,@!mm:integer; {|m| values at vertical edges}
+@!pd,@!rd:integer; {data fields from edge-and-weight nodes}
+@!pm,@!rm:integer; {|m| values from edge-and-weight nodes}
+@!w:integer; {the difference in accumulated weight}
+@!ww:integer; {as much of |w| that can be stored in a single node}
+@!dw:integer; {an increment to be added to |w|}
+
+@ At the point where we test |w<>0|, variable |w| contains
+the accumulated weight from edges already passed in
+row~|p| minus the accumulated weight from edges already passed in row~|q|.
+
+@<Insert the horizontal edges defined by adjacent rows |p,q|...@>=
+r:=sorted(p); free_node(p,row_node_size); p:=r;@/
+pd:=ho(info(p)); pm:=pd div 8;@/
+r:=sorted(q); rd:=ho(info(r)); rm:=rd div 8; w:=0;
+loop@+ begin if pm<rm then mm:=pm@+else mm:=rm;
+ if w<>0 then
+ @<Insert horizontal edges of weight |w| between |m| and~|mm|@>;
+ if pd<rd then
+ begin dw:=(pd mod 8)-zero_w;
+ @<Advance pointer |p| to the next vertical edge,
+ after destroying the previous one@>;
+ end
+ else begin if r=sentinel then goto done; {|rd=pd=ho(max_halfword)|}
+ dw:=-((rd mod 8)-zero_w);
+ @<Advance pointer |r| to the next vertical edge@>;
+ end;
+ m:=mm; w:=w+dw;
+ end;
+done:
+
+@ @<Advance pointer |r| to the next vertical edge@>=
+r:=link(r); rd:=ho(info(r)); rm:=rd div 8
+
+@ @<Advance pointer |p| to the next vertical edge...@>=
+s:=link(p); free_avail(p); p:=s; pd:=ho(info(p)); pm:=pd div 8
+
+@ Certain ``magic'' values are needed to make the following code work,
+because of the various offsets in our data structure. For now, let's not
+worry about their precise values; we shall compute |m_magic| and |n_magic|
+later, after we see what the code looks like.
+
+@ @<Insert horizontal edges of weight |w| between |m| and~|mm|@>=
+if m<>mm then
+ begin if mm-m_magic>=move_size then confusion("xy");
+@:this can't happen xy}{\quad xy@>
+ extras:=(abs(w)-1) div 3;
+ if extras>0 then
+ begin if w>0 then xw:=+3@+else xw:=-3;
+ ww:=w-extras*xw;
+ end
+ else ww:=w;
+ repeat j:=m-m_magic;
+ for k:=1 to extras do
+ begin s:=get_avail; info(s):=n_magic+xw;
+ link(s):=move[j]; move[j]:=s;
+ end;
+ s:=get_avail; info(s):=n_magic+ww;
+ link(s):=move[j]; move[j]:=s;@/
+ incr(m);
+ until m=mm;
+ end
+
+@ @<Other local variables for |xy...@>=
+@!extras:integer; {the number of additional nodes to make weights |>3|}
+@!xw:-3..3; {the additional weight in extra nodes}
+@!k:integer; {loop counter for inserting extra nodes}
+
+@ At the beginning of this step, |move[m_spread]=sentinel|, because no
+horizontal edges will extend to the right of column |m_max(cur_edges)|.
+
+@<Adjust the header to reflect the new edges@>=
+move[m_spread]:=0; j:=0;
+while move[j]=sentinel do incr(j);
+if j=m_spread then init_edges(cur_edges) {all edge weights are zero}
+else begin mm:=m_min(cur_edges);
+ m_min(cur_edges):=n_min(cur_edges);
+ m_max(cur_edges):=n_max(cur_edges)+1;
+ m_offset(cur_edges):=zero_field;
+ jj:=m_spread-1;
+ while move[jj]=sentinel do decr(jj);
+ n_min(cur_edges):=j+mm; n_max(cur_edges):=jj+mm; q:=cur_edges;
+ repeat p:=get_node(row_node_size); link(q):=p; knil(p):=q;
+ sorted(p):=move[j]; unsorted(p):=null; incr(j); q:=p;
+ until j>jj;
+ link(q):=cur_edges; knil(cur_edges):=q;
+ n_pos(cur_edges):=n_max(cur_edges)+1; n_rover(cur_edges):=cur_edges;
+ last_window_time(cur_edges):=0;
+ end;
+
+@ The values of |m_magic| and |n_magic| can be worked out by trying the
+code above on a small example; if they work correctly in simple cases,
+they should work in general.
+
+@<Compute the magic offset values@>=
+m_magic:=m_min(cur_edges)+m_offset(cur_edges)-zero_field;
+n_magic:=8*n_max(cur_edges)+8+zero_w+min_halfword
+
+@ Now let's look at the subroutine that merges the edges from a given
+edge structure into |cur_edges|. The given edge structure loses all its
+edges.
+
+@p procedure merge_edges(@!h:pointer);
+label done;
+var @!p,@!q,@!r,@!pp,@!qq,@!rr:pointer; {list manipulation registers}
+@!n:integer; {row number}
+@!k:halfword; {key register that we compare to |info(q)|}
+@!delta:integer; {change to the edge/weight data}
+begin if link(h)<>h then
+ begin if (m_min(h)<m_min(cur_edges))or(m_max(h)>m_max(cur_edges))or@|
+ (n_min(h)<n_min(cur_edges))or(n_max(h)>n_max(cur_edges)) then
+ edge_prep(m_min(h)-zero_field,m_max(h)-zero_field,
+ n_min(h)-zero_field,n_max(h)-zero_field+1);
+ if m_offset(h)<>m_offset(cur_edges) then
+ @<Adjust the data of |h| to account for a difference of offsets@>;
+ n:=n_min(cur_edges); p:=link(cur_edges); pp:=link(h);
+ while n<n_min(h) do
+ begin incr(n); p:=link(p);
+ end;
+ repeat @<Merge row |pp| into row |p|@>;
+ pp:=link(pp); p:=link(p);
+ until pp=h;
+ end;
+end;
+
+@ @<Adjust the data of |h| to account for a difference of offsets@>=
+begin pp:=link(h); delta:=8*(m_offset(cur_edges)-m_offset(h));
+repeat qq:=sorted(pp);
+while qq<>sentinel do
+ begin info(qq):=info(qq)+delta; qq:=link(qq);
+ end;
+qq:=unsorted(pp);
+while qq>void do
+ begin info(qq):=info(qq)+delta; qq:=link(qq);
+ end;
+pp:=link(pp);
+until pp=h;
+end
+
+@ The |sorted| and |unsorted| lists are merged separately. After this
+step, row~|pp| will have no edges remaining, since they will all have
+been merged into row~|p|.
+
+@<Merge row |pp|...@>=
+qq:=unsorted(pp);
+if qq>void then
+ if unsorted(p)<=void then unsorted(p):=qq
+ else begin while link(qq)>void do qq:=link(qq);
+ link(qq):=unsorted(p); unsorted(p):=unsorted(pp);
+ end;
+unsorted(pp):=null; qq:=sorted(pp);
+if qq<>sentinel then
+ begin if unsorted(p)=void then unsorted(p):=null;
+ sorted(pp):=sentinel; r:=sorted_loc(p); q:=link(r); {|q=sorted(p)|}
+ if q=sentinel then sorted(p):=qq
+ else loop@+begin k:=info(qq);
+ while k>info(q) do
+ begin r:=q; q:=link(r);
+ end;
+ link(r):=qq; rr:=link(qq); link(qq):=q;
+ if rr=sentinel then goto done;
+ r:=qq; qq:=rr;
+ end;
+ end;
+done:
+
+@ The |total_weight| routine computes the total of all pixel weights
+in a given edge structure. It's not difficult to prove that this is
+the sum of $(-w)$ times $x$ taken over all edges,
+where $w$ and~$x$ are the weight and $x$~coordinates stored in an edge.
+It's not necessary to worry that this quantity will overflow the
+size of an |integer| register, because it will be less than~$2^{31}$
+unless the edge structure has more than 174,762 edges. However, we had
+better not try to compute it as a |scaled| integer, because a total
+weight of almost $12\times 2^{12}$ can be produced by only four edges.
+
+@p function total_weight(@!h:pointer):integer; {|h| is an edge header}
+var @!p,@!q:pointer; {variables that traverse the given structure}
+@!n:integer; {accumulated total so far}
+@!m:0..65535; {packed $x$ and $w$ values, including offsets}
+begin n:=0; p:=link(h);
+while p<>h do
+ begin q:=sorted(p);
+ while q<>sentinel do
+ @<Add the contribution of node |q| to the total weight,
+ and set |q:=link(q)|@>;
+ q:=unsorted(p);
+ while q>void do
+ @<Add the contribution of node |q| to the total weight,
+ and set |q:=link(q)|@>;
+ p:=link(p);
+ end;
+total_weight:=n;
+end;
+
+@ It's not necessary to add the offsets to the $x$ coordinates, because
+an entire edge structure can be shifted without affecting its total weight.
+Similarly, we don't need to subtract |zero_field|.
+
+@<Add the contribution of node |q| to the total weight...@>=
+begin m:=ho(info(q)); n:=n-((m mod 8)-zero_w)*(m div 8);
+q:=link(q);
+end
+
+@ So far we've done lots of things to edge structures assuming that
+edges are actually present, but we haven't seen how edges get created
+in the first place. Let's turn now to the problem of generating new edges.
+
+\MF\ will display new edges as they are being computed, if |tracing_edges|
+is positive. In order to keep such data reasonably compact, only the
+points at which the path makes a $90^\circ$ or $180^\circ$ turn are listed.
+
+The tracing algorithm must remember some past history in order to suppress
+unnecessary data. Three variables |trace_x|, |trace_y|, and |trace_yy|
+provide this history: The last coordinates printed were |(trace_x,trace_y)|,
+and the previous edge traced ended at |(trace_x,trace_yy)|. Before anything
+at all has been traced, |trace_x=-4096|.
+
+@<Glob...@>=
+@!trace_x:integer; {$x$~coordinate most recently shown in a trace}
+@!trace_y:integer; {$y$~coordinate most recently shown in a trace}
+@!trace_yy:integer; {$y$~coordinate most recently encountered}
+
+@ Edge tracing is initiated by the |begin_edge_tracing| routine,
+continued by the |trace_a_corner| routine, and terminated by the
+|end_edge_tracing| routine.
+
+@p procedure begin_edge_tracing;
+begin print_diagnostic("Tracing edges","",true);
+print(" (weight "); print_int(cur_wt); print_char(")"); trace_x:=-4096;
+end;
+@#
+procedure trace_a_corner;
+begin if file_offset>max_print_line-13 then print_nl("");
+print_char("("); print_int(trace_x); print_char(","); print_int(trace_yy);
+print_char(")"); trace_y:=trace_yy;
+end;
+@#
+procedure end_edge_tracing;
+begin if trace_x=-4096 then print_nl("(No new edges added.)")
+@.No new edges added@>
+else begin trace_a_corner; print_char(".");
+ end;
+end_diagnostic(true);
+end;
+
+@ Just after a new edge weight has been put into the |info| field of
+node~|r|, in row~|n|, the following routine continues an ongoing trace.
+
+@p procedure trace_new_edge(@!r:pointer;@!n:integer);
+var @!d:integer; {temporary data register}
+@!w:-3..3; {weight associated with an edge transition}
+@!m,@!n0,@!n1:integer; {column and row numbers}
+begin d:=ho(info(r)); w:=(d mod 8)-zero_w; m:=(d div 8)-m_offset(cur_edges);
+if w=cur_wt then
+ begin n0:=n+1; n1:=n;
+ end
+else begin n0:=n; n1:=n+1;
+ end; {the edges run from |(m,n0)| to |(m,n1)|}
+if m<>trace_x then
+ begin if trace_x=-4096 then
+ begin print_nl(""); trace_yy:=n0;
+ end
+ else if trace_yy<>n0 then print_char("?") {shouldn't happen}
+ else trace_a_corner;
+ trace_x:=m; trace_a_corner;
+ end
+else begin if n0<>trace_yy then print_char("!"); {shouldn't happen}
+ if ((n0<n1)and(trace_y>trace_yy))or((n0>n1)and(trace_y<trace_yy)) then
+ trace_a_corner;
+ end;
+trace_yy:=n1;
+end;
+
+@ One way to put new edge weights into an edge structure is to use the
+following routine, which simply draws a straight line from |(x0,y0)| to
+|(x1,y1)|. More precisely, it introduces weights for the edges of the
+discrete path $\bigl(\lfloor t[x_0,x_1]+{1\over2}+\epsilon\rfloor,
+\lfloor t[y_0,y_1]+{1\over2}+\epsilon\delta\rfloor\bigr)$,
+as $t$ varies from 0 to~1, where $\epsilon$ and $\delta$ are extremely small
+positive numbers.
+
+The structure header is assumed to be |cur_edges|; downward edge weights
+will be |cur_wt|, while upward ones will be |-cur_wt|.
+
+Of course, this subroutine will be called only in connection with others
+that eventually draw a complete cycle, so that the sum of the edge weights
+in each row will be zero whenever the row is displayed.
+
+@p procedure line_edges(@!x0,@!y0,@!x1,@!y1:scaled);
+label done,done1;
+var @!m0,@!n0,@!m1,@!n1:integer; {rounded and unscaled coordinates}
+@!delx,@!dely:scaled; {the coordinate differences of the line}
+@!yt:scaled; {smallest |y| coordinate that rounds the same as |y0|}
+@!tx:scaled; {tentative change in |x|}
+@!p,@!r:pointer; {list manipulation registers}
+@!base:integer; {amount added to edge-and-weight data}
+@!n:integer; {current row number}
+begin n0:=round_unscaled(y0);
+n1:=round_unscaled(y1);
+if n0<>n1 then
+ begin m0:=round_unscaled(x0); m1:=round_unscaled(x1);
+ delx:=x1-x0; dely:=y1-y0;
+ yt:=n0*unity-half_unit; y0:=y0-yt; y1:=y1-yt;
+ if n0<n1 then @<Insert upward edges for a line@>
+ else @<Insert downward edges for a line@>;
+ n_rover(cur_edges):=p; n_pos(cur_edges):=n+zero_field;
+ end;
+end;
+
+@ Here we are careful to cancel any effect of rounding error.
+
+@<Insert upward edges for a line@>=
+begin base:=8*m_offset(cur_edges)+min_halfword+zero_w-cur_wt;
+if m0<=m1 then edge_prep(m0,m1,n0,n1)@+else edge_prep(m1,m0,n0,n1);
+@<Move to row |n0|, pointed to by |p|@>;
+y0:=unity-y0;
+loop@+ begin r:=get_avail; link(r):=unsorted(p); unsorted(p):=r;@/
+ tx:=take_fraction(delx,make_fraction(y0,dely));
+ if ab_vs_cd(delx,y0,dely,tx)<0 then decr(tx);
+ {now $|tx|=\lfloor|y0|\cdot|delx|/|dely|\rfloor$}
+ info(r):=8*round_unscaled(x0+tx)+base;@/
+ y1:=y1-unity;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ if y1<unity then goto done;
+ p:=link(p); y0:=y0+unity; incr(n);
+ end;
+done: end
+
+@ @<Insert downward edges for a line@>=
+begin base:=8*m_offset(cur_edges)+min_halfword+zero_w+cur_wt;
+if m0<=m1 then edge_prep(m0,m1,n1,n0)@+else edge_prep(m1,m0,n1,n0);
+decr(n0); @<Move to row |n0|, pointed to by |p|@>;
+loop@+ begin r:=get_avail; link(r):=unsorted(p); unsorted(p):=r;@/
+ tx:=take_fraction(delx,make_fraction(y0,dely));
+ if ab_vs_cd(delx,y0,dely,tx)<0 then incr(tx);
+ {now $|tx|=\lceil|y0|\cdot|delx|/|dely|\rceil$, since |dely<0|}
+ info(r):=8*round_unscaled(x0-tx)+base;@/
+ y1:=y1+unity;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ if y1>=0 then goto done1;
+ p:=knil(p); y0:=y0+unity; decr(n);
+ end;
+done1: end
+
+@ @<Move to row |n0|, pointed to by |p|@>=
+n:=n_pos(cur_edges)-zero_field; p:=n_rover(cur_edges);
+if n<>n0 then
+ if n<n0 then
+ repeat incr(n); p:=link(p);
+ until n=n0
+ else repeat decr(n); p:=knil(p);
+ until n=n0
+
+@ \MF\ inserts most of its edges into edge structures via the
+|move_to_edges| subroutine, which uses the data stored in the |move| array
+to specify a sequence of ``rook moves.'' The starting point |(m0,n0)|
+and finishing point |(m1,n1)| of these moves, as seen from the standpoint
+of the first octant, are supplied as parameters; the moves should, however,
+be rotated into a given octant. (We're going to study octant
+transformations in great detail later; the reader may wish to come back to
+this part of the program after mastering the mysteries of octants.)
+
+The rook moves themselves are defined as follows, from a |first_octant|
+point of view: ``Go right |move[k]| steps, then go up one, for |0<=k<n1-n0|;
+then go right |move[n1-n0]| steps and stop.'' The sum of |move[k]|
+for |0<=k<=n1-n0| will be equal to |m1-m0|.
+
+As in the |line_edges| routine, we use |+cur_wt| as the weight of
+all downward edges and |-cur_wt| as the weight of all upward edges,
+after the moves have been rotated to the proper octant direction.
+
+There are two main cases to consider: \\{fast\_case} is for moves that
+travel in the direction of octants 1, 4, 5, and~8, while \\{slow\_case}
+is for moves that travel toward octants 2, 3, 6, and~7. The latter directions
+are comparatively cumbersome because they generate more upward or downward
+edges; a curve that travels horizontally doesn't produce any edges at all,
+but a curve that travels vertically touches lots of rows.
+
+@d fast_case_up=60 {for octants 1 and 4}
+@d fast_case_down=61 {for octants 5 and 8}
+@d slow_case_up=62 {for octants 2 and 3}
+@d slow_case_down=63 {for octants 6 and 7}
+
+@p procedure move_to_edges(@!m0,@!n0,@!m1,@!n1:integer);
+label fast_case_up,fast_case_down,slow_case_up,slow_case_down,done;
+var @!delta:0..move_size; {extent of |move| data}
+@!k:0..move_size; {index into |move|}
+@!p,@!r:pointer; {list manipulation registers}
+@!dx:integer; {change in edge-weight |info| when |x| changes by 1}
+@!edge_and_weight:integer; {|info| to insert}
+@!j:integer; {number of consecutive vertical moves}
+@!n:integer; {the current row pointed to by |p|}
+debug @!sum:integer;@+gubed@;@/
+begin delta:=n1-n0;
+debug sum:=move[0]; for k:=1 to delta do sum:=sum+abs(move[k]);
+if sum<>m1-m0 then confusion("0");@+gubed@;@/
+@:this can't happen 0}{\quad 0@>
+@<Prepare for and switch to the appropriate case, based on |octant|@>;
+fast_case_up:@<Add edges for first or fourth octants, then |goto done|@>;
+fast_case_down:@<Add edges for fifth or eighth octants, then |goto done|@>;
+slow_case_up:@<Add edges for second or third octants, then |goto done|@>;
+slow_case_down:@<Add edges for sixth or seventh octants, then |goto done|@>;
+done: n_pos(cur_edges):=n+zero_field; n_rover(cur_edges):=p;
+end;
+
+@ The current octant code appears in a global variable. If, for example,
+we have |octant=third_octant|, it means that a curve traveling in a north to
+north-westerly direction has been rotated for the purposes of internal
+calculations so that the |move| data travels in an east to north-easterly
+direction. We want to unrotate as we update the edge structure.
+
+@<Glob...@>=
+@!octant:first_octant..sixth_octant; {the current octant of interest}
+
+@ @<Prepare for and switch to the appropriate case, based on |octant|@>=
+case octant of
+first_octant:begin dx:=8; edge_prep(m0,m1,n0,n1); goto fast_case_up;
+ end;
+second_octant:begin dx:=8; edge_prep(n0,n1,m0,m1); goto slow_case_up;
+ end;
+third_octant:begin dx:=-8; edge_prep(-n1,-n0,m0,m1); negate(n0);
+ goto slow_case_up;
+ end;
+fourth_octant:begin dx:=-8; edge_prep(-m1,-m0,n0,n1); negate(m0);
+ goto fast_case_up;
+ end;
+fifth_octant:begin dx:=-8; edge_prep(-m1,-m0,-n1,-n0); negate(m0);
+ goto fast_case_down;
+ end;
+sixth_octant:begin dx:=-8; edge_prep(-n1,-n0,-m1,-m0); negate(n0);
+ goto slow_case_down;
+ end;
+seventh_octant:begin dx:=8; edge_prep(n0,n1,-m1,-m0); goto slow_case_down;
+ end;
+eighth_octant:begin dx:=8; edge_prep(m0,m1,-n1,-n0); goto fast_case_down;
+ end;
+end; {there are only eight octants}
+
+@ @<Add edges for first or fourth octants, then |goto done|@>=
+@<Move to row |n0|, pointed to by |p|@>;
+if delta>0 then
+ begin k:=0;
+ edge_and_weight:=8*(m0+m_offset(cur_edges))+min_halfword+zero_w-cur_wt;
+ repeat edge_and_weight:=edge_and_weight+dx*move[k];
+ fast_get_avail(r); link(r):=unsorted(p); info(r):=edge_and_weight;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ unsorted(p):=r; p:=link(p); incr(k); incr(n);
+ until k=delta;
+ end;
+goto done
+
+@ @<Add edges for fifth or eighth octants, then |goto done|@>=
+n0:=-n0-1; @<Move to row |n0|, pointed to by |p|@>;
+if delta>0 then
+ begin k:=0;
+ edge_and_weight:=8*(m0+m_offset(cur_edges))+min_halfword+zero_w+cur_wt;
+ repeat edge_and_weight:=edge_and_weight+dx*move[k];
+ fast_get_avail(r); link(r):=unsorted(p); info(r):=edge_and_weight;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ unsorted(p):=r; p:=knil(p); incr(k); decr(n);
+ until k=delta;
+ end;
+goto done
+
+@ @<Add edges for second or third octants, then |goto done|@>=
+edge_and_weight:=8*(n0+m_offset(cur_edges))+min_halfword+zero_w-cur_wt;
+n0:=m0; k:=0; @<Move to row |n0|, pointed to by |p|@>;
+repeat j:=move[k];
+while j>0 do
+ begin fast_get_avail(r); link(r):=unsorted(p); info(r):=edge_and_weight;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ unsorted(p):=r; p:=link(p); decr(j); incr(n);
+ end;
+edge_and_weight:=edge_and_weight+dx; incr(k);
+until k>delta;
+goto done
+
+@ @<Add edges for sixth or seventh octants, then |goto done|@>=
+edge_and_weight:=8*(n0+m_offset(cur_edges))+min_halfword+zero_w+cur_wt;
+n0:=-m0-1; k:=0; @<Move to row |n0|, pointed to by |p|@>;
+repeat j:=move[k];
+while j>0 do
+ begin fast_get_avail(r); link(r):=unsorted(p); info(r):=edge_and_weight;
+ if internal[tracing_edges]>0 then trace_new_edge(r,n);
+ unsorted(p):=r; p:=knil(p); decr(j); decr(n);
+ end;
+edge_and_weight:=edge_and_weight+dx; incr(k);
+until k>delta;
+goto done
+
+@ All the hard work of building an edge structure is undone by the following
+subroutine.
+
+@<Declare the recycling subroutines@>=
+procedure toss_edges(@!h:pointer);
+var @!p,@!q:pointer; {for list manipulation}
+begin q:=link(h);
+while q<>h do
+ begin flush_list(sorted(q));
+ if unsorted(q)>void then flush_list(unsorted(q));
+ p:=q; q:=link(q); free_node(p,row_node_size);
+ end;
+free_node(h,edge_header_size);
+end;
+
+@* \[21] Subdivision into octants.
+When \MF\ digitizes a path, it reduces the problem to the special
+case of paths that travel in ``first octant'' directions; i.e.,
+each cubic $z(t)=\bigl(x(t),y(t)\bigr)$ being digitized will have the property
+that $0\L y'(t)\L x'(t)$. This assumption makes digitizing simpler
+and faster than if the direction of motion has to be tested repeatedly.
+
+When $z(t)$ is cubic, $x'(t)$ and $y'(t)$ are quadratic, hence the four
+polynomials $x'(t)$, $y'(t)$, $x'(t)-y'(t)$, and $x'(t)+y'(t)$ cross
+through~0 at most twice each. If we subdivide the given cubic at these
+places, we get at most nine subintervals in each of which
+$x'(t)$, $y'(t)$, $x'(t)-y'(t)$, and $x'(t)+y'(t)$ all have a constant
+sign. The curve can be transformed in each of these subintervals so that
+it travels entirely in first octant directions, if we reflect $x\swap-x$,
+$y\swap-y$, and/or $x\swap y$ as necessary. (Incidentally, it can be
+shown that a cubic such that $x'(t)=16(2t-1)^2+2(2t-1)-1$ and
+$y'(t)=8(2t-1)^2+4(2t-1)$ does indeed split into nine subintervals.)
+
+@ The transformation that rotates coordinates, so that first octant motion
+can be assumed, is defined by the |skew| subroutine, which sets global
+variables |cur_x| and |cur_y| to the values that are appropriate in a
+given octant. (Octants are encoded as they were in the |n_arg| subroutine.)
+
+This transformation is ``skewed'' by replacing |(x,y)| by |(x-y,y)|,
+once first octant motion has been established. It turns out that
+skewed coordinates are somewhat better to work with when curves are
+actually digitized.
+
+@d set_two_end(#)==cur_y:=#;@+end
+@d set_two(#)==begin cur_x:=#; set_two_end
+
+@p procedure skew(@!x,@!y:scaled;@!octant:small_number);
+begin case octant of
+first_octant: set_two(x-y)(y);
+second_octant: set_two(y-x)(x);
+third_octant: set_two(y+x)(-x);
+fourth_octant: set_two(-x-y)(y);
+fifth_octant: set_two(-x+y)(-y);
+sixth_octant: set_two(-y+x)(-x);
+seventh_octant: set_two(-y-x)(x);
+eighth_octant: set_two(x+y)(-y);
+end; {there are no other cases}
+end;
+
+@ Conversely, the following subroutine sets |cur_x| and
+|cur_y| to the original coordinate values of a point, given an octant
+code and the point's coordinates |(x,y)| after they have been mapped into
+the first octant and skewed.
+
+@<Declare subroutines for printing expressions@>=
+procedure unskew(@!x,@!y:scaled;@!octant:small_number);
+begin case octant of
+first_octant: set_two(x+y)(y);
+second_octant: set_two(y)(x+y);
+third_octant: set_two(-y)(x+y);
+fourth_octant: set_two(-x-y)(y);
+fifth_octant: set_two(-x-y)(-y);
+sixth_octant: set_two(-y)(-x-y);
+seventh_octant: set_two(y)(-x-y);
+eighth_octant: set_two(x+y)(-y);
+end; {there are no other cases}
+end;
+
+@ @<Glob...@>=
+@!cur_x,@!cur_y:scaled;
+ {outputs of |skew|, |unskew|, and a few other routines}
+
+@ The conversion to skewed and rotated coordinates takes place in
+stages, and at one point in the transformation we will have negated the
+$x$ and/or $y$ coordinates so as to make curves travel in the first
+{\sl quadrant}. At this point the relevant ``octant'' code will be
+either |first_octant| (when no transformation has been done),
+or |fourth_octant=first_octant+negate_x| (when $x$ has been negated),
+or |fifth_octant=first_octant+negate_x+negate_y| (when both have been
+negated), or |eighth_octant=first_octant+negate_y| (when $y$ has been
+negated). The |abnegate| routine is sometimes needed to convert
+from one of these transformations to another.
+
+@p procedure abnegate(@!x,@!y:scaled;
+ @!octant_before,@!octant_after:small_number);
+begin if odd(octant_before)=odd(octant_after) then cur_x:=x
+ else cur_x:=-x;
+if (octant_before>negate_y)=(octant_after>negate_y) then cur_y:=y
+ else cur_y:=-y;
+end;
+
+@ Now here's a subroutine that's handy for subdivision: Given a
+quadratic polynomial $B(a,b,c;t)$, the |crossing_point| function
+returns the unique |fraction| value |t| between 0 and~1 at which
+$B(a,b,c;t)$ changes from positive to negative, or returns
+|t=fraction_one+1| if no such value exists. If |a<0| (so that $B(a,b,c;t)$
+is already negative at |t=0|), |crossing_point| returns the value zero.
+
+@d no_crossing==begin crossing_point:=fraction_one+1; return;
+ end
+@d one_crossing==begin crossing_point:=fraction_one; return;
+ end
+@d zero_crossing==begin crossing_point:=0; return;
+ end
+
+@p function crossing_point(@!a,@!b,@!c:integer):fraction;
+label exit;
+var @!d:integer; {recursive counter}
+@!x,@!xx,@!x0,@!x1,@!x2:integer; {temporary registers for bisection}
+begin if a<0 then zero_crossing;
+if c>=0 then
+ begin if b>=0 then
+ if c>0 then no_crossing
+ else if (a=0)and(b=0) then no_crossing
+ else one_crossing;
+ if a=0 then zero_crossing;
+ end
+else if a=0 then if b<=0 then zero_crossing;
+@<Use bisection to find the crossing point, if one exists@>;
+exit:end;
+
+@ The general bisection method is quite simple when $n=2$, hence
+|crossing_point| does not take much time. At each stage in the
+recursion we have a subinterval defined by |l| and~|j| such that
+$B(a,b,c;2^{-l}(j+t))=B(x_0,x_1,x_2;t)$, and we want to ``zero in'' on
+the subinterval where $x_0\G0$ and $\min(x_1,x_2)<0$.
+
+It is convenient for purposes of calculation to combine the values
+of |l| and~|j| in a single variable $d=2^l+j$, because the operation
+of bisection then corresponds simply to doubling $d$ and possibly
+adding~1. Furthermore it proves to be convenient to modify
+our previous conventions for bisection slightly, maintaining the
+variables $X_0=2^lx_0$, $X_1=2^l(x_0-x_1)$, and $X_2=2^l(x_1-x_2)$.
+With these variables the conditions $x_0\ge0$ and $\min(x_1,x_2)<0$ are
+equivalent to $\max(X_1,X_1+X_2)>X_0\ge0$.
+
+The following code maintains the invariant relations
+$0\L|x0|<\max(|x1|,|x1|+|x2|)$,
+$\vert|x1|\vert<2^{30}$, $\vert|x2|\vert<2^{30}$;
+it has been constructed in such a way that no arithmetic overflow
+will occur if the inputs satisfy
+$a<2^{30}$, $\vert a-b\vert<2^{30}$, and $\vert b-c\vert<2^{30}$.
+
+@<Use bisection to find the crossing point...@>=
+d:=1; x0:=a; x1:=a-b; x2:=b-c;
+repeat x:=half(x1+x2);
+if x1-x0>x0 then
+ begin x2:=x; double(x0); double(d);
+ end
+else begin xx:=x1+x-x0;
+ if xx>x0 then
+ begin x2:=x; double(x0); double(d);
+ end
+ else begin x0:=x0-xx;
+ if x<=x0 then if x+x2<=x0 then no_crossing;
+ x1:=x; d:=d+d+1;
+ end;
+ end;
+until d>=fraction_one;
+crossing_point:=d-fraction_one
+
+@ Octant subdivision is applied only to cycles, i.e., to closed paths.
+A ``cycle spec'' is a data structure that contains specifications of
+@!@^cycle spec@>
+cubic curves and octant mappings for the cycle that has been subdivided
+into segments belonging to single octants. It is composed entirely of
+knot nodes, similar to those in the representation of paths; but the
+|explicit| type indications have been replaced by positive numbers
+that give further information. Additional |endpoint| data is also
+inserted at the octant boundaries.
+
+Recall that a cubic polynomial is represented by four control points
+that appear in adjacent nodes |p| and~|q| of a knot list. The |x|~coordinates
+are |x_coord(p)|, |right_x(p)|, |left_x(q)|, and |x_coord(q)|; the
+|y|~coordinates are similar. We shall call this ``the cubic following~|p|''
+or ``the cubic between |p| and~|q|'' or ``the cubic preceding~|q|.''
+
+Cycle specs are circular lists of cubic curves mixed with octant
+boundaries. Like cubics, the octant boundaries are represented in
+consecutive knot nodes |p| and~|q|. In such cases |right_type(p)=
+left_type(q)=endpoint|, and the fields |right_x(p)|, |right_y(p)|,
+|left_x(q)|, and |left_y(q)| are replaced by other fields called
+|right_octant(p)|, |right_transition(p)|, |left_octant(q)|, and
+|left_transition(q)|, respectively. For example, when the curve direction
+moves from the third octant to the fourth octant, the boundary nodes say
+|right_octant(p)=third_octant|, |left_octant(q)=fourth_octant|,
+and |right_transition(p)=left_transition(q)=diagonal|. A |diagonal|
+transition occurs when moving between octants 1~\AM~2, 3~\AM~4, 5~\AM~6, or
+7~\AM~8; an |axis| transition occurs when moving between octants 8~\AM~1,
+2~\AM~3, 4~\AM~5, 6~\AM~7. (Such transition information is redundant
+but convenient.) Fields |x_coord(p)| and |y_coord(p)| will contain
+coordinates of the transition point after rotation from third octant
+to first octant; i.e., if the true coordinates are $(x,y)$, the
+coordinates $(y,-x)$ will appear in node~|p|. Similarly, a fourth-octant
+transformation will have been applied after the transition, so
+we will have |x_coord(q)=@t$-x$@>| and |y_coord(q)=y|.
+
+The cubic between |p| and |q| will contain positive numbers in the
+fields |right_type(p)| and |left_type(q)|; this makes cubics
+distinguishable from octant boundaries, because |endpoint=0|.
+The value of |right_type(p)| will be the current octant code,
+during the time that cycle specs are being constructed; it will
+refer later to a pen offset position, if the envelope of a cycle is
+being computed. A cubic that comes from some subinterval of the $k$th
+step in the original cyclic path will have |left_type(q)=k|.
+
+@d right_octant==right_x {the octant code before a transition}
+@d left_octant==left_x {the octant after a transition}
+@d right_transition==right_y {the type of transition}
+@d left_transition==left_y {ditto, either |axis| or |diagonal|}
+@d axis=0 {a transition across the $x'$- or $y'$-axis}
+@d diagonal=1 {a transition where $y'=\pm x'$}
+
+@ Here's a routine that prints a cycle spec in symbolic form, so that it
+is possible to see what subdivision has been made. The point coordinates
+are converted back from \MF's internal ``rotated'' form to the external
+``true'' form. The global variable~|cur_spec| should point to a knot just
+after the beginning of an octant boundary, i.e., such that
+|left_type(cur_spec)=endpoint|.
+
+@d print_two_true(#)==unskew(#,octant); print_two(cur_x,cur_y)
+
+@p procedure print_spec(@!s:str_number);
+label not_found,done;
+var @!p,@!q:pointer; {for list traversal}
+@!octant:small_number; {the current octant code}
+begin print_diagnostic("Cycle spec",s,true);
+@.Cycle spec at line...@>
+p:=cur_spec; octant:=left_octant(p); print_ln;
+print_two_true(x_coord(cur_spec),y_coord(cur_spec));
+print(" % beginning in octant `");
+loop@+ begin print(octant_dir[octant]); print_char("'");
+ loop@+ begin q:=link(p);
+ if right_type(p)=endpoint then goto not_found;
+ @<Print the cubic between |p| and |q|@>;
+ p:=q;
+ end;
+not_found: if q=cur_spec then goto done;
+ p:=q; octant:=left_octant(p); print_nl("% entering octant `");
+ end;
+@.entering the nth octant@>
+done: print_nl(" & cycle"); end_diagnostic(true);
+end;
+
+@ Symbolic octant direction names are kept in the |octant_dir| array.
+
+@<Glob...@>=
+@!octant_dir:array[first_octant..sixth_octant] of str_number;
+
+@ @<Set init...@>=
+octant_dir[first_octant]:="ENE";
+octant_dir[second_octant]:="NNE";
+octant_dir[third_octant]:="NNW";
+octant_dir[fourth_octant]:="WNW";
+octant_dir[fifth_octant]:="WSW";
+octant_dir[sixth_octant]:="SSW";
+octant_dir[seventh_octant]:="SSE";
+octant_dir[eighth_octant]:="ESE";
+
+@ @<Print the cubic between...@>=
+begin print_nl(" ..controls ");
+print_two_true(right_x(p),right_y(p));
+print(" and ");
+print_two_true(left_x(q),left_y(q));
+print_nl(" ..");
+print_two_true(x_coord(q),y_coord(q));
+print(" % segment "); print_int(left_type(q)-1);
+end
+
+@ A much more compact version of a spec is printed to help users identify
+``strange paths.''
+
+@p procedure print_strange(@!s:str_number);
+var @!p:pointer; {for list traversal}
+@!f:pointer; {starting point in the cycle}
+@!q:pointer; {octant boundary to be printed}
+@!t:integer; {segment number, plus 1}
+begin if interaction=error_stop_mode then wake_up_terminal;
+print_nl(">");
+@.>\relax@>
+@<Find the starting point, |f|@>;
+@<Determine the octant boundary |q| that precedes |f|@>;
+t:=0;
+repeat if left_type(p)<>endpoint then
+ begin if left_type(p)<>t then
+ begin t:=left_type(p); print_char(" "); print_int(t-1);
+ end;
+ if q<>null then
+ begin @<Print the turns, if any, that start at |q|, and advance |q|@>;
+ print_char(" "); print(octant_dir[left_octant(q)]); q:=null;
+ end;
+ end
+else if q=null then q:=p;
+p:=link(p);
+until p=f;
+print_char(" "); print_int(left_type(p)-1);
+if q<>null then @<Print the turns...@>;
+print_err(s);
+end;
+
+@ If the segment numbers on the cycle are $t_1$, $t_2$, \dots, $t_m$,
+and if |m<=max_quarterword|,
+we have $t_{k-1}\L t_k$ except for at most one value of~$k$. If there are
+no exceptions, $f$ will point to $t_1$; otherwise it will point to the
+exceptional~$t_k$.
+
+There is at least one segment number (i.e., we always have $m>0$), because
+|print_strange| is never called upon to display an entirely ``dead'' cycle.
+
+@<Find the starting point, |f|@>=
+p:=cur_spec; t:=max_quarterword+1;
+repeat p:=link(p);
+if left_type(p)<>endpoint then
+ begin if left_type(p)<t then f:=p;
+ t:=left_type(p);
+ end;
+until p=cur_spec
+
+@ @<Determine the octant boundary...@>=
+p:=cur_spec; q:=p;
+repeat p:=link(p);
+if left_type(p)=endpoint then q:=p;
+until p=f
+
+@ When two octant boundaries are adjacent, the path is simply changing direction
+without moving. Such octant directions are shown in parentheses.
+
+@<Print the turns...@>=
+if left_type(link(q))=endpoint then
+ begin print(" ("); print(octant_dir[left_octant(q)]); q:=link(q);
+ while left_type(link(q))=endpoint do
+ begin print_char(" "); print(octant_dir[left_octant(q)]); q:=link(q);
+ end;
+ print_char(")");
+ end
+
+@ The |make_spec| routine is what subdivides paths into octants:
+Given a pointer |cur_spec| to a cyclic path, |make_spec| mungs the path data
+and returns a pointer to the corresponding cyclic spec.
+All ``dead'' cubics (i.e., cubics that don't move at all from
+their starting points) will have been removed from the result.
+@!@^dead cubics@>
+
+The idea of |make_spec| is fairly simple: Each cubic is first
+subdivided, if necessary, into pieces belonging to single octants;
+then the octant boundaries are inserted. But some of the details of
+this transformation are not quite obvious.
+
+If |autorounding>0|, the path will be adjusted so that critical tangent
+directions occur at ``good'' points with respect to the pen called |cur_pen|.
+
+The resulting spec will have all |x| and |y| coordinates at most
+$2^{28}-|half_unit|-1-|safety_margin|$ in absolute value. The pointer
+that is returned will start some octant, as required by |print_spec|.
+
+@p @t\4@>@<Declare subroutines needed by |make_spec|@>@;
+function make_spec(@!h:pointer;
+ @!safety_margin:scaled;@!tracing:integer):pointer;
+ {converts a path to a cycle spec}
+label continue,done;
+var @!p,@!q,@!r,@!s:pointer; {for traversing the lists}
+@!k:integer; {serial number of path segment, or octant code}
+@!chopped:integer; {positive if data truncated,
+ negative if data dangerously large}
+@<Other local variables for |make_spec|@>@;
+begin cur_spec:=h;
+if tracing>0 then
+ print_path(cur_spec,", before subdivision into octants",true);
+max_allowed:=fraction_one-half_unit-1-safety_margin;
+@<Truncate the values of all coordinates that exceed |max_allowed|, and stamp
+ segment numbers in each |left_type| field@>;
+quadrant_subdivide; {subdivide each cubic into pieces belonging to quadrants}
+if (internal[autorounding]>0)and(chopped=0) then xy_round;
+octant_subdivide; {complete the subdivision}
+if (internal[autorounding]>unity)and(chopped=0) then diag_round;
+@<Remove dead cubics@>;
+@<Insert octant boundaries and compute the turning number@>;
+while left_type(cur_spec)<>endpoint do cur_spec:=link(cur_spec);
+if tracing>0 then
+ if (internal[autorounding]<=0)or(chopped<>0) then
+ print_spec(", after subdivision")
+ else if internal[autorounding]>unity then
+ print_spec(", after subdivision and double autorounding")
+ else print_spec(", after subdivision and autorounding");
+make_spec:=cur_spec;
+end;
+
+@ The |make_spec| routine has an interesting side effect, namely to set
+the global variable |turning_number| to the number of times the tangent
+vector of the given cyclic path winds around the origin.
+
+Another global variable |cur_spec| points to the specification as it is
+being made, since several subroutines must go to work on it.
+
+And there are two global variables that affect the rounding
+decisions, as we'll see later; they are called |cur_pen| and |cur_path_type|.
+The latter will be |double_path_code| if |make_spec| is being
+applied to a double path.
+
+@d double_path_code=0 {command modifier for `\&{doublepath}'}
+@d contour_code=1 {command modifier for `\&{contour}'}
+@d also_code=2 {command modifier for `\&{also}'}
+
+@<Glob...@>=
+@!cur_spec:pointer; {the principal output of |make_spec|}
+@!turning_number:integer; {another output of |make_spec|}
+@!cur_pen:pointer; {an implicit input of |make_spec|, used in autorounding}
+@!cur_path_type:double_path_code..contour_code; {likewise}
+@!max_allowed:scaled; {coordinates must be at most this big}
+
+@ First we do a simple preprocessing step. The segment numbers inserted
+here will propagate to all descendants of cubics that are split into
+subintervals. These numbers must be nonzero, but otherwise they are
+present merely for diagnostic purposes. The cubic from |p| to~|q|
+that represents ``time interval'' |(t-1)..t| usually has |left_type(q)=t|,
+except when |t| is too large to be stored in a quarterword.
+
+@d procrustes(#)==@+if abs(#)>=dmax then
+ if abs(#)>max_allowed then
+ begin chopped:=1;
+ if #>0 then #:=max_allowed@+else #:=-max_allowed;
+ end
+ else if chopped=0 then chopped:=-1
+
+@<Truncate the values of all coordinates that exceed...@>=
+p:=cur_spec; k:=1; chopped:=0; dmax:=half(max_allowed);
+repeat procrustes(left_x(p)); procrustes(left_y(p));
+procrustes(x_coord(p)); procrustes(y_coord(p));
+procrustes(right_x(p)); procrustes(right_y(p));@/
+p:=link(p); left_type(p):=k;
+if k<max_quarterword then incr(k)@+else k:=1;
+until p=cur_spec;
+if chopped>0 then
+ begin print_err("Curve out of range");
+@.Curve out of range@>
+ help4("At least one of the coordinates in the path I'm about to")@/
+ ("digitize was really huge (potentially bigger than 4095).")@/
+ ("So I've cut it back to the maximum size.")@/
+ ("The results will probably be pretty wild.");
+ put_get_error;
+ end
+
+@ We may need to get rid of constant ``dead'' cubics that clutter up
+the data structure and interfere with autorounding.
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure remove_cubic(@!p:pointer); {removes the cubic following~|p|}
+var @!q:pointer; {the node that disappears}
+begin q:=link(p); right_type(p):=right_type(q); link(p):=link(q);@/
+x_coord(p):=x_coord(q); y_coord(p):=y_coord(q);@/
+right_x(p):=right_x(q); right_y(p):=right_y(q);@/
+free_node(q,knot_node_size);
+end;
+
+@ The subdivision process proceeds by first swapping $x\swap-x$, if
+necessary, to ensure that $x'\G0$; then swapping $y\swap-y$, if necessary,
+to ensure that $y'\G0$; and finally swapping $x\swap y$, if necessary,
+to ensure that $x'\G y'$.
+
+Recall that the octant codes have been defined in such a way that, for
+example, |third_octant=first_octant+negate_x+switch_x_and_y|. The program
+uses the fact that |negate_x<negate_y<switch_x_and_y| to handle ``double
+negation'': If |c| is an octant code that possibly involves |negate_x|
+and/or |negate_y|, but not |switch_x_and_y|, then negating~|y| changes~|c|
+either to |c+negate_y| or |c-negate_y|, depending on whether
+|c<=negate_y| or |c>negate_y|. Octant codes are always greater than zero.
+
+The first step is to subdivide on |x| and |y| only, so that horizontal
+and vertical autorounding can be done before we compare $x'$ to $y'$.
+
+@<Declare subroutines needed by |make_spec|@>=
+@t\4@>@<Declare the procedure called |split_cubic|@>@;
+procedure quadrant_subdivide;
+label continue,exit;
+var @!p,@!q,@!r,@!s,@!pp,@!qq:pointer; {for traversing the lists}
+@!first_x,@!first_y:scaled; {unnegated coordinates of node |cur_spec|}
+@!del1,@!del2,@!del3,@!del,@!dmax:scaled; {proportional to the control
+ points of a quadratic derived from a cubic}
+@!t:fraction; {where a quadratic crosses zero}
+@!dest_x,@!dest_y:scaled; {final values of |x| and |y| in the current cubic}
+@!constant_x:boolean; {is |x| constant between |p| and |q|?}
+begin p:=cur_spec; first_x:=x_coord(cur_spec); first_y:=y_coord(cur_spec);
+repeat continue: q:=link(p);
+@<Subdivide the cubic between |p| and |q| so that the results travel
+ toward the right halfplane@>;
+@<Subdivide all cubics between |p| and |q| so that the results travel
+ toward the first quadrant; but |return| or |goto continue| if the
+ cubic from |p| to |q| was dead@>;
+p:=q;
+until p=cur_spec;
+exit:end;
+
+@ All three subdivision processes are similar, so it's possible to
+get the general idea by studying the first one (which is the simplest).
+The calculation makes use of the fact that the derivatives of
+Bernshte{\u\i}n polynomials satisfy
+$B'(z_0,z_1,\ldots,z_n;t)=nB(z_1-z_0,\ldots,z_n-z_{n-1};t)$.
+
+When this routine begins, |right_type(p)| is |explicit|; we should
+set |right_type(p):=first_octant|. However, no assignment is made,
+because |explicit=first_octant|. The author apologizes for using
+such trickery here; it is really hard to do redundant computations
+just for the sake of purity.
+
+@<Subdivide the cubic between |p| and |q| so that the results travel
+ toward the right halfplane...@>=
+if q=cur_spec then
+ begin dest_x:=first_x; dest_y:=first_y;
+ end
+else begin dest_x:=x_coord(q); dest_y:=y_coord(q);
+ end;
+del1:=right_x(p)-x_coord(p); del2:=left_x(q)-right_x(p);
+del3:=dest_x-left_x(q);
+@<Scale up |del1|, |del2|, and |del3| for greater accuracy;
+ also set |del| to the first nonzero element of |(del1,del2,del3)|@>;
+if del=0 then constant_x:=true
+else begin constant_x:=false;
+ if del<0 then @<Complement the |x| coordinates of the
+ cubic between |p| and~|q|@>;
+ t:=crossing_point(del1,del2,del3);
+ if t<fraction_one then
+ @<Subdivide the cubic with respect to $x'$, possibly twice@>;
+ end
+
+@ If |del1=del2=del3=0|, it's impossible to obey the title of this
+section. We just set |del=0| in that case.
+@^inner loop@>
+
+@<Scale up |del1|, |del2|, and |del3| for greater accuracy...@>=
+if del1<>0 then del:=del1
+else if del2<>0 then del:=del2
+else del:=del3;
+if del<>0 then
+ begin dmax:=abs(del1);
+ if abs(del2)>dmax then dmax:=abs(del2);
+ if abs(del3)>dmax then dmax:=abs(del3);
+ while dmax<fraction_half do
+ begin double(dmax); double(del1); double(del2); double(del3);
+ end;
+ end
+
+@ During the subdivision phases of |make_spec|, the |x_coord| and |y_coord|
+fields of node~|q| are not transformed to agree with the octant
+stated in |right_type(p)|; they remain consistent with |right_type(q)|.
+But |left_x(q)| and |left_y(q)| are governed by |right_type(p)|.
+
+@<Complement the |x| coordinates...@>=
+begin negate(x_coord(p)); negate(right_x(p));
+negate(left_x(q));@/
+negate(del1); negate(del2); negate(del3);@/
+negate(dest_x);
+right_type(p):=first_octant+negate_x;
+end
+
+@ When a cubic is split at a |fraction| value |t|, we obtain two cubics
+whose B\'ezier control points are obtained by a generalization of the
+bisection process: The formula
+`$z_k^{(j+1)}={1\over2}(z_k^{(j)}+z\k^{(j)})$' becomes
+`$z_k^{(j+1)}=t[z_k^{(j)},z\k^{(j)}]$'.
+
+It is convenient to define a \.{WEB} macro |t_of_the_way| such that
+|t_of_the_way(a)(b)| expands to |a-(a-b)*t|, i.e., to |t[a,b]|.
+
+If |0<=t<=1|, the quantity |t[a,b]| is always between |a| and~|b|, even in
+the presence of rounding errors. Our subroutines
+also obey the identity |t[a,b]+t[b,a]=a+b|.
+
+@d t_of_the_way_end(#)==#,t@=)@>
+@d t_of_the_way(#)==#-take_fraction@=(@>#-t_of_the_way_end
+
+@<Declare the procedure called |split_cubic|@>=
+procedure split_cubic(@!p:pointer;@!t:fraction;
+ @!xq,@!yq:scaled); {splits the cubic after |p|}
+var @!v:scaled; {an intermediate value}
+@!q,@!r:pointer; {for list manipulation}
+begin q:=link(p); r:=get_node(knot_node_size); link(p):=r; link(r):=q;@/
+left_type(r):=left_type(q); right_type(r):=right_type(p);@#
+v:=t_of_the_way(right_x(p))(left_x(q));
+right_x(p):=t_of_the_way(x_coord(p))(right_x(p));
+left_x(q):=t_of_the_way(left_x(q))(xq);
+left_x(r):=t_of_the_way(right_x(p))(v);
+right_x(r):=t_of_the_way(v)(left_x(q));
+x_coord(r):=t_of_the_way(left_x(r))(right_x(r));@#
+v:=t_of_the_way(right_y(p))(left_y(q));
+right_y(p):=t_of_the_way(y_coord(p))(right_y(p));
+left_y(q):=t_of_the_way(left_y(q))(yq);
+left_y(r):=t_of_the_way(right_y(p))(v);
+right_y(r):=t_of_the_way(v)(left_y(q));
+y_coord(r):=t_of_the_way(left_y(r))(right_y(r));
+end;
+
+@ Since $x'(t)$ is a quadratic equation, it can cross through zero
+at~most twice. When it does cross zero, we make doubly sure that the
+derivative is really zero at the splitting point, in case rounding errors
+have caused the split cubic to have an apparently nonzero derivative.
+We also make sure that the split cubic is monotonic.
+
+@<Subdivide the cubic with respect to $x'$, possibly twice@>=
+begin split_cubic(p,t,dest_x,dest_y); r:=link(p);
+if right_type(r)>negate_x then right_type(r):=first_octant
+else right_type(r):=first_octant+negate_x;
+if x_coord(r)<x_coord(p) then x_coord(r):=x_coord(p);
+left_x(r):=x_coord(r);
+if right_x(p)>x_coord(r) then right_x(p):=x_coord(r);
+ {we always have |x_coord(p)<=right_x(p)|}
+negate(x_coord(r)); right_x(r):=x_coord(r);
+negate(left_x(q)); negate(dest_x);@/
+del2:=t_of_the_way(del2)(del3);
+ {now |0,del2,del3| represent $x'$ on the remaining interval}
+if del2>0 then del2:=0;
+t:=crossing_point(0,-del2,-del3);
+if t<fraction_one then @<Subdivide the cubic a second time
+ with respect to $x'$@>
+else begin if x_coord(r)>dest_x then
+ begin x_coord(r):=dest_x; left_x(r):=-x_coord(r); right_x(r):=x_coord(r);
+ end;
+ if left_x(q)>dest_x then left_x(q):=dest_x
+ else if left_x(q)<x_coord(r) then left_x(q):=x_coord(r);
+ end;
+end
+
+@ @<Subdivide the cubic a second time with respect to $x'$@>=
+begin split_cubic(r,t,dest_x,dest_y); s:=link(r);
+if x_coord(s)<dest_x then x_coord(s):=dest_x;
+if x_coord(s)<x_coord(r) then x_coord(s):=x_coord(r);
+right_type(s):=right_type(p);
+left_x(s):=x_coord(s); {now |x_coord(r)=right_x(r)<=left_x(s)|}
+if left_x(q)<dest_x then left_x(q):=-dest_x
+else if left_x(q)>x_coord(s) then left_x(q):=-x_coord(s)
+else negate(left_x(q));
+negate(x_coord(s)); right_x(s):=x_coord(s);
+end
+
+@ The process of subdivision with respect to $y'$ is like that with respect
+to~$x'$, with the slight additional complication that two or three cubics
+might now appear between |p| and~|q|.
+
+@<Subdivide all cubics between |p| and |q| so that the results travel
+ toward the first quadrant...@>=
+pp:=p;
+repeat qq:=link(pp);
+abnegate(x_coord(qq),y_coord(qq),right_type(qq),right_type(pp));
+dest_x:=cur_x; dest_y:=cur_y;@/
+del1:=right_y(pp)-y_coord(pp); del2:=left_y(qq)-right_y(pp);
+del3:=dest_y-left_y(qq);
+@<Scale up |del1|, |del2|, and |del3| for greater accuracy;
+ also set |del| to the first nonzero element of |(del1,del2,del3)|@>;
+if del<>0 then {they weren't all zero}
+ begin if del<0 then @<Complement the |y| coordinates of the
+ cubic between |pp| and~|qq|@>;
+ t:=crossing_point(del1,del2,del3);
+ if t<fraction_one then
+ @<Subdivide the cubic with respect to $y'$, possibly twice@>;
+ end
+else @<Do any special actions needed when |y| is constant;
+ |return| or |goto continue| if a dead cubic from |p| to |q| is removed@>;
+pp:=qq;
+until pp=q;
+if constant_x then @<Correct the octant code in segments with decreasing |y|@>
+
+@ @<Complement the |y| coordinates...@>=
+begin negate(y_coord(pp)); negate(right_y(pp));
+negate(left_y(qq));@/
+negate(del1); negate(del2); negate(del3);@/
+negate(dest_y);
+right_type(pp):=right_type(pp)+negate_y;
+end
+
+@ @<Subdivide the cubic with respect to $y'$, possibly twice@>=
+begin split_cubic(pp,t,dest_x,dest_y); r:=link(pp);
+if right_type(r)>negate_y then right_type(r):=right_type(r)-negate_y
+else right_type(r):=right_type(r)+negate_y;
+if y_coord(r)<y_coord(pp) then y_coord(r):=y_coord(pp);
+left_y(r):=y_coord(r);
+if right_y(pp)>y_coord(r) then right_y(pp):=y_coord(r);
+ {we always have |y_coord(pp)<=right_y(pp)|}
+negate(y_coord(r)); right_y(r):=y_coord(r);
+negate(left_y(qq)); negate(dest_y);@/
+if x_coord(r)<x_coord(pp) then x_coord(r):=x_coord(pp)
+else if x_coord(r)>dest_x then x_coord(r):=dest_x;
+if left_x(r)>x_coord(r) then
+ begin left_x(r):=x_coord(r);
+ if right_x(pp)>x_coord(r) then right_x(pp):=x_coord(r);
+ end;
+if right_x(r)<x_coord(r) then
+ begin right_x(r):=x_coord(r);
+ if left_x(qq)<x_coord(r) then left_x(qq):=x_coord(r);
+ end;
+del2:=t_of_the_way(del2)(del3);
+ {now |0,del2,del3| represent $y'$ on the remaining interval}
+if del2>0 then del2:=0;
+t:=crossing_point(0,-del2,-del3);
+if t<fraction_one then @<Subdivide the cubic a second time
+ with respect to $y'$@>
+else begin if y_coord(r)>dest_y then
+ begin y_coord(r):=dest_y; left_y(r):=-y_coord(r); right_y(r):=y_coord(r);
+ end;
+ if left_y(qq)>dest_y then left_y(qq):=dest_y
+ else if left_y(qq)<y_coord(r) then left_y(qq):=y_coord(r);
+ end;
+end
+
+@ @<Subdivide the cubic a second time with respect to $y'$@>=
+begin split_cubic(r,t,dest_x,dest_y); s:=link(r);@/
+if y_coord(s)<dest_y then y_coord(s):=dest_y;
+if y_coord(s)<y_coord(r) then y_coord(s):=y_coord(r);
+right_type(s):=right_type(pp);
+left_y(s):=y_coord(s); {now |y_coord(r)=right_y(r)<=left_y(s)|}
+if left_y(qq)<dest_y then left_y(qq):=-dest_y
+else if left_y(qq)>y_coord(s) then left_y(qq):=-y_coord(s)
+else negate(left_y(qq));
+negate(y_coord(s)); right_y(s):=y_coord(s);
+if x_coord(s)<x_coord(r) then x_coord(s):=x_coord(r)
+else if x_coord(s)>dest_x then x_coord(s):=dest_x;
+if left_x(s)>x_coord(s) then
+ begin left_x(s):=x_coord(s);
+ if right_x(r)>x_coord(s) then right_x(r):=x_coord(s);
+ end;
+if right_x(s)<x_coord(s) then
+ begin right_x(s):=x_coord(s);
+ if left_x(qq)<x_coord(s) then left_x(qq):=x_coord(s);
+ end;
+end
+
+@ If the cubic is constant in $y$ and increasing in $x$, we have classified
+it as traveling in the first octant. If the cubic is constant
+in~$y$ and decreasing in~$x$, it is desirable to classify it as traveling
+in the fifth octant (not the fourth), because autorounding will be consistent
+with respect to doublepaths only if the octant number changes by four when
+the path is reversed. Therefore we negate the $y$~coordinates
+when they are constant but the curve is decreasing in~$x$; this gives
+the desired result except in pathological paths.
+
+If the cubic is ``dead,'' i.e., constant in both |x| and |y|, we remove
+it unless it is the only cubic in the entire path. We |goto continue|
+if it wasn't the final cubic, so that the test |p=cur_spec| does not
+falsely imply that all cubics have been processed.
+
+@<Do any special actions needed when |y| is constant...@>=
+if constant_x then {|p=pp|, |q=qq|, and the cubic is dead}
+ begin if q<>p then
+ begin remove_cubic(p); {remove the dead cycle and recycle node |q|}
+ if cur_spec<>q then goto continue
+ else begin cur_spec:=p; return;
+ end; {the final cubic was dead and is gone}
+ end;
+ end
+else if not odd(right_type(pp)) then {the $x$ coordinates were negated}
+ @<Complement the |y| coordinates...@>
+
+@ A similar correction to octant codes deserves to be made when |x| is
+constant and |y| is decreasing.
+
+@<Correct the octant code in segments with decreasing |y|@>=
+begin pp:=p;
+repeat qq:=link(pp);
+if right_type(pp)>negate_y then {the $y$ coordinates were negated}
+ begin right_type(pp):=right_type(pp)+negate_x;
+ negate(x_coord(pp)); negate(right_x(pp)); negate(left_x(qq));
+ end;
+pp:=qq;
+until pp=q;
+end
+
+@ Finally, the process of subdividing to make $x'\G y'$ is like the other
+two subdivisions, with a few new twists. We skew the coordinates at this time.
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure octant_subdivide;
+var @!p,@!q,@!r,@!s:pointer; {for traversing the lists}
+@!del1,@!del2,@!del3,@!del,@!dmax:scaled; {proportional to the control
+ points of a quadratic derived from a cubic}
+@!t:fraction; {where a quadratic crosses zero}
+@!dest_x,@!dest_y:scaled; {final values of |x| and |y| in the current cubic}
+begin p:=cur_spec;
+repeat q:=link(p);@/
+x_coord(p):=x_coord(p)-y_coord(p);
+right_x(p):=right_x(p)-right_y(p);
+left_x(q):=left_x(q)-left_y(q);@/
+@<Subdivide the cubic between |p| and |q| so that the results travel
+ toward the first octant@>;
+p:=q;
+until p=cur_spec;
+end;
+
+@ @<Subdivide the cubic between |p| and |q| so that the results travel
+ toward the first octant@>=
+@<Set up the variables |(del1,del2,del3)| to represent $x'-y'$@>;
+@<Scale up |del1|, |del2|, and |del3| for greater accuracy;
+ also set |del| to the first nonzero element of |(del1,del2,del3)|@>;
+if del<>0 then {they weren't all zero}
+ begin if del<0 then @<Swap the |x| and |y| coordinates of the
+ cubic between |p| and~|q|@>;
+ t:=crossing_point(del1,del2,del3);
+ if t<fraction_one then
+ @<Subdivide the cubic with respect to $x'-y'$, possibly twice@>;
+ end
+
+@ @<Set up the variables |(del1,del2,del3)| to represent $x'-y'$@>=
+if q=cur_spec then
+ begin unskew(x_coord(q),y_coord(q),right_type(q));
+ skew(cur_x,cur_y,right_type(p)); dest_x:=cur_x; dest_y:=cur_y;
+ end
+else begin abnegate(x_coord(q),y_coord(q),right_type(q),right_type(p));
+ dest_x:=cur_x-cur_y; dest_y:=cur_y;
+ end;
+del1:=right_x(p)-x_coord(p); del2:=left_x(q)-right_x(p);
+del3:=dest_x-left_x(q)
+
+@ The swapping here doesn't simply interchange |x| and |y| values,
+because the coordinates are skewed. It turns out that this is easier
+than ordinary swapping, because it can be done in two assignment statements
+rather than three.
+
+@ @<Swap the |x| and |y| coordinates...@>=
+begin y_coord(p):=x_coord(p)+y_coord(p); negate(x_coord(p));@/
+right_y(p):=right_x(p)+right_y(p); negate(right_x(p));@/
+left_y(q):=left_x(q)+left_y(q); negate(left_x(q));@/
+negate(del1); negate(del2); negate(del3);@/
+dest_y:=dest_x+dest_y; negate(dest_x);@/
+right_type(p):=right_type(p)+switch_x_and_y;
+end
+
+@ A somewhat tedious case analysis is carried out here to make sure that
+nasty rounding errors don't destroy our assumptions of monotonicity.
+
+@<Subdivide the cubic with respect to $x'-y'$, possibly twice@>=
+begin split_cubic(p,t,dest_x,dest_y); r:=link(p);
+if right_type(r)>switch_x_and_y then right_type(r):=right_type(r)-switch_x_and_y
+else right_type(r):=right_type(r)+switch_x_and_y;
+if y_coord(r)<y_coord(p) then y_coord(r):=y_coord(p)
+else if y_coord(r)>dest_y then y_coord(r):=dest_y;
+if x_coord(p)+y_coord(r)>dest_x+dest_y then
+ y_coord(r):=dest_x+dest_y-x_coord(p);
+if left_y(r)>y_coord(r) then
+ begin left_y(r):=y_coord(r);
+ if right_y(p)>y_coord(r) then right_y(p):=y_coord(r);
+ end;
+if right_y(r)<y_coord(r) then
+ begin right_y(r):=y_coord(r);
+ if left_y(q)<y_coord(r) then left_y(q):=y_coord(r);
+ end;
+if x_coord(r)<x_coord(p) then x_coord(r):=x_coord(p)
+else if x_coord(r)+y_coord(r)>dest_x+dest_y then
+ x_coord(r):=dest_x+dest_y-y_coord(r);
+left_x(r):=x_coord(r);
+if right_x(p)>x_coord(r) then right_x(p):=x_coord(r);
+ {we always have |x_coord(p)<=right_x(p)|}
+y_coord(r):=y_coord(r)+x_coord(r); right_y(r):=right_y(r)+x_coord(r);@/
+negate(x_coord(r)); right_x(r):=x_coord(r);@/
+left_y(q):=left_y(q)+left_x(q); negate(left_x(q));@/
+dest_y:=dest_y+dest_x; negate(dest_x);
+if right_y(r)<y_coord(r) then
+ begin right_y(r):=y_coord(r);
+ if left_y(q)<y_coord(r) then left_y(q):=y_coord(r);
+ end;
+del2:=t_of_the_way(del2)(del3);
+ {now |0,del2,del3| represent $x'-y'$ on the remaining interval}
+if del2>0 then del2:=0;
+t:=crossing_point(0,-del2,-del3);
+if t<fraction_one then
+ @<Subdivide the cubic a second time with respect to $x'-y'$@>
+else begin if x_coord(r)>dest_x then
+ begin x_coord(r):=dest_x; left_x(r):=-x_coord(r); right_x(r):=x_coord(r);
+ end;
+ if left_x(q)>dest_x then left_x(q):=dest_x
+ else if left_x(q)<x_coord(r) then left_x(q):=x_coord(r);
+ end;
+end
+
+@ @<Subdivide the cubic a second time with respect to $x'-y'$@>=
+begin split_cubic(r,t,dest_x,dest_y); s:=link(r);@/
+if y_coord(s)<y_coord(r) then y_coord(s):=y_coord(r)
+else if y_coord(s)>dest_y then y_coord(s):=dest_y;
+if x_coord(r)+y_coord(s)>dest_x+dest_y then
+ y_coord(s):=dest_x+dest_y-x_coord(r);
+if left_y(s)>y_coord(s) then
+ begin left_y(s):=y_coord(s);
+ if right_y(r)>y_coord(s) then right_y(r):=y_coord(s);
+ end;
+if right_y(s)<y_coord(s) then
+ begin right_y(s):=y_coord(s);
+ if left_y(q)<y_coord(s) then left_y(q):=y_coord(s);
+ end;
+if x_coord(s)+y_coord(s)>dest_x+dest_y then x_coord(s):=dest_x+dest_y-y_coord(s)
+else begin if x_coord(s)<dest_x then x_coord(s):=dest_x;
+ if x_coord(s)<x_coord(r) then x_coord(s):=x_coord(r);
+ end;
+right_type(s):=right_type(p);
+left_x(s):=x_coord(s); {now |x_coord(r)=right_x(r)<=left_x(s)|}
+if left_x(q)<dest_x then
+ begin left_y(q):=left_y(q)+dest_x; left_x(q):=-dest_x;@+end
+else if left_x(q)>x_coord(s) then
+ begin left_y(q):=left_y(q)+x_coord(s); left_x(q):=-x_coord(s);@+end
+else begin left_y(q):=left_y(q)+left_x(q); negate(left_x(q));@+end;
+y_coord(s):=y_coord(s)+x_coord(s); right_y(s):=right_y(s)+x_coord(s);@/
+negate(x_coord(s)); right_x(s):=x_coord(s);@/
+if right_y(s)<y_coord(s) then
+ begin right_y(s):=y_coord(s);
+ if left_y(q)<y_coord(s) then left_y(q):=y_coord(s);
+ end;
+end
+
+@ It's time now to consider ``autorounding,'' which tries to make horizontal,
+vertical, and diagonal tangents occur at places that will produce appropriate
+images after the curve is digitized.
+
+The first job is to fix things so that |x(t)| plus the horizontal pen offset
+is an integer multiple of the
+current ``granularity'' when the derivative $x'(t)$ crosses through zero.
+The given cyclic path contains regions where $x'(t)\G0$ and regions
+where $x'(t)\L0$. The |quadrant_subdivide| routine is called into action
+before any of the path coordinates have been skewed, but some of them
+may have been negated. In regions where $x'(t)\G0$ we have |right_type=
+first_octant| or |right_type=eighth_octant|; in regions where $x'(t)\L0$,
+we have |right_type=fifth_octant| or |right_type=fourth_octant|.
+
+Within any such region the transformed $x$ values increase monotonically
+from, say, $x_0$ to~$x_1$. We want to modify things by applying a linear
+transformation to all $x$ coordinates in the region, after which
+the $x$ values will increase monotonically from round$(x_0)$ to round$(x_1)$.
+
+This rounding scheme sounds quite simple, and it usually is. But several
+complications can arise that might make the task more difficult. In the
+first place, autorounding is inappropriate at cusps where $x'$ jumps
+discontinuously past zero without ever being zero. In the second place,
+the current pen might be unsymmetric in such a way that $x$ coordinates
+should round differently in different parts of the curve.
+These considerations imply that round$(x_0)$ might be greater
+than round$(x_1)$, even though $x_0\L x_1$; in such cases we do not want
+to carry out the linear transformation. Furthermore, it's possible to have
+round$(x_1)-\hbox{round} (x_0)$ positive but much greater than $x_1-x_0$;
+then the transformation might distort the curve drastically, and again we
+want to avoid it. Finally, the rounded points must be consistent between
+adjacent regions, hence we can't transform one region without knowing
+about its neighbors.
+
+To handle all these complications, we must first look at the whole
+cycle and choose rounded $x$ values that are ``safe.'' The following
+procedure does this: Given $m$~values $(b_0,b_1,\ldots,b_{m-1})$ before
+rounding and $m$~corresponding values $(a_0,a_1,\ldots,a_{m-1})$ that would
+be desirable after rounding, the |make_safe| routine sets $a$'s to $b$'s
+if necessary so that $0\L(a\k-a_k)/(b\k-b_k)\L2$ afterwards. It is
+symmetric under cyclic permutation, reversal, and/or negation of the inputs.
+(Instead of |a|, |b|, and~|m|, the program uses the names |after|,
+|before|, and |cur_rounding_ptr|.)
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure make_safe;
+var @!k:0..max_wiggle; {runs through the list of inputs}
+@!all_safe:boolean; {does everything look OK so far?}
+@!next_a:scaled; {|after[k]| before it might have changed}
+@!delta_a,@!delta_b:scaled; {|after[k+1]-after[k]| and |before[k+1]-before[k]|}
+begin before[cur_rounding_ptr]:=before[0]; {wrap around}
+node_to_round[cur_rounding_ptr]:=node_to_round[0];
+repeat after[cur_rounding_ptr]:=after[0]; all_safe:=true; next_a:=after[0];
+for k:=0 to cur_rounding_ptr-1 do
+ begin delta_b:=before[k+1]-before[k];
+ if delta_b>=0 then delta_a:=after[k+1]-next_a
+ else delta_a:=next_a-after[k+1];
+ next_a:=after[k+1];
+ if (delta_a<0)or(delta_a>abs(delta_b+delta_b)) then
+ begin all_safe:=false; after[k]:=before[k];
+ if k=cur_rounding_ptr-1 then after[0]:=before[0]
+ else after[k+1]:=before[k+1];
+ end;
+ end;
+until all_safe;
+end;
+
+@ The global arrays used by |make_safe| are accompanied by an array of
+pointers into the current knot list.
+
+@<Glob...@>=
+@!before,@!after:array[0..max_wiggle] of scaled; {data for |make_safe|}
+@!node_to_round:array[0..max_wiggle] of pointer; {reference back to the path}
+@!cur_rounding_ptr:0..max_wiggle; {how many are being used}
+@!max_rounding_ptr:0..max_wiggle; {how many have been used}
+
+@ @<Set init...@>=
+max_rounding_ptr:=0;
+
+@ New entries go into the tables via the |before_and_after| routine:
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure before_and_after(@!b,@!a:scaled;@!p:pointer);
+begin if cur_rounding_ptr=max_rounding_ptr then
+ if max_rounding_ptr<max_wiggle then incr(max_rounding_ptr)
+ else overflow("rounding table size",max_wiggle);
+@:METAFONT capacity exceeded rounding table size}{\quad rounding table size@>
+after[cur_rounding_ptr]:=a; before[cur_rounding_ptr]:=b;
+node_to_round[cur_rounding_ptr]:=p; incr(cur_rounding_ptr);
+end;
+
+@ A global variable called |cur_gran| is used instead of |internal[
+granularity]|, because we want to work with a number that's guaranteed to
+be positive.
+
+@<Glob...@>=
+@!cur_gran:scaled; {the current granularity (which normally is |unity|)}
+
+@ The |good_val| function computes a number |a| that's as close as
+possible to~|b|, with the property that |a+o| is a multiple of
+|cur_gran|.
+
+If we assume that |cur_gran| is even (since it will in fact be a multiple
+of |unity| in all reasonable applications), we have the identity
+|good_val(-b-1,-o)=-good_val(b,o)|.
+
+@<Declare subroutines needed by |make_spec|@>=
+function good_val(@!b,@!o:scaled):scaled;
+var @!a:scaled; {accumulator}
+begin a:=b+o;
+if a>=0 then a:=a-(a mod cur_gran)-o
+else a:=a+((-(a+1)) mod cur_gran)-cur_gran+1-o;
+if b-a<a+cur_gran-b then good_val:=a
+else good_val:=a+cur_gran;
+end;
+
+@ When we're rounding a doublepath, we might need to compromise between
+two opposing tendencies, if the pen thickness is not a multiple of the
+granularity. The following ``compromise'' adjustment, suggested by
+John Hobby, finds the best way out of the dilemma. (Only the value
+@^Hobby, John Douglas@>
+modulo |cur_gran| is relevant in our applications, so the result turns
+out to be essentially symmetric in |u| and~|v|.)
+
+@<Declare subroutines needed by |make_spec|@>=
+function compromise(@!u,@!v:scaled):scaled;
+begin compromise:=half(good_val(u+u,-u-v));
+end;
+
+@ Here, then, is the procedure that rounds $x$ coordinates as described;
+it does the same for $y$ coordinates too, independently.
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure xy_round;
+var @!p,@!q:pointer; {list manipulation registers}
+@!b,@!a:scaled; {before and after values}
+@!pen_edge:scaled; {offset that governs rounding}
+@!alpha:fraction; {coefficient of linear transformation}
+begin cur_gran:=abs(internal[granularity]);
+if cur_gran=0 then cur_gran:=unity;
+p:=cur_spec; cur_rounding_ptr:=0;
+repeat q:=link(p);
+@<If node |q| is a transition point for |x| coordinates,
+ compute and save its before-and-after coordinates@>;
+p:=q;
+until p=cur_spec;
+if cur_rounding_ptr>0 then @<Transform the |x| coordinates@>;
+p:=cur_spec; cur_rounding_ptr:=0;
+repeat q:=link(p);
+@<If node |q| is a transition point for |y| coordinates,
+ compute and save its before-and-after coordinates@>;
+p:=q;
+until p=cur_spec;
+if cur_rounding_ptr>0 then @<Transform the |y| coordinates@>;
+end;
+
+@ When |x| has been negated, the |octant| codes are even. We allow
+for an error of up to .01 pixel (i.e., 655 |scaled| units) in the
+derivative calculations at transition nodes.
+
+@<If node |q| is a transition point for |x| coordinates...@>=
+if odd(right_type(p))<>odd(right_type(q)) then
+ begin if odd(right_type(q)) then b:=x_coord(q)@+else b:=-x_coord(q);
+ if (abs(x_coord(q)-right_x(q))<655)or@|
+ (abs(x_coord(q)+left_x(q))<655) then
+ @<Compute before-and-after |x| values based on the current pen@>
+ else a:=b;
+ if abs(a)>max_allowed then
+ if a>0 then a:=max_allowed@+else a:=-max_allowed;
+ before_and_after(b,a,q);
+ end
+
+@ When we study the data representation for pens, we'll learn that the
+|x|~coordinate of the current pen's west edge is
+$$\hbox{|y_coord(link(cur_pen+seventh_octant))|},$$
+and that there are similar ways to address other important offsets.
+
+@d north_edge(#)==y_coord(link(#+fourth_octant))
+@d south_edge(#)==y_coord(link(#+first_octant))
+@d east_edge(#)==y_coord(link(#+second_octant))
+@d west_edge(#)==y_coord(link(#+seventh_octant))
+
+@<Compute before-and-after |x| values based on the current pen@>=
+begin if cur_pen=null_pen then pen_edge:=0
+else if cur_path_type=double_path_code then
+ pen_edge:=compromise(east_edge(cur_pen),west_edge(cur_pen))
+else if odd(right_type(q)) then pen_edge:=west_edge(cur_pen)
+else pen_edge:=east_edge(cur_pen);
+a:=good_val(b,pen_edge);
+end
+
+@ The monotone transformation computed here with fixed-point arithmetic is
+guaranteed to take consecutive |before| values $(b,b')$ into consecutive
+|after| values $(a,a')$, even in the presence of rounding errors,
+as long as $\vert b-b'\vert<2^{28}$.
+
+@<Transform the |x| coordinates@>=
+begin make_safe;
+repeat decr(cur_rounding_ptr);
+if (after[cur_rounding_ptr]<>before[cur_rounding_ptr])or@|
+ (after[cur_rounding_ptr+1]<>before[cur_rounding_ptr+1]) then
+ begin p:=node_to_round[cur_rounding_ptr];
+ if odd(right_type(p)) then
+ begin b:=before[cur_rounding_ptr]; a:=after[cur_rounding_ptr];
+ end
+ else begin b:=-before[cur_rounding_ptr]; a:=-after[cur_rounding_ptr];
+ end;
+ if before[cur_rounding_ptr]=before[cur_rounding_ptr+1] then
+ alpha:=fraction_one
+ else alpha:=make_fraction(after[cur_rounding_ptr+1]-after[cur_rounding_ptr],@|
+ before[cur_rounding_ptr+1]-before[cur_rounding_ptr]);
+ repeat x_coord(p):=take_fraction(alpha,x_coord(p)-b)+a;
+ right_x(p):=take_fraction(alpha,right_x(p)-b)+a;
+ p:=link(p); left_x(p):=take_fraction(alpha,left_x(p)-b)+a;
+ until p=node_to_round[cur_rounding_ptr+1];
+ end;
+until cur_rounding_ptr=0;
+end
+
+@ When |y| has been negated, the |octant| codes are |>negate_y|. Otherwise
+these routines are essentially identical to the routines for |x| coordinates
+that we have just seen.
+
+@<If node |q| is a transition point for |y| coordinates...@>=
+if (right_type(p)>negate_y)<>(right_type(q)>negate_y) then
+ begin if right_type(q)<=negate_y then b:=y_coord(q)@+else b:=-y_coord(q);
+ if (abs(y_coord(q)-right_y(q))<655)or@|
+ (abs(y_coord(q)+left_y(q))<655) then
+ @<Compute before-and-after |y| values based on the current pen@>
+ else a:=b;
+ if abs(a)>max_allowed then
+ if a>0 then a:=max_allowed@+else a:=-max_allowed;
+ before_and_after(b,a,q);
+ end
+
+@ @<Compute before-and-after |y| values based on the current pen@>=
+begin if cur_pen=null_pen then pen_edge:=0
+else if cur_path_type=double_path_code then
+ pen_edge:=compromise(north_edge(cur_pen),south_edge(cur_pen))
+else if right_type(q)<=negate_y then pen_edge:=south_edge(cur_pen)
+else pen_edge:=north_edge(cur_pen);
+a:=good_val(b,pen_edge);
+end
+
+@ @<Transform the |y| coordinates@>=
+begin make_safe;
+repeat decr(cur_rounding_ptr);
+if (after[cur_rounding_ptr]<>before[cur_rounding_ptr])or@|
+ (after[cur_rounding_ptr+1]<>before[cur_rounding_ptr+1]) then
+ begin p:=node_to_round[cur_rounding_ptr];
+ if right_type(p)<=negate_y then
+ begin b:=before[cur_rounding_ptr]; a:=after[cur_rounding_ptr];
+ end
+ else begin b:=-before[cur_rounding_ptr]; a:=-after[cur_rounding_ptr];
+ end;
+ if before[cur_rounding_ptr]=before[cur_rounding_ptr+1] then
+ alpha:=fraction_one
+ else alpha:=make_fraction(after[cur_rounding_ptr+1]-after[cur_rounding_ptr],@|
+ before[cur_rounding_ptr+1]-before[cur_rounding_ptr]);
+ repeat y_coord(p):=take_fraction(alpha,y_coord(p)-b)+a;
+ right_y(p):=take_fraction(alpha,right_y(p)-b)+a;
+ p:=link(p); left_y(p):=take_fraction(alpha,left_y(p)-b)+a;
+ until p=node_to_round[cur_rounding_ptr+1];
+ end;
+until cur_rounding_ptr=0;
+end
+
+@ Rounding at diagonal tangents takes place after the subdivision into
+octants is complete, hence after the coordinates have been skewed.
+The details are somewhat tricky, because we want to round to points
+whose skewed coordinates are halfway between integer multiples of
+the granularity. Furthermore, both coordinates change when they are
+rounded; this means we need a generalization of the |make_safe| routine,
+ensuring safety in both |x| and |y|.
+
+In spite of these extra complications, we can take comfort in the fact
+that the basic structure of the routine is the same as before.
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure diag_round;
+var @!p,@!q,@!pp:pointer; {list manipulation registers}
+@!b,@!a,@!bb,@!aa,@!d,@!c,@!dd,@!cc:scaled; {before and after values}
+@!pen_edge:scaled; {offset that governs rounding}
+@!alpha,@!beta:fraction; {coefficients of linear transformation}
+@!next_a:scaled; {|after[k]| before it might have changed}
+@!all_safe:boolean; {does everything look OK so far?}
+@!k:0..max_wiggle; {runs through before-and-after values}
+@!first_x,@!first_y:scaled; {coordinates before rounding}
+begin p:=cur_spec; cur_rounding_ptr:=0;
+repeat q:=link(p);
+@<If node |q| is a transition point between octants,
+ compute and save its before-and-after coordinates@>;
+p:=q;
+until p=cur_spec;
+if cur_rounding_ptr>0 then @<Transform the skewed coordinates@>;
+end;
+
+@ We negate the skewed |x| coordinates in the before-and-after table when
+the octant code is greater than |switch_x_and_y|.
+
+@<If node |q| is a transition point between octants...@>=
+if right_type(p)<>right_type(q) then
+ begin if right_type(q)>switch_x_and_y then b:=-x_coord(q)
+ else b:=x_coord(q);
+ if abs(right_type(q)-right_type(p))=switch_x_and_y then
+ if (abs(x_coord(q)-right_x(q))<655)or(abs(x_coord(q)+left_x(q))<655) then
+ @<Compute a good coordinate at a diagonal transition@>
+ else a:=b
+ else a:=b;
+ before_and_after(b,a,q);
+ end
+
+@ In octants whose code number is even, $x$~has been
+negated; we want to round ambiguous cases downward instead of upward,
+so that the rounding will be consistent with octants whose code
+number is odd. This downward bias can be achieved by
+subtracting~1 from the first argument of |good_val|.
+
+@d diag_offset(#)==x_coord(knil(link(cur_pen+#)))
+
+@<Compute a good coordinate at a diagonal transition@>=
+begin if cur_pen=null_pen then pen_edge:=0
+else if cur_path_type=double_path_code then @<Compute a compromise |pen_edge|@>
+else if right_type(q)<=switch_x_and_y then pen_edge:=diag_offset(right_type(q))
+else pen_edge:=-diag_offset(right_type(q));
+if odd(right_type(q)) then a:=good_val(b,pen_edge+half(cur_gran))
+else a:=good_val(b-1,pen_edge+half(cur_gran));
+end
+
+@ (It seems a shame to compute these compromise offsets repeatedly. The
+author would have stored them directly in the pen data structure, if the
+granularity had been constant.)
+
+@<Compute a compromise...@>=
+case right_type(q) of
+first_octant,second_octant:pen_edge:=compromise(diag_offset(first_octant),@|
+ -diag_offset(fifth_octant));
+fifth_octant,sixth_octant:pen_edge:=-compromise(diag_offset(first_octant),@|
+ -diag_offset(fifth_octant));
+third_octant,fourth_octant:pen_edge:=compromise(diag_offset(fourth_octant),@|
+ -diag_offset(eighth_octant));
+seventh_octant,eighth_octant:pen_edge:=-compromise(diag_offset(fourth_octant),@|
+ -diag_offset(eighth_octant));
+end {there are no other cases}
+
+@ @<Transform the skewed coordinates@>=
+begin p:=node_to_round[0]; first_x:=x_coord(p); first_y:=y_coord(p);
+@<Make sure that all the diagonal roundings are safe@>;
+for k:=0 to cur_rounding_ptr-1 do
+ begin a:=after[k]; b:=before[k];
+ aa:=after[k+1]; bb:=before[k+1];
+ if (a<>b)or(aa<>bb) then
+ begin p:=node_to_round[k]; pp:=node_to_round[k+1];
+ @<Determine the before-and-after values of both coordinates@>;
+ if b=bb then alpha:=fraction_one
+ else alpha:=make_fraction(aa-a,bb-b);
+ if d=dd then beta:=fraction_one
+ else beta:=make_fraction(cc-c,dd-d);
+ repeat x_coord(p):=take_fraction(alpha,x_coord(p)-b)+a;
+ y_coord(p):=take_fraction(beta,y_coord(p)-d)+c;
+ right_x(p):=take_fraction(alpha,right_x(p)-b)+a;
+ right_y(p):=take_fraction(beta,right_y(p)-d)+c;
+ p:=link(p); left_x(p):=take_fraction(alpha,left_x(p)-b)+a;
+ left_y(p):=take_fraction(beta,left_y(p)-d)+c;
+ until p=pp;
+ end;
+ end;
+end
+
+@ In node |p|, the coordinates |(b,d)| will be rounded to |(a,c)|;
+in node |pp|, the coordinates |(bb,dd)| will be rounded to |(aa,cc)|.
+(We transform the values from node |pp| so that they agree with the
+conventions of node |p|.)
+
+If |aa<>bb|, we know that |abs(right_type(p)-right_type(pp))=switch_x_and_y|.
+
+@<Determine the before-and-after values of both coordinates@>=
+if aa=bb then
+ begin if pp=node_to_round[0] then
+ unskew(first_x,first_y,right_type(pp))
+ else unskew(x_coord(pp),y_coord(pp),right_type(pp));
+ skew(cur_x,cur_y,right_type(p));
+ bb:=cur_x; aa:=bb; dd:=cur_y; cc:=dd;
+ if right_type(p)>switch_x_and_y then
+ begin b:=-b; a:=-a;
+ end;
+ end
+else begin if right_type(p)>switch_x_and_y then
+ begin bb:=-bb; aa:=-aa; b:=-b; a:=-a;
+ end;
+ if pp=node_to_round[0] then dd:=first_y-bb@+else dd:=y_coord(pp)-bb;
+ if odd(aa-bb) then
+ if right_type(p)>switch_x_and_y then cc:=dd-half(aa-bb+1)
+ else cc:=dd-half(aa-bb-1)
+ else cc:=dd-half(aa-bb);
+ end;
+d:=y_coord(p);
+if odd(a-b) then
+ if right_type(p)>switch_x_and_y then c:=d-half(a-b-1)
+ else c:=d-half(a-b+1)
+else c:=d-half(a-b)
+
+@ @<Make sure that all the diagonal roundings are safe@>=
+before[cur_rounding_ptr]:=before[0]; {cf.~|make_safe|}
+node_to_round[cur_rounding_ptr]:=node_to_round[0];
+repeat after[cur_rounding_ptr]:=after[0]; all_safe:=true; next_a:=after[0];
+for k:=0 to cur_rounding_ptr-1 do
+ begin a:=next_a; b:=before[k]; next_a:=after[k+1];
+ aa:=next_a; bb:=before[k+1];
+ if (a<>b)or(aa<>bb) then
+ begin p:=node_to_round[k]; pp:=node_to_round[k+1];
+ @<Determine the before-and-after values of both coordinates@>;
+ if (aa<a)or(cc<c)or(aa-a>2*(bb-b))or(cc-c>2*(dd-d)) then
+ begin all_safe:=false; after[k]:=before[k];
+ if k=cur_rounding_ptr-1 then after[0]:=before[0]
+ else after[k+1]:=before[k+1];
+ end;
+ end;
+ end;
+until all_safe
+
+@ Here we get rid of ``dead'' cubics, i.e., polynomials that don't move at
+all when |t|~changes, since the subdivision process might have introduced
+such things. If the cycle reduces to a single point, however, we are left
+with a single dead cubic that will not be removed until later.
+
+@<Remove dead cubics@>=
+p:=cur_spec;
+repeat continue: q:=link(p);
+if p<>q then
+ begin if x_coord(p)=right_x(p) then
+ if y_coord(p)=right_y(p) then
+ if x_coord(p)=left_x(q) then
+ if y_coord(p)=left_y(q) then
+ begin unskew(x_coord(q),y_coord(q),right_type(q));
+ skew(cur_x,cur_y,right_type(p));
+ if x_coord(p)=cur_x then if y_coord(p)=cur_y then
+ begin remove_cubic(p); {remove the cubic following |p|}
+ if q<>cur_spec then goto continue;
+ cur_spec:=p; q:=p;
+ end;
+ end;
+ end;
+p:=q;
+until p=cur_spec;
+
+@ Finally we come to the last steps of |make_spec|, when boundary nodes
+are inserted between cubics that move in different octants. The main
+complication remaining arises from consecutive cubics whose octants
+are not adjacent; we should insert more than one octant boundary
+at such sharp turns, so that the envelope-forming routine will work.
+
+For this purpose, conversion tables between numeric and Gray codes for
+octants are desirable.
+
+@<Glob...@>=
+@!octant_number:array[first_octant..sixth_octant] of 1..8;
+@!octant_code:array[1..8] of first_octant..sixth_octant;
+
+@ @<Set init...@>=
+octant_code[1]:=first_octant;
+octant_code[2]:=second_octant;
+octant_code[3]:=third_octant;
+octant_code[4]:=fourth_octant;
+octant_code[5]:=fifth_octant;
+octant_code[6]:=sixth_octant;
+octant_code[7]:=seventh_octant;
+octant_code[8]:=eighth_octant;
+for k:=1 to 8 do octant_number[octant_code[k]]:=k;
+
+@ The main loop for boundary insertion deals with three consecutive
+nodes |p,q,r|.
+
+@<Insert octant boundaries and compute the turning number@>=
+turning_number:=0;
+p:=cur_spec; q:=link(p);
+repeat r:=link(q);
+if (right_type(p)<>right_type(q))or(q=r) then
+ @<Insert one or more octant boundary nodes just before~|q|@>;
+p:=q; q:=r;
+until p=cur_spec;
+
+@ The |new_boundary| subroutine comes in handy at this point. It inserts
+a new boundary node just after a given node |p|, using a given octant code
+to transform the new node's coordinates. The ``transition'' fields are
+not computed here.
+
+@<Declare subroutines needed by |make_spec|@>=
+procedure new_boundary(@!p:pointer;@!octant:small_number);
+var @!q,@!r:pointer; {for list manipulation}
+begin q:=link(p); {we assume that |right_type(q)<>endpoint|}
+r:=get_node(knot_node_size); link(r):=q; link(p):=r;
+left_type(r):=left_type(q); {but possibly |left_type(q)=endpoint|}
+left_x(r):=left_x(q); left_y(r):=left_y(q);
+right_type(r):=endpoint; left_type(q):=endpoint;
+right_octant(r):=octant; left_octant(q):=right_type(q);
+unskew(x_coord(q),y_coord(q),right_type(q));
+skew(cur_x,cur_y,octant); x_coord(r):=cur_x; y_coord(r):=cur_y;
+end;
+
+@ The case |q=r| occurs if and only if |p=q=r=cur_spec|, when we want to turn
+$360^\circ$ in eight steps and then remove a solitary dead cubic.
+The program below happens to work in that case, but the reader isn't
+expected to understand why.
+
+@<Insert one or more octant boundary nodes just before~|q|@>=
+begin new_boundary(p,right_type(p)); s:=link(p);
+o1:=octant_number[right_type(p)]; o2:=octant_number[right_type(q)];
+case o2-o1 of
+1,-7,7,-1: goto done;
+2,-6: clockwise:=false;
+3,-5,4,-4,5,-3: @<Decide whether or not to go clockwise@>;
+6,-2: clockwise:=true;
+0:clockwise:=rev_turns;
+end; {there are no other cases}
+@<Insert additional boundary nodes, then |goto done|@>;
+done: if q=r then
+ begin q:=link(q); r:=q; p:=s; link(s):=q; left_octant(q):=right_octant(q);
+ left_type(q):=endpoint; free_node(cur_spec,knot_node_size); cur_spec:=q;
+ end;
+@<Fix up the transition fields and adjust the turning number@>;
+end
+
+@ @<Other local variables for |make_spec|@>=
+@!o1,@!o2:small_number; {octant numbers}
+@!clockwise:boolean; {should we turn clockwise?}
+@!dx1,@!dy1,@!dx2,@!dy2:integer; {directions of travel at a cusp}
+@!dmax,@!del:integer; {temporary registers}
+
+@ A tricky question arises when a path jumps four octants. We want the
+direction of turning to be counterclockwise if the curve has changed
+direction by $180^\circ$, or by something so close to $180^\circ$ that
+the difference is probably due to rounding errors; otherwise we want to
+turn through an angle of less than $180^\circ$. This decision needs to
+be made even when a curve seems to have jumped only three octants, since
+a curve may approach direction $(-1,0)$ from the fourth octant, then
+it might leave from direction $(+1,0)$ into the first.
+
+The following code solves the problem by analyzing the incoming
+direction |(dx1,dy1)| and the outgoing direction |(dx2,dy2)|.
+
+@<Decide whether or not to go clockwise@>=
+begin @<Compute the incoming and outgoing directions@>;
+unskew(dx1,dy1,right_type(p)); del:=pyth_add(cur_x,cur_y);@/
+dx1:=make_fraction(cur_x,del); dy1:=make_fraction(cur_y,del);
+ {$\cos\theta_1$ and $\sin\theta_1$}
+unskew(dx2,dy2,right_type(q)); del:=pyth_add(cur_x,cur_y);@/
+dx2:=make_fraction(cur_x,del); dy2:=make_fraction(cur_y,del);
+ {$\cos\theta_2$ and $\sin\theta_2$}
+del:=take_fraction(dx1,dy2)-take_fraction(dx2,dy1); {$\sin(\theta_2-\theta_1)$}
+if del>4684844 then clockwise:=false
+else if del<-4684844 then clockwise:=true
+ {$2^{28}\cdot\sin 1^\circ\approx4684844.68$}
+else clockwise:=rev_turns;
+end
+
+@ Actually the turnarounds just computed will be clockwise,
+not counterclockwise, if
+the global variable |rev_turns| is |true|; it is usually |false|.
+
+@<Glob...@>=
+@!rev_turns:boolean; {should we make U-turns in the English manner?}
+
+@ @<Set init...@>=
+rev_turns:=false;
+
+@ @<Compute the incoming and outgoing directions@>=
+dx1:=x_coord(s)-left_x(s); dy1:=y_coord(s)-left_y(s);
+if dx1=0 then if dy1=0 then
+ begin dx1:=x_coord(s)-right_x(p); dy1:=y_coord(s)-right_y(p);
+ if dx1=0 then if dy1=0 then
+ begin dx1:=x_coord(s)-x_coord(p); dy1:=y_coord(s)-y_coord(p);
+ end; {and they {\sl can't} both be zero}
+ end;
+dmax:=abs(dx1);@+if abs(dy1)>dmax then dmax:=abs(dy1);
+while dmax<fraction_one do
+ begin double(dmax); double(dx1); double(dy1);
+ end;
+dx2:=right_x(q)-x_coord(q); dy2:=right_y(q)-y_coord(q);
+if dx2=0 then if dy2=0 then
+ begin dx2:=left_x(r)-x_coord(q); dy2:=left_y(r)-y_coord(q);
+ if dx2=0 then if dy2=0 then
+ begin if right_type(r)=endpoint then
+ begin cur_x:=x_coord(r); cur_y:=y_coord(r);
+ end
+ else begin unskew(x_coord(r),y_coord(r),right_type(r));
+ skew(cur_x,cur_y,right_type(q));
+ end;
+ dx2:=cur_x-x_coord(q); dy2:=cur_y-y_coord(q);
+ end; {and they {\sl can't} both be zero}
+ end;
+dmax:=abs(dx2);@+if abs(dy2)>dmax then dmax:=abs(dy2);
+while dmax<fraction_one do
+ begin double(dmax); double(dx2); double(dy2);
+ end
+
+@ @<Insert additional boundary nodes...@>=
+loop@+ begin if clockwise then
+ if o1=1 then o1:=8@+else decr(o1)
+ else if o1=8 then o1:=1@+else incr(o1);
+ if o1=o2 then goto done;
+ new_boundary(s,octant_code[o1]);
+ s:=link(s); left_octant(s):=right_octant(s);
+ end
+
+@ Now it remains to insert the redundant
+transition information into the |left_transition|
+and |right_transition| fields between adjacent octants, in the octant
+boundary nodes that have just been inserted between |link(p)| and~|q|.
+The turning number is easily computed from these transitions.
+
+@<Fix up the transition fields and adjust the turning number@>=
+p:=link(p);
+repeat s:=link(p);
+o1:=octant_number[right_octant(p)]; o2:=octant_number[left_octant(s)];
+if abs(o1-o2)=1 then
+ begin if o2<o1 then o2:=o1;
+ if odd(o2) then right_transition(p):=axis
+ else right_transition(p):=diagonal;
+ end
+else begin if o1=8 then incr(turning_number)@+else decr(turning_number);
+ right_transition(p):=axis;
+ end;
+left_transition(s):=right_transition(p);
+p:=s;
+until p=q
+
+@* \[22] Filling a contour.
+Given the low-level machinery for making moves and for transforming a
+cyclic path into a cycle spec, we're almost able to fill a digitized path.
+All we need is a high-level routine that walks through the cycle spec and
+controls the overall process.
+
+Our overall goal is to plot the integer points $\bigl(\round(x(t)),
+\round(y(t))\bigr)$ and to connect them by rook moves, assuming that
+$\round(x(t))$ and $\round(y(t))$ don't both jump simultaneously from
+one integer to another as $t$~varies; these rook moves will be the edge
+of the contour that will be filled. We have reduced this problem to the
+case of curves that travel in first octant directions, i.e., curves
+such that $0\L y'(t)\L x'(t)$, by transforming the original coordinates.
+
+\def\xtilde{{\tilde x}} \def\ytilde{{\tilde y}}
+Another transformation makes the problem still simpler. We shall say that
+we are working with {\sl biased coordinates\/} when $(x,y)$ has been
+replaced by $(\xtilde,\ytilde)=(x-y,y+{1\over2})$. When a curve travels
+in first octant directions, the corresponding curve with biased
+coordinates travels in first {\sl quadrant\/} directions; the latter
+condition is symmetric in $x$ and~$y$, so it has advantages for the
+design of algorithms. The |make_spec| routine gives us skewed coordinates
+$(x-y,y)$, hence we obtain biased coordinates by simply adding $1\over2$
+to the second component.
+
+The most important fact about biased coordinates is that we can determine the
+rounded unbiased path $\bigl(\round(x(t)),\round(y(t))\bigr)$ from the
+truncated biased path $\bigl(\lfloor\xtilde(t)\rfloor,\lfloor\ytilde(t)\rfloor
+\bigr)$ and information about the initial and final endpoints. If the
+unrounded and unbiased
+path begins at $(x_0,y_0)$ and ends at $(x_1,y_1)$, it's possible to
+prove (by induction on the length of the truncated biased path) that the
+rounded unbiased path is obtained by the following construction:
+
+\yskip\textindent{1)} Start at $\bigl(\round(x_0),\round(y_0)\bigr)$.
+
+\yskip\textindent{2)} If $(x_0+{1\over2})\bmod1\G(y_0+{1\over2})\bmod1$,
+move one step right.
+
+\yskip\textindent{3)} Whenever the path
+$\bigl(\lfloor\xtilde(t)\rfloor,\lfloor\ytilde(t)\rfloor\bigr)$
+takes an upward step (i.e., when
+$\lfloor\xtilde(t+\epsilon)\rfloor=\lfloor\xtilde(t)\rfloor$ and
+$\lfloor\ytilde(t+\epsilon)\rfloor=\lfloor\ytilde(t)\rfloor+1$),
+move one step up and then one step right.
+
+\yskip\textindent{4)} Whenever the path
+$\bigl(\lfloor\xtilde(t)\rfloor,\lfloor\ytilde(t)\rfloor\bigr)$
+takes a rightward step (i.e., when
+$\lfloor\xtilde(t+\epsilon)\rfloor=\lfloor\xtilde(t)\rfloor+1$ and
+$\lfloor\ytilde(t+\epsilon)\rfloor=\lfloor\ytilde(t)\rfloor$),
+move one step right.
+
+\yskip\textindent{5)} Finally, if
+$(x_1+{1\over2})\bmod1\G(y_1+{1\over2})\bmod1$, move one step left (thereby
+cancelling the previous move, which was one step right). You will now be
+at the point $\bigl(\round(x_1),\round(y_1)\bigr)$.
+
+@ In order to validate the assumption that $\round(x(t))$ and $\round(y(t))$
+don't both jump simultaneously, we shall consider that a coordinate pair
+$(x,y)$ actually represents $(x+\epsilon,y+\epsilon\delta)$, where
+$\epsilon$ and $\delta$ are extremely small positive numbers---so small
+that their precise values never matter. This convention makes rounding
+unambiguous, since there is always a unique integer point nearest to any
+given scaled numbers~$(x,y)$.
+
+When coordinates are transformed so that \MF\ needs to work only in ``first
+octant'' directions, the transformations involve negating~$x$, negating~$y$,
+and/or interchanging $x$ with~$y$. Corresponding adjustments to the
+rounding conventions must be made so that consistent values will be
+obtained. For example, suppose that we're working with coordinates that
+have been transformed so that a third-octant curve travels in first-octant
+directions. The skewed coordinates $(x,y)$ in our data structure represent
+unskewed coordinates $(-y,x+y)$, which are actually $(-y+\epsilon,
+x+y+\epsilon\delta)$. We should therefore round as if our skewed coordinates
+were $(x+\epsilon+\epsilon\delta,y-\epsilon)$ instead of $(x,y)$. The following
+table shows how the skewed coordinates should be perturbed when rounding
+decisions are made:
+$$\vcenter{\halign{#\hfil&&\quad$#$\hfil&\hskip4em#\hfil\cr
+|first_octant|&(x+\epsilon-\epsilon\delta,y+\epsilon\delta)&
+ |fifth_octant|&(x-\epsilon+\epsilon\delta,y-\epsilon\delta)\cr
+|second_octant|&(x-\epsilon+\epsilon\delta,y+\epsilon)&
+ |sixth_octant|&(x+\epsilon-\epsilon\delta,y-\epsilon)\cr
+|third_octant|&(x+\epsilon+\epsilon\delta,y-\epsilon)&
+ |seventh_octant|&(x-\epsilon-\epsilon\delta,y+\epsilon)\cr
+|fourth_octant|&(x-\epsilon-\epsilon\delta,y+\epsilon\delta)&
+ |eighth_octant|&(x+\epsilon+\epsilon\delta,y-\epsilon\delta)\cr}}$$
+
+Four small arrays are set up so that the rounding operations will be
+fairly easy in any given octant.
+
+@<Glob...@>=
+@!y_corr,@!xy_corr,@!z_corr:array[first_octant..sixth_octant] of 0..1;
+@!x_corr:array[first_octant..sixth_octant] of -1..1;
+
+@ Here |xy_corr| is 1 if and only if the $x$ component of a skewed coordinate
+is to be decreased by an infinitesimal amount; |y_corr| is similar, but for
+the $y$ components. The other tables are set up so that the condition
+$$(x+y+|half_unit|)\bmod|unity|\G(y+|half_unit|)\bmod|unity|$$
+is properly perturbed to the condition
+$$(x+y+|half_unit|-|x_corr|-|y_corr|)\bmod|unity|\G
+ (y+|half_unit|-|y_corr|)\bmod|unity|+|z_corr|.$$
+
+@<Set init...@>=
+x_corr[first_octant]:=0; y_corr[first_octant]:=0;
+xy_corr[first_octant]:=0;@/
+x_corr[second_octant]:=0; y_corr[second_octant]:=0;
+xy_corr[second_octant]:=1;@/
+x_corr[third_octant]:=-1; y_corr[third_octant]:=1;
+xy_corr[third_octant]:=0;@/
+x_corr[fourth_octant]:=1; y_corr[fourth_octant]:=0;
+xy_corr[fourth_octant]:=1;@/
+x_corr[fifth_octant]:=0; y_corr[fifth_octant]:=1;
+xy_corr[fifth_octant]:=1;@/
+x_corr[sixth_octant]:=0; y_corr[sixth_octant]:=1;
+xy_corr[sixth_octant]:=0;@/
+x_corr[seventh_octant]:=1; y_corr[seventh_octant]:=0;
+xy_corr[seventh_octant]:=1;@/
+x_corr[eighth_octant]:=-1; y_corr[eighth_octant]:=1;
+xy_corr[eighth_octant]:=0;@/
+for k:=1 to 8 do z_corr[k]:=xy_corr[k]-x_corr[k];
+
+@ Here's a procedure that handles the details of rounding at the
+endpoints: Given skewed coordinates |(x,y)|, it sets |(m1,n1)|
+to the corresponding rounded lattice points, taking the current
+|octant| into account. Global variable |d1| is also set to 1 if
+$(x+y+{1\over2})\bmod1\G(y+{1\over2})\bmod1$.
+
+@p procedure end_round(@!x,@!y:scaled);
+begin y:=y+half_unit-y_corr[octant];
+x:=x+y-x_corr[octant];
+m1:=floor_unscaled(x); n1:=floor_unscaled(y);
+if x-unity*m1>=y-unity*n1+z_corr[octant] then d1:=1@+else d1:=0;
+end;
+
+@ The outputs |(m1,n1,d1)| of |end_round| will sometimes be moved
+to |(m0,n0,d0)|.
+
+@<Glob...@>=
+@!m0,@!n0,@!m1,@!n1:integer; {lattice point coordinates}
+@!d0,@!d1:0..1; {displacement corrections}
+
+@ We're ready now to fill the pixels enclosed by a given cycle spec~|h|;
+the knot list that represents the cycle is destroyed in the process.
+The edge structure that gets all the resulting data is |cur_edges|,
+and the edges are weighted by |cur_wt|.
+
+@p procedure fill_spec(@!h:pointer);
+var @!p,@!q,@!r,@!s:pointer; {for list traversal}
+begin if internal[tracing_edges]>0 then begin_edge_tracing;
+p:=h; {we assume that |left_type(h)=endpoint|}
+repeat octant:=left_octant(p);
+@<Set variable |q| to the node at the end of the current octant@>;
+if q<>p then
+ begin @<Determine the starting and ending
+ lattice points |(m0,n0)| and |(m1,n1)|@>;
+ @<Make the moves for the current octant@>;
+ move_to_edges(m0,n0,m1,n1);
+ end;
+p:=link(q);
+until p=h;
+toss_knot_list(h);
+if internal[tracing_edges]>0 then end_edge_tracing;
+end;
+
+@ @<Set variable |q| to the node at the end of the current octant@>=
+q:=p;
+while right_type(q)<>endpoint do q:=link(q)
+
+@ @<Determine the starting and ending lattice points |(m0,n0)| and |(m1,n1)|@>=
+end_round(x_coord(p),y_coord(p)); m0:=m1; n0:=n1; d0:=d1;@/
+end_round(x_coord(q),y_coord(q))
+
+@ Finally we perform the five-step process that was explained at
+the very beginning of this part of the program.
+
+@<Make the moves for the current octant@>=
+if n1-n0>=move_size then overflow("move table size",move_size);
+@:METAFONT capacity exceeded move table size}{\quad move table size@>
+move[0]:=d0; move_ptr:=0; r:=p;
+repeat s:=link(r);@/
+make_moves(x_coord(r),right_x(r),left_x(s),x_coord(s),@|
+ y_coord(r)+half_unit,right_y(r)+half_unit,left_y(s)+half_unit,
+ y_coord(s)+half_unit,@| xy_corr[octant],y_corr[octant]);
+r:=s;
+until r=q;
+move[move_ptr]:=move[move_ptr]-d1;
+if internal[smoothing]>0 then smooth_moves(0,move_ptr)
+
+@* \[23] Polygonal pens.
+The next few parts of the program deal with the additional complications
+associated with ``envelopes,'' leading up to an algorithm that fills a
+contour with respect to a pen whose boundary is a convex polygon. The
+mathematics underlying this algorithm is based on simple aspects of the
+theory of tracings developed by Leo Guibas, Lyle Ramshaw, and Jorge
+Stolfi [``A kinetic framework for computational geometry,''
+{\sl Proc.\ IEEE Symp.\ Foundations of Computer Science\/ \bf24} (1983),
+100--111].
+@^Guibas, Leonidas Ioannis@>
+@^Ramshaw, Lyle Harold@>
+@^Stolfi, Jorge@>
+
+If the vertices of the polygon are $w_0$, $w_1$, \dots, $w_{n-1}$, $w_n=w_0$,
+in counterclockwise order, the convexity condition requires that ``left
+turns'' are made at each vertex when a person proceeds from $w_0$ to
+$w_1$ to $\cdots$ to~$w_n$. The envelope is obtained if we offset a given
+curve $z(t)$ by $w_k$ when that curve is traveling in a direction
+$z'(t)$ lying between the directions $w_k-w_{k-1}$ and $w\k-w_k$.
+At times~$t$ when the curve direction $z'(t)$ increases past
+$w\k-w_k$, we temporarily stop plotting the offset curve and we insert
+a straight line from $z(t)+w_k$ to $z(t)+w\k$; notice that this straight
+line is tangent to the offset curve. Similarly, when the curve direction
+decreases past $w_k-w_{k-1}$, we stop plotting and insert a straight
+line from $z(t)+w_k$ to $z(t)+w_{k-1}$; the latter line is actually a
+``retrograde'' step, which won't be part of the final envelope under
+\MF's assumptions. The result of this construction is a continuous path
+that consists of alternating curves and straight line segments. The
+segments are usually so short, in practice, that they blend with the
+curves; after all, it's possible to represent any digitized path as
+a sequence of digitized straight lines.
+
+The nicest feature of this approach to envelopes is that it blends
+perfectly with the octant subdivision process we have already developed.
+The envelope travels in the same direction as the curve itself, as we
+plot it, and we need merely be careful what offset is being added.
+Retrograde motion presents a problem, but we will see that there is
+a decent way to handle it.
+
+@ We shall represent pens by maintaining eight lists of offsets,
+one for each octant direction. The offsets at the boundary points
+where a curve turns into a new octant will appear in the lists for
+both octants. This means that we can restrict consideration to
+segments of the original polygon whose directions aim in the first
+octant, as we have done in the simpler case when envelopes were not
+required.
+
+An example should help to clarify this situation: Consider the
+quadrilateral whose vertices are $w_0=(0,-1)$, $w_1=(3,-1)$,
+$w_2=(6,1)$, and $w_3=(1,2)$. A curve that travels in the first octant
+will be offset by $w_1$ or $w_2$, unless its slope drops to zero
+en route to the eighth octant; in the latter case we should switch to $w_0$ as
+we cross the octant boundary. Our list for the first octant will
+contain the three offsets $w_0$, $w_1$,~$w_2$. By convention we will
+duplicate a boundary offset if the angle between octants doesn't
+explicitly appear; in this case there is no explicit line of slope~1
+at the end of the list, so the full list is
+$$w_0\;w_1\;w_2\;w_2\;=\;(0,-1)\;(3,-1)\;(6,1)\;(6,1).$$
+With skewed coordinates $(u-v,v)$ instead of $(u,v)$ we obtain the list
+$$w_0\;w_1\;w_2\;w_2\;\mapsto\;(1,-1)\;(4,-1)\;(5,1)\;(5,1),$$
+which is what actually appears in the data structure. In the second
+octant there's only one offset; we list it twice (with coordinates
+interchanged, so as to make the second octant look like the first),
+and skew those coordinates, obtaining
+$$\tabskip\centering
+\halign to\hsize{$\hfil#\;\mapsto\;{}$\tabskip=0pt&
+ $#\hfil$&\quad in the #\hfil\tabskip\centering\cr
+w_2\;w_2&(-5,6)\;(-5,6)\cr
+\noalign{\vskip\belowdisplayskip
+\vbox{\noindent\strut as the list of transformed and skewed offsets to use
+when curves travel in the second octant. Similarly, we will have\strut}
+\vskip\abovedisplayskip}
+w_2\;w_2&(7,-6)\;(7,-6)&third;\cr
+w_2\;w_2\;w_3\;w_3&(-7,1)\;(-7,1)\;(-3,2)\;(-3,2)&fourth;\cr
+w_3\;w_3&(1,-2)\;(1,-2)&fifth;\cr
+w_3\;w_3\;w_0\;w_0&(-1,1)\;(-1,1)\;(1,0)\;(1,0)&sixth;\cr
+w_0\;w_0&(1,0)\;(1,0)&seventh;\cr
+w_0\;w_0&(-1,1)\;(-1,1)&eighth.\cr}$$
+Notice that $w_1$ is considered here to be internal to the first octant;
+it's not part of the eighth. We could equally well have taken $w_0$ out
+of the first octant list and put it into the eighth; then the first octant
+list would have been
+$$w_1\;w_1\;w_2\;w_2\;\mapsto\;(4,-1)\;(4,-1)\;(5,1)\;(5,1)$$
+and the eighth octant list would have been
+$$w_0\;w_0\;w_1\;\mapsto\;(-1,1)\;(-1,1)\;(2,1).$$
+
+Actually, there's one more complication: The order of offsets is reversed
+in even-numbered octants, because the transformation of coordinates has
+reversed counterclockwise and clockwise orientations in those octants.
+The offsets in the fourth octant, for example, are really $w_3$, $w_3$,
+$w_2$,~$w_2$, not $w_2$, $w_2$, $w_3$,~$w_3$.
+
+@ In general, the list of offsets for an octant will have the form
+$$w_0\;\;w_1\;\;\ldots\;\;w_n\;\;w_{n+1}$$
+(if we renumber the subscripts in each list), where $w_0$ and $w_{n+1}$
+are offsets common to the neighboring lists. We'll often have $w_0=w_1$
+and/or $w_n=w_{n+1}$, but the other $w$'s will be distinct. Curves
+that travel between slope~0 and direction $w_2-w_1$ will use offset~$w_1$;
+curves that travel between directions $w_k-w_{k-1}$ and $w\k-w_k$ will
+use offset~$w_k$, for $1<k<n$; curves between direction $w_n-w_{n-1}$
+and slope~1 (actually slope~$\infty$ after skewing) will use offset~$w_n$.
+In even-numbered octants, the directions are actually $w_k-w\k$ instead
+of $w\k-w_k$, because the offsets have been listed in reverse order.
+
+Each offset $w_k$ is represented by skewed coordinates $(u_k-v_k,v_k)$,
+where $(u_k,v_k)$ is the representation of $w_k$ after it has been rotated
+into a first-octant disguise.
+
+@ The top-level data structure of a pen polygon is a 10-word node containing
+a reference count followed by pointers to the eight offset lists, followed
+by an indication of the pen's range of values.
+@^reference counts@>
+
+If |p|~points to such a node, and if the
+offset list for, say, the fourth octant has entries $w_0$, $w_1$, \dots,
+$w_n$,~$w_{n+1}$, then |info(p+fourth_octant)| will equal~$n$, and
+|link(p+fourth_octant)| will point to the offset node containing~$w_0$.
+Memory location |p+fourth_octant| is said to be the {\sl header\/} of
+the pen-offset list for the fourth octant. Since this is an even-numbered
+octant, $w_0$ is the offset that goes with the fifth octant, and
+$w_{n+1}$ goes with the third.
+
+The elements of the offset list themselves are doubly linked 3-word nodes,
+containing coordinates in their |x_coord| and |y_coord| fields.
+The two link fields are called |link| and |knil|; if |w|~points to
+the node for~$w_k$, then |link(w)| and |knil(w)| point respectively
+to the nodes for $w\k$ and~$w_{k-1}$. If |h| is the list header,
+|link(h)| points to the node for~$w_0$ and |knil(link(h))| to the
+node for~$w_{n+1}$.
+
+The tenth word of a pen header node contains the maximum absolute value of
+an $x$ or $y$ coordinate among all of the unskewed pen offsets.
+
+The |link| field of a pen header node should be |null| if and only if
+the pen is a single point.
+
+@d pen_node_size=10
+@d coord_node_size=3
+@d max_offset(#)==mem[#+9].sc
+
+@ The |print_pen| subroutine illustrates these conventions by
+reconstructing the vertices of a polygon from \MF's complicated
+internal offset representation.
+
+@<Declare subroutines for printing expressions@>=
+procedure print_pen(@!p:pointer;@!s:str_number;@!nuline:boolean);
+var @!nothing_printed:boolean; {has there been any action yet?}
+@!k:1..8; {octant number}
+@!h:pointer; {offset list head}
+@!m,@!n:integer; {offset indices}
+@!w,@!ww:pointer; {pointers that traverse the offset list}
+begin print_diagnostic("Pen polygon",s,nuline);
+nothing_printed:=true; print_ln;
+for k:=1 to 8 do
+ begin octant:=octant_code[k]; h:=p+octant; n:=info(h); w:=link(h);
+ if not odd(k) then w:=knil(w); {in even octants, start at $w_{n+1}$}
+ for m:=1 to n+1 do
+ begin if odd(k) then ww:=link(w)@+else ww:=knil(w);
+ if (x_coord(ww)<>x_coord(w))or(y_coord(ww)<>y_coord(w)) then
+ @<Print the unskewed and unrotated coordinates of node |ww|@>;
+ w:=ww;
+ end;
+ end;
+if nothing_printed then
+ begin w:=link(p+first_octant); print_two(x_coord(w)+y_coord(w),y_coord(w));
+ end;
+print_nl(" .. cycle"); end_diagnostic(true);
+end;
+
+@ @<Print the unskewed and unrotated coordinates of node |ww|@>=
+begin if nothing_printed then nothing_printed:=false
+else print_nl(" .. ");
+print_two_true(x_coord(ww),y_coord(ww));
+end
+
+@ A null pen polygon, which has just one vertex $(0,0)$, is
+predeclared for error recovery. It doesn't need a proper
+reference count, because the |toss_pen| procedure below
+will never delete it from memory.
+@^reference counts@>
+
+@<Initialize table entries...@>=
+ref_count(null_pen):=null; link(null_pen):=null;@/
+info(null_pen+1):=1; link(null_pen+1):=null_coords;
+for k:=null_pen+2 to null_pen+8 do mem[k]:=mem[null_pen+1];
+max_offset(null_pen):=0;@/
+link(null_coords):=null_coords;
+knil(null_coords):=null_coords;@/
+x_coord(null_coords):=0;
+y_coord(null_coords):=0;
+
+@ Here's a trivial subroutine that inserts a copy of an offset
+on the |link| side of its clone in the doubly linked list.
+
+@p procedure dup_offset(@!w:pointer);
+var @!r:pointer; {the new node}
+begin r:=get_node(coord_node_size);
+x_coord(r):=x_coord(w);
+y_coord(r):=y_coord(w);
+link(r):=link(w); knil(link(w)):=r;
+knil(r):=w; link(w):=r;
+end;
+
+@ The following algorithm is somewhat more interesting: It converts a
+knot list for a cyclic path into a pen polygon, ignoring everything
+but the |x_coord|, |y_coord|, and |link| fields. If the given path
+vertices do not define a convex polygon, an error message is issued
+and the null pen is returned.
+
+@p function make_pen(@!h:pointer):pointer;
+label done,done1,not_found,found;
+var @!o,@!oo,@!k:small_number; {octant numbers---old, new, and current}
+@!p:pointer; {top-level node for the new pen}
+@!q,@!r,@!s,@!w,@!hh:pointer; {for list manipulation}
+@!n:integer; {offset counter}
+@!dx,@!dy:scaled; {polygon direction}
+@!mc:scaled; {the largest coordinate}
+begin @<Stamp all nodes with an octant code, compute the maximum offset,
+ and set |hh| to the node that begins the first octant;
+ |goto not_found| if there's a problem@>;
+if mc>=fraction_one-half_unit then goto not_found;
+p:=get_node(pen_node_size); q:=hh; max_offset(p):=mc; ref_count(p):=null;
+if link(q)<>q then link(p):=null+1;
+for k:=1 to 8 do @<Construct the offset list for the |k|th octant@>;
+goto found;
+not_found:p:=null_pen; @<Complain about a bad pen path@>;
+found: if internal[tracing_pens]>0 then print_pen(p," (newly created)",true);
+make_pen:=p;
+end;
+
+@ @<Complain about a bad pen path@>=
+if mc>=fraction_one-half_unit then
+ begin print_err("Pen too large");
+@.Pen too large@>
+ help2("The cycle you specified has a coordinate of 4095.5 or more.")@/
+ ("So I've replaced it by the trivial path `(0,0)..cycle'.");@/
+ end
+else begin print_err("Pen cycle must be convex");
+@.Pen cycle must be convex@>
+ help3("The cycle you specified either has consecutive equal points")@/
+ ("or turns right or turns through more than 360 degrees.")@/
+ ("So I've replaced it by the trivial path `(0,0)..cycle'.");@/
+ end;
+put_get_error
+
+@ There should be exactly one node whose octant number is less than its
+predecessor in the cycle; that is node~|hh|.
+
+The loop here will terminate in all cases, but the proof is somewhat tricky:
+If there are at least two distinct $y$~coordinates in the cycle, we will have
+|o>4| and |o<=4| at different points of the cycle. Otherwise there are
+at least two distinct $x$~coordinates, and we will have |o>2| somewhere,
+|o<=2| somewhere.
+
+@<Stamp all nodes...@>=
+q:=h; r:=link(q); mc:=abs(x_coord(h));
+if q=r then
+ begin hh:=h; right_type(h):=0; {this trick is explained below}
+ if mc<abs(y_coord(h)) then mc:=abs(y_coord(h));
+ end
+else begin o:=0; hh:=null;
+ loop@+ begin s:=link(r);
+ if mc<abs(x_coord(r)) then mc:=abs(x_coord(r));
+ if mc<abs(y_coord(r)) then mc:=abs(y_coord(r));
+ dx:=x_coord(r)-x_coord(q); dy:=y_coord(r)-y_coord(q);
+ if dx=0 then if dy=0 then goto not_found; {double point}
+ if ab_vs_cd(dx,y_coord(s)-y_coord(r),dy,x_coord(s)-x_coord(r))<0 then
+ goto not_found; {right turn}
+ @<Determine the octant code for direction |(dx,dy)|@>;
+ right_type(q):=octant; oo:=octant_number[octant];
+ if o>oo then
+ begin if hh<>null then goto not_found; {$>360^\circ$}
+ hh:=q;
+ end;
+ o:=oo;
+ if (q=h)and(hh<>null) then goto done;
+ q:=r; r:=s;
+ end;
+ done:end
+
+
+@ We want the octant for |(-dx,-dy)| to be
+exactly opposite the octant for |(dx,dy)|.
+
+@<Determine the octant code for direction |(dx,dy)|@>=
+if dx>0 then octant:=first_octant
+else if dx=0 then
+ if dy>0 then octant:=first_octant@+else octant:=first_octant+negate_x
+else begin negate(dx); octant:=first_octant+negate_x;
+ end;
+if dy<0 then
+ begin negate(dy); octant:=octant+negate_y;
+ end
+else if dy=0 then
+ if octant>first_octant then octant:=first_octant+negate_x+negate_y;
+if dx<dy then octant:=octant+switch_x_and_y
+
+@ Now |q| points to the node that the present octant shares with the previous
+octant, and |right_type(q)| is the octant code during which |q|~should advance.
+We have set |right_type(q)=0| in the special case that |q| should never advance
+(because the pen is degenerate).
+
+The number of offsets |n| must be smaller than |max_quarterword|, because
+the |fill_envelope| routine stores |n+1| in the |right_type| field
+of a knot node.
+
+@<Construct the offset list...@>=
+begin octant:=octant_code[k]; n:=0; h:=p+octant;
+loop@+ begin r:=get_node(coord_node_size);
+ skew(x_coord(q),y_coord(q),octant); x_coord(r):=cur_x; y_coord(r):=cur_y;
+ if n=0 then link(h):=r
+ else @<Link node |r| to the previous node@>;
+ w:=r;
+ if right_type(q)<>octant then goto done1;
+ q:=link(q); incr(n);
+ end;
+done1: @<Finish linking the offset nodes, and duplicate the
+ borderline offset nodes if necessary@>;
+if n>=max_quarterword then overflow("pen polygon size",max_quarterword);
+@:METAFONT capacity exceeded pen polygon size}{\quad pen polygon size@>
+info(h):=n;
+end
+
+@ Now |w| points to the node that was inserted most recently, and
+|k| is the current octant number.
+
+@<Link node |r| to the previous node@>=
+if odd(k) then
+ begin link(w):=r; knil(r):=w;
+ end
+else begin knil(w):=r; link(r):=w;
+ end
+
+@ We have inserted |n+1| nodes; it remains to duplicate the nodes at the
+ends, if slopes 0 and~$\infty$ aren't already represented. At the end of
+this section the total number of offset nodes should be |n+2|
+(since we call them $w_0$, $w_1$, \dots,~$w_{n+1}$).
+
+@<Finish linking the offset nodes, and duplicate...@>=
+r:=link(h);
+if odd(k) then
+ begin link(w):=r; knil(r):=w;
+ end
+else begin knil(w):=r; link(r):=w; link(h):=w; r:=w;
+ end;
+if (y_coord(r)<>y_coord(link(r)))or(n=0) then
+ begin dup_offset(r); incr(n);
+ end;
+r:=knil(r);
+if x_coord(r)<>x_coord(knil(r)) then dup_offset(r)
+else decr(n)
+
+@ Conversely, |make_path| goes back from a pen to a cyclic path that
+might have generated it. The structure of this subroutine is essentially
+the same as |print_pen|.
+
+@p @t\4@>@<Declare the function called |trivial_knot|@>@;
+function make_path(@!pen_head:pointer):pointer;
+var @!p:pointer; {the most recently copied knot}
+@!k:1..8; {octant number}
+@!h:pointer; {offset list head}
+@!m,@!n:integer; {offset indices}
+@!w,@!ww:pointer; {pointers that traverse the offset list}
+begin p:=temp_head;
+for k:=1 to 8 do
+ begin octant:=octant_code[k]; h:=pen_head+octant; n:=info(h); w:=link(h);
+ if not odd(k) then w:=knil(w); {in even octants, start at $w_{n+1}$}
+ for m:=1 to n+1 do
+ begin if odd(k) then ww:=link(w)@+else ww:=knil(w);
+ if (x_coord(ww)<>x_coord(w))or(y_coord(ww)<>y_coord(w)) then
+ @<Copy the unskewed and unrotated coordinates of node |ww|@>;
+ w:=ww;
+ end;
+ end;
+if p=temp_head then
+ begin w:=link(pen_head+first_octant);
+ p:=trivial_knot(x_coord(w)+y_coord(w),y_coord(w)); link(temp_head):=p;
+ end;
+link(p):=link(temp_head); make_path:=link(temp_head);
+end;
+
+@ @<Copy the unskewed and unrotated coordinates of node |ww|@>=
+begin unskew(x_coord(ww),y_coord(ww),octant);
+link(p):=trivial_knot(cur_x,cur_y); p:=link(p);
+end
+
+@ @<Declare the function called |trivial_knot|@>=
+function trivial_knot(@!x,@!y:scaled):pointer;
+var @!p:pointer; {a new knot for explicit coordinates |x| and |y|}
+begin p:=get_node(knot_node_size);
+left_type(p):=explicit; right_type(p):=explicit;@/
+x_coord(p):=x; left_x(p):=x; right_x(p):=x;@/
+y_coord(p):=y; left_y(p):=y; right_y(p):=y;@/
+trivial_knot:=p;
+end;
+
+@ That which can be created can be destroyed.
+
+@d add_pen_ref(#)==incr(ref_count(#))
+@d delete_pen_ref(#)==if ref_count(#)=null then toss_pen(#)
+ else decr(ref_count(#))
+
+@<Declare the recycling subroutines@>=
+procedure toss_pen(@!p:pointer);
+var @!k:1..8; {relative header locations}
+@!w,@!ww:pointer; {pointers to offset nodes}
+begin if p<>null_pen then
+ begin for k:=1 to 8 do
+ begin w:=link(p+k);
+ repeat ww:=link(w); free_node(w,coord_node_size); w:=ww;
+ until w=link(p+k);
+ end;
+ free_node(p,pen_node_size);
+ end;
+end;
+
+@ The |find_offset| procedure sets |(cur_x,cur_y)| to the offset associated
+with a given direction~|(x,y)| and a given pen~|p|. If |x=y=0|, the
+result is |(0,0)|. If two different offsets apply, one of them is
+chosen arbitrarily.
+
+@p procedure find_offset(@!x,@!y:scaled; @!p:pointer);
+label done,exit;
+var @!octant:first_octant..sixth_octant; {octant code for |(x,y)|}
+@!s:-1..+1; {sign of the octant}
+@!n:integer; {number of offsets remaining}
+@!h,@!w,@!ww:pointer; {list traversal registers}
+begin @<Compute the octant code; skew and rotate the coordinates |(x,y)|@>;
+if odd(octant_number[octant]) then s:=-1@+else s:=+1;
+h:=p+octant; w:=link(link(h)); ww:=link(w); n:=info(h);
+while n>1 do
+ begin if ab_vs_cd(x,y_coord(ww)-y_coord(w),@|
+ y,x_coord(ww)-x_coord(w))<>s then goto done;
+ w:=ww; ww:=link(w); decr(n);
+ end;
+done:unskew(x_coord(w),y_coord(w),octant);
+exit:end;
+
+@ @<Compute the octant code; skew and rotate the coordinates |(x,y)|@>=
+if x>0 then octant:=first_octant
+else if x=0 then
+ if y<=0 then
+ if y=0 then
+ begin cur_x:=0; cur_y:=0; return;
+ end
+ else octant:=first_octant+negate_x
+ else octant:=first_octant
+else begin x:=-x;
+ if y=0 then octant:=first_octant+negate_x+negate_y
+ else octant:=first_octant+negate_x;
+ end;
+if y<0 then
+ begin octant:=octant+negate_y; y:=-y;
+ end;
+if x>=y then x:=x-y
+else begin octant:=octant+switch_x_and_y; x:=y-x; y:=y-x;
+ end
+
+@* \[24] Filling an envelope.
+We are about to reach the culmination of \MF's digital plotting routines:
+Almost all of the previous algorithms will be brought to bear on \MF's
+most difficult task, which is to fill the envelope of a given cyclic path
+with respect to a given pen polygon.
+
+But we still must complete some of the preparatory work before taking such
+a big plunge.
+
+@ Given a pointer |c| to a nonempty list of cubics,
+and a pointer~|h| to the header information of a pen polygon segment,
+the |offset_prep| routine changes the list into cubics that are
+associated with particular pen offsets. Namely, the cubic between |p|
+and~|q| should be associated with the |k|th offset when |right_type(p)=k|.
+
+List |c| is actually part of a cycle spec, so it terminates at the
+first node whose |right_type| is |endpoint|. The cubics all have
+monotone-nondecreasing $x(t)$ and $y(t)$.
+
+@p @t\4@>@<Declare subroutines needed by |offset_prep|@>@;
+procedure offset_prep(@!c,@!h:pointer);
+label done,not_found;
+var @!n:halfword; {the number of pen offsets}
+@!p,@!q,@!r,@!lh,@!ww:pointer; {for list manipulation}
+@!k:halfword; {the current offset index}
+@!w:pointer; {a pointer to offset $w_k$}
+@<Other local variables for |offset_prep|@>@;
+begin p:=c; n:=info(h); lh:=link(h); {now |lh| points to $w_0$}
+while right_type(p)<>endpoint do
+ begin q:=link(p);
+ @<Split the cubic between |p| and |q|, if necessary, into cubics
+ associated with single offsets, after which |q| should
+ point to the end of the final such cubic@>;
+ @<Advance |p| to node |q|, removing any ``dead'' cubics that
+ might have been introduced by the splitting process@>;
+ end;
+end;
+
+@ @<Advance |p| to node |q|, removing any ``dead'' cubics...@>=
+repeat r:=link(p);
+if x_coord(p)=right_x(p) then if y_coord(p)=right_y(p) then
+ if x_coord(p)=left_x(r) then if y_coord(p)=left_y(r) then
+ if x_coord(p)=x_coord(r) then if y_coord(p)=y_coord(r) then
+ begin remove_cubic(p);
+ if r=q then q:=p;
+ r:=p;
+ end;
+p:=r;
+until p=q
+
+@ The splitting process uses a subroutine like |split_cubic|, but
+(for ``bulletproof'' operation) we check to make sure that the
+resulting (skewed) coordinates satisfy $\Delta x\G0$ and $\Delta y\G0$
+after splitting; |make_spec| has made sure that these relations hold
+before splitting. (This precaution is surely unnecessary, now that
+|make_spec| is so much more careful than it used to be. But who
+wants to take a chance? Maybe the hardware will fail or something.)
+
+@<Declare subroutines needed by |offset_prep|@>=
+procedure split_for_offset(@!p:pointer;@!t:fraction);
+var @!q:pointer; {the successor of |p|}
+@!r:pointer; {the new node}
+begin q:=link(p); split_cubic(p,t,x_coord(q),y_coord(q)); r:=link(p);
+if y_coord(r)<y_coord(p) then y_coord(r):=y_coord(p)
+else if y_coord(r)>y_coord(q) then y_coord(r):=y_coord(q);
+if x_coord(r)<x_coord(p) then x_coord(r):=x_coord(p)
+else if x_coord(r)>x_coord(q) then x_coord(r):=x_coord(q);
+end;
+
+@ If the pen polygon has |n| offsets, and if $w_k=(u_k,v_k)$ is the $k$th
+of these, the $k$th pen slope is defined by the formula
+$$s_k={v\k-v_k\over u\k-u_k},\qquad\hbox{for $0<k<n$}.$$
+In odd-numbered octants, the numerator and denominator of this fraction
+will be nonnegative; in even-numbered octants they will both be nonpositive.
+Furthermore we always have $0=s_0\le s_1\le\cdots\le s_n=\infty$. The goal of
+|offset_prep| is to find an offset index~|k| to associate with
+each cubic, such that the slope $s(t)$ of the cubic satisfies
+$$s_{k-1}\le s(t)\le s_k\qquad\hbox{for $0\le t\le 1$.}\eqno(*)$$
+We may have to split a cubic into as many as $2n-1$ pieces before each
+piece corresponds to a unique offset.
+
+@<Split the cubic between |p| and |q|, if necessary, into cubics...@>=
+if n<=1 then right_type(p):=1 {this case is easy}
+else begin @<Prepare for derivative computations;
+ |goto not_found| if the current cubic is dead@>;
+ @<Find the initial slope, |dy/dx|@>;
+ if dx=0 then @<Handle the special case of infinite slope@>
+ else begin @<Find the index |k| such that $s_{k-1}\L\\{dy}/\\{dx}<s_k$@>;
+ @<Complete the offset splitting process@>;
+ end;
+not_found: end
+
+@ The slope of a cubic $B(z_0,z_1,z_2,z_3;t)=\bigl(x(t),y(t)\bigr)$ can be
+calculated from the quadratic polynomials
+${1\over3}x'(t)=B(x_1-x_0,x_2-x_1,x_3-x_2;t)$ and
+${1\over3}y'(t)=B(y_1-y_0,y_2-y_1,y_3-y_2;t)$.
+Since we may be calculating slopes from several cubics
+split from the current one, it is desirable to do these calculations
+without losing too much precision. ``Scaled up'' values of the
+derivatives, which will be less tainted by accumulated errors than
+derivatives found from the cubics themselves, are maintained in
+local variables |x0|, |x1|, and |x2|, representing $X_0=2^l(x_1-x_0)$,
+$X_1=2^l(x_2-x_1)$, and $X_2=2^l(x_3-x_2)$; similarly |y0|, |y1|, and~|y2|
+represent $Y_0=2^l(y_1-y_0)$, $Y_1=2^l(y_2-y_1)$, and $Y_2=2^l(y_3-y_2)$.
+To test whether the slope of the cubic is $\ge s$ or $\le s$, we will test
+the sign of the quadratic ${1\over3}2^l\bigl(y'(t)-sx'(t)\bigr)$ if $s\le1$,
+or ${1\over3}2^l\bigl(y'(t)/s-x'(t)\bigr)$ if $s>1$.
+
+@<Other local variables for |offset_prep|@>=
+@!x0,@!x1,@!x2,@!y0,@!y1,@!y2:integer; {representatives of derivatives}
+@!t0,@!t1,@!t2:integer; {coefficients of polynomial for slope testing}
+@!du,@!dv,@!dx,@!dy:integer; {for slopes of the pen and the curve}
+@!max_coef:integer; {used while scaling}
+@!x0a,@!x1a,@!x2a,@!y0a,@!y1a,@!y2a:integer; {intermediate values}
+@!t:fraction; {where the derivative passes through zero}
+@!s:fraction; {slope or reciprocal slope}
+
+@ @<Prepare for derivative computations...@>=
+x0:=right_x(p)-x_coord(p); {should be |>=0|}
+x2:=x_coord(q)-left_x(q); {likewise}
+x1:=left_x(q)-right_x(p); {but this might be negative}
+y0:=right_y(p)-y_coord(p); y2:=y_coord(q)-left_y(q);
+y1:=left_y(q)-right_y(p);
+max_coef:=abs(x0); {we take |abs| just to make sure}
+if abs(x1)>max_coef then max_coef:=abs(x1);
+if abs(x2)>max_coef then max_coef:=abs(x2);
+if abs(y0)>max_coef then max_coef:=abs(y0);
+if abs(y1)>max_coef then max_coef:=abs(y1);
+if abs(y2)>max_coef then max_coef:=abs(y2);
+if max_coef=0 then goto not_found;
+while max_coef<fraction_half do
+ begin double(max_coef);
+ double(x0); double(x1); double(x2);
+ double(y0); double(y1); double(y2);
+ end
+
+@ Let us first solve a special case of the problem: Suppose we
+know an index~$k$ such that either (i)~$s(t)\G s_{k-1}$ for all~$t$
+and $s(0)<s_k$, or (ii)~$s(t)\L s_k$ for all~$t$ and $s(0)>s_{k-1}$.
+Then, in a sense, we're halfway done, since one of the two inequalities
+in $(*)$ is satisfied, and the other couldn't be satisfied for
+any other value of~|k|.
+
+The |fin_offset_prep| subroutine solves the stated subproblem.
+It has a boolean parameter called |rising| that is |true| in
+case~(i), |false| in case~(ii). When |rising=false|, parameters
+|x0| through |y2| represent the negative of the derivative of
+the cubic following |p|; otherwise they represent the actual derivative.
+The |w| parameter should point to offset~$w_k$.
+
+@<Declare subroutines needed by |offset_prep|@>=
+procedure fin_offset_prep(@!p:pointer;@!k:halfword;@!w:pointer;
+ @!x0,@!x1,@!x2,@!y0,@!y1,@!y2:integer;@!rising:boolean;@!n:integer);
+label exit;
+var @!ww:pointer; {for list manipulation}
+@!du,@!dv:scaled; {for slope calculation}
+@!t0,@!t1,@!t2:integer; {test coefficients}
+@!t:fraction; {place where the derivative passes a critical slope}
+@!s:fraction; {slope or reciprocal slope}
+@!v:integer; {intermediate value for updating |x0..y2|}
+begin loop
+ begin right_type(p):=k;
+ if rising then
+ if k=n then return
+ else ww:=link(w) {a pointer to $w\k$}
+ else if k=1 then return
+ else ww:=knil(w); {a pointer to $w_{k-1}$}
+ @<Compute test coefficients |(t0,t1,t2)|
+ for $s(t)$ versus $s_k$ or $s_{k-1}$@>;
+ t:=crossing_point(t0,t1,t2);
+ if t>=fraction_one then return;
+ @<Split the cubic at $t$,
+ and split off another cubic if the derivative crosses back@>;
+ if rising then incr(k)@+else decr(k);
+ w:=ww;
+ end;
+exit:end;
+
+@ @<Compute test coefficients |(t0,t1,t2)| for $s(t)$ versus...@>=
+du:=x_coord(ww)-x_coord(w); dv:=y_coord(ww)-y_coord(w);
+if abs(du)>=abs(dv) then {$s_{k-1}\le1$ or $s_k\le1$}
+ begin s:=make_fraction(dv,du);
+ t0:=take_fraction(x0,s)-y0;
+ t1:=take_fraction(x1,s)-y1;
+ t2:=take_fraction(x2,s)-y2;
+ end
+else begin s:=make_fraction(du,dv);
+ t0:=x0-take_fraction(y0,s);
+ t1:=x1-take_fraction(y1,s);
+ t2:=x2-take_fraction(y2,s);
+ end
+
+@ The curve has crossed $s_k$ or $s_{k-1}$; its initial segment satisfies
+$(*)$, and it might cross again and return towards $s_{k-1}$ or $s_k$,
+respectively, yielding another solution of $(*)$.
+
+@<Split the cubic at $t$, and split off another...@>=
+begin split_for_offset(p,t); right_type(p):=k; p:=link(p);@/
+v:=t_of_the_way(x0)(x1); x1:=t_of_the_way(x1)(x2);
+x0:=t_of_the_way(v)(x1);@/
+v:=t_of_the_way(y0)(y1); y1:=t_of_the_way(y1)(y2);
+y0:=t_of_the_way(v)(y1);@/
+t1:=t_of_the_way(t1)(t2);
+if t1>0 then t1:=0; {without rounding error, |t1| would be |<=0|}
+t:=crossing_point(0,-t1,-t2);
+if t<fraction_one then
+ begin split_for_offset(p,t); right_type(link(p)):=k;@/
+ v:=t_of_the_way(x1)(x2); x1:=t_of_the_way(x0)(x1);
+ x2:=t_of_the_way(x1)(v);@/
+ v:=t_of_the_way(y1)(y2); y1:=t_of_the_way(y0)(y1);
+ y2:=t_of_the_way(y1)(v);
+ end;
+end
+
+@ Now we must consider the general problem of |offset_prep|, when
+nothing is known about a given cubic. We start by finding its
+slope $s(0)$ in the vicinity of |t=0|.
+
+If $z'(t)=0$, the given cubic is numerically unstable, since the
+slope direction is probably being influenced primarily by rounding
+errors. A user who specifies such cuspy curves should expect to generate
+rather wild results. The present code tries its best to believe the
+existing data, as if no rounding errors were present.
+
+@ @<Find the initial slope, |dy/dx|@>=
+dx:=x0; dy:=y0;
+if dx=0 then if dy=0 then
+ begin dx:=x1; dy:=y1;
+ if dx=0 then if dy=0 then
+ begin dx:=x2; dy:=y2;
+ end;
+ end
+
+@ The next step is to bracket the initial slope between consecutive
+slopes of the pen polygon. The most important invariant relation in the
+following loop is that |dy/dx>=@t$s_{k-1}$@>|.
+
+@<Find the index |k| such that $s_{k-1}\L\\{dy}/\\{dx}<s_k$@>=
+k:=1; w:=link(lh);
+loop@+ begin if k=n then goto done;
+ ww:=link(w);
+ if ab_vs_cd(dy,abs(x_coord(ww)-x_coord(w)),@|
+ dx,abs(y_coord(ww)-y_coord(w)))>=0 then
+ begin incr(k); w:=ww;
+ end
+ else goto done;
+ end;
+done:
+
+@ Finally we want to reduce the general problem to situations that
+|fin_offset_prep| can handle. If |k=1|, we already are in the desired
+situation. Otherwise we can split the cubic into at most three parts
+with respect to $s_{k-1}$, and apply |fin_offset_prep| to each part.
+
+@<Complete the offset splitting process@>=
+if k=1 then t:=fraction_one+1
+else begin ww:=knil(w); @<Compute test coeff...@>;
+ t:=crossing_point(-t0,-t1,-t2);
+ end;
+if t>=fraction_one then fin_offset_prep(p,k,w,x0,x1,x2,y0,y1,y2,true,n)
+else begin split_for_offset(p,t); r:=link(p);@/
+ x1a:=t_of_the_way(x0)(x1); x1:=t_of_the_way(x1)(x2);
+ x2a:=t_of_the_way(x1a)(x1);@/
+ y1a:=t_of_the_way(y0)(y1); y1:=t_of_the_way(y1)(y2);
+ y2a:=t_of_the_way(y1a)(y1);@/
+ fin_offset_prep(p,k,w,x0,x1a,x2a,y0,y1a,y2a,true,n); x0:=x2a; y0:=y2a;
+ t1:=t_of_the_way(t1)(t2);
+ if t1<0 then t1:=0;
+ t:=crossing_point(0,t1,t2);
+ if t<fraction_one then
+ @<Split off another |rising| cubic for |fin_offset_prep|@>;
+ fin_offset_prep(r,k-1,ww,-x0,-x1,-x2,-y0,-y1,-y2,false,n);
+ end
+
+@ @<Split off another |rising| cubic for |fin_offset_prep|@>=
+begin split_for_offset(r,t);@/
+x1a:=t_of_the_way(x1)(x2); x1:=t_of_the_way(x0)(x1);
+x0a:=t_of_the_way(x1)(x1a);@/
+y1a:=t_of_the_way(y1)(y2); y1:=t_of_the_way(y0)(y1);
+y0a:=t_of_the_way(y1)(y1a);@/
+fin_offset_prep(link(r),k,w,x0a,x1a,x2,y0a,y1a,y2,true,n);
+x2:=x0a; y2:=y0a;
+end
+
+@ @<Handle the special case of infinite slope@>=
+fin_offset_prep(p,n,knil(knil(lh)),-x0,-x1,-x2,-y0,-y1,-y2,false,n)
+
+@ OK, it's time now for the biggie. The |fill_envelope| routine generalizes
+|fill_spec| to polygonal envelopes. Its outer structure is essentially the
+same as before, except that octants with no cubics do contribute to
+the envelope.
+
+@p @t\4@>@<Declare the procedure called |skew_line_edges|@>@;
+@t\4@>@<Declare the procedure called |dual_moves|@>@;
+procedure fill_envelope(@!spec_head:pointer);
+label done, done1;
+var @!p,@!q,@!r,@!s:pointer; {for list traversal}
+@!h:pointer; {head of pen offset list for current octant}
+@!www:pointer; {a pen offset of temporary interest}
+@<Other local variables for |fill_envelope|@>@;
+begin if internal[tracing_edges]>0 then begin_edge_tracing;
+p:=spec_head; {we assume that |left_type(spec_head)=endpoint|}
+repeat octant:=left_octant(p); h:=cur_pen+octant;
+@<Set variable |q| to the node at the end of the current octant@>;
+@<Determine the envelope's starting and ending
+ lattice points |(m0,n0)| and |(m1,n1)|@>;
+offset_prep(p,h); {this may clobber node~|q|, if it becomes ``dead''}
+@<Set variable |q| to the node at the end of the current octant@>;
+@<Make the envelope moves for the current octant and insert them
+ in the pixel data@>;
+p:=link(q);
+until p=spec_head;
+if internal[tracing_edges]>0 then end_edge_tracing;
+toss_knot_list(spec_head);
+end;
+
+@ In even-numbered octants we have reflected the coordinates an odd number
+of times, hence clockwise and counterclockwise are reversed; this means that
+the envelope is being formed in a ``dual'' manner. For the time being, let's
+concentrate on odd-numbered octants, since they're easier to understand.
+After we have coded the program for odd-numbered octants, the changes needed
+to dualize it will not be so mysterious.
+
+It is convenient to assume that we enter an odd-numbered octant with
+an |axis| transition (where the skewed slope is zero) and leave at a
+|diagonal| one (where the skewed slope is infinite). Then all of the
+offset points $z(t)+w(t)$ will lie in a rectangle whose lower left and
+upper right corners are the initial and final offset points. If this
+assumption doesn't hold we can implicitly change the curve so that it does.
+For example, if the entering transition is diagonal, we can draw a
+straight line from $z_0+w_{n+1}$ to $z_0+w_0$ and continue as if the
+curve were moving rightward. The effect of this on the envelope is simply
+to ``doubly color'' the region enveloped by a section of the pen that
+goes from $w_0$ to $w_1$ to $\cdots$ to $w_{n+1}$ to~$w_0$. The additional
+straight line at the beginning (and a similar one at the end, where it
+may be necessary to go from $z_1+w_{n+1}$ to $z_1+w_0$) can be drawn by
+the |line_edges| routine; we are thereby saved from the embarrassment that
+these lines travel backwards from the current octant direction.
+
+Once we have established the assumption that the curve goes from
+$z_0+w_0$ to $z_1+w_{n+1}$, any further retrograde moves that might
+occur within the octant can be essentially ignored; we merely need to
+keep track of the rightmost edge in each row, in order to compute
+the envelope.
+
+Envelope moves consist of offset cubics intermixed with straight line
+segments. We record them in a separate |env_move| array, which is
+something like |move| but it keeps track of the rightmost position of the
+envelope in each row.
+
+@<Glob...@>=
+@!env_move:array[0..move_size] of integer;
+
+@ @<Determine the envelope's starting and ending...@>=
+w:=link(h);@+if left_transition(p)=diagonal then w:=knil(w);
+@!stat if internal[tracing_edges]>unity then
+ @<Print a line of diagnostic info to introduce this octant@>;
+tats@;@/
+ww:=link(h); www:=ww; {starting and ending offsets}
+if odd(octant_number[octant]) then www:=knil(www)@+else ww:=knil(ww);
+if w<>ww then skew_line_edges(p,w,ww);
+end_round(x_coord(p)+x_coord(ww),y_coord(p)+y_coord(ww));
+m0:=m1; n0:=n1; d0:=d1;@/
+end_round(x_coord(q)+x_coord(www),y_coord(q)+y_coord(www));
+if n1-n0>=move_size then overflow("move table size",move_size)
+@:METAFONT capacity exceeded move table size}{\quad move table size@>
+
+@ @<Print a line of diagnostic info to introduce this octant@>=
+begin print_nl("@@ Octant "); print(octant_dir[octant]);
+@:]]]\AT!_Octant}{\.{\AT! Octant...}@>
+print(" ("); print_int(info(h)); print(" offset");
+if info(h)<>1 then print_char("s");
+print("), from ");
+print_two_true(x_coord(p)+x_coord(w),y_coord(p)+y_coord(w));@/
+ww:=link(h);@+if right_transition(q)=diagonal then ww:=knil(ww);
+print(" to ");
+print_two_true(x_coord(q)+x_coord(ww),y_coord(q)+y_coord(ww));
+end
+
+@ A slight variation of the |line_edges| procedure comes in handy
+when we must draw the retrograde lines for nonstandard entry and exit
+conditions.
+
+@<Declare the procedure called |skew_line_edges|@>=
+procedure skew_line_edges(@!p,@!w,@!ww:pointer);
+var @!x0,@!y0,@!x1,@!y1:scaled; {from and to}
+begin if (x_coord(w)<>x_coord(ww))or(y_coord(w)<>y_coord(ww)) then
+ begin x0:=x_coord(p)+x_coord(w); y0:=y_coord(p)+y_coord(w);@/
+ x1:=x_coord(p)+x_coord(ww); y1:=y_coord(p)+y_coord(ww);@/
+ unskew(x0,y0,octant); {unskew and unrotate the coordinates}
+ x0:=cur_x; y0:=cur_y;@/
+ unskew(x1,y1,octant);@/
+ @!stat if internal[tracing_edges]>unity then
+ begin print_nl("@@ retrograde line from ");
+@:]]]\AT!_retro_}{\.{\AT! retrograde line...}@>
+ @.retrograde line...@>
+ print_two(x0,y0); print(" to "); print_two(cur_x,cur_y); print_nl("");
+ end;@+tats@;@/
+ line_edges(x0,y0,cur_x,cur_y); {then draw a straight line}
+ end;
+end;
+
+@ The envelope calculations require more local variables than we needed
+in the simpler case of |fill_spec|. At critical points in the computation,
+|w| will point to offset $w_k$; |m| and |n| will record the current
+lattice positions. The values of |move_ptr| after the initial and before
+the final offset adjustments are stored in |smooth_bot| and |smooth_top|,
+respectively.
+
+@<Other local variables for |fill_envelope|@>=
+@!m,@!n:integer; {current lattice position}
+@!mm0,@!mm1:integer; {skewed equivalents of |m0| and |m1|}
+@!k:integer; {current offset number}
+@!w,@!ww:pointer; {pointers to the current offset and its neighbor}
+@!smooth_bot,@!smooth_top:0..move_size; {boundaries of smoothing}
+@!xx,@!yy,@!xp,@!yp,@!delx,@!dely,@!tx,@!ty:scaled;
+ {registers for coordinate calculations}
+
+@ @<Make the envelope moves for the current octant...@>=
+if odd(octant_number[octant]) then
+ begin @<Initialize for ordinary envelope moves@>;
+ r:=p; right_type(q):=info(h)+1;
+ loop@+ begin if r=q then smooth_top:=move_ptr;
+ while right_type(r)<>k do
+ @<Insert a line segment to approach the correct offset@>;
+ if r=p then smooth_bot:=move_ptr;
+ if r=q then goto done;
+ move[move_ptr]:=1; n:=move_ptr; s:=link(r);@/
+ make_moves(x_coord(r)+x_coord(w),right_x(r)+x_coord(w),
+ left_x(s)+x_coord(w),x_coord(s)+x_coord(w),@|
+ y_coord(r)+y_coord(w)+half_unit,right_y(r)+y_coord(w)+half_unit,
+ left_y(s)+y_coord(w)+half_unit,y_coord(s)+y_coord(w)+half_unit,@|
+ xy_corr[octant],y_corr[octant]);@/
+ @<Transfer moves from the |move| array to |env_move|@>;
+ r:=s;
+ end;
+done: @<Insert the new envelope moves in the pixel data@>;
+ end
+else dual_moves(h,p,q);
+right_type(q):=endpoint
+
+@ @<Initialize for ordinary envelope moves@>=
+k:=0; w:=link(h); ww:=knil(w);
+mm0:=floor_unscaled(x_coord(p)+x_coord(w)-xy_corr[octant]);
+mm1:=floor_unscaled(x_coord(q)+x_coord(ww)-xy_corr[octant]);
+for n:=0 to n1-n0 do env_move[n]:=mm0;
+env_move[n1-n0]:=mm1; move_ptr:=0; m:=mm0
+
+@ At this point |n| holds the value of |move_ptr| that was current
+when |make_moves| began to record its moves.
+
+@<Transfer moves from the |move| array to |env_move|@>=
+repeat m:=m+move[n]-1;
+if m>env_move[n] then env_move[n]:=m;
+incr(n);
+until n>move_ptr
+
+@ Retrograde lines (when |k| decreases) do not need to be recorded in
+|env_move| because their edges are not the furthest right in any row.
+
+@<Insert a line segment to approach the correct offset@>=
+begin xx:=x_coord(r)+x_coord(w); yy:=y_coord(r)+y_coord(w)+half_unit;
+@!stat if internal[tracing_edges]>unity then
+ begin print_nl("@@ transition line "); print_int(k); print(", from ");
+@:]]]\AT!_trans_}{\.{\AT! transition line...}@>
+@.transition line...@>
+ print_two_true(xx,yy-half_unit);
+ end;@+tats@;@/
+if right_type(r)>k then
+ begin incr(k); w:=link(w);
+ xp:=x_coord(r)+x_coord(w); yp:=y_coord(r)+y_coord(w)+half_unit;
+ if yp<>yy then
+ @<Record a line segment from |(xx,yy)| to |(xp,yp)| in |env_move|@>;
+ end
+else begin decr(k); w:=knil(w);
+ xp:=x_coord(r)+x_coord(w); yp:=y_coord(r)+y_coord(w)+half_unit;
+ end;
+stat if internal[tracing_edges]>unity then
+ begin print(" to ");
+ print_two_true(xp,yp-half_unit);
+ print_nl("");
+ end;@+tats@;@/
+m:=floor_unscaled(xp-xy_corr[octant]);
+move_ptr:=floor_unscaled(yp-y_corr[octant])-n0;
+if m>env_move[move_ptr] then env_move[move_ptr]:=m;
+end
+
+@ In this step we have |xp>=xx| and |yp>=yy|.
+
+@<Record a line segment from |(xx,yy)| to |(xp,yp)| in |env_move|@>=
+begin ty:=floor_scaled(yy-y_corr[octant]); dely:=yp-yy; yy:=yy-ty;
+ty:=yp-y_corr[octant]-ty;
+if ty>=unity then
+ begin delx:=xp-xx; yy:=unity-yy;
+ loop@+ begin tx:=take_fraction(delx,make_fraction(yy,dely));
+ if ab_vs_cd(tx,dely,delx,yy)+xy_corr[octant]>0 then decr(tx);
+ m:=floor_unscaled(xx+tx);
+ if m>env_move[move_ptr] then env_move[move_ptr]:=m;
+ ty:=ty-unity;
+ if ty<unity then goto done1;
+ yy:=yy+unity; incr(move_ptr);
+ end;
+ done1:end;
+end
+
+@ @<Insert the new envelope moves in the pixel data@>=
+debug if (m<>mm1)or(move_ptr<>n1-n0) then confusion("1");@+gubed@;@/
+@:this can't happen /}{\quad 1@>
+move[0]:=d0+env_move[0]-mm0;
+for n:=1 to move_ptr do
+ move[n]:=env_move[n]-env_move[n-1]+1;
+move[move_ptr]:=move[move_ptr]-d1;
+if internal[smoothing]>0 then smooth_moves(smooth_bot,smooth_top);
+move_to_edges(m0,n0,m1,n1);
+if right_transition(q)=axis then
+ begin w:=link(h); skew_line_edges(q,knil(w),w);
+ end
+
+@ We've done it all in the odd-octant case; the only thing remaining
+is to repeat the same ideas, upside down and/or backwards.
+
+The following code has been split off as a subprocedure of |fill_envelope|,
+because some \PASCAL\ compilers cannot handle procedures as large as
+|fill_envelope| would otherwise be.
+
+@<Declare the procedure called |dual_moves|@>=
+procedure dual_moves(@!h,@!p,@!q:pointer);
+label done,done1;
+var @!r,@!s:pointer; {for list traversal}
+@<Other local variables for |fill_envelope|@>@;
+begin @<Initialize for dual envelope moves@>;
+r:=p; {recall that |right_type(q)=endpoint=0| now}
+loop@+ begin if r=q then smooth_top:=move_ptr;
+ while right_type(r)<>k do
+ @<Insert a line segment dually to approach the correct offset@>;
+ if r=p then smooth_bot:=move_ptr;
+ if r=q then goto done;
+ move[move_ptr]:=1; n:=move_ptr; s:=link(r);@/
+ make_moves(x_coord(r)+x_coord(w),right_x(r)+x_coord(w),
+ left_x(s)+x_coord(w),x_coord(s)+x_coord(w),@|
+ y_coord(r)+y_coord(w)+half_unit,right_y(r)+y_coord(w)+half_unit,
+ left_y(s)+y_coord(w)+half_unit,y_coord(s)+y_coord(w)+half_unit,@|
+ xy_corr[octant],y_corr[octant]);
+ @<Transfer moves dually from the |move| array to |env_move|@>;
+ r:=s;
+ end;
+done:@<Insert the new envelope moves dually in the pixel data@>;
+end;
+
+@ In the dual case the normal situation is to arrive with a |diagonal|
+transition and to leave at the |axis|. The leftmost edge in each row
+is relevant instead of the rightmost one.
+
+@<Initialize for dual envelope moves@>=
+k:=info(h)+1; ww:=link(h); w:=knil(ww);@/
+mm0:=floor_unscaled(x_coord(p)+x_coord(w)-xy_corr[octant]);
+mm1:=floor_unscaled(x_coord(q)+x_coord(ww)-xy_corr[octant]);
+for n:=1 to n1-n0+1 do env_move[n]:=mm1;
+env_move[0]:=mm0; move_ptr:=0; m:=mm0
+
+@ @<Transfer moves dually from the |move| array to |env_move|@>=
+repeat if m<env_move[n] then env_move[n]:=m;
+m:=m+move[n]-1;
+incr(n);
+until n>move_ptr
+
+@ Dual retrograde lines occur when |k| increases; the edges of such lines
+are not the furthest left in any row.
+
+@<Insert a line segment dually to approach the correct offset@>=
+begin xx:=x_coord(r)+x_coord(w); yy:=y_coord(r)+y_coord(w)+half_unit;
+@!stat if internal[tracing_edges]>unity then
+ begin print_nl("@@ transition line "); print_int(k); print(", from ");
+@:]]]\AT!_trans_}{\.{\AT! transition line...}@>
+@.transition line...@>
+ print_two_true(xx,yy-half_unit);
+ end;@+tats@;@/
+if right_type(r)<k then
+ begin decr(k); w:=knil(w);
+ xp:=x_coord(r)+x_coord(w); yp:=y_coord(r)+y_coord(w)+half_unit;
+ if yp<>yy then
+ @<Record a line segment from |(xx,yy)| to |(xp,yp)| dually in |env_move|@>;
+ end
+else begin incr(k); w:=link(w);
+ xp:=x_coord(r)+x_coord(w); yp:=y_coord(r)+y_coord(w)+half_unit;
+ end;
+stat if internal[tracing_edges]>unity then
+ begin print(" to ");
+ print_two_true(xp,yp-half_unit);
+ print_nl("");
+ end;@+tats@;@/
+m:=floor_unscaled(xp-xy_corr[octant]);
+move_ptr:=floor_unscaled(yp-y_corr[octant])-n0;
+if m<env_move[move_ptr] then env_move[move_ptr]:=m;
+end
+
+@ Again, |xp>=xx| and |yp>=yy|; but this time we are interested in the {\sl
+smallest\/} |m| that belongs to a given |move_ptr| position, instead of
+the largest~|m|.
+
+@<Record a line segment from |(xx,yy)| to |(xp,yp)| dually in |env_move|@>=
+begin ty:=floor_scaled(yy-y_corr[octant]); dely:=yp-yy; yy:=yy-ty;
+ty:=yp-y_corr[octant]-ty;
+if ty>=unity then
+ begin delx:=xp-xx; yy:=unity-yy;
+ loop@+ begin if m<env_move[move_ptr] then env_move[move_ptr]:=m;
+ tx:=take_fraction(delx,make_fraction(yy,dely));
+ if ab_vs_cd(tx,dely,delx,yy)+xy_corr[octant]>0 then decr(tx);
+ m:=floor_unscaled(xx+tx);
+ ty:=ty-unity; incr(move_ptr);
+ if ty<unity then goto done1;
+ yy:=yy+unity;
+ end;
+done1: if m<env_move[move_ptr] then env_move[move_ptr]:=m;
+ end;
+end
+
+@ Since |env_move| contains minimum values instead of maximum values, the
+finishing-up process is slightly different in the dual case.
+
+@<Insert the new envelope moves dually in the pixel data@>=
+debug if (m<>mm1)or(move_ptr<>n1-n0) then confusion("2");@+gubed@;@/
+@:this can't happen /}{\quad 2@>
+move[0]:=d0+env_move[1]-mm0;
+for n:=1 to move_ptr do
+ move[n]:=env_move[n+1]-env_move[n]+1;
+move[move_ptr]:=move[move_ptr]-d1;
+if internal[smoothing]>0 then smooth_moves(smooth_bot,smooth_top);
+move_to_edges(m0,n0,m1,n1);
+if right_transition(q)=diagonal then
+ begin w:=link(h); skew_line_edges(q,w,knil(w));
+ end
+
+@* \[25] Elliptical pens.
+To get the envelope of a cyclic path with respect to an ellipse, \MF\
+calculates the envelope with respect to a polygonal approximation to
+the ellipse, using an approach due to John Hobby (Ph.D. thesis,
+Stanford University, 1985).
+@^Hobby, John Douglas@>
+This has two important advantages over trying to obtain the ``exact''
+envelope:
+
+\yskip\textindent{1)}It gives better results, because the polygon has been
+designed to counteract problems that arise from digitization; the
+polygon includes sub-pixel corrections to an exact ellipse that make
+the results essentially independent of where the path falls on the raster.
+For example, the exact envelope with respect to a pen of diameter~1
+blackens a pixel if and only if the path intersects a circle of diameter~1
+inscribed in that pixel; the resulting pattern has ``blots'' when the path
+is travelling diagonally in unfortunate raster positions. A much better
+result is obtained when pixels are blackened only when the path intersects
+an inscribed {\sl diamond\/} of diameter~1. Such a diamond is precisely
+the polygon that \MF\ uses in the special case of a circle whose diameter is~1.
+
+\yskip\textindent{2)}Polygonal envelopes of cubic splines are cubic
+splines, hence it isn't necessary to introduce completely different
+routines. By contrast, exact envelopes of cubic splines with respect
+to circles are complicated curves, more difficult to plot than cubics.
+
+@ Hobby's construction involves some interesting number theory.
+If $u$ and~$v$ are relatively prime integers, we divide the
+set of integer points $(m,n)$ into equivalence classes by saying
+that $(m,n)$ belongs to class $um+vn$. Then any two integer points
+that lie on a line of slope $-u/v$ belong to the same class, because
+such points have the form $(m+tv,n-tu)$. Neighboring lines of slope $-u/v$
+that go through integer points are separated by distance $1/\psqrt{u^2+v^2}$
+from each other, and these lines are perpendicular to lines of slope~$v/u$.
+If we start at the origin and travel a distance $k/\psqrt{u^2+v^2}$ in
+direction $(u,v)$, we reach the line of slope~$-u/v$ whose points
+belong to class~$k$.
+
+For example, let $u=2$ and $v=3$. Then the points $(0,0)$, $(3,-2)$,
+$\ldots$ belong to class~0; the points $(-1,1)$, $(2,-1)$, $\ldots$ belong
+to class~1; and the distance between these two lines is $1/\sqrt{13}$.
+The point $(2,3)$ itself belongs to class~13, hence its distance from
+the origin is $13/\sqrt{13}=\sqrt{13}$ (which we already knew).
+
+Suppose we wish to plot envelopes with respect to polygons with
+integer vertices. Then the best polygon for curves that travel in
+direction $(v,-u)$ will contain the points of class~$k$ such that
+$k/\psqrt{u^2+v^2}$ is as close as possible to~$d$, where $d$ is the
+maximum distance of the given ellipse from the line $ux+vy=0$.
+
+The |fillin| correction assumes that a diagonal line has an
+apparent thickness $$2f\cdot\min(\vert u\vert,\vert v\vert)/\psqrt{u^2+v^2}$$
+greater than would be obtained with truly square pixels. (If a
+white pixel at an exterior corner is assumed to have apparent
+darkness $f_1$ and a black pixel at an interior corner is assumed
+to have apparent darkness $1-f_2$, then $f=f_1-f_2$ is the |fillin|
+parameter.) Under this assumption we want to choose $k$ so that
+$\bigl(k+2f\cdot\min(\vert u\vert,\vert v\vert)\bigr)\big/\psqrt{u^2+v^2}$
+is as close as possible to $d$.
+
+Integer coordinates for the vertices work nicely because the thickness of
+the envelope at any given slope is independent of the position of the
+path with respect to the raster. It turns out, in fact, that the same
+property holds for polygons whose vertices have coordinates that are
+integer multiples of~$1\over2$, because ellipses are symmetric about
+the origin. It's convenient to double all dimensions and require the
+resulting polygon to have vertices with integer coordinates. For example,
+to get a circle of {\sl diameter}~$r$, we shall compute integer
+coordinates for a circle of {\sl radius}~$r$. The circle of radius~$r$
+will want to be represented by a polygon that contains the boundary
+points $(0,\pm r)$ and~$(\pm r,0)$; later we will divide everything
+by~2 and get a polygon with $(0,\pm{1\over2}r)$ and $(\pm{1\over2}r,0)$
+on its boundary.
+
+@ In practice the important slopes are those having small values of
+$u$ and~$v$; these make regular patterns in which our eyes quickly
+spot irregularities. For example, horizontal and vertical lines
+(when $u=0$ and $\vert v\vert=1$, or $\vert u\vert=1$ and $v=0$)
+are the most important; diagonal lines (when $\vert u\vert=\vert v\vert=1$)
+are next; and then come lines with slope $\pm2$ or $\pm1/2$.
+
+The nicest way to generate all rational directions having small
+numerators and denominators is to generalize the Stern--Brocot tree
+[cf.~{\sl Concrete Mathematics}, section 4.5]
+@^Brocot, Achille@>
+@^Stern, Moritz Abraham@>
+to a ``Stern--Brocot wreath'' as follows: Begin with four nodes
+arranged in a circle, containing the respective directions
+$(u,v)=(1,0)$, $(0,1)$, $(-1,0)$, and~$(0,-1)$. Then between pairs of
+consecutive terms $(u,v)$ and $(u',v')$ of the wreath, insert the
+direction $(u+u',v+v')$; continue doing this until some stopping
+criterion is fulfilled.
+
+It is not difficult to verify that, regardless of the stopping
+criterion, consecutive directions $(u,v)$ and $(u',v')$ of this
+wreath will always satisfy the relation $uv'-u'v=1$. Such pairs
+of directions have a nice property with respect to the equivalence
+classes described above. Let $l$ be a line of equivalent integer points
+$(m+tv,n-tu)$ with respect to~$(u,v)$, and let $l'$ be a line of
+equivalent integer points $(m'+tv',n'-tu')$ with respect to~$(u',v')$.
+Then $l$ and~$l'$ intersect in an integer point $(m'',n'')$, because
+the determinant of the linear equations for intersection is $uv'-u'v=1$.
+Notice that the class number of $(m'',n'')$ with respect to $(u+u',v+v')$
+is the sum of its class numbers with respect to $(u,v)$ and~$(u',v')$.
+Moreover, consecutive points on~$l$ and~$l'$ belong to classes that
+differ by exactly~1 with respect to $(u+u',v+v')$.
+
+This leads to a nice algorithm in which we construct a polygon having
+``correct'' class numbers for as many small-integer directions $(u,v)$
+as possible: Assuming that lines $l$ and~$l'$ contain points of the
+correct class for $(u,v)$ and~$(u',v')$, respectively, we determine
+the intersection $(m'',n'')$ and compute its class with respect to
+$(u+u',v+v')$. If the class is too large to be the best approximation,
+we move back the proper number of steps from $(m'',n'')$ toward smaller
+class numbers on both $l$ and~$l'$, unless this requires moving to points
+that are no longer in the polygon; in this way we arrive at two points that
+determine a line~$l''$ having the appropriate class. The process continues
+recursively, until it cannot proceed without removing the last remaining
+point from the class for $(u,v)$ or the class for $(u',v')$.
+
+@ The |make_ellipse| subroutine produces a pointer to a cyclic path
+whose vertices define a polygon suitable for envelopes. The control
+points on this path will be ignored; in fact, the fields in knot nodes
+that are usually reserved for control points are occupied by other
+data that helps |make_ellipse| compute the desired polygon.
+
+Parameters |major_axis| and |minor_axis| define the axes of the ellipse;
+and parameter |theta| is an angle by which the ellipse is rotated
+counterclockwise. If |theta=0|, the ellipse has the equation
+$(x/a)^2+(y/b)^2=1$, where |a=major_axis/2| and |b=minor_axis/2|.
+In general, the points of the ellipse are generated in the complex plane
+by the formula $e^{i\theta}(a\cos t+ib\sin t)$, as $t$~ranges over all
+angles. Notice that if |major_axis=minor_axis=d|, we obtain a circle
+of diameter~|d|, regardless of the value of |theta|.
+
+The method sketched above is used to produce the elliptical polygon,
+except that the main work is done only in the halfplane obtained from
+the three starting directions $(0,-1)$, $(1,0)$,~$(0,1)$. Since the ellipse
+has circular symmetry, we use the fact that the last half of the polygon
+is simply the negative of the first half. Furthermore, we need to compute only
+one quarter of the polygon if the ellipse has axis symmetry.
+
+@p function make_ellipse(@!major_axis,@!minor_axis:scaled;
+ @!theta:angle):pointer;
+label done,done1,found;
+var @!p,@!q,@!r,@!s:pointer; {for list manipulation}
+@!h:pointer; {head of the constructed knot list}
+@!alpha,@!beta,@!gamma,@!delta:integer; {special points}
+@!c,@!d:integer; {class numbers}
+@!u,@!v:integer; {directions}
+@!symmetric:boolean; {should the result be symmetric about the axes?}
+begin @<Initialize the ellipse data structure by beginning with
+ directions $(0,-1)$, $(1,0)$, $(0,1)$@>;
+@<Interpolate new vertices in the ellipse data structure until
+ improvement is impossible@>;
+if symmetric then
+ @<Complete the half ellipse by reflecting the quarter already computed@>;
+@<Complete the ellipse by copying the negative of the half already computed@>;
+make_ellipse:=h;
+end;
+
+@ A special data structure is used only with |make_ellipse|: The
+|right_x|, |left_x|, |right_y|, and |left_y| fields of knot nodes
+are renamed |right_u|, |left_v|, |right_class|, and |left_length|,
+in order to store information that simplifies the necessary computations.
+
+If |p| and |q| are consecutive knots in this data structure, the
+|x_coord| and |y_coord| fields of |p| and~|q| contain current vertices
+of the polygon; their values are integer multiples
+of |half_unit|. Both of these vertices belong to equivalence class
+|right_class(p)| with respect to the direction
+$\bigl($|right_u(p),left_v(q)|$\bigr)$. The number of points of this class
+on the line from vertex~|p| to vertex~|q| is |1+left_length(q)|.
+In particular, |left_length(q)=0| means that |x_coord(p)=x_coord(q)|
+and |y_coord(p)=y_coord(q)|; such duplicate vertices will be
+discarded during the course of the algorithm.
+
+The contents of |right_u(p)| and |left_v(q)| are integer multiples
+of |half_unit|, just like the coordinate fields. Hence, for example,
+the point $\bigl($|x_coord(p)-left_v(q),y_coord(p)+right_u(p)|$\bigr)$
+also belongs to class number |right_class(p)|. This point is one
+step closer to the vertex in node~|q|; it equals that vertex
+if and only if |left_length(q)=1|.
+
+The |left_type| and |right_type| fields are not used, but |link|
+has its normal meaning.
+
+To start the process, we create four nodes for the three directions
+$(0,-1)$, $(1,0)$, and $(0,1)$. The corresponding vertices are
+$(-\alpha,-\beta)$, $(\gamma,-\beta)$, $(\gamma,\beta)$, and
+$(\alpha,\beta)$, where $(\alpha,\beta)$ is a half-integer approximation
+to where the ellipse rises highest above the $x$-axis, and where
+$\gamma$ is a half-integer approximation to the maximum $x$~coordinate
+of the ellipse. The fourth of these nodes is not actually calculated
+if the ellipse has axis symmetry.
+
+@d right_u==right_x {|u| value for a pen edge}
+@d left_v==left_x {|v| value for a pen edge}
+@d right_class==right_y {equivalence class number of a pen edge}
+@d left_length==left_y {length of a pen edge}
+
+@<Initialize the ellipse data structure...@>=
+@<Calculate integers $\alpha$, $\beta$, $\gamma$ for the vertex
+ coordinates@>;
+p:=get_node(knot_node_size); q:=get_node(knot_node_size);
+r:=get_node(knot_node_size);
+if symmetric then s:=null@+else s:=get_node(knot_node_size);
+h:=p; link(p):=q; link(q):=r; link(r):=s; {|s=null| or |link(s)=null|}
+@<Revise the values of $\alpha$, $\beta$, $\gamma$, if necessary,
+ so that degenerate lines of length zero will not be obtained@>;
+x_coord(p):=-alpha*half_unit;
+y_coord(p):=-beta*half_unit;
+x_coord(q):=gamma*half_unit;@/
+y_coord(q):=y_coord(p); x_coord(r):=x_coord(q);@/
+right_u(p):=0; left_v(q):=-half_unit;@/
+right_u(q):=half_unit; left_v(r):=0;@/
+right_u(r):=0;
+right_class(p):=beta; right_class(q):=gamma; right_class(r):=beta;@/
+left_length(q):=gamma+alpha;
+if symmetric then
+ begin y_coord(r):=0; left_length(r):=beta;
+ end
+else begin y_coord(r):=-y_coord(p); left_length(r):=beta+beta;@/
+ x_coord(s):=-x_coord(p); y_coord(s):=y_coord(r);@/
+ left_v(s):=half_unit; left_length(s):=gamma-alpha;
+ end
+
+@ One of the important invariants of the pen data structure is that
+the points are distinct. We may need to correct the pen specification
+in order to avoid this. (The result of \&{pencircle} will always be at
+least one pixel wide and one pixel tall, although \&{makepen} is
+capable of producing smaller pens.)
+
+@<Revise the values of $\alpha$, $\beta$, $\gamma$, if necessary...@>=
+if beta=0 then beta:=1;
+if gamma=0 then gamma:=1;
+if gamma<=abs(alpha) then
+ if alpha>0 then alpha:=gamma-1
+ else alpha:=1-gamma
+
+@ If $a$ and $b$ are the semi-major and semi-minor axes,
+the given ellipse rises highest above the $x$-axis at the point
+$\bigl((a^2-b^2)\sin\theta\cos\theta/\rho\bigr)+i\rho$, where
+$\rho=\sqrt{(a\sin\theta)^2+(b\cos\theta)^2}$. It reaches
+furthest to the right of~the $y$-axis at the point
+$\sigma+i(a^2-b^2)\sin\theta\cos\theta/\sigma$, where
+$\sigma=\sqrt{(a\cos\theta)^2+(b\sin\theta)^2}$.
+
+@<Calculate integers $\alpha$, $\beta$, $\gamma$...@>=
+if (major_axis=minor_axis)or(theta mod ninety_deg=0) then
+ begin symmetric:=true; alpha:=0;
+ if odd(theta div ninety_deg) then
+ begin beta:=major_axis; gamma:=minor_axis;
+ n_sin:=fraction_one; n_cos:=0; {|n_sin| and |n_cos| are used later}
+ end
+ else begin beta:=minor_axis; gamma:=major_axis; theta:=0;
+ end; {|n_sin| and |n_cos| aren't needed in this case}
+ end
+else begin symmetric:=false;
+ n_sin_cos(theta); {set up $|n_sin|=\sin\theta$ and $|n_cos|=\cos\theta$}
+ gamma:=take_fraction(major_axis,n_sin);
+ delta:=take_fraction(minor_axis,n_cos);
+ beta:=pyth_add(gamma,delta);
+ alpha:=take_fraction(take_fraction(major_axis,
+ make_fraction(gamma,beta)),n_cos)@|
+ -take_fraction(take_fraction(minor_axis,
+ make_fraction(delta,beta)),n_sin);
+ alpha:=(alpha+half_unit) div unity;
+ gamma:=pyth_add(take_fraction(major_axis,n_cos),
+ take_fraction(minor_axis,n_sin));
+ end;
+beta:=(beta+half_unit) div unity;
+gamma:=(gamma+half_unit) div unity
+
+@ Now |p|, |q|, and |r| march through the list, always representing
+three consecutive vertices and two consecutive slope directions.
+When a new slope is interpolated, we back up slightly, until
+further refinement is impossible; then we march forward again.
+The somewhat magical operations performed in this part of the
+algorithm are justified by the theory sketched earlier.
+Complications arise only from the need to keep zero-length lines
+out of the final data structure.
+
+@<Interpolate new vertices in the ellipse data structure...@>=
+loop@+ begin u:=right_u(p)+right_u(q); v:=left_v(q)+left_v(r);
+ c:=right_class(p)+right_class(q);@/
+ @<Compute the distance |d| from class~0 to the edge of the ellipse
+ in direction |(u,v)|, times $\psqrt{u^2+v^2}$,
+ rounded to the nearest integer@>;
+ delta:=c-d; {we want to move |delta| steps back
+ from the intersection vertex~|q|}
+ if delta>0 then
+ begin if delta>left_length(r) then delta:=left_length(r);
+ if delta>=left_length(q) then
+ @<Remove the line from |p| to |q|,
+ and adjust vertex~|q| to introduce a new line@>
+ else @<Insert a new line for direction |(u,v)| between |p| and~|q|@>;
+ end
+ else p:=q;
+ @<Move to the next remaining triple |(p,q,r)|, removing and skipping past
+ zero-length lines that might be present; |goto done| if all
+ triples have been processed@>;
+ end;
+done:
+
+@ The appearance of a zero-length line means that we should advance |p|
+past it. We must not try to straddle a missing direction, because the
+algorithm works only on consecutive pairs of directions.
+
+@<Move to the next remaining triple |(p,q,r)|...@>=
+loop@+ begin q:=link(p);
+ if q=null then goto done;
+ if left_length(q)=0 then
+ begin link(p):=link(q); right_class(p):=right_class(q);
+ right_u(p):=right_u(q); free_node(q,knot_node_size);
+ end
+ else begin r:=link(q);
+ if r=null then goto done;
+ if left_length(r)=0 then
+ begin link(p):=r; free_node(q,knot_node_size); p:=r;
+ end
+ else goto found;
+ end;
+ end;
+found:
+
+@ The `\&{div} 8' near the end of this step comes from
+the fact that |delta| is scaled by~$2^{15}$ and $d$~by~$2^{16}$,
+while |take_fraction| removes a scale factor of~$2^{28}$.
+We also make sure that $d\G\max(\vert u\vert,\vert v\vert)$, so that
+the pen will always include a circular pen of diameter~1 as a subset;
+then it won't be possible to get disconnected path envelopes.
+
+@<Compute the distance |d| from class~0 to the edge of the ellipse...@>=
+delta:=pyth_add(u,v);
+if major_axis=minor_axis then d:=major_axis {circles are easy}
+else begin if theta=0 then
+ begin alpha:=u; beta:=v;
+ end
+ else begin alpha:=take_fraction(u,n_cos)+take_fraction(v,n_sin);
+ beta:=take_fraction(v,n_cos)-take_fraction(u,n_sin);
+ end;
+ alpha:=make_fraction(alpha,delta);
+ beta:=make_fraction(beta,delta);
+ d:=pyth_add(take_fraction(major_axis,alpha),
+ take_fraction(minor_axis,beta));
+ end;
+alpha:=abs(u); beta:=abs(v);
+if alpha<beta then
+ begin alpha:=abs(v); beta:=abs(u);
+ end; {now $\alpha=\max(\vert u\vert,\vert v\vert)$,
+ $\beta=\min(\vert u\vert,\vert v\vert)$}
+if internal[fillin]<>0 then
+ d:=d-take_fraction(internal[fillin],make_fraction(beta+beta,delta));
+d:=take_fraction((d+4) div 8,delta); alpha:=alpha div half_unit;
+if d<alpha then d:=alpha
+
+@ At this point there's a line of length |<=delta| from vertex~|p|
+to vertex~|q|, orthogonal to direction $\bigl($|right_u(p),left_v(q)|$\bigr)$;
+and there's a line of length |>=delta| from vertex~|q| to
+to vertex~|r|, orthogonal to direction $\bigl($|right_u(q),left_v(r)|$\bigr)$.
+The best line to direction $(u,v)$ should replace the line from
+|p| to~|q|; this new line will have the same length as the old.
+
+@<Remove the line from |p| to |q|...@>=
+begin delta:=left_length(q);@/
+right_class(p):=c-delta; right_u(p):=u; left_v(q):=v;@/
+x_coord(q):=x_coord(q)-delta*left_v(r);
+y_coord(q):=y_coord(q)+delta*right_u(q);@/
+left_length(r):=left_length(r)-delta;
+end
+
+@ Here is the main case, now that we have dealt with the exception:
+We insert a new line of length |delta| for direction |(u,v)|, decreasing
+each of the adjacent lines by |delta| steps.
+
+@<Insert a new line for direction |(u,v)| between |p| and~|q|@>=
+begin s:=get_node(knot_node_size); link(p):=s; link(s):=q;@/
+x_coord(s):=x_coord(q)+delta*left_v(q);
+y_coord(s):=y_coord(q)-delta*right_u(p);@/
+x_coord(q):=x_coord(q)-delta*left_v(r);
+y_coord(q):=y_coord(q)+delta*right_u(q);@/
+left_v(s):=left_v(q); right_u(s):=u; left_v(q):=v;@/
+right_class(s):=c-delta;@/
+left_length(s):=left_length(q)-delta; left_length(q):=delta;
+left_length(r):=left_length(r)-delta;
+end
+
+@ Only the coordinates need to be copied, not the class numbers and other stuff.
+At this point either |link(p)| or |link(link(p))| is |null|.
+
+@<Complete the half ellipse...@>=
+begin s:=null; q:=h;
+loop@+ begin r:=get_node(knot_node_size); link(r):=s; s:=r;@/
+ x_coord(s):=x_coord(q); y_coord(s):=-y_coord(q);
+ if q=p then goto done1;
+ q:=link(q);
+ if y_coord(q)=0 then goto done1;
+ end;
+done1: if (link(p)<>null) then free_node(link(p),knot_node_size);
+link(p):=s; beta:=-y_coord(h);
+while y_coord(p)<>beta do p:=link(p);
+q:=link(p);
+end
+
+@ Now we use a somewhat tricky fact: The pointer |q| will be null if and
+only if the line for the final direction $(0,1)$ has been removed. If
+that line still survives, it should be combined with a possibly
+surviving line in the initial direction $(0,-1)$.
+
+@<Complete the ellipse by copying...@>=
+if q<>null then
+ begin if right_u(h)=0 then
+ begin p:=h; h:=link(h); free_node(p,knot_node_size);@/
+ x_coord(q):=-x_coord(h);
+ end;
+ p:=q;
+ end
+else q:=p;
+r:=link(h); {now |p=q|, |x_coord(p)=-x_coord(h)|, |y_coord(p)=-y_coord(h)|}
+repeat s:=get_node(knot_node_size); link(p):=s; p:=s;@/
+x_coord(p):=-x_coord(r); y_coord(p):=-y_coord(r); r:=link(r);
+until r=q;
+link(p):=h
+
+@* \[26] Direction and intersection times.
+A path of length $n$ is defined parametrically by functions $x(t)$ and
+$y(t)$, for |0<=t<=n|; we can regard $t$ as the ``time'' at which the path
+reaches the point $\bigl(x(t),y(t)\bigr)$. In this section of the program
+we shall consider operations that determine special times associated with
+given paths: the first time that a path travels in a given direction, and
+a pair of times at which two paths cross each other.
+
+@ Let's start with the easier task. The function |find_direction_time| is
+given a direction |(x,y)| and a path starting at~|h|. If the path never
+travels in direction |(x,y)|, the direction time will be~|-1|; otherwise
+it will be nonnegative.
+
+Certain anomalous cases can arise: If |(x,y)=(0,0)|, so that the given
+direction is undefined, the direction time will be~0. If $\bigl(x'(t),
+y'(t)\bigr)=(0,0)$, so that the path direction is undefined, it will be
+assumed to match any given direction at time~|t|.
+
+The routine solves this problem in nondegenerate cases by rotating the path
+and the given direction so that |(x,y)=(1,0)|; i.e., the main task will be
+to find when a given path first travels ``due east.''
+
+@p function find_direction_time(@!x,@!y:scaled;@!h:pointer):scaled;
+label exit,found,not_found,done;
+var @!max:scaled; {$\max\bigl(\vert x\vert,\vert y\vert\bigr)$}
+@!p,@!q:pointer; {for list traversal}
+@!n:scaled; {the direction time at knot |p|}
+@!tt:scaled; {the direction time within a cubic}
+@<Other local variables for |find_direction_time|@>@;
+begin @<Normalize the given direction for better accuracy;
+ but |return| with zero result if it's zero@>;
+n:=0; p:=h;
+loop@+ begin if right_type(p)=endpoint then goto not_found;
+ q:=link(p);
+ @<Rotate the cubic between |p| and |q|; then
+ |goto found| if the rotated cubic travels due east at some time |tt|;
+ but |goto not_found| if an entire cyclic path has been traversed@>;
+ p:=q; n:=n+unity;
+ end;
+not_found: find_direction_time:=-unity; return;
+found: find_direction_time:=n+tt;
+exit:end;
+
+@ @<Normalize the given direction for better accuracy...@>=
+if abs(x)<abs(y) then
+ begin x:=make_fraction(x,abs(y));
+ if y>0 then y:=fraction_one@+else y:=-fraction_one;
+ end
+else if x=0 then
+ begin find_direction_time:=0; return;
+ end
+else begin y:=make_fraction(y,abs(x));
+ if x>0 then x:=fraction_one@+else x:=-fraction_one;
+ end
+
+@ Since we're interested in the tangent directions, we work with the
+derivative $${1\over3}B'(x_0,x_1,x_2,x_3;t)=
+B(x_1-x_0,x_2-x_1,x_3-x_2;t)$$ instead of
+$B(x_0,x_1,x_2,x_3;t)$ itself. The derived coefficients are also scaled up
+in order to achieve better accuracy.
+
+The given path may turn abruptly at a knot, and it might pass the critical
+tangent direction at such a time. Therefore we remember the direction |phi|
+in which the previous rotated cubic was traveling. (The value of |phi| will be
+undefined on the first cubic, i.e., when |n=0|.)
+
+@<Rotate the cubic between |p| and |q|; then...@>=
+tt:=0;
+@<Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples of the control
+ points of the rotated derivatives@>;
+if y1=0 then if x1>=0 then goto found;
+if n>0 then
+ begin @<Exit to |found| if an eastward direction occurs at knot |p|@>;
+ if p=h then goto not_found;
+ end;
+if (x3<>0)or(y3<>0) then phi:=n_arg(x3,y3);
+@<Exit to |found| if the curve whose derivatives are specified by
+ |x1,x2,x3,y1,y2,y3| travels eastward at some time~|tt|@>
+
+@ @<Other local variables for |find_direction_time|@>=
+@!x1,@!x2,@!x3,@!y1,@!y2,@!y3:scaled; {multiples of rotated derivatives}
+@!theta,@!phi:angle; {angles of exit and entry at a knot}
+@!t:fraction; {temp storage}
+
+@ @<Set local variables |x1,x2,x3| and |y1,y2,y3| to multiples...@>=
+x1:=right_x(p)-x_coord(p); x2:=left_x(q)-right_x(p);
+x3:=x_coord(q)-left_x(q);@/
+y1:=right_y(p)-y_coord(p); y2:=left_y(q)-right_y(p);
+y3:=y_coord(q)-left_y(q);@/
+max:=abs(x1);
+if abs(x2)>max then max:=abs(x2);
+if abs(x3)>max then max:=abs(x3);
+if abs(y1)>max then max:=abs(y1);
+if abs(y2)>max then max:=abs(y2);
+if abs(y3)>max then max:=abs(y3);
+if max=0 then goto found;
+while max<fraction_half do
+ begin double(max); double(x1); double(x2); double(x3);
+ double(y1); double(y2); double(y3);
+ end;
+t:=x1; x1:=take_fraction(x1,x)+take_fraction(y1,y);
+y1:=take_fraction(y1,x)-take_fraction(t,y);@/
+t:=x2; x2:=take_fraction(x2,x)+take_fraction(y2,y);
+y2:=take_fraction(y2,x)-take_fraction(t,y);@/
+t:=x3; x3:=take_fraction(x3,x)+take_fraction(y3,y);
+y3:=take_fraction(y3,x)-take_fraction(t,y)
+
+@ @<Exit to |found| if an eastward direction occurs at knot |p|@>=
+theta:=n_arg(x1,y1);
+if theta>=0 then if phi<=0 then if phi>=theta-one_eighty_deg then goto found;
+if theta<=0 then if phi>=0 then if phi<=theta+one_eighty_deg then goto found
+
+@ In this step we want to use the |crossing_point| routine to find the
+roots of the quadratic equation $B(y_1,y_2,y_3;t)=0$.
+Several complications arise: If the quadratic equation has a double root,
+the curve never crosses zero, and |crossing_point| will find nothing;
+this case occurs iff $y_1y_3=y_2^2$ and $y_1y_2<0$. If the quadratic
+equation has simple roots, or only one root, we may have to negate it
+so that $B(y_1,y_2,y_3;t)$ crosses from positive to negative at its first root.
+And finally, we need to do special things if $B(y_1,y_2,y_3;t)$ is
+identically zero.
+
+@ @<Exit to |found| if the curve whose derivatives are specified by...@>=
+if x1<0 then if x2<0 then if x3<0 then goto done;
+if ab_vs_cd(y1,y3,y2,y2)=0 then
+ @<Handle the test for eastward directions when $y_1y_3=y_2^2$;
+ either |goto found| or |goto done|@>;
+if y1<=0 then
+ if y1<0 then
+ begin y1:=-y1; y2:=-y2; y3:=-y3;
+ end
+ else if y2>0 then
+ begin y2:=-y2; y3:=-y3;
+ end;
+@<Check the places where $B(y_1,y_2,y_3;t)=0$ to see if
+ $B(x_1,x_2,x_3;t)\ge0$@>;
+done:
+
+@ The quadratic polynomial $B(y_1,y_2,y_3;t)$ begins |>=0| and has at most
+two roots, because we know that it isn't identically zero.
+
+It must be admitted that the |crossing_point| routine is not perfectly accurate;
+rounding errors might cause it to find a root when $y_1y_3>y_2^2$, or to
+miss the roots when $y_1y_3<y_2^2$. The rotation process is itself
+subject to rounding errors. Yet this code optimistically tries to
+do the right thing.
+
+@d we_found_it==begin tt:=(t+@'4000) div @'10000; goto found;
+ end
+
+@<Check the places where $B(y_1,y_2,y_3;t)=0$...@>=
+t:=crossing_point(y1,y2,y3);
+if t>fraction_one then goto done;
+y2:=t_of_the_way(y2)(y3);
+x1:=t_of_the_way(x1)(x2);
+x2:=t_of_the_way(x2)(x3);
+x1:=t_of_the_way(x1)(x2);
+if x1>=0 then we_found_it;
+if y2>0 then y2:=0;
+tt:=t; t:=crossing_point(0,-y2,-y3);
+if t>fraction_one then goto done;
+x1:=t_of_the_way(x1)(x2);
+x2:=t_of_the_way(x2)(x3);
+if t_of_the_way(x1)(x2)>=0 then
+ begin t:=t_of_the_way(tt)(fraction_one); we_found_it;
+ end
+
+@ @<Handle the test for eastward directions when $y_1y_3=y_2^2$;
+ either |goto found| or |goto done|@>=
+begin if ab_vs_cd(y1,y2,0,0)<0 then
+ begin t:=make_fraction(y1,y1-y2);
+ x1:=t_of_the_way(x1)(x2);
+ x2:=t_of_the_way(x2)(x3);
+ if t_of_the_way(x1)(x2)>=0 then we_found_it;
+ end
+else if y3=0 then
+ if y1=0 then
+ @<Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0|@>
+ else if x3>=0 then
+ begin tt:=unity; goto found;
+ end;
+goto done;
+end
+
+@ At this point we know that the derivative of |y(t)| is identically zero,
+and that |x1<0|; but either |x2>=0| or |x3>=0|, so there's some hope of
+traveling east.
+
+@<Exit to |found| if the derivative $B(x_1,x_2,x_3;t)$ becomes |>=0|...@>=
+begin t:=crossing_point(-x1,-x2,-x3);
+if t<=fraction_one then we_found_it;
+if ab_vs_cd(x1,x3,x2,x2)<=0 then
+ begin t:=make_fraction(x1,x1-x2); we_found_it;
+ end;
+end
+
+@ The intersection of two cubics can be found by an interesting variant
+of the general bisection scheme described in the introduction to |make_moves|.\
+Given $w(t)=B(w_0,w_1,w_2,w_3;t)$ and $z(t)=B(z_0,z_1,z_2,z_3;t)$,
+we wish to find a pair of times $(t_1,t_2)$ such that $w(t_1)=z(t_2)$,
+if an intersection exists. First we find the smallest rectangle that
+encloses the points $\{w_0,w_1,w_2,w_3\}$ and check that it overlaps
+the smallest rectangle that encloses
+$\{z_0,z_1,z_2,z_3\}$; if not, the cubics certainly don't intersect.
+But if the rectangles do overlap, we bisect the intervals, getting
+new cubics $w'$ and~$w''$, $z'$~and~$z''$; the intersection routine first
+tries for an intersection between $w'$ and~$z'$, then (if unsuccessful)
+between $w'$ and~$z''$, then (if still unsuccessful) between $w''$ and~$z'$,
+finally (if thrice unsuccessful) between $w''$ and~$z''$. After $l$~successful
+levels of bisection we will have determined the intersection times $t_1$
+and~$t_2$ to $l$~bits of accuracy.
+
+\def\submin{_{\rm min}} \def\submax{_{\rm max}}
+As before, it is better to work with the numbers $W_k=2^l(w_k-w_{k-1})$
+and $Z_k=2^l(z_k-z_{k-1})$ rather than the coefficients $w_k$ and $z_k$
+themselves. We also need one other quantity, $\Delta=2^l(w_0-z_0)$,
+to determine when the enclosing rectangles overlap. Here's why:
+The $x$~coordinates of~$w(t)$ are between $u\submin$ and $u\submax$,
+and the $x$~coordinates of~$z(t)$ are between $x\submin$ and $x\submax$,
+if we write $w_k=(u_k,v_k)$ and $z_k=(x_k,y_k)$ and $u\submin=
+\min(u_0,u_1,u_2,u_3)$, etc. These intervals of $x$~coordinates
+overlap if and only if $u\submin\L x\submax$ and
+$x\submin\L u\submax$. Letting
+$$U\submin=\min(0,U_1,U_1+U_2,U_1+U_2+U_3),\;
+ U\submax=\max(0,U_1,U_1+U_2,U_1+U_2+U_3),$$
+we have $2^lu\submin=2^lu_0+U\submin$, etc.; the condition for overlap
+reduces to
+$$X\submin-U\submax\L 2^l(u_0-x_0)\L X\submax-U\submin.$$
+Thus we want to maintain the quantity $2^l(u_0-x_0)$; similarly,
+the quantity $2^l(v_0-y_0)$ accounts for the $y$~coordinates. The
+coordinates of $\Delta=2^l(w_0-z_0)$ must stay bounded as $l$ increases,
+because of the overlap condition; i.e., we know that $X\submin$,
+$X\submax$, and their relatives are bounded, hence $X\submax-
+U\submin$ and $X\submin-U\submax$ are bounded.
+
+@ Incidentally, if the given cubics intersect more than once, the process
+just sketched will not necessarily find the lexicographically smallest pair
+$(t_1,t_2)$. The solution actually obtained will be smallest in ``shuffled
+order''; i.e., if $t_1=(.a_1a_2\ldots a_{16})_2$ and
+$t_2=(.b_1b_2\ldots b_{16})_2$, then we will minimize
+$a_1b_1a_2b_2\ldots a_{16}b_{16}$, not
+$a_1a_2\ldots a_{16}b_1b_2\ldots b_{16}$.
+Shuffled order agrees with lexicographic order if all pairs of solutions
+$(t_1,t_2)$ and $(t_1',t_2')$ have the property that $t_1<t_1'$ iff
+$t_2<t_2'$; but in general, lexicographic order can be quite different,
+and the bisection algorithm would be substantially less efficient if it were
+constrained by lexicographic order.
+
+For example, suppose that an overlap has been found for $l=3$ and
+$(t_1,t_2)= (.101,.011)$ in binary, but that no overlap is produced by
+either of the alternatives $(.1010,.0110)$, $(.1010,.0111)$ at level~4.
+Then there is probably an intersection in one of the subintervals
+$(.1011,.011x)$; but lexicographic order would require us to explore
+$(.1010,.1xxx)$ and $(.1011,.00xx)$ and $(.1011,.010x)$ first. We wouldn't
+want to store all of the subdivision data for the second path, so the
+subdivisions would have to be regenerated many times. Such inefficiencies
+would be associated with every `1' in the binary representation of~$t_1$.
+
+@ The subdivision process introduces rounding errors, hence we need to
+make a more liberal test for overlap. It is not hard to show that the
+computed values of $U_i$ differ from the truth by at most~$l$, on
+level~$l$, hence $U\submin$ and $U\submax$ will be at most $3l$ in error.
+If $\beta$ is an upper bound on the absolute error in the computed
+components of $\Delta=(|delx|,|dely|)$ on level~$l$, we will replace
+the test `$X\submin-U\submax\L|delx|$' by the more liberal test
+`$X\submin-U\submax\L|delx|+|tol|$', where $|tol|=6l+\beta$.
+
+More accuracy is obtained if we try the algorithm first with |tol=0|;
+the more liberal tolerance is used only if an exact approach fails.
+It is convenient to do this double-take by letting `3' in the preceding
+paragraph be a parameter, which is first 0, then 3.
+
+@<Glob...@>=
+@!tol_step:0..6; {either 0 or 3, usually}
+
+@ We shall use an explicit stack to implement the recursive bisection
+method described above. In fact, the |bisect_stack| array is available for
+this purpose. It will contain numerous 5-word packets like
+$(U_1,U_2,U_3,U\submin,U\submax)$, as well as 20-word packets comprising
+the 5-word packets for $U$, $V$, $X$, and~$Y$.
+
+The following macros define the allocation of stack positions to
+the quantities needed for bisection-intersection.
+
+@d stack_1(#)==bisect_stack[#] {$U_1$, $V_1$, $X_1$, or $Y_1$}
+@d stack_2(#)==bisect_stack[#+1] {$U_2$, $V_2$, $X_2$, or $Y_2$}
+@d stack_3(#)==bisect_stack[#+2] {$U_3$, $V_3$, $X_3$, or $Y_3$}
+@d stack_min(#)==bisect_stack[#+3]
+ {$U\submin$, $V\submin$, $X\submin$, or $Y\submin$}
+@d stack_max(#)==bisect_stack[#+4]
+ {$U\submax$, $V\submax$, $X\submax$, or $Y\submax$}
+@d int_packets=20 {number of words to represent $U_k$, $V_k$, $X_k$, and $Y_k$}
+@#
+@d u_packet(#)==#-5
+@d v_packet(#)==#-10
+@d x_packet(#)==#-15
+@d y_packet(#)==#-20
+@d l_packets==bisect_ptr-int_packets
+@d r_packets==bisect_ptr
+@d ul_packet==u_packet(l_packets) {base of $U'_k$ variables}
+@d vl_packet==v_packet(l_packets) {base of $V'_k$ variables}
+@d xl_packet==x_packet(l_packets) {base of $X'_k$ variables}
+@d yl_packet==y_packet(l_packets) {base of $Y'_k$ variables}
+@d ur_packet==u_packet(r_packets) {base of $U''_k$ variables}
+@d vr_packet==v_packet(r_packets) {base of $V''_k$ variables}
+@d xr_packet==x_packet(r_packets) {base of $X''_k$ variables}
+@d yr_packet==y_packet(r_packets) {base of $Y''_k$ variables}
+@#
+@d u1l==stack_1(ul_packet) {$U'_1$}
+@d u2l==stack_2(ul_packet) {$U'_2$}
+@d u3l==stack_3(ul_packet) {$U'_3$}
+@d v1l==stack_1(vl_packet) {$V'_1$}
+@d v2l==stack_2(vl_packet) {$V'_2$}
+@d v3l==stack_3(vl_packet) {$V'_3$}
+@d x1l==stack_1(xl_packet) {$X'_1$}
+@d x2l==stack_2(xl_packet) {$X'_2$}
+@d x3l==stack_3(xl_packet) {$X'_3$}
+@d y1l==stack_1(yl_packet) {$Y'_1$}
+@d y2l==stack_2(yl_packet) {$Y'_2$}
+@d y3l==stack_3(yl_packet) {$Y'_3$}
+@d u1r==stack_1(ur_packet) {$U''_1$}
+@d u2r==stack_2(ur_packet) {$U''_2$}
+@d u3r==stack_3(ur_packet) {$U''_3$}
+@d v1r==stack_1(vr_packet) {$V''_1$}
+@d v2r==stack_2(vr_packet) {$V''_2$}
+@d v3r==stack_3(vr_packet) {$V''_3$}
+@d x1r==stack_1(xr_packet) {$X''_1$}
+@d x2r==stack_2(xr_packet) {$X''_2$}
+@d x3r==stack_3(xr_packet) {$X''_3$}
+@d y1r==stack_1(yr_packet) {$Y''_1$}
+@d y2r==stack_2(yr_packet) {$Y''_2$}
+@d y3r==stack_3(yr_packet) {$Y''_3$}
+@#
+@d stack_dx==bisect_stack[bisect_ptr] {stacked value of |delx|}
+@d stack_dy==bisect_stack[bisect_ptr+1] {stacked value of |dely|}
+@d stack_tol==bisect_stack[bisect_ptr+2] {stacked value of |tol|}
+@d stack_uv==bisect_stack[bisect_ptr+3] {stacked value of |uv|}
+@d stack_xy==bisect_stack[bisect_ptr+4] {stacked value of |xy|}
+@d int_increment=int_packets+int_packets+5 {number of stack words per level}
+
+@<Check the ``constant''...@>=
+if int_packets+17*int_increment>bistack_size then bad:=32;
+
+@ Computation of the min and max is a tedious but fairly fast sequence of
+instructions; exactly four comparisons are made in each branch.
+
+@d set_min_max(#)==
+ if stack_1(#)<0 then
+ if stack_3(#)>=0 then
+ begin if stack_2(#)<0 then stack_min(#):=stack_1(#)+stack_2(#)
+ else stack_min(#):=stack_1(#);
+ stack_max(#):=stack_1(#)+stack_2(#)+stack_3(#);
+ if stack_max(#)<0 then stack_max(#):=0;
+ end
+ else begin stack_min(#):=stack_1(#)+stack_2(#)+stack_3(#);
+ if stack_min(#)>stack_1(#) then stack_min(#):=stack_1(#);
+ stack_max(#):=stack_1(#)+stack_2(#);
+ if stack_max(#)<0 then stack_max(#):=0;
+ end
+ else if stack_3(#)<=0 then
+ begin if stack_2(#)>0 then stack_max(#):=stack_1(#)+stack_2(#)
+ else stack_max(#):=stack_1(#);
+ stack_min(#):=stack_1(#)+stack_2(#)+stack_3(#);
+ if stack_min(#)>0 then stack_min(#):=0;
+ end
+ else begin stack_max(#):=stack_1(#)+stack_2(#)+stack_3(#);
+ if stack_max(#)<stack_1(#) then stack_max(#):=stack_1(#);
+ stack_min(#):=stack_1(#)+stack_2(#);
+ if stack_min(#)>0 then stack_min(#):=0;
+ end
+
+@ It's convenient to keep the current values of $l$, $t_1$, and $t_2$ in
+the integer form $2^l+2^lt_1$ and $2^l+2^lt_2$. The |cubic_intersection|
+routine uses global variables |cur_t| and |cur_tt| for this purpose;
+after successful completion, |cur_t| and |cur_tt| will contain |unity|
+plus the |scaled| values of $t_1$ and~$t_2$.
+
+The values of |cur_t| and |cur_tt| will be set to zero if |cubic_intersection|
+finds no intersection. The routine gives up and gives an approximate answer
+if it has backtracked
+more than 5000 times (otherwise there are cases where several minutes
+of fruitless computation would be possible).
+
+@d max_patience=5000
+
+@<Glob...@>=
+@!cur_t,@!cur_tt:integer; {controls and results of |cubic_intersection|}
+@!time_to_go:integer; {this many backtracks before giving up}
+@!max_t:integer; {maximum of $2^{l+1}$ so far achieved}
+
+@ The given cubics $B(w_0,w_1,w_2,w_3;t)$ and
+$B(z_0,z_1,z_2,z_3;t)$ are specified in adjacent knot nodes |(p,link(p))|
+and |(pp,link(pp))|, respectively.
+
+@p procedure cubic_intersection(@!p,@!pp:pointer);
+label continue, not_found, exit;
+var @!q,@!qq:pointer; {|link(p)|, |link(pp)|}
+begin time_to_go:=max_patience; max_t:=2;
+@<Initialize for intersections at level zero@>;
+loop@+ begin continue:
+ if delx-tol<=stack_max(x_packet(xy))-stack_min(u_packet(uv)) then
+ if delx+tol>=stack_min(x_packet(xy))-stack_max(u_packet(uv)) then
+ if dely-tol<=stack_max(y_packet(xy))-stack_min(v_packet(uv)) then
+ if dely+tol>=stack_min(y_packet(xy))-stack_max(v_packet(uv)) then
+ begin if cur_t>=max_t then
+ begin if max_t=two then {we've done 17 bisections}
+ begin cur_t:=half(cur_t+1); cur_tt:=half(cur_tt+1); return;
+ end;
+ double(max_t); appr_t:=cur_t; appr_tt:=cur_tt;
+ end;
+ @<Subdivide for a new level of intersection@>;
+ goto continue;
+ end;
+ if time_to_go>0 then decr(time_to_go)
+ else begin while appr_t<unity do
+ begin double(appr_t); double(appr_tt);
+ end;
+ cur_t:=appr_t; cur_tt:=appr_tt; return;
+ end;
+ @<Advance to the next pair |(cur_t,cur_tt)|@>;
+ end;
+exit:end;
+
+@ The following variables are global, although they are used only by
+|cubic_intersection|, because it is necessary on some machines to
+split |cubic_intersection| up into two procedures.
+
+@<Glob...@>=
+@!delx,@!dely:integer; {the components of $\Delta=2^l(w_0-z_0)$}
+@!tol:integer; {bound on the uncertainty in the overlap test}
+@!uv,@!xy:0..bistack_size; {pointers to the current packets of interest}
+@!three_l:integer; {|tol_step| times the bisection level}
+@!appr_t,@!appr_tt:integer; {best approximations known to the answers}
+
+@ We shall assume that the coordinates are sufficiently non-extreme that
+integer overflow will not occur.
+@^overflow in arithmetic@>
+
+@<Initialize for intersections at level zero@>=
+q:=link(p); qq:=link(pp); bisect_ptr:=int_packets;@/
+u1r:=right_x(p)-x_coord(p); u2r:=left_x(q)-right_x(p);
+u3r:=x_coord(q)-left_x(q); set_min_max(ur_packet);@/
+v1r:=right_y(p)-y_coord(p); v2r:=left_y(q)-right_y(p);
+v3r:=y_coord(q)-left_y(q); set_min_max(vr_packet);@/
+x1r:=right_x(pp)-x_coord(pp); x2r:=left_x(qq)-right_x(pp);
+x3r:=x_coord(qq)-left_x(qq); set_min_max(xr_packet);@/
+y1r:=right_y(pp)-y_coord(pp); y2r:=left_y(qq)-right_y(pp);
+y3r:=y_coord(qq)-left_y(qq); set_min_max(yr_packet);@/
+delx:=x_coord(p)-x_coord(pp); dely:=y_coord(p)-y_coord(pp);@/
+tol:=0; uv:=r_packets; xy:=r_packets; three_l:=0; cur_t:=1; cur_tt:=1
+
+@ @<Subdivide for a new level of intersection@>=
+stack_dx:=delx; stack_dy:=dely; stack_tol:=tol; stack_uv:=uv; stack_xy:=xy;
+bisect_ptr:=bisect_ptr+int_increment;@/
+double(cur_t); double(cur_tt);@/
+u1l:=stack_1(u_packet(uv)); u3r:=stack_3(u_packet(uv));
+u2l:=half(u1l+stack_2(u_packet(uv)));
+u2r:=half(u3r+stack_2(u_packet(uv)));
+u3l:=half(u2l+u2r); u1r:=u3l;
+set_min_max(ul_packet); set_min_max(ur_packet);@/
+v1l:=stack_1(v_packet(uv)); v3r:=stack_3(v_packet(uv));
+v2l:=half(v1l+stack_2(v_packet(uv)));
+v2r:=half(v3r+stack_2(v_packet(uv)));
+v3l:=half(v2l+v2r); v1r:=v3l;
+set_min_max(vl_packet); set_min_max(vr_packet);@/
+x1l:=stack_1(x_packet(xy)); x3r:=stack_3(x_packet(xy));
+x2l:=half(x1l+stack_2(x_packet(xy)));
+x2r:=half(x3r+stack_2(x_packet(xy)));
+x3l:=half(x2l+x2r); x1r:=x3l;
+set_min_max(xl_packet); set_min_max(xr_packet);@/
+y1l:=stack_1(y_packet(xy)); y3r:=stack_3(y_packet(xy));
+y2l:=half(y1l+stack_2(y_packet(xy)));
+y2r:=half(y3r+stack_2(y_packet(xy)));
+y3l:=half(y2l+y2r); y1r:=y3l;
+set_min_max(yl_packet); set_min_max(yr_packet);@/
+uv:=l_packets; xy:=l_packets;
+double(delx); double(dely);@/
+tol:=tol-three_l+tol_step; double(tol); three_l:=three_l+tol_step
+
+@ @<Advance to the next pair |(cur_t,cur_tt)|@>=
+not_found: if odd(cur_tt) then
+ if odd(cur_t) then @<Descend to the previous level and |goto not_found|@>
+ else begin incr(cur_t);
+ delx:=delx+stack_1(u_packet(uv))+stack_2(u_packet(uv))
+ +stack_3(u_packet(uv));
+ dely:=dely+stack_1(v_packet(uv))+stack_2(v_packet(uv))
+ +stack_3(v_packet(uv));
+ uv:=uv+int_packets; {switch from |l_packets| to |r_packets|}
+ decr(cur_tt); xy:=xy-int_packets; {switch from |r_packets| to |l_packets|}
+ delx:=delx+stack_1(x_packet(xy))+stack_2(x_packet(xy))
+ +stack_3(x_packet(xy));
+ dely:=dely+stack_1(y_packet(xy))+stack_2(y_packet(xy))
+ +stack_3(y_packet(xy));
+ end
+else begin incr(cur_tt); tol:=tol+three_l;
+ delx:=delx-stack_1(x_packet(xy))-stack_2(x_packet(xy))
+ -stack_3(x_packet(xy));
+ dely:=dely-stack_1(y_packet(xy))-stack_2(y_packet(xy))
+ -stack_3(y_packet(xy));
+ xy:=xy+int_packets; {switch from |l_packets| to |r_packets|}
+ end
+
+@ @<Descend to the previous level...@>=
+begin cur_t:=half(cur_t); cur_tt:=half(cur_tt);
+if cur_t=0 then return;
+bisect_ptr:=bisect_ptr-int_increment; three_l:=three_l-tol_step;
+delx:=stack_dx; dely:=stack_dy; tol:=stack_tol; uv:=stack_uv; xy:=stack_xy;@/
+goto not_found;
+end
+
+@ The |path_intersection| procedure is much simpler.
+It invokes |cubic_intersection| in lexicographic order until finding a
+pair of cubics that intersect. The final intersection times are placed in
+|cur_t| and~|cur_tt|.
+
+@p procedure path_intersection(@!h,@!hh:pointer);
+label exit;
+var @!p,@!pp:pointer; {link registers that traverse the given paths}
+@!n,@!nn:integer; {integer parts of intersection times, minus |unity|}
+begin @<Change one-point paths into dead cycles@>;
+tol_step:=0;
+repeat n:=-unity; p:=h;
+ repeat if right_type(p)<>endpoint then
+ begin nn:=-unity; pp:=hh;
+ repeat if right_type(pp)<>endpoint then
+ begin cubic_intersection(p,pp);
+ if cur_t>0 then
+ begin cur_t:=cur_t+n; cur_tt:=cur_tt+nn; return;
+ end;
+ end;
+ nn:=nn+unity; pp:=link(pp);
+ until pp=hh;
+ end;
+ n:=n+unity; p:=link(p);
+ until p=h;
+tol_step:=tol_step+3;
+until tol_step>3;
+cur_t:=-unity; cur_tt:=-unity;
+exit:end;
+
+@ @<Change one-point paths...@>=
+if right_type(h)=endpoint then
+ begin right_x(h):=x_coord(h); left_x(h):=x_coord(h);
+ right_y(h):=y_coord(h); left_y(h):=y_coord(h); right_type(h):=explicit;
+ end;
+if right_type(hh)=endpoint then
+ begin right_x(hh):=x_coord(hh); left_x(hh):=x_coord(hh);
+ right_y(hh):=y_coord(hh); left_y(hh):=y_coord(hh); right_type(hh):=explicit;
+ end;
+
+@* \[27] Online graphic output.
+\MF\ displays images on the user's screen by means of a few primitive
+operations that are defined below. These operations have deliberately been
+kept simple so that they can be implemented without great difficulty on a
+wide variety of machines. Since \PASCAL\ has no traditional standards for
+graphic output, some system-dependent code needs to be written in order to
+support this aspect of \MF; but the necessary routines are usually quite
+easy to write.
+@^system dependencies@>
+
+In fact, there are exactly four such routines:
+
+\yskip\hang
+|init_screen| does whatever initialization is necessary to
+support the other operations; it is a boolean function that returns
+|false| if graphic output cannot be supported (e.g., if the other three
+routines have not been written, or if the user doesn't have the
+right kind of terminal).
+
+\yskip\hang
+|blank_rectangle| updates a buffer area in memory so that
+all pixels in a specified rectangle will be set to the background color.
+
+\yskip\hang
+|paint_row| assigns values to specified pixels in a row of
+the buffer just mentioned, based on ``transition'' indices explained below.
+
+\yskip\hang
+|update_screen| displays the current screen buffer; the
+effects of |blank_rectangle| and |paint_row| commands may or may not
+become visible until the next |update_screen| operation is performed.
+(Thus, |update_screen| is analogous to |update_terminal|.)
+
+\yskip\noindent
+The \PASCAL\ code here is a minimum version of |init_screen| and
+|update_screen|, usable on \MF\ installations that don't
+support screen output. If |init_screen| is changed to return |true|
+instead of |false|, the other routines will simply log the fact
+that they have been called; they won't really display anything.
+The standard test routines for \MF\ use this log information to check
+that \MF\ is working properly, but the |wlog| instructions should be
+removed from production versions of \MF.
+
+@p function init_screen:boolean;
+begin init_screen:=false;
+end;
+@#
+procedure update_screen; {will be called only if |init_screen| returns |true|}
+begin @!init wlog_ln('Calling UPDATESCREEN');@+tini {for testing only}
+end;
+
+@ The user's screen is assumed to be a rectangular area, |screen_width|
+pixels wide and |screen_depth| pixels deep. The pixel in the upper left
+corner is said to be in column~0 of row~0; the pixel in the lower right
+corner is said to be in column |screen_width-1| of row |screen_depth-1|.
+Notice that row numbers increase from top to bottom, contrary to \MF's
+other coordinates.
+
+Each pixel is assumed to have two states, referred to in this documentation
+as |black| and |white|. The background color is called |white| and the
+other color is called |black|; but any two distinct pixel values
+can actually be used. For example, the author developed \MF\ on a
+system for which |white| was black and |black| was bright green.
+
+@d white=0 {background pixels}
+@d black=1 {visible pixels}
+
+@<Types...@>=
+@!screen_row=0..screen_depth; {a row number on the screen}
+@!screen_col=0..screen_width; {a column number on the screen}
+@!trans_spec=array[screen_col] of screen_col; {a transition spec, see below}
+@!pixel_color=white..black; {specifies one of the two pixel values}
+
+@ We'll illustrate the |blank_rectangle| and |paint_row| operations by
+pretending to declare a screen buffer called |screen_pixel|. This code
+is actually commented out, but it does specify the intended effects.
+
+@<Glob...@>=
+@{@!screen_pixel:array[screen_row,screen_col] of pixel_color;@+@}
+
+@ The |blank_rectangle| routine simply whitens all pixels that lie in
+columns |left_col| through |right_col-1|, inclusive, of rows
+|top_row| through |bot_row-1|, inclusive, given four parameters that satisfy
+the relations
+$$\hbox{|0<=left_col<=right_col<=screen_width|,\quad
+ |0<=top_row<=bot_row<=screen_depth|.}$$
+If |left_col=right_col| or |top_row=bot_row|, nothing happens.
+
+The commented-out code in the following procedure is for illustrative
+purposes only.
+@^system dependencies@>
+
+@p procedure blank_rectangle(@!left_col,@!right_col:screen_col;
+ @!top_row,@!bot_row:screen_row);
+var @!r:screen_row;
+@!c:screen_col;
+begin @{@+for r:=top_row to bot_row-1 do
+ for c:=left_col to right_col-1 do
+ screen_pixel[r,c]:=white;@+@}@/
+@!init wlog_cr; {this will be done only after |init_screen=true|}
+wlog_ln('Calling BLANKRECTANGLE(',left_col:1,',',
+ right_col:1,',',top_row:1,',',bot_row:1,')');@+tini
+end;
+
+@ The real work of screen display is done by |paint_row|. But it's not
+hard work, because the operation affects only
+one of the screen rows, and it affects only a contiguous set of columns
+in that row. There are four parameters: |r|~(the row),
+|b|~(the initial color),
+|a|~(the array of transition specifications),
+and |n|~(the number of transitions). The elements of~|a| will satisfy
+$$0\L a[0]<a[1]<\cdots<a[n]\L |screen_width|;$$
+the value of |r| will satisfy |0<=r<screen_depth|; and |n| will be positive.
+
+The general idea is to paint blocks of pixels in alternate colors;
+the precise details are best conveyed by means of a \PASCAL\
+program (see the commented-out code below).
+@^system dependencies@>
+
+@p procedure paint_row(@!r:screen_row;@!b:pixel_color;var @!a:trans_spec;
+ @!n:screen_col);
+var @!k:screen_col; {an index into |a|}
+@!c:screen_col; {an index into |screen_pixel|}
+begin @{ k:=0; c:=a[0];
+repeat incr(k);
+ repeat screen_pixel[r,c]:=b; incr(c);
+ until c=a[k];
+ b:=black-b; {$|black|\swap|white|$}
+ until k=n;@+@}@/
+@!init wlog('Calling PAINTROW(',r:1,',',b:1,';');
+ {this is done only after |init_screen=true|}
+for k:=0 to n do
+ begin wlog(a[k]:1); if k<>n then wlog(',');
+ end;
+wlog_ln(')');@+tini
+end;
+
+@ The remainder of \MF's screen routines are system-independent calls
+on the four primitives just defined.
+
+First we have a global boolean variable that tells if |init_screen|
+has been called, and another one that tells if |init_screen| has
+given a |true| response.
+
+@<Glob...@>=
+@!screen_started:boolean; {have the screen primitives been initialized?}
+@!screen_OK:boolean; {is it legitimate to call |blank_rectangle|,
+ |paint_row|, and |update_screen|?}
+
+@ @d start_screen==begin if not screen_started then
+ begin screen_OK:=init_screen; screen_started:=true;
+ end;
+ end
+
+@<Set init...@>=
+screen_started:=false; screen_OK:=false;
+
+@ \MF\ provides the user with 16 ``window'' areas on the screen, in each
+of which it is possible to produce independent displays.
+
+It should be noted that \MF's windows aren't really independent
+``clickable'' entities in the sense of multi-window graphic workstations;
+\MF\ simply maps them into subsets of a single screen image that is
+controlled by |init_screen|, |blank_rectangle|, |paint_row|, and
+|update_screen| as described above. Implementations of \MF\ on a
+multi-window workstation probably therefore make use of only two
+windows in the other sense: one for the terminal output and another
+for the screen with \MF's 16 areas. Henceforth we shall
+use the term window only in \MF's sense.
+
+@<Types...@>=
+@!window_number=0..15;
+
+@ A user doesn't have to use any of the 16 windows. But when a window is
+``opened,'' it is allocated to a specific rectangular portion of the screen
+and to a specific rectangle with respect to \MF's coordinates. The relevant
+data is stored in global arrays |window_open|, |left_col|, |right_col|,
+|top_row|, |bot_row|, |m_window|, and |n_window|.
+
+The |window_open| array is boolean, and its significance is obvious. The
+|left_col|, \dots, |bot_row| arrays contain screen coordinates that
+can be used to blank the entire window with |blank_rectangle|. And the
+other two arrays just mentioned handle the conversion between
+actual coordinates and screen coordinates: \MF's pixel in column~$m$
+of row~$n$ will appear in screen column |m_window+m| and in screen row
+|n_window-n|, provided that these lie inside the boundaries of the window.
+
+Another array |window_time| holds the number of times this window has
+been updated.
+
+@<Glob...@>=
+@!window_open:array[window_number] of boolean;
+ {has this window been opened?}
+@!left_col:array[window_number] of screen_col;
+ {leftmost column position on screen}
+@!right_col:array[window_number] of screen_col;
+ {rightmost column position, plus~1}
+@!top_row:array[window_number] of screen_row;
+ {topmost row position on screen}
+@!bot_row:array[window_number] of screen_row;
+ {bottommost row position, plus~1}
+@!m_window:array[window_number] of integer;
+ {offset between user and screen columns}
+@!n_window:array[window_number] of integer;
+ {offset between user and screen rows}
+@!window_time:array[window_number] of integer;
+ {it has been updated this often}
+
+@ @<Set init...@>=
+for k:=0 to 15 do
+ begin window_open[k]:=false; window_time[k]:=0;
+ end;
+
+@ Opening a window isn't like opening a file, because you can open it
+as often as you like, and you never have to close it again. The idea is
+simply to define special points on the current screen display.
+
+Overlapping window specifications may cause complex effects that can
+be understood only by scrutinizing \MF's display algorithms; thus it
+has been left undefined in the \MF\ user manual, although the behavior
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+is in fact predictable.
+
+Here is a subroutine that implements the command `\&{openwindow}~|k|
+\&{from}~$(\\{r0},\\{c0})$ \&{to}~$(\\{r1},\\{c1})$ \&{at}~$(x,y)$'.
+
+@p procedure open_a_window(@!k:window_number;@!r0,@!c0,@!r1,@!c1:scaled;
+ @!x,@!y:scaled);
+var @!m,@!n:integer; {pixel coordinates}
+begin @<Adjust the coordinates |(r0,c0)| and |(r1,c1)| so that
+ they lie in the proper range@>;
+window_open[k]:=true; incr(window_time[k]);@/
+left_col[k]:=c0; right_col[k]:=c1; top_row[k]:=r0; bot_row[k]:=r1;@/
+@<Compute the offsets between screen coordinates and actual coordinates@>;
+start_screen;
+if screen_OK then
+ begin blank_rectangle(c0,c1,r0,r1); update_screen;
+ end;
+end;
+
+@ A window whose coordinates don't fit the existing screen size will be
+truncated until they do.
+
+@<Adjust the coordinates |(r0,c0)| and |(r1,c1)|...@>=
+if r0<0 then r0:=0@+else r0:=round_unscaled(r0);
+r1:=round_unscaled(r1);
+if r1>screen_depth then r1:=screen_depth;
+if r1<r0 then
+ if r0>screen_depth then r0:=r1@+else r1:=r0;
+if c0<0 then c0:=0@+else c0:=round_unscaled(c0);
+c1:=round_unscaled(c1);
+if c1>screen_width then c1:=screen_width;
+if c1<c0 then
+ if c0>screen_width then c0:=c1@+else c1:=c0
+
+@ Three sets of coordinates are rampant, and they must be kept straight!
+(i)~\MF's main coordinates refer to the edges between pixels. (ii)~\MF's
+pixel coordinates (within edge structures) say that the pixel bounded by
+$(m,n)$, $(m,n+1)$, $(m+1,n)$, and~$(m+1,n+1)$ is in pixel row number~$n$
+and pixel column number~$m$. (iii)~Screen coordinates, on the other hand,
+have rows numbered in increasing order from top to bottom, as mentioned
+above.
+@^coordinates, explained@>
+
+The program here first computes integers $m$ and $n$ such that
+pixel column~$m$ of pixel row~$n$ will be at the upper left corner
+of the window. Hence pixel column |m-c0| of pixel row |n+r0|
+will be at the upper left corner of the screen.
+
+@<Compute the offsets between screen coordinates and actual coordinates@>=
+m:=round_unscaled(x); n:=round_unscaled(y)-1;@/
+m_window[k]:=c0-m; n_window[k]:=r0+n
+
+@ Now here comes \MF's most complicated operation related to window
+display: Given the number~|k| of an open window, the pixels of positive
+weight in |cur_edges| will be shown as |black| in the window; all other
+pixels will be shown as |white|.
+
+@p procedure disp_edges(@!k:window_number);
+label done,found;
+var @!p,@!q:pointer; {for list manipulation}
+@!already_there:boolean; {is a previous incarnation in the window?}
+@!r:integer; {row number}
+@<Other local variables for |disp_edges|@>@;
+begin if screen_OK then
+ if left_col[k]<right_col[k] then if top_row[k]<bot_row[k] then
+ begin already_there:=false;
+ if last_window(cur_edges)=k then
+ if last_window_time(cur_edges)=window_time[k] then
+ already_there:=true;
+ if not already_there then
+ blank_rectangle(left_col[k],right_col[k],top_row[k],bot_row[k]);
+ @<Initialize for the display computations@>;
+ p:=link(cur_edges); r:=n_window[k]-(n_min(cur_edges)-zero_field);
+ while (p<>cur_edges)and(r>=top_row[k]) do
+ begin if r<bot_row[k] then
+ @<Display the pixels of edge row |p| in screen row |r|@>;
+ p:=link(p); decr(r);
+ end;
+ update_screen;
+ incr(window_time[k]);
+ last_window(cur_edges):=k; last_window_time(cur_edges):=window_time[k];
+ end;
+end;
+
+@ Since it takes some work to display a row, we try to avoid recomputation
+whenever we can.
+
+@<Display the pixels of edge row |p| in screen row |r|@>=
+begin if unsorted(p)>void then sort_edges(p)
+else if unsorted(p)=void then if already_there then goto done;
+unsorted(p):=void; {this time we'll paint, but maybe not next time}
+@<Set up the parameters needed for |paint_row|;
+ but |goto done| if no painting is needed after all@>;
+paint_row(r,b,row_transition,n);
+done: end
+
+@ The transition-specification parameter to |paint_row| is always the same
+array.
+
+@<Glob...@>=
+@!row_transition:trans_spec; {an array of |black|/|white| transitions}
+
+@ The job remaining is to go through the list |sorted(p)|, unpacking the
+|info| fields into |m| and weight, then making |black| the pixels whose
+accumulated weight~|w| is positive.
+
+@<Other local variables for |disp_edges|@>=
+@!n:screen_col; {the highest active index in |row_transition|}
+@!w,@!ww:integer; {old and new accumulated weights}
+@!b:pixel_color; {status of first pixel in the row transitions}
+@!m,@!mm:integer; {old and new screen column positions}
+@!d:integer; {edge-and-weight without |min_halfword| compensation}
+@!m_adjustment:integer; {conversion between edge and screen coordinates}
+@!right_edge:integer; {largest edge-and-weight that could affect the window}
+@!min_col:screen_col; {the smallest screen column number in the window}
+
+@ Some precomputed constants make the display calculations faster.
+
+@<Initialize for the display computations@>=
+m_adjustment:=m_window[k]-m_offset(cur_edges);@/
+right_edge:=8*(right_col[k]-m_adjustment);@/
+min_col:=left_col[k]
+
+@ @<Set up the parameters needed for |paint_row|...@>=
+n:=0; ww:=0; m:=-1; w:=0;
+q:=sorted(p); row_transition[0]:=min_col;
+loop@+ begin if q=sentinel then d:=right_edge
+ else d:=ho(info(q));
+ mm:=(d div 8)+m_adjustment;
+ if mm<>m then
+ begin @<Record a possible transition in column |m|@>;
+ m:=mm; w:=ww;
+ end;
+ if d>=right_edge then goto found;
+ ww:=ww+(d mod 8)-zero_w;
+ q:=link(q);
+ end;
+found:@<Wind up the |paint_row| parameter calculation by inserting the
+ final transition; |goto done| if no painting is needed@>;
+
+@ Now |m| is a screen column |<right_col[k]|.
+
+@<Record a possible transition in column |m|@>=
+if w<=0 then
+ begin if ww>0 then if m>min_col then
+ begin if n=0 then
+ if already_there then
+ begin b:=white; incr(n);
+ end
+ else b:=black
+ else incr(n);
+ row_transition[n]:=m;
+ end;
+ end
+else if ww<=0 then if m>min_col then
+ begin if n=0 then b:=black;
+ incr(n); row_transition[n]:=m;
+ end
+
+@ If the entire row is |white| in the window area, we can omit painting it
+when |already_there| is false, since it has already been blanked out in
+that case.
+
+When the following code is invoked, |row_transition[n]| will be
+strictly less than |right_col[k]|.
+
+@<Wind up the |paint_row|...@>=
+if already_there or(ww>0) then
+ begin if n=0 then
+ if ww>0 then b:=black
+ else b:=white;
+ incr(n); row_transition[n]:=right_col[k];
+ end
+else if n=0 then goto done
+
+@* \[28] Dynamic linear equations.
+\MF\ users define variables implicitly by stating equations that should be
+satisfied; the computer is supposed to be smart enough to solve those equations.
+And indeed, the computer tries valiantly to do so, by distinguishing five
+different types of numeric values:
+
+\smallskip\hang
+|type(p)=known| is the nice case, when |value(p)| is the |scaled| value
+of the variable whose address is~|p|.
+
+\smallskip\hang
+|type(p)=dependent| means that |value(p)| is not present, but |dep_list(p)|
+points to a {\sl dependency list\/} that expresses the value of variable~|p|
+as a |scaled| number plus a sum of independent variables with |fraction|
+coefficients.
+
+\smallskip\hang
+|type(p)=independent| means that |value(p)=64s+m|, where |s>0| is a ``serial
+number'' reflecting the time this variable was first used in an equation;
+also |0<=m<64|, and each dependent variable
+that refers to this one is actually referring to the future value of
+this variable times~$2^m$. (Usually |m=0|, but higher degrees of
+scaling are sometimes needed to keep the coefficients in dependency lists
+from getting too large. The value of~|m| will always be even.)
+
+\smallskip\hang
+|type(p)=numeric_type| means that variable |p| hasn't appeared in an
+equation before, but it has been explicitly declared to be numeric.
+
+\smallskip\hang
+|type(p)=undefined| means that variable |p| hasn't appeared before.
+
+\smallskip\noindent
+We have actually discussed these five types in the reverse order of their
+history during a computation: Once |known|, a variable never again
+becomes |dependent|; once |dependent|, it almost never again becomes
+|independent|; once |independent|, it never again becomes |numeric_type|;
+and once |numeric_type|, it never again becomes |undefined| (except
+of course when the user specifically decides to scrap the old value
+and start again). A backward step may, however, take place: Sometimes
+a |dependent| variable becomes |independent| again, when one of the
+independent variables it depends on is reverting to |undefined|.
+
+@d s_scale=64 {the serial numbers are multiplied by this factor}
+@d new_indep(#)== {create a new independent variable}
+ begin if serial_no>el_gordo-s_scale then
+ overflow("independent variables",serial_no div s_scale);
+@:METAFONT capacity exceeded independent variables}{\quad independent variables@>
+ type(#):=independent; serial_no:=serial_no+s_scale;
+ value(#):=serial_no;
+ end
+
+@<Glob...@>=
+@!serial_no:integer; {the most recent serial number, times |s_scale|}
+
+@ @<Make variable |q+s| newly independent@>=new_indep(q+s)
+
+@ But how are dependency lists represented? It's simple: The linear combination
+$\alpha_1v_1+\cdots+\alpha_kv_k+\beta$ appears in |k+1| value nodes. If
+|q=dep_list(p)| points to this list, and if |k>0|, then |value(q)=
+@t$\alpha_1$@>| (which is a |fraction|); |info(q)| points to the location
+of $v_1$; and |link(p)| points to the dependency list
+$\alpha_2v_2+\cdots+\alpha_kv_k+\beta$. On the other hand if |k=0|,
+then |value(q)=@t$\beta$@>| (which is |scaled|) and |info(q)=null|.
+The independent variables $v_1$, \dots,~$v_k$ have been sorted so that
+they appear in decreasing order of their |value| fields (i.e., of
+their serial numbers). \ (It is convenient to use decreasing order,
+since |value(null)=0|. If the independent variables were not sorted by
+serial number but by some other criterion, such as their location in |mem|,
+the equation-solving mechanism would be too system-dependent, because
+the ordering can affect the computed results.)
+
+The |link| field in the node that contains the constant term $\beta$ is
+called the {\sl final link\/} of the dependency list. \MF\ maintains
+a doubly-linked master list of all dependency lists, in terms of a permanently
+allocated node
+in |mem| called |dep_head|. If there are no dependencies, we have
+|link(dep_head)=dep_head| and |prev_dep(dep_head)=dep_head|;
+otherwise |link(dep_head)| points to the first dependent variable, say~|p|,
+and |prev_dep(p)=dep_head|. We have |type(p)=dependent|, and |dep_list(p)|
+points to its dependency list. If the final link of that dependency list
+occurs in location~|q|, then |link(q)| points to the next dependent
+variable (say~|r|); and we have |prev_dep(r)=q|, etc.
+
+@d dep_list(#)==link(value_loc(#))
+ {half of the |value| field in a |dependent| variable}
+@d prev_dep(#)==info(value_loc(#))
+ {the other half; makes a doubly linked list}
+@d dep_node_size=2 {the number of words per dependency node}
+
+@<Initialize table entries...@>= serial_no:=0;
+link(dep_head):=dep_head; prev_dep(dep_head):=dep_head;
+info(dep_head):=null; dep_list(dep_head):=null;
+
+@ Actually the description above contains a little white lie. There's
+another kind of variable called |proto_dependent|, which is
+just like a |dependent| one except that the $\alpha$ coefficients
+in its dependency list are |scaled| instead of being fractions.
+Proto-dependency lists are mixed with dependency lists in the
+nodes reachable from |dep_head|.
+
+@ Here is a procedure that prints a dependency list in symbolic form.
+The second parameter should be either |dependent| or |proto_dependent|,
+to indicate the scaling of the coefficients.
+
+@<Declare subroutines for printing expressions@>=
+procedure print_dependency(@!p:pointer;@!t:small_number);
+label exit;
+var @!v:integer; {a coefficient}
+@!pp,@!q:pointer; {for list manipulation}
+begin pp:=p;
+loop@+ begin v:=abs(value(p)); q:=info(p);
+ if q=null then {the constant term}
+ begin if (v<>0)or(p=pp) then
+ begin if value(p)>0 then if p<>pp then print_char("+");
+ print_scaled(value(p));
+ end;
+ return;
+ end;
+ @<Print the coefficient, unless it's $\pm1.0$@>;
+ if type(q)<>independent then confusion("dep");
+@:this can't happen dep}{\quad dep@>
+ print_variable_name(q); v:=value(q) mod s_scale;
+ while v>0 do
+ begin print("*4"); v:=v-2;
+ end;
+ p:=link(p);
+ end;
+exit:end;
+
+@ @<Print the coefficient, unless it's $\pm1.0$@>=
+if value(p)<0 then print_char("-")
+else if p<>pp then print_char("+");
+if t=dependent then v:=round_fraction(v);
+if v<>unity then print_scaled(v)
+
+@ The maximum absolute value of a coefficient in a given dependency list
+is returned by the following simple function.
+
+@p function max_coef(@!p:pointer):fraction;
+var @!x:fraction; {the maximum so far}
+begin x:=0;
+while info(p)<>null do
+ begin if abs(value(p))>x then x:=abs(value(p));
+ p:=link(p);
+ end;
+max_coef:=x;
+end;
+
+@ One of the main operations needed on dependency lists is to add a multiple
+of one list to the other; we call this |p_plus_fq|, where |p| and~|q| point
+to dependency lists and |f| is a fraction.
+
+If the coefficient of any independent variable becomes |coef_bound| or
+more, in absolute value, this procedure changes the type of that variable
+to `|independent_needing_fix|', and sets the global variable |fix_needed|
+to~|true|. The value of $|coef_bound|=\mu$ is chosen so that
+$\mu^2+\mu<8$; this means that the numbers we deal with won't
+get too large. (Instead of the ``optimum'' $\mu=(\sqrt{33}-1)/2\approx
+2.3723$, the safer value 7/3 is taken as the threshold.)
+
+The changes mentioned in the preceding paragraph are actually done only if
+the global variable |watch_coefs| is |true|. But it usually is; in fact,
+it is |false| only when \MF\ is making a dependency list that will soon
+be equated to zero.
+
+Several procedures that act on dependency lists, including |p_plus_fq|,
+set the global variable |dep_final| to the final (constant term) node of
+the dependency list that they produce.
+
+@d coef_bound==@'4525252525 {|fraction| approximation to 7/3}
+@d independent_needing_fix=0
+
+@<Glob...@>=
+@!fix_needed:boolean; {does at least one |independent| variable need scaling?}
+@!watch_coefs:boolean; {should we scale coefficients that exceed |coef_bound|?}
+@!dep_final:pointer; {location of the constant term and final link}
+
+@ @<Set init...@>=
+fix_needed:=false; watch_coefs:=true;
+
+@ The |p_plus_fq| procedure has a fourth parameter, |t|, that should be
+set to |proto_dependent| if |p| is a proto-dependency list. In this
+case |f| will be |scaled|, not a |fraction|. Similarly, the fifth parameter~|tt|
+should be |proto_dependent| if |q| is a proto-dependency list.
+
+List |q| is unchanged by the operation; but list |p| is totally destroyed.
+
+The final link of the dependency list or proto-dependency list returned
+by |p_plus_fq| is the same as the original final link of~|p|. Indeed, the
+constant term of the result will be located in the same |mem| location
+as the original constant term of~|p|.
+
+Coefficients of the result are assumed to be zero if they are less than
+a certain threshold. This compensates for inevitable rounding errors,
+and tends to make more variables `|known|'. The threshold is approximately
+$10^{-5}$ in the case of normal dependency lists, $10^{-4}$ for
+proto-dependencies.
+
+@d fraction_threshold=2685 {a |fraction| coefficient less than this is zeroed}
+@d half_fraction_threshold=1342 {half of |fraction_threshold|}
+@d scaled_threshold=8 {a |scaled| coefficient less than this is zeroed}
+@d half_scaled_threshold=4 {half of |scaled_threshold|}
+
+@<Declare basic dependency-list subroutines@>=
+function p_plus_fq(@!p:pointer;@!f:integer;@!q:pointer;
+ @!t,@!tt:small_number):pointer;
+label done;
+var @!pp,@!qq:pointer; {|info(p)| and |info(q)|, respectively}
+@!r,@!s:pointer; {for list manipulation}
+@!threshold:integer; {defines a neighborhood of zero}
+@!v:integer; {temporary register}
+begin if t=dependent then threshold:=fraction_threshold
+else threshold:=scaled_threshold;
+r:=temp_head; pp:=info(p); qq:=info(q);
+loop@+ if pp=qq then
+ if pp=null then goto done
+ else @<Contribute a term from |p|, plus |f| times the
+ corresponding term from |q|@>
+ else if value(pp)<value(qq) then
+ @<Contribute a term from |q|, multiplied by~|f|@>
+ else begin link(r):=p; r:=p; p:=link(p); pp:=info(p);
+ end;
+done: if t=dependent then
+ value(p):=slow_add(value(p),take_fraction(value(q),f))
+else value(p):=slow_add(value(p),take_scaled(value(q),f));
+link(r):=p; dep_final:=p; p_plus_fq:=link(temp_head);
+end;
+
+@ @<Contribute a term from |p|, plus |f|...@>=
+begin if tt=dependent then v:=value(p)+take_fraction(f,value(q))
+else v:=value(p)+take_scaled(f,value(q));
+value(p):=v; s:=p; p:=link(p);
+if abs(v)<threshold then free_node(s,dep_node_size)
+else begin if abs(v)>=coef_bound then if watch_coefs then
+ begin type(qq):=independent_needing_fix; fix_needed:=true;
+ end;
+ link(r):=s; r:=s;
+ end;
+pp:=info(p); q:=link(q); qq:=info(q);
+end
+
+@ @<Contribute a term from |q|, multiplied by~|f|@>=
+begin if tt=dependent then v:=take_fraction(f,value(q))
+else v:=take_scaled(f,value(q));
+if abs(v)>half(threshold) then
+ begin s:=get_node(dep_node_size); info(s):=qq; value(s):=v;
+ if abs(v)>=coef_bound then if watch_coefs then
+ begin type(qq):=independent_needing_fix; fix_needed:=true;
+ end;
+ link(r):=s; r:=s;
+ end;
+q:=link(q); qq:=info(q);
+end
+
+@ It is convenient to have another subroutine for the special case
+of |p_plus_fq| when |f=1.0|. In this routine lists |p| and |q| are
+both of the same type~|t| (either |dependent| or |proto_dependent|).
+
+@p function p_plus_q(@!p:pointer;@!q:pointer;@!t:small_number):pointer;
+label done;
+var @!pp,@!qq:pointer; {|info(p)| and |info(q)|, respectively}
+@!r,@!s:pointer; {for list manipulation}
+@!threshold:integer; {defines a neighborhood of zero}
+@!v:integer; {temporary register}
+begin if t=dependent then threshold:=fraction_threshold
+else threshold:=scaled_threshold;
+r:=temp_head; pp:=info(p); qq:=info(q);
+loop@+ if pp=qq then
+ if pp=null then goto done
+ else @<Contribute a term from |p|, plus the
+ corresponding term from |q|@>
+ else if value(pp)<value(qq) then
+ begin s:=get_node(dep_node_size); info(s):=qq; value(s):=value(q);
+ q:=link(q); qq:=info(q); link(r):=s; r:=s;
+ end
+ else begin link(r):=p; r:=p; p:=link(p); pp:=info(p);
+ end;
+done: value(p):=slow_add(value(p),value(q));
+link(r):=p; dep_final:=p; p_plus_q:=link(temp_head);
+end;
+
+@ @<Contribute a term from |p|, plus the...@>=
+begin v:=value(p)+value(q);
+value(p):=v; s:=p; p:=link(p); pp:=info(p);
+if abs(v)<threshold then free_node(s,dep_node_size)
+else begin if abs(v)>=coef_bound then if watch_coefs then
+ begin type(qq):=independent_needing_fix; fix_needed:=true;
+ end;
+ link(r):=s; r:=s;
+ end;
+q:=link(q); qq:=info(q);
+end
+
+@ A somewhat simpler routine will multiply a dependency list
+by a given constant~|v|. The constant is either a |fraction| less than
+|fraction_one|, or it is |scaled|. In the latter case we might be forced to
+convert a dependency list to a proto-dependency list.
+Parameters |t0| and |t1| are the list types before and after;
+they should agree unless |t0=dependent| and |t1=proto_dependent|
+and |v_is_scaled=true|.
+
+@p function p_times_v(@!p:pointer;@!v:integer;
+ @!t0,@!t1:small_number;@!v_is_scaled:boolean):pointer;
+var @!r,@!s:pointer; {for list manipulation}
+@!w:integer; {tentative coefficient}
+@!threshold:integer;
+@!scaling_down:boolean;
+begin if t0<>t1 then scaling_down:=true@+else scaling_down:=not v_is_scaled;
+if t1=dependent then threshold:=half_fraction_threshold
+else threshold:=half_scaled_threshold;
+r:=temp_head;
+while info(p)<>null do
+ begin if scaling_down then w:=take_fraction(v,value(p))
+ else w:=take_scaled(v,value(p));
+ if abs(w)<=threshold then
+ begin s:=link(p); free_node(p,dep_node_size); p:=s;
+ end
+ else begin if abs(w)>=coef_bound then
+ begin fix_needed:=true; type(info(p)):=independent_needing_fix;
+ end;
+ link(r):=p; r:=p; value(p):=w; p:=link(p);
+ end;
+ end;
+link(r):=p;
+if v_is_scaled then value(p):=take_scaled(value(p),v)
+else value(p):=take_fraction(value(p),v);
+p_times_v:=link(temp_head);
+end;
+
+@ Similarly, we sometimes need to divide a dependency list
+by a given |scaled| constant.
+
+@<Declare basic dependency-list subroutines@>=
+function p_over_v(@!p:pointer;@!v:scaled;
+ @!t0,@!t1:small_number):pointer;
+var @!r,@!s:pointer; {for list manipulation}
+@!w:integer; {tentative coefficient}
+@!threshold:integer;
+@!scaling_down:boolean;
+begin if t0<>t1 then scaling_down:=true@+else scaling_down:=false;
+if t1=dependent then threshold:=half_fraction_threshold
+else threshold:=half_scaled_threshold;
+r:=temp_head;
+while info(p)<>null do
+ begin if scaling_down then
+ if abs(v)<@'2000000 then w:=make_scaled(value(p),v*@'10000)
+ else w:=make_scaled(round_fraction(value(p)),v)
+ else w:=make_scaled(value(p),v);
+ if abs(w)<=threshold then
+ begin s:=link(p); free_node(p,dep_node_size); p:=s;
+ end
+ else begin if abs(w)>=coef_bound then
+ begin fix_needed:=true; type(info(p)):=independent_needing_fix;
+ end;
+ link(r):=p; r:=p; value(p):=w; p:=link(p);
+ end;
+ end;
+link(r):=p; value(p):=make_scaled(value(p),v);
+p_over_v:=link(temp_head);
+end;
+
+@ Here's another utility routine for dependency lists. When an independent
+variable becomes dependent, we want to remove it from all existing
+dependencies. The |p_with_x_becoming_q| function computes the
+dependency list of~|p| after variable~|x| has been replaced by~|q|.
+
+This procedure has basically the same calling conventions as |p_plus_fq|:
+List~|q| is unchanged; list~|p| is destroyed; the constant node and the
+final link are inherited from~|p|; and the fourth parameter tells whether
+or not |p| is |proto_dependent|. However, the global variable |dep_final|
+is not altered if |x| does not occur in list~|p|.
+
+@p function p_with_x_becoming_q(@!p,@!x,@!q:pointer;@!t:small_number):pointer;
+var @!r,@!s:pointer; {for list manipulation}
+@!v:integer; {coefficient of |x|}
+@!sx:integer; {serial number of |x|}
+begin s:=p; r:=temp_head; sx:=value(x);
+while value(info(s))>sx do
+ begin r:=s; s:=link(s);
+ end;
+if info(s)<>x then p_with_x_becoming_q:=p
+else begin link(temp_head):=p; link(r):=link(s); v:=value(s);
+ free_node(s,dep_node_size);
+ p_with_x_becoming_q:=p_plus_fq(link(temp_head),v,q,t,dependent);
+ end;
+end;
+
+@ Here's a simple procedure that reports an error when a variable
+has just received a known value that's out of the required range.
+
+@<Declare basic dependency-list subroutines@>=
+procedure val_too_big(@!x:scaled);
+begin if internal[warning_check]>0 then
+ begin print_err("Value is too large ("); print_scaled(x); print_char(")");
+@.Value is too large@>
+ help4("The equation I just processed has given some variable")@/
+ ("a value of 4096 or more. Continue and I'll try to cope")@/
+ ("with that big value; but it might be dangerous.")@/
+ ("(Set warningcheck:=0 to suppress this message.)");
+ error;
+ end;
+end;
+
+@ When a dependent variable becomes known, the following routine
+removes its dependency list. Here |p| points to the variable, and
+|q| points to the dependency list (which is one node long).
+
+@<Declare basic dependency-list subroutines@>=
+procedure make_known(@!p,@!q:pointer);
+var @!t:dependent..proto_dependent; {the previous type}
+begin prev_dep(link(q)):=prev_dep(p);
+link(prev_dep(p)):=link(q); t:=type(p);
+type(p):=known; value(p):=value(q); free_node(q,dep_node_size);
+if abs(value(p))>=fraction_one then val_too_big(value(p));
+if internal[tracing_equations]>0 then if interesting(p) then
+ begin begin_diagnostic; print_nl("#### ");
+@:]]]\#\#\#\#_}{\.{\#\#\#\#}@>
+ print_variable_name(p); print_char("="); print_scaled(value(p));
+ end_diagnostic(false);
+ end;
+if cur_exp=p then if cur_type=t then
+ begin cur_type:=known; cur_exp:=value(p);
+ free_node(p,value_node_size);
+ end;
+end;
+
+@ The |fix_dependencies| routine is called into action when |fix_needed|
+has been triggered. The program keeps a list~|s| of independent variables
+whose coefficients must be divided by~4.
+
+In unusual cases, this fixup process might reduce one or more coefficients
+to zero, so that a variable will become known more or less by default.
+
+@<Declare basic dependency-list subroutines@>=
+procedure fix_dependencies;
+label done;
+var @!p,@!q,@!r,@!s,@!t:pointer; {list manipulation registers}
+@!x:pointer; {an independent variable}
+begin r:=link(dep_head); s:=null;
+while r<>dep_head do
+ begin t:=r;
+ @<Run through the dependency list for variable |t|, fixing
+ all nodes, and ending with final link~|q|@>;
+ r:=link(q);
+ if q=dep_list(t) then make_known(t,q);
+ end;
+while s<>null do
+ begin p:=link(s); x:=info(s); free_avail(s); s:=p;
+ type(x):=independent; value(x):=value(x)+2;
+ end;
+fix_needed:=false;
+end;
+
+@ @d independent_being_fixed=1 {this variable already appears in |s|}
+
+@<Run through the dependency list for variable |t|...@>=
+r:=value_loc(t); {|link(r)=dep_list(t)|}
+loop@+ begin q:=link(r); x:=info(q);
+ if x=null then goto done;
+ if type(x)<=independent_being_fixed then
+ begin if type(x)<independent_being_fixed then
+ begin p:=get_avail; link(p):=s; s:=p;
+ info(s):=x; type(x):=independent_being_fixed;
+ end;
+ value(q):=value(q) div 4;
+ if value(q)=0 then
+ begin link(r):=link(q); free_node(q,dep_node_size); q:=r;
+ end;
+ end;
+ r:=q;
+ end;
+done:
+
+@ The |new_dep| routine installs a dependency list~|p| into the value node~|q|,
+linking it into the list of all known dependencies. We assume that
+|dep_final| points to the final node of list~|p|.
+
+@p procedure new_dep(@!q,@!p:pointer);
+var @!r:pointer; {what used to be the first dependency}
+begin dep_list(q):=p; prev_dep(q):=dep_head;
+r:=link(dep_head); link(dep_final):=r; prev_dep(r):=dep_final;
+link(dep_head):=q;
+end;
+
+@ Here is one of the ways a dependency list gets started.
+The |const_dependency| routine produces a list that has nothing but
+a constant term.
+
+@p function const_dependency(@!v:scaled):pointer;
+begin dep_final:=get_node(dep_node_size);
+value(dep_final):=v; info(dep_final):=null;
+const_dependency:=dep_final;
+end;
+
+@ And here's a more interesting way to start a dependency list from scratch:
+The parameter to |single_dependency| is the location of an
+independent variable~|x|, and the result is the simple dependency list
+`|x+0|'.
+
+In the unlikely event that the given independent variable has been doubled so
+often that we can't refer to it with a nonzero coefficient,
+|single_dependency| returns the simple list `0'. This case can be
+recognized by testing that the returned list pointer is equal to
+|dep_final|.
+
+@p function single_dependency(@!p:pointer):pointer;
+var @!q:pointer; {the new dependency list}
+@!m:integer; {the number of doublings}
+begin m:=value(p) mod s_scale;
+if m>28 then single_dependency:=const_dependency(0)
+else begin q:=get_node(dep_node_size);
+ value(q):=two_to_the[28-m]; info(q):=p;@/
+ link(q):=const_dependency(0); single_dependency:=q;
+ end;
+end;
+
+@ We sometimes need to make an exact copy of a dependency list.
+
+@p function copy_dep_list(@!p:pointer):pointer;
+label done;
+var @!q:pointer; {the new dependency list}
+begin q:=get_node(dep_node_size); dep_final:=q;
+loop@+ begin info(dep_final):=info(p); value(dep_final):=value(p);
+ if info(dep_final)=null then goto done;
+ link(dep_final):=get_node(dep_node_size);
+ dep_final:=link(dep_final); p:=link(p);
+ end;
+done:copy_dep_list:=q;
+end;
+
+@ But how do variables normally become known? Ah, now we get to the heart of the
+equation-solving mechanism. The |linear_eq| procedure is given a |dependent|
+or |proto_dependent| list,~|p|, in which at least one independent variable
+appears. It equates this list to zero, by choosing an independent variable
+with the largest coefficient and making it dependent on the others. The
+newly dependent variable is eliminated from all current dependencies,
+thereby possibly making other dependent variables known.
+
+The given list |p| is, of course, totally destroyed by all this processing.
+
+@p procedure linear_eq(@!p:pointer;@!t:small_number);
+var @!q,@!r,@!s:pointer; {for link manipulation}
+@!x:pointer; {the variable that loses its independence}
+@!n:integer; {the number of times |x| had been halved}
+@!v:integer; {the coefficient of |x| in list |p|}
+@!prev_r:pointer; {lags one step behind |r|}
+@!final_node:pointer; {the constant term of the new dependency list}
+@!w:integer; {a tentative coefficient}
+begin @<Find a node |q| in list |p| whose coefficient |v| is largest@>;
+x:=info(q); n:=value(x) mod s_scale;@/
+@<Divide list |p| by |-v|, removing node |q|@>;
+if internal[tracing_equations]>0 then @<Display the new dependency@>;
+@<Simplify all existing dependencies by substituting for |x|@>;
+@<Change variable |x| from |independent| to |dependent| or |known|@>;
+if fix_needed then fix_dependencies;
+end;
+
+@ @<Find a node |q| in list |p| whose coefficient |v| is largest@>=
+q:=p; r:=link(p); v:=value(q);
+while info(r)<>null do
+ begin if abs(value(r))>abs(v) then
+ begin q:=r; v:=value(r);
+ end;
+ r:=link(r);
+ end
+
+@ Here we want to change the coefficients from |scaled| to |fraction|,
+except in the constant term. In the common case of a trivial equation
+like `\.{x=3.14}', we will have |v=-fraction_one|, |q=p|, and |t=dependent|.
+
+@<Divide list |p| by |-v|, removing node |q|@>=
+s:=temp_head; link(s):=p; r:=p;
+repeat if r=q then
+ begin link(s):=link(r); free_node(r,dep_node_size);
+ end
+else begin w:=make_fraction(value(r),v);
+ if abs(w)<=half_fraction_threshold then
+ begin link(s):=link(r); free_node(r,dep_node_size);
+ end
+ else begin value(r):=-w; s:=r;
+ end;
+ end;
+r:=link(s);
+until info(r)=null;
+if t=proto_dependent then value(r):=-make_scaled(value(r),v)
+else if v<>-fraction_one then value(r):=-make_fraction(value(r),v);
+final_node:=r; p:=link(temp_head)
+
+@ @<Display the new dependency@>=
+if interesting(x) then
+ begin begin_diagnostic; print_nl("## "); print_variable_name(x);
+@:]]]\#\#_}{\.{\#\#}@>
+ w:=n;
+ while w>0 do
+ begin print("*4"); w:=w-2;
+ end;
+ print_char("="); print_dependency(p,dependent); end_diagnostic(false);
+ end
+
+@ @<Simplify all existing dependencies by substituting for |x|@>=
+prev_r:=dep_head; r:=link(dep_head);
+while r<>dep_head do
+ begin s:=dep_list(r); q:=p_with_x_becoming_q(s,x,p,type(r));
+ if info(q)=null then make_known(r,q)
+ else begin dep_list(r):=q;
+ repeat q:=link(q);
+ until info(q)=null;
+ prev_r:=q;
+ end;
+ r:=link(prev_r);
+ end
+
+@ @<Change variable |x| from |independent| to |dependent| or |known|@>=
+if n>0 then @<Divide list |p| by $2^n$@>;
+if info(p)=null then
+ begin type(x):=known;
+ value(x):=value(p);
+ if abs(value(x))>=fraction_one then val_too_big(value(x));
+ free_node(p,dep_node_size);
+ if cur_exp=x then if cur_type=independent then
+ begin cur_exp:=value(x); cur_type:=known;
+ free_node(x,value_node_size);
+ end;
+ end
+else begin type(x):=dependent; dep_final:=final_node; new_dep(x,p);
+ if cur_exp=x then if cur_type=independent then cur_type:=dependent;
+ end
+
+@ @<Divide list |p| by $2^n$@>=
+begin s:=temp_head; link(temp_head):=p; r:=p;
+repeat if n>30 then w:=0
+else w:=value(r) div two_to_the[n];
+if (abs(w)<=half_fraction_threshold)and(info(r)<>null) then
+ begin link(s):=link(r);
+ free_node(r,dep_node_size);
+ end
+else begin value(r):=w; s:=r;
+ end;
+r:=link(s);
+until info(s)=null;
+p:=link(temp_head);
+end
+
+@ The |check_mem| procedure, which is used only when \MF\ is being
+debugged, makes sure that the current dependency lists are well formed.
+
+@<Check the list of linear dependencies@>=
+q:=dep_head; p:=link(q);
+while p<>dep_head do
+ begin if prev_dep(p)<>q then
+ begin print_nl("Bad PREVDEP at "); print_int(p);
+@.Bad PREVDEP...@>
+ end;
+ p:=dep_list(p); r:=inf_val;
+ repeat if value(info(p))>=value(r) then
+ begin print_nl("Out of order at "); print_int(p);
+@.Out of order...@>
+ end;
+ r:=info(p); q:=p; p:=link(q);
+ until r=null;
+ end
+
+@* \[29] Dynamic nonlinear equations.
+Variables of numeric type are maintained by the general scheme of
+independent, dependent, and known values that we have just studied;
+and the components of pair and transform variables are handled in the
+same way. But \MF\ also has five other types of values: \&{boolean},
+\&{string}, \&{pen}, \&{path}, and \&{picture}; what about them?
+
+Equations are allowed between nonlinear quantities, but only in a
+simple form. Two variables that haven't yet been assigned values are
+either equal to each other, or they're not.
+
+Before a boolean variable has received a value, its type is |unknown_boolean|;
+similarly, there are variables whose type is |unknown_string|, |unknown_pen|,
+|unknown_path|, and |unknown_picture|. In such cases the value is either
+|null| (which means that no other variables are equivalent to this one), or
+it points to another variable of the same undefined type. The pointers in the
+latter case form a cycle of nodes, which we shall call a ``ring.''
+Rings of undefined variables may include capsules, which arise as
+intermediate results within expressions or as \&{expr} parameters to macros.
+
+When one member of a ring receives a value, the same value is given to
+all the other members. In the case of paths and pictures, this implies
+making separate copies of a potentially large data structure; users should
+restrain their enthusiasm for such generality, unless they have lots and
+lots of memory space.
+
+@ The following procedure is called when a capsule node is being
+added to a ring (e.g., when an unknown variable is mentioned in an expression).
+
+@p function new_ring_entry(@!p:pointer):pointer;
+var q:pointer; {the new capsule node}
+begin q:=get_node(value_node_size); name_type(q):=capsule;
+type(q):=type(p);
+if value(p)=null then value(q):=p@+else value(q):=value(p);
+value(p):=q;
+new_ring_entry:=q;
+end;
+
+@ Conversely, we might delete a capsule or a variable before it becomes known.
+The following procedure simply detaches a quantity from its ring,
+without recycling the storage.
+
+@<Declare the recycling subroutines@>=
+procedure ring_delete(@!p:pointer);
+var @!q:pointer;
+begin q:=value(p);
+if q<>null then if q<>p then
+ begin while value(q)<>p do q:=value(q);
+ value(q):=value(p);
+ end;
+end;
+
+@ Eventually there might be an equation that assigns values to all of the
+variables in a ring. The |nonlinear_eq| subroutine does the necessary
+propagation of values.
+
+If the parameter |flush_p| is |true|, node |p| itself needn't receive a
+value; it will soon be recycled.
+
+@p procedure nonlinear_eq(@!v:integer;@!p:pointer;@!flush_p:boolean);
+var @!t:small_number; {the type of ring |p|}
+@!q,@!r:pointer; {link manipulation registers}
+begin t:=type(p)-unknown_tag; q:=value(p);
+if flush_p then type(p):=vacuous@+else p:=q;
+repeat r:=value(q); type(q):=t;
+case t of
+boolean_type: value(q):=v;
+string_type: begin value(q):=v; add_str_ref(v);
+ end;
+pen_type: begin value(q):=v; add_pen_ref(v);
+ end;
+path_type: value(q):=copy_path(v);
+picture_type: value(q):=copy_edges(v);
+end; {there ain't no more cases}
+q:=r;
+until q=p;
+end;
+
+@ If two members of rings are equated, and if they have the same type,
+the |ring_merge| procedure is called on to make them equivalent.
+
+@p procedure ring_merge(@!p,@!q:pointer);
+label exit;
+var @!r:pointer; {traverses one list}
+begin r:=value(p);
+while r<>p do
+ begin if r=q then
+ begin @<Exclaim about a redundant equation@>;
+ return;
+ end;
+ r:=value(r);
+ end;
+r:=value(p); value(p):=value(q); value(q):=r;
+exit:end;
+
+@ @<Exclaim about a redundant equation@>=
+begin print_err("Redundant equation");@/
+@.Redundant equation@>
+help2("I already knew that this equation was true.")@/
+ ("But perhaps no harm has been done; let's continue.");@/
+put_get_error;
+end
+
+@* \[30] Introduction to the syntactic routines.
+Let's pause a moment now and try to look at the Big Picture.
+The \MF\ program consists of three main parts: syntactic routines,
+semantic routines, and output routines. The chief purpose of the
+syntactic routines is to deliver the user's input to the semantic routines,
+while parsing expressions and locating operators and operands. The
+semantic routines act as an interpreter responding to these operators,
+which may be regarded as commands. And the output routines are
+periodically called on to produce compact font descriptions that can be
+used for typesetting or for making interim proof drawings. We have
+discussed the basic data structures and many of the details of semantic
+operations, so we are good and ready to plunge into the part of \MF\ that
+actually controls the activities.
+
+Our current goal is to come to grips with the |get_next| procedure,
+which is the keystone of \MF's input mechanism. Each call of |get_next|
+sets the value of three variables |cur_cmd|, |cur_mod|, and |cur_sym|,
+representing the next input token.
+$$\vbox{\halign{#\hfil\cr
+ \hbox{|cur_cmd| denotes a command code from the long list of codes
+ given earlier;}\cr
+ \hbox{|cur_mod| denotes a modifier of the command code;}\cr
+ \hbox{|cur_sym| is the hash address of the symbolic token that was
+ just scanned,}\cr
+ \hbox{\qquad or zero in the case of a numeric or string
+ or capsule token.}\cr}}$$
+Underlying this external behavior of |get_next| is all the machinery
+necessary to convert from character files to tokens. At a given time we
+may be only partially finished with the reading of several files (for
+which \&{input} was specified), and partially finished with the expansion
+of some user-defined macros and/or some macro parameters, and partially
+finished reading some text that the user has inserted online,
+and so on. When reading a character file, the characters must be
+converted to tokens; comments and blank spaces must
+be removed, numeric and string tokens must be evaluated.
+
+To handle these situations, which might all be present simultaneously,
+\MF\ uses various stacks that hold information about the incomplete
+activities, and there is a finite state control for each level of the
+input mechanism. These stacks record the current state of an implicitly
+recursive process, but the |get_next| procedure is not recursive.
+
+@<Glob...@>=
+@!cur_cmd: eight_bits; {current command set by |get_next|}
+@!cur_mod: integer; {operand of current command}
+@!cur_sym: halfword; {hash address of current symbol}
+
+@ The |print_cmd_mod| routine prints a symbolic interpretation of a
+command code and its modifier.
+It consists of a rather tedious sequence of print
+commands, and most of it is essentially an inverse to the |primitive|
+routine that enters a \MF\ primitive into |hash| and |eqtb|. Therefore almost
+all of this procedure appears elsewhere in the program, together with the
+corresponding |primitive| calls.
+
+@<Declare the procedure called |print_cmd_mod|@>=
+procedure print_cmd_mod(@!c,@!m:integer);
+begin case c of
+@t\4@>@<Cases of |print_cmd_mod| for symbolic printing of primitives@>@/
+othercases print("[unknown command code!]")
+endcases;
+end;
+
+@ Here is a procedure that displays a given command in braces, in the
+user's transcript file.
+
+@d show_cur_cmd_mod==show_cmd_mod(cur_cmd,cur_mod)
+
+@p procedure show_cmd_mod(@!c,@!m:integer);
+begin begin_diagnostic; print_nl("{");
+print_cmd_mod(c,m); print_char("}");
+end_diagnostic(false);
+end;
+
+@* \[31] Input stacks and states.
+The state of \MF's input mechanism appears in the input stack, whose
+entries are records with five fields, called |index|, |start|, |loc|,
+|limit|, and |name|. The top element of this stack is maintained in a
+global variable for which no subscripting needs to be done; the other
+elements of the stack appear in an array. Hence the stack is declared thus:
+
+@<Types...@>=
+@!in_state_record = record
+ @!index_field: quarterword;
+ @!start_field,@!loc_field, @!limit_field, @!name_field: halfword;
+ end;
+
+@ @<Glob...@>=
+@!input_stack : array[0..stack_size] of in_state_record;
+@!input_ptr : 0..stack_size; {first unused location of |input_stack|}
+@!max_in_stack: 0..stack_size; {largest value of |input_ptr| when pushing}
+@!cur_input : in_state_record; {the ``top'' input state}
+
+@ We've already defined the special variable |@!loc==cur_input.loc_field|
+in our discussion of basic input-output routines. The other components of
+|cur_input| are defined in the same way:
+
+@d index==cur_input.index_field {reference for buffer information}
+@d start==cur_input.start_field {starting position in |buffer|}
+@d limit==cur_input.limit_field {end of current line in |buffer|}
+@d name==cur_input.name_field {name of the current file}
+
+@ Let's look more closely now at the five control variables
+(|index|,~|start|,~|loc|,~|limit|,~|name|),
+assuming that \MF\ is reading a line of characters that have been input
+from some file or from the user's terminal. There is an array called
+|buffer| that acts as a stack of all lines of characters that are
+currently being read from files, including all lines on subsidiary
+levels of the input stack that are not yet completed. \MF\ will return to
+the other lines when it is finished with the present input file.
+
+(Incidentally, on a machine with byte-oriented addressing, it would be
+appropriate to combine |buffer| with the |str_pool| array,
+letting the buffer entries grow downward from the top of the string pool
+and checking that these two tables don't bump into each other.)
+
+The line we are currently working on begins in position |start| of the
+buffer; the next character we are about to read is |buffer[loc]|; and
+|limit| is the location of the last character present. We always have
+|loc<=limit|. For convenience, |buffer[limit]| has been set to |"%"|, so
+that the end of a line is easily sensed.
+
+The |name| variable is a string number that designates the name of
+the current file, if we are reading a text file. It is 0 if we
+are reading from the terminal for normal input, or 1 if we are executing a
+\&{readstring} command, or 2 if we are reading a string that was
+moved into the buffer by \&{scantokens}.
+
+@ Additional information about the current line is available via the
+|index| variable, which counts how many lines of characters are present
+in the buffer below the current level. We have |index=0| when reading
+from the terminal and prompting the user for each line; then if the user types,
+e.g., `\.{input font}', we will have |index=1| while reading
+the file \.{font.mf}. However, it does not follow that |index| is the
+same as the input stack pointer, since many of the levels on the input
+stack may come from token lists.
+
+The global variable |in_open| is equal to the |index|
+value of the highest non-token-list level. Thus, the number of partially read
+lines in the buffer is |in_open+1|, and we have |in_open=index|
+when we are not reading a token list.
+
+If we are not currently reading from the terminal,
+we are reading from the file variable |input_file[index]|. We use
+the notation |terminal_input| as a convenient abbreviation for |name=0|,
+and |cur_file| as an abbreviation for |input_file[index]|.
+
+The global variable |line| contains the line number in the topmost
+open file, for use in error messages. If we are not reading from
+the terminal, |line_stack[index]| holds the line number for the
+enclosing level, so that |line| can be restored when the current
+file has been read.
+
+If more information about the input state is needed, it can be
+included in small arrays like those shown here. For example,
+the current page or segment number in the input file might be
+put into a variable |@!page|, maintained for enclosing levels in
+`\ignorespaces|@!page_stack:array[1..max_in_open] of integer|\unskip'
+by analogy with |line_stack|.
+@^system dependencies@>
+
+@d terminal_input==(name=0) {are we reading from the terminal?}
+@d cur_file==input_file[index] {the current |alpha_file| variable}
+
+@<Glob...@>=
+@!in_open : 0..max_in_open; {the number of lines in the buffer, less one}
+@!open_parens : 0..max_in_open; {the number of open text files}
+@!input_file : array[1..max_in_open] of alpha_file;
+@!line : integer; {current line number in the current source file}
+@!line_stack : array[1..max_in_open] of integer;
+
+@ However, all this discussion about input state really applies only to the
+case that we are inputting from a file. There is another important case,
+namely when we are currently getting input from a token list. In this case
+|index>max_in_open|, and the conventions about the other state variables
+are different:
+
+\yskip\hang|loc| is a pointer to the current node in the token list, i.e.,
+the node that will be read next. If |loc=null|, the token list has been
+fully read.
+
+\yskip\hang|start| points to the first node of the token list; this node
+may or may not contain a reference count, depending on the type of token
+list involved.
+
+\yskip\hang|token_type|, which takes the place of |index| in the
+discussion above, is a code number that explains what kind of token list
+is being scanned.
+
+\yskip\hang|name| points to the |eqtb| address of the control sequence
+being expanded, if the current token list is a macro not defined by
+\&{vardef}. Macros defined by \&{vardef} have |name=null|; their name
+can be deduced by looking at their first two parameters.
+
+\yskip\hang|param_start|, which takes the place of |limit|, tells where
+the parameters of the current macro or loop text begin in the |param_stack|.
+
+\yskip\noindent The |token_type| can take several values, depending on
+where the current token list came from:
+
+\yskip
+\indent|forever_text|, if the token list being scanned is the body of
+a \&{forever} loop;
+
+\indent|loop_text|, if the token list being scanned is the body of
+a \&{for} or \&{forsuffixes} loop;
+
+\indent|parameter|, if a \&{text} or \&{suffix} parameter is being scanned;
+
+\indent|backed_up|, if the token list being scanned has been inserted as
+`to be read again'.
+
+\indent|inserted|, if the token list being scanned has been inserted as
+part of error recovery;
+
+\indent|macro|, if the expansion of a user-defined symbolic token is being
+scanned.
+
+\yskip\noindent
+The token list begins with a reference count if and only if |token_type=
+macro|.
+@^reference counts@>
+
+@d token_type==index {type of current token list}
+@d token_state==(index>max_in_open) {are we scanning a token list?}
+@d file_state==(index<=max_in_open) {are we scanning a file line?}
+@d param_start==limit {base of macro parameters in |param_stack|}
+@d forever_text=max_in_open+1 {|token_type| code for loop texts}
+@d loop_text=max_in_open+2 {|token_type| code for loop texts}
+@d parameter=max_in_open+3 {|token_type| code for parameter texts}
+@d backed_up=max_in_open+4 {|token_type| code for texts to be reread}
+@d inserted=max_in_open+5 {|token_type| code for inserted texts}
+@d macro=max_in_open+6 {|token_type| code for macro replacement texts}
+
+@ The |param_stack| is an auxiliary array used to hold pointers to the token
+lists for parameters at the current level and subsidiary levels of input.
+This stack grows at a different rate from the others.
+
+@<Glob...@>=
+@!param_stack:array [0..param_size] of pointer;
+ {token list pointers for parameters}
+@!param_ptr:0..param_size; {first unused entry in |param_stack|}
+@!max_param_stack:integer;
+ {largest value of |param_ptr|}
+
+@ Thus, the ``current input state'' can be very complicated indeed; there
+can be many levels and each level can arise in a variety of ways. The
+|show_context| procedure, which is used by \MF's error-reporting routine to
+print out the current input state on all levels down to the most recent
+line of characters from an input file, illustrates most of these conventions.
+The global variable |file_ptr| contains the lowest level that was
+displayed by this procedure.
+
+@<Glob...@>=
+@!file_ptr:0..stack_size; {shallowest level shown by |show_context|}
+
+@ The status at each level is indicated by printing two lines, where the first
+line indicates what was read so far and the second line shows what remains
+to be read. The context is cropped, if necessary, so that the first line
+contains at most |half_error_line| characters, and the second contains
+at most |error_line|. Non-current input levels whose |token_type| is
+`|backed_up|' are shown only if they have not been fully read.
+
+@p procedure show_context; {prints where the scanner is}
+label done;
+var @!old_setting:0..max_selector; {saved |selector| setting}
+@<Local variables for formatting calculations@>@/
+begin file_ptr:=input_ptr; input_stack[file_ptr]:=cur_input;
+ {store current state}
+loop@+begin cur_input:=input_stack[file_ptr]; {enter into the context}
+ @<Display the current context@>;
+ if file_state then
+ if (name>2) or (file_ptr=0) then goto done;
+ decr(file_ptr);
+ end;
+done: cur_input:=input_stack[input_ptr]; {restore original state}
+end;
+
+@ @<Display the current context@>=
+if (file_ptr=input_ptr) or file_state or
+ (token_type<>backed_up) or (loc<>null) then
+ {we omit backed-up token lists that have already been read}
+ begin tally:=0; {get ready to count characters}
+ old_setting:=selector;
+ if file_state then
+ begin @<Print location of current line@>;
+ @<Pseudoprint the line@>;
+ end
+ else begin @<Print type of token list@>;
+ @<Pseudoprint the token list@>;
+ end;
+ selector:=old_setting; {stop pseudoprinting}
+ @<Print two lines using the tricky pseudoprinted information@>;
+ end
+
+@ This routine should be changed, if necessary, to give the best possible
+indication of where the current line resides in the input file.
+For example, on some systems it is best to print both a page and line number.
+@^system dependencies@>
+
+@<Print location of current line@>=
+if name<=1 then
+ if terminal_input and(file_ptr=0) then print_nl("<*>")
+ else print_nl("<insert>")
+else if name=2 then print_nl("<scantokens>")
+else begin print_nl("l."); print_int(line);
+ end;
+print_char(" ")
+
+@ @<Print type of token list@>=
+case token_type of
+forever_text: print_nl("<forever> ");
+loop_text: @<Print the current loop value@>;
+parameter: print_nl("<argument> ");
+backed_up: if loc=null then print_nl("<recently read> ")
+ else print_nl("<to be read again> ");
+inserted: print_nl("<inserted text> ");
+macro: begin print_ln;
+ if name<>null then slow_print(text(name))
+ else @<Print the name of a \&{vardef}'d macro@>;
+ print("->");
+ end;
+othercases print_nl("?") {this should never happen}
+@.?\relax@>
+endcases
+
+@ The parameter that corresponds to a loop text is either a token list
+(in the case of \&{forsuffixes}) or a ``capsule'' (in the case of \&{for}).
+We'll discuss capsules later; for now, all we need to know is that
+the |link| field in a capsule parameter is |void| and that
+|print_exp(p,0)| displays the value of capsule~|p| in abbreviated form.
+
+@<Print the current loop value@>=
+begin print_nl("<for("); p:=param_stack[param_start];
+if p<>null then
+ if link(p)=void then print_exp(p,0) {we're in a \&{for} loop}
+ else show_token_list(p,null,20,tally);
+print(")> ");
+end
+
+@ The first two parameters of a macro defined by \&{vardef} will be token
+lists representing the macro's prefix and ``at point.'' By putting these
+together, we get the macro's full name.
+
+@<Print the name of a \&{vardef}'d macro@>=
+begin p:=param_stack[param_start];
+if p=null then show_token_list(param_stack[param_start+1],null,20,tally)
+else begin q:=p;
+ while link(q)<>null do q:=link(q);
+ link(q):=param_stack[param_start+1];
+ show_token_list(p,null,20,tally);
+ link(q):=null;
+ end;
+end
+
+@ Now it is necessary to explain a little trick. We don't want to store a long
+string that corresponds to a token list, because that string might take up
+lots of memory; and we are printing during a time when an error message is
+being given, so we dare not do anything that might overflow one of \MF's
+tables. So `pseudoprinting' is the answer: We enter a mode of printing
+that stores characters into a buffer of length |error_line|, where character
+$k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if
+|k<trick_count|, otherwise character |k| is dropped. Initially we set
+|tally:=0| and |trick_count:=1000000|; then when we reach the
+point where transition from line 1 to line 2 should occur, we
+set |first_count:=tally| and |trick_count:=@tmax@>(error_line,
+tally+1+error_line-half_error_line)|. At the end of the
+pseudoprinting, the values of |first_count|, |tally|, and
+|trick_count| give us all the information we need to print the two lines,
+and all of the necessary text is in |trick_buf|.
+
+Namely, let |l| be the length of the descriptive information that appears
+on the first line. The length of the context information gathered for that
+line is |k=first_count|, and the length of the context information
+gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|,
+where |h=half_error_line|, we print |trick_buf[0..k-1]| after the
+descriptive information on line~1, and set |n:=l+k|; here |n| is the
+length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h|
+and print `\.{...}' followed by
+$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$
+where subscripts of |trick_buf| are circular modulo |error_line|. The
+second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|,
+unless |n+m>error_line|; in the latter case, further cropping is done.
+This is easier to program than to explain.
+
+@<Local variables for formatting...@>=
+@!i:0..buf_size; {index into |buffer|}
+@!l:integer; {length of descriptive information on line 1}
+@!m:integer; {context information gathered for line 2}
+@!n:0..error_line; {length of line 1}
+@!p: integer; {starting or ending place in |trick_buf|}
+@!q: integer; {temporary index}
+
+@ The following code tells the print routines to gather
+the desired information.
+
+@d begin_pseudoprint==
+ begin l:=tally; tally:=0; selector:=pseudo;
+ trick_count:=1000000;
+ end
+@d set_trick_count==
+ begin first_count:=tally;
+ trick_count:=tally+1+error_line-half_error_line;
+ if trick_count<error_line then trick_count:=error_line;
+ end
+
+@ And the following code uses the information after it has been gathered.
+
+@<Print two lines using the tricky pseudoprinted information@>=
+if trick_count=1000000 then set_trick_count;
+ {|set_trick_count| must be performed}
+if tally<trick_count then m:=tally-first_count
+else m:=trick_count-first_count; {context on line 2}
+if l+first_count<=half_error_line then
+ begin p:=0; n:=l+first_count;
+ end
+else begin print("..."); p:=l+first_count-half_error_line+3;
+ n:=half_error_line;
+ end;
+for q:=p to first_count-1 do print_char(trick_buf[q mod error_line]);
+print_ln;
+for q:=1 to n do print_char(" "); {print |n| spaces to begin line~2}
+if m+n<=error_line then p:=first_count+m else p:=first_count+(error_line-n-3);
+for q:=first_count to p-1 do print_char(trick_buf[q mod error_line]);
+if m+n>error_line then print("...")
+
+@ But the trick is distracting us from our current goal, which is to
+understand the input state. So let's concentrate on the data structures that
+are being pseudoprinted as we finish up the |show_context| procedure.
+
+@<Pseudoprint the line@>=
+begin_pseudoprint;
+if limit>0 then for i:=start to limit-1 do
+ begin if i=loc then set_trick_count;
+ print(buffer[i]);
+ end
+
+@ @<Pseudoprint the token list@>=
+begin_pseudoprint;
+if token_type<>macro then show_token_list(start,loc,100000,0)
+else show_macro(start,loc,100000)
+
+@ Here is the missing piece of |show_token_list| that is activated when the
+token beginning line~2 is about to be shown:
+
+@<Do magic computation@>=set_trick_count
+
+@* \[32] Maintaining the input stacks.
+The following subroutines change the input status in commonly needed ways.
+
+First comes |push_input|, which stores the current state and creates a
+new level (having, initially, the same properties as the old).
+
+@d push_input==@t@> {enter a new input level, save the old}
+ begin if input_ptr>max_in_stack then
+ begin max_in_stack:=input_ptr;
+ if input_ptr=stack_size then overflow("input stack size",stack_size);
+@:METAFONT capacity exceeded input stack size}{\quad input stack size@>
+ end;
+ input_stack[input_ptr]:=cur_input; {stack the record}
+ incr(input_ptr);
+ end
+
+@ And of course what goes up must come down.
+
+@d pop_input==@t@> {leave an input level, re-enter the old}
+ begin decr(input_ptr); cur_input:=input_stack[input_ptr];
+ end
+
+@ Here is a procedure that starts a new level of token-list input, given
+a token list |p| and its type |t|. If |t=macro|, the calling routine should
+set |name|, reset~|loc|, and increase the macro's reference count.
+
+@d back_list(#)==begin_token_list(#,backed_up) {backs up a simple token list}
+
+@p procedure begin_token_list(@!p:pointer;@!t:quarterword);
+begin push_input; start:=p; token_type:=t;
+param_start:=param_ptr; loc:=p;
+end;
+
+@ When a token list has been fully scanned, the following computations
+should be done as we leave that level of input.
+@^inner loop@>
+
+@p procedure end_token_list; {leave a token-list input level}
+label done;
+var @!p:pointer; {temporary register}
+begin if token_type>=backed_up then {token list to be deleted}
+ if token_type<=inserted then
+ begin flush_token_list(start); goto done;
+ end
+ else delete_mac_ref(start); {update reference count}
+while param_ptr>param_start do {parameters must be flushed}
+ begin decr(param_ptr);
+ p:=param_stack[param_ptr];
+ if p<>null then
+ if link(p)=void then {it's an \&{expr} parameter}
+ begin recycle_value(p); free_node(p,value_node_size);
+ end
+ else flush_token_list(p); {it's a \&{suffix} or \&{text} parameter}
+ end;
+done: pop_input; check_interrupt;
+end;
+
+@ The contents of |cur_cmd,cur_mod,cur_sym| are placed into an equivalent
+token by the |cur_tok| routine.
+@^inner loop@>
+
+@p @t\4@>@<Declare the procedure called |make_exp_copy|@>@;@/
+function cur_tok:pointer;
+var @!p:pointer; {a new token node}
+@!save_type:small_number; {|cur_type| to be restored}
+@!save_exp:integer; {|cur_exp| to be restored}
+begin if cur_sym=0 then
+ if cur_cmd=capsule_token then
+ begin save_type:=cur_type; save_exp:=cur_exp;
+ make_exp_copy(cur_mod); p:=stash_cur_exp; link(p):=null;
+ cur_type:=save_type; cur_exp:=save_exp;
+ end
+ else begin p:=get_node(token_node_size);
+ value(p):=cur_mod; name_type(p):=token;
+ if cur_cmd=numeric_token then type(p):=known
+ else type(p):=string_type;
+ end
+else begin fast_get_avail(p); info(p):=cur_sym;
+ end;
+cur_tok:=p;
+end;
+
+@ Sometimes \MF\ has read too far and wants to ``unscan'' what it has
+seen. The |back_input| procedure takes care of this by putting the token
+just scanned back into the input stream, ready to be read again.
+If |cur_sym<>0|, the values of |cur_cmd| and |cur_mod| are irrelevant.
+
+@p procedure back_input; {undoes one token of input}
+var @!p:pointer; {a token list of length one}
+begin p:=cur_tok;
+while token_state and(loc=null) do end_token_list; {conserve stack space}
+back_list(p);
+end;
+
+@ The |back_error| routine is used when we want to restore or replace an
+offending token just before issuing an error message. We disable interrupts
+during the call of |back_input| so that the help message won't be lost.
+
+@p procedure back_error; {back up one token and call |error|}
+begin OK_to_interrupt:=false; back_input; OK_to_interrupt:=true; error;
+end;
+@#
+procedure ins_error; {back up one inserted token and call |error|}
+begin OK_to_interrupt:=false; back_input; token_type:=inserted;
+OK_to_interrupt:=true; error;
+end;
+
+@ The |begin_file_reading| procedure starts a new level of input for lines
+of characters to be read from a file, or as an insertion from the
+terminal. It does not take care of opening the file, nor does it set |loc|
+or |limit| or |line|.
+@^system dependencies@>
+
+@p procedure begin_file_reading;
+begin if in_open=max_in_open then overflow("text input levels",max_in_open);
+@:METAFONT capacity exceeded text input levels}{\quad text input levels@>
+if first=buf_size then overflow("buffer size",buf_size);
+@:METAFONT capacity exceeded buffer size}{\quad buffer size@>
+incr(in_open); push_input; index:=in_open;
+line_stack[index]:=line; start:=first;
+name:=0; {|terminal_input| is now |true|}
+end;
+
+@ Conversely, the variables must be downdated when such a level of input
+is finished:
+
+@p procedure end_file_reading;
+begin first:=start; line:=line_stack[index];
+if index<>in_open then confusion("endinput");
+@:this can't happen endinput}{\quad endinput@>
+if name>2 then a_close(cur_file); {forget it}
+pop_input; decr(in_open);
+end;
+
+@ In order to keep the stack from overflowing during a long sequence of
+inserted `\.{show}' commands, the following routine removes completed
+error-inserted lines from memory.
+
+@p procedure clear_for_error_prompt;
+begin while file_state and terminal_input and@|
+ (input_ptr>0)and(loc=limit) do end_file_reading;
+print_ln; clear_terminal;
+end;
+
+@ To get \MF's whole input mechanism going, we perform the following
+actions.
+
+@<Initialize the input routines@>=
+begin input_ptr:=0; max_in_stack:=0;
+in_open:=0; open_parens:=0; max_buf_stack:=0;
+param_ptr:=0; max_param_stack:=0;
+first:=1;
+start:=1; index:=0; line:=0; name:=0;
+force_eof:=false;
+if not init_terminal then goto final_end;
+limit:=last; first:=last+1; {|init_terminal| has set |loc| and |last|}
+end;
+
+@* \[33] Getting the next token.
+The heart of \MF's input mechanism is the |get_next| procedure, which
+we shall develop in the next few sections of the program. Perhaps we
+shouldn't actually call it the ``heart,'' however; it really acts as \MF's
+eyes and mouth, reading the source files and gobbling them up. And it also
+helps \MF\ to regurgitate stored token lists that are to be processed again.
+
+The main duty of |get_next| is to input one token and to set |cur_cmd|
+and |cur_mod| to that token's command code and modifier. Furthermore, if
+the input token is a symbolic token, that token's |hash| address
+is stored in |cur_sym|; otherwise |cur_sym| is set to zero.
+
+Underlying this simple description is a certain amount of complexity
+because of all the cases that need to be handled.
+However, the inner loop of |get_next| is reasonably short and fast.
+
+@ Before getting into |get_next|, we need to consider a mechanism by which
+\MF\ helps keep errors from propagating too far. Whenever the program goes
+into a mode where it keeps calling |get_next| repeatedly until a certain
+condition is met, it sets |scanner_status| to some value other than |normal|.
+Then if an input file ends, or if an `\&{outer}' symbol appears,
+an appropriate error recovery will be possible.
+
+The global variable |warning_info| helps in this error recovery by providing
+additional information. For example, |warning_info| might indicate the
+name of a macro whose replacement text is being scanned.
+
+@d normal=0 {|scanner_status| at ``quiet times''}
+@d skipping=1 {|scanner_status| when false conditional text is being skipped}
+@d flushing=2 {|scanner_status| when junk after a statement is being ignored}
+@d absorbing=3 {|scanner_status| when a \&{text} parameter is being scanned}
+@d var_defining=4 {|scanner_status| when a \&{vardef} is being scanned}
+@d op_defining=5 {|scanner_status| when a macro \&{def} is being scanned}
+@d loop_defining=6 {|scanner_status| when a \&{for} loop is being scanned}
+
+@<Glob...@>=
+@!scanner_status:normal..loop_defining; {are we scanning at high speed?}
+@!warning_info:integer; {if so, what else do we need to know,
+ in case an error occurs?}
+
+@ @<Initialize the input routines@>=
+scanner_status:=normal;
+
+@ The following subroutine
+is called when an `\&{outer}' symbolic token has been scanned or
+when the end of a file has been reached. These two cases are distinguished
+by |cur_sym|, which is zero at the end of a file.
+
+@p function check_outer_validity:boolean;
+var @!p:pointer; {points to inserted token list}
+begin if scanner_status=normal then check_outer_validity:=true
+else begin deletions_allowed:=false;
+ @<Back up an outer symbolic token so that it can be reread@>;
+ if scanner_status>skipping then
+ @<Tell the user what has run away and try to recover@>
+ else begin print_err("Incomplete if; all text was ignored after line ");
+@.Incomplete if...@>
+ print_int(warning_info);@/
+ help3("A forbidden `outer' token occurred in skipped text.")@/
+ ("This kind of error happens when you say `if...' and forget")@/
+ ("the matching `fi'. I've inserted a `fi'; this might work.");
+ if cur_sym=0 then help_line[2]:=@|
+ "The file ended while I was skipping conditional text.";
+ cur_sym:=frozen_fi; ins_error;
+ end;
+ deletions_allowed:=true; check_outer_validity:=false;
+ end;
+end;
+
+@ @<Back up an outer symbolic token so that it can be reread@>=
+if cur_sym<>0 then
+ begin p:=get_avail; info(p):=cur_sym;
+ back_list(p); {prepare to read the symbolic token again}
+ end
+
+@ @<Tell the user what has run away...@>=
+begin runaway; {print the definition-so-far}
+if cur_sym=0 then print_err("File ended")
+@.File ended while scanning...@>
+else begin print_err("Forbidden token found");
+@.Forbidden token found...@>
+ end;
+print(" while scanning ");
+help4("I suspect you have forgotten an `enddef',")@/
+("causing me to read past where you wanted me to stop.")@/
+("I'll try to recover; but if the error is serious,")@/
+("you'd better type `E' or `X' now and fix your file.");@/
+case scanner_status of
+@t\4@>@<Complete the error message,
+ and set |cur_sym| to a token that might help recover from the error@>@;
+end; {there are no other cases}
+ins_error;
+end
+
+@ As we consider various kinds of errors, it is also appropriate to
+change the first line of the help message just given; |help_line[3]|
+points to the string that might be changed.
+
+@<Complete the error message,...@>=
+flushing: begin print("to the end of the statement");
+ help_line[3]:="A previous error seems to have propagated,";
+ cur_sym:=frozen_semicolon;
+ end;
+absorbing: begin print("a text argument");
+ help_line[3]:="It seems that a right delimiter was left out,";
+ if warning_info=0 then cur_sym:=frozen_end_group
+ else begin cur_sym:=frozen_right_delimiter;
+ equiv(frozen_right_delimiter):=warning_info;
+ end;
+ end;
+var_defining, op_defining: begin print("the definition of ");
+ if scanner_status=op_defining then slow_print(text(warning_info))
+ else print_variable_name(warning_info);
+ cur_sym:=frozen_end_def;
+ end;
+loop_defining: begin print("the text of a "); slow_print(text(warning_info));
+ print(" loop");
+ help_line[3]:="I suspect you have forgotten an `endfor',";
+ cur_sym:=frozen_end_for;
+ end;
+
+@ The |runaway| procedure displays the first part of the text that occurred
+when \MF\ began its special |scanner_status|, if that text has been saved.
+
+@<Declare the procedure called |runaway|@>=
+procedure runaway;
+begin if scanner_status>flushing then
+ begin print_nl("Runaway ");
+ case scanner_status of
+ absorbing: print("text?");
+ var_defining,op_defining: print("definition?");
+ loop_defining: print("loop?");
+ end; {there are no other cases}
+ print_ln; show_token_list(link(hold_head),null,error_line-10,0);
+ end;
+end;
+
+@ We need to mention a procedure that may be called by |get_next|.
+
+@p procedure@?firm_up_the_line; forward;
+
+@ And now we're ready to take the plunge into |get_next| itself.
+
+@d switch=25 {a label in |get_next|}
+@d start_numeric_token=85 {another}
+@d start_decimal_token=86 {and another}
+@d fin_numeric_token=87
+ {and still another, although |goto| is considered harmful}
+
+@p procedure get_next; {sets |cur_cmd|, |cur_mod|, |cur_sym| to next token}
+@^inner loop@>
+label restart, {go here to get the next input token}
+ exit, {go here when the next input token has been got}
+ found, {go here when the end of a symbolic token has been found}
+ switch, {go here to branch on the class of an input character}
+ start_numeric_token,start_decimal_token,fin_numeric_token,done;
+ {go here at crucial stages when scanning a number}
+var @!k:0..buf_size; {an index into |buffer|}
+@!c:ASCII_code; {the current character in the buffer}
+@!class:ASCII_code; {its class number}
+@!n,@!f:integer; {registers for decimal-to-binary conversion}
+begin restart: cur_sym:=0;
+if file_state then
+@<Input from external file; |goto restart| if no input found,
+ or |return| if a non-symbolic token is found@>
+else @<Input from token list; |goto restart| if end of list or
+ if a parameter needs to be expanded,
+ or |return| if a non-symbolic token is found@>;
+@<Finish getting the symbolic token in |cur_sym|;
+ |goto restart| if it is illegal@>;
+exit:end;
+
+@ When a symbolic token is declared to be `\&{outer}', its command code
+is increased by |outer_tag|.
+@^inner loop@>
+
+@<Finish getting the symbolic token in |cur_sym|...@>=
+cur_cmd:=eq_type(cur_sym); cur_mod:=equiv(cur_sym);
+if cur_cmd>=outer_tag then
+ if check_outer_validity then cur_cmd:=cur_cmd-outer_tag
+ else goto restart
+
+@ A percent sign appears in |buffer[limit]|; this makes it unnecessary
+to have a special test for end-of-line.
+@^inner loop@>
+
+@<Input from external file;...@>=
+begin switch: c:=buffer[loc]; incr(loc); class:=char_class[c];
+case class of
+digit_class: goto start_numeric_token;
+period_class: begin class:=char_class[buffer[loc]];
+ if class>period_class then goto switch
+ else if class<period_class then {|class=digit_class|}
+ begin n:=0; goto start_decimal_token;
+ end;
+@:. }{\..\ token@>
+ end;
+space_class: goto switch;
+percent_class: begin @<Move to next line of file,
+ or |goto restart| if there is no next line@>;
+ check_interrupt;
+ goto switch;
+ end;
+string_class: @<Get a string token and |return|@>;
+isolated_classes: begin k:=loc-1; goto found;
+ end;
+invalid_class: @<Decry the invalid character and |goto restart|@>;
+othercases do_nothing {letters, etc.}
+endcases;@/
+k:=loc-1;
+while char_class[buffer[loc]]=class do incr(loc);
+goto found;
+start_numeric_token:@<Get the integer part |n| of a numeric token;
+ set |f:=0| and |goto fin_numeric_token| if there is no decimal point@>;
+start_decimal_token:@<Get the fraction part |f| of a numeric token@>;
+fin_numeric_token:@<Pack the numeric and fraction parts of a numeric token
+ and |return|@>;
+found: cur_sym:=id_lookup(k,loc-k);
+end
+
+@ We go to |restart| instead of to |switch|, because we might enter
+|token_state| after the error has been dealt with
+(cf.\ |clear_for_error_prompt|).
+
+@<Decry the invalid...@>=
+begin print_err("Text line contains an invalid character");
+@.Text line contains...@>
+help2("A funny symbol that I can't read has just been input.")@/
+("Continue, and I'll forget that it ever happened.");@/
+deletions_allowed:=false; error; deletions_allowed:=true;
+goto restart;
+end
+
+@ @<Get a string token and |return|@>=
+begin if buffer[loc]="""" then cur_mod:=""
+else begin k:=loc; buffer[limit+1]:="""";
+ repeat incr(loc);
+ until buffer[loc]="""";
+ if loc>limit then @<Decry the missing string delimiter and |goto restart|@>;
+ if (loc=k+1) and (length(buffer[k])=1) then cur_mod:=buffer[k]
+ else begin str_room(loc-k);
+ repeat append_char(buffer[k]); incr(k);
+ until k=loc;
+ cur_mod:=make_string;
+ end;
+ end;
+incr(loc); cur_cmd:=string_token; return;
+end
+
+@ We go to |restart| after this error message, not to |switch|,
+because the |clear_for_error_prompt| routine might have reinstated
+|token_state| after |error| has finished.
+
+@<Decry the missing string delimiter and |goto restart|@>=
+begin loc:=limit; {the next character to be read on this line will be |"%"|}
+print_err("Incomplete string token has been flushed");
+@.Incomplete string token...@>
+help3("Strings should finish on the same line as they began.")@/
+ ("I've deleted the partial string; you might want to")@/
+ ("insert another by typing, e.g., `I""new string""'.");@/
+deletions_allowed:=false; error; deletions_allowed:=true; goto restart;
+end
+
+@ @<Get the integer part |n| of a numeric token...@>=
+n:=c-"0";
+while char_class[buffer[loc]]=digit_class do
+ begin if n<4096 then n:=10*n+buffer[loc]-"0";
+ incr(loc);
+ end;
+if buffer[loc]="." then if char_class[buffer[loc+1]]=digit_class then goto done;
+f:=0; goto fin_numeric_token;
+done: incr(loc)
+
+@ @<Get the fraction part |f| of a numeric token@>=
+k:=0;
+repeat if k<17 then {digits for |k>=17| cannot affect the result}
+ begin dig[k]:=buffer[loc]-"0"; incr(k);
+ end;
+incr(loc);
+until char_class[buffer[loc]]<>digit_class;
+f:=round_decimals(k);
+if f=unity then
+ begin incr(n); f:=0;
+ end
+
+@ @<Pack the numeric and fraction parts of a numeric token and |return|@>=
+if n<4096 then cur_mod:=n*unity+f
+else begin print_err("Enormous number has been reduced");
+@.Enormous number...@>
+ help2("I can't handle numbers bigger than about 4095.99998;")@/
+ ("so I've changed your constant to that maximum amount.");@/
+ deletions_allowed:=false; error; deletions_allowed:=true;
+ cur_mod:=@'1777777777;
+ end;
+cur_cmd:=numeric_token; return
+
+@ Let's consider now what happens when |get_next| is looking at a token list.
+@^inner loop@>
+
+@<Input from token list;...@>=
+if loc>=hi_mem_min then {one-word token}
+ begin cur_sym:=info(loc); loc:=link(loc); {move to next}
+ if cur_sym>=expr_base then
+ if cur_sym>=suffix_base then
+ @<Insert a suffix or text parameter and |goto restart|@>
+ else begin cur_cmd:=capsule_token;
+ cur_mod:=param_stack[param_start+cur_sym-(expr_base)];
+ cur_sym:=0; return;
+ end;
+ end
+else if loc>null then
+ @<Get a stored numeric or string or capsule token and |return|@>
+else begin {we are done with this token list}
+ end_token_list; goto restart; {resume previous level}
+ end
+
+@ @<Insert a suffix or text parameter...@>=
+begin if cur_sym>=text_base then cur_sym:=cur_sym-param_size;
+ {|param_size=text_base-suffix_base|}
+begin_token_list(param_stack[param_start+cur_sym-(suffix_base)],parameter);
+goto restart;
+end
+
+@ @<Get a stored numeric or string or capsule token...@>=
+begin if name_type(loc)=token then
+ begin cur_mod:=value(loc);
+ if type(loc)=known then cur_cmd:=numeric_token
+ else begin cur_cmd:=string_token; add_str_ref(cur_mod);
+ end;
+ end
+else begin cur_mod:=loc; cur_cmd:=capsule_token;
+ end;
+loc:=link(loc); return;
+end
+
+@ All of the easy branches of |get_next| have now been taken care of.
+There is one more branch.
+
+@<Move to next line of file, or |goto restart|...@>=
+if name>2 then @<Read next line of file into |buffer|, or
+ |goto restart| if the file has ended@>
+else begin if input_ptr>0 then
+ {text was inserted during error recovery or by \&{scantokens}}
+ begin end_file_reading; goto restart; {resume previous level}
+ end;
+ if selector<log_only then open_log_file;
+ if interaction>nonstop_mode then
+ begin if limit=start then {previous line was empty}
+ print_nl("(Please type a command or say `end')");
+@.Please type...@>
+ print_ln; first:=start;
+ prompt_input("*"); {input on-line into |buffer|}
+@.*\relax@>
+ limit:=last; buffer[limit]:="%";
+ first:=limit+1; loc:=start;
+ end
+ else fatal_error("*** (job aborted, no legal end found)");
+@.job aborted@>
+ {nonstop mode, which is intended for overnight batch processing,
+ never waits for on-line input}
+ end
+
+@ The global variable |force_eof| is normally |false|; it is set |true|
+by an \&{endinput} command.
+
+@<Glob...@>=
+@!force_eof:boolean; {should the next \&{input} be aborted early?}
+
+@ @<Read next line of file into |buffer|, or
+ |goto restart| if the file has ended@>=
+begin incr(line); first:=start;
+if not force_eof then
+ begin if input_ln(cur_file,true) then {not end of file}
+ firm_up_the_line {this sets |limit|}
+ else force_eof:=true;
+ end;
+if force_eof then
+ begin print_char(")"); decr(open_parens);
+ update_terminal; {show user that file has been read}
+ force_eof:=false;
+ end_file_reading; {resume previous level}
+ if check_outer_validity then goto restart@+else goto restart;
+ end;
+buffer[limit]:="%"; first:=limit+1; loc:=start; {ready to read}
+end
+
+@ If the user has set the |pausing| parameter to some positive value,
+and if nonstop mode has not been selected, each line of input is displayed
+on the terminal and the transcript file, followed by `\.{=>}'.
+\MF\ waits for a response. If the response is null (i.e., if nothing is
+typed except perhaps a few blank spaces), the original
+line is accepted as it stands; otherwise the line typed is
+used instead of the line in the file.
+
+@p procedure firm_up_the_line;
+var @!k:0..buf_size; {an index into |buffer|}
+begin limit:=last;
+if internal[pausing]>0 then if interaction>nonstop_mode then
+ begin wake_up_terminal; print_ln;
+ if start<limit then for k:=start to limit-1 do print(buffer[k]);
+ first:=limit; prompt_input("=>"); {wait for user response}
+@.=>@>
+ if last>first then
+ begin for k:=first to last-1 do {move line down in buffer}
+ buffer[k+start-first]:=buffer[k];
+ limit:=start+last-first;
+ end;
+ end;
+end;
+
+@* \[34] Scanning macro definitions.
+\MF\ has a variety of ways to tuck tokens away into token lists for later
+use: Macros can be defined with \&{def}, \&{vardef}, \&{primarydef}, etc.;
+repeatable code can be defined with \&{for}, \&{forever}, \&{forsuffixes}.
+All such operations are handled by the routines in this part of the program.
+
+The modifier part of each command code is zero for the ``ending delimiters''
+like \&{enddef} and \&{endfor}.
+
+@d start_def=1 {command modifier for \&{def}}
+@d var_def=2 {command modifier for \&{vardef}}
+@d end_def=0 {command modifier for \&{enddef}}
+@d start_forever=1 {command modifier for \&{forever}}
+@d end_for=0 {command modifier for \&{endfor}}
+
+@<Put each...@>=
+primitive("def",macro_def,start_def);@/
+@!@:def_}{\&{def} primitive@>
+primitive("vardef",macro_def,var_def);@/
+@!@:var_def_}{\&{vardef} primitive@>
+primitive("primarydef",macro_def,secondary_primary_macro);@/
+@!@:primary_def_}{\&{primarydef} primitive@>
+primitive("secondarydef",macro_def,tertiary_secondary_macro);@/
+@!@:secondary_def_}{\&{secondarydef} primitive@>
+primitive("tertiarydef",macro_def,expression_tertiary_macro);@/
+@!@:tertiary_def_}{\&{tertiarydef} primitive@>
+primitive("enddef",macro_def,end_def); eqtb[frozen_end_def]:=eqtb[cur_sym];@/
+@!@:end_def_}{\&{enddef} primitive@>
+@#
+primitive("for",iteration,expr_base);@/
+@!@:for_}{\&{for} primitive@>
+primitive("forsuffixes",iteration,suffix_base);@/
+@!@:for_suffixes_}{\&{forsuffixes} primitive@>
+primitive("forever",iteration,start_forever);@/
+@!@:forever_}{\&{forever} primitive@>
+primitive("endfor",iteration,end_for); eqtb[frozen_end_for]:=eqtb[cur_sym];@/
+@!@:end_for_}{\&{endfor} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+macro_def:if m<=var_def then
+ if m=start_def then print("def")
+ else if m<start_def then print("enddef")
+ else print("vardef")
+ else if m=secondary_primary_macro then print("primarydef")
+ else if m=tertiary_secondary_macro then print("secondarydef")
+ else print("tertiarydef");
+iteration: if m<=start_forever then
+ if m=start_forever then print("forever")@+else print("endfor")
+ else if m=expr_base then print("for")@+else print("forsuffixes");
+
+@ Different macro-absorbing operations have different syntaxes, but they
+also have a lot in common. There is a list of special symbols that are to
+be replaced by parameter tokens; there is a special command code that
+ends the definition; the quotation conventions are identical. Therefore
+it makes sense to have most of the work done by a single subroutine. That
+subroutine is called |scan_toks|.
+
+The first parameter to |scan_toks| is the command code that will
+terminate scanning (either |macro_def| or |iteration|).
+
+The second parameter, |subst_list|, points to a (possibly empty) list
+of two-word nodes whose |info| and |value| fields specify symbol tokens
+before and after replacement. The list will be returned to free storage
+by |scan_toks|.
+
+The third parameter is simply appended to the token list that is built.
+And the final parameter tells how many of the special operations
+\.{\#\AT!}, \.{\AT!}, and \.{\AT!\#} are to be replaced by suffix parameters.
+When such parameters are present, they are called \.{(SUFFIX0)},
+\.{(SUFFIX1)}, and \.{(SUFFIX2)}.
+
+@p function scan_toks(@!terminator:command_code;
+ @!subst_list,@!tail_end:pointer;@!suffix_count:small_number):pointer;
+label done,found;
+var @!p:pointer; {tail of the token list being built}
+@!q:pointer; {temporary for link management}
+@!balance:integer; {left delimiters minus right delimiters}
+begin p:=hold_head; balance:=1; link(hold_head):=null;
+loop@+ begin get_next;
+ if cur_sym>0 then
+ begin @<Substitute for |cur_sym|, if it's on the |subst_list|@>;
+ if cur_cmd=terminator then
+ @<Adjust the balance; |goto done| if it's zero@>
+ else if cur_cmd=macro_special then
+ @<Handle quoted symbols, \.{\#\AT!}, \.{\AT!}, or \.{\AT!\#}@>;
+ end;
+ link(p):=cur_tok; p:=link(p);
+ end;
+done: link(p):=tail_end; flush_node_list(subst_list);
+scan_toks:=link(hold_head);
+end;
+
+@ @<Substitute for |cur_sym|...@>=
+begin q:=subst_list;
+while q<>null do
+ begin if info(q)=cur_sym then
+ begin cur_sym:=value(q); cur_cmd:=relax; goto found;
+ end;
+ q:=link(q);
+ end;
+found:end
+
+@ @<Adjust the balance; |goto done| if it's zero@>=
+if cur_mod>0 then incr(balance)
+else begin decr(balance);
+ if balance=0 then goto done;
+ end
+
+@ Four commands are intended to be used only within macro texts: \&{quote},
+\.{\#\AT!}, \.{\AT!}, and \.{\AT!\#}. They are variants of a single command
+code called |macro_special|.
+
+@d quote=0 {|macro_special| modifier for \&{quote}}
+@d macro_prefix=1 {|macro_special| modifier for \.{\#\AT!}}
+@d macro_at=2 {|macro_special| modifier for \.{\AT!}}
+@d macro_suffix=3 {|macro_special| modifier for \.{\AT!\#}}
+
+@<Put each...@>=
+primitive("quote",macro_special,quote);@/
+@!@:quote_}{\&{quote} primitive@>
+primitive("#@@",macro_special,macro_prefix);@/
+@!@:]]]\#\AT!_}{\.{\#\AT!} primitive@>
+primitive("@@",macro_special,macro_at);@/
+@!@:]]]\AT!_}{\.{\AT!} primitive@>
+primitive("@@#",macro_special,macro_suffix);@/
+@!@:]]]\AT!\#_}{\.{\AT!\#} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+macro_special: case m of
+ macro_prefix: print("#@@");
+ macro_at: print_char("@@");
+ macro_suffix: print("@@#");
+ othercases print("quote")
+ endcases;
+
+@ @<Handle quoted...@>=
+begin if cur_mod=quote then get_next
+else if cur_mod<=suffix_count then cur_sym:=suffix_base-1+cur_mod;
+end
+
+@ Here is a routine that's used whenever a token will be redefined. If
+the user's token is unredefinable, the `|frozen_inaccessible|' token is
+substituted; the latter is redefinable but essentially impossible to use,
+hence \MF's tables won't get fouled up.
+
+@p procedure get_symbol; {sets |cur_sym| to a safe symbol}
+label restart;
+begin restart: get_next;
+if (cur_sym=0)or(cur_sym>frozen_inaccessible) then
+ begin print_err("Missing symbolic token inserted");
+@.Missing symbolic token...@>
+ help3("Sorry: You can't redefine a number, string, or expr.")@/
+ ("I've inserted an inaccessible symbol so that your")@/
+ ("definition will be completed without mixing me up too badly.");
+ if cur_sym>0 then
+ help_line[2]:="Sorry: You can't redefine my error-recovery tokens."
+ else if cur_cmd=string_token then delete_str_ref(cur_mod);
+ cur_sym:=frozen_inaccessible; ins_error; goto restart;
+ end;
+end;
+
+@ Before we actually redefine a symbolic token, we need to clear away its
+former value, if it was a variable. The following stronger version of
+|get_symbol| does that.
+
+@p procedure get_clear_symbol;
+begin get_symbol; clear_symbol(cur_sym,false);
+end;
+
+@ Here's another little subroutine; it checks that an equals sign
+or assignment sign comes along at the proper place in a macro definition.
+
+@p procedure check_equals;
+begin if cur_cmd<>equals then if cur_cmd<>assignment then
+ begin missing_err("=");@/
+@.Missing `='@>
+ help5("The next thing in this `def' should have been `=',")@/
+ ("because I've already looked at the definition heading.")@/
+ ("But don't worry; I'll pretend that an equals sign")@/
+ ("was present. Everything from here to `enddef'")@/
+ ("will be the replacement text of this macro.");
+ back_error;
+ end;
+end;
+
+@ A \&{primarydef}, \&{secondarydef}, or \&{tertiarydef} is rather easily
+handled now that we have |scan_toks|. In this case there are
+two parameters, which will be \.{EXPR0} and \.{EXPR1} (i.e.,
+|expr_base| and |expr_base+1|).
+
+@p procedure make_op_def;
+var @!m:command_code; {the type of definition}
+@!p,@!q,@!r:pointer; {for list manipulation}
+begin m:=cur_mod;@/
+get_symbol; q:=get_node(token_node_size);
+info(q):=cur_sym; value(q):=expr_base;@/
+get_clear_symbol; warning_info:=cur_sym;@/
+get_symbol; p:=get_node(token_node_size);
+info(p):=cur_sym; value(p):=expr_base+1; link(p):=q;@/
+get_next; check_equals;@/
+scanner_status:=op_defining; q:=get_avail; ref_count(q):=null;
+r:=get_avail; link(q):=r; info(r):=general_macro;
+link(r):=scan_toks(macro_def,p,null,0);
+scanner_status:=normal; eq_type(warning_info):=m;
+equiv(warning_info):=q; get_x_next;
+end;
+
+@ Parameters to macros are introduced by the keywords \&{expr},
+\&{suffix}, \&{text}, \&{primary}, \&{secondary}, and \&{tertiary}.
+
+@<Put each...@>=
+primitive("expr",param_type,expr_base);@/
+@!@:expr_}{\&{expr} primitive@>
+primitive("suffix",param_type,suffix_base);@/
+@!@:suffix_}{\&{suffix} primitive@>
+primitive("text",param_type,text_base);@/
+@!@:text_}{\&{text} primitive@>
+primitive("primary",param_type,primary_macro);@/
+@!@:primary_}{\&{primary} primitive@>
+primitive("secondary",param_type,secondary_macro);@/
+@!@:secondary_}{\&{secondary} primitive@>
+primitive("tertiary",param_type,tertiary_macro);@/
+@!@:tertiary_}{\&{tertiary} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+param_type:if m>=expr_base then
+ if m=expr_base then print("expr")
+ else if m=suffix_base then print("suffix")
+ else print("text")
+ else if m<secondary_macro then print("primary")
+ else if m=secondary_macro then print("secondary")
+ else print("tertiary");
+
+@ Let's turn next to the more complex processing associated with \&{def}
+and \&{vardef}. When the following procedure is called, |cur_mod|
+should be either |start_def| or |var_def|.
+
+@p @t\4@>@<Declare the procedure called |check_delimiter|@>@;
+@t\4@>@<Declare the function called |scan_declared_variable|@>@;
+procedure scan_def;
+var @!m:start_def..var_def; {the type of definition}
+@!n:0..3; {the number of special suffix parameters}
+@!k:0..param_size; {the total number of parameters}
+@!c:general_macro..text_macro; {the kind of macro we're defining}
+@!r:pointer; {parameter-substitution list}
+@!q:pointer; {tail of the macro token list}
+@!p:pointer; {temporary storage}
+@!base:halfword; {|expr_base|, |suffix_base|, or |text_base|}
+@!l_delim,@!r_delim:pointer; {matching delimiters}
+begin m:=cur_mod; c:=general_macro; link(hold_head):=null;@/
+q:=get_avail; ref_count(q):=null; r:=null;@/
+@<Scan the token or variable to be defined;
+ set |n|, |scanner_status|, and |warning_info|@>;
+k:=n;
+if cur_cmd=left_delimiter then
+ @<Absorb delimited parameters, putting them into lists |q| and |r|@>;
+if cur_cmd=param_type then
+ @<Absorb undelimited parameters, putting them into list |r|@>;
+check_equals;
+p:=get_avail; info(p):=c; link(q):=p;
+@<Attach the replacement text to the tail of node |p|@>;
+scanner_status:=normal; get_x_next;
+end;
+
+@ We don't put `|frozen_end_group|' into the replacement text of
+a \&{vardef}, because the user may want to redefine `\.{endgroup}'.
+
+@<Attach the replacement text to the tail of node |p|@>=
+if m=start_def then link(p):=scan_toks(macro_def,r,null,n)
+else begin q:=get_avail; info(q):=bg_loc; link(p):=q;
+ p:=get_avail; info(p):=eg_loc;
+ link(q):=scan_toks(macro_def,r,p,n);
+ end;
+if warning_info=bad_vardef then flush_token_list(value(bad_vardef))
+
+@ @<Glob...@>=
+@!bg_loc,@!eg_loc:1..hash_end;
+ {hash addresses of `\.{begingroup}' and `\.{endgroup}'}
+
+@ @<Scan the token or variable to be defined;...@>=
+if m=start_def then
+ begin get_clear_symbol; warning_info:=cur_sym; get_next;
+ scanner_status:=op_defining; n:=0;
+ eq_type(warning_info):=defined_macro; equiv(warning_info):=q;
+ end
+else begin p:=scan_declared_variable;
+ flush_variable(equiv(info(p)),link(p),true);
+ warning_info:=find_variable(p); flush_list(p);
+ if warning_info=null then @<Change to `\.{a bad variable}'@>;
+ scanner_status:=var_defining; n:=2;
+ if cur_cmd=macro_special then if cur_mod=macro_suffix then {\.{\AT!\#}}
+ begin n:=3; get_next;
+ end;
+ type(warning_info):=unsuffixed_macro-2+n; value(warning_info):=q;
+ end {|suffixed_macro=unsuffixed_macro+1|}
+
+@ @<Change to `\.{a bad variable}'@>=
+begin print_err("This variable already starts with a macro");
+@.This variable already...@>
+help2("After `vardef a' you can't say `vardef a.b'.")@/
+ ("So I'll have to discard this definition.");
+error; warning_info:=bad_vardef;
+end
+
+@ @<Initialize table entries...@>=
+name_type(bad_vardef):=root; link(bad_vardef):=frozen_bad_vardef;
+equiv(frozen_bad_vardef):=bad_vardef; eq_type(frozen_bad_vardef):=tag_token;
+
+@ @<Absorb delimited parameters, putting them into lists |q| and |r|@>=
+repeat l_delim:=cur_sym; r_delim:=cur_mod; get_next;
+if (cur_cmd=param_type)and(cur_mod>=expr_base) then base:=cur_mod
+else begin print_err("Missing parameter type; `expr' will be assumed");
+@.Missing parameter type@>
+ help1("You should've had `expr' or `suffix' or `text' here.");
+ back_error; base:=expr_base;
+ end;
+@<Absorb parameter tokens for type |base|@>;
+check_delimiter(l_delim,r_delim);
+get_next;
+until cur_cmd<>left_delimiter
+
+@ @<Absorb parameter tokens for type |base|@>=
+repeat link(q):=get_avail; q:=link(q); info(q):=base+k;@/
+get_symbol; p:=get_node(token_node_size); value(p):=base+k; info(p):=cur_sym;
+if k=param_size then overflow("parameter stack size",param_size);
+@:METAFONT capacity exceeded parameter stack size}{\quad parameter stack size@>
+incr(k); link(p):=r; r:=p; get_next;
+until cur_cmd<>comma
+
+@ @<Absorb undelimited parameters, putting them into list |r|@>=
+begin p:=get_node(token_node_size);
+if cur_mod<expr_base then
+ begin c:=cur_mod; value(p):=expr_base+k;
+ end
+else begin value(p):=cur_mod+k;
+ if cur_mod=expr_base then c:=expr_macro
+ else if cur_mod=suffix_base then c:=suffix_macro
+ else c:=text_macro;
+ end;
+if k=param_size then overflow("parameter stack size",param_size);
+incr(k); get_symbol; info(p):=cur_sym; link(p):=r; r:=p; get_next;
+if c=expr_macro then if cur_cmd=of_token then
+ begin c:=of_macro; p:=get_node(token_node_size);
+ if k=param_size then overflow("parameter stack size",param_size);
+ value(p):=expr_base+k; get_symbol; info(p):=cur_sym;
+ link(p):=r; r:=p; get_next;
+ end;
+end
+
+@* \[35] Expanding the next token.
+Only a few command codes |<min_command| can possibly be returned by
+|get_next|; in increasing order, they are
+|if_test|, |fi_or_else|, |input|, |iteration|, |repeat_loop|,
+|exit_test|, |relax|, |scan_tokens|, |expand_after|, and |defined_macro|.
+
+\MF\ usually gets the next token of input by saying |get_x_next|. This is
+like |get_next| except that it keeps getting more tokens until
+finding |cur_cmd>=min_command|. In other words, |get_x_next| expands
+macros and removes conditionals or iterations or input instructions that
+might be present.
+
+It follows that |get_x_next| might invoke itself recursively. In fact,
+there is massive recursion, since macro expansion can involve the
+scanning of arbitrarily complex expressions, which in turn involve
+macro expansion and conditionals, etc.
+@^recursion@>
+
+Therefore it's necessary to declare a whole bunch of |forward|
+procedures at this point, and to insert some other procedures
+that will be invoked by |get_x_next|.
+
+@p procedure@?scan_primary; forward;@t\2@>
+procedure@?scan_secondary; forward;@t\2@>
+procedure@?scan_tertiary; forward;@t\2@>
+procedure@?scan_expression; forward;@t\2@>
+procedure@?scan_suffix; forward;@t\2@>@/
+@t\4@>@<Declare the procedure called |macro_call|@>@;@/
+procedure@?get_boolean; forward;@t\2@>
+procedure@?pass_text; forward;@t\2@>
+procedure@?conditional; forward;@t\2@>
+procedure@?start_input; forward;@t\2@>
+procedure@?begin_iteration; forward;@t\2@>
+procedure@?resume_iteration; forward;@t\2@>
+procedure@?stop_iteration; forward;@t\2@>
+
+@ An auxiliary subroutine called |expand| is used by |get_x_next|
+when it has to do exotic expansion commands.
+
+@p procedure expand;
+var @!p:pointer; {for list manipulation}
+@!k:integer; {something that we hope is |<=buf_size|}
+@!j:pool_pointer; {index into |str_pool|}
+begin if internal[tracing_commands]>unity then if cur_cmd<>defined_macro then
+ show_cur_cmd_mod;
+case cur_cmd of
+if_test:conditional; {this procedure is discussed in Part 36 below}
+fi_or_else:@<Terminate the current conditional and skip to \&{fi}@>;
+input:@<Initiate or terminate input from a file@>;
+iteration:if cur_mod=end_for then
+ @<Scold the user for having an extra \&{endfor}@>
+ else begin_iteration; {this procedure is discussed in Part 37 below}
+repeat_loop: @<Repeat a loop@>;
+exit_test: @<Exit a loop if the proper time has come@>;
+relax: do_nothing;
+expand_after: @<Expand the token after the next token@>;
+scan_tokens: @<Put a string into the input buffer@>;
+defined_macro:macro_call(cur_mod,null,cur_sym);
+end; {there are no other cases}
+end;
+
+@ @<Scold the user...@>=
+begin print_err("Extra `endfor'");
+@.Extra `endfor'@>
+help2("I'm not currently working on a for loop,")@/
+ ("so I had better not try to end anything.");@/
+error;
+end
+
+@ The processing of \&{input} involves the |start_input| subroutine,
+which will be declared later; the processing of \&{endinput} is trivial.
+
+@<Put each...@>=
+primitive("input",input,0);@/
+@!@:input_}{\&{input} primitive@>
+primitive("endinput",input,1);@/
+@!@:end_input_}{\&{endinput} primitive@>
+
+@ @<Cases of |print_cmd_mod|...@>=
+input: if m=0 then print("input")@+else print("endinput");
+
+@ @<Initiate or terminate input...@>=
+if cur_mod>0 then force_eof:=true
+else start_input
+
+@ We'll discuss the complicated parts of loop operations later. For now
+it suffices to know that there's a global variable called |loop_ptr|
+that will be |null| if no loop is in progress.
+
+@<Repeat a loop@>=
+begin while token_state and(loc=null) do end_token_list; {conserve stack space}
+if loop_ptr=null then
+ begin print_err("Lost loop");
+@.Lost loop@>
+ help2("I'm confused; after exiting from a loop, I still seem")@/
+ ("to want to repeat it. I'll try to forget the problem.");@/
+ error;
+ end
+else resume_iteration; {this procedure is in Part 37 below}
+end
+
+@ @<Exit a loop if the proper time has come@>=
+begin get_boolean;
+if internal[tracing_commands]>unity then show_cmd_mod(nullary,cur_exp);
+if cur_exp=true_code then
+ if loop_ptr=null then
+ begin print_err("No loop is in progress");
+@.No loop is in progress@>
+ help1("Why say `exitif' when there's nothing to exit from?");
+ if cur_cmd=semicolon then error@+else back_error;
+ end
+ else @<Exit prematurely from an iteration@>
+else if cur_cmd<>semicolon then
+ begin missing_err(";");@/
+@.Missing `;'@>
+ help2("After `exitif <boolean exp>' I expect to see a semicolon.")@/
+ ("I shall pretend that one was there."); back_error;
+ end;
+end
+
+@ Here we use the fact that |forever_text| is the only |token_type| that
+is less than |loop_text|.
+
+@<Exit prematurely...@>=
+begin p:=null;
+repeat if file_state then end_file_reading
+else begin if token_type<=loop_text then p:=start;
+ end_token_list;
+ end;
+until p<>null;
+if p<>info(loop_ptr) then fatal_error("*** (loop confusion)");
+@.loop confusion@>
+stop_iteration; {this procedure is in Part 37 below}
+end
+
+@ @<Expand the token after the next token@>=
+begin get_next;
+p:=cur_tok; get_next;
+if cur_cmd<min_command then expand else back_input;
+back_list(p);
+end
+
+@ @<Put a string into the input buffer@>=
+begin get_x_next; scan_primary;
+if cur_type<>string_type then
+ begin disp_err(null,"Not a string");
+@.Not a string@>
+ help2("I'm going to flush this expression, since")@/
+ ("scantokens should be followed by a known string.");
+ put_get_flush_error(0);
+ end
+else begin back_input;
+ if length(cur_exp)>0 then @<Pretend we're reading a new one-line file@>;
+ end;
+end
+
+@ @<Pretend we're reading a new one-line file@>=
+begin begin_file_reading; name:=2;
+k:=first+length(cur_exp);
+if k>=max_buf_stack then
+ begin if k>=buf_size then
+ begin max_buf_stack:=buf_size;
+ overflow("buffer size",buf_size);
+@:METAFONT capacity exceeded buffer size}{\quad buffer size@>
+ end;
+ max_buf_stack:=k+1;
+ end;
+j:=str_start[cur_exp]; limit:=k;
+while first<limit do
+ begin buffer[first]:=so(str_pool[j]); incr(j); incr(first);
+ end;
+buffer[limit]:="%"; first:=limit+1; loc:=start; flush_cur_exp(0);
+end
+
+@ Here finally is |get_x_next|.
+
+The expression scanning routines to be considered later
+communicate via the global quantities |cur_type| and |cur_exp|;
+we must be very careful to save and restore these quantities while
+macros are being expanded.
+@^inner loop@>
+
+@p procedure get_x_next;
+var @!save_exp:pointer; {a capsule to save |cur_type| and |cur_exp|}
+begin get_next;
+if cur_cmd<min_command then
+ begin save_exp:=stash_cur_exp;
+ repeat if cur_cmd=defined_macro then macro_call(cur_mod,null,cur_sym)
+ else expand;
+ get_next;
+ until cur_cmd>=min_command;
+ unstash_cur_exp(save_exp); {that restores |cur_type| and |cur_exp|}
+ end;
+end;
+
+@ Now let's consider the |macro_call| procedure, which is used to start up
+all user-defined macros. Since the arguments to a macro might be expressions,
+|macro_call| is recursive.
+@^recursion@>
+
+The first parameter to |macro_call| points to the reference count of the
+token list that defines the macro. The second parameter contains any
+arguments that have already been parsed (see below). The third parameter
+points to the symbolic token that names the macro. If the third parameter
+is |null|, the macro was defined by \&{vardef}, so its name can be
+reconstructed from the prefix and ``at'' arguments found within the
+second parameter.
+
+What is this second parameter? It's simply a linked list of one-word items,
+whose |info| fields point to the arguments. In other words, if |arg_list=null|,
+no arguments have been scanned yet; otherwise |info(arg_list)| points to
+the first scanned argument, and |link(arg_list)| points to the list of
+further arguments (if any).
+
+Arguments of type \&{expr} are so-called capsules, which we will
+discuss later when we concentrate on expressions; they can be
+recognized easily because their |link| field is |void|. Arguments of type
+\&{suffix} and \&{text} are token lists without reference counts.
+
+@ After argument scanning is complete, the arguments are moved to the
+|param_stack|. (They can't be put on that stack any sooner, because
+the stack is growing and shrinking in unpredictable ways as more arguments
+are being acquired.) Then the macro body is fed to the scanner; i.e.,
+the replacement text of the macro is placed at the top of the \MF's
+input stack, so that |get_next| will proceed to read it next.
+
+@<Declare the procedure called |macro_call|@>=
+@t\4@>@<Declare the procedure called |print_macro_name|@>@;
+@t\4@>@<Declare the procedure called |print_arg|@>@;
+@t\4@>@<Declare the procedure called |scan_text_arg|@>@;
+procedure macro_call(@!def_ref,@!arg_list,@!macro_name:pointer);
+ {invokes a user-defined control sequence}
+label found;
+var @!r:pointer; {current node in the macro's token list}
+@!p,@!q:pointer; {for list manipulation}
+@!n:integer; {the number of arguments}
+@!l_delim,@!r_delim:pointer; {a delimiter pair}
+@!tail:pointer; {tail of the argument list}
+begin r:=link(def_ref); add_mac_ref(def_ref);
+if arg_list=null then n:=0
+else @<Determine the number |n| of arguments already supplied,
+ and set |tail| to the tail of |arg_list|@>;
+if internal[tracing_macros]>0 then
+ @<Show the text of the macro being expanded, and the existing arguments@>;
+@<Scan the remaining arguments, if any; set |r| to the first token
+ of the replacement text@>;
+@<Feed the arguments and replacement text to the scanner@>;
+end;
+
+@ @<Show the text of the macro...@>=
+begin begin_diagnostic; print_ln; print_macro_name(arg_list,macro_name);
+if n=3 then print("@@#"); {indicate a suffixed macro}
+show_macro(def_ref,null,100000);
+if arg_list<>null then
+ begin n:=0; p:=arg_list;
+ repeat q:=info(p);
+ print_arg(q,n,0);
+ incr(n); p:=link(p);
+ until p=null;
+ end;
+end_diagnostic(false);
+end
+
+@ @<Declare the procedure called |print_macro_name|@>=
+procedure print_macro_name(@!a,@!n:pointer);
+var @!p,@!q:pointer; {they traverse the first part of |a|}
+begin if n<>null then slow_print(text(n))
+else begin p:=info(a);
+ if p=null then slow_print(text(info(info(link(a)))))
+ else begin q:=p;
+ while link(q)<>null do q:=link(q);
+ link(q):=info(link(a));
+ show_token_list(p,null,1000,0);
+ link(q):=null;
+ end;
+ end;
+end;
+
+@ @<Declare the procedure called |print_arg|@>=
+procedure print_arg(@!q:pointer;@!n:integer;@!b:pointer);
+begin if link(q)=void then print_nl("(EXPR")
+else if (b<text_base)and(b<>text_macro) then print_nl("(SUFFIX")
+else print_nl("(TEXT");
+print_int(n); print(")<-");
+if link(q)=void then print_exp(q,1)
+else show_token_list(q,null,1000,0);
+end;
+
+@ @<Determine the number |n| of arguments already supplied...@>=
+begin n:=1; tail:=arg_list;
+while link(tail)<>null do
+ begin incr(n); tail:=link(tail);
+ end;
+end
+
+@ @<Scan the remaining arguments, if any; set |r|...@>=
+cur_cmd:=comma+1; {anything |<>comma| will do}
+while info(r)>=expr_base do
+ begin @<Scan the delimited argument represented by |info(r)|@>;
+ r:=link(r);
+ end;
+if cur_cmd=comma then
+ begin print_err("Too many arguments to ");
+@.Too many arguments...@>
+ print_macro_name(arg_list,macro_name); print_char(";");
+ print_nl(" Missing `"); slow_print(text(r_delim));
+@.Missing `)'...@>
+ print("' has been inserted");
+ help3("I'm going to assume that the comma I just read was a")@/
+ ("right delimiter, and then I'll begin expanding the macro.")@/
+ ("You might want to delete some tokens before continuing.");
+ error;
+ end;
+if info(r)<>general_macro then @<Scan undelimited argument(s)@>;
+r:=link(r)
+
+@ At this point, the reader will find it advisable to review the explanation
+of token list format that was presented earlier, paying special attention to
+the conventions that apply only at the beginning of a macro's token list.
+
+On the other hand, the reader will have to take the expression-parsing
+aspects of the following program on faith; we will explain |cur_type|
+and |cur_exp| later. (Several things in this program depend on each other,
+and it's necessary to jump into the circle somewhere.)
+
+@<Scan the delimited argument represented by |info(r)|@>=
+if cur_cmd<>comma then
+ begin get_x_next;
+ if cur_cmd<>left_delimiter then
+ begin print_err("Missing argument to ");
+@.Missing argument...@>
+ print_macro_name(arg_list,macro_name);
+ help3("That macro has more parameters than you thought.")@/
+ ("I'll continue by pretending that each missing argument")@/
+ ("is either zero or null.");
+ if info(r)>=suffix_base then
+ begin cur_exp:=null; cur_type:=token_list;
+ end
+ else begin cur_exp:=0; cur_type:=known;
+ end;
+ back_error; cur_cmd:=right_delimiter; goto found;
+ end;
+ l_delim:=cur_sym; r_delim:=cur_mod;
+ end;
+@<Scan the argument represented by |info(r)|@>;
+if cur_cmd<>comma then @<Check that the proper right delimiter was present@>;
+found: @<Append the current expression to |arg_list|@>
+
+@ @<Check that the proper right delim...@>=
+if (cur_cmd<>right_delimiter)or(cur_mod<>l_delim) then
+ if info(link(r))>=expr_base then
+ begin missing_err(",");
+@.Missing `,'@>
+ help3("I've finished reading a macro argument and am about to")@/
+ ("read another; the arguments weren't delimited correctly.")@/
+ ("You might want to delete some tokens before continuing.");
+ back_error; cur_cmd:=comma;
+ end
+ else begin missing_err(text(r_delim));
+@.Missing `)'@>
+ help2("I've gotten to the end of the macro parameter list.")@/
+ ("You might want to delete some tokens before continuing.");
+ back_error;
+ end
+
+@ A \&{suffix} or \&{text} parameter will have been scanned as
+a token list pointed to by |cur_exp|, in which case we will have
+|cur_type=token_list|.
+
+@<Append the current expression to |arg_list|@>=
+begin p:=get_avail;
+if cur_type=token_list then info(p):=cur_exp
+else info(p):=stash_cur_exp;
+if internal[tracing_macros]>0 then
+ begin begin_diagnostic; print_arg(info(p),n,info(r)); end_diagnostic(false);
+ end;
+if arg_list=null then arg_list:=p
+else link(tail):=p;
+tail:=p; incr(n);
+end
+
+@ @<Scan the argument represented by |info(r)|@>=
+if info(r)>=text_base then scan_text_arg(l_delim,r_delim)
+else begin get_x_next;
+ if info(r)>=suffix_base then scan_suffix
+ else scan_expression;
+ end
+
+@ The parameters to |scan_text_arg| are either a pair of delimiters
+or zero; the latter case is for undelimited text arguments, which
+end with the first semicolon or \&{endgroup} or \&{end} that is not
+contained in a group.
+
+@<Declare the procedure called |scan_text_arg|@>=
+procedure scan_text_arg(@!l_delim,@!r_delim:pointer);
+label done;
+var @!balance:integer; {excess of |l_delim| over |r_delim|}
+@!p:pointer; {list tail}
+begin warning_info:=l_delim; scanner_status:=absorbing;
+p:=hold_head; balance:=1; link(hold_head):=null;
+loop@+ begin get_next;
+ if l_delim=0 then @<Adjust the balance for an undelimited argument;
+ |goto done| if done@>
+ else @<Adjust the balance for a delimited argument;
+ |goto done| if done@>;
+ link(p):=cur_tok; p:=link(p);
+ end;
+done: cur_exp:=link(hold_head); cur_type:=token_list;
+scanner_status:=normal;
+end;
+
+@ @<Adjust the balance for a delimited argument...@>=
+begin if cur_cmd=right_delimiter then
+ begin if cur_mod=l_delim then
+ begin decr(balance);
+ if balance=0 then goto done;
+ end;
+ end
+else if cur_cmd=left_delimiter then if cur_mod=r_delim then incr(balance);
+end
+
+@ @<Adjust the balance for an undelimited...@>=
+begin if end_of_statement then {|cur_cmd=semicolon|, |end_group|, or |stop|}
+ begin if balance=1 then goto done
+ else if cur_cmd=end_group then decr(balance);
+ end
+else if cur_cmd=begin_group then incr(balance);
+end
+
+@ @<Scan undelimited argument(s)@>=
+begin if info(r)<text_macro then
+ begin get_x_next;
+ if info(r)<>suffix_macro then
+ if (cur_cmd=equals)or(cur_cmd=assignment) then get_x_next;
+ end;
+case info(r) of
+primary_macro:scan_primary;
+secondary_macro:scan_secondary;
+tertiary_macro:scan_tertiary;
+expr_macro:scan_expression;
+of_macro:@<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>;
+suffix_macro:@<Scan a suffix with optional delimiters@>;
+text_macro:scan_text_arg(0,0);
+end; {there are no other cases}
+back_input; @<Append the current expression to |arg_list|@>;
+end
+
+@ @<Scan an expression followed by `\&{of} $\langle$primary$\rangle$'@>=
+begin scan_expression; p:=get_avail; info(p):=stash_cur_exp;
+if internal[tracing_macros]>0 then
+ begin begin_diagnostic; print_arg(info(p),n,0); end_diagnostic(false);
+ end;
+if arg_list=null then arg_list:=p@+else link(tail):=p;
+tail:=p;incr(n);
+if cur_cmd<>of_token then
+ begin missing_err("of"); print(" for ");
+@.Missing `of'@>
+ print_macro_name(arg_list,macro_name);
+ help1("I've got the first argument; will look now for the other.");
+ back_error;
+ end;
+get_x_next; scan_primary;
+end
+
+@ @<Scan a suffix with optional delimiters@>=
+begin if cur_cmd<>left_delimiter then l_delim:=null
+else begin l_delim:=cur_sym; r_delim:=cur_mod; get_x_next;
+ end;
+scan_suffix;
+if l_delim<>null then
+ begin if(cur_cmd<>right_delimiter)or(cur_mod<>l_delim) then
+ begin missing_err(text(r_delim));
+@.Missing `)'@>
+ help2("I've gotten to the end of the macro parameter list.")@/
+ ("You might want to delete some tokens before continuing.");
+ back_error;
+ end;
+ get_x_next;
+ end;
+end
+
+@ Before we put a new token list on the input stack, it is wise to clean off
+all token lists that have recently been depleted. Then a user macro that ends
+with a call to itself will not require unbounded stack space.
+
+@<Feed the arguments and replacement text to the scanner@>=
+while token_state and(loc=null) do end_token_list; {conserve stack space}
+if param_ptr+n>max_param_stack then
+ begin max_param_stack:=param_ptr+n;
+ if max_param_stack>param_size then
+ overflow("parameter stack size",param_size);
+@:METAFONT capacity exceeded parameter stack size}{\quad parameter stack size@>
+ end;
+begin_token_list(def_ref,macro); name:=macro_name; loc:=r;
+if n>0 then
+ begin p:=arg_list;
+ repeat param_stack[param_ptr]:=info(p); incr(param_ptr); p:=link(p);
+ until p=null;
+ flush_list(arg_list);
+ end
+
+@ It's sometimes necessary to put a single argument onto |param_stack|.
+The |stack_argument| subroutine does this.
+
+@p procedure stack_argument(@!p:pointer);
+begin if param_ptr=max_param_stack then
+ begin incr(max_param_stack);
+ if max_param_stack>param_size then
+ overflow("parameter stack size",param_size);
+@:METAFONT capacity exceeded parameter stack size}{\quad parameter stack size@>
+ end;
+param_stack[param_ptr]:=p; incr(param_ptr);
+end;
+
+@* \[36] Conditional processing.
+Let's consider now the way \&{if} commands are handled.
+
+Conditions can be inside conditions, and this nesting has a stack
+that is independent of other stacks.
+Four global variables represent the top of the condition stack:
+|cond_ptr| points to pushed-down entries, if~any; |cur_if| tells whether
+we are processing \&{if} or \&{elseif}; |if_limit| specifies
+the largest code of a |fi_or_else| command that is syntactically legal;
+and |if_line| is the line number at which the current conditional began.
+
+If no conditions are currently in progress, the condition stack has the
+special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|.
+Otherwise |cond_ptr| points to a two-word node; the |type|, |name_type|, and
+|link| fields of the first word contain |if_limit|, |cur_if|, and
+|cond_ptr| at the next level, and the second word contains the
+corresponding |if_line|.
+
+@d if_node_size=2 {number of words in stack entry for conditionals}
+@d if_line_field(#)==mem[#+1].int
+@d if_code=1 {code for \&{if} being evaluated}
+@d fi_code=2 {code for \&{fi}}
+@d else_code=3 {code for \&{else}}
+@d else_if_code=4 {code for \&{elseif}}
+
+@<Glob...@>=
+@!cond_ptr:pointer; {top of the condition stack}
+@!if_limit:normal..else_if_code; {upper bound on |fi_or_else| codes}
+@!cur_if:small_number; {type of conditional being worked on}
+@!if_line:integer; {line where that conditional began}
+
+@ @<Set init...@>=
+cond_ptr:=null; if_limit:=normal; cur_if:=0; if_line:=0;
+
+@ @<Put each...@>=
+primitive("if",if_test,if_code);@/
+@!@:if_}{\&{if} primitive@>
+primitive("fi",fi_or_else,fi_code); eqtb[frozen_fi]:=eqtb[cur_sym];@/
+@!@:fi_}{\&{fi} primitive@>
+primitive("else",fi_or_else,else_code);@/
+@!@:else_}{\&{else} primitive@>
+primitive("elseif",fi_or_else,else_if_code);@/
+@!@:else_if_}{\&{elseif} primitive@>
+
+@ @<Cases of |print_cmd_mod|...@>=
+if_test,fi_or_else: case m of
+ if_code:print("if");
+ fi_code:print("fi");
+ else_code:print("else");
+ othercases print("elseif")
+ endcases;
+
+@ Here is a procedure that ignores text until coming to an \&{elseif},
+\&{else}, or \&{fi} at level zero of $\&{if}\ldots\&{fi}$
+nesting. After it has acted, |cur_mod| will indicate the token that
+was found.
+
+\MF's smallest two command codes are |if_test| and |fi_or_else|; this
+makes the skipping process a bit simpler.
+
+@p procedure pass_text;
+label done;
+var l:integer;
+begin scanner_status:=skipping; l:=0; warning_info:=line;
+loop@+ begin get_next;
+ if cur_cmd<=fi_or_else then
+ if cur_cmd<fi_or_else then incr(l)
+ else begin if l=0 then goto done;
+ if cur_mod=fi_code then decr(l);
+ end
+ else @<Decrease the string reference count,
+ if the current token is a string@>;
+ end;
+done: scanner_status:=normal;
+end;
+
+@ @<Decrease the string reference count...@>=
+if cur_cmd=string_token then delete_str_ref(cur_mod)
+
+@ When we begin to process a new \&{if}, we set |if_limit:=if_code|; then
+if \&{elseif} or \&{else} or \&{fi} occurs before the current \&{if}
+condition has been evaluated, a colon will be inserted.
+A construction like `\.{if fi}' would otherwise get \MF\ confused.
+
+@<Push the condition stack@>=
+begin p:=get_node(if_node_size); link(p):=cond_ptr; type(p):=if_limit;
+name_type(p):=cur_if; if_line_field(p):=if_line;
+cond_ptr:=p; if_limit:=if_code; if_line:=line; cur_if:=if_code;
+end
+
+@ @<Pop the condition stack@>=
+begin p:=cond_ptr; if_line:=if_line_field(p);
+cur_if:=name_type(p); if_limit:=type(p); cond_ptr:=link(p);
+free_node(p,if_node_size);
+end
+
+@ Here's a procedure that changes the |if_limit| code corresponding to
+a given value of |cond_ptr|.
+
+@p procedure change_if_limit(@!l:small_number;@!p:pointer);
+label exit;
+var q:pointer;
+begin if p=cond_ptr then if_limit:=l {that's the easy case}
+else begin q:=cond_ptr;
+ loop@+ begin if q=null then confusion("if");
+@:this can't happen if}{\quad if@>
+ if link(q)=p then
+ begin type(q):=l; return;
+ end;
+ q:=link(q);
+ end;
+ end;
+exit:end;
+
+@ The user is supposed to put colons into the proper parts of conditional
+statements. Therefore, \MF\ has to check for their presence.
+
+@p procedure check_colon;
+begin if cur_cmd<>colon then
+ begin missing_err(":");@/
+@.Missing `:'@>
+ help2("There should've been a colon after the condition.")@/
+ ("I shall pretend that one was there.");@;
+ back_error;
+ end;
+end;
+
+@ A condition is started when the |get_x_next| procedure encounters
+an |if_test| command; in that case |get_x_next| calls |conditional|,
+which is a recursive procedure.
+@^recursion@>
+
+@p procedure conditional;
+label exit,done,reswitch,found;
+var @!save_cond_ptr:pointer; {|cond_ptr| corresponding to this conditional}
+@!new_if_limit:fi_code..else_if_code; {future value of |if_limit|}
+@!p:pointer; {temporary register}
+begin @<Push the condition stack@>;@+save_cond_ptr:=cond_ptr;
+reswitch: get_boolean; new_if_limit:=else_if_code;
+if internal[tracing_commands]>unity then
+ @<Display the boolean value of |cur_exp|@>;
+found: check_colon;
+if cur_exp=true_code then
+ begin change_if_limit(new_if_limit,save_cond_ptr);
+ return; {wait for \&{elseif}, \&{else}, or \&{fi}}
+ end;
+@<Skip to \&{elseif} or \&{else} or \&{fi}, then |goto done|@>;
+done: cur_if:=cur_mod; if_line:=line;
+if cur_mod=fi_code then @<Pop the condition stack@>
+else if cur_mod=else_if_code then goto reswitch
+else begin cur_exp:=true_code; new_if_limit:=fi_code; get_x_next; goto found;
+ end;
+exit:end;
+
+@ In a construction like `\&{if} \&{if} \&{true}: $0=1$: \\{foo}
+\&{else}: \\{bar} \&{fi}', the first \&{else}
+that we come to after learning that the \&{if} is false is not the
+\&{else} we're looking for. Hence the following curious logic is needed.
+
+@<Skip to \&{elseif}...@>=
+loop@+ begin pass_text;
+ if cond_ptr=save_cond_ptr then goto done
+ else if cur_mod=fi_code then @<Pop the condition stack@>;
+ end
+
+
+@ @<Display the boolean value...@>=
+begin begin_diagnostic;
+if cur_exp=true_code then print("{true}")@+else print("{false}");
+end_diagnostic(false);
+end
+
+@ The processing of conditionals is complete except for the following
+code, which is actually part of |get_x_next|. It comes into play when
+\&{elseif}, \&{else}, or \&{fi} is scanned.
+
+@<Terminate the current conditional and skip to \&{fi}@>=
+if cur_mod>if_limit then
+ if if_limit=if_code then {condition not yet evaluated}
+ begin missing_err(":");
+@.Missing `:'@>
+ back_input; cur_sym:=frozen_colon; ins_error;
+ end
+ else begin print_err("Extra "); print_cmd_mod(fi_or_else,cur_mod);
+@.Extra else@>
+@.Extra elseif@>
+@.Extra fi@>
+ help1("I'm ignoring this; it doesn't match any if.");
+ error;
+ end
+else begin while cur_mod<>fi_code do pass_text; {skip to \&{fi}}
+ @<Pop the condition stack@>;
+ end
+
+@* \[37] Iterations.
+To bring our treatment of |get_x_next| to a close, we need to consider what
+\MF\ does when it sees \&{for}, \&{forsuffixes}, and \&{forever}.
+
+There's a global variable |loop_ptr| that keeps track of the \&{for} loops
+that are currently active. If |loop_ptr=null|, no loops are in progress;
+otherwise |info(loop_ptr)| points to the iterative text of the current
+(innermost) loop, and |link(loop_ptr)| points to the data for any other
+loops that enclose the current one.
+
+A loop-control node also has two other fields, called |loop_type| and
+|loop_list|, whose contents depend on the type of loop:
+
+\yskip\indent|loop_type(loop_ptr)=null| means that |loop_list(loop_ptr)|
+points to a list of one-word nodes whose |info| fields point to the
+remaining argument values of a suffix list and expression list.
+
+\yskip\indent|loop_type(loop_ptr)=void| means that the current loop is
+`\&{forever}'.
+
+\yskip\indent|loop_type(loop_ptr)=p>void| means that |value(p)|,
+|step_size(p)|, and |final_value(p)| contain the data for an arithmetic
+progression.
+
+\yskip\noindent In the latter case, |p| points to a ``progression node''
+whose first word is not used. (No value could be stored there because the
+link field of words in the dynamic memory area cannot be arbitrary.)
+
+@d loop_list_loc(#)==#+1 {where the |loop_list| field resides}
+@d loop_type(#)==info(loop_list_loc(#)) {the type of \&{for} loop}
+@d loop_list(#)==link(loop_list_loc(#)) {the remaining list elements}
+@d loop_node_size=2 {the number of words in a loop control node}
+@d progression_node_size=4 {the number of words in a progression node}
+@d step_size(#)==mem[#+2].sc {the step size in an arithmetic progression}
+@d final_value(#)==mem[#+3].sc {the final value in an arithmetic progression}
+
+@<Glob...@>=
+@!loop_ptr:pointer; {top of the loop-control-node stack}
+
+@ @<Set init...@>=
+loop_ptr:=null;
+
+@ If the expressions that define an arithmetic progression in
+a \&{for} loop don't have known numeric values, the |bad_for|
+subroutine screams at the user.
+
+@p procedure bad_for(@!s:str_number);
+begin disp_err(null,"Improper "); {show the bad expression above the message}
+@.Improper...replaced by 0@>
+print(s); print(" has been replaced by 0");
+help4("When you say `for x=a step b until c',")@/
+ ("the initial value `a' and the step size `b'")@/
+ ("and the final value `c' must have known numeric values.")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+put_get_flush_error(0);
+end;
+
+@ Here's what \MF\ does when \&{for}, \&{forsuffixes}, or \&{forever}
+has just been scanned. (This code requires slight familiarity with
+expression-parsing routines that we have not yet discussed; but it seems
+to belong in the present part of the program, even though the author
+didn't write it until later. The reader may wish to come back to it.)
+
+@p procedure begin_iteration;
+label continue,done,found;
+var @!m:halfword; {|expr_base| (\&{for}) or |suffix_base| (\&{forsuffixes})}
+@!n:halfword; {hash address of the current symbol}
+@!p,@!q,@!s,@!pp:pointer; {link manipulation registers}
+begin m:=cur_mod; n:=cur_sym; s:=get_node(loop_node_size);
+if m=start_forever then
+ begin loop_type(s):=void; p:=null; get_x_next; goto found;
+ end;
+get_symbol; p:=get_node(token_node_size); info(p):=cur_sym; value(p):=m;@/
+get_x_next;
+if (cur_cmd<>equals)and(cur_cmd<>assignment) then
+ begin missing_err("=");@/
+@.Missing `='@>
+ help3("The next thing in this loop should have been `=' or `:='.")@/
+ ("But don't worry; I'll pretend that an equals sign")@/
+ ("was present, and I'll look for the values next.");@/
+ back_error;
+ end;
+@<Scan the values to be used in the loop@>;
+found:@<Check for the presence of a colon@>;
+@<Scan the loop text and put it on the loop control stack@>;
+resume_iteration;
+end;
+
+@ @<Check for the presence of a colon@>=
+if cur_cmd<>colon then
+ begin missing_err(":");@/
+@.Missing `:'@>
+ help3("The next thing in this loop should have been a `:'.")@/
+ ("So I'll pretend that a colon was present;")@/
+ ("everything from here to `endfor' will be iterated.");
+ back_error;
+ end
+
+@ We append a special |frozen_repeat_loop| token in place of the
+`\&{endfor}' at the end of the loop. This will come through \MF's scanner
+at the proper time to cause the loop to be repeated.
+
+(If the user tries some shenanigan like `\&{for} $\ldots$ \&{let} \&{endfor}',
+he will be foiled by the |get_symbol| routine, which keeps frozen
+tokens unchanged. Furthermore the |frozen_repeat_loop| is an \&{outer}
+token, so it won't be lost accidentally.)
+
+@ @<Scan the loop text...@>=
+q:=get_avail; info(q):=frozen_repeat_loop;
+scanner_status:=loop_defining; warning_info:=n;
+info(s):=scan_toks(iteration,p,q,0); scanner_status:=normal;@/
+link(s):=loop_ptr; loop_ptr:=s
+
+@ @<Initialize table...@>=
+eq_type(frozen_repeat_loop):=repeat_loop+outer_tag;
+text(frozen_repeat_loop):=" ENDFOR";
+
+@ The loop text is inserted into \MF's scanning apparatus by the
+|resume_iteration| routine.
+
+@p procedure resume_iteration;
+label not_found,exit;
+var @!p,@!q:pointer; {link registers}
+begin p:=loop_type(loop_ptr);
+if p>void then {|p| points to a progression node}
+ begin cur_exp:=value(p);
+ if @<The arithmetic progression has ended@> then goto not_found;
+ cur_type:=known; q:=stash_cur_exp; {make |q| an \&{expr} argument}
+ value(p):=cur_exp+step_size(p); {set |value(p)| for the next iteration}
+ end
+else if p<void then
+ begin p:=loop_list(loop_ptr);
+ if p=null then goto not_found;
+ loop_list(loop_ptr):=link(p); q:=info(p); free_avail(p);
+ end
+else begin begin_token_list(info(loop_ptr),forever_text); return;
+ end;
+begin_token_list(info(loop_ptr),loop_text);
+stack_argument(q);
+if internal[tracing_commands]>unity then @<Trace the start of a loop@>;
+return;
+not_found:stop_iteration;
+exit:end;
+
+@ @<The arithmetic progression has ended@>=
+((step_size(p)>0)and(cur_exp>final_value(p)))or@|
+ ((step_size(p)<0)and(cur_exp<final_value(p)))
+
+@ @<Trace the start of a loop@>=
+begin begin_diagnostic; print_nl("{loop value=");
+@.loop value=n@>
+if (q<>null)and(link(q)=void) then print_exp(q,1)
+else show_token_list(q,null,50,0);
+print_char("}"); end_diagnostic(false);
+end
+
+@ A level of loop control disappears when |resume_iteration| has decided
+not to resume, or when an \&{exitif} construction has removed the loop text
+from the input stack.
+
+@p procedure stop_iteration;
+var @!p,@!q:pointer; {the usual}
+begin p:=loop_type(loop_ptr);
+if p>void then free_node(p,progression_node_size)
+else if p<void then
+ begin q:=loop_list(loop_ptr);
+ while q<>null do
+ begin p:=info(q);
+ if p<>null then
+ if link(p)=void then {it's an \&{expr} parameter}
+ begin recycle_value(p); free_node(p,value_node_size);
+ end
+ else flush_token_list(p); {it's a \&{suffix} or \&{text} parameter}
+ p:=q; q:=link(q); free_avail(p);
+ end;
+ end;
+p:=loop_ptr; loop_ptr:=link(p); flush_token_list(info(p));
+free_node(p,loop_node_size);
+end;
+
+@ Now that we know all about loop control, we can finish up
+the missing portion of |begin_iteration| and we'll be done.
+
+The following code is performed after the `\.=' has been scanned in
+a \&{for} construction (if |m=expr_base|) or a \&{forsuffixes} construction
+(if |m=suffix_base|).
+
+@<Scan the values to be used in the loop@>=
+loop_type(s):=null; q:=loop_list_loc(s); link(q):=null; {|link(q)=loop_list(s)|}
+repeat get_x_next;
+if m<>expr_base then scan_suffix
+else begin if cur_cmd>=colon then if cur_cmd<=comma then goto continue;
+ scan_expression;
+ if cur_cmd=step_token then if q=loop_list_loc(s) then
+ @<Prepare for step-until construction and |goto done|@>;
+ cur_exp:=stash_cur_exp;
+ end;
+link(q):=get_avail; q:=link(q); info(q):=cur_exp; cur_type:=vacuous;
+continue: until cur_cmd<>comma;
+done:
+
+@ @<Prepare for step-until construction and |goto done|@>=
+begin if cur_type<>known then bad_for("initial value");
+pp:=get_node(progression_node_size); value(pp):=cur_exp;@/
+get_x_next; scan_expression;
+if cur_type<>known then bad_for("step size");
+step_size(pp):=cur_exp;
+if cur_cmd<>until_token then
+ begin missing_err("until");@/
+@.Missing `until'@>
+ help2("I assume you meant to say `until' after `step'.")@/
+ ("So I'll look for the final value and colon next.");
+ back_error;
+ end;
+get_x_next; scan_expression;
+if cur_type<>known then bad_for("final value");
+final_value(pp):=cur_exp; loop_type(s):=pp; goto done;
+end
+
+@* \[38] File names.
+It's time now to fret about file names. Besides the fact that different
+operating systems treat files in different ways, we must cope with the
+fact that completely different naming conventions are used by different
+groups of people. The following programs show what is required for one
+particular operating system; similar routines for other systems are not
+difficult to devise.
+@^system dependencies@>
+
+\MF\ assumes that a file name has three parts: the name proper; its
+``extension''; and a ``file area'' where it is found in an external file
+system. The extension of an input file is assumed to be
+`\.{.mf}' unless otherwise specified; it is `\.{.log}' on the
+transcript file that records each run of \MF; it is `\.{.tfm}' on the font
+metric files that describe characters in the fonts \MF\ creates; it is
+`\.{.gf}' on the output files that specify generic font information; and it
+is `\.{.base}' on the base files written by \.{INIMF} to initialize \MF.
+The file area can be arbitrary on input files, but files are usually
+output to the user's current area. If an input file cannot be
+found on the specified area, \MF\ will look for it on a special system
+area; this special area is intended for commonly used input files.
+
+Simple uses of \MF\ refer only to file names that have no explicit
+extension or area. For example, a person usually says `\.{input} \.{cmr10}'
+instead of `\.{input} \.{cmr10.new}'. Simple file
+names are best, because they make the \MF\ source files portable;
+whenever a file name consists entirely of letters and digits, it should be
+treated in the same way by all implementations of \MF. However, users
+need the ability to refer to other files in their environment, especially
+when responding to error messages concerning unopenable files; therefore
+we want to let them use the syntax that appears in their favorite
+operating system.
+
+@ \MF\ uses the same conventions that have proved to be satisfactory for
+\TeX. In order to isolate the system-dependent aspects of file names, the
+@^system dependencies@>
+system-independent parts of \MF\ are expressed in terms
+of three system-dependent
+procedures called |begin_name|, |more_name|, and |end_name|. In
+essence, if the user-specified characters of the file name are $c_1\ldots c_n$,
+the system-independent driver program does the operations
+$$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n);
+\,|end_name|.$$
+These three procedures communicate with each other via global variables.
+Afterwards the file name will appear in the string pool as three strings
+called |cur_name|\penalty10000\hskip-.05em,
+|cur_area|, and |cur_ext|; the latter two are null (i.e.,
+|""|), unless they were explicitly specified by the user.
+
+Actually the situation is slightly more complicated, because \MF\ needs
+to know when the file name ends. The |more_name| routine is a function
+(with side effects) that returns |true| on the calls |more_name|$(c_1)$,
+\dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$
+returns |false|; or, it returns |true| and $c_n$ is the last character
+on the current input line. In other words,
+|more_name| is supposed to return |true| unless it is sure that the
+file name has been completely scanned; and |end_name| is supposed to be able
+to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
+whether $|more_name|(c_n)$ returned |true| or |false|.
+
+@<Glob...@>=
+@!cur_name:str_number; {name of file just scanned}
+@!cur_area:str_number; {file area just scanned, or \.{""}}
+@!cur_ext:str_number; {file extension just scanned, or \.{""}}
+
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+
+@ Input files that can't be found in the user's area may appear in a standard
+system area called |MF_area|.
+This system area name will, of course, vary from place to place.
+@^system dependencies@>
+
+@d MF_area=="MFinputs:"
+@.MFinputs@>
+
+@ Here now is the first of the system-dependent routines for file name scanning.
+@^system dependencies@>
+
+@p procedure begin_name;
+begin area_delimiter:=0; ext_delimiter:=0;
+end;
+
+@ And here's the second.
+@^system dependencies@>
+
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin if (c=">")or(c=":") then
+ begin area_delimiter:=pool_ptr; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
+ str_room(1); append_char(c); {contribute |c| to the current string}
+ more_name:=true;
+ end;
+end;
+
+@ The third.
+@^system dependencies@>
+
+@p procedure end_name;
+begin if str_ptr+3>max_str_ptr then
+ begin if str_ptr+3>max_strings then
+ overflow("number of strings",max_strings-init_str_ptr);
+@:METAFONT capacity exceeded number of strings}{\quad number of strings@>
+ max_str_ptr:=str_ptr+3;
+ end;
+if area_delimiter=0 then cur_area:=""
+else begin cur_area:=str_ptr; incr(str_ptr);
+ str_start[str_ptr]:=area_delimiter+1;
+ end;
+if ext_delimiter=0 then
+ begin cur_ext:=""; cur_name:=make_string;
+ end
+else begin cur_name:=str_ptr; incr(str_ptr);
+ str_start[str_ptr]:=ext_delimiter; cur_ext:=make_string;
+ end;
+end;
+
+@ Conversely, here is a routine that takes three strings and prints a file
+name that might have produced them. (The routine is system dependent, because
+some operating systems put the file area last instead of first.)
+@^system dependencies@>
+
+@<Basic printing...@>=
+procedure print_file_name(@!n,@!a,@!e:integer);
+begin slow_print(a); slow_print(n); slow_print(e);
+end;
+
+@ Another system-dependent routine is needed to convert three internal
+\MF\ strings
+to the |name_of_file| value that is used to open files. The present code
+allows both lowercase and uppercase letters in the file name.
+@^system dependencies@>
+
+@d append_to_name(#)==begin c:=#; incr(k);
+ if k<=file_name_size then name_of_file[k]:=xchr[c];
+ end
+
+@p procedure pack_file_name(@!n,@!a,@!e:str_number);
+var @!k:integer; {number of positions filled in |name_of_file|}
+@!c: ASCII_code; {character being packed}
+@!j:pool_pointer; {index into |str_pool|}
+begin k:=0;
+for j:=str_start[a] to str_start[a+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[n] to str_start[n+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[e] to str_start[e+1]-1 do append_to_name(so(str_pool[j]));
+if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
+for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
+end;
+
+@ A messier routine is also needed, since base file names must be scanned
+before \MF's string mechanism has been initialized. We shall use the
+global variable |MF_base_default| to supply the text for default system areas
+and extensions related to base files.
+@^system dependencies@>
+
+@d base_default_length=18 {length of the |MF_base_default| string}
+@d base_area_length=8 {length of its area part}
+@d base_ext_length=5 {length of its `\.{.base}' part}
+@d base_extension=".base" {the extension, as a \.{WEB} constant}
+
+@<Glob...@>=
+@!MF_base_default:packed array[1..base_default_length] of char;
+
+@ @<Set init...@>=
+MF_base_default:='MFbases:plain.base';
+@.MFbases@>
+@.plain@>
+@^system dependencies@>
+
+@ @<Check the ``constant'' values for consistency@>=
+if base_default_length>file_name_size then bad:=41;
+
+@ Here is the messy routine that was just mentioned. It sets |name_of_file|
+from the first |n| characters of |MF_base_default|, followed by
+|buffer[a..b]|, followed by the last |base_ext_length| characters of
+|MF_base_default|.
+
+We dare not give error messages here, since \MF\ calls this routine before
+the |error| routine is ready to roll. Instead, we simply drop excess characters,
+since the error will be detected in another way when a strange file name
+isn't found.
+@^system dependencies@>
+
+@p procedure pack_buffered_name(@!n:small_number;@!a,@!b:integer);
+var @!k:integer; {number of positions filled in |name_of_file|}
+@!c: ASCII_code; {character being packed}
+@!j:integer; {index into |buffer| or |MF_base_default|}
+begin if n+b-a+1+base_ext_length>file_name_size then
+ b:=a+file_name_size-n-1-base_ext_length;
+k:=0;
+for j:=1 to n do append_to_name(xord[MF_base_default[j]]);
+for j:=a to b do append_to_name(buffer[j]);
+for j:=base_default_length-base_ext_length+1 to base_default_length do
+ append_to_name(xord[MF_base_default[j]]);
+if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
+for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
+end;
+
+@ Here is the only place we use |pack_buffered_name|. This part of the program
+becomes active when a ``virgin'' \MF\ is trying to get going, just after
+the preliminary initialization, or when the user is substituting another
+base file by typing `\.\&' after the initial `\.{**}' prompt. The buffer
+contains the first line of input in |buffer[loc..(last-1)]|, where
+|loc<last| and |buffer[loc]<>" "|.
+
+@<Declare the function called |open_base_file|@>=
+function open_base_file:boolean;
+label found,exit;
+var @!j:0..buf_size; {the first space after the file name}
+begin j:=loc;
+if buffer[loc]="&" then
+ begin incr(loc); j:=loc; buffer[last]:=" ";
+ while buffer[j]<>" " do incr(j);
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(base_file) then goto found;
+ pack_buffered_name(base_area_length,loc,j-1);
+ {now try the system base file area}
+ if w_open_in(base_file) then goto found;
+ wake_up_terminal;
+ wterm_ln('Sorry, I can''t find that base;',' will try PLAIN.');
+@.Sorry, I can't find...@>
+ update_terminal;
+ end;
+ {now pull out all the stops: try for the system \.{plain} file}
+pack_buffered_name(base_default_length-base_ext_length,1,0);
+if not w_open_in(base_file) then
+ begin wake_up_terminal;
+ wterm_ln('I can''t find the PLAIN base file!');
+@.I can't find PLAIN...@>
+@.plain@>
+ open_base_file:=false; return;
+ end;
+found:loc:=j; open_base_file:=true;
+exit:end;
+
+@ Operating systems often make it possible to determine the exact name (and
+possible version number) of a file that has been opened. The following routine,
+which simply makes a \MF\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var @!k:1..file_name_size; {index into |name_of_file|}
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings) then
+ make_name_string:="?"
+else begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+end;
+function a_make_name_string(var @!f:alpha_file):str_number;
+begin a_make_name_string:=make_name_string;
+end;
+function b_make_name_string(var @!f:byte_file):str_number;
+begin b_make_name_string:=make_name_string;
+end;
+function w_make_name_string(var @!f:word_file):str_number;
+begin w_make_name_string:=make_name_string;
+end;
+
+@ Now let's consider the ``driver''
+routines by which \MF\ deals with file names
+in a system-independent manner. First comes a procedure that looks for a
+file name in the input by taking the information from the input buffer.
+(We can't use |get_next|, because the conversion to tokens would
+destroy necessary information.)
+
+This procedure doesn't allow semicolons or percent signs to be part of
+file names, because of other conventions of \MF. The manual doesn't
+use semicolons or percents immediately after file names, but some users
+no doubt will find it natural to do so; therefore system-dependent
+changes to allow such characters in file names should probably
+be made with reluctance, and only when an entire file name that
+includes special characters is ``quoted'' somehow.
+@^system dependencies@>
+
+@p procedure scan_file_name;
+label done;
+begin begin_name;
+while buffer[loc]=" " do incr(loc);
+loop@+begin if (buffer[loc]=";")or(buffer[loc]="%") then goto done;
+ if not more_name(buffer[loc]) then goto done;
+ incr(loc);
+ end;
+done: end_name;
+end;
+
+@ The global variable |job_name| contains the file name that was first
+\&{input} by the user. This name is extended by `\.{.log}' and `\.{.gf}' and
+`\.{.base}' and `\.{.tfm}' in the names of \MF's output files.
+
+@<Glob...@>=
+@!job_name:str_number; {principal file name}
+@!log_opened:boolean; {has the transcript file been opened?}
+@!log_name:str_number; {full name of the log file}
+
+@ Initially |job_name=0|; it becomes nonzero as soon as the true name is known.
+We have |job_name=0| if and only if the `\.{log}' file has not been opened,
+except of course for a short time just after |job_name| has become nonzero.
+
+@<Initialize the output...@>=job_name:=0; log_opened:=false;
+
+@ Here is a routine that manufactures the output file names, assuming that
+|job_name<>0|. It ignores and changes the current settings of |cur_area|
+and |cur_ext|.
+
+@d pack_cur_name==pack_file_name(cur_name,cur_area,cur_ext)
+
+@p procedure pack_job_name(@!s:str_number); {|s = ".log"|, |".gf"|,
+ |".tfm"|, or |base_extension|}
+begin cur_area:=""; cur_ext:=s;
+cur_name:=job_name; pack_cur_name;
+end;
+
+@ Actually the main output file extension is usually something like
+|".300gf"| instead of just |".gf"|; the additional number indicates the
+resolution in pixels per inch, based on the setting of |hppp| when
+the file is opened.
+
+@<Glob...@>=
+@!gf_ext:str_number; {default extension for the output file}
+
+@ If some trouble arises when \MF\ tries to open a file, the following
+routine calls upon the user to supply another file name. Parameter~|s|
+is used in the error message to identify the type of file; parameter~|e|
+is the default extension if none is given. Upon exit from the routine,
+variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are
+ready for another attempt at file opening.
+
+@p procedure prompt_file_name(@!s,@!e:str_number);
+label done;
+var @!k:0..buf_size; {index into |buffer|}
+begin if interaction=scroll_mode then wake_up_terminal;
+if s="input file name" then print_err("I can't find file `")
+@.I can't find file x@>
+else print_err("I can't write on file `");
+@.I can't write on file x@>
+print_file_name(cur_name,cur_area,cur_ext); print("'.");
+if e=".mf" then show_context;
+print_nl("Please type another "); print(s);
+@.Please type...@>
+if interaction<scroll_mode then
+ fatal_error("*** (job aborted, file error in nonstop mode)");
+@.job aborted, file error...@>
+clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>;
+if cur_ext="" then cur_ext:=e;
+pack_cur_name;
+end;
+
+@ @<Scan file name in the buffer@>=
+begin begin_name; k:=first;
+while (buffer[k]=" ")and(k<last) do incr(k);
+loop@+ begin if k=last then goto done;
+ if not more_name(buffer[k]) then goto done;
+ incr(k);
+ end;
+done:end_name;
+end
+
+@ The |open_log_file| routine is used to open the transcript file and to help
+it catch up to what has previously been printed on the terminal.
+
+@p procedure open_log_file;
+var @!old_setting:0..max_selector; {previous |selector| setting}
+@!k:0..buf_size; {index into |months| and |buffer|}
+@!l:0..buf_size; {end of first input line}
+@!m:integer; {the current month}
+@!months:packed array [1..36] of char; {abbreviations of month names}
+begin old_setting:=selector;
+if job_name=0 then job_name:="mfput";
+@.mfput@>
+pack_job_name(".log");
+while not a_open_out(log_file) do @<Try to get a different log file name@>;
+log_name:=a_make_name_string(log_file);
+selector:=log_only; log_opened:=true;
+@<Print the banner line, including the date and time@>;
+input_stack[input_ptr]:=cur_input; {make sure bottom level is in memory}
+print_nl("**");
+@.**@>
+l:=input_stack[0].limit_field-1; {last position of first line}
+for k:=1 to l do print(buffer[k]);
+print_ln; {now the transcript file contains the first line of input}
+selector:=old_setting+2; {|log_only| or |term_and_log|}
+end;
+
+@ Sometimes |open_log_file| is called at awkward moments when \MF\ is
+unable to print error messages or even to |show_context|.
+The |prompt_file_name| routine can result in a |fatal_error|, but the |error|
+routine will not be invoked because |log_opened| will be false.
+
+The normal idea of |batch_mode| is that nothing at all should be written
+on the terminal. However, in the unusual case that
+no log file could be opened, we make an exception and allow
+an explanatory message to be seen.
+
+Incidentally, the program always refers to the log file as a `\.{transcript
+file}', because some systems cannot use the extension `\.{.log}' for
+this file.
+
+@<Try to get a different log file name@>=
+begin selector:=term_only;
+prompt_file_name("transcript file name",".log");
+end
+
+@ @<Print the banner...@>=
+begin wlog(banner);
+slow_print(base_ident); print(" ");
+print_int(round_unscaled(internal[day])); print_char(" ");
+months:='JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC';
+m:=round_unscaled(internal[month]);
+for k:=3*m-2 to 3*m do wlog(months[k]);
+print_char(" "); print_int(round_unscaled(internal[year])); print_char(" ");
+m:=round_unscaled(internal[time]);
+print_dd(m div 60); print_char(":"); print_dd(m mod 60);
+end
+
+@ Here's an example of how these file-name-parsing routines work in practice.
+We shall use the macro |set_output_file_name| when it is time to
+crank up the output file.
+
+@d set_output_file_name==
+ begin if job_name=0 then open_log_file;
+ pack_job_name(gf_ext);
+ while not b_open_out(gf_file) do
+ prompt_file_name("file name for output",gf_ext);
+ output_file_name:=b_make_name_string(gf_file);
+ end
+
+@<Glob...@>=
+@!gf_file: byte_file; {the generic font output goes here}
+@!output_file_name: str_number; {full name of the output file}
+
+@ @<Initialize the output...@>=output_file_name:=0;
+
+@ Let's turn now to the procedure that is used to initiate file reading
+when an `\.{input}' command is being processed.
+
+@p procedure start_input; {\MF\ will \.{input} something}
+label done;
+begin @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>;
+if cur_ext="" then cur_ext:=".mf";
+pack_cur_name;
+loop@+ begin begin_file_reading; {set up |cur_file| and new level of input}
+ if a_open_in(cur_file) then goto done;
+ if cur_area="" then
+ begin pack_file_name(cur_name,MF_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+ end;
+ end_file_reading; {remove the level that didn't work}
+ prompt_file_name("input file name",".mf");
+ end;
+done: name:=a_make_name_string(cur_file); str_ref[cur_name]:=max_str_ref;
+if job_name=0 then
+ begin job_name:=cur_name; open_log_file;
+ end; {|open_log_file| doesn't |show_context|, so |limit|
+ and |loc| needn't be set to meaningful values yet}
+if term_offset+length(name)>max_print_line-2 then print_ln
+else if (term_offset>0)or(file_offset>0) then print_char(" ");
+print_char("("); incr(open_parens); slow_print(name); update_terminal;
+if name=str_ptr-1 then {we can conserve string pool space now}
+ begin flush_string(name); name:=cur_name;
+ end;
+@<Read the first line of the new file@>;
+end;
+
+@ Here we have to remember to tell the |input_ln| routine not to
+start with a |get|. If the file is empty, it is considered to
+contain a single blank line.
+@^system dependencies@>
+
+@<Read the first line...@>=
+begin line:=1;
+if input_ln(cur_file,false) then do_nothing;
+firm_up_the_line;
+buffer[limit]:="%"; first:=limit+1; loc:=start;
+end
+
+@ @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>=
+while token_state and(loc=null) do end_token_list;
+if token_state then
+ begin print_err("File names can't appear within macros");
+@.File names can't...@>
+ help3("Sorry...I've converted what follows to tokens,")@/
+ ("possibly garbaging the name you gave.")@/
+ ("Please delete the tokens and insert the name again.");@/
+ error;
+ end;
+if file_state then scan_file_name
+else begin cur_name:=""; cur_ext:=""; cur_area:="";
+ end
+
+@* \[39] Introduction to the parsing routines.
+We come now to the central nervous system that sparks many of \MF's activities.
+By evaluating expressions, from their primary constituents to ever larger
+subexpressions, \MF\ builds the structures that ultimately define fonts of type.
+
+Four mutually recursive subroutines are involved in this process: We call them
+$$\hbox{|scan_primary|, |scan_secondary|, |scan_tertiary|,
+and |scan_expression|.}$$
+@^recursion@>
+Each of them is parameterless and begins with the first token to be scanned
+already represented in |cur_cmd|, |cur_mod|, and |cur_sym|. After execution,
+the value of the primary or secondary or tertiary or expression that was
+found will appear in the global variables |cur_type| and |cur_exp|. The
+token following the expression will be represented in |cur_cmd|, |cur_mod|,
+and |cur_sym|.
+
+Technically speaking, the parsing algorithms are ``LL(1),'' more or less;
+backup mechanisms have been added in order to provide reasonable error
+recovery.
+
+@<Glob...@>=
+@!cur_type:small_number; {the type of the expression just found}
+@!cur_exp:integer; {the value of the expression just found}
+
+@ @<Set init...@>=
+cur_exp:=0;
+
+@ Many different kinds of expressions are possible, so it is wise to have
+precise descriptions of what |cur_type| and |cur_exp| mean in all cases:
+
+\smallskip\hang
+|cur_type=vacuous| means that this expression didn't turn out to have a
+value at all, because it arose from a \&{begingroup}$\,\ldots\,$\&{endgroup}
+construction in which there was no expression before the \&{endgroup}.
+In this case |cur_exp| has some irrelevant value.
+
+\smallskip\hang
+|cur_type=boolean_type| means that |cur_exp| is either |true_code|
+or |false_code|.
+
+\smallskip\hang
+|cur_type=unknown_boolean| means that |cur_exp| points to a capsule
+node that is in
+a ring of equivalent booleans whose value has not yet been defined.
+
+\smallskip\hang
+|cur_type=string_type| means that |cur_exp| is a string number (i.e., an
+integer in the range |0<=cur_exp<str_ptr|). That string's reference count
+includes this particular reference.
+
+\smallskip\hang
+|cur_type=unknown_string| means that |cur_exp| points to a capsule
+node that is in
+a ring of equivalent strings whose value has not yet been defined.
+
+\smallskip\hang
+|cur_type=pen_type| means that |cur_exp| points to a pen header node. This
+node contains a reference count, which takes account of this particular
+reference.
+
+\smallskip\hang
+|cur_type=unknown_pen| means that |cur_exp| points to a capsule
+node that is in
+a ring of equivalent pens whose value has not yet been defined.
+
+\smallskip\hang
+|cur_type=future_pen| means that |cur_exp| points to a knot list that
+should eventually be made into a pen. Nobody else points to this particular
+knot list. The |future_pen| option occurs only as an output of |scan_primary|
+and |scan_secondary|, not as an output of |scan_tertiary| or |scan_expression|.
+
+\smallskip\hang
+|cur_type=path_type| means that |cur_exp| points to a the first node of
+a path; nobody else points to this particular path. The control points of
+the path will have been chosen.
+
+\smallskip\hang
+|cur_type=unknown_path| means that |cur_exp| points to a capsule
+node that is in
+a ring of equivalent paths whose value has not yet been defined.
+
+\smallskip\hang
+|cur_type=picture_type| means that |cur_exp| points to an edges header node.
+Nobody else points to this particular set of edges.
+
+\smallskip\hang
+|cur_type=unknown_picture| means that |cur_exp| points to a capsule
+node that is in
+a ring of equivalent pictures whose value has not yet been defined.
+
+\smallskip\hang
+|cur_type=transform_type| means that |cur_exp| points to a |transform_type|
+capsule node. The |value| part of this capsule
+points to a transform node that contains six numeric values,
+each of which is |independent|, |dependent|, |proto_dependent|, or |known|.
+
+\smallskip\hang
+|cur_type=pair_type| means that |cur_exp| points to a capsule
+node whose type is |pair_type|. The |value| part of this capsule
+points to a pair node that contains two numeric values,
+each of which is |independent|, |dependent|, |proto_dependent|, or |known|.
+
+\smallskip\hang
+|cur_type=known| means that |cur_exp| is a |scaled| value.
+
+\smallskip\hang
+|cur_type=dependent| means that |cur_exp| points to a capsule node whose type
+is |dependent|. The |dep_list| field in this capsule points to the associated
+dependency list.
+
+\smallskip\hang
+|cur_type=proto_dependent| means that |cur_exp| points to a |proto_dependent|
+capsule node . The |dep_list| field in this capsule
+points to the associated dependency list.
+
+\smallskip\hang
+|cur_type=independent| means that |cur_exp| points to a capsule node
+whose type is |independent|. This somewhat unusual case can arise, for
+example, in the expression
+`$x+\&{begingroup}\penalty0\,\&{string}\,x; 0\,\&{endgroup}$'.
+
+\smallskip\hang
+|cur_type=token_list| means that |cur_exp| points to a linked list of
+tokens.
+
+\smallskip\noindent
+The possible settings of |cur_type| have been listed here in increasing
+numerical order. Notice that |cur_type| will never be |numeric_type| or
+|suffixed_macro| or |unsuffixed_macro|, although variables of those types
+are allowed. Conversely, \MF\ has no variables of type |vacuous| or
+|token_list|.
+
+@ Capsules are two-word nodes that have a similar meaning
+to |cur_type| and |cur_exp|. Such nodes have |name_type=capsule|,
+and their |type| field is one of the possibilities for |cur_type| listed above.
+Also |link<=void| in capsules that aren't part of a token list.
+
+The |value| field of a capsule is, in most cases, the value that
+corresponds to its |type|, as |cur_exp| corresponds to |cur_type|.
+However, when |cur_exp| would point to a capsule,
+no extra layer of indirection is present; the |value|
+field is what would have been called |value(cur_exp)| if it had not been
+encapsulated. Furthermore, if the type is |dependent| or
+|proto_dependent|, the |value| field of a capsule is replaced by
+|dep_list| and |prev_dep| fields, since dependency lists in capsules are
+always part of the general |dep_list| structure.
+
+The |get_x_next| routine is careful not to change the values of |cur_type|
+and |cur_exp| when it gets an expanded token. However, |get_x_next| might
+call a macro, which might parse an expression, which might execute lots of
+commands in a group; hence it's possible that |cur_type| might change
+from, say, |unknown_boolean| to |boolean_type|, or from |dependent| to
+|known| or |independent|, during the time |get_x_next| is called. The
+programs below are careful to stash sensitive intermediate results in
+capsules, so that \MF's generality doesn't cause trouble.
+
+Here's a procedure that illustrates these conventions. It takes
+the contents of $(|cur_type|\kern-.3pt,|cur_exp|\kern-.3pt)$
+and stashes them away in a
+capsule. It is not used when |cur_type=token_list|.
+After the operation, |cur_type=vacuous|; hence there is no need to
+copy path lists or to update reference counts, etc.
+
+The special link |void| is put on the capsule returned by
+|stash_cur_exp|, because this procedure is used to store macro parameters
+that must be easily distinguishable from token lists.
+
+@<Declare the stashing/unstashing routines@>=
+function stash_cur_exp:pointer;
+var @!p:pointer; {the capsule that will be returned}
+begin case cur_type of
+unknown_types,transform_type,pair_type,dependent,proto_dependent,
+ independent:p:=cur_exp;
+othercases begin p:=get_node(value_node_size); name_type(p):=capsule;
+ type(p):=cur_type; value(p):=cur_exp;
+ end
+endcases;@/
+cur_type:=vacuous; link(p):=void; stash_cur_exp:=p;
+end;
+
+@ The inverse of |stash_cur_exp| is the following procedure, which
+deletes an unnecessary capsule and puts its contents into |cur_type|
+and |cur_exp|.
+
+The program steps of \MF\ can be divided into two categories: those in
+which |cur_type| and |cur_exp| are ``alive'' and those in which they are
+``dead,'' in the sense that |cur_type| and |cur_exp| contain relevant
+information or not. It's important not to ignore them when they're alive,
+and it's important not to pay attention to them when they're dead.
+
+There's also an intermediate category: If |cur_type=vacuous|, then
+|cur_exp| is irrelevant, hence we can proceed without caring if |cur_type|
+and |cur_exp| are alive or dead. In such cases we say that |cur_type|
+and |cur_exp| are {\sl dormant}. It is permissible to call |get_x_next|
+only when they are alive or dormant.
+
+The \\{stash} procedure above assumes that |cur_type| and |cur_exp|
+are alive or dormant. The \\{unstash} procedure assumes that they are
+dead or dormant; it resuscitates them.
+
+@<Declare the stashing/unstashing...@>=
+procedure unstash_cur_exp(@!p:pointer);
+begin cur_type:=type(p);
+case cur_type of
+unknown_types,transform_type,pair_type,dependent,proto_dependent,
+ independent: cur_exp:=p;
+othercases begin cur_exp:=value(p);
+ free_node(p,value_node_size);
+ end
+endcases;@/
+end;
+
+@ The following procedure prints the values of expressions in an
+abbreviated format. If its first parameter |p| is null, the value of
+|(cur_type,cur_exp)| is displayed; otherwise |p| should be a capsule
+containing the desired value. The second parameter controls the amount of
+output. If it is~0, dependency lists will be abbreviated to
+`\.{linearform}' unless they consist of a single term. If it is greater
+than~1, complicated structures (pens, pictures, and paths) will be displayed
+in full.
+@.linearform@>
+
+@<Declare subroutines for printing expressions@>=
+@t\4@>@<Declare the procedure called |print_dp|@>@;
+@t\4@>@<Declare the stashing/unstashing routines@>@;
+procedure print_exp(@!p:pointer;@!verbosity:small_number);
+var @!restore_cur_exp:boolean; {should |cur_exp| be restored?}
+@!t:small_number; {the type of the expression}
+@!v:integer; {the value of the expression}
+@!q:pointer; {a big node being displayed}
+begin if p<>null then restore_cur_exp:=false
+else begin p:=stash_cur_exp; restore_cur_exp:=true;
+ end;
+t:=type(p);
+if t<dependent then v:=value(p)@+else if t<independent then v:=dep_list(p);
+@<Print an abbreviated value of |v| with format depending on |t|@>;
+if restore_cur_exp then unstash_cur_exp(p);
+end;
+
+@ @<Print an abbreviated value of |v| with format depending on |t|@>=
+case t of
+vacuous:print("vacuous");
+boolean_type:if v=true_code then print("true")@+else print("false");
+unknown_types,numeric_type:@<Display a variable
+ that's been declared but not defined@>;
+string_type:begin print_char(""""); slow_print(v); print_char("""");
+ end;
+pen_type,future_pen,path_type,picture_type:@<Display a complex type@>;
+transform_type,pair_type:if v=null then print_type(t)
+ else @<Display a big node@>;
+known:print_scaled(v);
+dependent,proto_dependent:print_dp(t,v,verbosity);
+independent:print_variable_name(p);
+othercases confusion("exp")
+@:this can't happen exp}{\quad exp@>
+endcases
+
+@ @<Display a big node@>=
+begin print_char("("); q:=v+big_node_size[t];
+repeat if type(v)=known then print_scaled(value(v))
+else if type(v)=independent then print_variable_name(v)
+else print_dp(type(v),dep_list(v),verbosity);
+v:=v+2;
+if v<>q then print_char(",");
+until v=q;
+print_char(")");
+end
+
+@ Values of type \&{picture}, \&{path}, and \&{pen} are displayed verbosely
+in the log file only, unless the user has given a positive value to
+\\{tracingonline}.
+
+@<Display a complex type@>=
+if verbosity<=1 then print_type(t)
+else begin if selector=term_and_log then
+ if internal[tracing_online]<=0 then
+ begin selector:=term_only;
+ print_type(t); print(" (see the transcript file)");
+ selector:=term_and_log;
+ end;
+ case t of
+ pen_type:print_pen(v,"",false);
+ future_pen:print_path(v," (future pen)",false);
+ path_type:print_path(v,"",false);
+ picture_type:begin cur_edges:=v; print_edges("",false,0,0);
+ end;
+ end; {there are no other cases}
+ end
+
+@ @<Declare the procedure called |print_dp|@>=
+procedure print_dp(@!t:small_number;@!p:pointer;@!verbosity:small_number);
+var @!q:pointer; {the node following |p|}
+begin q:=link(p);
+if (info(q)=null) or (verbosity>0) then print_dependency(p,t)
+else print("linearform");
+@.linearform@>
+end;
+
+@ The displayed name of a variable in a ring will not be a capsule unless
+the ring consists entirely of capsules.
+
+@<Display a variable that's been declared but not defined@>=
+begin print_type(t);
+if v<>null then
+ begin print_char(" ");
+ while (name_type(v)=capsule) and (v<>p) do v:=value(v);
+ print_variable_name(v);
+ end;
+end
+
+@ When errors are detected during parsing, it is often helpful to
+display an expression just above the error message, using |exp_err|
+or |disp_err| instead of |print_err|.
+
+@d exp_err(#)==disp_err(null,#) {displays the current expression}
+
+@<Declare subroutines for printing expressions@>=
+procedure disp_err(@!p:pointer;@!s:str_number);
+begin if interaction=error_stop_mode then wake_up_terminal;
+print_nl(">> ");
+@.>>@>
+print_exp(p,1); {``medium verbose'' printing of the expression}
+if s<>"" then
+ begin print_nl("! "); print(s);
+@.!\relax@>
+ end;
+end;
+
+@ If |cur_type| and |cur_exp| contain relevant information that should
+be recycled, we will use the following procedure, which changes |cur_type|
+to |known| and stores a given value in |cur_exp|. We can think of |cur_type|
+and |cur_exp| as either alive or dormant after this has been done,
+because |cur_exp| will not contain a pointer value.
+
+@<Declare the procedure called |flush_cur_exp|@>=
+procedure flush_cur_exp(@!v:scaled);
+begin case cur_type of
+unknown_types,transform_type,pair_type,@|dependent,proto_dependent,independent:
+ begin recycle_value(cur_exp); free_node(cur_exp,value_node_size);
+ end;
+pen_type: delete_pen_ref(cur_exp);
+string_type:delete_str_ref(cur_exp);
+future_pen,path_type: toss_knot_list(cur_exp);
+picture_type:toss_edges(cur_exp);
+othercases do_nothing
+endcases;@/
+cur_type:=known; cur_exp:=v;
+end;
+
+@ There's a much more general procedure that is capable of releasing
+the storage associated with any two-word value packet.
+
+@<Declare the recycling subroutines@>=
+procedure recycle_value(@!p:pointer);
+label done;
+var @!t:small_number; {a type code}
+@!v:integer; {a value}
+@!vv:integer; {another value}
+@!q,@!r,@!s,@!pp:pointer; {link manipulation registers}
+begin t:=type(p);
+if t<dependent then v:=value(p);
+case t of
+undefined,vacuous,boolean_type,known,numeric_type:do_nothing;
+unknown_types:ring_delete(p);
+string_type:delete_str_ref(v);
+pen_type:delete_pen_ref(v);
+path_type,future_pen:toss_knot_list(v);
+picture_type:toss_edges(v);
+pair_type,transform_type:@<Recycle a big node@>;
+dependent,proto_dependent:@<Recycle a dependency list@>;
+independent:@<Recycle an independent variable@>;
+token_list,structured:confusion("recycle");
+@:this can't happen recycle}{\quad recycle@>
+unsuffixed_macro,suffixed_macro:delete_mac_ref(value(p));
+end; {there are no other cases}
+type(p):=undefined;
+end;
+
+@ @<Recycle a big node@>=
+if v<>null then
+ begin q:=v+big_node_size[t];
+ repeat q:=q-2; recycle_value(q);
+ until q=v;
+ free_node(v,big_node_size[t]);
+ end
+
+@ @<Recycle a dependency list@>=
+begin q:=dep_list(p);
+while info(q)<>null do q:=link(q);
+link(prev_dep(p)):=link(q);
+prev_dep(link(q)):=prev_dep(p);
+link(q):=null; flush_node_list(dep_list(p));
+end
+
+@ When an independent variable disappears, it simply fades away, unless
+something depends on it. In the latter case, a dependent variable whose
+coefficient of dependence is maximal will take its place.
+The relevant algorithm is due to Ignacio~A. Zabala, who implemented it
+as part of his Ph.D. thesis (Stanford University, December 1982).
+@^Zabala Salelles, Ignacio Andr\'es@>
+
+For example, suppose that variable $x$ is being recycled, and that the
+only variables depending on~$x$ are $y=2x+a$ and $z=x+b$. In this case
+we want to make $y$ independent and $z=.5y-.5a+b$; no other variables
+will depend on~$y$. If $\\{tracingequations}>0$ in this situation,
+we will print `\.{\#\#\# -2x=-y+a}'.
+
+There's a slight complication, however: An independent variable $x$
+can occur both in dependency lists and in proto-dependency lists.
+This makes it necessary to be careful when deciding which coefficient
+is maximal.
+
+Furthermore, this complication is not so slight when
+a proto-dependent variable is chosen to become independent. For example,
+suppose that $y=2x+100a$ is proto-dependent while $z=x+b$ is dependent;
+then we must change $z=.5y-50a+b$ to a proto-dependency, because of the
+large coefficient `50'.
+
+In order to deal with these complications without wasting too much time,
+we shall link together the occurrences of~$x$ among all the linear
+dependencies, maintaining separate lists for the dependent and
+proto-dependent cases.
+
+@<Recycle an independent variable@>=
+begin max_c[dependent]:=0; max_c[proto_dependent]:=0;@/
+max_link[dependent]:=null; max_link[proto_dependent]:=null;@/
+q:=link(dep_head);
+while q<>dep_head do
+ begin s:=value_loc(q); {now |link(s)=dep_list(q)|}
+ loop@+ begin r:=link(s);
+ if info(r)=null then goto done;
+ if info(r)<>p then s:=r
+ else begin t:=type(q); link(s):=link(r); info(r):=q;
+ if abs(value(r))>max_c[t] then
+ @<Record a new maximum coefficient of type |t|@>
+ else begin link(r):=max_link[t]; max_link[t]:=r;
+ end;
+ end;
+ end;
+done: q:=link(r);
+ end;
+if (max_c[dependent]>0)or(max_c[proto_dependent]>0) then
+ @<Choose a dependent variable to take the place of the disappearing
+ independent variable, and change all remaining dependencies
+ accordingly@>;
+end
+
+@ The code for independency removal makes use of three two-word arrays.
+
+@<Glob...@>=
+@!max_c:array[dependent..proto_dependent] of integer;
+ {max coefficient magnitude}
+@!max_ptr:array[dependent..proto_dependent] of pointer;
+ {where |p| occurs with |max_c|}
+@!max_link:array[dependent..proto_dependent] of pointer;
+ {other occurrences of |p|}
+
+@ @<Record a new maximum coefficient...@>=
+begin if max_c[t]>0 then
+ begin link(max_ptr[t]):=max_link[t]; max_link[t]:=max_ptr[t];
+ end;
+max_c[t]:=abs(value(r)); max_ptr[t]:=r;
+end
+
+@ @<Choose a dependent...@>=
+begin if (max_c[dependent] div @'10000 >=
+ max_c[proto_dependent]) then
+ t:=dependent
+else t:=proto_dependent;
+@<Determine the dependency list |s| to substitute for the independent
+ variable~|p|@>;
+t:=dependent+proto_dependent-t; {complement |t|}
+if max_c[t]>0 then {we need to pick up an unchosen dependency}
+ begin link(max_ptr[t]):=max_link[t]; max_link[t]:=max_ptr[t];
+ end;
+if t<>dependent then @<Substitute new dependencies in place of |p|@>
+else @<Substitute new proto-dependencies in place of |p|@>;
+flush_node_list(s);
+if fix_needed then fix_dependencies;
+check_arith;
+end
+
+@ Let |s=max_ptr[t]|. At this point we have $|value|(s)=\pm|max_c|[t]$,
+and |info(s)| points to the dependent variable~|pp| of type~|t| from
+whose dependency list we have removed node~|s|. We must reinsert
+node~|s| into the dependency list, with coefficient $-1.0$, and with
+|pp| as the new independent variable. Since |pp| will have a larger serial
+number than any other variable, we can put node |s| at the head of the
+list.
+
+@<Determine the dep...@>=
+s:=max_ptr[t]; pp:=info(s); v:=value(s);
+if t=dependent then value(s):=-fraction_one@+else value(s):=-unity;
+r:=dep_list(pp); link(s):=r;
+while info(r)<>null do r:=link(r);
+q:=link(r); link(r):=null;
+prev_dep(q):=prev_dep(pp); link(prev_dep(pp)):=q;
+new_indep(pp);
+if cur_exp=pp then if cur_type=t then cur_type:=independent;
+if internal[tracing_equations]>0 then @<Show the transformed dependency@>
+
+@ Now $(-v)$ times the formerly independent variable~|p| is being replaced
+by the dependency list~|s|.
+
+@<Show the transformed...@>=
+if interesting(p) then
+ begin begin_diagnostic; print_nl("### ");
+@:]]]\#\#\#_}{\.{\#\#\#}@>
+ if v>0 then print_char("-");
+ if t=dependent then vv:=round_fraction(max_c[dependent])
+ else vv:=max_c[proto_dependent];
+ if vv<>unity then print_scaled(vv);
+ print_variable_name(p);
+ while value(p) mod s_scale>0 do
+ begin print("*4"); value(p):=value(p)-2;
+ end;
+ if t=dependent then print_char("=")@+else print(" = ");
+ print_dependency(s,t);
+ end_diagnostic(false);
+ end
+
+@ Finally, there are dependent and proto-dependent variables whose
+dependency lists must be brought up to date.
+
+@<Substitute new dependencies...@>=
+for t:=dependent to proto_dependent do
+ begin r:=max_link[t];
+ while r<>null do
+ begin q:=info(r);
+ dep_list(q):=p_plus_fq(dep_list(q),@|
+ make_fraction(value(r),-v),s,t,dependent);
+ if dep_list(q)=dep_final then make_known(q,dep_final);
+ q:=r; r:=link(r); free_node(q,dep_node_size);
+ end;
+ end
+
+@ @<Substitute new proto...@>=
+for t:=dependent to proto_dependent do
+ begin r:=max_link[t];
+ while r<>null do
+ begin q:=info(r);
+ if t=dependent then {for safety's sake, we change |q| to |proto_dependent|}
+ begin if cur_exp=q then if cur_type=dependent then
+ cur_type:=proto_dependent;
+ dep_list(q):=p_over_v(dep_list(q),unity,dependent,proto_dependent);
+ type(q):=proto_dependent; value(r):=round_fraction(value(r));
+ end;
+ dep_list(q):=p_plus_fq(dep_list(q),@|
+ make_scaled(value(r),-v),s,proto_dependent,proto_dependent);
+ if dep_list(q)=dep_final then make_known(q,dep_final);
+ q:=r; r:=link(r); free_node(q,dep_node_size);
+ end;
+ end
+
+@ Here are some routines that provide handy combinations of actions
+that are often needed during error recovery. For example,
+`|flush_error|' flushes the current expression, replaces it by
+a given value, and calls |error|.
+
+Errors often are detected after an extra token has already been scanned.
+The `\\{put\_get}' routines put that token back before calling |error|;
+then they get it back again. (Or perhaps they get another token, if
+the user has changed things.)
+
+@<Declare the procedure called |flush_cur_exp|@>=
+procedure flush_error(@!v:scaled);@+begin error; flush_cur_exp(v);@+end;
+@#
+procedure@?back_error; forward;@t\2@>@/
+procedure@?get_x_next; forward;@t\2@>@/
+@#
+procedure put_get_error;@+begin back_error; get_x_next;@+end;
+@#
+procedure put_get_flush_error(@!v:scaled);@+begin put_get_error;
+ flush_cur_exp(v);@+end;
+
+@ A global variable called |var_flag| is set to a special command code
+just before \MF\ calls |scan_expression|, if the expression should be
+treated as a variable when this command code immediately follows. For
+example, |var_flag| is set to |assignment| at the beginning of a
+statement, because we want to know the {\sl location\/} of a variable at
+the left of `\.{:=}', not the {\sl value\/} of that variable.
+
+The |scan_expression| subroutine calls |scan_tertiary|,
+which calls |scan_secondary|, which calls |scan_primary|, which sets
+|var_flag:=0|. In this way each of the scanning routines ``knows''
+when it has been called with a special |var_flag|, but |var_flag| is
+usually zero.
+
+A variable preceding a command that equals |var_flag| is converted to a
+token list rather than a value. Furthermore, an `\.{=}' sign following an
+expression with |var_flag=assignment| is not considered to be a relation
+that produces boolean expressions.
+
+
+@<Glob...@>=
+@!var_flag:0..max_command_code; {command that wants a variable}
+
+@ @<Set init...@>=
+var_flag:=0;
+
+@* \[40] Parsing primary expressions.
+The first parsing routine, |scan_primary|, is also the most complicated one,
+since it involves so many different cases. But each case---with one
+exception---is fairly simple by itself.
+
+When |scan_primary| begins, the first token of the primary to be scanned
+should already appear in |cur_cmd|, |cur_mod|, and |cur_sym|. The values
+of |cur_type| and |cur_exp| should be either dead or dormant, as explained
+earlier. If |cur_cmd| is not between |min_primary_command| and
+|max_primary_command|, inclusive, a syntax error will be signalled.
+
+@<Declare the basic parsing subroutines@>=
+procedure scan_primary;
+label restart, done, done1, done2;
+var @!p,@!q,@!r:pointer; {for list manipulation}
+@!c:quarterword; {a primitive operation code}
+@!my_var_flag:0..max_command_code; {initial value of |var_flag|}
+@!l_delim,@!r_delim:pointer; {hash addresses of a delimiter pair}
+@<Other local variables for |scan_primary|@>@;
+begin my_var_flag:=var_flag; var_flag:=0;
+restart:check_arith;
+@<Supply diagnostic information, if requested@>;
+case cur_cmd of
+left_delimiter:@<Scan a delimited primary@>;
+begin_group:@<Scan a grouped primary@>;
+string_token:@<Scan a string constant@>;
+numeric_token:@<Scan a primary that starts with a numeric token@>;
+nullary:@<Scan a nullary operation@>;
+unary,type_name,cycle,plus_or_minus:@<Scan a unary operation@>;
+primary_binary:@<Scan a binary operation with `\&{of}' between its operands@>;
+str_op:@<Convert a suffix to a string@>;
+internal_quantity:@<Scan an internal numeric quantity@>;
+capsule_token:make_exp_copy(cur_mod);
+tag_token:@<Scan a variable primary;
+ |goto restart| if it turns out to be a macro@>;
+othercases begin bad_exp("A primary"); goto restart;
+@.A primary expression...@>
+ end
+endcases;@/
+get_x_next; {the routines |goto done| if they don't want this}
+done: if cur_cmd=left_bracket then
+ if cur_type>=known then @<Scan a mediation construction@>;
+end;
+
+@ Errors at the beginning of expressions are flagged by |bad_exp|.
+
+@p procedure bad_exp(@!s:str_number);
+var save_flag:0..max_command_code;
+begin print_err(s); print(" expression can't begin with `");
+print_cmd_mod(cur_cmd,cur_mod); print_char("'");
+help4("I'm afraid I need some sort of value in order to continue,")@/
+ ("so I've tentatively inserted `0'. You may want to")@/
+ ("delete this zero and insert something else;")@/
+ ("see Chapter 27 of The METAFONTbook for an example.");
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+back_input; cur_sym:=0; cur_cmd:=numeric_token; cur_mod:=0; ins_error;@/
+save_flag:=var_flag; var_flag:=0; get_x_next;
+var_flag:=save_flag;
+end;
+
+@ @<Supply diagnostic information, if requested@>=
+debug if panicking then check_mem(false);@+gubed@;@/
+if interrupt<>0 then if OK_to_interrupt then
+ begin back_input; check_interrupt; get_x_next;
+ end
+
+@ @<Scan a delimited primary@>=
+begin l_delim:=cur_sym; r_delim:=cur_mod; get_x_next; scan_expression;
+if (cur_cmd=comma) and (cur_type>=known) then
+ @<Scan the second of a pair of numerics@>
+else check_delimiter(l_delim,r_delim);
+end
+
+@ The |stash_in| subroutine puts the current (numeric) expression into a field
+within a ``big node.''
+
+@p procedure stash_in(@!p:pointer);
+var @!q:pointer; {temporary register}
+begin type(p):=cur_type;
+if cur_type=known then value(p):=cur_exp
+else begin if cur_type=independent then
+ @<Stash an independent |cur_exp| into a big node@>
+ else begin mem[value_loc(p)]:=mem[value_loc(cur_exp)];
+ {|dep_list(p):=dep_list(cur_exp)| and |prev_dep(p):=prev_dep(cur_exp)|}
+ link(prev_dep(p)):=p;
+ end;
+ free_node(cur_exp,value_node_size);
+ end;
+cur_type:=vacuous;
+end;
+
+@ In rare cases the current expression can become |independent|. There
+may be many dependency lists pointing to such an independent capsule,
+so we can't simply move it into place within a big node. Instead,
+we copy it, then recycle it.
+
+@ @<Stash an independent |cur_exp|...@>=
+begin q:=single_dependency(cur_exp);
+if q=dep_final then
+ begin type(p):=known; value(p):=0; free_node(q,dep_node_size);
+ end
+else begin type(p):=dependent; new_dep(p,q);
+ end;
+recycle_value(cur_exp);
+end
+
+@ @<Scan the second of a pair of numerics@>=
+begin p:=get_node(value_node_size); type(p):=pair_type; name_type(p):=capsule;
+init_big_node(p); q:=value(p); stash_in(x_part_loc(q));@/
+get_x_next; scan_expression;
+if cur_type<known then
+ begin exp_err("Nonnumeric ypart has been replaced by 0");
+@.Nonnumeric...replaced by 0@>
+ help4("I thought you were giving me a pair `(x,y)'; but")@/
+ ("after finding a nice xpart `x' I found a ypart `y'")@/
+ ("that isn't of numeric type. So I've changed y to zero.")@/
+ ("(The y that I didn't like appears above the error message.)");
+ put_get_flush_error(0);
+ end;
+stash_in(y_part_loc(q));
+check_delimiter(l_delim,r_delim);
+cur_type:=pair_type; cur_exp:=p;
+end
+
+@ The local variable |group_line| keeps track of the line
+where a \&{begingroup} command occurred; this will be useful
+in an error message if the group doesn't actually end.
+
+@<Other local variables for |scan_primary|@>=
+@!group_line:integer; {where a group began}
+
+@ @<Scan a grouped primary@>=
+begin group_line:=line;
+if internal[tracing_commands]>0 then show_cur_cmd_mod;
+save_boundary_item(p);
+repeat do_statement; {ends with |cur_cmd>=semicolon|}
+until cur_cmd<>semicolon;
+if cur_cmd<>end_group then
+ begin print_err("A group begun on line ");
+@.A group...never ended@>
+ print_int(group_line);
+ print(" never ended");
+ help2("I saw a `begingroup' back there that hasn't been matched")@/
+ ("by `endgroup'. So I've inserted `endgroup' now.");
+ back_error; cur_cmd:=end_group;
+ end;
+unsave; {this might change |cur_type|, if independent variables are recycled}
+if internal[tracing_commands]>0 then show_cur_cmd_mod;
+end
+
+@ @<Scan a string constant@>=
+begin cur_type:=string_type; cur_exp:=cur_mod;
+end
+
+@ Later we'll come to procedures that perform actual operations like
+addition, square root, and so on; our purpose now is to do the parsing.
+But we might as well mention those future procedures now, so that the
+suspense won't be too bad:
+
+\smallskip
+|do_nullary(c)| does primitive operations that have no operands (e.g.,
+`\&{true}' or `\&{pencircle}');
+
+\smallskip
+|do_unary(c)| applies a primitive operation to the current expression;
+
+\smallskip
+|do_binary(p,c)| applies a primitive operation to the capsule~|p|
+and the current expression.
+
+@<Scan a nullary operation@>=do_nullary(cur_mod)
+
+@ @<Scan a unary operation@>=
+begin c:=cur_mod; get_x_next; scan_primary; do_unary(c); goto done;
+end
+
+@ A numeric token might be a primary by itself, or it might be the
+numerator of a fraction composed solely of numeric tokens, or it might
+multiply the primary that follows (provided that the primary doesn't begin
+with a plus sign or a minus sign). The code here uses the facts that
+|max_primary_command=plus_or_minus| and
+|max_primary_command-1=numeric_token|. If a fraction is found that is less
+than unity, we try to retain higher precision when we use it in scalar
+multiplication.
+
+@<Other local variables for |scan_primary|@>=
+@!num,@!denom:scaled; {for primaries that are fractions, like `1/2'}
+
+@ @<Scan a primary that starts with a numeric token@>=
+begin cur_exp:=cur_mod; cur_type:=known; get_x_next;
+if cur_cmd<>slash then
+ begin num:=0; denom:=0;
+ end
+else begin get_x_next;
+ if cur_cmd<>numeric_token then
+ begin back_input;
+ cur_cmd:=slash; cur_mod:=over; cur_sym:=frozen_slash;
+ goto done;
+ end;
+ num:=cur_exp; denom:=cur_mod;
+ if denom=0 then @<Protest division by zero@>
+ else cur_exp:=make_scaled(num,denom);
+ check_arith; get_x_next;
+ end;
+if cur_cmd>=min_primary_command then
+ if cur_cmd<numeric_token then {in particular, |cur_cmd<>plus_or_minus|}
+ begin p:=stash_cur_exp; scan_primary;
+ if (abs(num)>=abs(denom))or(cur_type<pair_type) then do_binary(p,times)
+ else begin frac_mult(num,denom);
+ free_node(p,value_node_size);
+ end;
+ end;
+goto done;
+end
+
+@ @<Protest division...@>=
+begin print_err("Division by zero");
+@.Division by zero@>
+help1("I'll pretend that you meant to divide by 1."); error;
+end
+
+@ @<Scan a binary operation with `\&{of}' between its operands@>=
+begin c:=cur_mod; get_x_next; scan_expression;
+if cur_cmd<>of_token then
+ begin missing_err("of"); print(" for "); print_cmd_mod(primary_binary,c);
+@.Missing `of'@>
+ help1("I've got the first argument; will look now for the other.");
+ back_error;
+ end;
+p:=stash_cur_exp; get_x_next; scan_primary; do_binary(p,c); goto done;
+end
+
+@ @<Convert a suffix to a string@>=
+begin get_x_next; scan_suffix; old_setting:=selector; selector:=new_string;
+show_token_list(cur_exp,null,100000,0); flush_token_list(cur_exp);
+cur_exp:=make_string; selector:=old_setting; cur_type:=string_type;
+goto done;
+end
+
+@ If an internal quantity appears all by itself on the left of an
+assignment, we return a token list of length one, containing the address
+of the internal quantity plus |hash_end|. (This accords with the conventions
+of the save stack, as described earlier.)
+
+@<Scan an internal...@>=
+begin q:=cur_mod;
+if my_var_flag=assignment then
+ begin get_x_next;
+ if cur_cmd=assignment then
+ begin cur_exp:=get_avail;
+ info(cur_exp):=q+hash_end; cur_type:=token_list; goto done;
+ end;
+ back_input;
+ end;
+cur_type:=known; cur_exp:=internal[q];
+end
+
+@ The most difficult part of |scan_primary| has been saved for last, since
+it was necessary to build up some confidence first. We can now face the task
+of scanning a variable.
+
+As we scan a variable, we build a token list containing the relevant
+names and subscript values, simultaneously following along in the
+``collective'' structure to see if we are actually dealing with a macro
+instead of a value.
+
+The local variables |pre_head| and |post_head| will point to the beginning
+of the prefix and suffix lists; |tail| will point to the end of the list
+that is currently growing.
+
+Another local variable, |tt|, contains partial information about the
+declared type of the variable-so-far. If |tt>=unsuffixed_macro|, the
+relation |tt=type(q)| will always hold. If |tt=undefined|, the routine
+doesn't bother to update its information about type. And if
+|undefined<tt<unsuffixed_macro|, the precise value of |tt| isn't critical.
+
+@ @<Other local variables for |scan_primary|@>=
+@!pre_head,@!post_head,@!tail:pointer;
+ {prefix and suffix list variables}
+@!tt:small_number; {approximation to the type of the variable-so-far}
+@!t:pointer; {a token}
+@!macro_ref:pointer; {reference count for a suffixed macro}
+
+@ @<Scan a variable primary...@>=
+begin fast_get_avail(pre_head); tail:=pre_head; post_head:=null; tt:=vacuous;
+loop@+ begin t:=cur_tok; link(tail):=t;
+ if tt<>undefined then
+ begin @<Find the approximate type |tt| and corresponding~|q|@>;
+ if tt>=unsuffixed_macro then
+ @<Either begin an unsuffixed macro call or
+ prepare for a suffixed one@>;
+ end;
+ get_x_next; tail:=t;
+ if cur_cmd=left_bracket then
+ @<Scan for a subscript; replace |cur_cmd| by |numeric_token| if found@>;
+ if cur_cmd>max_suffix_token then goto done1;
+ if cur_cmd<min_suffix_token then goto done1;
+ end; {now |cur_cmd| is |internal_quantity|, |tag_token|, or |numeric_token|}
+done1:@<Handle unusual cases that masquerade as variables, and |goto restart|
+ or |goto done| if appropriate;
+ otherwise make a copy of the variable and |goto done|@>;
+end
+
+@ @<Either begin an unsuffixed macro call or...@>=
+begin link(tail):=null;
+if tt>unsuffixed_macro then {|tt=suffixed_macro|}
+ begin post_head:=get_avail; tail:=post_head; link(tail):=t;@/
+ tt:=undefined; macro_ref:=value(q); add_mac_ref(macro_ref);
+ end
+else @<Set up unsuffixed macro call and |goto restart|@>;
+end
+
+@ @<Scan for a subscript; replace |cur_cmd| by |numeric_token| if found@>=
+begin get_x_next; scan_expression;
+if cur_cmd<>right_bracket then
+ @<Put the left bracket and the expression back to be rescanned@>
+else begin if cur_type<>known then bad_subscript;
+ cur_cmd:=numeric_token; cur_mod:=cur_exp; cur_sym:=0;
+ end;
+end
+
+@ The left bracket that we thought was introducing a subscript might have
+actually been the left bracket in a mediation construction like `\.{x[a,b]}'.
+So we don't issue an error message at this point; but we do want to back up
+so as to avoid any embarrassment about our incorrect assumption.
+
+@<Put the left bracket and the expression back to be rescanned@>=
+begin back_input; {that was the token following the current expression}
+back_expr; cur_cmd:=left_bracket; cur_mod:=0; cur_sym:=frozen_left_bracket;
+end
+
+@ Here's a routine that puts the current expression back to be read again.
+
+@p procedure back_expr;
+var @!p:pointer; {capsule token}
+begin p:=stash_cur_exp; link(p):=null; back_list(p);
+end;
+
+@ Unknown subscripts lead to the following error message.
+
+@p procedure bad_subscript;
+begin exp_err("Improper subscript has been replaced by zero");
+@.Improper subscript...@>
+help3("A bracketed subscript must have a known numeric value;")@/
+ ("unfortunately, what I found was the value that appears just")@/
+ ("above this error message. So I'll try a zero subscript.");
+flush_error(0);
+end;
+
+@ Every time we call |get_x_next|, there's a chance that the variable we've
+been looking at will disappear. Thus, we cannot safely keep |q| pointing
+into the variable structure; we need to start searching from the root each time.
+
+@<Find the approximate type |tt| and corresponding~|q|@>=
+@^inner loop@>
+begin p:=link(pre_head); q:=info(p); tt:=undefined;
+if eq_type(q) mod outer_tag=tag_token then
+ begin q:=equiv(q);
+ if q=null then goto done2;
+ loop@+ begin p:=link(p);
+ if p=null then
+ begin tt:=type(q); goto done2;
+ end;
+ if type(q)<>structured then goto done2;
+ q:=link(attr_head(q)); {the |collective_subscript| attribute}
+ if p>=hi_mem_min then {it's not a subscript}
+ begin repeat q:=link(q);
+ until attr_loc(q)>=info(p);
+ if attr_loc(q)>info(p) then goto done2;
+ end;
+ end;
+ end;
+done2:end
+
+@ How do things stand now? Well, we have scanned an entire variable name,
+including possible subscripts and/or attributes; |cur_cmd|, |cur_mod|, and
+|cur_sym| represent the token that follows. If |post_head=null|, a
+token list for this variable name starts at |link(pre_head)|, with all
+subscripts evaluated. But if |post_head<>null|, the variable turned out
+to be a suffixed macro; |pre_head| is the head of the prefix list, while
+|post_head| is the head of a token list containing both `\.{\AT!}' and
+the suffix.
+
+Our immediate problem is to see if this variable still exists. (Variable
+structures can change drastically whenever we call |get_x_next|; users
+aren't supposed to do this, but the fact that it is possible means that
+we must be cautious.)
+
+The following procedure prints an error message when a variable
+unexpectedly disappears. Its help message isn't quite right for
+our present purposes, but we'll be able to fix that up.
+
+@p procedure obliterated(@!q:pointer);
+begin print_err("Variable "); show_token_list(q,null,1000,0);
+print(" has been obliterated");
+@.Variable...obliterated@>
+help5("It seems you did a nasty thing---probably by accident,")@/
+ ("but nevertheless you nearly hornswoggled me...")@/
+ ("While I was evaluating the right-hand side of this")@/
+ ("command, something happened, and the left-hand side")@/
+ ("is no longer a variable! So I won't change anything.");
+end;
+
+@ If the variable does exist, we also need to check
+for a few other special cases before deciding that a plain old ordinary
+variable has, indeed, been scanned.
+
+@<Handle unusual cases that masquerade as variables...@>=
+if post_head<>null then @<Set up suffixed macro call and |goto restart|@>;
+q:=link(pre_head); free_avail(pre_head);
+if cur_cmd=my_var_flag then
+ begin cur_type:=token_list; cur_exp:=q; goto done;
+ end;
+p:=find_variable(q);
+if p<>null then make_exp_copy(p)
+else begin obliterated(q);@/
+ help_line[2]:="While I was evaluating the suffix of this variable,";
+ help_line[1]:="something was redefined, and it's no longer a variable!";
+ help_line[0]:="In order to get back on my feet, I've inserted `0' instead.";
+ put_get_flush_error(0);
+ end;
+flush_node_list(q); goto done
+
+@ The only complication associated with macro calling is that the prefix
+and ``at'' parameters must be packaged in an appropriate list of lists.
+
+@<Set up unsuffixed macro call and |goto restart|@>=
+begin p:=get_avail; info(pre_head):=link(pre_head); link(pre_head):=p;
+info(p):=t; macro_call(value(q),pre_head,null); get_x_next; goto restart;
+end
+
+@ If the ``variable'' that turned out to be a suffixed macro no longer exists,
+we don't care, because we have reserved a pointer (|macro_ref|) to its
+token list.
+
+@<Set up suffixed macro call and |goto restart|@>=
+begin back_input; p:=get_avail; q:=link(post_head);
+info(pre_head):=link(pre_head); link(pre_head):=post_head;
+info(post_head):=q; link(post_head):=p; info(p):=link(q); link(q):=null;
+macro_call(macro_ref,pre_head,null); decr(ref_count(macro_ref));
+get_x_next; goto restart;
+end
+
+@ Our remaining job is simply to make a copy of the value that has been
+found. Some cases are harder than others, but complexity arises solely
+because of the multiplicity of possible cases.
+
+@<Declare the procedure called |make_exp_copy|@>=
+@t\4@>@<Declare subroutines needed by |make_exp_copy|@>@;
+procedure make_exp_copy(@!p:pointer);
+label restart;
+var @!q,@!r,@!t:pointer; {registers for list manipulation}
+begin restart: cur_type:=type(p);
+case cur_type of
+vacuous,boolean_type,known:cur_exp:=value(p);
+unknown_types:cur_exp:=new_ring_entry(p);
+string_type:begin cur_exp:=value(p); add_str_ref(cur_exp);
+ end;
+pen_type:begin cur_exp:=value(p); add_pen_ref(cur_exp);
+ end;
+picture_type:cur_exp:=copy_edges(value(p));
+path_type,future_pen:cur_exp:=copy_path(value(p));
+transform_type,pair_type:@<Copy the big node |p|@>;
+dependent,proto_dependent:encapsulate(copy_dep_list(dep_list(p)));
+numeric_type:begin new_indep(p); goto restart;
+ end;
+independent: begin q:=single_dependency(p);
+ if q=dep_final then
+ begin cur_type:=known; cur_exp:=0; free_node(q,dep_node_size);
+ end
+ else begin cur_type:=dependent; encapsulate(q);
+ end;
+ end;
+othercases confusion("copy")
+@:this can't happen copy}{\quad copy@>
+endcases;
+end;
+
+@ The |encapsulate| subroutine assumes that |dep_final| is the
+tail of dependency list~|p|.
+
+@<Declare subroutines needed by |make_exp_copy|@>=
+procedure encapsulate(@!p:pointer);
+begin cur_exp:=get_node(value_node_size); type(cur_exp):=cur_type;
+name_type(cur_exp):=capsule; new_dep(cur_exp,p);
+end;
+
+@ The most tedious case arises when the user refers to a
+\&{pair} or \&{transform} variable; we must copy several fields,
+each of which can be |independent|, |dependent|, |proto_dependent|,
+or |known|.
+
+@<Copy the big node |p|@>=
+begin if value(p)=null then init_big_node(p);
+t:=get_node(value_node_size); name_type(t):=capsule; type(t):=cur_type;
+init_big_node(t);@/
+q:=value(p)+big_node_size[cur_type]; r:=value(t)+big_node_size[cur_type];
+repeat q:=q-2; r:=r-2; install(r,q);
+until q=value(p);
+cur_exp:=t;
+end
+
+@ The |install| procedure copies a numeric field~|q| into field~|r| of
+a big node that will be part of a capsule.
+
+@<Declare subroutines needed by |make_exp_copy|@>=
+procedure install(@!r,@!q:pointer);
+var p:pointer; {temporary register}
+begin if type(q)=known then
+ begin value(r):=value(q); type(r):=known;
+ end
+else if type(q)=independent then
+ begin p:=single_dependency(q);
+ if p=dep_final then
+ begin type(r):=known; value(r):=0; free_node(p,dep_node_size);
+ end
+ else begin type(r):=dependent; new_dep(r,p);
+ end;
+ end
+ else begin type(r):=type(q); new_dep(r,copy_dep_list(dep_list(q)));
+ end;
+end;
+
+@ Expressions of the form `\.{a[b,c]}' are converted into
+`\.{b+a*(c-b)}', without checking the types of \.b~or~\.c,
+provided that \.a is numeric.
+
+@<Scan a mediation...@>=
+begin p:=stash_cur_exp; get_x_next; scan_expression;
+if cur_cmd<>comma then
+ begin @<Put the left bracket and the expression back...@>;
+ unstash_cur_exp(p);
+ end
+else begin q:=stash_cur_exp; get_x_next; scan_expression;
+ if cur_cmd<>right_bracket then
+ begin missing_err("]");@/
+@.Missing `]'@>
+ help3("I've scanned an expression of the form `a[b,c',")@/
+ ("so a right bracket should have come next.")@/
+ ("I shall pretend that one was there.");@/
+ back_error;
+ end;
+ r:=stash_cur_exp; make_exp_copy(q);@/
+ do_binary(r,minus); do_binary(p,times); do_binary(q,plus); get_x_next;
+ end;
+end
+
+@ Here is a comparatively simple routine that is used to scan the
+\&{suffix} parameters of a macro.
+
+@<Declare the basic parsing subroutines@>=
+procedure scan_suffix;
+label done;
+var @!h,@!t:pointer; {head and tail of the list being built}
+@!p:pointer; {temporary register}
+begin h:=get_avail; t:=h;
+loop@+ begin if cur_cmd=left_bracket then
+ @<Scan a bracketed subscript and set |cur_cmd:=numeric_token|@>;
+ if cur_cmd=numeric_token then p:=new_num_tok(cur_mod)
+ else if (cur_cmd=tag_token)or(cur_cmd=internal_quantity) then
+ begin p:=get_avail; info(p):=cur_sym;
+ end
+ else goto done;
+ link(t):=p; t:=p; get_x_next;
+ end;
+done: cur_exp:=link(h); free_avail(h); cur_type:=token_list;
+end;
+
+@ @<Scan a bracketed subscript and set |cur_cmd:=numeric_token|@>=
+begin get_x_next; scan_expression;
+if cur_type<>known then bad_subscript;
+if cur_cmd<>right_bracket then
+ begin missing_err("]");@/
+@.Missing `]'@>
+ help3("I've seen a `[' and a subscript value, in a suffix,")@/
+ ("so a right bracket should have come next.")@/
+ ("I shall pretend that one was there.");@/
+ back_error;
+ end;
+cur_cmd:=numeric_token; cur_mod:=cur_exp;
+end
+
+@* \[41] Parsing secondary and higher expressions.
+After the intricacies of |scan_primary|\kern-1pt,
+the |scan_secondary| routine is
+refreshingly simple. It's not trivial, but the operations are relatively
+straightforward; the main difficulty is, again, that expressions and data
+structures might change drastically every time we call |get_x_next|, so a
+cautious approach is mandatory. For example, a macro defined by
+\&{primarydef} might have disappeared by the time its second argument has
+been scanned; we solve this by increasing the reference count of its token
+list, so that the macro can be called even after it has been clobbered.
+
+@<Declare the basic parsing subroutines@>=
+procedure scan_secondary;
+label restart,continue;
+var @!p:pointer; {for list manipulation}
+@!c,@!d:halfword; {operation codes or modifiers}
+@!mac_name:pointer; {token defined with \&{primarydef}}
+begin restart:if(cur_cmd<min_primary_command)or@|
+ (cur_cmd>max_primary_command) then
+ bad_exp("A secondary");
+@.A secondary expression...@>
+scan_primary;
+continue: if cur_cmd<=max_secondary_command then
+ if cur_cmd>=min_secondary_command then
+ begin p:=stash_cur_exp; c:=cur_mod; d:=cur_cmd;
+ if d=secondary_primary_macro then
+ begin mac_name:=cur_sym; add_mac_ref(c);
+ end;
+ get_x_next; scan_primary;
+ if d<>secondary_primary_macro then do_binary(p,c)
+ else begin back_input; binary_mac(p,c,mac_name);
+ decr(ref_count(c)); get_x_next; goto restart;
+ end;
+ goto continue;
+ end;
+end;
+
+@ The following procedure calls a macro that has two parameters,
+|p| and |cur_exp|.
+
+@p procedure binary_mac(@!p,@!c,@!n:pointer);
+var @!q,@!r:pointer; {nodes in the parameter list}
+begin q:=get_avail; r:=get_avail; link(q):=r;@/
+info(q):=p; info(r):=stash_cur_exp;@/
+macro_call(c,q,n);
+end;
+
+@ The next procedure, |scan_tertiary|, is pretty much the same deal.
+
+@<Declare the basic parsing subroutines@>=
+procedure scan_tertiary;
+label restart,continue;
+var @!p:pointer; {for list manipulation}
+@!c,@!d:halfword; {operation codes or modifiers}
+@!mac_name:pointer; {token defined with \&{secondarydef}}
+begin restart:if(cur_cmd<min_primary_command)or@|
+ (cur_cmd>max_primary_command) then
+ bad_exp("A tertiary");
+@.A tertiary expression...@>
+scan_secondary;
+if cur_type=future_pen then materialize_pen;
+continue: if cur_cmd<=max_tertiary_command then
+ if cur_cmd>=min_tertiary_command then
+ begin p:=stash_cur_exp; c:=cur_mod; d:=cur_cmd;
+ if d=tertiary_secondary_macro then
+ begin mac_name:=cur_sym; add_mac_ref(c);
+ end;
+ get_x_next; scan_secondary;
+ if d<>tertiary_secondary_macro then do_binary(p,c)
+ else begin back_input; binary_mac(p,c,mac_name);
+ decr(ref_count(c)); get_x_next; goto restart;
+ end;
+ goto continue;
+ end;
+end;
+
+@ A |future_pen| becomes a full-fledged pen here.
+
+@p procedure materialize_pen;
+label common_ending;
+var @!a_minus_b,@!a_plus_b,@!major_axis,@!minor_axis:scaled; {ellipse variables}
+@!theta:angle; {amount by which the ellipse has been rotated}
+@!p:pointer; {path traverser}
+@!q:pointer; {the knot list to be made into a pen}
+begin q:=cur_exp;
+if left_type(q)=endpoint then
+ begin print_err("Pen path must be a cycle");
+@.Pen path must be a cycle@>
+ help2("I can't make a pen from the given path.")@/
+ ("So I've replaced it by the trivial path `(0,0)..cycle'.");
+ put_get_error; cur_exp:=null_pen; goto common_ending;
+ end
+else if left_type(q)=open then
+ @<Change node |q| to a path for an elliptical pen@>;
+cur_exp:=make_pen(q);
+common_ending: toss_knot_list(q); cur_type:=pen_type;
+end;
+
+@ We placed the three points $(0,0)$, $(1,0)$, $(0,1)$ into a \&{pencircle},
+and they have now been transformed to $(u,v)$, $(A+u,B+v)$, $(C+u,D+v)$;
+this gives us enough information to deduce the transformation
+$(x,y)\mapsto(Ax+Cy+u,Bx+Dy+v)$.
+
+Given ($A,B,C,D)$ we can always find $(a,b,\theta,\phi)$ such that
+$$\eqalign{A&=a\cos\phi\cos\theta-b\sin\phi\sin\theta;\cr
+B&=a\cos\phi\sin\theta+b\sin\phi\cos\theta;\cr
+C&=-a\sin\phi\cos\theta-b\cos\phi\sin\theta;\cr
+D&=-a\sin\phi\sin\theta+b\cos\phi\cos\theta.\cr}$$
+In this notation, the unit circle $(\cos t,\sin t)$ is transformed into
+$$\bigl(a\cos(\phi+t)\cos\theta-b\sin(\phi+t)\sin\theta,\;
+a\cos(\phi+t)\sin\theta+b\sin(\phi+t)\cos\theta\bigr)\;+\;(u,v),$$
+which is an ellipse with semi-axes~$(a,b)$, rotated by~$\theta$ and
+shifted by~$(u,v)$. To solve the stated equations, we note that it is
+necessary and sufficient to solve
+$$\eqalign{A-D&=(a-b)\cos(\theta-\phi),\cr
+B+C&=(a-b)\sin(\theta-\phi),\cr}
+\qquad
+\eqalign{A+D&=(a+b)\cos(\theta+\phi),\cr
+B-C&=(a+b)\sin(\theta+\phi);\cr}$$
+and it is easy to find $a-b$, $a+b$, $\theta-\phi$, and $\theta+\phi$
+from these formulas.
+
+The code below uses |(txx,tyx,txy,tyy,tx,ty)| to stand for
+$(A,B,C,D,u,v)$.
+
+@<Change node |q|...@>=
+begin tx:=x_coord(q); ty:=y_coord(q);
+txx:=left_x(q)-tx; tyx:=left_y(q)-ty;
+txy:=right_x(q)-tx; tyy:=right_y(q)-ty;
+a_minus_b:=pyth_add(txx-tyy,tyx+txy); a_plus_b:=pyth_add(txx+tyy,tyx-txy);
+major_axis:=half(a_minus_b+a_plus_b); minor_axis:=half(abs(a_plus_b-a_minus_b));
+if major_axis=minor_axis then theta:=0 {circle}
+else theta:=half(n_arg(txx-tyy,tyx+txy)+n_arg(txx+tyy,tyx-txy));
+free_node(q,knot_node_size);
+q:=make_ellipse(major_axis,minor_axis,theta);
+if (tx<>0)or(ty<>0) then @<Shift the coordinates of path |q|@>;
+end
+
+@ @<Shift the coordinates of path |q|@>=
+begin p:=q;
+repeat x_coord(p):=x_coord(p)+tx; y_coord(p):=y_coord(p)+ty; p:=link(p);
+until p=q;
+end
+
+@ Finally we reach the deepest level in our quartet of parsing routines.
+This one is much like the others; but it has an extra complication from
+paths, which materialize here.
+
+@d continue_path=25 {a label inside of |scan_expression|}
+@d finish_path=26 {another}
+
+@<Declare the basic parsing subroutines@>=
+procedure scan_expression;
+label restart,done,continue,continue_path,finish_path,exit;
+var @!p,@!q,@!r,@!pp,@!qq:pointer; {for list manipulation}
+@!c,@!d:halfword; {operation codes or modifiers}
+@!my_var_flag:0..max_command_code; {initial value of |var_flag|}
+@!mac_name:pointer; {token defined with \&{tertiarydef}}
+@!cycle_hit:boolean; {did a path expression just end with `\&{cycle}'?}
+@!x,@!y:scaled; {explicit coordinates or tension at a path join}
+@!t:endpoint..open; {knot type following a path join}
+begin my_var_flag:=var_flag;
+restart:if(cur_cmd<min_primary_command)or@|
+ (cur_cmd>max_primary_command) then
+ bad_exp("An");
+@.An expression...@>
+scan_tertiary;
+continue: if cur_cmd<=max_expression_command then
+ if cur_cmd>=min_expression_command then
+ if (cur_cmd<>equals)or(my_var_flag<>assignment) then
+ begin p:=stash_cur_exp; c:=cur_mod; d:=cur_cmd;
+ if d=expression_tertiary_macro then
+ begin mac_name:=cur_sym; add_mac_ref(c);
+ end;
+ if (d<ampersand)or((d=ampersand)and@|
+ ((type(p)=pair_type)or(type(p)=path_type))) then
+ @<Scan a path construction operation;
+ but |return| if |p| has the wrong type@>
+ else begin get_x_next; scan_tertiary;
+ if d<>expression_tertiary_macro then do_binary(p,c)
+ else begin back_input; binary_mac(p,c,mac_name);
+ decr(ref_count(c)); get_x_next; goto restart;
+ end;
+ end;
+ goto continue;
+ end;
+exit:end;
+
+@ The reader should review the data structure conventions for paths before
+hoping to understand the next part of this code.
+
+@<Scan a path construction operation...@>=
+begin cycle_hit:=false;
+@<Convert the left operand, |p|, into a partial path ending at~|q|;
+ but |return| if |p| doesn't have a suitable type@>;
+continue_path: @<Determine the path join parameters;
+ but |goto finish_path| if there's only a direction specifier@>;
+if cur_cmd=cycle then @<Get ready to close a cycle@>
+else begin scan_tertiary;
+ @<Convert the right operand, |cur_exp|,
+ into a partial path from |pp| to~|qq|@>;
+ end;
+@<Join the partial paths and reset |p| and |q| to the head and tail
+ of the result@>;
+if cur_cmd>=min_expression_command then
+ if cur_cmd<=ampersand then if not cycle_hit then goto continue_path;
+finish_path:
+@<Choose control points for the path and put the result into |cur_exp|@>;
+end
+
+@ @<Convert the left operand, |p|, into a partial path ending at~|q|...@>=
+begin unstash_cur_exp(p);
+if cur_type=pair_type then p:=new_knot
+else if cur_type=path_type then p:=cur_exp
+else return;
+q:=p;
+while link(q)<>p do q:=link(q);
+if left_type(p)<>endpoint then {open up a cycle}
+ begin r:=copy_knot(p); link(q):=r; q:=r;
+ end;
+left_type(p):=open; right_type(q):=open;
+end
+
+@ A pair of numeric values is changed into a knot node for a one-point path
+when \MF\ discovers that the pair is part of a path.
+
+@p@t\4@>@<Declare the procedure called |known_pair|@>@;
+function new_knot:pointer; {convert a pair to a knot with two endpoints}
+var @!q:pointer; {the new node}
+begin q:=get_node(knot_node_size); left_type(q):=endpoint;
+right_type(q):=endpoint; link(q):=q;@/
+known_pair; x_coord(q):=cur_x; y_coord(q):=cur_y;
+new_knot:=q;
+end;
+
+@ The |known_pair| subroutine sets |cur_x| and |cur_y| to the components
+of the current expression, assuming that the current expression is a
+pair of known numerics. Unknown components are zeroed, and the
+current expression is flushed.
+
+@<Declare the procedure called |known_pair|@>=
+procedure known_pair;
+var @!p:pointer; {the pair node}
+begin if cur_type<>pair_type then
+ begin exp_err("Undefined coordinates have been replaced by (0,0)");
+@.Undefined coordinates...@>
+ help5("I need x and y numbers for this part of the path.")@/
+ ("The value I found (see above) was no good;")@/
+ ("so I'll try to keep going by using zero instead.")@/
+ ("(Chapter 27 of The METAFONTbook explains that")@/
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+ ("you might want to type `I ???' now.)");
+ put_get_flush_error(0); cur_x:=0; cur_y:=0;
+ end
+else begin p:=value(cur_exp);
+ @<Make sure that both |x| and |y| parts of |p| are known;
+ copy them into |cur_x| and |cur_y|@>;
+ flush_cur_exp(0);
+ end;
+end;
+
+@ @<Make sure that both |x| and |y| parts of |p| are known...@>=
+if type(x_part_loc(p))=known then cur_x:=value(x_part_loc(p))
+else begin disp_err(x_part_loc(p),
+ "Undefined x coordinate has been replaced by 0");
+@.Undefined coordinates...@>
+ help5("I need a `known' x value for this part of the path.")@/
+ ("The value I found (see above) was no good;")@/
+ ("so I'll try to keep going by using zero instead.")@/
+ ("(Chapter 27 of The METAFONTbook explains that")@/
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+ ("you might want to type `I ???' now.)");
+ put_get_error; recycle_value(x_part_loc(p)); cur_x:=0;
+ end;
+if type(y_part_loc(p))=known then cur_y:=value(y_part_loc(p))
+else begin disp_err(y_part_loc(p),
+ "Undefined y coordinate has been replaced by 0");
+ help5("I need a `known' y value for this part of the path.")@/
+ ("The value I found (see above) was no good;")@/
+ ("so I'll try to keep going by using zero instead.")@/
+ ("(Chapter 27 of The METAFONTbook explains that")@/
+ ("you might want to type `I ???' now.)");
+ put_get_error; recycle_value(y_part_loc(p)); cur_y:=0;
+ end
+
+@ At this point |cur_cmd| is either |ampersand|, |left_brace|, or |path_join|.
+
+@<Determine the path join parameters...@>=
+if cur_cmd=left_brace then
+ @<Put the pre-join direction information into node |q|@>;
+d:=cur_cmd;
+if d=path_join then @<Determine the tension and/or control points@>
+else if d<>ampersand then goto finish_path;
+get_x_next;
+if cur_cmd=left_brace then
+ @<Put the post-join direction information into |x| and |t|@>
+else if right_type(q)<>explicit then
+ begin t:=open; x:=0;
+ end
+
+@ The |scan_direction| subroutine looks at the directional information
+that is enclosed in braces, and also scans ahead to the following character.
+A type code is returned, either |open| (if the direction was $(0,0)$),
+or |curl| (if the direction was a curl of known value |cur_exp|), or
+|given| (if the direction is given by the |angle| value that now
+appears in |cur_exp|).
+
+There's nothing difficult about this subroutine, but the program is rather
+lengthy because a variety of potential errors need to be nipped in the bud.
+
+@p function scan_direction:small_number;
+var @!t:given..open; {the type of information found}
+@!x:scaled; {an |x| coordinate}
+begin get_x_next;
+if cur_cmd=curl_command then @<Scan a curl specification@>
+else @<Scan a given direction@>;
+if cur_cmd<>right_brace then
+ begin missing_err("}");@/
+@.Missing `\char`\}'@>
+ help3("I've scanned a direction spec for part of a path,")@/
+ ("so a right brace should have come next.")@/
+ ("I shall pretend that one was there.");@/
+ back_error;
+ end;
+get_x_next; scan_direction:=t;
+end;
+
+@ @<Scan a curl specification@>=
+begin get_x_next; scan_expression;
+if (cur_type<>known)or(cur_exp<0) then
+ begin exp_err("Improper curl has been replaced by 1");
+@.Improper curl@>
+ help1("A curl must be a known, nonnegative number.");
+ put_get_flush_error(unity);
+ end;
+t:=curl;
+end
+
+@ @<Scan a given direction@>=
+begin scan_expression;
+if cur_type>pair_type then @<Get given directions separated by commas@>
+else known_pair;
+if (cur_x=0)and(cur_y=0) then t:=open
+else begin t:=given; cur_exp:=n_arg(cur_x,cur_y);
+ end;
+end
+
+@ @<Get given directions separated by commas@>=
+begin if cur_type<>known then
+ begin exp_err("Undefined x coordinate has been replaced by 0");
+@.Undefined coordinates...@>
+ help5("I need a `known' x value for this part of the path.")@/
+ ("The value I found (see above) was no good;")@/
+ ("so I'll try to keep going by using zero instead.")@/
+ ("(Chapter 27 of The METAFONTbook explains that")@/
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+ ("you might want to type `I ???' now.)");
+ put_get_flush_error(0);
+ end;
+x:=cur_exp;
+if cur_cmd<>comma then
+ begin missing_err(",");@/
+@.Missing `,'@>
+ help2("I've got the x coordinate of a path direction;")@/
+ ("will look for the y coordinate next.");
+ back_error;
+ end;
+get_x_next; scan_expression;
+if cur_type<>known then
+ begin exp_err("Undefined y coordinate has been replaced by 0");
+ help5("I need a `known' y value for this part of the path.")@/
+ ("The value I found (see above) was no good;")@/
+ ("so I'll try to keep going by using zero instead.")@/
+ ("(Chapter 27 of The METAFONTbook explains that")@/
+ ("you might want to type `I ???' now.)");
+ put_get_flush_error(0);
+ end;
+cur_y:=cur_exp; cur_x:=x;
+end
+
+@ At this point |right_type(q)| is usually |open|, but it may have been
+set to some other value by a previous operation. We must maintain
+the value of |right_type(q)| in cases such as
+`\.{..\{curl2\}z\{0,0\}..}'.
+
+@<Put the pre-join...@>=
+begin t:=scan_direction;
+if t<>open then
+ begin right_type(q):=t; right_given(q):=cur_exp;
+ if left_type(q)=open then
+ begin left_type(q):=t; left_given(q):=cur_exp;
+ end; {note that |left_given(q)=left_curl(q)|}
+ end;
+end
+
+@ Since |left_tension| and |left_y| share the same position in knot nodes,
+and since |left_given| is similarly equivalent to |left_x|, we use
+|x| and |y| to hold the given direction and tension information when
+there are no explicit control points.
+
+@<Put the post-join...@>=
+begin t:=scan_direction;
+if right_type(q)<>explicit then x:=cur_exp
+else t:=explicit; {the direction information is superfluous}
+end
+
+@ @<Determine the tension and/or...@>=
+begin get_x_next;
+if cur_cmd=tension then @<Set explicit tensions@>
+else if cur_cmd=controls then @<Set explicit control points@>
+else begin right_tension(q):=unity; y:=unity; back_input; {default tension}
+ goto done;
+ end;
+if cur_cmd<>path_join then
+ begin missing_err("..");@/
+@.Missing `..'@>
+ help1("A path join command should end with two dots.");
+ back_error;
+ end;
+done:end
+
+@ @<Set explicit tensions@>=
+begin get_x_next; y:=cur_cmd;
+if cur_cmd=at_least then get_x_next;
+scan_primary;
+@<Make sure that the current expression is a valid tension setting@>;
+if y=at_least then negate(cur_exp);
+right_tension(q):=cur_exp;
+if cur_cmd=and_command then
+ begin get_x_next; y:=cur_cmd;
+ if cur_cmd=at_least then get_x_next;
+ scan_primary;
+ @<Make sure that the current expression is a valid tension setting@>;
+ if y=at_least then negate(cur_exp);
+ end;
+y:=cur_exp;
+end
+
+@ @d min_tension==three_quarter_unit
+
+@<Make sure that the current expression is a valid tension setting@>=
+if (cur_type<>known)or(cur_exp<min_tension) then
+ begin exp_err("Improper tension has been set to 1");
+@.Improper tension@>
+ help1("The expression above should have been a number >=3/4.");
+ put_get_flush_error(unity);
+ end
+
+@ @<Set explicit control points@>=
+begin right_type(q):=explicit; t:=explicit; get_x_next; scan_primary;@/
+known_pair; right_x(q):=cur_x; right_y(q):=cur_y;
+if cur_cmd<>and_command then
+ begin x:=right_x(q); y:=right_y(q);
+ end
+else begin get_x_next; scan_primary;@/
+ known_pair; x:=cur_x; y:=cur_y;
+ end;
+end
+
+@ @<Convert the right operand, |cur_exp|, into a partial path...@>=
+begin if cur_type<>path_type then pp:=new_knot
+else pp:=cur_exp;
+qq:=pp;
+while link(qq)<>pp do qq:=link(qq);
+if left_type(pp)<>endpoint then {open up a cycle}
+ begin r:=copy_knot(pp); link(qq):=r; qq:=r;
+ end;
+left_type(pp):=open; right_type(qq):=open;
+end
+
+@ If a person tries to define an entire path by saying `\.{(x,y)\&cycle}',
+we silently change the specification to `\.{(x,y)..cycle}', since a cycle
+shouldn't have length zero.
+
+@<Get ready to close a cycle@>=
+begin cycle_hit:=true; get_x_next; pp:=p; qq:=p;
+if d=ampersand then if p=q then
+ begin d:=path_join; right_tension(q):=unity; y:=unity;
+ end;
+end
+
+@ @<Join the partial paths and reset |p| and |q|...@>=
+begin if d=ampersand then
+ if (x_coord(q)<>x_coord(pp))or(y_coord(q)<>y_coord(pp)) then
+ begin print_err("Paths don't touch; `&' will be changed to `..'");
+@.Paths don't touch@>
+ help3("When you join paths `p&q', the ending point of p")@/
+ ("must be exactly equal to the starting point of q.")@/
+ ("So I'm going to pretend that you said `p..q' instead.");
+ put_get_error; d:=path_join; right_tension(q):=unity; y:=unity;
+ end;
+@<Plug an opening in |right_type(pp)|, if possible@>;
+if d=ampersand then @<Splice independent paths together@>
+else begin @<Plug an opening in |right_type(q)|, if possible@>;
+ link(q):=pp; left_y(pp):=y;
+ if t<>open then
+ begin left_x(pp):=x; left_type(pp):=t;
+ end;
+ end;
+q:=qq;
+end
+
+@ @<Plug an opening in |right_type(q)|...@>=
+if right_type(q)=open then
+ if (left_type(q)=curl)or(left_type(q)=given) then
+ begin right_type(q):=left_type(q); right_given(q):=left_given(q);
+ end
+
+@ @<Plug an opening in |right_type(pp)|...@>=
+if right_type(pp)=open then
+ if (t=curl)or(t=given) then
+ begin right_type(pp):=t; right_given(pp):=x;
+ end
+
+@ @<Splice independent paths together@>=
+begin if left_type(q)=open then if right_type(q)=open then
+ begin left_type(q):=curl; left_curl(q):=unity;
+ end;
+if right_type(pp)=open then if t=open then
+ begin right_type(pp):=curl; right_curl(pp):=unity;
+ end;
+right_type(q):=right_type(pp); link(q):=link(pp);@/
+right_x(q):=right_x(pp); right_y(q):=right_y(pp);
+free_node(pp,knot_node_size);
+if qq=pp then qq:=q;
+end
+
+@ @<Choose control points for the path...@>=
+if cycle_hit then
+ begin if d=ampersand then p:=q;
+ end
+else begin left_type(p):=endpoint;
+ if right_type(p)=open then
+ begin right_type(p):=curl; right_curl(p):=unity;
+ end;
+ right_type(q):=endpoint;
+ if left_type(q)=open then
+ begin left_type(q):=curl; left_curl(q):=unity;
+ end;
+ link(q):=p;
+ end;
+make_choices(p);
+cur_type:=path_type; cur_exp:=p
+
+@ Finally, we sometimes need to scan an expression whose value is
+supposed to be either |true_code| or |false_code|.
+
+@<Declare the basic parsing subroutines@>=
+procedure get_boolean;
+begin get_x_next; scan_expression;
+if cur_type<>boolean_type then
+ begin exp_err("Undefined condition will be treated as `false'");
+@.Undefined condition...@>
+ help2("The expression shown above should have had a definite")@/
+ ("true-or-false value. I'm changing it to `false'.");@/
+ put_get_flush_error(false_code); cur_type:=boolean_type;
+ end;
+end;
+
+@* \[42] Doing the operations.
+The purpose of parsing is primarily to permit people to avoid piles of
+parentheses. But the real work is done after the structure of an expression
+has been recognized; that's when new expressions are generated. We
+turn now to the guts of \MF, which handles individual operators that
+have come through the parsing mechanism.
+
+We'll start with the easy ones that take no operands, then work our way
+up to operators with one and ultimately two arguments. In other words,
+we will write the three procedures |do_nullary|, |do_unary|, and |do_binary|
+that are invoked periodically by the expression scanners.
+
+First let's make sure that all of the primitive operators are in the
+hash table. Although |scan_primary| and its relatives made use of the
+\\{cmd} code for these operators, the \\{do} routines base everything
+on the \\{mod} code. For example, |do_binary| doesn't care whether the
+operation it performs is a |primary_binary| or |secondary_binary|, etc.
+
+@<Put each...@>=
+primitive("true",nullary,true_code);@/
+@!@:true_}{\&{true} primitive@>
+primitive("false",nullary,false_code);@/
+@!@:false_}{\&{false} primitive@>
+primitive("nullpicture",nullary,null_picture_code);@/
+@!@:null_picture_}{\&{nullpicture} primitive@>
+primitive("nullpen",nullary,null_pen_code);@/
+@!@:null_pen_}{\&{nullpen} primitive@>
+primitive("jobname",nullary,job_name_op);@/
+@!@:job_name_}{\&{jobname} primitive@>
+primitive("readstring",nullary,read_string_op);@/
+@!@:read_string_}{\&{readstring} primitive@>
+primitive("pencircle",nullary,pen_circle);@/
+@!@:pen_circle_}{\&{pencircle} primitive@>
+primitive("normaldeviate",nullary,normal_deviate);@/
+@!@:normal_deviate_}{\&{normaldeviate} primitive@>
+primitive("odd",unary,odd_op);@/
+@!@:odd_}{\&{odd} primitive@>
+primitive("known",unary,known_op);@/
+@!@:known_}{\&{known} primitive@>
+primitive("unknown",unary,unknown_op);@/
+@!@:unknown_}{\&{unknown} primitive@>
+primitive("not",unary,not_op);@/
+@!@:not_}{\&{not} primitive@>
+primitive("decimal",unary,decimal);@/
+@!@:decimal_}{\&{decimal} primitive@>
+primitive("reverse",unary,reverse);@/
+@!@:reverse_}{\&{reverse} primitive@>
+primitive("makepath",unary,make_path_op);@/
+@!@:make_path_}{\&{makepath} primitive@>
+primitive("makepen",unary,make_pen_op);@/
+@!@:make_pen_}{\&{makepen} primitive@>
+primitive("totalweight",unary,total_weight_op);@/
+@!@:total_weight_}{\&{totalweight} primitive@>
+primitive("oct",unary,oct_op);@/
+@!@:oct_}{\&{oct} primitive@>
+primitive("hex",unary,hex_op);@/
+@!@:hex_}{\&{hex} primitive@>
+primitive("ASCII",unary,ASCII_op);@/
+@!@:ASCII_}{\&{ASCII} primitive@>
+primitive("char",unary,char_op);@/
+@!@:char_}{\&{char} primitive@>
+primitive("length",unary,length_op);@/
+@!@:length_}{\&{length} primitive@>
+primitive("turningnumber",unary,turning_op);@/
+@!@:turning_number_}{\&{turningnumber} primitive@>
+primitive("xpart",unary,x_part);@/
+@!@:x_part_}{\&{xpart} primitive@>
+primitive("ypart",unary,y_part);@/
+@!@:y_part_}{\&{ypart} primitive@>
+primitive("xxpart",unary,xx_part);@/
+@!@:xx_part_}{\&{xxpart} primitive@>
+primitive("xypart",unary,xy_part);@/
+@!@:xy_part_}{\&{xypart} primitive@>
+primitive("yxpart",unary,yx_part);@/
+@!@:yx_part_}{\&{yxpart} primitive@>
+primitive("yypart",unary,yy_part);@/
+@!@:yy_part_}{\&{yypart} primitive@>
+primitive("sqrt",unary,sqrt_op);@/
+@!@:sqrt_}{\&{sqrt} primitive@>
+primitive("mexp",unary,m_exp_op);@/
+@!@:m_exp_}{\&{mexp} primitive@>
+primitive("mlog",unary,m_log_op);@/
+@!@:m_log_}{\&{mlog} primitive@>
+primitive("sind",unary,sin_d_op);@/
+@!@:sin_d_}{\&{sind} primitive@>
+primitive("cosd",unary,cos_d_op);@/
+@!@:cos_d_}{\&{cosd} primitive@>
+primitive("floor",unary,floor_op);@/
+@!@:floor_}{\&{floor} primitive@>
+primitive("uniformdeviate",unary,uniform_deviate);@/
+@!@:uniform_deviate_}{\&{uniformdeviate} primitive@>
+primitive("charexists",unary,char_exists_op);@/
+@!@:char_exists_}{\&{charexists} primitive@>
+primitive("angle",unary,angle_op);@/
+@!@:angle_}{\&{angle} primitive@>
+primitive("cycle",cycle,cycle_op);@/
+@!@:cycle_}{\&{cycle} primitive@>
+primitive("+",plus_or_minus,plus);@/
+@!@:+ }{\.{+} primitive@>
+primitive("-",plus_or_minus,minus);@/
+@!@:- }{\.{-} primitive@>
+primitive("*",secondary_binary,times);@/
+@!@:* }{\.{*} primitive@>
+primitive("/",slash,over); eqtb[frozen_slash]:=eqtb[cur_sym];@/
+@!@:/ }{\.{/} primitive@>
+primitive("++",tertiary_binary,pythag_add);@/
+@!@:++_}{\.{++} primitive@>
+primitive("+-+",tertiary_binary,pythag_sub);@/
+@!@:+-+_}{\.{+-+} primitive@>
+primitive("and",and_command,and_op);@/
+@!@:and_}{\&{and} primitive@>
+primitive("or",tertiary_binary,or_op);@/
+@!@:or_}{\&{or} primitive@>
+primitive("<",expression_binary,less_than);@/
+@!@:< }{\.{<} primitive@>
+primitive("<=",expression_binary,less_or_equal);@/
+@!@:<=_}{\.{<=} primitive@>
+primitive(">",expression_binary,greater_than);@/
+@!@:> }{\.{>} primitive@>
+primitive(">=",expression_binary,greater_or_equal);@/
+@!@:>=_}{\.{>=} primitive@>
+primitive("=",equals,equal_to);@/
+@!@:= }{\.{=} primitive@>
+primitive("<>",expression_binary,unequal_to);@/
+@!@:<>_}{\.{<>} primitive@>
+primitive("substring",primary_binary,substring_of);@/
+@!@:substring_}{\&{substring} primitive@>
+primitive("subpath",primary_binary,subpath_of);@/
+@!@:subpath_}{\&{subpath} primitive@>
+primitive("directiontime",primary_binary,direction_time_of);@/
+@!@:direction_time_}{\&{directiontime} primitive@>
+primitive("point",primary_binary,point_of);@/
+@!@:point_}{\&{point} primitive@>
+primitive("precontrol",primary_binary,precontrol_of);@/
+@!@:precontrol_}{\&{precontrol} primitive@>
+primitive("postcontrol",primary_binary,postcontrol_of);@/
+@!@:postcontrol_}{\&{postcontrol} primitive@>
+primitive("penoffset",primary_binary,pen_offset_of);@/
+@!@:pen_offset_}{\&{penoffset} primitive@>
+primitive("&",ampersand,concatenate);@/
+@!@:!!!}{\.{\&} primitive@>
+primitive("rotated",secondary_binary,rotated_by);@/
+@!@:rotated_}{\&{rotated} primitive@>
+primitive("slanted",secondary_binary,slanted_by);@/
+@!@:slanted_}{\&{slanted} primitive@>
+primitive("scaled",secondary_binary,scaled_by);@/
+@!@:scaled_}{\&{scaled} primitive@>
+primitive("shifted",secondary_binary,shifted_by);@/
+@!@:shifted_}{\&{shifted} primitive@>
+primitive("transformed",secondary_binary,transformed_by);@/
+@!@:transformed_}{\&{transformed} primitive@>
+primitive("xscaled",secondary_binary,x_scaled);@/
+@!@:x_scaled_}{\&{xscaled} primitive@>
+primitive("yscaled",secondary_binary,y_scaled);@/
+@!@:y_scaled_}{\&{yscaled} primitive@>
+primitive("zscaled",secondary_binary,z_scaled);@/
+@!@:z_scaled_}{\&{zscaled} primitive@>
+primitive("intersectiontimes",tertiary_binary,intersect);@/
+@!@:intersection_times_}{\&{intersectiontimes} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+nullary,unary,primary_binary,secondary_binary,tertiary_binary,
+ expression_binary,cycle,plus_or_minus,slash,ampersand,equals,and_command:
+ print_op(m);
+
+@ OK, let's look at the simplest \\{do} procedure first.
+
+@p procedure do_nullary(@!c:quarterword);
+var @!k:integer; {all-purpose loop index}
+begin check_arith;
+if internal[tracing_commands]>two then
+ show_cmd_mod(nullary,c);
+case c of
+true_code,false_code:begin cur_type:=boolean_type; cur_exp:=c;
+ end;
+null_picture_code:begin cur_type:=picture_type;
+ cur_exp:=get_node(edge_header_size); init_edges(cur_exp);
+ end;
+null_pen_code:begin cur_type:=pen_type; cur_exp:=null_pen;
+ end;
+normal_deviate:begin cur_type:=known; cur_exp:=norm_rand;
+ end;
+pen_circle:@<Make a special knot node for \&{pencircle}@>;
+job_name_op: begin if job_name=0 then open_log_file;
+ cur_type:=string_type; cur_exp:=job_name;
+ end;
+read_string_op:@<Read a string from the terminal@>;
+end; {there are no other cases}
+check_arith;
+end;
+
+@ @<Make a special knot node for \&{pencircle}@>=
+begin cur_type:=future_pen; cur_exp:=get_node(knot_node_size);
+left_type(cur_exp):=open; right_type(cur_exp):=open;
+link(cur_exp):=cur_exp;@/
+x_coord(cur_exp):=0; y_coord(cur_exp):=0;@/
+left_x(cur_exp):=unity; left_y(cur_exp):=0;@/
+right_x(cur_exp):=0; right_y(cur_exp):=unity;@/
+end
+
+@ @<Read a string...@>=
+begin if interaction<=nonstop_mode then
+ fatal_error("*** (cannot readstring in nonstop modes)");
+begin_file_reading; name:=1; prompt_input("");
+str_room(last-start);
+for k:=start to last-1 do append_char(buffer[k]);
+end_file_reading; cur_type:=string_type; cur_exp:=make_string;
+end
+
+@ Things get a bit more interesting when there's an operand. The
+operand to |do_unary| appears in |cur_type| and |cur_exp|.
+
+@p @t\4@>@<Declare unary action procedures@>@;
+procedure do_unary(@!c:quarterword);
+var @!p,@!q:pointer; {for list manipulation}
+@!x:integer; {a temporary register}
+begin check_arith;
+if internal[tracing_commands]>two then
+ @<Trace the current unary operation@>;
+case c of
+plus:if cur_type<pair_type then
+ if cur_type<>picture_type then bad_unary(plus);
+minus:@<Negate the current expression@>;
+@t\4@>@<Additional cases of unary operators@>@;
+end; {there are no other cases}
+check_arith;
+end;
+
+@ The |nice_pair| function returns |true| if both components of a pair
+are known.
+
+@<Declare unary action procedures@>=
+function nice_pair(@!p:integer;@!t:quarterword):boolean;
+label exit;
+begin if t=pair_type then
+ begin p:=value(p);
+ if type(x_part_loc(p))=known then
+ if type(y_part_loc(p))=known then
+ begin nice_pair:=true; return;
+ end;
+ end;
+nice_pair:=false;
+exit:end;
+
+@ @<Declare unary action...@>=
+procedure print_known_or_unknown_type(@!t:small_number;@!v:integer);
+begin print_char("(");
+if t<dependent then
+ if t<>pair_type then print_type(t)
+ else if nice_pair(v,pair_type) then print("pair")
+ else print("unknown pair")
+else print("unknown numeric");
+print_char(")");
+end;
+
+@ @<Declare unary action...@>=
+procedure bad_unary(@!c:quarterword);
+begin exp_err("Not implemented: "); print_op(c);
+@.Not implemented...@>
+print_known_or_unknown_type(cur_type,cur_exp);
+help3("I'm afraid I don't know how to apply that operation to that")@/
+ ("particular type. Continue, and I'll simply return the")@/
+ ("argument (shown above) as the result of the operation.");
+put_get_error;
+end;
+
+@ @<Trace the current unary operation@>=
+begin begin_diagnostic; print_nl("{"); print_op(c); print_char("(");@/
+print_exp(null,0); {show the operand, but not verbosely}
+print(")}"); end_diagnostic(false);
+end
+
+@ Negation is easy except when the current expression
+is of type |independent|, or when it is a pair with one or more
+|independent| components.
+
+It is tempting to argue that the negative of an independent variable
+is an independent variable, hence we don't have to do anything when
+negating it. The fallacy is that other dependent variables pointing
+to the current expression must change the sign of their
+coefficients if we make no change to the current expression.
+
+Instead, we work around the problem by copying the current expression
+and recycling it afterwards (cf.~the |stash_in| routine).
+
+@<Negate the current expression@>=
+case cur_type of
+pair_type,independent: begin q:=cur_exp; make_exp_copy(q);
+ if cur_type=dependent then negate_dep_list(dep_list(cur_exp))
+ else if cur_type=pair_type then
+ begin p:=value(cur_exp);
+ if type(x_part_loc(p))=known then negate(value(x_part_loc(p)))
+ else negate_dep_list(dep_list(x_part_loc(p)));
+ if type(y_part_loc(p))=known then negate(value(y_part_loc(p)))
+ else negate_dep_list(dep_list(y_part_loc(p)));
+ end; {if |cur_type=known| then |cur_exp=0|}
+ recycle_value(q); free_node(q,value_node_size);
+ end;
+dependent,proto_dependent:negate_dep_list(dep_list(cur_exp));
+known:negate(cur_exp);
+picture_type:negate_edges(cur_exp);
+othercases bad_unary(minus)
+endcases
+
+@ @<Declare unary action...@>=
+procedure negate_dep_list(@!p:pointer);
+label exit;
+begin loop@+begin negate(value(p));
+ if info(p)=null then return;
+ p:=link(p);
+ end;
+exit:end;
+
+@ @<Additional cases of unary operators@>=
+not_op: if cur_type<>boolean_type then bad_unary(not_op)
+ else cur_exp:=true_code+false_code-cur_exp;
+
+@ @d three_sixty_units==23592960 {that's |360*unity|}
+@d boolean_reset(#)==if # then cur_exp:=true_code@+else cur_exp:=false_code
+
+@<Additional cases of unary operators@>=
+sqrt_op,m_exp_op,m_log_op,sin_d_op,cos_d_op,floor_op,
+ uniform_deviate,odd_op,char_exists_op:@t@>@;@/
+ if cur_type<>known then bad_unary(c)
+ else case c of
+ sqrt_op:cur_exp:=square_rt(cur_exp);
+ m_exp_op:cur_exp:=m_exp(cur_exp);
+ m_log_op:cur_exp:=m_log(cur_exp);
+ sin_d_op,cos_d_op:begin n_sin_cos((cur_exp mod three_sixty_units)*16);
+ if c=sin_d_op then cur_exp:=round_fraction(n_sin)
+ else cur_exp:=round_fraction(n_cos);
+ end;
+ floor_op:cur_exp:=floor_scaled(cur_exp);
+ uniform_deviate:cur_exp:=unif_rand(cur_exp);
+ odd_op: begin boolean_reset(odd(round_unscaled(cur_exp)));
+ cur_type:=boolean_type;
+ end;
+ char_exists_op:@<Determine if a character has been shipped out@>;
+ end; {there are no other cases}
+
+@ @<Additional cases of unary operators@>=
+angle_op:if nice_pair(cur_exp,cur_type) then
+ begin p:=value(cur_exp);
+ x:=n_arg(value(x_part_loc(p)),value(y_part_loc(p)));
+ if x>=0 then flush_cur_exp((x+8)div 16)
+ else flush_cur_exp(-((-x+8)div 16));
+ end
+ else bad_unary(angle_op);
+
+@ If the current expression is a pair, but the context wants it to
+be a path, we call |pair_to_path|.
+
+@<Declare unary action...@>=
+procedure pair_to_path;
+begin cur_exp:=new_knot; cur_type:=path_type;
+end;
+
+@ @<Additional cases of unary operators@>=
+x_part,y_part:if (cur_type<=pair_type)and(cur_type>=transform_type) then
+ take_part(c)
+ else bad_unary(c);
+xx_part,xy_part,yx_part,yy_part: if cur_type=transform_type then take_part(c)
+ else bad_unary(c);
+
+@ In the following procedure, |cur_exp| points to a capsule, which points to
+a big node. We want to delete all but one part of the big node.
+
+@<Declare unary action...@>=
+procedure take_part(@!c:quarterword);
+var @!p:pointer; {the big node}
+begin p:=value(cur_exp); value(temp_val):=p; type(temp_val):=cur_type;
+link(p):=temp_val; free_node(cur_exp,value_node_size);
+make_exp_copy(p+2*(c-x_part));
+recycle_value(temp_val);
+end;
+
+@ @<Initialize table entries...@>=
+name_type(temp_val):=capsule;
+
+@ @<Additional cases of unary...@>=
+char_op: if cur_type<>known then bad_unary(char_op)
+ else begin cur_exp:=round_unscaled(cur_exp) mod 256; cur_type:=string_type;
+ if cur_exp<0 then cur_exp:=cur_exp+256;
+ if length(cur_exp)<>1 then
+ begin str_room(1); append_char(cur_exp); cur_exp:=make_string;
+ end;
+ end;
+decimal: if cur_type<>known then bad_unary(decimal)
+ else begin old_setting:=selector; selector:=new_string;
+ print_scaled(cur_exp); cur_exp:=make_string;
+ selector:=old_setting; cur_type:=string_type;
+ end;
+oct_op,hex_op,ASCII_op: if cur_type<>string_type then bad_unary(c)
+ else str_to_num(c);
+
+@ @<Declare unary action...@>=
+procedure str_to_num(@!c:quarterword); {converts a string to a number}
+var @!n:integer; {accumulator}
+@!m:ASCII_code; {current character}
+@!k:pool_pointer; {index into |str_pool|}
+@!b:8..16; {radix of conversion}
+@!bad_char:boolean; {did the string contain an invalid digit?}
+begin if c=ASCII_op then
+ if length(cur_exp)=0 then n:=-1
+ else n:=so(str_pool[str_start[cur_exp]])
+else begin if c=oct_op then b:=8@+else b:=16;
+ n:=0; bad_char:=false;
+ for k:=str_start[cur_exp] to str_start[cur_exp+1]-1 do
+ begin m:=so(str_pool[k]);
+ if (m>="0")and(m<="9") then m:=m-"0"
+ else if (m>="A")and(m<="F") then m:=m-"A"+10
+ else if (m>="a")and(m<="f") then m:=m-"a"+10
+ else begin bad_char:=true; m:=0;
+ end;
+ if m>=b then
+ begin bad_char:=true; m:=0;
+ end;
+ if n<32768 div b then n:=n*b+m@+else n:=32767;
+ end;
+ @<Give error messages if |bad_char| or |n>=4096|@>;
+ end;
+flush_cur_exp(n*unity);
+end;
+
+@ @<Give error messages if |bad_char|...@>=
+if bad_char then
+ begin exp_err("String contains illegal digits");
+@.String contains illegal digits@>
+ if c=oct_op then
+ help1("I zeroed out characters that weren't in the range 0..7.")
+ else help1("I zeroed out characters that weren't hex digits.");
+ put_get_error;
+ end;
+if n>4095 then
+ begin print_err("Number too large ("); print_int(n); print_char(")");
+@.Number too large@>
+ help1("I have trouble with numbers greater than 4095; watch out.");
+ put_get_error;
+ end
+
+@ The length operation is somewhat unusual in that it applies to a variety
+of different types of operands.
+
+@<Additional cases of unary...@>=
+length_op: if cur_type=string_type then flush_cur_exp(length(cur_exp)*unity)
+ else if cur_type=path_type then flush_cur_exp(path_length)
+ else if cur_type=known then cur_exp:=abs(cur_exp)
+ else if nice_pair(cur_exp,cur_type) then
+ flush_cur_exp(pyth_add(value(x_part_loc(value(cur_exp))),@|
+ value(y_part_loc(value(cur_exp)))))
+ else bad_unary(c);
+
+@ @<Declare unary action...@>=
+function path_length:scaled; {computes the length of the current path}
+var @!n:scaled; {the path length so far}
+@!p:pointer; {traverser}
+begin p:=cur_exp;
+if left_type(p)=endpoint then n:=-unity@+else n:=0;
+repeat p:=link(p); n:=n+unity;
+until p=cur_exp;
+path_length:=n;
+end;
+
+@ The turning number is computed only with respect to null pens. A different
+pen might affect the turning number, in degenerate cases, because autorounding
+will produce a slightly different path, or because excessively large coordinates
+might be truncated.
+
+@<Additional cases of unary...@>=
+turning_op:if cur_type=pair_type then flush_cur_exp(0)
+ else if cur_type<>path_type then bad_unary(turning_op)
+ else if left_type(cur_exp)=endpoint then
+ flush_cur_exp(0) {not a cyclic path}
+ else begin cur_pen:=null_pen; cur_path_type:=contour_code;
+ cur_exp:=make_spec(cur_exp,
+ fraction_one-half_unit-1-el_gordo,0);
+ flush_cur_exp(turning_number*unity); {convert to |scaled|}
+ end;
+
+@ @d type_test_end== flush_cur_exp(true_code)
+ else flush_cur_exp(false_code);
+ cur_type:=boolean_type;
+ end
+@d type_range_end(#)==(cur_type<=#) then type_test_end
+@d type_range(#)==begin if (cur_type>=#) and type_range_end
+@d type_test(#)==begin if cur_type=# then type_test_end
+
+@<Additional cases of unary operators@>=
+boolean_type: type_range(boolean_type)(unknown_boolean);
+string_type: type_range(string_type)(unknown_string);
+pen_type: type_range(pen_type)(future_pen);
+path_type: type_range(path_type)(unknown_path);
+picture_type: type_range(picture_type)(unknown_picture);
+transform_type,pair_type: type_test(c);
+numeric_type: type_range(known)(independent);
+known_op,unknown_op: test_known(c);
+
+@ @<Declare unary action procedures@>=
+procedure test_known(@!c:quarterword);
+label done;
+var @!b:true_code..false_code; {is the current expression known?}
+@!p,@!q:pointer; {locations in a big node}
+begin b:=false_code;
+case cur_type of
+vacuous,boolean_type,string_type,pen_type,future_pen,path_type,picture_type,
+ known: b:=true_code;
+transform_type,pair_type:begin p:=value(cur_exp); q:=p+big_node_size[cur_type];
+ repeat q:=q-2;
+ if type(q)<>known then goto done;
+ until q=p;
+ b:=true_code;
+done: end;
+othercases do_nothing
+endcases;
+if c=known_op then flush_cur_exp(b)
+else flush_cur_exp(true_code+false_code-b);
+cur_type:=boolean_type;
+end;
+
+@ @<Additional cases of unary operators@>=
+cycle_op: begin if cur_type<>path_type then flush_cur_exp(false_code)
+ else if left_type(cur_exp)<>endpoint then flush_cur_exp(true_code)
+ else flush_cur_exp(false_code);
+ cur_type:=boolean_type;
+ end;
+
+@ @<Additional cases of unary operators@>=
+make_pen_op: begin if cur_type=pair_type then pair_to_path;
+ if cur_type=path_type then cur_type:=future_pen
+ else bad_unary(make_pen_op);
+ end;
+make_path_op: begin if cur_type=future_pen then materialize_pen;
+ if cur_type<>pen_type then bad_unary(make_path_op)
+ else begin flush_cur_exp(make_path(cur_exp)); cur_type:=path_type;
+ end;
+ end;
+total_weight_op: if cur_type<>picture_type then bad_unary(total_weight_op)
+ else flush_cur_exp(total_weight(cur_exp));
+reverse: if cur_type=path_type then
+ begin p:=htap_ypoc(cur_exp);
+ if right_type(p)=endpoint then p:=link(p);
+ toss_knot_list(cur_exp); cur_exp:=p;
+ end
+ else if cur_type=pair_type then pair_to_path
+ else bad_unary(reverse);
+
+@ Finally, we have the operations that combine a capsule~|p|
+with the current expression.
+
+@p @t\4@>@<Declare binary action procedures@>@;
+procedure do_binary(@!p:pointer;@!c:quarterword);
+label done,done1,exit;
+var @!q,@!r,@!rr:pointer; {for list manipulation}
+@!old_p,@!old_exp:pointer; {capsules to recycle}
+@!v:integer; {for numeric manipulation}
+begin check_arith;
+if internal[tracing_commands]>two then
+ @<Trace the current binary operation@>;
+@<Sidestep |independent| cases in capsule |p|@>;
+@<Sidestep |independent| cases in the current expression@>;
+case c of
+plus,minus:@<Add or subtract the current expression from |p|@>;
+@t\4@>@<Additional cases of binary operators@>@;
+end; {there are no other cases}
+recycle_value(p); free_node(p,value_node_size); {|return| to avoid this}
+exit:check_arith; @<Recycle any sidestepped |independent| capsules@>;
+end;
+
+@ @<Declare binary action...@>=
+procedure bad_binary(@!p:pointer;@!c:quarterword);
+begin disp_err(p,"");
+exp_err("Not implemented: ");
+@.Not implemented...@>
+if c>=min_of then print_op(c);
+print_known_or_unknown_type(type(p),p);
+if c>=min_of then print("of")@+else print_op(c);
+print_known_or_unknown_type(cur_type,cur_exp);@/
+help3("I'm afraid I don't know how to apply that operation to that")@/
+ ("combination of types. Continue, and I'll return the second")@/
+ ("argument (see above) as the result of the operation.");
+put_get_error;
+end;
+
+@ @<Trace the current binary operation@>=
+begin begin_diagnostic; print_nl("{(");
+print_exp(p,0); {show the operand, but not verbosely}
+print_char(")"); print_op(c); print_char("(");@/
+print_exp(null,0); print(")}"); end_diagnostic(false);
+end
+
+@ Several of the binary operations are potentially complicated by the
+fact that |independent| values can sneak into capsules. For example,
+we've seen an instance of this difficulty in the unary operation
+of negation. In order to reduce the number of cases that need to be
+handled, we first change the two operands (if necessary)
+to rid them of |independent| components. The original operands are
+put into capsules called |old_p| and |old_exp|, which will be
+recycled after the binary operation has been safely carried out.
+
+@<Recycle any sidestepped |independent| capsules@>=
+if old_p<>null then
+ begin recycle_value(old_p); free_node(old_p,value_node_size);
+ end;
+if old_exp<>null then
+ begin recycle_value(old_exp); free_node(old_exp,value_node_size);
+ end
+
+@ A big node is considered to be ``tarnished'' if it contains at least one
+independent component. We will define a simple function called `|tarnished|'
+that returns |null| if and only if its argument is not tarnished.
+
+@<Sidestep |independent| cases in capsule |p|@>=
+case type(p) of
+transform_type,pair_type: old_p:=tarnished(p);
+independent: old_p:=void;
+othercases old_p:=null
+endcases;
+if old_p<>null then
+ begin q:=stash_cur_exp; old_p:=p; make_exp_copy(old_p);
+ p:=stash_cur_exp; unstash_cur_exp(q);
+ end;
+
+@ @<Sidestep |independent| cases in the current expression@>=
+case cur_type of
+transform_type,pair_type:old_exp:=tarnished(cur_exp);
+independent:old_exp:=void;
+othercases old_exp:=null
+endcases;
+if old_exp<>null then
+ begin old_exp:=cur_exp; make_exp_copy(old_exp);
+ end
+
+@ @<Declare binary action...@>=
+function tarnished(@!p:pointer):pointer;
+label exit;
+var @!q:pointer; {beginning of the big node}
+@!r:pointer; {current position in the big node}
+begin q:=value(p); r:=q+big_node_size[type(p)];
+repeat r:=r-2;
+if type(r)=independent then
+ begin tarnished:=void; return;
+ end;
+until r=q;
+tarnished:=null;
+exit:end;
+
+@ @<Add or subtract the current expression from |p|@>=
+if (cur_type<pair_type)or(type(p)<pair_type) then
+ if (cur_type=picture_type)and(type(p)=picture_type) then
+ begin if c=minus then negate_edges(cur_exp);
+ cur_edges:=cur_exp; merge_edges(value(p));
+ end
+ else bad_binary(p,c)
+else if cur_type=pair_type then
+ if type(p)<>pair_type then bad_binary(p,c)
+ else begin q:=value(p); r:=value(cur_exp);
+ add_or_subtract(x_part_loc(q),x_part_loc(r),c);
+ add_or_subtract(y_part_loc(q),y_part_loc(r),c);
+ end
+ else if type(p)=pair_type then bad_binary(p,c)
+ else add_or_subtract(p,null,c)
+
+@ The first argument to |add_or_subtract| is the location of a value node
+in a capsule or pair node that will soon be recycled. The second argument
+is either a location within a pair or transform node of |cur_exp|,
+or it is null (which means that |cur_exp| itself should be the second
+argument). The third argument is either |plus| or |minus|.
+
+The sum or difference of the numeric quantities will replace the second
+operand. Arithmetic overflow may go undetected; users aren't supposed to
+be monkeying around with really big values.
+@^overflow in arithmetic@>
+
+@<Declare binary action...@>=
+@t\4@>@<Declare the procedure called |dep_finish|@>@;
+procedure add_or_subtract(@!p,@!q:pointer;@!c:quarterword);
+label done,exit;
+var @!s,@!t:small_number; {operand types}
+@!r:pointer; {list traverser}
+@!v:integer; {second operand value}
+begin if q=null then
+ begin t:=cur_type;
+ if t<dependent then v:=cur_exp@+else v:=dep_list(cur_exp);
+ end
+else begin t:=type(q);
+ if t<dependent then v:=value(q)@+else v:=dep_list(q);
+ end;
+if t=known then
+ begin if c=minus then negate(v);
+ if type(p)=known then
+ begin v:=slow_add(value(p),v);
+ if q=null then cur_exp:=v@+else value(q):=v;
+ return;
+ end;
+ @<Add a known value to the constant term of |dep_list(p)|@>;
+ end
+else begin if c=minus then negate_dep_list(v);
+ @<Add operand |p| to the dependency list |v|@>;
+ end;
+exit:end;
+
+@ @<Add a known value to the constant term of |dep_list(p)|@>=
+r:=dep_list(p);
+while info(r)<>null do r:=link(r);
+value(r):=slow_add(value(r),v);
+if q=null then
+ begin q:=get_node(value_node_size); cur_exp:=q; cur_type:=type(p);
+ name_type(q):=capsule;
+ end;
+dep_list(q):=dep_list(p); type(q):=type(p);
+prev_dep(q):=prev_dep(p); link(prev_dep(p)):=q;
+type(p):=known; {this will keep the recycler from collecting non-garbage}
+
+@ We prefer |dependent| lists to |proto_dependent| ones, because it is
+nice to retain the extra accuracy of |fraction| coefficients.
+But we have to handle both kinds, and mixtures too.
+
+@<Add operand |p| to the dependency list |v|@>=
+if type(p)=known then
+ @<Add the known |value(p)| to the constant term of |v|@>
+else begin s:=type(p); r:=dep_list(p);
+ if t=dependent then
+ begin if s=dependent then
+ if max_coef(r)+max_coef(v)<coef_bound then
+ begin v:=p_plus_q(v,r,dependent); goto done;
+ end; {|fix_needed| will necessarily be false}
+ t:=proto_dependent; v:=p_over_v(v,unity,dependent,proto_dependent);
+ end;
+ if s=proto_dependent then v:=p_plus_q(v,r,proto_dependent)
+ else v:=p_plus_fq(v,unity,r,proto_dependent,dependent);
+ done: @<Output the answer, |v| (which might have become |known|)@>;
+ end
+
+@ @<Add the known |value(p)| to the constant term of |v|@>=
+begin while info(v)<>null do v:=link(v);
+value(v):=slow_add(value(p),value(v));
+end
+
+@ @<Output the answer, |v| (which might have become |known|)@>=
+if q<>null then dep_finish(v,q,t)
+else begin cur_type:=t; dep_finish(v,null,t);
+ end
+
+@ Here's the current situation: The dependency list |v| of type |t|
+should either be put into the current expression (if |q=null|) or
+into location |q| within a pair node (otherwise). The destination (|cur_exp|
+or |q|) formerly held a dependency list with the same
+final pointer as the list |v|.
+
+@<Declare the procedure called |dep_finish|@>=
+procedure dep_finish(@!v,@!q:pointer;@!t:small_number);
+var @!p:pointer; {the destination}
+@!vv:scaled; {the value, if it is |known|}
+begin if q=null then p:=cur_exp@+else p:=q;
+dep_list(p):=v; type(p):=t;
+if info(v)=null then
+ begin vv:=value(v);
+ if q=null then flush_cur_exp(vv)
+ else begin recycle_value(p); type(q):=known; value(q):=vv;
+ end;
+ end
+else if q=null then cur_type:=t;
+if fix_needed then fix_dependencies;
+end;
+
+@ Let's turn now to the six basic relations of comparison.
+
+@<Additional cases of binary operators@>=
+less_than,less_or_equal,greater_than,greater_or_equal,equal_to,unequal_to:
+ begin@t@>@;
+ if (cur_type>pair_type)and(type(p)>pair_type) then
+ add_or_subtract(p,null,minus) {|cur_exp:=(p)-cur_exp|}
+ else if cur_type<>type(p) then
+ begin bad_binary(p,c); goto done;
+ end
+ else if cur_type=string_type then
+ flush_cur_exp(str_vs_str(value(p),cur_exp))
+ else if (cur_type=unknown_string)or(cur_type=unknown_boolean) then
+ @<Check if unknowns have been equated@>
+ else if (cur_type=pair_type)or(cur_type=transform_type) then
+ @<Reduce comparison of big nodes to comparison of scalars@>
+ else if cur_type=boolean_type then flush_cur_exp(cur_exp-value(p))
+ else begin bad_binary(p,c); goto done;
+ end;
+ @<Compare the current expression with zero@>;
+done: end;
+
+@ @<Compare the current expression with zero@>=
+if cur_type<>known then
+ begin if cur_type<known then
+ begin disp_err(p,"");
+ help1("The quantities shown above have not been equated.")@/
+ end
+ else help2("Oh dear. I can't decide if the expression above is positive,")@/
+ ("negative, or zero. So this comparison test won't be `true'.");
+ exp_err("Unknown relation will be considered false");
+@.Unknown relation...@>
+ put_get_flush_error(false_code);
+ end
+else case c of
+ less_than: boolean_reset(cur_exp<0);
+ less_or_equal: boolean_reset(cur_exp<=0);
+ greater_than: boolean_reset(cur_exp>0);
+ greater_or_equal: boolean_reset(cur_exp>=0);
+ equal_to: boolean_reset(cur_exp=0);
+ unequal_to: boolean_reset(cur_exp<>0);
+ end; {there are no other cases}
+ cur_type:=boolean_type
+
+@ When two unknown strings are in the same ring, we know that they are
+equal. Otherwise, we don't know whether they are equal or not, so we
+make no change.
+
+@<Check if unknowns have been equated@>=
+begin q:=value(cur_exp);
+while (q<>cur_exp)and(q<>p) do q:=value(q);
+if q=p then flush_cur_exp(0);
+end
+
+@ @<Reduce comparison of big nodes to comparison of scalars@>=
+begin q:=value(p); r:=value(cur_exp);
+rr:=r+big_node_size[cur_type]-2;
+loop@+ begin add_or_subtract(q,r,minus);
+ if type(r)<>known then goto done1;
+ if value(r)<>0 then goto done1;
+ if r=rr then goto done1;
+ q:=q+2; r:=r+2;
+ end;
+done1:take_part(x_part+half(r-value(cur_exp)));
+end
+
+@ Here we use the sneaky fact that |and_op-false_code=or_op-true_code|.
+
+@<Additional cases of binary operators@>=
+and_op,or_op: if (type(p)<>boolean_type)or(cur_type<>boolean_type) then
+ bad_binary(p,c)
+ else if value(p)=c+false_code-and_op then cur_exp:=value(p);
+
+@ @<Additional cases of binary operators@>=
+times: if (cur_type<pair_type)or(type(p)<pair_type) then bad_binary(p,times)
+ else if (cur_type=known)or(type(p)=known) then
+ @<Multiply when at least one operand is known@>
+ else if (nice_pair(p,type(p))and(cur_type>pair_type))
+ or(nice_pair(cur_exp,cur_type)and(type(p)>pair_type)) then
+ begin hard_times(p); return;
+ end
+ else bad_binary(p,times);
+
+@ @<Multiply when at least one operand is known@>=
+begin if type(p)=known then
+ begin v:=value(p); free_node(p,value_node_size);
+ end
+else begin v:=cur_exp; unstash_cur_exp(p);
+ end;
+if cur_type=known then cur_exp:=take_scaled(cur_exp,v)
+else if cur_type=pair_type then
+ begin p:=value(cur_exp);
+ dep_mult(x_part_loc(p),v,true);
+ dep_mult(y_part_loc(p),v,true);
+ end
+else dep_mult(null,v,true);
+return;
+end
+
+@ @<Declare binary action...@>=
+procedure dep_mult(@!p:pointer;@!v:integer;@!v_is_scaled:boolean);
+label exit;
+var @!q:pointer; {the dependency list being multiplied by |v|}
+@!s,@!t:small_number; {its type, before and after}
+begin if p=null then q:=cur_exp
+else if type(p)<>known then q:=p
+else begin if v_is_scaled then value(p):=take_scaled(value(p),v)
+ else value(p):=take_fraction(value(p),v);
+ return;
+ end;
+t:=type(q); q:=dep_list(q); s:=t;
+if t=dependent then if v_is_scaled then
+ if ab_vs_cd(max_coef(q),abs(v),coef_bound-1,unity)>=0 then t:=proto_dependent;
+q:=p_times_v(q,v,s,t,v_is_scaled); dep_finish(q,p,t);
+exit:end;
+
+@ Here is a routine that is similar to |times|; but it is invoked only
+internally, when |v| is a |fraction| whose magnitude is at most~1,
+and when |cur_type>=pair_type|.
+
+@p procedure frac_mult(@!n,@!d:scaled); {multiplies |cur_exp| by |n/d|}
+var @!p:pointer; {a pair node}
+@!old_exp:pointer; {a capsule to recycle}
+@!v:fraction; {|n/d|}
+begin if internal[tracing_commands]>two then
+ @<Trace the fraction multiplication@>;
+case cur_type of
+transform_type,pair_type:old_exp:=tarnished(cur_exp);
+independent:old_exp:=void;
+othercases old_exp:=null
+endcases;
+if old_exp<>null then
+ begin old_exp:=cur_exp; make_exp_copy(old_exp);
+ end;
+v:=make_fraction(n,d);
+if cur_type=known then cur_exp:=take_fraction(cur_exp,v)
+else if cur_type=pair_type then
+ begin p:=value(cur_exp);
+ dep_mult(x_part_loc(p),v,false);
+ dep_mult(y_part_loc(p),v,false);
+ end
+else dep_mult(null,v,false);
+if old_exp<>null then
+ begin recycle_value(old_exp); free_node(old_exp,value_node_size);
+ end
+end;
+
+@ @<Trace the fraction multiplication@>=
+begin begin_diagnostic; print_nl("{("); print_scaled(n); print_char("/");
+print_scaled(d); print(")*("); print_exp(null,0); print(")}");
+end_diagnostic(false);
+end
+
+@ The |hard_times| routine multiplies a nice pair by a dependency list.
+
+@<Declare binary action procedures@>=
+procedure hard_times(@!p:pointer);
+var @!q:pointer; {a copy of the dependent variable |p|}
+@!r:pointer; {the big node for the nice pair}
+@!u,@!v:scaled; {the known values of the nice pair}
+begin if type(p)=pair_type then
+ begin q:=stash_cur_exp; unstash_cur_exp(p); p:=q;
+ end; {now |cur_type=pair_type|}
+r:=value(cur_exp); u:=value(x_part_loc(r)); v:=value(y_part_loc(r));
+@<Move the dependent variable |p| into both parts of the pair node |r|@>;
+dep_mult(x_part_loc(r),u,true); dep_mult(y_part_loc(r),v,true);
+end;
+
+@ @<Move the dependent variable |p|...@>=
+type(y_part_loc(r)):=type(p);
+new_dep(y_part_loc(r),copy_dep_list(dep_list(p)));@/
+type(x_part_loc(r)):=type(p);
+mem[value_loc(x_part_loc(r))]:=mem[value_loc(p)];
+link(prev_dep(p)):=x_part_loc(r);
+free_node(p,value_node_size)
+
+@ @<Additional cases of binary operators@>=
+over: if (cur_type<>known)or(type(p)<pair_type) then bad_binary(p,over)
+ else begin v:=cur_exp; unstash_cur_exp(p);
+ if v=0 then @<Squeal about division by zero@>
+ else begin if cur_type=known then cur_exp:=make_scaled(cur_exp,v)
+ else if cur_type=pair_type then
+ begin p:=value(cur_exp);
+ dep_div(x_part_loc(p),v);
+ dep_div(y_part_loc(p),v);
+ end
+ else dep_div(null,v);
+ end;
+ return;
+ end;
+
+@ @<Declare binary action...@>=
+procedure dep_div(@!p:pointer;@!v:scaled);
+label exit;
+var @!q:pointer; {the dependency list being divided by |v|}
+@!s,@!t:small_number; {its type, before and after}
+begin if p=null then q:=cur_exp
+else if type(p)<>known then q:=p
+else begin value(p):=make_scaled(value(p),v); return;
+ end;
+t:=type(q); q:=dep_list(q); s:=t;
+if t=dependent then
+ if ab_vs_cd(max_coef(q),unity,coef_bound-1,abs(v))>=0 then t:=proto_dependent;
+q:=p_over_v(q,v,s,t); dep_finish(q,p,t);
+exit:end;
+
+@ @<Squeal about division by zero@>=
+begin exp_err("Division by zero");
+@.Division by zero@>
+help2("You're trying to divide the quantity shown above the error")@/
+ ("message by zero. I'm going to divide it by one instead.");
+put_get_error;
+end
+
+@ @<Additional cases of binary operators@>=
+pythag_add,pythag_sub: if (cur_type=known)and(type(p)=known) then
+ if c=pythag_add then cur_exp:=pyth_add(value(p),cur_exp)
+ else cur_exp:=pyth_sub(value(p),cur_exp)
+ else bad_binary(p,c);
+
+@ The next few sections of the program deal with affine transformations
+of coordinate data.
+
+@<Additional cases of binary operators@>=
+rotated_by,slanted_by,scaled_by,shifted_by,transformed_by,
+ x_scaled,y_scaled,z_scaled: @t@>@;@/
+ if (type(p)=path_type)or(type(p)=future_pen)or(type(p)=pen_type) then
+ begin path_trans(p,c); return;
+ end
+ else if (type(p)=pair_type)or(type(p)=transform_type) then big_trans(p,c)
+ else if type(p)=picture_type then
+ begin edges_trans(p,c); return;
+ end
+ else bad_binary(p,c);
+
+@ Let |c| be one of the eight transform operators. The procedure call
+|set_up_trans(c)| first changes |cur_exp| to a transform that corresponds to
+|c| and the original value of |cur_exp|. (In particular, |cur_exp| doesn't
+change at all if |c=transformed_by|.)
+
+Then, if all components of the resulting transform are |known|, they are
+moved to the global variables |txx|, |txy|, |tyx|, |tyy|, |tx|, |ty|;
+and |cur_exp| is changed to the known value zero.
+
+@<Declare binary action...@>=
+procedure set_up_trans(@!c:quarterword);
+label done,exit;
+var @!p,@!q,@!r:pointer; {list manipulation registers}
+begin if (c<>transformed_by)or(cur_type<>transform_type) then
+ @<Put the current transform into |cur_exp|@>;
+@<If the current transform is entirely known, stash it in global variables;
+ otherwise |return|@>;
+exit:end;
+
+@ @<Glob...@>=
+@!txx,@!txy,@!tyx,@!tyy,@!tx,@!ty:scaled; {current transform coefficients}
+
+@ @<Put the current transform...@>=
+begin p:=stash_cur_exp; cur_exp:=id_transform; cur_type:=transform_type;
+q:=value(cur_exp);
+case c of
+@<For each of the eight cases, change the relevant fields of |cur_exp|
+ and |goto done|;
+ but do nothing if capsule |p| doesn't have the appropriate type@>@;
+end; {there are no other cases}
+disp_err(p,"Improper transformation argument");
+@.Improper transformation argument@>
+help3("The expression shown above has the wrong type,")@/
+ ("so I can't transform anything using it.")@/
+ ("Proceed, and I'll omit the transformation.");
+put_get_error;
+done: recycle_value(p); free_node(p,value_node_size);
+end
+
+@ @<If the current transform is entirely known, ...@>=
+q:=value(cur_exp); r:=q+transform_node_size;
+repeat r:=r-2;
+if type(r)<>known then return;
+until r=q;
+txx:=value(xx_part_loc(q));
+txy:=value(xy_part_loc(q));
+tyx:=value(yx_part_loc(q));
+tyy:=value(yy_part_loc(q));
+tx:=value(x_part_loc(q));
+ty:=value(y_part_loc(q));
+flush_cur_exp(0)
+
+@ @<For each of the eight cases...@>=
+rotated_by:if type(p)=known then
+ @<Install sines and cosines, then |goto done|@>;
+slanted_by:if type(p)>pair_type then
+ begin install(xy_part_loc(q),p); goto done;
+ end;
+scaled_by:if type(p)>pair_type then
+ begin install(xx_part_loc(q),p); install(yy_part_loc(q),p); goto done;
+ end;
+shifted_by:if type(p)=pair_type then
+ begin r:=value(p); install(x_part_loc(q),x_part_loc(r));
+ install(y_part_loc(q),y_part_loc(r)); goto done;
+ end;
+x_scaled:if type(p)>pair_type then
+ begin install(xx_part_loc(q),p); goto done;
+ end;
+y_scaled:if type(p)>pair_type then
+ begin install(yy_part_loc(q),p); goto done;
+ end;
+z_scaled:if type(p)=pair_type then
+ @<Install a complex multiplier, then |goto done|@>;
+transformed_by:do_nothing;
+
+@ @<Install sines and cosines, then |goto done|@>=
+begin n_sin_cos((value(p) mod three_sixty_units)*16);
+value(xx_part_loc(q)):=round_fraction(n_cos);
+value(yx_part_loc(q)):=round_fraction(n_sin);
+value(xy_part_loc(q)):=-value(yx_part_loc(q));
+value(yy_part_loc(q)):=value(xx_part_loc(q));
+goto done;
+end
+
+@ @<Install a complex multiplier, then |goto done|@>=
+begin r:=value(p);
+install(xx_part_loc(q),x_part_loc(r));
+install(yy_part_loc(q),x_part_loc(r));
+install(yx_part_loc(q),y_part_loc(r));
+if type(y_part_loc(r))=known then negate(value(y_part_loc(r)))
+else negate_dep_list(dep_list(y_part_loc(r)));
+install(xy_part_loc(q),y_part_loc(r));
+goto done;
+end
+
+@ Procedure |set_up_known_trans| is like |set_up_trans|, but it
+insists that the transformation be entirely known.
+
+@<Declare binary action...@>=
+procedure set_up_known_trans(@!c:quarterword);
+begin set_up_trans(c);
+if cur_type<>known then
+ begin exp_err("Transform components aren't all known");
+@.Transform components...@>
+ help3("I'm unable to apply a partially specified transformation")@/
+ ("except to a fully known pair or transform.")@/
+ ("Proceed, and I'll omit the transformation.");
+ put_get_flush_error(0);
+ txx:=unity; txy:=0; tyx:=0; tyy:=unity; tx:=0; ty:=0;
+ end;
+end;
+
+@ Here's a procedure that applies the transform |txx..ty| to a pair of
+coordinates in locations |p| and~|q|.
+
+@<Declare binary action...@>=
+procedure trans(@!p,@!q:pointer);
+var @!v:scaled; {the new |x| value}
+begin v:=take_scaled(mem[p].sc,txx)+take_scaled(mem[q].sc,txy)+tx;
+mem[q].sc:=take_scaled(mem[p].sc,tyx)+take_scaled(mem[q].sc,tyy)+ty;
+mem[p].sc:=v;
+end;
+
+@ The simplest transformation procedure applies a transform to all
+coordinates of a path. The |null_pen| remains unchanged if it isn't
+being shifted.
+
+@<Declare binary action...@>=
+procedure path_trans(@!p:pointer;@!c:quarterword);
+label exit;
+var @!q:pointer; {list traverser}
+begin set_up_known_trans(c); unstash_cur_exp(p);
+if cur_type=pen_type then
+ begin if max_offset(cur_exp)=0 then if tx=0 then if ty=0 then return;
+ flush_cur_exp(make_path(cur_exp)); cur_type:=future_pen;
+ end;
+q:=cur_exp;
+repeat if left_type(q)<>endpoint then
+ trans(q+3,q+4); {that's |left_x| and |left_y|}
+trans(q+1,q+2); {that's |x_coord| and |y_coord|}
+if right_type(q)<>endpoint then
+ trans(q+5,q+6); {that's |right_x| and |right_y|}
+q:=link(q);
+until q=cur_exp;
+exit:end;
+
+@ The next simplest transformation procedure applies to edges.
+It is simple primarily because \MF\ doesn't allow very general
+transformations to be made, and because the tricky subroutines
+for edge transformation have already been written.
+
+@<Declare binary action...@>=
+procedure edges_trans(@!p:pointer;@!c:quarterword);
+label exit;
+begin set_up_known_trans(c); unstash_cur_exp(p); cur_edges:=cur_exp;
+if empty_edges(cur_edges) then return; {the empty set is easy to transform}
+if txx=0 then if tyy=0 then
+ if txy mod unity=0 then if tyx mod unity=0 then
+ begin xy_swap_edges; txx:=txy; tyy:=tyx; txy:=0; tyx:=0;
+ if empty_edges(cur_edges) then return;
+ end;
+if txy=0 then if tyx=0 then
+ if txx mod unity=0 then if tyy mod unity=0 then
+ @<Scale the edges, shift them, and |return|@>;
+print_err("That transformation is too hard");
+@.That transformation...@>
+help3("I can apply complicated transformations to paths,")@/
+ ("but I can only do integer operations on pictures.")@/
+ ("Proceed, and I'll omit the transformation.");
+put_get_error;
+exit:end;
+
+@ @<Scale the edges, shift them, and |return|@>=
+begin if (txx=0)or(tyy=0) then
+ begin toss_edges(cur_edges);
+ cur_exp:=get_node(edge_header_size); init_edges(cur_exp);
+ end
+else begin if txx<0 then
+ begin x_reflect_edges; txx:=-txx;
+ end;
+ if tyy<0 then
+ begin y_reflect_edges; tyy:=-tyy;
+ end;
+ if txx<>unity then x_scale_edges(txx div unity);
+ if tyy<>unity then y_scale_edges(tyy div unity);
+ @<Shift the edges by |(tx,ty)|, rounded@>;
+ end;
+return;
+end
+
+@ @<Shift the edges...@>=
+tx:=round_unscaled(tx); ty:=round_unscaled(ty);
+if (m_min(cur_edges)+tx<=0)or(m_max(cur_edges)+tx>=8192)or@|
+ (n_min(cur_edges)+ty<=0)or(n_max(cur_edges)+ty>=8191)or@|
+ (abs(tx)>=4096)or(abs(ty)>=4096) then
+ begin print_err("Too far to shift");
+@.Too far to shift@>
+ help3("I can't shift the picture as requested---it would")@/
+ ("make some coordinates too large or too small.")@/
+ ("Proceed, and I'll omit the transformation.");
+ put_get_error;
+ end
+else begin if tx<>0 then
+ begin if not valid_range(m_offset(cur_edges)-tx) then fix_offset;
+ m_min(cur_edges):=m_min(cur_edges)+tx;
+ m_max(cur_edges):=m_max(cur_edges)+tx;
+ m_offset(cur_edges):=m_offset(cur_edges)-tx;
+ last_window_time(cur_edges):=0;
+ end;
+ if ty<>0 then
+ begin n_min(cur_edges):=n_min(cur_edges)+ty;
+ n_max(cur_edges):=n_max(cur_edges)+ty;
+ n_pos(cur_edges):=n_pos(cur_edges)+ty;
+ last_window_time(cur_edges):=0;
+ end;
+ end
+
+@ The hard cases of transformation occur when big nodes are involved,
+and when some of their components are unknown.
+
+@<Declare binary action...@>=
+@t\4@>@<Declare subroutines needed by |big_trans|@>@;
+procedure big_trans(@!p:pointer;@!c:quarterword);
+label exit;
+var @!q,@!r,@!pp,@!qq:pointer; {list manipulation registers}
+@!s:small_number; {size of a big node}
+begin s:=big_node_size[type(p)]; q:=value(p); r:=q+s;
+repeat r:=r-2;
+if type(r)<>known then @<Transform an unknown big node and |return|@>;
+until r=q;
+@<Transform a known big node@>;
+exit:end; {node |p| will now be recycled by |do_binary|}
+
+@ @<Transform an unknown big node and |return|@>=
+begin set_up_known_trans(c); make_exp_copy(p); r:=value(cur_exp);
+if cur_type=transform_type then
+ begin bilin1(yy_part_loc(r),tyy,xy_part_loc(q),tyx,0);
+ bilin1(yx_part_loc(r),tyy,xx_part_loc(q),tyx,0);
+ bilin1(xy_part_loc(r),txx,yy_part_loc(q),txy,0);
+ bilin1(xx_part_loc(r),txx,yx_part_loc(q),txy,0);
+ end;
+bilin1(y_part_loc(r),tyy,x_part_loc(q),tyx,ty);
+bilin1(x_part_loc(r),txx,y_part_loc(q),txy,tx);
+return;
+end
+
+@ Let |p| point to a two-word value field inside a big node of |cur_exp|,
+and let |q| point to a another value field. The |bilin1| procedure
+replaces |p| by $p\cdot t+q\cdot u+\delta$.
+
+@<Declare subroutines needed by |big_trans|@>=
+procedure bilin1(@!p:pointer;@!t:scaled;@!q:pointer;@!u,@!delta:scaled);
+var @!r:pointer; {list traverser}
+begin if t<>unity then dep_mult(p,t,true);
+if u<>0 then
+ if type(q)=known then delta:=delta+take_scaled(value(q),u)
+ else begin @<Ensure that |type(p)=proto_dependent|@>;
+ dep_list(p):=p_plus_fq(dep_list(p),u,dep_list(q),proto_dependent,type(q));
+ end;
+if type(p)=known then value(p):=value(p)+delta
+else begin r:=dep_list(p);
+ while info(r)<>null do r:=link(r);
+ delta:=value(r)+delta;
+ if r<>dep_list(p) then value(r):=delta
+ else begin recycle_value(p); type(p):=known; value(p):=delta;
+ end;
+ end;
+if fix_needed then fix_dependencies;
+end;
+
+@ @<Ensure that |type(p)=proto_dependent|@>=
+if type(p)<>proto_dependent then
+ begin if type(p)=known then new_dep(p,const_dependency(value(p)))
+ else dep_list(p):=p_times_v(dep_list(p),unity,dependent,proto_dependent,true);
+ type(p):=proto_dependent;
+ end
+
+@ @<Transform a known big node@>=
+set_up_trans(c);
+if cur_type=known then @<Transform known by known@>
+else begin pp:=stash_cur_exp; qq:=value(pp);
+ make_exp_copy(p); r:=value(cur_exp);
+ if cur_type=transform_type then
+ begin bilin2(yy_part_loc(r),yy_part_loc(qq),
+ value(xy_part_loc(q)),yx_part_loc(qq),null);
+ bilin2(yx_part_loc(r),yy_part_loc(qq),
+ value(xx_part_loc(q)),yx_part_loc(qq),null);
+ bilin2(xy_part_loc(r),xx_part_loc(qq),
+ value(yy_part_loc(q)),xy_part_loc(qq),null);
+ bilin2(xx_part_loc(r),xx_part_loc(qq),
+ value(yx_part_loc(q)),xy_part_loc(qq),null);
+ end;
+ bilin2(y_part_loc(r),yy_part_loc(qq),
+ value(x_part_loc(q)),yx_part_loc(qq),y_part_loc(qq));
+ bilin2(x_part_loc(r),xx_part_loc(qq),
+ value(y_part_loc(q)),xy_part_loc(qq),x_part_loc(qq));
+ recycle_value(pp); free_node(pp,value_node_size);
+ end;
+
+@ Let |p| be a |proto_dependent| value whose dependency list ends
+at |dep_final|. The following procedure adds |v| times another
+numeric quantity to~|p|.
+
+@<Declare subroutines needed by |big_trans|@>=
+procedure add_mult_dep(@!p:pointer;@!v:scaled;@!r:pointer);
+begin if type(r)=known then
+ value(dep_final):=value(dep_final)+take_scaled(value(r),v)
+else begin dep_list(p):=
+ p_plus_fq(dep_list(p),v,dep_list(r),proto_dependent,type(r));
+ if fix_needed then fix_dependencies;
+ end;
+end;
+
+@ The |bilin2| procedure is something like |bilin1|, but with known
+and unknown quantities reversed. Parameter |p| points to a value field
+within the big node for |cur_exp|; and |type(p)=known|. Parameters
+|t| and~|u| point to value fields elsewhere; so does parameter~|q|,
+unless it is |null| (which stands for zero). Location~|p| will be
+replaced by $p\cdot t+v\cdot u+q$.
+
+@<Declare subroutines needed by |big_trans|@>=
+procedure bilin2(@!p,@!t:pointer;@!v:scaled;@!u,@!q:pointer);
+var @!vv:scaled; {temporary storage for |value(p)|}
+begin vv:=value(p); type(p):=proto_dependent;
+new_dep(p,const_dependency(0)); {this sets |dep_final|}
+if vv<>0 then add_mult_dep(p,vv,t); {|dep_final| doesn't change}
+if v<>0 then add_mult_dep(p,v,u);
+if q<>null then add_mult_dep(p,unity,q);
+if dep_list(p)=dep_final then
+ begin vv:=value(dep_final); recycle_value(p);
+ type(p):=known; value(p):=vv;
+ end;
+end;
+
+@ @<Transform known by known@>=
+begin make_exp_copy(p); r:=value(cur_exp);
+if cur_type=transform_type then
+ begin bilin3(yy_part_loc(r),tyy,value(xy_part_loc(q)),tyx,0);
+ bilin3(yx_part_loc(r),tyy,value(xx_part_loc(q)),tyx,0);
+ bilin3(xy_part_loc(r),txx,value(yy_part_loc(q)),txy,0);
+ bilin3(xx_part_loc(r),txx,value(yx_part_loc(q)),txy,0);
+ end;
+bilin3(y_part_loc(r),tyy,value(x_part_loc(q)),tyx,ty);
+bilin3(x_part_loc(r),txx,value(y_part_loc(q)),txy,tx);
+end
+
+@ Finally, in |bilin3| everything is |known|.
+
+@<Declare subroutines needed by |big_trans|@>=
+procedure bilin3(@!p:pointer;@!t,@!v,@!u,@!delta:scaled);
+begin if t<>unity then delta:=delta+take_scaled(value(p),t)
+else delta:=delta+value(p);
+if u<>0 then value(p):=delta+take_scaled(v,u)
+else value(p):=delta;
+end;
+
+@ @<Additional cases of binary operators@>=
+concatenate: if (cur_type=string_type)and(type(p)=string_type) then cat(p)
+ else bad_binary(p,concatenate);
+substring_of: if nice_pair(p,type(p))and(cur_type=string_type) then
+ chop_string(value(p))
+ else bad_binary(p,substring_of);
+subpath_of: begin if cur_type=pair_type then pair_to_path;
+ if nice_pair(p,type(p))and(cur_type=path_type) then
+ chop_path(value(p))
+ else bad_binary(p,subpath_of);
+ end;
+
+@ @<Declare binary action...@>=
+procedure cat(@!p:pointer);
+var @!a,@!b:str_number; {the strings being concatenated}
+@!k:pool_pointer; {index into |str_pool|}
+begin a:=value(p); b:=cur_exp; str_room(length(a)+length(b));
+for k:=str_start[a] to str_start[a+1]-1 do append_char(so(str_pool[k]));
+for k:=str_start[b] to str_start[b+1]-1 do append_char(so(str_pool[k]));
+cur_exp:=make_string; delete_str_ref(b);
+end;
+
+@ @<Declare binary action...@>=
+procedure chop_string(@!p:pointer);
+var @!a,@!b:integer; {start and stop points}
+@!l:integer; {length of the original string}
+@!k:integer; {runs from |a| to |b|}
+@!s:str_number; {the original string}
+@!reversed:boolean; {was |a>b|?}
+begin a:=round_unscaled(value(x_part_loc(p)));
+b:=round_unscaled(value(y_part_loc(p)));
+if a<=b then reversed:=false
+else begin reversed:=true; k:=a; a:=b; b:=k;
+ end;
+s:=cur_exp; l:=length(s);
+if a<0 then
+ begin a:=0;
+ if b<0 then b:=0;
+ end;
+if b>l then
+ begin b:=l;
+ if a>l then a:=l;
+ end;
+str_room(b-a);
+if reversed then
+ for k:=str_start[s]+b-1 downto str_start[s]+a do append_char(so(str_pool[k]))
+else for k:=str_start[s]+a to str_start[s]+b-1 do append_char(so(str_pool[k]));
+cur_exp:=make_string; delete_str_ref(s);
+end;
+
+@ @<Declare binary action...@>=
+procedure chop_path(@!p:pointer);
+var @!q:pointer; {a knot in the original path}
+@!pp,@!qq,@!rr,@!ss:pointer; {link variables for copies of path nodes}
+@!a,@!b,@!k,@!l:scaled; {indices for chopping}
+@!reversed:boolean; {was |a>b|?}
+begin l:=path_length; a:=value(x_part_loc(p)); b:=value(y_part_loc(p));
+if a<=b then reversed:=false
+else begin reversed:=true; k:=a; a:=b; b:=k;
+ end;
+@<Dispense with the cases |a<0| and/or |b>l|@>;
+q:=cur_exp;
+while a>=unity do
+ begin q:=link(q); a:=a-unity; b:=b-unity;
+ end;
+if b=a then @<Construct a path from |pp| to |qq| of length zero@>
+else @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>;
+left_type(pp):=endpoint; right_type(qq):=endpoint; link(qq):=pp;
+toss_knot_list(cur_exp);
+if reversed then
+ begin cur_exp:=link(htap_ypoc(pp)); toss_knot_list(pp);
+ end
+else cur_exp:=pp;
+end;
+
+@ @<Dispense with the cases |a<0| and/or |b>l|@>=
+if a<0 then
+ if left_type(cur_exp)=endpoint then
+ begin a:=0; if b<0 then b:=0;
+ end
+ else repeat a:=a+l; b:=b+l;
+ until a>=0; {a cycle always has length |l>0|}
+if b>l then if left_type(cur_exp)=endpoint then
+ begin b:=l; if a>l then a:=l;
+ end
+ else while a>=l do
+ begin a:=a-l; b:=b-l;
+ end
+
+@ @<Construct a path from |pp| to |qq| of length $\lceil b\rceil$@>=
+begin pp:=copy_knot(q); qq:=pp;
+repeat q:=link(q); rr:=qq; qq:=copy_knot(q); link(rr):=qq; b:=b-unity;
+until b<=0;
+if a>0 then
+ begin ss:=pp; pp:=link(pp);
+ split_cubic(ss,a*@'10000,x_coord(pp),y_coord(pp)); pp:=link(ss);
+ free_node(ss,knot_node_size);
+ if rr=ss then
+ begin b:=make_scaled(b,unity-a); rr:=pp;
+ end;
+ end;
+if b<0 then
+ begin split_cubic(rr,(b+unity)*@'10000,x_coord(qq),y_coord(qq));
+ free_node(qq,knot_node_size);
+ qq:=link(rr);
+ end;
+end
+
+@ @<Construct a path from |pp| to |qq| of length zero@>=
+begin if a>0 then
+ begin qq:=link(q);
+ split_cubic(q,a*@'10000,x_coord(qq),y_coord(qq)); q:=link(q);
+ end;
+pp:=copy_knot(q); qq:=pp;
+end
+
+@ The |pair_value| routine changes the current expression to a
+given ordered pair of values.
+
+@<Declare binary action...@>=
+procedure pair_value(@!x,@!y:scaled);
+var @!p:pointer; {a pair node}
+begin p:=get_node(value_node_size); flush_cur_exp(p); cur_type:=pair_type;
+type(p):=pair_type; name_type(p):=capsule; init_big_node(p);
+p:=value(p);@/
+type(x_part_loc(p)):=known; value(x_part_loc(p)):=x;@/
+type(y_part_loc(p)):=known; value(y_part_loc(p)):=y;@/
+end;
+
+@ @<Additional cases of binary operators@>=
+point_of,precontrol_of,postcontrol_of: begin if cur_type=pair_type then
+ pair_to_path;
+ if (cur_type=path_type)and(type(p)=known) then
+ find_point(value(p),c)
+ else bad_binary(p,c);
+ end;
+pen_offset_of: begin if cur_type=future_pen then materialize_pen;
+ if (cur_type=pen_type)and nice_pair(p,type(p)) then
+ set_up_offset(value(p))
+ else bad_binary(p,pen_offset_of);
+ end;
+direction_time_of: begin if cur_type=pair_type then pair_to_path;
+ if (cur_type=path_type)and nice_pair(p,type(p)) then
+ set_up_direction_time(value(p))
+ else bad_binary(p,direction_time_of);
+ end;
+
+@ @<Declare binary action...@>=
+procedure set_up_offset(@!p:pointer);
+begin find_offset(value(x_part_loc(p)),value(y_part_loc(p)),cur_exp);
+pair_value(cur_x,cur_y);
+end;
+@#
+procedure set_up_direction_time(@!p:pointer);
+begin flush_cur_exp(find_direction_time(value(x_part_loc(p)),
+ value(y_part_loc(p)),cur_exp));
+end;
+
+@ @<Declare binary action...@>=
+procedure find_point(@!v:scaled;@!c:quarterword);
+var @!p:pointer; {the path}
+@!n:scaled; {its length}
+@!q:pointer; {successor of |p|}
+begin p:=cur_exp;@/
+if left_type(p)=endpoint then n:=-unity@+else n:=0;
+repeat p:=link(p); n:=n+unity;
+until p=cur_exp;
+if n=0 then v:=0
+else if v<0 then
+ if left_type(p)=endpoint then v:=0
+ else v:=n-1-((-v-1) mod n)
+else if v>n then
+ if left_type(p)=endpoint then v:=n
+ else v:=v mod n;
+p:=cur_exp;
+while v>=unity do
+ begin p:=link(p); v:=v-unity;
+ end;
+if v<>0 then @<Insert a fractional node by splitting the cubic@>;
+@<Set the current expression to the desired path coordinates@>;
+end;
+
+@ @<Insert a fractional node...@>=
+begin q:=link(p); split_cubic(p,v*@'10000,x_coord(q),y_coord(q)); p:=link(p);
+end
+
+@ @<Set the current expression to the desired path coordinates...@>=
+case c of
+point_of: pair_value(x_coord(p),y_coord(p));
+precontrol_of: if left_type(p)=endpoint then pair_value(x_coord(p),y_coord(p))
+ else pair_value(left_x(p),left_y(p));
+postcontrol_of: if right_type(p)=endpoint then pair_value(x_coord(p),y_coord(p))
+ else pair_value(right_x(p),right_y(p));
+end {there are no other cases}
+
+@ @<Additional cases of bin...@>=
+intersect: begin if type(p)=pair_type then
+ begin q:=stash_cur_exp; unstash_cur_exp(p);
+ pair_to_path; p:=stash_cur_exp; unstash_cur_exp(q);
+ end;
+ if cur_type=pair_type then pair_to_path;
+ if (cur_type=path_type)and(type(p)=path_type) then
+ begin path_intersection(value(p),cur_exp);
+ pair_value(cur_t,cur_tt);
+ end
+ else bad_binary(p,intersect);
+ end;
+
+@* \[43] Statements and commands.
+The chief executive of \MF\ is the |do_statement| routine, which
+contains the master switch that causes all the various pieces of \MF\
+to do their things, in the right order.
+
+In a sense, this is the grand climax of the program: It applies all the
+tools that we have worked so hard to construct. In another sense, this is
+the messiest part of the program: It necessarily refers to other pieces
+of code all over the place, so that a person can't fully understand what is
+going on without paging back and forth to be reminded of conventions that
+are defined elsewhere. We are now at the hub of the web.
+
+The structure of |do_statement| itself is quite simple. The first token
+of the statement is fetched using |get_x_next|. If it can be the first
+token of an expression, we look for an equation, an assignment, or a
+title. Otherwise we use a \&{case} construction to branch at high speed to
+the appropriate routine for various and sundry other types of commands,
+each of which has an ``action procedure'' that does the necessary work.
+
+The program uses the fact that
+$$\hbox{|min_primary_command=max_statement_command=type_name|}$$
+to interpret a statement that starts with, e.g., `\&{string}',
+as a type declaration rather than a boolean expression.
+
+@p @t\4@>@<Declare generic font output procedures@>@;
+@t\4@>@<Declare action procedures for use by |do_statement|@>@;
+procedure do_statement; {governs \MF's activities}
+begin cur_type:=vacuous; get_x_next;
+if cur_cmd>max_primary_command then @<Worry about bad statement@>
+else if cur_cmd>max_statement_command then
+ @<Do an equation, assignment, title, or
+ `$\langle\,$expression$\,\rangle\,$\&{endgroup}'@>
+else @<Do a statement that doesn't begin with an expression@>;
+if cur_cmd<semicolon then
+ @<Flush unparsable junk that was found after the statement@>;
+error_count:=0;
+end;
+
+@ The only command codes |>max_primary_command| that can be present
+at the beginning of a statement are |semicolon| and higher; these
+occur when the statement is null.
+
+@<Worry about bad statement@>=
+begin if cur_cmd<semicolon then
+ begin print_err("A statement can't begin with `");
+@.A statement can't begin with x@>
+ print_cmd_mod(cur_cmd,cur_mod); print_char("'");
+ help5("I was looking for the beginning of a new statement.")@/
+ ("If you just proceed without changing anything, I'll ignore")@/
+ ("everything up to the next `;'. Please insert a semicolon")@/
+ ("now in front of anything that you don't want me to delete.")@/
+ ("(See Chapter 27 of The METAFONTbook for an example.)");@/
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+ back_error; get_x_next;
+ end;
+end
+
+@ The help message printed here says that everything is flushed up to
+a semicolon, but actually the commands |end_group| and |stop| will
+also terminate a statement.
+
+@<Flush unparsable junk that was found after the statement@>=
+begin print_err("Extra tokens will be flushed");
+@.Extra tokens will be flushed@>
+help6("I've just read as much of that statement as I could fathom,")@/
+("so a semicolon should have been next. It's very puzzling...")@/
+("but I'll try to get myself back together, by ignoring")@/
+("everything up to the next `;'. Please insert a semicolon")@/
+("now in front of anything that you don't want me to delete.")@/
+("(See Chapter 27 of The METAFONTbook for an example.)");@/
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+back_error; scanner_status:=flushing;
+repeat get_next;
+@<Decrease the string reference count...@>;
+until end_of_statement; {|cur_cmd=semicolon|, |end_group|, or |stop|}
+scanner_status:=normal;
+end
+
+@ If |do_statement| ends with |cur_cmd=end_group|, we should have
+|cur_type=vacuous| unless the statement was simply an expression;
+in the latter case, |cur_type| and |cur_exp| should represent that
+expression.
+
+@<Do a statement that doesn't...@>=
+begin if internal[tracing_commands]>0 then show_cur_cmd_mod;
+case cur_cmd of
+type_name:do_type_declaration;
+macro_def:if cur_mod>var_def then make_op_def
+ else if cur_mod>end_def then scan_def;
+@t\4@>@<Cases of |do_statement| that invoke particular commands@>@;
+end; {there are no other cases}
+cur_type:=vacuous;
+end
+
+@ The most important statements begin with expressions.
+
+@<Do an equation, assignment, title, or...@>=
+begin var_flag:=assignment; scan_expression;
+if cur_cmd<end_group then
+ begin if cur_cmd=equals then do_equation
+ else if cur_cmd=assignment then do_assignment
+ else if cur_type=string_type then @<Do a title@>
+ else if cur_type<>vacuous then
+ begin exp_err("Isolated expression");
+@.Isolated expression@>
+ help3("I couldn't find an `=' or `:=' after the")@/
+ ("expression that is shown above this error message,")@/
+ ("so I guess I'll just ignore it and carry on.");
+ put_get_error;
+ end;
+ flush_cur_exp(0); cur_type:=vacuous;
+ end;
+end
+
+@ @<Do a title@>=
+begin if internal[tracing_titles]>0 then
+ begin print_nl(""); slow_print(cur_exp); update_terminal;
+ end;
+if internal[proofing]>0 then
+ @<Send the current expression as a title to the output file@>;
+end
+
+@ Equations and assignments are performed by the pair of mutually recursive
+@^recursion@>
+routines |do_equation| and |do_assignment|. These routines are called when
+|cur_cmd=equals| and when |cur_cmd=assignment|, respectively; the left-hand
+side is in |cur_type| and |cur_exp|, while the right-hand side is yet
+to be scanned. After the routines are finished, |cur_type| and |cur_exp|
+will be equal to the right-hand side (which will normally be equal
+to the left-hand side).
+
+@<Declare action procedures for use by |do_statement|@>=
+@t\4@>@<Declare the procedure called |try_eq|@>@;
+@t\4@>@<Declare the procedure called |make_eq|@>@;
+procedure@?do_assignment; forward;@t\2@>@/
+procedure do_equation;
+var @!lhs:pointer; {capsule for the left-hand side}
+@!p:pointer; {temporary register}
+begin lhs:=stash_cur_exp; get_x_next; var_flag:=assignment; scan_expression;
+if cur_cmd=equals then do_equation
+else if cur_cmd=assignment then do_assignment;
+if internal[tracing_commands]>two then @<Trace the current equation@>;
+if cur_type=unknown_path then if type(lhs)=pair_type then
+ begin p:=stash_cur_exp; unstash_cur_exp(lhs); lhs:=p;
+ end; {in this case |make_eq| will change the pair to a path}
+make_eq(lhs); {equate |lhs| to |(cur_type,cur_exp)|}
+end;
+
+@ And |do_assignment| is similar to |do_equation|:
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure do_assignment;
+var @!lhs:pointer; {token list for the left-hand side}
+@!p:pointer; {where the left-hand value is stored}
+@!q:pointer; {temporary capsule for the right-hand value}
+begin if cur_type<>token_list then
+ begin exp_err("Improper `:=' will be changed to `='");
+@.Improper `:='@>
+ help2("I didn't find a variable name at the left of the `:=',")@/
+ ("so I'm going to pretend that you said `=' instead.");@/
+ error; do_equation;
+ end
+else begin lhs:=cur_exp; cur_type:=vacuous;@/
+ get_x_next; var_flag:=assignment; scan_expression;
+ if cur_cmd=equals then do_equation
+ else if cur_cmd=assignment then do_assignment;
+ if internal[tracing_commands]>two then @<Trace the current assignment@>;
+ if info(lhs)>hash_end then
+ @<Assign the current expression to an internal variable@>
+ else @<Assign the current expression to the variable |lhs|@>;
+ flush_node_list(lhs);
+ end;
+end;
+
+@ @<Trace the current equation@>=
+begin begin_diagnostic; print_nl("{("); print_exp(lhs,0);
+print(")=("); print_exp(null,0); print(")}"); end_diagnostic(false);
+end
+
+@ @<Trace the current assignment@>=
+begin begin_diagnostic; print_nl("{");
+if info(lhs)>hash_end then slow_print(int_name[info(lhs)-(hash_end)])
+else show_token_list(lhs,null,1000,0);
+print(":="); print_exp(null,0); print_char("}"); end_diagnostic(false);
+end
+
+@ @<Assign the current expression to an internal variable@>=
+if cur_type=known then internal[info(lhs)-(hash_end)]:=cur_exp
+else begin exp_err("Internal quantity `");
+@.Internal quantity...@>
+ slow_print(int_name[info(lhs)-(hash_end)]);
+ print("' must receive a known value");
+ help2("I can't set an internal quantity to anything but a known")@/
+ ("numeric value, so I'll have to ignore this assignment.");
+ put_get_error;
+ end
+
+@ @<Assign the current expression to the variable |lhs|@>=
+begin p:=find_variable(lhs);
+if p<>null then
+ begin q:=stash_cur_exp; cur_type:=und_type(p); recycle_value(p);
+ type(p):=cur_type; value(p):=null; make_exp_copy(p);
+ p:=stash_cur_exp; unstash_cur_exp(q); make_eq(p);
+ end
+else begin obliterated(lhs); put_get_error;
+ end;
+end
+
+
+@ And now we get to the nitty-gritty. The |make_eq| procedure is given
+a pointer to a capsule that is to be equated to the current expression.
+
+@<Declare the procedure called |make_eq|@>=
+procedure make_eq(@!lhs:pointer);
+label restart,done, not_found;
+var @!t:small_number; {type of the left-hand side}
+@!v:integer; {value of the left-hand side}
+@!p,@!q:pointer; {pointers inside of big nodes}
+begin restart: t:=type(lhs);
+if t<=pair_type then v:=value(lhs);
+case t of
+@t\4@>@<For each type |t|, make an equation and |goto done| unless |cur_type|
+ is incompatible with~|t|@>@;
+end; {all cases have been listed}
+@<Announce that the equation cannot be performed@>;
+done:check_arith; recycle_value(lhs); free_node(lhs,value_node_size);
+end;
+
+@ @<Announce that the equation cannot be performed@>=
+disp_err(lhs,""); exp_err("Equation cannot be performed (");
+@.Equation cannot be performed@>
+if type(lhs)<=pair_type then print_type(type(lhs))@+else print("numeric");
+print_char("=");
+if cur_type<=pair_type then print_type(cur_type)@+else print("numeric");
+print_char(")");@/
+help2("I'm sorry, but I don't know how to make such things equal.")@/
+ ("(See the two expressions just above the error message.)");
+put_get_error
+
+@ @<For each type |t|, make an equation and |goto done| unless...@>=
+boolean_type,string_type,pen_type,path_type,picture_type:
+ if cur_type=t+unknown_tag then
+ begin nonlinear_eq(v,cur_exp,false); unstash_cur_exp(cur_exp); goto done;
+ end
+ else if cur_type=t then
+ @<Report redundant or inconsistent equation and |goto done|@>;
+unknown_types:if cur_type=t-unknown_tag then
+ begin nonlinear_eq(cur_exp,lhs,true); goto done;
+ end
+ else if cur_type=t then
+ begin ring_merge(lhs,cur_exp); goto done;
+ end
+ else if cur_type=pair_type then if t=unknown_path then
+ begin pair_to_path; goto restart;
+ end;
+transform_type,pair_type:if cur_type=t then
+ @<Do multiple equations and |goto done|@>;
+known,dependent,proto_dependent,independent:if cur_type>=known then
+ begin try_eq(lhs,null); goto done;
+ end;
+vacuous:do_nothing;
+
+@ @<Report redundant or inconsistent equation and |goto done|@>=
+begin if cur_type<=string_type then
+ begin if cur_type=string_type then
+ begin if str_vs_str(v,cur_exp)<>0 then goto not_found;
+ end
+ else if v<>cur_exp then goto not_found;
+ @<Exclaim about a redundant equation@>; goto done;
+ end;
+print_err("Redundant or inconsistent equation");
+@.Redundant or inconsistent equation@>
+help2("An equation between already-known quantities can't help.")@/
+ ("But don't worry; continue and I'll just ignore it.");
+put_get_error; goto done;
+not_found: print_err("Inconsistent equation");
+@.Inconsistent equation@>
+help2("The equation I just read contradicts what was said before.")@/
+ ("But don't worry; continue and I'll just ignore it.");
+put_get_error; goto done;
+end
+
+@ @<Do multiple equations and |goto done|@>=
+begin p:=v+big_node_size[t]; q:=value(cur_exp)+big_node_size[t];
+repeat p:=p-2; q:=q-2; try_eq(p,q);
+until p=v;
+goto done;
+end
+
+@ The first argument to |try_eq| is the location of a value node
+in a capsule that will soon be recycled. The second argument is
+either a location within a pair or transform node pointed to by
+|cur_exp|, or it is |null| (which means that |cur_exp| itself
+serves as the second argument). The idea is to leave |cur_exp| unchanged,
+but to equate the two operands.
+
+@<Declare the procedure called |try_eq|@>=
+procedure try_eq(@!l,@!r:pointer);
+label done,done1;
+var @!p:pointer; {dependency list for right operand minus left operand}
+@!t:known..independent; {the type of list |p|}
+@!q:pointer; {the constant term of |p| is here}
+@!pp:pointer; {dependency list for right operand}
+@!tt:dependent..independent; {the type of list |pp|}
+@!copied:boolean; {have we copied a list that ought to be recycled?}
+begin @<Remove the left operand from its container, negate it, and
+ put it into dependency list~|p| with constant term~|q|@>;
+@<Add the right operand to list |p|@>;
+if info(p)=null then @<Deal with redundant or inconsistent equation@>
+else begin linear_eq(p,t);
+ if r=null then if cur_type<>known then if type(cur_exp)=known then
+ begin pp:=cur_exp; cur_exp:=value(cur_exp); cur_type:=known;
+ free_node(pp,value_node_size);
+ end;
+ end;
+end;
+
+@ @<Remove the left operand from its container, negate it, and...@>=
+t:=type(l);
+if t=known then
+ begin t:=dependent; p:=const_dependency(-value(l)); q:=p;
+ end
+else if t=independent then
+ begin t:=dependent; p:=single_dependency(l); negate(value(p));
+ q:=dep_final;
+ end
+else begin p:=dep_list(l); q:=p;
+ loop@+ begin negate(value(q));
+ if info(q)=null then goto done;
+ q:=link(q);
+ end;
+ done: link(prev_dep(l)):=link(q); prev_dep(link(q)):=prev_dep(l);
+ type(l):=known;
+ end
+
+@ @<Deal with redundant or inconsistent equation@>=
+begin if abs(value(p))>64 then {off by .001 or more}
+ begin print_err("Inconsistent equation");@/
+@.Inconsistent equation@>
+ print(" (off by "); print_scaled(value(p)); print_char(")");
+ help2("The equation I just read contradicts what was said before.")@/
+ ("But don't worry; continue and I'll just ignore it.");
+ put_get_error;
+ end
+else if r=null then @<Exclaim about a redundant equation@>;
+free_node(p,dep_node_size);
+end
+
+@ @<Add the right operand to list |p|@>=
+if r=null then
+ if cur_type=known then
+ begin value(q):=value(q)+cur_exp; goto done1;
+ end
+ else begin tt:=cur_type;
+ if tt=independent then pp:=single_dependency(cur_exp)
+ else pp:=dep_list(cur_exp);
+ end
+else if type(r)=known then
+ begin value(q):=value(q)+value(r); goto done1;
+ end
+ else begin tt:=type(r);
+ if tt=independent then pp:=single_dependency(r)
+ else pp:=dep_list(r);
+ end;
+if tt<>independent then copied:=false
+else begin copied:=true; tt:=dependent;
+ end;
+@<Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t|@>;
+if copied then flush_node_list(pp);
+done1:
+
+@ @<Add dependency list |pp| of type |tt| to dependency list~|p| of type~|t|@>=
+watch_coefs:=false;
+if t=tt then p:=p_plus_q(p,pp,t)
+else if t=proto_dependent then
+ p:=p_plus_fq(p,unity,pp,proto_dependent,dependent)
+else begin q:=p;
+ while info(q)<>null do
+ begin value(q):=round_fraction(value(q)); q:=link(q);
+ end;
+ t:=proto_dependent; p:=p_plus_q(p,pp,t);
+ end;
+watch_coefs:=true;
+
+@ Our next goal is to process type declarations. For this purpose it's
+convenient to have a procedure that scans a $\langle\,$declared
+variable$\,\rangle$ and returns the corresponding token list. After the
+following procedure has acted, the token after the declared variable
+will have been scanned, so it will appear in |cur_cmd|, |cur_mod|,
+and~|cur_sym|.
+
+@<Declare the function called |scan_declared_variable|@>=
+function scan_declared_variable:pointer;
+label done;
+var @!x:pointer; {hash address of the variable's root}
+@!h,@!t:pointer; {head and tail of the token list to be returned}
+@!l:pointer; {hash address of left bracket}
+begin get_symbol; x:=cur_sym;
+if cur_cmd<>tag_token then clear_symbol(x,false);
+h:=get_avail; info(h):=x; t:=h;@/
+loop@+ begin get_x_next;
+ if cur_sym=0 then goto done;
+ if cur_cmd<>tag_token then if cur_cmd<>internal_quantity then
+ if cur_cmd=left_bracket then @<Descend past a collective subscript@>
+ else goto done;
+ link(t):=get_avail; t:=link(t); info(t):=cur_sym;
+ end;
+done: if eq_type(x) mod outer_tag<>tag_token then clear_symbol(x,false);
+if equiv(x)=null then new_root(x);
+scan_declared_variable:=h;
+end;
+
+@ If the subscript isn't collective, we don't accept it as part of the
+declared variable.
+
+@<Descend past a collective subscript@>=
+begin l:=cur_sym; get_x_next;
+if cur_cmd<>right_bracket then
+ begin back_input; cur_sym:=l; cur_cmd:=left_bracket; goto done;
+ end
+else cur_sym:=collective_subscript;
+end
+
+@ Type declarations are introduced by the following primitive operations.
+
+@<Put each...@>=
+primitive("numeric",type_name,numeric_type);@/
+@!@:numeric_}{\&{numeric} primitive@>
+primitive("string",type_name,string_type);@/
+@!@:string_}{\&{string} primitive@>
+primitive("boolean",type_name,boolean_type);@/
+@!@:boolean_}{\&{boolean} primitive@>
+primitive("path",type_name,path_type);@/
+@!@:path_}{\&{path} primitive@>
+primitive("pen",type_name,pen_type);@/
+@!@:pen_}{\&{pen} primitive@>
+primitive("picture",type_name,picture_type);@/
+@!@:picture_}{\&{picture} primitive@>
+primitive("transform",type_name,transform_type);@/
+@!@:transform_}{\&{transform} primitive@>
+primitive("pair",type_name,pair_type);@/
+@!@:pair_}{\&{pair} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+type_name: print_type(m);
+
+@ Now we are ready to handle type declarations, assuming that a
+|type_name| has just been scanned.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure do_type_declaration;
+var @!t:small_number; {the type being declared}
+@!p:pointer; {token list for a declared variable}
+@!q:pointer; {value node for the variable}
+begin if cur_mod>=transform_type then t:=cur_mod@+else t:=cur_mod+unknown_tag;
+repeat p:=scan_declared_variable;
+flush_variable(equiv(info(p)),link(p),false);@/
+q:=find_variable(p);
+if q<>null then
+ begin type(q):=t; value(q):=null;
+ end
+else begin print_err("Declared variable conflicts with previous vardef");
+@.Declared variable conflicts...@>
+ help2("You can't use, e.g., `numeric foo[]' after `vardef foo'.")@/
+ ("Proceed, and I'll ignore the illegal redeclaration.");
+ put_get_error;
+ end;
+flush_list(p);
+if cur_cmd<comma then @<Flush spurious symbols after the declared variable@>;
+until end_of_statement;
+end;
+
+@ @<Flush spurious symbols after the declared variable@>=
+begin print_err("Illegal suffix of declared variable will be flushed");
+@.Illegal suffix...flushed@>
+help5("Variables in declarations must consist entirely of")@/
+ ("names and collective subscripts, e.g., `x[]a'.")@/
+ ("Are you trying to use a reserved word in a variable name?")@/
+ ("I'm going to discard the junk I found here,")@/
+ ("up to the next comma or the end of the declaration.");
+if cur_cmd=numeric_token then
+ help_line[2]:="Explicit subscripts like `x15a' aren't permitted.";
+put_get_error; scanner_status:=flushing;
+repeat get_next;
+@<Decrease the string reference count...@>;
+until cur_cmd>=comma; {either |end_of_statement| or |cur_cmd=comma|}
+scanner_status:=normal;
+end
+
+@ \MF's |main_control| procedure just calls |do_statement| repeatedly
+until coming to the end of the user's program.
+Each execution of |do_statement| concludes with
+|cur_cmd=semicolon|, |end_group|, or |stop|.
+
+@p procedure main_control;
+begin repeat do_statement;
+if cur_cmd=end_group then
+ begin print_err("Extra `endgroup'");
+@.Extra `endgroup'@>
+ help2("I'm not currently working on a `begingroup',")@/
+ ("so I had better not try to end anything.");
+ flush_error(0);
+ end;
+until cur_cmd=stop;
+end;
+
+@ @<Put each...@>=
+primitive("end",stop,0);@/
+@!@:end_}{\&{end} primitive@>
+primitive("dump",stop,1);@/
+@!@:dump_}{\&{dump} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+stop:if m=0 then print("end")@+else print("dump");
+
+@* \[44] Commands.
+Let's turn now to statements that are classified as ``commands'' because
+of their imperative nature. We'll begin with simple ones, so that it
+will be clear how to hook command processing into the |do_statement| routine;
+then we'll tackle the tougher commands.
+
+Here's one of the simplest:
+
+@<Cases of |do_statement|...@>=
+random_seed: do_random_seed;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_random_seed;
+begin get_x_next;
+if cur_cmd<>assignment then
+ begin missing_err(":=");
+@.Missing `:='@>
+ help1("Always say `randomseed:=<numeric expression>'.");
+ back_error;
+ end;
+get_x_next; scan_expression;
+if cur_type<>known then
+ begin exp_err("Unknown value will be ignored");
+@.Unknown value...ignored@>
+ help2("Your expression was too random for me to handle,")@/
+ ("so I won't change the random seed just now.");@/
+ put_get_flush_error(0);
+ end
+else @<Initialize the random seed to |cur_exp|@>;
+end;
+
+@ @<Initialize the random seed to |cur_exp|@>=
+begin init_randoms(cur_exp);
+if selector>=log_only then
+ begin old_setting:=selector; selector:=log_only;
+ print_nl("{randomseed:="); print_scaled(cur_exp); print_char("}");
+ print_nl(""); selector:=old_setting;
+ end;
+end
+
+@ And here's another simple one (somewhat different in flavor):
+
+@<Cases of |do_statement|...@>=
+mode_command: begin print_ln; interaction:=cur_mod;
+ @<Initialize the print |selector| based on |interaction|@>;
+ if log_opened then selector:=selector+2;
+ get_x_next;
+ end;
+
+@ @<Put each...@>=
+primitive("batchmode",mode_command,batch_mode);
+@!@:batch_mode_}{\&{batchmode} primitive@>
+primitive("nonstopmode",mode_command,nonstop_mode);
+@!@:nonstop_mode_}{\&{nonstopmode} primitive@>
+primitive("scrollmode",mode_command,scroll_mode);
+@!@:scroll_mode_}{\&{scrollmode} primitive@>
+primitive("errorstopmode",mode_command,error_stop_mode);
+@!@:error_stop_mode_}{\&{errorstopmode} primitive@>
+
+@ @<Cases of |print_cmd_mod|...@>=
+mode_command: case m of
+ batch_mode: print("batchmode");
+ nonstop_mode: print("nonstopmode");
+ scroll_mode: print("scrollmode");
+ othercases print("errorstopmode")
+ endcases;
+
+@ The `\&{inner}' and `\&{outer}' commands are only slightly harder.
+
+@<Cases of |do_statement|...@>=
+protection_command: do_protection;
+
+@ @<Put each...@>=
+primitive("inner",protection_command,0);@/
+@!@:inner_}{\&{inner} primitive@>
+primitive("outer",protection_command,1);@/
+@!@:outer_}{\&{outer} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+protection_command: if m=0 then print("inner")@+else print("outer");
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_protection;
+var @!m:0..1; {0 to unprotect, 1 to protect}
+@!t:halfword; {the |eq_type| before we change it}
+begin m:=cur_mod;
+repeat get_symbol; t:=eq_type(cur_sym);
+ if m=0 then
+ begin if t>=outer_tag then eq_type(cur_sym):=t-outer_tag;
+ end
+ else if t<outer_tag then eq_type(cur_sym):=t+outer_tag;
+ get_x_next;
+until cur_cmd<>comma;
+end;
+
+@ \MF\ never defines the tokens `\.(' and `\.)' to be primitives, but
+plain \MF\ begins with the declaration `\&{delimiters} \.{()}'. Such a
+declaration assigns the command code |left_delimiter| to `\.{(}' and
+|right_delimiter| to `\.{)}'; the |equiv| of each delimiter is the
+hash address of its mate.
+
+@<Cases of |do_statement|...@>=
+delimiters: def_delims;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure def_delims;
+var l_delim,r_delim:pointer; {the new delimiter pair}
+begin get_clear_symbol; l_delim:=cur_sym;@/
+get_clear_symbol; r_delim:=cur_sym;@/
+eq_type(l_delim):=left_delimiter; equiv(l_delim):=r_delim;@/
+eq_type(r_delim):=right_delimiter; equiv(r_delim):=l_delim;@/
+get_x_next;
+end;
+
+@ Here is a procedure that is called when \MF\ has reached a point
+where some right delimiter is mandatory.
+
+@<Declare the procedure called |check_delimiter|@>=
+procedure check_delimiter(@!l_delim,@!r_delim:pointer);
+label exit;
+begin if cur_cmd=right_delimiter then if cur_mod=l_delim then return;
+if cur_sym<>r_delim then
+ begin missing_err(text(r_delim));@/
+@.Missing `)'@>
+ help2("I found no right delimiter to match a left one. So I've")@/
+ ("put one in, behind the scenes; this may fix the problem.");
+ back_error;
+ end
+else begin print_err("The token `"); slow_print(text(r_delim));
+@.The token...delimiter@>
+ print("' is no longer a right delimiter");
+ help3("Strange: This token has lost its former meaning!")@/
+ ("I'll read it as a right delimiter this time;")@/
+ ("but watch out, I'll probably miss it later.");
+ error;
+ end;
+exit:end;
+
+@ The next four commands save or change the values associated with tokens.
+
+@<Cases of |do_statement|...@>=
+save_command: repeat get_symbol; save_variable(cur_sym); get_x_next;
+ until cur_cmd<>comma;
+interim_command: do_interim;
+let_command: do_let;
+new_internal: do_new_internal;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure@?do_statement; forward;@t\2@>@/
+procedure do_interim;
+begin get_x_next;
+if cur_cmd<>internal_quantity then
+ begin print_err("The token `");
+@.The token...quantity@>
+ if cur_sym=0 then print("(%CAPSULE)")
+ else slow_print(text(cur_sym));
+ print("' isn't an internal quantity");
+ help1("Something like `tracingonline' should follow `interim'.");
+ back_error;
+ end
+else begin save_internal(cur_mod); back_input;
+ end;
+do_statement;
+end;
+
+@ The following procedure is careful not to undefine the left-hand symbol
+too soon, lest commands like `{\tt let x=x}' have a surprising effect.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure do_let;
+var @!l:pointer; {hash location of the left-hand symbol}
+begin get_symbol; l:=cur_sym; get_x_next;
+if cur_cmd<>equals then if cur_cmd<>assignment then
+ begin missing_err("=");
+@.Missing `='@>
+ help3("You should have said `let symbol = something'.")@/
+ ("But don't worry; I'll pretend that an equals sign")@/
+ ("was present. The next token I read will be `something'.");
+ back_error;
+ end;
+get_symbol;
+case cur_cmd of
+defined_macro,secondary_primary_macro,tertiary_secondary_macro,
+ expression_tertiary_macro: add_mac_ref(cur_mod);
+othercases do_nothing
+endcases;@/
+clear_symbol(l,false); eq_type(l):=cur_cmd;
+if cur_cmd=tag_token then equiv(l):=null
+else equiv(l):=cur_mod;
+get_x_next;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_new_internal;
+begin repeat if int_ptr=max_internal then
+ overflow("number of internals",max_internal);
+@:METAFONT capacity exceeded number of int}{\quad number of internals@>
+get_clear_symbol; incr(int_ptr);
+eq_type(cur_sym):=internal_quantity; equiv(cur_sym):=int_ptr;
+int_name[int_ptr]:=text(cur_sym); internal[int_ptr]:=0;
+get_x_next;
+until cur_cmd<>comma;
+end;
+
+@ The various `\&{show}' commands are distinguished by modifier fields
+in the usual way.
+
+@d show_token_code=0 {show the meaning of a single token}
+@d show_stats_code=1 {show current memory and string usage}
+@d show_code=2 {show a list of expressions}
+@d show_var_code=3 {show a variable and its descendents}
+@d show_dependencies_code=4 {show dependent variables in terms of independents}
+
+@<Put each...@>=
+primitive("showtoken",show_command,show_token_code);@/
+@!@:show_token_}{\&{showtoken} primitive@>
+primitive("showstats",show_command,show_stats_code);@/
+@!@:show_stats_}{\&{showstats} primitive@>
+primitive("show",show_command,show_code);@/
+@!@:show_}{\&{show} primitive@>
+primitive("showvariable",show_command,show_var_code);@/
+@!@:show_var_}{\&{showvariable} primitive@>
+primitive("showdependencies",show_command,show_dependencies_code);@/
+@!@:show_dependencies_}{\&{showdependencies} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+show_command: case m of
+ show_token_code:print("showtoken");
+ show_stats_code:print("showstats");
+ show_code:print("show");
+ show_var_code:print("showvariable");
+ othercases print("showdependencies")
+ endcases;
+
+@ @<Cases of |do_statement|...@>=
+show_command:do_show_whatever;
+
+@ The value of |cur_mod| controls the |verbosity| in the |print_exp| routine:
+If it's |show_code|, complicated structures are abbreviated, otherwise
+they aren't.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure do_show;
+begin repeat get_x_next; scan_expression;
+print_nl(">> ");
+@.>>@>
+print_exp(null,2); flush_cur_exp(0);
+until cur_cmd<>comma;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure disp_token;
+begin print_nl("> ");
+@.>\relax@>
+if cur_sym=0 then @<Show a numeric or string or capsule token@>
+else begin slow_print(text(cur_sym)); print_char("=");
+ if eq_type(cur_sym)>=outer_tag then print("(outer) ");
+ print_cmd_mod(cur_cmd,cur_mod);
+ if cur_cmd=defined_macro then
+ begin print_ln; show_macro(cur_mod,null,100000);
+ end; {this avoids recursion between |show_macro| and |print_cmd_mod|}
+@^recursion@>
+ end;
+end;
+
+@ @<Show a numeric or string or capsule token@>=
+begin if cur_cmd=numeric_token then print_scaled(cur_mod)
+else if cur_cmd=capsule_token then
+ begin g_pointer:=cur_mod; print_capsule;
+ end
+else begin print_char(""""); slow_print(cur_mod); print_char("""");
+ delete_str_ref(cur_mod);
+ end;
+end
+
+@ The following cases of |print_cmd_mod| might arise in connection
+with |disp_token|, although they don't necessarily correspond to
+primitive tokens.
+
+@<Cases of |print_cmd_...@>=
+left_delimiter,right_delimiter: begin if c=left_delimiter then print("lef")
+ else print("righ");
+ print("t delimiter that matches "); slow_print(text(m));
+ end;
+tag_token:if m=null then print("tag")@+else print("variable");
+defined_macro: print("macro:");
+secondary_primary_macro,tertiary_secondary_macro,expression_tertiary_macro:
+ begin print_cmd_mod(macro_def,c); print("'d macro:");
+ print_ln; show_token_list(link(link(m)),null,1000,0);
+ end;
+repeat_loop:print("[repeat the loop]");
+internal_quantity:slow_print(int_name[m]);
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_show_token;
+begin repeat get_next; disp_token;
+get_x_next;
+until cur_cmd<>comma;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_show_stats;
+begin print_nl("Memory usage ");
+@.Memory usage...@>
+@!stat print_int(var_used); print_char("&"); print_int(dyn_used);
+if false then@+tats@t@>@;@/
+print("unknown");
+print(" ("); print_int(hi_mem_min-lo_mem_max-1);
+print(" still untouched)"); print_ln;
+print_nl("String usage ");
+print_int(str_ptr-init_str_ptr); print_char("&");
+print_int(pool_ptr-init_pool_ptr);
+print(" (");
+print_int(max_strings-max_str_ptr); print_char("&");
+print_int(pool_size-max_pool_ptr); print(" still untouched)"); print_ln;
+get_x_next;
+end;
+
+@ Here's a recursive procedure that gives an abbreviated account
+of a variable, for use by |do_show_var|.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure disp_var(@!p:pointer);
+var @!q:pointer; {traverses attributes and subscripts}
+@!n:0..max_print_line; {amount of macro text to show}
+begin if type(p)=structured then @<Descend the structure@>
+else if type(p)>=unsuffixed_macro then @<Display a variable macro@>
+else if type(p)<>undefined then
+ begin print_nl(""); print_variable_name(p); print_char("=");
+ print_exp(p,0);
+ end;
+end;
+
+@ @<Descend the structure@>=
+begin q:=attr_head(p);
+repeat disp_var(q); q:=link(q);
+until q=end_attr;
+q:=subscr_head(p);
+while name_type(q)=subscr do
+ begin disp_var(q); q:=link(q);
+ end;
+end
+
+@ @<Display a variable macro@>=
+begin print_nl(""); print_variable_name(p);
+if type(p)>unsuffixed_macro then print("@@#"); {|suffixed_macro|}
+print("=macro:");
+if file_offset>=max_print_line-20 then n:=5
+else n:=max_print_line-file_offset-15;
+show_macro(value(p),null,n);
+end
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_show_var;
+label done;
+begin repeat get_next;
+if cur_sym>0 then if cur_sym<=hash_end then
+ if cur_cmd=tag_token then if cur_mod<>null then
+ begin disp_var(cur_mod); goto done;
+ end;
+disp_token;
+done:get_x_next;
+until cur_cmd<>comma;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_show_dependencies;
+var @!p:pointer; {link that runs through all dependencies}
+begin p:=link(dep_head);
+while p<>dep_head do
+ begin if interesting(p) then
+ begin print_nl(""); print_variable_name(p);
+ if type(p)=dependent then print_char("=")
+ else print(" = "); {extra spaces imply proto-dependency}
+ print_dependency(dep_list(p),type(p));
+ end;
+ p:=dep_list(p);
+ while info(p)<>null do p:=link(p);
+ p:=link(p);
+ end;
+get_x_next;
+end;
+
+@ Finally we are ready for the procedure that governs all of the
+show commands.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure do_show_whatever;
+begin if interaction=error_stop_mode then wake_up_terminal;
+case cur_mod of
+show_token_code:do_show_token;
+show_stats_code:do_show_stats;
+show_code:do_show;
+show_var_code:do_show_var;
+show_dependencies_code:do_show_dependencies;
+end; {there are no other cases}
+if internal[showstopping]>0 then
+ begin print_err("OK");
+@.OK@>
+ if interaction<error_stop_mode then
+ begin help0; decr(error_count);
+ end
+ else help1("This isn't an error message; I'm just showing something.");
+ if cur_cmd=semicolon then error@+else put_get_error;
+ end;
+end;
+
+@ The `\&{addto}' command needs the following additional primitives:
+
+@d drop_code=0 {command modifier for `\&{dropping}'}
+@d keep_code=1 {command modifier for `\&{keeping}'}
+
+@<Put each...@>=
+primitive("contour",thing_to_add,contour_code);@/
+@!@:contour_}{\&{contour} primitive@>
+primitive("doublepath",thing_to_add,double_path_code);@/
+@!@:double_path_}{\&{doublepath} primitive@>
+primitive("also",thing_to_add,also_code);@/
+@!@:also_}{\&{also} primitive@>
+primitive("withpen",with_option,pen_type);@/
+@!@:with_pen_}{\&{withpen} primitive@>
+primitive("withweight",with_option,known);@/
+@!@:with_weight_}{\&{withweight} primitive@>
+primitive("dropping",cull_op,drop_code);@/
+@!@:dropping_}{\&{dropping} primitive@>
+primitive("keeping",cull_op,keep_code);@/
+@!@:keeping_}{\&{keeping} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+thing_to_add:if m=contour_code then print("contour")
+ else if m=double_path_code then print("doublepath")
+ else print("also");
+with_option:if m=pen_type then print("withpen")
+ else print("withweight");
+cull_op:if m=drop_code then print("dropping")
+ else print("keeping");
+
+@ @<Declare action procedures for use by |do_statement|@>=
+function scan_with:boolean;
+var @!t:small_number; {|known| or |pen_type|}
+@!result:boolean; {the value to return}
+begin t:=cur_mod; cur_type:=vacuous; get_x_next; scan_expression;
+result:=false;
+if cur_type<>t then @<Complain about improper type@>
+else if cur_type=pen_type then result:=true
+else @<Check the tentative weight@>;
+scan_with:=result;
+end;
+
+@ @<Complain about improper type@>=
+begin exp_err("Improper type");
+@.Improper type@>
+help2("Next time say `withweight <known numeric expression>';")@/
+ ("I'll ignore the bad `with' clause and look for another.");
+if t=pen_type then
+ help_line[1]:="Next time say `withpen <known pen expression>';";
+put_get_flush_error(0);
+end
+
+@ @<Check the tentative weight@>=
+begin cur_exp:=round_unscaled(cur_exp);
+if (abs(cur_exp)<4)and(cur_exp<>0) then result:=true
+else begin print_err("Weight must be -3, -2, -1, +1, +2, or +3");
+@.Weight must be...@>
+ help1("I'll ignore the bad `with' clause and look for another.");
+ put_get_flush_error(0);
+ end;
+end
+
+@ One of the things we need to do when we've parsed an \&{addto} or
+similar command is set |cur_edges| to the header of a supposed \&{picture}
+variable, given a token list for that variable.
+
+@<Declare action procedures for use by |do_statement|@>=
+procedure find_edges_var(@!t:pointer);
+var @!p:pointer;
+begin p:=find_variable(t); cur_edges:=null;
+if p=null then
+ begin obliterated(t); put_get_error;
+ end
+else if type(p)<>picture_type then
+ begin print_err("Variable "); show_token_list(t,null,1000,0);
+@.Variable x is the wrong type@>
+ print(" is the wrong type ("); print_type(type(p)); print_char(")");
+ help2("I was looking for a ""known"" picture variable.")@/
+ ("So I'll not change anything just now."); put_get_error;
+ end
+else cur_edges:=value(p);
+flush_node_list(t);
+end;
+
+@ @<Cases of |do_statement|...@>=
+add_to_command: do_add_to;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_add_to;
+label done, not_found;
+var @!lhs,@!rhs:pointer; {variable on left, path on right}
+@!w:integer; {tentative weight}
+@!p:pointer; {list manipulation register}
+@!q:pointer; {beginning of second half of doubled path}
+@!add_to_type:double_path_code..also_code; {modifier of \&{addto}}
+begin get_x_next; var_flag:=thing_to_add; scan_primary;
+if cur_type<>token_list then
+ @<Abandon edges command because there's no variable@>
+else begin lhs:=cur_exp; add_to_type:=cur_mod;@/
+ cur_type:=vacuous; get_x_next; scan_expression;
+ if add_to_type=also_code then @<Augment some edges by others@>
+ else @<Get ready to fill a contour, and fill it@>;
+ end;
+end;
+
+@ @<Abandon edges command because there's no variable@>=
+begin exp_err("Not a suitable variable");
+@.Not a suitable variable@>
+help4("At this point I needed to see the name of a picture variable.")@/
+ ("(Or perhaps you have indeed presented me with one; I might")@/
+ ("have missed it, if it wasn't followed by the proper token.)")@/
+ ("So I'll not change anything just now.");
+put_get_flush_error(0);
+end
+
+@ @<Augment some edges by others@>=
+begin find_edges_var(lhs);
+if cur_edges=null then flush_cur_exp(0)
+else if cur_type<>picture_type then
+ begin exp_err("Improper `addto'");
+@.Improper `addto'@>
+ help2("This expression should have specified a known picture.")@/
+ ("So I'll not change anything just now."); put_get_flush_error(0);
+ end
+else begin merge_edges(cur_exp); flush_cur_exp(0);
+ end;
+end
+
+@ @<Get ready to fill a contour...@>=
+begin if cur_type=pair_type then pair_to_path;
+if cur_type<>path_type then
+ begin exp_err("Improper `addto'");
+@.Improper `addto'@>
+ help2("This expression should have been a known path.")@/
+ ("So I'll not change anything just now.");
+ put_get_flush_error(0); flush_token_list(lhs);
+ end
+else begin rhs:=cur_exp; w:=1; cur_pen:=null_pen;
+ while cur_cmd=with_option do
+ if scan_with then
+ if cur_type=known then w:=cur_exp
+ else @<Change the tentative pen@>;
+ @<Complete the contour filling operation@>;
+ delete_pen_ref(cur_pen);
+ end;
+end
+
+@ We could say `|add_pen_ref(cur_pen)|; |flush_cur_exp(0)|' after changing
+|cur_pen| here. But that would have no effect, because the current expression
+will not be flushed. Thus we save a bit of code (at the risk of being too
+tricky).
+
+@<Change the tentative pen@>=
+begin delete_pen_ref(cur_pen); cur_pen:=cur_exp;
+end
+
+@ @<Complete the contour filling...@>=
+find_edges_var(lhs);
+if cur_edges=null then toss_knot_list(rhs)
+else begin lhs:=null; cur_path_type:=add_to_type;
+ if left_type(rhs)=endpoint then
+ if cur_path_type=double_path_code then @<Double the path@>
+ else @<Complain about non-cycle and |goto not_found|@>
+ else if cur_path_type=double_path_code then lhs:=htap_ypoc(rhs);
+ cur_wt:=w; rhs:=make_spec(rhs,max_offset(cur_pen),internal[tracing_specs]);
+ @<Check the turning number@>;
+ if max_offset(cur_pen)=0 then fill_spec(rhs)
+ else fill_envelope(rhs);
+ if lhs<>null then
+ begin rev_turns:=true;
+ lhs:=make_spec(lhs,max_offset(cur_pen),internal[tracing_specs]);
+ rev_turns:=false;
+ if max_offset(cur_pen)=0 then fill_spec(lhs)
+ else fill_envelope(lhs);
+ end;
+not_found: end
+
+@ @<Double the path@>=
+if link(rhs)=rhs then @<Make a trivial one-point path cycle@>
+else begin p:=htap_ypoc(rhs); q:=link(p);@/
+ right_x(path_tail):=right_x(q); right_y(path_tail):=right_y(q);
+ right_type(path_tail):=right_type(q);
+ link(path_tail):=link(q); free_node(q,knot_node_size);@/
+ right_x(p):=right_x(rhs); right_y(p):=right_y(rhs);
+ right_type(p):=right_type(rhs);
+ link(p):=link(rhs); free_node(rhs,knot_node_size);@/
+ rhs:=p;
+ end
+
+@ @<Make a trivial one-point path cycle@>=
+begin right_x(rhs):=x_coord(rhs); right_y(rhs):=y_coord(rhs);
+left_x(rhs):=x_coord(rhs); left_y(rhs):=y_coord(rhs);
+left_type(rhs):=explicit; right_type(rhs):=explicit;
+end
+
+@ @<Complain about non-cycle...@>=
+begin print_err("Not a cycle");
+@.Not a cycle@>
+help2("That contour should have ended with `..cycle' or `&cycle'.")@/
+ ("So I'll not change anything just now."); put_get_error;
+toss_knot_list(rhs); goto not_found;
+end
+
+@ @<Check the turning number@>=
+if turning_number<=0 then
+ if cur_path_type<>double_path_code then if internal[turning_check]>0 then
+ if (turning_number<0)and(link(cur_pen)=null) then negate(cur_wt)
+ else begin if turning_number=0 then
+ if (internal[turning_check]<=unity)and(link(cur_pen)=null) then goto done
+ else print_strange("Strange path (turning number is zero)")
+@.Strange path...@>
+ else print_strange("Backwards path (turning number is negative)");
+@.Backwards path...@>
+ help3("The path doesn't have a counterclockwise orientation,")@/
+ ("so I'll probably have trouble drawing it.")@/
+ ("(See Chapter 27 of The METAFONTbook for more help.)");
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+ put_get_error;
+ end;
+done:
+
+@ @<Cases of |do_statement|...@>=
+ship_out_command: do_ship_out;
+display_command: do_display;
+open_window: do_open_window;
+cull_command: do_cull;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+@t\4@>@<Declare the function called |tfm_check|@>@;
+procedure do_ship_out;
+label exit;
+var @!c:integer; {the character code}
+begin get_x_next; var_flag:=semicolon; scan_expression;
+if cur_type<>token_list then
+ if cur_type=picture_type then cur_edges:=cur_exp
+ else begin @<Abandon edges command because there's no variable@>;
+ return;
+ end
+else begin find_edges_var(cur_exp); cur_type:=vacuous;
+ end;
+if cur_edges<>null then
+ begin c:=round_unscaled(internal[char_code]) mod 256;
+ if c<0 then c:=c+256;
+ @<Store the width information for character code~|c|@>;
+ if internal[proofing]>=0 then ship_out(c);
+ end;
+flush_cur_exp(0);
+exit:end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_display;
+label not_found,common_ending,exit;
+var @!e:pointer; {token list for a picture variable}
+begin get_x_next; var_flag:=in_window; scan_primary;
+if cur_type<>token_list then
+ @<Abandon edges command because there's no variable@>
+else begin e:=cur_exp; cur_type:=vacuous;
+ get_x_next; scan_expression;
+ if cur_type<>known then goto common_ending;
+ cur_exp:=round_unscaled(cur_exp);
+ if cur_exp<0 then goto not_found;
+ if cur_exp>15 then goto not_found;
+ if not window_open[cur_exp] then goto not_found;
+ find_edges_var(e);
+ if cur_edges<>null then disp_edges(cur_exp);
+ return;
+ not_found: cur_exp:=cur_exp*unity;
+ common_ending: exp_err("Bad window number");
+@.Bad window number@>
+ help1("It should be the number of an open window.");
+ put_get_flush_error(0); flush_token_list(e);
+ end;
+exit:end;
+
+@ The only thing difficult about `\&{openwindow}' is that the syntax
+allows the user to go astray in many ways. The following subroutine
+helps keep the necessary program reasonably short and sweet.
+
+@<Declare action procedures for use by |do_statement|@>=
+function get_pair(@!c:command_code):boolean;
+var @!p:pointer; {a pair of values that are known (we hope)}
+@!b:boolean; {did we find such a pair?}
+begin if cur_cmd<>c then get_pair:=false
+else begin get_x_next; scan_expression;
+ if nice_pair(cur_exp,cur_type) then
+ begin p:=value(cur_exp);
+ cur_x:=value(x_part_loc(p)); cur_y:=value(y_part_loc(p));
+ b:=true;
+ end
+ else b:=false;
+ flush_cur_exp(0); get_pair:=b;
+ end;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_open_window;
+label not_found,exit;
+var @!k:integer; {the window number in question}
+@!r0,@!c0,@!r1,@!c1:scaled; {window coordinates}
+begin get_x_next; scan_expression;
+if cur_type<>known then goto not_found;
+k:=round_unscaled(cur_exp);
+if k<0 then goto not_found;
+if k>15 then goto not_found;
+if not get_pair(from_token) then goto not_found;
+r0:=cur_x; c0:=cur_y;
+if not get_pair(to_token) then goto not_found;
+r1:=cur_x; c1:=cur_y;
+if not get_pair(at_token) then goto not_found;
+open_a_window(k,r0,c0,r1,c1,cur_x,cur_y); return;
+not_found:print_err("Improper `openwindow'");
+@.Improper `openwindow'@>
+help2("Say `openwindow k from (r0,c0) to (r1,c1) at (x,y)',")@/
+ ("where all quantities are known and k is between 0 and 15.");
+put_get_error;
+exit:end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_cull;
+label not_found,exit;
+var @!e:pointer; {token list for a picture variable}
+@!keeping:drop_code..keep_code; {modifier of |cull_op|}
+@!w,@!w_in,@!w_out:integer; {culling weights}
+begin w:=1;
+get_x_next; var_flag:=cull_op; scan_primary;
+if cur_type<>token_list then
+ @<Abandon edges command because there's no variable@>
+else begin e:=cur_exp; cur_type:=vacuous; keeping:=cur_mod;
+ if not get_pair(cull_op) then goto not_found;
+ while (cur_cmd=with_option)and(cur_mod=known) do
+ if scan_with then w:=cur_exp;
+ @<Set up the culling weights,
+ or |goto not_found| if the thresholds are bad@>;
+ find_edges_var(e);
+ if cur_edges<>null then
+ cull_edges(floor_unscaled(cur_x+unity-1),floor_unscaled(cur_y),w_out,w_in);
+ return;
+ not_found: print_err("Bad culling amounts");
+@.Bad culling amounts@>
+ help1("Always cull by known amounts that exclude 0.");
+ put_get_error; flush_token_list(e);
+ end;
+exit:end;
+
+@ @<Set up the culling weights, or |goto not_found| if the thresholds are bad@>=
+if cur_x>cur_y then goto not_found;
+if keeping=drop_code then
+ begin if (cur_x>0)or(cur_y<0) then goto not_found;
+ w_out:=w; w_in:=0;
+ end
+else begin if (cur_x<=0)and(cur_y>=0) then goto not_found;
+ w_out:=0; w_in:=w;
+ end
+
+@ The \&{everyjob} command simply assigns a nonzero value to the global variable
+|start_sym|.
+
+@<Cases of |do_statement|...@>=
+every_job_command: begin get_symbol; start_sym:=cur_sym; get_x_next;
+ end;
+
+@ @<Glob...@>=
+@!start_sym:halfword; {a symbolic token to insert at beginning of job}
+
+@ @<Set init...@>=
+start_sym:=0;
+
+@ Finally, we have only the ``message'' commands remaining.
+
+@d message_code=0
+@d err_message_code=1
+@d err_help_code=2
+
+@<Put each...@>=
+primitive("message",message_command,message_code);@/
+@!@:message_}{\&{message} primitive@>
+primitive("errmessage",message_command,err_message_code);@/
+@!@:err_message_}{\&{errmessage} primitive@>
+primitive("errhelp",message_command,err_help_code);@/
+@!@:err_help_}{\&{errhelp} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+message_command: if m<err_message_code then print("message")
+ else if m=err_message_code then print("errmessage")
+ else print("errhelp");
+
+@ @<Cases of |do_statement|...@>=
+message_command: do_message;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_message;
+var @!m:message_code..err_help_code; {the type of message}
+begin m:=cur_mod; get_x_next; scan_expression;
+if cur_type<>string_type then
+ begin exp_err("Not a string");
+@.Not a string@>
+ help1("A message should be a known string expression.");
+ put_get_error;
+ end
+else case m of
+ message_code:begin print_nl(""); slow_print(cur_exp);
+ end;
+ err_message_code:@<Print string |cur_exp| as an error message@>;
+ err_help_code:@<Save string |cur_exp| as the |err_help|@>;
+ end; {there are no other cases}
+flush_cur_exp(0);
+end;
+
+@ The global variable |err_help| is zero when the user has most recently
+given an empty help string, or if none has ever been given.
+
+@<Save string |cur_exp| as the |err_help|@>=
+begin if err_help<>0 then delete_str_ref(err_help);
+if length(cur_exp)=0 then err_help:=0
+else begin err_help:=cur_exp; add_str_ref(err_help);
+ end;
+end
+
+@ If \&{errmessage} occurs often in |scroll_mode|, without user-defined
+\&{errhelp}, we don't want to give a long help message each time. So we
+give a verbose explanation only once.
+
+@<Glob...@>=
+@!long_help_seen:boolean; {has the long \&{errmessage} help been used?}
+
+@ @<Set init...@>=long_help_seen:=false;
+
+@ @<Print string |cur_exp| as an error message@>=
+begin print_err(""); slow_print(cur_exp);
+if err_help<>0 then use_err_help:=true
+else if long_help_seen then help1("(That was another `errmessage'.)")
+else begin if interaction<error_stop_mode then long_help_seen:=true;
+ help4("This error message was generated by an `errmessage'")@/
+ ("command, so I can't give any explicit help.")@/
+ ("Pretend that you're Miss Marple: Examine all clues,")@/
+@^Marple, Jane@>
+ ("and deduce the truth by inspired guesses.");
+ end;
+put_get_error; use_err_help:=false;
+end
+
+@* \[45] Font metric data.
+\TeX\ gets its knowledge about fonts from font metric files, also called
+\.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX,
+but other programs know about them too. One of \MF's duties is to
+write \.{TFM} files so that the user's fonts can readily be
+applied to typesetting.
+@:TFM files}{\.{TFM} files@>
+@^font metric files@>
+
+The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
+Since the number of bytes is always a multiple of~4, we could
+also regard the file as a sequence of 32-bit words, but \MF\ uses the
+byte interpretation. The format of \.{TFM} files was designed by
+Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds
+@^Ramshaw, Lyle Harold@>
+of information in a compact but useful form.
+
+@<Glob...@>=
+@!tfm_file:byte_file; {the font metric output goes here}
+@!metric_file_name: str_number; {full name of the font metric file}
+
+@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
+integers that give the lengths of the various subsequent portions
+of the file. These twelve integers are, in order:
+$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
+|lf|&length of the entire file, in words;\cr
+|lh|&length of the header data, in words;\cr
+|bc|&smallest character code in the font;\cr
+|ec|&largest character code in the font;\cr
+|nw|&number of words in the width table;\cr
+|nh|&number of words in the height table;\cr
+|nd|&number of words in the depth table;\cr
+|ni|&number of words in the italic correction table;\cr
+|nl|&number of words in the lig/kern table;\cr
+|nk|&number of words in the kern table;\cr
+|ne|&number of words in the extensible character table;\cr
+|np|&number of font parameter words.\cr}}$$
+They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
+|ne<=256|, and
+$$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
+Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
+and as few as 0 characters (if |bc=ec+1|).
+
+Incidentally, when two or more 8-bit bytes are combined to form an integer of
+16 or more bits, the most significant bytes appear first in the file.
+This is called BigEndian order.
+@!@^BigEndian order@>
+
+@ The rest of the \.{TFM} file may be regarded as a sequence of ten data
+arrays having the informal specification
+$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
+\tabskip\centering
+\halign to\displaywidth{\hfil\\{#}\tabskip=0pt&$\,:\,$\arr#\hfil
+ \tabskip\centering\cr
+header&|[0..lh-1]@t\\{stuff}@>|\cr
+char\_info&|[bc..ec]char_info_word|\cr
+width&|[0..nw-1]fix_word|\cr
+height&|[0..nh-1]fix_word|\cr
+depth&|[0..nd-1]fix_word|\cr
+italic&|[0..ni-1]fix_word|\cr
+lig\_kern&|[0..nl-1]lig_kern_command|\cr
+kern&|[0..nk-1]fix_word|\cr
+exten&|[0..ne-1]extensible_recipe|\cr
+param&|[1..np]fix_word|\cr}$$
+The most important data type used here is a |@!fix_word|, which is
+a 32-bit representation of a binary fraction. A |fix_word| is a signed
+quantity, with the two's complement of the entire word used to represent
+negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
+binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
+the smallest is $-2048$. We will see below, however, that all but two of
+the |fix_word| values must lie between $-16$ and $+16$.
+
+@ The first data array is a block of header information, which contains
+general facts about the font. The header must contain at least two words,
+|header[0]| and |header[1]|, whose meaning is explained below. Additional
+header information of use to other software routines might also be
+included, and \MF\ will generate it if the \.{headerbyte} command occurs.
+For example, 16 more words of header information are in use at the Xerox
+Palo Alto Research Center; the first ten specify the character coding
+scheme used (e.g., `\.{XEROX TEXT}' or `\.{TEX MATHSY}'), the next five
+give the font family name (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
+last gives the ``face byte.''
+
+\yskip\hang|header[0]| is a 32-bit check sum that \MF\ will copy into
+the \.{GF} output file. This helps ensure consistency between files,
+since \TeX\ records the check sums from the \.{TFM}'s it reads, and these
+should match the check sums on actual fonts that are used. The actual
+relation between this check sum and the rest of the \.{TFM} file is not
+important; the check sum is simply an identification number with the
+property that incompatible fonts almost always have distinct check sums.
+@^check sum@>
+
+\yskip\hang|header[1]| is a |fix_word| containing the design size of the
+font, in units of \TeX\ points. This number must be at least 1.0; it is
+fairly arbitrary, but usually the design size is 10.0 for a ``10 point''
+font, i.e., a font that was designed to look best at a 10-point size,
+whatever that really means. When a \TeX\ user asks for a font `\.{at}
+$\delta$ \.{pt}', the effect is to override the design size and replace it
+by $\delta$, and to multiply the $x$ and~$y$ coordinates of the points in
+the font image by a factor of $\delta$ divided by the design size. {\sl
+All other dimensions in the\/ \.{TFM} file are |fix_word|\kern-1pt\
+numbers in design-size units.} Thus, for example, the value of |param[6]|,
+which defines the \.{em} unit, is often the |fix_word| value $2^{20}=1.0$,
+since many fonts have a design size equal to one em. The other dimensions
+must be less than 16 design-size units in absolute value; thus,
+|header[1]| and |param[1]| are the only |fix_word| entries in the whole
+\.{TFM} file whose first byte might be something besides 0 or 255.
+@^design size@>
+
+@ Next comes the |char_info| array, which contains one |@!char_info_word|
+per character. Each word in this part of the file contains six fields
+packed into four bytes as follows.
+
+\yskip\hang first byte: |@!width_index| (8 bits)\par
+\hang second byte: |@!height_index| (4 bits) times 16, plus |@!depth_index|
+ (4~bits)\par
+\hang third byte: |@!italic_index| (6 bits) times 4, plus |@!tag|
+ (2~bits)\par
+\hang fourth byte: |@!remainder| (8 bits)\par
+\yskip\noindent
+The actual width of a character is \\{width}|[width_index]|, in design-size
+units; this is a device for compressing information, since many characters
+have the same width. Since it is quite common for many characters
+to have the same height, depth, or italic correction, the \.{TFM} format
+imposes a limit of 16 different heights, 16 different depths, and
+64 different italic corrections.
+
+Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
+\\{italic}[0]=0$ should always hold, so that an index of zero implies a
+value of zero. The |width_index| should never be zero unless the
+character does not exist in the font, since a character is valid if and
+only if it lies between |bc| and |ec| and has a nonzero |width_index|.
+
+@ The |tag| field in a |char_info_word| has four values that explain how to
+interpret the |remainder| field.
+
+\def\hangg#1 {\hang\hbox{#1 }}
+\yskip\hangg|tag=0| (|no_tag|) means that |remainder| is unused.\par
+\hangg|tag=1| (|lig_tag|) means that this character has a ligature/kerning
+program starting at location |remainder| in the |lig_kern| array.\par
+\hangg|tag=2| (|list_tag|) means that this character is part of a chain of
+characters of ascending sizes, and not the largest in the chain. The
+|remainder| field gives the character code of the next larger character.\par
+\hangg|tag=3| (|ext_tag|) means that this character code represents an
+extensible character, i.e., a character that is built up of smaller pieces
+so that it can be made arbitrarily large. The pieces are specified in
+|@!exten[remainder]|.\par
+\yskip\noindent
+Characters with |tag=2| and |tag=3| are treated as characters with |tag=0|
+unless they are used in special circumstances in math formulas. For example,
+\TeX's \.{\\sum} operation looks for a |list_tag|, and the \.{\\left}
+operation looks for both |list_tag| and |ext_tag|.
+
+@d no_tag=0 {vanilla character}
+@d lig_tag=1 {character has a ligature/kerning program}
+@d list_tag=2 {character has a successor in a charlist}
+@d ext_tag=3 {character is extensible}
+
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word in this array is a
+|@!lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+ step if the byte is 128 or more, otherwise the next step is obtained by
+ skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+ then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+ a kern step otherwise.\par
+\hang fourth byte: |remainder|.\par
+\yskip\noindent
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|remainder| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+If the very first instruction of the |lig_kern| array has |skip_byte=255|,
+the |next_char| byte is the so-called right boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte=255|,
+there is a special ligature/kerning program for a left boundary character,
+beginning at location |256*op_byte+remainder|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte>128|, the program actually begins in location
+|256*op_byte+remainder|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location |<=255|.
+
+Any instruction with |skip_byte>128| in the |lig_kern| array must satisfy
+the condition
+$$\hbox{|256*op_byte+remainder<nl|.}$$
+If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+command is performed.
+
+@d stop_flag=128+min_quarterword
+ {value indicating `\.{STOP}' in a lig/kern program}
+@d kern_flag=128+min_quarterword {op code for a kern step}
+@d skip_byte(#)==lig_kern[#].b0
+@d next_char(#)==lig_kern[#].b1
+@d op_byte(#)==lig_kern[#].b2
+@d rem_byte(#)==lig_kern[#].b3
+
+@ Extensible characters are specified by an |@!extensible_recipe|, which
+consists of four bytes called |@!top|, |@!mid|, |@!bot|, and |@!rep| (in this
+order). These bytes are the character codes of individual pieces used to
+build up a large symbol. If |top|, |mid|, or |bot| are zero, they are not
+present in the built-up result. For example, an extensible vertical line is
+like an extensible bracket, except that the top and bottom pieces are missing.
+
+Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box
+if the piece isn't present. Then the extensible characters have the form
+$TR^kMR^kB$ from top to bottom, for some |k>=0|, unless $M$ is absent;
+in the latter case we can have $TR^kB$ for both even and odd values of~|k|.
+The width of the extensible character is the width of $R$; and the
+height-plus-depth is the sum of the individual height-plus-depths of the
+components used, since the pieces are butted together in a vertical list.
+
+@d ext_top(#)==exten[#].b0 {|top| piece in a recipe}
+@d ext_mid(#)==exten[#].b1 {|mid| piece in a recipe}
+@d ext_bot(#)==exten[#].b2 {|bot| piece in a recipe}
+@d ext_rep(#)==exten[#].b3 {|rep| piece in a recipe}
+
+@ The final portion of a \.{TFM} file is the |param| array, which is another
+sequence of |fix_word| values.
+
+\yskip\hang|param[1]=slant| is the amount of italic slant, which is used
+to help position accents. For example, |slant=.25| means that when you go
+up one unit, you also go .25 units to the right. The |slant| is a pure
+number; it is the only |fix_word| other than the design size itself that is
+not scaled by the design size.
+@^design size@>
+
+\hang|param[2]=space| is the normal spacing between words in text.
+Note that character @'40 in the font need not have anything to do with
+blank spaces.
+
+\hang|param[3]=space_stretch| is the amount of glue stretching between words.
+
+\hang|param[4]=space_shrink| is the amount of glue shrinking between words.
+
+\hang|param[5]=x_height| is the size of one ex in the font; it is also
+the height of letters for which accents don't have to be raised or lowered.
+
+\hang|param[6]=quad| is the size of one em in the font.
+
+\hang|param[7]=extra_space| is the amount added to |param[2]| at the
+ends of sentences.
+
+\yskip\noindent
+If fewer than seven parameters are present, \TeX\ sets the missing parameters
+to zero.
+
+@d slant_code=1
+@d space_code=2
+@d space_stretch_code=3
+@d space_shrink_code=4
+@d x_height_code=5
+@d quad_code=6
+@d extra_space_code=7
+
+@ So that is what \.{TFM} files hold. One of \MF's duties is to output such
+information, and it does this all at once at the end of a job.
+In order to prepare for such frenetic activity, it squirrels away the
+necessary facts in various arrays as information becomes available.
+
+Character dimensions (\&{charwd}, \&{charht}, \&{chardp}, and \&{charic})
+are stored respectively in |tfm_width|, |tfm_height|, |tfm_depth|, and
+|tfm_ital_corr|. Other information about a character (e.g., about
+its ligatures or successors) is accessible via the |char_tag| and
+|char_remainder| arrays. Other information about the font as a whole
+is kept in additional arrays called |header_byte|, |lig_kern|,
+|kern|, |exten|, and |param|.
+
+@d undefined_label==lig_table_size {an undefined local label}
+
+@<Glob...@>=
+@!bc,@!ec:eight_bits; {smallest and largest character codes shipped out}
+@!tfm_width:array[eight_bits] of scaled; {\&{charwd} values}
+@!tfm_height:array[eight_bits] of scaled; {\&{charht} values}
+@!tfm_depth:array[eight_bits] of scaled; {\&{chardp} values}
+@!tfm_ital_corr:array[eight_bits] of scaled; {\&{charic} values}
+@!char_exists:array[eight_bits] of boolean; {has this code been shipped out?}
+@!char_tag:array[eight_bits] of no_tag..ext_tag; {|remainder| category}
+@!char_remainder:array[eight_bits] of 0..lig_table_size; {the |remainder| byte}
+@!header_byte:array[1..header_size] of -1..255;
+ {bytes of the \.{TFM} header, or $-1$ if unset}
+@!lig_kern:array[0..lig_table_size] of four_quarters; {the ligature/kern table}
+@!nl:0..32767-256; {the number of ligature/kern steps so far}
+@!kern:array[0..max_kerns] of scaled; {distinct kerning amounts}
+@!nk:0..max_kerns; {the number of distinct kerns so far}
+@!exten:array[eight_bits] of four_quarters; {extensible character recipes}
+@!ne:0..256; {the number of extensible characters so far}
+@!param:array[1..max_font_dimen] of scaled; {\&{fontinfo} parameters}
+@!np:0..max_font_dimen; {the largest \&{fontinfo} parameter specified so far}
+@!nw,@!nh,@!nd,@!ni:0..256; {sizes of \.{TFM} subtables}
+@!skip_table:array[eight_bits] of 0..lig_table_size; {local label status}
+@!lk_started:boolean; {has there been a lig/kern step in this command yet?}
+@!bchar:integer; {right boundary character}
+@!bch_label:0..lig_table_size; {left boundary starting location}
+@!ll,@!lll:0..lig_table_size; {registers used for lig/kern processing}
+@!label_loc:array[0..256] of -1..lig_table_size; {lig/kern starting addresses}
+@!label_char:array[1..256] of eight_bits; {characters for |label_loc|}
+@!label_ptr:0..256; {highest position occupied in |label_loc|}
+
+@ @<Set init...@>=
+for k:=0 to 255 do
+ begin tfm_width[k]:=0; tfm_height[k]:=0; tfm_depth[k]:=0; tfm_ital_corr[k]:=0;
+ char_exists[k]:=false; char_tag[k]:=no_tag; char_remainder[k]:=0;
+ skip_table[k]:=undefined_label;
+ end;
+for k:=1 to header_size do header_byte[k]:=-1;
+bc:=255; ec:=0; nl:=0; nk:=0; ne:=0; np:=0;@/
+internal[boundary_char]:=-unity;
+bch_label:=undefined_label;@/
+label_loc[0]:=-1; label_ptr:=0;
+
+@ @<Declare the function called |tfm_check|@>=
+function tfm_check(@!m:small_number):scaled;
+begin if abs(internal[m])>=fraction_half then
+ begin print_err("Enormous "); print(int_name[m]);
+@.Enormous charwd...@>
+@.Enormous chardp...@>
+@.Enormous charht...@>
+@.Enormous charic...@>
+@.Enormous designsize...@>
+ print(" has been reduced");
+ help1("Font metric dimensions must be less than 2048pt.");
+ put_get_error;
+ if internal[m]>0 then tfm_check:=fraction_half-1
+ else tfm_check:=1-fraction_half;
+ end
+else tfm_check:=internal[m];
+end;
+
+@ @<Store the width information for character code~|c|@>=
+if c<bc then bc:=c;
+if c>ec then ec:=c;
+char_exists[c]:=true;
+gf_dx[c]:=internal[char_dx]; gf_dy[c]:=internal[char_dy];
+tfm_width[c]:=tfm_check(char_wd);
+tfm_height[c]:=tfm_check(char_ht);
+tfm_depth[c]:=tfm_check(char_dp);
+tfm_ital_corr[c]:=tfm_check(char_ic)
+
+@ Now let's consider \MF's special \.{TFM}-oriented commands.
+
+@<Cases of |do_statement|...@>=
+tfm_command: do_tfm_command;
+
+@ @d char_list_code=0
+@d lig_table_code=1
+@d extensible_code=2
+@d header_byte_code=3
+@d font_dimen_code=4
+
+@<Put each...@>=
+primitive("charlist",tfm_command,char_list_code);@/
+@!@:char_list_}{\&{charlist} primitive@>
+primitive("ligtable",tfm_command,lig_table_code);@/
+@!@:lig_table_}{\&{ligtable} primitive@>
+primitive("extensible",tfm_command,extensible_code);@/
+@!@:extensible_}{\&{extensible} primitive@>
+primitive("headerbyte",tfm_command,header_byte_code);@/
+@!@:header_byte_}{\&{headerbyte} primitive@>
+primitive("fontdimen",tfm_command,font_dimen_code);@/
+@!@:font_dimen_}{\&{fontdimen} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+tfm_command: case m of
+ char_list_code:print("charlist");
+ lig_table_code:print("ligtable");
+ extensible_code:print("extensible");
+ header_byte_code:print("headerbyte");
+ othercases print("fontdimen")
+ endcases;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+function get_code:eight_bits; {scans a character code value}
+label found;
+var @!c:integer; {the code value found}
+begin get_x_next; scan_expression;
+if cur_type=known then
+ begin c:=round_unscaled(cur_exp);
+ if c>=0 then if c<256 then goto found;
+ end
+else if cur_type=string_type then if length(cur_exp)=1 then
+ begin c:=so(str_pool[str_start[cur_exp]]); goto found;
+ end;
+exp_err("Invalid code has been replaced by 0");
+@.Invalid code...@>
+help2("I was looking for a number between 0 and 255, or for a")@/
+ ("string of length 1. Didn't find it; will use 0 instead.");
+put_get_flush_error(0); c:=0;
+found: get_code:=c;
+end;
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure set_tag(@!c:halfword;@!t:small_number;@!r:halfword);
+begin if char_tag[c]=no_tag then
+ begin char_tag[c]:=t; char_remainder[c]:=r;
+ if t=lig_tag then
+ begin incr(label_ptr); label_loc[label_ptr]:=r; label_char[label_ptr]:=c;
+ end;
+ end
+else @<Complain about a character tag conflict@>;
+end;
+
+@ @<Complain about a character tag conflict@>=
+begin print_err("Character ");
+if (c>" ")and(c<127) then print(c)
+else if c=256 then print("||")
+else begin print("code "); print_int(c);
+ end;
+print(" is already ");
+@.Character c is already...@>
+case char_tag[c] of
+lig_tag: print("in a ligtable");
+list_tag: print("in a charlist");
+ext_tag: print("extensible");
+end; {there are no other cases}
+help2("It's not legal to label a character more than once.")@/
+ ("So I'll not change anything just now.");
+put_get_error; end
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_tfm_command;
+label continue,done;
+var @!c,@!cc:0..256; {character codes}
+@!k:0..max_kerns; {index into the |kern| array}
+@!j:integer; {index into |header_byte| or |param|}
+begin case cur_mod of
+char_list_code: begin c:=get_code;
+ {we will store a list of character successors}
+ while cur_cmd=colon do
+ begin cc:=get_code; set_tag(c,list_tag,cc); c:=cc;
+ end;
+ end;
+lig_table_code: @<Store a list of ligature/kern steps@>;
+extensible_code: @<Define an extensible recipe@>;
+header_byte_code, font_dimen_code: begin c:=cur_mod; get_x_next;
+ scan_expression;
+ if (cur_type<>known)or(cur_exp<half_unit) then
+ begin exp_err("Improper location");
+@.Improper location@>
+ help2("I was looking for a known, positive number.")@/
+ ("For safety's sake I'll ignore the present command.");
+ put_get_error;
+ end
+ else begin j:=round_unscaled(cur_exp);
+ if cur_cmd<>colon then
+ begin missing_err(":");
+@.Missing `:'@>
+ help1("A colon should follow a headerbyte or fontinfo location.");
+ back_error;
+ end;
+ if c=header_byte_code then @<Store a list of header bytes@>
+ else @<Store a list of font dimensions@>;
+ end;
+ end;
+end; {there are no other cases}
+end;
+
+@ @<Store a list of ligature/kern steps@>=
+begin lk_started:=false;
+continue: get_x_next;
+if(cur_cmd=skip_to)and lk_started then
+ @<Process a |skip_to| command and |goto done|@>;
+if cur_cmd=bchar_label then
+ begin c:=256; cur_cmd:=colon;@+end
+else begin back_input; c:=get_code;@+end;
+if(cur_cmd=colon)or(cur_cmd=double_colon)then
+ @<Record a label in a lig/kern subprogram and |goto continue|@>;
+if cur_cmd=lig_kern_token then @<Compile a ligature/kern command@>
+else begin print_err("Illegal ligtable step");
+@.Illegal ligtable step@>
+ help1("I was looking for `=:' or `kern' here.");
+ back_error; next_char(nl):=qi(0); op_byte(nl):=qi(0); rem_byte(nl):=qi(0);@/
+ skip_byte(nl):=stop_flag+1; {this specifies an unconditional stop}
+ end;
+if nl=lig_table_size then overflow("ligtable size",lig_table_size);
+@:METAFONT capacity exceeded ligtable size}{\quad ligtable size@>
+incr(nl);
+if cur_cmd=comma then goto continue;
+if skip_byte(nl-1)<stop_flag then skip_byte(nl-1):=stop_flag;
+done:end
+
+@ @<Put each...@>=
+primitive("=:",lig_kern_token,0);
+@!@:=:_}{\.{=:} primitive@>
+primitive("=:|",lig_kern_token,1);
+@!@:=:/_}{\.{=:\char'174} primitive@>
+primitive("=:|>",lig_kern_token,5);
+@!@:=:/>_}{\.{=:\char'174>} primitive@>
+primitive("|=:",lig_kern_token,2);
+@!@:=:/_}{\.{\char'174=:} primitive@>
+primitive("|=:>",lig_kern_token,6);
+@!@:=:/>_}{\.{\char'174=:>} primitive@>
+primitive("|=:|",lig_kern_token,3);
+@!@:=:/_}{\.{\char'174=:\char'174} primitive@>
+primitive("|=:|>",lig_kern_token,7);
+@!@:=:/>_}{\.{\char'174=:\char'174>} primitive@>
+primitive("|=:|>>",lig_kern_token,11);
+@!@:=:/>_}{\.{\char'174=:\char'174>>} primitive@>
+primitive("kern",lig_kern_token,128);
+@!@:kern_}{\&{kern} primitive@>
+
+@ @<Cases of |print_cmd...@>=
+lig_kern_token: case m of
+0:print("=:");
+1:print("=:|");
+2:print("|=:");
+3:print("|=:|");
+5:print("=:|>");
+6:print("|=:>");
+7:print("|=:|>");
+11:print("|=:|>>");
+othercases print("kern")
+endcases;
+
+@ Local labels are implemented by maintaining the |skip_table| array,
+where |skip_table[c]| is either |undefined_label| or the address of the
+most recent lig/kern instruction that skips to local label~|c|. In the
+latter case, the |skip_byte| in that instruction will (temporarily)
+be zero if there were no prior skips to this label, or it will be the
+distance to the prior skip.
+
+We may need to cancel skips that span more than 127 lig/kern steps.
+
+@d cancel_skips(#)==ll:=#;
+ repeat lll:=qo(skip_byte(ll)); skip_byte(ll):=stop_flag; ll:=ll-lll;
+ until lll=0
+@d skip_error(#)==begin print_err("Too far to skip");
+@.Too far to skip@>
+ help1("At most 127 lig/kern steps can separate skipto1 from 1::.");
+ error; cancel_skips(#);
+ end
+
+@<Process a |skip_to| command and |goto done|@>=
+begin c:=get_code;
+if nl-skip_table[c]>128 then
+ begin skip_error(skip_table[c]); skip_table[c]:=undefined_label;
+ end;
+if skip_table[c]=undefined_label then skip_byte(nl-1):=qi(0)
+else skip_byte(nl-1):=qi(nl-skip_table[c]-1);
+skip_table[c]:=nl-1; goto done;
+end
+
+@ @<Record a label in a lig/kern subprogram and |goto continue|@>=
+begin if cur_cmd=colon then
+ if c=256 then bch_label:=nl
+ else set_tag(c,lig_tag,nl)
+else if skip_table[c]<undefined_label then
+ begin ll:=skip_table[c]; skip_table[c]:=undefined_label;
+ repeat lll:=qo(skip_byte(ll));
+ if nl-ll>128 then
+ begin skip_error(ll); goto continue;
+ end;
+ skip_byte(ll):=qi(nl-ll-1); ll:=ll-lll;
+ until lll=0;
+ end;
+goto continue;
+end
+
+@ @<Compile a ligature/kern...@>=
+begin next_char(nl):=qi(c); skip_byte(nl):=qi(0);
+if cur_mod<128 then {ligature op}
+ begin op_byte(nl):=qi(cur_mod); rem_byte(nl):=qi(get_code);
+ end
+else begin get_x_next; scan_expression;
+ if cur_type<>known then
+ begin exp_err("Improper kern");
+@.Improper kern@>
+ help2("The amount of kern should be a known numeric value.")@/
+ ("I'm zeroing this one. Proceed, with fingers crossed.");
+ put_get_flush_error(0);
+ end;
+ kern[nk]:=cur_exp;
+ k:=0;@+while kern[k]<>cur_exp do incr(k);
+ if k=nk then
+ begin if nk=max_kerns then overflow("kern",max_kerns);
+@:METAFONT capacity exceeded kern}{\quad kern@>
+ incr(nk);
+ end;
+ op_byte(nl):=kern_flag+(k div 256);
+ rem_byte(nl):=qi((k mod 256));
+ end;
+lk_started:=true;
+end
+
+@ @d missing_extensible_punctuation(#)==
+ begin missing_err(#);
+@.Missing `\char`\#'@>
+ help1("I'm processing `extensible c: t,m,b,r'."); back_error;
+ end
+
+@<Define an extensible recipe@>=
+begin if ne=256 then overflow("extensible",256);
+@:METAFONT capacity exceeded extensible}{\quad extensible@>
+c:=get_code; set_tag(c,ext_tag,ne);
+if cur_cmd<>colon then missing_extensible_punctuation(":");
+ext_top(ne):=qi(get_code);
+if cur_cmd<>comma then missing_extensible_punctuation(",");
+ext_mid(ne):=qi(get_code);
+if cur_cmd<>comma then missing_extensible_punctuation(",");
+ext_bot(ne):=qi(get_code);
+if cur_cmd<>comma then missing_extensible_punctuation(",");
+ext_rep(ne):=qi(get_code);
+incr(ne);
+end
+
+@ @<Store a list of header bytes@>=
+repeat if j>header_size then overflow("headerbyte",header_size);
+@:METAFONT capacity exceeded headerbyte}{\quad headerbyte@>
+header_byte[j]:=get_code; incr(j);
+until cur_cmd<>comma
+
+@ @<Store a list of font dimensions@>=
+repeat if j>max_font_dimen then overflow("fontdimen",max_font_dimen);
+@:METAFONT capacity exceeded fontdimen}{\quad fontdimen@>
+while j>np do
+ begin incr(np); param[np]:=0;
+ end;
+get_x_next; scan_expression;
+if cur_type<>known then
+ begin exp_err("Improper font parameter");
+@.Improper font parameter@>
+ help1("I'm zeroing this one. Proceed, with fingers crossed.");
+ put_get_flush_error(0);
+ end;
+param[j]:=cur_exp; incr(j);
+until cur_cmd<>comma
+
+@ OK: We've stored all the data that is needed for the \.{TFM} file.
+All that remains is to output it in the correct format.
+
+An interesting problem needs to be solved in this connection, because
+the \.{TFM} format allows at most 256~widths, 16~heights, 16~depths,
+and 64~italic corrections. If the data has more distinct values than
+this, we want to meet the necessary restrictions by perturbing the
+given values as little as possible.
+
+\MF\ solves this problem in two steps. First the values of a given
+kind (widths, heights, depths, or italic corrections) are sorted;
+then the list of sorted values is perturbed, if necessary.
+
+The sorting operation is facilitated by having a special node of
+essentially infinite |value| at the end of the current list.
+
+@<Initialize table entries...@>=
+value(inf_val):=fraction_four;
+
+@ Straight linear insertion is good enough for sorting, since the lists
+are usually not terribly long. As we work on the data, the current list
+will start at |link(temp_head)| and end at |inf_val|; the nodes in this
+list will be in increasing order of their |value| fields.
+
+Given such a list, the |sort_in| function takes a value and returns a pointer
+to where that value can be found in the list. The value is inserted in
+the proper place, if necessary.
+
+At the time we need to do these operations, most of \MF's work has been
+completed, so we will have plenty of memory to play with. The value nodes
+that are allocated for sorting will never be returned to free storage.
+
+@d clear_the_list==link(temp_head):=inf_val
+
+@p function sort_in(@!v:scaled):pointer;
+label found;
+var @!p,@!q,@!r:pointer; {list manipulation registers}
+begin p:=temp_head;
+loop@+ begin q:=link(p);
+ if v<=value(q) then goto found;
+ p:=q;
+ end;
+found: if v<value(q) then
+ begin r:=get_node(value_node_size); value(r):=v; link(r):=q; link(p):=r;
+ end;
+sort_in:=link(p);
+end;
+
+@ Now we come to the interesting part, where we reduce the list if necessary
+until it has the required size. The |min_cover| routine is basic to this
+process; it computes the minimum number~|m| such that the values of the
+current sorted list can be covered by |m|~intervals of width~|d|. It
+also sets the global value |perturbation| to the smallest value $d'>d$
+such that the covering found by this algorithm would be different.
+
+In particular, |min_cover(0)| returns the number of distinct values in the
+current list and sets |perturbation| to the minimum distance between
+adjacent values.
+
+@p function min_cover(@!d:scaled):integer;
+var @!p:pointer; {runs through the current list}
+@!l:scaled; {the least element covered by the current interval}
+@!m:integer; {lower bound on the size of the minimum cover}
+begin m:=0; p:=link(temp_head); perturbation:=el_gordo;
+while p<>inf_val do
+ begin incr(m); l:=value(p);
+ repeat p:=link(p);
+ until value(p)>l+d;
+ if value(p)-l<perturbation then perturbation:=value(p)-l;
+ end;
+min_cover:=m;
+end;
+
+@ @<Glob...@>=
+@!perturbation:scaled; {quantity related to \.{TFM} rounding}
+@!excess:integer; {the list is this much too long}
+
+@ The smallest |d| such that a given list can be covered with |m| intervals
+is determined by the |threshold| routine, which is sort of an inverse
+to |min_cover|. The idea is to increase the interval size rapidly until
+finding the range, then to go sequentially until the exact borderline has
+been discovered.
+
+@p function threshold(@!m:integer):scaled;
+var @!d:scaled; {lower bound on the smallest interval size}
+begin excess:=min_cover(0)-m;
+if excess<=0 then threshold:=0
+else begin repeat d:=perturbation;
+ until min_cover(d+d)<=m;
+ while min_cover(d)>m do d:=perturbation;
+ threshold:=d;
+ end;
+end;
+
+@ The |skimp| procedure reduces the current list to at most |m| entries,
+by changing values if necessary. It also sets |info(p):=k| if |value(p)|
+is the |k|th distinct value on the resulting list, and it sets
+|perturbation| to the maximum amount by which a |value| field has
+been changed. The size of the resulting list is returned as the
+value of |skimp|.
+
+@p function skimp(@!m:integer):integer;
+var @!d:scaled; {the size of intervals being coalesced}
+@!p,@!q,@!r:pointer; {list manipulation registers}
+@!l:scaled; {the least value in the current interval}
+@!v:scaled; {a compromise value}
+begin d:=threshold(m); perturbation:=0;
+q:=temp_head; m:=0; p:=link(temp_head);
+while p<>inf_val do
+ begin incr(m); l:=value(p); info(p):=m;
+ if value(link(p))<=l+d then
+ @<Replace an interval of values by its midpoint@>;
+ q:=p; p:=link(p);
+ end;
+skimp:=m;
+end;
+
+@ @<Replace an interval...@>=
+begin repeat p:=link(p); info(p):=m;
+decr(excess);@+if excess=0 then d:=0;
+until value(link(p))>l+d;
+v:=l+half(value(p)-l);
+if value(p)-v>perturbation then perturbation:=value(p)-v;
+r:=q;
+repeat r:=link(r); value(r):=v;
+until r=p;
+link(q):=p; {remove duplicate values from the current list}
+end
+
+@ A warning message is issued whenever something is perturbed by
+more than 1/16\thinspace pt.
+
+@p procedure tfm_warning(@!m:small_number);
+begin print_nl("(some "); print(int_name[m]);
+@.some charwds...@>
+@.some chardps...@>
+@.some charhts...@>
+@.some charics...@>
+print(" values had to be adjusted by as much as ");
+print_scaled(perturbation); print("pt)");
+end;
+
+@ Here's an example of how we use these routines.
+The width data needs to be perturbed only if there are 256 distinct
+widths, but \MF\ must check for this case even though it is
+highly unusual.
+
+An integer variable |k| will be defined when we use this code.
+The |dimen_head| array will contain pointers to the sorted
+lists of dimensions.
+
+@<Massage the \.{TFM} widths@>=
+clear_the_list;
+for k:=bc to ec do if char_exists[k] then
+ tfm_width[k]:=sort_in(tfm_width[k]);
+nw:=skimp(255)+1; dimen_head[1]:=link(temp_head);
+if perturbation>=@'10000 then tfm_warning(char_wd)
+
+@ @<Glob...@>=
+@!dimen_head:array[1..4] of pointer; {lists of \.{TFM} dimensions}
+
+@ Heights, depths, and italic corrections are different from widths
+not only because their list length is more severely restricted, but
+also because zero values do not need to be put into the lists.
+
+@<Massage the \.{TFM} heights, depths, and italic corrections@>=
+clear_the_list;
+for k:=bc to ec do if char_exists[k] then
+ if tfm_height[k]=0 then tfm_height[k]:=zero_val
+ else tfm_height[k]:=sort_in(tfm_height[k]);
+nh:=skimp(15)+1; dimen_head[2]:=link(temp_head);
+if perturbation>=@'10000 then tfm_warning(char_ht);
+clear_the_list;
+for k:=bc to ec do if char_exists[k] then
+ if tfm_depth[k]=0 then tfm_depth[k]:=zero_val
+ else tfm_depth[k]:=sort_in(tfm_depth[k]);
+nd:=skimp(15)+1; dimen_head[3]:=link(temp_head);
+if perturbation>=@'10000 then tfm_warning(char_dp);
+clear_the_list;
+for k:=bc to ec do if char_exists[k] then
+ if tfm_ital_corr[k]=0 then tfm_ital_corr[k]:=zero_val
+ else tfm_ital_corr[k]:=sort_in(tfm_ital_corr[k]);
+ni:=skimp(63)+1; dimen_head[4]:=link(temp_head);
+if perturbation>=@'10000 then tfm_warning(char_ic)
+
+@ @<Initialize table entries...@>=
+value(zero_val):=0; info(zero_val):=0;
+
+@ Bytes 5--8 of the header are set to the design size, unless the user has
+some crazy reason for specifying them differently.
+@^design size@>
+
+Error messages are not allowed at the time this procedure is called,
+so a warning is printed instead.
+
+The value of |max_tfm_dimen| is calculated so that
+$$\hbox{|make_scaled(16*max_tfm_dimen,internal[design_size])|}
+ < \\{three\_bytes}.$$
+
+@d three_bytes==@'100000000 {$2^{24}$}
+
+@p procedure fix_design_size;
+var @!d:scaled; {the design size}
+begin d:=internal[design_size];
+if (d<unity)or(d>=fraction_half) then
+ begin if d<>0 then
+ print_nl("(illegal design size has been changed to 128pt)");
+@.illegal design size...@>
+ d:=@'40000000; internal[design_size]:=d;
+ end;
+if header_byte[5]<0 then if header_byte[6]<0 then
+ if header_byte[7]<0 then if header_byte[8]<0 then
+ begin header_byte[5]:=d div @'4000000;
+ header_byte[6]:=(d div 4096) mod 256;
+ header_byte[7]:=(d div 16) mod 256;
+ header_byte[8]:=(d mod 16)*16;
+ end;
+max_tfm_dimen:=16*internal[design_size]-1-internal[design_size] div @'10000000;
+if max_tfm_dimen>=fraction_half then max_tfm_dimen:=fraction_half-1;
+end;
+
+@ The |dimen_out| procedure computes a |fix_word| relative to the
+design size. If the data was out of range, it is corrected and the
+global variable |tfm_changed| is increased by~one.
+
+@p function dimen_out(@!x:scaled):integer;
+begin if abs(x)>max_tfm_dimen then
+ begin incr(tfm_changed);
+ if x>0 then x:=max_tfm_dimen@+else x:=-max_tfm_dimen;
+ end;
+x:=make_scaled(x*16,internal[design_size]);
+dimen_out:=x;
+end;
+
+@ @<Glob...@>=
+@!max_tfm_dimen:scaled; {bound on widths, heights, kerns, etc.}
+@!tfm_changed:integer; {the number of data entries that were out of bounds}
+
+@ If the user has not specified any of the first four header bytes,
+the |fix_check_sum| procedure replaces them by a ``check sum'' computed
+from the |tfm_width| data relative to the design size.
+@^check sum@>
+
+@p procedure fix_check_sum;
+label exit;
+var @!k:eight_bits; {runs through character codes}
+@!b1,@!b2,@!b3,@!b4:eight_bits; {bytes of the check sum}
+@!x:integer; {hash value used in check sum computation}
+begin if header_byte[1]<0 then if header_byte[2]<0 then
+ if header_byte[3]<0 then if header_byte[4]<0 then
+ begin @<Compute a check sum in |(b1,b2,b3,b4)|@>;
+ header_byte[1]:=b1; header_byte[2]:=b2;
+ header_byte[3]:=b3; header_byte[4]:=b4; return;
+ end;
+for k:=1 to 4 do if header_byte[k]<0 then header_byte[k]:=0;
+exit:end;
+
+@ @<Compute a check sum in |(b1,b2,b3,b4)|@>=
+b1:=bc; b2:=ec; b3:=bc; b4:=ec; tfm_changed:=0;
+for k:=bc to ec do if char_exists[k] then
+ begin x:=dimen_out(value(tfm_width[k]))+(k+4)*@'20000000; {this is positive}
+ b1:=(b1+b1+x) mod 255;
+ b2:=(b2+b2+x) mod 253;
+ b3:=(b3+b3+x) mod 251;
+ b4:=(b4+b4+x) mod 247;
+ end
+
+@ Finally we're ready to actually write the \.{TFM} information.
+Here are some utility routines for this purpose.
+
+@d tfm_out(#)==write(tfm_file,#) {output one byte to |tfm_file|}
+
+@p procedure tfm_two(@!x:integer); {output two bytes to |tfm_file|}
+begin tfm_out(x div 256); tfm_out(x mod 256);
+end;
+@#
+procedure tfm_four(@!x:integer); {output four bytes to |tfm_file|}
+begin if x>=0 then tfm_out(x div three_bytes)
+else begin x:=x+@'10000000000; {use two's complement for negative values}
+ x:=x+@'10000000000;
+ tfm_out((x div three_bytes) + 128);
+ end;
+x:=x mod three_bytes; tfm_out(x div unity);
+x:=x mod unity; tfm_out(x div @'400);
+tfm_out(x mod @'400);
+end;
+@#
+procedure tfm_qqqq(@!x:four_quarters); {output four quarterwords to |tfm_file|}
+begin tfm_out(qo(x.b0)); tfm_out(qo(x.b1)); tfm_out(qo(x.b2));
+tfm_out(qo(x.b3));
+end;
+
+@ @<Finish the \.{TFM} file@>=
+if job_name=0 then open_log_file;
+pack_job_name(".tfm");
+while not b_open_out(tfm_file) do
+ prompt_file_name("file name for font metrics",".tfm");
+metric_file_name:=b_make_name_string(tfm_file);
+@<Output the subfile sizes and header bytes@>;
+@<Output the character information bytes, then
+ output the dimensions themselves@>;
+@<Output the ligature/kern program@>;
+@<Output the extensible character recipes and the font metric parameters@>;
+@!stat if internal[tracing_stats]>0 then
+ @<Log the subfile sizes of the \.{TFM} file@>;@;@+tats@/
+print_nl("Font metrics written on "); slow_print(metric_file_name);
+print_char(".");
+@.Font metrics written...@>
+b_close(tfm_file)
+
+@ Integer variables |lh|, |k|, and |lk_offset| will be defined when we use
+this code.
+
+@<Output the subfile sizes and header bytes@>=
+k:=header_size;
+while header_byte[k]<0 do decr(k);
+lh:=(k+3) div 4; {this is the number of header words}
+if bc>ec then bc:=1; {if there are no characters, |ec=0| and |bc=1|}
+@<Compute the ligature/kern program offset and implant the
+ left boundary label@>;
+tfm_two(6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+lk_offset+nk+ne+np);
+ {this is the total number of file words that will be output}
+tfm_two(lh); tfm_two(bc); tfm_two(ec); tfm_two(nw); tfm_two(nh);
+tfm_two(nd); tfm_two(ni); tfm_two(nl+lk_offset); tfm_two(nk); tfm_two(ne);
+tfm_two(np);
+for k:=1 to 4*lh do
+ begin if header_byte[k]<0 then header_byte[k]:=0;
+ tfm_out(header_byte[k]);
+ end
+
+@ @<Output the character information bytes...@>=
+for k:=bc to ec do
+ if not char_exists[k] then tfm_four(0)
+ else begin tfm_out(info(tfm_width[k])); {the width index}
+ tfm_out((info(tfm_height[k]))*16+info(tfm_depth[k]));
+ tfm_out((info(tfm_ital_corr[k]))*4+char_tag[k]);
+ tfm_out(char_remainder[k]);
+ end;
+tfm_changed:=0;
+for k:=1 to 4 do
+ begin tfm_four(0); p:=dimen_head[k];
+ while p<>inf_val do
+ begin tfm_four(dimen_out(value(p))); p:=link(p);
+ end;
+ end
+
+@ We need to output special instructions at the beginning of the
+|lig_kern| array in order to specify the right boundary character
+and/or to handle starting addresses that exceed 255. The |label_loc|
+and |label_char| arrays have been set up to record all the
+starting addresses; we have $-1=|label_loc|[0]<|label_loc|[1]\le\cdots
+\le|label_loc|[|label_ptr]|$.
+
+@<Compute the ligature/kern program offset...@>=
+bchar:=round_unscaled(internal[boundary_char]);
+if(bchar<0)or(bchar>255)then
+ begin bchar:=-1; lk_started:=false; lk_offset:=0;@+end
+else begin lk_started:=true; lk_offset:=1;@+end;
+@<Find the minimum |lk_offset| and adjust all remainders@>;
+if bch_label<undefined_label then
+ begin skip_byte(nl):=qi(255); next_char(nl):=qi(0);
+ op_byte(nl):=qi(((bch_label+lk_offset)div 256));
+ rem_byte(nl):=qi(((bch_label+lk_offset)mod 256));
+ incr(nl); {possibly |nl=lig_table_size+1|}
+ end
+
+@ @<Find the minimum |lk_offset|...@>=
+k:=label_ptr; {pointer to the largest unallocated label}
+if label_loc[k]+lk_offset>255 then
+ begin lk_offset:=0; lk_started:=false; {location 0 can do double duty}
+ repeat char_remainder[label_char[k]]:=lk_offset;
+ while label_loc[k-1]=label_loc[k] do
+ begin decr(k); char_remainder[label_char[k]]:=lk_offset;
+ end;
+ incr(lk_offset); decr(k);
+ until lk_offset+label_loc[k]<256;
+ {N.B.: |lk_offset=256| satisfies this when |k=0|}
+ end;
+if lk_offset>0 then
+ while k>0 do
+ begin char_remainder[label_char[k]]
+ :=char_remainder[label_char[k]]+lk_offset;
+ decr(k);
+ end
+
+@ @<Output the ligature/kern program@>=
+for k:=0 to 255 do if skip_table[k]<undefined_label then
+ begin print_nl("(local label "); print_int(k); print(":: was missing)");
+@.local label l:: was missing@>
+ cancel_skips(skip_table[k]);
+ end;
+if lk_started then {|lk_offset=1| for the special |bchar|}
+ begin tfm_out(255); tfm_out(bchar); tfm_two(0);
+ end
+else for k:=1 to lk_offset do {output the redirection specs}
+ begin ll:=label_loc[label_ptr];
+ if bchar<0 then
+ begin tfm_out(254); tfm_out(0);
+ end
+ else begin tfm_out(255); tfm_out(bchar);
+ end;
+ tfm_two(ll+lk_offset);
+ repeat decr(label_ptr);
+ until label_loc[label_ptr]<ll;
+ end;
+for k:=0 to nl-1 do tfm_qqqq(lig_kern[k]);
+for k:=0 to nk-1 do tfm_four(dimen_out(kern[k]))
+
+@ @<Output the extensible character recipes...@>=
+for k:=0 to ne-1 do tfm_qqqq(exten[k]);
+for k:=1 to np do
+ if k=1 then
+ if abs(param[1])<fraction_half then tfm_four(param[1]*16)
+ else begin incr(tfm_changed);
+ if param[1]>0 then tfm_four(el_gordo)
+ else tfm_four(-el_gordo);
+ end
+ else tfm_four(dimen_out(param[k]));
+if tfm_changed>0 then
+ begin if tfm_changed=1 then print_nl("(a font metric dimension")
+@.a font metric dimension...@>
+ else begin print_nl("("); print_int(tfm_changed);
+@.font metric dimensions...@>
+ print(" font metric dimensions");
+ end;
+ print(" had to be decreased)");
+ end
+
+@ @<Log the subfile sizes of the \.{TFM} file@>=
+begin wlog_ln(' ');
+if bch_label<undefined_label then decr(nl);
+wlog_ln('(You used ',nw:1,'w,',@| nh:1,'h,',@| nd:1,'d,',@| ni:1,'i,',@|
+ nl:1,'l,',@| nk:1,'k,',@| ne:1,'e,',@|
+ np:1,'p metric file positions');
+wlog_ln(' out of ',@| '256w,16h,16d,64i,',@|
+ lig_table_size:1,'l,',max_kerns:1,'k,256e,',@|
+ max_font_dimen:1,'p)');
+end
+
+@* \[46] Generic font file format.
+The most important output produced by a typical run of \MF\ is the
+``generic font'' (\.{GF}) file that specifies the bit patterns of the
+characters that have been drawn. The term {\sl generic\/} indicates that
+this file format doesn't match the conventions of any name-brand manufacturer;
+but it is easy to convert \.{GF} files to the special format required by
+almost all digital phototypesetting equipment. There's a strong analogy
+between the \.{DVI} files written by \TeX\ and the \.{GF} files written
+by \MF; and, in fact, the file formats have a lot in common.
+
+A \.{GF} file is a stream of 8-bit bytes that may be
+regarded as a series of commands in a machine-like language. The first
+byte of each command is the operation code, and this code is followed by
+zero or more bytes that provide parameters to the command. The parameters
+themselves may consist of several consecutive bytes; for example, the
+`|boc|' (beginning of character) command has six parameters, each of
+which is four bytes long. Parameters are usually regarded as nonnegative
+integers; but four-byte-long parameters can be either positive or
+negative, hence they range in value from $-2^{31}$ to $2^{31}-1$.
+As in \.{TFM} files, numbers that occupy
+more than one byte position appear in BigEndian order,
+and negative numbers appear in two's complement notation.
+
+A \.{GF} file consists of a ``preamble,'' followed by a sequence of one or
+more ``characters,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that introduce the file; this must come
+first. Each ``character'' consists of a |boc| command, followed by any
+number of other commands that specify ``black'' pixels,
+followed by an |eoc| command. The characters appear in the order that \MF\
+generated them. If we ignore no-op commands (which are allowed between any
+two commands in the file), each |eoc| command is immediately followed by a
+|boc| command, or by a |post| command; in the latter case, there are no
+more characters in the file, and the remaining bytes form the postamble.
+Further details about the postamble will be explained later.
+
+Some parameters in \.{GF} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first file byte is number~0, then comes number~1, and so on.
+
+@ The \.{GF} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+relative instead of absolute. When a \.{GF}-reading program reads the
+commands for a character, it keeps track of two quantities: (a)~the current
+column number,~|m|; and (b)~the current row number,~|n|. These are 32-bit
+signed integers, although most actual font formats produced from \.{GF}
+files will need to curtail this vast range because of practical
+limitations. (\MF\ output will never allow $\vert m\vert$ or $\vert
+n\vert$ to get extremely large, but the \.{GF} format tries to be more general.)
+
+How do \.{GF}'s row and column numbers correspond to the conventions
+of \TeX\ and \MF? Well, the ``reference point'' of a character, in \TeX's
+view, is considered to be at the lower left corner of the pixel in row~0
+and column~0. This point is the intersection of the baseline with the left
+edge of the type; it corresponds to location $(0,0)$ in \MF\ programs.
+Thus the pixel in \.{GF} row~0 and column~0 is \MF's unit square, comprising the
+region of the plane whose coordinates both lie between 0 and~1. The
+pixel in \.{GF} row~|n| and column~|m| consists of the points whose \MF\
+coordinates |(x,y)| satisfy |m<=x<=m+1| and |n<=y<=n+1|. Negative values of
+|m| and~|x| correspond to columns of pixels {\sl left\/} of the reference
+point; negative values of |n| and~|y| correspond to rows of pixels {\sl
+below\/} the baseline.
+
+Besides |m| and |n|, there's also a third aspect of the current
+state, namely the @!|paint_switch|, which is always either |black| or
+|white|. Each \\{paint} command advances |m| by a specified amount~|d|,
+and blackens the intervening pixels if |paint_switch=black|; then
+the |paint_switch| changes to the opposite state. \.{GF}'s commands are
+designed so that |m| will never decrease within a row, and |n| will never
+increase within a character; hence there is no way to whiten a pixel that
+has been blackened.
+
+@ Here is a list of all the commands that may appear in a \.{GF} file. Each
+command is specified by its symbolic name (e.g., |boc|), its opcode byte
+(e.g., 67), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|d[2]|' means that parameter |d| is two bytes long.
+
+\yskip\hang|paint_0| 0. This is a \\{paint} command with |d=0|; it does
+nothing but change the |paint_switch| from \\{black} to \\{white} or vice~versa.
+
+\yskip\hang\\{paint\_1} through \\{paint\_63} (opcodes 1 to 63).
+These are \\{paint} commands with |d=1| to~63, defined as follows: If
+|paint_switch=black|, blacken |d|~pixels of the current row~|n|,
+in columns |m| through |m+d-1| inclusive. Then, in any case,
+complement the |paint_switch| and advance |m| by~|d|.
+
+\yskip\hang|paint1| 64 |d[1]|. This is a \\{paint} command with a specified
+value of~|d|; \MF\ uses it to paint when |64<=d<256|.
+
+\yskip\hang|@!paint2| 65 |d[2]|. Same as |paint1|, but |d|~can be as high
+as~65535.
+
+\yskip\hang|@!paint3| 66 |d[3]|. Same as |paint1|, but |d|~can be as high
+as $2^{24}-1$. \MF\ never needs this command, and it is hard to imagine
+anybody making practical use of it; surely a more compact encoding will be
+desirable when characters can be this large. But the command is there,
+anyway, just in case.
+
+\yskip\hang|boc| 67 |c[4]| |p[4]| |min_m[4]| |max_m[4]| |min_n[4]|
+|max_n[4]|. Beginning of a character: Here |c| is the character code, and
+|p| points to the previous character beginning (if any) for characters having
+this code number modulo 256. (The pointer |p| is |-1| if there was no
+prior character with an equivalent code.) The values of registers |m| and |n|
+defined by the instructions that follow for this character must
+satisfy |min_m<=m<=max_m| and |min_n<=n<=max_n|. (The values of |max_m| and
+|min_n| need not be the tightest bounds possible.) When a \.{GF}-reading
+program sees a |boc|, it can use |min_m|, |max_m|, |min_n|, and |max_n| to
+initialize the bounds of an array. Then it sets |m:=min_m|, |n:=max_n|, and
+|paint_switch:=white|.
+
+\yskip\hang|boc1| 68 |c[1]| |@!del_m[1]| |max_m[1]| |@!del_n[1]| |max_n[1]|.
+Same as |boc|, but |p| is assumed to be~$-1$; also |del_m=max_m-min_m|
+and |del_n=max_n-min_n| are given instead of |min_m| and |min_n|.
+The one-byte parameters must be between 0 and 255, inclusive.
+\ (This abbreviated |boc| saves 19~bytes per character, in common cases.)
+
+\yskip\hang|eoc| 69. End of character: All pixels blackened so far
+constitute the pattern for this character. In particular, a completely
+blank character might have |eoc| immediately following |boc|.
+
+\yskip\hang|skip0| 70. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=white|. \ (This finishes one row and begins another,
+ready to whiten the leftmost pixel in the new row.)
+
+\yskip\hang|skip1| 71 |d[1]|. Decrease |n| by |d+1|, set |m:=min_m|, and set
+|paint_switch:=white|. This is a way to produce |d| all-white rows.
+
+\yskip\hang|@!skip2| 72 |d[2]|. Same as |skip1|, but |d| can be as large
+as 65535.
+
+\yskip\hang|@!skip3| 73 |d[3]|. Same as |skip1|, but |d| can be as large
+as $2^{24}-1$. \MF\ obviously never needs this command.
+
+\yskip\hang|new_row_0| 74. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=black|. \ (This finishes one row and begins another,
+ready to {\sl blacken\/} the leftmost pixel in the new row.)
+
+\yskip\hang|@!new_row_1| through |@!new_row_164| (opcodes 75 to 238). Same as
+|new_row_0|, but with |m:=min_m+1| through |min_m+164|, respectively.
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |no_op| unless special \.{GF}-reading
+programs are being used. \MF\ generates \\{xxx} commands when encountering
+a \&{special} string; this occurs in the \.{GF} file only between
+characters, after the preamble, and before the postamble. However,
+\\{xxx} commands might appear within characters,
+in \.{GF} files generated by other
+processors. It is recommended that |x| be a string having the form of a
+keyword followed by possible parameters relevant to that keyword.
+
+\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+\MF\ uses this when sending a \&{special} string whose length exceeds~255.
+
+\yskip\hang|@!xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be
+ridiculously large; |k| mustn't be negative.
+
+\yskip\hang|yyy| 243 |y[4]|. This command is undefined in general;
+it functions as a 5-byte |no_op| unless special \.{GF}-reading programs
+are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a
+result of \&{numspecial} commands; the intent is to provide numeric
+parameters to \\{xxx} commands that immediately precede.
+
+\yskip\hang|@!no_op| 244. No operation, do nothing. Any number of |no_op|'s
+may occur between \.{GF} commands, but a |no_op| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|char_loc| 245 |c[1]| |dx[4]| |dy[4]| |w[4]| |p[4]|.
+This command will appear only in the postamble, which will be explained shortly.
+
+\yskip\hang|@!char_loc0| 246 |c[1]| |@!dm[1]| |w[4]| |p[4]|.
+Same as |char_loc|, except that |dy| is assumed to be zero, and the value
+of~|dx| is taken to be |65536*dm|, where |0<=dm<256|.
+
+\yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameter |i| is an identifying number for \.{GF} format, currently
+131. The other information is merely commentary; it is not given
+special interpretation like \\{xxx} commands are. (Note that \\{xxx}
+commands may immediately follow the preamble, before the first |boc|.)
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@d gf_id_byte=131 {identifies the kind of \.{GF} files described here}
+
+@ \MF\ refers to the following opcodes explicitly.
+
+@d paint_0=0 {beginning of the \\{paint} commands}
+@d paint1=64 {move right a given number of columns, then
+ black${}\swap{}$white}
+@d boc=67 {beginning of a character}
+@d boc1=68 {short form of |boc|}
+@d eoc=69 {end of a character}
+@d skip0=70 {skip no blank rows}
+@d skip1=71 {skip over blank rows}
+@d new_row_0=74 {move down one row and then right}
+@d max_new_row=164 {the largest \\{new\_row} command is |new_row_164|}
+@d xxx1=239 {for \&{special} strings}
+@d xxx3=241 {for long \&{special} strings}
+@d yyy=243 {for \&{numspecial} numbers}
+@d char_loc=245 {character locators in the postamble}
+@d pre=247 {preamble}
+@d post=248 {postamble beginning}
+@d post_post=249 {postamble ending}
+
+@ The last character in a \.{GF} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \MF\ has
+accumulated. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |@!ds[4]| |@!cs[4]| |@!hppp[4]| |@!vppp[4]|
+ |@!min_m[4]| |@!max_m[4]| |@!min_n[4]| |@!max_n[4]|\cr
+ $\langle\,$character locators$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the byte following the final |eoc| in the file
+(or to the byte following the preamble, if there are no characters);
+it can be used to locate the beginning of \\{xxx} commands
+that might have preceded the postamble. The |ds| and |cs| parameters
+@^design size@> @^check sum@>
+give the design size and check sum, respectively, which are exactly the
+values put into the header of the \.{TFM} file that \MF\ produces (or
+would produce) on this run. Parameters |hppp| and |vppp| are the ratios of
+pixels per point, horizontally and vertically, expressed as |scaled| integers
+(i.e., multiplied by $2^{16}$); they can be used to correlate the font
+with specific device resolutions, magnifications, and ``at sizes.'' Then
+come |min_m|, |max_m|, |min_n|, and |max_n|, which bound the values that
+registers |m| and~|n| assume in all characters in this \.{GF} file.
+(These bounds need not be the best possible; |max_m| and |min_n| may, on the
+other hand, be tighter than the similar bounds in |boc| commands. For
+example, some character may have |min_n=-100| in its |boc|, but it might
+turn out that |n| never gets lower than |-50| in any character; then
+|min_n| can have any value |<=-50|. If there are no characters in the file,
+it's possible to have |min_m>max_m| and/or |min_n>max_n|.)
+
+@ Character locators are introduced by |char_loc| commands,
+which specify a character residue~|c|, character escapements (|dx,dy|),
+a character width~|w|, and a pointer~|p|
+to the beginning of that character. (If two or more characters have the
+same code~|c| modulo 256, only the last will be indicated; the others can be
+located by following backpointers. Characters whose codes differ by a
+multiple of 256 are assumed to share the same font metric information,
+hence the \.{TFM} file contains only residues of character codes modulo~256.
+This convention is intended for oriental languages, when there are many
+character shapes but few distinct widths.)
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+The character escapements (|dx,dy|) are the values of \MF's \&{chardx}
+and \&{chardy} parameters; they are in units of |scaled| pixels;
+i.e., |dx| is in horizontal pixel units times $2^{16}$, and |dy| is in
+vertical pixel units times $2^{16}$. This is the intended amount of
+displacement after typesetting the character; for \.{DVI} files, |dy|
+should be zero, but other document file formats allow nonzero vertical
+escapement.
+
+The character width~|w| duplicates the information in the \.{TFM} file; it
+is a |fix_word| value relative to the design size, and it should be
+independent of magnification.
+
+The backpointer |p| points to the character's |boc|, or to the first of
+a sequence of consecutive \\{xxx} or |yyy| or |no_op| commands that
+immediately precede the |boc|, if such commands exist; such ``special''
+commands essentially belong to the characters, while the special commands
+after the final character belong to the postamble (i.e., to the font
+as a whole). This convention about |p| applies also to the backpointers
+in |boc| commands, even though it wasn't explained in the description
+of~|boc|. @^backpointers@>
+
+Pointer |p| might be |-1| if the character exists in the \.{TFM} file
+but not in the \.{GF} file. This unusual situation can arise in \MF\ output
+if the user had |proofing<0| when the character was being shipped out,
+but then made |proofing>=0| in order to get a \.{GF} file.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the character locators, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~131, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., @'337 in octal). \MF\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{GF} file makes it feasible for
+\.{GF}-reading programs to find the postamble first, on most computers,
+even though \MF\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{GF} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{GF} reader can discover all the information needed for individual characters.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{GF}
+format has been designed to work most efficiently with modern operating systems.
+But if \.{GF} files have to be processed under the restrictions of standard
+\PASCAL, one can simply read them from front to back. This will
+be adequate for most applications. However, the postamble-first approach
+would facilitate a program that merges two \.{GF} files, replacing data
+from one that is overridden by corresponding data in the other.
+
+@* \[47] Shipping characters out.
+The |ship_out| procedure, to be described below, is given a pointer to
+an edge structure. Its mission is to describe the positive pixels
+in \.{GF} form, outputting a ``character'' to |gf_file|.
+
+Several global variables hold information about the font file as a whole:\
+|gf_min_m|, |gf_max_m|, |gf_min_n|, and |gf_max_n| are the minimum and
+maximum \.{GF} coordinates output so far; |gf_prev_ptr| is the byte number
+following the preamble or the last |eoc| command in the output;
+|total_chars| is the total number of characters (i.e., |boc..eoc| segments)
+shipped out. There's also an array, |char_ptr|, containing the starting
+positions of each character in the file, as required for the postamble. If
+character code~|c| has not yet been output, |char_ptr[c]=-1|.
+
+@<Glob...@>=
+@!gf_min_m,@!gf_max_m,@!gf_min_n,@!gf_max_n:integer; {bounding rectangle}
+@!gf_prev_ptr:integer; {where the present/next character started/starts}
+@!total_chars:integer; {the number of characters output so far}
+@!char_ptr:array[eight_bits] of integer; {where individual characters started}
+@!gf_dx,@!gf_dy:array[eight_bits] of integer; {device escapements}
+
+@ @<Set init...@>=
+gf_prev_ptr:=0; total_chars:=0;
+
+@ The \.{GF} bytes are output to a buffer instead of being sent
+byte-by-byte to |gf_file|, because this tends to save a lot of
+subroutine-call overhead. \MF\ uses the same conventions for |gf_file|
+as \TeX\ uses for its \\{dvi\_file}; hence if system-dependent
+changes are needed, they should probably be the same for both programs.
+
+The output buffer is divided into two parts of equal size; the bytes found
+in |gf_buf[0..half_buf-1]| constitute the first half, and those in
+|gf_buf[half_buf..gf_buf_size-1]| constitute the second. The global
+variable |gf_ptr| points to the position that will receive the next
+output byte. When |gf_ptr| reaches |gf_limit|, which is always equal
+to one of the two values |half_buf| or |gf_buf_size|, the half buffer that
+is about to be invaded next is sent to the output and |gf_limit| is
+changed to its other value. Thus, there is always at least a half buffer's
+worth of information present, except at the very beginning of the job.
+
+Bytes of the \.{GF} file are numbered sequentially starting with 0;
+the next byte to be generated will be number |gf_offset+gf_ptr|.
+
+@<Types...@>=
+@!gf_index=0..gf_buf_size; {an index into the output buffer}
+
+@ Some systems may find it more efficient to make |gf_buf| a |packed|
+array, since output of four bytes at once may be facilitated.
+@^system dependencies@>
+
+@<Glob...@>=
+@!gf_buf:array[gf_index] of eight_bits; {buffer for \.{GF} output}
+@!half_buf:gf_index; {half of |gf_buf_size|}
+@!gf_limit:gf_index; {end of the current half buffer}
+@!gf_ptr:gf_index; {the next available buffer address}
+@!gf_offset:integer; {|gf_buf_size| times the number of times the
+ output buffer has been fully emptied}
+
+@ Initially the buffer is all in one piece; we will output half of it only
+after it first fills up.
+
+@<Set init...@>=
+half_buf:=gf_buf_size div 2; gf_limit:=gf_buf_size; gf_ptr:=0;
+gf_offset:=0;
+
+@ The actual output of |gf_buf[a..b]| to |gf_file| is performed by calling
+|write_gf(a,b)|. It is safe to assume that |a| and |b+1| will both be
+multiples of 4 when |write_gf(a,b)| is called; therefore it is possible on
+many machines to use efficient methods to pack four bytes per word and to
+output an array of words with one system call.
+@^system dependencies@>
+
+@<Declare generic font output procedures@>=
+procedure write_gf(@!a,@!b:gf_index);
+var k:gf_index;
+begin for k:=a to b do write(gf_file,gf_buf[k]);
+end;
+
+@ To put a byte in the buffer without paying the cost of invoking a procedure
+each time, we use the macro |gf_out|.
+
+@d gf_out(#)==@+begin gf_buf[gf_ptr]:=#; incr(gf_ptr);
+ if gf_ptr=gf_limit then gf_swap;
+ end
+
+@<Declare generic font output procedures@>=
+procedure gf_swap; {outputs half of the buffer}
+begin if gf_limit=gf_buf_size then
+ begin write_gf(0,half_buf-1); gf_limit:=half_buf;
+ gf_offset:=gf_offset+gf_buf_size; gf_ptr:=0;
+ end
+else begin write_gf(half_buf,gf_buf_size-1); gf_limit:=gf_buf_size;
+ end;
+end;
+
+@ Here is how we clean out the buffer when \MF\ is all through; |gf_ptr|
+will be a multiple of~4.
+
+@<Empty the last bytes out of |gf_buf|@>=
+if gf_limit=half_buf then write_gf(half_buf,gf_buf_size-1);
+if gf_ptr>0 then write_gf(0,gf_ptr-1)
+
+@ The |gf_four| procedure outputs four bytes in two's complement notation,
+without risking arithmetic overflow.
+
+@<Declare generic font output procedures@>=
+procedure gf_four(@!x:integer);
+begin if x>=0 then gf_out(x div three_bytes)
+else begin x:=x+@'10000000000;
+ x:=x+@'10000000000;
+ gf_out((x div three_bytes) + 128);
+ end;
+x:=x mod three_bytes; gf_out(x div unity);
+x:=x mod unity; gf_out(x div @'400);
+gf_out(x mod @'400);
+end;
+
+@ Of course, it's even easier to output just two or three bytes.
+
+@<Declare generic font output procedures@>=
+procedure gf_two(@!x:integer);
+begin gf_out(x div @'400); gf_out(x mod @'400);
+end;
+@#
+procedure gf_three(@!x:integer);
+begin gf_out(x div unity); gf_out((x mod unity) div @'400);
+gf_out(x mod @'400);
+end;
+
+@ We need a simple routine to generate a \\{paint}
+command of the appropriate type.
+
+@<Declare generic font output procedures@>=
+procedure gf_paint(@!d:integer); {here |0<=d<65536|}
+begin if d<64 then gf_out(paint_0+d)
+else if d<256 then
+ begin gf_out(paint1); gf_out(d);
+ end
+else begin gf_out(paint1+1); gf_two(d);
+ end;
+end;
+
+@ And |gf_string| outputs one or two strings. If the first string number
+is nonzero, an \\{xxx} command is generated.
+
+@<Declare generic font output procedures@>=
+procedure gf_string(@!s,@!t:str_number);
+var @!k:pool_pointer;
+@!l:integer; {length of the strings to output}
+begin if s<>0 then
+ begin l:=length(s);
+ if t<>0 then l:=l+length(t);
+ if l<=255 then
+ begin gf_out(xxx1); gf_out(l);
+ end
+ else begin gf_out(xxx3); gf_three(l);
+ end;
+ for k:=str_start[s] to str_start[s+1]-1 do gf_out(so(str_pool[k]));
+ end;
+if t<>0 then for k:=str_start[t] to str_start[t+1]-1 do gf_out(so(str_pool[k]));
+end;
+
+@ The choice between |boc| commands is handled by |gf_boc|.
+
+@d one_byte(#)== #>=0 then if #<256
+
+@<Declare generic font output procedures@>=
+procedure gf_boc(@!min_m,@!max_m,@!min_n,@!max_n:integer);
+label exit;
+begin if min_m<gf_min_m then gf_min_m:=min_m;
+if max_n>gf_max_n then gf_max_n:=max_n;
+if boc_p=-1 then if one_byte(boc_c) then
+ if one_byte(max_m-min_m) then if one_byte(max_m) then
+ if one_byte(max_n-min_n) then if one_byte(max_n) then
+ begin gf_out(boc1); gf_out(boc_c);@/
+ gf_out(max_m-min_m); gf_out(max_m);
+ gf_out(max_n-min_n); gf_out(max_n); return;
+ end;
+gf_out(boc); gf_four(boc_c); gf_four(boc_p);@/
+gf_four(min_m); gf_four(max_m); gf_four(min_n); gf_four(max_n);
+exit: end;
+
+@ Two of the parameters to |gf_boc| are global.
+
+@<Glob...@>=
+@!boc_c,@!boc_p:integer; {parameters of the next |boc| command}
+
+@ Here is a routine that gets a \.{GF} file off to a good start.
+
+@d check_gf==@t@>@+if output_file_name=0 then init_gf
+
+@<Declare generic font output procedures@>=
+procedure init_gf;
+var @!k:eight_bits; {runs through all possible character codes}
+@!t:integer; {the time of this run}
+begin gf_min_m:=4096; gf_max_m:=-4096; gf_min_n:=4096; gf_max_n:=-4096;
+for k:=0 to 255 do char_ptr[k]:=-1;
+@<Determine the file extension, |gf_ext|@>;
+set_output_file_name;
+gf_out(pre); gf_out(gf_id_byte); {begin to output the preamble}
+old_setting:=selector; selector:=new_string; print(" METAFONT output ");
+print_int(round_unscaled(internal[year])); print_char(".");
+print_dd(round_unscaled(internal[month])); print_char(".");
+print_dd(round_unscaled(internal[day])); print_char(":");@/
+t:=round_unscaled(internal[time]);
+print_dd(t div 60); print_dd(t mod 60);@/
+selector:=old_setting; gf_out(cur_length);
+gf_string(0,make_string); decr(str_ptr);
+pool_ptr:=str_start[str_ptr]; {flush that string from memory}
+gf_prev_ptr:=gf_offset+gf_ptr;
+end;
+
+@ @<Determine the file extension...@>=
+if internal[hppp]<=0 then gf_ext:=".gf"
+else begin old_setting:=selector; selector:=new_string; print_char(".");
+ print_int(make_scaled(internal[hppp],59429463));
+ {$2^{32}/72.27\approx59429463.07$}
+ print("gf"); gf_ext:=make_string; selector:=old_setting;
+ end
+
+@ With those preliminaries out of the way, |ship_out| is not especially
+difficult.
+
+@<Declare generic font output procedures@>=
+procedure ship_out(@!c:eight_bits);
+label done;
+var @!f:integer; {current character extension}
+@!prev_m,@!m,@!mm:integer; {previous and current pixel column numbers}
+@!prev_n,@!n:integer; {previous and current pixel row numbers}
+@!p,@!q:pointer; {for list traversal}
+@!prev_w,@!w,@!ww:integer; {old and new weights}
+@!d:integer; {data from edge-weight node}
+@!delta:integer; {number of rows to skip}
+@!cur_min_m:integer; {starting column, relative to the current offset}
+@!x_off,@!y_off:integer; {offsets, rounded to integers}
+begin check_gf; f:=round_unscaled(internal[char_ext]);@/
+x_off:=round_unscaled(internal[x_offset]);
+y_off:=round_unscaled(internal[y_offset]);
+if term_offset>max_print_line-9 then print_ln
+else if (term_offset>0)or(file_offset>0) then print_char(" ");
+print_char("["); print_int(c);
+if f<>0 then
+ begin print_char("."); print_int(f);
+ end;
+update_terminal;
+boc_c:=256*f+c; boc_p:=char_ptr[c]; char_ptr[c]:=gf_prev_ptr;@/
+if internal[proofing]>0 then @<Send nonzero offsets to the output file@>;
+@<Output the character represented in |cur_edges|@>;
+gf_out(eoc); gf_prev_ptr:=gf_offset+gf_ptr; incr(total_chars);
+print_char("]"); update_terminal; {progress report}
+if internal[tracing_output]>0 then
+ print_edges(" (just shipped out)",true,x_off,y_off);
+end;
+
+@ @<Send nonzero offsets to the output file@>=
+begin if x_off<>0 then
+ begin gf_string("xoffset",0); gf_out(yyy); gf_four(x_off*unity);
+ end;
+if y_off<>0 then
+ begin gf_string("yoffset",0); gf_out(yyy); gf_four(y_off*unity);
+ end;
+end
+
+@ @<Output the character represented in |cur_edges|@>=
+prev_n:=4096; p:=knil(cur_edges); n:=n_max(cur_edges)-zero_field;
+while p<>cur_edges do
+ begin @<Output the pixels of edge row |p| to font row |n|@>;
+ p:=knil(p); decr(n);
+ end;
+if prev_n=4096 then @<Finish off an entirely blank character@>
+else if prev_n+y_off<gf_min_n then
+ gf_min_n:=prev_n+y_off
+
+@ @<Finish off an entirely blank...@>=
+begin gf_boc(0,0,0,0);
+if gf_max_m<0 then gf_max_m:=0;
+if gf_min_n>0 then gf_min_n:=0;
+end
+
+@ In this loop, |prev_w| represents the weight at column |prev_m|, which is
+the most recent column reflected in the output so far; |w| represents the
+weight at column~|m|, which is the most recent column in the edge data.
+Several edges might cancel at the same column position, so we need to
+look ahead to column~|mm| before actually outputting anything.
+
+@<Output the pixels of edge row |p| to font row |n|@>=
+if unsorted(p)>void then sort_edges(p);
+q:=sorted(p); w:=0; prev_m:=-fraction_one; {$|fraction_one|\approx\infty$}
+ww:=0; prev_w:=0; m:=prev_m;
+repeat if q=sentinel then mm:=fraction_one
+else begin d:=ho(info(q)); mm:=d div 8; ww:=ww+(d mod 8)-zero_w;
+ end;
+if mm<>m then
+ begin if prev_w<=0 then
+ begin if w>0 then @<Start black at $(m,n)$@>;
+ end
+ else if w<=0 then @<Stop black at $(m,n)$@>;
+ m:=mm;
+ end;
+w:=ww; q:=link(q);
+until mm=fraction_one;
+if w<>0 then {this should be impossible}
+ print_nl("(There's unbounded black in character shipped out!)");
+@.There's unbounded black...@>
+if prev_m-m_offset(cur_edges)+x_off>gf_max_m then
+ gf_max_m:=prev_m-m_offset(cur_edges)+x_off
+
+
+@ @<Start black at $(m,n)$@>=
+begin if prev_m=-fraction_one then @<Start a new row at $(m,n)$@>
+else gf_paint(m-prev_m);
+prev_m:=m; prev_w:=w;
+end
+
+@ @<Stop black at $(m,n)$@>=
+begin gf_paint(m-prev_m); prev_m:=m; prev_w:=w;
+end
+
+@ @<Start a new row at $(m,n)$@>=
+begin if prev_n=4096 then
+ begin gf_boc(m_min(cur_edges)+x_off-zero_field,
+ m_max(cur_edges)+x_off-zero_field,@|
+ n_min(cur_edges)+y_off-zero_field,n+y_off);
+ cur_min_m:=m_min(cur_edges)-zero_field+m_offset(cur_edges);
+ end
+else if prev_n>n+1 then @<Skip down |prev_n-n| rows@>
+else @<Skip to column $m$ in the next row and |goto done|, or skip zero rows@>;
+gf_paint(m-cur_min_m); {skip to column $m$, painting white}
+done:prev_n:=n;
+end
+
+@ @<Skip to column $m$ in the next row...@>=
+begin delta:=m-cur_min_m;
+if delta>max_new_row then gf_out(skip0)
+else begin gf_out(new_row_0+delta); goto done;
+ end;
+end
+
+@ @<Skip down...@>=
+begin delta:=prev_n-n-1;
+if delta<@'400 then
+ begin gf_out(skip1); gf_out(delta);
+ end
+else begin gf_out(skip1+1); gf_two(delta);
+ end;
+end
+
+@ Now that we've finished |ship_out|, let's look at the other commands
+by which a user can send things to the \.{GF} file.
+
+@<Cases of |do_statement|...@>=
+special_command: do_special;
+
+@ @<Put each...@>=
+primitive("special",special_command,string_type);@/
+@!@:special_}{\&{special} primitive@>
+primitive("numspecial",special_command,known);@/
+@!@:num_special_}{\&{numspecial} primitive@>
+
+@ @<Declare action procedures for use by |do_statement|@>=
+procedure do_special;
+var @!m:small_number; {either |string_type| or |known|}
+begin m:=cur_mod; get_x_next; scan_expression;
+if internal[proofing]>=0 then
+ if cur_type<>m then @<Complain about improper special operation@>
+ else begin check_gf;
+ if m=string_type then gf_string(cur_exp,0)
+ else begin gf_out(yyy); gf_four(cur_exp);
+ end;
+ end;
+flush_cur_exp(0);
+end;
+
+@ @<Complain about improper special operation@>=
+begin exp_err("Unsuitable expression");
+@.Unsuitable expression@>
+help1("The expression shown above has the wrong type to be output.");
+put_get_error;
+end
+
+@ @<Send the current expression as a title to the output file@>=
+begin check_gf; gf_string("title ",cur_exp);
+@.title@>
+end
+
+@ @<Cases of |print_cmd...@>=
+special_command:if m=known then print("numspecial")
+ else print("special");
+
+@ @<Determine if a character has been shipped out@>=
+begin cur_exp:=round_unscaled(cur_exp) mod 256;
+if cur_exp<0 then cur_exp:=cur_exp+256;
+boolean_reset(char_exists[cur_exp]); cur_type:=boolean_type;
+end
+
+@ At the end of the program we must finish things off by writing the postamble.
+The \.{TFM} information should have been computed first.
+
+An integer variable |k| and a |scaled| variable |x| will be declared for
+use by this routine.
+
+@<Finish the \.{GF} file@>=
+begin gf_out(post); {beginning of the postamble}
+gf_four(gf_prev_ptr); gf_prev_ptr:=gf_offset+gf_ptr-5; {|post| location}
+gf_four(internal[design_size]*16);
+for k:=1 to 4 do gf_out(header_byte[k]); {the check sum}
+gf_four(internal[hppp]);
+gf_four(internal[vppp]);@/
+gf_four(gf_min_m); gf_four(gf_max_m);
+gf_four(gf_min_n); gf_four(gf_max_n);
+for k:=0 to 255 do if char_exists[k] then
+ begin x:=gf_dx[k] div unity;
+ if (gf_dy[k]=0)and(x>=0)and(x<256)and(gf_dx[k]=x*unity) then
+ begin gf_out(char_loc+1); gf_out(k); gf_out(x);
+ end
+ else begin gf_out(char_loc); gf_out(k);
+ gf_four(gf_dx[k]); gf_four(gf_dy[k]);
+ end;
+ x:=value(tfm_width[k]);
+ if abs(x)>max_tfm_dimen then
+ if x>0 then x:=three_bytes-1@+else x:=1-three_bytes
+ else x:=make_scaled(x*16,internal[design_size]);
+ gf_four(x); gf_four(char_ptr[k]);
+ end;
+gf_out(post_post); gf_four(gf_prev_ptr); gf_out(gf_id_byte);@/
+k:=4+((gf_buf_size-gf_ptr) mod 4); {the number of 223's}
+while k>0 do
+ begin gf_out(223); decr(k);
+ end;
+@<Empty the last bytes out of |gf_buf|@>;
+print_nl("Output written on "); slow_print(output_file_name);
+@.Output written...@>
+print(" ("); print_int(total_chars); print(" character");
+if total_chars<>1 then print_char("s");
+print(", "); print_int(gf_offset+gf_ptr); print(" bytes).");
+b_close(gf_file);
+end
+
+@* \[48] Dumping and undumping the tables.
+After \.{INIMF} has seen a collection of macros, it
+can write all the necessary information on an auxiliary file so
+that production versions of \MF\ are able to initialize their
+memory at high speed. The present section of the program takes
+care of such output and input. We shall consider simultaneously
+the processes of storing and restoring,
+so that the inverse relation between them is clear.
+@.INIMF@>
+
+The global variable |base_ident| is a string that is printed right
+after the |banner| line when \MF\ is ready to start. For \.{INIMF} this
+string says simply `\.{(INIMF)}'; for other versions of \MF\ it says,
+for example, `\.{(preloaded base=plain 1984.2.29)}', showing the year,
+month, and day that the base file was created. We have |base_ident=0|
+before \MF's tables are loaded.
+
+@<Glob...@>=
+@!base_ident:str_number;
+
+@ @<Set init...@>=
+base_ident:=0;
+
+@ @<Initialize table entries...@>=
+base_ident:=" (INIMF)";
+
+@ @<Declare act...@>=
+@!init procedure store_base_file;
+var @!k:integer; {all-purpose index}
+@!p,@!q: pointer; {all-purpose pointers}
+@!x: integer; {something to dump}
+@!w: four_quarters; {four ASCII codes}
+begin @<Create the |base_ident|, open the base file,
+ and inform the user that dumping has begun@>;
+@<Dump constants for consistency check@>;
+@<Dump the string pool@>;
+@<Dump the dynamic memory@>;
+@<Dump the table of equivalents and the hash table@>;
+@<Dump a few more things and the closing check word@>;
+@<Close the base file@>;
+end;
+tini
+
+@ Corresponding to the procedure that dumps a base file, we also have a function
+that reads~one~in. The function returns |false| if the dumped base is
+incompatible with the present \MF\ table sizes, etc.
+
+@d off_base=6666 {go here if the base file is unacceptable}
+@d too_small(#)==begin wake_up_terminal;
+ wterm_ln('---! Must increase the ',#);
+@.Must increase the x@>
+ goto off_base;
+ end
+
+@p @t\4@>@<Declare the function called |open_base_file|@>@;
+function load_base_file:boolean;
+label off_base,exit;
+var @!k:integer; {all-purpose index}
+@!p,@!q: pointer; {all-purpose pointers}
+@!x: integer; {something undumped}
+@!w: four_quarters; {four ASCII codes}
+begin @<Undump constants for consistency check@>;
+@<Undump the string pool@>;
+@<Undump the dynamic memory@>;
+@<Undump the table of equivalents and the hash table@>;
+@<Undump a few more things and the closing check word@>;
+load_base_file:=true; return; {it worked!}
+off_base: wake_up_terminal;
+ wterm_ln('(Fatal base file error; I''m stymied)');
+@.Fatal base file error@>
+load_base_file:=false;
+exit:end;
+
+@ Base files consist of |memory_word| items, and we use the following
+macros to dump words of different types:
+
+@d dump_wd(#)==begin base_file^:=#; put(base_file);@+end
+@d dump_int(#)==begin base_file^.int:=#; put(base_file);@+end
+@d dump_hh(#)==begin base_file^.hh:=#; put(base_file);@+end
+@d dump_qqqq(#)==begin base_file^.qqqq:=#; put(base_file);@+end
+
+@<Glob...@>=
+@!base_file:word_file; {for input or output of base information}
+
+@ The inverse macros are slightly more complicated, since we need to check
+the range of the values we are reading in. We say `|undump(a)(b)(x)|' to
+read an integer value |x| that is supposed to be in the range |a<=x<=b|.
+
+@d undump_wd(#)==begin get(base_file); #:=base_file^;@+end
+@d undump_int(#)==begin get(base_file); #:=base_file^.int;@+end
+@d undump_hh(#)==begin get(base_file); #:=base_file^.hh;@+end
+@d undump_qqqq(#)==begin get(base_file); #:=base_file^.qqqq;@+end
+@d undump_end_end(#)==#:=x;@+end
+@d undump_end(#)==(x>#) then goto off_base@+else undump_end_end
+@d undump(#)==begin undump_int(x); if (x<#) or undump_end
+@d undump_size_end_end(#)==too_small(#)@+else undump_end_end
+@d undump_size_end(#)==if x># then undump_size_end_end
+@d undump_size(#)==begin undump_int(x);
+ if x<# then goto off_base; undump_size_end
+
+@ The next few sections of the program should make it clear how we use the
+dump/undump macros.
+
+@<Dump constants for consistency check@>=
+dump_int(@$);@/
+dump_int(mem_min);@/
+dump_int(mem_top);@/
+dump_int(hash_size);@/
+dump_int(hash_prime);@/
+dump_int(max_in_open)
+
+@ Sections of a \.{WEB} program that are ``commented out'' still contribute
+strings to the string pool; therefore \.{INIMF} and \MF\ will have
+the same strings. (And it is, of course, a good thing that they do.)
+@.WEB@>
+@^string pool@>
+
+@<Undump constants for consistency check@>=
+x:=base_file^.int;
+if x<>@$ then goto off_base; {check that strings are the same}
+undump_int(x);
+if x<>mem_min then goto off_base;
+undump_int(x);
+if x<>mem_top then goto off_base;
+undump_int(x);
+if x<>hash_size then goto off_base;
+undump_int(x);
+if x<>hash_prime then goto off_base;
+undump_int(x);
+if x<>max_in_open then goto off_base
+
+@ @d dump_four_ASCII==
+ w.b0:=qi(so(str_pool[k])); w.b1:=qi(so(str_pool[k+1]));
+ w.b2:=qi(so(str_pool[k+2])); w.b3:=qi(so(str_pool[k+3]));
+ dump_qqqq(w)
+
+@<Dump the string pool@>=
+dump_int(pool_ptr);
+dump_int(str_ptr);
+for k:=0 to str_ptr do dump_int(str_start[k]);
+k:=0;
+while k+4<pool_ptr do
+ begin dump_four_ASCII; k:=k+4;
+ end;
+k:=pool_ptr-4; dump_four_ASCII;
+print_ln; print_int(str_ptr); print(" strings of total length ");
+print_int(pool_ptr)
+
+@ @d undump_four_ASCII==
+ undump_qqqq(w);
+ str_pool[k]:=si(qo(w.b0)); str_pool[k+1]:=si(qo(w.b1));
+ str_pool[k+2]:=si(qo(w.b2)); str_pool[k+3]:=si(qo(w.b3))
+
+@<Undump the string pool@>=
+undump_size(0)(pool_size)('string pool size')(pool_ptr);
+undump_size(0)(max_strings)('max strings')(str_ptr);
+for k:=0 to str_ptr do
+ begin undump(0)(pool_ptr)(str_start[k]); str_ref[k]:=max_str_ref;
+ end;
+k:=0;
+while k+4<pool_ptr do
+ begin undump_four_ASCII; k:=k+4;
+ end;
+k:=pool_ptr-4; undump_four_ASCII;
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;
+max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr
+
+@ By sorting the list of available spaces in the variable-size portion of
+|mem|, we are usually able to get by without having to dump very much
+of the dynamic memory.
+
+We recompute |var_used| and |dyn_used|, so that \.{INIMF} dumps valid
+information even when it has not been gathering statistics.
+
+@<Dump the dynamic memory@>=
+sort_avail; var_used:=0;
+dump_int(lo_mem_max); dump_int(rover);
+p:=mem_min; q:=rover; x:=0;
+repeat for k:=p to q+1 do dump_wd(mem[k]);
+x:=x+q+2-p; var_used:=var_used+q-p;
+p:=q+node_size(q); q:=rlink(q);
+until q=rover;
+var_used:=var_used+lo_mem_max-p; dyn_used:=mem_end+1-hi_mem_min;@/
+for k:=p to lo_mem_max do dump_wd(mem[k]);
+x:=x+lo_mem_max+1-p;
+dump_int(hi_mem_min); dump_int(avail);
+for k:=hi_mem_min to mem_end do dump_wd(mem[k]);
+x:=x+mem_end+1-hi_mem_min;
+p:=avail;
+while p<>null do
+ begin decr(dyn_used); p:=link(p);
+ end;
+dump_int(var_used); dump_int(dyn_used);
+print_ln; print_int(x);
+print(" memory locations dumped; current usage is ");
+print_int(var_used); print_char("&"); print_int(dyn_used)
+
+@ @<Undump the dynamic memory@>=
+undump(lo_mem_stat_max+1000)(hi_mem_stat_min-1)(lo_mem_max);
+undump(lo_mem_stat_max+1)(lo_mem_max)(rover);
+p:=mem_min; q:=rover;
+repeat for k:=p to q+1 do undump_wd(mem[k]);
+p:=q+node_size(q);
+if (p>lo_mem_max)or((q>=rlink(q))and(rlink(q)<>rover)) then goto off_base;
+q:=rlink(q);
+until q=rover;
+for k:=p to lo_mem_max do undump_wd(mem[k]);
+undump(lo_mem_max+1)(hi_mem_stat_min)(hi_mem_min);
+undump(null)(mem_top)(avail); mem_end:=mem_top;
+for k:=hi_mem_min to mem_end do undump_wd(mem[k]);
+undump_int(var_used); undump_int(dyn_used)
+
+@ A different scheme is used to compress the hash table, since its lower region
+is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output three
+words: |p|, |hash[p]|, and |eqtb[p]|. The hash table is, of course, densely
+packed for |p>=hash_used|, so the remaining entries are output in~a~block.
+
+@<Dump the table of equivalents and the hash table@>=
+dump_int(hash_used); st_count:=frozen_inaccessible-1-hash_used;
+for p:=1 to hash_used do if text(p)<>0 then
+ begin dump_int(p); dump_hh(hash[p]); dump_hh(eqtb[p]); incr(st_count);
+ end;
+for p:=hash_used+1 to hash_end do
+ begin dump_hh(hash[p]); dump_hh(eqtb[p]);
+ end;
+dump_int(st_count);@/
+print_ln; print_int(st_count); print(" symbolic tokens")
+
+@ @<Undump the table of equivalents and the hash table@>=
+undump(1)(frozen_inaccessible)(hash_used); p:=0;
+repeat undump(p+1)(hash_used)(p); undump_hh(hash[p]); undump_hh(eqtb[p]);
+until p=hash_used;
+for p:=hash_used+1 to hash_end do
+ begin undump_hh(hash[p]); undump_hh(eqtb[p]);
+ end;
+undump_int(st_count)
+
+@ We have already printed a lot of statistics, so we set |tracing_stats:=0|
+to prevent them from appearing again.
+
+@<Dump a few more things and the closing check word@>=
+dump_int(int_ptr);
+for k:=1 to int_ptr do
+ begin dump_int(internal[k]); dump_int(int_name[k]);
+ end;
+dump_int(start_sym); dump_int(interaction); dump_int(base_ident);
+dump_int(bg_loc); dump_int(eg_loc); dump_int(serial_no); dump_int(69069);
+internal[tracing_stats]:=0
+
+@ @<Undump a few more things and the closing check word@>=
+undump(max_given_internal)(max_internal)(int_ptr);
+for k:=1 to int_ptr do
+ begin undump_int(internal[k]);
+ undump(0)(str_ptr)(int_name[k]);
+ end;
+undump(0)(frozen_inaccessible)(start_sym);
+undump(batch_mode)(error_stop_mode)(interaction);
+undump(0)(str_ptr)(base_ident);
+undump(1)(hash_end)(bg_loc);
+undump(1)(hash_end)(eg_loc);
+undump_int(serial_no);@/
+undump_int(x);@+if (x<>69069)or eof(base_file) then goto off_base
+
+@ @<Create the |base_ident|...@>=
+selector:=new_string;
+print(" (preloaded base="); print(job_name); print_char(" ");
+print_int(round_unscaled(internal[year])); print_char(".");
+print_int(round_unscaled(internal[month])); print_char(".");
+print_int(round_unscaled(internal[day])); print_char(")");
+if interaction=batch_mode then selector:=log_only
+else selector:=term_and_log;
+str_room(1); base_ident:=make_string; str_ref[base_ident]:=max_str_ref;@/
+pack_job_name(base_extension);
+while not w_open_out(base_file) do
+ prompt_file_name("base file name",base_extension);
+print_nl("Beginning to dump on file ");
+@.Beginning to dump...@>
+slow_print(w_make_name_string(base_file)); flush_string(str_ptr-1);
+print_nl(""); slow_print(base_ident)
+
+@ @<Close the base file@>=
+w_close(base_file)
+
+@* \[49] The main program.
+This is it: the part of \MF\ that executes all those procedures we have
+written.
+
+Well---almost. We haven't put the parsing subroutines into the
+program yet; and we'd better leave space for a few more routines that may
+have been forgotten.
+
+@p @<Declare the basic parsing subroutines@>@;
+@<Declare miscellaneous procedures that were declared |forward|@>@;
+@<Last-minute procedures@>
+
+@ We've noted that there are two versions of \MF84. One, called \.{INIMF},
+@.INIMF@>
+has to be run first; it initializes everything from scratch, without
+reading a base file, and it has the capability of dumping a base file.
+The other one is called `\.{VIRMF}'; it is a ``virgin'' program that needs
+@.VIRMF@>
+to input a base file in order to get started. \.{VIRMF} typically has
+a bit more memory capacity than \.{INIMF}, because it does not need the
+space consumed by the dumping/undumping routines and the numerous calls on
+|primitive|, etc.
+
+The \.{VIRMF} program cannot read a base file instantaneously, of course;
+the best implementations therefore allow for production versions of \MF\ that
+not only avoid the loading routine for \PASCAL\ object code, they also have
+a base file pre-loaded. This is impossible to do if we stick to standard
+\PASCAL; but there is a simple way to fool many systems into avoiding the
+initialization, as follows:\quad(1)~We declare a global integer variable
+called |ready_already|. The probability is negligible that this
+variable holds any particular value like 314159 when \.{VIRMF} is first
+loaded.\quad(2)~After we have read in a base file and initialized
+everything, we set |ready_already:=314159|.\quad(3)~Soon \.{VIRMF}
+will print `\.*', waiting for more input; and at this point we
+interrupt the program and save its core image in some form that the
+operating system can reload speedily.\quad(4)~When that core image is
+activated, the program starts again at the beginning; but now
+|ready_already=314159| and all the other global variables have
+their initial values too. The former chastity has vanished!
+
+In other words, if we allow ourselves to test the condition
+|ready_already=314159|, before |ready_already| has been
+assigned a value, we can avoid the lengthy initialization. Dirty tricks
+rarely pay off so handsomely.
+@^dirty \PASCAL@>
+@^system dependencies@>
+
+On systems that allow such preloading, the standard program called \.{MF}
+should be the one that has \.{plain} base preloaded, since that agrees
+with {\sl The {\logos METAFONT\/}book}. Other versions, e.g., \.{CMMF},
+should also be provided for commonly used bases such as \.{cmbase}.
+@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
+@.cmbase@>
+@.plain@>
+
+@<Glob...@>=
+@!ready_already:integer; {a sacrifice of purity for economy}
+
+@ Now this is really it: \MF\ starts and ends here.
+
+The initial test involving |ready_already| should be deleted if the
+\PASCAL\ runtime system is smart enough to detect such a ``mistake.''
+@^system dependencies@>
+
+@p begin @!{|start_here|}
+history:=fatal_error_stop; {in case we quit during initialization}
+t_open_out; {open the terminal for output}
+if ready_already=314159 then goto start_of_MF;
+@<Check the ``constant'' values...@>@;
+if bad>0 then
+ begin wterm_ln('Ouch---my internal constants have been clobbered!',
+ '---case ',bad:1);
+@.Ouch...clobbered@>
+ goto final_end;
+ end;
+initialize; {set global variables to their starting values}
+@!init if not get_strings_started then goto final_end;
+init_tab; {initialize the tables}
+init_prim; {call |primitive| for each primitive}
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;@/
+max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr; fix_date_and_time;
+tini@/
+ready_already:=314159;
+start_of_MF: @<Initialize the output routines@>;
+@<Get the first line of input and prepare to start@>;
+history:=spotless; {ready to go!}
+if start_sym>0 then {insert the `\&{everyjob}' symbol}
+ begin cur_sym:=start_sym; back_input;
+ end;
+main_control; {come to life}
+final_cleanup; {prepare for death}
+end_of_MF: close_files_and_terminate;
+final_end: ready_already:=0;
+end.
+
+@ Here we do whatever is needed to complete \MF's job gracefully on the
+local operating system. The code here might come into play after a fatal
+error; it must therefore consist entirely of ``safe'' operations that
+cannot produce error messages. For example, it would be a mistake to call
+|str_room| or |make_string| at this time, because a call on |overflow|
+might lead to an infinite loop.
+@^system dependencies@>
+
+This program doesn't bother to close the input files that may still be open.
+
+@<Last-minute...@>=
+procedure close_files_and_terminate;
+var @!k:integer; {all-purpose index}
+@!lh:integer; {the length of the \.{TFM} header, in words}
+@!lk_offset:0..256; {extra words inserted at beginning of |lig_kern| array}
+@!p:pointer; {runs through a list of \.{TFM} dimensions}
+@!x:scaled; {a |tfm_width| value being output to the \.{GF} file}
+begin
+@!stat if internal[tracing_stats]>0 then
+ @<Output statistics about this job@>;@;@+tats@/
+wake_up_terminal; @<Finish the \.{TFM} and \.{GF} files@>;
+if log_opened then
+ begin wlog_cr;
+ a_close(log_file); selector:=selector-2;
+ if selector=term_only then
+ begin print_nl("Transcript written on ");
+@.Transcript written...@>
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+end;
+
+@ We want to finish the \.{GF} file if and only if it has already been started;
+this will be true if and only if |gf_prev_ptr| is positive.
+We want to produce a \.{TFM} file if and only if |fontmaking| is positive.
+The \.{TFM} widths must be computed if there's a \.{GF} file, even if
+there's going to be no \.{TFM}~file.
+
+We reclaim all of the variable-size memory at this point, so that
+there is no chance of another memory overflow after the memory capacity
+has already been exceeded.
+
+@<Finish the \.{TFM} and \.{GF} files@>=
+if (gf_prev_ptr>0)or(internal[fontmaking]>0) then
+ begin @<Make the dynamic memory into one big available node@>;
+ @<Massage the \.{TFM} widths@>;
+ fix_design_size; fix_check_sum;
+ if internal[fontmaking]>0 then
+ begin @<Massage the \.{TFM} heights, depths, and italic corrections@>;
+ internal[fontmaking]:=0; {avoid loop in case of fatal error}
+ @<Finish the \.{TFM} file@>;
+ end;
+ if gf_prev_ptr>0 then @<Finish the \.{GF} file@>;
+ end
+
+@ @<Make the dynamic memory into one big available node@>=
+rover:=lo_mem_stat_max+1; link(rover):=empty_flag; lo_mem_max:=hi_mem_min-1;
+if lo_mem_max-rover>max_halfword then lo_mem_max:=max_halfword+rover;
+node_size(rover):=lo_mem_max-rover; llink(rover):=rover; rlink(rover):=rover;
+link(lo_mem_max):=null; info(lo_mem_max):=null
+
+@ The present section goes directly to the log file instead of using
+|print| commands, because there's no need for these strings to take
+up |str_pool| memory when a non-{\bf stat} version of \MF\ is being used.
+
+@<Output statistics...@>=
+if log_opened then
+ begin wlog_ln(' ');
+ wlog_ln('Here is how much of METAFONT''s memory',' you used:');
+@.Here is how much...@>
+ wlog(' ',max_str_ptr-init_str_ptr:1,' string');
+ if max_str_ptr<>init_str_ptr+1 then wlog('s');
+ wlog_ln(' out of ', max_strings-init_str_ptr:1);@/
+ wlog_ln(' ',max_pool_ptr-init_pool_ptr:1,' string characters out of ',
+ pool_size-init_pool_ptr:1);@/
+ wlog_ln(' ',lo_mem_max-mem_min+mem_end-hi_mem_min+2:1,@|
+ ' words of memory out of ',mem_end+1-mem_min:1);@/
+ wlog_ln(' ',st_count:1,' symbolic tokens out of ',
+ hash_size:1);@/
+ wlog_ln(' ',max_in_stack:1,'i,',@|
+ int_ptr:1,'n,',@|
+ max_rounding_ptr:1,'r,',@|
+ max_param_stack:1,'p,',@|
+ max_buf_stack+1:1,'b stack positions out of ',@|
+ stack_size:1,'i,',
+ max_internal:1,'n,',
+ max_wiggle:1,'r,',
+ param_size:1,'p,',
+ buf_size:1,'b');
+ end
+
+@ We get to the |final_cleanup| routine when \&{end} or \&{dump} has
+been scanned.
+
+@<Last-minute...@>=
+procedure final_cleanup;
+label exit;
+var c:small_number; {0 for \&{end}, 1 for \&{dump}}
+begin c:=cur_mod;
+if job_name=0 then open_log_file;
+while input_ptr>0 do
+ if token_state then end_token_list@+else end_file_reading;
+while loop_ptr<>null do stop_iteration;
+while open_parens>0 do
+ begin print(" )"); decr(open_parens);
+ end;
+while cond_ptr<>null do
+ begin print_nl("(end occurred when ");@/
+@.end occurred...@>
+ print_cmd_mod(fi_or_else,cur_if);
+ {`\.{if}' or `\.{elseif}' or `\.{else}'}
+ if if_line<>0 then
+ begin print(" on line "); print_int(if_line);
+ end;
+ print(" was incomplete)");
+ if_line:=if_line_field(cond_ptr);
+ cur_if:=name_type(cond_ptr); loop_ptr:=cond_ptr;
+ cond_ptr:=link(cond_ptr); free_node(loop_ptr,if_node_size);
+ end;
+if history<>spotless then
+ if ((history=warning_issued)or(interaction<error_stop_mode)) then
+ if selector=term_and_log then
+ begin selector:=term_only;
+ print_nl("(see the transcript file for additional information)");
+@.see the transcript file...@>
+ selector:=term_and_log;
+ end;
+if c=1 then
+ begin @!init store_base_file; return;@+tini@/
+ print_nl("(dump is performed only by INIMF)"); return;
+@.dump...only by INIMF@>
+ end;
+exit:end;
+
+@ @<Last-minute...@>=
+@!init procedure init_prim; {initialize all the primitives}
+begin
+@<Put each...@>;
+end;
+@#
+procedure init_tab; {initialize other tables}
+var @!k:integer; {all-purpose index}
+begin @<Initialize table entries (done by \.{INIMF} only)@>@;
+end;
+tini
+
+@ When we begin the following code, \MF's tables may still contain garbage;
+the strings might not even be present. Thus we must proceed cautiously to get
+bootstrapped in.
+
+But when we finish this part of the program, \MF\ is ready to call on the
+|main_control| routine to do its work.
+
+@<Get the first line...@>=
+begin @<Initialize the input routines@>;
+if (base_ident=0)or(buffer[loc]="&") then
+ begin if base_ident<>0 then initialize; {erase preloaded base}
+ if not open_base_file then goto final_end;
+ if not load_base_file then
+ begin w_close(base_file); goto final_end;
+ end;
+ w_close(base_file);
+ while (loc<limit)and(buffer[loc]=" ") do incr(loc);
+ end;
+buffer[limit]:="%";@/
+fix_date_and_time; init_randoms((internal[time] div unity)+internal[day]);@/
+@<Initialize the print |selector|...@>;
+if loc<limit then if buffer[loc]<>"\" then start_input; {\&{input} assumed}
+end
+
+@* \[50] Debugging.
+Once \MF\ is working, you should be able to diagnose most errors with
+the \.{show} commands and other diagnostic features. But for the initial
+stages of debugging, and for the revelation of really deep mysteries, you
+can compile \MF\ with a few more aids, including the \PASCAL\ runtime
+checks and its debugger. An additional routine called |debug_help|
+will also come into play when you type `\.D' after an error message;
+|debug_help| also occurs just before a fatal error causes \MF\ to succumb.
+@^debugging@>
+@^system dependencies@>
+
+The interface to |debug_help| is primitive, but it is good enough when used
+with a \PASCAL\ debugger that allows you to set breakpoints and to read
+variables and change their values. After getting the prompt `\.{debug \#}', you
+type either a negative number (this exits |debug_help|), or zero (this
+goes to a location where you can set a breakpoint, thereby entering into
+dialog with the \PASCAL\ debugger), or a positive number |m| followed by
+an argument |n|. The meaning of |m| and |n| will be clear from the
+program below. (If |m=13|, there is an additional argument, |l|.)
+@.debug \#@>
+
+@d breakpoint=888 {place where a breakpoint is desirable}
+
+@<Last-minute...@>=
+@!debug procedure debug_help; {routine to display various things}
+label breakpoint,exit;
+var @!k,@!l,@!m,@!n:integer;
+begin loop begin wake_up_terminal;
+ print_nl("debug # (-1 to exit):"); update_terminal;
+@.debug \#@>
+ read(term_in,m);
+ if m<0 then return
+ else if m=0 then
+ begin goto breakpoint;@\ {go to every label at least once}
+ breakpoint: m:=0; @{'BREAKPOINT'@}@\
+ end
+ else begin read(term_in,n);
+ case m of
+ @t\4@>@<Numbered cases for |debug_help|@>@;
+ othercases print("?")
+ endcases;
+ end;
+ end;
+exit:end;
+gubed
+
+@ @<Numbered cases...@>=
+1: print_word(mem[n]); {display |mem[n]| in all forms}
+2: print_int(info(n));
+3: print_int(link(n));
+4: begin print_int(eq_type(n)); print_char(":"); print_int(equiv(n));
+ end;
+5: print_variable_name(n);
+6: print_int(internal[n]);
+7: do_show_dependencies;
+9: show_token_list(n,null,100000,0);
+10: slow_print(n);
+11: check_mem(n>0); {check wellformedness; print new busy locations if |n>0|}
+12: search_mem(n); {look for pointers to |n|}
+13: begin read(term_in,l); print_cmd_mod(n,l);
+ end;
+14: for k:=0 to n do print(buffer[k]);
+15: panicking:=not panicking;
+
+@* \[51] System-dependent changes.
+This section should be replaced, if necessary, by any special
+modifications of the program
+that are necessary to make \MF\ work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the published program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* \[52] Index.
+Here is where you can find all uses of each identifier in the program,
+with underlined entries pointing to where the identifier was defined.
+If the identifier is only one letter long, however, you get to see only
+the underlined entries. {\sl All references are to section numbers instead of
+page numbers.}
+
+This index also lists error messages and other aspects of the program
+that you might want to look up some day. For example, the entry
+for ``system dependencies'' lists all sections that should receive
+special attention from people who are installing \MF\ in a new
+operating environment. A list of various things that can't happen appears
+under ``this can't happen''.
+Approximately 25 sections are listed under ``inner loop''; these account
+for more than 60\pct! of \MF's running time, exclusive of input and output.
diff --git a/systems/knuth/dist/mf/mfbook.tex b/systems/knuth/dist/mf/mfbook.tex
new file mode 100644
index 0000000000..c1692a6f52
--- /dev/null
+++ b/systems/knuth/dist/mf/mfbook.tex
@@ -0,0 +1,20160 @@
+% This manual is copyright (C) 1986 by the American Mathematical Society.
+% All rights are reserved!
+% The file is distributed only for people to see its examples of TeX input,
+% not for use in the preparation of books like The METAFONTbook.
+% Permission for any other use of this file must be obtained in writing
+% from the copyright holder and also from the publisher (Addison-Wesley).
+\let\MFmanual=\!
+\loop\iftrue
+ \errmessage{This manual is copyrighted and should not be TeXed}\repeat
+\pausing1 \input manmac
+\ifproofmode\message{Proof mode is on!}\pausing1\fi
+ % halftitle
+\titlepage
+\pageno=-1985
+\null\bigskip
+\line{\titlefont The {\manual ()*+,-.*}book}
+\vfill
+\ifproofmode
+\rightline{The fine print in the upper right-hand}
+\rightline{corner of each page is a draft of intended}
+\rightline{index entries; it won't appear in the real book.}
+\rightline{Some index entries will be in |typewriter type|}
+\rightline{and/or enclosed in \<$\ldots$>, etc;}
+\rightline{such typographic distinctions aren't shown here.}
+\rightline{An index entry often extends for several pages;}
+\rightline{the actual scope will be determined later.}
+\rightline{Please note things that should be indexed but aren't.}
+\medskip
+\rightline{Apology: The xeroxed illustrations are often hard to see;}
+\rightline{they will be done professionally in the real book.}
+\fi
+\eject
+\titlepage\null\vfill\eject % blank page
+ % title
+\pageno=-1 % the front matter is numbered with roman numerals
+\font\auth=cmssdc10 scaled\magstep4 % used only on the title page
+\font\elevenbf=cmbx10 scaled\magstephalf % ditto
+\font\elevenit=cmti10 scaled\magstephalf % ditto
+\font\elevenrm=cmr10 scaled\magstephalf % ditto
+\titlepage
+\null\bigskip
+\line{\titlefont The {\manual ()*+,-.*}book}
+^^{Knuth, Donald Ervin}
+^^{Bibby, Duane Robert}
+\vskip 2pc
+\baselineskip 13pt \elevenbf
+\halign to\hsize{#\hfil\tabskip 0pt plus 1fil&#\hfil\tabskip0pt\cr
+\kern2.5mm\auth DONALD \kern+0pt E. \kern+0pt KNUTH&
+ \elevenit Stanford University\cr
+\noalign{\vskip 11pc}
+&\elevenit I\kern.7ptllustrations by\cr
+&DU\kern-1ptANE BIBBY\cr
+\noalign{\vfill}
+&\setbox0=\hbox{\manual77}%
+\setbox2=\hbox to\wd0{\hss\manual6\hss}%
+\raise2.3mm\box2\kern-\wd0\box0\cr % A-W logo
+&ADDISON\kern.1em--WESLEY\cr
+%&PUBLISHING COMP\kern-.13emANY\kern-1.5mm\cr
+\noalign{\vskip.5pc \global\elevenrm}
+&Upper Saddle River, NJ\cr
+&Boston\enspace$\cdot$\enspace Indianapolis\cr
+&San Francisco\enspace$\cdot$\enspace New York\cr
+&Toronto\enspace$\cdot$\enspace Montr\'eal\cr
+&London\enspace$\cdot$\enspace Munich\cr
+&Paris\enspace$\cdot$\enspace Madrid\cr
+&Capetown\enspace$\cdot$\enspace Sydney\enspace$\cdot$\enspace Tokyo\cr
+&Singapore\enspace$\cdot$\enspace Mexico City\cr}
+\kern24pt
+\eject
+ % copyright
+\titlepage
+\eightpoint
+\vbox to 8pc{}
+\noindent\strut
+%The quotation on page xxx is copyright $\copyright$ 19xx by Xxxx,
+%and used by permission.
+%\medskip
+%\noindent
+This manual describes \MF\ Version 2.0. Some
+of the advanced features mentioned here are absent from earlier versions.
+\medskip
+\noindent
+The joke on page 8 is due to Richard S. ^{Palais}.
+\medskip
+\noindent
+The ^{Wilkins} quotation on page 283 was suggested by Georgia K. M. ^{Tobin}.
+\medskip
+\noindent
+{\manual opqrstuq} is a trademark of Addison\kern.1em--Wesley
+ Publishing Company.
+\medskip
+\noindent
+\TeX\ is a trademark of the American Mathematical Society.
+\bigskip\medskip
+\noindent
+{\bf Library of Congress cataloging in publication data}
+\medskip
+{\tt\halign{#\hfil\cr
+Knuth, Donald Ervin, 1938-\cr
+\ \ \ The METAFONTbook.\cr
+\noalign{\medskip}
+\ \ \ (Computers \& Typesetting ; C)\cr
+\ \ \ Includes index.\cr
+\ \ \ 1.~METAFONT (Computer system).\ \ 2.~Type and type-\cr
+founding--Data processing.\ \ I.~Title.\ \ II.~Series:\cr
+Knuth, Donald Ervin, 1938-\ \ \ \ .\ \ Computers \&\cr
+typesetting ; C.\cr
+Z250.8.M46K58\ \ 1986\ \ \ \ \ \ \ \ \ 686.2\char13 24\ \ \ \ \ \ 85-28675\cr
+ISBN 0-201-13445-4\cr
+ISBN 0-201-13444-6 (soft)\cr}}
+\vfill
+\noindent
+%{\sl \kern-1pt Incorporates the final corrections made in 1995,
+% and a few dozen more.}
+{\sl \kern-1pt Incorporates all corrections known in 2013.}
+\smallskip
+\noindent
+Internet page {\tt http://www-cs-faculty.stanford.edu/\char`\~
+ knuth/abcde.html}
+contains current information about this book and related books.
+\smallskip
+\noindent
+Copyright $\copyright$ 1986 by the American Mathematical Society
+\smallskip
+\noindent
+This book is published jointly by the American Mathematical Society
+and Addison\kern.1em--Wesley Publishing Company.
+All rights reserved. No part of this publication may be reproduced, stored in
+a retrieval system, or transmitted, in any form or by any means,
+electronic, mechanical, photocopying, recording, or otherwise, without
+the prior written permission of the publishers. Printed in the United
+States of America. % Published simultaneously in Canada.
+\medskip
+\noindent
+%ISBN 0-201-13444-6\par % paperback
+%ISBN 0-201-13445-4\par % hardcover
+ISBN-13 \enspace 978-0-201-13445-2\par\noindent
+ISBN-10 \enspace\phantom{978-}0-201-13445-4\par\noindent
+ISBN-13 \enspace 978-0-201-13444-5 (soft)\par\noindent
+ISBN-10 \enspace\phantom{978-}0-201-13444-6 (soft)\par
+%11 12 13 14 15 16--CRS--07 06 05 04 03 02 % paperback
+%7 8 9 10 11 12 13--CRS--07 06 05 04 03 02 01 % hardcover
+\smallskip\noindent
+Text printed in the United States
+ at Courier Westford in Westford, Massachusetts.\par\noindent
+Eighth Printing, February 2012\par\noindent
+Twelfth Printing, February 2012 (soft)
+^^{Knuth, Donald Ervin}
+\eject
+ % dedication
+\titlepage
+\vbox to 8pc{}
+\rightline{\strut\eightssi To Hermann Zapf:}
+^^{Zapf, Hermann}
+\vskip2pt
+\rightline{\eightssi Whose strokes are the best}
+\vfill
+\eject
+ % blank page
+\titlepage
+\null\vfill
+\eject
+ % the preface
+\titlepage
+\def\rhead{Preface}
+\vbox to 8pc{
+\rightline{\titlefont Preface}\vss}
+{\topskip 9pc % this makes equal sinkage throughout the Preface
+\vskip-\parskip
+\tenpoint
+\noindent\hang\hangafter-2
+\smash{\lower12pt\hbox to 0pt{\hskip-\hangindent\cmman G\hfill}}\hskip-16pt
+{\sc ENERATION} {\sc OF} {\sc LETTERFORMS} \strut by mathematical means
+was first tried in the fifteenth century; it became popular in the
+sixteenth and seventeenth centuries; and it was abandoned (for good
+reasons) during the eighteenth century. Perhaps the twentieth century
+will turn out to be the right time for this idea to make a comeback,
+now that mathematics has advanced and computers are able to
+do the calculations.
+
+Modern printing equipment based on raster lines---in which metal ``type''
+has been replaced by purely combinatorial patterns of zeroes and ones
+that specify the desired position of ink in a discrete way---makes
+mathematics and computer science increasingly relevant to printing.
+We now have the ability to give a completely precise definition of letter
+shapes that will produce essentially equivalent results on all raster-based
+machines. Moreover, the shapes can be defined in terms of variable
+parameters; computers can ``draw'' new fonts of characters
+in seconds, making it possible for designers to perform valuable experiments
+that were previously unthinkable.
+
+\MF\ is a system for the design of alphabets suited to raster-based
+devices that print or display text. The characters that you are reading
+were all designed with \MF\!, in a completely precise way; and they
+were developed rather hastily by the author of the system, who is a rank
+amateur at such things. It seems clear that further work with \MF\ has
+the potential of producing typefaces of real ^{beauty}. This manual has
+been written for people who would like to help advance the art of
+mathematical type design.
+
+A top-notch designer of typefaces needs to have an unusually good eye
+and a highly developed sensitivity to the nuances of shapes.
+A top-notch user of computer languages needs to have an unusual
+talent for abstract reasoning and a highly developed ability to
+express intuitive ideas in formal terms. Very few people have both
+of these unusual combinations of skills; hence the best products of
+\MF\ will probably be collaborative efforts between two
+people who complement each other's abilities. Indeed, this situation
+isn't very different from the way types have been created for many
+generations, except that the r\^ole of ``punch-cutter'' is now being
+played by skilled computer specialists instead of by skilled
+metalworkers.
+
+A \MF\ user writes a ``program'' for each letter or symbol of a typeface.
+These programs are different from ordinary computer programs,
+because they are essentially {\sl declarative\/} rather than imperative.
+In the \MF\ language you explain where the major components of a
+desired shape are to be located, and how they relate to each other,
+but you don't have to work out the details of exactly where the lines
+cross, etc.; the computer takes over the work of solving equations as it
+deduces the consequences of your specifications. One of the advantages of
+\MF\ is that it provides a discipline according to which the principles
+of a particular alphabet design can be stated precisely. The underlying
+intelligence does not remain hidden in the mind of the designer; it is
+spelled out in the programs. Thus consistency can readily be obtained
+where consistency is desirable, and a font can readily be extended to
+new symbols that are compatible with the existing ones.
+
+It would be nice if a system like \MF\ were to simplify the task of type
+design to the point where beautiful new alphabets could be created in a
+few hours. This, alas, is impossible; an enormous amount of subtlety lies
+behind the seemingly simple letter shapes that we see every day, and the
+designers of high-quality typefaces have done their work so well that we
+don't notice the underlying complexity. One of the disadvantages of \MF\
+is that a person can easily use it to produce poor alphabets, cheaply and
+in great quantity. Let us hope that such experiments will have educational
+value as they reveal why the subtle tricks of the trade are important, but
+let us also hope that they will not cause bad workmanship to proliferate.
+Anybody can now produce a book in which all of the type is home-made, but
+a person or team of persons should expect to spend a year or more on the
+project if the type is actually supposed to look right. \MF\ won't put
+today's type designers out of work; on the contrary, it will tend to make
+them heroes and heroines, as more and more people come to appreciate their
+skills.
+
+Although there is no royal road to type design, there are some things that
+can, in fact, be done well with \MF\ in an afternoon. Geometric designs
+are rather easy; and it doesn't take long to make modifications to letters
+or symbols that have previously been expressed in \MF\ form. Thus,
+although comparatively few users of \MF\ will have the courage to do an
+entire alphabet from scratch, there will be many who will enjoy
+customizing someone else's design.
+
+This book is not a text about mathematics or about computers. But if
+you know the rudiments of those subjects (namely, contemporary high school
+mathematics, together with the knowledge of how to use the text
+editing or word processing facilities on your computing machine),
+you should be able to use \MF\ with little difficulty after reading
+what follows. Some parts of the exposition in the text are more obscure
+than others, however, since the author has tried to satisfy experienced
+\MF ers as well as beginners and casual users with a single manual.
+Therefore a special symbol has been used to warn about esoterica: When you
+see the sign
+$$\vbox{\hbox{\dbend}\vskip 11pt}$$
+at the beginning of a paragraph, watch out for a ``^{dangerous bend}''
+in the train of thought---don't read such a paragraph unless you need to.
+You will be able to use \MF\ reasonably well, even to design characters like
+the dangerous-bend symbol itself, without reading the fine print in such
+advanced sections.
+
+Some of the paragraphs in this manual are so far out that they are rated
+$$\vcenter{\hbox{\dbend\kern1pt\dbend}\vskip 11pt}\;;$$
+everything that was said about single dangerous-bend signs goes double
+for these. You should probably have at least a month's experience with
+\MF\ before you attempt to fathom such doubly dangerous depths
+of the system; in fact, most people will never need to know \MF\
+in this much detail, even if they use it every day. After all, it's
+possible to fry an egg without knowing anything about biochemistry.
+Yet the whole story is here in case you're curious. \ (About \MF\!, not eggs.)
+
+The reason for such different levels of complexity is that people change
+as they grow accustomed to any powerful tool. When you first try to use
+\MF\!, you'll find that some parts of it are very easy, while other things
+will take some getting used to. At first you'll probably try to control
+the shapes too rigidly, by overspecifying data that has been copied from
+some other medium. But later, after you have begun to get a feeling for
+what the machine can do well, you'll be a different person, and you'll be
+willing to let \MF\ help contribute to your designs as they are being
+developed. As you gain more and more experience working with this unusual
+apprentice, your perspective will continue to change and you will
+run into different sorts of challenges. That's the way it is with any
+powerful tool: There's always more to learn, and there are always better
+ways to do what you've done before. At every stage in the development
+you'll want a slightly different sort of manual. You may even want to
+write one yourself. By paying attention to the dangerous bend signs in
+this book you'll be better able to focus on the level that interests you
+at a particular time.
+
+Computer system manuals usually make dull reading, but take heart:
+This one contains {\sc ^{JOKES}} every once in a while. You might actually
+enjoy reading it. \ (However, most of the jokes can only be appreciated
+properly if you understand a technical point that is being made---so
+read {\sl carefully}.)
+
+Another noteworthy characteristic of this book is that it doesn't
+always tell the ^{truth}. When certain concepts of \MF\ are introduced
+informally, general rules will be stated; afterwards you will find that the
+rules aren't strictly true. In general, the later chapters contain more
+reliable information than the earlier ones do. The author feels that this
+technique of deliberate lying will actually make it easier for you to
+learn the ideas. Once you understand a simple but false rule, it will not
+be hard to supplement that rule with its exceptions.
+
+In order to help you internalize what you're reading,
+{\sc ^{EXERCISES}} are sprinkled through this manual. It is generally intended
+that every reader should try every exercise, except for questions that appear
+in the ``dangerous bend'' areas. If you can't solve a problem, you
+can always look up the answer.
+But please, try first to solve it by yourself; then you'll learn more
+and you'll learn faster. Furthermore, if you think you do know the solution,
+you should turn to Appendix~A and check it out, just to make sure.
+
+\bigskip
+\hrule
+\line{\vrule\hss\vbox{\medskip\ninepoint
+\leftskip=\parindent \rightskip=\parindent
+\noindent\strut W{\sc ARNING}: Type design can be hazardous to your other
+interests. Once you get hooked, you will develop intense feelings about
+letterforms; the medium will intrude on the messages that you read. And you
+will perpetually be thinking of improvements to the fonts that you see
+everywhere, especially those of your own design.
+\strut\medskip}\hss\vrule}
+\hrule
+
+\bigskip
+
+The \MF\ language described here has very little in common with the
+author's previous attempt at a language for alphabet design, because
+five years of experience with the old system has made it clear that a
+completely different approach is preferable. Both languages have
+been called \MF; but henceforth the old language should be called
+\MF\kern.05em79, and its use should rapidly fade away. Let's keep the name
+\MF\ for the language described here, since it is so much better, and
+since it will never change again. ^^{MF79}
+
+I wish to thank the hundreds of people who have helped me to formulate
+this ``definitive edition'' of \MF\!, based on their experiences with
+preliminary versions of the system. In particular, John ^{Hobby}
+discovered many of the algorithms that have made the new language
+possible. My work at Stanford has been generously supported by the
+^{National Science Foundation}, the ^{Office of Naval Research}, the ^{IBM
+Corporation}, and the ^{System Development Foundation}. I also wish to
+thank the ^{American Mathematical Society} for its encouragement and for
+publishing the {\sl ^{TUGboat}\/} newsletter (see Appendix~J\null).
+Above all, I deeply thank my wife, Jill, for the inspiration, ^^{Knuth, Jill}
+understanding, comfort, and support she has given me for more than
+25~years, especially during the eight years that I have been
+working intensively on mathematical typography.
+
+\medskip
+\line{{\sl Stanford, California}\hfil--- D. E. K.}^^{Knuth, Don}
+\line{\sl September 1985\hfil}
+
+} % end of the special \topskip
+\endchapter
+
+It is hoped that Divine Justice may find
+some suitable affliction for the malefactors
+who invent variations upon the alphabet of our fathers.~.\thinspace.\thinspace.
+The type-founder, worthy mechanic, has asserted himself
+with an overshadowing individuality,
+defacing with his monstrous creations and revivals
+every publication in the land.
+\author AMBROSE ^{BIERCE}, {\sl The Opinionator.~Alphab\^etes\/} %
+ (1911) % vol 10 of his collected works, p69
+ % probably written originally in 1898 or 1899
+
+\bigskip
+
+Can the new process yield a result that, say,
+a Club of Bibliophiles would recognise as a work of art
+comparable to the choice books they have in their cabinets?
+\author STANLEY ^{MORISON}, {\sl Typographic Design in Relation to
+ Photographic Composition\/} (1958) % pp 4--5
+
+\eject
+ % the table of contents
+\titlepage
+\vbox to 8pc{
+\rightline{\titlefont Contents}
+\vfill}
+^^{Contents of this manual, table}
+\def\rhead{Contents}
+\tenpoint
+\begingroup
+\countdef\counter=255
+\def\diamondleaders{\global\advance\counter by 1
+ \ifodd\counter \kern-10pt \fi
+ \leaders\hbox to 20pt{\ifodd\counter \kern13pt \else\kern3pt \fi
+ .\hss}}
+\baselineskip 15pt plus 5pt
+\def\\#1. #2. #3.{\line{\strut
+ \hbox to\parindent{\bf\hbox to 1em{\hss#1}\hss}%
+ \rm#2\diamondleaders\hfil\hbox to 2em{\hss#3}}}
+\\1. The Name of the Game. 1.
+\\2. Coordinates. 5.
+\\3. Curves. 13.
+\\4. Pens. 21.
+\\5. Running \MF\!\null. 31.
+\\6. How \MF\ Reads What You Type. 49.
+\\7. Variables. 53.
+\\8. Algebraic Expressions. 59.
+\\9. Equations. 75.
+\\10. Assignments. 87.
+\\11. Magnification and Resolution. 91.
+\\12. Boxes. 101.
+\\13. Drawing, Filling, and Erasing. 109.
+\\14. Paths. 123.
+\\15. Transformations. 141.
+\\16. Calligraphic Effects. 147.
+\\17. Grouping. 155.
+\\18. Definitions (also called Macros). 159.
+\\19. Conditions and Loops. 169.
+\\20. More about Macros. 175.
+\\21. Random Numbers. 183.
+\\22. Strings. 187.
+\\23. Online Displays. 191.
+\eject
+\vbox to 8pc{}
+\\24. Discreteness and Discretion. 195.
+\\25. Summary of Expressions. 209.
+\\26. Summary of the Language. 217.
+\\27. Recovery from Errors. 223.
+\null
+\leftline{\indent\bf Appendices}
+\\A. Answers to All the Exercises. 233.
+\\B. Basic Operations. 257.
+\\C. Character Codes. 281.
+\\D. Dirty Tricks. 285.
+\\E. Examples. 301.
+\\F. Font Metric Information. 315.
+\\G. Generic Font Files. 323.
+\\H. Hardcopy Proofs. 327.
+\\I\hskip 1pt. Index. 345.
+\\J\hskip 1pt. Joining the \TeX\ Community. 361.
+\null % 17 lines so far to balance the 23 on the other page
+\null % 18
+\null % 19
+\null % 20
+\null % 21
+\null % 22
+\null % 23
+\eject
+\endgroup
+ \beginchapter Chapter 1. The Name of\\the Game
+
+\pageno=1 % This is page number 1, number 1,
+This is a book about a computer system called \MF\!, \kern1pt just as
+\kern-1pt {\sl The \TeX
+book\/} is about \TeX. \MF\ and \TeX\ are good friends who intend to live
+together for a long time. Between them they take care of the two most
+fundamental tasks of typesetting: \TeX\ puts characters into the proper
+positions on a page, while \MF\ determines the shapes of the characters
+themselves. ^^{TeX} ^^{METAFONT, the name}
+
+Why is the system called \MF\thinspace? The `-{\manual FONT}\thinspace'
+part is easy to understand, because sets of related characters that are
+used in typesetting are traditionally known as fonts of type. The
+`{\manual META}-' part is more interesting: It indicates that we are
+interested in making high-level descriptions that transcend any of the
+individual fonts being described.
+
+Newly coined words beginning with `meta-' generally reflect our contemporary
+inclination to view things from outside or above, at a more abstract level than
+before, with what we feel is a more mature understanding. We now have
+metapsychology (the study of how the mind relates to its containing body),
+metahistory (the study of principles that control the course of events),
+metamathematics (the study of mathematical reasoning), metafiction
+(literary works that explicitly acknowledge their own forms), and so on.
+A metamathematician proves metatheorems (theorems about theorems);
+a computer scientist often works with metalanguages (languages for
+describing languages). Similarly, a ^{meta-font} is a schematic description
+of the shapes in a family of related fonts; the letterforms change
+appropriately as their underlying parameters change.
+
+Meta-design is much more difficult than design; it's easier to draw something
+than to explain how to draw it. One of the problems is that different sets
+of potential specifications can't easily be envisioned all at once.
+Another is that a computer has to be told absolutely everything.
+However, once we have successfully explained how to draw something
+in a sufficiently general manner, the same explanation will work for
+related shapes, in different circumstances; so~the time spent in formulating
+a precise explanation turns out to be worth it.
+
+Typefaces intended for text are normally seen small, and our eyes can read
+them best when the letters have been designed specifically for the size at
+which they are actually used. Although it is tempting to get 7-point fonts
+by simply making a 70\% reduction from the 10-point size, this shortcut
+leads to a serious degradation of quality. Much better results can be
+obtained by incorporating parametric variations into a meta-design. In
+fact, there are advantages to built-in variability even when you want to
+produce only one font of type in a single size, because it allows you to
+postpone making decisions about many aspects of your design. If you leave
+certain things undefined, treating them as parameters instead of
+``freezing'' the specifications at an early stage, the computer will be
+able to draw lots of examples with different settings of the parameters,
+and you will be able to see the results of all those experiments at the final
+size. This will greatly increase your ability to edit and fine-tune the font.
+
+If meta-fonts are so much better than plain old ordinary fonts, why weren't
+they developed long ago? The main reason is that computers did not exist until
+recently. People find it difficult and dull to carry out calculations with
+a multiplicity of parameters, while today's machines do such tasks with ease.
+The introduction of parameters is a natural outgrowth of automation.
+
+OK, let's grant that meta-fonts sound good, at least in theory. There's still
+the practical problem about how to achieve them. How can we actually
+specify shapes that depend on unspecified parameters?
+
+If only one parameter is varying, it's fairly easy to solve the problem in
+a visual way, by overlaying a series of drawings that show graphically how
+the shape changes. For example, if the parameter varies from 0 to~1, we
+might prepare five sketches, corresponding to the parameter values 0,
+$1\over4$, $1\over2$, $3\over4$, and~1. If these sketches follow a
+consistent pattern, we can readily ^{interpolate} to find the shape for a
+value like~$2\over3$ that lies between two of the given ones. We might
+even try extrapolating to parameter values like 1$1\over4$.
+
+But if there are two or more independent parameters, a purely visual solution
+becomes too cumbersome. We must go to a verbal approach, using some sort
+of language to describe the desired drawings. Let's imagine, for example,
+that we want to explain the shape of a certain letter `a' to a friend in
+a distant country, using only a telephone for communication; our friend
+is supposed to be able to reconstruct exactly the shape we have in mind.
+Once we figure out a sufficiently natural way to do that, for a particular
+fixed shape, it isn't much of a trick to go further and make our verbal
+description more general, by including variable parameters instead of
+restricting ourselves to constants.
+
+An analogy to cooking might make this point clearer. Suppose you have just
+baked a delicious berry pie, and your friends ask you to tell them the
+^{recipe} so that they can bake one too. If you have developed your cooking
+skills entirely by intuition, you might find it difficult to record exactly
+what you did. But there is a traditional language of recipes in which you
+could communicate the steps you followed; and if you take careful measurements,
+you might find that you used, say, 1$1\over4$ cups of sugar. The next step,
+if you were instructing a computer-controlled cooking machine, would be to
+go to a meta-recipe in which you use, say, $.25x$ cups of sugar for $x$
+cups of berries; or $.3x+.2y$ cups for $x$~cups of boysenberries and
+$y$~cups of blackberries.
+
+In other words, going from design to meta-design is essentially like
+going from arithmetic to elementary algebra. Numbers are replaced
+by simple formulas that involve unknown quantities. We will see
+many examples of this.
+
+A \MF\ definition of a complete typeface generally consists of three
+main parts. First there is a rather mundane set of subroutines that take care
+of necessary administrative details, such as assigning code numbers
+to individual characters; each character must also
+be positioned properly inside an invisible ``box,'' so that typesetting
+systems will produce the correct spacing. Next comes a more interesting
+collection of subroutines, designed to draw the basic strokes characteristic
+of the typeface (e.g., the serifs, bowls, arms, arches, and so on).
+These subroutines will typically be described in terms of their own special
+parameters, so that they can produce a variety of related strokes;
+a serif subroutine will, for example, be able to draw serifs of
+different lengths, although all of the serifs it draws should have the
+same ``feeling.'' Finally, there are routines for each of the characters.
+If the subroutines in the first and second parts have been chosen well,
+the routines of the third part will be fairly high-level descriptions
+that don't concern themselves unnecessarily with details; for example, it
+may be possible to substitute a different serif-drawing subroutine without
+changing any of the programs that use that subroutine, thereby obtaining
+a typeface of quite a different flavor. [A particularly striking example
+of this approach has been worked out by John~D. ^{Hobby} and ^{Gu} Guoan
+in ``A Chinese Meta-Font,'' {\sl TUGboat\/ \bf5} (1984), 119--136. By
+changing a set of 13 basic stroke subroutines, they were able to draw 128
+sample ^{Chinese characters} in three different styles (Song, Long Song,
+and Bold), using the same programs for the characters.]
+
+A well-written \MF\ program will express the designer's intentions more
+clearly than mere drawings ever can, because the language of algebra has
+simple ``idioms'' that make it possible to elucidate many visual relationships.
+Thus, \MF\ programs can be used to communicate knowledge
+about type design, just as recipes convey the expertise of a chef. But
+algebraic formulas are not easy to understand in isolation; \MF\ descriptions
+are meant to be read with an accompanying illustration, just as the
+constructions in geometry textbooks are accompanied by diagrams.
+Nobody is ever expected to read the text of a \MF\ program and say,
+``Ah, what a beautiful letter!'' But with one or more enlarged pictures
+of the letter, based on one or more settings of the parameters, a reader
+of the \MF\ program should be able to say, ``Ah, I~understand how this
+beautiful letter was drawn!'' We shall see that the \MF\ system makes it
+fairly easy to obtain annotated proof drawings that you can hold in your
+hand as you are working with a program.
+
+Although \MF\ is intended to provide a relatively painless way to describe
+meta-fonts, you can, of course, use it also to describe unvarying shapes that
+have no ``meta-ness'' at all. Indeed, you need not even use it to produce
+fonts; the system will happily draw geometric designs that have no relation
+to the characters or glyphs of any alphabet or script. The author
+occasionally uses \MF\ simply as a pocket calculator, to do elementary
+arithmetic in an interactive way. A computer doesn't mind if its
+programs are put to purposes that don't match their names.
+
+\endchapter
+
+[Tinguely] made some large, brightly coloured open reliefs,
+juxtaposing stationary and mobile shapes.
+He later gave them names like\/ %
+{\rm Meta-^{Kandinsky}}\kern-1pt\ and\/ {\rm Meta-^{Herbin}}\kern-.5pt,
+to clarify the ideas and attitudes %
+that lay at the root of their conception.
+\author K. G. PONTUS ^{HULT\'EN}, {\sl Jean ^{Tinguely}: M\'eta\/} (1972)
+ % translated from German by Mary Whittall, 1975, p46
+
+\bigskip
+
+The idea of a meta-font should now be clear. But what good is it?
+The ability to manipulate lots of parameters may be interesting and fun,
+but does anybody really need a 6\/{\manual\seventh}\kern1pt-point font
+that is one fourth of the way between Baskerville and Helvetica?
+\author DONALD E. ^{KNUTH}, {\sl The Concept of a Meta-Font\/} (1982)
+ % Visible Language 16, p19
+
+\eject
+ \beginchapter Chapter 2. Coordinates
+
+If we want to tell a computer how to draw a particular shape, we need a way to
+explain where the key points of that shape are supposed to be.
+\MF\ uses standard {\sl ^{Cartesian} ^{coordinates}\/} for this purpose:
+The location of a point is defined by specifying its $x$~coordinate, which
+is the number of units to the right of some reference point, and its
+$y$~coordinate, which is the number of units upward from the reference
+point. First we determine the horizontal (left/right) component of a
+point's position, then we determine the vertical (up/down) component.
+\MF's world is two-dimensional, so two coordinates are enough.%
+^^{x coordinate} ^^{y coordinate}
+
+For example, let's consider the following six points:
+\displayfig 2a (4.75pc)
+\MF's names for the positions of these points are
+\begindisplay
+$(x_1,y_1)=(0,100)$;&$(x_2,y_2)=(100,100)$;&$(x_3,y_3)=(200,100)$;\cr
+$(x_4,y_4)=(0,\hfill0)$;&$(x_5,y_5)=(100,\hfill0)$;&
+ $(x_6,y_6)=(200,\hfill0)$.\cr
+\enddisplay
+Point 4 is the same as the reference point, since both of its coordinates
+are zero; to get to point~$3=(200,100)$, you start at the reference point
+and go 200~steps right and 100~up; and so on.
+
+\exercise Which of the six example points is closest to the point $(60,30)$?
+\answer Point $5=(100,0)$ is closer than any of the others. \ (See
+the diagram below.)
+
+\exercise True or false: All points that lie on a given horizontal straight
+line have the same $x$~coordinate.
+\answer \decreasehsize 15pc
+\rightfig A2a (13pc x 5pc) ^9pt
+False. But they all do have the same $y$~coordinate.
+
+\exercise Explain where the point $(-5,15)$ is located.
+\answer 5 units to the {\sl left\/} of the reference point, and 15 units up.
+
+\exercise What are the coordinates of a point that lies exactly
+60~units below point~6 in the diagram above?
+(``Below'' means ``down the page,'' not ``under the page.'')
+\answer \restorehsize $(200,-60)$.
+
+In a typical application of \MF\!, you prepare a rough sketch of the shape
+you plan to define, on a piece of ^{graph paper}, and you label important
+points on that sketch with any convenient numbers. Then you write a \MF\
+program that explains (i)~the coordinates of those key points, and
+(ii)~the lines or curves that are supposed to go between them.
+
+\MF\ has its own internal graph paper, which forms a so-called ^{raster}
+or ^{grid} consisting of square ``^{pixels}.'' ^^{pel, see pixel}
+The output of \MF\ will \hbox{specify} that certain of the pixels are ``black''
+and that the others are ``white''; thus, the computer essentially converts
+shapes into binary patterns like the designs a~person can make when doing
+needlepoint with two colors of yarn.
+
+Coordinates are lengths, but we haven't discussed yet what the units of
+length actually are. It's important to choose convenient units,
+and \MF's coordinates are given in units of pixels. The little squares
+illustrated on the previous page, which correspond to differences
+of 10~units in an $x$~coordinate or a $y$~coordinate, therefore represent
+$10\times10$ arrays of pixels, and the rectangle enclosed by our six
+example points contains 20,000 pixels altogether.\footnote*{We
+sometimes use the term ``pixel'' to mean a square picture element,
+but sometimes we use it to signify a one-dimensional unit of length.
+A square pixel is one pixel-unit wide and one pixel-unit tall.}
+
+Coordinates don't have to be whole numbers. You can refer, for example,
+to point $(31.5,42.5)$, which lies smack in the middle of the pixel
+whose corners are at $(31,42)$, $(31,43)$, $(32,42)$, and~$(32,43)$.
+The computer works internally with coordinates that are integer multiples
+of ${1\over65536}\approx0.00002$ of the width of a pixel, so it is
+capable of making very fine distinctions. But \MF\ will never make
+a pixel half black; it's all or nothing, as far as the output is concerned.
+
+The fineness of a grid is usually called its {\sl ^{resolution}}, and
+resolution is usually expressed in pixel units per inch (in America)
+or pixel units per millimeter (elsewhere). For example, the type you
+are now reading was prepared by \MF\ with a resolution of slightly
+more than 700 pixels to the inch, but with slightly fewer than 30 pixels
+per~mm. For the time being we shall assume that the pixels are so tiny
+that the operation of rounding to whole pixels is unimportant;
+later we will consider the important questions that arise when \MF\ is
+producing low-resolution output.
+
+It's usually desirable to write \MF\ programs that can manufacture fonts
+at many different resolutions, so that a variety of low-resolution printing
+devices will be able to make proofs that are compatible with a variety of
+high-resolution devices. Therefore the key points in \MF\ programs are rarely
+specified in terms of pure numbers like `100'\thinspace; we generally make
+the coordinates relative to some other resolution-dependent quantity, so
+that changes will be easy to make. For example, it would have been better
+to use a definition something like the following, for the six points
+considered earlier:
+\begindisplay
+$(x_1,y_1)=(0,b)$;&$(x_2,y_2)=(a,b)$;&$(x_3,y_3)=(2a,b)$;\cr
+$(x_4,y_4)=(0,0)$;&$(x_5,y_5)=(a,0)$;&$(x_6,y_6)=(2a,0)$;\cr
+\enddisplay
+then the quantities $a$ and $b$ can be defined in some way appropriate to
+the desired resolution. We had $a=b=100$ in our previous example, but
+such constant values leave us with little or no flexibility.
+
+Notice the quantity `$2a$' in the definitions of $x_3$ and $x_6$; \MF\
+understands enough algebra to know that this means twice the value of~$a$,
+whatever $a$~is. We observed in Chapter~1 that simple uses of algebra give
+\MF\ its meta-ness. Indeed, it is interesting to note from a historical
+standpoint that ^{Cartesian} coordinates are named after Ren\'e
+^{Descartes}, not because he invented the idea of coordinates, but because
+he showed how to get much more out of that idea by applying algebraic
+methods. People had long since been using coordinates for such things as
+latitudes and longitudes, but Descartes observed that by putting unknown
+quantities into the coordinates it became possible to describe infinite
+sets of related points, and to deduce properties of curves that were
+extremely difficult to work out using geometrical methods alone.
+
+So far we have specified some points, but we haven't actually done
+anything with them. Let's suppose that we want to draw a straight line
+from point~1 to point~6, obtaining
+\displayfig 2b (5pc)
+One way to do this with \MF\ is to say
+\begindisplay
+@draw@ $(x_1,y_1)\to(x_6,y_6)$.
+\enddisplay
+The `$\to$' ^^{..} here tells the computer to connect two points.
+
+It turns out that we often want to write formulas like `$(x_1,y_1)$', so
+it will be possible to save lots of time if we have a special abbreviation
+for such things. Henceforth we shall use the notation $z_1$ to stand for
+$(x_1,y_1)$; and in general, ^^{z convention}
+$z_k$ with an arbitrary subscript will stand for the point $(x_k,y_k)$.
+The `@draw@' command above can therefore be written more simply as
+\begindisplay
+^@draw@ $z_1\to z_6$.
+\enddisplay
+Adding two more straight lines by saying, `@draw@ $z_2\to z_5$' and
+`@draw@ $z_3\to z_4$', we obtain a design that is slightly reminiscent of
+the ^{Union Jack}:
+\displayfig 2c (5.5pc)
+We shall call this a ^{hex symbol}, because it has six endpoints. Notice
+that the straight lines here have some thickness, and they are rounded at
+the ends as if they had been drawn with a felt-tip pen having a circular
+nib. \MF\ provides many ways to control the thicknesses of lines and to
+vary the terminal shapes, but we shall discuss such things in later
+chapters because our main concern right now is to learn about coordinates.
+
+If the hex symbol is scaled down so that its height parameter $b$
+is exactly equal to the height of the letters in this paragraph,
+it looks like this: `\thinspace{\manual\hexa}\thinspace'. Just for fun,
+let's try to typeset ten of them in a row:
+\begindisplay
+{\manual\hexa\hexa\hexa\hexa\hexa\hexa\hexa\hexa\hexa\hexa}
+\enddisplay
+How easy it is to do this!\footnote*{Now that authors have
+for the first time the power to invent new symbols with great ease, and to
+have those characters printed in their manuscripts on a wide variety of
+typesetting devices, we must face the question of how much experimentation
+is desirable. Will font freaks abuse this toy by overdoing it? Is it wise
+to introduce new symbols by the thousands? Such questions are beyond
+the scope of this book; but it is easy to imagine an epidemic of
+fontomania occurring, once people realize how much fun it is to design
+their own characters, hence it may be necessary to perform fontal
+lobotomies.} % This joke due to Richard Palais, commenting on draft in 1979
+
+Let's look a bit more closely at this new character.
+The {\manual\hexa} is a bit too tall, because it extends above points
+1, 2, and~3 when the thickness of the lines is taken into account;
+similarly, it sinks a bit too much below the baseline (i.e., below
+the line $y=0$ that contains points 4, 5, and~6). In order to correct
+this, we want to move the key points slightly. For example, point~$z_1$
+should not be exactly at $(0,b)$; we ought to arrange things so that
+the top of the pen is at $(0,b)$ when the center of the pen is at~$z_1$.
+We can express this condition for the top three points as follows:
+\begindisplay
+$"top"\,z_1=(0,b)$;&$"top"\,z_2=(a,b)$;&$"top"\,z_3=(2a,b)$;\cr
+\noalign{\vskip\belowdisplayskip
+\leftline{similarly, the remedy for points 4, 5, and 6 is to specify
+ the equations}
+\vskip\abovedisplayskip}
+$"bot"\,z_4=(0,0)$;&$"bot"\,z_5=(a,0)$;&$"bot"\,z_6=(2a,0)$.\cr
+\enddisplay
+The resulting squashed-in character is
+\displayfig 2d (4.5pc)
+(shown here with the original weight `\thinspace{\manual\hexb}\thinspace'
+and also in a bolder version `\thinspace{\manual\hexc}\thinspace').
+
+\exercise Ten of these bold hexes produce `\thinspace{\manual
+\hexc\hexc\hexc\hexc\hexc\hexc\hexc\hexc\hexc\hexc}\thinspace'; notice that
+adjacent symbols overlap each other. The reason is that each character
+has width $2a$, hence point~3 of one character coincides with point~1
+of the next. Suppose that we actually want the characters to be
+completely confined to a rectangular box of width~$2a$, so that
+adjacent characters come just shy of touching (\thinspace{\manual
+\hexd\hexd\hexd\hexd\hexd\hexd\hexd\hexd\hexd\hexd}\thinspace).
+Try to guess how the point-defining equations above could be modified
+to make this happen, assuming that
+\MF\ has operations `"lft"' and `"rt"' analogous to `"top"' and `"bot"'.
+\answer $"top"\,"lft"\,z_1=(0,b)$; \ $"top"\,z_2=(a,b)$; \
+$"top"\,"rt"\,z_3=(2a-1,b)$; \ $"bot"\,"lft"\,z_4=(0,0)$; \
+$"bot"\,z_5=(a,0)$; \ $"bot"\,"rt"\,z_6=(2a-1,0)$.
+Adjacent characters will be separated by exactly one column of white
+pixels, if the character is $2a$ pixels wide, because the right edge of
+black pixels is specified here to have the $x$~coordinate $2a-1$.
+
+Pairs of coordinates can be thought of as ``^{vectors}'' or ``displacements''
+as well as points. For example, $(15,8)$ can be regarded as a command to
+go right~15 and up~8; then point $(15,8)$ is the position we get to after
+starting at the reference point and obeying the command $(15,8)$. This
+interpretation works out nicely when we consider addition of vectors:
+If we move according to the vector $(15,8)$ and then move according to
+$(7,-3)$, the result is the same as if we move $(15,8)+(7,-3)=
+(15+7,8-3)=(22,5)$. The sum of two vectors $z_1=(x_1,y_1)$ and $z_2=
+(x_2,y_2)$ is the vector $z_1+z_2=(x_1+x_2,y_1+y_2)$ obtained by adding
+$x$ and $y$ components separately. This vector represents the result of
+moving by vector $z_1$ and then moving by vector $z_2$; alternatively,
+$z_1+z_2$ represents the point you get~to by starting at point~$z_1$
+^^{addition of vectors}
+and moving by vector~$z_2$.
+
+\exercise Consider the four fundamental vectors $(0,1)$, $(1,0)$,
+$(0,-1)$, and $(-1,0)$. Which of them corresponds to moving one pixel unit
+(a)~to the right? (b)~to the left? (c)~down? (d)~up?
+\answer $"right"=(1,0)$; $"left"=(-1,0)$; $"down"=(0,-1)$; $"up"=(0,1)$.
+
+Vectors can be subtracted as well as added; the value of $z_1-z_2$ is simply
+$(x_1-x_2,y_1-y_2)$. Furthermore it is natural to multiply a vector
+by a single number~$c$: The quantity $c$~times $(x,y)$, which is written
+$c(x,y)$, equals $(cx,cy)$. Thus, for example, $2z=2(x,y)=(2x,2y)$ turns
+out to be equal to $z+z$. ^^{multiplication of vector by scalar}
+In the special case $c=-1$, we write $-(x,y)=(-x,-y)$. ^^{negation of vectors}
+
+Now we come to an important notion, based on the fact that subtraction
+is the opposite of addition. {\sl If $z_1$ and $z_2$ are any two points,
+then $z_2-z_1$ is the vector that corresponds to moving from $z_1$ to~$z_2$.}
+The reason is simply that $z_2-z_1$ is what we must add to~$z_1$ in order
+to get~$z_2$: i.e., $z_1+(z_2-z_1)=z_2$. We shall call this the
+{\sl ^{vector subtraction principle}}. ^^{subtraction of vectors}
+It is used frequently in \MF\ programs when the designer wants to specify the
+direction and/or distance of one point from another.
+
+\MF\ programs often use another idea to express relations between points.
+Suppose we start at point~$z_1$ and travel in a straight line from there
+in the direction of point~$z_2$, but we don't go all the way. There's a
+special notation for this, using square brackets: ^^{bracket notation}
+\begindisplay \advance\baselineskip by 3pt
+${1\over3}[z_1,z_2]$ is the point one-third of the way from $z_1$ to $z_2$,\cr
+${1\over2}[z_1,z_2]$ is the point midway between $z_1$ and $z_2$,\cr
+$.8[z_1,z_2]$ is the point eight-tenths of the way from $z_1$ to $z_2$,\cr
+\enddisplay
+and, in general, $t[z_1,z_2]$ stands for the point that lies a fraction
+$t$ of the way from $z_1$ to~$z_2$. We call this the operation of {\sl
+^{mediation}\/} between points, or (informally) the ``^{of-the-way
+function}.'' If the fraction~$t$ increases from 0 to~1, the expression
+$t[z_1,z_2]$ traces out a straight line from $z_1$ to~$z_2$. According to
+the vector subtraction principle, we must move $z_2-z_1$ in order to go all
+the way from $z_1$ to~$z_2$, hence the point $t$~of~the~way between them is
+\begindisplay
+$t[z_1,z_2]\;=\;z_1+t(z_2-z_1)$.
+\enddisplay
+This is a general formula by which we can calculate $t[z_1,z_2]$ for any
+given values of $t$, $z_1$, and~$z_2$. But \MF\ has this formula built~in,
+so we can use the bracket notation explicitly.
+
+For example, let's go back to our first six example points, and suppose
+that we want to refer to the point that's 2/5 of the way from
+$z_2=(100,100)$ to $z_6=(200,0)$. In \MF\ we can write this simply as
+$.4[z_2,z_6]$. And if we need to compute the exact coordinates for some
+reason, we can always work them out from the general formula, getting
+$z_2+.4(z_6-z_2)=(100,100)+.4\bigl((200,0)-(100,100)\bigr)=(100,100)
++.4(100,-100)=(100,100)+(40,-40)=(140,60)$.
+
+\exercise True or false: The direction vector from $(5,-2)$ to $(2,3)$
+is $(-3,5)$.
+\answer True; this is $(2,3)-(5,-2)$.
+
+\exercise Explain what the notation `$0[z_1,z_2]$' means, if anything.
+What about `$1[z_1,z_2]$'? And `$2[z_1,z_2]$'? And `$(-.5)[z_1,z_2]$'?
+\answer $0[z_1,z_2]=z_1$, because we move none of the way towards~$z_2$;
+similarly $1[z_1,z_2]$ simplifies to~$z_2$, because we move all of the
+way. If we keep going in the same direction until we've gone twice as far
+as the distance from $z_1$ to~$z_2$, we get to $2[z_1,z_2]$. But if we
+start at point~$z_1$ and face~$z_2$, then back up exactly half the distance
+between them, we wind up at $(-.5)[z_1,z_2]$.
+
+\exercise True or false, for mathematicians: (a)~${1\over2}[z_1,z_2]=
+{1\over2}(z_1+z_2)$; \ (b)~${1\over3}[z_1,z_2]={1\over3}z_1+{2\over3}z_2$;
+\ (c)~$t[z_1,z_2]=(1-t)[z_2,z_1]$.
+\answer (a)~True; both are equal to $z_1+{1\over2}(z_2-z_1)$.
+(b)~False, but close; the right-hand side should be
+${2\over3}z_1+{1\over3}z_2$. (c)~True; both are equal to $(1-t)z_1+tz_2$.
+
+\setbox0=\vtop{\kern -6pt
+ \rightline{\rlap{\vbox to 250\apspix{
+ \setbox2=\vbox{\kern-1pt
+ \hbox{\tenex\char'77} % vertical arrow extension module
+ \kern-1pt}
+ \offinterlineskip
+ \vbox{\hbox{\tenex\char'170}\kern-1pt} % arrowhead at top
+ \cleaders\copy2\vfill
+ \kern3pt
+ \hbox to\wd2{\hss$b$\hss}
+ \kern3pt
+ \cleaders\copy2\vfill
+ \vbox{\kern-1pt\hbox{\tenex\char'171}\kern0pt} % arrowhead at bottom
+ }}\kern 30\apspix
+ \vbox{%\kern-.2pt \hrule \kern-.2pt
+ \hbox{%\kern-.2pt \vrule \kern-.2pt
+ \kern30\apspix\figbox{2e}{150\apspix}{250\apspix}\vbox
+% \kern30\apspix\kern-.2pt\vrule \kern-.2pt}
+% \kern-.2pt \hrule \kern-.2pt}\quad}
+ \kern30\apspix}
+ }\quad}
+ \kern2pt
+ \rightline{\hbox to 30\apspix{\kern-.2pt\vrule height 7pt depth 2pt
+ \hfil$s$\hfil\vrule\kern-.2pt}%
+ \hbox to 150\apspix{\leftarrowfill$\,a\,$\rightarrowfill}%
+ \hbox to 30\apspix{\kern-.2pt\vrule height 7pt depth 2pt
+ \hfil$s$\hfil\vrule\kern-.2pt}\quad}}
+\dp0=0pt
+
+\hangindent-300\apspix \hangafter-13
+Let's conclude \strut\vadjust{\box0}%
+this chapter by using mediation
+to help specify the five points in the stick-figure `{\manual\Aa}'
+shown enlarged at the right. The distance between points 1 and~5
+should be~$a$, and point~3 should be $b$ pixels above the baseline;
+these values $a$ and~$b$ have been predetermined by some method
+that doesn't concern us here, and so has a ``^{sidebar}'' parameter~$s$
+that specifies the horizontal distance of points 1 and~5 from the
+edges of the type. We shall assume that we don't know for sure what
+the height of the bar line should be; point~2 should be somewhere on the
+straight line from point~1 to point~3, and point~4 should be in the
+corresponding place between 5 and~3, but we want to try several
+possibilities before we make a decision.
+
+The width of the character will be $s+a+s$, and we can specify points
+$z_1$ and $z_5$ by the equations
+\begindisplay
+$"bot"\,z_1=(s,0)$;\qquad $z_5=z_1+(a,0)$.
+\enddisplay
+There are other ways to do the job, but these formulas clearly express
+our intention to have the bottom of the pen at the baseline, $s$ pixels
+to the right of the reference point, when the pen is at~$z_1$,
+and to have $z_5$ exactly $a$~pixels to the right of~$z_1$.
+Next, we can say
+\begindisplay
+$z_3=\bigl({1\over2}[x_1,x_5],b\bigr)$;
+\enddisplay
+this means that the $x$ coordinate of point 3 should be halfway between
+the $x$~coordinates of points 1 and~5, and that $y_3=b$. Finally, let's say
+\begindisplay
+$z_2="alpha"[z_1,z_3]$;\qquad $z_4="alpha"[z_5,z_3]$;
+\enddisplay
+the parameter "alpha" is a number between 0 and~1 that governs the
+position of the bar line, and it will be supplied later. When "alpha"
+has indeed received a value, we can say
+\begindisplay
+@draw@ $z_1\to z_3$;\qquad @draw@ $z_3\to z_5$;\qquad @draw@ $z_2\to z_4$.
+\enddisplay
+\MF\ will draw the characters `{\manual\sevenAs}' when "alpha" varies
+from 0.2 to 0.5 in steps of 0.05 and when $a=150$, $b=250$, $s=30$.
+The illustration on the previous page has $"alpha"=(3-\sqrt5\,)/2\approx
+0.38197$; this value makes the ratio of the area below the bar to the area
+above it equal to $(\sqrt5+1)/2\approx1.61803$, the so-called ``^{golden
+ratio}'' of classical Greek mathematics.
+
+\danger (Are you sure you should be reading this paragraph? The
+``^{dangerous bend}'' sign here is meant to warn you about material that
+ought to be skipped on first reading. And maybe also on second reading.
+The reader-beware paragraphs sometimes refer to concepts that aren't
+explained until later chapters.)
+
+\dangerexercise Why is it better to define $z_3$ as $\bigl({1\over2}[x_1,
+x_5],b\bigr)$, rather than to work out the explicit coordinates
+$z_3=(s+{1\over2}a,\,b)$ that are implied by the other equations?
+\answer There are several reasons. (1)~The equations in a \MF\ program
+should represent the programmer's intentions as directly as possible;
+it's hard to understand those intentions if you are shown only
+their ultimate consequences, since it's not easy to reconstruct algebraic
+manipulations that have gone on behind the scenes. (2)~It's easier and
+safer to let the computer do algebraic calculations, rather than
+to do them by hand. (3)~If the specifications for $z_1$ and $z_5$ change,
+the formula $\bigl({1\over2}[x_1,x_5],b\bigr)$
+still gives a reasonable value for~$z_3$. It's
+almost always good to anticipate the need for subsequent modifications.\par
+However, the stated formula for $z_3$ isn't the only reasonable way to
+proceed. We could, for example, give two equations
+\begindisplay
+$x_3-x_1=x_5-x_3$;\qquad $y_3=b$;
+\enddisplay
+the first of these states that the horizontal distance from 1 to 3 is
+the same as the horizontal distance from 3 to~5. We'll see later that
+\MF\ is able to solve a wide variety of equations.
+
+\ninepoint % all dangerous from here
+\ddangerexercise Given $z_1$, $z_3$, and $z_5$ as above, explain how
+to define $z_2$ and~$z_4$ so that all of the following conditions hold
+simultaneously:
+\enddanger
+
+\smallskip
+\item\bull the line from $z_2$ to $z_4$ slopes upward at a $20^\circ$ angle;
+
+\item\bull the $y$ coordinate of that line's midpoint is 2/3 of the
+way from $y_3$ to $y_1$;
+
+\item\bull $z_2$ and $z_4$ are on the respective lines $z_1\to z_3$ and
+$z_3\to z_5$.
+
+\smallskip\noindent
+(If you solve this exercise, you deserve an `{\manual\Az}'.)
+\answer The following four equations suffice to define the four
+unknown quantities $x_2$, $y_2$, $x_4$, and $y_4$:
+$z_4-z_2="whatever"\ast{\rm dir}\,20$;
+${1\over2}[y_2,y_4]={2\over3}[y_3,y_1]$;
+$z_2="whatever"[z_1,z_3]$;
+$z_4="whatever"[z_3,z_5]$. ^^"whatever" ^^{dir}
+
+\endchapter
+
+Here, where we reach the sphere of mathematics,
+we are among processes which seem to some
+the most inhuman of all human activities
+and the most remote from poetry.
+Yet it is here that the artist has the fullest scope for his imagination.
+\author HAVELOCK ^{ELLIS}, {\sl The Dance of Life\/} (1923) % pp 138--139
+
+\bigskip
+
+To anyone who has lived in a modern American city (except Boston)
+at least one of the underlying ideas of ^{Descartes}' analytic geometry
+will seem ridiculously evident. Yet, as remarked,
+it took mathematicians all of two thousand years
+to arrive at this simple thing.
+\author ERIC TEMPLE ^{BELL}, {\sl Mathematics: Queen and Servant of %
+ Science\/} (1951) % p123
+
+\eject
+ \beginchapter Chapter 3. Curves
+
+Albrecht ^{D\"urer} and other Renaissance men attempted to establish
+mathematical principles of type design, but the letters they came up with
+were not especially beautiful. Their methods failed because they
+restricted themselves to ``ruler and compass'' constructions, which cannot
+adequately express the nuances of good calligraphy. \MF\ gets around this
+problem by using more powerful mathematical techniques, which provide the
+necessary flexibility without really being too complicated. The purpose of
+the present chapter is to explain the simple principles by which a
+computer is able to draw ``pleasing'' ^{curves}.
+
+The basic idea is to start with four points $(z_1,z_2,z_3,z_4)$ and to
+^^{four-point method for curves}
+construct the three ^{midpoints} $z_{12}={1\over2}[z_1,z_2]$,
+$z_{23}={1\over2}[z_2,z_3]$, $z_{34}={1\over2}[z_3,z_4]$:
+\displayfig 3a (5pc)
+Then take those three midpoints $(z_{12},z_{23},z_{34})$ and construct
+two second-order midpoints $z_{123}={1\over2}[z_{12},z_{23}]$ and
+$z_{234}={1\over2}[z_{23},z_{34}]$; finally, construct the third-order
+midpoint $z_{1234}={1\over2}[z_{123},z_{234}]$:
+\displayfig 3b (5pc)
+This point $z_{1234}$ is one of the points of the curve determined by
+$(z_1,z_2,z_3,z_4)$. To get the remaining points of that curve,
+repeat the same construction on $(z_1,z_{12},z_{123},z_{1234})$ and
+on $(z_{1234},z_{234},z_{34},z_4)$, ad infinitum:
+\displayfig 3c (4.5pc)
+The process converges quickly, and the preliminary scaffolding
+(which appears above the limiting curve in our example) is ultimately discarded.
+The limiting curve has the following important properties:
+
+\smallskip
+\item\bull It begins at $z_1$, heading in the direction from $z_1$ to $z_2$.
+
+\item\bull It ends at $z_4$, heading in the direction from $z_3$ to $z_4$.
+
+\item\bull It stays entirely within the so-called convex hull of $z_1$,
+$z_2$, $z_3$, and $z_4$; i.e., all points of the curve lie ``between'' the
+defining points.
+
+\danger The recursive midpoint rule for curve-drawing was discovered in 1959
+by Paul ^{de Casteljau}, who showed that the curve could be described
+algebraically by the remarkably simple formula
+\begindisplay
+$z(t)\;=\;(1-t)^3z_1+3(1-t)^2t\,z_2+3(1-t)t^2z_3+t^3z_4$,
+\enddisplay
+as the parameter $t$ varies from 0 to 1. This polynomial of degree~3 in~$t$
+is called a {\sl ^{Bernshte{\u\i}n polynomial}}, because Serge\u\i~N.
+^{Bernshte{\u\i}n} introduced such functions in 1912 as part of his
+pioneering work on approximation theory. Curves traced out by Bernshte{\u\i}n
+polynomials of degree~3 are often called {\sl B\'ezier cubics}, after
+Pierre ^{B\'ezier} who realized their importance for computer-aided design
+during the 1960s.
+
+\danger It is interesting to observe that the Bernshte\u\i n polynomial
+of degree~1, i.e., the function $z(t)=(1-t)\,z_1+t\,z_2$, is precisely the
+^{mediation} operator $t[z_1,z_2]$ that we discussed in the previous chapter.
+Indeed, if the geometric construction we have just seen is changed to
+use $t$-of-the-way points instead of midpoints (i.e., if $z_{12}=
+t[z_1,z_2]$ and $z_{23}=t[z_2,z_3]$, etc.), then $z_{1234}$ turns out
+to be precisely $z(t)$ in the formula above.
+
+No matter what four points $(z_1,z_2,z_3,z_4)$ are given, the construction
+on the previous page defines a curved line that runs from $z_1$ to~$z_4$.
+This curve is not always interesting or beautiful; for example, if all
+four of the given points lie on a straight line, the entire ``curve''
+that they define will also be contained in that same line. We obtain
+rather different curves from the same four starting points if we
+number the points differently:
+\displayfig 3d (7.05pc)
+Some discretion is evidently advisable when the $z$'s are chosen. But the
+four-point method is good enough to obtain satisfactory approximations to
+any curve we want, provided that we break the desired curve into short
+enough segments and give four suitable control points for each segment.
+It turns out, in fact, that we can usually get by with only a few segments.
+For example, the four-point method can produce an approximate
+quarter-circle with less than 0.06\% error; it never yields an exact
+circle, but the differences between four such quarter-circles and a true
+circle are imperceptible.
+
+All of the curves that \MF\ draws are based on four points, as just
+described. But it isn't necessary for a user to specify all of those
+points, because the computer is usually able to figure out good values of
+$z_2$ and $z_3$ by itself. Only the endpoints $z_1$ and~$z_4$, through
+which the curve is actually supposed to pass, are usually mentioned
+explicitly in a \MF\ program.
+
+For example, let's return to the six points that were used to introduce the
+ideas of coordinates in Chapter~2. We said `@draw@ $z_1\to z_6$' in that
+chapter, in order to draw a straight line from point~$z_1$ to point~$z_6$.
+In general, if three or more points are listed instead of two, \MF\ will draw a
+^^{..} smooth curve through all the points. For example, the commands
+`@draw@ $z_4\to z_1\to z_2\to z_6$' and `@draw@ $z_5\to z_4\to z_1
+\to z_3\to z_6\to z_5$' will produce the respective results
+\displayfig 3e (7.75pc)
+(Unlabeled points in these diagrams are ^{control points} that \MF\ has
+supplied automatically so that it can use the four-point scheme to draw
+curves between each pair of adjacent points on the specified paths.)
+
+Notice that the curve is not smooth at $z_5$ in the right-hand example,
+because $z_5$~appears at both ends of that particular path. In order to
+get a completely smooth curve that returns to its starting point, you can
+say `@draw@ $z_5\to z_4\to z_1\to z_3\to z_6\to \cycle$' instead:
+\displayfig 3f (7.25pc)
+The word `^{cycle}' at the end of a path refers to the starting point
+of that path.
+\MF\ believes that this ^{bean-like shape}
+is the nicest way to connect the given points in the given cyclic order;
+but of course there are many decent curves that satisfy the specifications,
+and you may have another one in mind. You can obtain finer control
+by giving hints to the machine in various ways. For example, the
+bean curve can be ``pulled tighter'' between $z_1$ and~$z_3$ if you say
+\begindisplay
+@draw@ $z_5\to z_4\to z_1\to\tension1.2\to z_3\to z_6\to \cycle$;
+\enddisplay
+the so-called ^{tension} between points is normally 1, and an increase
+to 1.2 yields
+\displayfig 3g (5.75pc)
+
+\danger An asymmetric effect can be obtained by increasing the tension
+only at point~1 but not at points 3~or~4; the shape
+\displayfig 3h (6.5pc)
+comes from
+%\begindisplay
+%@draw@ $z_5\to z_4\to\tension1\and1.5\to z_1\to
+% \tension1.5\and1\to z_3$\cr
+%\hskip6em$\to z_6\to \cycle$.
+%\enddisplay
+`@draw@ $z_5\to z_4\to\tension1\and1.5\to z_1\to
+ \tension1.5\and1\to z_3\to z_6\to \cycle$'.
+The effect of tension has been achieved in this example by moving two of
+the anonymous control points closer to point~1.
+
+It's possible to control a curve in another way, by telling \MF\ what
+direction to travel at some or all of the points. Such directions are
+given inside curly braces; for example,
+\begindisplay
+@draw@ $z_5\to z_4\{"left"\}\to z_1\to z_3\to z_6\{"left"\}\to\cycle$
+\enddisplay
+says that the curve should be traveling leftward at points 4 and 6. The
+resulting curve is perfectly straight from $z_6$ to~$z_5$ to~$z_4$:
+\displayfig 3i (5.8pc)
+We will see later that `"left"' is an abbreviation for the vector $(-1,0)$,
+which stands for one unit of travel in a leftward direction. Any desired
+direction can be specified by enclosing a vector in $\{\ldots\}$'s; for
+example, the command `@draw@ $z_4\to z_2\{z_3-z_4\}\to z_3$' will draw a
+curve from $z_4$ to~$z_2$ to~$z_3$ such that the tangent direction at
+$z_2$ is parallel to the line $z_4\to z_3$, because $z_3-z_4$ is the
+vector that represents travel from $z_4$ to~$z_3$:
+\displayfig 3j (4.7pc)
+The same result would have been obtained from a command such as `@draw@
+$z_4\to z_2 \{10(z_3-z_4)\}\to z_3$', because the vector $10(z_3-z_4)$ has
+the same direction as $z_3-z_4$. \MF\ ignores the magnitudes of vectors
+when they are simply being used to specify directions.
+
+\exercise What do you think will be the result of
+`@draw@ $z_4\to z_2\{z_4-z_3\}\to z_3$', when points $z_2$, $z_3$,~$z_4$
+are the same as they have been in the last several examples?
+\answer The direction at $z_2$ is parallel to the line $z_4\to z_3$, but
+the vector $z_4-z_3$ specifies a direction towards $z_4$, which is
+$180^\circ$ different from the direction $z_3-z_4$ that was discussed in
+the text. Thus, we have a difficult specification to meet, and \MF\ draws
+a pretzel-shaped curve that loops around in a way that's too ugly to show
+here. The first part of the path, from $z_4$ to $z_2$, is mirror symmetric
+about the line~$z_1\to z_5$ that bisects $z_4\to z_2$, so it starts out in a
+south-by-southwesterly direction; the second part is mirror symmetric about
+the vertical line that bisects $z_2\to z_3$, so when the curve ends at~$z_3$
+it's traveling roughly northwest. The moral is: Don't specify a direction
+that runs opposite to (i.e., is the negative of) the one you really want.
+
+\exercise Explain how to get \MF\ to draw the wiggly shape
+\displayfig 3k (5pc)
+in which the curve aims directly at point 2 when it's at point~6, but
+directly away from point~2 when it's at point~4. [{\sl Hint:\/} No
+tension changes are needed; it's merely necessary to specify directions
+at $z_4$ and~$z_6$.]
+\answer @draw@ $z_5\to z_4\{z_4-z_2\}\to z_1\to z_3\to z_6\{z_2-z_6\}
+\to\cycle$.
+
+\MF\ allows you to change the shape of a curve at its endpoints by
+specifying different amounts of ``^{curl}.'' For example, the two commands
+\begindisplay
+@draw@ $z_4\{\curl0\}\to z_2\{z_3-z_4\}\to\{\curl0\}\,z_3$;\cr
+@draw@ $z_4\{\curl2\}\to z_2\{z_3-z_4\}\to\{\curl2\}\,z_3$\cr
+\enddisplay
+give the respective curves
+\displayfig 3l (5pc)
+which can be compared with the one shown earlier when no special curl was
+requested. \ (The specification `$\curl1$' is assumed at an endpoint
+if no explicit curl or direction has been mentioned, just as
+`$\tension1$' is implied between points when no tension has
+been explicitly given.) \ Chapter 14 explains more about~this.
+
+It's possible to get curved lines instead of straight lines even when
+only two points are named, if a direction has been prescribed at one or
+both of the points. For example,
+\begindisplay
+@draw@ $z_4\{z_2-z_4\}\to\{"down"\}\,z_6$\cr
+\enddisplay
+asks \MF\ for a curve that starts traveling towards $z_2$ but finishes
+in a downward direction:
+\displayfig 3m (4pc)
+
+\danger Here are some of the curves that \MF\ draws between two points, when
+it is asked to move outward from the left-hand point at an angle of
+$60^\circ$, and to approach the right-hand point at various angles:
+\displayfig 3aa (2.6cm)
+This diagram was produced by the \MF\ program ^^@for@ ^^@step@ ^^@until@ ^^"cm"
+\begindisplay
+@for@ $d=0$ @step@ 10 @until@ 120:\cr
+\indent @draw@ $(0,0)\{{\rm dir}\,60\}\to\{{\rm dir}\,{-d}\}(6"cm",0)$;
+ @endfor@;\cr
+\enddisplay
+the `^{dir}' function specifies a direction measured in degrees
+counterclockwise from a horizontal rightward line, hence `${\rm dir}\,{-d}$'
+gives a direction that is $d^\circ$ below the horizon. The lowest curves
+in the illustration correspond to small values of $d$, and the highest
+curves correspond to values near $120^\circ$.
+
+\danger A car that drives along the upper paths in the diagram above
+is always turning to the right, but in the lower paths it comes to a
+point where it needs to turn to the left in order to reach its destination
+from the specified direction.
+The place where a path changes its curvature from right to left or
+vice versa is called an ``^{inflection point}.'' \MF\ introduces
+inflection points when it seems better to change the curvature than
+to make a sharp turn; indeed, when $d$ is negative there is no way to
+avoid points of inflection, and the curves for small positive~$d$ ought to
+be similar to those obtained when $d$~has small negative values. The program
+\begindisplay
+@for@ $d=0$ @step@ $-10$ @until@ $-90$:\cr
+\indent @draw@ $(0,0)\{{\rm dir}\,60\}\to\{{\rm dir}\,{-d}\}(6"cm",0)$;
+ @endfor@\cr
+\enddisplay
+shows what \MF\ does when $d$ is negative:
+\displayfig 3bb (2.8cm)
+
+\danger It is sometimes desirable to avoid points of inflection, when $d$ is
+positive, and to require the curve to remain inside the triangle
+determined by its initial and final directions. This can be achieved
+^^{...}
+by using three dots instead of two when you specify a curve: The program
+\begindisplay
+@for@ $d=0$ @step@ 10 @until@ 120:\cr
+\indent @draw@ $(0,0)\{{\rm dir}\,60\}\ldots\{{\rm dir}\,{-d}\}(6"cm",0)$;
+ @endfor@\cr
+\enddisplay
+generates the curves
+\displayfig 3cc (2.6cm)
+which are the same as before except that inflection points do not occur
+for the small values of~$d$. The `$\ldots$' specification keeps the
+curve ``^{bounded}'' inside the triangle that is defined by the endpoints
+and directions; but it has no effect when there is
+no such triangle. More precisely, suppose that the curve goes from $z_0$
+to~$z_1$; if there's a point~$z$ such that the initial direction is from
+$z_0$ to~$z$ and the final direction is from $z$ to~$z_1$, then the curve
+specified by `$\ldots$' will stay entirely within the triangle whose
+corners are $z_0$, $z_1$, and~$z$. But if there's no such triangle
+(e.g., if $d<0$ or $d>120$ in our example program), both `$\ldots$'
+and~`$\to$' will produce the same curves.
+
+In this chapter we have seen lots of different ways to get \MF\ to draw
+curves. And there's one more way, which subsumes all of the others.
+If changes to tensions, curls, directions, and/or boundedness
+aren't enough to produce the sort of curve that a person wants, it's
+always possible as a last resort to specify all four of the points in the
+four-point method. For example, the command
+\begindisplay
+@draw@ $z_4\to\controls z_1\and z_2\to z_6$
+\enddisplay
+will draw the following curve from $z_4$ to $z_6$:^^{controls}
+\displayfig 3n (5pc)
+
+
+\endchapter
+
+And so I think I have omitted nothing
+% Et ainsi ie pense n'auoir rien omis des elemens,
+that is necessary to an understanding of curved lines.
+% qui sont necessaires pour la connoissance des lignes courbes.
+\author REN\'E ^{DESCARTES}, {\sl La G\'eom\'etrie\/} (1637) % p369
+
+\bigskip
+
+Rules or substitutes for the artist's hand must necessarily be inadequate,
+although, when set down by such men as
+^{D\"urer}, ^{Tory}, ^{Da Vinci}, ^{Serlio}, and others,
+they probably do establish canons of proportion and construction
+which afford a sound basis upon which to present new expressions.
+\author FREDERIC W. ^{GOUDY}, {\sl Typologia\/} (1940) % p 138f
+
+\eject
+ \beginchapter Chapter 4. Pens
+
+Our examples so far have involved straight lines or curved lines that look
+as if they were drawn by a felt-tip ^{pen}, where the ^{nib} of that pen
+was perfectly round. A mathematical ``line'' has no thickness, so it's
+invisible; but when we plot circular dots at each point of an infinitely
+thin line, we get a visible line that has constant thickness.
+
+Lines of constant thickness have their uses, but \MF\ also provides
+several other kinds of scrivener's tools, and we shall take a look at some
+of them in this chapter. We'll see not only that the sizes and shapes of
+pen nibs can be varied, but also that characters can be built up in such a
+way that the outlines of each stroke are precisely controlled.
+
+\def\kk{\kern2pt } % kidney-bean kern
+First let's consider the simplest extensions of what we have seen before.
+The letter `{\manual\Aa}' of Chapter~2 and the kidney-^{bean}
+`\kk{\manual\beana}\kk' of Chapter~3 were drawn with circular pen nibs of
+diameter $0.4\pt$, where `pt' stands for a printer's point;\footnote*{$
+1\,{\rm in}=2.54\,{\rm cm}=72.27\pt$ exactly, as explained in
+{\sl The \TeX book}.} $0.4\pt$ is the standard thickness of a ruled line
+`$\,\vcenter{\hrule width 2em}\,$' drawn by \TeX. Such a penpoint can be
+specified by telling \MF\ to
+\begindisplay
+\pickup @pencircle@ ^{scaled} $0.4"pt"$;
+\enddisplay
+\MF\ will use the pen it has most recently picked up ^^@pickup@
+whenever it is asked to `^@draw@' anything. A ^@pencircle@ is a
+circular pen whose diameter is the width of one pixel. Scaling it
+by $0.4"pt"$ will change it to the size that corresponds
+to $0.4\pt$ in the output, because ^"pt" is the number of pixels
+in $1\pt$. If the key points $(z_1,z_2,z_3,z_4,z_5,z_6)$ of Chapters 2 and~3
+have already been defined, the \MF\ commands
+\begindisplay
+\pickup @pencircle@ scaled $0.8"pt"$;\cr
+@draw@ $z_5\to z_4\to z_1\to z_3\to z_6\to \cycle$\cr
+\enddisplay
+will produce a bean shape twice as thick as before: `\kk{\manual\beanb}\kk'
+instead of `\kk{\manual\beana}\kk'.
+
+More interesting effects arise when we use non-circular pen nibs. For example,
+the command
+\begindisplay
+\pickup @pencircle@ ^{xscaled} $0.8"pt"$ ^{yscaled} $0.2"pt"$
+\enddisplay
+picks up a pen whose tip has the shape of an ellipse, $0.8\pt$ wide and
+$0.2\pt$ tall; magnified 10 times, it looks like this:
+`$\,\vcenter{\hbox{\manual\niba}}\,$'.
+\ (The operation of ``xscaling'' multiplies $x$~coordinates by a specified
+amount but leaves $y$~coordinates unchanged, and the operation of
+``yscaling'' is similar.) \ Using such a pen, the `\kk{\manual\beana}\kk'
+becomes `\kk{\manual\beanc}\kk', and `{\manual\Aa}' becomes `{\manual\Ab}'.
+Furthermore,
+\begindisplay
+\pickup @pencircle@ xscaled $0.8"pt"$ yscaled $0.2"pt"$ ^{rotated} 30
+\enddisplay
+takes that ellipse and rotates it $30^\circ$ counterclockwise, obtaining the nib
+`$\vcenter{\hbox{\manual\nibb}}$'; this changes `\kk{\manual\beanc}\kk' into
+`\kk{\manual\beand}\kk' and `{\manual\Ab}' into `{\manual\Ac}'. An
+enlarged view of the bean shape shows more clearly what is going on:
+\displayfig 4a (7pc)
+The right-hand example was obtained by eliminating the clause
+`yscaled~$0.2"pt"$'; this makes the pen almost razor thin, only
+one pixel tall before rotation.
+
+\exercise Describe the pen shapes defined by
+(a)~@pencircle@ xscaled~$0.2"pt"$ yscaled~$0.8"pt"$;
+\ (b)~@pencircle@ scaled~$0.8"pt"$ rotated~30;
+\ (c)~@pencircle@ xscaled~.25 scaled~$0.8"pt"$.
+\answer (a)~An ellipse $0.8\pt$ tall and $0.2\pt$ wide
+(`$\,\vcenter{\hbox{\manual\nibc}}\,$');
+\ (b)~a~circle of diameter $0.8\pt$ (rotation doesn't change a circle!);
+\ (c)~same as~(a).
+
+\exercise We've seen many examples of `^@draw@'
+used with two or more points. What do you think \MF\ will do
+if you ask it to perform the following commands?
+\begindisplay
+@draw@ $z_1$;\ @draw@ $z_2$; \ @draw@ $z_3$; \ @draw@ $z_4$;
+ \ @draw@ $z_5$; \ @draw@ $z_6$.
+\enddisplay
+\answer Six individual points will be drawn, instead of lines or curves.
+These points will be drawn with the current pen. However, for technical
+reasons explained in Chapter~24, the @draw@ command does its best work when it
+is moving the pen; the pixels you get at the endpoints of curves are
+not always what you would expect, especially at low resolutions. It is
+usually best to say `^@drawdot@' instead of `@draw@' when you are drawing
+only ^{one point}.
+
+\def\hidecoords(#1,#2){\hbox to 0pt{\hss$\scriptstyle(#1,#2)$\hss}}
+\setbox0=\vtop{\kern 42pt
+ \rightline{\vbox{\hbox to 208\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern3pt
+ \figbox{4b}{208\apspix}{216\apspix}\vbox
+ \kern-3pt
+ \hbox to 208\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\quad}}
+\dp0=0pt
+
+\hangindent-125pt \hangafter4
+\indent\strut\vadjust{\box0}%
+Let's turn now to the design of a real letter that has already appeared
+many times in this manual, namely the `\thinspace{\manual ^{T}}\thinspace' of
+`\MF'. All seven of ^^{METAFONT logo} the distinct letters in `\MF' will
+be used to illustrate various ideas as we get into the details of the
+language; we might as well start with~`\thinspace{\manual T}\thinspace',
+because it occurs twice, and (especially) because it's the simplest. An
+enlarged version of this letter is shown at the right of this paragraph,
+including the locations of its four key points $(z_1,z_2,z_3,z_4)$ and its
+^{bounding box}. Typesetting systems like \TeX\ are based on the
+assumption that each character fits in a rectangular ^{box}; we shall
+discuss boxes in detail later, but for now we will be content simply to
+know that such boundaries do exist.\footnote*{Strictly speaking, the
+bounding box doesn't actually have to ``bound'' the black pixels of a
+character; for example, the `\thinspace{\manual q}\thinspace' protrudes
+slightly below the baseline at point~4, and italic letters frequently
+extend rather far to the right of their boxes. However, \TeX\ positions
+all characters by lumping boxes together as if they were pieces of metal
+type that contain all of the ink.} Numbers $h$ and~$w$ ^^"h" ^^"w" will
+have been computed so that the corners of the box are at positions
+$(0,0)$, $(0,h)$, $(w,0)$, and~$(w,h)$ as shown.
+
+\hangindent-125pt
+\hangafter\prevgraf \advance\hangafter by -16 % 4+12 (12 lines for the figure)
+Each of the letters in `\MF' is drawn with a pen whose nib is an unrotated
+ellipse, 90\% as tall as it is wide. In the 10-point size, which is used
+for the main text of this book, the pen is $2/3\pt$ wide, so it has
+been specified by the command
+\begindisplay
+\pickup @pencircle@ scaled $2\over3$"pt" yscaled $9\over10$
+\enddisplay
+or something equivalent to this.
+
+We shall assume that a special value `$o$' has been computed so that the
+bottom of the vertical stroke in `\thinspace{\manual T}\thinspace' should
+descend exactly $o$~pixels below the baseline; ^^"o" this is called the
+amount of ``^{overshoot}.'' Given $h$, $w$, and~$o$, it is a simple matter
+to define the four key points and to draw the
+`\thinspace{\manual T}\thinspace': ^^"top" ^^"lft" ^^"rt" ^^"bot"
+\begindisplay
+$"top"\,"lft"\,z_1=(0,h)$; \quad $"top"\,"rt"\,z_2=(w,h)$;\cr
+$"top"\,z_3=(.5w,h)$; \quad $"bot"\,z_4=(.5w,-o)$;\cr
+@draw@ $z_1\to z_2$; \quad @draw@ $z_3\to z_4$.\cr
+\enddisplay
+
+\danger Sometimes it is easier and/or clearer to define the $x$ and~$y$
+^{coordinates} separately. For example, the key points of
+the~`\thinspace{\manual j}\thinspace'
+could also be specified thus:
+\begindisplay
+$"lft"\,x_1=0$;&$w-x_2=x_1$;&$x_3=x_4=.5w$;\cr
+$"top"\,y_1=h$;&$"bot"\,y_4=-o$;&$y_1=y_2=y_3$.\cr
+\enddisplay
+The equation $w-x_2=x_1$ expresses the fact that $x_2$ is just as far from
+the right edge of the bounding box as $x_1$ is from the left edge.
+
+\danger What exactly does `"top"\!' mean in a \MF\ equation? If the
+currently-picked-up pen extends $l$~pixels to the left of its center,
+$r$~pixels to the right, $t$~pixels upward and $b$~downward, then
+\begindisplay
+$"top"\,z=z+(0,t)$,\kern-1em&$"bot"\,z=z-(0,b)$,\kern-1em&
+$"lft"\,z=z-(l,0)$,\kern-1em&$"rt"\,z=z+(r,0)$,\cr
+\noalign{\vskip\belowdisplayskip
+\vbox{\noindent\strut
+when $z$ is a pair of coordinates. But---as the previous paragraph
+shows, if you study it carefully---we also have
+\strut}\vskip\abovedisplayskip}
+$"top"\,y=y+t$,&$"bot"\,y=y-b$,&
+$"lft"\,x=x-l$,&$"rt"\,x=x+r$,\cr
+\enddisplay
+when $x$ and $y$ are single values instead of coordinate pairs.
+You shouldn't apply `"top"\!' or `"bot"\!' to $x$~coordinates,
+nor `"lft"\!' or `"rt"\!' to $y$~coordinates.
+
+\dangerexercise True or false: $"top"\,"bot"\,z=z$, whenever $z$
+is a pair of coordinates.
+\answer True, for all of the pens discussed so far. But false in general,
+since we will see later that pens might extend further upward than
+downward; i.e., $t$~might be unequal to~$b$ in the equations for
+"top" and "bot".
+
+\setbox0=\vtop{\kern -12pt
+ \rightline{\vbox{\hbox to 288\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern3pt
+ \figbox{4c}{288\apspix}{216\apspix}\vbox
+ \kern-3pt
+ \hbox to 288\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\quad}}
+\dp0=0pt
+\begingroup\decreasehsize 165pt
+\dangerexercise An enlarged \strut\vadjust{\box0}%
+picture of \MF's `{\manual h}' shows that it has five key points. Assuming ^^{M}
+that special values $ss$ and~"ygap" have been precomputed and that the equations
+\begindisplay
+$x_1=ss=w-x_5$;\quad$y_3-y_1="ygap"$\cr
+\enddisplay
+have already been given, what further equations and `@draw@' ^^{METAFONT
+logo} commands will complete the specification of this letter? \ (The
+value of~$w$ will be greater for~`\thinspace{\manual h}\thinspace' than it was
+for~`\thinspace{\manual j}\thinspace'; it
+stands for the pixel width of whatever character is currently being drawn.)
+\answer $x_2=x_1$; $x_3={1\over2}[x_2,x_4]$; $x_4=x_5$; $"bot"\,y_1=-o$;
+$"top"\,y_2=h+o$; $y_4=y_2$; $y_5=y_1$; @draw@ $z_1\to z_2$;
+@draw@ $z_2\to z_3$; @draw@ $z_3\to z_4$; @draw@ $z_4\to z_5$.
+We will learn later that the four @draw@ commands can be replaced by
+\begindisplay
+@draw@ $z_1\dashto z_2\dashto z_3\dashto z_4\dashto z_5$;
+\enddisplay
+in fact, this will make \MF\ run slightly faster. ^^{--}
+
+\endgroup % end of the diminished \hsize
+
+\MF's ability to `@draw@' allows it to produce character shapes that are
+satisfactory for many applications, but the shapes are inherently limited
+by the fact that the simulated pen nib must stay the same through an
+entire stroke. Human penpushers are able to get richer effects by
+using different amounts of pressure and/or by rotating the pen as they draw.
+
+We can obtain finer control over the characters we produce if we specify
+their outlines, instead of working only with key points that lie somewhere
+in the middle. In fact, \MF\ works internally with outlines, and the
+computer finds it much easier to fill a region with solid black than to
+figure out what pixels are blackened by a moving pen. There's a `^@fill@'
+command that does region filling; for example, the solid ^{bean} shape
+\displayfig 4d (6.5pc)
+can be obtained from our six famous example points by giving the command
+\begindisplay
+@fill@ $z_5\to z_4\to z_1\to z_3\to z_6\to \cycle$.
+\enddisplay
+The filled region is essentially what would be cut out by an
+infinitely sharp ^{knife} blade if it traced over the given curve while
+cutting a piece of thin film. A @draw@ command needs to add thickness to
+its curve, because the result would otherwise be invisible; but a @fill@
+command adds no thickness.
+
+The curve in a @fill@ command must end with `^{cycle}', because an
+entire region must be filled. It wouldn't make sense to say, e.g.,
+`@fill@ $z_1\to z_2$'. The cycle being filled shouldn't cross itself,
+either; \MF\ would have lots of trouble trying to figure out how to
+obey a command like `@fill@ $z_1\to z_6\to z_3\to z_4\to\cycle$'.
+
+\dangerexercise Chapter 3 discusses the curve $z_5\to z_4\to z_1\to
+z_3\to z_6\to z_5$, which isn't smooth at~$z_5$. Since this curve
+doesn't end with `cycle', you can't use it in a @fill@ command.
+But it does define a closed region. How can \MF\ be instructed
+to fill that region?
+\answer Either say `@fill@ $z_5\to z_4\to z_1\to z_3\to z_6\to z_5\to
+\cycle$', which doubles point~$z_5$ and abandons smoothness there,
+or `@fill@ $z_5\{\curl1\}\to z_4\to z_1\to z_3\to z_6\to
+\{\curl1\}\cycle$'. In the latter case you can omit either one of
+the ^{curl} specifications, but not both.
+
+The black ^{triangle} `{\manual\char'170}' that appears in the statement of
+exercises in this book was drawn with the command
+\begindisplay
+@fill@ $z_1\dashto z_2\dashto z_3\dashto\cycle$
+\enddisplay
+after appropriate corner points $z_1$, $z_2$, and $z_3$ had been specified.
+In this case the outline of the region to be filled was specified in terms
+of the symbol `$\dashto$' instead of `$\to$'; ^^{--}^^{..}
+this is a convention we haven't discussed before. Each `$\dashto$'
+introduces a straight line segment, which is independent of the rest of
+^^{polygonal path}
+the path that it belongs to; thus it is quite different from `$\to$', which
+specifies a possibly curved line segment that connects smoothly with neighboring
+points and lines of a path. In this case `$\dashto$' was used so that the
+triangular region would have straight edges and sharp corners. We might say
+informally that `$\to$' means ``Connect the points with a nice curve,''
+while `$\dashto$' means ``Connect the points with a straight line.''
+
+\setbox0=\vtop{\kern -9pt
+ \rightline{\vbox{\hbox to 180\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern3pt
+ \figbox{4e}{180\apspix}{225\apspix}\vbox
+ \kern-3pt
+ \hbox to 180\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\quad}}
+\dp0=0pt
+\begingroup\decreasehsize 111pt
+
+\danger \strut\vadjust{\box0}%
+The corner points $z_1$, $z_2$, and $z_3$ were defined carefully
+so that the triangle would be {\sl^{equilateral}}, i.e., so that all three
+of its sides would have the same length. Since an equilateral triangle
+has $60^\circ$ angles, the following equations did the job:
+\begindisplay
+$x_1=x_2=w-x_3=s$;\cr
+$y_3=.5h$;\cr
+$z_1-z_2=(z_3-z_2)$ ^{rotated} 60.\cr
+\enddisplay
+Here $w$ and $h$ represent the character's width and height, and $s$~is
+the distance of the triangle from the left and right edges of the type.
+
+\endgroup % end of the diminished \hsize
+
+\danger The @fill@ command has a companion called ^@unfill@, which changes
+pixels from black to white inside a given region. For example, the solid
+bean shape on the previous page can be changed to
+\displayfig 4f (6.5pc)
+if we say also `@unfill@ ${1\over4}[z_4,z_2]\to{3\over4}[z_4,z_2]\to\cycle$;
+\ @unfill@ ${1\over4}[z_6,z_2]\to{3\over4}[z_6,z_2]\to\cycle$'.
+This example shows, incidentally, that \MF\ converts a two-point specification
+like `$z_1\to z_2\to\cycle$' into a more-or-less circular path, even though
+two points by themselves define only a straight line.
+
+\dangerexercise Let $z_0$ be the point $(.8[x_1,x_2],.5[y_1,y_4])$,
+and introduce six new points by letting $z'_k=.2[z_k,z_0]$ for $k=1,$ 2,
+\dots,~6. Explain how to obtain the shape
+\displayfig 4g (7.0pc)
+in which the interior region is defined by $z'_1\ldots z'_6$ instead of
+by $z_1\ldots z_6$.
+\answer After the six original points have been defined, say
+\begindisplay
+@fill@ $z_5\to z_4\to z_1\to z_3\to z_6\to\cycle$;\cr
+$z_0=(.8[x_1,x_2],.5[y_1,y_4])$;\cr
+@for@ $k=1$ @upto@ 6: $z_k'=.2[z_k,z_0]$; @endfor@\cr
+@unfill@ $z_5'\to z_4'\to z_1'\to z_3'\to z_6'\to\cycle$.\cr
+\enddisplay
+
+The ability to fill between outlines makes it possible to pretend that we
+have ^{broad-edge pens} that change in direction and pressure as they
+glide over the paper, if we consider the separate paths traced out by the
+pen's left edge and right edge. For example, the stroke
+\displayfig 4h (3.5pc)
+can be regarded as drawn by a pen that starts at the left, inclined
+at a $30^\circ$ angle; as the pen moves, it turns gradually until its
+^^{angle of pen} edge is strictly vertical by the time it reaches the
+right end. The pen motion was horizontal at positions 2 and~3. This stroke
+was actually obtained by the command
+\begindisplay
+@fill@ $z_{1l}\to z_{2l}\{"right"\}\to\{"right"\}\,z_{3l}$\cr
+$\hskip4em\dashto z_{3r}\{"left"\}\to\{"left"\}\,z_{2r}\to z_{1r}$\cr
+$\hskip4em\dashto\cycle$;
+\enddisplay
+i.e., \MF\ was asked to fill a region bounded by a ``left path'' from
+$z_{1l}$ to $z_{2l}$ to $z_{3l}$, followed by a straight line ^^{--}
+to~$z_{3r}$, then a reversed ``right path'' from $z_{3r}$ to $z_{2r}$ to
+$z_{1r}$, and finally a straight line back to the starting point~$z_{1l}$.
+
+Key positions of the ``pen'' are represented in this example by sets of
+three points, like $(z_{1l},z_1,z_{1r})$, which stand for the pen's left edge,
+its midpoint, and its right edge. The midpoint doesn't actually occur in the
+specification of the outline, but we'll see examples of its usefulness.
+The relationships between such triples of points are established by a
+`^"penpos"' command, which states the breadth of the pen and its angle of
+inclination at a particular position. For example, positions 1, 2, and~3
+in the stroke above were established by saying
+\begindisplay
+$\penpos1(1.2"pt",30)$;&
+$\penpos2(1.0"pt",45)$;&
+$\penpos3(0.8"pt",90)$;\cr
+\enddisplay
+this made the pen $1.2\pt$ broad and tipped $30^\circ$ with respect to
+the horizontal at position~1, etc. In general the idea is to specify
+`$\penpos k(b,d)$',
+where $k$ is the position number or position name, $b$ is the breadth (in
+pixels), and $d$~is the angle (in degrees). Pen angles are measured
+counterclockwise from the horizontal. Thus, an angle of~0 makes the right
+edge of the pen exactly $b$~pixels to the right of the left edge; an angle
+of~90 makes the right pen edge exactly $b$~pixels above the left; an angle
+of~$-90$ makes it exactly $b$~pixels below. An angle of 45 makes the right
+edge $b/{\sqrt2}$ pixels above and $b/{\sqrt2}$ pixels to the right of the
+left edge; an angle of~$-45$ makes it $b/{\sqrt2}$ pixels below and
+$b/{\sqrt2}$ to the right. When the pen angle is between $90^\circ$ and
+$180^\circ$, the ``right'' edge actually lies to the left of the ``left''
+edge. In terms of ^{compass directions} on a conventional map, an angle
+of~$0^\circ$ points due East, while $90^\circ$ points North and $-90^\circ$
+points South. The angle corresponding to Southwest is $-135^\circ$,
+also known as $+225^\circ$.
+
+\exercise What angle corresponds to the direction North-Northwest?
+\answer ${1\over2}\bigl["North",{1\over2}["North","West"]\bigr]=
+{1\over2}\bigl[90,{1\over2}[90,180]\bigr]={1\over2}[90,135]=112.5$.
+
+\begingroup \decreasehsize 9pc
+\exercise \xdef\circlex{4.\number\exno}%
+\rightfig 4i (7pc x 7pc) ^20pt
+What are the pen angles at positions 1, 2, 3, and~4 in
+the circular shape shown here? [{\sl Hint:\/} Each angle is a multiple
+of $30^\circ$. Note that $z_{3r}$ lies to the left of $z_{3l}$.]
+\answer $30^\circ$, $60^\circ$, $210^\circ$, and $240^\circ$. Since it's
+possible to add or subtract $360^\circ$ without changing the meaning,
+the answers $-330^\circ$, $-300^\circ$, $-150^\circ$, and $-120^\circ$
+are also correct.
+
+\exercise What are the coordinates of $z_{1l}$ and $z_{1r}$ after the
+command `$\penpos1(10,-90)$', if $z_1=(25,25)$?
+\answer $z_{1l}=(25,30)$, $z_{1r}=(25,20)$.
+
+\endgroup % end of the diminished \hsize
+\danger The statement `$\penpos k(b,d)$' is simply an abbreviation for
+two equations, `$z_k={1\over2}[z_{kl},z_{kr}]$' and
+`$z_{kr}=z_{kl}+(b,0)$ ^{rotated}~$d\,$'. You might want to use other
+equations to define the relationship between $z_{kl}$, $z_k$, and
+$z_{kr}$, instead of giving a "penpos" command, if an alternative
+formulation turns out to be more convenient.
+
+After `"penpos"' has specified the relations between three points, we still
+don't know exactly where they are; we only know their positions relative
+to each other. Another equation or two is needed in order to fix the
+horizontal and vertical locations of each triple. For example, the three
+"penpos" commands that led to the pen stroke on the previous page were
+accompanied by the equations
+\begindisplay
+$z_1=(0,2"pt")$;&$z_2=(4"pt",0)$;&$x_3=9"pt"$;&$y_{3l}=y_{2r}$;
+\enddisplay
+these made the information complete. There should be one $x$~equation and
+one $y$~equation for each position; or you can use a $z$~equation, which
+defines both $x$ and~$y$ simultaneously.
+
+It's a nuisance to write long-winded @fill@ commands when broad-edge
+pens are being simulated in this way, so \MF\ provides a convenient
+abbreviation: You can write simply
+\begindisplay
+^@penstroke@ $z_{1e}\to z_{2e}\{"right"\}\to\{"right"\}z_{3e}$
+\enddisplay
+instead of the command `\thinspace@fill@ $z_{1l}\to
+z_{2l}\{"right"\}\to\{"right"\}\,z_{3l} \dashto
+z_{3r}\{"left"\}\to\{"left"\}\,z_{2r}\to z_{1r}\dashto\cycle$' that was
+stated earlier. The letter `$e$' ^^"e" stands for the pen's edge. A @penstroke@
+command fills the region `$p.l\dashto \reverse p.r\dashto\cycle$', where
+$p.l$ and~$p.r$ are the left and right paths formed by changing each~`$e$'
+into `$l$' or~`$r$', respectively.
+
+\danger The @penstroke@ abbreviation can be used to draw cyclic paths
+as well as ordinary ones. For example, the circle in exercise \circlex\
+was created by saying simply `@penstroke@ $z_{1e}\to z_{2e}\to z_{3e}\to
+z_{4e}\to\cycle$'. This type of penstroke essentially expands into
+\begindisplay
+@fill@ $z_{1r}\to z_{2r}\to z_{3r}\to z_{4r}\to\cycle$;\cr
+@unfill@ $z_{1l}\to z_{2l}\to z_{3l}\to z_{4l}\to\cycle$;\cr
+\enddisplay
+or the operations `@fill@' and `@unfill@' are reversed, if points
+$(z_{1r},z_{2r}, z_{3r},z_{4r})$ are on the inside and
+$(z_{1l},z_{2l},z_{3l},z_{4l})$ are on the outside.
+
+\dangerexercise The circle of exercise \circlex\ was actually drawn with
+a slightly more complicated @penstroke@ command than just claimed: The
+edges of the curve were forced to be vertical at
+positions 1 and~3, horizontal at 2 and~4. How did the author do this?
+\answer He said `@penstroke@
+$z_{1e}\{"up"\}\to z_{2e}\{"left"\}\to z_{3e}\{"down"\}
+ \to z_{4e}\{"right"\}\to\cycle$'.
+
+\setbox0=\vtop{\kern 21pt
+ \rightline{\vbox{\hbox to 126\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern6pt
+ \figbox{4j}{126\apspix}{252\apspix}\vbox
+ \kern-3pt
+ \hbox to 126\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\qquad}}
+\dp0=0pt
+
+\hangindent-100pt \hangafter2
+\indent\strut\vadjust{\box0}%
+Here's an example of how this new sort of pen can be used to draw a
+sans-serif letter `{\manual\IOI}'. As usual, we assume ^^{I}
+that two variables, $h$ and~$w$, have been set up to give the height and
+width of the character in pixels. We shall also assume that there's a
+"stem" parameter, which specifies the nominal pen breadth. The breadth
+decreases to .9"stem" in the middle of the stroke, and the
+pen angle changes from $15^\circ$ to~$10^\circ$:
+\begindisplay
+$\penpos1("stem",15)$; \ $\penpos2(.9"stem",12)$;\cr
+$\penpos3("stem",10)$; \ $x_1=x_2=x_3=.5w$;\cr
+$y_1=h$; \ $y_2=.55h$; \ $y_3=0$;\cr
+$x_{2l}:={1\over6}[x_{2l},x_2]$;\cr
+@penstroke@ $z_{1e}\to z_{2e}\{down\}\to z_{3e}$.\cr
+\enddisplay
+Setting $x_1=x_2=x_3=.5w$ centers the stroke; setting $y_1=h$ and $y_3=0$
+makes it sit in the type box, protruding just slightly at the top and bottom.
+
+The second-last line of this program is something that we haven't seen
+before: It resets $x_{2l}$ to a value 1/6 of the way towards the center
+of the pen, thereby making the stroke ^{taper} a bit at the left.
+The `$:=$' operation is called an {\sl^{assignment}\/}; we shall
+^^{:=} study the differences between `$:=$' and~`$=$' in Chapter~10.
+
+\danger It is important to note that these simulated pens
+have a serious limitation compared to the way a real calligrapher's pen
+works: The left and right edges of a "penpos"-made pen must never cross,
+hence it is necessary to turn the pen when going around a curve.
+Consider, for example, the following two curves:
+\displayfig 4k (6pc)
+The left-hand circle was drawn with a broad-edge pen of fixed breadth,
+held at a fixed angle; consequently the left edge of the pen was responsible
+for the outer boundary on the left, but the inner boundary on the right.
+\ (This curve was produced by saying `\pickup @pencircle@ xscaled~0.8"pt"
+rotated~25; @draw@ $z_1\to z_2\to\cycle$'.) \ The right-hand shape
+was produced by `$\penpos1(0.8"pt",25)$; $\penpos2(0.8"pt",25)$;
+@penstroke@ $z_{1e}\to z_{2e}\to\cycle$'; important chunks of the shape
+are missing at the crossover points, because they don't lie on either of
+the circles $z_{1l}\to z_{2l}\to\cycle$ or $z_{1r}\to z_{2r}\to\cycle$.
+
+\danger To conclude this chapter we shall improve the ^{hex} character
+{\manual\hexb} of Chapter~2, which is too dark in the middle because it has
+been drawn with a pen of uniform thickness. The main trouble with unvarying
+pens is that they tend to produce black blotches where two strokes meet,
+unless the pens are comparatively thin or unless the strokes are nearly
+perpendicular. We want to thin out the lines at the center just enough
+to cure the darkness problem, without destroying the illusion that the lines
+still seem (at first glance) to have uniform thickness.
+
+\setbox0=\vtop{\kern 69pt
+ \rightline{\vbox{\hbox to 200\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern3pt
+ \figbox{4l}{200\apspix}{100\apspix}\vbox
+ \kern-3pt
+ \hbox to 200\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\quad}}
+\dp0=0pt
+
+\danger \strut\vadjust{\box0}%
+It isn't difficult to produce `\thinspace
+{\manual\hexe\hexe\hexe\hexe\hexe\hexe\hexe\hexe\hexe\hexe}\thinspace'
+instead of `\thinspace
+{\manual\hexb\hexb\hexb\hexb\hexb\hexb\hexb\hexb\hexb\hexb}\thinspace'
+when we work with dynamic pens:
+\begindisplay
+\pickup @pencircle@ scaled $b$;\cr
+$"top"\,z_1=(0,h)$; \ $"top"\,z_2=(.5w,h)$; \ $"top"\,z_3=(w,h)$;\cr
+$"bot"\,z_4=(0,0)$; \ $"bot"\,z_5=(.5w,0)$; \ $"bot"\,z_6=(w,0)$; \
+ @draw@ $z_2\to z_5$;\cr
+$z_{1'}=.25[z_1,z_6]$; \ $z_{6'}=.75[z_1,z_6]$; \
+$z_{3'}=.25[z_3,z_4]$; \ $z_{4'}=.75[z_3,z_4]$;\cr
+$"theta"_1:=\angle(z_6-z_1)+90$;\cr
+$"theta"_3:=\angle(z_4-z_3)+90$;\cr
+$\penpos{1'}(b,"theta"_1)$; \ $\penpos{6'}(b,"theta"_1)$;\cr
+$\penpos{3'}(b,"theta"_3)$; \ $\penpos{4'}(b,"theta"_3)$;\cr
+$\penpos7(.6b,"theta"_1)$; \ $\penpos8(.6b,"theta"_3)$;\cr
+$z_7=z_8=.5[z_1,z_6]$;\cr
+@draw@ $z_1\to z_{1'}$; \ @draw@ $z_{6'}\to z_6$;\cr
+@draw@ $z_3\to z_{3'}$; \ @draw@ $z_{4'}\to z_4$;\cr
+@penstroke@ $z_{1'e}\{z_{6'}-z_{1'}\}\to z_{7e}\to\{z_{6'}-z_{1'}\}z_{6'e}$;\cr
+@penstroke@ $z_{3'e}\{z_{4'}-z_{3'}\}\to z_{8e}\to\{z_{4'}-z_{3'}\}z_{4'e}$.\cr
+\enddisplay
+Here $b$ is the diameter of the pen at the terminal points;
+`^{angle}' computes the direction angle of a given vector.
+Adding $90^\circ$ to a direction angle gives a ^{perpendicular}
+direction (see the definitions of $"theta"_1$ and~$"theta"_3$).
+It isn't necessary to take anything off of the vertical stroke $z_2\to z_5$,
+because the two diagonal strokes fill more than the width of the vertical
+stroke at the point where they intersect.
+
+\setbox0=\vtop{\kern -30pt
+ \rightline{\vbox{\hbox to 200\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern6pt
+% \figbox{4m}{200\apspix}{100\apspix}\vbox
+ \figbox{4m}{200\apspix}{105\apspix}\vbox
+ \kern0pt
+ \hbox to 200\apspix{\hidecoords(0,0)\hfil
+ \hidecoords(w\mkern-2mu,0)}}\quad}}
+\dp0=0pt
+
+\begingroup \decreasehsize 125pt
+\dangerexercise \strut\vadjust{\box0}%
+Modify the hex character so that its ends are cut
+sharply and confined to the bounding box, as shown.
+\answer We use angles ^{perpendicular} to $(w,h)$ and $(w,-h)$ at the
+diagonal endpoints:
+\begindisplay
+$x_{1l}=x_{4l}=0$;\cr
+$x_2=x_5=.5w$;\cr
+$x_{3r}=x_{6r}=w$;\cr
+$y_{1r}=y_2=y_{3l}=h$;\cr
+$y_{4r}=y_5=y_{6l}=0$;\cr
+$z_{1'}=.25[z_1,z_6]$; \ $z_{6'}=.75[z_1,z_6]$;\cr
+$theta_1:=\angle(w,-h)+90$;\cr
+$\penpos1(b,theta_1)$; \ $\penpos6(b,theta_1)$;\cr
+$z_7=.5[z_1,z_6]$; \ $\penpos7(.6b,theta_1)$;\cr
+$\penpos{1'}(b,theta_1)$; \ $\penpos{6'}(b,theta_1)$;\cr
+@penstroke@ $z_{1e}\to z_{1'e}\{z_{6'}-z_{1'}\}\to z_{7e}\to
+\{z_{6'}-z_{1'}\}z_{6'e}\to z_{6e}$;\cr
+$z_{3'}=.25[z_3,z_4]$; \ $z_{4'}=.75[z_3,z_4]$;\cr
+$theta_3:=\angle(-w,-h)+90$;\cr
+$\penpos3(b,theta_3)$; \ $\penpos4(b,theta_3)$;\cr
+$z_8=.5[z_1,z_6]$; \ $\penpos8(.6b,theta_3)$;\cr
+$\penpos{3'}(b,theta_3)$; \ $\penpos{4'}(b,theta_3)$;\cr
+@penstroke@ $z_{3e}\to z_{3'e}\{z_{4'}-z_{3'}\}\to z_{8e}\to
+\{z_{4'}-z_{3'}\}z_{4'e}\to z_{4e}$;\cr
+$\penpos2(b,0)$; \ $\penpos5(b,0)$; \ @penstroke@ $z_{2e}\to z_{5e}$.\cr
+\enddisplay
+
+\endgroup % end of the diminished \hsize
+
+\endchapter
+
+It is very important that the nib be cut ``sharp,''
+and as often as its edge wears blunt it must be resharpened.
+It is impossible to make ``clean cut'' strokes with a blunt pen.
+\author EDWARD ^{JOHNSTON}, {\sl Writing \& Illuminating, %
+ \& Lettering\/} (1906)
+
+\bigskip
+
+I might compare the high-speed computing machine
+to a remarkably large and awkward pencil
+which takes a long time to sharpen and
+cannot be held in the fingers in the usual manner so that it
+gives the illusion of responding to my thoughts,
+but is fitted with a rather delicate engine
+and will write like a mad thing
+provided I am willing to let it dictate pretty much
+the subjects on which it writes.
+\author R. H. ^{BRUCK}, {\sl Computational Aspects of Certain
+ Combinatorial Problems\/} (1956) % AMS Symp Appl Math 6, p31
+
+\eject
+ \beginchapter Chapter 5. Running\\\MF
+
+It's high time now for you to stop reading and to start playing with the
+computer, since \MF\ is an interactive system that is best learned by
+trial and error. \ (In fact, one of the nicest things about computer graphics
+is that errors are often more interesting and more fun than ``successes.'')
+
+You probably will have to ask somebody how to deal with the idiosyncrasies
+of your particular version of the system, even though \MF\ itself works in
+essentially the same way on all machines; different computer terminals and
+different hardcopy devices make it necessary to have somewhat different
+interfaces. In~this chapter we shall assume that you have a computer
+terminal with a reasonably high-resolution graphics display; that you have
+access to a (possibly low-resolution) output device; and that you can
+rather easily get that device to work with newly created fonts.
+
+OK, are you ready to run the program? First you need to log in, of course;
+then start \MF\!, which is usually called ^|mf| for short. Once you've figured
+out how to do it, you'll be welcomed by a message something like
+$$\def\\{{\rm\ }} % take a wee bit off of the \tt spaces
+\vtop{\line{\indent \tt
+This\\is\\METAFONT,\\Version\\2.0\\(preloaded\\base=plain 89.11.8)}
+\leftline{\indent \tt **}}$$
+The `^|**|' is \MF's way of asking you for an input file name.
+% Incidentally, 89.11.8 was Hermann's 71st birthday.
+
+Now type `|\relax|'---that's ^{backslash}, |r|, |e|, |l|, |a|, |x|---and
+hit ^\<return> (or~whatever stands for ``end-of-line'' on your keyboard).
+\MF\ is all geared up for action, ready to make a big font; but you're
+saying that it's all right to take things easy, since this is going to
+be a real simple run. The backslash means that \MF\ should not read a file,
+it should get instructions from the keyboard; the `^|relax|' means
+``do nothing.''
+
+The machine will respond by typing a single asterisk: `^|*|'. This means
+it's ready to accept instructions (not the name of a file). Type the
+following, just for fun:
+\begintt
+drawdot (35,70); showit;
+\endtt
+and \<return>---don't forget to type the semicolons along with the other
+stuff. A more-or-less circular dot should now appear on your screen! And
+you should also be prompted with another asterisk.
+Type
+\begintt
+drawdot (65,70); showit;
+\endtt
+and \<return>, to get another dot. \ (Henceforth we won't keep mentioning
+the necessity of \<return>ing after each line of keyboard input.) \ Finally,
+type
+\begintt
+draw (20,40)..(50,25)..(80,40); showit; shipit; end.
+\endtt
+This draws a curve through three given points, displays the result,
+^^|showit| ^^|shipit| ^^|end|
+ships it to an output file, and stops. \MF\ should respond with `|[0]|',
+meaning that it has shipped out a character whose number is zero, in the
+``font'' just made; and it should also tell you that it has created
+an output file called `|mfput.2602gf|'. \ (The name ^|mfput| is used when
+you haven't specified any better name in response to the ^|**| at the
+beginning. The suffix |2602|^|gf| stands for ``^{generic font} at
+2602 pixels per inch.'' The data in |mfput.2602gf| can be converted into
+fonts suitable for a wide assortment of typographical output devices;
+since it doesn't match the font file conventions of any name-brand
+manufacturer, we call it generic.)
+
+This particular file won't make a very interesting font,
+because it contains only one character, and because it probably doesn't
+have the correct resolution for your output device. However, it does
+have the right resolution for hardcopy proofs of characters; your next
+step should therefore be to convert the data of |mfput.2602gf| into a
+picture, suitable for framing. There should be a program called
+^|GFtoDVI| on your computer. Apply it to |mfput.2602gf|, thereby
+obtaining a file called |mfput.dvi| ^^|dvi| that can be printed.
+Your friendly local computer hackers will tell you how to run
+|GFtoDVI| and how to print |mfput.dvi|; then you'll have a marvelous
+souvenir of your very first encounter with \MF\!. \looseness=-1
+
+\smallskip
+Once you have made a complete test run as just described, you will
+know how to get through the whole cycle, so you'll be ready to tackle
+a more complex project. Our next experiment will therefore be
+to work from a file, instead of typing the input online.
+
+Use your favorite text editor to create a file called |io.mf| that
+contains the following 23 lines of text (no more, no less):
+$$\halign{\hbox to\parindent{\hfil\sevenrm#\ \ }&#\hfil\cr
+1&|mode_setup;|\cr\noalign{^^@mode\_setup@}
+2&| em#:=10pt#; cap#:=7pt#;|\cr
+3&| thin#:=1/3pt#; thick#:=5/6pt#;|\cr
+4&| o#:=1/5pt#;|\cr
+5&|define_pixels(em,cap);|\cr
+6&|define_blacker_pixels(thin,thick);|\cr
+7&|define_corrected_pixels(o);|\cr
+8&| curve_sidebar=round 1/18em;|\cr
+9&|beginchar("O",0.8em#,cap#,0); "The letter O";|\cr
+10&| penpos1(thick,10); penpos2(.1[thin,thick],90-10);|\cr
+11&| penpos3(thick,180+10); penpos4(thin,270-10);|\cr
+12&| x1l=w-x3l=curve_sidebar; x2=x4=.5w;|\cr
+13&| y1=.49h; y2l=-o; y3=.51h; y4l=h+o;|\cr
+14&| penstroke z1e{down}..z2e{right}|\cr
+15&| ..z3e{up}..z4e{left}..cycle;|\cr
+16&| penlabels(1,2,3,4); endchar;|\cr
+17&|def test_I(expr code,trial_stem,trial_width) =|\cr
+18&| stem#:=trial_stem*pt#; define_blacker_pixels(stem);|\cr
+19&| beginchar(code,trial_width*em#,cap#,0); "The letter I";|\cr
+20&| penpos1(stem,15); penpos2(.9stem,12); penpos3(stem,10);|\cr
+21&| x1=x2=x3=.5w; y1=h; y2=.55h; y3=0; x2l:=1/6[x2l,x2];|\cr
+22&| penstroke z1e..z2e{down}..z3e;|\cr
+23&| penlabels(1,2,3); endchar; enddef;|\cr}$$
+(But don't type the numbers at the left of these lines; they're
+only for reference.)
+
+This example file is dedicated to ^{Io}, the Greek goddess of input
+and output. It's a trifle long, but you'll be able to get worthwhile
+experience by typing it; so go ahead and type it now. For your own
+good. And think about what you're typing, as you go; the example
+introduces several important features of \MF\ that you can learn
+as you're creating the file.
+
+Here's a brief explanation of what you've just typed: Line~1 contains a
+command that usually appears near the beginning of every \MF\ file;
+it tells the computer to get ready to work in whatever ``mode'' is
+currently desired. \ (A file like |io.mf| can be used to generate
+proofsheets as well as to make fonts for a variety of devices at a
+variety of magnifications, and `@mode\_setup@' is what adapts \MF\
+to the task at hand.) \ Lines 2--8 define parameters that will be used
+to draw the letters in the font. Lines 9--16 give a complete program
+for the letter `O'; and lines 17--23 give a program that will draw
+the letter~`I' in a number of related ways.
+
+It all looks pretty frightening at first glance, but a closer look
+shows that Io is not so mysterious once we penetrate her disguise.
+Let's spend a few minutes studying the file in more detail.
+
+Lines 2--4 define dimensions that are independent of the mode; the `|#|'
+^^{sharpsign} signs are meant to imply ``sharp'' or ``true'' ^{units of
+measure}, which remain the same whether we are making a font at high or
+low resolution. For example, one `|pt#|' is a true printer's point, one
+72.27th of an inch. This is quite different from the `^"pt"' we have
+discussed in previous chapters, because `"pt"' is the number of pixels
+that happen to correspond to a printer's point when the current resolution
+is taken into account. The value of `|pt#|' never changes, but
+@mode\_setup@ establishes the appropriate value of `"pt"'.
+
+The ^{assignments} `|em#:=10pt#|' and `|cap#:=7pt#|' in line~2 mean that
+the Io font has two parameters, called "em" and "cap", whose mode-independent
+values are 10 and~7 points, respectively. The statement ^^@define\_pixels@
+`|define_pixels(em,cap)|' on line~5 converts these values into pixel
+units. For example, if we are working at the comparatively low resolution
+of 3~pixels per~pt, the values of "em" and "cap" after the computer has
+performed the instructions on line~5 will be $"em"=30$ and $"cap"=21$.
+\ (We will see later that the widths of characters in this font are
+expressed in terms of ems, and that "cap" is the height of the capital
+letters. A change to line~2 will therefore affect the widths and/or heights
+of all the letters.)
+
+Similarly, the Io font has parameters called "thin" and "thick", defined
+on line~3 and converted to pixel units in line~6. These are used to control
+the breadth of a simulated pen when it draws the letter~O. Experience has
+shown that \MF\ produces better results on certain output devices if
+pixel-oriented pens are made slightly broader than the true dimensions would
+imply, because black pixels sometimes tend to ``burn off'' in the process
+of printing. The command on line~6, `|define_blacker_pixels|',
+^^@define\_blacker\_pixels@ adds a correction based on the device for which
+the font is being prepared. For example, if the resolution is 3~pixels
+per point, the value of "thin" when converted from true units to pixels
+by @define\_pixels@ would be~1, but @define\_blacker\_pixels@ might set
+"thin" to a value closer to~2.
+
+The `|o|' parameter ^^"o" on line 4 represents the amount by which curves will
+^{overshoot} their boundaries. This is converted to pixels in yet another
+way on line~7, so as to avoid yet another problem that arises in low-resolution
+printing. The author apologizes for letting such real-world considerations
+intrude into a textbook example; let's not get bogged down in fussy details
+now, since these refinements will be explained in Chapter~11 after we have
+mastered the basics.
+
+For now, the important point is simply that a typeface
+design usually involves parameters that represent physical lengths. The
+true, ``sharped'' forms of these parameters need to be converted to
+``unsharped'' pixel-oriented quantities, and best results are obtained when
+such conversions are done carefully. After \MF\ has obeyed line~7 of the
+example, the pixel-oriented parameters "em", "cap", "thin", "thick",
+and~"o" are ready to be used as we draw letters of the font.
+
+Line 8 defines a quantity called "curve\_sidebar" ^^{sidebar} that will
+measure the distance of the left and right edges of the `O' from the
+bounding box. It is computed by ^{rounding} ${1\over18}"em"$ to the nearest
+integer number of pixels. For example, if $"em"=30$ then ${30\over18}=
+{5\over3}$ yields the rounded value $"curve\_sidebar"=2$; there will be
+two all-white columns of pixels at the left and right of the `O',
+when we work at this particular resolution.
+
+Before we go any further, we ought to discuss the strange collection
+of words and pseudo-words in the file |io.mf|. Which of the terms
+`|mode_setup|', `|em|', `|curve_sidebar|' and so forth are part of
+the \MF\ language, and which of them are made up specifically for
+the Io example? Well, it turns out that almost {\sl nothing\/} in this
+example is written in the pure \MF\ language that the computer understands!
+\MF\ is really a low-level language that has been designed to allow easy
+adaptation to many different styles of programming, and |io.mf|
+illustrates just one of countless ways to use it. Most of the terms
+in |io.mf| are conventions of ``^{plain} \MF\!,'' which is a collection
+of subroutines found in Appendix~B\null. \MF's primitive capabilities are
+not meant to be used directly, because that would force a particular style
+on all users. A ``base file'' is generally loaded into the computer
+at the beginning of a run, so that a standard set of conventions is
+readily available. \MF's welcoming message, quoted at the
+beginning of this chapter, says `|preloaded| |base=plain|'; it
+means that the primitive \MF\ language has been extended to include the
+features of the plain base file. This book is not only about \MF; it also
+explains how to use the conventions of \MF's plain base. Similarly, {\sl
+The \TeX book\/} describes a standard extension of \TeX\ called ``plain
+\TeX\ format''; ^^{TeX} the ``plain'' extensions of \TeX\ and \MF\ are
+completely analogous to each other.
+
+The notions of @mode\_setup@, @define\_pixels@, @beginchar@, "penpos",
+and many other things found in |io.mf| are aspects
+of plain \MF\ but they are not hardwired into \MF\ itself. Appendix~B
+defines all of these things, as well as the relations between ``sharped''
+and ``unsharped'' variables. Even the fact that $z_1$ stands for
+$(x_1,y_1)$ is defined in Appendix~B\null; \MF\ does not have this built~in.
+You are free to define even fancier bases as you gain more experience,
+but the plain base is a suitable starting point for a novice.
+
+\danger If you have important applications that make use of a different
+base file, it's possible to create a version of \MF\ that has any desired
+base preloaded. Such a program is generally called by a special name,
+since the nickname `^|mf|' is reserved for the version that includes the
+standard plain base assumed in this book. For example, the author has made
+a special version called `^|cmmf|' just for the ^{Computer Modern} typefaces
+he has been developing, so that the Computer Modern base file does not
+have to be loaded each time he makes a new experiment.
+
+\danger There's a simple way to change the base file from the one that has
+been preloaded: If the first character you type in response to `^|**|' is
+an ^{ampersand} (\thinspace`|&|'\thinspace), \MF\ will replace its memory
+with a specified base file before proceeding. If, for example, there is a
+base file called `|cm.base|' but not a special program called `|cmmf|',
+you can substitute the Computer Modern base for the plain base in |mf| by
+typing `|&cm|' at the very beginning of a run. If you are working with a
+program that doesn't have the plain base preloaded, the first experiment
+in this chapter won't work as described, but you can do it by starting
+with `|&plain \relax|' instead of just `|\relax|'. These conventions are
+exactly the same as those of \TeX.
+
+Our Ionian example uses the following words that are not part of plain
+\MF: "em", "cap", "thin", "thick", "o", "curve\_sidebar", "test\_I", "code",
+"trial\_stem", "trial\_width", and "stem". If you change these to some other
+words or symbols---for example, if you replace `|thin|' and `|thick|' by
+`|t|' and `|T|' respectively, in lines 3, 6, 10, and~11---the results will
+be unchanged, unless your substitutions just happen to clash with something
+that plain \MF\ has already pre\"empted. In general, the best policy is to
+choose descriptive terms for the quantities in your programs, since they
+are not likely to conflict with reserved pseudo-words like "penpos" and
+@endchar@.
+
+We have already noted that lines 9--16 of the file represent a program
+for the letter `O'. The main part of this program, in lines 10--15,
+uses the ideas of Chapter~4, but we haven't seen the stuff in lines 9
+and~16 before. Plain \MF\ makes it convenient to define letters by starting
+each one with
+\begindisplay
+$@beginchar@\kern1pt($\<code>, \<width>, \<height>, \<depth>);^^@beginchar@
+\enddisplay
+here \<code> is either a quoted single character like |"O"| or a number that
+represents the character's position in the final font. The other three
+quantities \<width>, \<height>, and \<depth> say how big the ^{bounding box}
+is, so that typesetting systems like \TeX\ will be able to use the character.
+These three dimensions must be given in device-independent units, i.e.,
+in ``^{sharped}'' form.
+
+\exercise What are the height and width of the bounding box described
+in the @beginchar@ command on line~9 of |io.mf|, given the parameter
+values defined on line~2? Give your answer in terms of printer's points.
+\answer The width is |0.8em#|, and an |em#| is 10 true points, so the
+box will be exactly $8\pt$ wide in device-independent units. The
+height will be $7\pt$. \ (And the depth below the baseline will be $0\pt$.)
+
+Each @beginchar@ operation assigns values to special variables called
+$w$, $h$, and~$d$, ^^"w" ^^"h" ^^"d" which represent the respective
+width, height, and depth of the current character's bounding box,
+^{rounded} to the nearest integer number of pixels. Our example file
+uses $w$ and~$h$ to help establish the locations of several pen positions
+(see lines 12, 13, and~21 of |io.mf|).
+
+\exercise Continuing the previous exercise, what will be the values of
+$w$ and~$h$ if there are exactly 3.6 pixels per point?
+\answer $8\times3.6=28.8$ rounds to the value $w=29$; similarly, $h=25$.
+\ (And $d=0$.)
+
+There's a quoted phrase |"The| |letter| |O"| at the end of line~9; this is
+simply a title that will be used in printouts.
+
+The `|endchar|' ^^@endchar@ on line 16 finishes the character that was
+begun on line~9, by writing it to an output file and possibly displaying
+it on your screen. We will want
+to see the positions of the control points $z_1$, $z_2$,
+$z_3$, and~$z_4$ that are used in its design, together with the auxiliary
+points $(z_{1l},z_{2l},z_{3l},z_{4l})$ and $(z_{1r},z_{2r},z_{3r},z_{4r})$
+that come with the "penpos" conventions; the statement `|penlabels(1,2,3,4)|'
+^^"penlabels" takes care of labeling these points on the proofsheets.
+
+So much for the letter O. Lines 17--23 are analogous to what we've seen
+before, except that there's a new wrinkle: They contain a little program
+^^@def@ enclosed by `|def...enddef|', which means that a
+{\sl^{subroutine}\/} is being defined. In other words, those lines set up
+a whole bunch of \MF\ commands that we will want to execute several times
+with minor variations. The subroutine is called "test\_I" and it has three
+parameters called "code", "trial\_stem", and "trial\_width" (see line~17).
+The idea is that we'll want to draw several different versions of an `I',
+having different stem widths and character widths; but we want to type the
+program only once. Line~18 defines "stem"\0 and "stem", given a value of
+"trial\_stem"; and lines 19--23 complete the program for the letter~I
+(copying it from Chapter~4).
+
+\smallskip
+Oops---we've been talking much too long about |io.mf|. It's time to stop
+rambling and to begin Experiment~2 in earnest, because it will be much
+more fun to see what the computer actually does with that file.
+
+Are you brave enough to try Experiment 2? Sure.
+Get \MF\ going again, but this time when the machine says `^|**|' you should
+say `|io|', since that's the name of the file you have prepared so
+laboriously. \ (The file could also be specified by giving its full name
+`|io.mf|', but \MF\ automatically adds `|.mf|' ^^|mf| ^^{file names} when
+no suffix has been given explicitly.)
+
+If all goes well, the computer should now flash its lights a bit
+and---presto---a big `{\manual\IOO}' should be drawn on your screen.
+But if your luck is as good as the author's, something will probably go wrong
+the first time, most likely because of a typographic error in the file.
+A \MF\ program contains lots of data with comparatively little redundancy,
+so a single error can make a drastic change in the meaning. Check that
+you've typed everything perfectly: Be sure to notice the difference between
+the letter~`|l|' and the numeral~`|1|' (especially in line~12, where it
+says `|x1l|', not `|x11|' or~`|xll|'); be sure to distinguish between
+the letter~`|O|' and the numeral~`|0|' (especially in line~9); be sure to
+type the ``underline'' characters in words like `|mode_setup|'. We'll see
+later that \MF\ can recover gracefully from most errors, but your job for
+now is to make sure that you've got |io.mf| correct.
+
+Once you have a working file, the computer will draw you an `{\manual\IOO}'
+and it will also say something like this:
+\begintt
+(io.mf
+The letter O [79])
+*
+\endtt
+What does this mean? Well, `|(io.mf|' means that it has started to read your
+file, and `|The| |letter|~|O|' was printed when the title was found in
+line~9. Then when \MF\ got to the |endchar| on line~16, it said
+`|[79]|' to tell you that it had just output character number~79.
+\ (This is the ^{ASCII} code for the letter~|O|; Appendix~C lists all
+of these codes, if you need to know them.) The `|)|' after `|[79]|'
+means that \MF\ subsequently finished reading the file, and the `^|*|'
+means that it wants another instruction.
+
+Hmmm. The file contains programs for both I and O; why did we get only
+an~O? Answer: Because lines 17--23 simply define the subroutine "test\_I";
+they don't actually {\sl do\/} anything with that subroutine. We need to
+activate "test\_I" if we're going to see what it does. So let's type
+\begintt
+test_I("I",5/6,1/3);
+\endtt
+this invokes the subroutine, with $"code"=\null$|"I"|,
+$"trial\_stem"={5\over6}$, and $"trial\_width"={1\over3}$. The computer will
+now draw an~I corresponding to these values,\footnote*{Unless, of course,
+there was a typing error in lines 17--23, where "test\_I" is defined.} and
+it will prompt us for another command.
+
+It's time to type `^|end|' now, after which \MF\ should tell us that it has
+completed this run and made an output file called `|io.2602gf|'. Running this
+file through ^|GFtoDVI| as in Experiment~1 will produce two proofsheets,
+showing the `{\manual\IOO}' and the `{\manual\IOI}' we have created.
+The output won't be shown here, but you can see the results by doing
+the experiment personally.
+
+Look at those proofsheets now, because they provide instructive examples
+of the simulated broad-edge pen constructions introduced in Chapter~4.
+Compare the `{\manual\IOO}' with the program that drew it: Notice that
+the $\penpos2$ in line~10 makes the curve slightly thicker at the ^^"penpos"
+bottom than at the top; that the equation `$x_{1l}=w-x_{3l}="curve\_sidebar"$'
+in line~12 makes the right edge of the curve as far from the right of the
+bounding box as the left edge is from the left; that line~13 places point~1
+slightly lower than point~3. The proofsheet for `{\manual\IOI}' should look
+very much like the corresponding illustration near the end of Chapter~4,
+but it will be somewhat larger.
+
+\danger Your proof copy of the `{\manual\IOO}' should show twelve dots
+for key points; but only ten of them will be labeled, because there isn't
+room enough to put labels on points 2 and~4. The missing ^{labels} usually
+^^{overflow labels} appear in the upper right corner, where it might say, e.g.,
+`|4|~|=|~|4l|~|+|~|(-1,-5.9)|'; this
+means that point $z_4$ is one pixel to the left and 5.9 pixels down
+from point~$z_{4l}$, which is labeled. \ (Some implementations omit this
+information, because there isn't always room for it.)
+
+The proofsheets obtained in Experiment~2 show the key points and the
+bounding boxes, but this extra information can interfere with our
+perception of the character shape itself. There's a simple way to
+get proofs that allow a viewer to criticize the results from an aesthetic
+rather than a logical standpoint; the creation of such proofs will be the
+goal of our next experiment.
+
+Here's how to do Experiment~3: Start \MF\ as usual, then type
+\begintt
+\mode=smoke; input io
+\endtt
+in response to the `^|**|'. This will input file |io.mf| again,
+after establishing ``smoke'' mode. \ (As in Experiment~1, the command line
+begins with `|\|' so that the computer knows you aren't starting with
+the name of a file.) \ Then complete the run exactly ^^{backslash}
+as in Experiment~2, by typing `|test_I("I",5/6,1/3);| |end|';
+and apply |GFtoDVI| to the resulting file |io.2602gf|.
+
+This time the proofsheets will contain the same characters as before, but
+they will be darker and without labeled points. The bounding boxes will
+be indicated only by small markings at the corners; you can put these
+boxes next to each other and tack the results up on the wall, then stand
+back to see how the characters will look when set by a high-resolution
+typesetter. \ (This way of working is called ^"smoke" mode because it's
+analogous to the ``smoke proofs'' that punch-cutters traditionally used to
+test their handiwork. They held the newly cut type over a candle flame so
+that it would be covered with carbon; then they pressed it on paper to
+make a clean impression of the character, in order to see whether changes
+were needed.)
+
+\danger Incidentally, many systems allow you to invoke \MF\ by typing
+a one-line command like `|mf|~|io|' in the case of Experiment~2; you
+don't have to wait for the `|**|' before giving a file name. Similarly,
+the one-liners `|mf|~|\relax|' and `|mf|~|\mode=smoke;| |input|~|io|' can be
+used on many systems at the beginning of Experiments 1 and~3. You might want
+to try this, to see if it works on your computer; or you might ask
+somebody if there's a similar shortcut.
+
+Experiments 1, 2, and 3 have demonstrated how to make proof drawings of
+test characters, but they don't actually produce new fonts that can be
+used in typesetting. For this, we move onward to Experiment~4, in which
+we put ourselves in the position of a person who is just starting to
+design a new typeface. Let's imagine that we're happy with the~O of
+|io.mf|, and that we want a ``sans serif'' I in the general style produced
+by "test\_I", but we aren't sure about how thick the stem of the~I
+should be in order to make it blend properly with the~O. Moreover, we aren't
+sure how much white space to leave at the sides of the~I. So~we want to do
+some typesetting experiments, using a sequence of different I's.
+
+The ideal way to do this would be to produce a high-resolution test font and to
+view the output at its true size. But this may be too expensive, because fine
+printing equipment is usually available only for large production runs.
+The next-best alternative is to use a low-resolution printer but to magnify
+the output, so that the resolution is effectively increased. We shall adopt
+the latter strategy, because it gives us a chance to learn about
+^{magnification} as well as fontmaking.
+
+After starting \MF\ again, you can begin Experiment 4 by typing
+\begintt
+\mode=localfont; mag=4; input io
+\endtt
+in response to the `|**|'. The ^{plain base} at your installation is supposed
+to recognize ^|localfont| as the name of the mode that makes fonts for your
+``standard'' output device. The equation `|mag=4|' means that this run will
+produce a font that is magnified fourfold; i.e., the results will be
+4~times bigger than usual.
+
+The computer will read |io.mf| as before, but this time it won't display an~`O';
+characters are normally not displayed in fontmaking modes, because we usually
+want the computer to run as fast as possible when it's generating a font
+that has already been designed. All you'll see is `|(io.mf| |[79])|',
+followed by~`^|*|'. Now the fun starts: You should type
+\begintt
+code=100;
+for s=7 upto 10:
+ for w=5 upto 8:
+ test_I(incr code,s/10,w/20);
+endfor endfor end.
+\endtt
+(Here `^|upto|' must be typed as a single word.) \ We'll learn about
+repeating things with `^|for||...|^|endfor|' in Chapter~19. This little
+program produces 16 versions of the letter~I, with stem widths of
+$7\over10$, $8\over10$, $9\over10$, and~${10\over10}\pt$, and with
+character widths of $5\over20$, $6\over20$, $7\over20$, and~${8\over20}\,
+\rm em$. The sixteen trial characters will appear in positions 101 through~116
+of the font; it turns out that these are the ^{ASCII} codes for lowercase
+letters |e| through~|t| inclusive. \ (Other codes would have been used if
+`|code|' had been started at a value different from~100. The construction
+`|incr|~|code|' increases the value of |code| by~1 and produces the new value;
+thus, each use of |test_I| has a different code number.) ^^"incr"
+
+This run of \MF\ will not only produce a generic font |io.nnngf|, it will also
+create a file called |io.tfm|, the ``^{font metric file}'' that tells
+^^{output of METAFONT} ^^|tfm|
+typesetting systems like \TeX\ how to make use of the new font. The remaining
+part of Experiment~4 will be to put \TeX\ to work: We shall make some test
+patterns from the new font, in order to determine which `I' is best.
+
+You may need to ask a local system wizard for help at this point, because
+it may be necessary to move the file |io.tfm| to some special place where
+\TeX\ and the other typesetting software can find it. Furthermore, you'll
+need to run a program that converts |io.nnngf| to the font format used by your
+local output device. But with luck, these will both be fairly simple
+operations, and a new font called `|io|' will effectively be installed
+on your system. This font will contain seventeen letters, namely an |O| and
+sixteen |I|'s, where the |I|'s happen to be in the positions normally occupied
+by |e|, |f|, \dots,~|t|. Furthermore, the font will be magnified fourfold.
+
+\danger The magnification of the font will be reflected in its file name.
+For example, if "localfont" mode is for a device with 200 pixels per inch,
+the |io| font at 4$\times$ magnification will be called `|io.800gf|'.
+
+You can use \TeX\ to typeset from this font like any other, but for the
+purposes of Experiment~4 it's best to use a special \TeX\ package that has
+been specifically designed for font testing. All you need to do is to
+run \TeX---which is just like running \MF\!, except that you call it `|tex|'
+instead of `|mf|'; and you simply type `^|testfont|' in reply to \TeX's
+`|**|'. \ (The |testfont| routine should be available on your system; if
+not, you or somebody else can type it in, by copying the relevant material
+from Appendix~H\null.) \ You will then be asked for the name of the font
+you wish to test. Type
+\begintt
+io scaled 4000
+\endtt
+(which means the |io| font magnified by 4, in \TeX's jargon),
+since this is what \MF\ just created. The machine will now ask you for
+a test command, and you should reply
+\begintt
+\mixture
+\endtt
+to get the ``^{mixture}'' test. \ (Don't forget the ^{backslash}.) \
+You'll be asked for a ^{background letter}, a starting letter, and an
+ending letter; type `|O|', `|e|', and `|t|', respectively. This will
+produce sixteen lines of typeset output, in which the first line contains
+a mixture of |O| with~|e|, the second contains a mixture of |O|~with~|f|,
+and so on. To complete Experiment~4, type `|\end|' to \TeX, and print the
+file |testfont.dvi| ^^|dvi| that \TeX\ gives you.
+
+\setbox0=\hbox{\kern.5pt I\kern.5pt} \def\\{\copy0}
+If all goes well, you'll have sixteen lines that say `O\\OO\\\\OOO\\\\\\O\\',
+but with a different I on each line. In order to choose the line that looks
+best, without being influenced by neighboring lines, it's convenient to take
+two sheets of blank paper and use them to mask out all of the lines
+except the one you're studying. Caution: These letters are four times
+larger than the size at which the final font is meant to be viewed,
+so you should look at the samples from afar. Xerographic reductions may
+introduce distortions that will give misleading results. Sometimes when
+you stare at things like this too closely, they all look wrong, or
+they all look right; first impressions are usually more significant
+than the results of logical reflection. At any rate, you should be able
+to come up with an informed judgment about what values to use for the
+stem width and the character width of a decent `I'; these can then be
+incorporated into the program, the `|def|' and `|enddef|' parts of
+|io.mf| can be removed, and you can go on to design other characters
+that go with your I and~O. Furthermore you can always go back and make
+editorial changes after you see your letters in more contexts.
+
+\ddangerexercise The goddess Io was known in Egypt as ^{Isis}.
+Design an `{\manual\IOS}' for her.
+\answer Here's one way, using a variable "slab" to control the
+\rightfig A5a ({200\apspix} x 252\apspix) ^-71pt
+^^{S} pen breadth at the ends of the stroke:
+\begintt
+slab#:=.8pt#; define_blacker_pixels(slab);
+beginchar("S",5/9em#,cap#,0); "The letter S";
+penpos1(slab,70); penpos2(.5slab,80);
+penpos3(.5[slab,thick],200); penpos5(.5[slab,thick],210);
+penpos6(.7slab,80);
+penpos7(.25[slab,thick],72);
+x1=x5; y1r=.94h+o;
+x2=x4=x6=.5w; y2r=h+o; y4=.54h; y6l=-o;
+x3r=.04em; y3=.5[y4,y2];
+x5l=w-.03em; y5=.5[y4,y6];
+.5[x7l,x7]=.04em; y7l=.12h-o;
+path trial; trial=z3{down}..z4..{down}z5;
+pair dz; dz=direction 1 of trial;
+penpos4(thick,angle dz-90);
+penstroke z1e..z2e{left}..z3e{down}
+ ..z4e{dz}..z5e{down}..z6e{left}..z7e;
+penlabels(1,2,3,4,5,6,7); endchar;
+\endtt
+Notice that the pen angle at point 4 has been found by letting \MF\
+^^{direction} construct a ^{trial path} through the center points,
+then using the ^{perpendicular} direction. The letters work reasonably
+well at their true size: `{\manual\IOS\IOO} {\manual\IOI\IOO}
+{\manual\IOI\IOS} {\manual\IOI\IOS\IOI\IOS}.'
+
+Well, this isn't a book about type design; the example of |io.mf| is
+simply intended to illustrate how a type designer might want to operate,
+and to provide a run-through of the complete process from design of
+type to its use in a document. We must go back now to the world of
+computerese, and study a few more practical details about the use of \MF\!.
+
+This has been a long chapter, but take heart: There's only one more
+experiment to do, and then you will know enough about \MF\ to run it
+fearlessly by yourself forever after. The only thing you are still missing
+is some information about how to cope with error messages. Sometimes
+\MF\ stops and asks you what to do next. Indeed, this may have already
+happened, and you may have panicked.
+
+Error messages can be terrifying when you aren't prepared for them;
+but they can be fun when you have the right attitude. Just remember that
+you really haven't hurt the computer's feelings, and that nobody will
+hold the errors against you. Then you'll find that running \MF\ might
+actually be a creative experience instead of something to dread.
+
+The first step in Experiment 5 is to plant some intentional mistakes
+in the input file. Make a copy of |io.mf| and call it |badio.mf|; then
+change line~1 of |badio.mf| to
+\begintt
+mode setup; % an intentional error!
+\endtt
+(thereby omitting the underline character in |mode_setup|).
+Also change the first semicolon (\thinspace`|;|'\thinspace) on line~2
+to a colon (\thinspace`|:|'\thinspace);
+change `|thick,10|' to `|thick,l0|' on line~10 (i.e., replace the numeral~`|1|'
+by the letter~`|l|'\thinspace); and change `|thin|' to `|thinn|' on line~11.
+These four changes introduce typical typographic errors, and it will be
+instructive to see if they lead to any disastrous consequences.
+
+Now start \MF\ up again; but instead of cooperating with the computer, type
+`|mumble|' in reply to the~`|**|'. \ (As long as you're going to make
+intentional mistakes, you might as well make some dillies.) \
+\MF\ will say that it can't find any file called |mumble.mf|,
+and it will ask you for another name. Just hit \<return> this time;
+you'll see that you had better give the name of a real file.
+So type `|badio|' and wait for \MF\ to find one of the {\sl faux pas\/}
+in that messed-up travesty.
+
+Ah yes, the machine will soon stop, after typing something like this:
+\begintt
+>> mode.setup
+! Isolated expression.
+<to be read again>
+ ;
+l.1 mode setup;
+ % an intentional error!
+?
+\endtt
+\MF\ begins its error messages with `|!|', and it sometimes precedes them
+with one or two related mathematical expressions that are displayed on
+lines starting with `^|>>|'. Each error message is also followed by lines
+of context that show what the computer was reading at the time of the
+error. Such context lines occur in pairs; the top line of the pair (e.g.,
+`|mode| |setup;|'\thinspace) shows what \MF\ has looked at so far, and
+where it came from (`|l.1|', i.e., line number~1); the bottom line (here
+`|%|~|an| |intentional| |error!|'\thinspace) shows what \MF\ has yet to
+read. In this case there are two pairs of context lines; the top pair
+refers to a semicolon that \MF\ has read once but will be reading again,
+because it didn't belong with the preceding material.
+
+You don't have to take out pencil and paper in order to write down the
+error messages that you get before they disappear from view, since \MF\
+always writes a ``^{transcript}'' or ``^{log file}'' that records what
+happened during each session. For example, you should now have a file
+called |io.log| containing the transcript of Experiment~4, as well as a file
+|mfput.log| that contains the transcript of Experiment~1. \ (The old
+transcript of Experiment~2 was probably overwritten when you did
+Experiment~3, and again when you did Experiment~4, because all three
+transcripts were called |io.log|.) \ At the end of Experiment~5 you'll
+have a file |badio.log| that will serve as a helpful reminder of
+what errors need to be fixed up.
+
+The `^|?|' that appears after the context display means that \MF\ wants
+advice about what to do next. If you've never seen an error message before,
+or if you've forgotten what sort of response is expected, you can type
+`|?|' now (go ahead and try it!); \MF\ will respond as follows:
+\begintt
+Type <return> to proceed, S to scroll future error messages,
+R to run without stopping, Q to run quietly,
+I to insert something, E to edit your file,
+1 or ... or 9 to ignore the next 1 to 9 tokens of input,
+H for help, X to quit.
+\endtt
+This is your menu of options. You may choose to continue in various ways:
+
+\smallskip\item{1.}
+Simply type \<return>. \MF\ will resume its processing, after
+attempting to recover from the error as best it can.
+
+\smallbreak\item{2.} Type `|S|'. \MF\ will proceed without
+pausing for instructions if further errors arise. Subsequent error messages
+will flash by on your terminal, possibly faster than you can read them, and
+they will appear in your log file where you can scrutinize them at your
+leisure. Thus, `|S|'~is sort of like typing \<return> to every message.
+
+\smallbreak\item{3.} Type `|R|'. This is like `|S|' but even stronger,
+since it tells \MF\ not to stop for any reason, not even if a file name
+can't be found.
+
+\smallbreak\item{4.} Type `|Q|'. This is like `|R|' but even more so,
+since it tells \MF\ not only to proceed without stopping but also to
+suppress all further output to your terminal. It is a fast, but somewhat
+reckless, way to proceed (intended for running \MF\ with no operator in
+attendance).
+
+\smallbreak\item{5.} Type `|I|', followed by some text that you want to
+insert. \MF\ will read this text before encountering what it
+would ordinarily see ^^{inserting text online}
+^^{online interaction, see interaction} ^^{interacting with MF}
+next.
+
+\smallbreak\item{6.} Type a small number (less than 100). \MF\ will
+delete this many ^{tokens} from whatever it is
+about to read next, and it will pause again to give you another chance to
+look things over. ^^{deleting tokens}
+\ (A~``token'' is a name, number, or symbol that \MF\ reads as a unit;
+e.g., `|mode|' and `|setup|' and `|;|' are the first three tokens
+of |badio.mf|, but `|mode_setup|' is the first token of |io.mf|.
+Chapter~6 explains this concept precisely.)
+
+\smallbreak\item{7.} Type `|H|'. This is what you should do now and whenever
+you are faced with an error message that you haven't seen for a~while. \MF\
+has two messages built in for each perceived error: a formal one and an
+informal one. The formal message is printed first (e.g., `|!|~|Isolated|
+|expression.|'\thinspace); the informal one is printed if you request
+more help by typing `|H|', and it also appears in your log file if you
+are scrolling error messages. The informal message tries to complement the
+formal one by explaining what \MF\ thinks the trouble is, and often
+by suggesting a strategy for recouping your losses.^^{help messages}
+
+\smallbreak\item{8.} Type `|X|'. This stands for ``exit.'' It causes \MF\
+to stop working on your job, after putting the finishing touches on your
+|log| file and on any characters that have already been output to your |gf|
+and/or |tfm| files. The current (incomplete) character will not be output.
+
+\smallbreak\item{9.} Type `|E|'. This is like `|X|', but it also prepares
+the computer to edit the file that \MF\ is currently reading, at the
+current position, so that you can conveniently make a change before
+trying again.
+
+\smallbreak\noindent
+After you type `|H|' (or `|h|', which also works), you'll get a message
+that tries to explain the current problem: The mathematical quantity just
+read by \MF\ (i.e., |mode.setup|) was not followed by `|=|' or `|:=|', so
+there was nothing for the computer to do with it. Chapter~6 explains that
+a ^{space} between tokens (e.g., `|mode|~|setup|'\thinspace) is equivalent to
+a ^{period} between tokens (e.g., `|mode.setup|'\thinspace). The correct
+spelling `|mode_setup|' would be recognized as a preloaded subroutine of
+plain \MF\!, but plain \MF\ doesn't have any built-in meaning for
+|mode.setup|. Hence |mode.setup| appears as a sort of orphan, and \MF\
+realizes that something is amiss.
+
+In this case, it's OK to go ahead and type \<return>, because we really
+don't need to do the operations of @mode\_setup@ when no special mode
+has been selected. \MF\ will continue by forgetting the isolated expression,
+and it will ignore the rest of line~1 because everything after a
+^^{percent} `|%|'~sign is always ignored. \ (This is another thing that
+will be explained in Chapter~6; it's a handy way to put ^{comments}
+into your \MF\ programs.) \ The changes that were made to line~1 of |badio.mf|
+therefore have turned out to be relatively harmless. But \MF\ will
+almost immediately encounter the mutilated semicolon in line~2:
+\begintt
+! Extra tokens will be flushed.
+<to be read again>
+ :
+l.2 em#:=10pt#:
+ cap#:=7pt#;
+?
+\endtt
+What does this mean? Type `|H|' to find out. \MF\ has no idea what to
+do with a `|:|' at this place in the file, so it plans to recover by
+``^{flushing}'' or getting rid of everything it sees, until coming to a
+semicolon. It would be a bad idea to type \<return> now, since you'd lose
+the important assignment `|cap#:=7pt#|', and that would lead to worse errors.
+
+You might type `|X|' or `|E|' at this point, to exit from \MF\ and to fix
+the errors in lines 1 and~2 before trying again. But it's usually best
+to keep going, trying to detect and correct as many mistakes as possible
+in each run, since that increases your productivity while
+decreasing your computer bills. An experienced \MF\ user will quit
+after an error only if the error is unfixable, or if there's almost no
+chance that additional errors are present.
+
+The solution in this case is to proceed in two steps: First type `|1|',
+which tells \MF\ to delete the next token (the unwanted `|:|'); then type
+`|I;|', which inserts a semicolon. This semicolon protects the rest of line~2
+from being flushed away,
+so all will go well until \MF\ reaches another garbled line.
+
+The next error message is more elaborate, because it is detected while
+\MF\ is trying to carry out a "penpos" command; "penpos" is not a
+primitive operation (it is defined in plain \MF), hence a lot more
+context is given:
+\begintt
+>> l0
+! Improper transformation argument.
+<to be read again>
+ ;
+penpos->...(EXPR3),0)rotated(EXPR4);
+ x(SUFFIX2)=0.5(x(SUFF...
+l.10 penpos1(thick,l0)
+ ; penpos2(.1[thin,thick],90-10);
+?
+\endtt
+At first, such error messages will appear to be complete nonsense to you,
+because much of what you see is low-level \MF\ code that you never wrote. But
+you can overcome this hangup by getting a feeling for the way \MF\ operates.
+
+The bottom line shows how much progress \MF\ has made so far in the |badio|
+file: It has read `|penpos1(thick,l0)|' but not yet the semicolon, on line~10.
+The "penpos" routine expands into a long list of tokens; indeed, this list
+is so long that it can't all be shown on two lines, and the appearances of
+`^|...|' indicate that the definition of "penpos" has been truncated here.
+Parameter values are often inserted into the expansion of a high-level
+routine; in this case, for example, `|(EXPR3)|' and `|(EXPR4)|' correspond
+to the respective parameters `|thick|' and `|l0|', and `|(SUFFIX2)|'
+corresponds to~`|1|'. ^^|EXPR| ^^|SUFFIX|
+\MF\ detected an error just after encountering the phrase `|rotated(EXPR4)|';
+the value of |(EXPR4)| was an undefined quantity (namely `|l0|',
+which \MF\ treats as the subscripted variable~`$l_0$'\thinspace), and
+^{rotation} is permitted only when a known numeric value has been supplied.
+Rotations are particular instances of what \MF\ calls {\sl^{transformations}\/};
+hence \MF\ describes this particular error by saying that an ``improper
+transformation argument'' was present.
+
+When you get a multiline error message like this, the best clues about the
+source of the trouble are usually on the bottom line (since that is what
+you typed) and on the top line (since that is what triggered the error
+message). Somewhere in there you can usually spot the problem.
+
+If you type `|H|' now, you'll find that
+\MF\ has simply decided to continue without doing the requested rotation.
+Thus, if you respond by typing \<return>, \MF\ will go on as if the program
+had said `|penpos1(thick,0)|'. Comparatively little harm has been done;
+but there's actually a way to fix the error perfectly before proceeding:
+Insert the correct rotation by typing
+\begintt
+I rotated 10
+\endtt
+and \MF\ will rotate by 10 degrees as if `|l0|' had been `|10|'.
+
+What happens next in Experiment 5? \MF\ will hiccup on the remaining
+bug that we planted in the file. This time, however, the typo will
+not be discovered until much later, because there's nothing wrong
+with line~11 as it stands. \ (The variable |thinn| is not defined,
+but undefined quantities are no problem unless you're doing something
+complicated like rotation. Indeed, \MF\ programs typically
+consist of equations in which there are lots of unknowns;
+variables get more and more defined as time goes on. Hence spelling
+errors cannot possibly be detected until the last minute.) \
+Finally comes the moment of truth, when |badio| tries to draw a
+path through an unknown point; and you will get an error message
+that's even scarier than the previous one:
+\begintt
+>> 0.08682thinn+144
+! Undefined x coordinate has been replaced by 0.
+<to be read again>
+ {
+<for(l)> ...FFIX0){up}..z4(SUFFIX0){
+ left}..cycle; ENDFOR
+penstroke->...ath_.e:=(TEXT0);endfor
+ .if.cycle.path_.l:cyc...
+<to be read again>
+ ;
+l.15 ... ..z3e{up}..z4e{left}..cycle;
+|quad
+?
+\endtt
+Wow; what's this? The expansion of @penstroke@ involves a ``@for@ loop,''
+and the error was detected in the midst of it. The
+expression `|0.08682thinn+144|' just above the error message implies that
+the culprit in this case was a misspelled `|thin|'. If that hadn't been
+enough information, you could have gleaned another clue from the fact that
+`|z4(SUFFIX0)|' has just been read; |(SUFFIX0)| is the current loop value
+and `|<for(l)>|' indicates that the value in question is `|l|', hence
+$z_{4l}$ is under suspicion. \ (Sure enough, the undefined $x$~coordinate
+that provoked this error can be shown to be $x_{4l}=0.08682"thinn"+144$.)
+
+In any event the mistake on line~11 has propagated too far to be fixable,
+so you're justified in typing `|X|' or~`|E|' at this point. But type~`|S|'
+instead, just for fun: This tells \MF\ to plunge ahead, correcting all
+remaining errors as best it can. \ (There will be a few more problems,
+since several variables still depend on `|thinn|'.) \ \MF\ will draw a
+very strange letter~O before it gets to the end of the file. Then you
+should type `|end|' to terminate the run.
+
+If you try to edit |badio.mf| again, you'll notice that line~2 still
+contains ^^{editing} a colon instead of a semicolon. The fact that you
+told \MF\ to delete the colon and to insert additional material doesn't
+mean that your file has changed in any way. However, the transcript file
+|badio.log| has a record of all the errors, so it's a handy reference when
+you want to correct mistakes. \ (Why not look at
+|badio.log| now, and |io.log| too, in order to get familiar with log files?)
+
+\dangerexercise Suppose you were doing Experiment 3 with |badio| instead
+of~|io|, so you began by saying `|\mode=smoke|; |input| |badio|'. Then you
+would want to recover from the error on line~1 by inserting a correct
+@mode\_setup@ command, instead of by simply \<return>ing, because
+@mode\_setup@ is what really establishes "smoke" mode. Unfortunately if you
+try typing `|I|~|mode_setup|' in response to the ``isolated expression''
+error, it doesn't work. What should you type instead?
+\answer After an ``isolated expression,'' \MF\ thinks it is at the end of
+a statement or command, so it expects to see a semicolon next. You should
+type, e.g., `|I;|~|mode_setup|' to keep \MF\ happy.
+
+By doing the five experiments in this chapter you have learned at first hand
+(1)~how to produce proofsheets of various kinds, including ``smoke proofs'';
+(2)~how to make a new font and test it; (3)~how to keep calm when \MF\
+issues stern warnings. Congratulations! You're on the threshold of being able to
+do lots more. As you read the following chapters, the best strategy
+will be for you to continue making trial runs, using experiments
+of your own design.
+
+\exercise However, this has been an extremely long chapter,
+so you should go outside now and get some {\sl real\/} exercise.
+\answer Yes.
+
+\endchapter
+
+Let us learn how Io's frenzy came---
+She telling her disasters manifold.
+\author \AE SCHYLUS, ^^{Aeschylus} %
+ {\sl Prometheus Bound\/} (c.\thinspace470 B.C.) % verse 801
+ % This is the translation by Morshead
+
+\bigskip
+
+To the student who wishes to use graphical methods as a tool,
+it can not be emphasized too strongly that practice in the use of that tool
+is as essential as a knowledge of how to use it.
+The oft-repeated pedagogical phrase, ``we learn by doing,'' is applicable here.
+\author THEODORE ^{RUNNING}, {\sl Graphical Mathematics\/} (1927) % p viii
+
+\eject
+ \beginchapter Chapter 6. How \MF\\Reads What You\\Type
+
+So far in this book we've seen lots of things that \MF\ can do, but we haven't
+discussed what \MF\ can't do. We have looked at many examples of commands that
+\MF\ can understand, but we haven't dwelt on the fact that the computer will
+find many phrases unintelligible. It's time now to adopt a more systematic
+approach and to study the exact rules of \MF's language. Then we'll know what
+makes sense to the machine, and we'll also know how to avoid ungrammatical
+utterances.
+
+A \MF\ program consists of one or more lines of text, where each line is made
+up of letters, numbers, punctuation marks, and other symbols that appear on
+a standard computer keyboard. A total of 95 different characters can be
+employed, namely a blank space plus the 94 visible symbols of standard ^{ASCII}.
+\ (Appendix~C describes the American Standard Code for Information
+Interchange, popularly known as ``ASCII,'' under which code numbers 33
+through~126 have been assigned to 94 specific symbols. This particular
+coding scheme is not important to a \MF\ programmer; the only relevant thing
+is that 94 different nonblank symbols can be used.)
+
+\MF\ converts each line of text into a series of {\sl ^{tokens}}, and a
+programmer should understand exactly how this conversion takes place.
+Tokens are the individual lexical units that govern the computer's
+activities. They are the basic building blocks from which meaningful
+sequences of instructions can be constructed. We discussed tokens briefly
+at the end of the previous chapter; now we shall consider them in detail.
+Line~9 of the file |io.mf| in that chapter is a typical example of what
+the machine might encounter:
+\begintt
+beginchar("O",0.8em#,cap#,0); "The letter O";
+\endtt
+When \MF\ reads these ASCII characters it finds sixteen tokens:
+\begindisplay \chardef\"=`\" \openup 2pt
+\ttok{beginchar}\quad\ttok{(}\quad\ttok{\"O\"}\quad
+ \ttok{,}\quad\ttok{0.8}\quad\ttok{em}\quad\ttok{\#}\quad\ttok{,}\cr
+\ttok{cap}\quad\ttok{\#}\quad\ttok{,}\quad\ttok{0}\quad
+ \ttok{)}\quad\ttok{;}\quad\ttok{\"The letter O\"}\quad\ttok{;}\cr
+\enddisplay
+Two of these, |"O"| and |"The| |letter| |O"|, are called {\sl^{string tokens}\/}
+because they represent strings of characters. Two of them, `|0.8|' and `|0|',
+are called {\sl^{numeric tokens}\/} because they represent numbers. The
+other twelve---`|beginchar|', `|(|', etc.---are called {\sl^{symbolic
+tokens}\/}; such tokens can change their meaning while a \MF\ program runs,
+but string tokens and numeric tokens always have a predetermined significance.
+Notice that clusters of letters like `|beginchar|' are treated as a unit;
+the same holds with respect to letters mixed with ^{underline} characters,
+as in `|mode_setup|'. Indeed,
+the rules we are about to study will explain that clusters of other
+characters like `|0.8|' and `|:=|' are also considered to be
+indecomposable tokens. \MF\ has a definite way of deciding where one
+token stops and another one begins.
+
+It's often convenient to discuss ^{grammatical rules} by formulating them in
+a special notation that was introduced about 1960 by John ^{Backus} and
+Peter ^{Naur}. Parts of speech are represented by named quantities in
+^{angle brackets}, and {\sl^{syntax rules}\/} are used to express the ways
+in which those quantities can be built~up from simpler units. For example,
+here are three syntax rules that completely describe the possible forms of
+numeric tokens:
+\def\\#1{\thinspace{\tt#1}\thinspace}
+\beginsyntax
+<decimal digit>\is\\0\alt\\1\alt\\2\alt\\3\alt\\4\alt\\5\alt\\6%
+ \alt\\7\alt\\8\alt\\9
+<digit string>\is<decimal digit>\alt<digit string><decimal digit>
+<numeric token>\is<digit string>\alt[.]<digit string>
+ \alt<digit string>\\.<digit string>
+\endsyntax
+The first rule says that a \<decimal digit> is either `|0|' or `|1|' or
+$\cdots$ or `|9|'; thus it must be one of the ten numerals. The next
+rule says that a \<digit string> is either a \<decimal digit> or a
+\<digit string> followed by a \<decimal digit>; thus it must be a sequence
+of one or more digits. Finally, a \<numeric token> has one of three forms,
+exemplified respectively by `|15|', `|.05|', and `|3.14159|'.
+
+Syntax rules explain only the surface structure of a language, not the
+underlying meanings of things. For example, the rules above tell us that
+`|15|' is a \<numeric token>, but they don't imply that `|15|' has
+any connection with the number fifteen. Therefore syntax rules are
+generally accompanied by rules of {\sl^{semantics}}, which ascribe
+meanings to the strings of symbols that meet the conditions of the syntax.
+In the case of numeric tokens, the principles of ordinary decimal notation
+define the semantics, except that \MF\ deals only with numbers in a
+limited range: A numeric token must be less than 4096, and its value is
+always rounded to the nearest multiple of $1\over65536$. Thus, for example,
+`|.1|'~does not mean $1\over10$, it means $6554\over65536$ (which is
+slightly greater than $1\over10$). It turns out that the tokens
+`|.099999|' and `|0.10001|' both have exactly the same meaning as
+^^{numeric tokens, rounded values} ^^{numeric tokens, maximum value}
+`|.1|', because all three tokens represent the value $6554\over65536$.
+
+\dangerexercise Are the following pairs of numeric tokens equivalent
+to each other, when they appear in \MF\ programs?
+\ (a)~|0| and |0.00001|; \ (b)~|0.00001| and |0.00002|;
+\ (c)~|0.00002| and |0.00003|; \ (d)~|04095.999999| and |10000|?
+\answer (a) No, the second token represents $1\over65536$. \ (A token has
+the same meaning as~`|0|' ^^{zero} if and only if its decimal value
+is strictly less than $2^{-17}=.00000\,76293\,94531\,25$.) \ (b)~Yes; both
+tokens represent $1\over65536$, because 1~is the nearest integer to both
+$.00001\times65536=.65536$ and $0.00002\times65536=1.31072$. \ (c)~No,
+|0.00003| represents $2\over65536$. \ (d)~Yes, they both mean ``^{enormous
+number} that needs to be reduced''; \MF\ complains in both
+cases and substitutes the largest legal numeric token. \ (Rounding
+4095.999999 to the nearest multiple of $1\over65536$ yields 4096,
+which is too big.)
+
+\MF\ converts each line of text into a sequence of tokens by repeating
+the following rules until no more characters remain on the line:
+\smallskip
+\hang\textindent{1)}If the next character is a ^{space}, or if it's a ^{period}
+(\thinspace`|.|'\thinspace) that isn't ^^{decimal point} followed by a
+decimal digit or a period, ignore it and move on.
+
+\hang\textindent{2)}If the next character is a ^{percent sign}
+(\thinspace`|%|'\thinspace), ignore it and also ignore everything else
+that remains on the current line. \ (Percent signs therefore allow you to
+write ^{comments} that are unseen by \MF\!.)
+
+\hang\textindent{3)}If the next character is a ^{decimal digit} or a period
+that's followed by a decimal digit, the next token is a numeric token,
+consisting of the longest sequence of contiguous characters starting at
+the current place that satisfies the syntax for \<numeric token> above.
+
+\hang\textindent{4)}If the next character is a ^{double-quote mark} (\thinspace
+`|"|'\thinspace), the next token is a string token, consisting of all
+characters from the current place to the next double-quote, inclusive.
+\ (There must be at least one more double-quote remaining on the line,
+otherwise \MF\ will complain about an ``^{incomplete string}.'') \ A string
+token represents the sequence of characters between the double-quotes.
+
+\hang\textindent{5)}If the next character is a ^{parenthesis} (\thinspace
+`|(|' or `|)|'\thinspace), a comma (\thinspace`|,|'\thinspace), or a
+semicolon (\thinspace`|;|'\thinspace), the next token is a symbolic token
+consisting of that single character.
+
+\hang\textindent{6)}Otherwise the next token is a symbolic token consisting
+of the next character together with all immediately following characters
+that appear in the same row of the following
+^^{table of character classes} table:
+\begindisplay \displayindent=0pt
+|ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz|\hidewidth\cr
+|<=>:|\|\cr
+|`'|\cr
+|+-|\cr
+|/*\|\cr
+|!?|\cr
+|#&@$|\cr
+|^~|\cr
+|[|\cr
+|]|\cr
+|{}|\cr
+|.|&(see rules 1, 3, 6)\cr
+|, ; ( )|&(see rule 5; these characters are ``loners'')\cr
+|"|&(see rule 4 for details about string tokens)\cr
+|0123456789|&(see rule 3 for details about numeric tokens)\cr
+|%|&(see rule 2 for details about comments)\cr
+\enddisplay
+
+\noindent
+The best way to learn the six rules about tokens is to work the following
+exercise, after which you'll be able to read any input file just as the
+computer does.
+
+\exercise What tokens does \MF\ find in the (ridiculous) line
+\begindisplay
+|xx3.1.6..[[a+-bc_d.e] ]"a %" <|\||>(($1. 5"+-""" % weird?|
+\enddisplay
+\answer \cstok{xx}, \cstok{3.1} (a numeric token), \cstok{.6} (another
+numeric token), \cstok{..}, \cstok{[[}, \cstok{a}, \cstok{+-},
+\cstok{bc\_d}, \cstok{e}, \cstok{]}, \cstok{]}, {\chardef\"=`\"\cstok{\"a
+\%\"} (a string token), \cstok{<\|>}, \cstok{(} (see rule~5), \cstok{(},
+\cstok{\$}, \cstok{1} (a numeric token), \cstok{5} (likewise numeric),
+\cstok{\"+-\"} (a string token), and \cstok{\"\"}} (a string token that
+denotes an empty sequence of characters).
+All of these tokens are symbolic unless otherwise mentioned. \ (Notice that
+four of the spaces and two of the periods were deleted by rule~1.
+One way to verify that \MF\ finds precisely these tokens is to prepare a
+test file that says `|isolated| |expression;|' on its first line and that
+contains the stated text on its second line. Then respond to \MF's
+error message by repeatedly typing `|1|', so that one token is deleted
+at a time.)
+
+\exercise Criticize the following statement: \MF\ ignores all spaces in the
+input.
+\answer The statement is basically true but potentially misleading. You can
+insert any number of spaces {\sl between\/} tokens without changing the
+meaning of a program, but you cannot insert a space in the {\sl middle\/}
+of any token without changing something. You can delete spaces between
+tokens {\sl unless\/} that would ``glue'' two adjacent tokens together.
+
+\dangerexercise True or false: If the syntax for \<numeric token> were
+changed to include a fourth alternative, `\<digit string>|.|', the meaning
+of \MF\ programs would not change in any way.
+\answer False. It may seem that this new sort of numeric token would be
+recognized only in cases where the period is not followed by a digit,
+hence the period would be dropped anyway by rule~1. However, the new rule
+would have disastrous consequences in a line like `|draw| |z1..z2|'!
+
+
+\endchapter
+
+Yet wee with all our seeking could see no tokens.
+ % of any such Wall.
+\author PHILEMON ^{HOLLAND}, {\sl ^{Camden}'s Brittania\/} (1610)
+% OED says page 518, but I couldn't find it there in the 1637 edition
+
+\bigskip
+
+Unpropitious tokens interfered.
+\author WILLIAM ^{COWPER}, {\sl ^{Homer}'s Iliad\/} (1791) % Book 4 verse 455
+
+\eject
+ \beginchapter Chapter 7. Variables
+
+One of \MF's most important concepts is the notion of a
+{\sl^{variable}\/}---something that can take on a variety of different
+values. Indeed, this is one of the most important concepts in all of
+mathematics, and variables play a prominent r\^ole in almost all
+computer languages. The basic idea is that a program manipulates data,
+and the data values are stored in little compartments of a computer's
+memory. Each little compartment is a variable, and we refer to an item
+of data by giving its compartment a name.
+
+For example, the |io.mf| program for the letter {\manual\IOO} in Chapter~5
+contains lots of variables. Some of these, like `|x1l|' and `|y1|', represent
+coordinates. Others, like `|up|', represent
+directions. The variables `|em#|' and `|thin#|' stand for physical,
+machine-independent distances; the analogous variables `|em|' and `|thin|'
+stand for the corresponding machine-dependent distances in units of pixels.
+
+These examples indicate that different variables are often related to each
+other. There's an implicit connection between `|em#|' and `|em|',
+between `|x1|' and `|y1|'; the `"penpos"' convention
+sets up relationships between `|x1l|', `|x1|', and `|x1r|'. By choosing
+the names of variables carefully, programmers can make their programs
+much easier to understand, because the relationships between variables
+can be made to correspond to the ^^{data structure} structure
+of their names.
+
+In the previous chapter we discussed tokens, the atomic elements from which
+all \MF\ programs are made. We learned that there are three kinds of
+tokens: numeric (representing numbers), string (representing text), and
+symbolic (representing everything else). Symbolic tokens have no
+intrinsic meaning; any symbolic token can stand for whatever a programmer
+wants it to represent.
+
+Some symbolic tokens do, however, have predefined {\sl^{primitive}\/}
+meanings, when \MF\ begins its operations. For example, `|+|' stands
+initially for ``plus,'' and `|;|' stands for ``finish the current
+statement and move on to the next part of the program.'' It is customary
+to let such tokens retain their primitive meanings, but any symbolic token
+can actually be assigned a new meaning as a program is performed. For
+example, the definition of `|test_I|' in |io.mf| makes that token stand
+for a {\sl^{macro}}, i.e., a subroutine. We'll see later that you can
+instruct \MF\ to `|let| |plus=+|', after which `|plus|' will act just
+like `|+|' did.
+
+\MF\ divides symbolic tokens into two categories, depending on their
+current meaning. If the symbolic token currently stands for one of \MF's
+primitive operations, or if it has been defined to be a macro, it is
+called a {\sl^{spark}\/}; otherwise it is called a {\sl^{tag}}. Almost
+all symbolic tokens are tags, because only a few are defined to be sparks;
+however, \MF\ programs typically involve lots of sparks, because sparks
+are what make things happen. The symbolic tokens on the first five lines
+of |io.mf| include the following sparks:
+\begintt
+mode_setup ; := / define_pixels ( , )
+\endtt
+and the following tags:
+\begintt
+em # pt cap thin thick o
+\endtt
+(some of which appear several times). Tags are used to designate variables,
+but sparks cannot be used within a variable's name.
+
+Some variables, like `|em#|', have names that are made from more than one token;
+in fact, the variable `|x1l|' is named by three tokens, one of which is
+numeric. \MF\ has been designed so that it is easy to make compound names
+that correspond to the relations between variables. Conventional programming
+languages like ^{Pascal} would refer to `|x1l|' by the more
+cumbersome notation `|x[1].l|'; it turns out that `|x[1].l|' is an
+acceptable way to designate the variable |x1l| in a \MF\ program, but the
+shorthand form `|x1l|' is a great convenience because such variables
+are used frequently.
+
+Here are the formal rules of syntax by which \MF\ understands the names of
+variables:
+\def\\#1{\thinspace{\tt#1}\thinspace}
+\beginsyntax
+<variable>\is<tag><suffix>
+<suffix>\is<empty>\alt<suffix><subscript>\alt<suffix><tag>
+<subscript>\is<numeric token>\alt\\{\char`\[}<numeric expression>\\]
+\endsyntax
+First comes a tag, like `|x|'; then comes a {\sl^{suffix}\/} to the tag,
+like `|1l|'.
+The suffix might be empty, or it might consist of one or more subscripts
+or tags that are tacked on to the original tag. A {\sl^{subscript}\/} is
+a numeric index that permits you to construct ^{arrays} of related
+variables. The subscript is either a single numeric token, or it is a formula
+enclosed in square ^{brackets}; in the latter case the formula should produce a
+^^|[| numeric value. For example, `|x[1]|' and `|x[k]|' and `|x[3-2k]|' all mean
+^^|]| the same thing as `|x1|', if\/ |k|~is a variable whose value is~1. But
+`|x.k|' is not the same; it is the tag~`|x|' suffixed by the tag~`|k|',
+not the tag~`|x|' subscripted by the value of variable~|k|.
+
+\danger The variables `|x1|' and `|x01|' and `|x1.00|' are identical.
+Since any numeric token can be used as a subscript, fractional indices
+are possible; for example, `|x1.5|' is the same as `|x[3/2]|'. Notice,
+however, that `|B007|' and `|B.007|' are {\sl not\/} the same variable,
+because the latter has a fractional subscript.
+
+\danger \MF\ makes each \<suffix> as long as possible. In other words,
+a \<suffix> is always extended if it is followed by a \<subscript>
+or a~\<tag>.
+
+\dangerexercise Explain how to type a reference to the doubly subscripted
+variable `|a[1][5]|' without using square brackets.
+\answer You can put a space between the subscripts, as in `|a1|~|5|'. \
+(We'll see later that a ^{backslash} acts as a null symbol,
+hence `|a1\5|' is another solution.)
+
+\dangerexercise Is it possible to refer to {\sl any\/} variable without
+using square brackets?
+\answer No; |a[-1]| can't be accessed without using |[| and |]|. The
+only other form of \<subscript> is \<numeric token>, which can't be
+negative. \ (Well, strictly speaking, you could say `|let|~|?=[;|
+|let|~|??=]|' and then refer to `|a?-1??|'; but that's cheating.)
+
+\ddangerexercise Jonathan H. ^{Quick} (a student) used `|a.plus1|' as the name
+of a variable at the beginning of his program; later he said `|let|
+|plus=+|'. How could he refer to the variable `|a.plus1|' after that?
+\answer Assuming that `|+|' was still a spark when he said `|let|~|plus=+|',
+he can't refer to the variable `|a.plus1|' unless he changes the meaning of
+|plus| again to make it a~tag. \ (We will eventually learn a way to do this
+without permanently clobbering |plus|, as follows: `^|begingroup| ^|save|
+|plus;| |a.plus1| ^|endgroup|'.)
+
+\danger \MF\ has several special variables called {\sl^{internal
+quantities}\/} that are intimately wired-in to the computer's behavior.
+For example, there's an internal quantity called `^|fontmaking|' that controls
+whether or not a |tfm| file is produced; another one called `^|tracingtitles|'
+governs whether or not titles like |"The| |letter|~|O"| appear on your
+terminal; still another one called `^|smoothing|' affects the digitization of
+curves. \ (A complete list of \MF's internal quantities appears in
+Chapter~25.) \ The name of an internal quantity acts like a tag, but
+internal quantities cannot be suffixed or subscripted.
+Thus, the syntax rule for \<variable>
+should actually be replaced by a slightly more complicated pair of rules:
+\beginsyntax
+<variable>\is<external tag><suffix>\alt<internal quantity>
+<tag>\is<external tag>\alt<internal quantity>
+\endsyntax
+
+\dangerexercise True or false: Every \<variable> is a legal \<suffix>.
+\answer False. After `|newinternal x;|' you can't say
+`|x|\<tag>' in a \<suffix list>.
+
+\ddanger The `|[|' and `|]|' that appear in the syntax for \<subscript>
+stand for any symbolic tokens whose current meanings are the same as
+\MF's primitive meanings of left and right bracket, respectively;
+those tokens don't necessarily have to be brackets. Conversely, if the
+meanings of the tokens `|[|' and `|]|' have been changed, brackets cannot
+be used to delimit subscripts. Similar remarks apply to all of the
+symbolic tokens in all of the syntax rules from now on. \MF\ doesn't look
+at the form of a token; it considers only a token's current meaning.
+
+The examples of \MF\ programs in this book have used two different
+typographic conventions. Sometimes we refer to variables by using
+^{italic type} and/or genuine subscripts, e.g., `"em"' and `$x_{2r}$';
+but sometimes we refer to those same variables by using a ^{typewriter}-like
+style of type, e.g., `|em|' and~`|x2r|'. In general, the typewriter style
+is used when we are mainly concerned with the way a programmer is supposed
+to type something that will appear on the terminal or in a file; but fancier
+typography is used when we are focusing on the meaning of a program rather
+than its ASCII representation. It should be clear how to convert the fancier
+form into tokens that \MF\ can actually understand.
+
+\danger In general, we shall use italic type only for tags (e.g., "em",
+"x", "r"), while boldface and roman type will be used for sparks
+(e.g., @draw@, @fill@, cycle, rotated, sqrt). Tags that consist of special
+characters instead of letters will sometimes get special treatment;
+for example, |em#| and |z2'| might be rendered $"em"\0$ and $z'_2$,
+respectively.
+
+The variables we've discussed so far have almost always had numbers as their
+values, but in fact \MF's variables are allowed to assume values of eight
+different ^{types}. A variable can be of type
+\nobreak\smallskip
+\item\bull^{boolean}, representing the values `^{true}' or `^{false}';
+\item\bull^{string}, representing sequences of ASCII characters;
+\item\bull^{path}, representing a (possibly curved) line;
+\item\bull^{pen}, representing the shape of a pen nib;
+\item\bull^{picture}, representing an entire pattern of pixels;
+\item\bull^{transform}, representing the operations of scaling, rotating,
+ shifting, reflecting, and/or slanting;
+\item\bull^{pair}, representing two numbers (e.g., a point or a vector);
+\item\bull^{numeric}, representing a single number.
+\smallskip\noindent
+If you want a variable to represent something besides a number, you must
+first give a {\sl^{type declaration}\/} ^^{declarations} that states
+what the type will be. But if you refer to a variable whose type has not
+been declared, \MF\ won't complain, unless you try to use it in a way that
+demands a value that isn't numeric.
+
+Type declarations are easy. You simply name one of the eight types,
+then you list the variables that you wish to declare for that type.
+For example, the declaration
+\begindisplay
+@pair@ "right", "left", $a.p$
+\enddisplay
+says that "right" and "left" and $a.p$ will be variables of type @pair@,
+so that equations like
+\begindisplay
+$"right"=-"left"=2a.p=(1,0)$
+\enddisplay
+can be given later. These equations, incidentally, define the values
+$"right"=(1,0)$, $"left"=(-1,0)$, and $a.p=(.5,0)$. \ (Plain \MF\
+has the stated values of "right" and "left" already built~in.)
+
+The rules for declarations are slightly trickier when subscripts are
+involved, because \MF\ insists that all variables whose names are identical
+except for subscript values must have the same type. It's possible to
+set things up so that, for example, $a$~is numeric, $a.p$ is a pair,
+$a.q$ is a pen, $a.r$ is a path, and $a_1$ is a string; but if $a_1$
+is a string, then all other variables $a_2$, $a_3$, etc., must also be
+strings. In order to enforce this restriction, \MF\ allows only
+``collective'' subscripts, represented by empty brackets `^|[]|',
+to appear in type declarations. ^^{collective subscripts} For example,
+\begintt
+path r, r[], x[]arc, f[][]
+\endtt
+declares $r$ and all variables of the forms $r[i]$, $x[i]"arc"$,
+and $f[i][j]$ to be path variables. This declaration doesn't affect
+the types or values of other variables like $r[\,]"arc"$; it affects
+only the variables that are specifically mentioned.
+
+Declarations destroy all previous values of the variables being defined.
+For example, the path declaration above makes $r$ and $r[i]$ and $x[i]"arc"$
+and $f[i][j]$ undefined, even if those variables previously had paths
+as their values. The idea is that all such variables will start out with a
+clean slate so that they can receive appropriate new values based on
+subsequent equations. ^^{value, disappearance of}
+
+\exercise Numeric variables don't need to be declared. Therefore is there
+ever any reason for saying `|numeric| |x|'\thinspace?
+\answer Yes, because it removes any existing value that $x$ may have
+had, of whatever type; otherwise you couldn't safely use $x$ in a
+numeric equation. It's wise to declare numeric variables when you're
+not sure about their former status, and when you're sure that you don't
+care what their previous value was. A numeric declaration together with a
+comment also provides useful documentation. \ (Incidentally, `|numeric|~|x|'
+doesn't affect other variables like `|x2|' or `|x.x|' that might be present.)
+
+\danger The formal syntax rules for type declarations explain these
+grammatical conventions precisely. If the symbolic token that begins a
+declared variable was previously a spark, it loses its former meaning and
+immediately becomes a tag.
+\beginsyntax
+<declaration>\is<type><declaration list>
+<type>\is[boolean]\alt[string]\alt[path]\alt[pen]
+ \alt[picture]\alt[transform]\alt[pair]\alt[numeric]
+<declaration list>\is<declared variable>
+ \alt<declaration list>[,]<declared variable>
+<declared variable>\is<symbolic token><declared suffix>
+<declared suffix>\is<empty>\alt<declared suffix><tag>
+ \alt<declared suffix>\\{\char`\[}\\]
+\endsyntax
+
+\dangerexercise Find three errors in the supposed declaration
+`|transform| |t42,24t,,t,path|'.
+\answer (a)~The `|42|' is illegal because subscripts must be collective.
+\ (b)~The `|24|' is illegal because a \<declared variable> must start with
+a \<symbolic token>, not a numeric token. \ (c)~There's nothing wrong with
+the consecutive commas; the second comma begins a \<declared variable>, so
+it loses its former meaning and becomes a tag. Thus \MF\ tries to declare
+the variable `|,t,path|'. However, `|path|' cannot appear in a suffix,
+since it's a spark. \ (Yes, this is admittedly tricky. Computers follow rules.)
+
+\endchapter
+
+Beings low in the scale of nature are
+more variable than those which are higher.
+\author CHARLES ^{DARWIN}, {\sl On the Origin of Species\/} (1859) % p149
+
+\bigskip
+
+Among the variables, {\rm Beta ({\cmman\char'14\/}) Persei}, or\/ {\rm^{Algol}},
+is perhaps the most interesting, as its period is short.
+\author J. NORMAN ^{LOCKYER}, {\sl Elements of Astronomy\/} (1870)
+ % American edition, p40
+
+\eject
+ \beginchapter Chapter 8. Algebraic\\Expressions
+
+\MF\ programmers express themselves algebraically by writing algebraic
+formulas called {\sl^{expressions}}. The formulas are algebraic in the
+sense that they involve variables as well as constants. By combining
+variables and constants with appropriate mathematical operations, a
+programmer can specify an amazing variety of things with comparative ease.
+
+We have already seen many examples of expressions; our goal now is to make
+a more systematic study of what is possible. The general idea is that an
+expression is either a ^{variable} (e.g., `$x_1$'\thinspace) or a
+^{constant} (e.g., `20'\thinspace), or it consists of an ^{operator}
+(e.g., `$+$'\thinspace) together with its ^{operands} (e.g.,
+`$x_1+20$'\thinspace). The operands are, in turn, expressions built~up in
+the same way, perhaps enclosed in ^{parentheses}. For example,
+`$(x_1+20)/(x_2-20)$' is an expression that stands for the quotient of two
+subexpressions. It is possible to concoct extremely complicated algebraic
+expressions, but even the most intricate constructions are built from
+simple parts in simple ways.
+
+Mathematicians spent hundreds of years developing good ways to write formulas;
+then computer scientists came along and upset all the time-honored traditions.
+The main reason for making a change was the fact that computers find it
+difficult to deal with two-dimensional constructions like
+\begindisplay
+$\displaystyle{x_1+20\over x_2-20}+\sqrt{a^2-{2\over3}\sqrt b}.$
+\enddisplay
+One-dimensional sequences of tokens are much easier to input and to decode;
+hence programming languages generally put such formulas all on one line,
+^^{sqrt} by inserting parentheses, brackets, and asterisks as follows:
+\begintt
+(x[1]+20)/(x[2]-20)+sqrt(a**2-(2/3)*sqrt(b)).
+\endtt
+\MF\ will understand this formula, but it also accepts a notation that
+is shorter and closer to the standard conventions of mathematics:
+\begintt
+(x1+20)/(x2-20)+sqrt(a**2-2/3sqrt b).
+\endtt
+We observed in the previous chapter that \MF\ allows you to write `|x2|'
+instead of `|x[2]|'; similarly, you can write `|2x|' instead of `|2*x|'
+and `|2/3x|' instead of `|(2/3)*x|'. Such operations are extremely common
+in \MF\ programs, hence the language has been set up to facilitate them.
+On the other hand, \MF\ doesn't free you from all the inconveniences of
+computer languages; you must still write `|x*k|' for the ^{product} of
+$x$ times~$k$, and `|x[k]|' for the variable $x$~subscripted by~$k$,
+in order to avoid confusion with the suffixed variable `|x.k|'.
+
+We learned in the previous chapter that there are eight types of
+variables: numeric, boolean, string, and so~on. The same types apply
+to expressions; \MF\ deals not only with numeric expressions but also
+with boolean expressions, string expressions, and the others. For example,
+`$(0,0)\to(x_1,y_1)$'
+is a path-valued expression, formed by applying the operator `$\to$' to the
+subexpressions `$(0,0)$' and `$(x_1,y_1)$'; these subexpressions, in turn,
+have values of type ``pair,'' and they have been built up from values of
+type ``numeric.'' Each operation produces a result whose type can be
+determined from the types of the operands; furthermore, the simplest
+expressions (variables and constants) always have a definite type.
+Therefore the machine always knows what type of quantity it is dealing
+with, after it has evaluated an expression.
+
+If an expression contains several operators, \MF\ has to decide which
+^^{order of operations}
+operation should be done first. For example, in the expression `$a-b+c$'
+it is important to compute `$a-b$' first, then to add~$c$; if `$b+c$' were
+computed first, the result `$a-(b+c)$' would be quite different from the
+usual conventions of mathematics. On the other hand, mathematicians
+usually expect `$b/c$' to be computed first in an expression like
+`$a-b/c$'; multiplications and divisions are usually performed before
+additions and subtractions, unless the contrary is specifically indicated
+by parentheses as in `$(a-b)/c$'. The general rule is to evaluate
+subexpressions in parentheses first, then to do operations in order of
+their ``^{precedence}''; if two operations have the same precedence, the
+left one is done first. For example, `$a-b/c$' is equivalent to
+`$a-(b/c)$' because division takes precedence over subtraction; but
+`$a-b+c$' is equivalent to `$(a-b)+c$' because left-to-right order is
+used on operators of equal precedence.
+
+It's convenient to think of operators as if they are tiny ^{magnets} that
+attract their operands; the magnets for `$\ast$' and `/' are stronger
+than the magnets for `$+$' and `$-$', so they stick to their operands more
+tightly and we want to perform them first.
+
+\MF\ distinguishes four (and only four) levels of precedence. The
+strongest magnets are those that join `2' to~`$x$' and `sqrt' to `$b$'
+in expressions like `$2x$' and `sqrt$\,b$'. The next strongest are
+multiplicative operators like `$\ast$' and~`/'; then come the additive
+operators like `$+$' and~`$-$'. The weakest magnets are operators like
+`$\to$' or `$<$'. For example, the expression
+\begindisplay
+$a+{\rm sqrt}\,b/2x<c$
+\enddisplay
+is equivalent to the fully parenthesized formula
+\begindisplay
+$\bigl(a+\bigl(({\rm sqrt}\,b)/(2x)\bigr)\bigr)<c$.
+\enddisplay
+
+\exercise Insert parentheses into the formula `|z1+z2..z3/4*5..z6-7*8z9|',
+to show explicitly in what order \MF\ will do the operations.
+\answer |((z1+z2)..((z3/4)*5))..(z6-(7*(8z9)))|.
+
+\danger High-school algebra texts often avoid parentheses inside of
+parentheses by using ^{braces} and ^{brackets}. Therefore many people
+have been trained to write
+\begindisplay
+$\{a+[({\rm sqrt}\,b)/(2x)]\}<c$
+\enddisplay
+instead of the fully parenthesized formula above. However, professional
+mathematicians usually stick to only one kind of parentheses, because
+braces and brackets have other meanings that are more important. In this
+respect \MF\ is like the professionals: It reserves curly braces `|{}|'
+and square brackets `|[]|' for special purposes, so you should not
+try to substitute them for parentheses.
+
+\ddanger If you really want alternatives to parentheses, there is actually
+a way to get them. You can say, for example,
+\begintt
+delimiters [[ ]]; delimiters {{ }}
+\endtt
+after which double brackets and braces can be used in formulas like
+\begintt
+{{a+[[(sqrt b)/(2x)]]}}<c.
+\endtt
+The symbolic token `|{{|' has no relation to `|{|', and it
+has no primitive meaning, hence you are free to define it in any way you
+like; the ^@delimiters@ command defines a new pair of delimiters. In formulas
+with mixed delimiters as defined here, \MF\ will check that `|[[|' matches only
+with~`|]]|', `|{{|'~only with~`|}}|', and `|(|'~only with~`|)|'; thus you
+can more easily detect errors in large expressions. However, it's usually
+unnecessary to have any delimiters other than parentheses, because large
+expressions are rare, and because the rules of operator precedence make
+most parentheses superfluous.
+
+If you're reading this chapter carefully, you may be thinking, ``Hey wait!
+Isn't there a contradiction? A minute ago I was told that `|2/3x|' stands
+for `|(2/3)*x|', but now the rules of precedence appear to state that
+`|2/3x|' really stands for `|2/(3x)|'. What gives?'' Indeed, you have an
+excellent point; but there is no contradiction, because of another rule that
+hasn't been mentioned yet. When two {\sl numeric tokens\/} are divided, the
+^^{division of numeric tokens} magnetism of `|/|' is stronger than usual;
+in this case `|/|' has the same precedence as the implied multiplication
+operator in `|3x|'. Hence the operations in `|2/3x|' are carried out from
+left to right, as stated previously. \ (This is a good rule because it
+is almost always what a \MF\ programmer wants. However, one should bear
+in mind that `|a/3x|' means `|a/(3x)|' when |a| is {\sl not\/} a numeric token.)
+
+Because of the rule in the previous paragraph, the \MF\ programs in this
+book often say `${2\over3}x$' for what would be typed `|2/3x|' in a file.
+Such built-up ^{fractions} are never used except when the numerator and
+denominator are both numbers; a construction like `|a/3x|' will always be
+rendered as `$a/3x$', not~`$\,{a\over3x}\,$'.
+
+\MF\ knows how to do dozens of operations that haven't been mentioned yet
+in this book. Let's take a look at some of them, so that we will know
+they are available in case of need. It will be most instructive and
+most fun to learn about expressions by interacting with the computer;
+^^"tracingonline" ^^@scrollmode@ ^^@forever@ ^^@scantokens@ ^^{readstring}
+^^@message@
+therefore you should prepare the following short file, called ^|expr.mf|:
+\begintt
+string s[]; s1="abra";
+path p[]; p1=(0,0)..(3,3); p2=(0,0)..(3,3)..cycle;
+tracingonline:=1; scrollmode;
+forever: message "gimme an expr: "; s0:=readstring;
+show scantokens s0; endfor
+\endtt
+
+\danger You don't need to understand what's in |expr.mf| when you read this
+chapter for the first time, because the file uses \MF\ in ways that will be
+explained carefully later. But here is a translation, in case you're
+curious: Line~1 declares all variables of the form $s_k$ to be strings, and
+sets $s_1$ to the value |"abra"|. Line~2 declares all variables of the
+form~$p_k$ to be paths, and sets $p_1$ and~$p_2$ to simple example paths.
+Line~3 tells \MF\ to print diagnostic information ^{online}, i.e., on the
+terminal as well as in the ^{log file}; it also establishes
+`@scrollmode@', which means that the computer won't stop after error
+messages. Lines 4 and~5 set up an infinite loop in which \MF\ reads an
+expression from the terminal and shows the corresponding value.
+
+\outer\def\begindemo{$$\advance\baselineskip by2pt
+ \catcode`\"=\other
+ \halign\bgroup\indent\hbox to 160pt{\tt##\hfil}&\tt##\hfil\cr
+ \noalign{\vskip-2pt}}
+\outer\def\enddemo{\egroup$$}
+\def\werror{\ \rm(with error message)}
+\def\werrors{\ \rm(with error messages)}
+\def\demohead{\it\kern-2pt You type&\it\kern-1pt And the result is\cr
+ \noalign{\nobreak\vskip2pt}}
+
+If you start \MF\ and type `|expr|' when it asks for an input file name,
+it will read the file |expr.mf| and then it will say `^|gimme|
+|an|~|expr|'. Here's where the fun starts: You can type any expression,
+and \MF\ will compute
+and display its value. Try it; type `|2+2|' and \<return>, obtaining the
+value~`|>>|~|4|'. Isn't that amazing? Here are some more things to try:
+\begindemo
+\demohead
+1.2-2.3&-1.1\cr
+1.3-2.4&-1.09999\cr
+1.3*1000&1300.00305\cr
+2.4*1000&2399.9939\cr
+3/8&0.375\cr
+.375*1000&375\cr
+1/3&0.33333\cr
+1/3*3&0.99998\cr
+0.99999&0.99998\cr
+1-epsilon&0.99998\cr
+1/(1/3)&3.00005\cr
+1/3.00005&0.33333\cr
+.1*10&1.00006\cr
+1+4epsilon&1.00006\cr
+\enddemo
+These examples illustrate the small errors that occur because \MF\ does
+``fixed binary'' ^{arithmetic} using integer multiples of $1\over65536$.
+The result of $1.3-2.4$ is not quite the same as $-1.1$, because |1.3| is
+a little bit larger than~$13\over10$ and |2.4| is a little smaller
+than~$24\over10$. Small errors get magnified when they are multiplied by
+1000, but even after magnification the discrepancies are negligible because
+they are just tiny fractions of a pixel. You may be surprised that
+1/3~times~3 comes out to be .99998 instead of .99999; the truth is that both
+|0.99999| and |0.99998| represent the same value, namely $65535\over65536$; \MF\
+displays this value as |0.99998| because it is closer to .99998 than to
+.99999. Plain \MF\ defines ^"epsilon" to be $1\over65536$, the smallest
+representable number that is greater than zero; therefore |1-epsilon|
+is $65535\over65536$, and |1+4epsilon| is $65540\over65536$.
+\begindemo
+\demohead
+4096&4095.99998\werror\cr
+infinity&4095.99998\cr
+1000*1000&32767.99998\werror\cr
+infinity+epsilon&4096\cr
+100*100&10000\cr
+.1(100*100)&1000.06104\cr
+(100*100)/3&3333.33333\cr
+\enddemo
+\MF\ will complain that an `|Enormous| ^^{enormous number} |number| |has|
+|been| |reduced|' when you try to introduce constants that are 4096 or~more.
+Plain \MF\ defines ^"infinity" to be $4096-"epsilon"$, which is the largest
+legal numeric token. On the other hand, it turns out that larger numbers
+can actually arise when an expression is being evaluated; \MF\ doesn't
+worry about this unless the resulting magnitude is at least 32768.
+
+\dangerexercise If you try `|100*100/3|' instead of `|(100*100)/3|', you
+get `|3333.33282|'. Why?
+\answer The fraction |100/3| is evaluated first (because such divisions
+take precedence); the rounding error in this fraction is then magnified by~100.
+
+\ddanger Sometimes \MF\ will compute things more accurately than you would
+expect from the examples above, because many of its internal calculations
+are done with multiples of $2^{-28}$ instead of $2^{-16}$. For example,
+if $t=3$ the result of `|1/3t|' will be exactly~1
+(not 0.99998); the same thing happens if you write `|1/3(3)|'.
+
+Now let's try some more complicated expressions, using undefined
+variables as well as constants. \ (Are you actually trying these
+examples, or are you just reading the book? It's far better to type
+them yourself and to watch what happens; in fact, you're also allowed
+to type things that {\sl aren't\/} in the book!)
+\begindemo
+\demohead
+b+a&a+b\cr
+a+b&a+b\cr
+b+a-2b&a-b\cr
+2(a-b+.5)&2a-2b+1\cr
+.5(b-a)&-0.5a+0.5b\cr
+.5[a,b]&0.5a+0.5b\cr
+1/3[a,b]&0.66667a+0.33333b\cr
+0[a,b]&a\cr
+a[2,3]&a+2\cr
+t[a,a+1]&t+a\cr
+a*b&b\werror\cr
+1/b&b\werror\cr
+\enddemo
+\MF\ has a preferred way to arrange variables in order when they are added
+together; therefore `$a+b$' and `$b+a$' give the same result. Notice that
+the ^{mediation} construction `$.5[a,b]$' specifies a number that's halfway
+between $a$ and~$b$, as explained in Chapter~2. \MF\ does not allow you to
+^{multiply} two unknown numeric quantities together, nor can you ^{divide} by an
+unknown numeric; all of the unknown expressions that \MF\ works with must be
+``^{linear forms},'' i.e., they must be sums of variables with constant
+coefficients, plus an optional constant. \ (You might want to try typing
+`|t[a,b]|' now, in order to see what error message is given.)
+\begindemo
+\demohead
+sqrt 2&1.41422\cr
+sqrt 100&10\cr
+sqrt 100*100&1000\cr
+sqrt(100*100)&100\cr
+sqrt 100(100)&100\cr
+sqrt sqrt 100(100)&10\cr
+sqrt .01&0.09998\cr
+0.09998**2&0.01\cr
+2**1/2&1.41422\cr
+sqrt 2**2&2\cr
+sqrt -1&0\werror\cr
+sqrt a&a\werror\cr
+\enddemo
+Since ^|sqrt| has more ``magnetism'' than |*|, the formula |sqrt|~|100*100|
+^^{square roots}
+is evaluated as |(sqrt|~|100)*100|; but in `|sqrt|~|100(100)|' the
+|100(100)| is computed first. The reason is that `|(sqrt|~|100)(100)|' isn't
+a legal expression, so the operations in `|sqrt|~|100(100)|' must be carried
+out from right to left. If you are unsure about the order of evaluation,
+^^|**| you can always insert parentheses; but you'll find that \MF's rules of
+precedence are fairly natural as you gain experience.
+
+\exercise Is `|sqrt|~|2**2|' computed as `|(sqrt|~|2)**2|' or as
+`|sqrt(2**2)|'\thinspace?
+\answer A |sqrt| takes precedence over any operation with two operands, hence
+the machine computes `|(sqrt|~|2)**2|'; \MF\ was somewhat lucky that the
+answer turned out to be exactly~2. \ (The |sqrt| operation computes the
+nearest multiple of $1\over65536$, and the rounding error in this quantity
+is magnified when it is squared. If you try |sqrt|~|3**2|, you'll get
+|3.00002|; also |sqrt|~|2**4| turns out to be |4.00002|.) \ Incidentally,
+the ^|**| operation of plain \MF\ has the same precedence as |*| and~|/|;
+hence `|x*y**2|' means the same as `|(x*y)**2|', and `|-x**2|' means
+`|(-x)**2|', contrary to the conventions of {\eightrm ^{FORTRAN}}.
+
+Some \MF\ expressions have `^{true}' or `^{false}' values, instead of numbers;
+we will see later that they can be used to adapt \MF\ programs to special
+conditions.
+\begindemo
+\demohead
+0<1&true\cr
+0=1&false\cr
+a+1>a&true\cr
+a>=b&false\werror\cr
+"abc"<="b"&true\cr
+"B">"a!"&false\cr
+"b">"a?"&true\cr
+(1,2)<>(0,4)&true\cr
+(1,2)<(0,4)&false\cr
+(1,a)>(0,b)&true\cr
+numeric a&true\cr
+known a&false\cr
+not pen a&true\cr
+known "a" and numeric 1&true\cr
+(0>1) or (a<a)&false\cr
+0>1 or a<a&a\werrors\cr
+\enddemo
+^^{not} ^^@and@ ^^@or@ ^^{comparison}
+The tokens `^|>=|', `^|<=|', and `^|<>|' stand respectively for the
+^{relations} ^{greater-than-or-equal-to}, ^{less-than-or-equal-to}, and
+^{unequal-to}. When strings are compared, \MF\ uses the order of words in
+a dictionary, except that it uses ASCII code to define ordering of individual
+characters; thus, all uppercase letters are considered to be less than all
+lowercase letters. \ (See Appendix~C\null.) \ When pairs of numbers are
+compared, \MF\ considers only the $x$~coordinates, unless the $x$~coordinates
+are equal; in the latter case it compares the $y$~coordinates. The type
+of an expression can be ascertained by an expression like `|pair|~|a|',
+which is true if and only if |a|~is a pair. ^^{pair} ^^{numeric} ^^{pen}
+The expression `|known|~|a|' ^^{known} is true if and only if the value
+of~|a| is fully known.
+
+\dangerexercise What causes the error messages in `|0>1|~|or|~|a<a|'\thinspace?
+\answer Since `^@or@' has stronger precedence than `$<$' or `$>$', ^^|<| ^^|>|
+\MF\thinspace\ tries to evaluate this expression by putting things in
+parentheses as follows: `$(0>(1\mathbin{\bf or}a))<a$'. Now
+`$1\mathbin{\bf or}a$' makes no sense, because `@or@' operates only on
+booleans; in such cases \MF\ uses the right operand~`$a$' as the result. Then
+`$\mkern1mu0>a$' is indeterminate because $a$~is unknown; \MF\ treats this as
+false. Finally `${\rm false}<a$' is another illegal combination of types.
+
+\danger The rest of this chapter is entirely preceded by ``dangerous bend''
+signs, so you can safely omit it on first reading (unless you're hooked
+and can't stop).
+
+\danger \MF\ expressions can include many operations that are
+less familiar but still useful. For example, the ^{max} and ^{min}
+operations compute the ^{maximum} and ^{minimum} of numbers, strings,
+or pairs:
+\begindemo
+\demohead
+max(1,-2,4)&4\cr
+min(1,-2,4)&-2\cr
+max("a","b","ab")&"b"\cr
+min("a","b","ab")&"a"\cr
+max((1,5),(0,6),(1,4))&(1,5)\cr
+min((1,5),(0,6),(1,4))&(0,6)\cr
+max(.5a+1,.5a-1)&0.5a+1\cr
+\enddemo
+Numbers can be converted to ^{integers} in a variety of ways:
+\begindemo
+\demohead
+floor 3.14159&3\cr
+floor -3.14159&-4\cr
+floor -epsilon&-1\cr
+floor infinity&4095\cr
+ceiling 3.14159&4\cr
+ceiling -3.14159&-3\cr
+round 3.14159&3\cr
+round -3.14159&-3\cr
+round(1.1,2.8)&(1,3)\cr
+round(3.5,-3.5)&(4,-3)\cr
+round a&a+0.5\werror\cr
+8 mod 3&2\cr
+-8 mod 3&1\cr
+.8 mod .3&0.2\cr
+\enddemo
+The `^{floor}' operation computes the ^{greatest integer} that is less than
+or equal to its operand; this quantity is often denoted by $\lfloor x\rfloor$
+in mathematics texts. Plain \MF\ also includes the analogous `^{ceiling}'
+operation $\lceil x\rceil$, which is the ^{least integer} greater than or
+equal to~$x$. Furthermore, `^{round}$\,x$' is the integer nearest to~$x$;
+plain \MF\ computes this by using the formula $\lfloor x+.5\rfloor$, and
+applies it to both components of a pair if a pair is being rounded. The
+^{remainder} of $x$ with respect to~$y$, written `$x\bmod y$', ^^{mod} is
+calculated by using the formula $x-y\lfloor x/y\rfloor$.
+\begindemo
+\demohead
+abs -7&7\cr
+abs(3,4)&5\cr
+length(3,4)&5\cr
+3++4&5\cr
+300++400&500\cr
+sqrt(300**2 + 400**2)&181.01933\werrors\cr
+1++1&1.4142\cr
+0 ++ -7&7\cr
+5+-+4&3\cr
+\enddemo
+^^{abs} ^^{length} ^^{absolute value}
+\def\stt{\vbox to7.25pt{}}%
+The `^|++|' operation is called {\sl^{Pythagorean addition}\/}; $a\pyth+b$
+is the same thing as $\sqrt{\stt a^2+b^2}$. Most of the ^{square root}
+operations in computer programs could probably be avoided if $++$ were
+more widely available, because people seem to want square roots primarily
+when they are computing distances. Notice that $a\pyth+b\pyth+c=
+\sqrt{\stt a^2+b^2+c^2}$; we have the identity $(a\pyth+b)\pyth+c=a\pyth+(
+b\pyth+c)$ as well as $a\pyth+b=b\pyth+a$. It is better to use Pythagorean
+addition than to calculate $\sqrt{\stt a^2+b^2}$, because the computation
+of $a^2$ and $b^2$ might produce numbers that are too large even when
+$a\pyth+b$ is rather small. There's also an inverse operation,
+^{Pythagorean subtraction}, which is denoted by `^|+-+|'; the quantity
+$a\mathbin{+{-}+}b$ is equal to $\sqrt{\stt a^2-b^2}$.
+
+\dangerexercise When the author was preparing these examples he typed
+`|0++-7|' and was surprised to get the answer `|0|'. Why should this not
+have been a surprise?
+\answer The token `|++-|' is undefined, so it is a tag; therefore
+|++-7| is a subscripted variable, which was multiplied by zero.
+
+\ddangerexercise (For mathematicians.) \ Although the Pythagorean addition
+operation is associative and commutative, \MF\ says that
+$5\pyth+4\pyth+2\pyth+2=7=2\pyth+2\pyth+4\pyth+5$ yet
+$2\pyth+4\pyth+5\pyth+2=6.99998$. Why?
+\answer The associative law is valid for exact computations, but not
+for rounded computations. For example, it fails even in the case of
+multiplication, since $(.1\ast.1)\ast10=0.09995$ while $.1\ast(.1\ast10)=.1$
+when products are rounded to the nearest multiples of $1\over65536$.
+However, this observation doesn't quite explain the stated example, which
+would have yielded 7 in all cases if \MF\ had computed $2\pyth+4$ with
+full accuracy! The closest approximation to $\sqrt{20}$ is ^^{accuracy}
+$4{30942\over65536}$, but $2\pyth+4$ turns out to be $4{30941\over65536}$
+instead. \MF\ computes the absolutely best possible approximations to the
+true answers when it does multiplications, divisions, and square roots,
+but not when it does Pythagorean operations.
+
+\danger \MF\ uses the names `^{sind}' and `^{cosd}' for the ^{trigonometric}
+functions ^{sine} and ^{cosine}, because \MF's operations are designed to
+deal with angles expressed in degrees. But it turns out that programmers
+rarely need to refer to sines and cosines explicitly, because the `^{dir}'
+and `^{angle}' functions provide most of what a font designer needs.
+\begindemo
+\demohead
+sind 30&0.5\cr
+cosd 30&0.86603\cr
+sind -30&-0.5\cr
+cosd 360&1\cr
+sind 10 ++ cosd 10&1\cr
+dir 30&(0.86603,0.5)\cr
+dir -90&(0,-1)\cr
+angle(1,1)&45\cr
+angle(1,2)&63.43495\cr
+angle(1,-2)&-63.43495\cr
+sind 63.43495 / cosd 63.43495&1.99997\cr
+angle up&90\cr
+angle left&180\cr
+angle(-1000,-epsilon)&-180\cr
+angle dir 60&60.00008\cr
+angle(0,0)&0\werror\cr
+\enddemo
+Plain \MF\ defines `dir$\,x$' to be the pair of values $(\mathop{\rm cosd}x,
+\mathop{\rm sind}x)$; this is a vector, which points $x$~degrees above the
+rightward horizon. Conversely, the `angle' operator determines the angle
+that corresponds to a given vector.
+
+\ddanger Logarithms and exponentials are computed with respect to an
+unusual base, designed to enhance the accuracy of calculations
+involving fixed-radix numbers in \MF's range. The values ^{mlog}$\,x=256\ln x$
+and ^{mexp}$\,x=e^{x/256}$ produce reasonably good results when
+$x\mathbin{\ast\ast}y$ is computed by the formula mexp$(y\ast\mathop{\rm
+mlog} x)$.
+\begindemo
+\demohead
+mlog 2&177.44568\cr
+mexp mlog 2&2\cr
+mexp 8 mlog 2&256\cr
+mexp 256&2.71828\cr
+mlog 2.71828&255.99954\cr
+mlog 2.71829&256.00098\cr
+%mlog mexp 2&1.99998\cr
+15 mlog 2&2661.68518\cr
+mexp 2661.68518&32767.99998\cr
+mexp 2661.68519&32767.99998\werror\cr
+mexp-2661.68519&0.00003\cr
+\enddemo
+
+\danger \MF\ also generates two flavors of random numbers. It is very
+unlikely that you will get the particular values shown in the following
+examples, when you do the experiment yourself, because the results come
+out different each time the computer is asked for a new random number
+(unless you have specified a ``seed value'' as explained in Chapter~21).
+\begindemo
+\it\kern-2pt You type&\it\kern-1pt And the result might be\cr
+\noalign{\vskip2pt}
+uniformdeviate 100&47.4241\cr
+uniformdeviate 100&97.28148\cr
+uniformdeviate -100&-36.16279\cr
+(normaldeviate,normaldeviate)&(0.46236,-1.87648)\cr
+\enddemo
+The value of `uniformdeviate\thinspace100' is a random number between 0 and~100;
+^^{uniformdeviate} ^^{normaldeviate}
+the value of `normaldeviate' is a normally distributed random number whose
+mean value is zero and whose standard deviation is unity. Chapter~21 explains
+what this means and gives several applications.
+
+\danger Besides all of these operations on numbers, \MF\ has a rich collection
+^^{scaled} ^^{xscaled} ^^{yscaled} ^^{dir}
+of operations on pairs, some of which are indicated in the following examples:
+\begindemo
+\demohead
+right&(1,0)\cr
+(1,2)+(3,4)&(4,6)\cr
+1/3(3,10)&(1,3.33333)\cr
+z2-z1&(-x1+x2,-y1+y2)\cr
+.2[z1,z2]&(0.8x1+0.2x2,0.8y1+0.2y2)\cr
+3z&(3x,3y)\cr
+z scaled 3&(3x,3y)\cr
+z xscaled 2 yscaled 1/2&(2x,0.5y)\cr
+z shifted (2,3)&(x+2,y+3)\cr
+z shifted 3right&(x+3,y)\cr
+z slanted 1/6&(x+0.16667y,y)\cr
+z rotated 90&(-y,x)\cr
+z rotated 30&(-0.5y+0.86603x,0.86603y+0.5x)\cr
+xpart(z rotated 30)&-0.5y+0.86603x\cr
+ypart(z rotated 30)&0.86603y+0.5x\cr
+(1,2)*(3,4)&(3,4)\werror\cr
+(1,2)zscaled(3,4)&(-5,10)\cr
+(a,b)zscaled(3,4)&(3a-4b,4a+3b)\cr
+(a,b)zscaled dir 30&(0.86603a-0.5b,0.5a+0.86603b)\cr
+(1,2)dotprod(3,4)&11\cr
+(a,b)dotprod(3,4)&3a+4b\cr
+dir 21 dotprod dir 51&0.86603\cr
+(3,4)dotprod((30,40)rotated 90)&0\cr
+\enddemo
+(Recall that plain \MF\ converts `|z$|' into `|(x$,y$)|' when |$| is any
+\<suffix>.) \ ^^{xpart} ^^{ypart} ^^{shifted} ^^"right" ^^{slanted}
+^^{zscaled} ^^{dotprod} ^^"z" The operations exhibited here are almost
+all self-evident. When a point or vector is ^{rotated}, it is moved
+counterclockwise about $(0,0)$ through a given number
+of degrees. \MF\ computes the rotated coordinates by using
+^{sines} and ^{cosines} in an appropriate way; you don't have to
+remember the formulas! Although you cannot use `|*|' to multiply
+a pair by a pair, you can use `^{zscaled}' to get the effect of
+^{complex number} multiplication: Since $(1+2i)$ times $(3+4i)$ is
+$-5+10i$, we have $(1,2)\mathbin{\rm zscaled}(3,4)=(-5,10)$.
+There's also a ^{multiplication} that converts pairs into numbers:
+$(a,b)\mathbin{\rm dotprod}(c,d\mkern1mu)=ac+bd$. This is the
+``^{dot product},'' often written `$(a,b)\cdot(c,d\mkern1mu)$' in
+mathematics texts; it turns out to be equal to $a\pyth+b$ times
+$c\pyth+d$ times the cosine of the angle between the vectors $(a,b)$ and
+$(c,d)$. Since cosd$\,90^\circ=0$, two vectors are
+^{perpendicular} to each other if and only if their dot ^{product} is zero.
+
+\danger There are operations on strings, paths, and the other types too;
+we shall study such things carefully in later chapters. For now, it will
+suffice to give a few examples, keeping in mind that the file |expr.mf|
+defines |s| with any subscript to be a ^{string}, while |p| with any subscript
+is a path. Furthermore $s_1$ has been given the value |"abra"|, while
+$p_1$ is `$(0,0)\to(3,3)$' and $p_2$ is `$(0,0)\to(3,3)\to\cycle$'.
+\begindemo
+\demohead
+s2&unknown string s2\cr
+s1\&"cad"\&s1&"abracadabra"\cr
+length s1&4\cr
+length p1&1\cr
+length p2&2\cr
+cycle p1&false\cr
+cycle p2&true\cr
+substring (0,2) of s1&"ab"\cr
+substring (2,infinity) of s1&"ra"\cr
+point 0 of p1&(0,0)\cr
+point 1 of p1&(3,3)\cr
+point .5 of p1&(1.5,1.5)\cr
+point infinity of p1&(3,3)\cr
+point .5 of p2&(3,0)\cr
+point 1.5 of p2&(0,3)\cr
+point 2 of p2&(0,0)\cr
+point 2+epsilon of p2&(0.00009,-0.00009)\cr
+point -epsilon of p2&(-0.00009,0.00009)\cr
+point -1 of p1&(0,0)\cr
+direction 0 of p1&(1,1)\cr
+direction 0 of p2&(4,-4)\cr
+direction 1 of p2&(-4,4)\cr
+\enddemo
+^^{point} ^^{direction}
+The ^{length} of a path is the number of `$\to$' steps that it contains;
+the construction `^|cycle|~\<path>' can be used to tell whether or not a
+particular path is cyclic. If you say just `|p1|' you get to see
+path~$p_1$ with its ^{control points}:
+\begintt
+(0,0)..controls (1,1) and (2,2)
+ ..(3,3)
+\endtt
+Similarly, `|p2|' is
+\begintt
+(0,0)..controls (2,-2) and (5,1)
+ ..(3,3)..controls (1,5) and (-2,2)
+ ..cycle
+\endtt
+and `|subpath| |(0,1)| |of| |p2|' is analogous to a ^{substring}:^^{subpath}
+\begintt
+(0,0)..controls (2,-2) and (5,1)
+ ..(3,3)
+\endtt
+The expression `point $t$ of $p_2$' gives the position of a point that
+moves along path~$p_2$, starting with the initial point $(0,0)$ at $t=0$,
+then reaching point $(3,3)$ at $t=1$, etc.;
+ the value at $t=1/2$ is the
+third-order midpoint obtained by the construction of Chapter~3, using
+intermediate control points $(2,-2)$ and $(5,1)$.
+Since $p_2$ is a cyclic path of length~2,
+point $(t+2)$ of~$p_2$ is the same as point~$t$. Path $p_1$ is not
+cyclic, so its points turn out to be identical to point~0 when $t<0$,
+and identical to point~1 when $t>1$. The expression `direction~$t$
+of~\<path>' is similar to `point~$t$ of \<path>'; it yields a vector for the
+direction of travel at time~$t$.
+
+{\ninepoint
+\medbreak
+\parshape 14 3pc 12pc 3pc 12pc
+0pc 15pc 0pc 15pc 0pc 15pc 0pc 15pc 0pc 15pc 0pc 15pc
+0pc 15pc 0pc 15pc 0pc 15pc 0pc 15pc 0pc 15pc 0pc 29pc
+\noindent
+\hbox to0pt{\hskip-3pc\dbend\hfill}%
+\rightfig 8a (12pc x 12pc) ^16pt
+Paths are not necessarily traversed at constant speed. For example,
+the diagram at the right shows point $t$ of~$p_2$ at twenty equally
+spaced values of~$t$.
+\MF\ moves faster in this case at time~1.0 than at time 1.2; but the
+points are spread out fairly well, so the concept of fractional
+time can be useful. The diagram shows, incidentally, that
+path~$p_2$ is not an especially good approximation to
+a circle; there is no left-right symmetry, although the curve from point~1
+to point~2 is a mirror image of the curve from point~0 to point~1.
+This lack of circularity is not surprising, since
+$p_2$ was defined by simply specifying two points, $(0,0)$ and~$(3,3)$;
+at least four points are needed to get a path that is convincingly round.
+\parfillskip=0pt\par}
+
+\ddanger The ^{ampersand} operation `|&|' can be used to splice paths
+together in much the same way as it concatenates strings. For example, if
+you type `|p2|~|&|~|p1|', you get the path of length~3 that is obtained by
+breaking the cyclic connection at the end of path~$p_2$ and attaching~$p_1$:
+\begintt
+(0,0)..controls (2,-2) and (5,1)
+ ..(3,3)..controls (1,5) and (-2,2)
+ ..(0,0)..controls (1,1) and (2,2)
+ ..(3,3)
+\endtt
+Concatenated paths must have identical endpoints at the junction.
+
+\ddanger You can even ``slow down the clock'' by concatenating subpaths
+that have non-integer time specifications. For example, here's what you
+get if you ask for `|subpath|~|(0,.5)| |of|~|p2| |&| |subpath| |(.5,2)|
+|of|~|p2| |&| |cycle|':
+\begintt
+(0,0)..controls (1,-1) and (2.25,-0.75)
+ ..(3,0)..controls (3.75,0.75) and (4,2)
+ ..(3,3)..controls (1,5) and (-2,2)
+ ..cycle
+\endtt
+When $t$ goes from 0 to 1 in subpath $(0,.5)$ of $p_2$, you get the same
+points as when $t$ goes from 0 to~.5 in $p_2$; when $t$ goes from 0 to 1
+in subpath $(.5,2)$ of~$p_2$, you get the same points as when $t$ goes
+from .5 to~1 in~$p_2$; but when $t$ goes from 1 to~2 in subpath
+$(.5,2)$ of~$p_2$, it's the same as the segment from 1 to~2 in~$p_2$.
+
+\danger Let's conclude this chapter by discussing the exact rules of
+^{precedence} by which \MF\ decides what operations to do first. The
+informal notion of ``magnetism'' gives a good intuitive picture of what
+happens, but syntax rules express things unambiguously in borderline cases.
+
+\danger The four levels of precedence correspond to four kinds of formulas,
+which are called primaries, secondaries, tertiaries, and
+expressions. A {\sl^{primary}\/} is a~variable or a constant or a
+tightly bound unit like `|2x|' or `|sqrt 2|'; a {\sl^{secondary}\/}
+is~a primary or a sequence of primaries connected by multiplicative
+operators like `|*|' or `|scaled|'; a {\sl^{tertiary}\/} is a secondary
+or a sequence of secondaries connected by additive operators like `|+|'
+or `|++|'; an {\sl^{expression}\/} is a tertiary or a sequence of
+tertiaries connected by external operators like `|<|' or `|..|'. For example,
+the expression
+\begintt
+a+b/2>3c*sqrt4d
+\endtt
+is composed of the primaries `|a|', `|b|', `|2|', `|3c|', and `|sqrt4d|';
+the last of these is a primary containing `|4d|' as a primary within itself.
+The subformulas `|a|', `|b/2|', and `|3c*sqrt4d|' are secondaries; the
+subformulas `|a+b/2|' and `|3c*sqrt4d|' are tertiaries.
+
+\danger If an expression is enclosed in parentheses, it becomes a primary
+that can be used to build up larger secondaries, tertiaries, etc.
+
+\danger The full syntax for expressions is quite long, but most of it
+falls into a simple pattern. If $\alpha$, $\beta$, and~$\gamma$ are
+any ``types''---numeric, boolean, string, etc.---then \<$\alpha$ variable>
+refers to a variable of type~$\alpha$, \<$\beta$ primary> refers to a
+primary of type~$\beta$, and so on. Almost all of the syntax rules fit into
+the following general framework:
+\beginsyntax
+<$\alpha$ primary>\is<$\alpha$ variable>\alt<$\alpha$ constant>%
+ \alt[(]<$\alpha$ expression>[)]
+ \alt<operator that takes type $\beta$ to type $\alpha$><$\beta$ primary>
+<$\alpha$ secondary>\is\<$\alpha$ primary>
+ \alt<$\beta$ secondary><multiplicative op taking types $\beta$ and %
+ $\gamma$ to $\alpha$><$\gamma$ primary>\kern-1pt
+<$\alpha$ tertiary>\is\<$\alpha$ secondary>
+ \alt<$\beta$ tertiary><additive op taking types $\beta$ and %
+ $\gamma$ to $\alpha$><$\gamma$ secondary>
+<$\alpha$ expression>\is<$\alpha$ tertiary>
+ \alt<$\beta$ expression><external op taking types $\beta$ and %
+ $\gamma$ to $\alpha$><$\gamma$ tertiary>
+\endsyntax
+These schematic rules don't give the whole story, but they do give the
+general structure of the plot.
+
+\danger Chapter 25 spells out all of the syntax rules for all types of
+expressions. We shall consider only a portion of the numeric and pair
+cases here, in order to have a foretaste of the complete menu:
+\def\\#1{\thinspace{\tt#1}\thinspace}
+\beginsyntax
+<numeric primary>\is<numeric atom>
+ \alt<numeric atom>[\char'133]<numeric expression>%
+ [,]<numeric expression>[\char'135]
+ \alt[length]<string primary>
+ \alt[length]<path primary>
+ \alt[length]<pair primary>
+ \alt[angle]<pair primary>
+ \alt[xpart]<pair primary>
+ \alt[ypart]<pair primary>
+ \alt<numeric operator><numeric primary>
+<numeric atom>\is<numeric variable>
+ \alt<numeric token primary>
+ \alt[(]<numeric expression>[)]
+ \alt[normaldeviate]
+<numeric token primary>\is<numeric token>[/]<numeric token>
+ \alt<numeric token not followed by %
+ `{\tt/}$\thinspace\langle$numeric token$\rangle$'\thinspace>
+<numeric operator>\is[sqrt]\alt[sind]\alt[cosd]\alt[mlog]\alt[mexp]
+ \alt[floor]\alt[uniformdeviate]\alt<scalar multiplication operator>
+<scalar multiplication operator>\is<plus or minus>
+ \alt<numeric token primary not followed by %
+ {\tt+} or {\tt-} or a numeric token>
+<numeric secondary>\is<numeric primary>
+ \alt<numeric secondary><times or over><numeric primary>
+<times or over>\is[*]\alt[/]
+<numeric tertiary>\is<numeric secondary>
+ \alt<numeric tertiary><plus or minus><numeric secondary>
+ \alt<numeric tertiary><Pythagorean plus or minus><numeric secondary>
+<plus or minus>\is[+]\alt[-]
+<Pythagorean plus or minus>\is[++]\alt[+-+]
+<numeric expression>\is<numeric tertiary>
+\endsyntax
+All of the finicky details about ^{fractions} and such things are made
+explicit by this syntax. For example, we can use the rules to deduce that
+`|sind-1/3x-2|' is interpreted as `|(sind(-(1/3x)))-2|'; notice that the
+first minus sign in this formula is considered to be a ``scalar multiplication
+operator,'' which comes in at the primary level, while the second one denotes
+subtraction and enters in the construction of \<numeric tertiary>. The
+^{mediation} or ``^{of-the-way}'' operation `$t[a,b]$' is handled at the
+primary level.
+
+\danger Several operations that haven't been discussed yet do not appear
+in the syntax above, but they fit into the same general pattern; for example,
+we will see later that `^|ASCII|\<string primary>' and `^|xxpart|\<transform
+primary>' are additional cases of the syntax for \<numeric primary>.
+On the other hand, several operations that we have discussed in this chapter
+do not appear in the syntax, because they are not primitives of \MF\ itself;
+they are defined in the plain \MF\ base (Appendix B\null). For example,
+`^|ceiling|' is analogous to `|floor|', and `^|**|' is analogous to~`|*|'.
+Chapter~20 explains how \MF\ allows extensions to its built-in syntax,
+so that additional operations can be added at will.
+
+\dangerexercise How does \MF\ interpret `|2|~|2|'\thinspace?
+\ (There's a space between the 2's.)
+\answer It's impossible to make an expression from `\<numeric token>
+\<numeric token>', because the rule for \<scalar multiplication operator>
+specifically prohibits this. \MF\ will recognize the first `|2|' as
+a \<numeric primary>, which is ultimately regarded as a \<numeric
+expression>; the other `|2|' will probably be an extra token that is
+flushed away after an error message has been given.
+
+\ddangerexercise According to |expr.mf|, the value of `|1/2/3/4|' is
+|0.66667|; the value of `|a/2/3/4|' is |0.375a|. Explain why.
+\answer If a numeric token is followed by `|/|\<numeric token>' but
+not preceded by `\<numeric token>|/|', the syntax allows it to become part of
+an expression only by using the first case of \<numeric token
+primary>. Therefore `|1/2/3/4|' must be treated as `|(1/2)/(3/4)|',
+and `|a/2/3/4|' must be treated as `|a/(2/3)/4|'.
+
+\danger The rules of \<pair expression> are similar to those for
+\<numeric expression>, so it's convenient to learn them both at the same time.
+\beginsyntax
+<pair primary>\is<pair variable>
+ \alt[(]<numeric expression>[,]<numeric expression>[)]
+ \alt[(]<pair expression>[)]
+ \alt<numeric atom>[\char'133]<pair expression>%
+ [,]<pair expression>[\char'135]
+ \alt[point]<numeric expression>[of]<path primary>
+ \alt<scalar multiplication operator><pair primary>
+<pair secondary>\is<pair primary>
+ \alt<pair secondary><times or over><numeric primary>
+ \alt<numeric secondary>[*]<pair primary>
+ \alt<pair secondary><transformer>
+<transformer>\is[rotated]<numeric primary>
+ \alt[scaled]<numeric primary>
+ \alt[shifted]<pair primary>
+ \alt[slanted]<numeric primary>
+ \alt[transformed]<transform primary>
+ \alt[xscaled]<numeric primary>
+ \alt[yscaled]<numeric primary>
+ \alt[zscaled]<pair primary>
+<pair tertiary>\is<pair secondary>
+ \alt<pair tertiary><plus or minus><pair secondary>
+<pair expression>\is<pair tertiary>
+\endsyntax
+
+\dangerexercise Try to guess the syntax rules for \<string primary>,
+\<string secondary>, $\langle$string tertiary$\rangle$, and \<string
+expression>, based solely on the examples that have appeared in this
+chapter. \ [{\sl Hint:}\/ The `|&|' operation has the same precedence
+as `|..|'.]
+\answer \<string primary>\is\<string variable>\parbreak
+\qquad\alt\<string token>\parbreak
+\def\\#1{\thinspace{\tt#1}\thinspace}%
+\qquad\alt\\(\<string expression>\\)\parbreak
+\qquad\alt\\{substring}\<pair expression>\\{of}\<string primary>\parbreak
+\<string secondary>\is\<string primary>\parbreak
+\<string tertiary>\is\<string secondary>\parbreak
+\<string expression>\is\<string tertiary>\parbreak
+\qquad\alt\<string expression>\\{\char`\&}\<string tertiary>\par
+\medskip\noindent
+(The full syntax in Chapter~25 includes several more varieties of
+\<string primary> that haven't been hinted at yet.)
+
+\endchapter
+
+A maiden was sitting there who was lovely as any picture,
+% ein bildsch\"one Jungfrau,
+nay, so beautiful that no words can express it.
+% nein so sch\"on, dass es nicht so sagen ist.
+\author JAKOB and WILHELM ^{GRIMM}, {\sl Fairy Tales\/} (1815)
+ % Kinder- und hausm\"archen, vol 2, #166; translated by Margaret Hunt
+ % in Strong Hans (Der starke Hans), about 4/5 of the way through
+ % This quote and the next were found by online computer search at SAIL
+ % in the files GRIMM[lib,doc] and WUTHER[lib,doc]
+
+\bigskip
+
+He looked astonished at the expression.
+ % my face assumed... middle of chapter 13
+\author EMILY ^{BRONT\"E}, {\sl Wuthering Heights\/} (1847)
+
+\eject
+ \beginchapter Chapter 9. Equations
+
+The variables in a \MF\ program receive their values by appearing in
+{\sl^{equations}}, which express relationships that the programmer
+wants to achieve. We've seen in the previous chapter that algebraic
+expressions provide a rich language for dealing with both numerical
+and graphical relationships. Thus it is possible to express a great
+variety of design objectives in precise form by stating that certain
+algebraic expressions should be equal to each other.
+
+The most important things a \MF\ programmer needs to know about
+equations are (1)~how to translate intuitive design concepts into
+formal equations, and (2)~how to translate formal equations into
+intuitive design concepts. In other words, it's important to be able
+to {\sl write\/} equations, and it's also important to be able to
+{\sl read\/} equations that you or somebody else has written. This
+is not nearly as difficult as it might seem at first. The best way
+to learn~(1) is to get a lot of practice with~(2) and to generalize
+from specific examples. Therefore we shall begin this chapter by
+translating a lot of equations into ``simple English.''
+
+\newdimen\longesteq
+\setbox0=\hbox{\indent$z_{12}-z_{11}=z_{14}-z_{13}$\quad}
+\longesteq=\wd0
+\def\\#1\\{\medbreak\noindent
+ \hbox to\longesteq{\indent#1\hfil}%
+ \hangindent\longesteq\ignorespaces}
+\medskip
+\noindent\hbox to\longesteq{\indent\kern-1pt\sl Equation\hfil}%
+\kern-1pt{\sl Translation}\smallskip
+
+\\$a=3.14$\\
+The value of $a$ should be 3.14.
+
+\\$3.14=a$\\
+The number 3.14 should be the value of $a$. \ (This means the same
+thing as `$a=3.14$'; the left and right sides of an equation can be
+interchanged without affecting the meaning of that equation in any way.)
+
+\\$"mode"="smoke"$\\
+The value of ^"mode" should be equal to the value of ^"smoke". \
+(Plain \MF\ assigns a special meaning to `"smoke"', so that if
+^@mode\_setup@ is invoked when $"mode"="smoke"$ the computer will
+prepare ``smoke proofs'' as explained in Chapter~5 and Appendix~H.)
+
+\\$y_3=0$\\
+The $y$ coordinate of point 3 should be zero; i.e., point~3 should
+be at the ^{baseline}. \ (Point~3 is also known as~$z_3$, which is an
+abbreviation for the pair of coordinates $(x_3,y_3)$, if you are
+using the conventions of plain \MF\!.)
+
+\\$x_9=0$\\
+The $x$ coordinate of point 9 should be zero; i.e., point~9 should
+be at the left edge of the type box that encloses the current character.
+
+\\$x_{1l}="curve\_sidebar"$\\
+The $x$ coordinate of point $1l$ should be equal to the value of the
+variable called "curve\_sidebar". This puts $z_{1l}$ a certain
+distance from the left edge~of the type.
+
+\\$x_1=x_2$\\
+Points 1 and 2 should have the same $x$ coordinate; i.e., they should
+have the same horizontal position, so that one will lie directly
+above or below the other.
+
+\\$y_4=y_5+1$\\
+Point 4 should be one pixel higher than point~5.
+\ (However, points 4 and~5 might be far apart; this equation
+says nothing about the relation between $x_4$ and~$x_5$.)
+
+\\$y_6=y_7+2"mm"$\\
+Point 6 should be two millimeters higher than point~7. \ (Plain \MF's
+^@mode\_setup@ routine sets variable ^"mm" to the number of pixels in a
+millimeter, based on the resolution determined by "mode" and "mag".)
+
+\\$x_4=w-.01"in"$\\
+Point 4 should be one-hundredth of an inch inside the right edge of
+the type. \ (Plain \MF's ^@beginchar@ routine sets variable~^"w" equal
+to the width of whatever character is currently being drawn, expressed in
+pixels.)
+
+\\$y_4=.5h$\\
+Point 4 should be halfway between the baseline and the top of the type.
+\ (Plain \MF's @beginchar@ sets ^"h" to the height of the
+current character, in pixels.)
+
+\\$y_6=-d$\\
+Point 6 should be below the baseline, at the bottom edge of the type.
+\ (Each character has a ``^{bounding box}'' that runs from $(0,h)$
+at the upper left and $(w,h)$ at the upper right to $(0,-d)$ and~$(w,-d)$
+at the lower left and lower right; variable~^"d" represents the depth of
+the type. The values of $w$, $h$, and~$d$ might change from character to
+character, since the individual pieces of type in a computer-produced
+font need not have the same size.)
+
+\\$y_8=.5[h,-d]$\\
+Point 8 should be halfway between the top and bottom edges of the type.
+
+\\$w-x_5={2\over3}x_6$\\
+The distance from point 5 to the right edge of the type should be
+two-thirds of the distance from point~6 to the left edge of the~type.
+\ (Since $w$ is at the right edge, $w-x_5$ is the ^{distance} from
+point~5 to the right edge.)
+
+\\$z_0=(0,0)$\\
+Point 0 should be at the ^{reference point} of the current character,
+i.e., it should be on the baseline at the left edge of the type.
+This equation is an abbreviation for two equations, `$x_0=0$' and `$y_0=0$',
+because an equation between pairs of coordinates implies that the $x$
+and~$y$ coordinates must both agree. \ (Incidentally, plain \MF\
+defines a variable called ^"origin" whose value is $(0,0)$; hence
+this equation could also have been written `$z_0="origin"$'.)
+
+\\$z_9=(w,h)$\\
+Point 9 should be at the upper right corner of the current character's
+bounding box.
+
+\\$"top"\,z_8=(.5w,h)$\\
+If the pen that has currently been ``picked up'' is placed at point~8,
+its top edge should be at the top edge of the type. Furthermore,
+$x_8$~should be $.5w$; i.e., point~8 should be centered between the
+left and right edges of the type. \ (Chapter~4 contains further
+examples of `^"top"', as well as the corresponding operations
+`"bot"', `"lft"', and `"rt"'.)
+
+\\$z_4={3\over7}[z_5,z_6]$\\
+Point 4 should be three-sevenths of the way from point~5 to point~6.
+
+\\$z_{12}-z_{11}=z_{14}-z_{13}$\\
+The ^{vector} that moves from point 11 to point~12 should be the same
+as the vector that moves from point~13 to point~14. In other words,
+point~12 should have the same direction and distance from point~11
+as point~14 has from point~13.
+
+\\\smash{\vtop{\hbox{$z_3-z_2=$}
+ \hbox{\quad$(z_4\!-\!z_2)$\thinspace rotated\thinspace 15}}}\\
+Points 3 and 4 should be at the same distance from point~2, but
+the direction to point~3 should be 15~degrees counterclockwise from
+the direction to point~4.
+
+\exercise Translate the following equations into ``simple English'':
+\ (a)~$x_7-9=x_1$; \ (b)~$z_7=(x_4,.5[y_4,y_5])$; \
+(c)~$"lft"\,z_{21}="rt"\,z_{20}+1$.
+\answer (a)~Point 1 should lie nine pixels to the left of point~7,
+considering horizontal positions only; no information is given about the
+vertical positions $y_1$ or $y_7$. \ (b)~Point~7 should sit directly
+above or below point~4, and its distance up from the baseline should be
+halfway between that of points 4 and~5. \ (c)~The left edge of the
+currently-picked-up pen, when that pen is centered at point~21, should be
+one pixel to the right of its right edge when at point~20. \ (Thus there
+should be one clear pixel of white space between the images of the
+pen at points 20 and~21.)
+
+\exercise Now see if your knowledge of equation reading gives you the
+ability to write equations that correspond to the following objectives:
+\ (a)~Point~13 should be just as far below the baseline as point~11 is
+above the baseline. \ (b)~Point~10 should be one millimeter to the right
+of, and one pixel below, point~12. \ (c)~Point~43 should be one-third of
+the way from the top left corner of the type to the bottom right corner
+of the type.
+\answer (a) $y_{13}=-y_{11}$ (or $-y_{13}=y_{11}$, or $y_{13}+y_{11}=0$).
+\ (b)~$z_{10}=z_{12}+("mm",-1)$. \ (c)~$z_{43}={1\over3}[(0,h),(w,-d)]$.
+
+Let's return now to the six example points $(z_1,z_2,z_3,z_4,z_5,z_6)$
+that were used so often in Chapters 2 and~3. Changing the notation
+slightly, we might say that the points are
+\begindisplay
+$(x_1,y_1)=(0,h)$;&$(x_2,y_2)=(.5w,h)$;&$(x_3,y_3)=(w,h)$;\cr
+$(x_4,y_4)=(0,0)$;&$(x_5,y_5)=(.5w,0)$;&$(x_6,y_6)=(w,0)$.\cr
+\enddisplay
+There are many ways to specify these points by writing a series of
+equations. For example, the six equations just given would do fine;
+or the short names $z_1$ through~$z_6$ could be used instead of the
+long names $(x_1,y_1)$ through~$(x_6,y_6)$. But there are several
+other ways to specify those points and at the same time to ``explain''
+the relations they have to each other. One way is to define the
+$x$ and~$y$ coordinates separately:
+\begindisplay
+$x_1=x_4=0;\qquad x_2=x_5=.5w;\qquad x_3=x_6=w;$\cr
+$y_1=y_2=y_3=h;\qquad y_4=y_5=y_6=0$.\cr
+\enddisplay
+\MF\ allows you to state several equations at once, by using more than
+^^{=} one equality sign; for example, `$y_1=y_2=y_3=h$' stands for three
+equations, `$y_1=y_2$', `$y_2=y_3$', and `$y_3=h$'.
+
+In order to define the coordinates of six points, it's necessary to
+write twelve equations, because each equation contributes to the
+definition of one value, and because six points have twelve coordinates
+in all. However, an equation between pairs of coordinates counts as
+two equations between single numbers; that's why we were able to get by
+with only six `$=$'~signs in the first set of equations, while twelve
+were used in the second.
+
+Let's look at yet another way to specify those six points, by giving
+equations for their positions relative to each other:
+\begindisplay
+$z_1-z_4=z_2-z_5=z_3-z_6$\cr
+$z_2-z_1=z_3-z_2=z_5-z_4=z_6-z_5$\cr
+$z_4="origin"$; \ $z_3=(w,h)$.\cr
+\enddisplay
+^^"origin" First we say that the vectors from $z_4$ to~$z_1$,
+from $z_5$ to~$z_2$, and from $z_6$ to~$z_3$, are equal to each other;
+then we say the same thing for the vectors from $z_1$ to~$z_2$,
+$z_2$ to~$z_3$, $z_4$ to~$z_5$, and $z_5$ to~$z_6$. Finally the
+corner points $z_4$ and $z_3$ are given explicitly. That's a total
+of seven equations between pairs of coordinates, so it should be
+more than enough to define the six points of interest.
+
+However, it turns out that those seven equations are not enough!
+For example, the six points
+\begindisplay
+$z_1=z_4=(0,0)$; \ $z_2=z_5=(.5w,.5h)$; \ $z_3=z_6=(w,h)$
+\enddisplay
+also satisfy the same equations. A closer look explains why:
+The two formulas
+\begindisplay
+$z_1-z_4=z_2-z_5$\qquad and\qquad $z_2-z_1=z_5-z_4$
+\enddisplay
+actually say exactly the same thing. \ (Add $z_5-z_1$ to both sides
+of the first equation and you get `$z_5-z_4=z_2-z_1$'.) \ Similarly,
+$z_2-z_5=z_3-z_6$ is the same as $z_3-z_2=z_6-z_5$. Two of the
+seven equations give no new information, so we really have specified
+only five equations; that isn't enough. An additional relation
+such as `$z_1=(0,h)$' is needed to make the solution unique.
+
+\dangerexercise (For mathematicians.) \ Find a solution to the seven
+equations such that $z_1=z_2$. Also find another solution in which
+$z_1=z_6$.
+\answer (a) $z_1=z_2=z_3=(w,h)$; $z_4=z_5=z_6=(0,0)$.
+\ (b)~$z_1=z_6=(.5w,.5h)$; $z_2=(.75w,.75h)$; $z_3=(w,h)$;
+$z_4=(0,0)$; $z_5=(.25w,.25h)$.
+
+At the beginning of a \MF\ program, variables have no values,
+except that plain \MF\ has assigned special values to variables
+like "smoke" and "origin". Furthermore, when you begin a new
+character with @beginchar@, any previous values that may have been
+assigned to $x$ or $y$ variables are obliterated and forgotten.
+Values are gradually established as the computer reads equations and
+tries to solve them, together with any other equations that have already
+appeared in the program.
+
+It takes ten equations to define the values of ten variables.
+If you have given only nine equations it may turn out that none of
+the ten variables has yet been determined; for example, the
+nine equations
+\begindisplay
+$g_0=g_1=g_2=g_3=g_4=g_5=g_6=g_7=g_8=g_9$
+\enddisplay
+don't tell us any of the $g$ values. However, the further equation
+\begindisplay
+$g_0+g_1=1$
+\enddisplay
+will cause \MF\ to deduce that all ten of the $g$'s are equal to $1\over2$.
+
+\MF\ always computes the values of as many variables as possible, based
+on the equations it has seen so far. For example, after the two equations
+\begindisplay
+$a+b+2c=3$;\cr
+$a-b-2c=1$\cr
+\enddisplay
+the machine will know that $a=2$ (because the sum of these two equations is
+`$2a=4$'); but all it will know about $b$ and~$c$ is that $b+2c=1$.
+
+At any point in a program a variable is said to be either ``^{known}''
+or ``^{unknown},'' depending on whether or not its value can be
+deduced uniquely from the equations that have been stated so far.
+The sample expressions in Chapter~8 indicate that \MF\ can compute a
+variety of things with unknown variables; but sometimes a quantity
+must be known before it can be used. For example, \MF\ can multiply
+an unknown numeric or pair variable by a known numeric value, but it
+cannot multiply two unknowns.
+
+Equations can be given in any order, except that you might sometimes
+need to put certain equations first in order to make critical
+values known in the others. For example, \MF\ will find the
+solution $(a,b,c)=(2,7,-3)$ to the equations `$a+b+2c=3$;
+$a-b-2c=1$; $b+c=4$' if you give those equations in any other order,
+like `$b+c=4$; $a-b-2c=1$; $a+b+2c=3$'. But if the equations had
+been `$a+b+2c=3$; $a-b-2c=1$; $a\ast(b+c)=8$', you would not have
+been able to give the last one first, because \MF\ would have refused
+to multiply the unknown quantity~$a$ by another unknown quantity $b+c$.
+Here are the main things that \MF\ can do with unknown quantities:
+\begindisplay
+$-\<unknown>$\cr
+$\<unknown>+\<unknown>$\cr
+$\<unknown>-\<unknown>$\cr
+$\<unknown>\ast\<known>$\cr
+$\<known>\ast\<unknown>$\cr
+$\<unknown>/\<known>$\cr
+$\<known>[\<unknown>,\<unknown>]$\cr
+$\<unknown>[\<known>,\<known>]$\cr
+\enddisplay
+Some of the operations of plain \MF\!, defined in Appendix~B\null, also work
+with unknown quantities. For example, it's possible to say
+^"top"\thinspace\<unknown>, ^"bot"\thinspace\<unknown>,
+^"lft"\thinspace\<unknown>, ^"rt"\thinspace\<unknown>, and even
+\begindisplay
+@penpos@\<suffix>(\<unknown>,\thinspace\<known>).
+\enddisplay
+
+\danger A \MF\ program can say `\<unknown>$[a,b\mkern1mu]$' when $a-b$ is
+known, and variable~$a$ can be compared to variable~$b$ in boolean
+expressions ^^{comparison} like `$a<b$' when $a-b$ is known. The quantity
+$a-b$ might be known even when $a$ and~$b$ aren't known by themselves.
+
+\danger You might wonder how \MF\ is able to keep its knowledge up-to-date,
+based on scraps of partial information that it receives from miscellaneous
+equations. The best way to understand this is to watch how it happens,
+by asking the computer to show certain calculations that it usually keeps
+to itself. Here's one way to do it: Run \MF\ and say
+\begintt
+\tracingequations:=tracingonline:=1;
+\endtt
+^^"tracingequations" ^^"tracingonline"
+in response to the opening `|**|'. \ (Be sure to type the backslash `|\|',
+and to use `|:=|' instead of `|=|'. We will see in Chapter~27 that \MF\
+can be asked to ``trace'' many aspects of what it's doing.) \ Now type
+\begintt
+a+b+2c=3;
+\endtt
+the machine will reply by saying
+\begintt
+## c=-0.5b-0.5a+1.5
+\endtt
+since that is how it has digested your equation. \ (The `|##|' in this
+^^{hash hash} line identifies diagnostic information that comes from
+"tracingequations".) \ Now type
+\begintt
+a-b-2c=1;
+\endtt
+\MF\ will read this as if you had said `|a-b-2(-0.5b-0.5a+1.5)=1|',
+since it has previously learned how to replace |c| by an expression
+that involves only |a| and~|b|. This new equation can be simplified by
+multiplying out the left-hand side and collecting terms. The result is
+`|2a-3=1|', hence \MF\ will respond with
+\begintt
+## a=2
+\endtt
+and it will be your turn to type something again. Say
+\begintt
+showdependencies;
+\endtt
+^^@showdependencies@ \MF's response will be
+\begintt
+c=-0.5b+0.5
+\endtt
+indicating that there is only one variable whose value depends on others,
+and that its equation of dependency is now `$c=-0.5b+0.5$'. \ (The previous
+dependency equation `$c=-0.5b-0.5a+1.5$' has
+been simplified to take account of the newly discovered value, $a=2$.) \
+Finally type
+\begintt
+b+c=4;
+\endtt
+this spurs the computer on to say
+\begintt
+## b=7
+#### c=-3
+\endtt
+A line that begins with `|##|' states what \MF\ has deduced from
+the equation it has just read; a line that begins with `|####|' states
+^^{hash hash hash hash} an indirect consequence of that direct result,
+if some previously dependent variable has now become known.
+
+\danger It's interesting to continue the computer experiment just begun
+by typing the following lines, one at a time, and watching what happens:
+\begintt
+a'+b'+.5c'=3;
+a'-b'-.5c'=1;
+g0=g1=g2=g3=g4;
+showdependencies;
+g0+g1=1;
+z1-z4=z2-z5=z3-z6;
+z2-z1=z3-z2=z5-z4=z6-z5;
+z4=origin;
+z3=(w,h);
+x1=0;
+y6=0;
+w=2h=100;
+end.
+\endtt
+Notice that on the sixth line (\thinspace`$z_1-z_4=\cdots\,$'\thinspace)
+\MF\ reports four equations, but on the next line
+(\thinspace`$z_2-z_1=\cdots\,$'\thinspace) it reports only two. This
+happens because most of that line is redundant, as we have already
+observed.
+
+\danger This computer session indicates that \MF\ deals with two kinds
+of unknown numeric variables: {\sl^{dependent}\/} variables and
+{\sl^{independent}} ones.
+Every variable is independent at the beginning of its life, but every
+equation causes one of the independent variables to become dependent
+or ^{known}. Each `|##|' line emitted by "tracingequations" shows a
+newly dependent-or-known variable, together with an equivalent expression
+that involves only independent variables. For example, the line
+`|##|~|c=-0.5b-0.5a+1.5|'
+means that variable~$c$ has just become dependent and that it equals
+$-{1\over2}b-{1\over2}a+1.5$, where variables $b$ and~$a$ are independent.
+Similarly, `|##|~|a=2|' means that $a$~has just changed from
+independent to known. When an independent variable~$v$ changes to dependent
+or known, the equivalents of all dependent variables are updated so that
+they no longer depend on~$v$; in this updating process some or all of them
+may change from dependent to known, whereupon a `|####|' line will be printed.
+
+\ddanger When \MF\ reads a numeric equation it replaces all known variables
+by their numeric values and all dependent variables by their equivalents.
+The resulting equation can be converted into the form
+\begindisplay
+$c_1v_1+\cdots+c_mv_m=\alpha$
+\enddisplay
+where the $c$'s are nonzero constants and the $v$'s are independent variables;
+$\alpha$~is a numeric constant that might be zero. If some $c_k$ is so
+small that it probably would have been zero in a calculation free of
+rounding errors, it is replaced by zero and the corresponding $v_k$ is
+removed from the equation. Now if $m=0$, the equation is considered to be
+either {\sl^{redundant}\/} (if $\alpha$ is zero or extremely small)
+or {\sl^{inconsistent}\/} (otherwise). But if $m>0$, \MF\ chooses an
+independent variable~$v_k$ for which $c_k$ is maximum, and rewrites
+the equation in the form
+\begindisplay
+{\tt\#\#} $v_k=(\alpha-c_1v_1-\cdots-c_{k-1}v_{k-1}-c_{k+1}v_{k+1}-
+ \cdots-c_mv_m)/c_k$.
+\enddisplay
+Variable $v_k$ becomes dependent (if $m>1$) or known (if $m=1$).
+
+\danger Inconsistent equations are equations that have no solutions.
+For example, if you say `$0=1$', \MF\ will issue an error message
+^^{off by x}
+saying that the equation is ``off by~1.'' A less blatant inconsistency
+arises if you say, e.g., `$a=b+1$; $b=c+1$; $c=a+1$'; this last equation
+is off by three, for the former equations imply that $c=b-1=a-2$.
+The computer will simply ignore an inconsistent equation when you
+resume processing after such an error.
+
+\danger Redundant equations are equations that say nothing new.
+For example, `$0=0$' is redundant, and so is `$a=b+c$' if you have
+previously said that $c=a-b$. \MF\ stops with an error message if
+you give it a redundant equation between two numeric expressions,
+because this usually indicates an oversight in the program. However,
+no error is reported when an equation between pairs leads to one or
+two redundant equations between numerics. For example, the equation
+`$z_3=(0,h)$' will not trigger an error message when the program
+has previously established that $x_3=0$ or that $y_3=h$ or both.
+
+\danger Sometimes you might have to work a little bit to put an equation
+into a form that \MF\ can handle. For example, you can't say
+\begindisplay
+$x/y=2$
+\enddisplay
+when $y$ is independent or dependent, because \MF\ allows ^{division}
+only by known quantities. The alternative
+\begindisplay
+$x=2y$
+\enddisplay
+says the same thing and causes the computer no difficulties;
+furthermore it is a correct equation even when $y=0$.
+
+\ddanger \MF's ability to remember previous equations is limited to
+``linear'' dependencies ^^{linear dependencies} as explained above.
+A mathematician might want to introduce the condition $x\ge0$ by giving an
+equation such as `$x=\mathop{\rm abs}x$'; but \MF\ is incapable
+of dealing with such a constraint. Similarly, \MF\ can't cope with
+an equation like `$x=\mathop{\rm floor}x$', which states that
+$x$~is an integer. Systems of equations that involve the ^{absolute
+value} and/or ^{floor} operation can be extremely difficult to solve,
+and \MF\ doesn't pretend to be a mathematical genius.
+
+\ddanger The rules given earlier explain how an independent variable
+can become dependent or known; conversely, it's possible for a
+dependent variable to become independent again, in unusual circumstances.
+For example, suppose that the equation $a+b+2c=3$ in our example above
+had been followed by the equation $d=b+c+a/4$. Then there would be
+two dependent variables,
+\begintt
+## c=-0.5b-0.5a+1.5
+## d=0.5b-0.25a+1.5
+\endtt
+Now suppose that the next statement is `|numeric|~|a|', meaning that the
+old value of variable~$a$ should be discarded. \MF\ can't simply delete
+an independent variable that has things depending on it, so it
+chooses a dependent variable to take $a$'s place; the computer prints out
+\begintt
+### 0.5a=-0.5b-c+1.5
+\endtt
+^^{hash hash hash} meaning that $0.5a$ will be replaced by $-c-{1\over2}b
++{3\over2}$ in all dependencies, before $a$ is discarded. Variable $c$ is
+now independent again; `^@showdependencies@' will reveal that the only
+dependent variable is now $d$, which equals $0.75b+0.5c+0.75$. \ (This
+is correct, for if the variable~$a$ is eliminated from the two given
+equations we obtain $4d=3b+2c+3$.) \ The variable chosen for independence
+is one that has the greatest coefficient of dependency with respect
+to the variable that will disappear.
+
+\danger A designer often wants to stipulate that a certain point lies on
+a certain line. ^^{line, point to be on} This can be done easily by
+using a special feature of plain \MF\ called `^"whatever"', which
+stands for an anonymous numeric variable that has a different unknown
+value each time you use it. For example,
+\begindisplay
+$z_1="whatever"[z_2,z_3]$
+\enddisplay
+states that point 1 appears somewhere on the straight line that passes
+through points 2 and~3. \ (The expression $t[z_2,z_3]$ represents that
+entire straight line, as $t$ runs through all values from $-\infty$ to
+$+\infty$. We want $z_1$ to be equal to $t[z_2,z_3]$ for some value of~$t$,
+but we don't care what value it is.) \ The expression `"whatever"$[z_2,z_3]$'
+is legal whenever the difference $z_2-z_3$ is known; it's usually used
+only when $z_2$ and $z_3$ are both known, i.e., when both points have been
+determined by prior equations.
+
+\danger Here are a few more examples of equations that involve
+`"whatever"', together with their translations into English. These
+equations are more fun than the ``tame'' ones we considered at the
+beginning of this chapter, because they show off more of the
+computer's amazing ability to deduce explicit values from implicit
+statements.
+
+\ninepoint % it's all dangerous from here on!
+\setbox0=\hbox{\indent$z_7-z_6="whatever"\ast(z_3-z_2)$\quad}
+\longesteq=\wd0
+\noindent\hbox to\longesteq{\indent\kern-1pt\sl Equation\hfil}%
+\kern-1pt{\sl Translation}\smallskip
+
+\\$z_5-z_4="whatever"\ast\mathop{\rm dir}30$\\
+The angle between points 4 and~5 will be $30^\circ$ above the horizon.
+\ (This equation can also be written `$z_4=z_5+"whatever"\ast\mathop{\rm
+dir}30$', which states that point~4 is obtained by starting at point~5
+and moving by some unspecified multiple of ^{dir}$\,30$.)
+
+\\$z_7-z_6="whatever"\ast(z_3-z_2)$\\
+The line from point~6 to point~7 should be ^{parallel} to the
+line from point~2 to point~3.
+
+\\$\penpos8("whatever",60)$\\
+The simulated pen angle at point~8 should be 60 degrees; the breadth
+of the pen is unspecified, so it will be determined by other equations.
+
+\dangerexercise If $z_1$, $z_2$, $z_3$, and $z_4$ are known points,
+how can you tell \MF\ to compute the point $z$ that lies on the
+^{intersection} of the lines $z_1\to z_2$ and $z_3\to z_4$?
+\answer $z="whatever"[z_1,z_2]$; $z="whatever"[z_3,z_4]$. \ (Incidentally,
+it's interesting to watch this computation in action. Run \MF\ with
+|\tracingequations:=|\allowbreak|tracingonline:=1| and say, for example,
+\begintt
+z=whatever[(1,5),(8,19)]; z=whatever[(0,17),(6,1)];
+\endtt
+the solution appears as if by magic.
+If you use |alpha| and |beta| in place of the whatevers, the machine will
+also calculate values for "alpha" and "beta".)
+
+\dangerexercise Given five points $z_1$, $z_2$, $z_3$, $z_4$, and $z_5$,
+explain how to compute $z$ on the line $z_1\to z_2$ such that the line
+$z\to z_3$ is parallel to the line $z_4\to z_5$.
+\answer $z="whatever"[z_1,z_2]$; $z-z_3="whatever"\ast(z_5-z_4)$.
+
+\dangerexercise What \MF\ equation says that the line between points
+11 and~12 is {\sl^{perpendicular}\/} to the line between points 13 and~14?
+\answer $z_{11}-z_{12}="whatever"\ast(z_{13}-z_{14})$ ^{rotated} 90,
+assuming that $z_{13}-z_{14}$ is known. \ (It's also possible to say
+`$(z_{11}-z_{12})\mathbin{\rm dotprod} (z_{13}-z_{14})=0$', ^^{dotprod}
+although this risks overflow if the coordinates are large.)
+
+\dangerexercise (For mathematicians.) \ Given three points $z_1$, $z_2$,
+and $z_3$, explain how to compute the distance from $z_1$ to the straight
+line through $z_2$ and $z_3$.
+\answer One solution constructs the point $z_4$ on $z_2\to z_3$ such
+that $z_4\to z_1$ is perpendicular to $z_2\to z_3$, using ideas like
+those in the previous two exercises: `$z_4="whatever"[z_2,z_3]$;
+$z_4-z_1="whatever"\ast(z_3-z_2)$ rotated 90'. Then the requested distance
+^^{abs} ^^{ypart}^^{angle}
+is ${\rm length}(z_4-z_1)$. But there's a slicker solution: Just calculate
+$$\hbox{abs ypart$((z_1-z_2)\mathbin{\rm rotated}-{\rm angle}(z_3-z_2))$.}$$
+
+\ddangerexercise (For mathematicians.) \ Given three points $z_1$,
+$z_2$, $z_3$, and a length~$l$, explain how to compute the two points
+on the line $z_2\to z_3$ that are at distance~$l$ from $z_1$. \ (Assume
+that $l$~is greater than the distance from $z_1$ to the line.)
+\answer It would be nice to say simply `$z="whatever"[z_2,z_3]$' and
+then to be able to say either `length$(z-z_1)=l$' or `$z-z_1=(l,0)$
+rotated "whatever"'; but neither of the second equations is legal. \
+(Indeed, there couldn't possibly be a legal solution that has this general
+flavor, because any such solution would determine a unique $z$, while
+there are two points to be determined.) \ The best way seems to be to
+compute $z_4$ as in the previous exercise, ^^{pythagorean subtraction} and
+then to let
+$v=(l\mathbin{+{-}+}\mathop{\rm length} (z_4-z_1))\ast\mathop{\rm
+unitvector}(z_3-z_2)$; ^^{unitvector} ^^{length}
+the desired points are then $z_4+v$ and $z_4-v$.
+
+\ddangerexercise The applications of "whatever" that we have seen so far
+have been in equations between {\sl pairs\/} of numeric values, not
+in equations between simple numerics. Explain why an equation like
+`$a+2b="whatever"$' would be useless.
+\answer Such an equation tells us nothing new about $a$ or $b$. Indeed,
+each use of "whatever" introduces a new independent variable, and
+each new independent variable ``uses up'' one equation, since we need
+$n$ equations to determine the values of $n$~unknowns. On the other hand
+an equation between pairs counts as two equations; so there's a net
+gain of one, when "whatever" appears in an equation between pairs.
+
+\danger All of the equations so far in this chapter have been between numeric
+expressions or pair expressions. But \MF\ actually allows equations
+between any of the eight types of quantities. For example, you can write
+\begintt
+s1="go"; s1&s1=s2
+\endtt
+if $s_1$ and $s_2$ are string variables; this makes $s_1=\null$|"go"|
+and $s_2=\null$|"gogo"|. Moreover, the subsequent equations
+\begintt
+s3=s4; s5=s6; s3=s5; s4=s1&"sh"
+\endtt
+will make it possible for the machine to deduce that $s_6=\null$|"gosh"|.
+
+\danger But nonnumeric equations are not as versatile as numeric
+ones, because \MF\ does not perform operations on unknown quantities
+^^{unknown quantities, nonnumeric}
+of other types. For example, the equation
+\begintt
+"h"&s7="heck"
+\endtt
+cannot be used to define $s_7=\null$|"eck"|, because the ^{concatenation}
+operator~|&| works only with strings that are already known.
+
+\ddanger After the declaration `|string| |s[]|' and the equations
+`|s1=s2=s3|', the statement `|show|~|s0|' will produce the result
+`|unknown| |string| |s0|'; but `|show|~|s1|' will produce `|unknown|
+|string| |s2|'. Similarly, `|show|~|s2|' and `|show|~|s3|' will produce
+`|unknown| |string| |s3|' and `|unknown| |string| |s1|', respectively. In
+general, when several nonnumeric variables have been equated, they will
+point to each other in some cyclic order.
+
+\endchapter
+
+Let ``X'' equal my father's signature.
+\author FRED ^{ALLEN}, {\sl Vogues\/} (1924) % NYT review of show, Mar 28'24
+ % quoted in Much Ado About Me, p288
+
+\bigskip
+
+ALL ANIMALS ARE EQUAL
+BUT SOME ANIMALS ARE MORE EQUAL THAN OTHERS
+\author GEORGE ^{ORWELL}, {\sl Animal Farm\/} (1945) % Chapter 10
+
+\eject
+ \beginchapter Chapter 10. Assignments
+
+Variables usually get values by appearing in equations, as described in
+the preceding chapter. But there's also another way, in which `^|:=|'
+is used instead of~`|=|'. For example, the |io.mf| program in Chapter~5
+said
+\begintt
+stem# := trial_stem * pt#
+\endtt
+when it wanted to define the value of |stem#|.
+
+The ^{colon-equal} operator `|:=|' means ``discard the previous value of
+the variable and assign a new one''; we call this an {\sl^{assignment}\/}
+operation. It was convenient for |io.mf| to define |stem#| with an
+assignment instead of an equation, because |stem#| was getting several
+different values within a single font. The alternative would have been to say
+\begintt
+numeric stem#; stem# = trial_stem * pt#
+\endtt
+(thereby specifically undefining the previous value of |stem#| before using
+it in an equation); this is more cumbersome.
+
+The variable at the left of `|:=|' might appear also in the expression on
+the right. For example,
+\begintt
+code := code + 1
+\endtt
+means ``increase the value of "code" by 1.'' This assignment would make no
+sense as an equation, since `$"code"="code"+1$' is inconsistent. The former
+value of "code" is still relevant on the right-hand side when `$"code"+1$'
+is evaluated in this example, because old values are not discarded until
+the last minute; they are retained until just before a new assignment is made.
+
+\dangerexercise Is it possible to achieve the effect of `$"code":="code"+1$'
+by using equations and @numeric@ declarations but not assignments?
+\answer Yes, but it must be done in two steps: `@numeric@ "newcode";
+$"newcode"="code"+1$; @numeric@ "code"; $"code"="newcode"$'.
+
+Assignments are permitted only when the quantity at the left of the `|:=|'
+is a variable. For example, you can't say `|code+1:=code|'. More
+significantly, things like `|(x,y):=(0,0)|' are not permitted, although
+you can say `|w:=(0,0)|' if~$w$~has been declared to be a variable of
+type @pair@. This means that a statement like `|z1:=z2|' is illegal, because
+it's an abbreviation for the inadmissible construction `|(x1,y1):=(x2,y2)|';
+we must remember that |z1| is not really a variable, it's a pair of variables.
+
+The restriction in the previous paragraph is not terribly significant, because
+assignments play a relatively minor r\^ole in \MF\ programs. The best
+programming strategy is usually to specify equations instead of
+assignments, because equations indicate the relationships between
+variables in a declarative ^^{declarative versus imperative} ^^{imperative
+versus declarative} manner. A person who makes too many assignments is
+still locked into the habits of old-style ``imperative'' programming
+languages in which it is necessary to tell the computer exactly how to do
+everything; \MF's equation
+mechanism liberates us from that more complicated style of programming,
+because it lets the computer take over the job of solving equations.
+
+The use of assignments often imposes a definite order on the statements of
+a program, because the value of a variable is different before and after
+an assignment takes place. Equations are simpler than assignments because
+they can usually be written down in any order that comes naturally to you.
+
+Assignments do have their uses; otherwise \MF\ wouldn't bother with
+`|:=|' at all. But experienced \MF\ programmers introduce assignments
+sparingly---only when there's a good reason for doing so---because
+equations are generally easier to write and more enlightening to read.
+
+\danger \MF's ^{internal quantities} like "tracingequations" always have
+known numeric values, so there's no way to change them except by giving
+assignments. The computer experiment in Chapter~9 began with
+\begintt
+\tracingequations:=tracingonline:=1;
+\endtt
+this illustrates the fact that multiple assignments are possible, just
+like multiple equations. Here is the complete syntax for equations
+and assignments:
+\beginsyntax
+<equation>\is<expression>[=]<right-hand side>
+<assignment>\is<variable>[:=]<right-hand side>
+<right-hand side>\is<expression>\alt<equation>\alt<assignment>
+\endsyntax
+Notice that the syntax permits mixtures like `$a+b=c:=d+e$'; this is
+the same as the assignment `$c:=d+e$' and the equation `$a+b=c$'.
+
+\ddanger In a mixed equation/assignment like `$a+b=b:=b+1$', the old
+value of~$b$ is used to evaluate the expressions. For example, if $b$ equals~3
+before that statement, the result will be the same as `$a+3=b:=3+1$';
+therefore $b$ will be set to~4 and $a$~will be set to~1.
+
+\dangerexercise Suppose that you want variable $x_3$ to become ``like new,''
+^^{variables, reinitializing} ^^{reinitializing} ^^{independent variables}
+completely independent of any value that it formerly had; but you don't
+want to destroy the values of~$x_1$ and~$x_2$. You can't say `^@numeric@
+$x[\,]$', because that would obliterate all the $x_k$'s. What can you do
+instead? \checkequals\xwhat\exno
+\answer The assignment `$x_3:=\null$^"whatever"' does exactly what you want.
+
+\ddangerexercise Apply \MF\ to the short program
+\begindisplay
+@string@ $s[\,]$; \ $s_1=s_2=s_3=s_4$; \ $s_5=s_6$; \ $s_2:=s_5$; \
+ @showvariable@ $s$;
+\enddisplay
+and explain the results you get.
+\answer The result shows that $s_1=s_3=s_4$ and $s_2=s_5=s_6$ now:
+\begintt
+s[]=unknown string
+s1=unknown string s3
+s2=unknown string s6
+s3=unknown string s4
+s4=unknown string s1
+s5=unknown string s2
+s6=unknown string s5
+\endtt
+(The assignment $s_2:=s_5$ broke $s_2$'s former relationship with $s_1$,
+$s_3$, and $s_4$.)
+
+\ddanger If other variables depend on $v$ when $v$ is assigned a new value,
+the other variables do not change to reflect the new assignment; they still
+act as if they depended on the previous (unknown) value of~$v$. For example,
+if the equations `$2u=3v=w$' are followed by the assignment `$w:=6$', the
+values of $u$ and~$v$ won't become known, but \MF\ will still remember the
+fact that $v=.66667u$. \ (This is not a new rule; it's a consequence of
+the rules already stated. When an independent variable is discarded, a
+dependent variable may become independent in its place, as described in
+Chapter~9.)
+
+\ddangerexercise Apply \MF\ to the program
+\begindisplay
+$"tracingequations":="tracingonline":=1$;\cr
+$a=1$; \ $a:=a+b$; \ $a:=a+b$; \ $a:=a+b$;\cr
+@show@ $a,b$;\cr
+\enddisplay
+and explain the results you get.
+\answer The results are
+\begindisplay
+|## a=1|\cr
+|## a=b+1|&(after the first assignment)\cr
+|## b=0.5a-0.5|&(after the second assignment)\cr
+|### -1.5a=-%CAPSULEnnnn-0.5|&(after the third assignment)\cr
+|## a=%CAPSULEnnnn|&(after the third, see below)\cr
+|>> a|&(after `@show@'; variable $a$ is independent)\cr
+|>> 0.33333a-0.33333|&(this is the final value of $b$)\cr
+\enddisplay
+^^|CAPSULE| Let $a_k$ denote the value of $a$ after $k$ assignments were made.
+Thus, $a_0=1$, and $a_1$ was dependent on the independent variable~$b$.
+Then $a_1$ was discarded and $b$ became dependent on the independent
+variable~$a_2$. The right-hand side of the third assignment was
+therefore $a_2+b$. At the time $a_2$ was about to be discarded, \MF\
+had two dependencies $b=0.5a_2-0.5$ and $\kappa=1.5a_2-0.5$, where
+$\kappa$ was a nameless ``^{capsule}'' inside of the computer, representing
+the new value to be assigned. Since $\kappa$ had a higher coefficient
+of dependency than~$b$, \MF\ chose to make $\kappa$ an independent variable,
+after which $-1.5a_2$ was replaced by $-\kappa-0.5$ in all dependencies; hence
+$b$ was equal to $0.33333\kappa-0.33333$. After the third
+assignment was finished, $\kappa$ disappeared and $a_3$ became independent
+in its place. \ (The line `|##| |a=%CAPSULEnnnn|' means that $a$~was
+temporarily dependent on $\kappa$, before $\kappa$ was discarded. If
+the equation $a=\kappa$ had happened to make $\kappa$ dependent on~$a$, rather
+than vice versa, no ^^{hash hash} `|##|' line would have been printed;
+such lines are omitted when a capsule or part of a capsule has been made
+dependent, unless you have made ^"tracingcapsules"$\null>0$.)
+
+\endchapter
+
+At first his assignment had pleased,
+but as hour after hour passed
+with growing weariness,
+he chafed more and more.
+\author C. E. ^{MULFORD}, {\sl Hopalong Cassidy\/} (1910) % Chap 17 p154
+
+\bigskip
+
+\<left part> ::= \<variable> :=
+\<left part list> ::= \<left part> $\vert$ \<left part list>\<left part>
+\<assignment statement> ::= \<left part list>\<arithmetic expression> $\vert$
+\<left part list>\<Boolean expression>
+\author PETER ^{NAUR} et al., {\sl Report %
+ on the Algorithmic language ALGOL 60\/} (1960) % section 4.2.1
+
+\eject
+ \beginchapter Chapter 11. Magnification\\and\\Resolution
+
+A single \MF\ program can produce fonts of type for many different kinds
+of printing equipment, if the programmer has set things up so that the
+^{resolution} can be varied. The ``plain \MF\thinspace'' base file described
+in Appendix~B establishes a set of conventions that make such variability
+quite simple; the purpose of the present chapter is to explain those
+conventions.
+
+For concreteness let's assume that our computer has two output devices.
+One of them, called ^"cheapo", has a resolution of 200 pixels per
+inch (approximately 8 per millimeter); the other, called ^"luxo",
+has a resolution of 2000 pixels per inch. We would like to write \MF\
+programs that are able to produce fonts for both devices. For example,
+if the file |newface.mf| contains a program for a new typeface, we'd
+like to generate a low-resolution font by invoking \MF\ with
+\begintt
+\mode=cheapo; input newface
+\endtt
+and the same file should also produce a high-resolution font if we start with
+\begintt
+\mode=luxo; input newface
+\endtt
+instead. Other people with different printing equipment should also be
+able to use |newface.mf| with their own favorite ^"mode" values.
+
+The way to do this with plain \MF\ is to call ^@mode\_setup@ near the
+beginning of |newface.mf|; this routine establishes the values of
+variables like ^"pt" and ^"mm", which represent the respective numbers of
+pixels in a point and a millimeter. For example, when $"mode"= "cheapo"$,
+the values will be $"pt"=2.7674$ and $"mm"=7.87402$; when $"mode"="luxo"$,
+they will be $"pt"=27.674$ and $"mm"=78.74017$. The |newface.mf| program
+should be written in terms of such variables, so that the pixel patterns
+for characters will be about 10~times narrower and 10~times shorter in
+"cheapo" mode than they are in "luxo" mode. For example, a line that's
+drawn from $(0,0)$ to $(3"mm",0)$ will produce a line that's about 23.6
+pixels long in "cheapo" mode, and about 236.2 pixels long in "luxo" mode;
+the former line will appear to be 3\thinspace mm long when printed by
+"cheapo", while the latter will look 3\thinspace mm long when printed by
+"luxo".
+
+A further complication occurs when a typeface is being ^{magnified}; in such
+cases the font does not correspond to its normal size. For example, we might
+want to have a set of fonts for "cheapo" that are twice as big as usual,
+so that users can make transparencies for overhead projectors. \ (Such
+output could also be reduced to 50\% of its size as printed,
+on suitable reproduction equipment, thereby increasing the effective
+resolution from 200 to 400.) \ \TeX\ allows entire jobs to be magnified
+by a factor of~2 if the user says `|\magnification=2000|'; individual
+fonts can also be magnified in a \TeX\ job by saying, e.g., ^^{TeX}
+`|\font\f=newface| |scaled| |2000|'. The standard way to produce a font
+with two-fold magnification using the conventions of plain \MF\ is to say, e.g.,
+\begintt
+\mode=cheapo; mag=2; input newface;
+\endtt
+this will make $"pt"=5.5348$ and $"mm"=15.74803$.
+
+The @mode\_setup@ routine looks to see if ^"mag" has a known value;
+if not, it sets $"mag"=1$. Similarly, if "mode" is unknown,
+^^"proof" @mode\_setup@ sets $"mode"="proof"$.
+
+Plain \MF\ also computes the values of several other dimension-oriented
+values in addition to "pt" and "mm", corresponding to the dimensions
+that are understood by \TeX. Here is the complete list:
+\begindisplay \openup 1pt
+"pt"&printer's point&($\rm72.27\,pt=1\,in$)\cr
+^"pc"&pica&($\rm1\,pc=12\,pt$)\cr
+^"in"&inch&($\rm1\,in=2.54\,cm$)\cr
+^"bp"&big point&($\rm72\,bp=1\,in$)\cr
+^"cm"&centimeter&($\rm100\,cm=1\,meter$)\cr
+"mm"&millimeter&($\rm10\,mm=1\,cm$)\cr
+^"dd"&didot point&($\rm1157\,dd=1238\,pt$)\cr
+^"cc"&cicero&($\rm1\,cc=12\,dd$)\cr
+\enddisplay
+In each case the values are rounded to the nearest $1\over65536$th of a pixel.
+
+Although such standard physical ^{dimensions} are available, they haven't
+been used very much in traditional typefaces; designers usually specify
+other units like `"em"' or `"x\_height"' in order to define the sizes
+of letters, and such quantities generally have ad hoc values that vary
+from font to font. Plain \MF\ makes it easy to introduce ^{ad hoc
+dimensions} that will vary with the resolution and the magnification just
+as "pt" and "mm" do; all you have to do is define ``^{sharped}''
+dimensions that have the same name as your pixel-oriented dimensions, but
+with `|#|' ^^{hash} tacked on as a suffix. For example, $"em"\0$ and
+$"x\_height"\0$ (typed `|em#|' and `|x_height#|'\thinspace) would be the
+^{sharped dimensions} corresponding to "em" and "x\_height". Plain \MF\ has
+already defined the quantities $"pt"\0$, $"pc"\0$, $"in"\0$, $"bp"\0$,
+$"cm"\0$, $"mm"\0$, $"dd"\0$, and $"cc"\0$ for the standard units named above.
+
+Sharped dimensions like $"em"\0$ and $"x\_height"\0$ should always be
+defined in terms of resolution-independent dimension variables like $"pt"\0$,
+$"in"\0$, etc., so that their values do not change in any way when "mode"
+and "mag" are varied. The `|#|' sign implies unchangeability.
+After @mode\_setup@ has been called,
+the pixel-oriented dimensions can be calculated by simply saying
+\begindisplay
+^@define\_pixels@("em", "x\_height").
+\enddisplay
+This statement is an abbreviation for
+\begindisplay
+$"em":="em"\0\ast"hppp"$;&$"x\_height":="x\_height"\0\ast"hppp"$
+\enddisplay
+where ^"hppp" is an internal variable of \MF\ that represents the number
+of pixels per point in the horizontal dimension. Any number of ad hoc
+dimensions can be listed in a single @define\_pixels@ statement.
+Notice that `\#' is not an operator that could convert "em" to $"em"\0$;
+rounding errors would be mode-dependent.
+
+Chapter 5's demonstration program |io.mf| contains several examples of ad hoc
+dimensions defined in this way, and it also contains the statement
+\begindisplay
+^@define\_blacker\_pixels@("thin", "thick");
+\enddisplay
+what's this? Well, Appendix B makes that statement an abbreviation for
+\begindisplay
+$"thin":="thin"\0\ast"hppp"+"blacker"$;&
+$"thick":="thick"\0\ast"hppp"+"blacker"$;\cr
+\enddisplay
+in other words, the sharped dimensions are being unsharped in this case
+by converting them to pixels and then adding `"blacker"'. The variable
+^"blacker" is a special correction intended to help adapt a font to the
+idiosyncrasies of the current output device; @mode\_setup@ uses the value
+of "mode" to establish the value of "blacker". For example, "cheapo" mode
+might want $"blacker"=0.65$, while "luxo" mode might give best results
+when $"blacker"=0.1$. The general convention is to add "blacker" to
+pixel-oriented variables that determine the breadth of pens and the
+thickness of stems, so that the letters will be slightly darker on machines
+that otherwise would make them appear too light. Different machines treat
+pixels quite differently, because they are often based on quite different
+physical principles. For example, the author once worked with an extremely
+high-resolution device that tended to shrink stem lines rather drastically
+when it used a certain type of photographic paper, and it was necessary
+to set $"blacker"=4$ to get proper results on that machine; another
+high-resolution device seems to want "blacker" to be only~$0.2$. Experimentation
+is necessary to tune \MF's output to particular devices, but the author's
+experience suggests strongly that such a correction is worthwhile. When
+^^"proof" $"mode"="proof"$ or ^"smoke", the value of "blacker" is taken to
+be zero, since the output in these modes is presumably undistorted.
+
+\exercise Does `$"mode"="cheapo"$; $"mag"=10$' produce exactly the same
+font as `$"mode"="luxo"$', under the assumptions of this chapter?
+\answer Almost, but not quite. The values of standard dimension variables
+like "pt" and "mm" will be identical in both setups, as will the values of
+ad~hoc dimension variables like "em" and "x\_height". But pen-oriented
+dimensions that are defined via @define\_blacker\_pixels@ will be slightly
+different, because "cheapo" mode has $"blacker"=0.65$ while "luxo" mode
+has $"blacker"=0.1$ (since the "luxo" printer has different physical
+characteristics). Similarly, @define\_corrected\_pixels@ (which we are
+just about to discuss) will produce slightly different results in the two
+given modes.
+
+\danger Line 7 of |io.mf| says `^@define\_corrected\_pixels@($o$)', and
+this is yet a third way of converting from true physical dimensions to
+pixel-oriented values. According to Appendix~B\null, variable~$o$ is
+defined by the assignment
+\begindisplay
+$o:=\round(o\0\ast"hppp"\ast"o\_correction")+"eps"$
+\enddisplay
+^^{round} ^^"eps" ^^"o"
+where ^"o\_correction", like "blacker", is a magic number that depends on
+the output device for which fonts are being made. On a high-resolution
+device like "luxo", the appropriate value for the "o\_correction" factor
+is~1; but on a low-resolution device like "cheapo", the author has obtained
+more satisfactory results with $"o\_correction"=0.4$. The reason is that
+`$o$' is used to specify the number of pixels by which certain features
+of characters ``^{overshoot}'' the baseline or some other line to which
+they are visually related. High-resolution curves look better when they
+overshoot in this way, but low-resolution curves do not; therefore it is
+usually wise to curtail the amount of overshoot by applying the
+"o\_correction" factor. In "proof" and "smoke" modes the factor is
+equal to 1.0, since these modes correspond to high resolution.
+
+%\danger Plain \MF\ also provides a fourth way to define unsharped
+%dimensions from sharped ones, if you want the unsharped dimensions
+%to be rounded to the nearest integer number of pixels: Just say
+%`^@define\_whole\_pixels@'. For example,
+%\begindisplay
+%@define\_whole\_pixels@("foo")
+%\enddisplay
+%stands for `$"foo":=\round("foo"\0\ast"hppp")$'.
+
+\ddanger The properties of output devices are modeled also by a
+parameter that's called ^"fillin", which represents the amount by which
+diagonal strokes tend to be darker than horizontal or vertical strokes.
+More precisely, let us say that a ``^{corner}'' pixel is one whose color
+matches the color of five of its neighbors but not the other three, where the
+three exceptions include one horizontal neighbor, one vertical neighbor,
+and the diagonal neighbor between them. If a white corner pixel has
+apparent darkness $f_1$ and if a black corner pixel has apparent darkness
+$1-f_2$, then the "fillin" is $f_1-f_2$. \ (A ``true'' raster image would
+have $f_1=f_2=0$, but physical properties often cause pixels to influence
+their neighbors.)
+
+\ddanger Each output device for which you will be generating fonts should
+be represented by a symbolic ^"mode" name in the implementation of \MF\
+that you are using. Since these mode names vary from place to place, they
+are not standard aspects of the \MF\ language; for example, it is doubtful
+whether the hypothetical "cheapo" and "luxo" modes discussed in this
+chapter actually exist anywhere. The plain \MF\ base is intended to be
+extended to additional modes in a disciplined way, as described at the
+end of Appendix~B.
+
+\ddanger It's easy to create a new symbolic mode, using plain \MF's
+`^@mode\_def@\kern.75pt' convention. For example, the "luxo" mode we have been
+talking about could be defined by saying
+\begindisplay
+@mode\_def@ "luxo" $=$\cr
+\quad$"pixels\_per\_inch":=2000$;&|%| high res, almost 30 per point\cr
+\quad$"blacker":=.1$;&|%| make pens a teeny bit blacker\cr
+\quad$"o\_correction":=1$;&|%| keep the full overshoot\cr
+\quad$"fillin":=0.1$;&|%| compensate for darkened corners\cr
+\quad$"proofing":=0$;&|%| no, we're not making proofs\cr
+\quad$"fontmaking":=1$;&|%| yes, we are making a font\cr
+\quad$"tracingtitles":=1$; \ @enddef@;&|%| yes, show titles online\cr
+\enddisplay
+The name of the mode should be a single symbolic token. The resolution
+should be specified by assigning a value to "pixels\_per\_inch"; all other
+dimension values ("pt", "mm", etc.)\ will be computed from this one by
+@mode\_setup@. A mode definition should also assign values to the
+internal variables "blacker", "o\_correction", and "fillin" (which describe
+the device characteristics), as well as ^"proofing", ^"fontmaking", and
+^"tracingtitles" (which affect the amount of output that will be produced).
+In general, "proofing" and "fontmaking" are usually
+set to 0 and~1, respectively, in modes that are intended for font
+production rather than initial font design; "tracingtitles" is usually
+0~for low-resolution fonts (which are generated quickly), but 1~for
+high-resolution fonts (which go more slowly), because detailed online
+progress reports are desirable when comparatively long jobs are running.
+
+\ddanger Besides the seven mandatory quantities `"pixels\_per\_inch"',
+\dots, `"tracingtitles"' just discussed, a mode definition might assign
+a value to `^"aspect\_ratio"'. In the normal case when no
+"aspect\_ratio" is specified, it means that the fonts to be output
+are assumed to have square pixels. But if, for
+example, the @mode\_def@ sets $"aspect\_ratio":=5/4$,
+it means that the output pixels
+are assumed to be ^{nonsquare} in the ratio of 5 to~4; i.e.,
+5~vertical pixel units are equal to 4~horizontal pixel units. The
+pixel-oriented dimensions of plain \MF\ are given in terms of horizontal
+pixel units, so an aspect ratio of 5/4 together with 2000 pixels per inch
+would mean that there are 2500 vertical pixel units per inch; a square
+inch would consist of 2500 rows of pixels, with 2000 pixels in each row. \
+(Stating this another way, each pixel would be $1\over2000$ inches wide and
+$1\over2500$ inches high.) \ In such a case, plain \MF\ will set the
+^"currenttransform" variable so that all @draw@ and @fill@ commands
+stretch the curves by a factor of 5/4 in the vertical dimension; this
+compensates for the nonsquare pixels, so the typeface designer doesn't have to
+be aware of the fact that pixels aren't square.
+
+%\ddanger A mode definition might also do other things besides setting
+%the values of numeric variables like "blacker" or "aspect\_ratio".
+%For example, the @mode\_def@ for "smoke" in Appendix~B includes the
+%statements `@grayfont@ black; @let@ $"makebox"="maketicks"$';
+%this changes the style of proofsheets that you get in ^"smoke" mode.
+
+Let's look now at a concrete example, so that it will be clear how the
+ideas of device-independent font design can be implemented in practice.
+We shall study a file |logo.mf| that generates the seven letters of
+\MF's ^{logo}. There also are ``^{parameter}'' files |logo10.mf|, |logo9.mf|,
+etc., which use |logo.mf| to produce fonts in various sizes. For
+example, a font containing the 10-point characters `\thinspace\MF\thinspace'
+could be generated for the hypothetical "luxo" printer by running \MF\ with
+the command line
+\begintt
+\mode=luxo; input logo10
+\endtt
+if "luxo" mode really existed.
+
+The main purpose of |logo10.mf| is to establish the ``sharped'' values of
+several ad hoc dimensions; then it inputs |logo.mf|, which does the
+rest of the work. Here is the entire file |logo10.mf|:
+\begintt
+% 10-point METAFONT logo|smallskip
+font_size 10pt#; % the "design size" of this font
+ht#:=6pt#; % height of characters
+xgap#:=0.6pt#; % horizontal adjustment
+u#:=4/9pt#; % unit width
+s#:=0; % extra space at the left and the right
+o#:=1/9pt#; % overshoot
+px#:=2/3pt#; % horizontal thickness of pen
+input logo % now generate the font
+end % and stop.
+\endtt
+Similar files |logo9.mf| and |logo8.mf| will produce 9-point
+`\thinspace{\manual hijklmnj}\thinspace' and \hbox{8-point}
+`\thinspace{\manual opqrstuq}\thinspace'; the letters get a little
+wider in relation to their height, and the inter-character spacing
+gets significantly wider, as the size gets smaller:
+\begintt
+% 9-point METAFONT logo % 8-point METAFONT logo|smallskip
+font_size 9pt#; font_size 8pt#;
+ht#:=.9*6pt#; ht#:=.8*6pt#;
+xgap#:=.9*0.6pt#; xgap#:=.8*0.6pt#;
+u#:=.91*4/9pt#; u#:=.82*4/9pt#;
+s#:=.08pt#; s#:=.2pt#;
+o#:=1/10pt#; o#:=1/12pt#;
+px#:=.9*2/3pt#; px#:=.8*2/3pt#;
+input logo input logo
+end end
+\endtt
+It is interesting to compare the font generated by |logo10.mf| to the
+font generated by |logo8.mf| with |mag=10/8|: Both fonts will have
+the same values of "ht", "xgap", and "px", when the magnification has been
+taken into account. But the magnified 8-point font has a slightly larger
+value of "u" and a positive value of "s"; this changes
+`\thinspace\MF\thinspace' to `\thinspace{\manual/0123451}\thinspace'.
+
+\danger Every font has a ``^{design size},'' which is a more-or-less
+arbitrary number that reflects the size of type it is intended to blend
+with. ^^{TeX} Users of \TeX\ select magnified fonts in two ways, either
+by specifying an ``at size'' or by specifying a scale factor (times 1000).
+For example, the 8-point \MF\ logo can be used at 10/8 magnification by
+referring either to `|logo8| |at|~|10pt|' or to `|logo8| |scaled|~|1250|'
+in a \TeX\ document. When an ``^{at size}'' is specified, the amount of
+magnification is the stated size divided by the design~size. A typeface
+designer can specify the design size by using plain \MF's `^@font\_size@'
+command as illustrated on the previous page. \ (If no design size is
+specified, \MF\ will set it to $128\pt$, by default.)
+
+The file |logo.mf| itself begins by defining three more ad hoc dimensions
+in terms of the parameters that were set by the parameter file; these
+dimensions will be used in several of the programs for individual letters.
+Then |logo.mf| makes the conversion to pixel units:
+\begintt
+% Routines for the METAFONT logo
+% (logo10.mf is a typical parameter file)
+mode_setup;
+ygap#:=(ht#/13.5u#)*xgap#; % vertical adjustment
+leftstemloc#:=2.5u#+s#; % position of left stems
+barheight#:=.45ht#; % height of bar lines
+define_pixels(s,u,xgap,ygap,leftstemloc,barheight);
+py#:=.9px#; define_blacker_pixels(px,py); % pen dimensions
+pickup pencircle xscaled px yscaled py; logo_pen:=savepen;
+define_corrected_pixels(o);
+\endtt
+There's nothing new here except the use of `^"savepen"' in the
+second-last line; this, as we will see in Chapter~16, makes the
+currently-picked-up pen available for repeated use in the
+subsequent program.
+
+After the initial definitions just shown, |logo.mf| continues with
+programs for each of the seven letters. For example,
+here is the program for `{\manual ^{E}}', which illustrates the
+\rightfig 11a ({224\apspix} x {216\apspix}) ^-11pt
+use of $u\0$, $s\0$, $"ht"\0$, "leftstemloc", "barheight", "xgap",
+and "logo\_pen":
+\begintt
+beginchar("E",14u#+2s#,ht#,0);
+pickup logo_pen;
+x1=x2=x3=leftstemloc;
+x4=x6=w-x1+o; x5=x4-xgap;
+y1=y6; y2=y5; y3=y4;
+bot y1=0; top y3=h;
+y2=barheight;
+draw z6--z1--z3--z4; draw z2--z5;
+labels(1,2,3,4,5,6);
+endchar;
+\endtt
+We have seen the essentials of the {\manual M} and the {\manual T} in
+Chapter~4; programs for the other letters will appear later.
+
+\exercise The ad hoc dimensions $"ht"\0$, $"xgap"\0$, $u\0$, $s\0$,
+$o\0$, and $"px"\0$ defined in the parameter files all affect the letter
+`{\manual E}' defined by this program. For each of these dimensions,
+tell what would happen to the `{\manual E}' if that dimension were
+increased slightly while all the others stayed the same.
+\answer Increasing $"ht"\0$ would make the letter shape and the bounding
+box taller; increasing $"xgap"\0$ would move point~5 to the left, thereby
+making the middle bar shorter; increasing $u\0$ would make the shape and
+its bounding box wider; increasing $s\0$ would widen the bounding box
+at both sides without changing the letter shape; increasing $o\0$ would
+move points 4,~5, and~6 to the right; increasing $"px"\0$ would make
+the pen thicker (preserving the top edge of the upper bar, the bottom
+edge of the lower bar, and the center of the middle bar and the stem).
+
+\dangerexercise Guess the program for `{\manual l}' (which is ^^{F}
+almost the same as `{\manual i}'\thinspace).
+\answer The only possible surprise is the position of $y_1$,
+which should match similar details in the `{\manual h}'
+and the~`\kern1pt{\manual j}\kern1pt' of Chapter~4:
+\begintt
+beginchar("F",14*u#+2s#,ht#,0); pickup logo_pen;
+x1=x2=x3=leftstemloc; x4=w-x1+o; x5=x4-xgap;
+y2=y5; y3=y4; bot y1=-o; top y3=h; y2=barheight;
+draw z1--z3--z4; draw z2--z5;
+labels(1,2,3,4,5); endchar;
+\endtt
+
+\dangerexercise Write the complete programs for `{\manual h}' ^^{M} ^^{T}
+and `\kern1pt{\manual j}\kern1pt', based on the information in Chapter~4,
+but using the style of the program for `{\manual E}' above. The character
+widths should be $18u\0+2s\0$ and $13u\0+2s\0$, respectively.
+\checkequals\metaT\exno
+\answer The quantity called "ss" in Chapter~4 is now "leftstemloc".
+\begintt
+beginchar("M",18*u#+2s#,ht#,0); pickup logo_pen;
+x1=x2=leftstemloc; x4=x5=w-x1; x3=w-x3;
+y1=y5; y2=y4; bot y1=-o; top y2=h+o; y3=y1+ygap;
+draw z1--z2--z3--z4--z5;
+labels(1,2,3,4,5); endchar;|smallskip
+beginchar("T",13*u#+2s#,ht#,0); pickup logo_pen;
+lft x1=0; x2=w-x1; x3=x4=.5w;
+y1=y2=y3; top y1=h; bot y4=-o;
+draw z1--z2; draw z3--z4;
+labels(1,2,3,4); endchar;
+\endtt
+
+\danger The file |logo.mf| also contains the following cryptic instructions,
+which cause the letter pairs `\kern1pt{\manual jk}' and `{\manual lm}' to
+be typeset closer together than their bounding boxes would imply:
+\begintt
+ligtable "T": "A" kern -.5u#;
+ligtable "F": "O" kern -u#;|smallskip
+\endtt
+Without these corrections `\MF\kern1pt' would be ^^{kerning} ^^@kern@
+`{\manual hij\/kl\/mnj}\kern1pt'. Uppercase letters are often subject to
+such spacing corrections, especially in logos; \TeX\ will adjust the spacing
+if the typeface designer has supplied ^@ligtable@ information like this.
+
+\danger Finally, |logo.mf| closes with four more commands, which provide
+further information about how to typeset with this font:
+\begintt
+font_quad 18u#+2s#;
+font_normal_space 6u#+2s#;
+font_normal_stretch 3u#;
+font_normal_shrink 2u#;
+\endtt
+A ^@font\_quad@ is the unit of measure that a \TeX\ user calls one `|em|'
+when this font is selected. The normal space, stretch, and shrink parameters
+^^@font\_normal\_space@ ^^@font\_normal\_stretch@ ^^@font\_normal\_shrink@
+define the interword spacing when text is being typeset in this font.
+Actually a font like |logo10| is rarely used to typeset anything except
+the one word, `\MF\kern1pt'; but the spacing parameters have been
+included just in case somebody wants to typeset a sentence like
+`{\manual kn illiji jmhkjm ml hmnjknk mljin kji nmnlkj jmllii}'.
+
+\danger An optional `^|=|' or `^|:=|' sign may be typed after `@font\_size@',
+`@font\_quad@', etc., in case you think the file looks better that way.
+
+\danger Notice that ``sharped'' units must be given in the ^@ligtable@
+kerning commands and in the definition of device-independent
+parameters like @font\_size@
+and @font\_quad@. Appendix~F discusses the complete rules of @ligtable@
+and other commands by which \MF\ programs can send important information
+to typesetting systems like \TeX. Adding these extra bits of information
+to a \MF\ program after a font has been designed is something like
+adding an index to a book after that book has been written and proofread.
+
+\ddangerexercise What's the longest English word that can be typeset
+with the font |logo9|?
+\answer `{\manual nmnkjmnihinj\/}'; possibly also `{\manual hijklmmjnmji}';
+and Georgia ^{Tobin} suggests that `{\manual knjiinlimllhinj\/}'
+might be a legal term.
+
+\ninepoint % nothing but danger from here on, folks
+\danger Let's summarize the general contents of |logo.mf|, now that we
+have seen it all, because it provides an example of a complete typeface
+description (even though there are only seven letters):\enddanger
+
+\smallskip
+\item\bull The file begins by defining ad hoc dimensions and converting
+them to pixel units, using @mode\_setup@, @define\_pixels@, etc.
+
+\smallskip
+\item\bull Then come programs for individual letters. \ (These programs
+are often preceded by macro definitions for subroutines that occur several
+times. For example, we will see later that the `{\manual k}' and the
+`{\manual m}' of the logo are drawn with the help of a subroutine that makes
+half of a superellipse; the definition of this macro actually comes near
+the beginning of |logo.mf|, just before the programs for the letters.)
+
+\smallskip
+\item\bull Finally there are special commands like ^@ligtable@ and
+^@font\_quad@, to define parameters of the font that are helpful
+when typesetting.
+
+\smallskip
+\item\bull The file is accompanied by parameter files that define
+ad hoc dimensions for different incarnations of the typeface.
+
+\smallskip\noindent
+We could make lots of different parameter files, which would produce
+lots of different (but related) variations on the \MF\ logo; thus, |logo.mf|
+defines a ``^{meta-font}'' in the sense of Chapter~1.
+
+\dangerexercise What changes would be necessary to generalize the |logo|
+routines so that the bar-line height is not always 45 per~cent of the
+character height?
+\answer Delete the line of |logo.mf| that defines |barheight#|, and
+insert that line into each of the parameter files |logo10.mf|, |logo9.mf|,
+|logo8.mf|. Then other bar-line heights are possible by providing new
+parameter files; another degree of ``meta-ness'' has therefore been added
+to the meta-font.
+
+\danger ^{Assignments} (\thinspace`|:=|'\thinspace) have been used instead
+of equations (\thinspace`|=|'\thinspace) in the parameter files |logo10.mf|,
+|logo9.mf|, and |logo8.mf|, as well as
+in the opening lines of |io.mf| in Chapter~5; this contradicts the
+advice in Chapter~10, where we are told to stick to equations unless
+assignments are absolutely necessary. The author has found it convenient
+to develop the habit of using assignments whenever ad hoc dimensions
+are being defined, because he often makes experimental files in which
+the ad hoc dimensions are changed several times. For example, it's a good
+idea to test a particular letter with respect to a variety of different
+parameter settings when that letter is first being designed; such
+experiments can be done easily by copying the ad hoc parameter definitions
+from parameter files into a test file, provided that the parameters
+have been defined with assignments instead of equations.
+
+\danger \TeX\ users have found it convenient to have fonts in a series
+of magnifications that form a geometric series. A font is said
+to be scaled by `^{magstep}~1' if it has been magnified by~1.2;
+it is scaled by `magstep~2' if it has been magnified by $1.2\times1.2=1.44$;
+it is scaled by `magstep~3' if it has been magnified by $1.2\times1.2\times1.2=
+1.728$; and so on. Thus, if a job uses a font that is scaled by magstep~2,
+and if that entire job is magnified by magstep~1, the font actually used
+for printing will be scaled by magstep~3. The additive nature of magsteps
+makes it more likely that fonts will exist at the desired sizes when
+jobs are magnified. Plain \MF\ supports this convention by allowing
+constructions like
+\begintt
+\mode=cheapo; mag=magstep 2; input logo9
+\endtt
+if you want to generate the 9-point \MF\ logo for the "cheapo" printer,
+magnified by 1.44 (i.e., by magstep~2). You can also write `|magstep|~|0.5|'
+^^{TeX} for what \TeX\ calls `|\magstephalf|'; this magnifies by $\sqrt{1.2}$.
+
+\ddanger The sharped forms of dimensions are actually represented by plain
+\MF\ in terms of printer's points, so that `$"pt"\0$' turns out to be
+equal to~1. However, it is best for programmers not to make use of this
+fact; a program ought to say, e.g., `$"em"\0:=10"pt"\0$', even though
+the `$"pt"\0$' in this construction is redundant, and even though the
+computer would run a few microseconds faster without it.
+
+\ddangerexercise Suppose you want to simulate a low-resolution printer
+on a high resolution device; for concreteness, let's say that
+"luxo" is supposed to produce the output of "cheapo", with each black
+"cheapo" pixel replaced by a $10\times10$ square of black "luxo" pixels.
+Explain how to do this to the |logo10| font, by making appropriate
+changes to |logo.mf|. Your output file should be called |cheaplogo10.2000gf|.
+\answer (This is tricky.) \ Insert the lines
+\begintt
+if known pixmag: begingroup interim hppp:=pixmag*hppp;
+ special "title cheapo simulation" endgroup;
+ extra_endchar:="currentpicture:=currentpicture scaled pixmag;"
+ & "w:=w*pixmag;" & extra_endchar; fi
+\endtt
+right after `|mode_setup|' in |logo.mf|, and also include the line
+\begintt
+if known pixmag: hppp:=pixmag*hppp; vppp:=pixmag*vppp; fi
+\endtt
+at the very end of that file. Then run \MF\ with
+\begintt
+\mode="cheapo"; input cheaplogo10
+\endtt
+where the file `|cheaplogo10.mf|' says simply `|pixmag=10;| |input| |logo10|'.
+\ (The interim "hppp" setting and the ^@special@ command are
+used to fool \MF\ into giving the appropriate extension to the
+^|gf| file name. Incidentally, you could print with this font on "cheapo"
+at ten-fold magnification if you told \TeX\ to use the font `|cheaplogo10|
+|scaled| |10000|'; but on "luxo" you would simply call this font
+`|cheaplogo10|'.)
+
+\endchapter
+
+A great Temptation must be withstood with great Resolution.
+\author WILLIAM ^{BURKITT}, {\sl Expository Notes on the New Testament\/} %
+ (c.\thinspace1700) % commenting on Matt 4:10
+ % I examined only the fifth edition (1712), title page says `New-Testament'
+ % Another edition printed at New Haven in 1794 says `should' not `must'!
+
+\bigskip
+
+What some invent, the rest enlarge.
+\author JONATHAN ^{SWIFT}, {\sl Journal of a Modern Lady\/} (1729) % line 145
+
+\eject
+ \beginchapter Chapter 12. Boxes
+
+\looseness=-1
+Let's pause now to take a closer look at the ``bounding boxes'' that enclose
+individual characters. In olden days, metal type was cast on a
+rectangular body in which each piece of type had the same vertical
+extent, although the type widths would vary from character to character.
+Nowadays we are free of the mechanical constraints imposed by metal type,
+but the former metaphors are still useful: A~typesetting system like ^^{TeX}
+\TeX\ imagines that each character fits into a rectangular box, and words are
+typeset by putting such boxes snugly next to each other.
+
+% Here are some macros borrowed from The TeXbook
+\def\dolist{\afterassignment\dodolist\let\next= }%
+\def\dodolist{\ifx\next\endlist \let\next\relax
+ \else \\\let\next\dolist \fi
+ \next}
+\def\endlist{\endlist}
+\def\\{\expandafter\if\space\next\ \else \setbox0=\hbox{\next}\maketypebox\fi}
+\def\demobox#1{\setbox0=\hbox{\dolist#1\endlist}%
+ \copy0\kern-\wd0\makelightbox}
+ The main difference
+between the old conventions and the new~ones is that type boxes are now
+allowed to vary in height as well as in width. For example, when \TeX\
+typesets `A~line~of~type.' it puts boxes together that essentially look
+like this: `\thinspace\demobox{A line of type.}\thinspace'. \ (The `A'
+appears in a box `\thinspace\setbox0\hbox{A}\maketypebox\thinspace' that
+sits on a given baseline, while the `y' appears in a box
+`\thinspace\setbox0\hbox{y}\maketypebox\thinspace' that descends below the
+baseline.) \ \TeX\ never looks inside a box to see what character actually
+appears there; \TeX's job is to put boxes together in the right places
+on a page, based only on the box sizes. It is a typeface designer's job
+to decide how big the boxes should be and to create the characters inside
+the boxes.
+
+Boxes are two-dimensional objects, but we ascribe three dimensions to them
+because the vertical component is divided into two quantities, the
+{\sl^{height}\/} (above the ^{baseline}) and the {\sl^{depth}\/}
+(below the baseline). The horizontal dimension is, of course, called
+the {\sl^{width}}. Here is a picture of a typical box, showing its
+so-called ^{reference point} and baseline:
+
+{\eightpoint
+\setbox0=\hbox{$\uparrow$}
+\setbox1=\hbox to \wd0{$\hss\mid\hss$} % with luck, they'll line up
+\setbox2=\vbox{\copy0
+ \nointerlineskip \kern-.5pt \copy1
+ \nointerlineskip \kern-.5pt \copy1
+ \moveleft 1em\hbox{height}
+ \copy1 \nointerlineskip \kern-.5pt
+ \copy1 \nointerlineskip \kern-.5pt
+ \hbox{$\downarrow$}
+ \kern.2pt}
+\setbox3=\vbox{\kern.2pt\copy0
+ \moveleft 1em\hbox{depth}
+ \hbox{$\downarrow$}
+ \kern0pt}
+\setbox4=\vtop{\kern-3pt % this cancels the null text above the samplebox
+ \hbox{\samplebox{\ht2}{\ht3}{6em}{}%
+ \kern-6em
+ \raise3pt\hbox to 6em{\hss Baseline\hss}}
+ \kern3pt
+ \arrows{6em}{width}}
+\medskip\indent
+\setbox0=\hbox{$\vcenter{}$}% \ht0 is the axis height
+\lower\ht0\hbox{Reference point$-$\kern-.2em$\rightarrow$\kern2pt}%
+\raise\ht2\box4
+\kern1.5em
+\raise\ht2\vtop{\kern0pt\box2\nointerlineskip\box3}}
+
+\medskip\noindent
+The example characters in previous chapters have all had zero depth, but
+we will soon be seeing examples in which both height and depth are relevant.
+
+A character shape need not fit inside the boundaries of its box. Indeed,
+{\it italic\/} and {\sl slanted\/} letters are put into ordinary boxes
+just as if they were not slanted, so they frequently stick out at the right.
+For example, the letter `g\/' in the font you are now reading (^|cmr10|)
+can be compared with the `{\sl g\/}' in the corresponding slanted
+font (^|cmsl10|):
+\begindisplay
+\vbox to 40pt{\ifproofmode\hrule\vfill
+ \hsize=2.5in \baselineskip 6pt \fiverm\noindent
+ (A figure will be inserted here; too bad you can't see it now.
+ It shows two g's, as claimed. In fact, the same figure appeared
+ on page 63 of The TeXbook.)
+ \vfill\hrule\fi}
+\enddisplay
+The slanted `{\sl g\/}' has been drawn as if its box were skewed right at the
+top and left at the bottom, keeping the baseline fixed; but \TeX\ is told
+in both cases that the box is $5\pt$ wide, $4.3055\pt$ high, and $1.9444\pt$
+deep. Slanted letters will be spaced properly in spite of the fact that their
+boxes have been straightened up, because the letters will match correctly
+at the baseline.
+
+\danger Boxes also have a fourth dimension called the {\sl^{italic
+correction}}, which gives \TeX\ additional information about whether or
+not a letter protrudes at the right. For example, the italic correction
+for an unslanted `g\/' in |cmr10| is $0.1389\pt$, while the corresponding
+slanted letter in |cmsl10| has an italic correction of $0.8565\pt$. The
+italic correction is added to a box's width when math formulas like ${\rm
+g}^2$ or ${\sl g}^2$ are being typeset, and also in other cases as
+explained in {\sl The \TeX book}.
+
+Plain \MF's ^@beginchar@ command establishes the width, height, and depth
+of a box. These dimensions should be given in terms of ``^{sharped}''
+quantities that do not vary with the resolution or magnification, because
+the size of a character's type box should not depend in any way on the device
+that will be used to output that character. It is important to be able to
+define documents that will not change even though the technology for printing
+those documents is continually evolving. \MF\ can be used to produce fonts for
+new devices by introducing new ``modes,'' as we have seen in Chapter~11,
+but the new fonts should still give the same box dimensions to each character.
+Then the device-independent files output by \TeX\ will not have to be
+changed in any way when they are printed or displayed with the help of
+new equipment.
+
+The three dimensions in a @beginchar@ command are given in reverse
+alphabetical order: First comes the width, then the height, then the depth.
+The @beginchar@ routine converts these quantities into pixel units
+and assigns them to the three variables ^"w", ^"h", and~^"d". In fact,
+@beginchar@ rounds these dimensions to the nearest whole number of
+pixels; hence $w$, $h$, and~$d$ will always be integers.
+
+\MF's pixels are like squares on ^{graph paper}, with pixel boundaries
+at points with integer coordinates. The left edge of the type box lies
+on the line $x=0$, and the right edge lies on the line $x=w$; we have
+$y=h$ on the top edge and $y=-d$ on the bottom edge. There are $w$ pixels
+in each row and $h+d$ in each column, so there are exactly $wh+wd$ pixels
+inside the type box.
+
+Since $w$, $h$, and $d$ are integers, they probably do not exactly match
+the box dimensions that are assumed by device-independent typesetting
+systems like \TeX. Some characters will be a fraction of a pixel too wide;
+others will be a fraction of a pixel too narrow. However, it's still possible
+to obtain satisfactory results if the pixel boxes are stacked together
+based on their $w$ values and if the accumulated error is removed in the
+spaces between words, provided that the box positions do not ^{drift}
+too far away from their true device-independent locations. A designer should
+strive to obtain letterforms that work well together when they are placed
+together in boxes that are an integer number of pixels wide.
+
+\ddanger You might not like the value of $w$ that @beginchar@ computes by
+rounding the device-independent width to the nearest pixel boundary.
+For example, you might want to make the letter~`m' one pixel wider, at
+certain resolutions, so that its three stems are equally spaced or so that
+it will go better with your `n'. In such a case you can assign a new value
+to~$w$, at any time between @beginchar@ and ^@endchar@. This new value
+will not affect the device-independent box width assumed by \TeX, but it
+should be respected by the software that typesets ^|dvi| files using your font.
+
+\def\hidecoords(#1,#2){\hbox to 0pt{\hss$\scriptstyle(#1,#2)$\hss}}
+\setbox0=\vtop{\kern -94pt
+ \rightline{\vbox{\hbox to 140\apspix{\hidecoords(0,h)\hfil
+ \hidecoords(w\mkern-2mu,h)}
+ \kern3pt
+ \figbox{12a}{140\apspix}{360\apspix}\vbox
+ \kern-3pt
+ \hbox to 140\apspix{\hidecoords(0,-d)\hfil
+ \hidecoords(w\mkern-2mu,-d)}}\quad}}
+\dp0=0pt
+
+Here's an example of a character that has nonzero width, height, and depth;
+it's the left ^{parenthesis} in ^{Computer Modern} fonts like |cmr10|.
+Computer Modern typefaces are generated by \MF\ programs that involve
+lots of parameters, so this example also illustrates the principles of
+``^{meta-design}'': Many different varieties of left parentheses can be
+drawn by this one program. But let's focus our attention first on the
+comparatively simple way in which the box dimensions are established and
+used, before looking into the details of how a meta-parenthesis has
+actually been specified.
+% "hair", "thin", "thick" are actually "vair", "hair", "stem" in the code
+\def\xs(#1,#2){\{(z_{#1}-z_{#2})\,{\rm xscaled}\,3\}}%
+\begindisplay
+|"Left parenthesis"|;\cr
+@numeric@ $"ht"\0$, $"dp"\0$;\cr
+$"ht"\0="body\_height"\0$; \ $.5["ht"\0,-"dp"\0]="axis"\0$;\cr
+@beginchar@\kern1pt(|"("|$,7u\0,"ht"\0,"dp"\0)$;\cr
+@italcorr@ $"ht"\0\ast"slant"-.5u\0$;\cr
+@pickup@ "fine.nib";\cr
+$\penpos1("hair"-"fine",0)$;\strut\vadjust{\box0}\cr
+$\penpos2(.75["thin","thick"]-"fine",0)$;\cr
+$\penpos3("hair"-"fine",0)$;\cr
+$\mathop{"rt"}x_{1r}=\mathop{"rt"}x_{3r}= w-u$; \
+ $\mathop{"lft"}x_{2l}=x_1-4u$;\cr
+$\mathop{"top"}y_1=h$; \
+ $y_2=.5[y_1,y_3]="axis"$;\cr
+@filldraw@ $z_{1l}\xs(2l,1l)\ldots z_{2l}$\cr
+\qquad$\ldots\xs(3l,2l)z_{3l}$\cr
+\qquad$\dashto z_{3r}\xs(2r,3r)\ldots z_{2r}$\cr
+\qquad$\ldots\xs(1r,2r)z_{1r}\dashto\cycle$;\cr
+@penlabels@$(1,2,3)$; \ @endchar@;\cr
+\enddisplay
+
+The width of this left parenthesis is $7u\0$, where $u\0$
+is an ad hoc parameter that figures in all the widths of the Computer
+Modern characters. The height and depth have been calculated in such a way
+that the top and bottom of the bounding box are equally distant from an
+imaginary line called the {\sl^{axis}}, which is important in mathematical
+typesetting. \ (For example, \TeX\ puts the bar line at the axis
+in fractions like $1\over2$; many symbols like `$+$' and `$=$', as well as
+parentheses, are centered on the axis line.) \ Our example program puts the
+axis midway between the top and bottom of the type by saying that
+`$.5["ht"\0,-"dp"\0]="axis"\0$'. We also place the top at position
+`$"ht"\0="body\_height"\0$'\thinspace; here $"body\_height"\0$ is the
+height of the tallest characters in the entire typeface.
+It turns out that $"body\_height"\0$ is exactly $7.5"pt"\0$ in |cmr10|, and
+$"axis"\0=2.5"pt"\0$; hence $"dp"\0=2.5"pt"\0$,
+and the parenthesis is exactly $10\pt$ tall.
+
+The program for `(' uses a ^@filldraw@ command, which we haven't
+seen before in this book; it's basically a combination of @fill@
+and @draw@, where the filling is done with the currently-picked-up pen.
+Some of the Computer Modern fonts have characters with ``^{soft}'' edges
+while others have ``^{crisp}'' edges; the difference is due to the pen that
+is used to @filldraw@ the shapes. This pen is a circle whose diameter
+is called ^"fine"; when "fine" is fairly large, @filldraw@ will produce
+rounded corners, but when $"fine"=0$ (as it is in |cmr10|) the corners
+will be sharp.
+% (actually it isn't zero in cmr10, but this makes a better example)
+
+The statement `$\penpos1("hair"-"fine",0)$' makes the breadth of a
+simulated broad-edge pen equal to $"hair"-"fine"$ at position~1; i.e.,
+the distance between $z_{1l}$ and $z_{1r}$ will be $"hair"-"fine"$.
+We will be filling a region between $z_{1l}$ and $z_{1r}$ with a
+circle-shaped pen nib whose diameter is "fine"; the center of that
+nib will pass through $z_{1l}$ and $z_{1r}$, hence the pen will
+effectively add ${1\over2}"fine"$ to the breadth of the stroke at
+either side. The overall breadth at position~1 will therefore be
+${1\over2}"fine"+("hair"-"fine")+{1\over2}"fine"\;=\;"hair"$.
+(Computer Modern's ``^{hairline} thickness'' parameter, which governs
+the breadth of the thinnest strokes, is called "hair".) \ Similarly,
+the statement `$\penpos2(.75["thin","thick"]-"fine",0)$' makes the
+overall breadth of the pen at position~2 equal to $.75["thin","thick"]$,
+which is $3\over4$ of the way between two other parameters that govern
+stroke breadths in Computer Modern routines. If "fine" is increased while
+"hair", "thin", and "thick" stay the same, the effect will simply be to
+produce more rounded corners at positions 1 and~3, with little or no effect
+on the rest of the shape, provided that "fine" doesn't get so large
+that it exceeds "hair".
+
+\def\paren #1 #2 #3 #4 #5 #6 #7 #8 #9
+{\vbox{\dimen0=#3\apspix \hsize=7\dimen0
+ \centerline{\tt#1}
+ \medskip \kern3pt \kern270\apspix \kern-#4\apspix
+ \dimen2=#4\apspix \advance\dimen2 by -#5\apspix
+ \figbox{#2}{7\dimen0}{2\dimen2}\vbox
+ \kern-2\dimen2 \kern#4\apspix \kern90\apspix \kern-3pt \medskip
+ \tabskip 0pt plus 1fil
+ \halign to\hsize{$##$\cr
+ u=\hfil#3\cr
+ "ht"=\hfil#4\cr
+ "axis"=\hfil#5\cr
+ "fine"=\hfil#6\cr
+ "hair"=\hfil#7\cr
+ "thin"=\hfil#8\cr
+ "thick"=\hfil#9\cr}}}
+
+Here, for example, are five different left parentheses, drawn by our example
+program with various settings of the parameters:
+$$\line{\paren cmr10 12a 20 270 90 0 8 9 25
+ \hfil\paren cmbx10 12b 23 270 90 0 13 17 41
+ \hfil\paren cmvtt10 12c 21 250 110 22 22 25 25
+ \hfil\paren cmssdc10 12d 19 270 95 8 23 40 40
+ \hfil\paren cmti10 12e 18.4 270 90 7 8 11 23 }$$
+Parameter values are shown here in ^"proof"\kern-1pt\ mode pixel units,
+36 to the point. \ (Thus, for example, the value of $u\0$ in |cmr10| is
+${20\over36}"pt"\0$.) \ Since |cmbx10| is a ``bold extended'' font,
+its unit width~$u$ is slightly larger than the unit width of |cmr10|,
+and its pen widths (especially "thick") are significantly larger.
+The ``variable-width typewriter'' font |cmvtt10| has soft edges and
+strokes of almost uniform thickness, because "fine" and "hair" are almost
+as large as "thin" and "thick". This font also has a raised axis and a smaller
+height. An intermediate situation occurs in |cmssdc10|, a ``sans serif
+demibold condensed'' font that is similar to the type used in the chapter titles
+of this book; $"thick"="thin"$ in this font, but hairlines are noticeably
+thinner, and "fine" provides slightly rounded corners. The ``text italic''
+font |cmti10| has rounded ends, and the character shape has been ^{slanted}
+by .25; this means that each point $(x,y)$ has been moved to position
+$(x+.25y,y)$, in the path that is filled by @filldraw@.
+
+\danger The vertical line just to the right of the italic left parenthesis
+shows the ^{italic correction} of that character, i.e., the fourth box
+dimension mentioned earlier. This quantity was defined by the statement
+`^@italcorr@ $"ht"\0\ast"slant"-.5u\0$' in our program; here ^"slant" is
+a parameter of Computer Modern that is zero in all the unslanted fonts,
+but $"slant"=.25$ in the case of |cmti10|. The expression following
+@italcorr@ should always be given in sharped units. If the value is
+negative, the italic correction will be zero; otherwise the italic
+correction will be the stated amount.
+
+\danger The author has obtained satisfactory results by making the italic
+correction roughly equal to $.5u$ plus the maximum amount by which the
+character sticks out to the right of its box. For example, the top right
+end of the left parenthesis will be nearly at position $(w-u,"ht")$ before
+slanting, so its $x$~coordinate after slanting will be $w-u+"ht"\ast"slant"$;
+this will be the rightmost point of the
+character, if we assume that $"slant"\ge0$. Adding $.5u$, subtracting~$w$,
+and rewriting in terms of sharped units gives the stated formula. Notice
+that when $"slant"=0$ the statement reduces to `@italcorr@ $-.5u\0$';
+this means that unslanted left parentheses will have an italic correction
+of zero.
+
+\dangerexercise Write a program for right parentheses, to go with these
+left parentheses.
+\answer The changes are straightforward, except for the italic correction
+(for which a rough estimate like the one shown here is good enough):
+\def\xs(#1,#2){\{(z_{#1}-z_{#2})\,{\rm xscaled}\,3\}}%
+\begindisplay
+|"Right parenthesis"|;\cr
+@numeric@ $"ht"\0,"dp"\0$; \
+ $"ht"\0="body\_height"\0$; \
+ $.5["ht"\0,-"dp"\0]="axis"\0$;\cr
+@beginchar@\kern1pt(|")"|$,7u\0,"ht"\0,"dp"\0)$;
+ \ @italcorr@ $"axis"\0\ast"slant"-.5u\0$;\cr
+@pickup@ "fine.nib"; \ $\penpos1("hair"-"fine",0)$;\cr
+$\penpos2(.75["thin","thick"]-"fine",0)$; \ $\penpos3("hair"-"fine",0)$;\cr
+$\mathop{"lft"}x_{1l}=\mathop{"lft"}x_{3l}=u$; \
+ $\mathop{"rt"}x_{2r}=x_1+4u$; \
+$\mathop{"top"}y_1=h$; \
+ $y_2=.5[y_1,y_3]="axis"$;\cr
+@filldraw@ $z_{1l}\xs(2l,1l)\ldots z_{2l}\ldots\xs(3l,2l)z_{3l}$\cr
+\qquad$\dashto z_{3r}\xs(2r,3r)
+ \ldots z_{2r}\ldots\xs(1r,2r)z_{1r}\dashto\cycle$;\cr
+@penlabels@$(1,2,3)$; \ @endchar@;\cr
+\enddisplay
+We will see in Chapter 15 that it's possible to guarantee perfect symmetry
+between left and right parentheses by using picture transformations.
+
+The reader should bear in mind that the conventions of plain \MF\ and of
+Computer Modern are not hardwired into the \MF\ language; they are merely
+examples of how a person might use the system, and other typefaces may well
+be better served by quite different approaches. Our program for left
+parentheses makes use of @beginchar@, @endchar@, @italcorr@, @penlabels@,
+@pickup@, "penpos", "lft", "rt", "top", "z", and @filldraw@, all of which
+are defined somewhat arbitrarily in Appendix~B as part of the plain base;
+it also uses the quantities "u", "body\_height", "axis", "fine", "hair",
+"thin", "thick", and "slant", all of which are arbitrary parameters that
+the author decided to introduce in his programs for Computer Modern. Once
+you understand how to use arbitrary conventions like these, you will be
+able to modify them to suit your own purposes.
+
+\exercise (For people who know \TeX.) \ It's fairly clear that the width of
+a type box is important for typesetting, but what use does \TeX\ make of
+the height and depth?
+\answer When horizontal lines are being typeset, \TeX\ keeps track of the
+maximum height and maximum depth of all boxes on the line; this determines
+whether or not extra space is needed between baselines. The height and depth
+are also used to position an accent above or below a character, and to
+place symbols in mathematical formulas. Sometimes
+boxes are also stacked~up vertically, in which case their heights and depths
+are just as important as their widths are for horizontal setting.
+
+\ddanger The primitive commands by which \MF\ actually learns the dimensions
+of each box are rarely used directly, since they are intended to be embedded
+in higher-level commands like @beginchar@ and @italcorr@. But if you must
+know how things are done at the low level, here is the secret: There are
+four internal quantities called ^"charwd", ^"charht", ^"chardp", and ^"charic",
+whose values at the time of every ^@shipout@ command are assumed to be the
+box dimensions for the character being shipped out, in units of printer's
+points. \ (See
+the definitions of @beginchar@ and @italcorr@ in Appendix~B for examples
+of how these quantities can be manipulated.)
+
+\ninepoint % all dangerous from here on
+\ddanger Besides "charwd" and its cousins, \MF\ also has four other
+internal variables whose values are recorded at the time of every
+@shipout@:\enddanger
+
+\smallskip\textindent\bull^"charcode" is rounded to the nearest integer
+and then converted to a number between 0 and~255, by adding or subtracting
+multiples of~256 if necessary; this ``$c$~code'' is the ^{location} of the
+^^{c code} character within its font.
+
+\smallskip\textindent\bull^"charext" is rounded to the nearest integer;
+the resulting number is a secondary code that can be used to distinguish
+between two or more characters with equal $c$ codes. \ (\TeX\ ignores
+"charext" and assumes that each font contains at most 256 characters; but
+extensions to \TeX\ for ^{oriental} languages can use "charext" to handle
+much larger fonts.)
+
+\smallskip\textindent\bull^"chardx" and "chardy" represent horizontal and
+vertical {\sl escapement\/} in units of pixels. \ (Some typesetting
+systems use both of these device-dependent amounts to change their current
+position on a page, just after typesetting each character. Other systems,
+like the ^|dvi| software associated with \TeX, assume that $"chardy"=0$
+but use "chardx" as the horizontal escapement whenever a horizontal
+movement by "chardx" does not cause the subsequent position to ^{drift}
+too far from the device-independent position defined by accumulated
+"charwd" values. Plain \MF's @endchar@ routine keeps $"chardy"=0$, but
+sets $"chardx":=w$ just before shipping a character to the output. This
+explains why a change to~^"w" will affect the spacing between adjacent
+letters, as discussed earlier.) \looseness=-1
+
+\ddanger Two characters with the same $c$ code
+should have the same box dimensions and escapements; otherwise
+the second character will override the specifications of the first. The boolean
+expression `^{charexists}~$c$' can be used to determine whether or not
+a character with a particular $c$~code has already been shipped out.
+
+\danger Let's conclude this chapter by contemplating a \MF\ program that
+generates the ``^{dangerous bend}'' symbol, since that symbol appears so
+often in this book. It's a custom-made character intended to be used only at
+the very beginnings of paragraphs in which the baselines of the text are
+exactly $11\pt$ apart. Therefore it extends below its baseline by $11\pt$;
+but it is put into a box of depth zero, because \TeX\ would otherwise
+think that the first line of the paragraph contains an extremely deep
+character, and such depth would cause the second line to be moved down.
+$$\def\comment{\hfill{\tt\%} }
+\halign{\hbox to\hsize{\indent#\hfil}\cr
+$"baselinedistance"\0:=11"pt"\0$; \ ^@define\_pixels@("baselinedistance");\cr
+$"heavyline"\0:=50/36"pt"\0$; \ ^@define\_blacker\_pixels@("heavyline");\cr
+$@beginchar@\kern1pt(127,25u\0,"h\_height"\0+"border"\0,0)$; \
+ |"Dangerous bend symbol"|;\cr
+\pickup @pencircle@ scaled "rulethickness";
+ \ $\mathop{"top"}y_1={25\over27}h$; \ $\mathop{"lft"}x_4=0$;\cr
+$x_1+x_1=x_{1a}+x_{1b}=x_{4b}+x_{2a}=x_4+x_2=x_{4a}+x_{2b}=x_{3b}+x_{3a}=
+ x_3+x_3=w$;\cr
+$x_{4a}=x_{4b}=x_4+u$; \ $x_{3b}=x_{1a}=x_1-2u$;\cr
+$y_4+y_4=y_{4a}+y_{4b}=y_{3b}+y_{1a}=y_3+y_1=y_{3a}+y_{1b}=y_{2b}+y_{2a}=
+ y_2+y_2=0$;\cr
+$y_{1a}=y_{1b}=y_1-{2\over27}h$; \ $y_{4b}=y_{2a}=
+ y_4+{4\over27}h$;\cr
+@draw@ $z_{1a}\to z_1\to z_{1b}\ddashto z_{2a}\to z_2\to z_{2b}\ddashto$\cr
+\indent $z_{3a}\to z_3\to z_{3b}\ddashto z_{4a}\to z_4\to z_{4b}
+ \ddashto \rm cycle$;\comment the signboard\cr
+$x_{10}=x_{11}=x_{12}=x_{13}=.5w-u$;
+ \ $x_{14}=x_{15}=x_{16}=x_{17}=w-x_{10}$;\cr
+$y_{10}=y_{14}={28\over27}h$; \ $\mathop{"bot"}y_{13}=-"baselinedistance"$;\cr
+$z_{11}=(z_{10}\to z_{13})\;{\rm intersectionpoint}\;
+ (z_{1a}\{z_{1a}-z_{4b}\}\to z_1\{"right"\})$;\cr
+$y_{15}=y_{11}$; \ $y_{16}=y_{12}=-y_{11}$; \ $y_{17}=y_{20}=y_{21}=y_{13}$;\cr
+@draw@ $z_{11}\dashto z_{10}\dashto z_{14}\dashto z_{15}$;
+ @draw@ $z_{12}\dashto z_{13}$;
+ @draw@ $z_{16}\dashto z_{17}$; \comment the signpost\cr
+$x_{20}=w-x_{21}$; \ $x_{21}-x_{20}=16u$;
+ \ @draw@ $z_{20}\dashto z_{21}$; \comment ground level\cr
+$x_{36}=w-x_{31}$; \ $x_{36}-x_{31}=8u$;
+ \ $x_{32}=x_{33}=x_{36}$; \ $x_{31}=x_{34}=x_{35}$;\cr
+$y_{31}=-y_{36}={12\over27}h$; \ $y_{32}=-y_{35}={9\over27}h$;
+ \ $y_{33}=-y_{34}={3\over27}h$;\cr
+\pickup @pencircle@ scaled "heavyline";\cr
+@draw@ $z_{32}\{z_{32}-z_{31}\}\to z_{33}\ddashto
+ z_{34}\to z_{35}\{z_{36}-z_{35}\}$;
+ \comment the dangerous bend\cr
+\pickup ^@penrazor@ xscaled "heavyline"
+ ^{rotated} (^{angle}$(z_{32}-z_{31})+90$);\cr
+@draw@ $z_{31}\dashto z_{32}$;
+ \ @draw@ $z_{35}\dashto z_{36}$; \comment upper and lower bars\cr
+^@labels@$(1a,1b,2a,2b,3a,3b,4a,4b,@range@ 1 @thru@ 36)$; \ @endchar@;
+^^@range@^^@thru@\cr
+}$$
+
+\setbox0=\vtop{\kern -5pt
+ \figbox{12f}{500\apspix}{4.2in}\vbox}
+\dp0=0pt
+\vskip 18pt
+
+{\tolerance=2000 \hbadness=2000 \spaceskip=.3333em plus .25em minus .12em
+\hangindent 515\apspix
+\noindent\hbox to 515\apspix{\box0\hfil}%
+This program has several noteworthy points of~interest:
+(1)~The first parameter to ^@beginchar@ here is 127, not a
+string; this puts the character into font ^{location}~127. \ (2)~A sequence
+of equations like `$a=w-b$; $a'=w-b'$' can conveniently be shortened to
+`$a+b=a'+b'=w$'. \ (3)~Three hyphens `$\ddashto$' is an abbreviation for a
+line with ``infinite'' tension, ^^{---} i.e., an almost straight line that
+connects smoothly to its curved neighbors. \ (4)~An `intersectionpoint'
+operation finds out where ^^{intersectionpoint}
+two paths cross; we'll learn more about this in Chapter~14.\par}
+
+\endchapter
+
+Well, we are in the same box.
+\author RIDER ^{HAGGARD}, {\sl Dawn\/} (1884) % beginning of chapter 47
+
+\bigskip
+
+A story, too,
+may be boxed.
+\author DOROTHY ^{COLBURN}, {\sl Newspaper Nomenclature\/} (1927)
+ % American Speech v2 Feb 27 p240
+
+\eject
+ \beginchapter Chapter 13. Drawing, Filling,\\and Erasing
+
+The pictures that \MF\ produces are made up of tiny pixels that are either
+``on'' or ``off''; therefore you might imagine that the computer works
+behind the scenes with some sort of ^{graph paper}, and that it darkens some
+of the squares whenever you tell it to @draw@ a line or to @fill@ a region.
+
+\newdimen\tinypix \setbox0=\hbox{\sixrm0} \tinypix=5pt
+\newdimen\pixcorr \pixcorr=\tinypix \advance\pixcorr by-\wd0
+\def\spread#1{\if#1!\let\next\relax\else#1\kern\pixcorr\let\next\spread\fi
+ \next}
+\def\beginpixdisplay{$$\advance\abovedisplayskip by 2pt
+ \advance\belowdisplayskip by-2pt
+ \baselineskip=\tinypix
+ \halign\bgroup\sixrm\indent\spread##!\hfil\cr}
+\MF's internal graph paper is actually more sophisticated than this.
+Pixels aren't simply ``on'' or ``off'' when \MF\ is working on a picture;
+they can be ``doubly on'' or ``triply off.'' Each pixel contains a
+small {\sl integer\/} value, and when a character is finally shipped out
+to a font the black pixels are those whose value is greater than zero.
+For example, the two commands
+\begindisplay
+^@fill@ $(0,3)\dashto(9,3)\dashto(9,6)\dashto(0,6)\dashto\cycle$;\cr
+@fill@ $(3,0)\dashto(3,9)\dashto(6,9)\dashto(6,0)\dashto\cycle$
+\enddisplay
+yield the following $9\times9$ pattern of pixel values:
+\beginpixdisplay
+000111000\cr
+000111000\cr
+000111000\cr
+111222111\cr
+111222111\cr
+111222111\cr
+000111000\cr
+000111000\cr
+000111000\cr
+\enddisplay
+Pixels that have been filled twice now have a value of 2.
+
+When a simple region is ``filled,'' its pixel values are all increased by~1;
+when it is ``unfilled,'' they are all decreased by~1. The command
+\begindisplay
+^@unfill@ $(1,4)\dashto(8,4)\dashto(8,5)\dashto(1,5)\dashto\cycle$
+\enddisplay
+will therefore change the pattern above to
+\beginpixdisplay
+000111000\cr
+000111000\cr
+000111000\cr
+111222111\cr
+100111001\cr
+111222111\cr
+000111000\cr
+000111000\cr
+000111000\cr
+\enddisplay
+The pixels in the center have not been erased (i.e., they will still be
+black if this picture is output to a font), because they still have a
+positive value.
+
+Incidentally, this example illustrates the fact that the edges between
+\MF's pixels are lines that have integer ^{coordinates}, just as the
+squares on graph paper do. For example, the lower left `{\sixrm0}' in
+the $9\times9$ array above corresponds to the pixel whose boundary is
+`$(0,0)\dashto(1,0)\dashto(1,1)\dashto(0,1)\dashto\cycle$'. The $(x,y)$
+coordinates of the points inside this pixel lie between 0 and~1.
+
+\exercise What are the $(x,y)$ coordinates of the four corners of the
+{\sl middle\/} pixel in the $9\times9$ array?
+\answer $(4,4)$, $(4,5)$, $(5,5)$, $(5,4)$. \ (Therefore the command
+\begindisplay
+@unfill@ $(4,4)\dashto(4,5)\dashto(5,5)\dashto(5,4)\dashto\cycle$
+\enddisplay
+will decrease the value of this pixel by 1.)
+
+\exercise What picture would have been obtained if the @unfill@ command
+had been given {\sl before\/} the two @fill@ commands in the examples
+above?
+\answer The result would be exactly the same; @fill@ and @unfill@ commands
+can be given in any order. \ (After an initial @unfill@ command, some
+pixel values will be $-1$, the others will be zero.)
+
+\exercise Devise an @unfill@ command that will produce the pixel values
+\beginpixdisplay
+000111000\cr
+000101000\cr
+000101000\cr
+111212111\cr
+100101001\cr
+111212111\cr
+000101000\cr
+000101000\cr
+000111000\cr
+\enddisplay
+when it is used just after the @fill@ and @unfill@ commands already given.
+\answer @unfill@ $(4,1)\dashto(4,8)\dashto(5,8)\dashto(5,1)\dashto\cycle$.
+
+A ``simple'' region is one whose boundary does not intersect itself; more
+complicated effects occur when the boundary lines cross. For example,
+\begindisplay
+@fill@ $(0,1)\dashto(9,1)\dashto(9,4)\dashto(4,4)\dashto$\cr
+\indent$(4,0)\dashto(6,0)\dashto
+ (6,3)\dashto(8,3)\dashto(8,2)\dashto(0,2)\dashto\cycle$\cr
+\enddisplay
+produces the pixel pattern
+\beginpixdisplay
+000011111\cr
+000011001\cr
+111122111\cr
+000011000\cr
+\enddisplay
+Notice that some pixels receive the value 2, because they're ``^{doubly
+filled}.'' There's also a ``^{hole}'' where the pixel values remain zero,
+even though they are surrounded by filled pixels; the pixels in that hole
+are not considered to be in the region, but the doubly filled pixels
+are considered to be in the region twice.
+
+\exercise Show that the first $9\times9$ cross pattern on the previous
+page can be generated by a single @fill@ command. \ (The nine pixel
+values in the center should be~2, as if two separate regions had been
+filled, even though you are doing only one @fill@.)
+\answer Here are two of the many solutions:
+\begindisplay
+@fill@ $(0,3)\dashto(9,3)\dashto(9,6)\dashto(6,6)\dashto(6,9)\dashto$\cr
+\indent $(3,9)\dashto(3,0)\dashto(6,0)\dashto(6,6)\dashto(0,6)\dashto\cycle$;\cr
+@fill@ $(0,3)\dashto(9,3)\dashto(9,6)\dashto(0,6)\dashto(0,3)\dashto$\cr
+\indent $(3,3)\dashto(3,0)\dashto(6,0)\dashto(6,9)\dashto(3,9)\dashto
+ (3,3)\dashto\cycle$.\cr
+\enddisplay
+(It turns out that {\sl any\/} pixel pattern can be obtained by a single,
+sufficiently hairy @fill@ command. But unnatural commands are usually also
+inefficient and unreadable.)
+
+\exercise What do you think is the result of `@fill@ $(0,0)\dashto(1,0)\dashto
+(1,1)\dashto(0,1)\dashto(0,0)\dashto(1,0)\dashto(1,1)\dashto(0,1)\dashto
+\cycle$'\thinspace?
+\answer The value of the enclosed pixel is increased by 2. \ (We'll see later
+that there's a simpler way to do this.)
+
+A @fill@ command can produce even stranger effects when its boundary lines
+cross in only one place. If you say, for example,
+\begindisplay
+@fill@ $(0,2)\dashto(4,2)\dashto(4,4)\dashto(2,4)\dashto(2,0)
+ \dashto(0,0)\dashto\cycle$
+\enddisplay
+\MF\ will produce the $4\times4$ pattern
+\setbox0=\hbox to\tinypix{\hss
+ $\scriptscriptstyle{\hbox to3pt{}\over}$\hss\kern\pixcorr}
+\dp0=0pt
+\beginpixdisplay
+0011\cr
+0011\cr
+!\copy0\copy0 \spread00\cr
+!\copy0\copy0 \spread00\cr
+\enddisplay
+where `$\hbox to3pt{}\over$' stands for the value $-1$. Furthermore the
+machine will report that you have a ``^{strange path}'' whose ``^{turning
+number}'' is zero! What does this mean? Basically, it means that your
+path loops around on itself something like a figure~8; this causes a
+breakdown in \MF's usual rules for distinguishing the ``inside'' and
+``outside'' of a curve.
+
+\danger Every cyclic path has a {\sl turning number\/} that can be understood
+as follows. Imagine that you are driving a car along the path and that you
+have a digital compass that tells in what direction you're heading. For
+example, if the path is
+\begindisplay
+$(0,0)\dashto(2,0)\dashto(2,2)\dashto(0,2)\dashto\cycle$
+\enddisplay
+you begin driving in direction $0^\circ$, then you make four left turns.
+After the first turn, your compass heading is $90^\circ$; after the
+second, it is $180^\circ$; and after the third it is $270^\circ$. \ (The
+compass direction increases when you turn left and decreases when you turn
+right; therefore it now reads $270^\circ$, not $-90^\circ$.) \ At the
+end of this cycle the compass will read $360^\circ$, and if you go around
+again the reading will be $720^\circ$. Similarly, if you had traversed the
+path
+\begindisplay
+$(0,0)\dashto(0,2)\dashto(2,2)\dashto(2,0)\dashto\cycle$
+\enddisplay
+(which is essentially the same, but in the opposite direction), your compass
+heading would have started at $90^\circ$ and ended at $-270^\circ$;
+in this case each circuit would have {\sl decreased\/} the reading
+by~$360^\circ$. It is clear that a drive around any cyclic path will
+change the compass heading by some multiple of~$360^\circ$, since you
+end in the same direction you started. The turning number of a path is
+defined to be $t$ if the compass heading changes by exactly $t$~times
+$360^\circ$ when the path is traversed. Thus, the two example cycles we have
+just discussed have turning numbers of $+1$ and $-1$, respectively; and
+the ``strange path'' on the previous page that produced both positive and
+negative pixel values does indeed have a turning number of~0.
+
+\danger Here's how \MF\ actually implements a @fill@ command, assuming that
+the cyclic path being filled has a {\sl positive\/} turning number:
+The path is first ``^{digitized},'' if necessary, so that it lies entirely on
+the edges of pixels; in other words, it is distorted slightly so that it
+is confined to the lines between pixels on graph paper. \ (Our examples so
+far in this chapter have not needed any such adjustments.) \ Then each
+individual pixel value is increased by~$j$ and decreased by~$k$ if an
+infinite horizontal line to the left of that pixel intersects the
+digitized path $j$~times when the path is traveling downward and $k$~times
+when it is traveling upward. For example, let's look more closely at the
+non-simple path on the previous page that enclosed a hole:
+$$\def\\#1{\hbox to 11pt{\hss$#1$\hss}}
+\def\up{\hbox to0pt{\hss\lower3pt\vbox to 11pt{
+ \hbox{\tenex\char'77}\vss\hbox{\tenex\char'170}\kern0pt}\hss}}
+\def\down{\hbox to0pt{\hss\lower3pt\vbox to 11pt{
+ \hbox{\tenex\char'171}\vss\hbox{\tenex\char'77}\kern0pt}\hss}}
+\def\under{\smash{\rlap{\lower3.2pt\vbox{\hrule width 11pt}}}}
+\def\over{\smash{\rlap{\raise7.8pt\vbox{\hrule width 11pt}}}}
+\halign{\indent#\cr
+\\a\\a\\a\\a\over\down\\b\over\\b\over\under\\b\over\under\\b\over\\b\up\cr
+\under\\a\under\\a\under\\a\under\\a\down\under\\b\under\\b\up
+ \under\\c\under\\c\down\\d\up\cr
+\down\under\\e\under\\e\under\\e\under\\e\down\under\\f\under\\f\up
+ \under\\g\under\\g\under\\g\up\cr
+\\a\\a\\a\\a\down\under\\b\under\\b\up\\h\\h\\h\cr}$$
+Pixel $d$ has $j=2$ descending edges and $k=1$ ascending edges to its left,
+so its net value increases by $j-k=1$; pixels~$g$ are similar.
+Pixels~$c$ have $j=k=1$, so they lie in a ``hole'' that is unfilled;
+pixels~$f$ have $j=2$ and $k=0$, so they are doubly filled. This rule
+works because, intuitively, the inside of a region lies at the {\sl left\/}
+of a path whose turning number is positive.
+
+\dangerexercise True or false: When the turning number of a cyclic path is
+positive, a @fill@ command increases each individual pixel value by $l-m$,
+if an infinite horizontal line to the {\sl right\/} of that pixel intersects
+the digitized path $l$~times when the path is traveling upward and $m$~times
+when it is traveling downward. \ (For example, pixels~$e$ have $l=2$ and
+$m=1$; pixels~$c$ have $l=m=1$.)
+\answer True; $j-k=l-m$, since $k+l=j+m$. \ (What comes up must go down.)
+
+\danger When the turning number is negative, a similar rule applies,
+except that the pixel values are {\sl decreased\/} by~$j$ and {\sl
+increased\/} by~$k$; in this case the inside of the region lies at the
+{\sl right\/} of the path.
+
+\danger But when the turning number is zero, the inside of the region
+lies sometimes at the left, sometimes at the right. \MF\ uses the rule
+for positive turning number and reports that the path is ``strange.''
+You can avoid this error message by setting `$"turningcheck":=0$';
+^^"turningcheck" in this case the rule for positive turning number is
+always used for filling, even when the turning number is negative.
+
+Plain \MF's ^@draw@ command is different from @fill@ in two important ways.
+First, it uses the currently-picked-up pen, thereby ``thickening'' the path.
+Second, it does not require that the path be cyclic. There is also a third
+difference, which needs to be mentioned although it is not quite as important:
+A @draw@ command may increase the value of certain pixels by more than~1,
+even if the shape being drawn is fairly simple. For example, the pixel pattern
+{\parindent=0pt
+\beginpixdisplay
+0000000000000000000000000000000000000000000000000000000000000000000000\cr
+0000001111122222111110000000000000000000000000011111111000000000000000\cr
+0000111111111211111111100000000000000000000011111111111111000000000000\cr
+0001111111111011111111110000000000000000001111111111111111110000000000\cr
+0001111111111011111111110000000000000000111111111111111111111100000000\cr
+0011111111110001111111111000000000000001111111111111111111111110000000\cr
+0011111111110001111111111000000000000011111111111111111111111111000000\cr
+0011111111110001111111111000000000000111111111111111111111111111100000\cr
+0111111111100000111111111100000000001111111111111111111111111111110000\cr
+0111111111100000111111111100000000001111111111111111111111111111110000\cr
+0111111111100000111111111100000000011111111111111111111111111111111000\cr
+0111111111100000111111111100000000011111111111111111111111111111111000\cr
+0111111111100000111111111100000000111111111111111112111111111111111100\cr
+0111111111100000111111111100000000111111111111111112111111111111111100\cr
+0111111111100000111111111100000001111111111111111122111111111111111110\cr
+0111111111100000111111111100000001111111111111211121111211111111111110\cr
+0111111111100000111111111100000001111111111111112122221111111111111110\cr
+0111111111100000111111111100000001111111111111111100111111111111111110\cr
+0111111111100000111111111100000001111111111111112000011111111111111110\cr
+0111111111100000111111111100000001111111111112211000011211111111111110\cr
+0111111111100000111111111100000000111111111111110000001111111111111100\cr
+0111111111100000111111111100000000111111111111110000001111111111111100\cr
+0111111111100000111111111100000000011111111111100000000111111111111000\cr
+0111111111100000111111111100000000001111111111000000000011111111110000\cr
+0111111111100000111111111100000000000011111100000000000000111111000000\cr
+0000000000000000000000000000000000000000000000000000000000000000000000\cr
+\enddisplay}%
+was produced by two @draw@ commands. The left-hand shape came from
+\begindisplay
+\pickup ^@penrazor@ scaled 10;\quad \% a pen of width 10 and height 0\cr
+@draw@ $(6,1)\{"up"\}\to(13.5,25)\to\{"down"\}(21,1)$;\cr
+\enddisplay
+it's not difficult to imagine why some of the top pixels get the value~2
+here because an actual razor-thin pen would cover those pixels twice as it
+follows the given path. But the right-hand shape, which came from
+\begindisplay
+\pickup @pencircle@ scaled 16; \ @draw@ $(41,9)\to(51,17)\to(61,9)$
+\enddisplay
+is harder to explain; there seems to be no rhyme or reason to the pattern
+of 2's in that case. \MF's method for drawing curves with thick pens is
+too complicated to explain here, so we shall just regard it as a curious
+process that occasionally shoots out extra spurts of ink in the interior
+of the shape that it's filling. Sometimes a pixel value even gets as high
+as 3~or more; but if we ignore such anomalies and simply consider the set
+of pixels that receive a positive value, we find that a reasonable shape
+has been drawn.
+
+The left-parenthesis example in Chapter 12 illustrates the ^@filldraw@
+command, which is like @fill@ in that it requires a cyclic path, and like
+@draw@ in that it uses the current pen. Pixel values are increased inside
+the region that you would obtain by drawing the specified path with the current
+pen and then filling in the interior. Some of the pixel values in this
+region may increase by 2~or more. The turning number of the path
+should be nonzero.
+
+Besides @fill@, @draw@, and @filldraw@, you can also say `^@drawdot@',
+as illustrated at the beginning of Chapter~5. In this case you should specify
+only a single point; the currently-picked-up pen will be used to increase
+pixel values by~1 around that point. Chapter~24 explains that this gives
+slightly better results than if you were to draw a one-point path.
+
+\danger There's also an ^@undraw@ command, analogous to @unfill@; it
+decreases pixel values by the same amount that @draw@ would increase them.
+Furthermore---as you might expect---^@unfilldraw@ and ^@undrawdot@ are the
+respective opposites of @filldraw@ and @drawdot@.
+
+\danger If you try to use @unfill@ and/or @undraw@ in connection with
+@fill@ and/or @draw@, you'll soon discover that something else is
+necessary. Plain \MF\ has a ^@cullit@ command that replaces all
+negative pixel values by~0 and all positive pixel values by~1. This
+``^{culling}'' operation makes it possible to erase unwanted sections
+of a picture in spite of the vagaries of @draw@ and @undraw@, and in spite of
+the fact that overlapping regions may be doubly filled.
+
+\danger The command `^@erase@ @fill@ $c$' is an abbreviation for
+`@cullit@; @unfill@~$c$; @cullit@'; this zeros out the pixel values inside
+the cyclic path~$c$, and sets other pixel values to~1 if they were positive
+before erasing took place. \ (It works because the initial @cullit@ makes
+all the values 0 or~1, then the @unfill@ changes the values inside~$c$
+to 0 or negative. The final @cullit@ gets rid of the negative values,
+so that they won't detract from future filling and drawing.) \ You can
+also use `@draw@', `@filldraw@', or `@drawdot@' with `@erase@'; for example,
+`@erase@ @draw@~$p$' is an abbreviation for `@cullit@; @undraw@~$p$;
+@cullit@', which uses the currently-picked-up pen as if it were an
+eraser applied to path~$p$.
+
+{\ninepoint
+\medbreak
+\parshape 7 3pc 17pc 3pc 17pc
+0pc 20pc 0pc 20pc 0pc 20pc 0pc 20pc 0pc 29pc
+\noindent
+\hbox to0pt{\hskip-3pc\dbend\hfill}%
+\rightfig 13a ({166.66667\apspix} x {133.33333\apspix}) ^9pt
+The cube at the right of this paragraph illustrates one of the effects that
+is easily obtained by erasing. First the eight points are defined, and
+the ``back'' square is drawn; then two lines of the ``front'' square are
+erased, using a somewhat thicker pen; finally the remaining lines are
+drawn with the ordinary pen:
+\begindisplay
+$s\0:=5"pt"\0$; \ @define\_pixels@$(s)$; \ |%| side of the square\cr
+$z_1=(0,0)$; \ $z_2=(s,0)$; \ $z_3=(0,s)$; $z_4=(s,s)$;\cr
+^@for@ $k=1$ @upto@ 4: $z_{k+4}=z_k+({2\over3}s,{1\over3}s)$; \ @endfor@\cr
+\pickup @pencircle@ scaled $.4"pt"$; \
+@draw@ $z_5\dashto z_6\dashto z_8\dashto z_7\dashto \cycle$;\cr
+\pickup @pencircle@ scaled $1.6"pt"$; \
+@erase@ @draw@ $z_2\dashto z_4\dashto z_3$;\cr
+\pickup @pencircle@ scaled $.4"pt"$; \
+@draw@ $z_1\dashto z_2\dashto z_4\dashto z_3\dashto \cycle$;\cr
+@for@ $k=1$ @upto@ 4: @draw@ $z_k\dashto z_{k+4}$; \ @endfor@.\cr
+\enddisplay
+At its true size the resulting ^{cube} looks like this:
+`\thinspace{\manual\cubea}\thinspace'.\par}
+
+\dangerexercise Modify the draw-and-erase construction in the preceding
+paragraph so that you get the {\sl^{impossible cube}\/}
+`\thinspace{\manual\cubeb}\thinspace' instead.
+\answer The tricky part is to remember that `@erase@ @draw@ $z_i\dashto z_j$'
+will erase pixels near $z_i$ and $z_j$. Therefore if $z_3\dashto z_4$ is
+drawn before $z_4\dashto z_2$, we can't erase $z_4\dashto z_2$ without losing
+some of $z_3\dashto z_4$; it's necessary to erase only part of one line.
+One way to solve the problem is to do the following, after defining the
+points and picking up the pen as before:
+\begindisplay
+@draw@ $z_3\dashto z_4$; \ @draw@ $z_5\dashto z_6$;\cr
+^@cullit@; \ \pickup @pencircle@ scaled $1.6"pt"$;\cr
+^@undraw@ $z_7\dashto {1\over2}[z_7,z_5]$; \
+ @undraw@ $z_2\dashto {1\over2}[z_2,z_4]$;\cr
+@cullit@; \ \pickup @pencircle@ scaled $.4"pt"$;\cr
+@draw@ $z_3\dashto z_1\dashto z_2\dashto z_4$; \
+ @draw@ $z_5\dashto z_7\dashto z_8\dashto z_6$;\cr
+@for@ $k=1$ @upto@ 4: \ @draw@ $z_k\dashto z_{k+4}$; \ @endfor@.\cr
+\enddisplay
+(Note that it would not be quite enough to erase only from $z_7$ to
+${1\over3}[z_7,z_5]$!)\par
+It's also possible to solve this problem without partial erasing, if we
+use additional features of \MF\ that haven't been explained yet. Let's
+consider only the job of drawing $z_7\dashto z_5\dashto z_6$ and
+$z_3\dashto z_4\dashto z_2$, since the other eight lines can easily be
+added later. Alternative Solution~1 uses picture operations:
+\begindisplay
+@pen@ "eraser"; \ $"eraser"=@pencircle@$ scaled $1.6"pt"$;\cr
+@draw@ $z_3\dashto z_4$; \
+@erase@ @draw@ $z_7\dashto z_5$ ^@withpen@ "eraser"; \
+@draw@ $z_7\dashto z_5$;\cr
+@picture@ "savedpicture"; \ $"savedpicture"="currentpicture"$; \ ^@clearit@;\cr
+@draw@ $z_6\dashto z_5$; \
+@erase@ @draw@ $z_2\dashto z_4$ ^@withpen@ "eraser"; \
+@draw@ $z_2\dashto z_4$;\cr
+^@addto@ "currentpicture" @also@ "savedpicture".\cr
+\enddisplay
+Alternative Solution 2 is trickier, but still instructive; it uses
+`^@withweight@' options and the fact that @draw@ does not increase any
+pixel values by more than the stated weight when the path is a straight
+line:
+\begindisplay
+@draw@ $z_3\dashto z_4$; \
+^@undraw@ $z_7\dashto z_5$ @withpen@ "eraser";\cr
+@draw@ $z_7\dashto z_5$ @withweight@ 2; \
+^@cullit@ @withweight@ 2;\cr
+@draw@ $z_6\dashto z_5$; \
+^@undraw@ $z_2\dashto z_4$ @withpen@ "eraser";\cr
+@draw@ $z_2\dashto z_4$ @withweight@ 2;\cr
+\enddisplay
+(These alternative solutions were suggested by Bruce ^{Leban}.)
+
+\dangerexercise Write a \MF\ program to produce the symbol
+`{\manual\bicentennial}'. \ [{\sl Hints:\/} The character is $10\pt$
+wide, $7\pt$ high, and $2\pt$ deep. The starlike path can be defined by
+five points connected by ``tense'' lines as follows:
+\begindisplay
+@pair@ "center"; \ $"center"=(.5w,2"pt")$;\cr
+@numeric@ "radius"; \ $"radius"=5"pt"$;\cr
+@for@ $k=0$ @upto@ 4: \ $z_k="center"+("radius",0)$
+ ^{rotated}$(90+{360\over5}k)$; \ @endfor@\cr
+@def@ :: = ^^{tension} $\to\tension 5\to$ @enddef@;\cr
+@path@ "star"; \ $"star"=z_0::z_2::z_4::z_1::z_3::\cycle$;\cr
+\enddisplay
+You probably want to work with ^{subpaths} of ^"star" instead of drawing the
+whole path at once, in order to give the illusion that the curves cross over
+and under each other.]
+\answer Here's an analog of the first solution to the previous
+exercise:
+\begindisplay
+@beginchar@\kern1pt(|"*"|$,10"pt"\0,7"pt"\0,2"pt"\0)$;\cr
+@pair@ "center"; \dots \<as in the hint>\cr
+\pickup @pencircle@ scaled $.4"pt"$; \ @draw@ "star";\cr
+@cullit@; \ \pickup @pencircle@ scaled $1.6"pt"$;\cr
+@for@ $k=0$ @upto@ 4:
+ \ @undraw@ subpath$(k+.55,k+.7)$ @of@ "star"; \ @endfor@\cr
+@cullit@; \ \pickup @pencircle@ scaled $.4"pt"$;\cr
+@for@ $k=0$ @upto@ 4: \ @draw@ subpath$(k+.47,k+.8)$ @of@ "star"; \ @endfor@\cr
+@labels@(0,1,2,3,4); \ @endchar@.\cr
+\enddisplay
+However, as in the previous case, there's an Alternate Solution~1
+by Bruce ^{Leban} that is preferable because it doesn't depend
+on magic constants like .55 and~.47:
+\begindisplay
+@beginchar@ $\ldots$ \<as above> $\ldots$ scaled $.4"pt"$;\cr
+@picture@ "savedpicture"; \ $"savedpicture"=@nullpicture@$;\cr
+@pen@ "eraser"; \ $"eraser":=@pencircle@$ scaled $1.6"pt"$;\cr
+@for@ $k=0$ @upto@ 4:\cr
+\indent @draw@ subpath$(k,k+1)$ @of@ "star"; @cullit@;\cr
+\indent @undraw@ subpath$(k+3,k+4)$ @of@ "star" @withpen@ "eraser"; @cullit@;\cr
+\indent @addto@ "savedpicture" @also@ "currentpicture"; @clearit@; @endfor@\cr
+$"currentpicture":="savedpicture"$; \ @labels@(0,1,2,3,4); \ @endchar@.\cr
+\enddisplay
+
+\dangerexercise What does the command `@fill@ "star"' do, if "star" is the
+path defined above?
+\answer It increases pixel values by 1 in the five lobes of the star, and by~2
+in the central pentagon-like region.
+
+\decreasehsize 6pc
+\dangerexercise Devise a ^{macro} called `^@overdraw@' such that the command
+\rightfig 13aa (50pt x 100pt) ^11pt
+`@overdraw@~$c$' will erase the inside of region~$c$ and will then draw the
+boundary of~$c$ with the currently-picked-up pen, assuming that $c$~is a
+cyclic path that doesn't intersect itself. \ (Your macro could be used, for
+example, in the program
+\begindisplay
+@path@ $S$; \ $S=((0,1)\to(2,0)\to(4,2)\to$\cr
+\indent$(2,5.5)\to(0,8)\to(2,10)\to(3.5,9))$ scaled $9"pt"$;\cr
+@for@ $k=0$ @upto@ 35: @overdraw@ ^"fullcircle" scaled 3"mm"\cr
+\indent shifted ^{point} $k/35\ast \mathop{\rm length} S$ @of@ $S$;
+ @endfor@\cr
+\enddisplay
+to create the curious ^{S} shown here.)
+\answer @def@ @overdraw@ @expr@ $c$ = @erase@ @fill@ $c$; @draw@ $c$ @enddef@.
+
+\restorehsize
+\ddangerexercise The ^{M\"obius} Watchband Corporation has a logo that
+looks like this:
+\displayfig 13bb (.5in)
+Explain how to produce it (or something very similar) with \MF\!.
+\answer First we need to generalize the ^@overdraw@ macro of the previous
+exercise so that it applies to arbitrary cycles~$c$, even those that are
+self-intersecting:
+\begindisplay
+@def@ @overdraw@ @expr@ $c$ = ^@begingroup@\cr
+\indent@picture@ "region"; $"region":=@nullpicture@$;\cr
+\indent^@interim@ $"turningcheck":=0$; ^@addto@ "region" @contour@ $c$;\cr
+\indent^@cull@ "region" @dropping@ $(0,0)$;\cr
+\indent^@cullit@; @addto@ "currentpicture" ^@also@ $-"region"$; @cullit@;\cr
+\indent@draw@ $c$ ^@endgroup@ @enddef@;\cr
+\enddisplay
+(This code uses operations defined later in this chapter; it erases the
+"region" of pixels that would be made nonzero by the command `@fill@~$c$'.)
+\ The watchband is now formed by overdrawing its links, one at a time,
+doing first the ones that are underneath:
+\begindisplay
+@beginchar@$("M",1.25"in"\0,.5"in"\0,0)$; \
+ \pickup @pencircle@ scaled .4"pt";\cr
+$z_1=(20,-13)$; \ $z_2=(30,-6)$; \ $z_3=(20,1)$; $z_4=(4,-7)$;\cr
+\indent $z_5=(-12,-13)$; \ $z_6=(-24,-4)$; \ $z_7=(-15,6)$;\cr
+@path@ $M$; $M=("origin"\to z1\to z2\to z3\to z4\to z5\to z6\to z7\to$\cr
+\indent$"origin"\to -z7\to -z6\to -z5\to -z4\to -z3\to -z2\to -z1\to\cycle)$\cr
+^^"origin" \indent\indent scaled $(h/26)$ shifted $(.5w,.5h)$;\cr
+@def@ @link@(@expr@ $n$) =\cr
+\indent @overdraw@ subpath ${1\over3}(n,n+1)$ of $M\;\dashto$\cr
+\indent\indent subpath ${1\over3}(n+25,n+24)$ of $M\;\dashto\;\cycle\;$
+ @enddef@;\cr
+@for@ $k=1$ @upto@ 12: @link@$(k+11)$; @link@$(12-k)$; @endfor@
+@endchar@;\cr
+\enddisplay
+
+\danger Chapter 7 points out that variables can be of type `^@picture@',
+and Chapter~8 mentions that expressions can be of type `@picture@', but
+we still haven't seen any examples of picture variables or picture
+expressions. Plain \MF\ keeps the currently-worked-on picture in a
+picture variable called ^"currentpicture", and you can copy it by
+equating it to a picture variable of your own. For example, if you
+say `@picture@ $v[\,]$' at the beginning of your program, you can write
+equations like
+\begindisplay
+$v_1="currentpicture"$;
+\enddisplay
+this makes $v_1$ equal to the picture that has been drawn so far; i.e.,
+it gives $v_1$ the same array of pixel values that "currentpicture" now has.
+
+\begingroup\def\dbend{{\manual\char0}} % reverse-video dangerous bend sign
+\danger Pictures can be added or subtracted; for example, $v_1+v_2$
+^^{sum of pictures} ^^{negative of a picture} ^^{inverse video}
+stands for the picture whose pixel values are the sums of the pixel
+values of $v_1$ and~$v_2$. The ``^{reverse-video} ^{dangerous bend}'' sign that
+heads this paragraph was made by substituting the following code for
+the `@endchar@' in the program at the end of Chapter~12:
+\begindisplay
+@picture@ "dbend"; \ $"dbend"="currentpicture"$;\cr
+@endchar@; \ |%| end of the normal dangerous bend sign\cr
+@beginchar@$(0,25u\0,"h\_height"\0+"border"\0,0)$;\cr
+@fill@ $(0,-11"pt")\dashto(w,-11"pt")\dashto(w,h)\dashto(0,h)\dashto\cycle$;\cr
+$"currentpicture":="currentpicture"-"dbend"$;\cr
+@endchar@;\ |%| end of the reversed dangerous bend sign\cr
+\enddisplay
+^^{black/white reversal} The pixel values in "dbend" are all zero or more;
+thus the pixels with a positive value, after "dbend" has been subtracted from
+a filled rectangle, will be those that are inside the rectangle
+but zero in "dbend".
+
+\endgroup % back to normal \dbend
+
+\danger We will see in Chapter 15 that pictures can also be shifted,
+reflected, and rotated by multiples of $90^\circ$. For example,
+the statement `$"currentpicture":="currentpicture"$~shifted~3"right"'
+shifts the entire current picture three pixels to the right.
+
+\danger There's a ``constant'' picture called ^@nullpicture@, whose pixel
+values are all zero;
+plain \MF\ defines `^@clearit@' to be an abbreviation for the
+assignment `"currentpicture":=@nullpicture@'. The current picture is
+cleared automatically by every ^@beginchar@ and ^@mode\_setup@ command,
+so you usually don't have to say `@clearit@' in your own programs.
+
+\danger Here's the formal syntax for picture expressions. Although \MF\ has
+comparatively few built-in operations that deal with entire pictures,
+the operations that do exist have the same syntax as the similar operations
+we have seen applied to numbers and pairs.
+\beginsyntax
+<picture primary>\is<picture variable>
+ \alt[nullpicture]
+ \alt[(]<picture expression>[)]
+ \alt<plus or minus><picture primary>
+<picture secondary>\is<picture primary>
+ \alt<picture secondary><transformer>
+<picture tertiary>\is<picture secondary>
+ \alt<picture tertiary><plus or minus><picture secondary>
+<picture expression>\is<picture tertiary>
+\endsyntax
+
+\danger The ``total weight'' of a picture is the sum of all its pixel
+values, divided by 65536; you can compute this numeric quantity by
+saying
+\begindisplay
+^|totalweight| \<picture primary>.
+\enddisplay
+\MF\ divides by 65536 in order to avoid overflow in case of huge pictures.
+If the totalweight function returns a number whose absolute
+value is less than~.5, as it almost always is, you can safely divide that number
+by ^"epsilon" to obtain the integer sum of all pixel values
+(since $"epsilon"=1/65536$).
+
+\danger Let's turn to the computer again and try to evaluate some simple
+picture expressions interactively, using the general routine |expr.mf|
+of Chapter~8. When \MF\ says `|gimme|', you can type
+\begintt
+hide(fill unitsquare) currentpicture
+\endtt
+and the machine will respond as follows:
+\begintt
+>> Edge structure at line 5:
+row 0: 0+ 1- ||
+\endtt
+What does this mean? Well, `^@hide@' is plain \MF's sneaky way to insert
+a command or sequence of commands into the middle of an expression; such
+commands are executed before the rest of the expression is looked at. In
+this case the command `@fill@ "unitsquare"' sets one pixel value of the
+current picture to~1, because ^"unitsquare" is plain \MF's abbreviation
+for the path $(0,0)\dashto(1,0)\dashto(1,1)\dashto(0,1)\dashto\cycle$. The
+value of "currentpicture" is displayed as `|row|~|0:| |0+|~|1-|', because
+this means
+``in row~0, the pixel value increases at $x=0$ and decreases at $x=1$.''
+
+\danger \MF\ represents pictures internally by remembering only the vertical
+^{edges} where pixel values change. For example, the picture just displayed
+has just two edges, both in row~0, i.e., both in the row between $y$~coordinates
+0 and~1. \ (Row~$k$ contains vertical edges whose $x$~coordinates are integers
+and whose $y$~coordinates run between $k$ and $k+1$.) \ The fact that edges
+are represented, rather than entire arrays of pixels, makes it possible for
+\MF\ to operate efficiently at high resolutions, because the number of edges
+in a picture is essentially proportional to the ^{resolution} while the total
+number of pixels is proportional to the resolution {\sl squared}. A ten-fold
+increase in resolution therefore calls for only a ten-fold (rather than a
+hundred-fold) increase in memory space and execution time.
+
+\def\pixpat#1#2#3#4{\vcenter{\sixrm\baselineskip=\tinypix
+ \hbox{#1\kern\pixcorr#2}\hbox{#3\kern\pixcorr#4}}}
+\ddanger Continuing our computer experiments, let's declare a picture variable
+and fill a few more pixels:
+\begintt
+hide(picture V; fill unitsquare scaled 2; V=currentpicture) V
+\endtt
+The resulting picture has pixel values $\pixpat1121\,$,
+and its edges are shown thus:
+\begintt
+>> Edge structure at line 5:
+row 1: 0+ 2- ||
+row 0: 0+ 2- 0+ 1- ||
+\endtt
+If we now type `|-V|', the result is similar but with the signs changed:
+\begintt
+>> Edge structure at line 5:
+row 1: 0- 2+ ||
+row 0: 0- 2+ 0- 1+ ||
+\endtt
+(You should be doing the experiments as you read this.) \ A more interesting
+picture transformation occurs if we ask for `|V|~|rotated-90|'; the picture
+$\pixpat2111$ appears below the baseline, hence the following edges are shown:
+\begintt
+>> Edge structure at line 5:
+row -1: || 0++ 1- 2-
+row -2: || 0+ 2-
+\endtt
+Here `^|++|' denotes an edge where the weight increases by 2. The edges appear
+^^|+++| {\sl after\/} ^{vertical line}s `\|' in this case, while they appeared
+{\sl before\/} vertical lines in the previous examples; this means that \MF\
+has sorted the edges by their $x$~coordinates. Each @fill@ or @draw@ instruction
+contributes new edges to a picture, and unsorted edges accumulate until
+\MF\ needs to look at them in left-to-right order. \ (Type
+\begintt
+V rotated-90 rotated 90
+\endtt
+to see what $V$ itself looks like when its edges have been sorted.) \ The
+expression
+\begintt
+V + V rotated 90 shifted 2right
+\endtt
+produces an edge structure with both sorted and unsorted edges:
+\begintt
+>> Edge structure at line 5:
+row 1: 0+ 2- || 0+ 2-
+row 0: 0+ 2- 0+ 1- || 0+ 1+ 2--
+\endtt
+In general, addition of pictures is accomplished by simply combining the
+unsorted and sorted edges of each row separately.
+
+\ddangerexercise Guess what will happen if you type `|hide(cullit)|
+|currentpicture|' now; and verify your guess by actually doing the experiment.
+\answer The pixel pattern $\pixpat1121$ is culled to $\pixpat1111\,$,
+and \MF\ needs to sort the edges as it does this; so the result is simply
+\begintt
+row 1: || 0+ 2-
+row 0: || 0+ 2-
+\endtt
+
+\ddangerexercise Guess (and verify) what will happen when you type the
+expression
+\begintt
+(V + V + V rotated 90 shifted 2right
+ - V rotated-90 shifted 2up) rotated 90.
+\endtt
+[You must type this monstrous formula all on one line, even though it's too
+long to fit on a single line in this book.]
+\answer The pixel pattern is $\pixpat1121+\pixpat1121+\pixpat1112-\pixpat2111
+=\pixpat1243$ before the final rotation, with the reference point at the
+lower left corner of the~4; after rotation it is $\pixpat2314\,$, with the
+reference point at the lower {\sl right\/} corner of the~4. Rotation causes
+\MF\ to sort the edges, but the transition values per edge are never
+more than $\pm3$. You weren't expected to know about this limit of $\pm3$,
+but it accounts for what is actually reported:
+\begintt
+row 1: || -2++ -1+ 0---
+row 0: || -2+ -1+++ 0--- 0-
+\endtt
+
+\ddanger If you ask for `|V| |rotated| |45|', \MF\ will complain that
+$45^\circ$ rotation is too hard. \ (Try it.) \ After all, square pixels
+can't be ^{rotated} unless the angle of rotation is a multiple of $90^\circ$.
+On the other hand, `|V|~|scaled-1|' does work; you get
+\begintt
+>> Edge structure at line 5:
+row -1: 0- -2+ 0- -1+ ||
+row -2: 0- -2+ ||
+\endtt
+
+\ddangerexercise Why is `|V| |scaled-1|' different from `|-V|'\thinspace?
+\answer `|V| |scaled-1|' should be the same as `|V| |rotated| |180|',
+because transformations apply to coordinates rather than to pixel values.
+\ (Note, incidentally, that the reflections `|V|~^|xscaled-1|' and
+`|V|~^|yscaled-1|' both work, and that `|V|~|scaled-1|' is the same as
+`|V|~|xscaled-1| |yscaled-1|'.)
+
+\ddangerexercise Experiment with `|V| |shifted| |(1.5,3.14159)|' and
+^^{shifted} explain what happens.
+\answer The result is the same as `|V| |shifted| |(2,3)|'; the coordinates
+of a shift are rounded to the nearest integers when a picture is being shifted.
+
+\ddangerexercise Guess and verify the result of `|V| |scaled| |2|'.
+\answer |row 3: 0+ 4- |\|\parbreak
+|row 2: 0+ 4- |\|\parbreak
+|row 1: 0+ 4- 0+ 2- |\|\parbreak
+|row 0: 0+ 4- 0+ 2- |\|\par\nobreak
+\smallskip\noindent
+(Scaling of pictures must be by an integer.)
+
+\ddangerexercise Why does the machine always speak of an ^{edge structure}
+`|at| |line|~|5|'\thinspace?
+\answer \MF\ is currently executing instructions after having read
+as far as line~5 of the file |expr.mf|.
+
+\ddanger That completes our computer experiments. But before you log off,
+you might want to try typing `|totalweight V/epsilon|', just to verify
+that the sum of all pixel values in~$V$ is~5.
+
+\danger The commands we have discussed so far in this chapter---@fill@,
+@draw@, @filldraw@, @unfill@, etc.---are not really primitives of \MF;
+they are macros of plain \MF\!, defined in Appendix~B\null. Let's look now
+at the low-level operations on pictures that \MF\ actually performs
+behind the scenes. Here is the syntax:
+\beginsyntax
+<picture command>\is<addto command>\alt<cull command>
+<addto command>\is[addto]<picture variable>[also]<picture expression>
+ \alt[addto]<picture variable>[contour]<path expression><with list>
+ \alt[addto]<picture variable>[doublepath]<path expression><with list>
+<with list>\is<empty>\alt<with list><with clause>
+<with clause>\is[withpen]<pen expression>%
+ \alt[withweight]<numeric expression>\kern-3.5pt
+<cull command>\is[cull]<picture variable><keep or drop><pair expression>
+ \alt<cull command>[withweight]<numeric expression>
+<keep or drop>\is[keeping]\alt[dropping]
+\endsyntax
+The \<picture variable> in these commands should contain a known picture;
+the command modifies that picture, and assigns the resulting new value
+to the variable.
+
+\danger The first form of \<addto command>, `@addto@ $V$ @also@~$P$',
+has essentially the same meaning as `$V:=V+P$'. But the @addto@ statement
+is more efficient, because it destroys the old value of~$V$ as it adds~$P$;
+this saves both time and space. Earlier in this chapter we discussed
+the ^{reverse-video} ^{dangerous bend}, which was said to have been
+formed by the statement `$"currentpicture":="currentpicture"-"dbend"$'.
+That was a little white lie; the actual command was
+`@addto@ "currentpicture" @also@ $-"dbend"$'.
+
+\danger The details of the other forms of `@addto@' are slightly more
+complex, but (informally) they work like this, when $V="currentpicture"$
+and $q=\null$^"currentpen":
+\begindisplay
+Plain \MF&Corresponding \MF\ primitives\cr
+\noalign{\smallskip}
+^@fill@ $c$&@addto@ $V$ @contour@ $c$\cr
+^@unfill@ $c$&@addto@ $V$ @contour@ $c$ @withweight@ $-1$\cr
+^@draw@ $p$&@addto@ $V$ @doublepath@ $p$ @withpen@ $q$\cr
+^@undraw@ $p$&@addto@ $V$ @doublepath@ $p$ @withpen@ $q$ @withweight@ $-1$\cr
+^@filldraw@ $c$&@addto@ $V$ @contour@ $c$ @withpen@ $q$\cr
+^@unfilldraw@ $c$&@addto@ $V$ @contour@ $c$ @withpen@ $q$ @withweight@ $-1$\cr
+\enddisplay
+
+\ddanger The second form of \<addto command> is `@addto@ $V$ @contour@ $p$',
+followed by optional clauses that say either `@withpen@~$q$' or
+`@withweight@~$w$'. In this case $p$~must be a cyclic path; each pen~$q$
+must be known; and each weight~$w$ must be either $-3$,~$-2$, $-1$, $+1$,
+$+2$, or~$+3$, when rounded to the nearest integer. If more than one pen or
+weight is given, the last specification overrides all previous ones. If no
+pen is given, the pen is assumed to be `@nullpen@'; if no weight is given,
+the weight is assumed to be~$+1$. Thus, the second form of \<addto command>
+basically identifies a picture variable~$V$, a cyclic path~$p$, a pen~$q$,
+and a weight~$w$; and it has the following meaning, assuming that
+"turningcheck" is $\le0$: If~$q$~is the null pen, path~$p$ is digitized
+and each pixel value is increased by $(j-k)w$, where $j$ and~$k$ are the
+respective numbers of downward and upward path edges lying to the left
+of the pixel (as explained earlier in this chapter). If $q$ is not the
+null pen, the action is basically the same except that $p$ is converted to
+another path that ``^{envelope}s'' $p$ with respect to the shape of~$q$;
+this modified path is digitized and filled as before. \ (The modified path
+may cross itself in unusual ways, producing strange squirts of ink as
+illustrated earlier. But it will be well behaved if path~$p$ defines a
+^{convex} region, i.e., if a car that drives counterclockwise
+around $p$ never turns toward the right at any time.)
+
+\ddanger If $"turningcheck">0$ when an `$@addto@\ldots@contour@$' command
+^^"turningcheck" is being performed, the action is the same as just
+described, provided that path~$p$ has a positive ^{turning number}.
+However, if $p$'s turning number is negative, the action depends on
+whether or not pen~$q$ is simple or complex; a complex pen is one whose
+boundary contains at least two points. If the turning number is negative
+and the pen is simple, the weight~$w$ is changed to~$-w$. If the turning
+number is negative and the pen is complex, you get an error message about
+a ``^{backwards path}.'' Finally, if the turning number is zero, you get
+an error message about a ``^{strange path},'' unless the pen is simple and
+$"turningcheck"<=1$. Plain \MF\ sets $"turningcheck":=2$; the ^@filldraw@
+macro in Appendix~B avoids the ``backwards path'' error by explicitly
+reversing a path whose turning number is negative.
+
+\danger We mentioned that the command `@fill@ $(0,2)\dashto(4,2)\dashto
+(4,4)\dashto(2,4)\dashto(2,0)\dashto(0,0)\dashto\cycle$' causes \MF\
+to complain about a strange path; let's take a closer look at the
+error message that you get:
+\begintt
+> 0 ENE 1 NNE 2 (NNW WNW) WSW 3 SSW 4 WSW 5 (WNW NNW) NNE 0
+! Strange path (turning number is zero).
+\endtt
+What does this mean? The numbers represent ``^time'' on the cyclic path,
+from the starting point at time~0, to the next key point at time~1,
+and so on, finally returning to the starting point. Code names like
+`^|ENE|' stand for ^{compass directions} like ``East by North East'';
+\MF\ decides in which of eight ``^{octants}'' each part of a path travels,
+and |ENE| stands for all directions between the angles~$0^\circ$
+and~$45^\circ$, inclusive. Thus, this particular strange path starts in
+octant |ENE| at time~0, then it turns to octant ^|NNE| after time~1.
+An octant name is parenthesized when the path turns through that octant
+without moving; thus, for example, octants ^|NNW| and ^|WNW| are bypassed
+on the way to octant ^|WSW|. It's possible to compute the turning number
+from the given ^^|SSW| sequence of octants; therefore, if you don't think
+your path is really strange, the abbreviated octant codes should reveal
+where \MF\ has decided to take an unexpected turn. \ (Chapter~27 explains
+more about strange paths.)
+
+\ddanger The third form of \<addto command> is `@addto@ $V$ @doublepath@~$p$',
+followed by optional clauses that define a pen~$q$ and a weight~$w$ as in
+the second case. If $p$ is not a cyclic path, this case reduces to the
+second case, with $p$ replaced by the doubled-up path
+`$p\mathbin{\&}\mathop{\rm reverse}p \mathbin{\&}\cycle$' (unless $p$
+consists of only a single point, when the new path is simply
+`$p\to\cycle$'\thinspace). On the other hand if $p$ is a cyclic
+path, this case reduces to {\sl two\/} addto commands of the second type,
+in one of which $p$ is reversed; "turningcheck" is ignored during both of
+those commands.
+
+\danger An anomalous result may occur in the statement `@draw@~$p$'
+or, more generally, in `@addto@~$V$ @doublepath@~$p$ @withpen@~$q$' when
+$p$~is a very small cyclic path and the current pen~$q$ is very large: Pixels
+that would be covered by the pen regardless of where it is placed on~$p$
+might retain their original value. If this unusual circumstance hits you,
+the cure is simply to include the additional statement `@draw@~$z$' or
+`@addto@~$V$ @doublepath@~$z$ @withpen@~$q$', where $z$ is any point
+of~$p$, since this will cover all of the potentially uncovered pixels.
+
+\danger The ^@cull@ command transforms a picture variable so that
+all of its pixel values are either 0 or a specified weight~$w$, where $w$~is
+determined as in an @addto@ command. A pair of numbers $(a,b)$ is given,
+where $a$ must be less than or equal to~$b$. To cull ``@keeping@ $(a,b)$''
+means that each new pixel value is $w$ if and only if the corresponding
+old pixel value~$v$ was included in the range $a\le v\le b$; to cull
+``@dropping@ $(a,b)$'' means that each new pixel value is $w$ if and only
+if the corresponding old pixel value~$v$ was {\sl not\/} in that range.
+Thus, for example, `^@cullit@' is an abbreviation for
+\begindisplay \advance\belowdisplayskip by -4pt
+@cull@ "currentpicture" @keeping@ $(1,"infinity")$
+\enddisplay
+or for
+\begindisplay \advance\abovedisplayskip by -4pt
+@cull@ "currentpicture" @dropping@ $(-"infinity",0)$
+\enddisplay
+(which both mean the same thing). A more complicated example is
+\begindisplay
+@cull@ $V_5$ @dropping@ $(-3,2)$ @withweight@ $-2$;
+\enddisplay
+this changes the pixel values of $V_5$ to $-2$ if they were $-4$ or less,
+or if they were 3 or~more; pixel values between $-3$ and $+2$, inclusive,
+are zeroed.
+
+\danger A cull command must not change pixel values from zero to nonzero.
+For example, \MF\ doesn't let you say `@cull@ $V_1$ @keeping@ $(0,0)$',
+since that would give a value of~1 to infinitely many pixels.
+
+\dangerexercise What is the effect of the following sequence of commands?
+\begindisplay
+@picture@ $V[\,]$;\cr
+$V_1=V_2="currentpicture"$;\cr
+@cull@ $V_1$ @dropping@ $(0,0)$;\cr
+@cull@ $V_2$ @dropping@ $(-1,1)$;\cr
+$"currentpicture":=V_1-V_2$;\cr
+\enddisplay
+\answer The pixel values of "currentpicture" become 1 if they were $\pm1$,
+otherwise they become~0.
+
+\dangerexercise Given two picture variables $V_1$ and $V_2$, all of whose
+pixel values are known to be either 0 or~1, explain how to replace $V_1$ by
+(a)~$V_1\cap V_2$; \ (b)~$V_1\cup V_2$; \ (c)~$V_1\oplus V_2$. \ [The
+{\sl^{intersection}\/} $V_1\cap V_2$ has 1's where $V_1$ and $V_2$ both are~1;
+the {\sl^{union}\/} $V_1\cup V_2$ has 0's where $V_1$ and $V_2$ both are~0;
+the {\sl^{symmetric difference}\/} or {\sl^{selective complement}\/} ^^{xor}
+$V_1\oplus V_2$ has 1's where $V_1$ and $V_2$ are unequal.]
+\answer (a) @addto@ $V_1$ @also@ $V_2$; @cull@ $V_1$
+@keeping@ $(2,2)$. \ (b) Same, but cull keeping $(1,2)$.
+\ (c)~Same, but cull keeping $(1,1)$.
+
+\ddangerexercise Explain how to test whether or not two picture variables
+are equal.
+\answer Subtract one from the other, and cull the result dropping $(0,0)$;
+then test to see if the total weight is zero.
+
+\ddangerexercise Look at the definitions of @fill@, @draw@, etc., in
+Appendix~B and determine the effect of the following statements:
+\begindisplay
+\llap{a) }@draw@ $p$ @withpen@ $q$;\cr
+\llap{b) }@draw@ $p$ @withweight@ 3;\cr
+\llap{c) }@undraw@ $p$ @withweight@ $w$;\cr
+\llap{d) }@fill@ $c$ @withweight@ $-2$ @withpen@ $q$;\cr
+\llap{e) }@erase@ @fill@ $c$ @withweight@ 2 @withpen@ "currentpen";\cr
+\llap{f) }@cullit@ @withweight@ 2.\cr
+\enddisplay
+\answer (a)~Same as `@draw@ $p$', but using $q$ instead of the
+currently-picked-up pen. \ (b)~Same effect as `@draw@~$p$; @draw@~$p$;
+@draw@~$p$' (but faster). \ (c)~Same as `@draw@~$p$ @withweight@~$w$',
+because @undraw@'s `@withweight@~$-1$' is overridden.
+\ (d)~Same as `@unfilldraw@~$c$; @unfilldraw@~$c$',
+ but using $q$ instead of "currentpen".
+\ (e)~Same as `@erase@ @filldraw@~$c$', because the `@withweight@~2' is
+overridden. \ [Since @erase@ has culled all weights to 0 or~1, there's
+no need to ``doubly erase.'']
+\ (f)~Same effect as `@cullit@; @addto@ "currentpicture" @also@
+"currentpicture"' (but faster).
+
+\ddangerexercise Devise a ^@safefill@ macro such that `@safefill@ $c$' increases
+the pixel values of "currentpicture" by~1 in all pixels whose value would
+be changed by the command `@fill@~$c$'. \ (Unlike @fill@, the @safefill@ command
+never stops with a ``^{strange path}'' error; furthermore, it never increases
+a pixel value by more than~1, nor does it decrease any pixel values, even
+when the cycle~$c$ is quite wild.)
+\answer @vardef@ @safefill@ @expr@ $c$ $=$ ^@save@ "region";\parbreak
+\quad@picture@ "region"; "region"=@nullpicture@;\parbreak
+\quad^@interim@ ^"turningcheck"$\null:=0$;\parbreak
+\quad @addto@ "region" @contour@ $c$; \
+ @cull@ "region" @dropping@ $(0,0)$;\parbreak
+\quad @addto@ "currentpicture" @also@ "region" @enddef@.
+
+\ddangerexercise Explain how to replace a character by its ``^{outline}'':
+All black pixels whose four closest neighbors are also
+black should be changed to white, because they are in the interior.
+\ (Diagonally adjacent neighbors don't count.)
+\answer @cull@ "currentpicture" @keeping@ $(1,"infinity")$;\parbreak
+@picture@ $v$; \ $v:="currentpicture"$;\parbreak
+@cull@ "currentpicture" @keeping@ $(1,1)$ @withweight@ 3;\parbreak
+@addto@ "currentpicture" @also@
+ $v\;-\;v$ shifted "right"\parbreak
+\qquad $\null-\;v$ shifted "left"
+ $\null-\;v$ shifted "up"
+ $\null-\;v$ shifted "down";\parbreak
+@cull@ "currentpicture" @keeping@ $(1,4)$.
+
+\ddangerexercise In John ^{Conway}'s ``Game of ^{Life},'' pixels are said to
+be either alive or dead. Each pixel is in contact with eight neighbors.
+The live pixels in the $(n+1)$st generation are those who were dead and
+had exactly three live neighbors in the $n$th generation, or those
+who were alive and had exactly two or three live neighbors in the $n$th
+generation. Write a short \MF\ program that displays successive
+generations on your screen.
+\answer (We assume that "currentpicture" initially has some configuration
+in which all pixel values are zero or one; one means ``alive.'')
+\begindisplay
+@picture@ $v$; @def@ "c" $=$ "currentpicture" @enddef@;\cr
+@forever@: \ $v:=c$; \ @showit@;\cr
+\quad @addto@ $c$ @also@ $c$ shifted "left" $+$ "c" shifted "right";\cr
+\quad @addto@ $c$ @also@ $c$ shifted "up" $+$ "c" shifted "down";\cr
+\quad @addto@ $c$ @also@ $c-v$; \ @cull@ $c$ @keeping@ $(5,7)$; \ @endfor@.\cr
+\enddisplay
+(It is wise not to waste too much computer time watching this program.)
+
+\endchapter
+
+Blot out, correct, insert, refine,
+Enlarge, diminish, interline;
+Be mindful, when Invention fails,
+To scratch your Head, and bite your Nails.
+\author JONATHAN ^{SWIFT}, {\sl On Poetry: A Rapsody\/} (1733) % lines 87--90
+% Rapsody: stet!
+
+\bigskip
+
+The understanding that can be gained from computer drawings
+is more valuable than mere production.
+\author IVAN E. ^{SUTHERLAND}, {\sl Sketchpad\/} (1963) % chapter 9, section E
+
+\eject
+ \beginchapter Chapter 14. Paths
+
+The ^{boundaries} of regions to be filled, and the ^{trajectories} of
+moving pens, are ``^{paths}'' that can be specified by the general methods
+introduced in Chapter~3. \MF\ allows variables and expressions to be of
+type @path@, so that a designer can build new paths from old ones in many
+ways. Our purpose in this chapter will be to complete what Chapter~3
+began; we shall look first at some special features of plain \MF\ that
+facilitate the creation of paths, then we shall go into the details of
+everything that \MF\ knows about pathmaking.
+
+A few handy paths have been predefined in Appendix~B as part of plain \MF\!,
+because they turn out to be useful in a variety of applications. For example,
+^"quartercircle" is a path that represents one-fourth of a ^{circle} of
+diameter~1; it runs from point $(0.5,0)$ to point~$(0,0.5)$.
+The \MF\ program
+\begindisplay
+@beginchar@\kern1pt(|"a"|$,5"pt"\0,5"pt"\0,0)$;\cr
+@pickup@ @pencircle@ scaled $(.4"pt"+"blacker")$;\cr
+@draw@ "quartercircle" scaled 10"pt"; \ @endchar@;\cr
+\enddisplay
+therefore produces the character `\kern1pt{\manual\circa}' in position
+`{\tt a}' of a font.
+
+\exercise
+Write a program that puts a {\sl filled\/} quarter-circle
+`\kern1pt{\manual\circb}' into font position~`{\tt b}'.
+\answer @beginchar@\kern1pt(|"b"|$,5"pt"\0,5"pt"\0,0)$;\parbreak
+@fill@ $((0,0)\dashto"quartercircle"\dashto{\rm cycle})$
+scaled 10"pt"; \ @endchar@.
+
+\exercise
+Why are the `\kern1pt{\manual\circa}' and `\kern1pt{\manual\circb}'
+characters of these examples only $5\,$pt wide and $5\,$pt high, although
+they are made with the path `"quartercircle" scaled 10"pt"'?
+\answer A "quartercircle" corresponds to a circle whose diameter
+is~1; the radius is~$1\over2$.
+
+\dangerexercise
+Use a {\sl rotated\/} quarter-circle to produce `{\manual\circc}\kern1pt'
+in font position `{\tt c}'.
+\answer @beginchar@\kern1pt(|"c"|$,5"pt"\0,5"pt"\0,0)$;\parbreak
+@pickup@ @pencircle@ scaled $(.4"pt"+"blacker")$;\parbreak
+@draw@ "quartercircle" rotated 90 scaled 10"pt" shifted $(5"pt",0)$;
+ \ @endchar@.
+
+\dangerexercise
+Use "quartercircle" to produce `\kern1pt{\manual\circd}\kern1pt'
+in font position `{\tt d}'.
+\answer @beginchar@\kern1pt(|"d"|$,5"pt"\0\ast\rmsqrt2,5"pt"\0,0)$;\parbreak
+@pickup@ @pencircle@ scaled $(.4"pt"+"blacker")$;\parbreak
+@draw@ $((0,0)\dashto"quartercircle"\dashto{\rm cycle})$
+ rotated 45 scaled 10"pt" shifted $(.5w,0)$;\parbreak
+@endchar@.
+
+Plain \MF\ also provides a path called ^"halfcircle" that gives you
+`{\manual\circc\circa}'; this path is actually made from two
+quarter-circles, by defining
+\begindisplay
+"halfcircle" $=$ "quartercircle" \& $"quartercircle"\,{\rm rotated}\,90$.
+\enddisplay
+And of course there's also ^"fullcircle", a complete circle of unit diameter:
+\begindisplay
+"fullcircle" $=$ "halfcircle" \& $"halfcircle"\,{\rm rotated}\,180$ \& cycle.
+\enddisplay
+You can draw a circle of diameter $D$ centered at $(x,y)$ by saying
+\begindisplay
+@draw@ "fullcircle" scaled $D$ shifted $(x,y)$;
+\enddisplay
+similarly,\kern-.4pt\ `@draw@ "fullcircle" \kern-.5pt
+xscaled \kern-1pt$A$ yscaled \kern-1pt$B$'
+yields an ^{ellipse} with axes $A$~and~$B$\kern-1.3pt.\kern-.5pt
+
+Besides circles and parts of circles, there's also a standard square path
+called "unitsquare"; this is a cycle that runs from $(0,0)$ to $(1,0)$
+to $(1,1)$ to $(0,1)$ and back to~$(0,0)$. For example, the command
+`@fill@ "unitsquare"' adds~1 to a single pixel value, as discussed in
+the previous chapter.
+
+\exercise Use "fullcircle" and "unitsquare" to produce the characters
+`{\manual\circe}' and `{\manual\circf}' in font positions `{\tt e}'
+and~`{\tt f}', respectively. These characters should be $10\,$pt wide
+and $10\,$pt tall, and their centers should be $2.5\,$pt above
+the baseline.
+\answer @beginchar@\kern1pt(|"e"|$,10"pt"\0,7.5"pt"\0,2.5"pt"\0)$;\parbreak
+@pickup@ @pencircle@ scaled $(.4"pt"+"blacker")$;\parbreak
+@for@ $D=.2w,.6w,w$: \
+@draw@ "fullcircle" scaled $D$ shifted $(.5w,.5[-d,h])$;\parbreak
+@endfor@ @endchar@.
+\par\medskip\noindent
+The program for `{\manual\circf}' is similar, but `"fullcircle"
+scaled~$D$' is replaced by
+\begindisplay
+"unitsquare" shifted $-(.5,.5)$ rotated 45 scaled $(D/\rmsqrt2)$.
+\enddisplay
+
+\hrule
+\medskip
+\line{\figbox{14a}{220\apspix}{690\apspix}\vbox \hfil
+\vbox{\hsize=18pc \def\\{\vskip1.5pt} \parindent=0pt
+\eightpoint
+\obeylines
+\leavevmode @path@ $"branch"[\,]$, "trunk";
+\\
+$"branch"_1= "flex"((0,660),(-9,633),(-22,610))$
+ \quad\& "flex"$((-22,610),(-3,622),(17,617))$
+ \quad\& "flex"$((17,617),(7,637),(0,660))$ \& cycle;
+\\
+$"branch"_2="flex"((30,570),(10,590),(-1,616))$
+ \quad\& "flex"$((-1,616),(-11,592),(-29,576),(-32,562))$
+ \quad\& "flex"$((-32,562),(-10,577),(30,570))$ \& cycle;
+\\
+$"branch"_3="flex"((-1,570),(-17,550),(-40,535))$
+ \quad\& "flex"$((-40,535),(-45,510),(-60,477))$
+ \quad\& "flex"$((-60,477),(-20,510),(40,512))$
+ \quad\& "flex"$((40,512),(31,532),(8,550),(-1,570))$ \& cycle;
+\\
+$"branch"_4="flex"((0,509),(-14,492),(-32,481))$
+ \quad\& "flex"$((-32,481),(-42,455),(-62,430))$
+ \quad\& "flex"$((-62,430),(-20,450),(42,448))$
+ \quad\& "flex"$((42,448),(38,465),(4,493),(0,509))$ \& cycle;
+\\
+$"branch"_5="flex"((-22,470),(-23,435),(-44,410))$
+ \quad\& "flex"$((-44,410),(-10,421),(35,420))$
+ \quad\& "flex"$((35,420),(15,455),(-22,470))$ \& cycle;
+\\
+$"branch"_6="flex"((18,375),(9,396),(5,420))$
+ \quad\& "flex"$((5,420),(-5,410),(-50,375),(-50,350))$
+ \quad\& "flex"$((-50,350),(-25,375),(18,375))$ \& cycle;
+\\
+$"branch"_7="flex"((0,400),(-13,373),(-30,350))$
+ \quad\& "flex"$((-30,350),(0,358),(30,350))$
+ \quad\& "flex"$((30,350),(13,373),(0,400))$ \& cycle;
+\\
+$"branch"_8="flex"((50,275),(45,310),(3,360))$
+ \quad\& "flex"$((3,360),(-20,330),(-70,300),(-100,266))$
+ \quad\& "flex"$((-100,266),(-75,278),(-60,266))$
+ \quad\& "flex"$((-60,266),(0,310),(50,275))$ \& cycle;
+\\
+$"branch"_9="flex"((10,333),(-15,290),(-43,256))$
+ \quad\& "flex"$((-43,256),(8,262),(58,245))$
+ \quad\& "flex"$((58,245),(34,275),(10,333))$ \& cycle;
+\\
+$"branch"_{10}="flex"((8,262),(-21,249),(-55,240))$
+ \quad\& "flex"$((-55,240),(-51,232),(-53,220))$
+ \quad\& "flex"$((-53,220),(-28,229),(27,235))$
+ \quad\& "flex"$((27,235),(16,246),(8,262))$ \& cycle;
+\\
+$"branch"_{11}="flex"((0,250),(-25,220),(-70,195))$
+ \quad\& "flex"$((-70,195),(-78,180),(-90,170))$
+ \quad\& "flex"$((-90,170),(-5,188),(74,183))$
+ \quad\& "flex"$((74,183),(34,214),(0,250))$ \& cycle;
+\\
+$"branch"_{12}="flex"((8,215),(-35,175),(-72,155))$
+ \quad\& "flex"$((-72,155),(-75,130),(-92,110),(-95,88))$
+ \quad\& "flex"$((-95,88),(-65,117),(-54,104))$
+ \quad\& "flex"$((-54,104),(10,151),(35,142))$
+ \qquad$\to"flex"((42,130),(60,123),(76,124))$
+ \quad\& "flex"$((76,124),(62,146),(26,180),(8,215))$ \& cycle;
+\\
+$"trunk"=(0,660)\ddashto(-12,70)\to\{\curl 5\}(-28,-8)$
+ \quad\& "flex"$((-28,-8),(-16,-4),(-10,-11))$
+ \quad\& "flex"$((-10,-11),(0,-5),(14,-10))$
+ \quad\& "flex"$((14,-10),(20,-6),(29,-11))$
+ \quad\& $(29,-11)\{\curl 4\}\to(10,100)\ddashto{\rm cycle}$;
+}}
+
+Sometimes it's necessary to draw rather complicated curves, and plain \MF\
+provides a `^"flex"' operation that can simplify this task. The
+construction `$"flex"(z_1,z_2,z_3)$' stands for the path
+`$z_1\to z_2\{z_3-z_1\}\to z_3$',
+and similarly `$"flex"(z_1,z_2,z_3,z_4)$' stands for
+`$z_1\to z_2\{z_4-z_1\}\to z_3\{z_4-z_1\}\to z_4$'; in general
+\begindisplay
+$"flex"(z_1,z_2,\ldots,z_{n-1},z_n)$
+\enddisplay
+is an abbreviation for the path
+\begindisplay
+$z_1\to z_2\{z_n-z_1\}\to\;\cdots\;\to z_{n-1}\{z_n-z_1\}\to z_n$.
+\enddisplay
+The idea is to specify two endpoints, $z_1$ and $z_n$, together with
+one or more intermediate points where the path is traveling in the
+same direction as the straight line from $z_1$ to~$z_n$; these
+intermediate points are easy to see on a typical curve, so they
+are natural candidates for key points.
+
+For example, the command
+\begindisplay
+@fill@ \ $"flex"(z_1,z_2,z_3)$ \& $"flex"(z_3,z_4,z_5)$\cr
+\indent\& $"flex"(z_5,z_6,z_7)$ \& $"flex"(z_7,z_8,z_9,z_1)$ \& cycle\cr
+\enddisplay
+will fill the shape
+\displayfig 14b (7pc)
+after the points $z_1$, \dots, $z_9$ have been suitably defined. This
+shape occurs as the fourth branch from the top of ``^{El Palo Alto},''
+a tree that is often used to symbolize ^{Stanford University}. The thirteen
+paths on the opposite page were defined by simply sketching the tree on
+a piece of graph paper, then reading off approximate values of key
+points ``by eye'' while typing the code into a computer. \ (A good radio
+or television program helps to stave off boredom when you're typing
+a bunch of data like this.) \ The entire
+figure involves a total of 47~flexes, most of which are pretty mundane;
+but $"branch"_{12}$ does contain an interesting subpath of the form
+\begindisplay
+$"flex"(z_1,z_2,z_3)\to"flex"(z_4,z_5,z_6)$,
+\enddisplay
+which is an abbreviation for
+\begindisplay
+$z_1\to z_2\{z_3-z_1\}\to z_3\to z_4\to z_5\{z_6-z_4\}\to z_6$.
+\enddisplay
+Since $z_3\ne z_4$ in this example, a smooth curve runs through all six
+points, although two different flexes are involved.
+
+\hangindent -1in \hangafter-2
+Once the paths have been defined,
+\rightfig 14aa (.5in x 1.25in) ^-8pt
+it's easy to use them to make
+symbols like the white-on-black medallion shown here:
+\begindisplay
+@beginchar@\kern1pt(|"T"|$,.5"in"\0,1.25"in"\0,0)$;\cr
+\<Define the thirteen paths on the preceding pages>;\cr
+@fill@ "superellipse"$((w,.5h),(.5w,h),(0,.5h),(.5w,0),.8)$;\cr
+$"branch"_0="trunk"$;\cr
+@for@ $n=0$ @upto@ 12:\cr
+\quad ^@unfill@ $"branch"[n]$ shifted $(150,50)$ scaled $(w/300)$;\cr
+@endfor@ @endchar@;\cr
+\enddisplay
+The oval shape that encloses this tree is a ^"superellipse", which is
+another special kind of path provided by plain \MF\!\null. To get a general
+shape of this kind, you can write
+\begindisplay
+"superellipse"$("right\_point","top\_point","left\_point","bottom\_point",
+ "superness")$
+\enddisplay
+where `"superness"' controls the amount by which the curve differs from a
+true ^{ellipse}. For example, here are four superellipses, drawn with varying
+amounts of ^{superness}, using a
+@pencircle@ xscaled~0.7"pt" yscaled 0.2"pt" rotated~30:
+\displayfig 14c (150\apspix)
+The "superness" should be between 0.5 (when you get a diamond) and 1.0
+(when you get a square); values in the vicinity of 0.75 are usually preferred.
+The zero symbol `{\tt 0}' in this book's typewriter font was
+drawn as a superellipse of superness $2^{-.5}\approx.707$, which
+corresponds to a normal ellipse; the uppercase letter `{\tt O}' was
+drawn with superness $2^{-.25}\approx.841$, to help distinguish it
+from the zero. The ambiguous symbol `{\cmman0}' (which is not in the
+font, but \MF\ can of course draw it) lies between these two extremes; its
+superness is 0.77.
+
+\ddanger A mathematical superellipse satisfies the equation $\vert
+x/a\vert^\beta+\vert y/b\vert^\beta=1$, for some exponent $\beta$. It has
+extreme points $(\pm a,0)$ and $(0,\pm b)$, as well as the ``corner''
+points $(\pm\sigma a,\pm\sigma b)$, where $\sigma=2^{-1/\beta}$ is the
+superness. The tangent to the curve at $(\sigma a,\sigma b)$ runs in the
+direction $(-a,b)$, hence it is parallel to a line from $(a,0)$ to
+$(0,b)$. Gabriel ^{Lam\'e} invented the superellipse in 1818, and
+Piet ^{Hein} popularized the special case
+$\beta=2.5$ [see Martin ^{Gardner}, {\sl Mathematical
+Carnival\/} (New York: Knopf, 1975), 240--254]; this special case
+corresponds to a superness of $2^{-.4}\approx.7578582832552$. Plain \MF's
+"superellipse" routine does not produce a perfect superellipse, nor does
+^"fullcircle" yield a true circle, but the results are close enough for
+practical purposes.
+
+\ddangerexercise Try "superellipse" with superness values less than 0.5
+or greater than~1.0; explain why you get weird shapes in such cases.
+\answer There are inflection points, because there are no bounding triangles
+for the `$\ldots$' operations in the "superellipse" macro of Appendix~B,
+unless $.5\le s\le1$.
+
+Let's look now at the symbols that are used between key points, when we
+specify a path. There are five such tokens in plain \MF:
+\begindisplay
+$\to$&free curve;\cr
+$\ldots$&bounded curve;\cr
+$\dashto$&straight line;\cr
+$\ddashto$&``tense'' line;\cr
+\&&splice.\cr
+\enddisplay
+^^{..}^^{...}^^{--}^^{---}^^{ampersand}
+In general, when you write `$z_0\to z_1\to\<etc.>\to z_{n-1}\to z_n$',
+\MF\ will compute the path of length~$n$ that represents its idea of the
+``most pleasing curve'' through the given points $z_0$ through~$z_n$.
+The symbol `$\ldots$' is essentially the same as `$\to$'\thinspace, except
+that it confines the path to a bounding triangle whenever possible, as
+explained in Chapter~3. A straight line segment `$z_{k-1}\dashto z_k$'
+usually causes the path to change course abruptly at $z_{k-1}$ and $z_k$.
+By contrast, a segment specified by `$z_{k-1}\ddashto z_k$' will be a
+straight line that blends smoothly with the neighboring curves; i.e., the
+path will enter $z_{k-1}$ and leave~$z_k$ in the direction of
+$z_k-z_{k-1}$. \ (The "trunk" of El Palo Alto makes use of this option,
+and we have also used it to draw the signboard of the dangerous bend
+symbol at the end of Chapter~12.) \ Finally, the `\&' operation joins two
+independent paths together at a common point, just as `\&' concatenates
+two strings together.
+
+Here, for example, is a somewhat silly path that illustrates all five
+basic types of joinery:
+\displayfig 14d (120\apspix)
+\begindisplay
+$z_0=(0,100)$; \ $z_1=(50,0)$; \ $z_2=(180,0)$;\cr
+@for@ $n=3$ @upto@ 9: $z[n]=z[n-3]+(200,0)$; \ @endfor@\cr
+@draw@ $z_0\to z_1\ddashto z_2\ldots\{"up"\}z_3$\cr
+\qquad\& $z_3\to z_4\dashto z_5\ldots\{"up"\}z_6$\cr
+\qquad\& $z_6\ldots z_7\ddashto z_8\to\{"up"\}z_9$.\cr
+\enddisplay
+
+\danger The `$\ldots$' operation is usually used only when one or both of the
+adjacent directions have been specified (like `$\{"up"\}$' in this example).
+Plain \MF's ^"flex" construction actually uses `$\ldots$'\thinspace,
+not `$\to$' as stated earlier, because this avoids inflection points in
+certain situations.
+
+\danger A path like `$z_0\ddashto z_1\ddashto z_2$' is almost indistinguishable
+from the broken line `$z_0\dashto z_1\dashto z_2$', except that if you
+enlarge the former path you will see that its lines aren't perfectly
+straight; they bend just a little, so that the curve is ``smooth'' at
+$z_1$ although there's a rather sharp turn there. \ (This means that
+the ^{autorounding} operations discussed in Chapter~24 will apply.) \
+For example, the path $(0,3)\ddashto(0,0)\ddashto(3,0)$ is equivalent to
+\begindisplay
+$(0,3)\to \controls\,(-0.0002,2.9998)\and (-0.0002,0.0002)$\cr
+$\quad\to(0,0)\to \controls\,(0.0002,-0.0002) \and (2.9998,-0.0002)\to(3,0)$\cr
+\enddisplay
+while $(0,3)\dashto(0,0)\dashto(3,0)$ consists of two perfectly straight
+segments:
+\begindisplay
+$(0,3)\to \controls\,(0,2)\and (0,1)$\cr
+$\quad\to(0,0)\to \controls\,(1,0) \and (2,0)\to(3,0)$.\cr
+\enddisplay
+
+\dangerexercise Plain \MF's ^"unitsquare" path is defined to be
+`$(0,0)\dashto(1,0)\dashto(1,1)\dashto(0,1)\dashto\cycle$'.
+Explain how the same path could have been defined using only `$\to$' and~`\&',
+not `$\dashto$' or explicit directions.
+\answer $(0,0)\to(1,0)\;\&\;(1,0)\to(1,1)\;\&\;(1,1)\to(0,1)
+\;\&\;(0,1)\to(0,0)\;\&\;\cycle$. Incidentally, if each `\&' in this path
+is changed to `$\to$', we get a path that goes through the same points;
+but it is a path of length~8 that comes to a complete stop at each
+corner. In other words, the path remains motionless between times $1\le t\le2$,
+$3\le t\le4$, etc. This length-8 path therefore behaves somewhat strangely
+with respect to the `^{directiontime}' operation. It's better to use `\&'
+than to repeat points of a path.
+
+\ddanger Sometimes it's desirable to take a path and change all its
+connecting links to `$\ddashto$', regardless of what they were originally;
+the key points are left unchanged. Plain \MF\ has a ^"tensepath" operation
+that does this. For example, "tensepath"~"unitsquare"~$=$
+$(0,0)\ddashto(1,0)\ddashto(1,1)\ddashto(0,1)\ddashto\cycle$.
+
+When \MF\ is deciding what curves should be drawn in place of `$\to$' or
+`$\ldots$', it has to give special consideration to the beginning and
+ending points, so that the path will start and finish as gracefully as
+possible. The solution that usually works out best is to make the first
+and last path segments very nearly the same as arcs of circles; an
+unadorned path of length~2 like `$z_0\to z_1\to z_2$' will therefore turn
+out to be a good approximation to the unique circular arc that passes
+through $(z_0,z_1,z_2)$, except in extreme cases. You can change this
+default behavior at the endpoints either by specifying an explicit
+direction or by specifying an amount of ``^{curl}.'' If you call for
+curliness less than~1, the path will decrease its curvature in the
+vicinity of the endpoint (i.e., it will begin to turn less sharply); if
+you specify curliness greater than~1, the curvature will increase.
+\ (See the definition of El Palo Alto's "trunk", earlier in this chapter.)
+
+Here, for example, are some pairs of parentheses that were drawn using
+various amounts of curl. In each case the shape was drawn by a statement
+of the form `@penstroke@ $z_{0e}\{\curl c\}\to z_{1e}\to\{\curl c\}z_{2e}$';
+different values of $c$ produce different-looking parentheses:\def\\{\kern1pt}
+\begindisplay
+curl value\hidewidth&\hfil0&\hfil1&\hfil2&\hfil4&\kern-10pt"infinity"\cr
+yields\quad&\cmman 1\\2&\cmman 3\\4&\cmman 5\\6&\cmman 7\\8&\cmman 9\\:\cr
+\enddisplay
+(The parentheses of Computer Modern typefaces are defined by the
+somewhat more general scheme described in Chapter~12; explicit directions are
+specified at the endpoints, instead of curls, because this produces
+better results in unusual cases when the characters are extremely
+tall or extremely wide.)
+
+\danger The amount of curl should not be negative. When the curl is
+very large, \MF\ doesn't actually make an extremely sharp turn at the endpoint;
+instead, it changes the rest of the path so that there is comparatively
+little curvature at the neighboring point.
+
+\danger Chapter 3 points out that we can change \MF's default curves
+by specifying nonstandard ``^{tension}'' between points, or even by
+specifying explicit control points to be used in the four-point method.
+Let us now study the full syntax of path expressions, so that we
+can come to a complete understanding of the paths that \MF\ is able to make.
+Here are the general rules:
+\beginsyntax
+<path primary>\is<pair primary>\alt<path variable>
+ \alt[(]<path expression>[)]
+ \alt[reverse]<path primary>
+ \alt[subpath]<pair expression>[of]<path primary>
+<path secondary>\is<pair secondary>\alt<path primary>
+ \alt<path secondary><transformer>
+<path tertiary>\is<pair tertiary>\alt<path secondary>
+<path expression>\is<pair expression>\alt<path tertiary>
+ \alt<path subexpression><direction specifier>
+ \alt<path subexpression><path join>[cycle]
+<path subexpression>\is<path expression not ending with direction specifier>\kern-5pt\null
+ \alt<path subexpression><path join><path tertiary>
+<path join>\is<direction specifier><basic path join><direction specifier>
+<direction specifier>\is<empty>
+ \alt[\char'173][curl]<numeric expression>[\char'175]
+ \alt[\char'173]<pair expression>[\char'175]
+ \alt[\char'173]<numeric expression>[,]<numeric expression>[\char'175]
+<basic path join>\is[\&]\alt[..]\alt[..]<tension>[..]\alt[..]<controls>[..]
+<tension>\is[tension]<tension amount>
+ \alt[tension]<tension amount>[and]<tension amount>
+<tension amount>\is<numeric primary>
+ \alt[atleast]<numeric primary>
+<controls>\is[controls]<pair primary>
+ \alt[controls]<pair primary>[and]<pair primary>
+\endsyntax
+The operations `$\ldots$' and `$\dashto$' and `$\ddashto$' are conspicuously
+absent from this syntax; that is because Appendix~B defines them as macros:
+\begindisplay
+$\ldots$&is an abbreviation for `$\to\tension\atleast1\to$'\thinspace;\cr
+$\dashto$&is an abbreviation for `$\{\curl1\}\to\{\curl1\}$'\thinspace;\cr
+$\ddashto$&is an abbreviation for `$\to\tension"infinity"\to$'\thinspace.\cr
+\enddisplay
+
+\danger These syntax rules specify a wide variety of possibilities, even though
+they don't mention `$\dashto$' and such things explicitly, so we shall
+now spend a little while looking carefully at their implications.
+A path expression essentially has the form
+\begindisplay
+$p_0\quad j_1\quad p_1\quad j_2\quad\cdots\quad j_n\quad p_n$
+\enddisplay
+where each $p_k$ is a tertiary expression of type pair or path, and where
+each $j_k$ is a ``path join.'' A path join begins and ends with a
+``direction specifier,'' and has a ``basic path join'' in the middle.
+A direction specifier can be empty, or it can be `$\{\curl c\}$'
+for some $c\ge0$, or it can be a direction vector enclosed in braces.
+For example, `$\{"up"\}$' specifies an upward direction, because plain
+\MF\ defines ^"up" to be the pair $(0,1)$. This same direction could be
+specified by `$\{(0,1)\}$' or `$\{(0,10)\}$', or without parentheses as
+`$\{0,1\}$'. If a specified direction vector turns out to be $(0,0)$,
+\MF\ behaves as if no direction had been specified; i.e., `$\{0,0\}$'
+is equivalent to `\<empty>'. An empty direction specifier is implicitly
+filled in by rules that we shall discuss later.
+
+\danger A basic path join has three essential forms: \ (1)~`\&' simply
+concatenates two paths, which must share a common endpoint.
+\ (2)~`$\to\tension\alpha\and\beta\to$' means that a curve should be
+defined, having respective ``tensions'' $\alpha$ and~$\beta$.
+Both $\alpha$ and~$\beta$ must be equal to 3/4 or~more;
+we shall discuss ^{tension} later in this chapter.
+\ (3)~`$\to\controls u\and v\to$' defines a curve with intermediate
+control points $u$ and~$v$.
+
+\danger Special abbreviations are also allowed, so that the long forms
+of basic path joins can usually be avoided: `$\to$' by itself stands for
+`$\to\tension 1\and1\to$'\thinspace,
+ while `$\to\tension\alpha\to$' stands for
+`$\to\tension\alpha\and\alpha\to$'\thinspace,
+ and `$\to\controls u\to$' stands for
+`$\to\controls u\and u\to$'\thinspace.
+
+\danger Our examples so far have always constructed paths from points;
+but the syntax shows that it's also possible to write, e.g.,
+`$p_0\to p_1\to p_2$' when the $p$'s themselves are paths. What does
+this mean? Well, every such path will already have been changed into a
+sequence of curves with explicit control points; \MF\ expands such
+paths into the corresponding sequence of points and basic path joins
+of type~(3). For example, `$((0,0)\to(3,0))\to(3,3)$' is essentially
+the same as `$(0,0)\to\controls\,(1,0)\and(2,0)\to(3,0)\to(3,3)$',
+because `$(0,0)\to(3,0)$' is the path
+`$(0,0)\to\controls\,(1,0)\and(2,0)\to(3,0)$'.
+If a cycle is expanded into a subpath in this way, its cyclic
+nature will be lost; its last point will simply be a copy of its first point.
+
+\danger Now let's consider the rules by which empty direction specifiers
+can inherit specifications from their environment.
+An empty direction specifier at the beginning or end of a path, or just next
+to the `\&' operator, is effectively replaced by `$\{\curl1\}$'.
+This rule should be interpreted properly with respect to cyclic paths, which
+have no beginning or end; for example, `$z_0\to z_1\,\&\,z_1\to z_2\to\cycle$'
+is equivalent to `$z_0\to z_1\{\curl1\}\&\{\curl1\}z_1\to z_2\to\cycle$'.
+
+\danger If there's a nonempty direction specifier after a point but not
+before it, the nonempty one is copied into both places. Thus, for example,
+`$\to z\{w\}$' is treated as if it were `$\to\{w\}z\{w\}$'. If there's
+a nonempty direction specifier before a point but not after it, the
+nonempty one is duplicated in a similar way. A~basic path join
+`$\to\controls u\and v\to$' specifies explicit control points that
+override any direction specifiers that may immediately surround it.
+
+\danger An empty direction specifier next to an explicit control point
+inherits the direction of the adjacent path segment. More precisely,
+`$\to z\to\controls u\and v\to$' is treated as if it were
+`$\to\{u-z\}z\to\controls u\and v\to$' if $u\ne z$, or as if it were
+`$\to\{\curl1\}z\to\controls u\and v\to$' if $u=z$. Similarly,
+`$\to\controls u\and v\to z\to$' is treated as if $z$ were followed by
+$\{z-v\}$ if $z\ne v$, by $\{\curl1\}$ otherwise.
+
+\ddanger After the previous three rules have been applied, we might still
+be left with cases in which there are points surrounded on both sides
+by empty direction specifiers. \MF\ must choose appropriate directions
+at such points, and it does so by applying the following algorithm
+due to John ^{Hobby} [{\sl Discrete and Computational Geometry\/ \bf1}
+(1986), 123--140]: Given a sequence
+\begindisplay
+$z_0\{d_0\}\to\tension\alpha_0\and\beta_1\to z_1
+ \to\tension\alpha_1\and\beta_2\to z_2$\cr
+$\hskip5em\<etc.>\;z_{n-1}\to\tension\alpha_{n-1}\and\beta_n\to\{d_n\}z_n$\cr
+\enddisplay
+for which interior directions need to be determined, we will regard the
+$z$'s as if they were complex numbers. Let $l_k=\vert z_k-z_{k-1}\vert$ be
+the distance from $z_{k-1}$ to $z_k$, and let
+$\psi_k=\arg\bigl((z_{k+1}-z_k)/(z_k-z_{k-1} )\bigr)$ be the turning angle
+at~$z_k$. We wish to find direction vectors $w_0$, $w_1$, \dots,~$w_n$ so
+that the given sequence can effectively be replaced by
+\begindisplay
+$z_0\{w_0\}\to\tension\alpha_0\and\beta_1\to\{w_1\}z_1
+ \{w_1\}\to\tension\alpha_1\and\beta_2\to\{w_2\}z_2$\cr
+$\hskip5em\<etc.>\;z_{n-1}\{w_{n-1}\}\to
+ \tension\alpha_{n-1}\and\beta_n\to\{w_n\}z_n$.\cr
+\enddisplay
+Since only the directions of the $w$'s are significant, not the magnitudes,
+it suffices to determine the angles $\theta_k=\arg\bigl(w_k/(z_{k+1}-z_k
+)\bigr)$. For convenience, we also let $\phi_k=\arg\bigl((z_k-z_{k-1})/w_k
+\bigr)$, so that
+$$\line{\indent$\theta_k+\phi_k+\psi_k\;=\;0$.\hfil$(\ast)$}$$
+Hobby's paper introduces the notion of ``^{mock curvature}'' according to
+which the following equations should hold at interior points:
+$$\line{\indent$\beta_k^2l_k^{-1}\bigl(\alpha_{k-1}^{-1}(\theta_{k-1}
++\phi_k)-3\phi_k\bigr)=\alpha_k^2l_{k+1}^{-1}\bigl(\beta_{k+1}^{-1}
+(\theta_k+\phi_{k+1})-3\theta_k\bigr)$.\hfil$({\ast}{\ast})$}$$
+We also need to consider boundary conditions. If $d_0$ is an explicit
+direction vector~$w_0$, we know $\theta_0$; otherwise $d_0$ is
+`$\curl\gamma_0$' and we set up the equation
+$$\line{\indent$\alpha_0^2\bigl(\beta_1^{-1}(\theta_0+\phi_1)-3\theta_0\bigr)
+=\gamma_0\beta_1^2\bigl(\alpha_0^{-1}(\theta_0+\phi_1)-3\phi_1\bigr)$.
+\hfil$({\ast}{\ast}{\ast})$}$$
+If $d_n$ is an explicit vector~$w_n$, we know $\phi_n$; otherwise
+$d_n$ is `$\curl\gamma_n$' and we set
+$$\line{\indent$\beta_n^2\bigl(\alpha_{n-1}^{-1}(\theta_{n-1}+\phi_n)-3\phi_n
+\bigr)=\gamma_n\alpha_{n-1}^2\bigl(\beta_n^{-1}(\theta_{n-1}+\phi_n)-3
+\theta_{n-1}\bigr)$.\hfil$({\ast}{\ast}{\ast}')$}$$
+It can be shown that the conditions $\alpha_k\ge3/4$, $\beta_k\ge
+3/4$, $\gamma_k\ge0$ imply that there is a unique solution to the
+system of equations consisting of $(\ast)$ and $({\ast}{\ast})$ for $0<k<n$
+plus the two boundary equations; hence the desired quantities $\theta_0$,
+\dots,~$\theta_{n-1}$ and $\phi_1$, \dots,~$\phi_n$ are uniquely determined.
+\ (The only exception is the degenerate case $n=\gamma_0\gamma_1=1$.)
+
+\ddanger A similar scheme works for cycles, when there is no `$\{d_0\}$'
+or `$\{d_n\}$'. In this case equations $(\ast)$ and $({\ast}{\ast})$
+hold for all~$k$.
+
+\ddangerexercise Write out the equations that determine the directions chosen
+for the general cycle
+`$z_0\to\tension\alpha_0\and\beta_1\to
+ z_1\to\tension\alpha_1\and\beta_2\to
+ z_2\to\tension\alpha_2\and\beta_3\to\cycle$'
+of length~3. \ (You needn't try to solve the equations.)
+\answer Let $\delta_1=z_1-z_0$, $\delta_2=z_2-z_1$, $\delta_3=z_0-z_2$;
+$l_1=\vert\delta_1\vert$, $l_2=\vert\delta_2\vert$, $l_3=\vert\delta_3\vert$;
+$\psi_1=\arg(\delta_2/\delta_1)$, $\psi_2=\arg(\delta_3/\delta_2)$,
+$\psi_3=\arg(\delta_1/\delta_3)$. The equations to be solved are
+$(\ast)$ and $({\ast}{\ast})$ for $1\le k\le3$, where $\alpha_3=\alpha_0$
+and $\beta_4=\beta_1$. These six equations determine
+$\theta_1,\theta_2,\theta_3$ and $\phi_1,\phi_2,\phi_3$.
+
+\ddanger Whew\thinspace---\thinspace
+these rules have determined the directions at all points.
+To complete the job of path specification, we need merely explain how
+to change a segment like `$z_0\{w_0\}\to\tension\alpha\and\beta\to\{w_1\}
+z_1$' into a segment of the form
+`$z_0\to\controls u\and v\to z_1$'\thinspace;
+i.e., we finally want to know \MF's
+magic recipe for choosing the control points $u$ and~$v$.
+If $\theta=\arg\bigl(w_0/(z_1-z_0)\bigr)$ and
+$\phi=\arg\bigl((z_1-z_0)/w_1\bigr)$, the control points are
+\begindisplay
+$u=z_0+e^{i\theta}(z_1-z_0)f(\theta,\phi)/\alpha,\qquad
+v=z_1-e^{-i\phi}(z_1-z_0)f(\phi,\theta)/\beta$,
+\enddisplay
+where $f(\theta,\phi)$ is another formula due to John Hobby:
+\begindisplay
+$\displaystyle f(\theta,\phi)=
+{2+\sqrt2\,(\sin\theta-{1\over16}\sin\phi)
+ (\sin\phi-{1\over16}\sin\theta)(\cos\theta-\cos\phi)\over
+3\,\bigl(1+{1\over2}(\sqrt5-1)\cos\theta+{1\over2}(3-\sqrt5\,)\cos\phi\bigr)}.$
+\enddisplay
+
+\ddanger There's yet one more complication. If the tensions $\alpha$ and/or
+$\beta$ have been preceded by the keyword `^{atleast}', the values of
+$\alpha$ and/or $\beta$ are increased, if necessary, to the minimum
+values such that $u$ and~$v$ do not lie outside the ``^{bounding triangle},''
+which is discussed near the end of Chapter~3.
+
+\danger What do these complex rules imply, for \MF\ users who aren't ``into''
+mathematics? The most important fact is that the rules for paths are
+invariant under shifting, scaling, and rotation. In other words, if the
+key points $z_k$ of a path are all shifted, scaled, and/or rotated in the
+same way, the resulting path will be the same as you would get by
+shifting, scaling, and/or rotating the path defined by the unmodified
+$z_k$'s (except of course for possible rounding errors). However,
+this invariance property does not hold if the points or paths are
+xscaled and yscaled by separate amounts.
+
+\danger Another consequence of the rules is that ^{tension} specifications
+have a fairly straightforward interpretation in terms of control points,
+when the adjacent directions have been given: The formulas for $u$ and~$v$
+simply involve division by $\alpha$ and~$\beta$. This means, for example,
+that a tension of~2 brings the control points halfway~in towards the
+neighboring key points, and a tension of "infinity" makes the points very
+close indeed; contrariwise, tensions less than~1 move the control
+points out.
+
+\danger Tension and curl specifications also influence \MF's choices of
+directions at the key points. That is why, for example, the construction
+`$z_{k-1}\ddashto z_k$' (which means `$z_{k-1}\to\tension"infinity"\to
+z_k$'\thinspace) affects the direction of a larger path as it enters
+$z_{k-1}$ and leaves $z_k$.
+
+\danger The rules imply that a change in the position of point~$z_n$
+causes a change in the curve near point~$z_0$, when \MF\ has to choose
+directions at all points between $z_0$ and $z_n$. However, this effect
+is generally negligible except in the vicinity of the changed point.
+You can verify this by looking, for example, at the control
+points that \MF\ chooses for the path `$(0,0)\to(1,0)\to(2,0)\to
+(3,0)\to(4,0)\ldots\{"up"\}(5,y)$', as $y$ varies.
+
+\ddangerexercise Run \MF\ on the `|expr|' file of Chapter~8, and ask
+to see the path expression `^"unitsquare" shifted~$(0,1)\;\to\;$
+"unitsquare" shifted~$(1,0)$'. Account for the results that you get.
+\answer The path is of length~9, and it is equivalent to
+`$(0,1)\dashto(1,1)\dashto(1,2)\dashto(0,2)\dashto(0,1)\{"down"\}
+\to\{"right"\}(1,0)\dashto(2,0)\dashto(2,1)\dashto(1,1)\dashto(1,0)$'.
+Although "unitsquare" is a cycle, the cycle is broken when it is used
+inside a larger path; the resulting non-cyclic square path goes "down"
+when it ends and "right" when it begins.
+
+\ddangerexercise We've said that `$\dashto$' is plain \MF's abbreviation
+for `$\{\curl1\}\to\{\curl1\}$'. Would there be any essential difference
+if `$\dashto$' were defined to mean `$\{\curl2\}\to\{\curl2\}$'\thinspace?
+\answer Yes; for example, `$z_0\to z_1\to z_2\dashto z_3$' would be
+equivalent to `$z_0\to z_1\to\{\curl2\}z_2\{\curl2\}\to\{\curl2\}z_3$'.
+But a path like $z_0\dashto z_1\dashto z_2\dashto z_3$ would not be
+affected, because all directions would turn out to be the same as before.
+(The path `$z_0\{\curl a\}\to\{\curl b\}z_1$' is a straight line regardless
+of the values of $a$ and~$b$, because equations $({\ast}{\ast}{\ast})$
+and $({\ast}{\ast}{\ast}')$ always have the solution $\theta_0=\phi_1=0$
+when $n=1$.)
+
+\ddangerexercise Look closely at the syntax of \<path expression> and
+explain what \MF\ does with the specification `$(0,0)\to(3,3)\to\cycle
+\{\curl1\}$'.
+\answer It treats this as `$((0,0)\to(3,3)\to\cycle)\{\curl1\}$'; i.e.,
+the part up to and including `cycle' is treated as a subpath
+(cf.~`|p2|' in Chapter~8). The cycle is broken, after which we have
+`$(0,0)\to\controls\,(2,-2)\and(5,1)\to(3,3)\to\controls\,(1,5)\and
+(-2,2)\to(0,0)\{\curl1\}$'. Finally the `$\{\curl1\}$' is dropped,
+because all control points are known. \ (The syntax by itself isn't
+really enough to answer this question, as you probably realize.
+You also need to be told that the computation of directions and
+control points is performed whenever \MF\ uses the last two
+alternatives in the definition of \<path expression>.)
+
+\danger Now let's come back to simpler topics relating to paths.
+Once a path has been specified, there are lots of things you can
+do with it, besides drawing and filling and suchlike. For example,
+if $p$ is a path, you can reverse its direction by saying `reverse~$p$';
+the ^{reverse} of `$z_0\to\controls u\and v\to z_1$' is
+`$z_1\to\controls v\and u\to z_0$'.
+
+\dangerexercise True or false: length reverse $p$ $=$ length $p$,
+for all paths~$p$.
+\answer True. The length of a path is the number of
+`$z_k\to\controls u_k\and v_{k+1}\to z_{k+1}$' segments that it contains,
+after all control points have been chosen.
+
+\danger It's convenient to associate ``^{time}'' with paths,
+by imagining that we move along a path of length~$n$ as time passes
+from 0 to~$n$. \ (Chapter~8 has already illustrated this notion, with
+respect to an almost-but-not-quite-circular path called~|p2|; it's a good idea
+to review the discussion of paths and ^{subpaths} in Chapter~8 now before
+you read further.) \ Given a path
+\begindisplay
+$p=z_0\to\controls u_0\and v_1\to z_1\,\<etc.>\,z_{n-1}\to
+ \controls u_{n-1}\and v_n\to z_n$
+\enddisplay
+and a number $t$, \MF\ determines `point $t$ of $p$' as follows:
+If $t\le0$, the result is~$z_0$; if $t\ge n$, the result is~$z_n$;
+otherwise if $k\le t<k+1$, it is $(t-k)[z_k,u_k,v_{k+1},z_{k+1}]$,
+where we generalize the ^^{mediation} `$t[\alpha,\beta]$' notation
+so that $t[\alpha,\beta,\gamma]$ means
+$t\bigl[t[\alpha,\beta],t[\beta,\gamma]\bigr]$
+and $t[\alpha,\beta,\gamma,\delta]$ means
+$t\bigl[t[\alpha,\beta,\gamma],t[\beta,\gamma,\delta]\bigr]$. \ (This
+is a ^{Bernshte\u\i n} polynomial in~$t$, cf.~Chapter~3.) \
+Given a cyclic path
+\begindisplay
+$c=z_0\to\controls u_0\and v_1\to z_1\,\<etc.>\,z_{n-1}\to
+ \controls u_{n-1}\and v_n\to\cycle$
+\enddisplay
+and a number $t$, \MF\ determines `point $t$ of $c$' in essentially the
+same way, except that $t$ is first reduced modulo~$n$ so as to lie
+in the range $0\le t<n$.
+
+\ddangerexercise True or false:\quad point $t$ of $(z_0\dashto z_1)$ $=$
+$t[z_0,z_1]$.
+\answer True if $0\le t\le1$, except perhaps for rounding errors;
+otherwise false. The path $z_0\dashto z_1$ expands into `$z_0\to
+\controls1/3[z_0,z_1]\and2/3[z_0,z_1]\to z_1$', and the ^{Bernshte\u\i n}
+polynomial simplifies because $t[w,w+\delta,w+2\delta,w+3\delta]=w+3t\delta$.
+Incidentally, `point~$t$ of $(z_0\ddashto z_1)$' is usually quite
+different from $t[z_0,z_1]$.
+
+\danger Given a path $p$ and two time values $t_1\le t_2$,
+`subpath~$(t_1,t_2)$ of~$p$' contains all the values
+`point~$t$ of~$p$' as $t$ varies from $t_1$ to~$t_2$. There's no problem
+understanding how to define this subpath when $t_1$ and $t_2$ are integers;
+for example,
+\begindisplay
+subpath $(2,4)$ of $p$ $=$ $z_2\to\controls u_2\and v_3\to z_3
+ \to\controls u_3\and v_4\to z_4$
+\enddisplay
+in the notation above, if we assume that $n\ge 4$. The fractional case is
+handled by ``stretching time'' in one segment of the curve; for example,
+if $0<t<1$ we have
+\begindisplay
+subpath $(0,t)$ of $p$ $=$ $z_0\to\controls t[z_0,u_0]\and
+ t[z_0,u_0,v_1]\to t[z_0,u_0,v_1,z_1]$;\cr
+subpath $(t,1)$ of $p$ $=$ $t[z_0,u_0,v_1,z_1]\to\controls
+ t[u_0,v_1,z_1]\and t[v_1,z_1]\to z_1$.\cr
+\enddisplay
+These two subpaths together account for all points of
+`$z_0\to\controls u_0\and v_1\to z_1$'. To get subpath~$(t_1,t_2)$ of~$p$
+when $0<t_1<t_2<1$, \MF\ applies this construction twice, by computing
+subpath~$(t_1/t_2,1)$ of subpath~$(0,t_2)$ of~$p$.
+
+\ddanger The operation `subpath $(t_1,t_2)$ of $p$' is defined for all
+combinations of times $(t_1,t_2)$ and paths~$p$ by the following rules:
+Let $n={\rm length}\,p$. \ (1)~If $t_1>t_2$, subpath~$(t_1,t_2)$ of~$p$~$=$
+reverse subpath~$(t_2,t_1)$ of~$p$. Henceforth we shall assume that
+$t_1\le t_2$. \ (2)~If $t_1=t_2$, subpath~$(t_1,t_2)$ of~$p$~$=$
+point~$t_1$ of~$p$, a path of length zero. Henceforth we shall assume that
+$t_1<t_2$.
+\ (3)~If $t_1<0$ and $p$ is a cycle, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$(t_1+n,t_2+n)$ of~$p$. If $t_1<0$ and $p$ is not a cycle,
+ subpath~$(t_1,t_2)$ of~$p$~$=$ subpath~$\bigl(0,\max(0,t_2)\bigr)$ of~$p$.
+Henceforth we shall assume that $t_1\ge0$.
+\ (4)~If $t_1\ge n$ and $p$ is a cycle, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$(t_1-n,t_2-n)$ of~$p$.
+If $t_1<n<t_2$ and $p$ is a cycle, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$(t_1,t_2)$ of~$(p\,\&\,p\,\&\,\cycle)$.
+If $t_2>n$ and $p$ is not a cycle, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$\bigl(\min(t_1,n),n\bigr)$ of~$p$.
+Henceforth we shall assume that $0\le t_1<t_2\le n$.
+\ (5)~If $t_1\ge1$, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$(t_1-1,t_2-1)$ of subpath~$(1,n)$ of~$p$, where
+subpath~$(1,n)$ of~$p$ is obtained by removing the first segment of~$p$.
+Henceforth we shall assume that $0\le t_1<1$.
+\ (6)~If $t_2>1$, subpath~$(t_1,t_2)$ of~$p$~$=$
+ subpath~$(t_1,1)$ of~$p$~\& subpath~$(1,t_2)$ of~$p$.
+Henceforth we shall assume that $0\le t_1<t_2\le 1$.
+\ (7)~The remaining cases were defined in the preceding paragraph.
+
+\ddangerexercise What is the length of
+ `subpath $(2.718,3.142)$ of~$p$'\thinspace?
+\answer If $p$ is a cycle, or if $p$ is a path of length $\ge4$, the
+stated subpath has length~2. Otherwise the length is
+$\max(0,{\rm length}\,p-2)$.
+
+\danger Besides `point $t$ of $p$', \MF\ allows you to speak of
+`^{postcontrol}~$t$ of~$p$' and `^{precontrol}~$t$ of~$p$';
+this gives access to the control points of a path. Let
+\begindisplay
+$p=z_0\to\controls u_0\and v_1\to z_1\,\<etc.>\,z_{n-1}\to
+ \controls u_{n-1}\and v_n\to z_n$.
+\enddisplay
+If $t<n$, postcontrol $t$ of $p$ is the first control point in
+subpath~$(t,n)$ of~$p$; if $t\ge n$, postcontrol~$t$ of~$p$ is~$z_n$.
+If $t>0$, precontrol~$t$ of~$p$ is the last control point in
+subpath~$(0,t)$ of~$p$; if $t\le 0$, precontrol~$t$ of~$p$ is~$z_0$.
+In particular, if $t$ is an integer, postcontrol~$t$ of~$p$ is $u_t$
+for $0\le t<n$, and precontrol~$t$ of~$p$ is $v_t$ for $0<t\le n$.
+
+\danger The ability to extract key points and control points makes it
+possible to define interesting operations such as plain \MF's ^"interpath"
+function, which allows you to ^{interpolate between paths}. For example,
+`"interpath"$(1/3,p,q)$' will produce a path of length~$n$ whose
+points are 1/3[point~$t$~of~$p,\,$~point~$t$~of~$q$] for $0\le t\le n$,
+given any paths $p$ and~$q$ of length~$n$. It can be defined by a
+fairly simple program:
+\begindisplay
+@vardef@ "interpath"(@expr@ $a,p,q) =$\cr
+\quad @for@ $t=0$ @upto@ length$\,p\;-1$: $a$[point $t$ of $p,\,$
+ point $t$ of $q$]\cr
+\qquad$\to\controls$ $a$[postcontrol $t$ of $p,\,$
+ postcontrol $t$ of $q$]\cr
+\qquad\quad and $a$[precontrol $t+1$ of $p,\,$
+ precontrol $t+1$ of $q$] $\to$ @endfor@\cr
+\quad @if@ cycle $p$: cycle\qquad\% assume that $p,q$ are both cycles
+ or both noncycles\cr
+\quad @else@: $a$[point "infinity" of $p$, point "infinity" of $q$]
+ @fi@ @enddef@;\cr
+\enddisplay
+
+\danger On February 14, 1979, the author ^^{Knuth, D E}
+bought a box of chocolates and placed the box on a piece of
+graph paper (after suitably disposing of the contents). ^^{Knuth, J C}
+The experimental data gathered in this way led to a ``definitive''
+^^{valentine} heart shape:
+\begindisplay
+$"heart"=(100,162)\to(140,178)\{"right"\}\to(195,125)\{"down"\}$\cr
+\qquad$\to(100,0)\{\curl0\}
+ \to\{"up"\}(5,125)\to\{"right"\}(60,178)\to(100,162)$;\cr
+\enddisplay
+it is interesting to interpolate between ^"heart" and other paths, by using
+a program like
+\begindisplay
+@for@ $n=0$ @upto@ 10: @draw@ "interpath"$(n/10,p,"heart")$; @endfor@.
+\enddisplay
+For example, the left illustration below was obtained by taking
+\begindisplay
+$p=(100,0)\dashto(300,0)\dashto(200,0)\dashto(100,0)\dashto(0,0)
+ \dashto(-100,0)\dashto(100,0)$;
+\enddisplay
+notice that "interpath" doesn't necessarily preserve smoothness at the key
+points. The right illustration was obtained by duplicating point
+$(100,0)$ in~$heart$ (thereby making it a path of length~7) and taking
+\begindisplay
+$p=(100,200)\dashto(200,200)\dashto(200,100)$\cr
+\qquad$\dashto(200,0)\dashto(0,0)\dashto(0,100)\dashto(0,200)\dashto(100,200)$.
+\enddisplay
+\displayfig 14bb\&cc (1in)
+
+\danger Plain \MF\ allows you to say `^{direction}~$t$ of~$p$' in order
+to determine the direction in which path~$p$ is moving at time~$t$. This is
+simply an abbreviation for `(postcontrol~$t$~of~$p)-($precontrol~$t$~of~$p$)'.
+Sometimes a path veers abruptly and has no unique direction; in this case
+the direction function gives a result somewhere between the two possible
+extremes. For example, the "heart" path above turns a corner at
+time~3; `direction~3 of~"heart"' turns out to be
+$(-93.29172,0)$, but `direction~$3-"epsilon"$ of~"heart"' is
+$(-46.64589,-31.63852)$ and `direction~$3+"epsilon"$ of~"heart"' is
+$(-46.64589,31.63852)$.
+
+\outer\def\begindemo#1{$$\advance\baselineskip by2pt
+ \catcode`\"=\other
+ \halign\bgroup\indent\hbox to #1{\tt##\hfil}&\tt##\hfil\cr
+ \noalign{\vskip-2pt}}
+\outer\def\enddemo{\egroup$$}
+\def\demohead{\it\kern-2pt You type&\it\kern-1pt And the result is\cr
+ \noalign{\nobreak\vskip2pt}}
+
+\danger Conversely, \MF\ can tell you when a path heads in
+a given direction. You just ask for `^{directiontime}~$w$ of~$p$', where
+$w$~is a direction vector and $p$~is a path. This operation is best
+understood by looking at examples, so let's resume our dialog with the
+computer by applying \MF\ to the `|expr|' file as in Chapter~8. When
+\MF\ first says `|gimme|', our opening strategy this time will be
+to type
+\begintt
+hide(p3 = (0,0){right}..{up}(1,1)) p3
+\endtt
+so that we have a new path to play with. Now the fun begins:
+^^{dir}^^{angle}
+\begindemo{230pt}
+\demohead
+directiontime right of p3&0\cr
+directiontime up of p3&1\cr
+directiontime down of p3&-1\cr
+directiontime (1,1) of p3&0.5\cr
+directiontime left of reverse p3&1\cr
+direction directiontime (1,2) of p3 of p3&(0.23126,0.46251)\cr
+directiontime right of subpath(epsilon,1) of p3&0\cr
+directiontime right of subpath(2epsilon,1)of p3&-1\cr
+directiontime (1,1) of subpath(epsilon,1) of p3&0.49998\cr
+direction epsilon of p3&(0.55226,0)\cr
+direction 2epsilon of p3&(0.55229,0.00003)\cr
+directiontime dir 30 of p3&0.32925\cr
+angle direction 0.32925 of p3&29.99849\cr
+angle direction 0.32925+epsilon of p3&30.00081\cr
+directionpoint up of p3&(1,1)\cr
+\enddemo
+Note that directiontime yields $-1$ if the specified direction doesn't occur.
+At time ^"epsilon", path~$p_3$ is still traveling right, but at time
+2"epsilon" it has begun to turn upward. The `^{directionpoint}' operation
+is analogous to directiontime, but it gives the point on the path rather
+than the time of arrival. ^^"fullcircle"
+\begindemo{230pt}
+\demohead
+directiontime up of fullcircle&0\cr
+directiontime left of fullcircle&2\cr
+directiontime right of fullcircle&6\cr
+directiontime (-1,1) of fullcircle&1\cr
+directiontime (epsilon,infinity) of fullcircle&8\cr
+directiontime right of unitsquare&0\cr
+directiontime up of unitsquare&1\cr
+directiontime (1,1) of unitsquare&1\cr
+directiontime (-1,1) of unitsquare&2\cr
+\enddemo
+If a path travels in a given direction more than once, directiontime
+reports only the first time. The ^"unitsquare" path has sharp turns at
+the corners; directiontime considers that all directions between the
+incoming and outgoing ones are instantaneously present.
+
+\ddanger It's possible to construct pathological paths in which unusual
+things happen. For example, the path $p=(0,0)\to\controls\,(1,1)\and(0,1)
+\to(1,0)$ has a ``^{cusp}'' at time~0.5, when it comes to a dead stop and
+turns around. \ $\bigl($If you ask for `direction~0.5 of~$p$', the answer
+is zero, while direction~$0.5-\epsilon$ of~$p$ is $(0,2\epsilon)$ and
+direction~$0.5+\epsilon$ of~$p$ is $(0,-2\epsilon)$.$\bigr)$ \ The
+directiontime operation assumes that all possible directions actually
+occur when a path comes to a standstill, hence `directiontime~"right"
+of~$p$' will be 0.5 in this case even though it might be argued that
+$p$~never turns to the right. Paths with cusps are numerically unstable,
+and they might become ``^{strange}'' after transformations are applied,
+because rounding errors might change their ^{turning numbers}. The path~$p$
+in this example has control points that correspond to tensions of only
+0.28 with respect to the initial and final directions; since \MF\ insists
+that ^{tension}s be at least~0.75, this anomalous path could never have arisen
+if the control points hadn't been given explicitly.
+
+\ddangerexercise Write macros called ^"posttension" and ^"pretension"
+that determine the effective tensions of a path's control points at
+integer times~$t$. For example, `"pretension"~1 of ($z_0\to
+\tension\alpha\and\beta\to z_1$)' should be $\beta$ (approximately).
+Test your macro by computing "posttension"~0 of $\bigl((0,0)\{"right"\}
+\ldots\{"up"\}(1,10)\bigr)$.
+\answer @vardef@ "posttension" @expr@ $t$ of $p$ $=$\parbreak
+\quad@save@ $q$; @path@ $q$;\parbreak
+\quad$q={\rm point}\,t\,{\rm of}\,p\,\{{\rm direction}\,t\,{\rm of}\,p\}
+ \to\{{\rm direction}\,t\!+\!1\,{\rm of}\,p\}\,
+ {\rm point}\,t\!+\!1\,{\rm of}\,p$;\parbreak
+\quad length(postcontrol 0 of $q$ $-$ point 0 of $q$)\parbreak
+\qquad/length(postcontrol $t$ of $p$ $-$ point $t$ of $p$) @enddef@;\parbreak
+@vardef@ "pretension" @expr@ $t$ of $p$ $=$\parbreak
+\quad@save@ $q$; @path@ $q$;\parbreak
+\quad$q={\rm point}\,t\!-\!1\,{\rm of}\,p\,\{{\rm direction}\,
+ t\!-\!1\,{\rm of}\,p\}\to\{{\rm direction}\,t\,{\rm of}\,p\}\,
+ {\rm point}\,t\,{\rm of}\,p$;\parbreak
+\quad length(precontrol 1 of $q$ $-$ point 1 of $q$)\parbreak
+\qquad/length(precontrol $t$ of $p$ $-$ point $t$ of $p$) @enddef@;
+\par\nobreak\smallskip\noindent
+The stated posttension turns out to be 4.54019.
+
+\danger We have now discussed almost all of the things that \MF\ can do
+with paths; but there's one more important operation to consider,
+namely ^{intersection}. Given two paths $p$ and~$q$, you can write
+\begindisplay
+$p$ intersectiontimes $q$
+\enddisplay
+and the result will be a pair of times $(t,u)$ such that point~$t$
+of~$p$~$\approx$ point~$u$ of~$q$. For example, using the
+|expr| routine,^^"halfcircle"
+\begindemo{245pt}
+\demohead
+unitsquare intersectiontimes fullcircle&(0.50002,0)\cr
+unitsquare intersectiontimes fullcircle rotated 90&(0.50002,6)\cr
+reverse unitsquare intersectiontimes fullcircle&(0.50002,2)\cr
+fullcircle intersectiontimes unitsquare&(0,0.50002)\cr
+halfcircle rotated 45 intersectiontimes unitsquare&(1,3.5)\cr
+halfcircle rotated 89 intersectiontimes unitsquare&(0.02196,3.5)\cr
+halfcircle rotated 90 intersectiontimes unitsquare&(0,3.50002)\cr
+halfcircle rotated 91 intersectiontimes unitsquare&(-1,-1)\cr
+halfcircle rotated 45 intersectiontimes fullcircle&(0,1)\cr
+fullcircle intersectiontimes (-0.5,0)&(4,0)\cr
+unitsquare intersectionpoint fullcircle&(0.5,0)\cr
+reverse unitsquare intersectionpoint fullcircle&(0,0.5)\cr
+\enddemo
+Notice that the result is $(-1,-1)$ if the paths don't intersect.
+The last two examples illustrate the `^{intersectionpoint}'
+operator, which yields the common point of intersection. Both
+intersectiontimes and intersectionpoint apply at the ^{tertiary level} of
+^{precedence}, hence parentheses were not needed in these examples.
+
+\dangerexercise J. H. ^{Quick} (a student) wanted to construct a path~$r$
+that started on some previously defined path~$p$ and proceeded
+up to the point where it touched another path~$q$, after which $r$ was
+supposed to continue on path~$q$. So he wrote
+\begindisplay
+@path@ $r$; \ @numeric@ $t,u$; \ $(t,u)=p$ intersectiontimes $q$;\cr
+$r={\rm subpath}\,(0,t)\,{\rm of}\,p\;\;\&\;\;
+ {\rm subpath}\,(u,"infinity")\,{\rm of}\,q$;\cr
+\enddisplay
+but it didn't work. Why not?
+\answer The `\&' had to be changed to `$\to$', because point~$t$ of~$p$
+might not be exactly equal to point~$u$ of~$q$.
+
+\ddanger If the paths intersect more than once, \MF\ has a somewhat
+peculiar way of deciding what times $(t,u)$ should be reported by
+`$p$~intersectiontimes~$q$'. Suppose $p$ has length~$m$ and $q$ has
+length~$n$. \ (Paths of length~0 are first changed into motionless paths
+of length~1.) \ \MF\ proceeds to examine subpath~$(k,k+1)$ of~$p$
+versus subpath~$(l,l+1)$ of~$q$, for $k=0$, \dots,~$m-1$ and $l=0$,
+\dots,~$n-1$, with $l$ varying most rapidly. This reduces the general
+problem to the special case of paths of length~1, and the times $(t,u)$
+for the first such intersection found are added to $(k,l)$. But within
+paths of length~1 the search for intersection times is somewhat
+different: Instead of reporting the ``lexicographically smallest'' pair
+$(t,u)$ that corresponds to an intersection, \MF\ finds the $(t,u)$
+whose ``^{shuffled binary}'' representation $(.t_1u_1t_2u_2\ldots\,)_2$
+is minimum, where $(.t_1t_2\ldots\,)_2$ and $(.u_1u_2\ldots\,)_2$ are
+the radix-2 representations of $t$ and~$u$.
+
+\ddangerexercise (A mathematical puzzle.) \ The path
+$p=(0,0)\to\controls\,(2,2)\and(0,1)\to(1,0)$ loops on itself, so there
+are times $t<u$ such that point~$t$ of~$p$~$\approx$ point~$u$ of~$p$.
+Devise a simple way to compute $(t,u)$ in a \MF\ program, without
+using the subpath operation.
+\answer Since $p$ intersects itself infinitely often at times $(t,t)$,
+the task may seem impossible; but \MF's shuffled-binary search procedure
+provides a way. Namely, $p$~intersectiontimes reverse~$p$~$=$
+$(0.17227,0.28339)$, from which we can deduce that $t=0.17227$ and
+$1-u=0.28339$.
+
+\danger Let's conclude this chapter by applying what we've learned about
+paths to a real-life example. The {\sl^{Journal of Algorithms}\/} was
+published for many years by Academic Press, and its cover page carried the
+following ^{logo}, which was designed by J.~C. Knuth ^^{Knuth, J C}
+to blend with the style of type % namely, `Cairoli' by Chartpak
+used elsewhere on that page:
+\displayfig 14dd (25mm)
+A \MF\ program to produce this logo made it possible for the editors
+of the journal to use it on letterheads in their correspondence.
+Here is one way to write that program, without needing to erase anything:
+^^"superellipse" ^^"whatever" ^^{rotatedaround} ^^{reflectedabout}
+^^@forsuffixes@
+$$\halign{\hbox to\parindent{\hfil\sevenrm#\ \ \ }&#\hfil\cr
+1&@beginchar@\kern1pt(|"A"|$,29"mm"\0,25"mm"\0,0)$; \
+ $"thick"\0:=2"mm"\0$; \ $"thin"\0:=5/4"mm"\0$;\cr
+2&@define\_whole\_blacker\_pixels@$("thick","thin")$;\cr
+3&@forsuffixes@ $\$=a,b,c$: \ @transform@ \$;\cr
+4&\quad @forsuffixes@ $e=l,r$: \ @path@ $\$e,\$'e$; \
+ @numeric@ $t\$[\,]e$; \ @endfor@ @endfor@\cr
+5&$\penpos1("thick",0)$; $\penpos2("thick",90)$;
+ $\penpos3("thick",180)$; $\penpos4("thick",270)$;\cr
+6&$\penpos5("thick",0)$; $\penpos6("thick",90)$;
+ $\penpos7("thick",180)$; $\penpos8("thick",270)$;\cr
+7&$x_2=x_4=x_6=x_8=.5[x_5,x_7]=.5w$; \ $x_{1r}=w$; \ $x_{3r}=0$; \
+ $x_5-x_7=y_6-y_8$;\cr
+8&$y_1=y_3=y_5=y_7=.5[y_6,y_8]=.5h$; \ $y_{2r}=h$; \ $y_{4r}=0$; \
+ $y_{6r}=.75h$;\cr
+9&@forsuffixes@ $e=l,r$: \ $a.e=b'e=c'e="superellipse"
+ (z_{1e},z_{2e},z_{3e},z_{4e},.75)$;\cr
+10&\quad $a'e=b.e=c.e="superellipse"
+ (z_{5e},z_{6e},z_{7e},z_{8e},.72)$; \ @endfor@\cr
+11&$\penpos{a1}("thin",0)$; \ $\penpos{a5}("whatever",-90)$; \
+ $\penpos{a9}("thin",180)$;\cr
+12&$x_{a1l}-x_{a9l}=1/3(x_{5l}-x_{7l})$;\ $x_{a5}=.5w$; \
+ $y_{a1}=y_{a9}$; \ $y_{a5r}=4/7h$;\cr
+13&$x_{a3l}=x_{a1l}$; \ $x_{a3r}=x_{a1r}$; \ $x_{a4r}=1/6[x_{a3r},x_{1l}]$; \
+ $x_0=.5w$; \ $y_0=.52h$;\cr
+14&$x_{a6l}+x_{a4l}=x_{a6r}+x_{a4r}=
+ x_{a7l}+x_{a3l}=x_{a7r}+x_{a3r}=x_{a9}+x_{a1}=w$;\cr
+15&\thickmuskip=4mu $y_{a3r}=y_{a4r}=y_{a6r}=y_{a7r}=.2[y_{2l},y_0]$; \
+ $y_{a3l}=y_{a4l}=y_{a6l}=y_{a7l}=y_{a3r}-"thin"$;\cr
+16&$z_{a4l}=z_{a4r}
+ +("thin",0)\,{\rm rotated}({\rm angle}(z_{a4r}-z_{a5r})+90)$\cr
+17&\qquad$\null+"whatever"\ast(z_{a4r}-z_{a5r})$; \
+ $z_{a4l}-z_{a5l}="whatever"\ast(z_{a4r}-z_{a5r})$;\cr
+18&$z=a.r\;\hbox{intersectionpoint}\;(z_0\dashto(w,0))$; \
+ $y_{a1}-y_{a5}=\hbox{length}(z-z_0)$;\cr
+19&$b="identity"$ shifted $(0,y_0-y_{a1})$
+ rotatedaround$(z_0,90-\hbox{angle}(z_0-(w,0)))$;\cr
+20&$c=b$ reflectedabout $(z_2,z_4)$;\cr
+21&@for@ $n=1,3,4,5,6,7,9$:
+ \ @forsuffixes@ $e=l,,r$: \ @forsuffixes@ $\$=b,c$:\cr
+22&\quad $z_{\$[n]e}=z_{a[n]e}$ transformed \$; \ @endfor@ @endfor@ @endfor@\cr
+23&@forsuffixes@ $e=l,r$: \ @forsuffixes@ $\$=a,b,c$:\cr
+24&\quad $z_{\$2e}=\$r$ intersectionpoint $(z_{\$1e}\dashto z_{\$3e})$;\cr
+25&\quad $z_{\$8e}=\$r$ intersectionpoint $(z_{\$9e}\dashto z_{\$7e})$;\cr
+26&\quad $t_{\$1e}=\hbox{xpart}(\$e$
+ intersectiontimes $(z_{\$1l}\dashto z_{\$3l}))$;\cr
+27&\quad $t_{\$9e}=\hbox{xpart}(\$e$
+ intersectiontimes $(z_{\$9l}\dashto z_{\$7l}))$;\cr
+28&\quad $t_{\$4e}=\hbox{xpart}(\$'e$
+ intersectiontimes $(z_{\$5r}\dashto z_{\$4l}))$;\cr
+29&\quad $t_{\$6e}=\hbox{xpart}(\$'e$
+ intersectiontimes $(z_{\$5r}\dashto z_{\$6l}))$; \ @endfor@ @endfor@\cr
+30&^@penstroke@ subpath$(t_{a9e},t_{b6e})$ of $a.e$;\cr
+31&@penstroke@ subpath$(t_{b4e},t_{c4e})$ of $b'e$;\cr
+32&@penstroke@ subpath$(t_{c6e},t_{a1e}+8)$ of $c'e$;\cr
+33&@penstroke@ subpath$(t_{a6e},t_{b9e})$ of $a'e$;\cr
+34&@penstroke@ subpath$(t_{b1e},t_{c1e})$ of $b.e$;\cr
+35&@penstroke@ subpath$(t_{c9e},t_{a4e}+8)$ of $c.e$;\cr
+36&@forsuffixes@ $\$=a,b,c$: \
+ @penlabels@$(\$1,\$2,\$3,\$4,\$5,\$6,\$7,\$8,\$9)$;\cr
+37&\quad @penstroke@ $z_{\$2e}\dashto z_{\$3e}\dashto z_{\$4e}\dashto
+ z_{\$5e}\dashto z_{\$6e}\dashto z_{\$7e}\dashto z_{\$8e}$; \ @endfor@\cr
+38&@penlabels@(^@range@ 0 ^@thru@ 8); \ @endchar@;\cr
+}$$
+Lines 5--10 of this program define the main superellipses of the figure.
+The outer superellipse is eventually drawn as three separate strokes
+in lines 30--32, and the inner one is drawn as three strokes in lines 33--35.
+The rest of the figure consists of three arrows, whose point labels are
+prefaced by the respective labels $a,b,c$. Lines 11--18 define the `$a$'
+arrow; then lines 19--22 transform these points into the `$b$' and~`$c$'
+arrows, anticipating some of the things we shall discuss in Chapter~15.
+Thirty-six intersections between arrows and superellipses are computed
+in lines 23--29, and the arrows are finally drawn by the penstrokes
+specified in lines 36--37.
+
+\displayfig 14e (4.5in)
+
+\endchapter
+
+% se se\~nala con puntos el camino,
+The route is indicated by dots,
+% se expressan por numeros las jornadas,
+the days' journeys are expressed by numbers,
+% y se distinguen por abecedario los lugares \^o parages particulares.
+and letters are used to locate notable places and sites.
+% ... Hasta llegar al arroyo de S$\rm^n$ Francisco,
+.\thinspace.\thinspace. We arrived at the Arroyo de San Francisco,
+% en cuya orilla esta el pinabete que dixe ayer,
+beside which stream is the redwood tree ^^{El Palo Alto} I spoke of yesterday;
+% cuya altura medi con el Grafometro
+I measured its height with the Graphometer
+% y lo halle a poco mas o menos segun el calculo que hize,
+% de unas cincuenta varas de alto.
+and reckoned it to be fifty yards high, more or less.
+\author FRAY PEDRO ^{FONT}, {\sl Diary\/} (1776)
+ % from the second paragraph, then skipping to the entry for March 30
+
+\bigskip
+
+The practical teaching of the masters of Art %
+was summed by the O of ^{Giotto}.
+\author JOHN ^{RUSKIN}, {\sl The Cestus of Aglaia\/} (1865)
+ % in Art Journal, new series, vol 4, p197
+ % later published in section 144 of his Queen of the Air (1869)
+
+\eject
+ \beginchapter Chapter 15. Transformations
+
+Points, paths, pens, and pictures can be shifted, scaled, rotated,
+and revamped in a variety of ways. Our aim in this chapter will be to
+learn all about the built-in metamorphoses of \MF\kern-1pt, because
+they can make programs simpler and more versatile.
+
+The basic ^{transformations} have already appeared in many examples, but let's
+start by reviewing them here:
+\begindisplay
+$(x,y)$ ^{shifted} $(a,b)$&$=(x+a,y+b)$;\cr
+$(x,y)$ ^{scaled} $s$&$=(sx,sy)$;\cr
+$(x,y)$ ^{xscaled} $s$&$=(sx,y)$;\cr
+$(x,y)$ ^{yscaled} $s$&$=(x,sy)$;\cr
+$(x,y)$ ^{slanted} $s$&$=(x+sy,y)$;\cr
+$(x,y)$ ^{rotated} $\theta$&$=(x\cos\theta-y\sin\theta,
+ x\sin\theta+y\cos\theta)$;\cr
+$(x,y)$ ^{zscaled} $(u,v)$&$=(xu-yv,xv+yu)$.\cr
+\enddisplay
+One of the nice things about \MF\ is that you don't have to remember the
+sine-and-cosine formulas of trigonometry; you just have to know that
+`$(x,y)$~rotated~$\theta$' means `the vector~$(x,y)$ rotated $\theta$~degrees
+counterclockwise around~$(0,0)$', and the computer does all the necessary
+calculations by itself. The operation of zscaling may look a bit strange,
+but it is simply a combination of rotating by angle$\,(u,v)$ and scaling
+by length$\,(u,v)$.
+
+Plain \MF\ provides two more transformations that are commonly needed: You can
+say `$(x,y)$ ^{rotatedaround} $(z_0,\theta\mkern1mu)$' if you want to rotate
+around point~$z_0$ instead of point~$(0,0)$. And you can say
+`$(x,y)$~^{reflectedabout}~$(z_1,z_2)$' if you want to find the point directly
+opposite $(x,y)$ on the other side of the straight line that runs through
+$z_1$ and~$z_2$.
+
+All of these operations are special manifestations of a single glorious
+maneuver that can be written in the general form
+\begindisplay
+$(x,y)$ ^{transformed} $t$.
+\enddisplay
+Here $t$ is a variable (or primary expression) of type ^@transform@; it
+stands for any desired sequence of shiftings, scalings, slantings, etc.,
+all in one fell swoop.
+
+You can give ^{equations} between transforms, just as you can give equations
+between other types of things in \MF\ programs. Thus, for example,
+you might say
+\begindisplay
+@transform@ $t[\,]$; \ $t_2=t_1$ shifted $(2,2)$ rotated 30;
+\enddisplay
+then an expression like `$(x,y)$ transformed $t_1$ shifted $(2,2)$ rotated 30'
+can be abbreviated to `$(x,y)$ transformed $t_2$', which is simpler and faster.
+
+There's a special transform variable called ^"identity" with the amazing
+property that
+\begindisplay
+$(x,y)$ transformed "identity" $=$ $(x,y)$
+\enddisplay
+for all $x$ and $y$. You might think that "identity" is useless, since it
+does nothing, but in fact it's a natural starting point for building other
+transforms. For example, line~19 of the program at the end of the previous
+chapter says
+\begindisplay
+$b="identity"$ shifted $(0,y_0-y_{a1})$ rotatedaround$(z_0,"theta")$;
+\enddisplay
+this defines the transform variable $b$ to be a compound transformation
+that is used on lines 21 and~22 to construct the lower left arrow
+as a shifted and rotated copy of the upper arrow, in the character being drawn.
+
+\danger A @transform@ variable $t$ represents six numbers
+$(t_x,t_y,t_{xx},t_{xy},t_{yx},t_{yy})$, in much the same way
+as a @pair@ variable represents two numbers $(x,y)$. The general
+transformation `$(x,y)$~transformed~$t$' is simply an abbreviation for
+\begindisplay
+$(t_x+x\,t_{xx}+y\,t_{xy},\;t_y+x\,t_{yx}+y\,t_{yy})$;
+\enddisplay
+thus, for example, `$t_{xy}$' appears in the xpart of the transform as the
+coefficient of~$y$. If you say `^@show@~$t$' when $t$~is a completely
+unknown transform, the computer will type
+\begintt
+>> (xpart t,ypart t,xxpart t,xypart t,yxpart t,yypart t)
+\endtt
+just as it would type `{\tt>> (xpart u,ypart u)}' for a completely
+unknown variable~$u$ of type @pair@. You can access individual components
+of a transform by referring to `^{xpart}~$t$', `^{ypart}~$t$',
+^^{xypart}^^{yxpart}^^{yypart}
+`^{xxpart}~$t$', etc.
+
+\outer\def\begindemo#1{$$\advance\baselineskip by2pt
+ \catcode`\"=\other
+ \halign\bgroup\indent\hbox to #1{\tt##\hfil}&\tt##\hfil\cr
+ \noalign{\vskip-2pt}}
+\outer\def\enddemo{\egroup$$}
+\def\demohead{\it\kern-2pt You type&\it\kern-1pt And the result is\cr
+ \noalign{\nobreak\vskip2pt}}
+
+\danger Once again, we can learn best by computer experiments with the
+|expr| file (cf.~Chapter~8); this time the idea is to play with transforms:
+\begindemo{175pt}
+\demohead
+identity&(0,0,1,0,0,1)\cr
+identity shifted (a,b)&(a,b,1,0,0,1)\cr
+identity scaled s&(0,0,s,0,0,s)\cr
+identity xscaled s&(0,0,s,0,0,1)\cr
+identity yscaled s&(0,0,1,0,0,s)\cr
+identity slanted s&(0,0,1,s,0,1)\cr
+identity rotated 90&(0,0,0,-1,1,0)\cr
+identity rotated 30&(0,0,0.86603,-0.5,0.5,0.86603)\cr
+identity rotatedaround ((2,3),90)&(5,1,0,-1,1,0)\cr
+(x,y) rotatedaround ((2,3),90)&(-y+5,x+1)\cr
+(x,y) reflectedabout ((0,0),(0,1))&(-x,y)\cr
+(x,y) reflectedabout ((0,0),(1,1))&(y,x)\cr
+(x,y) reflectedabout ((5,0),(0,10))&(-0.8y-0.6x+8,0.6y-0.8x+4)\cr
+\enddemo
+
+\dangerexercise Guess the result of `|(x,y) reflectedabout ((0,0),(1,0))|'.
+\answer |(x,-y)|.
+
+\dangerexercise What transform takes $(x,y)$ into $(-x,-y)$?
+\answer $(x,y)$ rotated 180, or $(x,y)$ scaled $-1$.
+
+\dangerexercise True or false:\quad $\bigl(-(x,y)\bigr)$ transformed $t$
+$=$ $-\bigl((x,y)$ transformed $t\bigr)$.
+\answer True if and only if ${\rm xpart}\,t={\rm ypart}\,t=0$. If the
+stated equation holds for at least one pair $(x,y)$, it holds for all $(x,y)$.
+According to the syntax of Chapter~8, \MF\ interprets `$-(x,y)$ transformed~$t$'
+as $\bigl(-(x,y)\bigr)$ transformed~$t$. \ (Incidentally, mathematicians
+call \MF's transformers ``^{affine transformations},'' and the special case in
+which the xpart and ypart are zero is called ``^{homogeneous}.'')
+
+\danger In order to have some transform variables to work with, it's necessary
+to `^{hide}' some declarations and commands before giving the next |expr|s:
+\begindemo{175pt}
+\demohead
+hide(transform t[]) t1&(xpart t1,ypart t1,xxpart...)\cr
+hide(t1=identity zscaled(1,2)) t1&(0,0,1,-2,2,1)\cr
+hide(t2=t1 shifted (1,2)) t2&(1,2,1,-2,2,1)\cr
+t2 xscaled s&(s,2,s,-2s,2,1)\cr
+unknown t2&false\cr
+transform t2&true\cr
+t1=t2&false\cr
+t1<t2&true\cr
+inverse t2&(-1,0,0.2,0.4,-0.4,0.2)\cr
+inverse t2 transformed t2&(0,0,0.99998,0,0,0.99998)\cr
+hide(t3 transformed t2=identity) t3&(-1,0,0.2,0.4,-0.4,0.2)\cr
+\enddemo
+The ^"inverse" function finds the transform that undoes the work
+of another; the equation that defines $t_3$ above shows how to
+calculate an inverse indirectly, without using "inverse".
+
+\danger Like numeric expressions and pair expressions, transform
+expressions can be either ``^{known}'' or ``^{unknown}'' at any given
+point in a program. \ (If any component of a transform is unknown, the
+whole transform is regarded as unknown.) \ You are always allowed to use
+the constructions
+\begindisplay
+\<known> transformed \<known>\cr
+\<unknown> transformed \<known>\cr
+\<known> transformed \<unknown>\cr
+\enddisplay
+but \MF\ will balk at `\<unknown> transformed \<unknown>'. This is
+not the most lenient rule that could have been implemented, but it
+does have the virtue of being easily remembered.
+
+\dangerexercise If $z_1$ and $z_2$ are unknown pairs, you can't
+say `$z_1$ shifted~$z_2$', because `shifted~$z_2$' is an unknown
+transform. What can you legally say instead?
+\answer $z_1+z_2$.
+
+\begingroup\def\dbend{{\manual\char126}} % lefty dangerous bend sign
+\dangerexercise Suppose "dbend" is a picture variable that contains
+a normal dangerous bend sign, as in the ``reverse-video'' example
+of Chapter~13. Explain how to transform it into the ^{left-handed
+dangerous bend} that heads this paragraph.
+\answer @beginchar@$(126,25u\0,"hheight"\0+"border"\0,0)$;
+|"Dangerous left bend"|;\parbreak
+$"currentpicture":="dbend"$ reflectedabout $\bigl((.5w,0),(.5w,h)\bigr)$; \
+@endchar@;\medskip\noindent
+The same idea can be used to create right ^{parentheses} as perfect mirror
+images of left parentheses, etc., if the parentheses aren't slanted.
+
+\endgroup
+
+\danger The next three lines illustrate the fact that you can specify
+a transform completely by specifying the images of three points:
+\begindemo{175pt}
+\demohead
+hide((0,0)transformed t4=(1,2)) t4&(1,2,xxpart t4,xypart t4,...)\cr
+hide((1,0)transformed t4=(4,5)) t4&(1,2,3,xypart t4,3,yypart t4)\cr
+hide((1,4)transformed t4=(0,0)) t4&(1,2,3,-1,3,-1.25)\cr
+\enddemo
+The points at which the transform is given shouldn't all lie on
+a straight line.
+
+\danger Now let's use transformation to make a little ^{ornament}, based
+on a `{\manual\oneu\kern1pt}' shape replicated four times:
+\qquad\xleaders\hbox{$\vcenter{\hbox{\manual\fouru}}$}\hfill\
+
+\vskip-6mm
+\displayfig 15a (396\apspix)
+
+\begingroup\ninepoint\noindent The following program merits careful study:
+$$\halign{\hbox to\parindent{\hfil\sevenrm#\ \ \ }&#\hfil\cr
+1&@beginchar@\kern1pt(|"4"|$,11"pt"\0,11"pt"\0,0)$;\cr
+2&@pickup@ @pencircle@ scaled 3/4"pt" yscaled 1/3 rotated 30;\cr
+3&@transform@ $t$;\cr
+4&$t="identity"$ ^{rotatedaround}$\bigl((.5w,.5h),-90\bigr)$;\cr
+5&$x_2=.35w$; \ $x_3=.6w$;\cr
+6&$y_2=.1h$; \ $"top"\,y_3=.4h$;\cr
+7&@path@ $p$; \ $p=z_2\{"right"\}\ldots\{"up"\}z_3$;\cr
+8&$"top"\,z_1$ $=$ point .5 of $p$ transformed $t$;\cr
+9&@draw@ $z_1\ldots p$;\cr
+10&@addto@ "currentpicture" @also@ "currentpicture" transformed $t$;\cr
+11&@addto@ "currentpicture" @also@ "currentpicture"
+ transformed ($t$ transformed $t$);\cr
+12&@labels@$(1,2,3)$; \ @endchar@;\cr
+}$$
+^^@addto@
+Lines 3 and 4 compute the transform that moves each
+`{\manual\oneu\kern1pt}' to its clockwise neighbor. Lines 5--7 compute the
+right half of the `{\manual\oneu\kern1pt}'. Line~8 is the most
+interesting: It puts point $z_1$ on the rotated path. Line~9 draws the
+`{\manual\oneu\kern1pt}', line~10 changes it into two, and line~11 changes
+two into four. The parentheses on line~11 could have been omitted, but it
+is much faster to transform a transform than to transform a picture.
+
+\endgroup
+
+\ddanger \MF\ will transform a ^{picture} expression only when $t_{xx}$,
+$t_{xy}$, $t_{yx}$, and~$t_{yy}$ are integers and either $t_{xy}=t_{yx}=0$
+or $t_{xx}=t_{yy}=0$; furthermore, the values of $t_x$ and~$t_y$ are
+rounded to the nearest integers. Otherwise the transformation would not
+take pixel boundaries into pixel boundaries.
+
+\ddangerexercise Explain how to rotate the ornament by $45^\circ$.
+\qquad\xleaders\hbox{\kern1pt$\vcenter{\hbox{\manual\fourc}}$}\hfill\
+\answer Change line 9 to
+\begindisplay
+@draw@ $(z_1\ldots p)$ rotatedaround$\bigl((.5w,.5h),-45\bigr)$\cr
+\quad @withpen@ @pencircle@ scaled 3/4"pt" yscaled 1/3 rotated $-15$;\cr
+\enddisplay
+
+Plain \MF\ maintains a special variable called ^"currenttransform",
+behind the scenes. Every ^@fill@ and ^@draw@ command is affected by this
+variable; for example, the statement `@fill@~$p$' actually fills the
+interior of the path
+\begindisplay
+$p$ transformed "currenttransform"
+\enddisplay
+instead of $p$ itself. We haven't mentioned this before, because
+"currenttransform" is usually equal to "identity"; but nonstandard
+settings of "currenttransform" can be used for special effects that
+are occasionally desired. For example, it's possible to change
+`\MF\kern1pt' to `{\manual 89:;<=>:}\kern3pt' by simply saying
+\begindisplay
+$"currenttransform":="identity"$ slanted 1/4
+\enddisplay
+and executing the programs of |logo.mf| that are described in Chapter~11;
+no other changes to those programs are necessary.
+
+It's worth noting that the pen nib used to draw `{\manual 89:;<=>:}\kern3pt'
+was not slanted when "currenttransform" was changed; only the ``tracks'' of
+the pen, the paths in @draw@ commands, were modified. Thus the slanted image
+was not simply obtained by slanting the unslanted image.
+
+\ddanger When fonts are being made for devices with ^{nonsquare pixels},
+plain \MF\ will set "currenttransform" to `"identity" yscaled
+^"aspect\_ratio"', and ^@pickup@ will similarly yscale the pen nibs
+that are used for drawing. In this case the slanted
+`{\manual 89:;<=>:}\kern3pt' letters should be drawn with
+\begindisplay
+$"currenttransform":="identity"$ slanted 1/4 yscaled "aspect\_ratio".
+\enddisplay
+
+\ddangerexercise Our program for
+`\kern1pt\lower2.5pt\hbox{\manual\fouru}\kern1pt' doesn't work when pixels
+aren't square. Fix it so that it handles a general "aspect\_ratio".
+\answer Replace line 10 by
+\begindisplay
+@pickup@ @pencircle@ scaled 3/4"pt" yscaled 1/3 rotated $-60$;\cr
+@draw@ ($z_1\ldots p$) transformed $t$;\cr
+\enddisplay
+
+\endchapter
+
+Change begets change. Nothing propagates so fast.
+\author CHARLES ^{DICKENS}, {\sl Martin Chuzzlewit\/} (1843)
+ % opening lines of chapter 18
+
+\bigskip
+
+There are some that never know how to change.
+\author MARK ^{TWAIN}, {\sl Joan of Arc\/} (1896)
+ % book 2, chapter 26, second page
+
+\eject
+ \beginChapter Chapter 16. Calligraphic\\Effects
+
+^{Pens} were introduced in Chapter 4, and we ought to make a systematic study
+of what \MF\ can do with them before we spill any more ink. The purpose
+of this chapter will be to explore the uses of ``fixed'' pen nibs---i.e.,
+variables and expressions of type ^@pen@---rather than to consider
+the creation of shapes by means of outlines or penstrokes.
+
+When you say `^@pickup@ ^\<pen expression>', the macros of plain \MF\ do
+several things for you: They create a representation of the specified
+pen~nib, and assign it to a pen variable called ^"currentpen"; then they
+store away information about the top, bottom, left, and right extents of
+that pen, for use in ^"top", ^"bot", ^"lft", and ^"rt" operations.
+A ^@draw@ or ^@drawdot@ or ^@filldraw@ command will make use of
+"currentpen" to modify the current picture.
+
+You can also say `@pickup@ \<numeric expression>'; in this case the numeric
+expression designates the code number of a previously picked-up pen
+that was saved by `^"savepen"'. For example, the |logo.mf| file in Chapter~11
+begins by picking up the pen that's used to draw `\MF\kern1pt', then
+it says `$"logo\_pen":="savepen"$'. Every character program later in that
+file begins with the command `@pickup@ "logo\_pen"', which is a fast
+operation because it doesn't require the generation of a new
+pen representation inside the computer.
+
+\danger Caution: Every time you use "savepen", it produces a new integer
+value and stashes away another pen for later use. If you keep doing this,
+\MF's memory will become cluttered with the representations of pens
+that you may never need again. The command `^@clear\_pen\_memory@'
+discards all previously saved pens and lets \MF\ start afresh.
+
+\danger But what is a \<pen expression>? Good question. So far in this book,
+almost everything that we've picked up was a pencircle followed by
+some sequence of transformations; for example, the "logo\_pen" of
+Chapter~11 was `@pencircle@ xscaled~"px" yscaled~"py"'. Chapter~13
+also made brief mention of another kind of pen, when it said
+\begindisplay
+@pickup@ ^@penrazor@ scaled 10;
+\enddisplay
+this command picks up an infinitely thin pen that runs from point
+$(-5,0)$ to point $(5,0)$ with respect to its center. Later in this
+chapter we shall make use of pens like
+\begindisplay
+^@pensquare@ xscaled 30 yscaled 3 rotated 30;
+\enddisplay
+this pen has a rectangular boundary measuring 30 pixels $\times$ 3 pixels,
+inclined at an angle of $30^\circ$ to the baseline.
+
+\danger You can define pens of any ^{convex polygon}al shape by saying
+`^@makepen@~$p$', where $p$ is a cyclic path. It turns out that \MF\
+looks only at the key points of~$p$, not the control points, so we may
+as well assume that $p$ has the form $z_0\dashto z_1\dashto\<etc.>\dashto
+\cycle$. This path must have the property that it turns left at every
+key point (i.e., $z_{k+1}$ must lie to the left of the line from $z_{k-1}$
+to~$z_k$, for all~$k$), unless the cycle contains fewer than three key
+points; furthermore the path must have a ^{turning number} of~1 (i.e.,
+it must not make more than one counterclockwise loop). Plain \MF's
+@penrazor@ stands for
+`@makepen@ $\bigl((-.5,0)\dashto(.5,0)\dashto \cycle\bigr)$',
+and @pensquare@ is an abbreviation for
+`@makepen@ $\bigl("unitsquare"$ shifted $-(.5,.5)\bigr)$'.
+But @pencircle@ is not defined via @makepen@; it is a
+primitive operation of \MF. It represents a true ^{circle} of diameter~1,
+% no need for `\MF\!.' here
+passing through the points $(\pm.5,0)$ and $(0,\pm.5)$.
+
+\danger The complete syntax for pen expressions is rather short, because
+you can't really do all that much with pens. But it also contains a
+surprise:
+\beginsyntax
+<pen primary>\is<pen variable>
+ \alt[(]<pen expression>[)]
+ \alt[nullpen]
+<future pen primary>\is[pencircle]
+ \alt[makepen]<path primary>
+<pen secondary>\is<pen primary>
+<future pen secondary>\is<future pen primary>
+ \alt<future pen secondary><transformer>
+ \alt<pen secondary><transformer>
+<pen tertiary>\is<pen secondary>
+ \alt<future pen secondary>
+<pen expression>\is<pen tertiary>
+\endsyntax
+The constant `^@nullpen@' is just the single point $(0,0)$, which is
+invisible---unless you use it in ^@filldraw@, which then reduces to
+^@fill@. \ (A ^@beginchar@ command initializes "currentpen" to @nullpen@,
+in order to reduce potentially dangerous dependencies between the programs
+for different characters.) \
+The surprise in these rules is the notion of a ``^{future pen},''
+which stands for a path or an ellipse that has not yet been converted
+into \MF's internal representation of a true pen. The conversion process
+is rather complicated, so \MF\ procrastinates until being sure that no
+more transformations are going to be made. A true pen is formed at the
+tertiary level, when future pens are no longer permitted in the syntax.
+
+\danger The distinction between pens and future pens would make no
+difference to a user, except for another surprising fact: All of \MF's
+pens are convex polygons, even the pens that are made from @pencircle@
+and its variants! Thus, for example, the pen you get from an
+untransformed pencircle is identical to the pen you get by specifying
+the ^{diamond-shaped nib}
+\begindisplay
+@makepen@$\,\bigl((.5,0)\dashto(0,.5)\dashto(-.5,0)\dashto
+ (0,-.5)\dashto\cycle\bigr)$.
+\enddisplay
+And the pens you get from `@pencircle@ scaled 20' and `@pencircle@
+xscaled~30 yscaled~20' are polygons with 32 and 40 sides, respectively:
+\displayfig 16a\&b (220\apspix)
+The vertices of the polygons, shown as heavy dots in this illustration,
+all have ``half-integer'' coordinates; i.e., each coordinate is either
+an integer or an integer plus 1/2. Every polygon that comes from a
+@pencircle@ is symmetric under $180^\circ$ rotation; furthermore,
+there will be reflective left/right and top/bottom symmetry if the future
+pen is a circle, or if it's an ellipse that has not been rotated.
+
+\danger This conversion to polygons explains why future pens must, in
+general, be distinguished from ordinary ones. For example, the extra
+parentheses in `(@pencircle@ xscaled~30) yscaled~20' will yield
+a result quite different from the elliptical polygon just illustrated.
+The parentheses force conversion of `@pencircle@ xscaled~30' from
+future pen to pen, and this polygon turns out to be
+\begindisplay
+$(12.5,-0.5) \dashto (15,0) \dashto (12.5,0.5)$\cr
+\qquad$\dashto (-12.5,0.5) \dashto
+(-15,0) \dashto (-12.5,-0.5) \dashto\cycle$,\cr
+\enddisplay
+an approximation to a $30\times1$ ellipse. Then yscaling by 20 yields
+\displayfig 16c (220\apspix)
+
+\danger Why does \MF\ work with polygonal approximations to circles,
+instead of true circles? That's another good question. The main reason is
+that suitably chosen polygons give better results than the real thing,
+when ^{digitization} is taken into account. For example, suppose we want
+to draw a straight line of slope 1/2 that's exactly one pixel thick, from
+$(0,y)$ to $(200,y+100)$. The image of a perfectly circular pen of
+diameter~1 that travels along this line has outlines that run from
+$(0,y\pm\alpha)$ to $(200,y+100\pm\alpha)$, where
+$\alpha=\sqrt5/4\approx0.559$. If we digitize these outlines and fill the
+region between them, we find that for some values of~$y$ (e.g., $y=0.1$)
+the result is a repeating pixel pattern like
+`\smash{\hbox{$\vcenter{\offinterlineskip
+\setbox4=\hbox{\manual R}
+\hbox{\hphantom{$\,\ldots\,$}\kern5\wd4\copy4$\,\ldots\,$}
+\hbox{\hphantom{$\,\ldots\,$}\kern3\wd4\copy4\copy4}
+\hbox{\hphantom{$\,\ldots\,$}\kern\wd4\copy4\copy4}
+\hbox{\smash{$\,\ldots\,$}\copy4}}$}}'; but for other values of~$y$ (e.g.,
+$y=0.3$) the repeating pattern of pixels is \vbox to11pt{}50 percent darker:
+`\smash{\raise2pt\hbox{$\vcenter{\offinterlineskip
+\setbox4=\hbox{\manual R}
+\hbox{\hphantom{$\,\ldots\,$}\kern4\wd4\copy4\copy4$\,\ldots\,$}
+\hbox{\hphantom{$\,\ldots\,$}\kern2\wd4\copy4\copy4\copy4}
+\hbox{\hphantom{$\,\ldots\,$}\copy4\copy4\copy4}
+\hbox{\smash{$\,\ldots\,$}\copy4}}$}}'. Similarly, some diagonal
+lines of slope~1 digitize to be twice as dark as others, when a truly
+circular pen is considered. But the diamond-shaped nib that \MF\ uses
+for a pencircle of diameter~1 does not have this defect; all straight
+lines of the same slope will digitize to lines of uniform darkness.
+Moreover, curved lines drawn with the diamond nib always yield one pixel per
+column when they move more-or-less horizontally (with slopes between $+1$
+and $-1$), and they always yield one pixel per row when they move vertically.
+By contrast, the outlines of curves drawn with circular pens produce
+occasional ``blots.'' Circles and ellipses of all diameters can profitably
+be replaced by polygons whose sub-pixel corrections to the ideal shape
+will produce better digitizations; \MF\ does this in accordance with the
+interesting theory developed by John~D. ^{Hobby} in his Ph.D.
+dissertation (Stanford University, 1985).
+
+\ddanger It's much easier to compute the outlines of a polygonal pen that
+follows a given curve than to figure out the corresponding outlines of
+a truly circular pen; thus polygons win over circles with respect
+to both quality and speed. When a curve is traveling in a
+direction between the edge vectors $z_{k+1}-z_k$ and~$z_k-z_{k-1}$ of
+a polygonal pen, the curve's outline will be offset from its center
+by~$z_k$. If you want fine control over this curve-drawing process,
+\MF\ provides the primitive operation `^{penoffset}~$w$ of~$p$', where
+$w$~is a vector and $p$~is a pen. If $w=(0,0)$, the result is $(0,0)$;
+if the direction of~$w$ lies strictly between $z_{k+1}-z_k$ and $z_k
+-z_{k-1}$, the result is~$z_k$; and if $w$ has the same direction as
+$z_{k+1}-z_k$ for some~$k$, the result is either $z_k$ or~$z_{k+1}$,
+whichever \MF\ finds most convenient to compute.
+
+\ddangerexercise Explain how to use penoffset to find the point or
+points at the ``top'' of a pen (i.e., the point or points with largest
+$y$~coordinate).
+\answer If there are two points $z_k$ and $z_{k+1}$ with maximum
+$y$~coordinate, the value of `penoffset $(-"infinity","epsilon")$ of~$p$'
+will be~$z_k$ and `penoffset $(-"infinity",-"epsilon")$ of~$p$' will
+be~$z_{k+1}$; `penoffset~"left" of~$p$' will be one or the other. If
+there's only one top point, all three of these formulas will produce it.
+\ (Actually \MF\ also allows pens to be made with three or more
+vertices in a straight line. If there are more than two top vertices,
+you can use penoffset to discover the first and the last, as above;
+furthermore, if you really want to find them all, ^@makepath@ will produce
+a path from which they can be deduced in a straightforward manner.)
+
+\ddanger The primitive operation `^@makepath@ $p$', where $p$ is
+a (polygonal) pen whose vertices are $z_0$, $z_1$, \dots,~$z_{n-1}$,
+produces the path `$z_0\to\controls z_0\and z_1\to z_1\to\<etc.>\to
+z_{n-1}\to\controls z_{n-1}\and z_0\to\cycle$', which is one of the
+paths that might have generated~$p$. This gives access to all the
+offsets of a pen.
+
+\ddanger When a @pencircle@ is transformed by any of the operations
+in Chapter~15, it changes into an ellipse of some sort, since all of
+\MF's transformations preserve ellipse-hood. The diameter of the
+ellipse in each direction~$\theta$ is decreased by $2\min\bigl(
+\vert\sin\theta\vert,\vert\cos\theta\vert\bigr)$ times the current
+value of~^"fillin", before converting to a polygon; this helps to
+compensate for the variation in thickness of diagonal strokes with
+respect to horizontal or vertical strokes, on certain output devices.
+\ (\MF\ uses "fillin" only when creating polygons from ellipses,
+but users can of course refer to "fillin" within their own routines
+for drawing strokes.) \ The final polygon will never be perfectly flat
+like ^@penrazor@, even if you say `xscaled~0' and/or `yscaled~0';
+its center will always be surrounded at least by the basic diamond nib
+that corresponds to a circle of diameter~1.
+
+\dangerexercise Run \MF\ on the |expr| file of Chapter~8 and look at
+what is typed when you ask for `|pencircle|' and `|pencircle|
+|scaled|~|1.1|'. \ (The first will exhibit the diamond nib, while
+the second will show a polygon that's equivalent to @pensquare@.) \
+Continue experimenting until you find the ``threshold'' diameter where
+\MF\ decides to switch between these two polygons.
+\answer `@pencircle@ scaled 1.06060' is the diamond but
+`@pencircle@ scaled 1.06061' is~the square. \ (This assumes that
+^"fillin"$\null=0$. If, for example, $"fillin"=.1$, the change doesn't
+occur until the diameter is 1.20204.) \ The next change is at diameter
+1.5, which gives a diamond twice the size of the first.
+
+\danger \MF's polygonal pens work well for drawing lines and curves,
+but this pleasant fact has an unpleasant corollary: They do not always
+digitize well at the ^{endpoints}, where curves start and stop. The
+reason for this is explored further in Chapter~24; polygon vertices that
+give nice uniform stroke widths might also be ``ambiguous'' points that
+cause difficulties when we consider rounding to the raster. Therefore a
+special ^@drawdot@ routine is provided for drawing one-point paths.
+It is sometimes advantageous to apply @drawdot@ to the first and last
+points of a path~$p$, after having said `^@draw@~$p$'; this can
+fatten up the endpoints slightly, making them look more consistent with
+each other.
+
+\danger Plain \MF\ also provides two routines that can be used to clean~up
+endpoints in a different way: The command `^@cutoff@$\,(z,\theta)$'
+removes half of the ^"currentpen" image at point~$z$, namely all points
+of the pen that lie in directions between $(\theta-90)^\circ$ and
+$(\theta+90)^\circ$ from the center point. And the command `^@cutdraw@~$p$'
+is an abbreviation for the following three commands:
+\begindisplay
+@draw@ $p$; \ @cutoff@\thinspace(point 0 of $p$, $180+\null$angle
+direction 0 of $p$);\cr
+@cutoff@\thinspace(point "infinity" of $p$, angle
+direction "infinity" of $p$).\cr
+\enddisplay
+The effect is to draw a curve whose ends are clipped perpendicular to the
+starting and ending directions. For example, the command
+\begindisplay
+@cutdraw@ $z_4\to\controls z_1\and z_2\to z_6$
+\enddisplay
+produces the following curve, which invites comparison with the corresponding
+uncut version at the end of Chapter~3:
+\displayfig 16d (5pc)
+
+\decreasehsize 48mm
+\danger Here's another example of @cutoff@, in which the endpoints of
+\rightfig 16e ({208\apspix} x {216\apspix}) ^15pt
+\MF's~`^{T}' have been cropped at $10^\circ$ angles to the
+perpendicular of the stroke direction:
+\begintt
+pickup logo_pen;
+top lft z1=(0,h); top rt z2=(w,h);
+top z3=(.5w,h); z4=(.5w,0);
+draw z1--z2;
+cutoff(z1,170); cutoff(z2,-10);
+draw z3--z4; cutoff(z4,-80).
+\endtt
+
+\restorehsize
+\ddanger The @cutoff@ macro of Appendix~B deals with several things
+that we've been studying recently, so it will be instructive to look
+at it now (slightly simplified):
+\begindisplay
+@def@ @cutoff@\thinspace(@expr@ $z,"theta"$) $=$\cr
+\quad$"cut\_pic":=@nullpicture@$;\cr
+\quad^@addto@ "cut\_pic" @doublepath@ $z$ @withpen@ "currentpen";\cr
+\quad@addto@ "cut\_pic" @contour@
+ $((0,-1)\dashto(1,-1)\dashto(1,1)\dashto(0,1)\dashto\cycle)$\cr
+\qquad scaled $1.42(1+\max(-"pen\_lft","pen\_rt","pen\_top",-"pen\_bot"))$\cr
+\qquad rotated "theta" shifted "z";\cr
+\quad^@cull@ "cut\_pic" @keeping@ $(2,2)$ @withweight@ $-1$;\cr
+\quad@addto@ "currentpicture" @also@ "cut\_pic" @enddef@.\cr
+\enddisplay
+The main work is done in a separate ^{picture} variable called "cut\_pic",
+so that neighboring strokes won't be affected. First "cut\_pic" is set to
+the full digitized pen image (by making a ^@doublepath@ from a single
+point). Then a rectangle that includes the cutoff region is added in;
+^"pen\_lft", "pen\_rt", "pen\_top", and "pen\_bot" are the quantities used
+to compute the functions ^"lft", ^"rt", ^"top", and ^"bot", so they bound
+the size of the pen. The culling operation produces the intersection of
+pen and rectangle, which is finally subtracted from "currentpicture".
+
+\ddanger We shall conclude this chapter by studying two examples of how
+\MF's pen-and-curve-drawing facilities can combine in interesting ways.
+First, let's examine two ``^{tilde}'' characters
+\displayfig 16f\&g (50\apspix)
+which were both created by a single command of the form
+\begindisplay
+@draw@ $z_1\to\controls z_2\and z_3\to z_4$.
+\enddisplay
+The left example was done with a ^@pencircle@ xscaled .8"pt" yscaled .2"pt"
+rotated~50, and the right example was exactly the same but with ^@pensquare@.
+The control points $z_2$ and~$z_3$ that made this work were defined by
+\begindisplay
+$y_2-y_1=y_4-y_3=3(y_4-y_1)$;\cr
+$z_2-z_1=z_4-z_3="whatever"\ast{\rm dir}\,50$.\cr % partly redundant
+\enddisplay
+The second pair of equations is an old calligrapher's trick, namely to start
+and finish a~stroke in the direction of the pen you're holding.
+The first pair of equations is a mathematician's trick, based on the
+fact that the ^{Bernshte{\u\i}n polynomial} $t[0,3,-2,1]$ goes from
+0~to~1 to~0~to~1 as $t$ goes from 0 to~.25 to~.75~to~1.
+
+\ddanger Next, let's try to draw a fancy ^{serif} with
+the same two pens, holding them at a $20^\circ$~angle instead of a
+$50^\circ$~angle. Here are two examples
+\displayfig 16h\&i (195\apspix)
+that can be created by `^@filldraw@' commands:
+\begindisplay
+@filldraw@ $z_1\to\controls z_2\to z_3$\cr
+\qquad$\dashto("flex"(z_3,.5[z_3,z_4]+"dishing",z_4))$
+ shifted$\,(0,-"epsilon")$\cr
+\qquad$\dashto z_4\to\controls z_5\to z_6\dashto\cycle$.\cr
+\enddisplay
+The ^"dishing" parameter causes a slight rise between $z_3$ and~$z_4$;
+the ^"flex" has been lowered by ^"epsilon" in order to avoid the danger
+of ``^{strange paths},'' which might otherwise be caused by tiny loops
+at $z_3$ or~$z_4$. But the most interesting thing about this example
+is the use of double control points, $z_2$ and~$z_5$, in two of the
+path segments. \ (Recall that `$\controls z_2$' means the same thing
+^^{controls} as `$\controls z_2\and z_2$'.) \ These points were determined
+by the equations
+\begindisplay
+$x_2=x_1$; \ $z_2=z_3+"whatever"\ast{\rm dir}\,20$;\cr
+$x_5=x_6$; \ $z_5=z_4+"whatever"\ast{\rm dir}\,{-20}$;\cr
+\enddisplay
+thus, they make the strokes vertical at $z_1$ and $z_6$, parallel to the
+pen angle at~$z_3$, and parallel to the complementary angle at~$z_4$.
+
+
+\endchapter
+
+The pen, probably more than any other tool,
+has had the strongest influence upon lettering
+in respect of serif design .\thinspace.\thinspace.
+It is probable that the letters [of the Trajan column]
+were painted before they were incised,
+and though their main structure is attributed to the pen
+and their ultimate design to the technique of the chisel,
+they undoubtedly owe much of their freedom
+to the influence of the brush.
+\author L. C. ^{EVETTS}, {\sl Roman Lettering\/} (1938) % pp 3 and 13
+
+\bigskip
+
+Remember that it takes time, patience, critical practice
+and knowledge to learn any art or craft.
+No ``art experience'' is going to result from any busy work
+for a few hours experimenting with the edged pen.
+.\thinspace.\thinspace. Take as much time as you require,
+and do not become impatient.
+If it takes a month to get it,
+then be happy that it takes only a month.
+\author LLOYD ^{REYNOLDS}, {\sl Italic Calligraphy \& Handwriting\/} (1969)
+
+\eject
+ \beginchapter Chapter 17. Grouping
+
+We have now covered all the visual, graphic aspects of \MF---its
+points, paths, pens, and pictures; but we still don't know everything
+about \MF's organizational, administrative aspects---its programs.
+The next few chapters of this book therefore concentrate on
+how to put programs together effectively.
+
+A \MF\ program is a sequence of statements separated by semicolons and
+followed by `^@end@'. More precisely, the syntax rules
+\beginsyntax
+<program>\is<statement list>[end]
+<statement list>\is<empty>\alt<statement>[;]<statement list>
+\endsyntax
+define a \<program> in terms of a \<statement>.
+
+But what are ^{statements}? Well, they are of various kinds. An ``equation''
+states that two expressions are supposed to be equal. An ``assignment''
+assigns the value of an expression to a variable. A ``declaration''
+states that certain variables will have a certain type.
+A ``definition'' defines a macro. A ``title'' gives a descriptive name to
+the character that is to follow. A ``command'' orders \MF\ to do some
+specific operation, immediately. The ``^{empty statement}'' tells \MF\ to
+do absolutely nothing. And a ``^{compound statement}'' is a list of other
+statements treated as a ^{group}.
+\beginsyntax
+<statement>\is<equation>\alt<assignment>\alt<declaration>
+ \alt<definition>\alt<title>\alt<command>\alt<empty>
+ \alt[begingroup] <statement list> <statement> [endgroup]
+\endsyntax
+We've given the syntax for \<equation> and \<assignment> in Chapter~10;
+the syntax for \<declaration> appeared in Chapter~7; \<definition> and
+\<title> and \<command> will appear in later chapters. Our main concern
+just now is with the final type of \<statement>, where @begingroup@
+and @endgroup@ bind other statements into a unit, just as parentheses
+add structure to the elements of an algebraic expression.
+
+The main purpose of grouping is to protect the values of variables in
+one part of the program from being clobbered in another. A symbolic token
+can be given a new meaning inside a group, without changing the
+meaning it had outside that group. \ (Recall that \MF\ deals with
+three basic kinds of tokens, as discussed in Chapter~6; it is impossible
+to change the meaning of a numeric token or a string token, but
+symbolic tokens can change meanings~freely.)
+
+There are two ways to protect the values of variables in a group. One
+is called a \<save command>, and the other is called an \<interim command>:
+\beginsyntax
+<save command>\is[save]<symbolic token list>
+<symbolic token list>\is<symbolic token>
+ \alt<symbolic token list>[,]<symbolic token>
+<interim command>\is\kern-1.5pt[interim]%
+ <internal quantity>[:=]<right-hand side>\kern-1pt
+\endsyntax
+The symbolic tokens in a @save@ command all lose their current meanings, but
+those old meanings are put into a safe place and restored at the end of
+the current group. Each token becomes undefined, as if it had never
+appeared before. For example, the command
+\begindisplay
+@save@ $x,y$
+\enddisplay
+effectively causes all previously known variables like $x_1$ and $y_{5r}$ to
+become inaccessible; the variable $x_1$ could now appear in a new equation,
+where it would have no connection with its out-of-group value. You could
+also give the silly command
+\begindisplay
+@save@ @save@;
+\enddisplay
+this would make the token `|save|' itself into a ^\<tag> instead of a
+^\<spark>, so you couldn't use it to save anything else until the group ended.
+
+\danger An @interim@ command is more restrictive than a @save@, since it
+applies only to an ^\<internal quantity>. \ (Recall that internal
+quantities are special variables like "tracingequations" that take numeric
+values only; a complete list of all the standard internal quantities can
+be found in Chapter~25, but that list isn't exhaustive because you can
+define new ones for your own use.) \ \MF\ treats an interim command just
+like an ordinary assignment, except that it undoes the assignment when the
+group~ends.
+
+\danger If you save something two or more times in the same group,
+the first saved value takes precedence. For example, in the construction
+\begindisplay
+@begingroup@\cr
+\noalign{\vskip-3pt}\dots\cr
+@interim@ $"autorounding":=0$; \ @save@ $x$;\cr
+\noalign{\vskip-3pt}\dots\cr
+@interim@ $"autorounding":=1$; \ @save@ $x$;\cr
+\noalign{\vskip-3pt}\dots\cr
+@endgroup@\cr
+\enddisplay
+the values of "autorounding" and $x$ after the end of the group will be
+their previous values just before the statement `@interim@ $"autorounding":=0$'.
+(Incidentally, these might not be the values they had upon entry to the group.)
+
+\danger Tokens and internal quantities regain their old meanings and
+values at the end of a group only if they were explicitly saved in a
+@save@ or @interim@ command. All other changes in meaning and/or value
+will survive outside the group.
+
+\danger The ^@beginchar@ operation of plain \MF\ includes a @begingroup@,
+and ^@endchar@ includes @endgroup@. Thus, for example, interim assignments
+can be made in a program for one character without any effect on other
+characters.
+
+\danger A \<save command> that's not in a group simply clears the meanings
+of the symbolic tokens specified; their old meanings are not actually saved,
+because they never will have to be restored. An \<interim command>
+outside a group acts just like a normal assignment.
+
+\danger If you set the internal quantity ^"tracingrestores" to a positive
+value, \MF\ will make a note in your transcript file whenever it is
+restoring the former value of a symbolic token or internal quantity.
+This can be useful when you're debugging a program that doesn't seem
+to make sense.
+
+Groups can also be used within algebraic expressions. This is
+the other important reason for grouping; it allows \MF\ to do arbitrarily
+complicated things while in the middle of other calculations, thereby
+greatly increasing the power of macro definitions (which we shall study
+in the next chapter). A {\sl^{group expression}\/} has the general form
+\begindisplay
+{\tt begingroup}\thinspace\<statement list>\thinspace\<expression>
+\thinspace{\tt endgroup}
+\enddisplay
+and it fits into the syntax of expressions at the primary level. The
+meaning of a group expression is: ``Perform the list of statements,
+then evaluate the expression, then restore anything that was saved
+in this group.''
+
+\danger Group expressions belong in the syntax rules for each type
+of expression, but they were not mentioned in previous chapters because
+it would have been unnecessarily distracting. Thus, for example, the syntax for
+\<numeric primary> actually includes the additional alternative
+\begindisplay
+|begingroup|\thinspace\<statement list>\<numeric expression>%
+ \thinspace|endgroup|.
+\enddisplay
+The same goes for \<pair primary>, \<picture primary>, etc.; Chapter~25
+has the complete rules of syntax for all types of expressions.
+
+\dangerexercise What is the value of the expression
+\begintt
+begingroup x:=x+1; x endgroup + begingroup x:=2x; x endgroup
+\endtt
+if $x$ initially has the value $a$? What would the value have been if
+the two group expressions had appeared in the opposite order?
+Verify your answers using the |expr| routine of Chapter~8.
+\answer $(a+1)+(2a+2)=3a+3$ and $(2a)+(2a+1)=4a+1$, respectively.
+The final value of~$x$ in the first case is $2a+2$, hence $a=.5x-1$;
+|expr| will report the answer as |1.5x| (in terms of $x$'s new value),
+since it has not been told about `$a$'. In the second case |expr| will,
+similarly, say |2x-1|.\par
+This example shows that $\alpha+\beta$ is not necessarily equal
+to ^^{commutativity} $\beta+\alpha$, when $\alpha$ and~$\beta$ involve
+group expressions. \MF\ evaluates expressions strictly from left to
+right, performing the statements within groups as they appear.
+
+\dangerexercise Appendix B defines ^"whatever" to be an abbreviation for
+the group expression `@begingroup@ @save@ ?; ? @endgroup@'. Why
+does this work? \checkequals\Xwhat\exno
+\answer The save instruction gives `?' a fresh meaning, hence `?' is
+a numeric variable unconnected to any other variables. When the group
+ends and `?' is restored to its old meaning, the value of the group
+expression no longer has a name. \ (It's called a ``^{capsule}'' if
+you try to @show@ it.) \ Therefore the value of the group expression
+is a new, nameless variable, as desired.
+
+\ddangerexercise What is the value of `@begingroup@ @save@ ?; \
+$(?,?)$ @endgroup@'\thinspace?
+\answer It's a nameless pair whose xpart and ypart are equal; thus it
+is essentially equivalent to `$"whatever"\ast(1,1)$'.
+
+\ddangerexercise According to exercise 10.\xwhat, the assignment
+`$x_3:="whatever"$' will make the numeric variable $x_3$ behave like new,
+without affecting other variables like $x_2$. Devise a similar stratagem
+that works for arrays of @picture@ variables.
+\answer `$v_3:=@begingroup@$ @save@ ?; @picture@ ?; ?\ @endgroup@'
+refreshes the picture variable~$v_3$ without changing other variables
+like~$v_2$. This construction works also for pairs, pens, strings, etc.
+
+\endchapter
+
+It is often difficult
+to account for some beginners grouping right away
+and others proving almost hopeless.
+\author A. G. ^{FULTON}, {\sl Notes on Rifle Shooting\/} (1913)
+ % according to OED Supplement, but this pamphlet has vanished from their files!
+
+\bigskip
+
+Rock bands prefer San Francisco groupies to New York groupies.
+\author ELLEN ^{WILLIS}, {\sl But Now I'm Gonna Move\/} (1971)
+ % New Yorker, 23 Oct 71, p170
+
+\eject
+ \beginchapter Chapter 18. Definitions\\(also called Macros)
+
+You can often save time writing \MF\ programs by letting single tokens
+stand for sequences of other tokens that are used repeatedly. For example,
+Appendix~B defines `$\ddashto$' to be an abbreviation for ^^{---}
+`$\to\tension"infinity"\to$', and this definition is preloaded as
+part of the plain \MF\ base. Programs that use such definitions are not
+only easier to write, they're also easier to read. But Appendix~B
+doesn't contain every definition that every programmer might want;
+the present chapter therefore explains how you can make ^{definitions}
+of your own.
+
+In the simplest case, you just say
+\begindisplay
+@def@ \<symbolic token> $=$ \<replacement text> @enddef@
+\enddisplay
+and the symbolic token will henceforth expand into the tokens of the
+replacement text. For example, Appendix~B says
+\begintt
+def --- = ..tension infinity.. enddef;
+\endtt
+it makes `$z_1\ddashto z_2$' become `$z_1\to\tension"infinity"\to z_2$'.
+The ^{replacement text} can be any sequence of tokens not including
+`@enddef@\kern1pt'; or it can include entire subdefinitions like
+`@def@~$\ldots$~@enddef@\kern1pt', according to certain rules
+that we shall explain later.
+
+Definitions get more interesting when they include {\sl^{parameters}},
+which are replaced by {\sl^{arguments}\/} when the definition is expanded.
+For example, Appendix~B also says
+\begintt
+def rotatedaround(expr z,theta) =
+ shifted -z rotated theta shifted z enddef;
+\endtt
+this means that an expression like `$z_1$ ^{rotatedaround}$\,(z_2,30)$' will
+expand into `$z_1$ shifted~$-z_2$ rotated~30 shifted~$z_2$'.
+
+The parameters `|z|' and `|theta|' in this definition could have been any
+symbolic tokens whatever; there's no connection between them and
+appearances of `|z|' and `|theta|' outside the definition. \ (For example,
+`|z|'~would ordinarily stand for `|(x,y)|', but it's just a simple token
+here.) \ The definition could even have been written with ``primitive''
+tokens as parameters, like
+\begintt
+def rotatedaround(expr;,+) =
+ shifted-; rotated+shifted; enddef;
+\endtt
+the effect would be exactly the same. \ (Of course, there's no point in
+doing such a thing unless you are purposely trying to make your
+definition inscrutable.)
+
+When `|rotatedaround|' is used, the arguments that are substituted for |z|
+and |theta| are first evaluated and put into ``^{capsules},'' so that they
+will behave like primary expressions. Thus, for example, `$z_1$
+rotatedaround$\,(z_2+z_3,30)$' will not expand into `$z_1$ shifted~$-z_2+z_3$
+rotated~30 shifted~$z_2+z_3$'---which means something entirely different---but
+rather into `$z_1$ shifted~$-\alpha$ rotated~30 shifted~$\alpha$', where
+$\alpha$ is a nameless internal variable that contains the value of
+$z_2+z_3$.
+
+\danger A capsule value cannot be changed, so an @expr@ parameter should not
+^^{:=} appear at the left of the ^{assignment} operator `$:=$'.
+
+\danger Macros are great when they work, but complicated macros sometimes
+surprise their creators. \MF\ provides ``tracing'' facilities so that you
+can see what the computer thinks it's doing, when you're trying to
+diagnose the reasons for unexpected behavior. If you say
+`^"tracingmacros"$\null:=1$', the transcript file of your run will record
+every macro that is subsequently expanded, followed by the values of its
+arguments as soon as they have been computed.
+For example, `rotatedaround$\,("up",30)$' might produce the
+^^|EXPR0| following lines of diagnostic information:
+\begintt
+rotatedaround(EXPR0)(EXPR1)->shifted-(EXPR0)rotated(EXPR1)sh
+ifted(EXPR0)
+(EXPR0)<-(0,1)
+(EXPR1)<-30
+\endtt
+
+\danger Here's another example from Appendix B\null. It illustrates the
+usefulness of ^{group expressions} in macro definitions:
+\begindisplay
+@def@ ^{reflectedabout}$\,(@expr@\ p,q)$ $=$\cr
+\quad transformed @begingroup@\cr
+\qquad ^@save@ $T$; \ ^@transform@ $T$;\cr
+\qquad $p$ transformed $T$ $=$ $p$;\cr
+\qquad $q$ transformed $T$ $=$ $q$;\cr
+\qquad ^{xxpart} $T$ $=$ $-$^{yypart} $T$;\cr
+\qquad ^{xypart} $T$ $=$ ^{yxpart} $T$;\cr
+\qquad $T$ @endgroup@ @enddef@;\cr
+\enddisplay
+thus a new transform, $T$, is computed in the midst of another expression,
+and the macro `reflectedabout($p,q$)' essentially expands into
+`transformed $T$'.
+
+Some macros, like `rotatedaround', are meant for general-purpose use.
+But it's also convenient to write ^{special-purpose macros} that simplify
+the development of particular typefaces. For example, let's consider the
+\MF\ logo from this standpoint. The program for `{\manual E}' in
+Chapter~11 starts with
+\begintt
+beginchar("E",14u#+2s#,ht#,0); pickup logo_pen;
+\endtt
+and the programs for `{\manual M}', `\kern1pt{\manual T}\kern1pt',
+etc., all have almost the same beginning. Therefore we might as
+well put the following definition near the top of the file |logo.mf|:
+\begintt
+def beginlogochar(expr code, unit_width) =
+ beginchar(code,unit_width*u#+2s#,ht#,0);
+ pickup logo_pen enddef;
+\endtt
+Then we can start the `{\manual E}' by saying simply ^^|beginlogochar|
+\begintt
+beginlogochar("E",14);
+\endtt
+similar simplifications apply to all seven letters. Notice from
+this example that macros can be used inside macros (since `|beginchar|'
+and `|pickup|' are themselves macros, defined in Appendix~B\null); once you
+have defined a macro, you have essentially extended the \MF\ language.
+Notice also that ^@expr@ parameters can be expressions of any type;
+for example, |"E"| is a string, and the first parameter of
+`rotatedaround' is a pair.
+
+\decreasehsize 48mm
+Chapter 11 didn't give the programs for `{\manual A}' or `{\manual O}'.
+\rightfig 18a ({240\apspix} x {216\apspix}) ^15pt
+It turns out that those programs can be simplified if we write
+them in terms of an auxiliary subroutine called `|super_half|'.
+For example, here is how the `{\manual O}' is made:
+\begintt
+beginlogochar("O",15);
+x1=x4=.5w; top y1=h+o; bot y4=-o;
+x2=w-x3=1.5u+s; y2=y3=barheight;
+super_half(2,1,3);
+super_half(2,4,3);
+labels(1,2,3,4); endchar;
+\endtt
+
+\restorehsize\medbreak\noindent
+The |super_half| routine is supposed to draw half of a ^{superellipse},
+through three points whose subscripts are specified.
+
+\restorehsize
+We could define |super_half| as a macro with three @expr@ parameters,
+referring to the first point as `|z[i]|', say; but there's a better way.
+Parameters to macros can be classified as suffixes, by saying ^@suffix@
+instead of @expr@. In this case
+the actual arguments may be any ^\<suffix>, i.e., any sequence of
+subscripts and tags that complete the name of a variable as explained
+in Chapter~7. Here's what |super_half| looks like, using this idea:
+\begintt
+def super_half(suffix i,j,k) =
+ draw z.i{0,y.j-y.i}
+ ... (.8[x.j,x.i],.8[y.i,y.j]){z.j-z.i}
+ ... z.j{x.k-x.i,0}
+ ... (.8[x.j,x.k],.8[y.k,y.j]){z.k-z.j}
+ ... z.k{0,y.k-y.j} enddef;
+\endtt
+
+\exercise Would the program for `{\manual O}' still work if the two calls of
+|super_half| had been `|super_half(3,1,2)|' and `|super_half(3,4,2)|'\thinspace?
+\answer Yes; the direction at |z.j| will be either "left" or "right".
+
+\exercise Guess the program for \MF's `{\manual A}', which has the
+same width as `{\manual O}'.
+\answer |beginlogochar("A",15);|
+\rightfig A18a ({240\apspix} x {216\apspix}) ^3pt \parbreak
+|x1=.5w;|\parbreak
+|x2=x4=leftstemloc;|\parbreak
+|x3=x5=w-x2;|\parbreak
+|top y1=h+o;|\parbreak
+|y2=y3=barheight;|\parbreak
+|bot y4=bot y5=-o;|\parbreak
+|draw z4--z2--z3--z5;|\parbreak
+|super_half(2,1,3);|\parbreak
+|labels(1,2,3,4,5);|\parbreak
+|endchar;|\par\smallskip\noindent
+Notice that all three calls of |super_half| in |logo.mf| are of the form
+`"super\_half"$(2,j,3)$'. But it would not be good style to eliminate
+parameters $i$ and~$k$, even though |super_half| is a ^{special-purpose}
+subroutine; that would make it too too special.
+
+\danger Besides parameters of type @expr@ and @suffix@, \MF\ also
+allows a third type called ^@text@. In this case the actual argument
+is any sequence of tokens, and this sequence is not evaluated
+beforehand; a text argument is simply copied in place of the
+corresponding parameter. This makes it possible to write macros that
+deal with lists of things. For example, Appendix~B's `@define\_pixels@'
+macro is defined thus:
+\begintt
+def define_pixels(text t) =
+ forsuffixes a=t: a := a# * hppp; endfor enddef;
+\endtt
+this means that `|define_pixels(em,cap)|' will expand into
+\begintt
+forsuffixes a=em,cap: a := a# * hppp; endfor
+\endtt
+which, in turn, expands into the tokens `|em|~|:=|~|em#|~|*|~|hppp;|
+|cap|~|:=|~|cap#|~|*|~|hppp;|' as we will see in Chapter~19.
+
+\danger Let's look now at a subroutine for drawing ^{serifs}, since
+this typifies the sort of special-purpose macro one expects to see
+in the design of a meta-typeface. Serifs can take many forms,
+so we must choose from myriads of possibilities. We shall consider
+two rather different approaches, one based on outline-filling and the
+other based on the use of a fixed pen nib. In both cases it will be
+necessary to omit some of the refinements that would be desirable
+in a complete typeface design, to keep the examples from
+getting too complicated.
+
+\danger
+\parshape 13
+3pc 13pc
+3pc 13pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 16pc
+0pc 29pc
+Our first example is a serif routine that
+constructs six points $z_{\$a}$, $z_{\$b}$, \dots,~$z_{\$\mkern-1muf}$ around a
+\rightfig 18b (48mm x 40mm) ^26pt
+given triple of ``^{penpos}'' points $z_{\$l}$, $z_{\$}$, $z_{\$r}$; here
+\$ is a suffix that's a parameter to the "serif" macro. Other parameters
+are: "breadth", the distance between the parallel lines that run from
+$z_{\$l}$ to $z_{\$a}$ and from $z_{\$r}$ to $z_{\$\mkern-1muf}$; "theta", the
+direction angle of those two lines; "left\_jut", the distance from
+$z_{\$l}$ to $z_{\$b}$; and "right\_jut", the distance from $z_{\$r}$ to
+$z_{\$e}$. \ (The serif ``juts out'' by the amounts of the
+^{jut} parameters.) \ There's also a "serif\_edge" macro, which constructs
+the path shown. The routines refer to three variables that are assumed to
+apply to all serifs: "slab", the vertical distance from $z_{\$b}$~%
+and~$z_{\$e}$ to $z_{\$c}$~and~$z_{\$d}$; "bracket", the vertical distance
+from $z_{\$a}$~and~$z_{\$\mkern-1muf}$ to $z_{\$l}$~and~$z_{\$r}$; and
+"serif\_darkness", a fraction that controls how much of the triangular
+regions $(z_{\$a},z_{\$l},z_{\$b})$ and $(z_{\$\mkern-1muf},z_{\$r},z_{\$e})$
+^^{]]} will be filled in.
+\begindisplay
+@def@ "serif"\thinspace(@suffix@ \$)(@expr@
+ $"breadth","theta","left\_jut","right\_jut")=$\cr
+\quad $\penpos\$("breadth"/{\rm abs\,sind}\,"theta",0)$;\cr
+\quad $z_{\$a}-z_{\$l}=z_{\$\mkern-1muf}-z_{\$r}=
+ ("bracket"/{\rm abs\,sind}\,"theta")\ast {\rm dir}\,"theta"$;\cr
+\quad $y_{\$c}=y_{\$d}$; \ $y_{\$b}=y_{\$e}=y_\$$; \
+ $y_{\$b}-y_{\$c}=@if@\;"theta"<0:\;{-}\;@fi@\;"slab"$;\cr
+\quad $x_{\$b}=x_{\$c}=x_{\$l}-"left\_jut"$; \
+ $x_{\$d}=x_{\$e}=x_{\$r}+"right\_jut"$;\cr
+\quad @labels@$(\$a,\$b,\$c,\$d,\$e,\$\mkern-1muf)$ @enddef@;\cr
+\noalign{\smallskip}
+@def@ "serif\_edge" @suffix@ \$ =\cr
+\quad $\bigl("serif\_bracket"(\$a,\$l,\$b)\dashto z_{\$c}$\cr
+\qquad $\dashto z_{\$d}\dashto {\rm reverse}\,
+ "serif\_bracket"(\$\mkern-1muf,\$r,\$e)\bigr)$ @enddef@;\cr
+\noalign{\smallskip}
+@def@ "serif\_bracket"(@suffix@ $i,j,k$) $=$\cr
+\quad $\bigl(z.i\{z.j-z.i\}
+ \ldots"serif\_darkness"[z.j,.5[z.i,z.k]\,]\{z.k-z.i\}$\cr
+\qquad$\ldots z.k\{z.k-z.j\}\bigr)$ @enddef@;\cr
+\enddisplay
+
+\dangerexercise Under what circumstances will the "serif\_edge"
+go through points $z_{\$l}$ and $z_{\$r}$?
+\answer If $"bracket"=0$ or $"serif\_darkness"=0$. \ (It's probably
+not a good idea to make $"serif\_darkness"=0$, because this would lead to
+an extreme case of the `$\ldots$' triangle, ^^{...} which might not
+be numerically stable in the presence of rounding errors.)
+Another case, not really desirable, is $"left\_jut"="right\_jut"=0$.
+
+
+\dangerexercise Should this "serif" macro be used before
+points $z_{\$l}$, $z_\$$, and $z_{\$r}$ have been defined, or should those
+points be defined first?
+\answer That's a strange question. The "serif" routine includes a
+"penpos" that defines $z_{\$l}$, $z_\$$, and $z_{\$r}$ relative
+to each other, and it defines the other six points relative to them.
+Outside the routine the user ought to specify just one $x$~coordinate
+and one $y$~coordinate, in order to position all of the points.
+This can be done either before or after "serif" is called, but
+\MF\ has an easier job if it's done beforehand.
+
+\danger Here are two sample letters that show how these serif routines
+might be used. The programs assume that the font has several additional
+ad~hoc parameters: $u$,~a~unit of character width; "ht",~the character
+height; "thin" and "thick", the two stroke weights; and "jut", the amount
+by which serifs protrude on a ``normal'' letter like `H'.
+
+\begingroup\ninepoint\noindent
+\displayfig 18c (252\apspix)
+$$\halign to\hsize\bgroup\indent#\hfil\tabskip1em plus1fil minus1fil
+ &\tabskip0pt\hfil\%\ #\cr
+@beginchar@\kern1pt(|"A"|$,13u\0,ht\0,0)$;\cr
+$z_1=(.5w,1.05h)$;&top point\cr
+$x_{4l}=w-x_{5r}=u$; \ $y_{4l}=y_{5r}="slab"$;&bottom points\cr
+@numeric@ $"theta"[\,]$;\cr
+$"theta"_4={\rm angle}(z_1-z_{4l})$;&left stroke angle\cr
+$"theta"_5={\rm angle}(z_1-z_{5r})$;&right stroke angle\cr
+$"serif"(4,"thin","theta"_4,.6"jut","jut")$;&left serifs\cr
+$"serif"(5,"thick","theta"_5,"jut",.6"jut")$;&right serifs\cr
+$z_0=z_{4r}+"whatever"\ast{\rm dir}\,"theta"_4$\cr
+\qquad$=z_{5l}+"whatever"\ast{\rm dir}\,"theta"_5$;&inside top point\cr
+@fill@ $z_1\dashto "serif\_edge"_4\dashto z_0$&the left stroke\cr
+\qquad$\&\;z_0\dashto "serif\_edge"_5\dashto z_1\;\&\;\cycle$;&the
+ right stroke\cr
+$\penpos2("whatever","theta"_4)$;\cr
+$\penpos3("whatever","theta"_5)$;\cr
+$y_{2r}=y_{3r}=.5[y_4,y_0]$;&crossbar height\cr
+$y_{2l}=y_{3l}=y_{2r}-"thin"$;&crossbar thickness\cr
+$z_2="whatever"[z_1,z_{4r}]$;\cr
+$z_3="whatever"[z_1,z_{5l}]$;\cr
+@penstroke@ $z_{2e}\dashto z_{3e}$;&the crossbar\cr
+@penlabels@$(0,1,2,3,4,5)$; \ @endchar@;\cr
+\noalign{\medskip}
+@beginchar@\kern1pt(|"I"|$,6u\0,ht\0,0)$;\cr
+$x_1=x_2=.5w$;\cr
+$y_1=h-y_2$; \ $y_2="slab"$;\cr
+"serif"$(1,"thick",-90,1.1jut,1.1jut)$;&upper serifs\cr
+"serif"$(2,"thick",90,1.1jut,1.1jut)$;&lower serifs\cr
+@fill@ $"serif\_edge"_2\dashto{\rm reverse}\,"serif\_edge"_1\dashto\cycle$;
+ &the stroke\cr
+@penlabels@$(1,2)$; \ @endchar@;\cr
+\enddisplay
+The illustration was prepared with $"thin"=.5"pt"$, $"thick"=1.1"pt"$,
+$u=.6"pt"$, $"ht"=7"pt"$, $"slab"=.25"pt"$, $"jut"=.9"pt"$, $"bracket"="pt"$,
+and $"serif\_darkness"=1/3$.
+\par\endgroup
+
+\dangerexercise Could the equations defining $y_1$ and $y_2$ in the program
+for~|"I"| have been replaced by `$y_{1c}=h$' and `$y_{2c}=0$'?
+\answer Yes; see the previous exercise. \ (But in the program for |"A"|
+it's necessary to define $y_{4l}$ and $y_{5r}$, so that $"theta"_4$
+and~$"theta"_5$ can be calculated.)
+
+\dangerexercise Write the program for an |"H"| to go with these letters.
+\answer \rightfig A18b (48mm x 43mm) ^10pt
+@beginchar@\kern1pt(|"H"|$,13u\0,"ht"\0,0)$;\parbreak
+$x_1=x_2=x_5=3u$;\parbreak
+$x_3=x_4=x_6=w-x_1$;\parbreak
+$y_{1c}=y_{3c}=h$; \ $y_{2c}=y_{4c}=0$;\parbreak
+$"serif"(1,"thick",-90,"jut","jut")$;\parbreak
+$"serif"(2,"thick",90,"jut","jut")$;\parbreak
+$"serif"(3,"thick",-90,"jut","jut")$;\parbreak
+$"serif"(4,"thick",90,"jut","jut")$;\parbreak
+@fill@ $"serif\_edge"_2$\parbreak
+\quad$\dashto{\rm reverse}\,"serif\_edge"_1\dashto\cycle$;\parbreak
+@fill@ $"serif\_edge"_4$\parbreak
+\quad$\dashto{\rm reverse}\,"serif\_edge"_3\dashto\cycle$;\parbreak
+$\penpos5("thin",90)$; \ $\penpos6("thin",90)$;\parbreak
+$y_5=y_6=.52h$; \ @penstroke@ $z_{5e}\dashto z_{6e}$;\parbreak
+@penlabels@$(1,2,3,4,5,6)$; \ @endchar@.
+
+\ddanger A second approach to serifs can be based on the example at
+the end of Chapter~16. In this case we assume that "broad\_pen" is
+a `@pensquare@ xscaled~"px" yscaled~"py" rotated~"phi"' for
+some $"px">"py"$ and some small angle~"phi". Thicker strokes will
+be made by using this pen to fill a larger region; the serif routine
+is given the distance "xx" between $z_{\$l}$ and $z_{\$r}$.
+There's a pair variable called "dishing" that
+controls the curvature between $z_{\$c}$ and~$z_{\$d}$. Top and
+bottom serifs are similar, but they are sufficiently different that it's
+easier to write separate macros for each case.
+\begindisplay
+@def@ "bot\_serif"(@suffix@ \$)(@expr@ $"xx","theta",
+ "left\_jut","right\_jut")=$\cr
+\quad $\penpos\$("xx",0)$; \
+ $z_{\$a}-z_{\$l}=z_{\$\mkern-1muf}-z_{\$r}=
+ ("bracket"/{\rm abs\,sind\,}"theta")\ast{\rm dir}\,"theta"$;\cr
+\quad $y_{\$c}="top"\,y_{\$l}$; \ $y_{\$d}=y_{\$r}$; \
+ $x_{\$c}=x_{\$l}-"left\_jut"$; \ $x_{\$d}=x_{\$r}+"right\_jut"$;\cr
+\quad $z_{\$b}=z_{\$l}+"whatever"\ast{\rm dir}\,"theta"
+ =z_{\$c}+"whatever"\ast{\rm dir}\,"phi"$;\cr
+\quad $z_{\$e}=z_{\$r}+"whatever"\ast{\rm dir}\,"theta"
+ =z_{\$d}+"whatever"\ast{\rm dir}\,{-"phi"}$;\cr
+\quad @labels@$(\$a,\$b,\$c,\$d,\$e,\$\mkern-1muf)$ @enddef@;\cr
+\noalign{\smallskip}
+@def@ "bot\_serif\_edge" @suffix@ \$ $=$\cr
+\quad $\bigl(z_{\$a}\to\controls z_{\$b}\to z_{\$c}$\cr
+\qquad $\dashto("flex"(z_{\$c},.5[z_{\$c},z_{\$d}]+"dishing",
+ z_{\$d}))$ shifted $(0,-"epsilon")$\cr
+\qquad $\dashto z_{\$d}\to\controls z_{\$e}\to z_{\$\mkern-1muf}
+ \bigr)$ @enddef@;\cr
+\enddisplay
+\displayfig 18d (272\apspix)
+\begindisplay
+@beginchar@\kern1pt(|"A"|$,13u\0,"ht"\0,0)$; \ @pickup@ "broad\_pen";\cr
+$z_1=(.5w,"top"\,h)$; \ $"lft"\,x_{4l}=w-"rt"\,x_{5r}=1.2u$; \
+ $y_{4l}=y_{5r}=0$;\cr
+@numeric@ $"theta"[\,]$; \ $"theta"_4={\rm angle}(z_1-z_{4l})$; \
+ $"theta"_5={\rm angle}(z_1-z_{5r})$;\cr
+@numeric@ "xxx";
+\hbox spread-8pt{%
+$"px"\ast{\rm sind}("theta"_5-"phi")+"xxx"\ast{\rm sind}\,"theta"_5
+ = "px"\ast{\rm cosd}\,"phi"+"xx"$};\cr
+$"bot\_serif"(4,0,"theta"_4,.8"jut",.8"jut")$; \
+$"bot\_serif"(5,"xxx","theta"_5,.6"jut",.8"jut")$;\cr
+$z_0=z_{4r}+"whatever"\ast{\rm dir}\,"theta"_4
+ =z_{5l}+"whatever"\ast{\rm dir}\,"theta"_5$;\cr
+@filldraw@ $z_1\dashto "bot\_serif\_edge"_4
+ \dashto z_0\;\&\;z_0\dashto "bot\_serif\_edge"_5
+ \dashto z_1\;\&\;\cycle$;\cr
+$"top"\,y_2="top"\,y_3=.45"bot"\,y_0$; \
+ $z_2="whatever"[z_1,z_{4r}]$; \ $z_3="whatever"[z_1,z_{5l}]$;\cr
+@draw@ $z_2\dashto z_3$; \ @penlabels@$(0,1,2,3,4,5)$; @endchar@;\cr
+\noalign{\medskip}
+@beginchar@\kern1pt(|"I"|$,6u\0,"ht"\0,0)$; \ @pickup@ "broad\_pen";\cr
+$x_1=x_2=.5w$; \ $y_1=h$; \ $y_2=0$;\cr
+$"top\_serif"(1,"xx",-90,1.1"jut",1.1"jut")$; \
+$"bot\_serif"(2,"xx",90,1.1"jut",1.1"jut")$;\cr
+@filldraw@ $"bot\_serif\_edge"_2\dashto
+ {\rm reverse}\,"top\_serif\_edge"_1\dashto\cycle$;\cr
+@penlabels@$(1,2)$; \ @endchar@;\cr
+\enddisplay
+In the illustration, $"px"=.8"pt"$, $"py"=.2"pt"$, $"phi"=20$,
+$"xx"=.3"pt"$, $u=.6"pt"$, $"ht"=7"pt"$, $"jut"=.9"pt"$, $"bracket"="pt"$,
+and $"dishing"=(.25"pt",0)$ rotated~20.
+
+\ddangerexercise Write the missing code for "top\_serif" and
+"top\_serif\_edge".
+\answer @def@ "top\_serif"(@suffix@ \$)(@expr@ $"xx","theta",
+ "left\_jut","right\_jut")=$\parbreak
+\quad $\penpos\$("xx",0)$; \
+$z_{\$a}-z_{\$l}=z_{\$\mkern-1muf}-z_{\$r}=
+ ("bracket"/{\rm abs\,sind\,}"theta")\ast{\rm dir}\,"theta"$;\parbreak
+\quad $y_{\$c}=y_{\$d}=y_\$$; \
+ $x_{\$c}=x_{\$l}-"left\_jut"$; \ $x_{\$d}=x_{\$r}+"right\_jut"$;\parbreak
+\quad $z_{\$b}=z_{\$l}+"whatever"\ast{\rm dir}\,"theta"
+ =z_{\$c}+"whatever"\ast{\rm dir}\,{-"phi"}$;\parbreak
+\quad $z_{\$e}=z_{\$r}+"whatever"\ast{\rm dir}\,"theta"
+ =z_{\$d}+"whatever"\ast{\rm dir}\,"phi"$;\parbreak
+\quad @labels@$(\$a,\$b,\$c,\$d,\$e,\$\mkern-1muf)$ @enddef@;\par
+\smallskip\indent
+@def@ "top\_serif\_edge" @suffix@ \$ $=$\parbreak
+\quad $\bigl(z_{\$a}\to\controls z_{\$b}\to z_{\$c}$\parbreak
+\qquad $\dashto("flex"(z_{\$c},.5[z_{\$c},z_{\$d}]-"dishing",
+ z_{\$d}))$ shifted $(0,+"epsilon")$\parbreak
+\qquad $\dashto z_{\$d}\to\controls z_{\$e}\to z_{\$\mkern-1muf}
+ \bigr)$ @enddef@;
+
+\ddangerexercise (For mathematicians.) \
+Explain the equation for "xxx" in the program for~|"A"|.
+\answer Assuming that $"py"=0$, the effective right stroke weight would be
+$"px"\cdot\sin(\theta_5-\phi)$ if it were drawn with one stroke of "broad\_pen",
+and $"xxx"\cdot\sin\theta_5$ is the additional weight corresponding to separate
+strokes "xxx" apart. The right-hand side of the equation is the same
+calculation in the case of vertical strokes ($\theta=90^\circ$), when the
+stroke weight of |"I"| is considered. \ (Since a similar calculation
+needs to be done for the letters K, V, W, X, Y, and Z, it would be a good
+idea to embed these details in another macro.)
+
+\ddangerexercise Write the program for an |"H"| to go with these letters.
+\answer \rightfig A18c (48mm x 45mm) ^10pt
+@beginchar@\kern1pt(|"H"|$,13u\0,"ht"\0,0)$;\parbreak
+$x_1=x_2=x_5=3u$;\parbreak
+$x_3=x_4=x_6=w-x_1$;\parbreak
+$y_1=y_3=h$; \ $y_2=y_4=0$;\parbreak
+$"top\_serif"(1,"xx",-90,"jut","jut")$;\parbreak
+$"bot\_serif"(2,"xx",90,"jut","jut")$;\parbreak
+$"top\_serif"(3,"xx",-90,"jut","jut")$;\parbreak
+$"bot\_serif"(4,"xx",90,"jut","jut")$;\parbreak
+@filldraw@ $"bot\_serif\_edge"_2$\parbreak
+\quad$\dashto{\rm reverse}\,"top\_serif\_edge"_1\dashto\cycle$;\parbreak
+@fill@ $"bot\_serif\_edge"_4$\parbreak
+\quad$\dashto{\rm reverse}\,"top\_serif\_edge"_3\dashto\cycle$;\parbreak
+$y_5=y_6=.52h$; \ @draw@ $z_5\dashto z_6$;\parbreak
+@penlabels@$(1,2,3,4,5,6)$; \ @endchar@.
+
+\danger A close look at the "serif\_edge" routines in these examples
+will reveal that some parentheses are curiously lacking: We said
+`@def@ "serif\_edge" @suffix@~\$' instead of
+`@def@ "serif\_edge"(@suffix@~\$)', and we used the macro by saying
+`$"serif\_edge"_5$' instead of
+`$"serif\_edge"(5)$'. The reason is that \MF\ allows the final parameter
+of a macro to be without delimiters; this is something that could not
+have been guessed from a study of previous examples. It is time now
+to stop looking at specific cases and to start examining the complete
+set of rules for macro definitions. Here is the syntax:
+\beginsyntax
+<definition>\is<definition heading><is><replacement text>[enddef]
+<is>\is[=]\alt[:=]
+<definition heading>\is[def]<symbolic token><parameter heading>
+ \alt<vardef heading>
+ \alt<leveldef heading>
+<parameter heading>\is<delimited parameters><undelimited parameters>
+<delimited parameters>\is<empty>
+ \alt<delimited parameters>[(]<parameter type><parameter tokens>[)]
+<parameter type>\is[expr]
+ \alt[suffix]
+ \alt[text]
+<parameter tokens>\is<symbolic token>
+ \alt<parameter tokens>[,]<symbolic token>
+<undelimited parameters>\is<empty>
+ \alt[primary]<symbolic token>
+ \alt[secondary]<symbolic token>
+ \alt[tertiary]<symbolic token>
+ \alt[expr]<symbolic token>
+ \alt[expr]<symbolic token>[of]<symbolic token>
+ \alt[suffix]<symbolic token>
+ \alt[text]<symbolic token>
+\endsyntax
+(We'll discuss ^\<vardef heading> and ^\<leveldef heading> in Chapter~20.)
+\ The basic idea is that we name the macro to be defined, then we name
+zero or more delimited parameters (i.e., parameters in parentheses),
+then we name zero or more undelimited parameters. Then comes an `$=$'~sign,
+followed by the replacement text, and @enddef@. The `$=$'~sign might also
+be~`$:=$'\thinspace; both mean the same thing.
+
+\danger Delimited parameters are of type @expr@, @suffix@, or @text@;
+two or more parameters of the same type may be listed together, separated
+by commas. For example, `(@expr@~$a,b$)' means exactly the same thing as
+`(@expr@~$a$)(@expr@~$b$)'. Undelimited parameters have eight possible
+forms, as shown in the syntax.
+
+\ninepoint % all dangerous from here on
+
+\danger The \<replacement text> is simply filed away for future use,
+not interpreted, when \MF\ reads a definition. But a few tokens are
+treated specially:\enddanger\nobreak
+
+\medskip
+\item\bull @def@, ^@vardef@, ^@primarydef@, ^@secondarydef@, and
+^@tertiarydef@ are considered to introduce definitions inside definitions.
+
+\smallskip
+\item\bull @enddef@ ends the replacement text, unless it matches a
+previous @def@-like token (as listed in the preceding rule).
+
+\smallskip
+\item\bull Each \<symbolic token> that stands for a parameter, by
+virtue of its appearance in the \<parameter heading> or \<leveldef
+heading>, is changed to a special in\-ternal ``parameter
+token'' wherever it occurs in the
+replacement text. Whenever this special token is subsequently encountered,
+\MF\ will substitute the appropriate argument.
+
+\smallskip
+\item\bull ^@quote@ disables any special interpretation of the immediately
+following token. A~`@quote@' doesn't survive in the replacement text
+(unless, of course, it has been quoted).
+
+\dangerexercise Check your understanding of these rules by
+figuring out what the replacement text is, in the following weird definition:
+\begintt
+def foo(text t) expr e of p :=
+ def t = e enddef; quote def quote t = p enddef
+\endtt
+\answer The replacement text contains ten tokens,
+\begindisplay
+\ttok{def}\quad\<t>\quad\ttok{=}\quad\<e>\quad\ttok{enddef}
+\quad\ttok{;}\quad\ttok{def}\quad\ttok{t}\quad\ttok{=}\quad\<p>
+\enddisplay
+where \<t>, \<e>, and \<p> are placeholders for argument insertion.
+When this macro is expanded with $"tracingmacros">0$, \MF\ will type
+\begintt
+foo(TEXT0)<expr>of<primary>->def(TEXT0)=(EXPR1)enddef;def.t=(EXPR2)
+\endtt
+followed by the arguments |(TEXT0)|, |(EXPR1)|, and |(EXPR2)|.
+
+\danger \MF\ does not expand macros when it reads a \<definition>;
+but at almost all other times it will replace a defined token by the
+corresponding replacement text, after finding all the arguments.
+The replacement text will then be read as if it had been present
+in the program all along.
+
+\danger How does \MF\ determine the arguments to a macro? Well,
+it knows what kinds of arguments to expect, based on the parameter
+heading. Let's consider delimited arguments first:\enddanger\nobreak
+
+\medskip
+\item\bull A delimited
+@expr@ argument should be of the form `(\<expression>)'; the expression
+is evaluated and put into a special ``^{capsule}'' token that will be
+substituted for the parameter wherever it appears in the replacement text.
+
+\smallskip
+\item\bull A delimited @suffix@ argument should be of the form
+`(\<suffix>)'; subscripts that occur in the suffix are evaluated
+and replaced by numeric tokens. The result is a list of zero or more
+tokens that will be substituted for the parameter wherever it appears
+in the replacement text.
+
+\smallskip
+\item\bull A delimited @text@ argument should be of the form
+`(\<text>)', where \<text> is any sequence of tokens that is balanced
+with respect to the delimiters surrounding it. This sequence of tokens
+will be substituted for the parameter wherever it appears in the
+replacement text.
+
+\smallskip
+\item\bull When there are two or more delimited parameters, you can
+separate the arguments by commas instead of putting parentheses around
+each one. For example, three delimited arguments could be written
+either as `$(a)(b)(c)$' or `$(a,b)(c)$' or `$(a)(b,c)$' or `$(a,b,c)$'.
+However, this abbreviation doesn't work after text arguments, which
+must be followed by~`)' because text arguments can include commas.
+
+\ddanger Chapter 8 points out that you can use other ^{delimiters}
+besides parentheses. In general, a comma following a delimited
+@expr@ or @suffix@ argument is equivalent to two tokens `)\thinspace(',
+corresponding to whatever delimiters enclose that comma.
+
+\ddangerexercise After `|def| |f(expr| |a)(text| |b,c)=...enddef|'
+and `|delimiters|~|{{|~|}}|', what are the arguments in
+`|f{{x,(,}}((}}))|'?
+\answer According to the rule just stated, the first comma is an
+abbreviation for `|}}|~|{{|'. Hence the first argument is a capsule
+containing the value of~$x$; the second is the text `|(,|'\thinspace;
+the third is the text `|(}})|'.
+
+\danger The rules for undelimited arguments are similar. An
+undelimited @primary@, @secondary@, @tertiary@, or @expr@ is the
+longest syntactically correct ^\<primary>, ^\<secondary>, ^\<tertiary>,
+or ^\<expression> that immediately follows the delimited arguments.
+An undelimited `@expr@~$x$~^{of}~$y$' specifies two arguments, found
+by taking the longest syntactically correct \<expression>~of~\<primary>.
+In each of these cases, the expression might also be preceded by an
+optional `^{=}' or~`^{:=}'. An undelimited @suffix@ is the longest
+\<suffix> that immediately follows the delimited arguments; \MF\ also
+allows `(\<suffix>)' in this case, but not `=\<suffix>' or `:=\<suffix>'.
+An undelimited @text@ essentially runs to the end of the current
+statement; more precisely, it runs to the first `;'\ or `^@endgroup@' or
+`^@end@' that is not part of a ^{group} within the argument.
+
+\danger Appendix B contains lots of macros that illustrate these
+rules. For example,
+\begindisplay
+@def@ ^@fill@ @expr@ $c$ $=$ @addto@ "currentpicture" @contour@ $c$ @enddef@;\cr
+@def@ ^@erase@ @text@ $t$ $=$ @cullit@; \ $t$ @withweight@ $-1$;
+ @cullit@ @enddef@;\cr
+\enddisplay
+these are slight simplifications of the real definitions, but they retain the
+basic ideas. The command `@erase@~@fill@~$p$' causes `@fill@~$p$' to be
+the @text@ argument to~@erase@, after which `$p$' becomes the @expr@
+argument to~@fill@.
+
+\ddangerexercise The `@pickup@' macro in Appendix B starts with
+`@def@~@pickup@~@secondary@~$q$'; why is the argument a secondary
+instead of an expression?
+\answer This snares ^{future pen}s before they're converted to pens, because
+@pickup@ wants to yscale by "aspect\_ratio" before ellipses change to
+polygons.
+
+\ddangerexercise Explain why the following `^"hide"' macro allows you to
+hide any sequence of statements in the midst of an expression:
+\begindisplay
+@def@ "hide"(@text@ $t)="gobble"@begingroup@\,t;$ @endgroup@ @enddef@;\cr
+@def@ "gobble" @primary@ $g=@enddef@$;\cr
+\enddisplay
+\answer The construction `"hide"\thinspace(\<statement list>)' expands into
+`"gobble" @begingroup@ \<statement list>; @endgroup@', so the
+argument to "gobble" must be evaluated. The @begingroup@ causes \MF\
+to start executing statements. When that has been done, the final
+statement turns out to be \<empty>, so the argument to "gobble"
+turns out to be a ^{vacuous} expression (cf.\ Chapter~25). Finally,
+"gobble"'s replacement text is empty, so the hidden text has indeed
+disappeared. \ (The "hide" macro in Appendix~B is actually a bit
+more efficient, but a bit trickier.)
+
+\endchapter
+
+DEFINI\/$'$\kern-.5ptTION, {\rm s. \ [definitio}, Latin.{\rm]}
+1. A short description of a thing by its properties.
+\author SAMUEL ^{JOHNSON}, {\sl A Dictionary of the English Language\/} (1755)
+
+\bigskip
+
+DEFINI\/$''$\kern-.5ptTION, {\rm n. \ [{\sl L.} definitio}. See\/ {\rm Define.]}
+1. A brief description of a thing by its properties;
+as a\/ {\rm definition} \kern-.5pt of wit or of a circle.
+\author NOAH~^{WEBSTER}, {\sl An~American~%
+ Dictionary~of~the~English~Language\/}~(1828)
+
+\eject
+ \beginchapter Chapter 19. Conditions\\and Loops
+
+If decisions never had to be made, life would be much easier, and so would
+programming. But sometimes it is necessary to choose between alternatives,
+and \MF\ allows programs to take different paths depending on the circumstances.
+You just say something like
+\begindisplay
+@if@ not "decisions": \ $"life":="programming":="easier"("much")$\cr
+@elseif@ $"choice"=a$: \ "program\_a"\cr
+@else@: \ "program\_b" \ @fi@\cr
+\enddisplay
+which reduces, for example, to `"program\_b"' if and only if
+$"decisions"=@true@$ and $"choice"\ne a$. The normal left-to-right
+order of program interpretation can also be modified by specifying
+``^{loops},'' which tell the computer to read certain tokens repeatedly,
+with minor variations, until some ^{condition} becomes true. We have
+seen many examples of these mechanisms already; the purpose of the
+present chapter is to discuss the entire range of possibilities.
+
+\MF's conditions and loops are different from those in most other
+programming languages, because the conditional or iterated code does
+not have to fit into the syntactic structure. For example, you can
+write strange things like
+\begintt
+p = (if b: 0,0)..(1,5 else: u,v fi)
+\endtt
+where the conditional text `$0,0)\to(1,5$' makes no sense by itself,
+although it becomes meaningful when read in context. In this respect
+conditions and loops behave like macros. They specify rules of
+token transformation that can be said to take place in \MF's ``^{mouth}''
+before the tokens are actually digested in the computer's ``^{stomach}.''
+
+The first conditional example above has three alternatives, in the form
+\begindisplay
+@if@ \<boolean$_1$>: \<text$_1$> \
+@elseif@ \<boolean$_2$>: \<text$_2$> \
+@else@: \<text$_3$> \ @fi@
+\enddisplay
+and the second example has just two; there can be any number of
+`^@elseif@\kern1pt' clauses before `^@else@:'. Only one of the conditional
+texts will survive, namely the first one whose condition is true;
+`@else@:'\ is always true. You can also omit `@else@:'\
+entirely, in which case `@else@:\thinspace\<empty>' is implied just before
+the closing `^@fi@'. For example, plain \MF's @mode\_setup@ routine
+includes the conditional~command
+\begindisplay
+@if@ unknown "mag": \ $"mag":=1$; \ @fi@
+\enddisplay
+whose effect is to set "mag" equal to 1 if it hasn't already received
+a value; in this case there's only one alternative.
+
+\exercise Would it be wrong to put the `;' after the `@fi@' in the example
+just given?
+\answer Then \MF's ``stomach'' would see `;' if "mag" is known, but there
+would be no change if "mag" is unknown. An extra semicolon is harmless,
+since \MF\ statements can be \<empty>. But it's wise to get in the habit
+of putting `;' before @fi@, because it saves a wee bit of time and because
+`;' definitely belongs before ^@endfor@.
+
+\danger The informal rules just stated can, of course, be expressed more
+formally as rules of syntax:
+\beginsyntax
+<condition>\is[if]<boolean expression>[:]<conditional text><alternatives>[fi]
+<alternatives>\is<empty>
+ \alt[else][:]<conditional text>
+ \alt[elseif]<boolean expression>[:]<conditional text><alternatives>
+\endsyntax
+Every conditional construction begins with `^@if@\kern1pt' and ends with
+`@fi@'. The conditional texts are any sequences of tokens that are
+balanced with respect to `@if@\kern1pt' and~`@fi@'; furthermore,
+`@elseif@\kern1pt' and `@else@' can occur in a conditional text only when
+enclosed by `@if@\kern1pt' and~`@fi@'.
+
+\danger Each `@if@\kern1pt' and `@elseif@\kern1pt' must be followed by a
+\<boolean expression>, i.e., by an expression whose value is either
+`@true@' or `@false@'. ^{Boolean expressions} are named after George
+^{Boole}, the founder of algebraic approaches to logic. Chapter~7 points
+out that variables can be of type ^@boolean@, and numerous examples of
+boolean expressions appear in Chapter~8. It's time now to be more
+systematic, so that we will know the facts about boolean expressions just
+as we have become well-versed in numeric expressions, pair expressions,
+picture expressions, path expressions, transform expressions, and pen
+expressions. Here are the relevant syntax rules:
+\beginsyntax
+<boolean primary>\is<boolean variable>
+ \alt[true]\alt[false]
+ \alt[(]<boolean expression>[)]
+ \alt[begingroup]<statement list><boolean expression>[endgroup]
+ \alt[known]<primary>\alt[unknown]<primary>
+ \alt<type><primary>\alt[cycle]<primary>
+ \alt[odd]<numeric primary>
+ \alt[not]<boolean primary>
+<boolean secondary>\is<boolean primary>
+ \alt<boolean secondary>[and]<boolean primary>
+<boolean tertiary>\is<boolean secondary>
+ \alt<boolean tertiary>[or]<boolean secondary>
+<boolean expression>\is<boolean tertiary>
+ \alt<numeric expression><relation><numeric tertiary>
+ \alt<pair expression><relation><pair tertiary>
+ \alt<transform expression><relation><transform tertiary>
+ \alt<boolean expression><relation><boolean tertiary>
+ \alt<string expression><relation><string tertiary>
+<relation>\is[\char'74]\alt[\char'74=]\alt[>]\alt[>=]\alt[=]\alt[\char'74>]
+\endsyntax
+Most of these operations were already explained in Chapter~8, so it's only
+necessary to mention the more subtle points now. A ^\<primary> of any
+type can be tested to see whether it has a specific type, and whether it
+has a known or unknown value based on the equations so far. In these tests,
+a ^\<future pen primary> is considered to be of type ^@pen@. The test
+`cycle~$p$' is true if and only if $p$~is a cyclic path. The `odd' function
+first rounds its argument to an integer, then tests to see if the integer
+is odd. The `not' function changes true to false and vice versa. The `and'
+function yields true only if both arguments are true; the `or' function
+yields true unless both arguments are false. Relations on pairs, transforms,
+or strings are decided by the first unequal component from left to right.
+\ (A ^{transform} is considered to be a 6-tuple as in Chapter~15.) \
+
+\dangerexercise What do you think: Is @false@ $>$ @true@?
+\answer No; that would be shocking.
+
+\dangerexercise Could `(odd $n$) and not (odd $-n$)' possibly be true?
+\answer Yes, if and only if $n-{1\over2}$ is a nonnegative even integer.
+\ (Because ambiguous values are rounded upwards.)
+
+\dangerexercise Could `(cycle $p$) and not (known $p$)' possibly be true?
+\answer No.
+
+\dangerexercise Define an `even' macro such that `even~$n$' is true if
+and only if round$(n)$ is an even integer. \ [{\sl Hint:\/} There's a
+slick answer.]
+\answer @def@ even $=$ not odd @enddef@.
+
+\ddanger Boolean expressions beginning with a ^\<type> should not come
+at the very beginning of a statement, because \MF\ will think that
+a ^\<declaration> is coming up instead of an \<expression>. Thus, for
+example, if $b$~is a boolean variable, the equation `$@path@\,p=b$'
+should be rewritten either as `$b=@path@\,p$' or as `$(@path@\,p)=b$'.
+
+\ddanger A boolean expression like `$x=y$' that involves the ^{equality}
+relation looks very much like an ^{equation}. \MF\ will consider `$=$'
+to be a \<relation> unless the expression to its left occurs at the
+very beginning of a ^\<statement> or the very beginning of a ^\<right-hand
+side>. If you want to change an equation into a relation,
+just insert parentheses, as in `$(x=y)=b$' or `$b=(x=y)$'.
+
+\ddanger After a ^\<path join>, the token `^{cycle}' is not considered
+to be the beginning of a \<boolean primary>. \ (Cf.\ Chapter~14.)
+
+\ddanger The boolean expression `^@path@ $((0,0))$' is false, even
+though `$((0,0))$' meets Chapter~14's syntax rules for
+\<path primary>, via (\<path expression>) and
+(\<path tertiary>) and
+(\<pair tertiary>). A ^{pair expression} is not considered to be
+of type @path@ unless the path interpretation is mandatory.
+
+\ddangerexercise Evaluate `length $((3,4))$' and `length $((3,4)\{0,0\})$'
+and `length reverse~$(3,4)$'.
+\answer The first is~5, because the pair is not considered to be a path.
+The second and third are~0, because the pair is forced to become a path.
+
+OK, that covers all there is to be said about conditions. What about
+loops? It's easiest to explain loops by giving the syntax first:
+\beginsyntax
+<loop>\is<loop header>[:]<loop text>[endfor]
+<loop header>\is[for]<symbolic token><is><for list>
+ \alt[for]<symbolic token><is><progression>
+ \alt[forsuffixes]<symbolic token><is><suffix list>
+ \alt[forever]
+<is>\is[=]\alt[:=]
+<for list>\is<expression>\alt<empty>
+ \alt<for list>[,]<expression>\alt<for list>[,]<empty>
+<suffix list>\is<suffix>
+ \alt<suffix list>[,]<suffix>
+<progression>\is<initial value>[step]<step size>[until]<limit value>
+<initial value>\is<numeric expression>
+<step size>\is<numeric expression>
+<limit value>\is<numeric expression>
+<exit clause>\is[exitif]<boolean expression>[;]
+\endsyntax
+As in macro definitions, `$=$' and `$:=$' are interchangeable here.
+
+This syntax shows that loops can be of four kinds, which we might
+indicate schematically as follows:
+\begindisplay
+@for@ $x=\epsilon_1,\epsilon_2,\epsilon_3$: text($x$) @endfor@\cr
+\noalign{\vskip 1pt plus 1pt}
+@for@ $x=\nu_1$ @step@ $\nu_2$ @until@ $\nu_3$: text($x$) @endfor@\cr
+\noalign{\vskip 1pt plus 1pt}
+@forsuffixes@ $s=\sigma_1,\sigma_2,\sigma_3$: text($s$) @endfor@\cr
+\noalign{\vskip 1pt plus 1pt}
+@forever@: text @endfor@\cr
+\enddisplay
+The first case expands to
+`text($\epsilon_1$) text($\epsilon_2$) text($\epsilon_3$)'; the
+$\epsilon$'s here are expressions of any type, not necessarily ``known,''
+and they are evaluated and put into ^{capsules} before being substituted
+for~$x$. The $\epsilon$'s might also be empty, in which case
+text($\epsilon$) is omitted.
+The second case is more complicated, and it will be explained carefully
+below; simple cases like `1~@step@~2 @until@~7' are equivalent to
+short lists like `$1,3,5,7$'. The third case expands to
+`text($\sigma_1$) text($\sigma_2$) text($\sigma_3$)'; the $\sigma$'s here
+are arbitrary suffixes (possibly empty), in which subscripts will have been
+evaluated and changed to numeric tokens before being substituted for~$s$.
+The final case expands into the sequence `text~text~text~$\ldots$',
+ad~infinitum; there's an escape from this (and from the other three kinds
+of loop) if an \<exit clause> appears in the text, as explained below.
+
+Notice that if the loop text is a single statement that's supposed to
+be repeated several times, you should put a `^{;}' just before the
+@endfor@, not just after it; \MF's loops do not insert ^{semicolons}
+automatically, because they are intended to be used in the midst of
+expressions as well as with statements that are being iterated.
+
+Plain \MF\ defines `^@upto@' as an abbreviation for `@step@~1~@until@',
+and `^@downto@' as an abbreviation for `@step@~$-1$~@until@'. Therefore
+you can say, e.g., `\thinspace@for@ $x=1$ @upto@~9:\thinspace' instead of
+`\thinspace@for@ $x=1,2,3,4,5,6,7,8,9$:\thinspace'.
+
+\danger When you say `@for@ $x=\nu_1$ @step@ $\nu_2$ @until@~$\nu_3$',
+\MF\ evaluates the three numeric expressions, which must have known values.
+Then it reads the loop text. If $\nu_2>0$ and $\nu_1>\nu_3$, or if
+$\nu_2<0$ and $\nu_1<\nu_3$, the loop is not performed at all. Otherwise
+text($\nu_1$) is performed, $\nu_1$ is replaced by $\nu_1+\nu_2$, and
+the same process is repeated with the new value of $\nu_1$.
+
+\dangerexercise Read the rules in the previous paragraph carefully, then
+explain for what values of~$x$ the loop is performed if you say
+(a)~`\thinspace@for@~$x=1$ @step@~2 @until@~0'\thinspace. \
+(b)~`\thinspace@for@~$x=1$ @step@~$-2$ @until@~0\thinspace'. \
+(c)~`\thinspace@for@~$x=1$ @step@~0 @until@~0\thinspace'. \
+(d)~`\thinspace@for@~$x=0$ @step@~.1 @until@~1\thinspace'.
+\answer (a) The loop text is never executed. \
+(b)~It's executed only once, for $x=1$. \
+(c)~It's executed infinitely often, for $x=1,1,1,\ldots\,$. \
+(d)~Since ten times \MF's internal representation of
+.1 is slightly larger than 1, the answer
+is not what you probably expect! The loop text is executed for
+$x=0$,~0.1, 0.20001, 0.30002, 0.40002, 0.50003, 0.60004, 0.70004, 0.80005,
+and 0.90005 only. \ (If you want the values $(0,.1,.2,\ldots,1)$, say
+`\thinspace@for@ $"xx"=0$ @upto@~10: $x:="xx"/10$; \<text> @endfor@' instead.)
+
+\danger A \<loop text> is rather like the \<replacement text> of a macro.
+It is any sequence of tokens that is balanced with respect to
+un^{quote}d appearances of @for@/@forsuffixes@/@forever@ and @endfor@
+delimiters. \MF\ reads the entire loop text quickly and stores it away
+before trying to perform it or to expand macros within it. All occurrences
+of the controlled \<symbolic token> in the loop text are changed to
+special internal parameter tokens that mean ``insert an argument here,''
+where the argument is of type @expr@ in the case of @for@, of
+type @suffix@ in the case of @forsuffixes@. This rule implies, in
+particular, that the symbolic token has no connection with similarly
+named variables elsewhere in the program.
+
+\dangerexercise What values are shown by the following program?
+\begintt
+n=0; for n=1: m=n; endfor show m,n; end.
+\endtt
+\answer $m=1$, $n=0$.
+
+\danger The ^"flex" routine described in Chapter~14 provides an interesting
+example of how loops can be used inside of macros inside of expressions:
+\begindisplay
+@pair@ $"z\_"\,[\,]$, $"dz\_"$; \ @numeric@ "n\_"\thinspace;
+ &\% private variables\cr
+@def@ "flex"(@text@ $t$) $=$&\% $t$ is a list of pairs\cr
+\quad^"hide"$\bigl(\,"n\_":=0$;\cr
+\qquad @for@ $z=t$: $"z\_"\,[{\rm incr}\,"n\_"]:=z$; @endfor@\cr
+\qquad $"dz\_":="z\_"\,["n\_"]-"z\_"\,[1]\,\bigr)$\cr
+\quad $"z\_"\,[1]$ @for@ $k=2$ @upto@ $"n\_"-1$:
+ $\ldots"z\_"\,[k]\{"dz\_"\}$ @endfor@\hidewidth\cr
+\qquad $\ldots"z\_"\,["n\_"]$ @enddef@;\cr
+\enddisplay
+The first loop stores the given pairs temporarily in an array, and it also
+counts how many there are; this calculation is ``hidden.'' Then
+the actual flex-path is contributed to the program with the help of
+a second loop. \ (Appendix~B uses the convention that symbolic tokens
+ending in `^{\_}' should not appear in a user's program; this often
+makes it unnecessary to `^@save@' tokens.)
+
+\danger When \MF\ encounters the construction `^@exitif@ \<boolean
+expression>;', it evaluates the boolean expression. If the
+expression is true, the (innermost) loop being iterated is terminated
+abruptly. Otherwise, nothing special happens.
+
+\dangerexercise Define an `^@exitunless@' macro such that
+`@exitunless@ \<boolean expression>;' will exit the current loop
+if the boolean expression is false.
+\answer @def@ @exitunless@ @expr@ $b$ $=$ @exitif@ not $b$ @enddef@.
+\ (The simpler alternative `@def@ @exitunless@ $=$ @exitif@ not
+@enddef@\kern1pt' wouldn't work, since `not' applies only to the following
+\<boolean primary>.)
+
+\ddangerexercise Write a \MF\ program that sets $p[k]$ to the $k$th
+^{prime number}, for $1\le k\le30$. Thus, $p[1]$ should be~2,
+$p[2]=3$, etc.
+\answer |numeric p[]; boolean n_is_prime; p[1]=2; k:=1;|\parbreak
+|for n=3 step 2 until infinity:|\parbreak
+| n_is_prime:=true;|\parbreak
+| for j=2 upto k: if n mod p[j]=0: n_is_prime:=false; fi|\parbreak
+| exitif n/p[j]<p[j]; endfor|\parbreak
+| if n_is_prime: p[incr k]:=n; exitif k=30; fi|\parbreak
+| endfor fi|\parbreak
+^^@show@^^@str@
+|show for k=1 upto 30: str p[k]&"="&decimal p[k], endfor "done" end.|
+
+\ddangerexercise When you run \MF\ on the file `|expr.mf|' of
+Chapter~8, you get into a `^@forever@' loop that can be stopped
+if you type, e.g., `|0|~|end|'. But what can you type to get out
+of the loop without ending the run? \ (The goal is to make
+\MF\ type~`|*|', without incurring any error messages.)
+\answer `|0; exitif true;|'.
+
+\endchapter
+
+If? thou Protector of this damned Strumpet,
+Talk'st thou to me of Ifs: thou art a Traytor,
+Off with his Head.
+\author WILLIAM ^{SHAKESPEARE}, {\sl Richard the Third\/} (1593)
+
+\bigskip
+
+% When ye pray,
+Use not vain repetitions.
+\author {\sl ^{Matthew} 6\thinspace:\thinspace7\/} (c.~70 A.D.)
+
+\eject
+ \beginchapter Chapter 20. More\\About\\Macros
+
+Chapter 18 gave the basic facts about macro definitions, but it didn't
+tell the whole story. It's time now for the Ultimate Truth to be revealed.
+
+\ninepoint
+\danger But this whole chapter consists of ``dangerous bend'' paragraphs,
+since the subject matter will be appreciated best by people who have
+worked with \MF\ for a little while.
+We shall discuss the following topics:\enddanger
+\smallskip
+\item\bull Definitions that begin with `@vardef@\kern1pt'; these embed macros
+into the variables of a program and extend the unary operators of
+\MF\ expressions.
+
+\item\bull Definitions that begin with `@primarydef@\kern.3pt',
+`@secondarydef@\kern.3pt', or `@tertiarydef@\kern.3pt'; these extend the
+binary operators of \MF\ expressions.
+
+\item\bull Other primitives of \MF\ that expand into sequences of tokens
+in a macro-like way, including `@input@' and `@scantokens@'.
+
+\item\bull Rules that explain when tokens are subject to expansion
+and when they aren't.
+
+\danger First let's consider the \<vardef heading> that was left
+undefined in Chapter~18. The ordinary macros discussed in that chapter
+begin with
+\begindisplay
+@def@ \<symbolic token>\<parameter heading>
+\enddisplay
+and then comes `$=$', etc. You can also begin a definition by saying
+\begindisplay
+^@vardef@ \<declared variable>\<parameter heading>
+\enddisplay
+instead; in this case the ^\<declared variable> might consist of
+several tokens, and you are essentially defining a variable whose
+``value'' is of type ``macro.'' For example, suppose you decide to say
+\begindisplay
+@pair@ $a.p$; \ @pen@ $a.q$; \ @path@ $a.r$; \
+@vardef@ $a.s=\ldots$ @enddef@;
+\enddisplay
+then $a.p$, $a.q$, and $a.r$ will be variables of types @pair@, @pen@,
+and @path@, but $a.s$ will expand into a sequence of tokens. \
+(The language {\eightrm^{SIMULA67}} demonstrated that it is advantageous
+to include procedures as parts of variable data structures; \MF\ does an
+analogous thing with macros.)
+
+\danger After a definition like `@def@ $t=\ldots$', the token $t$ becomes
+a ``^{spark}''; i.e., you can't use it in a suffix. But after
+`@vardef@ $t=\ldots$', the token~$t$ remains a ``^{tag},'' because
+macro expansion will take place only when $t$~is the first token in
+a variable name. Some of the definitions in Appendix~B are vardefs
+instead of defs for just that reason; for example,
+\begindisplay
+@vardef@ dir @primary@ $d$ $=$ "right" rotated $d$ @enddef@
+\enddisplay
+allows a user to have variable names like `|p5dir|'.
+
+\danger A variable is syntactically a primary expression, and \MF\ would
+get unnecessarily confused if the replacement texts of vardef macros
+were very different from primary expressions. Therefore, the
+tokens `^@begingroup@'
+and `^@endgroup@' are automatically inserted at the beginning and end
+of every vardef replacement text. If you say `^@showvariable@~$a$'
+just after making the declarations and definition above, the machine
+will reply as follows:
+\begintt
+a.p=pair
+a.q=unknown pen
+a.r=unknown path
+a.s=macro:->begingroup...endgroup
+\endtt
+
+\danger The `^{incr}' macro of Appendix B increases its argument by~1
+and produces the increased value as its result. The inserted `@begingroup@'
+and `@endgroup@' come in handy here:
+\begindisplay
+@vardef@ incr @suffix@ \$ $=$ $\$:=\$+1$; \ \$ @enddef@.
+\enddisplay
+Notice that the argument is a ^@suffix@, not an @expr@, because
+every variable name is a special case of a ^\<suffix>, and because
+an ^@expr@ parameter should never appear to the left ^^{:=} of~`$:=$'.
+Incidentally, according to the rules for ^{undelimited suffix parameters}
+in Chapter~18, you're allowed to say either `incr~$v$' or `incr$(v)$' when
+applying incr to~$v$.
+
+\danger There's another kind of vardef, in which the variable name being
+defined can have any additional suffix when it is used; this suffix is
+treated as an argument to the macro. In this case you write
+\begindisplay
+@vardef@ \<declared variable>|@#| \<parameter heading>
+\enddisplay
+^^{at sharp} and you can use |@#| in the replacement text (where it
+behaves like any other @suffix@ parameter). For example, Appendix~B says
+\begindisplay
+@vardef@ $z$|@#| $=$ $(x$|@#|$,y$|@#|) @enddef@;
+\enddisplay
+this is the magic definition that makes `$z_{3r}$' equivalent to
+`$(x_{3r},y_{3r})$', etc. In fact, we now know that `|z3r|' actually
+expands into eleven tokens:
+\begintt
+begingroup (x3r, y3r) endgroup
+\endtt
+
+\ddangerexercise True or false: After `|vardef| |a@#| |suffix| |b| |=|
+$\ldots$~|enddef|', the suffix argument~|b| will always be empty.
+\answer False; consider `|a1(2)|'.
+
+\ddanger Plain \MF\ includes a ^"solve" macro that uses ^{binary search}
+to find numerical solutions to ^{nonlinear equations}, which are too
+difficult to resolve in the ordinary way. ^^{equations, nonlinear}
+To use "solve", you first define a macro $f$ such that $f(x)$ is either
+@true@ or @false@; then you say
+\begindisplay
+"solve" $f("true\_x","false\_x")$
+\enddisplay
+where "true\_x" and "false\_x" are values such that $f("true\_x")=@true@$
+and $f("false\_x")=@false@$. The resulting value~$x$ will be at the cutting
+edge between truth and falsity, in the sense that $x$~will be within a
+given ^"tolerance" of values for which $f$ yields both outcomes.
+\begindisplay
+@vardef@ "solve"|@#|(@expr@ $"true\_x","false\_x"$) $=$\cr
+\quad $"tx\_":="true\_x"$; \ $"fx\_":="false\_x"$;\cr
+\quad^@forever@: $"x\_":=.5["tx\_","fx\_"]$; \
+ ^@exitif@ abs$("tx\_"-"fx\_")\le"tolerance"$;\cr
+\quad @if@ |@#|$("x\_"):\ "tx\_" \ @else@:\ "fx\_"\ @fi@$
+ :=\ "x\_"\thinspace; @endfor@;\cr
+\quad "x\_" @enddef@;\cr
+\enddisplay
+
+\ddanger For example, the "solve" routine makes it possible to solve the
+following interesting problem posed by Richard ^{Southall}: Given
+points $z_1$,~$z_2$, $z_3$,~$z_4$ such that $x_1<x_2<x_3<x_4$ and
+$y_1<y_2=y_3>y_4$, find the point~$z$ between $z_2$ and~$z_3$ such that
+\MF\ will choose to travel "right" at~$z$ in the path
+\begindisplay
+$z_1\,\{z_2-z_1\}\to z\to\{z_4-z_3\}\,z_4$.
+\enddisplay
+If we try $z=z_2$, \MF\ will choose a direction at $z$ that has a positive
+(upward) $y$-component; but at $z=z_3$, \MF's chosen direction will have a
+negative (downward) $y$-component. Somewhere in between is a ``^{nice}''
+value of~$z$ for which the curve will not rise above the line $y=y_2$.
+What is this~$z$?
+\displayfig 20a (115\apspix)
+Chapter 14 gives equations from which $z$ could be computed, in principle,
+but those equations involve trigonometry in a complicated fashion.
+It's nice to know that we can find~$z$ rather easily in spite of those
+complexities:
+\begindisplay
+@vardef@ "upward"(@expr@ $x$) $=$\cr
+\quad ypart direction 1 of $\bigl(z_1\{z_2-z_1\}
+ \to(x,y_2)\to\{z_4-z_3\}z_4\bigr)>0$ @enddef@;\cr
+$z=\bigl("solve"\,"upward"(x_2,x_3),y_2\bigr)$.\cr
+\enddisplay
+
+\ddangerexercise It might happen in unusual cases that $"upward"(x)$
+is @false@ for all $x_2\le x\le x_3$, hence "solve" is being invoked
+under invalid assumptions. What result does it give~then?
+\answer A value very close to $z_2$.
+
+\ddangerexercise Use "solve" to find $\root3\of{10}$, and compare
+the answer to the ^{cube root} obtained in the normal way.
+\answer |vardef lo_cube(expr x)=x*x*x<10 enddef;|\parbreak
+|show solve lo_cube(0,10), 10**1/3; end.|\par\nobreak\medskip\noindent
+^^{**} With the default ^"tolerance" of 0.1,
+this will show the respective values |2.14844| and |2.1544|.
+A more general routine could also be written, with `10' as a parameter:
+\begintt
+vardef lo_cube[](expr x)=x*x*x<@ enddef;
+show solve lo_cube10(0,10);
+\endtt
+if we ask for minimum tolerance ($"tolerance":="epsilon"$), the
+result is |2.15445|; the true value is $\approx 2.15443469$.
+
+\ddanger The syntax for \<declared variable> in Chapter~7 allows for
+^{collective subscripts} as well as tags in the name of the variable
+being declared. Thus, you can say
+\begindisplay
+@vardef@ $a[\,]b[\,]=\ldots$ @enddef@;
+\enddisplay
+what does this mean? Well, it means that all variables like |a1b2|
+are macros with a common replacement text. Every vardef has two
+^^{at} ^^{sharp at}
+implicit suffix parameters, `|#@|' and~`|@|', which can be used in
+the replacement text to discover what subscripts have actually been
+used. Parameter~`|@|' is the final token of the variable name
+(`|2|' in this example); parameter `|#@|' is everything preceding
+the final token (in this case `|a1b|'). These notations are supposed to
+be memorable because `|@|' is where you're ``at,'' while `|#@|' is
+everything before and `|@#|' is everything after.
+
+\ddangerexercise After `|vardef| |p[]dir=(#@dx,#@dy)| |enddef|', what's
+the expansion of `|p5dir|'\thinspace?
+\answer |begingroup(p5dx,p5dy)endgroup|.
+
+\ddangerexercise Explain how it's possible to retrieve the first subscript
+in the replacement text of |vardef|~|a[]b[]| (thereby obtaining,
+for example, `|1|' instead of `|a1b|').
+\answer Say `|first#@|' after defining `|vardef| |first.a[]@#=@| |enddef|'.
+\ (There are other solutions, e.g., using substrings of ^@str@~|#@|,
+but this one is perhaps the most instructive.)
+
+\ddangerexercise Say `^|showvariable| |incr,z|' to \MF\ and explain
+^^{incr} ^^{z} the machine's reply.
+\answer The machine answers thus:
+\begintt
+incr=macro:<suffix>->
+ begingroup(SUFFIX2):=(SUFFIX2)+1;(SUFFIX2)endgroup
+z@#=macro:->begingroup(x(SUFFIX2),y(SUFFIX2))endgroup
+\endtt
+Parameters to a macro are numbered sequentially, starting with zero,
+and classified as either ^|(EXPR|$_n$|)|, ^|(SUFFIX|$_n$|)|, or
+^|(TEXT|$_n$|)|. In a vardef, |(SUFFIX0)| and |(SUFFIX1)| are always
+reserved for the implicit parameters |#@| and~|@|; |(SUFFIX2)| will
+be |@#|, if it is used in the parameter heading, otherwise it will be the
+^^{at sharp} ^^{at} ^^{sharp at} first explicit parameter, if
+it happens to be a suffix parameter.
+
+\ddanger A vardef wipes out all type declarations and macro definitions
+for variables whose name begins with the newly defined macro variable name.
+For example, `|vardef|~|a|' causes variables like |a.p|
+and |a1b2| to disappear silently; `|vardef|~|a.s|' wipes out
+|a.s.p|, etc. Moreover, after `|vardef|~|a|' is
+in effect, you are not allowed to say `|pair|~|a.p|' or `|vardef|~|a[]|',
+since such variables would be inaccessible.
+
+\ddanger The syntax for \<definition> in Chapter 18 was incomplete,
+because $\langle$vardef heading$\rangle$ and \<leveldef heading> were
+omitted. Here are the missing rules:
+\beginsyntax
+<vardef heading>\is[vardef]<declared variable><parameter heading>
+ \alt[vardef]<declared variable>[\char'100\#]<parameter heading>
+<leveldef heading>\is<leveldef><parameter><symbolic token><parameter>
+<leveldef>\is[primarydef]\alt[secondarydef]\alt[tertiarydef]
+<parameter>\is<symbolic token>
+\endsyntax
+The new things here are @primarydef@, @secondarydef@, and @tertiarydef@,
+which permit you to extend \MF's repertoire of binary operators. For example,
+the `dotprod' operator is defined as follows in Appendix~B:
+\begindisplay
+@primarydef@ $w$ dotprod $z$ $=$\cr
+\quad $({\rm xpart}\,w\ast{\rm xpart}\,z\;+\;
+ {\rm ypart}\,w\ast{\rm ypart}\,z)$ @enddef@.\cr
+\enddisplay
+\MF's syntax for expressions has effectively gained a new rule
+\beginsyntax
+<numeric secondary>\is<pair secondary>[dotprod]<pair primary>
+\endsyntax
+in addition to the other forms of \<numeric secondary>, because of this
+primarydef.
+
+\ddanger The names `@primarydef@\kern1pt', `@secondarydef@\kern1pt',
+and `@tertiarydef@\kern1pt' may
+seem off by one, because they define operators at one level higher up:
+A primarydef defines a binary operator that forms a secondary expression
+from a secondary and a primary; such operators are at the same level
+as `$\ast$' and `rotated'.
+A secondarydef defines a binary operator that forms a tertiary expression
+from a tertiary and a secondary; such operators are at the same level
+as~`$+$'~and~`or'.
+A tertiarydef defines a binary operator that forms an expression
+from an expression and a tertiary; such operators are at the same level
+as~`$<$'~and~`\&'.
+
+\ddanger Plain \MF's `^{intersectionpoint}' macro is defined by a
+@secondarydef@ because it is analogous to `^{intersectiontimes}', which
+occurs at the same level (namely the secondary~$\rightarrow$~tertiary level):
+\begindisplay
+@secondarydef@ $p$ intersectionpoint $q$ $=$\cr
+\quad @begingroup@ ^@save@ $"x\_","y\_"$; \
+ $("x\_","y\_")=p$ intersectiontimes $q$;\cr
+\quad @if@ $"x\_"<0$: ^@errmessage@(|"The paths don't intersect"|);
+ \ $(0,0)$\cr
+\quad @else@: .5[point "x\_" of $p$,
+ point "y\_" of $q$] @fi@ @endgroup@ @enddef@.\cr
+\enddisplay
+Notice that ^@begingroup@ and ^@endgroup@ are necessary here; they aren't
+inserted automatically as they would have been in a @vardef@.
+
+\ddangerexercise Define a `^{transum}' macro operation that yields
+the ^{sum} of two ^{transforms}. \ (If $t_3=t_1$~transum~$t_2$, then
+$z$~transformed~$t_3=z$~transformed~$t_1+z$~transformed~$t_2$,
+for~all~pairs~$z$.)
+\answer |secondarydef t transum tt =|\parbreak
+| begingroup save T; transform T;|\parbreak
+| for z=origin,up,right:|^^"origin"\parbreak
+| z transformed t + z transformed tt = z transformed T; endfor|\parbreak
+| T endgroup enddef.|
+
+\ddanger \looseness=-1
+Now we've covered all the types of \<definition>, and it's time to
+take stock and think about the total picture. \MF's ^{mastication} process
+converts an input file into a long sequence of tokens, as explained in
+Chapter~6, and its digestive processes work strictly on those tokens.
+When a symbolic token is about to be digested, \MF\ looks up the token's
+current meaning, and in certain cases \MF\ will expand that token into
+a sequence of other tokens before continuing; this ``^{expansion process}''
+applies to macros and to @if@ and~@for@, as well as to certain other
+special primitives that we shall consider momentarily. Expansion
+continues until an unexpandable token is found; then the ^{digestion process}
+can continue. Sometimes, however, the expansion is not carried out; for
+example, after \MF\ has digested a @def@ token, it stops all expansion until
+just after it reaches the corresponding @enddef@. A complete list of
+all occasions when tokens are not expanded appears later in this chapter.
+
+\ddanger Let's consider all the tokens that cause expansion to occur,
+whenever expansion hasn't been inhibited:\enddanger
+
+\nobreak\medskip
+\textindent\bull Macros. When a macro is expanded, \MF\ first reads and
+evaluates the arguments (if any), as already explained.
+\ (Expansion continues while @expr@ and @suffix@ arguments are
+being evaluated, but it is suppressed within @text@ arguments.) \
+Then \MF\ replaces the macro and its arguments by the replacement text.
+
+\smallbreak
+\textindent\bull ^{Conditions}. When `^@if@\kern1pt' is expanded, \MF\
+reads and evaluates the boolean expression, then skips ahead, if necessary,
+until coming to either `^@fi@' or a condition that's true; then it will
+continue to read the next token. When `^@elseif@\kern1pt' or `^@else@'
+or `@fi@' is expanded, a conditional text has just ended, so \MF\
+skips to the closing `@fi@' and the expansion is empty.
+
+\smallbreak
+\textindent\bull ^{Loops}. When `^@for@' or `^@forsuffixes@' or
+`^@forever@' is expanded, \MF\ reads the specifications up to the colon,
+then reads the loop text (without expansion) up to the @endfor@.
+Finally it rereads the loop text repeatedly, with expansion. When
+`^@exitif@\kern1pt' is expanded, \MF\ evaluates the following boolean
+expression and throws away the semicolon; if the expression proves
+to be true, the current loop is terminated.
+
+\smallbreak
+\textindent\bull ^@scantokens@ \<string primary>. When `@scantokens@'
+is expanded, \MF\ evaluates the following primary expression, which
+should be of type @string@. This string is converted to tokens by the
+rules of Chapter~6, as if
+it had been input from a file containing just one line of text.
+
+\smallbreak
+\textindent\bull ^@input@ ^\<filename>. When `@input@' is expanded,
+the expansion is null, but \MF\ prepares to read from the specified
+file before looking at any more tokens from its current source.
+A \<filename> is subject to special restrictions explained on the
+next page.
+
+\smallbreak
+\textindent\bull ^@endinput@. When `@endinput@' is expanded, the
+expansion is null. But the next time \MF\ gets to the end of an
+input line, it will stop reading from the file containing that line.
+
+\smallbreak
+\textindent\bull ^@expandafter@. When `@expandafter@' is expanded,
+\MF\ first reads one more token, without expanding it; let's
+call this token~$t$. Then \MF\ reads the token that comes after~$t$
+(and possibly more tokens, if that token takes an argument),
+replacing it by its expansion. Finally, \MF\ puts~$t$ back in front
+of that expansion.
+
+\nobreak\smallskip
+\textindent\bull ^^{backslash} |\|. When `|\|' is expanded, the
+expansion is null, i.e., empty.
+
+\ddanger The syntax for \<filename> is not standard in \MF\!, because
+different operating systems have different conventions. You should
+ask your local system wizards for details on just how they have
+decided to implement ^{file names}. The situation is complicated by
+the fact that \MF's process of converting to tokens is irreversible;
+for example, `|x01|' and `|x1.0|' both yield identical sequences
+of tokens. Therefore \MF\ doesn't even try to convert a file name
+to tokens; an ^|input| operation must appear only in a text file, not
+in a list of tokens like the replacement text of a macro! \ (You can get
+around this restriction by saying
+\begindisplay
+^@scantokens@ |"input foo"|
+\enddisplay
+or, more generally,
+\begindisplay
+^@scantokens@ (|"input "| \& "fname")
+\enddisplay
+if "fname" is a string variable containing the \<filename> you want to
+input.) \ Although file names have nonstandard syntax, a sequence of six
+or fewer ordinary letters and/or digits should be a
+file name that works in essentially the same way on all installations of
+\MF\!\null. Uppercase letters are considered to be distinct from their
+lowercase counterparts, on many systems.
+
+\ddanger Here now is the promised list of all cases when expandable
+tokens are not expanded. Some of the situations involve primitives
+that haven't been discussed yet, but we'll get to them eventually.
+Expansion is suppressed at the following times:\enddanger
+
+\nobreak\medskip\item\bull
+When tokens are being deleted during error recovery (see Chapter~5).
+
+\smallskip\item\bull
+When tokens are being skipped because conditional text is being ignored.
+
+\smallskip\item\bull
+When \MF\ is reading the definition of a macro.
+
+\smallskip\item\bull
+When \MF\ is reading a loop text, or the symbolic token that
+immediately follows @for@ or @forsuffixes@.
+
+\smallskip\item\bull
+When \MF\ is reading the @text@ argument of a macro.
+
+\smallskip\item\bull
+When \MF\ is reading the initial symbolic token of a \<declared variable>
+in a type declaration.
+
+\smallskip\item\bull
+When \MF\ is reading the symbolic tokens to be defined by ^@delimiters@,
+^@inner@, ^@let@, ^@newinternal@, or ^@outer@.
+
+\smallskip\item\bull
+When \MF\ is reading the symbolic tokens to be shown by ^@showtoken@
+or ^@showvariable@.
+
+\smallskip\item\bull
+When \MF\ is reading the symbolic tokens to be saved by ^@save@.
+
+\smallskip\item\bull
+When \MF\ is reading the token after ^@expandafter@, ^@everyjob@,
+or the `$=$' following @let@.
+
+\medskip\noindent
+The expansion process is not suppressed while reading the suffix that
+follows the initial token of a \<declared variable>, not even in a
+\<vardef heading>.
+
+\endchapter
+
+% quam oppressis, qui novas res moliebantur, ...
+The two lieutenants,
+Fonteius Capito in Germany,
+% in Germania, Fonteio Capitone;
+and Claudius Macro in Africa,
+% in Africa, Clodio Macro, legatis.
+who opposed his advancement,
+were put down.
+\author ^{SUETONIUS}, %
+ {\sl Sergius Sulpicius Galba\/} (c.\thinspace125 A.D.) % chapter 11
+% from the translation by Alexander Thomson
+% (he says Macer, not Macro, but other translators call this man Macro)
+
+\bigskip
+
+By introducing macro instructions in the source language,
+the designer can bring about the same ease of programming
+as could be achieved by giving the computer
+a more powerful operation list than it really has.
+But naturally, one does not get the same advantages
+in terms of economy of memory space and computer time
+as would be obtained if the more powerful instructions
+were really built into the machine.
+\author O. ^{DOPPING}, {\sl Computers \& Data Processing\/} (1970) % ch19 p312
+
+\eject
+ \beginchapter Chapter 21. Random\\Numbers
+
+\newcount\n \n=93 \def\nextn{\global\advance\n1 \rand\char\n}%
+\def\threenextn{\nextn&\nextn&\nextn}%
+It's fun to play games with
+{\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn}
+by writing programs that incorporate
+an element of ^{chance}. You can generate unpredictable shapes, and
+you can add patternless perturbations to break up the rigid symmetry that
+is usually associated with mathematical constructions.
+ Musicians who use computers to
+synthesize their compositions have found that ^{music} has more ``life'' if
+its rhythms are slightly irregular and offbeat; perfect 1--2--3--4 pulses
+sound pretty dull by contrast. The same phenomenon might prove to
+be true in typography.
+
+\MF\ allows you to introduce controlled indeterminacy in two ways:
+(1)~`^{uniformdeviate}~$t$' gives a number~$u$ that's randomly distributed
+between 0 and~$t$; \ (2)~`^{normaldeviate}' gives a ^{random number}~$x$
+that has the so-called normal distribution with mean zero and variance one.
+
+\danger More precisely, if $t>0$ and $u=\null$uniformdeviate~$t$, we will
+have $0\le u<t$, and for each fraction $0\le p\le1$ we will have
+$0\le u<pt$ with approximate probability~$p$. If $t<0$, the results are
+similar but negated, with $0\ge u>t$. Finally if $t=0$, we always have
+$u=0$; this is the only case where $u=t$ is possible.
+
+\danger A normaldeviate, $x$, will be positive about half the time and
+negative about half the time. Its distribution is ``^{bell-shaped}'' in
+the sense that a particular value $x$ occurs with probability roughly
+proportional to $e^{-x^2/2}$; the graph of this function looks something
+like a bell. The probability is about 68\% that $\vert x\vert<1$,
+about 95\% that $\vert x\vert<2$, and about 99.7\% that $\vert x\vert<3$.
+It's a pretty safe bet that $\vert x\vert<4$.
+
+Instead of relying on mathematical formulas to explain this random
+behavior, we can actually see the results graphically by letting \MF\
+draw some ``^{scatter plots}.'' Consider the following program, which
+draws a $10\pt\times10\pt$ square and puts 100 little dots inside it:
+\begindisplay
+@beginchar@$\,(@incr@ "code",10"pt"\0,10"pt"\0,0)$;\cr
+@pickup@ @pencircle@ scaled .3"pt"; \ @draw@ "unitsquare" scaled $w$;\cr
+@pickup@ @pencircle@ scaled 1"pt";\cr
+@for@ $k=1$ @upto@ 100:\cr
+\quad @drawdot@(uniformdeviate $w,\,$uniformdeviate $w$);
+ \ @endfor@ @endchar@.\cr
+\enddisplay
+The resulting ``characters,'' if we repeat the experiment ten times,
+\n=-1 look like~this:
+\begindisplay
+\threenextn&\threenextn&\threenextn&\nextn\rm\quad.
+\enddisplay
+And if we replace `uniformdeviate $w$' by `$.5w+w/6\ast\null$normaldeviate',
+we get
+\begindisplay
+\threenextn&\threenextn&\threenextn&\nextn\rm\quad.
+\enddisplay
+Finally, if we say `@drawdot@(uniformdeviate $w,\,.5w+w/6\ast\null
+$normaldeviate)' the results are a mixture of the other two cases:
+\begindisplay
+\threenextn&\threenextn&\threenextn&\nextn\rm\quad.
+\enddisplay
+
+\exercise Consider the program fragment `@if@ uniformdeviate$\,1\kern-1pt<
+\kern-1pt1/3$:\
+"case\_a" @else@:~"case\_b"~@fi@'\kern-.2pt. True or false:
+"case\_b" will occur about three times as often as "case\_a".
+\answer False; about twice as often (2/3 versus 1/3).
+
+\exercise \MF's uniformdeviate operator usually doesn't give you an integer.
+Explain how to generate random integers between 1 and~$n$, in such a way
+that each value will be about equally likely.
+\answer |1+floor uniformdeviate n|.
+
+\exercise What does the formula `(uniformdeviate 1)[$z_1,z_2$]' represent?
+\answer A random point on the straight line segment from $z_1$ to $z_2$.
+\ (The point $z_1$ itself will occur with probability about 1/65536;
+but point $z_2$ will never occur.)
+
+\exercise Guess what the following program will produce:
+\begintt
+beginchar(incr code,100pt#,10pt#,0);
+for n:=0 upto 99:
+ fill unitsquare xscaled 1pt yscaled uniformdeviate h
+ shifted (n*pt,0); endfor endchar.
+\endtt
+\answer A random ``^{skyline}'' texture, $100\pt$ wide $\times$ $10\pt$ tall:
+{\rand\char127} The density decreases uniformly as you go up in altitude.
+
+\dangerexercise And what does this puzzle program draw?
+\begintt
+beginchar(incr code,24pt#,10pt#,0);
+numeric count[];
+pickup pencircle scaled 1pt;
+for n:=1 upto 100:
+ x:=.5w+w/6*normaldeviate;
+ y:=floor(x/pt);
+ if unknown count[y]: count[y]:=-1; fi
+ drawdot(x,pt*incr count[y]); endfor endchar.
+\endtt
+\answer A more-or-less bell-shaped ^{histogram}: {\rand\char126}
+
+\danger Let's try now to put more ``life'' in the \MF\ ^{logo}, by
+asking Lady Luck to add small perturbations to each of the key points.
+First we define "noise",
+\begindisplay
+@vardef@ "noise" $=$ normaldeviate$\null\ast"craziness"$ @enddef@;
+\enddisplay
+the ^"craziness" parameter will control the degree of haphazard variation.
+\rightfig 21a ({240\apspix} x {216\apspix}) ^-20pt
+Then we can write the following program for the logo's `{\manual n}':
+\begindisplay
+@beginlogochar@\thinspace(|"N"|$,15)$;\cr
+$x_1="leftstemloc"+"noise"$;\cr
+$x_2="leftstemloc"+"noise"$;\cr
+$w-x_4="leftstemloc"+"noise"$;\cr
+$w-x_5="leftstemloc"+"noise"$;\cr
+$"bot"\,y_1="noise"-"o"$;\cr
+$"top"\,y_2=h+o+"noise"$;\cr
+$y_3=y_4+"ygap"+"noise"$;\cr
+$"bot"\,y_4="noise"-"o"$;\cr
+$"top"\,y_5=h+o+"noise"$;\cr
+$z_3="whatever"[z_4,z_5]$;\cr
+@draw@ $z_1\dashto z_2\dashto z_3$; \
+@draw@ $z_4\dashto z_5$; \ @labels@$(1,2,3,4,5)$; \ @endchar@.
+\enddisplay
+The illustration here was drawn with $"craziness"=0$, so there was no noise.
+
+\danger Three trials of the $9\pt$ `{\manual n}' with $"craziness"=.1"pt"$
+gave the following results:
+\displayfig 21b\&c\&d (195\apspix)
+And here's what happens if you do similar things to all the
+letters of \MF\!, with "craziness" decreasing from $.45"pt"$ to zero in
+steps of $.05"pt"$:
+\begindisplay \global\advance\n by 8
+% we haven't room for craziness .5!
+%\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+\nextn\nextn\nextn\kern-.23333pt\nextn\nextn\kern-.55555pt\nextn\nextn\nextn\cr
+{\manual METAFONT}\cr
+\enddisplay
+
+\danger Every time you run a program that refers to random numbers,
+you'll get different results, because \MF\ uses the date and time of day
+to change its generator. This unpredictable behavior
+is normally what you want, but it can be troublesome if your
+program draws a lovely shape that you'd like to see again.
+Or perhaps one of your runs will uncover a program bug; you won't be able to
+diagnose the problem, because it probably won't recur!
+The solution is to say
+\begindisplay
+^@randomseed@ $:=$ \<numeric expression>
+\enddisplay
+and to remember the value of that numeric expression. \ (The value
+will automatically be recorded in the transcript file of your run.) \
+You will get the same sequence of uniform and normal deviates on
+any two runs that begin with the same @randomseed@, because \MF's
+numbers are only ``pseudo-random.''
+
+\endchapter
+
+% En musiker, som jag k\"ande, roade sig med
+A musician whom I knew amused himself
+% att st\"amma sitt piano hur som helst utan rim och reson.
+%by tuning his piano arbitrarily, without any rhyme or reason.
+by tuning his piano haphazardly, without any rhyme or reason.
+% D\"arefter spelade han Beethovens Sonate path\'etique utantill.
+Afterwards he played ^{Beethoven}'s\/ {\rm Sonate Path\'etique} by heart.
+% Det var en otrolig fr\"ojd att h\"ora ett gammlt stycke leva upp igen.
+It was an unbelievable delight to hear an old piece come back to life.
+% Jag hade h\"ort denna sonat spelas under tjugo \aa r, st\"andigt
+%^^{beauty} I had heard this sonata for twenty years,
+^^{beauty} How often had I previously heard this sonata, always the same way,
+% utan hopp att se den utvecklas; fixerad, of\"orm\"ogen att n\aa\ l\"angre.
+%never dreaming that it was capable of being developed further.
+never dreaming that it was capable of being developed further!
+% utan hopp att se den utvecklas; fixerad, of\"orm\"ogen att n\aa\ l\"angre.
+\author AUGUST ^{STRINDBERG}, {\sl Chance in Artistic Creation} (1894)
+% The original was in French, but I was unable to locate anything
+% but the above Swedish translation, from Modern Museet catalog 28 (1962)
+% Nye Konstriktningar! eller Slumpen i det konstn\"arliga skapandet
+% [New Directions in Art! or, Chance in Artistic Creation]
+% 2009.10.21 here's the original French!:
+% J'ai connu un musicien qui se plaisait \'a accorder son piano
+% au petit bonheur, sans rime ni raison. Puis il jouait par c{\oe}ur
+% la {\it Sonate path\'etique\/} de Beethoven, et c'\'etait une
+% jouissance incroyable d'entendre ce vieux morceau se rajeunir.
+% Que de fois je 'lavais entendu ex\'ecuter devant moi, cetta sonate,
+% toujours le m\^eme, fix\'ee, sans esp\'erance de la voir se
+% d\'evelopper en d'autres sons, jamais, incapable d'une \'evolution\thinspace!
+
+\bigskip
+
+[Education] must lead us from chance and arbitrariness
+to rational clarity and intellectual order.
+\author L. ^{MIES VAN DER ROHE}, {\sl Inaugural Address\/} (1938)
+
+\eject
+ \beginchapter Chapter 22. Strings
+
+\MF\ is not a word processor, but a \MF\ programmer can process words and
+other short strings of symbols in rudimentary ways. Strings can help
+explain what a program is doing; for example, the |io.mf| file of
+Chapter~5 mentions |"The|~|letter|~|O"| as a title that should appear
+on proofsheets, and it also says |"O"| in order to identify the
+position of a character in the output font.
+
+Chapter 6 points out that a \<string token> is any sequence of
+characters enclosed in double-quote (|"|) marks, except that you're
+not allowed to use the double-quote character itself in this way.
+If you need that character, plain \MF\ provides it in a string of
+length~1 called ^"ditto". Thus
+\begintt
+"A string expression can contain a `" & ditto & "' mark"
+\endtt
+even though a \<string token> cannot.
+
+A string expression can be used all by itself as a statement, just as
+if it were an equation or declaration or command. Such a statement is called
+a ^\<title>, provided that it is immediately followed by a~`|;|'.
+If ^"tracingtitles"$\null>0$ when a title is encountered, \MF\
+will type the title on the user's terminal. If ^"proofing"$\null>0$
+when a title is encountered, \MF\ will copy the title into the output
+file, so that it can be put onto proofsheets by postprocessors such
+as the ^|GFtoDVI| program described in Appendix~H.
+
+\danger Appendix H explains how to specify the strings that are used as
+^{labels} for the key points on proofsheets.
+
+\ddanger Here's the full syntax for string expressions. All of the
+activity except for ^{concatenation} (`\&') ^^{ampersand} takes
+place at the primary level:
+\beginsyntax
+<string primary>\is<string token>
+ \alt<string variable>
+ \alt[(]<string expression>[)]
+ \alt[begingroup]<statement list><string expression>[endgroup]
+ \alt[jobname]
+ \alt[readstring]
+ \alt[str]<suffix>
+ \alt[char]<numeric primary>
+ \alt[decimal]<numeric primary>
+ \alt[substring]<pair primary>[of]<string primary>
+<string secondary>\is<string primary>
+<string tertiary>\is<string secondary>
+<string expression>\is<string tertiary>
+ \alt<string expression>[\&]<string tertiary>
+\endsyntax
+The new features here are |jobname|, |readstring|, |str|, |char|,
+|decimal|, and |substring|; we shall consider each of them in turn.
+
+\ddanger The name of your job (\kern1pt@jobname@) is the name of the first
+file you input, provided that the first line of instructions to \MF\
+(the `^|**|' line or ^{command line}) causes input of some file.
+Otherwise the job name is ^|mfput|, as in Experiment~1 of Chapter~5.
+
+\ddanger When you say `^@readstring@', \MF\ stops and waits for the user
+to type a line at the terminal. The value of @readstring@ is the contents
+of this line, with trailing spaces eliminated.
+\ (You probably should use the @message@ command first, to give the
+user a clue about what to type; for example, see the |expr.mf| file
+of Chapter~8, which gets its input expressions via @readstring@.
+The ^@stop@ macro of Appendix~B makes use of the fact that @readstring@
+halts the computer; it doesn't actually look at the string.)
+
+\ddanger An arbitrary ^\<suffix> is converted to a string by ^@str@,
+using the method by which \MF\ displays suffix arguments in
+diagnostic typeouts. Negative subscripts are enclosed in
+square brackets; spaces or dots are inserted between tokens whose
+characters belong to the same class (according to the table in
+Chapter~6). For example, if $n=1$ then `@str@~$x[n]a$' is |"x1a"|;
+`@str@~$x\,n\,a$' is |"x.n.a"|.
+
+\ddanger The result of `^@char@~$n$' is a string of length~1,
+representing the character whose ^{ASCII} code is~$n$.
+\ (Appendix~C explains this code.) \ The value of~$n$ is first
+rounded to the nearest integer, then multiples of~256 are
+added or subtracted if necessary until $0\le n<256$; this
+defines @char@~$n$ in all cases.
+
+\ddanger The ^{decimal representation} of a known numeric value~$x$
+is available in string form as `@decimal@~$x$'. If $x$ is negative,
+the first character of this string will be~`|-|'. If $x$ is not
+an integer, a decimal point will be included, followed by as
+many digits as are necessary to characterize the value. \ (These
+conventions are the same as those illustrated in the example
+outputs of Chapter~8.)
+
+\ddanger The rules for ^{substring} are like the rules for ^{subpath}
+in Chapter~14. \MF\ thinks of a string as if its characters were
+written in the squares of a piece of ^{graph paper}, between
+coordinates $x=0$ and $x=n$, where $n$~is the length of the string.
+In simple cases, substring$\,(a,b)$
+then refers to the characters between $x=a$ and~$x=b$. The
+rules for the general case are slightly more involved: If $b<a$,
+the result will be the ^{reverse} of substring$\,(b,a)$.
+Otherwise $a$ and~$b$ are replaced respectively by
+$\max\bigl(0,\min(n,\round a)\bigr)$ and
+$\max\bigl(0,\min(n,\round b)\bigr)$; this leads to the simple
+case $0\le a\le b\le n$ described above, when the resulting
+string has length $b-a$.
+
+\ddanger Strings can be converted into numbers, although Chapter~8
+didn't mention this fact in its syntax for \<numeric primary>. The
+primitive operations are
+\begindisplay
+{\tt ASCII}\thinspace\<string primary>\alt
+\thinspace{\tt oct}\thinspace\<string primary>\alt
+\thinspace{\tt hex}\thinspace\<string primary>
+\enddisplay
+where `^{ASCII}' returns the ASCII code of the first character of the
+string, `^{oct}' computes an integer from a string representing
+^{octal notation} (radix~8), and `^{hex}' computes an integer from
+a string representing ^{hexadecimal notation} (radix~16). For example,
+\begindisplay
+ASCII |"100"| $=$ 49;\qquad oct |"100"| $=$ 64;\qquad hex |"100"| $=$ 256.
+\enddisplay
+Several exceptional conditions need to be mentioned:
+(1)~ASCII~|""|~$=-1$; otherwise ASCII yields an integer between 0 and~255.
+\ (2)~The characters in the string argument to `oct' must all be
+digits in the range |0|--|7|.
+\ (3)~The characters in the string argument to `hex' must all be
+digits in the range |0|--|9|, |A|--|F|, or |a|--|f|.
+\ (4)~The number that results from `oct' or `hex' must be less than 4096.
+Thus, `oct~|"7777"|' and `hex~|"FFF"|' are the maximum legal values.
+
+\ddangerexercise Under what circumstances is (a) ASCII @char@ $n=n$?
+\ (b)~@char@~ASCII~$s=s$?
+\answer (a) Iff $n$ is an integer between 0 and 255.
+(b) Iff $s$ is a string of length~1.
+
+\ddangerexercise Why are there primitive operations to convert from
+strings to numbers assuming octal notation and hexadecimal notation,
+but not assuming decimal notation?
+\answer Whoever says that there's no such primitive operation has
+forgotten about @scantokens@.
+
+\ddangerexercise Write an "octal" macro that converts a nonnegative
+integer to an octal string.
+\answer |vardef octal primary n =|\parbreak
+| save m,s; m:=abs round n; string s; s=decimal(m mod 8);|\parbreak
+| forever: m:=m div 8; exitif m=0;|\parbreak
+| s:=decimal(m mod 8) & s; endfor|\parbreak
+| s enddef;|\par\nobreak\medskip\noindent
+`|str[m mod 8]|' could also be used instead of `|decimal(m mod 8)|'.
+
+\ddanger A ^\<message command> allows you to communicate directly
+or indirectly with the user. It has the general syntax
+\beginsyntax
+<message command>\is<message op><string expression>
+<message op>\is[message]\alt[errmessage]\alt[errhelp]
+\endsyntax
+If you say `@message@~$s$', the characters of $s$ will be typed on the
+terminal, at the beginning of a new line; `@errmessage@~$s$' is
+similar, but the string will be preceded by |"! "| and followed
+by~|"."|, followed by lines of context as in \MF's normal error messages.
+If the user asks for ^{help} after an @errmessage@ error,
+the most recent @errhelp@ string will be typed (unless it was empty).
+
+\ddanger \MF\ doesn't allow you to have an array of different
+macros $m[i]$; but you can have an array of strings that have
+macro-like behavior, via ^@scantokens@. The ^@mode\_def@ construction
+of Appendix~B exploits this idea.
+
+\endchapter
+
+Many other useful Practises
+mecanicks perform by this Theo.
+as the finding the length of strings.
+\author WILLIAM ^{ALINGHAM}, {\sl Geometry Epitomized\/} (1695)
+ % p51 acc to OED; but British Library doesn't have this edition!
+ % they have 1701 `An epitome of geometry', as does Yale
+
+\bigskip
+
+Forgive me, if my trembling Pen displays
+What never yet was sung in mortal Lays.
+But how shall I attempt such arduous String?
+\author JAMES ^{THOMSON}, {\sl The Castle of Indolence\/} (1748)
+ % canto 1 verse 31
+
+\eject
+ \beginchapter Chapter 23. Online\\Displays
+
+How do you get pictures to appear on your screen? Plain \MF\ provides
+the `^@showit@' command, which displays the ^"currentpicture".
+Furthermore you can ask for `^@screenchars@'; this automatically
+does a @showit@ at the time of each ^@endchar@. And you can see all
+the action by asking for `^@screenstrokes@'; this automatically
+does a @showit@ after every @draw@ or @fill@.
+
+\ddanger The above-described features of plain \MF\ are implemented
+from low-level primitive commands, by macros that appear in Appendix~B\null.
+At the lowest level, \MF\ obeys commands such as `@display@
+"currentpicture" @inwindow@~1'; there's also an `@openwindow@'
+command that defines a correspondence between \MF\ coordinates and
+screen coordinates. The syntax is
+\beginsyntax
+<display command>\is[display]<picture variable>[inwindow]<window>
+<window>\is<numeric expression>
+<openwindow command>\is[openwindow]<window><window spec>
+<window spec>\is<screen place>[at]<pair expression>
+<screen place>\is[from]<screen coordinates>[to]<screen coordinates>
+<screen coordinates>\is<pair expression>
+\endsyntax
+A \<window> is an integer between 0 and 15, inclusive; it represents
+one of sixteen ``windows'' or ``portholes'' that \MF\ provides
+between its pictures and the outside world. The \<window> mentioned
+in a @display@ command must previously have been ``opened'' by
+an @openwindow@ command.
+
+\ddanger \MF's windows should not be confused with the so-called
+windows provided by many modern operating systems. If you have
+such a system, you'll probably find that all of \MF's pictorial
+output appears in one operating-system window, and all of its
+terminal I/O appears in another, and you might be running other
+jobs (like the system editor) in another. \MF's windows are not so
+fancy as this; they are just internal subwindows of one big
+picture window.
+
+\ddanger The command `@openwindow@ $k$ @from@ $(r_0,c_0)$ @to@ $(r_1,c_1)$
+@at@~$(x,y)$' associates a rectangular area of the user's screen
+(or of the user's big picture window) with pixels in \MF's coordinate
+system. All of the numbers in this command (namely $k$, $r_0$,~$c_0$,
+$r_1$,~$c_1$, $x$, and~$y$) are rounded to the nearest integer if they
+aren't integers already. Furthermore $r_0$ is replaced by
+$\max\bigl(0,\min("maxr",r_0)\bigr)$ and $r_1$ is replaced by
+$\max\bigl(r_0,\min("maxr",r_1)\bigr)$, where "maxr" is the maximum
+number of rows on the screen; similar adjustments are made to $c_0$
+and~$c_1$. The two $(r,c)$ values are row and column
+numbers on the screen; the topmost row is conventionally taken to be
+row zero, and the leftmost column is taken to be column zero.
+\ (These conventions for screen coordinates are quite different from
+the normal ^{Cartesian} coordinate system used everywhere else
+in \MF\!, but somehow they seem appropriate when applied to screens.) \
+Point~$(x,y)$ of \MF's raster will be equated to the upper left
+corner of the rectangle, i.e., to the upper left corner of the pixel
+in screen column~$c_0$ of screen row~$r_0$. The window itself
+occupies $r_1-r_0$ rows and $c_1-c_0$ columns. It follows that
+the pixel in column~$c_1$ of row~$r_1$ is not in the window itself,
+but it is the screen pixel diagonally just below and to the right of the
+lower right corner of the window.
+
+\ddangerexercise What are the \MF\ coordinates of the boundary of
+such a window?
+\answer Point $(x,y)$ is the upper left corner, ${(x+c_1-c_0,y)}$ is the
+upper right corner, ${(x,y-r_1+r_0)}$ is the lower left corner, and
+${(x+c_1-c_0,y-r_1+r_0)}$ is the lower right corner. \ (Pixels
+outside this rectangle will not be displayed.)
+
+\danger If you run \MF\ on a system that doesn't support general
+bitmap displays, the @display@ and @openwindow@ commands will do
+nothing. You'll have to look at hardcopy output, off\/line.
+\ (But your \MF\ might run a bit faster.)
+
+\ddanger The syntax for @display@ insists that you display a
+\<picture variable>, not a \<picture expression>; thus, you
+can't `@display@ ^@nullpicture@'. Plain \MF\ defines a special
+variable ^"blankpicture" that's entirely blank, just so that
+you can easily display nothing whenever you like.
+
+\ddanger A window may be opened any number of times, hence moved
+to different locations on the screen. Opening a window blanks the
+corresponding screen rectangle as if you had displayed "blankpicture".
+
+\ddanger The effect of overlapping windows is undefined, because \MF\
+does not always repaint pixels that have remained unchanged between
+displays.
+
+\ddanger Changes to a picture do not change the displays that were
+generated from it, until you give another display command explicitly.
+Thus, the images emblazoned on your screen might not exist any longer
+in \MF's picture memory.
+
+\ddanger Plain \MF\ has an `@openit@' macro that opens
+^"currentwindow"; this variable "currentwindow" is always zero
+unless you change it yourself. The @showit@ macro displays
+"currentpicture" in "currentwindow"; and it's also designed
+to call @openit@---but only the very first time @showit@ is invoked.
+This means that the screen normally won't be touched until the moment you
+first try to display something.
+
+\ddanger Appendix E explains how to manage a more elaborate scheme
+in which six windows can be used to show how ^{meta-characters} vary
+under six different font-parameter settings. The author ^^{Knuth}
+used such a six-window system when developing the Computer Modern
+typefaces; here is a typical example of what appeared on his
+^^{a} terminal when the letter~`a' was being refined:
+\displayfig 23 (68mm)
+
+\ddangerexercise The @openit@ macro in Appendix~B specifies $(-50,300)$
+as the upper left corner point of the window used for showing
+all the pictures. This might clip off the bottom of a large character,
+if your screen is limited to, say, 360 rows. How could you change
+@openit@ so that the character images will be raised 20 rows higher
+than they would be in the standard setting?
+\answer Redefine @openit@ so that it puts the top left at $(-50,280)$.
+
+\ddangerexercise Design a `^@new\_window@' routine that allocates
+windows 1, 2, \dots,~15. If the user says `|new_window $(u,v)|',
+where |$|~is any suffix and |u,v| are pairs of coordinates for
+two opposite corners of a rectangle, your macro should map that
+rectangle to the next available screen rectangle and open it as
+window number |window$|. The allocation should be left to right,
+top to bottom; assume that the screen is an infinite rectangle,
+^"screen\_cols" wide.
+\answer (This routine is due to John ^{Hobby}.)
+\begintt
+newinternal n_windows; % the number of windows allocated so far
+newinternal screen_bot; % the first untouched screen row
+pair screen_corner; % the upper left corner of next window
+def wipescreen = % do this to initialize or reinitialize
+ for i:=1 upto n_windows: display blankpicture inwindow i; endfor
+ n_windows := screen_bot := 0; screen_corner := origin enddef;
+wipescreen;
+vardef new_window@#(expr u,v) = save r,c,up_lft; pair up_lft;
+ if n_windows=15: errmessage "No more windows left"
+ else: window@# := incr n_windows;
+ up_lft = (min(xpart u,xpart v), max(ypart u, ypart v));
+ (r,c) = (u+v-2up_lft) rotated 90;
+ if ypart screen_corner + c > screen_cols:
+ screen_corner:=(screen_bot,0); fi
+ openwindow window@# from screen_corner
+ to screen_corner+(r,c) at up_lft;
+ screen_bot := max(screen_bot,xpart screen_corner + r);
+ screen_corner := screen_corner + (0,c) fi; enddef;
+\endtt
+
+\endchapter
+
+Editing will be done on-line with a display scope and keyboard.
+\author RICHARD L. ^{VENEZKY}, in {\sl American Documentation\/} (1968)
+ % p72; ``Storage, Retrieval, and Editing of Information for a Dictionary''
+
+\bigskip
+
+In future I might be obliged to turn for material to the tube.
+\author IGOR ^{STRAVINSKY}, in {\sl Harper's\/} (1970) % April, p112
+ % that year he wrote a regular column called Performing Arts
+
+\eject
+ \beginchapter Chapter 24. Discreteness\\and Discretion
+
+Pixel patterns are indistinguishable from continuous curves, when the
+pixels are small enough. After all, the human eye is composed of
+discrete receptors, and visible light has a finite wavelength.
+Our hypothetical ^"luxo" printer of Chapter~11, with its resolution
+of 2000 pixels per inch, would surely be able to produce printed
+pages of high quality, if it existed; the physical properties of ink
+would smooth out all the tiny bumps, obliterating all the evidence that
+the letterforms had been digitized. However, it will always be less
+expensive to work with devices of lower resolution, and we want the output
+of \MF\ to look as good as possible on the machines that we can afford to
+buy. The purpose of this chapter is to discuss the principles of
+``discreet ^{rounding},'' i.e., to consider the tasteful application of
+mathematical techniques by which \MF\ can be made to produce satisfactory
+shapes even when the resolution is rather coarse.
+
+The technical material in this chapter is entirely marked with danger
+signs, since careful rounding tends to make \MF\ programs more complex; a
+novice user will not wish to worry about such details. On the other hand,
+an expert \MF er will take pains to round things properly even when
+preparing high-resolution fonts, since the subtle refinements we are about
+to discuss will often lead to significantly better letterforms.
+
+We should realize before we begin that it would be a mistake to
+set our hopes too high. Mechanically generated letters that are untouched
+by human hands and unseen by human eyes can never be expected to compete
+with alphabets that are carefully crafted to look best on a particular
+device. There's no substitute for actually looking at the letters
+and changing their pixels until the result looks right. Therefore our
+goal should not be to make ^{hand-tuning} obsolete; it should rather be
+to make hand-tuning tolerable. Let us try to create meta-designs so
+that we would never want to change more than a few pixels per character,
+say half a dozen, regardless of the resolution. At low resolutions, six
+pixels will of course be a significant percentage of the whole, and at higher
+resolutions six well-considered pixel changes can still lead to worthwhile
+improvements. The point is that if our design comes close enough, a
+person with a good bitmap-editing program will be able to optimize an
+entire font in less than an hour. This is an attainable goal, if rounding
+is done judiciously.
+
+\danger \MF\ tries to adjust curves automatically, so that they are
+well adapted to the ^{raster}, if the internal quantities ^"autorounding"
+and/or ^"smoothing" have positive values. \ (Plain \MF\ sets
+$"autorounding":=2$ and $"smoothing":=1$, so you generally get these
+features unless you turn them off yourself.) \ But all the examples in
+this chapter will be generated with $"autorounding":="smoothing":=0$
+unless otherwise mentioned, because this will keep \MF's automatic
+mechanisms from interfering with our experiments. We shall discuss the
+pros and cons of automatic rounding after we have explored the general
+problem in more detail.
+
+\danger The first thing we need to understand about rounding is \MF's
+procedure for ^{digitizing} a path. A path of length~$n$ can be regarded
+as a trajectory~$z(t)$ that is traced out as $t$~varies from 0 to~$n$. In
+these terms, the corresponding digitized path is most easily described by
+the formula `round~$z(t)$' for $0\le t\le n$; each $z(t)$ is rounded to
+the nearest point with integer coordinates. For example, if a path goes
+through point~$(3.1,5.7)$, its digitization will go through point~$(3,6)$.
+The digitized trajectory makes discrete jumps at certain values of $t$,
+when round~$z(t)$ hops from one point to another; the two points will be
+one pixel apart, and we can imagine that the digitized path traverses the
+horizontal or vertical edge between them when it jumps.
+
+\danger When an ordinary region is being filled, this rule for
+digitizing paths boils down to a simple criterion that's easy to
+visualize: {\sl A pixel belongs to the digitized region if and only if
+its center point lies inside the original undigitized path.} For example,
+two versions of Chapter~5's Ionian `{\manual\IOO}' are shown here
+at a resolution of 200 pixels per inch, using the characteristics
+of ^"lowres" mode in Appendix~B:
+\displayfig 24a\&b (190\apspix)
+The heavy broken lines are digitized paths, and the pixels inside these
+ragged boundaries are those whose centers lie in the shaded regions.
+
+\danger The `{\manual\IOO}' on the left has digitized well; but the
+one on the right has problems, because it was based on curves that
+were generated without taking the raster into account. The difference
+between these two letters is entirely due to line~8 of the program
+in Chapter~5, which says
+\begindisplay
+"curve\_sidebar" $=$ round $1/18"em"$;
+\enddisplay
+this equation determines the position of the leftmost and rightmost
+edges of the `{\manual\IOO}' before digitization, and it leads to
+the nice digitized form in the left-hand example. Without the word
+`^{round}', we get the inferior right-hand example, which was
+obtained by exactly the same \MF\ program except that "curve\_sidebar"
+was set to $1/18"em"$ exactly. One little token---which changed an exact
+calculation to an approximate, rounded calculation---made all the difference!
+
+\danger Curves that are placed in arbitrary positions on
+a raster can lead to digital disasters, even though the curves themselves
+aren't bad. For example, suppose we take the right-hand example above
+and shift it just 0.05 and 0.10 pixels to the right:
+\displayfig 24c\&d (190\apspix)
+The first shift of 0.05 pixels causes a tiny ^{pimple} to appear
+at the right edge; after another small shift the pimple has grown into a
+mole, and the left edge has become too ^{flat}. \looseness=-1
+
+\danger A designer who is asked to make a digital `O' that is 22 pixels
+wide will certainly have pixels in mind when making the design. Therefore
+it's not surprising that our program to generate a digital~`O' should
+pay attention to actual pixel positions by rounding "curve\_sidebar" as
+in this example. We have distorted the infinite-resolution curve
+slightly so that it will digitize well, before digitizing it.
+
+\danger A path $z(t)$ will digitize well if the digitization process doesn't
+change it too much; thus, we want $z(t)$ to be essentially the same as
+round$\,z(t)$, at all the important places. But what places are ``important''?
+Experience shows that the most critical points are those where the path
+travels horizontally or vertically, i.e., where it runs parallel to
+the raster lines. It's best to arrange things so that a curve becomes
+parallel to the raster lines just when it touches or nearly touches those
+lines; then it will appear to have the right curvature after digitization.
+The worst case occurs when a curve becomes parallel to the raster just
+when it's halfway between raster lines; then it gets a pimple or a flat spot.
+
+\ddanger Diagonal slopes, where a curve has a $\pm45^\circ$ tangent angle,
+are also potential sources of unwanted pimples and flats. Similarly, at
+higher resolutions it is sometimes possible to detect small glitches
+when a curve travels with slopes of $\pm1/2$ or $\pm2/1$. Rational
+slopes $m/n$ where $m$ and~$n$ are small integers turn out to be
+somewhat dangerous. But diagonals are of secondary importance; horizontal
+and vertical slopes lead to more severe problems.
+
+\danger These considerations suggest a simple general principle for adapting
+the outlines of shapes to be digitized: {\sl If you know that the outline
+will have a vertical tangent at some point, round the $x$~coordinate to an
+integer and leave the $y$~coordinate unchanged. If you know that the
+outline will have a horizontal tangent at some point, round the
+$y$~coordinate to an integer and leave the $x$~coordinate unchanged.}
+
+\ddanger Incidentally, the horizontal tangent points in our `{\manual\IOO}'
+examples were taken care~of by the fact that `^@define\_corrected\_pixels@'
+makes the ^{overshoot} parameter~$o$ nearly an integer, together with
+the fact that ^@beginchar@ makes $h$ an integer. If the $y$~coordinates
+had not been rounded at the horizontal tangent points,
+our bad examples would have looked even worse.
+
+\danger Before we go further into the study of rounding, we had better
+face up to a technicality that's sometimes important: We said that the
+pixels of a digitized region are those whose centers lie inside the
+undigitized region; but this rule is vague about what happens when the
+centers happen to fall precisely on the undigitized boundary. Similarly,
+when we said that round$\,z(t)$ jumps from one point to an adjacent point,
+we ignored the fact that a curve such as $z(t)=(t,t)$ actually
+jumps from $(0,0)$ to $(1,1)$ when it is rounded as $t$ passes 1/2;
+those points are not adjacent.
+\MF\ skirts both of these problems in an interesting way:
+It shifts all of its paths to the
+right by an infinitesimal amount~$\delta$, and it also shifts them
+upward by an even smaller
+infinitesimal amount~$\delta\epsilon$, so that no path actually
+touches a pixel center. Here $\delta$ and~$\epsilon$ are positive numbers
+that are chosen to be so small that their actual values don't matter.
+For example, the path $z(t)=(t,t)$ becomes $(t+\delta,t+\delta\epsilon)$,
+which jumps from $(0,0)$ to $(1,0)$ to $(1,1)$ because it momentarily
+rounds to $(1,0)$ when $t=1/2-2\delta\epsilon$.
+
+\danger Points of the form $(m+1/2,n+1/2)$, where $m$ and $n$ are integers,
+lie in the centers of their pixels. They are called ``ambiguous'' points
+because we can't round them to the nearest integer neighbor without
+deciding which of four adjacent points is to be considered the nearest.
+If we imagine taking a curved outline and shifting it slowly to the
+right, the digitized image makes abrupt transitions when the outline
+passes over an ^{ambiguous point}. When a path comes near an ambiguous
+point, the path is farthest away from its digitization. Thus the
+ambiguous points are points of instability, and digitizing works best
+when paths don't get too close to them.
+
+\danger Let's consider now what happens when we ^@draw@ with a pen,
+instead of filling an outline. It may seem that the simplest possible @draw@
+command would be something like this:
+\begindisplay
+@pickup@ @pencircle@; \ @draw@ $(0,0)\to(10,0)$;
+\enddisplay
+what could be easier? But a closer look shows that this is actually
+about the worst case that could be imagined! A circular pen of
+diameter~1 that goes from $(0,0)$ to $(10,0)$ has upper and lower
+boundaries that go from $(0,\pm1/2)$ to $(10,\pm1/2)$,
+and both of these boundaries run smack through lots of
+ambiguous points. \MF\ has to decide whether to fill the row of pixels
+with $0\le y\le1$ or the lower row with $-1\le y\le0$, neither of which is
+centered on the given line. According to the rule stated earlier, \MF\
+shifts the path very slightly to the right and very, very slightly up;
+thus the pixels actually filled are bounded by
+$(0,0)\dashto(10,0)\dashto(10,1)\dashto(0,1)\dashto\cycle$.
+
+\dangerexercise Continuing this example, what pixels would have been
+filled if the path had been `$(0,0)\to(10,-"epsilon")$'\thinspace?
+\answer The entire path now has negative $y$~coordinates except at
+point~$(0,0)$, so the outline of the filled region is
+ $(0,-1)\dashto(10,-1)\dashto(10,0)\dashto(0,0)\dashto(0,1)
+\dashto\cycle$. \ $\bigl($Notice that the
+digitized outline actually goes up to $(0,1)$ before coming straight down
+again. This fills no pixels, but \MF\ correctly puts ``cancelling'' edges
+from $(0,0)$ to $(0,1)$ and back to $(0,0)$ into its edge structure, because the
+point $(0,.5)$ is on the boundary and rounds to $(0,1).\bigr)$
+
+\danger In general when we @draw@ with a fixed pen, good digitizations
+depend on where the edges of the pen happen to fall, not on the
+path followed by the pen's center. Thus, for example, if the path we're
+drawing has a vertical tangent at point~$z_1$, we don't necessarily
+want $x_1$~to be an integer; we want "lft"$\,x_1$ and "rt"$\,x_1$
+to be integers. If there's a horizontal tangent at~$z_2$, we want
+"top"$\,y_2$ and "bot"$\,y_2$ to be integers. The pens created by
+^@pencircle@ always have the property that $("lft"\,x)-("rt"\,x)$
+and $("top"\,y)-("bot"\,y)$ are integers; hence both edges will
+be in good or bad positions simultaneously.
+
+\danger Suppose that we want $x_1$ to be approximately equal to~$\alpha$,
+and we also want it to be at a good place for vertical tangents with respect
+to the pen that has currently been picked up. One way to define $x_1$ is to say
+\begindisplay
+$"lft"\,x_1=\round("lft"\,\alpha)$;
+\enddisplay
+this does the right thing, because it makes "lft"$\,x_1$ an integer and
+it also makes $x_1\approx\alpha$. Similarly, to make~$y_2\approx\beta$
+good for horizontal tangents, we can say
+\begindisplay
+$"top"\,y_2=\round("top"\,\beta)$.
+\enddisplay
+Such operations occur frequently in practice, so plain \MF\ provides
+^^"good.x" ^^"good.y" ^^{gumdrop} convenient abbreviations: We can say simply
+\begindisplay
+$x_1="good.x"\,\alpha$; \ $y_2="good.y"\,\beta$
+\enddisplay
+instead of using indirect equations for $"lft"\,x_1$ and $"top"\,y_2$.
+
+\danger Let's look one last time at the letters of the \MF\ logo, in
+order to make them round properly. Chapter~11 describes a file ^|logo.mf|
+that draws the seven characters, but we can improve the results by
+making pixel-oriented refinements. In the first place, we can replace
+the command
+\begindisplay
+@define\_pixels@($s,u,"xgap","ygap","leftstemloc","barheight"$)
+\enddisplay
+by something better: Looking at the uses of these ad hoc dimensions,
+we see that ^"xgap" and ^"ygap" ought to be integers; ^"leftstemloc"
+should be a "good.x" value for "logo\_pen"; and ^"barheight" should
+be a "good.y" value. Therefore we say
+\begindisplay
+^@define\_pixels@$(s,u)$;\cr
+^@define\_whole\_pixels@$("xgap","ygap")$;\cr
+^@define\_good\_x\_pixels@$("leftstemloc")$;\cr
+^@define\_good\_y\_pixels@$("barheight")$;\cr
+\enddisplay
+these commands, provided by plain \MF\!, will do the right thing.
+\ (The "logo\_pen" should be picked up before the last two commands are
+given.) \ These few changes, and a change to the `{\manual m}', suffice to
+fix all the letters except `\kern1pt{\manual j}\kern1pt'.
+
+\dangerexercise The program for \MF's `{\manual m}' ^^{O}
+appears in Chapter~18. What changes would you suggest to make
+it digitize well?
+\answer The horizontal tangents are already taken care of by the equations
+$"top"\,y_1=h+o$ and $"bot"\,y_4=-o$, so nothing needs to be done there.
+We should, however, say
+\begindisplay
+$x_2=w-x_3="good.x"(1.5u+s)$
+\enddisplay
+so that vertical tangents will occur in good places. Since $w$~is an
+integer, and since the "logo\_pen" has left-right symmetry,
+$w-x_3$ will be good if and only if $x_3$ is.
+
+\danger The `\kern1pt{\manual j}\kern1pt' ^^{T} presents a new problem,
+because we want it to be symmetric between left and right. If the pen
+breadth is odd, we want the character width~$w$ to be odd, so that there
+will be as many pixels to the left of the stem as there are to the right.
+If the pen breadth is even, we want $w$ to be even. Therefore we have a
+50-50 chance of being unhappy with the value of~$w$ that is computed by
+^@beginchar@.
+
+\dangerexercise Prove that the value of $w$ is satisfactory for
+`\kern1pt{\manual j}\kern1pt' with respect to the "logo\_pen" if and
+only if $.5w$ is a good $x$~value for vertical strokes.
+\answer Let $b$ be the pen breadth. Then $.5w$ is a good $x$ value if and only
+if $"lft"\,.5w$ is an integer; but $"lft"\,.5w=.5w-.5b$, and this is an
+integer if and only if $w-b$ is even.
+
+\danger If $w$ is not a good value, we want to replace it by either
+$w+1$ or~$w-1$, whichever is closer to the device-independent width
+from which $w$ was rounded. For example, if $w$ was rounded to 22 from
+the ideal width~21.7, we want to change it to 21 rather than~23.
+Plain \MF's ^@change\_width@ routine does this. Hence we have the
+following program for `\kern1pt{\manual j}\kern1pt', in place of the
+\rightfig 4b ({208\apspix} x {216\apspix}) ^-18pt
+simpler version found in exercise 11.\metaT:
+\begindisplay
+@beginlogochar@(|"T"|$,13)$;\cr
+@if@ $.5w<>"good.x"\,.5w$: @change\_width@; @fi@\cr
+$"lft"\,x_1=-"eps"$;\cr
+$x_2=w-x_1$;\cr
+$x_3=x_4=.5w$;\cr
+$y_1=y_2=y_3$; \ $"top"\,y_1=h$; \ $"bot"\,y_4=-o$;\cr
+@draw@ $z_1\dashto z_2$; \ @draw@ $z_3\dashto z_4$;\cr
+@labels@$(1,2,3,4)$; \ @endchar@.\cr
+\enddisplay
+\decreasehsize 44mm
+Chapter 4 said that `\kern1pt{\manual j}\kern1pt' was the simplest of the
+seven logo letters, but it has turned out to be the trickiest.
+
+\restorehsize
+\ddanger This program has one unexplained feature. Why was $"lft"\,x_1$
+set to $-"eps"$ instead of zero? The answer requires an understanding
+of the pen polygons discussed in Chapter~16. The edges of those polygons
+are highly likely to pass through ambiguous points when the center of
+the pen has integer or half-integer coordinates. \MF\ shifts paths slightly
+to the right and up, in order to resolve ambiguities; therefore if
+ambiguous points occur at the left and right edges of the
+`\kern1pt{\manual j}\kern1pt', some pixels will be lost at the left but
+gained at the right. The constant ^"eps" is 0.00049, which is small but
+positive enough that \MF\ will surely notice it. Subtracting "eps"
+from~$x_1$ and adding "eps" to~$x_2$ avoids ambiguous edge points and
+keeps the result symmetric.
+
+\ddanger Since the ^{overshoot} `$o$' is always "eps" more than an
+integer, it is unnecessary to do anything similar at point~$z_4$;
+the equation `$"bot"\,y_4=-o$' is sufficient.
+
+\ddanger Point $z_3$ in the middle of the `{\manual h}' ^^{M} is in
+a satisfactory position because $"bot"\,y_3="ygap"-"o"$.
+If $"bot"\,y_3$ were exactly an integer, the~`{\manual h}' would often turn
+out to be unsymmetric, because of ambiguous points on the boundary
+at~$z_3$.
+
+\ddangerexercise True or false: If "currentpen" is @pencircle@ xscaled "px"
+yscaled~"py", the command `@draw@ $(-"epsilon",0)\to(+"epsilon",0)$'
+will produce an image that has both left-right and top-bottom symmetry.
+\ (Assume that "autorounding"="smoothing"=0.)
+\answer There are no ambiguous points on the outlines of this stroke,
+except perhaps on the top and bottom edges; the latter can occur only if
+$\round"py"$ is odd. Hence there is always left-right symmetry, but
+top-bottom symmetry might fail because of a missing row at the bottom
+(e.g., when $"px"="py"=3$). In a case like the `\kern1pt{\manual j}\kern1pt'
+we do have both symmetries, because $y_1$ and $x_4$ are in good positions.
+
+\ddangerexercise The polygon for `^@pencircle@ scaled 3' is an octagon
+whose vertices are at the points $(\pm0.5,\pm1.5)$ and $(\pm1.5,\pm0.5)$.
+Prove that if you `^@draw@~$(x,y)$' with this pen, the result never has
+both top-bottom and left-right symmetry.
+\answer No matter where you place the octagon so that it isn't touching
+any ambiguous points, exactly seven ambiguous points are inside it; hence
+every one-point ^@draw@ fills exactly seven pixels. \ (In fact,
+you always get one of the patterns
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{\kern\Blankpix RR}\hbox{RRR}\hbox{\kern\Blankpix RR}\kern1pt}}$,
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{\kern\Blankpix R}\hbox{RRR}\hbox{RRR}\kern1pt}}$,
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{RR}\hbox{RRR}\hbox{RR}\kern1pt}}$, or
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{RRR}\hbox{RRR}\hbox{\kern\Blankpix R}\kern1pt}}$.)
+
+\ddanger Rounding can also help to position points at which we don't
+have horizontal or vertical tangents. For example, consider the
+``^{sharp sign}'' or ``^{hash mark}'' character that's drawn by the
+\rightfig 24e ({300\apspix} x {320\apspix}) ^-60pt
+following program:
+\begindisplay
+$u\0:={10\over18}"pt"\0$; \ @define\_pixels@$(u)$;\cr
+@beginchar@$\,(0,15u\0,{250\over36}"pt"\0,{70\over36}"pt"\0)$;\cr
+@pickup@ @pencircle@\cr
+\qquad scaled $(.4"pt"+"blacker")$;\cr
+$"lft"\,x_1=\round u-"eps"$;\cr
+$x_3=x_1$;\cr
+$x_2=x_4=w-x_1$;\cr
+$y_1=y_2="good.y"(.5[-d,h]+"pt")$;\cr
+$y_3=y_4=h-d-y_1$;\cr
+@draw@ $z_1\dashto z_2$; \ @draw@ $z_3\dashto z_4$;\cr
+$"lft"\,x_6=\round 3u$;\cr
+$x_7=w-x_6$;\cr
+$x_8="good.x"\,.5w$;\cr
+$x_5-x_6=x_7-x_8$;\cr
+$"top"\,y_5="top"\,y_7=h+"eps"$;\cr
+$"bot"\,y_6="bot"\,y_8=-d-"eps"$;\cr
+@draw@ $z_5\dashto z_6$; \ @draw@ $z_7\dashto z_8$;\cr
+@labels@(^@range@ 1 ^@thru@ 8);\cr
+@endchar@.\cr
+\enddisplay
+If we digitize this character according to ^"lowres" mode at 200
+pixels per inch, we get the following results:
+\begindisplay
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+SSSSSSSSSSSRSSSSSRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRSSSSSRSSSSSSSSSSS\cr
+}}\qquad
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+}}\qquad
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSSRRSSSSRRSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSSSSSSSSRRSSSSRRSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSSRRSSSSRRSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSSSSSSRRSSSSRRSSSSSSSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSRRRRRRRRRRRRRRRRRRRSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSSRRSSSSRRSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+SSSSSRRSSSSRRSSSSSSSSSS\cr
+}}
+\enddisplay
+The left-hand example was obtained by omitting the `round' and `"good.x"'
+instructions in the equations for $x_6$ and~$x_8$. This meant that points
+$z_6$ and $z_8$ fell into different, possibly unlucky, raster positions,
+so the two diagonal strokes digitized differently even though they
+came from essentially identical undigitized lines. The middle example
+was produced by the given program without changes. And the right-hand
+example was produced by drawing the diagonals in a more complicated way:
+The commands `@draw@~$z_5\dashto z_6$; @draw@~$z_7\dashto z_8$;' were
+replaced by
+\begindisplay
+$y_{15}=y_1$; \ $z_{15}="whatever"[z_5,z_6]$; \
+ $y_{36}=y_3$; \ $z_{36}="whatever"[z_5,z_6]$;\cr
+$y_{27}=y_2$; \ $z_{27}="whatever"[z_7,z_8]$; \
+ $y_{48}=y_4$; \ $z_{48}="whatever"[z_7,z_8]$;\cr
+\noalign{\smallskip}
+@draw@ $z_5\dashto("good.x"(x_{15}+.5),y_1)\dashto("good.x"(x_{15}-.5),y_1)$\cr
+\qquad\qquad$\dashto("good.x"(x_{36}+.5),y_3)\dashto("good.x"(x_{36}-.5),y_3)
+ \dashto z_6$;\cr
+@draw@ $z_7\dashto("good.x"(x_{27}+.5),y_2)\dashto("good.x"(x_{27}-.5),y_2)$\cr
+\qquad\qquad$\dashto("good.x"(x_{48}+.5),y_4)\dashto("good.x"(x_{48}-.5),y_4)
+ \dashto z_8$;\cr
+\enddisplay
+The idea here was to control the goodness of the points where the
+diagonals intersect the horizontal bar lines, and to hide one of the
+``^{jaggies}'' inside each bar line. If we do the same three experiments
+but triple the resolution, we get similar results but the differences are
+not quite so obvious:
+\begindisplay
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+}}\qquad
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+}}\qquad
+\vbox{\manual\offinterlineskip\halign{#\hfil\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+}}
+\enddisplay
+
+\danger When letters are drawn by filling outlines, the left and right
+outlines are digitized independently; therefore corresponding outlines
+should usually be offset from each other by an integer amount whenever
+possible. For example, suppose that the letter~`^{n}' is being drawn
+with commands like
+\begindisplay
+$\penpos2("stem",0)$; \ $\penpos4("stem",0)$
+\enddisplay
+to specify the stroke widths at the base of the two ^{stems}.
+We will therefore have $x_{2r}-x_{2l}=x_{4r}-x_{4l}="stem"$. If
+"stem" is not an integer, say $"stem"=2.7$, we might have
+$x_{2l}=2.1$, $x_{2r}=4.8$, $x_{4l}=9.6$, $x_{4r}=12.3$;
+then $x_{2r}-x_{2l}$ will digitize to $5-2=3$, so the left stem
+will be three pixels wide, but the right stem will be only
+$12-10=2$ pixels wide. We could get around this problem by
+insisting that either $x_{2l}$ or~$x_{2r}$ be an integer,
+and that either $x_{4l}$ or~$x_{4r}$ be an integer; then both stems
+would be three pixels wide. But other quantities calculated from "stem"
+(e.g., the breadth of diagonal strokes) would then be based on a
+value of~2.7 instead of the stem width~3 that an observer of the
+font actually perceives. Therefore it is best to make "stem" an integer.
+The proper way to do this is generally to say
+\begindisplay
+^@define\_whole\_blacker\_pixels@("stem");
+\enddisplay
+this command computes "stem" from $"stem"\0$ by the formula
+\begindisplay
+$"stem":=max\bigl(1,\,\round("stem"\0\ast"hppp"+"blacker")\bigr)$.
+\enddisplay
+(Notice that this rounding operation is not allowed to reduce "stem"
+to zero at low resolutions.)
+
+\danger Even when the "stem" width is an integer in the `n' example,
+we probably want to arrange things so that $x_{2l}$, $x_{2r}$, $x_{4l}$,
+and~$x_{4r}$ are integers, because this will give the least distortion
+under digitization. Suppose, however, that it's most convenient to define
+the pen position at the center of the stroke instead of at the edge; i.e.,
+the program would say just `$x_2=\alpha$' if rounding were not taken into
+account. How should $x_2$ be defined, when we want $x_{2l}$ to be an
+integer? We could say
+\begindisplay
+$x_2=\alpha$; \ $x_{2l}:=\round x_{2l}$; \ $x_{2r}:=\round x_{2r}$; \
+$x_2:=.5[x_{2l},x_{2r}]$
+\enddisplay
+but that's too complicated; moreover, it will fail if any other
+variables depend on $x_2$, $x_{2l}$, or $x_{2r}$, because such
+dependencies are forgotten when new values are assigned.
+In the case of fixed pens we solved this problem by saying
+`$x_2="good.x"\,\alpha$'; but the "good.x" function doesn't know
+about "stem". One solution is to say
+\begindisplay
+$x_{2l}=\round(\alpha-.5"stem")$,
+\enddisplay
+or equivalently, `$x_{2r}=\round(\alpha+.5"stem")$'. This does the
+job all right, but it isn't completely satisfying. It requires
+knowledge of the breadth that was specified in the $\penpos2$ command,
+and it works only when the penpos angle is~0. If the penpos command
+is changed, the corresponding equation for rounding must be
+changed too. There's another solution that's more general and more
+attractive once you get used to it:
+\begindisplay
+$x_{2l}=\round\bigl(x_{2l}-(x_2-\alpha)\bigr)$.
+\enddisplay
+Why does this work? The argument to `^{round}' must be a known value,
+but both $x_{2l}$ and~$x_2$ are unknown. Fortunately, their difference
+$x_{2l}-x_2$ is known, because of the $\penpos2$ command. The
+rounding operation makes $x_2\approx\alpha$ because it makes $x_{2l}$
+approximately equal to the value of $x_{2l}$ minus the difference
+between $x_2$ and~$\alpha$.
+
+\ddangerexercise The generality of this technique can be appreciated
+by considering the following more difficult problem that the author
+faced while designing a~`^{w}': Suppose you want $x_1-x_2$ to be
+an integer and $x_3\approx x_4$, and suppose that $x_2$, $x_3-x_1$,
+and~$x_4+x_1$ are known; but $x_1$ is unknown, hence $x_3$ and~$x_4$
+are also unknown. According to our general idea, we want to specify an
+equation of the form `$x_1-x_2=\round(x_1-x_2+f)$', where $x_1-x_2+f$
+is known and $f$~is a formula that should be approximately zero.
+In this case $x_3-x_4$ is approximately zero, and $(x_3-x_1)-(x_4+x_1)$
+is known; what value of~$f$ should we choose?
+\answer $f=.5(x_4-x_3)$; the desired equation is
+`$x_1-x_2=\round\bigl(x_1-x_2+.5(x_4-x_3)\bigr)$'.
+
+\ddanger In many fonts, such as the one you are now reading,
+curved lines swell out so that the thick parts of~`^{o}' are actually
+a bit broader than the stems of~`n'. Therefore the ^{Computer Modern}
+font routines discussed in Appendix~E have two parameters,
+$"stem"\0$ and $"curve"\0$, to govern the stroke thickness.
+For example, the font ^|cmr9| used in the present paragraph has
+$"stem"\0=2/3"pt"\0$ and $"curve"\0=7/9"pt"\0$. Both of these should
+be integers, hence the ^@font\_setup@ macro in Appendix~E
+dutifully says
+\begindisplay
+@define\_whole\_blacker\_pixels@$("stem","curve")$.
+\enddisplay
+Although this looks good on paper, it can cause problems at certain
+low resolutions, because the rounding operation might make ^"stem" and
+^"curve" rather different from each other even though $"stem"\0$ and
+$"curve"\0$ are fairly close. For example, the resolution might be
+just at the value where |cmr9|'s "stem" turns out to be only~2
+but "curve" is~3. Curves shouldn't be that much darker than stems;
+they would look too splotchy. Therefore plain \MF\
+has a `^@lowres\_fix@' subroutine, and Appendix~E says
+\begindisplay
+@lowres\_fix@("stem","curve") 1.2
+\enddisplay
+after "stem" and "curve" have been defined as above. In this particular
+case @lowres\_fix@ will reset $"curve":="stem"$ if it turns out that the
+ratio $"curve"/"stem"$ is greater than 1.2 times the ratio
+$"curve"\0/"stem"\0$. Since $"curve"\0/"stem"\0=7/6$ in the case of |cmr9|,
+this means that the ratio $"curve"/"stem"$ after rounding is allowed
+to be at most~1.4; if $"curve"=3$ and $"stem"=2$, the "curve" parameter
+will be lowered to~2. In general the command
+\begindisplay
+@lowres\_fix@($d_1,d_2,\ldots,d_n$) $r$
+\enddisplay
+will set $d_n:=\cdots d_2:=d_1$ if $\max(d_1,d_2,\ldots,d_n)/\!
+\min(d_1,d_2,\ldots,d_n)$ is greater than
+$r\cdot\max(d_1\0,d_2\0,\ldots,d_n\0)/\!\min(d_1\0,d_2\0,\ldots,d_n\0)$.
+
+\ddangerexercise
+\parshape 12
+3pc 201pt
+3pc 201pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 237pt
+0pc 29pc
+\rightfig 4e ({180\apspix} x {225\apspix}) ^15pt
+Good digitization can also require attention to the shapes of the
+digitized angles where straight lines meet. The purpose of
+the present exercise is to illustrate the relevant ideas by
+studying the `\kern1pt{\manual\char'170}' symbol, for which a program
+appears in Chapter~4. If that program is used without change to produce
+low-resolution ^{triangle}s, the results might turn out to be unsatisfactory
+because, for example, point~3 at the right of the triangle
+might digitize into a snubnosed or asymmetric shape.
+If $y_3$ is an integer, the triangle will be top-bottom symmetric, but
+the right-hand tip will be two pixels tall and this will look too blunt.
+Therefore we should choose~$y_3$ to be an integer plus~1/2.
+Given this value of~$y_3$, what will be the shape of the rightmost
+four columns of the digitized tip, as $x_3$ varies?
+\answer Let $x_3=n+{1\over2}+\theta$, where $n$ is an integer and
+$0\le\theta<1$. By drawing lines of slope~$30^\circ$ from the pixel
+centers, we find that there are three cases for the rightmost four
+columns:
+\begindisplay
+Case A,
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{RR}\hbox{RRRR}\hbox{RR}\kern1pt}}$;\qquad
+Case B,
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{R}\hbox{RR}\hbox{RRRR}\hbox{RR}\hbox{R}\kern1pt}}$;\qquad
+Case C,
+$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{R}\hbox{RRR}\hbox{RRRR}\hbox{RRR}\hbox{R}\kern1pt}}$.
+\enddisplay
+Case A occurs for $0\le\theta<2\sqrt3-3$; Case B occurs for
+$2\sqrt3-3\le\theta<\sqrt3-1$; Case~C occurs for
+$\sqrt3-1\le\theta<1$. The tip in Case~A looks a bit too sharp,
+and Case~C looks too blunt, so Case~B seems best. This case occurs
+when $x_3$ is near an integer, so it's OK to let $x_3$ be an integer.
+
+\ddangerexercise Continuing the previous exercise, assume that $x_1$
+is an integer. What value of~$y_1$ will make the upper tip of the
+triangle look like
+`\thinspace$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{R}\hbox{RR}\hbox{RRRR}\kern1pt}}$' after digitization?
+\answer Let $y_1=n+\theta$. If $\theta$ lies between
+${1\over2}\sqrt3-{1\over2}$ and ${1\over6}\sqrt3+{1\over2}$,
+the top row after digitization will contain two black pixels.
+If $\theta$ lies between ${1\over6}\sqrt3+{1\over2}$ and
+${5\over6}\sqrt3-{1\over2}$, we get the desired shape. Otherwise
+we get `\thinspace$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{R}\hbox{RRR}\hbox{RRRR}\kern1pt}}$'.
+
+\ddangerexercise Concluding the previous exercise, modify the
+program of Chapter 4 so that the upper tip and
+the upper part of the right tip both digitize to the shape
+`\thinspace$\vcenter{\vbox{\offinterlineskip\manual
+ \hbox{R}\hbox{RR}\hbox{RRRR}\kern1pt}}$'.
+\answer (We choose $\theta={1\over2}\sqrt3$ in the previous exercise, since
+^^{floor} this is the midpoint of the desirable interval.) The equations
+are changed to
+\begindisplay
+$x_1=x_2=w-x_3=\round s$;\cr
+$y_3=.5+{\rm floor}\,.5h$;\cr
+$z_1-z_2=(z_3-z_2)$ rotated 60;\cr
+$y_1:=.5\rmsqrt3+\round(y_1-.5\rmsqrt3)$;\cr
+$y_2:=h-y_1$;\cr
+\enddisplay
+and then we @fill@ $z_1\dashto z_2\dashto z_3\dashto\cycle$ as before.
+
+\ddanger So far in this chapter we've assumed that pixels are square. But
+sometimes ^^{nonsquare} we need to prepare output for devices with
+general rectangular pixels, and this adds an extra dimension of
+complexity to rounding. Plain \MF\ sets things up so that
+^"currenttransform" multiplies all $y$~coordinates by
+^"aspect\_ratio", when paths are filled or drawn, or when pens are
+picked up. Furthermore the ^"top" and ^"bot" functions divide the
+amount of offset by "aspect\_ratio". This means that \MF\ programs
+can still be written as if pixels were square;
+the normal `angle' and `direction' functions, etc., can be used.
+But the good places for rounding horizontal tangents are not at
+integer values of~$y$ in general, they are actually at values that
+will become integers after multiplication by the aspect ratio.
+
+\ddanger The ^"vround" function rounds its argument to the nearest
+$y$~coordinate that corresponds to a pixel boundary in the
+general case. Thus if $"aspect\_ratio"=1$, "vround" simply rounds
+to the nearest integer, just like `round'; but if, say,
+$"aspect\_ratio"=4/3$, then "vround" will round to the nearest
+multiple of~$3/4$. Plain \MF\ uses "vround" instead of `round'
+when it computes an ^{overshoot} correction, and also when ^@beginchar@
+computes the values of ^"h" and~^"d". The ^"good.y" function produces
+a good $y$~value that takes "aspect\_ratio" properly into account.
+
+\ddangerexercise Without looking at Appendix B\null, try to guess how
+the "vround" and "good.y" macros are defined.
+\answer @vardef@ "vround" @primary@ $v$ $=$\parbreak
+\qquad floor$(v\ast"aspect\_ratio"+.5)/"aspect\_ratio"$ @enddef@;\parbreak
+@vardef@ "good.y" @primary@ $y$ $=$\parbreak
+\qquad "vround"$(y+"pen\_top")-"pen\_top"$ @enddef@.
+
+\ddangerexercise What are the ``ambiguous points'' when pixels
+are not square?
+\answer $\bigl(m+1/2,(n+1/2)/"aspect\_ratio"\bigr)$. These are the points
+that "currenttransform" maps into pixel centers.
+
+\ddanger The \MF\ ^{logo} as we have described it so far will round
+properly with respect to arbitrary aspect ratios if we make only
+a few more refinements. The value of "ygap" should be vrounded
+instead of rounded, so we initialize it by saying
+\begindisplay
+^@define\_whole\_vertical\_pixels@("ygap").
+\enddisplay
+Furthermore we should say
+\begindisplay
+$"ho"\0:="o"\0$; \ ^@define\_horizontal\_corrected\_pixels@("ho");
+\enddisplay
+and "ho" should replace ^"o" in the equations for $x_4$ in the programs
+for `{\manual i}' and~`{\manual l}'. ^^{E} ^^{F}
+Everything else should work satisfactorily as it stands.
+
+\ddanger Appendix B includes macros ^"good.top", ^"good.bot", ^"good.lft",
+and ^"good.rt" that take pairs as arguments. If you say, for example,
+`$z_3="good.top"(\alpha,\beta)$' it means that $z_3$ will be near
+$(\alpha,\beta)$ and that when $z_3$ is modified by ^"currenttransform"
+the top point of ^"currentpen" placed at the transformed point will
+be in a good raster position.
+
+\danger \MF's `^"autorounding"' feature tries to adjust curves to the
+raster for you, but it is a mixed blessing. Here's how it works:
+If the internal quantity "autorounding" is positive, the $x$~coordinates
+of all paths that are filled or drawn are rounded to good raster positions
+wherever there's a vertical tangent; and the $y$~coordinates
+are rounded to good raster positions wherever there's a horizontal
+tangent. The rest of the curve is distorted appropriately, as if
+the raster were stretching or shrinking slightly. If $"autorounding">1$,
+you get even more changes: Paths are perturbed slightly at $\pm45^\circ$
+tangent directions, so that second-order ^{pimples} and flat spots don't
+appear there.
+
+\danger For example, if we return to the Ionian `{\manual\IOO}' with
+which we began this chapter, let's suppose that "curve\_sidebar" was left
+unrounded. We saw that the result was bad when "autorounding" was~0;
+when $"autorounding"=1$ and~2 we get this:
+\displayfig 24f\&g (190\apspix)
+The stroke has gotten a lot thinner at the sides, by comparison with
+the original design (which, incidentally, can be seen in the illustrations
+below). Although autorounding has produced a fairly recognizable O~shape,
+the character of the original has been lost, especially in the case
+$"autorounding"=2$; indeed, the inner outline has been brought towards the
+center, in the upper left and lower right sectors, and this has made the
+digitized inner boundary perfectly symmetric!
+
+\ddanger There's an internal quantity called ^"granularity", normally
+equal to~1, which affects autorounding by effectively scaling~up
+the raster size. If, for example, $"granularity"=4$, the autorounded
+$x$~coordinates and $y$~coordinates will become multiples of~4 instead
+of simply integers. The illustrations above were produced by
+setting $"granularity"=10$ and $"mag"=10$; this made the
+effects of autorounding visible. The granularity should always be an integer.
+
+\ddanger Besides "autorounding", there's a `smoothing' feature
+that becomes active when ^"smoothing"$\null>0$. The basic idea is
+to try to make the edges of a curve follow a regular progression
+instead of wobbling. A complete discussion of the smoothing algorithm
+is beyond the scope of this manual, but an example should make the
+general idea clear: Let's use the letters $R$ and~$D$ to stand for
+single-pixel steps to the right and down, respectively. If a digitized
+path goes `"RDDRDRDDD"', say, the number of downward steps per
+rightward step is first decreasing, then increasing; the "smoothing"
+process changes this to `"RDDRDDRDD"'. If smoothing is applied to the
+Ionian `{\manual\IOO}' shapes above, nothing happens; but if we go back
+to the original obtained with $"autorounding"=0$, we get a few changes:
+\displayfig 24b\&h (190\apspix)
+Three pixels have been added by "smoothing" in the right-hand illustration;
+e.g., a pattern "RDRDDDDRDD" has become "RDDRDDDRDD".
+
+\danger If you do your own rounding, it turns out that autorounding
+and smoothing usually change very few pixels, if any; thus your
+safest strategy is probably to turn them off in such cases. If you
+define your strokes by outlines, autorounding and smoothing
+apply independently to the left and right edges, so they may
+hurt as often as they help; again, they should probably be turned off.
+But if you are drawing with fixed pens, autorounding generally
+works well and saves a lot of fuss. If the pens are circles or
+nearly circles, smoothing is also helpful; but if the pens are
+more ``calligraphic,'' they are supposed to produce nonsmooth
+edges occasionally, so you had better set $"smoothing":=0$.
+
+\ddanger If you ``^{slant}'' a font by modifying "currenttransform"
+as described in Chapter~15, positions of horizontal tangency will
+remain the same. But positions of vertical tangency will change
+drastically, and they will probably not fall in known parts
+of your design. This means, for example, that autorounding will be
+helpful in a slanted pen-generated font like the
+`{\manual 89:;<=>:}\kern2pt' logo. However, the author ^^{Knuth}
+found that the outline-generated letters of ^{Computer Modern}
+{\it^{italic}\/} came out better with $"autorounding"=0$, because
+autorounding tended to make some characters too dark and others too light.
+
+\ninepoint
+
+\ddanger The effect of autorounding can be studied numerically
+if you set ^"tracingspecs" to a positive value; this displays \MF's
+internal calculations as it finds horizontal, vertical, and diagonal
+tangent points. \ (\MF\ prepares to digitize paths by first
+subdividing each B\'ezier segment into pieces that travel in only one
+``^{octant}'' direction.) \ For example, if $"autorounding"=0$
+and $"tracingspecs"=1$, and if "curve\_sidebar" is left unrounded,
+the file |io.log| will contain the following information about the
+outer curve of the `{\manual\IOO}':
+\beginlines \advance\hsize.71pt
+|Path at line 15, before subdivision into octants:|
+|(1.53745,9.05345)..controls (1.53745,4.00511) and (5.75409,-0.00049)|
+| ..(10.85147,-0.00049)..controls (16.2217,-0.00049) and (20.46255,4.51297)|%
+ \kern.5em\null
+| ..(20.46255,9.94655)..controls (20.46255,14.99713) and (16.23842,19.00049)|
+ \kern-.71pt
+| ..(11.13652,19.00049)..controls (5.77066,19.00049) and (1.53745,14.48491)|%
+ \kern.5em\null
+| ..cycle|
+\smallskip
+|Cycle spec at line 15, after subdivision:|
+|(1.53745,9.05345) % beginning in octant `SSE'|
+| ..controls (1.53745,6.58786) and (2.54324,4.371)|
+| ..(4.16621,2.74803) % segment 0|
+|% entering octant `ESE'|
+| ..controls (5.8663,1.04794) and (8.24362,-0.00049)|
+| ..(10.85147,-0.00049) % segment 0|
+|% entering octant `ENE'|
+\endlines
+$\ldots$ and so on; there are lots more numbers! What does this all mean?
+^^|ENE|^^|ESE|^^|SSE|^^{compass directions}
+Well, the first segment of the curve, from $(1.53745,9.05345)$ to
+$(10.85147,-0.00049)$,
+has been subdivided into two parts at the place where the slope is $-1$.
+The first of these parts travels basically `South by South East' and
+the second travels `East by South East'. The other three segments are
+subdivided in a similar way (not shown here). If you try the same
+experiment but with $"autorounding"=1$, some rather different numbers
+emerge: \looseness=-1
+
+\goodbreak
+\beginlines
+|Cycle spec at line 15, after subdivision and autorounding:|
+|(2,9.05348) % beginning in octant `SSE'|
+| ..controls (2,6.50526) and (3.02194,4.22272)|
+| ..(4.6577,2.58696) % segment 0|
+|% entering octant `ESE'|
+| ..controls (6.2624,0.98225) and (8.45786,0)|
+| ..(10.85873,0) % segment 0|
+|% entering octant `ENE'|
+\endlines
+Point $(1.53745,9.05345)$, where there was a vertical tangent, has been
+rounded to $(2,9.05348)$; point $(10.85147,-.00049)$, where there was
+a horizontal tangent, has been rounded to $(10.85873,0)$; the intermediate
+control points have been adjusted accordingly. \ (Rounding of $x$~coordinates
+has been done separately from $y$~coordinates.) \ Finally, with
+$"autorounding"=2$, additional adjustments are made so that the
+$45^\circ$ transition point will occur at what \MF\ thinks is a good spot:
+\beginlines
+|Cycle spec at line 15, after subdivision and double autorounding:|
+|(2,9.05348) % beginning in octant `SSE'|
+| ..controls (2,6.6761) and (3.07103,4.42897)|
+| ..(4.78537,2.71463) % segment 0|
+|% entering octant `ESE'|
+| ..controls (6.46927,1.03073) and (8.62749,0)|
+| ..(10.85873,0) % segment 0|
+|% entering octant `ENE'|
+\endlines
+(Notice that $4.78537+2.71463=7.50000$; when the slope
+is~$-1$ at a transition point $(x,y)$, the curve stays as far away as
+possible from ambiguous points near the transition if $x+y+.5$ is an integer.)
+
+\endchapter
+
+\rightline{\vbox{\offinterlineskip\manual\halign{#\hfil\cr
+SRRRRRRRRRSSSSSSSSSSSRRRRRRSSSSSSSRRRRRRRRRSSSSSSS\cr
+SSRRRSSSSSRRSSSSSSSRRSSSSSSRRSSSSSSRRRSSSSRRSSSSSS\cr
+SSSRRSSSSSSRRSSSSSRRSSSSSSSSRRSSSSSSRRSSSSSRRSSSSS\cr
+SSSRRSSSSSSRRSSSSRRSSSSSSSSSSRRSSSSSRRSSSSSRRSSSSS\cr
+SSSRRSSSSSSRRSSSRRSSSSSSSSSSSSRRSSSSRRSSSSSRRSSSSS\cr
+SSSRRSSSSSSRRSSSRRSSSSSSSSSSSSRRSSSSRRSSSSSRRSSSSS\cr
+SSSRRSSSSSSRRSSRRSSSSSSSSSSSSSSRRSSSRRSSSSSRRSSSSS\cr
+SSSRRSSSSSRRSSSRRSSSSSSSSSSSSSSRRSSSRRSSSSRRSSSSSS\cr
+SSSRRSSRRRRSSSSRRSSSSSSSSSSSSSSRRSSSRRRRRRSSSSSSSS\cr
+SSSRRSSSSSSSSSSRRSSSSSSSSSSSSSSRRSSSRRSSRRRSSSSSSS\cr
+SSSRRSSSSSSSSSSRRSSSSSSSSSSSSSSRRSSSRRSSSRRRSSSSSS\cr
+SSSRRSSSSSSSSSSRRSSSSSSSSSSSSSSRRSSSRRSSSSRRRSSSSS\cr
+SSSRRSSSSSSSSSSSRRSSSSSSSSSSSSRRSSSSRRSSSSSRRRSSSS\cr
+SSSRRSSSSSSSSSSSRRSSSSSSSSSSSSRRSSSSRRSSSSSSRRSSSS\cr
+SSSRRSSSSSSSSSSSSRRSSSSSSSSSSRRSSSSSRRSSSSSSRRRSSS\cr
+SSSRRSSSSSSSSSSSSSRRSSSSSSSSRRSSSSSSRRSSSSSSSRRRSS\cr
+SSRRRRSSSSSSSSSSSSSRRSSSSSSRRSSSSSSRRRRSSSSSSSRRRS\cr
+SRRRRRRSSSSSSSSSSSSSSRRRRRRSSSSSSSRRRRRRSSSSSSSRRR\cr
+\kern23\Blankpix RRR\cr
+\kern24\Blankpix RRR\cr
+\kern25\Blankpix RRR\cr
+\kern26\Blankpix RRRR\cr
+}}}
+\author PIERRE ^{LE B\'E}, {\sl B\'ele Pr\'erie\/} (1601)
+ % (an extract from his third alphabet)
+
+\bigskip
+\bigskip
+
+\rightline{\vbox{\offinterlineskip\manual\halign{#\hfil\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQPPPPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQPPPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQPPPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQ\cr
+}\kern7\blankpix}\vbox{\offinterlineskip\manual\halign{#\hfil\cr
+\kern19\blankpix PPPPPPPPPP\cr
+QQQQQQQQQQQQQQQQPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQ\cr
+QQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQ\cr
+QQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQ\cr
+QQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQQQPPPPPPPPPPPPPPQQQQQQPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQQQPPPPPPPPPPPPQQQQQQQQQQPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPQQQQQQQQQQQQPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQPPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQQ\cr
+QQQQPPPPPPPPPPPQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQQPQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQQPPQQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPQQQQQQQQQQPPPPQQQQQQQQPPPPPPPPPPQQQ\cr
+QQQPPPPPPPPPPPQQQQQQQQPPPPPPQQQQQQQPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQPPPPPPPPQQQQQQPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQPPPPPPPPPQQQQQQPPPPPPPPPQQQQ\cr
+QQQQPPPPPPPPPPQQQQQPPPPPPPPPPPQQQQQPPPPPPPPQQQQQ\cr
+QQQQPPPPPPPPPPQQQQQQQPPPPPPPPPPQQQQPPPPPPPPQQQQQ\cr
+QQQQPPPPPPPPPPPQQQQQQQPPPPPPPPPPQQQPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQPPPPPPPPPQQQPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPQQQQQQQQPPPPPPPPQQQPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQQPPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQQPPPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQQQPPPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQQQPPPPPPPPPPPPPPQQQQQQQQPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQQQQPPPPPPPPPPPPPPPQQQQQQPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQQQQQQQQPPPPPPPPPPPPPPPPPPPPQQPPPPPPPPQQQQQQ\cr
+QQQQQQQQQQQQQQPPPPPPPPPPPPPPPPPPQQPPPPPPPPPQQQQQ\cr
+QQQQQQQQQQQQQQQQPPPPPPPPPPPPPPQQQQPPPPPPPPPPQQQQ\cr
+\kern19\blankpix PPPPPPP\kern9\blankpix PPPPPPPPPPP\cr
+\kern36\blankpix PPPPPPPPP\cr
+\kern37\blankpix PPPPPPP\cr
+\kern37\blankpix PPPPPP\cr
+\kern38\blankpix PPPP\cr
+\kern39\blankpix PP\cr
+\kern39\blankpix P\cr
+}}\vbox{\offinterlineskip\manual\halign{#\hfil\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQPPPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQPPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQPPPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPPPPPPQPPPPPPPPPQQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQPPPPPPPPPPQQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQPPPPPPPPPPPQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQPPPPPPPPPPQQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQPPPPPPPPPPPQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQPPPPPPPPPPQQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQPPPPPPPPPPPQQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQPPPPPPPPPPPQQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQPPPPPPPPPPPQQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQPPPPPPPPPPPQQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQPPPPPPPPPPPQQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQPPPPPPPPPPPQQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQPPPPPPPPPPPQQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPPQQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQPPPPPPPPPPPPQQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPPPQ\cr
+QQQQQPPPPPPPPPPQQQQQQQQQQQQQQQQPPPPPPPPPPPPQ\cr
+}\kern7\blankpix}}
+\author MATTHEW ^{CARTER}, {\sl Bell Centennial\/} (1978)
+ % from the 6pt `name and number' font as he digitized it by hand
+ % reference: Type & Technology Manuscript No.1, Cooper Union (1982)
+
+\eject
+ \beginChapter Chapter 25. Summary of\\Expressions
+
+We've seen that \MF\ can handle a wide variety of algebraic ^{expressions};
+now it's time to consolidate what we have learned. The purpose of this
+chapter and the one that follows is to present a precise and concise
+summary of everything that \MF\ knows how to do.
+
+We shall be concerned here solely with \MF's {\sl^{primitive}\/} operations,
+rather than with the higher-level features of the plain \MF\ base that
+comprise the bulk of typical programs. Therefore novice users should put
+off reading Chapters 25 and~26 until they feel a need to know what
+goes on at the more mundane levels inside the computer. Appendix~B contains
+a summary of the features of plain \MF\!, together with a ready-reference guide
+to the things that most people want to know about \MF\ usage.
+
+\ninepoint\medskip
+The remainder of this chapter is set in small type, like that of the
+present paragraph, since it is analogous to material that is marked
+``doubly dangerous'' in other chapters. Instead of using dangerous
+bend signs repeatedly, let us simply agree that Chapters 25 and~26 are
+dangerous by definition.
+
+Chapter 8 introduced the general idea of expressions and the four-fold
+``primary, secondary, tertiary, expression'' ^{hierarchy} on which
+their syntax is based. \MF's variables can have any of eight types:
+@boolean@, @numeric@, @pair@, @path@, @pen@, @picture@, @string@,
+and @transform@. Its expressions can actually have nine different
+types, although the ninth one---``^{vacuous}''---is not particularly
+interesting since it has only one possible value. Here is the overall
+syntax:
+\beginsyntax
+<primary>\is<boolean primary>\alt<numeric primary>
+ \alt<pair primary>\alt<path primary>
+ \alt<pen primary>\alt<future pen primary>
+ \alt<picture primary>\alt<string primary>
+ \alt<transform primary>\alt<vacuous primary>\endgraf\medskip
+<secondary>\is<boolean secondary>\alt<numeric secondary>
+ \alt<pair secondary>\alt<path secondary>
+ \alt<pen secondary>\alt<future pen secondary>
+ \alt<picture secondary>\alt<string secondary>
+ \alt<transform secondary>\alt<vacuous secondary>\endgraf\medskip
+<tertiary>\is<boolean tertiary>\alt<numeric tertiary>
+ \alt<pair tertiary>\alt<path tertiary>
+ \alt<pen tertiary>\alt<picture tertiary>
+ \alt<string tertiary>\alt<transform tertiary>
+ \alt<vacuous tertiary>\endgraf\medskip
+<expression>\is<boolean expression>\alt<numeric expression>
+ \alt<pair expression>\alt<path expression>
+ \alt<pen expression>\alt<picture expression>
+ \alt<string expression>\alt<transform expression>
+ \alt<vacuous expression>
+\endsyntax
+We shall discuss the different types of expressions in alphabetic order;
+thus, if you are dying to know what a ``vacuous'' expression is,
+you should skip to the end of the chapter. \looseness=-1
+
+\medbreak
+\textindent\bull
+Boolean expressions were discussed in Chapter 19. The full syntax has
+one more operation, `charexists', that was not mentioned there:
+\beginsyntax
+<boolean primary>\is<boolean variable>\alt<boolean argument>
+ \alt[true]\alt[false]
+ \alt[(]<boolean expression>[)]
+ \alt[begingroup]<statement list><boolean expression>[endgroup]
+ \alt[known]<primary>\alt[unknown]<primary>
+ \alt<type><primary>\alt[cycle]<primary>
+ \alt[odd]<numeric primary>
+ \alt[charexists]<numeric primary>
+ \alt[not]<boolean primary>
+<boolean secondary>\is<boolean primary>
+ \alt<boolean secondary>[and]<boolean primary>
+<boolean tertiary>\is<boolean secondary>
+ \alt<boolean tertiary>[or]<boolean secondary>
+<boolean expression>\is<boolean tertiary>
+ \alt<numeric expression><relation><numeric tertiary>
+ \alt<pair expression><relation><pair tertiary>
+ \alt<transform expression><relation><transform tertiary>
+ \alt<boolean expression><relation><boolean tertiary>
+ \alt<string expression><relation><string tertiary>
+<relation>\is[\char'74]\alt[\char'74=]\alt[>]\alt[>=]\alt[=]\alt[\char'74>]
+\endsyntax
+The expression `charexists $x$' is true if and only if a ^@shipout@
+command has previously been done with ^"charcode"$\null=x$. \ (The value
+of~$x$ is first rounded to an integer, and reduced to the range
+$0\le x<256$ by adding or subtracting multiples of~256.)
+
+In these rules, tokens like `|true|' that appear in typewriter type stand for
+any ^{tokens} whose current meaning is the same as the meaning of `|true|'
+when \MF\ starts from scratch; the particular token `|true|'---whose
+meaning may indeed change as a program runs---is not really involved.
+
+The special tokens `|(|' and~`|)|' in these rules do not refer to
+^{parentheses}; they refer to any matching pair of ^{delimiters} defined
+by a ^@delimiters@ command.
+
+A \<boolean variable> denotes a ^\<variable> whose type is @boolean@; a
+$\langle$numeric variable$\rangle$ is a \<variable> whose type is
+@numeric@; and so~on. The syntax for \<variable> was discussed in
+Chapter~7. A \<boolean argument> is an ^@expr@ ^{argument} to a macro,
+where the value of the expression is of type @boolean@;
+@expr@ arguments are put into special ``^{capsule}''
+tokens as explained in Chapter~18.
+
+\medbreak
+\textindent\bull
+Numeric expressions have the richest syntax of all, because they form the
+nucleus of the entire \MF\ language:
+\beginsyntax
+<numeric atom>\is<numeric variable>\alt<numeric argument>
+ \alt<numeric token primary>
+ \alt<internal quantity>
+ \alt[normaldeviate]
+ \alt[(]<numeric expression>[)]
+ \alt[begingroup]<statement list><numeric expression>[endgroup]
+<numeric token primary>\is<numeric token>[/]<numeric token>
+ \alt<numeric token not followed by `{\tt/}$\langle$numeric token$\rangle$'>
+<numeric primary>\is<numeric atom>
+ \alt<numeric atom>[\char'133]<numeric expression>%
+ [,]<numeric expression>[\char'135]
+ \alt[length]<numeric primary>\alt[length]<pair primary>
+ \alt[length]<path primary>\alt[length]<string primary>
+ \alt[ASCII]<string primary>\alt[oct]<string primary>\alt[hex]<string primary>
+ \alt<pair part><pair primary>\alt<transform part><transform primary>
+ \alt[angle]<pair primary>
+ \alt[turningnumber]<path primary>\alt[totalweight]<picture primary>
+ \alt<numeric operator><numeric primary>
+ \alt[directiontime]<pair expression>[of]<path primary>
+<pair part>\is[xpart]\alt[ypart]
+<transform part>\is<pair part>\alt[xxpart]\alt[xypart]\alt[yxpart]\alt[yypart]
+<numeric operator>\is[sqrt]\alt[sind]\alt[cosd]\alt[mlog]\alt[mexp]
+ \alt[floor]\alt[uniformdeviate]\alt<scalar multiplication operator>
+<scalar multiplication operator>\is<plus or minus>
+ \alt<numeric token primary not followed by {\tt+} or {\tt-} or a numeric token>
+<numeric secondary>\is<numeric primary>
+ \alt<numeric secondary><times or over><numeric primary>
+<times or over>\is[*]\alt[/]
+<numeric tertiary>\is<numeric secondary>
+ \alt<numeric tertiary><plus or minus><numeric secondary>
+ \alt<numeric tertiary><Pythagorean plus or minus><numeric secondary>
+<plus or minus>\is[+]\alt[-]
+<Pythagorean plus or minus>\is[++]\alt[+-+]
+<numeric expression>\is<numeric tertiary>
+\endsyntax
+Each of the operations mentioned in this syntax has already been explained
+somewhere in this book; Appendix~I tells where.
+
+\medbreak
+This is a good time to list all of the internal quantities that are
+initially present in \MF:
+\begindisplay
+^"tracingtitles"&show titles online when they appear\cr
+^"tracingequations"\hidewidth&show each variable when it becomes known\cr
+^"tracingcapsules"\hidewidth&show capsules as well as variables\cr
+^"tracingchoices"&show the control points chosen for paths\cr
+^"tracingspecs"&show subdivision of paths into octants before digitizing\cr
+^"tracingpens"&show vertices of pens as they are made from future pens\cr
+^"tracingcommands"\hidewidth
+ &show commands and operations before they're performed\cr
+^"tracingrestores"&show when a symbol or internal quantity is restored\cr
+^"tracingmacros"&show macros before they are expanded\cr
+^"tracingedges"&show digitized edges as they are computed\cr
+^"tracingoutput"&show digitized edges as they are output\cr
+^"tracingonline"&show long diagnostics on the terminal and in the log\cr
+^"tracingstats"&log the memory usage at end of job\cr
+^"pausing"&show lines on the terminal before they are read\cr
+^"showstopping"&stop after each @show@ command\cr
+^"fontmaking"&produce font metric output\cr
+^"proofing"&produce proof mode output\cr
+^"turningcheck"&reorient clockwise paths, flag strange ones\cr
+^"warningcheck"&advise when a variable value gets large\cr
+^"smoothing"&remove certain glitches from digitized curves\cr
+^"autorounding"&move paths to ``good'' tangent points\cr
+^"granularity"&the pixel size for "autorounding"\cr
+^"fillin"&the extra darkness of diagonals (to be counteracted)\cr
+^"year"&the current year (e.g., 1986)\cr
+^"month"&the current month (e.g., 3 $\equiv$ March)\cr
+^"day"&the current day of the month\cr
+^"time"&the number of minutes past midnight when job started\cr
+^"charcode"&the number of the next character to be output\cr
+^"charext"&the extension code of the next character to be output\cr
+^"charwd"&the width of the next character to be output, in points\cr
+^"charht"&the height of the next character to be output, in points\cr
+^"chardp"&the depth of the next character to be output, in points\cr
+^"charic"&the italic correction of the next character, in points\cr
+^"chardx"&the device's $x$ movement for the next character, in pixels\cr
+^"chardy"&the device's $y$ movement for the next character, in pixels\cr
+^"designsize"&the approximate size of the current typeface, in points\cr
+^"hppp"&the number of horizontal pixels per point\cr
+^"vppp"&the number of vertical pixels per point\cr
+^"xoffset"&the horizontal displacement of shipped-out characters\cr
+^"yoffset"&the vertical displacement of shipped-out characters\cr
+^"boundarychar"&the right boundary character for ligatures and kerns\cr
+\enddisplay
+All of these quantities are numeric. They are initially zero at the
+start of a job, except for "year", "month", "day", and "time", which
+are initialized to the time the run began; furthermore, "boundarychar" is
+initially~$-1$. A "granularity" of zero is equivalent to $"granularity"=1$.
+A preloaded base file like plain \MF\ will usually give nonzero values to
+several other internal quantities on this list.
+
+\medbreak
+\textindent\bull
+Now we come to expressions of type @pair@, which are the second most
+important elements of \MF\ programs:
+\beginsyntax
+<pair primary>\is<pair variable>\alt<pair argument>
+ \alt[(]<numeric expression>[,]<numeric expression>[)]
+ \alt[(]<pair expression>[)]
+ \alt[begingroup]<statement list><pair expression>[endgroup]
+ \alt<numeric atom>[\char'133]<pair expression>[,]<pair expression>[\char'135]
+ \alt<scalar multiplication operator><pair primary>
+ \alt[point]<numeric expression>[of]<path primary>
+ \alt[precontrol]<numeric expression>[of]<path primary>
+ \alt[postcontrol]<numeric expression>[of]<path primary>
+ \alt[penoffset]<pair expression>[of]<pen primary>
+ \alt[penoffset]<pair expression>[of]<future pen primary>
+<pair secondary>\is<pair primary>
+ \alt<pair secondary><times or over><numeric primary>
+ \alt<numeric secondary>[*]<pair primary>
+ \alt<pair secondary><transformer>
+<transformer>\is[rotated]<numeric primary>
+ \alt[scaled]<numeric primary>
+ \alt[shifted]<pair primary>
+ \alt[slanted]<numeric primary>
+ \alt[transformed]<transform primary>
+ \alt[xscaled]<numeric primary>
+ \alt[yscaled]<numeric primary>
+ \alt[zscaled]<pair primary>
+<pair tertiary>\is<pair secondary>
+ \alt<pair tertiary><plus or minus><pair secondary>
+ \alt<path tertiary>[intersectiontimes]<path secondary>
+<pair expression>\is<pair tertiary>
+\endsyntax
+A pair is a special case of a path (namely, it's a path of length zero);
+Chapter 19 explains that \MF\ doesn't change the type from pair to path
+unless there is no other way to meet the syntax rules.
+
+\medbreak
+\textindent\bull
+Speaking of paths, they come next in our survey:
+\beginsyntax
+<path primary>\is<pair primary>\alt<path variable>\alt<path argument>
+ \alt[(]<path expression>[)]
+ \alt[begingroup]<statement list><path expression>[endgroup]
+ \alt[makepath]<pen primary>\alt[makepath]<future pen primary>
+ \alt[reverse]<path primary>
+ \alt[subpath]<pair expression>[of]<path primary>
+<path secondary>\is<pair secondary>\alt<path primary>
+ \alt<path secondary><transformer>
+<path tertiary>\is<pair tertiary>\alt<path secondary>
+<path expression>\is<pair expression>\alt<path tertiary>
+ \alt<path subexpression><direction specifier>
+ \alt<path subexpression><path join>[cycle]
+<path subexpression>\is<path expression not ending with direction specifier>\kern-5pt\null
+ \alt<path subexpression><path join><path tertiary>
+<path join>\is<direction specifier><basic path join><direction specifier>
+<direction specifier>\is<empty>
+ \alt[\char'173][curl]<numeric expression>[\char'175]
+ \alt[\char'173]<pair expression>[\char'175]
+ \alt[\char'173]<numeric expression>[,]<numeric expression>[\char'175]
+<basic path join>\is[\&]
+ \alt[..]
+ \alt[..]<tension>[..]
+ \alt[..]<controls>[..]
+<tension>\is[tension]<tension amount>
+ \alt[tension]<tension amount>[and]<tension amount>
+<tension amount>\is<numeric primary>
+ \alt[atleast]<numeric primary>
+<controls>\is[controls]<pair primary>
+ \alt[controls]<pair primary>[and]<pair primary>
+\endsyntax
+Chapter 14 tells all about path creation.
+
+\medbreak
+\textindent\bull
+Pens and future pens coexist as follows:
+\beginsyntax
+<pen primary>\is<pen variable>\alt<pen argument>
+ \alt[nullpen]
+ \alt[(]<pen expression>[)]
+ \alt[begingroup]<statement list><pen expression>[endgroup]
+<future pen primary>\is[pencircle]
+ \alt[makepen]<path primary>
+<pen secondary>\is<pen primary>
+<future pen secondary>\is<future pen primary>
+ \alt<future pen secondary><transformer>
+ \alt<pen secondary><transformer>
+<pen tertiary>\is<pen secondary>
+ \alt<future pen secondary>
+<pen expression>\is<pen tertiary>
+\endsyntax
+See Chapter 16 for a thorough discussion of pen usage.
+
+\medbreak
+\textindent\bull
+Pictures can be null, added, or subtracted:
+\beginsyntax
+<picture primary>\is<picture variable>\alt<picture argument>
+ \alt[nullpicture]
+ \alt[(]<picture expression>[)]
+ \alt[begingroup]<statement list><picture expression>[endgroup]
+ \alt<plus or minus><picture primary>
+<picture secondary>\is<picture primary>
+ \alt<picture secondary><transformer>
+<picture tertiary>\is<picture secondary>
+ \alt<picture tertiary><plus or minus><picture secondary>
+<picture expression>\is<picture tertiary>
+\endsyntax
+Chapter 13 is the definitive reference for picture operations.
+
+\medbreak
+\textindent\bull
+Strings are still fresh in our minds from Chapter 22, but we should
+repeat the syntax again for completeness here.
+\beginsyntax
+<string primary>\is<string variable>\alt<string argument>
+ \alt<string token>
+ \alt[jobname]
+ \alt[readstring]
+ \alt[(]<string expression>[)]
+ \alt[begingroup]<statement list><string expression>[endgroup]
+ \alt[str]<suffix>
+ \alt[char]<numeric primary>
+ \alt[decimal]<numeric primary>
+ \alt[substring]<pair primary>[of]<string primary>
+<string secondary>\is<string primary>
+<string tertiary>\is<string secondary>
+<string expression>\is<string tertiary>
+ \alt<string expression>[\&]<string tertiary>
+\endsyntax
+There's nothing more to say about strings.
+
+\medbreak
+\textindent\bull
+Chapter 15 explains transforms, but gives no formal syntax. The rules are:
+\beginsyntax
+<transform primary>\is<transform variable>\alt<transform argument>
+ \alt[(]<transform expression>[)]
+ \alt[begingroup]<statement list><transform expression>[endgroup]
+<transform secondary>\is<transform primary>
+ \alt<transform secondary><transformer>
+<transform tertiary>\is<transform secondary>
+<transform expression>\is<transform tertiary>
+\endsyntax
+Note that ^"identity" doesn't appear here; it is a variable defined
+in Appendix~B\null, not a primitive of the language.
+
+\medbreak
+\textindent\bull
+Finally, we come to the new kind of expression, which wasn't mentioned
+in previous chapters because it is so trivial.
+\beginsyntax
+<vacuous primary>\is<vacuous argument>
+ \alt<compound>
+ \alt[(]<vacuous expression>[)]
+ \alt[begingroup]<statement list><vacuous expression>[endgroup]
+<vacuous secondary>\is<vacuous primary>
+<vacuous tertiary>\is<vacuous secondary>
+<vacuous expression>\is<vacuous tertiary>
+\endsyntax
+A \<compound> is defined in Chapter 26.
+
+\ddangerexercise Construct minimal examples of each of the
+nine types of expression (boolean, numeric, \dots,~vacuous).
+You should use only ``^{sparks}'' in your constructions, not \<tag>
+tokens or capsules; in particular, variables are not permitted
+(otherwise this exercise would be too easy). Your expressions should
+be as short as possible in the sense of {\sl fewest tokens\/}; the number
+of keystrokes needed to type them is irrelevant.
+\answer By looking at the syntax rules, we find, for example,
+\begindisplay
+\<boolean expression>&|true|\cr
+\<numeric expression>&|0|\cr
+\<pair expression>&|(0,0)|\cr
+\<path expression>&|makepath pencircle|\cr
+\<pen expression>&|nullpen|\cr
+\<picture expression>&|nullpicture|\cr
+\<string expression>&|""|\cr
+\<transform expression>&Impossible!\cr
+\<vacuous expression>&|begingroup endgroup|\cr
+\enddisplay
+Every \<transform expression> includes either a variable or a capsule.
+Incidentally, there are some amusing alternative 5-token solutions for
+\<pair expression>:
+\begintt
+postcontrol 0 of makepath nullpen
+makepath pencircle intersectiontimes makepath nullpen
+\endtt
+
+\endchapter
+
+This is of you very well remembred,
+and well and sommaryly rehersed.
+\author THOMAS ^{MORE}, {\sl A Dialogue Concernynge Heresyes\/} (1529)
+ % Bk 2, Ch 1
+ % p178 ll C7--8 in 1557 edition, where the spelling is slightly different
+
+\bigskip
+
+Below the tomato blobs was a band of white with vertical black stripes,
+to which he could assign no meaning whatever,
+till some one else came by, murmuring:
+``What expression he gets with his foreground!''
+.\thinspace.\thinspace. %
+Ah, they were all Expressionists now, he had heard, on the Continent.
+So it was coming here too, was it?
+\author JOHN ^{GALSWORTHY}, {\sl To Let\/} (1921) % Chapter 1, p13
+
+\eject
+ \beginchapter Chapter 26. Summary of\\the Language
+
+The grand tour of \MF's syntax that was begun in the previous chapter
+is concluded in this one, so that a complete reference guide is
+available for people who need to know the details.
+\ (Another summary appears in Appendix~B.)
+
+\ninepoint\medskip
+\MF\ actually has a few features that didn't seem to be worth mentioning
+in earlier chapters, so they will be introduced here as part of our
+exhaustive survey. If there is any disagreement between something that
+was said previously and something that will be said below, the facts
+in the present chapter should be regarded as better approximations
+to the ^{truth}.
+
+We shall study \MF's digestive processes, i.e., what \MF\ does in
+response to the tokens that arrive in its ``stomach.''
+^^{anatomy of METAFONT}
+Chapter~6 describes the process by which input files are converted to
+lists of tokens in \MF's ``mouth,'' and Chapters 18--20 explain how
+expandable tokens are converted to unexpandable ones in \MF's ``gullet''
+by a process similar to regurgitation. In particular, conditions and
+loops are handled by the expansion mechanism, and we need not
+discuss them further. When unexpandable tokens
+finally reach \MF's gastro-intestinal tract, the real activities
+begin; expressions are evaluated, equations are solved, variables are
+declared, and commands are executed. In this chapter we shall discuss the
+primitive operations that actually draw pictures and produce output.
+
+Let's start by looking at the full syntax for \<program> and for
+\<statement>:
+\beginsyntax
+<program>\is<statement list>[end]\alt<statement list>[dump]
+<statement list>\is<empty>\alt<statement>[;]<statement list>
+<statement>\is<empty>\alt<title>
+ \alt<equation>\alt<assignment>
+ \alt<declaration>\alt<definition>
+ \alt<compound>\alt<command>
+<title>\is<string expression>
+<compound>\is[begingroup]<statement list><non-title statement>[endgroup]
+<command>\is<save command>
+ \alt<interim command>
+ \alt<newinternal command>
+ \alt<randomseed command>
+ \alt<let command>
+ \alt<delimiters command>
+ \alt<protection command>
+ \alt<everyjob command>
+ \alt<show command>
+ \alt<message command>
+ \alt<mode command>
+ \alt<picture command>
+ \alt<display command>
+ \alt<openwindow command>
+ \alt<shipout command>
+ \alt<special command>
+ \alt<font metric command>
+\endsyntax
+The \<empty> statement does nothing, but it is very handy because you can
+always feel safe when you put extra semicolons between statements.
+A \<title> does almost nothing, but it provides useful documentation
+as explained in Chapter~22.
+The syntax of \<equation> and \<assignment> can be found in Chapter~10;
+\<declaration> is in Chapter~7; \<definition> is in Chapters 18 and~20.
+We shall concentrate in this chapter on the various types of {\sl
+^{commands}}, especially on those that haven't been mentioned before.
+\beginsyntax
+<save command>\is[save]<symbolic token list>
+<symbolic token list>\is<symbolic token>
+ \alt<symbolic token list>[,]<symbolic token>
+<interim command>\is\kern-1.5pt[interim]%
+ <internal quantity>[:=]<right-hand side>\kern-1pt
+\endsyntax
+The @save@ and @interim@ commands cause values to be restored at the end
+of the current group, as discussed in Chapter~17.
+\beginsyntax
+<newinternal command>\is[newinternal]<symbolic token list>
+\endsyntax
+Each of the symbolic tokens specified in a @newinternal@ command will
+henceforth behave exactly as an \<internal quantity>, initially zero.
+Thus, they can be used in @interim@ commands; they are ^{tags} but not
+^{external tags} (see Chapter~7). Since \MF\ can access internal
+quantities quickly, you can use them to gain efficiency.
+\beginsyntax
+<randomseed command>\is[randomseed][:=]<numeric expression>
+\endsyntax
+The @randomseed@ command specifies a ``seed'' value that defines
+the pseudo-random numbers to be delivered by
+`uniformdeviate' and `normaldeviate' (cf.~Chapter~21).
+The default value, if you don't specify your own seed, is
+^^"day" ^^"time" $"day"+"time"\ast"epsilon"$.
+\beginsyntax
+<let command>\is[let]<symbolic token><is><symbolic token>
+<is>\is[=]\alt[:=]
+\endsyntax
+The @let@ command changes the current meaning of the left-hand token
+to the current meaning of the right-hand token. For example,
+after `@let@ $"diamonds"=@forever@$', the token "diamonds" will
+introduce loops. If the left-hand token was the first token of
+any variable names, those variables all disappear. If the right-hand
+token was the first token in any variable names, those variables
+remain unchanged, and the left-hand token becomes
+an unknown, independent variable. \ (The purpose of @let@ is to redefine
+primitive meanings or macro meanings, not to equate variables in any way.)
+\ If the right-hand symbol is one of a pair of matching delimiters,
+the subsequent behavior of the left-hand symbol is undefined.
+For example, it's a bad idea to say `@let@~$[\,[=($;~@let@~$]\,]=)$'.
+\beginsyntax
+<delimiters command>\is[delimiters]<symbolic token><symbolic token>
+\endsyntax
+The @delimiters@ command gives new meanings to the two symbolic tokens;
+henceforth they will match each other (and only each other). For example,
+Appendix~B says `@delimiters@~()'; without this command, parentheses
+would be ordinary symbolic tokens. Any distinct symbolic tokens can be
+defined to act as delimiters, and many different pairs of delimiters
+can be in use simultaneously.
+\beginsyntax
+<protection command>\is[outer]<symbolic token list>
+ \alt[inner]<symbolic token list>
+\endsyntax
+A ``^{forbidden}'' stamp is added to or removed from symbolic tokens
+by an @outer@ or @inner@ command, without changing the essential meanings
+of those tokens. A token that has been called @outer@ should not appear
+when \MF\ is skipping over tokens at high speed; the program will stop
+and insert an appropriate delimiter, if an @outer@ token is sensed in
+the wrong place, since such tokens are supposed to occur only at
+``quiet'' times. \ (Unquiet times occur when \MF\ is skipping tokens
+because of a false ^{condition}, or because it is reading the ^{replacement
+text} of a macro or the ^{loop text} of a loop, or because it is scanning
+the ^{text argument} to a macro, or because it is ^{flushing} erroneous
+tokens that were found at the end of a statement.) \ Without such
+protection, a missing right delimiter could cause \MF\ to eat up your
+whole program before any error was detected; @outer@ tokens keep such
+errors localized. An @inner@ command undoes the effect of @outer@; so
+does `@let@', and so does any other command or definition that changes the
+meaning of a symbolic token. All tokens are initially @inner@.
+\beginsyntax
+<everyjob command>\is[everyjob]<symbolic token>
+\endsyntax
+The command `@everyjob@$\,S$' tells \MF\ that token $S$ should be inserted
+first, just before the input file is read, when a job starts. \ (This
+is meaningful only in a base file that will be loaded or preloaded
+at the beginning of a run; it is analogous to \TeX's |\everyjob| command.)
+\beginsyntax
+<show command>\is[show]<expression list>
+ \alt[showvariable]<symbolic token list>
+ \alt[showtoken]<symbolic token list>
+ \alt[showdependencies]
+ \alt[showstats]
+\endsyntax
+A simple @show@ command displays the value of each expression, in turn.
+Paths, pens, and pictures are shown only in the transcript file, unless
+^"tracingonline" is positive. The @showvariable@ command gives the
+structure of all variables that begin with a given external tag,
+together with their values in an abbreviated form; this allows you to see
+which of its subscripts and attributes have occurred. For example, if you're
+using plain \MF\ conventions, `@showvariable@~$x,y$' will show all
+coordinates that have been defined since the last @beginchar@. The @showtoken@
+command gives the current meaning of a token, so that you can tell whether
+it is primitive or not, @outer@ or not. (If @showvariable@ is applied to
+a spark instead of a tag, it gives the same information as @showtoken@.)
+\ Every unknown numeric variable that's currently dependent is shown by
+@showdependencies@ (except that unknown capsules are shown only
+when ^"tracingcapsules" is positive). And finally, @showstats@ gives
+information about \MF's current memory usage.
+Each of these commands will stop and say `|!|~^|OK.|', if the internal
+quantity "showstopping" has a positive value; this gives you a chance
+to enter more @show@ commands ^{interactive}ly, while you're trying to
+debug a program.
+\beginsyntax
+<message command>\is<message op><string expression>
+<message op>\is[message]\alt[errmessage]\alt[errhelp]
+\endsyntax
+Communication with the user is possible via @message@, @errmessage@,
+and @errhelp@, as discussed in Chapter~22.
+\beginsyntax
+<mode command>\is[batchmode]\alt[nonstopmode]
+ \alt[scrollmode]\alt[errorstopmode]
+\endsyntax
+The four ``mode commands'' control the amount of interaction during error
+recovery, just as in~\TeX. A job starts in @errorstopmode@, and you can
+also resurrect this mode by ^{interrupting} \MF; @scrollmode@,
+@nonstopmode@, and @batchmode@ are the modes you get into by hitting
+`|S|', `|R|', or `|Q|', respectively, in response to error messages
+(cf.~Chapter~5).
+\beginsyntax
+<picture command>\is<addto command>\alt<cull command>
+<addto command>\is[addto]<picture variable>[also]<picture expression>
+ \alt[addto]<picture variable>[contour]<path expression><with list>
+ \alt[addto]<picture variable>[doublepath]<path expression><with list>
+<with list>\is<empty>\alt<with list><with clause>
+<with clause>\is[withpen]<pen expression>%
+ \alt[withweight]<numeric expression>\kern-3.5pt
+<cull command>\is[cull]<picture variable><keep or drop><pair expression>
+ \alt<cull command>[withweight]<numeric expression>
+<keep or drop>\is[keeping]\alt[dropping]
+\endsyntax
+The @addto@ and @cull@ commands are the principal means of making
+changes to pictures; they are discussed fully in Chapter~13.
+\beginsyntax
+<display command>\is[display]<picture variable>[inwindow]<window>
+<window>\is<numeric expression>
+<openwindow command>\is[openwindow]<window><window spec>
+<window spec>\is<screen place>[at]<pair expression>
+<screen place>\is[from]<screen coordinates>[to]<screen coordinates>
+<screen coordinates>\is<pair expression>
+\endsyntax
+Chapter~23 explains how to display stuff on your screen via @display@
+and @openwindow@\kern-1pt.
+\beginsyntax
+<shipout command>\is[shipout]<picture expression>
+\endsyntax
+You may have wondered how \MF\ actually gets pictorial information into
+a font. Here at last is the answer: `@shipout@~$v$' puts the pixels of
+positive weight, as defined by the picture expression~$v$, into a ^{generic
+font} output file, where they will be the bitmap image associated with
+character number $"charcode"\bmod256+"charext"\ast256$. The pixels of~$v$
+are shifted by $("xoffset","yoffset")$ as they are shipped out.
+\ (However, no output is done if ^"proofing"$\null<0$. The values of
+^"xoffset", ^"yoffset", ^"charcode", and ^"charext" are first rounded to
+integers, if necessary.) \ This command also saves the values of
+^"charwd", ^"charht", ^"chardp", ^"charic", ^"chardx", and "chardy"; they
+will be associated with the current "charcode" when ^{font metric
+information} is produced. \ (See Appendices F and~G for the basic
+principles of font metric information and generic font files.)
+\beginsyntax
+<special command>\is[special]<string expression>
+ \alt[numspecial]<numeric expression>
+\endsyntax
+The @special@ and @numspecial@ commands send alphabetic and numeric
+information
+to the generic font output file, if "proofing" is nonnegative.
+For example, the labels on proofsheets are specified in this
+way by macros of plain \MF\!\null. Appendices G and~H provide further details.
+
+\medbreak
+We have now discussed every kind of command but one; and the remaining
+one is even more special than the \<special command>, so we had better
+defer its discussion to an appendix. Appendix~F will complete the syntax
+by defining \<font metric command>. For now, we merely need to know that
+font metric commands specify fussy font facts; examples are the kerning and
+`@font\_normal\_space@' statements in the \MF\ logo program of Chapter~11.
+
+\medbreak
+And there's one more loose end to tie up, going back to the very
+first syntax rule in this chapter: The token `^@dump@' can be
+substituted for `^@end@', if a special version of \MF\ called
+`^|INIMF|' is being used. This writes a file containing the macros
+defined so far, together with the current values of variables and
+the current meanings of symbolic tokens, so
+that they can be loaded as a base file. \ (It is analogous to
+\TeX's |\dump| command.) \ Base files are discussed at the end of
+Appendix~B.
+
+\ddangerexercise Run \MF\ with the input ^^@outer@ ^^@delimiters@ ^^@showtoken@
+\begintt
+\newinternal a;
+let b=a; outer a,b,c;
+let c=b; delimiters a::;
+showtoken a,b,c; end
+\endtt
+and explain the computer's responses.
+\answer The responses are
+\begintt
+> a=left delimiter that matches ::
+> b=(outer) a
+> c=a
+\endtt
+because: $a$ has been redefined from internal quantity to delimiter;
+$b$~is still an internal quantity (named~$a$), and it has been stamped
+@outer@; $c$~denotes the same internal quantity, but it hasn't got outerness.
+
+\endchapter
+
+Our life is frittered away by detail.
+An honest man has hardly need
+to count more than his ten fingers,
+or in extreme cases he may add his ten toes,
+and lump the rest. Simplicity, simplicity, simplicity!
+I say, let your affairs be as two or three,
+and not a hundred or a thousand .\thinspace.\thinspace.
+Simplify, simplify.
+\author HENRY DAVID ^{THOREAU}, {\sl Walden\/} (1854) % 1st ed, ch2, graf15
+
+\bigskip
+
+The awesome memory of thy ever attentive computer
+accepts all words as ^{truth}.
+Think, therefore, in analytical, modular steps,
+for the truth or untruth spoken through thy fingertips
+will be acted upon unerringly.
+\author HERMANN ^{ZAPF}, {\sl The Ten Commandments of %
+ Photo\kern1pt-\kern-1ptTypesetting\/} (1982) % 2nd Commandment
+
+\eject
+ \beginchapter Chapter 27. Recovery\\from\\Errors
+
+OK, everything you need to know about \MF\ has been explained---unless you
+happen to be fallible. If you don't plan to make any errors, don't bother to
+read this chapter. Otherwise you might find it helpful to make use of some
+of the ways that \MF\ tries to pinpoint bugs in your programs.
+
+In the trial runs you did when reading Chapter 5, you learned the general
+form of ^{error messages}, and you also learned the various ways in which
+you can respond to \MF's complaints. With practice, you will be able to
+correct most errors ``online,'' as soon as \MF\ has detected them, by
+inserting and deleting a few things. On the other hand, some errors are
+more devastating than others; one error might cause some other perfectly
+valid construction to be loused~up. Furthermore, \MF\ doesn't always
+diagnose your errors correctly, since the number of ways to misunderstand
+the rules is vast; \MF\ is a rather simple-minded computer program that
+doesn't readily comprehend the human point of view. In fact, there will be times
+when you and \MF\ disagree about something that you feel makes perfectly
+good sense. This chapter tries to help avoid a breakdown in communication
+by explaining how to learn \MF's reasons for its actions.
+
+Ideally you'll be in a mellow mood when you approach \MF\!, and you will
+regard any error
+messages as amusing puzzles---``Why did the machine do
+that?''---rather than as personal insults.
+\MF\ knows how to issue more than a hundred different sorts of error messages,
+and you probably never will encounter all of them, because some types of
+mistakes are very hard to make.
+
+Let's go back to the `^|badio.mf|' example file of Chapter~5, since it
+has more to teach us. If you have a better memory than the author, you'll
+recall that the first error message was
+\begintt
+>> mode.setup
+! Isolated expression.
+<to be read again>
+ ;
+l.1 mode setup;
+ % an intentional error!
+?
+\endtt
+In Chapter 5 we just charged ahead at this point, but it would be more
+^^{!} ^^{to be read again}
+normal for a mature \MF er to think ``Shucks, I meant to type
+`|mode_setup|', but I forgot the underscore. Luckily this didn't cause
+any harm; \MF\ just found an ^{isolated expression}, `"mode.setup"', which
+it will ignore. So let me now insert the correct command, `@mode\_setup@'.''
+
+Good thinking; so you type `|I| |mode_setup|', right? Wrong~$\ldots$~sorry.
+Lots of error messages occur before \MF\ has read a ^{semicolon} in
+preparation for another ^{statement}; the important clue in this case
+comes from the two lines
+\begintt
+<to be read again>
+ ;
+\endtt
+which tell us that the semicolon is still pending. So the correct
+response would have been to type `|I;|~|mode_setup|' instead. Without
+the semicolon, you get what appears at first to be a horrible mess:
+\begintt
+! Extra tokens will be flushed.
+<to be read again>
+ warningcheck
+mode_setup->warningcheck
+ :=0;if.unknown.mode:mode=proof;fi...
+<insert> mode_setup
+|quad
+<to be read again>
+ ;
+l.1 mode setup;
+ % an intentional error!
+?
+\endtt
+But relax, there's a simple way out. The help message says
+^^|Extra tokens will be flushed| ^^{flushing}
+`Please insert a ^{semicolon} now in front of anything that you
+don't want me to delete'; all you have to do is type `|I;|' and
+the net effect will be the same as if you had correctly inserted a semicolon
+before |mode_setup| in the first place.
+
+The moral of this story is: {\sl When you insert a new statement during
+error recovery, you frequently need to put a semicolon just ahead of~it.}
+But if you forget, \MF\ gives you another chance.
+
+After proceeding through |badio| with the interactions suggested in
+^^|Undefined coordinate| ^^{misspelling} ^^{typographic errors}
+Chap\-ter~5, we will come again to the error
+\begintt
+>> 0.08682thinn+144
+! Undefined x coordinate has been replaced by 0.
+\endtt
+(This is where the erroneous `|thinn|' was detected.) \ The help message for
+this error has some curious advice:
+\begintt
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+\endtt
+Chapter 27? That's us! What happens if we do type `|I ???|' now? We get
+\begintt
+x4l=0.08682thinn+144
+y4=-0.4924thinn+259.0005
+x4r=-0.08682thinn+144
+y4r=-0.9848thinn+259.0005
+! OK.
+\endtt
+It is now abundantly clear that `|thin|' was misspelled. Plain \MF\
+defines `^|???|' to be a macro that shows all of the current
+dependencies between numeric variables and stops with `^|OK|';
+this is useful because a badly typed variable name might have become a
+^{dependent variable} instead of an ^{independent variable}, in which
+case it would be revealed by `|???|' but not by the error message.
+
+One more example of online error correction should suffice to make
+the general strategy clear. Suppose you accidentally type square brackets
+instead of parentheses; the computer will scream:
+\begintt
+! A primary expression can't begin with `['.
+<inserted text>
+ 0
+<to be read again>
+ [
+<*> show round[
+ 1 + sqrt43];
+?
+\endtt
+(By coincidence, the help message for this particular error also refers to
+Chapter~27.) \ When \MF\ needs to see an expression, because of the tokens
+it has already digested, it will try to insert `|0|' in order to keep going.
+In this case we can see that zero isn't what we intended; so we type
+`|7|' to delete the next seven tokens, and the computer comes back with
+\begintt
+<*> show round[1 + sqrt43]
+ ;
+?
+\endtt
+Now `|I (1 + sqrt43)|' will insert the correct formula, and the program will
+be able to continue happily as if there were no mistake.
+
+\exercise Why was `|7|' the right number of tokens to delete?
+\answer We want to delete
+\begindisplay
+\ttok{0}\quad\ttok{[}\quad\ttok{1}\quad\ttok{+}\quad\ttok{sqrt}\quad
+\ttok{43}\quad\ttok{]}
+\enddisplay
+from the sequence of tokens that \MF\ is about to read next, in order to
+get rid of the right bracket, which we can see is going to be just as
+erroneous as the left bracket was. However, there is another way to
+proceed (and indeed, this alternative would be preferable to counting
+tokens, if the bracketed expression were longer): We could simply
+^^{delimiter} ^^|Missing token has been inserted|
+delete 2~tokens, then `|I(|'. This would produce another error stop,
+\begintt
+! Missing `)' has been inserted.
+<to be read again>
+ ]
+<*> show round[1 + sqrt43]
+ ;
+? h
+I found no right delimiter to match a left one. So I've
+put one in, behind the scenes; this may fix the problem.
+?
+\endtt
+after which it's easy to delete the `|]|' and continue successfully.
+
+\dangerexercise If the user hadn't deleted or inserted anything, but had
+just plunged ahead, \MF\ would have come up with another error:
+\begintt
+>> 0
+! Extra tokens will be flushed.
+<to be read again>
+ [
+<to be read again>
+ (7.55743)
+<to be read again>
+ ]
+<*> show round[1 + sqrt43]
+ ;
+?
+\endtt
+Explain what happened. What should be done next?
+\answer \MF\ looked ahead, to see if the expression being evaluated
+was going to be something like `|round 0[1+sqrt43,x]|'. But when it
+found no comma, it put back several tokens so that they could be
+read again. \ (The subexpression |1+sqrt43| had already been evaluated,
+so a ``^{capsule}'' for its value, 7.55743, was inserted among the
+tokens to be reread.) \ The expression ended with `0', and `round~0' was
+shown. Then \MF\ found extra tokens following the @show@ command; a
+semicolon should have come next. To continue, the user should just plunge
+ahead recklessly once again, letting \MF\ delete those unwanted tokens.
+
+It's wise to remember that the first error in your program may well spawn
+spurious ``errors'' later on, because anomalous commands can inflict
+serious injury on \MF's ability to cope with the subsequent material.
+But most of the time you will find that a single run through the
+machine will locate all of the places in which your input conflicts
+with \MF's rules.
+
+\danger Sometimes an error is so bad that \MF\ is forced to quit
+prematurely. For example, if you are running in ^@batchmode@ or
+^@nonstopmode@, \MF\ makes an ``^{emergency stop}'' if it needs
+input from the terminal; this happens when a necessary file can't
+be opened, or when no ^@end@ was found in the input.
+Here are some of the messages you might get just before
+\MF\ gives up the ghost: \enddanger
+
+{\ninepoint
+\def\fatal#1. {\medbreak{\tt#1.}\par\nobreak\smallskip\noindent\ignorespaces}
+\fatal
+Fatal base file error; I'm stymied.
+^^|Fatal base file error|
+This means that the preloaded base you have specified cannot be used,
+because it was prepared for a different version of \MF\!.
+\fatal
+That makes 100 errors; please try again.
+\MF\ has scrolled past 100 errors since the last statement ended, so
+it's probably in an~endless ^{loop}. ^^{infinite loop}
+\fatal
+I can't go on meeting you like this.
+^^|I can't go on|
+A previous error has gotten \MF\ out of whack. Fix it and try again.
+\fatal
+This can't happen.
+^^|This can't happen|
+Something is wrong with the \MF\ you are using. Complain fiercely.
+\goodbreak}
+
+\danger There's also a dreadful message that \MF\ issues only with
+great reluctance. But it can happen:
+\begintt
+METAFONT capacity exceeded, sorry.
+\endtt
+^^|METAFONT capacity exceeded|
+This, alas, means that you have tried to stretch \MF\ too far. The
+message will tell you what part of \MF's memory has become overloaded;
+one of the following nineteen things will be mentioned:
+\begindisplay
+|number of strings|\qquad(strings and names of symbolic tokens and files)\cr
+|pool size|\qquad(the characters in such strings)\cr
+|main memory size|\qquad(pairs, paths, pens, pictures, token lists,
+ transforms, etc.)\cr
+|hash size|\qquad(symbolic token names)\cr
+|input stack size|\qquad(simultaneous input sources)\cr
+|number of internals|\qquad(internal quantities)\cr
+|rounding table size|\qquad(transitions between octants in cycles)\cr
+|parameter stack size|\qquad(macro parameters)\cr
+|buffer size|\qquad(characters in lines being read from files)\cr
+|text input levels|\qquad(@input@ files and error insertions)\cr
+|path size|\qquad(key points per path)\cr
+|move table size|\qquad(rows of picture being simultaneously accessed)\cr
+|pen polygon size|\qquad(pen offsets per octant)\cr
+|ligtable size|\qquad(accumulated @ligtable@ instructions)\cr
+|kern|\qquad(distinct kern amounts)\cr
+|extensible|\qquad(built-up characters)\cr
+|headerbyte|\qquad(largest @headerbyte@ address)\cr
+|fontdimen|\qquad(largest @fontdimen@ address)\cr
+|independent variables|\qquad(distinct numeric variables)\cr
+\enddisplay
+The current amount of memory available will also be shown.
+
+\danger If you have a job that doesn't overflow \MF's capacity, yet
+you want to see just how closely you have approached the limits,
+just set ^"tracingstats" to a positive value before the end of your
+job. The log file will then conclude with a report on your actual
+usage of the first nine things named above (i.e., the number of strings,
+\dots, the buffer size), in that order. ^^{stack positions}
+Furthermore, the @showstats@ command can be used to discover the current
+string memory and main ^{memory usage} at any time during a run.
+The main memory statistics are broken into two
+parts; `|490&5950|' means, for example, that 490 words are being used
+for ``large'' things like pens, capsules, and
+transforms, while 5950 words are being used for ``small'' things like
+tokens and edges.
+
+\danger What can be done if \MF's capacity is exceeded? All of the
+above-listed components of the capacity can be increased, except the memory
+for kerns and extensible characters, provided
+that your computer is large enough; in fact, the space necessary to
+increase one component can usually be obtained by decreasing some
+other component, without increasing the total size of \MF\!\null.
+If you have an especially important application, you may be able
+to convince your local system people to provide you with a special
+\MF\ whose capacities have been hand-tailored to your needs.
+But before taking such a drastic step, be sure that you are using
+\MF\ properly. If you have specified a gigantic picture that has
+lots of transitions between black and white pixels, you should
+change your approach, because \MF\ has to remember every change between
+adjacent pixel values in every currently accessible picture.
+If you keep saving different pens, you might be wasting memory as
+discussed in Chapter~16. If you have built up an enormous macro library,
+you should realize that \MF\ has to remember all of the replacement texts
+that you define; therefore if memory space is in short supply, you should
+load only the macros that you need.
+
+\danger Some erroneous \MF\ programs will overflow any finite
+memory capacity. For example, after `|def recurse=(recurse)enddef|', the
+^^{recursion} use of |recurse| will immediately bomb out:
+\begintt
+! METAFONT capacity exceeded, sorry [input stack size=30].
+recurse->(recurse
+ )
+recurse->(recurse
+ )
+recurse->(recurse
+ )
+...
+\endtt
+The same sort of error will obviously occur no matter how much you increase
+\MF's input stack size.
+
+\danger Most implementations of \MF\ allow you to ^{interrupt} the program
+in some way. This makes it possible to diagnose the causes of ^{infinite
+loops}, if the machine doesn't stop because of memory limitations.
+\MF\ switches to ^@errorstopmode@ when interrupted; hence
+you have a chance to insert commands into the input: You can abort the
+run, or you can ^@show@ or change the current contents of variables,
+etc. In such cases you will probably want to ``^{hide}'' your diagnostic
+commands, for example by typing
+\begintt
+I hide(showstopping:=1; alpha:=2; show x)
+\endtt
+so that you don't mess up the expression \MF\ is currently evaluating.
+Interruption can also give you a feeling for where \MF\ is spending most
+of its time, if you happen to be using an inefficient macro, since random
+interrupts will tend to occur in whatever place \MF\ visits most often.
+
+\danger \MF's second most frustrating error messages are its occasional
+claims that you have ``^{strange}'' paths. Sometimes a glance at your
+output will make it clear that you did indeed specify a path that
+crossed over itself, something like a figure-8; but sometimes a path
+that looks fine to you will be rejected by the computer. In such
+cases you need to decipher \MF's octant codes, which look scary at
+first although they turn out to be helpful when you get used to them.
+For example, let's reconsider |branch4| of ^{El Palo Alto}, from
+the program in Chapter~14:
+\begintt
+branch4=
+ flex((0,509),(-14,492),(-32,481))
+ &flex((-32,481),(-42,455),(-62,430))
+ &flex((-62,430),(-20,450),(42,448))
+ &flex((42,448),(38,465),(4,493),(0,509))
+ &cycle;
+\endtt
+If the number |450| in the third ^{flex} had been |452| instead,
+\MF\ would have stopped and told you this:
+\begintt
+> 0 SSW WSW 1 2 SSW 3 WSW 4 (WNW NNW) NNE ENE 5 ESE 6 (ENE)
+ NNE NNW 7 WNW NNW 8 NNE 0 (NNW WNW WSW)
+! Strange path (turning number is zero).
+<to be read again>
+ ;
+<for(4)> ...]shifted(150,50)scaled(w/300);
+ ENDFOR
+p.4,l.94 endfor
+ endchar;
+?
+\endtt
+The `|for(4)|' in the fifth-last line implies that |branch4| is
+at fault, because it says that the ^@for@ loop index is~4;
+but the ^{octant} codes like `^|SSW|' are your only clues about why
+|branch4| is considered strange. \ (A simpler example appeared
+in Chapter~13, which you might want to review now.) \
+^^{compass directions} ^^|SSE|^^|ESE|^^|WSW|^^|WNW|^^|NNE|^^|NNW|^^|ENE|
+You probably also have a proofmode diagram:
+\displayfig 27a (34mm)
+Starting at time~0, and at the point $(0,509)$, the path goes South by
+Southwest, then West by Southwest until time~2 (the end of the first flex).
+Then it goes |SSW| again, and |WSW| again (that's the second flex).
+But at time~4, the path makes a sharp turn through the directions
+|WNW| and |NNW|, {\sl without moving\/} (because these octant codes are in
+parentheses). Aha! That's where the path was supposed to turn
+^{counterclockwise}, through |SSW| and~|SSE| and~|ESE|; \MF\ turned clockwise
+because it was the shortest way to go. The path actually makes a little
+loop at time~4, between the end of the second flex and the beginning of
+the third. Therefore its turning number is indeed zero, and the path is
+strange by definition.
+
+\dangerexercise At what point do the second and third flexes cross,
+in this example?
+\answer The little program
+\begintt
+path p,q; p=flex((-32,481),(-42,455),(-62,430));
+q=flex((-62,430),(-20,452),(42,448));
+show p intersectiontimes q, p intersectionpoint q,
+ angle -direction 2 of p, angle direction 0 of q; end
+\endtt
+gives the following results:
+\begintt
+>> (1.88403,0.07692)
+>> (-59.32149,432.59523)
+>> 43.14589
+>> 45.47263
+\endtt
+(Actually, the paths would also cross if |452| were |451|, but
+it's such a close call that \MF\ doesn't call the path strange;
+\MF\ prefers to turn ^{counterclockwise} when the amount of turn
+is close enough to $180^\circ$, even if it's slightly more.)
+
+\danger There are three main ways to avoid problems with strange paths.
+One is to stay away from paths that turn so abruptly. Or you can displace the
+paths by "epsilon", as in the serif example at the end of Chapter~16.
+\ (Displacing by ^"eps" would be even safer.) \ Or you can discipline
+yourself to fill all cycles counterclockwise, so that you can set
+^"turningcheck"$\null:=0$; this means that \MF\ won't check for
+strange paths, but that's OK because tiny little loops won't hurt anything
+if you are filling cycles in the correct direction.
+
+\ddanger Sometimes the octant codes of a strange path are shown backwards,
+because the system may have tried to reverse the path to get rid of
+its strangeness.
+
+Sooner or later---hopefully sooner---you'll get \MF\ to process your
+whole file without stopping once to complain. But maybe the output
+still won't be right; the mere fact that \MF\ didn't stop doesn't mean
+that you can avoid looking at proofsheets. At this stage it's usually easy to
+see how to fix typographic errors by correcting the input; hardcopy proofs
+such as those discussed in Appendix~H usually clear up obvious mistakes,
+especially if you have remembered to label the key points in your constructions.
+
+But your output may contain seemingly inexplicable errors.
+If you can't find out what went wrong, try the old trick of simplifying
+your program: Remove all the things that do work, until you obtain
+the shortest possible input file that fails in the same way as the
+original. The shorter the file, the easier it will be for you or somebody
+else to pinpoint the problem.
+
+\danger One of the important tricks for shortening a buggy program is to
+assign a positive value to ^"tracingspecs", because this will put all the
+key points and control points of a problematic path into your log file. \
+(See the example at the end of Chapter~24, ``before subdivision.'') \ If
+something is wrong with the treatment of some path, you can copy the
+path's description from the log file and use it directly in \MF\ input,
+thereby avoiding all the complexity of equations that might have been
+involved in that path's original creation.
+
+\danger We've just talked about "tracingstats" and "tracingspecs";
+\MF\ is able to produce lots of other kinds of tracing. For example,
+Chapter~22 discusses ^"tracingtitles",
+Chapter~18 discusses ^"tracingmacros", Chapter~17 discusses
+^"tracingrestores", and Chapter~9 discusses ^"tracingequations".
+You can also invoke ^"tracingchoices", which shows all paths before and
+after their ^{control points} are chosen according to the rules
+in Chapter~14; or ^"tracingpens", which shows the pen polygons that
+arise when a future pen becomes a full-fledged @pen@; or ^"tracingoutput",
+which shows every picture that's shipped out, using edge-transitions
+to represent the pixel values as illustrated in Chapter~13. Each of
+these types of tracing is enabled by assigning a positive value to the
+corresponding internal quantity; for example, you can simply set
+$"tracingpens":=1$ (or~^@interim@ $"tracingpens":=1$)
+if you want the data about pens.
+
+\danger If ^"tracingcommands"$\null=1$, \MF\ shows every ^{command}
+just before it is carried out. If $"tracingcommands"=2$, \MF\ also shows
+every ^{ex\-pand\-able token} just before it is expanded (except that
+macros are separate, they're traced only when $"tracingmacros">0$). And if
+$"tracingcommands"=3$, \MF\ also shows every ^{algebraic} ^{operation}
+just before it is evaluated. Thus you can get ``stream of
+consciousness'' information about everything \MF\ is doing.
+
+\begingroup\ninepoint
+\danger ^{Digitized output} can be monitored by setting ^"tracingedges"%
+$\null=1$. For example, if we ask \MF\ to draw the Ionian `{\manual\IOO}'
+of Chapter~5 at a resolution of 100~pixels per inch (^"lowres" mode
+with ^"mag"$\null=.5$), "tracingedges" will report as follows:\enddanger
+\beginlines
+|Tracing edges at line 15: (weight 1)|
+|(1,5)(1,2)(2,2)(2,1)(3,1)(3,0)(8,0)(8,1)(9,1)(9,2)(10,2)(10,8)(9,8)|
+|(9,9)(8,9)(8,10)(3,10)(3,9)(2,9)(2,8)(1,8)(1,5).|
+\smallskip
+|Tracing edges at line 15: (weight -1)|
+|(3,5)(3,2)(4,2)(4,1)(7,1)(7,2)(8,2)(8,8)(7,8)(7,9)(4,9)(4,8)(3,8)(3,5).|
+\endlines
+By following these edges (and negating their weights on the inner boundary)
+we find that the character at this low resolution is symmetric:
+\begindisplay
+\vbox{\offinterlineskip\manual\halign{#\hfil\cr
+SSSRRRRRSSS\cr
+SSRRSSSRRSS\cr
+SRRSSSSSRRS\cr
+SRRSSSSSRRS\cr
+SRRSSSSSRRS\cr
+SRRSSSSSRRS\cr
+SRRSSSSSRRS\cr
+SRRSSSSSRRS\cr
+SSRRSSSRRSS\cr
+SSSRRRRRSSS\cr}}
+\enddisplay
+
+\endgroup
+\ddanger Further information about digitization comes out when
+$"tracingedges">1$, if fixed pens are used to ^@draw@ or ^@filldraw@ a
+shape. In this case detailed information is presented about the activity
+in each ^{octant} direction; straight line ``^{transition}'' edges are
+also reported whenever \MF\ changes from one ^{penoffset} to another.
+
+\ddanger The "tracing"$\ldots$ commands put all of their output into your log
+file, unless the ^"tracingonline" parameter is positive; in the latter
+case, all diagnostic information goes to the terminal as well as to the
+log file. Plain \MF\ has a ^@tracingall@ macro that turns on the
+maximum amount of tracing of all kinds. It not only sets~up
+"tracingcommands", "tracingedges", "tracingspecs", and so on,
+it also sets $"tracingonline":=1$, and it sets ^"showstopping"$\null:=1$ so
+that you can do interactive debugging via ^@show@ commands. This is the works.
+There's also ^@loggingall@, which is like @tracingall@ except that it
+doesn't touch "tracingonline" or "showstopping". You can say ^@interact@
+if you want just $"tracingonline":="showstopping":=1$. Finally, there's
+^@tracingnone@, which shuts off every form of tracing after you've had enough.
+
+\ddanger Some production versions of \MF\ have been streamlined for
+speed. These implementations don't look at the value of ^"tracingstats",
+nor do you get extra information when $"tracingedges">1$,
+because \MF\ runs faster when it doesn't have
+to maintain statistics or keep tabs on whether tracing is required.
+If you want all of \MF's diagnostic tools, you should be sure to
+use the right version.
+
+\ddanger If you set ^"pausing"$\null:=1$, \MF\ will give you a chance to edit
+each line of input as it is read from the file. In this way you can
+make temporary patches (e.g., you can insert @show@$\ldots$ commands)
+while troubleshooting, without changing the actual contents
+of the file, and you can keep \MF\ running at human speed.
+
+Final hint: When working on a large font, it's best to prepare
+only a few characters at a time. Set up a ``{test}'' file and a ``{master}''
+file, and do your work in the test file. \ (Appendix~E suggests a
+convenient way to prepare control files that supply parameters to individual
+test characters as well as to the whole font.) \
+After the characters come out looking right, you can append them to the
+master file; and you can run the master file through \MF\ occasionally,
+in order to see how the font is shaping up. Characters can always be
+moved back to the test file if you have to fix some unexpected problems.
+
+\ddangerexercise Final exercise: Find all of the ^{lies} in this
+manual, and all of the ^{jokes}.
+\answer If this exercise isn't just a joke, the title of this
+appendix is a lie. \ (When you've solved this exercise you might also
+try to find all the lies and/or jokes that are the same in both
+this book and {\sl The \TeX book}.)
+
+\line{Final exhortation: G{\sc O} {\sc FORTH} now and create
+{\sl masterpieces of digital typography!\/}}
+
+\endchapter
+
+% Advierto tambien que en quanto \^a los rumbos del camino
+With respect to the directions of the route
+% puedo haver tenido alguna equivocacion.
+I may have made some errors.
+\author FRAY PEDRO ^{FONT}, {\sl Diary\/} (1776)
+ % opening remarks
+
+\bigskip
+
+The road to wisdom? Well, it's plain
+and simple to express:
+\tabskip\centering\halign to\hsize{#\hfil\tabskip=0pt\cr%
+ Err\cr%
+ and err\cr%
+ and err again\cr%
+ but less\cr%
+ and less\cr%
+ and less.\cr}%
+\author PIET ^{HEIN}, {\sl Grooks\/} (1966) % p34
+
+\eject
+ \beginchapter Appendix A. Answers to\\All the\\Exercises
+
+The preface to this manual points out the wisdom of trying to figure out
+each exercise before you look up the answer here. But these answers are intended
+to be read, since they occasionally provide additional information that
+you are best equipped to understand when you have just worked on a problem.
+
+\immediate\closeout\ans % this makes the answers file ready
+\ninepoint
+\input answers
+
+\endchapter
+
+Looke into this Businesse thorowly,
+And call these foule Offendors to their Answeres.
+\author WILLIAM ^{SHAKESPEARE}, %
+ {\sl Second Part of Henry the Sixth\/} (1594) % Act 2 Sc 1 ll 198--199
+
+\bigskip
+
+If you can't solve a problem,
+you can always look up the answer.
+But please, try first to solve it by yourself;
+then you'll learn more and you'll learn faster.
+\author DONALD E. ^{KNUTH}, {\sl The %
+ {\manual \char`\\]\char`\^\char`\_efg\char`\^}\kern1ptbook\/} (1986)
+
+\eject
+ \beginchapter Appendix B. Basic\\Operations
+
+This appendix defines the macros of the plain \MF\ base. Let's begin
+^^{table, useful}
+with an informal ^{inventory} of all the features that are available.
+
+\def\bb{$\,\left\{\vcenter\bgroup\halign\bgroup\hfil##\hfil\cr}
+\def\ee{\crcr\egroup\egroup\right\}\,$}
+\def\\{\hfil\break}
+\begingroup\lineskip=3pt plus .5pt
+
+\medbreak\textindent\bull {\it ^{Boolean} things:\/} \
+|true|, |false|; \ \ \bb|known|\cr|unknown|\cr|cycle|\ee\<expression>;\\
+\lower2pt\vbox to 7pt{}%
+\smash{\raise3pt\hbox{{\tt odd} \<numeric>; \ \ {\tt charexists} \<numeric>;}}\\
+\bb|boolean|\cr|numeric|\cr|pair|\cr|path|\cr
+|pen|\cr|picture|\cr|string|\cr|transform|\ee\<expression>; \
+\bb\<boolean>\cr\<numeric>\cr\<pair>\cr\<string>\cr\<transform>\ee
+ \bb|<|\cr|<=|\cr|=|\cr|<>|\cr|>=|\cr|>|\ee
+ \bb\<boolean>\cr\<numeric>\cr\<pair>\cr\<string>\cr\<transform>\ee;\\
+\raise3pt\hbox{\strut}%
+|not| \<boolean>; \ \<boolean> |and| \<boolean>; \ \<boolean> |or| \<boolean>.
+
+\medbreak\textindent\bull {\it ^{Numeric} things:\/} \
+|tracingtitles|, \dots, |yoffset| (see Chapter~25);\\
+|eps|, |epsilon|, |infinity|; \ |tolerance|, |join_radius|, |displaying|; \
+\<constant>;\\
+\bb|sqrt|\cr|sind|\cr|cosd|\cr|mlog|\cr|mexp|\ee\<numeric>; \
+\bb|floor|\cr|round|\cr|hround|\cr|vround|\cr|ceiling|\ee\<numeric>; \
+\bb|lft|\cr|rt|\cr|top|\cr|bot|\cr|good.x|\cr|good.y|\ee\<numeric>;\\
+\bb|xpart|\cr|ypart|\ee\bb\<pair>\cr\<transform>\ee; \
+\bb|xxpart|\cr|xypart|\cr|yxpart|\cr|yypart|\ee\<transform>; \
+\bb|ASCII|\cr|oct|\cr|hex|\ee\<string>;\\
+|normaldeviate|; \ |uniformdeviate| \<numeric>; \ |whatever|;\\
+\lower6pt\null
+|angle| \<pair>; \ |turningnumber| \<cycle>; \ |totalweight| \<picture>;\\
+\bb|+|\cr\noalign{\kern-2pt}|-|\cr\noalign{\kern-2pt}\<constant>\ee\<numeric>; \
+\bb|incr|\cr|decr|\ee\<variable>; \
+|byte|\bb\<numeric>\cr\<string>\ee;\\
+\<numeric>\bb|+|\cr|-|\ee\<numeric>; \
+\<numeric>\bb|++|\cr|+-+|\ee\<numeric>;\\
+\vbox to24pt{}%
+\smash{\<numeric>\bb\tt*\cr\tt/\cr\tt**\ee\<numeric>}; \
+\<numeric>\bb|mod|\cr|div|\ee\<numeric>;\\
+\<pair> |dotprod| \<pair>; \
+\bb|max|\cr|min|\ee|(|\<numerics>|)|; \
+\bb|abs|\cr|length|\ee\bb\<numeric>\cr\<pair>\cr\<path>\cr\<string>\ee;\\
+\<numeric>|[|\<numeric>|,|\<numeric>|]|; \
+|solve|\<function>|(|\<numeric>|,|\<numeric>|)|;\\
+|directiontime| \<pair> |of| \<path>.
+
+\medbreak\textindent\bull {\it ^{Pair} things:\/} \
+|left|, |right|, |up|, |down|, |origin|; \
+|(|\<numeric>|,|\<numeric>|)|;\\
+|z|\<suffix>; \ |dir| \<numeric>; \ |unitvector| \<pair>; \ |round| \<pair>;\\
+\bb|lft|\cr|rt|\cr|top|\cr|bot|\ee\<pair>; \
+\bb|good.lft|\cr|good.rt|\cr|good.top|\cr|good.bot|\ee\<pair>; \
+\bb|point|\cr|precontrol|\cr|postcontrol|\cr|direction|\ee%
+ \<numeric> |of| \<path>;\\
+\bb|+|\cr\noalign{\kern-2pt}|-|\cr\noalign{\kern-2pt}\<constant>\ee\<pair>; \
+\<pair>\bb|+|\cr|-|\ee\<pair>; \
+\<numeric>|[|\<pair>|,|\<pair>|]|;\\
+\<numeric>|*|\<pair>; \
+\<pair>\bb|*|\cr|/|\ee\<numeric>; \
+\<pair>\<transformer>;\\
+\<path>\bb|intersectionpoint|\cr|intersectiontimes|\ee\<path>; \
+\bb|max|\cr|min|\ee|(|\<pairs>|)|;\\
+\raise3pt\hbox{\strut}%
+|penoffset| \<pair> |of| \<pen>; \
+|directionpoint| \<pair> |of| \<path>.
+
+\medbreak\textindent\bull {\it ^{Path} things:\/} \
+|quartercircle|, |halfcircle|, |fullcircle|;\\
+|unitsquare|; \
+|flex(|\<pairs>|)|; \
+|makepath| \<pen>;\\
+|superellipse(|\<pair>|,|\<pair>|,|\<pair>|,|\<pair>|,|\<numeric>|)|;\\
+|reverse| \<path>; \
+|counterclockwise| \<path>; \
+|tensepath| \<path>;\\
+\<path>\<transformer>; \
+|interpath(|\<numeric>|,|\<path>|,|\<path>|)|;\\
+\bb\<pair>\cr\<path>\ee
+\bb|{|\<pair>|}|\cr|{|\<curl>|}|\cr\<empty>\ee
+\bb\strut|..|\cr|...|\cr|..|\<tension>|..|\cr|..|\<controls>|..|\cr
+ |--|\cr|---|\cr|&|\cr|softjoin|\ee
+\bb|{|\<pair>|}|\cr|{|\<curl>|}|\cr\<empty>\ee
+\bb\<pair>\cr\<path>\cr|cycle|\ee;\\
+|subpath| \<pair> |of| \<path>.
+
+\medbreak\textindent\bull {\it ^{Pen} things:\/} \
+|pencircle|, |pensquare|, |penrazor|, |penspeck|;\\
+|nullpen|; \ |currentpen|; \
+|makepen| \<path>; \
+\<pen>\<transformer>.
+
+\medbreak\textindent\bull {\it ^{Picture} things:\/} \
+|nullpicture|, |blankpicture|; \ |unitpixel|;\\
+|currentpicture|; \
+\bb|+|\cr|-|\ee\<picture>; \
+\<picture>\bb|+|\cr|-|\ee\<picture>;\\
+\<picture>\<transformer>.
+
+\medbreak\textindent\bull {\it ^{String} things:\/} \
+|"constant"|; \ |ditto|; \ |jobname|; \ |readstring|;\\
+|str|\<suffix>; \
+|decimal| \<numeric>; \
+|char| \<numeric>;\\
+\<string> |&| \<string>; \
+\bb|max|\cr|min|\ee|(|\<strings>|)|; \
+|substring| \<pair> |of| \<string>.
+
+\medbreak\textindent\bull {\it ^{Transform} things:\/} \
+|identity|; \ |currenttransform|;\\
+|inverse| \<transform>; \
+\<transform>\<transformer>.
+
+\advance\lineskip by 3pt
+\advance\medskipamount by 3pt
+\medbreak\textindent\bull {\it ^{Transformers}:\/} \
+|transformed| \<transform>;\\
+\bb|rotated|\cr|slanted|\ee\<numeric>; \
+\bb|scaled|\cr|xscaled|\cr|yscaled|\ee\<numeric>; \
+\bb|shifted|\cr|zscaled|\ee\<pair>;\\
+|reflectedabout(|\<pair>|,|\<pair>|)|; \
+|rotatedaround(|\<pair>|,|\<numeric>|)|.
+
+\medbreak\textindent\bull {\it ^{Conditions}:\/}\\
+|if| \<boolean>|: |\<text> \bb|elseif|\<boolean>|: |\<text>\ee$^{\ge0}$%
+\bb|else:| \<text>\cr\<empty>\ee|fi|.
+
+\smallbreak\textindent\bull {\it ^{Loops}:\/} \ |forever:| \<text> |endfor|;\\
+|for| $\nu$ \bb|=|\cr|:=|\ee
+\bb\<numeric> |upto| \<numeric>\cr
+ \<numeric> |downto| \<numeric>\cr
+ \<numeric>\thinspace|step|\thinspace
+ \<numeric>\thinspace|until|\thinspace\<numeric>\ee
+|:| \<text$(\nu)$> |endfor|;\\
+|for| $\epsilon$ \bb|=|\cr|:=|\ee
+ \<expressions>|:| \<text$(\epsilon)$> |endfor|;\\
+|forsuffixes| $\sigma$ \bb|=|\cr|:=|\ee
+ \<suffixes>|:| \<text$(\sigma)$> |endfor|;\\
+|exitif| \<boolean>|;| ; \quad
+|exitunless| \<boolean>|;| .
+
+\medbreak\textindent\bull {\it ^{Diagnostic things}:\/} \
+|???|; \ |interact|; \
+|hide(|\<statements>|)|;\\
+|loggingall|, |tracingall|, |tracingnone|.
+
+\textindent\bull {\it ^{Starting a job}:\/} \
+|\mode=|\<modename>; \ |mag=|\bb\<numeric>\cr|magstep|\<numeric>\ee;\\
+|screenchars|; \ |screenstrokes|; \ |imagerules|; \ |gfcorners|; \
+|nodisplays|;\\
+|notransforms|; \ |input| \<filename>.
+
+\medbreak\textindent\bull {\it ^{Conversion to pixel units}:\/} \
+|mode_setup|; \ |fix_units|;\\
+|pixels_per_inch|, |blacker|, |fillin|, |o_correction|;\\
+|mm#|, |cm#|, |pt#|, |pc#|, |dd#|, |cc#|, |bp#|, |in#|;\\
+|mm|, |cm|, |pt|, |pc|, |dd|, |cc|, |bp|, |in|;\\
+|mode_def|; \ |extra_setup|;\\
+\bb|define_pixels|\cr
+|define_whole_pixels|\cr
+|define_whole_vertical_pixels|\cr
+|define_good_x_pixels|\cr
+|define_good_y_pixels|\cr
+|define_blacker_pixels|\cr
+|define_whole_blacker_pixels|\cr
+|define_whole_vertical_blacker_pixels|\cr
+|define_corrected_pixels|\cr
+|define_horizontal_corrected_pixels|\cr
+|lowres_fix|\ee|(|\<names>|)|.
+
+\advance\lineskip by-4pt
+\advance\medskipamount by-4pt
+\medbreak\textindent\bull {\it Character and font administration:\/}\\
+|beginchar(|\<code>|,|\<width$\0$>|,|\<height$\0$>|,|\<depth$\0$>|)|; \ \
+|extra_beginchar|;\\
+|italcorr| \<numeric$\0$>; \ |change_width|; \ |endchar|; \ \
+|extra_endchar|;\\
+\bb|font_size|\cr|font_slant|\cr|font_normal_space|\cr
+ |font_normal_stretch|\cr|font_normal_shrink|\cr|font_x_height|\cr
+ |font_quad|\cr|font_extra_space|\ee
+\bb|=|\cr\noalign{\kern-2pt}|:=|\cr\noalign{\kern-2pt}\<empty>\ee
+\<numeric$\0$>; \
+\bb|ligtable|\<ligs/kerns>\cr|charlist|\<codes>\cr|extensible|\<codes>\cr
+ |fontdimen|\<info>\cr|headerbytes|\<info>\ee;\\
+\bb|font_identifier|\cr|font_coding_scheme|\ee
+\smash{\bb\tt=\cr\noalign{\kern-2pt}\tt:=\cr\noalign{\kern-2pt}\<empty>\ee}%
+\<string>.
+
+\medbreak\textindent\bull {\it ^{Drawing}:\/} \
+|penpos|\<suffix>|(|\<length>|,|\<angle>|)|; \
+|penstroke| \<path($e$)>;\\
+|pickup|\bb\<pen>\cr\<saved pen number>\ee; \
+\<pen number>|:=savepen|; \ |clear_pen_memory|;\\
+\lower6pt\null|pen_lft|, |pen_rt|, |pen_top|, |pen_bot|;\\
+\bb|fill|\cr|unfill|\cr|filldraw|\cr|unfilldraw|\ee\<cycle>; \
+\bb|draw|\cr|undraw|\cr|cutdraw|\ee\<path>; \
+\bb|drawdot|\cr|undrawdot|\ee\<pair>;\\
+\vbox to 10pt{}|erase| \<picture command>; \
+|cutoff(|\<pair>|,|\<angle>|)|;\\
+|addto| \<picture variable> |also| \<picture>;\\
+|addto| \<picture variable>\bb|contour| \<cycle>\cr|doublepath| \<path>\ee
+ $\hbox{\bb|withpen|\<pen>\cr|withweight|\<numeric>\ee}^
+ {\smash{\lower3pt\hbox{$\scriptstyle\ge0$}}}\!$;\\
+|cull| \<picture variable>\bb|keeping|\cr|dropping|\ee\<pair>%
+ \bb|withweight|\<numeric>\cr\<empty>\ee.
+
+\medbreak\textindent\bull {\it ^{Screen display}:\/} \
+|currentwindow|; \
+|screen_rows|, |screen_cols|;\\
+|openwindow| \<numeric> |from| \<screen pair> |to| \<screen pair>
+ |at| \<pair>;\\
+|display| \<picture variable> |inwindow| \<numeric>.
+
+\advance\lineskip by 4pt
+\advance\medskipamount by 2pt
+\medbreak\textindent\bull {\it ^{Statements}:\/} \
+\<empty>; \ \<string>; \ |begingroup| \<statements> |endgroup|;\\
+\bb\<boolean>\cr\<numeric>\cr\<pair>\cr\<path>\cr
+ \<pen>\cr\<picture>\cr\<string>\cr\<transform>\ee
+$\hbox{\bb\bb|=|\cr|:=|\ee
+ \bb\<boolean>\cr\<numeric>\cr\<pair>\cr\<path>\cr
+ \<pen>\cr\<picture>\cr\<string>\cr\<transform>\ee\ee}^
+ {\smash{\lower6pt\hbox{$\scriptstyle\ge1$}}}\!\!$; \qquad
+\bb|boolean|\cr|numeric|\cr|pair|\cr|path|\cr
+|pen|\cr|picture|\cr|string|\cr\thinspace|transform|\thinspace\ee\<names>;\\
+|save| \<names>; \
+|interim| \<internal> |:=| \<numeric>; \
+|let| \<name>\bb|=|\cr|:=|\ee\<name>;\\
+\bb|def|\cr|vardef|\ee\<name>\<parameters>\bb|=|\cr|:=|\ee
+ \<text>\thinspace|enddef|;\\
+\vbox to24pt{}\bb|primarydef|\cr|secondarydef|\cr|tertiarydef|\ee
+ \ $\alpha$ \<name> $\beta$ \bb|=|\cr|:=|\ee
+ \<text$(\alpha,\beta)$>\thinspace|enddef|;\\
+\strut|showit|; \ |shipit|; \ |cullit|; \ |openit|; \
+|clearit|; \ |clearxy|; \ |clearpen|;\\
+|stop| \<string>; \
+|show| \<expressions>; \
+\bb|message|\cr|errmessage|\cr|errhelp|\ee\<string>;\\
+\bb|showvariable|\cr|showtoken|\ee\<names>; \
+\bb|showdependencies|\cr|showstats|\ee;\\
+\strut see also Chapter 26 for some more exotic commands.
+
+\advance\lineskip by -1pt
+\advance\medskipamount by 1pt
+\medbreak\textindent\bull {\it ^{Proofsheet} information:\/}\\
+\bb|labels|\cr|penlabels|\ee
+ \bb|top|\cr|lft|\cr|rt|\cr|bot|\cr\<empty>\ee
+ \bb|nodot|\cr\<empty>\ee
+ |(|\<suffixes>|)|;\\
+|makelabel|\bb|top|\cr|lft|\cr|rt|\cr|bot|\cr\<empty>\ee
+ \bb|nodot|\cr\<empty>\ee
+ |(|\<string>|,|\<pair>|)|; \
+\bb|titlefont|\cr|labelfont|\cr|grayfont|\cr|slantfont|\ee \<name>;\\
+\bb|proofrule|\cr|screenrule|\ee|(|\<pair>|,|\<pair>|)|; \
+|makegrid(|\<pairs>|)(|\<pairs>|)|;\\
+|proofrulethickness| \<numeric>; \ |proofoffset| \<pair>.
+
+\medbreak\textindent\bull {\it Hacks:\/} \ |gobble|, |gobbled|, |killtext|; \
+|capsule_def|; \ |numtok|.
+
+\medbreak
+\endgroup % end of special \lineskip
+\ninepoint
+\danger The remainder of this appendix contains an edited transcript
+of the ``plain ^{base file},'' which is a set of macros that come with
+normal implementations of \MF\!\null. These macros serve three basic purposes:
+\ (1)~They make \MF\ usable, because \MF's primitive capabilities
+operate at a very low level. A~``virgin'' \MF\ system that has no
+macros is like a newborn baby that has an immense amount to learn about
+the real world; but it is capable of learning fast. \ (2)~The plain
+\MF\ macros provide a basis for more elaborate and powerful bases
+tailored to individual tastes and applications. You can do a lot with
+plain \MF\!, but pretty soon you'll want to do even more. \ (3)~The macros
+also serve to illustrate how additional bases can be designed. \enddanger
+
+Somewhere in your computer system you should be able to find a file called
+^|plain.mf| that contains what has been preloaded into the
+running \MF\ system that you use. That file should match the
+code discussed below, except that it might do some things in an
+equivalent but slightly more efficient manner.
+
+When we come to macros whose use has not yet been explained---for
+example, somehow |softjoin| and |stop| never made it
+into Chapters 1 through~27---we shall consider them from a user's
+viewpoint. But most of the comments that follow are addressed to a
+potential base-file designer.
+
+A special program called ^|INIMF| is used to install \MF; |INIMF| is
+just like \MF\ except that it is able to `^@dump@' a base file
+suitable for preloading. This operation requires additional program
+space, so |INIMF| generally has less memory available
+than you would expect to find in a production version of \MF\!.
+
+\subsection Getting started. A base file has to have a ^@delimiters@
+command near the beginning, since |INIMF| doesn't have any delimiters
+built~in. The first few lines usually also give the base file a name and
+version number as shown here.
+\beginlines
+|% This is the plain METAFONT base that's described in The METAFONTbook.|
+|% N.B.: Please change "base_version" whenever this file is modified!|
+|% And don't modify the file under any circumstances.|
+|string base_name, base_version; base_name="plain"; base_version="2.71";|
+\smallskip
+^|message|| "Preloading the plain base, version " & base_version;|
+\smallskip
+|delimiters (); % this makes parentheses behave like parentheses|
+\endlines
+
+Next we define some of the simplest macros, which provide ``syntactic sugar''
+for commonly occurring idioms. ^^{blash blash}
+For example, `@stop@ |"hello"|' displays `|hello|' on the terminal and waits
+until \<return> is typed.
+\beginlines
+|def |^|upto|| = step 1 until enddef; def |^|downto|| = step -1 until enddef;|
+|def |^|exitunless|| expr c = exitif not c enddef;|
+|let |^|relax|| = \; % ignore the word `relax', as in TeX|
+|let \\ = \; % double relaxation is like single|
+|def |^|]]|| = ] ] enddef; % right brackets should be loners|
+|def |^|--|| = {curl 1}..{curl 1} enddef;|
+|def |^|---|| = .. tension infinity .. enddef;|
+|def |^|...|| = .. tension atleast 1 .. enddef;|
+\smallskip
+|def |^|gobble|| primary g = enddef; def |^|killtext|| text t = enddef;|
+|primarydef g |^|gobbled|| gg = enddef;|
+|def |^|hide||(text t) = exitif numeric begingroup t; endgroup; enddef;|
+|def |^|???|| = hide(interim showstopping:=1; showdependencies) enddef;|
+|def |^|stop|| expr s = message s; gobble readstring enddef;|
+\endlines
+(Chapter 20 points out that `|\|' is an expandable token that expands
+into nothing. Plain \MF\ allows also `|\\|', because there's a
+formatting program called ^|MFT| that uses~|\\| to insert extra spacing
+in a ^{pretty-printed} listing.) \ The ``clever'' code for @hide@
+is based on the fact that a ^{vacuous} expression is not numeric;
+hence no loop is exited, ^^@exitif@ and the computer doesn't mind the
+fact that we may not be in a loop at all.
+
+The values of ^{internal quantities} are next on the agenda:
+\beginlines
+^|smoothing||:=1; |^|autorounding||:=2; % this adjusts curves to the raster|
+^|turningcheck||:=2; % this will warn about a "strange path"|
+^|granularity||:=1; % this says that pixels are pixels|
+\smallskip
+|def |^|interact|| = % prepares to make "show" commands stop|
+| hide(showstopping:=1; tracingonline:=1) enddef;|
+|def |^|loggingall|| = % puts tracing info into the log|
+| tracingcommands:=3; tracingedges:=2; tracingtitles:=1;|
+| tracingequations:=1; tracingcapsules:=1; tracingspecs:=1;|
+| tracingpens:=1; tracingchoices:=1; tracingstats:=1;|
+| tracingoutput:=1; tracingmacros:=1; tracingrestores:=1;|
+| enddef;|
+|def |^|tracingall|| = % turns on every form of tracing|
+| tracingonline:=1; showstopping:=1; loggingall enddef;|
+|def |^|tracingnone|| = % turns off every form of tracing|
+| tracingcommands:=0; tracingonline:=0; showstopping:=0;|
+| tracingedges:=0; tracingtitles:=0; tracingequations:=0;|
+| tracingcapsules:=0; tracingspecs:=0; tracingpens:=0;|
+| tracingchoices:=0; tracingstats:=0; tracingoutput:=0;|
+| tracingmacros:=0; tracingrestores:=0; enddef;|
+\endlines
+The user can say @interact@ in the midst of a statement; but
+@loggingall@, @tracingall@, and @tracingnone@ should come
+between statements. \ (You don't need a ^{semicolon} after them,
+because they come equipped with their own closing `|;|'.)
+
+\subsection Math routines. The second major part of |plain.mf|
+contains the definitions of basic constants and mathematical
+macros that extend the primitive capabilities of \MF's expressions.
+\beginlines
+|% numeric constants|
+|newinternal eps,epsilon,infinity;|
+^|eps|| := .00049; % this is a pretty small positive number|
+^|epsilon|| := 1/256/256; % but this is the smallest|
+^|infinity|| := 4095.99998; % and this is the largest|
+\smallbreak
+|% pair constants|
+|pair right,left,up,down,origin;|
+^|origin||=(0,0); |^|up||=-|^|down||=(0,1); |^|right||=-|^|left||=(1,0);|
+\smallbreak
+|% path constants|
+|path quartercircle,halfcircle,fullcircle,unitsquare;|
+^|quartercircle||=(right{up}..(right+up)/sqrt2..up{left}) scaled .5;|
+^|halfcircle||=quartercircle & quartercircle rotated 90;|
+^|fullcircle||=halfcircle & halfcircle rotated 180 & cycle;|
+^|unitsquare||=(0,0)--(1,0)--(1,1)--(0,1)--cycle;|
+\smallbreak
+|% transform constants|
+|transform identity;|
+|for z=origin,right,up: z transformed |^|identity|| = z; endfor|
+\smallbreak
+|% picture constants|
+|picture blankpicture,unitpixel;|
+^|blankpicture||=nullpicture; % `display blankpicture...'|
+^|unitpixel||=nullpicture; addto unitpixel contour unitsquare;|
+\smallbreak
+|% string constants|
+|string ditto; |^|ditto|| = char 34; % ASCII double-quote mark|
+\smallbreak
+|% pen constants|
+|def capsule_def(suffix s) primary u = def s = u enddef enddef;|
+|capsule_def(pensquare) makepen(unitsquare shifted -(.5,.5));|
+|capsule_def(penrazor) makepen((-.5,0)--(.5,0)--cycle);|
+|pen penspeck; penspeck=pensquare scaled eps;|
+\endlines
+The ^@pensquare@ and ^@penrazor@ constants are defined here in a
+surprisingly roundabout way, just so that they can be ^{future pens}
+instead of pens. \MF\ can transform a future pen much faster than a
+pen, since pens have a complex internal data structure, so this
+trick saves time. But how does it work? Well, a variable cannot
+be a future pen, but a ^{capsule} can; hence @pensquare@ and @penrazor@
+are defined, via ^@capsule\_def@, to be macros that expand into single capsules.
+Incidentally, ^@penspeck@ is an extremely tiny little pen that is used by the
+@drawdot@ macro. Since it is not intended to be transformed,
+we are better off making it a pen; then it's immediately ready for use.
+
+Now that the basic constants have been defined, we turn to
+mathematical operations. There's one operation that has no arguments:
+\beginlines
+|% nullary operators|
+|vardef |^|whatever|| = save ?; ? enddef;|
+\endlines
+The reasoning behind this is discussed in exercise 17.\Xwhat.
+
+Operations that take one argument are introduced next.
+\beginlines
+|% unary operators|
+|let |^|abs|| = length;|
+\smallskip
+|vardef |^|round|| primary u =|
+| if numeric u: floor(u+.5)|
+| elseif pair u: (hround xpart u, vround ypart u)|
+| else: u fi enddef;|
+\smallskip
+|vardef |^|hround|| primary x = floor(x+.5) enddef;|
+|vardef |^|vround|| primary y = floor(y.o_+.5)_o_ enddef;|
+\smallskip
+|vardef |^|ceiling|| primary x = -floor(-x) enddef;|
+\smallbreak
+|vardef |^|byte|| primary s = if string s: ASCII fi s enddef;|
+\smallbreak
+|vardef |^|dir|| primary d = right rotated d enddef;|
+\smallskip
+|vardef |^|unitvector|| primary z = z/abs z enddef;|
+\smallbreak
+|vardef |^|inverse|| primary T =|
+| transform T_; T_ transformed T = identity; T_ enddef;|
+\smallbreak
+|vardef |^|counterclockwise|| primary c =|
+| if turningcheck>0:|
+| interim |^|autorounding||:=0;|
+| if |^|turningnumber|| c <= 0: reverse fi fi c enddef;|
+\smallbreak
+|vardef |^|tensepath|| expr r =|
+| for k=0 upto length r - 1: point k of r --- endfor|
+| if cycle r: cycle else: point infinity of r fi enddef;|
+\endlines
+Notice that the variable `|T_|' was not saved by the "inverse"
+function. The plain base routines gain ^{efficiency} by
+using ``^{private}'' tokens that are assumed to be distinct
+from any of the user's tokens; these private tokens always
+end with the ^{underscore} character,~`|_|'. If ordinary user programs
+never contain such token names, no surprises will occur,
+provided that different macro designers who combine their routines are
+careful that their private names are not in conflict.
+
+The private tokens `|o_|' and `|_o_|' used in |vround| stand
+for `|*aspect_ratio|' and `|/aspect_ratio|', respectively,
+as we shall see shortly.
+
+Now we define `mod' and `div', being careful to do this in such a way that
+the identities $a(x\;\hbox{mod}\;y)=(ax)\;\hbox{mod}\;(ay)$ and
+$(ax)\;\hbox{div}\;(ay)=x\;\hbox{div}\;y$ are valid.
+\beginlines
+|% binary operators|
+|primarydef x |^|mod|| y = (x-y*floor(x/y)) enddef;|
+|primarydef x |^|div|| y = floor(x/y) enddef;|
+|primarydef w |^|dotprod|| z = (xpart w * xpart z + ypart w * ypart z) enddef;|
+\endlines
+
+The `|**|' operator is designed to be most efficient when it's used
+for squaring. A separate `^|takepower|' routine is used for exponents
+other than~2, so that \MF\ doesn't have to skip over lots of tokens
+in the common case. The |takepower| routine is careful to give the
+correct answer in expressions like `|(-2)**(-3)|' and `|0**0|'.
+\beginlines
+|primarydef x |^|**|| y = if y=2: x*x else: takepower y of x fi enddef;|
+|def takepower expr y of x =|
+| if x>0: mexp(y*mlog x)|
+| elseif (x=0) and (y>0): 0|
+| else: 1|
+| if y=floor y:|
+| if y>=0: for n=1 upto y: *x endfor|
+| else: for n=-1 downto y: /x endfor fi|
+| else: hide(errmessage "Undefined power: " & decimal x&"**"&decimal y)|
+| fi fi enddef;|
+\endlines
+
+\MF's primitive path operations have been defined in such a way that the
+following higher-level operations are easy:
+\beginlines
+|vardef |^|direction|| expr t of p =|
+| postcontrol t of p - precontrol t of p enddef;|
+\smallskip
+|vardef |^|directionpoint|| expr z of p =|
+| a_:=|^|directiontime|| z of p;|
+| if a_<0: errmessage("The direction doesn't occur"); fi|
+| point a_ of p enddef;|
+\smallskip
+|secondarydef p |^|intersectionpoint|| q =|
+| begingroup save x_,y_; (x_,y_)=p |^|intersectiontimes|| q;|
+| if x_<0: errmessage("The paths don't intersect"); (0,0)|
+| else: .5[point x_ of p, point y_ of q] fi endgroup|
+|enddef;|
+\weakendlines
+The private token `|a_|' will be declared as an ^{internal quantity}.
+Internal quantities are more ^{efficient} than ordinary numeric variables.
+
+Plain \MF's `^{softjoin}' operation provides a way to hook paths together
+without the abrupt change of direction implied by~`|&|'. Assuming that
+the final point of~$p$ is the first point of~$q$, the path `$p$~softjoin~$q$'
+begins on~$p$ until coming within "join\_radius" of this common point;
+then it curves over and finishes~$q$ in essentially the same way.
+The internal quantity ^"join\_radius" should be set to the desired
+value before softjoin is applied. \ (This routine is due to N.~N. ^{Billawala}.)
+\beginlines
+|tertiarydef p softjoin q =|
+| begingroup c_:=|^|fullcircle|| scaled 2join_radius shifted point 0 of q;|
+| a_:=ypart(c_ intersectiontimes p); b_:=ypart(c_ intersectiontimes q);|
+| if a_<0:point 0 of p{direction 0 of p} else: subpath(0,a_) of p fi|
+| ... if b_<0:{direction infinity of q}point infinity of q|
+| else: subpath(b_,infinity) of q fi endgroup enddef;|
+|newinternal join_radius,a_,b_; path c_;|
+\endlines
+
+The remaining math operators don't fall into the ordinary patterns; something
+is unusual about each of them. First we have `|incr|' and `|decr|', which apply
+only to variables; they have the side effect of changing the variable's value.
+\beginlines
+% special operators
+|vardef |^|incr|| suffix $ = $:=$+1; $ enddef;|
+|vardef |^|decr|| suffix $ = $:=$-1; $ enddef;|
+\weakendlines
+You can say either `|incr|~|x|' or `|incr|~|(x)|', within
+an expression; but `|incr|~|x|' by itself is not a valid statement.
+
+To reflect about a line, we compute a ^{transform} on the fly:
+\beginlines
+|def |^|reflectedabout||(expr w,z) = % reflects about the line w..z|
+| transformed|
+| begingroup transform T_;|
+| w transformed T_ = w; z transformed T_ = z;|
+| xxpart T_ = -yypart T_; xypart T_ = yxpart T_; % T_ is a reflection|
+| T_ endgroup enddef;|
+\smallskip
+|def |^|rotatedaround||(expr z, d) = % rotates d degrees around z|
+| shifted -z rotated d shifted z enddef;|
+|let |^|rotatedabout|| = rotatedaround; % for roundabout people|
+\endlines
+
+Now we come to an interesting trick: The user writes something like
+`min$(a,b)$' or `max$(a,b,c,d)$', and
+\MF's notation for macro calls makes it easy to separate the first argument
+from the rest---assuming that at least two arguments are present.
+\beginlines
+|vardef |^|max||(expr u)(text t) = % t is a list of numerics, pairs, or strings|
+| save u_; setu_ u; for uu = t: if uu>u_: u_:=uu; fi endfor|
+| u_ enddef;|
+\smallskip
+|vardef |^|min||(expr u)(text t) = % t is a list of numerics, pairs, or strings|
+| save u_; setu_ u; for uu = t: if uu<u_: u_:=uu; fi endfor|
+| u_ enddef;|
+\smallskip
+|def setu_ primary u =|
+| if pair u: pair u_ elseif string u: string u_ fi;|
+| u_=u enddef;|
+\weakendlines
+^^"setu\_" Appendix D discusses some variations on this theme.
+
+The ^|flex| routine defines part of a path whose directions at the
+endpoints will depend on the environment, because this path is not
+enclosed in parentheses.
+\beginlines
+|def flex(text t) = % t is a list of pairs|
+| hide(n_:=0; for z=t: z_[incr n_]:=z; endfor|
+| dz_:=z_[n_]-z_1)|
+| z_1 for k=2 upto n_-1: ...z_[k]{dz_} endfor ...z_[n_] enddef;|
+|newinternal n_; pair z_[],dz_;|
+\endlines
+
+The five parameters to `superellipse' are the right, the top, the left,
+the bottom, and the superness.
+\beginlines
+|def |^|superellipse||(expr r,t,l,b,s)=|
+| r{up}...(s[xpart t,xpart r],s[ypart r,ypart t]){t-r}...|
+| t{left}...(s[xpart t,xpart l],s[ypart l,ypart t]){l-t}...|
+| l{down}...(s[xpart b,xpart l],s[ypart l,ypart b]){b-l}...|
+| b{right}...(s[xpart b,xpart r],s[ypart r,ypart b]){r-b}...cycle enddef;|
+\endlines
+
+Chapter~14 illustrates the `interpath' routine, which interpolates
+between paths to find a path that would be written `$a[p,q]$' if
+\MF's macro notation were more general.
+\beginlines
+|vardef |^|interpath||(expr a,p,q) =|
+| for t=0 upto length p-1: a[point t of p, point t of q]|
+| ..controls a[postcontrol t of p, postcontrol t of q]|
+| and a[precontrol t+1 of p, precontrol t+1 of q] .. endfor|
+| if cycle p: cycle|
+| else: a[point infinity of p, point infinity of q] fi enddef;|
+\endlines
+
+Finally we come to the "solve" macro, which has already been presented
+in Chapter~20. Appendix~D gives further illustrations of its use.
+\beginlines
+|vardef |^|solve||@#(expr true_x,false_x)= % @#(true_x)=true, @#(false_x)=false|
+| tx_:=true_x; fx_:=false_x;|
+| forever: x_:=.5[tx_,fx_]; exitif abs(tx_-fx_)<=tolerance;|
+| if @#(x_): tx_ else: fx_ fi :=x_; endfor|
+| x_ enddef; % now x_ is near where @# changes from true to false|
+|newinternal |^|tolerance||, tx_,fx_,x_; tolerance:=.1;|
+\finalendlines
+
+\subsection Conversion to pixels. The next main subdivision of |plain.mf|
+contains macros and constants that help convert dimensions from
+device-independent ``sharped'' or ``true'' units into the pixel units
+corresponding to a particular device. First comes a subroutine that
+computes eight basic units, assuming that the number
+^^{mm} ^^{cm} ^^{pt} ^^{pc} ^^{dd} ^^{cc} ^^{bp} ^^{in} ^^@fix\_units@
+of ^"pixels\_per\_inch" is known:
+\beginlines
+|def fix_units = % define the conversion factors, given pixels_per_inch|
+| mm:=pixels_per_inch/25.4; cm:=pixels_per_inch/2.54;|
+| pt:=pixels_per_inch/72.27; pc:=pixels_per_inch/6.0225;|
+| dd:=1238/1157pt; cc:=12dd;|
+| bp:=pixels_per_inch/72; in:=pixels_per_inch;|
+| hppp:=pt; % horizontal pixels per point|
+| vppp:=aspect_ratio*hppp; % vertical pixels per point|
+| enddef;|
+\endlines
+
+^{Sharped units} are actually expressed in terms of points, but a virtuous
+user will not write programs that exploit this fact.
+\beginlines
+|mm#=2.84528; pt#=1; dd#=1.07001; bp#=1.00375;|
+|cm#=28.45276; pc#=12; cc#=12.84010; in#=72.27;|
+\endlines
+
+A particular device is supposed to be modeled by four parameters, called
+^"pixels\_per\_inch", ^"blacker", ^"o\_correction", and ^"fillin", as discussed
+in Chapter~11. Appropriate
+values will be assigned to these internal quantities by @mode\_setup@.
+\beginlines
+|newinternal pixels_per_inch; % the given resolution|
+|newinternal blacker, o_correction; % device-oriented corrections|
+\endlines
+(The fourth parameter, "fillin", is already an internal quantity of \MF\!.)
+
+Here are the ten principal ways to convert from
+^^{define\_pixels (and nine others)} sharped units to pixels:
+\beginlines
+|def define_pixels(text t) =|
+| forsuffixes $=t: $:=$.#*hppp; endfor enddef;|
+|def define_whole_pixels(text t) =|
+| forsuffixes $=t: $:=hround($.#*hppp); endfor enddef;|
+|def define_whole_vertical_pixels(text t) =|
+| forsuffixes $=t: $:=vround($.#*hppp); endfor enddef;|
+|def define_good_x_pixels(text t) =|
+| forsuffixes $=t: $:=good.x($.#*hppp); endfor enddef;|
+|def define_good_y_pixels(text t) =|
+| forsuffixes $=t: $:=good.y($.#*hppp); endfor enddef;|
+|def define_blacker_pixels(text t) =|
+| forsuffixes $=t: $:=$.#*hppp+blacker; endfor enddef;|
+|def define_whole_blacker_pixels(text t) =|
+| forsuffixes $=t: $:=hround($.#*hppp+blacker);|
+| if $<=0: $:=1; fi endfor enddef;|
+|def define_whole_vertical_blacker_pixels(text t) =|
+| forsuffixes $=t: $:=vround($.#*hppp+blacker);|
+| if $<=0: $:=1_o_; fi endfor enddef;|
+|def define_corrected_pixels(text t) =|
+| forsuffixes $=t: $:=vround($.#*hppp*o_correction)+eps; endfor enddef;|
+|def define_horizontal_corrected_pixels(text t) =|
+| forsuffixes $=t: $:=hround($.#*hppp*o_correction)+eps; endfor enddef;|
+\endlines
+
+Chapter 24 discusses the ^@lowres\_fix@ routine, which helps to correct
+anomalies that may have occurred when sharped dimensions were rounded
+to whole pixels.
+\beginlines
+|def lowres_fix(text t) expr ratio =|
+| begingroup save min,max,first;|
+| forsuffixes $=t:|
+| if unknown min: min=max=first=$; min#=max#=$.#;|
+| elseif $.#<min#: min:=$; min#:=$.#;|
+| elseif $.#>max#: max:=$; max#:=$.#; fi endfor|
+| if max/min>ratio*max#/min#: forsuffixes $=t: $:=first; endfor fi|
+| endgroup enddef;|
+\finalendlines
+
+\subsection Modes of operation. The standard way to create a font with
+plain \MF\ is to start~up the program by saying
+\begindisplay
+|\mode=|\<mode name>|; mag=|\<magnification>|; input |%
+ \<font file name>
+\enddisplay
+in response to \MF's initial `^|**|'.
+The ^|mag| is omitted if the magnification is~1, and the ^|mode| is omitted
+if |mode=proof|. Additional commands like `|screenchars|' might be
+given before the `^|input|'; we shall discuss them later. If you are
+using another base file, called say the `|super|' base, this whole
+command line should be preceded by `|&super|'. The mode name should have
+been predeclared in your base file, by the |mode_def| routine below.
+If, however, you need a special mode that isn't in the base, you can put
+its commands into a file (e.g., `|specmode.mf|') and invoke it
+by saying
+\begindisplay
+^|\smode||="specmode"; mag=|$\,\cdots$
+\enddisplay
+instead of giving a predeclared mode name.
+
+Here is the ^@mode\_setup@ routine,
+which is usually one of the first macros to be called in a \MF\ program:
+\beginlines
+|def mode_setup =|
+| warningcheck:=0;|
+| if unknown mode: mode=proof; fi|
+| numeric aspect_ratio; transform currenttransform;|
+| scantokens if string mode:("input "&mode) else: mode_name[mode] fi;|
+| if unknown mag: mag=1; fi|
+| if unknown aspect_ratio: aspect_ratio=1; fi|
+| displaying:=proofing;|
+| pixels_per_inch:=pixels_per_inch*mag;|
+| if aspect_ratio=1: let o_=\; let _o_=\|
+| else: def o_=*aspect_ratio enddef; def _o_=/aspect_ratio enddef fi;|
+| fix_units;|
+| scantokens extra_setup; % the user's special last-minute adjustments|
+| currenttransform:=|
+| if unknown currenttransform: identity else: currenttransform fi|
+| yscaled aspect_ratio;|
+| clearit;|
+| pickup pencircle scaled (.4pt+blacker);|
+| warningcheck:=1; enddef;|
+|def |^|smode|| = string mode; mode enddef;|
+|string extra_setup, mode_name[];|
+|extra_setup=""; % usually there's nothing special to do|
+|newinternal |^|displaying||; % if positive, endchar will `showit'|
+\endlines
+^^"extra\_setup" ^^"mode\_name"
+The first `^@scantokens@' in @mode\_setup@ either reads a special
+file or calls a macro that expands into commands defining the mode.
+Notice that "aspect\_ratio" is always cleared to an undefined value
+when these commands are performed; you can't simply give a value to
+"aspect\_ratio" when you set "mode" and~"mag". If the aspect ratio
+isn't assigned a definite value by the mode routine, it will become unity,
+and the `|o_|' and `|_o_|' operations will be omitted from subsequent
+calculations. Notice also that the mode commands might do something special
+to "mag", since "mag" isn't examined until after the mode routine has
+acted. The "currenttransform" might also be given a special value. \MF's
+^"warningcheck" is temporarily disabled during these computations, since
+there might be more than 4096 pixels per inch. After @mode\_setup@ is
+finished, the "currentpicture" will be null, the "currenttransform"
+will take the "aspect\_ratio" into account, and the "currentpen" will be a
+circular nib with the standard default thickness of $0.4\pt$. \ (You should
+save this pen if you want to use it in a character, because @beginchar@
+will clear it away.)
+
+Plain \TeX\ has a convention for magnifying fonts in terms of ``magsteps,''
+where magstep~$m=1.2^m$. A geometric progression of font sizes is
+convenient, because scaling by magstep~$m$ followed by magstep~$n$ is
+^^{mexp} equivalent to scaling by magstep~$m+n$.
+\beginlines
+|vardef |^|magstep|| primary m = mexp(46.67432m) enddef;|
+\endlines
+
+When a mode is defined (e.g., `|proof|'), a numeric variable of that
+name is created and assigned a unique number (e.g.,~1). Then an
+^{underscore} character is appended, and a macro is defined for the
+resulting name (e.g., `|proof_|'). The "mode\_name" array is used to
+convert between number and name (e.g., "mode\_name"$_1=\null$|"proof_"|).
+\beginlines
+|def mode_def suffix $ =|
+| $:=incr number_of_modes;|
+| mode_name[$]:=str$ & "_";|
+| |^|expandafter|| |^|quote|| def scantokens mode_name[$] enddef;|
+|newinternal number_of_modes;|
+\endlines
+(This ^@mode\_def@ strategy was suggested by Bruce ^{Leban}.)
+
+Three basic modes are now defined, starting with two for proofing:
+\beginlines
+|% proof mode: for initial design of characters|
+|mode_def |^|proof|| =|
+| proofing:=2; % yes, we're making full proofs|
+| fontmaking:=0; % no, we're not making a font|
+| tracingtitles:=1; % yes, show titles online|
+| pixels_per_inch:=2601.72; % that's 36 pixels per pt|
+| blacker:=0; % no additional blackness|
+| fillin:=0; % no compensation for fillin|
+| o_correction:=1; % no reduction in overshoot|
+| enddef;|
+\smallbreak
+|% smoke mode: for label-free proofs to mount on the wall|
+|mode_def |^|smoke|| =|
+| proof_; % same as proof mode, except:|
+| proofing:=1; % yes, we're making unlabeled proofs|
+| extra_setup:=extra_setup&"grayfont black"; % with solid black pixels|
+| let makebox=maketicks; % make the boxes less obtrusive|
+| enddef;|
+\weakendlines
+Notice that "smoke" mode saves a lot of fuss by calling on `|proof_|';
+this is the macro that was defined by the first @mode\_def@.
+
+A typical mode for font generation appears next. ^^"fontmaking"
+\beginlines
+|% lowres mode: for certain devices that print 200 pixels per inch|
+|mode_def |^|lowres|| =|
+| proofing:=0; % no, we're not making proofs|
+| fontmaking:=1; % yes, we are making a font|
+| tracingtitles:=0; % no, don't show titles at all|
+| pixels_per_inch:=200; % that's pretty low resolution|
+| blacker:=.65; % make pens a bit blacker|
+| fillin:=.2; % compensate for diagonal fillin|
+| o_correction:=.4; % but don't overshoot as much|
+| enddef;|
+\smallskip
+|localfont:=lowres; % the mode most commonly used to make fonts|
+\endlines
+Installations of \MF\ typically have several more predefined modes, and they
+generally set "localfont" to something else. Such alterations should
+not be made in the master file |plain.mf|; they should appear in a separate
+file, as discussed below.
+
+\subsection Drawing and filling. Now we come to the macros that provide
+an interface between the user and \MF's primitive picture commands.
+^^"currentpen" ^^"currentpicture" ^^"currenttransform"
+First, some important program variables are introduced:
+\beginlines
+|pen currentpen;|
+|path currentpen_path;|
+|picture currentpicture;|
+|transform currenttransform;|
+|def t_ = transformed currenttransform enddef;|
+\endlines
+
+The key macros are ^@fill@, ^@draw@, ^@filldraw@, and ^@drawdot@.
+\beginlines
+|def fill expr c = addto_currentpicture contour c.t_ enddef;|
+|def addto_currentpicture = addto currentpicture enddef;|
+|def draw expr p =|
+| addto_currentpicture doublepath p.t_ withpen currentpen enddef;|
+|def filldraw expr c = fill counterclockwise c withpen currentpen enddef;|
+|def drawdot expr z = if unknown currentpen_path: def_pen_path_ fi|
+| addto_currentpicture contour|
+| currentpen_path shifted (z.t_) withpen penspeck enddef;|
+|def def_pen_path_ =|
+| hide(currentpen_path=tensepath makepath currentpen) enddef;|
+\endlines
+
+And they have negative counterparts:
+\beginlines
+|def |^|unfill|| expr c = fill c withweight -1 enddef;|
+|def |^|undraw|| expr p = draw p withweight -1 enddef;|
+|def |^|unfilldraw|| expr c = filldraw c withweight -1 enddef;|
+|def |^|undrawdot|| expr z = drawdot z withweight -1 enddef;|
+|def |^|erase|| text t = begingroup interim default_wt_:=-1;|
+| cullit; t withweight -1; cullit; endgroup enddef;|
+|newinternal default_wt_; default_wt_:=1;|
+\endlines
+
+It's more difficult to cut off the ends of a stroke, but the
+following macros (discussed near the end of Chapter~16) do the job:
+\beginlines
+|def |^|cutdraw|| expr p = % caution: you may need autorounding=0|
+| cutoff(point 0 of p, 180+angle direction 0 of p);|
+| cutoff(point infinity of p, angle direction infinity of p);|
+| culldraw p enddef;|
+\smallbreak
+|def |^|culldraw|| expr p = addto pic_ doublepath p.t_ withpen currentpen;|
+| cull pic_ dropping(-infinity,0) withweight default_wt_;|
+| addto_currentpicture also pic_; pic_:=nullpicture; killtext enddef;|
+|vardef |^|cutoff||(expr z,theta) =|
+| interim autorounding := 0; interim smoothing := 0;|
+| addto pic_ doublepath z.t_ withpen currentpen;|
+| addto pic_ contour|
+| (cut_ scaled (1+max(-pen_lft,pen_rt,pen_top,-pen_bot))|
+| rotated theta shifted z)t_;|
+| cull pic_ keeping (2,2) withweight -default_wt_;|
+| addto currentpicture also pic_; pic_:=nullpicture enddef;|
+|picture pic_; pic_:=nullpicture;|
+|path cut_; cut_ = ((0,-1)--(1,-1)--(1,1)--(0,1)--cycle) scaled 1.42;|
+\weakendlines
+The use of ^"default\_wt\_" here makes `^@erase@ @cutdraw@' work. The
+private variable "pic\_" is usually kept equal to @nullpicture@ in
+order to conserve memory space.
+
+Picking up a pen not only sets "currentpen", it also establishes
+the values of ^"pen\_lft", ^"pen\_rt", ^"pen\_top", and ^"pen\_bot",
+which are used by "lft", "rt", "top", and "bot".
+\beginlines
+|def |^|pickup|| secondary q =|
+| if numeric q: numeric_pickup_ else: pen_pickup_ fi q enddef;|
+|def numeric_pickup_ primary q =|
+| if unknown pen_[q]: errmessage "Unknown pen"; clearpen|
+| else: currentpen:=pen_[q];|
+| pen_lft:=pen_lft_[q]; pen_rt:=pen_rt_[q];|
+| pen_top:=pen_top_[q]; pen_bot:=pen_bot_[q];|
+| currentpen_path:=pen_path_[q] fi; enddef;|
+|def pen_pickup_ primary q =|
+| currentpen:=q yscaled aspect_ratio;|
+| pen_lft:=xpart penoffset down of currentpen;|
+| pen_rt:=xpart penoffset up of currentpen;|
+| pen_top:=(ypart penoffset left of currentpen)_o_;|
+| pen_bot:=(ypart penoffset right of currentpen)_o_;|
+| path currentpen_path; enddef;|
+|newinternal pen_lft,pen_rt,pen_top,pen_bot,pen_count_;|
+\endlines
+And saving a pen saves all the relevant values for later retrieval.
+\beginlines
+|vardef |^|savepen|| = pen_[incr pen_count_]=currentpen;|
+| pen_lft_[pen_count_]=pen_lft;|
+| pen_rt_[pen_count_]=pen_rt;|
+| pen_top_[pen_count_]=pen_top;|
+| pen_bot_[pen_count_]=pen_bot;|
+| pen_path_[pen_count_]=currentpen_path;|
+| pen_count_ enddef;|
+\smallbreak
+|def |^|clearpen|| = currentpen:=nullpen;|
+| pen_lft:=pen_rt:=pen_top:=pen_bot:=0;|
+| path currentpen_path; enddef;|
+\smallbreak
+|def clear_pen_memory =|^^@clear\_pen\_memory@
+| pen_count_:=0;|
+| numeric pen_lft_[],pen_rt_[],pen_top_[],pen_bot_[];|
+| pen currentpen,pen_[];|
+| path currentpen_path, pen_path_[];|
+| enddef;|
+\endlines
+
+The four basic pen-edge functions offer no surprises:
+^^"lft"^^"rt"^^"top"^^"bot"
+\beginlines
+|vardef lft primary x = x + if pair x: (pen_lft,0) else: pen_lft fi enddef;|
+|vardef rt primary x = x + if pair x: (pen_rt,0) else: pen_rt fi enddef;|
+|vardef top primary y = y + if pair y: (0,pen_top) else: pen_top fi enddef;|
+|vardef bot primary y = y + if pair y: (0,pen_bot) else: pen_bot fi enddef;|
+\endlines
+There are six functions that ^{round} to good positions for pen placement.
+\beginlines
+|vardef |^|good.x|| primary x = hround(x+pen_lft)-pen_lft enddef;|
+|vardef |^|good.y|| primary y = vround(y+pen_top)-pen_top enddef;|
+|vardef |^|good.lft|| primary z = save z_; pair z_;|
+| (z_+(pen_lft,0))t_=round((z+(pen_lft,0))t_); z_ enddef;|
+|vardef |^|good.rt|| primary z = save z_; pair z_;|
+| (z_+(pen_rt,0))t_=round((z+(pen_rt,0))t_); z_ enddef;|
+|vardef |^|good.top|| primary z = save z_; pair z_;|
+| (z_+(0,pen_top))t_=round((z+(0,pen_top))t_); z_ enddef;|
+|vardef |^|good.bot|| primary z = save z_; pair z_;|
+| (z_+(0,pen_bot))t_=round((z+(0,pen_bot))t_); z_ enddef;|
+\endlines
+
+So much for fixed pens. When pen-like strokes are defined by
+outlines, the ^"penpos" macro is of primary importance. Since "penpos"
+may be used quite frequently, we might as well write out the $x$ and~$y$
+coordinates explicitly instead of using the (somewhat slower) $z$~convention:
+\beginlines
+|vardef penpos@#(expr b,d) =|
+| (x@#r-x@#l,y@#r-y@#l)=(b,0) rotated d;|
+| x@#=.5(x@#l+x@#r); y@#=.5(y@#l+y@#r) enddef;|
+\endlines
+
+Simulated pen strokes are provided by the convenient ^@penstroke@ command.
+\beginlines
+|def penstroke text t =|
+| forsuffixes e = l,r: path_.e:=t; endfor|
+| if cycle path_.l: cyclestroke_|
+| else: fill path_.l -- reverse path_.r -- cycle fi enddef;|
+|def cyclestroke_ =|
+| begingroup interim turningcheck:=0;|
+| addto pic_ contour path_.l.t_ withweight 1;|
+| addto pic_ contour path_.r.t_ withweight -1;|
+| cull pic_ dropping origin withweight default_wt_;|
+| addto_currentpicture also pic_;|
+| pic_:=nullpicture endgroup enddef;|
+|path path_.l,path_.r;|
+\finalendlines
+
+\subsection Proof labels and rules. The next main section of |plain.mf|
+is devoted to macros for the annotations on proofsheets. These macros
+are discussed in Appendix~H\null, and they use the ^@special@ and ^@numspecial@
+commands discussed in Appendix~G.
+
+Labels are generated at the lowest level by @makelabel@\kern1pt:^^"lcode\_"
+\beginlines
+|vardef |^|makelabel||@#(expr s,z) = % puts string s at point z|
+| if known z: special lcode_@# & s;|
+| numspecial xpart(z.t_); numspecial ypart(z.t_) fi enddef;|
+\smallskip
+|string lcode_,lcode_.top,lcode_.lft,lcode_.rt,lcode_.bot,|
+| lcode_.top.nodot,lcode_.lft.nodot,lcode_.rt.nodot,lcode_.bot.nodot;|
+|lcode_.top=" 1"; lcode_.lft=" 2"; lcode_.rt=" 3"; lcode_.bot=" 4";|
+|lcode_=" 0"; % change to " /" to avoid listing in overflow column|
+|lcode_.top.nodot=" 5"; lcode_.lft.nodot=" 6";|
+|lcode_.rt.nodot=" 7"; lcode_.bot.nodot=" 8";|
+\endlines
+
+Users generally don't invoke @makelabel@ directly, because there's a convenient
+shorthand. For example, `@labels@$(1,2,3)$' expands into
+`@makelabel@\kern1pt(|"1"|$,z_1$); @makelabel@\kern1pt(|"2"|$,z_2$);
+@makelabel@\kern1pt(|"3"|$,z_3$)'.
+\ (But nothing happens if ^"proofing"$\null\le1$.)
+\beginlines
+|vardef |^|labels||@#(text t) =|
+| if proofing>1: forsuffixes $=t: makelabel@#(str$,z$); endfor fi enddef;|
+|vardef |^|penlabels||@#(text t) =|
+| if proofing>1: forsuffixes $$=l,,r: forsuffixes $=t:|
+| makelabel@#(str$.$$,z$.$$); endfor endfor fi enddef;|
+\endlines
+When there are lots of purely numeric labels, you can say, e.g.,
+\begindisplay
+@labels@(1, @range@ 5 @thru@ 9, @range@ 100 @thru@ 124, 223)
+\enddisplay
+which is equivalent to `@labels@$(1,5,6,7,8,9,100,101,\ldots,124,223)$'.
+Labels are omitted from the proofsheets if the corresponding $z$ value
+isn't known, so it doesn't hurt (much) to include unused subscript numbers
+in a range.
+\beginlines
+|def |^|range|| expr x = numtok[x] enddef;|
+|def |^|numtok|| suffix x=x enddef;|
+|tertiarydef m |^|thru|| n =|
+| m for x=m+1 step 1 until n: , numtok[x] endfor enddef;|
+\weakendlines
+(This @range@ abbreviation will work in any ^@forsuffixes@ list;
+and in a `@for@' list you can even omit the word `@range@'.
+But you might fill~up \MF's main memory if too many values are involved.)
+
+A straight line will be drawn on the proofsheet by @proofrule@.
+Although @makelabel@ takes the current transform into account,
+@proofrule@ does not. There's also a corresponding routine `@screenrule@'
+that puts a straight line in the current picture, so that design
+guidelines will be visible on your screen:
+\beginlines
+|def |^|proofrule||(expr w,z) =|
+| special "rule"; numspecial xpart w; numspecial ypart w;|
+| numspecial xpart z; numspecial ypart z enddef;|
+|def |^|screenrule||(expr w,z) =|
+| addto currentpicture doublepath w--z withpen rulepen enddef;|
+|pen rulepen; rulepen = pensquare scaled 2;|
+\endlines
+(The ^"rulepen" is two pixels wide, because screen rules are usually
+drawn exactly over raster lines. A two-pixel-wide pen straddles the pixel
+edges so that you can ``see'' the correct line position. If
+a two-pixel-wide line proves to be too dark, you can redefine
+"rulepen" to be simply ^@pensquare@; then \MF\ will draw the
+thinnest possible screen rule, but it will be
+a half-pixel too high and a half-pixel too far to the right.)
+
+You can produce lots of proof rules with ^@makegrid@, which connects
+an arbitrary list of $x$~coordinates with an arbitrary list
+of $y$~coordinates:
+\beginlines
+|def makegrid(text xlist,ylist) =|
+| xmin_ := min(xlist); xmax_ := max(xlist);|
+| ymin_ := min(ylist); ymax_ := max(ylist);|
+| for x=xlist: proofrule((x,ymin_), (x,ymax_)); endfor|
+| for y=ylist: proofrule((xmin_,y), (xmax_,y)); endfor|
+| enddef;|
+\endlines
+
+Finally we have a few macros that allow further communication with
+the hardcopy proof-drawing routine of Appendix~H\null. You can change the
+fonts, the thickness of proof rules, and the position of the image
+on its page.
+\beginlines
+|vardef |^|titlefont|| suffix $ = special "titlefont "&str$ enddef;|
+|vardef |^|labelfont|| suffix $ = special "labelfont "&str$ enddef;|
+|vardef |^|grayfont|| suffix $ = special "grayfont "&str$ enddef;|
+|vardef |^|slantfont|| suffix $ = special "slantfont "&str$ enddef;|
+|def |^|proofoffset|| primary z = % shift proof output by z|
+| special "offset"; numspecial xpart z; numspecial ypart z enddef;|
+|vardef |^|proofrulethickness|| expr x =|
+| special "rulethickness"; numspecial x enddef;|
+\finalendlines
+
+\subsection Character and font administration.
+After this elaborate preparation, we're finally ready
+to consider the @beginchar@$\,\ldots\,$@endchar@
+framework for the individual characters of a font. Each ^@beginchar@ begins
+a group, which should end at the next ^@endchar@. Then @beginchar@
+stores the given character code and device-independent
+box dimensions in \MF's internal variables ^"charcode", ^"charwd",
+^"charht", and ^"chardp". Then it computes the device-dependent box
+dimensions ^"w", ^"h", and~^"d". Finally it
+clears the $z$ variables, the current picture, and the
+current pen.
+\beginlines
+|def beginchar(expr c,w_sharp,h_sharp,d_sharp) =|
+| begingroup|
+| charcode:=if known c: byte c else: 0 fi;|
+| charwd:=w_sharp; charht:=h_sharp; chardp:=d_sharp;|
+| w:=hround(charwd*hppp); h:=vround(charht*hppp); d:=vround(chardp*hppp);|
+| charic:=0; clearxy; clearit; clearpen; scantokens extra_beginchar;|
+| enddef;|
+\endlines
+The ^{italic correction} is normally zero, unless the user gives an
+`^@italcorr@' command; even then, the correction stays zero unless
+the given value is positive:
+\beginlines
+|def italcorr expr x_sharp = if x_sharp>0: charic:=x_sharp fi enddef;|
+\endlines
+When we want to change the pixel width $w$ from even to odd or vice
+versa, the ^@change\_width@ macro does the right thing.
+\beginlines
+|def change_width =|
+| w:=w if w>charwd*hppp:- else:+ fi 1 enddef;|
+\endlines
+(The user might also decide to change $w$ in some other way.) \ The
+current value of~$w$ at the time of @endchar@ will be the
+``official'' pixel width of the character, ^"chardx", that is
+shipped to the |gf| output file.
+\beginlines
+|def endchar =|
+| scantokens extra_endchar;|
+| if proofing>0: makebox(proofrule); fi|
+| chardx:=w; % desired width of the character in pixels|
+| shipit;|
+| if displaying>0: makebox(screenrule); showit; fi|
+| endgroup enddef;|
+\endlines
+Extensions to these routines can be provided by putting commands in the
+string variables ^"extra\_beginchar" and ^"extra\_endchar".
+\beginlines
+|string extra_beginchar, extra_endchar;|
+|extra_beginchar=extra_endchar="";|
+\endlines
+
+A ``^{bounding box}'' that surrounds the character according to the
+specifications given in @beginchar@ is produced by ^@makebox@, which
+takes into account the possibility that pixels might not be square.
+An extra line is drawn to mark the width of the character with its
+^{italic correction} included, if this correction is nonzero.
+\beginlines
+|def makebox(text r) =|
+| for y=0,h.o_,-d.o_: r((0,y),(w,y)); endfor % horizontals|
+| for x=0,w: r((x,-d.o_),(x,h.o_)); endfor % verticals|
+| if charic<>0: r((w+charic*hppp,h.o_),(w+charic*hppp,.5h.o_)); fi|
+| enddef;|
+\endlines
+
+The ^@maketicks@ routine is an alternative to @makebox@ that draws less
+conspicuous lines. This makes it easier to visualize a character's
+appearance near the edges of its bounding box.
+\beginlines
+|def maketicks(text r) =|
+| for y=0,h.o_,-d.o_: r((0,y),(10,y)); r((w-10,y),(w,y)); endfor|
+| for x=0,w: r((x,10-d.o_),(x,-d.o_)); r((x,h.o_-10),(x,h.o_)); endfor|
+| if charic<>0: r((w+charic*hppp,h.o_-10),(w+charic*hppp,h.o_)); fi|
+| enddef;|
+\endlines
+
+Overall information about the font as a whole is generally supplied
+^^@font\_size\_etc@
+by the following commands, which are explained in Appendix~F\null.
+\beginlines
+|def font_size expr x = designsize:=x enddef;|
+|def font_slant expr x = fontdimen 1: x enddef;|
+|def font_normal_space expr x = fontdimen 2: x enddef;|
+|def font_normal_stretch expr x = fontdimen 3: x enddef;|
+|def font_normal_shrink expr x = fontdimen 4: x enddef;|
+|def font_x_height expr x = fontdimen 5: x enddef;|
+|def font_quad expr x = fontdimen 6: x enddef;|
+|def font_extra_space expr x = fontdimen 7: x enddef;|
+\smallskip
+|def font_identifier expr x = font_identifier_:=x enddef;|
+|def font_coding_scheme expr x = font_coding_scheme_:=x enddef;|
+|string font_identifier_, font_coding_scheme_;|
+|font_identifier_=font_coding_scheme_="UNSPECIFIED";|
+\finalendlines
+\bigskip
+
+\subsection The endgame. What have we left out? A few miscellaneous
+things still need to be handled. First, we almost forgot to define the
+^"z"~convention for points:
+\beginlines
+|vardef z@#=(x@#,y@#) enddef;|
+\endlines
+Then we need to do something rudimentary about \MF's ``windows.''
+^^"screen\_rows" ^^"screen\_cols"
+\beginlines
+|newinternal screen_rows, screen_cols, currentwindow;|
+|screen_rows:=400; % these values should be corrected,|
+|screen_cols:=500; % by reading in a separate file after plain.mf|
+\smallskip
+|def |^|openit|| = openwindow currentwindow|
+| from origin to (screen_rows,screen_cols) at (-50,300) enddef;|
+|def |^|showit|| = openit; let showit=showit_; showit enddef; % first time only|
+|def showit_ = display currentpicture inwindow currentwindow enddef;|
+\endlines
+Plain \MF\ has several other terse commands like `@openit@' and `@showit@':
+\beginlines
+|def |^|clearxy|| = save x,y enddef;|
+|def |^|clearit|| = currentpicture:=nullpicture enddef;|
+|def |^|shipit|| = shipout currentpicture enddef;|
+|def |^|cullit|| = cull currentpicture dropping (-infinity,0) enddef;|
+\endlines
+
+\medbreak
+The next several macros are handy things to put on your ^{command line}
+when you are starting a \MF\ job (i.e., just before `|input| \<font file
+name>'):
+
+\smallskip\item\bull |screenchars|. Say this when you're making a font
+but want the characters to be displayed just before they are shipped out.
+\item\bull |screenstrokes|. Say this when you're in "proof" mode
+and want to see each stroke as it's added to the current picture.
+\item\bull |imagerules|. Say this when you want to include the bounding box
+in the current character, before you begin to draw it.
+\item\bull |gfcorners|. Say this when you expect to make proofsheets
+with large pixels, from a low-resolution font.
+\item\bull |nodisplays|. Say this to save computer time when you don't
+want "proof" mode to display each character automatically.
+\item\bull |notransforms|. Say this to save computer time when you know
+that the current transform is the identity.
+\beginlines
+|def |^|screenchars|| = % endchar should `showit'|
+| extra_endchar:=extra_endchar&"showit;" enddef;|
+\smallskip
+|def |^|screenstrokes|| = % every stroke should `showit'|
+| def addto_currentpicture text t=|
+| addto currentpicture t; showit enddef; enddef;|
+\smallbreak
+|def |^|imagerules|| = % a box should be part of the character image|
+| extra_beginchar:=extra_beginchar & "makebox(screenrule);" enddef;|
+\smallbreak
+|def |^|gfcorners|| = % `maketicks' should send rules to the gf file|
+| extra_setup:=extra_setup & "let makebox=maketicks;proofing:=1;" enddef;|
+\smallbreak
+|def |^|nodisplays|| = % endchar shouldn't `showit'|
+| extra_setup:=extra_setup & "displaying:=0;" enddef;|
+\smallskip
+|def |^|notransforms|| = % currenttransform should not be used|
+| let t_ = \ enddef;|
+\endlines
+
+We make `^@bye@' synonymous with `^@end@', just in case \TeX\ users expect
+\MF\ programs to end like \TeX\ documents do.
+\beginlines
+|let bye = end; outer end,bye;|
+\endlines
+
+And finally, we provide the default environment that a user gets when
+^^@clear\_pen\_memory@ ^^@mode\_setup@
+simple experiments like those at the beginning of Chapter~5 are desired.
+\beginlines
+|clear_pen_memory; % initialize the `savepen' mechanism|
+|mode_setup; % establish proof mode as the default|
+|numeric |^|mode||,|^|mag||; % but leave mode and mag undefined|
+\weakendlines
+Whew! That's the end of the |plain.mf| file.
+
+\subsection Adapting to local conditions. In order to make plain \MF\
+programs interchangeable between different computers, everybody should use
+the same |plain.mf| base. But there are some things that clearly should
+be customized at each installation:
+
+\smallskip\item\bull Additional modes should be defined, so that fonts
+can be made for whatever output devices are of interest.
+
+\item\bull The proper ^"localfont" mode should be established.
+
+\item\bull The correct numbers should be assigned to
+^"screen\_rows" and ^"screen\_cols".
+
+\smallbreak
+\noindent
+Here's an example of a supplementary file `|local.mf|'
+that would be appropriate for a computer center with the
+hypothetical "cheapo" and "luxo" printers described in Chapter~11.
+We assume that "cheapo" mode is to be identical to "lowres" mode,
+except that the "cheapo" fonts should be generated with a {\sl negative\/}
+value of ^"fillin" (because "cheapo" tends to make diagonal lines lighter
+than normal, not heavier). The terminal screens are assumed to be
+768 pixels wide and 512 pixels high.
+\beginlines
+|% A file to be loaded after "plain.mf".|
+|base_version:=base_version&"/drofnats";|
+\smallskip
+|screen_rows:=512; screen_cols:=768;|
+\smallskip
+|mode_def cheapo = % cheapo mode: to generate fonts for cheapo|
+| lowres_; % do as in lowres mode, except:|
+| fillin:=-.1; % compensate for lighter diagonals|
+| enddef;|
+\smallskip
+|mode_def luxo = % luxo mode: to generate fonts for luxo|
+| proofing:=0; % no, we're not making proofs|
+| fontmaking:=1; % yes, we are making a font|
+| tracingtitles:=1; % yes, show titles online|
+| pixels_per_inch:=2000; % almost 30 pixels per pt|
+| blacker:=.2; % make pens a teeny bit blacker|
+| fillin:=.1; % but compensate for heavy diagonals|
+| o_correction:=1; % and keep the full overshoot|
+| enddef;|
+\smallskip
+|localfont:=cheapo;|
+\weakendlines
+The macro `^@bye@' might also be redefined, as suggested at the close
+of Appendix~F.
+
+To prepare a preloaded base file at this installation, a suitably
+privileged person should run ^|INIMF| in the following way:
+\begintt
+This is METAFONT, Version 2.0 (INIMF) 8 NOV 1989 10:09
+**plain
+(plain.mf
+Preloading the plain base, version 2.0)
+*input local
+(local.mf)
+*dump
+Beginning to dump on file plain.base
+\endtt
+^^{dump}(The stuff after `|**|' or `|*|' is typed by the user; everything
+else is typed by the system. A few more messages actually come out.)
+
+Notice that |local.mf| does not include any new macros or features that a
+programmer could use in a special way. Therefore it doesn't make plain
+\MF\ incompatible with implementations at other computing centers.
+
+Changes and/or extensions to the |plain.mf| macros should never be made,
+unless the resulting base file is clearly distinguished from the standard
+plain base. But new, differently named bases are welcome.
+For example, the author prepared a special base for the
+^{Computer Modern} fonts, so that they could be generated without first
+reading the same 700 lines of macro definitions each time. To load
+this base at high speed, he can type `|&cm|' after \MF's initial
+`|**|'. ^^{asterisk} ^^{ampersand} (Or, on some machines, he has a special
+version called `|cmmf|' in which the new base is already present.)
+
+\endchapter
+
+None but the Base, in baseness doth delight.
+\author MICHAEL ^{DRAYTON}, {\sl Robert, Duke of Normandy\/} (1605)
+ % line 262; quite different in the 1596 version
+
+\bigskip
+
+So far all was plain sailing, as the saying is;
+but Mr.\thinspace Till knew that his main difficulties were yet to come.
+\author FRANCIS E. ^{PAGET}, {\sl Milford Malvoisin\/} (1842) % p209
+ % in the 22nd paragraph of Chapter 9
+
+\eject
+ \beginchapter Appendix C. Character\\Codes
+
+Different computers tend to have different ways of representing the
+characters in files of text, but \MF\ gives the same results on
+all machines, because it converts everything to a standard internal
+code when it reads a file. \MF\ also converts back from its internal
+representation to the appropriate external code, when it writes
+a file of text; therefore most users need not be aware of the fact
+that the ^{codes} have actually switched back and forth inside the machine.
+
+The purpose of this appendix is to define \MF's internal code,
+which has the same characteristics on all implementations of \MF\!\null.
+The existence of such a code is important, because it
+makes \MF\ programs portable.
+\MF's scheme is based on the American Standard Code for
+Information Interchange, known popularly as ``^{ASCII}.'' There are
+128 codes, numbered 0~to~127; we conventionally express the numbers
+in ^{oct}al notation, from |oct"000"| to |oct"177"|, or in
+^{hex}adecimal notation, from |hex"00"| to |hex"7F"|. Thus, the value of
+|ASCII"b"| is normally called |oct"142"| or |hex"62"|, not 98. In the
+^{ASCII} scheme, codes |oct"000"| through |oct"037"| and
+code |oct"177"| are~assigned to special functions; for example,
+code |oct"007"| is called |BEL|, and it means ``Ring the bell.''
+The other 95 codes are assigned to visible symbols and to the
+blank space character. Here is a
+chart that shows ASCII codes in such a way that octal and hexadecimal
+equivalents can easily be read off:
+\beginchart{\global\count255='41\postdisplaypenalty=0\tentt
+ \def\chartstrut{\lower4.3pt\vbox to13.6pt{}}}
+&\oct{00x}&&NUL&&SOH&&STX&&ETX&&EOT&&ENQ&&ACK&&BEL&&\oddline0
+&\oct{01x}&&BS&&HT&&LF&&VT&&FF&&CR&&SO&&SI&\evenline
+&\oct{02x}&&DLE&&DC1&&DC2&&DC3&&DC4&&NAK&&SYN&&ETB&&\oddline1
+&\oct{03x}&&CAN&&EM&&SUB&&ESC&&FS&&GS&&RS&&US&\evenline
+&\oct{04x}&& &&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline2
+&\oct{05x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{06x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline3
+&\oct{07x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{10x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline4
+&\oct{11x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{12x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline5
+&\oct{13x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{14x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline6
+&\oct{15x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{16x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline7
+&\oct{17x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&DEL&\evenline
+\endchart
+
+Ever since ASCII was established in the early 1960s, people have had
+different ideas about what to do with positions |oct"000"| thru |oct"037"| and
+|oct"177"|, because most of the functions assigned to those codes are
+appropriate only for special purposes like file transmission, not for
+applications to printing or to interactive computing.
+Manufacturers soon started producing line printers that were capable of
+generating 128 characters, 33~of~which were tailored to the special needs
+of particular customers; part of the advantage of a standard code was
+therefore lost.
+An extended ASCII code intended for text editing and interactive computing
+ was developed at several universities about 1965, and
+for many years there have been terminals in use at Stanford, MIT,
+Carnegie-Mellon, and elsewhere that have 120 or~121 symbols, not just~95.
+For example, the author ^^{Knuth} developed \MF\ on a keyboard that
+includes the symbols `{\tentex\char'32}', `{\tentex\char'34}',
+`{\tentex\char'35}', and `{\tentex\char'30}', which are easier to use than
+the character pairs `{\tentex^{<>}}', `{\tentex^{<=}}', `{\tentex^{>=}}',
+and `{\tentex^{:=}}'. The full character set looks like this:
+\beginchart{\tentex\postdisplaypenalty=0}
+\normalchart
+\endchart
+\MF\ can also be configured to accept any or all of the character codes
+128--255.
+However, \MF\ programs that make use of anything in addition to the 95
+standard ASCII characters cannot be expected to run on other systems, so
+the use of extended character sets is discouraged.
+
+A possible middle ground has been suggested, based on the fact that
+it's easy to write a
+program that converts extended-character files into standard files by
+substituting `|<>|' for `{\tentex\char'32}', etc. In the author's
+implementation at Stanford, the symbols
+`{\tentex\char'32}', `{\tentex\char'34}', `{\tentex\char'35}',
+and `{\tentex\char'30}' are considered to be in the same class as
+`{\tentex<}', `{\tentex=}', `{\tentex:}', and `{\tentex>}' when tokens are
+formed (see Chapter~6). Tokens like `{\tentex\char'32=}'
+and `{\tentex<\char'35}' are therefore distinct, although
+they both become `{\tentex<>=}' after
+conversion. As long as such tokens are avoided,
+the author's programs can easily be expurgated into a
+portable form for general distribution. \ (Another feasible approach would
+have been to convert nonstandard codes to character pairs during \MF's
+input process; that would have been slightly less efficient.)
+
+Computers with non-ASCII character sets should specify a correspondence
+between 95 distinct characters and the standard ASCII codes |oct"040"|
+thru |oct"176"|. \MF\ programs written on any such machines will be
+completely interchangeable with each other.
+
+\endchapter
+
+If any shall suggest, that some of the Enquiries here insisted upon
+(as particularly those about the Letters of the Alphabet)
+do seem too minute and trivial, for any prudent Man
+to bestow his serious thoughts and time about.
+Such Persons may know, that the discovery
+of the true nature and Cause of any the most minute thing,
+doth promote real Knowledge,
+and therefore cannot be unfit for any Mans endeauours,
+who is willing to contribute to the advancement of Learning.
+\author JOHN ^{WILKINS}, {\sl Towards a Real Character\/} (1668)
+ % preface to the reader
+
+\bigskip
+
+Clearly even the simple A.B.C.~is a thing of mystery.
+Like all codes, it should not be trifled with,
+but it is to be feared that in modern times
+it has not always been respected.
+\author STANLEY ^{MORISON}, {\sl On Type Faces\/} (1923)
+ % from the introduction, near the beginning
+\eject
+ \beginchapter Appendix D. Dirty Tricks
+
+Any powerful computer language can be used in ways that go considerably
+beyond what the language designer originally had in mind, especially
+when macro expansion is possible. Sometimes the unexpected constructions
+are just amusing; sometimes they are disgustingly arcane. But
+sometimes they turn out to be quite useful, and they graduate from ``tricks''
+to the status of ``techniques.'' \ (For example, several of the macros
+now in Appendix~B started out as suggestions for Appendix~D\null.) \
+In any case, gurus of a language always like to explore its limits.
+The depths of \MF\ have hardly been plumbed, but this appendix probably
+reached a new low at the time it was written.
+
+Acknowledgment: More than half of the ideas in this appendix are due to
+John ^{Hobby}, who has been a tireless and inspiring co-worker during the
+entire development of the new \MF\ system.
+
+\advance\medskipamount by -.5pt
+\advance\abovedisplayskip by -.5pt
+\advance\belowdisplayskip by -.5pt
+
+\ninepoint\medskip
+\setbox0=\hbox spread-7\fontdimen4\font % that removes all the shrinkability
+ {\strut Please don't read this material until you've had}
+\setbox1=\hbox to\wd0{\strut plenty of experience with plain \MF\!.}
+\line{\leaders\hbox{\dbend\kern2pt}\hfil\vtop{\box0\box1}}
+\nointerlineskip
+\noindent\strut After you have read and understood the secrets below, you'll
+know all sorts of devious combinations of \MF\ commands,
+and you will often be tempted to write inscrutable macros. Always remember,
+however, that there's usually a simpler and better way to do something
+than the first way that pops into your head. You may not have to
+resort to any subterfuge at all, since \MF\ is able to do lots of things
+in a straightforward way. Try for simple solutions first.
+
+\subsection Macro madness. If you need to write complicated ^{macros}, you'll
+need to be familiar with the fine points in Chapter~20. \MF's symbolic tokens
+are divided into two main categories, ``expandable'' and ``unexpandable'';
+the former category includes all macros and @if@$\,\ldots\,$@fi@ tests and
+@for@$\,\ldots\,$@endfor@ loops, as well as special operations like @input@,
+while the latter category includes the primitive operators and commands
+listed in Chapters 25 and~26. The expansion of expandable tokens takes place
+in \MF's ``^{mouth},'' but primitive statements (including equations,
+declarations, and the various types of commands) are done in \MF's
+``^{stomach}.'' There's a communication between the two, since the stomach
+evaluates expressions that are needed as arguments to the mouth's macros;
+any statement can be embedded in a group expression, so arbitrarily
+complicated things can be done as part of the ^{expansion} process.
+
+Let's begin by considering a toy problem that is treated at the beginning
+of Appendix~D in {\sl The \TeX book}, in case some readers are interested in
+comparing \TeX\ to \MF\!\null. Given a numeric variable $n\ge0$, we wish to
+define a macro |asts| whose replacement text consists of precisely
+$n$~asterisks. This task is somewhat tricky because expansion is suppressed
+when a replacement text is being read; we want to use a ^@for@ loop, but
+loops are special cases of expansion. In other words,
+\begintt
+def asts = for x=1 upto n: * endfor enddef
+\endtt
+defines |asts| to be a macro with a @for@ loop in its replacement text;
+\advance\belowdisplayskip by -1.5pt
+in practice, |asts| would behave as if it contained $n$ asterisks (using
+possibly different values of~$n$), but
+we have not solved the stated problem. The alternative
+\begintt
+def makedef primary n =
+ def asts = for x=1 upto n: * endfor enddef enddef;
+makedef n
+\endtt
+``freezes'' the present value of $n$; but this doesn't solve the problem either.
+
+\advance\medskipamount by .5pt
+\advance\abovedisplayskip by .5pt
+\advance\belowdisplayskip by 2pt
+
+\goodbreak
+
+One solution is to build up the definition by adding one asterisk at a time,
+using ^@expandafter@ as follows:
+\begintt
+def asts = enddef;
+for x=1 upto n:
+ expandafter def expandafter asts expandafter = asts * enddef;
+endfor.
+\endtt
+The three @expandafter@s provide a ``finger'' into the replacement text,
+before @def@ suppresses expansion; without them the replacement text
+would turn out to be `|asts|~|*|', causing infinite recursion.
+
+This solution involves a running time proportional to $n^2$, so the
+reader might wonder why a simpler approach like
+\begintt
+expandafter def expandafter asts expandafter =
+ for x = 1 upto n: * endfor enddef
+\endtt
+wasn't suggested? The reason is that this doesn't work, unless $n=0$!
+A @for@ loop isn't entirely expanded by @expandafter@; only \MF's first
+step in loop expansion is carried out. Namely, the loop text is read,
+and a special ^{inaccessible} token `^|ENDFOR|' is placed at its end.
+Later on when \MF's mouth encounters `|ENDFOR|' (which incidentally is an
+expandable token, but it wasn't listed in Chapter~20), the loop text is
+re-inserted into the input stream, unless of course the loop has finished.
+The special |ENDFOR| is an `^@outer@' token, hence it should not
+appear in replacement texts; \MF\ will therefore stop with a ``^{forbidden
+token}'' error if you try the above with $n\ge1$.
+^^@inner@ You might try to defeat the outerness by saying
+\begintt
+for x=1: inner endfor;
+\endtt
+but \MF\ won't let you. And even if this had worked, it wouldn't have
+solved the problem; it would simply have put |ENDFOR| into the
+replacement text of |asts|, because expansion is inhibited when the
+replacement text is being read.
+
+There's another way to solve the problem that seems to have running
+time proportional to~$n$ rather than~$n^2$:
+\begintt
+scantokens("def asts=" for x=1 upto n: & "* " endfor) enddef;
+\endtt
+^^{*} but actually \MF's string ^{concatenation}
+operation takes time proportional to the length of the strings it
+deals with, so the running time is still order~$n^2$.
+Furthermore, the ^{string} operations in \MF\ are rather primitive,
+because this isn't a major aspect of the language; so it turns out that
+this approach uses order~$n^2$ storage cells in the string pool, although
+they are recycled later. Even if the ^{pool size} were infinite, \MF's
+``^{buffer size}'' would be exceeded for large~$n$, because ^@scantokens@
+puts the string into the input buffer before scanning it.
+
+Is there a solution of order $n$? Yes, of course. For example,
+\begintt
+def a=a* enddef;
+for x=0 upto n:
+ if x=n: def a=quote quote def asts = enddef; fi
+ expandafter endfor a enddef;
+showtoken asts.
+\endtt
+(The first `^@quote@' is removed by the @for@, hence one will survive until
+$a$~is redefined. If you don't understand this program, try ^^{debugging tricks}
+running it with $n=3$; insert an isolated expression `|0;|' just before
+the~`|if|', and look at the lines of context that are shown when
+\MF\ gives you four error messages.) \ The only flaw in this method is
+that it uses~up $n$ cells of stack space; \MF's ^{input stack size}
+may have to be increased, if $n$ is bigger than 25~or~so.
+
+\smallbreak
+The asterisk problem is just a puzzle; let's turn now to a genuine
+application. Suppose we want to define a macro called `"ten"' ^^{Derek}
+whose replacement text is the contents of the parameter file ^|logo10.mf|
+in Chapter~11, up to but {\sl not\/} including the last two lines of
+that file. Those last two lines say
+\begintt
+input logo % now generate the font
+end % and stop.
+\endtt
+The "ten" macro will make it possible to set up the 10-point parameters
+repeatedly (perhaps alternating with 9-point parameters in a "nine" macro);
+Appendix~E explains how to create a meta-design tool via such macros.
+
+One idea would be to try to input the entire file |logo10.mf| as the
+replacement text for "ten". We could nullify the effect of the last three
+unwanted tokens by saying
+\begintt
+save input,logo,end;
+forsuffixes s=input,logo,end: let s=\; endfor
+\endtt
+just before "ten" is used. To get the entire file as a replacement text,
+we can try one of the approaches that worked in the asterisk problem, say
+\begintt
+expandafter def expandafter ten expandafter = input logo10 enddef.
+\endtt
+But this first attempt runs awry if we haven't already redefined `^@end@';
+Appendix~B makes `@end@' an `^@outer@' token, preventing its appearance
+in replacement texts. So we say `^@inner@ @end@'
+and try again, only to discover an unwritten law that somehow never
+came up in Chapters 20 or~26:
+\begintt
+Runaway definition?
+font_size10pt#;ht#:=6pt#;xgap#:=0.6pt#;u#:=4/9pt#;s#:=0;o#:=1/ ETC.
+! File ended while scanning the definition of ten.
+<inserted text>
+ enddef
+l.2 ...fter ten expandafter = input logo10
+ enddef;
+\endtt
+^^|Runaway| ^^|File ended...|
+The ^{end of a file} is invisible; but it's treated like an `@outer@'
+token, in the sense that a file should never end when \MF\ is passing
+rapidly over text.
+
+Therefore this whole approach is doomed to failure. We'll have to find a
+way to stop the replacement text before the file ends.
+OK, we'll redefine `@input@' so that it means `@enddef@\kern1pt', and
+redefine "logo" so that it means `^@endinput@'.
+\begintt
+let INPUT = input; let input = enddef; let logo = endinput;
+expandafter def expandafter ten expandafter = INPUT logo10;
+showtoken ten.
+\endtt
+It works! By the way, the line with three expandafters can be replaced by
+a more elegant construction that uses @scantokens@ as follows:
+\begintt
+scantokens "def ten=" INPUT logo10;
+\endtt
+This does the job because \MF\ always looks ahead and expands the token
+immediately following an expression that is being evaluated. \ (The
+expression in this case is the string |"def|~|ten="|, which is an argument
+to @scantokens@. The token that immediately follows an expression
+almost always needs to be examined in order to be sure that the expression
+has ended, so \MF\ always examines it.) \ Curiously, the @expandafter@
+alternative causes "ten"'s replacement text to begin with the tokens
+`|font_size10pt#;ht#:=...|', while the @scantokens@ way makes it start
+with `|designsize:=(10);ht#:=...|'. Do you see why? In the second case,
+expansion continued until an unexpandable token (`|designsize|') was
+found, so the |font_size| macro was changed into its replacement text; but
+@expandafter@ just expanded `|INPUT|'.
+
+Now let's make the problem a bit harder. Suppose we know that `|input|'
+comes at the end of where we want to read, but we don't know that `|logo|'
+will follow. We know that some program file name will be there, but
+it might not be for the logo font. Furthermore, let's assume that `|end|'
+might not be present; therefore we can't simply redefine it to be @enddef@.
+In this case we can make `|input|' into a right ^{delimiter}, and
+read the file as a {\sl delimited ^{text argument}\/}; ^^{argument}
+that will give us enough time to insert other tokens, which will
+terminate the input and flush the unwanted file name. But the construction
+is more complex:
+\begintt
+let INPUT = input; delimiters begintext input;
+def makedef(expr name)(text t) =
+ expandafter def scantokens name = t enddef;
+ endinput flushfilename enddef;
+def flushfilename suffix s = enddef;
+makedef("ten") expandafter begintext INPUT logo10;
+showtoken ten.
+\endtt
+This example merits careful study, perhaps with `^@tracingall@' to
+show exactly how \MF\ proceeds. We have assumed that the unknown file
+name can be parsed as a suffix; this solves the problem that a file cannot
+end inside of a @text@ parameter or a false condition. \ (If we knew that
+`@end@' were present, we could have replaced `|endinput|~|flushfilename|' by
+`|if|~|false:|' and redefined `|end|' to be `|fi|'.)
+
+Let's turn now to a simpler problem. \MF\ allows you to consider the
+`^{and}' of two Boolean expressions, but it always evaluates both
+expressions. This is problematical in situations like
+\begintt
+if pair x and (x>(0,0)): A else: B fi
+\endtt
+because the expression `|x>(0,0)|' will stop with an error message
+unless $x$ is of type @pair@. The obvious way to avoid this error,
+\begintt
+if pair x: if x>(0,0): A else: B fi else: B fi
+\endtt
+is cumbersome and requires |B| to appear twice. What we want is a
+``^{conditional and}'' operation in which the second Boolean expression is
+evaluated only if the first one turns out to be true; then we can safely write
+\begintt
+if pair x cand (x>(0,0)): A else: B fi.
+\endtt
+Similarly we might want ``^{conditional or}'' in which the second operand is
+evaluated only if the first is false, for situations like
+\begintt
+if unknown x cor (x<0): A else: B fi.
+\endtt
+Such ^|cand| and ^|cor| macros can be defined as follows:
+\begintt
+def cand(text q) = startif true q else: false fi enddef;
+def cor(text q) = startif true true else: q fi enddef;
+tertiarydef p startif true = if p: enddef;
+\endtt
+the ^{text arguments} are now evaluated only when necessary. We have essentially
+^^@if@ replaced the original line by
+\begintt
+if if pair x: x>(0,0) else: false fi: A else: B fi.
+\endtt
+This construction has one catch; namely, the right-hand operands of |cand| and
+|cor| must be explicitly enclosed in delimiters. But delimiters are only a
+minor nuisance, because the operands to `and' and `or' usually need them
+anyway. It would be impossible to make |cand| and |cor| obey the normal
+expression ^{hierarchy}; when macros make primary/secondary/tertiary
+distinctions, they evaluate their arguments, and such evaluation is
+precisely what |cand| and |cor| want to avoid.
+
+If these |cand| and |cor| macros were changed so that they took
+{\sl undelimited\/} text arguments, the text argument wouldn't stop at a colon.
+We could, however, use such modified macros with ^{group delimiters}
+instead. For example, after
+\begintt
+let {{ = begingroup; let }} = endgroup;
+def cand text q = startif true q else: false fi enddef
+\endtt
+we could write things like
+\begintt
+if {{pair x cand x>(0,0)}}: A else: B fi.
+\endtt
+(Not that this buys us anything; it just illustrates a property of
+undelimited text arguments.) \ Group delimiters
+are not valid delimiters of {\sl delimited\/} text arguments.
+
+Speaking of group delimiters, the gratuitous ^@begingroup@ and ^@endgroup@
+tokens added by ^@vardef@ are usually helpful, but they can be a nuisance.
+For example, suppose we want to write a |zz|~macro such that
+`|zz1..zz2..zz3|' expands into
+\begintt
+z1{dz1}..z2{dz2}..z3{dz3}
+\endtt
+It would be trivial to do this with @def@:
+\begintt
+def zz suffix $ = z${dz$} enddef;
+\endtt
+but this makes |zz| a ``^{spark}.'' Let's suppose that we want to use
+@vardef@, so that |zz| will be usable in suffixes of variable names.
+Additional @begingroup@ and @endgroup@ delimiters will mess up the
+syntax for paths, so we need to get rid of them. Here's one way to
+finesse the problem:
+\begintt
+vardef zz@# =
+ endgroup gobbled true z@#{dz@#} gobble begingroup enddef.
+\endtt
+The ^|gobbled| and ^|gobble| functions of Appendix~B will remove the ^{vacuous
+expressions} `@begingroup@~@endgroup@' at the beginning and end of
+the replacement text.
+
+(The initial @begingroup@ @endgroup@ won't be gobbled if the vardef is
+being read as a primary instead of as a secondary, tertiary, or
+expression. But in such cases you probably don't mind having @begingroup@
+present.)
+
+\subsection Fortuitous loops.
+The `^{max}' and `^{min}' macros in Appendix~B make use of the fact
+that commas are like `|)(|' in argument lists. Although the
+definition heading is
+\begintt
+def max(expr x)(text t)
+\endtt
+we can write `max$(a,b,c)$' and this makes $x=a$ and $t=\null$`$b,c$'.
+Of course, a person isn't supposed to say `max$(a)(b)(c)$'.
+
+Here are two more applications of the idea: We want `^{inorder}$(a,b,c)$'
+to be true if and only if $a\le b\le c$; and we want
+`^@equally\_spaced@$(x_1,x_2,x_3)\,"dx"$' to produce the equations
+`$x_2-x_1=x_3-x_2=dx$'.
+\begintt
+def inorder(expr x)(text t) =
+ ((x for u=t: <= u)
+ and (u endfor gobbled true true)) enddef;
+def equally_spaced(expr x)(text t) expr dx =
+ x for u=t: - u = u endfor gobbled true
+ - dx enddef.
+\endtt
+Isn't this fun? \ (Look closely.)
+
+There is a problem, however, if we try to use these macros with
+loops in the arguments. Consider the expressions
+\begintt
+inorder(for n=1 upto 10: a[n], endfor infinity),
+inorder(a[1] for n=2 upto 10: ,a[n] endfor),
+inorder(a[1],a[2] for n=3 upto 10: ,a[n] endfor);
+\endtt
+the first two give error messages, but the third one works!
+The reason is that, in the first two cases,
+the @for@ loop begins to be expanded before \MF\ begins to read the
+^{text argument}, hence ^|ENDFOR| rears its ugly head again.
+We can avoid this problem by rewriting the macros in a more complicated
+way that doesn't try to single out the first argument~$x$:
+\begintt
+def inorder(text t) =
+ expandafter startinorder for u=t:
+ <= u endgroup and begingroup u endfor
+ gobbled true true endgroup) enddef;
+def startinorder text t =
+ (begingroup true enddef;
+def equally_spaced(text t) expr dx =
+ if pair dx: (whatever,whatever) else: whatever fi
+ for u=t: - u = u endfor gobbled true
+ - dx enddef;
+\endtt
+Two separate tricks have been used here: (1)~The `^@endgroup@' within
+`inorder' will stop an undelimited text argument; this gets rid
+of the unwanted `|<=|~|u|' at the beginning. (2)~A throwaway variable,
+`^"whatever"', nullifies an unwanted equation at the beginning of
+`@equally\_spaced@'. With the new definitions,
+all three of the expressions above will be understood,
+and so will things like
+\begintt
+equally_spaced(for n=1 upto 10: x[n], endfor whatever) dx.
+\endtt
+Furthermore the single-argument cases now work:
+`inorder($a$)' will always be true, and
+`@equally\_spaced@($x)\,"dx"$' will produce no new equations.
+
+If we want to improve ^{max} and ^{min} in the same way, so that a person can
+specify loop arguments like
+\begintt
+max(a[1] for n=2 upto 10: ,a[n] endfor)
+\endtt
+and so that `max($a)=a$' in the case of a single argument, we have to
+work harder, because max and min treat their first argument in quite
+a special way; they need to apply the special macro ^"setu\_", which defines
+the type of the auxiliary variable "u\_". The fastest way to solve this
+problem is probably to use a token whose meaning changes during the
+first time through the loop:
+\begintt
+vardef max(text t) =
+ let switch_ = firstset_;
+ for u=t: switch_ u>u_: u_ := u ;fi endfor
+ u_ enddef;
+vardef min(text t) =
+ let switch_ = firstset_;
+ for u=t: switch_ u<u_: u_ := u ;fi endfor
+ u_ enddef;
+def firstset_ primary u =
+ setu_ u; let switch_ = if; if false: enddef.
+\endtt
+Incidentally, the author's ^^{Knuth} first programs for max and min
+contained an interesting bug. They started with `@save@ "u\_"', and they
+tried to recognize the first time through the loop by testing if "u\_" was
+unknown. This failed because "u\_" could be constantly unknown in
+well-defined cases like max$(x,x+1,x+2)$.
+
+\subsection Types. Our programs for |inorder|, |equally_spaced|, and
+|max| are careful not to make unnecessary assumptions about the type
+of an expression. The `round' and `byte' functions in Appendix~B
+are further examples of macros that change behavior based
+on the types of their @expr@ arguments. Let's look more closely at
+applications of type testing. \looseness=-1
+
+When the author was developing macros for plain \MF\!, his first
+``correct'' solution for |max| had the following form:
+\begintt
+vardef max(text t) =
+ save u_; boolean u_;
+ for u=t: if boolean u_: setu_ u
+ elseif u_<u: u_ := u fi; endfor
+ u_ enddef.
+\endtt
+This was interesting because it showed that there was no need to
+set "u\_" to true or false; the simple fact that it was boolean
+was enough to indicate the first time through the loop. \ (A slightly
+different "setu\_" macro was used at that time.)
+
+We might want to generalize the `^{scaled}' operation of \MF\ so that
+`scaled~$(x,y)$' is shorthand for `^{xscaled}~$x$ ^{yscaled}~$y$'.
+That's pretty easy:
+\begintt
+let SCALED = scaled;
+def scaled primary z =
+ if pair z: xscaled xpart z yscaled ypart z
+ else: SCALED z fi enddef;
+\endtt
+It's better to keep the primitive operation `|SCALED| |z|' here than to replace
+it by the slower variant `|xscaled| |z| |yscaled| |z|'.
+
+\MF\ allows you to compare booleans, numerics, pairs, strings, and
+transforms for equality; but it doesn't allow the expression
+`$p=q$' where $p$ and~$q$ are paths or pens or pictures. Let's
+write a general ^{equality test} macro ^^{==} such that `$p==q$'
+will be true if and only if $p$ and $q$ are known and equal,
+whatever their type.
+\begintt
+tertiarydef p == q =
+ if unknown p or unknown q: false
+ elseif boolean p and boolean q: p=q
+ elseif numeric p and numeric q: p=q
+ elseif pair p and pair q: p=q
+ elseif string p and string q: p=q
+ elseif transform p and transform q: p=q
+ elseif path p and path q:
+ if (cycle p = cycle q) and (length p = length q)
+ and (point 0 of p = point 0 of q): patheq p of q
+ else: false fi
+ elseif pen p and pen q: (makepath p == makepath q)
+ elseif picture p and picture q: piceq p of q
+ elseif vacuous p and vacuous q: true
+ else: false fi enddef;
+vardef vacuous primary p =
+ not(boolean p or numeric p or pair p or path p
+ or pen p or picture p or string p or transform p) enddef;
+vardef patheq expr p of q =
+ save t; boolean t; t=true;
+ for k=1 upto length p:
+ t := (postcontrol k-1 of p = postcontrol k-1 of q)
+ and (precontrol k of p = precontrol k of q)
+ and (point k of p = point k of q);
+ exitunless t; endfor
+ t enddef;
+vardef piceq expr p of q =
+ save t; picture t;
+ t=p; addto t also -q;
+ cull t dropping origin;
+ (totalweight t=0) enddef;
+\endtt
+If $p$ and $q$ are numeric or pair expressions, we could relax the condition
+that they both be known by saying `@if@ known $p-q$: $p=q$ @else@:~@false@ @fi@';
+transforms could be handled similarly by testing each of their six parts.
+But there's no way to tell if booleans, paths, etc., have been equated
+when they're both unknown, without the risk of irrevocably changing the
+values of other variables.
+
+\subsection ^{Nonlinear equations}. \MF\ has a built-in solution mechanism
+for linear equations, but it balks at nonlinear ones.
+You might be able to solve a set of nonlinear equations yourself by
+means of algebra or calculus, but in difficult cases it is probably
+simplest to use the `^"solve"' macro of plain \MF\!\null. This makes it
+possible to solve $n$~equations in $n$~unknowns, provided that at most
+one of the equations is nonlinear when one of the unknowns is fixed.
+
+The general technique will be illustrated here in the case $n=3$.
+Let us try to find numbers $a$, $b$, and~$c$ such that
+$$\eqalign{-2a+3b/c&=c-3;\cr
+ ac+2b&=c^3-20;\cr
+ a^3+b^3&=c^2.\cr}$$
+When $c$ is fixed, the first two equations are linear in $a$ and~$b$.
+We make an inequality out of the remaining equation by changing `$=$'
+to~`$<$', then we embed the system in a boolean-valued function:
+\begintt
+vardef f(expr c) = save a,b;
+ -2a + 3b/c = c - 3;
+ a*c + 2b = c*c*c - 20;
+ a*a*a + b*b*b < c*c enddef;
+c = solve f(1,7);
+-2a + 3b/c = c - 3;
+a*c + 2b = c*c*c - 20;
+show a, b, c.
+\endtt
+If we set ^"tolerance"$\null="epsilon"$ (which is the minimum value
+that avoids infinite looping in the "solve" routine), the values
+$a=1$, $b=2$, and $c=3$ are shown (so it is obvious that the example
+was rigged). If "tolerance" has its default value~0.1, we get
+$a=1.05061$, $b=2.1279$, $c=3.01563$; this would probably be close
+enough for practical purposes, assuming that the numbers represent
+pixels. \ (Increasing the tolerance saves time because it
+decreases the number of iterations within "solve"; you have to
+balance time versus necessary accuracy.)
+
+The only tricky thing about this use of solve was the choice of the
+numbers 1 and~7 in `$f(1,7)$'. In typical applications we'll usually have
+obvious values of the unknown where $f$ will be true and false,
+but a bit of experimentation was necessary for the problem considered
+here. In fact,
+it turns out that $f(-3)$ is~true and $f(-1)$ is false, in this
+particular system; setting $c="solve"\,f(-3,-1)$ leads to
+another solution: $a=7.51442$, $b=-7.48274$, $c=-2.3097$. Furthermore,
+it's interesting to observe that this system has no solution with
+$c$ between $-1$ and~$+1$, even though $f(+1)$ is true and
+$f(-1)$ is false! When $c\rightarrow0$, the quantity $a^3+b^3$
+approaches $-\infty$ when $c$~is negative, $+\infty$ when $c$~is
+positive. An attempt to `"solve" $f(1,-1)$' will divide by zero and
+come up with several arithmetic overflows.
+
+\hangindent=-42mm \hangafter=-7
+Let's consider now a real application instead of a contrived example.
+\rightfig Da (34mm x 24mm) ^20pt
+We wish to find the vertices of a ^{parallelogram}
+$z_{1l}$,~$z_{1r}$, $z_{0l}$,~$z_{0r}$, such that
+\begindisplay
+$x_{1l}=a$; \ \ $y_{1r}=b$; \ \ $z_{0r}=(c,d)$;\cr
+length$(z_{1r}-z_{1l})$ $=$ length$(z_{0r}-z_{0l})$ $=$ "stem",\cr
+\enddisplay
+and such that the lines $z_{1r}\dashto z_{1l}$ and
+$z_{1r}\dashto z_{0r}$ meet at a given angle~$\phi$. We can consider
+the common angle~$\theta$ of $z_{1r}-z_{1l}$ and $z_{0r}-z_{0l}$ to be
+the ``nonlinear'' unknown, so the equations to be solved can be
+written
+\begindisplay
+$\penpos1("stem",\theta)$; \ \ $\penpos0("stem",\theta)$;\cr
+$x_{1l}=a$; \ \ $y_{1r}=b$; \ \ $z_{0r}=(c,d)$;\cr
+angle$(z_{1r}-z_{0r})\,=\,\theta+\phi$.\cr
+\enddisplay
+When $\theta$ has a given value, all but the last of these equations
+are linear; hence we can solve them by turning the crank in our general method:
+\begintt
+vardef f(expr theta) = save x,y;
+ penpos1(stem,theta); penpos0(stem,theta);
+ x1l=a; y1r=b; z0r=(c,d);
+ angle(z1r-z0r)<theta+phi enddef;
+theta=solve f(90,0);
+penpos1(stem,theta); penpos0(stem,theta);
+x1l=a; y1r=b; z0r=(c,d);
+show z1l,z1r,z0l,z0r,theta,angle(z1r-z0r).
+\endtt
+For example, if $a=1$, $b=28$, $c=14$, $d=19$, $"stem"=5$, and $\phi=80$,
+we get
+\begindisplay \def\qquad{\hskip1.5em}
+$(1,23.703)$&$(3.557,28)$&$(11.443,14.703)$&$(14,19)$&59.25&139.25
+\enddisplay
+as answers when $"tolerance"="epsilon"$, and
+\begindisplay \def\qquad{\hskip1.5em}
+$(1,23.702)$&$(3.554,28)$&$(11.446,14.702)$&$(14,19)$&59.28&139.25
+\enddisplay
+when $"tolerance"=0.1$.
+The function $f$ prescribed by the general method
+can often be simplified; for example, in this case we can remove
+redundancies and get just
+\begintt
+vardef f(expr theta) = save x,y;
+ penpos1(stem,theta); x1l=a; y1r=b;
+ angle(z1r-(c,d))<theta+phi enddef.
+\endtt
+The problem just solved can be called the ``^{d} problem,'' because it arose in
+connection with N.~N. ^{Billawala}'s meta-design of a ^{black-letter}
+`{\manual?}', and because it appears in Appendix~D.
+
+\subsection Nonlinear interpolation.
+Suppose a designer has empirically determined good values of some quantity
+$f(x)$ for several values of~$x$; for example, $f(x)$ might be a
+stroke weight or a serif length or an amount of overshoot, etc. These
+empirical values can be generalized and incorporated into a ^{meta-design}
+if we are able to ^{interpolate} between the original $x$'s, obtaining
+$f(x)$ at intermediate points.
+
+Suppose the data points are known for $x=x_1<x_2<\cdots<x_n$. We can
+represent $f(x)$ by its graph, which we can assume is well approximated
+by the \MF\ path defined by
+\begindisplay
+$F\,=\,\bigl(x_1,f(x_1)\bigr)\to\bigl(x_2,f(x_2)\bigr)\to
+\<etc.>\to\bigl(x_n,f(x_n)\bigr)$
+\enddisplay
+if $f(x)$ is a reasonable ^{function}. Therefore interpolation can be
+done by using path intersection (!):
+\begintt
+vardef interpolate expr F of x = save t; t =
+ if x < xpart point 0 of F: extrap_error 0
+ elseif x > xpart point infinity of F: extrap_error infinity
+ else: xpart(F intersectiontimes verticalline x) fi;
+ ypart point t of F enddef;
+def extrap_error = hide(errhelp "The extreme value will be used.";
+ errmessage "`interpolate' has been asked to extrapolate";
+ errhelp "") enddef;
+vardef verticalline primary x =
+ (x,-infinity)--(x,infinity) enddef;
+\endtt
+For example, if $f(1)=1$, $f(3)=2$, and $f(15)=4$, this interpolation
+scheme gives `interpolate $(1,1)\to(3,2)\to(15,4)$ of~7' the value 3.37.
+
+\subsection Drawing with ^{overlays}. Let's leave numerical computations
+now and go back into the realm of pictures. Bruce ^{Leban} has suggested
+an extension of plain \MF's `^@clearit@/^@showit@/^@shipit@' commands
+by which `^@fill@' and `^@draw@' essentially operate on imaginary sheets of
+clear plastic. A new command `^@keepit@' places a fresh sheet of plastic
+on top of whatever has already been drawn, thereby preserving the covered image
+against subsequent erasures.
+
+We can implement @keepit@ by introducing a new picture variable
+^"totalpicture", and new boolean variables ^"totalnull", ^"currentnull",
+then defining macros as follows:
+\begintt
+def clearit = currentpicture:=totalpicture:=nullpicture;
+ currentnull:=totalnull:=true; enddef;
+def keepit = cull currentpicture keeping (1,infinity);
+ addto totalpicture also currentpicture;
+ currentpicture:=nullpicture;
+ totalnull:=currentnull; currentnull:=true; enddef;
+def addto_currentpicture =
+ currentnull:=false; addto currentpicture enddef;
+def mergeit (text do) =
+ if totalnull: do currentpicture
+ elseif currentnull: do totalpicture
+ else: begingroup save v; picture v; v:=currentpicture;
+ cull v keeping (1,infinity); addto v also totalpicture;
+ do v endgroup fi enddef;
+def shipit = mergeit(shipout) enddef;
+def showit_ = mergeit(show_) enddef;
+def show_ suffix v = display v inwindow currentwindow enddef;
+\endtt
+The "totalnull" and "currentnull" bookkeeping isn't strictly necessary,
+but it contributes greatly to the efficiency of this scheme if the
+extra generality of @keepit@ is not actually being used.
+The `$v$' computations in @mergeit@ involve copying the accumulated
+picture before displaying it or shipping it out; this takes time,
+and it almost doubles the amount of memory needed, so we try to avoid it
+when possible.
+
+\subsection Filing pictures. If you want to store a picture in a file
+and read it in to some other \MF\ job, you face two problems:
+(1)~\MF's @shipout@ command implicitly culls the picture, so that only
+binary data is left. Pixel values $>0$ are distinguished from pixel
+values $<=0$, but no other information about those values will survive.
+\ (2)~The result of ^@shipout@ can be used in another \MF\ job only if
+you have an auxiliary program that converts from binary ^|gf| format
+to a \MF\ source program; \MF\ can write |gf| files, but it can't
+read them.
+
+These problems can be resolved by using \MF's ^{transcript} or ^{log file}
+as the output medium, instead of using the |gf| file. For example, let's
+consider first the use of ^"tracingedges". Suppose we say
+\begindisplay
+"tracingedges" $:=$ 1;\cr
+\<any sequence of @fill@, @draw@, or @filldraw@ commands>\cr
+@message@ |"Tracing edges completed."|; \ $"tracingedges":=0$;\cr
+\enddisplay
+then the log file will contain lines such as the following:
+\beginlines
+|Tracing edges at line 15: (weight 1)|
+|(1,5)(1,2)(2,2)(2,1)(3,1)(3,0)(8,0)(8,1)(9,1)(9,2)(10,2)(10,8)(9,8)|
+|(9,9)(8,9)(8,10)(3,10)(3,9)(2,9)(2,8)(1,8)(1,5).|
+\smallskip
+|Tracing edges at line 15: (weight -1)|
+|(3,5)(3,2)(4,2)(4,1)(7,1)(7,2)(8,2)(8,8)(7,8)(7,9)(4,9)(4,8)(3,8)(3,5).|
+\smallskip
+|Tracing edges at line 18: (weight -1)|
+|(No new edges added.)|
+\smallskip
+|Tracing edges completed.|
+\endlines
+Let us write macros so that these lines are acceptable input to \MF\!.
+\begintt
+def Tracing=begingroup save :,[,],Tracing,edges,at,weight,w;
+ delimiters []; let Tracing = endfill; interim turningcheck := 0;
+ vardef at@#(expr wt) = save (,); w := wt;
+ let ( = lp; let ) = rp; fill[gobble begingroup enddef;
+ let edges = \; let weight = \; let : = \; enddef;
+def lp = [ enddef;
+def rp = ] -- enddef;
+vardef No@# = origin enddef;
+def endfill = cycle] withweight w endgroup; enddef;
+def completed = endgroup; enddef;
+\endtt
+^^"turningcheck" ^^@save@ ^^@delimiters@
+The precise form of edge-traced output, with its limited vocabulary
+and its restricted use of parentheses and commas, has been exploited here.
+
+With slight changes to this code, you can get weird effects.
+For example, if the definition of |rp| is changed to `|]..tension 4..|',
+^^{tension} and if `|scaled|~|5pt|' is inserted before `|withweight|',
+the image will be an ``^{almost digitized}'' character:
+\displayfig Daa (18.5mm)
+(The bumps at the left here are due to the repeated points `|(1,5)|' and
+`|(3,5)|' in the original data. You can remove them by adding an extra
+pass, first tracing the edges that are output by the {\sl unmodified\/}
+|Tracing| macros.)
+
+Although the effects of @fill@ and @draw@ can be captured by
+"tracingedges", other operations like ^{culling} are not traced.
+Let us therefore consider the more general picture representation
+that \MF\ produces when ^"tracingoutput" is positive, or when you
+ask it to ^@show@ a picture (see Chapter~13). The macros on the next
+page will recreate a picture from input of the form
+\begintt
+beginpicture
+row 1: 1+ -2- || 0+ 2-
+row 0: || 0+ 2++ 5---
+row -2: 0- -2+ ||
+endpicture
+\endtt
+where the middle three lines have been copied verbatim from a transcript
+file. \ (The task would be easier if the token `|-|' didn't have
+to perform two different functions!)
+\begintt
+let neg_ = -; let colon_ = :;
+def beginpicture =
+ begingroup save row, ||, :, ---, --, +, ++, +++, v, xx, yy, done;
+ picture v; v := nullpicture; interim turningcheck := 0;
+ let --- = mmm_; let -- = mm_;
+ let + = p_; let ++ = pp_; let +++ = ppp_;
+ let row = pic_row; let || = relax; let : = pic_colon; : enddef;
+def pic_row primary y = done; yy := y; enddef;
+def pic_colon primary x =
+ if known x colon_ ; xx := x; pic_edge fi enddef;
+def pic_edge =
+ let - = m_;
+ addto v contour unitsquare xscaled xx shifted(0,yy) enddef;
+def mmm_ = withweight 3; let - = neg_; : enddef;
+def mm_ = withweight 2; let - = neg_; : enddef;
+def m_ = withweight 1; let - = neg_; : enddef;
+def p_ = withweight neg_1; let - = neg_; : enddef;
+def pp_ = withweight neg_2; let - = neg_; : enddef;
+def ppp_ = withweight neg_3; let - = neg_; : enddef;
+transform xy_swap; xy_swap = identity rotated 90 xscaled -1;
+def endpicture = done;
+ v transformed xy_swap transformed xy_swap endgroup enddef;
+\endtt
+The reader will find it instructive to study these macros closely.
+When `|done|' appears, it is an unknown primary, so |pic_colon|
+will not attempt to generate another edge. Each new edge also
+inserts a cancelling edge at $x=0$. The two applications ^^"xy\_swap"
+of |xy_swap| at the end will clear away all redundant edges. (Double
+swapping is a bit faster than the operation `|rotated-90| |rotated|~|90|'
+that was used for this purpose in Chapter~13.)
+
+\subsection Fattening a pen. Let's move on to another aspect of
+\MF\ by considering
+an operation on ^{pen} ^{polygons}: Given a @pen@ value~$p$,
+the task is to construct a pen `^@taller@~$p$' that is one pixel
+taller. For example, if $p$ is the ^{diamond} nib
+`$(0.5,0)\dashto(0,0.5)\dashto(-0.5,0)\dashto(0,-0.5)\dashto\cycle$',
+the taller nib will be
+\begindisplay
+$(0.5,0.5)\dashto(0,1)\dashto(-0.5,0.5)\dashto(-0.5,-0.5)\dashto(0,-1)
+ \dashto(0.5,-0.5)\dashto\cycle$;
+\enddisplay
+if $p$ is a tilted ^{penrazor} `$(-x,-y)\dashto(x,y)\dashto\cycle$',
+the taller nib will be
+\begindisplay
+$(-x,-y-0.5)\dashto(x,y-0.5)\dashto(x,y+0.5)\dashto(-x,-y+0.5)\dashto\cycle$,
+\enddisplay
+assuming that $x>0$. The macro itself turns out to be fairly simple, but
+it makes instructive use of ^{path} and pen operations.
+
+We want to split the pen into two parts, a ``bottom'' half and a ``top''
+half; the bottom half should be shifted down by .5~pixels, and the
+top half should be shifted up. The dividing points between halves occur
+at the leftmost and rightmost vertices of the pen. Hmmm; a potential problem
+arises if there are two or more leftmost or rightmost points; for example,
+what if we try to make `@taller@ @taller@~$p$'? Fortunately \MF\ doesn't
+mind if a pen polygon has three or more consecutive vertices that
+lie on a line, hence we can safely choose {\sl any\/} leftmost
+point and any rightmost point.
+
+The next question is, ``How should we find leftmost and rightmost
+points?'' We will, of course, use ^@makepath@ to find the set of all
+vertices; so we could simply traverse the path and find the minimum
+and maximum $x$~coordinates. However, it will be faster (and more fun)
+to use either ^{directiontime} or ^{penoffset} for this purpose.
+Let's try directiontime first:
+\begintt
+vardef taller primary p =
+ save r, n, t, T; path r;
+ r = tensepath makepath p; n = length r;
+ t = round directiontime up of r;
+ T = round directiontime down of r;
+ if t>T: t := t-n; fi
+ makepen(subpath(T-n,t) of r shifted .5down
+ --subpath(t,T) of r shifted .5up -- cycle) enddef;
+\endtt
+The result of @makepath@ has control points equal to their adjacent
+vertices, so it could not be used with directiontime.
+\ (If any key point is equal to its precontrol or postcontrol,
+the ``^{velocity}'' of the path is zero at that point; directiontime
+assumes that all directions occur whenever the velocity drops to zero.) \
+Therefore we have used `^@tensepath@'.
+This almost works, once we realize that the values
+of $t$ and~$T$ sometimes need to be rounded to integers. But it
+fails for pens like @penspeck@ that have points very close together,
+since @tensepath@ is no better than an unadulterated @makepath@ in such cases.
+Furthermore, even if we could define a nice path from~$p$ (for example
+by scaling it up), we would run into problems of
+numerical instability, in cases like @penrazor@ where
+the pen polygon takes a $180^\circ$ turn. Razor-thin pens cannot be recognized
+easily, because they might have more than two vertices; for example,
+rotations of future pens such as
+`@makepen@($"left"\to"origin"\to"right"\to\cycle$)' are problematical.
+
+We can obtain a more robust result by using penoffset, because
+this operation makes use of the convexity of the polygon. The
+``fastest'' solution looks like this:
+\begintt
+vardef taller primary p =
+ save q, r, n, t, T; pen q; q = p;
+ path r; r = makepath q; n = length r;
+ t = round xpart(r intersectiontimes penoffset up of q);
+ T = round xpart(r intersectiontimes penoffset down of q);
+ if t>T: t := t-n; fi
+ makepen(subpath(T-n,t) of r shifted .5down
+ --subpath(t,T) of r shifted .5up -- cycle) enddef;
+\endtt
+^^{intersectiontimes} ^^{subpath}
+(The argument $p$ is copied into $q$, in case it's a ^{future pen};
+this means that the conversion of future pen to pen need be
+done only once instead of three times.)
+
+\subsection ^{Bernshte{\u\i}n} polynomials. And now, for our last trick,
+let's try to extend \MF's syntax so that it will accept generalized
+^{mediation} formulas of the form `$t[u_1,\ldots,u_n]$' for all $n\ge2$.
+\ (This notation was introduced for $n=3$ and~4 in Chapter~14, when we were
+considering fractional subpaths.) \ If $n>2$, the identity
+\begindisplay
+$t[\,u_1,\ldots,u_n]\;=\;t\bigl[t[u_1,\ldots,u_{n-1}],t[u_2,\ldots,u_n]\,\bigr]$
+\enddisplay
+defines $t[u_1,\ldots,u_n]$ recursively, and it can be shown that the
+alternative definition
+\begindisplay
+$t[\,u_1,\ldots,u_n]\;=\;t\bigl[t[u_1,u_2],\ldots,t[u_{n-1},u_n]\,\bigr]$
+\enddisplay
+gives the same result. \ (Indeed, we have
+\begindisplay
+$\displaystyle t[u_1,\ldots,u_n]\;=\;\sum_{k=1}^n{n-1\choose k-1}
+ (1-t)^{n-k}t^{k-1}u_k,$
+\enddisplay
+a Bernshte{\u\i}n polynomial of order $n-1$.)
+
+Our problem is to change the meaning of \MF's ^{brackets} so that
+expressions like `$1/2[a,b,c,d]$' will evaluate to `$.125a+.375b+.375c
++.125d$' in accordance with the formulas just given, but we don't want
+to mess up the other primitive uses of brackets in contexts like
+`|x[n]|' and `|path|~|p[][]a|'. We also want to be able to use
+brackets inside of brackets.
+
+The reader is challenged to try solving this problem before looking at
+the weird solution that follows. Perhaps there is a simpler way?
+\begintt
+def lbrack = hide(delimiters []) lookahead [ enddef;
+let [[[ = [; let ]]] = ]; let [ = lbrack;
+def lookahead(text t) =
+ hide(let [ = lbrack;
+ for u=t, hide(n_ := 0; let switch_ = first_): switch_ u; endfor)
+ if n_<3: [[[t]]] else: Bernshtein n_ fi enddef;
+def first_ primary u =
+ if numeric u: numeric u_[[[]]]; store_ u
+ elseif pair u: pair u_[[[]]]; store_ u fi;
+ let switch_ = store_ enddef;
+def store_ primary u = u_[[[incr n_]]] := u enddef;
+primarydef t Bernshtein nn =
+ begingroup for n=nn downto 2:
+ for k=1 upto n-1: u_[[[k]]]:=t[[[u_[[[k]]],u_[[[k+1]]] ]]];
+ endfor endfor u_[[[1]]] endgroup enddef;
+\endtt
+The most subtle thing about this code is the way it uses the `empty'
+option of a ^\<for list> to dispense with ^{empty text arguments}.
+Since \MF\ evaluates all the expressions of a ^@for@ loop before
+reading the loop text, and since `|n_|' and `|u_|' are used here
+only when no recursion is taking place, it is unnecessary to ^{save}
+their values even when brackets are nested inside of brackets.
+
+Of course this trick slows \MF\ down tremendously, whenever brackets
+appear, so it is just of academic interest. But it seems to work
+in all cases except with respect to formulas that involve `^|]]|'
+(two consecutive brackets); the latter token, which plain \MF\ expands
+to `|]|~|]|', is not expanded when |lookahead| reads its ^{text
+argument}, hence the user must remember to insert a space between
+consecutive brackets. \looseness=-1
+
+\endchapter
+
+Their tricks an' craft hae put me daft,
+They've taen me in, an' a' that.
+\author ROBERT ^{BURNS}, {\sl The Jolly Beggar\/} (1799) % air 7
+
+\bigskip
+
+Ebery house hab him dutty carner.
+\author ^{ANDERSON} and ^{CUNDALL}, {\sl Jamaica Proverbs and Sayings\/} (1927)
+ % #755 in 2nd edition; was not in the first (1910) edition
+
+\eject
+ \beginchapter Appendix E. Examples
+
+We've seen lots of examples of individual letters or parts of letters;
+let's concentrate now on the problem of getting things all together.
+The next two pages contain the entire contents of an example file
+`|logo.mf|', which generates the letters of the \MF\ ^{logo}. The file
+is short, because only seven letters are involved, and because those letters
+were intentionally done in a style that would be easy for the system they name.
+But the file is complete, and it illustrates in simplified form all
+the essential aspects of larger fonts: Ad~hoc dimensions are
+converted to pixels; subroutines are defined; programs for
+individual letters appear; intercharacter and interword
+spacing conventions are nailed down. Furthermore, the character programs
+are careful to draw letters that will be
+well adapted to the raster, even if pixels on the output device are
+not square. % It's all there, in one short program.
+
+We've been studying the `\MF' letters off and on since Chapter~4, making
+our examples slightly more complex as more of the language has been
+encountered. Finally we're ready to pull out all the stops and look at the
+real, professional-quality |logo.mf|, which incorporates all the best
+suggestions that have appeared in the text and in answers to the exercises.
+
+It's easy to generate a font with |logo.mf|, by proceeding as explained
+in Chapter~11. For example, the |logo10| font that produces `\MF' in
+10-point size can be created for a low-resolution printer by running
+\MF\ with the ^{command line}
+\begintt
+\mode=lowres; input logo10
+\endtt
+where the ^{parameter file} |logo10.mf| appears in that chapter. Furthermore
+the slanted version `{\manual 89:;<=>:}\kern3pt' can be created by
+inputting the parameter file |logosl10.mf|, which says simply
+\begintt
+% 10-point slanted METAFONT logo
+slant := 1/4;
+input logo10
+\endtt
+The ^"slant" parameter affects ^"currenttransform" as explained in
+Chapter~15.
+
+There isn't a great deal of ``^{meta-ness}'' in the |logo.mf| design,
+because only a few forms of the \MF\ logo are needed. However, some
+interesting variations are possible; for example, if we use the
+parameter files
+\begindisplay \def\qquad{\hskip4em\relax} \advance\belowdisplayskip by 3pt
+|font_size 30pt#;|&|font_size 10pt#;|\cr
+|ht#:=25pt#;|&|ht#:=6pt#;|\cr
+|xgap#:=1.5pt#;|&|xgap#:=2pt#;|\cr
+|u#:=3/9pt#;|&|u#:=4/3pt#;|\cr
+|s#:=1/3pt#;|&|s#:=-2/3pt#;|\cr
+|o#:=2/9pt#;|&|o#:=1/9pt#;|\cr
+|px#:=1pt#;|&|px#:=1/3pt#;|\cr
+|slant:=-1/9;|\cr
+\enddisplay
+we get \kern2pt{\manual BCDGHIJD} and \kern4pt{\manual KLUVWvwU},
+\kern4pt\ respectively.
+
+\goodbreak\begingroup\obeylines\everypar{\strut}\parindent=0pt
+|% Routines for the METAFONT logo, as found in The METAFONTbook|
+|% (logo10.mf is a typical parameter file)|
+\medskip
+|mode_setup;|
+|if unknown slant: slant:=0 else: currenttransform:=|
+| identity slanted slant yscaled aspect_ratio fi;|
+\medskip
+|ygap#:=(ht#/13.5u#)*xgap#; % vertical adjustment|
+|ho#:=o#; % horizontal overshoot|
+|leftstemloc#:=2.5u#+s#; % position of left stem|
+|barheight#:=.45ht#; % height of bar lines|
+|py#:=.9px#; % vertical pen thickness|
+\medskip
+|define_pixels(s,u);|
+|define_whole_pixels(xgap);|
+|define_whole_vertical_pixels(ygap);|
+|define_blacker_pixels(px,py);|
+|pickup pencircle xscaled px yscaled py;|
+|logo_pen:=savepen;|
+|define_good_x_pixels(leftstemloc);|
+|define_good_y_pixels(barheight);|
+|define_corrected_pixels(o);|
+|define_horizontal_corrected_pixels(ho);|
+\medskip
+|def beginlogochar(expr code, unit_width) =|
+| beginchar(code,unit_width*u#+2s#,ht#,0);|
+| pickup logo_pen enddef;|
+\medskip
+|def super_half(suffix i,j,k) =|
+| draw z.i{0,y.j-y.i}|
+| ... (.8[x.j,x.i],.8[y.i,y.j]){z.j-z.i}|
+| ... z.j{x.k-x.i,0}|
+| ... (.8[x.j,x.k],.8[y.k,y.j]){z.k-z.j}|
+| ... z.k{0,y.k-y.j} enddef;|
+\medskip
+|beginlogochar("M",18);|
+|x1=x2=leftstemloc; x4=x5=w-x1; x3=w-x3;|
+|y1=y5; y2=y4; bot y1=-o;|
+|top y2=h+o; y3=y1+ygap;|
+|draw z1--z2--z3--z4--z5;|
+|labels(1,2,3,4,5); endchar;|
+\medskip
+|beginlogochar("E",14);|
+|x1=x2=x3=leftstemloc;|
+|x4=x6=w-x1+ho; x5=x4-xgap;|
+|y1=y6; y2=y5; y3=y4;|
+|bot y1=0; top y3=h; y2=barheight;|
+\nointerlineskip
+\smash{\vbox{
+ \rightline{\figbox{Eb}{224\apspix}{216\apspix}\vbox} % F
+ \kern12pt
+ \rightline{\figbox{A18a}{240\apspix}{216\apspix}\vbox} % A
+ \kern12pt
+ \rightline{\figbox{Ea}{208\apspix}{216\apspix}\vbox} % T
+ \kern3.5pt}}
+\endgroup\eject\begingroup\obeylines\everypar{\strut}\parindent=0pt
+\smash{\vtop{\kern0pt\kern6pt
+ \rightline{\figbox{18a}{240\apspix}{216\apspix}\vbox} % O
+ \kern12pt
+ \rightline{\figbox{4c}{288\apspix}{216\apspix}\vbox} % M
+ \kern12pt
+ \rightline{\figbox{11a}{224\apspix}{216\apspix}\vbox} % E
+ \kern12pt
+ \rightline{\figbox{21a}{240\apspix}{216\apspix}\vbox} % N
+ }}\nointerlineskip
+|draw z6--z1--z3--z4; draw z2--z5;|
+|labels(1,2,3,4,5,6); endchar;|
+\medskip
+|beginlogochar("T",13);|
+|italcorr ht#*slant + .5u#;|
+|if .5w<>good.x .5w: change_width; fi|
+|lft x1=-eps; x2=w-x1; x3=x4=.5w;|
+|y1=y2=y3; top y1=h; bot y4=-o;|
+|draw z1--z2; draw z3--z4;|
+|labels(1,2,3,4); endchar;|
+\medskip
+|beginlogochar("A",15);|
+|x1=.5w; x2=x4=leftstemloc; x3=x5=w-x2;|
+|top y1=h+o; y2=y3=barheight;|
+|bot y4=bot y5=-o;|
+|draw z4--z2--z3--z5; super_half(2,1,3);|
+|labels(1,2,3,4,5); endchar;|
+\medskip
+|beginlogochar("F",14);|
+|x1=x2=x3=leftstemloc;|
+|x4=w-x1+ho; x5=x4-xgap;|
+|y2=y5; y3=y4; bot y1=-o;|
+|top y3=h; y2=barheight;|
+|draw z1--z3--z4; draw z2--z5;|
+|labels(1,2,3,4,5); endchar;|
+\medskip
+|beginlogochar("O",15);|
+|x1=x4=.5w; top y1=h+o; bot y4=-o;|
+|x2=w-x3=good.x(1.5u+s); y2=y3=barheight;|
+|super_half(2,1,3); super_half(2,4,3);|
+|labels(1,2,3,4); endchar;|
+\medskip
+|beginlogochar("N",15);|
+|x1=x2=leftstemloc; x3=x4=x5=w-x1;|
+|bot y1=bot y4=-o;|
+|top y2=top y5=h+o; y3=y4+ygap;|
+|draw z1--z2--z3; draw z4--z5;|
+|labels(1,2,3,4,5); endchar;|
+\medskip
+|ligtable "T": "A" kern -.5u#;|
+|ligtable "F": "O" kern -u#;|
+\medskip
+|font_quad:=18u#+2s#;|
+|font_normal_space:=6u#+2s#;|
+|font_normal_stretch:=3u#;|
+|font_normal_shrink:=2u#;|
+|font_identifier:="MFLOGO" if slant<>0: & "SL" fi;|
+|font_coding_scheme:="AEFMNOT only";|
+\endgroup\goodbreak
+
+Everything in |logo.mf| has already been explained previously in this
+book except for the very last two lines, which define a `^@font\_identifier@'
+and a `^@font\_coding\_scheme@'. These are optional bits of information
+that are discussed in Appendix~F\null. Furthermore an ^{italic correction}
+has been specified for the letter `{\manual T}', since it's the final
+letter of `\kern-1.4pt{\manual 89:;<=>:\/}'.
+
+\medskip\ninepoint
+The program for a complete typeface will differ from the program for
+this simple logo font primarily in degree; there will be lots more
+parameters, lots more subroutines, lots more characters, lots more
+ligatures and kerns and whatnot. But there will probably also be
+more administrative machinery, designed to facilitate the creation,
+testing, and modification of characters, since a large enterprise
+requires good organization. The remainder of this appendix is
+devoted to an example of how this might be done: We shall discuss
+the additional kinds of routines that the author ^^{Knuth} found
+helpful while he was developing the ^{Computer Modern} family
+of typefaces.
+
+The complete, unexpurgated programs for Computer Modern appear in {\sl
+Computers \& Typesetting}, Volume~E\null; but since they have evolved
+over a long period of time, they are rather complex. We shall simplify
+the details so that it will be easier to grasp the important issues
+without being distracted by irrelevant technicalities.
+
+The simple logo fonts discussed above are generated by two types
+of files: There are parameter files like |logo10.mf|, and there is
+a program file |logo.mf|. The Computer Modern fonts, being more
+extensive, are generated by four types of files: There are
+{\sl^{parameter files}\/} like `|cmr10.mf|', which specify the
+ad hoc dimensions for particular sizes and styles of type; there are
+{\sl^{driver files}\/} like `|roman.mf|', which serve as chief
+executives of the font-generation process; there are
+{\sl^{program files}\/} like `|punct.mf|', which contain programs
+for individual characters; and there's a {\sl^{base file}\/} called
+`|cmbase.mf|', which contains the subroutines and other macros used
+throughout the system.
+
+Our logo example could have been cast in this more general mold by moving
+the character programs into a program file `|METAFON.mf|', and by moving
+most of the opening material into a base file `|logobase.mf|'
+that looks like this:
+\beginlines
+|% Base file for the METAFONT logo|
+|logobase:=1; % when logobase is known, this file has been input|
+\smallskip
+|def font_setup =|
+| if unknown slant: slant:=0 else: currenttransform:=|
+%| identity slanted slant yscaled aspect_ratio fi;|
+\qquad\smash{\vdots}\qquad\vbox to10pt{}%
+ \raise1pt\hbox{(the previous code is unchanged)}
+| define_corrected_pixels(o);|
+| define_horizontal_corrected_pixels(ho); enddef;|
+\endlines
+followed by the definitions of |beginlogochar| and |super_half|.
+Then we're left with a driver file |logo.mf| that looks like this:
+\beginlines
+|% Driver file for the METAFONT logo|
+|if unknown logobase: input logobase fi|
+\smallskip
+|mode_setup; font_setup; % establish pixel-oriented units|
+|input METAFON % generate the characters|
+\smallskip
+|ligtable "T": "A" kern -.5u#;|
+\weakendlines
+and so on, concluding as before.
+
+In general, a parameter file calls on a driver file, which calls on
+one or more program files; the base file contains predefined macros
+shared by all. There may be several driver files, each using a
+different combination of program files; for example, Computer Modern
+has `|roman.mf|' and `|italic.mf|', % a little white lie, multiplied below
+both of which call on |punct.mf| to generate punctuation marks,
+although they use different program files to generate the lowercase
+alphabets. Characters are partitioned into program files so that
+they can be shared by different drivers.
+
+Parameter files in Computer Modern don't quite follow the conventions
+of\/ |logo10.mf|. Here, for example, are the
+opening and closing lines of ^|cmr10.mf|:
+\beginlines
+|% Computer Modern Roman 10 point|
+|if unknown cmbase: input cmbase fi|
+\smallskip
+|font_identifier "CMR"; font_size 10pt#;|
+\smallskip
+|u#:=20/36pt#; % unit width|
+|serif_fit:=0pt#; % extra sidebar near serifs|
+|letter_fit:=0pt#; % extra space added to all sidebars|
+\vskip-3pt
+\qquad\vdots
+|serifs:=true; % should serifs and bulbs be attached?|
+|monospace:=false; % should all characters have the same width?|
+\smallskip
+|generate roman % switch to the driver file|
+\endlines
+The main differences are: \ (1) There's special code at the beginning, to
+make sure that |cmbase.mf| has been loaded. The base file includes
+several things that are needed right away; for example, |cmbase| declares
+the variables `"serifs"' and `^"monospace"' to be of type @boolean@,
+so that boolean-valued parameter assignments like `$"serifs":=@true@$'
+will be legal. \ (2)~The @font\_identifier@ is defined in the parameter file,
+not in the driver file. \ (3)~The last line says `^@generate@' instead of
+`@input@'; the base file defines @generate@ to be the same as @input@,
+but other meanings are assigned by utility routines that we'll study later.
+\ (4)~The final `^@end@' is no longer present in the parameter file.
+
+The |roman.mf| driver looks like this (vastly simplified):
+^^@font\_slant@ ^^@font\_quad@ ^^@font\_normal\_space@
+^^@font\_normal\_stretch@ ^^@font\_normal\_shrink@
+\beginlines
+|% The Computer Modern Roman family of fonts|
+\smallskip
+|mode_setup; font_setup;|
+\smallskip
+|input romanu; % upper case (majuscules)|
+|input romanl; % lower case (minuscules)|
+|input romand; % numerals|
+|input punct; % punctuation marks|
+\smallskip
+|font_slant slant;|
+|if monospace: font_quad 18u#;|
+| font_normal_space 9u#; % no stretching or shrinking|
+|else: font_quad 18u#+4letter_fit#;|
+| font_normal_space 6u#+2letter_fit#; % interword spacing|
+| font_normal_stretch 3u#; % with ``glue''|
+| font_normal_shrink 2u#;|
+| input romlig; % f ligatures|
+| |^|ligtable|| "f": "i" =: oct"014", "f" =: oct"013", "l" =: oct"015",|
+| "'" kern u#, "?" kern u#, "!" kern u#;|
+| ligtable oct"013": "i" =: oct"016", "l" =: oct"017", % ffi and ffl|
+| "'" kern u#, "?" kern u#, "!" kern u#;|
+| ligtable "-": "-" =: oct"173"; % en dash|
+| ligtable oct"173": "-" =: oct"174"; % em dash|
+| ligtable "`": "`" =: oct"134"; % open quotes|
+| ligtable "'": "'" =: oct"042", % close quotes|
+| "?" kern 2u#, "!" kern 2u#;|
+|fi; |^|bye.|
+\endlines
+In a ^{monospaced} font like ^|cmtt10|, all characters will be exactly
+$9u\0$ wide. Both |cmr10| and~|cmtt10| use the |roman| driver, but
+|roman| omits the ligatures and changes the interword spacing
+when it is producing monospaced fonts.
+
+The program files of Computer Modern have slightly different conventions
+from those of plain \MF\!\null. Here, for example, are the ^^{.} ^^{em dash}
+programs for two of the simplest ^{punctuation marks}:
+\beginlines
+|cmchar "Period";|
+|numeric dot_diam#; dot_diam# = if monospace: 5/4 fi dot_size#;|
+|define_whole_blacker_pixels(dot_diam);|
+|beginchar(".",5u#,dot_diam#,0);|
+|adjust_fit(0,0); pickup fine.nib;|
+|pos1(dot_diam,0); pos2(dot_diam,90);|
+|x1l=good.x(x1l+.5w-x1); bot y2l=0; z1=z2; dot(1,2); % dot|
+|penlabels(1,2); endchar;|
+\medskip
+\leftline{\hskip3pc\figbox{Ec\&Ed}{3in}{360\apspix}\vbox}
+\smallskip
+|iff not monospace: cmchar "Em dash";|
+|beginchar(oct"174",18u#,x_height#,0);|
+|italcorr .61803x_height#*slant + .5u#;|
+|adjust_fit(letter_fit#,letter_fit#);|
+|pickup crisp.nib; pos1(vair,90); pos2(vair,90);|
+|y1r=y2r=good.y(y1r+.61803h-y1); lft x1=-eps; rt x2=w+eps;|
+|filldraw stroke z1e--z2e; % crossbar|
+|penlabels(1,2); endchar;|
+\endlines
+The new structural features in these programs are: (1)~`^@cmchar@',
+which appears at the very beginning of each character program;
+(2)~`^@iff@~\<boolean expression>:', which precedes @cmchar@ if
+the character is to be generated only when the boolean expression
+is true; (3)~`^@adjust\_fit@', which can change the amount of white space
+at the character's left and/or right; (4)~pens called `"fine.nib"' and
+`"crisp.nib"'; (5)~new macros `"pos"', `"dot"', and `"stroke"',
+discussed further below.
+
+The base file |cmbase.mf| begins as follows:
+\beginlines
+|% The base file for Computer Modern (a supplement to plain.mf)|
+\smallskip
+|cmbase:=1; % when cmbase is known, this file has been input|
+\smallskip
+|let cmchar = relax; % `cmchar' should precede each character|
+|let generate = input; % `generate' should follow the parameters|
+\smallskip
+|newinternal slant, superness,| $\cdots$ | % purely numeric parameters|
+|boolean serifs, monospace,| $\cdots$ | % boolean parameters|
+\endlines
+These few lines are straightforward enough. Although |cmchar| is defined
+to be the same as ^|relax|, which does nothing, the definition of
+|cmchar| will be changed by certain utility programs below; this will
+prove to be a convenience when characters are designed, tested, and maintained.
+
+The next few lines of |cmbase| are trickier. They implement the `@iff@\kern1pt'
+feature, which bypasses unwanted characters at high speed.
+\beginlines
+|let semi_ = ;; let colon_ = :; let endchar_ = endchar;|
+|def iff expr b =|
+| if b: let next_ = use_it else: let next_ = lose_it fi;|
+| next_ enddef;|
+|def use_it = let : = restore_colon; enddef;|
+|def restore_colon = let : = colon_; enddef;|
+|def lose_it = let endchar = fi; inner cmchar; let ; = fix_ semi_|
+| if false enddef;|
+|def fix_ = let ; = semi_; let endchar = endchar_; outer cmchar; enddef;|
+|def always_iff = let : = endgroup; killboolean enddef;|
+|def killboolean text t = use_it enddef;|
+|outer cmchar;|
+\weakendlines
+^^@always\_if@ ^^@inner@ ^^@outer@
+(The |lose_it| routine assumes that every character program will end
+with `|endchar;|'.)
+
+The most interesting part of |cmbase| is probably the way it allows the
+``^{side\-bearings}'' of each character to be fine-tuned. The amount of
+space at the left and right edges of the character's ``^{bounding box}''
+can be adjusted without actually shifting the picture, and without
+changing the width that was specified in @beginchar@. Here's how it works:
+After a @beginchar@ command and an optional @italcorr@, each Computer
+Modern character program is supposed to say
+\begindisplay
+@adjust\_fit@(\<left sidebearing adjustment>,\thinspace
+ \<right sidebearing adjustment>);
+\enddisplay
+sidebearing adjustments are given in true, ``sharped'' units.
+The ^@adjust\_fit@ routine essentially adds extra space at the left
+and right, corresponding to the sidebearing adjustments. An ad-hoc
+dimension called ``^"letter\_fit"$\0$'' is also added to all sidebearings,
+behind the scenes.
+
+Our example program for the |"."|\ says simply `@adjust\_fit@$(0,0)$';
+this means that only "letter\_fit" is added. The program for em-dash
+says `@adjust\_fit@$("letter\_fit"\0,\allowbreak"letter\_fit"\0)$', hence
+the sidebearings are increased by 2"letter\_fit" at each side.
+The total character width of the em-dash comes to $18u\0+
+4"letter\_fit"\0$ (which is indeed one em, the value of ^@font\_quad@
+specified in the |roman| driver file).
+
+The program for lowercase `^{b}' in file |romanl.mf| says
+`@adjust\_fit@$("serif\_fit"\0,0)$'; this adds the ^"serif\_fit"
+parameter at the left, to compensate for the possible appearance
+of a serif at the left of this character. The "serif\_fit" is
+zero in |cmr10|, but it has a negative value in a ^{sans-serif} font,
+and a positive value when serifs are extralong.
+
+The nice thing about @adjust\_fit@ is that it's an ``add-on''
+specification that doesn't affect the rest of the character design.
+The program can still be written as if 0~were the left edge and
+$w$~were the right edge; afterwards the fit can be adjusted without
+changing the program or the shapes.
+
+There are two versions of @adjust\_fit@, one for normal fonts
+and one for ^{mono\-space} fonts. Both of them are slightly complicated
+by something called ^"shrink\_fit", which will be explained later;
+for the moment, let's just imagine that $"shrink\_fit"=0$. Here is the
+routine for the normal case:
+\beginlines
+|def normal_adjust_fit(expr left_adjustment,right_adjustment) =|
+| l := -hround(left_adjustment*hppp)-letter_fit;|
+| interim xoffset := -l;|
+| charwd := charwd+2letter_fit#+left_adjustment+right_adjustment;|
+| r := l+hround(charwd*hppp)-shrink_fit;|
+| w := r-hround(right_adjustment*hppp)-letter_fit;|
+| enddef;|
+\endlines
+Variables ^"l" and ^"r" are set to the actual pixel boundaries of the
+character; thus, plain \MF's bounding box has $0\le x\le w$, but
+Computer Modern's has $l\le x\le r$. ^{Rounding} has been done
+very carefully so that the sidebearings will have consistent
+relationships across an entire font. Notice that ^"w"~has been
+recalculated; this means that @adjust\_fit@ can affect the digitization,
+but---we hope---in a beneficial way.
+
+In a monospaced font, the @adjust\_fit@ routine changes the
+unit-width parameter, ^"u", so that the total width after adjustment
+comes out to be constant. Similar adjustments are made to parameters
+like ^"jut", the nominal serif length. The width of all characters
+in a monospaced font will be $"mono\_charwd"\0$ in true units,
+^"mono\_charwd" in pixels. The italic correction of all
+characters will be $"mono\_charic"\0$.
+\beginlines
+|def mono_adjust_fit(expr left_adjustment,right_adjustment) =|
+| numeric expansion_factor; mono_charwd# = 2letter_fit#|
+| + expansion_factor*(charwd+left_adjustment+right_adjustment);|
+| forsuffixes $=u,jut,| $\cdots$ |:|
+| $ := $.#*expansion_factor*hppp; endfor|
+| l := -hround(left_adjustment*expansion_factor*hppp)-letter_fit;|
+| interim xoffset := -l;|
+| r := l+mono_charwd-shrink_fit;|
+| w := r-hround(right_adjustment*expansion_factor*hppp)-letter_fit;|
+| charwd := mono_charwd#; charic := mono_charic#;|
+| enddef;|
+\weakendlines
+It took the author ^^{Knuth} umpteen trials to get this routine right.
+
+The ^"xoffset" calculations in @adjust\_fit@ are enough to shift the
+character by the proper amount when it's being ^{shipped out}. We just
+have to take care of getting the correct character width in pixels,
+and |cmbase| does this by setting
+^^"extra\_endchar"
+\beginlines
+|extra_endchar := extra_endchar&"r:=r+shrink_fit;w:=r-l;";|
+\endlines
+
+No other changes to plain \MF's ^@endchar@ routine are needed; but we do
+need to redefine ^|makebox| and ^|maketicks|, in order to show the
+adjusted bounding box. It's convenient to change |makebox| so that it also
+slants the box, in a slanted font, and so that it draws vertical lines
+one unit apart as aids to the designer; several more horizontal lines
+are also drawn:
+\beginlines
+|def makebox(text rule) =|
+| for y=0,asc_height,body_height,x_height,bar_height,|
+| -desc_depth,-body_depth: rule((l,y)t_,(r,y)t_); endfor % horizontals|
+| for x=l,r: rule((x,-body_depth)t_,(x,body_height)t_); endfor % verticals|
+| for x=u*(1+floor(l/u)) step u until r-1:|
+| rule((x,-body_depth)t_,(x,body_height)t_); endfor % more verticals|
+| if charic<>0:|
+| rule((r+charic*pt,h.o_),(r+charic*pt,.5h.o_)); fi % italic correction|
+| enddef;|
+\smallskip
+|def maketicks(text rule) =|
+| for y=0,h.o_,-d.o_:|
+| rule((l,y),(l+10,y)); rule((r-10,y),(r,y)); endfor % horizontals|
+| for x=l,r: rule((x,10-d.o_),(x,-d.o_));|
+| rule((x,h.o_-10),(x,h.o_)); endfor % verticals|
+| if charic<>0:|
+| rule((r+charic*pt,h.o_-10),(r+charic*pt,h.o_)); fi % italic correction|
+| enddef;|
+\weakendlines
+(Examples of the new |makebox| routine appear in the illustrations for
+period and em-dash earlier in this appendix, and also in Chapter~23.)
+
+\smallskip
+Plain \MF's ^@change\_width@ routine must also be generalized:
+\beginlines
+|def change_width = if not monospace: % change width by +1 or -1|
+| if r+shrink_fit-l = floor(charwd*hppp): w := w+1; r := r+1;|
+| else: w := w-1; r := r-1; fi fi enddef;|
+\endlines
+
+The Computer Modern ^@font\_setup@ routine is invoked at the beginning of
+each driver file. This is what converts sharped units to pixels;
+@font\_setup@ also computes additional quantities that are important to the
+font as a whole. It's a long macro, but here are its important features:
+\beginlines
+|def font_setup =|
+| define_pixels(u,jut,| $\cdots$ |);|
+| define_whole_pixels(letter_fit,fine,crisp,| $\cdots$ |);|
+| define_whole_vertical_pixels(body_height,cap_height,| $\cdots$ |);|
+| define_whole_blacker_pixels(hair,stem,curve,| $\cdots$ |);|
+| define_whole_vertical_blacker_pixels(vair,slab,| $\cdots$ |);|
+| define_corrected_pixels(o,| $\cdots$ |);|
+\smallbreak
+| if monospace: mono_charwd# := 9u#; define_whole_pixels(mono_charwd);|
+| mono_charic# := max(0,body_height#*slant);|
+| let adjust_fit = mono_adjust_fit;|
+| else: let adjust_fit = normal_adjust_fit; fi|
+| lowres_fix(stem,curve) 1.2;|
+^^@lowres\_fix@ \smallbreak
+| |\<Initialize pen nibs, see below>
+\smallbreak
+| |^|currenttransform||:=identity slanted slant|
+| yscaled aspect_ratio scaled |^|granularity||;|
+| shrink_fit := 1+hround(2letter_fit#*hppp)-2letter_fit;|
+| if not string mode: if mode <= smoke: shrink_fit := 0; fi fi|
+| enddef;|
+\endlines
+If $"letter\_fit"\0=0$, the `^"shrink\_fit"' is set to~1; otherwise
+"shrink\_fit" is 0, 1, or~2, depending on how "letter\_fit" has
+rounded to an integer. This amount is essentially subtracted from~^"w"
+before each character in the font has been drawn. Experience shows that
+this trick greatly improves the readability of fonts at ^{medium}
+and ^{low resolutions}.
+
+Many of the Computer Modern characters are drawn with ^@filldraw@, which
+is a mixture of outline-filling and fixed-pen drawing. Several macros
+are included in |cmbase| to facilitate filldrawing, especially
+`^"pos"' and `^"stroke"':
+\beginlines
+|vardef pos@#(expr b,d) =|
+| (x@#r-x@#l,y@#r-y@#l)=(b-currentbreadth,0) rotated d;|
+| x@#=.5(x@#l+x@#r); y@#=.5(y@#l+y@#r) enddef;|
+\smallbreak
+|vardef stroke text t =|
+| forsuffixes e=l,r: path_.e:=t; endfor|
+| path_.l -- reverse path_.r -- cycle enddef;|
+\endlines
+Thus "pos" is like ^"penpos", except that it subtracts ^"currentbreadth"
+from the overall breadth. \ (Cf.~the program for left parentheses in
+Chapter~12.) \ The "stroke" routine is a simplified alternative to
+@penstroke@, such that @penstroke@ is equivalent to `@fill@~"stroke"'
+if the specified path isn't a cycle.
+
+The value of "currentbreadth" is maintained by redefining plain \MF's
+`^"numeric\_pickup\_"' macro so that it includes the new line
+\beginlines
+| if known breadth_[q]: currentbreadth:=breadth_[q]; fi|
+\endlines
+The ^@clear\_pen\_memory@ macro is redefined so that its second line now says
+\beginlines
+| numeric pen_lft_[],pen_rt_[],pen_top_[],pen_bot_[],breadth_[];|
+\endlines
+relevant entries of the "breadth\_" array will be defined by @font\_setup@,
+as we'll see soon.
+
+The example programs for period and em-dash say `@pickup@ "fine.nib"' and
+`@pickup@ "crisp.nib"'. These nibs are initialized by @font\_setup@ in
+the following way:
+\beginlines
+| clear_pen_memory;|
+| forsuffixes $ = fine,crisp,| $\cdots$ |:|
+| $.breadth := $;|
+| pickup if $=0: nullpen else: pencircle scaled $; $ := $-eps fi;|
+| $.nib := |^|savepen||; breadth_[$.nib] := $;|
+| forsuffixes $$ = lft,rt,top,bot: shiftdef($.$$,$$ 0); endfor endfor|
+\weakendlines
+If, for example, we have $"fine"=4$, this code sets $"fine.breadth":=4$,
+$"fine.nib":=1$, $"fine":=4-"eps"$, and $"breadth\_"[1]:=4-"eps"$.
+\ (A small amount~^"eps" has been subtracted so that "pos" will
+usually find $b-"currentbreadth">0$.) \ Furthermore, four subroutines
+^"fine.lft", "fine.rt", "fine.top", and "fine.bot" are defined, so
+that it's easy to refer to the edges of "fine.nib" when it has not been
+picked up. These four subroutines are created by a slightly
+tricky ^|shiftdef| macro:
+\beginlines
+|def shiftdef(suffix $)(expr delta) =|
+| vardef $ primary x = x+delta enddef enddef;|
+\endlines
+
+OK, we've just about covered everything in |cmbase| that handles the
+extra administrative complexity inherent in a large-scale design.
+The rest of the base file simply contains subroutines like
+^"serif" and ^"dot", for recurring features of the characters themselves.
+Such subroutines needn't be shown here.
+
+To make a binary file called ^|cm.base|, there's a trivial file `|cm.mf|':
+\beginlines
+|% This file creates `cm.base', assuming that plain.base is preloaded|
+|input cmbase; |^|dump.|
+\endlines
+
+\medbreak
+Besides parameter files, driver files, program files, and the base file,
+the Computer Modern routines also include a number of {\sl^{utility files}\/}
+that provide a convenient environment for designing new characters and
+improving old ones. We'll conclude this appendix by studying the contents
+of those utility files.
+
+Let's suppose, for example, that test proofs have revealed problems
+with the characters `k' and `S', so we want to fix them. Instead of
+working with the font as a whole, we can copy the programs for those two
+characters (and only those two) into a temporary file called `^|test.mf|'.
+Then we can run \MF\ on the file `^|rtest.mf|', which says the following:
+\beginlines
+|% try all characters on `test.mf' using the parameters of cmr10|
+|if unknown cmbase: input cmbase fi|
+|mode_setup;|
+\smallskip
+|def generate suffix t = enddef;|
+|input cmr10; font_setup;|
+\smallbreak
+|let echar = endchar;|
+|def endchar = echar; stop "done with char "&decimal charcode&". " enddef;|
+|let iff = always_iff;|
+\smallskip
+|input test; bye|
+\endlines
+This will produce proofs of `k' and `S', using the |cmr10| parameters.
+Notice the simple trick by which |rtest| is able to stay in charge
+after inputting |cmr10|, without letting the |roman| driver come into
+action: `|generate|' is redefined so that it becomes innocuous.
+Furthermore |rtest| changes ^|endchar| so that \MF\ will ^{stop} and
+display each character before moving~on to the next. The `^|iff|'
+convention is changed to `|always_iff|', so that every test character will
+^^@always\_iff@ be tested even if the boolean expression is undefined;
+this makes it easier to copy from program files
+into the test file and back again, since the |iff| indications do not
+have to be touched.
+
+If you invoke \MF\ with `|\mode=lowres;| |input| |rtest|', you'll generate
+a low-resolution font called |rtest| with the parameters of |cmr10|,
+but containing only the characters in the test file. If you leave out
+the mode, you get proof mode as usual.
+
+There are similar pseudo-drivers |ttest.mf| (for |cmtt10| instead of |cmr10|),
+|btest.mf| (for |cmbx10|), etc.; these make it possible to try the
+test characters with many different parameter settings. There's also
+|ztest.mf|, which inputs parameters from a temporary file `|z.mf|' that
+contains special parameters of interest at the moment. \ (If file
+|z.mf| does not exist, you'll get a chance to specify another
+parameter file, online.) \looseness=-1
+
+A more elaborate ^{pseudo-driver file} called `|6test.mf|' allows you
+to test up to six parameter settings simultaneously, and to see the
+results all at once on your screen, as illustrated in Chapter~23.
+Here is the program that does the necessary magic:
+\beginlines
+|% try all characters on `test.mf' using six different sets of parameters|
+|if unknown cmbase: input cmbase fi|
+|mag=.5; % the user can override this equation|
+|mode_setup; let mode_setup=\;|
+\smallskip
+|boolean running;|
+|def abort = hide(scrollmode; running := false) enddef;|
+|def pause = stop "done with char "&decimal charcode&". " enddef;|
+|let iff = always_iff;|
+|def ligtable text t=enddef;|
+|def charlist text t=enddef;|
+|def extensible text t=enddef;|
+\smallbreak
+|string currenttitle;|
+|let semi = ;; let echar = endchar; let endchar = enddef;|
+|def cmchar expr s = currenttitle := s;|
+| let ; = testchar semi quote def chartext = enddef;|
+|def testchar = semi let ; = semi;|
+| running := true; errorstopmode;|
+| for k=1 upto 6:|
+| if running: if known params[k]: scantokens params[k]; font_setup;|
+| currentwindow:=k;|
+| currenttitle & ", " & fontname[k];|
+| chartext echar; fi fi endfor|
+| pause; enddef;|
+\smallbreak
+|string params[],fontname[];|
+|params[1] = "roman_params"; fontname[1] = "cmr10";|
+|params[2] = "sans_params"; fontname[2] = "cmssbx10";|
+|params[3] = "ital_params"; fontname[3] = "cmti10";|
+|params[4] = "tt_params"; fontname[4] = "cmtt10";|
+|params[5] = "bold_params"; fontname[5] = "cmb10";|
+|params[6] = "quote_params"; fontname[6] = "cmssqi8";|
+\smallbreak
+|w_rows = floor 1/2 screen_rows; w_cols = floor 1/3 screen_cols;|
+|def open(expr k,i,j)=|
+| openwindow k from ((i-1)*w_rows,(j-1)*w_cols) to (i*w_rows,j*w_cols)|
+| at (-10,140) enddef;|
+|def openit =|
+| open(1,1,1); open(2,1,2); open(3,1,3);|
+| open(4,2,1); open(5,2,2); open(6,2,3); enddef;|
+\smallbreak
+|begingroup delimiters begintext generate;|
+| def makedef(expr s)(text t) =|
+| expandafter def scantokens s = t enddef; flushtext enddef;|
+| def flushtext suffix t = enddef;|
+| for k=1 upto 6: if known params[k]:|
+| makedef(params[k])|
+| expandafter expandafter expandafter begintext|
+| scantokens ("input "&fontname[k]); fi endfor|
+|endgroup;|
+\smallskip
+|input test; bye|
+\endlines
+^^@errorstopmode@ ^^@scrollmode@ ^^@quote@ ^^@openwindow@ ^^@openit@
+^^"currentwindow" ^^@expandafter@ ^^@scantokens@
+Parameters are moved from parameter files into macros, using a trick
+discussed near the beginning of Appendix~D\null. Then ^@cmchar@ is redefined
+so that the entire text of each character-to-be-tested will be embedded
+in another macro called "chartext". Each instance of "chartext" is
+repeatedly applied to each of the six font setups.
+
+An error that occurs with the first or second set of parameters may be
+so bad that you won't want to see what happens with the third, fourth,
+fifth, and sixth sets. For example, when |test.mf| contains characters
+that are being newly designed, some equations might have been omitted
+or mistyped, so the results will be ludicrous. In this case you can
+^{interrupt} the program and type `|I|~^|abort|'. The |6test| routine
+has an |abort| macro that will stop at the end of the current font setup
+and move directly to the next character, without trying any of the
+remaining parameter combinations.
+
+It's possible to include material in |test.mf| that isn't part of
+a character program. For example, you might want to redefine a subroutine
+in the base file. Only the character programs themselves (i.e., the
+sequences of tokens between `@cmchar@' and `@endchar@;') are subject to
+six-fold repetition.
+
+Some large characters may not appear in full, because there might not be
+room for them on the screen at the stated magnification. You can make
+everything smaller by running \MF\ with, say, `|\mag=1/3; input 6test|'.
+The computer will stop with an error message, saying that the equation
+`|mag=.5|' is ^{inconsistent}; but you can safely proceed, because you
+will have the magnification you want.
+
+\endchapter
+
+An ensampull yn doyng ys more commendabull
+\indent{\cmman\char'15}en ys techyng o{\cmman\char'15}er prechyng.
+\author JOHN ^{MIRK}, {\sl The Festyuall\/} (c.\thinspace1400)
+ % from MS page 123b; p216 in Erbe's transcription
+
+\bigskip
+
+Old people love to give good advice,
+% Les vieillards aiment \`a donner de bons pr\'eceptes,
+to console themselves for no longer being able to give bad examples.
+% pour se consoler de n'\^etre plus en \'etat de donner de mauvais exemples.
+\author ^{LA ROCHEFOUCAULD}, {\sl Maximes\/} (1665)
+
+\eject
+ \beginchapter Appendix F. Font Metric\\Information
+
+The \TeX\ typesetting system assumes that some ``intelligence'' has been
+built into the fonts it uses. In other words, information stored with
+^^{TeX} \TeX's fonts will have important effects on \TeX's behavior. This
+has two consequences: (a)~Typesetting is quite flexible, since few
+conventions are frozen into \TeX\ itself. (b)~Font designers must work
+a little harder, since they have to tell \TeX\ what to do. The purpose
+of this appendix is to explain how you, as a font designer, can cope
+with~(b) in order to achieve spectacular successes with~(a).
+
+The information used by \TeX\ is embedded in compact binary files called
+\TeX\ Font Metric (^|tfm|) files. Although the `|t|' in `|tfm|' stands
+for \TeX, this is an artifact of history, because other formatting systems
+can work with |tfm| files too. The files should have been called just `|fm|',
+but it's too late now.
+
+\MF\ is able to produce two different kinds of binary output files.
+One, a `|gf|' file, contains digitized character shapes and some additional
+information needed by programs that drive printing devices; such files
+are discussed in Appendix~G\null. The other type of output is a |tfm| file,
+which contains font information used by formatting routines like \TeX;
+such files are our present concern. You get a |tfm| file if and only
+if \MF's internal quantity `^"fontmaking"' is positive at the end
+of your job. \ (Plain \MF's @mode\_setup@ routine usually sets
+"fontmaking" to an appropriate value automatically.)
+
+\medskip\ninepoint
+The |tfm| file contains some information about each character, some
+information about combinations of characters, and some information
+about the font as a whole. We shall consider these three kinds
+of information in turn. All of the font metric data that refers to
+physical dimensions should be expressed in device-independent,
+``^{sharp}'' units; when a particular font is produced with different
+modes or magnifications, all its |tfm| files should be identical.
+
+A formatting program like \TeX\ needs to know the size of each character's
+``^{bounding} ^{box}.'' For example, when \TeX\ typesets a word like
+`box', it places the first letter `b' into a little box in such a way that
+the \MF\ pixel whose lower left corner is at $(0,0)$ will appear
+on the baseline of the current line being typeset, at the left edge
+of the box. \ (We assume for simplicity that ^"xoffset" and ^"yoffset"
+were zero when `b' was shipped out.) \ The second letter,~`o', is placed
+in a second little box adjacent to the first one, so we obviously must tell
+\TeX\ how wide to make the `b'.
+
+In fact, \TeX\ also wants to know the height and depth of each letter.
+This affects the placing of ^{accents}, if you wish to typeset
+`\d{\~b}\kern.28pt\d{\~o}\kern-.28pt\d{\~x}\d{\~y}', and it also
+avoids overlap when adjacent lines contain boxes that go unusually
+far above or below the baselines.
+
+A total of four dimensions is given for each character, in sharp
+units (i.e., in units of printer's points):
+
+\smallskip
+\item\bull ^"charwd", the width of the bounding box.
+\item\bull ^"charht", the height (above the baseline) of the bounding box.
+\item\bull ^"chardp", the depth (below the baseline) of the bounding box.
+This is a {\sl positive\/} number if the character descends below the
+baseline, even though the corresponding $y$ values are negative.
+\item\bull ^"charic", the character's ``^{italic correction}.'' \TeX\
+adds this amount to the width of the box (at the right-hand side)
+in two cases: (a)~When the user specifies an italic correction explicitly,
+by typing |\/| immediately after the character. (b)~When an ^{isolated}
+character is used in math mode, unless it has a subscript but no
+superscript. For example, the italic correction is applied to `$P$' in
+the formulas `$P(x)$' and `$P^2$', but not in the formula `$P_n$';
+it is applied to position the superscript but not the subscript
+in `$P_n^2$'.
+\smallskip\noindent
+In plain \MF\ programs, you specify "charwd", "charht", and "chardp"
+in a ^@beginchar@ command, and you specify "charic" (if it's positive)
+in an ^@italcorr@ command. But @beginchar@ and @italcorr@ are macros,
+not primitives of \MF\!\null. What really happens is that \MF\ records the
+value of its internal quantities "charwd", "charht", "chardp", and "charic"
+at the time of a ^@shipout@ command. These values (and all other
+dimensions to be mentioned below) must be less than $2048"pt"\0$ in
+absolute value.
+
+A font contains at most 256 character codes; the ^{charexists} operator
+can be used to tell which codes have already appeared. If two or more
+characters are shipped out with the same code number (possibly with
+different ^"charext" values), the "charwd",
+"charht", "chardp", and "charic" of the final one are assumed to
+apply to them all.
+
+At most 15 different nonzero heights, 15 different nonzero depths,
+and 63 different nonzero italic corrections may appear in a single
+font. If these limits are exceeded, \MF\ will change one or more
+values, by as little as possible, until the restriction holds.
+A warning message is issued if such changes are necessary; for example,
+^^|some char values|
+`|(some| |charht| |values| |had| |to| |be| |adjusted| |by| |as| |much|
+|as| |0.12pt)|' means that~you had too many different nonzero heights, but
+\MF\ found a way to reduce the number to at most~15 by changing some of
+them; none of them had to be changed by more than 0.12 points. No warning
+is actually given unless the maximum amount of perturbation exceeds
+${1\over16}\pt$.
+
+\medbreak
+The next kind of information that \TeX\ wants is concerned with
+pairs of adjacent characters that are typeset from the same font.
+For example, \TeX\ moves the~`x' slightly closer to the~`o' in the
+word `box', and it moves the~`o' slightly away from the~`b', because
+of information stored in the |tfm| file for the font you're now reading.
+This space adjustment is called {\sl^{kerning}}. Otherwise (if the
+three characters had simply been placed next to each other according
+to their "charwd" values) the word would have been `b{}o{}x', which
+looks slightly worse. Similarly, there's a difference between
+`difference' and `dif{\null}ference', because the |tfm| file tells \TeX\
+to substitute the ligature `ff' when there are two f's in a row.
+
+Ligature information and kerning information is specified in short
+``^{ligtable programs}'' of a particularly simple form. Here's an example
+that illustrates most of the features (although it is not a serious
+example of typographic practice):
+\beginlines
+^|ligtable|| "f": "f" =: oct"013", "i" |\||=: oct"020", skipto 1;|
+|ligtable "o": "b": "p": "e" kern .5u#, "o" kern .5u#, "x" kern-.5u#,|
+| 1:: "!" kern u#;|
+\endlines
+This sequence of instructions can be paraphrased as follows:
+\smallskip
+\hangindent 3pc
+Dear \TeX, when you're typesetting an~`f' with this font, and when the
+following character also belongs to this font, look at it closely because
+you might need to do something special: If that following character is
+another~`f', replace the two f's by character code |oct"013"|
+[namely `\char'13'\kern.5pt];
+if it's an `i', retain the `f' but replace the `i' by character code
+|oct"020"| [a dotless `\char'20'\kern.5pt];
+otherwise skip down to label `|1::|' for further instructions.
+When you're typesetting an `o' or~`b' or~`p', if the next input to \TeX\ is
+`e' or~`o', add a half unit
+of space between the letters; if it's an `x', subtract a half unit; if it's an
+exclamation point, add a full unit. The last instruction applies also
+to exclamation points following~`f' (because of the label `|1::|').
+\smallskip\noindent
+When a character code appears in front of a colon, the colon ``labels''
+the starting place for that character's ligature and kerning program,
+which continues to the end of the ligtable statement. A double colon denotes
+a ``local label''; a |skipto| instruction advances to the next matching local
+label, which must appear before 128 ligtable steps intervene. The special
+label \|\||:| can be used to initiate ligtable instructions for an invisible
+``left boundary character'' that is implicitly present just before every
+word; an invisible ``right boundary character'' equal to ^"boundarychar" is
+also implicitly present just after every word, if "boundarychar" lies between
+0 and~255.
+
+The general syntax for ligtable programs is pretty easy to guess from
+these examples, but we ought to exhibit it for completeness:
+\beginsyntax \chardef\\=`\|
+<ligtable command>\is[ligtable]<ligtable program><optional skip>
+<ligtable program>\is<ligtable step>\alt<ligtable program>[,]<ligtable step>
+<optional skip>\is[,] [skipto]<code>\alt<empty>
+<ligtable step>\is<code><ligature op><code>
+ \alt<code>[kern]<numeric expression>
+ \alt<label><ligtable step>
+<ligature op>\is[=:]\alt[\\=:]\alt[\\=:>]\alt[=:\\]\alt[=:\\>]%
+ \alt[\\=:\\]\alt[\\=:\\>]\alt[\\=:\\>>]
+<label>\is<code>[:]\alt<code>[::]\alt[\\\\:]
+<code>\is<numeric expression>\alt<string expression>
+\endsyntax
+A \<code> should have a numeric value between 0 and 255, inclusive,
+after having been rounded to the nearest integer; or it should be a
+string of length~1, in which case it denotes the corresponding
+^{ASCII} code (Appendix~C\null). For example, |"A"| and |64.61| both
+specify the code value 65. Vertical bars to the left or right of `|=:|'
+tell \TeX\ to retain the original left and/or right character that invoked a
+ligature. Additional `|>|' signs tell \TeX\ to advance its focus of attention
+instead of doing any further ligtable operations at the current
+character position.
+
+{\sl Caution:\/} Novices often go overboard on kerning. Things usually
+work out best if you kern by at most half of what looks right to you
+at first, since kerning should not be noticeable by its presence
+(only by its absence). Kerning that looks right in a logo or in a
+headline display often interrupts the rhythm of reading when it appears
+in ordinary textual material.
+
+You can improve \TeX's efficiency by ordering the steps of a ligtable
+program so that the most frequent alternatives come first.
+\TeX\ will stop reading the program when it finds the first ``hit.''
+
+\medbreak
+Several characters of a font can be linked together in a series
+by means of a ^@charlist@ command. For example,
+\begintt
+charlist oct"000": oct"020": oct"022": oct"040": oct"060"
+\endtt
+is used in the font ^|cmex10| to specify the left parentheses that
+\TeX\ uses in displayed math formulas, in increasing order of size.
+\TeX\ follows charlists to make variable-size delimiters and
+variable-width ^{accents}, as well as to link text-size operators
+like `$\sum$' to the display-size `$\displaystyle\sum$'.
+
+\TeX\ builds up large delimiters by using ``^{extensible}'' characters,
+which are specified by giving top, middle, bottom, and repeatable
+characters in an ^@extensible@ command. For example, the extensible
+left ^{parentheses} in |cmex10| are defined by
+\begintt
+extensible oct"060": oct"060", 0, oct"100", oct"102";
+\endtt
+this says that character code |oct"060"| specifies an extensible
+delimiter constructed from itself as the top piece, from character number
+|oct"100"| as the bottom piece, and from character number |oct"102"| as
+the piece that should be repeated as often as necessary to reach
+a desired size. In this particular example there is no middle
+piece, but characters like curly braces have a middle piece as well.
+A zero value in the top, middle, or bottom position means that
+no character should be used in that part of the construction;
+but a zero value in the final position means that character number zero
+is the repeater. The width of an extensible character is taken to
+be the width of the repeater. \looseness=-1
+
+The first eight different sizes of parentheses available to \TeX\ in
+|cmex10|, when the user asks for `|\left(|', look like this:
+\begindisplay
+$\bigl(\quad\Bigl(\quad\biggl(\quad\Biggl(\quad
+\mathopen{\hbox{$\left(\vbox to20.5pt{}\right.\nulldelimiterspace=0pt$}}\quad
+\mathopen{\hbox{$\left(\vbox to23.5pt{}\right.\nulldelimiterspace=0pt$}}\quad
+\mathopen{\hbox{$\left(\vbox to26.5pt{}\right.\nulldelimiterspace=0pt$}}\quad
+\mathopen{\hbox{$\left(\vbox to29.5pt{}\right.\nulldelimiterspace=0pt$}}$
+\enddisplay
+According to what we know from the examples of @charlist@ and @extensible@
+above, the first four of these are the characters in positions
+|oct"000"|, |oct"020"|, |oct"022"|, and |oct"040"|. The other four have
+character |oct"060"| on top; character |oct"100"| is at the bottom;
+and there are respectively zero, one, two, and three occurrences
+of character |oct"102"| in the middle.
+
+Here is the formal syntax:
+\beginsyntax
+<charlist command>\is[charlist]<labeled code>
+<labeled code>\is<code>
+ \alt<label><labeled code>
+<extensible command>\is[extensible]<label><four codes>
+<four codes>\is<code>[,]<code>[,]<code>[,]<code>
+\endsyntax
+Notice that a \<label> can appear in a ligtable, charlist, or extensible
+command. These appearances are mutually exclusive: No code may be used
+more than once as a label. Thus, for example, a character with a
+ligature/kerning program cannot also be extensible, nor can it be
+in a charlist (except as the final item).
+
+\medbreak
+The last type of information that appears in a |tfm| file applies to
+the font as a whole. Two kinds of data are involved, bytes and
+numerics; and they are specified in ``headerbyte'' and ``fontdimen''
+commands, according to the following general syntax:
+\beginsyntax
+<headerbyte command>\is[headerbyte]<numeric expression>[:]<byte list>
+<fontdimen command>\is[fontdimen]<numeric expression>[:]<numeric list>
+<byte list>\is<code>
+ \alt<byte list>[,]<code>
+<numeric list>\is<numeric expression>
+ \alt<numeric list>[,]<numeric expression>
+\endsyntax
+We shall defer discussion of header bytes until later, because they
+are usually unnecessary. But @fontdimen@ commands are important.
+Numeric parameters of a font can be specified by saying, e.g.,
+\begintt
+fontdimen 3: 2.5, 6.5, 0, 4x
+\endtt
+which means that parameters 3--6 are to be 2.5, 6.5, 0, and $4x$,
+respectively. These are the parameters that \TeX\ calls |\fontdimen3|
+thru |\fontdimen6|. \ (Parameter numbering is old-fashioned:
+There is no |\fontdimen0|.)
+
+The first seven fontdimen parameters have special significance, so plain
+\MF\ has seven macros to specify them symbolically, one at a time:
+
+\smallskip
+\item\bull^@font\_slant@ (|\fontdimen1|) is the amount of ^{slant}
+per point; \TeX\ uses this information when raising or lowering an
+accent character.
+\item\bull^@font\_normal\_space@ (|\fontdimen2|) is the interword spacing.
+If the value is zero, all characters of this
+font will be considered to be ``^{isolated}'' in math mode, so the
+^{italic correction} will be added more often than otherwise.
+\item\bull^@font\_normal\_stretch@ (|\fontdimen3|) is the ^{stretchability}
+of interword spacing, as explained in {\sl The \TeX book}.
+\item\bull^@font\_normal\_shrink@ (|\fontdimen4|) is the ^{shrinkability}
+of interword spacing, as explained in {\sl The \TeX book}.
+\item\bull^@font\_x\_height@ (|\fontdimen5|) is the height of characters
+for which accents are correctly positioned. An accent over a character
+will be raised by the difference between the character's "charht"
+and this value. The ^{x-height} is also the unit of height that
+\TeX\ calls one `|ex|'.
+\item\bull^@font\_quad@ (|\fontdimen6|) is the unit of width that
+\TeX\ calls one `|em|'.
+\item\bull^@font\_extra\_space@ (|\fontdimen7|) is the additional amount
+added to the normal interword space between sentences, depending
+on the ``spacefactor'' as defined in {\sl The \TeX book}.
+\smallskip\noindent
+Parameters are zero unless otherwise specified.
+
+Math symbol fonts for \TeX\ are required to have at least 22 fontdimen
+parameters, instead of the usual seven; math extension fonts need at least~13.
+Appendix~G of {\sl The \TeX book\/} explains the precise significance
+of these additional parameters, which control such things as the
+placement of superscripts and subscripts.
+
+\medbreak
+The {\sl^{design size}\/} of a font is not one of the fontdimen
+parameters; it's an internal quantity of \MF\ that is actually output
+among the header bytes as explained below. When a \TeX\ user asks
+for a font `|at|' a certain size, the font is scaled by the ratio
+between the ``^{at size}'' and the design size. For example,
+|cmr10| has a design size of $10\pt$; if a \TeX\ user requests
+`|cmr10|~|at|~|15pt|', the result is the same as `|cmr10|~|scaled|~|1500|'
+(or, in plain \MF\ terms, |cmr10| with |mag=1.5|).
+
+What does the design size really mean? It's an imprecise notion,
+because there need be no connection between the design size and any specific
+measurement in a font. Typographers have always been vague when
+they speak about ``10~point'' fonts, because some fonts look larger
+than others even though the horizontal and vertical dimensions are the same.
+It's something like dress sizes or shoe sizes.
+
+In general, the design size is a statement about the approximate size
+of the type. Type with a larger design size generally looks bigger
+than type with a smaller design size. Two fonts with the same design
+size are supposed to work well together; for example, |cmr9| and
+|cmtt9| both have $9\pt$ design size, although the uppercase letters of
+|cmtt9| are quite a bit smaller (`|A|' versus `A').
+
+The "designsize" must be at least $1"pt"\0$. And, as with all |tfm|
+dimensions, it must be less than $2048"pt"\0$. Any other value is
+changed to $128"pt"\0$.
+
+\MF\ looks at the value of ^"designsize" only when the job ends, so you
+needn't set it before characters are shipped out. At the end of a job,
+when the |tfm| file is being written, \MF\ checks to make sure that every
+dimension of the font is less than 16 times the design size in absolute
+value, because this limitation is imposed by the |tfm| file format. Thus,
+for example, if the design size is $10\pt$, you cannot have a character
+whose width or height is $160\pt$ or more. If one or more dimensions prove
+to be too big, \MF\ will tell you how many of them had to be changed.
+
+\medbreak
+The ^@headerbyte@ command is similar to @fontdimen@, but it gives
+8-bit \<code> data instead of numeric information. For example,
+\begintt
+headerbyte 33: 0, 214, 0, "c"
+\endtt
+says that bytes 33--36 of the |tfm| file header will be 0, 214,
+0, and~99. The first four header bytes (numbers 1--4) are automatically
+set to a ^{check sum}, unless you have specified other values for
+at least one of those bytes. \ (This check sum will match a similar
+value in the |gf|~file, so that other typesetting software can check
+the consistency of the different files they use.) \ Similarly,
+the next four header bytes (numbers 5--8) are set automatically to
+the design size times $2^{20}$, unless you have specified something
+else. \looseness=-1
+
+\TeX\ looks only at the first eight header bytes, so you needn't use the
+header\-byte command if you are simply producing a font for
+standard \TeX. But other software that reads |tfm| files may have
+a need for more header information. For example, the original
+|tfm| format (developed by Lyle ^{Ramshaw} at ^{Xerox} Palo Alto
+Research Center) included ^@font\_coding\_scheme@ information
+in bytes 9--48 of the header, and ^@font\_identifier@ information in
+bytes 49--68. The design size of certain fonts was also packed into
+byte~72. Each font in the ``Xerox world'' is uniquely identified by
+its font identifier and its design size, rather than by its font file name.
+
+The ``font coding scheme'' is merely a comment that can be used
+to help understand large collections of fonts; it's usually a nice thing
+to know. Some of the coding scheme names in common use are
+\begindisplay
+|TeX text|&|TeX math italic|\cr
+|TeX typewriter text|&|TeX math symbols|\cr
+|XEROX text|&|TeX math extension|\cr
+|ASCII|&|TeX extended ASCII|\cr
+|PI|&|GRAPHIC|\cr
+\enddisplay
+The coding-scheme string should not include parentheses.
+
+Here are macros that can be used, if desired, to convert plain
+\MF's @font\_identifier@ and @font\_coding\_scheme@ into the format
+^^{substring} ^^{BCPL strings}
+required by Ramshaw's original |tfm| files:
+\beginlines
+|def BCPL_string(expr s,n) = % string s becomes an n-byte BCPL string|
+| for l:=if length(s)>=n: n-1 else: length(s) fi: l|
+| for k:=1 upto l: , substring (k-1,k) of s endfor|
+| for k:=l+2 upto n: , 0 endfor endfor enddef;|
+\smallskip
+^|inner|| end;|
+|def bye = if fontmaking>0:|
+| headerbyte 9: BCPL_string(font_coding_scheme_,40);|
+| special "codingscheme " & font_coding_scheme_;|
+| headerbyte 49: BCPL_string(font_identifier_,20);|
+| special "identifier " & font_identifier_;|
+| headerbyte 72: max(0, 254 - round 2designsize); fi|
+| end enddef;|
+^|outer|| bye,end;|
+\endlines
+These macros could be included among the ^|local.mf| extensions to
+|plain.mf| at particular installations. When a user says `^@bye@' instead
+of `^@end@', the additional headerbyte documentation will then be
+automatically inserted into the |tfm| file.
+
+\medbreak
+Let us now conclude this appendix by summarizing what we've learned.
+A \MF\ programmer can provide various types of information about how
+to typeset with a font, by using font metric commands. Simple versions
+of these commands, sufficient for simple fonts, are standard operations
+in plain \MF; examples have appeared in Chapter~11
+and the beginning of Appendix~E\null. The general cases are handled by
+five types of font metric commands:
+\beginsyntax
+<font metric command>\is<ligtable command>
+ \alt<charlist command>
+ \alt<extensible command>
+ \alt<fontdimen command>
+ \alt<headerbyte command>
+\endsyntax
+This completes the syntax of \MF\ that was left slightly unfinished
+in Chapter~26.
+
+\endchapter
+
+Such things induced me to untangle the chaos
+% Voil\`a ce qui m'a engag\'e \`a d\'ebrouiller ce chaos,
+by introducing order where it had never been before:
+% en mettant dans cette partie un ordre qui n'y avoit jamais r\'egn\'e :
+I think I may say I have had the good fortune to succeed
+% je crois avoir eu le bonheur d'y r\'eussir
+with an exactness \& a precision leaving nothing more to be desired,
+% avec une justesse \& une pr\'ecision qui ne laissent rien \`a desirer,
+by the invention of\/ {\rm Typographic points}.
+% par l'invention des \it Points typographiques.
+\author PIERRE ^{FOURNIER}, {\sl Manuel Typographique\/} (1764) % p129
+
+\bigskip
+
+One should absorb the color of life,
+but one should never remember its details.
+Details are always vulgar.
+\author OSCAR ^{WILDE}, {\sl The Picture of Dorian Gray\/} (1890)
+ % middle of ch6 in original edition [Lippincott's vol 46]; ch8 subsequently
+
+\eject
+ \beginchapter Appendix G. Generic\\Font\\Files
+
+\MF's main output goes into a ^|gf| or ``Generic Font'' file, so-called
+because it can easily be translated into any other digital font format,
+although it does not match the specifications of any ``name brand''
+manufacturer. The purpose of this appendix is to explain exactly what
+kinds of information go into the |gf| file, and under what circumstances
+\MF\ puts things there.
+
+\ninepoint\medskip
+A |gf| file is a compact binary representation of a digitized font,
+containing all the information needed by ``^{device driver}''
+software that produces printed documents from \TeX's ^|dvi| files. The
+exact internal representation scheme of |gf| files doesn't concern us
+here, but we ought to know what type of data is encoded.
+
+\smallskip
+The first thing in a |gf| file is a string that explains its origin.
+\MF\ writes strings of the form
+\begintt
+METAFONT output 1986.06.24:1635
+\endtt
+based on the values of the internal quantities ^"day", ^"month",
+^"year", and ^"time" when the |gf| file was started. \ (In this case
+$"day"=24$, $"month"=6$, $"year"=1986$, % my 25th wedding anniversary
+and $"time"=16\times60+35=995$.)
+
+After the opening string, the |gf| file contains a sequence of
+``special'' commands interspersed with shipped-out character images.
+^{Special commands} are intended to provide a loophole for future
+extensions to \MF's set of primitives, so that \MF\ itself will not
+have to change. Some specials are predefined, but others will
+undoubtedly be created in years to come. \ (\TeX\ has an analogous
+|\special| command, which puts an arbitrary string into a |dvi| file.)
+
+A special command gets into the |gf| file when you say `^@special@
+\<string>' or `^@numspecial@ \<numeric>' at a time when
+^"proofing"$\null\ge0$. A @special@ string should come before
+@numspecial@, and it
+should either be a keyword all by itself or it should consist of a keyword
+followed by a space followed by additional information. Keywords that
+specify operations requiring numeric arguments should be followed by
+numbers produced by @numspecial@. For example, the `^@proofrule@' macro
+in Appendix~B expands into a sequence of five special commands,
+\begindisplay
+@special@ |"rule"|;\cr
+@numspecial@ $x_1$; \ @numspecial@ $y_1$;\cr
+@numspecial@ $x_2$; \ @numspecial@ $y_2$;\cr
+\enddisplay
+this represents a rule on the proofsheet that runs from point $(x_1,y_1)$
+to point $(x_2,y_2)$. If you say `|grayfont gray5|', the ^@grayfont@
+macro in Appendix~B expands to `@special@ |"grayfont gray5"|'.
+Software that reads |gf| files will examine all of the special strings,
+until coming to a space or to the end of the string. If the resulting
+keyword isn't known to the program, the special string will be ignored,
+together with all numspecials that immediately follow. But when the
+keyword is known, the program will be able to determine the corresponding
+arguments. For example, the |GFtoDVI| program described in Appendix~H
+knows about the plain \MF\ keywords `|rule|' and `|grayfont|'.
+
+\MF\ might also create @special@ commands on its own initiative, but only
+when "proofing" is strictly greater than zero. There are
+two cases: (1)~When a ^\<title> statement occurs,
+the special string `|"title "|\thinspace\&\thinspace\<title>'
+is output. \ (This is how the phrase `|The letter O|' got onto your
+proofsheets in the experiments of Chapter~5.) \ (2)~Just before a
+character image is shipped out, \MF\ implicitly executes the following
+sequence of instructions:
+\begindisplay
+@if@ round $"xoffset"\ne0$: \ @special@ |"xoffset"|; \
+ @numspecial@ round ^"xoffset"; @fi@\cr
+@if@ round $"yoffset"\ne0$: \ @special@ |"yoffset"|; \
+ @numspecial@ round ^"yoffset"; @fi@\cr
+\enddisplay
+
+A ^@shipout@ command sends a digitized picture to the |gf|
+file, if $"proofing"\ge0$, but nothing is output if $"proofing"<0$.
+Furthermore the current values of ^"charwd", ^"charht", ^"chardp", ^"charic",
+^"chardx", and ^"chardy" are stored away for the current ^"charcode";
+these values are stored in all cases, regardless of the value of "proofing".
+The current character code is henceforth said to ``exist.'' ^^@charexists@
+
+When a ^{picture} is shipped out, its pixels of positive value are
+considered to be ``black,'' and all other pixels are considered to be
+``white.'' The pattern of blacks and whites is encoded in such a way
+that doubling the resolution approximately doubles the length of the
+|gf| output, in most cases.
+
+\MF\ reports its progress by typing `|[|$c$|]|' on the terminal
+when character code~$c$ is being shipped out. \ (The `^|[|' is typed
+before output conversion begins, and the `^|]|' is typed after; hence you
+can see how much time output takes.) \ If "charext" is nonzero, after
+being rounded to an integer, the typed message is `|[|$c.x$|]|' instead;
+for example, `[65.3]' refers to character~65 with extension code~3.
+
+\TeX\ allows only 256 characters per font, but extensions of \TeX\
+intended for ^{oriental} languages will presumably use the "charext"
+feature. All characters with the same code share the same width,
+height, and depth, but they can correspond to distinct graphics if they have
+different extension codes.
+
+\medbreak
+A @special@ command generally refers to the picture that follows it,
+rather than the picture that precedes~it. Special commands before the
+first digitized picture might, however, give instructions about
+the font as a whole. Special commands that follow the final picture
+invariably refer to the font as a whole. \ (For example, the
+`^@bye@' macro at the end of Appendix~F creates two special
+strings that will appear after the final character of a font.)
+
+\medbreak
+No |gf| file will be written unless a character is shipped out or a
+special command is performed at a time when $"proofing"\ge0$, or unless a
+title statement is encountered at a time when $"proofing">0$. When one of
+these things first happens, the |gf| file receives its name. If no
+^@input@ commands have yet occurred, \MF\ will set the job name to
+`^|mfput|'; otherwise the job name will already have been determined. The
+full name of the |gf| file will be
+`\<jobname>|.|\<resolution>\thinspace|gf|', where the \<resolution> is
+based on the current value of~^"hppp". \ (If $"hppp"\le0$, the resolution
+will be omitted; otherwise it will be converted to an equivalent number of
+pixels per inch, in the horizontal dimension.) \ Subsequent @input@
+operations or changes to~"hppp" will not change the ^^{file name}
+name of the |gf| file.
+
+\medbreak
+The end of a |gf| file contains a bunch of numeric data needed for
+typesetting. First come the ^{design size} and the ^{check sum};
+these match precisely the data in the |tfm| file, unless the header
+bytes of the |tfm| have explicitly been set to something else.
+Then come the values of "hppp" and "vppp". \ (These are the values
+at the end of the job, so "hppp" might not agree with the \<resolution>
+value in the |gf| file name.)
+
+Finally, the |gf| file gets the ^"charwd", ^"chardx", and ^"chardy"
+of each existing character code. The values of "chardx" and "chardy"
+represent desired ``escapements'' when characters are typeset on a
+particular device (cf.\ Chapter~12). The "charwd" values are identical to
+the widths in the |tfm| file.
+
+\medbreak
+The check sum is based entirely on the "charwd" data; two fonts
+with the same character widths will have the same check sum, but
+two fonts with different character widths will almost never have
+the same check sum.
+
+The purpose of check sums can be understood by considering the following
+scenario: A font named |cmr10| might be generated by \MF\ at any time,
+producing a |tfm| file called |cmr10.tfm| and a |gf| file called,
+say, |cmr10.300gf|. A document named |doc|, which uses |cmr10|,
+might be generated by \TeX\ at any time, producing a |dvi| file
+called |doc.dvi|; \TeX\ had to read |cmr10.tfm| in order to
+produce this |dvi| file. Now on some future date, a ``^{device driver}''
+program will be used to print |doc.dvi|, using the font
+|cmr10.300gf|. Meanwhile, the font may have changed.
+If the current |gf| file doesn't match the |tfm| file that was assumed
+by \TeX, mysterious glitches will probably occur in the printed document,
+because |dvi| information is kept concise by the assumption that the
+device driver knows the |tfm| widths of all characters. Potential
+problems are kept to a minimum if \TeX\ puts the assumed design size
+and check sum of each font into the |dvi| files it produces;
+a device driver can then issue a warning message when it finds a
+|gf| file that is inconsistent with \TeX's assumptions.
+
+\endchapter
+
+But if our\/ {\rm Letter-Cutter} \kern-1pt will have no Forge,
+yet he must of necessity accommodate himself %
+with a\/ {\rm Vice, Hand-Vice, Hammers,}
+\leavevmode{\rm Files, Small} \kern-1pt and\/ {\rm Fine Files} (commonly %
+ called\/ \kern1pt{\rm Watch-makers Files})
+of these he saves all, as they wear out.
+\author JOSEPH ^{MOXON}, {\sl Mechanick Exercises\/} (1683)
+ % part 12, section 1
+
+\bigskip
+
+The natural definition lists all possible generic characters.
+% 189. NATURALIS Character (186) notas omnes (92--113)
+% genericas possibiles (167) allegat;
+\author CAROLUS ^{LINN\AE US}, {\sl Philosophia Botanica\/} (1751)
+ % this translation due to Frans A. Stafleu
+
+\eject
+ \beginchapter Appendix H. Hardcopy Proofs
+
+A font cannot be proved correct like a mathematical theorem; a font must
+be seen to be believed. Moreover, if some characters of a font are faulty,
+the best way to fix them is to look at diagrams that indicate what went wrong.
+Therefore \MF\ is incomplete by itself; additional programs are needed to
+convert the output of \MF\ into graphic form.
+
+The purpose of this appendix is to discuss two such auxiliary programs,
+which serve as examples of many others that could be devised. The first
+of these, called ^|GFtoDVI|\null, takes |gf| files and converts them into
+^|dvi| files, which can be printed just like the output of \TeX. Each
+character image in the |gf| file will have a printed page to itself, with
+labeled points and with bounding boxes just as in the illustrations
+we have seen throughout this book. \ (Indeed, the illustrations in this
+book were produced by |GFtoDVI|\null.) \ The second auxiliary program to
+be discussed below is \TeX\ itself; we shall look at a set of \TeX\ macros
+designed to facilitate font testing.
+
+\ninepoint
+\subsection Large scale proofs. The |gf| files produced by plain \MF\
+when it is in ^"proof" mode or ^"smoke" mode can be converted to
+annotated diagrams by running them through |GFtoDVI|\null, as we know from
+the experiments in Chapter~5. It's also possible to study low-resolution
+characters with |GFtoDVI|\null, especially if plain \MF's
+`^|gfcorners|' feature has been used. ^^{low resolution proofs}
+We shall now take a thorough look at what |GFtoDVI| can do.
+
+All communication from \MF\ to |GFtoDVI| comes through the |gf| file and
+from options that you might type when you run |GFtoDVI|\null. If there are
+no ``^{special}'' commands in the |gf| file (cf.~Appendix~G\null), each page
+of |GFtoDVI|'s output will show just the ``black'' pixels of a character;
+furthermore there will be a title line at the top of the page, showing
+the date and time of the \MF\ run, together with the character code
+number and extension code (if they are nonzero). The black pixels are
+typeset via characters of a so-called ``^{gray font},'' described in
+detail below; by changing the gray font you can produce a variety of
+different outputs from a single |gf| file.
+
+To get other things on your proof sheets, ``special'' commands must
+appear in the |gf| file. For example, \MF\ will automatically output
+a |title| command, if $"proofing">0$, as explained in Appendix~G\null;
+|GFtoDVI| will typeset this title on the title line of the next character
+image that follows the command. If there are several title statements,
+they all will appear; they are supposed to fit on a single line.
+
+The most important special commands tell |GFtoDVI| to create labeled
+points on the character diagram. When you say, for example,
+`^@labels@$(1,2)$' in a plain \MF\ program, at a time when
+^"proofing"$\null>1$, the macros of Appendix~B will convert this to the
+special commands
+\begindisplay
+@special@ |" 01"|; \ ^@numspecial@ $x_1$; \ @numspecial@ $y_1$;\cr
+@special@ |" 02"|; \ @numspecial@ $x_2$; \ @numspecial@ $y_2$;\cr
+\enddisplay
+|GFtoDVI| will then put a dot labeled `|1|' at point $(x_1,y_1)$
+and a dot labeled `|2|' at~$(x_2,y_2)$.
+
+Labels are placed in one of four positions relative to their dots---%
+either at the top, the left, the right, or the bottom. |GFtoDVI| will
+ordinarily try to place all labels so that they don't interfere with
+each other, and so that they stay clear of other dots.
+ But if you want to exercise fine control over the placement
+yourself, you can say, for example, `@labels@."top"$(1a,2a)$'; in this
+case the specified labels will appear above their dots, regardless of whether or
+not other labels and/or dots are thereby overprinted. The |gf| file
+^^{labels.top} in this case will contain
+\begindisplay
+@special@ |" 11a"|; \ @numspecial@ $x_{1a}$; \ @numspecial@ $y_{1a}$;\cr
+@special@ |" 12a"|; \ @numspecial@ $x_{2a}$; \ @numspecial@ $y_{2a}$.\cr
+\enddisplay
+|GFtoDVI| looks at the character following a leading blank space to
+determine what sort of labeling convention is desired; the subsequent
+characters are the text of the label.
+
+The command `@labels@."top"$(1a,2a)$' in plain \MF\ is just an
+abbreviation for `^@makelabel@."top"(|"1a"|$,z_{1a}$);
+@makelabel@."top"(|"2a"|$,z_{2a}$)', when $"proofing">1$; the @makelabel@
+macro is really the fundamental one, and you should use it directly if you
+want more unusual effects. Suppose, for example, you just want to
+put a dot but no label at point~$z_5$; then you can say
+`@makelabel@(|""|$,z_5$)'. And suppose you want to put a label to the
+left of point~$z_5$ but with no dot; you can say
+`@makelabel@."lft".^"nodot"(|"5"|$,z_5$)'. Furthermore you could say
+`@makelabel@."lft".^"nodot"(|"5"|$,z_5-(2,3)$)' to move that label left
+by~2 pixels and down by~3 pixels, thereby getting the effect of a label
+that is diagonally adjacent to its dot. Labels without dots can also
+be used to put words on a diagram.
+
+|GFtoDVI| recognizes nine varieties of labels in all, based on the
+first two characters of the special string command:
+\smallskip
+\item\bull@makelabel@ (special |" 0"|): choose the label position automatically.
+\item\bull@makelabel@."top" (special |" 1"|): center the label just above
+the dot.
+\item\bull@makelabel@."lft" (special |" 2"|): place the label just left of
+the dot.
+\item\bull@makelabel@."rt" (special |" 3"|): place the label just right of
+the dot.
+\item\bull@makelabel@."bot" (special |" 4"|): center the label just below
+the dot.
+\item\bull@makelabel@."top"."nodot" (special |" 5"|): like "top", but omit
+the dot.
+\item\bull@makelabel@."lft"."nodot" (special |" 6"|): like "lft", but omit
+the dot.
+\item\bull@makelabel@."rt"."nodot" (special |" 7"|): like "rt", but omit
+the dot.
+\item\bull@makelabel@."bot"."nodot" (special |" 8"|): like "bot", but omit
+the dot.
+\smallskip\noindent
+The first case is called {\sl autolabeling\/}; this is the normal command.
+Autolabeling always places a dot, whether or not that dot overlaps other dots,
+but you don't always get a label. Autolabels are typeset only after
+all explicit labels have been established; then |GFtoDVI| tries to
+place as many of the remaining labels as possible.
+
+If there's no place to put an autolabel, an ``^{overflow equation}'' is
+put in the upper right corner of the proofsheet. For example, the
+overflow equation `|5 = 5r + (-4.9,0)|' means that there was no room
+for label~|5|, whose dot is 4.9 pixels to the left of the dot for~|5r|
+(which is labeled).
+
+You can avoid overflow equations by sending |GFtoDVI| the special command
+|" /"| instead of |" 0"|; ^^{/} this is a variant of autolabeling that
+does everything as usual except that the label will simply be forgotten if
+it can't be placed. To do this with plain \MF\!, set
+`$"lcode\_":=\null$|" /"|' near the beginning of your program; ^"lcode\_"
+is the string that @makelabel@ uses to specify autolabeling.
+
+The next most important kind of annotation for proofs is a straight line
+or ``^{rule}.'' Plain \MF's command for this is `^@proofrule@$(z_1,z_2)$',
+which expands to
+\begindisplay
+@special@ |"rule"|; \ @numspecial@ $x_1$; \ @numspecial@ $y_1$;\cr
+\qquad @numspecial@ $x_2$; \ @numspecial@ $y_2$.\cr
+\enddisplay
+|GFtoDVI| has trouble drawing diagonal rules, because standard ^|dvi|
+format includes no provision for drawing straight lines unless they are
+vertical or horizontal. Therefore you might get an error message
+unless $x_1=x_2$ (vertical rule) or $y_1=y_2$ (horizontal rule).
+However, a limited escape from this restriction is available via a
+``^{slant font},'' by which |GFtoDVI| is able to typeset diagonal lines
+as sequences of characters. Only one slope is permitted per job,
+but this is better than nothing (see below).
+
+To control the weight of proof rules, you say, e.g., `^@proofrulethickness@
+1.5$"mm"\0$' in a plain \MF\ program; this expands to
+\begindisplay
+@special@ |"rulethickness"|; \ @numspecial@ $1.5"mm"\0$.
+\enddisplay
+Each horizontal or vertical rule is drawn as if by a pen of the current
+rulethickness, hence you can get different weights of lines in a single
+diagram. If the current rulethickness is negative, no rule will appear; if
+it is zero, a default rulethickness based on a parameter of the gray font
+will be used; if it is positive, the stated thickness will be increased if
+necessary until it equals an integer number of pixels, and that value will
+be used to draw the rule. At the beginning of each character the current
+rulethickness is zero.
+
+You can reposition an entire diagram on its page by saying `^@proofoffset@
+$(x,y)$'; this expands to
+\begindisplay
+@special@ |"offset"|; \ @numspecial@ $x$; \ @numspecial@ $y$
+\enddisplay
+and it tells |GFtoDVI| to shift everything except the title line on the
+next character image, $x$~pixels to the right and $y$~pixels upward.
+
+|GFtoDVI| uses four fonts to typeset its output: (1) The {\sl
+{title font}\/} is used for the top line on each page.
+(2)~The {\sl{label font}\/} is used for all labels.
+(3)~The {\sl{gray font}\/} is used for dots and for black pixels.
+(4)~The {\sl{slant font}\/} is used for diagonal rules.
+Appropriate default fonts will be used at each installation unless
+you substitute specific fonts yourself, by using the @special@ commands
+^@titlefont@, ^@labelfont@, ^@grayfont@, or ^@slantfont@.
+|GFtoDVI| also understands special strings like `|"grayfontarea
+/usr/dek"|', which can be used to specify a nonstandard file area
+or directory name for the gray font. Furthermore the |gf| file might
+^^{grayfontarea} ^^{labelfontat}
+say, e.g.,
+\begindisplay
+@special@ |"labelfontat"|; @numspecial@ 20
+\enddisplay
+if you want the label font to be loaded at $20\pt$ instead of its ^{design
+size}. The area name and the at size must be given after the font name
+itself; in other words, `|"grayfont"|' cancels a previous
+`|"grayfontarea"|'.
+
+The four fonts used by |GFtoDVI| must be established before the first
+character bitmap appears in the |gf| file. This means that the special font
+commands must be given before the first ^@shipout@ or ^@endchar@ in your
+program; but they shouldn't appear until after ^@mode\_setup@, so that
+your |gf| file will have the correct name. If it's inconvenient to
+specify the fonts that way, you can change them at run time when
+you use |GFtoDVI|\null: Just type `^|/|' following the name of the |gf| file
+that's being input, and you will be asked to type special strings online.
+For example, the run-time dialog might look like this:
+\begintt
+This is GFtoDVI, Version 2.0
+GF file name: io.2602gf/
+Special font substitution: labelfont cmbx10
+OK; any more? grayfont black
+OK; any more?
+\endtt
+After the final carriage return, |GFtoDVI| does its normal thing,
+ignoring font specifications in the file that conflict with those
+just given.
+
+\subsection ^{Gray fonts}. A proof diagram constructed by |GFtoDVI| can
+be regarded as an array of rectangles, where each rectangle is either
+blank or filled with a special symbol that we shall call `{\manual R}'. A
+blank rectangle represents a white pixel, while {\manual R} represents a
+black pixel. Additional labels and reference lines are often superimposed
+on this array of rectangles; hence it is usually best to choose a symbol
+{\manual R} that has a somewhat gray appearance, although any symbol can
+actually be used.
+
+In order to construct such proofs, |GFtoDVI| needs to work with
+a special type of font known as a ``gray font''; it's possible to
+obtain a wide variety of different sorts of proofs by using different
+sorts of gray fonts. The next few paragraphs explain exactly what gray
+fonts are supposed to contain, in case you want to design your own.
+
+The simplest gray font contains only two characters, namely {\manual R}
+and another symbol that is used for dots that identify key points.
+If proofs with relatively large pixels are desired, a two-character
+gray font is all that's needed. However, if the pixel size is to be
+relatively small, practical considerations make a two-character
+font too inefficient, since it requires the typesetting of tens
+of thousands of tiny little characters; printing-device drivers
+rarely work very well when they are presented with data that is
+so different from ordinary text. Therefore a gray font with small
+pixels usually has a number of characters that replicate {\manual R} in
+such a way that comparatively few characters actually need to be
+typeset.
+
+Since many printing devices are not able to cope with
+arbitrarily large or complex characters, it is not possible for a
+single gray font to work well on all machines. In fact,
+{\manual R} must have a width that is an integer multiple of the printing
+device's units of horizontal and vertical positioning,
+since rounding the positions of grey
+characters would otherwise produce unsightly streaks on proof output.
+Thus, there is no way to make the gray font as device-independent as
+normal fonts of type can be.
+
+This understood, we can now take a look at what |GFtoDVI| expects to
+see in a gray font. The character~{\manual R} always appears in position~1. It
+must have positive height~$h$ and positive width~$w$; its depth
+and italic correction are ignored.
+
+Positions 2--120 of a gray font are reserved for special combinations of\/
+{\manual R}'s and blanks, stacked on top of each other. None of these
+character codes need be present in the font; but if they are, the slots
+must be occupied by characters of width~$w$ that have certain
+configurations of\/ {\manual R}'s and blanks, prescribed for each character
+position. For example, position~3 of the font should either contain no
+character at all, or it should contain a character consisting of two
+{\manual R}'s, one above the other; one of these {\manual R}'s should rest
+on the baseline, and the other should appear immediately below.
+
+It will be convenient to use a horizontal notation like `{\manual RSRRS}'
+to stand for a vertical stack of\/ {\manual R}'s and blanks. The convention
+will be that the stack is built from bottom to top, and the topmost
+rectangle should sit on the baseline. Thus, `{\manual RSRRS}' stands
+actually for a character of height~$h$ and depth~$4h$ that looks like this:
+\begindisplay
+\vbox{\offinterlineskip\halign{\manual#\hfil\cr
+\phantom{R}\cr
+R\rm\smash{\hbox{\raise.5pt\hbox{$\longleftarrow$ baseline}}}\cr
+R\cr
+\phantom{R}\cr
+R\cr
+}}
+\enddisplay
+We use a horizontal notation in this discussion instead of a vertical one
+because column vectors take too much space, and because the horizontal
+notation corresponds to binary numbers in a convenient way.
+
+Positions 1--63 of a gray font are reserved for the patterns {\manual R},
+{\manual RS}, {\manual RR}, {\manual RSS}, {\manual RSR}, and so~on up to
+{\manual RRRRRR}, just as in the normal binary notation of the numbers
+1--63, with {\manual R}'s substituted for 1's and blanks for 0's.
+Positions 64--70 are reserved for the special patterns {\manual RSSSSSS},
+{\manual RRSSSSS}, {\manual RRRSSSS}, {\manual RRRRSSS}, {\manual
+RRRRRSS}, {\manual RRRRRRS}, {\manual RRRRRRR} of length seven; positions
+71--78 are, similarly, reserved for the length-eight patterns {\manual
+RSSSSSSS} through {\manual RRRRRRRR}. The length-nine patterns {\manual
+RSSSSSSSS} through {\manual RRRRRRRRR} are assigned to positions 79--87,
+the length-ten patterns to positions 88--97, the length-eleven patterns to
+positions 98--108, and the length-twelve patterns to positions 109--120.
+
+Position 0 of a gray font is reserved for the ``dot'' character, which
+should have positive height~$h'$ and positive width~$w'$. When |GFtoDVI|
+wants to put a dot at some place $(x,y)$ on the figure, it positions
+the dot character so that its reference point is at $(x,y)$. The
+dot will be considered to occupy a rectangle whose corners are at
+$(x\pm w',y\pm h')$; the rectangular
+box for a label will butt up against the rectangle enclosing the dot.
+
+All other character positions of a gray font (namely, positions 121--255)
+are unreserved, in the sense that they have no predefined meaning.
+But |GFtoDVI| may access them via the ^@charlist@ feature of
+|tfm| files, starting with any of the characters in positions
+1--120. In such a case each succeeding character in a list should be
+equivalent to two of its predecessors, horizontally adjacent to each other.
+For example, in
+\begindisplay
+@charlist@ 53: 121: 122: 123
+\enddisplay
+character 121 will stand for two 53's, character 122 for two 121's (i.e.,
+four 53's), and character 123 for two 122's (i.e., eight 53's). Since
+position~53 contains the pattern {\manual RRSRSR}, character~123 in this example
+would have height~$h$, depth~$5h$, and width~$8w$, and it would stand for
+the pattern
+\begindisplay
+\vbox{\offinterlineskip\halign{\manual#\hfil\cr
+RRRRRRRR\cr
+\phantom{SSSSSSSS}\rm
+ \smash{\hbox{\raise.5pt\hbox{$\longleftarrow$ baseline}}}\cr
+RRRRRRRR\cr
+\phantom{SSSSSSSS}\cr
+RRRRRRRR\cr
+RRRRRRRR\cr
+}}
+\enddisplay
+Such a pattern is, of course, rather unlikely to occur in a |gf| file,
+but |GFtoDVI| would be able to use if it were present. Designers
+of gray fonts should provide characters only for patterns that they think
+will occur often enough to make the doubling worthwhile. For example,
+the character in position 120 ({\manual RRRRRRRRRRRR}), or whatever is the
+tallest stack of\/ {\manual R}'s present in the font, is a natural candidate for
+repeated doubling.
+
+Here's how |GFtoDVI| decides what characters of the gray font will be used,
+given a configuration of black and white pixels: If there are no black
+pixels, stop. Otherwise look at the top row that contains at least one
+black pixel, and the eleven rows that follow. For each such column,
+find the largest~$k$ such that $1\leq k\leq120$ and the gray font contains
+character~$k$ and the pattern assigned to position~$k$ appears in the
+given column. Typeset character $k$ (unless no such character exists)
+and erase the corresponding black pixels; use doubled characters,
+if they are present in the gray font, if two or more consecutive equal
+characters need to be typeset. Repeat the same process on the remaining
+configuration, until all the black pixels have been erased.
+
+If all characters in positions 1--63 are present, this process is guaranteed to
+take care of at least six rows each time; and with characters 64--120 as well,
+it usually takes care of twelve, since all patterns that contain at most
+one ``run'' of\/ {\manual R}'s are present.
+
+Some of the ^@fontdimen@ parameters discussed in Appendix~F are important
+in gray fonts. The ^@font\_slant@ value~$s$, if nonzero, will cause
+|GFtoDVI| to skew its output; in this case the character {\manual R} will
+presumably be a parallelogram with a corresponding slant, rather than the
+usual rectangle. \MF's coordinate $(x,y)$ will appear in physical position
+$(xw+yhs,yh)$ on the proofsheets. \ (This is appropriate for proofing unslanted
+fonts whose pixels will become slanted by mechanical obliquing.)
+
+Parameter @fontdimen@ 8 of a gray font specifies the thickness of rules
+that go on the proofs. If this parameter is zero, \TeX's default
+rule thickness (0.4\thinspace pt) will be used.
+The other parameters of a gray font are ignored by |GFtoDVI|\null, but
+it is conventional to set ^@font\_normal\_space@ and ^@font\_quad@ to~$w$,
+^@font\_x\_height@ to~$h$.
+
+For best results the designer of a gray font should choose $w$ and~$h$
+so that the user's |dvi|-to-hardcopy software will not make any
+rounding errors. Furthermore, the dot should be an even number~$2m$ of
+pixels in diameter, and the rule thickness should work out to an
+even number~$2n$ of pixels; then the dots and rules will be centered on
+the correct positions, in the common case of integer coordinates. Gray fonts
+are almost always intended for particular output devices, even though
+`|dvi|' stands for ``device independent''; we use |dvi| files for \MF\
+proofs chiefly because software to print |dvi| files is already in place.
+
+The \MF\ program for a fairly versatile gray font generator, called
+`^|grayf.mf|', appears on the next few pages. It should be invoked by a
+parameter file that establishes values of several quantities:
+\smallskip
+\item\bull If ^"large\_pixels" is of type @boolean@, only 15 characters
+will be generated; otherwise there will be 123.
+\item\bull If ^"pix\_picture" is of type @picture@, it should be the
+desired pixel image `{\manual R}', and in this case ^"pix\_wd" and
+^"pix\_ht" should be the width and height in pixels. Otherwise a default
+gray pixel pattern will be used.
+\item\bull If ^"rep" is known, it should be a positive integer; the default
+pixel pattern will be replicated so that the final
+proofs will be "rep" times bigger than usual, and the pattern will be clipped
+slightly at the edges so that discrete pixels can be seen plainly.
+\item\bull If ^"lightweight" is of type @boolean@, the default pixel
+pattern will be only half as dark as usual.
+\item\bull If ^"dotsize" is known, it should be the diameter of the
+special dot character, in pixel units.
+\item\bull The ^@font\_identifier@ should be specified.
+\smallskip\noindent
+(The "rep" and "lightweight" options are ignored if "pix\_picture" is
+explicitly given.) \
+Since gray fonts are inherently device-dependent, we do not start
+with ``sharp'' dimensions as in normal fonts; we go backwards and
+compute the sharp units from pixel units.
+
+The name of each gray font should include the name of the device for
+which it is intended. \ (A ``favorite'' proof device can also be chosen
+at each installation, for which the alternate font names `^|gray|'
+and `^|black|' are valid; these installation-dependent fonts are the
+defaults for "proof" mode and "smoke" mode.)
+
+Here, for example, is a suitable parameter file `|graycheap.mf|', which
+generates a vanilla-flavored gray font for the hypothetical "cheapo"
+printer:
+\beginlines
+|% Gray font for Cheapo with proofsheet resolution 50 pixels per inch|
+\smallskip
+|if mode<>cheapo: errmessage "This file is for cheapo only"; fi|
+\smallskip
+|font_identifier "GRAYCHEAP";|
+|input grayf|
+\endlines
+(The proofsheet resolution will be 50 pixels per inch, because "cheapo" has
+200 pixels per inch, and the default "pix\_picture" in |grayf|
+will be four pixels square in this case.) \ If the default pixel pattern
+turns out to be such a dark gray that the labels and rules are obscured,
+the statement `|boolean lightweight|' should be added. A solid black font
+with slightly higher-resolution images can be generated by the following
+file `|blackcheap.mf|':
+\beginlines
+|% Black font for Cheapo with proofsheet resolution 66.7 pixels per inch|
+\smallskip
+|if mode<>cheapo: errmessage "This file is for cheapo only"; fi|
+\smallskip
+|picture pix_picture; pix_wd := pix_ht := 3;|
+|pix_picture := unitpixel scaled 3;|
+\smallskip
+|font_identifier "BLACKCHEAP";|
+|input grayf|
+\endlines
+And here is a file `|graycheap5.mf|' that generates a gray font suitable
+for studying large proofs of low-resolution characters:
+\beginlines
+|% Gray font for Cheapo with proofsheet resolution 10 pixels per inch|
+\smallskip
+|if mode<>cheapo: errmessage "This file is for cheapo only"; fi|
+\smallskip
+|rep=5; boolean large_pixels;|
+\smallskip
+|font_identifier "GRAYCHEAP";|
+|input grayf|
+\endlines
+
+Now let's look at the program file `|grayf.mf|' itself. It begins with
+a simple test to ensure that "mag" and "rep" are positive integers, if
+they're known; then comes some less obvious code that handles
+magnification in a nonstandard way:
+\beginlines
+|% More-or-less general gray font generator|
+|% See Appendix H of The METAFONTbook for how to use it|
+\smallskip
+|forsuffixes m = mag,rep:|
+| if unknown m: m := 1;|
+| elseif (m<1) or (m<>floor m):|
+| errmessage "Sorry, " & str m & " must be a positive integer";|
+| m := 1; fi endfor|
+\smallbreak
+|mg := mag; mag := 1; mode_setup;|
+|if mg>1: hppp := hppp*mg; vppp := vppp*mg;|
+| extra_endchar:=|
+| "if charcode>0:currentpicture:=currentpicture scaled mg;fi"|
+| & extra_endchar; fi;|
+\endlines
+This circumlocution is the easiest way to guarantee that the ^|tfm| file
+will be completely unaffected by magnification.
+
+The next part of |grayf| computes the pixel representation, "pix\_picture".
+\beginlines
+|if picture pix_picture: rep := 1;|
+| cull pix_picture keeping (1,infinity);|
+|else: for z=(0,2),(1,0),(2,3),(3,1):|
+| fill unitsquare shifted z; endfor|
+| if not boolean lightweight:|
+| addto currentpicture also|
+| currentpicture rotated 90 xscaled -1; fi|
+| if unknown scale: scale := max(1,round(pixels_per_inch/300)); fi|
+| pix_wd := pix_ht := 4scale;|
+| if rep>1: picture pix;|
+| currentpicture := currentpicture shifted-(1,1); pix := currentpicture;|
+| for r=1 upto rep-1: addto currentpicture also pix shifted(4r,0); endfor|
+| cullit; pix := currentpicture;|
+| for r=1 upto rep-1: addto currentpicture also pix shifted(0,4r); endfor|
+| unfill unitsquare xscaled 4rep yscaled 2 shifted-(1,1);|
+| unfill unitsquare yscaled 4rep xscaled 2 shifted-(1,1); cullit; fi|
+| picture pix_picture; pix_picture := currentpicture scaled scale;|
+| pix_wd := pix_ht := 4scale*rep; fi|
+\weakendlines
+The lightweight pattern has 4 of every 16 pixels turned on; the normal
+pattern has twice as many.
+
+Character 0 is the dot, which is quite simple:
+\beginlines
+|def # = *72.27/pixels_per_inch enddef;|
+|if unknown dotsize: dotsize := 2.5pix_wd/rep; fi|
+\smallskip
+|beginchar(0,1.2dotsize#,1.2dotsize#,0);|
+|fill fullcircle scaled dotsize scaled mg; endchar;|
+\endlines
+
+The special coding scheme of gray fonts is implemented next:
+\beginlines
+|numeric a[]; newinternal b,k;|
+|def next_binary =|
+| k := 0; forever: if k>b: a[incr b] := 0; fi|
+| exitif a[k]=0; a[k] := 0; k := k+1; endfor|
+| a[k] := 1 enddef;|
+|def next_special_binary =|
+| if a[0]=1: for k=0 upto b: a[k] := 0; endfor a[incr b]|
+| else: k := 0; forever: exitif a[incr k]=1; endfor|
+| a[k-1] fi := 1 enddef;|
+\smallbreak
+|def make_char =|
+| clearit; next_binary;|
+| for k=0 upto b: if a[k]=1:|
+| addto currentpicture also pix_picture shifted(0,-k*pix_ht); fi endfor|
+| charcode := charcode+1; chardp := b*charht;|
+| scantokens extra_endchar; shipout currentpicture enddef;|
+\endlines
+
+Now we are ready to generate all the pixel characters.
+^^@charlist@^^"chardx"^^"charwd"^^"charht"
+\beginlines
+|charwd := pix_wd#; charht := pix_ht#; chardx := pix_wd*mg;|
+|b := -1;|
+\smallskip
+|if boolean large_pixels:|
+| for k=1 upto 7: make_char; charlist k:k+120; endfor|
+| charcode := 120; b := -1;|
+| addto pix_picture also pix_picture shifted (chardx,0);|
+| charwd := 2charwd; chardx := 2chardx;|
+| for k=1 upto 7: make_char; endfor|
+|else: for k=1 upto 63: make_char; endfor|
+| let next_binary = next_special_binary;|
+| for k=64 upto 120: make_char; endfor|
+| for k=121,122: charcode := k;|
+| addto currentpicture also currentpicture shifted (chardx,0);|
+| charwd := 2charwd; chardx := 2chardx;|
+| scantokens extra_endchar; shipout currentpicture; endfor|
+| charlist 120:121:122; fi|
+\endlines
+
+The program closes by establishing fontwide parameters:
+\beginlines
+|font_coding_scheme "GFGRAY";|
+|font_size 8(pix_wd#);|
+|font_normal_space pix_wd#;|
+|font_x_height pix_ht#;|
+|font_quad pix_wd#;|
+|fontdimen 8: if known rulethickness: rulethickness|
+| else: pix_wd#/(2rep) fi;|
+|bye.|
+\weakendlines
+(The extra complications of an ^"aspect\_ratio" or a slant have not
+been addressed.)
+
+\subsection ^{Slant fonts}. |GFtoDVI| also makes use of another special
+type of font, if it is necessary to typeset slanted rules. The format of
+such so-called ``slant fonts'' is quite a bit simpler than the format of
+gray fonts.
+
+A slant font contains exactly $n$ characters, in positions 1 to~$n$,
+for some positive integer~$n$.
+The character in position~$k$ represents a slanted line $k$ units
+tall, starting at the baseline. These lines all have a fixed slant ratio~$s$.
+The vertical ``unit'' is usually chosen to be an integral number of pixels,
+small enough so that it suffices to draw rules that are an
+integer number of units high; in fact, it should probably be no larger
+than the thickness of the rules being drawn.
+
+The following simple algorithm is used to typeset a rule that is $m$ units
+high: Compute $q=\lceil m/n\rceil$; then typeset $q$~characters of
+approximately equal size, namely $(m\bmod q)$ copies of character number
+$\lceil m/q\rceil$ and $q-(m\bmod q)$ copies of character number
+$\lfloor m/q\rfloor$. For example, if $n=15$ and $m=100$, we have $q=7$;
+a 100-unit-high rule will be composed of 7~pieces, using characters
+14,~14, 14, 14, 14, 15,~15.
+
+|GFtoDVI| looks at the ^"charht" of character $n$ only, so the |tfm| file
+need not be accurate about the heights of the other characters. \ (This is
+fortunate, since ^|tfm| format allows at most 15 different nonzero
+heights per font.)
+
+The ^"charwd" of character~$k$ should be $k/n$ times $s$ times the "charht"
+of~$n$.
+
+The ^@font\_slant@ parameter should be $s$. It is customary to
+set the parameter @fontdimen@~8 to the thickness of
+the slanted rules, but |GFtoDVI| doesn't look at it.
+
+Here's an example of a slant-font parameter file, `|slantcheap6|',
+for the "cheapo" printer and a slant of 1/6:
+\beginlines
+|% Slant font for Cheapo with slope 1/6|
+\smallskip
+|if mode<>cheapo: errmessage "This file is for cheapo only"; fi|
+\smallskip
+|s=1/6; % the slant ratio|
+|n=30; % the number of characters|
+|r#=.4pt#; % thickness of the rules|
+|u=1; % vertical unit|
+\smallskip
+|font_identifier "SLANTCHEAP6";|
+|input slant|
+\endlines
+The corresponding program file `|slant.mf|' looks like this:
+\beginlines
+|% More-or-less general slant font generator for GFtoDVI|
+|% The calling file should set the font_identifier and|
+|% n = number of characters|
+|% s = slant ratio|
+|% r# = rule thickness (in sharp units)|
+|% u = vertical unit (in pixels)|
+\smallskip
+|if unknown mag: mag := 1;|
+|elseif (mag<1) or (mag<>floor mag):|
+| errmessage "Sorry, mag must be a positive integer"; mag := 1; fi|
+\smallbreak
+|mg := mag; mag := 1; mode_setup; u# := u*72.27/pixels_per_inch;|
+|pixels_per_inch := pixels_per_inch*mg; fix_units;|
+\smallbreak
+|define_whole_pixels(u); define_blacker_pixels(r);|
+|pickup pencircle scaled r; ruler := savepen;|
+\smallbreak
+|for k=1 upto n:|
+| beginchar(k,k*u#*s,n*u#,0);|
+| pickup ruler; draw origin--(k*u*s,k*u); % draw the line|
+| unfill (lft-1,bot -1)--(rt 1,bot -1)|
+| --(rt 1,0)--(lft-1,0)--cycle; % clip the ends|
+| unfill ((lft -1,0)--(rt 1,0)|
+| --(rt 1,top 1)--(lft -1,top 1)--cycle) shifted (k*u*s,k*u);|
+| endchar; endfor|
+\smallbreak
+|font_size 16pt#;|
+|font_slant s;|
+|fontdimen 8: r#;|
+|font_coding_scheme "GFSLANT";|
+|bye.|
+\endlines
+
+\subsection Font samples. The real test of a font is its appearance
+at the final size, after it has actually been typeset. The \TeX\
+typesetting system can be used with the following example macro file
+`^|testfont.tex|' (in addition to plain \TeX\ format) to
+put a new font through its paces.
+
+We shall comment on typical uses of |testfont| as we examine its parts.
+At the beginning, |testfont.tex| turns off several of \TeX's normal features.
+\beginlines
+|% A testbed for font evaluation|
+\smallskip
+|\tracinglostchars=0 % missing characters are OK|
+|\tolerance=1000 % and so are loose lines|
+|\raggedbottom % pages can be short|
+|\nopagenumbers % and they won't be numbered|
+|\parindent=0pt % nor will paragraphs be indented|
+|\hyphenpenalty=200 % hyphens are discouraged|
+|\doublehyphendemerits=30000 % and two in a row are terrible|
+\smallskip
+|\newlinechar=`@ % we want to type multiline messages|
+|\chardef\other=12 % and redefine "catcodes"|
+\smallskip
+|\newcount\m \newcount\n \newcount\p \newdimen\dim % temporary variables|
+\endlines
+
+Then there are macros to print the time and date---an extremely valuable
+thing to have on any proofsheet.
+\beginlines
+|\def\today{\ifcase\month\or|
+| January\or February\or March\or April\or May\or June\or|
+| July\or August\or September\or October\or November\or December\fi|
+| \space\number\day, \number\year}|
+|\def\hours{\n=\time \divide\n 60|
+| \m=-\n \multiply\m 60 \advance\m \time|
+| \twodigits\n\twodigits\m}|
+|\def\twodigits#1{\ifnum #1<10 0\fi \number#1}|
+\endlines
+
+An online ``menu'' of the available test routines will be typed at your
+terminal if you request |\help|.
+\beginlines
+|{\catcode`\|\||=0 \catcode`\\=\other % use |\|| as the escape, temporarily|
+\||gdef|\||help{|\||message{%|
+|\init switches to another font;@%|
+|\end or \bye finishes the run;@%|
+|\table prints the font layout in tabular format;@%|
+|\text prints a sample text, assuming TeX text font conventions;@%|
+|\sample combines \table and \text;@%|
+|\mixture mixes a background character with a series of others;@%|
+|\alternation interleaves a background character with a series;@%|
+|\alphabet prints all lowercase letters within a given background;@%|
+|\ALPHABET prints all uppercase letters within a given background;@%|
+|\series prints a series of letters within a given background;@%|
+|\lowers prints a comprehensive test of lowercase;@%|
+|\uppers prints a comprehensive test of uppercase;@%|
+|\digits prints a comprehensive test of numerals;@%|
+|\math prints a comprehensive test of TeX math italic;@%|
+|\names prints a text that mixes upper and lower case;@%|
+|\punct prints a punctuation test;@%|
+|\bigtest combines many of the above routines;@%|
+|\help repeats this message;@%|
+|and you can use ordinary TeX commands (e.g., to \input a file).}}}|
+\endlines
+
+The program prompts you for a font name. If the font is in your local
+directory instead of a system directory, you might have to
+specify the directory name as part of the font name. You should
+also specify scaling if the font has been magnified, as in the example
+of Chapter~5. Several fonts can be tested during a single run, if you
+say `|\init|' before `|\end|'.
+\beginlines
+|\def\init{\message{@Name of the font to test = }|
+| \read-1 to\fontname \startfont|
+| \message{Now type a test command (\string\help\space for help):}}|
+|\def\startfont{\font\testfont=\fontname \spaceskip=0pt|
+| \leftline{\sevenrm Test of \fontname\unskip\ on \today\ at \hours}|
+| \medskip|
+| \testfont \setbaselineskip|
+| \ifdim\fontdimen6\testfont<10pt \rightskip=0pt plus 20pt|
+| \else\rightskip=0pt plus 2em \fi|
+| \spaceskip=\fontdimen2\testfont % space between words (\raggedright)|
+| \xspaceskip=\fontdimen2\testfont|
+| \advance\xspaceskip by\fontdimen7\testfont}|
+\endlines
+The specified font will be called |\testfont|. As soon as you have
+specified it, |\init| calls on |\startfont|, which puts a title line
+on the page; then it chooses what it hopes will be a good distance between
+baselines, and gets ready to typeset text with ``^{ragged right}'' margins.
+\ (The code above improves on plain \TeX's ^|\raggedright|.)
+
+The baselineskip distance is taken to be $6\pt$ plus the height of the
+tallest character plus the depth of the deepest character. This is the
+distance between baselines for ``series'' tests, but it is decreased
+by $4\pt$ when the sample text is set. If you
+want to change the baseline distance chosen by |testfont|,
+you can just say, e.g., `|\baselineskip=11pt|'.
+\beginlines
+|\def\setbaselineskip{\setbox0=\hbox{\n=0|
+|\loop\char\n \ifnum \n<255 \advance\n 1 \repeat} % 256 chars in \box0|
+|\baselineskip=6pt \advance\baselineskip\ht0 \advance\baselineskip\dp0 }|
+\endlines
+
+When |testfont| prompts you for a ``^{background character}''
+or a ``^{starting character}'' or an ``^{ending character},'' you
+can type the character you want (assuming ASCII code);
+or you can say, e.g., `|#35|' to get character code number 35.
+Codes 0--32 and 127--255 have to be specified with the `|#|' option,
+on non-fancy installations of \TeX,
+and so does code 35 (which is the ASCII code of `|#|' itself).
+\beginlines
+|\def\setchar#1{{\escapechar-1\message{\string#1 character = }%|
+| \def\do##1{\catcode`##1=\other}\dospecials|
+| \read-1 to\next|
+| \expandafter\finsetchar\next\next#1}}|
+|\def\finsetchar#1#2\next#3{\global\chardef#3=`#1|
+| \ifnum #3=`\# \global\chardef#3=#2 \fi}|
+|\def\promptthree{\setchar\background|
+| \setchar\starting \setchar\ending}|
+\endlines
+(The \TeX\ hackery here is a bit subtle, because special characters
+like `|\|' and `|$|' must temporarily lose their special significance.)
+
+Suppose the background character is `|o|' and the starting and ending
+characters are respectively `|p|' and~`|q|'. Then the ^|\mixture|
+operation will typeset `|opooppooopppop|' and `|oqooqqoooqqqoq|';
+the ^|\alternation| operation will typeset `|opopopopopopopopo|' and
+`|oqoqoqoqoqoqoqoqo|'. Other patterns could be added in a similar way.
+\beginlines
+|\def\mixture{\promptthree \domix\mixpattern}|
+|\def\alternation{\promptthree \domix\altpattern}|
+|\def\mixpattern{\0\1\0\0\1\1\0\0\0\1\1\1\0\1}|
+|\def\altpattern{\0\1\0\1\0\1\0\1\0\1\0\1\0\1\0\1\0}|
+|\def\domix#1{\par\chardef\0=\background \n=\starting|
+| \loop \chardef\1=\n #1\endgraf|
+| \ifnum \n<\ending \advance\n 1 \repeat}|
+\endlines
+
+The |\series| operation puts the background character between all the
+others (e.g., `|opoqo|'). Special series containing the lowercase
+letters of \TeX\ text fonts (including `\char'31', `\char'32',
+`\char'33', and ~\char'34') and the uppercase letters (including
+`\char'35', `\char'36', and~`\char'37') are provided.
+Although |\mixture| and |\alternation| show you the effects of
+ligatures and kerning, |\series| does not.
+\beginlines
+|\def\!{\discretionary{\background}{\background}{\background}}|
+|\def\series{\promptthree \!\doseries\starting\ending\par}|
+|\def\doseries#1#2{\n=#1\loop\char\n\!\ifnum\n<#2\advance\n 1 \repeat}|
+|\def\complower{\!\doseries{`a}{`z}\doseries{'31}{'34}\par}|
+|\def\compupper{\!\doseries{`A}{`Z}\doseries{'35}{'37}\par}|
+|\def\compdigs{\!\doseries{`0}{`9}\par}|
+|\def\alphabet{\setchar\background\complower}|
+|\def\ALPHABET{\setchar\background\compupper}|
+\endlines
+(A long series might fill more than one line; \TeX's |\discretionary|
+break operation is used here so that the background character will end
+the line and be repeated at the beginning of the next.)
+
+A ``comprehensive'' test uses a series of background characters
+against a series of others. The series will consist of lowercase
+letters (`^|\lowers|'), uppercase letters (`^|\uppers|'), or
+numerals (`^|\digits|').
+\beginlines
+|\def\lowers{\docomprehensive\complower{`a}{`z}{'31}{'34}}|
+|\def\uppers{\docomprehensive\compupper{`A}{`Z}{'35}{'37}}|
+|\def\digits{\docomprehensive\compdigs{`0}{`4}{`5}{`9}}|
+|\def\docomprehensive#1#2#3#4#5{\par\chardef\background=#2|
+| \loop{#1} \ifnum\background<#3\m=\background\advance\m 1|
+| \chardef\background=\m \repeat \chardef\background=#4|
+| \loop{#1} \ifnum\background<#5\m=\background\advance\m 1|
+| \chardef\background=\m \repeat}|
+\endlines
+
+The ^|\names| test puts uppercase letters and accents
+together with lowercase letters. The accents will look funny
+if the test font doesn't have them in plain \TeX's favorite positions.
+\beginlines
+|\def\names{ {\AA}ngel\aa\ Beatrice Claire|
+| Diana \'Erica Fran\c{c}oise Ginette H\'el\`ene Iris|
+| Jackie K\=aren {\L}au\.ra Mar{\'\i}a N\H{a}ta{\l}{\u\i}e {\O}ctave|
+| Pauline Qu\^eneau Roxanne Sabine T\~a{\'\j}a Ur\v{s}ula|
+| Vivian Wendy Xanthippe Yv{\o}nne Z\"azilie\par}|
+\endlines
+
+Punctuation marks are tested in juxtaposition with different
+sorts of letters, by the `^|\punct|' macro:
+\beginlines
+|\def\punct{\par\dopunct{min}\dopunct{pig}\dopunct{hid}|
+| \dopunct{HIE}\dopunct{TIP}\dopunct{fluff}|
+| \$1,234.56 + 7/8 = 9\% @ \#0\par}|
+|\def\dopunct#1{#1,\ #1:\ #1;\ `#1'\|
+| ?||`#1?\ !||`#1!\ (#1)\ [#1]\ #1*\ #1.\par}|
+\endlines
+
+Mixtures and alternations and series are excellent ways to discover
+that letters are too dark, too light, or too tightly spaced. But
+a font also has to be readable; in fact, this is the number one
+objective. So |testfont| provides a sample `^|\text|'. One of the sentences
+is optional, because it contains lots of accents and unusual letters;
+you can omit it from the text by saying `^|\omitaccents|'.
+Furthermore, you can type your own text, online, or you can input one from
+^^{Stanfords} ^^{Kafka} ^^{AEsop}
+a file, instead of using this canned example.
+\beginlines
+|\def\text{{\advance\baselineskip-4pt|
+|\setbox0=\hbox{abcdefghijklmnopqrstuvwxyz}|
+|\ifdim\hsize>2\wd0 \ifdim 15pc>2\wd0 \hsize=15pc \else\hsize=2\wd0 \fi\fi|
+|On November 14, 1885, Senator \& Mrs.~Leland Stanford called together at|
+|their San Francisco mansion the 24~prominent men who had been chosen as|
+|the first trustees of The Leland Stanford Junior University. They|
+|handed to the board the Founding Grant of the University, which they had|
+|executed three days before. This document---with various amendments,|
+|legislative acts, and court decrees---remains as the University's|
+|charter. In bold, sweeping language it stipulates that the objectives of|
+|the University are ``to qualify students for personal success and direct|
+|usefulness in life; and to promote the publick welfare by exercising an|
+|influence in behalf of humanity and civilization, teaching the blessings|
+|of liberty regulated by law, and inculcating love and reverence for the|
+|great principles of government as derived from the inalienable rights of|
+|man to life, liberty, and the pursuit of happiness.'' \moretext|
+|(!||`THE DAZED BROWN FOX QUICKLY GAVE 12345--67890 JUMPS!)\par}}|
+|\def\moretext{?||`But aren't Kafka's Schlo{\ss} and {\AE}sop's {\OE}uvres|
+|often na{\"\i}ve vis-\`a-vis the d{\ae}monic ph{\oe}nix's official|
+|r\^ole in fluffy souffl\'es? }|
+|\def\omitaccents{\let\moretext=\relax}|
+\endlines
+
+Now comes one of the hardest parts of the file, from the \TeX\
+standpoint: The |\table| macro prints a font diagram, omitting
+groups of sixteen characters that are entirely absent from the font.
+The format of this table is the same as that used in Appendix~F
+of {\sl The \TeX book}. When the font contains unusually large characters
+that ought to be vertically centered, you should say `^|\centerlargechars|'
+before `|\table|'. \ (A \TeX\ math symbol font or math extension font
+would use this feature.)
+\beginlines
+|\def\oct#1{\hbox{\rm\'{}\kern-.2em\it#1\/\kern.05em}} % octal constant|
+|\def\hex#1{\hbox{\rm\H{}\tt#1}} % hexadecimal constant|
+|\def\setdigs#1"#2{\gdef\h{#2}% \h=hex prefix; \0\1=corresponding octal|
+| \m=\n \divide\m by 64 \xdef\0{\the\m}%|
+| \multiply\m by-64 \advance\m by\n \divide\m by 8 \xdef\1{\the\m}}|
+|\def\testrow{\setbox0=\hbox{\penalty 1\def\\{\char"\h}%|
+| \\0\\1\\2\\3\\4\\5\\6\\7\\8\\9\\A\\B\\C\\D\\E\\F%|
+| \global\p=\lastpenalty}} % \p=1 if none of the characters exist|
+|\def\oddline{\cr|
+| \noalign{\nointerlineskip}|
+| \multispan{19}\hrulefill&|
+| \setbox0=\hbox{\lower 2.3pt\hbox{\hex{\h x}}}\smash{\box0}\cr|
+| \noalign{\nointerlineskip}}|
+|\newif\ifskipping|
+|\def\evenline{\loop\skippingfalse|
+| \ifnum\n<256 \m=\n \divide\m 16 \chardef\next=\m|
+| \expandafter\setdigs\meaning\next \testrow|
+| \ifnum\p=1 \skippingtrue \fi\fi|
+| \ifskipping \global\advance\n 16 \repeat|
+| \ifnum\n=256 \let\next=\endchart\else\let\next=\morechart\fi|
+| \next}|
+|\def\morechart{\cr\noalign{\hrule\penalty5000}|
+| \chartline \oddline \m=\1 \advance\m 1 \xdef\1{\the\m}|
+| \chartline \evenline}|
+|\def\chartline{&\oct{\0\1x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&}|
+|\def\chartstrut{\lower4.5pt\vbox to14pt{}}|
+|\def\table{$$\global\n=0|
+| \halign to\hsize\bgroup|
+| \chartstrut##\tabskip0pt plus10pt&|
+| &\hfil##\hfil&\vrule##\cr|
+| \lower6.5pt\null|
+| &&&\oct0&&\oct1&&\oct2&&\oct3&&\oct4&&\oct5&&\oct6&&\oct7&\evenline}|
+|\def\endchart{\cr\noalign{\hrule}|
+| \raise11.5pt\null&&&\hex 8&&\hex 9&&\hex A&&\hex B&|
+| &\hex C&&\hex D&&\hex E&&\hex F&\cr\egroup$$\par}|
+|\def\:{\setbox0=\hbox{\noboundary\char\n\noboundary}%|
+| \ifdim\ht0>7.5pt\reposition|
+| \else\ifdim\dp0>2.5pt\reposition\fi\fi|
+| \box0\global\advance\n 1 }|
+|\def\reposition{\setbox0=\vbox{\kern2pt\box0}\dim=\dp0|
+| \advance\dim 2pt \dp0=\dim}|
+|\def\centerlargechars{|
+| \def\reposition{\setbox0=\hbox{$\vcenter{\kern2pt\box0\kern2pt}$}}}|
+\endlines
+
+Two of the most important combinations of tests are treated now:
+^|\sample| prints the |\table| and the |text|; ^|\bigtest| gives
+you the works, plus a mysterious word that is traditional in type
+specimens: ^^{hamburgefonstiv}
+\beginlines
+|\def\sample{\table\text}|
+\smallskip
+|\def\bigtest{\sample|
+| hamburgefonstiv HAMBURGEFONSTIV\par|
+| \names \punct \lowers \uppers \digits}|
+\endlines
+
+Finally, there's a |\math|
+routine useful for checking out the spacing in the ^{math} ^{italic}
+fonts used by plain \TeX; |\mathsy| does a similar thing for the
+uppercase letters in a math symbols font.
+\beginlines
+|\def\math{\textfont1=\testfont \skewchar\testfont=\skewtrial|
+| \mathchardef\Gamma="100 \mathchardef\Delta="101|
+| \mathchardef\Theta="102 \mathchardef\Lambda="103 \mathchardef\Xi="104|
+| \mathchardef\Pi="105 \mathchardef\Sigma="106 \mathchardef\Upsilon="107|
+| \mathchardef\Phi="108 \mathchardef\Psi="109 \mathchardef\Omega="10A|
+| \def\ii{i} \def\jj{j}|
+| \def\\##1{|\||##1|\||+}\mathtrial|
+| \def\\##1{##1_2+}\mathtrial|
+| \def\\##1{##1^2+}\mathtrial|
+| \def\\##1{##1/2+}\mathtrial|
+| \def\\##1{2/##1+}\mathtrial|
+| \def\\##1{##1,{}+}\mathtrial|
+| \def\\##1{d##1+}\mathtrial|
+| \let\ii=\imath \let\jj=\jmath \def\\##1{\hat##1+}\mathtrial}|
+|\newcount\skewtrial \skewtrial='177|
+|\def\mathtrial{$\\A \\B \\C \\D \\E \\F \\G \\H \\I \\J \\K \\L \\M \\N|
+| \\O \\P \\Q \\R \\S \\T \\U \\V \\W \\X \\Y \\Z \\a \\b \\c \\d \\e \\f|
+| \\g \\h \\\ii \\\jj \\k \\l \\m \\n \\o \\p \\q \\r \\s \\t \\u \\v \\w|
+| \\x \\y \\z \\\alpha \\\beta \\\gamma \\\delta \\\epsilon \\\zeta|
+| \\\eta \\\theta \\\iota \\\kappa \\\lambda \\\mu \\\nu \\\xi \\\pi|
+| \\\rho \\\sigma \\\tau \\\upsilon \\\phi \\\chi \\\psi \\\omega|
+| \\\vartheta \\\varpi \\\varphi \\\Gamma \\\Delta \\\Theta \\\Lambda|
+| \\\Xi \\\Pi \\\Sigma \\\Upsilon \\\Phi \\\Psi \\\Omega|
+| \\\partial \\\ell \\\wp$\par}|
+|\def\mathsy{\begingroup\skewtrial='060 % for math symbol font tests|
+| \def\mathtrial{$\\A \\B \\C \\D \\E \\F \\G \\H \\I \\J \\K \\L|
+| \\M \\N \\O \\P \\Q \\R \\S \\T \\U \\V \\W \\X \\Y \\Z$\par}|
+| \math\endgroup}|
+\endlines
+
+The last line of |testfont| is
+\beginlines
+|\ifx\noinit!\else\init\fi|
+\endlines
+and it means ``automatically call `^|\init|' unless `|\noinit|' is
+an exclamation point.'' Why this? Well,
+you might have your own test file from which you'd like to use the
+facilities of |testfont|, without typing commands online.
+If your file says `|\let\noinit!| |\input testfont|' \TeX\ will
+read in |testfont| but the routine will not prompt you for a file name.
+The file can then continue to test one or more fonts by saying, e.g.,
+\beginlines
+|\def\fontname{cmbx10 }\startfont\sample\vfill\eject|
+|\def\fontname{cmti10 scaled \magstep3}\startfont\sample\vfill\eject|
+\endlines
+thereby defining ^|\fontname| directly, and using ^|\startfont|
+to do the initialization instead of\/ |\init|.
+
+\medbreak
+To conclude this appendix, let's look at the listing of a file
+that can be used to test special constructions in math fonts
+with the conventions of plain \TeX:
+\beginlines
+|\raggedright \rightskip=2em plus 5em minus 2em|
+\smallbreak
+|$\hbar \not\equiv B$, but $\sqrt C \mapsto \sqrt x$,|
+|$Z \hookrightarrow W$, $Z \hookleftarrow W$,|
+|$Z \longmapsto W$, $Z \bowtie W$, $Z \models W$,|
+|$Z \Longrightarrow W$, $Z \longrightarrow W$,|
+|$Z \longleftarrow W$, $Z \Longleftarrow W$,|
+|$Z \longleftrightarrow W$, $Z \Longleftrightarrow W$,|
+|$\overbrace{\hbox{very long things for testing}}$,|
+|$\underbrace{\hbox{very long things for testing}}$,|
+|$Z \choose W$, $Z \brack W$, $Z \brace W$, $Z \sqrt W$,|
+|$Z \cong W$, $Z \notin W$, $Z \rightleftharpoons W$,|
+|$\widehat Z$, $\widehat{ZW}$, $\widehat{Z+W}$,|
+|$\widetilde Z$, $\widetilde{ZW}$, $\widetilde{Z+W}$.|
+\smallbreak
+|\def\sizetest#1#2{$$|
+| \Bigggl{#1}\bigggl{#1}\Biggl{#1}\biggl{#1}\Bigl{#1}\bigl{#1}\left#1|
+| \bullet|
+| \right#2\bigr{#2}\Bigr{#2}\biggr{#2}\Biggr{#2}\bigggr{#2}\Bigggr{#2}$$}|
+|\def\biggg#1{{\hbox{$\left#1\vbox to20.5pt{}\right.$}}}|
+|\def\bigggl{\mathopen\biggg} \def\bigggr{\mathclose\biggg}|
+|\def\Biggg#1{{\hbox{$\left#1\vbox to23.5pt{}\right.$}}}|
+|\def\Bigggl{\mathopen\Biggg} \def\Bigggr{\mathclose\Biggg}|
+\smallbreak
+|\sizetest () \sizetest [] \sizetest \lgroup\rgroup|
+|\sizetest \lmoustache\rmoustache \sizetest \vert\Vert|
+|\sizetest \arrowvert\Arrowvert \sizetest \uparrow\downarrow|
+|\sizetest \updownarrow\Updownarrow \sizetest \Uparrow\Downarrow|
+|\sizetest \bracevert{\delimiter"342} \sizetest \backslash/|
+|\sizetest \langle\rangle \sizetest \lbrace\rbrace|
+|\sizetest \lceil\rceil \sizetest \lfloor\rfloor|
+\smallbreak
+|$$\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{\sqrt{-1}}}}}}}}}$$|
+\smallbreak
+|\def\dobig{\do\bigvee \do\bigwedge \do\bigotimes \do\bigoplus \do\bigodot|
+| \do\bigcap \do\bigcup \do\biguplus \do\bigsqcup|
+| \do\int \do\ointop \do\smallint \do\prod \do\coprod \do\sum}|
+|\def\do#1{#1_a^b A} $\dobig$ $$\dobig$$|
+\smallbreak
+|\bye|
+\endlines
+
+\endchapter
+
+Be sure of it: Giue me the Occular proofe.
+\author WILLIAM ^{SHAKESPEARE}, {\sl Othello\/} (1604) % act 3 sc 3 l 360
+
+\bigskip
+
+The figure itself appears here
+as a very necessary adjunct to the verbalization.
+In Euclid's presentation we cannot wholly follow the argumentation
+without the figure, and unless we are strong enough
+to imagine the figure in our mind's eye, we would also be reduced
+to supplying our own figure if the author had not done it for us.
+Notice also that the language of the proof has a
+formal and severely restricted quality about it.
+This is not the language of history, nor of drama,
+nor of day to day life;
+this is language that has been sharpened and refined so as to serve
+the precise needs of a precise but limited intellectual goal.
+\author P. J. ^{DAVIS} and R. ^{HERSH}, {\sl Proof\/} (1981)
+ % The Mathematical Experience (Birkh\"auser), near p150
+
+\eject
+ \beginchapter Appendix I. Index
+
+\maxdepth=4pt
+The author has tried to provide as complete an index as possible, so that
+people will be able to find things that are tucked away in obscure
+corners of this long book. Therefore the index itself is rather long. A
+short summary of the simpler aspects of \MF\ appears at the beginning of
+Appendix~B; a summary of the standard character classes for tokens
+can be found at the end of Chapter~6; a summary of other special things
+appears under `tables' below.
+
+\medskip\ninepoint
+Page numbers are \underbar{underlined} in the index when they represent
+the definition or the main source of information about whatever is being
+indexed. \ (Underlined entries are the most definitive, but not
+necessarily the easiest for a beginner to understand.) \ A page number is
+given in italics (e.g., `{\it123\/}') when that page contains an instructive
+example of how the concept in question might be used. Sometimes both
+underlining and italics are appropriate. When an index entry refers to a
+page containing a relevant exercise, the answer to that exercise (in
+Appendix~A) might divulge further information; an answer page is not
+indexed here unless it refers to a topic that isn't included in the
+statement of the relevant exercise.
+
+\smallskip
+Index entries for quoted symbols like `T' refer to example programs
+that draw the symbols in question.
+
+\smallskip
+Symbolic tokens that are preceded by an asterisk (*) in this index are
+primitives of \MF; i.e., they are built in. It may be dangerous to
+redefine them.
+
+\begindoublecolumns
+\eightpoint \baselineskip=9.9pt % shooting for 53 lines/page
+\parskip=0pt plus .8pt
+\newdimen\sqht \sqht=2.4pt % \square
+\newbox\astbox \setbox\astbox=\hbox to0pt{\hss\lower1pt\hbox{*}}
+\raggedright \tolerance=5000 \hbadness=5000 \parfillskip 0pt plus 3em
+\ttglue=.4em
+\def\<#1>{\leavevmode\hbox{$\mkern-2mu\langle$#1\/$\rangle$}}
+\let\oldcstok=\cstok
+\def\cstok{\leavevmode\kern-2pt\oldcstok}
+\def\vdots{\vbox{\baselineskip 3pt\kern2pt\hbox{.}\hbox{.}\hbox{.}}}
+\def\ddots{\mathinner{\mskip1mu\raise5pt\vbox{\kern2pt\hbox{.}}\mskip2mu
+ \raise3pt\hbox{.}\mskip2mu\raise1pt\hbox{.}\mskip1mu}}
+\def\see{{\sl see\/}~\ignorespaces}
+\def\also{\hfil\penalty50\hfilneg{\sl see~also\/}~\ignorespaces}
+\let\oldttv=\ttverbatim
+\let\+=\relax
+\def\sub{\penalty100 \vskip -\parskip \quad}
+\def\LaTeX{L\kern -.36em\raise.6ex\hbox{\sixrm A}\kern-.15em\TeX}
+\def\MF{{\manual opqrstuq}}
+\def\ttverbatim{\oldttv \catcode`\*=\other \catcode`\,=\other
+ \catcode`\.=\other \catcode`\;=\other \catcode`\@=\other \catcode`\+=\other}
+\let\comma=, \let\period=. \let\asterisk=* \let\semicolon=;
+\def\backup{\leavevmode\kern-1pt}\catcode`\-=11
+\catcode`\*=\active \def*{\leavevmode\copy\astbox}
+\catcode`\,=\active \def,{\eightrm\comma}
+\catcode`\;=\active \def;{\/\eightrm\semicolon}
+\catcode`\.=\active \def.{\eightrm\period\par\hangindent 2em }
+\catcode`\+=\active \def+#1{\ifcat0\noexpand#1$\setbox0\hbox{#1}\dp0=0pt
+ \underline{\box0}$\let\next=+%
+ \else\let\next=#1\fi \next}
+\catcode`\@=\active \let@=\eightit
+\parindent=0pt
+\hyphenpenalty=10000 \exhyphenpenalty=10000
+\def\newletter{\medbreak\hangindent 2em}
+\hangindent 2em
+|6test.mf|, 312--313.
+|#| (hash mark), \see sharped dimensions.
+`\#', 200--201.
+|##| (traced equation), 80--83, 239.
+|###| (removed independent variable), 83.
+|####| (deduced equation), 81.
+*|#@| (prefix of at point), +177, @251.
+|%| (percent sign), 43, +50.
+*|&| (ampersand), 213--214, \see concatenation.
+\sub for preloaded bases, +35, 279.
+|'| (apostrophe or prime), @25, @55, @81.
+|"| (double-quote mark), +50--+51.
+|""| (empty string), +188, 236, 254, @276, @294, @328.
+`(', 103--105, 128, 318.
+|(| (left parenthesis), 59, +60, 61, @62--@63, 71--73, 165, 210--215.
+|((|, 51.
+|)| (right parenthesis), 59, +60, 61, @62--@63, 71--73, 165, 210--215.
+|))|, 51.
+*|[| (left bracket), 9--10, +54, 55, 60, +72, 80, 211--212, 298--299, 324.
+|[[|, 61.
+|[]| (collective subscript), +56, 177, @273.
+|[1]| (progress report), 37, +324.
+*|]| (right bracket), 9--10, +54, 55, 60, +72, 80, 211--212, 298--299, 324.
+|]]|, 61, @162, +262, 299.
+*|{| (left brace), 16--18, 60, +129, 213.
+|{{|, 61, 289.
+*|}| (right brace), 16--18, 60, +129, 213.
+|}}|, 61, 289.
+*|+| (plus sign), @62, @63, +72, 80, 211.
+*|++| (Pythagorean addition), @+66, @67, 72, 211.
+\sub (double edge), 117, 296--297.
+|+++| (triple edge), 296--297.
+*|+-+| (Pythagorean subtraction), @+66, 72, 211, @238.
+*|-| (minus sign), @62, @63, +72, 80, 211, 297.
+|--| (straight join), @24--@26, 127--129, @234, +262.
+\sub (double edge), 117, 296--297.
+|---| (tense join), @107, 127--129, +262.
+\sub (triple edge), 296--297.
+|->| (macro expansion), 44, 160, 249, 251.
+`---' (em dash), 306.
+|_| (underline), 49, +51, 173, 265, 270.
+|*| (asterisk), 285--286.
+\sub as prompt character, 31, 37, 279.
+\sub \llap{\char`\*}as times sign, @59, @62--@64, +72, +73, 80, 211--212.
+|**|, as command-line prompt, 31--32, 35--40, 187, 269, 279.
+\sub as exponentiation sign, @59, @64, 72, @237, @251, +265.
+|/| (slash), 328, 329.
+\sub \llap{\char`\*}as divided-by sign, @59, @62, @63, +72, 80, 82, 210--211.
+\| (vertical line), 117, 297.
+*|\| (backslash), +179, @236, @262.
+\sub at beginning of command line, @31, @38, 40.
+|\\|, +262.
+*|<| (less than sign), @64, @65, +170, 210, 237.
+*|<=| (less than or equal to), @64, 65, +170, 210, 282.
+|<-| (argument value), 160.
+*|<>| (unequal to), @64, 65, +170, 210, 282.
+\<> (angle brackets), 49--50.
+*|=| (equals sign), @5, @6, @23, @64, @75--@85, +88, 97, 165, 167, +170,
+ 171, 210, 218.
+|==|, 292.
+*|=:| (ligature replacement), @305, @306, @316, +317.
+*\||=:|, @316, +317.
+*\||=:>|, +317.
+*|=:|\|, +317.
+*|=:|\||>|, +317.
+*\||=:|\|, +317.
+*\||=:|\||>|, +317.
+*\||=:|\||>>|, +317.
+\leavevmode{\tt \rlap/=} (unequals sign), 282.
+*|>| (greater than sign), @64, +170, 210, 237.
+|>>| (shown value), 41, 62.
+*|>=| (greater than or equal to), @64, 65, +170, 210, 282.
+*|,| (comma), 57, 72, 73, 129, 155, 165--167, 171, 317, 318.
+|,,|\thinspace, 51.
+|.|~(period), 43, +50, 51.
+`\char`\.', 306.
+*|..| (free join), @7, @15--@19, @24, 127--133, 213.
+|...| (bounded join), 18--19, 127, 248, +262.
+|...| (truncation of displayed context), 44.
+*|;| (semicolon), 155, 169, 171, 172, 187, 217, 223--224, 263, 312.
+|;;|\thinspace, 51.
+*|:| (colon), 169, 171, 317--319.
+*|::| (local label), +317.
+*\|\||:| (left boundary label), +317.
+*|:=| (gets), @28, @33, 87, +88, 97, @98, 155--156, 159, 165, 167, 171,
+ 176, 218, 282.
+|?|, @41, +42--+43.
+|???|, @224, +262.
+|!| (exclamation point), 41, 189.
+*|@| (at point), +177, @251.
+*|@#| (suffix of at point), @176, +177, +178, 251, @273--@274.
+\newletter
+`a', 192.
+`A', 163, 164, 248, 302--303.
+|abort|, 312--313.
+|abs| (absolute value), @66, 82, @238, +264.
+accents, 315, 317.
+accuracy, 50, @62--@69, @143, 237.
+ad hoc dimensions, 92, @95.
+Adams, John, 359.
+addition of pictures, 115, @117, @245.
+addition of vectors, 9, @68.
+*|addto|, +118--+119, @144, @151, @242--@245.
+\<addto command>, +220.
+|adjust_fit|, 306--308.
+{\AE}schylus, 47.
+{\AE}sopus, 340.
+affine transformations, 247.
+algebraic operations, 59--73, 209--215, 230.
+Algol, 57, 89.
+Alingham, William, 189.
+Allen, Fred (= Sullivan, John Florence), 85.
+almost digitized character, 296.
+*|also|, +118, 220, @242--@245.
+|\alternation|, 338.
+alternatives, 169.
+|always_iff|, +307, @311--@312.
+ambiguous points, 150, 198--200, 204.
+American Mathematical Society, ii, ix.
+anatomy of \MF, 169, 179, 217, 285, +344.
+*|and|, @65, +129, +170, 210, 213, 288--289.
+Anderson, Izett William, 299.
+*|angle|, @29, @67, +72, @107, @135, 211, @238.
+angle brackets, 49--50.
+angle of pen, 21--22, 26--28, 152, 164.
+arccosine, arcsine, arctangent, \see |angle|.
+arguments, 159--160, +166--+167, 210, 288.
+arithmetic, 59--63.
+arrays, 54--57.
+ASCII, 49, 188, 281--283, 317.
+*|ASCII|, 72, +188, 211.
+|aspect_ratio|, 94, 145, 204, 269, 335.
+\<assignment>, +88.
+assignments, @28, @33, 87--89, @98, 159.
+*|at|, +191, 220, @252, @277, @312.
+at size, 96, 319.
+*|atleast|, 129, +132, 213, @262.
+*|autorounding|, 127, 195, +204--+205, @206, 212, @262, @264, 271--272.
+axis, 103.
+\newletter
+`b', 308.
+background character, @40, 338--339.
+Backus, John Warner, 49.
+backwards path, 119.
+|badio.mf|, 41, 223.
+|barheight|, 96, 161, 199, 302--303.
+base file, 34--35, 261, 278--279, +304, 307.
+baseline, 75--77, +101.
+\<basic path join>, 129, +213.
+*|batchmode|, +219, 226.
+BCPL strings, 320.
+bean-like shape, 15--16, 21--22, 24--25.
+beauty, v, 185.
+Beethoven, Ludwig van, 185.
+|beginchar|, 35, 76, @96, 102--103, 107, 115, 148, 156, 197, 199, 204,
+ +275, 316.
+*|begingroup|, +155--+157, 175, 178, 210--215, 217, @236, @243, @275, @289.
+|beginlogochar|, 160, 302.
+Bell, Eric Temple, 11.
+bell-shaped distribution, 251.
+Bernshte{\u\i}n, Serge{\u\i} \thinspace Natanovich, 14.
+\sub polynomials, 14, 133, 152, 246, 298--299.
+B\'ezier, Pierre Etienne, 14.
+Bibby, Duane Robert, i.
+Bierce, Ambrose Gwinnett, ix.
+|\bigtest|, +341.
+Billawala, Nazneen Noorudin, 266, 294.
+binary search, 176--177, @293--@294.
+black, 270, 332--333.
+black-letter, 294.
+black/white reversal, 115.
+|blacker|, 93--94, 268, +270--+271.
+|blankpicture|, 192, +263.
+Boole, George, 170.
+*|boolean|, 55, +56.
+\<boolean expression>, 170, +210.
+Boolean expressions, 170, 257.
+\<boolean primary>, 170, +210.
+\<boolean secondary>, 170, +210.
+\<boolean tertiary>, 170, +210.
+|bot|, @23, 80, 147, 151, 204, +273.
+boundaries, 24--29, 123--125.
+*|boundarychar|, 212, 317.
+bounded curves, 19, 132.
+bounding box, 22, 35, 76, +101--+107, 276, 307, 315.
+bounding triangle, 19, 132.
+box, \see bounding box.
+|bp| (big point), 92, +267, 268.
+braces, 16--18, 60, +129, 213.
+bracket notation, \see mediation.
+brackets, 9--10, +54, 55, 60, +72, 80, 211--212, 298--299, 324.
+broad-edge pens, 26--29, 151--152, 162--165.
+Bront\"e, Emily Jane, 73.
+Bruck, Richard Hubert, 29.
+buffer size, 226, 286.
+built-up symbols, 318.
+Burkitt, William, 99.
+Burns, Robert, 299.
+|bye|, +278, 279, @306, +321, 324.
+|byte|, +264, @275.
+\<byte list>, +318.
+\newletter
+$c$ code, 106, 324.
+Camden, William, 51.
+Campbell, John Campbell, 359.
+|cand|, 288--289.
+|CAPSULE|, 239.
+|capsule_def|, 264.
+capsules, 159, 166, 172, 210, 239, 247, 254, 264.
+Carter, Matthew, 207.
+Cartesian coordinates, 5--6, 191.
+|cc| (cicero), 92, +267, 268.
+|ceiling|, @65, 66, 72, +264.
+|\centerlargechars|, 340, +341.
+chance, 183--185.
+|change_width|, @199, +276, +309.
+*|char|, 187, +188, 214, @263.
+*|charcode|, 106, 210, 212, +220, @275, 324.
+*|chardp|, 106, 212, 220, @275, +315--+316, 324.
+*|chardx|, 106, 212, 220, @276, +324, @334.
+*|chardy|, 212, +324.
+*|charexists|, +106, 210, 316, 324.
+*|charext|, 106, 212, +220, 316, 324.
+*|charht|, 106, 212, 220, @275, +315--+316, 324, @334, @335.
+*|charic|, 106, 212, 220, @275, +315--+316, 324.
+*|charlist|, @317, +318, 331, @334, @335.
+\<charlist command>, +318.
+*|charwd|, 106, 212, 220, @275, +315--+316, 324, @334, @335.
+|cheapo|, 91--93, 99, 278--279, 332--333.
+check sums, 320, 324, +325.
+Chinese characters, 3, 106, 324.
+circles, 123--124, 148.
+|clear_pen_memory|, 147, +273, @278, @310.
+|clearit|, 115, @242, @275, +277, 295.
+|clearpen|, +272, @275.
+|clearxy|, @275, +277.
+|cm| (centimeter), @18, 92, +267, 268.
+|cm.base|, 35, 279, 311.
+|cmchar|, @306, +307, 312--313.
+|cmex10|, 317--318.
+|cmmf|, 35, 279.
+|cmr9|, 203, 320.
+|cmr10|, 101, 305--306, 319.
+|cmr10.mf|, 305.
+|cmsl10|, 101.
+|cmtt10|, 306.
+\<code>, +317.
+codes, 281--283.
+Colburn, Dorothy, 107.
+collective subscripts, 56, 177.
+\<command>, +217.
+command line, 38, 187, 269, 277, 301.
+commands, 155, 217--220, 230, 321.
+comments, 43, 50--51.
+commutativity, 247.
+comparison, @65--@66, 80, 170.
+compass directions, 26, 119, 206--207, 228--229.
+complex numbers, 69.
+\<compound>, +217.
+compound statement, +155, 217.
+Computer Modern, 35, 103--105, 203, 206, 279, 304--313.
+concatenation, of paths, @70--@71, @123, 127, 129, +130, @245.
+\sub of strings, @69, 84--85, +187, @278, @286, @312.
+\<condition>, +169.
+conditional and/or, 288--289.
+conditions, 169--171, 179, 219, 259.
+constants, 59, @62, 263--264.
+contents of this manual, table, x--xi.
+*|contour|, +118--+119, 220.
+control points, @13--@19, 70--71, 133, 229.
+*|controls|, @19, 70--71, +129--+130, 133, @152, 213.
+\<controls>, 129, +213.
+conversion to pixel units, 259, +268.
+convex polygons, @119, 147, 297--298.
+Conway, John Horton, 121.
+coordinates, 5--11, 23, 109, 191, 193.
+|cor|, 288--289.
+corner pixels, 93--94.
+*|cosd|, @67, 72, 211.
+cosines, 67, 69.
+counterclockwise, 111, 119, 229, 255.
+|counterclockwise|, +264.
+Cowper, William, 51.
+|craziness|, 184--185.
+crispness, 103--104.
+cube roots, 177.
+cubes, 113.
+*|cull|, 118, +120, @151, @243--@245.
+\<cull command>, 118, +220.
+|culldraw|, @271, +272.
+culling, 113, 120, @151, @242--@245, 296.
+|cullit|, @113, 120, @242, @243, +277.
+Cundall, Frank, 299.
+*|curl|, @17, +128--+131, 213, 234.
+|currentbreadth|, 310--311.
+|currentnull|, 295.
+|currentpen|, 118, 147, 150, 204, +271--+272.
+|currentpicture|, 114, @115, @116, 118, 120, 191, +271--+272, 295.
+|currenttransform|, 94, @145, 204, +269, 271, 301, 310.
+|currentwindow|, 192, @312.
+curves, 13--19, \see paths.
+cusps, 136.
+|cutdraw|, @151, +271--+272.
+|cutoff|, @150, +272.
+*|cycle|, @15, @16, @24--@28, @69, +129--+131, 170, 171, 210, 213.
+\newletter
+|d|, 35, @76, 102, 204, +275.
+`d', 294.
+da Vinci, Leonardo, 19.
+dangerous bend, vii, 11, 106--107, 115, 143.
+Darwin, Charles Robert, 57.
+data structures, 53--57.
+Davis, Philip Jacob, 343.
+*|day|, +212, 218, 323.
+|dd| (didot point), 92, +267, 268.
+de Casteljau, Paul de Faget, 14.
+debugging tricks, 229--231, 286.
+*|decimal|, +187--+188, 214.
+\<decimal digit>, +50.
+decimal point, 50--51.
+decimal representation, 188.
+\<declaration>, 56, +171.
+\<declaration list>, +57.
+declarations, 56--57.
+declarative versus imperative, 87.
+\<declared suffix>, +57.
+\<declared variable>, +57, 175.
+|decr|, +266.
+*|def|, @36, @159--@162, +165--+167.
+|default_wt_|, 271--272.
+|define_blacker_pixels|, @33, 92--93, @106, +268, 302.
+|define_corrected_pixels|, 93, 197, +268, 302.
+|define_good_x_pixels|, 199, +268, 302.
+|define_good_y_pixels|, 199, +268, 302.
+|define_horizontal_corrected_pixels|, @204, +268, 302.
+|define_pixels|, @33, 92, @106, 199, +268, 302.
+|define_whole_blacker_pixels|, 202, +268.
+|define_whole_pixels|, 199, +268, 302.
+|define_whole_vertical_blacker_pixels|, +268.
+|define_whole_vertical_pixels|, @204, +268, 302.
+\<definition>, +165.
+\<definition heading>, +165.
+definitions, 159--167, 175--180.
+deleting tokens, 42--43, 225.
+\<delimited parameters>, +165.
+delimiters, 61, 167, 210, 254, 288--289.
+*|delimiters|, 61, 180, 210, +218, @221, @262, @296, @299, @313.
+\<delimiters command>, +218.
+dependent variables, +81--+83, 88, 224.
+depth, 101.
+Derek, Bo, 287.
+Descartes, Ren\'e, 6, 11, 19.
+design size, 96, +319--+320, 324, 329.
+*|designsize|, 212, 320.
+device drivers, 323, 325.
+diagnostic aids, 229--231, 259, 286.
+diamond-shaped nib, 148--149, 297.
+Dickens, Charles John Huffam, 145.
+difference of pictures, 115, @244.
+digestion process, 179, 217--221.
+\<digit string>, +50.
+digitization, 111, 149, 195--207, 230.
+|\digits|, 339.
+dimensions, 92, +267.
+|dir|, @18, @67, @68, @83--@84, @135, @163--@164, 175, @233, +264.
+|direction|, @69, 70, @135, @235, +265.
+\<direction specifier>, 129, +213.
+|directionpoint|, @135, +265.
+*|directiontime|, @135, @+136, 211, 245, 265, @295.
+|dishing|, 152, 164.
+*|display|, +191--+192, 220.
+\<display command>, +220.
+|displaying|, 269, 276, 278.
+distance, 76, 84.
+|ditto|, @187, +263.
+|div|, +265.
+division, @59, @62, @63, 80, 82.
+\sub of numeric tokens, 61, 73.
+Dopping, Olle, 181.
+|dot|, 306, 311.
+dot product, 69.
+|dotprod|, @69, 178, @238, 265.
+|dotsize|, 332, 334.
+double-quote mark, 50--51, 187.
+*|doublepath|, 118, +119, @151, 220.
+doubly filled pixels, 110--112.
+|down|, @32, +263.
+|downto|, 172, +262.
+|draw|, @7, @15--@19, 21, 112, 118--120, 145, 147, 150, 198, 230, +271, 295.
+\sub one point, 22, 150, 200, 253.
+|drawdot|, @31, 113, 147, +150, 234, +271.
+Drayton, Michael, 279.
+drift, 102, 106.
+driver files, 304--306.
+*|dropping|, 118, +120, 220.
+D\"urer, Albrecht, 13, 19.
+*|dump|, 217, +221, 262, @279, @311.
+|.dvi|, 32, 40, 103, 106, 323, 327, 328.
+\newletter
+|e|, 27--29, 273.
+`E', 96--97, 204, 302--303.
+edge structure, 116--117, 296--297.
+edges, 116.
+editing, 46.
+efficiency, 39, 99, 141, 144, 147, 228, 230, 234, 244, 264, 265, 277,
+ 291, 297, 298.
+El Palo Alto, 124--126, 139, 228--229.
+ellipses, 123, 126.
+Ellis, Henry Havelock, 11.
+*|else|, +169--+170, 179.
+*|elseif|, +169--+170, 179.
+em dash, 306.
+emergency stops, 226.
+empty option in {\bf for\/} list, 171, @299.
+empty statement, 155, 217.
+empty text argument, 299.
+*|end|, @31, @37, 155, 167, 217, 221, 226, 278, 287, 305, @321.
+end of a file, 287.
+|endchar|, @36, 102, 156, 191, +276, 309, 311, 329.
+*|enddef|, @94, @159--@164, 165, @175--@178.
+*|endfor|, @18, @39, +171--+172, @173, 250, @290.
+|ENDFOR|, 45, 286, 290.
+*|endgroup|, +155--+157, 167, 175, 178, 210--215, 217, @236, @243,
+ @276, @289, @290.
+ending character, @40, 338--339.
+*|endinput|, +179, @287--@288.
+endpoints, 128, 150--151.
+|ENE|, 119, 206--207, 228.
+enormous number, 63, 236.
+envelopes, 118--119, 150, 230.
+|eps|, 93, @199--@200, 229, +263, @310--@311.
+|epsilon|, @62--@69, 115, @135, 152, 229, +263.
+equality test, general, 292.
+equality versus equation, 171.
+|equally_spaced|, 290.
+\<equation>, +88.
+equations, @5, @6, @23, @75--@85, 88, @141, 171.
+\sub nonlinear, 84--85, 176--177, @292--@294.
+equilateral triangle, 25, 203.
+|erase|, @113, 120, 167, +271, 272.
+*|errhelp|, +189, 219, @294.
+*|errmessage|, @178, +189, 219, @294.
+error messages, 41--46, 223--228.
+*|errorstopmode|, +219, 227, @313.
+|ESE|, 206--207, 228--229.
+*|everyjob|, 180, +219.
+\<everyjob command>, +219.
+Evetts, Leonard Charles, 153.
+exercises, viii, 5--231.
+\<exit clause>, +171.
+*|exitif|, 171, +173, @176, 179, @262.
+|exitunless|, 173, +262.
+expandable tokens, 179, 230.
+*|expandafter|, +179, 180, @270, @286--@290, @313.
+expansion process, +179--+180, @285--@291.
+exponential, \see |mexp|.
+*|expr|, @160, @162, 165, @166, 167, @176, 210.
+|(EXPR|$_n$|)|, 44, 160, 249, 251.
+|expr.mf|, +61, 62--71, 116--117, 132, 135--137, 142--143, 150, 173.
+\<expression>, 167, +209.
+expressions, 59--73, 209--215.
+*|extensible|, 318.
+\<extensible command>, +318.
+external tags, 55, 218.
+|extra_beginchar|, 275--276, @278.
+|extra_endchar|, 276, @277, @309.
+|extra_setup|, 269, @270, @278.
+|!| |Extra| |tokens| |will| |be| |flushed|, 43--44, 224--225.
+\newletter
+`F', 97, 204, 302--303.
+*|false|, 55, @64--@65, 170, 210.
+faster operation, 39, 99, 141, 144, 147, 228, 230, 234, 244, 264, 265, 277,
+ 291, 297, 298.
+|Fatal| |base| |file| |error|, 226.
+fatter pens, 297--298.
+*|fi|, +169--+170, 179.
+|!| |File| |ended...|, 287.
+file names, 36, 39, +180, 324, 329.
+\<filename>, 179--180.
+|fill|, @24--@27, 109--112, @116, 118--121, 145, 167, +271, 295.
+|filldraw|, @103--@105, 112--113, 118--119, 147, 148, @152, @164,
+ 230, +271, @306, 310.
+*|fillin|, +93--+94, 150, 212, 247, 268, 278--279.
+|fine|, 103--104, 306--307, 310--311.
+|fine.lft|, 311.
+|fix_units|, +267.
+flat spots, 196--197.
+|flex|, @124--@125, 127, @152, 173, 228--229, +267.
+*|floor|, @65, 66, 72, 83, 211, @253.
+flushing, 43--44, 219, 224--225.
+Font, Fray Pedro, 139, 231.
+\<font metric command>, +321.
+font metric information, 39, 220, 315--321.
+|font_coding_scheme|, +277, @303, 304, +320--+321.
+|font_extra_space|, +277, 319.
+|font_identifier|, +277, @303, 304, @305, 320, @332--@333.
+|font_normal_shrink|, @97, +276, @305, 319.
+|font_normal_space|, @97, +276, @305, 319, 332.
+|font_normal_stretch|, @97, +276, @305, 319.
+|font_quad|, @97, +277, 308, 319, 332.
+|font_setup|, 203, 305, 309--312.
+|font_size|, @95, 96, +276.
+|font_slant|, +276, @305, 319, 331, @335--@336.
+|font_x_height|, +277, 319, 332.
+*|fontdimen|, @276--@277, +318--+319, 331--332, @335.
+\<fontdimen command>, +318.
+*|fontmaking|, 54, @94, 211, @270, +315.
+|\fontname|, 342.
+*|for|, @18, @39, @113, +171--+173, 179, 228, @285--@291, @299.
+\<for list>, +171, 299.
+forbidden tokens, +173, 218--219, 286.
+*|forever|, @61, +171--+173, @176, 179.
+*|forsuffixes|, +171--+172.
+{\sevenrm FORTRAN} language, 237.
+\<four codes>, +318.
+four-point method for curves, 13--14, 133.
+Fournier, Simon Pierre, 321. % Harry Carter says S.P. is right, not P.S.!
+fractions, 61, @62--@63, +72, 73.
+*|from| 191, 220, @252, @277, @312.
+|fullcircle|, @114, 123--124, 126, @135--@137, +263, @266.
+Fulton, A\period\ G\period, 157.
+function values by interpolation, 294--295.
+\<future pen primary>, 148, +214.
+\<future pen secondary>, 148, +214.
+future pens, 148--149, 170, 249, 264, 298.
+\newletter
+Galsworthy, John, 215.
+Gardner, Martin, 126.
+|generate|, 305, 307, 311, 313.
+|gf|, 32, 241, 295, 323--325.
+|gfcorners|, 277, +278, 327.
+|GFtoDVI|, 32, 37, 187, 327--336.
+|gimme|, 61--62.
+Giotto de Bondone, 139.
+|gobble|, @167, +262, @289.
+|gobbled|, +262, @289--@290.
+golden ratio, 11.
+|good.bot|, 204, +273.
+|good.lft|, 204, +273.
+|good.rt|, 204, +273.
+|good.top|, 204, +273.
+|good.x|, @198, @268, +273.
+|good.y|, @198, 204, @268, +273.
+Goudy, Frederic William, 19.
+grammatical rules, 49--50.
+*|granularity|, +205, 212, 262, 310.
+graph paper, 5, 102, 109, 188.
+|gray|, 332.
+gray fonts, 327, 330--335.
+|grayf.mf|, 332--335.
+|grayfont|, 270, +275, 323, 329.
+|grayfontarea|, 329.
+|grayfontat|, 329.
+greater than or equal to, 65.
+greatest integer function, \see floor.
+grid, 5, 109, 275.
+Grimm, Jakob Ludwig Karl, 73.
+Grimm, Wilhelm Karl, 73.
+group delimiters, 289.
+group expressions, 157, 160.
+groups, 155--157, 167.
+Gu Guoan, 3.
+\newletter
+|h|, @22--@25, 35--36, @76--@78, 102, 204, +275.
+`H', 163, 165.
+Haggard, Sir Henry Rider, 107.
+hairlines, 104--105.
+|halfcircle|, 123, @136, +263.
+hamburgefonstiv, 341.
+hand tuning, 195.
+*|headerbyte|, 318, +320--+321.
+\<headerbyte command>, +318.
+hearts, 134.
+height, 101.
+Hein, Piet, 126, 231.
+help messages, 43--45, 189, 224--225.
+Herbin, Auguste, 3.
+Hersh, Reuben, 343.
+*|hex|, +188, 211, 281.
+hex symbol, 7--8, 28--29.
+hexadecimal notation, 188.
+|hide|, @116, @143, 167, @173, @227, +262.
+hierarchy of operators, 60--61, 71--73, 137, 209, 289.
+histogram, 251.
+Hobby, John Douglas, viii, 3, 130, 131, 149, 252, 285.
+holes, 110.
+Holland, Philemon, 51.
+Homerus, 51.
+homogeneous transforms, 247.
+*|hppp|, 92--93, 212, 267, 268, 324.
+|hround|, +264, @268.
+Hult\'en, Karl Gunnar Pontus, 3.
+\newletter
+`I', 28, 32, 39, 163, 164.
+|!| |I| |can't| |go| |on|, 226.
+IBM Corporation, ix.
+|identity|, @141--@145, 215, +263.
+*|if|, +169--+170, 179, 289.
+|iff|, @306, +307, 311.
+|imagerules|, 277, +278.
+imperative versus declarative, 87.
+impossible cube, 113.
+|in| (inch), 92, +267, 268.
+inaccessible token, 286.
+incomplete string, 50--51.
+inconsistent equations, 82, 313.
+|incr|, @39, 176--177, +266.
+independent variables, +81--+83, 88, 224, 226.
+infinite loops, 172, 226--227.
+|infinity|, @62--@69, +263, @266.
+inflection points, 18--19.
+|INIMF|, 221, 262, 279.
+|\init|, 342.
+\<initial value>, +171.
+*|inner|, 180, +218--+219, 286--287, @307, @321.
+|inorder|, 290.
+*|input|, +179, 180, @269, @287--@288, 324.
+input stack size, 226, 287.
+inserting text online, 42, 45, 61, 188, 223--225.
+integers, 65--66.
+|interact|, 230, +262.
+interacting with \MF, 42--45, 61, 188--189, 191--193, 219, 223--225.
+*|interim|, +155--+156, 230, @243, @244, @271, @272.
+\<interim command>, 155, +218.
+internal quantities, 54--55, 88, 218, 262.
+\sub table, 211--212.
+\<internal quantity>, 156, 218, 265.
+|interpath|, 134, +267.
+interpolation, 2, 134, 294--295.
+interrupting \MF, 219, 227--228, 313.
+intersection, of lines, 84.
+\sub of paths, 136--137.
+\sub of pictures, 120.
+|intersectionpoint|, @107, @137, @138, 178, +265.
+*|intersectiontimes|, +136, @178, 213, @265, @294, @298.
+|inverse|, @143, +264.
+inverse video, 115, 118.
+*|inwindow|, +191, @277.
+Io, 33, 40, 47.
+\<is>, 165, 171, +218.
+Isis, 40.
+|!| |Isolated expression|, 223.
+isolated math characters, 316, 319.
+|italcorr|, @103--@105, +275, @303, @306, @316.
+italic corrections, 102, 105, 275, 276, 304, 315--316, 319.
+italic type, 55, 206, 341.
+\newletter
+jaggies, 201.
+*|jobname|, +187, 214, 324.
+Johnson, Samuel, 167.
+Johnston, Edward, 29.
+|join_radius|, 266.
+jokes, viii, 231.
+Journal of Algorithms, 137--139.
+|jut|, 162, 308.
+\newletter
+Kafka, Franz, 340.
+Kandinski\u\i, Vasili\u\i\ Vasil'evich, 3.
+\<keep or drop>, +118, 120.
+*|keeping|, 118, +120, 220.
+|keepit|, 295.
+*|kern|, @97, @316, +317.
+kerning, 97, 316--317.
+|killtext|, +262, @272.
+knife, 24.
+*|known|, @65, 79--82, 143, +170, 210.
+Knuth, Donald Ervin, i, ii, ix, 3, 134, 192, 206, 255, 282, 291, 304, 308, 345,
+ 361.
+Knuth, Nancy Jill Carter, ix, 134, 137.
+\newletter
+|l|, 308--309.
+La Rochefoucauld, Fran\c cois VI, 313.
+\<label>, +317.
+\<labeled code>, +318.
+|labelfont|, +275, 329.
+|labelfontarea|, 329.
+|labelfontat|, 329.
+|labels|, @107, +274--+275, 327--328.
+labels in font metric information, 317--318.
+labels on "proofmode" output, 37, 187, 274--275.
+|labels.top|, 328.
+Lam\'e, Gabriel, 126.
+|large_pixels|, 332.
+|lcode_|, 274, 328.
+le B\'e, Pierre, 207.
+least integer function, \see ceiling.
+Leban, Bruce Philip, 242, 243, 270, 295.
+|left|, @16, +263.
+left-handed dangerous bend, 143.
+|leftstemloc|, 96, 199, 302.
+*|length|, @66, @69, 72, 211, 238.
+less than or equal to, 65.
+*|let|, 53, 180, +218, @287--@289, @299, @311.
+\<let command>, +218.
+|letter_fit|, 307--308.
+\<leveldef>, +178.
+\<leveldef heading>, 165, +178.
+|lft|, @23, @77, 80, 147, 151, +273.
+lies, viii, 231.
+Life, 121.
+\<ligature op>, +317.
+ligatures, 305--306, 315--317.
+|lightweight|, 332.
+*|ligtable|, @97, @305, +316--+317.
+\<ligtable command>, +317.
+\<ligtable program>, +317.
+\<ligtable step>, +317.
+\<limit value>, +171.
+line, point to be on, 83--84.
+linear dependencies, 82--83.
+linear forms, 64, 82.
+Linn\'e, Carl von (= Linn\ae us, Carolus), 325.
+|local.mf|, 278--279, 321.
+|localfont|, 39, 271, 278, @279.
+locations of characters within a font, 106--107, 281--283, 320.
+Lockyer, Sir Joseph Norman, 57.
+log file, 42, 46, 62, 230, 295--297.
+logarithm, \see |mlog|.
+|loggingall|, 230, +263.
+logo of \MF, ii, 22--23, 95--99, 160--161, 184--185, 199--200, 204, 301--304.
+|logo.mf|, 95--98, 199, 302--303.
+logos, {\it i}, 97, @114, @137--@139.
+|logo10.mf|, 95, 287, 301, 304.
+\<loop>, +171.
+\<loop header>, +171.
+loop text, 171--172, 219, 286.
+loops, 169, 179, 226--227, 259, 290--291, 299.
+low-resolution proofs, 99, 327.
+|\lowers|, 339.
+|lowres|, 196, 201, 230, +270.
+|lowres_fix|, 203, +268, 310.
+|luxo|, 91--94, 99, 195, 278--279.
+\newletter
+`M', 23, 97, 200, 302--303.
+macros, @36--@37, 53, 114, 159--167, 175--179, 285--299.
+|mag|, @39, +91--+93, 98, 169, 230, 269, 278, 333--334.
+magnets, 60--61.
+magnification, 38--40, 91--99.
+|magstep|, 98, +270.
+|makebox|, 270, +276, 309.
+|makegrid|, +275.
+|makelabel|, +274, 328.
+*|makepath|, +150, 213, 247, @298.
+*|makepen|, +147--+148, 214, @264.
+|maketicks|, 270, +276, 309.
+mastication, 169, 179, 285.
+|\math|, 341.
+Matthew, Saint, 173.
+|max|, @65, +266, 290--291.
+maximum, 65.
+mediation, 9--11, 14, @63, @68, 72, 80, 133, 298--299.
+memory usage, 226--227.
+*|message|, @61, +189, @262.
+\<message command>, 189, +219.
+\<message op>, 189, +219.
+meta-design, 1--3, 103--105, 294.
+meta-font, 1--3, 98, 192, 301--304.
+meta-ness, 3, 301.
+\MF, the logo, ii, 22--23, 95--99, 160--161, 184--185, 199--200,
+ 204, 301--304.
+\sub the name, 1--3.
+|METAFONT| |capacity| |exceeded|, 226--227.
+\MF\kern1pt79, viii.
+*|mexp|, @+67, 72, 211, @265, @270.
+|mf|, 31, 35.
+|.mf|, 36.
+|mfput|, 31--32, 187, 324.
+|MFT|, 262.
+midpoints, 9, 13.
+Mies van der Rohe, Ludwig, 185.
+|min|, @65, +266, 290--291.
+minimum, 65.
+Mirk, John, 313.
+|!| |Missing| |`)'| |has| |been| |inserted|, 254.
+misspelling, 45, 224.
+|\mixture|, @40, +338.
+*|mlog|, @+67, 72, 211, @265.
+|mm| (millimeter), @76, 91--92, +267, 268.
+M\"obius, August Ferdinand, 114.
+mock curvature, 131.
+|mod|, @66, +265.
+|mode|, @38--@39, @75, 91--94, 269, 278.
+\<mode command>, +219.
+|mode_def|, 94, 189, @+270, @278--@279.
+|mode_name|, 269.
+|mode_setup|, @32--@34, 75, 76, 91--94, @96, 115, 169, +269, 278, @304, @305,
+ 329.
+|mono_charwd|, 308.
+|monospace|, 305--308.
+*|month|, +212, 323.
+More, Sir Thomas, 215.
+Morison, Stanley, ix, 283.
+mouth, 169, 179, 285.
+Moxon, Joseph, 325.
+Mulford, Clarence Edward, 89.
+multiplication, @59, @62--@64, 69, 79--80, 82.
+\sub of vector by scalar, 9.
+music, 183, 185.
+\newletter
+`n', 201--203.
+`N', 184--185, 303.
+|\names|, 339.
+National Science Foundation, ix.
+Naur, Peter, 49, 89.
+negation, of pictures, 115.
+\sub of vectors, 9.
+|new_window|, 193.
+*|newinternal|, 180, +218.
+\<newinternal command>, +218.
+nice tangent points, 177.
+|NNE|, 119, 228.
+|NNW|, 26, 119, 228--229.
+|nodisplays|, 277, +278.
+|nodot|, 274, 328.
+nonlinear equations, 84--85, 176--177, @292--@294.
+nonsquare pixels, 94, 145, 204.
+*|nonstopmode|, +219, 226.
+*|normaldeviate|, @68, 72, @183--@185, 210.
+*|not|, @65, +170, 210.
+|notransforms|, 277, +278.
+*|nullpen|, +148, 214, @272.
+*|nullpicture|, +115, 192, 214, @272, @277.
+*|numeric|, 55, +56, @65, 88.
+\<numeric atom>, 72, +210.
+\<numeric expression>, 72, +211.
+numeric expressions, 72--73, 257.
+\<numeric list>, +318.
+\<numeric operator>, 72, +211.
+\<numeric primary>, 72, +211.
+\<numeric secondary>, 72, 178, +211.
+\<numeric tertiary>, 72, +211.
+\<numeric token>, +50, 236.
+\<numeric token primary>, 72, +210.
+numeric tokens, 49--50, 166.
+\sub maximum value, 50.
+\sub rounded fractional values, 50.
+|numeric_pickup_|, +272, 310.
+*|numspecial|, 220, @274, +323--+324, @327--@329.
+|numtok|, @+274.
+\newletter
+|o|, @23, @34, +93, 197, 200, 204, 302.
+`o', 203.
+`O', 32--37, 199, 303.
+|o_correction|, 93--94, 268.
+*|oct|, +188, 211, 281.
+octal notation, 188.
+octants, 119, 206--207, 228--230.
+*|odd|, +170, 210, 250.
+*|of|, 73, 129, 165--167, 187, 211--214.
+of-the-way function, \see mediation.
+off by $x$, 82.
+Office of Naval Research, ix.
+|offset|, 275, 379.
+|!| |OK|, 219, 224.
+|\omitaccents|, 340.
+one-point {\bf draw}, 22, 150, 200, 253.
+online interaction, 42--45, 61, 188--189, 191--193, 219, 223--225.
+|openit|, +277, 312.
+*|openwindow|, +191--+193, 220, @277, @312--@313.
+\<openwindow command>, 191, +220.
+operands, 59.
+operators, 59, 230.
+\<optional skip>, +317.
+*|or|, @65, +170, 210, 237, 288--289.
+order of operations, 60--61, 137, 247, 289.
+oriental characters, 3, 106, 324.
+|origin|, @77--@78, @243, @251, +263.
+ornament, 144--145.
+Orwell, George (= Blair, Eric Arthur), 85.
+*|outer|, 180, +218--+219, 221, 286--287, @307, @321.
+outlines, 121.
+output of \MF, 39, 42, 315--325.
+|overdraw|, 114, 243.
+overflow labels, 37, 328.
+overlays, 295.
+overshoot, 23, 34, 93, 197, 200, 204.
+\newletter
+`P', 207.
+Paget, Francis Edward, 279.
+*|pair|, 55, +56, 65.
+\<pair expression>, 73, +213.
+pair expressions, 73, 171, 258.
+\<pair part>, +211.
+\<pair primary>, 73, +212.
+\<pair secondary>, 73, +212.
+\<pair tertiary>, 73, +213.
+Palais, Richard Sheldon, ii.
+parallel lines, 84.
+parallelogram, 293--294.
+\<parameter>, +178.
+parameter files, 301, 304.
+\<parameter heading>, +165.
+\<parameter tokens>, +165.
+\<parameter type>, +165.
+parameters, v, 1--3.
+\sub to fonts, 95, 103--104, 305.
+\sub to macros, 159--167, 175--178.
+parentheses, 51, 59, +60, 61, 71, 128, 210--215, 247.
+Pascal language, 54.
+*|path|, 55, +56, 171.
+\<path expression>, 129, +213.
+path expressions, 129--134, 258.
+\<path join>, 129--130, 171, +213.
+\<path primary>, 129, +213.
+\<path secondary>, 129, +213.
+\<path tertiary>, 129, +213.
+paths, 13--19, 123--139.
+*|pausing|, 211, +231.
+|pc| (pica), 92, +267, 268.
+pels, \see pixels.
+*|pen|, 55, +56, @65, 170.
+\<pen expression>, 147, 148, +214.
+pen expressions, 147--148, 258, 298.
+\<pen primary>, 148, +214.
+\<pen secondary>, 148, +214.
+\<pen tertiary>, 148, +214.
+|pen_bot|, 151, +272.
+|pen_lft|, 151, +272.
+|pen_rt|, 151, +272.
+|pen_top|, 151, +272.
+*|pencircle|, @21--@23, @28, @29, +147--+149, @150--@152, 198, 200, 214.
+|penlabels|, 36, +274.
+*|penoffset|, +150, 212, 230, @298.
+|penpos|, @26--@29, 37, @103, @162, +273, 310.
+|penrazor|, @107, @112, 147, 150, +264, 297.
+pens, 21--39, 147--152, 297--298.
+|penspeck|, +264, @271.
+|pensquare|, 147, 152, +264, 275.
+|penstroke|, 27--29, @138, +273.
+perpendicular, 29, 69, 84, 235.
+|pickup|, @21--@23, 145, 147, +272.
+*|picture|, 55, +56, @114.
+\<picture command>, 118, +220.
+\<picture expression>, 115, +214.
+picture expressions, 115, 258.
+\sub transformation of, 144, 297.
+\<picture primary>, 115, +214.
+\<picture secondary>, 115, +214.
+\<picture tertiary>, 115, +214.
+pictures, 109--121.
+pimples, 196--197, 204.
+|pix_ht|, 332, @333.
+|pix_picture|, 332, @333.
+|pix_wd|, 332, @333.
+pixels, 5, 109, 259, 324.
+|pixels_per_inch|, 267, 268.
+plain \MF\ base, 34, +257--279.
+|plain.mf|, 261--278.
+\<plus or minus>, 72, +211.
+*|point|, @69--@70, 73, @114, +133, 212, @267.
+polygonal path, 24, 297.
+pool size, 226, 286.
+|pos|, 310.
+*|postcontrol|, +134, 212, @267.
+|posttension|, 136.
+precedence, 60--61, 71--73, 137, 289.
+*|precontrol|, +134, 212, @267.
+|pretension|, 136.
+pretty-printed \MF\ programs, 262.
+*|primary|, 165, 167.
+\<primary>, 71, 170, +209.
+*|primarydef|, 166, @+178.
+prime numbers, 173.
+primitives, 53, 209, 345.
+private tokens, 173, 265, 270.
+product, @59, @62--@64, 69, 79--80, 82.
+\sub of vector by scalar, 9.
+\<program>, 155, +217.
+program files, 304, 306.
+\<progression>, +171.
+|proof| mode, 92, 93, 104, +270, 327.
+*|proofing|, @94, 187, 211, 220, @270, 274, +323--+324, 327.
+|proofoffset|, +275, 329.
+|proofrule|, +274, 323, 328--329.
+|proofrulethickness|, +275, 329.
+proofsheets, 37, 261, 327--343.
+\<protection command>, +218.
+pseudo-driver files, 311--313.
+|pt| (printer's point), @21--@23, @33, 91--92, +267, 268.
+|\punct|, 339.
+punctuation marks, 306.
+Pythagorean addition, @+66, @67, 72, 211.
+\<Pythagorean plus or minus>, 72, +211.
+Pythagorean subtraction, @+66, 72, 211, @238.
+\newletter
+`Q', 207.
+|quartercircle|, 123, +263.
+Quick, Jonathan Horatio, 54, 137.
+*|quote|, +166, 172, @270, @286, @312.
+\newletter
+|r|, 308--309.
+`R', 207.
+|\raggedright|, 338.
+Ramshaw, Lyle Harold, 320.
+random numbers, 183--185.
+*|randomseed|, 185, 218.
+\<randomseed command>, +218.
+|range|, @107, @138, @200, +274.
+raster, 5, 91, 109, 195.
+*|readstring|, @61, +187--+188, 214.
+recipes, 2.
+recursion, 227.
+redundant equations, 82.
+reference point, 77, +101.
+|reflectedabout|, @138, 141, @142, 160, +266.
+reinitializing a variable, 88, 157.
+\<relation>, 170, +210.
+relations, @64--@65, 170--171.
+|relax|, @31, +262, @307.
+remainder, 66.
+|rep|, 332, 335.
+replacement text, 159, +166, 219.
+resolution, 6, 38--39, 91--99, 116.
+\<return> key, 31.
+*|reverse|, 129, +132, 213.
+reverse video, 115, 118.
+Reynolds, Lloyd Jay, 153.
+|right|, @26, @68, +263.
+\<right-hand side>, +88, 171.
+*|rotated|, @21--@22, @25, 27, 44, @68, 73, @107, @114, @117, +141, 212, @238.
+|rotatedabout|, +266.
+|rotatedaround|, @138, 141, @142, @144, 159--160, +266.
+|round|, @66, 196, 202, +264, @273.
+rounding, 34--35, 50, 195--207, 308.
+|rt|, @23, @77, 80, @103, 147, 151, +273.
+|rtest.mf|, 311.
+|rule|, 234, 328.
+|rulepen|, +274, 275.
+rules on proofsheets, 328--329.
+|rulethickness|, 275, 329.
+runaway, 287.
+Running, Theodore Rudolph, 47.
+Ruskin, John, 139.
+\newletter
+`S', 40, 114.
+|safefill|, 121.
+|\sample|, 341.
+sans-serif, 105, 305, 308.
+*|save|, +155--+156, @160, 173, @178, 180, 218, @236, @244, @296, 299.
+\<save command>, 155, +218.
+|savepen|, @96, 147, +272, @310.
+\<scalar multiplication operator>, 72, +211.
+*|scaled|, @21--@23, @68, 73, +141, 212, 244, 291.
+*|scantokens|, @61, +179, @180, 189, 251, @269, @270, @286--@288, @313.
+scatter plots, 183.
+\<screen coordinates>, 191, +220.
+\<screen place>, 191, +220.
+|screen_cols|, 193, 277, @278.
+|screen_rows|, 277, @278.
+|screenchars|, 191, +277.
+|screenrule|, 274, 278.
+|screenstrokes|, 191, +277.
+*|scrollmode|, @61, +219, @313.
+*|secondary|, 165, 167.
+\<secondary>, 71, +209.
+*|secondarydef|, 166, @178.
+selective complement, 120.
+semantics, 50.
+semicolons, 155, 169, 171, 172, 187, 217, 223--224, 263, 312.
+|serif_fit|, 308.
+serifs, 152, 162--165, 308.
+Serlio, Sebastiano, 19.
+|setu_|, 266, 291.
+Shakespeare, William, 173, 255, 343.
+sharped dimensions, @32--@35, 91--99, 102--103, 268, 315.
+|shiftdef|, 311.
+*|shifted|, @68, 73, @117, +141, 213.
+|shipit|, @31, @276, +277, 295.
+*|shipout|, 106, 210, +220, @277, @295, 316, 324, 329.
+\<shipout command>, +220.
+*|show|, 142, +219, @227, 230, @250, 296.
+\<show command>, +219.
+*|showdependencies|, 81, 83, +219, @262.
+|showit|, @31, 191, @276, +277, 295.
+*|showstats|, +219.
+*|showstopping|, 211, @227, 230, @262.
+*|showtoken|, 180, +219, @221.
+*|showvariable|, 175, 177, 180, +219.
+|shrink_fit|, 308--310.
+shrinkability, 319.
+shuffled binary numbers, 137.
+sidebearings, 10, 34--35, 307--308.
+{\sevenrm SIMULA67} language, 175.
+*|sind|, @67, 72, 211.
+*|skipto|, @316, +317.
+skyline, 251.
+|slant|, 105, 206, 301--303, 310, 319.
+slant fonts, 329, 335--336.
+*|slanted|, @68, 73, 105, +141, 213.
+|slantfont|, +275, 329.
+|slantfontarea|, 329.
+|slantfontat|, 329.
+|smode|, 269.
+|smoke| mode, 38, 75, 93, +270, 327.
+*|smoothing|, 55, 195, 205--206, 212, @262.
+|softjoin|, 262, +266.
+|solve|, 176--177, +267, @292--@294.
+|(some| |charht| |values...)|, 316.
+Southall, Richard Francis, 176.
+spaces, 43, 50, 236.
+sparks, +53--+55, 156, 175, 215, 219, 289.
+*|special|, 220, @240--@241, @274, +323--+324, @327--@329.
+\<special command>, +220.
+special-purpose macros, 160, 248.
+*|sqrt|, @59, @64, 72, 211.
+square roots, 66, \also |sqrt|.
+|SSE|, 206--207, 228--229.
+|SSW|, 119, 228--229.
+stack positions, 227.
+Stanford, Amasa Leland, 340.
+Stanford, Jane Elizabeth Lathrop, 340.
+Stanford University, 125, 340.
+star, 114.
+|\startfont|, +337, 338, @342.
+starting a job, 39, 95, 259, 277.
+starting character, @40, 338--339.
+\<statement>, 155, 171, +217.
+\<statement list>, 155, +217.
+statements, 155, 217--221.
+\sub summary, 260--261.
+stems, 201--203.
+*|step|, @18, 171.
+\<step size>, +171.
+stomach, 169, 217, 285.
+|stop|, +262, @311--@312.
+stopping \MF, \see |end|.
+*|str|, +187--+188, 214, @250, @251.
+strange paths, 110--111, 119, 121, 136, 152, 228--229.
+Stravinski{\u\i}, Igor' F\"edorovich, 193.
+stretchability, 319.
+Strindberg, Johan August, 185.
+*|string|, 55, +56, 69.
+\<string expression>, 73, 187, +214.
+string expressions, 187--189, 258, 286.
+\<string primary>, 187, +214.
+\<string secondary>, 187, +214.
+\<string tertiary>, 187, +214.
+string tokens, 49--51.
+|stroke|, @306, 310.
+*|subpath|, @70, @71, 114, 129, +133, 134, 188, 213, @298.
+subroutines, \see macros.
+\<subscript>, +54.
+subscripts, 54--57.
+*|substring|, @69, 187, +188, 214, @320.
+subtraction, of pictures, 115, @244.
+\sub of vectors, 9.
+Suetonius Tranquillus, Gaius, 181.
+*|suffix|, @161, 165, @176.
+\<suffix>, +54, 161, 176, 188.
+\<suffix list>, +171, 236.
+|(SUFFIX|$_n$|)|, 44, 251.
+sum, of pictures, 115, @117, @245.
+\sub of transforms, 178.
+\sub of vectors, 9.
+|superellipse|, @126, @138, +267.
+superellipses, 126, 161.
+|superness|, 126.
+Sutherland, Ivan Edward, 121.
+Swift, Jonathan, 99, 121.
+\<symbolic token list>, 155, +218.
+symbolic tokens, 49--51.
+symmetric difference, 120.
+syntax rules, 49--50.
+System Development Foundation, ix.
+\newletter
+`T', 22--23, 97, 151, 199--200, 302--303.
+tables of \MF\ trivia:
+\sub character classes, 51.
+\sub character codes, 281--282.
+\sub expandable tokens, 179--180.
+\sub |fontdimen| parameters, 319.
+\sub internal quantities, 211--212.
+\sub language features, 257--261.
+\sub proof\/ label options, 328.
+\sub types, 55.
+\sub units of measure, 92.
+tags, +53--+55, 156, 175, 218--219.
+|takepower|, +265.
+taller pens, 297--298.
+tapered stroke, 28.
+|tensepath|, 128, +264, @298.
+*|tension|, @15--@16, @114, +129--+132, 136, @296.
+\<tension>, 129, +213.
+\<tension amount>, 129, +213.
+*|tertiary|, 165, 167.
+\<tertiary>, 71, @137, +209.
+*|tertiarydef|, 166, +178, @266.
+|test.mf|, 311--312.
+|testfont.tex|, 40, 336--342.
+\TeX, 1, 34, 40, 91, 96, 98, 101--102, 315, 336--343, 361.
+*|text|, @161, +165--+167.
+|\text|, 340.
+|(TEXT|$_n$|)|, 45, 249, 251.
+text arguments, 219, 288--290, 299.
+|.tfm|, 39, 315--317, 333, 335.
+|!| |This| |can't| |happen|, 226.
+Thomson, James, 189.
+Thoreau, Henry David, 221.
+|thru|, @107, @138, @200, +274.
+tilde, 152.
+*|time|, +212, 218, 323.
+time in paths, 119, 133--137.
+\<times or over>, 72, +211.
+Tinguely, Jean, 3.
+\<title>, +187, 217--218, 323.
+|title|, 323, 327.
+|titlefont|, +275, 329.
+|titlefontarea|, 329.
+|titlefontat|, 329.
+*|to|, 191, 220, @252, @277, @312.
+|<to| |be| |read| |again>|, 223.
+Tobin, Georgia Kay Mase, ii, 240.
+tokens, 42--43, +49--+51, 210.
+|tolerance|, 176, 251, 267, 293.
+|top|, @23, @77, 80, @103, 147, 151, 204, +273.
+Tory, Geoffroy, 19.
+|totalnull|, 295.
+*|totalweight|, +115, 211, @292.
+|tracingall|, 230, +263, 288.
+*|tracingcapsules|, 211, 219, 239.
+*|tracingchoices|, 211, +229.
+*|tracingcommands|, 211, +230.
+*|tracingedges|, 211, +230, @295--@296.
+*|tracingequations|, 80--83, 211, 229.
+*|tracingmacros|, +160, 211, 229.
+|tracingnone|, 230, +263.
+*|tracingonline|, @61, 80, 211, 219, +230.
+*|tracingoutput|, 211, +229--+230, 296.
+*|tracingpens|, 211, +229, 230.
+*|tracingrestores|, +156, 211, 229.
+*|tracingspecs|, 206--207, 211, +229.
+*|tracingstats|, 211, +227, 230.
+*|tracingtitles|, 55, @94, +187, 211, 229.
+Trajanus, 153.
+trajectories, \see paths.
+transcript file, 42, 46, 62, 230, 295--297.
+*|transform|, 55, +56, 57, 141--143, @160, 266.
+\<transform expression>, +215.
+transform expressions, 141--143, 170, 178, 258.
+\<transform part>, +211.
+\<transform primary>, +215.
+\<transform secondary>, +215.
+\<transform tertiary>, +215.
+transformations, 44, 141--145.
+*|transformed|, 73, 141--145, 213.
+\<transformer>, 73, +213.
+transition lines, 230.
+|transum|, 178.
+trial path, 235.
+triangle, 24--25, 203.
+trigonometric functions, 67, 69, 131, 177.
+*|true|, 55, @64--@65, 170, 210.
+truth, viii, 217, 221.
+{\sl TUGboat}, ix, 361.
+turning numbers, 110, +111, 112, 119, 136, 147.
+*|turningcheck|, 112, +119, 212, 229, @244, 262, 296.
+*|turningnumber|, 111, 211, 257, @264.
+Twain, Mark (= Clemens, Samuel Langhorne), 145.
+\<type>, +56, 171.
+type declarations, 56.
+types, 55.
+typewriter type, 55, 105.
+typographic errors, 45, 224.
+\newletter
+|u|, 103--104, 305--308.
+|!| |Undefined| |coordinate|, 224.
+undelimited arguments, +167.
+\<undelimited parameters>, +165.
+undelimited suffix parameters, +167, 176, 265, 270.
+underline characters, 49, +51, 173, 265, 270.
+|undraw|, 113, 118, 120, 242, +271.
+|undrawdot|, 113, +271.
+unequal to, 65.
+|unfill|, @25, 27, 109--110, 118, @126, +271.
+|unfilldraw|, 113, 118, +271.
+*|uniformdeviate|, @68, 72, +183, 184, 211.
+union, 120.
+Union Jack, 7.
+|unitpixel|, +263, @333.
+units of measure, 33, 91--99, 267--268.
+\sub table, 92.
+|unitsquare|, @116, 128, 132, 136, +263.
+|unitvector|, @238, +264.
+*|unknown|, 79--82, 143, +170, 210.
+unknown quantities, nonnumeric, 84--85.
+\sub numeric, 79--83.
+*|until|, @18, 171.
+|up|, @32, @129, +263.
+|\uppers|, 339.
+|upto|, @39, 172, +262.
+utility files, 311--313.
+\newletter
+\<vacuous expression>, +215.
+vacuous expressions, 209, +215, 250, 262, 289, 292.
+\<vacuous primary>, +215.
+\<vacuous secondary>, +215.
+\<vacuous tertiary>, +215.
+valentine, 134.
+values, disappearance of, 56, 88, 156--157.
+*|vardef|, 166, @175--@178, 289.
+\<vardef heading>, +178.
+\<variable>, 54, +55, 210.
+variables, 53--57, 59.
+\sub reinitializing, 88, 157.
+vector subtraction principle, 9.
+vectors, 9--10, 77.
+velocity zero, 136, 298.
+Venezky, Richard Lawrence, 193.
+*|vppp|, 212, 267, 324.
+|vround|, @204, @+264, @268.
+\newletter
+|w|, @22--@25, 35--36, @76--@78, 102--103, 106, +275--+276, 308--310.
+`w', 202.
+*|warningcheck|, 212, @269, 270.
+Warren, Mercy Otis, 359.
+Webster, Noah, 167.
+|whatever|, @83--@84, @138, 157, @233, 239, +264, @290.
+width, 101.
+Wilde, Oscar Fingal O'Flahertie Wills, 321.
+Wilkins, John, ii, 283.
+Willis, Ellen Jane, 157.
+\<window>, 191, +220.
+\<window spec>, 191, +220.
+\<with clause>, +118, 120.
+*|withpen|, 118, 220, @242.
+*|withweight|, 118, 220, @242, @297.
+|WNW|, 119, 228--229.
+|WSW|, 119, 228--229.
+\newletter
+$x$ coordinates, @5--@7.
+x-height, 319.
+Xerox Corporation, 320.
+|xgap|, 95--96, 199.
+*|xoffset|, 212, +220, @309, 315, 324.
+xor, 120.
+*|xpart|, @68, 72, @138, 142, 211.
+*|xscaled|, @21--@23, @68, 73, +141, 213, 244, 291.
+*|xxpart|, 72, 142, @160, 211.
+|xy_swap|, 297.
+*|xypart|, 142, @160, 211.
+\newletter
+$y$ coordinates, @5--@7.
+*|year|, +212, 323.
+|ygap|, 96, 199.
+*|yoffset|, 212, +220, 315, 324.
+*|ypart|, @68, 72, 142, 211, 238.
+*|yscaled|, @21--@23, @68, 73, +141, 213, 244, 291.
+*|yxpart|, 142, @160, 211.
+*|yypart|, 142, @160, 211.
+\newletter
+|z| convention, 7, @68, 69, 251, +277.
+Zapf, Hermann, iii, 221.
+zero, 236.
+*|zscaled|, @68--@69, 73, +141, 213.
+|ztest.mf|, 312.
+\enddoublecolumns
+\endchapter
+
+The more we search,
+the More are we Deceived.
+\author MERCY OTIS ^{WARREN}, ^^{Adams} {\sl To Mr.\thinspace Adams\/} (1773)
+ % in Mass. Historical Soc. Collections, vol73 (1917), p402; line 32
+
+\bigskip
+
+A heavy weight is now to be removed from my conscience.
+So essential did I consider an Index to be to every book,
+that I proposed to bring a Bill into Parliament
+to deprive an author who publishes a book without an Index
+of the privilege of copyright; and, moreover,
+to subject him, for his offence, to a pecuniary penalty.
+Yet, from difficulties started by my printers,
+my own books have hitherto been without an Index.
+\author LORD ^{CAMPBELL}, {\sl Lives of the Chief Justices %
+ of England}, vol.\thinspace 3 (1857) % end of the preface
+
+\eject
+ \beginchapter Appendix J. Joining the\\\TeX\ Community
+
+This appendix is about grouping of another kind: \TeX\ and \MF\ users from
+around the world have banded together to form the \TeX\ Users Group (TUG),
+in order to exchange information about common problems and solutions.
+
+A newsletter/journal called {\sl TUGboat\/} has been published
+since 1980, featuring articles about all aspects of \TeX\ and \MF\!\null.
+^^{TeX} TUG has a network of ``site coordinators'' who serve as focal points of
+communication for people with the same computer configurations.
+Occasional short courses are given,
+to provide concentrated training in special topics; videotapes of
+these courses are available for rental.
+Meetings of the entire TUG membership are held at least once a year.
+You can buy \MF\ T-shirts at these meetings.
+
+Information about membership in TUG and subscription to {\sl TUGboat\/}
+is available from
+
+\smallskip
+{\obeylines
+\TeX\ Users Group
+|email: TUG@tug.org|
+|internet: http://www.tug.org|
+}
+
+\endchapter
+
+TUG is established to serve members having a common interest
+in \TeX, a system for typesetting technical text,
+and in {\manual \char`\\]\char`\^\char`\_efg\char`\^}\!, %
+ a system for font design.
+\author T\kern-.15em\lower.5ex\hbox{E}\kern-.005em X %
+ USERS GROUP, {\sl Bylaws, Article II\/} (1983) % TUGboat 4 (1983) p60
+
+\bigskip
+
+Don't delay, subscribe today! That address again is
+\TeX\ Users Group
+email: {\eighttt TUG\char`\@ tug.org}
+internet: {\eighttt http://www.tug.org/}
+\author DONALD E. ^{KNUTH}, {\sl The \TeX book\/} (1996) % Appendix J
+
+\eject
+ \end
diff --git a/systems/knuth/dist/mf/trap.fot b/systems/knuth/dist/mf/trap.fot
new file mode 100644
index 0000000000..6bceb9f67e
--- /dev/null
+++ b/systems/knuth/dist/mf/trap.fot
@@ -0,0 +1,59 @@
+This is METAFONT, Version 2.7182818 (INIMF)
+** &trap trap
+(trap.mf
+\*//*\trap\pass2!
+! Missing symbolic token inserted.
+<inserted text>
+ INACCESSIBLE
+l.21 ...t next=\; delimiters ^~7
+ ! fi
+! Extra tokens will be flushed.
+<to be read again>
+ !
+l.21 ... next=\; delimiters ^~7!
+ fi
+! Forbidden token found while scanning to the end of the statement.
+<inserted text>
+ ;
+<to be read again>
+ \
+l.22 next\
+ ; % the second pass will now compute silently; the ...
+
+hello again^^_
+>> pen (see the transcript file)
+>> path (see the transcript file)
+>> path (see the transcript file)
+>> path (see the transcript file)
+f<expr>->let)=];let[=(;show._
+(EXPR0)<-xx
+{let}
+{let}
+{show}
+>> _1
+{showdependencies}
+xpart '=-ypart '-0.66667
+{qq:=pen}
+{showstats}
+Memory usage 1462&588 (104 still untouched)
+String usage 41&161 (815&7634 still untouched)
+{[repeat the loop]}
+! A group begun on line 163 never ended.
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+{endgroup}
+ )
+(end occurred when else on line 93 was incomplete)
+(end occurred when if on line 36 was incomplete)
+(end occurred when elseif on line 21 was incomplete)
+(see the transcript file for additional information)
+(illegal design size has been changed to 128pt)
+(some chardp values had to be adjusted by as much as 0.5pt)
+(local label 0:: was missing)
+(local label 5:: was missing)
+(4 font metric dimensions had to be decreased)
+Font metrics written on trap.tfm.
+Output written on trap.72270gf (5 characters, 1028 bytes).
+Transcript written on trap.log.
diff --git a/systems/knuth/dist/mf/trap.log b/systems/knuth/dist/mf/trap.log
new file mode 100644
index 0000000000..611ce589e2
--- /dev/null
+++ b/systems/knuth/dist/mf/trap.log
@@ -0,0 +1,4266 @@
+This is METAFONT, Version 2.7182818 (preloaded base=trap 2014.1.7) 7 JAN 2014 18:11
+** &trap trap
+(trap.mf
+{if}
+{known(0)}
+{not(true)}
+{false}
+{known("")}
+{true}
+
+/*\`'@#->begingroup.message(SUFFIX1)&str(SUFFIX0)&jobname&char.ASCII'`&s
+tr(SUFFIX2)!endgroup
+(SUFFIX0)<-/*\
+(SUFFIX1)<-`'
+(SUFFIX2)<-pass2
+{begingroup}
+{message}
+
+`'->begingroup'`endgroup
+(SUFFIX0)<-
+(SUFFIX1)<-`'
+{begingroup}
+
+'`->"\*/"
+{endgroup}
+{("\*/")&("/*\")}
+{jobname}
+{("\*//*\")&("trap")}
+
+'`->"\*/"
+{ASCII("\*/")}
+{char(92)}
+{("\*//*\trap")&("\")}
+{("\*//*\trap\")&("pass2!")}
+
+\*//*\trap\pass2!
+{endgroup}
+{outer}
+{let}
+{delimiters}
+
+! Missing symbolic token inserted.
+<inserted text>
+ INACCESSIBLE
+l.21 ...t next=\; delimiters ^~7
+ ! fi
+Sorry: You can't redefine a number, string, or expr.
+I've inserted an inaccessible symbol so that your
+definition will be completed without mixing me up too badly.
+
+! Extra tokens will be flushed.
+<to be read again>
+ !
+l.21 ... next=\; delimiters ^~7!
+ fi
+I've just read as much of that statement as I could fathom,
+so a semicolon should have been next. It's very puzzling...
+but I'll try to get myself back together, by ignoring
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+! Forbidden token found while scanning to the end of the statement.
+<inserted text>
+ ;
+<to be read again>
+ \
+l.22 next\
+ ; % the second pass will now compute silently; the ...
+A previous error seems to have propagated,
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{\}
+{batchmode}
+
+! An expression can't begin with `endgroup'.
+<inserted text>
+ 0
+<to be read again>
+ endgroup
+l.23 batchmode; ^~7,endgroup
+ pausing:=1; exitif p exitif bool...
+I'm afraid I need some sort of value in order to continue,
+so I've tentatively inserted `0'. You may want to
+delete this zero and insert something else;
+see Chapter 27 of The METAFONTbook for an example.
+
+! Missing ` INACCESSIBLE' has been inserted.
+<to be read again>
+ endgroup
+l.23 batchmode; ^~7,endgroup
+ pausing:=1; exitif p exitif bool...
+I found no right delimiter to match a left one. So I've
+put one in, behind the scenes; this may fix the problem.
+
+! Extra `endgroup'.
+<recently read> endgroup
+
+l.23 batchmode; ^~7,endgroup
+ pausing:=1; exitif p exitif bool...
+I'm not currently working on a `begingroup',
+so I had better not try to end anything.
+
+{pausing:=1}
+{exitif}
+{exitif}
+{pencircle}
+{endfor}
+! Extra `endfor'.
+l.23 ...ean pen pencircle endfor
+
+I'm not currently working on a for loop,
+so I had better not try to end anything.
+
+{scantokens}
+{begingroup}
+{message}
+{char(0)}
+{("^^@")&("watch this")}
+^^@watch this
+{-(1)}
+{char(-1)}
+{("pair p[],';")&("^^ff")}
+{endgroup}
+{pen(future pen)}
+{boolean(true)}
+{true}
+! No loop is in progress.
+<to be read again>
+ pair
+<scantokens> pair
+ p[],';^^ff
+<to be read again>
+ path
+l.25 path
+ p[][]p,w,qw; qw=(1,-2)..(2,-1)..(2.5,0.5)..(1,2)..(...
+Why say `exitif' when there's nothing to exit from?
+
+>> p
+! Undefined condition will be treated as `false'.
+<to be read again>
+ pair
+<scantokens> pair
+ p[],';^^ff
+<to be read again>
+ path
+l.25 path
+ p[][]p,w,qw; qw=(1,-2)..(2,-1)..(2.5,0.5)..(1,2)..(...
+The expression shown above should have had a definite
+true-or-false value. I'm changing it to `false'.
+
+{false}
+! Missing `;' has been inserted.
+<to be read again>
+ pair
+<scantokens> pair
+ p[],';^^ff
+<to be read again>
+ path
+l.25 path
+ p[][]p,w,qw; qw=(1,-2)..(2,-1)..(2.5,0.5)..(1,2)..(...
+After `exitif <boolean exp>' I expect to see a semicolon.
+I shall pretend that one was there.
+
+{pair}
+! Text line contains an invalid character.
+<scantokens> pair p[],';^^ff
+
+<to be read again>
+ path
+l.25 path
+ p[][]p,w,qw; qw=(1,-2)..(2,-1)..(2.5,0.5)..(1,2)..(...
+A funny symbol that I can't read has just been input.
+Continue, and I'll forget that it ever happened.
+
+{path}
+{-(2)}
+{-(1)}
+{turningnumber((xpart ',ypart '))}
+Path at line 25, before choices:
+(1,-2){curl 1}
+ ..(2,-1)
+ ..(2.5,0.5)
+ ..(1,2)
+ ..{curl 1}(0,2.5)
+
+Path at line 25, after choices:
+(1,-2)..controls (1.37755,-1.71404) and (1.71404,-1.37755)
+ ..(2,-1)..controls (2.33353,-0.55965) and (2.59729,-0.04124)
+ ..(2.5,0.5)..controls (2.36812,1.23369) and (1.6712,1.65662)
+ ..(1,2)..controls (0.66821,2.16974) and (0.33485,2.33641)
+ ..(0,2.5)
+
+{(unknown path qw)=(path)}
+{numeric}
+! Enormous number has been reduced.
+l.26 ...[$] ]]=10000000000000000
+ ; "this string constant is in...
+I can't handle numbers bigger than about 4095.99998;
+so I've changed your constant to that maximum amount.
+
+{(p[[ [-1] ]])=(4095.99998)}
+## p[[ [-1] ]]=4095.99998
+! Incomplete string token has been flushed.
+l.26 ...g constant is incomplete
+
+Strings should finish on the same line as they began.
+I've deleted the partial string; you might want to
+insert another by typing, e.g., `I"new string"'.
+
+{string}
+! Declared variable conflicts with previous vardef.
+<to be read again>
+ ,
+l.27 string foo[]p,
+ p~if true:[]; p~000=char34&char200&char34;
+You can't use, e.g., `numeric foo[]' after `vardef foo'.
+Proceed, and I'll ignore the illegal redeclaration.
+
+{if}
+{true}
+{true}
+{char(34)}
+{char(200)}
+{(""")&("^^c8")}
+{char(34)}
+{(""^^c8")&(""")}
+{(unknown string p~0)=(""^^c8"")}
+{boolean}
+{fi}
+{showvariable}
+boolean.boolean=unknown boolean
+! OK.
+l.28 ...n; showvariable boolean;
+ def\\= =end enddef;
+
+{def}
+{picture}
+{show}
+{scantokens}
+{length("^^c8")}
+>> 1
+! OK.
+<recently read> ;
+
+l.29 ...w length scantokens p~0;
+
+
+{pen}
+! Illegal suffix of declared variable will be flushed.
+<to be read again>
+ [
+<to be read again>
+ "a"
+l.30 pen p~[]~,q["a"
+ ,qq; p~1~=q=pencircle scaled mexp(-3016.5...
+Variables in declarations must consist entirely of
+names and collective subscripts, e.g., `x[]a'.
+Are you trying to use a reserved word in a variable name?
+I'm going to discard the junk I found here,
+up to the next comma or the end of the declaration.
+
+{pencircle}
+{-(3016.57654)}
+{mexp(-3016.57654)}
+{(future pen)scaled(0)}
+Pen polygon at line 30 (newly created):
+(0.5,0)
+ .. (0,0.5)
+ .. (-0.5,0)
+ .. (0,-0.5)
+ .. cycle
+
+{(unknown pen q)=(pen)}
+{(unknown pen p~1~)=(pen)}
+{transform}
+! Illegal suffix of declared variable will be flushed.
+<to be read again>
+ 0
+l.31 transform p,pp0
+ ; if p=p:qq=makepen((1,0)..cycle) xscaled...
+Variables in declarations must consist entirely of
+names and collective subscripts, e.g., `x[]a'.
+Explicit subscripts like `x15a' aren't permitted.
+I'm going to discard the junk I found here,
+up to the next comma or the end of the declaration.
+
+{if}
+{((xpart p,ypart p,xxpart p,xypart p,yxpart p,yypart p))=((xpart p,ypart
+ p,xxpart p,xypart p,yxpart p,yypart p))}
+{true}
+Path at line 31, before choices:
+(1,0)
+ ..cycle
+
+Path at line 31, after choices:
+(1,0)..controls (1,0) and (1,0)
+ ..cycle
+
+{makepen(path)}
+{hex("1000")}
+! Number too large (4096).
+<to be read again>
+ ;
+l.31 ...cle) xscaled hex "1000";
+ fi
+I have trouble with numbers greater than 4095; watch out.
+
+{(future pen)xscaled(4096)}
+! Pen too large.
+<to be read again>
+ ;
+l.31 ...cle) xscaled hex "1000";
+ fi
+The cycle you specified has a coordinate of 4095.5 or more.
+So I've replaced it by the trivial path `(0,0)..cycle'.
+
+Pen polygon at line 31 (newly created):
+(0,0)
+ .. cycle
+
+{(unknown pen qq)=(pen)}
+{fi}
+Path at line 32, before choices:
+(0,0)
+ ..(1,0)
+ ..(0,1)
+ ..(0,0)
+ ..(1,0)
+ ..(0,1)
+ ..cycle
+
+Path at line 32, after choices:
+(0,0)..controls (0.29056,-0.29056) and (0.75859,-0.30772)
+ ..(1,0)..controls (1.51964,0.66237) and (0.66237,1.51964)
+ ..(0,1)..controls (-0.30772,0.75859) and (-0.29056,0.29056)
+ ..(0,0)..controls (0.29056,-0.29056) and (0.75859,-0.30772)
+ ..(1,0)..controls (1.51964,0.66237) and (0.66237,1.51964)
+ ..(0,1)..controls (-0.30772,0.75859) and (-0.29056,0.29056)
+ ..cycle
+
+{makepen(path)}
+! Pen cycle must be convex.
+<to be read again>
+ ;
+l.32 ...)..(1,0)..(0,1)..cycle);
+
+The cycle you specified either has consecutive equal points
+or turns right or turns through more than 360 degrees.
+So I've replaced it by the trivial path `(0,0)..cycle'.
+
+Pen polygon at line 32 (newly created):
+(0,0)
+ .. cycle
+
+{qq:=pen}
+{vardef}
+! Missing parameter type; `expr' will be assumed.
+<to be read again>
+ )
+l.33 ...ext suffix a,b endtext()
+ )suffix@=show #@; p.a.b() end...
+You should've had `expr' or `suffix' or `text' here.
+
+{expandafter}
+{\}
+{let}
+
+\\->=end
+{outer}
+{pencircle}
+{(future pen)scaled(4.5)}
+{(future pen)yscaled(2)}
+Pen polygon at line 34 (newly created):
+(0.5,-4.5)
+ .. (1,-4)
+ .. (2,-2.5)
+ .. (2.5,0)
+ .. (2,2.5)
+ .. (1,4)
+ .. (0.5,4.5)
+ .. (-0.5,4.5)
+ .. (-1,4)
+ .. (-2,2.5)
+ .. (-2.5,0)
+ .. (-2,-2.5)
+ .. (-1,-4)
+ .. (-0.5,-4.5)
+ .. cycle
+
+{qq:=pen}
+{((6,12))-((xpart p7,ypart p7))}
+{((0,1))transformed((xpart p,ypart p,xxpart p,xypart p,yxpart p,yypart p
+))}
+{(x)-(x)}
+{(2)/(0)}
+>> 2
+! Division by zero.
+<to be read again>
+ ,
+l.35 ...)transformed p=(2/(x-x),
+ 3/0)transformed p;
+You're trying to divide the quantity shown above the error
+message by zero. I'm going to divide it by one instead.
+
+! Division by zero.
+l.35 ...ansformed p=(2/(x-x),3/0
+ )transformed p;
+I'll pretend that you meant to divide by 1.
+
+{((2,3))transformed((xpart p,ypart p,xxpart p,xypart p,yxpart p,yypart p
+))}
+{((linearform,linearform))=((linearform,linearform))}
+## yxpart p=-yypart p
+## xxpart p=-xypart p
+{((-xpart p7+6,-ypart p7+12))=((linearform,linearform))}
+## ypart p7=-ypart p-yypart p+12
+## xpart p7=-xpart p-xypart p+6
+{\}
+{if}
+{string(unknown string p~[-1])}
+{true}
+{(p0.1 0.2)-(p0.1 0.2)}
+! The token `endtext' is no longer a right delimiter.
+l.36 ...1.2-p.1.199999,1 endtext
+ transformed p;
+Strange: This token has lost its former meaning!
+I'll read it as a right delimiter this time;
+but watch out, I'll probably miss it later.
+
+{((0,1))transformed((xpart p,ypart p,-xypart p,xypart p,-yypart p,yypart
+ p))}
+{((linearform,linearform))=((linearform,linearform))}
+## ypart p=-yypart p+6
+#### ypart p7=6
+## xpart p=-xypart p+3
+#### xpart p7=3
+{(unknown path p1 2p)=((3,6))}
+{showstopping:=0}
+{showvariable}
+p=(-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p)
+p[]=pair
+p[][]=numeric
+p[][]p=unknown path
+p[][]p~=macro:(SUFFIX2)(SUFFIX3)(EXPR4)<suffix>->begingroup ETC.
+p[]~=unknown boolean
+p~=path
+p~[]=unknown string
+p~[]~=unknown pen
+p~[]~[][]=unknown picture
+p~[-1]=unknown string p~[-1]
+p~0=""^^c8""
+p~1~=pen
+p[[ [] ]]=numeric
+p[[ [-1] ]]=4095.99998
+p0.1 0.2=p0.1 0.2
+p1 2p=path
+p7=(3,6)
+{((-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))=((-xy
+part p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))}
+{let}
+{let}
+{xxpart((-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))
+}
+{(-xypart p)+(0.002)}
+{yxpart((-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))
+}
+{((xpart p2,ypart p2))-((xpart p1,ypart p1))}
+{(1)*((linearform,linearform))}
+{((xpart p1,ypart p1))+((linearform,linearform))}
+{(y)+(0.00002)}
+{yypart((-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))
+}
+{xypart((-xypart p+3,-yypart p+6,-xypart p,xypart p,-yypart p,yypart p))
+}
+{((5,y))=((yypart p,xypart p))}
+## y=xypart p
+## yypart p=5
+#### ypart p=1
+#### yxpart p=-5
+{((5,xypart p+0.00002))=((5,xypart p))}
+{((xpart p2,ypart p2))=((5,xypart p))}
+## ypart p2=xypart p
+## xpart p2=5
+{((-xypart p+0.002,-5))=((5,xypart p))}
+## xypart p=-5
+#### ypart p2=-5
+#### y=-5
+#### xpart p=8
+#### xxpart p=5
+! Inconsistent equation (off by -0.002).
+<to be read again>
+ ;
+l.38 ...,y)=(yypart p,xypart p);
+
+The equation I just read contradicts what was said before.
+But don't worry; continue and I'll just ignore it.
+
+Path at line 39, before choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)
+ ..cycle
+
+Path at line 39, after choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)..controls (17.52783,8.54388) and (-15.45978,-4.12262)
+ ..cycle
+
+{reverse(path)}
+{(path)transformed((8,1,5,-5,-5,5))}
+{(path)=(unknown path p2 3p)}
+
+p1 2p~(SUFFIX2)(SUFFIX3)(EXPR4)<suffix>->begingroup.show(SUFFIX0);p(SUFF
+IX2)(SUFFIX3)((EXPR4)endgroup
+(SUFFIX0)<-p1 2p
+(SUFFIX1)<-~
+(SUFFIX2)<-
+(SUFFIX3)<-2 3p~
+! A primary expression can't begin with `right delimiter that matches ('
+.
+<inserted text>
+ 0
+<to be read again>
+ )
+l.40 ...000000001]2p~(,[2]3p~,-)
+ =p~1~2[pausing];
+I'm afraid I need some sort of value in order to continue,
+so I've tentatively inserted `0'. You may want to
+delete this zero and insert something else;
+see Chapter 27 of The METAFONTbook for an example.
+
+{-(0)}
+(EXPR4)<-0
+(SUFFIX5)<-
+{begingroup}
+{show}
+>> Path at line 40:
+(3,6)
+
+
+p2 3p~(SUFFIX2)(SUFFIX3)(EXPR4)<suffix>->begingroup.show(SUFFIX0);p(SUFF
+IX2)(SUFFIX3)((EXPR4)endgroup
+(SUFFIX0)<-p2 3p
+(SUFFIX1)<-~
+! Missing `,' has been inserted.
+<to be read again>
+ (0)
+p1 2p~->...IX2)(SUFFIX3)((EXPR4)
+ endgroup
+<to be read again>
+ =
+l.40 ...00000001]2p~(,[2]3p~,-)=
+ p~1~2[pausing];
+I've finished reading a macro argument and am about to
+read another; the arguments weren't delimited correctly.
+You might want to delete some tokens before continuing.
+
+(SUFFIX2)<-
+! Missing `,' has been inserted.
+<to be read again>
+ (0)
+p1 2p~->...IX2)(SUFFIX3)((EXPR4)
+ endgroup
+<to be read again>
+ =
+l.40 ...00000001]2p~(,[2]3p~,-)=
+ p~1~2[pausing];
+I've finished reading a macro argument and am about to
+read another; the arguments weren't delimited correctly.
+You might want to delete some tokens before continuing.
+
+(SUFFIX3)<-
+! Missing `)' has been inserted.
+<to be read again>
+ endgroup
+<to be read again>
+ =
+l.40 ...00000001]2p~(,[2]3p~,-)=
+ p~1~2[pausing];
+I've gotten to the end of the macro parameter list.
+You might want to delete some tokens before continuing.
+
+(EXPR4)<-0
+(SUFFIX5)<-
+{begingroup}
+{show}
+>> Path at line 40:
+(8,1)..controls (-48.68579,57.68579) and (52.91974,-43.91974)
+ ..(28,-19)..controls (-7,16) and (63,-54)
+ ..cycle
+
+>> (8,1,5,-5,-5,5)
+! Isolated expression.
+<to be read again>
+ (
+p2 3p~->...;p(SUFFIX2)(SUFFIX3)(
+ (EXPR4)endgroup
+<to be read again>
+ endgroup
+<to be read again>
+ =
+l.40 ...00000001]2p~(,[2]3p~,-)=
+ p~1~2[pausing];
+I couldn't find an `=' or `:=' after the
+expression that is shown above this error message,
+so I guess I'll just ignore it and carry on.
+
+! Extra tokens will be flushed.
+<to be read again>
+ (
+p2 3p~->...;p(SUFFIX2)(SUFFIX3)(
+ (EXPR4)endgroup
+<to be read again>
+ endgroup
+<to be read again>
+ =
+l.40 ...00000001]2p~(,[2]3p~,-)=
+ p~1~2[pausing];
+I've just read as much of that statement as I could fathom,
+so a semicolon should have been next. It's very puzzling...
+but I'll try to get myself back together, by ignoring
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+{endgroup}
+{endgroup}
+{(vacuous)=(unknown picture p~1~2 1)}
+>> vacuous
+>> unknown picture p~1~2 1
+! Equation cannot be performed (vacuous=unknown picture).
+<to be read again>
+ ;
+l.40 ...2]3p~,-)=p~1~2[pausing];
+
+I'm sorry, but I don't know how to make such things equal.
+(See the two expressions just above the error message.)
+
+{vardef}
+Runaway definition?
+if.p(SUFFIX1)(SUFFIX1)=(SUFFIX1)(SUFFIX1)p.fi
+! Forbidden token found while scanning the definition of p~[].
+<inserted text>
+ enddef
+<to be read again>
+ ;
+l.41 ...iary t:=if p@ @=@ @p fi;
+ vardef p[][]p~[]=BAD; inner ;;
+I suspect you have forgotten an `enddef',
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{vardef}
+! This variable already starts with a macro.
+l.41 ...@p fi; vardef p[][]p~[]=
+ BAD; inner ;;
+After `vardef a' you can't say `vardef a.b'.
+So I'll have to discard this definition.
+
+Runaway definition?
+BAD
+! Forbidden token found while scanning the definition of a bad variable.
+<inserted text>
+ enddef
+<to be read again>
+ ;
+l.41 ...i; vardef p[][]p~[]=BAD;
+ inner ;;
+I suspect you have forgotten an `enddef',
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{inner}
+{show}
+{-(2)}
+
+p~[-2]@#<tertiary>->begingroup.if.p(SUFFIX1)(SUFFIX1)=(SUFFIX1)(SUFFIX1)
+p.fi.endgroup
+(SUFFIX0)<-p~
+(SUFFIX1)<-[-2]
+(SUFFIX2)<-~
+! A tertiary expression can't begin with `['.
+<inserted text>
+ 0
+<to be read again>
+ [
+<to be read again>
+ (3000)
+<to be read again>
+ ,
+l.42 show p~[-2]~[3000,
+ x]++4000>path p3; showvariable p,P;
+I'm afraid I need some sort of value in order to continue,
+so I've tentatively inserted `0'. You may want to
+delete this zero and insert something else;
+see Chapter 27 of The METAFONTbook for an example.
+
+{(x)-(3000)}
+{(0)*(x-3000)}
+{(3000)+(0)}
+{(3000)++(4000)}
+(EXPR3)<-4999.99998
+{begingroup}
+{if}
+{(p[-2][-2])=(-2)}
+>> p[-2][-2]+2
+! Unknown relation will be considered false.
+<to be read again>
+ [-2]
+p~[-2]->...1)=(SUFFIX1)(SUFFIX1)
+ p.fi.endgroup
+<to be read again>
+ >
+l.42 show p~[-2]~[3000,x]++4000>
+ path p3; showvariable p,P;
+Oh dear. I can't decide if the expression above is positive,
+negative, or zero. So this comparison test won't be `true'.
+
+{false}
+! Missing `:' has been inserted.
+<to be read again>
+ [-2]
+p~[-2]->...1)=(SUFFIX1)(SUFFIX1)
+ p.fi.endgroup
+<to be read again>
+ >
+l.42 show p~[-2]~[3000,x]++4000>
+ path p3; showvariable p,P;
+There should've been a colon after the condition.
+I shall pretend that one was there.
+
+{endgroup}
+{path((xpart p3,ypart p3))}
+{(vacuous)>(false)}
+>> vacuous
+>> false
+! Not implemented: (vacuous)>(boolean).
+<to be read again>
+ ;
+l.42 ...~[3000,x]++4000>path p3;
+ showvariable p,P;
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+>> false
+{showvariable}
+p=(8,1,5,-5,-5,5)
+p[]=pair
+p[][]=numeric
+p[][]p=unknown path
+p[][]p~=macro:(SUFFIX2)(SUFFIX3)(EXPR4)<suffix>->begingroup ETC.
+p[]~=unknown boolean
+p~=path
+p~[]@#=macro:<tertiary>->begingroup.if.p(SUFFIX1)(SUFFIX1) ETC.
+p[[ [] ]]=numeric
+p[[ [-1] ]]=4095.99998
+p[-2][-2]=p[-2][-2]
+p0.1 0.2=p0.1 0.2
+p1=(xpart p1,ypart p1)
+p1 2p=path
+p2=(5,-5)
+p2 3p=path
+p3=(xpart p3,ypart p3)
+p7=(3,6)
+> P=tag
+{numeric}
+{(2)*(alpha)}
+{(p3~)=(2alpha)}
+## alpha=0.5p3~
+{(1)/(-1)}
+{(3)*(beta)}
+{(p[-1]~)=(3beta)}
+## beta=0.33333p[-1]~
+{begingroup}
+{save}
+{showvariable}
+> p=tag
+{(3)*(0.33333(SAVED)p[-1]~)}
+{((SAVED)p[-1]~)=(1)}
+## (SAVED)p[-1]~=1
+#### beta=0.33333
+{restoring p}
+{endgroup}
+{showvariable}
+p=(8,1,5,-5,-5,5)
+p[]=pair
+p[][]=numeric
+p[][]p=unknown path
+p[][]p~=macro:(SUFFIX2)(SUFFIX3)(EXPR4)<suffix>->begingroup ETC.
+p[]~=numeric
+p~=path
+p~[]@#=macro:<tertiary>->begingroup.if.p(SUFFIX1)(SUFFIX1) ETC.
+p[[ [] ]]=numeric
+p[[ [-1] ]]=4095.99998
+p[-2][-2]=p[-2][-2]
+p[-1]~=1
+p0.1 0.2=p0.1 0.2
+p1=(xpart p1,ypart p1)
+p1 2p=path
+p2=(5,-5)
+p2 3p=path
+p3=(xpart p3,ypart p3)
+p3~=p3~
+p7=(3,6)
+{def}
+{def}
+! Missing `=' has been inserted.
+<to be read again>
+ false
+l.45 ...enddef;def!primary!false
+ ):!fi enddef;
+The next thing in this `def' should have been `=',
+because I've already looked at the definition heading.
+But don't worry; I'll pretend that an equals sign
+was present. Everything from here to `enddef'
+will be the replacement text of this macro.
+
+{def}
+{(path)scaled(-1)}
+Path at line 46, before choices:
+(1,-2)..controls (1.37755,-1.71404) and (1.71404,-1.37755)
+ ..(2,-1)..controls (2.33353,-0.55965) and (2.59729,-0.04124)
+ ..(2.5,0.5)..controls (2.36812,1.23369) and (1.6712,1.65662)
+ ..(1,2)..controls (0.66821,2.16974) and (0.33485,2.33641)
+ ..(0,2.5)
+ ..(-1,2)..controls (-1.37755,1.71404) and (-1.71404,1.37755)
+ ..(-2,1)..controls (-2.33353,0.55965) and (-2.59729,0.04124)
+ ..(-2.5,-0.5)..controls (-2.36812,-1.23369) and (-1.6712,-1.65662)
+ ..(-1,-2)..controls (-0.66821,-2.16974) and (-0.33485,-2.33641)
+ ..(0,-2.5)
+ ..cycle
+
+Path at line 46, after choices:
+(1,-2)..controls (1.37755,-1.71404) and (1.71404,-1.37755)
+ ..(2,-1)..controls (2.33353,-0.55965) and (2.59729,-0.04124)
+ ..(2.5,0.5)..controls (2.36812,1.23369) and (1.6712,1.65662)
+ ..(1,2)..controls (0.66821,2.16974) and (0.33485,2.33641)
+ ..(0,2.5)..controls (-0.37186,2.68167) and (-0.668,2.25146)
+ ..(-1,2)..controls (-1.37755,1.71404) and (-1.71404,1.37755)
+ ..(-2,1)..controls (-2.33353,0.55965) and (-2.59729,0.04124)
+ ..(-2.5,-0.5)..controls (-2.36812,-1.23369) and (-1.6712,-1.65662)
+ ..(-1,-2)..controls (-0.66821,-2.16974) and (-0.33485,-2.33641)
+ ..(0,-2.5)..controls (0.37186,-2.68167) and (0.668,-2.25146)
+ ..cycle
+
+{makepen(path)}
+Pen polygon at line 46 (newly created):
+(1,-2)
+ .. (2,-1)
+ .. (2.5,0.5)
+ .. (1,2)
+ .. (0,2.5)
+ .. (-1,2)
+ .. (-2,1)
+ .. (-2.5,-0.5)
+ .. (-1,-2)
+ .. (0,-2.5)
+ .. cycle
+
+{qq:=pen}
+{primarydef}
+{secondarydef}
+
+//<expr>->
+
+//<expr>->
+{pencircle}
+{length(path)}
+{(future pen)slanted(1)}
+{((3,6))-((5,-5))}
+
+_aa__<secondary>->if(true
+{(0.1)*(15)}
+{odd(1.50009)}
+{not(false)}
+{known((8,1,5,-5,-5,5))}
+{(true)and(true)}
+(EXPR0)<-true
+{if}
+{true}
+
+!<primary>->false):(EXPR0)fi
+(EXPR0)<-(5,-5)
+{false}
+{(true)or(false)}
+{true}
+{fi}
+{-((5,-5))}
+{-((5,-5))}
+{+(1)}
+{-(1)}
+>> -1
+! Improper curl has been replaced by 1.
+<to be read again>
+ )
+l.51 {curl- +1)
+ ..tension atleast1..cycle sqrt2++sqrt2***[[]];
+A curl must be a known, nonnegative number.
+
+! Missing `}' has been inserted.
+<to be read again>
+ )
+l.51 {curl- +1)
+ ..tension atleast1..cycle sqrt2++sqrt2***[[]];
+I've scanned a direction spec for part of a path,
+so a right brace should have come next.
+I shall pretend that one was there.
+
+Path at line 51, before choices:
+(-5,5)
+
+Path at line 51, after choices:
+(-5,5)
+
+Path at line 51, before choices:
+(3,6)..controls (5,-5) and (-5,5)
+ ..(-5,5)..tension atleast1
+ ..{2896.30943,-2896.30934}cycle
+
+Path at line 51, after choices:
+(3,6)..controls (5,-5) and (-5,5)
+ ..(-5,5)..controls (-3.29726,7.86205) and (0.64516,8.35484)
+ ..cycle
+
+! Missing `)' has been inserted.
+<to be read again>
+ sqrt
+l.51 ...ion atleast1..cycle sqrt
+ 2++sqrt2***[[]];
+I found no right delimiter to match a left one. So I've
+put one in, behind the scenes; this may fix the problem.
+
+{((-2,11))subpath(path)}
+{reverse(path)}
+{makepen(path)}
+
+**->[[show(EXPR0)*(EXPR1)]]
+(EXPR0)<-future pen
+(EXPR1)<-future pen
+{begingroup}
+{show}
+{(future pen)*(future pen)}
+>> future pen
+>> future pen
+! Not implemented: (future pen)*(future pen).
+<to be read again>
+ ]]
+<to be read again>
+ sqrt
+l.51 ...ion atleast1..cycle sqrt
+ 2++sqrt2***[[]];
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+! Pen path must be a cycle.
+<to be read again>
+ ]]
+<to be read again>
+ sqrt
+l.51 ...ion atleast1..cycle sqrt
+ 2++sqrt2***[[]];
+I can't make a pen from the given path.
+So I've replaced it by the trivial path `(0,0)..cycle'.
+
+>> Pen polygon at line 51:
+(0,0)
+ .. cycle
+
+{endgroup}
+(EXPR0)<-vacuous
+{sqrt(2)}
+{sqrt(2)}
+{(1.41422)++(1.41422)}
+{begingroup}
+{endgroup}
+
+***->expandafter(EXPR1)scantokens"**oct"(EXPR0)
+(EXPR0)<-2
+(EXPR1)<-vacuous
+{expandafter}
+{scantokens}
+{oct(2)}
+>> 2
+! Not implemented: oct(known numeric).
+<to be read again>
+ ;
+l.51 ...cle sqrt2++sqrt2***[[]];
+
+I'm afraid I don't know how to apply that operation to that
+particular type. Continue, and I'll simply return the
+argument (shown above) as the result of the operation.
+
+
+**->[[show(EXPR0)*(EXPR1)]]
+(EXPR0)<-vacuous
+(EXPR1)<-2
+{begingroup}
+{show}
+{(vacuous)*(2)}
+>> vacuous
+>> 2
+! Not implemented: (vacuous)*(known numeric).
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.51 ...cle sqrt2++sqrt2***[[]];
+
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+>> 2
+{endgroup}
+(EXPR0)<-vacuous
+{begingroup}
+{interim}
+{-(20.5)}
+{charcode:=-20.5}
+{proofing:=-20.5}
+{-(2048)}
+{chardp:=-2048}
+{shipout}
+{nullpicture}
+! Enormous chardp has been reduced.
+<to be read again>
+ ]]
+l.52 ...48;shipout nullpicture]]
+ ;
+Font metric dimensions must be less than 2048pt.
+
+{restoring proofing=0}
+{endgroup}
+{if}
+{-(275.50002)}
+{charexists(-275.50002)}
+{known(unknown path p0 0p)}
+{(true)>(false)}
+{known(path)}
+{(true)=(true)}
+{true}
+{randomseed}
+! Missing `:=' has been inserted.
+<to be read again>
+ charcode
+l.53 ... p~: randomseed charcode
+ ; fi
+Always say `randomseed:=<numeric expression>'.
+
+{randomseed:=-20.5}
+{fi}
+{randomseed}
+>> "goof"
+! Unknown value will be ignored.
+<to be read again>
+ ;
+l.54 randomseed:="goof";
+ a[($,18++1+-+18),(2,3)]=b[(3,2),(1,$);
+Your expression was too random for me to handle,
+so I won't change the random seed just now.
+
+{(18)++(1)}
+{(18.02776)+-+(18)}
+{((2,3))-((-1,1))}
+{(a)*((3,2))}
+{((-1,1))+((3a,2a))}
+! Missing `]' has been inserted.
+<to be read again>
+ ;
+l.54 ...8),(2,3)]=b[(3,2),(1,$);
+
+I've scanned an expression of the form `a[b,c',
+so a right bracket should have come next.
+I shall pretend that one was there.
+
+{((1,-1))-((3,2))}
+{(b)*((-2,-3))}
+{((3,2))+((-2b,-3b))}
+{((3a-1,2a+1))=((-2b+3,-3b+2))}
+## b=-0.66667a+0.33333
+## a=2
+#### b=-1
+{show}
+{(^)+(1)}
+{(~)+(2)}
+{-(1)}
+{((^+1,~+2))slanted(-1)}
+{-(2)}
+{((linearform,~+2))yscaled(-2)}
+{-((3,4))}
+{((linearform,-2~-4))zscaled((-3,-4))}
+>> (-5~-3^-13,10~-4^+16)
+{((xpart pp,ypart pp,xxpart pp,xypart pp,yxpart pp,yypart pp))xscaled(9)
+}
+{((8,1,5,-5,-5,5))transformed((9xpart pp,ypart pp,9xxpart pp,9xypart pp,
+yxpart pp,yypart pp))}
+>> (9xpart pp+72xxpart pp+9xypart pp,ypart pp+8yxpart pp+yypart pp,45xxp
+art pp-45xypart pp,-45xxpart pp+45xypart pp,5yxpart pp-5yypart pp,-5yxpa
+rt pp+5yypart pp)
+{((xpart pp,ypart pp,xxpart pp,xypart pp,yxpart pp,yypart pp))shifted((1
+,2))}
+{((8,1,5,-5,-5,5))transformed((8,1,5,-5,-5,5))}
+{((xpart pp+1,ypart pp+2,xxpart pp,xypart pp,yxpart pp,yypart pp))transf
+ormed((43,-34,50,-50,-50,50))}
+>> (50xpart pp-50ypart pp-7,-50xpart pp+50ypart pp+16,50xxpart pp-50yxpa
+rt pp,50xypart pp-50yypart pp,-50xxpart pp+50yxpart pp,-50xypart pp+50yy
+part pp)
+{(2)-(1)}
+{(_0)*(1)}
+{(1)+(_0)}
+{-(_0+1)}
+>> -_0-1
+{show}
+{normaldeviate}
+{-(-2048)}
+{uniformdeviate(2048)}
+{angle((-0.41438,761.67789))}
+{cosd(90.03117)}
+{(200)*(-0.00055)}
+{mexp(-0.10986)}
+{sqrt(0.99957)}
+{mlog(0.99979)}
+{sind(-0.05469)}
+{floor(-0.00096)}
+>> -1
+{string}
+{(unknown string s2)=(unknown string s4)}
+{(unknown string s1)=(unknown string s2)}
+{(unknown string s3)=(unknown string s5)}
+{(unknown string s2)=(unknown string s4)}
+! Redundant equation.
+<to be read again>
+ ;
+l.58 ... s1=s2=s4; s3=s5; s1=s2;
+ if s1<=s4<>(s1<>s3):show[[ch...
+I already knew that this equation was true.
+But perhaps no harm has been done; let's continue.
+
+{if}
+{(unknown string s2)<=(unknown string s1)}
+{(unknown string s2)<>(unknown string s5)}
+>> unknown string s2
+>> unknown string s5
+! Unknown relation will be considered false.
+<to be read again>
+ )
+l.58 ...=s2; if s1<=s4<>(s1<>s3)
+ :show[[char34=s2:=s3]]fi;
+The quantities shown above have not been equated.
+
+{(true)<>(false)}
+{true}
+{show}
+{begingroup}
+{char(34)}
+{s2:=unknown string s5}
+{(""")=(unknown string s2)}
+{endgroup}
+{fi}
+>> vacuous
+{(0.1)point(path)}
+{begingroup}
+{pencircle}
+Pen polygon at line 59 (newly created):
+(0.5,0)
+ .. (0,0.5)
+ .. (-0.5,0)
+ .. (0,-0.5)
+ .. cycle
+
+{endgroup}
+{((3.24413,0.64801))penoffset(pen)}
+{((0,-0.5))rotated(540)}
+{((0,0.5))/(0.33333)}
+{-(1.5)}
+! Paths don't touch; `&' will be changed to `..'.
+<to be read again>
+ )
+l.60 ...tcontrol-1.5of(p~&cycle)
+ -precontrol1/2of p~(p~)=s1:=s...
+When you join paths `p&q', the ending point of p
+must be exactly equal to the starting point of q.
+So I'm going to pretend that you said `p..q' instead.
+
+Path at line 60, before choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)
+ ..cycle
+
+Path at line 60, after choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)..controls (17.52783,8.54388) and (-15.45978,-4.12262)
+ ..cycle
+
+{(-1.5)postcontrol(path)}
+{(0.5)precontrol(path)}
+{((-2.75,-5))-((3.75,-1))}
+! Missing `of' has been inserted for directiontime.
+<to be read again>
+ (
+l.60 ...cle)-precontrol1/2of p~(
+ p~)=s1:=s4:=s4;
+I've got the first argument; will look now for the other.
+
+{((-6.5,-4))directiontime(path)}
+{decimal(0.5)}
+{((0,1.50003))substring("0.5")}
+{s4:=unknown string s1}
+{s1:=unknown string s4}
+{("0.")=(unknown string s1)}
+{path}
+{length(" ")}
+! Missing `,' has been inserted.
+<to be read again>
+ }
+l.61 ...p~[]; p~1=p2{length" "}
+ &cycle; p~1=p2=p~0; p2..contr...
+I've got the x coordinate of a path direction;
+will look for the y coordinate next.
+
+! An expression can't begin with `}'.
+<inserted text>
+ 0
+<to be read again>
+ }
+l.61 ...p~[]; p~1=p2{length" "}
+ &cycle; p~1=p2=p~0; p2..contr...
+I'm afraid I need some sort of value in order to continue,
+so I've tentatively inserted `0'. You may want to
+delete this zero and insert something else;
+see Chapter 27 of The METAFONTbook for an example.
+
+Path at line 61, before choices:
+(5,-5){4096,0}
+ ..{4096,0}cycle
+
+Path at line 61, after choices:
+(5,-5)..controls (5,-5) and (5,-5)
+ ..cycle
+
+{(unknown path p~1)=(path)}
+{((5,-5))=(unknown path p~0)}
+{(path)=(path)}
+! Redundant or inconsistent equation.
+<to be read again>
+ ;
+l.61 ..." "}&cycle; p~1=p2=p~0;
+ p2..controls-p2..cycle=p~2;(p7
+An equation between already-known quantities can't help.
+But don't worry; continue and I'll just ignore it.
+
+{-((5,-5))}
+Path at line 61, before choices:
+(5,-5)..controls (-5,5) and (-5,5)
+ ..cycle
+
+Path at line 61, after choices:
+(5,-5)..controls (-5,5) and (-5,5)
+ ..cycle
+
+{(path)=(unknown path p~2)}
+{length(path)}
+Path at line 62, before choices:
+(3,6)..tension 1.2
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)
+ ..(5,-5)..controls (-5,5) and (-5,5)
+ ..(5,-5){0,4096}..tension 1 and atleast1
+ ..cycle
+
+Path at line 62, after choices:
+(3,6)..controls (-3.01212,4.82085) and (-1.0424,-6.01257)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (-5,5) and (-5,5)
+ ..(5,-5)..controls (5,0.02791) and (4.99951,6.39217)
+ ..cycle
+
+>> x
+! Improper tension has been set to 1.
+<to be read again>
+ ..
+l.63 x..
+ {curl1}-p7{curl hex "IsBad"}..tension.75and.74999..p...
+The expression above should have been a number >=3/4.
+
+{-((3,6))}
+{hex("IsBad")}
+>> "IsBad"
+! String contains illegal digits.
+<to be read again>
+ }
+l.63 ...l1}-p7{curl hex "IsBad"}
+ ..tension.75and.74999..p2{0,1...
+I zeroed out characters that weren't hex digits.
+
+>> 0.74998
+! Improper tension has been set to 1.
+<to be read again>
+ ..
+l.63 ...}..tension.75and.74999..
+ p2{0,1}&p2{_,'}..cycle:=p
+The expression above should have been a number >=3/4.
+
+>> _
+! Undefined x coordinate has been replaced by 0.
+<to be read again>
+ ,
+l.63 ...and.74999..p2{0,1}&p2{_,
+ '}..cycle:=p
+I need a `known' x value for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+>> (xpart ',ypart ')
+! Undefined y coordinate has been replaced by 0.
+<to be read again>
+ }
+l.63 ...d.74999..p2{0,1}&p2{_,'}
+ ..cycle:=p
+I need a `known' y value for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+Path at line 63, before choices:
+(3,6)..controls (-3.01212,4.82085) and (-1.0424,-6.01257)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (-5,5) and (-5,5)
+ ..(5,-5)..controls (5,0.02791) and (4.99951,6.39217)
+ ..(3,6)
+ ..{curl 1}(-3,-6){curl 2989}..tension 0.75 and 1
+ ..{0,4096}(5,-5){curl 1}
+ ..cycle
+
+Path at line 63, after choices:
+(3,6)..controls (-3.01212,4.82085) and (-1.0424,-6.01257)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (5,-5) and (5,-5)
+ ..(5,-5)..controls (-5,5) and (-5,5)
+ ..(5,-5)..controls (5,0.02791) and (4.99951,6.39217)
+ ..(3,6)..controls (-2.44821,4.93144) and (-5.41408,-1.00029)
+ ..(-3,-6)..controls (-3.70045,-12.19485) and (5,-11.61679)
+ ..(5,-5)..controls (12.45406,-3.75087) and (10.41669,7.45464)
+ ..cycle
+
+>> path
+! Improper `:=' will be changed to `='.
+l.63 ...p2{0,1}&p2{_,'}..cycle:=
+ p
+I didn't find a variable name at the left of the `:=',
+so I'm going to pretend that you said `=' instead.
+
+{(path)=(unknown path p~4)}
+{-(9)}
+{((3.5001,7.00002))subpath(path)}
+{((-9,9))subpath(path)}
+{(path)=(unknown path p~6)}
+{show}
+>> Path at line 65:
+(-2.5,2.5)..controls (-2.49893,2.49893) and (0.00107,-0.00107)
+ ..(5,-5)..controls (5,0.02791) and (4.99951,6.39217)
+ ..(3,6)..controls (-2.44821,4.93144) and (-5.41408,-1.00029)
+ ..(-3,-6)..controls (-3.70045,-12.19485) and (5,-11.61679)
+ ..(5,-5)..controls (5.0001,-4.99998) and (5.00021,-4.99997)
+ ..(5.00032,-4.99995)
+
+{((1,2))directiontime(path)}
+>> 1
+{-(1.00002)}
+{((1,-1.00002))directiontime(path)}
+>> 3.24937
+Path at line 66, before choices:
+(0,0)..controls (1,1) and (0,1)
+ ..(1,0)
+
+Path at line 66, after choices:
+(0,0)..controls (1,1) and (0,1)
+ ..(1,0)
+
+{(unknown path p~3)=(path)}
+{show}
+Path at line 66, before choices:
+(5,-5){-2896.30943,-2896.30934}
+ ..{1831.78674,3663.57385}cycle
+
+Path at line 66, after choices:
+(5,-5)..controls (5,-5) and (5,-5)
+ ..cycle
+
+>> Path at line 66:
+(5,-5)..controls (5,-5) and (5,-5)
+ ..cycle
+
+{((0.31416,1))subpath(path)}
+{((1,1))directiontime(path)}
+{(1)-(0.31416)}
+{(0.27097)*(0.68584)}
+{(0.31416)+(0.18584)}
+>> 0.5
+{((0,0.25))subpath(path)}
+{((0.25,1))subpath(path)}
+Path at line 68, before choices:
+(0,0)..controls (0.25,0.25) and (0.375,0.4375)
+ ..(0.4375,0.5625)..controls (0.625,0.9375) and (0.25,0.75)
+ ..(1,0)
+
+Path at line 68, after choices:
+(0,0)..controls (0.25,0.25) and (0.375,0.4375)
+ ..(0.4375,0.5625)..controls (0.625,0.9375) and (0.25,0.75)
+ ..(1,0)
+
+! Missing `endtext' has been inserted.
+<to be read again>
+ ;
+l.68 ...~3)shifted begintext1,0;
+
+I found no right delimiter to match a left one. So I've
+put one in, behind the scenes; this may fix the problem.
+
+{(path)shifted((1,0))}
+{(unknown path p~5)=(path)}
+{(2/3)*((xpart ',ypart '))}
+{((0.66667xpart ',0.66667ypart '))zscaled((xpart ',ypart '))}
+>> (0,0,xpart ',-ypart ',ypart ',xpart ')
+! Transform components aren't all known.
+<to be read again>
+ {
+l.69 p~3:=2/3'zscaled'{
+ p~3}..controls(2,2/3(3))and penoffset(...
+I'm unable to apply a partially specified transformation
+except to a fully known pair or transform.
+Proceed, and I'll omit the transformation.
+
+>> 0.66667xpart '
+! Undefined x coordinate has been replaced by 0.
+<to be read again>
+ {
+l.69 p~3:=2/3'zscaled'{
+ p~3}..controls(2,2/3(3))and penoffset(...
+I need a `known' x value for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+>> 0.66667ypart '
+! Undefined y coordinate has been replaced by 0.
+<to be read again>
+ {
+l.69 p~3:=2/3'zscaled'{
+ p~3}..controls(2,2/3(3))and penoffset(...
+I need a `known' y value for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+>> path
+! Undefined coordinates have been replaced by (0,0).
+<to be read again>
+ }
+l.69 p~3:=2/3'zscaled'{p~3}
+ ..controls(2,2/3(3))and penoffset(...
+I need x and y numbers for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+{(2/3)*(3)}
+{(1/2)*(x)}
+{((0.5x,-5))penoffset((0,1))}
+>> (0.5x,-5)
+>> (0,1)
+! Not implemented: penoffset(unknown pair)of(pair).
+<to be read again>
+ (
+l.69 ...enoffset(1/2x,y)of(0,1)(
+ 1,0);
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+! Missing `..' has been inserted.
+<to be read again>
+ (
+l.69 ...enoffset(1/2x,y)of(0,1)(
+ 1,0);
+A path join command should end with two dots.
+
+Path at line 69, before choices:
+(0,0)..controls (2,2) and (0,1)
+ ..(1,0)
+
+Path at line 69, after choices:
+(0,0)..controls (2,2) and (0,1)
+ ..(1,0)
+
+{p~3:=path}
+{show}
+{reverse(path)}
+{(path)intersectiontimes(path)}
+>> (0.17227,0.28339)
+{(0.17227)point(path)}
+>> (0.71329,0.78188)
+{(1)-(0.28339)}
+{(0.71661)point(path)}
+>> (0.7133,0.78189)
+{show}
+{(path)shifted((0.01,0))}
+{(path)intersectiontimes(path)}
+{xpart((1.47693,1.18973))}
+{(1.47693)point(path)}
+{(path)shifted((0.01,0))}
+{(path)intersectiontimes(path)}
+{ypart((1.47693,1.18973))}
+{(1.18973)point(path)}
+{((1.50499,0.71521))-((1.495,0.71521))}
+>> (0.01,0)
+{begingroup}
+{interim}
+{tracingedges:=1}
+{-(1)}
+{-(1.00002)}
+{(-1)+-+(-1.00002)}
+! Pythagorean subtraction 1+-+1.00002 has been replaced by 0.
+l.73 ...es:=1; e[-1+-+ -1.00001]
+ =nullpicture; addto e1 also[[
+Since I don't take square roots of negative numbers,
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{nullpicture}
+{(unknown picture e0)=(picture)}
+{addto}
+{begingroup}
+{addto}
+{(path)scaled(3)}
+Path at line 74, before subdivision into octants:
+(3,0)..controls (3.75,0.75) and (4.125,1.3125)
+ ..(4.3125,1.6875)..controls (4.875,2.8125) and (3.75,2.25)
+ ..(6,0)..controls (3.75,2.25) and (4.875,2.8125)
+ ..(4.3125,1.6875)..controls (4.125,1.3125) and (3.75,0.75)
+ ..cycle
+
+Cycle spec at line 74, after subdivision:
+(3,0) % beginning in octant `NNE'
+ ..controls (3.75,0.75) and (4.125,1.3125)
+ ..(4.3125,1.6875) % segment 0
+ ..controls (4.5,2.0625) and (4.5,2.25)
+ ..(4.5,2.25) % segment 1
+% entering octant `NNW'
+% entering octant `WNW'
+% entering octant `WSW'
+% entering octant `SSW'
+% entering octant `SSE'
+ ..controls (4.5,2.25) and (4.5,1.5)
+ ..(6,0) % segment 1
+% entering octant `ESE'
+% entering octant `ENE'
+% entering octant `NNE'
+% entering octant `NNW'
+ ..controls (4.5,1.5) and (4.5,2.25)
+ ..(4.5,2.25) % segment 2
+% entering octant `WNW'
+% entering octant `WSW'
+% entering octant `SSW'
+ ..controls (4.5,2.25) and (4.5,2.0625)
+ ..(4.3125,1.6875) % segment 2
+ ..controls (4.125,1.3125) and (3.75,0.75)
+ ..(3,0) % segment 3
+% entering octant `SSE'
+% entering octant `ESE'
+% entering octant `ENE'
+ & cycle
+
+Tracing edges at line 74: (weight 1)
+(4,0)(4,1)(5,1)(5,3)(4,3)(4,1)(5,1)(5,0)(6,0)(6,1)(5,1)(5,3)
+(4,3)(4,1)(3,1)(3,0).
+
+{(unknown picture e1)=(unknown picture e2)}
+{(picture)=(unknown picture e1)}
+{cull}
+{nullpicture}
+{endgroup}
+{show}
+{-(4095)}
+{(picture)shifted((4089,-4095))}
+>> Edge structure at line 75:
+row -4093: | 4093+ 4094-
+row -4094: | 4093+ 4094-
+row -4095: | 4092+ 4093- 4094+ 4095-
+
+{-(4095)}
+{(picture)shifted((-4095,4092))}
+{-(3)}
+{(picture)shifted((-3,0))}
+>> Edge structure at line 75:
+row 4094: -4094+ -4093- -4094+ -4093- |
+row 4093: -4094+ -4093- -4094+ -4093- |
+row 4092: -4095+ -4092- -4093+ -4094- |
+
+{-(4095)}
+{(picture)shifted((4089,-4095))}
+! Too far to shift.
+<to be read again>
+ ]]
+l.76 e2 shifted(4089,-4095)]]
+ ;addto e1 also e2 shifted(-2,$)...
+I can't shift the picture as requested---it would
+make some coordinates too large or too small.
+Proceed, and I'll omit the transformation.
+
+>> Edge structure at line 76:
+row 2: 4+ 5- 4+ 5- |
+row 1: 4+ 5- 4+ 5- |
+row 0: 3+ 6- 5+ 4- |
+
+{restoring tracingedges=32767.99998}
+{endgroup}
+{addto}
+{-(2)}
+{(picture)shifted((-2,-1))}
+{-(4)}
+{(picture)shifted((-4,-1))}
+{e1:=picture}
+{addto}
+{(picture)rotated(89.999)}
+! That transformation is too hard.
+<to be read again>
+ +
+l.77 ...e0 also e1rotated89.999+
+ e1scaled$; show e0 xscaled-10...
+I can apply complicated transformations to paths,
+but I can only do integer operations on pictures.
+Proceed, and I'll omit the transformation.
+
+{(picture)scaled(-1)}
+{(picture)+(picture)}
+{show}
+{-(10)}
+{(picture)xscaled(-10)}
+{(picture)yscaled(2)}
+{(picture)xscaled(82)}
+! Scaled picture would be too big.
+<to be read again>
+ yscaled
+l.78 yscaled
+ 683;addto e1 doublepath (0,9) withweight-3 withwe...
+I can't xscale the picture as requested---it would
+make some coordinates too large or too small.
+Proceed, and I'll omit the transformation.
+
+{(picture)yscaled(683)}
+! Scaled picture would be too big.
+<to be read again>
+ ;
+l.78 yscaled683;
+ addto e1 doublepath (0,9) withweight-3 withwe...
+I can't yscale the picture as requested---it would
+make some coordinates too large or too small.
+Proceed, and I'll omit the transformation.
+
+>> Edge structure at line 78:
+row 5: -40- -50+ -40- -50+ |
+row 4: -40- -50+ -40- -50+ |
+row 3: -30+ 0- -10+ -20- -40- -50+ -40- -50+ | -10+ 0-
+row 2: -30+ 0- -10+ -20- -40- -50+ -40- -50+ | -10+ 0-
+row 1: 20- 10+ 20- 10+ -20+ -10- -20+ -10- -30- -60+ -50- -40+ |
+ -10+ -10+ 0- 0- 10+ 20-
+row 0: 20- 10+ 20- 10+ -20+ -10- -20+ -10- -30- -60+ -50- -40+ |
+ -10+ -10+ 0- 0- 10+ 20-
+row -1: 20- 10+ 20- 10+ -20+ -10- -20+ -10- | -20+ -10- 0+ 0+ 10-
+ 10-
+row -2: 20- 10+ 20- 10+ -20+ -10- -20+ -10- | -20+ -10- 0+ 0+ 10-
+ 10-
+row -3: 30- 0+ 10- 20+ | 0+ 10-
+row -4: 30- 0+ 10- 20+ | 0+ 10-
+
+{addto}
+{-(3)}
+{turningnumber(path)}
+! Weight must be -3, -2, -1, +1, +2, or +3.
+<to be read again>
+ withpen
+l.79 withpen
+ pencircle xscaled(oct"180"++1) rotated-angle(64...
+I'll ignore the bad `with' clause and look for another.
+
+{pencircle}
+{oct("180")}
+>> "180"
+! String contains illegal digits.
+<to be read again>
+ ++
+l.79 ...ircle xscaled(oct"180"++
+ 1) rotated-angle(64,$) shifte...
+I zeroed out characters that weren't in the range 0..7.
+
+{(64)++(1)}
+{(future pen)xscaled(64.00781)}
+{angle((64,-1))}
+{-(-0.89517)}
+{(future pen)rotated(0.89517)}
+{(future pen)shifted((9,8))}
+Pen polygon at line 80 (newly created):
+(9,7.5)
+ .. (37.5,8)
+ .. (41,8.5)
+ .. (9,8.5)
+ .. (-19.5,8)
+ .. (-23,7.5)
+ .. cycle
+
+Path at line 80, before choices:
+(-1,-1){curl 1}
+ ..(1,0)
+ ..(1,1)
+ ..(-1,0)
+ ..{curl 1}cycle
+
+Path at line 80, after choices:
+(-1,-1)..controls (-0.18568,-1.14464) and (0.62712,-0.73824)
+ ..(1,0)..controls (1.16457,0.32584) and (1.21637,0.71506)
+ ..(1,1)..controls (0.46898,1.69931) and (-0.7587,1.2167)
+ ..(-1,0)..controls (-1.06546,-0.33012) and (-1.06546,-0.66988)
+ ..cycle
+
+{makepen(path)}
+{(future pen)xscaled(4095.49998)}
+Pen polygon at line 80 (newly created):
+(4095.49998,0)
+ .. (4095.49998,1)
+ .. (-4095.49998,0)
+ .. (-4095.49998,-1)
+ .. cycle
+
+Path at line 80, before subdivision into octants:
+(0,9)..controls (0,9) and (0,9)
+ ..cycle
+
+! Curve out of range.
+<to be read again>
+ ;
+l.80 ...cycle)xscaled4095.49999;
+
+At least one of the coordinates in the path I'm about to
+digitize was really huge (potentially bigger than 4095).
+So I've cut it back to the maximum size.
+The results will probably be pretty wild.
+
+Cycle spec at line 80, after subdivision:
+(0,0) % beginning in octant `ENE'
+% entering octant `NNE'
+% entering octant `NNW'
+% entering octant `WNW'
+% entering octant `WSW'
+% entering octant `SSW'
+% entering octant `SSE'
+% entering octant `ESE'
+ & cycle
+
+Tracing edges at line 80: (weight -3)
+@ Octant ENE (2 offsets), from (-4095.49998,-1) to (4095.49998,0)
+@ transition line 0, from (-4095.49998,-1) to (-4095.49998,-1)
+@ transition line 1, from (-4095.49998,-1) to (4095.49998,0)
+@ transition line 2, from (4095.49998,0) to (4095.49998,0)
+(0,-1)
+@ Octant NNE (1 offset), from (4095.49998,0) to (4095.49998,1)
+@ transition line 2, from (4095.49998,0) to (4095.49998,0)
+@ transition line 1, from (4095.49998,0) to (4095.49998,1)
+(0,0)(4095,0)
+@ Octant NNW (0 offsets), from (4095.49998,1) to (4095.49998,1)
+@ transition line 0, from (4095.49998,1) to (4095.49998,1)
+@ Octant WNW (0 offsets), from (4095.49998,1) to (4095.49998,1)
+@ transition line 1, from (4095.49998,1) to (4095.49998,1)
+@ Octant WSW (2 offsets), from (4095.49998,1) to (-4095.49998,0)
+@ transition line 0, from (4095.49998,1) to (4095.49998,1)
+@ transition line 1, from (4095.49998,1) to (-4095.49998,0)
+@ transition line 2, from (-4095.49998,0) to (-4095.49998,0)
+(4095,1)(0,1)
+@ Octant SSW (1 offset), from (-4095.49998,0) to (-4095.49998,-1)
+@ transition line 2, from (-4095.49998,0) to (-4095.49998,0)
+@ transition line 1, from (-4095.49998,0) to (-4095.49998,-1)
+(0,0)(-4095,0)
+@ Octant SSE (0 offsets), from (-4095.49998,-1) to (-4095.49998,-1)
+@ transition line 0, from (-4095.49998,-1) to (-4095.49998,-1)
+@ Octant ESE (0 offsets), from (-4095.49998,-1) to (-4095.49998,-1)
+@ transition line 1, from (-4095.49998,-1) to (-4095.49998,-1)
+(-4095,-1).
+
+{show}
+>> Edge structure at line 81:
+row 1: | 0+ 1-
+row 0: 0--- 4095+++ -2+ -1- -2+ -1- | 0+ 1-
+row -1: -4095--- 0+++ -2+ -1- -2+ -1- | -1+ 0- 1+ 2-
+row -2: -3+ 0- -1+ -2- |
+
+{totalweight(picture)}
+>> -0.37476
+{charcode:=5}
+{chardp:=5}
+{-(1.5)}
+{xoffset:=-1.5}
+{shipout}
+[5]
+Edge structure at line 81 (just shipped out):
+row 1: | -1+ 0-
+row 0: | -3+ -3+ -2- -2- -1--- -1+ 0- 4094+++
+row -1: | -4096--- -3+ -3+ -2- -2- -2+ -1- -1+++ 0+ 1-
+row -2: | -4+ -3- -2+ -1-
+
+{showstats}
+Memory usage 1084&202 (749 still untouched)
+String usage 24&92 (858&11309 still untouched)
+{addto}
+{(path)yscaled(0.01666)}
+Path at line 82, before subdivision into octants:
+(0,0)..controls (15,0.06665) and (-15,-0.19995)
+ ..(4,0)..controls (-15,-0.19995) and (15,0.06665)
+ ..cycle
+
+Cycle spec at line 82, after subdivision:
+(0,0) % beginning in octant `ENE'
+ ..controls (1.74272,0.00775) and (2.87802,0.01099)
+ ..(3.55333,0.01099) % segment 0
+% entering octant `ESE'
+ ..controls (4.12251,0.01099) and (4.36491,0.00868)
+ ..(4.36877,0.00482) % segment 0
+% entering octant `SSE'
+ ..controls (4.3688,0.00479) and (4.36882,0.00476)
+ ..(4.36882,0.00473) % segment 0
+% entering octant `SSW'
+ ..controls (4.36882,0.0047) and (4.3688,0.00467)
+ ..(4.36877,0.00464) % segment 0
+% entering octant `WSW'
+ ..controls (4.34875,-0.01538) and (-1.88759,-0.07579)
+ ..(-2.48894,-0.07579) % segment 0
+% entering octant `WNW'
+ ..controls (-2.5191,-0.07579) and (-2.5351,-0.07564)
+ ..(-2.53542,-0.07532) % segment 0
+% entering octant `NNW'
+% entering octant `NNE'
+% entering octant `ENE'
+ ..controls (-2.53209,-0.07199) and (-0.88672,-0.05144)
+ ..(4,0) % segment 0
+% entering octant `NNE'
+% entering octant `NNW'
+% entering octant `WNW'
+% entering octant `WSW'
+ ..controls (-0.88672,-0.05144) and (-2.5321,-0.072)
+ ..(-2.53542,-0.07532) % segment 1
+% entering octant `SSW'
+% entering octant `SSE'
+% entering octant `ESE'
+ ..controls (-2.53511,-0.07562) and (-2.5191,-0.07579)
+ ..(-2.48894,-0.07579) % segment 1
+% entering octant `ENE'
+ ..controls (-1.88759,-0.07579) and (4.34877,-0.01537)
+ ..(4.36877,0.00464) % segment 1
+% entering octant `NNE'
+ ..controls (4.3688,0.00467) and (4.36882,0.0047)
+ ..(4.36882,0.00473) % segment 1
+% entering octant `NNW'
+ ..controls (4.36882,0.00476) and (4.3688,0.00479)
+ ..(4.36877,0.00482) % segment 1
+% entering octant `WNW'
+ ..controls (4.36491,0.00868) and (4.12251,0.01099)
+ ..(3.55333,0.01099) % segment 1
+% entering octant `WSW'
+ ..controls (2.87802,0.01099) and (1.74272,0.00775)
+ ..(0,0) % segment 1
+% entering octant `SSW'
+% entering octant `SSE'
+% entering octant `ESE'
+ & cycle
+
+Tracing edges at line 82: (weight 1)
+(No new edges added.)
+
+{(picture)yscaled(0)}
+{e3:=picture}
+{autorounding:=2}
+{addto}
+Path at line 83, before choices:
+(0.5,0){curl 1}
+ ..{curl 1}(3.5,1.5)
+
+Path at line 83, after choices:
+(0.5,0)..controls (1.5,0.5) and (2.5,1)
+ ..(3.5,1.5)
+
+Path at line 83, before subdivision into octants:
+(0.5,0)..controls (1.5,0.5) and (2.5,1)
+ ..(3.5,1.5)..controls (2.5,1) and (1.5,0.5)
+ ..cycle
+
+Cycle spec at line 83, after subdivision and double autorounding:
+(0.5,0) % beginning in octant `ENE'
+ ..controls (1.5,0.5) and (2.5,1)
+ ..(3.5,1.5) % segment 0
+% entering octant `NNE'
+% entering octant `NNW'
+% entering octant `WNW'
+% entering octant `WSW'
+ ..controls (2.5,1) and (1.5,0.5)
+ ..(0.5,0) % segment 1
+% entering octant `SSW'
+% entering octant `SSE'
+% entering octant `ESE'
+ & cycle
+
+Tracing edges at line 83: (weight 2)
+(2,0)(2,1)(4,1)(4,2)(4,1)(2,1)(2,0).
+
+{tracingspecs:=0}
+Path at line 84, before choices:
+(1,1)
+ ..cycle
+
+Path at line 84, after choices:
+(1,1)..controls (1,1) and (1,1)
+ ..cycle
+
+{makepen(path)}
+{(future pen)yscaled(1.5)}
+Pen polygon at line 84 (newly created):
+(1,1.5)
+ .. cycle
+
+{q:=pen}
+{((-1,yy))rotated((8,1,5,-5,-5,5))}
+>> (8,1,5,-5,-5,5)
+! Improper transformation argument.
+<to be read again>
+ {
+l.85 p~8=(($,yy)rotated p{
+ 0,1}..{0,$}(1,0){0,$}..cycle)scaled...
+The expression shown above has the wrong type,
+so I can't transform anything using it.
+Proceed, and I'll omit the transformation.
+
+>> yy
+! Undefined y coordinate has been replaced by 0.
+<to be read again>
+ {
+l.85 p~8=(($,yy)rotated p{
+ 0,1}..{0,$}(1,0){0,$}..cycle)scaled...
+I need a `known' y value for this part of the path.
+The value I found (see above) was no good;
+so I'll try to keep going by using zero instead.
+(Chapter 27 of The METAFONTbook explains that
+you might want to type `I ???' now.)
+
+Path at line 85, before choices:
+(-1,0){0,4096}
+ ..{0,-4096}(1,0){0,-4096}
+ ..{0,4096}cycle
+
+Path at line 85, after choices:
+(-1,0)..controls (-1,1.33333) and (1,1.33333)
+ ..(1,0)..controls (1,-1.33333) and (-1,-1.33333)
+ ..cycle
+
+{(path)scaled(2)}
+{(path)shifted((1000.49,9))}
+{(unknown path p~8)=(path)}
+{turningcheck:=1}
+{addto}
+>> (8,1,5,-5,-5,5)
+! Improper type.
+<to be read again>
+ withpen
+l.86 ...n q withweight p withpen
+ cycle p;
+Next time say `withweight <known numeric expression>';
+I'll ignore the bad `with' clause and look for another.
+
+{cycle((8,1,5,-5,-5,5))}
+>> false
+! Improper type.
+<to be read again>
+ ;
+l.86 ...eight p withpen cycle p;
+
+Next time say `withpen <known pen expression>';
+I'll ignore the bad `with' clause and look for another.
+
+Tracing edges at line 86: (weight 1)
+@ Octant NNE (0 offsets), from (999,10.5) to (999.15451,11.65451)
+@ transition line 1, from (999.15451,11.65451) to (999.15451,11.65451)
+(999,11)
+@ Octant ENE (0 offsets), from (999.15451,11.65451) to (1001,12)
+@ transition line 0, from (999.15451,11.65451) to (999.15451,11.65451)
+@ Octant ESE (0 offsets), from (1001,12) to (1002.8455,11.6545)
+@ transition line 1, from (1002.8455,11.6545) to (1002.8455,11.6545)
+@ Octant SSE (0 offsets), from (1002.8455,11.6545) to (1003,10.5)
+@ transition line 0, from (1002.8455,11.6545) to (1002.8455,11.6545)
+(999,12)(1003,12)
+@ Octant SSW (0 offsets), from (1003,10.5) to (1002.84549,9.34549)
+@ transition line 1, from (1002.84549,9.34549) to (1002.84549,9.34549)
+@ Octant WSW (0 offsets), from (1002.84549,9.34549) to (1001,9)
+@ transition line 0, from (1002.84549,9.34549) to (1002.84549,9.34549)
+@ Octant WNW (0 offsets), from (1001,9) to (999.1545,9.3455)
+@ transition line 1, from (999.1545,9.3455) to (999.1545,9.3455)
+@ Octant NNW (0 offsets), from (999.1545,9.3455) to (999,10.5)
+@ transition line 0, from (999.1545,9.3455) to (999.1545,9.3455)
+(1003,9)(999,9)(999,11).
+
+Tracing edges at line 86: (weight 1)
+@ Octant SSE (0 offsets), from (999,10.5) to (999.15451,9.34549)
+@ transition line 0, from (999,10.5) to (999,10.5)
+(999,11)
+@ Octant ESE (0 offsets), from (999.15451,9.34549) to (1001,9)
+@ transition line 1, from (1001,9) to (1001,9)
+@ Octant ENE (0 offsets), from (1001,9) to (1002.8455,9.3455)
+@ transition line 0, from (1001,9) to (1001,9)
+@ Octant NNE (0 offsets), from (1002.8455,9.3455) to (1003,10.5)
+@ transition line 1, from (1003,10.5) to (1003,10.5)
+(999,9)(1003,9)
+@ Octant NNW (0 offsets), from (1003,10.5) to (1002.84549,11.65451)
+@ transition line 0, from (1003,10.5) to (1003,10.5)
+@ Octant WNW (0 offsets), from (1002.84549,11.65451) to (1001,12)
+@ transition line 1, from (1001,12) to (1001,12)
+@ Octant WSW (0 offsets), from (1001,12) to (999.1545,11.6545)
+@ transition line 0, from (1001,12) to (1001,12)
+@ Octant SSW (0 offsets), from (999.1545,11.6545) to (999,10.5)
+@ transition line 1, from (999,10.5) to (999,10.5)
+(1003,12)(999,12)(999,11).
+
+{begingroup}
+{interim}
+{(xx)=(0.1)}
+## xx=0.1
+{autorounding:=0.1}
+{addto}
+Tracing edges at line 87: (weight -2)
+@ Octant NNE (0 offsets), from (999,10.5) to (999.38197,11.42706)
+@ transition line 1, from (999.38197,11.42706) to (999.38197,11.42706)
+@ Octant ENE (0 offsets), from (999.38197,11.42706) to (1001,12)
+@ transition line 0, from (999.38197,11.42706) to (999.38197,11.42706)
+(999,11)
+@ Octant ESE (0 offsets), from (1001,12) to (1002.61804,11.42705)
+@ transition line 1, from (1002.61804,11.42705) to (1002.61804,11.42705)
+(999,12)(1003,12)
+@ Octant SSE (0 offsets), from (1002.61804,11.42705) to (1003,10.5)
+@ transition line 0, from (1002.61804,11.42705) to (1002.61804,11.42705)
+@ Octant SSW (0 offsets), from (1003,10.5) to (1002.61803,9.57294)
+@ transition line 1, from (1002.61803,9.57294) to (1002.61803,9.57294)
+@ Octant WSW (0 offsets), from (1002.61803,9.57294) to (1001,9)
+@ transition line 0, from (1002.61803,9.57294) to (1002.61803,9.57294)
+@ Octant WNW (0 offsets), from (1001,9) to (999.38196,9.57295)
+@ transition line 1, from (999.38196,9.57295) to (999.38196,9.57295)
+(1003,9)(999,9)
+@ Octant NNW (0 offsets), from (999.38196,9.57295) to (999,10.5)
+@ transition line 0, from (999.38196,9.57295) to (999.38196,9.57295)
+(999,11).
+
+{restoring autorounding=2}
+{endgroup}
+{-(14.5)}
+{charext:=-14.5}
+{chardp:=-14.5}
+{chardx:=-14.5}
+{shipout}
+{-(picture)}
+{(picture)-(picture)}
+{-(picture)}
+{turningnumber(path)}
+{(6)*(-1)}
+{(picture)shifted((0,-6))}
+{(picture)+(picture)}
+[5.-14]
+Edge structure at line 88 (just shipped out):
+row 11: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 10: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 9: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 5: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 4: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 3: | 998- 998+ 998++ 1002-- 1002- 1002+
+row 2: | 3+ 3+ 3+ 3+ 4- 4- 4- 4-
+row 1: | -1+ -1+ 0- 0- 1+ 2- 3+ 3+ 3+ 3+ 4- 4- 4- 4-
+row 0: | -3+ -3+ -3+ -2- -2- -2- -1+ -1+ 0- 0- 0+ 0+ 1- 1- 2+ 2+
+ 3- 3- 4+ 4+ 5- 5-
+row -1: | -3+ -3+ -2- -2- -2+ -2+ -1- -1- 0+ 0+ 0+ 1- 1- 1-
+row -2: | -4+ -3- -2+ -2+ -1- -1-
+row -4: | 3+ 3+ 4- 4-
+row -5: | 3+ 3+ 4- 4-
+row -6: | 2+ 3- 4+ 5-
+
+Path at line 89, before choices:
+(0,0){curl 1}
+ ..(1,0.5)
+ ..(5,1.5)
+ ..(7,2.5)
+ ..(12,3.5)
+ ..{curl 1}(13,4)
+
+Path at line 89, after choices:
+(0,0)..controls (0.3153,0.20053) and (0.6504,0.36807)
+ ..(1,0.5)..controls (2.29114,0.98723) and (3.72412,0.97192)
+ ..(5,1.5)..controls (5.68967,1.78545) and (6.30771,2.22081)
+ ..(7,2.5)..controls (8.58867,3.1407) and (10.3946,2.90645)
+ ..(12,3.5)..controls (12.3506,3.62962) and (12.68594,3.7973)
+ ..(13,4)
+
+{(unknown path p~9)=(path)}
+{addto}
+Tracing edges at line 89: (weight 1)
+(1,0)(1,1)(5,1)(5,2)(7,2)(7,3)(12,3)(12,4)(12,3)(7,3)(7,2)(5,2)
+(5,1)(1,1)(1,0).
+
+{smoothing:=1}
+{addto}
+Tracing edges at line 90: (weight 1)
+(1,0)(1,1)(4,1)(4,2)(8,2)(8,3)(12,3)(12,4)(12,3)(8,3)(8,2)(4,2)
+(4,1)(1,1)(1,0).
+
+{addto}
+{-(4095)}
+Path at line 91, before choices:
+(-4095,0){curl 1}..tension 0.75 and 999
+ ..{curl 1}(0,2)
+
+Path at line 91, after choices:
+(-4095,0)..controls (-2275,0.88889) and (-1.36636,1.99933)
+ ..(0,2)
+
+Tracing edges at line 91: (weight 1)
+(-3071,0)(-3071,1)(-1024,1)(-1024,2)(-1024,1)(-3071,1)(-3071,0).
+
+{show}
+{-(90)}
+{(picture)rotated(-90)}
+>> Edge structure at line 91:
+
+{(picture)+(picture)}
+{(90)*(-1)}
+{(picture)rotated(-90)}
+{(picture)rotated(90)}
+>> Edge structure at line 91:
+row 2: | 4+++ 4+ 5--- 5-
+row 1: | 0+++ 0+ 1--- 1- 2++ 3-- 4+++ 4+ 5--- 5-
+row 0: | -2+++ -2+++ -1--- -1--- 0+++ 0+ 2--- 2- 3++ 4-- 5++ 6--
+row -1: | -2+++ -2+ 0--- 0- 1+++ 1+++ 2--- 2---
+row -2: | -3++ -2-- -1+++ -1+ 0--- 0-
+
+{if}
+{if}
+{elseif}
+! Missing `:' has been inserted.
+<inserted text>
+ :
+<to be read again>
+ elseif
+l.92 if "a" if "ab">"b" elseif
+ path reverse (3,4): >="aa":foo...
+
+{("ab")>("b")}
+{false}
+{reverse((3,4))}
+{path(path)}
+{true}
+{("a")>=("aa")}
+{false}
+{if}
+{((xpart ',ypart '))-((1,yy))}
+{((xpart '-1,linearform))<((xpart ',ypart '))}
+{true}
+{fi}
+{else}
+! Extra else.
+l.93 ... if '-(1,yy)<': :fi else
+ def dup text t=[[t;save endd...
+I'm ignoring this; it doesn't match any if.
+
+{def}
+{def}
+{def}
+
+||<tertiary>->show.substring(EXPR0)of("a"
+(EXPR0)<-(2,-1)
+{show}
+{("a")&("bc")}
+{((2,-1))substring("abc")}
+>> "ba"
+{tertiarydef}
+{def}
+{show}
+{((23.3,4.5))subpath(path)}
+Path at line 100, before choices:
+(0,0){curl 2}
+ ..(13,4)..controls (12.84297,3.89865) and (12.68063,3.80606)
+ ..(12.51372,3.72261){curl 3}
+ ..{curl 4}(-1,-1){curl 4}
+ ..{curl 2}cycle
+
+Path at line 100, after choices:
+(0,0)..controls (-40.15552,-36.7088) and (58.71173,33.50317)
+ ..(13,4)..controls (12.84297,3.89865) and (12.68063,3.80606)
+ ..(12.51372,3.72261)..controls (8.00914,2.1484) and (3.50458,0.5742)
+ ..(-1,-1)..controls (-0.66667,-0.66667) and (-0.33333,-0.33333)
+ ..cycle
+
+>> Path at line 100:
+(0,0)..controls (-40.15552,-36.7088) and (58.71173,33.50317)
+ ..(13,4)..controls (12.84297,3.89865) and (12.68063,3.80606)
+ ..(12.51372,3.72261)..controls (8.00914,2.1484) and (3.50458,0.5742)
+ ..(-1,-1)..controls (-0.66667,-0.66667) and (-0.33333,-0.33333)
+ ..cycle
+
+{numspecial}
+{(2)+(3)}
+
+++->[[dup.showtoken(EXPR0);]];def.x.expr.z.of(EXPR1)=z.enddef;texts(x=((
+EXPR1)+0)(EXPR1)+(EXPR1))("xx",foo((EXPR0)))=0]]
+(EXPR0)<-path
+(EXPR1)<-5
+{begingroup}
+
+dup<text>->[[(TEXT0);save
+(TEXT0)<-showtoken(path)
+{begingroup}
+{showtoken}
+> (path)
+{save}
+{restoring ;}
+{endgroup}
+{def}
+! Missing symbolic token inserted.
+<inserted text>
+ INACCESSIBLE
+++->...]];def.x.expr.z.of(EXPR1)
+ =z.enddef;texts(x=((EXPR1)+0)...
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+Sorry: You can't redefine a number, string, or expr.
+I've inserted an inaccessible symbol so that your
+definition will be completed without mixing me up too badly.
+
+
+texts(TEXT0)(TEXT1)<expr>->for.n:=,for.n"yy":n,length.if.false:endfor(TE
+XT1),(TEXT0),:if.string.n:forsuffixes.n=foo1,[foo(n)],':show(TEXT0),(TEX
+T1)|(n;exitif.not('<='+((EXPR2),yy))endfor.for.m= :+endfor.for.m=alpha.s
+tep-1.1 3$:+m.endfor.fi.endfor
+(TEXT0)<-x=((5)+0)(5)+(5)
+(TEXT1)<-"xx",foo((path))
+(EXPR2)<-0
+{for}
+{for}
+! Missing `=' has been inserted.
+<to be read again>
+ "yy"
+texts->for.n:=,for.n"yy"
+ :n,length.if.false:endfor(TEXT1),(TEX...
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+The next thing in this loop should have been `=' or `:='.
+But don't worry; I'll pretend that an equals sign
+was present, and I'll look for the values next.
+
+{loop value="yy"}
+{if}
+{false}
+{false}
+! Incomplete if; all text was ignored after line 100.
+<inserted text>
+ fi
+<to be read again>
+ ENDFOR
+<for("yy")> ....if.false: ENDFOR
+
+texts->...length.if.false:endfor
+ (TEXT1),(TEXT0),:if.string.n:...
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+A forbidden `outer' token occurred in skipped text.
+This kind of error happens when you say `if...' and forget
+the matching `fi'. I've inserted a `fi'; this might work.
+
+{[repeat the loop]}
+{length("xx")}
+
+foo(TEXT2)->begingroup(TEXT2)endgroup
+(SUFFIX0)<-
+(SUFFIX1)<-foo
+(TEXT2)<-(path)
+{begingroup}
+{endgroup}
+
+x<expr>of<primary>->(EXPR0)
+{(5)+(0)}
+(EXPR0)<-5
+! Missing `of' has been inserted for x.
+<to be read again>
+ (5)
+<argument> x=((5)+0)(5)
+ +(5)
+texts->...:endfor(TEXT1),(TEXT0)
+ ,:if.string.n:forsuffixes.n=f...
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+I've got the first argument; will look now for the other.
+
+(EXPR1)<-5
+{(5)+(5)}
+{loop value="yy"}
+{if}
+{string("yy")}
+{true}
+{forsuffixes}
+! Missing symbolic token inserted.
+<inserted text>
+ INACCESSIBLE
+<for("yy")> ...orsuffixes(EXPR0)
+ =foo1,[foo((EXPR0))],':show.x...
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+Sorry: You can't redefine a number, string, or expr.
+I've inserted an inaccessible symbol so that your
+definition will be completed without mixing me up too badly.
+
+
+foo(TEXT2)->begingroup(TEXT2)endgroup
+(SUFFIX0)<-
+(SUFFIX1)<-foo
+(TEXT2)<-("yy")
+{begingroup}
+{endgroup}
+>> "yy"
+! Improper subscript has been replaced by zero.
+<for("yy")> ...o1,[foo((EXPR0))]
+ ,':show.x=((5)+0)(5)+(5),"xx"...
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+A bracketed subscript must have a known numeric value;
+unfortunately, what I found was the value that appears just
+above this error message. So I'll try a zero subscript.
+
+{loop value=foo1}
+{show}
+
+x<expr>of<primary>->(EXPR0)
+{(5)+(0)}
+(EXPR0)<-5
+! Missing `of' has been inserted for x.
+<to be read again>
+ (5)
+<for(foo1)> show.x=((5)+0)(5)
+ +(5),"xx",foo((path))|(("yy");ex...
+<for("yy")> ...'+((0),yy))endfor
+ .for.m= :+endfor.for.m=alpha....
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+I've got the first argument; will look now for the other.
+
+(EXPR1)<-5
+{(5)+(5)}
+>> 10
+>> "xx"
+foo(TEXT2)->begingroup(TEXT2)endgroup
+(SUFFIX0)<-
+(SUFFIX1)<-foo
+(TEXT2)<-(path)
+{begingroup}
+{endgroup}
+
+|<suffix>->,(SUFFIX0)
+! Missing `)' has been inserted.
+<to be read again>
+ ("yy")
+<for(foo1)> ...o((path))|(("yy")
+ ;exitif.not('<='+((0),yy)) EN...
+<for("yy")> ...'+((0),yy))endfor
+ .for.m= :+endfor.for.m=alpha....
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+I've gotten to the end of the macro parameter list.
+You might want to delete some tokens before continuing.
+
+(SUFFIX0)<-
+>> Path at line 100:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)
+
+>> "yy"
+{exitif}
+{((xpart ',ypart '))+((0,yy))}
+{((xpart ',ypart '))<=((xpart ',linearform))}
+>> -yy
+! Unknown relation will be considered false.
+<to be read again>
+ )
+<for(foo1)> ...ot('<='+((0),yy))
+ ENDFOR
+<for("yy")> ...'+((0),yy))endfor
+ .for.m= :+endfor.for.m=alpha....
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+Oh dear. I can't decide if the expression above is positive,
+negative, or zero. So this comparison test won't be `true'.
+
+{[repeat the loop]}
+{loop value=0}
+{not(false)}
+{true}
+{for}
+{for}
+>> 0.5p3~
+! Improper initial value has been replaced by 0.
+<to be read again>
+ step
+<for("yy")> ....for.m=alpha.step
+ -1.1 3$:+m.endfor.fi ENDFOR
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+When you say `for x=a step b until c',
+the initial value `a' and the step size `b'
+and the final value `c' must have known numeric values.
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{-(1.1)}
+! Missing `until' has been inserted.
+<to be read again>
+ 3
+<for("yy")> ...=alpha.step-1.1 3
+ $:+m.endfor.fi ENDFOR
+texts->...3$:+m.endfor.fi.endfor
+
+<to be read again>
+ ]]
+<to be read again>
+ ;
+l.100 ...cle;numspecial p~++2+3;
+ [[
+I assume you meant to say `until' after `step'.
+So I'll look for the final value and colon next.
+
+{(3)*(-1)}
+{loop value=0}
+{[repeat the loop]}
+{loop value=-1.1}
+{+(0)}
+{[repeat the loop]}
+{loop value=-2.20001}
+{(0)+(-1.1)}
+{[repeat the loop]}
+{fi}
+{[repeat the loop]}
+{loop value=2}
+{if}
+{string(2)}
+{false}
+{[repeat the loop]}
+{loop value=path}
+{if}
+{string(path)}
+{false}
+{[repeat the loop]}
+{loop value=10}
+{if}
+{string(10)}
+{false}
+{[repeat the loop]}
+{(-1.1)+(-2.20001)}
+{endgroup}
+{begingroup}
+{let}
+{save}
+{def}
+{def}
+
+texts->input
+{input}
+(trap.mf
+if->endinput?
+{endinput}
+{if}
+{known(0)}
+{not(true)}
+{false}
+)
+! Incomplete if; all text was ignored after line 2.
+<inserted text>
+ fi
+l.101 ...input enddef;texts trap
+ ]];
+The file ended while I was skipping conditional text.
+This kind of error happens when you say `if...' and forget
+the matching `fi'. I've inserted a `fi'; this might work.
+
+{restoring \}
+{restoring if}
+{endgroup}
+
+dup<text>->[[(TEXT0);save
+Runaway text?
+[[def.texts.secondary.x=primarydef.y++y=x@y.enddef;showtoken ETC.
+! Forbidden token found while scanning a text argument.
+<inserted text>
+ endgroup
+<to be read again>
+ \
+l.102 ...showtoken++;x enddef]]\
+ ;
+It seems that a right delimiter was left out,
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+(TEXT0)<-[[def.texts.secondary.x=primarydef.y++y=x@y.enddef;showtoken++;
+x.enddef]]
+{begingroup}
+{begingroup}
+{def}
+{endgroup}
+{save}
+! Missing symbolic token inserted.
+<inserted text>
+ INACCESSIBLE
+<to be read again>
+ \
+l.102 ...showtoken++;x enddef]]\
+ ;
+Sorry: You can't redefine my error-recovery tokens.
+I've inserted an inaccessible symbol so that your
+definition will be completed without mixing me up too badly.
+
+{\}
+{proofing:=1}
+
+texts<secondary>->primarydef.y++y=(EXPR0)@y.enddef;showtoken++;(EXPR0)
+(EXPR0)<-"a"
+{primarydef}
+{showtoken}
+> ++=primarydef'd macro:
+("a")@(EXPR1)
+{("a")&("b")}
+ab
+texts<secondary>->primarydef.y++y=(EXPR0)@y.enddef;showtoken++;(EXPR0)
+{(1.00002)*(a1)}
+{-(1.00002a1)}
+(EXPR0)<--1.00002a1
+{primarydef}
+{showtoken}
+> ++=primarydef'd macro:
+(-1.00002a1)@(EXPR1)
+{(a2)+(a3)}
+{(linearform)+(a4)}
+{(linearform)+(a5)}
+{(linearform)+(a6)}
+{-(linearform)}
+{(-1.00002a1)=(linearform)}
+## a1=0.99998a6+0.99998a5+0.99998a4+0.99998a3+0.99998a2
+{-((a3,a2))}
+{((-a3,-a2))/(0.99998)}
+{(a4)+(a5)}
+{begingroup}
+{showdependencies}
+a1=0.99998a6+0.99998a5+0.99998a4+0.99998a3+0.99998a2
+alpha=0.5p3~
+{endgroup}
+{(linearform)+(a6)}
+{(a3)+(a4)}
+{(linearform)+(a5)}
+{(linearform)+(a6)}
+{-((linearform,linearform))}
+{((-1.00002a3,-1.00002a2))=((linearform,linearform))}
+## a2=0.99998a6+0.99998a5+0.99998a4+0.99998a3
+## a3=0.99998a6+0.99998a5+0.99998a4
+{restoring INACCESSIBLE}
+{endgroup}
+{(1.00002)*(0.25a4*4)}
+{(0.25a4*4)+(1)}
+{(0.25a5*4)+(0.25a6*4)}
+{(0.25a4*4+1)=(linearform)}
+## a4*4=0.99998a6*4+0.99998a5*4-3.99994
+{(0.9)*(0.5p3~)}
+{(0.45p3~)+(7)}
+{alpha:=0.45p3~+7}
+## alpha=0.45p3~+7
+{showdependencies}
+alpha=0.45p3~+7
+a4=0.25a6*4+0.25a5*4-0.99998
+a3=0.49998a6*4+0.49998a5*4-0.99997
+a2=0.99997a6*4+0.99997a5*4-1.99992
+a1=1.99992a6*4+1.99992a5*4-3.99982
+{(0.66667)-(0.25a6*4)}
+{(0.25a6*4)=(-0.25a6*4+0.66667)}
+## a6*4=1.33334
+{(0.25a5*4)=(0.33333)}
+## a5*4=1.33331
+#### a4=-0.33333
+#### a3=0.33333
+#### a2=0.66664
+#### a1=1.33328
+{cull}
+{(4)*(0.33333)}
+{chardp:=27}
+{charcode:=27}
+{openwindow}
+! Improper `openwindow'.
+<to be read again>
+ ;
+l.107 ...) to (0,0) at "whoops";
+ addto p; shipout p; cull p;
+Say `openwindow k from (r0,c0) to (r1,c1) at (x,y)',
+where all quantities are known and k is between 0 and 15.
+
+{addto}
+>> (8,1,5,-5,-5,5)
+! Not a suitable variable.
+<to be read again>
+ ;
+l.107 ...) at "whoops"; addto p;
+ shipout p; cull p;
+At this point I needed to see the name of a picture variable.
+(Or perhaps you have indeed presented me with one; I might
+have missed it, if it wasn't followed by the proper token.)
+So I'll not change anything just now.
+
+{shipout}
+! Variable p is the wrong type (transform).
+<to be read again>
+ ;
+l.107 ...s"; addto p; shipout p;
+ cull p;
+I was looking for a "known" picture variable.
+So I'll not change anything just now.
+
+{cull}
+>> (8,1,5,-5,-5,5)
+! Not a suitable variable.
+<to be read again>
+ ;
+l.107 ...o p; shipout p; cull p;
+
+At this point I needed to see the name of a picture variable.
+(Or perhaps you have indeed presented me with one; I might
+have missed it, if it wasn't followed by the proper token.)
+So I'll not change anything just now.
+
+{openwindow}
+{-(0.5)}
+
+Calling BLANKRECTANGLE(0,0,0,0)
+Calling UPDATESCREEN
+{special}
+>> (8,1,5,-5,-5,5)
+! Unsuitable expression.
+<to be read again>
+ ;
+l.108 ...$) at (0,0); special p;
+ numspecial "p";
+The expression shown above has the wrong type to be output.
+
+{numspecial}
+>> "p"
+! Unsuitable expression.
+<to be read again>
+ ;
+l.108 ...cial p; numspecial "p";
+
+The expression shown above has the wrong type to be output.
+
+{openwindow}
+{(32)*(1024)}
+! Arithmetic overflow.
+l.109 ....49999 from (0,32*1024)
+ to (1,31*1057) at (0,0); shi...
+Uh, oh. A little while ago one of the quantities that I was
+computing got too large, so I'm afraid your answers will be
+somewhat askew. You'll probably have to adopt different
+tactics next time. But I shall try to carry on anyway.
+
+{(31)*(1057)}
+
+Calling BLANKRECTANGLE(100,100,0,1)
+Calling UPDATESCREEN
+{shipout}
+[27.-14]
+Edge structure at line 109 (just shipped out):
+row 0: | -3++ -2-- -1++ 4094--
+row -1: | -4096++ -3-- -2++ -1--
+
+{openwindow}
+Path at line 110, before choices:
+(0,0)
+
+Path at line 110, after choices:
+(0,0)
+
+{length(path)}
+
+Calling BLANKRECTANGLE(0,2,0,4)
+Calling UPDATESCREEN
+{openwindow}
+{length((-1,0))}
+{-(5)}
+
+Calling BLANKRECTANGLE(1,10,1,3)
+Calling UPDATESCREEN
+{display}
+{nullpicture}
+>> picture
+! Not a suitable variable.
+<to be read again>
+ ;
+l.112 display nullpicture;
+ display p inwindow 3; display p in...
+At this point I needed to see the name of a picture variable.
+(Or perhaps you have indeed presented me with one; I might
+have missed it, if it wasn't followed by the proper token.)
+So I'll not change anything just now.
+
+{display}
+>> 3
+! Bad window number.
+<to be read again>
+ ;
+l.112 ...; display p inwindow 3;
+ display p inwindow 6;
+It should be the number of an open window.
+
+{display}
+! Variable p is the wrong type (transform).
+<to be read again>
+ ;
+l.112 ...; display p inwindow 6;
+
+I was looking for a "known" picture variable.
+So I'll not change anything just now.
+
+{display}
+
+Calling BLANKRECTANGLE(1,10,1,3)
+Calling PAINTROW(2,1;4,5,6,10)
+Calling UPDATESCREEN
+{cull}
+! Enormous number has been reduced.
+l.113 ...opping (0.1,4095.999999
+ ) withweight 3.5
+I can't handle numbers bigger than about 4095.99998;
+so I've changed your constant to that maximum amount.
+
+! Weight must be -3, -2, -1, +1, +2, or +3.
+<to be read again>
+ withweight
+l.114 withweight
+ -3.5; display e0 inwindow 5.5; addto e0 also ...
+I'll ignore the bad `with' clause and look for another.
+
+{-(3.5)}
+! Bad culling amounts.
+<to be read again>
+ ;
+l.114 withweight-3.5;
+ display e0 inwindow 5.5; addto e0 also ...
+Always cull by known amounts that exclude 0.
+
+{display}
+
+Calling BLANKRECTANGLE(1,10,1,3)
+Calling PAINTROW(2,1;4,5,6,8,9,10)
+Calling PAINTROW(1,1;6,7,8,9)
+Calling UPDATESCREEN
+{addto}
+>> (8,1,5,-5,-5,5)
+! Improper `addto'.
+<to be read again>
+ ;
+l.114 ...w 5.5; addto e0 also p;
+ addto e0 contour 0;
+This expression should have specified a known picture.
+So I'll not change anything just now.
+
+{addto}
+>> 0
+! Improper `addto'.
+<to be read again>
+ ;
+l.114 ... p; addto e0 contour 0;
+
+This expression should have been a known path.
+So I'll not change anything just now.
+
+{display}
+
+Calling BLANKRECTANGLE(0,2,0,4)
+Calling PAINTROW(2,1;0,1)
+Calling PAINTROW(1,1;0,1)
+Calling PAINTROW(0,1;1,2)
+Calling UPDATESCREEN
+{addto}
+! Not a cycle.
+<to be read again>
+ ;
+l.115 ...; addto e0 contour p~9;
+
+That contour should have ended with `..cycle' or `&cycle'.
+So I'll not change anything just now.
+
+{display}
+{(3)+(3)}
+
+Calling BLANKRECTANGLE(1,10,1,3)
+Calling PAINTROW(2,1;4,5,6,10)
+Calling UPDATESCREEN
+{display}
+{begingroup}
+{vardef}
+{endgroup}
+! Variable e0e0 has been obliterated.
+<to be read again>
+ ;
+l.116 ...vardef e[]e=enddef;6]];
+
+It seems you did a nasty thing---probably by accident,
+but nevertheless you nearly hornswoggled me...
+While I was evaluating the right-hand side of this
+command, something happened, and the left-hand side
+is no longer a variable! So I won't change anything.
+
+{addto}
+{display}
+Calling PAINTROW(1,1;0,1,2)
+Calling PAINTROW(0,0;0,1,2)
+Calling UPDATESCREEN
+{ligtable}
+{display}
+{display}
+Calling UPDATESCREEN
+{show}
+{begingroup}
+{interim}
+{tracingcommands:=0}
+
+lig(TEXT0)(TEXT1)->ligtable0::for*=1step1until60:0kern.boundarychar+*,en
+dfor.skipto0;ligtable(TEXT0):(TEXT0)(TEXT1)0,skipto255;boundarychar:=bou
+ndarychar+51.29999
+(TEXT0)<-"g"
+(TEXT1)<-=:|
+
+lig(TEXT0)(TEXT1)->ligtable0::for*=1step1until60:0kern.boundarychar+*,en
+dfor.skipto0;ligtable(TEXT0):(TEXT0)(TEXT1)0,skipto255;boundarychar:=bou
+ndarychar+51.29999
+(TEXT0)<-"h":"i"
+(TEXT1)<-|=:
+! Character h is already in a ligtable.
+<to be read again>
+ :
+<argument> "h":
+ "i"
+lig->...;ligtable(TEXT0):(TEXT0)
+ (TEXT1)0,skipto255;boundarych...
+l.119 ...=:|); lig("h":"i")(|=:)
+ ;
+It's not legal to label a character more than once.
+So I'll not change anything just now.
+
+
+lig(TEXT0)(TEXT1)->ligtable0::for*=1step1until60:0kern.boundarychar+*,en
+dfor.skipto0;ligtable(TEXT0):(TEXT0)(TEXT1)0,skipto255;boundarychar:=bou
+ndarychar+51.29999
+(TEXT0)<-"j"
+(TEXT1)<-|=:>
+
+lig(TEXT0)(TEXT1)->ligtable0::for*=1step1until60:0kern.boundarychar+*,en
+dfor.skipto0;ligtable(TEXT0):(TEXT0)(TEXT1)0,skipto255;boundarychar:=bou
+ndarychar+51.29999
+(TEXT0)<-"k"
+(TEXT1)<-=:|>
+
+lig(TEXT0)(TEXT1)->ligtable0::for*=1step1until60:0kern.boundarychar+*,en
+dfor.skipto0;ligtable(TEXT0):(TEXT0)(TEXT1)0,skipto255;boundarychar:=bou
+ndarychar+51.29999
+(TEXT0)<-"l"
+(TEXT1)<-|=:|>
+{restoring tracingcommands=2.1}
+{endgroup}
+>> vacuous
+{begingroup}
+{let}
+{vardef}
+{endgroup}
+! Variable c.a1 has been obliterated.
+<to be read again>
+ ;
+l.121 ...;vardef b=enddef;1]] ];
+ ligtable"m":0=:0,skipto5;
+It seems you did a nasty thing---probably by accident,
+but nevertheless you nearly hornswoggled me...
+While I was evaluating the suffix of this variable,
+something was redefined, and it's no longer a variable!
+In order to get back on my feet, I've inserted `0' instead.
+
+{b1:=0}
+! Variable b1 has been obliterated.
+<to be read again>
+ ;
+l.121 ...;vardef b=enddef;1]] ];
+ ligtable"m":0=:0,skipto5;
+It seems you did a nasty thing---probably by accident,
+but nevertheless you nearly hornswoggled me...
+While I was evaluating the right-hand side of this
+command, something happened, and the left-hand side
+is no longer a variable! So I won't change anything.
+
+{ligtable}
+! A statement can't begin with `skipto'.
+<to be read again>
+ !!
+l.122 !!
+ ; errhelp 0; errmessage "Be like Jane";
+I was looking for the beginning of a new statement.
+If you just proceed without changing anything, I'll ignore
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+! Extra tokens will be flushed.
+<to be read again>
+ !!
+l.122 !!
+ ; errhelp 0; errmessage "Be like Jane";
+I've just read as much of that statement as I could fathom,
+so a semicolon should have been next. It's very puzzling...
+but I'll try to get myself back together, by ignoring
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+{errhelp}
+>> 0
+! Not a string.
+<to be read again>
+ ;
+l.122 !!; errhelp 0;
+ errmessage "Be like Jane";
+A message should be a known string expression.
+
+{errmessage}
+! Be like Jane.
+<to be read again>
+ ;
+l.122 ...message "Be like Jane";
+
+This error message was generated by an `errmessage'
+command, so I can't give any explicit help.
+Pretend that you're Miss Marple: Examine all clues,
+and deduce the truth by inspired guesses.
+
+{errhelp}
+{errmessage}
+! .
+<to be read again>
+ ;
+l.123 ...e%%%lp%"; errmessage"";
+ errhelp ""; errmessage "Anot...
+He%
+lp
+
+
+{errhelp}
+{errmessage}
+! Another.
+<to be read again>
+ ;
+l.123 ...; errmessage "Another";
+
+(That was another `errmessage'.)
+
+{headerbyte}
+>> 0
+! Improper location.
+<to be read again>
+ ;
+l.124 headerbyte 0;
+ headerbyte(48.5)substring(-9,9)of"long"; ...
+I was looking for a known, positive number.
+For safety's sake I'll ignore the present command.
+
+{headerbyte}
+! Missing `:' has been inserted.
+<to be read again>
+ substring
+l.124 ...aderbyte(48.5)substring
+ (-9,9)of"long"; for\=0:\
+A colon should follow a headerbyte or fontinfo location.
+
+{-(9)}
+{((-9,9))substring("long")}
+>> "long"
+! Invalid code has been replaced by 0.
+<to be read again>
+ ;
+l.124 ...ubstring(-9,9)of"long";
+ for\=0:\
+I was looking for a number between 0 and 255, or for a
+string of length 1. Didn't find it; will use 0 instead.
+
+{for}
+Runaway loop?
+! Forbidden token found while scanning the text of a for loop.
+<inserted text>
+ endfor
+<to be read again>
+ \
+l.124 ...-9,9)of"long"; for\=0:\
+
+I suspect you have forgotten an `endfor',
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{loop value=0}
+{[repeat the loop]}
+{\}
+{headerbyte}
+{(2)*(0.33333)}
+{fontdimen}
+{(2)*(0.33333)}
+>> "q"
+! Improper font parameter.
+<to be read again>
+ ;
+l.125 ..."; fontdimen 9:2a6,"q";
+ fontdimen 1:2048;
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{fontdimen}
+{fontmaking:=1}
+{extensible}
+! Missing `:' has been inserted.
+<to be read again>
+ 5
+l.126 ...king:=1; extensible 5 5
+ ,"c"255.5,"d"; charlist 0:5:"...
+I'm processing `extensible c: t,m,b,r'.
+
+! Missing `,' has been inserted.
+<to be read again>
+ 255.5
+l.126 ...extensible 5 5,"c"255.5
+ ,"d"; charlist 0:5:"a":"d";
+I'm processing `extensible c: t,m,b,r'.
+
+>> 255.5
+! Invalid code has been replaced by 0.
+<to be read again>
+ ,
+l.126 ...xtensible 5 5,"c"255.5,
+ "d"; charlist 0:5:"a":"d";
+I was looking for a number between 0 and 255, or for a
+string of length 1. Didn't find it; will use 0 instead.
+
+{charlist}
+! Character code 5 is already extensible.
+<to be read again>
+ :
+l.126 ...,"d"; charlist 0:5:"a":
+ "d";
+It's not legal to label a character more than once.
+So I'll not change anything just now.
+
+{ligtable}
+! Too far to skip.
+l.127 ligtable255:255::
+ "a"=:"b","d" kern -2048,"c":0:99.5:"e"...
+At most 127 lig/kern steps can separate skipto1 from 1::.
+
+{-(2048)}
+! Character code 0 is already in a charlist.
+<to be read again>
+ :
+l.127 ...","d" kern -2048,"c":0:
+ 99.5:"e"|=:|"f",0kern';
+It's not legal to label a character more than once.
+So I'll not change anything just now.
+
+>> (xpart ',ypart ')
+! Improper kern.
+<to be read again>
+ ;
+l.127 ...99.5:"e"|=:|"f",0kern';
+
+The amount of kern should be a known numeric value.
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{ligtable}
+! Character code 5 is already extensible.
+<to be read again>
+ :
+l.128 ligtable 5:
+ 0; def clear(text x)=interim x:=$ enddef; cl...
+It's not legal to label a character more than once.
+So I'll not change anything just now.
+
+! Illegal ligtable step.
+<to be read again>
+ ;
+l.128 ligtable 5:0;
+ def clear(text x)=interim x:=$ enddef; cl...
+I was looking for `=:' or `kern' here.
+
+{def}
+
+clear(TEXT0)->interim(TEXT0):=$
+(TEXT0)<-hppp
+{interim}
+{hppp:=-1}
+{(0)=(0)}
+! Redundant equation.
+<to be read again>
+ ;
+l.128 ...f; clear(hppp); vppp=0;
+
+I already knew that this equation was true.
+But perhaps no harm has been done; let's continue.
+
+{begingroup}
+
+clear(TEXT0)->interim(TEXT0):=$
+(TEXT0)<-tracingmacros
+{interim}
+{tracingmacros:=-1}
+{interim}
+{tracingcommands:=-1}
+Runaway text?
+tracingoutput
+! Forbidden token found while scanning a text argument.
+<inserted text>
+ )
+<to be read again>
+ \
+l.129 ...); clear(tracingoutput\
+ ;
+It seems that a right delimiter was left out,
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{restoring proofing=1}
+{restoring tracingoutput=1}
+{restoring tracingcommands=2.1}
+{restoring tracingmacros=1}
+{endgroup}
+{def}
+{def}
+{addto}
+Path at line 139, before choices:
+(0,0){4096,0}
+ ..{4096,0}(1,0){0,-4096}
+ ..{0,4096}cycle
+
+Path at line 139, after choices:
+(0,0)..controls (0.33333,0) and (0.66667,0)
+ ..(1,0)..controls (1,-0.66667) and (0,-0.66667)
+ ..cycle
+
+> 0 ENE 1 (ESE SSE) SSW WSW WNW NNW 0 (NNE)
+! Backwards path (turning number is negative).
+<to be read again>
+ ;
+l.139 ....{0,1}cycle withpen qq;
+
+The path doesn't have a counterclockwise orientation,
+so I'll probably have trouble drawing it.
+(See Chapter 27 of The METAFONTbook for more help.)
+
+Tracing edges at line 139: (weight 1)
+@ Octant ENE (2 offsets), from (2.5,-0.5) to (1.5,-2)
+@ retrograde line from (2.5,-0.5) to (0.5,-2)
+(3,0)(3,-1)(1,-1)
+@ transition line 0, from (0.5,-2) to (0.5,-2)
+@ transition line 1, from (1.5,-2) to (2.5,-1.5)
+@ transition line 2, from (2.5,-1.5) to (3.5,-0.5)
+(1,-2)(3,-2)(3,-1)(4,-1)
+@ retrograde line from (3.5,-0.5) to (1.5,-2)
+(4,0)(4,-1)(2,-1)
+@ Octant ESE (2 offsets), from (1.5,-2) to (-1,0)
+@ retrograde line from (1.5,-2) to (-1,0)
+(2,-2)(1,-2)(1,-1)(0,-1)
+@ transition line 3, from (-1,0) to (0.5,-1.5)
+@ transition line 2, from (0.5,-1.5) to (1.5,-2)
+@ transition line 1, from (1.5,-2) to (1.5,-2)
+(0,0)(0,-1)(1,-1)
+@ retrograde line from (1.5,-2) to (-1,0)
+(1,-2)(1,-1)(0,-1)
+@ Octant SSE (0 offsets), from (-1,0) to (-1,0)
+@ transition line 0, from (-1,0) to (-1,0)
+@ Octant SSW (2 offsets), from (-1,0) to (-0.82407,0.67593)
+@ retrograde line from (-1,0) to (-0.5,1.5)
+(0,0)(-1,0)(-1,1)(0,1)
+@ transition line 3, from (-0.5,1.5) to (-0.5,1.5)
+@ transition line 2, from (-0.5,1.5) to (-1,0)
+@ transition line 1, from (-1.06265,-0.39499) to (-0.56265,1.10501)
+@ transition line 2, from (-0.82407,0.67593) to (-1.32407,-0.82407)
+@ transition line 1, from (-1.32407,-0.82407) to (-1.32407,-0.82407)
+(0,2)(0,1)(-1,1)
+@ retrograde line from (-1.32407,-0.82407) to (-0.82407,0.67593)
+(-1,-1)
+@ Octant WSW (2 offsets), from (-0.82407,0.67593) to (1,2)
+@ retrograde line from (-0.82407,0.67593) to (1.17593,2.17593)
+(-1,1)(0,1)
+@ transition line 0, from (1.17593,2.17593) to (1.17593,2.17593)
+@ transition line 1, from (1.17593,2.17593) to (0.17593,1.67593)
+@ transition line 2, from (0,1.5) to (-1,0.5)
+(0,2)
+@ retrograde line from (-1,0.5) to (1,2)
+(0,1)
+@ Octant WNW (2 offsets), from (1,2) to (3.32407,0.17593)
+@ retrograde line from (1,2) to (3.5,0)
+(0,2)(2,2)(2,1)(3,1)
+@ transition line 3, from (3.5,0) to (2,1.5)
+@ transition line 2, from (1.82407,1.67593) to (0.82407,2.17593)
+@ transition line 1, from (0.82407,2.17593) to (0.82407,2.17593)
+(3,0)(3,1)(2,1)
+@ retrograde line from (0.82407,2.17593) to (3.32407,0.17593)
+(2,2)(2,1)(3,1)
+@ Octant NNW (0 offsets), from (3.32407,0.17593) to (3,1)
+@ transition line 0, from (3.32407,0.17593) to (3.32407,0.17593)
+(3,0)
+@ Octant NNE (2 offsets), from (3,1) to (2.5,-0.5)
+@ retrograde line from (3,1) to (2.5,-0.5)
+(3,1)
+@ transition line 3, from (2.5,-0.5) to (2.5,-0.5)
+@ transition line 2, from (2.5,-0.5) to (3,1)
+@ transition line 1, from (3,1) to (3,1)
+(3,0)
+@ retrograde line from (3,1) to (2.5,-0.5)
+(3,1)(3,0).
+
+
+f(SUFFIX0)(EXPR1)(EXPR2)(TEXT3)->numeric.w;show(EXPR1);addto(SUFFIX0)con
+tour(0,0)..(2,0)..(1,$)..(1,1)..cycle.withpen.qq;addto(SUFFIX0)doublepat
+h(0,0){1,1}..{2,1}(2,1)withpen.qq;addto(SUFFIX0)doublepath(($,$){1,0}..(
+1,1){1,0})scaled0.5withpen.nullpen;cull(SUFFIX0)keeping(4,4)withweight1.
+5;
+! Missing `]' has been inserted.
+<to be read again>
+ ,
+l.140 f(e[3,
+ w); g(e3,transformed p,penoffset-(1,1.3)of(pencir...
+I've seen a `[' and a subscript value, in a suffix,
+so a right bracket should have come next.
+I shall pretend that one was there.
+
+(SUFFIX0)<-e3
+(EXPR1)<-unknown path w
+! Missing argument to f.
+<to be read again>
+ ;
+l.140 f(e[3,w);
+ g(e3,transformed p,penoffset-(1,1.3)of(pencir...
+That macro has more parameters than you thought.
+I'll continue by pretending that each missing argument
+is either zero or null.
+
+(EXPR2)<-0
+! Missing argument to f.
+<to be read again>
+ ;
+l.140 f(e[3,w);
+ g(e3,transformed p,penoffset-(1,1.3)of(pencir...
+That macro has more parameters than you thought.
+I'll continue by pretending that each missing argument
+is either zero or null.
+
+(TEXT3)<-
+{numeric}
+{show}
+>> unknown path %CAPSULE1615
+{addto}
+Path at line 140, before choices:
+(0,0)
+ ..(2,0)
+ ..(1,-1)
+ ..(1,1)
+ ..cycle
+
+Path at line 140, after choices:
+(0,0)..controls (0.56189,-0.5286) and (1.43811,0.5286)
+ ..(2,0)..controls (2.70831,-0.66634) and (1.66634,-1.70831)
+ ..(1,-1)..controls (0.4714,-0.43811) and (1.5286,0.43811)
+ ..(1,1)..controls (0.33366,1.70831) and (-0.70831,0.66634)
+ ..cycle
+
+> 0 ESE ENE ESE 1 SSE SSW WSW WNW NNW 2 NNE NNW 3 WNW WSW SSW SSE ESE 0
+! Strange path (turning number is zero).
+<to be read again>
+ ;
+f->.....(1,1)..cycle.withpen.qq;
+ addto(SUFFIX0)doublepath(0,0)...
+<to be read again>
+ ;
+l.140 f(e[3,w);
+ g(e3,transformed p,penoffset-(1,1.3)of(pencir...
+The path doesn't have a counterclockwise orientation,
+so I'll probably have trouble drawing it.
+(See Chapter 27 of The METAFONTbook for more help.)
+
+Tracing edges at line 140: (weight 1)
+@ Octant ENE (2 offsets), from (0.26321,-2.6526) to (1.73679,-2.34741)
+@ transition line 0, from (0.26321,-2.6526) to (0.26321,-2.6526)
+@ transition line 1, from (1.73679,-2.34741) to (2.73679,-1.84741)
+@ transition line 2, from (2.73679,-1.84741) to (3.73679,-0.84741)
+(1,-3)(1,-2)(3,-2)
+@ retrograde line from (3.73679,-0.84741) to (1.73679,-2.34741)
+(3,-1)
+@ Octant ESE (2 offsets), from (1.73679,-2.34741) to (-0.25465,-0.56029)
+@ retrograde line from (1.73679,-2.34741) to (-0.76321,-0.34741)
+(3,-2)(1,-2)(1,-1)(-1,-1)
+@ transition line 3, from (-0.76321,-0.34741) to (0.73679,-1.84741)
+@ transition line 2, from (0.73679,-1.84741) to (1.73679,-2.34741)
+@ transition line 1, from (2.05333,-2.41772) to (1.05333,-1.91772)
+@ transition line 2, from (1.24535,-2.06029) to (2.24535,-2.56029)
+@ transition line 1, from (2.24535,-2.56029) to (2.24535,-2.56029)
+(-1,0)(-1,-1)(0,-1)(0,-2)(2,-2)
+@ retrograde line from (2.24535,-2.56029) to (-0.25465,-0.56029)
+(2,-3)(2,-2)(1,-2)
+@ Octant SSE (0 offsets), from (-0.25465,-0.56029) to (0,-1.13196)
+@ transition line 0, from (-0.25465,-0.56029) to (-0.25465,-0.56029)
+@ Octant SSW (2 offsets), from (0,-1.13196) to (0.25,-0.25)
+@ retrograde line from (0,-1.13196) to (0.5,0.36804)
+(1,-1)(0,-1)
+@ transition line 3, from (0.5,0.36804) to (0.5,0.36804)
+@ transition line 2, from (0.5,0.36804) to (0,-1.13196)
+@ transition line 1, from (-0.04594,-1.41693) to (0.45406,0.08307)
+@ transition line 2, from (0.25,-0.25) to (-0.25,-1.75)
+@ transition line 1, from (-0.25,-1.75) to (-0.25,-1.75)
+(0,0)
+@ retrograde line from (-0.25,-1.75) to (0.25,-0.25)
+(0,-2)
+@ Octant WSW (2 offsets), from (0.25,-0.25) to (1.63197,1)
+@ retrograde line from (0.25,-0.25) to (2.25,1.25)
+(0,0)(1,0)
+@ transition line 0, from (2.25,1.25) to (2.25,1.25)
+@ transition line 1, from (2.25,1.25) to (1.25,0.75)
+@ transition line 2, from (1.03186,0.59352) to (2.03186,1.09352)
+@ transition line 1, from (1.63197,1) to (0.63197,0.5)
+@ transition line 2, from (0.63197,0.5) to (-0.36803,-0.5)
+(1,1)
+@ retrograde line from (-0.36803,-0.5) to (1.63197,1)
+(1,0)
+@ Octant WNW (2 offsets), from (1.63197,1) to (3.56024,-0.74529)
+@ retrograde line from (1.63197,1) to (4.13197,-1)
+(1,1)(2,1)(2,0)(4,0)
+@ transition line 3, from (4.13197,-1) to (2.63197,0.5)
+@ transition line 2, from (2.63197,0.5) to (1.63197,1)
+@ transition line 1, from (1.29863,1.07983) to (2.29863,0.57983)
+@ transition line 2, from (2.06024,0.75471) to (1.06024,1.25471)
+@ transition line 1, from (1.06024,1.25471) to (1.06024,1.25471)
+(4,-1)(4,0)(3,0)
+@ retrograde line from (1.06024,1.25471) to (3.56024,-0.74529)
+(3,1)(2,1)(2,0)(3,0)
+@ Octant NNW (0 offsets), from (3.56024,-0.74529) to (3.3474,-0.23679)
+@ transition line 0, from (3.56024,-0.74529) to (3.56024,-0.74529)
+(3,-1)
+@ Octant NNE (2 offsets), from (3.3474,-0.23679) to (3.65259,1.23679)
+@ retrograde line from (3.3474,-0.23679) to (2.8474,-1.73679)
+(3,0)
+@ transition line 3, from (2.8474,-1.73679) to (2.8474,-1.73679)
+@ transition line 2, from (2.8474,-1.73679) to (3.3474,-0.23679)
+@ transition line 1, from (3.65259,1.23679) to (3.65259,1.23679)
+(3,-2)
+@ Octant NNW (0 offsets), from (3.65259,1.23679) to (3.43976,1.74529)
+@ transition line 0, from (3.65259,1.23679) to (3.65259,1.23679)
+(3,1)(4,1)
+@ Octant WNW (2 offsets), from (3.43976,1.74529) to (0.36803,4)
+@ transition line 3, from (3.43976,1.74529) to (1.93976,3.24529)
+@ transition line 2, from (1.70137,3.42017) to (0.70137,3.92017)
+@ transition line 1, from (0.36803,4) to (0.36803,4)
+(4,2)(3,2)(3,3)(2,3)
+@ Octant WSW (2 offsets), from (0.36803,4) to (-2.25,2.25)
+@ transition line 0, from (0.36803,4) to (0.36803,4)
+@ transition line 1, from (-0.03186,3.90648) to (-1.03186,3.40648)
+@ transition line 2, from (-1.25,3.25) to (-2.25,2.25)
+(2,4)(-1,4)(-1,3)(-2,3)
+@ Octant SSW (2 offsets), from (-2.25,2.25) to (-3,0.13196)
+@ transition line 3, from (-2.25,2.25) to (-2.25,2.25)
+@ transition line 2, from (-2.45406,1.91695) to (-2.95406,0.41695)
+@ transition line 1, from (-3,0.13196) to (-3,0.13196)
+(-2,2)(-3,2)
+@ Octant SSE (0 offsets), from (-3,0.13196) to (-2.74533,-0.43973)
+@ transition line 0, from (-3,0.13196) to (-3,0.13196)
+@ Octant ESE (2 offsets), from (-2.74533,-0.43973) to (0.26321,-2.6526)
+@ transition line 3, from (-2.74533,-0.43973) to (-1.24533,-1.93973)
+@ transition line 2, from (-1.05331,-2.08229) to (-0.05331,-2.58229)
+@ transition line 1, from (0.26321,-2.6526) to (0.26321,-2.6526)
+(-3,-1)(-2,-1)(-2,-2)(0,-2)(0,-3).
+
+{addto}
+Path at line 140, before choices:
+(0,0){2896.30943,2896.30934}
+ ..{3663.57385,1831.78674}(2,1)
+
+Path at line 140, after choices:
+(0,0)..controls (0.53566,0.53566) and (1.32689,0.66344)
+ ..(2,1)
+
+Tracing edges at line 140: (weight 1)
+@ Octant ENE (2 offsets), from (0,-2.5) to (4,0)
+@ transition line 0, from (0,-2.5) to (0,-2.5)
+@ transition line 1, from (0,-2.5) to (1,-2)
+@ transition line 2, from (1.607,-1.57747) to (0.607,-2.07747)
+@ transition line 1, from (2,-1.5) to (3,-1)
+@ transition line 2, from (3,-1) to (4,0)
+(2,-2)(2,-1)(4,-1)
+@ Octant NNE (2 offsets), from (4,0) to (4.5,1.5)
+@ transition line 3, from (4,0) to (4,0)
+@ transition line 2, from (4,0) to (4.5,1.5)
+@ transition line 1, from (4.5,1.5) to (4.5,1.5)
+(4,1)(5,1)
+@ Octant NNW (0 offsets), from (4.5,1.5) to (4.5,1.5)
+@ transition line 0, from (4.5,1.5) to (4.5,1.5)
+@ Octant WNW (2 offsets), from (4.5,1.5) to (2,3.5)
+@ transition line 3, from (4.5,1.5) to (3,3)
+@ transition line 2, from (3,3) to (2,3.5)
+@ transition line 1, from (2,3.5) to (2,3.5)
+(5,2)(4,2)(4,3)(2,3)
+@ Octant WSW (2 offsets), from (2,3.5) to (-2,1)
+@ transition line 0, from (2,3.5) to (2,3.5)
+@ transition line 1, from (0.607,2.92253) to (-0.393,2.42253)
+@ transition line 2, from (-1,2) to (-2,1)
+(2,4)(2,3)(0,3)(0,2)(-1,2)
+@ Octant SSW (2 offsets), from (-2,1) to (-2.5,-0.5)
+@ transition line 3, from (-2,1) to (-2,1)
+@ transition line 2, from (-2,1) to (-2.5,-0.5)
+@ transition line 1, from (-2.5,-0.5) to (-2.5,-0.5)
+(-1,1)(-2,1)
+@ Octant SSE (0 offsets), from (-2.5,-0.5) to (-2.5,-0.5)
+@ transition line 0, from (-2.5,-0.5) to (-2.5,-0.5)
+@ Octant ESE (2 offsets), from (-2.5,-0.5) to (0,-2.5)
+@ transition line 3, from (-2.5,-0.5) to (-1,-2)
+@ transition line 2, from (-1,-2) to (0,-2.5)
+@ transition line 1, from (0,-2.5) to (0,-2.5)
+(-2,-1)(-1,-1)(-1,-2).
+
+{addto}
+Path at line 140, before choices:
+(-1,-1){4096,0}
+ ..{4096,0}(1,1)
+
+Path at line 140, after choices:
+(-1,-1)..controls (0.10457,-1) and (-0.10457,1)
+ ..(1,1)
+
+{(path)scaled(0.5)}
+{nullpen}
+Tracing edges at line 140: (weight 1)
+(0,0)(0,1)(0,0).
+
+{cull}
+
+g(SUFFIX0)->show(SUFFIX0)
+(SUFFIX0)<-e3
+! Too many arguments to g;
+ Missing `)' has been inserted.
+l.140 f(e[3,w); g(e3,
+ transformed p,penoffset-(1,1.3)of(pencir...
+I'm going to assume that the comma I just read was a
+right delimiter, and then I'll begin expanding the macro.
+You might want to delete some tokens before continuing.
+
+{show}
+{(picture)transformed((8,1,5,-5,-5,5))}
+>> Edge structure at line 140:
+
+{-((1,1.3))}
+{pencircle}
+{(future pen)scaled(20)}
+{-(0.5)}
+{(future pen)yscaled(-0.5)}
+Pen polygon at line 140 (newly created):
+(1.5,-5)
+ .. (4.5,-4.5)
+ .. (6,-4)
+ .. (8,-3)
+ .. (9.5,-1.5)
+ .. (10,-0.5)
+ .. (10,0.5)
+ .. (9.5,1.5)
+ .. (8,3)
+ .. (6,4)
+ .. (4.5,4.5)
+ .. (1.5,5)
+ .. (-1.5,5)
+ .. (-4.5,4.5)
+ .. (-6,4)
+ .. (-8,3)
+ .. (-9.5,1.5)
+ .. (-10,0.5)
+ .. (-10,-0.5)
+ .. (-9.5,-1.5)
+ .. (-8,-3)
+ .. (-6,-4)
+ .. (-4.5,-4.5)
+ .. (-1.5,-5)
+ .. cycle
+
+{((-1,-1.3))penoffset(pen)}
+>> (-9.5,1.5)
+Path at line 141, before choices:
+(0,0)..controls (1,1) and (-1,1)
+ ..(3,0)
+
+Path at line 141, after choices:
+(0,0)..controls (1,1) and (-1,1)
+ ..(3,0)
+
+{((0,1))directiontime(path)}
+>> 0.33333
+{(3.14159)point(path)}
+{((3.14159,4))subpath(path)}
+{((7.68668,2.72437))intersectiontimes(path)}
+>> (0,0)
+Path at line 143, before choices:
+(-1,1.1){curl 1}
+ ..{curl 1}(1,-1)
+
+Path at line 143, after choices:
+(-1,1.1)..controls (-0.33333,0.40001) and (0.33333,-0.3)
+ ..(1,-1)
+
+{(-1)precontrol((0,0))}
+{(path)intersectiontimes((0,0))}
+>> (-1,-1)
+{addto}
+{-(4094.99998)}
+{-(0.00002)}
+Path at line 144, before choices:
+(-4094.99998,0){curl 1}
+ ..{curl 1}(4094.99998,-0.00002)
+
+Path at line 144, after choices:
+(-4094.99998,0)..controls (-1365,0) and (1365,-0.00002)
+ ..(4094.99998,-0.00002)
+
+{pencircle}
+{(0)penoffset(future pen)}
+Pen polygon at line 145 (newly created):
+(0.5,0)
+ .. (0,0.5)
+ .. (-0.5,0)
+ .. (0,-0.5)
+ .. cycle
+
+>> 0
+>> pen
+! Not implemented: penoffset(known numeric)of(pen).
+<to be read again>
+ ;
+l.145 pencircle;
+ addto e3 also e3 shifted (0,257); ,"flushed ...
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+Tracing edges at line 145: (weight 1)
+@ Octant ESE (1 offset), from (-4095.49998,0) to (4094.99998,-0.50002)
+@ transition line 2, from (-4095.49998,0) to (-4094.99998,-0.5)
+@ transition line 1, from (4094.99998,-0.50002) to (4094.99998,-0.50002)
+(-4095,0)
+@ Octant ENE (1 offset), from (4094.99998,-0.50002) to (4095.49998,-0.00
+002)
+@ transition line 0, from (4094.99998,-0.50002) to (4094.99998,-0.50002)
+@ transition line 1, from (4094.99998,-0.50002) to (4095.49998,-0.00002)
+(-4095,-1)(4095,-1)
+@ Octant NNE (0 offsets), from (4095.49998,-0.00002) to (4095.49998,-0.0
+0002)
+@ transition line 1, from (4095.49998,-0.00002) to (4095.49998,-0.00002)
+@ Octant NNW (0 offsets), from (4095.49998,-0.00002) to (4095.49998,-0.0
+0002)
+@ transition line 0, from (4095.49998,-0.00002) to (4095.49998,-0.00002)
+@ Octant WNW (1 offset), from (4095.49998,-0.00002) to (-4094.99998,0.5)
+@ transition line 2, from (4095.49998,-0.00002) to (4094.99998,0.49998)
+@ transition line 1, from (-4094.99998,0.5) to (-4094.99998,0.5)
+(4095,0)(-4095,0)
+@ Octant WSW (1 offset), from (-4094.99998,0.5) to (-4095.49998,0)
+@ transition line 0, from (-4094.99998,0.5) to (-4094.99998,0.5)
+@ transition line 1, from (-4094.99998,0.5) to (-4095.49998,0)
+(-4095,1)
+@ Octant SSW (0 offsets), from (-4095.49998,0) to (-4095.49998,0)
+@ transition line 1, from (-4095.49998,0) to (-4095.49998,0)
+@ Octant SSE (0 offsets), from (-4095.49998,0) to (-4095.49998,0)
+@ transition line 0, from (-4095.49998,0) to (-4095.49998,0)
+(-4095,0).
+
+{addto}
+{(picture)shifted((0,257))}
+! A statement can't begin with `,'.
+<to be read again>
+ ,
+l.145 ...o e3 shifted (0,257); ,
+ "flushed with pride"; numeric...
+I was looking for the beginning of a new statement.
+If you just proceed without changing anything, I'll ignore
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+! Extra tokens will be flushed.
+<to be read again>
+ ,
+l.145 ...o e3 shifted (0,257); ,
+ "flushed with pride"; numeric...
+I've just read as much of that statement as I could fathom,
+so a semicolon should have been next. It's very puzzling...
+but I'll try to get myself back together, by ignoring
+everything up to the next `;'. Please insert a semicolon
+now in front of anything that you don't want me to delete.
+(See Chapter 27 of The METAFONTbook for an example.)
+
+{numeric}
+{def}
+
+f(EXPR0)(EXPR1)(EXPR2)->showdependencies;tracingcapsules:=1;showdependen
+cies;show1/3(3,6)*(((EXPR0)+(EXPR1))+((EXPR1)-(EXPR0))),(1,1)/sqrt2zscal
+ed((EXPR0)+1,(EXPR0)+2)-((EXPR0)+1,(EXPR0)+2)rotated45,(0,1)zscaled(1,(E
+XPR1)+2)-(1,(EXPR1)+2)rotated90
+{(xx)+(1)}
+{(xx+1)/(0.3)}
+(EXPR0)<-3.3333xx+3.3333
+{(yy)-(1)}
+{(yy-1)/(0.5)}
+(EXPR1)<-2yy-2
+(EXPR2)<-(xx,0)
+{showdependencies}
+alpha=0.45p3~+7
+{tracingcapsules:=1}
+{showdependencies}
+xpart %CAPSULE1359=xx
+%CAPSULE1367=2yy-2
+%CAPSULE1375 = 3.3333xx+3.3333
+alpha=0.45p3~+7
+{show}
+{(1/3)*((3,6))}
+{(3.3333xx+3.3333)+(2yy-2)}
+{(2yy-2)-(3.3333xx+3.3333)}
+{(linearform)+(linearform)}
+{((1,2))*(4yy-4)}
+>> (4yy-4,8yy-8)
+{sqrt(2)}
+{((1,1))/(1.41422)}
+{(3.3333xx+3.3333)+(1)}
+{(3.3333xx+3.3333)+(2)}
+{((0.7071,0.7071))zscaled((3.3333xx+4.3333,3.3333xx+5.3333))}
+{(3.3333xx+3.3333)+(1)}
+{(3.3333xx+3.3333)+(2)}
+{((3.3333xx+4.3333,3.3333xx+5.3333))rotated(45)}
+{((-0.7071,4.71399xx+6.83531))-((-0.7071,4.71399xx+6.83531))}
+>> (0,0)
+{(2yy-2)+(2)}
+{((0,1))zscaled((1,2yy))}
+{(2yy-2)+(2)}
+{((1,2yy))rotated(90)}
+{((-2yy,1))-((-2yy,1))}
+>> (0,0)
+{(1000)*(o3)}
+{(o1)-(o2)}
+{(4000)*(linearform)}
+{(1000o3)-(linearform)}
+{(4000)*(o2)}
+{(linearform)+(4000o2)}
+{(linearform)+(9)}
+{(0.01)*(o3)}
+{-(0.01o3)}
+{(3)*(ooo)}
+{(-0.01o3)+(3ooo)}
+{begingroup}
+{(o2)+(o4)}
+{(linearform)+(o5)}
+{(linearform)-(20)}
+{(9/10)*(linearform)}
+{(oo)=(linearform)}
+## oo=0.9o5+0.9o4+0.9o2-18
+{(0.5)*(o2)}
+{(o1)+(0.5o2)}
+{(8/9)*(linearform)}
+{(o5)=(linearform)}
+## o5=0.44444o2+0.88889o1
+{(o4)=(linearform)}
+## o4=0.44444o2+0.88889o1
+{(0.0001)*(o2)}
+{-(0.0001o2)}
+{(o6)=(-0.0001o2)}
+## o6=-0.0001o2
+{showdependencies}
+o6=-0.0001o2
+o4=0.44444o2+0.88889o1
+o5=0.44444o2+0.88889o1
+oo=1.7o2+1.6o1-18
+%CAPSULE382 = 3ooo-0.01o3
+xpart %CAPSULE1049 = 8000o2-4000o1+1000o3+9
+xpart %CAPSULE1383=xpart '
+ypart %CAPSULE1383=ypart '
+alpha=0.45p3~+7
+{numeric}
+### 4000o1 = -xpart %CAPSULE1049+8000o2+1000o3+9
+### -4.87383o2 = -oo-0.0004xpart %CAPSULE1049+0.39673o3-17.99643
+#### o6=-0.00027
+### -0.04366o3 = -o4+0.46689oo+8.40439
+### -o4 = -o5
+### 0.22894o5 = -%CAPSULE382+0.10689oo+3ooo+1.92412
+{begingroup}
+{pair}
+### -alfa=-xpart %CAPSULE1494
+{endgroup}
+{xpart((xpart %CAPSULE1494,0))}
+### -xpart %CAPSULE17=-%CAPSULE1176
+{endgroup}
+{(%CAPSULE382)+(%CAPSULE1176)}
+### -%CAPSULE382=-%CAPSULE1893+%CAPSULE1176
+### -%CAPSULE1893=-ypart %CAPSULE1049
+{begingroup}
+{save}
+{(1)-(p$)}
+{restoring p}
+### p$=-ypart %CAPSULE604+1
+{endgroup}
+{(2/3)*((-ypart %CAPSULE604+1,ypart %CAPSULE604))}
+### ypart %CAPSULE604=-xpart %CAPSULE604+1
+### -0.66667xpart %CAPSULE604=-xpart %CAPSULE1889
+{-((xpart %CAPSULE1889,-xpart %CAPSULE1889+0.66667))}
+### xpart %CAPSULE1889=-xpart %CAPSULE1172
+{((xpart %CAPSULE1049,ypart %CAPSULE1049))=((xpart %CAPSULE1172,-xpart %
+CAPSULE1172-0.66667))}
+## xpart %CAPSULE1172=-ypart %CAPSULE1049-0.66667
+## ypart %CAPSULE1049=-xpart %CAPSULE1049-0.66667
+### -xpart %CAPSULE1049=-xpart %CAPSULE1172
+{((xpart ',ypart '))=((xpart %CAPSULE1172,-xpart %CAPSULE1172-0.66667))}
+## xpart %CAPSULE1172=-ypart '-0.66667
+## xpart '=-ypart '-0.66667
+{begingroup}
+{(0.5)*(ooo)}
+{begingroup}
+{numeric}
+### -ooo=-%CAPSULE1494
+{endgroup}
+{(%CAPSULE1494)+(1)}
+### -%CAPSULE1494=-%CAPSULE1350+1
+{(1/2)*(%CAPSULE1350)}
+### -0.5%CAPSULE1350=-%CAPSULE1367
+{-(%CAPSULE1367)}
+### %CAPSULE1367=-%CAPSULE1893
+{(2)*(%CAPSULE1893)}
+### -2%CAPSULE1893=-%CAPSULE1885
+{(-0.5%CAPSULE1885-0.5)=(%CAPSULE1885)}
+## %CAPSULE1885=-0.33333
+{(oo)=(-0.33333)}
+## oo=-0.33333
+{endgroup}
+{(4000)*(-0.33333)}
+{yoffset:=-1333.31299}
+{xoffset:=-1333.31299}
+{for}
+{sqrt(-1)}
+! Square root of -1 has been replaced by 0.
+l.153 for @=angle(sqrt$,
+ mlog$):charext:=uniformdeviate$;charh...
+Since I don't take square roots of negative numbers,
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{mlog(-1)}
+! Logarithm of -1 has been replaced by 0.
+l.153 for @=angle(sqrt$,mlog$)
+ :charext:=uniformdeviate$;charh...
+Since I don't take logs of non-positive numbers,
+I'm zeroing this one. Proceed, with fingers crossed.
+
+{angle((0,0))}
+! angle(0,0) is taken as zero.
+l.153 for @=angle(sqrt$,mlog$):
+ charext:=uniformdeviate$;charh...
+The `angle' between two identical points is undefined.
+I'm zeroing this one. Proceed, with fingers crossed.
+
+Runaway loop?
+charext:=uniformdeviate$;charht:=2048;granularity:=-8; ETC.
+! Forbidden token found while scanning the text of a for loop.
+<inserted text>
+ endfor
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+I suspect you have forgotten an `endfor',
+causing me to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+{loop value=0}
+{uniformdeviate(-1)}
+{charext:=-0.00507}
+{charht:=2048}
+{-(8)}
+{granularity:=-8}
+{addto}
+{-(100)}
+{-(99)}
+Path at line 163, before choices:
+(0,-100)..tension 500
+ ..(100,-99)..tension 3000
+ ..cycle
+
+Path at line 163, after choices:
+(0,-100)..controls (-36.92659,-103.51175) and (136.98943,-101.7726)
+ ..(100,-99)..controls (99.9889,-98.99916) and (0.01108,-99.99895)
+ ..cycle
+
+Tracing edges at line 163: (weight 1)
+(-3,-101)(-3,-102)(71,-102)(71,-101)(99,-101)(99,-100)(103,-100)
+(103,-99)(48,-99)(48,-100)(-7,-100)(-7,-101).
+
+{tracingoutput:=0}
+{shipout}
+! Enormous charht has been reduced.
+<to be read again>
+ ;
+<for(0)> ...=(EXPR0);shipout.e3;
+ special"bye";interim.char99="...
+<inserted text> endfor
+
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+Font metric dimensions must be less than 2048pt.
+
+[109]
+{special}
+{interim}
+! The token `char' isn't an internal quantity.
+<to be read again>
+ char
+<for(0)> ...al"bye";interim.char
+ 99="c";true=false;[[clear(tra...
+<inserted text> endfor
+
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+Something like `tracingonline' should follow `interim'.
+
+{char(99)}
+{("c")=("c")}
+! Redundant equation.
+<to be read again>
+ ;
+<for(0)> ...;interim.char99="c";
+ true=false;[[clear(tracingcom...
+<inserted text> endfor
+
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+I already knew that this equation was true.
+But perhaps no harm has been done; let's continue.
+
+{true}
+{false}
+{(true)=(false)}
+! Inconsistent equation.
+<to be read again>
+ ;
+<for(0)> ...ar99="c";true=false;
+ [[clear(tracingcommands);char...
+<inserted text> endfor
+
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+The equation I just read contradicts what was said before.
+But don't worry; continue and I'll just ignore it.
+
+{begingroup}
+
+clear(TEXT0)->interim(TEXT0):=$
+(TEXT0)<-tracingcommands
+{interim}
+{tracingcommands:=-1}
+! Enormous charht has been reduced.
+<to be read again>
+ ;
+<for(0)> ...shipout+nullpicture;
+ "careful"for.for=(EXPR0)step2...
+<inserted text> endfor
+
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+Font metric dimensions must be less than 2048pt.
+
+[0]
+careful METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT M
+ETAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT M
+ETAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT M
+ETAFONT METAFONT METAFONT METAFONT
+{restoring tracingcommands=2.1}
+{endgroup}
+{scrollmode}
+
+{char(31)}
+{("hello again")&("^^_")}
+hello again^^_
+{save}
+### -0.45p3~=-alpha+7
+{-(0.043)}
+{fillin:=-0.043}
+{def}
+{begingroup}
+{tracingspecs:=1}
+{show}
+{nullpen}
+
+>> Pen polygon at line 163:
+(0,0)
+ .. cycle
+
+{makepath(pen)}
+
+>> Path at line 163:
+(1,-2)..controls (1,-2) and (2,-1)
+ ..(2,-1)..controls (2,-1) and (2.5,0.5)
+ ..(2.5,0.5)..controls (2.5,0.5) and (1,2)
+ ..(1,2)..controls (1,2) and (0,2.5)
+ ..(0,2.5)..controls (0,2.5) and (-1,2)
+ ..(-1,2)..controls (-1,2) and (-2,1)
+ ..(-2,1)..controls (-2,1) and (-2.5,-0.5)
+ ..(-2.5,-0.5)..controls (-2.5,-0.5) and (-1,-2)
+ ..(-1,-2)..controls (-1,-2) and (0,-2.5)
+ ..(0,-2.5)..controls (0,-2.5) and (1,-2)
+ ..cycle
+
+{(pen)rotated(1)}
+Pen polygon at line 163 (newly created):
+(0.97366,1.51723)
+ .. cycle
+
+{makepath(pen)}
+
+>> Path at line 163:
+(0.97366,1.51723)..controls (0.97366,1.51723) and (0.97366,1.51723)
+ ..cycle
+
+{pencircle}
+{makepath(future pen)}
+Pen polygon at line 163 (newly created):
+(0.5,-0.5)
+ .. (0.5,0.5)
+ .. (-0.5,0.5)
+ .. (-0.5,-0.5)
+ .. cycle
+
+{(path)rotated(-1)}
+
+>> Path at line 163:
+(0.4912,-0.50865)..controls (0.4912,-0.50865) and (0.50865,0.4912)
+ ..(0.50865,0.4912)..controls (0.50865,0.4912) and (-0.4912,0.50865)
+ ..(-0.4912,0.50865)..controls (-0.4912,0.50865) and (-0.50865,-0.4912)
+ ..(-0.50865,-0.4912)..controls (-0.50865,-0.4912) and (0.4912,-0.50865)
+ ..cycle
+
+{addto}
+Path at line 163, before choices:
+(0,2){0,-4096}
+ ..{0,-4096}(0,1){0,-4096}
+ ..{4096,0}(3,0){4096,0}
+ ..{4096,0}(4,0){4096,0}
+ ..{0,-4096}cycle
+
+Path at line 163, after choices:
+(0,2)..controls (0,1.66667) and (0,1.33333)
+ ..(0,1)..controls (0,-0.20886) and (1.63324,0)
+ ..(3,0)..controls (3.33333,0) and (3.66667,0)
+ ..(4,0)..controls (13.63031,0) and (0,10.64238)
+ ..cycle
+
+Path at line 163, before choices:
+(0,0)
+ ..(5,2.9)
+ ..(4,3)
+ ..cycle
+
+Path at line 163, after choices:
+(0,0)..controls (0.9363,-8.0181) and (11.81119,0.07109)
+ ..(5,2.9)..controls (4.68596,3.03043) and (4.33879,3.02028)
+ ..(4,3)..controls (1.86353,2.87216) and (-0.21431,1.83528)
+ ..cycle
+
+{makepen(path)}
+Pen polygon at line 163 (newly created):
+(5,2.9)
+ .. (4,3)
+ .. (0,0)
+ .. cycle
+
+Path at line 163, before subdivision into octants:
+(0,2)..controls (0,1.66667) and (0,1.33333)
+ ..(0,1)..controls (0,-0.20886) and (1.63324,0)
+ ..(3,0)..controls (3.33333,0) and (3.66667,0)
+ ..(4,0)..controls (13.63031,0) and (0,10.64238)
+ ..cycle
+
+Cycle spec at line 163, after subdivision and double autorounding:
+(1.5,0.00272) % beginning in octant `SSE'
+ ..controls (1.5,-0.49254) and (1.63411,-0.82727)
+ ..(1.8592,-1.05237) % segment 1
+% entering octant `ESE'
+ ..controls (2.23936,-1.43253) and (2.87909,-1.5)
+ ..(3.57066,-1.5) % segment 1
+% entering octant `ENE'
+ ..controls (3.95114,-1.5) and (4.3473,-1.47957)
+ ..(4.72458,-1.47957) % segment 1
+ ..controls (5.08287,-1.47957) and (5.44116,-1.47957)
+ ..(5.79944,-1.47957) % segment 2
+ ..controls (7.35368,-1.47957) and (8.34424,-1.12393)
+ ..(8.90538,-0.56279) % segment 3
+% entering octant `NNE'
+ ..controls (9.31982,-0.14835) and (9.5,0.37819)
+ ..(9.5,0.95631) % segment 3
+% entering octant `NNW'
+ ..controls (9.5,2.00116) and (8.61821,3.4318)
+ ..(7.41951,4.6305) % segment 3
+% entering octant `WNW'
+ ..controls (6.32909,5.72092) and (5.11603,6.5)
+ ..(4.2104,6.5) % segment 3
+% entering octant `WSW'
+ ..controls (3.64668,6.5) and (3.12122,6.31134)
+ ..(2.68588,5.876) % segment 3
+% entering octant `SSW'
+ ..controls (1.97163,5.16176) and (1.5,3.78351)
+ ..(1.5,1.485) % segment 3
+ ..controls (1.5,0.99092) and (1.5,0.49681)
+ ..(1.5,0.00272) % segment 0
+ & cycle
+
+Tracing edges at line 163: (weight 1)
+@ Octant SSE (0 offsets), from (1.5,0.00272) to (1.8592,-1.05237)
+@ transition line 0, from (1.5,0.00272) to (1.5,0.00272)
+(2,0)
+@ Octant ESE (0 offsets), from (1.8592,-1.05237) to (3.57066,-1.5)
+@ transition line 1, from (3.57066,-1.5) to (3.57066,-1.5)
+@ Octant ENE (2 offsets), from (3.57066,-1.5) to (13.90538,2.3372)
+@ transition line 0, from (3.57066,-1.5) to (3.57066,-1.5)
+@ transition line 1, from (8.41534,-0.93503) to (13.41534,1.96497)
+@ transition line 2, from (13.90538,2.3372) to (13.90538,2.3372)
+(2,-1)(9,-1)(9,0)(11,0)(11,1)(13,1)
+@ Octant NNE (0 offsets), from (13.90538,2.3372) to (14.5,3.85631)
+@ transition line 1, from (14.5,3.85631) to (14.5,3.85631)
+(13,2)(14,2)
+@ Octant NNW (0 offsets), from (14.5,3.85631) to (12.41951,7.53049)
+@ transition line 0, from (14.5,3.85631) to (14.5,3.85631)
+(14,6)(13,6)(13,7)(12,7)
+@ Octant WNW (2 offsets), from (12.41951,7.53049) to (8.2104,9.5)
+@ transition line 3, from (12.41951,7.53049) to (12.41951,7.53049)
+@ transition line 2, from (9.38113,9.39124) to (8.38113,9.49124)
+@ transition line 1, from (8.2104,9.5) to (8.2104,9.5)
+(12,8)(11,8)(11,9)(8,9)
+@ Octant WSW (2 offsets), from (8.2104,9.5) to (2.68588,5.876)
+@ transition line 0, from (8.2104,9.5) to (8.2104,9.5)
+@ transition line 1, from (6.92668,9.08539) to (2.92668,6.08539)
+@ transition line 2, from (2.68588,5.876) to (2.68588,5.876)
+(8,10)(8,9)(6,9)(6,8)(5,8)(5,7)(3,7)
+@ Octant SSW (0 offsets), from (2.68588,5.876) to (1.5,0.00272)
+@ transition line 1, from (1.5,0.00272) to (1.5,0.00272)
+(3,6)(2,6)(2,0).
+
+Path at line 163, before subdivision into octants:
+(0,2)..controls (0,10.64238) and (13.63031,0)
+ ..(4,0)..controls (3.66667,0) and (3.33333,0)
+ ..(3,0)..controls (1.63324,0) and (0,-0.20886)
+ ..(0,1)..controls (0,1.33333) and (0,1.66667)
+ ..cycle
+
+Cycle spec at line 163, after subdivision and double autorounding:
+(2.68588,5.876) % beginning in octant `ENE'
+ ..controls (3.12122,6.31134) and (3.64668,6.5)
+ ..(4.2104,6.5) % segment 0
+% entering octant `ESE'
+ ..controls (5.11603,6.5) and (6.32909,5.72092)
+ ..(7.41953,4.63048) % segment 0
+% entering octant `SSE'
+ ..controls (8.61824,3.43176) and (9.5,2.00116)
+ ..(9.5,0.95631) % segment 0
+% entering octant `SSW'
+ ..controls (9.5,0.37819) and (9.31982,-0.14835)
+ ..(8.90538,-0.56279) % segment 0
+% entering octant `WSW'
+ ..controls (8.34425,-1.12392) and (7.35368,-1.47957)
+ ..(5.79944,-1.47957) % segment 0
+ ..controls (5.44116,-1.47957) and (5.08287,-1.47957)
+ ..(4.72458,-1.47957) % segment 1
+ ..controls (4.3473,-1.47957) and (3.95114,-1.5)
+ ..(3.57066,-1.5) % segment 2
+% entering octant `WNW'
+ ..controls (2.87909,-1.5) and (2.23936,-1.43253)
+ ..(1.85919,-1.05235) % segment 2
+% entering octant `NNW'
+ ..controls (1.6341,-0.82726) and (1.5,-0.49254)
+ ..(1.5,0.00272) % segment 2
+% entering octant `NNE'
+ ..controls (1.5,0.49681) and (1.5,0.99092)
+ ..(1.5,1.485) % segment 3
+ ..controls (1.5,3.78351) and (1.97165,5.16177)
+ ..(2.68588,5.876) % segment 0
+ & cycle
+
+Tracing edges at line 163: (weight 1)
+@ Octant ENE (2 offsets), from (7.68588,8.776) to (4.2104,6.5)
+@ retrograde line from (7.68588,8.776) to (2.68588,5.876)
+(7,9)(7,8)(5,8)(5,7)(4,7)
+@ transition line 0, from (2.68588,5.876) to (2.68588,5.876)
+@ transition line 1, from (2.68588,5.876) to (7.68588,8.776)
+@ transition line 2, from (8.13123,9.12088) to (3.13123,6.22089)
+@ transition line 1, from (4.2104,6.5) to (9.2104,9.4)
+@ transition line 2, from (9.2104,9.4) to (9.2104,9.4)
+(4,6)(4,7)(6,7)(6,8)(8,8)
+@ retrograde line from (9.2104,9.4) to (4.2104,6.5)
+(8,9)(8,8)(6,8)
+@ Octant ESE (0 offsets), from (4.2104,6.5) to (7.41953,4.63048)
+@ transition line 1, from (7.41953,4.63048) to (7.41953,4.63048)
+(6,7)(4,7)(4,6)(6,6)
+@ Octant SSE (0 offsets), from (7.41953,4.63048) to (9.5,0.95631)
+@ transition line 0, from (7.41953,4.63048) to (7.41953,4.63048)
+(6,5)(8,5)(8,3)(9,3)
+@ Octant SSW (0 offsets), from (9.5,0.95631) to (8.90538,-0.56279)
+@ transition line 1, from (8.90538,-0.56279) to (8.90538,-0.56279)
+@ Octant WSW (2 offsets), from (8.90538,-0.56279) to (7.57066,1.5)
+@ retrograde line from (8.90538,-0.56279) to (12.90538,2.43721)
+(9,-1)(9,0)(10,0)(10,1)(12,1)
+@ transition line 0, from (12.90538,2.43721) to (12.90538,2.43721)
+@ transition line 1, from (12.90538,2.43721) to (8.90538,-0.56279)
+@ transition line 2, from (8.6665,-0.76936) to (12.6665,2.23064)
+@ transition line 1, from (7.57066,1.5) to (3.57066,-1.5)
+@ transition line 2, from (3.57066,-1.5) to (3.57066,-1.5)
+(12,2)(8,2)(8,1)(6,1)(6,0)(5,0)
+@ retrograde line from (3.57066,-1.5) to (7.57066,1.5)
+(5,-1)(5,0)(6,0)(6,1)(8,1)
+@ Octant WNW (2 offsets), from (7.57066,1.5) to (6.85919,1.84764)
+@ retrograde line from (7.57066,1.5) to (8.57066,1.4)
+(8,2)
+@ transition line 3, from (8.57066,1.4) to (8.57066,1.4)
+@ transition line 2, from (8.57066,1.4) to (7.57066,1.5)
+@ transition line 1, from (6.95764,1.52585) to (7.95764,1.42584)
+@ transition line 2, from (6.85919,1.84764) to (5.85919,1.94765)
+@ transition line 1, from (5.85919,1.94765) to (5.85919,1.94765)
+(8,1)
+@ retrograde line from (5.85919,1.94765) to (6.85919,1.84764)
+@ Octant NNW (0 offsets), from (6.85919,1.84764) to (6.5,2.90271)
+@ transition line 0, from (6.85919,1.84764) to (6.85919,1.84764)
+(8,2)(7,2)
+@ Octant NNE (0 offsets), from (6.5,2.90271) to (7.68588,8.776)
+@ transition line 1, from (7.68588,8.776) to (7.68588,8.776)
+(7,9).
+
+{tracingonline:=1}
+
+f<expr>->let)=];let[=(;show._
+(EXPR0)<-xx
+{let}
+{let}
+{show}
+>> _1
+{showdependencies}
+xpart '=-ypart '-0.66667
+{qq:=pen}
+{showstats}
+Memory usage 1462&588 (104 still untouched)
+String usage 41&161 (815&7634 still untouched)
+{[repeat the loop]}
+! A group begun on line 163 never ended.
+<to be read again>
+ endtext
+l.163 ... showstats; bye endtext
+
+I saw a `begingroup' back there that hasn't been matched
+by `endgroup'. So I've inserted `endgroup' now.
+
+{endgroup}
+ )
+(end occurred when else on line 93 was incomplete)
+(end occurred when if on line 36 was incomplete)
+(end occurred when elseif on line 21 was incomplete)
+Here is how much of METAFONT's memory you used:
+ 68 strings out of 883
+ 3752 string characters out of 11386
+ 2897 words of memory out of 3001
+ 289 symbolic tokens out of 2100
+ 8i,43n,14r,8p,167b stack positions out of 30i,100n,300r,150p,500b
+
+(illegal design size has been changed to 128pt)
+(some chardp values had to be adjusted by as much as 0.5pt)
+(local label 0:: was missing)
+(local label 5:: was missing)
+(4 font metric dimensions had to be decreased)
+(You used 3w,2h,16d,2i,312l,301k,1e,10p metric file positions
+ out of 256w,16h,16d,64i,5000l,500k,256e,50p)
+
+Font metrics written on trap.tfm.
+Output written on trap.72270gf (5 characters, 1028 bytes).
diff --git a/systems/knuth/dist/mf/trap.mf b/systems/knuth/dist/mf/trap.mf
new file mode 100644
index 0000000000..adf4d7bd48
--- /dev/null
+++ b/systems/knuth/dist/mf/trap.mf
@@ -0,0 +1,170 @@
+% This is a diabolical test file for MF84. Don't get stuck.
+if not known inimf: .inimf.=.0. % next lines are skipped if format loaded
+ inimf; nonstopmode; tracingtitles:=day; showstopping:=year; hppp:=1000;
+ << == >> ::: || `` '' -- !! ?? ## && @@ $$ [[ ]] {{ }} . (( 5.5.5 )) ++ "..";
+ begingroup save =; let=,; save,; newinternal $=,; let ):=, endgroup;
+ let year=month; showvariable errorstopmode,readstring,2,"2",,,(,),<<,year;
+ tracingrestores:=tracingcommands:=.00000762939453125; % that's 2^(-17)
+ if tracingcommands>0:tracingcommands:=if not cycle "":1.1 forever;fi;
+ tracingcommands:=2.1 exitif tracingcommands>2 endfor; showtoken |=:|>;
+ tracingedges:=1/.00001; tracingequations:=$+1; p~=tracingedges+.00001;
+ interim tracingspecs:=tracingpens:=tracingchoices:=tracingstats:=
+ warningcheck:=tracingoutput:=tracingmacros:=1; $:=ASCII""; $:=x; p~:=p~;
+ delimiters (); delimiters begintext endtext; vardef foo(text t)=t enddef;
+ def lig(text t,|)=ligtable0::for *=1step1until60:0kern boundarychar+*,endfor
+ skipto0;ligtable t:t|0,skipto255;boundarychar:=boundarychar+51.29999enddef;
+ foo begintext interim proofing:=(-.5; shipout nullpicture; special"3" endtext;
+ for n=tracingpens step 1 until proofing:fi endfor showstats; let!!=skipto;
+ path p~; p~=(0,0)..controls (15,4) and (-15,-12)..(4,0); everyjob /*\;;
+ vardef /*\`'@#=message @ & str#@ &jobname&char ASCII'`&str@#!enddef;
+ let next=dump; vardef `'='` enddef; def '`="\*/" enddef;
+elseif known"": `'pass[2.]; outer\; let next=\; delimiters ^~7! fi
+next\; % the second pass will now compute silently; the first pass will halt
+batchmode; ^~7,endgroup pausing:=1; exitif p exitif boolean pen pencircle endfor
+scantokens begingroup message char0&"watch this"; "pair p[],';"&char-1endgroup
+path p[][]p,w,qw; qw=(1,-2)..(2,-1)..(2.5,0.5)..(1,2)..(turningnumber',2.5);
+numeric p[][]; p[[ [$] ]]=10000000000000000; "this string constant is incomplete
+string foo[]p,p~if true:[]; p~000=char34&char200&char34;
+boolean p[]~,boolean fi.boolean; showvariable boolean; def\\= =end enddef;
+picture e[]e[], e[], p~[]~[][]; show length scantokens p~0;
+pen p~[]~,q["a",qq; p~1~=q=pencircle scaled mexp(-3016.57654);
+transform p,pp0; if p=p:qq=makepen((1,0)..cycle) xscaled hex "1000";fi
+qq:=makepen((0,0)..(1,0)..(0,1)..(0,0)..(1,0)..(0,1)..cycle);
+vardef p[][]p~ begintext suffix a,b endtext())suffix@=show #@; p.a.b() enddef;
+expandafter let\endtext\\; outer endtext,\,;;qq:=pencircle scaled 4.5 yscaled 2;
+(6,12)-p7=(0,1)transformed p=(2/(x-x),3/0)transformed p;
+p1\2p=p007=begintext if string p~[$]: p.1.2-p.1.199999,1 endtext transformed p;
+showstopping:=0;showvariable p; p=p; let [[=begingroup; let ]]=endgroup;
+(xxpart p+.002,yxpart p)=1[p1,p2]=(5,y+.00001)=(5,y)=(yypart p,xypart p);
+reverse(p~..cycle) transformed p=p2. 3.p;
+p[000000000001]2p~(,[2]3p~,-)=p~1~2[pausing];
+vardef p~[]@# tertiary t:=if p@ @=@ @p fi; vardef p[][]p~[]=BAD; inner ;;
+show p~[-2]~[3000,x]++4000>path p3; showvariable p,P;
+numeric p[]~; p3~=2alpha; p[1/$]~=3beta;
+begingroup save p; showvariable p; 3beta=1]]; showvariable p;
+def//expr;=enddef;def!primary!false):!fi enddef;
+def _aa__ secondary _a_=if(true enddef; qq:=makepen(qw..(qw scaled$)..cycle);
+primarydef _**__=[[show _*__]] enddef;
+secondarydef _***__=expandafter __ scantokens"**oct"_ enddef;
+// //pencircle slanted length p~**makepen reverse subpath p7-p2 of
+ (p7{p2}..controls _aa__ not odd.1(15) and known p or !p2and-p2..{1,1}(-p2
+ {curl- +1)..tension atleast1..cycle sqrt2++sqrt2***[[]];
+[[interim proofing:=charcode:=-20.5;chardp:=-2048;shipout nullpicture]];
+if charexists -275.50002>known p0 0p=known p~: randomseed charcode; fi
+randomseed:="goof"; a[($,18++1+-+18),(2,3)]=b[(3,2),(1,$);
+show (^+1,~+2) slanted-1 yscaled-2 zscaled-(3,4), p transformed(pp xscaled 9),
+ pp shifted (1,2) transformed(p transformed p), -_[0][1,2]; show
+floor sind mlog sqrt mexp200cosd angle(normaldeviate,uniformdeviate-chardp);
+string s[]; s1=s2=s4; s3=s5; s1=s2; if s1<=s4<>(s1<>s3):show[[char34=s2:=s3]]fi;
+substring penoffset point.1of.p~of[[pencircle]]rotated1080/2/1/3of decimal
+ directiontime postcontrol-1.5of(p~&cycle)-precontrol1/2of p~(p~)=s1:=s4:=s4;
+path p~[]; p~1=p2{length" "}&cycle; p~1=p2=p~0; p2..controls-p2..cycle=p~2;(p7
+..tension1.2..p~[length p~2]..p~2&{0,1}p2..tension1and atleast1..cycle)..tension
+ x..{curl1}-p7{curl hex "IsBad"}..tension.75and.74999..p2{0,1}&p2{_,'}..cycle:=p
+ ~4; subpath(-9,9)of subpath(3.5001,7.00001)of p~4=p~6;
+show p~6, directiontime(1,2) of p~6, directiontime(1,-1.00001) of p~6;
+p~3=(0,0)..controls (1,1) and (0,1)..(1,0); show p2..p2{p7}&{$,$}cycle,
+ (directiontime(1,1) of subpath(.314159,1) of p~3)[.314159,1];
+p~5=(subpath(0,.25)of p~3&subpath(.25,1)of p~3)shifted begintext1,0;
+p~3:=2/3'zscaled'{p~3}..controls(2,2/3(3))and penoffset(1/2x,y)of(0,1)(1,0);
+show p~3 intersectiontimes reverse p~3, point.17227 of p~3, point1-.28339of p~3;
+show point xpart(p~5 intersectiontimes p~5 shifted (.01,0))of p~5-
+ point ypart(p~5 intersectiontimes p~5 shifted (.01,0))of p~5;
+[[interim tracingedges:=1; e[-1+-+ -1.00001]=nullpicture; addto e1 also[[
+addto e0 doublepath p~5 scaled 3 withpen q; e0=e1=e2; cull e1 dropping (0,.1);
+nullpicture]];show e1 shifted(4089,-4095), e2 shifted(-4095,4092)shifted (-3,0),
+e2 shifted(4089,-4095)]];addto e1 also e2 shifted(-2,$); e1:=e1 shifted(-4,$);
+addto e0 also e1rotated89.999+e1scaled$; show e0 xscaled-10 yscaled2 xscaled82
+yscaled683;addto e1 doublepath (0,9) withweight-3 withweight turningnumber p~6
+ withpen pencircle xscaled(oct"180"++1) rotated-angle(64,$) shifted (9,8)
+ withpen makepen(($,$)..(1,0)..(1,1)..($,0)..($,$)&cycle)xscaled4095.49999;
+show e1, totalweight e1; chardp:=charcode:=5; xoffset:=-1.5; shipout e1;
+showstats; addto e2doublepath p~ yscaled1/60; e3:=e2 yscaled 0;
+autorounding:=2; addto e3doublepath(.5,0)..(3.5,1.5)withweight2;
+tracingspecs:=0; q:=makepen((1,1)..cycle) yscaled 1.5;
+p~8=(($,yy)rotated p{0,1}..{0,$}(1,0){0,$}..cycle)scaled2shifted(1000.49,9);
+turningcheck:=1;addto e2doublepath p~8 withpen q withweight p withpen cycle p;
+[[interim autorounding:=xx=.1; addto e2 contour p~8 withpen q withweight2]];
+chardx:=chardp:=charext:=-14.5;shipout-(-e0-e2)+e2shifted(0,6turningnumber p~8);
+p~9=(0,0)..(1,.5)..(5,1.5)..(7,2.5)..(12,3.5)..(13,4);addto e3 doublepath p~9;
+smoothing:=1; addto e3 doublepath p~9; addto e3 doublepath (-4095,0)..tension
+3/4 and 999..(0,2); show e3 rotated-90, (e0+e0) rotated90$ rotated90;
+if "a" if "ab">"b" elseif path reverse (3,4): >="aa":foo elseif fi "bar"
+else if '-(1,yy)<': :fi else def dup text t=[[t;save enddef;def|suffix$=,$
+enddef; def||tertiary p=show substring p of("a" enddef;||(2,$)&"bc");
+tertiarydef x++y=[[dup showtoken x;]];def quote x expr z of y=z enddef;
+ texts(quote x=(y+0)y+y)("xx",foo(x))=0]] enddef; def texts(text t,tt)expr?=
+ for n:=,for n"yy":n,length if false:endfor tt,t,:if string n:forsuffixes n=
+ foo1,[foo(n)],':show t,tt|(n;exitif not('<='+(?,yy)) endfor for m= :+endfor
+ for m=alpha step-1.1 3$: +m endfor fi endfor enddef; show (0,0){curl2}..
+ subpath(23.3,4.5)of p~9{curl3}..($,$){curl4}..cycle;numspecial p~++2+3;[[
+let?=if;save if,\;def if=endinput?enddef;def texts=input enddef;texts trap ]];
+dup[[def texts secondary x=primarydef y++y=x@y enddef; showtoken++;x enddef]]\;
+proofing:=1;texts:="a"&"b";% strings "yy" and "ab" no longer appear in memory
+texts-1.00001a1=-(a2+a3+a4+a5+a6);-(a3,a2)/.99999=-(a4+a5+[[showdependencies;
+a6]],a3+a4+a5+a6)]]; 1.00001a4+1=a5+a6; alpha:=.9alpha+7; showdependencies;
+a5=a6=2/3-a6; cull e1 dropping($,4a5)withweight1.5; charcode:=chardp:=27;
+openwindow 3 from (0,0) to (0,0) at "whoops"; addto p; shipout p; cull p;
+openwindow -.5 from ($,$) to ($,$) at (0,0); special p; numspecial "p";
+openwindow 15.49999 from (0,32*1024) to (1,31*1057) at (0,0); shipout e1;
+openwindow 5 from (0,length((0,0){0,0})) to (4,2) at ($,1);
+openwindow 6 from (length($,0),1) to (3,10) at (-5,2);
+display nullpicture; display p inwindow 3; display p inwindow 6;
+display e1 inwindow 6; cull e0 dropping (0.1,4095.999999) withweight 3.5
+withweight-3.5; display e0 inwindow 5.5; addto e0 also p; addto e0 contour 0;
+display e0 inwindow 5.49999; addto e0 contour p~9;
+display e1 inwindow 3+3; display e0e0 inwindow[[vardef e[]e=enddef;6]];
+addto e0 also e1; display e0 inwindow 5; ligtable||:255|=:|>>0,skipto0;
+display e1 inwindow 15; display e1 inwindow 6;
+show [[interim tracingcommands:=0; lig("g")(=:|); lig("h":"i")(|=:);
+lig("j")(|=:>);lig("k")(=:|>); lig("l")(|=:|>)]];
+b1:=c.a[ [[let c=++;vardef b=enddef;1]] ]; ligtable"m":0=:0,skipto5;
+!!; errhelp 0; errmessage "Be like Jane";
+errhelp "He%%%lp%"; errmessage""; errhelp ""; errmessage "Another";
+headerbyte 0; headerbyte(48.5)substring(-9,9)of"long"; for\=0:\
+headerbyte 9:2a6,"q"; fontdimen 9:2a6,"q"; fontdimen 1:2048;
+fontmaking:=1; extensible 5 5,"c"255.5,"d"; charlist 0:5:"a":"d";
+ligtable255:255::"a"=:"b","d" kern -2048,"c":0:99.5:"e"|=:|"f",0kern';
+ligtable 5:0; def clear(text x)=interim x:=$ enddef; clear(hppp); vppp=0;
+[[clear(tracingmacros); clear(tracingcommands); clear(tracingoutput\;
+ clear(proofing);designsize:=.99999;charcode:=ASCII char-418.5;vppp:=designsize;
+ def dp expr d = charcode:=charcode+1; chardp:=d; shipout nullpicture enddef;
+ dp 13; dp 12; dp 0; dp 21; dp -2; dp 17; dp 11; dp 3; charic:=-1000; dp -1;
+ dp 25; dp 31; dp 19; dp 7; charwd:=256; chardy:=6; dp 23; dp 30]];
+def f(suffix@@)(expr a,b)(text t)=numeric w; show a; % wipes out the old w
+addto @@ contour (0,0)..(2,0)..(1,$)..(1,1)..cycle withpen qq; % strange path
+addto @@ doublepath (0,0){1,1}..{2,1}(2,1) withpen qq; % carefully chosen
+addto @@ doublepath(($,$){1,0}..(1,1){1,0})scaled.5 withpen nullpen;
+cull @@ keeping (4,4) withweight1.5; enddef; def g(suffix$)=show $ enddef;
+addto e0 contour (0,0){1,0}..{1,0}(1,0){0,$}..{0,1}cycle withpen qq;
+f(e[3,w); g(e3,transformed p,penoffset-(1,1.3)of(pencircle scaled20 yscaled-.5),
+ directiontime (0,1) of ((0,0)..controls(1,1)and($,1)..(3,0)),
+ point 3.14159 of p~9 intersectiontimes subpath (3.14159,4) of p~9,
+ (($,1.1)..(1,$)) intersectiontimes precontrol$ of (0,0);
+addto e3 doublepath(-4094.99998,0)..(4094.99998,-.00001) withpen penoffset 0 of
+pencircle; addto e3 also e3 shifted (0,257); ,"flushed with pride"; numeric xx;
+def f(expr x,y,z)=showdependencies;tracingcapsules:=1;showdependencies;show
+ 1/3(3,6)*((x+y)+(y-x)), (1,1)/sqrt2 zscaled (x+1,x+2) - (x+1,x+2) rotated 45,
+ (0,1) zscaled (1,y+2)-(1,y+2) rotated 90 enddef; f((xx+1)/.3,(yy-1)/.5,(xx,0));
+'=(1000o3-4000(o1-o2)+4000o2+9,-.01o3+3ooo+
+ [[oo=9/10(o2+o4+o5-20);o4=o5=8/9(o1+.5o2); o6=-.0001o2;showdependencies;
+ numeric o[];xpart(alfa,[[pair alfa;0]])]])=-2/3[[save p;(p$,1-p$)]];
+xoffset:=yoffset:=4000[[oo=.5ooo=2*-1/2(ooo+[[numeric ooo;1]]);oo]];
+for @=angle(sqrt$,mlog$):charext:=uniformdeviate$;charht:=2048;granularity:=-8;
+addto e3 contour (0,-100)..tension 500..(100,-99)..tension 3000..cycle;
+tracingoutput:=@; shipout e3; special "bye"; interim char 99 = "c"; true=false;
+[[clear(tracingcommands); charcode:=ASCII char 269-13; shipout+nullpicture;
+"careful" quote for for = @ step 200 until 2*2600: &" METAFONT" endfor;]];
+scrollmode; "hello again"&char31; save p; fillin:=-.043;
+def f expr x=let )=]; let [=(; show _ enddef; begingroup tracingspecs:=1;
+show nullpen, makepath.qq, makepath(q rotated1), makepath pencircle rotated $;
+addto e0 doublepath (0,2){0,$}..{0,$}(0,1)..{1,0}(3,0)..(4,0){1,0}..cycle
+ withpen makepen((0,0)..(5,2.9)..(4,3)..cycle); tracingonline:=1; f xx[1);
+showdependencies; qq:=q; showstats; bye endtext
+% things not tested:
+% interaction (error insertion/deletion, interrupts, \pausing, files not there)
+% date, time; initialization of random number generator without randomseed
+% system-dependent parsing of file names, areas, extensions
+% certain error messages, especially fatal ones
+% things that can't happen in INIMF
+% unusual cases of fixed-point arithmetic
diff --git a/systems/knuth/dist/mf/trap.pl b/systems/knuth/dist/mf/trap.pl
new file mode 100644
index 0000000000..bae32a41fb
--- /dev/null
+++ b/systems/knuth/dist/mf/trap.pl
@@ -0,0 +1,490 @@
+(CODINGSCHEME Q)
+(DESIGNSIZE R 128.0)
+(COMMENT DESIGNSIZE IS IN POINTS)
+(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)
+(CHECKSUM O 5546464252)
+(FONTDIMEN
+ (SLANT R 2047.999999)
+ (SPACE R 0.0)
+ (STRETCH R 0.0)
+ (SHRINK R 0.0)
+ (XHEIGHT R 0.0)
+ (QUAD R 0.0)
+ (EXTRASPACE R 0.0)
+ (PARAMETER D 8 R 0.0)
+ (PARAMETER D 9 R 0.005208)
+ (PARAMETER D 10 R 0.0)
+ )
+(BOUNDARYCHAR O 377)
+(LIGTABLE
+ (COMMENT THIS PART OF THE PROGRAM IS NEVER USED!
+ )
+ (LABEL BOUNDARYCHAR)
+ (/LIG/>> O 377 O 0)
+ (KRN O 0 R 0.0)
+ (KRN O 0 R 0.0078125)
+ (KRN O 0 R 0.015625)
+ (KRN O 0 R 0.0234375)
+ (KRN O 0 R 0.03125)
+ (KRN O 0 R 0.0390625)
+ (KRN O 0 R 0.046875)
+ (KRN O 0 R 0.0546875)
+ (KRN O 0 R 0.0625)
+ (KRN O 0 R 0.0703125)
+ (KRN O 0 R 0.078125)
+ (KRN O 0 R 0.0859375)
+ (KRN O 0 R 0.09375)
+ (KRN O 0 R 0.1015625)
+ (KRN O 0 R 0.109375)
+ (KRN O 0 R 0.1171875)
+ (KRN O 0 R 0.125)
+ (KRN O 0 R 0.1328125)
+ (KRN O 0 R 0.140625)
+ (KRN O 0 R 0.1484375)
+ (KRN O 0 R 0.15625)
+ (KRN O 0 R 0.1640625)
+ (KRN O 0 R 0.171875)
+ (KRN O 0 R 0.1796875)
+ (KRN O 0 R 0.1875)
+ (KRN O 0 R 0.1953125)
+ (KRN O 0 R 0.203125)
+ (KRN O 0 R 0.2109375)
+ (KRN O 0 R 0.21875)
+ (KRN O 0 R 0.2265625)
+ (KRN O 0 R 0.234375)
+ (KRN O 0 R 0.2421875)
+ (KRN O 0 R 0.25)
+ (KRN O 0 R 0.2578125)
+ (KRN O 0 R 0.265625)
+ (KRN O 0 R 0.2734375)
+ (KRN O 0 R 0.28125)
+ (KRN O 0 R 0.2890625)
+ (KRN O 0 R 0.296875)
+ (KRN O 0 R 0.3046875)
+ (KRN O 0 R 0.3125)
+ (KRN O 0 R 0.3203125)
+ (KRN O 0 R 0.328125)
+ (KRN O 0 R 0.3359375)
+ (KRN O 0 R 0.34375)
+ (KRN O 0 R 0.3515625)
+ (KRN O 0 R 0.359375)
+ (KRN O 0 R 0.3671875)
+ (KRN O 0 R 0.375)
+ (KRN O 0 R 0.3828125)
+ (KRN O 0 R 0.390625)
+ (KRN O 0 R 0.3984375)
+ (KRN O 0 R 0.40625)
+ (KRN O 0 R 0.4140625)
+ (KRN O 0 R 0.421875)
+ (KRN O 0 R 0.4296875)
+ (KRN O 0 R 0.4375)
+ (KRN O 0 R 0.4453125)
+ (KRN O 0 R 0.453125)
+ (KRN O 0 R 0.4609375)
+ (SKIP D 1)
+ (LABEL C g)
+ (LIG/ C g O 0)
+ (STOP)
+ (KRN O 0 R 0.400782)
+ (KRN O 0 R 0.408594)
+ (KRN O 0 R 0.416407)
+ (KRN O 0 R 0.424219)
+ (KRN O 0 R 0.432032)
+ (KRN O 0 R 0.439844)
+ (KRN O 0 R 0.447657)
+ (KRN O 0 R 0.455469)
+ (KRN O 0 R 0.463282)
+ (KRN O 0 R 0.471094)
+ (KRN O 0 R 0.478907)
+ (KRN O 0 R 0.486719)
+ (KRN O 0 R 0.494532)
+ (KRN O 0 R 0.502344)
+ (KRN O 0 R 0.510157)
+ (KRN O 0 R 0.517969)
+ (KRN O 0 R 0.525782)
+ (KRN O 0 R 0.533594)
+ (KRN O 0 R 0.541407)
+ (KRN O 0 R 0.549219)
+ (KRN O 0 R 0.557032)
+ (KRN O 0 R 0.564844)
+ (KRN O 0 R 0.572657)
+ (KRN O 0 R 0.580469)
+ (KRN O 0 R 0.588282)
+ (KRN O 0 R 0.596094)
+ (KRN O 0 R 0.603907)
+ (KRN O 0 R 0.611719)
+ (KRN O 0 R 0.619532)
+ (KRN O 0 R 0.627344)
+ (KRN O 0 R 0.635157)
+ (KRN O 0 R 0.642969)
+ (KRN O 0 R 0.650782)
+ (KRN O 0 R 0.658594)
+ (KRN O 0 R 0.666407)
+ (KRN O 0 R 0.674219)
+ (KRN O 0 R 0.682032)
+ (KRN O 0 R 0.689844)
+ (KRN O 0 R 0.697657)
+ (KRN O 0 R 0.705469)
+ (KRN O 0 R 0.713282)
+ (KRN O 0 R 0.721094)
+ (KRN O 0 R 0.728907)
+ (KRN O 0 R 0.736719)
+ (KRN O 0 R 0.744532)
+ (KRN O 0 R 0.752344)
+ (KRN O 0 R 0.760157)
+ (KRN O 0 R 0.767969)
+ (KRN O 0 R 0.775782)
+ (KRN O 0 R 0.783594)
+ (KRN O 0 R 0.791407)
+ (KRN O 0 R 0.799219)
+ (KRN O 0 R 0.807032)
+ (KRN O 0 R 0.814844)
+ (KRN O 0 R 0.822657)
+ (KRN O 0 R 0.830469)
+ (KRN O 0 R 0.838282)
+ (KRN O 0 R 0.846094)
+ (KRN O 0 R 0.853907)
+ (KRN O 0 R 0.861719)
+ (SKIP D 1)
+ (LABEL C h)
+ (LABEL C i)
+ (/LIG C i O 0)
+ (STOP)
+ (KRN O 0 R 0.801562)
+ (KRN O 0 R 0.809375)
+ (KRN O 0 R 0.817187)
+ (KRN O 0 R 0.825)
+ (KRN O 0 R 0.832812)
+ (KRN O 0 R 0.840625)
+ (KRN O 0 R 0.848437)
+ (KRN O 0 R 0.85625)
+ (KRN O 0 R 0.864062)
+ (KRN O 0 R 0.871875)
+ (KRN O 0 R 0.879687)
+ (KRN O 0 R 0.8875)
+ (KRN O 0 R 0.895312)
+ (KRN O 0 R 0.903125)
+ (KRN O 0 R 0.910937)
+ (KRN O 0 R 0.91875)
+ (KRN O 0 R 0.926562)
+ (KRN O 0 R 0.934375)
+ (KRN O 0 R 0.942187)
+ (KRN O 0 R 0.95)
+ (KRN O 0 R 0.957812)
+ (KRN O 0 R 0.965625)
+ (KRN O 0 R 0.973437)
+ (KRN O 0 R 0.98125)
+ (KRN O 0 R 0.989062)
+ (KRN O 0 R 0.996875)
+ (KRN O 0 R 1.004687)
+ (KRN O 0 R 1.0125)
+ (KRN O 0 R 1.020312)
+ (KRN O 0 R 1.028125)
+ (KRN O 0 R 1.035937)
+ (KRN O 0 R 1.04375)
+ (KRN O 0 R 1.051562)
+ (KRN O 0 R 1.059375)
+ (KRN O 0 R 1.067187)
+ (KRN O 0 R 1.075)
+ (KRN O 0 R 1.082812)
+ (KRN O 0 R 1.090625)
+ (KRN O 0 R 1.098437)
+ (KRN O 0 R 1.10625)
+ (KRN O 0 R 1.114062)
+ (KRN O 0 R 1.121875)
+ (KRN O 0 R 1.129687)
+ (KRN O 0 R 1.1375)
+ (KRN O 0 R 1.145312)
+ (KRN O 0 R 1.153125)
+ (KRN O 0 R 1.160937)
+ (KRN O 0 R 1.16875)
+ (KRN O 0 R 1.176562)
+ (KRN O 0 R 1.184375)
+ (KRN O 0 R 1.192187)
+ (KRN O 0 R 1.2)
+ (KRN O 0 R 1.207812)
+ (KRN O 0 R 1.215625)
+ (KRN O 0 R 1.223437)
+ (KRN O 0 R 1.23125)
+ (KRN O 0 R 1.239062)
+ (KRN O 0 R 1.246875)
+ (KRN O 0 R 1.254687)
+ (KRN O 0 R 1.2625)
+ (SKIP D 1)
+ (LABEL C j)
+ (/LIG> C j O 0)
+ (SKIP D 123)
+ (KRN O 0 R 1.202344)
+ (KRN O 0 R 1.210156)
+ (KRN O 0 R 1.217969)
+ (KRN O 0 R 1.225781)
+ (KRN O 0 R 1.233594)
+ (KRN O 0 R 1.241406)
+ (KRN O 0 R 1.249219)
+ (KRN O 0 R 1.257031)
+ (KRN O 0 R 1.264844)
+ (KRN O 0 R 1.272656)
+ (KRN O 0 R 1.280469)
+ (KRN O 0 R 1.288281)
+ (KRN O 0 R 1.296094)
+ (KRN O 0 R 1.303906)
+ (KRN O 0 R 1.311719)
+ (KRN O 0 R 1.319531)
+ (KRN O 0 R 1.327344)
+ (KRN O 0 R 1.335156)
+ (KRN O 0 R 1.342969)
+ (KRN O 0 R 1.350781)
+ (KRN O 0 R 1.358594)
+ (KRN O 0 R 1.366406)
+ (KRN O 0 R 1.374219)
+ (KRN O 0 R 1.382031)
+ (KRN O 0 R 1.389844)
+ (KRN O 0 R 1.397656)
+ (KRN O 0 R 1.405469)
+ (KRN O 0 R 1.413281)
+ (KRN O 0 R 1.421094)
+ (KRN O 0 R 1.428906)
+ (KRN O 0 R 1.436719)
+ (KRN O 0 R 1.444531)
+ (KRN O 0 R 1.452344)
+ (KRN O 0 R 1.460156)
+ (KRN O 0 R 1.467969)
+ (KRN O 0 R 1.475781)
+ (KRN O 0 R 1.483594)
+ (KRN O 0 R 1.491406)
+ (KRN O 0 R 1.499219)
+ (KRN O 0 R 1.507031)
+ (KRN O 0 R 1.514844)
+ (KRN O 0 R 1.522656)
+ (KRN O 0 R 1.530469)
+ (KRN O 0 R 1.538281)
+ (KRN O 0 R 1.546094)
+ (KRN O 0 R 1.553906)
+ (KRN O 0 R 1.561719)
+ (KRN O 0 R 1.569531)
+ (KRN O 0 R 1.577344)
+ (KRN O 0 R 1.585156)
+ (KRN O 0 R 1.592969)
+ (KRN O 0 R 1.600781)
+ (KRN O 0 R 1.608594)
+ (KRN O 0 R 1.616406)
+ (KRN O 0 R 1.624219)
+ (KRN O 0 R 1.632031)
+ (KRN O 0 R 1.639844)
+ (KRN O 0 R 1.647656)
+ (KRN O 0 R 1.655469)
+ (KRN O 0 R 1.663281)
+ (SKIP D 1)
+ (LABEL C k)
+ (LIG/> C k O 0)
+ (SKIP D 62)
+ (KRN O 0 R 1.603125)
+ (KRN O 0 R 1.610937)
+ (KRN O 0 R 1.61875)
+ (KRN O 0 R 1.626562)
+ (KRN O 0 R 1.634375)
+ (KRN O 0 R 1.642187)
+ (KRN O 0 R 1.65)
+ (KRN O 0 R 1.657812)
+ (KRN O 0 R 1.665625)
+ (KRN O 0 R 1.673437)
+ (KRN O 0 R 1.68125)
+ (KRN O 0 R 1.689062)
+ (KRN O 0 R 1.696875)
+ (KRN O 0 R 1.704687)
+ (KRN O 0 R 1.7125)
+ (KRN O 0 R 1.720312)
+ (KRN O 0 R 1.728125)
+ (KRN O 0 R 1.735937)
+ (KRN O 0 R 1.74375)
+ (KRN O 0 R 1.751562)
+ (KRN O 0 R 1.759375)
+ (KRN O 0 R 1.767187)
+ (KRN O 0 R 1.775)
+ (KRN O 0 R 1.782812)
+ (KRN O 0 R 1.790625)
+ (KRN O 0 R 1.798437)
+ (KRN O 0 R 1.80625)
+ (KRN O 0 R 1.814062)
+ (KRN O 0 R 1.821875)
+ (KRN O 0 R 1.829687)
+ (KRN O 0 R 1.8375)
+ (KRN O 0 R 1.845312)
+ (KRN O 0 R 1.853125)
+ (KRN O 0 R 1.860937)
+ (KRN O 0 R 1.86875)
+ (KRN O 0 R 1.876562)
+ (KRN O 0 R 1.884375)
+ (KRN O 0 R 1.892187)
+ (KRN O 0 R 1.9)
+ (KRN O 0 R 1.907812)
+ (KRN O 0 R 1.915625)
+ (KRN O 0 R 1.923437)
+ (KRN O 0 R 1.93125)
+ (KRN O 0 R 1.939062)
+ (KRN O 0 R 1.946875)
+ (KRN O 0 R 1.954687)
+ (KRN O 0 R 1.9625)
+ (KRN O 0 R 1.970312)
+ (KRN O 0 R 1.978125)
+ (KRN O 0 R 1.985937)
+ (KRN O 0 R 1.99375)
+ (KRN O 0 R 2.001562)
+ (KRN O 0 R 2.009375)
+ (KRN O 0 R 2.017187)
+ (KRN O 0 R 2.025)
+ (KRN O 0 R 2.032812)
+ (KRN O 0 R 2.040625)
+ (KRN O 0 R 2.048437)
+ (KRN O 0 R 2.05625)
+ (KRN O 0 R 2.064062)
+ (STOP)
+ (LABEL C l)
+ (/LIG/> C l O 0)
+ (SKIP D 1)
+ (LABEL C m)
+ (LIG O 0 O 0)
+ (STOP)
+ (LIG C a C b)
+ (KRN C d R -15.999999)
+ (LABEL C c)
+ (LABEL C d)
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ (STOP)
+ (COMMENT THIS PART OF THE PROGRAM IS NEVER USED!
+ )
+ )
+(CHARACTER O 0
+ (CHARWD R 2.0)
+ (CHARHT R 15.999999)
+ (CHARDP R 0.234375)
+ (CHARIC R -7.8125)
+ (NEXTLARGER O 5)
+ )
+(CHARACTER O 5
+ (CHARWD R 0.0)
+ (CHARDP R -0.113281)
+ (VARCHAR
+ (TOP O 5)
+ (MID C c)
+ (REP C d)
+ )
+ )
+(CHARACTER O 33
+ (CHARWD R 0.0)
+ (CHARDP R 0.2109375)
+ )
+(CHARACTER O 137
+ (CHARWD R 0.0)
+ (CHARDP R 0.1015625)
+ )
+(CHARACTER O 140
+ (CHARWD R 0.0)
+ (CHARDP R 0.089844)
+ )
+(CHARACTER C a
+ (CHARWD R 0.0)
+ (NEXTLARGER C d)
+ )
+(CHARACTER C b
+ (CHARWD R 0.0)
+ (CHARDP R 0.1640625)
+ )
+(CHARACTER C c
+ (CHARWD R 0.0)
+ (CHARDP R -0.011719)
+ (COMMENT
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ )
+ )
+(CHARACTER C d
+ (CHARWD R 0.0)
+ (CHARDP R 0.1328125)
+ (COMMENT
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ )
+ )
+(CHARACTER C e
+ (CHARWD R 0.0)
+ (CHARDP R 0.089844)
+ )
+(CHARACTER C f
+ (CHARWD R 0.0)
+ (CHARDP R 0.0234375)
+ )
+(CHARACTER C g
+ (CHARWD R 0.0)
+ (CHARDP R -0.011719)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (LIG/ C g O 0)
+ )
+ )
+(CHARACTER C h
+ (CHARWD R 0.0)
+ (CHARDP R 0.1953125)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (/LIG C i O 0)
+ )
+ )
+(CHARACTER C i
+ (CHARWD R 0.0)
+ (CHARDP R 0.2421875)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (/LIG C i O 0)
+ )
+ )
+(CHARACTER C j
+ (CHARWD R 0.0)
+ (CHARDP R 0.1484375)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (/LIG> C j O 0)
+ (LIG C a C b)
+ (KRN C d R -15.999999)
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ )
+ )
+(CHARACTER C k
+ (CHARWD R 0.0)
+ (CHARDP R 0.0546875)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (LIG/> C k O 0)
+ (LIG C a C b)
+ (KRN C d R -15.999999)
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ )
+ )
+(CHARACTER C l
+ (CHARWD R 2.0)
+ (CHARDP R 0.1796875)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (/LIG/> C l O 0)
+ (LIG C a C b)
+ (KRN C d R -15.999999)
+ (/LIG/ C e C f)
+ (KRN O 0 R 0.0)
+ )
+ )
+(CHARACTER C m
+ (CHARWD R 2.0)
+ (CHARHT R 15.999999)
+ (CHARDP R 0.234375)
+ (CHARIC R -7.8125)
+ (COMMENT
+ (LIG O 0 O 0)
+ )
+ )
+(CHARACTER O 354
+ (CHARWD R 0.0)
+ (CHARDP R -15.999999)
+ )
diff --git a/systems/knuth/dist/mf/trap.typ b/systems/knuth/dist/mf/trap.typ
new file mode 100644
index 0000000000..159c5ce7fc
--- /dev/null
+++ b/systems/knuth/dist/mf/trap.typ
@@ -0,0 +1,210 @@
+This is GFtype, Version 3.1
+Options selected: Mnemonic output = true; pixel output = true.
+' METAFONT output 2014.01.07:1811'
+
+35: beginning of char 5: -4096<=m<=4094 -2<=n<=1
+(initially n=1) paint (4095)1
+64: skip0 0 (n=0) paint (4093)1
+69: skip0 0 (n=-1) paint (4096)1
+74: skip0 0 (n=-2) paint (4092)1(1)1
+81: eoc
+(The character is too large to be displayed in full.)
+.<--This pixel's lower left corner is at (-4096,2) in METAFONT coordinates
+
+
+
+
+.<--This pixel's upper left corner is at (-4096,-2) in METAFONT coordinates
+
+82: beginning of char 5 with extension -14: -4<=m<=1002 -6<=n<=11
+(previous character with the same code started at byte 35)
+(initially n=11) paint (1002)4
+111: skip0 0 (n=10) paint (1002)4
+116: skip0 0 (n=9) paint (1002)4
+121: skip1 3 (n=5) paint (1002)4
+127: skip0 0 (n=4) paint (1002)4
+132: skip0 0 (n=3) paint (1002)4
+137: newrow 7 (n=2) paint 1
+139: newrow 3 (n=1) paint 1(1)1(1)1
+145: newrow 1 (n=0) paint 1(1)2(1)1(1)1
+153: newrow 1 (n=-1) paint 2(1)1
+157: newrow 0 (n=-2) paint 1(1)1
+161: skip1 1 (n=-4) paint (7)1
+165: newrow 7 (n=-5) paint 1
+167: newrow 6 (n=-6) paint 1(1)1
+171: eoc
+(The character is too large to be displayed in full.)
+.<--This pixel's lower left corner is at (-4,12) in METAFONT coordinates
+
+
+
+
+
+
+
+
+
+ *
+ * * *
+ * ** * *
+ ** *
+* *
+
+ *
+ *
+ * *
+.<--This pixel's upper left corner is at (-4,-6) in METAFONT coordinates
+
+172: yyy -216270 (-3.30002)
+177: xxx 'title ab'
+187: xxx 'xoffset'
+196: yyy -65536 (-1)
+201: beginning of char 27 with extension -14: -4096<=m<=4094 -1<=n<=0
+(initially n=0) paint (4093)1(1)4095
+234: newrow 0 (n=-1) paint 4093(1)1
+240: eoc
+(The character is too large to be displayed in full.)
+.<--This pixel's lower left corner is at (-4096,1) in METAFONT coordinates
+
+********************************************************************************
+.<--This pixel's upper left corner is at (-4096,-1) in METAFONT coordinates
+
+241: xxx 'xoffset'
+250: yyy -87359488 (-1333)
+255: xxx 'yoffset'
+264: yyy -87359488 (-1333)
+269: beginning of char 109: -5428<=m<=2762 -1435<=n<=-1077
+(initially n=-1077) paint (0)8190
+298: skip2 256 (n=-1334) paint (0)8190
+305: skip1 98 (n=-1433) paint (4143)55
+311: skip0 0 (n=-1434) paint (4088)106
+317: skip0 0 (n=-1435) paint (4092)74
+323: eoc
+(The character is too large to be displayed in full.)
+.<--This pixel's lower left corner is at (-5428,-1076) in METAFONT coordinates
+********************************************************************************
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+.<--This pixel's upper left corner is at (-5428,-1156) in METAFONT coordinates
+
+324: xxx 'bye'
+329: xxx 'xoffset'
+338: yyy -87359488 (-1333)
+343: xxx 'yoffset'
+352: yyy -87359488 (-1333)
+357: beginning of char 0: 0<=m<=0 0<=n<=0
+(initially n=0)
+363: eoc
+(The character is entirely blank.)
+
+364: xxx 'title careful METAFONT METAFONT METAFONT METAFONT METAFONT METAF
+ONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT ME
+TAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT METAFONT
+ METAFONT METAFONT METAFONT METAFONT'
+624: xxx 'title hello again?'
+624: ! non-ASCII character in xxx command!
+
+Postamble starts at byte 644, after special info at byte 364.
+design size = 134217728 (128pt)
+check sum = 765094058
+hppp = -65536 (-1)
+vppp = 65535 (0.99998)
+min m = -5428, max m = 4094
+min n = -1435, max n = 11
+Character 0: dx -950272 (-14.5), dy 393216 (6), width 2097152 (-256), loc 324
+Character 5: dx -950272 (-14.5), width 0 (0), loc 82
+Character 27: dx -950272 (-14.5), width 0 (0), loc 172
+Character 95: dx -950272 (-14.5), width 0 (0), loc -1
+Character 96: dx -950272 (-14.5), width 0 (0), loc -1
+Character 97: dx -950272 (-14.5), width 0 (0), loc -1
+Character 98: dx -950272 (-14.5), width 0 (0), loc -1
+Character 99: dx -950272 (-14.5), width 0 (0), loc -1
+Character 100: dx -950272 (-14.5), width 0 (0), loc -1
+Character 101: dx -950272 (-14.5), width 0 (0), loc -1
+Character 102: dx -950272 (-14.5), width 0 (0), loc -1
+Character 103: dx -950272 (-14.5), width 0 (0), loc -1
+Character 104: dx -950272 (-14.5), width 0 (0), loc -1
+Character 105: dx -950272 (-14.5), width 0 (0), loc -1
+Character 106: dx -950272 (-14.5), width 0 (0), loc -1
+Character 107: dx -950272 (-14.5), width 0 (0), loc -1
+Character 108: dx -950272 (-14.5), dy 393216 (6), width 2097152 (-256), loc -1
+Character 109: dx -950272 (-14.5), dy 393216 (6), width 2097152 (-256), loc 241
+Character 236: dx 0 (0), width 0 (0), loc -1
+The file had 5 characters altogether.
diff --git a/systems/knuth/dist/mf/trapin.log b/systems/knuth/dist/mf/trapin.log
new file mode 100644
index 0000000000..2fbafd0e6b
--- /dev/null
+++ b/systems/knuth/dist/mf/trapin.log
@@ -0,0 +1,179 @@
+This is METAFONT, Version 2.7182818 (INIMF) 7 JAN 2014 17:51
+**\input trap
+(trap.mf
+>> << == >> ::: ||`` ''--!! ??## && @@ $$[[]]{{ }}((5.5 0.5))
+>> ".."
+! Not implemented: (unknown numeric)++(string).
+<to be read again>
+ ;
+l.4 ...}} . (( 5.5.5 )) ++ "..";
+
+I'm afraid I don't know how to apply that operation to that
+combination of types. Continue, and I'll return the second
+argument (see above) as the result of the operation.
+
+..
+! Missing `=' has been inserted.
+<to be read again>
+ ,
+l.5 begingroup save =; let=,
+ ; save,; newinternal $=,; let )...
+You should have said `let symbol = something'.
+But don't worry; I'll pretend that an equals sign
+was present. The next token I read will be `something'.
+
+> errorstopmode=errorstopmode
+> readstring=readstring
+> 2
+> "2"
+> ,=,
+> (=tag
+> )=,
+<< == >> ::: ||`` ''--!! ??## && @@ $$[[]]{{ }}(([][]))=numeric
+<< == >> ::: ||`` ''--!! ??## && @@ $$[[]]{{ }}((5.5 0.5))=<< == >> :::
+||`` ''--!! ??## && @@ $$[[]]{{ }}((5.5 0.5))
+> year=month
+! OK.
+l.6 ...ring,2,"2",,,(,),<<,year;
+
+
+! Missing `:' has been inserted.
+<to be read again>
+ ;
+l.8 ...not cycle "":1.1 forever;
+ fi;
+The next thing in this loop should have been a `:'.
+So I'll pretend that a colon was present;
+everything from here to `endfor' will be iterated.
+
+{fi}
+{exitif}
+{[repeat the loop]}
+{false}
+{fi}
+{exitif}
+{[repeat the loop]}
+{(2.1)>(2)}
+{true}
+{tracingcommands:=2.1}
+{showtoken}
+> |=:|>=|=:|>
+! OK.
+l.9 ... endfor; showtoken |=:|>;
+
+
+! Arithmetic overflow.
+l.10 tracingedges:=1/.00001
+ ; tracingequations:=$+1; p~=trac...
+Uh, oh. A little while ago one of the quantities that I was
+computing got too large, so I'm afraid your answers will be
+somewhat askew. You'll probably have to adopt different
+tactics next time. But I shall try to carry on anyway.
+
+{tracingedges:=32767.99998}
+{(0)+(1)}
+{tracingequations:=1}
+{(32767.99998)+(0.00002)}
+! Arithmetic overflow.
+l.10 ... p~=tracingedges+.00001;
+
+Uh, oh. A little while ago one of the quantities that I was
+computing got too large, so I'm afraid your answers will be
+somewhat askew. You'll probably have to adopt different
+tactics next time. But I shall try to carry on anyway.
+
+{(p~)=(32767.99998)}
+## p~=32767.99998
+{interim}
+{tracingmacros:=1}
+{tracingoutput:=1}
+{warningcheck:=1}
+{tracingstats:=1}
+{tracingchoices:=1}
+{tracingpens:=1}
+{tracingspecs:=1}
+{ASCII("")}
+{$:=-1}
+{$:=x}
+>> x
+! Internal quantity `$' must receive a known value.
+<to be read again>
+ ;
+l.12 ...os:=1; $:=ASCII""; $:=x;
+ p~:=p~;
+I can't set an internal quantity to anything but a known
+numeric value, so I'll have to ignore this assignment.
+
+{p~:=32767.99998}
+## p~=32767.99998
+! Value is too large (32767.99998).
+l.12 ...:=ASCII""; $:=x; p~:=p~;
+
+The equation I just processed has given some variable
+a value of 4096 or more. Continue and I'll try to cope
+with that big value; but it might be dangerous.
+(Set warningcheck:=0 to suppress this message.)
+
+{delimiters}
+{delimiters}
+{vardef}
+{def}
+
+foo(TEXT2)->begingroup(TEXT2)endgroup
+(SUFFIX0)<-
+(SUFFIX1)<-foo
+(TEXT2)<-interim.proofing:=(-0.5;shipout.nullpicture;special"3"
+{begingroup}
+{interim}
+{-(0.5)}
+! Missing `)' has been inserted.
+<to be read again>
+ ;
+<argument> ...m.proofing:=(-0.5;
+ shipout.nullpicture;special"3"
+foo->begingroup(TEXT2)
+ endgroup
+l.16 ...ture; special"3" endtext
+ ;
+I found no right delimiter to match a left one. So I've
+put one in, behind the scenes; this may fix the problem.
+
+{proofing:=-0.5}
+{shipout}
+{nullpicture}
+{special}
+{restoring proofing=0}
+{endgroup}
+{for}
+{showstats}
+Memory usage 291&41 (1922 still untouched)
+String usage 26&83 (891&11438 still untouched)
+! OK.
+l.17 ...ing:fi endfor showstats;
+ let!!=skipto;
+
+{let}
+{path}
+{-(15)}
+{-(12)}
+Path at line 18, before choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)
+
+Path at line 18, after choices:
+(0,0)..controls (15,4) and (-15,-12)
+ ..(4,0)
+
+{(unknown path p~)=(path)}
+{everyjob}
+{vardef}
+{let}
+{vardef}
+{def}
+{elseif}
+ )
+Beginning to dump on file trap.base
+ (preloaded base=trap 2014.1.7)
+1117 strings of total length 20614
+395 memory locations dumped; current usage is 317&67
+265 symbolic tokens
diff --git a/systems/knuth/dist/mf/trapman.tex b/systems/knuth/dist/mf/trapman.tex
new file mode 100644
index 0000000000..7968c6e44e
--- /dev/null
+++ b/systems/knuth/dist/mf/trapman.tex
@@ -0,0 +1,400 @@
+% The TRAP manual: How to validate MF --- last updated by D E Knuth on 4 Dec 89
+\font\eighttt= cmtt8
+\font\eightrm= cmr8
+\font\titlefont=cmssdc10 at 40pt
+\let\mc=\eightrm
+\font\logo=manfnt % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\rm
+\let\mainfont=\tenrm
+
+\def\.#1{\hbox{\tt#1}}
+\def\\#1{\hbox{\it#1\/\hskip.05em}} % italic type for identifiers
+
+\parskip 2pt plus 1pt
+\baselineskip 12pt plus .25pt
+
+\def\verbatim#1{\begingroup \frenchspacing
+ \def\do##1{\catcode`##1=12 } \dospecials
+ \parskip 0pt \parindent 0pt
+ \catcode`\ =\active \catcode`\^^M=\active
+ \tt \def\par{\ \endgraf} \obeylines \obeyspaces
+ \input #1 \endgroup}
+% a blank line will be typeset at the end of the file;
+% if you're unlucky it will appear on a page by itself!
+{\obeyspaces\global\let =\ }
+
+\output{\shipout\box255\global\advance\pageno by 1} % for the title page only
+\null
+\vfill
+\centerline{\titlefont A Torture Test}
+\vskip8pt
+\centerline{\titlefont for \logo ()*+,-.*}
+\vskip 24pt
+\centerline{by Donald E. Knuth}
+\centerline{Stanford University}
+\vskip 6pt
+\centerline{({\sl Version 2, January 1990\/})}
+\vfill
+\centerline{\vbox{\hsize 4in
+\noindent Programs that claim to be implementations of \MF84 are
+supposed to be able to process the test routine contained in this
+report, producing the outputs contained in this report.}}
+\vskip 24pt
+{\baselineskip 9pt
+\eightrm\noindent
+The preparation of this report was supported in part by the National Science
+Foundation under grants IST-8201926 and MCS-8300984,
+and by the System Development Foundation.
+{\logo opqrstuq} is a trademark of Addison-Wesley Publishing Company.
+
+
+}\pageno=0\eject
+
+\output{\shipout\vbox{ % for subsequent pages
+ \baselineskip0pt\lineskip0pt
+ \hbox to\hsize{\strut
+ \ifodd\pageno \hfil\eightrm\firstmark\hfil
+ \mainfont\the\pageno
+ \else\mainfont\the\pageno\hfil
+ \eightrm\firstmark\hfil\fi}
+ \vskip 10pt
+ \box255}
+ \global\advance\pageno by 1}
+\let\runninghead=\mark
+\outer\def\section#1.{\noindent{\bf#1.}\quad
+ \runninghead{\uppercase{#1} }\ignorespaces}
+
+\section Introduction.
+People often think that their programs are ``debugged'' when large applications
+have been run successfully. But system programmers know that a typical large
+application tends to use at most about 50 per cent of the instructions
+in a typical compiler. Although the other half of the code---which tends
+to be the ``harder half''---might be riddled with errors, the system seems
+to be working quite impressively until an unusual case shows up on the
+next day. And on the following day another error manifests itself, and so on;
+months or years go by before certain parts of the compiler are even
+activated, much less tested in combination with other portions of the system,
+if user applications provide the only tests.
+
+How then shall we go about testing a compiler? Ideally we would like to
+have a formal proof of correctness, certified by a computer.
+This would give us a lot of confidence,
+although of course the formal verification program might itself be incorrect.
+A more serious drawback of automatic verification is that the formal
+specifications of the compiler are likely to be wrong, since they aren't
+much easier to write than the compiler itself. Alternatively, we can
+substitute an informal proof of correctness: The programmer writes his or
+her code in a structured manner and checks that appropriate relations
+remain invariant, etc. This helps greatly to reduce errors, but it cannot
+be expected to remove them completely; the task of checking a large
+system is sufficiently formidable that human beings cannot do it without
+making at least a few slips here and there.
+
+Thus, we have seen that test programs are unsatisfactory if they are simply
+large user applications; yet some sort of test program is needed because
+proofs of correctness aren't adequate either. People have proposed schemes
+for constructing test data automatically from a program text, but such
+approaches run the risk of circularity, since they cannot assume that a
+given program has the right structure.
+
+I have been having good luck with a somewhat different approach,
+first used in 1960 to debug an {\mc ALGOL} compiler. The idea is to
+construct a test file that is about as different from a typical user
+application as could be imagined. Instead of testing things that people
+normally want to do, the file tests complicated things that people would
+never dare to think of, and it embeds these complexities in still
+more arcane constructions. Instead of trying to make the compiler do the
+right thing, the goal is to make it fail (until the bugs have all been found).
+
+To write such a fiendish test routine, one simply gets into a nasty frame
+of mind and tries to do everything in the unexpected way. Parameters
+that are normally positive are set negative or zero; borderline cases
+are pushed to the limit; deliberate errors are made in hopes that the
+compiler will not be able to recover properly from them.
+
+A user's application tends to exercise 50\%\ of a compiler's logic,
+but my first fiendish tests tend to improve this to about 90\%. As the
+next step I generally make use of frequency-counting software to identify
+the instructions that have still not been called upon. Then I add ever more
+fiendishness to the test routine, until more than 99\%\ of the code
+has been used at least once. (The remaining bits are things that
+can occur only if the source program is really huge, or if certain
+fatal errors are detected; or they are cases so similar to other well-tested
+things that there can be little doubt of their validity.)
+
+Of course, this is not guaranteed to work. But my experience in 1960 was
+that only two bugs were ever found in that {\mc ALGOL} compiler after it
+correctly translated that original fiendish test. And one of those bugs
+was actually present in the results of the test; I simply had failed to
+notice that the output was incorrect. Similar experiences occurred later
+during the 60s and 70s, with respect to a few assemblers, compilers,
+and simulators that I wrote.
+
+This method of debugging, combined with the methodology of structured
+programming and informal proofs (otherwise known as careful desk checking),
+leads to greater reliability of production software than any other
+method I know. Therefore I have used it in developing \MF84, and the
+main bulk of this report is simply a presentation of the test program
+that was used to get the bugs out of \MF.
+
+Such a test file is useful also after a program has been debugged, since
+it can be used to give some assurance that subsequent modifications don't
+mess things up.
+
+The test file is called \.{TRAP.MF}, because of my warped sense of humor:
+\MF's companion system, \TeX, has a similar test file called \.{TRIP}, and I
+couldn't help thinking about Billy Goat Gruff and the story of ``trip,
+trap, trip, trap.''
+
+The contents of this test file are so remote from what people actually
+do with \MF, I feel apologetic if I have to explain the correct
+translation of \.{TRAP.MF}; nobody really cares about most of the
+nitty-gritty rules that are involved. Yet I believe \.{TRAP} exemplifies
+the sort of test program that has outstanding diagnostic ability, as
+explained above.
+
+If somebody claims to have a correct implementation of \MF, I will not
+believe it until I see that \.{TRAP.MF} is translated properly.
+I propose, in fact, that a program must meet two criteria before it
+can justifiably be called \MF: (1)~The person who wrote it must be
+happy with the way it works at his or her installation; and (2)~the
+program must produce the correct results from \.{TRAP.MF}.
+
+\MF\ is in the public domain, and its algorithms are published;
+I've done this since I do not want to discourage its use by placing
+proprietary restrictions on the software. However, I don't want
+faulty imitations to masquerade as \MF\ processors, since users
+want \MF\ to produce identical results on different machines.
+Hence I am planning to do whatever I can to suppress any systems that
+call themselves \MF\ without meeting conditions (1) and~(2).
+I have copyrighted the programs so that I have some chance to forbid
+unauthorized copies; I explicitly authorize copying of correct
+\MF\ implementations, and not of incorrect ones!
+
+The remainder of this report consists of appendices, whose contents ought
+to be described briefly here:
+
+Appendix A explains in detail how to carry out a test of \MF, given
+a tape that contains copies of the other appendices.
+
+Appendix B is \.{TRAP.MF}, the fiendish test file that has already
+been mentioned. People who think that they understand \MF\ are challenged
+to see if they know what \MF\ is supposed to do with this file.
+People who know only a little about \MF\ might still find it
+interesting to study Appendix~B, just to get some insights into the
+methodology advocated here.
+
+Appendix C is \.{TRAPIN.LOG}, a correct transcript file \.{TRAP.LOG}
+that results if \.{INIMF} is applied to \.{TRAP.MF}. (\.{INIMF} is
+the name of a version of \MF\ that does certain initializations;
+this run of \.{INIMF} also creates a binary base file called \.{TRAP.BASE}.)
+
+Appendix D is a correct transcript file \.{TRAP.LOG} that results if
+\.{INIMF} or any other version of \MF\ is applied to \.{TRAP.MF}
+with base file \.{TRAP.BASE}.
+
+Appendix E is \.{TRAP.TYP}, the symbolic version of a correct output
+file \.{TRAP.72270GF} that was produced at the same time as the \.{TRAP.LOG}
+file of Appendix~D.
+
+Appendix F is \.{TRAP.PL}, the symbolic version of a correct output
+file \.{TRAP.TFM} that was produced at the same time as the \.{TRAP.LOG}
+file of Appendix~D.
+
+Appendix G is \.{TRAP.FOT}, an abbreviated version of Appendix D that
+appears on the user's terminal during the run that produces \.{TRAP.LOG},
+\.{TRAP.72270GF}, and \.{TRAP.TFM}.
+
+The debugging of \MF\ and the testing of the adequacy of \.{TRAP.MF}
+could not have been done nearly as well as reported here except for
+the magnificent software support provided by my colleague David R. Fuchs.
+In particular, he extended our local Pascal compiler so that
+frequency counting and a number of other important features were added
+to its online debugging abilities.
+
+The method of testing advocated here has one chief difficulty that deserves
+comment: I had to verify by hand that \MF\ did the right things
+to \.{TRAP.MF}. This took many hours, and perhaps I have missed
+something (as I did in 1960); I must confess that I have not checked
+every single number in Appendices D, E, and~F. However, I'm willing to pay
+$\$$81.92 to the first finder of any remaining bug in \MF, and I will
+be surprised if that bug doesn't show up also in one of these appendices.
+
+\vfill\eject
+
+\section Appendix A: How to test \MF.
+
+\item{0.} Let's assume that you have a tape containing \.{TRAP.MF},
+\.{TRAPIN.LOG}, \.{TRAP.LOG}, \.{TRAP.TYP}, \.{TRAP.PL}, and \.{TRAP.FOT},
+as in Appendices B, C, D, E, F, and~G. Furthermore, let's suppose that you
+have a working \.{WEB} system, and that you have working programs
+\.{TFtoPL} and \.{GFtype}, as described in the \TeX ware and \MF ware reports.
+
+\item{1.} Prepare a version of \.{INIMF}. (This means that your \.{WEB}
+change file should have {\bf init} and {\bf tini} defined to be null.)
+The {\bf debug} and {\bf gubed} macros should be null, in order to
+activate special printouts that occur when $\\{tracingedges}>1.0$.
+The {\bf stat} and {\bf tats} macros should also be null, so that
+statistics are kept. Set \\{mem\_top} and \\{mem\_max} to 3000
+(or to \\{mem\_min} plus 3000, if \\{mem\_min} isn't zero),
+for purposes of this test version.
+Also set $\\{error\_line}=64$, $\\{half\_error\_line}=32$,
+$\\{max\_print\_line}=72$, $\\{screen\_width}=100$, and
+$\\{screen\_depth}=200$; these parameters affect many of the lines of
+the test output, so your job will be much easier if you use the same
+settings that were used to produce Appendix~E. Also (if possible) set
+$\\{gf\_buf\_size}=8$, since this tests more parts of the program.
+You probably should also use the ``normal'' settings of other parameters
+found in \.{MF.WEB} (e.g., $\\{max\_internal}=100$, $\\{buf\_size}=500$,
+etc.), since these show up in a few lines of the test output. Finally,
+change \MF's screen-display routines by putting the following simple lines
+in the change file:
+$$\vbox{\halign{\tt#\hfil\cr
+\char`\@x Screen routines:\cr
+begin init\char`\_screen:=false;\cr
+\char`\@y\cr
+begin init\char`\_screen:=true;
+ \char`\{screen instructions will be logged\char`\}\cr
+\char`\@z\cr}}$$
+None of the other screen routines (\\{update\_screen}, \\{blank\_rectangle},
+\\{paint\_row}) should be changed in any way; the effect will be to have
+\MF's actions recorded in the transcript files instead of on the screen,
+in a machine-independent way.
+
+\item{2.} Run the \.{INIMF} prepared in step 1. In response to the first
+`\.{**}' prompt, type carriage return (thus getting another `\.{**}').
+Then type `\.{\char`\\input trap}'. You should get an output that matches
+the file \.{TRAPIN.LOG} (Appendix~C). Don't be alarmed by the error
+messages that you see, unless they are different from those in Appendix~C.
+
+\def\sp{{\char'40}}
+\item{3.} Run \.{INIMF} again. This time type `\.{\sp\&trap\sp\sp trap\sp}'.
+(The spaces in this input help to check certain parts of \MF\ that
+aren't otherwise used.) You should get outputs \.{TRAP.LOG}, \.{TRAP.72270GF},
+and \.{TRAP.TFM}.
+Furthermore, your terminal should receive output that matches \.{TRAP.FOT}
+(Appendix~G). During the middle part of this test, however, the terminal
+will not be getting output, because \.{batchmode} is being
+tested; don't worry if nothing seems to be happening for a while---nothing
+is supposed to.
+
+\item{4.} Compare the \.{TRAP.LOG} file from step 3 with the ``master''
+\.{TRAP.LOG} file of step~0. (Let's hope you put that master file in a
+safe place so that it wouldn't be clobbered.) There should be perfect
+agreement between these files except in the following respects:
+
+\itemitem{a)} The dates and possibly the file names will
+naturally be different.
+
+\itemitem{b)} If you had different values for \\{stack\_size}, \\{buf\_size},
+etc., the corresponding capacity values will be different when they
+are printed out at the end.
+
+\itemitem{c)} Help messages may be different; indeed, the author encourages
+non-English help messages in versions of \MF\ for people who don't
+understand English as well as some other language.
+
+\itemitem{d)} The total number and length of strings at the end and/or
+``still untouched'' may well be different.
+
+\itemitem{e)} If your \MF\ uses a different memory allocation or
+packing scheme, the memory usage statistics may change.
+
+\itemitem{f)} If you use a different storage allocation scheme, the
+capsule numbers will probably be different, but the order of variables
+should be unchanged when dependent variables are shown. \MF\ should also
+choose the same variables to be dependent.
+
+\itemitem{g)} If your computer handles integer division of negative operands
+in a nonstandard way, you may get results that are rounded differently.
+Although \TeX\ is careful to be machine-independent in this regard,
+\MF\ is not, because integer divisions are present in so many places.
+
+\item{5.} Use \.{GFtype} to convert your file \.{TRAP.72270GF} to a file
+\.{TRAP.TYP}. (Both of \.{GFtype}'s options, i.e., mnemonic output and image
+output, should be enabled so that you get the maximum amount of output.)
+The resulting file should agree with the master \.{TRAP.TYP} file of step~0,
+assuming that your \.{GFtype} has the ``normal'' values of compile-time
+constants ($\\{top\_pixel}=69$, etc.).
+
+\item{6.} Use \.{TFtoPL} to convert your file \.{TRAP.TFM} to a file
+\.{TRAP.PL}. The resulting file should agree with the master \.{TRAP.PL}
+file of step~0.
+
+\item{7.} You might also wish to test \.{TRAP} with other versions of
+\MF\ (i.e., \.{VIRMF} or a production version with another base file
+preloaded). It should work unless \MF's primitives have been redefined in
+the base file. However, this step isn't essential, since all the code of
+\.{VIRMF} appears in \.{INIMF}; you probably won't catch any more errors
+this way, unless they would already become obvious from normal use of
+the~system.
+
+\vfill\eject
+
+\section Appendix B: The \.{TRAP.MF} file.
+The contents of the test routine are prefixed here with line numbers, for
+ease in comparing this file with the error messages printed later; the
+line numbers aren't actually present.
+\runninghead{APPENDIX B: \.{TRAP.MF} (CONTINUED)}
+
+\vskip 8pt
+\begingroup\count255=0
+\everypar{\global\advance\count255 by 1
+ \hbox to 20pt{\sevenrm\hfil\the\count255\ \ }}
+\verbatim{trap.mf}
+\endgroup
+\vfill\eject
+
+\section Appendix C: The \.{TRAPIN.LOG} file.
+When \.{INIMF} makes the \.{TRAP.BASE} file, it also creates a file called
+\.{TRAP.LOG} that looks like this.
+\runninghead{APPENDIX C: \.{TRAPIN.LOG} (CONTINUED)}
+
+\vskip8pt
+\verbatim{trapin.log}
+\vfill\eject
+
+\section Appendix D: The \.{TRAP.LOG} file.
+Here is the major output of the \.{TRAP} test; it is generated by running
+\.{INIMF} and loading \.{TRAP.BASE}, then reading \.{TRAP.MF}.
+\runninghead{APPENDIX D: \.{TRAP.LOG} (CONTINUED)}
+
+{\let\tt=\eighttt\leftskip 1in\baselineskip 9pt plus .1pt minus .1pt
+\vskip8pt
+\verbatim{trap.log}
+}
+\vfill\eject
+
+\section Appendix E: The \.{TRAP.TYP} file.
+Here is another major component of the test. It shows the output of \.{GFtype}
+applied to the file \.{TRAP.72270GF} that is created at the same time
+Appendix D was produced.
+\runninghead{APPENDIX E: \.{TRAP.TYP} (CONTINUED)}
+
+{\let\tt=\eighttt\leftskip 1in\baselineskip 9pt plus .1pt minus .1pt
+\vskip8pt
+\verbatim{trap.typ}
+}
+\vfill\eject
+
+\section Appendix F: The \.{TRAP.PL} file.
+In this case we have the output of \.{TFtoPL}
+applied to the file \.{TRAP.TFM} that is created at the same time
+Appendix D was produced.
+\runninghead{APPENDIX F: \.{TRAP.PL} (CONTINUED)}
+
+{\let\tt=\eighttt\leftskip 1in\baselineskip 9pt plus .1pt minus .1pt
+\vskip8pt
+\verbatim{trap.pl}
+}
+\vfill\eject
+
+\section Appendix G: The \.{TRAP.FOT} file.
+This shows what appeared on the terminal while Appendix D was being produced.
+\runninghead{APPENDIX G: \.{TRAP.FOT} (CONTINUED)}
+
+\vskip8pt
+\verbatim{trap.fot}
+
+\vfill\end
diff --git a/systems/knuth/dist/mfware/gftodvi.web b/systems/knuth/dist/mfware/gftodvi.web
new file mode 100644
index 0000000000..3540b500f1
--- /dev/null
+++ b/systems/knuth/dist/mfware/gftodvi.web
@@ -0,0 +1,4393 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Version 0 was completed on April 23, 1984.
+% Version 0.1 added char_code output (May 4).
+% Version 0.2 included rules and dots in the boundary calculations (May 25).
+% Version 0.3 added label type "/" (May 27).
+% Version 0.4 (by Arthur Samuel) improved the dot labeling routine (July 23).
+% Version 0.5 added the slant font for rules (September 2).
+% Version 0.6 changed label types and allowed invisible dots (September 28).
+% Version 1.0 switched to new GF format (December 8).
+% Version 1.1 switched to newer GF format (February 2, 1985).
+% Version 1.2 added the offset operations of MF version 0.8 (April 1, 1985).
+% Version 1.3 allowed online entry of gray font, etc. (April 22, 1985).
+% Version 1.4 allowed "almost" horizontal or vertical rules (May 20, 1985).
+% Version 1.5 corrected a bug in the diagonal slant routine (June 18, 1985).
+% Version 1.6 corrected a bug if labels exist but no dots (September 13, 1985).
+% Version 1.7 changed from am to cm fonts; fam became ext (October 5, 1985).
+% Version 2.0 was tuned up for the METAFONTware report (April, 1989).
+% Version 3.0 uses 8-bit codes and extended ligatures (October, 1989).
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+\font\logo=manfnt % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\let\swap=\leftrightarrow
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+
+\def\title{GF$\,$\lowercase{to}$\,$DVI}
+\def\contentspagenumber{301}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont GFtoDVI} processor}
+ \vskip 15pt
+ \centerline{(Version 3.0, October 1989)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926, MCS-8300984, and
+ CCR-8610181,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.
+ `{\logo hijklmnj}\kern1pt' is a trademark of Addison-Wesley
+ Publishing Company.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+The \.{GFtoDVI} utility program reads binary generic font (``\.{GF}'')
+files that are produced by font compilers such as \MF, and converts them
+into device-independent (``\.{DVI}'') files that can be printed to give
+annotated hardcopy proofs of the character shapes. The annotations are
+specified by the comparatively simple conventions of plain \MF; i.e.,
+there are mechanisms for labeling chosen points and for superimposing
+horizontal or vertical rules on the enlarged character shapes.
+
+The purpose of \.{GFtoDVI} is simply to make proof copies; it does not
+exhaustively test the validity of a \.{GF} file, nor do its algorithms
+much resemble the algorithms that are typically used to prepare font
+descriptions for commercial typesetting equipment. Another program,
+\.{GFtype}, is available for validity checking; \.{GFtype} also serves
+as a model of programs that convert fonts from \.{GF} format to some
+other coding scheme.
+
+The |banner| string defined here should be changed whenever \.{GFtoDVI}
+gets modified.
+
+@d banner=='This is GFtoDVI, Version 3.0' {printed when the program starts}
+
+@ This program is written in standard \PASCAL, except where it is necessary
+to use extensions; for example, \.{GFtoDVI} must read files whose names
+are dynamically specified, and such a task would be impossible in pure \PASCAL.
+All places where nonstandard constructions are used have been listed in
+the index under ``system dependencies.''
+@!@^system dependencies@>
+
+Another exception to standard \PASCAL\ occurs in the
+use of default branches in |case| statements; the conventions
+of \.{TANGLE}, \.{WEAVE}, etc., have been followed.
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The main input and output files are not mentioned in the program header,
+because their external names
+will be determined at run time (e.g., by interpreting the
+command line that invokes this program). Error messages and other remarks
+are written on the |output| file, which the user may
+choose to assign to the terminal if the system permits it.
+@^system dependencies@>
+
+The term |print| is used instead of |write| when this program writes on
+the |output| file, so that all such output can be easily deflected.
+
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+@d print_nl(#)==@+begin write_ln; write(#);@+end
+
+@p program GF_to_DVI(@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var @!i,@!j,@!m,@!n:integer; {loop indices for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ If the program has to stop prematurely, it goes to the
+`|final_end|'.
+
+@d final_end=9999 {label for the end of it all}
+
+@<Labels...@>=final_end;
+
+@ The following parameters can be changed at compile time to extend or
+reduce \.{GFtoDVI}'s capacity.
+
+@<Constants...@>=
+@!max_labels=2000; {maximum number of labels and dots and rules per character}
+@!pool_size=10000; {maximum total length of labels and other strings}
+@!max_strings=1100; {maximum number of labels and other strings}
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!file_name_size=50; {a file name shouldn't be longer than this}
+@!font_mem_size=2000; {space for font metric data}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!widest_row=8192; {maximum number of pixels per row}
+@!lig_lookahead=20; {size of stack used when inserting ligature characters}
+
+@ Labels are given symbolic names by the following definitions, so that
+occasional |goto| statements will be meaningful. We insert the label
+`|exit|:' just before the `\ignorespaces|end|\unskip' of a procedure in
+which we have used the `|return|' statement defined below; the label
+`|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|'.
+
+Incidentally, this program never declares a label that isn't actually used,
+because some fussy \PASCAL\ compilers will complain about redundant labels.
+
+@d exit=10 {go here to leave a procedure}
+@d reswitch=21 {go here to start a case statement again}
+@d continue=22 {go here to resume a loop}
+@d done=30 {go here to exit a loop}
+@d done1=31 {like |done|, when there is more than one loop}
+@d found=40 {go here when you've found it}
+@d not_found=45 {go here when you've found nothing}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
+@f loop == xclause
+ {\.{WEB}'s |xclause| acts like `\ignorespaces|while true do|\unskip'}
+@d do_nothing == {empty statement}
+@d return == goto exit {terminate a procedure call}
+@f return == nil {\.{WEB} will henceforth say |return| instead of \\{return}}
+
+@ If the \.{GF} file is badly malformed, the whole process must be aborted;
+\.{GFtoDVI} will give up, after issuing an error message about the symptoms
+that were noticed.
+
+Such errors might be discovered inside of subroutines inside of subroutines,
+so a procedure called |jump_out| has been introduced. This procedure, which
+simply transfers control to the label |final_end| at the end of the program,
+contains the only non-local |goto| statement in \.{GFtoDVI}.
+@^system dependencies@>
+
+@d abort(#)==@+begin print(' ',#); jump_out;@+end
+@d bad_gf(#)==abort('Bad GF file: ',#,'! (at byte ',cur_loc-1:1,')')
+@.Bad GF file@>
+
+@p procedure jump_out;
+begin goto final_end;
+end;
+
+@ As in \TeX\ and \MF, this program deals with numeric quantities that
+are integer multiples of~$2^{16}$, and calls them |scaled|.
+
+@d unity==@'200000 {|scaled| representation of 1.0}
+
+@<Types ...@>=
+@!scaled=integer; {fixed-point numbers}
+
+@* The character set.
+Like all programs written with the \.{WEB} system, \.{GFtoDVI} can be
+used with any character set. But it uses ASCII code internally, because
+the programming for portable input-output is easier when a fixed internal
+code is used. Furthermore, both \.{GF} and \.{DVI} files use ASCII code
+for file names and certain other strings.
+The next few sections of \.{GFtoDVI} have therefore been copied from the
+analogous ones in the \.{WEB} system routines.
+
+@<Types ...@>=
+@!ASCII_code=0..255; {eight-bit numbers, a subrange of the integers}
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when
+six-bit character sets were common, so it did not make provision for lowercase
+letters. Nowadays, of course, we need to deal with both capital and
+small letters in a convenient way. So we shall assume that the
+\PASCAL\ system being used for \.{GFtoDVI} has a character set containing
+at least the standard visible ASCII characters (|"!"| through |"~"|). If
+additional characters are present, \.{GFtoDVI} can be configured to
+work with them too.
+
+Some \PASCAL\ compilers use the original name |char| for the data type
+associated with the characters in text files, while other \PASCAL s
+consider |char| to be a 64-element subrange of a larger data type that has
+some other name. In order to accommodate this difference, we shall use
+the name |text_char| to stand for the data type of the characters in the
+output file. We shall also assume that |text_char| consists of
+the elements |chr(first_text_char)| through |chr(last_text_char)|,
+inclusive. The following definitions should be adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Types ...@>=
+@!text_file=packed file of text_char;
+
+@ The \.{GFtoDVI} processor converts 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...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+
+@ Under our assumption that the visible characters of standard ASCII are
+all present, the following assignment statements initialize the
+|xchr| array properly, without needing any system-dependent changes.
+
+@<Set init...@>=
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';
+
+@ Here now is the system-dependent part of the character set.
+If \.{GFtoDVI} is being implemented on a garden-variety \PASCAL\ for which
+only standard ASCII codes will appear in the input and output files, you
+don't need to make any changes here. But if you have, for example, an extended
+character set like the one in Appendix~C of {\sl The \TeX book}, the first
+line of code in this module should be changed to
+$$\hbox{|for i:=0 to @'37 do xchr[i]:=chr(i);|}$$
+\.{WEB}'s character set is essentially identical to \TeX's.
+@^system dependencies@>
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:='?';
+for i:=@'177 to @'377 do xchr[i]:='?';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=" ";
+for i:=1 to @'377 do xord[xchr[i]]:=i;
+xord['?']:="?";
+
+@ The |input_ln| routine waits for the user to type a line at his or her
+terminal; then it puts ASCII-code equivalents for the characters on that line
+into the |buffer| array. The |term_in| file is used for terminal input.
+@^system dependencies@>
+
+Since the terminal is being used for both input and output, some systems
+need a special routine to make sure that the user can see a prompt message
+before waiting for input based on that message. (Otherwise the message
+may just be sitting in a hidden buffer somewhere, and the user will have
+no idea what the program is waiting for.) We shall call a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+
+@d update_terminal == break(output) {empty the terminal output buffer}
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of 0..255;
+@!term_in:text_file; {the terminal, considered as an input file}
+
+@ A global variable |line_length| records the first buffer position after
+the line just read.
+@^system dependencies@>
+
+@p procedure input_ln; {inputs a line from the terminal}
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+line_length:=0;
+while (line_length<terminal_line_length)and not eoln(term_in) do
+ begin buffer[line_length]:=xord[term_in^]; incr(line_length); get(term_in);
+ end;
+end;
+
+@ The global variable |buf_ptr| is used while scanning each line of input;
+it points to the first unread character in |buffer|.
+
+@<Glob...@>=
+@!buf_ptr:0..terminal_line_length; {the number of characters read}
+@!line_length:0..terminal_line_length; {end of line read by |input_ln|}
+
+@* Device-independent file format.
+Before we get into the details of \.{GFtoDVI}, we need to know exactly
+what \.{DVI} files are. The form of such files was designed by David R.
+@^Fuchs, David Raymond@>
+Fuchs in 1979. Almost any reasonable typesetting device can be driven by
+a program that takes \.{DVI} files as input, and dozens of such
+\.{DVI}-to-whatever programs have been written. Thus, it is possible to
+print the output of document compilers like \TeX\ on many different kinds
+of equipment. (The following material has been copied almost verbatim from the
+program for \TeX.)
+
+A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
+series of commands in a machine-like language. The first byte of each command
+is the operation code, and this code is followed by zero or more bytes
+that provide parameters to the command. The parameters themselves may consist
+of several consecutive bytes; for example, the `|set_rule|' command has two
+parameters, each of which is four bytes long. Parameters are usually
+regarded as nonnegative integers; but four-byte-long parameters,
+and shorter parameters that denote distances, can be
+either positive or negative. Such parameters are given in two's complement
+notation. For example, a two-byte-long distance parameter has a value between
+$-2^{15}$ and $2^{15}-1$.
+@.DVI {\rm files}@>
+
+Incidentally, when two or more 8-bit bytes are combined to form an integer of
+16 or more bits, the most significant bytes appear first in the file.
+This is called BigEndian order.
+@^BigEndian order@>
+
+A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
+or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that define the dimensions used in the
+file; this must come first. Each ``page'' consists of a |bop| command,
+followed by any number of other commands that tell where characters are to
+be placed on a physical page, followed by an |eop| command. The pages
+appear in the order that they were generated, not in any particular
+numerical order. If we ignore |nop| commands and \\{fnt\_def} commands
+(which are allowed between any two commands in the file), each |eop|
+command is immediately followed by a |bop| command, or by a |post|
+command; in the latter case, there are no more pages in the file, and the
+remaining bytes form the postamble. Further details about the postamble
+will be explained later.
+
+Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first byte is number~0, then comes number~1, and so on. For example,
+one of the parameters of a |bop| command points to the previous |bop|;
+this makes it feasible to read the pages in backwards order, in case the
+results are being directed to a device that stacks its output face up.
+Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
+first page occupies bytes 100 to 999, say, and if the second
+page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
+points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
+very first |bop|, i.e., the one that starts in byte 100, has a pointer of $-1$.)
+
+@ The \.{DVI} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+implicit instead of explicit. When a \.{DVI}-reading program reads the
+commands for a page, it keeps track of several quantities: (a)~The current
+font |f| is an integer; this value is changed only
+by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
+is given by two numbers called the horizontal and vertical coordinates,
+|h| and |v|. Both coordinates are zero at the upper left corner of the page;
+moving to the right corresponds to increasing the horizontal coordinate, and
+moving down corresponds to increasing the vertical coordinate. Thus, the
+coordinates are essentially Cartesian, except that vertical directions are
+flipped; the Cartesian version of |(h,v)| would be |(h,-v)|. (c)~The
+current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
+where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
+are used for vertical spacing. (d)~There is a stack containing
+|(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to
+change the current level of operation. Note that the current font~|f| is
+not pushed and popped; the stack contains only information about
+positioning.
+
+The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
+to 32 bits, including the sign. Since they represent physical distances,
+there is a small unit of measurement such that increasing |h| by~1 means
+moving a certain tiny distance to the right. The actual unit of
+measurement is variable, as explained below.
+
+@ Here is a list of all the commands that may appear in a \.{DVI} file. Each
+command is specified by its symbolic name (e.g., |bop|), its opcode byte
+(e.g., 139), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|p[4]|' means that parameter |p| is four bytes long.
+
+\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
+such that the reference point of the character is at |(h,v)|. Then
+increase |h| by the width of that character. Note that a character may
+have zero or negative width, so one cannot be sure that |h| will advance
+after this command; but |h| usually does increase.
+
+\yskip\hang|set_char_1| through |set_char_127| (opcodes 1 to 127).
+Do the operations of |set_char_0|; but use the character whose number
+matches the opcode, instead of character~0.
+
+\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
+number~|c| is typeset. \TeX82 uses this command for characters in the
+range |128<=c<256|.
+
+\yskip\hang|set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
+bytes long, so it is in the range |0<=c<65536|.
+
+\yskip\hang|set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
+bytes long, so it can be as large as $2^{24}-1$. Not even the Chinese
+language has this many characters, but this command might prove useful
+in some yet unforeseen way.
+
+\yskip\hang|set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
+bytes long, possibly even negative. Imagine that.
+
+\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
+of height |a| and width |b|, with its bottom left corner at |(h,v)|. Then
+set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note
+that if |b<0|, the value of |h| will decrease even though nothing else happens.
+
+\yskip\hang|put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
+such that the reference point of the character is at |(h,v)|. (The `put'
+commands are exactly like the `set' commands, except that they simply put out a
+character or a rule without moving the reference point afterwards.)
+
+\yskip\hang|put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
+
+\yskip\hang|put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
+
+\yskip\hang|put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
+
+\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
+|h| is not changed.
+
+\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
+may occur between \.{DVI} commands, but a |nop| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
+of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set
+the current font |f| to an undefined value. The ten $c_i$ parameters can
+be used to identify pages, if a user wants to print only part of a \.{DVI}
+file; \TeX82 gives them the values of \.{\\count0} $\ldots$ \.{\\count9}
+at the time \.{\\shipout} was invoked for this page. The parameter |p|
+points to the previous |bop| command in the file, where the first |bop|
+has $p=-1$.
+
+\yskip\hang|eop| 140. End of page: Print what you have read since the
+previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading
+programs that drive most output devices will have kept a buffer of the
+material that appears on the page that has just ended. This material is
+largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
+|h|~coordinate; so it usually needs to be sorted into some order that is
+appropriate for the device in question. \.{GFtoDVI} does not do such sorting.)
+
+\yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the
+top of the stack; do not change any of these values. Note that |f| is
+not pushed.
+
+\yskip\hang|pop| 142. Pop the top six values off of the stack and assign
+them to |(h,v,w,x,y,z)|. The number of pops should never exceed the number
+of pushes, since it would be highly embarrassing if the stack were empty
+at the time of a |pop| command.
+
+\yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units.
+The parameter is a signed number in two's complement notation, |-128<=b<128|;
+if |b<0|, the reference point actually moves left.
+
+\yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a
+two-byte quantity in the range |-32768<=b<32768|.
+
+\yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a
+three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a
+four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|w| gets particular values.
+
+\yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128<=b<128|. This command
+changes the current |w|~spacing and moves right by |b|.
+
+\yskip\hang|w2| 149 |b[2]|. Same as |w1|, but |b| is a two-byte-long
+parameter, |-32768<=b<32768|.
+
+\yskip\hang|w3| 150 |b[3]|. Same as |w1|, but |b| is a three-byte-long
+parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|w4| 151 |b[4]|. Same as |w1|, but |b| is a four-byte-long
+parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|'
+commands are like the `|w|' commands except that they involve |x| instead
+of |w|.
+
+\yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128<=b<128|. This command
+changes the current |x|~spacing and moves right by |b|.
+
+\yskip\hang|x2| 154 |b[2]|. Same as |x1|, but |b| is a two-byte-long
+parameter, |-32768<=b<32768|.
+
+\yskip\hang|x3| 155 |b[3]|. Same as |x1|, but |b| is a three-byte-long
+parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|x4| 156 |b[4]|. Same as |x1|, but |b| is a four-byte-long
+parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units.
+The parameter is a signed number in two's complement notation, |-128<=a<128|;
+if |a<0|, the reference point actually moves up.
+
+\yskip\hang|down2| 158 |a[2]|. Same as |down1|, except that |a| is a
+two-byte quantity in the range |-32768<=a<32768|.
+
+\yskip\hang|down3| 159 |a[3]|. Same as |down1|, except that |a| is a
+three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|down4| 160 |a[4]|. Same as |down1|, except that |a| is a
+four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|y| gets particular values.
+
+\yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128<=a<128|. This command
+changes the current |y|~spacing and moves down by |a|.
+
+\yskip\hang|y2| 163 |a[2]|. Same as |y1|, but |a| is a two-byte-long
+parameter, |-32768<=a<32768|.
+
+\yskip\hang|y3| 164 |a[3]|. Same as |y1|, but |a| is a three-byte-long
+parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|y4| 165 |a[4]|. Same as |y1|, but |a| is a four-byte-long
+parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands
+are like the `|y|' commands except that they involve |z| instead of |y|.
+
+\yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128<=a<128|. This command
+changes the current |z|~spacing and moves down by |a|.
+
+\yskip\hang|z2| 168 |a[2]|. Same as |z1|, but |a| is a two-byte-long
+parameter, |-32768<=a<32768|.
+
+\yskip\hang|z3| 169 |a[3]|. Same as |z1|, but |a| is a three-byte-long
+parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|z4| 170 |a[4]|. Same as |z1|, but |a| is a four-byte-long
+parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been
+defined by a \\{fnt\_def} instruction, as explained below.
+
+\yskip\hang|fnt_num_1| through |fnt_num_63| (opcodes 172 to 234). Set
+|f:=1|, \dots, |f:=63|, respectively.
+
+\yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font
+numbers in the range |64<=k<256|.
+
+\yskip\hang|fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
+bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this
+command, but large font numbers may prove useful for specifications of
+color or texture, or they may be used for special fonts that have fixed
+numbers in some external coding scheme.
+
+\yskip\hang|fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
+bytes long, so it can be as large as $2^{24}-1$.
+
+\yskip\hang|fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
+bytes long; this is for the really big font numbers (and for the negative ones).
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
+programs are being used. \TeX82 generates |xxx1| when a short enough
+\.{\\special} appears, setting |k| to the number of bytes being sent. It
+is recommended that |x| be a string having the form of a keyword followed
+by possible parameters relevant to that keyword.
+
+\yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
+large. \TeX82 uses |xxx4| when |xxx1| would be incorrect.
+
+\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<256|; font definitions will be explained shortly.
+
+\yskip\hang|fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<65536|.
+
+\yskip\hang|fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
+
+\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@ Only a few of the operation codes above are actually needed by \.{GFtoDVI}.
+
+@d set1=128 {typeset a character and move right}
+@d put_rule=137 {typeset a rule}
+@d bop=139 {beginning of page}
+@d eop=140 {ending of page}
+@d push=141 {save the current positions}
+@d pop=142 {restore previous positions}
+@d right4=146 {move right}
+@d down4=160 {move down}
+@d z0=166 {move down |z|}
+@d z4=170 {move down and set |z|}
+@d fnt_num_0=171 {set current font to 0}
+@d fnt_def1=243 {define the meaning of a font number}
+@d pre=247 {preamble}
+@d post=248 {postamble beginning}
+@d post_post=249 {postamble ending}
+
+@ The preamble contains basic information about the file as a whole. As
+stated above, there are six parameters:
+$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
+The |i| byte identifies \.{DVI} format; currently this byte is always set
+to~2. (The value |i=3| is currently used for an extended format that
+allows a mixture of right-to-left and left-to-right typesetting.
+Some day we will set |i=4|, when \.{DVI} format makes another
+incompatible change---perhaps in the year 2048.)
+
+The next two parameters, |num| and |den|, are positive integers that define
+the units of measurement; they are the numerator and denominator of a
+fraction by which all dimensions in the \.{DVI} file could be multiplied
+in order to get lengths in units of $10^{-7}$ meters. (For example, there are
+exactly 7227 \TeX\ points in 254 centimeters, and \TeX82 works with scaled
+points where there are $2^{16}$ sp in a point, so \TeX82 sets |num=25400000|
+and $|den|=7227\cdot2^{16}=473628672$.)
+@^sp@>
+
+The |mag| parameter is what \TeX82 calls \.{\\mag}, i.e., 1000 times the
+desired magnification. The actual fraction by which dimensions are
+multiplied is therefore $|mag|\cdot|num|/1000|den|$. Note that if a \TeX\
+source document does not call for any `\.{true}' dimensions, and if you
+change it only by specifying a different \.{\\mag} setting, the \.{DVI}
+file that \TeX\ creates will be completely unchanged except for the value
+of |mag| in the preamble and postamble. (Fancy \.{DVI}-reading programs allow
+users to override the |mag|~setting when a \.{DVI} file is being printed.)
+
+Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
+interpreted further. The length of comment |x| is |k|, where |0<=k<256|.
+
+@d dvi_id_byte=2 {identifies the kind of \.{DVI} files described here}
+
+@ Font definitions for a given font number |k| contain further parameters
+$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
+The four-byte value |c| is the check sum that \TeX\ (or whatever program
+generated the \.{DVI} file) found in the \.{TFM} file for this font;
+|c| should match the check sum of the font found by programs that read
+this \.{DVI} file.
+@^check sum@>
+
+Parameter |s| contains a fixed-point scale factor that is applied to the
+character widths in font |k|; font dimensions in \.{TFM} files and other
+font files are relative to this quantity, which is always positive and
+less than $2^{27}$. It is given in the same units as the other dimensions
+of the \.{DVI} file. Parameter |d| is similar to |s|; it is the ``design
+size,'' and (like~|s|) it is given in \.{DVI} units. Thus, font |k| is to be
+used at $|mag|\cdot s/1000d$ times its normal size.
+
+The remaining part of a font definition gives the external name of the font,
+which is an ASCII string of length |a+l|. The number |a| is the length
+of the ``area'' or directory, and |l| is the length of the font name itself;
+the standard local system font area is supposed to be used when |a=0|.
+The |n| field contains the area in its first |a| bytes.
+
+Font definitions must appear before the first use of a particular font number.
+Once font |k| is defined, it must not be defined again; however, we
+shall see below that font definitions appear in the postamble as well as
+in the pages, so in this sense each font number is defined exactly twice,
+if at all. Like |nop| commands, font definitions can
+appear before the first |bop|, or between an |eop| and a |bop|.
+
+@ The last page in a \.{DVI} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \TeX\ has
+accumulated about the file, making it possible to print subsets of the data
+with reasonable efficiency. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
+ $\langle\,$font definitions$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the final |bop| in the file. The next three
+parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
+appeared in the preamble.
+
+Parameters |l| and |u| give respectively the height-plus-depth of the tallest
+page and the width of the widest page, in the same units as other dimensions
+of the file. These numbers might be used by a \.{DVI}-reading program to
+position individual ``pages'' on large sheets of film or paper; however,
+the standard convention for output on normal size paper is to position each
+page so that the upper left-hand corner is exactly one inch from the left
+and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer
+software that attempts cleverly to center the output; a fixed position of
+the upper left corner is easiest for users to understand and to work with.
+Therefore |l| and~|u| are often ignored.
+
+Parameter |s| is the maximum stack depth (i.e., the largest excess of
+|push| commands over |pop| commands) needed to process this file. Then
+comes |t|, the total number of pages (|bop| commands) present.
+
+The postamble continues with font definitions, which are any number of
+\\{fnt\_def} commands as described above, possibly interspersed with |nop|
+commands. Each font number that is used in the \.{DVI} file must be defined
+exactly twice: Once before it is first selected by a \\{fnt} command, and once
+in the postamble.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the font definitions, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~2, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., @'337 in octal). \TeX\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{DVI} file makes it feasible for
+\.{DVI}-reading programs to find the postamble first, on most computers,
+even though \TeX\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{DVI} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{DVI} reader can discover all the information needed for typesetting the
+pages. Note that it is also possible to skip through the \.{DVI} file at
+reasonably high speed to locate a particular page, if that proves
+desirable. This saves a lot of time, since \.{DVI} files used in production
+jobs tend to be large.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{DVI}
+format has been designed to work most efficiently with modern operating systems.
+
+@* Generic font file format.
+The ``generic font'' (\.{GF}) input files that \.{GFtoDVI} must deal with
+have a structure that was inspired by \.{DVI} format, although the
+operation codes are quite different in most cases. The term {\sl
+generic\/} indicates that this file format doesn't match the conventions
+of any name-brand manufacturer; but it is easy to convert \.{GF} files to
+the special format required by almost all digital phototypesetting
+equipment. There's a strong analogy between the \.{DVI} files written by
+\TeX\ and the \.{GF} files written by \MF; and, in fact, the reader will
+notice that many of the paragraphs below are identical to their
+counterparts in the description of \.{DVI} already given. The following
+description has been lifted almost verbatim from the program for \MF.
+
+A \.{GF} file is a stream of 8-bit bytes that may be
+regarded as a series of commands in a machine-like language. The first
+byte of each command is the operation code, and this code is followed by
+zero or more bytes that provide parameters to the command. The parameters
+themselves may consist of several consecutive bytes; for example, the
+`|boc|' (beginning of character) command has six parameters, each of
+which is four bytes long. Parameters are usually regarded as nonnegative
+integers; but four-byte-long parameters can be either positive or
+negative, hence they range in value from $-2^{31}$ to $2^{31}-1$.
+As in \.{DVI} files, numbers that occupy
+more than one byte position appear in BigEndian order,
+and negative numbers appear in two's complement notation.
+
+A \.{GF} file consists of a ``preamble,'' followed by a sequence of one or
+more ``characters,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that introduce the file; this must come
+first. Each ``character'' consists of a |boc| command, followed by any
+number of other commands that specify ``black'' pixels,
+followed by an |eoc| command. The characters appear in the order that \MF\
+generated them. If we ignore no-op commands (which are allowed between any
+two commands in the file), each |eoc| command is immediately followed by a
+|boc| command, or by a |post| command; in the latter case, there are no
+more characters in the file, and the remaining bytes form the postamble.
+Further details about the postamble will be explained later.
+
+Some parameters in \.{GF} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first file byte is number~0, then comes number~1, and so on.
+
+@ The \.{GF} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+relative instead of absolute. When a \.{GF}-reading program reads the
+commands for a character, it keeps track of two quantities: (a)~the current
+column number,~|m|; and (b)~the current row number,~|n|. These are 32-bit
+signed integers, although most actual font formats produced from \.{GF}
+files will need to curtail this vast range because of practical
+limitations. (\MF\ output will never allow $\vert m\vert$ or $\vert
+n\vert$ to get extremely large, but the \.{GF} format tries to be more general.)
+
+How do \.{GF}'s row and column numbers correspond to the conventions
+of \TeX\ and \MF? Well, the ``reference point'' of a character, in \TeX's
+view, is considered to be at the lower left corner of the pixel in row~0
+and column~0. This point is the intersection of the baseline with the left
+edge of the type; it corresponds to location $(0,0)$ in \MF\ programs.
+Thus the pixel in \.{GF} row~0 and column~0 is \MF's unit square, comprising the
+region of the plane whose coordinates both lie between 0 and~1. The
+pixel in \.{GF} row~|n| and column~|m| consists of the points whose \MF\
+coordinates |(x,y)| satisfy |m<=x<=m+1| and |n<=y<=n+1|. Negative values of
+|m| and~|x| correspond to columns of pixels {\sl left\/} of the reference
+point; negative values of |n| and~|y| correspond to rows of pixels {\sl
+below\/} the baseline.
+
+Besides |m| and |n|, there's also a third aspect of the current
+state, namely the @!|paint_switch|, which is always either \\{black} or
+\\{white}. Each \\{paint} command advances |m| by a specified amount~|d|,
+and blackens the intervening pixels if |paint_switch=black|; then
+the |paint_switch| changes to the opposite state. \.{GF}'s commands are
+designed so that |m| will never decrease within a row, and |n| will never
+increase within a character; hence there is no way to whiten a pixel that
+has been blackened.
+
+@ Here is a list of all the commands that may appear in a \.{GF} file. Each
+command is specified by its symbolic name (e.g., |boc|), its opcode byte
+(e.g., 67), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|d[2]|' means that parameter |d| is two bytes long.
+
+\yskip\hang|paint_0| 0. This is a \\{paint} command with |d=0|; it does
+nothing but change the |paint_switch| from \\{black} to \\{white} or vice~versa.
+
+\yskip\hang\\{paint\_1} through \\{paint\_63} (opcodes 1 to 63).
+These are \\{paint} commands with |d=1| to~63, defined as follows: If
+|paint_switch=black|, blacken |d|~pixels of the current row~|n|,
+in columns |m| through |m+d-1| inclusive. Then, in any case,
+complement the |paint_switch| and advance |m| by~|d|.
+
+\yskip\hang|paint1| 64 |d[1]|. This is a \\{paint} command with a specified
+value of~|d|; \MF\ uses it to paint when |64<=d<256|.
+
+\yskip\hang|paint2| 65 |d[2]|. Same as |paint1|, but |d|~can be as high
+as~65535.
+
+\yskip\hang|paint3| 66 |d[3]|. Same as |paint1|, but |d|~can be as high
+as $2^{24}-1$. \MF\ never needs this command, and it is hard to imagine
+anybody making practical use of it; surely a more compact encoding will be
+desirable when characters can be this large. But the command is there,
+anyway, just in case.
+
+\yskip\hang|boc| 67 |c[4]| |p[4]| |min_m[4]| |max_m[4]| |min_n[4]|
+|max_n[4]|. Beginning of a character: Here |c| is the character code, and
+|p| points to the previous character beginning (if any) for characters having
+this code number modulo 256. (The pointer |p| is |-1| if there was no
+prior character with an equivalent code.) The values of registers |m| and |n|
+defined by the instructions that follow for this character must
+satisfy |min_m<=m<=max_m| and |min_n<=n<=max_n|. (The values of |max_m| and
+|min_n| need not be the tightest bounds possible.) When a \.{GF}-reading
+program sees a |boc|, it can use |min_m|, |max_m|, |min_n|, and |max_n| to
+initialize the bounds of an array. Then it sets |m:=min_m|, |n:=max_n|, and
+|paint_switch:=white|.
+
+\yskip\hang|boc1| 68 |c[1]| |@!del_m[1]| |max_m[1]| |@!del_n[1]| |max_n[1]|.
+Same as |boc|, but |p| is assumed to be~$-1$; also |del_m=max_m-min_m|
+and |del_n=max_n-min_n| are given instead of |min_m| and |min_n|.
+The one-byte parameters must be between 0 and 255, inclusive.
+\ (This abbreviated |boc| saves 19~bytes per character, in common cases.)
+
+\yskip\hang|eoc| 69. End of character: All pixels blackened so far
+constitute the pattern for this character. In particular, a completely
+blank character might have |eoc| immediately following |boc|.
+
+\yskip\hang|skip0| 70. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=white|. \ (This finishes one row and begins another,
+ready to whiten the leftmost pixel in the new row.)
+
+\yskip\hang|skip1| 71 |d[1]|. Decrease |n| by |d+1|, set |m:=min_m|, and set
+|paint_switch:=white|. This is a way to produce |d| all-white rows.
+
+\yskip\hang|skip2| 72 |d[2]|. Same as |skip1|, but |d| can be as large
+as 65535.
+
+\yskip\hang|skip3| 73 |d[3]|. Same as |skip1|, but |d| can be as large
+as $2^{24}-1$. \MF\ obviously never needs this command.
+
+\yskip\hang|new_row_0| 74. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=black|. \ (This finishes one row and begins another,
+ready to {\sl blacken\/} the leftmost pixel in the new row.)
+
+\yskip\hang|@!new_row_1| through |@!new_row_164| (opcodes 75 to 238). Same as
+|new_row_0|, but with |m:=min_m+1| through |min_m+164|, respectively.
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |no_op| unless special \.{GF}-reading
+programs are being used. \MF\ generates \\{xxx} commands when encountering
+a \&{special} string; this occurs in the \.{GF} file only between
+characters, after the preamble, and before the postamble. However,
+\\{xxx} commands might appear within characters,
+in \.{GF} files generated by other
+processors. It is recommended that |x| be a string having the form of a
+keyword followed by possible parameters relevant to that keyword.
+
+\yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+\MF\ uses this when sending a \&{special} string whose length exceeds~255.
+
+\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be
+ridiculously large; |k| mustn't be negative.
+
+\yskip\hang|yyy| 243 |y[4]|. This command is undefined in general;
+it functions as a 5-byte |no_op| unless special \.{GF}-reading programs
+are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a
+result of \&{numspecial} commands; the intent is to provide numeric
+parameters to \\{xxx} commands that immediately precede.
+
+\yskip\hang|no_op| 244. No operation, do nothing. Any number of |no_op|'s
+may occur between \.{GF} commands, but a |no_op| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|char_loc| 245 |c[1]| |dx[4]| |dy[4]| |w[4]| |p[4]|.
+This command will appear only in the postamble, which will be explained shortly.
+
+\yskip\hang|@!char_loc0| 246 |c[1]| |@!dm[1]| |w[4]| |p[4]|.
+Same as |char_loc|, except that |dy| is assumed to be zero, and the value
+of~|dx| is taken to be |65536*dm|, where |0<=dm<256|.
+
+\yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameter |i| is an identifying number for \.{GF} format, currently
+131. The other information is merely commentary; it is not given
+special interpretation like \\{xxx} commands are. (Note that \\{xxx}
+commands may immediately follow the preamble, before the first |boc|.)
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@d gf_id_byte=131 {identifies the kind of \.{GF} files described here}
+
+@ Here are the opcodes that \.{GFtoDVI} actually refers to.
+
+@d paint_0=0 {beginning of the \\{paint} commands}
+@d paint1=64 {move right a given number of columns, then
+ black${}\swap{}$white}
+@d paint2=65 {ditto, with potentially larger number of columns}
+@d paint3=66 {ditto, with potentially excessive number of columns}
+@d boc=67 {beginning of a character}
+@d boc1=68 {abbreviated |boc|}
+@d eoc=69 {end of a character}
+@d skip0=70 {skip no blank rows}
+@d skip1=71 {skip over blank rows}
+@d skip2=72 {skip over lots of blank rows}
+@d skip3=73 {skip over a huge number of blank rows}
+@d new_row_0=74 {move down one row and then right}
+@d xxx1=239 {for \&{special} strings}
+@d xxx2=240 {for somewhat long \&{special} strings}
+@d xxx3=241 {for extremely long \&{special} strings}
+@d xxx4=242 {for incredibly long \&{special} strings}
+@d yyy=243 {for \&{numspecial} numbers}
+@d no_op=244 {no operation}
+
+@ The last character in a \.{GF} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \MF\ has
+accumulated. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |@!ds[4]| |@!cs[4]| |@!hppp[4]| |@!vppp[4]|
+ |@!min_m[4]| |@!max_m[4]| |@!min_n[4]| |@!max_n[4]|\cr
+ $\langle\,$character locators$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the byte following the final |eoc| in the file
+(or to the byte following the preamble, if there are no characters);
+it can be used to locate the beginning of \\{xxx} commands
+that might have preceded the postamble. The |ds| and |cs| parameters
+@^design size@> @^check sum@>
+give the design size and check sum, respectively, of the font (see the
+description of \.{TFM} format below).
+Parameters |hppp| and |vppp| are the ratios of
+pixels per point, horizontally and vertically, expressed as |scaled| integers
+(i.e., multiplied by $2^{16}$); they can be used to correlate the font
+with specific device resolutions, magnifications, and ``at sizes.'' Then
+come |min_m|, |max_m|, |min_n|, and |max_n|, which bound the values that
+registers |m| and~|n| assume in all characters in this \.{GF} file.
+(These bounds need not be the best possible; |max_m| and |min_n| may, on the
+other hand, be tighter than the similar bounds in |boc| commands. For
+example, some character may have |min_n=-100| in its |boc|, but it might
+turn out that |n| never gets lower than |-50| in any character; then
+|min_n| can have any value |<=-50|. If there are no characters in the file,
+it's possible to have |min_m>max_m| and/or |min_n>max_n|.)
+
+@ Character locators are introduced by |char_loc| commands,
+which specify a character residue~|c|, character escapements (|dx,dy|),
+a character width~|w|, and a pointer~|p|
+to the beginning of that character. (If two or more characters have the
+same code~|c| modulo 256, only the last will be indicated; the others can be
+located by following backpointers. Characters whose codes differ by a
+multiple of 256 are assumed to share the same font metric information,
+hence the \.{TFM} file contains only residues of character codes modulo~256.
+This convention is intended for oriental languages, when there are many
+character shapes but few distinct widths.)
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+The character escapements (|dx,dy|) are the values of \MF's \&{chardx}
+and \&{chardy} parameters; they are in units of |scaled| pixels;
+i.e., |dx| is in horizontal pixel units times $2^{16}$, and |dy| is in
+vertical pixel units times $2^{16}$. This is the intended amount of
+displacement after typesetting the character; for \.{DVI} files, |dy|
+should be zero, but other document file formats allow nonzero vertical
+escapement.
+
+The character width~|w| duplicates the information in the \.{TFM} file; it
+is $2^{24}$ times the ratio of the true width to the font's design size.
+
+The backpointer |p| points to the character's |boc|, or to the first of
+a sequence of consecutive \\{xxx} or |yyy| or |no_op| commands that
+immediately precede the |boc|, if such commands exist; such ``special''
+commands essentially belong to the characters, while the special commands
+after the final character belong to the postamble (i.e., to the font
+as a whole). This convention about |p| applies also to the backpointers
+in |boc| commands, even though it wasn't explained in the description
+of~|boc|. @^backpointers@>
+
+Pointer |p| might be |-1| if the character exists in the \.{TFM} file
+but not in the \.{GF} file. This unusual situation can arise in \MF\ output
+if the user had |proofing<0| when the character was being shipped out,
+but then made |proofing>=0| in order to get a \.{GF} file.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the character locators, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~131, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., @'337 in octal). \MF\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{GF} file makes it feasible for
+\.{GF}-reading programs to find the postamble first, on most computers,
+even though \MF\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{GF} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{GF} reader can discover all the information needed for individual characters.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{GF}
+format has been designed to work most efficiently with modern operating systems.
+But if \.{GF} files have to be processed under the restrictions of standard
+\PASCAL, one can simply read them from front to back. This will
+be adequate for most applications. However, the postamble-first approach
+would facilitate a program that merges two \.{GF} files, replacing data
+from one that is overridden by corresponding data in the other.
+
+@* Extensions to the generic format.
+The \\{xxx} and \\{yyy} instructions understood by \.{GFtoDVI} will be
+listed now, so that we have a convenient reference to all of the special
+assumptions made later.
+
+Each special instruction begins with an \\{xxx} command, which consists of
+either a keyword by itself, or a keyword followed by a space followed
+by arguments. This \\{xxx} command may then be followed by \\{yyy}
+commands that are understood to be arguments.
+
+The keywords of special instructions that are intended to be used at
+many different sites should be published as widely as possible in order
+to minimize conflicts. The first person to establish a keyword presumably
+has a right to define it; \.{GFtoDVI}, as the first program
+to use extended \.{GF} commands, has the opportunity of choosing any
+keywords it likes, and the responsibility of choosing reasonable ones.
+Since labels are expected to account for the bulk of extended commands
+in typical uses of \MF, the ``null'' keyword has been set aside to
+denote a labeling command.
+
+@ Here then are the special commands of \.{GFtoDVI}.
+
+\def\string{$\langle\,$string$\,\rangle$}
+\def\okpagebreak{\vfil\penalty-100\vfilneg}
+\smallskip\hang\noindent
+\.{\SP n}\string\ $x$ $y$. Here \.n denotes the type of label; the
+characters \.1, \.2, \.3,~\.4 respectively denote labels forced to be
+at the top, left, right, or bottom of their dot, and the characters
+\.5, \.6, \.7,~\.8 stand for the same possibilities but with no dot printed.
+The character \.0 instructs \.{GFtoDVI} to choose one of the first four
+possibilities, if there's no overlap with other labels or dots, otherwise
+an ``overflow'' entry is placed at the right of the figure. The character
+\./ is the same as \.0 except that overflow entries are not produced. The
+label itself is the \string\ that follows. \MF\ coordinates of the
+point that is to receive this label are given by arguments $x$ and~$y$,
+in units of scaled pixels. (These arguments appear in \\{yyy} commands.)
+(Precise definitions of the size and positioning of labels, and of the
+notion of ``conflicting'' labels, will be given later.)
+
+\smallskip\hang\noindent
+\.{rule} $x_1$ $y_1$ $x_2$ $y_2$. This command draws a line from
+$(x_1,y_1)$ to $(x_2,y_2)$ in \MF\ coordinates. The present implementation
+does this only if the line is either horizontal or vertical, or if its
+slope matches the slope of the slant font.
+
+\smallskip\hang\noindent
+\.{title\SP}\string. This command (which is output by \MF\
+when it sees a ``title statement'') specifies a string that will appear
+at the top of the next proofsheet to be output by \.{GFtoDVI}.
+If more than one title is given, they will appear in sequence; titles
+should be short enough to fit on a single line.
+
+\smallskip\hang\noindent
+\.{titlefont\SP}\string. This command, and the other font-naming
+commands below, must precede the first |boc| in the \.{GF} file.
+It overrides the current font used to
+typeset the titles at the top of proofsheets. \.{GFtoDVI} has default
+fonts that will be used if none other are specified; the ``current'' title
+font is initially the default title font.
+
+\smallskip\hang\noindent
+\.{titlefontarea\SP}\string. This command overrides the current
+file area (or directory name) from which \.{GFtoDVI} will try to
+find metric information for the title font.
+
+\smallskip\hang\noindent
+\.{titlefontat} $s$. This command overrides the current ``at size'' that
+will be used for the title font. (See the discussion of font metric files
+below, for the meaning of ``at size'' versus ``design size.'') The
+value of~$s$ is given in units of scaled points.
+
+\okpagebreak
+\smallskip\hang\noindent
+\.{labelfont\SP}\string. This command overrides the current font
+used to typeset the labels that are superimposed on proof figures.
+(The label font is fairly arbitrary, but it should be dark enough to
+stand out when superimposed on gray pixels, and it should contain at
+least the decimal digits and the characters `\.(', `\.)', `\.=', `\.+',
+`\.-', `\.,', and `\..'.)
+
+\smallskip\hang\noindent
+\.{labelfontarea\SP}\string. This command overrides the current
+file area (or directory name) from which \.{GFtoDVI} will try to
+find metric information for the label font.
+
+\smallskip\hang\noindent
+\.{labelfontat} $s$. This command overrides the current ``at size'' that
+will be used for the label font.
+
+\okpagebreak
+\smallskip\hang\noindent
+\.{grayfont\SP}\string. This command overrides the current font
+used to typeset the black pixels and the dots for labels. (Gray fonts
+will be explained in detail later.)
+@^gray fonts@>
+
+\smallskip\hang\noindent
+\.{grayfontarea\SP}\string. This command overrides the current
+file area (or directory name) from which \.{GFtoDVI} will try to
+find metric information for the gray font.
+
+\smallskip\hang\noindent
+\.{grayfontat} $s$. This command overrides the current ``at size'' that
+will be used for the gray font.
+
+\okpagebreak
+\smallskip\hang\noindent
+\.{slantfont\SP}\string. This command overrides the current font
+used to typeset rules that are neither horizontal nor vertical. (Slant
+fonts will be explained in detail later.)
+@^slant fonts@>
+
+\smallskip\hang\noindent
+\.{slantfontarea\SP}\string. This command overrides the current
+file area (or directory name) from which \.{GFtoDVI} will try to
+find metric information for the slant font.
+
+\smallskip\hang\noindent
+\.{slantfontat} $s$. This command overrides the current ``at size'' that
+will be used for the slant font.
+
+\okpagebreak
+\smallskip\hang\noindent
+\.{rulethickness} $t$. This command overrides the current value used
+for the thickness of rules. If the current value is negative, no rule
+will be drawn; if the current value is zero, the rule thickness will
+be specified by a parameter of the gray font. Each \.{rule} command
+uses the rule thickness that is current at the time the command appears;
+hence it is possible to get different thicknesses of rules on the same
+figure. The value of $t$ is given in units of scaled points (\TeX's `\.{sp}').
+At the beginning of each character the current rule thickness is zero.
+
+\smallskip\hang\noindent
+\.{offset} $x$ $y$. This command overrides the current offset values
+that are added to all coordinates of a character being output; $x$ and
+$y$ are given as scaled \MF\ coordinates. This simply has the effect
+of repositioning the figures on the pages; the title line always appears
+in the same place, but the figure can be moved up, down, left, or right.
+At the beginning of each character the current offsets are zero.
+
+\smallskip\hang\noindent
+\.{xoffset} $x$. This command is output by \MF\ just before shipping out
+a character whose $x$~offset is nonzero. \.{GFtoDVI} adds the specified
+amount to the $x$ coordinates of all dots, labels, and rules
+in the following character.
+
+\smallskip\hang\noindent
+\.{yoffset} $y$. This command is output by \MF\ just before shipping out
+a character whose $y$~offset is nonzero. \.{GFtoDVI} adds the specified
+amount to the $y$ coordinates of all dots, labels, and rules
+in the following character.
+
+@* Font metric data.
+Before we can get into the meaty details of \.{GFtoDVI}, we need to
+deal with yet another esoteric binary file format, since \.{GFtoDVI}
+also does elementary typesetting operations. Therefore it has to
+read important information about the fonts it will be using.
+The following material (again copied almost verbatim from \TeX)
+describes the contents of so-called \TeX\ font metric (\.{TFM}) files.
+
+The idea behind \.{TFM} files is that typesetting routines
+need a compact way to store the relevant information about
+fonts, and computer centers need a compact way to store the
+relevant information about several hundred fonts. \.{TFM} files are
+compact, and most of the information they contain is highly relevant,
+so they provide a solution to the problem. \.{GFtoDVI} uses only
+four fonts, but interesting changes in its output will occur when
+those fonts are varied.
+
+The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
+Since the number of bytes is always a multiple of 4, we could
+also regard the file as a sequence of 32-bit words; but \TeX\ uses the
+byte interpretation, and so does \.{GFtoDVI}. The individual bytes
+are considered to be unsigned numbers.
+
+@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
+integers that give the lengths of the various subsequent portions
+of the file. These twelve integers are, in order:
+$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
+|@!lf|&length of the entire file, in words;\cr
+|@!lh|&length of the header data, in words;\cr
+|@!bc|&smallest character code in the font;\cr
+|@!ec|&largest character code in the font;\cr
+|@!nw|&number of words in the width table;\cr
+|@!nh|&number of words in the height table;\cr
+|@!nd|&number of words in the depth table;\cr
+|@!ni|&number of words in the italic correction table;\cr
+|@!nl|&number of words in the lig/kern table;\cr
+|@!nk|&number of words in the kern table;\cr
+|@!ne|&number of words in the extensible character table;\cr
+|@!np|&number of font parameter words.\cr}}$$
+They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
+and
+$$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
+Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
+and as few as 0 characters (if |bc=ec+1|). When two or more 8-bit bytes are
+combined to form an integer of 16 or more bits, the bytes appear in
+BigEndian order.
+@^BigEndian order@>
+
+@<Glob...@>=
+@!lf,@!lh,@!bc,@!ec,@!nw,@!nh,@!nd,@!ni,@!nl,@!nk,@!ne,@!np:0..@'77777;
+ {subfile sizes}
+
+@ The rest of the \.{TFM} file may be regarded as a sequence of ten data
+arrays having the informal specification
+$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
+\vbox{\halign{\hfil\\{#}&$\,:\,$\arr#\hfil\cr
+header&|[0..lh-1]@t\\{stuff}@>|\cr
+char\_info&|[bc..ec]char_info_word|\cr
+width&|[0..nw-1]fix_word|\cr
+height&|[0..nh-1]fix_word|\cr
+depth&|[0..nd-1]fix_word|\cr
+italic&|[0..ni-1]fix_word|\cr
+lig\_kern&|[0..nl-1]lig_kern_command|\cr
+kern&|[0..nk-1]fix_word|\cr
+exten&|[0..ne-1]extensible_recipe|\cr
+param&|[1..np]fix_word|\cr}}$$
+The most important data type used here is a |@!fix_word|, which is
+a 32-bit representation of a binary fraction. A |fix_word| is a signed
+quantity, with the two's complement of the entire word used to represent
+negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
+binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
+the smallest is $-2048$. We will see below, however, that all but two of
+the |fix_word| values must lie between $-16$ and $+16$.
+
+@ The first data array is a block of header information, which contains
+general facts about the font. The header must contain at least two words,
+and for \.{TFM} files to be used with Xerox printing software it must
+contain at least 18 words, allocated as described below. When different
+kinds of devices need to be interfaced, it may be necessary to add further
+words to the header block.
+
+\yskip\hang|header[0]| is a 32-bit check sum that \.{GFtoDVI} will copy into the
+\.{DVI} output file whenever it uses the font. Later on when the \.{DVI}
+file is printed, possibly on another computer, the actual font that gets
+used is supposed to have a check sum that agrees with the one in the
+\.{TFM} file used by \.{GFtoDVI}. In this way, users will be warned about
+potential incompatibilities. (However, if the check sum is zero in either
+the font file or the \.{TFM} file, no check is made.) The actual relation
+between this check sum and the rest of the \.{TFM} file is not important;
+the check sum is simply an identification number with the property that
+incompatible fonts almost always have distinct check sums.
+@^check sum@>
+
+\yskip\hang|header[1]| is a |fix_word| containing the design size of the
+font, in units of \TeX\ points (7227 \TeX\ points = 254 cm). This number
+must be at least 1.0; it is fairly arbitrary, but usually the design size
+is 10.0 for a ``10 point'' font, i.e., a font that was designed to look
+best at a 10-point size, whatever that really means. When a \TeX\ user
+asks for a font `\.{at} $\delta$ \.{pt}', the effect is to override the
+design size and replace it by $\delta$, and to multiply the $x$ and~$y$
+coordinates of the points in the font image by a factor of $\delta$
+divided by the design size. Similarly, specific sizes can be substituted
+for the design size by \.{GFtoDVI} commands like `\.{titlefontat}'. {\sl
+All other dimensions in the\/\ \.{TFM} file are |fix_word| numbers in
+design-size units.} Thus, for example, the value of |param[6]|, one \.{em}
+or \.{\\quad}, is often the |fix_word| value $2^{20}=1.0$, since many
+fonts have a design size equal to one em. The other dimensions must be
+less than 16 design-size units in absolute value; thus, |header[1]| and
+|param[1]| are the only |fix_word| entries in the whole \.{TFM} file whose
+first byte might be something besides 0 or 255. @^design size@>@^at size@>
+
+\yskip\hang|header[2..11]|, if present, contains 40 bytes that identify
+the character coding scheme. The first byte, which must be between 0 and
+39, is the number of subsequent ASCII bytes actually relevant in this
+string, which is intended to specify what character-code-to-symbol
+convention is present in the font. Examples are \.{ASCII} for standard
+ASCII, \.{TeX text} for fonts like \.{cmr10} and \.{cmti9}, \.{TeX math
+extension} for \.{cmex10}, \.{XEROX text} for Xerox fonts, \.{GRAPHIC} for
+special-purpose non-alphabetic fonts, \.{GFGRAY} for \.{GFtoDVI}'s
+gray fonts, \.{GFSLANT} for \.{GFtoDVI}'s slant fonts, \.{UNSPECIFIED} for
+the default case when there is no information. Parentheses should not
+appear in this name. (Such a string is said to be in {\mc BCPL} format.)
+@^coding scheme@>@^gray fonts@>@^slant fonts@>
+
+\yskip\hang|header[12..@twhatever@>]| might also be present.
+
+@ Next comes the |char_info| array, which contains one |char_info_word|
+per character. Each |char_info_word| contains six fields packed into
+four bytes as follows.
+
+\yskip\hang first byte: |@!width_index| (8 bits)\par
+\hang second byte: |@!height_index| (4 bits) times 16, plus |@!depth_index|
+ (4~bits)\par
+\hang third byte: |@!italic_index| (6 bits) times 4, plus |@!tag|
+ (2~bits)\par
+\hang fourth byte: |@!remainder| (8 bits)\par
+\yskip\noindent
+The actual width of a character is \\{width}|[width_index]|, in design-size
+units; this is a device for compressing information, since many characters
+have the same width. Since it is quite common for many characters
+to have the same height, depth, or italic correction, the \.{TFM} format
+imposes a limit of 16 different heights, 16 different depths, and
+64 different italic corrections.
+
+Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
+\\{italic}[0]=0$ should always hold, so that an index of zero implies a
+value of zero. The |width_index| should never be zero unless the
+character does not exist in the font, since a character is valid if and
+only if it lies between |bc| and |ec| and has a nonzero |width_index|.
+
+@ The |tag| field in a |char_info_word| has four values that explain how to
+interpret the |remainder| field.
+
+\yskip\hang|tag=0| (|no_tag|) means that |remainder| is unused.\par
+\hang|tag=1| (|lig_tag|) means that this character has a ligature/kerning
+program starting at |lig_kern[remainder]|.\par
+\hang|tag=2| (|list_tag|) means that this character is part of a chain of
+characters of ascending sizes, and not the largest in the chain. The
+|remainder| field gives the character code of the next larger character.\par
+\hang|tag=3| (|ext_tag|) means that this character code represents an
+extensible character, i.e., a character that is built up of smaller pieces
+so that it can be made arbitrarily large. The pieces are specified in
+|@!exten[remainder]|.\par
+
+@d no_tag=0 {vanilla character}
+@d lig_tag=1 {character has a ligature/kerning program}
+@d list_tag=2 {character has a successor in a charlist}
+@d ext_tag=3 {character is extensible}
+
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word in this array is a
+|@!lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+ step if the byte is 128 or more, otherwise the next step is obtained by
+ skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+ then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+ a kern step otherwise.\par
+\hang fourth byte: |remainder|.\par
+\yskip\noindent
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|remainder| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+If the very first instruction of the |lig_kern| array has |skip_byte=255|,
+the |next_char| byte is the so-called right boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte=255|,
+there is a special ligature/kerning program for a left boundary character,
+beginning at location |256*op_byte+remainder|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte>128|, the program actually begins in location
+|256*op_byte+remainder|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location |<=255|.
+
+Any instruction with |skip_byte>128| in the |lig_kern| array must have
+|256*op_byte+remainder<nl|. If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+or kerning command is performed.
+
+@d stop_flag=128 {value indicating `\.{STOP}' in a lig/kern program}
+@d kern_flag=128 {op code for a kern step}
+
+@ Extensible characters are specified by an |@!extensible_recipe|, which
+consists of four bytes called |@!top|, |@!mid|, |@!bot|, and |@!rep| (in this
+order). These bytes are the character codes of individual pieces used to
+build up a large symbol. If |top|, |mid|, or |bot| are zero, they are not
+present in the built-up result. For example, an extensible vertical line is
+like an extensible bracket, except that the top and bottom pieces are missing.
+
+@ The final portion of a \.{TFM} file is the |param| array, which is another
+sequence of |fix_word| values.
+
+\yskip\hang|param[1]=@!slant| is the amount of italic slant.
+For example, |slant=.25| means that when you go
+up one unit, you also go .25 units to the right. The |slant| is a pure
+number; it's the only |fix_word| other than the design size itself that is
+not scaled by the design size.
+
+\hang|param[2]=space| is the normal spacing between words in text.
+Note that character |" "| in the font need not have anything to do with
+blank spaces.
+
+\hang|param[3]=space_stretch| is the amount of glue stretching between words.
+
+\hang|param[4]=space_shrink| is the amount of glue shrinking between words.
+
+\hang|param[5]=x_height| is the height of letters for which accents don't
+have to be raised or lowered.
+
+\hang|param[6]=quad| is the size of one em in the font.
+
+\hang|param[7]=extra_space| is the amount added to |param[2]| at the
+ends of sentences.
+
+When the character coding scheme is \.{GFGRAY} or \.{GFSLANT}, the font is
+supposed to contain an additional parameter called
+|default_rule_thickness|. Other special parameters go with other coding
+schemes.
+
+@* Input from binary files.
+We have seen that \.{GF} and \.{DVI} and \.{TFM} files are sequences of
+8-bit bytes. The bytes appear physically in what is called a `|packed
+file of 0..255|' in \PASCAL\ lingo.
+
+Packing is system dependent, and many \PASCAL\ systems fail to implement
+such files in a sensible way (at least, from the viewpoint of producing
+good production software). For example, some systems treat all
+byte-oriented files as text, looking for end-of-line marks and such
+things. Therefore some system-dependent code is often needed to deal with
+binary files, even though most of the program in this section of
+\.{GFtoDVI} is written in standard \PASCAL.
+@^system dependencies@>
+
+One common way to solve the problem is to consider files of |integer|
+numbers, and to convert an integer in the range $-2^{31}\L x<2^{31}$ to
+a sequence of four bytes $(a,b,c,d)$ using the following code, which
+avoids the controversial integer division of negative numbers:
+$$\vbox{\halign{#\hfil\cr
+|if x>=0 then a:=x div @'100000000|\cr
+|else begin x:=(x+@'10000000000)+@'10000000000; a:=x div @'100000000+128;|\cr
+\quad|end|;\cr
+|x:=x mod @'100000000;|\cr
+|b:=x div @'200000; x:=x mod @'200000;|\cr
+|c:=x div @'400; d:=x mod @'400;|\cr}}$$
+The four bytes are then kept in a buffer and output one by one. (On 36-bit
+computers, an additional division by 16 is necessary at the beginning.
+Another way to separate an integer into four bytes is to use/abuse
+\PASCAL's variant records, storing an integer and retrieving bytes that are
+packed in the same place; {\sl caveat implementor!\/}) It is also desirable
+in some cases to read a hundred or so integers at a time, maintaining a
+larger buffer.
+
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types ...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+
+@ The program deals with three binary file variables: |gf_file| is the main
+input file that we are converting into a document; |dvi_file| is the main
+output file that will specify that document; and |tfm_file| is
+the current font metric file from which character-width information is
+being read.
+
+@<Glob...@>=
+@!gf_file:byte_file; {the character data we are reading}
+@!dvi_file:byte_file; {the typesetting instructions we are writing}
+@!tfm_file:byte_file; {a font metric file}
+
+@ To prepare these files for input or output, we |reset| or |rewrite|
+them. An extension of \PASCAL\ is needed, since we want to associate
+it with external files whose names are specified dynamically (i.e., not
+known at compile time). The following code assumes that `|reset(f,s)|' and
+`|rewrite(f,s)|' do this, when |f| is a file variable and |s| is a string
+variable that specifies the file name.
+@^system dependencies@>
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin reset(gf_file,name_of_file);
+cur_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin reset(tfm_file,name_of_file);
+end;
+@#
+procedure open_dvi_file; {prepares to write packed bytes in |dvi_file|}
+begin rewrite(dvi_file,name_of_file);
+end;
+
+@ If you looked carefully at the preceding code, you probably asked,
+``What are |cur_loc| and |name_of_file|?'' Good question. They are global
+variables: The integer |cur_loc| tells which byte of the input file will
+be read next, and the string |name_of_file| will be set to the current
+file name before the file-opening procedures are called.
+
+@<Glob...@>=
+@!cur_loc:integer; {current byte number in |gf_file|}
+@!name_of_file:packed array[1..file_name_size] of char; {external file name}
+
+@ It turns out to be convenient to read four bytes at a time, when we are
+inputting from \.{TFM} files. The input goes into global variables
+|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
+the fourth.
+
+@<Glob...@>=
+@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once}
+
+@ The |read_tfm_word| procedure sets |b0| through |b3| to the next
+four bytes in the current \.{TFM} file.
+@^system dependencies@>
+
+@p procedure read_tfm_word;
+begin read(tfm_file,b0); read(tfm_file,b1);
+read(tfm_file,b2); read(tfm_file,b3);
+end;
+
+@ We shall use another set of simple functions to read the next byte or
+bytes from |gf_file|. There are four possibilities, each of which is
+treated as a separate function in order to minimize the overhead for
+subroutine calls.
+@^system dependencies@>
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(gf_file,a); read(gf_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+
+@* Reading the font information.
+Now let's get down to brass tacks and consider the more substantial
+routines that actually convert \.{TFM} data into a form suitable for
+computation. The routines in this part of the program have been borrowed
+from \TeX, with slight changes, since \.{GFtoDVI} has to do some of the
+things that \TeX\ does.
+
+The \.{TFM} data is stored in a large array called
+|font_info|. Each item of |font_info| is a |memory_word|; the |fix_word|
+data gets converted into |scaled| entries, while everything else goes into
+words of type |four_quarters|. (These data structures are special cases of
+the more general memory words of \TeX. On some machines it is necessary to
+define |min_quarterword=-128| and |max_quarterword=127| in order to pack
+four quarterwords into a single word.)
+@^system dependencies@>
+
+@d min_quarterword=0 {change this to allow efficient packing, if necessary}
+@d max_quarterword=255 {ditto}
+@d qi(#)==#+min_quarterword
+ {to put an |eight_bits| item into a quarterword}
+@d qo(#)==#-min_quarterword
+ {to take an |eight_bits| item out of a quarterword}
+@d title_font=1
+@d label_font=2
+@d gray_font=3
+@d slant_font=4
+@d logo_font=5
+@d non_char==qi(256)
+@d non_address==font_mem_size
+
+@<Types ...@>=
+@!font_index = 0..font_mem_size;
+@!quarterword = min_quarterword..max_quarterword; {1/4 of a word}
+@!four_quarters = packed record@;@/
+ @!b0:quarterword;
+ @!b1:quarterword;
+ @!b2:quarterword;
+ @!b3:quarterword;
+ end;
+@!memory_word = record@;@/
+ case boolean of
+ true: (@!sc:scaled);
+ false: (@!qqqq:four_quarters);
+ end;
+@!internal_font_number=title_font..logo_font;
+
+@ Besides |font_info|, there are also a number of index arrays that point
+into it, so that we can locate width and height information, etc. For
+example, the |char_info| data for character |c| in font |f| will be in
+|font_info[char_base[f]+c].qqqq|; and if |w| is the |width_index| part of
+this word (the |b0| field), the width of the character is
+|font_info[width_base[f]+w].sc|. (These formulas assume that
+|min_quarterword| has already been added to |w|, but not to |c|.)
+
+@<Glob...@>=
+@!font_info:array[font_index] of memory_word; {the font metric data}
+@!fmem_ptr:font_index; {first unused word of |font_info|}
+@!font_check:array[internal_font_number] of four_quarters; {check sum}
+@!font_size:array[internal_font_number] of scaled; {``at'' size}
+@!font_dsize:array[internal_font_number] of scaled; {``design'' size}
+@!font_bc:array[internal_font_number] of eight_bits;
+ {beginning (smallest) character code}
+@!font_ec:array[internal_font_number] of eight_bits;
+ {ending (largest) character code}
+@!char_base:array[internal_font_number] of integer;
+ {base addresses for |char_info|}
+@!width_base:array[internal_font_number] of integer;
+ {base addresses for widths}
+@!height_base:array[internal_font_number] of integer;
+ {base addresses for heights}
+@!depth_base:array[internal_font_number] of integer;
+ {base addresses for depths}
+@!italic_base:array[internal_font_number] of integer;
+ {base addresses for italic corrections}
+@!lig_kern_base:array[internal_font_number] of integer;
+ {base addresses for ligature/kerning programs}
+@!kern_base:array[internal_font_number] of integer;
+ {base addresses for kerns}
+@!exten_base:array[internal_font_number] of integer;
+ {base addresses for extensible recipes}
+@!param_base:array[internal_font_number] of integer;
+ {base addresses for font parameters}
+@!bchar_label:array[internal_font_number] of font_index;
+ {start of |lig_kern| program for left boundary character,
+ |non_address| if there is none}
+@!font_bchar:array[internal_font_number] of min_quarterword..non_char;
+ {right boundary character, |non_char| if there is none}
+
+@ @<Set init...@>=
+fmem_ptr:=0;
+
+@ Of course we want to define macros that suppress the detail of how font
+information is actually packed, so that we don't have to write things like
+$$\hbox{|font_info[width_base[f]+font_info[char_base[f]+c].qqqq.b0].sc|}$$
+too often. The \.{WEB} definitions here make |char_info(f)(c)| the
+|four_quarters| word of font information corresponding to character
+|c| of font |f|. If |q| is such a word, |char_width(f)(q)| will be
+the character's width; hence the long formula above is at least
+abbreviated to
+$$\hbox{|char_width(f)(char_info(f)(c))|.}$$
+In practice we will try to fetch |q| first and look at several of its
+fields at the same time.
+
+The italic correction of a character will be denoted by
+|char_italic(f)(q)|, so it is analogous to |char_width|. But we will get
+at the height and depth in a slightly different way, since we usually want
+to compute both height and depth if we want either one. The value of
+|height_depth(q)| will be the 8-bit quantity
+$$b=|height_index|\times16+|depth_index|,$$ and if |b| is such a byte we
+will write |char_height(f)(b)| and |char_depth(f)(b)| for the height and
+depth of the character |c| for which |q=char_info(f)(c)|. Got that?
+
+The tag field will be called |char_tag(q)|; and the remainder byte will be
+called |rem_byte(q)|.
+
+@d char_info_end(#)==#].qqqq
+@d char_info(#)==font_info[char_base[#]+char_info_end
+@d char_width_end(#)==#.b0].sc
+@d char_width(#)==font_info[width_base[#]+char_width_end
+@d char_exists(#)==(#.b0>min_quarterword)
+@d char_italic_end(#)==(qo(#.b2)) div 4].sc
+@d char_italic(#)==font_info[italic_base[#]+char_italic_end
+@d height_depth(#)==qo(#.b1)
+@d char_height_end(#)==(#) div 16].sc
+@d char_height(#)==font_info[height_base[#]+char_height_end
+@d char_depth_end(#)==# mod 16].sc
+@d char_depth(#)==font_info[depth_base[#]+char_depth_end
+@d char_tag(#)==((qo(#.b2)) mod 4)
+@d skip_byte(#)==qo(#.b0)
+@d next_char(#)==#.b1
+@d op_byte(#)==qo(#.b2)
+@d rem_byte(#)==#.b3
+
+@ Here are some macros that help process ligatures and kerns.
+We write |char_kern(f)(j)| to find the amount of kerning specified by
+kerning command~|j| in font~|f|.
+
+@d lig_kern_start(#)==lig_kern_base[#]+rem_byte {beginning of lig/kern program}
+@d lig_kern_restart_end(#)==256*(op_byte(#))+rem_byte(#)
+@d lig_kern_restart(#)==lig_kern_base[#]+lig_kern_restart_end
+@d char_kern_end(#)==256*(op_byte(#)-128)+rem_byte(#)].sc
+@d char_kern(#)==font_info[kern_base[#]+char_kern_end
+
+@ Font parameters are referred to as |slant(f)|, |space(f)|, etc.
+
+@d param_end(#)==param_base[#]].sc
+@d param(#)==font_info[#+param_end
+@d slant==param(1) {slant to the right, per unit distance upward}
+@d space==param(2) {normal space between words}
+@d x_height==param(5) {one ex}
+@d default_rule_thickness==param(8) {thickness of rules}
+
+@ Here is the subroutine that inputs the information on |tfm_file|, assuming
+that the file has just been reset. Parameter~|f| tells which metric file is
+being read (either |title_font| or |label_font| or |gray_font| or |slant_font|
+or |logo_font|); parameter~|s| is the ``at'' size, which will be
+substituted for the design size if it is positive.
+
+This routine does only limited checking of the validity of the file,
+because another program (\.{TFtoPL}) is available to diagnose errors in
+the rare case that something is amiss.
+
+@d bad_tfm=11 {label for |read_font_info|}
+@d abend==goto bad_tfm {do this when the \.{TFM} data is wrong}
+
+@p procedure read_font_info(@!f:integer;@!s:scaled); {input a \.{TFM} file}
+label done,bad_tfm;
+var k:font_index; {index into |font_info|}
+@!lf,@!lh,@!bc,@!ec,@!nw,@!nh,@!nd,@!ni,@!nl,@!nk,@!ne,@!np:0..65535;
+ {sizes of subfiles}
+@!bch_label:integer; {left boundary label for ligatures}
+@!bchar:0..256; {right boundary character for ligatures}
+@!qw:four_quarters;@!sw:scaled; {accumulators}
+@!z:scaled; {the design size or the ``at'' size}
+@!alpha:integer;@!beta:1..16;
+ {auxiliary quantities used in fixed-point multiplication}
+begin @<Read and check the font data; |abend| if the \.{TFM} file is
+ malformed; otherwise |goto done|@>;
+bad_tfm: print_nl('Bad TFM file for');
+@.Bad TFM file...@>
+case f of
+title_font:abort('titles!');
+label_font:abort('labels!');
+gray_font:abort('pixels!');
+slant_font:abort('slants!');
+logo_font:abort('METAFONT logo!');
+end; {there are no other cases}
+done: {it might be good to close |tfm_file| now}
+end;
+
+@ @<Read and check...@>=
+@<Read the {\.{TFM}} size fields@>;
+@<Use size fields to allocate font information@>;
+@<Read the {\.{TFM}} header@>;
+@<Read character data@>;
+@<Read box dimensions@>;
+@<Read ligature/kern program@>;
+@<Read extensible character recipes@>;
+@<Read font parameters@>;
+@<Make final adjustments and |goto done|@>
+
+@ @d read_two_halves_end(#)==#:=b2*256+b3
+@d read_two_halves(#)==read_tfm_word; #:=b0*256+b1; read_two_halves_end
+
+@<Read the {\.{TFM}} size fields@>=
+begin read_two_halves(lf)(lh);
+read_two_halves(bc)(ec);
+if (bc>ec+1)or(ec>255) then abend;
+if bc>255 then {|bc=256| and |ec=255|}
+ begin bc:=1; ec:=0;
+ end;
+read_two_halves(nw)(nh);
+read_two_halves(nd)(ni);
+read_two_halves(nl)(nk);
+read_two_halves(ne)(np);
+if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then abend;
+end
+
+@ The preliminary settings of the index variables |width_base|,
+|lig_kern_base|, |kern_base|, and |exten_base| will be corrected later by
+subtracting |min_quarterword| from them; and we will subtract 1 from
+|param_base| too. It's best to forget about such anomalies until later.
+
+@<Use size fields to allocate font information@>=
+lf:=lf-6-lh; {|lf| words should be loaded into |font_info|}
+if np<8 then lf:=lf+8-np; {at least eight parameters will appear}
+if fmem_ptr+lf>font_mem_size then abort('No room for TFM file!');
+@.No room for TFM file@>
+char_base[f]:=fmem_ptr-bc;
+width_base[f]:=char_base[f]+ec+1;
+height_base[f]:=width_base[f]+nw;
+depth_base[f]:=height_base[f]+nh;
+italic_base[f]:=depth_base[f]+nd;
+lig_kern_base[f]:=italic_base[f]+ni;
+kern_base[f]:=lig_kern_base[f]+nl;
+exten_base[f]:=kern_base[f]+nk;
+param_base[f]:=exten_base[f]+ne
+
+@ Only the first two words of the header are needed by \.{GFtoDVI}.
+
+@d store_four_quarters(#)==
+ begin read_tfm_word;
+ qw.b0:=qi(b0); qw.b1:=qi(b1); qw.b2:=qi(b2); qw.b3:=qi(b3);
+ #:=qw;
+ end
+
+@<Read the {\.{TFM}} header@>=
+begin if lh<2 then abend;
+store_four_quarters(font_check[f]);
+read_tfm_word;
+if b0>127 then abend; {design size must be positive}
+z:=((b0*256+b1)*256+b2)*16+(b3 div 16);
+if z<unity then abend;
+while lh>2 do
+ begin read_tfm_word; decr(lh); {ignore the rest of the header}
+ end;
+font_dsize[f]:=z;
+if s>0 then z:=s;
+font_size[f]:=z;
+end
+
+@ @<Read character data@>=
+for k:=fmem_ptr to width_base[f]-1 do
+ begin store_four_quarters(font_info[k].qqqq);
+ if (b0>=nw)or(b1 div @'20>=nh)or(b1 mod @'20>=nd)or
+ (b2 div 4>=ni) then abend;
+ case b2 mod 4 of
+ lig_tag: if b3>=nl then abend;
+ ext_tag: if b3>=ne then abend;
+ no_tag,list_tag: do_nothing;
+ end; {there are no other cases}
+ end
+
+@ A |fix_word| whose four bytes are $(b0,b1,b2,b3)$ from left to right
+represents the number
+$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
+b_1\cdot2^{-4}+b_2\cdot2^{-12}+b_3\cdot2^{-20}&b_0=0;\cr
+-16+b_1\cdot2^{-4}+b_2\cdot2^{-12}+b_3\cdot2^{-20}&b_0=255.\cr}}\right.$$
+(No other choices of |b0| are allowed, since the magnitude of a number in
+design-size units must be less than 16.) We want to multiply this
+quantity by the integer~|z|, which is known to be less than $2^{27}$. Let
+$\alpha=16z$. If $|z|<2^{23}$, the individual multiplications $b\cdot z$,
+$c\cdot z$, $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2,
+4, 8, or 16, to obtain a multiplier less than $2^{23}$, and we can
+compensate for this later. If |z| has thereby been replaced by
+$|z|^\prime=|z|/2^e$, let $\beta=2^{4-e}$; we shall compute
+$$\lfloor(b_1+b_2\cdot2^{-8}+b_3\cdot2^{-16})\,z^\prime/\beta\rfloor$$
+if $a=0$, or the same quantity minus $\alpha$ if $a=255$.
+
+@d store_scaled(#)==begin read_tfm_word;
+ sw:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
+ if b0=0 then #:=sw@+else if b0=255 then #:=sw-alpha@+else abend;
+ end
+
+@<Read box dimensions@>=
+begin @<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
+for k:=width_base[f] to lig_kern_base[f]-1 do
+ store_scaled(font_info[k].sc);
+if font_info[width_base[f]].sc<>0 then abend; {\\{width}[0] must be zero}
+if font_info[height_base[f]].sc<>0 then abend; {\\{height}[0] must be zero}
+if font_info[depth_base[f]].sc<>0 then abend; {\\{depth}[0] must be zero}
+if font_info[italic_base[f]].sc<>0 then abend; {\\{italic}[0] must be zero}
+end
+
+@ @<Replace |z|...@>=
+begin alpha:=16*z; beta:=16;
+while z>=@'40000000 do
+ begin z:=z div 2; beta:=beta div 2;
+ end;
+end
+
+@ @d check_byte_range(#)==begin if (#<bc)or(#>ec) then abend@+end
+
+@<Read ligature/kern program@>=
+begin bch_label:=@'77777; bchar:=256;
+if nl>0 then
+ begin for k:=lig_kern_base[f] to kern_base[f]-1 do
+ begin store_four_quarters(font_info[k].qqqq);
+ if b0>stop_flag then
+ begin if 256*b2+b3>=nl then abend;
+ if b0=255 then if k=lig_kern_base[f] then bchar:=b1;
+ end
+ else begin if b1<>bchar then check_byte_range(b1);
+ if b2<kern_flag then check_byte_range(b3)
+ else if 256*(b2-128)+b3>=nk then abend;
+ end;
+ end;
+ if b0=255 then bch_label:=256*b2+b3;
+ end;
+for k:=kern_base[f] to exten_base[f]-1 do
+ store_scaled(font_info[k].sc);
+end
+
+@ @<Read extensible character recipes@>=
+for k:=exten_base[f] to param_base[f]-1 do
+ begin store_four_quarters(font_info[k].qqqq);
+ if b0<>0 then check_byte_range(b0);
+ if b1<>0 then check_byte_range(b1);
+ if b2<>0 then check_byte_range(b2);
+ check_byte_range(b3);
+ end
+
+@ @<Read font parameters@>=
+begin for k:=1 to np do
+ if k=1 then {the |slant| parameter is a pure number}
+ begin read_tfm_word;
+ if b0>127 then sw:=b0-256@+else sw:=b0;
+ sw:=sw*@'400+b1; sw:=sw*@'400+b2;
+ font_info[param_base[f]].sc:=(sw*@'20)+(b3 div@'20);
+ end
+ else store_scaled(font_info[param_base[f]+k-1].sc);
+for k:=np+1 to 8 do font_info[param_base[f]+k-1].sc:=0;
+end
+
+@ Now to wrap it up, we have checked all the necessary things about the \.{TFM}
+file, and all we need to do is put the finishing touches on the data for
+the new font.
+
+@d adjust(#)==#[f]:=qo(#[f])
+ {correct for the excess |min_quarterword| that was added}
+
+@<Make final adjustments...@>=
+font_bc[f]:=bc; font_ec[f]:=ec;
+if bch_label<nl then bchar_label[f]:=bch_label+lig_kern_base[f]
+else bchar_label[f]:=non_address;
+font_bchar[f]:=qi(bchar);
+adjust(width_base); adjust(lig_kern_base);
+adjust(kern_base); adjust(exten_base);
+decr(param_base[f]);
+fmem_ptr:=fmem_ptr+lf; goto done
+
+@* The string pool.
+\.{GFtoDVI} remembers strings by putting them into an array called
+|str_pool|. The |str_start| array tells where each string starts in the pool.
+
+@<Types ...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+
+@ As new strings enter, we keep track of the storage currently used, by
+means of two global variables called |pool_ptr| and |str_ptr|. These are
+periodically reset to their initial values when we move from one character
+to another, because most strings are of only temporary interest.
+
+@<Glob...@>=
+@!str_pool:packed array[pool_pointer] of ASCII_code; {the characters}
+@!str_start : array[str_number] of pool_pointer; {the starting pointers}
+@!pool_ptr : pool_pointer; {first unused position in |str_pool|}
+@!str_ptr : str_number; {start of the current string being created}
+@!init_str_ptr:str_number; {|str_ptr| setting when a new character starts}
+
+@ Several of the elementary string operations are performed using \.{WEB}
+macros instead of using \PASCAL\ procedures, because many of the
+operations are done quite frequently and we want to avoid the
+overhead of procedure calls. For example, here is
+a simple macro that computes the length of a string.
+@.WEB@>
+
+@d length(#)==(str_start[#+1]-str_start[#]) {the number of characters
+ in string number \#}
+
+@ Strings are created by appending character codes to |str_pool|.
+The macro called |append_char|, defined here, does not check to see if the
+value of |pool_ptr| has gotten too high; that test is supposed to be
+made before |append_char| is used.
+
+To test if there is room to append |l| more characters to |str_pool|,
+we shall write |str_room(l)|, which aborts \.{GFtoDVI} and gives an
+apologetic error message if there isn't enough room.
+
+@d append_char(#) == {put |ASCII_code| \# at the end of |str_pool|}
+begin str_pool[pool_ptr]:=#; incr(pool_ptr);
+end
+@d str_room(#) == {make sure that the pool hasn't overflowed}
+ begin if pool_ptr+# > pool_size then
+ abort('Too many strings!');
+@.Too many strings@>
+ end
+
+@ Once a sequence of characters has been appended to |str_pool|, it
+officially becomes a string when the function |make_string| is called.
+This function returns the identification number of the new string as its
+value.
+
+@p function make_string : str_number; {current string enters the pool}
+begin if str_ptr=max_strings then
+ abort('Too many labels!');
+@.Too many labels@>
+incr(str_ptr); str_start[str_ptr]:=pool_ptr;
+make_string:=str_ptr-1;
+end;
+
+@ The first strings in the string pool are the keywords that \.{GFtoDVI}
+recognizes in the \\{xxx} commands of a \.{GF} file. They are entered
+into |str_pool| by means of a tedious bunch of assignment statements,
+together with calls on the |first_string| subroutine.
+
+@d init_str0(#)==first_string(#)
+@d init_str1(#)==buffer[1]:=#; init_str0
+@d init_str2(#)==buffer[2]:=#; init_str1
+@d init_str3(#)==buffer[3]:=#; init_str2
+@d init_str4(#)==buffer[4]:=#; init_str3
+@d init_str5(#)==buffer[5]:=#; init_str4
+@d init_str6(#)==buffer[6]:=#; init_str5
+@d init_str7(#)==buffer[7]:=#; init_str6
+@d init_str8(#)==buffer[8]:=#; init_str7
+@d init_str9(#)==buffer[9]:=#; init_str8
+@d init_str10(#)==buffer[10]:=#; init_str9
+@d init_str11(#)==buffer[11]:=#; init_str10
+@d init_str12(#)==buffer[12]:=#; init_str11
+@d init_str13(#)==buffer[13]:=#; init_str12
+@d longest_keyword=13
+
+@p procedure first_string(@!c:integer);
+begin if str_ptr<>c then abort('?'); {internal consistency check}
+@.?@>
+while l>0 do
+ begin append_char(buffer[l]); decr(l);
+ end;
+incr(str_ptr); str_start[str_ptr]:=pool_ptr;
+end;
+
+@ @<Glob...@>=
+@!l:integer; {length of string being made by |first_string|}
+
+@ Here are the tedious assignments just promised.
+String number 0 is the empty string.
+
+@d null_string=0 {the empty keyword}
+@d area_code=4 {add to font code for the `\.{area}' keywords}
+@d at_code=8 {add to font code for the `\.{at}' keywords}
+@d rule_code=13 {code for the keyword `\.{rule}'}
+@d title_code=14 {code for the keyword `\.{title}'}
+@d rule_thickness_code=15 {code for the keyword `\.{rulethickness}'}
+@d offset_code=16 {code for the keyword `\.{offset}'}
+@d x_offset_code=17 {code for the keyword `\.{xoffset}'}
+@d y_offset_code=18 {code for the keyword `\.{yoffset}'}
+@d max_keyword=18 {largest keyword code number}
+
+@<Initialize the strings@>=
+str_ptr:=0; pool_ptr:=0; str_start[0]:=0;@/
+l:=0; init_str0(null_string);@/
+l:=9; init_str9("t")("i")("t")("l")("e")("f")("o")("n")("t")(title_font);@/
+l:=9; init_str9("l")("a")("b")("e")("l")("f")("o")("n")("t")(label_font);@/
+l:=8; init_str8("g")("r")("a")("y")("f")("o")("n")("t")(gray_font);@/
+l:=9; init_str9("s")("l")("a")("n")("t")("f")("o")("n")("t")(slant_font);@/
+l:=13; init_str13("t")("i")("t")("l")("e")
+ ("f")("o")("n")("t")("a")("r")("e")("a")(title_font+area_code);@/
+l:=13; init_str13("l")("a")("b")("e")("l")
+ ("f")("o")("n")("t")("a")("r")("e")("a")(label_font+area_code);@/
+l:=12; init_str12("g")("r")("a")("y")
+ ("f")("o")("n")("t")("a")("r")("e")("a")(gray_font+area_code);@/
+l:=13; init_str13("s")("l")("a")("n")("t")
+ ("f")("o")("n")("t")("a")("r")("e")("a")(slant_font+area_code);@/
+l:=11; init_str11("t")("i")("t")("l")("e")
+ ("f")("o")("n")("t")("a")("t")(title_font+at_code);@/
+l:=11; init_str11("l")("a")("b")("e")("l")
+ ("f")("o")("n")("t")("a")("t")(label_font+at_code);@/
+l:=10; init_str10("g")("r")("a")("y")
+ ("f")("o")("n")("t")("a")("t")(gray_font+at_code);@/
+l:=11; init_str11("s")("l")("a")("n")("t")
+ ("f")("o")("n")("t")("a")("t")(slant_font+at_code);@/
+l:=4; init_str4("r")("u")("l")("e")(rule_code);@/
+l:=5; init_str5("t")("i")("t")("l")("e")(title_code);@/
+l:=13; init_str13("r")("u")("l")("e")
+ ("t")("h")("i")("c")("k")("n")("e")("s")("s")(rule_thickness_code);@/
+l:=6; init_str6("o")("f")("f")("s")("e")("t")(offset_code);@/
+l:=7; init_str7("x")("o")("f")("f")("s")("e")("t")(x_offset_code);@/
+l:=7; init_str7("y")("o")("f")("f")("s")("e")("t")(y_offset_code);@/
+
+@ We will also find it useful to have the following strings. (The names of
+default fonts will presumably be different at different sites.)
+@^system dependencies@>
+@^default fonts@>
+
+@d gf_ext=max_keyword+1 {string number for `\.{.gf}'}
+@d dvi_ext=max_keyword+2 {string number for `\.{.dvi}'}
+@d tfm_ext=max_keyword+3 {string number for `\.{.tfm}'}
+@d page_header=max_keyword+4 {string number for `\.{\ \ Page\ }'}
+@d char_header=max_keyword+5 {string number for `\.{\ \ Character\ }'}
+@d ext_header=max_keyword+6 {string number for `\.{\ \ Ext\ }'}
+@d left_quotes=max_keyword+7 {string number for `\.{\ \ ``}'}
+@d right_quotes=max_keyword+8 {string number for `\.{''}'}
+@d equals_sign=max_keyword+9 {string number for `\.{ = }'}
+@d plus_sign=max_keyword+10 {string number for `\.{ + (}'}
+@d default_title_font=max_keyword+11
+ {string number for the default |title_font|}
+@d default_label_font=max_keyword+12
+ {string number for the default |label_font|}
+@d default_gray_font=max_keyword+13 {string number for the default |gray_font|}
+@d logo_font_name=max_keyword+14 {string number for the font with \MF\ logo}
+@d small_logo=max_keyword+15 {string number for `\.{METAFONT}'}
+@d home_font_area=max_keyword+16 {string number for system-dependent font area}
+
+@<Initialize the strings@>=
+l:=3; init_str3(".")("g")("f")(gf_ext);@/
+l:=4; init_str4(".")("d")("v")("i")(dvi_ext);@/
+l:=4; init_str4(".")("t")("f")("m")(tfm_ext);@/
+l:=7; init_str7(" ")(" ")("P")("a")("g")("e")(" ")(page_header);@/
+l:=12; init_str12(" ")(" ")("C")("h")("a")("r")("a")("c")("t")("e")("r")(" ")
+ (char_header);@/
+l:=6; init_str6(" ")(" ")("E")("x")("t")(" ")(ext_header);@/
+l:=4; init_str4(" ")(" ")("`")("`")(left_quotes);@/
+l:=2; init_str2("'")("'")(right_quotes);@/
+l:=3; init_str3(" ")("=")(" ")(equals_sign);@/
+l:=4; init_str4(" ")("+")(" ")("(")(plus_sign);@/
+l:=4; init_str4("c")("m")("r")("8")(default_title_font);@/
+l:=6; init_str6("c")("m")("t")("t")("1")("0")(default_label_font);@/
+l:=4; init_str4("g")("r")("a")("y")(default_gray_font);@/
+l:=5; init_str5("l")("o")("g")("o")("8")(logo_font_name);@/
+l:=8; init_str8("M")("E")("T")("A")("F")("O")("N")("T")(small_logo);
+
+@ If an \\{xxx} command has just been encountered in the \.{GF} file,
+the following procedure interprets its keyword. More precisely, we assume
+that |cur_gf| contains an op-code byte just read from the \.{GF} file,
+where |xxx1<=cur_gf<=no_op|. The |interpret_xxx| procedure will read the
+rest of the command, in the following way:
+\smallskip
+\item{1)} If |cur_gf| is |no_op| or |yyy|, or if it's an \\{xxx} command with
+an unknown keyword, the bytes are simply read and ignored, and the
+value |no_operation| is returned.
+
+\item{2)} If |cur_gf| is an \\{xxx} command (either |xxx1| or $\cdots$
+or |xxx4|), and if the associated string matches a keyword exactly,
+the string number of that keyword is returned (e.g., |rule_thickness_code|).
+
+\item{3)} If |cur_gf| is an \\{xxx} command whose string begins with
+keyword and space, the string number of that keyword is returned, and
+the remainder of the string is put into the string pool (where it will be
+string number |cur_string|. Exception: If the keyword is |null_string|,
+the character immediately following the blank space is put into the
+global variable |label_type|, and the remaining characters go into the
+string pool.
+
+\smallskip\noindent
+In all cases, |cur_gf| will then be reset to the op-code byte that
+immediately follows the original command.
+
+@d no_operation=max_keyword+1
+
+@<Types ...@>=
+@!keyword_code=null_string..no_operation;
+
+@ @<Glob...@>=
+@!cur_gf:eight_bits; {the byte most recently read from |gf_file|}
+@!cur_string:str_number; {the string following a keyword and space}
+@!label_type:eight_bits; {the character following a null keyword and space}
+
+@ We will be using this procedure when reading the \.{GF} file just
+after the preamble and just after |eoc| commands.
+
+@p function interpret_xxx:keyword_code;
+label done,done1,not_found;
+var @!k:integer; {number of bytes in an \\{xxx} command}
+@!j:integer; {number of bytes read so far}
+@!l:0..longest_keyword; {length of keyword to check}
+@!m:keyword_code; {runs through the list of known keywords}
+@!n1:0..longest_keyword; {buffered character being checked}
+@!n2:pool_pointer; {pool character being checked}
+@!c:keyword_code; {the result to return}
+begin c:=no_operation; cur_string:=null_string;
+case cur_gf of
+no_op:goto done;
+yyy:begin k:=signed_quad; goto done;
+ end;
+xxx1:k:=get_byte;
+xxx2:k:=get_two_bytes;
+xxx3:k:=get_three_bytes;
+xxx4:k:=signed_quad;
+end; {there are no other cases}
+@<Read the next |k| characters of the \.{GF} file;
+ change |c| and |goto done| if a keyword is recognized@>;
+done: cur_gf:=get_byte; interpret_xxx:=c;
+end;
+
+@ @<Read the next |k|...@>=
+j:=0;@+if k<2 then goto not_found;
+loop@+ begin l:=j;
+ if j=k then goto done1;
+ if j=longest_keyword then goto not_found;
+ incr(j); buffer[j]:=get_byte;
+ if buffer[j]=" " then goto done1;
+ end;
+done1:@<If the keyword in |buffer[1..l]| is known, change |c| and |goto done|@>;
+not_found: while j<k do
+ begin incr(j); cur_gf:=get_byte;
+ end
+
+@ @<If the keyword...@>=
+for m:=null_string to max_keyword do if length(m)=l then
+ begin n1:=0; n2:=str_start[m];
+ while (n1<l)and(buffer[n1+1]=str_pool[n2]) do
+ begin incr(n1); incr(n2);
+ end;
+ if n1=l then
+ begin c:=m;
+ if m=null_string then
+ begin incr(j); label_type:=get_byte;
+ end;
+ str_room(k-j);
+ while j<k do
+ begin incr(j); append_char(get_byte);
+ end;
+ cur_string:=make_string; goto done;
+ end;
+ end
+
+@ When an \\{xxx} command takes a numeric argument, |get_yyy| reads
+that argument and puts the following byte into |cur_gf|.
+
+@p function get_yyy:scaled;
+var @!v:scaled; {value just read}
+begin if cur_gf<>yyy then get_yyy:=0
+else begin v:=signed_quad; cur_gf:=get_byte; get_yyy:=v;
+ end;
+end;
+
+@ A simpler method is used for special commands between |boc| and |eoc|,
+since \.{GFtoDVI} doesn't even look at them.
+
+@p procedure skip_nop;
+label done;
+var @!k:integer; {number of bytes in an \\{xxx} command}
+@!j:integer; {number of bytes read so far}
+begin case cur_gf of
+no_op:goto done;
+yyy:begin k:=signed_quad; goto done;
+ end;
+xxx1:k:=get_byte;
+xxx2:k:=get_two_bytes;
+xxx3:k:=get_three_bytes;
+xxx4:k:=signed_quad;
+end; {there are no other cases}
+for j:=1 to k do cur_gf:=get_byte;
+done: cur_gf:=get_byte;
+end;
+
+@* File names.
+It's time now to fret about file names. \.{GFtoDVI} uses the conventions of
+\TeX\ and \MF\ to convert file names into strings that can be used to open
+files. Three routines called |begin_name|, |more_name|, and |end_name| are
+involved, so that the system-dependent parts of file naming conventions are
+isolated from the system-independent ways in which file names are used.
+(See the \TeX\ or \MF\ program listing for further explanation.)
+@^system dependencies@>
+
+@<Glob...@>=
+@!cur_name:str_number; {name of file just scanned}
+@!cur_area:str_number; {file area just scanned, or |null_string|}
+@!cur_ext:str_number; {file extension just scanned, or |null_string|}
+
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+
+@ Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|home_font_area|. This system area name will, of course, vary from place
+to place. The program here sets it to `\.{TeXfonts:}'.
+@^system dependencies@>
+@.TeXfonts@>
+
+@<Initialize the strings@>=
+l:=9; init_str9("T")("e")("X")("f")("o")("n")("t")("s")(":")(home_font_area);@/
+
+@ Here now is the first of the system-dependent routines for file name scanning.
+@^system dependencies@>
+
+@p procedure begin_name;
+begin area_delimiter:=0; ext_delimiter:=0;
+end;
+
+@ And here's the second.
+@^system dependencies@>
+
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin if (c=">")or(c=":") then
+ begin area_delimiter:=pool_ptr; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
+ str_room(1); append_char(c); {contribute |c| to the current string}
+ more_name:=true;
+ end;
+end;
+
+@ The third.
+@^system dependencies@>
+
+@p procedure end_name;
+begin if str_ptr+3>max_strings then
+ abort('Too many strings!');
+@.Too many strings@>
+if area_delimiter=0 then cur_area:=null_string
+else begin cur_area:=str_ptr; incr(str_ptr);
+ str_start[str_ptr]:=area_delimiter+1;
+ end;
+if ext_delimiter=0 then
+ begin cur_ext:=null_string; cur_name:=make_string;
+ end
+else begin cur_name:=str_ptr; incr(str_ptr);
+ str_start[str_ptr]:=ext_delimiter; cur_ext:=make_string;
+ end;
+end;
+
+@ Another system-dependent routine is needed to convert three strings
+into the |name_of_file| value that is used to open files. The present code
+allows both lowercase and uppercase letters in the file name.
+@^system dependencies@>
+
+@d append_to_name(#)==begin c:=#; incr(k);
+ if k<=file_name_size then name_of_file[k]:=xchr[c];
+ end
+
+@p procedure pack_file_name(@!n,@!a,@!e:str_number);
+var k:integer; {number of positions filled in |name_of_file|}
+@!c: ASCII_code; {character being packed}
+@!j:integer; {index into |str_pool|}
+@!name_length:0..file_name_size; {number of characters packed}
+begin k:=0;
+for j:=str_start[a] to str_start[a+1]-1 do append_to_name(str_pool[j]);
+for j:=str_start[n] to str_start[n+1]-1 do append_to_name(str_pool[j]);
+for j:=str_start[e] to str_start[e+1]-1 do append_to_name(str_pool[j]);
+if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
+for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
+end;
+
+@ Now let's consider the routines by which \.{GFtoDVI} deals with file names
+in a system-independent manner.
+The global variable |job_name| contains the \.{GF} file name that is
+being input. This name is extended by `\.{dvi}'
+in order to make the name of the output file.
+
+@<Glob...@>=
+@!job_name:str_number; {principal file name}
+
+@ The |start_gf| procedure prompts the user for the name of the generic
+font file to be input. It opens the file, making sure that some input is
+present; then it opens the output file.
+
+Although this routine is system-independent, it should probably be
+modified to take the file name from the command line (without an initial
+prompt), on systems that permit such things.
+
+@p procedure start_gf;
+label found,done;
+begin loop@+begin print_nl('GF file name: '); input_ln;
+@.GF file name@>
+ buf_ptr:=0; buffer[line_length]:="?";
+ while buffer[buf_ptr]=" " do incr(buf_ptr);
+ if buf_ptr<line_length then
+ begin @<Scan the file name in the buffer@>;
+ if cur_ext=null_string then cur_ext:=gf_ext;
+ pack_file_name(cur_name,cur_area,cur_ext); open_gf_file;
+ if not eof(gf_file) then goto found;
+ print_nl('Oops... I can''t find file '); print(name_of_file);
+@.Oops...@>
+@.I can't find...@>
+ end;
+ end;
+found:job_name:=cur_name; pack_file_name(job_name,null_string,dvi_ext);
+open_dvi_file;
+end;
+
+@ @<Scan the file name in the buffer@>=
+if buffer[line_length-1]="/" then
+ begin interaction:=true; decr(line_length);
+ end;
+begin_name;
+loop@+ begin if buf_ptr=line_length then goto done;
+ if not more_name(buffer[buf_ptr]) then goto done;
+ incr(buf_ptr);
+ end;
+done:end_name
+
+@ Special instructions found near the beginning of the \.{GF} file might
+change the names, areas, and ``at'' sizes of the fonts that \.{GFtoDVI}
+will be using. But when we reach the first |boc| instruction, we input
+all of the \.{TFM} files. The global variable |interaction| is set |true|
+if a |"/"| was removed at the end of the file name; this means that the
+user will have a chance to issue special instructions online just before
+the fonts are loaded.
+
+@d check_fonts==@+if fonts_not_loaded then load_fonts
+
+@<Glob...@>=
+@!interaction:boolean; {is the user allowed to type specials online?}
+@!fonts_not_loaded:boolean; {have the \.{TFM} files still not been input?}
+@!font_name:array[internal_font_number] of str_number; {current font names}
+@!font_area:array[internal_font_number] of str_number; {current font areas}
+@!font_at:array[internal_font_number] of scaled; {current font ``at'' sizes}
+
+@ @<Set init...@>=
+interaction:=false; fonts_not_loaded:=true;
+font_name[title_font]:=default_title_font;
+font_name[label_font]:=default_label_font;
+font_name[gray_font]:=default_gray_font;
+font_name[slant_font]:=null_string;
+font_name[logo_font]:=logo_font_name;
+for k:=title_font to logo_font do
+ begin font_area[k]:=null_string; font_at[k]:=0;
+ end;
+
+@ After the following procedure has been performed, there will be no
+turning back; the fonts will have been firmly established in
+\.{GFtoDVI}'s memory.
+
+@<Declare the procedure called |load_fonts|@>=
+procedure load_fonts;
+label done,continue,found,not_found;
+var @!f:internal_font_number;
+@!i:four_quarters; {font information word}
+@!j,@!k,@!v:integer; {registers for initializing font tables}
+@!m:title_font..slant_font+area_code; {keyword found}
+@!n1:0..longest_keyword; {buffered character being checked}
+@!n2:pool_pointer; {pool character being checked}
+begin if interaction then @<Get online special input@>;
+fonts_not_loaded:=false;
+for f:=title_font to logo_font do
+ if (f<>slant_font)or(length(font_name[f])>0) then
+ begin if length(font_area[f])=0 then font_area[f]:=home_font_area;
+ pack_file_name(font_name[f],font_area[f],tfm_ext);
+ open_tfm_file; read_font_info(f,font_at[f]);
+ if font_area[f]=home_font_area then font_area[f]:=null_string;
+ dvi_font_def(f); {put the font name in the \.{DVI} file}
+ end;
+@<Initialize global variables that depend on the font data@>;
+end;
+
+@ @<Get online special input@>=
+loop@+ begin not_found: print_nl('Special font substitution: ');
+@.Special font subst...@>
+ continue: input_ln;
+ if line_length=0 then goto done;
+ @<Search buffer for valid keyword; if successful, |goto found|@>;
+ print('Please say, e.g., "grayfont foo" or "slantfontarea baz".');
+ goto not_found;
+ found: @<Update the font name or area@>;
+ print('OK; any more? '); goto continue;
+ end;
+done:
+
+@ @<Search buffer for valid keyword; if successful, |goto found|@>=
+buf_ptr:=0; buffer[line_length]:=" ";
+while buffer[buf_ptr]<>" " do incr(buf_ptr);
+for m:=title_font to slant_font+area_code do if length(m)=buf_ptr then
+ begin n1:=0; n2:=str_start[m];
+ while (n1<buf_ptr)and(buffer[n1]=str_pool[n2]) do
+ begin incr(n1); incr(n2);
+ end;
+ if n1=buf_ptr then goto found;
+ end
+
+@ @<Update the font name or area@>=
+incr(buf_ptr); str_room(line_length-buf_ptr);
+while buf_ptr<line_length do
+ begin append_char(buffer[buf_ptr]); incr(buf_ptr);
+ end;
+if m>area_code then font_area[m-area_code]:=make_string
+else begin font_name[m]:=make_string; font_area[m]:=null_string;
+ font_at[m]:=0;
+ end;
+init_str_ptr:=str_ptr
+
+@* Shipping pages out.
+The following routines are used to write the \.{DVI} file. They have
+been copied from \TeX, but simplified; we don't have to handle
+nearly as much generality as \TeX\ does.
+
+Statistics about the entire set of pages that will be shipped out must be
+reported in the \.{DVI} postamble. The global variables |total_pages|,
+|max_v|, |max_h|, and |last_bop| are used to record this information.
+
+@<Glob...@>=
+@!total_pages:integer; {the number of pages that have been shipped out}
+@!max_v:scaled; {maximum height-plus-depth of pages shipped so far}
+@!max_h:scaled; {maximum width of pages shipped so far}
+@!last_bop:integer; {location of previous |bop| in the \.{DVI} output}
+
+@ @<Set init...@>=
+total_pages:=0; max_v:=0; max_h:=0; last_bop:=-1;
+
+@ The \.{DVI} bytes are output to a buffer instead of being written directly
+to the output file. This makes it possible to reduce the overhead of
+subroutine calls.
+
+The output buffer is divided into two parts of equal size; the bytes found
+in |dvi_buf[0..half_buf-1]| constitute the first half, and those in
+|dvi_buf[half_buf..dvi_buf_size-1]| constitute the second. The global
+variable |dvi_ptr| points to the position that will receive the next
+output byte. When |dvi_ptr| reaches |dvi_limit|, which is always equal
+to one of the two values |half_buf| or |dvi_buf_size|, the half buffer that
+is about to be invaded next is sent to the output and |dvi_limit| is
+changed to its other value. Thus, there is always at least a half buffer's
+worth of information present, except at the very beginning of the job.
+
+Bytes of the \.{DVI} file are numbered sequentially starting with 0;
+the next byte to be generated will be number |dvi_offset+dvi_ptr|.
+
+@<Types ...@>=
+@!dvi_index=0..dvi_buf_size; {an index into the output buffer}
+
+@ Some systems may find it more efficient to make |dvi_buf| a |packed|
+array, since output of four bytes at once may be facilitated.
+@^system dependencies@>
+
+@<Glob...@>=
+@!dvi_buf:array[dvi_index] of eight_bits; {buffer for \.{DVI} output}
+@!half_buf:dvi_index; {half of |dvi_buf_size|}
+@!dvi_limit:dvi_index; {end of the current half buffer}
+@!dvi_ptr:dvi_index; {the next available buffer address}
+@!dvi_offset:integer; {|dvi_buf_size| times the number of times the
+ output buffer has been fully emptied}
+
+@ Initially the buffer is all in one piece; we will output half of it only
+after it first fills up.
+
+@<Set init...@>=
+half_buf:=dvi_buf_size div 2; dvi_limit:=dvi_buf_size; dvi_ptr:=0;
+dvi_offset:=0;
+
+@ The actual output of |dvi_buf[a..b]| to |dvi_file| is performed by calling
+|write_dvi(a,b)|. It is safe to assume that |a| and |b+1| will both be
+multiples of 4 when |write_dvi(a,b)| is called; therefore it is possible on
+many machines to use efficient methods to pack four bytes per word and to
+output an array of words with one system call.
+@^system dependencies@>
+
+@p procedure write_dvi(@!a,@!b:dvi_index);
+var k:dvi_index;
+begin for k:=a to b do write(dvi_file,dvi_buf[k]);
+end;
+
+@ To put a byte in the buffer without paying the cost of invoking a procedure
+each time, we use the macro |dvi_out|.
+
+@d dvi_out(#)==@+begin dvi_buf[dvi_ptr]:=#; incr(dvi_ptr);
+ if dvi_ptr=dvi_limit then dvi_swap;
+ end
+
+@p procedure dvi_swap; {outputs half of the buffer}
+begin if dvi_limit=dvi_buf_size then
+ begin write_dvi(0,half_buf-1); dvi_limit:=half_buf;
+ dvi_offset:=dvi_offset+dvi_buf_size; dvi_ptr:=0;
+ end
+else begin write_dvi(half_buf,dvi_buf_size-1); dvi_limit:=dvi_buf_size;
+ end;
+end;
+
+@ Here is how we clean out the buffer when \TeX\ is all through; |dvi_ptr|
+will be a multiple of~4.
+
+@<Empty the last bytes out of |dvi_buf|@>=
+if dvi_limit=half_buf then write_dvi(half_buf,dvi_buf_size-1);
+if dvi_ptr>0 then write_dvi(0,dvi_ptr-1)
+
+@ The |dvi_four| procedure outputs four bytes in two's complement notation,
+without risking arithmetic overflow.
+
+@p procedure dvi_four(@!x:integer);
+begin if x>=0 then dvi_out(x div @'100000000)
+else begin x:=x+@'10000000000;
+ x:=x+@'10000000000;
+ dvi_out((x div @'100000000) + 128);
+ end;
+x:=x mod @'100000000; dvi_out(x div @'200000);
+x:=x mod @'200000; dvi_out(x div @'400);
+dvi_out(x mod @'400);
+end;
+
+@ Here's a procedure that outputs a font definition.
+
+@d select_font(#)==dvi_out(fnt_num_0+#) {set current font to \#}
+
+@p procedure dvi_font_def(@!f:internal_font_number);
+var k:integer; {index into |str_pool|}
+begin dvi_out(fnt_def1);
+dvi_out(f);@/
+dvi_out(qo(font_check[f].b0));
+dvi_out(qo(font_check[f].b1));
+dvi_out(qo(font_check[f].b2));
+dvi_out(qo(font_check[f].b3));@/
+dvi_four(font_size[f]);
+dvi_four(font_dsize[f]);@/
+dvi_out(length(font_area[f]));
+dvi_out(length(font_name[f]));
+@<Output the font name whose internal number is |f|@>;
+end;@/
+@t\4@>@<Declare the procedure called |load_fonts|@>@;
+
+@ @<Output the font name whose internal number is |f|@>=
+for k:=str_start[font_area[f]] to str_start[font_area[f]+1]-1 do
+ dvi_out(str_pool[k]);
+for k:=str_start[font_name[f]] to str_start[font_name[f]+1]-1 do
+ dvi_out(str_pool[k])
+
+@ The |typeset| subroutine typesets any eight-bit character.
+
+@p procedure typeset(@!c:eight_bits);
+begin if c>=128 then dvi_out(set1);
+dvi_out(c);
+end;
+
+@ The |dvi_scaled| subroutine takes a |real| value |x| and outputs
+a decimal approximation to |x/unity|, correct to one decimal place.
+
+@p procedure dvi_scaled(@!x:real);
+var @!n:integer; {an integer approximation to |10*x/unity|}
+@!m:integer; {the integer part of the answer}
+@!k:integer; {the number of digits in |m|}
+begin n:=round(x/6553.6);
+if n<0 then
+ begin dvi_out("-"); n:=-n;
+ end;
+m:=n div 10; k:=0;
+repeat incr(k); buffer[k]:=(m mod 10)+"0"; m:=m div 10;
+until m=0;
+repeat dvi_out(buffer[k]); decr(k);
+until k=0;
+if n mod 10 <> 0 then
+ begin dvi_out("."); dvi_out((n mod 10)+"0");
+ end;
+end;
+
+@ At the end of the program, we must finish things off by writing the
+post\-amble. An integer variable~|k| will be declared for use by this routine.
+
+@<Finish the \.{DVI} file and |goto final_end|@>=
+begin dvi_out(post); {beginning of the postamble}
+dvi_four(last_bop); last_bop:=dvi_offset+dvi_ptr-5; {|post| location}
+dvi_four(25400000); dvi_four(473628672); {conversion ratio for sp}
+dvi_four(1000); {magnification factor}
+dvi_four(max_v); dvi_four(max_h);@/
+dvi_out(0); dvi_out(3); {`\\{max\_push}' is said to be 3}@/
+dvi_out(total_pages div 256); dvi_out(total_pages mod 256);@/
+if not fonts_not_loaded then
+ for k:=title_font to logo_font do
+ if length(font_name[k])>0 then dvi_font_def(k);
+dvi_out(post_post); dvi_four(last_bop); dvi_out(dvi_id_byte);@/
+k:=4+((dvi_buf_size-dvi_ptr) mod 4); {the number of 223's}
+while k>0 do
+ begin dvi_out(223); decr(k);
+ end;
+@<Empty the last bytes out of |dvi_buf|@>;
+goto final_end;
+end
+
+@* Rudimentary typesetting.
+One of \.{GFtoDVI}'s little duties is to be a mini-\TeX: It must be able
+to typeset the equivalent of `\.{\\hbox\{}$\langle$string$\rangle$\.\}' for
+a given string of ASCII characters, using either the title font or the
+label font.
+
+The |hbox| procedure does this. The width, height, and depth of the
+box defined by string~|s| in font~|f| are computed in global variables
+|box_width|, |box_height|, and |box_depth|.
+
+The task would be trivial if it weren't for ligatures and kerns, which
+are implemented here in full generality. (Infinite looping is possible
+if the \.{TFM} file is malformed; \.{TFtoPL} will diagnose such problems.)
+
+We assume that |" "| is a space character; character code @'40 will not
+be typeset unless it is accessed via a ligature.
+
+If parameter |send_it| is |false|, we merely want to know the box dimensions.
+Otherwise typesetting commands are also sent to
+the \.{DVI} file; we assume in this case that font |f| has already been
+selected in the \.{DVI} file as the current font.
+
+@d set_cur_r==if k<end_k then cur_r:=qi(str_pool[k])
+ else cur_r:=bchar
+
+@p procedure hbox(@!s:str_number;@!f:internal_font_number;@!send_it:boolean);
+label continue, done;
+var @!k,@!end_k,@!max_k:pool_pointer; {indices into |str_pool|}
+@!i,@!j:four_quarters; {font information words}
+@!cur_l:0..256; {character to the left of the ``cursor''}
+@!cur_r:min_quarterword..non_char; {character to the right of the ``cursor''}
+@!bchar:min_quarterword..non_char; {right boundary character}
+@!stack_ptr:0..lig_lookahead; {number of entries on |lig_stack|}
+@!l:font_index; {pointer to lig/kern instruction}
+@!kern_amount:scaled; {extra space to be typeset}
+@!hd:eight_bits; {height and depth indices for a character}
+@!x:scaled; {temporary register}
+@!save_c:ASCII_code; {character temporarily blanked out}
+begin box_width:=0; box_height:=0; box_depth:=0;@/
+k:=str_start[s]; max_k:=str_start[s+1];
+save_c:=str_pool[max_k]; str_pool[max_k]:=" ";
+while k<max_k do
+ begin if str_pool[k]=" " then @<Typeset a space in font |f| and advance~|k|@>
+ else begin end_k:=k;
+ repeat incr(end_k); until str_pool[end_k]=" ";
+ kern_amount:=0; cur_l:=256; stack_ptr:=0; bchar:=font_bchar[f];
+ set_cur_r; suppress_lig:=false;
+continue: @<If there's a ligature or kern at the cursor position,
+ update the cursor data structures, possibly advancing~|k|; continue
+ until the cursor wants to move right@>;
+ @<Typeset character |cur_l|, if it exists in the font;
+ also append an optional kern@>;
+ @<Move the cursor to the right and |goto continue|, if there's
+ more work to do in the current word@>;
+ end; {now |k=end_k|}
+ end;
+str_pool[max_k]:=save_c;
+end;
+
+@ @<Glob...@>=
+@!box_width:scaled; {width of box constructed by |hbox|}
+@!box_height:scaled; {height of box constructed by |hbox|}
+@!box_depth:scaled; {depth of box constructed by |hbox|}
+@!lig_stack:array[1..lig_lookahead] of quarterword; {inserted ligature chars}
+@!dummy_info:four_quarters; {fake |char_info| for nonexistent character}
+@!suppress_lig:boolean; {should we bypass checking for ligatures next time?}
+
+@ @<Set init...@>=
+dummy_info.b0:=qi(0); dummy_info.b1:=qi(0); dummy_info.b2:=qi(0);
+dummy_info.b3:=qi(0);
+
+@ @<Typeset a space...@>=
+begin box_width:=box_width+space(f);
+if send_it then
+ begin dvi_out(right4); dvi_four(space(f));
+ end;
+incr(k);
+end
+
+@ @<If there's a ligature...@>=
+if(cur_l<font_bc[f])or(cur_l>font_ec[f]) then
+ begin i:=dummy_info;
+ if cur_l=256 then l:=bchar_label[f]@+else l:=non_address;
+ end
+else begin i:=char_info(f)(cur_l);
+ if char_tag(i)<>lig_tag then l:=non_address
+ else begin l:=lig_kern_start(f)(i); j:=font_info[l].qqqq;
+ if skip_byte(j)>stop_flag then l:=lig_kern_restart(f)(j);
+ end;
+ end;
+if suppress_lig then suppress_lig:=false
+else while l<qi(kern_base[f]) do
+ begin j:=font_info[l].qqqq;
+ if next_char(j)=cur_r then if skip_byte(j)<=stop_flag then
+ if op_byte(j)>=kern_flag then
+ begin kern_amount:=char_kern(f)(j); goto done;
+ end
+ else @<Carry out a ligature operation, updating the cursor structure
+ and possibly advancing~|k|; |goto continue| if the cursor doesn't
+ advance, otherwise |goto done|@>;
+ if skip_byte(j)>=stop_flag then goto done;
+ l:=l+skip_byte(j)+1;
+ end;
+done:
+
+@ At this point |i| contains |char_info| for |cur_l|.
+
+@<Typeset character...@>=
+if char_exists(i) then
+ begin box_width:=box_width+char_width(f)(i)+kern_amount;@/
+ hd:=height_depth(i);
+ x:=char_height(f)(hd);
+ if x>box_height then box_height:=x;
+ x:=char_depth(f)(hd);
+ if x>box_depth then box_depth:=x;
+ if send_it then
+ begin typeset(cur_l);
+ if kern_amount<>0 then
+ begin dvi_out(right4); dvi_four(kern_amount);
+ end;
+ end;
+ kern_amount:=0;
+ end
+
+@ @d pop_stack==begin decr(stack_ptr);
+ if stack_ptr>0 then cur_r:=lig_stack[stack_ptr]
+ else set_cur_r;
+ end
+
+@<Carry out a ligature operation, updating the cursor structure...@>=
+begin case op_byte(j) of
+1,5:cur_l:=qo(rem_byte(j));
+2,6:begin cur_r:=rem_byte(j);
+ if stack_ptr=0 then
+ begin stack_ptr:=1;
+ if k<end_k then incr(k) {a non-space character is consumed}
+ else bchar:=non_char; {the right boundary character is consumed}
+ end;
+ lig_stack[stack_ptr]:=cur_r;
+ end;
+3,7,11:begin cur_r:=rem_byte(j); incr(stack_ptr); lig_stack[stack_ptr]:=cur_r;
+ if op_byte(j)=11 then suppress_lig:=true;
+ end;
+othercases begin cur_l:=qo(rem_byte(j));
+ if stack_ptr>0 then pop_stack
+ else if k=end_k then goto done
+ else begin incr(k); set_cur_r;
+ end;
+ end
+endcases;
+if op_byte(j)>3 then goto done;
+goto continue;
+end
+
+@ @<Move the cursor to the right and |goto continue|...@>=
+cur_l:=qo(cur_r);
+if stack_ptr>0 then
+ begin pop_stack; goto continue;
+ end;
+if k<end_k then
+ begin incr(k); set_cur_r; goto continue;
+ end
+
+@* Gray fonts.
+A proof diagram constructed by \.{GFtoDVI}
+can be regarded as an array of rectangles, where each rectangle is either
+blank or filled with a special symbol that we shall call $x$. A blank
+rectangle represents a white pixel, while $x$ represents a black pixel.
+Additional labels and reference lines are often superimposed on this
+array of rectangles; hence it is usually best to choose a symbol $x$ that
+has a somewhat gray appearance, although any symbol can actually be used.
+
+In order to construct such proofs, \.{GFtoDVI} needs to work with
+a special type of font known as a ``gray font''; it's possible to
+obtain a wide variety of different sorts of proofs by using different
+sorts of gray fonts. The next few paragraphs explain exactly what gray
+fonts are supposed to contain, in case you want to design your own.
+@^gray fonts@>
+
+@ The simplest gray font contains only two characters, namely $x$
+and another symbol that is used for dots that identify key points.
+If proofs with relatively large pixels are desired, a two-character
+gray font is all that's needed. However, if the pixel size is to be
+relatively small, practical considerations make a two-character
+font too inefficient, since it requires the typesetting of tens
+of thousands of tiny little characters; printing device drivers
+rarely work very well when they are presented with data that is
+so different from ordinary text. Therefore a gray font with small
+pixels usually has a number of characters that replicate $x$ in
+such a way that comparatively few characters actually need to be
+typeset.
+
+Since many printing devices are not able to cope with
+arbitrarily large or complex characters, it is not possible for a
+single gray font to work well on all machines. In fact,
+$x$ must have a width that is an integer multiple of the printing
+device's unit of horizontal position, since rounding the positions of grey
+characters would otherwise produce unsightly streaks on proof output.
+Thus, there is no way to make the gray font as device-independent as
+the rest of the system, in the sense that we would expect approximately
+identical output on machines with different resolution. Fortunately,
+proof sheets are rarely considered to be final documents; hence
+\.{GFtoDVI} is set up to provide results that adapt suitably to
+local conditions.
+
+@ With such constraints understood, we can now take a look at what
+\.{GFtoDVI} expects to see in a gray font. The character~$x$ always
+appears in position~1. It must have positive height~$h$ and positive
+width~$w$; its depth and italic correction are ignored.
+
+Positions 2--120 of a gray font are reserved for special combinations
+of $x$'s and blanks, stacked on top of each other. None of these character
+codes need be present in the font; but if they are, the slots should be
+occupied by characters of width~$w$ that have certain configurations of
+$x$'s and blanks, prescribed for each character position. For example,
+position~3 of the font should either contain no character at all,
+or it should contain a character consisting of two $x$'s, one above
+the other; one of these $x$'s should appear immediately above the
+baseline, and the other should appear immediately below.
+
+It will be convenient to use a horizontal notation like `\.{XOXXO}'
+to stand for a vertical stack of $x$'s and blanks. The convention
+will be that the stack is built from bottom to top, and the topmost
+rectangle should sit on the baseline. Thus, `\.{XOXXO}' stands
+actually for a character of depth~$4h$ that looks like this:
+$$\vcenter{\halign{\hfil#\hfil\cr
+blank\cr
+$x$\rlap{\qquad\raise8pt\hbox{\smash{\hbox{$\longleftarrow$ baseline}}}}\cr
+$x$\cr
+blank\cr
+$x$\cr
+}}$$
+(We use a horizontal notation instead of a vertical one in this explanation,
+because column
+vectors take too much space, and because the horizontal notation corresponds
+to binary numbers in a convenient way.)
+
+Positions 1--63 of a gray font are reserved for the patterns \.X, \.{XO},
+\.{XX}, \.{XOO}, \.{XOX}, \dots, \.{XXXXXX}, just as in the normal
+binary notation of the numbers 1--63. Positions 64--70 are reserved for
+the special patterns \.{XOOOOOO}, \.{XXOOOOO}, \dots, \.{XXXXXXO},
+\.{XXXXXXX} of length seven; positions 71--78 are, similarly, reserved for
+the length-eight patterns \.{XOOOOOOO} through \.{XXXXXXXX}. The
+length-nine patterns \.{XOOOOOOOO} through \.{XXXXXXXXX} are assigned
+to positions 79--87, the length-ten patterns to positions 88--97,
+the length-eleven patterns to positions 98--108, and the length-twelve
+patterns to positions 109--120.
+
+The following program sets a global array |c[1..120]| to the bit patterns
+just described. Another array |d[1..120]| is set to contain only the next
+higher bit; this determines the depth of the corresponding character.
+
+@<Set init...@>=
+c[1]:=1; d[1]:=2; two_to_the[0]:=1; m:=1;
+for k:=1 to 13 do two_to_the[k]:=2*two_to_the[k-1];
+for k:=2 to 6 do @<Add a full set of |k|-bit characters@>;
+for k:=7 to 12 do @<Add special |k|-bit characters of the form \.{X..XO..O}@>;
+
+@ @<Glob...@>=
+@!c:array[1..120] of 1..4095; {bit patterns for a gray font}
+@!d:array[1..120] of 2..4096; {the superleading bits}
+@!two_to_the:array[0..13] of 1..8192; {powers of 2}
+
+@ @<Add a full set of |k|-bit...@>=
+begin n:=two_to_the[k-1];
+for j:=0 to n-1 do
+ begin incr(m); c[m]:=m; d[m]:=n+n;
+ end;
+end
+
+@ @<Add special |k|-bit...@>=
+begin n:=two_to_the[k-1];
+for j:=k downto 1 do
+ begin incr(m); d[m]:=n+n;
+ if j=k then c[m]:=n
+ else c[m]:=c[m-1]+two_to_the[j-1];
+ end;
+end
+
+@ Position 0 of a gray font is reserved for the ``dot'' character, which
+should have positive height~$h'$ and positive width~$w'$. When \.{GFtoDVI}
+wants to put a dot at some place $(x,y)$ on the figure, it positions
+the dot character so that its reference point is at $(x,y)$. The
+dot will be considered to occupy a rectangle $(x+\delta,y+\epsilon)$
+for $-w'\leq\delta\leq w'$ and $-h'\leq\epsilon\leq h'$; the rectangular
+box for a label will butt up against the rectangle enclosing the dot.
+
+@ All other character positions of a gray font (namely, positions 121--255)
+are unreserved, in the sense that they have no predefined meaning.
+But \.{GFtoDVI} may access them via the ``character list'' feature of
+\.{TFM} files, starting with any of the characters in positions
+1--120. In such a case each succeeding character in a list should be
+equivalent to two of its predecessors, horizontally adjacent to each other.
+For example, in a character list like
+$$53,\;121,\;122,\;123$$
+character 121 will stand for two 53's, character 122 for two 121's (i.e.,
+four 53's), and character 123 for two 122's (i.e., eight 53's). Since
+position~53 contains the pattern \.{XXOXOX}, character~123 in this example
+would have height~$h$, depth~$5h$, and width~$8w$, and it would stand for
+the pattern
+$$\vcenter{\halign{&$\hfil#\hfil$\cr
+x&x&x&x&x&x&x&x\cr
+&&&&&&&\cr
+x&x&x&x&x&x&x&x\cr
+&&&&&&&\cr
+x&x&x&x&x&x&x&x\cr
+x&x&x&x&x&x&x&x\cr}}$$
+Such a pattern is, of course, rather unlikely to occur in a \.{GF} file,
+but \.{GFtoDVI} would be able to use if it were present. Designers
+of gray fonts should provide characters only for patterns that they think
+will occur often enough to make the doubling worthwhile. For example,
+the character in position 120 (\.{XXXXXXXXXXXX}), or whatever is the
+tallest stack of $x$'s present in the font, is a natural candidate for
+repeated doubling.
+
+Here's how \.{GFtoDVI} decides what characters of the gray font will be used,
+given a configuration of black and white pixels: If there are no black
+pixels, stop. Otherwise look at the top row that contains at least one
+black pixel, and the eleven rows that follow. For each such column,
+find the largest~$k$ such that $1\leq k\leq120$ and the gray font contains
+character~$k$ and the pattern assigned to position~$k$ appears in the
+given column. Typeset character $k$ (unless no such character exists)
+and erase the corresponding black pixels; use doubled characters,
+if they are present in the gray font, if two or more consecutive equal
+characters need to be typeset. Repeat the same process on the remaining
+configuration, until all the black pixels have been erased.
+
+If all characters in positions 1--120 are present, this process is guaranteed to
+take care of at least six rows each time; and it usually takes care of
+twelve, since all patterns that contain at most one ``run'' of $x$'s
+are present.
+
+@ Fonts have optional parameters, as described in Appendix~F of {\sl The
+\TeX book}, and some of these are important in gray fonts. The
+slant parameter~$s$, if nonzero, will cause \.{GFtoDVI} to skew its
+output; in this case the character $x$ will presumably be a parallelogram
+with a corresponding slant, rather than the usual rectangle. \MF's
+coordinate $(x,y)$ will appear in physical position $(xw+yhs,yh)$
+on the proofsheets.
+
+Parameter number~8 of a gray font specifies the thickness of rules
+that go on the proofs. If this parameter is zero, \TeX's default
+rule thickness (0.4\thinspace pt) will be used.
+
+The other parameters of a gray font are ignored by \.{GFtoDVI}, but
+it is conventional to set the font space parameter to~$w$ and the
+xheight parameter to~$h$.
+
+@ For best results the designer of a gray font should choose $h$ and~$w$
+so that the user's \.{DVI}-to-hardcopy software will not make any
+rounding errors. Furthermore, the dot should be an even number~$2m$ of
+pixels in diameter, and the rule thickness should work out to an
+even number~$2n$ of pixels; then the dots and rules will be centered on
+the correct positions, in case of integer coordinates. Gray fonts
+are almost always intended for particular output devices, even though
+`\.{DVI}' stands for `device independent'; we use \.{DVI} files for \MF\
+proofs chiefly because software to print \.{DVI} files is already in place.
+
+@* Slant fonts.
+\.{GFtoDVI} also makes use of another special type of font, if it is
+necessary to typeset slanted rules. The format of such so-called
+``slant fonts'' is quite a bit simpler than the format of gray fonts.
+
+A slant font should contain exactly $n$ characters, in positions 1 to~$n$,
+where the character in position~$k$ represents a slanted line $k$ units
+tall, starting at the baseline. These lines all have a fixed slant ratio~$s$.
+
+The following simple algorithm is used to typeset a rule that is $m$ units
+high: Compute $q=\lceil m/n\rceil$; then typeset $q$~characters of
+approximately equal size, namely $(m\bmod q)$ copies of character number
+$\lceil m/q\rceil$ and $q-(m\bmod q)$ copies of character number
+$\lfloor m/q\rfloor$. For example, if $n=15$ and $m=100$, we have $q=7$;
+a 100-unit-high rule will be composed of 7~pieces, using characters
+14,~14, 14, 14, 14, 15,~15.
+
+@<Glob...@>=
+@!rule_slant:real; {the slant ratio $s$ in the slant font,
+ or zero if there is no slant font}
+@!slant_n:integer; {the number of characters in the slant font}
+@!slant_unit:real; {the number of scaled points in the slant font unit}
+@!slant_reported:real; {invalid slant ratio reported to the user}
+
+@ \.{GFtoDVI} looks only at the height of character $n$, so the \.{TFM} file
+need not be accurate about the heights of the other characters. (This is
+fortunate, since \.{TFM} format allows at most 16 different heights per font.)
+
+The width of character~$k$ should be $k/n$ times $s$ times the height of
+character~$n$.
+
+The slant parameter of a slant file should be $s$. It is customary to
+set the |default_rule_thickness| parameter (number~8) to the thickness of
+the slanted rules, but \.{GFtoDVI} doesn't look at it.
+
+@ For best results on a particular output device, it is usually wise to
+choose the `unit' in the above discussion to be an integer number of pixels,
+and to make it no larger than the default rule thickness in the gray font
+being used.
+
+@ @<Initialize glob...@>=
+if length(font_name[slant_font])=0 then rule_slant:=0.0
+else begin rule_slant:=slant(slant_font)/unity;
+ slant_n:=font_ec[slant_font];
+ i:=char_info(slant_font)(slant_n);
+ slant_unit:=char_height(slant_font)(height_depth(i))/slant_n;
+ end;
+slant_reported:=0.0;
+
+@ The following error message is given when an absent slant has been
+requested.
+
+@p procedure slant_complaint(@!r:real);
+begin if abs(r-slant_reported)>0.001 then
+ begin print_nl('Sorry, I can''t make diagonal rules of slant ',r:10:5,'!');
+@.Sorry, I can't...@>
+ slant_reported:=r;
+ end;
+end;
+
+@* Representation of rectangles.
+OK---the preliminary spadework has now been done. We're ready at last
+to concentrate on \.{GFtoDVI}'s {\sl raison d'\^^Detre}.
+
+One of the most interesting tasks remaining is to make
+a ``map'' of the labels that have been allocated.
+There usually aren't a great many labels, so we don't need fancy data
+structures; but we do make use of linked nodes containing nine fields.
+The nodes generally represent rectangular boxes according to the
+following conventions:
+\smallskip\hang\noindent
+|xl|, |xr|, |yt|, and |yb| are the left, right, top, and bottom locations
+of a rectangle, expressed in \.{DVI} coordinates. (This program uses
+scaled points as \.{DVI} coordinates. Since \.{DVI} coordinates increase
+as one moves down the page, |yb| will be greater than |yt|.)
+\smallskip\hang\noindent
+|xx| and |yy| are the coordinates of the reference point of a box to be
+typeset from this node, again in \.{DVI} coordinates.
+\smallskip\hang\noindent
+|prev| and |next| point to the predecessor and successor of this node.
+Sometimes the nodes are singly linked and only |next| is relevant; otherwise
+the nodes are doubly linked in order of their |yy| coordinates, so that we
+can move down by going to |next|, or up by going to |prev|.
+\smallskip\hang\noindent
+|info| is the number of a string associated with this node.
+\smallskip\noindent
+
+The nine fields of a node appear in nine global arrays.
+Null pointers are denoted by |null|, which happens to be zero.
+
+@d null=0
+
+@<Types ...@>=
+@!node_pointer=null..max_labels;
+
+@ @<Glob...@>=
+@!xl,@!xr,@!yt,@!yb:array[1..max_labels] of scaled; {boundary coordinates}
+@!xx,@!yy:array[0..max_labels] of scaled; {reference coordinates}
+@!prev,@!next:array[0..max_labels] of node_pointer; {links}
+@!info:array[1..max_labels] of str_number; {associated strings}
+@!max_node:node_pointer; {the largest node in use}
+@!max_height:scaled; {greatest difference between |yy| and |yt|}
+@!max_depth:scaled; {greatest difference between |yb| and |yy|}
+
+
+@ It's easy to allocate a new node (unless no more room is left):
+
+@p function get_avail:node_pointer;
+begin incr(max_node);
+if max_node=max_labels then abort('Too many labels and/or rules!');
+@.Too many labels@>
+get_avail:=max_node;
+end;
+
+@ The doubly linked nodes are sorted by |yy| coordinates so that we don't have
+to work too hard to find nearest neighbors or to determine if rectangles overlap.
+The first node in the doubly linked rectangle list is always in location~0,
+and the last node is always in location |max_labels|; the |yy| coordinates
+of these nodes are very small and very large, respectively.
+
+@d end_of_list==max_labels
+
+@<Set init...@>=
+yy[0]:=-@'10000000000; yy[end_of_list]:=@'10000000000;
+
+@ The |node_ins| procedure inserts a new rectangle, represented by node~|p|,
+into the doubly linked list. There's a second parameter, |q|; node~|q| should
+already be in the doubly linked list, preferably with |yy[q]| near |yy[p]|.
+
+@p procedure node_ins(@!p,@!q:node_pointer);
+var @!r:node_pointer; {for tree traversal}
+begin if yy[p]>=yy[q] then
+ begin repeat r:=q; q:=next[q];@+until yy[p]<=yy[q];
+ next[r]:=p; prev[p]:=r; next[p]:=q; prev[q]:=p;
+ end
+else begin repeat r:=q; q:=prev[q];@+until yy[p]>=yy[q];
+ prev[r]:=p; next[p]:=r; prev[p]:=q; next[q]:=p;
+ end;
+if yy[p]-yt[p]>max_height then max_height:=yy[p]-yt[p];
+if yb[p]-yy[p]>max_depth then max_depth:=yb[p]-yy[p];
+end;
+
+@ The data structures need to be initialized for each character in the
+\.{GF} file.
+
+@<Initialize variables for the next character@>=
+max_node:=0; next[0]:=end_of_list; prev[end_of_list]:=0;
+max_height:=0; max_depth:=0;
+
+@ The |overlap| subroutine determines whether or not the rectangle specified
+in node~|p| has a nonempty intersection with some rectangle in the doubly
+linked list. Again |q|~is a parameter that gives us a starting point
+in the list. We assume that |q<>end_of_list|, so that |next[q]| is meaningful.
+
+@p function overlap(@!p,@!q:node_pointer):boolean;
+label exit;
+var @!y_thresh:scaled; {cutoff value to speed the search}
+@!x_left,@!x_right,@!y_top,@!y_bot:scaled; {boundaries to test for overlap}
+@!r:node_pointer; {runs through the neighbors of |q|}
+begin x_left:=xl[p]; x_right:=xr[p]; y_top:=yt[p]; y_bot:=yb[p];
+@<Look for overlaps in the successors of node |q|@>;
+@<Look for overlaps in node |q| and its predecessors@>;
+overlap:=false;
+exit: end;
+
+@ @<Look for overlaps in the successors of node |q|@>=
+y_thresh:=y_bot+max_height; r:=next[q];
+while yy[r]<y_thresh do
+ begin if y_bot > yt[r] then if x_left<xr[r] then
+ if x_right>xl[r] then if y_top<yb[r] then
+ begin overlap:=true; return;
+ end;
+ r:=next[r];
+ end
+
+@ @<Look for overlaps in node |q| and its predecessors@>=
+y_thresh:=y_top-max_depth; r:=q;
+while yy[r]>y_thresh do
+ begin if y_bot > yt[r] then if x_left<xr[r] then
+ if x_right>xl[r] then if y_top<yb[r] then
+ begin overlap:=true; return;
+ end;
+ r:=prev[r];
+ end
+
+@ Nodes that represent dots instead of labels satisfy the following
+constraints:
+$$\vcenter{\halign{#\hfil&\quad#\hfil\cr
+|info[p]<0;|&|p>=first_dot|;\cr
+|xl[p]=xx[p]-dot_width|,&|xr[p]=xx[p]+dot_width|;\cr
+|yt[p]=yy[p]-dot_height|,&|yb[p]=yy[p]+dot_height|.\cr}}$$
+
+The |nearest_dot| subroutine finds a node whose reference point is as
+close as possible to a given position, ignoring nodes that are too close.
+More precisely, the ``nearest'' node
+minimizes$$d(q,p)=\max\bigl(\vert |xx|[q]-|xx|[p]\vert,
+ \vert |yy|[q]-|yy|[p]\vert\bigr)$$ over all nodes~|q|
+with |d(q,p)>=d0|. We call the subroutine |nearest_dot| because it is used only
+when the doubly linked list contains nothing but dots.
+
+The routine also sets the global variable |twin| to |true|, if there is a
+node |q<>p| with |d(q,p)<d0|.
+
+@ @<Glob...@>=
+@!first_dot:node_pointer; {the node address where dots begin}
+@!twin:boolean; {is there a nearer dot than the ``nearest'' dot?}
+
+@ If there is no nearest dot, the value |null| is returned;
+otherwise a pointer to the nearest dot is returned.
+
+@p function nearest_dot(@!p:node_pointer; @!d0:scaled):node_pointer;
+var @!best_q:node_pointer; {value to return}
+@!d_min,@!d:scaled; {distances}
+begin twin:=false; best_q:=0; d_min:=@'2000000000;
+@<Search for the nearest dot in nodes following |p|@>;
+@<Search for the nearest dot in nodes preceding |p|@>;
+nearest_dot:=best_q;
+end;
+
+@ @<Search for the nearest dot in nodes following |p|@>=
+q:=next[p];
+while yy[q]<yy[p]+d_min do
+ begin d:=abs(xx[q]-xx[p]);
+ if d<yy[q]-yy[p] then d:=yy[q]-yy[p];
+ if d<d0 then twin:=true
+ else if d<d_min then
+ begin d_min:=d; best_q:=q;
+ end;
+ q:=next[q];
+ end
+
+@ @<Search for the nearest dot in nodes preceding |p|@>=
+q:=prev[p];
+while yy[q]>yy[p]-d_min do
+ begin d:=abs(xx[q]-xx[p]);
+ if d<yy[p]-yy[q] then d:=yy[p]-yy[q];
+ if d<d0 then twin:=true
+ else if d<d_min then
+ begin d_min:=d; best_q:=q;
+ end;
+ q:=prev[q];
+ end
+
+@* Doing the labels.
+Each ``character'' in the \.{GF} file is preceded by a number of special
+commands that define labels, titles, rules, etc. We store these away,
+to be considered later when the |boc| command appears. The |boc|
+command establishes the size information by which labels and rules
+can be positioned, so we spew out the label information as soon as
+we see the |boc|. The gray pixels will be typeset after all the labels
+for a particular character have been finished.
+
+@ Here is the part of \.{GFtoDVI} that stores information preceding a~|boc|.
+It comes into play when |cur_gf| is between |xxx1| and~|no_op|, inclusive.
+
+@d font_change(#)==if fonts_not_loaded then
+ begin #; end
+ else print_nl('(Tardy font change will be ignored (byte ',
+@.Tardy font change...@>
+ cur_loc:1,')!)')
+
+@<Process a no-op command@>=
+begin k:=interpret_xxx;
+case k of
+no_operation: do_nothing;
+title_font,label_font,gray_font,slant_font:font_change(font_name[k]:=cur_string;
+ font_area[k]:=null_string;font_at[k]:=0;init_str_ptr:=str_ptr);
+title_font+area_code,label_font+area_code,gray_font+area_code,
+ slant_font+area_code:@|
+ font_change(font_area[k-area_code]:=cur_string;init_str_ptr:=str_ptr);
+title_font+at_code,label_font+at_code,gray_font+at_code,
+ slant_font+at_code:@|
+ font_change(font_at[k-at_code]:=get_yyy;init_str_ptr:=str_ptr);
+rule_thickness_code:rule_thickness:=get_yyy;
+rule_code:@<Store a rule@>;
+offset_code:@<Override the offsets@>;
+x_offset_code:x_offset:=get_yyy;
+y_offset_code:y_offset:=get_yyy;
+title_code:@<Store a title@>;
+null_string:@<Store a label@>;
+end; {there are no other cases}
+end
+
+@ The following quantities are cleared just before reading the
+\.{GF} commands pertaining to a character.
+
+@<Glob...@>=
+@!rule_thickness:scaled; {the current rule thickness
+ (zero means use the default)}
+@!offset_x,@!offset_y:scaled; {the current offsets for images}
+@!x_offset,@!y_offset:scaled; {the current offsets for labels}
+@!pre_min_x,@!pre_max_x,@!pre_min_y,@!pre_max_y:scaled;
+ {extreme values of coordinates preceding a character, in \MF\ pixels}
+
+@ @<Initialize variables for the next character@>=
+rule_thickness:=0;
+offset_x:=0; offset_y:=0; x_offset:=0; y_offset:=0;
+pre_min_x:=@'2000000000; pre_max_x:=-@'2000000000;
+pre_min_y:=@'2000000000; pre_max_y:=-@'2000000000;
+
+@ @<Override the offsets@>=
+begin offset_x:=get_yyy; offset_y:=get_yyy;
+end
+
+@ Rules that will need to be drawn are kept in a linked list accessible
+via |rule_ptr|, in last-in-first-out order. The nodes of this list will
+never get into the doubly linked list, and indeed these nodes use different
+field conventions entirely (because rules may be slanted).
+
+@d x0==xl {starting |x| coordinate of a stored rule}
+@d y0==yt {starting |y| coordinate (in scaled \MF\ pixels)}
+@d x1==xr {ending |x| coordinate of a stored rule}
+@d y1==yb {ending |y| coordinate of a stored rule}
+@d rule_size==xx {thickness of a stored rule, in scaled points}
+
+@<Glob...@>=
+@!rule_ptr:node_pointer; {top of the stack of remembered rules}
+
+@ @<Store a rule@>=
+begin p:=get_avail; next[p]:=rule_ptr; rule_ptr:=p;@/
+x0[p]:=get_yyy; y0[p]:=get_yyy; x1[p]:=get_yyy; y1[p]:=get_yyy;
+if x0[p]<pre_min_x then pre_min_x:=x0[p];
+if x0[p]>pre_max_x then pre_max_x:=x0[p];
+if y0[p]<pre_min_y then pre_min_y:=y0[p];
+if y0[p]>pre_max_y then pre_max_y:=y0[p];
+if x1[p]<pre_min_x then pre_min_x:=x1[p];
+if x1[p]>pre_max_x then pre_max_x:=x1[p];
+if y1[p]<pre_min_y then pre_min_y:=y1[p];
+if y1[p]>pre_max_y then pre_max_y:=y1[p];
+rule_size[p]:=rule_thickness;
+end
+
+@ Titles and labels are, likewise, stored temporarily in singly linked lists.
+In this case the lists are first-in-first-out.
+Variables |title_tail| and |label_tail| point to the most recently inserted
+title or label; variables |title_head| and |label_head|
+point to the beginning of the list. (A standard coding trick is used
+for |label_head|, which is kept in |next[end_of_list]|; we have
+|label_tail=end_of_list| when the list is empty.)
+
+The |prev| field in nodes of the temporary label list specifies the
+type of label, so we call it |lab_typ|.
+
+@d lab_typ==prev {the type of a stored label (|"/"..."8"|)}
+@d label_head==next[end_of_list]
+
+@<Glob...@>=
+@!label_tail:node_pointer; {tail of the queue of remembered labels}
+@!title_head,@!title_tail:node_pointer; {head and tail of the queue for titles}
+
+@ We must start the lists out empty.
+
+@<Initialize variables for the next char...@>=
+rule_ptr:=null;
+title_head:=null; title_tail:=null; label_head:=null; label_tail:=end_of_list;
+first_dot:=max_labels;
+
+@ @<Store a title@>=
+begin p:=get_avail; info[p]:=cur_string;
+if title_head=null then title_head:=p
+else next[title_tail]:=p;
+title_tail:=p;
+end
+
+@ We store the coordinates of each label in units of \MF\ pixels; they
+will be converted to \.{DVI} coordinates later.
+
+@<Store a label@>=
+if (label_type<"/")or(label_type>"8") then
+ print_nl('Bad label type precedes byte ',cur_loc:1,'!')
+@.Bad label type...@>
+else begin p:=get_avail; next[label_tail]:=p; label_tail:=p;@/
+ lab_typ[p]:=label_type; info[p]:=cur_string;@/
+ xx[p]:=get_yyy; yy[p]:=get_yyy;
+ if xx[p]<pre_min_x then pre_min_x:=xx[p];
+ if xx[p]>pre_max_x then pre_max_x:=xx[p];
+ if yy[p]<pre_min_y then pre_min_y:=yy[p];
+ if yy[p]>pre_max_y then pre_max_y:=yy[p];
+ end
+
+@ The process of ferreting everything away comes to an abrupt halt
+when a |boc| command is sensed. The following steps are performed
+at such times:
+
+@<Process a character@>=
+begin check_fonts;
+@<Finish reading the parameters of the |boc|@>;
+@<Get ready to convert \MF\ coordinates to \.{DVI} coordinates@>;
+@<Output the |bop| and the title line@>;
+print('[',total_pages:1); update_terminal; {print a progress report}
+@<Output all rules for the current character@>;
+@<Output all labels for the current character@>;
+do_pixels;
+dvi_out(eop); {finish the page}
+@<Adjust the maximum page width@>;
+print(']'); update_terminal;
+end
+
+@ @<Finish reading the parameters of the |boc|@>=
+if cur_gf=boc then
+ begin ext:=signed_quad; {read the character code}
+ char_code:=ext mod 256;
+ if char_code<0 then char_code:=char_code+256;
+ ext:=(ext-char_code) div 256;
+ k:=signed_quad; {read and ignore the prev pointer}
+ min_x:=signed_quad; {read the minimum $x$ coordinate}
+ max_x:=signed_quad; {read the maximum $x$ coordinate}
+ min_y:=signed_quad; {read the minimum $y$ coordinate}
+ max_y:=signed_quad; {read the maximum $y$ coordinate}
+ end
+else begin ext:=0; char_code:=get_byte; {|cur_gf=boc1|}
+ min_x:=get_byte; max_x:=get_byte; min_x:=max_x-min_x;@/
+ min_y:=get_byte; max_y:=get_byte; min_y:=max_y-min_y;
+ end;
+if max_x-min_x>widest_row then abort('Character too wide!')
+@.Character too wide@>
+
+@ @<Glob...@>=
+@!char_code,@!ext:integer; {the current character code and extension}
+@!min_x,@!max_x,@!min_y,@!max_y:integer; {character boundaries, in pixels}
+@!x,@!y:integer; {current painting position, in pixels}
+@!z:integer; {initial painting position in row, relative to |min_x|}
+
+@ \MF\ coordinates $(x,y)$ are converted to \.{DVI} coordinates by the
+following routine. Real values |x_ratio|, |y_ratio|, and |slant_ratio|
+will have been calculated based on the gray font; |scaled| values
+|delta_x| and |delta_y| will have been computed so that, in the absence
+of slanting and offsets, the \MF\ coordinates |(min_x,max_y+1)| will correspond
+to the \.{DVI} coordinates $(0,50\,\rm pt)$.
+
+@p procedure convert(@!x,@!y:scaled);
+begin x:=x+x_offset; y:=y+y_offset;
+dvi_y:=-round(y_ratio*y)+delta_y;
+dvi_x:=round(x_ratio*x+slant_ratio*y)+delta_x;
+end;
+
+@ @<Glob...@>=
+@!x_ratio,@!y_ratio,@!slant_ratio:real; {conversion factors}
+@!unsc_x_ratio,@!unsc_y_ratio,@!unsc_slant_ratio:real;
+ {ditto, times |unity|}
+@!fudge_factor:real; {unconversion factor}
+@!delta_x,@!delta_y:scaled; {magic constants used by |convert|}
+@!dvi_x,@!dvi_y:scaled; {outputs of |convert|, in scaled points}
+@!over_col:scaled; {overflow labels start here}
+@!page_height,page_width:scaled; {size of the current page}
+
+@ @<Initialize global variables that depend on the font data@>=
+i:=char_info(gray_font)(1);
+if not char_exists(i) then abort('Missing pixel char!');
+@.Missing pixel char@>
+unsc_x_ratio:=char_width(gray_font)(i);
+x_ratio:=unsc_x_ratio/unity;
+unsc_y_ratio:=char_height(gray_font)(height_depth(i));
+y_ratio:=unsc_y_ratio/unity;
+unsc_slant_ratio:=slant(gray_font)*y_ratio;
+slant_ratio:=unsc_slant_ratio/unity;
+if x_ratio*y_ratio=0 then abort('Vanishing pixel size!');
+@.Vanishing pixel size@>
+fudge_factor:=(slant_ratio/x_ratio)/y_ratio;
+
+@ @<Get ready to convert...@>=
+if pre_min_x<min_x*unity then offset_x:=offset_x+min_x*unity-pre_min_x;
+if pre_max_y>max_y*unity then offset_y:=offset_y+max_y*unity-pre_max_y;
+if pre_max_x>max_x*unity then pre_max_x:=pre_max_x div unity
+else pre_max_x:=max_x;
+if pre_min_y<min_y*unity then pre_min_y:=pre_min_y div unity
+else pre_min_y:=min_y;
+delta_y:=round(unsc_y_ratio*(max_y+1)-y_ratio*offset_y)+3276800;
+delta_x:=round(x_ratio*offset_x-unsc_x_ratio*min_x);
+if slant_ratio>=0 then
+ over_col:=round(unsc_x_ratio*pre_max_x+unsc_slant_ratio*max_y)
+else over_col:=round(unsc_x_ratio*pre_max_x+unsc_slant_ratio*min_y);
+over_col:=over_col+delta_x+10000000;
+page_height:=round(unsc_y_ratio*(max_y+1-pre_min_y))+3276800-offset_y;
+if page_height>max_v then max_v:=page_height;
+page_width:=over_col-10000000
+
+@ The |dvi_goto| subroutine outputs bytes to the \.{DVI} file that
+will initiate typesetting at given \.{DVI} coordinates, assuming that
+the current position of the \.{DVI} reader is $(0,0)$. This subroutine
+begins by outputting a |push| command; therefore, a |pop| command should
+be given later. That |pop| will restore the \.{DVI} position to $(0,0)$.
+
+@p procedure dvi_goto(@!x,@!y:scaled);
+begin dvi_out(push);
+if x<>0 then
+ begin dvi_out(right4); dvi_four(x);
+ end;
+if y<>0 then
+ begin dvi_out(down4); dvi_four(y);
+ end;
+end;
+
+@ @<Output the |bop| and the title line@>=
+dvi_out(bop); incr(total_pages); dvi_four(total_pages);
+dvi_four(char_code); dvi_four(ext);
+for k:=3 to 9 do dvi_four(0);
+dvi_four(last_bop); last_bop:=dvi_offset+dvi_ptr-45;@/
+dvi_goto(0,655360); {the top baseline is 10\thinspace pt down}
+if use_logo then
+ begin select_font(logo_font); hbox(small_logo,logo_font,true);
+ end;
+select_font(title_font); hbox(time_stamp,title_font,true);@/
+hbox(page_header,title_font,true); dvi_scaled(total_pages*65536.0);@/
+if (char_code<>0)or(ext<>0) then
+ begin hbox(char_header,title_font,true); dvi_scaled(char_code*65536.0);
+ if ext<>0 then
+ begin hbox(ext_header,title_font,true); dvi_scaled(ext*65536.0);
+ end;
+ end;
+if title_head<>null then
+ begin next[title_tail]:=null;
+ repeat hbox(left_quotes,title_font,true);
+ hbox(info[title_head],title_font,true);
+ hbox(right_quotes,title_font,true);
+ title_head:=next[title_head];
+ until title_head=null;
+ end;
+dvi_out(pop)
+
+@ @d tol==6554 {one tenth of a point, in \.{DVI} coordinates}
+
+@<Output all rules for the current character@>=
+if rule_slant<>0 then select_font(slant_font);
+while rule_ptr<>null do
+ begin p:=rule_ptr; rule_ptr:=next[p];@/
+ if rule_size[p]=0 then rule_size[p]:=gray_rule_thickness;
+ if rule_size[p]>0 then
+ begin convert(x0[p],y0[p]); temp_x:=dvi_x; temp_y:=dvi_y;
+ convert(x1[p],y1[p]);
+ if abs(temp_x-dvi_x)<tol then @<Output a vertical rule@>
+ else if abs(temp_y-dvi_y)<tol then @<Output a horizontal rule@>
+ else @<Try to output a diagonal rule@>;
+ end;
+ end
+
+@ @<Glob...@>=
+@!gray_rule_thickness:scaled; {thickness of rules, according to the gray font}
+@!temp_x,@!temp_y:scaled; {temporary registers for intermediate calculations}
+
+@ @<Initialize glob...@>=
+gray_rule_thickness:=default_rule_thickness(gray_font);
+if gray_rule_thickness=0 then gray_rule_thickness:=26214; {0.4\thinspace pt}
+
+@ @<Output a vertical rule@>=
+begin if temp_y>dvi_y then
+ begin k:=temp_y; temp_y:=dvi_y; dvi_y:=k;
+ end;
+dvi_goto(dvi_x-(rule_size[p] div 2), dvi_y);
+dvi_out(put_rule); dvi_four(dvi_y-temp_y); dvi_four(rule_size[p]);
+dvi_out(pop);
+end
+
+@ @<Output a horizontal rule@>=
+begin if temp_x<dvi_x then
+ begin k:=temp_x; temp_x:=dvi_x; dvi_x:=k;
+ end;
+dvi_goto(dvi_x,dvi_y+(rule_size[p] div 2));
+dvi_out(put_rule); dvi_four(rule_size[p]); dvi_four(temp_x-dvi_x);
+dvi_out(pop);
+end
+
+@ @<Try to output a diagonal rule@>=
+if (rule_slant=0)or@|
+ (abs(temp_x+rule_slant*(temp_y-dvi_y)-dvi_x)>rule_size[p]) then
+ slant_complaint((dvi_x-temp_x)/(temp_y-dvi_y))
+else begin if temp_y>dvi_y then
+ begin k:=temp_y; temp_y:=dvi_y; dvi_y:=k;@/
+ k:=temp_x; temp_x:=dvi_x; dvi_x:=k;
+ end;
+ m:=round((dvi_y-temp_y)/slant_unit);
+ if m>0 then
+ begin dvi_goto(dvi_x,dvi_y);
+ q:=((m-1) div slant_n)+1; k:=m div q;
+ p:=m mod q; q:=q-p;
+ @<Vertically typeset |q| copies of character |k|@>;
+ @<Vertically typeset |p| copies of character |k+1|@>;
+ dvi_out(pop);
+ end;
+ end
+
+@ @<Vertically typeset |q| copies of character |k|@>=
+typeset(k); dy:=round(k*slant_unit); dvi_out(z4); dvi_four(-dy);
+while q>1 do
+ begin typeset(k); dvi_out(z0); decr(q);
+ end
+
+@ @<Vertically typeset |p| copies of character |k+1|@>=
+if p>0 then
+ begin incr(k); typeset(k);
+ dy:=round(k*slant_unit); dvi_out(z4); dvi_four(-dy);
+ while p>1 do
+ begin typeset(k); dvi_out(z0); decr(p);
+ end;
+ end
+
+@ Now we come to a more interesting part of the computation, where we
+go through the stored labels and try to fit them in the illustration for
+the current character, together with their associated dots.
+
+It would simplify font-switching slightly if we were to typeset the labels
+first, but we find it desirable to typeset the dots first and then turn to the
+labels. This procedure makes it possible for us to allow the dots to
+overlap each other without allowing the labels to overlap. After the
+dots are in place, we typeset all prescribed labels, that is, labels with a
+|lab_typ| of |"1".."8"|; these, too, are allowed to overlap the dots and
+each other.
+
+@<Output all labels for the current character@>=
+overflow_line:=1;
+if label_head<>null then
+ begin next[label_tail]:=null; select_font(gray_font);
+ @<Output all dots@>;
+ @<Find nearest dots, to help in label positioning@>;
+ select_font(label_font);
+ @<Output all prescribed labels@>;
+ @<Output all attachable labels@>;
+ @<Output all overflow labels@>;
+ end
+
+@ @<Glob...@>=
+@!overflow_line:integer; {the number of labels that didn't fit, plus~1}
+
+@ A label that appears above its dot is considered to occupy a
+rectangle of height~$h+\Delta$, depth~$d$, and width~$w+2\Delta$, where
+$(h,w,d)$ are the height, width, and depth of the label computed by |hbox|,
+and $\Delta$ is an additional amount of blank space that keeps labels from
+coming too close to each other. (\.{GFtoDVI} arbitrarily defines $\Delta$
+to be one half the width of a space in the label font.) This label is
+centered over its dot, with its baseline $d+h'$ above the center of the dot;
+here $h'=|dot_height|$ is the height of character~0 in the gray font.
+
+Similarly, a label that appears below its dot is considered to occupy
+a rectangle of height~$h$, depth~$d+\Delta$, and width~$w+2\Delta$; the
+baseline is $h+h'$ below the center of the dot.
+
+A label at the right of its dot is considered to occupy a rectangle of
+height~$h+\Delta$, depth~$d+\Delta$, and width~$w+\Delta$. Its
+reference point can be found by starting at the center of the dot and
+moving right $w'=|dot_width|$ (i.e., the width of character~0 in the
+gray font), then moving down by half the x-height of the label font.
+A label at the left of its dot is similar.
+
+A dot is considered to occupy a rectangle of height $2h'$ and width~$2w'$,
+centered on the dot.
+
+When the label type is |"1"| or more, the labels
+are put into the doubly linked list unconditionally.
+ Otherwise they are put into the list
+only if we can find a way to fit them in without
+overlapping any previously inserted rectangles.
+
+@<Glob...@>=
+@!delta:scaled; {extra padding to keep labels from being too close}
+@!half_x_height:scaled; {amount to drop baseline of label below the dot center}
+@!thrice_x_height:scaled; {baseline separation for overflow labels}
+@!dot_width,@!dot_height:scaled; {$w'$ and $h'$ in the discussion above}
+
+@ @<Initialize global variables that depend on the font data@>=
+i:=char_info(gray_font)(0);
+if not char_exists(i) then abort('Missing dot char!');
+@.Missing dot char@>
+dot_width:=char_width(gray_font)(i);
+dot_height:=char_height(gray_font)(height_depth(i));
+delta:=space(label_font) div 2;
+thrice_x_height:=3*x_height(label_font);
+half_x_height:=thrice_x_height div 6;
+
+@ Here is a subroutine that computes the rectangle boundaries
+|xl[p]|, |xr[p]|, |yt[p]|, |yb[p]|, and the reference point coordinates
+|xx[p]|,~|yy[p]|, for a label that is to be placed above a dot.
+The coordinates of the dot's center are assumed given in |dvi_x|
+and |dvi_y|; the |hbox| subroutine is assumed to have
+already computed the height, width, and depth of the label box.
+
+@p procedure top_coords(@!p:node_pointer);
+begin xx[p]:=dvi_x-(box_width div 2); xl[p]:=xx[p]-delta;
+xr[p]:=xx[p]+box_width+delta;@/
+yb[p]:=dvi_y-dot_height; yy[p]:=yb[p]-box_depth;
+yt[p]:=yy[p]-box_height-delta;
+end;
+
+@ The other three label positions are handled by similar routines.
+
+@p procedure bot_coords(@!p:node_pointer);
+begin xx[p]:=dvi_x-(box_width div 2); xl[p]:=xx[p]-delta;
+xr[p]:=xx[p]+box_width+delta;@/
+yt[p]:=dvi_y+dot_height; yy[p]:=yt[p]+box_height;
+yb[p]:=yy[p]+box_depth+delta;
+end;
+@#
+procedure right_coords(@!p:node_pointer);
+begin xl[p]:=dvi_x+dot_width; xx[p]:=xl[p]; xr[p]:=xx[p]+box_width+delta;@/
+yy[p]:=dvi_y+half_x_height; yb[p]:=yy[p]+box_depth+delta;
+yt[p]:=yy[p]-box_height-delta;
+end;
+@#
+procedure left_coords(@!p:node_pointer);
+begin xr[p]:=dvi_x-dot_width; xx[p]:=xr[p]-box_width; xl[p]:=xx[p]-delta;@/
+yy[p]:=dvi_y+half_x_height; yb[p]:=yy[p]+box_depth+delta;
+yt[p]:=yy[p]-box_height-delta;
+end;
+
+@ @<Output all dots@>=
+p:=label_head; first_dot:=max_node+1;
+while p<>null do
+ begin convert(xx[p],yy[p]); xx[p]:=dvi_x; yy[p]:=dvi_y;
+ if lab_typ[p]<"5" then
+ @<Enter a dot for label |p| in the rectangle list,
+ and typeset the dot@>;
+ p:=next[p];
+ end
+
+@ We plant links between dots and their labels by using (or abusing) the
+|xl| and |info| fields, which aren't needed for their normal purposes.
+
+@d dot_for_label==xl
+@d label_for_dot==info
+
+@<Enter a dot...@>=
+begin q:=get_avail; dot_for_label[p]:=q; label_for_dot[q]:=p;@/
+xx[q]:=dvi_x; xl[q]:=dvi_x-dot_width; xr[q]:=dvi_x+dot_width;@/
+yy[q]:=dvi_y; yt[q]:=dvi_y-dot_height; yb[q]:=dvi_y+dot_height;@/
+node_ins(q,0);@/
+dvi_goto(xx[q],yy[q]); dvi_out(0); dvi_out(pop);
+end
+
+@ Prescribed labels are now taken out of the singly linked list and
+inserted into the doubly linked list.
+
+@<Output all prescribed labels@>=
+q:=end_of_list; {|label_head=next[q]|}
+while next[q]<>null do
+ begin p:=next[q];
+ if lab_typ[p]>"0" then
+ begin next[q]:=next[p];
+ @<Enter a prescribed label for node |p| into the rectangle list,
+ and typeset it@>;
+ end
+ else q:=next[q];
+ end
+
+@ @<Enter a prescr...@>=
+begin hbox(info[p],label_font,false); {Compute the size of this label}
+dvi_x:=xx[p]; dvi_y:=yy[p];
+if lab_typ[p]<"5" then r:=dot_for_label[p]@+else r:=0;
+case lab_typ[p] of
+"1","5":top_coords(p);
+"2","6":left_coords(p);
+"3","7":right_coords(p);
+"4","8":bot_coords(p);
+end; {no other cases are possible}
+node_ins(p,r);@/
+dvi_goto(xx[p],yy[p]); hbox(info[p],label_font,true); dvi_out(pop);
+end
+
+@ \.{GFtoDVI}'s algorithm for positioning the ``floating'' labels
+was devised by Arthur~L. Samuel.
+@^Samuel, Arthur Lee@>
+It tries to place labels in a priority order, based on the position of
+the nearest dot to a given dot. If that dot, for example, lies in the first
+octant (i.e., east to northeast of the given dot), the given label will
+be put into the west slot unless that slot is already blocked; then the
+south slot will be tried, etc.
+
+First we need to compute the octants. We also note if two or more dots
+are nearly coincident, since Samuel's algorithm modifies the priority
+order on that case. The information is temporarily recorded in the |xr| array.
+
+@d octant==xr {octant code for nearest dot, plus 8 for coincident dots}
+
+@<Find nearest dots, to help in label positioning@>=
+p:=label_head;
+while p<>null do
+ begin if lab_typ[p]<="0" then
+ @<Compute the octant code for floating label |p|@>;
+ p:=next[p];
+ end;
+
+@ There's a sneaky way to identify octant numbers, represented by the
+code shown here. (Remember that |y|~coordinates increase downward
+in the \.{DVI} convention.)
+
+@d first_octant=0
+@d second_octant=1
+@d third_octant=2
+@d fourth_octant=3
+@d fifth_octant=7
+@d sixth_octant=6
+@d seventh_octant=5
+@d eighth_octant=4
+
+@<Compute the octant code for floating label |p|@>=
+begin r:=dot_for_label[p]; q:=nearest_dot(r,10);
+if twin then octant[p]:=8@+else octant[p]:=0;
+if q<>null then
+ begin dx:=xx[q]-xx[r]; dy:=yy[q]-yy[r];
+ if dy>0 then octant[p]:=octant[p]+4;
+ if dx<0 then incr(octant[p]);
+ if dy>dx then incr(octant[p]);
+ if -dy>dx then incr(octant[p]);
+ end;
+end
+
+@ A procedure called |place_label| will try to place the remaining
+labels in turn. If it fails, we ``disconnect'' the dot from this
+label so that an unlabeled dot will not appear as a reference in the
+overflow column.
+
+@<Output all attachable labels@>=
+q:=end_of_list; {now |next[q]=label_head|}
+while next[q]<>null do
+ begin p:=next[q]; r:=next[p]; s:=dot_for_label[p];
+ if place_label(p) then next[q]:=r
+ else begin label_for_dot[s]:=null; {disconnect the dot}
+ if lab_typ[p]="/" then next[q]:=r {remove label from list}
+ else q:=p; {retain label in list for the overflow column}
+ end;
+ end
+
+@ Here is the |place_label| routine, which uses the previously computed
+|octant| information as a heuristic. If the label can be placed, it
+is inserted into the rectangle list and typeset.
+
+@p function place_label(@!p:node_pointer):boolean;
+label exit, found;
+var @!oct:0..15; {octant code}
+@!dfl:node_pointer; {saved value of |dot_for_label[p]|}
+begin hbox(info[p],label_font,false); {Compute the size of this label}
+dvi_x:=xx[p]; dvi_y:=yy[p];
+@<Find non-overlapping coordinates, if possible, and |goto| found;
+ otherwise set |place_label:=false| and |return|@>;
+found:node_ins(p,dfl);@/
+dvi_goto(xx[p],yy[p]); hbox(info[p],label_font,true); dvi_out(pop);
+place_label:=true;
+exit:end;
+
+@ @<Find non-overlapping coordinates, if possible...@>=
+dfl:=dot_for_label[p]; oct:=octant[p];
+@<Try the first choice for label direction@>;
+@<Try the second choice for label direction@>;
+@<Try the third choice for label direction@>;
+@<Try the fourth choice for label direction@>;
+xx[p]:=dvi_x; yy[p]:=dvi_y; dot_for_label[p]:=dfl; {no luck; restore the coordinates}
+place_label:=false; return
+
+@ @<Try the first choice for label direction@>=
+case oct of
+first_octant,eighth_octant,second_octant+8,seventh_octant+8: left_coords(p);
+second_octant,third_octant,first_octant+8,fourth_octant+8: bot_coords(p);
+fourth_octant,fifth_octant,third_octant+8,sixth_octant+8: right_coords(p);
+sixth_octant,seventh_octant,fifth_octant+8,eighth_octant+8: top_coords(p);
+end;
+if not overlap(p,dfl) then goto found
+
+@ @<Try the second choice for label direction@>=
+case oct of
+first_octant,fourth_octant,fifth_octant+8,eighth_octant+8: bot_coords(p);
+second_octant,seventh_octant,third_octant+8,sixth_octant+8: left_coords(p);
+third_octant,sixth_octant,second_octant+8,seventh_octant+8: right_coords(p);
+fifth_octant,eighth_octant,first_octant+8,fourth_octant+8: top_coords(p);
+end;
+if not overlap(p,dfl) then goto found
+
+@ @<Try the third choice for label direction@>=
+case oct of
+first_octant,fourth_octant,sixth_octant+8,seventh_octant+8: top_coords(p);
+second_octant,seventh_octant,fourth_octant+8,fifth_octant+8: right_coords(p);
+third_octant,sixth_octant,first_octant+8,eighth_octant+8: left_coords(p);
+fifth_octant,eighth_octant,second_octant+8,third_octant+8: bot_coords(p);
+end;
+if not overlap(p,dfl) then goto found
+
+@ @<Try the fourth choice for label direction@>=
+case oct of
+first_octant,eighth_octant,first_octant+8,eighth_octant+8: right_coords(p);
+second_octant,third_octant,second_octant+8,third_octant+8: top_coords(p);
+fourth_octant,fifth_octant,fourth_octant+8,fifth_octant+8: left_coords(p);
+sixth_octant,seventh_octant,sixth_octant+8,seventh_octant+8: bot_coords(p);
+end;
+if not overlap(p,dfl) then goto found
+
+@ @<Output all overflow labels@>=
+@<Remove all rectangles from list, except for dots that have labels@>;
+p:=label_head;
+while p<>null do
+ begin @<Typeset an overflow label for |p|@>;
+ p:=next[p];
+ end
+
+@ When we remove a dot that couldn't be labeled, we set its |next| field
+to the preceding node that survives, so that we can use the |nearest_dot|
+routine later. (This is a bit of a kludge.)
+
+@<Remove all rectangles from list, except for dots that have labels@>=
+p:=next[0];
+while p<>end_of_list do
+ begin q:=next[p];
+ if (p<first_dot) or (label_for_dot[p]=null) then
+ begin r:=prev[p]; next[r]:=q; prev[q]:=r; next[p]:=r;
+ end;
+ p:=q;
+ end
+
+@ Now we have to insert |p| into the list temporarily, because of the
+way |nearest_dot| works.
+
+@<Typeset an overflow label for |p|@>=
+begin r:=next[dot_for_label[p]]; s:=next[r]; t:=next[p];
+next[p]:=s; prev[s]:=p; next[r]:=p; prev[p]:=r;@/
+q:=nearest_dot(p,0);@/
+next[r]:=s; prev[s]:=r; next[p]:=t; {remove |p| again}
+incr(overflow_line);
+dvi_goto(over_col,overflow_line*thrice_x_height+655360);
+hbox(info[p],label_font,true);
+if q<>null then
+ begin hbox(equals_sign,label_font,true);
+ hbox(info[label_for_dot[q]],label_font,true);
+ hbox(plus_sign,label_font,true);
+ dvi_scaled((xx[p]-xx[q])/x_ratio+(yy[p]-yy[q])*fudge_factor);
+ dvi_out(",");
+ dvi_scaled((yy[q]-yy[p])/y_ratio);
+ dvi_out(")");
+ end;
+dvi_out(pop);
+end
+
+@ @<Adjust the maximum page width@>=
+if overflow_line>1 then page_width:=over_col+10000000;
+ {overflow labels are estimated to occupy $10^7\,$sp}
+if page_width>max_h then max_h:=page_width
+
+@* Doing the pixels.
+The most interesting part of \.{GFtoDVI} is the way it makes use of a gray
+font to typeset the pixels of a character. In fact, the author must admit having
+great fun devising the algorithms below. Perhaps the reader will also
+enjoy reading them.
+
+The basic idea will be to use an array of 12-bit integers to represent the next
+twelve rows that need to be typeset. The binary expansions of these integers,
+reading from least significant bit to most significant bit, will represent
+pixels from top to bottom.
+
+@ We have already used such a binary representation in the tables
+|c[1..120]| and |d[1..120]| of bit patterns and lengths that are potentially
+present in a gray font; we shall now use those tables to compute
+an auxiliary array |b[0..4095]|. Given a 12-bit number~$v$, the gray-font
+character appropriate to $v$'s binary pattern will be~|b[v]|. If no
+character should be typeset for this pattern in the current row,
+|b[v]| will be~0.
+
+The array |b| can have many different configurations, depending on how
+many characters are actually present in the gray font. But
+it's not difficult to compute |b| by going through the existing characters
+in increasing order and marking all patterns~$x$ to which they apply.
+
+@<Initialize glob...@>=
+for k:=0 to 4095 do b[k]:=0;
+for k:=font_bc[gray_font] to font_ec[gray_font] do
+ if k>=1 then if k<=120 then
+ if char_exists(char_info(gray_font)(k)) then
+ begin v:=c[k];
+ repeat b[v]:=k; v:=v+d[k];
+ until v>4095;
+ end;
+
+@ We also compute an auxiliary array |rho[0..4095]| such that $\\{rho}[v]=2^j$
+when |v| is an odd multiple of~$2^j$; we also set $\\{rho}[0]=2^{12}$.
+
+@<Initialize g...@>=
+for j:=0 to 11 do
+ begin k:=two_to_the[j]; v:=k;
+ repeat rho[v]:=k; v:=v+k+k;
+ until v>4095;
+ end;
+rho[0]:=4096;
+
+@ @<Glob...@>=
+@!b:array[0..4095] of 0..120; {largest existing character for a given pattern}
+@!rho:array[0..4095] of 1..4096; {the ``ruler function''}
+
+@ But how will we use these tables? Let's imagine that the \.{DVI} file
+already contains instructions that have selected the gray font and moved
+to the proper horizontal coordinate for the row that we wish to process next.
+Let's suppose that 12-bit patterns have been set up in array~|a|, and that
+the global variables |starting_col| and |finishing_col| are known such
+that |a[j]| is zero unless |starting_col<=j<=finishing_col|. Here's what
+we can do, assuming that appropriate local variables and labels have
+been declared:
+
+@<Typeset the pixels of the current row@>=
+j:=starting_col;
+loop@+ begin while (j<=finishing_col)and(b[a[j]]=0) do incr(j);
+ if j>finishing_col then goto done;
+ dvi_out(push); @<Move to column |j| in the \.{DVI} output@>;
+ repeat v:=b[a[j]]; a[j]:=a[j]-c[v];
+ k:=j; incr(j);
+ while b[a[j]]=v do
+ begin a[j]:=a[j]-c[v]; incr(j);
+ end;
+ k:=j-k; @<Output the equivalent of |k| copies of character |v|@>;
+ until b[a[j]]=0;
+ dvi_out(pop);
+ end;
+done:
+
+@ @<Move to column |j| in the \.{DVI} output@>=
+dvi_out(right4);
+dvi_four(round(unsc_x_ratio*j+unsc_slant_ratio*y)+delta_x)
+
+@ The doubling-up property of gray font character lists is utilized here.
+
+@<Output the equivalent of |k| copies of character |v|@>=
+reswitch: if k=1 then typeset(v)
+else begin i:=char_info(gray_font)(v);
+ if char_tag(i)=list_tag then {|v| has a successor}
+ begin if odd(k) then typeset(v);
+ k:=k div 2; v:=qo(rem_byte(i)); goto reswitch;
+ end
+ else repeat typeset(v); decr(k);
+ until k=0;
+ end
+
+@ @<Glob...@>=
+@!a:array[0..widest_row] of 0..4095; {bit patterns for twelve rows}
+
+@ In order to use the approach above, we need to be able to initialize
+array~|a|, and we need to be able to keep it up to date as new rows
+scroll by. A moment's thought about the problem reveals that we will either
+have to read an entire character from the \.{GF} file into memory,
+or we'll need to adopt a coroutine-like approach: A single \\{skip}
+command in the \.{GF} file might need to be processed in pieces, since
+it might generate more rows of zeroes than we are ready to absorb
+all at once into~|a|.
+
+The coroutine method actually turns out to be quite simple, so we shall
+introduce a global variable |blank_rows|, which tells how many rows of
+blanks should be generated before we read the \.{GF} instructions
+for another row.
+
+@<Glob...@>=
+@!blank_rows:integer;
+ {rows of blanks carried over from a previous \.{GF} command}
+
+@ Initialization and updating of~|a| can now be handled as follows,
+if we introduce another variable~|l| that is set initially to~1:
+
+@<Add more rows to |a|, until 12-bit entries are obtained@>=
+repeat @<Put the bits for the next row, times |l|, into |a|@>;
+l:=l+l; decr(y);
+until l=4096;
+
+@ As before, |cur_gf| will contain the first \.{GF} command that has
+not yet been interpreted.
+
+@<Put the bits...@>=
+if blank_rows>0 then decr(blank_rows)
+else if cur_gf<>eoc then
+ begin x:=z;
+ if starting_col>x then starting_col:=x;
+ @<Read and process \.{GF} commands until coming to the end of this row@>;
+ end;
+
+@ @d do_skip==z:=0; paint_black:=false
+@d end_with(#)==begin #; cur_gf:=get_byte; goto done1;@+end
+@d five_cases(#)==#,#+1,#+2,#+3,#+4
+@d eight_cases(#)==#,#+1,#+2,#+3,#+4,#+5,#+6,#+7
+@d thirty_two_cases(#)==eight_cases(#),eight_cases(#+8),
+ eight_cases(#+16), eight_cases(#+24)
+@d sixty_four_cases(#)==thirty_two_cases(#), thirty_two_cases(#+32)
+
+@<Read and process...@>=
+loop @+begin continue: case cur_gf of
+ sixty_four_cases(0): k:=cur_gf;
+ paint1:k:=get_byte;
+ paint2:k:=get_two_bytes;
+ paint3:k:=get_three_bytes;
+ eoc:goto done1;
+ skip0:end_with(blank_rows:=0; do_skip);
+ skip1:end_with(blank_rows:=get_byte; do_skip);
+ skip2:end_with(blank_rows:=get_two_bytes; do_skip);
+ skip3:end_with(blank_rows:=get_three_bytes; do_skip);
+ sixty_four_cases(new_row_0),sixty_four_cases(new_row_0+64),
+ thirty_two_cases(new_row_0+128),five_cases(new_row_0+160):
+ end_with(z:=cur_gf-new_row_0;paint_black:=true);
+ xxx1,xxx2,xxx3,xxx4,yyy,no_op:begin skip_nop; goto continue;
+ end;
+ othercases bad_gf('Improper opcode')
+ endcases;@/
+ @<Paint |k| bits and read another command@>;
+ end;
+done1:
+
+@ @<Paint |k| bits and read another command@>=
+if x+k>finishing_col then finishing_col:=x+k;
+if paint_black then for j:=x to x+k-1 do a[j]:=a[j]+l;
+paint_black:=not paint_black;
+x:=x+k;
+cur_gf:=get_byte
+
+@ When the current row has been typeset, all entries of |a| will be even;
+we want to divide them by~2 and incorporate a new row with $l=2^{11}$.
+However, if they are all multiples of~4, we actually want to divide by~4
+and incorporate two new rows, with $l=2^{10}$ and $l=2^{11}$. In general,
+we want to divide by the maximum possible power of~2 and add the corresponding
+number of new rows; that's where the |rho|~array comes in handy:
+
+@<Advance to the next row that needs to be typeset;
+ or |return|, if we're all done@>=
+l:=rho[a[starting_col]];
+for j:=starting_col+1 to finishing_col do if l>rho[a[j]] then l:=rho[a[j]];
+if l=4096 then
+ if cur_gf=eoc then return
+ else begin y:=y-blank_rows; blank_rows:=0; l:=1;
+ starting_col:=z; finishing_col:=z;
+ end
+else begin while a[starting_col]=0 do incr(starting_col);
+ while a[finishing_col]=0 do decr(finishing_col);
+ for j:=starting_col to finishing_col do a[j]:=a[j] div l;
+ l:=4096 div l;
+ end
+
+@ We now have constructed the major components of the necessary routine;
+it simply remains to glue them all together in the proper framework.
+
+@p procedure do_pixels;
+label done,done1,reswitch,continue,exit;
+var @!paint_black:boolean; {the paint switch}
+@!starting_col,@!finishing_col:0..widest_row; {currently nonzero area}
+@!j:0..widest_row; {for traversing that area}
+@!l:integer; {power of two used to manipulate bit patterns}
+@!i:four_quarters; {character information word}
+@!v:eight_bits; {character corresponding to a pixel pattern}
+begin select_font(gray_font);
+delta_x:=delta_x+round(unsc_x_ratio*min_x);
+for j:=0 to max_x-min_x do a[j]:=0;
+l:=1; z:=0; starting_col:=0; finishing_col:=0; y:=max_y+12; paint_black:=false;
+blank_rows:=0; cur_gf:=get_byte;
+loop@+ begin @<Add more rows...@>;
+ dvi_goto(0,delta_y-round(unsc_y_ratio*y)); @<Typeset the pixels...@>;
+ dvi_out(pop); @<Advance to the next...@>;
+ end;
+exit:end;
+
+@* The main program.
+Now we are ready to put it all together. This is where \.{GFtoDVI} starts,
+and where it ends.
+
+@p begin initialize; {get all variables initialized}
+@<Initialize the strings@>;
+start_gf; {open the input and output files}
+@<Process the preamble@>;
+cur_gf:=get_byte; init_str_ptr:=str_ptr;
+loop@+ begin @<Initialize variables for the next character@>;
+ while (cur_gf>=xxx1)and(cur_gf<=no_op) do @<Process a no-op command@>;
+ if cur_gf=post then @<Finish the \.{DVI} file and |goto final_end|@>;
+ if cur_gf<>boc then if cur_gf<>boc1 then abort('Missing boc!');
+@.Missing boc@>
+ @<Process a character@>;
+ cur_gf:=get_byte; str_ptr:=init_str_ptr; pool_ptr:=str_start[str_ptr];
+ end;
+final_end:end.
+
+@ The main program needs a few global variables in order to do its work.
+
+@<Glob...@>=
+@!k,@!m,@!p,@!q,@!r,@!s,@!t,@!dx,@!dy:integer; {general purpose registers}
+@!time_stamp:str_number; {the date and time when the input file was made}
+@!use_logo:boolean; {should \MF's logo be put on the title line?}
+
+@ \MF\ sets the opening string to 32 bytes that give date and time as follows:
+$$\hbox{|' METAFONT output yyyy.mm.dd:tttt'|}$$
+We copy this to the \.{DVI} file, but remove the `\.{METAFONT}' part so that
+it can be replaced by its proper logo.
+
+@<Process the preamble@>=
+if get_byte<>pre then bad_gf('No preamble');
+@.No preamble@>
+if get_byte<>gf_id_byte then bad_gf('Wrong ID');
+@.Wrong ID@>
+k:=get_byte; {|k| is the length of the initial string to be copied}
+for m:=1 to k do append_char(get_byte);
+dvi_out(pre); dvi_out(dvi_id_byte); {output the preamble}
+dvi_four(25400000); dvi_four(473628672); {conversion ratio for sp}
+dvi_four(1000); {magnification factor}
+dvi_out(k); use_logo:=false; s:=str_start[str_ptr];
+for m:=1 to k do dvi_out(str_pool[s+m-1]);
+if str_pool[s]=" " then
+ if str_pool[s+1]="M" then
+ if str_pool[s+2]="E" then
+ if str_pool[s+3]="T" then
+ if str_pool[s+4]="A" then
+ if str_pool[s+5]="F" then
+ if str_pool[s+6]="O" then
+ if str_pool[s+7]="N" then
+ if str_pool[s+8]="T" then
+ begin incr(str_ptr); str_start[str_ptr]:=s+9; use_logo:=true;
+ end; {we will substitute `\MF' for \.{METAFONT}}
+time_stamp:=make_string
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtoDVI} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* Index.
+Here is a list of the section numbers where each identifier is used.
+Cross references to error messages and a few other tidbits of information
+also appear.
diff --git a/systems/knuth/dist/mfware/gftopk.web b/systems/knuth/dist/mfware/gftopk.web
new file mode 100644
index 0000000000..ab9ba10465
--- /dev/null
+++ b/systems/knuth/dist/mfware/gftopk.web
@@ -0,0 +1,2193 @@
+% This program is by Tomas Rokicki. A few routines were borrowed from
+% GFtoPXL by Arthur Samuel, who borrowed from GFtype by DRF and DEK,
+% who borrowed from DVItype, and so on.
+
+% Version 0.0 (development): started 26 July 1985 TGR.
+% Version 1.0: finished 29 July 1985 TGR.
+% Version 1.1: revised for new pk format 9 August 1985 TGR.
+% Version 1.2: fixed two's complement bug 23 January 1985 TGR.
+% Version 1.3: fixed bounding box calculations and some documentation.
+% 7 September 1986 TGR
+% Version 1.4: fixed row to glyph conversion 14 November 1987 TGR
+% Version 1.5: eliminated semicolons before endcases 12 July 1988 TGR
+% Version 2.0: slightly tuned up for METAFONTware report 17 Apr 1989 DEK/TGR
+% Version 2.1: fixed paint0/endrow bug reported by John Hobby 31 Jul 1989 TGR
+% Version 2.2: minor tune up; retain previous source info 21 Nov 1989 don
+% Version 2.3: fixed a few bugs with selection of preamble types, if
+% gf_ch < 0, or if comp_size = 1016 (both unlikely). Removed some
+% code that would never get executed since bad_gf terminates. Also
+% some other nits that don't really affect functionality. 29 Jul 1990 TGR
+% Bugs and fixes reported by Peter Breitenlohner (PEB).
+% Corrected two typos -- 21 Dec 96 (don)
+% Version 2.4: fixed cases that might move to negative. 06 January 2014 PEB
+
+\def\versiondate{06 January 2014}
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\noindent\ignorespaces}
+\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\font\tenss=cmss10 % for `The METAFONTbook'
+\def\PASCAL{Pascal}
+\def\ph{{\mc PASCAL-H}}
+\font\logo=manfnt % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\def\<#1>{$\langle#1\rangle$}
+\def\section{\mathhexbox278}
+\let\swap=\leftrightarrow
+\def\round{\mathop{\rm round}\nolimits}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
+
+\def\title{GFtoPK}
+\def\contentspagenumber{201}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont GFtoPK} processor}
+ \vskip 15pt
+ \centerline{(Version 2.4, \versiondate)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926, MCS-8300984, and
+ CCR-8610181,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.
+ `{\logo hijklmnj}\kern1pt' is a trademark of Addison-Wesley
+ Publishing Company.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+This program reads a \.{GF} file and packs it into a \.{PK} file. \.{PK} files
+are significantly smaller than \.{GF} files, and they are much easier to
+interpret. This program is meant to be the bridge between \MF\ and \.{DVI}
+drivers that read \.{PK} files. Here are some statistics comparing typical
+input and output file sizes:
+
+$$\vbox{
+\halign{#\hfil\quad&\hfil#\qquad&&\hfil#\quad\cr
+Font&\omit\hfil Resolution\hfil\quad
+ &\.{GF} size&\.{PK} size&Reduction factor\cr
+\noalign{\medskip}
+cmr10&300&13200&5484&42\char`\%\cr
+cmr10&360&15342&6496&42\char`\%\cr
+cmr10&432&18120&7808&43\char`\%\cr
+cmr10&511&21020&9440&45\char`\%\cr
+cmr10&622&24880&11492&46\char`\%\cr
+cmr10&746&29464&13912&47\char`\%\cr
+cminch&300&48764&22076&45\char`\%\cr
+}}$$
+It is hoped that the simplicity and small size of the \.{PK} files will make
+them widely accepted.
+
+The \.{PK} format was designed and implemented by Tomas Rokicki during
+@^Rokicki, Tomas Gerhard Paul@>
+the summer of 1985. This program borrows a few routines from \.{GFtoPXL} by
+Arthur Samuel.
+@^Samuel, Arthur Lee@>
+
+The |banner| string defined here should be changed whenever \.{GFtoPK}
+gets modified. The |preamble_comment| macro (near the end of the program)
+should be changed too.
+
+@d banner=='This is GFtoPK, Version 2.4' {printed when the program starts}
+
+@ Some of the diagnostic information is printed using
+|d_print_ln|. When debugging, it should be set the same as
+|print_ln|, defined later.
+@^debugging@>
+
+@d d_print_ln(#)==
+
+@ This program is written in standard \PASCAL, except where it is
+necessary to use extensions; for example, one extension is to use a
+default |case| as in \.{TANGLE}, \.{WEAVE}, etc. All places where
+nonstandard constructions are used should be listed in the index under
+``system dependencies.''
+@!@^system dependencies@>
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The binary input comes from |gf_file|, and the output font is written
+on |pk_file|. All text output is written on \PASCAL's standard |output|
+file. The term |print| is used instead of |write| when this program writes
+on |output|, so that all such output could easily be redirected if desired.
+
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+
+@p program GFtoPK(@!gf_file,@!pk_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ If the program has to stop prematurely, it goes to the
+`|final_end|'.
+
+@d final_end=9999 {label for the end of it all}
+
+@<Labels...@>=final_end;
+
+@ The following parameters can be changed at compile time to extend or
+reduce \.{GFtoPK}'s capacity. The values given here should be quite
+adequate for most uses. Assuming an average of about three strokes per
+raster line, there are six run-counts per line, and therefore |max_row|
+will be sufficient for a character 2600 pixels high.
+
+@<Constants...@>=
+@!line_length=79; {bracketed lines of output will be at most this long}
+@!max_row=16000; {largest index in the main |row| array}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+
+@ If the \.{GF} file is badly malformed, the whole process must be aborted;
+\.{GFtoPK} will give up, after issuing an error message about the symptoms
+that were noticed.
+
+Such errors might be discovered inside of subroutines inside of subroutines,
+so a procedure called |jump_out| has been introduced. This procedure, which
+simply transfers control to the label |final_end| at the end of the program,
+contains the only non-local |goto| statement in \.{GFtoPK}.
+@^system dependencies@>
+
+@d abort(#)==begin print(' ',#); jump_out;
+ end
+@d bad_gf(#)==abort('Bad GF file: ',#,'!')
+@.Bad GF file@>
+
+@p procedure jump_out;
+begin goto final_end;
+end;
+
+@* The character set.
+Like all programs written with the \.{WEB} system, \.{GFtoPK} can be
+used with any character set. But it uses ASCII code internally, because
+the programming for portable input-output is easier when a fixed internal
+code is used.
+
+The next few sections of \.{GFtoPK} have therefore been copied from the
+analogous ones in the \.{WEB} system routines. They have been considerably
+simplified, since \.{GFtoPK} need not deal with the controversial
+ASCII codes less than @'40 or greater than @'176.
+If such codes appear in the \.{GF} file,
+they will be printed as question marks.
+
+@<Types...@>=
+@!ASCII_code=" ".."~"; {a subrange of the integers}
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lower case
+letters. Nowadays, of course, we need to deal with both upper and lower case
+alphabets in a convenient way, especially in a program like \.{GFtoPK}.
+So we shall assume that the \PASCAL\ system being used for \.{GFtoPK}
+has a character set containing at least the standard visible characters
+of ASCII code (|"!"| through |"~"|).
+
+Some \PASCAL\ compilers use the original name |char| for the data type
+associated with the characters in text files, while other \PASCAL s
+consider |char| to be a 64-element subrange of a larger data type that has
+some other name. In order to accommodate this difference, we shall use
+the name |text_char| to stand for the data type of the characters in the
+output file. We shall also assume that |text_char| consists of
+the elements |chr(first_text_char)| through |chr(last_text_char)|,
+inclusive. The following definitions should be adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=127 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+
+@ The \.{GFtoPK} processor converts 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...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [0..255] of text_char;
+ {specifies conversion of output characters}
+
+@ Under our assumption that the visible characters of standard ASCII are
+all present, the following assignment statements initialize the
+|xchr| array properly, without needing any system-dependent changes.
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:='?';
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';
+for i:=@'177 to 255 do xchr[i]:='?';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
+for i:=" " to "~" do xord[xchr[i]]:=i;
+
+@* Generic font file format.
+The most important output produced by a typical run of \MF\ is the
+``generic font'' (\.{GF}) file that specifies the bit patterns of the
+characters that have been drawn. The term {\sl generic\/} indicates that
+this file format doesn't match the conventions of any name-brand manufacturer;
+but it is easy to convert \.{GF} files to the special format required by
+almost all digital phototypesetting equipment. There's a strong analogy
+between the \.{DVI} files written by \TeX\ and the \.{GF} files written
+by \MF; and, in fact, the file formats have a lot in common.
+
+A \.{GF} file is a stream of 8-bit bytes that may be
+regarded as a series of commands in a machine-like language. The first
+byte of each command is the operation code, and this code is followed by
+zero or more bytes that provide parameters to the command. The parameters
+themselves may consist of several consecutive bytes; for example, the
+`|boc|' (beginning of character) command has six parameters, each of
+which is four bytes long. Parameters are usually regarded as nonnegative
+integers; but four-byte-long parameters can be either positive or
+negative, hence they range in value from $-2^{31}$ to $2^{31}-1$.
+As in \.{TFM} files, numbers that occupy
+more than one byte position appear in BigEndian order,
+and negative numbers appear in two's complement notation.
+
+A \.{GF} file consists of a ``preamble,'' followed by a sequence of one or
+more ``characters,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that introduce the file; this must come
+first. Each ``character'' consists of a |boc| command, followed by any
+number of other commands that specify ``black'' pixels,
+followed by an |eoc| command. The characters appear in the order that \MF\
+generated them. If we ignore no-op commands (which are allowed between any
+two commands in the file), each |eoc| command is immediately followed by a
+|boc| command, or by a |post| command; in the latter case, there are no
+more characters in the file, and the remaining bytes form the postamble.
+Further details about the postamble will be explained later.
+
+Some parameters in \.{GF} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first file byte is number~0, then comes number~1, and so on.
+
+@ The \.{GF} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+relative instead of absolute. When a \.{GF}-reading program reads the
+commands for a character, it keeps track of two quantities: (a)~the current
+column number,~|m|; and (b)~the current row number,~|n|. These are 32-bit
+signed integers, although most actual font formats produced from \.{GF}
+files will need to curtail this vast range because of practical
+limitations. (\MF\ output will never allow $\vert m\vert$ or $\vert
+n\vert$ to get extremely large, but the \.{GF} format tries to be more
+general.)
+
+How do \.{GF}'s row and column numbers correspond to the conventions
+of \TeX\ and \MF? Well, the ``reference point'' of a character, in \TeX's
+view, is considered to be at the lower left corner of the pixel in row~0
+and column~0. This point is the intersection of the baseline with the left
+edge of the type; it corresponds to location $(0,0)$ in \MF\ programs.
+Thus the pixel in \.{GF} row~0 and column~0 is \MF's unit square, comprising
+the region of the plane whose coordinates both lie between 0 and~1. The
+pixel in \.{GF} row~|n| and column~|m| consists of the points whose \MF\
+coordinates |(x,y)| satisfy |m<=x<=m+1| and |n<=y<=n+1|. Negative values of
+|m| and~|x| correspond to columns of pixels {\sl left\/} of the reference
+point; negative values of |n| and~|y| correspond to rows of pixels {\sl
+below\/} the baseline.
+
+Besides |m| and |n|, there's also a third aspect of the current
+state, namely the @!|paint_switch|, which is always either \\{black} or
+\\{white}. Each \\{paint} command advances |m| by a specified amount~|d|,
+and blackens the intervening pixels if |paint_switch=black|; then
+the |paint_switch| changes to the opposite state. \.{GF}'s commands are
+designed so that |m| will never decrease within a row, and |n| will never
+increase within a character; hence there is no way to whiten a pixel that
+has been blackened.
+
+@ Here is a list of all the commands that may appear in a \.{GF} file. Each
+command is specified by its symbolic name (e.g., |boc|), its opcode byte
+(e.g., 67), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|d[2]|' means that parameter |d| is two bytes long.
+
+\yskip\hang|paint_0| 0. This is a \\{paint} command with |d=0|; it does
+nothing but change the |paint_switch| from \\{black} to \\{white} or
+vice~versa.
+
+\yskip\hang\\{paint\_1} through \\{paint\_63} (opcodes 1 to 63).
+These are \\{paint} commands with |d=1| to~63, defined as follows: If
+|paint_switch=black|, blacken |d|~pixels of the current row~|n|,
+in columns |m| through |m+d-1| inclusive. Then, in any case,
+complement the |paint_switch| and advance |m| by~|d|.
+
+\yskip\hang|paint1| 64 |d[1]|. This is a \\{paint} command with a specified
+value of~|d|; \MF\ uses it to paint when |64<=d<256|.
+
+\yskip\hang|@!paint2| 65 |d[2]|. Same as |paint1|, but |d|~can be as high
+as~65535.
+
+\yskip\hang|@!paint3| 66 |d[3]|. Same as |paint1|, but |d|~can be as high
+as $2^{24}-1$. \MF\ never needs this command, and it is hard to imagine
+anybody making practical use of it; surely a more compact encoding will be
+desirable when characters can be this large. But the command is there,
+anyway, just in case.
+
+\yskip\hang|boc| 67 |c[4]| |p[4]| |min_m[4]| |max_m[4]| |min_n[4]|
+|max_n[4]|. Beginning of a character: Here |c| is the character code, and
+|p| points to the previous character beginning (if any) for characters having
+this code number modulo 256. (The pointer |p| is |-1| if there was no
+prior character with an equivalent code.) The values of registers |m| and |n|
+defined by the instructions that follow for this character must
+satisfy |min_m<=m<=max_m| and |min_n<=n<=max_n|. (The values of |max_m| and
+|min_n| need not be the tightest bounds possible.) When a \.{GF}-reading
+program sees a |boc|, it can use |min_m|, |max_m|, |min_n|, and |max_n| to
+initialize the bounds of an array. Then it sets |m:=min_m|, |n:=max_n|, and
+|paint_switch:=white|.
+
+\yskip\hang|boc1| 68 |c[1]| |@!del_m[1]| |max_m[1]| |@!del_n[1]| |max_n[1]|.
+Same as |boc|, but |p| is assumed to be~$-1$; also |del_m=max_m-min_m|
+and |del_n=max_n-min_n| are given instead of |min_m| and |min_n|.
+The one-byte parameters must be between 0 and 255, inclusive.
+\ (This abbreviated |boc| saves 19~bytes per character, in common cases.)
+
+\yskip\hang|eoc| 69. End of character: All pixels blackened so far
+constitute the pattern for this character. In particular, a completely
+blank character might have |eoc| immediately following |boc|.
+
+\yskip\hang|skip0| 70. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=white|. \ (This finishes one row and begins another,
+ready to whiten the leftmost pixel in the new row.)
+
+\yskip\hang|skip1| 71 |d[1]|. Decrease |n| by |d+1|, set |m:=min_m|, and set
+|paint_switch:=white|. This is a way to produce |d| all-white rows.
+
+\yskip\hang|@!skip2| 72 |d[2]|. Same as |skip1|, but |d| can be as large
+as 65535.
+
+\yskip\hang|@!skip3| 73 |d[3]|. Same as |skip1|, but |d| can be as large
+as $2^{24}-1$. \MF\ obviously never needs this command.
+
+\yskip\hang|new_row_0| 74. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=black|. \ (This finishes one row and begins another,
+ready to {\sl blacken\/} the leftmost pixel in the new row.)
+
+\yskip\hang|@!new_row_1| through |@!new_row_164| (opcodes 75 to 238). Same as
+|new_row_0|, but with |m:=min_m+1| through |min_m+164|, respectively.
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |no_op| unless special \.{GF}-reading
+programs are being used. \MF\ generates \\{xxx} commands when encountering
+a \&{special} string; this occurs in the \.{GF} file only between
+characters, after the preamble, and before the postamble. However,
+\\{xxx} commands might appear within characters,
+in \.{GF} files generated by other
+processors. It is recommended that |x| be a string having the form of a
+keyword followed by possible parameters relevant to that keyword.
+
+\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+\MF\ uses this when sending a \&{special} string whose length exceeds~255.
+
+\yskip\hang|@!xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be
+ridiculously large; |k| mustn't be negative.
+
+\yskip\hang|yyy| 243 |y[4]|. This command is undefined in general;
+it functions as a 5-byte |no_op| unless special \.{GF}-reading programs
+are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a
+result of \&{numspecial} commands; the intent is to provide numeric
+parameters to \\{xxx} commands that immediately precede.
+
+\yskip\hang|no_op| 244. No operation, do nothing. Any number of |no_op|'s
+may occur between \.{GF} commands, but a |no_op| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|char_loc| 245 |c[1]| |dx[4]| |dy[4]| |w[4]| |p[4]|.
+This command will appear only in the postamble, which will be explained
+shortly.
+
+\yskip\hang|@!char_loc0| 246 |c[1]| |@!dm[1]| |w[4]| |p[4]|.
+Same as |char_loc|, except that |dy| is assumed to be zero, and the value
+of~|dx| is taken to be |65536*dm|, where |0<=dm<256|.
+
+\yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameter |i| is an identifying number for \.{GF} format, currently
+131. The other information is merely commentary; it is not given
+special interpretation like \\{xxx} commands are. (Note that \\{xxx}
+commands may immediately follow the preamble, before the first |boc|.)
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@d gf_id_byte=131 {identifies the kind of \.{GF} files described here}
+
+@ Here are the opcodes that \.{GFtoPK} actually refers to.
+
+@d paint_0=0 {beginning of the \\{paint} commands}
+@d paint1=64 {move right a given number of columns, then
+ black${}\swap{}$white}
+@d boc=67 {beginning of a character}
+@d boc1=68 {abbreviated |boc|}
+@d eoc=69 {end of a character}
+@d skip0=70 {skip no blank rows}
+@d skip1=71 {skip over blank rows}
+@d new_row_0=74 {move down one row and then right}
+@d max_new_row=238 {move down one row and then right}
+@d xxx1=239 {for \&{special} strings}
+@d yyy=243 {for \&{numspecial} numbers}
+@d no_op=244 {no operation}
+@d char_loc=245 {character locators in the postamble}
+@d char_loc0=246 {character locators in the postamble}
+@d pre=247 {preamble}
+@d post=248 {postamble beginning}
+@d post_post=249 {postamble ending}
+@d undefined_commands==250,251,252,253,254,255
+
+@ The last character in a \.{GF} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \MF\ has
+accumulated. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |@!ds[4]| |@!cs[4]| |@!hppp[4]| |@!vppp[4]|
+ |@!min_m[4]| |@!max_m[4]| |@!min_n[4]| |@!max_n[4]|\cr
+ $\langle\,$character locators$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the byte following the final |eoc| in the file
+(or to the byte following the preamble, if there are no characters);
+it can be used to locate the beginning of \\{xxx} commands
+that might have preceded the postamble. The |ds| and |cs| parameters
+@^design size@> @^check sum@>
+give the design size and check sum, respectively, which are exactly the
+values put into the header of any \.{TFM} file that shares information with
+this \.{GF} file. Parameters |hppp| and |vppp| are the ratios of
+pixels per point, horizontally and vertically, expressed as |scaled| integers
+(i.e., multiplied by $2^{16}$); they can be used to correlate the font
+with specific device resolutions, magnifications, and ``at sizes.'' Then
+come |min_m|, |max_m|, |min_n|, and |max_n|, which bound the values that
+registers |m| and~|n| assume in all characters in this \.{GF} file.
+(These bounds need not be the best possible; |max_m| and |min_n| may, on the
+other hand, be tighter than the similar bounds in |boc| commands. For
+example, some character may have |min_n=-100| in its |boc|, but it might
+turn out that |n| never gets lower than |-50| in any character; then
+|min_n| can have any value |<=-50|. If there are no characters in the file,
+it's possible to have |min_m>max_m| and/or |min_n>max_n|.)
+
+@ Character locators are introduced by |char_loc| commands,
+which specify a character residue~|c|, character escapements (|dx,dy|),
+a character width~|w|, and a pointer~|p|
+to the beginning of that character. (If two or more characters have the
+same code~|c| modulo 256, only the last will be indicated; the others can be
+located by following backpointers. Characters whose codes differ by a
+multiple of 256 are assumed to share the same font metric information,
+hence the \.{TFM} file contains only residues of character codes modulo~256.
+This convention is intended for oriental languages, when there are many
+character shapes but few distinct widths.)
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+The character escapements (|dx,dy|) are the values of \MF's \&{chardx}
+and \&{chardy} parameters; they are in units of |scaled| pixels;
+i.e., |dx| is in horizontal pixel units times $2^{16}$, and |dy| is in
+vertical pixel units times $2^{16}$. This is the intended amount of
+displacement after typesetting the character; for \.{DVI} files, |dy|
+should be zero, but other document file formats allow nonzero vertical
+escapement.
+
+The character width~|w| duplicates the information in the \.{TFM} file; it
+is $2^{24}$ times the ratio of the true width to the font's design size.
+
+The backpointer |p| points to the character's |boc|, or to the first of
+a sequence of consecutive \\{xxx} or |yyy| or |no_op| commands that
+immediately precede the |boc|, if such commands exist; such ``special''
+commands essentially belong to the characters, while the special commands
+after the final character belong to the postamble (i.e., to the font
+as a whole). This convention about |p| applies also to the backpointers
+in |boc| commands, even though it wasn't explained in the description
+of~|boc|. @^backpointers@>
+
+Pointer |p| might be |-1| if the character exists in the \.{TFM} file
+but not in the \.{GF} file. This unusual situation can arise in \MF\ output
+if the user had |proofing<0| when the character was being shipped out,
+but then made |proofing>=0| in order to get a \.{GF} file.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the character locators, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~131, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., @'337 in octal). \MF\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{GF} file makes it feasible for
+\.{GF}-reading programs to find the postamble first, on most computers,
+even though \MF\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{GF} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{GF} reader can discover all the information needed for individual
+characters.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{GF}
+format has been designed to work most efficiently with modern operating
+systems. \.{GFtoPK} first reads the postamble, and then scans the file from
+front to back.
+
+@* Packed file format.
+The packed file format is a compact representation of the data contained in a
+\.{GF} file. The information content is the same, but packed (\.{PK}) files
+are almost always less than half the size of their \.{GF} counterparts. They
+are also easier to convert into a raster representation because they do not
+have a profusion of \\{paint}, \\{skip}, and \\{new\_row} commands to be
+separately interpreted. In addition, the \.{PK} format expressly forbids
+\&{special} commands within a character. The minimum bounding box for each
+character is explicit in the format, and does not need to be scanned for as in
+the \.{GF} format. Finally, the width and escapement values are combined with
+the raster information into character ``packets'', making it simpler in many
+cases to process a character.
+
+A \.{PK} file is organized as a stream of 8-bit bytes. At times, these bytes
+might be split into 4-bit nybbles or single bits, or combined into multiple
+byte parameters. When bytes are split into smaller pieces, the `first' piece
+is always the most significant of the byte. For instance, the first bit of
+a byte is the bit with value 128; the first nybble can be found by dividing
+a byte by 16. Similarly, when bytes are combined into multiple byte
+parameters, the first byte is the most significant of the parameter. If the
+parameter is signed, it is represented by two's-complement notation.
+
+The set of possible eight-bit values is separated into two sets, those that
+introduce a character definition, and those that do not. The values that
+introduce a character definition range from 0 to 239; byte values
+above 239 are interpreted as commands. Bytes that introduce character
+definitions are called flag bytes, and various fields within the byte indicate
+various things about how the character definition is encoded. Command bytes
+have zero or more parameters, and can never appear within a character
+definition or between parameters of another command, where they would be
+interpeted as data.
+
+A \.{PK} file consists of a preamble, followed by a sequence of one or more
+character definitions, followed by a postamble. The preamble command must
+be the first byte in the file, followed immediately by its parameters.
+Any number of character definitions may follow, and any command but the
+preamble command and the postamble command may occur between character
+definitions. The very last command in the file must be the postamble.
+
+@ The packed file format is intended to be easy to read and interpret by
+device drivers. The small size of the file reduces the input/output overhead
+each time a font is loaded. For those drivers that load and save each font
+file into memory, the small size also helps reduce the memory requirements.
+The length of each character packet is specified, allowing the character raster
+data to be loaded into memory by simply counting bytes, rather than
+interpreting each command; then, each character can be interpreted on a demand
+basis. This also makes it possible for a driver to skip a particular
+character quickly if it knows that the character is unused.
+
+@ First, the command bytes will be presented; then the format of the
+character definitions will be defined. Eight of the possible sixteen
+commands (values 240 through 255) are currently defined; the others are
+reserved for future extensions. The commands are listed below. Each command
+is specified by its symbolic name (e.g., \\{pk\_no\_op}), its opcode byte,
+and any parameters. The parameters are followed by a bracketed number
+telling how many bytes they occupy, with the number preceded by a plus sign if
+it is a signed quantity. (Four byte quantities are always signed, however.)
+
+\yskip\hang|pk_xxx1| 240 |k[1]| |x[k]|. This command is undefined in general;
+it functions as a $(k+2)$-byte \\{no\_op} unless special \.{PK}-reading
+programs are being used. \MF\ generates \\{xxx} commands when encountering
+a \&{special} string. It is recommended that |x| be a string having the form
+of a keyword followed by possible parameters relevant to that keyword.
+
+\yskip\hang\\{pk\_xxx2} 241 |k[2]| |x[k]|. Like |pk_xxx1|, but |0<=k<65536|.
+
+\yskip\hang\\{pk\_xxx3} 242 |k[3]| |x[k]|. Like |pk_xxx1|, but
+|0<=k<@t$2^{24}$@>|. \MF\ uses this when sending a \&{special} string whose
+length exceeds~255.
+
+\yskip\hang\\{pk\_xxx4} 243 |k[4]| |x[k]|. Like |pk_xxx1|, but |k| can be
+ridiculously large; |k| musn't be negative.
+
+\yskip\hang|pk_yyy| 244 |y[4]|. This command is undefined in general; it
+functions as a five-byte \\{no\_op} unless special \.{PK} reading programs
+are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a result of
+\&{numspecial} commands; the intent is to provide numeric parameters to
+\\{xxx} commands that immediately precede.
+
+\yskip\hang|pk_post| 245. Beginning of the postamble. This command is
+followed by enough |pk_no_op| commands to make the file a multiple
+of four bytes long. Zero through three bytes are usual, but any number
+is allowed.
+This should make the file easy to read on machines that pack four bytes to
+a word.
+
+\yskip\hang|pk_no_op| 246. No operation, do nothing. Any number of
+|pk_no_op|'s may appear between \.{PK} commands, but a |pk_no_op| cannot be
+inserted between a command and its parameters, between two parameters, or
+inside a character definition.
+
+\yskip\hang|pk_pre| 247 |i[1]| |k[1]| |x[k]| |ds[4]| |cs[4]| |hppp[4]|
+|vppp[4]|. Preamble command. Here, |i| is the identification byte of the
+file, currently equal to 89. The string |x| is merely a comment, usually
+indicating the source of the \.{PK} file. The parameters |ds| and |cs| are
+the design size of the file in $1/2^{20}$ points, and the checksum of the
+file, respectively. The checksum should match the \.{TFM} file and the
+\.{GF} files for this font. Parameters |hppp| and |vppp| are the ratios
+of pixels per point, horizontally and vertically, multiplied by $2^{16}$; they
+can be used to correlate the font with specific device resolutions,
+magnifications, and ``at sizes''. Usually, the name of the \.{PK} file is
+formed by concatenating the font name (e.g., cmr10) with the resolution at
+which the font is prepared in pixels per inch multiplied by the magnification
+factor, and the letters \.{pk}. For instance, cmr10 at 300 dots per inch
+should be named \.{cmr10.300pk}; at one thousand dots per inch and magstephalf,
+it should be named \.{cmr10.1095pk}.
+
+@ We put a few of the above opcodes into definitions for symbolic use by
+this program.
+
+@d pk_id = 89 {the version of \.{PK} file described}
+@d pk_xxx1 = 240 {\&{special} commands}
+@d pk_yyy = 244 {\&{numspecial} commands}
+@d pk_post = 245 {postamble}
+@d pk_no_op = 246 {no operation}
+@d pk_pre = 247 {preamble}
+
+@ The \.{PK} format has two conflicting goals: to pack character raster and
+size information as compactly as possible, while retaining ease of translation
+into raster and other forms. A suitable compromise was found in the use of
+run-encoding of the raster information. Instead of packing the individual
+bits of the character, we instead count the number of consecutive `black' or
+`white' pixels in a horizontal raster row, and then encode this number. Run
+counts are found for each row from left to right, traversing rows from the
+top to bottom. This is essentially the way the \.{GF} format works.
+Instead of presenting each row individually, however, we concatenate all
+of the horizontal raster rows into one long string of pixels, and encode this
+row. With knowledge of the width of the bit-map, the original character glyph
+can easily be reconstructed. In addition, we do not need special commands to
+mark the end of one row and the beginning of the next.
+
+Next, we place the burden of finding the minimum bounding box on the part
+of the font generator, since the characters will usually be used much more
+often than they are generated. The minimum bounding box is the smallest
+rectangle that encloses all `black' pixels of a character. We also
+eliminate the need for a special end of character marker, by supplying
+exactly as many bits as are required to fill the minimum bounding box, from
+which the end of the character is implicit.
+
+Let us next consider the distribution of the run counts. Analysis of several
+dozen pixel files at 300 dots per inch yields a distribution peaking at four,
+falling off slowly until ten, then a bit more steeply until twenty, and then
+asymptotically approaching the horizontal. Thus, the great majority of our
+run counts will fit in a four-bit nybble. The eight-bit byte is attractive for
+our run-counts, as it is the standard on many systems; however, the wasted four
+bits in the majority of cases seem a high price to pay. Another possibility
+is to use a Huffman-type encoding scheme with a variable number of bits for
+each run-count; this was rejected because of the overhead in fetching and
+examining individual bits in the file. Thus, the character raster definitions
+in the \.{PK} file format are based on the four-bit nybble.
+
+@ An analysis of typical pixel files yielded another interesting statistic:
+Fully 37\char`\%\
+of the raster rows were duplicates of the previous row. Thus, the \.{PK}
+format allows the specification of repeat counts, which indicate how many times
+a horizontal raster row is to be repeated. These repeated rows are taken out
+of the character glyph before individual rows are concatenated into the long
+string of pixels.
+
+For elegance, we disallow a run count of zero. The case of a null raster
+description should be gleaned from the character width and height being equal
+to zero, and no raster data should be read. No other zero counts are ever
+necessary. Also, in the absence of repeat counts, the repeat value is set to
+be zero (only the original row is sent.) If a repeat count is seen, it takes
+effect on the current row. The current row is defined as the row on which the
+first pixel of the next run count will lie. The repeat count is set back to
+zero when the last pixel in the current row is seen, and the row is sent out.
+
+This poses a problem for entirely black and entirely white rows, however. Let
+us say that the current row ends with four white pixels, and then we have five
+entirely empty rows, followed by a black pixel at the beginning of the next
+row, and the character width is ten pixels. We would like to use a repeat
+count, but there is no legal place to put it. If we put it before the white
+run count, it will apply to the current row. If we put it after, it applies
+to the row with the black pixel at the beginning. Thus, entirely white or
+entirely black repeated rows are always packed as large run counts (in this
+case, a white run count of 54) rather than repeat counts.
+
+@ Now we turn our attention to the actual packing of the run counts and
+repeat counts into nybbles. There are only sixteen possible nybble values.
+We need to indicate run counts and repeat counts. Since the run counts are
+much more common, we will devote the majority of the nybble values to them.
+We therefore indicate a repeat count by a nybble of 14 followed by a packed
+number, where a packed number will be explained later. Since the repeat
+count value of one is so common, we indicate a repeat one command by a single
+nybble of 15. A 14 followed by the packed number 1 is still legal for a
+repeat one count. The run counts are coded directly as packed
+numbers.
+
+For packed numbers, therefore, we have the nybble values 0 through 13. We
+need to represent the positive integers up to, say, $2^{31}-1$. We would
+like the more common smaller numbers to take only one or two nybbles, and
+the infrequent large numbers to take three or more. We could therefore
+allocate one nybble value to indicate a large run count taking three or more
+nybbles. We do this with the value 0.
+
+@ We are left with the values 1 through 13. We can allocate some of these, say
+|dyn_f|, to be one-nybble run counts.
+These will work for the run counts |1..dyn_f|. For subsequent run
+counts, we will use a nybble greater than |dyn_f|, followed by a second nybble,
+whose value can run from 0 through 15. Thus, the two-nybble values will
+run from |dyn_f+1..(13-dyn_f)*16+dyn_f|. We have our definition of large run
+count values now, being all counts greater than |(13-dyn_f)*16+dyn_f|.
+
+We can analyze our several dozen pixel files and determine an optimal value of
+|dyn_f|, and use this value for all of the characters. Unfortunately, values
+of |dyn_f| that pack small characters well tend to pack the large characters
+poorly, and values that pack large characters well are not efficient for the
+smaller characters. Thus, we choose the optimal |dyn_f| on a character basis,
+picking the value that will pack each individual character in the smallest
+number of nybbles. Legal values of |dyn_f| run from 0 (with no one-nybble run
+counts) to 13 (with no two-nybble run counts).
+
+@ Our only remaining task in the coding of packed numbers is the large run
+counts. We use a scheme suggested by D.~E.~Knuth
+@^Knuth, Donald Ervin@>
+that simply and elegantly represents arbitrarily large values. The
+general scheme to represent an integer |i| is to write its hexadecimal
+representation, with leading zeros removed. Then we count the number of
+digits, and prepend one less than that many zeros before the hexadecimal
+representation. Thus, the values from one to fifteen occupy one nybble;
+the values sixteen through 255 occupy three, the values 256 through 4095
+require five, etc.
+
+For our purposes, however, we have already represented the numbers one
+through |(13-dyn_f)*16+dyn_f|. In addition, the one-nybble values have
+already been taken by our other commands, which means that only the values
+from sixteen up are available to us for long run counts. Thus, we simply
+normalize our long run counts, by subtracting |(13-dyn_f)*16+dyn_f+1| and
+adding 16, and then we represent the result according to the scheme above.
+
+@ The final algorithm for decoding the run counts based on the above scheme
+might look like this, assuming that a procedure called \\{get\_nyb} is
+available to get the next nybble from the file, and assuming that the global
+|repeat_count| indicates whether a row needs to be repeated. Note that this
+routine is recursive, but since a repeat count can never directly follow
+another repeat count, it can only be recursive to one level.
+
+@p@{ function pk_packed_num : integer ;
+var i,@!j : integer ;
+begin
+ i := get_nyb ;
+ if i = 0 then begin
+ repeat j := get_nyb ; incr(i) ; until j <> 0 ;
+ while i > 0 do begin j := j * 16 + get_nyb ; decr(i) ; end ;
+ pk_packed_num := j - 15 + (13-dyn_f)*16 + dyn_f ;
+ end else if i <= dyn_f then
+ pk_packed_num := i
+ else if i < 14 then
+ pk_packed_num := (i-dyn_f-1)*16+get_nyb+dyn_f+1
+ else begin
+ if i = 14 then
+ repeat_count := pk_packed_num
+ else
+ repeat_count := 1 ;
+ pk_packed_num := pk_packed_num ;
+ end ;
+end ; @}
+
+@ For low resolution fonts, or characters with `gray' areas, run encoding can
+often make the character many times larger. Therefore, for those characters
+that cannot be encoded efficiently with run counts, the \.{PK} format allows
+bit-mapping of the characters. This is indicated by a |dyn_f| value of
+14. The bits are packed tightly, by concatenating all of the horizontal raster
+rows into one long string, and then packing this string eight bits to a byte.
+The number of bytes required can be calculated by |(width*height+7) div 8|.
+This format should only be used when packing the character by run counts takes
+more bytes than this, although, of course, it is legal for any character.
+Any extra bits in the last byte should be set to zero.
+
+@ At this point, we are ready to introduce the format for a character
+descriptor. It consists of three parts: a flag byte, a character preamble,
+and the raster data. The most significant four bits of the flag byte
+yield the |dyn_f| value for that character. (Notice that only values of
+0 through 14 are legal for |dyn_f|, with 14 indicating a bit mapped character;
+thus, the flag bytes do not conflict with the command bytes, whose upper nybble
+is always 15.) The next bit (with weight 8) indicates whether the first run
+count is a black count or a white count, with a one indicating a black count.
+For bit-mapped characters, this bit should be set to a zero. The next bit
+(with weight 4) indicates whether certain later parameters (referred to as size
+parameters) are given in one-byte or two-byte quantities, with a one indicating
+that they are in two-byte quantities. The last two bits are concatenated on to
+the beginning of the packet-length parameter in the character preamble,
+which will be explained below.
+
+However, if the last three bits of the flag byte are all set (normally
+indicating that the size parameters are two-byte values and that a 3 should be
+prepended to the length parameter), then a long format of the character
+preamble should be used instead of one of the short forms.
+
+Therefore, there are three formats for the character preamble; the one that
+is used depends on the least significant three bits of the flag byte. If the
+least significant three bits are in the range zero through three, the short
+format is used. If they are in the range four through six, the extended short
+format is used. Otherwise, if the least significant bits are all set, then
+the long form of the character preamble is used. The preamble formats are
+explained below.
+
+\yskip\hang Short form: |flag[1]| |pl[1]| |cc[1]| |tfm[3]| |dm[1]| |w[1]|
+|h[1]| |hoff[+1]| |voff[+1]|.
+If this format of the character preamble is used, the above
+parameters must all fit in the indicated number of bytes, signed or unsigned
+as indicated. Almost all of the standard \TeX\ font characters fit; the few
+exceptions are fonts such as \.{cminch}.
+
+\yskip\hang Extended short form: |flag[1]| |pl[2]| |cc[1]| |tfm[3]| |dm[2]|
+|w[2]| |h[2]| |hoff[+2]| |voff[+2]|. Larger characters use this extended
+format.
+
+\yskip\hang Long form: |flag[1]| |pl[4]| |cc[4]| |tfm[4]| |dx[4]| |dy[4]|
+|w[4]| |h[4]| |hoff[4]| |voff[4]|. This is the general format that
+allows all of the
+parameters of the \.{GF} file format, including vertical escapement.
+\vskip\baselineskip
+The |flag| parameter is the flag byte. The parameter |pl| (packet length)
+contains the offset
+of the byte following this character descriptor, with respect to the beginning
+of the |tfm| width parameter. This is given so a \.{PK} reading program can,
+once it has read the flag byte, packet length, and character code (|cc|), skip
+over the character by simply reading this many more bytes. For the two short
+forms of the character preamble, the last two bits of the flag byte should be
+considered the two most-significant bits of the packet length. For the short
+format, the true packet length might be calculated as |(flag mod 4)*256+pl|;
+for the short extended format, it might be calculated as
+|(flag mod 4)*65536+pl|.
+
+The |w| parameter is the width and the |h| parameter is the height in pixels
+of the minimum bounding box. The |dx| and |dy| parameters are the horizontal
+and vertical escapements, respectively. In the short formats, |dy| is assumed
+to be zero and |dm| is |dx| but in pixels;
+in the long format, |dx| and |dy| are both
+in pixels multiplied by $2^{16}$. The |hoff| is the horizontal offset from the
+upper left pixel to the reference pixel; the |voff| is the vertical offset.
+They are both given in pixels, with right and down being positive. The
+reference pixel is the pixel that occupies the unit square in \MF; the
+\MF\ reference point is the lower left hand corner of this pixel. (See the
+example below.)
+
+@ \TeX\ requires all characters that have the same character codes
+modulo 256 to have also the same |tfm| widths and escapement values. The \.{PK}
+format does not itself make this a requirement, but in order for the font to
+work correctly with the \TeX\ software, this constraint should be observed.
+(The standard version of \TeX\ cannot output character codes greater
+than 255, but extended versions do exist.)
+
+Following the character preamble is the raster information for the
+character, packed by run counts or by bits, as indicated by the flag byte.
+If the character is packed by run counts and the required number of nybbles
+is odd, then the last byte of the raster description should have a zero
+for its least significant nybble.
+
+@ As an illustration of the \.{PK} format, the character \char4\ from the font
+amr10 at 300 dots per inch will be encoded. This character was chosen
+because it illustrates some
+of the borderline cases. The raster for the character looks like this (the
+row numbers are chosen for convenience, and are not \MF's row numbers.)
+
+\vskip\baselineskip
+{\def\smbox{\vrule height 7pt width 7pt depth 0pt \hskip 3pt}%
+\catcode`\*=\active \let*=\smbox
+\centerline{\vbox{\baselineskip=10pt
+\halign{\hfil#\quad&&\hfil#\hfil\cr
+0& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+1& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+2& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+3& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+4& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+5& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+6& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+7\cr
+8\cr
+9& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+10& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+11& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+12& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+13& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+14& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+15& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+16& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+17& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+18& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+19\cr
+20\cr
+21\cr
+22& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+23& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+24& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+25& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+26& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+27& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+28&+& &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+&\hphantom{*}&\hphantom{*}\cr
+}}}}
+The width of the minimum bounding box for this character is 20; its height
+is 29. The `+' represents the reference pixel; notice how it lies outside the
+minimum bounding box. The |hoff| value is $-2$, and the |voff| is~28.
+
+The first task is to calculate the run counts and repeat counts. The repeat
+counts are placed at the first transition (black to white or white to black)
+in a row, and are enclosed in brackets. White counts are enclosed in
+parentheses. It is relatively easy to generate the counts list:
+\vskip\baselineskip
+\centerline{82 [2] (16) 2 (42) [2] 2 (12) 2 (4) [3]}
+\centerline{16 (4) [2] 2 (12) 2 (62) [2] 2 (16) 82}
+\vskip\baselineskip
+Note that any duplicated rows that are not all white or all black are removed
+before the run counts are calculated. The rows thus removed are rows 5, 6,
+10, 11, 13, 14, 15, 17, 18, 23, and 24.
+
+@ The next step in the encoding of this character is to calculate the optimal
+value of |dyn_f|. The details of how this calculation is done are not
+important here; suffice it to say that there is a simple algorithm that can
+determine the best value of |dyn_f| in one pass over the count list. For this
+character, the optimal value turns out to be 8 (atypically low). Thus, all
+count values less than or equal to 8 are packed in one nybble; those from
+nine to $(13-8)*16+8$ or 88 are packed in two nybbles. The run encoded values
+now become (in hex, separated according to the above list):
+\vskip\baselineskip
+\centerline{\tt D9 E2 97 2 B1 E2 2 93 2 4 E3}
+\centerline{\tt 97 4 E2 2 93 2 C5 E2 2 97 D9}
+\vskip\baselineskip\noindent
+which comes to 36 nybbles, or 18 bytes. This is shorter than the 73 bytes
+required for the bit map, so we use the run count packing.
+
+@ The short form of the character preamble is used because all of the
+parameters fit in their respective lengths. The packet length is therefore
+18 bytes for the raster, plus
+eight bytes for the character preamble parameters following the character
+code, or 26. The |tfm| width for this character is 640796, or {\tt 9C71C} in
+hexadecimal. The horizontal escapement is 25 pixels. The flag byte is
+88 hex, indicating the short preamble, the black first count, and the
+|dyn_f| value of 8. The final total character packet, in hexadecimal, is:
+\vskip\baselineskip
+$$\vbox{\halign{\hfil #\quad&&{\tt #\ }\cr
+Flag byte&88\cr
+Packet length&1A\cr
+Character code&04\cr
+|tfm| width&09&C7&1C\cr
+Horizontal escapement (pixels)&19\cr
+Width of bit map&14\cr
+Height of bit map&1D\cr
+Horizontal offset (signed)&FE\cr
+Vertical offset&1C\cr
+Raster data&D9&E2&97\cr
+&2B&1E&22\cr
+&93&24&E3\cr
+&97&4E&22\cr
+&93&2C&5E\cr
+&22&97&D9\cr}}$$
+
+@* Input and output for binary files.
+We have seen that a \.{GF} file is a sequence of 8-bit bytes. The bytes
+appear physically in what is called a `|packed file of 0..255|'
+in \PASCAL\ lingo. The \.{PK} file is also a sequence of 8-bit bytes.
+
+Packing is system dependent, and many \PASCAL\ systems fail to implement
+such files in a sensible way (at least, from the viewpoint of producing
+good production software). For example, some systems treat all
+byte-oriented files as text, looking for end-of-line marks and such
+things. Therefore some system-dependent code is often needed to deal with
+binary files, even though most of the program in this section of
+\.{GFtoPK} is written in standard \PASCAL.
+@^system dependencies@>
+
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+
+@ The program deals with two binary file variables: |gf_file| is the
+input file that we are translating into \.{PK} format, to be written
+on |pk_file|.
+
+@<Glob...@>=
+@!gf_file:byte_file; {the stuff we are \.{GFtoPK}ing}
+@!pk_file:byte_file; {the stuff we have \.{GFtoPK}ed}
+
+@ To prepare the |gf_file| for input, we |reset| it.
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin reset(gf_file);
+gf_loc := 0 ;
+end;
+
+@ To prepare the |pk_file| for output, we |rewrite| it.
+
+@p procedure open_pk_file; {prepares to write packed bytes in |pk_file|}
+begin rewrite(pk_file);
+pk_loc := 0 ; pk_open := true ;
+end;
+
+@ The variable |pk_loc| contains the number of the byte about to
+be written to the |pk_file|, and |gf_loc| is the byte about to be read
+from the |gf_file|. Also, |pk_open| indicates that the packed file has
+been opened and is ready for output.
+
+@<Glob...@>=
+@!pk_loc:integer; {where we are about to write, in |pk_file|}
+@!gf_loc:integer; {where are we in the |gf_file|}
+@!pk_open:boolean; {is the packed file open?}
+
+@ We do not open the |pk_file| until after the postamble of the |gf_file|
+has been read. This can be used, for instance, to calculate a resolution
+to put in the suffix of the |pk_file| name. This also means, however, that
+specials in the postamble (which \MF\ never generates) do not get sent to
+the |pk_file|.
+
+@<Set init...@>=
+pk_open := false ;
+
+@ We shall use two simple functions to read the next byte or
+bytes from |gf_file|. We either need to get an individual byte or a
+set of four bytes.
+@^system dependencies@>
+
+@p function gf_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then bad_gf('Unexpected end of file!')
+@.Unexpected end of file@>
+else begin read(gf_file,b); gf_byte:=b;
+ end;
+incr(gf_loc);
+end;
+@#
+function gf_signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+if a<128 then gf_signed_quad:=((a*256+b)*256+c)*256+d
+else gf_signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+gf_loc := gf_loc + 4 ;
+end;
+
+@ We also need a few routines to write data to the \.{PK} file. We write
+data in 4-, 8-, 16-, 24-, and 32-bit chunks, so we define the appropriate
+routines. We must be careful not to let the sign bit mess us up, as some
+\PASCAL s implement division of a negative integer differently.
+
+@p procedure pk_byte(a:integer) ;
+begin
+ if pk_open then begin
+ if a < 0 then a := a + 256 ;
+ write(pk_file, a) ;
+ incr(pk_loc) ;
+ end ;
+end ;
+@#
+procedure pk_halfword(a:integer) ;
+begin
+ if a < 0 then a := a + 65536 ;
+ write(pk_file, a div 256) ;
+ write(pk_file, a mod 256) ;
+ pk_loc := pk_loc + 2 ;
+end ;
+@#
+procedure pk_three_bytes(a:integer);
+begin
+ write(pk_file, a div 65536 mod 256) ;
+ write(pk_file, a div 256 mod 256) ;
+ write(pk_file, a mod 256) ;
+ pk_loc := pk_loc + 3 ;
+end ;
+@#
+procedure pk_word(a:integer) ;
+var b : integer ;
+begin
+ if pk_open then begin
+ if a < 0 then begin
+ a := a + @'10000000000 ;
+ a := a + @'10000000000 ;
+ b := 128 + a div 16777216 ;
+ end else b := a div 16777216 ;
+ write(pk_file, b) ;
+ write(pk_file, a div 65536 mod 256) ;
+ write(pk_file, a div 256 mod 256) ;
+ write(pk_file, a mod 256) ;
+ pk_loc := pk_loc + 4 ;
+ end ;
+end ;
+@#
+procedure pk_nyb(a:integer) ;
+begin
+ if bit_weight = 16 then begin
+ output_byte := a * 16 ;
+ bit_weight := 1 ;
+ end else begin
+ pk_byte(output_byte + a) ;
+ bit_weight := 16 ;
+ end ;
+end ;
+
+@ We need the globals |bit_weight| and |output_byte| for buffering.
+
+@<Glob...@>=
+@!bit_weight : integer ; {output bit weight}
+@!output_byte : integer ; {output byte for pk file}
+
+@ Finally we come to the routines that are used for random access of the
+|gf_file|. To correctly find and read the postamble of the file, we need
+two routines, one to find the length of the |gf_file|, and one to position
+the |gf_file|. We assume that the first byte of the file is numbered zero.
+
+Such routines are, of course, highly system dependent. They are implemented
+here in terms of two assumed system routines called |set_pos| and |cur_pos|.
+The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is negative
+or larger than the total number of items in |f|; in the latter case,
+|set_pos(f,n)| moves to the end of file |f|. The call |cur_pos(f)| gives the
+total number of items in |f|, if |eof(f)| is true; we use |cur_pos| only in
+such a situation.
+@^system dependencies@>
+
+@p procedure find_gf_length ;
+begin
+ set_pos(gf_file, -1) ; gf_len := cur_pos(gf_file) ;
+end ;
+@#
+procedure move_to_byte(@!n : integer) ;
+begin
+ set_pos(gf_file, n); gf_loc := n ;
+end ;
+
+@ The global |gf_len| contains the final total length of the |gf_file|.
+
+@<Glob...@>=
+@!gf_len : integer ; {length of |gf_file|}
+
+@* Plan of attack.
+It would seem at first that converting a \.{GF} file to \.{PK} format should
+be relatively easy, since they both use a form of run-encoding. Unfortunately,
+several idiosyncracies of the \.{GF} format make this conversion slightly
+cumbersome.
+The \.{GF} format separates the raster information from the escapement values
+and \.{TFM} widths; the \.{PK} format combines all information about a single
+character into one character packet. The \.{GF} run-encoding is
+on a row-by-row basis, and the \.{PK} format is on a glyph basis, as if all
+of the raster rows in the glyph were concatenated into one long row. The
+encoding of the run-counts in the \.{GF} files is fixed, whereas the \.{PK}
+format uses a dynamic encoding scheme that must be adjusted for each
+character. And,
+finally, any repeated rows can be marked and sent with a single command in
+the \.{PK} format.
+
+There are four major steps in the conversion process. First, the postamble
+of the |gf_file| is found and read, and the data from the character locators
+is stored in memory. Next, the preamble of the |pk_file| is written. The
+third and by far
+the most difficult step reads the raster representation of all of the
+characters from the \.{GF} file, packs them, and writes them to the |pk_file|.
+Finally, the postamble is written to the |pk_file|.
+
+The conversion of the character raster information from the |gf_file| to the
+format required by the |pk_file| takes several smaller steps.
+The \.{GF} file is read, the commands are interpreted, and the run
+counts are stored in the working |row| array. Each row is terminated by a
+|end_of_row| value, and the character glyph is terminated by an
+|end_of_char| value. Then, this representation of the character glyph
+is scanned to determine the minimum bounding box in which it will fit,
+correcting the |min_m|, |max_m|, |min_n|, and |max_n| values, and calculating
+the offset values. The third sub-step is to restructure the row list from
+a list based on rows to a list based on the entire glyph. Then, an optimal
+value of |dyn_f| is calculated, and the final
+size of the counts is found for the \.{PK} file format, and compared with
+the bit-wise packed glyph. If the run-encoding scheme is shorter, the
+character is written to the |pk_file| as row counts; otherwise, it is written
+using a bit-packed scheme.
+
+To save various information while the \.{GF} file is being loaded, we need
+several arrays. The |tfm_width|, |dx|, and |dy| arrays store the obvious
+values. The |status| array contains
+the current status of the particular character. A value of 0 indicates
+that the character has never been defined; a 1 indicates that the character
+locator for that character was read in; and a 2 indicates that the raster
+information for at least
+one character was read from the |gf_file| and written to the |pk_file|.
+The |row| array contains row counts. It is filled anew
+for each character, and is used as a general workspace. The \.{GF} counts are
+stored starting at location 2 in this array, so that the \.{PK} counts can be
+written to the same array, overwriting the \.{GF} counts, without destroying
+any counts before they are used. (A possible repeat count in the first row
+might make the first row of the \.{PK} file one count longer; all succeeding
+rows are guaranteed to be the same length or shorter because of the
+|end_of_row| flags in the \.{GF} format that are unnecessary in the \.{PK}
+format.)
+
+@d virgin==0 {never heard of this character yet}
+@d located==1 {locators read for this character}
+@d sent==2 {at least one of these characters has been sent}
+
+@<Glob...@>=
+@!tfm_width: array[0..255] of integer; {the \.{TFM} widths of characters}
+@!dx, @!dy: array[0..255] of integer; {the horizontal and vertical escapements}
+@!status: array[0..255] of virgin..sent; {character status}
+@!row: array[0..max_row] of integer; {the row counts for working}
+
+@ Here we initialize all of the character |status| values to |virgin|.
+
+@<Set init...@>=
+for i := 0 to 255 do
+ status[i] := virgin ;
+
+@ And, finally, we need to define the |end_of_row| and |end_of_char| values.
+These cannot be values that can be taken on either by legitimate run counts,
+even when wrapping around an entire character. Nor can they be values that
+repeat counts can take on. Since repeat counts can be arbitrarily large, we
+restrict ourselves to negative values whose absolute values are greater than
+the largest possible repeat count.
+
+@d end_of_row==(-99999) {indicates the end of a row}
+@d end_of_char==(-99998) {indicates the end of a character}
+
+@* Reading the generic font file.
+There are two major procedures in this program that do all of the work.
+The first is |convert_gf_file|, which interprets the \.{GF} commands and
+puts row counts into the |row| array. The second, which we only
+anticipate at the moment, actually packs the row counts into nybbles and
+writes them to the packed file.
+
+@p @<Packing procedures@> ;
+procedure convert_gf_file;
+var
+ @!i, @!j, @!k : integer ; {general purpose indices}
+ @!gf_com : integer ; {current gf command}
+ @<Locals to |convert_gf_file|@>
+begin
+ open_gf_file ;
+ if gf_byte <> pre then bad_gf('First byte is not preamble');
+@.First byte is not preamble@>
+ if gf_byte <> gf_id_byte then
+ bad_gf('Identification byte is incorrect');
+@.Identification byte incorrect@>
+ @<Find and interpret postamble@> ;
+ move_to_byte(2) ;
+ open_pk_file ;
+ @<Write preamble@> ;
+ repeat
+ gf_com := gf_byte ;
+ case gf_com of
+ boc, boc1 : @<Interpret character@> ;
+ @<Specials and |no_op| cases@> ;
+ post : ; {we will actually do the work for this one later}
+ othercases bad_gf('Unexpected ',gf_com:1,' command between characters')
+@.Unexpected command@>
+ endcases ;
+ until gf_com = post ;
+ @<Write postamble@> ;
+end ;
+
+@ We need a few easy macros to expand some case statements:
+
+@d four_cases(#)==#,#+1,#+2,#+3
+@d sixteen_cases(#)==four_cases(#),four_cases(#+4),four_cases(#+8),
+ four_cases(#+12)
+@d sixty_four_cases(#)==sixteen_cases(#),sixteen_cases(#+16),
+ sixteen_cases(#+32),sixteen_cases(#+48)
+@d one_sixty_five_cases(#)==sixty_four_cases(#),sixty_four_cases(#+64),
+ sixteen_cases(#+128),sixteen_cases(#+144),four_cases(#+160),#+164
+
+@ In this program, all special commands are passed unchanged and any |no_op|
+bytes are ignored, so we write some code to handle these:
+
+@<Specials and |no_op| cases@>=
+four_cases(xxx1) : begin
+ pk_byte(gf_com - xxx1 + pk_xxx1) ;
+ i := 0 ; for j := 0 to gf_com - xxx1 do begin
+ k := gf_byte ; pk_byte(k) ; i := i * 256 + k ;
+ end ;
+ for j := 1 to i do pk_byte(gf_byte) ; end ;
+yyy : begin pk_byte(pk_yyy) ; pk_word(gf_signed_quad) ; end ;
+no_op :
+
+@ Now we need the routine that handles the character commands. Again,
+only a subset of the gf commands are permissible inside character
+definitions, so we only look for these.
+
+@<Interpret character@>=
+begin
+ if gf_com = boc then begin
+ gf_ch := gf_signed_quad ;
+ i := gf_signed_quad ; {dispose of back pointer}
+ min_m := gf_signed_quad ;
+ max_m := gf_signed_quad ;
+ min_n := gf_signed_quad ;
+ max_n := gf_signed_quad ;
+ end else begin
+ gf_ch := gf_byte ;
+ i := gf_byte ;
+ max_m := gf_byte ;
+ min_m := max_m - i ;
+ i := gf_byte ;
+ max_n := gf_byte ;
+ min_n := max_n - i ;
+ end ;
+ d_print_ln('Character ',gf_ch:1) ;
+ if gf_ch>=0 then gf_ch_mod_256 := gf_ch mod 256
+ else gf_ch_mod_256 := 255-((-(1+gf_ch)) mod 256);
+ if status[gf_ch_mod_256] = virgin then
+ bad_gf('no character locator for character ',gf_ch:1) ;
+@.no character locator...@>
+ @<Convert character to packed form@> ;
+end
+
+@ Communication between the procedures |convert_gf_file| and
+|pack_and_send_character| is done with a few global variables.
+
+@<Glob...@>=
+@!gf_ch : integer ; {the character we are working with}
+@!gf_ch_mod_256 : integer ; {locater pointer}
+@!pred_pk_loc : integer ; {where we predict the end of the character to be.}
+@!max_n, @!min_n : integer ; {the maximum and minimum horizontal rows}
+@!max_m, @!min_m : integer ; {the maximum and minimum vertical rows}
+@!row_ptr : integer ; {where we are in the |row| array.}
+
+@ Now we are at the beginning of a character that we need the raster for.
+Before we get into the complexities of decoding the |paint|, |skip|, and
+|new_row| commands, let's define a macro that will help us fill up the
+|row| array. Note that we check that |row_ptr| never exceeds |max_row|;
+Instead of
+calling |bad_gf| directly, as this macro is repeated eight times, we simply
+set the |bad| flag true.
+
+@d put_in_rows(#)==begin if row_ptr > max_row then bad := true else begin
+row[row_ptr]:=#; incr(row_ptr); end ; end
+
+@ Now we have the procedure that decodes the various commands and puts counts
+into the |row| array. This would be a trivial procedure, except for
+the |paint_0| command. Because the |paint_0| command exists, it is possible
+to have a sequence like |paint| 42, |paint_0|, |paint| 38, |paint_0|,
+|paint_0|, |paint_0|, |paint| 33, |skip_0|. This would be an entirely empty
+row, but if we left the zeros in the |row| array, it would be difficult
+to recognize the row as empty.
+
+This type of situation probably would never
+occur in practice, but it is defined by the \.{GF} format, so we must be able
+to handle it. The extra code is really quite simple, just difficult to
+understand; and it does not cut down the speed appreciably. Our goal is
+this: to collapse sequences like |paint| 42, |paint_0|, |paint| 32 to a single
+count of 74, and to insure that the last count of a row is a black count rather
+than a white count. A buffer variable |extra|, and two state flags, |on| and
+|state|, enable us to accomplish this.
+
+The |on| variable is essentially the |paint_switch| described in the \.{GF}
+description. If it is true, then we are currently painting black pixels.
+The |extra| variable holds a count that is about to be placed into the
+|row| array. We hold it in this array until we get a |paint| command
+of the opposite color that is greater than 0. If we get a |paint_0| command,
+then the |state| flag is turned on, indicating that the next count we receive
+can be added to the |extra| variable as it is the same color.
+
+@<Convert character to packed form@>=
+begin
+ bad := false ;
+ row_ptr := 2 ;
+ on := false ;
+ extra := 0 ;
+ state := true ;
+ repeat
+ gf_com := gf_byte ;
+ case gf_com of
+@t\4@>@<Cases for |paint| commands@>;
+four_cases(skip0) : begin
+ i := 0 ; for j := 1 to gf_com - skip0 do i := i * 256 + gf_byte ;
+ if on = state then put_in_rows(extra) ;
+ for j := 0 to i do put_in_rows(end_of_row) ;
+ on := false ; extra := 0 ; state := true ;
+end ;
+one_sixty_five_cases(new_row_0) : begin
+ if on = state then put_in_rows(extra) ;
+ put_in_rows(end_of_row) ;
+ on := true ; extra := gf_com - new_row_0 ; state := false ;
+end ;
+@t\4@>@<Specials and |no_op| cases@> ;
+eoc : begin
+ if on = state then put_in_rows(extra) ;
+ if ( row_ptr > 2 ) and ( row[row_ptr - 1] <> end_of_row) then
+ put_in_rows(end_of_row) ;
+ put_in_rows(end_of_char) ;
+ if bad then abort('Ran out of internal memory for row counts!') ;
+@.Ran out of memory@>
+ pack_and_send_character ;
+ status[gf_ch_mod_256] := sent ;
+ if pk_loc <> pred_pk_loc then
+ abort('Internal error while writing character!') ;
+@.Internal error@>
+end ;
+othercases bad_gf('Unexpected ',gf_com:1,' command in character definition')
+@.Unexpected command@>
+ endcases ;
+ until gf_com = eoc ;
+end
+
+@ A few more locals used above and below:
+
+@<Locals to |convert_gf_file|@>=
+@!on : boolean ; {indicates whether we are white or black}
+@!state : boolean ; {a state variable---is the next count the same race as
+ the one in the |extra| buffer?}
+@!extra : integer ; {where we pool our counts}
+@!bad : boolean ; {did we run out of space?}
+
+@ @<Cases for |paint| commands@>=
+paint_0 : begin
+ state := not state ;
+ on := not on ;
+end ;
+sixty_four_cases(paint_0+1),paint1+1,paint1+2 : begin
+ if gf_com < paint1 then i := gf_com - paint_0
+ else begin
+ i := 0 ; for j := 0 to gf_com - paint1 do i := i * 256 + gf_byte ;
+ end ;
+ if state then begin
+ extra := extra + i ;
+ state := false ;
+ end else begin
+ put_in_rows(extra) ;
+ extra := i ;
+ end ;
+ on := not on ;
+end
+
+@ Our last remaining task is to interpret the postamble commands. The only
+things that may appear in the postamble are |post_post|, |char_loc|,
+|char_loc0|, and the special commands.
+Note that any special commands that might appear in the postamble are
+not written to the |pk_file|. Since \MF\ does not generate special commands
+in the postamble, this should not be a major difficulty.
+
+@<Find and interpret postamble@>=
+find_gf_length ;
+if gf_len<8 then bad_gf('only ',gf_len:1,' bytes long');
+@.only n bytes long@>
+post_loc := gf_len - 4 ;
+repeat
+ if post_loc = 0 then bad_gf('all 223''s');
+@.all 223\char39s@>
+ move_to_byte(post_loc); k := gf_byte; decr(post_loc) ;
+until k <> 223 ;
+if k <> gf_id_byte then bad_gf('ID byte is ',k:1);
+@.ID byte is wrong@>
+if post_loc<5 then bad_gf('post location is ',post_loc:1) ;
+@.post location is@>
+move_to_byte(post_loc - 3);
+q := gf_signed_quad ;
+if (q<0) or (q>post_loc-3) then bad_gf('post pointer is ',q:1) ;
+@.post pointer is wrong@>
+move_to_byte(q) ; k := gf_byte ;
+if k <> post then bad_gf('byte at ',q:1,' is not post') ;
+@.byte is not post@>
+i := gf_signed_quad ; {skip over junk}
+design_size := gf_signed_quad ;
+check_sum := gf_signed_quad ;
+hppp := gf_signed_quad ;
+h_mag := round ( hppp * 72.27 / 65536 ) ;
+vppp := gf_signed_quad ;
+if hppp <> vppp then print_ln('Odd aspect ratio!') ;
+@.Odd aspect ratio@>
+i := gf_signed_quad ; i := gf_signed_quad ; {skip over junk}
+i := gf_signed_quad ; i := gf_signed_quad ;
+repeat
+ gf_com := gf_byte ;
+ case gf_com of
+char_loc, char_loc0 : begin
+ gf_ch := gf_byte ;
+ if status[gf_ch] <> virgin then
+ bad_gf('Locator for this character already found.');
+@.Locator...already found@>
+ if gf_com = char_loc then begin
+ dx[gf_ch] := gf_signed_quad ;
+ dy[gf_ch] := gf_signed_quad ;
+ end else begin
+ dx[gf_ch] := gf_byte * 65536 ;
+ dy[gf_ch] := 0 ;
+ end ;
+ tfm_width[gf_ch] := gf_signed_quad ;
+ i := gf_signed_quad ;
+ status[gf_ch] := located ;
+end ;
+@<Specials and |no_op| cases@> ;
+post_post : ;
+othercases bad_gf('Unexpected ',gf_com:1,' in postamble')
+@.Unexpected command@>
+ endcases ;
+until gf_com = post_post
+
+@ Just a few more locals:
+
+@<Locals to |convert_gf_file|@>=
+@!hppp, @!vppp : integer ; {horizontal and vertical pixels per point}
+@!q : integer ; {quad temporary}
+@!post_loc : integer ; {where the postamble was}
+
+@* Converting the counts to packed format.
+This procedure is passed the set of row counts from the \.{GF} file. It
+writes the character to the \.{PK} file. First, the minimum bounding box
+is determined. Next, the row-oriented count list is converted to a count
+list based on the entire glyph. Finally, we calculate
+the optimal |dyn_f| and send the character.
+
+@<Packing procedures@>=
+procedure pack_and_send_character ;
+var i, @!j, @!k : integer ; {general indices}
+@<Locals to |pack_and_send_character|@>
+begin
+ @<Scan for bounding box@> ;
+ @<Convert row-list to glyph-list@> ;
+ @<Calculate |dyn_f| and packed size and write character@> ;
+end
+
+@ Now we have the row counts in our |row| array. To find the real |max_n|,
+we look for
+the first non-|end_of_row| value in the |row|. If it is an |end_of_char|,
+the entire character is blank. Otherwise, we first eliminate all of the blank
+rows at the end of the character. Next, for each remaining row, we check the
+first white count for a new |min_m|, and the total length of the row
+for a new |max_m|.
+
+@<Scan for bounding box@>=
+i := 2 ; decr(row_ptr) ;
+while row[i] = end_of_row do incr(i) ;
+if row[i] <> end_of_char then begin
+ max_n := max_n - i + 2 ;
+ while row[row_ptr - 2] = end_of_row do begin
+ decr(row_ptr) ; row[row_ptr] := end_of_char ;
+ end ;
+ min_n := max_n + 1 ;
+ extra := max_m - min_m + 1 ;
+ max_m := 0 ;
+ j := i ;
+ while row[j] <> end_of_char do begin
+ decr(min_n) ;
+ if row[j] <> end_of_row then begin
+ k := row[j] ;
+ if k < extra then extra := k ;
+ incr(j) ;
+ while row[j] <> end_of_row do begin
+ k := k + row[j] ; incr(j) ;
+ end ;
+ if max_m < k then max_m := k ;
+ end ;
+ incr(j) ;
+ end ;
+ min_m := min_m + extra ;
+ max_m := min_m + max_m - 1 - extra ;
+ height := max_n - min_n + 1 ;
+ width := max_m - min_m + 1 ;
+ x_offset := - min_m ;
+ y_offset := max_n ;
+ d_print_ln('W ',width:1,' H ',height:1,' X ',x_offset:1, ' Y ',y_offset:1);
+end else begin
+ height := 0 ; width := 0 ; x_offset := 0 ; y_offset := 0 ;
+ d_print_ln('Empty raster.');
+end
+
+@ We must convert the run-count array from a row orientation to a glyph
+orientation, with repeat counts for repeated rows. We seperate this task
+into two smaller tasks, on a per row basis. But first, we define a new
+macro to help us fill up this new array. Here, we have no fear that we will
+run out of space, as the glyph representation is provably smaller than the
+rows representation.
+
+@d put_count(#)==begin row[put_ptr] := #; incr(put_ptr);
+if repeat_flag > 0 then begin
+ row[put_ptr] := - repeat_flag ; repeat_flag := 0 ; incr(put_ptr) ; end ;
+end
+
+@<Convert row-list to glyph-list@>=
+put_ptr := 0 ; row_ptr := 2 ; repeat_flag := 0 ;
+state := true ; buff := 0 ;
+while row[row_ptr] = end_of_row do incr(row_ptr) ;
+while row[row_ptr] <> end_of_char do begin
+ @<Skip over repeated rows@> ;
+ @<Reformat count list@> ;
+end ;
+if buff > 0 then
+ put_count(buff) ;
+put_count(end_of_char)
+
+@ Some more locals for |pack_and_send_character| used above:
+
+@<Locals to |pack_and_send_character|@>=
+@!extra : integer ; {little buffer for count values}
+@!put_ptr : integer ; {next location to fill in |row|}
+@!repeat_flag : integer ; {how many times the current row is repeated}
+@!h_bit : integer ; {horizontal bit count for each row}
+@!buff : integer ; {our count accumulator}
+
+@ In this short section of code, we are at the beginning of a new row.
+We scan forward, looking for repeated rows. If there are any, |repeat_flag|
+gets the count, and the |row_ptr| points to the beginning of the last of the
+repeated rows. Two points must be made here. First, we do not count all-black
+or all-white rows as repeated, as a large ``paint'' count will take care of
+them, and also there is no black to white or white to black transition in the
+row where we could insert a repeat count. That is the meaning of the big
+if statement that conditions this section. Secondly, the |while row[i] =
+row[j] do| loop is guaranteed to terminate, as $|j| > |i|$ and the character
+is terminated by a unique |end_of_char| value.
+
+@<Skip over repeated rows@>=
+i := row_ptr ;
+if ( row[i] <> end_of_row ) and ( ( row[i] <> extra ) or ( row[i+1] <>
+ width ) ) then begin
+ j := i + 1 ;
+ while row[j-1] <> end_of_row do incr(j) ;
+ while row[i] = row[j] do begin
+ if row[i] = end_of_row then begin
+ incr(repeat_flag) ;
+ row_ptr := i + 1 ;
+ end ;
+ incr(i) ; incr(j) ;
+ end ;
+end
+
+@ Here we actually spit out a row. The routine is somewhat similar to the
+routine where we actually interpret the \.{GF} commands in the count buffering.
+We must make sure to keep track of how many bits have actually been sent, so
+when we hit the end of a row, we can send a white count for the remaining
+bits, and possibly add the white count of the next row to it. And, finally,
+we must not forget to subtract the |extra| white space at the beginning of
+each row from the first white count.
+
+@<Reformat count list@>=
+if row[row_ptr] <> end_of_row then row[row_ptr] := row[row_ptr] - extra ;
+h_bit := 0;
+while row[row_ptr] <> end_of_row do begin
+ h_bit := h_bit + row[row_ptr] ;
+ if state then begin
+ buff := buff + row[row_ptr] ;
+ state := false ;
+ end else if row[row_ptr] > 0 then begin
+ put_count(buff) ;
+ buff := row[row_ptr] ;
+ end else state := true ;
+ incr(row_ptr) ;
+end ;
+if h_bit < width then
+ if state then
+ buff := buff + width - h_bit
+ else begin
+ put_count(buff) ;
+ buff := width - h_bit ;
+ state := true ;
+ end
+else state := false ;
+incr(row_ptr)
+
+@ Here is another piece of rather intricate code. We determine the
+smallest size in which we can pack the data, calculating |dyn_f| in the
+process. To do this, we calculate the size required if |dyn_f| is 0, and put
+this in |comp_size|. Then, we calculate the changes in the size for each
+increment of |dyn_f|, and stick these values in the |deriv| array. Finally,
+we scan through this array and find the final minimum value, which we then
+use to send the character data.
+
+@<Calculate |dyn_f| and packed size and write character@>=
+for i := 1 to 13 do deriv[i] := 0 ;
+i := 0 ;
+first_on := row[i] = 0 ;
+if first_on then incr(i) ;
+comp_size := 0 ;
+while row[i] <> end_of_char do
+ @<Process count for best |dyn_f| value@> ;
+b_comp_size := comp_size ;
+dyn_f := 0 ;
+for i := 1 to 13 do begin
+ comp_size := comp_size + deriv[i] ;
+ if comp_size <= b_comp_size then begin
+ b_comp_size := comp_size ;
+ dyn_f := i ;
+ end ;
+end ;
+comp_size := (b_comp_size + 1) div 2 ;
+if (comp_size > (height * width + 7) div 8) or (height * width = 0) then begin
+ comp_size := (height * width + 7) div 8 ;
+ dyn_f := 14 ;
+end ;
+d_print_ln('Best packing is dyn_f of ',dyn_f:1,' with length '
+ ,comp_size:1);
+@<Write character preamble@> ;
+if dyn_f <> 14 then
+ @<Send compressed format@>
+else if height > 0 then
+ @<Send bit map@>
+
+@ When we enter this module, we have a count at |row[i]|. First, we add to
+the |comp_size| the number of
+nybbles that this count would require, assuming |dyn_f| to be zero. When
+|dyn_f| is zero, there are no one nybble counts, so we simply choose between
+two-nybble and extensible counts and add the appropriate value.
+
+Next, we take the count value and determine the value of |dyn_f| (if any) that
+would cause this count to take either more or less nybbles. If a valid value
+for |dyn_f| exists in this range, we accumulate this change in the |deriv|
+array.
+
+One special case handled here is a repeat count of one.
+A repeat count of one will never change the length of the raster
+representation, no matter what |dyn_f| is, because it is always
+represented by the nybble value 15.
+
+@<Process count for best |dyn_f| value@>=
+begin
+ j := row[i] ;
+ if j = -1 then incr(comp_size)
+ else begin
+ if j < 0 then begin
+ incr(comp_size) ;
+ j := - j ;
+ end ;
+ if j < 209 then comp_size := comp_size + 2
+ else begin
+ k := j - 193 ;
+ while k >= 16 do begin
+ k := k div 16 ;
+ comp_size := comp_size + 2 ;
+ end ;
+ incr(comp_size) ;
+ end ;
+ if j < 14 then decr(deriv[j])
+ else if j < 209 then incr(deriv[(223 - j) div 15])
+ else begin
+ k := 16 ;
+ while ( k * 16 < j + 3 ) do k := k * 16 ;
+ if j-k <= 192 then deriv[(207-j+k) div 15] := deriv[(207-j+k) div 15]
+ + 2 ;
+ end ;
+ end ;
+ incr(i) ;
+end
+
+@ We need a handful of locals:
+
+@<Locals to |pack_and_send_character|@>=
+@!dyn_f : integer ; {packing value}
+@!height, @!width : integer ; {height and width of character}
+@!x_offset, @!y_offset : integer ; {offsets}
+@!deriv : array[1..13] of integer ; {derivative}
+@!b_comp_size : integer ; {best size}
+@!first_on : boolean ; {indicates that the first bit is on}
+@!flag_byte : integer ; {flag byte for character}
+@!state : boolean ; {state variable}
+@!on : boolean ; {white or black?}
+
+@ Now we write the character preamble information. First we need to determine
+which of the three formats we should use.
+
+@<Write character preamble@>=
+flag_byte := dyn_f * 16 ;
+if first_on then flag_byte := flag_byte + 8 ;
+if (gf_ch <> gf_ch_mod_256) or (tfm_width[gf_ch_mod_256] > 16777215) or
+ (tfm_width[gf_ch_mod_256] < 0) or (dy[gf_ch_mod_256] <> 0) or
+ (dx[gf_ch_mod_256] < 0) or (dx[gf_ch_mod_256] mod 65536 <> 0) or
+ (comp_size > 196594) or (width > 65535) or
+ (height > 65535) or (x_offset > 32767) or (y_offset > 32767) or
+ (x_offset < -32768) or (y_offset < -32768) then
+ @<Write long character preamble@>
+else if (dx[gf_ch] > 16777215) or (width > 255) or (height > 255) or
+ (x_offset > 127) or (y_offset > 127) or (x_offset < -128) or
+ (y_offset < -128) or (comp_size > 1015) then
+ @<Write two-byte short character preamble@>
+else
+ @<Write one-byte short character preamble@>
+
+@ If we must write a long character preamble, we
+adjust a few parameters, then write the data.
+
+@<Write long character preamble@>=
+begin
+ flag_byte := flag_byte + 7 ;
+ pk_byte(flag_byte) ;
+ comp_size := comp_size + 28 ;
+ pk_word(comp_size) ;
+ pk_word(gf_ch) ;
+ pred_pk_loc := pk_loc + comp_size ;
+ pk_word(tfm_width[gf_ch_mod_256]) ;
+ pk_word(dx[gf_ch_mod_256]) ;
+ pk_word(dy[gf_ch_mod_256]) ;
+ pk_word(width) ;
+ pk_word(height) ;
+ pk_word(x_offset) ;
+ pk_word(y_offset) ;
+end
+
+@ Here we write a short short character preamble, with one-byte size
+parameters.
+
+@<Write one-byte short character preamble@>=
+begin
+ comp_size := comp_size + 8 ;
+ flag_byte := flag_byte + comp_size div 256 ;
+ pk_byte(flag_byte) ;
+ pk_byte(comp_size mod 256) ;
+ pk_byte(gf_ch) ;
+ pred_pk_loc := pk_loc + comp_size ;
+ pk_three_bytes(tfm_width[gf_ch_mod_256]) ;
+ pk_byte(dx[gf_ch_mod_256] div 65536) ;
+ pk_byte(width) ;
+ pk_byte(height) ;
+ pk_byte(x_offset) ;
+ pk_byte(y_offset) ;
+end
+
+@ Here we write an extended short character preamble, with two-byte
+size parameters.
+
+@<Write two-byte short character preamble@>=
+begin
+ comp_size := comp_size + 13 ;
+ flag_byte := flag_byte + comp_size div 65536 + 4 ;
+ pk_byte(flag_byte) ;
+ pk_halfword(comp_size mod 65536) ;
+ pk_byte(gf_ch) ;
+ pred_pk_loc := pk_loc + comp_size ;
+ pk_three_bytes(tfm_width[gf_ch_mod_256]) ;
+ pk_halfword(dx[gf_ch_mod_256] div 65536) ;
+ pk_halfword(width) ;
+ pk_halfword(height) ;
+ pk_halfword(x_offset) ;
+ pk_halfword(y_offset) ;
+end
+
+@ At this point, we have decided that the run-encoded format is smaller. (This
+is almost always the case.) We send out the data, a nybble at a time.
+
+@<Send compressed format@>=
+begin
+ bit_weight := 16 ;
+ max_2 := 208 - 15 * dyn_f ;
+ i := 0 ;
+ if row[i] = 0 then incr(i) ;
+ while row[i] <> end_of_char do begin
+ j := row[i] ;
+ if j = -1 then
+ pk_nyb(15)
+ else begin
+ if j < 0 then begin
+ pk_nyb(14) ;
+ j := - j ;
+ end ;
+ if j <= dyn_f then pk_nyb(j)
+ else if j <= max_2 then begin
+ j := j - dyn_f - 1 ;
+ pk_nyb(j div 16 + dyn_f + 1) ;
+ pk_nyb(j mod 16) ;
+ end else begin
+ j := j - max_2 + 15 ;
+ k := 16 ;
+ while k <= j do begin
+ k := k * 16 ;
+ pk_nyb(0) ;
+ end ;
+ while k > 1 do begin
+ k := k div 16 ;
+ pk_nyb(j div k) ;
+ j := j mod k ;
+ end ;
+ end ;
+ end ;
+ incr(i) ;
+ end ;
+ if bit_weight <> 16 then pk_byte(output_byte) ;
+end
+
+@ This code is for the case where we have decided to send the character raster
+packed by bits. It uses the bit counts as well, sending eight at a time.
+Here we have a miniature packed format interpreter, as we must repeat any rows
+that are repeated. The algorithm to do this was a lot of fun to generate. Can
+you figure out how it works?
+
+@<Send bit map@>=
+begin
+ buff := 0 ;
+ p_bit := 8 ;
+ i := 1 ;
+ h_bit := width ;
+ on := false ;
+ state := false ;
+ count := row[0] ;
+ repeat_flag := 0 ;
+ while ( row[i] <> end_of_char ) or state or ( count > 0 ) do begin
+ if state then begin
+ count := r_count ; i := r_i ; on := r_on ;
+ decr(repeat_flag) ;
+ end else begin
+ r_count := count ; r_i := i ; r_on := on ;
+ end ;
+ @<Send one row by bits@> ;
+ if state and ( repeat_flag = 0 ) then begin
+ count := s_count ; i := s_i ; on := s_on ;
+ state := false ;
+ end else if not state and ( repeat_flag > 0 ) then begin
+ s_count := count ; s_i := i ; s_on := on ;
+ state := true ;
+ end ;
+ end ;
+ if p_bit <> 8 then pk_byte(buff) ;
+end
+
+@ All of the remaining locals:
+
+@<Locals to |pack_and_send_character|@>=
+@!comp_size : integer ; {length of the packed representation in bytes}
+@!count : integer ; {number of bits in current state to send}
+@!p_bit : integer ; {what bit are we about to send out?}
+@!r_on, @!s_on : boolean ; {state saving variables}
+@!r_count, @!s_count : integer ; {ditto}
+@!r_i, @!s_i : integer ; {and again.}
+@!max_2 : integer ; {the highest count that fits in two bytes}
+
+@ We make the |power| array global.
+
+@<Glob...@>=
+@!power : array[0..8] of integer ; {easy powers of two}
+
+@ We initialize the power array.
+
+@<Set init...@>=
+power[0] := 1 ;
+for i := 1 to 8 do power[i] := power[i-1] + power[i-1] ;
+
+@ Here we are at the beginning of a row and simply output the next |width| bits.
+We break the possibilities up into three cases: we finish a byte but not
+the row, we finish a row, and we finish neither a row nor a byte. But,
+first, we insure that we have a |count| value.
+
+@<Send one row by bits@>=
+repeat
+ if count = 0 then begin
+ if row[i] < 0 then begin
+ if not state then repeat_flag := - row[i] ;
+ incr(i) ;
+ end ;
+ count := row[i] ;
+ incr(i) ;
+ on := not on ;
+ end ;
+ if ( count >= p_bit ) and ( p_bit < h_bit ) then begin
+{ we end a byte, we don't end the row }
+ if on then buff := buff + power[p_bit] - 1 ;
+ pk_byte(buff) ; buff := 0 ;
+ h_bit := h_bit - p_bit ; count := count - p_bit ; p_bit := 8 ;
+ end else if ( count < p_bit ) and ( count < h_bit ) then begin
+{ we end neither the row nor the byte }
+ if on then buff := buff + power[p_bit] - power[p_bit - count] ;
+ p_bit := p_bit - count ; h_bit := h_bit - count ; count := 0 ;
+ end else begin
+{ we end a row and maybe a byte }
+ if on then buff := buff + power[p_bit] - power[p_bit - h_bit] ;
+ count := count - h_bit ; p_bit := p_bit - h_bit ; h_bit := width ;
+ if p_bit = 0 then begin
+ pk_byte(buff) ; buff := 0 ; p_bit := 8 ;
+ end ;
+ end ;
+until h_bit = width
+
+@ Now we are ready for the routine that writes the preamble of the packed
+file.
+
+@d preamble_comment == 'GFtoPK 2.4 output from '
+@d comm_length = 23 {length of |preamble_comment|}
+@d from_length = 6 {length of its |' from '| part}
+
+@<Write preamble@>=
+pk_byte(pk_pre) ;
+pk_byte(pk_id) ;
+i := gf_byte ; {get length of introductory comment}
+repeat if i=0 then j:="."@+else j:=gf_byte;
+decr(i); {some people think it's wise to avoid |goto| statements}
+until j<>" "; {remove leading blanks}
+incr(i); {this many bytes to copy}
+if i=0 then k:=comm_length-from_length
+else k := i+comm_length;
+if k>255 then pk_byte(255)@+else pk_byte(k);
+for k := 1 to comm_length do
+ if(i>0)or(k<=comm_length-from_length) then pk_byte(xord[comment[k]]) ;
+print('''') ;
+for k := 1 to i do
+ begin if k>1 then j:=gf_byte;
+ print(xchr[j]);
+ if k<256-comm_length then pk_byte(j);
+ end;
+print_ln('''') ;@/
+pk_word(design_size) ;
+pk_word(check_sum) ;
+pk_word(hppp) ;
+pk_word(vppp)
+
+@ Of course, we need an array to hold the comment.
+
+@<Glob...@>=
+@!comment : packed array[1..comm_length] of char ;
+
+@ @<Set init...@>=
+comment := preamble_comment ;
+
+@ Writing the postamble is even easier.
+
+@<Write postamble@>=
+pk_byte(pk_post) ;
+while (pk_loc mod 4 <> 0) do pk_byte(pk_no_op)
+
+@ Once we are finished with the \.{GF} file, we check the status of each
+character to insure that each character that had a locater also had raster
+information.
+
+@<Check for un-rasterized locaters@>=
+for i := 0 to 255 do
+ if status[i] = located then
+ print_ln('Character ',i:1,' missing raster information!')
+@.missing raster information@>
+
+@ Finally, the main program.
+
+@p begin
+ initialize ;
+ convert_gf_file ;
+ @<Check for un-rasterized locaters@> ;
+ print_ln(gf_len:1,' bytes packed to ',pk_loc:1,' bytes.') ;
+final_end : end .
+
+@ A few more globals.
+
+@<Glob...@>=
+@!check_sum : integer ; {the checksum of the file}
+@!design_size : integer ; {the design size of the font}
+@!h_mag : integer ; {the pixel magnification in pixels per inch}
+@!i : integer ;
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtoPK} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/systems/knuth/dist/mfware/gftype.web b/systems/knuth/dist/mfware/gftype.web
new file mode 100644
index 0000000000..ef6feeefef
--- /dev/null
+++ b/systems/knuth/dist/mfware/gftype.web
@@ -0,0 +1,1394 @@
+% This program by D. R. Fuchs is not copyrighted and can be used freely.
+% Version 0 was implemented in March 1984, before METAFONT itself was complete.
+% Version 1.0 incorporated refinements for the TRAP test (August 1984).
+% Version 1.1 fixed a trivial bug in the stated initial y value (August 1984).
+% Version 1.2 clarified the pixel coordinates (September 1984).
+% Version 2.0 changed over to new GF format (December 1984).
+% Version 2.1 changed over to newer GF format (February 1985).
+% Version 2.2 changed `family' to `extension' (October 1985).
+% Version 3.0 changed the default prompts (April 1989).
+% Version 3.1 checked character locators (Breitenlohner, March 1991).
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\noindent\ignorespaces}
+\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\font\tenss=cmss10 % for `The METAFONTbook'
+\def\PASCAL{Pascal}
+\def\ph{\hbox{Pascal-H}}
+\font\logo=manfnt % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\def\<#1>{$\langle#1\rangle$}
+\def\section{\mathhexbox278}
+\let\swap=\leftrightarrow
+\def\round{\mathop{\rm round}\nolimits}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
+
+\def\title{GFtype}
+\def\contentspagenumber{101}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont GFtype} processor}
+ \vskip 15pt
+ \centerline{(Version 3.1, March 1991)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926, MCS-8300984, and
+ CCR-8610181,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.
+ `{\logo hijklmnj}\kern1pt' is a trademark of Addison-Wesley
+ Publishing Company.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+The \.{GFtype} utility program reads binary generic-font (``\.{GF}'')
+files that are produced by font compilers such as \MF, and converts them
+into symbolic form. This program has three chief purposes:
+(1)~It can be used to look at the pixels of a font, with one pixel per
+character in a text file; (2)~it can be used to
+determine whether a \.{GF} file is valid or invalid, when diagnosing
+compiler errors; and (3)~it serves as an example of a program that reads
+\.{GF} files correctly, for system programmers who are developing
+\.{GF}-related software.
+
+The original version of this program was written by David R. Fuchs in
+March, 1984. Donald E. Knuth made a few modifications later that year as
+\MF\ was taking shape.
+@^Fuchs, David Raymond@>
+@^Knuth, Donald Ervin@>
+
+The |banner| string defined here should be changed whenever \.{GFtype}
+gets modified.
+
+@d banner=='This is GFtype, Version 3.1' {printed when the program starts}
+
+@ This program is written in standard \PASCAL, except where it is
+necessary to use extensions; for example, one extension is to use a
+default |case| as in \.{TANGLE}, \.{WEAVE}, etc. All places where
+nonstandard constructions are used have been listed in the index under
+``system dependencies.''
+@!@^system dependencies@>
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The binary input comes from |gf_file|, and the symbolic output is written
+on \PASCAL's standard |output| file. The term |print| is used instead of
+|write| when this program writes on |output|, so that all such output
+could easily be redirected if desired.
+
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+@d print_nl==write_ln
+
+@p program GF_type(@!gf_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ If the program has to stop prematurely, it goes to the
+`|final_end|'.
+
+@d final_end=9999 {label for the end of it all}
+
+@<Labels...@>=final_end;
+
+@ Four parameters can be changed at compile time to extend or
+reduce \.{GFtype}'s capacity.
+Note that the total number of bits in the main |image_array| will be
+$$\hbox{$(|max_row|+1)\;\times\;(|max_col|+1)$}.$$
+(\MF's full pixel range is rarely implemented, because it would require
+8~megabytes of memory.)
+
+@<Constants...@>=
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!line_length=79; {\\{xxx} strings will not produce lines longer than this}
+@!max_row=79; {vertical extent of pixel image array}
+@!max_col=79; {horizontal extent of pixel image array}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d negate(#) == #:=-# {change the sign of a variable}
+
+@ If the \.{GF} file is badly malformed, the whole process must be aborted;
+\.{GFtype} will give up, after issuing an error message about the symptoms
+that were noticed.
+
+Such errors might be discovered inside of subroutines inside of subroutines,
+so a procedure called |jump_out| has been introduced. This procedure, which
+simply transfers control to the label |final_end| at the end of the program,
+contains the only non-local |goto| statement in \.{GFtype}.
+@^system dependencies@>
+
+@d abort(#)==begin print(' ',#); jump_out;
+ end
+@d bad_gf(#)==abort('Bad GF file: ',#,'!')
+@.Bad GF file@>
+
+@p procedure jump_out;
+begin goto final_end;
+end;
+
+@* The character set.
+Like all programs written with the \.{WEB} system, \.{GFtype} can be
+used with any character set. But it uses ASCII code internally, because
+the programming for portable input-output is easier when a fixed internal
+code is used.
+
+The next few sections of \.{GFtype} have therefore been copied from the
+analogous ones in the \.{WEB} system routines. They have been considerably
+simplified, since \.{GFtype} need not deal with the controversial
+ASCII codes less than @'40 or greater than @'176.
+If such codes appear in the \.{GF} file,
+they will be printed as question marks.
+
+@<Types...@>=
+@!ASCII_code=" ".."~"; {a subrange of the integers}
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lower case
+letters. Nowadays, of course, we need to deal with both upper and lower case
+alphabets in a convenient way, especially in a program like \.{GFtype}.
+So we shall assume that the \PASCAL\ system being used for \.{GFtype}
+has a character set containing at least the standard visible characters
+of ASCII code (|"!"| through |"~"|).
+
+Some \PASCAL\ compilers use the original name |char| for the data type
+associated with the characters in text files, while other \PASCAL s
+consider |char| to be a 64-element subrange of a larger data type that has
+some other name. In order to accommodate this difference, we shall use
+the name |text_char| to stand for the data type of the characters in the
+output file. We shall also assume that |text_char| consists of
+the elements |chr(first_text_char)| through |chr(last_text_char)|,
+inclusive. The following definitions should be adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=127 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+
+@ The \.{GFtype} processor converts 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...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [0..255] of text_char;
+ {specifies conversion of output characters}
+
+@ Under our assumption that the visible characters of standard ASCII are
+all present, the following assignment statements initialize the
+|xchr| array properly, without needing any system-dependent changes.
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:='?';
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';
+for i:=@'177 to 255 do xchr[i]:='?';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
+for i:=" " to "~" do xord[xchr[i]]:=i;
+
+@* Generic font file format.
+The most important output produced by a typical run of \MF\ is the
+``generic font'' (\.{GF}) file that specifies the bit patterns of the
+characters that have been drawn. The term {\sl generic\/} indicates that
+this file format doesn't match the conventions of any name-brand manufacturer;
+but it is easy to convert \.{GF} files to the special format required by
+almost all digital phototypesetting equipment. There's a strong analogy
+between the \.{DVI} files written by \TeX\ and the \.{GF} files written
+by \MF; and, in fact, the file formats have a lot in common.
+It is therefore not surprising that \.{GFtype} is identical in many
+respects to the \.{DVItype} program.
+
+A \.{GF} file is a stream of 8-bit bytes that may be
+regarded as a series of commands in a machine-like language. The first
+byte of each command is the operation code, and this code is followed by
+zero or more bytes that provide parameters to the command. The parameters
+themselves may consist of several consecutive bytes; for example, the
+`|boc|' (beginning of character) command has six parameters, each of
+which is four bytes long. Parameters are usually regarded as nonnegative
+integers; but four-byte-long parameters can be either positive or
+negative, hence they range in value from $-2^{31}$ to $2^{31}-1$.
+As in \.{TFM} files, numbers that occupy
+more than one byte position appear in BigEndian order,
+and negative numbers appear in two's complement notation.
+
+A \.{GF} file consists of a ``preamble,'' followed by a sequence of one or
+more ``characters,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that introduce the file; this must come
+first. Each ``character'' consists of a |boc| command, followed by any
+number of other commands that specify ``black'' pixels,
+followed by an |eoc| command. The characters appear in the order that \MF\
+generated them. If we ignore no-op commands (which are allowed between any
+two commands in the file), each |eoc| command is immediately followed by a
+|boc| command, or by a |post| command; in the latter case, there are no
+more characters in the file, and the remaining bytes form the postamble.
+Further details about the postamble will be explained later.
+
+Some parameters in \.{GF} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first file byte is number~0, then comes number~1, and so on.
+
+@ The \.{GF} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+relative instead of absolute. When a \.{GF}-reading program reads the
+commands for a character, it keeps track of two quantities: (a)~the current
+column number,~|m|; and (b)~the current row number,~|n|. These are 32-bit
+signed integers, although most actual font formats produced from \.{GF}
+files will need to curtail this vast range because of practical
+limitations. (\MF\ output will never allow $\vert m\vert$ or $\vert
+n\vert$ to get extremely large, but the \.{GF} format tries to be more general.)
+
+How do \.{GF}'s row and column numbers correspond to the conventions
+of \TeX\ and \MF? Well, the ``reference point'' of a character, in \TeX's
+view, is considered to be at the lower left corner of the pixel in row~0
+and column~0. This point is the intersection of the baseline with the left
+edge of the type; it corresponds to location $(0,0)$ in \MF\ programs.
+Thus the pixel in \.{GF} row~0 and column~0 is \MF's unit square, comprising the
+region of the plane whose coordinates both lie between 0 and~1. The
+pixel in \.{GF} row~|n| and column~|m| consists of the points whose \MF\
+coordinates |(x,y)| satisfy |m<=x<=m+1| and |n<=y<=n+1|. Negative values of
+|m| and~|x| correspond to columns of pixels {\sl left\/} of the reference
+point; negative values of |n| and~|y| correspond to rows of pixels {\sl
+below\/} the baseline.
+
+Besides |m| and |n|, there's also a third aspect of the current
+state, namely the @!|paint_switch|, which is always either \\{black} or
+\\{white}. Each \\{paint} command advances |m| by a specified amount~|d|,
+and blackens the intervening pixels if |paint_switch=black|; then
+the |paint_switch| changes to the opposite state. \.{GF}'s commands are
+designed so that |m| will never decrease within a row, and |n| will never
+increase within a character; hence there is no way to whiten a pixel that
+has been blackened.
+
+@ Here is a list of all the commands that may appear in a \.{GF} file. Each
+command is specified by its symbolic name (e.g., |boc|), its opcode byte
+(e.g., 67), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|d[2]|' means that parameter |d| is two bytes long.
+
+\yskip\hang|paint_0| 0. This is a \\{paint} command with |d=0|; it does
+nothing but change the |paint_switch| from \\{black} to \\{white} or vice~versa.
+
+\yskip\hang\\{paint\_1} through \\{paint\_63} (opcodes 1 to 63).
+These are \\{paint} commands with |d=1| to~63, defined as follows: If
+|paint_switch=black|, blacken |d|~pixels of the current row~|n|,
+in columns |m| through |m+d-1| inclusive. Then, in any case,
+complement the |paint_switch| and advance |m| by~|d|.
+
+\yskip\hang|paint1| 64 |d[1]|. This is a \\{paint} command with a specified
+value of~|d|; \MF\ uses it to paint when |64<=d<256|.
+
+\yskip\hang|@!paint2| 65 |d[2]|. Same as |paint1|, but |d|~can be as high
+as~65535.
+
+\yskip\hang|@!paint3| 66 |d[3]|. Same as |paint1|, but |d|~can be as high
+as $2^{24}-1$. \MF\ never needs this command, and it is hard to imagine
+anybody making practical use of it; surely a more compact encoding will be
+desirable when characters can be this large. But the command is there,
+anyway, just in case.
+
+\yskip\hang|boc| 67 |c[4]| |p[4]| |min_m[4]| |max_m[4]| |min_n[4]|
+|max_n[4]|. Beginning of a character: Here |c| is the character code, and
+|p| points to the previous character beginning (if any) for characters having
+this code number modulo 256. (The pointer |p| is |-1| if there was no
+prior character with an equivalent code.) The values of registers |m| and |n|
+defined by the instructions that follow for this character must
+satisfy |min_m<=m<=max_m| and |min_n<=n<=max_n|. (The values of |max_m| and
+|min_n| need not be the tightest bounds possible.) When a \.{GF}-reading
+program sees a |boc|, it can use |min_m|, |max_m|, |min_n|, and |max_n| to
+initialize the bounds of an array. Then it sets |m:=min_m|, |n:=max_n|, and
+|paint_switch:=white|.
+
+\yskip\hang|boc1| 68 |c[1]| |@!del_m[1]| |max_m[1]| |@!del_n[1]| |max_n[1]|.
+Same as |boc|, but |p| is assumed to be~$-1$; also |del_m=max_m-min_m|
+and |del_n=max_n-min_n| are given instead of |min_m| and |min_n|.
+The one-byte parameters must be between 0 and 255, inclusive.
+\ (This abbreviated |boc| saves 19~bytes per character, in common cases.)
+
+\yskip\hang|eoc| 69. End of character: All pixels blackened so far
+constitute the pattern for this character. In particular, a completely
+blank character might have |eoc| immediately following |boc|.
+
+\yskip\hang|skip0| 70. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=white|. \ (This finishes one row and begins another,
+ready to whiten the leftmost pixel in the new row.)
+
+\yskip\hang|skip1| 71 |d[1]|. Decrease |n| by |d+1|, set |m:=min_m|, and set
+|paint_switch:=white|. This is a way to produce |d| all-white rows.
+
+\yskip\hang|@!skip2| 72 |d[2]|. Same as |skip1|, but |d| can be as large
+as 65535.
+
+\yskip\hang|@!skip3| 73 |d[3]|. Same as |skip1|, but |d| can be as large
+as $2^{24}-1$. \MF\ obviously never needs this command.
+
+\yskip\hang|new_row_0| 74. Decrease |n| by 1 and set |m:=min_m|,
+|paint_switch:=black|. \ (This finishes one row and begins another,
+ready to {\sl blacken\/} the leftmost pixel in the new row.)
+
+\yskip\hang|@!new_row_1| through |@!new_row_164| (opcodes 75 to 238). Same as
+|new_row_0|, but with |m:=min_m+1| through |min_m+164|, respectively.
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |no_op| unless special \.{GF}-reading
+programs are being used. \MF\ generates \\{xxx} commands when encountering
+a \&{special} string; this occurs in the \.{GF} file only between
+characters, after the preamble, and before the postamble. However,
+\\{xxx} commands might appear within characters,
+in \.{GF} files generated by other
+processors. It is recommended that |x| be a string having the form of a
+keyword followed by possible parameters relevant to that keyword.
+
+\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+\MF\ uses this when sending a \&{special} string whose length exceeds~255.
+
+\yskip\hang|@!xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be
+ridiculously large; |k| mustn't be negative.
+
+\yskip\hang|yyy| 243 |y[4]|. This command is undefined in general;
+it functions as a 5-byte |no_op| unless special \.{GF}-reading programs
+are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a
+result of \&{numspecial} commands; the intent is to provide numeric
+parameters to \\{xxx} commands that immediately precede.
+
+\yskip\hang|no_op| 244. No operation, do nothing. Any number of |no_op|'s
+may occur between \.{GF} commands, but a |no_op| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|char_loc| 245 |c[1]| |dx[4]| |dy[4]| |w[4]| |p[4]|.
+This command will appear only in the postamble, which will be explained shortly.
+
+\yskip\hang|@!char_loc0| 246 |c[1]| |@!dm[1]| |w[4]| |p[4]|.
+Same as |char_loc|, except that |dy| is assumed to be zero, and the value
+of~|dx| is taken to be |65536*dm|, where |0<=dm<256|.
+
+\yskip\hang|pre| 247 |i[1]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameter |i| is an identifying number for \.{GF} format, currently
+131. The other information is merely commentary; it is not given
+special interpretation like \\{xxx} commands are. (Note that \\{xxx}
+commands may immediately follow the preamble, before the first |boc|.)
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@d gf_id_byte=131 {identifies the kind of \.{GF} files described here}
+
+@ Here are the opcodes that \.{GFtype} actually refers to.
+
+@d paint_0=0 {beginning of the \\{paint} commands}
+@d paint1=64 {move right a given number of columns, then
+ black${}\swap{}$white}
+@d boc=67 {beginning of a character}
+@d boc1=68 {abbreviated |boc|}
+@d eoc=69 {end of a character}
+@d skip0=70 {skip no blank rows}
+@d skip1=71 {skip over blank rows}
+@d new_row_0=74 {move down one row and then right}
+@d xxx1=239 {for \&{special} strings}
+@d yyy=243 {for \&{numspecial} numbers}
+@d no_op=244 {no operation}
+@d char_loc=245 {character locators in the postamble}
+@d pre=247 {preamble}
+@d post=248 {postamble beginning}
+@d post_post=249 {postamble ending}
+@d undefined_commands==250,251,252,253,254,255
+
+@ The last character in a \.{GF} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \MF\ has
+accumulated. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |@!ds[4]| |@!cs[4]| |@!hppp[4]| |@!vppp[4]|
+ |min_m[4]| |max_m[4]| |min_n[4]| |max_n[4]|\cr
+ $\langle\,$character locators$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the byte following the final |eoc| in the file
+(or to the byte following the preamble, if there are no characters);
+it can be used to locate the beginning of \\{xxx} commands
+that might have preceded the postamble. The |ds| and |cs| parameters
+@^design size@> @^check sum@>
+give the design size and check sum, respectively, which are exactly the
+values put into the header of any \.{TFM} file that shares information with this
+\.{GF} file. Parameters |hppp| and |vppp| are the ratios of
+pixels per point, horizontally and vertically, expressed as |scaled| integers
+(i.e., multiplied by $2^{16}$); they can be used to correlate the font
+with specific device resolutions, magnifications, and ``at sizes.'' Then
+come |min_m|, |max_m|, |min_n|, and |max_n|, which bound the values that
+registers |m| and~|n| assume in all characters in this \.{GF} file.
+(These bounds need not be the best possible; |max_m| and |min_n| may, on the
+other hand, be tighter than the similar bounds in |boc| commands. For
+example, some character may have |min_n=-100| in its |boc|, but it might
+turn out that |n| never gets lower than |-50| in any character; then
+|min_n| can have any value |<=-50|. If there are no characters in the file,
+it's possible to have |min_m>max_m| and/or |min_n>max_n|.)
+
+@ Character locators are introduced by |char_loc| commands,
+which specify a character residue~|c|, character escapements (|dx,dy|),
+a character width~|w|, and a pointer~|p|
+to the beginning of that character. (If two or more characters have the
+same code~|c| modulo 256, only the last will be indicated; the others can be
+located by following backpointers. Characters whose codes differ by a
+multiple of 256 are assumed to share the same font metric information,
+hence the \.{TFM} file contains only residues of character codes modulo~256.
+This convention is intended for oriental languages, when there are many
+character shapes but few distinct widths.)
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+The character escapements (|dx,dy|) are the values of \MF's \&{chardx}
+and \&{chardy} parameters; they are in units of |scaled| pixels;
+i.e., |dx| is in horizontal pixel units times $2^{16}$, and |dy| is in
+vertical pixel units times $2^{16}$. This is the intended amount of
+displacement after typesetting the character; for \.{DVI} files, |dy|
+should be zero, but other document file formats allow nonzero vertical
+escapement.
+
+The character width~|w| duplicates the information in the \.{TFM} file; it
+is $2^{24}$ times the ratio of the true width to the font's design size.
+
+The backpointer |p| points to the character's |boc|, or to the first of
+a sequence of consecutive \\{xxx} or |yyy| or |no_op| commands that
+immediately precede the |boc|, if such commands exist; such ``special''
+commands essentially belong to the characters, while the special commands
+after the final character belong to the postamble (i.e., to the font
+as a whole). This convention about |p| applies also to the backpointers
+in |boc| commands, even though it wasn't explained in the description
+of~|boc|. @^backpointers@>
+
+Pointer |p| might be |-1| if the character exists in the \.{TFM} file
+but not in the \.{GF} file. This unusual situation can arise in \MF\ output
+if the user had |proofing<0| when the character was being shipped out,
+but then made |proofing>=0| in order to get a \.{GF} file.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the character locators, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~131, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., @"DF in hexadecimal).
+@^Fuchs, David Raymond@>
+\MF\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+
+This curious way to finish off a \.{GF} file makes it feasible for
+\.{GF}-reading programs to find the postamble first, on most computers,
+even though \MF\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{GF} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{GF} reader can discover all the information needed for individual characters.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{GF}
+format has been designed to work most efficiently with modern operating systems.
+But if \.{GF} files have to be processed under the restrictions of standard
+\PASCAL, one can simply read them from front to back. This will
+be adequate for most applications. However, the postamble-first approach
+would facilitate a program that merges two \.{GF} files, replacing data
+from one that is overridden by corresponding data in the other.
+
+@* Input from binary files.
+We have seen that a \.{GF} file is a sequence of 8-bit bytes. The bytes
+appear physically in what is called a `|packed file of 0..255|'
+in \PASCAL\ lingo.
+
+Packing is system dependent, and many \PASCAL\ systems fail to implement
+such files in a sensible way (at least, from the viewpoint of producing
+good production software). For example, some systems treat all
+byte-oriented files as text, looking for end-of-line marks and such
+things. Therefore some system-dependent code is often needed to deal with
+binary files, even though most of the program in this section of
+\.{GFtype} is written in standard \PASCAL.
+@^system dependencies@>
+
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+
+@ The program deals with one binary file variable: |gf_file| is the main
+input file that we are translating into symbolic form.
+
+@<Glob...@>=
+@!gf_file:byte_file; {the stuff we are \.{GF}typing}
+
+@ To prepare this file for input, we |reset| it.
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin reset(gf_file);
+cur_loc:=0;
+end;
+
+@ If you looked carefully at the preceding code, you probably asked,
+``What is |cur_loc|?'' Good question. It's a global variable that holds
+the number of the byte about to be read next from |gf_file|.
+
+@<Glob...@>=
+@!cur_loc:integer; {where we are about to look, in |gf_file|}
+
+@ We shall use a set of simple functions to read the next byte or
+bytes from |gf_file|. There are four possibilities, each of which is
+treated as a separate function in order to minimize the overhead for
+subroutine calls.
+@^system dependencies@>
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(gf_file,a); read(gf_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+
+@* Optional modes of output.
+\.{GFtype} will print different quantities of information based on some
+options that the user must specify: We set |wants_mnemonics| if the
+user wants to see a mnemonic dump of the \.{GF} file; and we set
+|wants_pixels| if the user wants to see a pixel image of each
+character.
+
+When \.{GFtype} begins, it engages the user in a brief dialog so that the
+options will be specified. This part of \.{GFtype} requires nonstandard
+\PASCAL\ constructions to handle the online interaction; so it may be
+preferable in some cases to omit the dialog and simply to produce the maximum
+possible output (|wants_mnemonics=wants_pixels=true|). On other hand, the
+necessary system-dependent routines are not complicated, so they can be
+introduced without terrible trauma.
+@^system dependencies@>
+
+@<Glob...@>=
+@!wants_mnemonics: boolean; {controls mnemonic output}
+@!wants_pixels: boolean; {controls pixel output}
+
+@ @<Set init...@>=
+wants_mnemonics:=true; wants_pixels:=true;
+
+@ The |input_ln| routine waits for the user to type a line at his or her
+terminal; then it puts ASCII-code equivalents for the characters on that line
+into the |buffer| array. The |term_in| file is used for terminal input,
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!term_in:text_file; {the terminal, considered as an input file}
+@!term_out:text_file; {the terminal, considered as an output file}
+
+@ Since the terminal is being used for both input and output, some systems
+need a special routine to make sure that the user can see a prompt message
+before waiting for input based on that message. (Otherwise the message
+may just be sitting in a hidden buffer somewhere, and the user will have
+no idea what the program is waiting for.) We shall invoke a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+
+@ During the dialog, extensions of \.{GFtype} might treat the first blank
+space in a line as the end of that line. Therefore |input_ln| makes sure
+that there is always at least one blank space in |buffer|.
+
+(This routine is more complex than the present implementation needs, but
+it has been copied from \.{DVItype} so that system-dependent changes that
+worked before will work again.)
+@^system dependencies@>
+
+@p procedure input_ln; {inputs a line from the terminal}
+var k:0..terminal_line_length;
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+k:=0;
+while (k<terminal_line_length)and not eoln(term_in) do
+ begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
+ end;
+buffer[k]:=" ";
+end;
+
+@ This is humdrum.
+
+@p function lower_casify(@!c:ASCII_code):ASCII_code;
+begin
+if (c>="A") and (c<="Z") then lower_casify:=c+"a"-"A"
+else lower_casify:=c;
+end;
+
+@ The selected options are put into global variables by the |dialog|
+procedure, which is called just as \.{GFtype} begins.
+@^system dependencies@>
+
+@p procedure dialog;
+label 1,2;
+begin rewrite(term_out); {prepare the terminal for output}
+write_ln(term_out,banner);@/
+@<Determine whether the user |wants_mnemonics|@>;
+@<Determine whether the user |wants_pixels|@>;
+@<Print all the selected options@>;
+end;
+
+@ @<Determine whether the user |wants_mnemonics|@>=
+1: write(term_out,'Mnemonic output? (default=no, ? for help): ');
+@.Mnemonic output?@>
+input_ln;
+buffer[0]:=lower_casify(buffer[0]);
+if buffer[0]<>"?" then
+ wants_mnemonics:=(buffer[0]="y")or(buffer[0]="1")or(buffer[0]="t")
+else begin write(term_out,'Type Y for complete listing,');
+ write_ln(term_out,' N for errors/images only.');
+ goto 1;
+ end
+
+@ @<Determine whether the user |wants_pixels|@>=
+2: write(term_out,'Pixel output? (default=yes, ? for help): ');
+@.Pixel output?@>
+input_ln;
+buffer[0]:=lower_casify(buffer[0]);
+if buffer[0]<>"?" then
+ wants_pixels:=(buffer[0]="y")or(buffer[0]="1")or(buffer[0]="t")
+ or(buffer[0]=" ")
+else begin write(term_out,'Type Y to list characters pictorially');
+ write_ln(term_out,' with *''s, N to omit this option.');
+ goto 2;
+ end
+
+@ After the dialog is over, we print the options so that the user
+can see what \.{GFtype} thought was specified.
+
+@<Print all the selected options@>=
+print('Options selected: Mnemonic output = ');
+@.Options selected@>
+if wants_mnemonics then print('true')@+else print('false');
+print('; pixel output = ');
+if wants_pixels then print('true')@+else print('false');
+print_ln('.')
+
+@* The image array.
+The definition of \.{GF} files refers to two registers,
+|m| and~|n|, which hold integer column and row numbers. We actually
+keep the values $m'=m-|min_m|$ and $n'=|max_n|-n$ instead, so that
+our internal image array always has |m,n>=0|. We also
+need to remember |paint_switch|, whose value is either |black|
+or |white|.
+
+@<Glob...@>=
+@!m,@!n:integer; {current state values, modified by |min_m| and |max_n|}
+@!paint_switch: pixel;
+
+@ We'll need a big array of pixels to hold the character image. Each
+pixel should be represented as a single bit in order to save space.
+Some systems may prefer the following definitions, while others
+may do better using the |boolean| type and boolean constants.
+@^system dependencies@>
+
+@d white=0 {could also be |false|}
+@d black=1 {could also be |true|}
+
+@<Types...@>=
+@!pixel=white..black; {could also be |boolean|}
+
+@ In order to allow different systems to change the |image| array easily from
+row-major order to column-major order (or vice versa), or to transpose it top
+and bottom or left and right, we declare and access it as follows.
+@^system dependencies@>
+
+@d image==image_array[m,n]
+
+@<Glob...@>=
+@!image_array: packed array [0..max_col,0..max_row] of pixel;
+
+@ A |boc| command has parameters |min_m|, |max_m|, |min_n|, and |max_n|
+that define a rectangular subarray in which the pixels of the current
+character must lie. The program here computes limits on \.{GFtype}'s
+modified |m| and |n| variables, and clears the resulting subarray
+to all |white|.
+
+(There may be a faster way to clear a subarray on particular systems,
+using nonstandard extensions of \PASCAL.)
+@^system dependencies@>
+
+@<Clear the image@>=
+begin max_subcol:=max_m_stated-min_m_stated-1;
+if max_subcol>max_col then max_subcol:=max_col;
+max_subrow:=max_n_stated-min_n_stated;
+if max_subrow>max_row then max_subrow:=max_row;
+n:=0;
+while n<=max_subrow do
+ begin m:=0;
+ while m<=max_subcol do
+ begin image:=white; incr(m);
+ end;
+ incr(n);
+ end;
+end
+
+@ @<Glob...@>=
+@!max_subrow,@!max_subcol:integer; {size of current subarray of interest}
+
+@ As we paint the pixels of a character, we will record its actual
+boundaries in variables |max_m_observed| and |max_n_observed|.
+Then the following routine will be called on to output the image,
+using blanks for |white| and asterisks for |black|. Blanks are
+emitted only when they are followed by nonblanks, in order to conserve
+space in the output. Further compaction could be achieved on many
+systems by using tab marks.
+@^system dependencies@>
+
+An integer variable |b| will be declared for use in counting blanks.
+
+@<Print the image@>=
+begin @<Compare the subarray boundaries with the observed boundaries@>;
+if max_subcol>=0 then {there was at least one \\{paint} command}
+ @<Print asterisk patterns for rows 0 to |max_subrow|@>
+else print_ln('(The character is entirely blank.)');
+end
+
+@ @<Glob...@>=
+@!min_m_stated, @!max_m_stated, @!min_n_stated, @!max_n_stated: integer;
+ {bounds stated in the \.{GF} file}
+@!max_m_observed,@!max_n_observed: integer;
+ {bounds on $(m',n')$ actually observed when painting}
+@!min_m_overall, @!max_m_overall, @!min_n_overall, @!max_n_overall: integer;
+ {bounds observed in the entire file so far}
+
+@ If the given character is substantially smaller than the |boc|
+command predicted, we don't want to bother to output rows and columns
+that are all blank.
+
+@<Compare the subarray boundaries with the observed boundaries@>=
+if (max_m_observed>max_col)or(max_n_observed>max_row) then
+ print_ln('(The character is too large to be displayed in full.)');
+@.The character is too large...@>
+if max_subcol>max_m_observed then max_subcol:=max_m_observed;
+if max_subrow>max_n_observed then max_subrow:=max_n_observed;
+
+@ @<Print asterisk patterns...@>=
+begin print_ln('.<--This pixel''s lower left corner is at (',
+ min_m_stated:1,',',max_n_stated+1:1,') in METAFONT coordinates');
+@.This pixel's lower...@>
+n:=0;
+while n<=max_subrow do
+ begin m:=0; b:=0;
+ while m<=max_subcol do
+ begin if image=white then incr(b)
+ else begin while b>0 do
+ begin print(' '); decr(b);
+ end;
+ print('*');
+ end;
+ incr(m);
+ end;
+ print_nl; incr(n);
+ end;
+print_ln('.<--This pixel''s upper left corner is at (',
+ min_m_stated:1,',',max_n_stated-max_subrow:1,
+ ') in METAFONT coordinates');
+@.This pixel's upper@>
+end
+
+@* Translation to symbolic form.
+The main work of \.{GFtype} is accomplished by the |do_char| procedure,
+which produces the output for an entire character, assuming that the |boc|
+command for that character has already been processed. This procedure is
+essentially an interpretive routine that reads and acts on the \.{GF}
+commands.
+
+@ We steal the following routine from \MF.
+
+@d unity == @'200000 {$2^{16}$, represents 1.00000}
+
+@p procedure print_scaled(@!s:integer);
+ {prints a scaled number, rounded to five digits}
+var @!delta:integer; {amount of allowable inaccuracy}
+begin if s<0 then
+ begin print('-'); negate(s); {print the sign, if negative}
+ end;
+print(s div unity:1); {print the integer part}
+s:=10*(s mod unity)+5;
+if s<>5 then
+ begin delta:=10; print('.');
+ repeat if delta>unity then
+ s:=s+@'100000-(delta div 2); {round the final digit}
+ print(chr(ord('0')+(s div unity))); s:=10*(s mod unity); delta:=delta*10;
+ until s<=delta;
+ end;
+end;
+
+@ Let's keep track of how many characters are in the font, and the
+locations of where each one occurred in the file.
+
+@<Glob...@>=
+@!total_chars:integer; {the total number of characters seen so far}
+@!char_ptr: array[0..255] of integer; {correct character location pointer}
+@!gf_prev_ptr: integer; {|char_ptr| for next character}
+@!character_code: integer; {current character number}
+
+@ @<Set init...@>=
+for i:=0 to 255 do char_ptr[i]:=-1; {mark characters as not being in the file}
+total_chars:=0;
+
+@ Before we get into the details of |do_char|, it is convenient to
+consider a simpler routine that computes the first parameter of each
+opcode.
+
+@d four_cases(#)==#,#+1,#+2,#+3
+@d eight_cases(#)==four_cases(#),four_cases(#+4)
+@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
+@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
+@d thirty_seven_cases(#)==thirty_two_cases(#),four_cases(#+32),#+36
+@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)
+
+@p function first_par(o:eight_bits):integer;
+begin case o of
+sixty_four_cases(paint_0): first_par:=o-paint_0;
+paint1,skip1,char_loc,char_loc+1,xxx1: first_par:=get_byte;
+paint1+1,skip1+1,xxx1+1: first_par:=get_two_bytes;
+paint1+2,skip1+2,xxx1+2: first_par:=get_three_bytes;
+xxx1+3,yyy: first_par:=signed_quad;
+boc,boc1,eoc,skip0,no_op,pre,post,post_post,undefined_commands: first_par:=0;
+sixty_four_cases(new_row_0), sixty_four_cases(new_row_0+64),
+ thirty_seven_cases(new_row_0+128): first_par:=o-new_row_0;
+end;
+end;
+
+@ Strictly speaking, the |do_char| procedure is really a function with
+side effects, not a `\&{procedure}'\thinspace; it returns the value |false|
+if \.{GFtype} should be aborted because of some unusual happening. The
+subroutine is organized as a typical interpreter, with a multiway branch
+on the command code.
+
+@p function do_char:boolean;
+label 9998,9999;
+var o:eight_bits; {operation code of the current command}
+@!p,@!q:integer; {parameters of the current command}
+@!aok:boolean; {the value to return}
+begin {we've already scanned the |boc|}
+aok:=true;
+while true do @<Translate the next command in the \.{GF} file;
+ |goto 9999| if it was |eoc|;
+ |goto 9998| if premature termination is needed@>;
+9998: print_ln('!'); aok:=false;
+9999: do_char:=aok;
+end;
+
+@ @d show_label(#)==print(a:1,': ',#)
+@d show_mnemonic(#)==if wants_mnemonics then begin print_nl; show_label(#); end
+@d error(#)==begin show_label('! ',#); print_nl; end
+@d nl_error(#)==begin print_nl; show_label('! ',#); print_nl; end
+@d start_op==a:=cur_loc; o:=get_byte; p:=first_par(o);
+ if eof(gf_file) then bad_gf('the file ended prematurely')
+@.the file ended prematurely@>
+
+@<Translate the next command...@>=
+begin start_op;
+@<Start translation of command |o| and |goto| the appropriate label to
+ finish the job@>;
+end
+
+@ The multiway switch in |first_par|, above, was organized by the length
+of each command; the one in |do_char| is organized by the semantics.
+
+@<Start translation...@>=
+if o<=paint1+3 then @<Translate a sequence of |paint| commands,
+ until reaching a non-|paint|@>;
+case o of
+four_cases(skip0): @<Translate a |skip| command@>;
+sixty_four_cases(new_row_0), sixty_four_cases(new_row_0+64),
+ thirty_seven_cases(new_row_0+128):
+ @<Translate a |new_row| command@>;
+@t\4@>@<Cases for commands |no_op|, |pre|, |post|, |post_post|, |boc|,
+ and |eoc|@>@;
+four_cases(xxx1): @<Translate an |xxx| command@>;
+yyy: @<Translate a |yyy| command@>;
+othercases error('undefined command ',o:1,'!')
+@.undefined command@>
+endcases
+
+@ @<Cases for commands |no_op|...@>=
+no_op: show_mnemonic('no op');
+pre: begin error('preamble command within a character!'); goto 9998;
+ end;
+@.preamble command within...@>
+post,post_post: begin error('postamble command within a character!');
+@.postamble command within...@>
+ goto 9998;
+ end;
+boc,boc1: begin error('boc occurred before eoc!'); goto 9998;
+@.boc occurred before eoc@>
+ end;
+eoc: begin show_mnemonic('eoc');
+ print_nl; goto 9999;
+ end;
+
+@ @<Translate an |xxx| command@>=
+begin show_mnemonic('xxx '''); bad_char:=false; b:=16;
+if p<0 then nl_error('string of negative length!');
+@.string of negative length@>
+while p>0 do
+ begin q:=get_byte;
+ if (q<" ")or(q>"~") then bad_char:=true;
+ if wants_mnemonics then
+ begin print(xchr[q]);
+ if b<line_length then incr(b)
+ else begin print_nl; b:=2;
+ end;
+ end;
+ decr(p);
+ end;
+if wants_mnemonics then print('''');
+if bad_char then nl_error('non-ASCII character in xxx command!');
+@.non-ASCII character...@>
+end
+
+@ @<Glob...@>=
+@!bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
+
+@ @<Translate a |yyy| command@>=
+begin show_mnemonic('yyy ',p:1,' (');
+if wants_mnemonics then
+ begin print_scaled(p); print(')');
+ end;
+end
+
+@ The bulk of a \.{GF} file generally consists of |paint| commands,
+so we collect them together and print them in an abbreviated format
+on one line.
+
+@<Translate a sequence of |paint| commands...@>=
+begin if wants_mnemonics then print(' paint ');
+repeat @<Paint the next |p| pixels@>;
+start_op;
+until o>paint1+3;
+end
+
+@ @<Paint the next...@>=
+if wants_mnemonics then
+ if paint_switch=white then print('(',p:1,')')@+else print(p:1);
+m:=m+p;
+if m>max_m_observed then max_m_observed:=m-1;
+if wants_pixels then
+ @<Paint pixels |m-p| through |m-1| in row |n| of the subarray@>;
+paint_switch:=white+black-paint_switch
+ {could also be |paint_switch:=not paint_switch|}
+
+@ We use the fact that the subarray has been initialized to all |white|.
+
+@<Paint pixels |m-p|...@>=
+if paint_switch=black then if n<=max_subrow then
+ begin l:=m-p; r:=m-1;
+ if r>max_subcol then r:=max_subcol;
+ m:=l;
+ while m<=r do
+ begin image:=black; incr(m);
+ end;
+ m:=l+p;
+ end
+
+@ @<Translate a |new_row| command@>=
+begin show_mnemonic('newrow ',p:1);
+incr(n); m:=p; paint_switch:=black;
+if wants_mnemonics then print(' (n=',max_n_stated-n:1,')');
+end
+
+@ @<Translate a |skip| command@>=
+begin show_mnemonic('skip',(o-skip1+1)mod 4:1,' ',p:1);
+n:=n+p+1; m:=0; paint_switch:=white;
+if wants_mnemonics then print(' (n=',max_n_stated-n:1,')');
+end
+
+@* Reading the postamble.
+Now imagine that we are reading the \.{GF} file and positioned just
+after the |post| command. That, in fact, is the situation,
+when the following part of \.{GFtype} is called upon to read, translate,
+and check the rest of the postamble.
+
+@p procedure read_postamble;
+var k:integer; {loop index}
+@!p,@!q,@!m,@!u,@!v,@!w,@!c:integer; {general purpose registers}
+begin post_loc:=cur_loc-1;
+print('Postamble starts at byte ',post_loc:1);
+@.Postamble starts at byte n@>
+if post_loc=gf_prev_ptr then print_ln('.')
+else print_ln(', after special info at byte ',gf_prev_ptr:1,'.');
+p:=signed_quad;
+if p<>gf_prev_ptr then
+ error('backpointer in byte ',cur_loc-4:1,
+ ' should be ',gf_prev_ptr:1,' not ',p:1,'!');
+@.backpointer...should be p@>
+design_size:=signed_quad; check_sum:=signed_quad;@/
+print('design size = ',design_size:1,' (');
+print_scaled(design_size div 16); print_ln('pt)');
+print_ln('check sum = ',check_sum:1);@/
+hppp:=signed_quad; vppp:=signed_quad;@/
+print('hppp = ',hppp:1,' ('); print_scaled(hppp); print_ln(')');
+print('vppp = ',vppp:1,' ('); print_scaled(vppp); print_ln(')');
+pix_ratio:=(design_size/1048576)*(hppp/1048576);
+min_m_stated:=signed_quad; max_m_stated:=signed_quad;
+min_n_stated:=signed_quad; max_n_stated:=signed_quad;@/
+print_ln('min m = ',min_m_stated:1,', max m = ',max_m_stated:1);@/
+if min_m_stated>min_m_overall then
+ error('min m should be <=',min_m_overall:1,'!');
+if max_m_stated<max_m_overall then
+ error('max m should be >=',max_m_overall:1,'!');
+print_ln('min n = ',min_n_stated:1,', max n = ',max_n_stated:1);@/
+if min_n_stated>min_n_overall then
+ error('min n should be <=',min_n_overall:1,'!');
+if max_n_stated<max_n_overall then
+ error('max n should be >=',max_n_overall:1,'!');
+@<Process the character locations in the postamble@>;
+@<Make sure that the end of the file is well-formed@>;
+end;
+
+@ @<Glob...@>=
+@!design_size,@!check_sum: integer; {\.{TFM}-oriented parameters}
+@!hppp, @!vppp: integer; {magnification-oriented parameters}
+@!post_loc: integer; {location of the |post| command}
+@!pix_ratio: real; {multiply by this to convert \.{TFM} width to scaled pixels}
+
+@ @<Set init...@>=
+min_m_overall:=max_int; max_m_overall:=-max_int;
+min_n_overall:=max_int; max_n_overall:=-max_int;
+
+@ When we get to the present code, the |post_post| command has
+just been read.
+
+@<Make sure that the end of the file is well-formed@>=
+if k<>post_post then
+ error('should be postpost!');
+@.should be postpost@>
+for k:=0 to 255 do if char_ptr[k]>0 then
+ error('missing locator for character ',k:1,'!');
+@.missing locator...@>
+q:=signed_quad;
+if q<>post_loc then
+ error('postamble pointer should be ',post_loc:1,' not ',q:1,'!');
+@.postamble pointer should be...@>
+m:=get_byte;
+if m<>gf_id_byte then error('identification byte should be ',gf_id_byte:1,
+ ', not ',m:1,'!');
+@.identification byte should be n@>
+k:=cur_loc; m:=223;
+while (m=223)and not eof(gf_file) do m:=get_byte;
+if not eof(gf_file) then bad_gf('signature in byte ',cur_loc-1:1,
+@.signature...should be...@>
+ ' should be 223')
+else if cur_loc<k+4 then
+ error('not enough signature bytes at end of file!')
+@.not enough signature bytes...@>
+
+@ @<Process the character locations...@>=
+repeat a:=cur_loc; k:=get_byte;
+if (k=char_loc)or(k=char_loc+1) then
+ begin c:=first_par(k);
+ if k=char_loc then
+ begin u:=signed_quad; v:=signed_quad;
+ end
+ else begin u:=get_byte*unity; v:=0;
+ end;
+ w:=signed_quad; p:=signed_quad;
+ print('Character ',c:1,': dx ',u:1,' (');
+ print_scaled(u);
+ if v<>0 then
+ begin print('), dy ',v:1,' ('); print_scaled(v);
+ end;
+ print('), width ',w:1,' (');
+ w:=round(w*pix_ratio);
+ print_scaled(w);
+ print_ln('), loc ',p:1);
+ if char_ptr[c]=0 then
+ error('duplicate locator for this character!')
+@.duplicate locator...@>
+ else if p<>char_ptr[c] then
+ error('character location should be ',char_ptr[c]:1,'!');
+@.character location should be...@>
+ char_ptr[c]:=0;
+ k:=no_op;
+ end;
+until k<>no_op
+
+@* The main program.
+Now we are ready to put it all together. This is where \.{GFtype} starts,
+and where it ends.
+
+@p begin initialize; {get all variables initialized}
+dialog; {set up all the options}
+@<Process the preamble@>;
+@<Translate all the characters@>;
+print_nl;
+read_postamble;
+print('The file had ',total_chars:1,' character');
+if total_chars<>1 then print('s');
+print(' altogether.');
+@.The file had n characters...@>
+final_end:end.
+
+@ The main program needs a few global variables in order to do its work.
+
+@<Glob...@>=
+@!a:integer; {byte number of the current command}
+@!b,@!c,@!l,@!o,@!p,@!q,@!r:integer; {general purpose registers}
+
+@ \.{GFtype} looks at the preamble in order to do error checking, and to
+display the introductory comment.
+
+@<Process the preamble@>=
+open_gf_file;
+o:=get_byte; {fetch the first byte}
+if o<>pre then bad_gf('First byte isn''t start of preamble!');
+@.First byte isn't...@>
+o:=get_byte; {fetch the identification byte}
+if o<>gf_id_byte then
+ bad_gf('identification byte should be ',gf_id_byte:1,
+ ' not ',o:1);
+@.identification byte should be n@>
+o:=get_byte; {fetch the length of the introductory comment}
+print('''');
+while o>0 do
+ begin decr(o); print(xchr[get_byte]);
+ end;
+print_ln('''');
+
+@ @<Translate all...@>=
+repeat gf_prev_ptr:=cur_loc;
+ @<Pass |no_op|, |xxx| and |yyy| commands@>;
+ if o<>post then
+ begin if o<>boc then if o<>boc1 then
+ bad_gf('byte ',cur_loc-1:1,' is not boc (',o:1,')');
+@.byte n is not boc@>
+ print_nl; print(cur_loc-1:1,': beginning of char ');
+ @<Pass a |boc| command@>;
+ if not do_char then bad_gf('char ended unexpectedly');
+@.char ended unexpectedly@>
+ max_n_observed:=n;
+ if wants_pixels then @<Print the image@>;
+ @<Pass an |eoc| command@>;
+ end;
+until o=post;
+
+@ @<Pass |no_op|, |xxx| and |yyy| commands@>=
+repeat start_op;
+ if o=yyy then
+ begin @<Translate a |yyy|...@>; o:=no_op;
+ end
+ else if (o>=xxx1) and (o<=xxx1+3) then
+ begin @<Translate an |xxx|...@>; o:=no_op;
+ end
+ else if o=no_op then show_mnemonic('no op');
+until o<>no_op;
+
+@ @<Pass a |boc|...@>=
+a:=cur_loc-1; incr(total_chars);
+if o=boc then
+ begin character_code:=signed_quad;
+ p:=signed_quad;
+ c:=character_code mod 256;
+ if c<0 then c:=c+256;
+ min_m_stated:=signed_quad; max_m_stated:=signed_quad;
+ min_n_stated:=signed_quad; max_n_stated:=signed_quad;
+ end
+else begin character_code:=get_byte; p:=-1; c:=character_code;
+ q:=get_byte; max_m_stated:=get_byte; min_m_stated:=max_m_stated-q;
+ q:=get_byte; max_n_stated:=get_byte; min_n_stated:=max_n_stated-q;
+ end;
+print(c:1);
+if character_code<>c then
+ print(' with extension ',(character_code-c) div 256 : 1);
+if wants_mnemonics then
+ print_ln(': ',min_m_stated:1,'<=m<=',max_m_stated:1,' ',
+ min_n_stated:1,'<=n<=',max_n_stated:1);
+max_m_observed:=-1;
+if char_ptr[c]<>p then
+ error('previous character pointer should be ',char_ptr[c]:1,
+ ', not ',p:1,'!')
+@.previous character...@>
+else if p>0 then if wants_mnemonics then
+ print_ln('(previous character with the same code started at byte ',
+ p:1,')');
+char_ptr[c]:=gf_prev_ptr;
+if wants_mnemonics then print('(initially n=',max_n_stated:1,')');
+if wants_pixels then @<Clear the image@>;
+m:=0; n:=0; paint_switch:=white;
+
+@ @<Pass an |eoc|...@>=
+max_m_observed:=min_m_stated+max_m_observed+1;
+n:=max_n_stated-max_n_observed; {now |n| is the minimum |n| observed}
+if min_m_stated<min_m_overall then min_m_overall:=min_m_stated;
+if max_m_observed>max_m_overall then max_m_overall:=max_m_observed;
+if n<min_n_overall then min_n_overall:=n;
+if max_n_stated>max_n_overall then max_n_overall:=max_n_stated;
+if max_m_observed>max_m_stated then
+ print_ln('The previous character should have had max m >= ',
+ max_m_observed:1,'!');
+@.previous character...@>
+if n<min_n_stated then
+ print_ln('The previous character should have had min n <= ',n:1,'!')
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtype} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/systems/knuth/dist/mfware/mft.web b/systems/knuth/dist/mfware/mft.web
new file mode 100644
index 0000000000..18a949b1e9
--- /dev/null
+++ b/systems/knuth/dist/mfware/mft.web
@@ -0,0 +1,1980 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Version 0.0 was more-or-less debugged on June 4, 1985.
+% Version 0.1 improved formatting of : and added \\ (June 15, 1985).
+% Version 0.2 improved formatting of good, fixed @@ bug (August 4, 1985).
+% Version 0.3 fixed minor bug in change_file move (August 30, 1985).
+% Version 0.4 fixed minor bug regarding empty comments (April 8, 1989).
+% Version 1.0 was tuned up for the METAFONTware report (April 16, 1989).
+% Version 1.1 ditto, with input handled by Hosek's idea (April 27, 1989).
+% Version 2 has the new primitives of METAFONT 2.0 (October 16, 1989).
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+\font\logo=manfnt % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\def\pb{$\.|\ldots\.|$} % MF brackets (|...|)
+\def\v{\.{\char'174}} % vertical (|) in typewriter font
+\def\dleft{[\![} \def\dright{]\!]} % double brackets
+\mathchardef\RA="3221 % right arrow
+\mathchardef\BA="3224 % double arrow
+\def\({} % kludge for alphabetizing certain module names
+\chardef\V=`\| % vertical line in a string
+
+\def\title{MFT}
+\def\contentspagenumber{401}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont MFT} processor}
+ \vskip 15pt
+ \centerline{(Version 2.0, October 1989)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926, MCS-8300984, and
+ CCR-8610181,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.
+ `{\logo hijklmnj}\kern1pt' is a trademark of Addison-Wesley
+ Publishing Company.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+This program converts a \MF\ source file to a \TeX\ file. It was written
+by D.~E. Knuth in June, 1985; a somewhat similar {\mc SAIL} program had
+@^Knuth, Donald Ervin@>
+been developed in January, 1980.
+
+The general idea is to input a file called, say, \.{foo.mf} and to produce an
+output file called, say, \.{foo.tex}. The latter file, when processed by \TeX,
+will yield a ``prettyprinted'' representation of the input file.
+@^user manual@>
+
+Line breaks in the input are carried over into the output; moreover,
+blank spaces at the beginning of a line are converted to quads of indentation
+in the output. Thus, the user has full control over the indentation and line
+breaks. Each line of input is translated independently of the others.
+
+A slight change to \MF's comment convention allows further control.
+Namely, `\.{\%\%}' indicates that the remainder of an input line should be
+copied verbatim to the output; this interrupts the translation and forces
+\.{MFT} to produce a certain result.
+
+Furthermore, `\.{\%\%\%} $\langle\,$token$_1\,\rangle\ldots
+\langle\,$token$_n\,\rangle$'
+introduces a change in \.{MFT}'s formatting rules; all tokens after the first
+will henceforth be translated according to the current conventions for
+$\langle\,$token$_1\,\rangle$. The tokens must be symbolic (i.e., not
+numeric or string tokens). For example, the input line
+$$\.{\%\%\% addto fill draw filldraw}$$
+says that the `\.{fill}', `\.{draw}', and `\.{filldraw}' operations of
+plain \MF\ should be formatted as the primitive token `\.{addto}', i.e.,
+in boldface type. (Without such reformatting commands, \.{MFT} would treat
+`\.{fill}' like an ordinary tag or variable name. In fact, you need
+a reformatting command even to get parentheses to act like delimiters!)
+
+\MF\ comments, which follow a single \.\% sign, should be valid \TeX\
+input. But \MF\ material can be included in \pb\ within a comment; this
+will be translated by \.{MFT} as if it were not in a comment. For example,
+a phrase like `\.{make} \.{\V x2r\V} \.{zero}' will be translated into
+`\.{make \$x\_\{2r\}\$ zero}'.
+
+The rules just stated apply to lines that contain one, two, or three \.\% signs
+in a row. Comments to \.{MFT} can follow `\.{\%\%\%\%}'.
+Five or more \.\% signs should not be used.
+
+Beside the normal input file, \.{MFT} also looks for a change file
+(e.g., `\.{foo.ch}'), which allows substitutions to be made in the
+translation. The change file follows the conventions of \.{WEB}, and
+it should be null if there are no changes. (Changes usually contain
+verbatim instructions to compensate for the fact that \.{MFT} cannot
+format everything in an optimum way.)
+
+There's also a third input file (e.g., `\.{plain.mft}'), which is
+input before the other two. This file normally contains the `\.{\%\%\%}'
+formatting commands that are necessary to tune \.{MFT} to a particular
+style of \MF\ code, so it is called the style file.
+
+The output of \.{MFT} should be accompanied by the macros in a small
+package called \.{mftmac.tex}.
+@.mftmac@>
+
+Caveat: This program is not as ``bulletproof'' as the other routines
+produced by Stanford's \TeX\ project. It takes care of a great deal of
+tedious formatting, but it can produce strange output, because \MF\ is
+an extremely general language. Users should proofread their output carefully.
+
+@ \.{MFT} uses a few features of the local \PASCAL\ compiler that may
+need to be changed in other installations:
+
+\yskip\item{1)} Case statements have a default.
+\item{2)} Input-output routines may need to be adapted for use with a particular
+character set and/or for printing messages on the user's terminal.
+
+\yskip\noindent
+These features are also present in the \PASCAL\ version of \TeX, where they
+are used in a similar (but more complex) way. System-dependent portions
+of \.{MFT} can be identified by looking at the entries for `system
+dependencies' in the index below.
+@!@^system dependencies@>
+
+The ``banner line'' defined here should be changed whenever \.{MFT}
+is modified.
+
+@d banner=='This is MFT, Version 2.0'
+
+@ The program begins with a fairly normal header, made up of pieces that
+@^system dependencies@>
+will mostly be filled in later. The \.{MF} input comes from files |mf_file|,
+|change_file|, and |style_file|; the \TeX\ output goes to file |tex_file|.
+
+If it is necessary to abort the job because of a fatal error, the program
+calls the `|jump_out|' procedure, which goes to the label |end_of_MFT|.
+
+@d end_of_MFT = 9999 {go here to wrap it up}
+
+@p @t\4@>@<Compiler directives@>@/
+program MFT(@!mf_file,@!change_file,@!style_file,@!tex_file);
+label end_of_MFT; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@<Error handling procedures@>@/
+procedure initialize;
+ var @<Local variables for initialization@>@/
+ begin @<Set initial values@>@/
+ end;
+
+@ The \PASCAL\ compiler used to develop this system has ``compiler
+directives'' that can appear in comments whose first character is a dollar sign.
+In our case these directives tell the compiler to detect
+@^system dependencies@>
+things that are out of range.
+
+@<Compiler directives@>=
+@{@&$C+,A+,D-@} {range check, catch arithmetic overflow, no debug overhead}
+
+@ Labels are given symbolic names by the following definitions. We insert
+the label `|exit|:' just before the `\ignorespaces|end|\unskip' of a
+procedure in which we have used the `|return|' statement defined below;
+the label `|restart|' is occasionally used at the very beginning of a
+procedure; and the label `|reswitch|' is occasionally used just prior to
+a \&{case} statement in which some cases change the conditions and we wish to
+branch to the newly applicable case.
+Loops that are set up with the \&{loop} construction defined below are
+commonly exited by going to `|done|' or to `|found|' or to `|not_found|',
+and they are sometimes repeated by going to `|continue|'.
+
+@d exit=10 {go here to leave a procedure}
+@d restart=20 {go here to start a procedure again}
+@d reswitch=21 {go here to start a case statement again}
+@d continue=22 {go here to resume a loop}
+@d done=30 {go here to exit a loop}
+@d found=31 {go here when you've found it}
+@d not_found=32 {go here when you've found something else}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
+@d do_nothing == {empty statement}
+@d return == goto exit {terminate a procedure call}
+@f return == nil
+@f loop == xclause
+
+@ We assume that |case| statements may include a default case that applies
+if no matching label is found. Thus, we shall use constructions like
+@^system dependencies@>
+$$\vbox{\halign{#\hfil\cr
+|case x of|\cr
+1: $\langle\,$code for $x=1\,\rangle$;\cr
+3: $\langle\,$code for $x=3\,\rangle$;\cr
+|othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr
+|endcases|\cr}}$$
+since most \PASCAL\ compilers have plugged this hole in the language by
+incorporating some sort of default mechanism. For example, the compiler
+used to develop \.{WEB} and \TeX\ allows `|others|:' as a default label,
+and other \PASCAL s allow syntaxes like `\ignorespaces|else|\unskip' or
+`\&{otherwise}' or `\\{otherwise}:', etc. The definitions of |othercases|
+and |endcases| should be changed to agree with local conventions.
+(Of course, if no default mechanism is available, the |case| statements of
+this program must be extended by listing all remaining cases.)
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The following parameters are set big enough to handle the Computer
+Modern fonts, so they should be sufficient for most applications of \.{MFT}.
+
+@<Constants...@>=
+@!max_bytes=10000; {the number of bytes in tokens; must be less than 65536}
+@!max_names=1000; {number of tokens}
+@!hash_size=353; {should be prime}
+@!buf_size=100; {maximum length of input line}
+@!line_length=80; {lines of \TeX\ output have at most this many characters,
+ should be less than 256}
+
+@ A global variable called |history| will contain one of four values
+at the end of every run: |spotless| means that no unusual messages were
+printed; |harmless_message| means that a message of possible interest
+was printed but no serious errors were detected; |error_message| means that
+at least one error was found; |fatal_message| means that the program
+terminated abnormally. The value of |history| does not influence the
+behavior of the program; it is simply computed for the convenience
+of systems that might want to use such information.
+
+@d spotless=0 {|history| value for normal jobs}
+@d harmless_message=1 {|history| value when non-serious info was printed}
+@d error_message=2 {|history| value when an error was noted}
+@d fatal_message=3 {|history| value when we had to stop prematurely}
+@#
+@d mark_harmless==@t@>@+if history=spotless then history:=harmless_message
+@d mark_error==history:=error_message
+@d mark_fatal==history:=fatal_message
+
+@<Glob...@>=@!history:spotless..fatal_message; {how bad was this run?}
+
+@ @<Set init...@>=history:=spotless;
+
+@* The character set.
+\.{MFT} works internally with ASCII codes, like all other programs
+associated with \TeX\ and \MF. The present section has been lifted
+almost verbatim from the \MF\ program.
+@^ASCII code@>
+
+@ Characters of text that have been converted to \MF's internal form
+are said to be of type |ASCII_code|, which is a subrange of the integers.
+
+@<Types...@>=
+@!ASCII_code=0..255; {eight-bit numbers}
+
+@ 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, especially in a program for font design;
+so the present specification of \.{MFT} has been written under the assumption
+that the \PASCAL\ compiler and run-time system permit the use of text files
+with more than 64 distinguishable characters. More precisely, we assume that
+the character set contains at least the letters and symbols associated
+with ASCII codes @'40 through @'176. If additional characters are present,
+\.{MFT} can be configured to work with them too.
+
+Since we are dealing with more characters than were present in the first
+\PASCAL\ compilers, we have to decide what to call the associated data
+type. Some \PASCAL s use the original name |char| for the
+characters in text files, even though there now are more than 64 such
+characters, while other \PASCAL s consider |char| to be a 64-element
+subrange of a larger data type that has some other name.
+
+In order to accommodate this difference, we shall use the name |text_char|
+to stand for the data type of the characters that are converted to and
+from |ASCII_code| when they are input and output. We shall also assume
+that |text_char| consists of the elements |chr(first_text_char)| through
+|chr(last_text_char)|, inclusive. The following definitions should be
+adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+
+@ @<Local variables for init...@>=
+@!i:0..255;
+
+@ The \.{MFT} processor converts 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.
+
+@<Glob...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+
+@ Since we are assuming that our \PASCAL\ system 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. On the other hand, it is possible to implement \.{MFT} with
+less complete character sets, and in such cases it will be necessary to
+change something here.
+@^system dependencies@>
+
+@<Set init...@>=
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';
+
+@ The ASCII code is ``standard'' only to a certain extent, since many
+computer installations have found it advantageous to have ready access
+to more than 94 printing characters. If \.{MFT} is being used
+on a garden-variety \PASCAL\ for which only standard ASCII
+codes will appear in the input and output files, it doesn't really matter
+what codes are specified in |xchr[0..@'37]|, but the safest policy is to
+blank everything out by using the code shown below.
+
+However, other settings of |xchr| will make \.{MFT} more friendly on
+computers that have an extended character set, so that users can type things
+like `\.^^Z' instead of `\.{<>}', and so that \.{MFT} can echo the
+page breaks found in its input. People with extended character sets can
+assign codes arbitrarily, giving an |xchr| equivalent to whatever
+characters the users of \.{MFT} are allowed to have in their input files.
+Appropriate changes to \.{MFT}'s |char_class| table should then be made.
+(Unlike \TeX, each installation of \MF\ has a fixed assignment of category
+codes, called the |char_class|.) Such changes make portability of programs
+more difficult, so they should be introduced cautiously if at all.
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]|
+where |i<j<@'177|, the value of |xord[xchr[i]]| will turn out to be
+|j| or more; hence, standard ASCII code numbers will be used instead of
+codes below @'40 in case there is a coincidence.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=@'177;
+for i:=@'200 to @'377 do xord[xchr[i]]:=i;
+for i:=1 to @'176 do xord[xchr[i]]:=i;
+
+@* Input and output.
+The I/O conventions of this program are essentially identical to those
+of \.{WEAVE}. Therefore people who need to make modifications should be
+able to do so without too many headaches.
+
+@ Terminal output is done by writing on file |term_out|, which is assumed to
+consist of characters of type |text_char|:
+@^system dependencies@>
+
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@d print_ln(#)==write_ln(term_out,#) {`|print|' and then start new line}
+@d new_line==write_ln(term_out) {start new line on the terminal}
+@d print_nl(#)== {print information starting on a new line}
+ begin new_line; print(#);
+ end
+
+@<Globals...@>=
+@!term_out:text_file; {the terminal as an output file}
+
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal. Here is one way to do this
+on the \PASCAL\ system that was used in \.{WEAVE}'s initial development:
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
+
+@ The |update_terminal| procedure is called when we want
+to make sure that everything we have output to the terminal so far has
+actually left the computer's internal buffers and been sent.
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+
+@ The main input comes from |mf_file|; this input may be overridden
+by changes in |change_file|. (If |change_file| is empty, there are no changes.)
+Furthermore the |style_file| is input first; it is unchangeable.
+
+@<Globals...@>=
+@!mf_file:text_file; {primary input}
+@!change_file:text_file; {updates}
+@!style_file:text_file; {formatting bootstrap}
+
+@ The following code opens the input files. Since these files were listed
+in the program header, we assume that the \PASCAL\ runtime system has
+already checked that suitable file names have been given; therefore no
+additional error checking needs to be done.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read the inputs}
+begin reset(mf_file); reset(change_file); reset(style_file);
+end;
+
+@ The main output goes to |tex_file|.
+
+@<Globals...@>=
+@!tex_file: text_file;
+
+@ The following code opens |tex_file|.
+Since this file was listed in the program header, we assume that the
+\PASCAL\ runtime system has checked that a suitable external file name has
+been given.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(tex_file);
+
+@ Input goes into an array called |buffer|.
+
+@<Globals...@>=@!buffer: array[0..buf_size] of ASCII_code;
+
+@ The |input_ln| procedure brings the next line of input from the specified
+file into the |buffer| array and returns the value |true|, unless the file has
+already been entirely read, in which case it returns |false|. The conventions
+of \TeX\ are followed; i.e., |ASCII_code| numbers representing the next line
+of the file are input into |buffer[0]|, |buffer[1]|, \dots,
+|buffer[limit-1]|; trailing blanks are ignored;
+and the global variable |limit| is set to the length of the
+@^system dependencies@>
+line. The value of |limit| must be strictly less than |buf_size|.
+
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+var final_limit:0..buf_size; {|limit| without trailing blanks}
+begin limit:=0; final_limit:=0;
+if eof(f) then input_ln:=false
+else begin while not eoln(f) do
+ begin buffer[limit]:=xord[f^]; get(f);
+ incr(limit);
+ if buffer[limit-1]<>" " then final_limit:=limit;
+ if limit=buf_size then
+ begin while not eoln(f) do get(f);
+ decr(limit); {keep |buffer[buf_size]| empty}
+ if final_limit>limit then final_limit:=limit;
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ end;
+ read_ln(f); limit:=final_limit; input_ln:=true;
+ end;
+end;
+
+@* Reporting errors to the user.
+The command `|err_print('! Error message')|' will report a syntax error to
+the user, by printing the error message at the beginning of a new line and
+then giving an indication of where the error was spotted in the source file.
+Note that no period follows the error message, since the error routine
+will automatically supply a period.
+
+The actual error indications are provided by a procedure called |error|.
+
+@d err_print(#)==
+ begin new_line; print(#); error;
+ end
+
+@<Error handling...@>=
+procedure error; {prints `\..' and location of error message}
+var@!k,@!l: 0..buf_size; {indices into |buffer|}
+begin @<Print error location based on input buffer@>;
+update_terminal; mark_error;
+end;
+
+@ The error locations can be indicated by using the global variables
+|loc|, |line|, |styling|, 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 |style_file| or |change_file| or |mf_file|.
+This routine should be modified on systems whose standard text editor
+has special line-numbering conventions.
+@^system dependencies@>
+
+@<Print error location based on input buffer@>=
+begin if styling then print('. (style file ')
+else if changing then print('. (change file ')@+else print('. (');
+print_ln('l.', line:1, ')');
+if loc>=limit then l:=limit else l:=loc;
+for k:=1 to l do
+ 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}
+end
+
+@ The |jump_out| procedure just cuts across all active procedure levels
+and jumps out of the program. This is the only non-local \&{goto} statement
+in \.{MFT}. It is used when no recovery from a particular error has
+been provided.
+
+Some \PASCAL\ compilers do not implement non-local |goto| statements.
+@^system dependencies@>
+In such cases the code that appears at label |end_of_MFT| should be
+copied into the |jump_out| procedure, followed by a call to a system procedure
+that terminates the program.
+
+@d fatal_error(#)==begin new_line; print(#); error; mark_fatal; jump_out;
+ end
+
+@<Error handling...@>=
+procedure jump_out;
+begin goto end_of_MFT;
+end;
+
+@ Sometimes the program's behavior is far different from what it should be,
+and \.{MFT} prints an error message that is really for the \.{MFT}
+maintenance person, not the user. In such cases the program says
+|confusion('indication of where we are')|.
+
+@d confusion(#)==fatal_error('! This can''t happen (',#,')')
+@.This can't happen@>
+
+@ An overflow stop occurs if \.{MFT}'s tables aren't large enough.
+
+@d overflow(#)==fatal_error('! Sorry, ',#,' capacity exceeded')
+@.Sorry, x capacity exceeded@>
+
+@* Inserting the changes.
+Let's turn now to the low-level routine |get_line|
+that takes care of merging |change_file| into |mf_file|. The |get_line|
+procedure also updates the line numbers for error messages.
+(This routine was copied from \.{WEAVE}, but updated to include |styling|.)
+
+@<Globals...@>=
+@!line:integer; {the number of the current line in the current file}
+@!other_line:integer; {the number of the current line in the input file that
+ is not currently being read}
+@!temp_line:integer; {used when interchanging |line| with |other_line|}
+@!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}
+@!changing: boolean; {if |true|, the current line is from |change_file|}
+@!styling: boolean; {if |true|, the current line is from |style_file|}
+
+@ As we change |changing| from |true| to |false| and back again, we must
+remember to swap the values of |line| and |other_line| so that the |err_print|
+routine will be sure to report the correct line number.
+
+@d change_changing==
+ changing := not changing;
+ temp_line:=other_line; other_line:=line; line:=temp_line
+ {|line @t$\null\BA\null$@> other_line|}
+
+@ 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 |mf_file|. After the change file has been completely input, we
+set |change_limit:=0|, so that no further matches will be made.
+
+@<Globals...@>=
+@!change_buffer:array[0..buf_size] of ASCII_code;
+@!change_limit:0..buf_size; {the last position occupied in |change_buffer|}
+
+@ Here's a simple function that checks if the two buffers are different.
+
+@p 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 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;
+exit: end;
+
+@ Procedure |prime_the_change_buffer| sets |change_buffer| in preparation
+for the next matching operation. Since blank lines in the change file are
+not used for matching, we have |(change_limit=0)and not changing| if and
+only if the change file is exhausted. This procedure is called only
+when |changing| is true; hence error messages will be reported correctly.
+
+@p 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@>;
+@<Skip to the next nonblank line; |return| if end of file@>;
+@<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>;
+exit: end;
+
+@ While looking for a line that begins with \.{@@x} in the change file,
+we allow lines that begin with \.{@@}, as long as they don't begin with
+\.{@@y} or \.{@@z} (which would probably indicate that the change file is
+fouled up).
+
+@<Skip over comment lines in the change file...@>=
+loop@+ begin incr(line);
+ if not input_ln(change_file) then return;
+ if limit<2 then goto continue;
+ if buffer[0]<>"@@" then goto continue;
+ if (buffer[1]>="X")and(buffer[1]<="Z") then
+ buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
+ if buffer[1]="x" then goto done;
+ if (buffer[1]="y")or(buffer[1]="z") then
+ begin loc:=2; err_print('! Where is the matching @@x?');
+@.Where is the match...@>
+ end;
+continue: end;
+done:
+
+@ Here we are looking at lines following the \.{@@x}.
+
+@<Skip to the next nonblank line...@>=
+repeat incr(line);
+ if not input_ln(change_file) then
+ begin err_print('! Change file ended after @@x');
+@.Change file ended...@>
+ return;
+ end;
+until limit>0;
+
+@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>=
+begin change_limit:=limit;
+if limit>0 then for k:=0 to limit-1 do change_buffer[k]:=buffer[k];
+end
+
+@ 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 \.{@@y} 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|.
+
+@p procedure check_change; {switches to |change_file| if the buffers match}
+label exit;
+var n:integer; {the number of discrepancies found}
+@!k:0..buf_size; {index into the buffers}
+begin if lines_dont_match then return;
+n:=0;
+loop@+ begin change_changing; {now it's |true|}
+ incr(line);
+ if not input_ln(change_file) then
+ begin err_print('! Change file ended before @@y');
+@.Change file ended...@>
+ change_limit:=0; change_changing; {|false| again}
+ return;
+ end;
+ @<If the current line starts with \.{@@y},
+ report any discrepancies and |return|@>;
+ @<Move |buffer| and |limit|...@>;
+ change_changing; {now it's |false|}
+ incr(line);
+ if not input_ln(mf_file) then
+ begin err_print('! MF file ended during a change');
+@.MF file ended...@>
+ input_has_ended:=true; return;
+ end;
+ if lines_dont_match then incr(n);
+ end;
+exit: end;
+
+@ @<If the current line starts with \.{@@y}...@>=
+if limit>1 then if buffer[0]="@@" then
+ begin if (buffer[1]>="X")and(buffer[1]<="Z") then
+ buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
+ if (buffer[1]="x")or(buffer[1]="z") then
+ begin loc:=2; err_print('! Where is the matching @@y?');
+@.Where is the match...@>
+ end
+ else if buffer[1]="y" then
+ begin if n>0 then
+ begin loc:=2; err_print('! Hmm... ',n:1,
+ ' of the preceding lines failed to match');
+@.Hmm... n of the preceding...@>
+ end;
+ return;
+ end;
+ end
+
+@ Here's what we do to get the input rolling.
+
+@<Initialize the input system@>=
+begin open_input; line:=0; other_line:=0;@/
+changing:=true; prime_the_change_buffer; change_changing;@/
+styling:=true; limit:=0; loc:=1; buffer[0]:=" "; input_has_ended:=false;
+end
+
+@ The |get_line| procedure is called when |loc>limit|; it puts the next
+line of merged input into the buffer and updates the other variables
+appropriately.
+
+@p procedure get_line; {inputs the next line}
+label restart;
+begin restart: if styling then
+ @<Read from |style_file| and maybe turn off |styling|@>;
+if not styling then
+ begin if changing then
+ @<Read from |change_file| and maybe turn off |changing|@>;
+ if not changing then
+ begin @<Read from |mf_file| and maybe turn on |changing|@>;
+ if changing then goto restart;
+ end;
+ end;
+end;
+
+@ @<Read from |mf_file|...@>=
+begin incr(line);
+if not input_ln(mf_file) then input_has_ended:=true
+else if limit=change_limit then
+ if buffer[0]=change_buffer[0] then
+ if change_limit>0 then check_change;
+end
+
+@ @<Read from |style_file|...@>=
+begin incr(line);
+if not input_ln(style_file) then
+ begin styling:=false; line:=0;
+ end;
+end
+
+@ @<Read from |change_file|...@>=
+begin incr(line);
+if not input_ln(change_file) then
+ begin err_print('! Change file ended without @@z');
+@.Change file ended...@>
+ buffer[0]:="@@"; buffer[1]:="z"; limit:=2;
+ end;
+if limit>1 then {check if the change has ended}
+ if buffer[0]="@@" then
+ begin if (buffer[1]>="X")and(buffer[1]<="Z") then
+ buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
+ if (buffer[1]="x")or(buffer[1]="y") then
+ begin loc:=2; err_print('! Where is the matching @@z?');
+@.Where is the match...@>
+ end
+ else if buffer[1]="z" then
+ begin prime_the_change_buffer; change_changing;
+ end;
+ end;
+end
+
+@ 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 |mf_file|.
+
+@<Check that all changes have been read@>=
+if change_limit<>0 then {|changing| is false}
+ begin for loc:=0 to change_limit do buffer[loc]:=change_buffer[loc];
+ limit:=change_limit; changing:=true; line:=other_line; loc:=change_limit;
+ err_print('! Change file entry did not match');
+@.Change file entry did not match@>
+ end
+
+@* Data structures.
+\.{MFT} puts token names
+into the large |byte_mem| array, which is packed with eight-bit integers.
+Allocation is sequential, since names are never deleted.
+
+An auxiliary array |byte_start| is used as a directory for |byte_mem|;
+the |link| and |ilk| arrays give further information about names.
+These auxiliary arrays consist of sixteen-bit items.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!sixteen_bits=0..65535; {unsigned two-byte quantity}
+
+@ \.{MFT} has been designed to avoid the need for indices that are more
+than sixteen bits wide, so that it can be used on most computers.
+
+@<Globals...@>=
+@!byte_mem: packed array [0..max_bytes] of ASCII_code; {characters of names}
+@!byte_start: array [0..max_names] of sixteen_bits; {directory into |byte_mem|}
+@!link: array [0..max_names] of sixteen_bits; {hash table links}
+@!ilk: array [0..max_names] of sixteen_bits; {type codes}
+
+@ The names of tokens are found by computing a hash address |h| and
+then looking at strings of bytes signified by |hash[h]|, |link[hash[h]]|,
+|link[link[hash[h]]]|, \dots, until either finding the desired name
+or encountering a zero.
+
+A `|name_pointer|' variable, which signifies a name, is an index into
+|byte_start|. The actual sequence of characters in the name pointed to by
+|p| appears in positions |byte_start[p]| to |byte_start[p+1]-1|, inclusive,
+of |byte_mem|.
+
+We usually have |byte_start[name_ptr]=byte_ptr|, which is
+the starting position for the next name to be stored in |byte_mem|.
+
+@d length(#)==byte_start[#+1]-byte_start[#] {the length of a name}
+
+@<Types...@>=
+@!name_pointer=0..max_names; {identifies a name}
+
+@ @<Global...@>=
+@!name_ptr:name_pointer; {first unused position in |byte_start|}
+@!byte_ptr:0..max_bytes; {first unused position in |byte_mem|}
+
+@ @<Set init...@>=
+byte_start[0]:=0; byte_ptr:=0;
+byte_start[1]:=0; {this makes name 0 of length zero}
+name_ptr:=1;
+
+@ The hash table described above is updated by the |lookup| procedure,
+which finds a given name and returns a pointer to its index in
+|byte_start|. The token is supposed to match character by character.
+If it was not already present, it is inserted into the table.
+
+Because of the way \.{MFT}'s scanning mechanism works, it is most convenient
+to let |lookup| search for a token that is present in the |buffer|
+array. Two other global variables specify its position in the buffer: the
+first character is |buffer[id_first]|, and the last is |buffer[id_loc-1]|.
+
+@<Glob...@>=
+@!id_first:0..buf_size; {where the current token begins in the buffer}
+@!id_loc:0..buf_size; {just after the current token in the buffer}
+@#
+@!hash:array [0..hash_size] of sixteen_bits; {heads of hash lists}
+
+@ Initially all the hash lists are empty.
+
+@<Local variables for init...@>=
+@!h:0..hash_size; {index into hash-head array}
+
+@ @<Set init...@>=
+for h:=0 to hash_size-1 do hash[h]:=0;
+
+@ Here now is the main procedure for finding tokens.
+
+@p function lookup:name_pointer; {finds current token}
+label found;
+var i:0..buf_size; {index into |buffer|}
+@!h:0..hash_size; {hash code}
+@!k:0..max_bytes; {index into |byte_mem|}
+@!l:0..buf_size; {length of the given token}
+@!p:name_pointer; {where the token is being sought}
+begin l:=id_loc-id_first; {compute the length}
+@<Compute the hash code |h|@>;
+@<Compute the name location |p|@>;
+if p=name_ptr then @<Enter a new name into the table at position |p|@>;
+lookup:=p;
+end;
+
+@ A simple hash code is used: If the sequence of
+ASCII codes is $c_1c_2\ldots c_m$, its hash value will be
+$$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,|hash_size|.$$
+
+@<Compute the hash...@>=
+h:=buffer[id_first]; i:=id_first+1;
+while i<id_loc do
+ begin h:=(h+h+buffer[i]) mod hash_size; incr(i);
+ end
+
+@ If the token is new, it will be placed in position |p=name_ptr|,
+otherwise |p| will point to its existing location.
+
+@<Compute the name location...@>=
+p:=hash[h];
+while p<>0 do
+ begin if length(p)=l then
+ @<Compare name |p| with current token,
+ |goto found| if equal@>;
+ p:=link[p];
+ end;
+p:=name_ptr; {the current token is new}
+link[p]:=hash[h]; hash[h]:=p; {insert |p| at beginning of hash list}
+found:
+
+@ @<Compare name |p|...@>=
+begin i:=id_first; k:=byte_start[p];
+while (i<id_loc)and(buffer[i]=byte_mem[k]) do
+ begin incr(i); incr(k);
+ end;
+if i=id_loc then goto found; {all characters agree}
+end
+
+@ When we begin the following segment of the program, |p=name_ptr|.
+
+@<Enter a new name...@>=
+begin if byte_ptr+l>max_bytes then overflow('byte memory');
+if name_ptr+1>max_names then overflow('name');
+i:=id_first; {get ready to move the token into |byte_mem|}
+while i<id_loc do
+ begin byte_mem[byte_ptr]:=buffer[i]; incr(byte_ptr); incr(i);
+ end;
+incr(name_ptr); byte_start[name_ptr]:=byte_ptr;
+@<Assign the default value to |ilk[p]|@>;
+end
+
+@* Initializing the primitive tokens.
+Each token read by \.{MFT} is recognized as belonging to one of the
+following ``types'':
+
+@d indentation=0 {internal code for space at beginning of a line}
+@d end_of_line=1 {internal code for hypothetical token at end of a line}
+@d end_of_file=2 {internal code for hypothetical token at end of the input}
+@d verbatim=3 {internal code for the token `\.{\%\%}'}
+@d set_format=4 {internal code for the token `\.{\%\%\%}'}
+@d mft_comment=5 {internal code for the token `\.{\%\%\%\%}'}
+@d min_action_type=6 {smallest code for tokens that produce ``real'' output}
+@d numeric_token=6 {internal code for tokens like `\.{3.14159}'}
+@d string_token=7 {internal code for tokens like `|"pie"|'}
+@d min_symbolic_token=8 {smallest internal code for a symbolic token}
+@d op=8 {internal code for tokens like `\.{sqrt}'}
+@d command=9 {internal code for tokens like `\.{addto}'}
+@d endit=10 {internal code for tokens like `\.{fi}'}
+@d binary=11 {internal code for tokens like `\.{and}'}
+@d abinary=12 {internal code for tokens like `\.{+}'}
+@d bbinary=13 {internal code for tokens like `\.{step}'}
+@d ampersand=14 {internal code for the token `\.{\char`\&}'}
+@d pyth_sub=15 {internal code for the token `\.{+-+}'}
+@d as_is=16 {internal code for tokens like `\.{]}'}
+@d bold=17 {internal code for tokens like `\.{nullpen}'}
+@d type_name=18 {internal code for tokens like `\.{numeric}'}
+@d path_join=19 {internal code for the token `\.{..}'}
+@d colon=20 {internal code for the token `\.:'}
+@d semicolon=21 {internal code for the token `\.;'}
+@d backslash=22 {internal code for the token `\.{\\}'}
+@d double_back=23 {internal code for the token `\.{\\\\}'}
+@d less_or_equal=24 {internal code for the token `\.{<=}'}
+@d greater_or_equal=25 {internal code for the token `\.{>=}'}
+@d not_equal=26 {internal code for the token `\.{<>}'}
+@d sharp=27 {internal code for the token `\.{\char`\#}'}
+@d comment=28 {internal code for the token `\.{\char`\%}'}
+@d recomment=29 {internal code used to resume a comment after `\pb'}
+@d min_suffix=30 {smallest code for symbolic tokens in suffixes}
+@d internal=30 {internal code for tokens like `\.{pausing}'}
+@d input_command=31 {internal code for tokens like `\.{input}'}
+@d special_tag=32 {internal code for tags that take at most one subscript}
+@d tag=33 {internal code for nonprimitive tokens}
+
+@<Assign the default value to |ilk[p]|@>=ilk[p]:=tag
+
+@ We have to get \MF's primitives into the hash table, and the
+simplest way to do this is to insert them every time \.{MFT} is run.
+
+A few macros permit us to do the initialization with a compact program.
+We use the fact that the longest primitive is \.{intersectiontimes},
+which is 17 letters long.
+
+@d spr17(#)==buffer[17]:=#;cur_tok:=lookup;ilk[cur_tok]:=
+@d spr16(#)==buffer[16]:=#;spr17
+@d spr15(#)==buffer[15]:=#;spr16
+@d spr14(#)==buffer[14]:=#;spr15
+@d spr13(#)==buffer[13]:=#;spr14
+@d spr12(#)==buffer[12]:=#;spr13
+@d spr11(#)==buffer[11]:=#;spr12
+@d spr10(#)==buffer[10]:=#;spr11
+@d spr9(#)==buffer[9]:=#;spr10
+@d spr8(#)==buffer[8]:=#;spr9
+@d spr7(#)==buffer[7]:=#;spr8
+@d spr6(#)==buffer[6]:=#;spr7
+@d spr5(#)==buffer[5]:=#;spr6
+@d spr4(#)==buffer[4]:=#;spr5
+@d spr3(#)==buffer[3]:=#;spr4
+@d spr2(#)==buffer[2]:=#;spr3
+@d spr1(#)==buffer[1]:=#;spr2
+@d pr1==id_first:=17; spr17
+@d pr2==id_first:=16; spr16
+@d pr3==id_first:=15; spr15
+@d pr4==id_first:=14; spr14
+@d pr5==id_first:=13; spr13
+@d pr6==id_first:=12; spr12
+@d pr7==id_first:=11; spr11
+@d pr8==id_first:=10; spr10
+@d pr9==id_first:=9; spr9
+@d pr10==id_first:=8; spr8
+@d pr11==id_first:=7; spr7
+@d pr12==id_first:=6; spr6
+@d pr13==id_first:=5; spr5
+@d pr14==id_first:=4; spr4
+@d pr15==id_first:=3; spr3
+@d pr16==id_first:=2; spr2
+@d pr17==id_first:=1; spr1
+
+@ The intended use of the macros above might not be immediately obvious,
+but the riddle is answered by the following:
+
+@<Store all the primitives@>=
+id_loc:=18;@/
+pr2(".")(".")(path_join);@/
+pr1("[")(as_is);@/
+pr1("]")(as_is);@/
+pr1("}")(as_is);@/
+pr1("{")(as_is);@/
+pr1(":")(colon);@/
+pr2(":")(":")(colon);@/
+pr3("|")("|")(":")(colon);@/
+pr2(":")("=")(as_is);@/
+pr1(",")(as_is);@/
+pr1(";")(semicolon);@/
+pr1("\")(backslash);@/
+pr2("\")("\")(double_back);@/
+pr5("a")("d")("d")("t")("o")(command);@/
+pr2("a")("t")(bbinary);@/
+pr7("a")("t")("l")("e")("a")("s")("t")(op);@/
+pr10("b")("e")("g")("i")("n")("g")("r")("o")("u")("p")(command);
+pr8("c")("o")("n")("t")("r")("o")("l")("s")(op);@/
+pr4("c")("u")("l")("l")(command);@/
+pr4("c")("u")("r")("l")(op);@/
+pr10("d")("e")("l")("i")("m")("i")("t")("e")("r")("s")(command);@/
+pr7("d")("i")("s")("p")("l")("a")("y")(command);@/
+pr8("e")("n")("d")("g")("r")("o")("u")("p")(endit);@/
+pr8("e")("v")("e")("r")("y")("j")("o")("b")(command);@/
+pr6("e")("x")("i")("t")("i")("f")(command);@/
+pr11("e")("x")("p")("a")("n")("d")("a")("f")("t")("e")("r")(command);@/
+pr4("f")("r")("o")("m")(bbinary);@/
+pr8("i")("n")("w")("i")("n")("d")("o")("w")(bbinary);@/
+pr7("i")("n")("t")("e")("r")("i")("m")(command);@/
+pr3("l")("e")("t")(command);@/
+pr11("n")("e")("w")("i")("n")("t")("e")("r")("n")("a")("l")(command);@/
+pr2("o")("f")(command);@/
+pr10("o")("p")("e")("n")("w")("i")("n")("d")("o")("w")(command);@/
+pr10("r")("a")("n")("d")("o")("m")("s")("e")("e")("d")(command);@/
+pr4("s")("a")("v")("e")(command);@/
+pr10("s")("c")("a")("n")("t")("o")("k")("e")("n")("s")(command);@/
+pr7("s")("h")("i")("p")("o")("u")("t")(command);@/
+pr4("s")("t")("e")("p")(bbinary);@/
+pr3("s")("t")("r")(command);@/
+pr7("t")("e")("n")("s")("i")("o")("n")(op);@/
+pr2("t")("o")(bbinary);@/
+pr5("u")("n")("t")("i")("l")(bbinary);@/
+pr3("d")("e")("f")(command);@/
+pr6("v")("a")("r")("d")("e")("f")(command);@/
+
+@ (There are so many primitives, it's necessary to break this long
+initialization code up into pieces so as not to overflow \.{WEAVE}'s capacity.)
+
+@<Store all the primitives@>=
+pr10("p")("r")("i")("m")("a")("r")("y")("d")("e")("f")(command);@/
+pr12("s")("e")("c")("o")("n")("d")("a")("r")("y")("d")("e")("f")(command);@/
+pr11("t")("e")("r")("t")("i")("a")("r")("y")("d")("e")("f")(command);@/
+pr6("e")("n")("d")("d")("e")("f")(endit);@/
+pr3("f")("o")("r")(command);@/
+pr11("f")("o")("r")("s")("u")("f")("f")("i")("x")("e")("s")(command);@/
+pr7("f")("o")("r")("e")("v")("e")("r")(command);@/
+pr6("e")("n")("d")("f")("o")("r")(endit);@/
+pr5("q")("u")("o")("t")("e")(command);@/
+pr4("e")("x")("p")("r")(command);@/
+pr6("s")("u")("f")("f")("i")("x")(command);@/
+pr4("t")("e")("x")("t")(command);@/
+pr7("p")("r")("i")("m")("a")("r")("y")(command);@/
+pr9("s")("e")("c")("o")("n")("d")("a")("r")("y")(command);@/
+pr8("t")("e")("r")("t")("i")("a")("r")("y")(command);@/
+pr5("i")("n")("p")("u")("t")(input_command);@/
+pr8("e")("n")("d")("i")("n")("p")("u")("t")(bold);@/
+pr2("i")("f")(command);@/
+pr2("f")("i")(endit);@/
+pr4("e")("l")("s")("e")(command);@/
+pr6("e")("l")("s")("e")("i")("f")(command);@/
+pr4("t")("r")("u")("e")(bold);@/
+pr5("f")("a")("l")("s")("e")(bold);@/
+pr11("n")("u")("l")("l")("p")("i")("c")("t")("u")("r")("e")(bold);@/
+pr7("n")("u")("l")("l")("p")("e")("n")(bold);@/
+pr7("j")("o")("b")("n")("a")("m")("e")(bold);@/
+pr10("r")("e")("a")("d")("s")("t")("r")("i")("n")("g")(bold);@/
+pr9("p")("e")("n")("c")("i")("r")("c")("l")("e")(bold);@/
+pr4("g")("o")("o")("d")(special_tag);@/
+pr2("=")(":")(as_is);@/
+pr3("=")(":")("|")(as_is);@/
+pr4("=")(":")("|")(">")(as_is);@/
+pr3("|")("=")(":")(as_is);@/
+pr4("|")("=")(":")(">")(as_is);@/
+pr4("|")("=")(":")("|")(as_is);@/
+pr5("|")("=")(":")("|")(">")(as_is);@/
+pr6("|")("=")(":")("|")(">")(">")(as_is);@/
+pr4("k")("e")("r")("n")(binary);
+pr6("s")("k")("i")("p")("t")("o")(command);@/
+
+@ (Does anybody out there remember the commercials that went \.{LS-MFT}?)
+
+@<Store all the prim...@>=
+pr13("n")("o")("r")("m")("a")("l")("d")("e")("v")("i")("a")("t")("e")(op);@/
+pr3("o")("d")("d")(op);@/
+pr5("k")("n")("o")("w")("n")(op);@/
+pr7("u")("n")("k")("n")("o")("w")("n")(op);@/
+pr3("n")("o")("t")(op);@/
+pr7("d")("e")("c")("i")("m")("a")("l")(op);@/
+pr7("r")("e")("v")("e")("r")("s")("e")(op);@/
+pr8("m")("a")("k")("e")("p")("a")("t")("h")(op);@/
+pr7("m")("a")("k")("e")("p")("e")("n")(op);@/
+pr11("t")("o")("t")("a")("l")("w")("e")("i")("g")("h")("t")(op);@/
+pr3("o")("c")("t")(op);@/
+pr3("h")("e")("x")(op);@/
+pr5("A")("S")("C")("I")("I")(op);@/
+pr4("c")("h")("a")("r")(op);@/
+pr6("l")("e")("n")("g")("t")("h")(op);@/
+pr13("t")("u")("r")("n")("i")("n")("g")("n")("u")("m")("b")("e")("r")(op);@/
+pr5("x")("p")("a")("r")("t")(op);@/
+pr5("y")("p")("a")("r")("t")(op);@/
+pr6("x")("x")("p")("a")("r")("t")(op);@/
+pr6("x")("y")("p")("a")("r")("t")(op);@/
+pr6("y")("x")("p")("a")("r")("t")(op);@/
+pr6("y")("y")("p")("a")("r")("t")(op);@/
+pr4("s")("q")("r")("t")(op);@/
+pr4("m")("e")("x")("p")(op);@/
+pr4("m")("l")("o")("g")(op);@/
+pr4("s")("i")("n")("d")(op);@/
+pr4("c")("o")("s")("d")(op);@/
+pr5("f")("l")("o")("o")("r")(op);@/
+pr14("u")("n")("i")("f")("o")("r")("m")("d")("e")("v")("i")("a")("t")("e")(op);
+ @/
+pr10("c")("h")("a")("r")("e")("x")("i")("s")("t")("s")(op);@/
+pr5("a")("n")("g")("l")("e")(op);@/
+pr5("c")("y")("c")("l")("e")(op);@/
+
+@ (If you think this \.{WEB} code is ugly, you should see the Pascal code
+it produces.)
+
+@<Store all the primitives@>=
+pr13("t")("r")("a")("c")("i")("n")("g")
+ ("t")("i")("t")("l")("e")("s")(internal);@/
+pr16("t")("r")("a")("c")("i")("n")("g")
+ ("e")("q")("u")("a")("t")("i")("o")("n")("s")(internal);@/
+pr15("t")("r")("a")("c")("i")("n")("g")
+ ("c")("a")("p")("s")("u")("l")("e")("s")(internal);@/
+pr14("t")("r")("a")("c")("i")("n")("g")
+ ("c")("h")("o")("i")("c")("e")("s")(internal);@/
+pr12("t")("r")("a")("c")("i")("n")("g")
+ ("s")("p")("e")("c")("s")(internal);@/
+pr11("t")("r")("a")("c")("i")("n")("g")
+ ("p")("e")("n")("s")(internal);@/
+pr15("t")("r")("a")("c")("i")("n")("g")
+ ("c")("o")("m")("m")("a")("n")("d")("s")(internal);@/
+pr13("t")("r")("a")("c")("i")("n")("g")
+ ("m")("a")("c")("r")("o")("s")(internal);@/
+pr12("t")("r")("a")("c")("i")("n")("g")
+ ("e")("d")("g")("e")("s")(internal);@/
+pr13("t")("r")("a")("c")("i")("n")("g")
+ ("o")("u")("t")("p")("u")("t")(internal);@/
+pr12("t")("r")("a")("c")("i")("n")("g")
+ ("s")("t")("a")("t")("s")(internal);@/
+pr13("t")("r")("a")("c")("i")("n")("g")
+ ("o")("n")("l")("i")("n")("e")(internal);@/
+
+@ @<Store all the primitives@>=
+pr4("y")("e")("a")("r")(internal);@/
+pr5("m")("o")("n")("t")("h")(internal);@/
+pr3("d")("a")("y")(internal);@/
+pr4("t")("i")("m")("e")(internal);@/
+pr8("c")("h")("a")("r")("c")("o")("d")("e")(internal);@/
+pr7("c")("h")("a")("r")("f")("a")("m")(internal);@/
+pr6("c")("h")("a")("r")("w")("d")(internal);@/
+pr6("c")("h")("a")("r")("h")("t")(internal);@/
+pr6("c")("h")("a")("r")("d")("p")(internal);@/
+pr6("c")("h")("a")("r")("i")("c")(internal);@/
+pr6("c")("h")("a")("r")("d")("x")(internal);@/
+pr6("c")("h")("a")("r")("d")("y")(internal);@/
+pr10("d")("e")("s")("i")("g")("n")("s")("i")("z")("e")(internal);@/
+pr4("h")("p")("p")("p")(internal);@/
+pr4("v")("p")("p")("p")(internal);@/
+pr7("x")("o")("f")("f")("s")("e")("t")(internal);@/
+pr7("y")("o")("f")("f")("s")("e")("t")(internal);@/
+pr7("p")("a")("u")("s")("i")("n")("g")(internal);@/
+pr12("s")("h")("o")("w")
+ ("s")("t")("o")("p")("p")("i")("n")("g")(internal);@/
+pr10("f")("o")("n")("t")("m")("a")("k")("i")("n")("g")(internal);@/
+pr8("p")("r")("o")("o")("f")("i")("n")("g")(internal);@/
+pr9("s")("m")("o")("o")("t")("h")("i")("n")("g")(internal);@/
+pr12("a")("u")("t")("o")("r")("o")("u")("n")("d")("i")("n")("g")(internal);@/
+pr11("g")("r")("a")("n")("u")("l")("a")("r")("i")("t")("y")(internal);@/
+pr6("f")("i")("l")("l")("i")("n")(internal);@/
+pr12("t")("u")("r")("n")("i")("n")("g")("c")("h")("e")("c")("k")(internal);@/
+pr12("w")("a")("r")("n")("i")("n")("g")("c")("h")("e")("c")("k")(internal);@/
+pr12("b")("o")("u")("n")("d")("a")("r")("y")("c")("h")("a")("r")(internal);@/
+
+@ Still more.
+
+@<Store all the prim...@>=
+pr1("+")(abinary);@/
+pr1("-")(abinary);@/
+pr1("*")(abinary);@/
+pr1("/")(as_is);@/
+pr2("+")("+")(binary);@/
+pr3("+")("-")("+")(pyth_sub);@/
+pr3("a")("n")("d")(binary);@/
+pr2("o")("r")(binary);@/
+pr1("<")(as_is);@/
+pr2("<")("=")(less_or_equal);@/
+pr1(">")(as_is);@/
+pr2(">")("=")(greater_or_equal);@/
+pr1("=")(as_is);@/
+pr2("<")(">")(not_equal);@/
+pr9("s")("u")("b")("s")("t")("r")("i")("n")("g")(command);@/
+pr7("s")("u")("b")("p")("a")("t")("h")(command);@/
+pr13("d")("i")("r")("e")("c")("t")("i")("o")("n")@|
+ ("t")("i")("m")("e")(command);@/
+pr5("p")("o")("i")("n")("t")(command);@/
+pr10("p")("r")("e")("c")("o")("n")("t")("r")("o")("l")(command);@/
+pr11("p")("o")("s")("t")("c")("o")("n")("t")("r")("o")("l")(command);@/
+pr9("p")("e")("n")("o")("f")("f")("s")("e")("t")(command);@/
+pr1("&")(ampersand);@/
+pr7("r")("o")("t")("a")("t")("e")("d")(binary);@/
+pr7("s")("l")("a")("n")("t")("e")("d")(binary);@/
+pr6("s")("c")("a")("l")("e")("d")(binary);@/
+pr7("s")("h")("i")("f")("t")("e")("d")(binary);@/
+pr11("t")("r")("a")("n")("s")("f")("o")("r")("m")("e")("d")(binary);@/
+pr7("x")("s")("c")("a")("l")("e")("d")(binary);@/
+pr7("y")("s")("c")("a")("l")("e")("d")(binary);@/
+pr7("z")("s")("c")("a")("l")("e")("d")(binary);@/
+pr17("i")("n")("t")("e")("r")("s")("e")("c")("t")("i")("o")("n")@|
+ ("t")("i")("m")("e")("s")(binary);@/
+pr7("n")("u")("m")("e")("r")("i")("c")(type_name);@/
+pr6("s")("t")("r")("i")("n")("g")(type_name);@/
+pr7("b")("o")("o")("l")("e")("a")("n")(type_name);@/
+pr4("p")("a")("t")("h")(type_name);@/
+pr3("p")("e")("n")(type_name);@/
+pr7("p")("i")("c")("t")("u")("r")("e")(type_name);@/
+pr9("t")("r")("a")("n")("s")("f")("o")("r")("m")(type_name);@/
+pr4("p")("a")("i")("r")(type_name);@/
+
+@ At last we are done with the tedious initialization of primitives.
+
+@<Store all the prim...@>=
+pr3("e")("n")("d")(endit);@/
+pr4("d")("u")("m")("p")(endit);@/
+pr9("b")("a")("t")("c")("h")("m")("o")("d")("e")(bold);
+pr11("n")("o")("n")("s")("t")("o")("p")("m")("o")("d")("e")(bold);
+pr10("s")("c")("r")("o")("l")("l")("m")("o")("d")("e")(bold);
+pr13("e")("r")("r")("o")("r")("s")("t")("o")("p")@|
+ ("m")("o")("d")("e")(bold);
+pr5("i")("n")("n")("e")("r")(command);@/
+pr5("o")("u")("t")("e")("r")(command);@/
+pr9("s")("h")("o")("w")("t")("o")("k")("e")("n")(command);@/
+pr9("s")("h")("o")("w")("s")("t")("a")("t")("s")(bold);@/
+pr4("s")("h")("o")("w")(command);@/
+pr12("s")("h")("o")("w")("v")("a")("r")("i")("a")("b")("l")("e")(command);@/
+pr16("s")("h")("o")("w")@|
+ ("d")("e")("p")("e")("n")("d")("e")("n")("c")("i")("e")("s")(bold);@/
+pr7("c")("o")("n")("t")("o")("u")("r")(command);@/
+pr10("d")("o")("u")("b")("l")("e")("p")("a")("t")("h")(command);@/
+pr4("a")("l")("s")("o")(command);@/
+pr7("w")("i")("t")("h")("p")("e")("n")(command);@/
+pr10("w")("i")("t")("h")("w")("e")("i")("g")("h")("t")(command);@/
+pr8("d")("r")("o")("p")("p")("i")("n")("g")(command);@/
+pr7("k")("e")("e")("p")("i")("n")("g")(command);@/
+pr7("m")("e")("s")("s")("a")("g")("e")(command);@/
+pr10("e")("r")("r")("m")("e")("s")("s")("a")("g")("e")(command);@/
+pr7("e")("r")("r")("h")("e")("l")("p")(command);@/
+pr8("c")("h")("a")("r")("l")("i")("s")("t")(command);@/
+pr8("l")("i")("g")("t")("a")("b")("l")("e")(command);@/
+pr10("e")("x")("t")("e")("n")("s")("i")("b")("l")("e")(command);@/
+pr10("h")("e")("a")("d")("e")("r")("b")("y")("t")("e")(command);@/
+pr9("f")("o")("n")("t")("d")("i")("m")("e")("n")(command);@/
+pr7("s")("p")("e")("c")("i")("a")("l")(command);@/
+pr10("n")("u")("m")("s")("p")("e")("c")("i")("a")("l")(command);@/
+pr1("%")(comment);@/
+pr2("%")("%")(verbatim);@/
+pr3("%")("%")("%")(set_format);@/
+pr4("%")("%")("%")("%")(mft_comment);@/
+pr1("#")(sharp);@/
+
+@ We also want to store a few other strings of characters that are
+used in \.{MFT}'s translation to \TeX\ code.
+
+@d ttr1(#)==byte_mem[byte_ptr-1]:=#; cur_tok:=name_ptr;
+ incr(name_ptr); byte_start[name_ptr]:=byte_ptr
+@d ttr2(#)==byte_mem[byte_ptr-2]:=#; ttr1
+@d ttr3(#)==byte_mem[byte_ptr-3]:=#; ttr2
+@d ttr4(#)==byte_mem[byte_ptr-4]:=#; ttr3
+@d ttr5(#)==byte_mem[byte_ptr-5]:=#; ttr4
+@d tr1==incr(byte_ptr); ttr1
+@d tr2==byte_ptr:=byte_ptr+2; ttr2
+@d tr3==byte_ptr:=byte_ptr+3; ttr3
+@d tr4==byte_ptr:=byte_ptr+4; ttr4
+@d tr5==byte_ptr:=byte_ptr+5; ttr5
+
+@<Glob...@>=
+@!translation:array[ASCII_code] of name_pointer;
+@!i:ASCII_code; {index into |translation|}
+
+@ @<Store all the translations@>=
+for i:=0 to 255 do translation[i]:=0;
+tr2("\")("$"); translation["$"]:=cur_tok;@/
+tr2("\")("#"); translation["#"]:=cur_tok;@/
+tr2("\")("&"); translation["&"]:=cur_tok;@/
+tr2("\")("{"); translation["{"]:=cur_tok;@/
+tr2("\")("}"); translation["}"]:=cur_tok;@/
+tr2("\")("_"); translation["_"]:=cur_tok;@/
+tr2("\")("%"); translation["%"]:=cur_tok;@/
+tr4("\")("B")("S")(" "); translation["\"]:=cur_tok;@/
+tr4("\")("H")("A")(" "); translation["^"]:=cur_tok;@/
+tr4("\")("T")("I")(" "); translation["~"]:=cur_tok;@/
+tr5("\")("a")("s")("t")(" "); translation["*"]:=cur_tok;@/
+tr4("\")("A")("M")(" "); tr_amp:=cur_tok;@/
+@.\\AM, etc@>
+tr4("\")("B")("L")(" "); tr_skip:=cur_tok;@/
+tr4("\")("S")("H")(" "); tr_sharp:=cur_tok;@/
+tr4("\")("P")("S")(" "); tr_ps:=cur_tok;@/
+tr4("\")("l")("e")(" "); tr_le:=cur_tok;@/
+tr4("\")("g")("e")(" "); tr_ge:=cur_tok;@/
+tr4("\")("n")("e")(" "); tr_ne:=cur_tok;@/
+tr5("\")("q")("u")("a")("d"); tr_quad:=cur_tok;@/
+
+@ @<Glob...@>=
+@!tr_le,@!tr_ge,@!tr_ne,@!tr_amp,@!tr_sharp,@!tr_skip,@!tr_ps,
+ @!tr_quad:name_pointer; {special translations}
+
+@* Inputting the next token.
+\.{MFT}'s lexical scanning routine is called |get_next|. This procedure
+inputs the next token of \MF\ input and puts its encoded meaning into
+two global variables, |cur_type| and |cur_tok|.
+
+@<Glob...@>=
+@!cur_type:eight_bits; {type of token just scanned}
+@!cur_tok:integer; {hash table or buffer location}
+@!prev_type:eight_bits; {previous value of |cur_type|}
+@!prev_tok:integer; {previous value of |cur_tok|}
+
+@ @<Set init...@>=
+cur_type:=end_of_line; cur_tok:=0;
+
+@ Two global state variables affect the behavior of |get_next|: A space
+will be considered significant when |start_of_line| is |true|,
+and the buffer will be considered devoid of information when |empty_buffer|
+is |true|.
+
+@<Glob...@>=
+@!start_of_line:boolean; {has the current line had nothing but spaces so far?}
+@!empty_buffer:boolean; {is it time to input a new line?}
+
+@ The 256 |ASCII_code| characters are grouped into classes by means of
+the |char_class| table. Individual class numbers have no semantic
+or syntactic significance, expect in a few instances defined here.
+There's also |max_class|, which can be used as a basis for additional
+class numbers in nonstandard extensions of \MF.
+
+@d digit_class=0 {the class number of \.{0123456789}}
+@d period_class=1 {the class number of `\..'}
+@d space_class=2 {the class number of spaces and nonstandard characters}
+@d percent_class=3 {the class number of `\.\%'}
+@d string_class=4 {the class number of `\."'}
+@d right_paren_class=8 {the class number of `\.)'}
+@d isolated_classes==5,6,7,8 {characters that make length-one tokens only}
+@d letter_class=9 {letters and the underline character}
+@d left_bracket_class=17 {`\.['}
+@d right_bracket_class=18 {`\.]'}
+@d invalid_class=20 {bad character in the input}
+@d end_line_class=21 {end of an input line (\.{MFT} only)}
+@d max_class=21 {the largest class number}
+
+@<Glob...@>=
+@!char_class:array[ASCII_code] of 0..max_class; {the class numbers}
+
+@ If changes are made to accommodate non-ASCII character sets, they should be
+essentially the same in \.{MFT} as in \MF. However, \.{MFT} has an additional
+class number, the |end_line_class|, which is used only for the special
+character |carriage_return| that is placed at the end of the input buffer.
+@^character set dependencies@>
+@^system dependencies@>
+
+@d carriage_return=@'15 {special code placed in |buffer[limit]|}
+
+@<Set init...@>=
+for i:="0" to "9" do char_class[i]:=digit_class;
+char_class["."]:=period_class;
+char_class[" "]:=space_class;
+char_class["%"]:=percent_class;
+char_class[""""]:=string_class;@/
+char_class[","]:=5;
+char_class[";"]:=6;
+char_class["("]:=7;
+char_class[")"]:=right_paren_class;
+for i:="A" to "Z" do char_class[i]:=letter_class;
+for i:="a" to "z" do char_class[i]:=letter_class;
+char_class["_"]:=letter_class;@/
+char_class["<"]:=10;
+char_class["="]:=10;
+char_class[">"]:=10;
+char_class[":"]:=10;
+char_class["|"]:=10;@/
+char_class["`"]:=11;
+char_class["'"]:=11;@/
+char_class["+"]:=12;
+char_class["-"]:=12;@/
+char_class["/"]:=13;
+char_class["*"]:=13;
+char_class["\"]:=13;@/
+char_class["!"]:=14;
+char_class["?"]:=14;@/
+char_class["#"]:=15;
+char_class["&"]:=15;
+char_class["@@"]:=15;
+char_class["$"]:=15;@/
+char_class["^"]:=16;
+char_class["~"]:=16;@/
+char_class["["]:=left_bracket_class;
+char_class["]"]:=right_bracket_class;@/
+char_class["{"]:=19;
+char_class["}"]:=19;@/
+for i:=0 to " "-1 do char_class[i]:=invalid_class;
+char_class[carriage_return]:=end_line_class;@/
+for i:=127 to 255 do char_class[i]:=invalid_class;
+
+@ And now we're ready to take the plunge into |get_next| itself.
+
+@d switch=25 {a label in |get_next|}
+@d pass_digits=85 {another}
+@d pass_fraction=86 {and still another, although |goto| is considered harmful}
+
+@p procedure get_next; {sets |cur_type| and |cur_tok| to next token}
+label switch,pass_digits,pass_fraction,done,found,exit;
+var @!c:ASCII_code; {the current character in the buffer}
+@!class:ASCII_code; {its class number}
+begin prev_type:=cur_type; prev_tok:=cur_tok;
+if empty_buffer then
+ @<Bring in a new line of input; |return| if the file has ended@>;
+switch: c:=buffer[loc]; id_first:=loc; incr(loc); class:=char_class[c];
+@<Branch on the |class|, scan the token; |return| directly if the
+ token is special, or |goto found| if it needs to be looked up@>;
+found:id_loc:=loc; cur_tok:=lookup; cur_type:=ilk[cur_tok];
+exit:end;
+
+@ @d emit(#)==@t@>@+begin cur_type:=#; cur_tok:=id_first; return;@+end
+
+@<Branch on the |class|...@>=
+case class of
+digit_class:goto pass_digits;
+period_class:begin class:=char_class[buffer[loc]];
+ if class>period_class then goto switch {ignore isolated `\..'}
+ else if class<period_class then goto pass_fraction; {|class=digit_class|}
+ end;
+space_class:if start_of_line then emit(indentation)
+ else goto switch;
+end_line_class: emit(end_of_line);
+string_class:@<Get a string token and |return|@>;
+isolated_classes: goto found;
+invalid_class:@<Decry the invalid character and |goto switch|@>;
+othercases do_nothing {letters, etc.}
+endcases;@/
+while char_class[buffer[loc]]=class do incr(loc);
+goto found;
+pass_digits: while char_class[buffer[loc]]=digit_class do incr(loc);
+if buffer[loc]<>"." then goto done;
+if char_class[buffer[loc+1]]<>digit_class then goto done;
+incr(loc);
+pass_fraction:repeat incr(loc);
+until char_class[buffer[loc]]<>digit_class;
+done:emit(numeric_token)
+
+@ @<Get a string token and |return|@>=
+loop@+begin if buffer[loc]="""" then
+ begin incr(loc); emit(string_token);
+ end;
+ if loc=limit then @<Decry the missing string delimiter and |goto switch|@>;
+ incr(loc);
+ end
+
+@ @<Decry the missing string delimiter and |goto switch|@>=
+begin err_print('! Incomplete string will be ignored'); goto switch;
+@.Incomplete string...@>
+end
+
+@ @<Decry the invalid character and |goto switch|@>=
+begin err_print('! Invalid character will be ignored'); goto switch;
+@.Invalid character...@>
+end
+
+@ @<Bring in a new line of input; |return| if the file has ended@>=
+begin get_line;
+if input_has_ended then emit(end_of_file);
+buffer[limit]:=carriage_return; loc:=0; start_of_line:=true;
+empty_buffer:=false;
+end
+
+@* Low-level output routines.
+The \TeX\ output is supposed to appear in lines at most |line_length|
+characters long, so we place it into an output buffer. During the output
+process, |out_line| will hold the current line number of the line about to
+be output.
+
+@<Glo...@>=
+@!out_buf:array[0..line_length] of ASCII_code; {assembled characters}
+@!out_ptr:0..line_length; {number of characters in |out_buf|}
+@!out_line: integer; {coordinates of next line to be output}
+
+@ The |flush_buffer| routine empties the buffer up to a given breakpoint,
+and moves any remaining characters to the beginning of the next line.
+If the |per_cent| parameter is |true|, a |"%"| is appended to the line
+that is being output; in this case the breakpoint |b| should be strictly
+less than |line_length|. If the |per_cent| parameter is |false|,
+trailing blanks are suppressed.
+The characters emptied from the buffer form a new line of output.
+
+@p procedure flush_buffer(@!b:eight_bits;@!per_cent:boolean);
+ {outputs |out_buf[1..b]|, where |b<=out_ptr|}
+label done;
+var j,@!k:0..line_length;
+begin j:=b;
+if not per_cent then {remove trailing blanks}
+ loop@+ begin if j=0 then goto done;
+ if out_buf[j]<>" " then goto done;
+ decr(j);
+ end;
+done: for k:=1 to j do write(tex_file,xchr[out_buf[k]]);
+if per_cent then write(tex_file,xchr["%"]);
+write_ln(tex_file); incr(out_line);
+if b<out_ptr then for k:=b+1 to out_ptr do out_buf[k-b]:=out_buf[k];
+out_ptr:=out_ptr-b;
+end;
+
+@ \.{MFT} calls |flush_buffer(out_ptr,false)| before it has input
+anything. We initialize the output variables
+so that the first line of the output file will be `\.{\\input mftmac}'.
+@.\\input mftmac@>
+@.mftmac@>
+
+@<Set init...@>=
+out_ptr:=1; out_buf[1]:=" "; out_line:=1; write(tex_file,'\input mftmac');
+
+@ When we wish to append the character |c| to the output buffer, we write
+`$|out|(c)$'; this will cause the buffer to be emptied if it was already
+full. Similarly, `$|out2|(c_1)(c_2)$' appends a pair of characters.
+A line break will occur at a space or after a single-nonletter
+\TeX\ control sequence.
+
+@d oot(#)==@;@/
+ if out_ptr=line_length then break_out;
+ incr(out_ptr); out_buf[out_ptr]:=#;
+@d oot1(#)==oot(#)@+end
+@d oot2(#)==oot(#)@,oot1
+@d oot3(#)==oot(#)@,oot2
+@d oot4(#)==oot(#)@,oot3
+@d oot5(#)==oot(#)@,oot4
+@d out==@+begin oot1
+@d out2==@+begin oot2
+@d out3==@+begin oot3
+@d out4==@+begin oot4
+@d out5==@+begin oot5
+
+@ The |break_out| routine is called just before the output buffer is about
+to overflow. To make this routine a little faster, we initialize position
+0 of the output buffer to `\.\\'; this character isn't really output.
+
+@<Set init...@>=
+out_buf[0]:="\";
+
+@ A long line is broken at a blank space or just before a backslash that isn't
+preceded by another backslash. In the latter case, a |"%"| is output at
+the break. (This policy has a known bug, in the rare situation that the
+backslash was in a string constant that's being output ``verbatim.'')
+
+@p procedure break_out; {finds a way to break the output line}
+label exit;
+var k:0..line_length; {index into |out_buf|}
+@!d:ASCII_code; {character from the buffer}
+begin k:=out_ptr;
+loop@+ begin if k=0 then
+ @<Print warning message, break the line, |return|@>;
+ d:=out_buf[k];
+ if d=" " then
+ begin flush_buffer(k,false); return;
+ end;
+ if (d="\")and(out_buf[k-1]<>"\") then {in this case |k>1|}
+ begin flush_buffer(k-1,true); return;
+ end;
+ decr(k);
+ end;
+exit:end;
+
+@ We get to this module only in unusual cases that the entire output line
+consists of a string of backslashes followed by a string of nonblank
+non-backslashes. In such cases it is almost always safe to break the
+line by putting a |"%"| just before the last character.
+
+@<Print warning message...@>=
+begin print_nl('! Line had to be broken (output l.',out_line:1);
+@.Line had to be broken@>
+print_ln('):');
+for k:=1 to out_ptr-1 do print(xchr[out_buf[k]]);
+new_line; mark_harmless;
+flush_buffer(out_ptr-1,true); return;
+end
+
+@ To output a string of bytes from |byte_mem|, we call |out_str|.
+
+@p procedure out_str(@!p:name_pointer); {outputs a string}
+var @!k:0..max_bytes; {index into |byte_mem|}
+begin for k:=byte_start[p] to byte_start[p+1]-1 do out(byte_mem[k]);
+end;
+
+@ The |out_name| subroutine is used to output a symbolic token.
+Unusual characters are translated into forms that won't screw up.
+
+@p procedure out_name(@!p:name_pointer); {outputs a name}
+var @!k:0..max_bytes; {index into |byte_mem|}
+@!t:name_pointer; {translation of character being output, if any}
+begin for k:=byte_start[p] to byte_start[p+1]-1 do
+ begin t:=translation[byte_mem[k]];
+ if t=0 then out(byte_mem[k])
+ else out_str(t);
+ end;
+end;
+
+@ We often want to output a name after calling a numeric macro
+(e.g., `\.{\\1\{foo\}}').
+
+@p procedure out_mac_and_name(@!n:ASCII_code; @!p:name_pointer);
+begin out("\"); out(n);
+if length(p)=1 then out_name(p)
+else begin out("{"); out_name(p); out("}");
+ end;
+end;
+
+@ Here's a routine that simply copies from the input buffer to the output
+buffer.
+
+@p procedure copy(@!first_loc:integer); {output |buffer[first_loc..loc-1]|}
+var @!k:0..buf_size; {|buffer| location being copied}
+begin for k:=first_loc to loc-1 do out(buffer[k]);
+end;
+
+@* Translation.
+The main work of \.{MFT} is accomplished by a routine that translates
+the tokens, one by one, with a limited amount of lookahead/lookbehind.
+Automata theorists might loosely call this a ``finite state transducer,''
+because the flow of control is comparatively simple.
+
+@p procedure do_the_translation;
+label restart,reswitch,done,exit;
+var @!k:0..buf_size; {looks ahead in the buffer}
+@!t:integer; {type that spreads to new tokens}
+begin restart:if out_ptr>0 then flush_buffer(out_ptr,false);
+empty_buffer:=true;
+loop@+ begin get_next;
+ if start_of_line then @<Do special actions at the start of a line@>;
+ reswitch:case cur_type of
+ numeric_token:@<Translate a numeric token or a fraction@>;
+ string_token:@<Translate a string token@>;
+ indentation:out_str(tr_quad);
+ end_of_line,mft_comment:@<Wind up a line of translation and |goto restart|,
+ or finish a \pb\ segment and |goto reswitch|@>;
+ end_of_file:return;
+@t\4@> @<Cases that translate primitive tokens@>@;
+ comment,recomment:@<Translate a comment and |goto restart|,
+ unless there's a \pb\ segment@>;
+ verbatim:@<Copy the rest of the current input line to the output,
+ then |goto restart|@>;
+ set_format:@<Change the translation format of tokens,
+ and |goto restart| or |reswitch|@>;
+ internal,special_tag,tag:@<Translate a tag and possible subscript@>;
+ end; {all cases have been listed}
+ end;
+exit:end;
+
+@ @<Do special actions at the start of a line@>=
+if cur_type>=min_action_type then
+ begin out("$"); start_of_line:=false;
+ case cur_type of
+ endit:out2("\")("!");
+@.\\!@>
+ binary,abinary,bbinary,ampersand,pyth_sub:out2("{")("}");
+@.\{\}@>
+ othercases do_nothing
+ endcases;
+ end
+else if cur_type=end_of_line then
+ begin out_str(tr_skip); goto restart;
+ end
+else if cur_type=mft_comment then goto restart
+
+@ Let's start with some of the easier translations, so that the harder
+ones will also be easy when we get to them. A string like |"cat"|
+comes out `\.{\\7"cat"}'.
+
+@<Translate a string token@>=
+begin out2("\")("7"); copy(cur_tok);
+@.\\7@>
+end
+
+@ Similarly, the translation of `\.{sqrt}' is `\.{\\1\{sqrt\}}'.
+
+@<Cases that translate primitive tokens@>=
+op: out_mac_and_name("1",cur_tok);
+@.\\1@>
+command: out_mac_and_name("2",cur_tok);
+@.\\2@>
+type_name: if prev_type=command then out_mac_and_name("1",cur_tok)
+ else out_mac_and_name("2",cur_tok);
+endit: out_mac_and_name("3",cur_tok);
+@.\\3@>
+bbinary: out_mac_and_name("4",cur_tok);
+@.\\4@>
+bold: out_mac_and_name("5",cur_tok);
+@.\\5@>
+binary: out_mac_and_name("6",cur_tok);
+@.\\6@>
+path_join: out_mac_and_name("8",cur_tok);
+@.\\8@>
+colon: out_mac_and_name("?",cur_tok);
+@.\\?@>
+
+@ Here are a few more easy cases.
+
+@<Cases that translate primitive tokens@>=
+as_is,sharp,abinary: out_name(cur_tok);
+double_back: out2("\")(";");
+@.\\;@>
+semicolon: begin out_name(cur_tok); get_next;
+ if cur_type<>end_of_line then if cur_type<>endit then out2("\")(" ");
+@.\\\char32@>
+ goto reswitch;
+ end;
+
+@ Some of the primitives have a fixed output (independent of |cur_tok|):
+
+@<Cases that translate primitive tokens@>=
+backslash:out_str(translation["\"]);
+pyth_sub:out_str(tr_ps);
+less_or_equal:out_str(tr_le);
+greater_or_equal:out_str(tr_ge);
+not_equal:out_str(tr_ne);
+ampersand:out_str(tr_amp);
+
+@ The remaining primitive is slightly special.
+
+@<Cases that translate primitive tokens@>=
+input_command: begin out_mac_and_name("2",cur_tok);
+ out5("\")("h")("b")("o")("x");
+ @<Scan the file name and output it in \.{typewriter type}@>;
+ end;
+
+@ File names have different formats on different computers, so we don't scan
+them with |get_next|. Here we use
+a rule that probably covers most cases satisfactorily: We ignore leading
+blanks, then consider the file name to consist of all subsequent characters
+up to the first blank, semicolon, comment, or end-of-line.
+(A |carriage_return| appears at the end of the line.)
+
+@<Scan the file name and output it in \.{typewriter type}@>=
+while buffer[loc]=" " do incr(loc);
+out5("{")("\")("t")("t")(" ");
+while (buffer[loc]<>" ")and(buffer[loc]<>"%")and(buffer[loc]<>";")
+ and(loc<limit) do
+ begin out(buffer[loc]); incr(loc);
+ end;
+out("}")
+
+@ @<Translate a numeric token or a fraction@>=
+if buffer[loc]="/" then
+ if char_class[buffer[loc+1]]=digit_class then {it's a fraction}
+ begin out5("\")("f")("r")("a")("c"); copy(cur_tok); get_next;
+@.\\frac@>
+ out2("/")("{"); get_next; copy(cur_tok); out("}");
+ end
+ else copy(cur_tok)
+else copy(cur_tok)
+
+@ @<Translate a tag and possible subscript@>=
+begin if length(cur_tok)=1 then out_name(cur_tok)
+else out_mac_and_name("\",cur_tok);
+@.\\\\@>
+get_next;
+if byte_mem[byte_start[prev_tok]]="'" then goto reswitch;
+case prev_type of
+internal:begin if (cur_type=numeric_token)or(cur_type>=min_suffix) then
+ out2("\")(",");
+@.\\,@>
+ goto reswitch;
+ end;
+special_tag:if cur_type<min_suffix then goto reswitch
+ else begin out("."); cur_type:=internal; goto reswitch;
+@..@>
+ end;
+tag:begin if cur_type=tag then if byte_mem[byte_start[cur_tok]]="'" then
+ goto reswitch; {a sequence of primes goes on the main line}
+ if (cur_type=numeric_token)or(cur_type>=min_suffix) then
+ @<Translate a subscript@>
+ else if cur_type=sharp then out_str(tr_sharp)
+ else goto reswitch;
+ end;
+end; {there are no other cases}
+end
+
+@ @<Translate a subscript@>=
+begin out2("_")("{");
+loop@+ begin if cur_type>=min_suffix then out_name(cur_tok)
+ else copy(cur_tok);
+ if prev_type=special_tag then
+ begin get_next; goto done;
+ end;
+ get_next;
+ if cur_type<min_suffix then if cur_type<>numeric_token then goto done;
+ if cur_type=prev_type then
+ if cur_type=numeric_token then out2("\")(",")
+@.\\,@>
+ else if char_class[byte_mem[byte_start[cur_tok]]]=@|
+ char_class[byte_mem[byte_start[prev_tok]]] then
+ if byte_mem[byte_start[prev_tok]]<>"." then out(".")
+ else out2("\")(",");
+ end;
+done: out("}"); goto reswitch;
+end
+
+@ The tricky thing about comments is that they might contain \pb.
+We scan ahead for this, and replace the second `\.{\char'174}'
+by a |carriage_return|.
+
+@<Translate a comment and |goto restart|...@>=
+begin if cur_type=comment then out2("\")("9");
+@.\\9@>
+id_first:=loc;
+while (loc<limit)and(buffer[loc]<>"|") do incr(loc);
+copy(id_first);
+if loc<limit then
+ begin start_of_line:=true; incr(loc); k:=loc;
+ while (k<limit)and(buffer[k]<>"|") do incr(k);
+ buffer[k]:=carriage_return;
+ end
+else begin if out_buf[out_ptr]="\" then out(" ");
+ out4("\")("p")("a")("r"); goto restart;
+@.\\par@>
+ end;
+end
+
+@ @<Copy the rest of the current input line to the output...@>=
+begin id_first:=loc; loc:=limit; copy(id_first);
+if out_ptr=0 then
+ begin out_ptr:=1; out_buf[1]:=" ";
+ end;
+goto restart;
+end
+
+@ @<Wind up a line of translation...@>=
+begin out("$");
+if (loc<limit)and(cur_type=end_of_line) then
+ begin cur_type:=recomment; goto reswitch;
+ end
+else begin out4("\")("p")("a")("r"); goto restart;
+@.\\par@>
+ end;
+end
+
+@ @<Change the translation format...@>=
+begin start_of_line:=false; get_next; t:=cur_type;
+while cur_type>=min_symbolic_token do
+ begin get_next;
+ if cur_type>=min_symbolic_token then ilk[cur_tok]:=t;
+ end;
+if cur_type<>end_of_line then if cur_type<>mft_comment then
+ begin err_print('! Only symbolic tokens should appear after %%%');
+@.Only symbolic tokens...@>
+ goto reswitch;
+ end;
+empty_buffer:=true; goto restart;
+end
+
+@* The main program.
+Let's put it all together now: \.{MFT} starts and ends here.
+@^system dependencies@>
+
+@p begin initialize; {beginning of the main program}
+print_ln(banner); {print a ``banner line''}
+@<Store all the primitives@>;
+@<Store all the translations@>;
+@<Initialize the input...@>;
+do_the_translation;
+@<Check that all changes have been read@>;
+end_of_MFT:{here files should be closed if the operating system requires it}
+@<Print the job |history|@>;
+end.
+
+@ Some implementations may wish to pass the |history| value to the
+operating system so that it can be used to govern whether or not other
+programs are started. Here we simply report the history to the user.
+@^system dependencies@>
+
+@<Print the job |history|@>=
+case history of
+spotless: print_nl('(No errors were found.)');
+harmless_message: print_nl('(Did you see the warning message above?)');
+error_message: print_nl('(Pardon me, but I think I spotted something wrong.)');
+fatal_message: print_nl('(That was a fatal error, my friend.)');
+end {there are no other cases}
+
+@* System-dependent changes.
+This module should be replaced, if necessary, by changes to the program
+that are necessary to make \.{MFT} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous modules preserve the module numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new modules, can be inserted here; then only the index
+itself will get a new module number.
+@^system dependencies@>
+
+@* Index.
diff --git a/systems/knuth/dist/tex/glue.web b/systems/knuth/dist/tex/glue.web
new file mode 100644
index 0000000000..38a6d667c8
--- /dev/null
+++ b/systems/knuth/dist/tex/glue.web
@@ -0,0 +1,421 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% It was written on 18 Dec 1981 and revised on 24 May 1991.
+
+% Here is TeX material that gets inserted after \input webmac
+\def\PASCAL{Pascal}
+\font\eightrm=cmr8
+
+\def\title{GLUE}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont Fixed-Point Glue Setting}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 6in\baselineskip9pt
+ \vbox{\eightrm\baselineskip9pt\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-7921977 and MCS-7723728;
+ by Office of Naval Research grant N00014-81-K-0330;
+ and by the IBM Corporation. `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+
+@* Introduction.
+If \TeX\ is being implemented on a microcomputer that does 32-bit
+addition and subtraction, but with multiplication and division restricted to
+multipliers and divisors that are either powers of~2 or positive
+integers less than~$2^{15}$, it can still do the computations associated
+with the setting of glue in a suitable way. This program illustrates one
+solution to the problem.
+
+Another purpose of this program is to provide the first ``short'' example
+of the use of \.{WEB}.
+
+@ The program itself is written in standard \PASCAL. It begins with a
+normal program header, most of which will be filled in with other parts of this
+``web'' as we are ready to introduce them.
+@^program header@>
+
+@p program GLUE(@!input,@!output);
+ type @<Types in the outer block@>@;
+ var @<Globals in the outer block@>@;
+ procedure initialize; {this procedure gets things started}
+ var @<Local variables for initialization@>@;
+ begin @<Set initial values@>;
+ end;
+
+@ Here are two macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+
+@* The problem and a solution.
+We are concerned here with the ``setting of glue'' that occurs when a
+\TeX\ box is being packaged. Let $x_1$, \dots,~$x_n$ be integers whose sum
+$s=x_1+\cdots+x_n$ is positive, and let $t$ be another positive integer.
+These $x_i$ represent scaled amounts of glue in units of sp (scaled
+points), where one sp is $2^{-16}$ of a printer's point. The other
+quantity $t$ represents the total by which the glue should stretch or
+shrink. Following the conventions of \TeX82, we will assume that the
+integers we deal with are less than $2^{31}$ in absolute value.
+
+After the glue has been set, the actual amounts of incremental glue space
+(in~sp) will be the integers $f(x_1)$, \dots,~$f(x_n)$, where $f$ is a
+function that we wish to compute. We want $f(x)$ to be nearly proportional
+to~$x$, and we also want the sum $f(x_1)+\cdots+f(x_n)$ to be nearly
+equal to~$t$. If we were using floating-point arithmetic, we would simply
+compute $f(x)\equiv(t/s)\cdot x$ and hope for the best; but the goal here
+is to compute a suitable~$f$ using only the fixed-point arithmetic operations
+of a typical ``16-bit microcomputer.''
+
+The solution adopted here is to determine integers $a$, $b$, $c$ such that
+$$f(x)=\bigl\lfloor 2^{-b}c\lfloor 2^{-a}x\rfloor\bigr\rfloor$$
+if $x$ is nonnegative. Thus, we take $x$ and shift it right by $a$~bits,
+then multiply by~$c$ (which is $2^{15}$ or less), and shift the product
+right by $b$~bits. The quantities $a$, $b$, and~$c$ are to be chosen
+so that this calculation doesn't cause overflow and so that $f(x_1)+\cdots
++f(x_n)$ is reasonably close to~$t$.
+
+The following method is used to calculate $a$ and~$b$:
+Suppose $$y=\max_{1\le i\le n}\vert x_i\vert\,.$$
+Let $d$ and $e$ be the smallest integers such that $t<2^ds$ and $y<2^e$.
+Since $s$ and~$t$ are less than~$2^{31}$, we have $-30\le d\le31$ and
+$1\le e\le31$. An error message is given if $d+e\ge31$; in such a case
+some $x_m$ has $\vert x_m\vert\ge 2^{e-1}$ and we are trying to change
+$\vert x_m\vert$ to $\vert(t/s)x_m\vert\ge2^{d+e-2}\ge2^{30}$~sp, which
+\TeX\ does not permit. (Consider, for example, the ``worst case'' situation
+$x_1=2^{30}+1$, $x_2=-2^{30}$, $t=2^{31}-1$; surely we need not bother
+trying to accommodate such anomalous combinations of values.) On the other
+hand if $d+e\le31$, we set $a=e-16$ and $b=31-d-e$. Notice that this choice
+of~$a$ guarantees that $\lfloor2^{-a}\vert x_i\vert\rfloor<2^{16}$. We will
+choose~$c$ to be at most~$2^{15}$, so that the product will be less
+than~$2^{31}$.
+
+The computation of $c$ is the tricky part.
+@^hairy mathematics@>
+The ``ideal'' value for $c$ would be $\rho=2^{a+b}t/s$, since $f(x)$ should
+be approximately $(t/s)\cdot x$. Furthermore it is better to have $c$ slightly
+larger than~$\rho$, instead of slightly smaller, since the other operations
+in $f(x)$ have a downward bias. Therefore we shall compute $c=\lceil\rho\rceil$.
+Since $2^{a+b}t/s<2^{a+b+d}=2^{15}$, we have $c\le2^{15}$ as desired.
+
+We want to compute $c=\lceil\rho\rceil$ exactly in all cases. There is no
+difficulty if $s<2^{15}$, since $c$ can be computed directly using the
+formula $c=\bigl\lfloor(2^{a+b}t+s-1)/s\bigr\rfloor$; overflow will not
+occur since $2^{a+b}t<2^{15}s<2^{30}$.
+
+Otherwise let $s=s_12^l+s_2$, where $2^{14}\le s_1<2^{15}$ and $0\le s_2<2^l$.
+We will essentially carry out a long division. Let $t$ be ``normalized''
+so that $2^{30}\le2^ht<2^{31}$ for some~$h$. Then we form the quotient and
+remainder of $2^ht$ divided by~$s_1$,
+$$ 2^ht=qs_1+r_0, \qquad 0\le r_0<s_1.$$
+It follows that $2^{h+l}t-qs=2^lr_0-qs_2=r$, say. If $0\ge r>-s$ we have
+$q=\lceil2^{h+l}t/s\rceil$; otherwise we can replace $(q,r)$ by
+$(q\pm1,r\mp s)$ repeatedly until $r$ is in the correct range. It is not
+difficult to prove that $q$ needs to be increased at most once and decreased
+at most seven times, since $2^lr_0-qs_2<2^ls_1\le s$ and since
+$qs_2/s\le(2^ht/s_1)(s_2/2^ls_1)<2^{31}/s_1^2\le8$. Finally, we have
+$a+b-h-l=-1$ or~$-2$, since $2^{28+l}\le2^{14}s=2^{a+b+d-1}s\le2^{a+b}t<
+2^{a+b+d}s=2^{15}s<2^{30+l}$ and $2^{30}\le2^ht<2^{31}$. Hence
+$c=\lceil2^{a+b-h-l}q\rceil=\lceil{1\over2}q\rceil$ or~$\lceil{1\over4}q\rceil$.
+
+An error analysis shows that these values of $a$, $b$, and $c$ work
+satisfactorily, except in unusual cases where we wouldn't expect them to.
+@^error analysis@>
+When $x\ge0$ we have
+$$\eqalign{f(x)&=2^{-b}(2^{a+b}t/s+\theta_0)(2^{-a}x-\theta_1)-\theta_2\cr
+&=(t/s)x+\theta_02^{-a-b}x-\theta_12^at/s-2^{-b}\theta_0\theta_1-\theta_2\cr}$$
+where $0\le\theta_0,\theta_1,\theta_2<1$. Now $0\le\theta_02^{-a-b}x
+<2^{e-a-b}=2^{d+e-15}$ and $0\le\theta_12^at/s<2^{a+d}=2^{d+e-16}$, and
+the other two terms are negligible. Therefore $f(x_1)+\cdots+f(x_n)$ differs
+from~$t$ by at most about $2^{d+e-15}n$. Since $2^{d+e}$ is larger than
+$(t/s)y$, which is the largest stretching or shrinking of glue after expansion,
+the error is at worst about $n/32000$ times as much as this, so it is quite
+reasonable. For example, even if fill glue is being used to stretch
+20 inches, the error will still be less than $1\over1600$ of an inch.
+
+@ To sum up: Given the positive integers $s$, $t$, and $y$ as above, we
+set $$a\gets\lfloor\lg y\rfloor-15,\qquad b\gets29-\lfloor\lg y\rfloor-
+\lfloor\lg t/s\rfloor,\qquad\hbox{and}\qquad c\gets\lceil2^{a+b}t/s\rceil.$$
+The implementation below shows how to do the job in \PASCAL\ without using
+large numbers.
+
+@ \TeX\ wants to have the glue-setting information in a 32-bit data type
+called |glue_ratio|. The \PASCAL\ implementation of \TeX82 has |glue_ratio
+=real|, but alternative definitions of |glue_ratio| are explicitly allowed.
+
+For our purposes we shall let |glue_ratio| be a record that is packed with
+three fields: The |a_part| will hold the positive integer |a+16|, the
+|b_part| will hold the nonnegative integer~|b|, and the |c_part| will hold
+the nonnegative integer~|c|. When the formulas above tell us to take
+|b>30|, we might as well set |c:=0| instead, because |f(x)| will be
+zero in all cases when |b>30|. Note that we have only about 25 bits of
+information in all, so it should fit in 32 bits with ease.
+
+@<Types...@>=
+@!glue_ratio=packed record
+ @!a_part: 1..31; {the quantity |e=a+16| in our derivation}
+ @!b_part: 0..30; {the quantity |b| in our derivation}
+ @!c_part: 0..@'100000; {the quantity |c| in our derivation}
+ end;
+@!scaled = integer; {this data type is used for quantities in sp units}
+
+@ The real problem is to define the procedures that \TeX\ needs to
+deal with such |glue_ratio| values:
+(a)~Given scaled numbers |s|, |t|, and~|y| as above, to compute the
+corresponding |glue_ratio|.
+(b)~Given a nonnegative scaled number~|x| and a |glue_ratio|~|g|, to
+compute the scaled number~|f(x)|.
+(c)~Given a |glue_ratio|~|g|, to print out a decimal equivalent of
+|g| for diagnostic purposes.
+
+The procedures below can be incorporated into \TeX82 via a change file
+without great difficulty. A few modifications will be needed, because
+\TeX's |glue_ratio| values can be negative in unusual cases---when the
+amount of stretchability or shrinkability is less than zero. Negative
+values in the |c_part| will handle such problems, if proper care is
+taken. The error message below should either become a warning message
+or a call to \TeX's |print_err| routine; in the latter case, an
+@^error message@>
+appropriate help message should be given, stating that glue cannot
+stretch to more than 18~feet long, but that it's OK to proceed with
+fingers crossed.
+
+@*Glue multiplication.
+The easiest procedure of the three just mentioned is the one that is
+needed most often, namely, the computation of~|f(x)|.
+
+\PASCAL\ doesn't have built-in binary shift commands or built-in exponentiation,
+although many computers do have this capability. Therefore our arithmetic
+routines use an array called `|two_to_the|', containing powers of~two.
+Divisions by powers of two are never done in the programs below when the
+dividend is negative, so the operations can safely be replaced by right
+shifts on machines for which this is most appropriate. (Contrary to popular
+opinion, the operation `|x div 2|' is not the same as shifting |x|
+right one binary place, on a machine with two's complement arithmetic,
+when |x| is a negative odd integer. But division
+{\it is\/} equivalent to shifting when |x| is nonnegative.)
+
+@<Globals...@>=
+@!two_to_the: array[0..30] of integer; {$|two_to_the|[k]=2^k$}
+
+@ @<Local variables for init...@>=
+@!k:1..30; {an index for initializing |two_to_the|}
+
+@ @<Set init...@>=
+two_to_the[0]:=1;
+for k:=1 to 30 do two_to_the[k]:=two_to_the[k-1]+two_to_the[k-1];
+
+@ We will use the abbreviations |ga|, |gb|, and |gc| as convenient
+alternatives to \PASCAL's \&{with} statement. The glue-multiplication
+function |f|, which replaces several occurrences of the `|float|' macro
+in \TeX82, is now easy to state:
+
+@d ga==g.a_part
+@d gb==g.b_part
+@d gc==g.c_part
+
+@p function glue_mult(@!x:scaled;@!g:glue_ratio):integer;
+ {returns |f(x)| as above, assuming that |x>=0|}
+begin if ga>16 then x:=x div two_to_the[ga-16] {right shift by |a| places}
+else x:=x*two_to_the[16-ga]; {left shift by |-a| places}
+glue_mult:=(x*gc) div two_to_the[gb]; {right shift by |b| places}
+end; {note that |b| may be as large as 30}
+
+@*Glue setting.
+The |glue_fix| procedure computes |a|, |b|, and |c| by the method
+explained above. \TeX\ does not normally compute the quantity~|y|, but
+it could be made to do so without great difficulty.
+
+This procedure replaces several occurrences of the `|unfloat|' macro in
+\TeX82. It would be written as a function that returns a |glue_ratio|,
+if \PASCAL\ would allow functions to produce records as values.
+
+@p procedure glue_fix(@!s,@!t,@!y:scaled; var@!g:glue_ratio);
+var @!a,@!b,@!c:integer; {components of the desired ratio}
+@!k,@!h:integer; {$30-\lfloor\lg s\rfloor$, $30-\lfloor\lg t\rfloor$}
+@!s0:integer; {original (unnormalized) value of |s|}
+@!q,@!r,@!s1:integer; {quotient, remainder, divisor}
+@!w:integer; {$2^l$, where $l=16-k$}
+begin @<Normalize |s|, |t|, and |y|, computing |a|, |k|, and |h|@>;
+if t<s then b:=15-a-k+h@+else b:=14-a-k+h;
+if (b<0) or (b>30) then
+ begin if b<0 then write_ln('! Excessive glue.'); {error message}
+@^error message@>
+ b:=0; c:=0; {make |f(x)| identically zero}
+ end
+else begin if k>=16 then {easy case, $s_0<2^{15}$}
+ c:=(t div two_to_the[h-a-b]+s0-1) div s0 {here |1<=h-a-b<=k-14<=16|}
+ else @<Compute |c| by long division@>;
+ end;
+ga:=a+16; gb:=b; gc:=c;
+end;
+
+@ @<Normalize |s|, |t|, and |y|, computing |a|, |k|, and |h|@>=
+begin a:=15; k:=0; h:=0; s0:=s;
+while y<@'10000000000 do {|y| is known to be positive}
+ begin decr(a); y:=y+y;
+ end;
+while s<@'10000000000 do {|s| is known to be positive}
+ begin incr(k); s:=s+s;
+ end;
+while t<@'10000000000 do {|t| is known to be positive}
+ begin incr(h); t:=t+t;
+ end;
+end {now $2^{30}\le t=2^ht_0<2^{31}$ and $2^{30}\le s=2^ks_0<2^{31}$,
+ hence $d=k-h$ if $t/s<1$}
+
+@ @<Compute |c| by long division@>=
+begin w:=two_to_the[16-k];
+s1:=s0 div w;
+q:=t div s1;
+r:=((t mod s1)*w)-((s0 mod w)*q);
+if r>0 then
+ begin incr(q); r:=r-s0;
+ end
+else while r<=-s0 do
+ begin decr(q); r:=r+s0;
+ end;
+if a+b+k-h=15 then c:=(q+1) div 2 @+else c:=(q+3) div 4;
+end
+
+@*Glue-set printing.
+The last of the three procedures we need is |print_gr|, which displays a
+|glue_ratio| in symbolic decimal form. Before constructing such a procedure,
+we shall consider some simpler routines, copying them from an early
+draft of the program \TeX82.
+
+@d unity==@'200000 {$2^{16}$, represents 1.0000}
+
+@<Glob...@>=
+@!dig:array[0..15] of 0..9; {for storing digits}
+
+@ An array of digits is printed out by |print_digs|.
+
+@p procedure print_digs(@!k:integer); {prints |dig[k-1]| \dots |dig[0]|}
+begin while k>0 do
+ begin decr(k); write(chr(ord('0')+dig[k]));
+ end;
+end;
+
+@ A nonnegative integer is printed out by |print_int|.
+
+@p procedure print_int(@!n:integer); {prints an integer in decimal form}
+var @!k:0..12; {index to current digit; we assume that $0\le n<10^{12}$}
+begin k:=0;
+repeat dig[k]:=n mod 10; n:=n div 10; incr(k);
+until n=0;
+print_digs(k);
+end;
+
+@ And here is a procedure to print a nonnegative |scaled| number.
+
+@p procedure print_scaled(s:scaled);
+ {prints a scaled real, truncated to four digits}
+var k:0..3; {index to current digit of the fraction part}
+begin print_int(s div unity); {print the integer part}
+s:=((s mod unity)*10000) div unity;
+for k:=0 to 3 do
+ begin dig[k]:=s mod 10; s:=s div 10;
+ end;
+write('.'); print_digs(4);
+end;
+
+@ Now we're ready to print a |glue_ratio|. Since the effective multiplier
+is $2^{-a-b}c$, we will display the scaled integer $2^{16-a-b}c$, taking
+care to print something special if this quantity is terribly large.
+
+@p procedure print_gr(@!g:glue_ratio); {prints a glue multiplier}
+var @!j:-29..31; {the amount to shift |c|}
+begin j:=32-ga-gb;
+while j>15 do
+ begin write('2x'); decr(j); {indicate multiples of 2 for BIG cases}
+ end;
+if j<0 then print_scaled(gc div two_to_the[-j]) {shift right}
+else print_scaled(gc*two_to_the[j]); {shift left}
+end;
+
+@* The driver program.
+In order to test these routines, we will assume that the |input| file
+contains a sequence of test cases, where each test case consists of the
+integer numbers $t$, $x_1$, \dots,~$x_n$, 0. The final test case should
+be followed by an additional zero.
+
+@<Glob...@>=
+@!x:array[1..1000] of scaled; {the $x_i$}
+@!t:scaled; {the desired total}
+@!m:integer; {the test case number}
+
+@ Each case will be processed by the following routine, which assumes
+that |t| has already been read.
+
+@p procedure test; {processes the next data set, given |t| and~|m|}
+var @!n: 0..1000; {the number of items}
+k:0..1000; {runs through the items}
+y:scaled; {$\max_{1\le i\le n}\vert x_i\vert$}
+@!g:glue_ratio; {the computed glue multiplier}
+@!s:scaled; {the sum $x_1+\cdots+x_n$}
+@!ts:scaled; {the sum $f(x_1)+\cdots+f(x_n)$}
+begin write_ln('Test data set number ',m:1,':');
+@<Read $x_1,\ldots,x_n$@>;
+@<Compute |s| and |y|@>;
+if s<=0 then write_ln('Invalid data (nonpositive sum); this set rejected.')
+else begin @<Compute |g| and print it@>;
+ @<Print the values of $x_i$, $f(x_i)$, and the totals@>;
+ end;
+end;
+
+@ @<Read $x_1,\ldots,x_n$@>=
+begin n:=0;
+repeat incr(n); read(x[n]);
+until x[n]=0;
+decr(n);
+end
+
+@ @<Compute |s| and |y|@>=
+begin s:=0; y:=0;
+for k:=1 to n do
+ begin s:=s+x[k];
+ if y<abs(x[k]) then y:=abs(x[k]);
+ end;
+end
+
+@ @<Compute |g| and print it@>=
+begin glue_fix(s,t,y,g); {set |g|, perhaps print an error message}
+write(' Glue ratio is '); print_gr(g);
+write_ln(' (',ga-16:1,',',gb:1,',',gc:1,')');
+end
+
+@ @<Print the values of $x_i$, $f(x_i)$, and the totals@>=
+begin ts:=0;
+for k:=1 to n do
+ begin write(x[k]:20);
+ if x[k]>=0 then y:=glue_mult(x[k],g)
+ else y:=-glue_mult(-x[k],g);
+ write_ln(y:15);
+ ts:=ts+y;
+ end;
+write_ln(' Totals',s:13,ts:15,' (versus ',t:1,')');
+end
+
+@ Here is the main program.
+@^main program@>
+
+@p begin initialize;
+m:=1;
+read(t);
+while t>0 do
+ begin test;
+ incr(m); read(t);
+ end;
+end.
+
+@*Index. Here are the section numbers where various identifiers are used in the
+program, and where various topics are discussed.
+
+
+
+
+
diff --git a/systems/knuth/dist/tex/tex.web b/systems/knuth/dist/tex/tex.web
new file mode 100644
index 0000000000..e22c46b509
--- /dev/null
+++ b/systems/knuth/dist/tex/tex.web
@@ -0,0 +1,24985 @@
+% This program is copyright (C) 1982 by D. E. Knuth; all rights are reserved.
+% Copying of this file is authorized only if (1) you are D. E. Knuth, or if
+% (2) you make absolutely no changes to your copy. (The WEB system provides
+% for alterations via an auxiliary file; the master file should stay intact.)
+% See Appendix H of the WEB manual for hints on how to install this program.
+% And see Appendix A of the TRIP manual for details about how to validate it.
+
+% TeX is a trademark of the American Mathematical Society.
+% METAFONT is a trademark of Addison-Wesley Publishing Company.
+
+% Version 0 was released in September 1982 after it passed a variety of tests.
+% Version 1 was released in November 1983 after thorough testing.
+% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984).
+% Version 1.2 allowed `0' in response to an error, et alia (October 1984).
+% Version 1.3 made memory allocation more flexible and local (November 1984).
+% Version 1.4 fixed accents right after line breaks, et alia (April 1985).
+% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985).
+% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986).
+% Version 2.1 corrected anomalies in discretionary breaks (January 1987).
+% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987).
+% Version 2.3 avoided incomplete page in premature termination (August 1987).
+% Version 2.4 fixed \noaligned rules in indented displays (August 1987).
+% Version 2.5 saved cur_order when expanding tokens (September 1987).
+% Version 2.6 added 10sp slop when shipping leaders (November 1987).
+% Version 2.7 improved rounding of negative-width characters (November 1987).
+% Version 2.8 fixed weird bug if no \patterns are used (December 1987).
+% Version 2.9 made \csname\endcsname's "relax" local (December 1987).
+% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988).
+% Version 2.92 fixed \patterns, also file names with complex macros (May 1988).
+% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988).
+% Version 2.94 kept open_log_file from calling fatal_error (November 1988).
+% Version 2.95 solved that problem a better way (December 1988).
+% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989).
+% Version 2.97 corrected blunder in creating 2.95 (February 1989).
+% Version 2.98 omitted save_for_after at outer level (March 1989).
+% Version 2.99 caught $$\begingroup\halign..$$ (June 1989).
+% Version 2.991 caught .5\ifdim.6... (June 1989).
+% Version 2.992 introduced major changes for 8-bit extensions (September 1989).
+% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989).
+% Version 3.0 fixed unusual displays; was more \output robust (March 1990).
+% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990).
+% Version 3.14 fixed unprintable font names and corrected typos (March 1991).
+% Version 3.141 more of same; reconstituted ligatures better (March 1992).
+% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993).
+% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995).
+% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002).
+% Version 3.1415926 was a general cleanup with minor fixes (February 2008).
+% Version 3.14159265 was similar (January 2014).
+
+% A reward of $327.68 will be paid to the first finder of any remaining bug.
+
+% Although considerable effort has been expended to make the TeX program
+% correct and reliable, no warranty is implied; the author disclaims any
+% obligation or liability for damages, including but not limited to
+% special, indirect, or consequential damages arising out of or in
+% connection with the use or performance of this software. This work has
+% been a ``labor of love'' and the author hopes that users enjoy it.
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\noindent\ignorespaces}
+\def\hangg#1 {\hang\hbox{#1 }}
+\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+\def\ph{\hbox{Pascal-H}}
+\def\pct!{{\char`\%}} % percent sign in ordinary text
+\font\logo=logo10 % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\def\<#1>{$\langle#1\rangle$}
+\def\section{\mathhexbox278}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
+
+\outer\def\N#1. \[#2]#3.{\MN#1.\vfil\eject % begin starred section
+ \def\rhead{PART #2:\uppercase{#3}} % define running headline
+ \message{*\modno} % progress report
+ \edef\next{\write\cont{\Z{\?#2]#3}{\modno}{\the\pageno}}}\next
+ \ifon\startsection{\bf\ignorespaces#3.\quad}\ignorespaces}
+\let\?=\relax % we want to be able to \write a \?
+
+\def\title{\TeX82}
+\def\topofcontents{\hsize 5.5in
+ \vglue 0pt plus 1fil minus 1.5in
+ \def\?##1]{\hbox to 1in{\hfil##1.\ }}
+ }
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+\pageno=3
+\def\glob{13} % this should be the section number of "<Global...>"
+\def\gglob{20, 26} % this should be the next two sections of "<Global...>"
+
+@* \[1] Introduction.
+This is \TeX, a document compiler intended to produce typesetting of high
+quality.
+The \PASCAL\ program that follows is the definition of \TeX82, a standard
+@:PASCAL}{\PASCAL@>
+@!@:TeX82}{\TeX82@>
+version of \TeX\ that is designed to be highly portable so that identical output
+will be obtainable on a great variety of computers.
+
+The main purpose of the following program is to explain the algorithms of \TeX\
+as clearly as possible. As a result, the program will not necessarily be very
+efficient when a particular \PASCAL\ compiler has translated it into a
+particular machine language. However, the program has been written so that it
+can be tuned to run efficiently in a wide variety of operating environments
+by making comparatively few changes. Such flexibility is possible because
+the documentation that follows is written in the \.{WEB} language, which is
+at a higher level than \PASCAL; the preprocessing step that converts \.{WEB}
+to \PASCAL\ is able to introduce most of the necessary refinements.
+Semi-automatic translation to other languages is also feasible, because the
+program below does not make extensive use of features that are peculiar to
+\PASCAL.
+
+A large piece of software like \TeX\ has inherent complexity that cannot
+be reduced below a certain level of difficulty, although each individual
+part is fairly simple by itself. The \.{WEB} language is intended to make
+the algorithms as readable as possible, by reflecting the way the
+individual program pieces fit together and by providing the
+cross-references that connect different parts. Detailed comments about
+what is going on, and about why things were done in certain ways, have
+been liberally sprinkled throughout the program. These comments explain
+features of the implementation, but they rarely attempt to explain the
+\TeX\ language itself, since the reader is supposed to be familiar with
+{\sl The \TeX book}.
+@.WEB@>
+@:TeXbook}{\sl The \TeX book@>
+
+@ The present implementation has a long ancestry, beginning in the summer
+of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded
+a prototype
+@^Plass, Michael Frederick@>
+@^Liang, Franklin Mark@>
+@^Knuth, Donald Ervin@>
+based on some specifications that the author had made in May of that year.
+This original proto\TeX\ included macro definitions and elementary
+manipulations on boxes and glue, but it did not have line-breaking,
+page-breaking, mathematical formulas, alignment routines, error recovery,
+or the present semantic nest; furthermore,
+it used character lists instead of token lists, so that a control sequence
+like \.{\\halign} was represented by a list of seven characters. A
+complete version of \TeX\ was designed and coded by the author in late
+1977 and early 1978; that program, like its prototype, was written in the
+{\mc SAIL} language, for which an excellent debugging system was
+available. Preliminary plans to convert the {\mc SAIL} code into a form
+somewhat like the present ``web'' were developed by Luis Trabb~Pardo and
+@^Trabb Pardo, Luis Isidoro@>
+the author at the beginning of 1979, and a complete implementation was
+created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which
+@^Zabala Salelles, Ignacio Andr\'es@>
+was written by the author during the latter part of 1981 and the early
+part of 1982, also incorporates ideas from the 1979 implementation of
+@^Guibas, Leonidas Ioannis@>
+@^Sedgewick, Robert@>
+@^Wyatt, Douglas Kirk@>
+\TeX\ in {\mc MESA} that was written by Leonidas Guibas, Robert Sedgewick,
+and Douglas Wyatt at the Xerox Palo Alto Research Center. Several hundred
+refinements were introduced into \TeX82 based on the experiences gained with
+the original implementations, so that essentially every part of the system
+has been substantially improved. After the appearance of ``Version 0'' in
+September 1982, this program benefited greatly from the comments of
+many other people, notably David~R. Fuchs and Howard~W. Trickey.
+A final revision in September 1989 extended the input character set to
+eight-bit codes and introduced the ability to hyphenate words from
+different languages, based on some ideas of Michael~J. Ferguson.
+@^Fuchs, David Raymond@>
+@^Trickey, Howard Wellington@>
+@^Ferguson, Michael John@>
+
+No doubt there still is plenty of room for improvement, but the author
+is firmly committed to keeping \TeX82 ``frozen'' from now on; stability
+and reliability are to be its main virtues.
+
+On the other hand, the \.{WEB} description can be extended without changing
+the core of \TeX82 itself, and the program has been designed so that such
+extensions are not extremely difficult to make.
+The |banner| string defined here should be changed whenever \TeX\
+undergoes any modifications, so that it will be clear which version of
+\TeX\ might be the guilty party when a problem arises.
+@^extensions to \TeX@>
+@^system dependencies@>
+
+If this program is changed, the resulting system should not be called
+`\TeX'; the official name `\TeX' by itself is reserved
+for software systems that are fully compatible with each other.
+A special test suite called the ``\.{TRIP} test'' is available for
+helping to determine whether a particular implementation deserves to be
+known as `\TeX' [cf.~Stanford Computer Science report CS1027,
+November 1984].
+
+@d banner=='This is TeX, Version 3.14159265' {printed when \TeX\ starts}
+
+@ Different \PASCAL s have slightly different conventions, and the present
+@!@:PASCAL H}{\ph@>
+program expresses \TeX\ in terms of the \PASCAL\ that was
+available to the author in 1982. Constructions that apply to
+this particular compiler, which we shall call \ph, should help the
+reader see how to make an appropriate interface for other systems
+if necessary. (\ph\ is Charles Hedrick's modification of a compiler
+@^Hedrick, Charles Locke@>
+for the DECsystem-10 that was originally developed at the University of
+Hamburg; cf.\ {\sl SOFTWARE---Practice \AM\ Experience \bf6} (1976),
+29--42. The \TeX\ program below is intended to be adaptable, without
+extensive changes, to most other versions of \PASCAL, so it does not fully
+use the admirable features of \ph. Indeed, a conscious effort has been
+made here to avoid using several idiosyncratic features of standard
+\PASCAL\ itself, so that most of the code can be translated mechanically
+into other high-level languages. For example, the `\&{with}' and `\\{new}'
+features are not used, nor are pointer types, set types, or enumerated
+scalar types; there are no `\&{var}' parameters, except in the case of files;
+there are no tag fields on variant records; there are no assignments
+|real:=integer|; no procedures are declared local to other procedures.)
+
+The portions of this program that involve system-dependent code, where
+changes might be necessary because of differences between \PASCAL\ compilers
+and/or differences between
+operating systems, can be identified by looking at the sections whose
+numbers are listed under `system dependencies' in the index. Furthermore,
+the index entries for `dirty \PASCAL' list all places where the restrictions
+of \PASCAL\ have not been followed perfectly, for one reason or another.
+@!@^system dependencies@>
+@!@^dirty \PASCAL@>
+
+Incidentally, \PASCAL's standard |round| function can be problematical,
+because it disagrees with the IEEE floating-point standard.
+Many implementors have
+therefore chosen to substitute their own home-grown rounding procedure.
+
+@ The program begins with a normal \PASCAL\ program heading, whose
+components will be filled in later, using the conventions of \.{WEB}.
+@.WEB@>
+For example, the portion of the program called `\X\glob:Global
+variables\X' below will be replaced by a sequence of variable declarations
+that starts in $\section\glob$ of this documentation. In this way, we are able
+to define each individual global variable when we are prepared to
+understand what it means; we do not have to define all of the globals at
+once. Cross references in $\section\glob$, where it says ``See also
+sections \gglob, \dots,'' also make it possible to look at the set of
+all global variables, if desired. Similar remarks apply to the other
+portions of the program heading.
+
+Actually the heading shown here is not quite normal: The |program| line
+does not mention any |output| file, because \ph\ would ask the \TeX\ user
+to specify a file name if |output| were specified here.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@f mtype==type {`\&{mtype}' will be equivalent to `\&{type}'}
+@f type==true {but `|type|' will not be treated as a reserved word}
+
+@p @t\4@>@<Compiler directives@>@/
+program TEX; {all file names are defined dynamically}
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+mtype @<Types in the outer block@>@/
+var @<Global variables@>@/
+@#
+procedure initialize; {this procedure gets things started properly}
+ var @<Local variables for initialization@>@/
+ begin @<Initialize whatever \TeX\ might access@>@;
+ end;@#
+@t\4@>@<Basic printing procedures@>@/
+@t\4@>@<Error handling procedures@>@/
+
+@ The overall \TeX\ program begins with the heading just shown, after which
+comes a bunch of procedure declarations and function declarations.
+Finally we will get to the main program, which begins with the
+comment `|start_here|'. If you want to skip down to the
+main program now, you can look up `|start_here|' in the index.
+But the author suggests that the best way to understand this program
+is to follow pretty much the order of \TeX's components as they appear in the
+\.{WEB} description you are now reading, since the present ordering is
+intended to combine the advantages of the ``bottom up'' and ``top down''
+approaches to the problem of understanding a somewhat complicated system.
+
+@ Three labels must be declared in the main program, so we give them
+symbolic names.
+
+@d start_of_TEX=1 {go here when \TeX's variables are initialized}
+@d end_of_TEX=9998 {go here to close files and terminate gracefully}
+@d final_end=9999 {this label marks the ending of the program}
+
+@<Labels in the out...@>=
+start_of_TEX@t\hskip-2pt@>, end_of_TEX@t\hskip-2pt@>,@,final_end;
+ {key control points}
+
+@ Some of the code below is intended to be used only when diagnosing the
+strange behavior that sometimes occurs when \TeX\ is being installed or
+when system wizards are fooling around with \TeX\ without quite knowing
+what they are doing. Such code will not normally be compiled; it is
+delimited by the codewords `$|debug|\ldots|gubed|$', with apologies
+to people who wish to preserve the purity of English.
+
+Similarly, there is some conditional code delimited by
+`$|stat|\ldots|tats|$' that is intended for use when statistics are to be
+kept about \TeX's memory usage. The |stat| $\ldots$ |tats| code also
+implements diagnostic information for \.{\\tracingparagraphs} and
+\.{\\tracingpages}.
+@^debugging@>
+
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@f debug==begin
+@f gubed==end
+@#
+@d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering
+ usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering
+ usage statistics}
+@f stat==begin
+@f tats==end
+
+@ This program has two important variations: (1) There is a long and slow
+version called \.{INITEX}, which does the extra calculations needed to
+@.INITEX@>
+initialize \TeX's internal tables; and (2)~there is a shorter and faster
+production version, which cuts the initialization to a bare minimum.
+Parts of the program that are needed in (1) but not in (2) are delimited by
+the codewords `$|init|\ldots|tini|$'.
+
+@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
+@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
+@f init==begin
+@f tini==end
+
+@<Initialize whatever...@>=
+@<Set initial values of key variables@>@/
+@!init @<Initialize table entries (done by \.{INITEX} only)@>@;@+tini
+
+@ If the first character of a \PASCAL\ comment is a dollar sign,
+\ph\ treats the comment as a list of ``compiler directives'' that will
+affect the translation of this program into machine language. The
+directives shown below specify full checking and inclusion of the \PASCAL\
+debugger when \TeX\ is being debugged, but they cause range checking and other
+redundant code to be eliminated when the production system is being generated.
+Arithmetic overflow will be detected in all cases.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+@^overflow in arithmetic@>
+
+@<Compiler directives@>=
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+
+@ This \TeX\ implementation conforms to the rules of the {\sl Pascal User
+@:PASCAL}{\PASCAL@>
+@^system dependencies@>
+Manual} published by Jensen and Wirth in 1975, except where system-dependent
+@^Wirth, Niklaus@>
+@^Jensen, Kathleen@>
+code is necessary to make a useful system program, and except in another
+respect where such conformity would unnecessarily obscure the meaning
+and clutter up the code: We assume that |case| statements may include a
+default case that applies if no matching label is found. Thus, we shall use
+constructions like
+$$\vbox{\halign{\ignorespaces#\hfil\cr
+|case x of|\cr
+1: $\langle\,$code for $x=1\,\rangle$;\cr
+3: $\langle\,$code for $x=3\,\rangle$;\cr
+|othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr
+|endcases|\cr}}$$
+since most \PASCAL\ compilers have plugged this hole in the language by
+incorporating some sort of default mechanism. For example, the \ph\
+compiler allows `|others|:' as a default label, and other \PASCAL s allow
+syntaxes like `\&{else}' or `\&{otherwise}' or `\\{otherwise}:', etc. The
+definitions of |othercases| and |endcases| should be changed to agree with
+local conventions. Note that no semicolon appears before |endcases| in
+this program, so the definition of |endcases| should include a semicolon
+if the compiler wants one. (Of course, if no default mechanism is
+available, the |case| statements of \TeX\ will have to be laboriously
+extended by listing all remaining cases. People who are stuck with such
+\PASCAL s have, in fact, done this, successfully but not happily!)
+@:PASCAL H}{\ph@>
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The following parameters can be changed at compile time to extend or
+reduce \TeX's capacity. They may have different values in \.{INITEX} and
+in production versions of \TeX.
+@.INITEX@>
+@^system dependencies@>
+
+@<Constants...@>=
+@!mem_max=30000; {greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|}
+@!mem_min=0; {smallest index in \TeX's internal |mem| array;
+ must be |min_halfword| or more;
+ must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|}
+@!buf_size=500; {maximum number of characters simultaneously present in
+ current lines of open files and in control sequences between
+ \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
+@!error_line=72; {width of context lines on terminal error messages}
+@!half_error_line=42; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!stack_size=200; {maximum number of simultaneous input sources}
+@!max_in_open=6; {maximum number of input files and error insertions that
+ can be going on simultaneously}
+@!font_max=75; {maximum internal font number; must not exceed |max_quarterword|
+ and must be at most |font_base+256|}
+@!font_mem_size=20000; {number of words of |font_info| for all fonts}
+@!param_size=60; {maximum number of simultaneous macro parameters}
+@!nest_size=40; {maximum number of semantic levels simultaneously active}
+@!max_strings=3000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=8000; {the minimum number of characters that should be
+ available for the user's control sequences and font names,
+ after \TeX's own error messages are stored}
+@!pool_size=32000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all fonts and
+ control sequences; must exceed |string_vacancies| by the total
+ length of \TeX's own strings, which is currently about 23000}
+@!save_size=600; {space for saving values outside of current group; must be
+ at most |max_halfword|}
+@!trie_size=8000; {space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX}
+@!trie_op_size=500; {space for ``opcodes'' in the hyphenation patterns}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!file_name_size=40; {file names shouldn't be longer than this}
+@!pool_name='TeXformats:TEX.POOL ';
+ {string of length |file_name_size|; tells where the string pool appears}
+@.TeXformats@>
+
+@ Like the preceding parameters, the following quantities can be changed
+at compile time to extend or reduce \TeX's capacity. But if they are changed,
+it is necessary to rerun the initialization program \.{INITEX}
+@.INITEX@>
+to generate new tables for the production \TeX\ program.
+One can't simply make helter-skelter changes to the following constants,
+since certain rather complex initialization
+numbers are computed from them. They are defined here using
+\.{WEB} macros, instead of being put into \PASCAL's |const| list, in order to
+emphasize this distinction.
+
+@d mem_bot=0 {smallest index in the |mem| array dumped by \.{INITEX};
+ must not be less than |mem_min|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INITEX};
+ must be substantially larger than |mem_bot|
+ and not greater than |mem_max|}
+@d font_base=0 {smallest internal font number; must not be less
+ than |min_quarterword|}
+@d hash_size=2100 {maximum number of control sequences; it should be at most
+ about |(mem_max-mem_min)/10|}
+@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
+@d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions}
+@^system dependencies@>
+
+@ In case somebody has inadvertently made bad settings of the ``constants,''
+\TeX\ checks them using a global variable called |bad|.
+
+This is the first of many sections of \TeX\ where global variables are
+defined.
+
+@<Glob...@>=
+@!bad:integer; {is some ``constant'' wrong?}
+
+@ Later on we will say `\ignorespaces|if mem_max>=max_halfword then bad:=14|',
+or something similar. (We can't do that until |max_halfword| has been defined.)
+
+@<Check the ``constant'' values for consistency@>=
+bad:=0;
+if (half_error_line<30)or(half_error_line>error_line-15) then bad:=1;
+if max_print_line<60 then bad:=2;
+if dvi_buf_size mod 8<>0 then bad:=3;
+if mem_bot+1100>mem_top then bad:=4;
+if hash_prime>hash_size then bad:=5;
+if max_in_open>=128 then bad:=6;
+if mem_top<256+11 then bad:=7; {we will want |null_list>255|}
+
+@ Labels are given symbolic names by the following definitions, so that
+occasional |goto| statements will be meaningful. We insert the label
+`|exit|' just before the `\ignorespaces|end|\unskip' of a procedure in
+which we have used the `|return|' statement defined below; the label
+`|restart|' is occasionally used at the very beginning of a procedure; and
+the label `|reswitch|' is occasionally used just prior to a |case|
+statement in which some cases change the conditions and we wish to branch
+to the newly applicable case. Loops that are set up with the |loop|
+construction defined below are commonly exited by going to `|done|' or to
+`|found|' or to `|not_found|', and they are sometimes repeated by going to
+`|continue|'. If two or more parts of a subroutine start differently but
+end up the same, the shared code may be gathered together at
+`|common_ending|'.
+
+Incidentally, this program never declares a label that isn't actually used,
+because some fussy \PASCAL\ compilers will complain about redundant labels.
+
+@d exit=10 {go here to leave a procedure}
+@d restart=20 {go here to start a procedure again}
+@d reswitch=21 {go here to start a case statement again}
+@d continue=22 {go here to resume a loop}
+@d done=30 {go here to exit a loop}
+@d done1=31 {like |done|, when there is more than one loop}
+@d done2=32 {for exiting the second loop in a long block}
+@d done3=33 {for exiting the third loop in a very long block}
+@d done4=34 {for exiting the fourth loop in an extremely long block}
+@d done5=35 {for exiting the fifth loop in an immense block}
+@d done6=36 {for exiting the sixth loop in a block}
+@d found=40 {go here when you've found it}
+@d found1=41 {like |found|, when there's more than one per routine}
+@d found2=42 {like |found|, when there's more than two per routine}
+@d not_found=45 {go here when you've found nothing}
+@d common_ending=50 {go here when you want to merge with another branch}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d negate(#) == #:=-# {change the sign of a variable}
+@d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
+@f loop == xclause
+ {\.{WEB}'s |xclause| acts like `\ignorespaces|while true do|\unskip'}
+@d do_nothing == {empty statement}
+@d return == goto exit {terminate a procedure call}
+@f return == nil
+@d empty=0 {symbolic name for a null constant}
+
+@* \[2] The character set.
+In order to make \TeX\ readily portable to a wide variety of
+computers, all of its input text is converted to an internal eight-bit
+code that includes standard ASCII, the ``American Standard Code for
+Information Interchange.'' This 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 to a
+text file.
+
+Such an internal code is relevant to users of \TeX\ primarily because it
+governs the positions of characters in the fonts. For example, the
+character `\.A' has ASCII code $65=@'101$, and when \TeX\ typesets
+this letter it specifies character number 65 in the current font.
+If that font actually has `\.A' in a different position, \TeX\ doesn't
+know what the real position is; the program that does the actual printing from
+\TeX's device-independent files is responsible for converting from ASCII to
+a particular font encoding.
+@^ASCII code@>
+
+\TeX's internal code also defines the value of constants
+that begin with a reverse apostrophe; and it provides an index to the
+\.{\\catcode}, \.{\\mathcode}, \.{\\uccode}, \.{\\lccode}, and \.{\\delcode}
+tables.
+
+@ Characters of text that have been converted to \TeX's internal form
+are said to be of type |ASCII_code|, which is a subrange of the integers.
+
+@<Types...@>=
+@!ASCII_code=0..255; {eight-bit numbers}
+
+@ 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, especially in a program for typesetting;
+so the present specification of \TeX\ has been written under the assumption
+that the \PASCAL\ compiler and run-time system permit the use of text files
+with more than 64 distinguishable characters. More precisely, we assume that
+the character set contains at least the letters and symbols associated
+with ASCII codes @'40 through @'176; all of these characters are now
+available on most computer terminals.
+
+Since we are dealing with more characters than were present in the first
+\PASCAL\ compilers, we have to decide what to call the associated data
+type. Some \PASCAL s use the original name |char| for the
+characters in text files, even though there now are more than 64 such
+characters, while other \PASCAL s consider |char| to be a 64-element
+subrange of a larger data type that has some other name.
+
+In order to accommodate this difference, we shall use the name |text_char|
+to stand for the data type of the characters that are converted to and
+from |ASCII_code| when they are input and output. We shall also assume
+that |text_char| consists of the elements |chr(first_text_char)| through
+|chr(last_text_char)|, inclusive. The following definitions should be
+adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Local variables for init...@>=
+@!i:integer;
+
+@ The \TeX\ processor converts 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.
+
+@<Glob...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+
+@ Since we are assuming that our \PASCAL\ system 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 the standard part of the |xchr| array
+properly, without needing any system-dependent changes. On the other
+hand, it is possible to implement \TeX\ with less complete character
+sets, and in such cases it will be necessary to change something here.
+@^system dependencies@>
+
+@<Set init...@>=
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';@/
+
+@ Some of the ASCII codes without visible characters have been given symbolic
+names in this program because they are used with a special meaning.
+
+@d null_code=@'0 {ASCII code that might disappear}
+@d carriage_return=@'15 {ASCII code used at end of line}
+@d invalid_code=@'177 {ASCII code that many systems prohibit in text files}
+
+@ The ASCII code is ``standard'' only to a certain extent, since many
+computer installations have found it advantageous to have ready access
+to more than 94 printing characters. Appendix~C of {\sl The \TeX book\/}
+gives a complete specification of the intended correspondence between
+characters and \TeX's internal representation.
+@:TeXbook}{\sl The \TeX book@>
+
+If \TeX\ is being used
+on a garden-variety \PASCAL\ for which only standard ASCII
+codes will appear in the input and output files, it doesn't really matter
+what codes are specified in |xchr[0..@'37]|, but the safest policy is to
+blank everything out by using the code shown below.
+
+However, other settings of |xchr| will make \TeX\ more friendly on
+computers that have an extended character set, so that users can type things
+like `\.^^Z' instead of `\.{\\ne}'. People with extended character sets can
+assign codes arbitrarily, giving an |xchr| equivalent to whatever
+characters the users of \TeX\ are allowed to have in their input files.
+It is best to make the codes correspond to the intended interpretations as
+shown in Appendix~C whenever possible; but this is not necessary. For
+example, in countries with an alphabet of more than 26 letters, it is
+usually best to map the additional letters into codes less than~@'40.
+To get the most ``permissive'' character set, change |' '| on the
+right of these assignment statements to |chr(i)|.
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]|
+where |i<j<@'177|, the value of |xord[xchr[i]]| will turn out to be
+|j| or more; hence, standard ASCII code numbers will be used instead of
+codes below @'40 in case there is a coincidence.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=invalid_code;
+for i:=@'200 to @'377 do xord[xchr[i]]:=i;
+for i:=0 to @'176 do xord[xchr[i]]:=i;
+
+@* \[3] Input and output.
+The bane of portability is the fact that different operating systems treat
+input and output quite differently, perhaps because computer scientists
+have not given sufficient attention to this problem. People have felt somehow
+that input and output are not part of ``real'' programming. Well, it is true
+that some kinds of programming are more fun than others. With existing
+input/output conventions being so diverse and so messy, the only sources of
+joy in such parts of the code are the rare occasions when one can find a
+way to make the program a little less bad than it might have been. We have
+two choices, either to attack I/O now and get it over with, or to postpone
+I/O until near the end. Neither prospect is very attractive, so let's
+get it over with.
+
+The basic operations we need to do are (1)~inputting and outputting of
+text, to or from a file or the user's terminal; (2)~inputting and
+outputting of eight-bit bytes, to or from a file; (3)~instructing the
+operating system to initiate (``open'') or to terminate (``close'') input or
+output from a specified file; (4)~testing whether the end of an input
+file has been reached.
+
+\TeX\ needs to deal with two kinds of files.
+We shall use the term |alpha_file| for a file that contains textual data,
+and the term |byte_file| for a file that contains eight-bit binary information.
+These two types turn out to be the same on many computers, but
+sometimes there is a significant distinction, so we shall be careful to
+distinguish between them. Standard protocols for transferring
+such files from computer to computer, via high-speed networks, are
+now becoming available to more and more communities of users.
+
+The program actually makes use also of a third kind of file, called a
+|word_file|, when dumping and reloading base information for its own
+initialization. We shall define a word file later; but it will be possible
+for us to specify simple operations on word files before they are defined.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!alpha_file=packed file of text_char; {files that contain textual data}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+
+@ Most of what we need to do with respect to input and output can be handled
+by the I/O facilities that are standard in \PASCAL, i.e., the routines
+called |get|, |put|, |eof|, and so on. But
+standard \PASCAL\ does not allow file variables to be associated with file
+names that are determined at run time, so it cannot be used to implement
+\TeX; some sort of extension to \PASCAL's ordinary |reset| and |rewrite|
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \TeX\ can open a file whose external name is specified by
+|name_of_file|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file:packed array[1..file_name_size] of char;@;@/
+ {on some systems this may be a \&{record} variable}
+@!name_length:0..file_name_size;@/{this many characters are actually
+ relevant in |name_of_file| (the rest are blank)}
+
+@ The \ph\ compiler with which the present version of \TeX\ was prepared has
+extended the rules of \PASCAL\ in a very convenient way. To open file~|f|,
+we can write
+$$\vbox{\halign{#\hfil\qquad&#\hfil\cr
+|reset(f,@t\\{name}@>,'/O')|&for input;\cr
+|rewrite(f,@t\\{name}@>,'/O')|&for output.\cr}}$$
+The `\\{name}' parameter, which is of type `{\bf packed array
+$[\langle\\{any}\rangle]$ of \\{char}}', stands for the name of
+the external file that is being opened for input or output.
+Blank spaces that might appear in \\{name} are ignored.
+
+The `\.{/O}' parameter tells the operating system not to issue its own
+error messages if something goes wrong. If a file of the specified name
+cannot be found, or if such a file cannot be opened for some other reason
+(e.g., someone may already be trying to write the same file), we will have
+|@!erstat(f)<>0| after an unsuccessful |reset| or |rewrite|. This allows
+\TeX\ to undertake appropriate corrective action.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+\TeX's file-opening procedures return |false| if no file identified by
+|name_of_file| could be opened.
+
+@d reset_OK(#)==erstat(#)=0
+@d rewrite_OK(#)==erstat(#)=0
+
+@p function a_open_in(var f:alpha_file):boolean;
+ {open a text file for input}
+begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f);
+end;
+@#
+function a_open_out(var f:alpha_file):boolean;
+ {open a text file for output}
+begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f);
+end;
+@#
+function b_open_in(var f:byte_file):boolean;
+ {open a binary file for input}
+begin reset(f,name_of_file,'/O'); b_open_in:=reset_OK(f);
+end;
+@#
+function b_open_out(var f:byte_file):boolean;
+ {open a binary file for output}
+begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f);
+end;
+@#
+function w_open_in(var f:word_file):boolean;
+ {open a word file for input}
+begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f);
+end;
+@#
+function w_open_out(var f:word_file):boolean;
+ {open a word file for output}
+begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f);
+end;
+
+@ Files can be closed with the \ph\ routine `|close(f)|', which
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+should be used when all input or output with respect to |f| has been completed.
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+These procedures should not generate error messages if a file is
+being closed before it has been successfully opened.
+
+@p procedure a_close(var f:alpha_file); {close a text file}
+begin close(f);
+end;
+@#
+procedure b_close(var f:byte_file); {close a binary file}
+begin close(f);
+end;
+@#
+procedure w_close(var f:word_file); {close a word file}
+begin close(f);
+end;
+
+@ Binary input and output are done with \PASCAL's ordinary |get| and |put|
+procedures, so we don't have to make any other special arrangements for
+binary~I/O. Text output is also easy to do with standard \PASCAL\ routines.
+The treatment of text input is more difficult, however, because
+of the necessary translation to |ASCII_code| values.
+\TeX's conventions should be efficient, and they should
+blend nicely with the user's operating environment.
+
+@ Input from text files is read one line at a time, using a routine called
+|input_ln|. This function is defined in terms of global variables called
+|buffer|, |first|, and |last| that will be described in detail later; for
+now, it suffices for us to know that |buffer| is an array of |ASCII_code|
+values, and that |first| and |last| are indices into this array
+representing the beginning and ending of a line of text.
+
+@<Glob...@>=
+@!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read}
+@!first:0..buf_size; {the first unused position in |buffer|}
+@!last:0..buf_size; {end of the line just input to |buffer|}
+@!max_buf_stack:0..buf_size; {largest index used in |buffer|}
+
+@ The |input_ln| function brings the next line of input from the specified
+file into available positions of the buffer array and returns the value
+|true|, unless the file has already been entirely read, in which case it
+returns |false| and sets |last:=first|. In general, the |ASCII_code|
+numbers that represent the next line of the file are input into
+|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the
+global variable |last| is set equal to |first| plus the length of the
+line. Trailing blanks are removed from the line; thus, either |last=first|
+(in which case the line was entirely blank) or |buffer[last-1]<>" "|.
+
+An overflow error is given, however, if the normal actions of |input_ln|
+would make |last>=buf_size|; this is done so that other parts of \TeX\
+can safely look at the contents of |buffer[last+1]| without overstepping
+the bounds of the |buffer| array. Upon entry to |input_ln|, the condition
+|first<buf_size| will always hold, so that there is always room for an
+``empty'' line.
+
+The variable |max_buf_stack|, which is used to keep track of how large
+the |buf_size| parameter must be to accommodate the present job, is
+also kept up to date by |input_ln|.
+
+If the |bypass_eoln| parameter is |true|, |input_ln| will do a |get|
+before looking at the first character of the line; this skips over
+an |eoln| that was in |f^|. The procedure does not do a |get| when it
+reaches the end of the line; therefore it can be used to acquire input
+from the user's terminal as well as from ordinary text files.
+
+Standard \PASCAL\ says that a file should have |eoln| immediately
+before |eof|, but \TeX\ needs only a weaker restriction: If |eof|
+occurs in the middle of a line, the system function |eoln| should return
+a |true| result (even though |f^| will be undefined).
+
+Since the inner loop of |input_ln| is part of \TeX'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, if such routines are available. The following
+code uses standard \PASCAL\ to illustrate what needs to be done, but
+finer tuning is often possible at well-developed \PASCAL\ sites.
+@^inner loop@>
+
+@p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean;
+ {inputs the next line or returns |false|}
+var last_nonblank:0..buf_size; {|last| with trailing blanks removed}
+begin if bypass_eoln then if not eof(f) then get(f);
+ {input the first character of the line into |f^|}
+last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
+if eof(f) then input_ln:=false
+else begin last_nonblank:=first;
+ while not eoln(f) do
+ begin if last>=max_buf_stack then
+ begin max_buf_stack:=last+1;
+ if max_buf_stack=buf_size then
+ @<Report overflow of the input buffer, and abort@>;
+ end;
+ buffer[last]:=xord[f^]; get(f); incr(last);
+ if buffer[last-1]<>" " then last_nonblank:=last;
+ end;
+ last:=last_nonblank; input_ln:=true;
+ end;
+end;
+
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file variable is called |term_in|, and when it
+is considered an output file the file variable is |term_out|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!term_in:alpha_file; {the terminal as an input file}
+@!term_out:alpha_file; {the terminal as an output file}
+
+@ Here is how to open the terminal files
+in \ph. The `\.{/I}' switch suppresses the first |get|.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d t_open_in==reset(term_in,'TTY:','/O/I') {open the terminal for text input}
+@d t_open_out==rewrite(term_out,'TTY:','/O') {open the terminal for text output}
+
+@ Sometimes it is necessary to synchronize the input/output mixture that
+happens on the user's terminal, and three system-dependent
+procedures are used for this
+purpose. The first of these, |update_terminal|, 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.
+The second, |clear_terminal|, is called when we wish to cancel any
+input that the user may have typed ahead (since we are about to
+issue an unexpected error message). The third, |wake_up_terminal|,
+is supposed to revive the terminal if the user has disabled it by
+some instruction to the operating system. The following macros show how
+these operations can be specified in \ph:
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@d clear_terminal == break_in(term_in,true) {clear the terminal input buffer}
+@d wake_up_terminal == do_nothing {cancel the user's cancellation of output}
+
+@ We need a special routine to read the first line of \TeX\ input from
+the user's terminal. This line is different because it is read before we
+have opened the transcript file; there is sort of a ``chicken and
+egg'' problem here. If the user types `\.{\\input paper}' on the first
+line, or if some macro invoked by that line does such an \.{\\input},
+the transcript file will be named `\.{paper.log}'; but if no \.{\\input}
+commands are performed during the first line of terminal input, the transcript
+file will acquire its default name `\.{texput.log}'. (The transcript file
+will not contain error messages generated by the first line before the
+first \.{\\input} command.)
+@.texput@>
+
+The first line is even more special if we are lucky enough to have an operating
+system that treats \TeX\ differently from a run-of-the-mill \PASCAL\ object
+program. It's nice to let the user start running a \TeX\ job by typing
+a command line like `\.{tex paper}'; in such a case, \TeX\ will operate
+as if the first line of input were `\.{paper}', i.e., the first line will
+consist of the remainder of the command line, after the part that invoked
+\TeX.
+
+The first line is special also because it may be read before \TeX\ has
+input a format file. In such cases, normal error messages cannot yet
+be given. The following code uses concepts that will be explained later.
+(If the \PASCAL\ compiler does not support non-local |@!goto|\unskip, the
+@^system dependencies@>
+statement `|goto final_end|' should be replaced by something that
+quietly terminates the program.)
+
+@<Report overflow of the input buffer, and abort@>=
+if format_ident=0 then
+ begin write_ln(term_out,'Buffer size exceeded!'); goto final_end;
+@.Buffer size exceeded@>
+ end
+else begin cur_input.loc_field:=first; cur_input.limit_field:=last-1;
+ overflow("buffer size",buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ end
+
+@ Different systems have different ways to get started. But regardless of
+what conventions are adopted, the routine that initializes the terminal
+should satisfy the following specifications:
+
+\yskip\textindent{1)}It should open file |term_in| for input from the
+ terminal. (The file |term_out| will already be open for output to the
+ terminal.)
+
+\textindent{2)}If the user has given a command line, this line should be
+ considered the first line of terminal input. Otherwise the
+ user should be prompted with `\.{**}', and the first line of input
+ should be whatever is typed in response.
+
+\textindent{3)}The first line of input, which might or might not be a
+ command line, should appear in locations |first| to |last-1| of the
+ |buffer| array.
+
+\textindent{4)}The global variable |loc| should be set so that the
+ character to be read next by \TeX\ is in |buffer[loc]|. This
+ character should not be blank, and we should have |loc<last|.
+
+\yskip\noindent(It may be necessary to prompt the user several times
+before a non-blank line comes in. The prompt is `\.{**}' instead of the
+later `\.*' because the meaning is slightly different: `\.{\\input}' need
+not be typed immediately after~`\.{**}'.)
+
+@d loc==cur_input.loc_field {location of first unread character in |buffer|}
+
+@ The following program does the required initialization
+without retrieving a possible command line.
+It should be clear how to modify this routine to deal with command lines,
+if the system permits them.
+@^system dependencies@>
+
+@p function init_terminal:boolean; {gets the terminal input started}
+label exit;
+begin t_open_in;
+loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal;
+@.**@>
+ if not input_ln(term_in,true) then {this shouldn't happen}
+ begin write_ln(term_out);
+ write(term_out,'! End of file on the terminal... why?');
+@.End of file on the terminal@>
+ init_terminal:=false; return;
+ end;
+ loc:=first;
+ while (loc<last)and(buffer[loc]=" ") do incr(loc);
+ if loc<last then
+ begin init_terminal:=true;
+ return; {return unless the line was all blank}
+ end;
+ write_ln(term_out,'Please type the name of your input file.');
+ end;
+exit:end;
+
+@* \[4] String handling.
+Control sequence names and diagnostic messages are variable-length strings
+of eight-bit characters. Since \PASCAL\ does not have a well-developed string
+mechanism, \TeX\ does all of its string processing by homegrown methods.
+
+Elaborate facilities for dynamic strings are not needed, so all of the
+necessary operations can be handled with a simple data structure.
+The array |str_pool| contains all of the (eight-bit) ASCII codes in all
+of the strings, and the array |str_start| contains indices of the starting
+points of each string. Strings are referred to by integer numbers, so that
+string number |s| comprises the characters |str_pool[j]| for
+|str_start[s]<=j<str_start[s+1]|. Additional integer variables
+|pool_ptr| and |str_ptr| indicate the number of entries used so far
+in |str_pool| and |str_start|, respectively; locations
+|str_pool[pool_ptr]| and |str_start[str_ptr]| are
+ready for the next string to be allocated.
+
+String numbers 0 to 255 are reserved for strings that correspond to single
+ASCII characters. This is in accordance with the conventions of \.{WEB},
+@.WEB@>
+which converts single-character strings into the ASCII code number of the
+single character involved, while it converts other strings into integers
+and builds a string pool file. Thus, when the string constant \.{"."} appears
+in the program below, \.{WEB} converts it into the integer 46, which is the
+ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"}
+into some integer greater than~255. String number 46 will presumably be the
+single character `\..'; but some ASCII codes have no standard visible
+representation, and \TeX\ sometimes needs to be able to print an arbitrary
+ASCII character, so the first 256 strings are used to specify exactly what
+should be printed for each of the 256 possibilities.
+
+Elements of the |str_pool| array must be ASCII codes that can actually
+be printed; i.e., they must have an |xchr| equivalent in the local
+character set. (This restriction applies only to preloaded strings,
+not to those generated dynamically by the user.)
+
+Some \PASCAL\ compilers won't pack integers into a single byte unless the
+integers lie in the range |-128..127|. To accommodate such systems
+we access the string pool only via macros that can easily be redefined.
+@^system dependencies@>
+
+@d si(#) == # {convert from |ASCII_code| to |packed_ASCII_code|}
+@d so(#) == # {convert from |packed_ASCII_code| to |ASCII_code|}
+
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+@!packed_ASCII_code = 0..255; {elements of |str_pool| array}
+
+@ @<Glob...@>=
+@!str_pool:packed array[pool_pointer] of packed_ASCII_code; {the characters}
+@!str_start : array[str_number] of pool_pointer; {the starting pointers}
+@!pool_ptr : pool_pointer; {first unused position in |str_pool|}
+@!str_ptr : str_number; {number of the current string being created}
+@!init_pool_ptr : pool_pointer; {the starting value of |pool_ptr|}
+@!init_str_ptr : str_number; {the starting value of |str_ptr|}
+
+@ Several of the elementary string operations are performed using \.{WEB}
+macros instead of \PASCAL\ procedures, because many of the
+operations are done quite frequently and we want to avoid the
+overhead of procedure calls. For example, here is
+a simple macro that computes the length of a string.
+@.WEB@>
+
+@d length(#)==(str_start[#+1]-str_start[#]) {the number of characters
+ in string number \#}
+
+@ The length of the current string is called |cur_length|:
+
+@d cur_length == (pool_ptr - str_start[str_ptr])
+
+@ Strings are created by appending character codes to |str_pool|.
+The |append_char| macro, defined here, does not check to see if the
+value of |pool_ptr| has gotten too high; this test is supposed to be
+made before |append_char| is used. There is also a |flush_char|
+macro, which erases the last character appended.
+
+To test if there is room to append |l| more characters to |str_pool|,
+we shall write |str_room(l)|, which aborts \TeX\ and gives an
+apologetic error message if there isn't enough room.
+
+@d append_char(#) == {put |ASCII_code| \# at the end of |str_pool|}
+begin str_pool[pool_ptr]:=si(#); incr(pool_ptr);
+end
+@d flush_char == decr(pool_ptr) {forget the last character in the pool}
+@d str_room(#) == {make sure that the pool hasn't overflowed}
+ begin if pool_ptr+# > pool_size then
+ overflow("pool size",pool_size-init_pool_ptr);
+@:TeX capacity exceeded pool size}{\quad pool size@>
+ end
+
+@ Once a sequence of characters has been appended to |str_pool|, it
+officially becomes a string when the function |make_string| is called.
+This function returns the identification number of the new string as its
+value.
+
+@p function make_string : str_number; {current string enters the pool}
+begin if str_ptr=max_strings then
+ overflow("number of strings",max_strings-init_str_ptr);
+@:TeX capacity exceeded number of strings}{\quad number of strings@>
+incr(str_ptr); str_start[str_ptr]:=pool_ptr;
+make_string:=str_ptr-1;
+end;
+
+@ To destroy the most recently made string, we say |flush_string|.
+
+@d flush_string==begin decr(str_ptr); pool_ptr:=str_start[str_ptr];
+ end
+
+@ The following subroutine compares string |s| with another string of the
+same length that appears in |buffer| starting at position |k|;
+the result is |true| if and only if the strings are equal.
+Empirical tests indicate that |str_eq_buf| is used in such a way that
+it tends to return |true| about 80 percent of the time.
+
+@p function str_eq_buf(@!s:str_number;@!k:integer):boolean;
+ {test equality of strings}
+label not_found; {loop exit}
+var j: pool_pointer; {running index}
+@!result: boolean; {result of comparison}
+begin j:=str_start[s];
+while j<str_start[s+1] do
+ begin if so(str_pool[j])<>buffer[k] then
+ begin result:=false; goto not_found;
+ end;
+ incr(j); incr(k);
+ end;
+result:=true;
+not_found: str_eq_buf:=result;
+end;
+
+@ Here is a similar routine, but it compares two strings in the string pool,
+and it does not assume that they have the same length.
+
+@p function str_eq_str(@!s,@!t:str_number):boolean;
+ {test equality of strings}
+label not_found; {loop exit}
+var j,@!k: pool_pointer; {running indices}
+@!result: boolean; {result of comparison}
+begin result:=false;
+if length(s)<>length(t) then goto not_found;
+j:=str_start[s]; k:=str_start[t];
+while j<str_start[s+1] do
+ begin if str_pool[j]<>str_pool[k] then goto not_found;
+ incr(j); incr(k);
+ end;
+result:=true;
+not_found: str_eq_str:=result;
+end;
+
+@ The initial values of |str_pool|, |str_start|, |pool_ptr|,
+and |str_ptr| are computed by the \.{INITEX} program, based in part
+on the information that \.{WEB} has output while processing \TeX.
+@.INITEX@>
+@^string pool@>
+
+@p @!init function get_strings_started:boolean; {initializes the string pool,
+ but returns |false| if something goes wrong}
+label done,exit;
+var k,@!l:0..255; {small indices or counters}
+@!m,@!n:text_char; {characters input from |pool_file|}
+@!g:str_number; {garbage}
+@!a:integer; {accumulator for check sum}
+@!c:boolean; {check sum has been checked}
+begin pool_ptr:=0; str_ptr:=0; str_start[0]:=0;
+@<Make the first 256 strings@>;
+@<Read the other strings from the \.{TEX.POOL} file and return |true|,
+ or give an error message and return |false|@>;
+exit:end;
+tini
+
+@ @d app_lc_hex(#)==l:=#;
+ if l<10 then append_char(l+"0")@+else append_char(l-10+"a")
+
+@<Make the first 256...@>=
+for k:=0 to 255 do
+ begin if (@<Character |k| cannot be printed@>) then
+ begin append_char("^"); append_char("^");
+ if k<@'100 then append_char(k+@'100)
+ else if k<@'200 then append_char(k-@'100)
+ else begin app_lc_hex(k div 16); app_lc_hex(k mod 16);
+ end;
+ end
+ else append_char(k);
+ g:=make_string;
+ end
+
+@ The first 128 strings will contain 95 standard ASCII characters, and the
+other 33 characters will be printed in three-symbol form like `\.{\^\^A}'
+unless a system-dependent change is made here. Installations that have
+an extended character set, where for example |xchr[@'32]=@t\.{\'^^Z\'}@>|,
+would like string @'32 to be the single character @'32 instead of the
+three characters @'136, @'136, @'132 (\.{\^\^Z}). On the other hand,
+even people with an extended character set will want to represent string
+@'15 by \.{\^\^M}, since @'15 is |carriage_return|; the idea is to
+produce visible strings instead of tabs or line-feeds or carriage-returns
+or bell-rings or characters that are treated anomalously in text files.
+
+Unprintable characters of codes 128--255 are, similarly, rendered
+\.{\^\^80}--\.{\^\^ff}.
+
+The boolean expression defined here should be |true| unless \TeX\
+internal code number~|k| corresponds to a non-troublesome visible
+symbol in the local character set. An appropriate formula for the
+extended character set recommended in {\sl The \TeX book\/} would, for
+example, be `|k in [0,@'10..@'12,@'14,@'15,@'33,@'177..@'377]|'.
+If character |k| cannot be printed, and |k<@'200|, then character |k+@'100| or
+|k-@'100| must be printable; moreover, ASCII codes |[@'41..@'46,
+@'60..@'71, @'136, @'141..@'146, @'160..@'171]| must be printable.
+Thus, at least 81 printable characters are needed.
+@:TeXbook}{\sl The \TeX book@>
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Character |k| cannot be printed@>=
+ (k<" ")or(k>"~")
+
+@ When the \.{WEB} system program called \.{TANGLE} processes the \.{TEX.WEB}
+description that you are now reading, it outputs the \PASCAL\ program
+\.{TEX.PAS} and also a string pool file called \.{TEX.POOL}. The \.{INITEX}
+@.WEB@>@.INITEX@>
+program reads the latter file, where each string appears as a two-digit decimal
+length followed by the string itself, and the information is recorded in
+\TeX's string memory.
+
+@<Glob...@>=
+@!init @!pool_file:alpha_file; {the string-pool file output by \.{TANGLE}}
+tini
+
+@ @d bad_pool(#)==begin wake_up_terminal; write_ln(term_out,#);
+ a_close(pool_file); get_strings_started:=false; return;
+ end
+@<Read the other strings...@>=
+name_of_file:=pool_name; {we needn't set |name_length|}
+if a_open_in(pool_file) then
+ begin c:=false;
+ repeat @<Read one string, but return |false| if the
+ string memory space is getting too tight for comfort@>;
+ until c;
+ a_close(pool_file); get_strings_started:=true;
+ end
+else bad_pool('! I can''t read TEX.POOL.')
+@.I can't read TEX.POOL@>
+
+@ @<Read one string...@>=
+begin if eof(pool_file) then bad_pool('! TEX.POOL has no check sum.');
+@.TEX.POOL has no check sum@>
+read(pool_file,m,n); {read two digits of string length}
+if m='*' then @<Check the pool check sum@>
+else begin if (xord[m]<"0")or(xord[m]>"9")or@|
+ (xord[n]<"0")or(xord[n]>"9") then
+ bad_pool('! TEX.POOL line doesn''t begin with two digits.');
+@.TEX.POOL line doesn't...@>
+ l:=xord[m]*10+xord[n]-"0"*11; {compute the length}
+ if pool_ptr+l+string_vacancies>pool_size then
+ bad_pool('! You have to increase POOLSIZE.');
+@.You have to increase POOLSIZE@>
+ for k:=1 to l do
+ begin if eoln(pool_file) then m:=' '@+else read(pool_file,m);
+ append_char(xord[m]);
+ end;
+ read_ln(pool_file); g:=make_string;
+ end;
+end
+
+@ The \.{WEB} operation \.{@@\$} denotes the value that should be at the
+end of this \.{TEX.POOL} file; any other value means that the wrong pool
+file has been loaded.
+@^check sum@>
+
+@<Check the pool check sum@>=
+begin a:=0; k:=1;
+loop@+ begin if (xord[n]<"0")or(xord[n]>"9") then
+ bad_pool('! TEX.POOL check sum doesn''t have nine digits.');
+@.TEX.POOL check sum...@>
+ a:=10*a+xord[n]-"0";
+ if k=9 then goto done;
+ incr(k); read(pool_file,n);
+ end;
+done: if a<>@$ then bad_pool('! TEX.POOL doesn''t match; TANGLE me again.');
+@.TEX.POOL doesn't match@>
+c:=true;
+end
+
+@* \[5] On-line and off-line printing.
+Messages that are sent to a user's terminal and to the transcript-log file
+are produced by several `|print|' procedures. These procedures will
+direct their output to a variety of places, based on the setting of
+the global variable |selector|, which has the following possible
+values:
+
+\yskip
+\hang |term_and_log|, the normal setting, prints on the terminal and on the
+ transcript file.
+
+\hang |log_only|, prints only on the transcript file.
+
+\hang |term_only|, prints only on the terminal.
+
+\hang |no_print|, doesn't print at all. This is used only in rare cases
+ before the transcript file is open.
+
+\hang |pseudo|, puts output into a cyclic buffer that is used
+ by the |show_context| routine; when we get to that routine we shall discuss
+ the reasoning behind this curious mode.
+
+\hang |new_string|, appends the output to the current string in the
+ string pool.
+
+\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output.
+
+\yskip
+\noindent The symbolic names `|term_and_log|', etc., have been assigned
+numeric codes that satisfy the convenient relations |no_print+1=term_only|,
+|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|.
+
+Three additional global variables, |tally| and |term_offset| and
+|file_offset|, record the number of characters that have been printed
+since they were most recently cleared to zero. We use |tally| to record
+the length of (possibly very long) stretches of printing; |term_offset|
+and |file_offset|, on the other hand, keep track of how many characters
+have appeared so far on the current line that has been output to the
+terminal or to the transcript file, respectively.
+
+@d no_print=16 {|selector| setting that makes data disappear}
+@d term_only=17 {printing is destined for the terminal only}
+@d log_only=18 {printing is destined for the transcript file only}
+@d term_and_log=19 {normal |selector| setting}
+@d pseudo=20 {special |selector| setting for |show_context|}
+@d new_string=21 {printing is deflected to the string pool}
+@d max_selector=21 {highest selector setting}
+
+@<Glob...@>=
+@!log_file : alpha_file; {transcript of \TeX\ session}
+@!selector : 0..max_selector; {where to print a message}
+@!dig : array[0..22] of 0..15; {digits in a number being output}
+@!tally : integer; {the number of characters recently printed}
+@!term_offset : 0..max_print_line;
+ {the number of characters on the current terminal line}
+@!file_offset : 0..max_print_line;
+ {the number of characters on the current file line}
+@!trick_buf:array[0..error_line] of ASCII_code; {circular buffer for
+ pseudoprinting}
+@!trick_count: integer; {threshold for pseudoprinting, explained later}
+@!first_count: integer; {another variable for pseudoprinting}
+
+@ @<Initialize the output routines@>=
+selector:=term_only; tally:=0; term_offset:=0; file_offset:=0;
+
+@ Macro abbreviations for output to the terminal and to the log file are
+defined here for convenience. Some systems need special conventions
+for terminal output, and it is possible to adhere to those conventions
+by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section.
+@^system dependencies@>
+
+@d wterm(#)==write(term_out,#)
+@d wterm_ln(#)==write_ln(term_out,#)
+@d wterm_cr==write_ln(term_out)
+@d wlog(#)==write(log_file,#)
+@d wlog_ln(#)==write_ln(log_file,#)
+@d wlog_cr==write_ln(log_file)
+
+@ To end a line of text output, we call |print_ln|.
+
+@<Basic print...@>=
+procedure print_ln; {prints an end-of-line}
+begin case selector of
+term_and_log: begin wterm_cr; wlog_cr;
+ term_offset:=0; file_offset:=0;
+ end;
+log_only: begin wlog_cr; file_offset:=0;
+ end;
+term_only: begin wterm_cr; term_offset:=0;
+ end;
+no_print,pseudo,new_string: do_nothing;
+othercases write_ln(write_file[selector])
+endcases;@/
+end; {|tally| is not affected}
+
+@ The |print_char| procedure sends one character to the desired destination,
+using the |xchr| array to map it into an external character compatible with
+|input_ln|. All printing comes through |print_ln| or |print_char|.
+
+@<Basic printing...@>=
+procedure print_char(@!s:ASCII_code); {prints a single character}
+label exit;
+begin if @<Character |s| is the current new-line character@> then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+case selector of
+term_and_log: begin wterm(xchr[s]); wlog(xchr[s]);
+ incr(term_offset); incr(file_offset);
+ if term_offset=max_print_line then
+ begin wterm_cr; term_offset:=0;
+ end;
+ if file_offset=max_print_line then
+ begin wlog_cr; file_offset:=0;
+ end;
+ end;
+log_only: begin wlog(xchr[s]); incr(file_offset);
+ if file_offset=max_print_line then print_ln;
+ end;
+term_only: begin wterm(xchr[s]); incr(term_offset);
+ if term_offset=max_print_line then print_ln;
+ end;
+no_print: do_nothing;
+pseudo: if tally<trick_count then trick_buf[tally mod error_line]:=s;
+new_string: begin if pool_ptr<pool_size then append_char(s);
+ end; {we drop characters if the string space is full}
+othercases write(write_file[selector],xchr[s])
+endcases;@/
+incr(tally);
+exit:end;
+
+@ An entire string is output by calling |print|. Note that if we are outputting
+the single standard ASCII character \.c, we could call |print("c")|, since
+|"c"=99| is the number of a single-character string, as explained above. But
+|print_char("c")| is quicker, so \TeX\ goes directly to the |print_char|
+routine when it knows that this is safe. (The present implementation
+assumes that it is always safe to print a visible ASCII character.)
+@^system dependencies@>
+
+@<Basic print...@>=
+procedure print(@!s:integer); {prints string |s|}
+label exit;
+var j:pool_pointer; {current character code position}
+@!nl:integer; {new-line character to restore}
+begin if s>=str_ptr then s:="???" {this can't happen}
+@.???@>
+else if s<256 then
+ if s<0 then s:="???" {can't happen}
+ else begin if selector>pseudo then
+ begin print_char(s); return; {internal strings are not expanded}
+ end;
+ if (@<Character |s| is the current new-line character@>) then
+ if selector<pseudo then
+ begin print_ln; return;
+ end;
+ nl:=new_line_char; new_line_char:=-1;
+ {temporarily disable new-line character}
+ j:=str_start[s];
+ while j<str_start[s+1] do
+ begin print_char(so(str_pool[j])); incr(j);
+ end;
+ new_line_char:=nl; return;
+ end;
+j:=str_start[s];
+while j<str_start[s+1] do
+ begin print_char(so(str_pool[j])); incr(j);
+ end;
+exit:end;
+
+@ Control sequence names, file names, and strings constructed with
+\.{\\string} might contain |ASCII_code| values that can't
+be printed using |print_char|. Therefore we use |slow_print| for them:
+
+@<Basic print...@>=
+procedure slow_print(@!s:integer); {prints string |s|}
+var j:pool_pointer; {current character code position}
+begin if (s>=str_ptr) or (s<256) then print(s)
+else begin j:=str_start[s];
+ while j<str_start[s+1] do
+ begin print(so(str_pool[j])); incr(j);
+ end;
+ end;
+end;
+
+@ Here is the very first thing that \TeX\ prints: a headline that identifies
+the version number and format package. The |term_offset| variable is temporarily
+incorrect, but the discrepancy is not serious since we assume that the banner
+and format identifier together will occupy at most |max_print_line|
+character positions.
+
+@<Initialize the output...@>=
+wterm(banner);
+if format_ident=0 then wterm_ln(' (no format preloaded)')
+else begin slow_print(format_ident); print_ln;
+ end;
+update_terminal;
+
+@ The procedure |print_nl| is like |print|, but it makes sure that the
+string appears at the beginning of a new line.
+
+@<Basic print...@>=
+procedure print_nl(@!s:str_number); {prints string |s| at beginning of line}
+begin if ((term_offset>0)and(odd(selector)))or@|
+ ((file_offset>0)and(selector>=log_only)) then print_ln;
+print(s);
+end;
+
+@ The procedure |print_esc| prints a string that is preceded by
+the user's escape character (which is usually a backslash).
+
+@<Basic print...@>=
+procedure print_esc(@!s:str_number); {prints escape character, then |s|}
+var c:integer; {the escape character code}
+begin @<Set variable |c| to the current escape character@>;
+if c>=0 then if c<256 then print(c);
+slow_print(s);
+end;
+
+@ An array of digits in the range |0..15| is printed by |print_the_digs|.
+
+@<Basic print...@>=
+procedure print_the_digs(@!k:eight_bits);
+ {prints |dig[k-1]|$\,\ldots\,$|dig[0]|}
+begin while k>0 do
+ begin decr(k);
+ if dig[k]<10 then print_char("0"+dig[k])
+ else print_char("A"-10+dig[k]);
+ end;
+end;
+
+@ The following procedure, which prints out the decimal representation of a
+given integer |n|, has been written carefully so that it works properly
+if |n=0| or if |(-n)| would cause overflow. It does not apply |mod| or |div|
+to negative arguments, since such operations are not implemented consistently
+by all \PASCAL\ compilers.
+
+@<Basic print...@>=
+procedure print_int(@!n:integer); {prints an integer in decimal form}
+var k:0..23; {index to current digit; we assume that $|n|<10^{23}$}
+@!m:integer; {used to negate |n| in possibly dangerous cases}
+begin k:=0;
+if n<0 then
+ begin print_char("-");
+ if n>-100000000 then negate(n)
+ else begin m:=-1-n; n:=m div 10; m:=(m mod 10)+1; k:=1;
+ if m<10 then dig[0]:=m
+ else begin dig[0]:=0; incr(n);
+ end;
+ end;
+ end;
+repeat dig[k]:=n mod 10; n:=n div 10; incr(k);
+until n=0;
+print_the_digs(k);
+end;
+
+@ Here is a trivial procedure to print two digits; it is usually called with
+a parameter in the range |0<=n<=99|.
+
+@p procedure print_two(@!n:integer); {prints two least significant digits}
+begin n:=abs(n) mod 100; print_char("0"+(n div 10));
+print_char("0"+(n mod 10));
+end;
+
+@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|.
+
+@p procedure print_hex(@!n:integer);
+ {prints a positive integer in hexadecimal form}
+var k:0..22; {index to current digit; we assume that $0\L n<16^{22}$}
+begin k:=0; print_char("""");
+repeat dig[k]:=n mod 16; n:=n div 16; incr(k);
+until n=0;
+print_the_digs(k);
+end;
+
+@ Old versions of \TeX\ needed a procedure called |print_ASCII| whose function
+is now subsumed by |print|. We retain the old name here as a possible aid to
+future software arch\ae ologists.
+
+@d print_ASCII == print
+
+@ Roman numerals are produced by the |print_roman_int| routine. Readers
+who like puzzles might enjoy trying to figure out how this tricky code
+works; therefore no explanation will be given. Notice that 1990 yields
+\.{mcmxc}, not \.{mxm}.
+
+@p procedure print_roman_int(@!n:integer);
+label exit;
+var j,@!k: pool_pointer; {mysterious indices into |str_pool|}
+@!u,@!v: nonnegative_integer; {mysterious numbers}
+begin j:=str_start["m2d5c2l5x2v5i"]; v:=1000;
+loop@+ begin while n>=v do
+ begin print_char(so(str_pool[j])); n:=n-v;
+ end;
+ if n<=0 then return; {nonpositive input produces no output}
+ k:=j+2; u:=v div (so(str_pool[k-1])-"0");
+ if str_pool[k-1]=si("2") then
+ begin k:=k+2; u:=u div (so(str_pool[k-1])-"0");
+ end;
+ if n+u>=v then
+ begin print_char(so(str_pool[k])); n:=n+u;
+ end
+ else begin j:=j+2; v:=v div (so(str_pool[j-1])-"0");
+ end;
+ end;
+exit:end;
+
+@ The |print| subroutine will not print a string that is still being
+created. The following procedure will.
+
+@p procedure print_current_string; {prints a yet-unmade string}
+var j:pool_pointer; {points to current character code}
+begin j:=str_start[str_ptr];
+while j<pool_ptr do
+ begin print_char(so(str_pool[j])); incr(j);
+ end;
+end;
+
+@ Here is a procedure that asks the user to type a line of input,
+assuming that the |selector| setting is either |term_only| or |term_and_log|.
+The input is placed into locations |first| through |last-1| of the
+|buffer| array, and echoed on the transcript file if appropriate.
+
+This procedure is never called when |interaction<scroll_mode|.
+
+@d prompt_input(#)==begin wake_up_terminal; print(#); term_input;
+ end {prints a string and gets a line of input}
+
+@p procedure term_input; {gets a line from the terminal}
+var k:0..buf_size; {index into |buffer|}
+begin update_terminal; {now the user sees the prompt for sure}
+if not input_ln(term_in,true) then fatal_error("End of file on the terminal!");
+@.End of file on the terminal@>
+term_offset:=0; {the user's line ended with \<\rm return>}
+decr(selector); {prepare to echo the input}
+if last<>first then for k:=first to last-1 do print(buffer[k]);
+print_ln; incr(selector); {restore previous status}
+end;
+
+@* \[6] Reporting errors.
+When something anomalous is detected, \TeX\ typically does something like this:
+$$\vbox{\halign{#\hfil\cr
+|print_err("Something anomalous has been detected");|\cr
+|help3("This is the first line of my offer to help.")|\cr
+|("This is the second line. I'm trying to")|\cr
+|("explain the best way for you to proceed.");|\cr
+|error;|\cr}}$$
+A two-line help message would be given using |help2|, etc.; these informal
+helps should use simple vocabulary that complements the words used in the
+official error message that was printed. (Outside the U.S.A., the help
+messages should preferably be translated into the local vernacular. Each
+line of help is at most 60 characters long, in the present implementation,
+so that |max_print_line| will not be exceeded.)
+
+The |print_err| procedure supplies a `\.!' before the official message,
+and makes sure that the terminal is awake if a stop is going to occur.
+The |error| procedure supplies a `\..' after the official message, then it
+shows the location of the error; and if |interaction=error_stop_mode|,
+it also enters into a dialog with the user, during which time the help
+message may be printed.
+@^system dependencies@>
+
+@ The global variable |interaction| has four settings, representing increasing
+amounts of user interaction:
+
+@d batch_mode=0 {omits all stops and omits terminal output}
+@d nonstop_mode=1 {omits all stops}
+@d scroll_mode=2 {omits error stops}
+@d error_stop_mode=3 {stops at every opportunity to interact}
+@d print_err(#)==begin if interaction=error_stop_mode then wake_up_terminal;
+ print_nl("! "); print(#);
+ end
+
+@<Glob...@>=
+@!interaction:batch_mode..error_stop_mode; {current level of interaction}
+
+@ @<Set init...@>=interaction:=error_stop_mode;
+
+@ \TeX\ is careful not to call |error| when the print |selector| setting
+might be unusual. The only possible values of |selector| at the time of
+error messages are
+
+\yskip\hang|no_print| (when |interaction=batch_mode|
+ and |log_file| not yet open);
+
+\hang|term_only| (when |interaction>batch_mode| and |log_file| not yet open);
+
+\hang|log_only| (when |interaction=batch_mode| and |log_file| is open);
+
+\hang|term_and_log| (when |interaction>batch_mode| and |log_file| is open).
+
+@<Initialize the print |selector| based on |interaction|@>=
+if interaction=batch_mode then selector:=no_print@+else selector:=term_only
+
+@ A global variable |deletions_allowed| is set |false| if the |get_next|
+routine is active when |error| is called; this ensures that |get_next|
+and related routines like |get_token| will never be called recursively.
+A similar interlock is provided by |set_box_allowed|.
+@^recursion@>
+
+The global variable |history| records the worst level of error that
+has been detected. It has four possible values: |spotless|, |warning_issued|,
+|error_message_issued|, and |fatal_error_stop|.
+
+Another global variable, |error_count|, is increased by one when an
+|error| occurs without an interactive dialog, and it is reset to zero at
+the end of every paragraph. If |error_count| reaches 100, \TeX\ decides
+that there is no point in continuing further.
+
+@d spotless=0 {|history| value when nothing has been amiss yet}
+@d warning_issued=1 {|history| value when |begin_diagnostic| has been called}
+@d error_message_issued=2 {|history| value when |error| has been called}
+@d fatal_error_stop=3 {|history| value when termination was premature}
+
+@<Glob...@>=
+@!deletions_allowed:boolean; {is it safe for |error| to call |get_token|?}
+@!set_box_allowed:boolean; {is it safe to do a \.{\\setbox} assignment?}
+@!history:spotless..fatal_error_stop; {has the source input been clean so far?}
+@!error_count:-1..100; {the number of scrolled errors since the
+ last paragraph ended}
+
+@ The value of |history| is initially |fatal_error_stop|, but it will
+be changed to |spotless| if \TeX\ survives the initialization process.
+
+@<Set init...@>=
+deletions_allowed:=true; set_box_allowed:=true;
+error_count:=0; {|history| is initialized elsewhere}
+
+@ Since errors can be detected almost anywhere in \TeX, we want to declare the
+error procedures near the beginning of the program. But the error procedures
+in turn use some other procedures, which need to be declared |forward|
+before we get to |error| itself.
+
+It is possible for |error| to be called recursively if some error arises
+when |get_token| is being used to delete a token, and/or if some fatal error
+occurs while \TeX\ is trying to fix a non-fatal one. But such recursion
+@^recursion@>
+is never more than two levels deep.
+
+@<Error handling...@>=
+procedure@?normalize_selector; forward;@t\2@>@/
+procedure@?get_token; forward;@t\2@>@/
+procedure@?term_input; forward;@t\2@>@/
+procedure@?show_context; forward;@t\2@>@/
+procedure@?begin_file_reading; forward;@t\2@>@/
+procedure@?open_log_file; forward;@t\2@>@/
+procedure@?close_files_and_terminate; forward;@t\2@>@/
+procedure@?clear_for_error_prompt; forward;@t\2@>@/
+procedure@?give_err_help; forward;@t\2@>@/
+@t\4\hskip-\fontdimen2\font@>@;@+@!debug@+procedure@?debug_help;
+ forward;@;@+gubed
+
+@ Individual lines of help are recorded in the array |help_line|, which
+contains entries in positions |0..(help_ptr-1)|. They should be printed
+in reverse order, i.e., with |help_line[0]| appearing last.
+
+@d hlp1(#)==help_line[0]:=#;@+end
+@d hlp2(#)==help_line[1]:=#; hlp1
+@d hlp3(#)==help_line[2]:=#; hlp2
+@d hlp4(#)==help_line[3]:=#; hlp3
+@d hlp5(#)==help_line[4]:=#; hlp4
+@d hlp6(#)==help_line[5]:=#; hlp5
+@d help0==help_ptr:=0 {sometimes there might be no help}
+@d help1==@+begin help_ptr:=1; hlp1 {use this with one help line}
+@d help2==@+begin help_ptr:=2; hlp2 {use this with two help lines}
+@d help3==@+begin help_ptr:=3; hlp3 {use this with three help lines}
+@d help4==@+begin help_ptr:=4; hlp4 {use this with four help lines}
+@d help5==@+begin help_ptr:=5; hlp5 {use this with five help lines}
+@d help6==@+begin help_ptr:=6; hlp6 {use this with six help lines}
+
+@<Glob...@>=
+@!help_line:array[0..5] of str_number; {helps for the next |error|}
+@!help_ptr:0..6; {the number of help lines present}
+@!use_err_help:boolean; {should the |err_help| list be shown?}
+
+@ @<Set init...@>=
+help_ptr:=0; use_err_help:=false;
+
+@ The |jump_out| procedure just cuts across all active procedure levels and
+goes to |end_of_TEX|. This is the only nontrivial |@!goto| statement in the
+whole program. It is used when there is no recovery from a particular error.
+
+Some \PASCAL\ compilers do not implement non-local |goto| statements.
+@^system dependencies@>
+In such cases the body of |jump_out| should simply be
+`|close_files_and_terminate|;\thinspace' followed by a call on some system
+procedure that quietly terminates the program.
+
+@<Error hand...@>=
+procedure jump_out;
+begin goto end_of_TEX;
+end;
+
+@ Here now is the general |error| routine.
+
+@<Error hand...@>=
+procedure error; {completes the job of error reporting}
+label continue,exit;
+var c:ASCII_code; {what the user types}
+@!s1,@!s2,@!s3,@!s4:integer;
+ {used to save global variables when deleting tokens}
+begin if history<error_message_issued then history:=error_message_issued;
+print_char("."); show_context;
+if interaction=error_stop_mode then @<Get user's advice and |return|@>;
+incr(error_count);
+if error_count=100 then
+ begin print_nl("(That makes 100 errors; please try again.)");
+@.That makes 100 errors...@>
+ history:=fatal_error_stop; jump_out;
+ end;
+@<Put help message on the transcript file@>;
+exit:end;
+
+@ @<Get user's advice...@>=
+loop@+begin continue: clear_for_error_prompt; prompt_input("? ");
+@.?\relax@>
+ if last=first then return;
+ c:=buffer[first];
+ if c>="a" then c:=c+"A"-"a"; {convert to uppercase}
+ @<Interpret code |c| and |return| if done@>;
+ end
+
+@ It is desirable to provide an `\.E' option here that gives the user
+an easy way to return from \TeX\ to the system editor, with the offending
+line ready to be edited. But such an extension requires some system
+wizardry, so the present implementation simply types out the name of the
+file that should be
+edited and the relevant line number.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines haven't
+been commented~out.
+@^debugging@>
+
+@<Interpret code |c| and |return| if done@>=
+case c of
+"0","1","2","3","4","5","6","7","8","9": if deletions_allowed then
+ @<Delete \(c)|c-"0"| tokens and |goto continue|@>;
+@t\4\4@>@;@+@!debug "D": begin debug_help; goto continue;@+end;@+gubed@/
+"E": if base_ptr>0 then
+ begin print_nl("You want to edit file ");
+@.You want to edit file x@>
+ slow_print(input_stack[base_ptr].name_field);
+ print(" at line "); print_int(line);
+ interaction:=scroll_mode; jump_out;
+ end;
+"H": @<Print the help information and |goto continue|@>;
+"I":@<Introduce new material from the terminal and |return|@>;
+"Q","R","S":@<Change the interaction level and |return|@>;
+"X":begin interaction:=scroll_mode; jump_out;
+ end;
+othercases do_nothing
+endcases;@/
+@<Print the menu of available options@>
+
+@ @<Print the menu...@>=
+begin print("Type <return> to proceed, S to scroll future error messages,");@/
+@.Type <return> to proceed...@>
+print_nl("R to run without stopping, Q to run quietly,");@/
+print_nl("I to insert something, ");
+if base_ptr>0 then print("E to edit your file,");
+if deletions_allowed then
+ print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
+print_nl("H for help, X to quit.");
+end
+
+@ Here the author of \TeX\ apologizes for making use of the numerical
+relation between |"Q"|, |"R"|, |"S"|, and the desired interaction settings
+|batch_mode|, |nonstop_mode|, |scroll_mode|.
+@^Knuth, Donald Ervin@>
+
+@<Change the interaction...@>=
+begin error_count:=0; interaction:=batch_mode+c-"Q";
+print("OK, entering ");
+case c of
+"Q":begin print_esc("batchmode"); decr(selector);
+ end;
+"R":print_esc("nonstopmode");
+"S":print_esc("scrollmode");
+end; {there are no other cases}
+print("..."); print_ln; update_terminal; return;
+end
+
+@ When the following code is executed, |buffer[(first+1)..(last-1)]| may
+contain the material inserted by the user; otherwise another prompt will
+be given. In order to understand this part of the program fully, you need
+to be familiar with \TeX's input stacks.
+
+@<Introduce new material...@>=
+begin begin_file_reading; {enter a new syntactic level for terminal input}
+{now |state=mid_line|, so an initial blank space will count as a blank}
+if last>first+1 then
+ begin loc:=first+1; buffer[first]:=" ";
+ end
+else begin prompt_input("insert>"); loc:=first;
+@.insert>@>
+ end;
+first:=last;
+cur_input.limit_field:=last-1; {no |end_line_char| ends this line}
+return;
+end
+
+@ We allow deletion of up to 99 tokens at a time.
+
+@<Delete \(c)|c-"0"| tokens...@>=
+begin s1:=cur_tok; s2:=cur_cmd; s3:=cur_chr; s4:=align_state;
+align_state:=1000000; OK_to_interrupt:=false;
+if (last>first+1) and (buffer[first+1]>="0")and(buffer[first+1]<="9") then
+ c:=c*10+buffer[first+1]-"0"*11
+else c:=c-"0";
+while c>0 do
+ begin get_token; {one-level recursive call of |error| is possible}
+ decr(c);
+ end;
+cur_tok:=s1; cur_cmd:=s2; cur_chr:=s3; align_state:=s4; OK_to_interrupt:=true;
+help2("I have just deleted some text, as you asked.")@/
+("You can now delete more, or insert, or whatever.");
+show_context; goto continue;
+end
+
+@ @<Print the help info...@>=
+begin if use_err_help then
+ begin give_err_help; use_err_help:=false;
+ end
+else begin if help_ptr=0 then
+ help2("Sorry, I don't know how to help in this situation.")@/
+ @t\kern1em@>("Maybe you should try asking a human?");
+ repeat decr(help_ptr); print(help_line[help_ptr]); print_ln;
+ until help_ptr=0;
+ end;
+help4("Sorry, I already gave what help I could...")@/
+ ("Maybe you should try asking a human?")@/
+ ("An error might have occurred before I noticed any problems.")@/
+ ("``If all else fails, read the instructions.''");@/
+goto continue;
+end
+
+@ @<Put help message on the transcript file@>=
+if interaction>batch_mode then decr(selector); {avoid terminal output}
+if use_err_help then
+ begin print_ln; give_err_help;
+ end
+else while help_ptr>0 do
+ begin decr(help_ptr); print_nl(help_line[help_ptr]);
+ end;
+print_ln;
+if interaction>batch_mode then incr(selector); {re-enable terminal output}
+print_ln
+
+@ A dozen or so error messages end with a parenthesized integer, so we
+save a teeny bit of program space by declaring the following procedure:
+
+@p procedure int_error(@!n:integer);
+begin print(" ("); print_int(n); print_char(")"); error;
+end;
+
+@ In anomalous cases, the print selector might be in an unknown state;
+the following subroutine is called to fix things just enough to keep
+running a bit longer.
+
+@p procedure normalize_selector;
+begin if log_opened then selector:=term_and_log
+else selector:=term_only;
+if job_name=0 then open_log_file;
+if interaction=batch_mode then decr(selector);
+end;
+
+@ The following procedure prints \TeX's last words before dying.
+
+@d succumb==begin if interaction=error_stop_mode then
+ interaction:=scroll_mode; {no more interaction}
+ if log_opened then error;
+ @!debug if interaction>batch_mode then debug_help;@+gubed@;@/
+ history:=fatal_error_stop; jump_out; {irrecoverable error}
+ end
+
+@<Error hand...@>=
+procedure fatal_error(@!s:str_number); {prints |s|, and that's it}
+begin normalize_selector;@/
+print_err("Emergency stop"); help1(s); succumb;
+@.Emergency stop@>
+end;
+
+@ Here is the most dreaded error message.
+
+@<Error hand...@>=
+procedure overflow(@!s:str_number;@!n:integer); {stop due to finiteness}
+begin normalize_selector;
+print_err("TeX capacity exceeded, sorry [");
+@.TeX capacity exceeded ...@>
+print(s); print_char("="); print_int(n); print_char("]");
+help2("If you really absolutely need more capacity,")@/
+ ("you can ask a wizard to enlarge me.");
+succumb;
+end;
+
+@ The program might sometime run completely amok, at which point there is
+no choice but to stop. If no previous error has been detected, that's bad
+news; a message is printed that is really intended for the \TeX\
+maintenance person instead of the user (unless the user has been
+particularly diabolical). The index entries for `this can't happen' may
+help to pinpoint the problem.
+@^dry rot@>
+
+@<Error hand...@>=
+procedure confusion(@!s:str_number);
+ {consistency check violated; |s| tells where}
+begin normalize_selector;
+if history<error_message_issued then
+ begin print_err("This can't happen ("); print(s); print_char(")");
+@.This can't happen@>
+ help1("I'm broken. Please show this to someone who can fix can fix");
+ end
+else begin print_err("I can't go on meeting you like this");
+@.I can't go on...@>
+ help2("One of your faux pas seems to have wounded me deeply...")@/
+ ("in fact, I'm barely conscious. Please fix it and try again.");
+ end;
+succumb;
+end;
+
+@ Users occasionally want to interrupt \TeX\ while it's running.
+If the \PASCAL\ runtime system allows this, one can implement
+a routine that sets the global variable |interrupt| to some nonzero value
+when such an interrupt is signalled. Otherwise there is probably at least
+a way to make |interrupt| nonzero using the \PASCAL\ debugger.
+@^system dependencies@>
+@^debugging@>
+
+@d check_interrupt==begin if interrupt<>0 then pause_for_instructions;
+ end
+
+@<Global...@>=
+@!interrupt:integer; {should \TeX\ pause for instructions?}
+@!OK_to_interrupt:boolean; {should interrupts be observed?}
+
+@ @<Set init...@>=
+interrupt:=0; OK_to_interrupt:=true;
+
+@ When an interrupt has been detected, the program goes into its
+highest interaction level and lets the user have nearly the full flexibility of
+the |error| routine. \TeX\ checks for interrupts only at times when it is
+safe to do this.
+
+@p procedure pause_for_instructions;
+begin if OK_to_interrupt then
+ begin interaction:=error_stop_mode;
+ if (selector=log_only)or(selector=no_print) then
+ incr(selector);
+ print_err("Interruption");
+@.Interruption@>
+ help3("You rang?")@/
+ ("Try to insert some instructions for me (e.g.,`I\showlists'),")@/
+ ("unless you just want to quit by typing `X'.");
+ deletions_allowed:=false; error; deletions_allowed:=true;
+ interrupt:=0;
+ end;
+end;
+
+@* \[7] Arithmetic with scaled dimensions.
+The principal computations performed by \TeX\ are done entirely in terms of
+integers less than $2^{31}$ in magnitude; and divisions are done only when both
+dividend and divisor are nonnegative. Thus, the arithmetic specified in this
+program can be carried out in exactly the same way on a wide variety of
+computers, including some small ones. Why? Because the arithmetic
+calculations need to be spelled out precisely in order to guarantee that
+\TeX\ will produce identical output on different machines. If some
+quantities were rounded differently in different implementations, we would
+find that line breaks and even page breaks might occur in different places.
+Hence the arithmetic of \TeX\ has been designed with care, and systems that
+claim to be implementations of \TeX82 should follow precisely the
+@:TeX82}{\TeX82@>
+calculations as they appear in the present program.
+
+(Actually there are three places where \TeX\ uses |div| with a possibly negative
+numerator. These are harmless; see |div| in the index. Also if the user
+sets the \.{\\time} or the \.{\\year} to a negative value, some diagnostic
+information will involve negative-numerator division. The same remarks
+apply for |mod| as well as for |div|.)
+
+@ Here is a routine that calculates half of an integer, using an
+unambiguous convention with respect to signed odd numbers.
+
+@p function half(@!x:integer):integer;
+begin if odd(x) then half:=(x+1) div 2
+else half:=x @!div 2;
+end;
+
+@ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples
+of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit
+positions from the right end of a binary computer word.
+
+@d unity == @'200000 {$2^{16}$, represents 1.00000}
+@d two == @'400000 {$2^{17}$, represents 2.00000}
+
+@<Types...@>=
+@!scaled = integer; {this type is used for scaled integers}
+@!nonnegative_integer=0..@'17777777777; {$0\L x<2^{31}$}
+@!small_number=0..63; {this type is self-explanatory}
+
+@ The following function is used to create a scaled integer from a given decimal
+fraction $(.d_0d_1\ldots d_{k-1})$, where |0<=k<=17|. The digit $d_i$ is
+given in |dig[i]|, and the calculation produces a correctly rounded result.
+
+@p function round_decimals(@!k:small_number) : scaled;
+ {converts a decimal fraction}
+var a:integer; {the accumulator}
+begin a:=0;
+while k>0 do
+ begin decr(k); a:=(a+dig[k]*two) div 10;
+ end;
+round_decimals:=(a+1) div 2;
+end;
+
+@ Conversely, here is a procedure analogous to |print_int|. If the output
+of this procedure is subsequently read by \TeX\ and converted by the
+|round_decimals| routine above, it turns out that the original value will
+be reproduced exactly; the ``simplest'' such decimal number is output,
+but there is always at least one digit following the decimal point.
+
+The invariant relation in the \&{repeat} loop is that a sequence of
+decimal digits yet to be printed will yield the original number if and only if
+they form a fraction~$f$ in the range $s-\delta\L10\cdot2^{16}f<s$.
+We can stop if and only if $f=0$ satisfies this condition; the loop will
+terminate before $s$ can possibly become zero.
+
+@p procedure print_scaled(@!s:scaled); {prints scaled real, rounded to five
+ digits}
+var delta:scaled; {amount of allowable inaccuracy}
+begin if s<0 then
+ begin print_char("-"); negate(s); {print the sign, if negative}
+ end;
+print_int(s div unity); {print the integer part}
+print_char(".");
+s:=10*(s mod unity)+5; delta:=10;
+repeat if delta>unity then s:=s+@'100000-50000; {round the last digit}
+print_char("0"+(s div unity)); s:=10*(s mod unity); delta:=delta*10;
+until s<=delta;
+end;
+
+@ Physical sizes that a \TeX\ user specifies for portions of documents are
+represented internally as scaled points. Thus, if we define an `sp' (scaled
+@^sp@>
+point) as a unit equal to $2^{-16}$ printer's points, every dimension
+inside of \TeX\ is an integer number of sp. There are exactly
+4,736,286.72 sp per inch. Users are not allowed to specify dimensions
+larger than $2^{30}-1$ sp, which is a distance of about 18.892 feet (5.7583
+meters); two such quantities can be added without overflow on a 32-bit
+computer.
+
+The present implementation of \TeX\ does not check for overflow when
+@^overflow in arithmetic@>
+dimensions are added or subtracted. This could be done by inserting a
+few dozen tests of the form `\ignorespaces|if x>=@'10000000000 then
+@t\\{report\_overflow}@>|', but the chance of overflow is so remote that
+such tests do not seem worthwhile.
+
+\TeX\ needs to do only a few arithmetic operations on scaled quantities,
+other than addition and subtraction, and the following subroutines do most of
+the work. A single computation might use several subroutine calls, and it is
+desirable to avoid producing multiple error messages in case of arithmetic
+overflow; so the routines set the global variable |arith_error| to |true|
+instead of reporting errors directly to the user. Another global variable,
+|remainder|, holds the remainder after a division.
+
+@<Glob...@>=
+@!arith_error:boolean; {has arithmetic overflow occurred recently?}
+@!remainder:scaled; {amount subtracted to get an exact division}
+
+@ The first arithmetical subroutine we need computes $nx+y$, where |x|
+and~|y| are |scaled| and |n| is an integer. We will also use it to
+multiply integers.
+
+@d nx_plus_y(#)==mult_and_add(#,@'7777777777)
+@d mult_integers(#)==mult_and_add(#,0,@'17777777777)
+
+@p function mult_and_add(@!n:integer;@!x,@!y,@!max_answer:scaled):scaled;
+begin if n<0 then
+ begin negate(x); negate(n);
+ end;
+if n=0 then mult_and_add:=y
+else if ((x<=(max_answer-y) div n)and(-x<=(max_answer+y) div n)) then
+ mult_and_add:=n*x+y
+else begin arith_error:=true; mult_and_add:=0;
+ end;
+end;
+
+@ We also need to divide scaled dimensions by integers.
+
+@p function x_over_n(@!x:scaled;@!n:integer):scaled;
+var negative:boolean; {should |remainder| be negated?}
+begin negative:=false;
+if n=0 then
+ begin arith_error:=true; x_over_n:=0; remainder:=x;
+ end
+else begin if n<0 then
+ begin negate(x); negate(n); negative:=true;
+ end;
+ if x>=0 then
+ begin x_over_n:=x div n; remainder:=x mod n;
+ end
+ else begin x_over_n:=-((-x) div n); remainder:=-((-x) mod n);
+ end;
+ end;
+if negative then negate(remainder);
+end;
+
+@ Then comes the multiplication of a scaled number by a fraction |n/d|,
+where |n| and |d| are nonnegative integers |<=@t$2^{16}$@>| and |d| is
+positive. It would be too dangerous to multiply by~|n| and then divide
+by~|d|, in separate operations, since overflow might well occur; and it
+would be too inaccurate to divide by |d| and then multiply by |n|. Hence
+this subroutine simulates 1.5-precision arithmetic.
+
+@p function xn_over_d(@!x:scaled; @!n,@!d:integer):scaled;
+var positive:boolean; {was |x>=0|?}
+@!t,@!u,@!v:nonnegative_integer; {intermediate quantities}
+begin if x>=0 then positive:=true
+else begin negate(x); positive:=false;
+ end;
+t:=(x mod @'100000)*n;
+u:=(x div @'100000)*n+(t div @'100000);
+v:=(u mod d)*@'100000 + (t mod @'100000);
+if u div d>=@'100000 then arith_error:=true
+else u:=@'100000*(u div d) + (v div d);
+if positive then
+ begin xn_over_d:=u; remainder:=v mod d;
+ end
+else begin xn_over_d:=-u; remainder:=-(v mod d);
+ end;
+end;
+
+@ The next subroutine is used to compute the ``badness'' of glue, when a
+total~|t| is supposed to be made from amounts that sum to~|s|. According
+to {\sl The \TeX book}, the badness of this situation is $100(t/s)^3$;
+however, badness is simply a heuristic, so we need not squeeze out the
+last drop of accuracy when computing it. All we really want is an
+approximation that has similar properties.
+@:TeXbook}{\sl The \TeX book@>
+
+The actual method used to compute the badness is easier to read from the
+program than to describe in words. It produces an integer value that is a
+reasonably close approximation to $100(t/s)^3$, and all implementations
+of \TeX\ should use precisely this method. Any badness of $2^{13}$ or more is
+treated as infinitely bad, and represented by 10000.
+
+It is not difficult to prove that $$\hbox{|badness(t+1,s)>=badness(t,s)
+>=badness(t,s+1)|}.$$ The badness function defined here is capable of
+computing at most 1095 distinct values, but that is plenty.
+
+@d inf_bad = 10000 {infinitely bad value}
+
+@p function badness(@!t,@!s:scaled):halfword; {compute badness, given |t>=0|}
+var r:integer; {approximation to $\alpha t/s$, where $\alpha^3\approx
+ 100\cdot2^{18}$}
+begin if t=0 then badness:=0
+else if s<=0 then badness:=inf_bad
+else begin if t<=7230584 then r:=(t*297) div s {$297^3=99.94\times2^{18}$}
+ else if s>=1663497 then r:=t div (s div 297)
+ else r:=t;
+ if r>1290 then badness:=inf_bad {$1290^3<2^{31}<1291^3$}
+ else badness:=(r*r*r+@'400000) div @'1000000;
+ end; {that was $r^3/2^{18}$, rounded to the nearest integer}
+end;
+
+@ When \TeX\ ``packages'' a list into a box, it needs to calculate the
+proportionality ratio by which the glue inside the box should stretch
+or shrink. This calculation does not affect \TeX's decision making,
+so the precise details of rounding, etc., in the glue calculation are not
+of critical importance for the consistency of results on different computers.
+
+We shall use the type |glue_ratio| for such proportionality ratios.
+A glue ratio should take the same amount of memory as an
+|integer| (usually 32 bits) if it is to blend smoothly with \TeX's
+other data structures. Thus |glue_ratio| should be equivalent to
+|short_real| in some implementations of \PASCAL. Alternatively,
+it is possible to deal with glue ratios using nothing but fixed-point
+arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the
+routines cited there must be modified to allow negative glue ratios.)
+@^system dependencies@>
+
+@d set_glue_ratio_zero(#) == #:=0.0 {store the representation of zero ratio}
+@d set_glue_ratio_one(#) == #:=1.0 {store the representation of unit ratio}
+@d float(#) == # {convert from |glue_ratio| to type |real|}
+@d unfloat(#) == # {convert from |real| to type |glue_ratio|}
+@d float_constant(#) == #.0 {convert |integer| constant to |real|}
+
+@<Types...@>=
+@!glue_ratio=real; {one-word representation of a glue expansion factor}
+
+@* \[8] Packed data.
+In order to make efficient use of storage space, \TeX\ bases its major data
+structures on a |memory_word|, which contains either a (signed) integer,
+possibly scaled, or a (signed) |glue_ratio|, or a small number of
+fields that are one half or one quarter of the size used for storing
+integers.
+
+If |x| is a variable of type |memory_word|, it contains up to four
+fields that can be referred to as follows:
+$$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr
+|x|&.|int|&(an |integer|)\cr
+|x|&.|sc|\qquad&(a |scaled| integer)\cr
+|x|&.|gr|&(a |glue_ratio|)\cr
+|x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr
+|x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword
+ field)\cr
+|x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt
+ &\qquad\qquad\qquad(four quarterword fields)\cr}}$$
+This is somewhat cumbersome to write, and not very readable either, but
+macros will be used to make the notation shorter and more transparent.
+The \PASCAL\ code below gives a formal definition of |memory_word| and
+its subsidiary types, using packed variant records. \TeX\ makes no
+assumptions about the relative positions of the fields within a word.
+
+Since we are assuming 32-bit integers, a halfword must contain at least
+16 bits, and a quarterword must contain at least 8 bits.
+@^system dependencies@>
+But it doesn't hurt to have more bits; for example, with enough 36-bit
+words you might be able to have |mem_max| as large as 262142, which is
+eight times as much memory as anybody had during the first four years of
+\TeX's existence.
+
+N.B.: Valuable memory space will be dreadfully wasted unless \TeX\ is compiled
+by a \PASCAL\ that packs all of the |memory_word| variants into
+the space of a single integer. This means, for example, that |glue_ratio|
+words should be |short_real| instead of |real| on some computers. Some
+\PASCAL\ compilers will pack an integer whose subrange is `|0..255|' into
+an eight-bit field, but others insist on allocating space for an additional
+sign bit; on such systems you can get 256 values into a quarterword only
+if the subrange is `|-128..127|'.
+
+The present implementation tries to accommodate as many variations as possible,
+so it makes few assumptions. If integers having the subrange
+`|min_quarterword..max_quarterword|' can be packed into a quarterword,
+and if integers having the subrange `|min_halfword..max_halfword|'
+can be packed into a halfword, everything should work satisfactorily.
+
+It is usually most efficient to have |min_quarterword=min_halfword=0|,
+so one should try to achieve this unless it causes a severe problem.
+The values defined here are recommended for most 32-bit computers.
+
+@d min_quarterword=0 {smallest allowable value in a |quarterword|}
+@d max_quarterword=255 {largest allowable value in a |quarterword|}
+@d min_halfword==0 {smallest allowable value in a |halfword|}
+@d max_halfword==65535 {largest allowable value in a |halfword|}
+
+@ Here are the inequalities that the quarterword and halfword values
+must satisfy (or rather, the inequalities that they mustn't satisfy):
+
+@<Check the ``constant''...@>=
+init if (mem_min<>mem_bot)or(mem_max<>mem_top) then bad:=10;@+tini@;@/
+if (mem_min>mem_bot)or(mem_max<mem_top) then bad:=10;
+if (min_quarterword>0)or(max_quarterword<127) then bad:=11;
+if (min_halfword>0)or(max_halfword<32767) then bad:=12;
+if (min_quarterword<min_halfword)or@|
+ (max_quarterword>max_halfword) then bad:=13;
+if (mem_min<min_halfword)or(mem_max>=max_halfword)or@|
+ (mem_bot-mem_min>max_halfword+1) then bad:=14;
+if (font_base<min_quarterword)or(font_max>max_quarterword) then bad:=15;
+if font_max>font_base+256 then bad:=16;
+if (save_size>max_halfword)or(max_strings>max_halfword) then bad:=17;
+if buf_size>max_halfword then bad:=18;
+if max_quarterword-min_quarterword<255 then bad:=19;
+
+@ The operation of adding or subtracting |min_quarterword| occurs quite
+frequently in \TeX, so it is convenient to abbreviate this operation
+by using the macros |qi| and |qo| for input and output to and from
+quarterword format.
+
+The inner loop of \TeX\ will run faster with respect to compilers
+that don't optimize expressions like `|x+0|' and `|x-0|', if these
+macros are simplified in the obvious way when |min_quarterword=0|.
+@^inner loop@>@^system dependencies@>
+
+@d qi(#)==#+min_quarterword
+ {to put an |eight_bits| item into a quarterword}
+@d qo(#)==#-min_quarterword
+ {to take an |eight_bits| item out of a quarterword}
+@d hi(#)==#+min_halfword
+ {to put a sixteen-bit item into a halfword}
+@d ho(#)==#-min_halfword
+ {to take a sixteen-bit item from a halfword}
+
+@ The reader should study the following definitions closely:
+@^system dependencies@>
+
+@d sc==int {|scaled| data is equivalent to |integer|}
+
+@<Types...@>=
+@!quarterword = min_quarterword..max_quarterword; {1/4 of a word}
+@!halfword=min_halfword..max_halfword; {1/2 of a word}
+@!two_choices = 1..2; {used when there are two variants in a record}
+@!four_choices = 1..4; {used when there are four variants in a record}
+@!two_halves = packed record@;@/
+ @!rh:halfword;
+ case two_choices of
+ 1: (@!lh:halfword);
+ 2: (@!b0:quarterword; @!b1:quarterword);
+ end;
+@!four_quarters = packed record@;@/
+ @!b0:quarterword;
+ @!b1:quarterword;
+ @!b2:quarterword;
+ @!b3:quarterword;
+ end;
+@!memory_word = record@;@/
+ case four_choices of
+ 1: (@!int:integer);
+ 2: (@!gr:glue_ratio);
+ 3: (@!hh:two_halves);
+ 4: (@!qqqq:four_quarters);
+ end;
+@!word_file = file of memory_word;
+
+@ When debugging, we may want to print a |memory_word| without knowing
+what type it is; so we print it in all modes.
+@^dirty \PASCAL@>@^debugging@>
+
+@p @!debug procedure print_word(@!w:memory_word);
+ {prints |w| in all ways}
+begin print_int(w.int); print_char(" ");@/
+print_scaled(w.sc); print_char(" ");@/
+print_scaled(round(unity*float(w.gr))); print_ln;@/
+@^real multiplication@>
+print_int(w.hh.lh); print_char("="); print_int(w.hh.b0); print_char(":");
+print_int(w.hh.b1); print_char(";"); print_int(w.hh.rh); print_char(" ");@/
+print_int(w.qqqq.b0); print_char(":"); print_int(w.qqqq.b1); print_char(":");
+print_int(w.qqqq.b2); print_char(":"); print_int(w.qqqq.b3);
+end;
+gubed
+
+@* \[9] Dynamic memory allocation.
+The \TeX\ system does nearly all of its own memory allocation, so that it
+can readily be transported into environments that do not have automatic
+facilities for strings, garbage collection, etc., and so that it can be in
+control of what error messages the user receives. The dynamic storage
+requirements of \TeX\ are handled by providing a large array |mem| in
+which consecutive blocks of words are used as nodes by the \TeX\ routines.
+
+Pointer variables are indices into this array, or into another array
+called |eqtb| that will be explained later. A pointer variable might
+also be a special flag that lies outside the bounds of |mem|, so we
+allow pointers to assume any |halfword| value. The minimum halfword
+value represents a null pointer. \TeX\ does not assume that |mem[null]| exists.
+
+@d pointer==halfword {a flag or a location in |mem| or |eqtb|}
+@d null==min_halfword {the null pointer}
+
+@<Glob...@>=
+@!temp_ptr:pointer; {a pointer variable for occasional emergency use}
+
+@ The |mem| array is divided into two regions that are allocated separately,
+but the dividing line between these two regions is not fixed; they grow
+together until finding their ``natural'' size in a particular job.
+Locations less than or equal to |lo_mem_max| are used for storing
+variable-length records consisting of two or more words each. This region
+is maintained using an algorithm similar to the one described in exercise
+2.5--19 of {\sl The Art of Computer Programming}. However, no size field
+appears in the allocated nodes; the program is responsible for knowing the
+relevant size when a node is freed. Locations greater than or equal to
+|hi_mem_min| are used for storing one-word records; a conventional
+\.{AVAIL} stack is used for allocation in this region.
+
+Locations of |mem| between |mem_bot| and |mem_top| may be dumped as part
+of preloaded format files, by the \.{INITEX} preprocessor.
+@.INITEX@>
+Production versions of \TeX\ may extend the memory at both ends in order to
+provide more space; locations between |mem_min| and |mem_bot| are always
+used for variable-size nodes, and locations between |mem_top| and |mem_max|
+are always used for single-word nodes.
+
+The key pointers that govern |mem| allocation have a prescribed order:
+$$\advance\thickmuskip-2mu
+\hbox{|null<=mem_min<=mem_bot<lo_mem_max<
+ hi_mem_min<mem_top<=mem_end<=mem_max|.}$$
+
+Empirical tests show that the present implementation of \TeX\ tends to
+spend about 9\pct! of its running time allocating nodes, and about 6\pct!
+deallocating them after their use.
+
+@<Glob...@>=
+@!mem : array[mem_min..mem_max] of memory_word; {the big dynamic storage area}
+@!lo_mem_max : pointer; {the largest location of variable-size memory in use}
+@!hi_mem_min : pointer; {the smallest location of one-word memory in use}
+
+@ In order to study the memory requirements of particular applications, it
+is possible to prepare a version of \TeX\ that keeps track of current and
+maximum memory usage. When code between the delimiters |@!stat| $\ldots$
+|tats| is not ``commented out,'' \TeX\ will run a bit slower but it will
+report these statistics when |tracing_stats| is sufficiently large.
+
+@<Glob...@>=
+@!var_used, @!dyn_used : integer; {how much memory is in use}
+
+@ Let's consider the one-word memory region first, since it's the
+simplest. The pointer variable |mem_end| holds the highest-numbered location
+of |mem| that has ever been used. The free locations of |mem| that
+occur between |hi_mem_min| and |mem_end|, inclusive, are of type
+|two_halves|, and we write |info(p)| and |link(p)| for the |lh|
+and |rh| fields of |mem[p]| when it is of this type. The single-word
+free locations form a linked list
+$$|avail|,\;\hbox{|link(avail)|},\;\hbox{|link(link(avail))|},\;\ldots$$
+terminated by |null|.
+
+@d link(#) == mem[#].hh.rh {the |link| field of a memory word}
+@d info(#) == mem[#].hh.lh {the |info| field of a memory word}
+
+@<Glob...@>=
+@!avail : pointer; {head of the list of available one-word nodes}
+@!mem_end : pointer; {the last one-word node used in |mem|}
+
+@ If memory is exhausted, it might mean that the user has forgotten
+a right brace. We will define some procedures later that try to help
+pinpoint the trouble.
+
+@p @<Declare the procedure called |show_token_list|@>@/
+@<Declare the procedure called |runaway|@>
+
+@ The function |get_avail| returns a pointer to a new one-word node whose
+|link| field is null. However, \TeX\ will halt if there is no more room left.
+@^inner loop@>
+
+If the available-space list is empty, i.e., if |avail=null|,
+we try first to increase |mem_end|. If that cannot be done, i.e., if
+|mem_end=mem_max|, we try to decrease |hi_mem_min|. If that cannot be
+done, i.e., if |hi_mem_min=lo_mem_max+1|, we have to quit.
+
+@p function get_avail : pointer; {single-word node allocation}
+var p:pointer; {the new node being got}
+begin p:=avail; {get top location in the |avail| stack}
+if p<>null then avail:=link(avail) {and pop it off}
+else if mem_end<mem_max then {or go into virgin territory}
+ begin incr(mem_end); p:=mem_end;
+ end
+else begin decr(hi_mem_min); p:=hi_mem_min;
+ if hi_mem_min<=lo_mem_max then
+ begin runaway; {if memory is exhausted, display possible runaway text}
+ overflow("main memory size",mem_max+1-mem_min);
+ {quit; all one-word nodes are busy}
+@:TeX capacity exceeded main memory size}{\quad main memory size@>
+ end;
+ end;
+link(p):=null; {provide an oft-desired initialization of the new node}
+@!stat incr(dyn_used);@+tats@;{maintain statistics}
+get_avail:=p;
+end;
+
+@ Conversely, a one-word node is recycled by calling |free_avail|.
+This routine is part of \TeX's ``inner loop,'' so we want it to be fast.
+@^inner loop@>
+
+@d free_avail(#)== {single-word node liberation}
+ begin link(#):=avail; avail:=#;
+ @!stat decr(dyn_used);@+tats@/
+ end
+
+@ There's also a |fast_get_avail| routine, which saves the procedure-call
+overhead at the expense of extra programming. This routine is used in
+the places that would otherwise account for the most calls of |get_avail|.
+@^inner loop@>
+
+@d fast_get_avail(#)==@t@>@;@/
+ begin #:=avail; {avoid |get_avail| if possible, to save time}
+ if #=null then #:=get_avail
+ else begin avail:=link(#); link(#):=null;
+ @!stat incr(dyn_used);@+tats@/
+ end;
+ end
+
+@ The procedure |flush_list(p)| frees an entire linked list of
+one-word nodes that starts at position |p|.
+@^inner loop@>
+
+@p procedure flush_list(@!p:pointer); {makes list of single-word nodes
+ available}
+var @!q,@!r:pointer; {list traversers}
+begin if p<>null then
+ begin r:=p;
+ repeat q:=r; r:=link(r); @!stat decr(dyn_used);@+tats@/
+ until r=null; {now |q| is the last node on the list}
+ link(q):=avail; avail:=p;
+ end;
+end;
+
+@ The available-space list that keeps track of the variable-size portion
+of |mem| is a nonempty, doubly-linked circular list of empty nodes,
+pointed to by the roving pointer |rover|.
+
+Each empty node has size 2 or more; the first word contains the special
+value |max_halfword| in its |link| field and the size in its |info| field;
+the second word contains the two pointers for double linking.
+
+Each nonempty node also has size 2 or more. Its first word is of type
+|two_halves|\kern-1pt, and its |link| field is never equal to |max_halfword|.
+Otherwise there is complete flexibility with respect to the contents
+of its other fields and its other words.
+
+(We require |mem_max<max_halfword| because terrible things can happen
+when |max_halfword| appears in the |link| field of a nonempty node.)
+
+@d empty_flag == max_halfword {the |link| of an empty variable-size node}
+@d is_empty(#) == (link(#)=empty_flag) {tests for empty node}
+@d node_size == info {the size field in empty variable-size nodes}
+@d llink(#) == info(#+1) {left link in doubly-linked list of empty nodes}
+@d rlink(#) == link(#+1) {right link in doubly-linked list of empty nodes}
+
+@<Glob...@>=
+@!rover : pointer; {points to some node in the list of empties}
+
+@ A call to |get_node| with argument |s| returns a pointer to a new node
+of size~|s|, which must be 2~or more. The |link| field of the first word
+of this new node is set to null. An overflow stop occurs if no suitable
+space exists.
+
+If |get_node| is called with $s=2^{30}$, it simply merges adjacent free
+areas and returns the value |max_halfword|.
+
+@p function get_node(@!s:integer):pointer; {variable-size node allocation}
+label found,exit,restart;
+var p:pointer; {the node currently under inspection}
+@!q:pointer; {the node physically after node |p|}
+@!r:integer; {the newly allocated node, or a candidate for this honor}
+@!t:integer; {temporary register}
+begin restart: p:=rover; {start at some free node in the ring}
+repeat @<Try to allocate within node |p| and its physical successors,
+ and |goto found| if allocation was possible@>;
+@^inner loop@>
+p:=rlink(p); {move to the next node in the ring}
+until p=rover; {repeat until the whole list has been traversed}
+if s=@'10000000000 then
+ begin get_node:=max_halfword; return;
+ end;
+if lo_mem_max+2<hi_mem_min then if lo_mem_max+2<=mem_bot+max_halfword then
+ @<Grow more variable-size memory and |goto restart|@>;
+overflow("main memory size",mem_max+1-mem_min);
+ {sorry, nothing satisfactory is left}
+@:TeX capacity exceeded main memory size}{\quad main memory size@>
+found: link(r):=null; {this node is now nonempty}
+@!stat var_used:=var_used+s; {maintain usage statistics}
+tats@;@/
+get_node:=r;
+exit:end;
+
+@ The lower part of |mem| grows by 1000 words at a time, unless
+we are very close to going under. When it grows, we simply link
+a new node into the available-space list. This method of controlled
+growth helps to keep the |mem| usage consecutive when \TeX\ is
+implemented on ``virtual memory'' systems.
+@^virtual memory@>
+
+@<Grow more variable-size memory and |goto restart|@>=
+begin if hi_mem_min-lo_mem_max>=1998 then t:=lo_mem_max+1000
+else t:=lo_mem_max+1+(hi_mem_min-lo_mem_max) div 2;
+ {|lo_mem_max+2<=t<hi_mem_min|}
+p:=llink(rover); q:=lo_mem_max; rlink(p):=q; llink(rover):=q;@/
+if t>mem_bot+max_halfword then t:=mem_bot+max_halfword;
+rlink(q):=rover; llink(q):=p; link(q):=empty_flag; node_size(q):=t-lo_mem_max;@/
+lo_mem_max:=t; link(lo_mem_max):=null; info(lo_mem_max):=null;
+rover:=q; goto restart;
+end
+
+@ Empirical tests show that the routine in this section performs a
+node-merging operation about 0.75 times per allocation, on the average,
+after which it finds that |r>p+1| about 95\pct! of the time.
+
+@<Try to allocate...@>=
+q:=p+node_size(p); {find the physical successor}
+@^inner loop@>
+while is_empty(q) do {merge node |p| with node |q|}
+ begin t:=rlink(q);
+ if q=rover then rover:=t;
+ llink(t):=llink(q); rlink(llink(q)):=t;@/
+ q:=q+node_size(q);
+ end;
+r:=q-s;
+if r>p+1 then @<Allocate from the top of node |p| and |goto found|@>;
+if r=p then if rlink(p)<>p then
+ @<Allocate entire node |p| and |goto found|@>;
+node_size(p):=q-p {reset the size in case it grew}
+
+@ @<Allocate from the top...@>=
+begin node_size(p):=r-p; {store the remaining size}
+@^inner loop@>
+rover:=p; {start searching here next time}
+goto found;
+end
+
+@ Here we delete node |p| from the ring, and let |rover| rove around.
+
+@<Allocate entire...@>=
+begin rover:=rlink(p); t:=llink(p);
+llink(rover):=t; rlink(t):=rover;
+goto found;
+end
+
+@ Conversely, when some variable-size node |p| of size |s| is no longer needed,
+the operation |free_node(p,s)| will make its words available, by inserting
+|p| as a new empty node just before where |rover| now points.
+@^inner loop@>
+
+@p procedure free_node(@!p:pointer; @!s:halfword); {variable-size node
+ liberation}
+var q:pointer; {|llink(rover)|}
+begin node_size(p):=s; link(p):=empty_flag;
+q:=llink(rover); llink(p):=q; rlink(p):=rover; {set both links}
+llink(rover):=p; rlink(q):=p; {insert |p| into the ring}
+@!stat var_used:=var_used-s;@+tats@;{maintain statistics}
+end;
+
+@ Just before \.{INITEX} writes out the memory, it sorts the doubly linked
+available space list. The list is probably very short at such times, so a
+simple insertion sort is used. The smallest available location will be
+pointed to by |rover|, the next-smallest by |rlink(rover)|, etc.
+
+@p @!init procedure sort_avail; {sorts the available variable-size nodes
+ by location}
+var p,@!q,@!r: pointer; {indices into |mem|}
+@!old_rover:pointer; {initial |rover| setting}
+begin p:=get_node(@'10000000000); {merge adjacent free areas}
+p:=rlink(rover); rlink(rover):=max_halfword; old_rover:=rover;
+while p<>old_rover do @<Sort \(p)|p| into the list starting at |rover|
+ and advance |p| to |rlink(p)|@>;
+p:=rover;
+while rlink(p)<>max_halfword do
+ begin llink(rlink(p)):=p; p:=rlink(p);
+ end;
+rlink(p):=rover; llink(rover):=p;
+end;
+tini
+
+@ The following |while| loop is guaranteed to
+terminate, since the list that starts at
+|rover| ends with |max_halfword| during the sorting procedure.
+
+@<Sort \(p)|p|...@>=
+if p<rover then
+ begin q:=p; p:=rlink(q); rlink(q):=rover; rover:=q;
+ end
+else begin q:=rover;
+ while rlink(q)<p do q:=rlink(q);
+ r:=rlink(p); rlink(p):=rlink(q); rlink(q):=p; p:=r;
+ end
+
+@* \[10] Data structures for boxes and their friends.
+From the computer's standpoint, \TeX's chief mission is to create
+horizontal and vertical lists. We shall now investigate how the elements
+of these lists are represented internally as nodes in the dynamic memory.
+
+A horizontal or vertical list is linked together by |link| fields in
+the first word of each node. Individual nodes represent boxes, glue,
+penalties, or special things like discretionary hyphens; because of this
+variety, some nodes are longer than others, and we must distinguish different
+kinds of nodes. We do this by putting a `|type|' field in the first word,
+together with the link and an optional `|subtype|'.
+
+@d type(#) == mem[#].hh.b0 {identifies what kind of node this is}
+@d subtype(#) == mem[#].hh.b1 {secondary identification in some cases}
+
+@ A |@!char_node|, which represents a single character, is the most important
+kind of node because it accounts for the vast majority of all boxes.
+Special precautions are therefore taken to ensure that a |char_node| does
+not take up much memory space. Every such node is one word long, and in fact
+it is identifiable by this property, since other kinds of nodes have at least
+two words, and they appear in |mem| locations less than |hi_mem_min|.
+This makes it possible to omit the |type| field in a |char_node|, leaving
+us room for two bytes that identify a |font| and a |character| within
+that font.
+
+Note that the format of a |char_node| allows for up to 256 different
+fonts and up to 256 characters per font; but most implementations will
+probably limit the total number of fonts to fewer than 75 per job,
+and most fonts will stick to characters whose codes are
+less than 128 (since higher codes
+are more difficult to access on most keyboards).
+
+Extensions of \TeX\ intended for oriental languages will need even more
+than $256\times256$ possible characters, when we consider different sizes
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+and styles of type. It is suggested that Chinese and Japanese fonts be
+handled by representing such characters in two consecutive |char_node|
+entries: The first of these has |font=font_base|, and its |link| points
+to the second;
+the second identifies the font and the character dimensions.
+The saving feature about oriental characters is that most of them have
+the same box dimensions. The |character| field of the first |char_node|
+is a ``\\{charext}'' that distinguishes between graphic symbols whose
+dimensions are identical for typesetting purposes. (See the \MF\ manual.)
+Such an extension of \TeX\ would not be difficult; further details are
+left to the reader.
+
+In order to make sure that the |character| code fits in a quarterword,
+\TeX\ adds the quantity |min_quarterword| to the actual code.
+
+Character nodes appear only in horizontal lists, never in vertical lists.
+
+@d is_char_node(#) == (#>=hi_mem_min)
+ {does the argument point to a |char_node|?}
+@d font == type {the font code in a |char_node|}
+@d character == subtype {the character code in a |char_node|}
+
+@ An |hlist_node| stands for a box that was made from a horizontal list.
+Each |hlist_node| is seven words long, and contains the following fields
+(in addition to the mandatory |type| and |link|, which we shall not
+mention explicitly when discussing the other node types): The |height| and
+|width| and |depth| are scaled integers denoting the dimensions of the
+box. There is also a |shift_amount| field, a scaled integer indicating
+how much this box should be lowered (if it appears in a horizontal list),
+or how much it should be moved to the right (if it appears in a vertical
+list). There is a |list_ptr| field, which points to the beginning of the
+list from which this box was fabricated; if |list_ptr| is |null|, the box
+is empty. Finally, there are three fields that represent the setting of
+the glue: |glue_set(p)| is a word of type |glue_ratio| that represents
+the proportionality constant for glue setting; |glue_sign(p)| is
+|stretching| or |shrinking| or |normal| depending on whether or not the
+glue should stretch or shrink or remain rigid; and |glue_order(p)|
+specifies the order of infinity to which glue setting applies (|normal|,
+|fil|, |fill|, or |filll|). The |subtype| field is not used.
+
+@d hlist_node=0 {|type| of hlist nodes}
+@d box_node_size=7 {number of words to allocate for a box node}
+@d width_offset=1 {position of |width| field in a box node}
+@d depth_offset=2 {position of |depth| field in a box node}
+@d height_offset=3 {position of |height| field in a box node}
+@d width(#) == mem[#+width_offset].sc {width of the box, in sp}
+@d depth(#) == mem[#+depth_offset].sc {depth of the box, in sp}
+@d height(#) == mem[#+height_offset].sc {height of the box, in sp}
+@d shift_amount(#) == mem[#+4].sc {repositioning distance, in sp}
+@d list_offset=5 {position of |list_ptr| field in a box node}
+@d list_ptr(#) == link(#+list_offset) {beginning of the list inside the box}
+@d glue_order(#) == subtype(#+list_offset) {applicable order of infinity}
+@d glue_sign(#) == type(#+list_offset) {stretching or shrinking}
+@d normal=0 {the most common case when several cases are named}
+@d stretching = 1 {glue setting applies to the stretch components}
+@d shrinking = 2 {glue setting applies to the shrink components}
+@d glue_offset = 6 {position of |glue_set| in a box node}
+@d glue_set(#) == mem[#+glue_offset].gr
+ {a word of type |glue_ratio| for glue setting}
+
+@ The |new_null_box| function returns a pointer to an |hlist_node| in
+which all subfields have the values corresponding to `\.{\\hbox\{\}}'.
+The |subtype| field is set to |min_quarterword|, since that's the desired
+|span_count| value if this |hlist_node| is changed to an |unset_node|.
+
+@p function new_null_box:pointer; {creates a new box node}
+var p:pointer; {the new node}
+begin p:=get_node(box_node_size); type(p):=hlist_node;
+subtype(p):=min_quarterword;
+width(p):=0; depth(p):=0; height(p):=0; shift_amount(p):=0; list_ptr(p):=null;
+glue_sign(p):=normal; glue_order(p):=normal; set_glue_ratio_zero(glue_set(p));
+new_null_box:=p;
+end;
+
+@ A |vlist_node| is like an |hlist_node| in all respects except that it
+contains a vertical list.
+
+@d vlist_node=1 {|type| of vlist nodes}
+
+@ A |rule_node| stands for a solid black rectangle; it has |width|,
+|depth|, and |height| fields just as in an |hlist_node|. However, if
+any of these dimensions is $-2^{30}$, the actual value will be determined
+by running the rule up to the boundary of the innermost enclosing box.
+This is called a ``running dimension.'' The |width| is never running in
+an hlist; the |height| and |depth| are never running in a~vlist.
+
+@d rule_node=2 {|type| of rule nodes}
+@d rule_node_size=4 {number of words to allocate for a rule node}
+@d null_flag==-@'10000000000 {$-2^{30}$, signifies a missing item}
+@d is_running(#) == (#=null_flag) {tests for a running dimension}
+
+@ A new rule node is delivered by the |new_rule| function. It
+makes all the dimensions ``running,'' so you have to change the
+ones that are not allowed to run.
+
+@p function new_rule:pointer;
+var p:pointer; {the new node}
+begin p:=get_node(rule_node_size); type(p):=rule_node;
+subtype(p):=0; {the |subtype| is not used}
+width(p):=null_flag; depth(p):=null_flag; height(p):=null_flag;
+new_rule:=p;
+end;
+
+@ Insertions are represented by |ins_node| records, where the |subtype|
+indicates the corresponding box number. For example, `\.{\\insert 250}'
+leads to an |ins_node| whose |subtype| is |250+min_quarterword|.
+The |height| field of an |ins_node| is slightly misnamed; it actually holds
+the natural height plus depth of the vertical list being inserted.
+The |depth| field holds the |split_max_depth| to be used in case this
+insertion is split, and the |split_top_ptr| points to the corresponding
+|split_top_skip|. The |float_cost| field holds the |floating_penalty| that
+will be used if this insertion floats to a subsequent page after a
+split insertion of the same class. There is one more field, the
+|ins_ptr|, which points to the beginning of the vlist for the insertion.
+
+@d ins_node=3 {|type| of insertion nodes}
+@d ins_node_size=5 {number of words to allocate for an insertion}
+@d float_cost(#)==mem[#+1].int {the |floating_penalty| to be used}
+@d ins_ptr(#)==info(#+4) {the vertical list to be inserted}
+@d split_top_ptr(#)==link(#+4) {the |split_top_skip| to be used}
+
+@ A |mark_node| has a |mark_ptr| field that points to the reference count
+of a token list that contains the user's \.{\\mark} text.
+This field occupies a full word instead of a halfword, because
+there's nothing to put in the other halfword; it is easier in \PASCAL\ to
+use the full word than to risk leaving garbage in the unused half.
+
+@d mark_node=4 {|type| of a mark node}
+@d small_node_size=2 {number of words to allocate for most node types}
+@d mark_ptr(#)==mem[#+1].int {head of the token list for a mark}
+
+@ An |adjust_node|, which occurs only in horizontal lists,
+specifies material that will be moved out into the surrounding
+vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}'
+operation. The |adjust_ptr| field points to the vlist containing this
+material.
+
+@d adjust_node=5 {|type| of an adjust node}
+@d adjust_ptr==mark_ptr {vertical list to be moved out of horizontal list}
+
+@ A |ligature_node|, which occurs only in horizontal lists, specifies
+a character that was fabricated from the interaction of two or more
+actual characters. The second word of the node, which is called the
+|lig_char| word, contains |font| and |character| fields just as in a
+|char_node|. The characters that generated the ligature have not been
+forgotten, since they are needed for diagnostic messages and for
+hyphenation; the |lig_ptr| field points to a linked list of character
+nodes for all original characters that have been deleted. (This list
+might be empty if the characters that generated the ligature were
+retained in other nodes.)
+
+The |subtype| field is 0, plus 2 and/or 1 if the original source of the
+ligature included implicit left and/or right boundaries.
+
+@d ligature_node=6 {|type| of a ligature node}
+@d lig_char(#)==#+1 {the word where the ligature is to be found}
+@d lig_ptr(#)==link(lig_char(#)) {the list of characters}
+
+@ The |new_ligature| function creates a ligature node having given
+contents of the |font|, |character|, and |lig_ptr| fields. We also have
+a |new_lig_item| function, which returns a two-word node having a given
+|character| field. Such nodes are used for temporary processing as ligatures
+are being created.
+
+@p function new_ligature(@!f,@!c:quarterword; @!q:pointer):pointer;
+var p:pointer; {the new node}
+begin p:=get_node(small_node_size); type(p):=ligature_node;
+font(lig_char(p)):=f; character(lig_char(p)):=c; lig_ptr(p):=q;
+subtype(p):=0; new_ligature:=p;
+end;
+@#
+function new_lig_item(@!c:quarterword):pointer;
+var p:pointer; {the new node}
+begin p:=get_node(small_node_size); character(p):=c; lig_ptr(p):=null;
+new_lig_item:=p;
+end;
+
+@ A |disc_node|, which occurs only in horizontal lists, specifies a
+``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text
+that starts at |pre_break(p)| will precede the break, the text that starts at
+|post_break(p)| will follow the break, and text that appears in the next
+|replace_count(p)| nodes will be ignored. For example, an ordinary
+discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with
+|pre_break| pointing to a |char_node| containing a hyphen, |post_break=null|,
+and |replace_count=0|. All three of the discretionary texts must be
+lists that consist entirely of character, kern, box, rule, and ligature nodes.
+
+If |pre_break(p)=null|, the |ex_hyphen_penalty| will be charged for this
+break. Otherwise the |hyphen_penalty| will be charged. The texts will
+actually be substituted into the list by the line-breaking algorithm if it
+decides to make the break, and the discretionary node will disappear at
+that time; thus, the output routine sees only discretionaries that were
+not chosen.
+
+@d disc_node=7 {|type| of a discretionary node}
+@d replace_count==subtype {how many subsequent nodes to replace}
+@d pre_break==llink {text that precedes a discretionary break}
+@d post_break==rlink {text that follows a discretionary break}
+
+@p function new_disc:pointer; {creates an empty |disc_node|}
+var p:pointer; {the new node}
+begin p:=get_node(small_node_size); type(p):=disc_node;
+replace_count(p):=0; pre_break(p):=null; post_break(p):=null;
+new_disc:=p;
+end;
+
+@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
+|subtype| field in its first word says what `\\{whatsit}' it is, and
+implicitly determines the node size (which must be 2 or more) and the
+format of the remaining words. When a |whatsit_node| is encountered
+in a list, special actions are invoked; knowledgeable people who are
+careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
+things by adding code at the end of the program. For example, there
+might be a `\TeX nicolor' extension to specify different colors of ink,
+@^extensions to \TeX@>
+and the whatsit node might contain the desired parameters.
+
+The present implementation of \TeX\ treats the features associated with
+`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to
+illustrate how such routines might be coded. We shall defer further
+discussion of extensions until the end of this program.
+
+@d whatsit_node=8 {|type| of special extension nodes}
+
+@ A |math_node|, which occurs only in horizontal lists, appears before and
+after mathematical formulas. The |subtype| field is |before| before the
+formula and |after| after it. There is a |width| field, which represents
+the amount of surrounding space inserted by \.{\\mathsurround}.
+
+@d math_node=9 {|type| of a math node}
+@d before=0 {|subtype| for math node that introduces a formula}
+@d after=1 {|subtype| for math node that winds up a formula}
+
+@p function new_math(@!w:scaled;@!s:small_number):pointer;
+var p:pointer; {the new node}
+begin p:=get_node(small_node_size); type(p):=math_node;
+subtype(p):=s; width(p):=w; new_math:=p;
+end;
+
+@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|,
+|rule_node|, |ins_node|, |mark_node|, |adjust_node|, |ligature_node|,
+|disc_node|, |whatsit_node|, and |math_node| are at the low end of the
+type codes, by permitting a break at glue in a list if and only if the
+|type| of the previous node is less than |math_node|. Furthermore, a
+node is discarded after a break if its type is |math_node| or~more.
+
+@d precedes_break(#)==(type(#)<math_node)
+@d non_discardable(#)==(type(#)<math_node)
+
+@ A |glue_node| represents glue in a list. However, it is really only
+a pointer to a separate glue specification, since \TeX\ makes use of the
+fact that many essentially identical nodes of glue are usually present.
+If |p| points to a |glue_node|, |glue_ptr(p)| points to
+another packet of words that specify the stretch and shrink components, etc.
+
+Glue nodes also serve to represent leaders; the |subtype| is used to
+distinguish between ordinary glue (which is called |normal|) and the three
+kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|).
+The |leader_ptr| field points to a rule node or to a box node containing the
+leaders; it is set to |null| in ordinary glue nodes.
+
+Many kinds of glue are computed from \TeX's ``skip'' parameters, and
+it is helpful to know which parameter has led to a particular glue node.
+Therefore the |subtype| is set to indicate the source of glue, whenever
+it originated as a parameter. We will be defining symbolic names for the
+parameter numbers later (e.g., |line_skip_code=0|, |baseline_skip_code=1|,
+etc.); it suffices for now to say that the |subtype| of parametric glue
+will be the same as the parameter number, plus~one.
+
+In math formulas there are two more possibilities for the |subtype| in a
+glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu}
+instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}'
+feature that cancels the glue node immediately following if it appears
+in a subscript.
+
+@d glue_node=10 {|type| of node that points to a glue specification}
+@d cond_math_glue=98 {special |subtype| to suppress glue in the next node}
+@d mu_glue=99 {|subtype| for math glue}
+@d a_leaders=100 {|subtype| for aligned leaders}
+@d c_leaders=101 {|subtype| for centered leaders}
+@d x_leaders=102 {|subtype| for expanded leaders}
+@d glue_ptr==llink {pointer to a glue specification}
+@d leader_ptr==rlink {pointer to box or rule node for leaders}
+
+@ A glue specification has a halfword reference count in its first word,
+@^reference counts@>
+representing |null| plus the number of glue nodes that point to it (less one).
+Note that the reference count appears in the same position as
+the |link| field in list nodes; this is the field that is initialized
+to |null| when a node is allocated, and it is also the field that is flagged
+by |empty_flag| in empty nodes.
+
+Glue specifications also contain three |scaled| fields, for the |width|,
+|stretch|, and |shrink| dimensions. Finally, there are two one-byte
+fields called |stretch_order| and |shrink_order|; these contain the
+orders of infinity (|normal|, |fil|, |fill|, or |filll|)
+corresponding to the stretch and shrink values.
+
+@d glue_spec_size=4 {number of words to allocate for a glue specification}
+@d glue_ref_count(#) == link(#) {reference count of a glue specification}
+@d stretch(#) == mem[#+2].sc {the stretchability of this glob of glue}
+@d shrink(#) == mem[#+3].sc {the shrinkability of this glob of glue}
+@d stretch_order == type {order of infinity for stretching}
+@d shrink_order == subtype {order of infinity for shrinking}
+@d fil=1 {first-order infinity}
+@d fill=2 {second-order infinity}
+@d filll=3 {third-order infinity}
+
+@<Types...@>=
+@!glue_ord=normal..filll; {infinity to the 0, 1, 2, or 3 power}
+
+@ Here is a function that returns a pointer to a copy of a glue spec.
+The reference count in the copy is |null|, because there is assumed
+to be exactly one reference to the new specification.
+
+@p function new_spec(@!p:pointer):pointer; {duplicates a glue specification}
+var q:pointer; {the new spec}
+begin q:=get_node(glue_spec_size);@/
+mem[q]:=mem[p]; glue_ref_count(q):=null;@/
+width(q):=width(p); stretch(q):=stretch(p); shrink(q):=shrink(p);
+new_spec:=q;
+end;
+
+@ And here's a function that creates a glue node for a given parameter
+identified by its code number; for example,
+|new_param_glue(line_skip_code)| returns a pointer to a glue node for the
+current \.{\\lineskip}.
+
+@p function new_param_glue(@!n:small_number):pointer;
+var p:pointer; {the new node}
+@!q:pointer; {the glue specification}
+begin p:=get_node(small_node_size); type(p):=glue_node; subtype(p):=n+1;
+leader_ptr(p):=null;@/
+q:=@<Current |mem| equivalent of glue parameter number |n|@>@t@>;
+glue_ptr(p):=q; incr(glue_ref_count(q));
+new_param_glue:=p;
+end;
+
+@ Glue nodes that are more or less anonymous are created by |new_glue|,
+whose argument points to a glue specification.
+
+@p function new_glue(@!q:pointer):pointer;
+var p:pointer; {the new node}
+begin p:=get_node(small_node_size); type(p):=glue_node; subtype(p):=normal;
+leader_ptr(p):=null; glue_ptr(p):=q; incr(glue_ref_count(q));
+new_glue:=p;
+end;
+
+@ Still another subroutine is needed: This one is sort of a combination
+of |new_param_glue| and |new_glue|. It creates a glue node for one of
+the current glue parameters, but it makes a fresh copy of the glue
+specification, since that specification will probably be subject to change,
+while the parameter will stay put. The global variable |temp_ptr| is
+set to the address of the new spec.
+
+@p function new_skip_param(@!n:small_number):pointer;
+var p:pointer; {the new node}
+begin temp_ptr:=new_spec(@<Current |mem| equivalent of glue parameter...@>);
+p:=new_glue(temp_ptr); glue_ref_count(temp_ptr):=null; subtype(p):=n+1;
+new_skip_param:=p;
+end;
+
+@ A |kern_node| has a |width| field to specify a (normally negative)
+amount of spacing. This spacing correction appears in horizontal lists
+between letters like A and V when the font designer said that it looks
+better to move them closer together or further apart. A kern node can
+also appear in a vertical list, when its `|width|' denotes additional
+spacing in the vertical direction. The |subtype| is either |normal| (for
+kerns inserted from font information or math mode calculations) or |explicit|
+(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern|
+(for kerns inserted from non-math accents) or |mu_glue| (for kerns
+inserted from \.{\\mkern} specifications in math formulas).
+
+@d kern_node=11 {|type| of a kern node}
+@d explicit=1 {|subtype| of kern nodes from \.{\\kern} and \.{\\/}}
+@d acc_kern=2 {|subtype| of kern nodes from accents}
+
+@ The |new_kern| function creates a kern node having a given width.
+
+@p function new_kern(@!w:scaled):pointer;
+var p:pointer; {the new node}
+begin p:=get_node(small_node_size); type(p):=kern_node;
+subtype(p):=normal;
+width(p):=w;
+new_kern:=p;
+end;
+
+@ A |penalty_node| specifies the penalty associated with line or page
+breaking, in its |penalty| field. This field is a fullword integer, but
+the full range of integer values is not used: Any penalty |>=10000| is
+treated as infinity, and no break will be allowed for such high values.
+Similarly, any penalty |<=-10000| is treated as negative infinity, and a
+break will be forced.
+
+@d penalty_node=12 {|type| of a penalty node}
+@d inf_penalty=inf_bad {``infinite'' penalty value}
+@d eject_penalty=-inf_penalty {``negatively infinite'' penalty value}
+@d penalty(#) == mem[#+1].int {the added cost of breaking a list here}
+
+@ Anyone who has been reading the last few sections of the program will
+be able to guess what comes next.
+
+@p function new_penalty(@!m:integer):pointer;
+var p:pointer; {the new node}
+begin p:=get_node(small_node_size); type(p):=penalty_node;
+subtype(p):=0; {the |subtype| is not used}
+penalty(p):=m; new_penalty:=p;
+end;
+
+@ You might think that we have introduced enough node types by now. Well,
+almost, but there is one more: An |unset_node| has nearly the same format
+as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign}
+or \.{\\valign} that are not yet in their final form, since the box
+dimensions are their ``natural'' sizes before any glue adjustment has been
+made. The |glue_set| word is not present; instead, we have a |glue_stretch|
+field, which contains the total stretch of order |glue_order| that is
+present in the hlist or vlist being boxed.
+Similarly, the |shift_amount| field is replaced by a |glue_shrink| field,
+containing the total shrink of order |glue_sign| that is present.
+The |subtype| field is called |span_count|; an unset box typically
+contains the data for |qo(span_count)+1| columns.
+Unset nodes will be changed to box nodes when alignment is completed.
+
+@d unset_node=13 {|type| for an unset node}
+@d glue_stretch(#)==mem[#+glue_offset].sc {total stretch in an unset node}
+@d glue_shrink==shift_amount {total shrink in an unset node}
+@d span_count==subtype {indicates the number of spanned columns}
+
+@ In fact, there are still more types coming. When we get to math formula
+processing we will see that a |style_node| has |type=14|; and a number
+of larger type codes will also be defined, for use in math mode only.
+
+@ Warning: If any changes are made to these data structure layouts, such as
+changing any of the node sizes or even reordering the words of nodes,
+the |copy_node_list| procedure and the memory initialization code
+below may have to be changed. Such potentially dangerous parts of the
+program are listed in the index under `data structure assumptions'.
+@!@^data structure assumptions@>
+However, other references to the nodes are made symbolically in terms of
+the \.{WEB} macro definitions above, so that format changes will leave
+\TeX's other algorithms intact.
+@^system dependencies@>
+
+@* \[11] Memory layout.
+Some areas of |mem| are dedicated to fixed usage, since static allocation is
+more efficient than dynamic allocation when we can get away with it. For
+example, locations |mem_bot| to |mem_bot+3| are always used to store the
+specification for glue that is `\.{0pt plus 0pt minus 0pt}'. The
+following macro definitions accomplish the static allocation by giving
+symbolic names to the fixed positions. Static variable-size nodes appear
+in locations |mem_bot| through |lo_mem_stat_max|, and static single-word nodes
+appear in locations |hi_mem_stat_min| through |mem_top|, inclusive. It is
+harmless to let |lig_trick| and |garbage| share the same location of |mem|.
+
+@d zero_glue==mem_bot {specification for \.{0pt plus 0pt minus 0pt}}
+@d fil_glue==zero_glue+glue_spec_size {\.{0pt plus 1fil minus 0pt}}
+@d fill_glue==fil_glue+glue_spec_size {\.{0pt plus 1fill minus 0pt}}
+@d ss_glue==fill_glue+glue_spec_size {\.{0pt plus 1fil minus 1fil}}
+@d fil_neg_glue==ss_glue+glue_spec_size {\.{0pt plus -1fil minus 0pt}}
+@d lo_mem_stat_max==fil_neg_glue+glue_spec_size-1 {largest statically
+ allocated word in the variable-size |mem|}
+@#
+@d page_ins_head==mem_top {list of insertion data for current page}
+@d contrib_head==mem_top-1 {vlist of items not yet on current page}
+@d page_head==mem_top-2 {vlist for current page}
+@d temp_head==mem_top-3 {head of a temporary list of some kind}
+@d hold_head==mem_top-4 {head of a temporary list of another kind}
+@d adjust_head==mem_top-5 {head of adjustment list returned by |hpack|}
+@d active==mem_top-7 {head of active list in |line_break|, needs two words}
+@d align_head==mem_top-8 {head of preamble list for alignments}
+@d end_span==mem_top-9 {tail of spanned-width lists}
+@d omit_template==mem_top-10 {a constant token list}
+@d null_list==mem_top-11 {permanently empty list}
+@d lig_trick==mem_top-12 {a ligature masquerading as a |char_node|}
+@d garbage==mem_top-12 {used for scrap information}
+@d backup_head==mem_top-13 {head of token list built by |scan_keyword|}
+@d hi_mem_stat_min==mem_top-13 {smallest statically allocated word in
+ the one-word |mem|}
+@d hi_mem_stat_usage=14 {the number of one-word nodes always present}
+
+@ The following code gets |mem| off to a good start, when \TeX\ is
+initializing itself the slow~way.
+
+@<Local variables for init...@>=
+@!k:integer; {index into |mem|, |eqtb|, etc.}
+
+@ @<Initialize table entries...@>=
+for k:=mem_bot+1 to lo_mem_stat_max do mem[k].sc:=0;
+ {all glue dimensions are zeroed}
+@^data structure assumptions@>
+k:=mem_bot;@+while k<=lo_mem_stat_max do
+ {set first words of glue specifications}
+ begin glue_ref_count(k):=null+1;
+ stretch_order(k):=normal; shrink_order(k):=normal;
+ k:=k+glue_spec_size;
+ end;
+stretch(fil_glue):=unity; stretch_order(fil_glue):=fil;@/
+stretch(fill_glue):=unity; stretch_order(fill_glue):=fill;@/
+stretch(ss_glue):=unity; stretch_order(ss_glue):=fil;@/
+shrink(ss_glue):=unity; shrink_order(ss_glue):=fil;@/
+stretch(fil_neg_glue):=-unity; stretch_order(fil_neg_glue):=fil;@/
+rover:=lo_mem_stat_max+1;
+link(rover):=empty_flag; {now initialize the dynamic memory}
+node_size(rover):=1000; {which is a 1000-word available node}
+llink(rover):=rover; rlink(rover):=rover;@/
+lo_mem_max:=rover+1000; link(lo_mem_max):=null; info(lo_mem_max):=null;@/
+for k:=hi_mem_stat_min to mem_top do
+ mem[k]:=mem[lo_mem_max]; {clear list heads}
+@<Initialize the special list heads and constant nodes@>;
+avail:=null; mem_end:=mem_top;
+hi_mem_min:=hi_mem_stat_min; {initialize the one-word memory}
+var_used:=lo_mem_stat_max+1-mem_bot; dyn_used:=hi_mem_stat_usage;
+ {initialize statistics}
+
+@ If \TeX\ is extended improperly, the |mem| array might get screwed up.
+For example, some pointers might be wrong, or some ``dead'' nodes might not
+have been freed when the last reference to them disappeared. Procedures
+|check_mem| and |search_mem| are available to help diagnose such
+problems. These procedures make use of two arrays called |free| and
+|was_free| that are present only if \TeX's debugging routines have
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+
+@<Glob...@>=
+@!debug @!free: packed array [mem_min..mem_max] of boolean; {free cells}
+@t\hskip10pt@>@!was_free: packed array [mem_min..mem_max] of boolean;
+ {previously free cells}
+@t\hskip10pt@>@!was_mem_end,@!was_lo_max,@!was_hi_min: pointer;
+ {previous |mem_end|, |lo_mem_max|, and |hi_mem_min|}
+@t\hskip10pt@>@!panicking:boolean; {do we want to check memory constantly?}
+gubed
+
+@ @<Set initial...@>=
+@!debug was_mem_end:=mem_min; {indicate that everything was previously free}
+was_lo_max:=mem_min; was_hi_min:=mem_max;
+panicking:=false;
+gubed
+
+@ Procedure |check_mem| makes sure that the available space lists of
+|mem| are well formed, and it optionally prints out all locations
+that are reserved now but were free the last time this procedure was called.
+
+@p @!debug procedure check_mem(@!print_locs : boolean);
+label done1,done2; {loop exits}
+var p,@!q:pointer; {current locations of interest in |mem|}
+@!clobbered:boolean; {is something amiss?}
+begin for p:=mem_min to lo_mem_max do free[p]:=false; {you can probably
+ do this faster}
+for p:=hi_mem_min to mem_end do free[p]:=false; {ditto}
+@<Check single-word |avail| list@>;
+@<Check variable-size |avail| list@>;
+@<Check flags of unavailable nodes@>;
+if print_locs then @<Print newly busy locations@>;
+for p:=mem_min to lo_mem_max do was_free[p]:=free[p];
+for p:=hi_mem_min to mem_end do was_free[p]:=free[p];
+ {|was_free:=free| might be faster}
+was_mem_end:=mem_end; was_lo_max:=lo_mem_max; was_hi_min:=hi_mem_min;
+end;
+gubed
+
+@ @<Check single-word...@>=
+p:=avail; q:=null; clobbered:=false;
+while p<>null do
+ begin if (p>mem_end)or(p<hi_mem_min) then clobbered:=true
+ else if free[p] then clobbered:=true;
+ if clobbered then
+ begin print_nl("AVAIL list clobbered at ");
+@.AVAIL list clobbered...@>
+ print_int(q); goto done1;
+ end;
+ free[p]:=true; q:=p; p:=link(q);
+ end;
+done1:
+
+@ @<Check variable-size...@>=
+p:=rover; q:=null; clobbered:=false;
+repeat if (p>=lo_mem_max)or(p<mem_min) then clobbered:=true
+ else if (rlink(p)>=lo_mem_max)or(rlink(p)<mem_min) then clobbered:=true
+ else if not(is_empty(p))or(node_size(p)<2)or@|
+ (p+node_size(p)>lo_mem_max)or@| (llink(rlink(p))<>p) then clobbered:=true;
+ if clobbered then
+ begin print_nl("Double-AVAIL list clobbered at ");
+ print_int(q); goto done2;
+ end;
+for q:=p to p+node_size(p)-1 do {mark all locations free}
+ begin if free[q] then
+ begin print_nl("Doubly free location at ");
+@.Doubly free location...@>
+ print_int(q); goto done2;
+ end;
+ free[q]:=true;
+ end;
+q:=p; p:=rlink(p);
+until p=rover;
+done2:
+
+@ @<Check flags...@>=
+p:=mem_min;
+while p<=lo_mem_max do {node |p| should not be empty}
+ begin if is_empty(p) then
+ begin print_nl("Bad flag at "); print_int(p);
+@.Bad flag...@>
+ end;
+ while (p<=lo_mem_max) and not free[p] do incr(p);
+ while (p<=lo_mem_max) and free[p] do incr(p);
+ end
+
+@ @<Print newly busy...@>=
+begin print_nl("New busy locs:");
+for p:=mem_min to lo_mem_max do
+ if not free[p] and ((p>was_lo_max) or was_free[p]) then
+ begin print_char(" "); print_int(p);
+ end;
+for p:=hi_mem_min to mem_end do
+ if not free[p] and
+ ((p<was_hi_min) or (p>was_mem_end) or was_free[p]) then
+ begin print_char(" "); print_int(p);
+ end;
+end
+
+@ The |search_mem| procedure attempts to answer the question ``Who points
+to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem|
+that might not be of type |two_halves|. Strictly speaking, this is
+@^dirty \PASCAL@>
+undefined in \PASCAL, and it can lead to ``false drops'' (words that seem to
+point to |p| purely by coincidence). But for debugging purposes, we want
+to rule out the places that do {\sl not\/} point to |p|, so a few false
+drops are tolerable.
+
+@p @!debug procedure search_mem(@!p:pointer); {look for pointers to |p|}
+var q:integer; {current position being searched}
+begin for q:=mem_min to lo_mem_max do
+ begin if link(q)=p then
+ begin print_nl("LINK("); print_int(q); print_char(")");
+ end;
+ if info(q)=p then
+ begin print_nl("INFO("); print_int(q); print_char(")");
+ end;
+ end;
+for q:=hi_mem_min to mem_end do
+ begin if link(q)=p then
+ begin print_nl("LINK("); print_int(q); print_char(")");
+ end;
+ if info(q)=p then
+ begin print_nl("INFO("); print_int(q); print_char(")");
+ end;
+ end;
+@<Search |eqtb| for equivalents equal to |p|@>;
+@<Search |save_stack| for equivalents that point to |p|@>;
+@<Search |hyph_list| for pointers to |p|@>;
+end;
+gubed
+
+@* \[12] Displaying boxes.
+We can reinforce our knowledge of the data structures just introduced
+by considering two procedures that display a list in symbolic form.
+The first of these, called |short_display|, is used in ``overfull box''
+messages to give the top-level description of a list. The other one,
+called |show_node_list|, prints a detailed description of exactly what
+is in the data structure.
+
+The philosophy of |short_display| is to ignore the fine points about exactly
+what is inside boxes, except that ligatures and discretionary breaks are
+expanded. As a result, |short_display| is a recursive procedure, but the
+recursion is never more than one level deep.
+@^recursion@>
+
+A global variable |font_in_short_display| keeps track of the font code that
+is assumed to be present when |short_display| begins; deviations from this
+font will be printed.
+
+@<Glob...@>=
+@!font_in_short_display:integer; {an internal font number}
+
+@ Boxes, rules, inserts, whatsits, marks, and things in general that are
+sort of ``complicated'' are indicated only by printing `\.{[]}'.
+
+@p procedure short_display(@!p:integer); {prints highlights of list |p|}
+var n:integer; {for replacement counts}
+begin while p>mem_min do
+ begin if is_char_node(p) then
+ begin if p<=mem_end then
+ begin if font(p)<>font_in_short_display then
+ begin if (font(p)<font_base)or(font(p)>font_max) then
+ print_char("*")
+@.*\relax@>
+ else @<Print the font identifier for |font(p)|@>;
+ print_char(" "); font_in_short_display:=font(p);
+ end;
+ print_ASCII(qo(character(p)));
+ end;
+ end
+ else @<Print a short indication of the contents of node |p|@>;
+ p:=link(p);
+ end;
+end;
+
+@ @<Print a short indication of the contents of node |p|@>=
+case type(p) of
+hlist_node,vlist_node,ins_node,whatsit_node,mark_node,adjust_node,
+ unset_node: print("[]");
+rule_node: print_char("|");
+glue_node: if glue_ptr(p)<>zero_glue then print_char(" ");
+math_node: print_char("$");
+ligature_node: short_display(lig_ptr(p));
+disc_node: begin short_display(pre_break(p));
+ short_display(post_break(p));@/
+ n:=replace_count(p);
+ while n>0 do
+ begin if link(p)<>null then p:=link(p);
+ decr(n);
+ end;
+ end;
+othercases do_nothing
+endcases
+
+@ The |show_node_list| routine requires some auxiliary subroutines: one to
+print a font-and-character combination, one to print a token list without
+its reference count, and one to print a rule dimension.
+
+@p procedure print_font_and_char(@!p:integer); {prints |char_node| data}
+begin if p>mem_end then print_esc("CLOBBERED.")
+else begin if (font(p)<font_base)or(font(p)>font_max) then print_char("*")
+@.*\relax@>
+ else @<Print the font identifier for |font(p)|@>;
+ print_char(" "); print_ASCII(qo(character(p)));
+ end;
+end;
+@#
+procedure print_mark(@!p:integer); {prints token list data in braces}
+begin print_char("{");
+if (p<hi_mem_min)or(p>mem_end) then print_esc("CLOBBERED.")
+else show_token_list(link(p),null,max_print_line-10);
+print_char("}");
+end;
+@#
+procedure print_rule_dimen(@!d:scaled); {prints dimension in rule node}
+begin if is_running(d) then print_char("*") else print_scaled(d);
+@.*\relax@>
+end;
+
+@ Then there is a subroutine that prints glue stretch and shrink, possibly
+followed by the name of finite units:
+
+@p procedure print_glue(@!d:scaled;@!order:integer;@!s:str_number);
+ {prints a glue component}
+begin print_scaled(d);
+if (order<normal)or(order>filll) then print("foul")
+else if order>normal then
+ begin print("fil");
+ while order>fil do
+ begin print_char("l"); decr(order);
+ end;
+ end
+else if s<>0 then print(s);
+end;
+
+@ The next subroutine prints a whole glue specification.
+
+@p procedure print_spec(@!p:integer;@!s:str_number);
+ {prints a glue specification}
+begin if (p<mem_min)or(p>=lo_mem_max) then print_char("*")
+@.*\relax@>
+else begin print_scaled(width(p));
+ if s<>0 then print(s);
+ if stretch(p)<>0 then
+ begin print(" plus "); print_glue(stretch(p),stretch_order(p),s);
+ end;
+ if shrink(p)<>0 then
+ begin print(" minus "); print_glue(shrink(p),shrink_order(p),s);
+ end;
+ end;
+end;
+
+@ We also need to declare some procedures that appear later in this
+documentation.
+
+@p @<Declare procedures needed for displaying the elements of mlists@>@;
+@<Declare the procedure called |print_skip_param|@>
+
+@ Since boxes can be inside of boxes, |show_node_list| is inherently recursive,
+@^recursion@>
+up to a given maximum number of levels. The history of nesting is indicated
+by the current string, which will be printed at the beginning of each line;
+the length of this string, namely |cur_length|, is the depth of nesting.
+
+Recursive calls on |show_node_list| therefore use the following pattern:
+
+@d node_list_display(#)==
+ begin append_char("."); show_node_list(#); flush_char;
+ end {|str_room| need not be checked; see |show_box| below}
+
+@ A global variable called |depth_threshold| is used to record the maximum
+depth of nesting for which |show_node_list| will show information. If we
+have |depth_threshold=0|, for example, only the top level information will
+be given and no sublists will be traversed. Another global variable, called
+|breadth_max|, tells the maximum number of items to show at each level;
+|breadth_max| had better be positive, or you won't see anything.
+
+@<Glob...@>=
+@!depth_threshold : integer; {maximum nesting depth in box displays}
+@!breadth_max : integer; {maximum number of items shown at the same list level}
+
+@ Now we are ready for |show_node_list| itself. This procedure has been
+written to be ``extra robust'' in the sense that it should not crash or get
+into a loop even if the data structures have been messed up by bugs in
+the rest of the program. You can safely call its parent routine
+|show_box(p)| for arbitrary values of |p| when you are debugging \TeX.
+However, in the presence of bad data, the procedure may
+@^dirty \PASCAL@>@^debugging@>
+fetch a |memory_word| whose variant is different from the way it was stored;
+for example, it might try to read |mem[p].hh| when |mem[p]|
+contains a scaled integer, if |p| is a pointer that has been
+clobbered or chosen at random.
+
+@p procedure show_node_list(@!p:integer); {prints a node list symbolically}
+label exit;
+var n:integer; {the number of items already printed at this level}
+@!g:real; {a glue ratio, as a floating point number}
+begin if cur_length>depth_threshold then
+ begin if p>null then print(" []");
+ {indicate that there's been some truncation}
+ return;
+ end;
+n:=0;
+while p>mem_min do
+ begin print_ln; print_current_string; {display the nesting history}
+ if p>mem_end then {pointer out of range}
+ begin print("Bad link, display aborted."); return;
+@.Bad link...@>
+ end;
+ incr(n); if n>breadth_max then {time to stop}
+ begin print("etc."); return;
+@.etc@>
+ end;
+ @<Display node |p|@>;
+ p:=link(p);
+ end;
+exit:
+end;
+
+@ @<Display node |p|@>=
+if is_char_node(p) then print_font_and_char(p)
+else case type(p) of
+ hlist_node,vlist_node,unset_node: @<Display box |p|@>;
+ rule_node: @<Display rule |p|@>;
+ ins_node: @<Display insertion |p|@>;
+ whatsit_node: @<Display the whatsit node |p|@>;
+ glue_node: @<Display glue |p|@>;
+ kern_node: @<Display kern |p|@>;
+ math_node: @<Display math node |p|@>;
+ ligature_node: @<Display ligature |p|@>;
+ penalty_node: @<Display penalty |p|@>;
+ disc_node: @<Display discretionary |p|@>;
+ mark_node: @<Display mark |p|@>;
+ adjust_node: @<Display adjustment |p|@>;
+ @t\4@>@<Cases of |show_node_list| that arise in mlists only@>@;
+ othercases print("Unknown node type!")
+ endcases
+
+@ @<Display box |p|@>=
+begin if type(p)=hlist_node then print_esc("h")
+else if type(p)=vlist_node then print_esc("v")
+else print_esc("unset");
+print("box("); print_scaled(height(p)); print_char("+");
+print_scaled(depth(p)); print(")x"); print_scaled(width(p));
+if type(p)=unset_node then
+ @<Display special fields of the unset node |p|@>
+else begin @<Display the value of |glue_set(p)|@>;
+ if shift_amount(p)<>0 then
+ begin print(", shifted "); print_scaled(shift_amount(p));
+ end;
+ end;
+node_list_display(list_ptr(p)); {recursive call}
+end
+
+@ @<Display special fields of the unset node |p|@>=
+begin if span_count(p)<>min_quarterword then
+ begin print(" ("); print_int(qo(span_count(p))+1);
+ print(" columns)");
+ end;
+if glue_stretch(p)<>0 then
+ begin print(", stretch "); print_glue(glue_stretch(p),glue_order(p),0);
+ end;
+if glue_shrink(p)<>0 then
+ begin print(", shrink "); print_glue(glue_shrink(p),glue_sign(p),0);
+ end;
+end
+
+@ The code will have to change in this place if |glue_ratio| is
+a structured type instead of an ordinary |real|. Note that this routine
+should avoid arithmetic errors even if the |glue_set| field holds an
+arbitrary random value. The following code assumes that a properly
+formed nonzero |real| number has absolute value $2^{20}$ or more when
+it is regarded as an integer; this precaution was adequate to prevent
+floating point underflow on the author's computer.
+@^system dependencies@>
+@^dirty \PASCAL@>
+
+@<Display the value of |glue_set(p)|@>=
+g:=float(glue_set(p));
+if (g<>float_constant(0))and(glue_sign(p)<>normal) then
+ begin print(", glue set ");
+ if glue_sign(p)=shrinking then print("- ");
+ if abs(mem[p+glue_offset].int)<@'4000000 then print("?.?")
+ else if abs(g)>float_constant(20000) then
+ begin if g>float_constant(0) then print_char(">")
+ else print("< -");
+ print_glue(20000*unity,glue_order(p),0);
+ end
+ else print_glue(round(unity*g),glue_order(p),0);
+@^real multiplication@>
+ end
+
+@ @<Display rule |p|@>=
+begin print_esc("rule("); print_rule_dimen(height(p)); print_char("+");
+print_rule_dimen(depth(p)); print(")x"); print_rule_dimen(width(p));
+end
+
+@ @<Display insertion |p|@>=
+begin print_esc("insert"); print_int(qo(subtype(p)));
+print(", natural size "); print_scaled(height(p));
+print("; split("); print_spec(split_top_ptr(p),0);
+print_char(","); print_scaled(depth(p));
+print("); float cost "); print_int(float_cost(p));
+node_list_display(ins_ptr(p)); {recursive call}
+end
+
+@ @<Display glue |p|@>=
+if subtype(p)>=a_leaders then @<Display leaders |p|@>
+else begin print_esc("glue");
+ if subtype(p)<>normal then
+ begin print_char("(");
+ if subtype(p)<cond_math_glue then
+ print_skip_param(subtype(p)-1)
+ else if subtype(p)=cond_math_glue then print_esc("nonscript")
+ else print_esc("mskip");
+ print_char(")");
+ end;
+ if subtype(p)<>cond_math_glue then
+ begin print_char(" ");
+ if subtype(p)<cond_math_glue then print_spec(glue_ptr(p),0)
+ else print_spec(glue_ptr(p),"mu");
+ end;
+ end
+
+@ @<Display leaders |p|@>=
+begin print_esc("");
+if subtype(p)=c_leaders then print_char("c")
+else if subtype(p)=x_leaders then print_char("x");
+print("leaders "); print_spec(glue_ptr(p),0);
+node_list_display(leader_ptr(p)); {recursive call}
+end
+
+@ An ``explicit'' kern value is indicated implicitly by an explicit space.
+
+@<Display kern |p|@>=
+if subtype(p)<>mu_glue then
+ begin print_esc("kern");
+ if subtype(p)<>normal then print_char(" ");
+ print_scaled(width(p));
+ if subtype(p)=acc_kern then print(" (for accent)");
+@.for accent@>
+ end
+else begin print_esc("mkern"); print_scaled(width(p)); print("mu");
+ end
+
+@ @<Display math node |p|@>=
+begin print_esc("math");
+if subtype(p)=before then print("on")
+else print("off");
+if width(p)<>0 then
+ begin print(", surrounded "); print_scaled(width(p));
+ end;
+end
+
+@ @<Display ligature |p|@>=
+begin print_font_and_char(lig_char(p)); print(" (ligature ");
+if subtype(p)>1 then print_char("|");
+font_in_short_display:=font(lig_char(p)); short_display(lig_ptr(p));
+if odd(subtype(p)) then print_char("|");
+print_char(")");
+end
+
+@ @<Display penalty |p|@>=
+begin print_esc("penalty "); print_int(penalty(p));
+end
+
+@ The |post_break| list of a discretionary node is indicated by a prefixed
+`\.{\char'174}' instead of the `\..' before the |pre_break| list.
+
+@<Display discretionary |p|@>=
+begin print_esc("discretionary");
+if replace_count(p)>0 then
+ begin print(" replacing "); print_int(replace_count(p));
+ end;
+node_list_display(pre_break(p)); {recursive call}
+append_char("|"); show_node_list(post_break(p)); flush_char; {recursive call}
+end
+
+@ @<Display mark |p|@>=
+begin print_esc("mark"); print_mark(mark_ptr(p));
+end
+
+@ @<Display adjustment |p|@>=
+begin print_esc("vadjust"); node_list_display(adjust_ptr(p)); {recursive call}
+end
+
+@ The recursive machinery is started by calling |show_box|.
+@^recursion@>
+
+@p procedure show_box(@!p:pointer);
+begin @<Assign the values |depth_threshold:=show_box_depth| and
+ |breadth_max:=show_box_breadth|@>;
+if breadth_max<=0 then breadth_max:=5;
+if pool_ptr+depth_threshold>=pool_size then
+ depth_threshold:=pool_size-pool_ptr-1;
+ {now there's enough room for prefix string}
+show_node_list(p); {the show starts at |p|}
+print_ln;
+end;
+
+@* \[13] Destroying boxes.
+When we are done with a node list, we are obliged to return it to free
+storage, including all of its sublists. The recursive procedure
+|flush_node_list| does this for us.
+
+@ First, however, we shall consider two non-recursive procedures that do
+simpler tasks. The first of these, |delete_token_ref|, is called when
+a pointer to a token list's reference count is being removed. This means
+that the token list should disappear if the reference count was |null|,
+otherwise the count should be decreased by one.
+@^reference counts@>
+
+@d token_ref_count(#) == info(#) {reference count preceding a token list}
+
+@p procedure delete_token_ref(@!p:pointer); {|p| points to the reference count
+ of a token list that is losing one reference}
+begin if token_ref_count(p)=null then flush_list(p)
+else decr(token_ref_count(p));
+end;
+
+@ Similarly, |delete_glue_ref| is called when a pointer to a glue
+specification is being withdrawn.
+@^reference counts@>
+@d fast_delete_glue_ref(#)==@t@>@;@/
+ begin if glue_ref_count(#)=null then free_node(#,glue_spec_size)
+ else decr(glue_ref_count(#));
+ end
+
+@p procedure delete_glue_ref(@!p:pointer); {|p| points to a glue specification}
+fast_delete_glue_ref(p);
+
+@ Now we are ready to delete any node list, recursively.
+In practice, the nodes deleted are usually charnodes (about 2/3 of the time),
+and they are glue nodes in about half of the remaining cases.
+@^recursion@>
+
+@p procedure flush_node_list(@!p:pointer); {erase list of nodes starting at |p|}
+label done; {go here when node |p| has been freed}
+var q:pointer; {successor to node |p|}
+begin while p<>null do
+@^inner loop@>
+ begin q:=link(p);
+ if is_char_node(p) then free_avail(p)
+ else begin case type(p) of
+ hlist_node,vlist_node,unset_node: begin flush_node_list(list_ptr(p));
+ free_node(p,box_node_size); goto done;
+ end;
+ rule_node: begin free_node(p,rule_node_size); goto done;
+ end;
+ ins_node: begin flush_node_list(ins_ptr(p));
+ delete_glue_ref(split_top_ptr(p));
+ free_node(p,ins_node_size); goto done;
+ end;
+ whatsit_node: @<Wipe out the whatsit node |p| and |goto done|@>;
+ glue_node: begin fast_delete_glue_ref(glue_ptr(p));
+ if leader_ptr(p)<>null then flush_node_list(leader_ptr(p));
+ end;
+ kern_node,math_node,penalty_node: do_nothing;
+ ligature_node: flush_node_list(lig_ptr(p));
+ mark_node: delete_token_ref(mark_ptr(p));
+ disc_node: begin flush_node_list(pre_break(p));
+ flush_node_list(post_break(p));
+ end;
+ adjust_node: flush_node_list(adjust_ptr(p));
+ @t\4@>@<Cases of |flush_node_list| that arise in mlists only@>@;
+ othercases confusion("flushing")
+@:this can't happen flushing}{\quad flushing@>
+ endcases;@/
+ free_node(p,small_node_size);
+ done:end;
+ p:=q;
+ end;
+end;
+
+@* \[14] Copying boxes.
+Another recursive operation that acts on boxes is sometimes needed: The
+procedure |copy_node_list| returns a pointer to another node list that has
+the same structure and meaning as the original. Note that since glue
+specifications and token lists have reference counts, we need not make
+copies of them. Reference counts can never get too large to fit in a
+halfword, since each pointer to a node is in a different memory address,
+and the total number of memory addresses fits in a halfword.
+@^recursion@>
+@^reference counts@>
+
+(Well, there actually are also references from outside |mem|; if the
+|save_stack| is made arbitrarily large, it would theoretically be possible
+to break \TeX\ by overflowing a reference count. But who would want to do that?)
+
+@d add_token_ref(#)==incr(token_ref_count(#)) {new reference to a token list}
+@d add_glue_ref(#)==incr(glue_ref_count(#)) {new reference to a glue spec}
+
+@ The copying procedure copies words en masse without bothering
+to look at their individual fields. If the node format changes---for
+example, if the size is altered, or if some link field is moved to another
+relative position---then this code may need to be changed too.
+@^data structure assumptions@>
+
+@p function copy_node_list(@!p:pointer):pointer; {makes a duplicate of the
+ node list that starts at |p| and returns a pointer to the new list}
+var h:pointer; {temporary head of copied list}
+@!q:pointer; {previous position in new list}
+@!r:pointer; {current node being fabricated for new list}
+@!words:0..5; {number of words remaining to be copied}
+begin h:=get_avail; q:=h;
+while p<>null do
+ begin @<Make a copy of node |p| in node |r|@>;
+ link(q):=r; q:=r; p:=link(p);
+ end;
+link(q):=null; q:=link(h); free_avail(h);
+copy_node_list:=q;
+end;
+
+@ @<Make a copy of node |p|...@>=
+words:=1; {this setting occurs in more branches than any other}
+if is_char_node(p) then r:=get_avail
+else @<Case statement to copy different types and set |words| to the number
+ of initial words not yet copied@>;
+while words>0 do
+ begin decr(words); mem[r+words]:=mem[p+words];
+ end
+
+@ @<Case statement to copy...@>=
+case type(p) of
+hlist_node,vlist_node,unset_node: begin r:=get_node(box_node_size);
+ mem[r+6]:=mem[p+6]; mem[r+5]:=mem[p+5]; {copy the last two words}
+ list_ptr(r):=copy_node_list(list_ptr(p)); {this affects |mem[r+5]|}
+ words:=5;
+ end;
+rule_node: begin r:=get_node(rule_node_size); words:=rule_node_size;
+ end;
+ins_node: begin r:=get_node(ins_node_size); mem[r+4]:=mem[p+4];
+ add_glue_ref(split_top_ptr(p));
+ ins_ptr(r):=copy_node_list(ins_ptr(p)); {this affects |mem[r+4]|}
+ words:=ins_node_size-1;
+ end;
+whatsit_node:@<Make a partial copy of the whatsit node |p| and make |r|
+ point to it; set |words| to the number of initial words not yet copied@>;
+glue_node: begin r:=get_node(small_node_size); add_glue_ref(glue_ptr(p));
+ glue_ptr(r):=glue_ptr(p); leader_ptr(r):=copy_node_list(leader_ptr(p));
+ end;
+kern_node,math_node,penalty_node: begin r:=get_node(small_node_size);
+ words:=small_node_size;
+ end;
+ligature_node: begin r:=get_node(small_node_size);
+ mem[lig_char(r)]:=mem[lig_char(p)]; {copy |font| and |character|}
+ lig_ptr(r):=copy_node_list(lig_ptr(p));
+ end;
+disc_node: begin r:=get_node(small_node_size);
+ pre_break(r):=copy_node_list(pre_break(p));
+ post_break(r):=copy_node_list(post_break(p));
+ end;
+mark_node: begin r:=get_node(small_node_size); add_token_ref(mark_ptr(p));
+ words:=small_node_size;
+ end;
+adjust_node: begin r:=get_node(small_node_size);
+ adjust_ptr(r):=copy_node_list(adjust_ptr(p));
+ end; {|words=1=small_node_size-1|}
+othercases confusion("copying")
+@:this can't happen copying}{\quad copying@>
+endcases
+
+@* \[15] The command codes.
+Before we can go any further, we need to define symbolic names for the internal
+code numbers that represent the various commands obeyed by \TeX. These codes
+are somewhat arbitrary, but not completely so. For example, the command
+codes for character types are fixed by the language, since a user says,
+e.g., `\.{\\catcode \`\\\${} = 3}' to make \.{\char'44} a math delimiter,
+and the command code |math_shift| is equal to~3. Some other codes have
+been made adjacent so that |case| statements in the program need not consider
+cases that are widely spaced, or so that |case| statements can be replaced
+by |if| statements.
+
+At any rate, here is the list, for future reference. First come the
+``catcode'' commands, several of which share their numeric codes with
+ordinary commands when the catcode cannot emerge from \TeX's scanning routine.
+
+@d escape=0 {escape delimiter (called \.\\ in {\sl The \TeX book\/})}
+@:TeXbook}{\sl The \TeX book@>
+@d relax=0 {do nothing ( \.{\\relax} )}
+@d left_brace=1 {beginning of a group ( \.\{ )}
+@d right_brace=2 {ending of a group ( \.\} )}
+@d math_shift=3 {mathematics shift character ( \.\$ )}
+@d tab_mark=4 {alignment delimiter ( \.\&, \.{\\span} )}
+@d car_ret=5 {end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} )}
+@d out_param=5 {output a macro parameter}
+@d mac_param=6 {macro parameter symbol ( \.\# )}
+@d sup_mark=7 {superscript ( \.{\char'136} )}
+@d sub_mark=8 {subscript ( \.{\char'137} )}
+@d ignore=9 {characters to ignore ( \.{\^\^@@} )}
+@d endv=9 {end of \<v_j> list in alignment template}
+@d spacer=10 {characters equivalent to blank space ( \.{\ } )}
+@d letter=11 {characters regarded as letters ( \.{A..Z}, \.{a..z} )}
+@d other_char=12 {none of the special character types}
+@d active_char=13 {characters that invoke macros ( \.{\char`\~} )}
+@d par_end=13 {end of paragraph ( \.{\\par} )}
+@d match=13 {match a macro parameter}
+@d comment=14 {characters that introduce comments ( \.\% )}
+@d end_match=14 {end of parameters to macro}
+@d stop=14 {end of job ( \.{\\end}, \.{\\dump} )}
+@d invalid_char=15 {characters that shouldn't appear ( \.{\^\^?} )}
+@d delim_num=15 {specify delimiter numerically ( \.{\\delimiter} )}
+@d max_char_code=15 {largest catcode for individual characters}
+
+@ Next are the ordinary run-of-the-mill command codes. Codes that are
+|min_internal| or more represent internal quantities that might be
+expanded by `\.{\\the}'.
+
+@d char_num=16 {character specified numerically ( \.{\\char} )}
+@d math_char_num=17 {explicit math code ( \.{\\mathchar} )}
+@d mark=18 {mark definition ( \.{\\mark} )}
+@d xray=19 {peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~)}
+@d make_box=20 {make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~)}
+@d hmove=21 {horizontal motion ( \.{\\moveleft}, \.{\\moveright} )}
+@d vmove=22 {vertical motion ( \.{\\raise}, \.{\\lower} )}
+@d un_hbox=23 {unglue a box ( \.{\\unhbox}, \.{\\unhcopy} )}
+@d un_vbox=24 {unglue a box ( \.{\\unvbox}, \.{\\unvcopy} )}
+@d remove_item=25 {nullify last item ( \.{\\unpenalty},
+ \.{\\unkern}, \.{\\unskip} )}
+@d hskip=26 {horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~)}
+@d vskip=27 {vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~)}
+@d mskip=28 {math glue ( \.{\\mskip} )}
+@d kern=29 {fixed space ( \.{\\kern})}
+@d mkern=30 {math kern ( \.{\\mkern} )}
+@d leader_ship=31 {use a box ( \.{\\shipout}, \.{\\leaders}, etc.~)}
+@d halign=32 {horizontal table alignment ( \.{\\halign} )}
+@d valign=33 {vertical table alignment ( \.{\\valign} )}
+@d no_align=34 {temporary escape from alignment ( \.{\\noalign} )}
+@d vrule=35 {vertical rule ( \.{\\vrule} )}
+@d hrule=36 {horizontal rule ( \.{\\hrule} )}
+@d insert=37 {vlist inserted in box ( \.{\\insert} )}
+@d vadjust=38 {vlist inserted in enclosing paragraph ( \.{\\vadjust} )}
+@d ignore_spaces=39 {gobble |spacer| tokens ( \.{\\ignorespaces} )}
+@d after_assignment=40 {save till assignment is done ( \.{\\afterassignment} )}
+@d after_group=41 {save till group is done ( \.{\\aftergroup} )}
+@d break_penalty=42 {additional badness ( \.{\\penalty} )}
+@d start_par=43 {begin paragraph ( \.{\\indent}, \.{\\noindent} )}
+@d ital_corr=44 {italic correction ( \.{\\/} )}
+@d accent=45 {attach accent in text ( \.{\\accent} )}
+@d math_accent=46 {attach accent in math ( \.{\\mathaccent} )}
+@d discretionary=47 {discretionary texts ( \.{\\-}, \.{\\discretionary} )}
+@d eq_no=48 {equation number ( \.{\\eqno}, \.{\\leqno} )}
+@d left_right=49 {variable delimiter ( \.{\\left}, \.{\\right} )}
+@d math_comp=50 {component of formula ( \.{\\mathbin}, etc.~)}
+@d limit_switch=51 {diddle limit conventions ( \.{\\displaylimits}, etc.~)}
+@d above=52 {generalized fraction ( \.{\\above}, \.{\\atop}, etc.~)}
+@d math_style=53 {style specification ( \.{\\displaystyle}, etc.~)}
+@d math_choice=54 {choice specification ( \.{\\mathchoice} )}
+@d non_script=55 {conditional math glue ( \.{\\nonscript} )}
+@d vcenter=56 {vertically center a vbox ( \.{\\vcenter} )}
+@d case_shift=57 {force specific case ( \.{\\lowercase}, \.{\\uppercase}~)}
+@d message=58 {send to user ( \.{\\message}, \.{\\errmessage} )}
+@d extension=59 {extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~)}
+@d in_stream=60 {files for reading ( \.{\\openin}, \.{\\closein} )}
+@d begin_group=61 {begin local grouping ( \.{\\begingroup} )}
+@d end_group=62 {end local grouping ( \.{\\endgroup} )}
+@d omit=63 {omit alignment template ( \.{\\omit} )}
+@d ex_space=64 {explicit space ( \.{\\\ } )}
+@d no_boundary=65 {suppress boundary ligatures ( \.{\\noboundary} )}
+@d radical=66 {square root and similar signs ( \.{\\radical} )}
+@d end_cs_name=67 {end control sequence ( \.{\\endcsname} )}
+@d min_internal=68 {the smallest code that can follow \.{\\the}}
+@d char_given=68 {character code defined by \.{\\chardef}}
+@d math_given=69 {math code defined by \.{\\mathchardef}}
+@d last_item=70 {most recent item ( \.{\\lastpenalty},
+ \.{\\lastkern}, \.{\\lastskip} )}
+@d max_non_prefixed_command=70 {largest command code that can't be \.{\\global}}
+
+@ The next codes are special; they all relate to mode-independent
+assignment of values to \TeX's internal registers or tables.
+Codes that are |max_internal| or less represent internal quantities
+that might be expanded by `\.{\\the}'.
+
+@d toks_register=71 {token list register ( \.{\\toks} )}
+@d assign_toks=72 {special token list ( \.{\\output}, \.{\\everypar}, etc.~)}
+@d assign_int=73 {user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~)}
+@d assign_dimen=74 {user-defined length ( \.{\\hsize}, etc.~)}
+@d assign_glue=75 {user-defined glue ( \.{\\baselineskip}, etc.~)}
+@d assign_mu_glue=76 {user-defined muglue ( \.{\\thinmuskip}, etc.~)}
+@d assign_font_dimen=77 {user-defined font dimension ( \.{\\fontdimen} )}
+@d assign_font_int=78 {user-defined font integer ( \.{\\hyphenchar},
+ \.{\\skewchar} )}
+@d set_aux=79 {specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )}
+@d set_prev_graf=80 {specify state info ( \.{\\prevgraf} )}
+@d set_page_dimen=81 {specify state info ( \.{\\pagegoal}, etc.~)}
+@d set_page_int=82 {specify state info ( \.{\\deadcycles},
+ \.{\\insertpenalties} )}
+@d set_box_dimen=83 {change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} )}
+@d set_shape=84 {specify fancy paragraph shape ( \.{\\parshape} )}
+@d def_code=85 {define a character code ( \.{\\catcode}, etc.~)}
+@d def_family=86 {declare math fonts ( \.{\\textfont}, etc.~)}
+@d set_font=87 {set current font ( font identifiers )}
+@d def_font=88 {define a font file ( \.{\\font} )}
+@d register=89 {internal register ( \.{\\count}, \.{\\dimen}, etc.~)}
+@d max_internal=89 {the largest code that can follow \.{\\the}}
+@d advance=90 {advance a register or parameter ( \.{\\advance} )}
+@d multiply=91 {multiply a register or parameter ( \.{\\multiply} )}
+@d divide=92 {divide a register or parameter ( \.{\\divide} )}
+@d prefix=93 {qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} )}
+@d let=94 {assign a command code ( \.{\\let}, \.{\\futurelet} )}
+@d shorthand_def=95 {code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)}
+@d read_to_cs=96 {read into a control sequence ( \.{\\read} )}
+@d def=97 {macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )}
+@d set_box=98 {set a box ( \.{\\setbox} )}
+@d hyph_data=99 {hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )}
+@d set_interaction=100 {define level of interaction ( \.{\\batchmode}, etc.~)}
+@d max_command=100 {the largest command code seen at |big_switch|}
+
+@ The remaining command codes are extra special, since they cannot get through
+\TeX's scanner to the main control routine. They have been given values higher
+than |max_command| so that their special nature is easily discernible.
+The ``expandable'' commands come first.
+
+@d undefined_cs=max_command+1 {initial state of most |eq_type| fields}
+@d expand_after=max_command+2 {special expansion ( \.{\\expandafter} )}
+@d no_expand=max_command+3 {special nonexpansion ( \.{\\noexpand} )}
+@d input=max_command+4 {input a source file ( \.{\\input}, \.{\\endinput} )}
+@d if_test=max_command+5 {conditional text ( \.{\\if}, \.{\\ifcase}, etc.~)}
+@d fi_or_else=max_command+6 {delimiters for conditionals ( \.{\\else}, etc.~)}
+@d cs_name=max_command+7 {make a control sequence from tokens ( \.{\\csname} )}
+@d convert=max_command+8 {convert to text ( \.{\\number}, \.{\\string}, etc.~)}
+@d the=max_command+9 {expand an internal quantity ( \.{\\the} )}
+@d top_bot_mark=max_command+10 {inserted mark ( \.{\\topmark}, etc.~)}
+@d call=max_command+11 {non-long, non-outer control sequence}
+@d long_call=max_command+12 {long, non-outer control sequence}
+@d outer_call=max_command+13 {non-long, outer control sequence}
+@d long_outer_call=max_command+14 {long, outer control sequence}
+@d end_template=max_command+15 {end of an alignment template}
+@d dont_expand=max_command+16 {the following token was marked by \.{\\noexpand}}
+@d glue_ref=max_command+17 {the equivalent points to a glue specification}
+@d shape_ref=max_command+18 {the equivalent points to a parshape specification}
+@d box_ref=max_command+19 {the equivalent points to a box node, or is |null|}
+@d data=max_command+20 {the equivalent is simply a halfword number}
+
+@* \[16] The semantic nest.
+\TeX\ is typically in the midst of building many lists at once. For example,
+when a math formula is being processed, \TeX\ is in math mode and
+working on an mlist; this formula has temporarily interrupted \TeX\ from
+being in horizontal mode and building the hlist of a paragraph; and this
+paragraph has temporarily interrupted \TeX\ from being in vertical mode
+and building the vlist for the next page of a document. Similarly, when a
+\.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is temporarily
+interrupted from working in restricted horizontal mode, and it enters
+internal vertical mode. The ``semantic nest'' is a stack that
+keeps track of what lists and modes are currently suspended.
+
+At each level of processing we are in one of six modes:
+
+\yskip\hang|vmode| stands for vertical mode (the page builder);
+
+\hang|hmode| stands for horizontal mode (the paragraph builder);
+
+\hang|mmode| stands for displayed formula mode;
+
+\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox});
+
+\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox});
+
+\hang|-mmode| stands for math formula mode (not displayed).
+
+\yskip\noindent The mode is temporarily set to zero while processing \.{\\write}
+texts in the |ship_out| routine.
+
+Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that
+\TeX's ``big semantic switch'' can select the appropriate thing to
+do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current
+mode and |cur_cmd| is the current command code.
+
+@d vmode=1 {vertical mode}
+@d hmode=vmode+max_command+1 {horizontal mode}
+@d mmode=hmode+max_command+1 {math mode}
+
+@p procedure print_mode(@!m:integer); {prints the mode represented by |m|}
+begin if m>0 then
+ case m div (max_command+1) of
+ 0:print("vertical");
+ 1:print("horizontal");
+ 2:print("display math");
+ end
+else if m=0 then print("no")
+else case (-m) div (max_command+1) of
+ 0:print("internal vertical");
+ 1:print("restricted horizontal");
+ 2:print("math");
+ end;
+print(" mode");
+end;
+
+@ The state of affairs at any semantic level can be represented by
+five values:
+
+\yskip\hang|mode| is the number representing the semantic mode, as
+just explained.
+
+\yskip\hang|head| is a |pointer| to a list head for the list being built;
+|link(head)| therefore points to the first element of the list, or
+to |null| if the list is empty.
+
+\yskip\hang|tail| is a |pointer| to the final node of the list being
+built; thus, |tail=head| if and only if the list is empty.
+
+\yskip\hang|prev_graf| is the number of lines of the current paragraph that
+have already been put into the present vertical list.
+
+\yskip\hang|aux| is an auxiliary |memory_word| that gives further information
+that is needed to characterize the situation.
+
+\yskip\noindent
+In vertical mode, |aux| is also known as |prev_depth|; it is the scaled
+value representing the depth of the previous box, for use in baseline
+calculations, or it is |<=-1000|pt if the next box on the vertical list is to
+be exempt from baseline calculations. In horizontal mode, |aux| is also
+known as |space_factor| and |clang|; it holds the current space factor used in
+spacing calculations, and the current language used for hyphenation.
+(The value of |clang| is undefined in restricted horizontal mode.)
+In math mode, |aux| is also known as |incompleat_noad|; if
+not |null|, it points to a record that represents the numerator of a
+generalized fraction for which the denominator is currently being formed
+in the current list.
+
+There is also a sixth quantity, |mode_line|, which correlates
+the semantic nest with the user's input; |mode_line| contains the source
+line number at which the current level of nesting was entered. The negative
+of this line number is the |mode_line| at the level of the
+user's output routine.
+
+In horizontal mode, the |prev_graf| field is used for initial language data.
+
+The semantic nest is an array called |nest| that holds the |mode|, |head|,
+|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels
+below the currently active one. Information about the currently active
+level is kept in the global quantities |mode|, |head|, |tail|, |prev_graf|,
+|aux|, and |mode_line|, which live in a \PASCAL\ record that is ready to
+be pushed onto |nest| if necessary.
+
+@d ignore_depth==-65536000 {|prev_depth| value that is ignored}
+
+@<Types...@>=
+@!list_state_record=record@!mode_field:-mmode..mmode;@+
+ @!head_field,@!tail_field: pointer;
+ @!pg_field,@!ml_field: integer;@+
+ @!aux_field: memory_word;
+ end;
+
+@ @d mode==cur_list.mode_field {current mode}
+@d head==cur_list.head_field {header node of current list}
+@d tail==cur_list.tail_field {final node on current list}
+@d prev_graf==cur_list.pg_field {number of paragraph lines accumulated}
+@d aux==cur_list.aux_field {auxiliary data about the current list}
+@d prev_depth==aux.sc {the name of |aux| in vertical mode}
+@d space_factor==aux.hh.lh {part of |aux| in horizontal mode}
+@d clang==aux.hh.rh {the other part of |aux| in horizontal mode}
+@d incompleat_noad==aux.int {the name of |aux| in math mode}
+@d mode_line==cur_list.ml_field {source file line number at beginning of list}
+
+@<Glob...@>=
+@!nest:array[0..nest_size] of list_state_record;
+@!nest_ptr:0..nest_size; {first unused location of |nest|}
+@!max_nest_stack:0..nest_size; {maximum of |nest_ptr| when pushing}
+@!cur_list:list_state_record; {the ``top'' semantic state}
+@!shown_mode:-mmode..mmode; {most recent mode shown by \.{\\tracingcommands}}
+
+@ Here is a common way to make the current list grow:
+
+@d tail_append(#)==begin link(tail):=#; tail:=link(tail);
+ end
+
+@ We will see later that the vertical list at the bottom semantic level is split
+into two parts; the ``current page'' runs from |page_head| to |page_tail|,
+and the ``contribution list'' runs from |contrib_head| to |tail| of
+semantic level zero. The idea is that contributions are first formed in
+vertical mode, then ``contributed'' to the current page (during which time
+the page-breaking decisions are made). For now, we don't need to know
+any more details about the page-building process.
+
+@<Set init...@>=
+nest_ptr:=0; max_nest_stack:=0;
+mode:=vmode; head:=contrib_head; tail:=contrib_head;
+prev_depth:=ignore_depth; mode_line:=0;
+prev_graf:=0; shown_mode:=0;
+@<Start a new current page@>;
+
+@ When \TeX's work on one level is interrupted, the state is saved by
+calling |push_nest|. This routine changes |head| and |tail| so that
+a new (empty) list is begun; it does not change |mode| or |aux|.
+
+@p procedure push_nest; {enter a new semantic level, save the old}
+begin if nest_ptr>max_nest_stack then
+ begin max_nest_stack:=nest_ptr;
+ if nest_ptr=nest_size then overflow("semantic nest size",nest_size);
+@:TeX capacity exceeded semantic nest size}{\quad semantic nest size@>
+ end;
+nest[nest_ptr]:=cur_list; {stack the record}
+incr(nest_ptr); head:=get_avail; tail:=head; prev_graf:=0; mode_line:=line;
+end;
+
+@ Conversely, when \TeX\ is finished on the current level, the former
+state is restored by calling |pop_nest|. This routine will never be
+called at the lowest semantic level, nor will it be called unless |head|
+is a node that should be returned to free memory.
+
+@p procedure pop_nest; {leave a semantic level, re-enter the old}
+begin free_avail(head); decr(nest_ptr); cur_list:=nest[nest_ptr];
+end;
+
+@ Here is a procedure that displays what \TeX\ is working on, at all levels.
+
+@p procedure@?print_totals; forward;@t\2@>
+procedure show_activities;
+var p:0..nest_size; {index into |nest|}
+@!m:-mmode..mmode; {mode}
+@!a:memory_word; {auxiliary}
+@!q,@!r:pointer; {for showing the current page}
+@!t:integer; {ditto}
+begin nest[nest_ptr]:=cur_list; {put the top level into the array}
+print_nl(""); print_ln;
+for p:=nest_ptr downto 0 do
+ begin m:=nest[p].mode_field; a:=nest[p].aux_field;
+ print_nl("### "); print_mode(m);
+ print(" entered at line "); print_int(abs(nest[p].ml_field));
+ if m=hmode then if nest[p].pg_field <> @'40600000 then
+ begin print(" (language"); print_int(nest[p].pg_field mod @'200000);
+ print(":hyphenmin"); print_int(nest[p].pg_field div @'20000000);
+ print_char(","); print_int((nest[p].pg_field div @'200000) mod @'100);
+ print_char(")");
+ end;
+ if nest[p].ml_field<0 then print(" (\output routine)");
+ if p=0 then
+ begin @<Show the status of the current page@>;
+ if link(contrib_head)<>null then
+ print_nl("### recent contributions:");
+ end;
+ show_box(link(nest[p].head_field));
+ @<Show the auxiliary field, |a|@>;
+ end;
+end;
+
+@ @<Show the auxiliary...@>=
+case abs(m) div (max_command+1) of
+0: begin print_nl("prevdepth ");
+ if a.sc<=ignore_depth then print("ignored")
+ else print_scaled(a.sc);
+ if nest[p].pg_field<>0 then
+ begin print(", prevgraf ");
+ print_int(nest[p].pg_field); print(" line");
+ if nest[p].pg_field<>1 then print_char("s");
+ end;
+ end;
+1: begin print_nl("spacefactor "); print_int(a.hh.lh);
+ if m>0 then@+ if a.hh.rh>0 then
+ begin print(", current language "); print_int(a.hh.rh);@+
+ end;
+ end;
+2: if a.int<>null then
+ begin print("this will be denominator of:"); show_box(a.int);@+
+ end;
+end {there are no other cases}
+
+@* \[17] The table of equivalents.
+Now that we have studied the data structures for \TeX's semantic routines,
+we ought to consider the data structures used by its syntactic routines. In
+other words, our next concern will be
+the tables that \TeX\ looks at when it is scanning
+what the user has written.
+
+The biggest and most important such table is called |eqtb|. It holds the
+current ``equivalents'' of things; i.e., it explains what things mean
+or what their current values are, for all quantities that are subject to
+the nesting structure provided by \TeX's grouping mechanism. There are six
+parts to |eqtb|:
+
+\yskip\hangg 1) |eqtb[active_base..(hash_base-1)]| holds the current
+equivalents of single-character control sequences.
+
+\yskip\hangg 2) |eqtb[hash_base..(glue_base-1)]| holds the current
+equivalents of multiletter control sequences.
+
+\yskip\hangg 3) |eqtb[glue_base..(local_base-1)]| holds the current
+equivalents of glue parameters like the current baselineskip.
+
+\yskip\hangg 4) |eqtb[local_base..(int_base-1)]| holds the current
+equivalents of local halfword quantities like the current box registers,
+the current ``catcodes,'' the current font, and a pointer to the current
+paragraph shape.
+
+\yskip\hangg 5) |eqtb[int_base..(dimen_base-1)]| holds the current
+equivalents of fullword integer parameters like the current hyphenation
+penalty.
+
+\yskip\hangg 6) |eqtb[dimen_base..eqtb_size]| holds the current equivalents
+of fullword dimension parameters like the current hsize or amount of
+hanging indentation.
+
+\yskip\noindent Note that, for example, the current amount of
+baselineskip glue is determined by the setting of a particular location
+in region~3 of |eqtb|, while the current meaning of the control sequence
+`\.{\\baselineskip}' (which might have been changed by \.{\\def} or
+\.{\\let}) appears in region~2.
+
+@ Each entry in |eqtb| is a |memory_word|. Most of these words are of type
+|two_halves|, and subdivided into three fields:
+
+\yskip\hangg 1) The |eq_level| (a quarterword) is the level of grouping at
+which this equivalent was defined. If the level is |level_zero|, the
+equivalent has never been defined; |level_one| refers to the outer level
+(outside of all groups), and this level is also used for global
+definitions that never go away. Higher levels are for equivalents that
+will disappear at the end of their group. @^global definitions@>
+
+\yskip\hangg 2) The |eq_type| (another quarterword) specifies what kind of
+entry this is. There are many types, since each \TeX\ primitive like
+\.{\\hbox}, \.{\\def}, etc., has its own special code. The list of
+command codes above includes all possible settings of the |eq_type| field.
+
+\yskip\hangg 3) The |equiv| (a halfword) is the current equivalent value.
+This may be a font number, a pointer into |mem|, or a variety of other
+things.
+
+@d eq_level_field(#)==#.hh.b1
+@d eq_type_field(#)==#.hh.b0
+@d equiv_field(#)==#.hh.rh
+@d eq_level(#)==eq_level_field(eqtb[#]) {level of definition}
+@d eq_type(#)==eq_type_field(eqtb[#]) {command code for equivalent}
+@d equiv(#)==equiv_field(eqtb[#]) {equivalent value}
+@d level_zero=min_quarterword {level for undefined quantities}
+@d level_one=level_zero+1 {outermost level for defined quantities}
+
+@ Many locations in |eqtb| have symbolic names. The purpose of the next
+paragraphs is to define these names, and to set up the initial values of the
+equivalents.
+
+In the first region we have 256 equivalents for ``active characters'' that
+act as control sequences, followed by 256 equivalents for single-character
+control sequences.
+
+Then comes region~2, which corresponds to the hash table that we will
+define later. The maximum address in this region is used for a dummy
+control sequence that is perpetually undefined. There also are several
+locations for control sequences that are perpetually defined
+(since they are used in error recovery).
+
+@d active_base=1 {beginning of region 1, for active character equivalents}
+@d single_base=active_base+256 {equivalents of one-character control sequences}
+@d null_cs=single_base+256 {equivalent of \.{\\csname\\endcsname}}
+@d hash_base=null_cs+1 {beginning of region 2, for the hash table}
+@d frozen_control_sequence=hash_base+hash_size {for error recovery}
+@d frozen_protection=frozen_control_sequence {inaccessible but definable}
+@d frozen_cr=frozen_control_sequence+1 {permanent `\.{\\cr}'}
+@d frozen_end_group=frozen_control_sequence+2 {permanent `\.{\\endgroup}'}
+@d frozen_right=frozen_control_sequence+3 {permanent `\.{\\right}'}
+@d frozen_fi=frozen_control_sequence+4 {permanent `\.{\\fi}'}
+@d frozen_end_template=frozen_control_sequence+5 {permanent `\.{\\endtemplate}'}
+@d frozen_endv=frozen_control_sequence+6 {second permanent `\.{\\endtemplate}'}
+@d frozen_relax=frozen_control_sequence+7 {permanent `\.{\\relax}'}
+@d end_write=frozen_control_sequence+8 {permanent `\.{\\endwrite}'}
+@d frozen_dont_expand=frozen_control_sequence+9
+ {permanent `\.{\\notexpanded:}'}
+@d frozen_null_font=frozen_control_sequence+10
+ {permanent `\.{\\nullfont}'}
+@d font_id_base=frozen_null_font-font_base
+ {begins table of 257 permanent font identifiers}
+@d undefined_control_sequence=frozen_null_font+257 {dummy location}
+@d glue_base=undefined_control_sequence+1 {beginning of region 3}
+
+@<Initialize table entries...@>=
+eq_type(undefined_control_sequence):=undefined_cs;
+equiv(undefined_control_sequence):=null;
+eq_level(undefined_control_sequence):=level_zero;
+for k:=active_base to undefined_control_sequence-1 do
+ eqtb[k]:=eqtb[undefined_control_sequence];
+
+@ Here is a routine that displays the current meaning of an |eqtb| entry
+in region 1 or~2. (Similar routines for the other regions will appear
+below.)
+
+@<Show equivalent |n|, in region 1 or 2@>=
+begin sprint_cs(n); print_char("="); print_cmd_chr(eq_type(n),equiv(n));
+if eq_type(n)>=call then
+ begin print_char(":"); show_token_list(link(equiv(n)),null,32);
+ end;
+end
+
+@ Region 3 of |eqtb| contains the 256 \.{\\skip} registers, as well as the
+glue parameters defined here. It is important that the ``muskip''
+parameters have larger numbers than the others.
+
+@d line_skip_code=0 {interline glue if |baseline_skip| is infeasible}
+@d baseline_skip_code=1 {desired glue between baselines}
+@d par_skip_code=2 {extra glue just above a paragraph}
+@d above_display_skip_code=3 {extra glue just above displayed math}
+@d below_display_skip_code=4 {extra glue just below displayed math}
+@d above_display_short_skip_code=5
+ {glue above displayed math following short lines}
+@d below_display_short_skip_code=6
+ {glue below displayed math following short lines}
+@d left_skip_code=7 {glue at left of justified lines}
+@d right_skip_code=8 {glue at right of justified lines}
+@d top_skip_code=9 {glue at top of main pages}
+@d split_top_skip_code=10 {glue at top of split pages}
+@d tab_skip_code=11 {glue between aligned entries}
+@d space_skip_code=12 {glue between words (if not |zero_glue|)}
+@d xspace_skip_code=13 {glue after sentences (if not |zero_glue|)}
+@d par_fill_skip_code=14 {glue on last line of paragraph}
+@d thin_mu_skip_code=15 {thin space in math formula}
+@d med_mu_skip_code=16 {medium space in math formula}
+@d thick_mu_skip_code=17 {thick space in math formula}
+@d glue_pars=18 {total number of glue parameters}
+@d skip_base=glue_base+glue_pars {table of 256 ``skip'' registers}
+@d mu_skip_base=skip_base+256 {table of 256 ``muskip'' registers}
+@d local_base=mu_skip_base+256 {beginning of region 4}
+@#
+@d skip(#)==equiv(skip_base+#) {|mem| location of glue specification}
+@d mu_skip(#)==equiv(mu_skip_base+#) {|mem| location of math glue spec}
+@d glue_par(#)==equiv(glue_base+#) {|mem| location of glue specification}
+@d line_skip==glue_par(line_skip_code)
+@d baseline_skip==glue_par(baseline_skip_code)
+@d par_skip==glue_par(par_skip_code)
+@d above_display_skip==glue_par(above_display_skip_code)
+@d below_display_skip==glue_par(below_display_skip_code)
+@d above_display_short_skip==glue_par(above_display_short_skip_code)
+@d below_display_short_skip==glue_par(below_display_short_skip_code)
+@d left_skip==glue_par(left_skip_code)
+@d right_skip==glue_par(right_skip_code)
+@d top_skip==glue_par(top_skip_code)
+@d split_top_skip==glue_par(split_top_skip_code)
+@d tab_skip==glue_par(tab_skip_code)
+@d space_skip==glue_par(space_skip_code)
+@d xspace_skip==glue_par(xspace_skip_code)
+@d par_fill_skip==glue_par(par_fill_skip_code)
+@d thin_mu_skip==glue_par(thin_mu_skip_code)
+@d med_mu_skip==glue_par(med_mu_skip_code)
+@d thick_mu_skip==glue_par(thick_mu_skip_code)
+
+@<Current |mem| equivalent of glue parameter number |n|@>=glue_par(n)
+
+@ Sometimes we need to convert \TeX's internal code numbers into symbolic
+form. The |print_skip_param| routine gives the symbolic name of a glue
+parameter.
+
+@<Declare the procedure called |print_skip_param|@>=
+procedure print_skip_param(@!n:integer);
+begin case n of
+line_skip_code: print_esc("lineskip");
+baseline_skip_code: print_esc("baselineskip");
+par_skip_code: print_esc("parskip");
+above_display_skip_code: print_esc("abovedisplayskip");
+below_display_skip_code: print_esc("belowdisplayskip");
+above_display_short_skip_code: print_esc("abovedisplayshortskip");
+below_display_short_skip_code: print_esc("belowdisplayshortskip");
+left_skip_code: print_esc("leftskip");
+right_skip_code: print_esc("rightskip");
+top_skip_code: print_esc("topskip");
+split_top_skip_code: print_esc("splittopskip");
+tab_skip_code: print_esc("tabskip");
+space_skip_code: print_esc("spaceskip");
+xspace_skip_code: print_esc("xspaceskip");
+par_fill_skip_code: print_esc("parfillskip");
+thin_mu_skip_code: print_esc("thinmuskip");
+med_mu_skip_code: print_esc("medmuskip");
+thick_mu_skip_code: print_esc("thickmuskip");
+othercases print("[unknown glue parameter!]")
+endcases;
+end;
+
+@ The symbolic names for glue parameters are put into \TeX's hash table
+by using the routine called |primitive|, defined below. Let us enter them
+now, so that we don't have to list all those parameter names anywhere else.
+
+@<Put each of \TeX's primitives into the hash table@>=
+primitive("lineskip",assign_glue,glue_base+line_skip_code);@/
+@!@:line_skip_}{\.{\\lineskip} primitive@>
+primitive("baselineskip",assign_glue,glue_base+baseline_skip_code);@/
+@!@:baseline_skip_}{\.{\\baselineskip} primitive@>
+primitive("parskip",assign_glue,glue_base+par_skip_code);@/
+@!@:par_skip_}{\.{\\parskip} primitive@>
+primitive("abovedisplayskip",assign_glue,glue_base+above_display_skip_code);@/
+@!@:above_display_skip_}{\.{\\abovedisplayskip} primitive@>
+primitive("belowdisplayskip",assign_glue,glue_base+below_display_skip_code);@/
+@!@:below_display_skip_}{\.{\\belowdisplayskip} primitive@>
+primitive("abovedisplayshortskip",
+ assign_glue,glue_base+above_display_short_skip_code);@/
+@!@:above_display_short_skip_}{\.{\\abovedisplayshortskip} primitive@>
+primitive("belowdisplayshortskip",
+ assign_glue,glue_base+below_display_short_skip_code);@/
+@!@:below_display_short_skip_}{\.{\\belowdisplayshortskip} primitive@>
+primitive("leftskip",assign_glue,glue_base+left_skip_code);@/
+@!@:left_skip_}{\.{\\leftskip} primitive@>
+primitive("rightskip",assign_glue,glue_base+right_skip_code);@/
+@!@:right_skip_}{\.{\\rightskip} primitive@>
+primitive("topskip",assign_glue,glue_base+top_skip_code);@/
+@!@:top_skip_}{\.{\\topskip} primitive@>
+primitive("splittopskip",assign_glue,glue_base+split_top_skip_code);@/
+@!@:split_top_skip_}{\.{\\splittopskip} primitive@>
+primitive("tabskip",assign_glue,glue_base+tab_skip_code);@/
+@!@:tab_skip_}{\.{\\tabskip} primitive@>
+primitive("spaceskip",assign_glue,glue_base+space_skip_code);@/
+@!@:space_skip_}{\.{\\spaceskip} primitive@>
+primitive("xspaceskip",assign_glue,glue_base+xspace_skip_code);@/
+@!@:xspace_skip_}{\.{\\xspaceskip} primitive@>
+primitive("parfillskip",assign_glue,glue_base+par_fill_skip_code);@/
+@!@:par_fill_skip_}{\.{\\parfillskip} primitive@>
+primitive("thinmuskip",assign_mu_glue,glue_base+thin_mu_skip_code);@/
+@!@:thin_mu_skip_}{\.{\\thinmuskip} primitive@>
+primitive("medmuskip",assign_mu_glue,glue_base+med_mu_skip_code);@/
+@!@:med_mu_skip_}{\.{\\medmuskip} primitive@>
+primitive("thickmuskip",assign_mu_glue,glue_base+thick_mu_skip_code);@/
+@!@:thick_mu_skip_}{\.{\\thickmuskip} primitive@>
+
+@ @<Cases of |print_cmd_chr| for symbolic printing of primitives@>=
+assign_glue,assign_mu_glue: if chr_code<skip_base then
+ print_skip_param(chr_code-glue_base)
+ else if chr_code<mu_skip_base then
+ begin print_esc("skip"); print_int(chr_code-skip_base);
+ end
+ else begin print_esc("muskip"); print_int(chr_code-mu_skip_base);
+ end;
+
+@ All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'.
+
+@<Initialize table entries...@>=
+equiv(glue_base):=zero_glue; eq_level(glue_base):=level_one;
+eq_type(glue_base):=glue_ref;
+for k:=glue_base+1 to local_base-1 do eqtb[k]:=eqtb[glue_base];
+glue_ref_count(zero_glue):=glue_ref_count(zero_glue)+local_base-glue_base;
+
+@ @<Show equivalent |n|, in region 3@>=
+if n<skip_base then
+ begin print_skip_param(n-glue_base); print_char("=");
+ if n<glue_base+thin_mu_skip_code then print_spec(equiv(n),"pt")
+ else print_spec(equiv(n),"mu");
+ end
+else if n<mu_skip_base then
+ begin print_esc("skip"); print_int(n-skip_base); print_char("=");
+ print_spec(equiv(n),"pt");
+ end
+else begin print_esc("muskip"); print_int(n-mu_skip_base); print_char("=");
+ print_spec(equiv(n),"mu");
+ end
+
+@ Region 4 of |eqtb| contains the local quantities defined here. The
+bulk of this region is taken up by five tables that are indexed by eight-bit
+characters; these tables are important to both the syntactic and semantic
+portions of \TeX. There are also a bunch of special things like font and
+token parameters, as well as the tables of \.{\\toks} and \.{\\box}
+registers.
+
+@d par_shape_loc=local_base {specifies paragraph shape}
+@d output_routine_loc=local_base+1 {points to token list for \.{\\output}}
+@d every_par_loc=local_base+2 {points to token list for \.{\\everypar}}
+@d every_math_loc=local_base+3 {points to token list for \.{\\everymath}}
+@d every_display_loc=local_base+4 {points to token list for \.{\\everydisplay}}
+@d every_hbox_loc=local_base+5 {points to token list for \.{\\everyhbox}}
+@d every_vbox_loc=local_base+6 {points to token list for \.{\\everyvbox}}
+@d every_job_loc=local_base+7 {points to token list for \.{\\everyjob}}
+@d every_cr_loc=local_base+8 {points to token list for \.{\\everycr}}
+@d err_help_loc=local_base+9 {points to token list for \.{\\errhelp}}
+@d toks_base=local_base+10 {table of 256 token list registers}
+@d box_base=toks_base+256 {table of 256 box registers}
+@d cur_font_loc=box_base+256 {internal font number outside math mode}
+@d math_font_base=cur_font_loc+1 {table of 48 math font numbers}
+@d cat_code_base=math_font_base+48
+ {table of 256 command codes (the ``catcodes'')}
+@d lc_code_base=cat_code_base+256 {table of 256 lowercase mappings}
+@d uc_code_base=lc_code_base+256 {table of 256 uppercase mappings}
+@d sf_code_base=uc_code_base+256 {table of 256 spacefactor mappings}
+@d math_code_base=sf_code_base+256 {table of 256 math mode mappings}
+@d int_base=math_code_base+256 {beginning of region 5}
+@#
+@d par_shape_ptr==equiv(par_shape_loc)
+@d output_routine==equiv(output_routine_loc)
+@d every_par==equiv(every_par_loc)
+@d every_math==equiv(every_math_loc)
+@d every_display==equiv(every_display_loc)
+@d every_hbox==equiv(every_hbox_loc)
+@d every_vbox==equiv(every_vbox_loc)
+@d every_job==equiv(every_job_loc)
+@d every_cr==equiv(every_cr_loc)
+@d err_help==equiv(err_help_loc)
+@d toks(#)==equiv(toks_base+#)
+@d box(#)==equiv(box_base+#)
+@d cur_font==equiv(cur_font_loc)
+@d fam_fnt(#)==equiv(math_font_base+#)
+@d cat_code(#)==equiv(cat_code_base+#)
+@d lc_code(#)==equiv(lc_code_base+#)
+@d uc_code(#)==equiv(uc_code_base+#)
+@d sf_code(#)==equiv(sf_code_base+#)
+@d math_code(#)==equiv(math_code_base+#)
+ {Note: |math_code(c)| is the true math code plus |min_halfword|}
+
+@<Put each...@>=
+primitive("output",assign_toks,output_routine_loc);
+@!@:output_}{\.{\\output} primitive@>
+primitive("everypar",assign_toks,every_par_loc);
+@!@:every_par_}{\.{\\everypar} primitive@>
+primitive("everymath",assign_toks,every_math_loc);
+@!@:every_math_}{\.{\\everymath} primitive@>
+primitive("everydisplay",assign_toks,every_display_loc);
+@!@:every_display_}{\.{\\everydisplay} primitive@>
+primitive("everyhbox",assign_toks,every_hbox_loc);
+@!@:every_hbox_}{\.{\\everyhbox} primitive@>
+primitive("everyvbox",assign_toks,every_vbox_loc);
+@!@:every_vbox_}{\.{\\everyvbox} primitive@>
+primitive("everyjob",assign_toks,every_job_loc);
+@!@:every_job_}{\.{\\everyjob} primitive@>
+primitive("everycr",assign_toks,every_cr_loc);
+@!@:every_cr_}{\.{\\everycr} primitive@>
+primitive("errhelp",assign_toks,err_help_loc);
+@!@:err_help_}{\.{\\errhelp} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+assign_toks: if chr_code>=toks_base then
+ begin print_esc("toks"); print_int(chr_code-toks_base);
+ end
+else case chr_code of
+ output_routine_loc: print_esc("output");
+ every_par_loc: print_esc("everypar");
+ every_math_loc: print_esc("everymath");
+ every_display_loc: print_esc("everydisplay");
+ every_hbox_loc: print_esc("everyhbox");
+ every_vbox_loc: print_esc("everyvbox");
+ every_job_loc: print_esc("everyjob");
+ every_cr_loc: print_esc("everycr");
+ othercases print_esc("errhelp")
+ endcases;
+
+@ We initialize most things to null or undefined values. An undefined font
+is represented by the internal code |font_base|.
+
+However, the character code tables are given initial values based on the
+conventional interpretation of ASCII code. These initial values should
+not be changed when \TeX\ is adapted for use with non-English languages;
+all changes to the initialization conventions should be made in format
+packages, not in \TeX\ itself, so that global interchange of formats is
+possible.
+
+@d null_font==font_base
+@d var_code==@'70000 {math code meaning ``use the current family''}
+
+@<Initialize table entries...@>=
+par_shape_ptr:=null; eq_type(par_shape_loc):=shape_ref;
+eq_level(par_shape_loc):=level_one;@/
+for k:=output_routine_loc to toks_base+255 do
+ eqtb[k]:=eqtb[undefined_control_sequence];
+box(0):=null; eq_type(box_base):=box_ref; eq_level(box_base):=level_one;
+for k:=box_base+1 to box_base+255 do eqtb[k]:=eqtb[box_base];
+cur_font:=null_font; eq_type(cur_font_loc):=data;
+eq_level(cur_font_loc):=level_one;@/
+for k:=math_font_base to math_font_base+47 do eqtb[k]:=eqtb[cur_font_loc];
+equiv(cat_code_base):=0; eq_type(cat_code_base):=data;
+eq_level(cat_code_base):=level_one;@/
+for k:=cat_code_base+1 to int_base-1 do eqtb[k]:=eqtb[cat_code_base];
+for k:=0 to 255 do
+ begin cat_code(k):=other_char; math_code(k):=hi(k); sf_code(k):=1000;
+ end;
+cat_code(carriage_return):=car_ret; cat_code(" "):=spacer;
+cat_code("\"):=escape; cat_code("%"):=comment;
+cat_code(invalid_code):=invalid_char; cat_code(null_code):=ignore;
+for k:="0" to "9" do math_code(k):=hi(k+var_code);
+for k:="A" to "Z" do
+ begin cat_code(k):=letter; cat_code(k+"a"-"A"):=letter;@/
+ math_code(k):=hi(k+var_code+@"100);
+ math_code(k+"a"-"A"):=hi(k+"a"-"A"+var_code+@"100);@/
+ lc_code(k):=k+"a"-"A"; lc_code(k+"a"-"A"):=k+"a"-"A";@/
+ uc_code(k):=k; uc_code(k+"a"-"A"):=k;@/
+ sf_code(k):=999;
+ end;
+
+@ @<Show equivalent |n|, in region 4@>=
+if n=par_shape_loc then
+ begin print_esc("parshape"); print_char("=");
+ if par_shape_ptr=null then print_char("0")
+ else print_int(info(par_shape_ptr));
+ end
+else if n<toks_base then
+ begin print_cmd_chr(assign_toks,n); print_char("=");
+ if equiv(n)<>null then show_token_list(link(equiv(n)),null,32);
+ end
+else if n<box_base then
+ begin print_esc("toks"); print_int(n-toks_base); print_char("=");
+ if equiv(n)<>null then show_token_list(link(equiv(n)),null,32);
+ end
+else if n<cur_font_loc then
+ begin print_esc("box"); print_int(n-box_base); print_char("=");
+ if equiv(n)=null then print("void")
+ else begin depth_threshold:=0; breadth_max:=1; show_node_list(equiv(n));
+ end;
+ end
+else if n<cat_code_base then @<Show the font identifier in |eqtb[n]|@>
+else @<Show the halfword code in |eqtb[n]|@>
+
+@ @<Show the font identifier in |eqtb[n]|@>=
+begin if n=cur_font_loc then print("current font")
+else if n<math_font_base+16 then
+ begin print_esc("textfont"); print_int(n-math_font_base);
+ end
+else if n<math_font_base+32 then
+ begin print_esc("scriptfont"); print_int(n-math_font_base-16);
+ end
+else begin print_esc("scriptscriptfont"); print_int(n-math_font_base-32);
+ end;
+print_char("=");@/
+print_esc(hash[font_id_base+equiv(n)].rh);
+ {that's |font_id_text(equiv(n))|}
+end
+
+@ @<Show the halfword code in |eqtb[n]|@>=
+if n<math_code_base then
+ begin if n<lc_code_base then
+ begin print_esc("catcode"); print_int(n-cat_code_base);
+ end
+ else if n<uc_code_base then
+ begin print_esc("lccode"); print_int(n-lc_code_base);
+ end
+ else if n<sf_code_base then
+ begin print_esc("uccode"); print_int(n-uc_code_base);
+ end
+ else begin print_esc("sfcode"); print_int(n-sf_code_base);
+ end;
+ print_char("="); print_int(equiv(n));
+ end
+else begin print_esc("mathcode"); print_int(n-math_code_base);
+ print_char("="); print_int(ho(equiv(n)));
+ end
+
+@ Region 5 of |eqtb| contains the integer parameters and registers defined
+here, as well as the |del_code| table. The latter table differs from the
+|cat_code..math_code| tables that precede it, since delimiter codes are
+fullword integers while the other kinds of codes occupy at most a
+halfword. This is what makes region~5 different from region~4. We will
+store the |eq_level| information in an auxiliary array of quarterwords
+that will be defined later.
+
+@d pretolerance_code=0 {badness tolerance before hyphenation}
+@d tolerance_code=1 {badness tolerance after hyphenation}
+@d line_penalty_code=2 {added to the badness of every line}
+@d hyphen_penalty_code=3 {penalty for break after discretionary hyphen}
+@d ex_hyphen_penalty_code=4 {penalty for break after explicit hyphen}
+@d club_penalty_code=5 {penalty for creating a club line}
+@d widow_penalty_code=6 {penalty for creating a widow line}
+@d display_widow_penalty_code=7 {ditto, just before a display}
+@d broken_penalty_code=8 {penalty for breaking a page at a broken line}
+@d bin_op_penalty_code=9 {penalty for breaking after a binary operation}
+@d rel_penalty_code=10 {penalty for breaking after a relation}
+@d pre_display_penalty_code=11
+ {penalty for breaking just before a displayed formula}
+@d post_display_penalty_code=12
+ {penalty for breaking just after a displayed formula}
+@d inter_line_penalty_code=13 {additional penalty between lines}
+@d double_hyphen_demerits_code=14 {demerits for double hyphen break}
+@d final_hyphen_demerits_code=15 {demerits for final hyphen break}
+@d adj_demerits_code=16 {demerits for adjacent incompatible lines}
+@d mag_code=17 {magnification ratio}
+@d delimiter_factor_code=18 {ratio for variable-size delimiters}
+@d looseness_code=19 {change in number of lines for a paragraph}
+@d time_code=20 {current time of day}
+@d day_code=21 {current day of the month}
+@d month_code=22 {current month of the year}
+@d year_code=23 {current year of our Lord}
+@d show_box_breadth_code=24 {nodes per level in |show_box|}
+@d show_box_depth_code=25 {maximum level in |show_box|}
+@d hbadness_code=26 {hboxes exceeding this badness will be shown by |hpack|}
+@d vbadness_code=27 {vboxes exceeding this badness will be shown by |vpack|}
+@d pausing_code=28 {pause after each line is read from a file}
+@d tracing_online_code=29 {show diagnostic output on terminal}
+@d tracing_macros_code=30 {show macros as they are being expanded}
+@d tracing_stats_code=31 {show memory usage if \TeX\ knows it}
+@d tracing_paragraphs_code=32 {show line-break calculations}
+@d tracing_pages_code=33 {show page-break calculations}
+@d tracing_output_code=34 {show boxes when they are shipped out}
+@d tracing_lost_chars_code=35 {show characters that aren't in the font}
+@d tracing_commands_code=36 {show command codes at |big_switch|}
+@d tracing_restores_code=37 {show equivalents when they are restored}
+@d uc_hyph_code=38 {hyphenate words beginning with a capital letter}
+@d output_penalty_code=39 {penalty found at current page break}
+@d max_dead_cycles_code=40 {bound on consecutive dead cycles of output}
+@d hang_after_code=41 {hanging indentation changes after this many lines}
+@d floating_penalty_code=42 {penalty for insertions heldover after a split}
+@d global_defs_code=43 {override \.{\\global} specifications}
+@d cur_fam_code=44 {current family}
+@d escape_char_code=45 {escape character for token output}
+@d default_hyphen_char_code=46 {value of \.{\\hyphenchar} when a font is loaded}
+@d default_skew_char_code=47 {value of \.{\\skewchar} when a font is loaded}
+@d end_line_char_code=48 {character placed at the right end of the buffer}
+@d new_line_char_code=49 {character that prints as |print_ln|}
+@d language_code=50 {current hyphenation table}
+@d left_hyphen_min_code=51 {minimum left hyphenation fragment size}
+@d right_hyphen_min_code=52 {minimum right hyphenation fragment size}
+@d holding_inserts_code=53 {do not remove insertion nodes from \.{\\box255}}
+@d error_context_lines_code=54 {maximum intermediate line pairs shown}
+@d int_pars=55 {total number of integer parameters}
+@d count_base=int_base+int_pars {256 user \.{\\count} registers}
+@d del_code_base=count_base+256 {256 delimiter code mappings}
+@d dimen_base=del_code_base+256 {beginning of region 6}
+@#
+@d del_code(#)==eqtb[del_code_base+#].int
+@d count(#)==eqtb[count_base+#].int
+@d int_par(#)==eqtb[int_base+#].int {an integer parameter}
+@d pretolerance==int_par(pretolerance_code)
+@d tolerance==int_par(tolerance_code)
+@d line_penalty==int_par(line_penalty_code)
+@d hyphen_penalty==int_par(hyphen_penalty_code)
+@d ex_hyphen_penalty==int_par(ex_hyphen_penalty_code)
+@d club_penalty==int_par(club_penalty_code)
+@d widow_penalty==int_par(widow_penalty_code)
+@d display_widow_penalty==int_par(display_widow_penalty_code)
+@d broken_penalty==int_par(broken_penalty_code)
+@d bin_op_penalty==int_par(bin_op_penalty_code)
+@d rel_penalty==int_par(rel_penalty_code)
+@d pre_display_penalty==int_par(pre_display_penalty_code)
+@d post_display_penalty==int_par(post_display_penalty_code)
+@d inter_line_penalty==int_par(inter_line_penalty_code)
+@d double_hyphen_demerits==int_par(double_hyphen_demerits_code)
+@d final_hyphen_demerits==int_par(final_hyphen_demerits_code)
+@d adj_demerits==int_par(adj_demerits_code)
+@d mag==int_par(mag_code)
+@d delimiter_factor==int_par(delimiter_factor_code)
+@d looseness==int_par(looseness_code)
+@d time==int_par(time_code)
+@d day==int_par(day_code)
+@d month==int_par(month_code)
+@d year==int_par(year_code)
+@d show_box_breadth==int_par(show_box_breadth_code)
+@d show_box_depth==int_par(show_box_depth_code)
+@d hbadness==int_par(hbadness_code)
+@d vbadness==int_par(vbadness_code)
+@d pausing==int_par(pausing_code)
+@d tracing_online==int_par(tracing_online_code)
+@d tracing_macros==int_par(tracing_macros_code)
+@d tracing_stats==int_par(tracing_stats_code)
+@d tracing_paragraphs==int_par(tracing_paragraphs_code)
+@d tracing_pages==int_par(tracing_pages_code)
+@d tracing_output==int_par(tracing_output_code)
+@d tracing_lost_chars==int_par(tracing_lost_chars_code)
+@d tracing_commands==int_par(tracing_commands_code)
+@d tracing_restores==int_par(tracing_restores_code)
+@d uc_hyph==int_par(uc_hyph_code)
+@d output_penalty==int_par(output_penalty_code)
+@d max_dead_cycles==int_par(max_dead_cycles_code)
+@d hang_after==int_par(hang_after_code)
+@d floating_penalty==int_par(floating_penalty_code)
+@d global_defs==int_par(global_defs_code)
+@d cur_fam==int_par(cur_fam_code)
+@d escape_char==int_par(escape_char_code)
+@d default_hyphen_char==int_par(default_hyphen_char_code)
+@d default_skew_char==int_par(default_skew_char_code)
+@d end_line_char==int_par(end_line_char_code)
+@d new_line_char==int_par(new_line_char_code)
+@d language==int_par(language_code)
+@d left_hyphen_min==int_par(left_hyphen_min_code)
+@d right_hyphen_min==int_par(right_hyphen_min_code)
+@d holding_inserts==int_par(holding_inserts_code)
+@d error_context_lines==int_par(error_context_lines_code)
+
+@<Assign the values |depth_threshold:=show_box_depth|...@>=
+depth_threshold:=show_box_depth;
+breadth_max:=show_box_breadth
+
+@ We can print the symbolic name of an integer parameter as follows.
+
+@p procedure print_param(@!n:integer);
+begin case n of
+pretolerance_code:print_esc("pretolerance");
+tolerance_code:print_esc("tolerance");
+line_penalty_code:print_esc("linepenalty");
+hyphen_penalty_code:print_esc("hyphenpenalty");
+ex_hyphen_penalty_code:print_esc("exhyphenpenalty");
+club_penalty_code:print_esc("clubpenalty");
+widow_penalty_code:print_esc("widowpenalty");
+display_widow_penalty_code:print_esc("displaywidowpenalty");
+broken_penalty_code:print_esc("brokenpenalty");
+bin_op_penalty_code:print_esc("binoppenalty");
+rel_penalty_code:print_esc("relpenalty");
+pre_display_penalty_code:print_esc("predisplaypenalty");
+post_display_penalty_code:print_esc("postdisplaypenalty");
+inter_line_penalty_code:print_esc("interlinepenalty");
+double_hyphen_demerits_code:print_esc("doublehyphendemerits");
+final_hyphen_demerits_code:print_esc("finalhyphendemerits");
+adj_demerits_code:print_esc("adjdemerits");
+mag_code:print_esc("mag");
+delimiter_factor_code:print_esc("delimiterfactor");
+looseness_code:print_esc("looseness");
+time_code:print_esc("time");
+day_code:print_esc("day");
+month_code:print_esc("month");
+year_code:print_esc("year");
+show_box_breadth_code:print_esc("showboxbreadth");
+show_box_depth_code:print_esc("showboxdepth");
+hbadness_code:print_esc("hbadness");
+vbadness_code:print_esc("vbadness");
+pausing_code:print_esc("pausing");
+tracing_online_code:print_esc("tracingonline");
+tracing_macros_code:print_esc("tracingmacros");
+tracing_stats_code:print_esc("tracingstats");
+tracing_paragraphs_code:print_esc("tracingparagraphs");
+tracing_pages_code:print_esc("tracingpages");
+tracing_output_code:print_esc("tracingoutput");
+tracing_lost_chars_code:print_esc("tracinglostchars");
+tracing_commands_code:print_esc("tracingcommands");
+tracing_restores_code:print_esc("tracingrestores");
+uc_hyph_code:print_esc("uchyph");
+output_penalty_code:print_esc("outputpenalty");
+max_dead_cycles_code:print_esc("maxdeadcycles");
+hang_after_code:print_esc("hangafter");
+floating_penalty_code:print_esc("floatingpenalty");
+global_defs_code:print_esc("globaldefs");
+cur_fam_code:print_esc("fam");
+escape_char_code:print_esc("escapechar");
+default_hyphen_char_code:print_esc("defaulthyphenchar");
+default_skew_char_code:print_esc("defaultskewchar");
+end_line_char_code:print_esc("endlinechar");
+new_line_char_code:print_esc("newlinechar");
+language_code:print_esc("language");
+left_hyphen_min_code:print_esc("lefthyphenmin");
+right_hyphen_min_code:print_esc("righthyphenmin");
+holding_inserts_code:print_esc("holdinginserts");
+error_context_lines_code:print_esc("errorcontextlines");
+othercases print("[unknown integer parameter!]")
+endcases;
+end;
+
+@ The integer parameter names must be entered into the hash table.
+
+@<Put each...@>=
+primitive("pretolerance",assign_int,int_base+pretolerance_code);@/
+@!@:pretolerance_}{\.{\\pretolerance} primitive@>
+primitive("tolerance",assign_int,int_base+tolerance_code);@/
+@!@:tolerance_}{\.{\\tolerance} primitive@>
+primitive("linepenalty",assign_int,int_base+line_penalty_code);@/
+@!@:line_penalty_}{\.{\\linepenalty} primitive@>
+primitive("hyphenpenalty",assign_int,int_base+hyphen_penalty_code);@/
+@!@:hyphen_penalty_}{\.{\\hyphenpenalty} primitive@>
+primitive("exhyphenpenalty",assign_int,int_base+ex_hyphen_penalty_code);@/
+@!@:ex_hyphen_penalty_}{\.{\\exhyphenpenalty} primitive@>
+primitive("clubpenalty",assign_int,int_base+club_penalty_code);@/
+@!@:club_penalty_}{\.{\\clubpenalty} primitive@>
+primitive("widowpenalty",assign_int,int_base+widow_penalty_code);@/
+@!@:widow_penalty_}{\.{\\widowpenalty} primitive@>
+primitive("displaywidowpenalty",
+ assign_int,int_base+display_widow_penalty_code);@/
+@!@:display_widow_penalty_}{\.{\\displaywidowpenalty} primitive@>
+primitive("brokenpenalty",assign_int,int_base+broken_penalty_code);@/
+@!@:broken_penalty_}{\.{\\brokenpenalty} primitive@>
+primitive("binoppenalty",assign_int,int_base+bin_op_penalty_code);@/
+@!@:bin_op_penalty_}{\.{\\binoppenalty} primitive@>
+primitive("relpenalty",assign_int,int_base+rel_penalty_code);@/
+@!@:rel_penalty_}{\.{\\relpenalty} primitive@>
+primitive("predisplaypenalty",assign_int,int_base+pre_display_penalty_code);@/
+@!@:pre_display_penalty_}{\.{\\predisplaypenalty} primitive@>
+primitive("postdisplaypenalty",assign_int,int_base+post_display_penalty_code);@/
+@!@:post_display_penalty_}{\.{\\postdisplaypenalty} primitive@>
+primitive("interlinepenalty",assign_int,int_base+inter_line_penalty_code);@/
+@!@:inter_line_penalty_}{\.{\\interlinepenalty} primitive@>
+primitive("doublehyphendemerits",
+ assign_int,int_base+double_hyphen_demerits_code);@/
+@!@:double_hyphen_demerits_}{\.{\\doublehyphendemerits} primitive@>
+primitive("finalhyphendemerits",
+ assign_int,int_base+final_hyphen_demerits_code);@/
+@!@:final_hyphen_demerits_}{\.{\\finalhyphendemerits} primitive@>
+primitive("adjdemerits",assign_int,int_base+adj_demerits_code);@/
+@!@:adj_demerits_}{\.{\\adjdemerits} primitive@>
+primitive("mag",assign_int,int_base+mag_code);@/
+@!@:mag_}{\.{\\mag} primitive@>
+primitive("delimiterfactor",assign_int,int_base+delimiter_factor_code);@/
+@!@:delimiter_factor_}{\.{\\delimiterfactor} primitive@>
+primitive("looseness",assign_int,int_base+looseness_code);@/
+@!@:looseness_}{\.{\\looseness} primitive@>
+primitive("time",assign_int,int_base+time_code);@/
+@!@:time_}{\.{\\time} primitive@>
+primitive("day",assign_int,int_base+day_code);@/
+@!@:day_}{\.{\\day} primitive@>
+primitive("month",assign_int,int_base+month_code);@/
+@!@:month_}{\.{\\month} primitive@>
+primitive("year",assign_int,int_base+year_code);@/
+@!@:year_}{\.{\\year} primitive@>
+primitive("showboxbreadth",assign_int,int_base+show_box_breadth_code);@/
+@!@:show_box_breadth_}{\.{\\showboxbreadth} primitive@>
+primitive("showboxdepth",assign_int,int_base+show_box_depth_code);@/
+@!@:show_box_depth_}{\.{\\showboxdepth} primitive@>
+primitive("hbadness",assign_int,int_base+hbadness_code);@/
+@!@:hbadness_}{\.{\\hbadness} primitive@>
+primitive("vbadness",assign_int,int_base+vbadness_code);@/
+@!@:vbadness_}{\.{\\vbadness} primitive@>
+primitive("pausing",assign_int,int_base+pausing_code);@/
+@!@:pausing_}{\.{\\pausing} primitive@>
+primitive("tracingonline",assign_int,int_base+tracing_online_code);@/
+@!@:tracing_online_}{\.{\\tracingonline} primitive@>
+primitive("tracingmacros",assign_int,int_base+tracing_macros_code);@/
+@!@:tracing_macros_}{\.{\\tracingmacros} primitive@>
+primitive("tracingstats",assign_int,int_base+tracing_stats_code);@/
+@!@:tracing_stats_}{\.{\\tracingstats} primitive@>
+primitive("tracingparagraphs",assign_int,int_base+tracing_paragraphs_code);@/
+@!@:tracing_paragraphs_}{\.{\\tracingparagraphs} primitive@>
+primitive("tracingpages",assign_int,int_base+tracing_pages_code);@/
+@!@:tracing_pages_}{\.{\\tracingpages} primitive@>
+primitive("tracingoutput",assign_int,int_base+tracing_output_code);@/
+@!@:tracing_output_}{\.{\\tracingoutput} primitive@>
+primitive("tracinglostchars",assign_int,int_base+tracing_lost_chars_code);@/
+@!@:tracing_lost_chars_}{\.{\\tracinglostchars} primitive@>
+primitive("tracingcommands",assign_int,int_base+tracing_commands_code);@/
+@!@:tracing_commands_}{\.{\\tracingcommands} primitive@>
+primitive("tracingrestores",assign_int,int_base+tracing_restores_code);@/
+@!@:tracing_restores_}{\.{\\tracingrestores} primitive@>
+primitive("uchyph",assign_int,int_base+uc_hyph_code);@/
+@!@:uc_hyph_}{\.{\\uchyph} primitive@>
+primitive("outputpenalty",assign_int,int_base+output_penalty_code);@/
+@!@:output_penalty_}{\.{\\outputpenalty} primitive@>
+primitive("maxdeadcycles",assign_int,int_base+max_dead_cycles_code);@/
+@!@:max_dead_cycles_}{\.{\\maxdeadcycles} primitive@>
+primitive("hangafter",assign_int,int_base+hang_after_code);@/
+@!@:hang_after_}{\.{\\hangafter} primitive@>
+primitive("floatingpenalty",assign_int,int_base+floating_penalty_code);@/
+@!@:floating_penalty_}{\.{\\floatingpenalty} primitive@>
+primitive("globaldefs",assign_int,int_base+global_defs_code);@/
+@!@:global_defs_}{\.{\\globaldefs} primitive@>
+primitive("fam",assign_int,int_base+cur_fam_code);@/
+@!@:fam_}{\.{\\fam} primitive@>
+primitive("escapechar",assign_int,int_base+escape_char_code);@/
+@!@:escape_char_}{\.{\\escapechar} primitive@>
+primitive("defaulthyphenchar",assign_int,int_base+default_hyphen_char_code);@/
+@!@:default_hyphen_char_}{\.{\\defaulthyphenchar} primitive@>
+primitive("defaultskewchar",assign_int,int_base+default_skew_char_code);@/
+@!@:default_skew_char_}{\.{\\defaultskewchar} primitive@>
+primitive("endlinechar",assign_int,int_base+end_line_char_code);@/
+@!@:end_line_char_}{\.{\\endlinechar} primitive@>
+primitive("newlinechar",assign_int,int_base+new_line_char_code);@/
+@!@:new_line_char_}{\.{\\newlinechar} primitive@>
+primitive("language",assign_int,int_base+language_code);@/
+@!@:language_}{\.{\\language} primitive@>
+primitive("lefthyphenmin",assign_int,int_base+left_hyphen_min_code);@/
+@!@:left_hyphen_min_}{\.{\\lefthyphenmin} primitive@>
+primitive("righthyphenmin",assign_int,int_base+right_hyphen_min_code);@/
+@!@:right_hyphen_min_}{\.{\\righthyphenmin} primitive@>
+primitive("holdinginserts",assign_int,int_base+holding_inserts_code);@/
+@!@:holding_inserts_}{\.{\\holdinginserts} primitive@>
+primitive("errorcontextlines",assign_int,int_base+error_context_lines_code);@/
+@!@:error_context_lines_}{\.{\\errorcontextlines} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+assign_int: if chr_code<count_base then print_param(chr_code-int_base)
+ else begin print_esc("count"); print_int(chr_code-count_base);
+ end;
+
+@ The integer parameters should really be initialized by a macro package;
+the following initialization does the minimum to keep \TeX\ from
+complete failure.
+@^null delimiter@>
+
+@<Initialize table entries...@>=
+for k:=int_base to del_code_base-1 do eqtb[k].int:=0;
+mag:=1000; tolerance:=10000; hang_after:=1; max_dead_cycles:=25;
+escape_char:="\"; end_line_char:=carriage_return;
+for k:=0 to 255 do del_code(k):=-1;
+del_code("."):=0; {this null delimiter is used in error recovery}
+
+@ The following procedure, which is called just before \TeX\ initializes its
+input and output, establishes the initial values of the date and time.
+@^system dependencies@>
+Since standard \PASCAL\ cannot provide such information, something special
+is needed. The program here simply specifies July 4, 1776, at noon; but
+users probably want a better approximation to the truth.
+
+@p procedure fix_date_and_time;
+begin time:=12*60; {minutes since midnight}
+day:=4; {fourth day of the month}
+month:=7; {seventh month of the year}
+year:=1776; {Anno Domini}
+end;
+
+@ @<Show equivalent |n|, in region 5@>=
+begin if n<count_base then print_param(n-int_base)
+else if n<del_code_base then
+ begin print_esc("count"); print_int(n-count_base);
+ end
+else begin print_esc("delcode"); print_int(n-del_code_base);
+ end;
+print_char("="); print_int(eqtb[n].int);
+end
+
+@ @<Set variable |c| to the current escape character@>=c:=escape_char
+
+@ @<Character |s| is the current new-line character@>=s=new_line_char
+
+@ \TeX\ is occasionally supposed to print diagnostic information that
+goes only into the transcript file, unless |tracing_online| is positive.
+Here are two routines that adjust the destination of print commands:
+
+@p procedure begin_diagnostic; {prepare to do some tracing}
+begin old_setting:=selector;
+if (tracing_online<=0)and(selector=term_and_log) then
+ begin decr(selector);
+ if history=spotless then history:=warning_issued;
+ end;
+end;
+@#
+procedure end_diagnostic(@!blank_line:boolean);
+ {restore proper conditions after tracing}
+begin print_nl("");
+if blank_line then print_ln;
+selector:=old_setting;
+end;
+
+@ Of course we had better declare another global variable, if the previous
+routines are going to work.
+
+@<Glob...@>=
+@!old_setting:0..max_selector;
+
+@ The final region of |eqtb| contains the dimension parameters defined
+here, and the 256 \.{\\dimen} registers.
+
+@d par_indent_code=0 {indentation of paragraphs}
+@d math_surround_code=1 {space around math in text}
+@d line_skip_limit_code=2 {threshold for |line_skip| instead of |baseline_skip|}
+@d hsize_code=3 {line width in horizontal mode}
+@d vsize_code=4 {page height in vertical mode}
+@d max_depth_code=5 {maximum depth of boxes on main pages}
+@d split_max_depth_code=6 {maximum depth of boxes on split pages}
+@d box_max_depth_code=7 {maximum depth of explicit vboxes}
+@d hfuzz_code=8 {tolerance for overfull hbox messages}
+@d vfuzz_code=9 {tolerance for overfull vbox messages}
+@d delimiter_shortfall_code=10 {maximum amount uncovered by variable delimiters}
+@d null_delimiter_space_code=11 {blank space in null delimiters}
+@d script_space_code=12 {extra space after subscript or superscript}
+@d pre_display_size_code=13 {length of text preceding a display}
+@d display_width_code=14 {length of line for displayed equation}
+@d display_indent_code=15 {indentation of line for displayed equation}
+@d overfull_rule_code=16 {width of rule that identifies overfull hboxes}
+@d hang_indent_code=17 {amount of hanging indentation}
+@d h_offset_code=18 {amount of horizontal offset when shipping pages out}
+@d v_offset_code=19 {amount of vertical offset when shipping pages out}
+@d emergency_stretch_code=20 {reduces badnesses on final pass of line-breaking}
+@d dimen_pars=21 {total number of dimension parameters}
+@d scaled_base=dimen_base+dimen_pars
+ {table of 256 user-defined \.{\\dimen} registers}
+@d eqtb_size=scaled_base+255 {largest subscript of |eqtb|}
+@#
+@d dimen(#)==eqtb[scaled_base+#].sc
+@d dimen_par(#)==eqtb[dimen_base+#].sc {a scaled quantity}
+@d par_indent==dimen_par(par_indent_code)
+@d math_surround==dimen_par(math_surround_code)
+@d line_skip_limit==dimen_par(line_skip_limit_code)
+@d hsize==dimen_par(hsize_code)
+@d vsize==dimen_par(vsize_code)
+@d max_depth==dimen_par(max_depth_code)
+@d split_max_depth==dimen_par(split_max_depth_code)
+@d box_max_depth==dimen_par(box_max_depth_code)
+@d hfuzz==dimen_par(hfuzz_code)
+@d vfuzz==dimen_par(vfuzz_code)
+@d delimiter_shortfall==dimen_par(delimiter_shortfall_code)
+@d null_delimiter_space==dimen_par(null_delimiter_space_code)
+@d script_space==dimen_par(script_space_code)
+@d pre_display_size==dimen_par(pre_display_size_code)
+@d display_width==dimen_par(display_width_code)
+@d display_indent==dimen_par(display_indent_code)
+@d overfull_rule==dimen_par(overfull_rule_code)
+@d hang_indent==dimen_par(hang_indent_code)
+@d h_offset==dimen_par(h_offset_code)
+@d v_offset==dimen_par(v_offset_code)
+@d emergency_stretch==dimen_par(emergency_stretch_code)
+
+@p procedure print_length_param(@!n:integer);
+begin case n of
+par_indent_code:print_esc("parindent");
+math_surround_code:print_esc("mathsurround");
+line_skip_limit_code:print_esc("lineskiplimit");
+hsize_code:print_esc("hsize");
+vsize_code:print_esc("vsize");
+max_depth_code:print_esc("maxdepth");
+split_max_depth_code:print_esc("splitmaxdepth");
+box_max_depth_code:print_esc("boxmaxdepth");
+hfuzz_code:print_esc("hfuzz");
+vfuzz_code:print_esc("vfuzz");
+delimiter_shortfall_code:print_esc("delimitershortfall");
+null_delimiter_space_code:print_esc("nulldelimiterspace");
+script_space_code:print_esc("scriptspace");
+pre_display_size_code:print_esc("predisplaysize");
+display_width_code:print_esc("displaywidth");
+display_indent_code:print_esc("displayindent");
+overfull_rule_code:print_esc("overfullrule");
+hang_indent_code:print_esc("hangindent");
+h_offset_code:print_esc("hoffset");
+v_offset_code:print_esc("voffset");
+emergency_stretch_code:print_esc("emergencystretch");
+othercases print("[unknown dimen parameter!]")
+endcases;
+end;
+
+@ @<Put each...@>=
+primitive("parindent",assign_dimen,dimen_base+par_indent_code);@/
+@!@:par_indent_}{\.{\\parindent} primitive@>
+primitive("mathsurround",assign_dimen,dimen_base+math_surround_code);@/
+@!@:math_surround_}{\.{\\mathsurround} primitive@>
+primitive("lineskiplimit",assign_dimen,dimen_base+line_skip_limit_code);@/
+@!@:line_skip_limit_}{\.{\\lineskiplimit} primitive@>
+primitive("hsize",assign_dimen,dimen_base+hsize_code);@/
+@!@:hsize_}{\.{\\hsize} primitive@>
+primitive("vsize",assign_dimen,dimen_base+vsize_code);@/
+@!@:vsize_}{\.{\\vsize} primitive@>
+primitive("maxdepth",assign_dimen,dimen_base+max_depth_code);@/
+@!@:max_depth_}{\.{\\maxdepth} primitive@>
+primitive("splitmaxdepth",assign_dimen,dimen_base+split_max_depth_code);@/
+@!@:split_max_depth_}{\.{\\splitmaxdepth} primitive@>
+primitive("boxmaxdepth",assign_dimen,dimen_base+box_max_depth_code);@/
+@!@:box_max_depth_}{\.{\\boxmaxdepth} primitive@>
+primitive("hfuzz",assign_dimen,dimen_base+hfuzz_code);@/
+@!@:hfuzz_}{\.{\\hfuzz} primitive@>
+primitive("vfuzz",assign_dimen,dimen_base+vfuzz_code);@/
+@!@:vfuzz_}{\.{\\vfuzz} primitive@>
+primitive("delimitershortfall",
+ assign_dimen,dimen_base+delimiter_shortfall_code);@/
+@!@:delimiter_shortfall_}{\.{\\delimitershortfall} primitive@>
+primitive("nulldelimiterspace",
+ assign_dimen,dimen_base+null_delimiter_space_code);@/
+@!@:null_delimiter_space_}{\.{\\nulldelimiterspace} primitive@>
+primitive("scriptspace",assign_dimen,dimen_base+script_space_code);@/
+@!@:script_space_}{\.{\\scriptspace} primitive@>
+primitive("predisplaysize",assign_dimen,dimen_base+pre_display_size_code);@/
+@!@:pre_display_size_}{\.{\\predisplaysize} primitive@>
+primitive("displaywidth",assign_dimen,dimen_base+display_width_code);@/
+@!@:display_width_}{\.{\\displaywidth} primitive@>
+primitive("displayindent",assign_dimen,dimen_base+display_indent_code);@/
+@!@:display_indent_}{\.{\\displayindent} primitive@>
+primitive("overfullrule",assign_dimen,dimen_base+overfull_rule_code);@/
+@!@:overfull_rule_}{\.{\\overfullrule} primitive@>
+primitive("hangindent",assign_dimen,dimen_base+hang_indent_code);@/
+@!@:hang_indent_}{\.{\\hangindent} primitive@>
+primitive("hoffset",assign_dimen,dimen_base+h_offset_code);@/
+@!@:h_offset_}{\.{\\hoffset} primitive@>
+primitive("voffset",assign_dimen,dimen_base+v_offset_code);@/
+@!@:v_offset_}{\.{\\voffset} primitive@>
+primitive("emergencystretch",assign_dimen,dimen_base+emergency_stretch_code);@/
+@!@:emergency_stretch_}{\.{\\emergencystretch} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+assign_dimen: if chr_code<scaled_base then
+ print_length_param(chr_code-dimen_base)
+ else begin print_esc("dimen"); print_int(chr_code-scaled_base);
+ end;
+
+@ @<Initialize table entries...@>=
+for k:=dimen_base to eqtb_size do eqtb[k].sc:=0;
+
+@ @<Show equivalent |n|, in region 6@>=
+begin if n<scaled_base then print_length_param(n-dimen_base)
+else begin print_esc("dimen"); print_int(n-scaled_base);
+ end;
+print_char("="); print_scaled(eqtb[n].sc); print("pt");
+end
+
+@ Here is a procedure that displays the contents of |eqtb[n]|
+symbolically.
+
+@p@t\4@>@<Declare the procedure called |print_cmd_chr|@>@;@/
+@!stat procedure show_eqtb(@!n:pointer);
+begin if n<active_base then print_char("?") {this can't happen}
+else if n<glue_base then @<Show equivalent |n|, in region 1 or 2@>
+else if n<local_base then @<Show equivalent |n|, in region 3@>
+else if n<int_base then @<Show equivalent |n|, in region 4@>
+else if n<dimen_base then @<Show equivalent |n|, in region 5@>
+else if n<=eqtb_size then @<Show equivalent |n|, in region 6@>
+else print_char("?"); {this can't happen either}
+end;
+tats
+
+@ The last two regions of |eqtb| have fullword values instead of the
+three fields |eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary,
+but \TeX\ needs to store the |eq_level| information in another array
+called |xeq_level|.
+
+@<Glob...@>=
+@!eqtb:array[active_base..eqtb_size] of memory_word;
+@!xeq_level:array[int_base..eqtb_size] of quarterword;
+
+@ @<Set init...@>=
+for k:=int_base to eqtb_size do xeq_level[k]:=level_one;
+
+@ When the debugging routine |search_mem| is looking for pointers having a
+given value, it is interested only in regions 1 to~3 of~|eqtb|, and in the
+first part of region~4.
+
+@<Search |eqtb| for equivalents equal to |p|@>=
+for q:=active_base to box_base+255 do
+ begin if equiv(q)=p then
+ begin print_nl("EQUIV("); print_int(q); print_char(")");
+ end;
+ end
+
+@* \[18] The hash table.
+Control sequences are stored and retrieved by means of a fairly standard hash
+table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C
+in {\sl The Art of Computer Programming\/}). Once a control sequence enters the
+table, it is never removed, because there are complicated situations
+involving \.{\\gdef} where the removal of a control sequence at the end of
+a group would be a mistake preventable only by the introduction of a
+complicated reference-count mechanism.
+
+The actual sequence of letters forming a control sequence identifier is
+stored in the |str_pool| array together with all the other strings. An
+auxiliary array |hash| consists of items with two halfword fields per
+word. The first of these, called |next(p)|, points to the next identifier
+belonging to the same coalesced list as the identifier corresponding to~|p|;
+and the other, called |text(p)|, points to the |str_start| entry for
+|p|'s identifier. If position~|p| of the hash table is empty, we have
+|text(p)=0|; if position |p| is either empty or the end of a coalesced
+hash list, we have |next(p)=0|. An auxiliary pointer variable called
+|hash_used| is maintained in such a way that all locations |p>=hash_used|
+are nonempty. The global variable |cs_count| tells how many multiletter
+control sequences have been defined, if statistics are being kept.
+
+A global boolean variable called |no_new_control_sequence| is set to
+|true| during the time that new hash table entries are forbidden.
+
+@d next(#) == hash[#].lh {link for coalesced lists}
+@d text(#) == hash[#].rh {string number for control sequence name}
+@d hash_is_full == (hash_used=hash_base) {test if all positions are occupied}
+@d font_id_text(#) == text(font_id_base+#) {a frozen font identifier's name}
+
+@<Glob...@>=
+@!hash: array[hash_base..undefined_control_sequence-1] of two_halves;
+ {the hash table}
+@!hash_used:pointer; {allocation pointer for |hash|}
+@!no_new_control_sequence:boolean; {are new identifiers legal?}
+@!cs_count:integer; {total number of known identifiers}
+
+@ @<Set init...@>=
+no_new_control_sequence:=true; {new identifiers are usually forbidden}
+next(hash_base):=0; text(hash_base):=0;
+for k:=hash_base+1 to undefined_control_sequence-1 do hash[k]:=hash[hash_base];
+
+@ @<Initialize table entries...@>=
+hash_used:=frozen_control_sequence; {nothing is used}
+cs_count:=0;
+eq_type(frozen_dont_expand):=dont_expand;
+text(frozen_dont_expand):="notexpanded:";
+@.notexpanded:@>
+
+@ Here is the subroutine that searches the hash table for an identifier
+that matches a given string of length |l>1| appearing in |buffer[j..
+(j+l-1)]|. If the identifier is found, the corresponding hash table address
+is returned. Otherwise, if the global variable |no_new_control_sequence|
+is |true|, the dummy address |undefined_control_sequence| is returned.
+Otherwise the identifier is inserted into the hash table and its location
+is returned.
+
+@p function id_lookup(@!j,@!l:integer):pointer; {search the hash table}
+label found; {go here if you found it}
+var h:integer; {hash code}
+@!d:integer; {number of characters in incomplete current string}
+@!p:pointer; {index in |hash| array}
+@!k:pointer; {index in |buffer| array}
+begin @<Compute the hash code |h|@>;
+p:=h+hash_base; {we start searching here; note that |0<=h<hash_prime|}
+loop@+begin if text(p)>0 then if length(text(p))=l then
+ if str_eq_buf(text(p),j) then goto found;
+ if next(p)=0 then
+ begin if no_new_control_sequence then
+ p:=undefined_control_sequence
+ else @<Insert a new control sequence after |p|, then make
+ |p| point to it@>;
+ goto found;
+ end;
+ p:=next(p);
+ end;
+found: id_lookup:=p;
+end;
+
+@ @<Insert a new control...@>=
+begin if text(p)>0 then
+ begin repeat if hash_is_full then overflow("hash size",hash_size);
+@:TeX capacity exceeded hash size}{\quad hash size@>
+ decr(hash_used);
+ until text(hash_used)=0; {search for an empty location in |hash|}
+ next(p):=hash_used; p:=hash_used;
+ end;
+str_room(l); d:=cur_length;
+while pool_ptr>str_start[str_ptr] do
+ begin decr(pool_ptr); str_pool[pool_ptr+l]:=str_pool[pool_ptr];
+ end; {move current string up to make room for another}
+for k:=j to j+l-1 do append_char(buffer[k]);
+text(p):=make_string; pool_ptr:=pool_ptr+d;
+@!stat incr(cs_count);@+tats@;@/
+end
+
+@ The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it
+should be a prime number. The theory of hashing tells us to expect fewer
+than two table probes, on the average, when the search is successful.
+[See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.]
+@^Vitter, Jeffrey Scott@>
+
+@<Compute the hash code |h|@>=
+h:=buffer[j];
+for k:=j+1 to j+l-1 do
+ begin h:=h+h+buffer[k];
+ while h>=hash_prime do h:=h-hash_prime;
+ end
+
+@ Single-character control sequences do not need to be looked up in a hash
+table, since we can use the character code itself as a direct address.
+The procedure |print_cs| prints the name of a control sequence, given
+a pointer to its address in |eqtb|. A space is printed after the name
+unless it is a single nonletter or an active character. This procedure
+might be invoked with invalid data, so it is ``extra robust.'' The
+individual characters must be printed one at a time using |print|, since
+they may be unprintable.
+
+@<Basic printing...@>=
+procedure print_cs(@!p:integer); {prints a purported control sequence}
+begin if p<hash_base then {single character}
+ if p>=single_base then
+ if p=null_cs then
+ begin print_esc("csname"); print_esc("endcsname"); print_char(" ");
+ end
+ else begin print_esc(p-single_base);
+ if cat_code(p-single_base)=letter then print_char(" ");
+ end
+ else if p<active_base then print_esc("IMPOSSIBLE.")
+@.IMPOSSIBLE@>
+ else print(p-active_base)
+else if p>=undefined_control_sequence then print_esc("IMPOSSIBLE.")
+else if (text(p)<0)or(text(p)>=str_ptr) then print_esc("NONEXISTENT.")
+@.NONEXISTENT@>
+else begin print_esc(text(p));
+ print_char(" ");
+ end;
+end;
+
+@ Here is a similar procedure; it avoids the error checks, and it never
+prints a space after the control sequence.
+
+@<Basic printing procedures@>=
+procedure sprint_cs(@!p:pointer); {prints a control sequence}
+begin if p<hash_base then
+ if p<single_base then print(p-active_base)
+ else if p<null_cs then print_esc(p-single_base)
+ else begin print_esc("csname"); print_esc("endcsname");
+ end
+else print_esc(text(p));
+end;
+
+@ We need to put \TeX's ``primitive'' control sequences into the hash
+table, together with their command code (which will be the |eq_type|)
+and an operand (which will be the |equiv|). The |primitive| procedure
+does this, in a way that no \TeX\ user can. The global value |cur_val|
+contains the new |eqtb| pointer after |primitive| has acted.
+
+@p @!init procedure primitive(@!s:str_number;@!c:quarterword;@!o:halfword);
+var k:pool_pointer; {index into |str_pool|}
+@!j:small_number; {index into |buffer|}
+@!l:small_number; {length of the string}
+begin if s<256 then cur_val:=s+single_base
+else begin k:=str_start[s]; l:=str_start[s+1]-k;
+ {we will move |s| into the (empty) |buffer|}
+ for j:=0 to l-1 do buffer[j]:=so(str_pool[k+j]);
+ cur_val:=id_lookup(0,l); {|no_new_control_sequence| is |false|}
+ flush_string; text(cur_val):=s; {we don't want to have the string twice}
+ end;
+eq_level(cur_val):=level_one; eq_type(cur_val):=c; equiv(cur_val):=o;
+end;
+tini
+
+@ Many of \TeX's primitives need no |equiv|, since they are identifiable
+by their |eq_type| alone. These primitives are loaded into the hash table
+as follows:
+
+@<Put each of \TeX's primitives into the hash table@>=
+primitive(" ",ex_space,0);@/
+@!@:Single-character primitives /}{\quad\.{\\\ }@>
+primitive("/",ital_corr,0);@/
+@!@:Single-character primitives /}{\quad\.{\\/}@>
+primitive("accent",accent,0);@/
+@!@:accent_}{\.{\\accent} primitive@>
+primitive("advance",advance,0);@/
+@!@:advance_}{\.{\\advance} primitive@>
+primitive("afterassignment",after_assignment,0);@/
+@!@:after_assignment_}{\.{\\afterassignment} primitive@>
+primitive("aftergroup",after_group,0);@/
+@!@:after_group_}{\.{\\aftergroup} primitive@>
+primitive("begingroup",begin_group,0);@/
+@!@:begin_group_}{\.{\\begingroup} primitive@>
+primitive("char",char_num,0);@/
+@!@:char_}{\.{\\char} primitive@>
+primitive("csname",cs_name,0);@/
+@!@:cs_name_}{\.{\\csname} primitive@>
+primitive("delimiter",delim_num,0);@/
+@!@:delimiter_}{\.{\\delimiter} primitive@>
+primitive("divide",divide,0);@/
+@!@:divide_}{\.{\\divide} primitive@>
+primitive("endcsname",end_cs_name,0);@/
+@!@:end_cs_name_}{\.{\\endcsname} primitive@>
+primitive("endgroup",end_group,0);
+@!@:end_group_}{\.{\\endgroup} primitive@>
+text(frozen_end_group):="endgroup"; eqtb[frozen_end_group]:=eqtb[cur_val];@/
+primitive("expandafter",expand_after,0);@/
+@!@:expand_after_}{\.{\\expandafter} primitive@>
+primitive("font",def_font,0);@/
+@!@:font_}{\.{\\font} primitive@>
+primitive("fontdimen",assign_font_dimen,0);@/
+@!@:font_dimen_}{\.{\\fontdimen} primitive@>
+primitive("halign",halign,0);@/
+@!@:halign_}{\.{\\halign} primitive@>
+primitive("hrule",hrule,0);@/
+@!@:hrule_}{\.{\\hrule} primitive@>
+primitive("ignorespaces",ignore_spaces,0);@/
+@!@:ignore_spaces_}{\.{\\ignorespaces} primitive@>
+primitive("insert",insert,0);@/
+@!@:insert_}{\.{\\insert} primitive@>
+primitive("mark",mark,0);@/
+@!@:mark_}{\.{\\mark} primitive@>
+primitive("mathaccent",math_accent,0);@/
+@!@:math_accent_}{\.{\\mathaccent} primitive@>
+primitive("mathchar",math_char_num,0);@/
+@!@:math_char_}{\.{\\mathchar} primitive@>
+primitive("mathchoice",math_choice,0);@/
+@!@:math_choice_}{\.{\\mathchoice} primitive@>
+primitive("multiply",multiply,0);@/
+@!@:multiply_}{\.{\\multiply} primitive@>
+primitive("noalign",no_align,0);@/
+@!@:no_align_}{\.{\\noalign} primitive@>
+primitive("noboundary",no_boundary,0);@/
+@!@:no_boundary_}{\.{\\noboundary} primitive@>
+primitive("noexpand",no_expand,0);@/
+@!@:no_expand_}{\.{\\noexpand} primitive@>
+primitive("nonscript",non_script,0);@/
+@!@:non_script_}{\.{\\nonscript} primitive@>
+primitive("omit",omit,0);@/
+@!@:omit_}{\.{\\omit} primitive@>
+primitive("parshape",set_shape,0);@/
+@!@:par_shape_}{\.{\\parshape} primitive@>
+primitive("penalty",break_penalty,0);@/
+@!@:penalty_}{\.{\\penalty} primitive@>
+primitive("prevgraf",set_prev_graf,0);@/
+@!@:prev_graf_}{\.{\\prevgraf} primitive@>
+primitive("radical",radical,0);@/
+@!@:radical_}{\.{\\radical} primitive@>
+primitive("read",read_to_cs,0);@/
+@!@:read_}{\.{\\read} primitive@>
+primitive("relax",relax,256); {cf.\ |scan_file_name|}
+@!@:relax_}{\.{\\relax} primitive@>
+text(frozen_relax):="relax"; eqtb[frozen_relax]:=eqtb[cur_val];@/
+primitive("setbox",set_box,0);@/
+@!@:set_box_}{\.{\\setbox} primitive@>
+primitive("the",the,0);@/
+@!@:the_}{\.{\\the} primitive@>
+primitive("toks",toks_register,0);@/
+@!@:toks_}{\.{\\toks} primitive@>
+primitive("vadjust",vadjust,0);@/
+@!@:vadjust_}{\.{\\vadjust} primitive@>
+primitive("valign",valign,0);@/
+@!@:valign_}{\.{\\valign} primitive@>
+primitive("vcenter",vcenter,0);@/
+@!@:vcenter_}{\.{\\vcenter} primitive@>
+primitive("vrule",vrule,0);@/
+@!@:vrule_}{\.{\\vrule} primitive@>
+
+@ Each primitive has a corresponding inverse, so that it is possible to
+display the cryptic numeric contents of |eqtb| in symbolic form.
+Every call of |primitive| in this program is therefore accompanied by some
+straightforward code that forms part of the |print_cmd_chr| routine
+below.
+
+@<Cases of |print_cmd_chr|...@>=
+accent: print_esc("accent");
+advance: print_esc("advance");
+after_assignment: print_esc("afterassignment");
+after_group: print_esc("aftergroup");
+assign_font_dimen: print_esc("fontdimen");
+begin_group: print_esc("begingroup");
+break_penalty: print_esc("penalty");
+char_num: print_esc("char");
+cs_name: print_esc("csname");
+def_font: print_esc("font");
+delim_num: print_esc("delimiter");
+divide: print_esc("divide");
+end_cs_name: print_esc("endcsname");
+end_group: print_esc("endgroup");
+ex_space: print_esc(" ");
+expand_after: print_esc("expandafter");
+halign: print_esc("halign");
+hrule: print_esc("hrule");
+ignore_spaces: print_esc("ignorespaces");
+insert: print_esc("insert");
+ital_corr: print_esc("/");
+mark: print_esc("mark");
+math_accent: print_esc("mathaccent");
+math_char_num: print_esc("mathchar");
+math_choice: print_esc("mathchoice");
+multiply: print_esc("multiply");
+no_align: print_esc("noalign");
+no_boundary:print_esc("noboundary");
+no_expand: print_esc("noexpand");
+non_script: print_esc("nonscript");
+omit: print_esc("omit");
+radical: print_esc("radical");
+read_to_cs: print_esc("read");
+relax: print_esc("relax");
+set_box: print_esc("setbox");
+set_prev_graf: print_esc("prevgraf");
+set_shape: print_esc("parshape");
+the: print_esc("the");
+toks_register: print_esc("toks");
+vadjust: print_esc("vadjust");
+valign: print_esc("valign");
+vcenter: print_esc("vcenter");
+vrule: print_esc("vrule");
+
+@ We will deal with the other primitives later, at some point in the program
+where their |eq_type| and |equiv| values are more meaningful. For example,
+the primitives for math mode will be loaded when we consider the routines
+that deal with formulas. It is easy to find where each particular
+primitive was treated by looking in the index at the end; for example, the
+section where |"radical"| entered |eqtb| is listed under `\.{\\radical}
+primitive'. (Primitives consisting of a single nonalphabetic character,
+@!like `\.{\\/}', are listed under `Single-character primitives'.)
+@!@^Single-character primitives@>
+
+Meanwhile, this is a convenient place to catch up on something we were unable
+to do before the hash table was defined:
+
+@<Print the font identifier for |font(p)|@>=
+print_esc(font_id_text(font(p)))
+
+@* \[19] Saving and restoring equivalents.
+The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups
+in \TeX\ means that |eqtb| entries valid in outer groups should be saved
+and restored later if they are overridden inside the braces. When a new |eqtb|
+value is being assigned, the program therefore checks to see if the previous
+entry belongs to an outer level. In such a case, the old value is placed
+on the |save_stack| just before the new value enters |eqtb|. At the
+end of a grouping level, i.e., when the right brace is sensed, the
+|save_stack| is used to restore the outer values, and the inner ones are
+destroyed.
+
+Entries on the |save_stack| are of type |memory_word|. The top item on
+this stack is |save_stack[p]|, where |p=save_ptr-1|; it contains three
+fields called |save_type|, |save_level|, and |save_index|, and it is
+interpreted in one of four ways:
+
+\yskip\hangg 1) If |save_type(p)=restore_old_value|, then
+|save_index(p)| is a location in |eqtb| whose current value should
+be destroyed at the end of the current group and replaced by |save_stack[p-1]|.
+Furthermore if |save_index(p)>=int_base|, then |save_level(p)|
+should replace the corresponding entry in |xeq_level|.
+
+\yskip\hangg 2) If |save_type(p)=restore_zero|, then |save_index(p)|
+is a location in |eqtb| whose current value should be destroyed at the end
+of the current group, when it should be
+replaced by the current value of |eqtb[undefined_control_sequence]|.
+
+\yskip\hangg 3) If |save_type(p)=insert_token|, then |save_index(p)|
+is a token that should be inserted into \TeX's input when the current
+group ends.
+
+\yskip\hangg 4) If |save_type(p)=level_boundary|, then |save_level(p)|
+is a code explaining what kind of group we were previously in, and
+|save_index(p)| points to the level boundary word at the bottom of
+the entries for that group.
+
+@d save_type(#)==save_stack[#].hh.b0 {classifies a |save_stack| entry}
+@d save_level(#)==save_stack[#].hh.b1
+ {saved level for regions 5 and 6, or group code}
+@d save_index(#)==save_stack[#].hh.rh
+ {|eqtb| location or token or |save_stack| location}
+@d restore_old_value=0 {|save_type| when a value should be restored later}
+@d restore_zero=1 {|save_type| when an undefined entry should be restored}
+@d insert_token=2 {|save_type| when a token is being saved for later use}
+@d level_boundary=3 {|save_type| corresponding to beginning of group}
+
+@ Here are the group codes that are used to discriminate between different
+kinds of groups. They allow \TeX\ to decide what special actions, if any,
+should be performed when a group ends.
+\def\grp{\.{\char'173...\char'175}}
+
+Some groups are not supposed to be ended by right braces. For example,
+the `\.\$' that begins a math formula causes a |math_shift_group| to
+be started, and this should be terminated by a matching `\.\$'. Similarly,
+a group that starts with \.{\\left} should end with \.{\\right}, and
+one that starts with \.{\\begingroup} should end with \.{\\endgroup}.
+
+@d bottom_level=0 {group code for the outside world}
+@d simple_group=1 {group code for local structure only}
+@d hbox_group=2 {code for `\.{\\hbox}\grp'}
+@d adjusted_hbox_group=3 {code for `\.{\\hbox}\grp' in vertical mode}
+@d vbox_group=4 {code for `\.{\\vbox}\grp'}
+@d vtop_group=5 {code for `\.{\\vtop}\grp'}
+@d align_group=6 {code for `\.{\\halign}\grp', `\.{\\valign}\grp'}
+@d no_align_group=7 {code for `\.{\\noalign}\grp'}
+@d output_group=8 {code for output routine}
+@d math_group=9 {code for, e.g., `\.{\char'136}\grp'}
+@d disc_group=10 {code for `\.{\\discretionary}\grp\grp\grp'}
+@d insert_group=11 {code for `\.{\\insert}\grp', `\.{\\vadjust}\grp'}
+@d vcenter_group=12 {code for `\.{\\vcenter}\grp'}
+@d math_choice_group=13 {code for `\.{\\mathchoice}\grp\grp\grp\grp'}
+@d semi_simple_group=14 {code for `\.{\\begingroup...\\endgroup}'}
+@d math_shift_group=15 {code for `\.{\$...\$}'}
+@d math_left_group=16 {code for `\.{\\left...\\right}'}
+@d max_group_code=16
+
+@<Types...@>=
+@!group_code=0..max_group_code; {|save_level| for a level boundary}
+
+@ The global variable |cur_group| keeps track of what sort of group we are
+currently in. Another global variable, |cur_boundary|, points to the
+topmost |level_boundary| word. And |cur_level| is the current depth of
+nesting. The routines are designed to preserve the condition that no entry
+in the |save_stack| or in |eqtb| ever has a level greater than |cur_level|.
+
+@ @<Glob...@>=
+@!save_stack : array[0..save_size] of memory_word;
+@!save_ptr : 0..save_size; {first unused entry on |save_stack|}
+@!max_save_stack:0..save_size; {maximum usage of save stack}
+@!cur_level: quarterword; {current nesting level for groups}
+@!cur_group: group_code; {current group type}
+@!cur_boundary: 0..save_size; {where the current level begins}
+
+@ At this time it might be a good idea for the reader to review the introduction
+to |eqtb| that was given above just before the long lists of parameter names.
+Recall that the ``outer level'' of the program is |level_one|, since
+undefined control sequences are assumed to be ``defined'' at |level_zero|.
+
+@<Set init...@>=
+save_ptr:=0; cur_level:=level_one; cur_group:=bottom_level; cur_boundary:=0;
+max_save_stack:=0;
+
+@ The following macro is used to test if there is room for up to six more
+entries on |save_stack|. By making a conservative test like this, we can
+get by with testing for overflow in only a few places.
+
+@d check_full_save_stack==if save_ptr>max_save_stack then
+ begin max_save_stack:=save_ptr;
+ if max_save_stack>save_size-6 then overflow("save size",save_size);
+@:TeX capacity exceeded save size}{\quad save size@>
+ end
+
+@ Procedure |new_save_level| is called when a group begins. The
+argument is a group identification code like `|hbox_group|'. After
+calling this routine, it is safe to put five more entries on |save_stack|.
+
+In some cases integer-valued items are placed onto the
+|save_stack| just below a |level_boundary| word, because this is a
+convenient place to keep information that is supposed to ``pop up'' just
+when the group has finished.
+For example, when `\.{\\hbox to 100pt}\grp' is being treated, the 100pt
+dimension is stored on |save_stack| just before |new_save_level| is
+called.
+
+We use the notation |saved(k)| to stand for an integer item that
+appears in location |save_ptr+k| of the save stack.
+
+@d saved(#)==save_stack[save_ptr+#].int
+
+@p procedure new_save_level(@!c:group_code); {begin a new level of grouping}
+begin check_full_save_stack;
+save_type(save_ptr):=level_boundary; save_level(save_ptr):=cur_group;
+save_index(save_ptr):=cur_boundary;
+if cur_level=max_quarterword then overflow("grouping levels",
+@:TeX capacity exceeded grouping levels}{\quad grouping levels@>
+ max_quarterword-min_quarterword);
+ {quit if |(cur_level+1)| is too big to be stored in |eqtb|}
+cur_boundary:=save_ptr; incr(cur_level); incr(save_ptr); cur_group:=c;
+end;
+
+@ Just before an entry of |eqtb| is changed, the following procedure should
+be called to update the other data structures properly. It is important
+to keep in mind that reference counts in |mem| include references from
+within |save_stack|, so these counts must be handled carefully.
+@^reference counts@>
+
+@p procedure eq_destroy(@!w:memory_word); {gets ready to forget |w|}
+var q:pointer; {|equiv| field of |w|}
+begin case eq_type_field(w) of
+call,long_call,outer_call,long_outer_call: delete_token_ref(equiv_field(w));
+glue_ref: delete_glue_ref(equiv_field(w));
+shape_ref: begin q:=equiv_field(w); {we need to free a \.{\\parshape} block}
+ if q<>null then free_node(q,info(q)+info(q)+1);
+ end; {such a block is |2n+1| words long, where |n=info(q)|}
+box_ref: flush_node_list(equiv_field(w));
+othercases do_nothing
+endcases;
+end;
+
+@ To save a value of |eqtb[p]| that was established at level |l|, we
+can use the following subroutine.
+
+@p procedure eq_save(@!p:pointer;@!l:quarterword); {saves |eqtb[p]|}
+begin check_full_save_stack;
+if l=level_zero then save_type(save_ptr):=restore_zero
+else begin save_stack[save_ptr]:=eqtb[p]; incr(save_ptr);
+ save_type(save_ptr):=restore_old_value;
+ end;
+save_level(save_ptr):=l; save_index(save_ptr):=p; incr(save_ptr);
+end;
+
+@ The procedure |eq_define| defines an |eqtb| entry having specified
+|eq_type| and |equiv| fields, and saves the former value if appropriate.
+This procedure is used only for entries in the first four regions of |eqtb|,
+i.e., only for entries that have |eq_type| and |equiv| fields.
+After calling this routine, it is safe to put four more entries on
+|save_stack|, provided that there was room for four more entries before
+the call, since |eq_save| makes the necessary test.
+
+@p procedure eq_define(@!p:pointer;@!t:quarterword;@!e:halfword);
+ {new data for |eqtb|}
+begin if eq_level(p)=cur_level then eq_destroy(eqtb[p])
+else if cur_level>level_one then eq_save(p,eq_level(p));
+eq_level(p):=cur_level; eq_type(p):=t; equiv(p):=e;
+end;
+
+@ The counterpart of |eq_define| for the remaining (fullword) positions in
+|eqtb| is called |eq_word_define|. Since |xeq_level[p]>=level_one| for all
+|p|, a `|restore_zero|' will never be used in this case.
+
+@p procedure eq_word_define(@!p:pointer;@!w:integer);
+begin if xeq_level[p]<>cur_level then
+ begin eq_save(p,xeq_level[p]); xeq_level[p]:=cur_level;
+ end;
+eqtb[p].int:=w;
+end;
+
+@ The |eq_define| and |eq_word_define| routines take care of local definitions.
+@^global definitions@>
+Global definitions are done in almost the same way, but there is no need
+to save old values, and the new value is associated with |level_one|.
+
+@p procedure geq_define(@!p:pointer;@!t:quarterword;@!e:halfword);
+ {global |eq_define|}
+begin eq_destroy(eqtb[p]);
+eq_level(p):=level_one; eq_type(p):=t; equiv(p):=e;
+end;
+@#
+procedure geq_word_define(@!p:pointer;@!w:integer); {global |eq_word_define|}
+begin eqtb[p].int:=w; xeq_level[p]:=level_one;
+end;
+
+@ Subroutine |save_for_after| puts a token on the stack for save-keeping.
+
+@p procedure save_for_after(@!t:halfword);
+begin if cur_level>level_one then
+ begin check_full_save_stack;
+ save_type(save_ptr):=insert_token; save_level(save_ptr):=level_zero;
+ save_index(save_ptr):=t; incr(save_ptr);
+ end;
+end;
+
+@ The |unsave| routine goes the other way, taking items off of |save_stack|.
+This routine takes care of restoration when a level ends; everything
+belonging to the topmost group is cleared off of the save stack.
+
+@p@t\4@>@<Declare the procedure called |restore_trace|@>@;@/
+procedure@?back_input; forward; @t\2@>
+procedure unsave; {pops the top level off the save stack}
+label done;
+var p:pointer; {position to be restored}
+@!l:quarterword; {saved level, if in fullword regions of |eqtb|}
+@!t:halfword; {saved value of |cur_tok|}
+begin if cur_level>level_one then
+ begin decr(cur_level);
+ @<Clear off top level from |save_stack|@>;
+ end
+else confusion("curlevel"); {|unsave| is not used when |cur_group=bottom_level|}
+@:this can't happen curlevel}{\quad curlevel@>
+end;
+
+@ @<Clear off...@>=
+loop@+begin decr(save_ptr);
+ if save_type(save_ptr)=level_boundary then goto done;
+ p:=save_index(save_ptr);
+ if save_type(save_ptr)=insert_token then
+ @<Insert token |p| into \TeX's input@>
+ else begin if save_type(save_ptr)=restore_old_value then
+ begin l:=save_level(save_ptr); decr(save_ptr);
+ end
+ else save_stack[save_ptr]:=eqtb[undefined_control_sequence];
+ @<Store \(s)|save_stack[save_ptr]| in |eqtb[p]|, unless
+ |eqtb[p]| holds a global value@>;
+ end;
+ end;
+done: cur_group:=save_level(save_ptr); cur_boundary:=save_index(save_ptr)
+
+@ A global definition, which sets the level to |level_one|,
+@^global definitions@>
+will not be undone by |unsave|. If at least one global definition of
+|eqtb[p]| has been carried out within the group that just ended, the
+last such definition will therefore survive.
+
+@<Store \(s)|save...@>=
+if p<int_base then
+ if eq_level(p)=level_one then
+ begin eq_destroy(save_stack[save_ptr]); {destroy the saved value}
+ @!stat if tracing_restores>0 then restore_trace(p,"retaining");@+tats@;@/
+ end
+ else begin eq_destroy(eqtb[p]); {destroy the current value}
+ eqtb[p]:=save_stack[save_ptr]; {restore the saved value}
+ @!stat if tracing_restores>0 then restore_trace(p,"restoring");@+tats@;@/
+ end
+else if xeq_level[p]<>level_one then
+ begin eqtb[p]:=save_stack[save_ptr]; xeq_level[p]:=l;
+ @!stat if tracing_restores>0 then restore_trace(p,"restoring");@+tats@;@/
+ end
+else begin
+ @!stat if tracing_restores>0 then restore_trace(p,"retaining");@+tats@;@/
+ end
+
+@ @<Declare the procedure called |restore_trace|@>=
+@!stat procedure restore_trace(@!p:pointer;@!s:str_number);
+ {|eqtb[p]| has just been restored or retained}
+begin begin_diagnostic; print_char("{"); print(s); print_char(" ");
+show_eqtb(p); print_char("}");
+end_diagnostic(false);
+end;
+tats
+
+@ When looking for possible pointers to a memory location, it is helpful
+to look for references from |eqtb| that might be waiting on the
+save stack. Of course, we might find spurious pointers too; but this
+routine is merely an aid when debugging, and at such times we are
+grateful for any scraps of information, even if they prove to be irrelevant.
+@^dirty \PASCAL@>
+
+@<Search |save_stack| for equivalents that point to |p|@>=
+if save_ptr>0 then for q:=0 to save_ptr-1 do
+ begin if equiv_field(save_stack[q])=p then
+ begin print_nl("SAVE("); print_int(q); print_char(")");
+ end;
+ end
+
+@ Most of the parameters kept in |eqtb| can be changed freely, but there's
+an exception: The magnification should not be used with two different
+values during any \TeX\ job, since a single magnification is applied to an
+entire run. The global variable |mag_set| is set to the current magnification
+whenever it becomes necessary to ``freeze'' it at a particular value.
+
+@<Glob...@>=
+@!mag_set:integer; {if nonzero, this magnification should be used henceforth}
+
+@ @<Set init...@>=
+mag_set:=0;
+
+@ The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag|
+for magnification.
+
+@p procedure prepare_mag;
+begin if (mag_set>0)and(mag<>mag_set) then
+ begin print_err("Incompatible magnification ("); print_int(mag);
+@.Incompatible magnification@>
+ print(");"); print_nl(" the previous value will be retained");
+ help2("I can handle only one magnification ratio per job. So I've")@/
+ ("reverted to the magnification you used earlier on this run.");@/
+ int_error(mag_set);
+ geq_word_define(int_base+mag_code,mag_set); {|mag:=mag_set|}
+ end;
+if (mag<=0)or(mag>32768) then
+ begin print_err("Illegal magnification has been changed to 1000");@/
+@.Illegal magnification...@>
+ help1("The magnification ratio must be between 1 and 32768.");
+ int_error(mag); geq_word_define(int_base+mag_code,1000);
+ end;
+mag_set:=mag;
+end;
+
+@* \[20] Token lists.
+A \TeX\ token is either a character or a control sequence, and it is
+@^token@>
+represented internally in one of two ways: (1)~A character whose ASCII
+code number is |c| and whose command code is |m| is represented as the
+number $2^8m+c$; the command code is in the range |1<=m<=14|. (2)~A control
+sequence whose |eqtb| address is |p| is represented as the number
+|cs_token_flag+p|. Here |cs_token_flag=@t$2^{12}-1$@>| is larger than
+$2^8m+c$, yet it is small enough that |cs_token_flag+p< max_halfword|;
+thus, a token fits comfortably in a halfword.
+
+A token |t| represents a |left_brace| command if and only if
+|t<left_brace_limit|; it represents a |right_brace| command if and only if
+we have |left_brace_limit<=t<right_brace_limit|; and it represents a |match| or
+|end_match| command if and only if |match_token<=t<=end_match_token|.
+The following definitions take care of these token-oriented constants
+and a few others.
+
+@d cs_token_flag==@'7777 {amount added to the |eqtb| location in a
+ token that stands for a control sequence; is a multiple of~256, less~1}
+@d left_brace_token=@'0400 {$2^8\cdot|left_brace|$}
+@d left_brace_limit=@'1000 {$2^8\cdot(|left_brace|+1)$}
+@d right_brace_token=@'1000 {$2^8\cdot|right_brace|$}
+@d right_brace_limit=@'1400 {$2^8\cdot(|right_brace|+1)$}
+@d math_shift_token=@'1400 {$2^8\cdot|math_shift|$}
+@d tab_token=@'2000 {$2^8\cdot|tab_mark|$}
+@d out_param_token=@'2400 {$2^8\cdot|out_param|$}
+@d space_token=@'5040 {$2^8\cdot|spacer|+|" "|$}
+@d letter_token=@'5400 {$2^8\cdot|letter|$}
+@d other_token=@'6000 {$2^8\cdot|other_char|$}
+@d match_token=@'6400 {$2^8\cdot|match|$}
+@d end_match_token=@'7000 {$2^8\cdot|end_match|$}
+
+@ @<Check the ``constant''...@>=
+if cs_token_flag+undefined_control_sequence>max_halfword then bad:=21;
+
+@ A token list is a singly linked list of one-word nodes in |mem|, where
+each word contains a token and a link. Macro definitions, output-routine
+definitions, marks, \.{\\write} texts, and a few other things
+are remembered by \TeX\ in the form
+of token lists, usually preceded by a node with a reference count in its
+|token_ref_count| field. The token stored in location |p| is called
+|info(p)|.
+
+Three special commands appear in the token lists of macro definitions.
+When |m=match|, it means that \TeX\ should scan a parameter
+for the current macro; when |m=end_match|, it means that parameter
+matching should end and \TeX\ should start reading the macro text; and
+when |m=out_param|, it means that \TeX\ should insert parameter
+number |c| into the text at this point.
+
+The enclosing \.{\char'173} and \.{\char'175} characters of a macro
+definition are omitted, but the final right brace of an output routine
+is included at the end of its token list.
+
+Here is an example macro definition that illustrates these conventions.
+After \TeX\ processes the text
+$$\.{\\def\\mac a\#1\#2 \\b \{\#1\\-a \#\#1\#2 \#2\}}$$
+the definition of \.{\\mac} is represented as a token list containing
+$$\def\,{\hskip2pt}
+\vbox{\halign{\hfil#\hfil\cr
+(reference count), |letter|\,\.a, |match|\,\#, |match|\,\#, |spacer|\,\.\ ,
+\.{\\b}, |end_match|,\cr
+|out_param|\,1, \.{\\-}, |letter|\,\.a, |spacer|\,\.\ , |mac_param|\,\#,
+|other_char|\,\.1,\cr
+|out_param|\,2, |spacer|\,\.\ , |out_param|\,2.\cr}}$$
+The procedure |scan_toks| builds such token lists, and |macro_call|
+does the parameter matching.
+@^reference counts@>
+
+Examples such as
+$$\.{\\def\\m\{\\def\\m\{a\}\ b\}}$$
+explain why reference counts would be needed even if \TeX\ had no \.{\\let}
+operation: When the token list for \.{\\m} is being read, the redefinition of
+\.{\\m} changes the |eqtb| entry before the token list has been fully
+consumed, so we dare not simply destroy a token list when its
+control sequence is being redefined.
+
+If the parameter-matching part of a definition ends with `\.{\#\{}',
+the corresponding token list will have `\.\{' just before the `|end_match|'
+and also at the very end. The first `\.\{' is used to delimit the parameter; the
+second one keeps the first from disappearing.
+
+@ The procedure |show_token_list|, which prints a symbolic form of
+the token list that starts at a given node |p|, illustrates these
+conventions. The token list being displayed should not begin with a reference
+count. However, the procedure is intended to be robust, so that if the
+memory links are awry or if |p| is not really a pointer to a token list,
+nothing catastrophic will happen.
+
+An additional parameter |q| is also given; this parameter is either null
+or it points to a node in the token list where a certain magic computation
+takes place that will be explained later. (Basically, |q| is non-null when
+we are printing the two-line context information at the time of an error
+message; |q| marks the place corresponding to where the second line
+should begin.)
+
+For example, if |p| points to the node containing the first \.a in the
+token list above, then |show_token_list| will print the string
+$$\hbox{`\.{a\#1\#2\ \\b\ ->\#1\\-a\ \#\#1\#2\ \#2}';}$$
+and if |q| points to the node containing the second \.a,
+the magic computation will be performed just before the second \.a is printed.
+
+The generation will stop, and `\.{\\ETC.}' will be printed, if the length
+of printing exceeds a given limit~|l|. Anomalous entries are printed in the
+form of control sequences that are not followed by a blank space, e.g.,
+`\.{\\BAD.}'; this cannot be confused with actual control sequences because
+a real control sequence named \.{BAD} would come out `\.{\\BAD\ }'.
+
+@<Declare the procedure called |show_token_list|@>=
+procedure show_token_list(@!p,@!q:integer;@!l:integer);
+label exit;
+var m,@!c:integer; {pieces of a token}
+@!match_chr:ASCII_code; {character used in a `|match|'}
+@!n:ASCII_code; {the highest parameter number, as an ASCII digit}
+begin match_chr:="#"; n:="0"; tally:=0;
+while (p<>null) and (tally<l) do
+ begin if p=q then @<Do magic computation@>;
+ @<Display token |p|, and |return| if there are problems@>;
+ p:=link(p);
+ end;
+if p<>null then print_esc("ETC.");
+@.ETC@>
+exit:
+end;
+
+@ @<Display token |p|...@>=
+if (p<hi_mem_min) or (p>mem_end) then
+ begin print_esc("CLOBBERED."); return;
+@.CLOBBERED@>
+ end;
+if info(p)>=cs_token_flag then print_cs(info(p)-cs_token_flag)
+else begin m:=info(p) div @'400; c:=info(p) mod @'400;
+ if info(p)<0 then print_esc("BAD.")
+@.BAD@>
+ else @<Display the token $(|m|,|c|)$@>;
+ end
+
+@ The procedure usually ``learns'' the character code used for macro
+parameters by seeing one in a |match| command before it runs into any
+|out_param| commands.
+
+@<Display the token ...@>=
+case m of
+left_brace,right_brace,math_shift,tab_mark,sup_mark,sub_mark,spacer,
+ letter,other_char: print(c);
+mac_param: begin print(c); print(c);
+ end;
+out_param: begin print(match_chr);
+ if c<=9 then print_char(c+"0")
+ else begin print_char("!"); return;
+ end;
+ end;
+match: begin match_chr:=c; print(c); incr(n); print_char(n);
+ if n>"9" then return;
+ end;
+end_match: print("->");
+@.->@>
+othercases print_esc("BAD.")
+@.BAD@>
+endcases
+
+@ Here's the way we sometimes want to display a token list, given a pointer
+to its reference count; the pointer may be null.
+
+@p procedure token_show(@!p:pointer);
+begin if p<>null then show_token_list(link(p),null,10000000);
+end;
+
+@ The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in
+symbolic form, including the expansion of a macro or mark.
+
+@p procedure print_meaning;
+begin print_cmd_chr(cur_cmd,cur_chr);
+if cur_cmd>=call then
+ begin print_char(":"); print_ln; token_show(cur_chr);
+ end
+else if cur_cmd=top_bot_mark then
+ begin print_char(":"); print_ln;
+ token_show(cur_mark[cur_chr]);
+ end;
+end;
+
+@* \[21] Introduction to the syntactic routines.
+Let's pause a moment now and try to look at the Big Picture.
+The \TeX\ program consists of three main parts: syntactic routines,
+semantic routines, and output routines. The chief purpose of the
+syntactic routines is to deliver the user's input to the semantic routines,
+one token at a time. The semantic routines act as an interpreter
+responding to these tokens, which may be regarded as commands. And the
+output routines are periodically called on to convert box-and-glue
+lists into a compact set of instructions that will be sent
+to a typesetter. We have discussed the basic data structures and utility
+routines of \TeX, so we are good and ready to plunge into the real activity by
+considering the syntactic routines.
+
+Our current goal is to come to grips with the |get_next| procedure,
+which is the keystone of \TeX's input mechanism. Each call of |get_next|
+sets the value of three variables |cur_cmd|, |cur_chr|, and |cur_cs|,
+representing the next input token.
+$$\vbox{\halign{#\hfil\cr
+ \hbox{|cur_cmd| denotes a command code from the long list of codes
+ given above;}\cr
+ \hbox{|cur_chr| denotes a character code or other modifier of the command
+ code;}\cr
+ \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr
+ \hbox{\qquad if the current token was a control sequence,
+ otherwise it's zero.}\cr}}$$
+Underlying this external behavior of |get_next| is all the machinery
+necessary to convert from character files to tokens. At a given time we
+may be only partially finished with the reading of several files (for
+which \.{\\input} was specified), and partially finished with the expansion
+of some user-defined macros and/or some macro parameters, and partially
+finished with the generation of some text in a template for \.{\\halign},
+and so on. When reading a character file, special characters must be
+classified as math delimiters, etc.; comments and extra blank spaces must
+be removed, paragraphs must be recognized, and control sequences must be
+found in the hash table. Furthermore there are occasions in which the
+scanning routines have looked ahead for a word like `\.{plus}' but only
+part of that word was found, hence a few characters must be put back
+into the input and scanned again.
+
+To handle these situations, which might all be present simultaneously,
+\TeX\ uses various stacks that hold information about the incomplete
+activities, and there is a finite state control for each level of the
+input mechanism. These stacks record the current state of an implicitly
+recursive process, but the |get_next| procedure is not recursive.
+Therefore it will not be difficult to translate these algorithms into
+low-level languages that do not support recursion.
+
+@<Glob...@>=
+@!cur_cmd: eight_bits; {current command set by |get_next|}
+@!cur_chr: halfword; {operand of current command}
+@!cur_cs: pointer; {control sequence found here, zero if none found}
+@!cur_tok: halfword; {packed representative of |cur_cmd| and |cur_chr|}
+
+@ The |print_cmd_chr| routine prints a symbolic interpretation of a
+command code and its modifier. This is used in certain `\.{You can\'t}'
+error messages, and in the implementation of diagnostic routines like
+\.{\\show}.
+
+The body of |print_cmd_chr| is a rather tedious listing of print
+commands, and most of it is essentially an inverse to the |primitive|
+routine that enters a \TeX\ primitive into |eqtb|. Therefore much of
+this procedure appears elsewhere in the program,
+together with the corresponding |primitive| calls.
+
+@d chr_cmd(#)==begin print(#); print_ASCII(chr_code);
+ end
+
+@<Declare the procedure called |print_cmd_chr|@>=
+procedure print_cmd_chr(@!cmd:quarterword;@!chr_code:halfword);
+begin case cmd of
+left_brace: chr_cmd("begin-group character ");
+right_brace: chr_cmd("end-group character ");
+math_shift: chr_cmd("math shift character ");
+mac_param: chr_cmd("macro parameter character ");
+sup_mark: chr_cmd("superscript character ");
+sub_mark: chr_cmd("subscript character ");
+endv: print("end of alignment template");
+spacer: chr_cmd("blank space ");
+letter: chr_cmd("the letter ");
+other_char: chr_cmd("the character ");
+@t\4@>@<Cases of |print_cmd_chr| for symbolic printing of primitives@>@/
+othercases print("[unknown command code!]")
+endcases;
+end;
+
+@ Here is a procedure that displays the current command.
+
+@p procedure show_cur_cmd_chr;
+begin begin_diagnostic; print_nl("{");
+if mode<>shown_mode then
+ begin print_mode(mode); print(": "); shown_mode:=mode;
+ end;
+print_cmd_chr(cur_cmd,cur_chr); print_char("}");
+end_diagnostic(false);
+end;
+
+@* \[22] Input stacks and states.
+This implementation of
+\TeX\ uses two different conventions for representing sequential stacks.
+@^stack conventions@>@^conventions for representing stacks@>
+
+\yskip\hangg 1) If there is frequent access to the top entry, and if the
+stack is essentially never empty, then the top entry is kept in a global
+variable (even better would be a machine register), and the other entries
+appear in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the
+semantic stack described above is handled this way, and so is the input
+stack that we are about to study.
+
+\yskip\hangg 2) If there is infrequent top access, the entire stack contents
+are in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack|
+is treated this way, as we have seen.
+
+\yskip\noindent
+The state of \TeX's input mechanism appears in the input stack, whose
+entries are records with six fields, called |state|, |index|, |start|, |loc|,
+|limit|, and |name|. This stack is maintained with
+convention~(1), so it is declared in the following way:
+
+@<Types...@>=
+@!in_state_record = record
+ @!state_field, @!index_field: quarterword;
+ @!start_field,@!loc_field, @!limit_field, @!name_field: halfword;
+ end;
+
+@ @<Glob...@>=
+@!input_stack : array[0..stack_size] of in_state_record;
+@!input_ptr : 0..stack_size; {first unused location of |input_stack|}
+@!max_in_stack: 0..stack_size; {largest value of |input_ptr| when pushing}
+@!cur_input : in_state_record;
+ {the ``top'' input state, according to convention (1)}
+
+@ We've already defined the special variable |loc==cur_input.loc_field|
+in our discussion of basic input-output routines. The other components of
+|cur_input| are defined in the same way:
+
+@d state==cur_input.state_field {current scanner state}
+@d index==cur_input.index_field {reference for buffer information}
+@d start==cur_input.start_field {starting position in |buffer|}
+@d limit==cur_input.limit_field {end of current line in |buffer|}
+@d name==cur_input.name_field {name of the current file}
+
+@ Let's look more closely now at the control variables
+(|state|,~|index|,~|start|,~|loc|,~|limit|,~|name|),
+assuming that \TeX\ is reading a line of characters that have been input
+from some file or from the user's terminal. There is an array called
+|buffer| that acts as a stack of all lines of characters that are
+currently being read from files, including all lines on subsidiary
+levels of the input stack that are not yet completed. \TeX\ will return to
+the other lines when it is finished with the present input file.
+
+(Incidentally, on a machine with byte-oriented addressing, it might be
+appropriate to combine |buffer| with the |str_pool| array,
+letting the buffer entries grow downward from the top of the string pool
+and checking that these two tables don't bump into each other.)
+
+The line we are currently working on begins in position |start| of the
+buffer; the next character we are about to read is |buffer[loc]|; and
+|limit| is the location of the last character present. If |loc>limit|,
+the line has been completely read. Usually |buffer[limit]| is the
+|end_line_char|, denoting the end of a line, but this is not
+true if the current line is an insertion that was entered on the user's
+terminal in response to an error message.
+
+The |name| variable is a string number that designates the name of
+the current file, if we are reading a text file. It is zero if we
+are reading from the terminal; it is |n+1| if we are reading from
+input stream |n|, where |0<=n<=16|. (Input stream 16 stands for
+an invalid stream number; in such cases the input is actually from
+the terminal, under control of the procedure |read_toks|.)
+
+The |state| variable has one of three values, when we are scanning such
+files:
+$$\baselineskip 15pt\vbox{\halign{#\hfil\cr
+1) |state=mid_line| is the normal state.\cr
+2) |state=skip_blanks| is like |mid_line|, but blanks are ignored.\cr
+3) |state=new_line| is the state at the beginning of a line.\cr}}$$
+These state values are assigned numeric codes so that if we add the state
+code to the next character's command code, we get distinct values. For
+example, `|mid_line+spacer|' stands for the case that a blank
+space character occurs in the middle of a line when it is not being
+ignored; after this case is processed, the next value of |state| will
+be |skip_blanks|.
+
+@d mid_line=1 {|state| code when scanning a line of characters}
+@d skip_blanks=2+max_char_code {|state| code when ignoring blanks}
+@d new_line=3+max_char_code+max_char_code {|state| code at start of line}
+
+@ Additional information about the current line is available via the
+|index| variable, which counts how many lines of characters are present
+in the buffer below the current level. We have |index=0| when reading
+from the terminal and prompting the user for each line; then if the user types,
+e.g., `\.{\\input paper}', we will have |index=1| while reading
+the file \.{paper.tex}. However, it does not follow that |index| is the
+same as the input stack pointer, since many of the levels on the input
+stack may come from token lists. For example, the instruction `\.{\\input
+paper}' might occur in a token list.
+
+The global variable |in_open| is equal to the |index|
+value of the highest non-token-list level. Thus, the number of partially read
+lines in the buffer is |in_open+1|, and we have |in_open=index|
+when we are not reading a token list.
+
+If we are not currently reading from the terminal, or from an input
+stream, we are reading from the file variable |input_file[index]|. We use
+the notation |terminal_input| as a convenient abbreviation for |name=0|,
+and |cur_file| as an abbreviation for |input_file[index]|.
+
+The global variable |line| contains the line number in the topmost
+open file, for use in error messages. If we are not reading from
+the terminal, |line_stack[index]| holds the line number for the
+enclosing level, so that |line| can be restored when the current
+file has been read. Line numbers should never be negative, since the
+negative of the current line number is used to identify the user's output
+routine in the |mode_line| field of the semantic nest entries.
+
+If more information about the input state is needed, it can be
+included in small arrays like those shown here. For example,
+the current page or segment number in the input file might be
+put into a variable |@!page|, maintained for enclosing levels in
+`\ignorespaces|@!page_stack:array[1..max_in_open] of integer|\unskip'
+by analogy with |line_stack|.
+@^system dependencies@>
+
+@d terminal_input==(name=0) {are we reading from the terminal?}
+@d cur_file==input_file[index] {the current |alpha_file| variable}
+
+@<Glob...@>=
+@!in_open : 0..max_in_open; {the number of lines in the buffer, less one}
+@!open_parens : 0..max_in_open; {the number of open text files}
+@!input_file : array[1..max_in_open] of alpha_file;
+@!line : integer; {current line number in the current source file}
+@!line_stack : array[1..max_in_open] of integer;
+
+@ Users of \TeX\ sometimes forget to balance left and right braces properly,
+and one of the ways \TeX\ tries to spot such errors is by considering an
+input file as broken into subfiles by control sequences that
+are declared to be \.{\\outer}.
+
+A variable called |scanner_status| tells \TeX\ whether or not to complain
+when a subfile ends. This variable has six possible values:
+
+\yskip\hang|normal|, means that a subfile can safely end here without incident.
+
+\yskip\hang|skipping|, means that a subfile can safely end here, but not a file,
+because we're reading past some conditional text that was not selected.
+
+\yskip\hang|defining|, means that a subfile shouldn't end now because a
+macro is being defined.
+
+\yskip\hang|matching|, means that a subfile shouldn't end now because a
+macro is being used and we are searching for the end of its arguments.
+
+\yskip\hang|aligning|, means that a subfile shouldn't end now because we are
+not finished with the preamble of an \.{\\halign} or \.{\\valign}.
+
+\yskip\hang|absorbing|, means that a subfile shouldn't end now because we are
+reading a balanced token list for \.{\\message}, \.{\\write}, etc.
+
+\yskip\noindent
+If the |scanner_status| is not |normal|, the variable |warning_index| points
+to the |eqtb| location for the relevant control sequence name to print
+in an error message.
+
+@d skipping=1 {|scanner_status| when passing conditional text}
+@d defining=2 {|scanner_status| when reading a macro definition}
+@d matching=3 {|scanner_status| when reading macro arguments}
+@d aligning=4 {|scanner_status| when reading an alignment preamble}
+@d absorbing=5 {|scanner_status| when reading a balanced text}
+
+@<Glob...@>=
+@!scanner_status : normal..absorbing; {can a subfile end now?}
+@!warning_index : pointer; {identifier relevant to non-|normal| scanner status}
+@!def_ref : pointer; {reference count of token list being defined}
+
+@ Here is a procedure that uses |scanner_status| to print a warning message
+when a subfile has ended, and at certain other crucial times:
+
+@<Declare the procedure called |runaway|@>=
+procedure runaway;
+var p:pointer; {head of runaway list}
+begin if scanner_status>skipping then
+ begin print_nl("Runaway ");
+@.Runaway...@>
+ case scanner_status of
+ defining: begin print("definition"); p:=def_ref;
+ end;
+ matching: begin print("argument"); p:=temp_head;
+ end;
+ aligning: begin print("preamble"); p:=hold_head;
+ end;
+ absorbing: begin print("text"); p:=def_ref;
+ end;
+ end; {there are no other cases}
+ print_char("?");print_ln; show_token_list(link(p),null,error_line-10);
+ end;
+end;
+
+@ However, all this discussion about input state really applies only to the
+case that we are inputting from a file. There is another important case,
+namely when we are currently getting input from a token list. In this case
+|state=token_list|, and the conventions about the other state variables
+are different:
+
+\yskip\hang|loc| is a pointer to the current node in the token list, i.e.,
+the node that will be read next. If |loc=null|, the token list has been
+fully read.
+
+\yskip\hang|start| points to the first node of the token list; this node
+may or may not contain a reference count, depending on the type of token
+list involved.
+
+\yskip\hang|token_type|, which takes the place of |index| in the
+discussion above, is a code number that explains what kind of token list
+is being scanned.
+
+\yskip\hang|name| points to the |eqtb| address of the control sequence
+being expanded, if the current token list is a macro.
+
+\yskip\hang|param_start|, which takes the place of |limit|, tells where
+the parameters of the current macro begin in the |param_stack|, if the
+current token list is a macro.
+
+\yskip\noindent The |token_type| can take several values, depending on
+where the current token list came from:
+
+\yskip\hang|parameter|, if a parameter is being scanned;
+
+\hang|u_template|, if the \<u_j> part of an alignment
+template is being scanned;
+
+\hang|v_template|, if the \<v_j> part of an alignment
+template is being scanned;
+
+\hang|backed_up|, if the token list being scanned has been inserted as
+`to be read again'.
+
+\hang|inserted|, if the token list being scanned has been inserted as
+the text expansion of a \.{\\count} or similar variable;
+
+\hang|macro|, if a user-defined control sequence is being scanned;
+
+\hang|output_text|, if an \.{\\output} routine is being scanned;
+
+\hang|every_par_text|, if the text of \.{\\everypar} is being scanned;
+
+\hang|every_math_text|, if the text of \.{\\everymath} is being scanned;
+
+\hang|every_display_text|, if the text of \.{\\everydisplay} is being scanned;
+
+\hang|every_hbox_text|, if the text of \.{\\everyhbox} is being scanned;
+
+\hang|every_vbox_text|, if the text of \.{\\everyvbox} is being scanned;
+
+\hang|every_job_text|, if the text of \.{\\everyjob} is being scanned;
+
+\hang|every_cr_text|, if the text of \.{\\everycr} is being scanned;
+
+\hang|mark_text|, if the text of a \.{\\mark} is being scanned;
+
+\hang|write_text|, if the text of a \.{\\write} is being scanned.
+
+\yskip\noindent
+The codes for |output_text|, |every_par_text|, etc., are equal to a constant
+plus the corresponding codes for token list parameters |output_routine_loc|,
+|every_par_loc|, etc. The token list begins with a reference count if and
+only if |token_type>=macro|.
+@^reference counts@>
+
+@d token_list=0 {|state| code when scanning a token list}
+@d token_type==index {type of current token list}
+@d param_start==limit {base of macro parameters in |param_stack|}
+@d parameter=0 {|token_type| code for parameter}
+@d u_template=1 {|token_type| code for \<u_j> template}
+@d v_template=2 {|token_type| code for \<v_j> template}
+@d backed_up=3 {|token_type| code for text to be reread}
+@d inserted=4 {|token_type| code for inserted texts}
+@d macro=5 {|token_type| code for defined control sequences}
+@d output_text=6 {|token_type| code for output routines}
+@d every_par_text=7 {|token_type| code for \.{\\everypar}}
+@d every_math_text=8 {|token_type| code for \.{\\everymath}}
+@d every_display_text=9 {|token_type| code for \.{\\everydisplay}}
+@d every_hbox_text=10 {|token_type| code for \.{\\everyhbox}}
+@d every_vbox_text=11 {|token_type| code for \.{\\everyvbox}}
+@d every_job_text=12 {|token_type| code for \.{\\everyjob}}
+@d every_cr_text=13 {|token_type| code for \.{\\everycr}}
+@d mark_text=14 {|token_type| code for \.{\\topmark}, etc.}
+@d write_text=15 {|token_type| code for \.{\\write}}
+
+@ The |param_stack| is an auxiliary array used to hold pointers to the token
+lists for parameters at the current level and subsidiary levels of input.
+This stack is maintained with convention (2), and it grows at a different
+rate from the others.
+
+@<Glob...@>=
+@!param_stack:array [0..param_size] of pointer;
+ {token list pointers for parameters}
+@!param_ptr:0..param_size; {first unused entry in |param_stack|}
+@!max_param_stack:integer;
+ {largest value of |param_ptr|, will be |<=param_size+9|}
+
+@ The input routines must also interact with the processing of
+\.{\\halign} and \.{\\valign}, since the appearance of tab marks and
+\.{\\cr} in certain places is supposed to trigger the beginning of special
+\<v_j> template text in the scanner. This magic is accomplished by an
+|align_state| variable that is increased by~1 when a `\.{\char'173}' is
+scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state|
+is nonzero during the \<u_j> template, after which it is set to zero; the
+\<v_j> template begins when a tab mark or \.{\\cr} occurs at a time that
+|align_state=0|.
+
+@<Glob...@>=
+@!align_state:integer; {group level with respect to current alignment}
+
+@ Thus, the ``current input state'' can be very complicated indeed; there
+can be many levels and each level can arise in a variety of ways. The
+|show_context| procedure, which is used by \TeX's error-reporting routine to
+print out the current input state on all levels down to the most recent
+line of characters from an input file, illustrates most of these conventions.
+The global variable |base_ptr| contains the lowest level that was
+displayed by this procedure.
+
+@<Glob...@>=
+@!base_ptr:0..stack_size; {shallowest level shown by |show_context|}
+
+@ The status at each level is indicated by printing two lines, where the first
+line indicates what was read so far and the second line shows what remains
+to be read. The context is cropped, if necessary, so that the first line
+contains at most |half_error_line| characters, and the second contains
+at most |error_line|. Non-current input levels whose |token_type| is
+`|backed_up|' are shown only if they have not been fully read.
+
+@p procedure show_context; {prints where the scanner is}
+label done;
+var old_setting:0..max_selector; {saved |selector| setting}
+@!nn:integer; {number of contexts shown so far, less one}
+@!bottom_line:boolean; {have we reached the final context to be shown?}
+@<Local variables for formatting calculations@>@/
+begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
+ {store current state}
+nn:=-1; bottom_line:=false;
+loop@+begin cur_input:=input_stack[base_ptr]; {enter into the context}
+ if (state<>token_list) then
+ if (name>17) or (base_ptr=0) then bottom_line:=true;
+ if (base_ptr=input_ptr)or bottom_line or(nn<error_context_lines) then
+ @<Display the current context@>
+ else if nn=error_context_lines then
+ begin print_nl("..."); incr(nn); {omitted if |error_context_lines<0|}
+ end;
+ if bottom_line then goto done;
+ decr(base_ptr);
+ end;
+done: cur_input:=input_stack[input_ptr]; {restore original state}
+end;
+
+@ @<Display the current context@>=
+begin if (base_ptr=input_ptr) or (state<>token_list) or
+ (token_type<>backed_up) or (loc<>null) then
+ {we omit backed-up token lists that have already been read}
+ begin tally:=0; {get ready to count characters}
+ old_setting:=selector;
+ if state<>token_list then
+ begin @<Print location of current line@>;
+ @<Pseudoprint the line@>;
+ end
+ else begin @<Print type of token list@>;
+ @<Pseudoprint the token list@>;
+ end;
+ selector:=old_setting; {stop pseudoprinting}
+ @<Print two lines using the tricky pseudoprinted information@>;
+ incr(nn);
+ end;
+end
+
+@ This routine should be changed, if necessary, to give the best possible
+indication of where the current line resides in the input file.
+For example, on some systems it is best to print both a page and line number.
+@^system dependencies@>
+
+@<Print location of current line@>=
+if name<=17 then
+ if terminal_input then
+ if base_ptr=0 then print_nl("<*>") else print_nl("<insert> ")
+ else begin print_nl("<read ");
+ if name=17 then print_char("*")@+else print_int(name-1);
+@.*\relax@>
+ print_char(">");
+ end
+else begin print_nl("l."); print_int(line);
+ end;
+print_char(" ")
+
+@ @<Print type of token list@>=
+case token_type of
+parameter: print_nl("<argument> ");
+u_template,v_template: print_nl("<template> ");
+backed_up: if loc=null then print_nl("<recently read> ")
+ else print_nl("<to be read again> ");
+inserted: print_nl("<inserted text> ");
+macro: begin print_ln; print_cs(name);
+ end;
+output_text: print_nl("<output> ");
+every_par_text: print_nl("<everypar> ");
+every_math_text: print_nl("<everymath> ");
+every_display_text: print_nl("<everydisplay> ");
+every_hbox_text: print_nl("<everyhbox> ");
+every_vbox_text: print_nl("<everyvbox> ");
+every_job_text: print_nl("<everyjob> ");
+every_cr_text: print_nl("<everycr> ");
+mark_text: print_nl("<mark> ");
+write_text: print_nl("<write> ");
+othercases print_nl("?") {this should never happen}
+endcases
+
+@ Here it is necessary to explain a little trick. We don't want to store a long
+string that corresponds to a token list, because that string might take up
+lots of memory; and we are printing during a time when an error message is
+being given, so we dare not do anything that might overflow one of \TeX's
+tables. So `pseudoprinting' is the answer: We enter a mode of printing
+that stores characters into a buffer of length |error_line|, where character
+$k+1$ is placed into \hbox{|trick_buf[k mod error_line]|} if
+|k<trick_count|, otherwise character |k| is dropped. Initially we set
+|tally:=0| and |trick_count:=1000000|; then when we reach the
+point where transition from line 1 to line 2 should occur, we
+set |first_count:=tally| and |trick_count:=@tmax@>(error_line,
+tally+1+error_line-half_error_line)|. At the end of the
+pseudoprinting, the values of |first_count|, |tally|, and
+|trick_count| give us all the information we need to print the two lines,
+and all of the necessary text is in |trick_buf|.
+
+Namely, let |l| be the length of the descriptive information that appears
+on the first line. The length of the context information gathered for that
+line is |k=first_count|, and the length of the context information
+gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k<=h|,
+where |h=half_error_line|, we print |trick_buf[0..k-1]| after the
+descriptive information on line~1, and set |n:=l+k|; here |n| is the
+length of line~1. If $l+k>h$, some cropping is necessary, so we set |n:=h|
+and print `\.{...}' followed by
+$$\hbox{|trick_buf[(l+k-h+3)..k-1]|,}$$
+where subscripts of |trick_buf| are circular modulo |error_line|. The
+second line consists of |n|~spaces followed by |trick_buf[k..(k+m-1)]|,
+unless |n+m>error_line|; in the latter case, further cropping is done.
+This is easier to program than to explain.
+
+@<Local variables for formatting...@>=
+@!i:0..buf_size; {index into |buffer|}
+@!j:0..buf_size; {end of current line in |buffer|}
+@!l:0..half_error_line; {length of descriptive information on line 1}
+@!m:integer; {context information gathered for line 2}
+@!n:0..error_line; {length of line 1}
+@!p: integer; {starting or ending place in |trick_buf|}
+@!q: integer; {temporary index}
+
+@ The following code sets up the print routines so that they will gather
+the desired information.
+
+@d begin_pseudoprint==
+ begin l:=tally; tally:=0; selector:=pseudo;
+ trick_count:=1000000;
+ end
+@d set_trick_count==
+ begin first_count:=tally;
+ trick_count:=tally+1+error_line-half_error_line;
+ if trick_count<error_line then trick_count:=error_line;
+ end
+
+@ And the following code uses the information after it has been gathered.
+
+@<Print two lines using the tricky pseudoprinted information@>=
+if trick_count=1000000 then set_trick_count;
+ {|set_trick_count| must be performed}
+if tally<trick_count then m:=tally-first_count
+else m:=trick_count-first_count; {context on line 2}
+if l+first_count<=half_error_line then
+ begin p:=0; n:=l+first_count;
+ end
+else begin print("..."); p:=l+first_count-half_error_line+3;
+ n:=half_error_line;
+ end;
+for q:=p to first_count-1 do print_char(trick_buf[q mod error_line]);
+print_ln;
+for q:=1 to n do print_char(" "); {print |n| spaces to begin line~2}
+if m+n<=error_line then p:=first_count+m else p:=first_count+(error_line-n-3);
+for q:=first_count to p-1 do print_char(trick_buf[q mod error_line]);
+if m+n>error_line then print("...")
+
+@ But the trick is distracting us from our current goal, which is to
+understand the input state. So let's concentrate on the data structures that
+are being pseudoprinted as we finish up the |show_context| procedure.
+
+@<Pseudoprint the line@>=
+begin_pseudoprint;
+if buffer[limit]=end_line_char then j:=limit
+else j:=limit+1; {determine the effective end of the line}
+if j>0 then for i:=start to j-1 do
+ begin if i=loc then set_trick_count;
+ print(buffer[i]);
+ end
+
+@ @<Pseudoprint the token list@>=
+begin_pseudoprint;
+if token_type<macro then show_token_list(start,loc,100000)
+else show_token_list(link(start),loc,100000) {avoid reference count}
+
+@ Here is the missing piece of |show_token_list| that is activated when the
+token beginning line~2 is about to be shown:
+
+@<Do magic computation@>=set_trick_count
+
+@* \[23] Maintaining the input stacks.
+The following subroutines change the input status in commonly needed ways.
+
+First comes |push_input|, which stores the current state and creates a
+new level (having, initially, the same properties as the old).
+
+@d push_input==@t@> {enter a new input level, save the old}
+ begin if input_ptr>max_in_stack then
+ begin max_in_stack:=input_ptr;
+ if input_ptr=stack_size then overflow("input stack size",stack_size);
+@:TeX capacity exceeded input stack size}{\quad input stack size@>
+ end;
+ input_stack[input_ptr]:=cur_input; {stack the record}
+ incr(input_ptr);
+ end
+
+@ And of course what goes up must come down.
+
+@d pop_input==@t@> {leave an input level, re-enter the old}
+ begin decr(input_ptr); cur_input:=input_stack[input_ptr];
+ end
+
+@ Here is a procedure that starts a new level of token-list input, given
+a token list |p| and its type |t|. If |t=macro|, the calling routine should
+set |name| and |loc|.
+
+@d back_list(#)==begin_token_list(#,backed_up) {backs up a simple token list}
+@d ins_list(#)==begin_token_list(#,inserted) {inserts a simple token list}
+
+@p procedure begin_token_list(@!p:pointer;@!t:quarterword);
+begin push_input; state:=token_list; start:=p; token_type:=t;
+if t>=macro then {the token list starts with a reference count}
+ begin add_token_ref(p);
+ if t=macro then param_start:=param_ptr
+ else begin loc:=link(p);
+ if tracing_macros>1 then
+ begin begin_diagnostic; print_nl("");
+ case t of
+ mark_text:print_esc("mark");
+ write_text:print_esc("write");
+ othercases print_cmd_chr(assign_toks,t-output_text+output_routine_loc)
+ endcases;@/
+ print("->"); token_show(p); end_diagnostic(false);
+ end;
+ end;
+ end
+else loc:=p;
+end;
+
+@ When a token list has been fully scanned, the following computations
+should be done as we leave that level of input. The |token_type| tends
+to be equal to either |backed_up| or |inserted| about 2/3 of the time.
+@^inner loop@>
+
+@p procedure end_token_list; {leave a token-list input level}
+begin if token_type>=backed_up then {token list to be deleted}
+ begin if token_type<=inserted then flush_list(start)
+ else begin delete_token_ref(start); {update reference count}
+ if token_type=macro then {parameters must be flushed}
+ while param_ptr>param_start do
+ begin decr(param_ptr);
+ flush_list(param_stack[param_ptr]);
+ end;
+ end;
+ end
+else if token_type=u_template then
+ if align_state>500000 then align_state:=0
+ else fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+pop_input;
+check_interrupt;
+end;
+
+@ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has
+seen. The |back_input| procedure takes care of this by putting the token
+just scanned back into the input stream, ready to be read again. This
+procedure can be used only if |cur_tok| represents the token to be
+replaced. Some applications of \TeX\ use this procedure a lot,
+so it has been slightly optimized for speed.
+@^inner loop@>
+
+@p procedure back_input; {undoes one token of input}
+var p:pointer; {a token list of length one}
+begin while (state=token_list)and(loc=null)and(token_type<>v_template) do
+ end_token_list; {conserve stack space}
+p:=get_avail; info(p):=cur_tok;
+if cur_tok<right_brace_limit then
+ if cur_tok<left_brace_limit then decr(align_state)
+ else incr(align_state);
+push_input; state:=token_list; start:=p; token_type:=backed_up;
+loc:=p; {that was |back_list(p)|, without procedure overhead}
+end;
+
+@ @<Insert token |p| into \TeX's input@>=
+begin t:=cur_tok; cur_tok:=p; back_input; cur_tok:=t;
+end
+
+@ The |back_error| routine is used when we want to replace an offending token
+just before issuing an error message. This routine, like |back_input|,
+requires that |cur_tok| has been set. We disable interrupts during the
+call of |back_input| so that the help message won't be lost.
+
+@p procedure back_error; {back up one token and call |error|}
+begin OK_to_interrupt:=false; back_input; OK_to_interrupt:=true; error;
+end;
+@#
+procedure ins_error; {back up one inserted token and call |error|}
+begin OK_to_interrupt:=false; back_input; token_type:=inserted;
+OK_to_interrupt:=true; error;
+end;
+
+@ The |begin_file_reading| procedure starts a new level of input for lines
+of characters to be read from a file, or as an insertion from the
+terminal. It does not take care of opening the file, nor does it set |loc|
+or |limit| or |line|.
+@^system dependencies@>
+
+@p procedure begin_file_reading;
+begin if in_open=max_in_open then overflow("text input levels",max_in_open);
+@:TeX capacity exceeded text input levels}{\quad text input levels@>
+if first=buf_size then overflow("buffer size",buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+incr(in_open); push_input; index:=in_open;
+line_stack[index]:=line; start:=first; state:=mid_line;
+name:=0; {|terminal_input| is now |true|}
+end;
+
+@ Conversely, the variables must be downdated when such a level of input
+is finished:
+
+@p procedure end_file_reading;
+begin first:=start; line:=line_stack[index];
+if name>17 then a_close(cur_file); {forget it}
+pop_input; decr(in_open);
+end;
+
+@ In order to keep the stack from overflowing during a long sequence of
+inserted `\.{\\show}' commands, the following routine removes completed
+error-inserted lines from memory.
+
+@p procedure clear_for_error_prompt;
+begin while (state<>token_list)and terminal_input and@|
+ (input_ptr>0)and(loc>limit) do end_file_reading;
+print_ln; clear_terminal;
+end;
+
+@ To get \TeX's whole input mechanism going, we perform the following
+actions.
+
+@<Initialize the input routines@>=
+begin input_ptr:=0; max_in_stack:=0;
+in_open:=0; open_parens:=0; max_buf_stack:=0;
+param_ptr:=0; max_param_stack:=0;
+first:=buf_size; repeat buffer[first]:=0; decr(first); until first=0;
+scanner_status:=normal; warning_index:=null; first:=1;
+state:=new_line; start:=1; index:=0; line:=0; name:=0;
+force_eof:=false;
+align_state:=1000000;@/
+if not init_terminal then goto final_end;
+limit:=last; first:=last+1; {|init_terminal| has set |loc| and |last|}
+end
+
+@* \[24] Getting the next token.
+The heart of \TeX's input mechanism is the |get_next| procedure, which
+we shall develop in the next few sections of the program. Perhaps we
+shouldn't actually call it the ``heart,'' however, because it really acts
+as \TeX's eyes and mouth, reading the source files and gobbling them up.
+And it also helps \TeX\ to regurgitate stored token lists that are to be
+processed again.
+@^eyes and mouth@>
+
+The main duty of |get_next| is to input one token and to set |cur_cmd|
+and |cur_chr| to that token's command code and modifier. Furthermore, if
+the input token is a control sequence, the |eqtb| location of that control
+sequence is stored in |cur_cs|; otherwise |cur_cs| is set to zero.
+
+Underlying this simple description is a certain amount of complexity
+because of all the cases that need to be handled.
+However, the inner loop of |get_next| is reasonably short and fast.
+
+When |get_next| is asked to get the next token of a \.{\\read} line,
+it sets |cur_cmd=cur_chr=cur_cs=0| in the case that no more tokens
+appear on that line. (There might not be any tokens at all, if the
+|end_line_char| has |ignore| as its catcode.)
+
+@ The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity
+is needed because a blank line of input is supposed to be exactly equivalent
+to the appearance of \.{\\par}; we must set |cur_cs:=par_loc|
+when detecting a blank line.
+
+@<Glob...@>=
+@!par_loc:pointer; {location of `\.{\\par}' in |eqtb|}
+@!par_token:halfword; {token representing `\.{\\par}'}
+
+@ @<Put each...@>=
+primitive("par",par_end,256); {cf.\ |scan_file_name|}
+@!@:par_}{\.{\\par} primitive@>
+par_loc:=cur_val; par_token:=cs_token_flag+par_loc;
+
+@ @<Cases of |print_cmd_chr|...@>=
+par_end:print_esc("par");
+
+@ Before getting into |get_next|, let's consider the subroutine that
+is called when an `\.{\\outer}' control sequence has been scanned or
+when the end of a file has been reached. These two cases are distinguished
+by |cur_cs|, which is zero at the end of a file.
+
+@p procedure check_outer_validity;
+var p:pointer; {points to inserted token list}
+@!q:pointer; {auxiliary pointer}
+begin if scanner_status<>normal then
+ begin deletions_allowed:=false;
+ @<Back up an outer control sequence so that it can be reread@>;
+ if scanner_status>skipping then
+ @<Tell the user what has run away and try to recover@>
+ else begin print_err("Incomplete "); print_cmd_chr(if_test,cur_if);
+@.Incomplete \\if...@>
+ print("; all text was ignored after line "); print_int(skip_line);
+ help3("A forbidden control sequence occurred in skipped text.")@/
+ ("This kind of error happens when you say `\if...' and forget")@/
+ ("the matching `\fi'. I've inserted a `\fi'; this might work.");
+ if cur_cs<>0 then cur_cs:=0
+ else help_line[2]:=@|
+ "The file ended while I was skipping conditional text.";
+ cur_tok:=cs_token_flag+frozen_fi; ins_error;
+ end;
+ deletions_allowed:=true;
+ end;
+end;
+
+@ An outer control sequence that occurs in a \.{\\read} will not be reread,
+since the error recovery for \.{\\read} is not very powerful.
+
+@<Back up an outer control sequence so that it can be reread@>=
+if cur_cs<>0 then
+ begin if (state=token_list)or(name<1)or(name>17) then
+ begin p:=get_avail; info(p):=cs_token_flag+cur_cs;
+ back_list(p); {prepare to read the control sequence again}
+ end;
+ cur_cmd:=spacer; cur_chr:=" "; {replace it by a space}
+ end
+
+@ @<Tell the user what has run away...@>=
+begin runaway; {print a definition, argument, or preamble}
+if cur_cs=0 then print_err("File ended")
+@.File ended while scanning...@>
+else begin cur_cs:=0; print_err("Forbidden control sequence found");
+@.Forbidden control sequence...@>
+ end;
+print(" while scanning ");
+@<Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or `\.{text}',
+ and insert tokens that should lead to recovery@>;
+print(" of "); sprint_cs(warning_index);
+help4("I suspect you have forgotten a `}', causing me")@/
+("to read past where you wanted me to stop.")@/
+("I'll try to recover; but if the error is serious,")@/
+("you'd better type `E' or `X' now and fix your file.");@/
+error;
+end
+
+@ The recovery procedure can't be fully understood without knowing more
+about the \TeX\ routines that should be aborted, but we can sketch the
+ideas here: For a runaway definition we will insert a right brace; for a
+runaway preamble, we will insert a special \.{\\cr} token and a right
+brace; and for a runaway argument, we will set |long_state| to
+|outer_call| and insert \.{\\par}.
+
+@<Print either `\.{definition}' or ...@>=
+p:=get_avail;
+case scanner_status of
+defining:begin print("definition"); info(p):=right_brace_token+"}";
+ end;
+matching:begin print("use"); info(p):=par_token; long_state:=outer_call;
+ end;
+aligning:begin print("preamble"); info(p):=right_brace_token+"}"; q:=p;
+ p:=get_avail; link(p):=q; info(p):=cs_token_flag+frozen_cr;
+ align_state:=-1000000;
+ end;
+absorbing:begin print("text"); info(p):=right_brace_token+"}";
+ end;
+end; {there are no other cases}
+ins_list(p)
+
+@ We need to mention a procedure here that may be called by |get_next|.
+
+@p procedure@?firm_up_the_line; forward;
+
+@ Now we're ready to take the plunge into |get_next| itself. Parts of
+this routine are executed more often than any other instructions of \TeX.
+@^mastication@>@^inner loop@>
+
+@d switch=25 {a label in |get_next|}
+@d start_cs=26 {another}
+
+@p procedure get_next; {sets |cur_cmd|, |cur_chr|, |cur_cs| to next token}
+label restart, {go here to get the next input token}
+ switch, {go here to eat the next character from a file}
+ reswitch, {go here to digest it again}
+ start_cs, {go here to start looking for a control sequence}
+ found, {go here when a control sequence has been found}
+ exit; {go here when the next input token has been got}
+var k:0..buf_size; {an index into |buffer|}
+@!t:halfword; {a token}
+@!cat:0..max_char_code; {|cat_code(cur_chr)|, usually}
+@!c,@!cc:ASCII_code; {constituents of a possible expanded code}
+@!d:2..3; {number of excess characters in an expanded code}
+begin restart: cur_cs:=0;
+if state<>token_list then
+@<Input from external file, |goto restart| if no input found@>
+else @<Input from token list, |goto restart| if end of list or
+ if a parameter needs to be expanded@>;
+@<If an alignment entry has just ended, take appropriate action@>;
+exit:end;
+
+@ An alignment entry ends when a tab or \.{\\cr} occurs, provided that the
+current level of braces is the same as the level that was present at the
+beginning of that alignment entry; i.e., provided that |align_state| has
+returned to the value it had after the \<u_j> template for that entry.
+@^inner loop@>
+
+@<If an alignment entry has just ended, take appropriate action@>=
+if cur_cmd<=car_ret then if cur_cmd>=tab_mark then if align_state=0 then
+ @<Insert the \(v)\<v_j> template and |goto restart|@>
+
+@ @<Input from external file, |goto restart| if no input found@>=
+@^inner loop@>
+begin switch: if loc<=limit then {current line not yet finished}
+ begin cur_chr:=buffer[loc]; incr(loc);
+ reswitch: cur_cmd:=cat_code(cur_chr);
+ @<Change state if necessary, and |goto switch| if the
+ current character should be ignored,
+ or |goto reswitch| if the current character
+ changes to another@>;
+ end
+else begin state:=new_line;@/
+ @<Move to next line of file,
+ or |goto restart| if there is no next line,
+ or |return| if a \.{\\read} line has finished@>;
+ check_interrupt;
+ goto switch;
+ end;
+end
+
+@ The following 48-way switch accomplishes the scanning quickly, assuming
+that a decent \PASCAL\ compiler has translated the code. Note that the numeric
+values for |mid_line|, |skip_blanks|, and |new_line| are spaced
+apart from each other by |max_char_code+1|, so we can add a character's
+command code to the state to get a single number that characterizes both.
+
+@d any_state_plus(#) == mid_line+#,skip_blanks+#,new_line+#
+
+@<Change state if necessary...@>=
+case state+cur_cmd of
+@<Cases where character is ignored@>: goto switch;
+any_state_plus(escape): @<Scan a control sequence
+ and set |state:=skip_blanks| or |mid_line|@>;
+any_state_plus(active_char): @<Process an active-character control sequence
+ and set |state:=mid_line|@>;
+any_state_plus(sup_mark): @<If this |sup_mark| starts an expanded character
+ like~\.{\^\^A} or~\.{\^\^df}, then |goto reswitch|,
+ otherwise set |state:=mid_line|@>;
+any_state_plus(invalid_char): @<Decry the invalid character and
+ |goto restart|@>;
+@t\4@>@<Handle situations involving spaces, braces, changes of state@>@;
+othercases do_nothing
+endcases
+
+@ @<Cases where character is ignored@>=
+any_state_plus(ignore),skip_blanks+spacer,new_line+spacer
+
+@ We go to |restart| instead of to |switch|, because |state| might equal
+|token_list| after the error has been dealt with
+(cf.\ |clear_for_error_prompt|).
+
+@<Decry the invalid...@>=
+begin print_err("Text line contains an invalid character");
+@.Text line contains...@>
+help2("A funny symbol that I can't read has just been input.")@/
+("Continue, and I'll forget that it ever happened.");@/
+deletions_allowed:=false; error; deletions_allowed:=true;
+goto restart;
+end
+
+@ @d add_delims_to(#)==#+math_shift,#+tab_mark,#+mac_param,
+ #+sub_mark,#+letter,#+other_char
+
+@<Handle situations involving spaces, braces, changes of state@>=
+mid_line+spacer:@<Enter |skip_blanks| state, emit a space@>;
+mid_line+car_ret:@<Finish line, emit a space@>;
+skip_blanks+car_ret,any_state_plus(comment):
+ @<Finish line, |goto switch|@>;
+new_line+car_ret:@<Finish line, emit a \.{\\par}@>;
+mid_line+left_brace: incr(align_state);
+skip_blanks+left_brace,new_line+left_brace: begin
+ state:=mid_line; incr(align_state);
+ end;
+mid_line+right_brace: decr(align_state);
+skip_blanks+right_brace,new_line+right_brace: begin
+ state:=mid_line; decr(align_state);
+ end;
+add_delims_to(skip_blanks),add_delims_to(new_line): state:=mid_line;
+
+@ When a character of type |spacer| gets through, its character code is
+changed to $\.{"\ "}=@'40$. This means that the ASCII codes for tab and space,
+and for the space inserted at the end of a line, will
+be treated alike when macro parameters are being matched. We do this
+since such characters are indistinguishable on most computer terminal displays.
+
+@<Finish line, emit a space@>=
+begin loc:=limit+1; cur_cmd:=spacer; cur_chr:=" ";
+end
+
+@ The following code is performed only when |cur_cmd=spacer|.
+
+@<Enter |skip_blanks| state, emit a space@>=
+begin state:=skip_blanks; cur_chr:=" ";
+end
+
+@ @<Finish line, |goto switch|@>=
+begin loc:=limit+1; goto switch;
+end
+
+@ @<Finish line, emit a \.{\\par}@>=
+begin loc:=limit+1; cur_cs:=par_loc; cur_cmd:=eq_type(cur_cs);
+cur_chr:=equiv(cur_cs);
+if cur_cmd>=outer_call then check_outer_validity;
+end
+
+@ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
+
+@d is_hex(#)==(((#>="0")and(#<="9"))or((#>="a")and(#<="f")))
+@d hex_to_cur_chr==
+ if c<="9" then cur_chr:=c-"0" @+else cur_chr:=c-"a"+10;
+ if cc<="9" then cur_chr:=16*cur_chr+cc-"0"
+ else cur_chr:=16*cur_chr+cc-"a"+10
+
+@<If this |sup_mark| starts an expanded character...@>=
+begin if cur_chr=buffer[loc] then if loc<limit then
+ begin c:=buffer[loc+1]; @+if c<@'200 then {yes we have an expanded char}
+ begin loc:=loc+2;
+ if is_hex(c) then if loc<=limit then
+ begin cc:=buffer[loc]; @+if is_hex(cc) then
+ begin incr(loc); hex_to_cur_chr; goto reswitch;
+ end;
+ end;
+ if c<@'100 then cur_chr:=c+@'100 @+else cur_chr:=c-@'100;
+ goto reswitch;
+ end;
+ end;
+state:=mid_line;
+end
+
+@ @<Process an active-character...@>=
+begin cur_cs:=cur_chr+active_base;
+cur_cmd:=eq_type(cur_cs); cur_chr:=equiv(cur_cs); state:=mid_line;
+if cur_cmd>=outer_call then check_outer_validity;
+end
+
+@ Control sequence names are scanned only when they appear in some line of
+a file; once they have been scanned the first time, their |eqtb| location
+serves as a unique identification, so \TeX\ doesn't need to refer to the
+original name any more except when it prints the equivalent in symbolic form.
+
+The program that scans a control sequence has been written carefully
+in order to avoid the blowups that might otherwise occur if a malicious
+user tried something like `\.{\\catcode\'15=0}'. The algorithm might
+look at |buffer[limit+1]|, but it never looks at |buffer[limit+2]|.
+
+If expanded characters like `\.{\^\^A}' or `\.{\^\^df}'
+appear in or just following
+a control sequence name, they are converted to single characters in the
+buffer and the process is repeated, slowly but surely.
+
+@<Scan a control...@>=
+begin if loc>limit then cur_cs:=null_cs {|state| is irrelevant in this case}
+else begin start_cs: k:=loc; cur_chr:=buffer[k]; cat:=cat_code(cur_chr);
+ incr(k);
+ if cat=letter then state:=skip_blanks
+ else if cat=spacer then state:=skip_blanks
+ else state:=mid_line;
+ if (cat=letter)and(k<=limit) then
+ @<Scan ahead in the buffer until finding a nonletter;
+ if an expanded code is encountered, reduce it
+ and |goto start_cs|; otherwise if a multiletter control
+ sequence is found, adjust |cur_cs| and |loc|, and
+ |goto found|@>
+ else @<If an expanded code is present, reduce it and |goto start_cs|@>;
+ cur_cs:=single_base+buffer[loc]; incr(loc);
+ end;
+found: cur_cmd:=eq_type(cur_cs); cur_chr:=equiv(cur_cs);
+if cur_cmd>=outer_call then check_outer_validity;
+end
+
+@ Whenever we reach the following piece of code, we will have
+|cur_chr=buffer[k-1]| and |k<=limit+1| and |cat=cat_code(cur_chr)|. If an
+expanded code like \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)..(k+1)]|
+or |buffer[(k-1)..(k+2)]|, we
+will store the corresponding code in |buffer[k-1]| and shift the rest of
+the buffer left two or three places.
+
+@<If an expanded...@>=
+begin if buffer[k]=cur_chr then @+if cat=sup_mark then @+if k<limit then
+ begin c:=buffer[k+1]; @+if c<@'200 then {yes, one is indeed present}
+ begin d:=2;
+ if is_hex(c) then @+if k+2<=limit then
+ begin cc:=buffer[k+2]; @+if is_hex(cc) then incr(d);
+ end;
+ if d>2 then
+ begin hex_to_cur_chr; buffer[k-1]:=cur_chr;
+ end
+ else if c<@'100 then buffer[k-1]:=c+@'100
+ else buffer[k-1]:=c-@'100;
+ limit:=limit-d; first:=first-d;
+ while k<=limit do
+ begin buffer[k]:=buffer[k+d]; incr(k);
+ end;
+ goto start_cs;
+ end;
+ end;
+end
+
+@ @<Scan ahead in the buffer...@>=
+begin repeat cur_chr:=buffer[k]; cat:=cat_code(cur_chr); incr(k);
+until (cat<>letter)or(k>limit);
+@<If an expanded...@>;
+if cat<>letter then decr(k);
+ {now |k| points to first nonletter}
+if k>loc+1 then {multiletter control sequence has been scanned}
+ begin cur_cs:=id_lookup(loc,k-loc); loc:=k; goto found;
+ end;
+end
+
+@ Let's consider now what happens when |get_next| is looking at a token list.
+
+@<Input from token list, |goto restart| if end of list or
+ if a parameter needs to be expanded@>=
+if loc<>null then {list not exhausted}
+@^inner loop@>
+ begin t:=info(loc); loc:=link(loc); {move to next}
+ if t>=cs_token_flag then {a control sequence token}
+ begin cur_cs:=t-cs_token_flag;
+ cur_cmd:=eq_type(cur_cs); cur_chr:=equiv(cur_cs);
+ if cur_cmd>=outer_call then
+ if cur_cmd=dont_expand then
+ @<Get the next token, suppressing expansion@>
+ else check_outer_validity;
+ end
+ else begin cur_cmd:=t div @'400; cur_chr:=t mod @'400;
+ case cur_cmd of
+ left_brace: incr(align_state);
+ right_brace: decr(align_state);
+ out_param: @<Insert macro parameter and |goto restart|@>;
+ othercases do_nothing
+ endcases;
+ end;
+ end
+else begin {we are done with this token list}
+ end_token_list; goto restart; {resume previous level}
+ end
+
+@ The present point in the program is reached only when the |expand|
+routine has inserted a special marker into the input. In this special
+case, |info(loc)| is known to be a control sequence token, and |link(loc)=null|.
+
+@d no_expand_flag=257 {this characterizes a special variant of |relax|}
+
+@<Get the next token, suppressing expansion@>=
+begin cur_cs:=info(loc)-cs_token_flag; loc:=null;@/
+cur_cmd:=eq_type(cur_cs); cur_chr:=equiv(cur_cs);
+if cur_cmd>max_command then
+ begin cur_cmd:=relax; cur_chr:=no_expand_flag;
+ end;
+end
+
+@ @<Insert macro parameter...@>=
+begin begin_token_list(param_stack[param_start+cur_chr-1],parameter);
+goto restart;
+end
+
+@ All of the easy branches of |get_next| have now been taken care of.
+There is one more branch.
+
+@d end_line_char_inactive == (end_line_char<0)or(end_line_char>255)
+
+@<Move to next line of file, or |goto restart|...@>=
+if name>17 then @<Read next line of file into |buffer|, or
+ |goto restart| if the file has ended@>
+else begin if not terminal_input then {\.{\\read} line has ended}
+ begin cur_cmd:=0; cur_chr:=0; return;
+ end;
+ if input_ptr>0 then {text was inserted during error recovery}
+ begin end_file_reading; goto restart; {resume previous level}
+ end;
+ if selector<log_only then open_log_file;
+ if interaction>nonstop_mode then
+ begin if end_line_char_inactive then incr(limit);
+ if limit=start then {previous line was empty}
+ print_nl("(Please type a command or say `\end')");
+@.Please type...@>
+ print_ln; first:=start;
+ prompt_input("*"); {input on-line into |buffer|}
+@.*\relax@>
+ limit:=last;
+ if end_line_char_inactive then decr(limit)
+ else buffer[limit]:=end_line_char;
+ first:=limit+1;
+ loc:=start;
+ end
+ else fatal_error("*** (job aborted, no legal \end found)");
+@.job aborted@>
+ {nonstop mode, which is intended for overnight batch processing,
+ never waits for on-line input}
+ end
+
+@ The global variable |force_eof| is normally |false|; it is set |true|
+by an \.{\\endinput} command.
+
+@<Glob...@>=
+@!force_eof:boolean; {should the next \.{\\input} be aborted early?}
+
+@ @<Read next line of file into |buffer|, or
+ |goto restart| if the file has ended@>=
+begin incr(line); first:=start;
+if not force_eof then
+ begin if input_ln(cur_file,true) then {not end of file}
+ firm_up_the_line {this sets |limit|}
+ else force_eof:=true;
+ end;
+if force_eof then
+ begin print_char(")"); decr(open_parens);
+ update_terminal; {show user that file has been read}
+ force_eof:=false;
+ end_file_reading; {resume previous level}
+ check_outer_validity; goto restart;
+ end;
+if end_line_char_inactive then decr(limit)
+else buffer[limit]:=end_line_char;
+first:=limit+1; loc:=start; {ready to read}
+end
+
+@ If the user has set the |pausing| parameter to some positive value,
+and if nonstop mode has not been selected, each line of input is displayed
+on the terminal and the transcript file, followed by `\.{=>}'.
+\TeX\ waits for a response. If the response is simply |carriage_return|, the
+line is accepted as it stands, otherwise the line typed is
+used instead of the line in the file.
+
+@p procedure firm_up_the_line;
+var k:0..buf_size; {an index into |buffer|}
+begin limit:=last;
+if pausing>0 then if interaction>nonstop_mode then
+ begin wake_up_terminal; print_ln;
+ if start<limit then for k:=start to limit-1 do print(buffer[k]);
+ first:=limit; prompt_input("=>"); {wait for user response}
+@.=>@>
+ if last>first then
+ begin for k:=first to last-1 do {move line down in buffer}
+ buffer[k+start-first]:=buffer[k];
+ limit:=start+last-first;
+ end;
+ end;
+end;
+
+@ Since |get_next| is used so frequently in \TeX, it is convenient
+to define three related procedures that do a little more:
+
+\yskip\hang|get_token| not only sets |cur_cmd| and |cur_chr|, it
+also sets |cur_tok|, a packed halfword version of the current token.
+
+\yskip\hang|get_x_token|, meaning ``get an expanded token,'' is like
+|get_token|, but if the current token turns out to be a user-defined
+control sequence (i.e., a macro call), or a conditional,
+or something like \.{\\topmark} or \.{\\expandafter} or \.{\\csname},
+it is eliminated from the input by beginning the expansion of the macro
+or the evaluation of the conditional.
+
+\yskip\hang|x_token| is like |get_x_token| except that it assumes that
+|get_next| has already been called.
+
+\yskip\noindent
+In fact, these three procedures account for almost every use of |get_next|.
+
+@ No new control sequences will be defined except during a call of
+|get_token|, or when \.{\\csname} compresses a token list, because
+|no_new_control_sequence| is always |true| at other times.
+
+@p procedure get_token; {sets |cur_cmd|, |cur_chr|, |cur_tok|}
+begin no_new_control_sequence:=false; get_next; no_new_control_sequence:=true;
+@^inner loop@>
+if cur_cs=0 then cur_tok:=(cur_cmd*@'400)+cur_chr
+else cur_tok:=cs_token_flag+cur_cs;
+end;
+
+@* \[25] Expanding the next token.
+Only a dozen or so command codes |>max_command| can possibly be returned by
+|get_next|; in increasing order, they are |undefined_cs|, |expand_after|,
+|no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|,
+|top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and
+|end_template|.{\emergencystretch=40pt\par}
+
+The |expand| subroutine is used when |cur_cmd>max_command|. It removes a
+``call'' or a conditional or one of the other special operations just
+listed. It follows that |expand| might invoke itself recursively. In all
+cases, |expand| destroys the current token, but it sets things up so that
+the next |get_next| will deliver the appropriate next token. The value of
+|cur_tok| need not be known when |expand| is called.
+
+Since several of the basic scanning routines communicate via global variables,
+their values are saved as local variables of |expand| so that
+recursive calls don't invalidate them.
+@^recursion@>
+
+@p@t\4@>@<Declare the procedure called |macro_call|@>@;@/
+@t\4@>@<Declare the procedure called |insert_relax|@>@;@/
+procedure@?pass_text; forward;@t\2@>
+procedure@?start_input; forward;@t\2@>
+procedure@?conditional; forward;@t\2@>
+procedure@?get_x_token; forward;@t\2@>
+procedure@?conv_toks; forward;@t\2@>
+procedure@?ins_the_toks; forward;@t\2@>
+procedure expand;
+var t:halfword; {token that is being ``expanded after''}
+@!p,@!q,@!r:pointer; {for list manipulation}
+@!j:0..buf_size; {index into |buffer|}
+@!cv_backup:integer; {to save the global quantity |cur_val|}
+@!cvl_backup,@!radix_backup,@!co_backup:small_number;
+ {to save |cur_val_level|, etc.}
+@!backup_backup:pointer; {to save |link(backup_head)|}
+@!save_scanner_status:small_number; {temporary storage of |scanner_status|}
+begin cv_backup:=cur_val; cvl_backup:=cur_val_level; radix_backup:=radix;
+co_backup:=cur_order; backup_backup:=link(backup_head);
+if cur_cmd<call then @<Expand a nonmacro@>
+else if cur_cmd<end_template then macro_call
+else @<Insert a token containing |frozen_endv|@>;
+cur_val:=cv_backup; cur_val_level:=cvl_backup; radix:=radix_backup;
+cur_order:=co_backup; link(backup_head):=backup_backup;
+end;
+
+@ @<Expand a nonmacro@>=
+begin if tracing_commands>1 then show_cur_cmd_chr;
+case cur_cmd of
+top_bot_mark:@<Insert the \(a)appropriate mark text into the scanner@>;
+expand_after:@<Expand the token after the next token@>;
+no_expand:@<Suppress expansion of the next token@>;
+cs_name:@<Manufacture a control sequence name@>;
+convert:conv_toks; {this procedure is discussed in Part 27 below}
+the:ins_the_toks; {this procedure is discussed in Part 27 below}
+if_test:conditional; {this procedure is discussed in Part 28 below}
+fi_or_else:@<Terminate the current conditional and skip to \.{\\fi}@>;
+input:@<Initiate or terminate input from a file@>;
+othercases @<Complain about an undefined macro@>
+endcases;
+end
+
+@ It takes only a little shuffling to do what \TeX\ calls \.{\\expandafter}.
+
+@<Expand the token after...@>=
+begin get_token; t:=cur_tok; get_token;
+if cur_cmd>max_command then expand@+else back_input;
+cur_tok:=t; back_input;
+end
+
+@ The implementation of \.{\\noexpand} is a bit trickier, because it is
+necessary to insert a special `|dont_expand|' marker into \TeX's reading
+mechanism. This special marker is processed by |get_next|, but it does
+not slow down the inner loop.
+
+Since \.{\\outer} macros might arise here, we must also
+clear the |scanner_status| temporarily.
+
+@<Suppress expansion...@>=
+begin save_scanner_status:=scanner_status; scanner_status:=normal;
+get_token; scanner_status:=save_scanner_status; t:=cur_tok;
+back_input; {now |start| and |loc| point to the backed-up token |t|}
+if t>=cs_token_flag then
+ begin p:=get_avail; info(p):=cs_token_flag+frozen_dont_expand;
+ link(p):=loc; start:=p; loc:=p;
+ end;
+end
+
+@ @<Complain about an undefined macro@>=
+begin print_err("Undefined control sequence");
+@.Undefined control sequence@>
+help5("The control sequence at the end of the top line")@/
+("of your error message was never \def'ed. If you have")@/
+("misspelled it (e.g., `\hobx'), type `I' and the correct")@/
+("spelling (e.g., `I\hbox'). Otherwise just continue,")@/
+("and I'll forget about whatever was undefined.");
+error;
+end
+
+@ The |expand| procedure and some other routines that construct token
+lists find it convenient to use the following macros, which are valid only if
+the variables |p| and |q| are reserved for token-list building.
+
+@d store_new_token(#)==begin q:=get_avail; link(p):=q; info(q):=#;
+ p:=q; {|link(p)| is |null|}
+ end
+@d fast_store_new_token(#)==begin fast_get_avail(q); link(p):=q; info(q):=#;
+ p:=q; {|link(p)| is |null|}
+ end
+
+@ @<Manufacture a control...@>=
+begin r:=get_avail; p:=r; {head of the list of characters}
+repeat get_x_token;
+if cur_cs=0 then store_new_token(cur_tok);
+until cur_cs<>0;
+if cur_cmd<>end_cs_name then @<Complain about missing \.{\\endcsname}@>;
+@<Look up the characters of list |r| in the hash table, and set |cur_cs|@>;
+flush_list(r);
+if eq_type(cur_cs)=undefined_cs then
+ begin eq_define(cur_cs,relax,256); {N.B.: The |save_stack| might change}
+ end; {the control sequence will now match `\.{\\relax}'}
+cur_tok:=cur_cs+cs_token_flag; back_input;
+end
+
+@ @<Complain about missing \.{\\endcsname}@>=
+begin print_err("Missing "); print_esc("endcsname"); print(" inserted");
+@.Missing \\endcsname...@>
+help2("The control sequence marked <to be read again> should")@/
+ ("not appear between \csname and \endcsname.");
+back_error;
+end
+
+@ @<Look up the characters of list |r| in the hash table...@>=
+j:=first; p:=link(r);
+while p<>null do
+ begin if j>=max_buf_stack then
+ begin max_buf_stack:=j+1;
+ if max_buf_stack=buf_size then
+ overflow("buffer size",buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ end;
+ buffer[j]:=info(p) mod @'400; incr(j); p:=link(p);
+ end;
+if j>first+1 then
+ begin no_new_control_sequence:=false; cur_cs:=id_lookup(first,j-first);
+ no_new_control_sequence:=true;
+ end
+else if j=first then cur_cs:=null_cs {the list is empty}
+else cur_cs:=single_base+buffer[first] {the list has length one}
+
+@ An |end_template| command is effectively changed to an |endv| command
+by the following code. (The reason for this is discussed below; the
+|frozen_end_template| at the end of the template has passed the
+|check_outer_validity| test, so its mission of error detection has been
+accomplished.)
+
+@<Insert a token containing |frozen_endv|@>=
+begin cur_tok:=cs_token_flag+frozen_endv; back_input;
+end
+
+@ The processing of \.{\\input} involves the |start_input| subroutine,
+which will be declared later; the processing of \.{\\endinput} is trivial.
+
+@<Put each...@>=
+primitive("input",input,0);@/
+@!@:input_}{\.{\\input} primitive@>
+primitive("endinput",input,1);@/
+@!@:end_input_}{\.{\\endinput} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+input: if chr_code=0 then print_esc("input")@+else print_esc("endinput");
+
+@ @<Initiate or terminate input...@>=
+if cur_chr>0 then force_eof:=true
+else if name_in_progress then insert_relax
+else start_input
+
+@ Sometimes the expansion looks too far ahead, so we want to insert
+a harmless \.{\\relax} into the user's input.
+
+@<Declare the procedure called |insert_relax|@>=
+procedure insert_relax;
+begin cur_tok:=cs_token_flag+cur_cs; back_input;
+cur_tok:=cs_token_flag+frozen_relax; back_input; token_type:=inserted;
+end;
+
+@ Here is a recursive procedure that is \TeX's usual way to get the
+next token of input. It has been slightly optimized to take account of
+common cases.
+
+@p procedure get_x_token; {sets |cur_cmd|, |cur_chr|, |cur_tok|,
+ and expands macros}
+label restart,done;
+begin restart: get_next;
+@^inner loop@>
+if cur_cmd<=max_command then goto done;
+if cur_cmd>=call then
+ if cur_cmd<end_template then macro_call
+ else begin cur_cs:=frozen_endv; cur_cmd:=endv;
+ goto done; {|cur_chr=null_list|}
+ end
+else expand;
+goto restart;
+done: if cur_cs=0 then cur_tok:=(cur_cmd*@'400)+cur_chr
+else cur_tok:=cs_token_flag+cur_cs;
+end;
+
+@ The |get_x_token| procedure is equivalent to two consecutive
+procedure calls: |get_next; x_token|.
+
+@p procedure x_token; {|get_x_token| without the initial |get_next|}
+begin while cur_cmd>max_command do
+ begin expand;
+ get_next;
+ end;
+if cur_cs=0 then cur_tok:=(cur_cmd*@'400)+cur_chr
+else cur_tok:=cs_token_flag+cur_cs;
+end;
+
+@ A control sequence that has been \.{\\def}'ed by the user is expanded by
+\TeX's |macro_call| procedure.
+
+Before we get into the details of |macro_call|, however, let's consider the
+treatment of primitives like \.{\\topmark}, since they are essentially
+macros without parameters. The token lists for such marks are kept in a
+global array of five pointers; we refer to the individual entries of this
+array by symbolic names |top_mark|, etc. The value of |top_mark| is either
+|null| or a pointer to the reference count of a token list.
+
+@d top_mark_code=0 {the mark in effect at the previous page break}
+@d first_mark_code=1 {the first mark between |top_mark| and |bot_mark|}
+@d bot_mark_code=2 {the mark in effect at the current page break}
+@d split_first_mark_code=3 {the first mark found by \.{\\vsplit}}
+@d split_bot_mark_code=4 {the last mark found by \.{\\vsplit}}
+@d top_mark==cur_mark[top_mark_code]
+@d first_mark==cur_mark[first_mark_code]
+@d bot_mark==cur_mark[bot_mark_code]
+@d split_first_mark==cur_mark[split_first_mark_code]
+@d split_bot_mark==cur_mark[split_bot_mark_code]
+
+@<Glob...@>=
+@!cur_mark:array[top_mark_code..split_bot_mark_code] of pointer;
+ {token lists for marks}
+
+@ @<Set init...@>=
+top_mark:=null; first_mark:=null; bot_mark:=null;
+split_first_mark:=null; split_bot_mark:=null;
+
+@ @<Put each...@>=
+primitive("topmark",top_bot_mark,top_mark_code);
+@!@:top_mark_}{\.{\\topmark} primitive@>
+primitive("firstmark",top_bot_mark,first_mark_code);
+@!@:first_mark_}{\.{\\firstmark} primitive@>
+primitive("botmark",top_bot_mark,bot_mark_code);
+@!@:bot_mark_}{\.{\\botmark} primitive@>
+primitive("splitfirstmark",top_bot_mark,split_first_mark_code);
+@!@:split_first_mark_}{\.{\\splitfirstmark} primitive@>
+primitive("splitbotmark",top_bot_mark,split_bot_mark_code);
+@!@:split_bot_mark_}{\.{\\splitbotmark} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+top_bot_mark: case chr_code of
+ first_mark_code: print_esc("firstmark");
+ bot_mark_code: print_esc("botmark");
+ split_first_mark_code: print_esc("splitfirstmark");
+ split_bot_mark_code: print_esc("splitbotmark");
+ othercases print_esc("topmark")
+ endcases;
+
+@ The following code is activated when |cur_cmd=top_bot_mark| and
+when |cur_chr| is a code like |top_mark_code|.
+
+@<Insert the \(a)appropriate mark text into the scanner@>=
+begin if cur_mark[cur_chr]<>null then
+ begin_token_list(cur_mark[cur_chr],mark_text);
+end
+
+@ Now let's consider |macro_call| itself, which is invoked when \TeX\ is
+scanning a control sequence whose |cur_cmd| is either |call|, |long_call|,
+|outer_call|, or |long_outer_call|. The control sequence definition
+appears in the token list whose reference count is in location |cur_chr|
+of |mem|.
+
+The global variable |long_state| will be set to |call| or to |long_call|,
+depending on whether or not the control sequence disallows \.{\\par}
+in its parameters. The |get_next| routine will set |long_state| to
+|outer_call| and emit \.{\\par}, if a file ends or if an \.{\\outer}
+control sequence occurs in the midst of an argument.
+
+@<Glob...@>=
+@!long_state:call..long_outer_call; {governs the acceptance of \.{\\par}}
+
+@ The parameters, if any, must be scanned before the macro is expanded.
+Parameters are token lists without reference counts. They are placed on
+an auxiliary stack called |pstack| while they are being scanned, since
+the |param_stack| may be losing entries during the matching process.
+(Note that |param_stack| can't be gaining entries, since |macro_call| is
+the only routine that puts anything onto |param_stack|, and it
+is not recursive.)
+
+@<Glob...@>=
+@!pstack:array[0..8] of pointer; {arguments supplied to a macro}
+
+@ After parameter scanning is complete, the parameters are moved to the
+|param_stack|. Then the macro body is fed to the scanner; in other words,
+|macro_call| places the defined text of the control sequence at the
+top of\/ \TeX's input stack, so that |get_next| will proceed to read it
+next.
+
+The global variable |cur_cs| contains the |eqtb| address of the control sequence
+being expanded, when |macro_call| begins. If this control sequence has not been
+declared \.{\\long}, i.e., if its command code in the |eq_type| field is
+not |long_call| or |long_outer_call|, its parameters are not allowed to contain
+the control sequence \.{\\par}. If an illegal \.{\\par} appears, the macro
+call is aborted, and the \.{\\par} will be rescanned.
+
+@<Declare the procedure called |macro_call|@>=
+procedure macro_call; {invokes a user-defined control sequence}
+label exit, continue, done, done1, found;
+var r:pointer; {current node in the macro's token list}
+@!p:pointer; {current node in parameter token list being built}
+@!q:pointer; {new node being put into the token list}
+@!s:pointer; {backup pointer for parameter matching}
+@!t:pointer; {cycle pointer for backup recovery}
+@!u,@!v:pointer; {auxiliary pointers for backup recovery}
+@!rbrace_ptr:pointer; {one step before the last |right_brace| token}
+@!n:small_number; {the number of parameters scanned}
+@!unbalance:halfword; {unmatched left braces in current parameter}
+@!m:halfword; {the number of tokens or groups (usually)}
+@!ref_count:pointer; {start of the token list}
+@!save_scanner_status:small_number; {|scanner_status| upon entry}
+@!save_warning_index:pointer; {|warning_index| upon entry}
+@!match_chr:ASCII_code; {character used in parameter}
+begin save_scanner_status:=scanner_status; save_warning_index:=warning_index;
+warning_index:=cur_cs; ref_count:=cur_chr; r:=link(ref_count); n:=0;
+if tracing_macros>0 then @<Show the text of the macro being expanded@>;
+if info(r)<>end_match_token then
+ @<Scan the parameters and make |link(r)| point to the macro body; but
+ |return| if an illegal \.{\\par} is detected@>;
+@<Feed the macro body and its parameters to the scanner@>;
+exit:scanner_status:=save_scanner_status; warning_index:=save_warning_index;
+end;
+
+@ Before we put a new token list on the input stack, it is wise to clean off
+all token lists that have recently been depleted. Then a user macro that ends
+with a call to itself will not require unbounded stack space.
+
+@<Feed the macro body and its parameters to the scanner@>=
+while (state=token_list)and(loc=null)and(token_type<>v_template) do
+ end_token_list; {conserve stack space}
+begin_token_list(ref_count,macro); name:=warning_index; loc:=link(r);
+if n>0 then
+ begin if param_ptr+n>max_param_stack then
+ begin max_param_stack:=param_ptr+n;
+ if max_param_stack>param_size then
+ overflow("parameter stack size",param_size);
+@:TeX capacity exceeded parameter stack size}{\quad parameter stack size@>
+ end;
+ for m:=0 to n-1 do param_stack[param_ptr+m]:=pstack[m];
+ param_ptr:=param_ptr+n;
+ end
+
+@ At this point, the reader will find it advisable to review the explanation
+of token list format that was presented earlier, since many aspects of that
+format are of importance chiefly in the |macro_call| routine.
+
+The token list might begin with a string of compulsory tokens before the
+first |match| or |end_match|. In that case the macro name is supposed to be
+followed by those tokens; the following program will set |s=null| to
+represent this restriction. Otherwise |s| will be set to the first token of
+a string that will delimit the next parameter.
+
+@<Scan the parameters and make |link(r)| point to the macro body...@>=
+begin scanner_status:=matching; unbalance:=0;
+long_state:=eq_type(cur_cs);
+if long_state>=outer_call then long_state:=long_state-2;
+repeat link(temp_head):=null;
+if (info(r)>match_token+255)or(info(r)<match_token) then s:=null
+else begin match_chr:=info(r)-match_token; s:=link(r); r:=s;
+ p:=temp_head; m:=0;
+ end;
+@<Scan a parameter until its delimiter string has been found; or, if |s=null|,
+ simply scan the delimiter string@>;@/
+{now |info(r)| is a token whose command code is either |match| or |end_match|}
+until info(r)=end_match_token;
+end
+
+@ If |info(r)| is a |match| or |end_match| command, it cannot be equal to
+any token found by |get_token|. Therefore an undelimited parameter---i.e.,
+a |match| that is immediately followed by |match| or |end_match|---will
+always fail the test `|cur_tok=info(r)|' in the following algorithm.
+
+@<Scan a parameter until its delimiter string has been found; or, ...@>=
+continue: get_token; {set |cur_tok| to the next token of input}
+if cur_tok=info(r) then
+ @<Advance \(r)|r|; |goto found| if the parameter delimiter has been
+ fully matched, otherwise |goto continue|@>;
+@<Contribute the recently matched tokens to the current parameter, and
+ |goto continue| if a partial match is still in effect;
+ but abort if |s=null|@>;
+if cur_tok=par_token then if long_state<>long_call then
+ @<Report a runaway argument and abort@>;
+if cur_tok<right_brace_limit then
+ if cur_tok<left_brace_limit then
+ @<Contribute an entire group to the current parameter@>
+ else @<Report an extra right brace and |goto continue|@>
+else @<Store the current token, but |goto continue| if it is
+ a blank space that would become an undelimited parameter@>;
+incr(m);
+if info(r)>end_match_token then goto continue;
+if info(r)<match_token then goto continue;
+found: if s<>null then @<Tidy up the parameter just scanned, and tuck it away@>
+
+@ @<Store the current token, but |goto continue| if it is...@>=
+begin if cur_tok=space_token then
+ if info(r)<=end_match_token then
+ if info(r)>=match_token then goto continue;
+store_new_token(cur_tok);
+end
+
+@ A slightly subtle point arises here: When the parameter delimiter ends
+with `\.{\#\{}', the token list will have a left brace both before and
+after the |end_match|\kern-.4pt. Only one of these should affect the
+|align_state|, but both will be scanned, so we must make a correction.
+
+@<Advance \(r)|r|; |goto found| if the parameter delimiter has been fully...@>=
+begin r:=link(r);
+if (info(r)>=match_token)and(info(r)<=end_match_token) then
+ begin if cur_tok<left_brace_limit then decr(align_state);
+ goto found;
+ end
+else goto continue;
+end
+
+@ @<Report an extra right brace and |goto continue|@>=
+begin back_input; print_err("Argument of "); sprint_cs(warning_index);
+@.Argument of \\x has...@>
+print(" has an extra }");
+help6("I've run across a `}' that doesn't seem to match anything.")@/
+ ("For example, `\def\a#1{...}' and `\a}' would produce")@/
+ ("this error. If you simply proceed now, the `\par' that")@/
+ ("I've just inserted will cause me to report a runaway")@/
+ ("argument that might be the root of the problem. But if")@/
+ ("your `}' was spurious, just type `2' and it will go away.");
+incr(align_state); long_state:=call; cur_tok:=par_token; ins_error;
+goto continue;
+end {a white lie; the \.{\\par} won't always trigger a runaway}
+
+@ If |long_state=outer_call|, a runaway argument has already been reported.
+
+@<Report a runaway argument and abort@>=
+begin if long_state=call then
+ begin runaway; print_err("Paragraph ended before ");
+@.Paragraph ended before...@>
+ sprint_cs(warning_index); print(" was complete");
+ help3("I suspect you've forgotten a `}', causing me to apply this")@/
+ ("control sequence to too much text. How can we recover?")@/
+ ("My plan is to forget the whole thing and hope for the best.");
+ back_error;
+ end;
+pstack[n]:=link(temp_head); align_state:=align_state-unbalance;
+for m:=0 to n do flush_list(pstack[m]);
+return;
+end
+
+@ When the following code becomes active, we have matched tokens from |s| to
+the predecessor of |r|, and we have found that |cur_tok<>info(r)|. An
+interesting situation now presents itself: If the parameter is to be
+delimited by a string such as `\.{ab}', and if we have scanned `\.{aa}',
+we want to contribute one `\.a' to the current parameter and resume
+looking for a `\.b'. The program must account for such partial matches and
+for others that can be quite complex. But most of the time we have |s=r|
+and nothing needs to be done.
+
+Incidentally, it is possible for \.{\\par} tokens to sneak in to certain
+parameters of non-\.{\\long} macros. For example, consider a case like
+`\.{\\def\\a\#1\\par!\{...\}}' where the first \.{\\par} is not followed
+by an exclamation point. In such situations it does not seem appropriate
+to prohibit the \.{\\par}, so \TeX\ keeps quiet about this bending of
+the rules.
+
+@<Contribute the recently matched tokens to the current parameter...@>=
+if s<>r then
+ if s=null then @<Report an improper use of the macro and abort@>
+ else begin t:=s;
+ repeat store_new_token(info(t)); incr(m); u:=link(t); v:=s;
+ loop@+ begin if u=r then
+ if cur_tok<>info(v) then goto done
+ else begin r:=link(v); goto continue;
+ end;
+ if info(u)<>info(v) then goto done;
+ u:=link(u); v:=link(v);
+ end;
+ done: t:=link(t);
+ until t=r;
+ r:=s; {at this point, no tokens are recently matched}
+ end
+
+@ @<Report an improper use...@>=
+begin print_err("Use of "); sprint_cs(warning_index);
+@.Use of x doesn't match...@>
+print(" doesn't match its definition");
+help4("If you say, e.g., `\def\a1{...}', then you must always")@/
+ ("put `1' after `\a', since control sequence names are")@/
+ ("made up of letters only. The macro here has not been")@/
+ ("followed by the required stuff, so I'm ignoring it.");
+error; return;
+end
+
+@ @<Contribute an entire group to the current parameter@>=
+begin unbalance:=1;
+@^inner loop@>
+loop@+ begin fast_store_new_token(cur_tok); get_token;
+ if cur_tok=par_token then if long_state<>long_call then
+ @<Report a runaway argument and abort@>;
+ if cur_tok<right_brace_limit then
+ if cur_tok<left_brace_limit then incr(unbalance)
+ else begin decr(unbalance);
+ if unbalance=0 then goto done1;
+ end;
+ end;
+done1: rbrace_ptr:=p; store_new_token(cur_tok);
+end
+
+@ If the parameter consists of a single group enclosed in braces, we must
+strip off the enclosing braces. That's why |rbrace_ptr| was introduced.
+
+@<Tidy up the parameter just scanned, and tuck it away@>=
+begin if (m=1)and(info(p)<right_brace_limit)and(p<>temp_head) then
+ begin link(rbrace_ptr):=null; free_avail(p);
+ p:=link(temp_head); pstack[n]:=link(p); free_avail(p);
+ end
+else pstack[n]:=link(temp_head);
+incr(n);
+if tracing_macros>0 then
+ begin begin_diagnostic; print_nl(match_chr); print_int(n);
+ print("<-"); show_token_list(pstack[n-1],null,1000);
+ end_diagnostic(false);
+ end;
+end
+
+@ @<Show the text of the macro being expanded@>=
+begin begin_diagnostic; print_ln; print_cs(warning_index);
+token_show(ref_count); end_diagnostic(false);
+end
+
+@* \[26] Basic scanning subroutines.
+Let's turn now to some procedures that \TeX\ calls upon frequently to digest
+certain kinds of patterns in the input. Most of these are quite simple;
+some are quite elaborate. Almost all of the routines call |get_x_token|,
+which can cause them to be invoked recursively.
+@^stomach@>
+@^recursion@>
+
+@ The |scan_left_brace| routine is called when a left brace is supposed to be
+the next non-blank token. (The term ``left brace'' means, more precisely,
+a character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to
+appear before the |left_brace|.
+
+@p procedure scan_left_brace; {reads a mandatory |left_brace|}
+begin @<Get the next non-blank non-relax non-call token@>;
+if cur_cmd<>left_brace then
+ begin print_err("Missing { inserted");
+@.Missing \{ inserted@>
+ help4("A left brace was mandatory here, so I've put one in.")@/
+ ("You might want to delete and/or insert some corrections")@/
+ ("so that I will find a matching right brace soon.")@/
+ ("(If you're confused by all this, try typing `I}' now.)");
+ back_error; cur_tok:=left_brace_token+"{"; cur_cmd:=left_brace;
+ cur_chr:="{"; incr(align_state);
+ end;
+end;
+
+@ @<Get the next non-blank non-relax non-call token@>=
+repeat get_x_token;
+until (cur_cmd<>spacer)and(cur_cmd<>relax)
+
+@ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded
+by optional spaces; `\.{\\relax}' is not ignored here.
+
+@p procedure scan_optional_equals;
+begin @<Get the next non-blank non-call token@>;
+if cur_tok<>other_token+"=" then back_input;
+end;
+
+@ @<Get the next non-blank non-call token@>=
+repeat get_x_token;
+until cur_cmd<>spacer
+
+@ In case you are getting bored, here is a slightly less trivial routine:
+Given a string of lowercase letters, like `\.{pt}' or `\.{plus}' or
+`\.{width}', the |scan_keyword| routine checks to see whether the next
+tokens of input match this string. The match must be exact, except that
+uppercase letters will match their lowercase counterparts; uppercase
+equivalents are determined by subtracting |"a"-"A"|, rather than using the
+|uc_code| table, since \TeX\ uses this routine only for its own limited
+set of keywords.
+
+If a match is found, the characters are effectively removed from the input
+and |true| is returned. Otherwise |false| is returned, and the input
+is left essentially unchanged (except for the fact that some macros
+may have been expanded, etc.).
+@^inner loop@>
+
+@p function scan_keyword(@!s:str_number):boolean; {look for a given string}
+label exit;
+var p:pointer; {tail of the backup list}
+@!q:pointer; {new node being added to the token list via |store_new_token|}
+@!k:pool_pointer; {index into |str_pool|}
+begin p:=backup_head; link(p):=null; k:=str_start[s];
+while k<str_start[s+1] do
+ begin get_x_token; {recursion is possible here}
+@^recursion@>
+ if (cur_cs=0)and@|
+ ((cur_chr=so(str_pool[k]))or(cur_chr=so(str_pool[k])-"a"+"A")) then
+ begin store_new_token(cur_tok); incr(k);
+ end
+ else if (cur_cmd<>spacer)or(p<>backup_head) then
+ begin back_input;
+ if p<>backup_head then back_list(link(backup_head));
+ scan_keyword:=false; return;
+ end;
+ end;
+flush_list(link(backup_head)); scan_keyword:=true;
+exit:end;
+
+@ Here is a procedure that sounds an alarm when mu and non-mu units
+are being switched.
+
+@p procedure mu_error;
+begin print_err("Incompatible glue units");
+@.Incompatible glue units@>
+help1("I'm going to assume that 1mu=1pt when they're mixed.");
+error;
+end;
+
+@ The next routine `|scan_something_internal|' is used to fetch internal
+numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}'
+when expanding constructions like `\.{\\the\\toks0}' and
+`\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int|
+procedure, which calls |scan_something_internal|; on the other hand,
+|scan_something_internal| also calls |scan_int|, for constructions like
+`\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we
+have to declare |scan_int| as a |forward| procedure. A few other
+procedures are also declared at this point.
+
+@p procedure@?scan_int; forward; {scans an integer value}
+@t\4\4@>@<Declare procedures that scan restricted classes of integers@>@;
+@t\4\4@>@<Declare procedures that scan font-related stuff@>
+
+@ \TeX\ doesn't know exactly what to expect when |scan_something_internal|
+begins. For example, an integer or dimension or glue value could occur
+immediately after `\.{\\hskip}'; and one can even say \.{\\the} with
+respect to token lists in constructions like
+`\.{\\xdef\\o\{\\the\\output\}}'. On the other hand, only integers are
+allowed after a construction like `\.{\\count}'. To handle the various
+possibilities, |scan_something_internal| has a |level| parameter, which
+tells the ``highest'' kind of quantity that |scan_something_internal| is
+allowed to produce. Six levels are distinguished, namely |int_val|,
+|dimen_val|, |glue_val|, |mu_val|, |ident_val|, and |tok_val|.
+
+The output of |scan_something_internal| (and of the other routines
+|scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global
+variable |cur_val|, and its level is put into |cur_val_level|. The highest
+values of |cur_val_level| are special: |mu_val| is used only when
+|cur_val| points to something in a ``muskip'' register, or to one of the
+three parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip};
+|ident_val| is used only when |cur_val| points to a font identifier;
+|tok_val| is used only when |cur_val| points to |null| or to the reference
+count of a token list. The last two cases are allowed only when
+|scan_something_internal| is called with |level=tok_val|.
+
+If the output is glue, |cur_val| will point to a glue specification, and
+the reference count of that glue will have been updated to reflect this
+reference; if the output is a nonempty token list, |cur_val| will point to
+its reference count, but in this case the count will not have been updated.
+Otherwise |cur_val| will contain the integer or scaled value in question.
+
+@d int_val=0 {integer values}
+@d dimen_val=1 {dimension values}
+@d glue_val=2 {glue specifications}
+@d mu_val=3 {math glue specifications}
+@d ident_val=4 {font identifier}
+@d tok_val=5 {token lists}
+
+@<Glob...@>=
+@!cur_val:integer; {value returned by numeric scanners}
+@!cur_val_level:int_val..tok_val; {the ``level'' of this value}
+
+@ The hash table is initialized with `\.{\\count}', `\.{\\dimen}', `\.{\\skip}',
+and `\.{\\muskip}' all having |register| as their command code; they are
+distinguished by the |chr_code|, which is either |int_val|, |dimen_val|,
+|glue_val|, or |mu_val|.
+
+@<Put each...@>=
+primitive("count",register,int_val);
+@!@:count_}{\.{\\count} primitive@>
+primitive("dimen",register,dimen_val);
+@!@:dimen_}{\.{\\dimen} primitive@>
+primitive("skip",register,glue_val);
+@!@:skip_}{\.{\\skip} primitive@>
+primitive("muskip",register,mu_val);
+@!@:mu_skip_}{\.{\\muskip} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+register: if chr_code=int_val then print_esc("count")
+ else if chr_code=dimen_val then print_esc("dimen")
+ else if chr_code=glue_val then print_esc("skip")
+ else print_esc("muskip");
+
+@ OK, we're ready for |scan_something_internal| itself. A second parameter,
+|negative|, is set |true| if the value that is found should be negated.
+It is assumed that |cur_cmd| and |cur_chr| represent the first token of
+the internal quantity to be scanned; an error will be signalled if
+|cur_cmd<min_internal| or |cur_cmd>max_internal|.
+
+@d scanned_result_end(#)==cur_val_level:=#;@+end
+@d scanned_result(#)==@+begin cur_val:=#;scanned_result_end
+
+@p procedure scan_something_internal(@!level:small_number;@!negative:boolean);
+ {fetch an internal parameter}
+var m:halfword; {|chr_code| part of the operand token}
+@!p:0..nest_size; {index into |nest|}
+begin m:=cur_chr;
+case cur_cmd of
+def_code: @<Fetch a character code from some table@>;
+toks_register,assign_toks,def_family,set_font,def_font: @<Fetch a token list or
+ font identifier, provided that |level=tok_val|@>;
+assign_int: scanned_result(eqtb[m].int)(int_val);
+assign_dimen: scanned_result(eqtb[m].sc)(dimen_val);
+assign_glue: scanned_result(equiv(m))(glue_val);
+assign_mu_glue: scanned_result(equiv(m))(mu_val);
+set_aux: @<Fetch the |space_factor| or the |prev_depth|@>;
+set_prev_graf: @<Fetch the |prev_graf|@>;
+set_page_int:@<Fetch the |dead_cycles| or the |insert_penalties|@>;
+set_page_dimen: @<Fetch something on the |page_so_far|@>;
+set_shape: @<Fetch the |par_shape| size@>;
+set_box_dimen: @<Fetch a box dimension@>;
+char_given,math_given: scanned_result(cur_chr)(int_val);
+assign_font_dimen: @<Fetch a font dimension@>;
+assign_font_int: @<Fetch a font integer@>;
+register: @<Fetch a register@>;
+last_item: @<Fetch an item in the current node, if appropriate@>;
+othercases @<Complain that \.{\\the} can't do this; give zero result@>
+endcases;@/
+while cur_val_level>level do @<Convert \(c)|cur_val| to a lower level@>;
+@<Fix the reference count, if any, and negate |cur_val| if |negative|@>;
+end;
+
+@ @<Fetch a character code from some table@>=
+begin scan_char_num;
+if m=math_code_base then scanned_result(ho(math_code(cur_val)))(int_val)
+else if m<math_code_base then scanned_result(equiv(m+cur_val))(int_val)
+else scanned_result(eqtb[m+cur_val].int)(int_val);
+end
+
+@ @<Fetch a token list...@>=
+if level<>tok_val then
+ begin print_err("Missing number, treated as zero");
+@.Missing number...@>
+ help3("A number should have been here; I inserted `0'.")@/
+ ("(If you can't figure out why I needed to see a number,")@/
+ ("look up `weird error' in the index to The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+ back_error; scanned_result(0)(dimen_val);
+ end
+else if cur_cmd<=assign_toks then
+ begin if cur_cmd<assign_toks then {|cur_cmd=toks_register|}
+ begin scan_eight_bit_int; m:=toks_base+cur_val;
+ end;
+ scanned_result(equiv(m))(tok_val);
+ end
+else begin back_input; scan_font_ident;
+ scanned_result(font_id_base+cur_val)(ident_val);
+ end
+
+@ Users refer to `\.{\\the\\spacefactor}' only in horizontal
+mode, and to `\.{\\the\\prevdepth}' only in vertical mode; so we put the
+associated mode in the modifier part of the |set_aux| command.
+The |set_page_int| command has modifier 0 or 1, for `\.{\\deadcycles}' and
+`\.{\\insertpenalties}', respectively. The |set_box_dimen| command is
+modified by either |width_offset|, |height_offset|, or |depth_offset|.
+And the |last_item| command is modified by either |int_val|, |dimen_val|,
+|glue_val|, |input_line_no_code|, or |badness_code|.
+
+@d input_line_no_code=glue_val+1 {code for \.{\\inputlineno}}
+@d badness_code=glue_val+2 {code for \.{\\badness}}
+
+@<Put each...@>=
+primitive("spacefactor",set_aux,hmode);
+@!@:space_factor_}{\.{\\spacefactor} primitive@>
+primitive("prevdepth",set_aux,vmode);@/
+@!@:prev_depth_}{\.{\\prevdepth} primitive@>
+primitive("deadcycles",set_page_int,0);
+@!@:dead_cycles_}{\.{\\deadcycles} primitive@>
+primitive("insertpenalties",set_page_int,1);
+@!@:insert_penalties_}{\.{\\insertpenalties} primitive@>
+primitive("wd",set_box_dimen,width_offset);
+@!@:wd_}{\.{\\wd} primitive@>
+primitive("ht",set_box_dimen,height_offset);
+@!@:ht_}{\.{\\ht} primitive@>
+primitive("dp",set_box_dimen,depth_offset);
+@!@:dp_}{\.{\\dp} primitive@>
+primitive("lastpenalty",last_item,int_val);
+@!@:last_penalty_}{\.{\\lastpenalty} primitive@>
+primitive("lastkern",last_item,dimen_val);
+@!@:last_kern_}{\.{\\lastkern} primitive@>
+primitive("lastskip",last_item,glue_val);
+@!@:last_skip_}{\.{\\lastskip} primitive@>
+primitive("inputlineno",last_item,input_line_no_code);
+@!@:input_line_no_}{\.{\\inputlineno} primitive@>
+primitive("badness",last_item,badness_code);
+@!@:badness_}{\.{\\badness} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+set_aux: if chr_code=vmode then print_esc("prevdepth")
+@+else print_esc("spacefactor");
+set_page_int: if chr_code=0 then print_esc("deadcycles")
+@+else print_esc("insertpenalties");
+set_box_dimen: if chr_code=width_offset then print_esc("wd")
+else if chr_code=height_offset then print_esc("ht")
+else print_esc("dp");
+last_item: case chr_code of
+ int_val: print_esc("lastpenalty");
+ dimen_val: print_esc("lastkern");
+ glue_val: print_esc("lastskip");
+ input_line_no_code: print_esc("inputlineno");
+ othercases print_esc("badness")
+ endcases;
+
+@ @<Fetch the |space_factor| or the |prev_depth|@>=
+if abs(mode)<>m then
+ begin print_err("Improper "); print_cmd_chr(set_aux,m);
+@.Improper \\spacefactor@>
+@.Improper \\prevdepth@>
+ help4("You can refer to \spacefactor only in horizontal mode;")@/
+ ("you can refer to \prevdepth only in vertical mode; and")@/
+ ("neither of these is meaningful inside \write. So")@/
+ ("I'm forgetting what you said and using zero instead.");
+ error;
+ if level<>tok_val then scanned_result(0)(dimen_val)
+ else scanned_result(0)(int_val);
+ end
+else if m=vmode then scanned_result(prev_depth)(dimen_val)
+else scanned_result(space_factor)(int_val)
+
+@ @<Fetch the |dead_cycles| or the |insert_penalties|@>=
+begin if m=0 then cur_val:=dead_cycles@+else cur_val:=insert_penalties;
+cur_val_level:=int_val;
+end
+
+@ @<Fetch a box dimension@>=
+begin scan_eight_bit_int;
+if box(cur_val)=null then cur_val:=0 @+else cur_val:=mem[box(cur_val)+m].sc;
+cur_val_level:=dimen_val;
+end
+
+@ Inside an \.{\\output} routine, a user may wish to look at the page totals
+that were present at the moment when output was triggered.
+
+@d max_dimen==@'7777777777 {$2^{30}-1$}
+
+@<Fetch something on the |page_so_far|@>=
+begin if (page_contents=empty) and (not output_active) then
+ if m=0 then cur_val:=max_dimen@+else cur_val:=0
+else cur_val:=page_so_far[m];
+cur_val_level:=dimen_val;
+end
+
+@ @<Fetch the |prev_graf|@>=
+if mode=0 then scanned_result(0)(int_val) {|prev_graf=0| within \.{\\write}}
+else begin nest[nest_ptr]:=cur_list; p:=nest_ptr;
+ while abs(nest[p].mode_field)<>vmode do decr(p);
+ scanned_result(nest[p].pg_field)(int_val);
+ end
+
+@ @<Fetch the |par_shape| size@>=
+begin if par_shape_ptr=null then cur_val:=0
+else cur_val:=info(par_shape_ptr);
+cur_val_level:=int_val;
+end
+
+@ Here is where \.{\\lastpenalty}, \.{\\lastkern}, and \.{\\lastskip} are
+implemented. The reference count for \.{\\lastskip} will be updated later.
+
+We also handle \.{\\inputlineno} and \.{\\badness} here, because they are
+legal in similar contexts.
+
+@<Fetch an item in the current node...@>=
+if cur_chr>glue_val then
+ begin if cur_chr=input_line_no_code then cur_val:=line
+ else cur_val:=last_badness; {|cur_chr=badness_code|}
+ cur_val_level:=int_val;
+ end
+else begin if cur_chr=glue_val then cur_val:=zero_glue@+else cur_val:=0;
+ cur_val_level:=cur_chr;
+ if not is_char_node(tail)and(mode<>0) then
+ case cur_chr of
+ int_val: if type(tail)=penalty_node then cur_val:=penalty(tail);
+ dimen_val: if type(tail)=kern_node then cur_val:=width(tail);
+ glue_val: if type(tail)=glue_node then
+ begin cur_val:=glue_ptr(tail);
+ if subtype(tail)=mu_glue then cur_val_level:=mu_val;
+ end;
+ end {there are no other cases}
+ else if (mode=vmode)and(tail=head) then
+ case cur_chr of
+ int_val: cur_val:=last_penalty;
+ dimen_val: cur_val:=last_kern;
+ glue_val: if last_glue<>max_halfword then cur_val:=last_glue;
+ end; {there are no other cases}
+ end
+
+@ @<Fetch a font dimension@>=
+begin find_font_dimen(false); font_info[fmem_ptr].sc:=0;
+scanned_result(font_info[cur_val].sc)(dimen_val);
+end
+
+@ @<Fetch a font integer@>=
+begin scan_font_ident;
+if m=0 then scanned_result(hyphen_char[cur_val])(int_val)
+else scanned_result(skew_char[cur_val])(int_val);
+end
+
+@ @<Fetch a register@>=
+begin scan_eight_bit_int;
+case m of
+int_val:cur_val:=count(cur_val);
+dimen_val:cur_val:=dimen(cur_val);
+glue_val: cur_val:=skip(cur_val);
+mu_val: cur_val:=mu_skip(cur_val);
+end; {there are no other cases}
+cur_val_level:=m;
+end
+
+@ @<Complain that \.{\\the} can't do this; give zero result@>=
+begin print_err("You can't use `"); print_cmd_chr(cur_cmd,cur_chr);
+@.You can't use x after ...@>
+print("' after "); print_esc("the");
+help1("I'm forgetting what you said and using zero instead.");
+error;
+if level<>tok_val then scanned_result(0)(dimen_val)
+else scanned_result(0)(int_val);
+end
+
+@ When a |glue_val| changes to a |dimen_val|, we use the width component
+of the glue; there is no need to decrease the reference count, since it
+has not yet been increased. When a |dimen_val| changes to an |int_val|,
+we use scaled points so that the value doesn't actually change. And when a
+|mu_val| changes to a |glue_val|, the value doesn't change either.
+
+@<Convert \(c)|cur_val| to a lower level@>=
+begin if cur_val_level=glue_val then cur_val:=width(cur_val)
+else if cur_val_level=mu_val then mu_error;
+decr(cur_val_level);
+end
+
+@ If |cur_val| points to a glue specification at this point, the reference
+count for the glue does not yet include the reference by |cur_val|.
+If |negative| is |true|, |cur_val_level| is known to be |<=mu_val|.
+
+@<Fix the reference count, if any, ...@>=
+if negative then
+ if cur_val_level>=glue_val then
+ begin cur_val:=new_spec(cur_val);
+ @<Negate all three glue components of |cur_val|@>;
+ end
+ else negate(cur_val)
+else if (cur_val_level>=glue_val)and(cur_val_level<=mu_val) then
+ add_glue_ref(cur_val)
+
+@ @<Negate all three...@>=
+begin negate(width(cur_val));
+negate(stretch(cur_val));
+negate(shrink(cur_val));
+end
+
+@ Our next goal is to write the |scan_int| procedure, which scans anything that
+\TeX\ treats as an integer. But first we might as well look at some simple
+applications of |scan_int| that have already been made inside of
+|scan_something_internal|.
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+procedure scan_eight_bit_int;
+begin scan_int;
+if (cur_val<0)or(cur_val>255) then
+ begin print_err("Bad register code");
+@.Bad register code@>
+ help2("A register number must be between 0 and 255.")@/
+ ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
+ end;
+end;
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+procedure scan_char_num;
+begin scan_int;
+if (cur_val<0)or(cur_val>255) then
+ begin print_err("Bad character code");
+@.Bad character code@>
+ help2("A character number must be between 0 and 255.")@/
+ ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
+ end;
+end;
+
+@ While we're at it, we might as well deal with similar routines that
+will be needed later.
+
+@<Declare procedures that scan restricted classes of integers@>=
+procedure scan_four_bit_int;
+begin scan_int;
+if (cur_val<0)or(cur_val>15) then
+ begin print_err("Bad number");
+@.Bad number@>
+ help2("Since I expected to read a number between 0 and 15,")@/
+ ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
+ end;
+end;
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+procedure scan_fifteen_bit_int;
+begin scan_int;
+if (cur_val<0)or(cur_val>@'77777) then
+ begin print_err("Bad mathchar");
+@.Bad mathchar@>
+ help2("A mathchar number must be between 0 and 32767.")@/
+ ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
+ end;
+end;
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+procedure scan_twenty_seven_bit_int;
+begin scan_int;
+if (cur_val<0)or(cur_val>@'777777777) then
+ begin print_err("Bad delimiter code");
+@.Bad delimiter code@>
+ help2("A numeric delimiter code must be between 0 and 2^{27}-1.")@/
+ ("I changed this one to zero."); int_error(cur_val); cur_val:=0;
+ end;
+end;
+
+@ An integer number can be preceded by any number of spaces and `\.+' or
+`\.-' signs. Then comes either a decimal constant (i.e., radix 10), an
+octal constant (i.e., radix 8, preceded by~\.\'), a hexadecimal constant
+(radix 16, preceded by~\."), an alphabetic constant (preceded by~\.\`), or
+an internal variable. After scanning is complete,
+|cur_val| will contain the answer, which must be at most
+$2^{31}-1=2147483647$ in absolute value. The value of |radix| is set to
+10, 8, or 16 in the cases of decimal, octal, or hexadecimal constants,
+otherwise |radix| is set to zero. An optional space follows a constant.
+
+@d octal_token=other_token+"'" {apostrophe, indicates an octal constant}
+@d hex_token=other_token+"""" {double quote, indicates a hex constant}
+@d alpha_token=other_token+"`" {reverse apostrophe, precedes alpha constants}
+@d point_token=other_token+"." {decimal point}
+@d continental_point_token=other_token+"," {decimal point, Eurostyle}
+
+@<Glob...@>=
+@!radix:small_number; {|scan_int| sets this to 8, 10, 16, or zero}
+
+@ We initialize the following global variables just in case |expand|
+comes into action before any of the basic scanning routines has assigned
+them a value.
+
+@<Set init...@>=
+cur_val:=0; cur_val_level:=int_val; radix:=0; cur_order:=normal;
+
+@ The |scan_int| routine is used also to scan the integer part of a
+fraction; for example, the `\.3' in `\.{3.14159}' will be found by
+|scan_int|. The |scan_dimen| routine assumes that |cur_tok=point_token|
+after the integer part of such a fraction has been scanned by |scan_int|,
+and that the decimal point has been backed up to be scanned again.
+
+@p procedure scan_int; {sets |cur_val| to an integer}
+label done;
+var negative:boolean; {should the answer be negated?}
+@!m:integer; {|@t$2^{31}$@> div radix|, the threshold of danger}
+@!d:small_number; {the digit just scanned}
+@!vacuous:boolean; {have no digits appeared?}
+@!OK_so_far:boolean; {has an error message been issued?}
+begin radix:=0; OK_so_far:=true;@/
+@<Get the next non-blank non-sign token; set |negative| appropriately@>;
+if cur_tok=alpha_token then @<Scan an alphabetic character code into |cur_val|@>
+else if (cur_cmd>=min_internal)and(cur_cmd<=max_internal) then
+ scan_something_internal(int_val,false)
+else @<Scan a numeric constant@>;
+if negative then negate(cur_val);
+end;
+
+@ @<Get the next non-blank non-sign token...@>=
+negative:=false;
+repeat @<Get the next non-blank non-call token@>;
+if cur_tok=other_token+"-" then
+ begin negative := not negative; cur_tok:=other_token+"+";
+ end;
+until cur_tok<>other_token+"+"
+
+@ A space is ignored after an alphabetic character constant, so that
+such constants behave like numeric ones.
+
+@<Scan an alphabetic character code into |cur_val|@>=
+begin get_token; {suppress macro expansion}
+if cur_tok<cs_token_flag then
+ begin cur_val:=cur_chr;
+ if cur_cmd<=right_brace then
+ if cur_cmd=right_brace then incr(align_state)
+ else decr(align_state);
+ end
+else if cur_tok<cs_token_flag+single_base then
+ cur_val:=cur_tok-cs_token_flag-active_base
+else cur_val:=cur_tok-cs_token_flag-single_base;
+if cur_val>255 then
+ begin print_err("Improper alphabetic constant");
+@.Improper alphabetic constant@>
+ help2("A one-character control sequence belongs after a ` mark.")@/
+ ("So I'm essentially inserting \0 here.");
+ cur_val:="0"; back_error;
+ end
+else @<Scan an optional space@>;
+end
+
+@ @<Scan an optional space@>=
+begin get_x_token; if cur_cmd<>spacer then back_input;
+end
+
+@ @<Scan a numeric constant@>=
+begin radix:=10; m:=214748364;
+if cur_tok=octal_token then
+ begin radix:=8; m:=@'2000000000; get_x_token;
+ end
+else if cur_tok=hex_token then
+ begin radix:=16; m:=@'1000000000; get_x_token;
+ end;
+vacuous:=true; cur_val:=0;@/
+@<Accumulate the constant until |cur_tok| is not a suitable digit@>;
+if vacuous then @<Express astonishment that no number was here@>
+else if cur_cmd<>spacer then back_input;
+end
+
+@ @d infinity==@'17777777777 {the largest positive value that \TeX\ knows}
+@d zero_token=other_token+"0" {zero, the smallest digit}
+@d A_token=letter_token+"A" {the smallest special hex digit}
+@d other_A_token=other_token+"A" {special hex digit of type |other_char|}
+
+@<Accumulate the constant...@>=
+loop@+ begin if (cur_tok<zero_token+radix)and(cur_tok>=zero_token)and
+ (cur_tok<=zero_token+9) then d:=cur_tok-zero_token
+ else if radix=16 then
+ if (cur_tok<=A_token+5)and(cur_tok>=A_token) then d:=cur_tok-A_token+10
+ else if (cur_tok<=other_A_token+5)and(cur_tok>=other_A_token) then
+ d:=cur_tok-other_A_token+10
+ else goto done
+ else goto done;
+ vacuous:=false;
+ if (cur_val>=m)and((cur_val>m)or(d>7)or(radix<>10)) then
+ begin if OK_so_far then
+ begin print_err("Number too big");
+@.Number too big@>
+ help2("I can only go up to 2147483647='17777777777=""7FFFFFFF,")@/
+ ("so I'm using that number instead of yours.");
+ error; cur_val:=infinity; OK_so_far:=false;
+ end;
+ end
+ else cur_val:=cur_val*radix+d;
+ get_x_token;
+ end;
+done:
+
+@ @<Express astonishment...@>=
+begin print_err("Missing number, treated as zero");
+@.Missing number...@>
+help3("A number should have been here; I inserted `0'.")@/
+ ("(If you can't figure out why I needed to see a number,")@/
+ ("look up `weird error' in the index to The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+back_error;
+end
+
+@ The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to
+a |scaled| value, i.e., an integral number of sp. One of its main tasks
+is therefore to interpret the abbreviations for various kinds of units and
+to convert measurements to scaled points.
+
+There are three parameters: |mu| is |true| if the finite units must be
+`\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed;
+|inf| is |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}'
+are permitted; and |shortcut| is |true| if |cur_val| already contains
+an integer and only the units need to be considered.
+
+The order of infinity that was found in the case of infinite glue is returned
+in the global variable |cur_order|.
+
+@<Glob...@>=
+@!cur_order:glue_ord; {order of infinity found by |scan_dimen|}
+
+@ Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen|
+may begin with |scan_int|. This explains why it is convenient to use
+|scan_int| also for the integer part of a decimal fraction.
+
+Several branches of |scan_dimen| work with |cur_val| as an integer and
+with an auxiliary fraction |f|, so that the actual quantity of interest is
+$|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked''
+representation is put into the single word |cur_val|, which suddenly
+switches significance from |integer| to |scaled|.
+
+@d attach_fraction=88 {go here to pack |cur_val| and |f| into |cur_val|}
+@d attach_sign=89 {go here when |cur_val| is correct except perhaps for sign}
+@d scan_normal_dimen==scan_dimen(false,false,false)
+
+@p procedure scan_dimen(@!mu,@!inf,@!shortcut:boolean);
+ {sets |cur_val| to a dimension}
+label done, done1, done2, found, not_found, attach_fraction, attach_sign;
+var negative:boolean; {should the answer be negated?}
+@!f:integer; {numerator of a fraction whose denominator is $2^{16}$}
+@<Local variables for dimension calculations@>@;
+begin f:=0; arith_error:=false; cur_order:=normal; negative:=false;
+if not shortcut then
+ begin @<Get the next non-blank non-sign...@>;
+ if (cur_cmd>=min_internal)and(cur_cmd<=max_internal) then
+ @<Fetch an internal dimension and |goto attach_sign|,
+ or fetch an internal integer@>
+ else begin back_input;
+ if cur_tok=continental_point_token then cur_tok:=point_token;
+ if cur_tok<>point_token then scan_int
+ else begin radix:=10; cur_val:=0;
+ end;
+ if cur_tok=continental_point_token then cur_tok:=point_token;
+ if (radix=10)and(cur_tok=point_token) then @<Scan decimal fraction@>;
+ end;
+ end;
+if cur_val<0 then {in this case |f=0|}
+ begin negative := not negative; negate(cur_val);
+ end;
+@<Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there
+ are |x| sp per unit; |goto attach_sign| if the units are internal@>;
+@<Scan an optional space@>;
+attach_sign: if arith_error or(abs(cur_val)>=@'10000000000) then
+ @<Report that this dimension is out of range@>;
+if negative then negate(cur_val);
+end;
+
+@ @<Fetch an internal dimension and |goto attach_sign|...@>=
+if mu then
+ begin scan_something_internal(mu_val,false);
+ @<Coerce glue to a dimension@>;
+ if cur_val_level=mu_val then goto attach_sign;
+ if cur_val_level<>int_val then mu_error;
+ end
+else begin scan_something_internal(dimen_val,false);
+ if cur_val_level=dimen_val then goto attach_sign;
+ end
+
+@ @<Local variables for dimension calculations@>=
+@!num,@!denom:1..65536; {conversion ratio for the scanned units}
+@!k,@!kk:small_number; {number of digits in a decimal fraction}
+@!p,@!q:pointer; {top of decimal digit stack}
+@!v:scaled; {an internal dimension}
+@!save_cur_val:integer; {temporary storage of |cur_val|}
+
+@ The following code is executed when |scan_something_internal| was
+called asking for |mu_val|, when we really wanted a ``mudimen'' instead
+of ``muglue.''
+
+@<Coerce glue to a dimension@>=
+if cur_val_level>=glue_val then
+ begin v:=width(cur_val); delete_glue_ref(cur_val); cur_val:=v;
+ end
+
+@ When the following code is executed, we have |cur_tok=point_token|, but this
+token has been backed up using |back_input|; we must first discard it.
+
+It turns out that a decimal point all by itself is equivalent to `\.{0.0}'.
+Let's hope people don't use that fact.
+
+@<Scan decimal fraction@>=
+begin k:=0; p:=null; get_token; {|point_token| is being re-scanned}
+loop@+ begin get_x_token;
+ if (cur_tok>zero_token+9)or(cur_tok<zero_token) then goto done1;
+ if k<17 then {digits for |k>=17| cannot affect the result}
+ begin q:=get_avail; link(q):=p; info(q):=cur_tok-zero_token;
+ p:=q; incr(k);
+ end;
+ end;
+done1: for kk:=k downto 1 do
+ begin dig[kk-1]:=info(p); q:=p; p:=link(p); free_avail(q);
+ end;
+f:=round_decimals(k);
+if cur_cmd<>spacer then back_input;
+end
+
+@ Now comes the harder part: At this point in the program, |cur_val| is a
+nonnegative integer and $f/2^{16}$ is a nonnegative fraction less than 1;
+we want to multiply the sum of these two quantities by the appropriate
+factor, based on the specified units, in order to produce a |scaled|
+result, and we want to do the calculation with fixed point arithmetic that
+does not overflow.
+
+@<Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$...@>=
+if inf then @<Scan for \(f)\.{fil} units; |goto attach_fraction| if found@>;
+@<Scan for \(u)units that are internal dimensions;
+ |goto attach_sign| with |cur_val| set if found@>;
+if mu then @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>;
+if scan_keyword("true") then @<Adjust \(f)for the magnification ratio@>;
+@.true@>
+if scan_keyword("pt") then goto attach_fraction; {the easy case}
+@.pt@>
+@<Scan for \(a)all other units and adjust |cur_val| and |f| accordingly;
+ |goto done| in the case of scaled points@>;
+attach_fraction: if cur_val>=@'40000 then arith_error:=true
+else cur_val:=cur_val*unity+f;
+done:
+
+@ A specification like `\.{filllll}' or `\.{fill L L L}' will lead to two
+error messages (one for each additional keyword \.{"l"}).
+
+@<Scan for \(f)\.{fil} units...@>=
+if scan_keyword("fil") then
+@.fil@>
+ begin cur_order:=fil;
+ while scan_keyword("l") do
+ begin if cur_order=filll then
+ begin print_err("Illegal unit of measure (");
+@.Illegal unit of measure@>
+ print("replaced by filll)");
+ help1("I dddon't go any higher than filll."); error;
+ end
+ else incr(cur_order);
+ end;
+ goto attach_fraction;
+ end
+
+@ @<Scan for \(u)units that are internal dimensions...@>=
+save_cur_val:=cur_val;
+@<Get the next non-blank non-call...@>;
+if (cur_cmd<min_internal)or(cur_cmd>max_internal) then back_input
+else begin if mu then
+ begin scan_something_internal(mu_val,false); @<Coerce glue...@>;
+ if cur_val_level<>mu_val then mu_error;
+ end
+ else scan_something_internal(dimen_val,false);
+ v:=cur_val; goto found;
+ end;
+if mu then goto not_found;
+if scan_keyword("em") then v:=(@<The em width for |cur_font|@>)
+@.em@>
+else if scan_keyword("ex") then v:=(@<The x-height for |cur_font|@>)
+@.ex@>
+else goto not_found;
+@<Scan an optional space@>;
+found:cur_val:=nx_plus_y(save_cur_val,v,xn_over_d(v,f,@'200000));
+goto attach_sign;
+not_found:
+
+@ @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>=
+if scan_keyword("mu") then goto attach_fraction
+@.mu@>
+else begin print_err("Illegal unit of measure ("); print("mu inserted)");
+@.Illegal unit of measure@>
+ help4("The unit of measurement in math glue must be mu.")@/
+ ("To recover gracefully from this error, it's best to")@/
+ ("delete the erroneous units; e.g., type `2' to delete")@/
+ ("two letters. (See Chapter 27 of The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+ error; goto attach_fraction;
+ end
+
+@ @<Adjust \(f)for the magnification ratio@>=
+begin prepare_mag;
+if mag<>1000 then
+ begin cur_val:=xn_over_d(cur_val,1000,mag);
+ f:=(1000*f+@'200000*remainder) div mag;
+ cur_val:=cur_val+(f div @'200000); f:=f mod @'200000;
+ end;
+end
+
+@ The necessary conversion factors can all be specified exactly as
+fractions whose numerator and denominator sum to 32768 or less.
+According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$;
+this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard
+@^Bosshard, Hans Rudolf@>
+in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980).
+
+@d set_conversion_end(#)== denom:=#; end
+@d set_conversion(#)==@+begin num:=#; set_conversion_end
+
+@<Scan for \(a)all other units and adjust |cur_val| and |f|...@>=
+if scan_keyword("in") then set_conversion(7227)(100)
+@.in@>
+else if scan_keyword("pc") then set_conversion(12)(1)
+@.pc@>
+else if scan_keyword("cm") then set_conversion(7227)(254)
+@.cm@>
+else if scan_keyword("mm") then set_conversion(7227)(2540)
+@.mm@>
+else if scan_keyword("bp") then set_conversion(7227)(7200)
+@.bp@>
+else if scan_keyword("dd") then set_conversion(1238)(1157)
+@.dd@>
+else if scan_keyword("cc") then set_conversion(14856)(1157)
+@.cc@>
+else if scan_keyword("sp") then goto done
+@.sp@>
+else @<Complain about unknown unit and |goto done2|@>;
+cur_val:=xn_over_d(cur_val,num,denom);
+f:=(num*f+@'200000*remainder) div denom;@/
+cur_val:=cur_val+(f div @'200000); f:=f mod @'200000;
+done2:
+
+@ @<Complain about unknown unit...@>=
+begin print_err("Illegal unit of measure ("); print("pt inserted)");
+@.Illegal unit of measure@>
+help6("Dimensions can be in units of em, ex, in, pt, pc,")@/
+ ("cm, mm, dd, cc, bp, or sp; but yours is a new one!")@/
+ ("I'll assume that you meant to say pt, for printer's points.")@/
+ ("To recover gracefully from this error, it's best to")@/
+ ("delete the erroneous units; e.g., type `2' to delete")@/
+ ("two letters. (See Chapter 27 of The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+error; goto done2;
+end
+
+
+@ @<Report that this dimension is out of range@>=
+begin print_err("Dimension too large");
+@.Dimension too large@>
+help2("I can't work with sizes bigger than about 19 feet.")@/
+ ("Continue and I'll use the largest value I can.");@/
+error; cur_val:=max_dimen; arith_error:=false;
+end
+
+@ The final member of \TeX's value-scanning trio is |scan_glue|, which
+makes |cur_val| point to a glue specification. The reference count of that
+glue spec will take account of the fact that |cur_val| is pointing to~it.
+
+The |level| parameter should be either |glue_val| or |mu_val|.
+
+Since |scan_dimen| was so much more complex than |scan_int|, we might expect
+|scan_glue| to be even worse. But fortunately, it is very simple, since
+most of the work has already been done.
+
+@p procedure scan_glue(@!level:small_number);
+ {sets |cur_val| to a glue spec pointer}
+label exit;
+var negative:boolean; {should the answer be negated?}
+@!q:pointer; {new glue specification}
+@!mu:boolean; {does |level=mu_val|?}
+begin mu:=(level=mu_val); @<Get the next non-blank non-sign...@>;
+if (cur_cmd>=min_internal)and(cur_cmd<=max_internal) then
+ begin scan_something_internal(level,negative);
+ if cur_val_level>=glue_val then
+ begin if cur_val_level<>level then mu_error;
+ return;
+ end;
+ if cur_val_level=int_val then scan_dimen(mu,false,true)
+ else if level=mu_val then mu_error;
+ end
+else begin back_input; scan_dimen(mu,false,false);
+ if negative then negate(cur_val);
+ end;
+@<Create a new glue specification whose width is |cur_val|; scan for its
+ stretch and shrink components@>;
+exit:end;
+
+@ @<Create a new glue specification whose width is |cur_val|...@>=
+q:=new_spec(zero_glue); width(q):=cur_val;
+if scan_keyword("plus") then
+@.plus@>
+ begin scan_dimen(mu,true,false);
+ stretch(q):=cur_val; stretch_order(q):=cur_order;
+ end;
+if scan_keyword("minus") then
+@.minus@>
+ begin scan_dimen(mu,true,false);
+ shrink(q):=cur_val; shrink_order(q):=cur_order;
+ end;
+cur_val:=q
+
+@ Here's a similar procedure that returns a pointer to a rule node. This
+routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule};
+therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store
+the default rule dimensions in the node, then to override them if
+`\.{height}' or `\.{width}' or `\.{depth}' specifications are
+found (in any order).
+
+@d default_rule=26214 {0.4\thinspace pt}
+
+@p function scan_rule_spec:pointer;
+label reswitch;
+var q:pointer; {the rule node being created}
+begin q:=new_rule; {|width|, |depth|, and |height| all equal |null_flag| now}
+if cur_cmd=vrule then width(q):=default_rule
+else begin height(q):=default_rule; depth(q):=0;
+ end;
+reswitch: if scan_keyword("width") then
+@.width@>
+ begin scan_normal_dimen; width(q):=cur_val; goto reswitch;
+ end;
+if scan_keyword("height") then
+@.height@>
+ begin scan_normal_dimen; height(q):=cur_val; goto reswitch;
+ end;
+if scan_keyword("depth") then
+@.depth@>
+ begin scan_normal_dimen; depth(q):=cur_val; goto reswitch;
+ end;
+scan_rule_spec:=q;
+end;
+
+@* \[27] Building token lists.
+The token lists for macros and for other things like \.{\\mark} and \.{\\output}
+and \.{\\write} are produced by a procedure called |scan_toks|.
+
+Before we get into the details of |scan_toks|, let's consider a much
+simpler task, that of converting the current string into a token list.
+The |str_toks| function does this; it classifies spaces as type |spacer|
+and everything else as type |other_char|.
+
+The token list created by |str_toks| begins at |link(temp_head)| and ends
+at the value |p| that is returned. (If |p=temp_head|, the list is empty.)
+
+@p function str_toks(@!b:pool_pointer):pointer;
+ {changes the string |str_pool[b..pool_ptr]| to a token list}
+var p:pointer; {tail of the token list}
+@!q:pointer; {new node being added to the token list via |store_new_token|}
+@!t:halfword; {token being appended}
+@!k:pool_pointer; {index into |str_pool|}
+begin str_room(1);
+p:=temp_head; link(p):=null; k:=b;
+while k<pool_ptr do
+ begin t:=so(str_pool[k]);
+ if t=" " then t:=space_token
+ else t:=other_token+t;
+ fast_store_new_token(t);
+ incr(k);
+ end;
+pool_ptr:=b; str_toks:=p;
+end;
+
+@ The main reason for wanting |str_toks| is the next function,
+|the_toks|, which has similar input/output characteristics.
+
+This procedure is supposed to scan something like `\.{\\skip\\count12}',
+i.e., whatever can follow `\.{\\the}', and it constructs a token list
+containing something like `\.{-3.0pt minus 0.5fill}'.
+
+@p function the_toks:pointer;
+var old_setting:0..max_selector; {holds |selector| setting}
+@!p,@!q,@!r:pointer; {used for copying a token list}
+@!b:pool_pointer; {base of temporary string}
+begin get_x_token; scan_something_internal(tok_val,false);
+if cur_val_level>=ident_val then @<Copy the token list@>
+else begin old_setting:=selector; selector:=new_string; b:=pool_ptr;
+ case cur_val_level of
+ int_val:print_int(cur_val);
+ dimen_val:begin print_scaled(cur_val); print("pt");
+ end;
+ glue_val: begin print_spec(cur_val,"pt"); delete_glue_ref(cur_val);
+ end;
+ mu_val: begin print_spec(cur_val,"mu"); delete_glue_ref(cur_val);
+ end;
+ end; {there are no other cases}
+ selector:=old_setting; the_toks:=str_toks(b);
+ end;
+end;
+
+@ @<Copy the token list@>=
+begin p:=temp_head; link(p):=null;
+if cur_val_level=ident_val then store_new_token(cs_token_flag+cur_val)
+else if cur_val<>null then
+ begin r:=link(cur_val); {do not copy the reference count}
+ while r<>null do
+ begin fast_store_new_token(info(r)); r:=link(r);
+ end;
+ end;
+the_toks:=p;
+end
+
+@ Here's part of the |expand| subroutine that we are now ready to complete:
+
+@p procedure ins_the_toks;
+begin link(garbage):=the_toks; ins_list(link(temp_head));
+end;
+
+@ The primitives \.{\\number}, \.{\\romannumeral}, \.{\\string}, \.{\\meaning},
+\.{\\fontname}, and \.{\\jobname} are defined as follows.
+
+@d number_code=0 {command code for \.{\\number}}
+@d roman_numeral_code=1 {command code for \.{\\romannumeral}}
+@d string_code=2 {command code for \.{\\string}}
+@d meaning_code=3 {command code for \.{\\meaning}}
+@d font_name_code=4 {command code for \.{\\fontname}}
+@d job_name_code=5 {command code for \.{\\jobname}}
+
+@<Put each...@>=
+primitive("number",convert,number_code);@/
+@!@:number_}{\.{\\number} primitive@>
+primitive("romannumeral",convert,roman_numeral_code);@/
+@!@:roman_numeral_}{\.{\\romannumeral} primitive@>
+primitive("string",convert,string_code);@/
+@!@:string_}{\.{\\string} primitive@>
+primitive("meaning",convert,meaning_code);@/
+@!@:meaning_}{\.{\\meaning} primitive@>
+primitive("fontname",convert,font_name_code);@/
+@!@:font_name_}{\.{\\fontname} primitive@>
+primitive("jobname",convert,job_name_code);@/
+@!@:job_name_}{\.{\\jobname} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+convert: case chr_code of
+ number_code: print_esc("number");
+ roman_numeral_code: print_esc("romannumeral");
+ string_code: print_esc("string");
+ meaning_code: print_esc("meaning");
+ font_name_code: print_esc("fontname");
+ othercases print_esc("jobname")
+ endcases;
+
+@ The procedure |conv_toks| uses |str_toks| to insert the token list
+for |convert| functions into the scanner; `\.{\\outer}' control sequences
+are allowed to follow `\.{\\string}' and `\.{\\meaning}'.
+
+@p procedure conv_toks;
+var old_setting:0..max_selector; {holds |selector| setting}
+@!c:number_code..job_name_code; {desired type of conversion}
+@!save_scanner_status:small_number; {|scanner_status| upon entry}
+@!b:pool_pointer; {base of temporary string}
+begin c:=cur_chr; @<Scan the argument for command |c|@>;
+old_setting:=selector; selector:=new_string; b:=pool_ptr;
+@<Print the result of command |c|@>;
+selector:=old_setting; link(garbage):=str_toks(b); ins_list(link(temp_head));
+end;
+
+@ @<Scan the argument for command |c|@>=
+case c of
+number_code,roman_numeral_code: scan_int;
+string_code, meaning_code: begin save_scanner_status:=scanner_status;
+ scanner_status:=normal; get_token; scanner_status:=save_scanner_status;
+ end;
+font_name_code: scan_font_ident;
+job_name_code: if job_name=0 then open_log_file;
+end {there are no other cases}
+
+@ @<Print the result of command |c|@>=
+case c of
+number_code: print_int(cur_val);
+roman_numeral_code: print_roman_int(cur_val);
+string_code:if cur_cs<>0 then sprint_cs(cur_cs)
+ else print_char(cur_chr);
+meaning_code: print_meaning;
+font_name_code: begin print(font_name[cur_val]);
+ if font_size[cur_val]<>font_dsize[cur_val] then
+ begin print(" at "); print_scaled(font_size[cur_val]);
+ print("pt");
+ end;
+ end;
+job_name_code: print(job_name);
+end {there are no other cases}
+
+@ Now we can't postpone the difficulties any longer; we must bravely tackle
+|scan_toks|. This function returns a pointer to the tail of a new token
+list, and it also makes |def_ref| point to the reference count at the
+head of that list.
+
+There are two boolean parameters, |macro_def| and |xpand|. If |macro_def|
+is true, the goal is to create the token list for a macro definition;
+otherwise the goal is to create the token list for some other \TeX\
+primitive: \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase},
+\.{\\uppercase}, \.{\\message}, \.{\\errmessage}, \.{\\write}, or
+\.{\\special}. In the latter cases a left brace must be scanned next; this
+left brace will not be part of the token list, nor will the matching right
+brace that comes at the end. If |xpand| is false, the token list will
+simply be copied from the input using |get_token|. Otherwise all expandable
+tokens will be expanded until unexpandable tokens are left, except that
+the results of expanding `\.{\\the}' are not expanded further.
+If both |macro_def| and |xpand| are true, the expansion applies
+only to the macro body (i.e., to the material following the first
+|left_brace| character).
+
+The value of |cur_cs| when |scan_toks| begins should be the |eqtb|
+address of the control sequence to display in ``runaway'' error
+messages.
+
+@p function scan_toks(@!macro_def,@!xpand:boolean):pointer;
+label found,done,done1,done2;
+var t:halfword; {token representing the highest parameter number}
+@!s:halfword; {saved token}
+@!p:pointer; {tail of the token list being built}
+@!q:pointer; {new node being added to the token list via |store_new_token|}
+@!unbalance:halfword; {number of unmatched left braces}
+@!hash_brace:halfword; {possible `\.{\#\{}' token}
+begin if macro_def then scanner_status:=defining
+@+else scanner_status:=absorbing;
+warning_index:=cur_cs; def_ref:=get_avail; token_ref_count(def_ref):=null;
+p:=def_ref; hash_brace:=0; t:=zero_token;
+if macro_def then @<Scan and build the parameter part of the macro definition@>
+else scan_left_brace; {remove the compulsory left brace}
+@<Scan and build the body of the token list; |goto found| when finished@>;
+found: scanner_status:=normal;
+if hash_brace<>0 then store_new_token(hash_brace);
+scan_toks:=p;
+end;
+
+@ @<Scan and build the parameter part...@>=
+begin loop begin get_token; {set |cur_cmd|, |cur_chr|, |cur_tok|}
+ if cur_tok<right_brace_limit then goto done1;
+ if cur_cmd=mac_param then
+ @<If the next character is a parameter number, make |cur_tok|
+ a |match| token; but if it is a left brace, store
+ `|left_brace|, |end_match|', set |hash_brace|, and |goto done|@>;
+ store_new_token(cur_tok);
+ end;
+done1: store_new_token(end_match_token);
+if cur_cmd=right_brace then
+ @<Express shock at the missing left brace; |goto found|@>;
+done: end
+
+@ @<Express shock...@>=
+begin print_err("Missing { inserted"); incr(align_state);
+@.Missing \{ inserted@>
+help2("Where was the left brace? You said something like `\def\a}',")@/
+ ("which I'm going to interpret as `\def\a{}'."); error; goto found;
+end
+
+@ @<If the next character is a parameter number...@>=
+begin s:=match_token+cur_chr; get_token;
+if cur_cmd=left_brace then
+ begin hash_brace:=cur_tok;
+ store_new_token(cur_tok); store_new_token(end_match_token);
+ goto done;
+ end;
+if t=zero_token+9 then
+ begin print_err("You already have nine parameters");
+@.You already have nine...@>
+ help1("I'm going to ignore the # sign you just used."); error;
+ end
+else begin incr(t);
+ if cur_tok<>t then
+ begin print_err("Parameters must be numbered consecutively");
+@.Parameters...consecutively@>
+ help2("I've inserted the digit you should have used after the #.")@/
+ ("Type `1' to delete what you did use."); back_error;
+ end;
+ cur_tok:=s;
+ end;
+end
+
+@ @<Scan and build the body of the token list; |goto found| when finished@>=
+unbalance:=1;
+loop@+ begin if xpand then @<Expand the next part of the input@>
+ else get_token;
+ if cur_tok<right_brace_limit then
+ if cur_cmd<right_brace then incr(unbalance)
+ else begin decr(unbalance);
+ if unbalance=0 then goto found;
+ end
+ else if cur_cmd=mac_param then
+ if macro_def then @<Look for parameter number or \.{\#\#}@>;
+ store_new_token(cur_tok);
+ end
+
+@ Here we insert an entire token list created by |the_toks| without
+expanding it further.
+
+@<Expand the next part of the input@>=
+begin loop begin get_next;
+ if cur_cmd<=max_command then goto done2;
+ if cur_cmd<>the then expand
+ else begin q:=the_toks;
+ if link(temp_head)<>null then
+ begin link(p):=link(temp_head); p:=q;
+ end;
+ end;
+ end;
+done2: x_token
+end
+
+@ @<Look for parameter number...@>=
+begin s:=cur_tok;
+if xpand then get_x_token else get_token;
+if cur_cmd<>mac_param then
+ if (cur_tok<=zero_token)or(cur_tok>t) then
+ begin print_err("Illegal parameter number in definition of ");
+@.Illegal parameter number...@>
+ sprint_cs(warning_index);
+ help3("You meant to type ## instead of #, right?")@/
+ ("Or maybe a } was forgotten somewhere earlier, and things")@/
+ ("are all screwed up? I'm going to assume that you meant ##.");
+ back_error; cur_tok:=s;
+ end
+ else cur_tok:=out_param_token-"0"+cur_chr;
+end
+
+@ Another way to create a token list is via the \.{\\read} command. The
+sixteen files potentially usable for reading appear in the following
+global variables. The value of |read_open[n]| will be |closed| if
+stream number |n| has not been opened or if it has been fully read;
+|just_open| if an \.{\\openin} but not a \.{\\read} has been done;
+and |normal| if it is open and ready to read the next line.
+
+@d closed=2 {not open, or at end of file}
+@d just_open=1 {newly opened, first line not yet read}
+
+@<Glob...@>=
+@!read_file:array[0..15] of alpha_file; {used for \.{\\read}}
+@!read_open:array[0..16] of normal..closed; {state of |read_file[n]|}
+
+@ @<Set init...@>=
+for k:=0 to 16 do read_open[k]:=closed;
+
+@ The |read_toks| procedure constructs a token list like that for any
+macro definition, and makes |cur_val| point to it. Parameter |r| points
+to the control sequence that will receive this token list.
+
+@p procedure read_toks(@!n:integer;@!r:pointer);
+label done;
+var p:pointer; {tail of the token list}
+@!q:pointer; {new node being added to the token list via |store_new_token|}
+@!s:integer; {saved value of |align_state|}
+@!m:small_number; {stream number}
+begin scanner_status:=defining; warning_index:=r;
+def_ref:=get_avail; token_ref_count(def_ref):=null;
+p:=def_ref; {the reference count}
+store_new_token(end_match_token);
+if (n<0)or(n>15) then m:=16@+else m:=n;
+s:=align_state; align_state:=1000000; {disable tab marks, etc.}
+repeat @<Input and store tokens from the next line of the file@>;
+until align_state=1000000;
+cur_val:=def_ref; scanner_status:=normal; align_state:=s;
+end;
+
+@ @<Input and store tokens from the next line of the file@>=
+begin_file_reading; name:=m+1;
+if read_open[m]=closed then @<Input for \.{\\read} from the terminal@>
+else if read_open[m]=just_open then @<Input the first line of |read_file[m]|@>
+else @<Input the next line of |read_file[m]|@>;
+limit:=last;
+if end_line_char_inactive then decr(limit)
+else buffer[limit]:=end_line_char;
+first:=limit+1; loc:=start; state:=new_line;@/
+loop@+ begin get_token;
+ if cur_tok=0 then goto done;
+ {|cur_cmd=cur_chr=0| will occur at the end of the line}
+ if align_state<1000000 then {unmatched `\.\}' aborts the line}
+ begin repeat get_token; until cur_tok=0;
+ align_state:=1000000; goto done;
+ end;
+ store_new_token(cur_tok);
+ end;
+done: end_file_reading
+
+@ Here we input on-line into the |buffer| array, prompting the user explicitly
+if |n>=0|. The value of |n| is set negative so that additional prompts
+will not be given in the case of multi-line input.
+
+@<Input for \.{\\read} from the terminal@>=
+if interaction>nonstop_mode then
+ if n<0 then prompt_input("")
+ else begin wake_up_terminal;
+ print_ln; sprint_cs(r); prompt_input("="); n:=-1;
+ end
+else fatal_error("*** (cannot \read from terminal in nonstop modes)")
+@.cannot \\read@>
+
+@ The first line of a file must be treated specially, since |input_ln|
+must be told not to start with |get|.
+@^system dependencies@>
+
+@<Input the first line of |read_file[m]|@>=
+if input_ln(read_file[m],false) then read_open[m]:=normal
+else begin a_close(read_file[m]); read_open[m]:=closed;
+ end
+
+@ An empty line is appended at the end of a |read_file|.
+@^empty line at end of file@>
+
+@<Input the next line of |read_file[m]|@>=
+begin if not input_ln(read_file[m],true) then
+ begin a_close(read_file[m]); read_open[m]:=closed;
+ if align_state<>1000000 then
+ begin runaway;
+ print_err("File ended within "); print_esc("read");
+@.File ended within \\read@>
+ help1("This \read has unbalanced braces.");
+ align_state:=1000000; error;
+ end;
+ end;
+end
+
+@* \[28] Conditional processing.
+We consider now the way \TeX\ handles various kinds of \.{\\if} commands.
+
+@d if_char_code=0 { `\.{\\if}' }
+@d if_cat_code=1 { `\.{\\ifcat}' }
+@d if_int_code=2 { `\.{\\ifnum}' }
+@d if_dim_code=3 { `\.{\\ifdim}' }
+@d if_odd_code=4 { `\.{\\ifodd}' }
+@d if_vmode_code=5 { `\.{\\ifvmode}' }
+@d if_hmode_code=6 { `\.{\\ifhmode}' }
+@d if_mmode_code=7 { `\.{\\ifmmode}' }
+@d if_inner_code=8 { `\.{\\ifinner}' }
+@d if_void_code=9 { `\.{\\ifvoid}' }
+@d if_hbox_code=10 { `\.{\\ifhbox}' }
+@d if_vbox_code=11 { `\.{\\ifvbox}' }
+@d ifx_code=12 { `\.{\\ifx}' }
+@d if_eof_code=13 { `\.{\\ifeof}' }
+@d if_true_code=14 { `\.{\\iftrue}' }
+@d if_false_code=15 { `\.{\\iffalse}' }
+@d if_case_code=16 { `\.{\\ifcase}' }
+
+@<Put each...@>=
+primitive("if",if_test,if_char_code);
+@!@:if_char_}{\.{\\if} primitive@>
+primitive("ifcat",if_test,if_cat_code);
+@!@:if_cat_code_}{\.{\\ifcat} primitive@>
+primitive("ifnum",if_test,if_int_code);
+@!@:if_int_}{\.{\\ifnum} primitive@>
+primitive("ifdim",if_test,if_dim_code);
+@!@:if_dim_}{\.{\\ifdim} primitive@>
+primitive("ifodd",if_test,if_odd_code);
+@!@:if_odd_}{\.{\\ifodd} primitive@>
+primitive("ifvmode",if_test,if_vmode_code);
+@!@:if_vmode_}{\.{\\ifvmode} primitive@>
+primitive("ifhmode",if_test,if_hmode_code);
+@!@:if_hmode_}{\.{\\ifhmode} primitive@>
+primitive("ifmmode",if_test,if_mmode_code);
+@!@:if_mmode_}{\.{\\ifmmode} primitive@>
+primitive("ifinner",if_test,if_inner_code);
+@!@:if_inner_}{\.{\\ifinner} primitive@>
+primitive("ifvoid",if_test,if_void_code);
+@!@:if_void_}{\.{\\ifvoid} primitive@>
+primitive("ifhbox",if_test,if_hbox_code);
+@!@:if_hbox_}{\.{\\ifhbox} primitive@>
+primitive("ifvbox",if_test,if_vbox_code);
+@!@:if_vbox_}{\.{\\ifvbox} primitive@>
+primitive("ifx",if_test,ifx_code);
+@!@:ifx_}{\.{\\ifx} primitive@>
+primitive("ifeof",if_test,if_eof_code);
+@!@:if_eof_}{\.{\\ifeof} primitive@>
+primitive("iftrue",if_test,if_true_code);
+@!@:if_true_}{\.{\\iftrue} primitive@>
+primitive("iffalse",if_test,if_false_code);
+@!@:if_false_}{\.{\\iffalse} primitive@>
+primitive("ifcase",if_test,if_case_code);
+@!@:if_case_}{\.{\\ifcase} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+if_test: case chr_code of
+ if_cat_code:print_esc("ifcat");
+ if_int_code:print_esc("ifnum");
+ if_dim_code:print_esc("ifdim");
+ if_odd_code:print_esc("ifodd");
+ if_vmode_code:print_esc("ifvmode");
+ if_hmode_code:print_esc("ifhmode");
+ if_mmode_code:print_esc("ifmmode");
+ if_inner_code:print_esc("ifinner");
+ if_void_code:print_esc("ifvoid");
+ if_hbox_code:print_esc("ifhbox");
+ if_vbox_code:print_esc("ifvbox");
+ ifx_code:print_esc("ifx");
+ if_eof_code:print_esc("ifeof");
+ if_true_code:print_esc("iftrue");
+ if_false_code:print_esc("iffalse");
+ if_case_code:print_esc("ifcase");
+ othercases print_esc("if")
+ endcases;
+
+@ Conditions can be inside conditions, and this nesting has a stack
+that is independent of the |save_stack|.
+
+Four global variables represent the top of the condition stack:
+|cond_ptr| points to pushed-down entries, if any; |if_limit| specifies
+the largest code of a |fi_or_else| command that is syntactically legal;
+|cur_if| is the name of the current type of conditional; and |if_line|
+is the line number at which it began.
+
+If no conditions are currently in progress, the condition stack has the
+special state |cond_ptr=null|, |if_limit=normal|, |cur_if=0|, |if_line=0|.
+Otherwise |cond_ptr| points to a two-word node; the |type|, |subtype|, and
+|link| fields of the first word contain |if_limit|, |cur_if|, and
+|cond_ptr| at the next level, and the second word contains the
+corresponding |if_line|.
+
+@d if_node_size=2 {number of words in stack entry for conditionals}
+@d if_line_field(#)==mem[#+1].int
+@d if_code=1 {code for \.{\\if...} being evaluated}
+@d fi_code=2 {code for \.{\\fi}}
+@d else_code=3 {code for \.{\\else}}
+@d or_code=4 {code for \.{\\or}}
+
+@<Glob...@>=
+@!cond_ptr:pointer; {top of the condition stack}
+@!if_limit:normal..or_code; {upper bound on |fi_or_else| codes}
+@!cur_if:small_number; {type of conditional being worked on}
+@!if_line:integer; {line where that conditional began}
+
+@ @<Set init...@>=
+cond_ptr:=null; if_limit:=normal; cur_if:=0; if_line:=0;
+
+@ @<Put each...@>=
+primitive("fi",fi_or_else,fi_code);
+@!@:fi_}{\.{\\fi} primitive@>
+text(frozen_fi):="fi"; eqtb[frozen_fi]:=eqtb[cur_val];
+primitive("or",fi_or_else,or_code);
+@!@:or_}{\.{\\or} primitive@>
+primitive("else",fi_or_else,else_code);
+@!@:else_}{\.{\\else} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+fi_or_else: if chr_code=fi_code then print_esc("fi")
+ else if chr_code=or_code then print_esc("or")
+ else print_esc("else");
+
+@ When we skip conditional text, we keep track of the line number
+where skipping began, for use in error messages.
+
+@<Glob...@>=
+@!skip_line:integer; {skipping began here}
+
+@ Here is a procedure that ignores text until coming to an \.{\\or},
+\.{\\else}, or \.{\\fi} at level zero of $\.{\\if}\ldots\.{\\fi}$
+nesting. After it has acted, |cur_chr| will indicate the token that
+was found, but |cur_tok| will not be set (because this makes the
+procedure run faster).
+
+@p procedure pass_text;
+label done;
+var l:integer; {level of $\.{\\if}\ldots\.{\\fi}$ nesting}
+@!save_scanner_status:small_number; {|scanner_status| upon entry}
+begin save_scanner_status:=scanner_status; scanner_status:=skipping; l:=0;
+skip_line:=line;
+loop@+ begin get_next;
+ if cur_cmd=fi_or_else then
+ begin if l=0 then goto done;
+ if cur_chr=fi_code then decr(l);
+ end
+ else if cur_cmd=if_test then incr(l);
+ end;
+done: scanner_status:=save_scanner_status;
+end;
+
+@ When we begin to process a new \.{\\if}, we set |if_limit:=if_code|; then
+if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if}
+condition has been evaluated, \.{\\relax} will be inserted.
+For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}'
+would otherwise require something after the `\.1'.
+
+@<Push the condition stack@>=
+begin p:=get_node(if_node_size); link(p):=cond_ptr; type(p):=if_limit;
+subtype(p):=cur_if; if_line_field(p):=if_line;
+cond_ptr:=p; cur_if:=cur_chr; if_limit:=if_code; if_line:=line;
+end
+
+@ @<Pop the condition stack@>=
+begin p:=cond_ptr; if_line:=if_line_field(p);
+cur_if:=subtype(p); if_limit:=type(p); cond_ptr:=link(p);
+free_node(p,if_node_size);
+end
+
+@ Here's a procedure that changes the |if_limit| code corresponding to
+a given value of |cond_ptr|.
+
+@p procedure change_if_limit(@!l:small_number;@!p:pointer);
+label exit;
+var q:pointer;
+begin if p=cond_ptr then if_limit:=l {that's the easy case}
+else begin q:=cond_ptr;
+ loop@+ begin if q=null then confusion("if");
+@:this can't happen if}{\quad if@>
+ if link(q)=p then
+ begin type(q):=l; return;
+ end;
+ q:=link(q);
+ end;
+ end;
+exit:end;
+
+@ A condition is started when the |expand| procedure encounters
+an |if_test| command; in that case |expand| reduces to |conditional|,
+which is a recursive procedure.
+@^recursion@>
+
+@p procedure conditional;
+label exit,common_ending;
+var b:boolean; {is the condition true?}
+@!r:"<"..">"; {relation to be evaluated}
+@!m,@!n:integer; {to be tested against the second operand}
+@!p,@!q:pointer; {for traversing token lists in \.{\\ifx} tests}
+@!save_scanner_status:small_number; {|scanner_status| upon entry}
+@!save_cond_ptr:pointer; {|cond_ptr| corresponding to this conditional}
+@!this_if:small_number; {type of this conditional}
+begin @<Push the condition stack@>;@+save_cond_ptr:=cond_ptr;this_if:=cur_chr;@/
+@<Either process \.{\\ifcase} or set |b| to the value of a boolean condition@>;
+if tracing_commands>1 then @<Display the value of |b|@>;
+if b then
+ begin change_if_limit(else_code,save_cond_ptr);
+ return; {wait for \.{\\else} or \.{\\fi}}
+ end;
+@<Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|@>;
+common_ending: if cur_chr=fi_code then @<Pop the condition stack@>
+else if_limit:=fi_code; {wait for \.{\\fi}}
+exit:end;
+
+@ In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first
+\.{\\else} that we come to after learning that the \.{\\if} is false is
+not the \.{\\else} we're looking for. Hence the following curious
+logic is needed.
+
+@ @<Skip to \.{\\else} or \.{\\fi}...@>=
+loop@+ begin pass_text;
+ if cond_ptr=save_cond_ptr then
+ begin if cur_chr<>or_code then goto common_ending;
+ print_err("Extra "); print_esc("or");
+@.Extra \\or@>
+ help1("I'm ignoring this; it doesn't match any \if.");
+ error;
+ end
+ else if cur_chr=fi_code then @<Pop the condition stack@>;
+ end
+
+@ @<Either process \.{\\ifcase} or set |b|...@>=
+case this_if of
+if_char_code, if_cat_code: @<Test if two characters match@>;
+if_int_code, if_dim_code: @<Test relation between integers or dimensions@>;
+if_odd_code: @<Test if an integer is odd@>;
+if_vmode_code: b:=(abs(mode)=vmode);
+if_hmode_code: b:=(abs(mode)=hmode);
+if_mmode_code: b:=(abs(mode)=mmode);
+if_inner_code: b:=(mode<0);
+if_void_code, if_hbox_code, if_vbox_code: @<Test box register status@>;
+ifx_code: @<Test if two tokens match@>;
+if_eof_code: begin scan_four_bit_int; b:=(read_open[cur_val]=closed);
+ end;
+if_true_code: b:=true;
+if_false_code: b:=false;
+if_case_code: @<Select the appropriate case
+ and |return| or |goto common_ending|@>;
+end {there are no other cases}
+
+@ @<Display the value of |b|@>=
+begin begin_diagnostic;
+if b then print("{true}")@+else print("{false}");
+end_diagnostic(false);
+end
+
+@ Here we use the fact that |"<"|, |"="|, and |">"| are consecutive ASCII
+codes.
+@^ASCII code@>
+
+@<Test relation between integers or dimensions@>=
+begin if this_if=if_int_code then scan_int@+else scan_normal_dimen;
+n:=cur_val; @<Get the next non-blank non-call...@>;
+if (cur_tok>=other_token+"<")and(cur_tok<=other_token+">") then
+ r:=cur_tok-other_token
+else begin print_err("Missing = inserted for ");
+@.Missing = inserted@>
+ print_cmd_chr(if_test,this_if);
+ help1("I was expecting to see `<', `=', or `>'. Didn't.");
+ back_error; r:="=";
+ end;
+if this_if=if_int_code then scan_int@+else scan_normal_dimen;
+case r of
+"<": b:=(n<cur_val);
+"=": b:=(n=cur_val);
+">": b:=(n>cur_val);
+end;
+end
+
+@ @<Test if an integer is odd@>=
+begin scan_int; b:=odd(cur_val);
+end
+
+@ @<Test box register status@>=
+begin scan_eight_bit_int; p:=box(cur_val);
+if this_if=if_void_code then b:=(p=null)
+else if p=null then b:=false
+else if this_if=if_hbox_code then b:=(type(p)=hlist_node)
+else b:=(type(p)=vlist_node);
+end
+
+@ An active character will be treated as category 13 following
+\.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}. We use the fact that
+active characters have the smallest tokens, among all control sequences.
+
+@d get_x_token_or_active_char==@t@>@;
+ begin get_x_token;
+ if cur_cmd=relax then if cur_chr=no_expand_flag then
+ begin cur_cmd:=active_char;
+ cur_chr:=cur_tok-cs_token_flag-active_base;
+ end;
+ end
+
+@<Test if two characters match@>=
+begin get_x_token_or_active_char;
+if (cur_cmd>active_char)or(cur_chr>255) then {not a character}
+ begin m:=relax; n:=256;
+ end
+else begin m:=cur_cmd; n:=cur_chr;
+ end;
+get_x_token_or_active_char;
+if (cur_cmd>active_char)or(cur_chr>255) then
+ begin cur_cmd:=relax; cur_chr:=256;
+ end;
+if this_if=if_char_code then b:=(n=cur_chr)@+else b:=(m=cur_cmd);
+end
+
+@ Note that `\.{\\ifx}' will declare two macros different if one is \\{long}
+or \\{outer} and the other isn't, even though the texts of the macros are
+the same.
+
+We need to reset |scanner_status|, since \.{\\outer} control sequences
+are allowed, but we might be scanning a macro definition or preamble.
+
+@<Test if two tokens match@>=
+begin save_scanner_status:=scanner_status; scanner_status:=normal;
+get_next; n:=cur_cs; p:=cur_cmd; q:=cur_chr;
+get_next; if cur_cmd<>p then b:=false
+else if cur_cmd<call then b:=(cur_chr=q)
+else @<Test if two macro texts match@>;
+scanner_status:=save_scanner_status;
+end
+
+@ Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are
+different in examples like this:
+$$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr
+ {}\\def\\a\{\\c\}&
+ {}\\def\\c\{\}\cr
+ {}\\def\\b\{\\d\}&
+ {}\\def\\d\{\}\cr}}$$
+
+@<Test if two macro texts match@>=
+begin p:=link(cur_chr); q:=link(equiv(n)); {omit reference counts}
+if p=q then b:=true
+else begin while (p<>null)and(q<>null) do
+ if info(p)<>info(q) then p:=null
+ else begin p:=link(p); q:=link(q);
+ end;
+ b:=((p=null)and(q=null));
+ end;
+end
+
+@ @<Select the appropriate case and |return| or |goto common_ending|@>=
+begin scan_int; n:=cur_val; {|n| is the number of cases to pass}
+if tracing_commands>1 then
+ begin begin_diagnostic; print("{case "); print_int(n); print_char("}");
+ end_diagnostic(false);
+ end;
+while n<>0 do
+ begin pass_text;
+ if cond_ptr=save_cond_ptr then
+ if cur_chr=or_code then decr(n)
+ else goto common_ending
+ else if cur_chr=fi_code then @<Pop the condition stack@>;
+ end;
+change_if_limit(or_code,save_cond_ptr);
+return; {wait for \.{\\or}, \.{\\else}, or \.{\\fi}}
+end
+
+@ The processing of conditionals is complete except for the following
+code, which is actually part of |expand|. It comes into play when
+\.{\\or}, \.{\\else}, or \.{\\fi} is scanned.
+
+@<Terminate the current conditional and skip to \.{\\fi}@>=
+if cur_chr>if_limit then
+ if if_limit=if_code then insert_relax {condition not yet evaluated}
+ else begin print_err("Extra "); print_cmd_chr(fi_or_else,cur_chr);
+@.Extra \\or@>
+@.Extra \\else@>
+@.Extra \\fi@>
+ help1("I'm ignoring this; it doesn't match any \if.");
+ error;
+ end
+else begin while cur_chr<>fi_code do pass_text; {skip to \.{\\fi}}
+ @<Pop the condition stack@>;
+ end
+
+@* \[29] File names.
+It's time now to fret about file names. Besides the fact that different
+operating systems treat files in different ways, we must cope with the
+fact that completely different naming conventions are used by different
+groups of people. The following programs show what is required for one
+particular operating system; similar routines for other systems are not
+difficult to devise.
+@^fingers@>
+@^system dependencies@>
+
+\TeX\ assumes that a file name has three parts: the name proper; its
+``extension''; and a ``file area'' where it is found in an external file
+system. The extension of an input file or a write file is assumed to be
+`\.{.tex}' unless otherwise specified; it is `\.{.log}' on the
+transcript file that records each run of \TeX; it is `\.{.tfm}' on the font
+metric files that describe characters in the fonts \TeX\ uses; it is
+`\.{.dvi}' on the output files that specify typesetting information; and it
+is `\.{.fmt}' on the format files written by \.{INITEX} to initialize \TeX.
+The file area can be arbitrary on input files, but files are usually
+output to the user's current area. If an input file cannot be
+found on the specified area, \TeX\ will look for it on a special system
+area; this special area is intended for commonly used input files like
+\.{webmac.tex}.
+
+Simple uses of \TeX\ refer only to file names that have no explicit
+extension or area. For example, a person usually says `\.{\\input} \.{paper}'
+or `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input}
+\.{paper.new}' or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file
+names are best, because they make the \TeX\ source files portable;
+whenever a file name consists entirely of letters and digits, it should be
+treated in the same way by all implementations of \TeX. However, users
+need the ability to refer to other files in their environment, especially
+when responding to error messages concerning unopenable files; therefore
+we want to let them use the syntax that appears in their favorite
+operating system.
+
+The following procedures don't allow spaces to be part of
+file names; but some users seem to like names that are spaced-out.
+System-dependent changes to allow such things should probably
+be made with reluctance, and only when an entire file name that
+includes spaces is ``quoted'' somehow.
+
+@ In order to isolate the system-dependent aspects of file names, the
+@^system dependencies@>
+system-independent parts of \TeX\ are expressed in terms
+of three system-dependent
+procedures called |begin_name|, |more_name|, and |end_name|. In
+essence, if the user-specified characters of the file name are $c_1\ldots c_n$,
+the system-independent driver program does the operations
+$$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n);
+\,|end_name|.$$
+These three procedures communicate with each other via global variables.
+Afterwards the file name will appear in the string pool as three strings
+called |cur_name|\penalty10000\hskip-.05em,
+|cur_area|, and |cur_ext|; the latter two are null (i.e.,
+|""|), unless they were explicitly specified by the user.
+
+Actually the situation is slightly more complicated, because \TeX\ needs
+to know when the file name ends. The |more_name| routine is a function
+(with side effects) that returns |true| on the calls |more_name|$(c_1)$,
+\dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$
+returns |false|; or, it returns |true| and the token following $c_n$ is
+something like `\.{\\hbox}' (i.e., not a character). In other words,
+|more_name| is supposed to return |true| unless it is sure that the
+file name has been completely scanned; and |end_name| is supposed to be able
+to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
+whether $|more_name|(c_n)$ returned |true| or |false|.
+
+@<Glob...@>=
+@!cur_name:str_number; {name of file just scanned}
+@!cur_area:str_number; {file area just scanned, or \.{""}}
+@!cur_ext:str_number; {file extension just scanned, or \.{""}}
+
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+
+@ Input files that can't be found in the user's area may appear in a standard
+system area called |TEX_area|. Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|TEX_font_area|. These system area names will, of course, vary from place
+to place.
+@^system dependencies@>
+
+@d TEX_area=="TeXinputs:"
+@.TeXinputs@>
+@d TEX_font_area=="TeXfonts:"
+@.TeXfonts@>
+
+@ Here now is the first of the system-dependent routines for file name scanning.
+@^system dependencies@>
+
+@p procedure begin_name;
+begin area_delimiter:=0; ext_delimiter:=0;
+end;
+
+@ And here's the second. The string pool might change as the file name is
+being scanned, since a new \.{\\csname} might be entered; therefore we keep
+|area_delimiter| and |ext_delimiter| relative to the beginning of the current
+string, instead of assigning an absolute address like |pool_ptr| to them.
+@^system dependencies@>
+
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin str_room(1); append_char(c); {contribute |c| to the current string}
+ if (c=">")or(c=":") then
+ begin area_delimiter:=cur_length; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=cur_length;
+ more_name:=true;
+ end;
+end;
+
+@ The third.
+@^system dependencies@>
+
+@p procedure end_name;
+begin if str_ptr+3>max_strings then
+ overflow("number of strings",max_strings-init_str_ptr);
+@:TeX capacity exceeded number of strings}{\quad number of strings@>
+if area_delimiter=0 then cur_area:=""
+else begin cur_area:=str_ptr;
+ str_start[str_ptr+1]:=str_start[str_ptr]+area_delimiter; incr(str_ptr);
+ end;
+if ext_delimiter=0 then
+ begin cur_ext:=""; cur_name:=make_string;
+ end
+else begin cur_name:=str_ptr;
+ str_start[str_ptr+1]:=str_start[str_ptr]+ext_delimiter-area_delimiter-1;
+ incr(str_ptr); cur_ext:=make_string;
+ end;
+end;
+
+@ Conversely, here is a routine that takes three strings and prints a file
+name that might have produced them. (The routine is system dependent, because
+some operating systems put the file area last instead of first.)
+@^system dependencies@>
+
+@<Basic printing...@>=
+procedure print_file_name(@!n,@!a,@!e:integer);
+begin slow_print(a); slow_print(n); slow_print(e);
+end;
+
+@ Another system-dependent routine is needed to convert three internal
+\TeX\ strings
+into the |name_of_file| value that is used to open files. The present code
+allows both lowercase and uppercase letters in the file name.
+@^system dependencies@>
+
+@d append_to_name(#)==begin c:=#; incr(k);
+ if k<=file_name_size then name_of_file[k]:=xchr[c];
+ end
+
+@p procedure pack_file_name(@!n,@!a,@!e:str_number);
+var k:integer; {number of positions filled in |name_of_file|}
+@!c: ASCII_code; {character being packed}
+@!j:pool_pointer; {index into |str_pool|}
+begin k:=0;
+for j:=str_start[a] to str_start[a+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[n] to str_start[n+1]-1 do append_to_name(so(str_pool[j]));
+for j:=str_start[e] to str_start[e+1]-1 do append_to_name(so(str_pool[j]));
+if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
+for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
+end;
+
+@ A messier routine is also needed, since format file names must be scanned
+before \TeX's string mechanism has been initialized. We shall use the
+global variable |TEX_format_default| to supply the text for default system areas
+and extensions related to format files.
+@^system dependencies@>
+
+@d format_default_length=20 {length of the |TEX_format_default| string}
+@d format_area_length=11 {length of its area part}
+@d format_ext_length=4 {length of its `\.{.fmt}' part}
+@d format_extension=".fmt" {the extension, as a \.{WEB} constant}
+
+@<Glob...@>=
+@!TEX_format_default:packed array[1..format_default_length] of char;
+
+@ @<Set init...@>=
+TEX_format_default:='TeXformats:plain.fmt';
+@.TeXformats@>
+@.plain@>
+@^system dependencies@>
+
+@ @<Check the ``constant'' values for consistency@>=
+if format_default_length>file_name_size then bad:=31;
+
+@ Here is the messy routine that was just mentioned. It sets |name_of_file|
+from the first |n| characters of |TEX_format_default|, followed by
+|buffer[a..b]|, followed by the last |format_ext_length| characters of
+|TEX_format_default|.
+
+We dare not give error messages here, since \TeX\ calls this routine before
+the |error| routine is ready to roll. Instead, we simply drop excess characters,
+since the error will be detected in another way when a strange file name
+isn't found.
+@^system dependencies@>
+
+@p procedure pack_buffered_name(@!n:small_number;@!a,@!b:integer);
+var k:integer; {number of positions filled in |name_of_file|}
+@!c: ASCII_code; {character being packed}
+@!j:integer; {index into |buffer| or |TEX_format_default|}
+begin if n+b-a+1+format_ext_length>file_name_size then
+ b:=a+file_name_size-n-1-format_ext_length;
+k:=0;
+for j:=1 to n do append_to_name(xord[TEX_format_default[j]]);
+for j:=a to b do append_to_name(buffer[j]);
+for j:=format_default_length-format_ext_length+1 to format_default_length do
+ append_to_name(xord[TEX_format_default[j]]);
+if k<=file_name_size then name_length:=k@+else name_length:=file_name_size;
+for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
+end;
+
+@ Here is the only place we use |pack_buffered_name|. This part of the program
+becomes active when a ``virgin'' \TeX\ is trying to get going, just after
+the preliminary initialization, or when the user is substituting another
+format file by typing `\.\&' after the initial `\.{**}' prompt. The buffer
+contains the first line of input in |buffer[loc..(last-1)]|, where
+|loc<last| and |buffer[loc]<>" "|.
+
+@<Declare the function called |open_fmt_file|@>=
+function open_fmt_file:boolean;
+label found,exit;
+var j:0..buf_size; {the first space after the format file name}
+begin j:=loc;
+if buffer[loc]="&" then
+ begin incr(loc); j:=loc; buffer[last]:=" ";
+ while buffer[j]<>" " do incr(j);
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(fmt_file) then goto found;
+ pack_buffered_name(format_area_length,loc,j-1);
+ {now try the system format file area}
+ if w_open_in(fmt_file) then goto found;
+ wake_up_terminal;
+ wterm_ln('Sorry, I can''t find that format;',' will try PLAIN.');
+@.Sorry, I can't find...@>
+ update_terminal;
+ end;
+ {now pull out all the stops: try for the system \.{plain} file}
+pack_buffered_name(format_default_length-format_ext_length,1,0);
+if not w_open_in(fmt_file) then
+ begin wake_up_terminal;
+ wterm_ln('I can''t find the PLAIN format file!');
+@.I can't find PLAIN...@>
+@.plain@>
+ open_fmt_file:=false; return;
+ end;
+found:loc:=j; open_fmt_file:=true;
+exit:end;
+
+@ Operating systems often make it possible to determine the exact name (and
+possible version number) of a file that has been opened. The following routine,
+which simply makes a \TeX\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var k:1..file_name_size; {index into |name_of_file|}
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings)or
+ (cur_length>0) then
+ make_name_string:="?"
+else begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+end;
+function a_make_name_string(var f:alpha_file):str_number;
+begin a_make_name_string:=make_name_string;
+end;
+function b_make_name_string(var f:byte_file):str_number;
+begin b_make_name_string:=make_name_string;
+end;
+function w_make_name_string(var f:word_file):str_number;
+begin w_make_name_string:=make_name_string;
+end;
+
+@ Now let's consider the ``driver''
+routines by which \TeX\ deals with file names
+in a system-independent manner. First comes a procedure that looks for a
+file name in the input by calling |get_x_token| for the information.
+
+@p procedure scan_file_name;
+label done;
+begin name_in_progress:=true; begin_name;
+@<Get the next non-blank non-call...@>;
+loop@+begin if (cur_cmd>other_char)or(cur_chr>255) then {not a character}
+ begin back_input; goto done;
+ end;
+ if not more_name(cur_chr) then goto done;
+ get_x_token;
+ end;
+done: end_name; name_in_progress:=false;
+end;
+
+@ The global variable |name_in_progress| is used to prevent recursive
+use of |scan_file_name|, since the |begin_name| and other procedures
+communicate via global variables. Recursion would arise only by
+devious tricks like `\.{\\input\\input f}'; such attempts at sabotage
+must be thwarted. Furthermore, |name_in_progress| prevents \.{\\input}
+@^recursion@>
+from being initiated when a font size specification is being scanned.
+
+Another global variable, |job_name|, contains the file name that was first
+\.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}'
+and `\.{.fmt}' in the names of \TeX's output files.
+
+@<Glob...@>=
+@!name_in_progress:boolean; {is a file name being scanned?}
+@!job_name:str_number; {principal file name}
+@!log_opened:boolean; {has the transcript file been opened?}
+
+@ Initially |job_name=0|; it becomes nonzero as soon as the true name is known.
+We have |job_name=0| if and only if the `\.{log}' file has not been opened,
+except of course for a short time just after |job_name| has become nonzero.
+
+@<Initialize the output...@>=
+job_name:=0; name_in_progress:=false; log_opened:=false;
+
+@ Here is a routine that manufactures the output file names, assuming that
+|job_name<>0|. It ignores and changes the current settings of |cur_area|
+and |cur_ext|.
+
+@d pack_cur_name==pack_file_name(cur_name,cur_area,cur_ext)
+
+@p procedure pack_job_name(@!s:str_number); {|s = ".log"|, |".dvi"|, or
+ |format_extension|}
+begin cur_area:=""; cur_ext:=s;
+cur_name:=job_name; pack_cur_name;
+end;
+
+@ If some trouble arises when \TeX\ tries to open a file, the following
+routine calls upon the user to supply another file name. Parameter~|s|
+is used in the error message to identify the type of file; parameter~|e|
+is the default extension if none is given. Upon exit from the routine,
+variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are
+ready for another attempt at file opening.
+
+@p procedure prompt_file_name(@!s,@!e:str_number);
+label done;
+var k:0..buf_size; {index into |buffer|}
+begin if interaction=scroll_mode then wake_up_terminal;
+if s="input file name" then print_err("I can't find file `")
+@.I can't find file x@>
+else print_err("I can't write on file `");
+@.I can't write on file x@>
+print_file_name(cur_name,cur_area,cur_ext); print("'.");
+if e=".tex" then show_context;
+print_nl("Please type another "); print(s);
+@.Please type...@>
+if interaction<scroll_mode then
+ fatal_error("*** (job aborted, file error in nonstop mode)");
+@.job aborted, file error...@>
+clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>;
+if cur_ext="" then cur_ext:=e;
+pack_cur_name;
+end;
+
+@ @<Scan file name in the buffer@>=
+begin begin_name; k:=first;
+while (buffer[k]=" ")and(k<last) do incr(k);
+loop@+ begin if k=last then goto done;
+ if not more_name(buffer[k]) then goto done;
+ incr(k);
+ end;
+done:end_name;
+end
+
+@ Here's an example of how these conventions are used. Whenever it is time to
+ship out a box of stuff, we shall use the macro |ensure_dvi_open|.
+
+@d ensure_dvi_open==if output_file_name=0 then
+ begin if job_name=0 then open_log_file;
+ pack_job_name(".dvi");
+ while not b_open_out(dvi_file) do
+ prompt_file_name("file name for output",".dvi");
+ output_file_name:=b_make_name_string(dvi_file);
+ end
+
+@<Glob...@>=
+@!dvi_file: byte_file; {the device-independent output goes here}
+@!output_file_name: str_number; {full name of the output file}
+@!log_name:str_number; {full name of the log file}
+
+@ @<Initialize the output...@>=output_file_name:=0;
+
+@ The |open_log_file| routine is used to open the transcript file and to help
+it catch up to what has previously been printed on the terminal.
+
+@p procedure open_log_file;
+var old_setting:0..max_selector; {previous |selector| setting}
+@!k:0..buf_size; {index into |months| and |buffer|}
+@!l:0..buf_size; {end of first input line}
+@!months:packed array [1..36] of char; {abbreviations of month names}
+begin old_setting:=selector;
+if job_name=0 then job_name:="texput";
+@.texput@>
+pack_job_name(".log");
+while not a_open_out(log_file) do @<Try to get a different log file name@>;
+log_name:=a_make_name_string(log_file);
+selector:=log_only; log_opened:=true;
+@<Print the banner line, including the date and time@>;
+input_stack[input_ptr]:=cur_input; {make sure bottom level is in memory}
+print_nl("**");
+@.**@>
+l:=input_stack[0].limit_field; {last position of first line}
+if buffer[l]=end_line_char then decr(l);
+for k:=1 to l do print(buffer[k]);
+print_ln; {now the transcript file contains the first line of input}
+selector:=old_setting+2; {|log_only| or |term_and_log|}
+end;
+
+@ Sometimes |open_log_file| is called at awkward moments when \TeX\ is
+unable to print error messages or even to |show_context|.
+The |prompt_file_name| routine can result in a |fatal_error|, but the |error|
+routine will not be invoked because |log_opened| will be false.
+
+The normal idea of |batch_mode| is that nothing at all should be written
+on the terminal. However, in the unusual case that
+no log file could be opened, we make an exception and allow
+an explanatory message to be seen.
+
+Incidentally, the program always refers to the log file as a `\.{transcript
+file}', because some systems cannot use the extension `\.{.log}' for
+this file.
+
+@<Try to get a different log file name@>=
+begin selector:=term_only;
+prompt_file_name("transcript file name",".log");
+end
+
+@ @<Print the banner...@>=
+begin wlog(banner);
+slow_print(format_ident); print(" ");
+print_int(day); print_char(" ");
+months:='JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC';
+for k:=3*month-2 to 3*month do wlog(months[k]);
+print_char(" "); print_int(year); print_char(" ");
+print_two(time div 60); print_char(":"); print_two(time mod 60);
+end
+
+@ Let's turn now to the procedure that is used to initiate file reading
+when an `\.{\\input}' command is being processed.
+
+@p procedure start_input; {\TeX\ will \.{\\input} something}
+label done;
+begin scan_file_name; {set |cur_name| to desired file name}
+if cur_ext="" then cur_ext:=".tex";
+pack_cur_name;
+loop@+ begin begin_file_reading; {set up |cur_file| and new level of input}
+ if a_open_in(cur_file) then goto done;
+ if cur_area="" then
+ begin pack_file_name(cur_name,TEX_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+ end;
+ end_file_reading; {remove the level that didn't work}
+ prompt_file_name("input file name",".tex");
+ end;
+done: name:=a_make_name_string(cur_file);
+if job_name=0 then
+ begin job_name:=cur_name; open_log_file;
+ end; {|open_log_file| doesn't |show_context|, so |limit|
+ and |loc| needn't be set to meaningful values yet}
+if term_offset+length(name)>max_print_line-2 then print_ln
+else if (term_offset>0)or(file_offset>0) then print_char(" ");
+print_char("("); incr(open_parens); slow_print(name); update_terminal;
+state:=new_line;
+if name=str_ptr-1 then {we can conserve string pool space now}
+ begin flush_string; name:=cur_name;
+ end;
+@<Read the first line of the new file@>;
+end;
+
+@ Here we have to remember to tell the |input_ln| routine not to
+start with a |get|. If the file is empty, it is considered to
+contain a single blank line.
+@^system dependencies@>
+@^empty line at end of file@>
+
+@<Read the first line...@>=
+begin line:=1;
+if input_ln(cur_file,false) then do_nothing;
+firm_up_the_line;
+if end_line_char_inactive then decr(limit)
+else buffer[limit]:=end_line_char;
+first:=limit+1; loc:=start;
+end
+
+@* \[30] Font metric data.
+\TeX\ gets its knowledge about fonts from font metric files, also called
+\.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX,
+but other programs know about them too.
+@:TFM files}{\.{TFM} files@>
+@^font metric files@>
+
+The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
+Since the number of bytes is always a multiple of 4, we could
+also regard the file as a sequence of 32-bit words, but \TeX\ uses the
+byte interpretation. The format of \.{TFM} files was designed by
+Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds
+@^Ramshaw, Lyle Harold@>
+of information in a compact but useful form.
+
+@<Glob...@>=
+@!tfm_file:byte_file;
+
+@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
+integers that give the lengths of the various subsequent portions
+of the file. These twelve integers are, in order:
+$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
+|lf|&length of the entire file, in words;\cr
+|lh|&length of the header data, in words;\cr
+|bc|&smallest character code in the font;\cr
+|ec|&largest character code in the font;\cr
+|nw|&number of words in the width table;\cr
+|nh|&number of words in the height table;\cr
+|nd|&number of words in the depth table;\cr
+|ni|&number of words in the italic correction table;\cr
+|nl|&number of words in the lig/kern table;\cr
+|nk|&number of words in the kern table;\cr
+|ne|&number of words in the extensible character table;\cr
+|np|&number of font parameter words.\cr}}$$
+They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
+and
+$$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
+Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
+and as few as 0 characters (if |bc=ec+1|).
+
+Incidentally, when two or more 8-bit bytes are combined to form an integer of
+16 or more bits, the most significant bytes appear first in the file.
+This is called BigEndian order.
+@!@^BigEndian order@>
+
+@ The rest of the \.{TFM} file may be regarded as a sequence of ten data
+arrays having the informal specification
+$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
+\vbox{\halign{\hfil\\{#}&$\,:\,$\arr#\hfil\cr
+header&|[0..lh-1]@t\\{stuff}@>|\cr
+char\_info&|[bc..ec]char_info_word|\cr
+width&|[0..nw-1]fix_word|\cr
+height&|[0..nh-1]fix_word|\cr
+depth&|[0..nd-1]fix_word|\cr
+italic&|[0..ni-1]fix_word|\cr
+lig\_kern&|[0..nl-1]lig_kern_command|\cr
+kern&|[0..nk-1]fix_word|\cr
+exten&|[0..ne-1]extensible_recipe|\cr
+param&|[1..np]fix_word|\cr}}$$
+The most important data type used here is a |@!fix_word|, which is
+a 32-bit representation of a binary fraction. A |fix_word| is a signed
+quantity, with the two's complement of the entire word used to represent
+negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
+binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
+the smallest is $-2048$. We will see below, however, that all but two of
+the |fix_word| values must lie between $-16$ and $+16$.
+
+@ The first data array is a block of header information, which contains
+general facts about the font. The header must contain at least two words,
+|header[0]| and |header[1]|, whose meaning is explained below.
+Additional header information of use to other software routines might
+also be included, but \TeX82 does not need to know about such details.
+For example, 16 more words of header information are in use at the Xerox
+Palo Alto Research Center; the first ten specify the character coding
+scheme used (e.g., `\.{XEROX text}' or `\.{TeX math symbols}'), the next five
+give the font identifier (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
+last gives the ``face byte.'' The program that converts \.{DVI} files
+to Xerox printing format gets this information by looking at the \.{TFM}
+file, which it needs to read anyway because of other information that
+is not explicitly repeated in \.{DVI}~format.
+
+\yskip\hang|header[0]| is a 32-bit check sum that \TeX\ will copy into
+the \.{DVI} output file. Later on when the \.{DVI} file is printed,
+possibly on another computer, the actual font that gets used is supposed
+to have a check sum that agrees with the one in the \.{TFM} file used by
+\TeX. In this way, users will be warned about potential incompatibilities.
+(However, if the check sum is zero in either the font file or the \.{TFM}
+file, no check is made.) The actual relation between this check sum and
+the rest of the \.{TFM} file is not important; the check sum is simply an
+identification number with the property that incompatible fonts almost
+always have distinct check sums.
+@^check sum@>
+
+\yskip\hang|header[1]| is a |fix_word| containing the design size of
+the font, in units of \TeX\ points. This number must be at least 1.0; it is
+fairly arbitrary, but usually the design size is 10.0 for a ``10 point''
+font, i.e., a font that was designed to look best at a 10-point size,
+whatever that really means. When a \TeX\ user asks for a font
+`\.{at} $\delta$ \.{pt}', the effect is to override the design size
+and replace it by $\delta$, and to multiply the $x$ and~$y$ coordinates
+of the points in the font image by a factor of $\delta$ divided by the
+design size. {\sl All other dimensions in the\/ \.{TFM} file are
+|fix_word|\kern-1pt\ numbers in design-size units}, with the exception of
+|param[1]| (which denotes the slant ratio). Thus, for example, the value
+of |param[6]|, which defines the \.{em} unit, is often the |fix_word| value
+$2^{20}=1.0$, since many fonts have a design size equal to one em.
+The other dimensions must be less than 16 design-size units in absolute
+value; thus, |header[1]| and |param[1]| are the only |fix_word|
+entries in the whole \.{TFM} file whose first byte might be something
+besides 0 or 255.
+
+@ Next comes the |char_info| array, which contains one |@!char_info_word|
+per character. Each word in this part of the file contains six fields
+packed into four bytes as follows.
+
+\yskip\hang first byte: |@!width_index| (8 bits)\par
+\hang second byte: |@!height_index| (4 bits) times 16, plus |@!depth_index|
+ (4~bits)\par
+\hang third byte: |@!italic_index| (6 bits) times 4, plus |@!tag|
+ (2~bits)\par
+\hang fourth byte: |@!remainder| (8 bits)\par
+\yskip\noindent
+The actual width of a character is \\{width}|[width_index]|, in design-size
+units; this is a device for compressing information, since many characters
+have the same width. Since it is quite common for many characters
+to have the same height, depth, or italic correction, the \.{TFM} format
+imposes a limit of 16 different heights, 16 different depths, and
+64 different italic corrections.
+
+@!@^italic correction@>
+The italic correction of a character has two different uses.
+(a)~In ordinary text, the italic correction is added to the width only if
+the \TeX\ user specifies `\.{\\/}' after the character.
+(b)~In math formulas, the italic correction is always added to the width,
+except with respect to the positioning of subscripts.
+
+Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
+\\{italic}[0]=0$ should always hold, so that an index of zero implies a
+value of zero. The |width_index| should never be zero unless the
+character does not exist in the font, since a character is valid if and
+only if it lies between |bc| and |ec| and has a nonzero |width_index|.
+
+@ The |tag| field in a |char_info_word| has four values that explain how to
+interpret the |remainder| field.
+
+\yskip\hangg|tag=0| (|no_tag|) means that |remainder| is unused.\par
+\hangg|tag=1| (|lig_tag|) means that this character has a ligature/kerning
+program starting at position |remainder| in the |lig_kern| array.\par
+\hangg|tag=2| (|list_tag|) means that this character is part of a chain of
+characters of ascending sizes, and not the largest in the chain. The
+|remainder| field gives the character code of the next larger character.\par
+\hangg|tag=3| (|ext_tag|) means that this character code represents an
+extensible character, i.e., a character that is built up of smaller pieces
+so that it can be made arbitrarily large. The pieces are specified in
+|@!exten[remainder]|.\par
+\yskip\noindent
+Characters with |tag=2| and |tag=3| are treated as characters with |tag=0|
+unless they are used in special circumstances in math formulas. For example,
+the \.{\\sum} operation looks for a |list_tag|, and the \.{\\left}
+operation looks for both |list_tag| and |ext_tag|.
+
+@d no_tag=0 {vanilla character}
+@d lig_tag=1 {character has a ligature/kerning program}
+@d list_tag=2 {character has a successor in a charlist}
+@d ext_tag=3 {character is extensible}
+
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word in this array is a
+|@!lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+ step if the byte is 128 or more, otherwise the next step is obtained by
+ skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+ then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+ a kern step otherwise.\par
+\hang fourth byte: |remainder|.\par
+\yskip\noindent
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|remainder| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+If the very first instruction of the |lig_kern| array has |skip_byte=255|,
+the |next_char| byte is the so-called right boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte=255|,
+there is a special ligature/kerning program for a left boundary character,
+beginning at location |256*op_byte+remainder|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte>128|, the program actually begins in location
+|256*op_byte+remainder|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location |<=255|.
+
+Any instruction with |skip_byte>128| in the |lig_kern| array must satisfy
+the condition
+$$\hbox{|256*op_byte+remainder<nl|.}$$
+If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+or kerning command is performed.
+
+@d stop_flag==qi(128) {value indicating `\.{STOP}' in a lig/kern program}
+@d kern_flag==qi(128) {op code for a kern step}
+@d skip_byte(#)==#.b0
+@d next_char(#)==#.b1
+@d op_byte(#)==#.b2
+@d rem_byte(#)==#.b3
+
+@ Extensible characters are specified by an |@!extensible_recipe|, which
+consists of four bytes called |@!top|, |@!mid|, |@!bot|, and |@!rep| (in this
+order). These bytes are the character codes of individual pieces used to
+build up a large symbol. If |top|, |mid|, or |bot| are zero, they are not
+present in the built-up result. For example, an extensible vertical line is
+like an extensible bracket, except that the top and bottom pieces are missing.
+
+Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box
+if the piece isn't present. Then the extensible characters have the form
+$TR^kMR^kB$ from top to bottom, for some |k>=0|, unless $M$ is absent;
+in the latter case we can have $TR^kB$ for both even and odd values of~|k|.
+The width of the extensible character is the width of $R$; and the
+height-plus-depth is the sum of the individual height-plus-depths of the
+components used, since the pieces are butted together in a vertical list.
+
+@d ext_top(#)==#.b0 {|top| piece in a recipe}
+@d ext_mid(#)==#.b1 {|mid| piece in a recipe}
+@d ext_bot(#)==#.b2 {|bot| piece in a recipe}
+@d ext_rep(#)==#.b3 {|rep| piece in a recipe}
+
+@ The final portion of a \.{TFM} file is the |param| array, which is another
+sequence of |fix_word| values.
+
+\yskip\hang|param[1]=slant| is the amount of italic slant, which is used
+to help position accents. For example, |slant=.25| means that when you go
+up one unit, you also go .25 units to the right. The |slant| is a pure
+number; it's the only |fix_word| other than the design size itself that is
+not scaled by the design size.
+
+\hang|param[2]=space| is the normal spacing between words in text.
+Note that character |" "| in the font need not have anything to do with
+blank spaces.
+
+\hang|param[3]=space_stretch| is the amount of glue stretching between words.
+
+\hang|param[4]=space_shrink| is the amount of glue shrinking between words.
+
+\hang|param[5]=x_height| is the size of one ex in the font; it is also
+the height of letters for which accents don't have to be raised or lowered.
+
+\hang|param[6]=quad| is the size of one em in the font.
+
+\hang|param[7]=extra_space| is the amount added to |param[2]| at the
+ends of sentences.
+
+\yskip\noindent
+If fewer than seven parameters are present, \TeX\ sets the missing parameters
+to zero. Fonts used for math symbols are required to have
+additional parameter information, which is explained later.
+
+@d slant_code=1
+@d space_code=2
+@d space_stretch_code=3
+@d space_shrink_code=4
+@d x_height_code=5
+@d quad_code=6
+@d extra_space_code=7
+
+@ So that is what \.{TFM} files hold. Since \TeX\ has to absorb such information
+about lots of fonts, it stores most of the data in a large array called
+|font_info|. Each item of |font_info| is a |memory_word|; the |fix_word|
+data gets converted into |scaled| entries, while everything else goes into
+words of type |four_quarters|.
+
+When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number
+to the user's font~\.{\\f}. Adding this number to |font_id_base| gives the
+|eqtb| location of a ``frozen'' control sequence that will always select
+the font.
+
+@<Types...@>=
+@!internal_font_number=font_base..font_max; {|font| in a |char_node|}
+@!font_index=0..font_mem_size; {index into |font_info|}
+
+@ Here now is the (rather formidable) array of font arrays.
+
+@d non_char==qi(256) {a |halfword| code that can't match a real character}
+@d non_address=0 {a spurious |bchar_label|}
+
+@<Glob...@>=
+@!font_info:array[font_index] of memory_word;
+ {the big collection of font data}
+@!fmem_ptr:font_index; {first unused word of |font_info|}
+@!font_ptr:internal_font_number; {largest internal font number in use}
+@!font_check:array[internal_font_number] of four_quarters; {check sum}
+@!font_size:array[internal_font_number] of scaled; {``at'' size}
+@!font_dsize:array[internal_font_number] of scaled; {``design'' size}
+@!font_params:array[internal_font_number] of font_index; {how many font
+ parameters are present}
+@!font_name:array[internal_font_number] of str_number; {name of the font}
+@!font_area:array[internal_font_number] of str_number; {area of the font}
+@!font_bc:array[internal_font_number] of eight_bits;
+ {beginning (smallest) character code}
+@!font_ec:array[internal_font_number] of eight_bits;
+ {ending (largest) character code}
+@!font_glue:array[internal_font_number] of pointer;
+ {glue specification for interword space, |null| if not allocated}
+@!font_used:array[internal_font_number] of boolean;
+ {has a character from this font actually appeared in the output?}
+@!hyphen_char:array[internal_font_number] of integer;
+ {current \.{\\hyphenchar} values}
+@!skew_char:array[internal_font_number] of integer;
+ {current \.{\\skewchar} values}
+@!bchar_label:array[internal_font_number] of font_index;
+ {start of |lig_kern| program for left boundary character,
+ |non_address| if there is none}
+@!font_bchar:array[internal_font_number] of min_quarterword..non_char;
+ {right boundary character, |non_char| if there is none}
+@!font_false_bchar:array[internal_font_number] of min_quarterword..non_char;
+ {|font_bchar| if it doesn't exist in the font, otherwise |non_char|}
+
+@ Besides the arrays just enumerated, we have directory arrays that make it
+easy to get at the individual entries in |font_info|. For example, the
+|char_info| data for character |c| in font |f| will be in
+|font_info[char_base[f]+c].qqqq|; and if |w| is the |width_index|
+part of this word (the |b0| field), the width of the character is
+|font_info[width_base[f]+w].sc|. (These formulas assume that
+|min_quarterword| has already been added to |c| and to |w|, since \TeX\
+stores its quarterwords that way.)
+
+@<Glob...@>=
+@!char_base:array[internal_font_number] of integer;
+ {base addresses for |char_info|}
+@!width_base:array[internal_font_number] of integer;
+ {base addresses for widths}
+@!height_base:array[internal_font_number] of integer;
+ {base addresses for heights}
+@!depth_base:array[internal_font_number] of integer;
+ {base addresses for depths}
+@!italic_base:array[internal_font_number] of integer;
+ {base addresses for italic corrections}
+@!lig_kern_base:array[internal_font_number] of integer;
+ {base addresses for ligature/kerning programs}
+@!kern_base:array[internal_font_number] of integer;
+ {base addresses for kerns}
+@!exten_base:array[internal_font_number] of integer;
+ {base addresses for extensible recipes}
+@!param_base:array[internal_font_number] of integer;
+ {base addresses for font parameters}
+
+@ @<Set init...@>=
+for k:=font_base to font_max do font_used[k]:=false;
+
+@ \TeX\ always knows at least one font, namely the null font. It has no
+characters, and its seven parameters are all equal to zero.
+
+@<Initialize table...@>=
+font_ptr:=null_font; fmem_ptr:=7;
+font_name[null_font]:="nullfont"; font_area[null_font]:="";
+hyphen_char[null_font]:="-"; skew_char[null_font]:=-1;
+bchar_label[null_font]:=non_address;
+font_bchar[null_font]:=non_char; font_false_bchar[null_font]:=non_char;
+font_bc[null_font]:=1; font_ec[null_font]:=0;
+font_size[null_font]:=0; font_dsize[null_font]:=0;
+char_base[null_font]:=0; width_base[null_font]:=0;
+height_base[null_font]:=0; depth_base[null_font]:=0;
+italic_base[null_font]:=0; lig_kern_base[null_font]:=0;
+kern_base[null_font]:=0; exten_base[null_font]:=0;
+font_glue[null_font]:=null; font_params[null_font]:=7;
+param_base[null_font]:=-1;
+for k:=0 to 6 do font_info[k].sc:=0;
+
+@ @<Put each...@>=
+primitive("nullfont",set_font,null_font);
+@!@:null_font_}{\.{\\nullfont} primitive@>
+text(frozen_null_font):="nullfont"; eqtb[frozen_null_font]:=eqtb[cur_val];
+
+@ Of course we want to define macros that suppress the detail of how font
+information is actually packed, so that we don't have to write things like
+$$\hbox{|font_info[width_base[f]+font_info[char_base[f]+c].qqqq.b0].sc|}$$
+too often. The \.{WEB} definitions here make |char_info(f)(c)| the
+|four_quarters| word of font information corresponding to character
+|c| of font |f|. If |q| is such a word, |char_width(f)(q)| will be
+the character's width; hence the long formula above is at least
+abbreviated to
+$$\hbox{|char_width(f)(char_info(f)(c))|.}$$
+Usually, of course, we will fetch |q| first and look at several of its
+fields at the same time.
+
+The italic correction of a character will be denoted by
+|char_italic(f)(q)|, so it is analogous to |char_width|. But we will get
+at the height and depth in a slightly different way, since we usually want
+to compute both height and depth if we want either one. The value of
+|height_depth(q)| will be the 8-bit quantity
+$$b=|height_index|\times16+|depth_index|,$$ and if |b| is such a byte we
+will write |char_height(f)(b)| and |char_depth(f)(b)| for the height and
+depth of the character |c| for which |q=char_info(f)(c)|. Got that?
+
+The tag field will be called |char_tag(q)|; the remainder byte will be
+called |rem_byte(q)|, using a macro that we have already defined above.
+
+Access to a character's |width|, |height|, |depth|, and |tag| fields is
+part of \TeX's inner loop, so we want these macros to produce code that is
+as fast as possible under the circumstances.
+@^inner loop@>
+
+@d char_info_end(#)==#].qqqq
+@d char_info(#)==font_info[char_base[#]+char_info_end
+@d char_width_end(#)==#.b0].sc
+@d char_width(#)==font_info[width_base[#]+char_width_end
+@d char_exists(#)==(#.b0>min_quarterword)
+@d char_italic_end(#)==(qo(#.b2)) div 4].sc
+@d char_italic(#)==font_info[italic_base[#]+char_italic_end
+@d height_depth(#)==qo(#.b1)
+@d char_height_end(#)==(#) div 16].sc
+@d char_height(#)==font_info[height_base[#]+char_height_end
+@d char_depth_end(#)==(#) mod 16].sc
+@d char_depth(#)==font_info[depth_base[#]+char_depth_end
+@d char_tag(#)==((qo(#.b2)) mod 4)
+
+@ The global variable |null_character| is set up to be a word of
+|char_info| for a character that doesn't exist. Such a word provides a
+convenient way to deal with erroneous situations.
+
+@<Glob...@>=
+@!null_character:four_quarters; {nonexistent character information}
+
+@ @<Set init...@>=
+null_character.b0:=min_quarterword; null_character.b1:=min_quarterword;
+null_character.b2:=min_quarterword; null_character.b3:=min_quarterword;
+
+@ Here are some macros that help process ligatures and kerns.
+We write |char_kern(f)(j)| to find the amount of kerning specified by
+kerning command~|j| in font~|f|. If |j| is the |char_info| for a character
+with a ligature/kern program, the first instruction of that program is either
+|i=font_info[lig_kern_start(f)(j)]| or |font_info[lig_kern_restart(f)(i)]|,
+depending on whether or not |skip_byte(i)<=stop_flag|.
+
+The constant |kern_base_offset| should be simplified, for \PASCAL\ compilers
+that do not do local optimization.
+@^system dependencies@>
+
+@d char_kern_end(#)==256*op_byte(#)+rem_byte(#)].sc
+@d char_kern(#)==font_info[kern_base[#]+char_kern_end
+@d kern_base_offset==256*(128+min_quarterword)
+@d lig_kern_start(#)==lig_kern_base[#]+rem_byte {beginning of lig/kern program}
+@d lig_kern_restart_end(#)==256*op_byte(#)+rem_byte(#)+32768-kern_base_offset
+@d lig_kern_restart(#)==lig_kern_base[#]+lig_kern_restart_end
+
+@ Font parameters are referred to as |slant(f)|, |space(f)|, etc.
+
+@d param_end(#)==param_base[#]].sc
+@d param(#)==font_info[#+param_end
+@d slant==param(slant_code) {slant to the right, per unit distance upward}
+@d space==param(space_code) {normal space between words}
+@d space_stretch==param(space_stretch_code) {stretch between words}
+@d space_shrink==param(space_shrink_code) {shrink between words}
+@d x_height==param(x_height_code) {one ex}
+@d quad==param(quad_code) {one em}
+@d extra_space==param(extra_space_code) {additional space at end of sentence}
+
+@<The em width for |cur_font|@>=quad(cur_font)
+
+@ @<The x-height for |cur_font|@>=x_height(cur_font)
+
+@ \TeX\ checks the information of a \.{TFM} file for validity as the
+file is being read in, so that no further checks will be needed when
+typesetting is going on. The somewhat tedious subroutine that does this
+is called |read_font_info|. It has four parameters: the user font
+identifier~|u|, the file name and area strings |nom| and |aire|, and the
+``at'' size~|s|. If |s|~is negative, it's the negative of a scale factor
+to be applied to the design size; |s=-1000| is the normal case.
+Otherwise |s| will be substituted for the design size; in this
+case, |s| must be positive and less than $2048\rm\,pt$
+(i.e., it must be less than $2^{27}$ when considered as an integer).
+
+The subroutine opens and closes a global file variable called |tfm_file|.
+It returns the value of the internal font number that was just loaded.
+If an error is detected, an error message is issued and no font
+information is stored; |null_font| is returned in this case.
+
+@d bad_tfm=11 {label for |read_font_info|}
+@d abort==goto bad_tfm {do this when the \.{TFM} data is wrong}
+
+@p function read_font_info(@!u:pointer;@!nom,@!aire:str_number;
+ @!s:scaled):internal_font_number; {input a \.{TFM} file}
+label done,bad_tfm,not_found;
+var k:font_index; {index into |font_info|}
+@!file_opened:boolean; {was |tfm_file| successfully opened?}
+@!lf,@!lh,@!bc,@!ec,@!nw,@!nh,@!nd,@!ni,@!nl,@!nk,@!ne,@!np:halfword;
+ {sizes of subfiles}
+@!f:internal_font_number; {the new font's number}
+@!g:internal_font_number; {the number to return}
+@!a,@!b,@!c,@!d:eight_bits; {byte variables}
+@!qw:four_quarters;@!sw:scaled; {accumulators}
+@!bch_label:integer; {left boundary start location, or infinity}
+@!bchar:0..256; {right boundary character, or 256}
+@!z:scaled; {the design size or the ``at'' size}
+@!alpha:integer;@!beta:1..16;
+ {auxiliary quantities used in fixed-point multiplication}
+begin g:=null_font;@/
+@<Read and check the font data; |abort| if the \.{TFM} file is
+ malformed; if there's no room for this font, say so and |goto
+ done|; otherwise |incr(font_ptr)| and |goto done|@>;
+bad_tfm: @<Report that the font won't be loaded@>;
+done: if file_opened then b_close(tfm_file);
+read_font_info:=g;
+end;
+
+@ There are programs called \.{TFtoPL} and \.{PLtoTF} that convert
+between the \.{TFM} format and a symbolic property-list format
+that can be easily edited. These programs contain extensive
+diagnostic information, so \TeX\ does not have to bother giving
+precise details about why it rejects a particular \.{TFM} file.
+@.TFtoPL@> @.PLtoTF@>
+
+@d start_font_error_message==print_err("Font "); sprint_cs(u);
+ print_char("="); print_file_name(nom,aire,"");
+ if s>=0 then
+ begin print(" at "); print_scaled(s); print("pt");
+ end
+ else if s<>-1000 then
+ begin print(" scaled "); print_int(-s);
+ end
+
+@<Report that the font won't be loaded@>=
+start_font_error_message;
+@.Font x=xx not loadable...@>
+if file_opened then print(" not loadable: Bad metric (TFM) file")
+else print(" not loadable: Metric (TFM) file not found");
+help5("I wasn't able to read the size data for this font,")@/
+("so I will ignore the font specification.")@/
+("[Wizards can fix TFM files using TFtoPL/PLtoTF.]")@/
+("You might try inserting a different font spec;")@/
+("e.g., type `I\font<same font id>=<substitute font name>'.");
+error
+
+@ @<Read and check...@>=
+@<Open |tfm_file| for input@>;
+@<Read the {\.{TFM}} size fields@>;
+@<Use size fields to allocate font information@>;
+@<Read the {\.{TFM}} header@>;
+@<Read character data@>;
+@<Read box dimensions@>;
+@<Read ligature/kern program@>;
+@<Read extensible character recipes@>;
+@<Read font parameters@>;
+@<Make final adjustments and |goto done|@>
+
+@ @<Open |tfm_file| for input@>=
+file_opened:=false;
+if aire="" then pack_file_name(nom,TEX_font_area,".tfm")
+else pack_file_name(nom,aire,".tfm");
+if not b_open_in(tfm_file) then abort;
+file_opened:=true
+
+@ Note: A malformed \.{TFM} file might be shorter than it claims to be;
+thus |eof(tfm_file)| might be true when |read_font_info| refers to
+|tfm_file^| or when it says |get(tfm_file)|. If such circumstances
+cause system error messages, you will have to defeat them somehow,
+for example by defining |fget| to be `\ignorespaces|begin get(tfm_file);|
+|if eof(tfm_file) then abort; end|\unskip'.
+@^system dependencies@>
+
+@d fget==get(tfm_file)
+@d fbyte==tfm_file^
+@d read_sixteen(#)==begin #:=fbyte;
+ if #>127 then abort;
+ fget; #:=#*@'400+fbyte;
+ end
+@d store_four_quarters(#)==begin fget; a:=fbyte; qw.b0:=qi(a);
+ fget; b:=fbyte; qw.b1:=qi(b);
+ fget; c:=fbyte; qw.b2:=qi(c);
+ fget; d:=fbyte; qw.b3:=qi(d);
+ #:=qw;
+ end
+
+@ @<Read the {\.{TFM}} size fields@>=
+begin read_sixteen(lf);
+fget; read_sixteen(lh);
+fget; read_sixteen(bc);
+fget; read_sixteen(ec);
+if (bc>ec+1)or(ec>255) then abort;
+if bc>255 then {|bc=256| and |ec=255|}
+ begin bc:=1; ec:=0;
+ end;
+fget; read_sixteen(nw);
+fget; read_sixteen(nh);
+fget; read_sixteen(nd);
+fget; read_sixteen(ni);
+fget; read_sixteen(nl);
+fget; read_sixteen(nk);
+fget; read_sixteen(ne);
+fget; read_sixteen(np);
+if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then abort;
+if (nw=0)or(nh=0)or(nd=0)or(ni=0) then abort;
+end
+
+@ The preliminary settings of the index-offset variables |char_base|,
+|width_base|, |lig_kern_base|, |kern_base|, and |exten_base| will be
+corrected later by subtracting |min_quarterword| from them; and we will
+subtract 1 from |param_base| too. It's best to forget about such anomalies
+until later.
+
+@<Use size fields to allocate font information@>=
+lf:=lf-6-lh; {|lf| words should be loaded into |font_info|}
+if np<7 then lf:=lf+7-np; {at least seven parameters will appear}
+if (font_ptr=font_max)or(fmem_ptr+lf>font_mem_size) then
+ @<Apologize for not loading the font, |goto done|@>;
+f:=font_ptr+1;
+char_base[f]:=fmem_ptr-bc;
+width_base[f]:=char_base[f]+ec+1;
+height_base[f]:=width_base[f]+nw;
+depth_base[f]:=height_base[f]+nh;
+italic_base[f]:=depth_base[f]+nd;
+lig_kern_base[f]:=italic_base[f]+ni;
+kern_base[f]:=lig_kern_base[f]+nl-kern_base_offset;
+exten_base[f]:=kern_base[f]+kern_base_offset+nk;
+param_base[f]:=exten_base[f]+ne
+
+@ @<Apologize for not loading...@>=
+begin start_font_error_message;
+print(" not loaded: Not enough room left");
+@.Font x=xx not loaded...@>
+help4("I'm afraid I won't be able to make use of this font,")@/
+("because my memory for character-size data is too small.")@/
+("If you're really stuck, ask a wizard to enlarge me.")@/
+("Or maybe try `I\font<same font id>=<name of loaded font>'.");
+error; goto done;
+end
+
+@ Only the first two words of the header are needed by \TeX82.
+
+@<Read the {\.{TFM}} header@>=
+begin if lh<2 then abort;
+store_four_quarters(font_check[f]);
+fget; read_sixteen(z); {this rejects a negative design size}
+fget; z:=z*@'400+fbyte; fget; z:=(z*@'20)+(fbyte div@'20);
+if z<unity then abort;
+while lh>2 do
+ begin fget;fget;fget;fget;decr(lh); {ignore the rest of the header}
+ end;
+font_dsize[f]:=z;
+if s<>-1000 then
+ if s>=0 then z:=s
+ else z:=xn_over_d(z,-s,1000);
+font_size[f]:=z;
+end
+
+@ @<Read character data@>=
+for k:=fmem_ptr to width_base[f]-1 do
+ begin store_four_quarters(font_info[k].qqqq);
+ if (a>=nw)or(b div @'20>=nh)or(b mod @'20>=nd)or
+ (c div 4>=ni) then abort;
+ case c mod 4 of
+ lig_tag: if d>=nl then abort;
+ ext_tag: if d>=ne then abort;
+ list_tag: @<Check for charlist cycle@>;
+ othercases do_nothing {|no_tag|}
+ endcases;
+ end
+
+@ We want to make sure that there is no cycle of characters linked together
+by |list_tag| entries, since such a cycle would get \TeX\ into an endless
+loop. If such a cycle exists, the routine here detects it when processing
+the largest character code in the cycle.
+
+@d check_byte_range(#)==begin if (#<bc)or(#>ec) then abort@+end
+@d current_character_being_worked_on==k+bc-fmem_ptr
+
+@<Check for charlist cycle@>=
+begin check_byte_range(d);
+while d<current_character_being_worked_on do
+ begin qw:=char_info(f)(d);
+ {N.B.: not |qi(d)|, since |char_base[f]| hasn't been adjusted yet}
+ if char_tag(qw)<>list_tag then goto not_found;
+ d:=qo(rem_byte(qw)); {next character on the list}
+ end;
+if d=current_character_being_worked_on then abort; {yes, there's a cycle}
+not_found:end
+
+@ A |fix_word| whose four bytes are $(a,b,c,d)$ from left to right represents
+the number
+$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
+-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
+(No other choices of |a| are allowed, since the magnitude of a number in
+design-size units must be less than 16.) We want to multiply this
+quantity by the integer~|z|, which is known to be less than $2^{27}$.
+If $|z|<2^{23}$, the individual multiplications $b\cdot z$,
+$c\cdot z$, $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2,
+4, 8, or 16, to obtain a multiplier less than $2^{23}$, and we can
+compensate for this later. If |z| has thereby been replaced by
+$|z|^\prime=|z|/2^e$, let $\beta=2^{4-e}$; we shall compute
+$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$
+if $a=0$, or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$.
+This calculation must be done exactly, in order to guarantee portability
+of \TeX\ between computers.
+
+@d store_scaled(#)==begin fget; a:=fbyte; fget; b:=fbyte;
+ fget; c:=fbyte; fget; d:=fbyte;@/
+ sw:=(((((d*z)div@'400)+(c*z))div@'400)+(b*z))div beta;
+ if a=0 then #:=sw@+else if a=255 then #:=sw-alpha@+else abort;
+ end
+
+@<Read box dimensions@>=
+begin @<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
+for k:=width_base[f] to lig_kern_base[f]-1 do
+ store_scaled(font_info[k].sc);
+if font_info[width_base[f]].sc<>0 then abort; {\\{width}[0] must be zero}
+if font_info[height_base[f]].sc<>0 then abort; {\\{height}[0] must be zero}
+if font_info[depth_base[f]].sc<>0 then abort; {\\{depth}[0] must be zero}
+if font_info[italic_base[f]].sc<>0 then abort; {\\{italic}[0] must be zero}
+end
+
+@ @<Replace |z|...@>=
+begin alpha:=16;
+while z>=@'40000000 do
+ begin z:=z div 2; alpha:=alpha+alpha;
+ end;
+beta:=256 div alpha; alpha:=alpha*z;
+end
+
+@ @d check_existence(#)==@t@>@;@/
+ begin check_byte_range(#);
+ qw:=char_info(f)(#); {N.B.: not |qi(#)|}
+ if not char_exists(qw) then abort;
+ end
+
+@<Read ligature/kern program@>=
+bch_label:=@'77777; bchar:=256;
+if nl>0 then
+ begin for k:=lig_kern_base[f] to kern_base[f]+kern_base_offset-1 do
+ begin store_four_quarters(font_info[k].qqqq);
+ if a>128 then
+ begin if 256*c+d>=nl then abort;
+ if a=255 then if k=lig_kern_base[f] then bchar:=b;
+ end
+ else begin if b<>bchar then check_existence(b);
+ if c<128 then check_existence(d) {check ligature}
+ else if 256*(c-128)+d>=nk then abort; {check kern}
+ if a<128 then if k-lig_kern_base[f]+a+1>=nl then abort;
+ end;
+ end;
+ if a=255 then bch_label:=256*c+d;
+ end;
+for k:=kern_base[f]+kern_base_offset to exten_base[f]-1 do
+ store_scaled(font_info[k].sc);
+
+@ @<Read extensible character recipes@>=
+for k:=exten_base[f] to param_base[f]-1 do
+ begin store_four_quarters(font_info[k].qqqq);
+ if a<>0 then check_existence(a);
+ if b<>0 then check_existence(b);
+ if c<>0 then check_existence(c);
+ check_existence(d);
+ end
+
+@ We check to see that the \.{TFM} file doesn't end prematurely; but
+no error message is given for files having more than |lf| words.
+
+@<Read font parameters@>=
+begin for k:=1 to np do
+ if k=1 then {the |slant| parameter is a pure number}
+ begin fget; sw:=fbyte; if sw>127 then sw:=sw-256;
+ fget; sw:=sw*@'400+fbyte; fget; sw:=sw*@'400+fbyte;
+ fget; font_info[param_base[f]].sc:=
+ (sw*@'20)+(fbyte div@'20);
+ end
+ else store_scaled(font_info[param_base[f]+k-1].sc);
+if eof(tfm_file) then abort;
+for k:=np+1 to 7 do font_info[param_base[f]+k-1].sc:=0;
+end
+
+@ Now to wrap it up, we have checked all the necessary things about the \.{TFM}
+file, and all we need to do is put the finishing touches on the data for
+the new font.
+
+@d adjust(#)==#[f]:=qo(#[f])
+ {correct for the excess |min_quarterword| that was added}
+
+@<Make final adjustments...@>=
+if np>=7 then font_params[f]:=np@+else font_params[f]:=7;
+hyphen_char[f]:=default_hyphen_char; skew_char[f]:=default_skew_char;
+if bch_label<nl then bchar_label[f]:=bch_label+lig_kern_base[f]
+else bchar_label[f]:=non_address;
+font_bchar[f]:=qi(bchar);
+font_false_bchar[f]:=qi(bchar);
+if bchar<=ec then if bchar>=bc then
+ begin qw:=char_info(f)(bchar); {N.B.: not |qi(bchar)|}
+ if char_exists(qw) then font_false_bchar[f]:=non_char;
+ end;
+font_name[f]:=nom;
+font_area[f]:=aire;
+font_bc[f]:=bc; font_ec[f]:=ec; font_glue[f]:=null;
+adjust(char_base); adjust(width_base); adjust(lig_kern_base);
+adjust(kern_base); adjust(exten_base);
+decr(param_base[f]);
+fmem_ptr:=fmem_ptr+lf; font_ptr:=f; g:=f; goto done
+
+@ Before we forget about the format of these tables, let's deal with two
+of \TeX's basic scanning routines related to font information.
+
+@<Declare procedures that scan font-related stuff@>=
+procedure scan_font_ident;
+var f:internal_font_number;
+@!m:halfword;
+begin @<Get the next non-blank non-call...@>;
+if cur_cmd=def_font then f:=cur_font
+else if cur_cmd=set_font then f:=cur_chr
+else if cur_cmd=def_family then
+ begin m:=cur_chr; scan_four_bit_int; f:=equiv(m+cur_val);
+ end
+else begin print_err("Missing font identifier");
+@.Missing font identifier@>
+ help2("I was looking for a control sequence whose")@/
+ ("current meaning has been defined by \font.");
+ back_error; f:=null_font;
+ end;
+cur_val:=f;
+end;
+
+@ The following routine is used to implement `\.{\\fontdimen} |n| |f|'.
+The boolean parameter |writing| is set |true| if the calling program
+intends to change the parameter value.
+
+@<Declare procedures that scan font-related stuff@>=
+procedure find_font_dimen(@!writing:boolean);
+ {sets |cur_val| to |font_info| location}
+var f:internal_font_number;
+@!n:integer; {the parameter number}
+begin scan_int; n:=cur_val; scan_font_ident; f:=cur_val;
+if n<=0 then cur_val:=fmem_ptr
+else begin if writing and(n<=space_shrink_code)and@|
+ (n>=space_code)and(font_glue[f]<>null) then
+ begin delete_glue_ref(font_glue[f]);
+ font_glue[f]:=null;
+ end;
+ if n>font_params[f] then
+ if f<font_ptr then cur_val:=fmem_ptr
+ else @<Increase the number of parameters in the last font@>
+ else cur_val:=n+param_base[f];
+ end;
+@<Issue an error message if |cur_val=fmem_ptr|@>;
+end;
+
+@ @<Issue an error message if |cur_val=fmem_ptr|@>=
+if cur_val=fmem_ptr then
+ begin print_err("Font "); print_esc(font_id_text(f));
+ print(" has only "); print_int(font_params[f]);
+ print(" fontdimen parameters");
+@.Font x has only...@>
+ help2("To increase the number of font parameters, you must")@/
+ ("use \fontdimen immediately after the \font is loaded.");
+ error;
+ end
+
+@ @<Increase the number of parameters...@>=
+begin repeat if fmem_ptr=font_mem_size then
+ overflow("font memory",font_mem_size);
+@:TeX capacity exceeded font memory}{\quad font memory@>
+font_info[fmem_ptr].sc:=0; incr(fmem_ptr); incr(font_params[f]);
+until n=font_params[f];
+cur_val:=fmem_ptr-1; {this equals |param_base[f]+font_params[f]|}
+end
+
+@ When \TeX\ wants to typeset a character that doesn't exist, the
+character node is not created; thus the output routine can assume
+that characters exist when it sees them. The following procedure
+prints a warning message unless the user has suppressed it.
+
+@p procedure char_warning(@!f:internal_font_number;@!c:eight_bits);
+begin if tracing_lost_chars>0 then
+ begin begin_diagnostic;
+ print_nl("Missing character: There is no ");
+@.Missing character@>
+ print_ASCII(c); print(" in font ");
+ slow_print(font_name[f]); print_char("!"); end_diagnostic(false);
+ end;
+end;
+
+@ Here is a function that returns a pointer to a character node for a
+given character in a given font. If that character doesn't exist,
+|null| is returned instead.
+
+@p function new_character(@!f:internal_font_number;@!c:eight_bits):pointer;
+label exit;
+var p:pointer; {newly allocated node}
+begin if font_bc[f]<=c then if font_ec[f]>=c then
+ if char_exists(char_info(f)(qi(c))) then
+ begin p:=get_avail; font(p):=f; character(p):=qi(c);
+ new_character:=p; return;
+ end;
+char_warning(f,c);
+new_character:=null;
+exit:end;
+
+@* \[31] Device-independent file format.
+The most important output produced by a run of \TeX\ is the ``device
+independent'' (\.{DVI}) file that specifies where characters and rules
+are to appear on printed pages. The form of these files was designed by
+David R. Fuchs in 1979. Almost any reasonable typesetting device can be
+@^Fuchs, David Raymond@>
+@:DVI_files}{\.{DVI} files@>
+driven by a program that takes \.{DVI} files as input, and dozens of such
+\.{DVI}-to-whatever programs have been written. Thus, it is possible to
+print the output of \TeX\ on many different kinds of equipment, using \TeX\
+as a device-independent ``front end.''
+
+A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
+series of commands in a machine-like language. The first byte of each command
+is the operation code, and this code is followed by zero or more bytes
+that provide parameters to the command. The parameters themselves may consist
+of several consecutive bytes; for example, the `|set_rule|' command has two
+parameters, each of which is four bytes long. Parameters are usually
+regarded as nonnegative integers; but four-byte-long parameters,
+and shorter parameters that denote distances, can be
+either positive or negative. Such parameters are given in two's complement
+notation. For example, a two-byte-long distance parameter has a value between
+$-2^{15}$ and $2^{15}-1$. As in \.{TFM} files, numbers that occupy
+more than one byte position appear in BigEndian order.
+
+A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
+or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that define the dimensions used in the
+file; this must come first. Each ``page'' consists of a |bop| command,
+followed by any number of other commands that tell where characters are to
+be placed on a physical page, followed by an |eop| command. The pages
+appear in the order that \TeX\ generated them. If we ignore |nop| commands
+and \\{fnt\_def} commands (which are allowed between any two commands in
+the file), each |eop| command is immediately followed by a |bop| command,
+or by a |post| command; in the latter case, there are no more pages in the
+file, and the remaining bytes form the postamble. Further details about
+the postamble will be explained later.
+
+Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first byte is number~0, then comes number~1, and so on. For example,
+one of the parameters of a |bop| command points to the previous |bop|;
+this makes it feasible to read the pages in backwards order, in case the
+results are being directed to a device that stacks its output face up.
+Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
+first page occupies bytes 100 to 999, say, and if the second
+page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
+points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
+very first |bop|, i.e., the one starting in byte 100, has a pointer of~$-1$.)
+
+@ The \.{DVI} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+implicit instead of explicit. When a \.{DVI}-reading program reads the
+commands for a page, it keeps track of several quantities: (a)~The current
+font |f| is an integer; this value is changed only
+by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
+is given by two numbers called the horizontal and vertical coordinates,
+|h| and |v|. Both coordinates are zero at the upper left corner of the page;
+moving to the right corresponds to increasing the horizontal coordinate, and
+moving down corresponds to increasing the vertical coordinate. Thus, the
+coordinates are essentially Cartesian, except that vertical directions are
+flipped; the Cartesian version of |(h,v)| would be |(h,-v)|. (c)~The
+current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
+where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
+are used for vertical spacing. (d)~There is a stack containing
+|(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to
+change the current level of operation. Note that the current font~|f| is
+not pushed and popped; the stack contains only information about
+positioning.
+
+The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
+to 32 bits, including the sign. Since they represent physical distances,
+there is a small unit of measurement such that increasing |h| by~1 means
+moving a certain tiny distance to the right. The actual unit of
+measurement is variable, as explained below; \TeX\ sets things up so that
+its \.{DVI} output is in sp units, i.e., scaled points, in agreement with
+all the |scaled| dimensions in \TeX's data structures.
+
+@ Here is a list of all the commands that may appear in a \.{DVI} file. Each
+command is specified by its symbolic name (e.g., |bop|), its opcode byte
+(e.g., 139), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|p[4]|' means that parameter |p| is four bytes long.
+
+\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
+such that the reference point of the character is at |(h,v)|. Then
+increase |h| by the width of that character. Note that a character may
+have zero or negative width, so one cannot be sure that |h| will advance
+after this command; but |h| usually does increase.
+
+\yskip\hang\\{set\_char\_1} through \\{set\_char\_127} (opcodes 1 to 127).
+Do the operations of |set_char_0|; but use the character whose number
+matches the opcode, instead of character~0.
+
+\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
+number~|c| is typeset. \TeX82 uses this command for characters in the
+range |128<=c<256|.
+
+\yskip\hang|@!set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
+bytes long, so it is in the range |0<=c<65536|. \TeX82 never uses this
+command, but it should come in handy for extensions of \TeX\ that deal
+with oriental languages.
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+\yskip\hang|@!set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
+bytes long, so it can be as large as $2^{24}-1$. Not even the Chinese
+language has this many characters, but this command might prove useful
+in some yet unforeseen extension.
+
+\yskip\hang|@!set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
+bytes long. Imagine that.
+
+\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
+of height~|a| and width~|b|, with its bottom left corner at |(h,v)|. Then
+set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note
+that if |b<0|, the value of |h| will decrease even though nothing else happens.
+See below for details about how to typeset rules so that consistency with
+\MF\ is guaranteed.
+
+\yskip\hang|@!put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
+such that the reference point of the character is at |(h,v)|. (The `put'
+commands are exactly like the `set' commands, except that they simply put out a
+character or a rule without moving the reference point afterwards.)
+
+\yskip\hang|@!put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
+
+\yskip\hang|@!put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
+
+\yskip\hang|@!put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
+
+\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
+|h| is not changed.
+
+\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
+may occur between \.{DVI} commands, but a |nop| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
+of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set
+the current font |f| to an undefined value. The ten $c_i$ parameters hold
+the values of \.{\\count0} $\ldots$ \.{\\count9} in \TeX\ at the time
+\.{\\shipout} was invoked for this page; they can be used to identify
+pages, if a user wants to print only part of a \.{DVI} file. The parameter
+|p| points to the previous |bop| in the file; the first
+|bop| has $p=-1$.
+
+\yskip\hang|eop| 140. End of page: Print what you have read since the
+previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading
+programs that drive most output devices will have kept a buffer of the
+material that appears on the page that has just ended. This material is
+largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
+|h|~coordinate; so it usually needs to be sorted into some order that is
+appropriate for the device in question.)
+
+\yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the
+top of the stack; do not change any of these values. Note that |f| is
+not pushed.
+
+\yskip\hang|pop| 142. Pop the top six values off of the stack and assign
+them respectively to |(h,v,w,x,y,z)|. The number of pops should never
+exceed the number of pushes, since it would be highly embarrassing if the
+stack were empty at the time of a |pop| command.
+
+\yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units.
+The parameter is a signed number in two's complement notation, |-128<=b<128|;
+if |b<0|, the reference point moves left.
+
+\yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a
+two-byte quantity in the range |-32768<=b<32768|.
+
+\yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a
+three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a
+four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|w| gets particular values.
+
+\yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128<=b<128|. This command
+changes the current |w|~spacing and moves right by |b|.
+
+\yskip\hang|@!w2| 149 |b[2]|. Same as |w1|, but |b| is two bytes long,
+|-32768<=b<32768|.
+
+\yskip\hang|@!w3| 150 |b[3]|. Same as |w1|, but |b| is three bytes long,
+|@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|@!w4| 151 |b[4]|. Same as |w1|, but |b| is four bytes long,
+|@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|'
+commands are like the `|w|' commands except that they involve |x| instead
+of |w|.
+
+\yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128<=b<128|. This command
+changes the current |x|~spacing and moves right by |b|.
+
+\yskip\hang|@!x2| 154 |b[2]|. Same as |x1|, but |b| is two bytes long,
+|-32768<=b<32768|.
+
+\yskip\hang|@!x3| 155 |b[3]|. Same as |x1|, but |b| is three bytes long,
+|@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|@!x4| 156 |b[4]|. Same as |x1|, but |b| is four bytes long,
+|@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units.
+The parameter is a signed number in two's complement notation, |-128<=a<128|;
+if |a<0|, the reference point moves up.
+
+\yskip\hang|@!down2| 158 |a[2]|. Same as |down1|, except that |a| is a
+two-byte quantity in the range |-32768<=a<32768|.
+
+\yskip\hang|@!down3| 159 |a[3]|. Same as |down1|, except that |a| is a
+three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|@!down4| 160 |a[4]|. Same as |down1|, except that |a| is a
+four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|y| gets particular values.
+
+\yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128<=a<128|. This command
+changes the current |y|~spacing and moves down by |a|.
+
+\yskip\hang|@!y2| 163 |a[2]|. Same as |y1|, but |a| is two bytes long,
+|-32768<=a<32768|.
+
+\yskip\hang|@!y3| 164 |a[3]|. Same as |y1|, but |a| is three bytes long,
+|@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|@!y4| 165 |a[4]|. Same as |y1|, but |a| is four bytes long,
+|@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands
+are like the `|y|' commands except that they involve |z| instead of |y|.
+
+\yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128<=a<128|. This command
+changes the current |z|~spacing and moves down by |a|.
+
+\yskip\hang|@!z2| 168 |a[2]|. Same as |z1|, but |a| is two bytes long,
+|-32768<=a<32768|.
+
+\yskip\hang|@!z3| 169 |a[3]|. Same as |z1|, but |a| is three bytes long,
+|@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|@!z4| 170 |a[4]|. Same as |z1|, but |a| is four bytes long,
+|@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been
+defined by a \\{fnt\_def} instruction, as explained below.
+
+\yskip\hang\\{fnt\_num\_1} through \\{fnt\_num\_63} (opcodes 172 to 234). Set
+|f:=1|, \dots, \hbox{|f:=63|}, respectively.
+
+\yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font
+numbers in the range |64<=k<256|.
+
+\yskip\hang|@!fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
+bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this
+command, but large font numbers may prove useful for specifications of
+color or texture, or they may be used for special fonts that have fixed
+numbers in some external coding scheme.
+
+\yskip\hang|@!fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
+bytes long, so it can be as large as $2^{24}-1$.
+
+\yskip\hang|@!fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
+bytes long; this is for the really big font numbers (and for the negative ones).
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
+programs are being used. \TeX82 generates |xxx1| when a short enough
+\.{\\special} appears, setting |k| to the number of bytes being sent. It
+is recommended that |x| be a string having the form of a keyword followed
+by possible parameters relevant to that keyword.
+
+\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|@!xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
+large. \TeX82 uses |xxx4| when sending a string of length 256 or more.
+
+\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<256|; font definitions will be explained shortly.
+
+\yskip\hang|@!fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<65536|.
+
+\yskip\hang|@!fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|@!fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
+
+\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@ @d set_char_0=0 {typeset character 0 and move right}
+@d set1=128 {typeset a character and move right}
+@d set_rule=132 {typeset a rule and move right}
+@d put_rule=137 {typeset a rule}
+@d nop=138 {no operation}
+@d bop=139 {beginning of page}
+@d eop=140 {ending of page}
+@d push=141 {save the current positions}
+@d pop=142 {restore previous positions}
+@d right1=143 {move right}
+@d w0=147 {move right by |w|}
+@d w1=148 {move right and set |w|}
+@d x0=152 {move right by |x|}
+@d x1=153 {move right and set |x|}
+@d down1=157 {move down}
+@d y0=161 {move down by |y|}
+@d y1=162 {move down and set |y|}
+@d z0=166 {move down by |z|}
+@d z1=167 {move down and set |z|}
+@d fnt_num_0=171 {set current font to 0}
+@d fnt1=235 {set current font}
+@d xxx1=239 {extension to \.{DVI} primitives}
+@d xxx4=242 {potentially long extension to \.{DVI} primitives}
+@d fnt_def1=243 {define the meaning of a font number}
+@d pre=247 {preamble}
+@d post=248 {postamble beginning}
+@d post_post=249 {postamble ending}
+
+@ The preamble contains basic information about the file as a whole. As
+stated above, there are six parameters:
+$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
+The |i| byte identifies \.{DVI} format; currently this byte is always set
+to~2. (The value |i=3| is currently used for an extended format that
+allows a mixture of right-to-left and left-to-right typesetting.
+Some day we will set |i=4|, when \.{DVI} format makes another
+incompatible change---perhaps in the year 2048.)
+
+The next two parameters, |num| and |den|, are positive integers that define
+the units of measurement; they are the numerator and denominator of a
+fraction by which all dimensions in the \.{DVI} file could be multiplied
+in order to get lengths in units of $10^{-7}$ meters. Since $\rm 7227{pt} =
+254{cm}$, and since \TeX\ works with scaled points where there are $2^{16}$
+sp in a point, \TeX\ sets
+$|num|/|den|=(254\cdot10^5)/(7227\cdot2^{16})=25400000/473628672$.
+@^sp@>
+
+The |mag| parameter is what \TeX\ calls \.{\\mag}, i.e., 1000 times the
+desired magnification. The actual fraction by which dimensions are
+multiplied is therefore $|mag|\cdot|num|/1000|den|$. Note that if a \TeX\
+source document does not call for any `\.{true}' dimensions, and if you
+change it only by specifying a different \.{\\mag} setting, the \.{DVI}
+file that \TeX\ creates will be completely unchanged except for the value
+of |mag| in the preamble and postamble. (Fancy \.{DVI}-reading programs allow
+users to override the |mag|~setting when a \.{DVI} file is being printed.)
+
+Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
+interpreted further. The length of comment |x| is |k|, where |0<=k<256|.
+
+@d id_byte=2 {identifies the kind of \.{DVI} files described here}
+
+@ Font definitions for a given font number |k| contain further parameters
+$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
+The four-byte value |c| is the check sum that \TeX\ found in the \.{TFM}
+file for this font; |c| should match the check sum of the font found by
+programs that read this \.{DVI} file.
+@^check sum@>
+
+Parameter |s| contains a fixed-point scale factor that is applied to
+the character widths in font |k|; font dimensions in \.{TFM} files and
+other font files are relative to this quantity, which is called the
+``at size'' elsewhere in this documentation. The value of |s| is
+always positive and less than $2^{27}$. It is given in the same units
+as the other \.{DVI} dimensions, i.e., in sp when \TeX82 has made the
+file. Parameter |d| is similar to |s|; it is the ``design size,'' and
+(like~|s|) it is given in \.{DVI} units. Thus, font |k| is to be used
+at $|mag|\cdot s/1000d$ times its normal size.
+
+The remaining part of a font definition gives the external name of the font,
+which is an ASCII string of length |a+l|. The number |a| is the length
+of the ``area'' or directory, and |l| is the length of the font name itself;
+the standard local system font area is supposed to be used when |a=0|.
+The |n| field contains the area in its first |a| bytes.
+
+Font definitions must appear before the first use of a particular font number.
+Once font |k| is defined, it must not be defined again; however, we
+shall see below that font definitions appear in the postamble as well as
+in the pages, so in this sense each font number is defined exactly twice,
+if at all. Like |nop| commands, font definitions can
+appear before the first |bop|, or between an |eop| and a |bop|.
+
+@ Sometimes it is desirable to make horizontal or vertical rules line up
+precisely with certain features in characters of a font. It is possible to
+guarantee the correct matching between \.{DVI} output and the characters
+generated by \MF\ by adhering to the following principles: (1)~The \MF\
+characters should be positioned so that a bottom edge or left edge that is
+supposed to line up with the bottom or left edge of a rule appears at the
+reference point, i.e., in row~0 and column~0 of the \MF\ raster. This
+ensures that the position of the rule will not be rounded differently when
+the pixel size is not a perfect multiple of the units of measurement in
+the \.{DVI} file. (2)~A typeset rule of height $a>0$ and width $b>0$
+should be equivalent to a \MF-generated character having black pixels in
+precisely those raster positions whose \MF\ coordinates satisfy
+|0<=x<@t$\alpha$@>b| and |0<=y<@t$\alpha$@>a|, where $\alpha$ is the number
+of pixels per \.{DVI} unit.
+@:METAFONT}{\MF@>
+@^alignment of rules with characters@>
+@^rules aligning with characters@>
+
+@ The last page in a \.{DVI} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \TeX\ has
+accumulated about the file, making it possible to print subsets of the data
+with reasonable efficiency. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
+ $\langle\,$font definitions$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the final |bop| in the file. The next three
+parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
+appeared in the preamble.
+
+Parameters |l| and |u| give respectively the height-plus-depth of the tallest
+page and the width of the widest page, in the same units as other dimensions
+of the file. These numbers might be used by a \.{DVI}-reading program to
+position individual ``pages'' on large sheets of film or paper; however,
+the standard convention for output on normal size paper is to position each
+page so that the upper left-hand corner is exactly one inch from the left
+and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer
+software that attempts cleverly to center the output; a fixed position of
+the upper left corner is easiest for users to understand and to work with.
+Therefore |l| and~|u| are often ignored.
+
+Parameter |s| is the maximum stack depth (i.e., the largest excess of
+|push| commands over |pop| commands) needed to process this file. Then
+comes |t|, the total number of pages (|bop| commands) present.
+
+The postamble continues with font definitions, which are any number of
+\\{fnt\_def} commands as described above, possibly interspersed with |nop|
+commands. Each font number that is used in the \.{DVI} file must be defined
+exactly twice: Once before it is first selected by a \\{fnt} command, and once
+in the postamble.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the font definitions, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~2, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., @'337 in octal). \TeX\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{DVI} file makes it feasible for
+\.{DVI}-reading programs to find the postamble first, on most computers,
+even though \TeX\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{DVI} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{DVI} reader can discover all the information needed for typesetting the
+pages. Note that it is also possible to skip through the \.{DVI} file at
+reasonably high speed to locate a particular page, if that proves
+desirable. This saves a lot of time, since \.{DVI} files used in production
+jobs tend to be large.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{DVI}
+format has been designed to work most efficiently with modern operating systems.
+But if \.{DVI} files have to be processed under the restrictions of standard
+\PASCAL, one can simply read them from front to back, since the necessary
+header information is present in the preamble and in the font definitions.
+(The |l| and |u| and |s| and |t| parameters, which appear only in the
+postamble, are ``frills'' that are handy but not absolutely necessary.)
+
+@* \[32] Shipping pages out.
+After considering \TeX's eyes and stomach, we come now to the bowels.
+@^bowels@>
+
+The |ship_out| procedure is given a pointer to a box; its mission is
+to describe that box in \.{DVI} form, outputting a ``page'' to |dvi_file|.
+The \.{DVI} coordinates $(h,v)=(0,0)$ should correspond to the upper left
+corner of the box being shipped.
+
+Since boxes can be inside of boxes inside of boxes, the main work of
+|ship_out| is done by two mutually recursive routines, |hlist_out|
+and |vlist_out|, which traverse the hlists and vlists inside of horizontal
+and vertical boxes.
+
+As individual pages are being processed, we need to accumulate
+information about the entire set of pages, since such statistics must be
+reported in the postamble. The global variables |total_pages|, |max_v|,
+|max_h|, |max_push|, and |last_bop| are used to record this information.
+
+The variable |doing_leaders| is |true| while leaders are being output.
+The variable |dead_cycles| contains the number of times an output routine
+has been initiated since the last |ship_out|.
+
+A few additional global variables are also defined here for use in
+|vlist_out| and |hlist_out|. They could have been local variables, but
+that would waste stack space when boxes are deeply nested, since the
+values of these variables are not needed during recursive calls.
+@^recursion@>
+
+@<Glob...@>=
+@!total_pages:integer; {the number of pages that have been shipped out}
+@!max_v:scaled; {maximum height-plus-depth of pages shipped so far}
+@!max_h:scaled; {maximum width of pages shipped so far}
+@!max_push:integer; {deepest nesting of |push| commands encountered so far}
+@!last_bop:integer; {location of previous |bop| in the \.{DVI} output}
+@!dead_cycles:integer; {recent outputs that didn't ship anything out}
+@!doing_leaders:boolean; {are we inside a leader box?}
+@#
+@!c,@!f:quarterword; {character and font in current |char_node|}
+@!rule_ht,@!rule_dp,@!rule_wd:scaled; {size of current rule being output}
+@!g:pointer; {current glue specification}
+@!lq,@!lr:integer; {quantities used in calculations for leaders}
+
+@ @<Set init...@>=
+total_pages:=0; max_v:=0; max_h:=0; max_push:=0; last_bop:=-1;
+doing_leaders:=false; dead_cycles:=0; cur_s:=-1;
+
+@ The \.{DVI} bytes are output to a buffer instead of being written directly
+to the output file. This makes it possible to reduce the overhead of
+subroutine calls, thereby measurably speeding up the computation, since
+output of \.{DVI} bytes is part of \TeX's inner loop. And it has another
+advantage as well, since we can change instructions in the buffer in order to
+make the output more compact. For example, a `|down2|' command can be
+changed to a `|y2|', thereby making a subsequent `|y0|' command possible,
+saving two bytes.
+
+The output buffer is divided into two parts of equal size; the bytes found
+in |dvi_buf[0..half_buf-1]| constitute the first half, and those in
+|dvi_buf[half_buf..dvi_buf_size-1]| constitute the second. The global
+variable |dvi_ptr| points to the position that will receive the next
+output byte. When |dvi_ptr| reaches |dvi_limit|, which is always equal
+to one of the two values |half_buf| or |dvi_buf_size|, the half buffer that
+is about to be invaded next is sent to the output and |dvi_limit| is
+changed to its other value. Thus, there is always at least a half buffer's
+worth of information present, except at the very beginning of the job.
+
+Bytes of the \.{DVI} file are numbered sequentially starting with 0;
+the next byte to be generated will be number |dvi_offset+dvi_ptr|.
+A byte is present in the buffer only if its number is |>=dvi_gone|.
+
+@<Types...@>=
+@!dvi_index=0..dvi_buf_size; {an index into the output buffer}
+
+@ Some systems may find it more efficient to make |dvi_buf| a |packed|
+array, since output of four bytes at once may be facilitated.
+@^system dependencies@>
+
+@<Glob...@>=
+@!dvi_buf:array[dvi_index] of eight_bits; {buffer for \.{DVI} output}
+@!half_buf:dvi_index; {half of |dvi_buf_size|}
+@!dvi_limit:dvi_index; {end of the current half buffer}
+@!dvi_ptr:dvi_index; {the next available buffer address}
+@!dvi_offset:integer; {|dvi_buf_size| times the number of times the
+ output buffer has been fully emptied}
+@!dvi_gone:integer; {the number of bytes already output to |dvi_file|}
+
+@ Initially the buffer is all in one piece; we will output half of it only
+after it first fills up.
+
+@<Set init...@>=
+half_buf:=dvi_buf_size div 2; dvi_limit:=dvi_buf_size; dvi_ptr:=0;
+dvi_offset:=0; dvi_gone:=0;
+
+@ The actual output of |dvi_buf[a..b]| to |dvi_file| is performed by calling
+|write_dvi(a,b)|. For best results, this procedure should be optimized to
+run as fast as possible on each particular system, since it is part of
+\TeX's inner loop. It is safe to assume that |a| and |b+1| will both be
+multiples of 4 when |write_dvi(a,b)| is called; therefore it is possible on
+many machines to use efficient methods to pack four bytes per word and to
+output an array of words with one system call.
+@^system dependencies@>
+@^inner loop@>
+@^defecation@>
+
+@p procedure write_dvi(@!a,@!b:dvi_index);
+var k:dvi_index;
+begin for k:=a to b do write(dvi_file,dvi_buf[k]);
+end;
+
+@ To put a byte in the buffer without paying the cost of invoking a procedure
+each time, we use the macro |dvi_out|.
+
+@d dvi_out(#)==@+begin dvi_buf[dvi_ptr]:=#; incr(dvi_ptr);
+ if dvi_ptr=dvi_limit then dvi_swap;
+ end
+
+@p procedure dvi_swap; {outputs half of the buffer}
+begin if dvi_limit=dvi_buf_size then
+ begin write_dvi(0,half_buf-1); dvi_limit:=half_buf;
+ dvi_offset:=dvi_offset+dvi_buf_size; dvi_ptr:=0;
+ end
+else begin write_dvi(half_buf,dvi_buf_size-1); dvi_limit:=dvi_buf_size;
+ end;
+dvi_gone:=dvi_gone+half_buf;
+end;
+
+@ Here is how we clean out the buffer when \TeX\ is all through; |dvi_ptr|
+will be a multiple of~4.
+
+@<Empty the last bytes out of |dvi_buf|@>=
+if dvi_limit=half_buf then write_dvi(half_buf,dvi_buf_size-1);
+if dvi_ptr>0 then write_dvi(0,dvi_ptr-1)
+
+@ The |dvi_four| procedure outputs four bytes in two's complement notation,
+without risking arithmetic overflow.
+
+@p procedure dvi_four(@!x:integer);
+begin if x>=0 then dvi_out(x div @'100000000)
+else begin x:=x+@'10000000000;
+ x:=x+@'10000000000;
+ dvi_out((x div @'100000000) + 128);
+ end;
+x:=x mod @'100000000; dvi_out(x div @'200000);
+x:=x mod @'200000; dvi_out(x div @'400);
+dvi_out(x mod @'400);
+end;
+
+@ A mild optimization of the output is performed by the |dvi_pop|
+routine, which issues a |pop| unless it is possible to cancel a
+`|push| |pop|' pair. The parameter to |dvi_pop| is the byte address
+following the old |push| that matches the new |pop|.
+
+@p procedure dvi_pop(@!l:integer);
+begin if (l=dvi_offset+dvi_ptr)and(dvi_ptr>0) then decr(dvi_ptr)
+else dvi_out(pop);
+end;
+
+@ Here's a procedure that outputs a font definition. Since \TeX82 uses at
+most 256 different fonts per job, |fnt_def1| is always used as the command code.
+
+@p procedure dvi_font_def(@!f:internal_font_number);
+var k:pool_pointer; {index into |str_pool|}
+begin dvi_out(fnt_def1);
+dvi_out(f-font_base-1);@/
+dvi_out(qo(font_check[f].b0));
+dvi_out(qo(font_check[f].b1));
+dvi_out(qo(font_check[f].b2));
+dvi_out(qo(font_check[f].b3));@/
+dvi_four(font_size[f]);
+dvi_four(font_dsize[f]);@/
+dvi_out(length(font_area[f]));
+dvi_out(length(font_name[f]));
+@<Output the font name whose internal number is |f|@>;
+end;
+
+@ @<Output the font name whose internal number is |f|@>=
+for k:=str_start[font_area[f]] to str_start[font_area[f]+1]-1 do
+ dvi_out(so(str_pool[k]));
+for k:=str_start[font_name[f]] to str_start[font_name[f]+1]-1 do
+ dvi_out(so(str_pool[k]))
+
+@ Versions of \TeX\ intended for small computers might well choose to omit
+the ideas in the next few parts of this program, since it is not really
+necessary to optimize the \.{DVI} code by making use of the |w0|, |x0|,
+|y0|, and |z0| commands. Furthermore, the algorithm that we are about to
+describe does not pretend to give an optimum reduction in the length
+of the \.{DVI} code; after all, speed is more important than compactness.
+But the method is surprisingly effective, and it takes comparatively little
+time.
+
+We can best understand the basic idea by first considering a simpler problem
+that has the same essential characteristics. Given a sequence of digits,
+say $3\,1\,4\,1\,5\,9\,2\,6\,5\,3\,5\,8\,9$, we want to assign subscripts
+$d$, $y$, or $z$ to each digit so as to maximize the number of ``$y$-hits''
+and ``$z$-hits''; a $y$-hit is an instance of two appearances of the same
+digit with the subscript $y$, where no $y$'s intervene between the two
+appearances, and a $z$-hit is defined similarly. For example, the sequence
+above could be decorated with subscripts as follows:
+$$3_z\,1_y\,4_d\,1_y\,5_y\,9_d\,2_d\,6_d\,5_y\,3_z\,5_y\,8_d\,9_d.$$
+There are three $y$-hits ($1_y\ldots1_y$ and $5_y\ldots5_y\ldots5_y$) and
+one $z$-hit ($3_z\ldots3_z$); there are no $d$-hits, since the two appearances
+of $9_d$ have $d$'s between them, but we don't count $d$-hits so it doesn't
+matter how many there are. These subscripts are analogous to the \.{DVI}
+commands called \\{down}, $y$, and $z$, and the digits are analogous to
+different amounts of vertical motion; a $y$-hit or $z$-hit corresponds to
+the opportunity to use the one-byte commands |y0| or |z0| in a \.{DVI} file.
+
+\TeX's method of assigning subscripts works like this: Append a new digit,
+say $\delta$, to the right of the sequence. Now look back through the
+sequence until one of the following things happens: (a)~You see
+$\delta_y$ or $\delta_z$, and this was the first time you encountered a
+$y$ or $z$ subscript, respectively. Then assign $y$ or $z$ to the new
+$\delta$; you have scored a hit. (b)~You see $\delta_d$, and no $y$
+subscripts have been encountered so far during this search. Then change
+the previous $\delta_d$ to $\delta_y$ (this corresponds to changing a
+command in the output buffer), and assign $y$ to the new $\delta$; it's
+another hit. (c)~You see $\delta_d$, and a $y$ subscript has been seen
+but not a $z$. Change the previous $\delta_d$ to $\delta_z$ and assign
+$z$ to the new $\delta$. (d)~You encounter both $y$ and $z$ subscripts
+before encountering a suitable $\delta$, or you scan all the way to the
+front of the sequence. Assign $d$ to the new $\delta$; this assignment may
+be changed later.
+
+The subscripts $3_z\,1_y\,4_d\ldots\,$ in the example above were, in fact,
+produced by this procedure, as the reader can verify. (Go ahead and try it.)
+
+@ In order to implement such an idea, \TeX\ maintains a stack of pointers
+to the \\{down}, $y$, and $z$ commands that have been generated for the
+current page. And there is a similar stack for \\{right}, |w|, and |x|
+commands. These stacks are called the down stack and right stack, and their
+top elements are maintained in the variables |down_ptr| and |right_ptr|.
+
+Each entry in these stacks contains four fields: The |width| field is
+the amount of motion down or to the right; the |location| field is the
+byte number of the \.{DVI} command in question (including the appropriate
+|dvi_offset|); the |link| field points to the next item below this one
+on the stack; and the |info| field encodes the options for possible change
+in the \.{DVI} command.
+
+@d movement_node_size=3 {number of words per entry in the down and right stacks}
+@d location(#)==mem[#+2].int {\.{DVI} byte number for a movement command}
+
+@<Glob...@>=
+@!down_ptr,@!right_ptr:pointer; {heads of the down and right stacks}
+
+@ @<Set init...@>=
+down_ptr:=null; right_ptr:=null;
+
+@ Here is a subroutine that produces a \.{DVI} command for some specified
+downward or rightward motion. It has two parameters: |w| is the amount
+of motion, and |o| is either |down1| or |right1|. We use the fact that
+the command codes have convenient arithmetic properties: |y1-down1=w1-right1|
+and |z1-down1=x1-right1|.
+
+@p procedure movement(@!w:scaled;@!o:eight_bits);
+label exit,found,not_found,2,1;
+var mstate:small_number; {have we seen a |y| or |z|?}
+@!p,@!q:pointer; {current and top nodes on the stack}
+@!k:integer; {index into |dvi_buf|, modulo |dvi_buf_size|}
+begin q:=get_node(movement_node_size); {new node for the top of the stack}
+width(q):=w; location(q):=dvi_offset+dvi_ptr;
+if o=down1 then
+ begin link(q):=down_ptr; down_ptr:=q;
+ end
+else begin link(q):=right_ptr; right_ptr:=q;
+ end;
+@<Look at the other stack entries until deciding what sort of \.{DVI} command
+ to generate; |goto found| if node |p| is a ``hit''@>;
+@<Generate a |down| or |right| command for |w| and |return|@>;
+found: @<Generate a |y0| or |z0| command in order to reuse a previous
+ appearance of~|w|@>;
+exit:end;
+
+@ The |info| fields in the entries of the down stack or the right stack
+have six possible settings: |y_here| or |z_here| mean that the \.{DVI}
+command refers to |y| or |z|, respectively (or to |w| or |x|, in the
+case of horizontal motion); |yz_OK| means that the \.{DVI} command is
+\\{down} (or \\{right}) but can be changed to either |y| or |z| (or
+to either |w| or |x|); |y_OK| means that it is \\{down} and can be changed
+to |y| but not |z|; |z_OK| is similar; and |d_fixed| means it must stay
+\\{down}.
+
+The four settings |yz_OK|, |y_OK|, |z_OK|, |d_fixed| would not need to
+be distinguished from each other if we were simply solving the
+digit-subscripting problem mentioned above. But in \TeX's case there is
+a complication because of the nested structure of |push| and |pop|
+commands. Suppose we add parentheses to the digit-subscripting problem,
+redefining hits so that $\delta_y\ldots \delta_y$ is a hit if all $y$'s between
+the $\delta$'s are enclosed in properly nested parentheses, and if the
+parenthesis level of the right-hand $\delta_y$ is deeper than or equal to
+that of the left-hand one. Thus, `(' and `)' correspond to `|push|'
+and `|pop|'. Now if we want to assign a subscript to the final 1 in the
+sequence
+$$2_y\,7_d\,1_d\,(\,8_z\,2_y\,8_z\,)\,1$$
+we cannot change the previous $1_d$ to $1_y$, since that would invalidate
+the $2_y\ldots2_y$ hit. But we can change it to $1_z$, scoring a hit
+since the intervening $8_z$'s are enclosed in parentheses.
+
+The program below removes movement nodes that are introduced after a |push|,
+before it outputs the corresponding |pop|.
+
+@d y_here=1 {|info| when the movement entry points to a |y| command}
+@d z_here=2 {|info| when the movement entry points to a |z| command}
+@d yz_OK=3 {|info| corresponding to an unconstrained \\{down} command}
+@d y_OK=4 {|info| corresponding to a \\{down} that can't become a |z|}
+@d z_OK=5 {|info| corresponding to a \\{down} that can't become a |y|}
+@d d_fixed=6 {|info| corresponding to a \\{down} that can't change}
+
+@ When the |movement| procedure gets to the label |found|, the value of
+|info(p)| will be either |y_here| or |z_here|. If it is, say, |y_here|,
+the procedure generates a |y0| command (or a |w0| command), and marks
+all |info| fields between |q| and |p| so that |y| is not OK in that range.
+
+@<Generate a |y0| or |z0| command...@>=
+info(q):=info(p);
+if info(q)=y_here then
+ begin dvi_out(o+y0-down1); {|y0| or |w0|}
+ while link(q)<>p do
+ begin q:=link(q);
+ case info(q) of
+ yz_OK: info(q):=z_OK;
+ y_OK: info(q):=d_fixed;
+ othercases do_nothing
+ endcases;
+ end;
+ end
+else begin dvi_out(o+z0-down1); {|z0| or |x0|}
+ while link(q)<>p do
+ begin q:=link(q);
+ case info(q) of
+ yz_OK: info(q):=y_OK;
+ z_OK: info(q):=d_fixed;
+ othercases do_nothing
+ endcases;
+ end;
+ end
+
+@ @<Generate a |down| or |right|...@>=
+info(q):=yz_OK;
+if abs(w)>=@'40000000 then
+ begin dvi_out(o+3); {|down4| or |right4|}
+ dvi_four(w); return;
+ end;
+if abs(w)>=@'100000 then
+ begin dvi_out(o+2); {|down3| or |right3|}
+ if w<0 then w:=w+@'100000000;
+ dvi_out(w div @'200000); w:=w mod @'200000; goto 2;
+ end;
+if abs(w)>=@'200 then
+ begin dvi_out(o+1); {|down2| or |right2|}
+ if w<0 then w:=w+@'200000;
+ goto 2;
+ end;
+dvi_out(o); {|down1| or |right1|}
+if w<0 then w:=w+@'400;
+goto 1;
+2: dvi_out(w div @'400);
+1: dvi_out(w mod @'400); return
+
+@ As we search through the stack, we are in one of three states,
+|y_seen|, |z_seen|, or |none_seen|, depending on whether we have
+encountered |y_here| or |z_here| nodes. These states are encoded as
+multiples of 6, so that they can be added to the |info| fields for quick
+decision-making.
+@^inner loop@>
+
+@d none_seen=0 {no |y_here| or |z_here| nodes have been encountered yet}
+@d y_seen=6 {we have seen |y_here| but not |z_here|}
+@d z_seen=12 {we have seen |z_here| but not |y_here|}
+
+@<Look at the other stack entries until deciding...@>=
+p:=link(q); mstate:=none_seen;
+while p<>null do
+ begin if width(p)=w then @<Consider a node with matching width;
+ |goto found| if it's a hit@>
+ else case mstate+info(p) of
+ none_seen+y_here: mstate:=y_seen;
+ none_seen+z_here: mstate:=z_seen;
+ y_seen+z_here,z_seen+y_here: goto not_found;
+ othercases do_nothing
+ endcases;
+ p:=link(p);
+ end;
+not_found:
+
+@ We might find a valid hit in a |y| or |z| byte that is already gone
+from the buffer. But we can't change bytes that are gone forever; ``the
+moving finger writes, $\ldots\,\,$.''
+
+@<Consider a node with matching width...@>=
+case mstate+info(p) of
+none_seen+yz_OK,none_seen+y_OK,z_seen+yz_OK,z_seen+y_OK:@t@>@;@/
+ if location(p)<dvi_gone then goto not_found
+ else @<Change buffered instruction to |y| or |w| and |goto found|@>;
+none_seen+z_OK,y_seen+yz_OK,y_seen+z_OK:@t@>@;@/
+ if location(p)<dvi_gone then goto not_found
+ else @<Change buffered instruction to |z| or |x| and |goto found|@>;
+none_seen+y_here,none_seen+z_here,y_seen+z_here,z_seen+y_here: goto found;
+othercases do_nothing
+endcases
+
+@ @<Change buffered instruction to |y| or |w| and |goto found|@>=
+begin k:=location(p)-dvi_offset;
+if k<0 then k:=k+dvi_buf_size;
+dvi_buf[k]:=dvi_buf[k]+y1-down1;
+info(p):=y_here; goto found;
+end
+
+@ @<Change buffered instruction to |z| or |x| and |goto found|@>=
+begin k:=location(p)-dvi_offset;
+if k<0 then k:=k+dvi_buf_size;
+dvi_buf[k]:=dvi_buf[k]+z1-down1;
+info(p):=z_here; goto found;
+end
+
+@ In case you are wondering when all the movement nodes are removed from
+\TeX's memory, the answer is that they are recycled just before
+|hlist_out| and |vlist_out| finish outputting a box. This restores the
+down and right stacks to the state they were in before the box was output,
+except that some |info|'s may have become more restrictive.
+
+@p procedure prune_movements(@!l:integer);
+ {delete movement nodes with |location>=l|}
+label done,exit;
+var p:pointer; {node being deleted}
+begin while down_ptr<>null do
+ begin if location(down_ptr)<l then goto done;
+ p:=down_ptr; down_ptr:=link(p); free_node(p,movement_node_size);
+ end;
+done: while right_ptr<>null do
+ begin if location(right_ptr)<l then return;
+ p:=right_ptr; right_ptr:=link(p); free_node(p,movement_node_size);
+ end;
+exit:end;
+
+@ The actual distances by which we want to move might be computed as the
+sum of several separate movements. For example, there might be several
+glue nodes in succession, or we might want to move right by the width of
+some box plus some amount of glue. More importantly, the baselineskip
+distances are computed in terms of glue together with the depth and
+height of adjacent boxes, and we want the \.{DVI} file to lump these
+three quantities together into a single motion.
+
+Therefore, \TeX\ maintains two pairs of global variables: |dvi_h| and |dvi_v|
+are the |h| and |v| coordinates corresponding to the commands actually
+output to the \.{DVI} file, while |cur_h| and |cur_v| are the coordinates
+corresponding to the current state of the output routines. Coordinate
+changes will accumulate in |cur_h| and |cur_v| without being reflected
+in the output, until such a change becomes necessary or desirable; we
+can call the |movement| procedure whenever we want to make |dvi_h=cur_h|
+or |dvi_v=cur_v|.
+
+The current font reflected in the \.{DVI} output is called |dvi_f|;
+there is no need for a `\\{cur\_f}' variable.
+
+The depth of nesting of |hlist_out| and |vlist_out| is called |cur_s|;
+this is essentially the depth of |push| commands in the \.{DVI} output.
+
+@d synch_h==if cur_h<>dvi_h then
+ begin movement(cur_h-dvi_h,right1); dvi_h:=cur_h;
+ end
+@d synch_v==if cur_v<>dvi_v then
+ begin movement(cur_v-dvi_v,down1); dvi_v:=cur_v;
+ end
+
+@<Glob...@>=
+@!dvi_h,@!dvi_v:scaled; {a \.{DVI} reader program thinks we are here}
+@!cur_h,@!cur_v:scaled; {\TeX\ thinks we are here}
+@!dvi_f:internal_font_number; {the current font}
+@!cur_s:integer; {current depth of output box nesting, initially $-1$}
+
+@ @<Initialize variables as |ship_out| begins@>=
+dvi_h:=0; dvi_v:=0; cur_h:=h_offset; dvi_f:=null_font;
+ensure_dvi_open;
+if total_pages=0 then
+ begin dvi_out(pre); dvi_out(id_byte); {output the preamble}
+@^preamble of \.{DVI} file@>
+ dvi_four(25400000); dvi_four(473628672); {conversion ratio for sp}
+ prepare_mag; dvi_four(mag); {magnification factor is frozen}
+ old_setting:=selector; selector:=new_string;
+ print(" TeX output "); print_int(year); print_char(".");
+ print_two(month); print_char("."); print_two(day);
+ print_char(":"); print_two(time div 60);
+ print_two(time mod 60);
+ selector:=old_setting; dvi_out(cur_length);
+ for s:=str_start[str_ptr] to pool_ptr-1 do dvi_out(so(str_pool[s]));
+ pool_ptr:=str_start[str_ptr]; {flush the current string}
+ end
+
+@ When |hlist_out| is called, its duty is to output the box represented
+by the |hlist_node| pointed to by |temp_ptr|. The reference point of that
+box has coordinates |(cur_h,cur_v)|.
+
+Similarly, when |vlist_out| is called, its duty is to output the box represented
+by the |vlist_node| pointed to by |temp_ptr|. The reference point of that
+box has coordinates |(cur_h,cur_v)|.
+@^recursion@>
+
+@p procedure@?vlist_out; forward; {|hlist_out| and |vlist_out| are mutually
+ recursive}
+
+@ The recursive procedures |hlist_out| and |vlist_out| each have local variables
+|save_h| and |save_v| to hold the values of |dvi_h| and |dvi_v| just before
+entering a new level of recursion. In effect, the values of |save_h| and
+|save_v| on \TeX's run-time stack correspond to the values of |h| and |v|
+that a \.{DVI}-reading program will push onto its coordinate stack.
+
+@d move_past=13 {go to this label when advancing past glue or a rule}
+@d fin_rule=14 {go to this label to finish processing a rule}
+@d next_p=15 {go to this label when finished with node |p|}
+
+@p @t\4@>@<Declare procedures needed in |hlist_out|, |vlist_out|@>@t@>@/
+procedure hlist_out; {output an |hlist_node| box}
+label reswitch, move_past, fin_rule, next_p;
+var base_line: scaled; {the baseline coordinate for this box}
+@!left_edge: scaled; {the left coordinate for this box}
+@!save_h,@!save_v: scaled; {what |dvi_h| and |dvi_v| should pop to}
+@!this_box: pointer; {pointer to containing box}
+@!g_order: glue_ord; {applicable order of infinity for glue}
+@!g_sign: normal..shrinking; {selects type of glue}
+@!p:pointer; {current position in the hlist}
+@!save_loc:integer; {\.{DVI} byte location upon entry}
+@!leader_box:pointer; {the leader box being replicated}
+@!leader_wd:scaled; {width of leader box being replicated}
+@!lx:scaled; {extra space between leader boxes}
+@!outer_doing_leaders:boolean; {were we doing leaders?}
+@!edge:scaled; {left edge of sub-box, or right edge of leader space}
+@!glue_temp:real; {glue value before rounding}
+@!cur_glue:real; {glue seen so far}
+@!cur_g:scaled; {rounded equivalent of |cur_glue| times the glue ratio}
+begin cur_g:=0; cur_glue:=float_constant(0);
+this_box:=temp_ptr; g_order:=glue_order(this_box);
+g_sign:=glue_sign(this_box); p:=list_ptr(this_box);
+incr(cur_s);
+if cur_s>0 then dvi_out(push);
+if cur_s>max_push then max_push:=cur_s;
+save_loc:=dvi_offset+dvi_ptr; base_line:=cur_v; left_edge:=cur_h;
+while p<>null do @<Output node |p| for |hlist_out| and move to the next node,
+ maintaining the condition |cur_v=base_line|@>;
+prune_movements(save_loc);
+if cur_s>0 then dvi_pop(save_loc);
+decr(cur_s);
+end;
+
+@ We ought to give special care to the efficiency of one part of |hlist_out|,
+since it belongs to \TeX's inner loop. When a |char_node| is encountered,
+we save a little time by processing several nodes in succession until
+reaching a non-|char_node|. The program uses the fact that |set_char_0=0|.
+@^inner loop@>
+
+@<Output node |p| for |hlist_out|...@>=
+reswitch: if is_char_node(p) then
+ begin synch_h; synch_v;
+ repeat f:=font(p); c:=character(p);
+ if f<>dvi_f then @<Change font |dvi_f| to |f|@>;
+ if c>=qi(128) then dvi_out(set1);
+ dvi_out(qo(c));@/
+ cur_h:=cur_h+char_width(f)(char_info(f)(c));
+ p:=link(p);
+ until not is_char_node(p);
+ dvi_h:=cur_h;
+ end
+else @<Output the non-|char_node| |p| for |hlist_out|
+ and move to the next node@>
+
+@ @<Change font |dvi_f| to |f|@>=
+begin if not font_used[f] then
+ begin dvi_font_def(f); font_used[f]:=true;
+ end;
+if f<=64+font_base then dvi_out(f-font_base-1+fnt_num_0)
+else begin dvi_out(fnt1); dvi_out(f-font_base-1);
+ end;
+dvi_f:=f;
+end
+
+@ @<Output the non-|char_node| |p| for |hlist_out|...@>=
+begin case type(p) of
+hlist_node,vlist_node:@<Output a box in an hlist@>;
+rule_node: begin rule_ht:=height(p); rule_dp:=depth(p); rule_wd:=width(p);
+ goto fin_rule;
+ end;
+whatsit_node: @<Output the whatsit node |p| in an hlist@>;
+glue_node: @<Move right or output leaders@>;
+kern_node,math_node:cur_h:=cur_h+width(p);
+ligature_node: @<Make node |p| look like a |char_node| and |goto reswitch|@>;
+othercases do_nothing
+endcases;@/
+goto next_p;
+fin_rule: @<Output a rule in an hlist@>;
+move_past: cur_h:=cur_h+rule_wd;
+next_p:p:=link(p);
+end
+
+@ @<Output a box in an hlist@>=
+if list_ptr(p)=null then cur_h:=cur_h+width(p)
+else begin save_h:=dvi_h; save_v:=dvi_v;
+ cur_v:=base_line+shift_amount(p); {shift the box down}
+ temp_ptr:=p; edge:=cur_h;
+ if type(p)=vlist_node then vlist_out@+else hlist_out;
+ dvi_h:=save_h; dvi_v:=save_v;
+ cur_h:=edge+width(p); cur_v:=base_line;
+ end
+
+@ @<Output a rule in an hlist@>=
+if is_running(rule_ht) then rule_ht:=height(this_box);
+if is_running(rule_dp) then rule_dp:=depth(this_box);
+rule_ht:=rule_ht+rule_dp; {this is the rule thickness}
+if (rule_ht>0)and(rule_wd>0) then {we don't output empty rules}
+ begin synch_h; cur_v:=base_line+rule_dp; synch_v;
+ dvi_out(set_rule); dvi_four(rule_ht); dvi_four(rule_wd);
+ cur_v:=base_line; dvi_h:=dvi_h+rule_wd;
+ end
+
+@ @d billion==float_constant(1000000000)
+@d vet_glue(#)== glue_temp:=#;
+ if glue_temp>billion then
+ glue_temp:=billion
+ else if glue_temp<-billion then
+ glue_temp:=-billion
+
+@<Move right or output leaders@>=
+begin g:=glue_ptr(p); rule_wd:=width(g)-cur_g;
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin cur_glue:=cur_glue+stretch(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+@^real multiplication@>
+ cur_g:=round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin cur_glue:=cur_glue-shrink(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+ cur_g:=round(glue_temp);
+ end;
+ end;
+rule_wd:=rule_wd+cur_g;
+if subtype(p)>=a_leaders then
+ @<Output leaders in an hlist, |goto fin_rule| if a rule
+ or to |next_p| if done@>;
+goto move_past;
+end
+
+@ @<Output leaders in an hlist...@>=
+begin leader_box:=leader_ptr(p);
+if type(leader_box)=rule_node then
+ begin rule_ht:=height(leader_box); rule_dp:=depth(leader_box);
+ goto fin_rule;
+ end;
+leader_wd:=width(leader_box);
+if (leader_wd>0)and(rule_wd>0) then
+ begin rule_wd:=rule_wd+10; {compensate for floating-point rounding}
+ edge:=cur_h+rule_wd; lx:=0;
+ @<Let |cur_h| be the position of the first box, and set |leader_wd+lx|
+ to the spacing between corresponding parts of boxes@>;
+ while cur_h+leader_wd<=edge do
+ @<Output a leader box at |cur_h|,
+ then advance |cur_h| by |leader_wd+lx|@>;
+ cur_h:=edge-10; goto next_p;
+ end;
+end
+
+@ The calculations related to leaders require a bit of care. First, in the
+case of |a_leaders| (aligned leaders), we want to move |cur_h| to
+|left_edge| plus the smallest multiple of |leader_wd| for which the result
+is not less than the current value of |cur_h|; i.e., |cur_h| should become
+$|left_edge|+|leader_wd|\times\lceil
+(|cur_h|-|left_edge|)/|leader_wd|\rceil$. The program here should work in
+all cases even though some implementations of \PASCAL\ give nonstandard
+results for the |div| operation when |cur_h| is less than |left_edge|.
+
+In the case of |c_leaders| (centered leaders), we want to increase |cur_h|
+by half of the excess space not occupied by the leaders; and in the
+case of |x_leaders| (expanded leaders) we increase |cur_h|
+by $1/(q+1)$ of this excess space, where $q$ is the number of times the
+leader box will be replicated. Slight inaccuracies in the division might
+accumulate; half of this rounding error is placed at each end of the leaders.
+
+@<Let |cur_h| be the position of the first box, ...@>=
+if subtype(p)=a_leaders then
+ begin save_h:=cur_h;
+ cur_h:=left_edge+leader_wd*((cur_h-left_edge)@!div leader_wd);
+ if cur_h<save_h then cur_h:=cur_h+leader_wd;
+ end
+else begin lq:=rule_wd div leader_wd; {the number of box copies}
+ lr:=rule_wd mod leader_wd; {the remaining space}
+ if subtype(p)=c_leaders then cur_h:=cur_h+(lr div 2)
+ else begin lx:=lr div (lq+1);
+ cur_h:=cur_h+((lr-(lq-1)*lx) div 2);
+ end;
+ end
+
+@ The `\\{synch}' operations here are intended to decrease the number of
+bytes needed to specify horizontal and vertical motion in the \.{DVI} output.
+
+@<Output a leader box at |cur_h|, ...@>=
+begin cur_v:=base_line+shift_amount(leader_box); synch_v; save_v:=dvi_v;@/
+synch_h; save_h:=dvi_h; temp_ptr:=leader_box;
+outer_doing_leaders:=doing_leaders; doing_leaders:=true;
+if type(leader_box)=vlist_node then vlist_out@+else hlist_out;
+doing_leaders:=outer_doing_leaders;
+dvi_v:=save_v; dvi_h:=save_h; cur_v:=base_line;
+cur_h:=save_h+leader_wd+lx;
+end
+
+@ The |vlist_out| routine is similar to |hlist_out|, but a bit simpler.
+
+@p procedure vlist_out; {output a |vlist_node| box}
+label move_past, fin_rule, next_p;
+var left_edge: scaled; {the left coordinate for this box}
+@!top_edge: scaled; {the top coordinate for this box}
+@!save_h,@!save_v: scaled; {what |dvi_h| and |dvi_v| should pop to}
+@!this_box: pointer; {pointer to containing box}
+@!g_order: glue_ord; {applicable order of infinity for glue}
+@!g_sign: normal..shrinking; {selects type of glue}
+@!p:pointer; {current position in the vlist}
+@!save_loc:integer; {\.{DVI} byte location upon entry}
+@!leader_box:pointer; {the leader box being replicated}
+@!leader_ht:scaled; {height of leader box being replicated}
+@!lx:scaled; {extra space between leader boxes}
+@!outer_doing_leaders:boolean; {were we doing leaders?}
+@!edge:scaled; {bottom boundary of leader space}
+@!glue_temp:real; {glue value before rounding}
+@!cur_glue:real; {glue seen so far}
+@!cur_g:scaled; {rounded equivalent of |cur_glue| times the glue ratio}
+begin cur_g:=0; cur_glue:=float_constant(0);
+this_box:=temp_ptr; g_order:=glue_order(this_box);
+g_sign:=glue_sign(this_box); p:=list_ptr(this_box);
+incr(cur_s);
+if cur_s>0 then dvi_out(push);
+if cur_s>max_push then max_push:=cur_s;
+save_loc:=dvi_offset+dvi_ptr; left_edge:=cur_h; cur_v:=cur_v-height(this_box);
+top_edge:=cur_v;
+while p<>null do @<Output node |p| for |vlist_out| and move to the next node,
+ maintaining the condition |cur_h=left_edge|@>;
+prune_movements(save_loc);
+if cur_s>0 then dvi_pop(save_loc);
+decr(cur_s);
+end;
+
+@ @<Output node |p| for |vlist_out|...@>=
+begin if is_char_node(p) then confusion("vlistout")
+@:this can't happen vlistout}{\quad vlistout@>
+else @<Output the non-|char_node| |p| for |vlist_out|@>;
+next_p:p:=link(p);
+end
+
+@ @<Output the non-|char_node| |p| for |vlist_out|@>=
+begin case type(p) of
+hlist_node,vlist_node:@<Output a box in a vlist@>;
+rule_node: begin rule_ht:=height(p); rule_dp:=depth(p); rule_wd:=width(p);
+ goto fin_rule;
+ end;
+whatsit_node: @<Output the whatsit node |p| in a vlist@>;
+glue_node: @<Move down or output leaders@>;
+kern_node:cur_v:=cur_v+width(p);
+othercases do_nothing
+endcases;@/
+goto next_p;
+fin_rule: @<Output a rule in a vlist, |goto next_p|@>;
+move_past: cur_v:=cur_v+rule_ht;
+end
+
+@ The |synch_v| here allows the \.{DVI} output to use one-byte commands
+for adjusting |v| in most cases, since the baselineskip distance will
+usually be constant.
+
+@<Output a box in a vlist@>=
+if list_ptr(p)=null then cur_v:=cur_v+height(p)+depth(p)
+else begin cur_v:=cur_v+height(p); synch_v;
+ save_h:=dvi_h; save_v:=dvi_v;
+ cur_h:=left_edge+shift_amount(p); {shift the box right}
+ temp_ptr:=p;
+ if type(p)=vlist_node then vlist_out@+else hlist_out;
+ dvi_h:=save_h; dvi_v:=save_v;
+ cur_v:=save_v+depth(p); cur_h:=left_edge;
+ end
+
+@ @<Output a rule in a vlist...@>=
+if is_running(rule_wd) then rule_wd:=width(this_box);
+rule_ht:=rule_ht+rule_dp; {this is the rule thickness}
+cur_v:=cur_v+rule_ht;
+if (rule_ht>0)and(rule_wd>0) then {we don't output empty rules}
+ begin synch_h; synch_v;
+ dvi_out(put_rule); dvi_four(rule_ht); dvi_four(rule_wd);
+ end;
+goto next_p
+
+@ @<Move down or output leaders@>=
+begin g:=glue_ptr(p); rule_ht:=width(g)-cur_g;
+if g_sign<>normal then
+ begin if g_sign=stretching then
+ begin if stretch_order(g)=g_order then
+ begin cur_glue:=cur_glue+stretch(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+@^real multiplication@>
+ cur_g:=round(glue_temp);
+ end;
+ end
+ else if shrink_order(g)=g_order then
+ begin cur_glue:=cur_glue-shrink(g);
+ vet_glue(float(glue_set(this_box))*cur_glue);
+ cur_g:=round(glue_temp);
+ end;
+ end;
+rule_ht:=rule_ht+cur_g;
+if subtype(p)>=a_leaders then
+ @<Output leaders in a vlist, |goto fin_rule| if a rule
+ or to |next_p| if done@>;
+goto move_past;
+end
+
+@ @<Output leaders in a vlist...@>=
+begin leader_box:=leader_ptr(p);
+if type(leader_box)=rule_node then
+ begin rule_wd:=width(leader_box); rule_dp:=0;
+ goto fin_rule;
+ end;
+leader_ht:=height(leader_box)+depth(leader_box);
+if (leader_ht>0)and(rule_ht>0) then
+ begin rule_ht:=rule_ht+10; {compensate for floating-point rounding}
+ edge:=cur_v+rule_ht; lx:=0;
+ @<Let |cur_v| be the position of the first box, and set |leader_ht+lx|
+ to the spacing between corresponding parts of boxes@>;
+ while cur_v+leader_ht<=edge do
+ @<Output a leader box at |cur_v|,
+ then advance |cur_v| by |leader_ht+lx|@>;
+ cur_v:=edge-10; goto next_p;
+ end;
+end
+
+@ @<Let |cur_v| be the position of the first box, ...@>=
+if subtype(p)=a_leaders then
+ begin save_v:=cur_v;
+ cur_v:=top_edge+leader_ht*((cur_v-top_edge)@!div leader_ht);
+ if cur_v<save_v then cur_v:=cur_v+leader_ht;
+ end
+else begin lq:=rule_ht div leader_ht; {the number of box copies}
+ lr:=rule_ht mod leader_ht; {the remaining space}
+ if subtype(p)=c_leaders then cur_v:=cur_v+(lr div 2)
+ else begin lx:=lr div (lq+1);
+ cur_v:=cur_v+((lr-(lq-1)*lx) div 2);
+ end;
+ end
+
+@ When we reach this part of the program, |cur_v| indicates the top of a
+leader box, not its baseline.
+
+@<Output a leader box at |cur_v|, ...@>=
+begin cur_h:=left_edge+shift_amount(leader_box); synch_h; save_h:=dvi_h;@/
+cur_v:=cur_v+height(leader_box); synch_v; save_v:=dvi_v;
+temp_ptr:=leader_box;
+outer_doing_leaders:=doing_leaders; doing_leaders:=true;
+if type(leader_box)=vlist_node then vlist_out@+else hlist_out;
+doing_leaders:=outer_doing_leaders;
+dvi_v:=save_v; dvi_h:=save_h; cur_h:=left_edge;
+cur_v:=save_v-height(leader_box)+leader_ht+lx;
+end
+
+@ The |hlist_out| and |vlist_out| procedures are now complete, so we are
+ready for the |ship_out| routine that gets them started in the first place.
+
+@p procedure ship_out(@!p:pointer); {output the box |p|}
+label done;
+var page_loc:integer; {location of the current |bop|}
+@!j,@!k:0..9; {indices to first ten count registers}
+@!s:pool_pointer; {index into |str_pool|}
+@!old_setting:0..max_selector; {saved |selector| setting}
+begin if tracing_output>0 then
+ begin print_nl(""); print_ln;
+ print("Completed box being shipped out");
+@.Completed box...@>
+ end;
+if term_offset>max_print_line-9 then print_ln
+else if (term_offset>0)or(file_offset>0) then print_char(" ");
+print_char("["); j:=9;
+while (count(j)=0)and(j>0) do decr(j);
+for k:=0 to j do
+ begin print_int(count(k));
+ if k<j then print_char(".");
+ end;
+update_terminal;
+if tracing_output>0 then
+ begin print_char("]");
+ begin_diagnostic; show_box(p); end_diagnostic(true);
+ end;
+@<Ship box |p| out@>;
+if tracing_output<=0 then print_char("]");
+dead_cycles:=0;
+update_terminal; {progress report}
+@<Flush the box from memory, showing statistics if requested@>;
+end;
+
+@ @<Flush the box from memory, showing statistics if requested@>=
+@!stat if tracing_stats>1 then
+ begin print_nl("Memory usage before: ");
+@.Memory usage...@>
+ print_int(var_used); print_char("&");
+ print_int(dyn_used); print_char(";");
+ end;
+tats@/
+flush_node_list(p);
+@!stat if tracing_stats>1 then
+ begin print(" after: ");
+ print_int(var_used); print_char("&");
+ print_int(dyn_used); print("; still untouched: ");
+ print_int(hi_mem_min-lo_mem_max-1); print_ln;
+ end;
+tats
+
+@ @<Ship box |p| out@>=
+@<Update the values of |max_h| and |max_v|; but if the page is too large,
+ |goto done|@>;
+@<Initialize variables as |ship_out| begins@>;
+page_loc:=dvi_offset+dvi_ptr;
+dvi_out(bop);
+for k:=0 to 9 do dvi_four(count(k));
+dvi_four(last_bop); last_bop:=page_loc;
+cur_v:=height(p)+v_offset; temp_ptr:=p;
+if type(p)=vlist_node then vlist_out@+else hlist_out;
+dvi_out(eop); incr(total_pages); cur_s:=-1;
+done:
+
+@ Sometimes the user will generate a huge page because other error messages
+are being ignored. Such pages are not output to the \.{dvi} file, since they
+may confuse the printing software.
+
+@<Update the values of |max_h| and |max_v|; but if the page is too large...@>=
+if (height(p)>max_dimen)or@|(depth(p)>max_dimen)or@|
+ (height(p)+depth(p)+v_offset>max_dimen)or@|
+ (width(p)+h_offset>max_dimen) then
+ begin print_err("Huge page cannot be shipped out");
+@.Huge page...@>
+ help2("The page just created is more than 18 feet tall or")@/
+ ("more than 18 feet wide, so I suspect something went wrong.");
+ error;
+ if tracing_output<=0 then
+ begin begin_diagnostic;
+ print_nl("The following box has been deleted:");
+@.The following...deleted@>
+ show_box(p);
+ end_diagnostic(true);
+ end;
+ goto done;
+ end;
+if height(p)+depth(p)+v_offset>max_v then max_v:=height(p)+depth(p)+v_offset;
+if width(p)+h_offset>max_h then max_h:=width(p)+h_offset
+
+@ At the end of the program, we must finish things off by writing the
+post\-amble. If |total_pages=0|, the \.{DVI} file was never opened.
+If |total_pages>=65536|, the \.{DVI} file will lie. And if
+|max_push>=65536|, the user deserves whatever chaos might ensue.
+
+An integer variable |k| will be declared for use by this routine.
+
+@<Finish the \.{DVI} file@>=
+while cur_s>-1 do
+ begin if cur_s>0 then dvi_out(pop)
+ else begin dvi_out(eop); incr(total_pages);
+ end;
+ decr(cur_s);
+ end;
+if total_pages=0 then print_nl("No pages of output.")
+@.No pages of output@>
+else begin dvi_out(post); {beginning of the postamble}
+ dvi_four(last_bop); last_bop:=dvi_offset+dvi_ptr-5; {|post| location}
+ dvi_four(25400000); dvi_four(473628672); {conversion ratio for sp}
+ prepare_mag; dvi_four(mag); {magnification factor}
+ dvi_four(max_v); dvi_four(max_h);@/
+ dvi_out(max_push div 256); dvi_out(max_push mod 256);@/
+ dvi_out((total_pages div 256) mod 256); dvi_out(total_pages mod 256);@/
+ @<Output the font definitions for all fonts that were used@>;
+ dvi_out(post_post); dvi_four(last_bop); dvi_out(id_byte);@/
+ k:=4+((dvi_buf_size-dvi_ptr) mod 4); {the number of 223's}
+ while k>0 do
+ begin dvi_out(223); decr(k);
+ end;
+ @<Empty the last bytes out of |dvi_buf|@>;
+ print_nl("Output written on "); slow_print(output_file_name);
+@.Output written on x@>
+ print(" ("); print_int(total_pages); print(" page");
+ if total_pages<>1 then print_char("s");
+ print(", "); print_int(dvi_offset+dvi_ptr); print(" bytes).");
+ b_close(dvi_file);
+ end
+
+@ @<Output the font definitions...@>=
+while font_ptr>font_base do
+ begin if font_used[font_ptr] then dvi_font_def(font_ptr);
+ decr(font_ptr);
+ end
+
+@* \[33] Packaging.
+We're essentially done with the parts of \TeX\ that are concerned with
+the input (|get_next|) and the output (|ship_out|). So it's time to
+get heavily into the remaining part, which does the real work of typesetting.
+
+After lists are constructed, \TeX\ wraps them up and puts them into boxes.
+Two major subroutines are given the responsibility for this task: |hpack|
+applies to horizontal lists (hlists) and |vpack| applies to vertical lists
+(vlists). The main duty of |hpack| and |vpack| is to compute the dimensions
+of the resulting boxes, and to adjust the glue if one of those dimensions
+is pre-specified. The computed sizes normally enclose all of the material
+inside the new box; but some items may stick out if negative glue is used,
+if the box is overfull, or if a \.{\\vbox} includes other boxes that have
+been shifted left.
+
+The subroutine call |hpack(p,w,m)| returns a pointer to an |hlist_node|
+for a box containing the hlist that starts at |p|. Parameter |w| specifies
+a width; and parameter |m| is either `|exactly|' or `|additional|'. Thus,
+|hpack(p,w,exactly)| produces a box whose width is exactly |w|, while
+|hpack(p,w,additional)| yields a box whose width is the natural width plus
+|w|. It is convenient to define a macro called `|natural|' to cover the
+most common case, so that we can say |hpack(p,natural)| to get a box that
+has the natural width of list |p|.
+
+Similarly, |vpack(p,w,m)| returns a pointer to a |vlist_node| for a
+box containing the vlist that starts at |p|. In this case |w| represents
+a height instead of a width; the parameter |m| is interpreted as in |hpack|.
+
+@d exactly=0 {a box dimension is pre-specified}
+@d additional=1 {a box dimension is increased from the natural one}
+@d natural==0,additional {shorthand for parameters to |hpack| and |vpack|}
+
+@ The parameters to |hpack| and |vpack| correspond to \TeX's primitives
+like `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note
+that `\.{\\hbox}' with no dimension following it is equivalent to
+`\.{\\hbox} \.{spread} \.{0pt}'. The |scan_spec| subroutine scans such
+constructions in the user's input, including the mandatory left brace that
+follows them, and it puts the specification onto |save_stack| so that the
+desired box can later be obtained by executing the following code:
+$$\vbox{\halign{#\hfil\cr
+|save_ptr:=save_ptr-2;|\cr
+|hpack(p,saved(1),saved(0)).|\cr}}$$
+Special care is necessary to ensure that the special |save_stack| codes
+are placed just below the new group code, because scanning can change
+|save_stack| when \.{\\csname} appears.
+
+@p procedure scan_spec(@!c:group_code;@!three_codes:boolean);
+ {scans a box specification and left brace}
+label found;
+var @!s:integer; {temporarily saved value}
+@!spec_code:exactly..additional;
+begin if three_codes then s:=saved(0);
+if scan_keyword("to") then spec_code:=exactly
+@.to@>
+else if scan_keyword("spread") then spec_code:=additional
+@.spread@>
+else begin spec_code:=additional; cur_val:=0;
+ goto found;
+ end;
+scan_normal_dimen;
+found: if three_codes then
+ begin saved(0):=s; incr(save_ptr);
+ end;
+saved(0):=spec_code; saved(1):=cur_val; save_ptr:=save_ptr+2;
+new_save_level(c); scan_left_brace;
+end;
+
+@ To figure out the glue setting, |hpack| and |vpack| determine how much
+stretchability and shrinkability are present, considering all four orders
+of infinity. The highest order of infinity that has a nonzero coefficient
+is then used as if no other orders were present.
+
+For example, suppose that the given list contains six glue nodes with
+the respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill.
+Then the total is essentially 2fil; and if a total additional space of 6pt
+is to be achieved by stretching, the actual amounts of stretch will be
+0pt, 0pt, 15pt, 0pt, $-9$pt, and 0pt, since only `fil' glue will be
+considered. (The `fill' glue is therefore not really stretching infinitely
+with respect to `fil'; nobody would actually want that to happen.)
+
+The arrays |total_stretch| and |total_shrink| are used to determine how much
+glue of each kind is present. A global variable |last_badness| is used
+to implement \.{\\badness}.
+
+@<Glob...@>=
+@!total_stretch, @!total_shrink: array[glue_ord] of scaled;
+ {glue found by |hpack| or |vpack|}
+@!last_badness:integer; {badness of the most recently packaged box}
+
+@ If the global variable |adjust_tail| is non-null, the |hpack| routine
+also removes all occurrences of |ins_node|, |mark_node|, and |adjust_node|
+items and appends the resulting material onto the list that ends at
+location |adjust_tail|.
+
+@< Glob...@>=
+@!adjust_tail:pointer; {tail of adjustment list}
+
+@ @<Set init...@>=adjust_tail:=null; last_badness:=0;
+
+@ Here now is |hpack|, which contains few if any surprises.
+
+@p function hpack(@!p:pointer;@!w:scaled;@!m:small_number):pointer;
+label reswitch, common_ending, exit;
+var r:pointer; {the box node that will be returned}
+@!q:pointer; {trails behind |p|}
+@!h,@!d,@!x:scaled; {height, depth, and natural width}
+@!s:scaled; {shift amount}
+@!g:pointer; {points to a glue specification}
+@!o:glue_ord; {order of infinity}
+@!f:internal_font_number; {the font in a |char_node|}
+@!i:four_quarters; {font information about a |char_node|}
+@!hd:eight_bits; {height and depth indices for a character}
+begin last_badness:=0; r:=get_node(box_node_size); type(r):=hlist_node;
+subtype(r):=min_quarterword; shift_amount(r):=0;
+q:=r+list_offset; link(q):=p;@/
+h:=0; @<Clear dimensions to zero@>;
+while p<>null do @<Examine node |p| in the hlist, taking account of its effect
+ on the dimensions of the new box, or moving it to the adjustment list;
+ then advance |p| to the next node@>;
+if adjust_tail<>null then link(adjust_tail):=null;
+height(r):=h; depth(r):=d;@/
+@<Determine the value of |width(r)| and the appropriate glue setting;
+ then |return| or |goto common_ending|@>;
+common_ending: @<Finish issuing a diagnostic message
+ for an overfull or underfull hbox@>;
+exit: hpack:=r;
+end;
+
+@ @<Clear dimensions to zero@>=
+d:=0; x:=0;
+total_stretch[normal]:=0; total_shrink[normal]:=0;
+total_stretch[fil]:=0; total_shrink[fil]:=0;
+total_stretch[fill]:=0; total_shrink[fill]:=0;
+total_stretch[filll]:=0; total_shrink[filll]:=0
+
+@ @<Examine node |p| in the hlist, taking account of its effect...@>=
+@^inner loop@>
+begin reswitch: while is_char_node(p) do
+ @<Incorporate character dimensions into the dimensions of
+ the hbox that will contain~it, then move to the next node@>;
+if p<>null then
+ begin case type(p) of
+ hlist_node,vlist_node,rule_node,unset_node:
+ @<Incorporate box dimensions into the dimensions of
+ the hbox that will contain~it@>;
+ ins_node,mark_node,adjust_node: if adjust_tail<>null then
+ @<Transfer node |p| to the adjustment list@>;
+ whatsit_node:@<Incorporate a whatsit node into an hbox@>;
+ glue_node:@<Incorporate glue into the horizontal totals@>;
+ kern_node,math_node: x:=x+width(p);
+ ligature_node: @<Make node |p| look like a |char_node|
+ and |goto reswitch|@>;
+ othercases do_nothing
+ endcases;@/
+ p:=link(p);
+ end;
+end
+
+
+@ @<Make node |p| look like a |char_node| and |goto reswitch|@>=
+begin mem[lig_trick]:=mem[lig_char(p)]; link(lig_trick):=link(p);
+p:=lig_trick; goto reswitch;
+end
+
+@ The code here implicitly uses the fact that running dimensions are
+indicated by |null_flag|, which will be ignored in the calculations
+because it is a highly negative number.
+
+@<Incorporate box dimensions into the dimensions of the hbox...@>=
+begin x:=x+width(p);
+if type(p)>=rule_node then s:=0 @+else s:=shift_amount(p);
+if height(p)-s>h then h:=height(p)-s;
+if depth(p)+s>d then d:=depth(p)+s;
+end
+
+@ The following code is part of \TeX's inner loop; i.e., adding another
+character of text to the user's input will cause each of these instructions
+to be exercised one more time.
+@^inner loop@>
+
+@<Incorporate character dimensions into the dimensions of the hbox...@>=
+begin f:=font(p); i:=char_info(f)(character(p)); hd:=height_depth(i);
+x:=x+char_width(f)(i);@/
+s:=char_height(f)(hd);@+if s>h then h:=s;
+s:=char_depth(f)(hd);@+if s>d then d:=s;
+p:=link(p);
+end
+
+@ Although node |q| is not necessarily the immediate predecessor of node |p|,
+it always points to some node in the list preceding |p|. Thus, we can delete
+nodes by moving |q| when necessary. The algorithm takes linear time, and the
+extra computation does not intrude on the inner loop unless it is necessary
+to make a deletion.
+@^inner loop@>
+
+@<Transfer node |p| to the adjustment list@>=
+begin while link(q)<>p do q:=link(q);
+if type(p)=adjust_node then
+ begin link(adjust_tail):=adjust_ptr(p);
+ while link(adjust_tail)<>null do adjust_tail:=link(adjust_tail);
+ p:=link(p); free_node(link(q),small_node_size);
+ end
+else begin link(adjust_tail):=p; adjust_tail:=p; p:=link(p);
+ end;
+link(q):=p; p:=q;
+end
+
+@ @<Incorporate glue into the horizontal totals@>=
+begin g:=glue_ptr(p); x:=x+width(g);@/
+o:=stretch_order(g); total_stretch[o]:=total_stretch[o]+stretch(g);
+o:=shrink_order(g); total_shrink[o]:=total_shrink[o]+shrink(g);
+if subtype(p)>=a_leaders then
+ begin g:=leader_ptr(p);
+ if height(g)>h then h:=height(g);
+ if depth(g)>d then d:=depth(g);
+ end;
+end
+
+@ When we get to the present part of the program, |x| is the natural width
+of the box being packaged.
+
+@<Determine the value of |width(r)| and the appropriate glue setting...@>=
+if m=additional then w:=x+w;
+width(r):=w; x:=w-x; {now |x| is the excess to be made up}
+if x=0 then
+ begin glue_sign(r):=normal; glue_order(r):=normal;
+ set_glue_ratio_zero(glue_set(r));
+ return;
+ end
+else if x>0 then @<Determine horizontal glue stretch setting, then |return|
+ or \hbox{|goto common_ending|}@>
+else @<Determine horizontal glue shrink setting, then |return|
+ or \hbox{|goto common_ending|}@>
+
+@ @<Determine horizontal glue stretch setting...@>=
+begin @<Determine the stretch order@>;
+glue_order(r):=o; glue_sign(r):=stretching;
+if total_stretch[o]<>0 then glue_set(r):=unfloat(x/total_stretch[o])
+@^real division@>
+else begin glue_sign(r):=normal;
+ set_glue_ratio_zero(glue_set(r)); {there's nothing to stretch}
+ end;
+if o=normal then if list_ptr(r)<>null then
+ @<Report an underfull hbox and |goto common_ending|, if this box
+ is sufficiently bad@>;
+return;
+end
+
+@ @<Determine the stretch order@>=
+if total_stretch[filll]<>0 then o:=filll
+else if total_stretch[fill]<>0 then o:=fill
+else if total_stretch[fil]<>0 then o:=fil
+else o:=normal
+
+@ @<Report an underfull hbox and |goto common_ending|, if...@>=
+begin last_badness:=badness(x,total_stretch[normal]);
+if last_badness>hbadness then
+ begin print_ln;
+ if last_badness>100 then print_nl("Underfull")@+else print_nl("Loose");
+ print(" \hbox (badness "); print_int(last_badness);
+@.Underfull \\hbox...@>
+@.Loose \\hbox...@>
+ goto common_ending;
+ end;
+end
+
+@ In order to provide a decent indication of where an overfull or underfull
+box originated, we use a global variable |pack_begin_line| that is
+set nonzero only when |hpack| is being called by the paragraph builder
+or the alignment finishing routine.
+
+@<Glob...@>=
+@!pack_begin_line:integer; {source file line where the current paragraph
+ or alignment began; a negative value denotes alignment}
+
+@ @<Set init...@>=
+pack_begin_line:=0;
+
+@ @<Finish issuing a diagnostic message for an overfull or underfull hbox@>=
+if output_active then print(") has occurred while \output is active")
+else begin if pack_begin_line<>0 then
+ begin if pack_begin_line>0 then print(") in paragraph at lines ")
+ else print(") in alignment at lines ");
+ print_int(abs(pack_begin_line));
+ print("--");
+ end
+ else print(") detected at line ");
+ print_int(line);
+ end;
+print_ln;@/
+font_in_short_display:=null_font; short_display(list_ptr(r)); print_ln;@/
+begin_diagnostic; show_box(r); end_diagnostic(true)
+
+@ @<Determine horizontal glue shrink setting...@>=
+begin @<Determine the shrink order@>;
+glue_order(r):=o; glue_sign(r):=shrinking;
+if total_shrink[o]<>0 then glue_set(r):=unfloat((-x)/total_shrink[o])
+@^real division@>
+else begin glue_sign(r):=normal;
+ set_glue_ratio_zero(glue_set(r)); {there's nothing to shrink}
+ end;
+if (total_shrink[o]<-x)and(o=normal)and(list_ptr(r)<>null) then
+ begin last_badness:=1000000;
+ set_glue_ratio_one(glue_set(r)); {use the maximum shrinkage}
+ @<Report an overfull hbox and |goto common_ending|, if this box
+ is sufficiently bad@>;
+ end
+else if o=normal then if list_ptr(r)<>null then
+ @<Report a tight hbox and |goto common_ending|, if this box
+ is sufficiently bad@>;
+return;
+end
+
+@ @<Determine the shrink order@>=
+if total_shrink[filll]<>0 then o:=filll
+else if total_shrink[fill]<>0 then o:=fill
+else if total_shrink[fil]<>0 then o:=fil
+else o:=normal
+
+@ @<Report an overfull hbox and |goto common_ending|, if...@>=
+if (-x-total_shrink[normal]>hfuzz)or(hbadness<100) then
+ begin if (overfull_rule>0)and(-x-total_shrink[normal]>hfuzz) then
+ begin while link(q)<>null do q:=link(q);
+ link(q):=new_rule;
+ width(link(q)):=overfull_rule;
+ end;
+ print_ln; print_nl("Overfull \hbox (");
+@.Overfull \\hbox...@>
+ print_scaled(-x-total_shrink[normal]); print("pt too wide");
+ goto common_ending;
+ end
+
+@ @<Report a tight hbox and |goto common_ending|, if...@>=
+begin last_badness:=badness(-x,total_shrink[normal]);
+if last_badness>hbadness then
+ begin print_ln; print_nl("Tight \hbox (badness "); print_int(last_badness);
+@.Tight \\hbox...@>
+ goto common_ending;
+ end;
+end
+
+@ The |vpack| subroutine is actually a special case of a slightly more
+general routine called |vpackage|, which has four parameters. The fourth
+parameter, which is |max_dimen| in the case of |vpack|, specifies the
+maximum depth of the page box that is constructed. The depth is first
+computed by the normal rules; if it exceeds this limit, the reference
+point is simply moved down until the limiting depth is attained.
+
+@d vpack(#)==vpackage(#,max_dimen) {special case of unconstrained depth}
+
+@p function vpackage(@!p:pointer;@!h:scaled;@!m:small_number;@!l:scaled):
+ pointer;
+label common_ending, exit;
+var r:pointer; {the box node that will be returned}
+@!w,@!d,@!x:scaled; {width, depth, and natural height}
+@!s:scaled; {shift amount}
+@!g:pointer; {points to a glue specification}
+@!o:glue_ord; {order of infinity}
+begin last_badness:=0; r:=get_node(box_node_size); type(r):=vlist_node;
+subtype(r):=min_quarterword; shift_amount(r):=0;
+list_ptr(r):=p;@/
+w:=0; @<Clear dimensions to zero@>;
+while p<>null do @<Examine node |p| in the vlist, taking account of its effect
+ on the dimensions of the new box; then advance |p| to the next node@>;
+width(r):=w;
+if d>l then
+ begin x:=x+d-l; depth(r):=l;
+ end
+else depth(r):=d;
+@<Determine the value of |height(r)| and the appropriate glue setting;
+ then |return| or |goto common_ending|@>;
+common_ending: @<Finish issuing a diagnostic message
+ for an overfull or underfull vbox@>;
+exit: vpackage:=r;
+end;
+
+@ @<Examine node |p| in the vlist, taking account of its effect...@>=
+begin if is_char_node(p) then confusion("vpack")
+@:this can't happen vpack}{\quad vpack@>
+else case type(p) of
+ hlist_node,vlist_node,rule_node,unset_node:
+ @<Incorporate box dimensions into the dimensions of
+ the vbox that will contain~it@>;
+ whatsit_node:@<Incorporate a whatsit node into a vbox@>;
+ glue_node: @<Incorporate glue into the vertical totals@>;
+ kern_node: begin x:=x+d+width(p); d:=0;
+ end;
+ othercases do_nothing
+ endcases;
+p:=link(p);
+end
+
+@ @<Incorporate box dimensions into the dimensions of the vbox...@>=
+begin x:=x+d+height(p); d:=depth(p);
+if type(p)>=rule_node then s:=0 @+else s:=shift_amount(p);
+if width(p)+s>w then w:=width(p)+s;
+end
+
+@ @<Incorporate glue into the vertical totals@>=
+begin x:=x+d; d:=0;@/
+g:=glue_ptr(p); x:=x+width(g);@/
+o:=stretch_order(g); total_stretch[o]:=total_stretch[o]+stretch(g);
+o:=shrink_order(g); total_shrink[o]:=total_shrink[o]+shrink(g);
+if subtype(p)>=a_leaders then
+ begin g:=leader_ptr(p);
+ if width(g)>w then w:=width(g);
+ end;
+end
+
+@ When we get to the present part of the program, |x| is the natural height
+of the box being packaged.
+
+@<Determine the value of |height(r)| and the appropriate glue setting...@>=
+if m=additional then h:=x+h;
+height(r):=h; x:=h-x; {now |x| is the excess to be made up}
+if x=0 then
+ begin glue_sign(r):=normal; glue_order(r):=normal;
+ set_glue_ratio_zero(glue_set(r));
+ return;
+ end
+else if x>0 then @<Determine vertical glue stretch setting, then |return|
+ or \hbox{|goto common_ending|}@>
+else @<Determine vertical glue shrink setting, then |return|
+ or \hbox{|goto common_ending|}@>
+
+@ @<Determine vertical glue stretch setting...@>=
+begin @<Determine the stretch order@>;
+glue_order(r):=o; glue_sign(r):=stretching;
+if total_stretch[o]<>0 then glue_set(r):=unfloat(x/total_stretch[o])
+@^real division@>
+else begin glue_sign(r):=normal;
+ set_glue_ratio_zero(glue_set(r)); {there's nothing to stretch}
+ end;
+if o=normal then if list_ptr(r)<>null then
+ @<Report an underfull vbox and |goto common_ending|, if this box
+ is sufficiently bad@>;
+return;
+end
+
+@ @<Report an underfull vbox and |goto common_ending|, if...@>=
+begin last_badness:=badness(x,total_stretch[normal]);
+if last_badness>vbadness then
+ begin print_ln;
+ if last_badness>100 then print_nl("Underfull")@+else print_nl("Loose");
+ print(" \vbox (badness "); print_int(last_badness);
+@.Underfull \\vbox...@>
+@.Loose \\vbox...@>
+ goto common_ending;
+ end;
+end
+
+@ @<Finish issuing a diagnostic message for an overfull or underfull vbox@>=
+if output_active then print(") has occurred while \output is active")
+else begin if pack_begin_line<>0 then {it's actually negative}
+ begin print(") in alignment at lines ");
+ print_int(abs(pack_begin_line));
+ print("--");
+ end
+ else print(") detected at line ");
+ print_int(line);
+ print_ln;@/
+ end;
+begin_diagnostic; show_box(r); end_diagnostic(true)
+
+@ @<Determine vertical glue shrink setting...@>=
+begin @<Determine the shrink order@>;
+glue_order(r):=o; glue_sign(r):=shrinking;
+if total_shrink[o]<>0 then glue_set(r):=unfloat((-x)/total_shrink[o])
+@^real division@>
+else begin glue_sign(r):=normal;
+ set_glue_ratio_zero(glue_set(r)); {there's nothing to shrink}
+ end;
+if (total_shrink[o]<-x)and(o=normal)and(list_ptr(r)<>null) then
+ begin last_badness:=1000000;
+ set_glue_ratio_one(glue_set(r)); {use the maximum shrinkage}
+ @<Report an overfull vbox and |goto common_ending|, if this box
+ is sufficiently bad@>;
+ end
+else if o=normal then if list_ptr(r)<>null then
+ @<Report a tight vbox and |goto common_ending|, if this box
+ is sufficiently bad@>;
+return;
+end
+
+@ @<Report an overfull vbox and |goto common_ending|, if...@>=
+if (-x-total_shrink[normal]>vfuzz)or(vbadness<100) then
+ begin print_ln; print_nl("Overfull \vbox (");
+@.Overfull \\vbox...@>
+ print_scaled(-x-total_shrink[normal]); print("pt too high");
+ goto common_ending;
+ end
+
+@ @<Report a tight vbox and |goto common_ending|, if...@>=
+begin last_badness:=badness(-x,total_shrink[normal]);
+if last_badness>vbadness then
+ begin print_ln; print_nl("Tight \vbox (badness "); print_int(last_badness);
+@.Tight \\vbox...@>
+ goto common_ending;
+ end;
+end
+
+@ When a box is being appended to the current vertical list, the
+baselineskip calculation is handled by the |append_to_vlist| routine.
+
+@p procedure append_to_vlist(@!b:pointer);
+var d:scaled; {deficiency of space between baselines}
+@!p:pointer; {a new glue node}
+begin if prev_depth>ignore_depth then
+ begin d:=width(baseline_skip)-prev_depth-height(b);
+ if d<line_skip_limit then p:=new_param_glue(line_skip_code)
+ else begin p:=new_skip_param(baseline_skip_code);
+ width(temp_ptr):=d; {|temp_ptr=glue_ptr(p)|}
+ end;
+ link(tail):=p; tail:=p;
+ end;
+link(tail):=b; tail:=b; prev_depth:=depth(b);
+end;
+
+@* \[34] Data structures for math mode.
+When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an
+{\sl mlist}, which is essentially a tree structure representing that
+formula. An mlist is a linear sequence of items, but we can regard it as
+a tree structure because mlists can appear within mlists. For example, many
+of the entries can be subscripted or superscripted, and such ``scripts''
+are mlists in their own right.
+
+An entire formula is parsed into such a tree before any of the actual
+typesetting is done, because the current style of type is usually not
+known until the formula has been fully scanned. For example, when the
+formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell
+that `\.{a+b}' will be in script size until `\.{\\over}' has appeared.
+
+During the scanning process, each element of the mlist being built is
+classified as a relation, a binary operator, an open parenthesis, etc.,
+or as a construct like `\.{\\sqrt}' that must be built up. This classification
+appears in the mlist data structure.
+
+After a formula has been fully scanned, the mlist is converted to an hlist
+so that it can be incorporated into the surrounding text. This conversion is
+controlled by a recursive procedure that decides all of the appropriate
+styles by a ``top-down'' process starting at the outermost level and working
+in towards the subformulas. The formula is ultimately pasted together using
+combinations of horizontal and vertical boxes, with glue and penalty nodes
+inserted as necessary.
+
+An mlist is represented internally as a linked list consisting chiefly
+of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat
+similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are
+allowed to appear in mlists together with the noads; \TeX\ tells the difference
+by means of the |type| field, since a noad's |type| is always greater than
+that of a node. An mlist does not contain character nodes, hlist nodes, vlist
+nodes, math nodes, ligature nodes,
+or unset nodes; in particular, each mlist item appears in the
+variable-size part of |mem|, so the |type| field is always present.
+
+@ Each noad is four or more words long. The first word contains the |type|
+and |subtype| and |link| fields that are already so familiar to us; the
+second, third, and fourth words are called the noad's |nucleus|, |subscr|,
+and |supscr| fields.
+
+Consider, for example, the simple formula `\.{\$x\^2\$}', which would be
+parsed into an mlist containing a single element called an |ord_noad|.
+The |nucleus| of this noad is a representation of `\.x', the |subscr| is
+empty, and the |supscr| is a representation of `\.2'.
+
+The |nucleus|, |subscr|, and |supscr| fields are further broken into
+subfields. If |p| points to a noad, and if |q| is one of its principal
+fields (e.g., |q=subscr(p)|), there are several possibilities for the
+subfields, depending on the |math_type| of |q|.
+
+\yskip\hang|math_type(q)=math_char| means that |fam(q)| refers to one of
+the sixteen font families, and |character(q)| is the number of a character
+within a font of that family, as in a character node.
+
+\yskip\hang|math_type(q)=math_text_char| is similar, but the character is
+unsubscripted and unsuperscripted and it is followed immediately by another
+character from the same font. (This |math_type| setting appears only
+briefly during the processing; it is used to suppress unwanted italic
+corrections.)
+
+\yskip\hang|math_type(q)=empty| indicates a field with no value (the
+corresponding attribute of noad |p| is not present).
+
+\yskip\hang|math_type(q)=sub_box| means that |info(q)| points to a box
+node (either an |hlist_node| or a |vlist_node|) that should be used as the
+value of the field. The |shift_amount| in the subsidiary box node is the
+amount by which that box will be shifted downward.
+
+\yskip\hang|math_type(q)=sub_mlist| means that |info(q)| points to
+an mlist; the mlist must be converted to an hlist in order to obtain
+the value of this field.
+
+\yskip\noindent In the latter case, we might have |info(q)=null|. This
+is not the same as |math_type(q)=empty|; for example, `\.{\$P\_\{\}\$}'
+and `\.{\$P\$}' produce different results (the former will not have the
+``italic correction'' added to the width of |P|, but the ``script skip''
+will be added).
+
+The definitions of subfields given here are evidently wasteful of space,
+since a halfword is being used for the |math_type| although only three
+bits would be needed. However, there are hardly ever many noads present at
+once, since they are soon converted to nodes that take up even more space,
+so we can afford to represent them in whatever way simplifies the
+programming.
+
+@d noad_size=4 {number of words in a normal noad}
+@d nucleus(#)==#+1 {the |nucleus| field of a noad}
+@d supscr(#)==#+2 {the |supscr| field of a noad}
+@d subscr(#)==#+3 {the |subscr| field of a noad}
+@d math_type==link {a |halfword| in |mem|}
+@d fam==font {a |quarterword| in |mem|}
+@d math_char=1 {|math_type| when the attribute is simple}
+@d sub_box=2 {|math_type| when the attribute is a box}
+@d sub_mlist=3 {|math_type| when the attribute is a formula}
+@d math_text_char=4 {|math_type| when italic correction is dubious}
+
+@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope,
+Clo, Pun, or Inn, for purposes of spacing and line breaking. An
+|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|,
+|punct_noad|, or |inner_noad| is used to represent portions of the various
+types. For example, an `\.=' sign in a formula leads to the creation of a
+|rel_noad| whose |nucleus| field is a representation of an equals sign
+(usually |fam=0|, |character=@'75|). A formula preceded by \.{\\mathrel}
+also results in a |rel_noad|. When a |rel_noad| is followed by an
+|op_noad|, say, and possibly separated by one or more ordinary nodes (not
+noads), \TeX\ will insert a penalty node (with the current |rel_penalty|)
+just after the formula that corresponds to the |rel_noad|, unless there
+already was a penalty immediately following; and a ``thick space'' will be
+inserted just before the formula that corresponds to the |op_noad|.
+
+A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually
+has a |subtype=normal|. The only exception is that an |op_noad| might
+have |subtype=limits| or |no_limits|, if the normal positioning of
+limits has been overridden for this operator.
+
+@d ord_noad=unset_node+3 {|type| of a noad classified Ord}
+@d op_noad=ord_noad+1 {|type| of a noad classified Op}
+@d bin_noad=ord_noad+2 {|type| of a noad classified Bin}
+@d rel_noad=ord_noad+3 {|type| of a noad classified Rel}
+@d open_noad=ord_noad+4 {|type| of a noad classified Ope}
+@d close_noad=ord_noad+5 {|type| of a noad classified Clo}
+@d punct_noad=ord_noad+6 {|type| of a noad classified Pun}
+@d inner_noad=ord_noad+7 {|type| of a noad classified Inn}
+@d limits=1 {|subtype| of |op_noad| whose scripts are to be above, below}
+@d no_limits=2 {|subtype| of |op_noad| whose scripts are to be normal}
+
+@ A |radical_noad| is five words long; the fifth word is the |left_delimiter|
+field, which usually represents a square root sign.
+
+A |fraction_noad| is six words long; it has a |right_delimiter| field
+as well as a |left_delimiter|.
+
+Delimiter fields are of type |four_quarters|, and they have four subfields
+called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields
+represent variable-size delimiters by giving the ``small'' and ``large''
+starting characters, as explained in Chapter~17 of {\sl The \TeX book}.
+@:TeXbook}{\sl The \TeX book@>
+
+A |fraction_noad| is actually quite different from all other noads. Not
+only does it have six words, it has |thickness|, |denominator|, and
+|numerator| fields instead of |nucleus|, |subscr|, and |supscr|. The
+|thickness| is a scaled value that tells how thick to make a fraction
+rule; however, the special value |default_code| is used to stand for the
+|default_rule_thickness| of the current size. The |numerator| and
+|denominator| point to mlists that define a fraction; we always have
+$$\hbox{|math_type(numerator)=math_type(denominator)=sub_mlist|}.$$ The
+|left_delimiter| and |right_delimiter| fields specify delimiters that will
+be placed at the left and right of the fraction. In this way, a
+|fraction_noad| is able to represent all of \TeX's operators \.{\\over},
+\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and
+ \.{\\abovewithdelims}.
+
+@d left_delimiter(#)==#+4 {first delimiter field of a noad}
+@d right_delimiter(#)==#+5 {second delimiter field of a fraction noad}
+@d radical_noad=inner_noad+1 {|type| of a noad for square roots}
+@d radical_noad_size=5 {number of |mem| words in a radical noad}
+@d fraction_noad=radical_noad+1 {|type| of a noad for generalized fractions}
+@d fraction_noad_size=6 {number of |mem| words in a fraction noad}
+@d small_fam(#)==mem[#].qqqq.b0 {|fam| for ``small'' delimiter}
+@d small_char(#)==mem[#].qqqq.b1 {|character| for ``small'' delimiter}
+@d large_fam(#)==mem[#].qqqq.b2 {|fam| for ``large'' delimiter}
+@d large_char(#)==mem[#].qqqq.b3 {|character| for ``large'' delimiter}
+@d thickness==width {|thickness| field in a fraction noad}
+@d default_code==@'10000000000 {denotes |default_rule_thickness|}
+@d numerator==supscr {|numerator| field in a fraction noad}
+@d denominator==subscr {|denominator| field in a fraction noad}
+
+@ The global variable |empty_field| is set up for initialization of empty
+fields in new noads. Similarly, |null_delimiter| is for the initialization
+of delimiter fields.
+
+@<Glob...@>=
+@!empty_field:two_halves;
+@!null_delimiter:four_quarters;
+
+@ @<Set init...@>=
+empty_field.rh:=empty; empty_field.lh:=null;@/
+null_delimiter.b0:=0; null_delimiter.b1:=min_quarterword;@/
+null_delimiter.b2:=0; null_delimiter.b3:=min_quarterword;
+
+@ The |new_noad| function creates an |ord_noad| that is completely null.
+
+@p function new_noad:pointer;
+var p:pointer;
+begin p:=get_node(noad_size);
+type(p):=ord_noad; subtype(p):=normal;
+mem[nucleus(p)].hh:=empty_field;
+mem[subscr(p)].hh:=empty_field;
+mem[supscr(p)].hh:=empty_field;
+new_noad:=p;
+end;
+
+@ A few more kinds of noads will complete the set: An |under_noad| has its
+nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places
+an accent over its nucleus; the accent character appears as
+|fam(accent_chr(p))| and |character(accent_chr(p))|. A |vcenter_noad|
+centers its nucleus vertically with respect to the axis of the formula;
+in such noads we always have |math_type(nucleus(p))=sub_box|.
+
+And finally, we have |left_noad| and |right_noad| types, to implement
+\TeX's \.{\\left} and \.{\\right}. The |nucleus| of such noads is
+replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces
+a |left_noad| such that |delimiter(p)| holds the family and character
+codes for all left parentheses. A |left_noad| never appears in an mlist
+except as the first element, and a |right_noad| never appears in an mlist
+except as the last element; furthermore, we either have both a |left_noad|
+and a |right_noad|, or neither one is present. The |subscr| and |supscr|
+fields are always |empty| in a |left_noad| and a |right_noad|.
+
+@d under_noad=fraction_noad+1 {|type| of a noad for underlining}
+@d over_noad=under_noad+1 {|type| of a noad for overlining}
+@d accent_noad=over_noad+1 {|type| of a noad for accented subformulas}
+@d accent_noad_size=5 {number of |mem| words in an accent noad}
+@d accent_chr(#)==#+4 {the |accent_chr| field of an accent noad}
+@d vcenter_noad=accent_noad+1 {|type| of a noad for \.{\\vcenter}}
+@d left_noad=vcenter_noad+1 {|type| of a noad for \.{\\left}}
+@d right_noad=left_noad+1 {|type| of a noad for \.{\\right}}
+@d delimiter==nucleus {|delimiter| field in left and right noads}
+@d scripts_allowed(#)==(type(#)>=ord_noad)and(type(#)<left_noad)
+
+@ Math formulas can also contain instructions like \.{\\textstyle} that
+override \TeX's normal style rules. A |style_node| is inserted into the
+data structure to record such instructions; it is three words long, so it
+is considered a node instead of a noad. The |subtype| is either |display_style|
+or |text_style| or |script_style| or |script_script_style|. The
+second and third words of a |style_node| are not used, but they are
+present because a |choice_node| is converted to a |style_node|.
+
+\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles
+|display_style|, \dots, |script_script_style|, and adds~1 to get the
+``cramped'' versions of these styles. This gives a numerical order that
+is backwards from the convention of Appendix~G in {\sl The \TeX book\/};
+i.e., a smaller style has a larger numerical value.
+@:TeXbook}{\sl The \TeX book@>
+
+@d style_node=unset_node+1 {|type| of a style node}
+@d style_node_size=3 {number of words in a style node}
+@d display_style=0 {|subtype| for \.{\\displaystyle}}
+@d text_style=2 {|subtype| for \.{\\textstyle}}
+@d script_style=4 {|subtype| for \.{\\scriptstyle}}
+@d script_script_style=6 {|subtype| for \.{\\scriptscriptstyle}}
+@d cramped=1 {add this to an uncramped style if you want to cramp it}
+
+@p function new_style(@!s:small_number):pointer; {create a style node}
+var p:pointer; {the new node}
+begin p:=get_node(style_node_size); type(p):=style_node;
+subtype(p):=s; width(p):=0; depth(p):=0; {the |width| and |depth| are not used}
+new_style:=p;
+end;
+
+@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which
+has special subfields |display_mlist|, |text_mlist|, |script_mlist|,
+and |script_script_mlist| pointing to the mlists for each style.
+
+@d choice_node=unset_node+2 {|type| of a choice node}
+@d display_mlist(#)==info(#+1) {mlist to be used in display style}
+@d text_mlist(#)==link(#+1) {mlist to be used in text style}
+@d script_mlist(#)==info(#+2) {mlist to be used in script style}
+@d script_script_mlist(#)==link(#+2) {mlist to be used in scriptscript style}
+
+@p function new_choice:pointer; {create a choice node}
+var p:pointer; {the new node}
+begin p:=get_node(style_node_size); type(p):=choice_node;
+subtype(p):=0; {the |subtype| is not used}
+display_mlist(p):=null; text_mlist(p):=null; script_mlist(p):=null;
+script_script_mlist(p):=null;
+new_choice:=p;
+end;
+
+@ Let's consider now the previously unwritten part of |show_node_list|
+that displays the things that can only be present in mlists; this
+program illustrates how to access the data structures just defined.
+
+In the context of the following program, |p| points to a node or noad that
+should be displayed, and the current string contains the ``recursion history''
+that leads to this point. The recursion history consists of a dot for each
+outer level in which |p| is subsidiary to some node, or in which |p| is
+subsidiary to the |nucleus| field of some noad; the dot is replaced by
+`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr|
+or |supscr| or |denominator| or |numerator| fields of noads. For example,
+the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for
+|x| in the (ridiculous) formula
+`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'.
+
+@<Cases of |show_node_list| that arise...@>=
+style_node:print_style(subtype(p));
+choice_node:@<Display choice node |p|@>;
+ord_noad,op_noad,bin_noad,rel_noad,open_noad,close_noad,punct_noad,inner_noad,
+ radical_noad,over_noad,under_noad,vcenter_noad,accent_noad,
+ left_noad,right_noad:@<Display normal noad |p|@>;
+fraction_noad:@<Display fraction noad |p|@>;
+
+@ Here are some simple routines used in the display of noads.
+
+@<Declare procedures needed for displaying the elements of mlists@>=
+procedure print_fam_and_char(@!p:pointer); {prints family and character}
+begin print_esc("fam"); print_int(fam(p)); print_char(" ");
+print_ASCII(qo(character(p)));
+end;
+@#
+procedure print_delimiter(@!p:pointer); {prints a delimiter as 24-bit hex value}
+var a:integer; {accumulator}
+begin a:=small_fam(p)*256+qo(small_char(p));
+a:=a*@"1000+large_fam(p)*256+qo(large_char(p));
+if a<0 then print_int(a) {this should never happen}
+else print_hex(a);
+end;
+
+@ The next subroutine will descend to another level of recursion when a
+subsidiary mlist needs to be displayed. The parameter |c| indicates what
+character is to become part of the recursion history. An empty mlist is
+distinguished from a field with |math_type(p)=empty|, because these are
+not equivalent (as explained above).
+@^recursion@>
+
+@<Declare procedures needed for displaying...@>=
+procedure@?show_info; forward;@t\2@>@?{|show_node_list(info(temp_ptr))|}
+procedure print_subsidiary_data(@!p:pointer;@!c:ASCII_code);
+ {display a noad field}
+begin if cur_length>=depth_threshold then
+ begin if math_type(p)<>empty then print(" []");
+ end
+else begin append_char(c); {include |c| in the recursion history}
+ temp_ptr:=p; {prepare for |show_info| if recursion is needed}
+ case math_type(p) of
+ math_char: begin print_ln; print_current_string; print_fam_and_char(p);
+ end;
+ sub_box: show_info; {recursive call}
+ sub_mlist: if info(p)=null then
+ begin print_ln; print_current_string; print("{}");
+ end
+ else show_info; {recursive call}
+ othercases do_nothing {|empty|}
+ endcases;@/
+ flush_char; {remove |c| from the recursion history}
+ end;
+end;
+
+@ The inelegant introduction of |show_info| in the code above seems better
+than the alternative of using \PASCAL's strange |forward| declaration for a
+procedure with parameters. The \PASCAL\ convention about dropping parameters
+from a post-|forward| procedure is, frankly, so intolerable to the author
+of \TeX\ that he would rather stoop to communication via a global temporary
+variable. (A similar stoopidity occurred with respect to |hlist_out| and
+|vlist_out| above, and it will occur with respect to |mlist_to_hlist| below.)
+@^Knuth, Donald Ervin@>
+@:PASCAL}{\PASCAL@>
+
+@p procedure show_info; {the reader will kindly forgive this}
+begin show_node_list(info(temp_ptr));
+end;
+
+@ @<Declare procedures needed for displaying...@>=
+procedure print_style(@!c:integer);
+begin case c div 2 of
+0: print_esc("displaystyle"); {|display_style=0|}
+1: print_esc("textstyle"); {|text_style=2|}
+2: print_esc("scriptstyle"); {|script_style=4|}
+3: print_esc("scriptscriptstyle"); {|script_script_style=6|}
+othercases print("Unknown style!")
+endcases;
+end;
+
+@ @<Display choice node |p|@>=
+begin print_esc("mathchoice");
+append_char("D"); show_node_list(display_mlist(p)); flush_char;
+append_char("T"); show_node_list(text_mlist(p)); flush_char;
+append_char("S"); show_node_list(script_mlist(p)); flush_char;
+append_char("s"); show_node_list(script_script_mlist(p)); flush_char;
+end
+
+@ @<Display normal noad |p|@>=
+begin case type(p) of
+ord_noad: print_esc("mathord");
+op_noad: print_esc("mathop");
+bin_noad: print_esc("mathbin");
+rel_noad: print_esc("mathrel");
+open_noad: print_esc("mathopen");
+close_noad: print_esc("mathclose");
+punct_noad: print_esc("mathpunct");
+inner_noad: print_esc("mathinner");
+over_noad: print_esc("overline");
+under_noad: print_esc("underline");
+vcenter_noad: print_esc("vcenter");
+radical_noad: begin print_esc("radical"); print_delimiter(left_delimiter(p));
+ end;
+accent_noad: begin print_esc("accent"); print_fam_and_char(accent_chr(p));
+ end;
+left_noad: begin print_esc("left"); print_delimiter(delimiter(p));
+ end;
+right_noad: begin print_esc("right"); print_delimiter(delimiter(p));
+ end;
+end;
+if subtype(p)<>normal then
+ if subtype(p)=limits then print_esc("limits")
+ else print_esc("nolimits");
+if type(p)<left_noad then print_subsidiary_data(nucleus(p),".");
+print_subsidiary_data(supscr(p),"^");
+print_subsidiary_data(subscr(p),"_");
+end
+
+@ @<Display fraction noad |p|@>=
+begin print_esc("fraction, thickness ");
+if thickness(p)=default_code then print("= default")
+else print_scaled(thickness(p));
+if (small_fam(left_delimiter(p))<>0)or@+
+ (small_char(left_delimiter(p))<>min_quarterword)or@|
+ (large_fam(left_delimiter(p))<>0)or@|
+ (large_char(left_delimiter(p))<>min_quarterword) then
+ begin print(", left-delimiter "); print_delimiter(left_delimiter(p));
+ end;
+if (small_fam(right_delimiter(p))<>0)or@|
+ (small_char(right_delimiter(p))<>min_quarterword)or@|
+ (large_fam(right_delimiter(p))<>0)or@|
+ (large_char(right_delimiter(p))<>min_quarterword) then
+ begin print(", right-delimiter "); print_delimiter(right_delimiter(p));
+ end;
+print_subsidiary_data(numerator(p),"\");
+print_subsidiary_data(denominator(p),"/");
+end
+
+@ That which can be displayed can also be destroyed.
+
+@<Cases of |flush_node_list| that arise...@>=
+style_node: begin free_node(p,style_node_size); goto done;
+ end;
+choice_node:begin flush_node_list(display_mlist(p));
+ flush_node_list(text_mlist(p));
+ flush_node_list(script_mlist(p));
+ flush_node_list(script_script_mlist(p));
+ free_node(p,style_node_size); goto done;
+ end;
+ord_noad,op_noad,bin_noad,rel_noad,open_noad,close_noad,punct_noad,inner_noad,
+ radical_noad,over_noad,under_noad,vcenter_noad,accent_noad:@t@>@;@/
+ begin if math_type(nucleus(p))>=sub_box then
+ flush_node_list(info(nucleus(p)));
+ if math_type(supscr(p))>=sub_box then
+ flush_node_list(info(supscr(p)));
+ if math_type(subscr(p))>=sub_box then
+ flush_node_list(info(subscr(p)));
+ if type(p)=radical_noad then free_node(p,radical_noad_size)
+ else if type(p)=accent_noad then free_node(p,accent_noad_size)
+ else free_node(p,noad_size);
+ goto done;
+ end;
+left_noad,right_noad: begin free_node(p,noad_size); goto done;
+ end;
+fraction_noad: begin flush_node_list(info(numerator(p)));
+ flush_node_list(info(denominator(p)));
+ free_node(p,fraction_noad_size); goto done;
+ end;
+
+@* \[35] Subroutines for math mode.
+In order to convert mlists to hlists, i.e., noads to nodes, we need several
+subroutines that are conveniently dealt with now.
+
+Let us first introduce the macros that make it easy to get at the parameters and
+other font information. A size code, which is a multiple of 16, is added to a
+family number to get an index into the table of internal font numbers
+for each combination of family and size. (Be alert: Size codes get
+larger as the type gets smaller.)
+
+@d text_size=0 {size code for the largest size in a family}
+@d script_size=16 {size code for the medium size in a family}
+@d script_script_size=32 {size code for the smallest size in a family}
+
+@<Basic printing procedures@>=
+procedure print_size(@!s:integer);
+begin if s=text_size then print_esc("textfont")
+else if s=script_size then print_esc("scriptfont")
+else print_esc("scriptscriptfont");
+end;
+
+@ Before an mlist is converted to an hlist, \TeX\ makes sure that
+the fonts in family~2 have enough parameters to be math-symbol
+fonts, and that the fonts in family~3 have enough parameters to be
+math-extension fonts. The math-symbol parameters are referred to by using the
+following macros, which take a size code as their parameter; for example,
+|num1(cur_size)| gives the value of the |num1| parameter for the current size.
+@^parameters for symbols@>
+@^font parameters@>
+
+@d mathsy_end(#)==fam_fnt(2+#)]].sc
+@d mathsy(#)==font_info[#+param_base[mathsy_end
+@d math_x_height==mathsy(5) {height of `\.x'}
+@d math_quad==mathsy(6) {\.{18mu}}
+@d num1==mathsy(8) {numerator shift-up in display styles}
+@d num2==mathsy(9) {numerator shift-up in non-display, non-\.{\\atop}}
+@d num3==mathsy(10) {numerator shift-up in non-display \.{\\atop}}
+@d denom1==mathsy(11) {denominator shift-down in display styles}
+@d denom2==mathsy(12) {denominator shift-down in non-display styles}
+@d sup1==mathsy(13) {superscript shift-up in uncramped display style}
+@d sup2==mathsy(14) {superscript shift-up in uncramped non-display}
+@d sup3==mathsy(15) {superscript shift-up in cramped styles}
+@d sub1==mathsy(16) {subscript shift-down if superscript is absent}
+@d sub2==mathsy(17) {subscript shift-down if superscript is present}
+@d sup_drop==mathsy(18) {superscript baseline below top of large box}
+@d sub_drop==mathsy(19) {subscript baseline below bottom of large box}
+@d delim1==mathsy(20) {size of \.{\\atopwithdelims} delimiters
+ in display styles}
+@d delim2==mathsy(21) {size of \.{\\atopwithdelims} delimiters in non-displays}
+@d axis_height==mathsy(22) {height of fraction lines above the baseline}
+@d total_mathsy_params=22
+
+@ The math-extension parameters have similar macros, but the size code is
+omitted (since it is always |cur_size| when we refer to such parameters).
+@^parameters for symbols@>
+@^font parameters@>
+
+@d mathex(#)==font_info[#+param_base[fam_fnt(3+cur_size)]].sc
+@d default_rule_thickness==mathex(8) {thickness of \.{\\over} bars}
+@d big_op_spacing1==mathex(9) {minimum clearance above a displayed op}
+@d big_op_spacing2==mathex(10) {minimum clearance below a displayed op}
+@d big_op_spacing3==mathex(11) {minimum baselineskip above displayed op}
+@d big_op_spacing4==mathex(12) {minimum baselineskip below displayed op}
+@d big_op_spacing5==mathex(13) {padding above and below displayed limits}
+@d total_mathex_params=13
+
+@ We also need to compute the change in style between mlists and their
+subsidiaries. The following macros define the subsidiary style for
+an overlined nucleus (|cramped_style|), for a subscript or a superscript
+(|sub_style| or |sup_style|), or for a numerator or denominator (|num_style|
+or |denom_style|).
+
+@d cramped_style(#)==2*(# div 2)+cramped {cramp the style}
+@d sub_style(#)==2*(# div 4)+script_style+cramped {smaller and cramped}
+@d sup_style(#)==2*(# div 4)+script_style+(# mod 2) {smaller}
+@d num_style(#)==#+2-2*(# div 6) {smaller unless already script-script}
+@d denom_style(#)==2*(# div 2)+cramped+2-2*(# div 6) {smaller, cramped}
+
+@ When the style changes, the following piece of program computes associated
+information:
+
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>=
+begin if cur_style<script_style then cur_size:=text_size
+else cur_size:=16*((cur_style-text_style) div 2);
+cur_mu:=x_over_n(math_quad(cur_size),18);
+end
+
+@ Here is a function that returns a pointer to a rule node having a given
+thickness |t|. The rule will extend horizontally to the boundary of the vlist
+that eventually contains it.
+
+@p function fraction_rule(@!t:scaled):pointer;
+ {construct the bar for a fraction}
+var p:pointer; {the new node}
+begin p:=new_rule; height(p):=t; depth(p):=0; fraction_rule:=p;
+end;
+
+@ The |overbar| function returns a pointer to a vlist box that consists of
+a given box |b|, above which has been placed a kern of height |k| under a
+fraction rule of thickness |t| under additional space of height |t|.
+
+@p function overbar(@!b:pointer;@!k,@!t:scaled):pointer;
+var p,@!q:pointer; {nodes being constructed}
+begin p:=new_kern(k); link(p):=b; q:=fraction_rule(t); link(q):=p;
+p:=new_kern(t); link(p):=q; overbar:=vpack(p,natural);
+end;
+
+@ The |var_delimiter| function, which finds or constructs a sufficiently
+large delimiter, is the most interesting of the auxiliary functions that
+currently concern us. Given a pointer |d| to a delimiter field in some noad,
+together with a size code |s| and a vertical distance |v|, this function
+returns a pointer to a box that contains the smallest variant of |d| whose
+height plus depth is |v| or more. (And if no variant is large enough, it
+returns the largest available variant.) In particular, this routine will
+construct arbitrarily large delimiters from extensible components, if
+|d| leads to such characters.
+
+The value returned is a box whose |shift_amount| has been set so that
+the box is vertically centered with respect to the axis in the given size.
+If a built-up symbol is returned, the height of the box before shifting
+will be the height of its topmost component.
+
+@p@t\4@>@<Declare subprocedures for |var_delimiter|@>
+function var_delimiter(@!d:pointer;@!s:small_number;@!v:scaled):pointer;
+label found,continue;
+var b:pointer; {the box that will be constructed}
+@!f,@!g: internal_font_number; {best-so-far and tentative font codes}
+@!c,@!x,@!y: quarterword; {best-so-far and tentative character codes}
+@!m,@!n: integer; {the number of extensible pieces}
+@!u: scaled; {height-plus-depth of a tentative character}
+@!w: scaled; {largest height-plus-depth so far}
+@!q: four_quarters; {character info}
+@!hd: eight_bits; {height-depth byte}
+@!r: four_quarters; {extensible pieces}
+@!z: small_number; {runs through font family members}
+@!large_attempt: boolean; {are we trying the ``large'' variant?}
+begin f:=null_font; w:=0; large_attempt:=false;
+z:=small_fam(d); x:=small_char(d);
+loop@+ begin @<Look at the variants of |(z,x)|; set |f| and |c| whenever
+ a better character is found; |goto found| as soon as a
+ large enough variant is encountered@>;
+ if large_attempt then goto found; {there were none large enough}
+ large_attempt:=true; z:=large_fam(d); x:=large_char(d);
+ end;
+found: if f<>null_font then
+ @<Make variable |b| point to a box for |(f,c)|@>
+else begin b:=new_null_box;
+ width(b):=null_delimiter_space; {use this width if no delimiter was found}
+ end;
+shift_amount(b):=half(height(b)-depth(b)) - axis_height(s);
+var_delimiter:=b;
+end;
+
+@ The search process is complicated slightly by the facts that some of the
+characters might not be present in some of the fonts, and they might not
+be probed in increasing order of height.
+
+@<Look at the variants of |(z,x)|; set |f| and |c|...@>=
+if (z<>0)or(x<>min_quarterword) then
+ begin z:=z+s+16;
+ repeat z:=z-16; g:=fam_fnt(z);
+ if g<>null_font then
+ @<Look at the list of characters starting with |x| in
+ font |g|; set |f| and |c| whenever
+ a better character is found; |goto found| as soon as a
+ large enough variant is encountered@>;
+ until z<16;
+ end
+
+@ @<Look at the list of characters starting with |x|...@>=
+begin y:=x;
+if (qo(y)>=font_bc[g])and(qo(y)<=font_ec[g]) then
+ begin continue: q:=char_info(g)(y);
+ if char_exists(q) then
+ begin if char_tag(q)=ext_tag then
+ begin f:=g; c:=y; goto found;
+ end;
+ hd:=height_depth(q);
+ u:=char_height(g)(hd)+char_depth(g)(hd);
+ if u>w then
+ begin f:=g; c:=y; w:=u;
+ if u>=v then goto found;
+ end;
+ if char_tag(q)=list_tag then
+ begin y:=rem_byte(q); goto continue;
+ end;
+ end;
+ end;
+end
+
+@ Here is a subroutine that creates a new box, whose list contains a
+single character, and whose width includes the italic correction for
+that character. The height or depth of the box will be negative, if
+the height or depth of the character is negative; thus, this routine
+may deliver a slightly different result than |hpack| would produce.
+
+@<Declare subprocedures for |var_delimiter|@>=
+function char_box(@!f:internal_font_number;@!c:quarterword):pointer;
+var q:four_quarters;
+@!hd:eight_bits; {|height_depth| byte}
+@!b,@!p:pointer; {the new box and its character node}
+begin q:=char_info(f)(c); hd:=height_depth(q);
+b:=new_null_box; width(b):=char_width(f)(q)+char_italic(f)(q);
+height(b):=char_height(f)(hd); depth(b):=char_depth(f)(hd);
+p:=get_avail; character(p):=c; font(p):=f; list_ptr(b):=p; char_box:=b;
+end;
+
+@ When the following code is executed, |char_tag(q)| will be equal to
+|ext_tag| if and only if a built-up symbol is supposed to be returned.
+
+@<Make variable |b| point to a box for |(f,c)|@>=
+if char_tag(q)=ext_tag then
+ @<Construct an extensible character in a new box |b|,
+ using recipe |rem_byte(q)| and font |f|@>
+else b:=char_box(f,c)
+
+@ When we build an extensible character, it's handy to have the
+following subroutine, which puts a given character on top
+of the characters already in box |b|:
+
+@<Declare subprocedures for |var_delimiter|@>=
+procedure stack_into_box(@!b:pointer;@!f:internal_font_number;
+ @!c:quarterword);
+var p:pointer; {new node placed into |b|}
+begin p:=char_box(f,c); link(p):=list_ptr(b); list_ptr(b):=p;
+height(b):=height(p);
+end;
+
+@ Another handy subroutine computes the height plus depth of
+a given character:
+
+@<Declare subprocedures for |var_delimiter|@>=
+function height_plus_depth(@!f:internal_font_number;@!c:quarterword):scaled;
+var q:four_quarters;
+@!hd:eight_bits; {|height_depth| byte}
+begin q:=char_info(f)(c); hd:=height_depth(q);
+height_plus_depth:=char_height(f)(hd)+char_depth(f)(hd);
+end;
+
+@ @<Construct an extensible...@>=
+begin b:=new_null_box;
+type(b):=vlist_node;
+r:=font_info[exten_base[f]+rem_byte(q)].qqqq;@/
+@<Compute the minimum suitable height, |w|, and the corresponding
+ number of extension steps, |n|; also set |width(b)|@>;
+c:=ext_bot(r);
+if c<>min_quarterword then stack_into_box(b,f,c);
+c:=ext_rep(r);
+for m:=1 to n do stack_into_box(b,f,c);
+c:=ext_mid(r);
+if c<>min_quarterword then
+ begin stack_into_box(b,f,c); c:=ext_rep(r);
+ for m:=1 to n do stack_into_box(b,f,c);
+ end;
+c:=ext_top(r);
+if c<>min_quarterword then stack_into_box(b,f,c);
+depth(b):=w-height(b);
+end
+
+@ The width of an extensible character is the width of the repeatable
+module. If this module does not have positive height plus depth,
+we don't use any copies of it, otherwise we use as few as possible
+(in groups of two if there is a middle part).
+
+@<Compute the minimum suitable height, |w|, and...@>=
+c:=ext_rep(r); u:=height_plus_depth(f,c);
+w:=0; q:=char_info(f)(c); width(b):=char_width(f)(q)+char_italic(f)(q);@/
+c:=ext_bot(r);@+if c<>min_quarterword then w:=w+height_plus_depth(f,c);
+c:=ext_mid(r);@+if c<>min_quarterword then w:=w+height_plus_depth(f,c);
+c:=ext_top(r);@+if c<>min_quarterword then w:=w+height_plus_depth(f,c);
+n:=0;
+if u>0 then while w<v do
+ begin w:=w+u; incr(n);
+ if ext_mid(r)<>min_quarterword then w:=w+u;
+ end
+
+@ The next subroutine is much simpler; it is used for numerators and
+denominators of fractions as well as for displayed operators and
+their limits above and below. It takes a given box~|b| and
+changes it so that the new box is centered in a box of width~|w|.
+The centering is done by putting \.{\\hss} glue at the left and right
+of the list inside |b|, then packaging the new box; thus, the
+actual box might not really be centered, if it already contains
+infinite glue.
+
+The given box might contain a single character whose italic correction
+has been added to the width of the box; in this case a compensating
+kern is inserted.
+
+@p function rebox(@!b:pointer;@!w:scaled):pointer;
+var p:pointer; {temporary register for list manipulation}
+@!f:internal_font_number; {font in a one-character box}
+@!v:scaled; {width of a character without italic correction}
+begin if (width(b)<>w)and(list_ptr(b)<>null) then
+ begin if type(b)=vlist_node then b:=hpack(b,natural);
+ p:=list_ptr(b);
+ if (is_char_node(p))and(link(p)=null) then
+ begin f:=font(p); v:=char_width(f)(char_info(f)(character(p)));
+ if v<>width(b) then link(p):=new_kern(width(b)-v);
+ end;
+ free_node(b,box_node_size);
+ b:=new_glue(ss_glue); link(b):=p;
+ while link(p)<>null do p:=link(p);
+ link(p):=new_glue(ss_glue);
+ rebox:=hpack(b,w,exactly);
+ end
+else begin width(b):=w; rebox:=b;
+ end;
+end;
+
+@ Here is a subroutine that creates a new glue specification from another
+one that is expressed in `\.{mu}', given the value of the math unit.
+
+@d mu_mult(#)==nx_plus_y(n,#,xn_over_d(#,f,@'200000))
+
+@p function math_glue(@!g:pointer;@!m:scaled):pointer;
+var p:pointer; {the new glue specification}
+@!n:integer; {integer part of |m|}
+@!f:scaled; {fraction part of |m|}
+begin n:=x_over_n(m,@'200000); f:=remainder;@/
+if f<0 then
+ begin decr(n); f:=f+@'200000;
+ end;
+p:=get_node(glue_spec_size);
+width(p):=mu_mult(width(g)); {convert \.{mu} to \.{pt}}
+stretch_order(p):=stretch_order(g);
+if stretch_order(p)=normal then stretch(p):=mu_mult(stretch(g))
+else stretch(p):=stretch(g);
+shrink_order(p):=shrink_order(g);
+if shrink_order(p)=normal then shrink(p):=mu_mult(shrink(g))
+else shrink(p):=shrink(g);
+math_glue:=p;
+end;
+
+@ The |math_kern| subroutine removes |mu_glue| from a kern node, given
+the value of the math unit.
+
+@p procedure math_kern(@!p:pointer;@!m:scaled);
+var @!n:integer; {integer part of |m|}
+@!f:scaled; {fraction part of |m|}
+begin if subtype(p)=mu_glue then
+ begin n:=x_over_n(m,@'200000); f:=remainder;@/
+ if f<0 then
+ begin decr(n); f:=f+@'200000;
+ end;
+ width(p):=mu_mult(width(p)); subtype(p):=explicit;
+ end;
+end;
+
+@ Sometimes it is necessary to destroy an mlist. The following
+subroutine empties the current list, assuming that |abs(mode)=mmode|.
+
+@p procedure flush_math;
+begin flush_node_list(link(head)); flush_node_list(incompleat_noad);
+link(head):=null; tail:=head; incompleat_noad:=null;
+end;
+
+@* \[36] Typesetting math formulas.
+\TeX's most important routine for dealing with formulas is called
+|mlist_to_hlist|. After a formula has been scanned and represented as an
+mlist, this routine converts it to an hlist that can be placed into a box
+or incorporated into the text of a paragraph. There are three implicit
+parameters, passed in global variables: |cur_mlist| points to the first
+node or noad in the given mlist (and it might be |null|); |cur_style| is a
+style code; and |mlist_penalties| is |true| if penalty nodes for potential
+line breaks are to be inserted into the resulting hlist. After
+|mlist_to_hlist| has acted, |link(temp_head)| points to the translated hlist.
+
+Since mlists can be inside mlists, the procedure is recursive. And since this
+is not part of \TeX's inner loop, the program has been written in a manner
+that stresses compactness over efficiency.
+@^recursion@>
+
+@<Glob...@>=
+@!cur_mlist:pointer; {beginning of mlist to be translated}
+@!cur_style:small_number; {style code at current place in the list}
+@!cur_size:small_number; {size code corresponding to |cur_style|}
+@!cur_mu:scaled; {the math unit width corresponding to |cur_size|}
+@!mlist_penalties:boolean; {should |mlist_to_hlist| insert penalties?}
+
+@ The recursion in |mlist_to_hlist| is due primarily to a subroutine
+called |clean_box| that puts a given noad field into a box using a given
+math style; |mlist_to_hlist| can call |clean_box|, which can call
+|mlist_to_hlist|.
+@^recursion@>
+
+The box returned by |clean_box| is ``clean'' in the
+sense that its |shift_amount| is zero.
+
+@p procedure@?mlist_to_hlist; forward;@t\2@>@/
+function clean_box(@!p:pointer;@!s:small_number):pointer;
+label found;
+var q:pointer; {beginning of a list to be boxed}
+@!save_style:small_number; {|cur_style| to be restored}
+@!x:pointer; {box to be returned}
+@!r:pointer; {temporary pointer}
+begin case math_type(p) of
+math_char: begin cur_mlist:=new_noad; mem[nucleus(cur_mlist)]:=mem[p];
+ end;
+sub_box: begin q:=info(p); goto found;
+ end;
+sub_mlist: cur_mlist:=info(p);
+othercases begin q:=new_null_box; goto found;
+ end
+endcases;@/
+save_style:=cur_style; cur_style:=s; mlist_penalties:=false;@/
+mlist_to_hlist; q:=link(temp_head); {recursive call}
+cur_style:=save_style; {restore the style}
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+found: if is_char_node(q)or(q=null) then x:=hpack(q,natural)
+ else if (link(q)=null)and(type(q)<=vlist_node)and(shift_amount(q)=0) then
+ x:=q {it's already clean}
+ else x:=hpack(q,natural);
+@<Simplify a trivial box@>;
+clean_box:=x;
+end;
+
+@ Here we save memory space in a common case.
+
+@<Simplify a trivial box@>=
+q:=list_ptr(x);
+if is_char_node(q) then
+ begin r:=link(q);
+ if r<>null then if link(r)=null then if not is_char_node(r) then
+ if type(r)=kern_node then {unneeded italic correction}
+ begin free_node(r,small_node_size); link(q):=null;
+ end;
+ end
+
+@ It is convenient to have a procedure that converts a |math_char|
+field to an ``unpacked'' form. The |fetch| routine sets |cur_f|, |cur_c|,
+and |cur_i| to the font code, character code, and character information bytes of
+a given noad field. It also takes care of issuing error messages for
+nonexistent characters; in such cases, |char_exists(cur_i)| will be |false|
+after |fetch| has acted, and the field will also have been reset to |empty|.
+
+@p procedure fetch(@!a:pointer); {unpack the |math_char| field |a|}
+begin cur_c:=character(a); cur_f:=fam_fnt(fam(a)+cur_size);
+if cur_f=null_font then
+ @<Complain about an undefined family and set |cur_i| null@>
+else begin if (qo(cur_c)>=font_bc[cur_f])and(qo(cur_c)<=font_ec[cur_f]) then
+ cur_i:=char_info(cur_f)(cur_c)
+ else cur_i:=null_character;
+ if not(char_exists(cur_i)) then
+ begin char_warning(cur_f,qo(cur_c));
+ math_type(a):=empty;
+ end;
+ end;
+end;
+
+@ @<Complain about an undefined family...@>=
+begin print_err(""); print_size(cur_size); print_char(" ");
+print_int(fam(a)); print(" is undefined (character ");
+print_ASCII(qo(cur_c)); print_char(")");
+help4("Somewhere in the math formula just ended, you used the")@/
+("stated character from an undefined font family. For example,")@/
+("plain TeX doesn't allow \it or \sl in subscripts. Proceed,")@/
+("and I'll try to forget that I needed that character.");
+error; cur_i:=null_character; math_type(a):=empty;
+end
+
+@ The outputs of |fetch| are placed in global variables.
+
+@<Glob...@>=
+@!cur_f:internal_font_number; {the |font| field of a |math_char|}
+@!cur_c:quarterword; {the |character| field of a |math_char|}
+@!cur_i:four_quarters; {the |char_info| of a |math_char|,
+ or a lig/kern instruction}
+
+@ We need to do a lot of different things, so |mlist_to_hlist| makes two
+passes over the given mlist.
+
+The first pass does most of the processing: It removes ``mu'' spacing from
+glue, it recursively evaluates all subsidiary mlists so that only the
+top-level mlist remains to be handled, it puts fractions and square roots
+and such things into boxes, it attaches subscripts and superscripts, and
+it computes the overall height and depth of the top-level mlist so that
+the size of delimiters for a |left_noad| and a |right_noad| will be known.
+The hlist resulting from each noad is recorded in that noad's |new_hlist|
+field, an integer field that replaces the |nucleus| or |thickness|.
+@^recursion@>
+
+The second pass eliminates all noads and inserts the correct glue and
+penalties between nodes.
+
+@d new_hlist(#)==mem[nucleus(#)].int {the translation of an mlist}
+
+@ Here is the overall plan of |mlist_to_hlist|, and the list of its
+local variables.
+
+@d done_with_noad=80 {go here when a noad has been fully translated}
+@d done_with_node=81 {go here when a node has been fully converted}
+@d check_dimensions=82 {go here to update |max_h| and |max_d|}
+@d delete_q=83 {go here to delete |q| and move to the next node}
+
+@p@t\4@>@<Declare math construction procedures@>
+procedure mlist_to_hlist;
+label reswitch, check_dimensions, done_with_noad, done_with_node, delete_q,
+ done;
+var mlist:pointer; {beginning of the given list}
+@!penalties:boolean; {should penalty nodes be inserted?}
+@!style:small_number; {the given style}
+@!save_style:small_number; {holds |cur_style| during recursion}
+@!q:pointer; {runs through the mlist}
+@!r:pointer; {the most recent noad preceding |q|}
+@!r_type:small_number; {the |type| of noad |r|, or |op_noad| if |r=null|}
+@!t:small_number; {the effective |type| of noad |q| during the second pass}
+@!p,@!x,@!y,@!z: pointer; {temporary registers for list construction}
+@!pen:integer; {a penalty to be inserted}
+@!s:small_number; {the size of a noad to be deleted}
+@!max_h,@!max_d:scaled; {maximum height and depth of the list translated so far}
+@!delta:scaled; {offset between subscript and superscript}
+begin mlist:=cur_mlist; penalties:=mlist_penalties;
+style:=cur_style; {tuck global parameters away as local variables}
+q:=mlist; r:=null; r_type:=op_noad; max_h:=0; max_d:=0;
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+while q<>null do @<Process node-or-noad |q| as much as possible in preparation
+ for the second pass of |mlist_to_hlist|, then move to the next
+ item in the mlist@>;
+@<Convert \(a)a final |bin_noad| to an |ord_noad|@>;
+@<Make a second pass over the mlist, removing all noads and inserting the
+ proper spacing and penalties@>;
+end;
+
+@ We use the fact that no character nodes appear in an mlist, hence
+the field |type(q)| is always present.
+
+@<Process node-or-noad...@>=
+begin @<Do first-pass processing based on |type(q)|; |goto done_with_noad|
+ if a noad has been fully processed, |goto check_dimensions| if it
+ has been translated into |new_hlist(q)|, or |goto done_with_node|
+ if a node has been fully processed@>;
+check_dimensions: z:=hpack(new_hlist(q),natural);
+if height(z)>max_h then max_h:=height(z);
+if depth(z)>max_d then max_d:=depth(z);
+free_node(z,box_node_size);
+done_with_noad: r:=q; r_type:=type(r);
+done_with_node: q:=link(q);
+end
+
+@ One of the things we must do on the first pass is change a |bin_noad| to
+an |ord_noad| if the |bin_noad| is not in the context of a binary operator.
+The values of |r| and |r_type| make this fairly easy.
+
+@<Do first-pass processing...@>=
+reswitch: delta:=0;
+case type(q) of
+bin_noad: case r_type of
+ bin_noad,op_noad,rel_noad,open_noad,punct_noad,left_noad:
+ begin type(q):=ord_noad; goto reswitch;
+ end;
+ othercases do_nothing
+ endcases;
+rel_noad,close_noad,punct_noad,right_noad: begin@t@>@;@/
+ @<Convert \(a)a final |bin_noad| to an |ord_noad|@>;
+ if type(q)=right_noad then goto done_with_noad;
+ end;
+@t\4@>@<Cases for noads that can follow a |bin_noad|@>@;
+@t\4@>@<Cases for nodes that can appear in an mlist, after which we
+ |goto done_with_node|@>@;
+othercases confusion("mlist1")
+@:this can't happen mlist1}{\quad mlist1@>
+endcases;@/
+@<Convert \(n)|nucleus(q)| to an hlist and attach the sub/superscripts@>
+
+@ @<Convert \(a)a final |bin_noad| to an |ord_noad|@>=
+if r_type=bin_noad then type(r):=ord_noad
+
+@ @<Cases for nodes that can appear in an mlist...@>=
+style_node: begin cur_style:=subtype(q);
+ @<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+ goto done_with_node;
+ end;
+choice_node: @<Change this node to a style node followed by the correct choice,
+ then |goto done_with_node|@>;
+ins_node,mark_node,adjust_node,
+ whatsit_node,penalty_node,disc_node: goto done_with_node;
+rule_node: begin if height(q)>max_h then max_h:=height(q);
+ if depth(q)>max_d then max_d:=depth(q); goto done_with_node;
+ end;
+glue_node: begin @<Convert \(m)math glue to ordinary glue@>;
+ goto done_with_node;
+ end;
+kern_node: begin math_kern(q,cur_mu); goto done_with_node;
+ end;
+
+@ @d choose_mlist(#)==begin p:=#(q); #(q):=null;@+end
+
+@<Change this node to a style node...@>=
+begin case cur_style div 2 of
+0: choose_mlist(display_mlist); {|display_style=0|}
+1: choose_mlist(text_mlist); {|text_style=2|}
+2: choose_mlist(script_mlist); {|script_style=4|}
+3: choose_mlist(script_script_mlist); {|script_script_style=6|}
+end; {there are no other cases}
+flush_node_list(display_mlist(q));
+flush_node_list(text_mlist(q));
+flush_node_list(script_mlist(q));
+flush_node_list(script_script_mlist(q));@/
+type(q):=style_node; subtype(q):=cur_style; width(q):=0; depth(q):=0;
+if p<>null then
+ begin z:=link(q); link(q):=p;
+ while link(p)<>null do p:=link(p);
+ link(p):=z;
+ end;
+goto done_with_node;
+end
+
+@ Conditional math glue (`\.{\\nonscript}') results in a |glue_node|
+pointing to |zero_glue|, with |subtype(q)=cond_math_glue|; in such a case
+the node following will be eliminated if it is a glue or kern node and if the
+current size is different from |text_size|. Unconditional math glue
+(`\.{\\muskip}') is converted to normal glue by multiplying the dimensions
+by |cur_mu|.
+@!@:non_script_}{\.{\\nonscript} primitive@>
+
+@<Convert \(m)math glue to ordinary glue@>=
+if subtype(q)=mu_glue then
+ begin x:=glue_ptr(q);
+ y:=math_glue(x,cur_mu); delete_glue_ref(x); glue_ptr(q):=y;
+ subtype(q):=normal;
+ end
+else if (cur_size<>text_size)and(subtype(q)=cond_math_glue) then
+ begin p:=link(q);
+ if p<>null then if (type(p)=glue_node)or(type(p)=kern_node) then
+ begin link(q):=link(p); link(p):=null; flush_node_list(p);
+ end;
+ end
+
+@ @<Cases for noads that can follow a |bin_noad|@>=
+left_noad: goto done_with_noad;
+fraction_noad: begin make_fraction(q); goto check_dimensions;
+ end;
+op_noad: begin delta:=make_op(q);
+ if subtype(q)=limits then goto check_dimensions;
+ end;
+ord_noad: make_ord(q);
+open_noad,inner_noad: do_nothing;
+radical_noad: make_radical(q);
+over_noad: make_over(q);
+under_noad: make_under(q);
+accent_noad: make_math_accent(q);
+vcenter_noad: make_vcenter(q);
+
+@ Most of the actual construction work of |mlist_to_hlist| is done
+by procedures with names
+like |make_fraction|, |make_radical|, etc. To illustrate
+the general setup of such procedures, let's begin with a couple of
+simple ones.
+
+@<Declare math...@>=
+procedure make_over(@!q:pointer);
+begin info(nucleus(q)):=@|
+ overbar(clean_box(nucleus(q),cramped_style(cur_style)),@|
+ 3*default_rule_thickness,default_rule_thickness);
+math_type(nucleus(q)):=sub_box;
+end;
+
+@ @<Declare math...@>=
+procedure make_under(@!q:pointer);
+var p,@!x,@!y: pointer; {temporary registers for box construction}
+@!delta:scaled; {overall height plus depth}
+begin x:=clean_box(nucleus(q),cur_style);
+p:=new_kern(3*default_rule_thickness); link(x):=p;
+link(p):=fraction_rule(default_rule_thickness);
+y:=vpack(x,natural);
+delta:=height(y)+depth(y)+default_rule_thickness;
+height(y):=height(x); depth(y):=delta-height(y);
+info(nucleus(q)):=y; math_type(nucleus(q)):=sub_box;
+end;
+
+@ @<Declare math...@>=
+procedure make_vcenter(@!q:pointer);
+var v:pointer; {the box that should be centered vertically}
+@!delta:scaled; {its height plus depth}
+begin v:=info(nucleus(q));
+if type(v)<>vlist_node then confusion("vcenter");
+@:this can't happen vcenter}{\quad vcenter@>
+delta:=height(v)+depth(v);
+height(v):=axis_height(cur_size)+half(delta);
+depth(v):=delta-height(v);
+end;
+
+@ According to the rules in the \.{DVI} file specifications, we ensure alignment
+@^square roots@>
+between a square root sign and the rule above its nucleus by assuming that the
+baseline of the square-root symbol is the same as the bottom of the rule. The
+height of the square-root symbol will be the thickness of the rule, and the
+depth of the square-root symbol should exceed or equal the height-plus-depth
+of the nucleus plus a certain minimum clearance~|clr|. The symbol will be
+placed so that the actual clearance is |clr| plus half the excess.
+
+@<Declare math...@>=
+procedure make_radical(@!q:pointer);
+var x,@!y:pointer; {temporary registers for box construction}
+@!delta,@!clr:scaled; {dimensions involved in the calculation}
+begin x:=clean_box(nucleus(q),cramped_style(cur_style));
+if cur_style<text_style then {display style}
+ clr:=default_rule_thickness+(abs(math_x_height(cur_size)) div 4)
+else begin clr:=default_rule_thickness; clr:=clr + (abs(clr) div 4);
+ end;
+y:=var_delimiter(left_delimiter(q),cur_size,height(x)+depth(x)+clr+
+ default_rule_thickness);
+delta:=depth(y)-(height(x)+depth(x)+clr);
+if delta>0 then clr:=clr+half(delta); {increase the actual clearance}
+shift_amount(y):=-(height(x)+clr);
+link(y):=overbar(x,clr,height(y));
+info(nucleus(q)):=hpack(y,natural); math_type(nucleus(q)):=sub_box;
+end;
+
+@ Slants are not considered when placing accents in math mode. The accenter is
+centered over the accentee, and the accent width is treated as zero with
+respect to the size of the final box.
+
+@<Declare math...@>=
+procedure make_math_accent(@!q:pointer);
+label done,done1;
+var p,@!x,@!y:pointer; {temporary registers for box construction}
+@!a:integer; {address of lig/kern instruction}
+@!c:quarterword; {accent character}
+@!f:internal_font_number; {its font}
+@!i:four_quarters; {its |char_info|}
+@!s:scaled; {amount to skew the accent to the right}
+@!h:scaled; {height of character being accented}
+@!delta:scaled; {space to remove between accent and accentee}
+@!w:scaled; {width of the accentee, not including sub/superscripts}
+begin fetch(accent_chr(q));
+if char_exists(cur_i) then
+ begin i:=cur_i; c:=cur_c; f:=cur_f;@/
+ @<Compute the amount of skew@>;
+ x:=clean_box(nucleus(q),cramped_style(cur_style)); w:=width(x); h:=height(x);
+ @<Switch to a larger accent if available and appropriate@>;
+ if h<x_height(f) then delta:=h@+else delta:=x_height(f);
+ if (math_type(supscr(q))<>empty)or(math_type(subscr(q))<>empty) then
+ if math_type(nucleus(q))=math_char then
+ @<Swap the subscript and superscript into box |x|@>;
+ y:=char_box(f,c);
+ shift_amount(y):=s+half(w-width(y));
+ width(y):=0; p:=new_kern(-delta); link(p):=x; link(y):=p;
+ y:=vpack(y,natural); width(y):=width(x);
+ if height(y)<h then @<Make the height of box |y| equal to |h|@>;
+ info(nucleus(q)):=y;
+ math_type(nucleus(q)):=sub_box;
+ end;
+end;
+
+@ @<Make the height of box |y|...@>=
+begin p:=new_kern(h-height(y)); link(p):=list_ptr(y); list_ptr(y):=p;
+height(y):=h;
+end
+
+@ @<Switch to a larger accent if available and appropriate@>=
+loop@+ begin if char_tag(i)<>list_tag then goto done;
+ y:=rem_byte(i);
+ i:=char_info(f)(y);
+ if not char_exists(i) then goto done;
+ if char_width(f)(i)>w then goto done;
+ c:=y;
+ end;
+done:
+
+@ @<Compute the amount of skew@>=
+s:=0;
+if math_type(nucleus(q))=math_char then
+ begin fetch(nucleus(q));
+ if char_tag(cur_i)=lig_tag then
+ begin a:=lig_kern_start(cur_f)(cur_i);
+ cur_i:=font_info[a].qqqq;
+ if skip_byte(cur_i)>stop_flag then
+ begin a:=lig_kern_restart(cur_f)(cur_i);
+ cur_i:=font_info[a].qqqq;
+ end;
+ loop@+ begin if qo(next_char(cur_i))=skew_char[cur_f] then
+ begin if op_byte(cur_i)>=kern_flag then
+ if skip_byte(cur_i)<=stop_flag then s:=char_kern(cur_f)(cur_i);
+ goto done1;
+ end;
+ if skip_byte(cur_i)>=stop_flag then goto done1;
+ a:=a+qo(skip_byte(cur_i))+1;
+ cur_i:=font_info[a].qqqq;
+ end;
+ end;
+ end;
+done1:
+
+@ @<Swap the subscript and superscript into box |x|@>=
+begin flush_node_list(x); x:=new_noad;
+mem[nucleus(x)]:=mem[nucleus(q)];
+mem[supscr(x)]:=mem[supscr(q)];
+mem[subscr(x)]:=mem[subscr(q)];@/
+mem[supscr(q)].hh:=empty_field;
+mem[subscr(q)].hh:=empty_field;@/
+math_type(nucleus(q)):=sub_mlist; info(nucleus(q)):=x;
+x:=clean_box(nucleus(q),cur_style); delta:=delta+height(x)-h; h:=height(x);
+end
+
+@ The |make_fraction| procedure is a bit different because it sets
+|new_hlist(q)| directly rather than making a sub-box.
+
+@<Declare math...@>=
+procedure make_fraction(@!q:pointer);
+var p,@!v,@!x,@!y,@!z:pointer; {temporary registers for box construction}
+@!delta,@!delta1,@!delta2,@!shift_up,@!shift_down,@!clr:scaled;
+ {dimensions for box calculations}
+begin if thickness(q)=default_code then thickness(q):=default_rule_thickness;
+@<Create equal-width boxes |x| and |z| for the numerator and denominator,
+ and compute the default amounts |shift_up| and |shift_down| by which they
+ are displaced from the baseline@>;
+if thickness(q)=0 then @<Adjust \(s)|shift_up| and |shift_down| for the case
+ of no fraction line@>
+else @<Adjust \(s)|shift_up| and |shift_down| for the case of a fraction line@>;
+@<Construct a vlist box for the fraction, according to |shift_up| and
+ |shift_down|@>;
+@<Put the \(f)fraction into a box with its delimiters, and make |new_hlist(q)|
+ point to it@>;
+end;
+
+@ @<Create equal-width boxes |x| and |z| for the numerator and denom...@>=
+x:=clean_box(numerator(q),num_style(cur_style));
+z:=clean_box(denominator(q),denom_style(cur_style));
+if width(x)<width(z) then x:=rebox(x,width(z))
+else z:=rebox(z,width(x));
+if cur_style<text_style then {display style}
+ begin shift_up:=num1(cur_size); shift_down:=denom1(cur_size);
+ end
+else begin shift_down:=denom2(cur_size);
+ if thickness(q)<>0 then shift_up:=num2(cur_size)
+ else shift_up:=num3(cur_size);
+ end
+
+@ The numerator and denominator must be separated by a certain minimum
+clearance, called |clr| in the following program. The difference between
+|clr| and the actual clearance is |2delta|.
+
+@<Adjust \(s)|shift_up| and |shift_down| for the case of no fraction line@>=
+begin if cur_style<text_style then clr:=7*default_rule_thickness
+else clr:=3*default_rule_thickness;
+delta:=half(clr-((shift_up-depth(x))-(height(z)-shift_down)));
+if delta>0 then
+ begin shift_up:=shift_up+delta;
+ shift_down:=shift_down+delta;
+ end;
+end
+
+@ In the case of a fraction line, the minimum clearance depends on the actual
+thickness of the line.
+
+@<Adjust \(s)|shift_up| and |shift_down| for the case of a fraction line@>=
+begin if cur_style<text_style then clr:=3*thickness(q)
+else clr:=thickness(q);
+delta:=half(thickness(q));
+delta1:=clr-((shift_up-depth(x))-(axis_height(cur_size)+delta));
+delta2:=clr-((axis_height(cur_size)-delta)-(height(z)-shift_down));
+if delta1>0 then shift_up:=shift_up+delta1;
+if delta2>0 then shift_down:=shift_down+delta2;
+end
+
+@ @<Construct a vlist box for the fraction...@>=
+v:=new_null_box; type(v):=vlist_node;
+height(v):=shift_up+height(x); depth(v):=depth(z)+shift_down;
+width(v):=width(x); {this also equals |width(z)|}
+if thickness(q)=0 then
+ begin p:=new_kern((shift_up-depth(x))-(height(z)-shift_down));
+ link(p):=z;
+ end
+else begin y:=fraction_rule(thickness(q));@/
+ p:=new_kern((axis_height(cur_size)-delta)-@|(height(z)-shift_down));@/
+ link(y):=p; link(p):=z;@/
+ p:=new_kern((shift_up-depth(x))-(axis_height(cur_size)+delta));
+ link(p):=y;
+ end;
+link(x):=p; list_ptr(v):=x
+
+@ @<Put the \(f)fraction into a box with its delimiters...@>=
+if cur_style<text_style then delta:=delim1(cur_size)
+else delta:=delim2(cur_size);
+x:=var_delimiter(left_delimiter(q), cur_size, delta); link(x):=v;@/
+z:=var_delimiter(right_delimiter(q), cur_size, delta); link(v):=z;@/
+new_hlist(q):=hpack(x,natural)
+
+@ If the nucleus of an |op_noad| is a single character, it is to be
+centered vertically with respect to the axis, after first being enlarged
+(via a character list in the font) if we are in display style. The normal
+convention for placing displayed limits is to put them above and below the
+operator in display style.
+
+The italic correction is removed from the character if there is a subscript
+and the limits are not being displayed. The |make_op|
+routine returns the value that should be used as an offset between
+subscript and superscript.
+
+After |make_op| has acted, |subtype(q)| will be |limits| if and only if
+the limits have been set above and below the operator. In that case,
+|new_hlist(q)| will already contain the desired final box.
+
+@<Declare math...@>=
+function make_op(@!q:pointer):scaled;
+var delta:scaled; {offset between subscript and superscript}
+@!p,@!v,@!x,@!y,@!z:pointer; {temporary registers for box construction}
+@!c:quarterword;@+@!i:four_quarters; {registers for character examination}
+@!shift_up,@!shift_down:scaled; {dimensions for box calculation}
+begin if (subtype(q)=normal)and(cur_style<text_style) then
+ subtype(q):=limits;
+if math_type(nucleus(q))=math_char then
+ begin fetch(nucleus(q));
+ if (cur_style<text_style)and(char_tag(cur_i)=list_tag) then {make it larger}
+ begin c:=rem_byte(cur_i); i:=char_info(cur_f)(c);
+ if char_exists(i) then
+ begin cur_c:=c; cur_i:=i; character(nucleus(q)):=c;
+ end;
+ end;
+ delta:=char_italic(cur_f)(cur_i); x:=clean_box(nucleus(q),cur_style);
+ if (math_type(subscr(q))<>empty)and(subtype(q)<>limits) then
+ width(x):=width(x)-delta; {remove italic correction}
+ shift_amount(x):=half(height(x)-depth(x)) - axis_height(cur_size);
+ {center vertically}
+ math_type(nucleus(q)):=sub_box; info(nucleus(q)):=x;
+ end
+else delta:=0;
+if subtype(q)=limits then
+ @<Construct a box with limits above and below it, skewed by |delta|@>;
+make_op:=delta;
+end;
+
+@ The following program builds a vlist box |v| for displayed limits. The
+width of the box is not affected by the fact that the limits may be skewed.
+
+@<Construct a box with limits above and below it...@>=
+begin x:=clean_box(supscr(q),sup_style(cur_style));
+y:=clean_box(nucleus(q),cur_style);
+z:=clean_box(subscr(q),sub_style(cur_style));
+v:=new_null_box; type(v):=vlist_node; width(v):=width(y);
+if width(x)>width(v) then width(v):=width(x);
+if width(z)>width(v) then width(v):=width(z);
+x:=rebox(x,width(v)); y:=rebox(y,width(v)); z:=rebox(z,width(v));@/
+shift_amount(x):=half(delta); shift_amount(z):=-shift_amount(x);
+height(v):=height(y); depth(v):=depth(y);
+@<Attach the limits to |y| and adjust |height(v)|, |depth(v)| to
+ account for their presence@>;
+new_hlist(q):=v;
+end
+
+@ We use |shift_up| and |shift_down| in the following program for the
+amount of glue between the displayed operator |y| and its limits |x| and
+|z|. The vlist inside box |v| will consist of |x| followed by |y| followed
+by |z|, with kern nodes for the spaces between and around them.
+
+@<Attach the limits to |y| and adjust |height(v)|, |depth(v)|...@>=
+if math_type(supscr(q))=empty then
+ begin free_node(x,box_node_size); list_ptr(v):=y;
+ end
+else begin shift_up:=big_op_spacing3-depth(x);
+ if shift_up<big_op_spacing1 then shift_up:=big_op_spacing1;
+ p:=new_kern(shift_up); link(p):=y; link(x):=p;@/
+ p:=new_kern(big_op_spacing5); link(p):=x; list_ptr(v):=p;
+ height(v):=height(v)+big_op_spacing5+height(x)+depth(x)+shift_up;
+ end;
+if math_type(subscr(q))=empty then free_node(z,box_node_size)
+else begin shift_down:=big_op_spacing4-height(z);
+ if shift_down<big_op_spacing2 then shift_down:=big_op_spacing2;
+ p:=new_kern(shift_down); link(y):=p; link(p):=z;@/
+ p:=new_kern(big_op_spacing5); link(z):=p;
+ depth(v):=depth(v)+big_op_spacing5+height(z)+depth(z)+shift_down;
+ end
+
+@ A ligature found in a math formula does not create a |ligature_node|, because
+there is no question of hyphenation afterwards; the ligature will simply be
+stored in an ordinary |char_node|, after residing in an |ord_noad|.
+
+The |math_type| is converted to |math_text_char| here if we would not want to
+apply an italic correction to the current character unless it belongs
+to a math font (i.e., a font with |space=0|).
+
+No boundary characters enter into these ligatures.
+
+@<Declare math...@>=
+procedure make_ord(@!q:pointer);
+label restart,exit;
+var a:integer; {address of lig/kern instruction}
+@!p,@!r:pointer; {temporary registers for list manipulation}
+begin restart:@t@>@;@/
+if math_type(subscr(q))=empty then if math_type(supscr(q))=empty then
+ if math_type(nucleus(q))=math_char then
+ begin p:=link(q);
+ if p<>null then if (type(p)>=ord_noad)and(type(p)<=punct_noad) then
+ if math_type(nucleus(p))=math_char then
+ if fam(nucleus(p))=fam(nucleus(q)) then
+ begin math_type(nucleus(q)):=math_text_char;
+ fetch(nucleus(q));
+ if char_tag(cur_i)=lig_tag then
+ begin a:=lig_kern_start(cur_f)(cur_i);
+ cur_c:=character(nucleus(p));
+ cur_i:=font_info[a].qqqq;
+ if skip_byte(cur_i)>stop_flag then
+ begin a:=lig_kern_restart(cur_f)(cur_i);
+ cur_i:=font_info[a].qqqq;
+ end;
+ loop@+ begin @<If instruction |cur_i| is a kern with |cur_c|, attach
+ the kern after~|q|; or if it is a ligature with |cur_c|, combine
+ noads |q| and~|p| appropriately; then |return| if the cursor has
+ moved past a noad, or |goto restart|@>;
+ if skip_byte(cur_i)>=stop_flag then return;
+ a:=a+qo(skip_byte(cur_i))+1;
+ cur_i:=font_info[a].qqqq;
+ end;
+ end;
+ end;
+ end;
+exit:end;
+
+@ Note that a ligature between an |ord_noad| and another kind of noad
+is replaced by an |ord_noad|, when the two noads collapse into one.
+But we could make a parenthesis (say) change shape when it follows
+certain letters. Presumably a font designer will define such
+ligatures only when this convention makes sense.
+
+\chardef\?='174 % vertical line to indicate character retention
+
+@<If instruction |cur_i| is a kern with |cur_c|, ...@>=
+if next_char(cur_i)=cur_c then if skip_byte(cur_i)<=stop_flag then
+ if op_byte(cur_i)>=kern_flag then
+ begin p:=new_kern(char_kern(cur_f)(cur_i));
+ link(p):=link(q); link(q):=p; return;
+ end
+ else begin check_interrupt; {allow a way out of infinite ligature loop}
+ case op_byte(cur_i) of
+ qi(1),qi(5): character(nucleus(q)):=rem_byte(cur_i); {\.{=:\?}, \.{=:\?>}}
+ qi(2),qi(6): character(nucleus(p)):=rem_byte(cur_i); {\.{\?=:}, \.{\?=:>}}
+ qi(3),qi(7),qi(11):begin r:=new_noad; {\.{\?=:\?}, \.{\?=:\?>}, \.{\?=:\?>>}}
+ character(nucleus(r)):=rem_byte(cur_i);
+ fam(nucleus(r)):=fam(nucleus(q));@/
+ link(q):=r; link(r):=p;
+ if op_byte(cur_i)<qi(11) then math_type(nucleus(r)):=math_char
+ else math_type(nucleus(r)):=math_text_char; {prevent combination}
+ end;
+ othercases begin link(q):=link(p);
+ character(nucleus(q)):=rem_byte(cur_i); {\.{=:}}
+ mem[subscr(q)]:=mem[subscr(p)]; mem[supscr(q)]:=mem[supscr(p)];@/
+ free_node(p,noad_size);
+ end
+ endcases;
+ if op_byte(cur_i)>qi(3) then return;
+ math_type(nucleus(q)):=math_char; goto restart;
+ end
+
+@ When we get to the following part of the program, we have ``fallen through''
+from cases that did not lead to |check_dimensions| or |done_with_noad| or
+|done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be
+converted to an hlist, and whose subscripts and superscripts need to be
+appended if they are present.
+
+If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount
+by which a superscript should be moved right with respect to a subscript
+when both are present.
+@^subscripts@>
+@^superscripts@>
+
+@<Convert \(n)|nucleus(q)| to an hlist and attach the sub/superscripts@>=
+case math_type(nucleus(q)) of
+math_char, math_text_char:
+ @<Create a character node |p| for |nucleus(q)|, possibly followed
+ by a kern node for the italic correction, and set |delta| to the
+ italic correction if a subscript is present@>;
+empty: p:=null;
+sub_box: p:=info(nucleus(q));
+sub_mlist: begin cur_mlist:=info(nucleus(q)); save_style:=cur_style;
+ mlist_penalties:=false; mlist_to_hlist; {recursive call}
+@^recursion@>
+ cur_style:=save_style; @<Set up the values...@>;
+ p:=hpack(link(temp_head),natural);
+ end;
+othercases confusion("mlist2")
+@:this can't happen mlist2}{\quad mlist2@>
+endcases;@/
+new_hlist(q):=p;
+if (math_type(subscr(q))=empty)and(math_type(supscr(q))=empty) then
+ goto check_dimensions;
+make_scripts(q,delta)
+
+@ @<Create a character node |p| for |nucleus(q)|...@>=
+begin fetch(nucleus(q));
+if char_exists(cur_i) then
+ begin delta:=char_italic(cur_f)(cur_i); p:=new_character(cur_f,qo(cur_c));
+ if (math_type(nucleus(q))=math_text_char)and(space(cur_f)<>0) then
+ delta:=0; {no italic correction in mid-word of text font}
+ if (math_type(subscr(q))=empty)and(delta<>0) then
+ begin link(p):=new_kern(delta); delta:=0;
+ end;
+ end
+else p:=null;
+end
+
+@ The purpose of |make_scripts(q,delta)| is to attach the subscript and/or
+superscript of noad |q| to the list that starts at |new_hlist(q)|,
+given that the subscript and superscript aren't both empty. The superscript
+will appear to the right of the subscript by a given distance |delta|.
+
+We set |shift_down| and |shift_up| to the minimum amounts to shift the
+baseline of subscripts and superscripts based on the given nucleus.
+
+@<Declare math...@>=
+procedure make_scripts(@!q:pointer;@!delta:scaled);
+var p,@!x,@!y,@!z:pointer; {temporary registers for box construction}
+@!shift_up,@!shift_down,@!clr:scaled; {dimensions in the calculation}
+@!t:small_number; {subsidiary size code}
+begin p:=new_hlist(q);
+if is_char_node(p) then
+ begin shift_up:=0; shift_down:=0;
+ end
+else begin z:=hpack(p,natural);
+ if cur_style<script_style then t:=script_size@+else t:=script_script_size;
+ shift_up:=height(z)-sup_drop(t);
+ shift_down:=depth(z)+sub_drop(t);
+ free_node(z,box_node_size);
+ end;
+if math_type(supscr(q))=empty then
+ @<Construct a subscript box |x| when there is no superscript@>
+else begin @<Construct a superscript box |x|@>;
+ if math_type(subscr(q))=empty then shift_amount(x):=-shift_up
+ else @<Construct a sub/superscript combination box |x|, with the
+ superscript offset by |delta|@>;
+ end;
+if new_hlist(q)=null then new_hlist(q):=x
+else begin p:=new_hlist(q);
+ while link(p)<>null do p:=link(p);
+ link(p):=x;
+ end;
+end;
+
+@ When there is a subscript without a superscript, the top of the subscript
+should not exceed the baseline plus four-fifths of the x-height.
+
+@<Construct a subscript box |x| when there is no superscript@>=
+begin x:=clean_box(subscr(q),sub_style(cur_style));
+width(x):=width(x)+script_space;
+if shift_down<sub1(cur_size) then shift_down:=sub1(cur_size);
+clr:=height(x)-(abs(math_x_height(cur_size)*4) div 5);
+if shift_down<clr then shift_down:=clr;
+shift_amount(x):=shift_down;
+end
+
+@ The bottom of a superscript should never descend below the baseline plus
+one-fourth of the x-height.
+
+@<Construct a superscript box |x|@>=
+begin x:=clean_box(supscr(q),sup_style(cur_style));
+width(x):=width(x)+script_space;
+if odd(cur_style) then clr:=sup3(cur_size)
+else if cur_style<text_style then clr:=sup1(cur_size)
+else clr:=sup2(cur_size);
+if shift_up<clr then shift_up:=clr;
+clr:=depth(x)+(abs(math_x_height(cur_size)) div 4);
+if shift_up<clr then shift_up:=clr;
+end
+
+@ When both subscript and superscript are present, the subscript must be
+separated from the superscript by at least four times |default_rule_thickness|.
+If this condition would be violated, the subscript moves down, after which
+both subscript and superscript move up so that the bottom of the superscript
+is at least as high as the baseline plus four-fifths of the x-height.
+
+@<Construct a sub/superscript combination box |x|...@>=
+begin y:=clean_box(subscr(q),sub_style(cur_style));
+width(y):=width(y)+script_space;
+if shift_down<sub2(cur_size) then shift_down:=sub2(cur_size);
+clr:=4*default_rule_thickness-
+ ((shift_up-depth(x))-(height(y)-shift_down));
+if clr>0 then
+ begin shift_down:=shift_down+clr;
+ clr:=(abs(math_x_height(cur_size)*4) div 5)-(shift_up-depth(x));
+ if clr>0 then
+ begin shift_up:=shift_up+clr;
+ shift_down:=shift_down-clr;
+ end;
+ end;
+shift_amount(x):=delta; {superscript is |delta| to the right of the subscript}
+p:=new_kern((shift_up-depth(x))-(height(y)-shift_down)); link(x):=p; link(p):=y;
+x:=vpack(x,natural); shift_amount(x):=shift_down;
+end
+
+@ We have now tied up all the loose ends of the first pass of |mlist_to_hlist|.
+The second pass simply goes through and hooks everything together with the
+proper glue and penalties. It also handles the |left_noad| and |right_noad| that
+might be present, since |max_h| and |max_d| are now known. Variable |p| points
+to a node at the current end of the final hlist.
+
+@<Make a second pass over the mlist, ...@>=
+p:=temp_head; link(p):=null; q:=mlist; r_type:=0; cur_style:=style;
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+while q<>null do
+ begin @<If node |q| is a style node, change the style and |goto delete_q|;
+ otherwise if it is not a noad, put it into the hlist,
+ advance |q|, and |goto done|; otherwise set |s| to the size
+ of noad |q|, set |t| to the associated type (|ord_noad..
+ inner_noad|), and set |pen| to the associated penalty@>;
+ @<Append inter-element spacing based on |r_type| and |t|@>;
+ @<Append any |new_hlist| entries for |q|, and any appropriate penalties@>;
+ r_type:=t;
+ delete_q: r:=q; q:=link(q); free_node(r,s);
+ done: end
+
+@ Just before doing the big |case| switch in the second pass, the program
+sets up default values so that most of the branches are short.
+
+@<If node |q| is a style node, change the style...@>=
+t:=ord_noad; s:=noad_size; pen:=inf_penalty;
+case type(q) of
+op_noad,open_noad,close_noad,punct_noad,inner_noad: t:=type(q);
+bin_noad: begin t:=bin_noad; pen:=bin_op_penalty;
+ end;
+rel_noad: begin t:=rel_noad; pen:=rel_penalty;
+ end;
+ord_noad,vcenter_noad,over_noad,under_noad: do_nothing;
+radical_noad: s:=radical_noad_size;
+accent_noad: s:=accent_noad_size;
+fraction_noad: begin t:=inner_noad; s:=fraction_noad_size;
+ end;
+left_noad,right_noad: t:=make_left_right(q,style,max_d,max_h);
+style_node: @<Change the current style and |goto delete_q|@>;
+whatsit_node,penalty_node,rule_node,disc_node,adjust_node,ins_node,mark_node,
+ glue_node,kern_node:@t@>@;@/
+ begin link(p):=q; p:=q; q:=link(q); link(p):=null; goto done;
+ end;
+othercases confusion("mlist3")
+@:this can't happen mlist3}{\quad mlist3@>
+endcases
+
+@ The |make_left_right| function constructs a left or right delimiter of
+the required size and returns the value |open_noad| or |close_noad|. The
+|right_noad| and |left_noad| will both be based on the original |style|,
+so they will have consistent sizes.
+
+We use the fact that |right_noad-left_noad=close_noad-open_noad|.
+
+@<Declare math...@>=
+function make_left_right(@!q:pointer;@!style:small_number;
+ @!max_d,@!max_h:scaled):small_number;
+var delta,@!delta1,@!delta2:scaled; {dimensions used in the calculation}
+begin if style<script_style then cur_size:=text_size
+else cur_size:=16*((style-text_style) div 2);
+delta2:=max_d+axis_height(cur_size);
+delta1:=max_h+max_d-delta2;
+if delta2>delta1 then delta1:=delta2; {|delta1| is max distance from axis}
+delta:=(delta1 div 500)*delimiter_factor;
+delta2:=delta1+delta1-delimiter_shortfall;
+if delta<delta2 then delta:=delta2;
+new_hlist(q):=var_delimiter(delimiter(q),cur_size,delta);
+make_left_right:=type(q)-(left_noad-open_noad); {|open_noad| or |close_noad|}
+end;
+
+@ @<Change the current style and |goto delete_q|@>=
+begin cur_style:=subtype(q); s:=style_node_size;
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+goto delete_q;
+end
+
+@ The inter-element spacing in math formulas depends on a $8\times8$ table that
+\TeX\ preloads as a 64-digit string. The elements of this string have the
+following significance:
+$$\vbox{\halign{#\hfil\cr
+\.0 means no space;\cr
+\.1 means a conditional thin space (\.{\\nonscript\\mskip\\thinmuskip});\cr
+\.2 means a thin space (\.{\\mskip\\thinmuskip});\cr
+\.3 means a conditional medium space
+ (\.{\\nonscript\\mskip\\medmuskip});\cr
+\.4 means a conditional thick space
+ (\.{\\nonscript\\mskip\\thickmuskip});\cr
+\.* means an impossible case.\cr}}$$
+This is all pretty cryptic, but {\sl The \TeX book\/} explains what is
+supposed to happen, and the string makes it happen.
+@:TeXbook}{\sl The \TeX book@>
+
+A global variable |magic_offset| is computed so that if |a| and |b| are
+in the range |ord_noad..inner_noad|, then |str_pool[a*8+b+magic_offset]|
+is the digit for spacing between noad types |a| and |b|.
+
+If \PASCAL\ had provided a good way to preload constant arrays, this part of
+the program would not have been so strange.
+@:PASCAL}{\PASCAL@>
+
+@d math_spacing=@;@/
+@t\hskip-35pt@>
+"0234000122*4000133**3**344*0400400*000000234000111*1111112341011"
+@t$ \hskip-35pt$@>
+
+@<Glob...@>=
+@!magic_offset:integer; {used to find inter-element spacing}
+
+@ @<Compute the magic offset@>=
+magic_offset:=str_start[math_spacing]-9*ord_noad
+
+@ @<Append inter-element spacing based on |r_type| and |t|@>=
+if r_type>0 then {not the first noad}
+ begin case so(str_pool[r_type*8+t+magic_offset]) of
+ "0": x:=0;
+ "1": if cur_style<script_style then x:=thin_mu_skip_code@+else x:=0;
+ "2": x:=thin_mu_skip_code;
+ "3": if cur_style<script_style then x:=med_mu_skip_code@+else x:=0;
+ "4": if cur_style<script_style then x:=thick_mu_skip_code@+else x:=0;
+ othercases confusion("mlist4")
+@:this can't happen mlist4}{\quad mlist4@>
+ endcases;
+ if x<>0 then
+ begin y:=math_glue(glue_par(x),cur_mu);
+ z:=new_glue(y); glue_ref_count(y):=null; link(p):=z; p:=z;@/
+ subtype(z):=x+1; {store a symbolic subtype}
+ end;
+ end
+
+@ We insert a penalty node after the hlist entries of noad |q| if |pen|
+is not an ``infinite'' penalty, and if the node immediately following |q|
+is not a penalty node or a |rel_noad| or absent entirely.
+
+@<Append any |new_hlist| entries for |q|, and any appropriate penalties@>=
+if new_hlist(q)<>null then
+ begin link(p):=new_hlist(q);
+ repeat p:=link(p);
+ until link(p)=null;
+ end;
+if penalties then if link(q)<>null then if pen<inf_penalty then
+ begin r_type:=type(link(q));
+ if r_type<>penalty_node then if r_type<>rel_noad then
+ begin z:=new_penalty(pen); link(p):=z; p:=z;
+ end;
+ end
+
+@* \[37] Alignment.
+It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because
+they cut across so many of the control structures of \TeX.
+
+Therefore the
+present page is probably not the best place for a beginner to start reading
+this program; it is better to master everything else first.
+
+Let us focus our thoughts on an example of what the input might be, in order
+to get some idea about how the alignment miracle happens. The example doesn't
+do anything useful, but it is sufficiently general to indicate all of the
+special cases that must be dealt with; please do not be disturbed by its
+apparent complexity and meaninglessness.
+$$\vbox{\halign{\.{#}\hfil\cr
+{}\\tabskip 2pt plus 3pt\cr
+{}\\halign to 300pt\{u1\#v1\&\cr
+\hskip 50pt\\tabskip 1pt plus 1fil u2\#v2\&\cr
+\hskip 50pt u3\#v3\\cr\cr
+\hskip 25pt a1\&\\omit a2\&\\vrule\\cr\cr
+\hskip 25pt \\noalign\{\\vskip 3pt\}\cr
+\hskip 25pt b1\\span b2\\cr\cr
+\hskip 25pt \\omit\&c2\\span\\omit\\cr\}\cr}}$$
+Here's what happens:
+
+\yskip
+(0) When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine
+places the 300pt dimension onto the |save_stack|, and an |align_group|
+code is placed above it. This will make it possible to complete the alignment
+when the matching `\.\}' is found.
+
+(1) The preamble is scanned next. Macros in the preamble are not expanded,
+@^preamble@>
+except as part of a tabskip specification. For example, if \.{u2} had been
+a macro in the preamble above, it would have been expanded, since \TeX\
+must look for `\.{minus...}' as part of the tabskip glue. A ``preamble list''
+is constructed based on the user's preamble; in our case it contains the
+following seven items:
+$$\vbox{\halign{\.{#}\hfil\qquad&(#)\hfil\cr
+{}\\glue 2pt plus 3pt&the tabskip preceding column 1\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 1\cr
+{}\\glue 2pt plus 3pt&the tabskip between columns 1 and 2\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 2\cr
+{}\\glue 1pt plus 1fil&the tabskip between columns 2 and 3\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 3\cr
+{}\\glue 1pt plus 1fil&the tabskip following column 3\cr}}$$
+These ``alignrecord'' entries have the same size as an |unset_node|,
+since they will later be converted into such nodes. However, at the
+moment they have no |type| or |subtype| fields; they have |info| fields
+instead, and these |info| fields are initially set to the value |end_span|,
+for reasons explained below. Furthermore, the alignrecord nodes have no
+|height| or |depth| fields; these are renamed |u_part| and |v_part|,
+and they point to token lists for the templates of the alignment.
+For example, the |u_part| field in the first alignrecord points to the
+token list `\.{u1}', i.e., the template preceding the `\.\#' for column~1.
+
+(2) \TeX\ now looks at what follows the \.{\\cr} that ended the preamble.
+It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back to
+be read again, and the template `\.{u1}' is fed to the scanner. Just
+before reading `\.{u1}', \TeX\ goes into restricted horizontal mode.
+Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then (when the
+{\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans an |endv|
+token, indicating the end of a column. At this point an |unset_node| is
+created, containing the contents of the current hlist (i.e., `\.{u1a1v1}').
+The natural width of this unset node replaces the |width| field of the
+alignrecord for column~1; in general, the alignrecords will record the
+maximum natural width that has occurred so far in a given column.
+
+(3) Since `\.{\\omit}' follows the `\.\&', the templates for column~2
+are now bypassed. Again \TeX\ goes into restricted horizontal mode and
+makes an |unset_node| from the resulting hlist; but this time the
+hlist contains simply `\.{a2}'. The natural width of the new unset box
+is remembered in the |width| field of the alignrecord for column~2.
+
+(4) A third |unset_node| is created for column 3, using essentially the
+mechanism that worked for column~1; this unset box contains `\.{u3\\vrule
+v3}'. The vertical rule in this case has running dimensions that will later
+extend to the height and depth of the whole first row, since each |unset_node|
+in a row will eventually inherit the height and depth of its enclosing box.
+
+(5) The first row has now ended; it is made into a single unset box
+comprising the following seven items:
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: u1a1v1\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: a2\cr
+{}\\glue 1pt plus 1fil\cr
+{}\\unsetbox for 1 column: u3\\vrule v3\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+The width of this unset row is unimportant, but it has the correct height
+and depth, so the correct baselineskip glue will be computed as the row
+is inserted into a vertical list.
+
+(6) Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends
+additional material (in this case \.{\\vskip 3pt}) to the vertical list.
+While processing this material, \TeX\ will be in internal vertical
+mode, and |no_align_group| will be on |save_stack|.
+
+(7) The next row produces an unset box that looks like this:
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 2 columns: u1b1v1u2b2v2\cr
+{}\\glue 1pt plus 1fil\cr
+{}\\unsetbox for 1 column: {\rm(empty)}\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+The natural width of the unset box that spans columns 1~and~2 is stored
+in a ``span node,'' which we will explain later; the |info| field of the
+alignrecord for column~1 now points to the new span node, and the |info|
+of the span node points to |end_span|.
+
+(8) The final row produces the unset box
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: {\rm(empty)}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 2 columns: u2c2v2\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+A new span node is attached to the alignrecord for column 2.
+
+(9) The last step is to compute the true column widths and to change all the
+unset boxes to hboxes, appending the whole works to the vertical list that
+encloses the \.{\\halign}. The rules for deciding on the final widths of
+each unset column box will be explained below.
+
+\yskip\noindent
+Note that as \.{\\halign} is being processed, we fearlessly give up control
+to the rest of \TeX. At critical junctures, an alignment routine is
+called upon to step in and do some little action, but most of the time
+these routines just lurk in the background. It's something like
+post-hypnotic suggestion.
+
+@ We have mentioned that alignrecords contain no |height| or |depth| fields.
+Their |glue_sign| and |glue_order| are pre-empted as well, since it
+is necessary to store information about what to do when a template ends.
+This information is called the |extra_info| field.
+
+@d u_part(#)==mem[#+height_offset].int {pointer to \<u_j> token list}
+@d v_part(#)==mem[#+depth_offset].int {pointer to \<v_j> token list}
+@d extra_info(#)==info(#+list_offset) {info to remember during template}
+
+@ Alignments can occur within alignments, so a small stack is used to access
+the alignrecord information. At each level we have a |preamble| pointer,
+indicating the beginning of the preamble list; a |cur_align| pointer,
+indicating the current position in the preamble list; a |cur_span| pointer,
+indicating the value of |cur_align| at the beginning of a sequence of
+spanned columns; a |cur_loop| pointer, indicating the tabskip glue before
+an alignrecord that should be copied next if the current list is extended;
+and the |align_state| variable, which indicates the nesting of braces so
+that \.{\\cr} and \.{\\span} and tab marks are properly intercepted.
+There also are pointers |cur_head| and |cur_tail| to the head and tail
+of a list of adjustments being moved out from horizontal mode to
+vertical~mode.
+
+The current values of these seven quantities appear in global variables;
+when they have to be pushed down, they are stored in 5-word nodes, and
+|align_ptr| points to the topmost such node.
+
+@d preamble==link(align_head) {the current preamble list}
+@d align_stack_node_size=5 {number of |mem| words to save alignment states}
+
+@<Glob...@>=
+@!cur_align:pointer; {current position in preamble list}
+@!cur_span:pointer; {start of currently spanned columns in preamble list}
+@!cur_loop:pointer; {place to copy when extending a periodic preamble}
+@!align_ptr:pointer; {most recently pushed-down alignment stack node}
+@!cur_head,@!cur_tail:pointer; {adjustment list pointers}
+
+@ The |align_state| and |preamble| variables are initialized elsewhere.
+
+@<Set init...@>=
+align_ptr:=null; cur_align:=null; cur_span:=null; cur_loop:=null;
+cur_head:=null; cur_tail:=null;
+
+@ Alignment stack maintenance is handled by a pair of trivial routines
+called |push_alignment| and |pop_alignment|.
+
+@p procedure push_alignment;
+var p:pointer; {the new alignment stack node}
+begin p:=get_node(align_stack_node_size);
+link(p):=align_ptr; info(p):=cur_align;
+llink(p):=preamble; rlink(p):=cur_span;
+mem[p+2].int:=cur_loop; mem[p+3].int:=align_state;
+info(p+4):=cur_head; link(p+4):=cur_tail;
+align_ptr:=p;
+cur_head:=get_avail;
+end;
+@#
+procedure pop_alignment;
+var p:pointer; {the top alignment stack node}
+begin free_avail(cur_head);
+p:=align_ptr;
+cur_tail:=link(p+4); cur_head:=info(p+4);
+align_state:=mem[p+3].int; cur_loop:=mem[p+2].int;
+cur_span:=rlink(p); preamble:=llink(p);
+cur_align:=info(p); align_ptr:=link(p);
+free_node(p,align_stack_node_size);
+end;
+
+@ \TeX\ has eight procedures that govern alignments: |init_align| and
+|fin_align| are used at the very beginning and the very end; |init_row| and
+|fin_row| are used at the beginning and end of individual rows; |init_span|
+is used at the beginning of a sequence of spanned columns (possibly involving
+only one column); |init_col| and |fin_col| are used at the beginning and
+end of individual columns; and |align_peek| is used after \.{\\cr} to see
+whether the next item is \.{\\noalign}.
+
+We shall consider these routines in the order they are first used during
+the course of a complete \.{\\halign}, namely |init_align|, |align_peek|,
+|init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|.
+
+@ When \.{\\halign} or \.{\\valign} has been scanned in an appropriate
+mode, \TeX\ calls |init_align|, whose task is to get everything off to a
+good start. This mostly involves scanning the preamble and putting its
+information into the preamble list.
+@^preamble@>
+
+@p @t\4@>@<Declare the procedure called |get_preamble_token|@>@t@>@/
+procedure@?align_peek; forward;@t\2@>@/
+procedure@?normal_paragraph; forward;@t\2@>@/
+procedure init_align;
+label done, done1, done2, continue;
+var save_cs_ptr:pointer; {|warning_index| value for error messages}
+@!p:pointer; {for short-term temporary use}
+begin save_cs_ptr:=cur_cs; {\.{\\halign} or \.{\\valign}, usually}
+push_alignment; align_state:=-1000000; {enter a new alignment level}
+@<Check for improper alignment in displayed math@>;
+push_nest; {enter a new semantic level}
+@<Change current mode to |-vmode| for \.{\\halign}, |-hmode| for \.{\\valign}@>;
+scan_spec(align_group,false);@/
+@<Scan the preamble and record it in the |preamble| list@>;
+new_save_level(align_group);
+if every_cr<>null then begin_token_list(every_cr,every_cr_text);
+align_peek; {look for \.{\\noalign} or \.{\\omit}}
+end;
+
+@ In vertical modes, |prev_depth| already has the correct value. But
+if we are in |mmode| (displayed formula mode), we reach out to the
+enclosing vertical mode for the |prev_depth| value that produces the
+correct baseline calculations.
+
+@<Change current mode...@>=
+if mode=mmode then
+ begin mode:=-vmode; prev_depth:=nest[nest_ptr-2].aux_field.sc;
+ end
+else if mode>0 then negate(mode)
+
+@ When \.{\\halign} is used as a displayed formula, there should be
+no other pieces of mlists present.
+
+@<Check for improper alignment in displayed math@>=
+if (mode=mmode)and((tail<>head)or(incompleat_noad<>null)) then
+ begin print_err("Improper "); print_esc("halign"); print(" inside $$'s");
+@.Improper \\halign...@>
+ help3("Displays can use special alignments (like \eqalignno)")@/
+ ("only if nothing but the alignment itself is between $$'s.")@/
+ ("So I've deleted the formulas that preceded this alignment.");
+ error; flush_math;
+ end
+
+@ @<Scan the preamble and record it in the |preamble| list@>=
+preamble:=null; cur_align:=align_head; cur_loop:=null; scanner_status:=aligning;
+warning_index:=save_cs_ptr; align_state:=-1000000;
+ {at this point, |cur_cmd=left_brace|}
+loop@+ begin @<Append the current tabskip glue to the preamble list@>;
+ if cur_cmd=car_ret then goto done; {\.{\\cr} ends the preamble}
+ @<Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret|,
+ looking for changes in the tabskip glue; append an
+ alignrecord to the preamble list@>;
+ end;
+done: scanner_status:=normal
+
+@ @<Append the current tabskip glue to the preamble list@>=
+link(cur_align):=new_param_glue(tab_skip_code);
+cur_align:=link(cur_align)
+
+@ @<Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret|...@>=
+@<Scan the template \<u_j>, putting the resulting token list in |hold_head|@>;
+link(cur_align):=new_null_box; cur_align:=link(cur_align); {a new alignrecord}
+info(cur_align):=end_span; width(cur_align):=null_flag;
+u_part(cur_align):=link(hold_head);
+@<Scan the template \<v_j>, putting the resulting token list in |hold_head|@>;
+v_part(cur_align):=link(hold_head)
+
+@ We enter `\.{\\span}' into |eqtb| with |tab_mark| as its command code,
+and with |span_code| as the command modifier. This makes \TeX\ interpret it
+essentially the same as an alignment delimiter like `\.\&', yet it is
+recognizably different when we need to distinguish it from a normal delimiter.
+It also turns out to be useful to give a special |cr_code| to `\.{\\cr}',
+and an even larger |cr_cr_code| to `\.{\\crcr}'.
+
+The end of a template is represented by two ``frozen'' control sequences
+called \.{\\endtemplate}. The first has the command code |end_template|, which
+is |>outer_call|, so it will not easily disappear in the presence of errors.
+The |get_x_token| routine converts the first into the second, which has |endv|
+as its command code.
+
+@d span_code=256 {distinct from any character}
+@d cr_code=257 {distinct from |span_code| and from any character}
+@d cr_cr_code=cr_code+1 {this distinguishes \.{\\crcr} from \.{\\cr}}
+@d end_template_token==cs_token_flag+frozen_end_template
+
+@<Put each of \TeX's primitives into the hash table@>=
+primitive("span",tab_mark,span_code);@/
+@!@:span_}{\.{\\span} primitive@>
+primitive("cr",car_ret,cr_code);
+@!@:cr_}{\.{\\cr} primitive@>
+text(frozen_cr):="cr"; eqtb[frozen_cr]:=eqtb[cur_val];@/
+primitive("crcr",car_ret,cr_cr_code);
+@!@:cr_cr_}{\.{\\crcr} primitive@>
+text(frozen_end_template):="endtemplate"; text(frozen_endv):="endtemplate";
+eq_type(frozen_endv):=endv; equiv(frozen_endv):=null_list;
+eq_level(frozen_endv):=level_one;@/
+eqtb[frozen_end_template]:=eqtb[frozen_endv];
+eq_type(frozen_end_template):=end_template;
+
+@ @<Cases of |print_cmd_chr|...@>=
+tab_mark: if chr_code=span_code then print_esc("span")
+ else chr_cmd("alignment tab character ");
+car_ret: if chr_code=cr_code then print_esc("cr")
+ else print_esc("crcr");
+
+@ The preamble is copied directly, except that \.{\\tabskip} causes a change
+to the tabskip glue, thereby possibly expanding macros that immediately
+follow it. An appearance of \.{\\span} also causes such an expansion.
+
+Note that if the preamble contains `\.{\\global\\tabskip}', the `\.{\\global}'
+token survives in the preamble and the `\.{\\tabskip}' defines new
+tabskip glue (locally).
+
+@<Declare the procedure called |get_preamble_token|@>=
+procedure get_preamble_token;
+label restart;
+begin restart: get_token;
+while (cur_chr=span_code)and(cur_cmd=tab_mark) do
+ begin get_token; {this token will be expanded once}
+ if cur_cmd>max_command then
+ begin expand; get_token;
+ end;
+ end;
+if cur_cmd=endv then
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+if (cur_cmd=assign_glue)and(cur_chr=glue_base+tab_skip_code) then
+ begin scan_optional_equals; scan_glue(glue_val);
+ if global_defs>0 then geq_define(glue_base+tab_skip_code,glue_ref,cur_val)
+ else eq_define(glue_base+tab_skip_code,glue_ref,cur_val);
+ goto restart;
+ end;
+end;
+
+@ Spaces are eliminated from the beginning of a template.
+
+@<Scan the template \<u_j>...@>=
+p:=hold_head; link(p):=null;
+loop@+ begin get_preamble_token;
+ if cur_cmd=mac_param then goto done1;
+ if (cur_cmd<=car_ret)and(cur_cmd>=tab_mark)and(align_state=-1000000) then
+ if (p=hold_head)and(cur_loop=null)and(cur_cmd=tab_mark)
+ then cur_loop:=cur_align
+ else begin print_err("Missing # inserted in alignment preamble");
+@.Missing \# inserted...@>
+ help3("There should be exactly one # between &'s, when an")@/
+ ("\halign or \valign is being set up. In this case you had")@/
+ ("none, so I've put one in; maybe that will work.");
+ back_error; goto done1;
+ end
+ else if (cur_cmd<>spacer)or(p<>hold_head) then
+ begin link(p):=get_avail; p:=link(p); info(p):=cur_tok;
+ end;
+ end;
+done1:
+
+@ @<Scan the template \<v_j>...@>=
+p:=hold_head; link(p):=null;
+loop@+ begin continue: get_preamble_token;
+ if (cur_cmd<=car_ret)and(cur_cmd>=tab_mark)and(align_state=-1000000) then
+ goto done2;
+ if cur_cmd=mac_param then
+ begin print_err("Only one # is allowed per tab");
+@.Only one \# is allowed...@>
+ help3("There should be exactly one # between &'s, when an")@/
+ ("\halign or \valign is being set up. In this case you had")@/
+ ("more than one, so I'm ignoring all but the first.");
+ error; goto continue;
+ end;
+ link(p):=get_avail; p:=link(p); info(p):=cur_tok;
+ end;
+done2: link(p):=get_avail; p:=link(p);
+info(p):=end_template_token {put \.{\\endtemplate} at the end}
+
+@ The tricky part about alignments is getting the templates into the
+scanner at the right time, and recovering control when a row or column
+is finished.
+
+We usually begin a row after each \.{\\cr} has been sensed, unless that
+\.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates
+the alignment. The |align_peek| routine is used to look ahead and do
+the right thing; it either gets a new row started, or gets a \.{\\noalign}
+started, or finishes off the alignment.
+
+@<Declare the procedure called |align_peek|@>=
+procedure align_peek;
+label restart;
+begin restart: align_state:=1000000; @<Get the next non-blank non-call token@>;
+if cur_cmd=no_align then
+ begin scan_left_brace; new_save_level(no_align_group);
+ if mode=-vmode then normal_paragraph;
+ end
+else if cur_cmd=right_brace then fin_align
+else if (cur_cmd=car_ret)and(cur_chr=cr_cr_code) then
+ goto restart {ignore \.{\\crcr}}
+else begin init_row; {start a new row}
+ init_col; {start a new column and replace what we peeked at}
+ end;
+end;
+
+@ To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'),
+we enter a new semantic level, copy the first tabskip glue, and change
+from internal vertical mode to restricted horizontal mode or vice versa.
+The |space_factor| and |prev_depth| are not used on this semantic level,
+but we clear them to zero just to be tidy.
+
+@p @t\4@>@<Declare the procedure called |init_span|@>@t@>@/
+procedure init_row;
+begin push_nest; mode:=(-hmode-vmode)-mode;
+if mode=-hmode then space_factor:=0 @+else prev_depth:=0;
+tail_append(new_glue(glue_ptr(preamble)));
+subtype(tail):=tab_skip_code+1;@/
+cur_align:=link(preamble); cur_tail:=cur_head; init_span(cur_align);
+end;
+
+@ The parameter to |init_span| is a pointer to the alignrecord where the
+next column or group of columns will begin. A new semantic level is
+entered, so that the columns will generate a list for subsequent packaging.
+
+@<Declare the procedure called |init_span|@>=
+procedure init_span(@!p:pointer);
+begin push_nest;
+if mode=-hmode then space_factor:=1000
+else begin prev_depth:=ignore_depth; normal_paragraph;
+ end;
+cur_span:=p;
+end;
+
+@ When a column begins, we assume that |cur_cmd| is either |omit| or else
+the current token should be put back into the input until the \<u_j>
+template has been scanned. (Note that |cur_cmd| might be |tab_mark| or
+|car_ret|.) We also assume that |align_state| is approximately 1000000 at
+this time. We remain in the same mode, and start the template if it is
+called for.
+
+@p procedure init_col;
+begin extra_info(cur_align):=cur_cmd;
+if cur_cmd=omit then align_state:=0
+else begin back_input; begin_token_list(u_part(cur_align),u_template);
+ end; {now |align_state=1000000|}
+end;
+
+@ The scanner sets |align_state| to zero when the \<u_j> template ends. When
+a subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state=0|,
+the scanner activates the following code, which fires up the \<v_j> template.
+We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|,
+|span_code|, or a character code, depending on how the column text has ended.
+
+This part of the program had better not be activated when the preamble
+to another alignment is being scanned, or when no alignment preamble is active.
+
+@<Insert the \(v)\<v_j>...@>=
+begin if (scanner_status=aligning) or (cur_align=null) then
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+cur_cmd:=extra_info(cur_align); extra_info(cur_align):=cur_chr;
+if cur_cmd=omit then begin_token_list(omit_template,v_template)
+else begin_token_list(v_part(cur_align),v_template);
+align_state:=1000000; goto restart;
+end
+
+@ The token list |omit_template| just referred to is a constant token
+list that contains the special control sequence \.{\\endtemplate} only.
+
+@<Initialize the special...@>=
+info(omit_template):=end_template_token; {|link(omit_template)=null|}
+
+@ When the |endv| command at the end of a \<v_j> template comes through the
+scanner, things really start to happen; and it is the |fin_col| routine
+that makes them happen. This routine returns |true| if a row as well as a
+column has been finished.
+
+@p function fin_col:boolean;
+label exit;
+var p:pointer; {the alignrecord after the current one}
+@!q,@!r:pointer; {temporary pointers for list manipulation}
+@!s:pointer; {a new span node}
+@!u:pointer; {a new unset box}
+@!w:scaled; {natural width}
+@!o:glue_ord; {order of infinity}
+@!n:halfword; {span counter}
+begin if cur_align=null then confusion("endv");
+q:=link(cur_align);@+if q=null then confusion("endv");
+@:this can't happen endv}{\quad endv@>
+if align_state<500000 then
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+p:=link(q);
+@<If the preamble list has been traversed, check that the row has ended@>;
+if extra_info(cur_align)<>span_code then
+ begin unsave; new_save_level(align_group);@/
+ @<Package an unset box for the current column and record its width@>;
+ @<Copy the tabskip glue between columns@>;
+ if extra_info(cur_align)>=cr_code then
+ begin fin_col:=true; return;
+ end;
+ init_span(p);
+ end;
+align_state:=1000000; @<Get the next non-blank non-call token@>;
+cur_align:=p;
+init_col; fin_col:=false;
+exit: end;
+
+@ @<If the preamble list has been traversed, check that the row has ended@>=
+if (p=null)and(extra_info(cur_align)<cr_code) then
+ if cur_loop<>null then @<Lengthen the preamble periodically@>
+ else begin print_err("Extra alignment tab has been changed to ");
+@.Extra alignment tab...@>
+ print_esc("cr");
+ help3("You have given more \span or & marks than there were")@/
+ ("in the preamble to the \halign or \valign now in progress.")@/
+ ("So I'll assume that you meant to type \cr instead.");
+ extra_info(cur_align):=cr_code; error;
+ end
+
+@ @<Lengthen the preamble...@>=
+begin link(q):=new_null_box; p:=link(q); {a new alignrecord}
+info(p):=end_span; width(p):=null_flag; cur_loop:=link(cur_loop);
+@<Copy the templates from node |cur_loop| into node |p|@>;
+cur_loop:=link(cur_loop);
+link(p):=new_glue(glue_ptr(cur_loop));
+end
+
+@ @<Copy the templates from node |cur_loop| into node |p|@>=
+q:=hold_head; r:=u_part(cur_loop);
+while r<>null do
+ begin link(q):=get_avail; q:=link(q); info(q):=info(r); r:=link(r);
+ end;
+link(q):=null; u_part(p):=link(hold_head);
+q:=hold_head; r:=v_part(cur_loop);
+while r<>null do
+ begin link(q):=get_avail; q:=link(q); info(q):=info(r); r:=link(r);
+ end;
+link(q):=null; v_part(p):=link(hold_head)
+
+@ @<Copy the tabskip glue...@>=
+tail_append(new_glue(glue_ptr(link(cur_align))));
+subtype(tail):=tab_skip_code+1
+
+@ @<Package an unset...@>=
+begin if mode=-hmode then
+ begin adjust_tail:=cur_tail; u:=hpack(link(head),natural); w:=width(u);
+ cur_tail:=adjust_tail; adjust_tail:=null;
+ end
+else begin u:=vpackage(link(head),natural,0); w:=height(u);
+ end;
+n:=min_quarterword; {this represents a span count of 1}
+if cur_span<>cur_align then @<Update width entry for spanned columns@>
+else if w>width(cur_align) then width(cur_align):=w;
+type(u):=unset_node; span_count(u):=n;@/
+@<Determine the stretch order@>;
+glue_order(u):=o; glue_stretch(u):=total_stretch[o];@/
+@<Determine the shrink order@>;
+glue_sign(u):=o; glue_shrink(u):=total_shrink[o];@/
+pop_nest; link(tail):=u; tail:=u;
+end
+
+@ A span node is a 2-word record containing |width|, |info|, and |link|
+fields. The |link| field is not really a link, it indicates the number of
+spanned columns; the |info| field points to a span node for the same
+starting column, having a greater extent of spanning, or to |end_span|,
+which has the largest possible |link| field; the |width| field holds the
+largest natural width corresponding to a particular set of spanned columns.
+
+A list of the maximum widths so far, for spanned columns starting at a
+given column, begins with the |info| field of the alignrecord for that
+column.
+
+@d span_node_size=2 {number of |mem| words for a span node}
+
+@<Initialize the special list heads...@>=
+link(end_span):=max_quarterword+1; info(end_span):=null;
+
+@ @<Update width entry for spanned columns@>=
+begin q:=cur_span;
+repeat incr(n); q:=link(link(q));
+until q=cur_align;
+if n>max_quarterword then confusion("256 spans"); {this can happen, but won't}
+@^system dependencies@>
+@:this can't happen 256 spans}{\quad 256 spans@>
+q:=cur_span; while link(info(q))<n do q:=info(q);
+if link(info(q))>n then
+ begin s:=get_node(span_node_size); info(s):=info(q); link(s):=n;
+ info(q):=s; width(s):=w;
+ end
+else if width(info(q))<w then width(info(q)):=w;
+end
+
+@ At the end of a row, we append an unset box to the current vlist (for
+\.{\\halign}) or the current hlist (for \.{\\valign}). This unset box
+contains the unset boxes for the columns, separated by the tabskip glue.
+Everything will be set later.
+
+@p procedure fin_row;
+var p:pointer; {the new unset box}
+begin if mode=-hmode then
+ begin p:=hpack(link(head),natural);
+ pop_nest; append_to_vlist(p);
+ if cur_head<>cur_tail then
+ begin link(tail):=link(cur_head); tail:=cur_tail;
+ end;
+ end
+else begin p:=vpack(link(head),natural); pop_nest;
+ link(tail):=p; tail:=p; space_factor:=1000;
+ end;
+type(p):=unset_node; glue_stretch(p):=0;
+if every_cr<>null then begin_token_list(every_cr,every_cr_text);
+align_peek;
+end; {note that |glue_shrink(p)=0| since |glue_shrink==shift_amount|}
+
+@ Finally, we will reach the end of the alignment, and we can breathe a
+sigh of relief that memory hasn't overflowed. All the unset boxes will now be
+set so that the columns line up, taking due account of spanned columns.
+
+@p procedure@?do_assignments; forward;@t\2@>@/
+procedure@?resume_after_display; forward;@t\2@>@/
+procedure@?build_page; forward;@t\2@>@/
+procedure fin_align;
+var @!p,@!q,@!r,@!s,@!u,@!v: pointer; {registers for the list operations}
+@!t,@!w:scaled; {width of column}
+@!o:scaled; {shift offset for unset boxes}
+@!n:halfword; {matching span amount}
+@!rule_save:scaled; {temporary storage for |overfull_rule|}
+@!aux_save:memory_word; {temporary storage for |aux|}
+begin if cur_group<>align_group then confusion("align1");
+@:this can't happen align}{\quad align@>
+unsave; {that |align_group| was for individual entries}
+if cur_group<>align_group then confusion("align0");
+unsave; {that |align_group| was for the whole alignment}
+if nest[nest_ptr-1].mode_field=mmode then o:=display_indent
+ else o:=0;
+@<Go through the preamble list, determining the column widths and
+ changing the alignrecords to dummy unset boxes@>;
+@<Package the preamble list, to determine the actual tabskip glue amounts,
+ and let |p| point to this prototype box@>;
+@<Set the glue in all the unset boxes of the current list@>;
+flush_node_list(p); pop_alignment;
+@<Insert the \(c)current list into its environment@>;
+end;@/
+@t\4@>@<Declare the procedure called |align_peek|@>
+
+@ It's time now to dismantle the preamble list and to compute the column
+widths. Let $w_{ij}$ be the maximum of the natural widths of all entries
+that span columns $i$ through $j$, inclusive. The alignrecord for column~$i$
+contains $w_{ii}$ in its |width| field, and there is also a linked list of
+the nonzero $w_{ij}$ for increasing $j$, accessible via the |info| field;
+these span nodes contain the value $j-i+|min_quarterword|$ in their
+|link| fields. The values of $w_{ii}$ were initialized to |null_flag|, which
+we regard as $-\infty$.
+
+The final column widths are defined by the formula
+$$w_j=\max_{1\L i\L j}\biggl( w_{ij}-\sum_{i\L k<j}(t_k+w_k)\biggr),$$
+where $t_k$ is the natural width of the tabskip glue between columns
+$k$ and~$k+1$. However, if $w_{ij}=-\infty$ for all |i| in the range
+|1<=i<=j| (i.e., if every entry that involved column~|j| also involved
+column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after
+column~|j|.
+
+\TeX\ computes these values by using the following scheme: First $w_1=w_{11}$.
+Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, for all $j>1$.
+Then $w_2=w_{22}$. Then replace $w_{3j}$ by $\max(w_{3j},w_{2j}-t_2-w_2)$
+for all $j>2$; and so on. If any $w_j$ turns out to be $-\infty$, its
+value is changed to zero and so is the next tabskip.
+
+@<Go through the preamble list,...@>=
+q:=link(preamble);
+repeat flush_list(u_part(q)); flush_list(v_part(q));
+p:=link(link(q));
+if width(q)=null_flag then
+ @<Nullify |width(q)| and the tabskip glue following this column@>;
+if info(q)<>end_span then
+ @<Merge the widths in the span nodes of |q| with those of |p|,
+ destroying the span nodes of |q|@>;
+type(q):=unset_node; span_count(q):=min_quarterword; height(q):=0;
+depth(q):=0; glue_order(q):=normal; glue_sign(q):=normal;
+glue_stretch(q):=0; glue_shrink(q):=0; q:=p;
+until q=null
+
+@ @<Nullify |width(q)| and the tabskip glue following this column@>=
+begin width(q):=0; r:=link(q); s:=glue_ptr(r);
+if s<>zero_glue then
+ begin add_glue_ref(zero_glue); delete_glue_ref(s);
+ glue_ptr(r):=zero_glue;
+ end;
+end
+
+@ Merging of two span-node lists is a typical exercise in the manipulation of
+linearly linked data structures. The essential invariant in the following
+|repeat| loop is that we want to dispense with node |r|, in |q|'s list,
+and |u| is its successor; all nodes of |p|'s list up to and including |s|
+have been processed, and the successor of |s| matches |r| or precedes |r|
+or follows |r|, according as |link(r)=n| or |link(r)>n| or |link(r)<n|.
+
+@<Merge the widths...@>=
+begin t:=width(q)+width(glue_ptr(link(q)));
+r:=info(q); s:=end_span; info(s):=p; n:=min_quarterword+1;
+repeat width(r):=width(r)-t; u:=info(r);
+while link(r)>n do
+ begin s:=info(s); n:=link(info(s))+1;
+ end;
+if link(r)<n then
+ begin info(r):=info(s); info(s):=r; decr(link(r)); s:=r;
+ end
+else begin if width(r)>width(info(s)) then width(info(s)):=width(r);
+ free_node(r,span_node_size);
+ end;
+r:=u;
+until r=end_span;
+end
+
+@ Now the preamble list has been converted to a list of alternating unset
+boxes and tabskip glue, where the box widths are equal to the final
+column sizes. In case of \.{\\valign}, we change the widths to heights,
+so that a correct error message will be produced if the alignment is
+overfull or underfull.
+
+@<Package the preamble list...@>=
+save_ptr:=save_ptr-2; pack_begin_line:=-mode_line;
+if mode=-vmode then
+ begin rule_save:=overfull_rule;
+ overfull_rule:=0; {prevent rule from being packaged}
+ p:=hpack(preamble,saved(1),saved(0)); overfull_rule:=rule_save;
+ end
+else begin q:=link(preamble);
+ repeat height(q):=width(q); width(q):=0; q:=link(link(q));
+ until q=null;
+ p:=vpack(preamble,saved(1),saved(0));
+ q:=link(preamble);
+ repeat width(q):=height(q); height(q):=0; q:=link(link(q));
+ until q=null;
+ end;
+pack_begin_line:=0
+
+@ @<Set the glue in all the unset...@>=
+q:=link(head); s:=head;
+while q<>null do
+ begin if not is_char_node(q) then
+ if type(q)=unset_node then
+ @<Set the unset box |q| and the unset boxes in it@>
+ else if type(q)=rule_node then
+ @<Make the running dimensions in rule |q| extend to the
+ boundaries of the alignment@>;
+ s:=q; q:=link(q);
+ end
+
+@ @<Make the running dimensions in rule |q| extend...@>=
+begin if is_running(width(q)) then width(q):=width(p);
+if is_running(height(q)) then height(q):=height(p);
+if is_running(depth(q)) then depth(q):=depth(p);
+if o<>0 then
+ begin r:=link(q); link(q):=null; q:=hpack(q,natural);
+ shift_amount(q):=o; link(q):=r; link(s):=q;
+ end;
+end
+
+@ The unset box |q| represents a row that contains one or more unset boxes,
+depending on how soon \.{\\cr} occurred in that row.
+
+@<Set the unset box |q| and the unset boxes in it@>=
+begin if mode=-vmode then
+ begin type(q):=hlist_node; width(q):=width(p);
+ end
+else begin type(q):=vlist_node; height(q):=height(p);
+ end;
+glue_order(q):=glue_order(p); glue_sign(q):=glue_sign(p);
+glue_set(q):=glue_set(p); shift_amount(q):=o;
+r:=link(list_ptr(q)); s:=link(list_ptr(p));
+repeat @<Set the glue in node |r| and change it from an unset node@>;
+r:=link(link(r)); s:=link(link(s));
+until r=null;
+end
+
+@ A box made from spanned columns will be followed by tabskip glue nodes and
+by empty boxes as if there were no spanning. This permits perfect alignment
+of subsequent entries, and it prevents values that depend on floating point
+arithmetic from entering into the dimensions of any boxes.
+
+@<Set the glue in node |r|...@>=
+n:=span_count(r); t:=width(s); w:=t; u:=hold_head;
+while n>min_quarterword do
+ begin decr(n);
+ @<Append tabskip glue and an empty box to list |u|,
+ and update |s| and |t| as the prototype nodes are passed@>;
+ end;
+if mode=-vmode then
+ @<Make the unset node |r| into an |hlist_node| of width |w|,
+ setting the glue as if the width were |t|@>
+else @<Make the unset node |r| into a |vlist_node| of height |w|,
+ setting the glue as if the height were |t|@>;
+shift_amount(r):=0;
+if u<>hold_head then {append blank boxes to account for spanned nodes}
+ begin link(u):=link(r); link(r):=link(hold_head); r:=u;
+ end
+
+@ @<Append tabskip glue and an empty box to list |u|...@>=
+s:=link(s); v:=glue_ptr(s); link(u):=new_glue(v); u:=link(u);
+subtype(u):=tab_skip_code+1; t:=t+width(v);
+if glue_sign(p)=stretching then
+ begin if stretch_order(v)=glue_order(p) then
+ t:=t+round(float(glue_set(p))*stretch(v));
+@^real multiplication@>
+ end
+else if glue_sign(p)=shrinking then
+ begin if shrink_order(v)=glue_order(p) then
+ t:=t-round(float(glue_set(p))*shrink(v));
+ end;
+s:=link(s); link(u):=new_null_box; u:=link(u); t:=t+width(s);
+if mode=-vmode then width(u):=width(s)@+else
+ begin type(u):=vlist_node; height(u):=width(s);
+ end
+
+@ @<Make the unset node |r| into an |hlist_node| of width |w|...@>=
+begin height(r):=height(q); depth(r):=depth(q);
+if t=width(r) then
+ begin glue_sign(r):=normal; glue_order(r):=normal;
+ set_glue_ratio_zero(glue_set(r));
+ end
+else if t>width(r) then
+ begin glue_sign(r):=stretching;
+ if glue_stretch(r)=0 then set_glue_ratio_zero(glue_set(r))
+ else glue_set(r):=unfloat((t-width(r))/glue_stretch(r));
+@^real division@>
+ end
+else begin glue_order(r):=glue_sign(r); glue_sign(r):=shrinking;
+ if glue_shrink(r)=0 then set_glue_ratio_zero(glue_set(r))
+ else if (glue_order(r)=normal)and(width(r)-t>glue_shrink(r)) then
+ set_glue_ratio_one(glue_set(r))
+ else glue_set(r):=unfloat((width(r)-t)/glue_shrink(r));
+ end;
+width(r):=w; type(r):=hlist_node;
+end
+
+@ @<Make the unset node |r| into a |vlist_node| of height |w|...@>=
+begin width(r):=width(q);
+if t=height(r) then
+ begin glue_sign(r):=normal; glue_order(r):=normal;
+ set_glue_ratio_zero(glue_set(r));
+ end
+else if t>height(r) then
+ begin glue_sign(r):=stretching;
+ if glue_stretch(r)=0 then set_glue_ratio_zero(glue_set(r))
+ else glue_set(r):=unfloat((t-height(r))/glue_stretch(r));
+@^real division@>
+ end
+else begin glue_order(r):=glue_sign(r); glue_sign(r):=shrinking;
+ if glue_shrink(r)=0 then set_glue_ratio_zero(glue_set(r))
+ else if (glue_order(r)=normal)and(height(r)-t>glue_shrink(r)) then
+ set_glue_ratio_one(glue_set(r))
+ else glue_set(r):=unfloat((height(r)-t)/glue_shrink(r));
+ end;
+height(r):=w; type(r):=vlist_node;
+end
+
+@ We now have a completed alignment, in the list that starts at |head|
+and ends at |tail|. This list will be merged with the one that encloses
+it. (In case the enclosing mode is |mmode|, for displayed formulas,
+we will need to insert glue before and after the display; that part of the
+program will be deferred until we're more familiar with such operations.)
+
+In restricted horizontal mode, the |clang| part of |aux| is undefined;
+an over-cautious \PASCAL\ runtime system may complain about this.
+@^dirty \PASCAL@>
+
+@<Insert the \(c)current list into its environment@>=
+aux_save:=aux; p:=link(head); q:=tail; pop_nest;
+if mode=mmode then @<Finish an alignment in a display@>
+else begin aux:=aux_save; link(tail):=p;
+ if p<>null then tail:=q;
+ if mode=vmode then build_page;
+ end
+
+@* \[38] Breaking paragraphs into lines.
+We come now to what is probably the most interesting algorithm of \TeX:
+the mechanism for choosing the ``best possible'' breakpoints that yield
+the individual lines of a paragraph. \TeX's line-breaking algorithm takes
+a given horizontal list and converts it to a sequence of boxes that are
+appended to the current vertical list. In the course of doing this, it
+creates a special data structure containing three kinds of records that are
+not used elsewhere in \TeX. Such nodes are created while a paragraph is
+being processed, and they are destroyed afterwards; thus, the other parts
+of \TeX\ do not need to know anything about how line-breaking is done.
+
+The method used here is based on an approach devised by Michael F. Plass and
+@^Plass, Michael Frederick@>
+@^Knuth, Donald Ervin@>
+the author in 1977, subsequently generalized and improved by the same two
+people in 1980. A detailed discussion appears in {\sl SOFTWARE---Practice
+\AM\ Experience \bf11} (1981), 1119--1184, where it is shown that the
+line-breaking problem can be regarded as a special case of the problem of
+computing the shortest path in an acyclic network. The cited paper includes
+numerous examples and describes the history of line breaking as it has been
+practiced by printers through the ages. The present implementation adds two
+new ideas to the algorithm of 1980: Memory space requirements are considerably
+reduced by using smaller records for inactive nodes than for active ones,
+and arithmetic overflow is avoided by using ``delta distances'' instead of
+keeping track of the total distance from the beginning of the paragraph to the
+current point.
+
+@ The |line_break| procedure should be invoked only in horizontal mode; it
+leaves that mode and places its output into the current vlist of the
+enclosing vertical mode (or internal vertical mode).
+There is one explicit parameter: |final_widow_penalty| is the amount of
+additional penalty to be inserted before the final line of the paragraph.
+
+There are also a number of implicit parameters: The hlist to be broken
+starts at |link(head)|, and it is nonempty. The value of |prev_graf| in the
+enclosing semantic level tells where the paragraph should begin in the
+sequence of line numbers, in case hanging indentation or \.{\\parshape}
+is in use; |prev_graf| is zero unless this paragraph is being continued
+after a displayed formula. Other implicit parameters, such as the
+|par_shape_ptr| and various penalties to use for hyphenation, etc., appear
+in |eqtb|.
+
+After |line_break| has acted, it will have updated the current vlist and the
+value of |prev_graf|. Furthermore, the global variable |just_box| will
+point to the final box created by |line_break|, so that the width of this
+line can be ascertained when it is necessary to decide whether to use
+|above_display_skip| or |above_display_short_skip| before a displayed formula.
+
+@<Glob...@>=
+@!just_box:pointer; {the |hlist_node| for the last line of the new paragraph}
+
+@ Since |line_break| is a rather lengthy procedure---sort of a small world unto
+itself---we must build it up little by little, somewhat more cautiously
+than we have done with the simpler procedures of \TeX. Here is the
+general outline.
+
+@p@t\4@>@<Declare subprocedures for |line_break|@>
+procedure line_break(@!final_widow_penalty:integer);
+label done,done1,done2,done3,done4,done5,continue;
+var @<Local variables for line breaking@>@;
+begin pack_begin_line:=mode_line; {this is for over/underfull box messages}
+@<Get ready to start line breaking@>;
+@<Find optimal breakpoints@>;
+@<Break the paragraph at the chosen breakpoints, justify the resulting lines
+to the correct widths, and append them to the current vertical list@>;
+@<Clean up the memory by removing the break nodes@>;
+pack_begin_line:=0;
+end;
+
+@ The first task is to move the list from |head| to |temp_head| and go
+into the enclosing semantic level. We also append the \.{\\parfillskip}
+glue to the end of the paragraph, removing a space (or other glue node) if
+it was there, since spaces usually precede blank lines and instances of
+`\.{\$\$}'. The |par_fill_skip| is preceded by an infinite penalty, so
+it will never be considered as a potential breakpoint.
+
+This code assumes that a |glue_node| and a |penalty_node| occupy the
+same number of |mem|~words.
+@^data structure assumptions@>
+
+@<Get ready to start...@>=
+link(temp_head):=link(head);
+if is_char_node(tail) then tail_append(new_penalty(inf_penalty))
+else if type(tail)<>glue_node then tail_append(new_penalty(inf_penalty))
+else begin type(tail):=penalty_node; delete_glue_ref(glue_ptr(tail));
+ flush_node_list(leader_ptr(tail)); penalty(tail):=inf_penalty;
+ end;
+link(tail):=new_param_glue(par_fill_skip_code);
+init_cur_lang:=prev_graf mod @'200000;
+init_l_hyf:=prev_graf div @'20000000;
+init_r_hyf:=(prev_graf div @'200000) mod @'100;
+pop_nest;
+
+@ When looking for optimal line breaks, \TeX\ creates a ``break node'' for
+each break that is {\sl feasible}, in the sense that there is a way to end
+a line at the given place without requiring any line to stretch more than
+a given tolerance. A break node is characterized by three things: the position
+of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|,
+or |disc_node|); the ordinal number of the line that will follow this
+breakpoint; and the fitness classification of the line that has just
+ended, i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|.
+
+@d tight_fit=3 {fitness classification for lines shrinking 0.5 to 1.0 of their
+ shrinkability}
+@d loose_fit=1 {fitness classification for lines stretching 0.5 to 1.0 of their
+ stretchability}
+@d very_loose_fit=0 {fitness classification for lines stretching more than
+ their stretchability}
+@d decent_fit=2 {fitness classification for all other lines}
+
+@ The algorithm essentially determines the best possible way to achieve
+each feasible combination of position, line, and fitness. Thus, it answers
+questions like, ``What is the best way to break the opening part of the
+paragraph so that the fourth line is a tight line ending at such-and-such
+a place?'' However, the fact that all lines are to be the same length
+after a certain point makes it possible to regard all sufficiently large
+line numbers as equivalent, when the looseness parameter is zero, and this
+makes it possible for the algorithm to save space and time.
+
+An ``active node'' and a ``passive node'' are created in |mem| for each
+feasible breakpoint that needs to be considered. Active nodes are three
+words long and passive nodes are two words long. We need active nodes only
+for breakpoints near the place in the paragraph that is currently being
+examined, so they are recycled within a comparatively short time after
+they are created.
+
+@ An active node for a given breakpoint contains six fields:
+
+\yskip\hang|link| points to the next node in the list of active nodes; the
+last active node has |link=last_active|.
+
+\yskip\hang|break_node| points to the passive node associated with this
+breakpoint.
+
+\yskip\hang|line_number| is the number of the line that follows this
+breakpoint.
+
+\yskip\hang|fitness| is the fitness classification of the line ending at this
+breakpoint.
+
+\yskip\hang|type| is either |hyphenated| or |unhyphenated|, depending on
+whether this breakpoint is a |disc_node|.
+
+\yskip\hang|total_demerits| is the minimum possible sum of demerits over all
+lines leading from the beginning of the paragraph to this breakpoint.
+
+\yskip\noindent
+The value of |link(active)| points to the first active node on a linked list
+of all currently active nodes. This list is in order by |line_number|,
+except that nodes with |line_number>easy_line| may be in any order relative
+to each other.
+
+@d active_node_size=3 {number of words in active nodes}
+@d fitness==subtype {|very_loose_fit..tight_fit| on final line for this break}
+@d break_node==rlink {pointer to the corresponding passive node}
+@d line_number==llink {line that begins at this breakpoint}
+@d total_demerits(#)==mem[#+2].int {the quantity that \TeX\ minimizes}
+@d unhyphenated=0 {the |type| of a normal active break node}
+@d hyphenated=1 {the |type| of an active node that breaks at a |disc_node|}
+@d last_active==active {the active list ends where it begins}
+
+@ @<Initialize the special list heads...@>=
+type(last_active):=hyphenated; line_number(last_active):=max_halfword;
+subtype(last_active):=0; {the |subtype| is never examined by the algorithm}
+
+@ The passive node for a given breakpoint contains only four fields:
+
+\yskip\hang|link| points to the passive node created just before this one,
+if any, otherwise it is |null|.
+
+\yskip\hang|cur_break| points to the position of this breakpoint in the
+horizontal list for the paragraph being broken.
+
+\yskip\hang|prev_break| points to the passive node that should precede this
+one in an optimal path to this breakpoint.
+
+\yskip\hang|serial| is equal to |n| if this passive node is the |n|th
+one created during the current pass. (This field is used only when
+printing out detailed statistics about the line-breaking calculations.)
+
+\yskip\noindent
+There is a global variable called |passive| that points to the most
+recently created passive node. Another global variable, |printed_node|,
+is used to help print out the paragraph when detailed information about
+the line-breaking computation is being displayed.
+
+@d passive_node_size=2 {number of words in passive nodes}
+@d cur_break==rlink {in passive node, points to position of this breakpoint}
+@d prev_break==llink {points to passive node that should precede this one}
+@d serial==info {serial number for symbolic identification}
+
+@<Glob...@>=
+@!passive:pointer; {most recent node on passive list}
+@!printed_node:pointer; {most recent node that has been printed}
+@!pass_number:halfword; {the number of passive nodes allocated on this pass}
+
+@ The active list also contains ``delta'' nodes that help the algorithm
+compute the badness of individual lines. Such nodes appear only between two
+active nodes, and they have |type=delta_node|. If |p| and |r| are active nodes
+and if |q| is a delta node between them, so that |link(p)=q| and |link(q)=r|,
+then |q| tells the space difference between lines in the horizontal list that
+start after breakpoint |p| and lines that start after breakpoint |r|. In
+other words, if we know the length of the line that starts after |p| and
+ends at our current position, then the corresponding length of the line that
+starts after |r| is obtained by adding the amounts in node~|q|. A delta node
+contains six scaled numbers, since it must record the net change in glue
+stretchability with respect to all orders of infinity. The natural width
+difference appears in |mem[q+1].sc|; the stretch differences in units of
+pt, fil, fill, and filll appear in |mem[q+2..q+5].sc|; and the shrink difference
+appears in |mem[q+6].sc|. The |subtype| field of a delta node is not used.
+
+@d delta_node_size=7 {number of words in a delta node}
+@d delta_node=2 {|type| field in a delta node}
+
+@ As the algorithm runs, it maintains a set of six delta-like registers
+for the length of the line following the first active breakpoint to the
+current position in the given hlist. When it makes a pass through the
+active list, it also maintains a similar set of six registers for the
+length following the active breakpoint of current interest. A third set
+holds the length of an empty line (namely, the sum of \.{\\leftskip} and
+\.{\\rightskip}); and a fourth set is used to create new delta nodes.
+
+When we pass a delta node we want to do operations like
+$$\hbox{\ignorespaces|for
+k:=1 to 6 do cur_active_width[k]:=cur_active_width[k]+mem[q+k].sc|};$$ and we
+want to do this without the overhead of |for| loops. The |do_all_six|
+macro makes such six-tuples convenient.
+
+@d do_all_six(#)==#(1);#(2);#(3);#(4);#(5);#(6)
+
+@<Glob...@>=
+@!active_width:array[1..6] of scaled;
+ {distance from first active node to~|cur_p|}
+@!cur_active_width:array[1..6] of scaled; {distance from current active node}
+@!background:array[1..6] of scaled; {length of an ``empty'' line}
+@!break_width:array[1..6] of scaled; {length being computed after current break}
+
+@ Let's state the principles of the delta nodes more precisely and concisely,
+so that the following programs will be less obscure. For each legal
+breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and
+$\beta(p)$ such that the length of material in a line from breakpoint~|p|
+to breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$.
+Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from
+the beginning of the paragraph to a point ``after'' a break at |p| and to a
+point ``before'' a break at |q|; and $\gamma$ is the width of an empty line,
+namely the length contributed by \.{\\leftskip} and \.{\\rightskip}.
+
+Suppose, for example, that the paragraph consists entirely of alternating
+boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and
+let the skips have widths $y_1\ldots y_n$, so that the paragraph can be
+represented by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint
+at $y_i$; then $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)=
+x_1+y_1+\cdots+x_i$. To check this, note that the length of material from
+$p_2$ to $p_5$, say, is $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5)
+-\alpha(p_2)$.
+
+The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and
+shrinkability as well as a natural width. If we were to compute $\alpha(p)$
+and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and
+the multiprecise numbers would have to be kept in the active nodes.
+\TeX\ avoids this problem by working entirely with relative differences
+or ``deltas.'' Suppose, for example, that the active list contains
+$a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints
+and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$
+and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is
+currently positioned at some other breakpoint |p|, the |active_width| array
+contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through
+the list of active nodes and considering a tentative line that runs from
+$a_2$ to~|p|, say, the |cur_active_width| array will contain the value
+$\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$,
+we want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this
+is just $\delta_2$, which appears in the active list between $a_2$ and
+$a_3$. The |background| array contains $\gamma$. The |break_width| array
+will be used to calculate values of new delta nodes when the active
+list is being updated.
+
+@ Glue nodes in a horizontal list that is being paragraphed are not supposed to
+include ``infinite'' shrinkability; that is why the algorithm maintains
+four registers for stretching but only one for shrinking. If the user tries to
+introduce infinite shrinkability, the shrinkability will be reset to finite
+and an error message will be issued. A boolean variable |no_shrink_error_yet|
+prevents this error message from appearing more than once per paragraph.
+
+@d check_shrinkage(#)==if (shrink_order(#)<>normal)and(shrink(#)<>0) then
+ begin #:=finite_shrink(#);
+ end
+
+@<Glob...@>=
+@!no_shrink_error_yet:boolean; {have we complained about infinite shrinkage?}
+
+@ @<Declare subprocedures for |line_break|@>=
+function finite_shrink(@!p:pointer):pointer; {recovers from infinite shrinkage}
+var q:pointer; {new glue specification}
+begin if no_shrink_error_yet then
+ begin no_shrink_error_yet:=false;
+ print_err("Infinite glue shrinkage found in a paragraph");
+@.Infinite glue shrinkage...@>
+ help5("The paragraph just ended includes some glue that has")@/
+ ("infinite shrinkability, e.g., `\hskip 0pt minus 1fil'.")@/
+ ("Such glue doesn't belong there---it allows a paragraph")@/
+ ("of any length to fit on one line. But it's safe to proceed,")@/
+ ("since the offensive shrinkability has been made finite.");
+ error;
+ end;
+q:=new_spec(p); shrink_order(q):=normal;
+delete_glue_ref(p); finite_shrink:=q;
+end;
+
+@ @<Get ready to start...@>=
+no_shrink_error_yet:=true;@/
+check_shrinkage(left_skip); check_shrinkage(right_skip);@/
+q:=left_skip; r:=right_skip; background[1]:=width(q)+width(r);@/
+background[2]:=0; background[3]:=0; background[4]:=0; background[5]:=0;@/
+background[2+stretch_order(q)]:=stretch(q);@/
+background[2+stretch_order(r)]:=@|background[2+stretch_order(r)]+stretch(r);@/
+background[6]:=shrink(q)+shrink(r);
+
+@ A pointer variable |cur_p| runs through the given horizontal list as we look
+for breakpoints. This variable is global, since it is used both by |line_break|
+and by its subprocedure |try_break|.
+
+Another global variable called |threshold| is used to determine the feasibility
+of individual lines: Breakpoints are feasible if there is a way to reach
+them without creating lines whose badness exceeds |threshold|. (The
+badness is compared to |threshold| before penalties are added, so that
+penalty values do not affect the feasibility of breakpoints, except that
+no break is allowed when the penalty is 10000 or more.) If |threshold|
+is 10000 or more, all legal breaks are considered feasible, since the
+|badness| function specified above never returns a value greater than~10000.
+
+Up to three passes might be made through the paragraph in an attempt to find at
+least one set of feasible breakpoints. On the first pass, we have
+|threshold=pretolerance| and |second_pass=final_pass=false|.
+If this pass fails to find a
+feasible solution, |threshold| is set to |tolerance|, |second_pass| is set
+|true|, and an attempt is made to hyphenate as many words as possible.
+If that fails too, we add |emergency_stretch| to the background
+stretchability and set |final_pass=true|.
+
+@<Glob...@>=
+@!cur_p:pointer; {the current breakpoint under consideration}
+@!second_pass:boolean; {is this our second attempt to break this paragraph?}
+@!final_pass:boolean; {is this our final attempt to break this paragraph?}
+@!threshold:integer; {maximum badness on feasible lines}
+
+@ The heart of the line-breaking procedure is `|try_break|', a subroutine
+that tests if the current breakpoint |cur_p| is feasible, by running
+through the active list to see what lines of text can be made from active
+nodes to~|cur_p|. If feasible breaks are possible, new break nodes are
+created. If |cur_p| is too far from an active node, that node is
+deactivated.
+
+The parameter |pi| to |try_break| is the penalty associated
+with a break at |cur_p|; we have |pi=eject_penalty| if the break is forced,
+and |pi=inf_penalty| if the break is illegal.
+
+The other parameter, |break_type|, is set to |hyphenated| or |unhyphenated|,
+depending on whether or not the current break is at a |disc_node|. The
+end of a paragraph is also regarded as `|hyphenated|'; this case is
+distinguishable by the condition |cur_p=null|.
+
+@d copy_to_cur_active(#)==cur_active_width[#]:=active_width[#]
+@d deactivate=60 {go here when node |r| should be deactivated}
+
+@<Declare subprocedures for |line_break|@>=
+procedure try_break(@!pi:integer;@!break_type:small_number);
+label exit,done,done1,continue,deactivate;
+var r:pointer; {runs through the active list}
+@!prev_r:pointer; {stays a step behind |r|}
+@!old_l:halfword; {maximum line number in current equivalence class of lines}
+@!no_break_yet:boolean; {have we found a feasible break at |cur_p|?}
+@<Other local variables for |try_break|@>@;
+begin @<Make sure that |pi| is in the proper range@>;
+no_break_yet:=true; prev_r:=active; old_l:=0;
+do_all_six(copy_to_cur_active);
+loop@+ begin continue: r:=link(prev_r);
+ @<If node |r| is of type |delta_node|, update |cur_active_width|,
+ set |prev_r| and |prev_prev_r|, then |goto continue|@>;
+ @<If a line number class has ended, create new active nodes for
+ the best feasible breaks in that class; then |return|
+ if |r=last_active|, otherwise compute the new |line_width|@>;
+ @<Consider the demerits for a line from |r| to |cur_p|;
+ deactivate node |r| if it should no longer be active;
+ then |goto continue| if a line from |r| to |cur_p| is infeasible,
+ otherwise record a new feasible break@>;
+ end;
+exit: @!stat @<Update the value of |printed_node| for
+ symbolic displays@>@+tats@;
+end;
+
+@ @<Other local variables for |try_break|@>=
+@!prev_prev_r:pointer; {a step behind |prev_r|, if |type(prev_r)=delta_node|}
+@!s:pointer; {runs through nodes ahead of |cur_p|}
+@!q:pointer; {points to a new node being created}
+@!v:pointer; {points to a glue specification or a node ahead of |cur_p|}
+@!t:integer; {node count, if |cur_p| is a discretionary node}
+@!f:internal_font_number; {used in character width calculation}
+@!l:halfword; {line number of current active node}
+@!node_r_stays_active:boolean; {should node |r| remain in the active list?}
+@!line_width:scaled; {the current line will be justified to this width}
+@!fit_class:very_loose_fit..tight_fit; {possible fitness class of test line}
+@!b:halfword; {badness of test line}
+@!d:integer; {demerits of test line}
+@!artificial_demerits:boolean; {has |d| been forced to zero?}
+@!save_link:pointer; {temporarily holds value of |link(cur_p)|}
+@!shortfall:scaled; {used in badness calculations}
+
+@ @<Make sure that |pi| is in the proper range@>=
+if abs(pi)>=inf_penalty then
+ if pi>0 then return {this breakpoint is inhibited by infinite penalty}
+ else pi:=eject_penalty {this breakpoint will be forced}
+
+@ The following code uses the fact that |type(last_active)<>delta_node|.
+
+@d update_width(#)==@|
+ cur_active_width[#]:=cur_active_width[#]+mem[r+#].sc
+
+@<If node |r|...@>=
+@^inner loop@>
+if type(r)=delta_node then
+ begin do_all_six(update_width);
+ prev_prev_r:=prev_r; prev_r:=r; goto continue;
+ end
+
+@ As we consider various ways to end a line at |cur_p|, in a given line number
+class, we keep track of the best total demerits known, in an array with
+one entry for each of the fitness classifications. For example,
+|minimal_demerits[tight_fit]| contains the fewest total demerits of feasible
+line breaks ending at |cur_p| with a |tight_fit| line; |best_place[tight_fit]|
+points to the passive node for the break before~|cur_p| that achieves such
+an optimum; and |best_pl_line[tight_fit]| is the |line_number| field in the
+active node corresponding to |best_place[tight_fit]|. When no feasible break
+sequence is known, the |minimal_demerits| entries will be equal to
+|awful_bad|, which is $2^{30}-1$. Another variable, |minimum_demerits|,
+keeps track of the smallest value in the |minimal_demerits| array.
+
+@d awful_bad==@'7777777777 {more than a billion demerits}
+
+@<Global...@>=
+@!minimal_demerits:array[very_loose_fit..tight_fit] of integer; {best total
+ demerits known for current line class and position, given the fitness}
+@!minimum_demerits:integer; {best total demerits known for current line class
+ and position}
+@!best_place:array[very_loose_fit..tight_fit] of pointer; {how to achieve
+ |minimal_demerits|}
+@!best_pl_line:array[very_loose_fit..tight_fit] of halfword; {corresponding
+ line number}
+
+@ @<Get ready to start...@>=
+minimum_demerits:=awful_bad;
+minimal_demerits[tight_fit]:=awful_bad;
+minimal_demerits[decent_fit]:=awful_bad;
+minimal_demerits[loose_fit]:=awful_bad;
+minimal_demerits[very_loose_fit]:=awful_bad;
+
+@ The first part of the following code is part of \TeX's inner loop, so
+we don't want to waste any time. The current active node, namely node |r|,
+contains the line number that will be considered next. At the end of the
+list we have arranged the data structure so that |r=last_active| and
+|line_number(last_active)>old_l|.
+@^inner loop@>
+
+@<If a line number class...@>=
+begin l:=line_number(r);
+if l>old_l then
+ begin {now we are no longer in the inner loop}
+ if (minimum_demerits<awful_bad)and@|
+ ((old_l<>easy_line)or(r=last_active)) then
+ @<Create new active nodes for the best feasible breaks
+ just found@>;
+ if r=last_active then return;
+ @<Compute the new line width@>;
+ end;
+end
+
+@ It is not necessary to create new active nodes having |minimal_demerits|
+greater than
+|minimum_demerits+abs(adj_demerits)|, since such active nodes will never
+be chosen in the final paragraph breaks. This observation allows us to
+omit a substantial number of feasible breakpoints from further consideration.
+
+@<Create new active nodes...@>=
+begin if no_break_yet then @<Compute the values of |break_width|@>;
+@<Insert a delta node to prepare for breaks at |cur_p|@>;
+if abs(adj_demerits)>=awful_bad-minimum_demerits then
+ minimum_demerits:=awful_bad-1
+else minimum_demerits:=minimum_demerits+abs(adj_demerits);
+for fit_class:=very_loose_fit to tight_fit do
+ begin if minimal_demerits[fit_class]<=minimum_demerits then
+ @<Insert a new active node
+ from |best_place[fit_class]| to |cur_p|@>;
+ minimal_demerits[fit_class]:=awful_bad;
+ end;
+minimum_demerits:=awful_bad;
+@<Insert a delta node to prepare for the next active node@>;
+end
+
+@ When we insert a new active node for a break at |cur_p|, suppose this
+new node is to be placed just before active node |a|; then we essentially
+want to insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where
+$\delta=\alpha(a)-\alpha(|cur_p|)$ and $\delta^\prime=\alpha(|cur_p|)-\alpha(a)$
+in the notation explained above. The |cur_active_width| array now holds
+$\gamma+\beta(|cur_p|)-\alpha(a)$; so $\delta$ can be obtained by
+subtracting |cur_active_width| from the quantity $\gamma+\beta(|cur_p|)-
+\alpha(|cur_p|)$. The latter quantity can be regarded as the length of a
+line ``from |cur_p| to |cur_p|''; we call it the |break_width| at |cur_p|.
+
+The |break_width| is usually negative, since it consists of the background
+(which is normally zero) minus the width of nodes following~|cur_p| that are
+eliminated after a break. If, for example, node |cur_p| is a glue node, the
+width of this glue is subtracted from the background; and we also look
+ahead to eliminate all subsequent glue and penalty and kern and math
+nodes, subtracting their widths as well.
+
+Kern nodes do not disappear at a line break unless they are |explicit|.
+
+@d set_break_width_to_background(#)==break_width[#]:=background[#]
+
+@<Compute the values of |break...@>=
+begin no_break_yet:=false; do_all_six(set_break_width_to_background);
+s:=cur_p;
+if break_type>unhyphenated then if cur_p<>null then
+ @<Compute the discretionary |break_width| values@>;
+while s<>null do
+ begin if is_char_node(s) then goto done;
+ case type(s) of
+ glue_node:@<Subtract glue from |break_width|@>;
+ penalty_node: do_nothing;
+ math_node: break_width[1]:=break_width[1]-width(s);
+ kern_node: if subtype(s)<>explicit then goto done
+ else break_width[1]:=break_width[1]-width(s);
+ othercases goto done
+ endcases;@/
+ s:=link(s);
+ end;
+done: end
+
+@ @<Subtract glue from |break...@>=
+begin v:=glue_ptr(s); break_width[1]:=break_width[1]-width(v);
+break_width[2+stretch_order(v)]:=break_width[2+stretch_order(v)]-stretch(v);
+break_width[6]:=break_width[6]-shrink(v);
+end
+
+@ When |cur_p| is a discretionary break, the length of a line ``from |cur_p| to
+|cur_p|'' has to be defined properly so that the other calculations work out.
+Suppose that the pre-break text at |cur_p| has length $l_0$, the post-break
+text has length $l_1$, and the replacement text has length |l|. Suppose
+also that |q| is the node following the replacement text. Then length of a
+line from |cur_p| to |q| will be computed as $\gamma+\beta(q)-\alpha(|cur_p|)$,
+where $\beta(q)=\beta(|cur_p|)-l_0+l$. The actual length will be the background
+plus $l_1$, so the length from |cur_p| to |cur_p| should be $\gamma+l_0+l_1-l$.
+If the post-break text of the discretionary is empty, a break may also
+discard~|q|; in that unusual case we subtract the length of~|q| and any
+other nodes that will be discarded after the discretionary break.
+
+The value of $l_0$ need not be computed, since |line_break| will put
+it into the global variable |disc_width| before calling |try_break|.
+
+@<Glob...@>=
+@!disc_width:scaled; {the length of discretionary material preceding a break}
+
+@ @<Compute the discretionary |break...@>=
+begin t:=replace_count(cur_p); v:=cur_p; s:=post_break(cur_p);
+while t>0 do
+ begin decr(t); v:=link(v);
+ @<Subtract the width of node |v| from |break_width|@>;
+ end;
+while s<>null do
+ begin @<Add the width of node |s| to |break_width|@>;
+ s:=link(s);
+ end;
+break_width[1]:=break_width[1]+disc_width;
+if post_break(cur_p)=null then s:=link(v);
+ {nodes may be discardable after the break}
+end
+
+@ Replacement texts and discretionary texts are supposed to contain
+only character nodes, kern nodes, ligature nodes, and box or rule nodes.
+
+@<Subtract the width of node |v|...@>=
+if is_char_node(v) then
+ begin f:=font(v);
+ break_width[1]:=break_width[1]-char_width(f)(char_info(f)(character(v)));
+ end
+else case type(v) of
+ ligature_node: begin f:=font(lig_char(v));@/
+ break_width[1]:=@|break_width[1]-
+ char_width(f)(char_info(f)(character(lig_char(v))));
+ end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]-width(v);
+ othercases confusion("disc1")
+@:this can't happen disc1}{\quad disc1@>
+ endcases
+
+@ @<Add the width of node |s| to |b...@>=
+if is_char_node(s) then
+ begin f:=font(s);
+ break_width[1]:=@|break_width[1]+char_width(f)(char_info(f)(character(s)));
+ end
+else case type(s) of
+ ligature_node: begin f:=font(lig_char(s));
+ break_width[1]:=break_width[1]+
+ char_width(f)(char_info(f)(character(lig_char(s))));
+ end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ break_width[1]:=break_width[1]+width(s);
+ othercases confusion("disc2")
+@:this can't happen disc2}{\quad disc2@>
+ endcases
+
+@ We use the fact that |type(active)<>delta_node|.
+
+@d convert_to_break_width(#)==@|
+ mem[prev_r+#].sc:=@|@t\hskip10pt@>mem[prev_r+#].sc
+ -cur_active_width[#]+break_width[#]
+@d store_break_width(#)==active_width[#]:=break_width[#]
+@d new_delta_to_break_width(#)==@|
+ mem[q+#].sc:=break_width[#]-cur_active_width[#]
+
+@<Insert a delta node to prepare for breaks at |cur_p|@>=
+if type(prev_r)=delta_node then {modify an existing delta node}
+ begin do_all_six(convert_to_break_width);
+ end
+else if prev_r=active then {no delta node needed at the beginning}
+ begin do_all_six(store_break_width);
+ end
+else begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
+ subtype(q):=0; {the |subtype| is not used}
+ do_all_six(new_delta_to_break_width);
+ link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
+ end
+
+@ When the following code is performed, we will have just inserted at
+least one active node before |r|, so |type(prev_r)<>delta_node|.
+
+@d new_delta_from_break_width(#)==@|mem[q+#].sc:=
+ cur_active_width[#]-break_width[#]
+
+@<Insert a delta node to prepare for the next active node@>=
+if r<>last_active then
+ begin q:=get_node(delta_node_size); link(q):=r; type(q):=delta_node;@/
+ subtype(q):=0; {the |subtype| is not used}
+ do_all_six(new_delta_from_break_width);
+ link(prev_r):=q; prev_prev_r:=prev_r; prev_r:=q;
+ end
+
+@ When we create an active node, we also create the corresponding
+passive node.
+
+@<Insert a new active node from |best_place[fit_class]| to |cur_p|@>=
+begin q:=get_node(passive_node_size);
+link(q):=passive; passive:=q; cur_break(q):=cur_p;
+@!stat incr(pass_number); serial(q):=pass_number;@+tats@;@/
+prev_break(q):=best_place[fit_class];@/
+q:=get_node(active_node_size); break_node(q):=passive;
+line_number(q):=best_pl_line[fit_class]+1;
+fitness(q):=fit_class; type(q):=break_type;
+total_demerits(q):=minimal_demerits[fit_class];
+link(q):=r; link(prev_r):=q; prev_r:=q;
+@!stat if tracing_paragraphs>0 then
+ @<Print a symbolic description of the new break node@>;
+tats@;@/
+end
+
+@ @<Print a symbolic description of the new break node@>=
+begin print_nl("@@@@"); print_int(serial(passive));
+@.\AT!\AT!@>
+print(": line "); print_int(line_number(q)-1);
+print_char("."); print_int(fit_class);
+if break_type=hyphenated then print_char("-");
+print(" t="); print_int(total_demerits(q));
+print(" -> @@@@");
+if prev_break(passive)=null then print_char("0")
+else print_int(serial(prev_break(passive)));
+end
+
+@ The length of lines depends on whether the user has specified
+\.{\\parshape} or \.{\\hangindent}. If |par_shape_ptr| is not null, it
+points to a $(2n+1)$-word record in |mem|, where the |info| in the first
+word contains the value of |n|, and the other $2n$ words contain the left
+margins and line lengths for the first |n| lines of the paragraph; the
+specifications for line |n| apply to all subsequent lines. If
+|par_shape_ptr=null|, the shape of the paragraph depends on the value of
+|n=hang_after|; if |n>=0|, hanging indentation takes place on lines |n+1|,
+|n+2|, \dots, otherwise it takes place on lines 1, \dots, $\vert
+n\vert$. When hanging indentation is active, the left margin is
+|hang_indent|, if |hang_indent>=0|, else it is 0; the line length is
+$|hsize|-\vert|hang_indent|\vert$. The normal setting is
+|par_shape_ptr=null|, |hang_after=1|, and |hang_indent=0|.
+Note that if |hang_indent=0|, the value of |hang_after| is irrelevant.
+@^length of lines@> @^hanging indentation@>
+
+@<Glob...@>=
+@!easy_line:halfword; {line numbers |>easy_line| are equivalent in break nodes}
+@!last_special_line:halfword; {line numbers |>last_special_line| all have
+ the same width}
+@!first_width:scaled; {the width of all lines |<=last_special_line|, if
+ no \.{\\parshape} has been specified}
+@!second_width:scaled; {the width of all lines |>last_special_line|}
+@!first_indent:scaled; {left margin to go with |first_width|}
+@!second_indent:scaled; {left margin to go with |second_width|}
+
+@ We compute the values of |easy_line| and the other local variables relating
+to line length when the |line_break| procedure is initializing itself.
+
+@<Get ready to start...@>=
+if par_shape_ptr=null then
+ if hang_indent=0 then
+ begin last_special_line:=0; second_width:=hsize;
+ second_indent:=0;
+ end
+ else @<Set line length parameters in preparation for hanging indentation@>
+else begin last_special_line:=info(par_shape_ptr)-1;
+ second_width:=mem[par_shape_ptr+2*(last_special_line+1)].sc;
+ second_indent:=mem[par_shape_ptr+2*last_special_line+1].sc;
+ end;
+if looseness=0 then easy_line:=last_special_line
+else easy_line:=max_halfword
+
+@ @<Set line length parameters in preparation for hanging indentation@>=
+begin last_special_line:=abs(hang_after);
+if hang_after<0 then
+ begin first_width:=hsize-abs(hang_indent);
+ if hang_indent>=0 then first_indent:=hang_indent
+ else first_indent:=0;
+ second_width:=hsize; second_indent:=0;
+ end
+else begin first_width:=hsize; first_indent:=0;
+ second_width:=hsize-abs(hang_indent);
+ if hang_indent>=0 then second_indent:=hang_indent
+ else second_indent:=0;
+ end;
+end
+
+@ When we come to the following code, we have just encountered the first
+active node~|r| whose |line_number| field contains |l|. Thus we want to
+compute the length of the $l\mskip1mu$th line of the current paragraph. Furthermore,
+we want to set |old_l| to the last number in the class of line numbers
+equivalent to~|l|.
+
+@<Compute the new line width@>=
+if l>easy_line then
+ begin line_width:=second_width; old_l:=max_halfword-1;
+ end
+else begin old_l:=l;
+ if l>last_special_line then line_width:=second_width
+ else if par_shape_ptr=null then line_width:=first_width
+ else line_width:=mem[par_shape_ptr+2*l@,].sc;
+ end
+
+@ The remaining part of |try_break| deals with the calculation of
+demerits for a break from |r| to |cur_p|.
+
+The first thing to do is calculate the badness, |b|. This value will always
+be between zero and |inf_bad+1|; the latter value occurs only in the
+case of lines from |r| to |cur_p| that cannot shrink enough to fit the necessary
+width. In such cases, node |r| will be deactivated.
+We also deactivate node~|r| when a break at~|cur_p| is forced, since future
+breaks must go through a forced break.
+
+@<Consider the demerits for a line from |r| to |cur_p|...@>=
+begin artificial_demerits:=false;@/
+@^inner loop@>
+shortfall:=line_width-cur_active_width[1]; {we're this much too short}
+if shortfall>0 then
+ @<Set the value of |b| to the badness for stretching the line,
+ and compute the corresponding |fit_class|@>
+else @<Set the value of |b| to the badness for shrinking the line,
+ and compute the corresponding |fit_class|@>;
+if (b>inf_bad)or(pi=eject_penalty) then
+ @<Prepare to deactivate node~|r|, and |goto deactivate| unless
+ there is a reason to consider lines of text from |r| to |cur_p|@>
+else begin prev_r:=r;
+ if b>threshold then goto continue;
+ node_r_stays_active:=true;
+ end;
+@<Record a new feasible break@>;
+if node_r_stays_active then goto continue; {|prev_r| has been set to |r|}
+deactivate: @<Deactivate node |r|@>;
+end
+
+@ When a line must stretch, the available stretchability can be found in the
+subarray |cur_active_width[2..5]|, in units of points, fil, fill, and filll.
+
+The present section is part of \TeX's inner loop, and it is most often performed
+when the badness is infinite; therefore it is worth while to make a quick
+test for large width excess and small stretchability, before calling the
+|badness| subroutine.
+@^inner loop@>
+
+@<Set the value of |b| to the badness for stretching...@>=
+if (cur_active_width[3]<>0)or(cur_active_width[4]<>0)or@|
+ (cur_active_width[5]<>0) then
+ begin b:=0; fit_class:=decent_fit; {infinite stretch}
+ end
+else begin if shortfall>7230584 then if cur_active_width[2]<1663497 then
+ begin b:=inf_bad; fit_class:=very_loose_fit; goto done1;
+ end;
+ b:=badness(shortfall,cur_active_width[2]);
+ if b>12 then
+ if b>99 then fit_class:=very_loose_fit
+ else fit_class:=loose_fit
+ else fit_class:=decent_fit;
+ done1:
+ end
+
+@ Shrinkability is never infinite in a paragraph;
+we can shrink the line from |r| to |cur_p| by at most |cur_active_width[6]|.
+
+@<Set the value of |b| to the badness for shrinking...@>=
+begin if -shortfall>cur_active_width[6] then b:=inf_bad+1
+else b:=badness(-shortfall,cur_active_width[6]);
+if b>12 then fit_class:=tight_fit@+else fit_class:=decent_fit;
+end
+
+@ During the final pass, we dare not lose all active nodes, lest we lose
+touch with the line breaks already found. The code shown here makes sure
+that such a catastrophe does not happen, by permitting overfull boxes as
+a last resort. This particular part of \TeX\ was a source of several subtle
+bugs before the correct program logic was finally discovered; readers
+who seek to ``improve'' \TeX\ should therefore think thrice before daring
+to make any changes here.
+@^overfull boxes@>
+
+@<Prepare to deactivate node~|r|, and |goto deactivate| unless...@>=
+begin if final_pass and (minimum_demerits=awful_bad) and@|
+ (link(r)=last_active) and
+ (prev_r=active) then
+ artificial_demerits:=true {set demerits zero, this break is forced}
+else if b>threshold then goto deactivate;
+node_r_stays_active:=false;
+end
+
+@ When we get to this part of the code, the line from |r| to |cur_p| is
+feasible, its badness is~|b|, and its fitness classification is |fit_class|.
+We don't want to make an active node for this break yet, but we will
+compute the total demerits and record them in the |minimal_demerits| array,
+if such a break is the current champion among all ways to get to |cur_p|
+in a given line-number class and fitness class.
+
+@<Record a new feasible break@>=
+if artificial_demerits then d:=0
+else @<Compute the demerits, |d|, from |r| to |cur_p|@>;
+@!stat if tracing_paragraphs>0 then
+ @<Print a symbolic description of this feasible break@>;
+tats@;@/
+d:=d+total_demerits(r); {this is the minimum total demerits
+ from the beginning to |cur_p| via |r|}
+if d<=minimal_demerits[fit_class] then
+ begin minimal_demerits[fit_class]:=d;
+ best_place[fit_class]:=break_node(r); best_pl_line[fit_class]:=l;
+ if d<minimum_demerits then minimum_demerits:=d;
+ end
+
+@ @<Print a symbolic description of this feasible break@>=
+begin if printed_node<>cur_p then
+ @<Print the list between |printed_node| and |cur_p|,
+ then set |printed_node:=cur_p|@>;
+print_nl("@@");
+@.\AT!@>
+if cur_p=null then print_esc("par")
+else if type(cur_p)<>glue_node then
+ begin if type(cur_p)=penalty_node then print_esc("penalty")
+ else if type(cur_p)=disc_node then print_esc("discretionary")
+ else if type(cur_p)=kern_node then print_esc("kern")
+ else print_esc("math");
+ end;
+print(" via @@@@");
+if break_node(r)=null then print_char("0")
+else print_int(serial(break_node(r)));
+print(" b=");
+if b>inf_bad then print_char("*")@+else print_int(b);
+@.*\relax@>
+print(" p="); print_int(pi); print(" d=");
+if artificial_demerits then print_char("*")@+else print_int(d);
+end
+
+@ @<Print the list between |printed_node| and |cur_p|...@>=
+begin print_nl("");
+if cur_p=null then short_display(link(printed_node))
+else begin save_link:=link(cur_p);
+ link(cur_p):=null; print_nl(""); short_display(link(printed_node));
+ link(cur_p):=save_link;
+ end;
+printed_node:=cur_p;
+end
+
+@ When the data for a discretionary break is being displayed, we will have
+printed the |pre_break| and |post_break| lists; we want to skip over the
+third list, so that the discretionary data will not appear twice. The
+following code is performed at the very end of |try_break|.
+
+@<Update the value of |printed_node|...@>=
+if cur_p=printed_node then if cur_p<>null then if type(cur_p)=disc_node then
+ begin t:=replace_count(cur_p);
+ while t>0 do
+ begin decr(t); printed_node:=link(printed_node);
+ end;
+ end
+
+@ @<Compute the demerits, |d|, from |r| to |cur_p|@>=
+begin d:=line_penalty+b;
+if abs(d)>=10000 then d:=100000000@+else d:=d*d;
+if pi<>0 then
+ if pi>0 then d:=d+pi*pi
+ else if pi>eject_penalty then d:=d-pi*pi;
+if (break_type=hyphenated)and(type(r)=hyphenated) then
+ if cur_p<>null then d:=d+double_hyphen_demerits
+ else d:=d+final_hyphen_demerits;
+if abs(fit_class-fitness(r))>1 then d:=d+adj_demerits;
+end
+
+@ When an active node disappears, we must delete an adjacent delta node if the
+active node was at the beginning or the end of the active list, or if it
+was surrounded by delta nodes. We also must preserve the property that
+|cur_active_width| represents the length of material from |link(prev_r)|
+to~|cur_p|.
+
+@d combine_two_deltas(#)==@|mem[prev_r+#].sc:=mem[prev_r+#].sc+mem[r+#].sc
+@d downdate_width(#)==@|cur_active_width[#]:=cur_active_width[#]-
+ mem[prev_r+#].sc
+
+@<Deactivate node |r|@>=
+link(prev_r):=link(r); free_node(r,active_node_size);
+if prev_r=active then @<Update the active widths, since the first active
+ node has been deleted@>
+else if type(prev_r)=delta_node then
+ begin r:=link(prev_r);
+ if r=last_active then
+ begin do_all_six(downdate_width);
+ link(prev_prev_r):=last_active;
+ free_node(prev_r,delta_node_size); prev_r:=prev_prev_r;
+ end
+ else if type(r)=delta_node then
+ begin do_all_six(update_width);
+ do_all_six(combine_two_deltas);
+ link(prev_r):=link(r); free_node(r,delta_node_size);
+ end;
+ end
+
+@ The following code uses the fact that |type(last_active)<>delta_node|. If the
+active list has just become empty, we do not need to update the
+|active_width| array, since it will be initialized when an active
+node is next inserted.
+
+@d update_active(#)==active_width[#]:=active_width[#]+mem[r+#].sc
+
+@<Update the active widths,...@>=
+begin r:=link(active);
+if type(r)=delta_node then
+ begin do_all_six(update_active);
+ do_all_six(copy_to_cur_active);
+ link(active):=link(r); free_node(r,delta_node_size);
+ end;
+end
+
+@* \[39] Breaking paragraphs into lines, continued.
+So far we have gotten a little way into the |line_break| routine, having
+covered its important |try_break| subroutine. Now let's consider the
+rest of the process.
+
+The main loop of |line_break| traverses the given hlist,
+starting at |link(temp_head)|, and calls |try_break| at each legal
+breakpoint. A variable called |auto_breaking| is set to true except
+within math formulas, since glue nodes are not legal breakpoints when
+they appear in formulas.
+
+The current node of interest in the hlist is pointed to by |cur_p|. Another
+variable, |prev_p|, is usually one step behind |cur_p|, but the real
+meaning of |prev_p| is this: If |type(cur_p)=glue_node| then |cur_p| is a legal
+breakpoint if and only if |auto_breaking| is true and |prev_p| does not
+point to a glue node, penalty node, explicit kern node, or math node.
+
+The following declarations provide for a few other local variables that are
+used in special calculations.
+
+@<Local variables for line breaking@>=
+@!auto_breaking:boolean; {is node |cur_p| outside a formula?}
+@!prev_p:pointer; {helps to determine when glue nodes are breakpoints}
+@!q,@!r,@!s,@!prev_s:pointer; {miscellaneous nodes of temporary interest}
+@!f:internal_font_number; {used when calculating character widths}
+
+@ The `\ignorespaces|loop|\unskip' in the following code is performed at most
+thrice per call of |line_break|, since it is actually a pass over the
+entire paragraph.
+
+@<Find optimal breakpoints@>=
+threshold:=pretolerance;
+if threshold>=0 then
+ begin @!stat if tracing_paragraphs>0 then
+ begin begin_diagnostic; print_nl("@@firstpass");@+end;@;@+tats@;@/
+ second_pass:=false; final_pass:=false;
+ end
+else begin threshold:=tolerance; second_pass:=true;
+ final_pass:=(emergency_stretch<=0);
+ @!stat if tracing_paragraphs>0 then begin_diagnostic;@+tats@;
+ end;
+loop@+ begin if threshold>inf_bad then threshold:=inf_bad;
+ if second_pass then @<Initialize for hyphenating a paragraph@>;
+ @<Create an active breakpoint representing the beginning of the paragraph@>;
+ cur_p:=link(temp_head); auto_breaking:=true;@/
+ prev_p:=cur_p; {glue at beginning is not a legal breakpoint}
+ while (cur_p<>null)and(link(active)<>last_active) do
+ @<Call |try_break| if |cur_p| is a legal breakpoint;
+ on the second pass, also try to hyphenate the next
+ word, if |cur_p| is a glue node;
+ then advance |cur_p| to the next node of the paragraph
+ that could possibly be a legal breakpoint@>;
+ if cur_p=null then
+ @<Try the final line break at the end of the paragraph,
+ and |goto done| if the desired breakpoints have been found@>;
+ @<Clean up the memory by removing the break nodes@>;
+ if not second_pass then
+ begin@!stat if tracing_paragraphs>0 then print_nl("@@secondpass");@;@+tats@/
+ threshold:=tolerance; second_pass:=true; final_pass:=(emergency_stretch<=0);
+ end {if at first you don't succeed, \dots}
+ else begin @!stat if tracing_paragraphs>0 then
+ print_nl("@@emergencypass");@;@+tats@/
+ background[2]:=background[2]+emergency_stretch; final_pass:=true;
+ end;
+ end;
+done: @!stat if tracing_paragraphs>0 then
+ begin end_diagnostic(true); normalize_selector;
+ end;@+tats@/
+
+@ The active node that represents the starting point does not need a
+corresponding passive node.
+
+@d store_background(#)==active_width[#]:=background[#]
+
+@<Create an active breakpoint representing the beginning of the paragraph@>=
+q:=get_node(active_node_size);
+type(q):=unhyphenated; fitness(q):=decent_fit;
+link(q):=last_active; break_node(q):=null;
+line_number(q):=prev_graf+1; total_demerits(q):=0; link(active):=q;
+do_all_six(store_background);@/
+passive:=null; printed_node:=temp_head; pass_number:=0;
+font_in_short_display:=null_font
+
+@ @<Clean...@>=
+q:=link(active);
+while q<>last_active do
+ begin cur_p:=link(q);
+ if type(q)=delta_node then free_node(q,delta_node_size)
+ else free_node(q,active_node_size);
+ q:=cur_p;
+ end;
+q:=passive;
+while q<>null do
+ begin cur_p:=link(q);
+ free_node(q,passive_node_size);
+ q:=cur_p;
+ end
+
+@ Here is the main switch in the |line_break| routine, where legal breaks
+are determined. As we move through the hlist, we need to keep the |active_width|
+array up to date, so that the badness of individual lines is readily calculated
+by |try_break|. It is convenient to use the short name |act_width| for
+the component of active width that represents real width as opposed to glue.
+
+@d act_width==active_width[1] {length from first active node to current node}
+@d kern_break==begin if not is_char_node(link(cur_p)) and auto_breaking then
+ if type(link(cur_p))=glue_node then try_break(0,unhyphenated);
+ act_width:=act_width+width(cur_p);
+ end
+
+@<Call |try_break| if |cur_p| is a legal breakpoint...@>=
+begin if is_char_node(cur_p) then
+ @<Advance \(c)|cur_p| to the node following the present
+ string of characters@>;
+case type(cur_p) of
+hlist_node,vlist_node,rule_node: act_width:=act_width+width(cur_p);
+whatsit_node: @<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>;
+glue_node: begin @<If node |cur_p| is a legal breakpoint, call |try_break|;
+ then update the active widths by including the glue in |glue_ptr(cur_p)|@>;
+ if second_pass and auto_breaking then
+ @<Try to hyphenate the following word@>;
+ end;
+kern_node: if subtype(cur_p)=explicit then kern_break
+ else act_width:=act_width+width(cur_p);
+ligature_node: begin f:=font(lig_char(cur_p));
+ act_width:=act_width+char_width(f)(char_info(f)(character(lig_char(cur_p))));
+ end;
+disc_node: @<Try to break after a discretionary fragment, then |goto done5|@>;
+math_node: begin auto_breaking:=(subtype(cur_p)=after); kern_break;
+ end;
+penalty_node: try_break(penalty(cur_p),unhyphenated);
+mark_node,ins_node,adjust_node: do_nothing;
+othercases confusion("paragraph")
+@:this can't happen paragraph}{\quad paragraph@>
+endcases;@/
+prev_p:=cur_p; cur_p:=link(cur_p);
+done5:end
+
+@ The code that passes over the characters of words in a paragraph is
+part of \TeX's inner loop, so it has been streamlined for speed. We use
+the fact that `\.{\\parfillskip}' glue appears at the end of each paragraph;
+it is therefore unnecessary to check if |link(cur_p)=null| when |cur_p| is a
+character node.
+@^inner loop@>
+
+@<Advance \(c)|cur_p| to the node following the present string...@>=
+begin prev_p:=cur_p;
+repeat f:=font(cur_p);
+act_width:=act_width+char_width(f)(char_info(f)(character(cur_p)));
+cur_p:=link(cur_p);
+until not is_char_node(cur_p);
+end
+
+@ When node |cur_p| is a glue node, we look at |prev_p| to see whether or not
+a breakpoint is legal at |cur_p|, as explained above.
+
+@<If node |cur_p| is a legal breakpoint, call...@>=
+if auto_breaking then
+ begin if is_char_node(prev_p) then try_break(0,unhyphenated)
+ else if precedes_break(prev_p) then try_break(0,unhyphenated)
+ else if (type(prev_p)=kern_node)and(subtype(prev_p)<>explicit) then
+ try_break(0,unhyphenated);
+ end;
+check_shrinkage(glue_ptr(cur_p)); q:=glue_ptr(cur_p);
+act_width:=act_width+width(q);@|
+active_width[2+stretch_order(q)]:=@|
+ active_width[2+stretch_order(q)]+stretch(q);@/
+active_width[6]:=active_width[6]+shrink(q)
+
+@ The following code knows that discretionary texts contain
+only character nodes, kern nodes, box nodes, rule nodes, and ligature nodes.
+
+@<Try to break after a discretionary fragment...@>=
+begin s:=pre_break(cur_p); disc_width:=0;
+if s=null then try_break(ex_hyphen_penalty,hyphenated)
+else begin repeat @<Add the width of node |s| to |disc_width|@>;
+ s:=link(s);
+ until s=null;
+ act_width:=act_width+disc_width;
+ try_break(hyphen_penalty,hyphenated);
+ act_width:=act_width-disc_width;
+ end;
+r:=replace_count(cur_p); s:=link(cur_p);
+while r>0 do
+ begin @<Add the width of node |s| to |act_width|@>;
+ decr(r); s:=link(s);
+ end;
+prev_p:=cur_p; cur_p:=s; goto done5;
+end
+
+@ @<Add the width of node |s| to |disc_width|@>=
+if is_char_node(s) then
+ begin f:=font(s);
+ disc_width:=disc_width+char_width(f)(char_info(f)(character(s)));
+ end
+else case type(s) of
+ ligature_node: begin f:=font(lig_char(s));
+ disc_width:=disc_width+
+ char_width(f)(char_info(f)(character(lig_char(s))));
+ end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ disc_width:=disc_width+width(s);
+ othercases confusion("disc3")
+@:this can't happen disc3}{\quad disc3@>
+ endcases
+
+@ @<Add the width of node |s| to |act_width|@>=
+if is_char_node(s) then
+ begin f:=font(s);
+ act_width:=act_width+char_width(f)(char_info(f)(character(s)));
+ end
+else case type(s) of
+ ligature_node: begin f:=font(lig_char(s));
+ act_width:=act_width+
+ char_width(f)(char_info(f)(character(lig_char(s))));
+ end;
+ hlist_node,vlist_node,rule_node,kern_node:
+ act_width:=act_width+width(s);
+ othercases confusion("disc4")
+@:this can't happen disc4}{\quad disc4@>
+ endcases
+
+@ The forced line break at the paragraph's end will reduce the list of
+breakpoints so that all active nodes represent breaks at |cur_p=null|.
+On the first pass, we insist on finding an active node that has the
+correct ``looseness.'' On the final pass, there will be at least one active
+node, and we will match the desired looseness as well as we can.
+
+The global variable |best_bet| will be set to the active node for the best
+way to break the paragraph, and a few other variables are used to
+help determine what is best.
+
+@<Glob...@>=
+@!best_bet:pointer; {use this passive node and its predecessors}
+@!fewest_demerits:integer; {the demerits associated with |best_bet|}
+@!best_line:halfword; {line number following the last line of the new paragraph}
+@!actual_looseness:integer; {the difference between |line_number(best_bet)|
+ and the optimum |best_line|}
+@!line_diff:integer; {the difference between the current line number and
+ the optimum |best_line|}
+
+@ @<Try the final line break at the end of the paragraph...@>=
+begin try_break(eject_penalty,hyphenated);
+if link(active)<>last_active then
+ begin @<Find an active node with fewest demerits@>;
+ if looseness=0 then goto done;
+ @<Find the best active node for the desired looseness@>;
+ if (actual_looseness=looseness)or final_pass then goto done;
+ end;
+end
+
+@ @<Find an active node...@>=
+r:=link(active); fewest_demerits:=awful_bad;
+repeat if type(r)<>delta_node then if total_demerits(r)<fewest_demerits then
+ begin fewest_demerits:=total_demerits(r); best_bet:=r;
+ end;
+r:=link(r);
+until r=last_active;
+best_line:=line_number(best_bet)
+
+@ The adjustment for a desired looseness is a slightly more complicated
+version of the loop just considered. Note that if a paragraph is broken
+into segments by displayed equations, each segment will be subject to the
+looseness calculation, independently of the other segments.
+
+@<Find the best active node...@>=
+begin r:=link(active); actual_looseness:=0;
+repeat if type(r)<>delta_node then
+ begin line_diff:=line_number(r)-best_line;
+ if ((line_diff<actual_looseness)and(looseness<=line_diff))or@|
+ ((line_diff>actual_looseness)and(looseness>=line_diff)) then
+ begin best_bet:=r; actual_looseness:=line_diff;
+ fewest_demerits:=total_demerits(r);
+ end
+ else if (line_diff=actual_looseness)and@|
+ (total_demerits(r)<fewest_demerits) then
+ begin best_bet:=r; fewest_demerits:=total_demerits(r);
+ end;
+ end;
+r:=link(r);
+until r=last_active;
+best_line:=line_number(best_bet);
+end
+
+@ Once the best sequence of breakpoints has been found (hurray), we call on the
+procedure |post_line_break| to finish the remainder of the work.
+(By introducing this subprocedure, we are able to keep |line_break|
+from getting extremely long.)
+
+@<Break the paragraph at the chosen...@>=
+post_line_break(final_widow_penalty)
+
+@ The total number of lines that will be set by |post_line_break|
+is |best_line-prev_graf-1|. The last breakpoint is specified by
+|break_node(best_bet)|, and this passive node points to the other breakpoints
+via the |prev_break| links. The finishing-up phase starts by linking the
+relevant passive nodes in forward order, changing |prev_break| to
+|next_break|. (The |next_break| fields actually reside in the same memory
+space as the |prev_break| fields did, but we give them a new name because
+of their new significance.) Then the lines are justified, one by one.
+
+@d next_break==prev_break {new name for |prev_break| after links are reversed}
+
+@<Declare subprocedures for |line_break|@>=
+procedure post_line_break(@!final_widow_penalty:integer);
+label done,done1;
+var q,@!r,@!s:pointer; {temporary registers for list manipulation}
+@!disc_break:boolean; {was the current break at a discretionary node?}
+@!post_disc_break:boolean; {and did it have a nonempty post-break part?}
+@!cur_width:scaled; {width of line number |cur_line|}
+@!cur_indent:scaled; {left margin of line number |cur_line|}
+@!t:quarterword; {used for replacement counts in discretionary nodes}
+@!pen:integer; {use when calculating penalties between lines}
+@!cur_line: halfword; {the current line number being justified}
+begin @<Reverse the links of the relevant passive nodes, setting |cur_p| to the
+ first breakpoint@>;
+cur_line:=prev_graf+1;
+repeat @<Justify the line ending at breakpoint |cur_p|, and append it to the
+ current vertical list, together with associated penalties and other
+ insertions@>;
+incr(cur_line); cur_p:=next_break(cur_p);
+if cur_p<>null then if not post_disc_break then
+ @<Prune unwanted nodes at the beginning of the next line@>;
+until cur_p=null;
+if (cur_line<>best_line)or(link(temp_head)<>null) then
+ confusion("line breaking");
+@:this can't happen line breaking}{\quad line breaking@>
+prev_graf:=best_line-1;
+end;
+
+@ The job of reversing links in a list is conveniently regarded as the job
+of taking items off one stack and putting them on another. In this case we
+take them off a stack pointed to by |q| and having |prev_break| fields;
+we put them on a stack pointed to by |cur_p| and having |next_break| fields.
+Node |r| is the passive node being moved from stack to stack.
+
+@<Reverse the links of the relevant passive nodes...@>=
+q:=break_node(best_bet); cur_p:=null;
+repeat r:=q; q:=prev_break(q); next_break(r):=cur_p; cur_p:=r;
+until q=null
+
+@ Glue and penalty and kern and math nodes are deleted at the beginning of
+a line, except in the anomalous case that the node to be deleted is actually
+one of the chosen breakpoints. Otherwise
+the pruning done here is designed to match
+the lookahead computation in |try_break|, where the |break_width| values
+are computed for non-discretionary breakpoints.
+
+@<Prune unwanted nodes at the beginning of the next line@>=
+begin r:=temp_head;
+loop@+ begin q:=link(r);
+ if q=cur_break(cur_p) then goto done1;
+ {|cur_break(cur_p)| is the next breakpoint}
+ {now |q| cannot be |null|}
+ if is_char_node(q) then goto done1;
+ if non_discardable(q) then goto done1;
+ if type(q)=kern_node then if subtype(q)<>explicit then goto done1;
+ r:=q; {now |type(q)=glue_node|, |kern_node|, |math_node| or |penalty_node|}
+ end;
+done1: if r<>temp_head then
+ begin link(r):=null; flush_node_list(link(temp_head));
+ link(temp_head):=q;
+ end;
+end
+
+@ The current line to be justified appears in a horizontal list starting
+at |link(temp_head)| and ending at |cur_break(cur_p)|. If |cur_break(cur_p)| is
+a glue node, we reset the glue to equal the |right_skip| glue; otherwise
+we append the |right_skip| glue at the right. If |cur_break(cur_p)| is a
+discretionary node, we modify the list so that the discretionary break
+is compulsory, and we set |disc_break| to |true|. We also append
+the |left_skip| glue at the left of the line, unless it is zero.
+
+@<Justify the line ending at breakpoint |cur_p|, and append it...@>=
+@<Modify the end of the line to reflect the nature of the break and to include
+ \.{\\rightskip}; also set the proper value of |disc_break|@>;
+@<Put the \(l)\.{\\leftskip} glue at the left and detach this line@>;
+@<Call the packaging subroutine, setting |just_box| to the justified box@>;
+@<Append the new box to the current vertical list, followed by the list of
+ special nodes taken out of the box by the packager@>;
+@<Append a penalty node, if a nonzero penalty is appropriate@>
+
+@ At the end of the following code, |q| will point to the final node on the
+list about to be justified.
+
+@<Modify the end of the line...@>=
+q:=cur_break(cur_p); disc_break:=false; post_disc_break:=false;
+if q<>null then {|q| cannot be a |char_node|}
+ if type(q)=glue_node then
+ begin delete_glue_ref(glue_ptr(q));
+ glue_ptr(q):=right_skip;
+ subtype(q):=right_skip_code+1; add_glue_ref(right_skip);
+ goto done;
+ end
+ else begin if type(q)=disc_node then
+ @<Change discretionary to compulsory and set
+ |disc_break:=true|@>
+ else if (type(q)=math_node)or(type(q)=kern_node) then width(q):=0;
+ end
+else begin q:=temp_head;
+ while link(q)<>null do q:=link(q);
+ end;
+@<Put the \(r)\.{\\rightskip} glue after node |q|@>;
+done:
+
+@ @<Change discretionary to compulsory...@>=
+begin t:=replace_count(q);
+@<Destroy the |t| nodes following |q|, and
+ make |r| point to the following node@>;
+if post_break(q)<>null then @<Transplant the post-break list@>;
+if pre_break(q)<>null then @<Transplant the pre-break list@>;
+link(q):=r; disc_break:=true;
+end
+
+@ @<Destroy the |t| nodes following |q|...@>=
+if t=0 then r:=link(q)
+else begin r:=q;
+ while t>1 do
+ begin r:=link(r); decr(t);
+ end;
+ s:=link(r);
+ r:=link(s); link(s):=null;
+ flush_node_list(link(q)); replace_count(q):=0;
+ end
+
+@ We move the post-break list from inside node |q| to the main list by
+re\-attaching it just before the present node |r|, then resetting |r|.
+
+@<Transplant the post-break list@>=
+begin s:=post_break(q);
+while link(s)<>null do s:=link(s);
+link(s):=r; r:=post_break(q); post_break(q):=null; post_disc_break:=true;
+end
+
+@ We move the pre-break list from inside node |q| to the main list by
+re\-attaching it just after the present node |q|, then resetting |q|.
+
+@<Transplant the pre-break list@>=
+begin s:=pre_break(q); link(q):=s;
+while link(s)<>null do s:=link(s);
+pre_break(q):=null; q:=s;
+end
+
+@ @<Put the \(r)\.{\\rightskip} glue after node |q|@>=
+r:=new_param_glue(right_skip_code); link(r):=link(q); link(q):=r; q:=r
+
+@ The following code begins with |q| at the end of the list to be
+justified. It ends with |q| at the beginning of that list, and with
+|link(temp_head)| pointing to the remainder of the paragraph, if any.
+
+@<Put the \(l)\.{\\leftskip} glue at the left...@>=
+r:=link(q); link(q):=null; q:=link(temp_head); link(temp_head):=r;
+if left_skip<>zero_glue then
+ begin r:=new_param_glue(left_skip_code);
+ link(r):=q; q:=r;
+ end
+
+@ @<Append the new box to the current vertical list...@>=
+append_to_vlist(just_box);
+if adjust_head<>adjust_tail then
+ begin link(tail):=link(adjust_head); tail:=adjust_tail;
+ end;
+adjust_tail:=null
+
+@ Now |q| points to the hlist that represents the current line of the
+paragraph. We need to compute the appropriate line width, pack the
+line into a box of this size, and shift the box by the appropriate
+amount of indentation.
+
+@<Call the packaging subroutine...@>=
+if cur_line>last_special_line then
+ begin cur_width:=second_width; cur_indent:=second_indent;
+ end
+else if par_shape_ptr=null then
+ begin cur_width:=first_width; cur_indent:=first_indent;
+ end
+else begin cur_width:=mem[par_shape_ptr+2*cur_line].sc;
+ cur_indent:=mem[par_shape_ptr+2*cur_line-1].sc;
+ end;
+adjust_tail:=adjust_head; just_box:=hpack(q,cur_width,exactly);
+shift_amount(just_box):=cur_indent
+
+@ Penalties between the lines of a paragraph come from club and widow lines,
+from the |inter_line_penalty| parameter, and from lines that end at
+discretionary breaks. Breaking between lines of a two-line paragraph gets
+both club-line and widow-line penalties. The local variable |pen| will
+be set to the sum of all relevant penalties for the current line, except
+that the final line is never penalized.
+
+@<Append a penalty node, if a nonzero penalty is appropriate@>=
+if cur_line+1<>best_line then
+ begin pen:=inter_line_penalty;
+ if cur_line=prev_graf+1 then pen:=pen+club_penalty;
+ if cur_line+2=best_line then pen:=pen+final_widow_penalty;
+ if disc_break then pen:=pen+broken_penalty;
+ if pen<>0 then
+ begin r:=new_penalty(pen);
+ link(tail):=r; tail:=r;
+ end;
+ end
+
+@* \[40] Pre-hyphenation.
+When the line-breaking routine is unable to find a feasible sequence of
+breakpoints, it makes a second pass over the paragraph, attempting to
+hyphenate the hyphenatable words. The goal of hyphenation is to insert
+discretionary material into the paragraph so that there are more
+potential places to break.
+
+The general rules for hyphenation are somewhat complex and technical,
+because we want to be able to hyphenate words that are preceded or
+followed by punctuation marks, and because we want the rules to work
+for languages other than English. We also must contend with the fact
+that hyphens might radically alter the ligature and kerning structure
+of a word.
+
+A sequence of characters will be considered for hyphenation only if it
+belongs to a ``potentially hyphenatable part'' of the current paragraph.
+This is a sequence of nodes $p_0p_1\ldots p_m$ where $p_0$ is a glue node,
+$p_1\ldots p_{m-1}$ are either character or ligature or whatsit or
+implicit kern nodes, and $p_m$ is a glue or penalty or insertion or adjust
+or mark or whatsit or explicit kern node. (Therefore hyphenation is
+disabled by boxes, math formulas, and discretionary nodes already inserted
+by the user.) The ligature nodes among $p_1\ldots p_{m-1}$ are effectively
+expanded into the original non-ligature characters; the kern nodes and
+whatsits are ignored. Each character |c| is now classified as either a
+nonletter (if |lc_code(c)=0|), a lowercase letter (if
+|lc_code(c)=c|), or an uppercase letter (otherwise); an uppercase letter
+is treated as if it were |lc_code(c)| for purposes of hyphenation. The
+characters generated by $p_1\ldots p_{m-1}$ may begin with nonletters; let
+$c_1$ be the first letter that is not in the middle of a ligature. Whatsit
+nodes preceding $c_1$ are ignored; a whatsit found after $c_1$ will be the
+terminating node $p_m$. All characters that do not have the same font as
+$c_1$ will be treated as nonletters. The |hyphen_char| for that font
+must be between 0 and 255, otherwise hyphenation will not be attempted.
+\TeX\ looks ahead for as many consecutive letters $c_1\ldots c_n$ as
+possible; however, |n| must be less than 64, so a character that would
+otherwise be $c_{64}$ is effectively not a letter. Furthermore $c_n$ must
+not be in the middle of a ligature. In this way we obtain a string of
+letters $c_1\ldots c_n$ that are generated by nodes $p_a\ldots p_b$, where
+|1<=a<=b+1<=m|. If |n>=l_hyf+r_hyf|, this string qualifies for hyphenation;
+however, |uc_hyph| must be positive, if $c_1$ is uppercase.
+
+The hyphenation process takes place in three stages. First, the candidate
+sequence $c_1\ldots c_n$ is found; then potential positions for hyphens
+are determined by referring to hyphenation tables; and finally, the nodes
+$p_a\ldots p_b$ are replaced by a new sequence of nodes that includes the
+discretionary breaks found.
+
+Fortunately, we do not have to do all this calculation very often, because
+of the way it has been taken out of \TeX's inner loop. For example, when
+the second edition of the author's 700-page book {\sl Seminumerical
+Algorithms} was typeset by \TeX, only about 1.2 hyphenations needed to be
+@^Knuth, Donald Ervin@>
+tried per paragraph, since the line breaking algorithm needed to use two
+passes on only about 5 per cent of the paragraphs.
+
+@<Initialize for hyphenating...@>=
+begin @!init if trie_not_ready then init_trie;@+tini@;@/
+cur_lang:=init_cur_lang; l_hyf:=init_l_hyf; r_hyf:=init_r_hyf;
+end
+
+@ The letters $c_1\ldots c_n$ that are candidates for hyphenation are placed
+into an array called |hc|; the number |n| is placed into |hn|; pointers to
+nodes $p_{a-1}$ and~$p_b$ in the description above are placed into variables
+|ha| and |hb|; and the font number is placed into |hf|.
+
+@<Glob...@>=
+@!hc:array[0..65] of 0..256; {word to be hyphenated}
+@!hn:small_number; {the number of positions occupied in |hc|}
+@!ha,@!hb:pointer; {nodes |ha..hb| should be replaced by the hyphenated result}
+@!hf:internal_font_number; {font number of the letters in |hc|}
+@!hu:array[0..63] of 0..256; {like |hc|, before conversion to lowercase}
+@!hyf_char:integer; {hyphen character of the relevant font}
+@!cur_lang,@!init_cur_lang:ASCII_code; {current hyphenation table of interest}
+@!l_hyf,@!r_hyf,@!init_l_hyf,@!init_r_hyf:integer; {limits on fragment sizes}
+@!hyf_bchar:halfword; {boundary character after $c_n$}
+
+@ Hyphenation routines need a few more local variables.
+
+@<Local variables for line...@>=
+@!j:small_number; {an index into |hc| or |hu|}
+@!c:0..255; {character being considered for hyphenation}
+
+@ When the following code is activated, the |line_break| procedure is in its
+second pass, and |cur_p| points to a glue node.
+
+@<Try to hyphenate...@>=
+begin prev_s:=cur_p; s:=link(prev_s);
+if s<>null then
+ begin @<Skip to node |ha|, or |goto done1| if no hyphenation
+ should be attempted@>;
+ if l_hyf+r_hyf>63 then goto done1;
+ @<Skip to node |hb|, putting letters into |hu| and |hc|@>;
+ @<Check that the nodes following |hb| permit hyphenation and that at least
+ |l_hyf+r_hyf| letters have been found, otherwise |goto done1|@>;
+ hyphenate;
+ end;
+done1: end
+
+@ @<Declare subprocedures for |line_break|@>=
+@t\4@>@<Declare the function called |reconstitute|@>
+procedure hyphenate;
+label common_ending,done,found,found1,found2,not_found,exit;
+var @<Local variables for hyphenation@>@;
+begin @<Find hyphen locations for the word in |hc|, or |return|@>;
+@<If no hyphens were found, |return|@>;
+@<Replace nodes |ha..hb| by a sequence of nodes that includes
+ the discretionary hyphens@>;
+exit:end;
+
+@ The first thing we need to do is find the node |ha| just before the
+first letter.
+
+@<Skip to node |ha|, or |goto done1|...@>=
+loop@+ begin if is_char_node(s) then
+ begin c:=qo(character(s)); hf:=font(s);
+ end
+ else if type(s)=ligature_node then
+ if lig_ptr(s)=null then goto continue
+ else begin q:=lig_ptr(s); c:=qo(character(q)); hf:=font(q);
+ end
+ else if (type(s)=kern_node)and(subtype(s)=normal) then goto continue
+ else if type(s)=whatsit_node then
+ begin @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>;
+ goto continue;
+ end
+ else goto done1;
+ if lc_code(c)<>0 then
+ if (lc_code(c)=c)or(uc_hyph>0) then goto done2
+ else goto done1;
+continue: prev_s:=s; s:=link(prev_s);
+ end;
+done2: hyf_char:=hyphen_char[hf];
+if hyf_char<0 then goto done1;
+if hyf_char>255 then goto done1;
+ha:=prev_s
+
+@ The word to be hyphenated is now moved to the |hu| and |hc| arrays.
+
+@<Skip to node |hb|, putting letters...@>=
+hn:=0;
+loop@+ begin if is_char_node(s) then
+ begin if font(s)<>hf then goto done3;
+ hyf_bchar:=character(s); c:=qo(hyf_bchar);
+ if lc_code(c)=0 then goto done3;
+ if hn=63 then goto done3;
+ hb:=s; incr(hn); hu[hn]:=c; hc[hn]:=lc_code(c); hyf_bchar:=non_char;
+ end
+ else if type(s)=ligature_node then
+ @<Move the characters of a ligature node to |hu| and |hc|;
+ but |goto done3| if they are not all letters@>
+ else if (type(s)=kern_node)and(subtype(s)=normal) then
+ begin hb:=s;
+ hyf_bchar:=font_bchar[hf];
+ end
+ else goto done3;
+ s:=link(s);
+ end;
+done3:
+
+@ We let |j| be the index of the character being stored when a ligature node
+is being expanded, since we do not want to advance |hn| until we are sure
+that the entire ligature consists of letters. Note that it is possible
+to get to |done3| with |hn=0| and |hb| not set to any value.
+
+@<Move the characters of a ligature node to |hu| and |hc|...@>=
+begin if font(lig_char(s))<>hf then goto done3;
+j:=hn; q:=lig_ptr(s);@+if q>null then hyf_bchar:=character(q);
+while q>null do
+ begin c:=qo(character(q));
+ if lc_code(c)=0 then goto done3;
+ if j=63 then goto done3;
+ incr(j); hu[j]:=c; hc[j]:=lc_code(c);@/
+ q:=link(q);
+ end;
+hb:=s; hn:=j;
+if odd(subtype(s)) then hyf_bchar:=font_bchar[hf]@+else hyf_bchar:=non_char;
+end
+
+@ @<Check that the nodes following |hb| permit hyphenation...@>=
+if hn<l_hyf+r_hyf then goto done1; {|l_hyf| and |r_hyf| are |>=1|}
+loop@+ begin if not(is_char_node(s)) then
+ case type(s) of
+ ligature_node: do_nothing;
+ kern_node: if subtype(s)<>normal then goto done4;
+ whatsit_node,glue_node,penalty_node,ins_node,adjust_node,mark_node:
+ goto done4;
+ othercases goto done1
+ endcases;
+ s:=link(s);
+ end;
+done4:
+
+@* \[41] Post-hyphenation.
+If a hyphen may be inserted between |hc[j]| and |hc[j+1]|, the hyphenation
+procedure will set |hyf[j]| to some small odd number. But before we look
+at \TeX's hyphenation procedure, which is independent of the rest of the
+line-breaking algorithm, let us consider what we will do with the hyphens
+it finds, since it is better to work on this part of the program before
+forgetting what |ha| and |hb|, etc., are all about.
+
+@<Glob...@>=
+@!hyf:array [0..64] of 0..9; {odd values indicate discretionary hyphens}
+@!init_list:pointer; {list of punctuation characters preceding the word}
+@!init_lig:boolean; {does |init_list| represent a ligature?}
+@!init_lft:boolean; {if so, did the ligature involve a left boundary?}
+
+@ @<Local variables for hyphenation@>=
+@!i,@!j,@!l:0..65; {indices into |hc| or |hu|}
+@!q,@!r,@!s:pointer; {temporary registers for list manipulation}
+@!bchar:halfword; {right boundary character of hyphenated word, or |non_char|}
+
+@ \TeX\ will never insert a hyphen that has fewer than
+\.{\\lefthyphenmin} letters before it or fewer than
+\.{\\righthyphenmin} after it; hence, a short word has
+comparatively little chance of being hyphenated. If no hyphens have
+been found, we can save time by not having to make any changes to the
+paragraph.
+
+@<If no hyphens were found, |return|@>=
+for j:=l_hyf to hn-r_hyf do if odd(hyf[j]) then goto found1;
+return;
+found1:
+
+@ If hyphens are in fact going to be inserted, \TeX\ first deletes the
+subsequence of nodes between |ha| and~|hb|. An attempt is made to
+preserve the effect that implicit boundary characters and punctuation marks
+had on ligatures inside the hyphenated word, by storing a left boundary or
+preceding character in |hu[0]| and by storing a possible right boundary
+in |bchar|. We set |j:=0| if |hu[0]| is to be part of the reconstruction;
+otherwise |j:=1|.
+The variable |s| will point to the tail of the current hlist, and
+|q| will point to the node following |hb|, so that
+things can be hooked up after we reconstitute the hyphenated word.
+
+@<Replace nodes |ha..hb| by a sequence of nodes...@>=
+q:=link(hb); link(hb):=null; r:=link(ha); link(ha):=null; bchar:=hyf_bchar;
+if is_char_node(ha) then
+ if font(ha)<>hf then goto found2
+ else begin init_list:=ha; init_lig:=false; hu[0]:=qo(character(ha));
+ end
+else if type(ha)=ligature_node then
+ if font(lig_char(ha))<>hf then goto found2
+ else begin init_list:=lig_ptr(ha); init_lig:=true; init_lft:=(subtype(ha)>1);
+ hu[0]:=qo(character(lig_char(ha)));
+ if init_list=null then if init_lft then
+ begin hu[0]:=256; init_lig:=false;
+ end; {in this case a ligature will be reconstructed from scratch}
+ free_node(ha,small_node_size);
+ end
+else begin {no punctuation found; look for left boundary}
+ if not is_char_node(r) then if type(r)=ligature_node then
+ if subtype(r)>1 then goto found2;
+ j:=1; s:=ha; init_list:=null; goto common_ending;
+ end;
+s:=cur_p; {we have |cur_p<>ha| because |type(cur_p)=glue_node|}
+while link(s)<>ha do s:=link(s);
+j:=0; goto common_ending;
+found2: s:=ha; j:=0; hu[0]:=256; init_lig:=false; init_list:=null;
+common_ending: flush_node_list(r);
+@<Reconstitute nodes for the hyphenated word, inserting discretionary hyphens@>;
+flush_list(init_list)
+
+@ We must now face the fact that the battle is not over, even though the
+{\def\!{\kern-1pt}%
+hyphens have been found: The process of reconstituting a word can be nontrivial
+because ligatures might change when a hyphen is present. {\sl The \TeX book\/}
+discusses the difficulties of the word ``difficult'', and
+the discretionary material surrounding a
+hyphen can be considerably more complex than that. Suppose
+\.{abcdef} is a word in a font for which the only ligatures are \.{b\!c},
+\.{c\!d}, \.{d\!e}, and \.{e\!f}. If this word permits hyphenation
+between \.b and \.c, the two patterns with and without hyphenation are
+$\.a\,\.b\,\.-\,\.{c\!d}\,\.{e\!f}$ and $\.a\,\.{b\!c}\,\.{d\!e}\,\.f$.
+Thus the insertion of a hyphen might cause effects to ripple arbitrarily
+far into the rest of the word. A further complication arises if additional
+hyphens appear together with such rippling, e.g., if the word in the
+example just given could also be hyphenated between \.c and \.d; \TeX\
+avoids this by simply ignoring the additional hyphens in such weird cases.}
+
+Still further complications arise in the presence of ligatures that do not
+delete the original characters. When punctuation precedes the word being
+hyphenated, \TeX's method is not perfect under all possible scenarios,
+because punctuation marks and letters can propagate information back and forth.
+For example, suppose the original pre-hyphenation pair
+\.{*a} changes to \.{*y} via a \.{\?=:} ligature, which changes to \.{xy}
+via a \.{=:\?} ligature; if $p_{a-1}=\.x$ and $p_a=\.y$, the reconstitution
+procedure isn't smart enough to obtain \.{xy} again. In such cases the
+font designer should include a ligature that goes from \.{xa} to \.{xy}.
+
+@ The processing is facilitated by a subroutine called |reconstitute|. Given
+a string of characters $x_j\ldots x_n$, there is a smallest index $m\ge j$
+such that the ``translation'' of $x_j\ldots x_n$ by ligatures and kerning
+has the form $y_1\ldots y_t$ followed by the translation of $x_{m+1}\ldots x_n$,
+where $y_1\ldots y_t$ is some nonempty sequence of character, ligature, and
+kern nodes. We call $x_j\ldots x_m$ a ``cut prefix'' of $x_j\ldots x_n$.
+For example, if $x_1x_2x_3=\.{fly}$, and if the font contains `fl' as a
+ligature and a kern between `fl' and `y', then $m=2$, $t=2$, and $y_1$ will
+be a ligature node for `fl' followed by an appropriate kern node~$y_2$.
+In the most common case, $x_j$~forms no ligature with $x_{j+1}$ and we
+simply have $m=j$, $y_1=x_j$. If $m<n$ we can repeat the procedure on
+$x_{m+1}\ldots x_n$ until the entire translation has been found.
+
+The |reconstitute| function returns the integer $m$ and puts the nodes
+$y_1\ldots y_t$ into a linked list starting at |link(hold_head)|,
+getting the input $x_j\ldots x_n$ from the |hu| array. If $x_j=256$,
+we consider $x_j$ to be an implicit left boundary character; in this
+case |j| must be strictly less than~|n|. There is a
+parameter |bchar|, which is either 256 or an implicit right boundary character
+assumed to be present just following~$x_n$. (The value |hu[n+1]| is never
+explicitly examined, but the algorithm imagines that |bchar| is there.)
+
+If there exists an index |k| in the range $j\le k\le m$ such that |hyf[k]|
+is odd and such that the result of |reconstitute| would have been different
+if $x_{k+1}$ had been |hchar|, then |reconstitute| sets |hyphen_passed|
+to the smallest such~|k|. Otherwise it sets |hyphen_passed| to zero.
+
+A special convention is used in the case |j=0|: Then we assume that the
+translation of |hu[0]| appears in a special list of charnodes starting at
+|init_list|; moreover, if |init_lig| is |true|, then |hu[0]| will be
+a ligature character, involving a left boundary if |init_lft| is |true|.
+This facility is provided for cases when a hyphenated
+word is preceded by punctuation (like single or double quotes) that might
+affect the translation of the beginning of the word.
+
+@<Glob...@>=
+@!hyphen_passed:small_number; {first hyphen in a ligature, if any}
+
+@ @<Declare the function called |reconstitute|@>=
+function reconstitute(@!j,@!n:small_number;@!bchar,@!hchar:halfword):
+ small_number;
+label continue,done;
+var @!p:pointer; {temporary register for list manipulation}
+@!t:pointer; {a node being appended to}
+@!q:four_quarters; {character information or a lig/kern instruction}
+@!cur_rh:halfword; {hyphen character for ligature testing}
+@!test_char:halfword; {hyphen or other character for ligature testing}
+@!w:scaled; {amount of kerning}
+@!k:font_index; {position of current lig/kern instruction}
+begin hyphen_passed:=0; t:=hold_head; w:=0; link(hold_head):=null;
+ {at this point |ligature_present=lft_hit=rt_hit=false|}
+@<Set up data structures with the cursor following position |j|@>;
+continue:@<If there's a ligature or kern at the cursor position, update the data
+ structures, possibly advancing~|j|; continue until the cursor moves@>;
+@<Append a ligature and/or kern to the translation;
+ |goto continue| if the stack of inserted ligatures is nonempty@>;
+reconstitute:=j;
+end;
+
+@ The reconstitution procedure shares many of the global data structures
+by which \TeX\ has processed the words before they were hyphenated.
+There is an implied ``cursor'' between characters |cur_l| and |cur_r|;
+these characters will be tested for possible ligature activity. If
+|ligature_present| then |cur_l| is a ligature character formed from the
+original characters following |cur_q| in the current translation list.
+There is a ``ligature stack'' between the cursor and character |j+1|,
+consisting of pseudo-ligature nodes linked together by their |link| fields.
+This stack is normally empty unless a ligature command has created a new
+character that will need to be processed later. A pseudo-ligature is
+a special node having a |character| field that represents a potential
+ligature and a |lig_ptr| field that points to a |char_node| or is |null|.
+We have
+$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack>null|;\cr
+ |qi(hu[j+1])|,&if |lig_stack=null| and |j<n|;\cr
+ bchar,&if |lig_stack=null| and |j=n|.\cr}$$
+
+@<Glob...@>=
+@!cur_l,@!cur_r:halfword; {characters before and after the cursor}
+@!cur_q:pointer; {where a ligature should be detached}
+@!lig_stack:pointer; {unfinished business to the right of the cursor}
+@!ligature_present:boolean; {should a ligature node be made for |cur_l|?}
+@!lft_hit,@!rt_hit:boolean; {did we hit a ligature with a boundary character?}
+
+@ @d append_charnode_to_t(#)== begin link(t):=get_avail; t:=link(t);
+ font(t):=hf; character(t):=#;
+ end
+@d set_cur_r==begin if j<n then cur_r:=qi(hu[j+1])@+else cur_r:=bchar;
+ if odd(hyf[j]) then cur_rh:=hchar@+else cur_rh:=non_char;
+ end
+
+@<Set up data structures with the cursor following position |j|@>=
+cur_l:=qi(hu[j]); cur_q:=t;
+if j=0 then
+ begin ligature_present:=init_lig; p:=init_list;
+ if ligature_present then lft_hit:=init_lft;
+ while p>null do
+ begin append_charnode_to_t(character(p)); p:=link(p);
+ end;
+ end
+else if cur_l<non_char then append_charnode_to_t(cur_l);
+lig_stack:=null; set_cur_r
+
+@ We may want to look at the lig/kern program twice, once for a hyphen
+and once for a normal letter. (The hyphen might appear after the letter
+in the program, so we'd better not try to look for both at once.)
+
+@<If there's a ligature or kern at the cursor position, update...@>=
+if cur_l=non_char then
+ begin k:=bchar_label[hf];
+ if k=non_address then goto done@+else q:=font_info[k].qqqq;
+ end
+else begin q:=char_info(hf)(cur_l);
+ if char_tag(q)<>lig_tag then goto done;
+ k:=lig_kern_start(hf)(q); q:=font_info[k].qqqq;
+ if skip_byte(q)>stop_flag then
+ begin k:=lig_kern_restart(hf)(q); q:=font_info[k].qqqq;
+ end;
+ end; {now |k| is the starting address of the lig/kern program}
+if cur_rh<non_char then test_char:=cur_rh@+else test_char:=cur_r;
+loop@+begin if next_char(q)=test_char then if skip_byte(q)<=stop_flag then
+ if cur_rh<non_char then
+ begin hyphen_passed:=j; hchar:=non_char; cur_rh:=non_char;
+ goto continue;
+ end
+ else begin if hchar<non_char then if odd(hyf[j]) then
+ begin hyphen_passed:=j; hchar:=non_char;
+ end;
+ if op_byte(q)<kern_flag then
+ @<Carry out a ligature replacement, updating the cursor structure
+ and possibly advancing~|j|; |goto continue| if the cursor doesn't
+ advance, otherwise |goto done|@>;
+ w:=char_kern(hf)(q); goto done; {this kern will be inserted below}
+ end;
+ if skip_byte(q)>=stop_flag then
+ if cur_rh=non_char then goto done
+ else begin cur_rh:=non_char; goto continue;
+ end;
+ k:=k+qo(skip_byte(q))+1; q:=font_info[k].qqqq;
+ end;
+done:
+
+@ @d wrap_lig(#)==if ligature_present then
+ begin p:=new_ligature(hf,cur_l,link(cur_q));
+ if lft_hit then
+ begin subtype(p):=2; lft_hit:=false;
+ end;
+ if # then if lig_stack=null then
+ begin incr(subtype(p)); rt_hit:=false;
+ end;
+ link(cur_q):=p; t:=p; ligature_present:=false;
+ end
+@d pop_lig_stack==begin if lig_ptr(lig_stack)>null then
+ begin link(t):=lig_ptr(lig_stack); {this is a charnode for |hu[j+1]|}
+ t:=link(t); incr(j);
+ end;
+ p:=lig_stack; lig_stack:=link(p); free_node(p,small_node_size);
+ if lig_stack=null then set_cur_r@+else cur_r:=character(lig_stack);
+ end {if |lig_stack| isn't |null| we have |cur_rh=non_char|}
+
+@<Append a ligature and/or kern to the translation...@>=
+wrap_lig(rt_hit);
+if w<>0 then
+ begin link(t):=new_kern(w); t:=link(t); w:=0;
+ end;
+if lig_stack>null then
+ begin cur_q:=t; cur_l:=character(lig_stack); ligature_present:=true;
+ pop_lig_stack; goto continue;
+ end
+
+@ @<Carry out a ligature replacement, updating the cursor structure...@>=
+begin if cur_l=non_char then lft_hit:=true;
+if j=n then if lig_stack=null then rt_hit:=true;
+check_interrupt; {allow a way out in case there's an infinite ligature loop}
+case op_byte(q) of
+qi(1),qi(5):begin cur_l:=rem_byte(q); {\.{=:\?}, \.{=:\?>}}
+ ligature_present:=true;
+ end;
+qi(2),qi(6):begin cur_r:=rem_byte(q); {\.{\?=:}, \.{\?=:>}}
+ if lig_stack>null then character(lig_stack):=cur_r
+ else begin lig_stack:=new_lig_item(cur_r);
+ if j=n then bchar:=non_char
+ else begin p:=get_avail; lig_ptr(lig_stack):=p;
+ character(p):=qi(hu[j+1]); font(p):=hf;
+ end;
+ end;
+ end;
+qi(3):begin cur_r:=rem_byte(q); {\.{\?=:\?}}
+ p:=lig_stack; lig_stack:=new_lig_item(cur_r); link(lig_stack):=p;
+ end;
+qi(7),qi(11):begin wrap_lig(false); {\.{\?=:\?>}, \.{\?=:\?>>}}
+ cur_q:=t; cur_l:=rem_byte(q); ligature_present:=true;
+ end;
+othercases begin cur_l:=rem_byte(q); ligature_present:=true; {\.{=:}}
+ if lig_stack>null then pop_lig_stack
+ else if j=n then goto done
+ else begin append_charnode_to_t(cur_r); incr(j); set_cur_r;
+ end;
+ end
+endcases;
+if op_byte(q)>qi(4) then if op_byte(q)<>qi(7) then goto done;
+goto continue;
+end
+
+@ Okay, we're ready to insert the potential hyphenations that were found.
+When the following program is executed, we want to append the word
+|hu[1..hn]| after node |ha|, and node |q| should be appended to the result.
+During this process, the variable |i| will be a temporary
+index into |hu|; the variable |j| will be an index to our current position
+in |hu|; the variable |l| will be the counterpart of |j|, in a discretionary
+branch; the variable |r| will point to new nodes being created; and
+we need a few new local variables:
+
+@<Local variables for hyph...@>=
+@!major_tail,@!minor_tail:pointer; {the end of lists in the main and
+ discretionary branches being reconstructed}
+@!c:ASCII_code; {character temporarily replaced by a hyphen}
+@!c_loc:0..63; {where that character came from}
+@!r_count:integer; {replacement count for discretionary}
+@!hyf_node:pointer; {the hyphen, if it exists}
+
+@ When the following code is performed, |hyf[0]| and |hyf[hn]| will be zero.
+
+@<Reconstitute nodes for the hyphenated word...@>=
+repeat l:=j; j:=reconstitute(j,hn,bchar,qi(hyf_char))+1;
+if hyphen_passed=0 then
+ begin link(s):=link(hold_head);
+ while link(s)>null do s:=link(s);
+ if odd(hyf[j-1]) then
+ begin l:=j; hyphen_passed:=j-1; link(hold_head):=null;
+ end;
+ end;
+if hyphen_passed>0 then
+ @<Create and append a discretionary node as an alternative to the
+ unhyphenated word, and continue to develop both branches until they
+ become equivalent@>;
+until j>hn;
+link(s):=q
+
+@ In this repeat loop we will insert another discretionary if |hyf[j-1]| is
+odd, when both branches of the previous discretionary end at position |j-1|.
+Strictly speaking, we aren't justified in doing this, because we don't know
+that a hyphen after |j-1| is truly independent of those branches. But in almost
+all applications we would rather not lose a potentially valuable hyphenation
+point. (Consider the word `difficult', where the letter `c' is in position |j|.)
+
+@d advance_major_tail==begin major_tail:=link(major_tail); incr(r_count);
+ end
+
+@<Create and append a discretionary node as an alternative...@>=
+repeat r:=get_node(small_node_size);
+link(r):=link(hold_head); type(r):=disc_node;
+major_tail:=r; r_count:=0;
+while link(major_tail)>null do advance_major_tail;
+i:=hyphen_passed; hyf[i]:=0;
+@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>;
+@<Put the \(c)characters |hu[i+1..@,]| into |post_break(r)|, appending to this
+ list and to |major_tail| until synchronization has been achieved@>;
+@<Move pointer |s| to the end of the current list, and set |replace_count(r)|
+ appropriately@>;
+hyphen_passed:=j-1; link(hold_head):=null;
+until not odd(hyf[j-1])
+
+@ The new hyphen might combine with the previous character via ligature
+or kern. At this point we have |l-1<=i<j| and |i<hn|.
+
+@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>=
+minor_tail:=null; pre_break(r):=null; hyf_node:=new_character(hf,hyf_char);
+if hyf_node<>null then
+ begin incr(i); c:=hu[i]; hu[i]:=hyf_char; free_avail(hyf_node);
+ end;
+while l<=i do
+ begin l:=reconstitute(l,i,font_bchar[hf],non_char)+1;
+ if link(hold_head)>null then
+ begin if minor_tail=null then pre_break(r):=link(hold_head)
+ else link(minor_tail):=link(hold_head);
+ minor_tail:=link(hold_head);
+ while link(minor_tail)>null do minor_tail:=link(minor_tail);
+ end;
+ end;
+if hyf_node<>null then
+ begin hu[i]:=c; {restore the character in the hyphen position}
+ l:=i; decr(i);
+ end
+
+@ The synchronization algorithm begins with |l=i+1<=j|.
+
+@<Put the \(c)characters |hu[i+1..@,]| into |post_break(r)|...@>=
+minor_tail:=null; post_break(r):=null; c_loc:=0;
+if bchar_label[hf]<>non_address then {put left boundary at beginning of new line}
+ begin decr(l); c:=hu[l]; c_loc:=l; hu[l]:=256;
+ end;
+while l<j do
+ begin repeat l:=reconstitute(l,hn,bchar,non_char)+1;
+ if c_loc>0 then
+ begin hu[c_loc]:=c; c_loc:=0;
+ end;
+ if link(hold_head)>null then
+ begin if minor_tail=null then post_break(r):=link(hold_head)
+ else link(minor_tail):=link(hold_head);
+ minor_tail:=link(hold_head);
+ while link(minor_tail)>null do minor_tail:=link(minor_tail);
+ end;
+ until l>=j;
+ while l>j do
+ @<Append characters of |hu[j..@,]| to |major_tail|, advancing~|j|@>;
+ end
+
+@ @<Append characters of |hu[j..@,]|...@>=
+begin j:=reconstitute(j,hn,bchar,non_char)+1;
+link(major_tail):=link(hold_head);
+while link(major_tail)>null do advance_major_tail;
+end
+
+@ Ligature insertion can cause a word to grow exponentially in size. Therefore
+we must test the size of |r_count| here, even though the hyphenated text
+was at most 63 characters long.
+
+@<Move pointer |s| to the end of the current list...@>=
+if r_count>127 then {we have to forget the discretionary hyphen}
+ begin link(s):=link(r); link(r):=null; flush_node_list(r);
+ end
+else begin link(s):=r; replace_count(r):=r_count;
+ end;
+s:=major_tail
+
+@* \[42] Hyphenation.
+When a word |hc[1..hn]| has been set up to contain a candidate for hyphenation,
+\TeX\ first looks to see if it is in the user's exception dictionary. If not,
+hyphens are inserted based on patterns that appear within the given word,
+using an algorithm due to Frank~M. Liang.
+@^Liang, Franklin Mark@>
+
+Let's consider Liang's method first, since it is much more interesting than the
+exception-lookup routine. The algorithm begins by setting |hyf[j]| to zero
+for all |j|, and invalid characters are inserted into |hc[0]|
+and |hc[hn+1]| to serve as delimiters. Then a reasonably fast method is
+used to see which of a given set of patterns occurs in the word
+|hc[0..(hn+1)]|. Each pattern $p_1\ldots p_k$ of length |k| has an associated
+sequence of |k+1| numbers $n_0\ldots n_k$; and if the pattern occurs in
+|hc[(j+1)..(j+k)]|, \TeX\ will set |hyf[j+i]:=@tmax@>(hyf[j+i],@t$n_i$@>)| for
+|0<=i<=k|. After this has been done for each pattern that occurs, a
+discretionary hyphen will be inserted between |hc[j]| and |hc[j+1]| when
+|hyf[j]| is odd, as we have already seen.
+
+The set of patterns $p_1\ldots p_k$ and associated numbers $n_0\ldots n_k$
+depends, of course, on the language whose words are being hyphenated, and
+on the degree of hyphenation that is desired. A method for finding
+appropriate |p|'s and |n|'s, from a given dictionary of words and acceptable
+hyphenations, is discussed in Liang's Ph.D. thesis (Stanford University,
+1983); \TeX\ simply starts with the patterns and works from there.
+
+@ The patterns are stored in a compact table that is also efficient for
+retrieval, using a variant of ``trie memory'' [cf.\ {\sl The Art of
+Computer Programming \bf3} (1973), 481--505]. We can find each pattern
+$p_1\ldots p_k$ by letting $z_0$ be one greater than the relevant language
+index and then, for |1<=i<=k|,
+setting |@t$z_i$@>:=trie_link@t$(z_{i-1})+p_i$@>|; the pattern will be
+identified by the number $z_k$. Since all the pattern information is
+packed together into a single |trie_link| array, it is necessary to
+prevent confusion between the data from inequivalent patterns, so another
+table is provided such that |trie_char@t$(z_i)=p_i$@>| for all |i|. There
+is also a table |trie_op|$(z_k)$ to identify the numbers $n_0\ldots n_k$
+associated with $p_1\ldots p_k$.
+
+Comparatively few different number sequences $n_0\ldots n_k$ actually occur,
+since most of the |n|'s are generally zero. Therefore the number sequences
+are encoded in such a way that |trie_op|$(z_k)$ is only one byte long.
+If |trie_op(@t$z_k$@>)<>min_quarterword|, when $p_1\ldots p_k$ has matched
+the letters in |hc[(l-k+1)..l@,]| of language |t|,
+we perform all of the required operations
+for this pattern by carrying out the following little program: Set
+|v:=trie_op(@t$z_k$@>)|. Then set |v:=v+op_start[t]|,
+|hyf[l-hyf_distance[v]]:=@tmax@>(hyf[l-hyf_distance[v]], hyf_num[v])|,
+and |v:=hyf_next[v]|; repeat, if necessary, until |v=min_quarterword|.
+
+@<Types...@>=
+@!trie_pointer=0..trie_size; {an index into |trie|}
+
+@ @d trie_link(#)==trie[#].rh {``downward'' link in a trie}
+@d trie_char(#)==trie[#].b1 {character matched at this trie location}
+@d trie_op(#)==trie[#].b0 {program for hyphenation at this trie location}
+
+@<Glob...@>=
+@!trie:array[trie_pointer] of two_halves; {|trie_link|, |trie_char|, |trie_op|}
+@!hyf_distance:array[1..trie_op_size] of small_number; {position |k-j| of $n_j$}
+@!hyf_num:array[1..trie_op_size] of small_number; {value of $n_j$}
+@!hyf_next:array[1..trie_op_size] of quarterword; {continuation code}
+@!op_start:array[ASCII_code] of 0..trie_op_size; {offset for current language}
+
+@ @<Local variables for hyph...@>=
+@!z:trie_pointer; {an index into |trie|}
+@!v:integer; {an index into |hyf_distance|, etc.}
+
+@ Assuming that these auxiliary tables have been set up properly, the
+hyphenation algorithm is quite short. In the following code we set |hc[hn+2]|
+to the impossible value 256, in order to guarantee that |hc[hn+3]| will
+never be fetched.
+
+@<Find hyphen locations for the word in |hc|...@>=
+for j:=0 to hn do hyf[j]:=0;
+@<Look for the word |hc[1..hn]| in the exception table, and |goto found| (with
+ |hyf| containing the hyphens) if an entry is found@>;
+if trie_char(cur_lang+1)<>qi(cur_lang) then return; {no patterns for |cur_lang|}
+hc[0]:=0; hc[hn+1]:=0; hc[hn+2]:=256; {insert delimiters}
+for j:=0 to hn-r_hyf+1 do
+ begin z:=trie_link(cur_lang+1)+hc[j]; l:=j;
+ while hc[l]=qo(trie_char(z)) do
+ begin if trie_op(z)<>min_quarterword then
+ @<Store \(m)maximum values in the |hyf| table@>;
+ incr(l); z:=trie_link(z)+hc[l];
+ end;
+ end;
+found: for j:=0 to l_hyf-1 do hyf[j]:=0;
+for j:=0 to r_hyf-1 do hyf[hn-j]:=0
+
+@ @<Store \(m)maximum values in the |hyf| table@>=
+begin v:=trie_op(z);
+repeat v:=v+op_start[cur_lang]; i:=l-hyf_distance[v];
+if hyf_num[v]>hyf[i] then hyf[i]:=hyf_num[v];
+v:=hyf_next[v];
+until v=min_quarterword;
+end
+
+@ The exception table that is built by \TeX's \.{\\hyphenation} primitive is
+organized as an ordered hash table [cf.\ Amble and Knuth, {\sl The Computer
+@^Amble, Ole@> @^Knuth, Donald Ervin@>
+Journal\/ \bf17} (1974), 135--142] using linear probing. If $\alpha$ and
+$\beta$ are words, we will say that $\alpha<\beta$ if $\vert\alpha\vert<
+\vert\beta\vert$ or if $\vert\alpha\vert=\vert\beta\vert$ and
+$\alpha$ is lexicographically smaller than $\beta$. (The notation $\vert
+\alpha\vert$ stands for the length of $\alpha$.) The idea of ordered hashing
+is to arrange the table so that a given word $\alpha$ can be sought by computing
+a hash address $h=h(\alpha)$ and then looking in table positions |h|, |h-1|,
+\dots, until encountering the first word $\L\alpha$. If this word is
+different from $\alpha$, we can conclude that $\alpha$ is not in the table.
+
+The words in the table point to lists in |mem| that specify hyphen positions
+in their |info| fields. The list for $c_1\ldots c_n$ contains the number |k| if
+the word $c_1\ldots c_n$ has a discretionary hyphen between $c_k$ and
+$c_{k+1}$.
+
+@<Types...@>=
+@!hyph_pointer=0..hyph_size; {an index into the ordered hash table}
+
+@ @<Glob...@>=
+@!hyph_word:array[hyph_pointer] of str_number; {exception words}
+@!hyph_list:array[hyph_pointer] of pointer; {lists of hyphen positions}
+@!hyph_count:hyph_pointer; {the number of words in the exception dictionary}
+
+@ @<Local variables for init...@>=
+@!z:hyph_pointer; {runs through the exception dictionary}
+
+@ @<Set init...@>=
+for z:=0 to hyph_size do
+ begin hyph_word[z]:=0; hyph_list[z]:=null;
+ end;
+hyph_count:=0;
+
+@ The algorithm for exception lookup is quite simple, as soon as we have
+a few more local variables to work with.
+
+@<Local variables for hyph...@>=
+@!h:hyph_pointer; {an index into |hyph_word| and |hyph_list|}
+@!k:str_number; {an index into |str_start|}
+@!u:pool_pointer; {an index into |str_pool|}
+
+@ First we compute the hash code |h|, then we search until we either
+find the word or we don't. Words from different languages are kept
+separate by appending the language code to the string.
+
+@<Look for the word |hc[1...@>=
+h:=hc[1]; incr(hn); hc[hn]:=cur_lang;
+for j:=2 to hn do h:=(h+h+hc[j]) mod hyph_size;
+loop@+ begin @<If the string |hyph_word[h]| is less than \(hc)|hc[1..hn]|,
+ |goto not_found|; but if the two strings are equal,
+ set |hyf| to the hyphen positions and |goto found|@>;
+ if h>0 then decr(h)@+else h:=hyph_size;
+ end;
+not_found: decr(hn)
+
+@ @<If the string |hyph_word[h]| is less than \(hc)...@>=
+k:=hyph_word[h]; if k=0 then goto not_found;
+if length(k)<hn then goto not_found;
+if length(k)=hn then
+ begin j:=1; u:=str_start[k];
+ repeat if so(str_pool[u])<hc[j] then goto not_found;
+ if so(str_pool[u])>hc[j] then goto done;
+ incr(j); incr(u);
+ until j>hn;
+ @<Insert hyphens as specified in |hyph_list[h]|@>;
+ decr(hn); goto found;
+ end;
+done:
+
+@ @<Insert hyphens as specified...@>=
+s:=hyph_list[h];
+while s<>null do
+ begin hyf[info(s)]:=1; s:=link(s);
+ end
+
+@ @<Search |hyph_list| for pointers to |p|@>=
+for q:=0 to hyph_size do
+ begin if hyph_list[q]=p then
+ begin print_nl("HYPH("); print_int(q); print_char(")");
+ end;
+ end
+
+@ We have now completed the hyphenation routine, so the |line_break| procedure
+is finished at last. Since the hyphenation exception table is fresh in our
+minds, it's a good time to deal with the routine that adds new entries to it.
+
+When \TeX\ has scanned `\.{\\hyphenation}', it calls on a procedure named
+|new_hyph_exceptions| to do the right thing.
+
+@d set_cur_lang==if language<=0 then cur_lang:=0
+ else if language>255 then cur_lang:=0
+ else cur_lang:=language
+
+@p procedure new_hyph_exceptions; {enters new exceptions}
+label reswitch, exit, found, not_found;
+var n:0..64; {length of current word; not always a |small_number|}
+@!j:0..64; {an index into |hc|}
+@!h:hyph_pointer; {an index into |hyph_word| and |hyph_list|}
+@!k:str_number; {an index into |str_start|}
+@!p:pointer; {head of a list of hyphen positions}
+@!q:pointer; {used when creating a new node for list |p|}
+@!s,@!t:str_number; {strings being compared or stored}
+@!u,@!v:pool_pointer; {indices into |str_pool|}
+begin scan_left_brace; {a left brace must follow \.{\\hyphenation}}
+set_cur_lang;
+@<Enter as many hyphenation exceptions as are listed,
+until coming to a right brace; then |return|@>;
+exit:end;
+
+@ @<Enter as many...@>=
+n:=0; p:=null;
+loop@+ begin get_x_token;
+ reswitch: case cur_cmd of
+ letter,other_char,char_given:@<Append a new letter or hyphen@>;
+ char_num: begin scan_char_num; cur_chr:=cur_val; cur_cmd:=char_given;
+ goto reswitch;
+ end;
+ spacer,right_brace: begin if n>1 then @<Enter a hyphenation exception@>;
+ if cur_cmd=right_brace then return;
+ n:=0; p:=null;
+ end;
+ othercases @<Give improper \.{\\hyphenation} error@>
+ endcases;
+ end
+
+@ @<Give improper \.{\\hyph...@>=
+begin print_err("Improper "); print_esc("hyphenation");
+@.Improper \\hyphenation...@>
+ print(" will be flushed");
+help2("Hyphenation exceptions must contain only letters")@/
+ ("and hyphens. But continue; I'll forgive and forget.");
+error;
+end
+
+@ @<Append a new letter or hyphen@>=
+if cur_chr="-" then @<Append the value |n| to list |p|@>
+else begin if lc_code(cur_chr)=0 then
+ begin print_err("Not a letter");
+@.Not a letter@>
+ help2("Letters in \hyphenation words must have \lccode>0.")@/
+ ("Proceed; I'll ignore the character I just read.");
+ error;
+ end
+ else if n<63 then
+ begin incr(n); hc[n]:=lc_code(cur_chr);
+ end;
+ end
+
+@ @<Append the value |n| to list |p|@>=
+begin if n<63 then
+ begin q:=get_avail; link(q):=p; info(q):=n; p:=q;
+ end;
+end
+
+@ @<Enter a hyphenation exception@>=
+begin incr(n); hc[n]:=cur_lang; str_room(n); h:=0;
+for j:=1 to n do
+ begin h:=(h+h+hc[j]) mod hyph_size;
+ append_char(hc[j]);
+ end;
+s:=make_string;
+@<Insert the \(p)pair |(s,p)| into the exception table@>;
+end
+
+@ @<Insert the \(p)pair |(s,p)|...@>=
+if hyph_count=hyph_size then overflow("exception dictionary",hyph_size);
+@:TeX capacity exceeded exception dictionary}{\quad exception dictionary@>
+incr(hyph_count);
+while hyph_word[h]<>0 do
+ begin @<If the string |hyph_word[h]| is less than \(or)or equal to
+ |s|, interchange |(hyph_word[h],hyph_list[h])| with |(s,p)|@>;
+ if h>0 then decr(h)@+else h:=hyph_size;
+ end;
+hyph_word[h]:=s; hyph_list[h]:=p
+
+@ @<If the string |hyph_word[h]| is less than \(or)...@>=
+k:=hyph_word[h];
+if length(k)<length(s) then goto found;
+if length(k)>length(s) then goto not_found;
+u:=str_start[k]; v:=str_start[s];
+repeat if str_pool[u]<str_pool[v] then goto found;
+if str_pool[u]>str_pool[v] then goto not_found;
+incr(u); incr(v);
+until u=str_start[k+1];
+found:q:=hyph_list[h]; hyph_list[h]:=p; p:=q;@/
+t:=hyph_word[h]; hyph_word[h]:=s; s:=t;
+not_found:
+
+@* \[43] Initializing the hyphenation tables.
+The trie for \TeX's hyphenation algorithm is built from a sequence of
+patterns following a \.{\\patterns} specification. Such a specification
+is allowed only in \.{INITEX}, since the extra memory for auxiliary tables
+and for the initialization program itself would only clutter up the
+production version of \TeX\ with a lot of deadwood.
+
+The first step is to build a trie that is linked, instead of packed
+into sequential storage, so that insertions are readily made.
+After all patterns have been processed, \.{INITEX}
+compresses the linked trie by identifying common subtries. Finally the
+trie is packed into the efficient sequential form that the hyphenation
+algorithm actually uses.
+
+@<Declare subprocedures for |line_break|@>=
+@!init @<Declare procedures for preprocessing hyphenation patterns@>@;
+tini
+
+@ Before we discuss trie building in detail, let's consider the simpler
+problem of creating the |hyf_distance|, |hyf_num|, and |hyf_next| arrays.
+
+Suppose, for example, that \TeX\ reads the pattern `\.{ab2cde1}'. This is
+a pattern of length 5, with $n_0\ldots n_5=0\,0\,2\,0\,0\,1$ in the
+notation above. We want the corresponding |trie_op| code |v| to have
+|hyf_distance[v]=3|, |hyf_num[v]=2|, and |hyf_next[v]=@t$v^\prime$@>|,
+where the auxiliary |trie_op| code $v^\prime$ has
+|hyf_distance[@t$v^\prime$@>]=0|, |hyf_num[@t$v^\prime$@>]=1|, and
+|hyf_next[@t$v^\prime$@>]=min_quarterword|.
+
+\TeX\ computes an appropriate value |v| with the |new_trie_op| subroutine
+below, by setting
+$$\hbox{|@t$v^\prime$@>:=new_trie_op(0,1,min_quarterword)|,\qquad
+|v:=new_trie_op(3,2,@t$v^\prime$@>)|.}$$
+This subroutine looks up its three
+parameters in a special hash table, assigning a new value only if these
+three have not appeared before for the current language.
+
+The hash table is called |trie_op_hash|, and the number of entries it contains
+is |trie_op_ptr|.
+
+@<Glob...@>=
+@!init@! trie_op_hash:array[-trie_op_size..trie_op_size] of 0..trie_op_size;
+ {trie op codes for quadruples}
+@!trie_used:array[ASCII_code] of quarterword;
+ {largest opcode used so far for this language}
+@!trie_op_lang:array[1..trie_op_size] of ASCII_code;
+ {language part of a hashed quadruple}
+@!trie_op_val:array[1..trie_op_size] of quarterword;
+ {opcode corresponding to a hashed quadruple}
+@!trie_op_ptr:0..trie_op_size; {number of stored ops so far}
+tini
+
+@ It's tempting to remove the |overflow| stops in the following procedure;
+|new_trie_op| could return |min_quarterword| (thereby simply ignoring
+part of a hyphenation pattern) instead of aborting the job. However, that would
+lead to different hyphenation results on different installations of \TeX\
+using the same patterns. The |overflow| stops are necessary for portability
+of patterns.
+
+@<Declare procedures for preprocessing hyph...@>=
+function new_trie_op(@!d,@!n:small_number;@!v:quarterword):quarterword;
+label exit;
+var h:-trie_op_size..trie_op_size; {trial hash location}
+@!u:quarterword; {trial op code}
+@!l:0..trie_op_size; {pointer to stored data}
+begin h:=abs(n+313*d+361*v+1009*cur_lang) mod (trie_op_size+trie_op_size)
+ - trie_op_size;
+loop@+ begin l:=trie_op_hash[h];
+ if l=0 then {empty position found for a new op}
+ begin if trie_op_ptr=trie_op_size then
+ overflow("pattern memory ops",trie_op_size);
+ u:=trie_used[cur_lang];
+ if u=max_quarterword then
+ overflow("pattern memory ops per language",
+ max_quarterword-min_quarterword);
+ incr(trie_op_ptr); incr(u); trie_used[cur_lang]:=u;
+ hyf_distance[trie_op_ptr]:=d;
+ hyf_num[trie_op_ptr]:=n; hyf_next[trie_op_ptr]:=v;
+ trie_op_lang[trie_op_ptr]:=cur_lang; trie_op_hash[h]:=trie_op_ptr;
+ trie_op_val[trie_op_ptr]:=u; new_trie_op:=u; return;
+ end;
+ if (hyf_distance[l]=d)and(hyf_num[l]=n)and(hyf_next[l]=v)
+ and(trie_op_lang[l]=cur_lang) then
+ begin new_trie_op:=trie_op_val[l]; return;
+ end;
+ if h>-trie_op_size then decr(h)@+else h:=trie_op_size;
+ end;
+exit:end;
+
+@ After |new_trie_op| has compressed the necessary opcode information,
+plenty of information is available to unscramble the data into the
+final form needed by our hyphenation algorithm.
+
+@<Sort \(t)the hyphenation op tables into proper order@>=
+op_start[0]:=-min_quarterword;
+for j:=1 to 255 do op_start[j]:=op_start[j-1]+qo(trie_used[j-1]);
+for j:=1 to trie_op_ptr do
+ trie_op_hash[j]:=op_start[trie_op_lang[j]]+trie_op_val[j]; {destination}
+for j:=1 to trie_op_ptr do while trie_op_hash[j]>j do
+ begin k:=trie_op_hash[j];@/
+ t:=hyf_distance[k]; hyf_distance[k]:=hyf_distance[j]; hyf_distance[j]:=t;@/
+ t:=hyf_num[k]; hyf_num[k]:=hyf_num[j]; hyf_num[j]:=t;@/
+ t:=hyf_next[k]; hyf_next[k]:=hyf_next[j]; hyf_next[j]:=t;@/
+ trie_op_hash[j]:=trie_op_hash[k]; trie_op_hash[k]:=k;
+ end
+
+@ Before we forget how to initialize the data structures that have been
+mentioned so far, let's write down the code that gets them started.
+
+@<Initialize table entries...@>=
+for k:=-trie_op_size to trie_op_size do trie_op_hash[k]:=0;
+for k:=0 to 255 do trie_used[k]:=min_quarterword;
+trie_op_ptr:=0;
+
+@ The linked trie that is used to preprocess hyphenation patterns appears
+in several global arrays. Each node represents an instruction of the form
+``if you see character |c|, then perform operation |o|, move to the
+next character, and go to node |l|; otherwise go to node |r|.''
+The four quantities |c|, |o|, |l|, and |r| are stored in four arrays
+|trie_c|, |trie_o|, |trie_l|, and |trie_r|. The root of the trie
+is |trie_l[0]|, and the number of nodes is |trie_ptr|. Null trie
+pointers are represented by zero. To initialize the trie, we simply
+set |trie_l[0]| and |trie_ptr| to zero. We also set |trie_c[0]| to some
+arbitrary value, since the algorithm may access it.
+
+The algorithms maintain the condition
+$$\hbox{|trie_c[trie_r[z]]>trie_c[z]|\qquad
+whenever |z<>0| and |trie_r[z]<>0|};$$ in other words, sibling nodes are
+ordered by their |c| fields.
+
+@d trie_root==trie_l[0] {root of the linked trie}
+
+@<Glob...@>=
+@!init @!trie_c:packed array[trie_pointer] of packed_ASCII_code;
+ {characters to match}
+@t\hskip10pt@>@!trie_o:packed array[trie_pointer] of quarterword;
+ {operations to perform}
+@t\hskip10pt@>@!trie_l:packed array[trie_pointer] of trie_pointer;
+ {left subtrie links}
+@t\hskip10pt@>@!trie_r:packed array[trie_pointer] of trie_pointer;
+ {right subtrie links}
+@t\hskip10pt@>@!trie_ptr:trie_pointer; {the number of nodes in the trie}
+@t\hskip10pt@>@!trie_hash:packed array[trie_pointer] of trie_pointer;
+ {used to identify equivalent subtries}
+tini
+
+@ Let us suppose that a linked trie has already been constructed.
+Experience shows that we can often reduce its size by recognizing common
+subtries; therefore another hash table is introduced for this purpose,
+somewhat similar to |trie_op_hash|. The new hash table will be
+initialized to zero.
+
+The function |trie_node(p)| returns |p| if |p| is distinct from other nodes
+that it has seen, otherwise it returns the number of the first equivalent
+node that it has seen.
+
+Notice that we might make subtries equivalent even if they correspond to
+patterns for different languages, in which the trie ops might mean quite
+different things. That's perfectly all right.
+
+@<Declare procedures for preprocessing hyph...@>=
+function trie_node(@!p:trie_pointer):trie_pointer; {converts
+ to a canonical form}
+label exit;
+var h:trie_pointer; {trial hash location}
+@!q:trie_pointer; {trial trie node}
+begin h:=abs(trie_c[p]+1009*trie_o[p]+@|
+ 2718*trie_l[p]+3142*trie_r[p]) mod trie_size;
+loop@+ begin q:=trie_hash[h];
+ if q=0 then
+ begin trie_hash[h]:=p; trie_node:=p; return;
+ end;
+ if (trie_c[q]=trie_c[p])and(trie_o[q]=trie_o[p])and@|
+ (trie_l[q]=trie_l[p])and(trie_r[q]=trie_r[p]) then
+ begin trie_node:=q; return;
+ end;
+ if h>0 then decr(h)@+else h:=trie_size;
+ end;
+exit:end;
+
+@ A neat recursive procedure is now able to compress a trie by
+traversing it and applying |trie_node| to its nodes in ``bottom up''
+fashion. We will compress the entire trie by clearing |trie_hash| to
+zero and then saying `|trie_root:=compress_trie(trie_root)|'.
+@^recursion@>
+
+@<Declare procedures for preprocessing hyph...@>=
+function compress_trie(@!p:trie_pointer):trie_pointer;
+begin if p=0 then compress_trie:=0
+else begin trie_l[p]:=compress_trie(trie_l[p]);
+ trie_r[p]:=compress_trie(trie_r[p]);
+ compress_trie:=trie_node(p);
+ end;
+end;
+
+@ The compressed trie will be packed into the |trie| array using a
+``top-down first-fit'' procedure. This is a little tricky, so the reader
+should pay close attention: The |trie_hash| array is cleared to zero
+again and renamed |trie_ref| for this phase of the operation; later on,
+|trie_ref[p]| will be nonzero only if the linked trie node |p| is the
+smallest character
+in a family and if the characters |c| of that family have been allocated to
+locations |trie_ref[p]+c| in the |trie| array. Locations of |trie| that
+are in use will have |trie_link=0|, while the unused holes in |trie|
+will be doubly linked with |trie_link| pointing to the next larger vacant
+location and |trie_back| pointing to the next smaller one. This double
+linking will have been carried out only as far as |trie_max|, where
+|trie_max| is the largest index of |trie| that will be needed.
+To save time at the low end of the trie, we maintain array entries
+|trie_min[c]| pointing to the smallest hole that is greater than~|c|.
+Another array |trie_taken| tells whether or not a given location is
+equal to |trie_ref[p]| for some |p|; this array is used to ensure that
+distinct nodes in the compressed trie will have distinct |trie_ref|
+entries.
+
+@d trie_ref==trie_hash {where linked trie families go into |trie|}
+@d trie_back(#)==trie[#].lh {backward links in |trie| holes}
+
+@<Glob...@>=
+@!init@!trie_taken:packed array[1..trie_size] of boolean;
+ {does a family start here?}
+@t\hskip10pt@>@!trie_min:array[ASCII_code] of trie_pointer;
+ {the first possible slot for each character}
+@t\hskip10pt@>@!trie_max:trie_pointer; {largest location used in |trie|}
+@t\hskip10pt@>@!trie_not_ready:boolean; {is the trie still in linked form?}
+tini
+
+@ Each time \.{\\patterns} appears, it contributes further patterns to
+the future trie, which will be built only when hyphenation is attempted or
+when a format file is dumped. The boolean variable |trie_not_ready|
+will change to |false| when the trie is compressed; this will disable
+further patterns.
+
+@<Initialize table entries...@>=
+trie_not_ready:=true; trie_root:=0; trie_c[0]:=si(0); trie_ptr:=0;
+
+@ Here is how the trie-compression data structures are initialized.
+If storage is tight, it would be possible to overlap |trie_op_hash|,
+|trie_op_lang|, and |trie_op_val| with |trie|, |trie_hash|, and |trie_taken|,
+because we finish with the former just before we need the latter.
+
+@<Get ready to compress the trie@>=
+@<Sort \(t)the hyphenation...@>;
+for p:=0 to trie_size do trie_hash[p]:=0;
+trie_root:=compress_trie(trie_root); {identify equivalent subtries}
+for p:=0 to trie_ptr do trie_ref[p]:=0;
+for p:=0 to 255 do trie_min[p]:=p+1;
+trie_link(0):=1; trie_max:=0
+
+@ The |first_fit| procedure finds the smallest hole |z| in |trie| such that
+a trie family starting at a given node |p| will fit into vacant positions
+starting at |z|. If |c=trie_c[p]|, this means that location |z-c| must
+not already be taken by some other family, and that |z-c+@t$c^\prime$@>|
+must be vacant for all characters $c^\prime$ in the family. The procedure
+sets |trie_ref[p]| to |z-c| when the first fit has been found.
+
+@<Declare procedures for preprocessing hyph...@>=
+procedure first_fit(@!p:trie_pointer); {packs a family into |trie|}
+label not_found,found;
+var h:trie_pointer; {candidate for |trie_ref[p]|}
+@!z:trie_pointer; {runs through holes}
+@!q:trie_pointer; {runs through the family starting at |p|}
+@!c:ASCII_code; {smallest character in the family}
+@!l,@!r:trie_pointer; {left and right neighbors}
+@!ll:1..256; {upper limit of |trie_min| updating}
+begin c:=so(trie_c[p]);
+z:=trie_min[c]; {get the first conceivably good hole}
+loop@+ begin h:=z-c;@/
+ @<Ensure that |trie_max>=h+256|@>;
+ if trie_taken[h] then goto not_found;
+ @<If all characters of the family fit relative to |h|, then
+ |goto found|,\30\ otherwise |goto not_found|@>;
+ not_found: z:=trie_link(z); {move to the next hole}
+ end;
+found: @<Pack the family into |trie| relative to |h|@>;
+end;
+
+@ By making sure that |trie_max| is at least |h+256|, we can be sure that
+|trie_max>z|, since |h=z-c|. It follows that location |trie_max| will
+never be occupied in |trie|, and we will have |trie_max>=trie_link(z)|.
+
+@<Ensure that |trie_max>=h+256|@>=
+if trie_max<h+256 then
+ begin if trie_size<=h+256 then overflow("pattern memory",trie_size);
+@:TeX capacity exceeded pattern memory}{\quad pattern memory@>
+ repeat incr(trie_max); trie_taken[trie_max]:=false;
+ trie_link(trie_max):=trie_max+1; trie_back(trie_max):=trie_max-1;
+ until trie_max=h+256;
+ end
+
+@ @<If all characters of the family fit relative to |h|...@>=
+q:=trie_r[p];
+while q>0 do
+ begin if trie_link(h+so(trie_c[q]))=0 then goto not_found;
+ q:=trie_r[q];
+ end;
+goto found
+
+@ @<Pack the family into |trie| relative to |h|@>=
+trie_taken[h]:=true; trie_ref[p]:=h; q:=p;
+repeat z:=h+so(trie_c[q]); l:=trie_back(z); r:=trie_link(z);
+trie_back(r):=l; trie_link(l):=r; trie_link(z):=0;
+if l<256 then
+ begin if z<256 then ll:=z @+else ll:=256;
+ repeat trie_min[l]:=r; incr(l);
+ until l=ll;
+ end;
+q:=trie_r[q];
+until q=0
+
+@ To pack the entire linked trie, we use the following recursive procedure.
+@^recursion@>
+
+@<Declare procedures for preprocessing hyph...@>=
+procedure trie_pack(@!p:trie_pointer); {pack subtries of a family}
+var q:trie_pointer; {a local variable that need not be saved on recursive calls}
+begin repeat q:=trie_l[p];
+if (q>0)and(trie_ref[q]=0) then
+ begin first_fit(q); trie_pack(q);
+ end;
+p:=trie_r[p];
+until p=0;
+end;
+
+@ When the whole trie has been allocated into the sequential table, we
+must go through it once again so that |trie| contains the correct
+information. Null pointers in the linked trie will be represented by the
+value~0, which properly implements an ``empty'' family.
+
+@<Move the data into |trie|@>=
+h.rh:=0; h.b0:=min_quarterword; h.b1:=min_quarterword; {|trie_link:=0|,
+ |trie_op:=min_quarterword|, |trie_char:=qi(0)|}
+if trie_root=0 then {no patterns were given}
+ begin for r:=0 to 256 do trie[r]:=h;
+ trie_max:=256;
+ end
+else begin trie_fix(trie_root); {this fixes the non-holes in |trie|}
+ r:=0; {now we will zero out all the holes}
+ repeat s:=trie_link(r); trie[r]:=h; r:=s;
+ until r>trie_max;
+ end;
+trie_char(0):=qi("?"); {make |trie_char(c)<>c| for all |c|}
+
+@ The fixing-up procedure is, of course, recursive. Since the linked trie
+usually has overlapping subtries, the same data may be moved several
+times; but that causes no harm, and at most as much work is done as it
+took to build the uncompressed trie.
+@^recursion@>
+
+@<Declare procedures for preprocessing hyph...@>=
+procedure trie_fix(@!p:trie_pointer); {moves |p| and its siblings into |trie|}
+var q:trie_pointer; {a local variable that need not be saved on recursive calls}
+@!c:ASCII_code; {another one that need not be saved}
+@!z:trie_pointer; {|trie| reference; this local variable must be saved}
+begin z:=trie_ref[p];
+repeat q:=trie_l[p]; c:=so(trie_c[p]);
+trie_link(z+c):=trie_ref[q]; trie_char(z+c):=qi(c); trie_op(z+c):=trie_o[p];
+if q>0 then trie_fix(q);
+p:=trie_r[p];
+until p=0;
+end;
+
+@ Now let's go back to the easier problem, of building the linked
+trie. When \.{INITEX} has scanned the `\.{\\patterns}' control
+sequence, it calls on |new_patterns| to do the right thing.
+
+@<Declare procedures for preprocessing hyph...@>=
+procedure new_patterns; {initializes the hyphenation pattern data}
+label done, done1;
+var k,@!l:0..64; {indices into |hc| and |hyf|;
+ not always in |small_number| range}
+@!digit_sensed:boolean; {should the next digit be treated as a letter?}
+@!v:quarterword; {trie op code}
+@!p,@!q:trie_pointer; {nodes of trie traversed during insertion}
+@!first_child:boolean; {is |p=trie_l[q]|?}
+@!c:ASCII_code; {character being inserted}
+begin if trie_not_ready then
+ begin set_cur_lang; scan_left_brace; {a left brace must follow \.{\\patterns}}
+ @<Enter all of the patterns into a linked trie, until coming to a right
+ brace@>;
+ end
+else begin print_err("Too late for "); print_esc("patterns");
+ help1("All patterns must be given before typesetting begins.");
+ error; link(garbage):=scan_toks(false,false); flush_list(def_ref);
+ end;
+end;
+
+@ Novices are not supposed to be using \.{\\patterns}, so the error
+messages are terse. (Note that all error messages appear in \TeX's string
+pool, even if they are used only by \.{INITEX}.)
+
+@<Enter all of the patterns into a linked trie...@>=
+k:=0; hyf[0]:=0; digit_sensed:=false;
+loop@+ begin get_x_token;
+ case cur_cmd of
+ letter,other_char:@<Append a new letter or a hyphen level@>;
+ spacer,right_brace: begin if k>0 then
+ @<Insert a new pattern into the linked trie@>;
+ if cur_cmd=right_brace then goto done;
+ k:=0; hyf[0]:=0; digit_sensed:=false;
+ end;
+ othercases begin print_err("Bad "); print_esc("patterns");
+@.Bad \\patterns@>
+ help1("(See Appendix H.)"); error;
+ end
+ endcases;
+ end;
+done:
+
+@ @<Append a new letter or a hyphen level@>=
+if digit_sensed or(cur_chr<"0")or(cur_chr>"9") then
+ begin if cur_chr="." then cur_chr:=0 {edge-of-word delimiter}
+ else begin cur_chr:=lc_code(cur_chr);
+ if cur_chr=0 then
+ begin print_err("Nonletter");
+@.Nonletter@>
+ help1("(See Appendix H.)"); error;
+ end;
+ end;
+ if k<63 then
+ begin incr(k); hc[k]:=cur_chr; hyf[k]:=0; digit_sensed:=false;
+ end;
+ end
+else if k<63 then
+ begin hyf[k]:=cur_chr-"0"; digit_sensed:=true;
+ end
+
+@ When the following code comes into play, the pattern $p_1\ldots p_k$
+appears in |hc[1..k]|, and the corresponding sequence of numbers $n_0\ldots
+n_k$ appears in |hyf[0..k]|.
+
+@<Insert a new pattern into the linked trie@>=
+begin @<Compute the trie op code, |v|, and set |l:=0|@>;
+q:=0; hc[0]:=cur_lang;
+while l<=k do
+ begin c:=hc[l]; incr(l); p:=trie_l[q]; first_child:=true;
+ while (p>0)and(c>so(trie_c[p])) do
+ begin q:=p; p:=trie_r[q]; first_child:=false;
+ end;
+ if (p=0)or(c<so(trie_c[p])) then
+ @<Insert a new trie node between |q| and |p|, and
+ make |p| point to it@>;
+ q:=p; {now node |q| represents $p_1\ldots p_{l-1}$}
+ end;
+if trie_o[q]<>min_quarterword then
+ begin print_err("Duplicate pattern");
+@.Duplicate pattern@>
+ help1("(See Appendix H.)"); error;
+ end;
+trie_o[q]:=v;
+end
+
+@ @<Insert a new trie node between |q| and |p|...@>=
+begin if trie_ptr=trie_size then overflow("pattern memory",trie_size);
+@:TeX capacity exceeded pattern memory}{\quad pattern memory@>
+incr(trie_ptr); trie_r[trie_ptr]:=p; p:=trie_ptr; trie_l[p]:=0;
+if first_child then trie_l[q]:=p@+else trie_r[q]:=p;
+trie_c[p]:=si(c); trie_o[p]:=min_quarterword;
+end
+
+@ @<Compute the trie op code, |v|...@>=
+if hc[1]=0 then hyf[0]:=0;
+if hc[k]=0 then hyf[k]:=0;
+l:=k; v:=min_quarterword;
+loop@+ begin if hyf[l]<>0 then v:=new_trie_op(k-l,hyf[l],v);
+ if l>0 then decr(l)@+else goto done1;
+ end;
+done1:
+
+@ Finally we put everything together: Here is how the trie gets to its
+final, efficient form.
+The following packing routine is rigged so that the root of the linked
+tree gets mapped into location 1 of |trie|, as required by the hyphenation
+algorithm. This happens because the first call of |first_fit| will
+``take'' location~1.
+
+@<Declare procedures for preprocessing hyphenation patterns@>=
+procedure init_trie;
+var @!p:trie_pointer; {pointer for initialization}
+@!j,@!k,@!t:integer; {all-purpose registers for initialization}
+@!r,@!s:trie_pointer; {used to clean up the packed |trie|}
+@!h:two_halves; {template used to zero out |trie|'s holes}
+begin @<Get ready to compress the trie@>;
+if trie_root<>0 then
+ begin first_fit(trie_root); trie_pack(trie_root);
+ end;
+@<Move the data into |trie|@>;
+trie_not_ready:=false;
+end;
+
+@* \[44] Breaking vertical lists into pages.
+The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation,
+is considerably simpler than |line_break| because it doesn't have to
+worry about hyphenation, and because its mission is to discover a single
+break instead of an optimum sequence of breakpoints. But before we get
+into the details of |vsplit|, we need to consider a few more basic things.
+
+@ A subroutine called |prune_page_top| takes a pointer to a vlist and
+returns a pointer to a modified vlist in which all glue, kern, and penalty nodes
+have been deleted before the first box or rule node. However, the first
+box or rule is actually preceded by a newly created glue node designed so that
+the topmost baseline will be at distance |split_top_skip| from the top,
+whenever this is possible without backspacing.
+
+In this routine and those that follow, we make use of the fact that a
+vertical list contains no character nodes, hence the |type| field exists
+for each node in the list.
+@^data structure assumptions@>
+
+@p function prune_page_top(@!p:pointer):pointer; {adjust top after page break}
+var prev_p:pointer; {lags one step behind |p|}
+@!q:pointer; {temporary variable for list manipulation}
+begin prev_p:=temp_head; link(temp_head):=p;
+while p<>null do
+ case type(p) of
+ hlist_node,vlist_node,rule_node:@<Insert glue for |split_top_skip|
+ and set~|p:=null|@>;
+ whatsit_node,mark_node,ins_node: begin prev_p:=p; p:=link(prev_p);
+ end;
+ glue_node,kern_node,penalty_node: begin q:=p; p:=link(q); link(q):=null;
+ link(prev_p):=p; flush_node_list(q);
+ end;
+ othercases confusion("pruning")
+@:this can't happen pruning}{\quad pruning@>
+ endcases;
+prune_page_top:=link(temp_head);
+end;
+
+@ @<Insert glue for |split_top_skip|...@>=
+begin q:=new_skip_param(split_top_skip_code); link(prev_p):=q; link(q):=p;
+ {now |temp_ptr=glue_ptr(q)|}
+if width(temp_ptr)>height(p) then width(temp_ptr):=width(temp_ptr)-height(p)
+else width(temp_ptr):=0;
+p:=null;
+end
+
+@ The next subroutine finds the best place to break a given vertical list
+so as to obtain a box of height~|h|, with maximum depth~|d|.
+A pointer to the beginning of the vertical list is given,
+and a pointer to the optimum breakpoint is returned. The list is effectively
+followed by a forced break, i.e., a penalty node with the |eject_penalty|;
+if the best break occurs at this artificial node, the value |null| is returned.
+
+An array of six |scaled| distances is used to keep track of the height
+from the beginning of the list to the current place, just as in |line_break|.
+In fact, we use one of the same arrays, only changing its name to reflect
+its new significance.
+
+@d active_height==active_width {new name for the six distance variables}
+@d cur_height==active_height[1] {the natural height}
+@d set_height_zero(#)==active_height[#]:=0 {initialize the height to zero}
+@#
+@d update_heights=90 {go here to record glue in the |active_height| table}
+
+@p function vert_break(@!p:pointer; @!h,@!d:scaled):pointer;
+ {finds optimum page break}
+label done,not_found,update_heights;
+var prev_p:pointer; {if |p| is a glue node, |type(prev_p)| determines
+ whether |p| is a legal breakpoint}
+@!q,@!r:pointer; {glue specifications}
+@!pi:integer; {penalty value}
+@!b:integer; {badness at a trial breakpoint}
+@!least_cost:integer; {the smallest badness plus penalties found so far}
+@!best_place:pointer; {the most recent break that leads to |least_cost|}
+@!prev_dp:scaled; {depth of previous box in the list}
+@!t:small_number; {|type| of the node following a kern}
+begin prev_p:=p; {an initial glue node is not a legal breakpoint}
+least_cost:=awful_bad; do_all_six(set_height_zero); prev_dp:=0;
+loop@+ begin @<If node |p| is a legal breakpoint, check if this break is
+ the best known, and |goto done| if |p| is null or
+ if the page-so-far is already too full to accept more stuff@>;
+ prev_p:=p; p:=link(prev_p);
+ end;
+done: vert_break:=best_place;
+end;
+
+@ A global variable |best_height_plus_depth| will be set to the natural size
+of the box that corresponds to the optimum breakpoint found by |vert_break|.
+(This value is used by the insertion-splitting algorithm of the page builder.)
+
+@<Glob...@>=
+@!best_height_plus_depth:scaled; {height of the best box, without stretching or
+ shrinking}
+
+@ A subtle point to be noted here is that the maximum depth~|d| might be
+negative, so |cur_height| and |prev_dp| might need to be corrected even
+after a glue or kern node.
+
+@<If node |p| is a legal breakpoint, check...@>=
+if p=null then pi:=eject_penalty
+else @<Use node |p| to update the current height and depth measurements;
+ if this node is not a legal breakpoint, |goto not_found|
+ or |update_heights|,
+ otherwise set |pi| to the associated penalty at the break@>;
+@<Check if node |p| is a new champion breakpoint; then \(go)|goto done|
+ if |p| is a forced break or if the page-so-far is already too full@>;
+if (type(p)<glue_node)or(type(p)>kern_node) then goto not_found;
+update_heights: @<Update the current height and depth measurements with
+ respect to a glue or kern node~|p|@>;
+not_found: if prev_dp>d then
+ begin cur_height:=cur_height+prev_dp-d;
+ prev_dp:=d;
+ end;
+
+@ @<Use node |p| to update the current height and depth measurements...@>=
+case type(p) of
+hlist_node,vlist_node,rule_node: begin@t@>@;@/
+ cur_height:=cur_height+prev_dp+height(p); prev_dp:=depth(p);
+ goto not_found;
+ end;
+whatsit_node:@<Process whatsit |p| in |vert_break| loop, |goto not_found|@>;
+glue_node: if precedes_break(prev_p) then pi:=0
+ else goto update_heights;
+kern_node: begin if link(p)=null then t:=penalty_node
+ else t:=type(link(p));
+ if t=glue_node then pi:=0@+else goto update_heights;
+ end;
+penalty_node: pi:=penalty(p);
+mark_node,ins_node: goto not_found;
+othercases confusion("vertbreak")
+@:this can't happen vertbreak}{\quad vertbreak@>
+endcases
+
+@ @d deplorable==100000 {more than |inf_bad|, but less than |awful_bad|}
+
+@<Check if node |p| is a new champion breakpoint; then \(go)...@>=
+if pi<inf_penalty then
+ begin @<Compute the badness, |b|, using |awful_bad|
+ if the box is too full@>;
+ if b<awful_bad then
+ if pi<=eject_penalty then b:=pi
+ else if b<inf_bad then b:=b+pi
+ else b:=deplorable;
+ if b<=least_cost then
+ begin best_place:=p; least_cost:=b;
+ best_height_plus_depth:=cur_height+prev_dp;
+ end;
+ if (b=awful_bad)or(pi<=eject_penalty) then goto done;
+ end
+
+@ @<Compute the badness, |b|, using |awful_bad| if the box is too full@>=
+if cur_height<h then
+ if (active_height[3]<>0) or (active_height[4]<>0) or
+ (active_height[5]<>0) then b:=0
+ else b:=badness(h-cur_height,active_height[2])
+else if cur_height-h>active_height[6] then b:=awful_bad
+else b:=badness(cur_height-h,active_height[6])
+
+@ Vertical lists that are subject to the |vert_break| procedure should not
+contain infinite shrinkability, since that would permit any amount of
+information to ``fit'' on one page.
+
+@<Update the current height and depth measurements with...@>=
+if type(p)=kern_node then q:=p
+else begin q:=glue_ptr(p);
+ active_height[2+stretch_order(q)]:=@|
+ active_height[2+stretch_order(q)]+stretch(q);@/
+ active_height[6]:=active_height[6]+shrink(q);
+ if (shrink_order(q)<>normal)and(shrink(q)<>0) then
+ begin@t@>@;@/
+ print_err("Infinite glue shrinkage found in box being split");@/
+@.Infinite glue shrinkage...@>
+ help4("The box you are \vsplitting contains some infinitely")@/
+ ("shrinkable glue, e.g., `\vss' or `\vskip 0pt minus 1fil'.")@/
+ ("Such glue doesn't belong there; but you can safely proceed,")@/
+ ("since the offensive shrinkability has been made finite.");
+ error; r:=new_spec(q); shrink_order(r):=normal; delete_glue_ref(q);
+ glue_ptr(p):=r; q:=r;
+ end;
+ end;
+cur_height:=cur_height+prev_dp+width(q); prev_dp:=0
+
+@ Now we are ready to consider |vsplit| itself. Most of
+its work is accomplished by the two subroutines that we have just considered.
+
+Given the number of a vlist box |n|, and given a desired page height |h|,
+the |vsplit| function finds the best initial segment of the vlist and
+returns a box for a page of height~|h|. The remainder of the vlist, if
+any, replaces the original box, after removing glue and penalties and
+adjusting for |split_top_skip|. Mark nodes in the split-off box are used to
+set the values of |split_first_mark| and |split_bot_mark|; we use the
+fact that |split_first_mark=null| if and only if |split_bot_mark=null|.
+
+The original box becomes ``void'' if and only if it has been entirely
+extracted. The extracted box is ``void'' if and only if the original
+box was void (or if it was, erroneously, an hlist box).
+
+@p function vsplit(@!n:eight_bits; @!h:scaled):pointer;
+ {extracts a page of height |h| from box |n|}
+label exit,done;
+var v:pointer; {the box to be split}
+p:pointer; {runs through the vlist}
+q:pointer; {points to where the break occurs}
+begin v:=box(n);
+if split_first_mark<>null then
+ begin delete_token_ref(split_first_mark); split_first_mark:=null;
+ delete_token_ref(split_bot_mark); split_bot_mark:=null;
+ end;
+@<Dispense with trivial cases of void or bad boxes@>;
+q:=vert_break(list_ptr(v),h,split_max_depth);
+@<Look at all the marks in nodes before the break, and set the final
+ link to |null| at the break@>;
+q:=prune_page_top(q); p:=list_ptr(v); free_node(v,box_node_size);
+if q=null then box(n):=null {the |eq_level| of the box stays the same}
+else box(n):=vpack(q,natural);
+vsplit:=vpackage(p,h,exactly,split_max_depth);
+exit: end;
+
+@ @<Dispense with trivial cases of void or bad boxes@>=
+if v=null then
+ begin vsplit:=null; return;
+ end;
+if type(v)<>vlist_node then
+ begin print_err(""); print_esc("vsplit"); print(" needs a ");
+ print_esc("vbox");
+@:vsplit_}{\.{\\vsplit needs a \\vbox}@>
+ help2("The box you are trying to split is an \hbox.")@/
+ ("I can't split such a box, so I'll leave it alone.");
+ error; vsplit:=null; return;
+ end
+
+@ It's possible that the box begins with a penalty node that is the
+``best'' break, so we must be careful to handle this special case correctly.
+
+@<Look at all the marks...@>=
+p:=list_ptr(v);
+if p=q then list_ptr(v):=null
+else loop@+begin if type(p)=mark_node then
+ if split_first_mark=null then
+ begin split_first_mark:=mark_ptr(p);
+ split_bot_mark:=split_first_mark;
+ token_ref_count(split_first_mark):=@|
+ token_ref_count(split_first_mark)+2;
+ end
+ else begin delete_token_ref(split_bot_mark);
+ split_bot_mark:=mark_ptr(p);
+ add_token_ref(split_bot_mark);
+ end;
+ if link(p)=q then
+ begin link(p):=null; goto done;
+ end;
+ p:=link(p);
+ end;
+done:
+
+@* \[45] The page builder.
+When \TeX\ appends new material to its main vlist in vertical mode, it uses
+a method something like |vsplit| to decide where a page ends, except that
+the calculations are done ``on line'' as new items come in.
+The main complication in this process is that insertions must be put
+into their boxes and removed from the vlist, in a more-or-less optimum manner.
+
+We shall use the term ``current page'' for that part of the main vlist that
+is being considered as a candidate for being broken off and sent to the
+user's output routine. The current page starts at |link(page_head)|, and
+it ends at |page_tail|. We have |page_head=page_tail| if this list is empty.
+@^current page@>
+
+Utter chaos would reign if the user kept changing page specifications
+while a page is being constructed, so the page builder keeps the pertinent
+specifications frozen as soon as the page receives its first box or
+insertion. The global variable |page_contents| is |empty| when the
+current page contains only mark nodes and content-less whatsit nodes; it
+is |inserts_only| if the page contains only insertion nodes in addition to
+marks and whatsits. Glue nodes, kern nodes, and penalty nodes are
+discarded until a box or rule node appears, at which time |page_contents|
+changes to |box_there|. As soon as |page_contents| becomes non-|empty|,
+the current |vsize| and |max_depth| are squirreled away into |page_goal|
+and |page_max_depth|; the latter values will be used until the page has
+been forwarded to the user's output routine. The \.{\\topskip} adjustment
+is made when |page_contents| changes to |box_there|.
+
+Although |page_goal| starts out equal to |vsize|, it is decreased by the
+scaled natural height-plus-depth of the insertions considered so far, and by
+the \.{\\skip} corrections for those insertions. Therefore it represents
+the size into which the non-inserted material should fit, assuming that
+all insertions in the current page have been made.
+
+The global variables |best_page_break| and |least_page_cost| correspond
+respectively to the local variables |best_place| and |least_cost| in the
+|vert_break| routine that we have already studied; i.e., they record the
+location and value of the best place currently known for breaking the
+current page. The value of |page_goal| at the time of the best break is
+stored in |best_size|.
+
+@d inserts_only=1
+ {|page_contents| when an insert node has been contributed, but no boxes}
+@d box_there=2 {|page_contents| when a box or rule has been contributed}
+
+@<Glob...@>=
+@!page_tail:pointer; {the final node on the current page}
+@!page_contents:empty..box_there; {what is on the current page so far?}
+@!page_max_depth:scaled; {maximum box depth on page being built}
+@!best_page_break:pointer; {break here to get the best page known so far}
+@!least_page_cost:integer; {the score for this currently best page}
+@!best_size:scaled; {its |page_goal|}
+
+@ The page builder has another data structure to keep track of insertions.
+This is a list of four-word nodes, starting and ending at |page_ins_head|.
+That is, the first element of the list is node |r@t$_1$@>=link(page_ins_head)|;
+node $r_j$ is followed by |r@t$_{j+1}$@>=link(r@t$_j$@>)|; and if there are
+|n| items we have |r@t$_{n+1}$@>=page_ins_head|. The |subtype| field of
+each node in this list refers to an insertion number; for example, `\.{\\insert
+250}' would correspond to a node whose |subtype| is |qi(250)|
+(the same as the |subtype| field of the relevant |ins_node|). These |subtype|
+fields are in increasing order, and |subtype(page_ins_head)=
+qi(255)|, so |page_ins_head| serves as a convenient sentinel
+at the end of the list. A record is present for each insertion number that
+appears in the current page.
+
+The |type| field in these nodes distinguishes two possibilities that
+might occur as we look ahead before deciding on the optimum page break.
+If |type(r)=inserting|, then |height(r)| contains the total of the
+height-plus-depth dimensions of the box and all its inserts seen so far.
+If |type(r)=split_up|, then no more insertions will be made into this box,
+because at least one previous insertion was too big to fit on the current
+page; |broken_ptr(r)| points to the node where that insertion will be
+split, if \TeX\ decides to split it, |broken_ins(r)| points to the
+insertion node that was tentatively split, and |height(r)| includes also the
+natural height plus depth of the part that would be split off.
+
+In both cases, |last_ins_ptr(r)| points to the last |ins_node|
+encountered for box |qo(subtype(r))| that would be at least partially
+inserted on the next page; and |best_ins_ptr(r)| points to the last
+such |ins_node| that should actually be inserted, to get the page with
+minimum badness among all page breaks considered so far. We have
+|best_ins_ptr(r)=null| if and only if no insertion for this box should
+be made to produce this optimum page.
+
+The data structure definitions here use the fact that the |@!height| field
+appears in the fourth word of a box node.
+@^data structure assumptions@>
+
+@d page_ins_node_size=4 {number of words for a page insertion node}
+@d inserting=0 {an insertion class that has not yet overflowed}
+@d split_up=1 {an overflowed insertion class}
+@d broken_ptr(#)==link(#+1)
+ {an insertion for this class will break here if anywhere}
+@d broken_ins(#)==info(#+1) {this insertion might break at |broken_ptr|}
+@d last_ins_ptr(#)==link(#+2) {the most recent insertion for this |subtype|}
+@d best_ins_ptr(#)==info(#+2) {the optimum most recent insertion}
+
+@<Initialize the special list heads...@>=
+subtype(page_ins_head):=qi(255);
+type(page_ins_head):=split_up; link(page_ins_head):=page_ins_head;
+
+@ An array |page_so_far| records the heights and depths of everything
+on the current page. This array contains six |scaled| numbers, like the
+similar arrays already considered in |line_break| and |vert_break|; and it
+also contains |page_goal| and |page_depth|, since these values are
+all accessible to the user via |set_page_dimen| commands. The
+value of |page_so_far[1]| is also called |page_total|. The stretch
+and shrink components of the \.{\\skip} corrections for each insertion are
+included in |page_so_far|, but the natural space components of these
+corrections are not, since they have been subtracted from |page_goal|.
+
+The variable |page_depth| records the depth of the current page; it has been
+adjusted so that it is at most |page_max_depth|. The variable
+|last_glue| points to the glue specification of the most recent node
+contributed from the contribution list, if this was a glue node; otherwise
+|last_glue=max_halfword|. (If the contribution list is nonempty,
+however, the value of |last_glue| is not necessarily accurate.)
+The variables |last_penalty| and |last_kern| are similar. And
+finally, |insert_penalties| holds the sum of the penalties associated with
+all split and floating insertions.
+
+@d page_goal==page_so_far[0] {desired height of information on page being built}
+@d page_total==page_so_far[1] {height of the current page}
+@d page_shrink==page_so_far[6] {shrinkability of the current page}
+@d page_depth==page_so_far[7] {depth of the current page}
+
+@<Glob...@>=
+@!page_so_far:array [0..7] of scaled; {height and glue of the current page}
+@!last_glue:pointer; {used to implement \.{\\lastskip}}
+@!last_penalty:integer; {used to implement \.{\\lastpenalty}}
+@!last_kern:scaled; {used to implement \.{\\lastkern}}
+@!insert_penalties:integer; {sum of the penalties for held-over insertions}
+
+@ @<Put each...@>=
+primitive("pagegoal",set_page_dimen,0);
+@!@:page_goal_}{\.{\\pagegoal} primitive@>
+primitive("pagetotal",set_page_dimen,1);
+@!@:page_total_}{\.{\\pagetotal} primitive@>
+primitive("pagestretch",set_page_dimen,2);
+@!@:page_stretch_}{\.{\\pagestretch} primitive@>
+primitive("pagefilstretch",set_page_dimen,3);
+@!@:page_fil_stretch_}{\.{\\pagefilstretch} primitive@>
+primitive("pagefillstretch",set_page_dimen,4);
+@!@:page_fill_stretch_}{\.{\\pagefillstretch} primitive@>
+primitive("pagefilllstretch",set_page_dimen,5);
+@!@:page_filll_stretch_}{\.{\\pagefilllstretch} primitive@>
+primitive("pageshrink",set_page_dimen,6);
+@!@:page_shrink_}{\.{\\pageshrink} primitive@>
+primitive("pagedepth",set_page_dimen,7);
+@!@:page_depth_}{\.{\\pagedepth} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+set_page_dimen: case chr_code of
+0: print_esc("pagegoal");
+1: print_esc("pagetotal");
+2: print_esc("pagestretch");
+3: print_esc("pagefilstretch");
+4: print_esc("pagefillstretch");
+5: print_esc("pagefilllstretch");
+6: print_esc("pageshrink");
+othercases print_esc("pagedepth")
+endcases;
+
+@ @d print_plus_end(#)==print(#);@+end
+@d print_plus(#)==if page_so_far[#]<>0 then
+ begin print(" plus "); print_scaled(page_so_far[#]); print_plus_end
+
+@p procedure print_totals;
+begin print_scaled(page_total);
+print_plus(2)("");
+print_plus(3)("fil");
+print_plus(4)("fill");
+print_plus(5)("filll");
+if page_shrink<>0 then
+ begin print(" minus "); print_scaled(page_shrink);
+ end;
+end;
+
+@ @<Show the status of the current page@>=
+if page_head<>page_tail then
+ begin print_nl("### current page:");
+ if output_active then print(" (held over for next output)");
+@.held over for next output@>
+ show_box(link(page_head));
+ if page_contents>empty then
+ begin print_nl("total height "); print_totals;
+@:total_height}{\.{total height}@>
+ print_nl(" goal height "); print_scaled(page_goal);
+@.goal height@>
+ r:=link(page_ins_head);
+ while r<>page_ins_head do
+ begin print_ln; print_esc("insert"); t:=qo(subtype(r));
+ print_int(t); print(" adds ");
+ if count(t)=1000 then t:=height(r)
+ else t:=x_over_n(height(r),1000)*count(t);
+ print_scaled(t);
+ if type(r)=split_up then
+ begin q:=page_head; t:=0;
+ repeat q:=link(q);
+ if (type(q)=ins_node)and(subtype(q)=subtype(r)) then incr(t);
+ until q=broken_ins(r);
+ print(", #"); print_int(t); print(" might split");
+ end;
+ r:=link(r);
+ end;
+ end;
+ end
+
+@ Here is a procedure that is called when the |page_contents| is changing
+from |empty| to |inserts_only| or |box_there|.
+
+@d set_page_so_far_zero(#)==page_so_far[#]:=0
+
+@p procedure freeze_page_specs(@!s:small_number);
+begin page_contents:=s;
+page_goal:=vsize; page_max_depth:=max_depth;
+page_depth:=0; do_all_six(set_page_so_far_zero);
+least_page_cost:=awful_bad;
+@!stat if tracing_pages>0 then
+ begin begin_diagnostic;
+ print_nl("%% goal height="); print_scaled(page_goal);
+@.goal height@>
+ print(", max depth="); print_scaled(page_max_depth);
+ end_diagnostic(false);
+ end;@;@+tats@;@/
+end;
+
+@ Pages are built by appending nodes to the current list in \TeX's
+vertical mode, which is at the outermost level of the semantic nest. This
+vlist is split into two parts; the ``current page'' that we have been
+talking so much about already, and the ``contribution list'' that receives
+new nodes as they are created. The current page contains everything that
+the page builder has accounted for in its data structures, as described
+above, while the contribution list contains other things that have been
+generated by other parts of \TeX\ but have not yet been
+seen by the page builder.
+The contribution list starts at |link(contrib_head)|, and it ends at the
+current node in \TeX's vertical mode.
+
+When \TeX\ has appended new material in vertical mode, it calls the procedure
+|build_page|, which tries to catch up by moving nodes from the contribution
+list to the current page. This procedure will succeed in its goal of
+emptying the contribution list, unless a page break is discovered, i.e.,
+unless the current page has grown to the point where the optimum next
+page break has been determined. In the latter case, the nodes after the
+optimum break will go back onto the contribution list, and control will
+effectively pass to the user's output routine.
+
+We make |type(page_head)=glue_node|, so that an initial glue node on
+the current page will not be considered a valid breakpoint.
+
+@<Initialize the special list...@>=
+type(page_head):=glue_node; subtype(page_head):=normal;
+
+@ The global variable |output_active| is true during the time the
+user's output routine is driving \TeX.
+
+@<Glob...@>=
+@!output_active:boolean; {are we in the midst of an output routine?}
+
+@ @<Set init...@>=
+output_active:=false; insert_penalties:=0;
+
+@ The page builder is ready to start a fresh page if we initialize
+the following state variables. (However, the page insertion list is initialized
+elsewhere.)
+
+@<Start a new current page@>=
+page_contents:=empty; page_tail:=page_head; link(page_head):=null;@/
+last_glue:=max_halfword; last_penalty:=0; last_kern:=0;
+page_depth:=0; page_max_depth:=0
+
+@ At certain times box 255 is supposed to be void (i.e., |null|),
+or an insertion box is supposed to be ready to accept a vertical list.
+If not, an error message is printed, and the following subroutine
+flushes the unwanted contents, reporting them to the user.
+
+@p procedure box_error(@!n:eight_bits);
+begin error; begin_diagnostic;
+print_nl("The following box has been deleted:");
+@.The following...deleted@>
+show_box(box(n)); end_diagnostic(true);
+flush_node_list(box(n)); box(n):=null;
+end;
+
+@ The following procedure guarantees that a given box register
+does not contain an \.{\\hbox}.
+
+@p procedure ensure_vbox(@!n:eight_bits);
+var p:pointer; {the box register contents}
+begin p:=box(n);
+if p<>null then if type(p)=hlist_node then
+ begin print_err("Insertions can only be added to a vbox");
+@.Insertions can only...@>
+ help3("Tut tut: You're trying to \insert into a")@/
+ ("\box register that now contains an \hbox.")@/
+ ("Proceed, and I'll discard its present contents.");
+ box_error(n);
+ end;
+end;
+
+@ \TeX\ is not always in vertical mode at the time |build_page|
+is called; the current mode reflects what \TeX\ should return to, after
+the contribution list has been emptied. A call on |build_page| should
+be immediately followed by `|goto big_switch|', which is \TeX's central
+control point.
+
+@d contribute=80 {go here to link a node into the current page}
+
+@p @t\4@>@<Declare the procedure called |fire_up|@>@;@/
+procedure build_page; {append contributions to the current page}
+label exit,done,done1,continue,contribute,update_heights;
+var p:pointer; {the node being appended}
+@!q,@!r:pointer; {nodes being examined}
+@!b,@!c:integer; {badness and cost of current page}
+@!pi:integer; {penalty to be added to the badness}
+@!n:min_quarterword..255; {insertion box number}
+@!delta,@!h,@!w:scaled; {sizes used for insertion calculations}
+begin if (link(contrib_head)=null)or output_active then return;
+repeat continue: p:=link(contrib_head);@/
+@<Update the values of |last_glue|, |last_penalty|, and |last_kern|@>;
+@<Move node |p| to the current page; if it is time for a page break,
+ put the nodes following the break back onto the contribution list,
+ and |return| to the user's output routine if there is one@>;
+until link(contrib_head)=null;
+@<Make the contribution list empty by setting its tail to |contrib_head|@>;
+exit:end;
+
+@ @d contrib_tail==nest[0].tail_field {tail of the contribution list}
+
+@<Make the contribution list empty...@>=
+if nest_ptr=0 then tail:=contrib_head {vertical mode}
+else contrib_tail:=contrib_head {other modes}
+
+@ @<Update the values of |last_glue|...@>=
+if last_glue<>max_halfword then delete_glue_ref(last_glue);
+last_penalty:=0; last_kern:=0;
+if type(p)=glue_node then
+ begin last_glue:=glue_ptr(p); add_glue_ref(last_glue);
+ end
+else begin last_glue:=max_halfword;
+ if type(p)=penalty_node then last_penalty:=penalty(p)
+ else if type(p)=kern_node then last_kern:=width(p);
+ end
+
+@ The code here is an example of a many-way switch into routines that
+merge together in different places. Some people call this unstructured
+programming, but the author doesn't see much wrong with it, as long as
+@^Knuth, Donald Ervin@>
+the various labels have a well-understood meaning.
+
+@<Move node |p| to the current page; ...@>=
+@<If the current page is empty and node |p| is to be deleted, |goto done1|;
+ otherwise use node |p| to update the state of the current page;
+ if this node is an insertion, |goto contribute|; otherwise if this node
+ is not a legal breakpoint, |goto contribute| or |update_heights|;
+ otherwise set |pi| to the penalty associated with this breakpoint@>;
+@<Check if node |p| is a new champion breakpoint; then \(if)if it is time for
+ a page break, prepare for output, and either fire up the user's
+ output routine and |return| or ship out the page and |goto done|@>;
+if (type(p)<glue_node)or(type(p)>kern_node) then goto contribute;
+update_heights:@<Update the current page measurements with respect to the
+ glue or kern specified by node~|p|@>;
+contribute: @<Make sure that |page_max_depth| is not exceeded@>;
+@<Link node |p| into the current page and |goto done|@>;
+done1:@<Recycle node |p|@>;
+done:
+
+@ @<Link node |p| into the current page and |goto done|@>=
+link(page_tail):=p; page_tail:=p;
+link(contrib_head):=link(p); link(p):=null; goto done
+
+@ @<Recycle node |p|@>=
+link(contrib_head):=link(p); link(p):=null; flush_node_list(p)
+
+@ The title of this section is already so long, it seems best to avoid
+making it more accurate but still longer, by mentioning the fact that a
+kern node at the end of the contribution list will not be contributed until
+we know its successor.
+
+@<If the current page is empty...@>=
+case type(p) of
+hlist_node,vlist_node,rule_node: if page_contents<box_there then
+ @<Initialize the current page, insert the \.{\\topskip} glue
+ ahead of |p|, and |goto continue|@>
+ else @<Prepare to move a box or rule node to the current page,
+ then |goto contribute|@>;
+whatsit_node: @<Prepare to move whatsit |p| to the current page,
+ then |goto contribute|@>;
+glue_node: if page_contents<box_there then goto done1
+ else if precedes_break(page_tail) then pi:=0
+ else goto update_heights;
+kern_node: if page_contents<box_there then goto done1
+ else if link(p)=null then return
+ else if type(link(p))=glue_node then pi:=0
+ else goto update_heights;
+penalty_node: if page_contents<box_there then goto done1@+else pi:=penalty(p);
+mark_node: goto contribute;
+ins_node: @<Append an insertion to the current page and |goto contribute|@>;
+othercases confusion("page")
+@:this can't happen page}{\quad page@>
+endcases
+
+@ @<Initialize the current page, insert the \.{\\topskip} glue...@>=
+begin if page_contents=empty then freeze_page_specs(box_there)
+else page_contents:=box_there;
+q:=new_skip_param(top_skip_code); {now |temp_ptr=glue_ptr(q)|}
+if width(temp_ptr)>height(p) then width(temp_ptr):=width(temp_ptr)-height(p)
+else width(temp_ptr):=0;
+link(q):=p; link(contrib_head):=q; goto continue;
+end
+
+@ @<Prepare to move a box or rule node to the current page...@>=
+begin page_total:=page_total+page_depth+height(p);
+page_depth:=depth(p);
+goto contribute;
+end
+
+@ @<Make sure that |page_max_depth| is not exceeded@>=
+if page_depth>page_max_depth then
+ begin page_total:=@|
+ page_total+page_depth-page_max_depth;@/
+ page_depth:=page_max_depth;
+ end;
+
+@ @<Update the current page measurements with respect to the glue...@>=
+if type(p)=kern_node then q:=p
+else begin q:=glue_ptr(p);
+ page_so_far[2+stretch_order(q)]:=@|
+ page_so_far[2+stretch_order(q)]+stretch(q);@/
+ page_shrink:=page_shrink+shrink(q);
+ if (shrink_order(q)<>normal)and(shrink(q)<>0) then
+ begin@t@>@;@/
+ print_err("Infinite glue shrinkage found on current page");@/
+@.Infinite glue shrinkage...@>
+ help4("The page about to be output contains some infinitely")@/
+ ("shrinkable glue, e.g., `\vss' or `\vskip 0pt minus 1fil'.")@/
+ ("Such glue doesn't belong there; but you can safely proceed,")@/
+ ("since the offensive shrinkability has been made finite.");
+ error;
+ r:=new_spec(q); shrink_order(r):=normal; delete_glue_ref(q);
+ glue_ptr(p):=r; q:=r;
+ end;
+ end;
+page_total:=page_total+page_depth+width(q); page_depth:=0
+
+@ @<Check if node |p| is a new champion breakpoint; then \(if)...@>=
+if pi<inf_penalty then
+ begin @<Compute the badness, |b|, of the current page,
+ using |awful_bad| if the box is too full@>;
+ if b<awful_bad then
+ if pi<=eject_penalty then c:=pi
+ else if b<inf_bad then c:=b+pi+insert_penalties
+ else c:=deplorable
+ else c:=b;
+ if insert_penalties>=10000 then c:=awful_bad;
+ @!stat if tracing_pages>0 then @<Display the page break cost@>;@+tats@;@/
+ if c<=least_page_cost then
+ begin best_page_break:=p; best_size:=page_goal;
+ least_page_cost:=c;
+ r:=link(page_ins_head);
+ while r<>page_ins_head do
+ begin best_ins_ptr(r):=last_ins_ptr(r);
+ r:=link(r);
+ end;
+ end;
+ if (c=awful_bad)or(pi<=eject_penalty) then
+ begin fire_up(p); {output the current page at the best place}
+ if output_active then return; {user's output routine will act}
+ goto done; {the page has been shipped out by default output routine}
+ end;
+ end
+
+@ @<Display the page break cost@>=
+begin begin_diagnostic; print_nl("%");
+print(" t="); print_totals;@/
+print(" g="); print_scaled(page_goal);@/
+print(" b=");
+if b=awful_bad then print_char("*")@+else print_int(b);
+@.*\relax@>
+print(" p="); print_int(pi);
+print(" c=");
+if c=awful_bad then print_char("*")@+else print_int(c);
+if c<=least_page_cost then print_char("#");
+end_diagnostic(false);
+end
+
+@ @<Compute the badness, |b|, of the current page...@>=
+if page_total<page_goal then
+ if (page_so_far[3]<>0) or (page_so_far[4]<>0) or@|
+ (page_so_far[5]<>0) then b:=0
+ else b:=badness(page_goal-page_total,page_so_far[2])
+else if page_total-page_goal>page_shrink then b:=awful_bad
+else b:=badness(page_total-page_goal,page_shrink)
+
+@ @<Append an insertion to the current page and |goto contribute|@>=
+begin if page_contents=empty then freeze_page_specs(inserts_only);
+n:=subtype(p); r:=page_ins_head;
+while n>=subtype(link(r)) do r:=link(r);
+n:=qo(n);
+if subtype(r)<>qi(n) then
+ @<Create a page insertion node with |subtype(r)=qi(n)|, and
+ include the glue correction for box |n| in the
+ current page state@>;
+if type(r)=split_up then insert_penalties:=insert_penalties+float_cost(p)
+else begin last_ins_ptr(r):=p;
+ delta:=page_goal-page_total-page_depth+page_shrink;
+ {this much room is left if we shrink the maximum}
+ if count(n)=1000 then h:=height(p)
+ else h:=x_over_n(height(p),1000)*count(n); {this much room is needed}
+ if ((h<=0)or(h<=delta))and(height(p)+height(r)<=dimen(n)) then
+ begin page_goal:=page_goal-h; height(r):=height(r)+height(p);
+ end
+ else @<Find the best way to split the insertion, and change
+ |type(r)| to |split_up|@>;
+ end;
+goto contribute;
+end
+
+@ We take note of the value of \.{\\skip} |n| and the height plus depth
+of \.{\\box}~|n| only when the first \.{\\insert}~|n| node is
+encountered for a new page. A user who changes the contents of \.{\\box}~|n|
+after that first \.{\\insert}~|n| had better be either extremely careful
+or extremely lucky, or both.
+
+@<Create a page insertion node...@>=
+begin q:=get_node(page_ins_node_size); link(q):=link(r); link(r):=q; r:=q;
+subtype(r):=qi(n); type(r):=inserting; ensure_vbox(n);
+if box(n)=null then height(r):=0
+else height(r):=height(box(n))+depth(box(n));
+best_ins_ptr(r):=null;@/
+q:=skip(n);
+if count(n)=1000 then h:=height(r)
+else h:=x_over_n(height(r),1000)*count(n);
+page_goal:=page_goal-h-width(q);@/
+page_so_far[2+stretch_order(q)]:=@|page_so_far[2+stretch_order(q)]+stretch(q);@/
+page_shrink:=page_shrink+shrink(q);
+if (shrink_order(q)<>normal)and(shrink(q)<>0) then
+ begin print_err("Infinite glue shrinkage inserted from "); print_esc("skip");
+@.Infinite glue shrinkage...@>
+ print_int(n);
+ help3("The correction glue for page breaking with insertions")@/
+ ("must have finite shrinkability. But you may proceed,")@/
+ ("since the offensive shrinkability has been made finite.");
+ error;
+ end;
+end
+
+@ Here is the code that will split a long footnote between pages, in an
+emergency. The current situation deserves to be recapitulated: Node |p|
+is an insertion into box |n|; the insertion will not fit, in its entirety,
+either because it would make the total contents of box |n| greater than
+\.{\\dimen} |n|, or because it would make the incremental amount of growth
+|h| greater than the available space |delta|, or both. (This amount |h| has
+been weighted by the insertion scaling factor, i.e., by \.{\\count} |n|
+over 1000.) Now we will choose the best way to break the vlist of the
+insertion, using the same criteria as in the \.{\\vsplit} operation.
+
+@<Find the best way to split the insertion...@>=
+begin if count(n)<=0 then w:=max_dimen
+else begin w:=page_goal-page_total-page_depth;
+ if count(n)<>1000 then w:=x_over_n(w,count(n))*1000;
+ end;
+if w>dimen(n)-height(r) then w:=dimen(n)-height(r);
+q:=vert_break(ins_ptr(p),w,depth(p));
+height(r):=height(r)+best_height_plus_depth;
+@!stat if tracing_pages>0 then @<Display the insertion split cost@>;@+tats@;@/
+if count(n)<>1000 then
+ best_height_plus_depth:=x_over_n(best_height_plus_depth,1000)*count(n);
+page_goal:=page_goal-best_height_plus_depth;
+type(r):=split_up; broken_ptr(r):=q; broken_ins(r):=p;
+if q=null then insert_penalties:=insert_penalties+eject_penalty
+else if type(q)=penalty_node then insert_penalties:=insert_penalties+penalty(q);
+end
+
+@ @<Display the insertion split cost@>=
+begin begin_diagnostic; print_nl("% split"); print_int(n);
+@.split@>
+print(" to "); print_scaled(w);
+print_char(","); print_scaled(best_height_plus_depth);@/
+print(" p=");
+if q=null then print_int(eject_penalty)
+else if type(q)=penalty_node then print_int(penalty(q))
+else print_char("0");
+end_diagnostic(false);
+end
+
+@ When the page builder has looked at as much material as could appear before
+the next page break, it makes its decision. The break that gave minimum
+badness will be used to put a completed ``page'' into box 255, with insertions
+appended to their other boxes.
+
+We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The
+program uses the fact that |bot_mark<>null| implies |first_mark<>null|;
+it also knows that |bot_mark=null| implies |top_mark=first_mark=null|.
+
+The |fire_up| subroutine prepares to output the current page at the best
+place; then it fires up the user's output routine, if there is one,
+or it simply ships out the page. There is one parameter, |c|, which represents
+the node that was being contributed to the page when the decision to
+force an output was made.
+
+@<Declare the procedure called |fire_up|@>=
+procedure fire_up(@!c:pointer);
+label exit;
+var p,@!q,@!r,@!s:pointer; {nodes being examined and/or changed}
+@!prev_p:pointer; {predecessor of |p|}
+@!n:min_quarterword..255; {insertion box number}
+@!wait:boolean; {should the present insertion be held over?}
+@!save_vbadness:integer; {saved value of |vbadness|}
+@!save_vfuzz: scaled; {saved value of |vfuzz|}
+@!save_split_top_skip: pointer; {saved value of |split_top_skip|}
+begin @<Set the value of |output_penalty|@>;
+if bot_mark<>null then
+ begin if top_mark<>null then delete_token_ref(top_mark);
+ top_mark:=bot_mark; add_token_ref(top_mark);
+ delete_token_ref(first_mark); first_mark:=null;
+ end;
+@<Put the \(o)optimal current page into box 255, update |first_mark| and
+ |bot_mark|, append insertions to their boxes, and put the
+ remaining nodes back on the contribution list@>;
+if (top_mark<>null)and(first_mark=null) then
+ begin first_mark:=top_mark; add_token_ref(top_mark);
+ end;
+if output_routine<>null then
+ if dead_cycles>=max_dead_cycles then
+ @<Explain that too many dead cycles have occurred in a row@>
+ else @<Fire up the user's output routine and |return|@>;
+@<Perform the default output routine@>;
+exit:end;
+
+@ @<Set the value of |output_penalty|@>=
+if type(best_page_break)=penalty_node then
+ begin geq_word_define(int_base+output_penalty_code,penalty(best_page_break));
+ penalty(best_page_break):=inf_penalty;
+ end
+else geq_word_define(int_base+output_penalty_code,inf_penalty)
+
+@ As the page is finally being prepared for output,
+pointer |p| runs through the vlist, with |prev_p| trailing behind;
+pointer |q| is the tail of a list of insertions that
+are being held over for a subsequent page.
+
+@<Put the \(o)optimal current page into box 255...@>=
+if c=best_page_break then best_page_break:=null; {|c| not yet linked in}
+@<Ensure that box 255 is empty before output@>;
+insert_penalties:=0; {this will count the number of insertions held over}
+save_split_top_skip:=split_top_skip;
+if holding_inserts<=0 then
+ @<Prepare all the boxes involved in insertions to act as queues@>;
+q:=hold_head; link(q):=null; prev_p:=page_head; p:=link(prev_p);
+while p<>best_page_break do
+ begin if type(p)=ins_node then
+ begin if holding_inserts<=0 then
+ @<Either insert the material specified by node |p| into the
+ appropriate box, or hold it for the next page;
+ also delete node |p| from the current page@>;
+ end
+ else if type(p)=mark_node then @<Update the values of
+ |first_mark| and |bot_mark|@>;
+ prev_p:=p; p:=link(prev_p);
+ end;
+split_top_skip:=save_split_top_skip;
+@<Break the current page at node |p|, put it in box~255,
+ and put the remaining nodes on the contribution list@>;
+@<Delete \(t)the page-insertion nodes@>
+
+@ @<Ensure that box 255 is empty before output@>=
+if box(255)<>null then
+ begin print_err(""); print_esc("box"); print("255 is not void");
+@:box255}{\.{\\box255 is not void}@>
+ help2("You shouldn't use \box255 except in \output routines.")@/
+ ("Proceed, and I'll discard its present contents.");
+ box_error(255);
+ end
+
+@ @<Update the values of |first_mark| and |bot_mark|@>=
+begin if first_mark=null then
+ begin first_mark:=mark_ptr(p);
+ add_token_ref(first_mark);
+ end;
+if bot_mark<>null then delete_token_ref(bot_mark);
+bot_mark:=mark_ptr(p); add_token_ref(bot_mark);
+end
+
+@ When the following code is executed, the current page runs from node
+|link(page_head)| to node |prev_p|, and the nodes from |p| to |page_tail|
+are to be placed back at the front of the contribution list. Furthermore
+the heldover insertions appear in a list from |link(hold_head)| to |q|; we
+will put them into the current page list for safekeeping while the user's
+output routine is active. We might have |q=hold_head|; and |p=null| if
+and only if |prev_p=page_tail|. Error messages are suppressed within
+|vpackage|, since the box might appear to be overfull or underfull simply
+because the stretch and shrink from the \.{\\skip} registers for inserts
+are not actually present in the box.
+
+@<Break the current page at node |p|, put it...@>=
+if p<>null then
+ begin if link(contrib_head)=null then
+ if nest_ptr=0 then tail:=page_tail
+ else contrib_tail:=page_tail;
+ link(page_tail):=link(contrib_head);
+ link(contrib_head):=p;
+ link(prev_p):=null;
+ end;
+save_vbadness:=vbadness; vbadness:=inf_bad;
+save_vfuzz:=vfuzz; vfuzz:=max_dimen; {inhibit error messages}
+box(255):=vpackage(link(page_head),best_size,exactly,page_max_depth);
+vbadness:=save_vbadness; vfuzz:=save_vfuzz;
+if last_glue<>max_halfword then delete_glue_ref(last_glue);
+@<Start a new current page@>; {this sets |last_glue:=max_halfword|}
+if q<>hold_head then
+ begin link(page_head):=link(hold_head); page_tail:=q;
+ end
+
+@ If many insertions are supposed to go into the same box, we want to know
+the position of the last node in that box, so that we don't need to waste time
+when linking further information into it. The |last_ins_ptr| fields of the
+page insertion nodes are therefore used for this purpose during the
+packaging phase.
+
+@<Prepare all the boxes involved in insertions to act as queues@>=
+begin r:=link(page_ins_head);
+while r<>page_ins_head do
+ begin if best_ins_ptr(r)<>null then
+ begin n:=qo(subtype(r)); ensure_vbox(n);
+ if box(n)=null then box(n):=new_null_box;
+ p:=box(n)+list_offset;
+ while link(p)<>null do p:=link(p);
+ last_ins_ptr(r):=p;
+ end;
+ r:=link(r);
+ end;
+end
+
+@ @<Delete \(t)the page-insertion nodes@>=
+r:=link(page_ins_head);
+while r<>page_ins_head do
+ begin q:=link(r); free_node(r,page_ins_node_size); r:=q;
+ end;
+link(page_ins_head):=page_ins_head
+
+@ We will set |best_ins_ptr:=null| and package the box corresponding to
+insertion node~|r|, just after making the final insertion into that box.
+If this final insertion is `|split_up|', the remainder after splitting
+and pruning (if any) will be carried over to the next page.
+
+@<Either insert the material specified by node |p| into...@>=
+begin r:=link(page_ins_head);
+while subtype(r)<>subtype(p) do r:=link(r);
+if best_ins_ptr(r)=null then wait:=true
+else begin wait:=false; s:=last_ins_ptr(r); link(s):=ins_ptr(p);
+ if best_ins_ptr(r)=p then
+ @<Wrap up the box specified by node |r|, splitting node |p| if
+ called for; set |wait:=true| if node |p| holds a remainder after
+ splitting@>
+ else begin while link(s)<>null do s:=link(s);
+ last_ins_ptr(r):=s;
+ end;
+ end;
+@<Either append the insertion node |p| after node |q|, and remove it
+ from the current page, or delete |node(p)|@>;
+end
+
+@ @<Wrap up the box specified by node |r|, splitting node |p| if...@>=
+begin if type(r)=split_up then
+ if (broken_ins(r)=p)and(broken_ptr(r)<>null) then
+ begin while link(s)<>broken_ptr(r) do s:=link(s);
+ link(s):=null;
+ split_top_skip:=split_top_ptr(p);
+ ins_ptr(p):=prune_page_top(broken_ptr(r));
+ if ins_ptr(p)<>null then
+ begin temp_ptr:=vpack(ins_ptr(p),natural);
+ height(p):=height(temp_ptr)+depth(temp_ptr);
+ free_node(temp_ptr,box_node_size); wait:=true;
+ end;
+ end;
+best_ins_ptr(r):=null;
+n:=qo(subtype(r));
+temp_ptr:=list_ptr(box(n));
+free_node(box(n),box_node_size);
+box(n):=vpack(temp_ptr,natural);
+end
+
+@ @<Either append the insertion node |p|...@>=
+link(prev_p):=link(p); link(p):=null;
+if wait then
+ begin link(q):=p; q:=p; incr(insert_penalties);
+ end
+else begin delete_glue_ref(split_top_ptr(p));
+ free_node(p,ins_node_size);
+ end;
+p:=prev_p
+
+@ The list of heldover insertions, running from |link(page_head)| to
+|page_tail|, must be moved to the contribution list when the user has
+specified no output routine.
+
+@<Perform the default output routine@>=
+begin if link(page_head)<>null then
+ begin if link(contrib_head)=null then
+ if nest_ptr=0 then tail:=page_tail@+else contrib_tail:=page_tail
+ else link(page_tail):=link(contrib_head);
+ link(contrib_head):=link(page_head);
+ link(page_head):=null; page_tail:=page_head;
+ end;
+ship_out(box(255)); box(255):=null;
+end
+
+@ @<Explain that too many dead cycles have occurred in a row@>=
+begin print_err("Output loop---"); print_int(dead_cycles);
+@.Output loop...@>
+print(" consecutive dead cycles");
+help3("I've concluded that your \output is awry; it never does a")@/
+("\shipout, so I'm shipping \box255 out myself. Next time")@/
+("increase \maxdeadcycles if you want me to be more patient!"); error;
+end
+
+@ @<Fire up the user's output routine and |return|@>=
+begin output_active:=true;
+incr(dead_cycles);
+push_nest; mode:=-vmode; prev_depth:=ignore_depth; mode_line:=-line;
+begin_token_list(output_routine,output_text);
+new_save_level(output_group); normal_paragraph;
+scan_left_brace;
+return;
+end
+
+@ When the user's output routine finishes, it has constructed a vlist
+in internal vertical mode, and \TeX\ will do the following:
+
+@<Resume the page builder after an output routine has come to an end@>=
+begin if (loc<>null) or
+ ((token_type<>output_text)and(token_type<>backed_up)) then
+ @<Recover from an unbalanced output routine@>;
+end_token_list; {conserve stack space in case more outputs are triggered}
+end_graf; unsave; output_active:=false; insert_penalties:=0;@/
+@<Ensure that box 255 is empty after output@>;
+if tail<>head then {current list goes after heldover insertions}
+ begin link(page_tail):=link(head);
+ page_tail:=tail;
+ end;
+if link(page_head)<>null then {and both go before heldover contributions}
+ begin if link(contrib_head)=null then contrib_tail:=page_tail;
+ link(page_tail):=link(contrib_head);
+ link(contrib_head):=link(page_head);
+ link(page_head):=null; page_tail:=page_head;
+ end;
+pop_nest; build_page;
+end
+
+@ @<Recover from an unbalanced output routine@>=
+begin print_err("Unbalanced output routine");
+@.Unbalanced output routine@>
+help2("Your sneaky output routine has problematic {'s and/or }'s.")@/
+("I can't handle that very well; good luck."); error;
+repeat get_token;
+until loc=null;
+end {loops forever if reading from a file, since |null=min_halfword<=0|}
+
+@ @<Ensure that box 255 is empty after output@>=
+if box(255)<>null then
+ begin print_err("Output routine didn't use all of ");
+ print_esc("box"); print_int(255);
+@.Output routine didn't use...@>
+ help3("Your \output commands should empty \box255,")@/
+ ("e.g., by saying `\shipout\box255'.")@/
+ ("Proceed; I'll discard its present contents.");
+ box_error(255);
+ end
+
+@* \[46] The chief executive.
+We come now to the |main_control| routine, which contains the master
+switch that causes all the various pieces of \TeX\ to do their things,
+in the right order.
+
+In a sense, this is the grand climax of the program: It applies all the
+tools that we have worked so hard to construct. In another sense, this is
+the messiest part of the program: It necessarily refers to other pieces
+of code all over the place, so that a person can't fully understand what is
+going on without paging back and forth to be reminded of conventions that
+are defined elsewhere. We are now at the hub of the web, the central nervous
+system that touches most of the other parts and ties them together.
+@^brain@>
+
+The structure of |main_control| itself is quite simple. There's a label
+called |big_switch|, at which point the next token of input is fetched
+using |get_x_token|. Then the program branches at high speed into one of
+about 100 possible directions, based on the value of the current
+mode and the newly fetched command code; the sum |abs(mode)+cur_cmd|
+indicates what to do next. For example, the case `|vmode+letter|' arises
+when a letter occurs in vertical mode (or internal vertical mode); this
+case leads to instructions that initialize a new paragraph and enter
+horizontal mode.
+
+The big |case| statement that contains this multiway switch has been labeled
+|reswitch|, so that the program can |goto reswitch| when the next token
+has already been fetched. Most of the cases are quite short; they call
+an ``action procedure'' that does the work for that case, and then they
+either |goto reswitch| or they ``fall through'' to the end of the |case|
+statement, which returns control back to |big_switch|. Thus, |main_control|
+is not an extremely large procedure, in spite of the multiplicity of things
+it must do; it is small enough to be handled by \PASCAL\ compilers that put
+severe restrictions on procedure size.
+@!@^action procedure@>
+
+One case is singled out for special treatment, because it accounts for most
+of \TeX's activities in typical applications. The process of reading simple
+text and converting it into |char_node| records, while looking for ligatures
+and kerns, is part of \TeX's ``inner loop''; the whole program runs
+efficiently when its inner loop is fast, so this part has been written
+with particular care.
+
+@ We shall concentrate first on the inner loop of |main_control|, deferring
+consideration of the other cases until later.
+@^inner loop@>
+
+@d big_switch=60 {go here to branch on the next token of input}
+@d main_loop=70 {go here to typeset a string of consecutive characters}
+@d main_loop_wrapup=80 {go here to finish a character or ligature}
+@d main_loop_move=90 {go here to advance the ligature cursor}
+@d main_loop_move_lig=95 {same, when advancing past a generated ligature}
+@d main_loop_lookahead=100 {go here to bring in another character, if any}
+@d main_lig_loop=110 {go here to check for ligatures or kerning}
+@d append_normal_space=120 {go here to append a normal space between words}
+
+@p @t\4@>@<Declare action procedures for use by |main_control|@>@;
+@t\4@>@<Declare the procedure called |handle_right_brace|@>@;
+procedure main_control; {governs \TeX's activities}
+label big_switch,reswitch,main_loop,main_loop_wrapup,
+ main_loop_move,main_loop_move+1,main_loop_move+2,main_loop_move_lig,
+ main_loop_lookahead,main_loop_lookahead+1,
+ main_lig_loop,main_lig_loop+1,main_lig_loop+2,
+ append_normal_space,exit;
+var@!t:integer; {general-purpose temporary variable}
+begin if every_job<>null then begin_token_list(every_job,every_job_text);
+big_switch: get_x_token;@/
+reswitch: @<Give diagnostic information, if requested@>;
+case abs(mode)+cur_cmd of
+hmode+letter,hmode+other_char,hmode+char_given: goto main_loop;
+hmode+char_num: begin scan_char_num; cur_chr:=cur_val; goto main_loop;@+end;
+hmode+no_boundary: begin get_x_token;
+ if (cur_cmd=letter)or(cur_cmd=other_char)or(cur_cmd=char_given)or
+ (cur_cmd=char_num) then cancel_boundary:=true;
+ goto reswitch;
+ end;
+hmode+spacer: if space_factor=1000 then goto append_normal_space
+ else app_space;
+hmode+ex_space,mmode+ex_space: goto append_normal_space;
+@t\4@>@<Cases of |main_control| that are not part of the inner loop@>@;
+end; {of the big |case| statement}
+goto big_switch;
+main_loop:@<Append character |cur_chr| and the following characters (if~any)
+ to the current hlist in the current font; |goto reswitch| when
+ a non-character has been fetched@>;
+append_normal_space:@<Append a normal inter-word space to the current list,
+ then |goto big_switch|@>;
+exit:end;
+
+@ When a new token has just been fetched at |big_switch|, we have an
+ideal place to monitor \TeX's activity.
+@^debugging@>
+
+@<Give diagnostic information, if requested@>=
+if interrupt<>0 then if OK_to_interrupt then
+ begin back_input; check_interrupt; goto big_switch;
+ end;
+@!debug if panicking then check_mem(false);@+@;@+gubed
+if tracing_commands>0 then show_cur_cmd_chr
+
+@ The following part of the program was first written in a structured
+manner, according to the philosophy that ``premature optimization is
+the root of all evil.'' Then it was rearranged into pieces of
+spaghetti so that the most common actions could proceed with little or
+no redundancy.
+
+The original unoptimized form of this algorithm resembles the
+|reconstitute| procedure, which was described earlier in connection with
+hyphenation. Again we have an implied ``cursor'' between characters
+|cur_l| and |cur_r|. The main difference is that the |lig_stack| can now
+contain a charnode as well as pseudo-ligatures; that stack is now
+usually nonempty, because the next character of input (if any) has been
+appended to it. In |main_control| we have
+$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack>null|;\cr
+ |font_bchar[cur_font]|,&otherwise;\cr}$$
+except when |character(lig_stack)=font_false_bchar[cur_font]|.
+Several additional global variables are needed.
+
+@<Glob...@>=
+@!main_f:internal_font_number; {the current font}
+@!main_i:four_quarters; {character information bytes for |cur_l|}
+@!main_j:four_quarters; {ligature/kern command}
+@!main_k:font_index; {index into |font_info|}
+@!main_p:pointer; {temporary register for list manipulation}
+@!main_s:integer; {space factor value}
+@!bchar:halfword; {right boundary character of current font, or |non_char|}
+@!false_bchar:halfword; {nonexistent character matching |bchar|, or |non_char|}
+@!cancel_boundary:boolean; {should the left boundary be ignored?}
+@!ins_disc:boolean; {should we insert a discretionary node?}
+
+@ The boolean variables of the main loop are normally false, and always reset
+to false before the loop is left. That saves us the extra work of initializing
+each time.
+
+@<Set init...@>=
+ligature_present:=false; cancel_boundary:=false; lft_hit:=false; rt_hit:=false;
+ins_disc:=false;
+
+@ We leave the |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we
+set it equal to |sf_code(cur_chr)|, except that it should never change
+from a value less than 1000 to a value exceeding 1000. The most common
+case is |sf_code(cur_chr)=1000|, so we want that case to be fast.
+
+The overall structure of the main loop is presented here. Some program labels
+are inside the individual sections.
+@^inner loop@>
+
+@d adjust_space_factor==@t@>@;@/
+ main_s:=sf_code(cur_chr);
+ if main_s=1000 then space_factor:=1000
+ else if main_s<1000 then
+ begin if main_s>0 then space_factor:=main_s;
+ end
+ else if space_factor<1000 then space_factor:=1000
+ else space_factor:=main_s
+
+@<Append character |cur_chr|...@>=
+adjust_space_factor;@/
+main_f:=cur_font;
+bchar:=font_bchar[main_f]; false_bchar:=font_false_bchar[main_f];
+if mode>0 then if language<>clang then fix_language;
+fast_get_avail(lig_stack); font(lig_stack):=main_f; cur_l:=qi(cur_chr);
+character(lig_stack):=cur_l;@/
+cur_q:=tail;
+if cancel_boundary then
+ begin cancel_boundary:=false; main_k:=non_address;
+ end
+else main_k:=bchar_label[main_f];
+if main_k=non_address then goto main_loop_move+2; {no left boundary processing}
+cur_r:=cur_l; cur_l:=non_char;
+goto main_lig_loop+1; {begin with cursor after left boundary}
+@#
+main_loop_wrapup:@<Make a ligature node, if |ligature_present|;
+ insert a null discretionary, if appropriate@>;
+main_loop_move:@<If the cursor is immediately followed by the right boundary,
+ |goto reswitch|; if it's followed by an invalid character, |goto big_switch|;
+ otherwise move the cursor one step to the right and |goto main_lig_loop|@>;
+main_loop_lookahead:@<Look ahead for another character, or leave |lig_stack|
+ empty if there's none there@>;
+main_lig_loop:@<If there's a ligature/kern command relevant to |cur_l| and
+ |cur_r|, adjust the text appropriately; exit to |main_loop_wrapup|@>;
+main_loop_move_lig:@<Move the cursor past a pseudo-ligature, then
+ |goto main_loop_lookahead| or |main_lig_loop|@>
+
+@ If |link(cur_q)| is nonnull when |wrapup| is invoked, |cur_q| points to
+the list of characters that were consumed while building the ligature
+character~|cur_l|.
+
+A discretionary break is not inserted for an explicit hyphen when we are in
+restricted horizontal mode. In particular, this avoids putting discretionary
+nodes inside of other discretionaries.
+@^inner loop@>
+
+@d pack_lig(#)== {the parameter is either |rt_hit| or |false|}
+ begin main_p:=new_ligature(main_f,cur_l,link(cur_q));
+ if lft_hit then
+ begin subtype(main_p):=2; lft_hit:=false;
+ end;
+ if # then if lig_stack=null then
+ begin incr(subtype(main_p)); rt_hit:=false;
+ end;
+ link(cur_q):=main_p; tail:=main_p; ligature_present:=false;
+ end
+
+@d wrapup(#)==if cur_l<non_char then
+ begin if link(cur_q)>null then
+ if character(tail)=qi(hyphen_char[main_f]) then ins_disc:=true;
+ if ligature_present then pack_lig(#);
+ if ins_disc then
+ begin ins_disc:=false;
+ if mode>0 then tail_append(new_disc);
+ end;
+ end
+
+@<Make a ligature node, if |ligature_present|;...@>=
+wrapup(rt_hit)
+
+@ @<If the cursor is immediately followed by the right boundary...@>=
+@^inner loop@>
+if lig_stack=null then goto reswitch;
+cur_q:=tail; cur_l:=character(lig_stack);
+main_loop_move+1:if not is_char_node(lig_stack) then goto main_loop_move_lig;
+main_loop_move+2:if(cur_chr<font_bc[main_f])or(cur_chr>font_ec[main_f]) then
+ begin char_warning(main_f,cur_chr); free_avail(lig_stack); goto big_switch;
+ end;
+main_i:=char_info(main_f)(cur_l);
+if not char_exists(main_i) then
+ begin char_warning(main_f,cur_chr); free_avail(lig_stack); goto big_switch;
+ end;
+link(tail):=lig_stack; tail:=lig_stack {|main_loop_lookahead| is next}
+
+@ Here we are at |main_loop_move_lig|.
+When we begin this code we have |cur_q=tail| and |cur_l=character(lig_stack)|.
+
+@<Move the cursor past a pseudo-ligature...@>=
+main_p:=lig_ptr(lig_stack);
+if main_p>null then tail_append(main_p); {append a single character}
+temp_ptr:=lig_stack; lig_stack:=link(temp_ptr);
+free_node(temp_ptr,small_node_size);
+main_i:=char_info(main_f)(cur_l); ligature_present:=true;
+if lig_stack=null then
+ if main_p>null then goto main_loop_lookahead
+ else cur_r:=bchar
+else cur_r:=character(lig_stack);
+goto main_lig_loop
+
+@ The result of \.{\\char} can participate in a ligature or kern, so we must
+look ahead for it.
+
+@<Look ahead for another character...@>=
+get_next; {set only |cur_cmd| and |cur_chr|, for speed}
+if cur_cmd=letter then goto main_loop_lookahead+1;
+if cur_cmd=other_char then goto main_loop_lookahead+1;
+if cur_cmd=char_given then goto main_loop_lookahead+1;
+x_token; {now expand and set |cur_cmd|, |cur_chr|, |cur_tok|}
+if cur_cmd=letter then goto main_loop_lookahead+1;
+if cur_cmd=other_char then goto main_loop_lookahead+1;
+if cur_cmd=char_given then goto main_loop_lookahead+1;
+if cur_cmd=char_num then
+ begin scan_char_num; cur_chr:=cur_val; goto main_loop_lookahead+1;
+ end;
+if cur_cmd=no_boundary then bchar:=non_char;
+cur_r:=bchar; lig_stack:=null; goto main_lig_loop;
+main_loop_lookahead+1: adjust_space_factor;
+fast_get_avail(lig_stack); font(lig_stack):=main_f;
+cur_r:=qi(cur_chr); character(lig_stack):=cur_r;
+if cur_r=false_bchar then cur_r:=non_char {this prevents spurious ligatures}
+
+@ Even though comparatively few characters have a lig/kern program, several
+of the instructions here count as part of \TeX's inner loop, since a
+@^inner loop@>
+potentially long sequential search must be performed. For example, tests with
+Computer Modern Roman showed that about 40 per cent of all characters
+actually encountered in practice had a lig/kern program, and that about four
+lig/kern commands were investigated for every such character.
+
+At the beginning of this code we have |main_i=char_info(main_f)(cur_l)|.
+
+@<If there's a ligature/kern command...@>=
+if char_tag(main_i)<>lig_tag then goto main_loop_wrapup;
+if cur_r=non_char then goto main_loop_wrapup;
+main_k:=lig_kern_start(main_f)(main_i); main_j:=font_info[main_k].qqqq;
+if skip_byte(main_j)<=stop_flag then goto main_lig_loop+2;
+main_k:=lig_kern_restart(main_f)(main_j);
+main_lig_loop+1:main_j:=font_info[main_k].qqqq;
+main_lig_loop+2:if next_char(main_j)=cur_r then
+ if skip_byte(main_j)<=stop_flag then
+ @<Do ligature or kern command, returning to |main_lig_loop|
+ or |main_loop_wrapup| or |main_loop_move|@>;
+if skip_byte(main_j)=qi(0) then incr(main_k)
+else begin if skip_byte(main_j)>=stop_flag then goto main_loop_wrapup;
+ main_k:=main_k+qo(skip_byte(main_j))+1;
+ end;
+goto main_lig_loop+1
+
+@ When a ligature or kern instruction matches a character, we know from
+|read_font_info| that the character exists in the font, even though we
+haven't verified its existence in the normal way.
+
+This section could be made into a subroutine, if the code inside
+|main_control| needs to be shortened.
+
+\chardef\?='174 % vertical line to indicate character retention
+
+@<Do ligature or kern command...@>=
+begin if op_byte(main_j)>=kern_flag then
+ begin wrapup(rt_hit);
+ tail_append(new_kern(char_kern(main_f)(main_j))); goto main_loop_move;
+ end;
+if cur_l=non_char then lft_hit:=true
+else if lig_stack=null then rt_hit:=true;
+check_interrupt; {allow a way out in case there's an infinite ligature loop}
+case op_byte(main_j) of
+qi(1),qi(5):begin cur_l:=rem_byte(main_j); {\.{=:\?}, \.{=:\?>}}
+ main_i:=char_info(main_f)(cur_l); ligature_present:=true;
+ end;
+qi(2),qi(6):begin cur_r:=rem_byte(main_j); {\.{\?=:}, \.{\?=:>}}
+ if lig_stack=null then {right boundary character is being consumed}
+ begin lig_stack:=new_lig_item(cur_r); bchar:=non_char;
+ end
+ else if is_char_node(lig_stack) then {|link(lig_stack)=null|}
+ begin main_p:=lig_stack; lig_stack:=new_lig_item(cur_r);
+ lig_ptr(lig_stack):=main_p;
+ end
+ else character(lig_stack):=cur_r;
+ end;
+qi(3):begin cur_r:=rem_byte(main_j); {\.{\?=:\?}}
+ main_p:=lig_stack; lig_stack:=new_lig_item(cur_r);
+ link(lig_stack):=main_p;
+ end;
+qi(7),qi(11):begin wrapup(false); {\.{\?=:\?>}, \.{\?=:\?>>}}
+ cur_q:=tail; cur_l:=rem_byte(main_j);
+ main_i:=char_info(main_f)(cur_l); ligature_present:=true;
+ end;
+othercases begin cur_l:=rem_byte(main_j); ligature_present:=true; {\.{=:}}
+ if lig_stack=null then goto main_loop_wrapup
+ else goto main_loop_move+1;
+ end
+endcases;
+if op_byte(main_j)>qi(4) then
+ if op_byte(main_j)<>qi(7) then goto main_loop_wrapup;
+if cur_l<non_char then goto main_lig_loop;
+main_k:=bchar_label[main_f]; goto main_lig_loop+1;
+end
+
+@ The occurrence of blank spaces is almost part of \TeX's inner loop,
+@^inner loop@>
+since we usually encounter about one space for every five non-blank characters.
+Therefore |main_control| gives second-highest priority to ordinary spaces.
+
+When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will
+see to it later that the corresponding glue specification is precisely
+|zero_glue|, not merely a pointer to some specification that happens
+to be full of zeroes. Therefore it is simple to test whether a glue parameter
+is zero or~not.
+
+@<Append a normal inter-word space...@>=
+if space_skip=zero_glue then
+ begin @<Find the glue specification, |main_p|, for
+ text spaces in the current font@>;
+ temp_ptr:=new_glue(main_p);
+ end
+else temp_ptr:=new_param_glue(space_skip_code);
+link(tail):=temp_ptr; tail:=temp_ptr;
+goto big_switch
+
+@ Having |font_glue| allocated for each text font saves both time and memory.
+If any of the three spacing parameters are subsequently changed by the
+use of \.{\\fontdimen}, the |find_font_dimen| procedure deallocates the
+|font_glue| specification allocated here.
+
+@<Find the glue specification...@>=
+begin main_p:=font_glue[cur_font];
+if main_p=null then
+ begin main_p:=new_spec(zero_glue); main_k:=param_base[cur_font]+space_code;
+ width(main_p):=font_info[main_k].sc; {that's |space(cur_font)|}
+ stretch(main_p):=font_info[main_k+1].sc; {and |space_stretch(cur_font)|}
+ shrink(main_p):=font_info[main_k+2].sc; {and |space_shrink(cur_font)|}
+ font_glue[cur_font]:=main_p;
+ end;
+end
+
+@ @<Declare act...@>=
+procedure app_space; {handle spaces when |space_factor<>1000|}
+var@!q:pointer; {glue node}
+begin if (space_factor>=2000)and(xspace_skip<>zero_glue) then
+ q:=new_param_glue(xspace_skip_code)
+else begin if space_skip<>zero_glue then main_p:=space_skip
+ else @<Find the glue specification...@>;
+ main_p:=new_spec(main_p);
+ @<Modify the glue specification in |main_p| according to the space factor@>;
+ q:=new_glue(main_p); glue_ref_count(main_p):=null;
+ end;
+link(tail):=q; tail:=q;
+end;
+
+@ @<Modify the glue specification in |main_p| according to the space factor@>=
+if space_factor>=2000 then width(main_p):=width(main_p)+extra_space(cur_font);
+stretch(main_p):=xn_over_d(stretch(main_p),space_factor,1000);
+shrink(main_p):=xn_over_d(shrink(main_p),1000,space_factor)
+
+@ Whew---that covers the main loop. We can now proceed at a leisurely
+pace through the other combinations of possibilities.
+
+@d any_mode(#)==vmode+#,hmode+#,mmode+# {for mode-independent commands}
+
+@<Cases of |main_control| that are not part of the inner loop@>=
+any_mode(relax),vmode+spacer,mmode+spacer,mmode+no_boundary:do_nothing;
+any_mode(ignore_spaces): begin @<Get the next non-blank non-call...@>;
+ goto reswitch;
+ end;
+vmode+stop: if its_all_over then return; {this is the only way out}
+@t\4@>@<Forbidden cases detected in |main_control|@>@+@,any_mode(mac_param):
+ report_illegal_case;
+@<Math-only cases in non-math modes, or vice versa@>: insert_dollar_sign;
+@t\4@>@<Cases of |main_control| that build boxes and lists@>@;
+@t\4@>@<Cases of |main_control| that don't depend on |mode|@>@;
+@t\4@>@<Cases of |main_control| that are for extensions to \TeX@>@;
+
+@ Here is a list of cases where the user has probably gotten into or out of math
+mode by mistake. \TeX\ will insert a dollar sign and rescan the current token.
+
+@d non_math(#)==vmode+#,hmode+#
+
+@<Math-only cases in non-math modes...@>=
+non_math(sup_mark), non_math(sub_mark), non_math(math_char_num),
+non_math(math_given), non_math(math_comp), non_math(delim_num),
+non_math(left_right), non_math(above), non_math(radical),
+non_math(math_style), non_math(math_choice), non_math(vcenter),
+non_math(non_script), non_math(mkern), non_math(limit_switch),
+non_math(mskip), non_math(math_accent),
+mmode+endv, mmode+par_end, mmode+stop, mmode+vskip, mmode+un_vbox,
+mmode+valign, mmode+hrule
+
+@ @<Declare action...@>=
+procedure insert_dollar_sign;
+begin back_input; cur_tok:=math_shift_token+"$";
+print_err("Missing $ inserted");
+@.Missing \$ inserted@>
+help2("I've inserted a begin-math/end-math symbol since I think")@/
+("you left one out. Proceed, with fingers crossed."); ins_error;
+end;
+
+@ When erroneous situations arise, \TeX\ usually issues an error message
+specific to the particular error. For example, `\.{\\noalign}' should
+not appear in any mode, since it is recognized by the |align_peek| routine
+in all of its legitimate appearances; a special error message is given
+when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate
+error message is simply that the user is not allowed to do what he or she
+has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode,
+and `\.{\\lower}' only in non-vertical modes. Such cases are enumerated
+here and in the other sections referred to under `See also \dots.'
+
+@<Forbidden cases...@>=
+vmode+vmove,hmode+hmove,mmode+hmove,any_mode(last_item),
+
+@ The `|you_cant|' procedure prints a line saying that the current command
+is illegal in the current mode; it identifies these things symbolically.
+
+@<Declare action...@>=
+procedure you_cant;
+begin print_err("You can't use `");
+@.You can't use x in y mode@>
+print_cmd_chr(cur_cmd,cur_chr);
+print("' in "); print_mode(mode);
+end;
+
+@ @<Declare act...@>=
+procedure report_illegal_case;
+begin you_cant;
+help4("Sorry, but I'm not programmed to handle this case;")@/
+("I'll just pretend that you didn't ask for it.")@/
+("If you're in the wrong mode, you might be able to")@/
+("return to the right one by typing `I}' or `I$' or `I\par'.");@/
+error;
+end;
+
+@ Some operations are allowed only in privileged modes, i.e., in cases
+that |mode>0|. The |privileged| function is used to detect violations
+of this rule; it issues an error message and returns |false| if the
+current |mode| is negative.
+
+@<Declare act...@>=
+function privileged:boolean;
+begin if mode>0 then privileged:=true
+else begin report_illegal_case; privileged:=false;
+ end;
+end;
+
+@ Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the
+endgame, since both of them have `|stop|' as their command code.
+
+@<Put each...@>=
+primitive("end",stop,0);@/
+@!@:end_}{\.{\\end} primitive@>
+primitive("dump",stop,1);@/
+@!@:dump_}{\.{\\dump} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+stop:if chr_code=1 then print_esc("dump")@+else print_esc("end");
+
+@ We don't want to leave |main_control| immediately when a |stop| command
+is sensed, because it may be necessary to invoke an \.{\\output} routine
+several times before things really grind to a halt. (The output routine
+might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.)
+Therefore |its_all_over| is |true| only when the current page
+and contribution list are empty, and when the last output was not a
+``dead cycle.''
+
+@<Declare act...@>=
+function its_all_over:boolean; {do this when \.{\\end} or \.{\\dump} occurs}
+label exit;
+begin if privileged then
+ begin if (page_head=page_tail)and(head=tail)and(dead_cycles=0) then
+ begin its_all_over:=true; return;
+ end;
+ back_input; {we will try to end again after ejecting residual material}
+ tail_append(new_null_box);
+ width(tail):=hsize;
+ tail_append(new_glue(fill_glue));
+ tail_append(new_penalty(-@'10000000000));@/
+ build_page; {append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000}}
+ end;
+its_all_over:=false;
+exit:end;
+
+@* \[47] Building boxes and lists.
+The most important parts of |main_control| are concerned with \TeX's
+chief mission of box-making. We need to control the activities that put
+entries on vlists and hlists, as well as the activities that convert
+those lists into boxes. All of the necessary machinery has already been
+developed; it remains for us to ``push the buttons'' at the right times.
+
+@ As an introduction to these routines, let's consider one of the simplest
+cases: What happens when `\.{\\hrule}' occurs in vertical mode, or
+`\.{\\vrule}' in horizontal mode or math mode? The code in |main_control|
+is short, since the |scan_rule_spec| routine already does most of what is
+required; thus, there is no need for a special action procedure.
+
+Note that baselineskip calculations are disabled after a rule in vertical
+mode, by setting |prev_depth:=ignore_depth|.
+
+@<Cases of |main_control| that build...@>=
+vmode+hrule,hmode+vrule,mmode+vrule: begin tail_append(scan_rule_spec);
+ if abs(mode)=vmode then prev_depth:=ignore_depth
+ else if abs(mode)=hmode then space_factor:=1000;
+ end;
+
+@ The processing of things like \.{\\hskip} and \.{\\vskip} is slightly
+more complicated. But the code in |main_control| is very short, since
+it simply calls on the action routine |append_glue|. Similarly, \.{\\kern}
+activates |append_kern|.
+
+@<Cases of |main_control| that build...@>=
+vmode+vskip,hmode+hskip,mmode+hskip,mmode+mskip: append_glue;
+any_mode(kern),mmode+mkern: append_kern;
+
+@ The |hskip| and |vskip| command codes are used for control sequences
+like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}.
+The difference is in the value of |cur_chr|.
+
+@d fil_code=0 {identifies \.{\\hfil} and \.{\\vfil}}
+@d fill_code=1 {identifies \.{\\hfill} and \.{\\vfill}}
+@d ss_code=2 {identifies \.{\\hss} and \.{\\vss}}
+@d fil_neg_code=3 {identifies \.{\\hfilneg} and \.{\\vfilneg}}
+@d skip_code=4 {identifies \.{\\hskip} and \.{\\vskip}}
+@d mskip_code=5 {identifies \.{\\mskip}}
+
+@<Put each...@>=
+primitive("hskip",hskip,skip_code);@/
+@!@:hskip_}{\.{\\hskip} primitive@>
+primitive("hfil",hskip,fil_code);
+@!@:hfil_}{\.{\\hfil} primitive@>
+primitive("hfill",hskip,fill_code);@/
+@!@:hfill_}{\.{\\hfill} primitive@>
+primitive("hss",hskip,ss_code);
+@!@:hss_}{\.{\\hss} primitive@>
+primitive("hfilneg",hskip,fil_neg_code);@/
+@!@:hfil_neg_}{\.{\\hfilneg} primitive@>
+primitive("vskip",vskip,skip_code);@/
+@!@:vskip_}{\.{\\vskip} primitive@>
+primitive("vfil",vskip,fil_code);
+@!@:vfil_}{\.{\\vfil} primitive@>
+primitive("vfill",vskip,fill_code);@/
+@!@:vfill_}{\.{\\vfill} primitive@>
+primitive("vss",vskip,ss_code);
+@!@:vss_}{\.{\\vss} primitive@>
+primitive("vfilneg",vskip,fil_neg_code);@/
+@!@:vfil_neg_}{\.{\\vfilneg} primitive@>
+primitive("mskip",mskip,mskip_code);@/
+@!@:mskip_}{\.{\\mskip} primitive@>
+primitive("kern",kern,explicit);
+@!@:kern_}{\.{\\kern} primitive@>
+primitive("mkern",mkern,mu_glue);@/
+@!@:mkern_}{\.{\\mkern} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+hskip: case chr_code of
+ skip_code:print_esc("hskip");
+ fil_code:print_esc("hfil");
+ fill_code:print_esc("hfill");
+ ss_code:print_esc("hss");
+ othercases print_esc("hfilneg")
+ endcases;
+vskip: case chr_code of
+ skip_code:print_esc("vskip");
+ fil_code:print_esc("vfil");
+ fill_code:print_esc("vfill");
+ ss_code:print_esc("vss");
+ othercases print_esc("vfilneg")
+ endcases;
+mskip: print_esc("mskip");
+kern: print_esc("kern");
+mkern: print_esc("mkern");
+
+@ All the work relating to glue creation has been relegated to the
+following subroutine. It does not call |build_page|, because it is
+used in at least one place where that would be a mistake.
+
+@<Declare action...@>=
+procedure append_glue;
+var s:small_number; {modifier of skip command}
+begin s:=cur_chr;
+case s of
+fil_code: cur_val:=fil_glue;
+fill_code: cur_val:=fill_glue;
+ss_code: cur_val:=ss_glue;
+fil_neg_code: cur_val:=fil_neg_glue;
+skip_code: scan_glue(glue_val);
+mskip_code: scan_glue(mu_val);
+end; {now |cur_val| points to the glue specification}
+tail_append(new_glue(cur_val));
+if s>=skip_code then
+ begin decr(glue_ref_count(cur_val));
+ if s>skip_code then subtype(tail):=mu_glue;
+ end;
+end;
+
+@ @<Declare act...@>=
+procedure append_kern;
+var s:quarterword; {|subtype| of the kern node}
+begin s:=cur_chr; scan_dimen(s=mu_glue,false,false);
+tail_append(new_kern(cur_val)); subtype(tail):=s;
+end;
+
+@ Many of the actions related to box-making are triggered by the appearance
+of braces in the input. For example, when the user says `\.{\\hbox}
+\.{to} \.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode,
+the information about the box size (100pt, |exactly|) is put onto |save_stack|
+with a level boundary word just above it, and |cur_group:=adjusted_hbox_group|;
+\TeX\ enters restricted horizontal mode to process the hlist. The right
+brace eventually causes |save_stack| to be restored to its former state,
+at which time the information about the box size (100pt, |exactly|) is
+available once again; a box is packaged and we leave restricted horizontal
+mode, appending the new box to the current list of the enclosing mode
+(in this case to the current list of vertical mode), followed by any
+vertical adjustments that were removed from the box by |hpack|.
+
+The next few sections of the program are therefore concerned with the
+treatment of left and right curly braces.
+
+@ If a left brace occurs in the middle of a page or paragraph, it simply
+introduces a new level of grouping, and the matching right brace will not have
+such a drastic effect. Such grouping affects neither the mode nor the
+current list.
+
+@<Cases of |main_control| that build...@>=
+non_math(left_brace): new_save_level(simple_group);
+any_mode(begin_group): new_save_level(semi_simple_group);
+any_mode(end_group): if cur_group=semi_simple_group then unsave
+ else off_save;
+
+@ We have to deal with errors in which braces and such things are not
+properly nested. Sometimes the user makes an error of commission by
+inserting an extra symbol, but sometimes the user makes an error of omission.
+\TeX\ can't always tell one from the other, so it makes a guess and tries
+to avoid getting into a loop.
+
+The |off_save| routine is called when the current group code is wrong. It tries
+to insert something into the user's input that will help clean off
+the top level.
+
+@<Declare act...@>=
+procedure off_save;
+var p:pointer; {inserted token}
+begin if cur_group=bottom_level then
+ @<Drop current token and complain that it was unmatched@>
+else begin back_input; p:=get_avail; link(temp_head):=p;
+ print_err("Missing ");
+ @<Prepare to insert a token that matches |cur_group|,
+ and print what it is@>;
+ print(" inserted"); ins_list(link(temp_head));
+ help5("I've inserted something that you may have forgotten.")@/
+ ("(See the <inserted text> above.)")@/
+ ("With luck, this will get me unwedged. But if you")@/
+ ("really didn't forget anything, try typing `2' now; then")@/
+ ("my insertion and my current dilemma will both disappear.");
+ error;
+ end;
+end;
+
+@ At this point, |link(temp_head)=p|, a pointer to an empty one-word node.
+
+@<Prepare to insert a token that matches |cur_group|...@>=
+case cur_group of
+semi_simple_group: begin info(p):=cs_token_flag+frozen_end_group;
+ print_esc("endgroup");
+@.Missing \\endgroup inserted@>
+ end;
+math_shift_group: begin info(p):=math_shift_token+"$"; print_char("$");
+@.Missing \$ inserted@>
+ end;
+math_left_group: begin info(p):=cs_token_flag+frozen_right; link(p):=get_avail;
+ p:=link(p); info(p):=other_token+"."; print_esc("right.");
+@.Missing \\right\hbox{.} inserted@>
+@^null delimiter@>
+ end;
+othercases begin info(p):=right_brace_token+"}"; print_char("}");
+@.Missing \} inserted@>
+ end
+endcases
+
+@ @<Drop current token and complain that it was unmatched@>=
+begin print_err("Extra "); print_cmd_chr(cur_cmd,cur_chr);
+@.Extra x@>
+help1("Things are pretty mixed up, but I think the worst is over.");@/
+error;
+end
+
+@ The routine for a |right_brace| character branches into many subcases,
+since a variety of things may happen, depending on |cur_group|. Some
+types of groups are not supposed to be ended by a right brace; error
+messages are given in hopes of pinpointing the problem. Most branches
+of this routine will be filled in later, when we are ready to understand
+them; meanwhile, we must prepare ourselves to deal with such errors.
+
+@<Cases of |main_control| that build...@>=
+any_mode(right_brace): handle_right_brace;
+
+@ @<Declare the procedure called |handle_right_brace|@>=
+procedure handle_right_brace;
+var p,@!q:pointer; {for short-term use}
+@!d:scaled; {holds |split_max_depth| in |insert_group|}
+@!f:integer; {holds |floating_penalty| in |insert_group|}
+begin case cur_group of
+simple_group: unsave;
+bottom_level: begin print_err("Too many }'s");
+@.Too many \}'s@>
+ help2("You've closed more groups than you opened.")@/
+ ("Such booboos are generally harmless, so keep going."); error;
+ end;
+semi_simple_group,math_shift_group,math_left_group: extra_right_brace;
+@t\4@>@<Cases of |handle_right_brace| where a |right_brace| triggers
+ a delayed action@>@;
+othercases confusion("rightbrace")
+@:this can't happen rightbrace}{\quad rightbrace@>
+endcases;
+end;
+
+@ @<Declare act...@>=
+procedure extra_right_brace;
+begin print_err("Extra }, or forgotten ");
+@.Extra \}, or forgotten x@>
+case cur_group of
+semi_simple_group: print_esc("endgroup");
+math_shift_group: print_char("$");
+math_left_group: print_esc("right");
+end;@/
+help5("I've deleted a group-closing symbol because it seems to be")@/
+("spurious, as in `$x}$'. But perhaps the } is legitimate and")@/
+("you forgot something else, as in `\hbox{$x}'. In such cases")@/
+("the way to recover is to insert both the forgotten and the")@/
+("deleted material, e.g., by typing `I$}'."); error;
+incr(align_state);
+end;
+
+@ Here is where we clear the parameters that are supposed to revert to their
+default values after every paragraph and when internal vertical mode is entered.
+
+@<Declare act...@>=
+procedure normal_paragraph;
+begin if looseness<>0 then eq_word_define(int_base+looseness_code,0);
+if hang_indent<>0 then eq_word_define(dimen_base+hang_indent_code,0);
+if hang_after<>1 then eq_word_define(int_base+hang_after_code,1);
+if par_shape_ptr<>null then eq_define(par_shape_loc,shape_ref,null);
+end;
+
+@ Now let's turn to the question of how \.{\\hbox} is treated. We actually
+need to consider also a slightly larger context, since constructions like
+`\.{\\setbox3=}\penalty0\.{\\hbox...}' and
+`\.{\\leaders}\penalty0\.{\\hbox...}' and
+`\.{\\lower3.8pt\\hbox...}'
+are supposed to invoke quite
+different actions after the box has been packaged. Conversely,
+constructions like `\.{\\setbox3=}' can be followed by a variety of
+different kinds of boxes, and we would like to encode such things in an
+efficient way.
+
+In other words, there are two problems: to represent the context of a box,
+and to represent its type.
+
+The first problem is solved by putting a ``context code'' on the |save_stack|,
+just below the two entries that give the dimensions produced by |scan_spec|.
+The context code is either a (signed) shift amount, or it is a large
+integer |>=box_flag|, where |box_flag=@t$2^{30}$@>|. Codes |box_flag| through
+|box_flag+255| represent `\.{\\setbox0}' through `\.{\\setbox255}';
+codes |box_flag+256| through |box_flag+511| represent `\.{\\global\\setbox0}'
+through `\.{\\global\\setbox255}';
+code |box_flag+512| represents `\.{\\shipout}'; and codes |box_flag+513|
+through |box_flag+515| represent `\.{\\leaders}', `\.{\\cleaders}',
+and `\.{\\xleaders}'.
+
+The second problem is solved by giving the command code |make_box| to all
+control sequences that produce a box, and by using the following |chr_code|
+values to distinguish between them: |box_code|, |copy_code|, |last_box_code|,
+|vsplit_code|, |vtop_code|, |vtop_code+vmode|, and |vtop_code+hmode|, where
+the latter two are used to denote \.{\\vbox} and \.{\\hbox}, respectively.
+
+@d box_flag==@'10000000000 {context code for `\.{\\setbox0}'}
+@d ship_out_flag==box_flag+512 {context code for `\.{\\shipout}'}
+@d leader_flag==box_flag+513 {context code for `\.{\\leaders}'}
+@d box_code=0 {|chr_code| for `\.{\\box}'}
+@d copy_code=1 {|chr_code| for `\.{\\copy}'}
+@d last_box_code=2 {|chr_code| for `\.{\\lastbox}'}
+@d vsplit_code=3 {|chr_code| for `\.{\\vsplit}'}
+@d vtop_code=4 {|chr_code| for `\.{\\vtop}'}
+
+@<Put each...@>=
+primitive("moveleft",hmove,1);
+@!@:move_left_}{\.{\\moveleft} primitive@>
+primitive("moveright",hmove,0);@/
+@!@:move_right_}{\.{\\moveright} primitive@>
+primitive("raise",vmove,1);
+@!@:raise_}{\.{\\raise} primitive@>
+primitive("lower",vmove,0);
+@!@:lower_}{\.{\\lower} primitive@>
+@#
+primitive("box",make_box,box_code);
+@!@:box_}{\.{\\box} primitive@>
+primitive("copy",make_box,copy_code);
+@!@:copy_}{\.{\\copy} primitive@>
+primitive("lastbox",make_box,last_box_code);
+@!@:last_box_}{\.{\\lastbox} primitive@>
+primitive("vsplit",make_box,vsplit_code);
+@!@:vsplit_}{\.{\\vsplit} primitive@>
+primitive("vtop",make_box,vtop_code);@/
+@!@:vtop_}{\.{\\vtop} primitive@>
+primitive("vbox",make_box,vtop_code+vmode);
+@!@:vbox_}{\.{\\vbox} primitive@>
+primitive("hbox",make_box,vtop_code+hmode);@/
+@!@:hbox_}{\.{\\hbox} primitive@>
+primitive("shipout",leader_ship,a_leaders-1); {|ship_out_flag=leader_flag-1|}
+@!@:ship_out_}{\.{\\shipout} primitive@>
+primitive("leaders",leader_ship,a_leaders);
+@!@:leaders_}{\.{\\leaders} primitive@>
+primitive("cleaders",leader_ship,c_leaders);
+@!@:c_leaders_}{\.{\\cleaders} primitive@>
+primitive("xleaders",leader_ship,x_leaders);
+@!@:x_leaders_}{\.{\\xleaders} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+hmove: if chr_code=1 then print_esc("moveleft")@+else print_esc("moveright");
+vmove: if chr_code=1 then print_esc("raise")@+else print_esc("lower");
+make_box: case chr_code of
+ box_code: print_esc("box");
+ copy_code: print_esc("copy");
+ last_box_code: print_esc("lastbox");
+ vsplit_code: print_esc("vsplit");
+ vtop_code: print_esc("vtop");
+ vtop_code+vmode: print_esc("vbox");
+ othercases print_esc("hbox")
+ endcases;
+leader_ship: if chr_code=a_leaders then print_esc("leaders")
+ else if chr_code=c_leaders then print_esc("cleaders")
+ else if chr_code=x_leaders then print_esc("xleaders")
+ else print_esc("shipout");
+
+@ Constructions that require a box are started by calling |scan_box| with
+a specified context code. The |scan_box| routine verifies
+that a |make_box| command comes next and then it calls |begin_box|.
+
+@<Cases of |main_control| that build...@>=
+vmode+hmove,hmode+vmove,mmode+vmove: begin t:=cur_chr;
+ scan_normal_dimen;
+ if t=0 then scan_box(cur_val)@+else scan_box(-cur_val);
+ end;
+any_mode(leader_ship): scan_box(leader_flag-a_leaders+cur_chr);
+any_mode(make_box): begin_box(0);
+
+@ The global variable |cur_box| will point to a newly made box. If the box
+is void, we will have |cur_box=null|. Otherwise we will have
+|type(cur_box)=hlist_node| or |vlist_node| or |rule_node|; the |rule_node|
+case can occur only with leaders.
+
+@<Glob...@>=
+@!cur_box:pointer; {box to be placed into its context}
+
+@ The |box_end| procedure does the right thing with |cur_box|, if
+|box_context| represents the context as explained above.
+
+@<Declare act...@>=
+procedure box_end(@!box_context:integer);
+var p:pointer; {|ord_noad| for new box in math mode}
+begin if box_context<box_flag then @<Append box |cur_box| to the current list,
+ shifted by |box_context|@>
+else if box_context<ship_out_flag then @<Store \(c)|cur_box| in a box register@>
+else if cur_box<>null then
+ if box_context>ship_out_flag then @<Append a new leader node that
+ uses |cur_box|@>
+ else ship_out(cur_box);
+end;
+
+@ The global variable |adjust_tail| will be non-null if and only if the
+current box might include adjustments that should be appended to the
+current vertical list.
+
+@<Append box |cur_box| to the current...@>=
+begin if cur_box<>null then
+ begin shift_amount(cur_box):=box_context;
+ if abs(mode)=vmode then
+ begin append_to_vlist(cur_box);
+ if adjust_tail<>null then
+ begin if adjust_head<>adjust_tail then
+ begin link(tail):=link(adjust_head); tail:=adjust_tail;
+ end;
+ adjust_tail:=null;
+ end;
+ if mode>0 then build_page;
+ end
+ else begin if abs(mode)=hmode then space_factor:=1000
+ else begin p:=new_noad;
+ math_type(nucleus(p)):=sub_box;
+ info(nucleus(p)):=cur_box; cur_box:=p;
+ end;
+ link(tail):=cur_box; tail:=cur_box;
+ end;
+ end;
+end
+
+@ @<Store \(c)|cur_box| in a box register@>=
+if box_context<box_flag+256 then
+ eq_define(box_base-box_flag+box_context,box_ref,cur_box)
+else geq_define(box_base-box_flag-256+box_context,box_ref,cur_box)
+
+@ @<Append a new leader node ...@>=
+begin @<Get the next non-blank non-relax...@>;
+if ((cur_cmd=hskip)and(abs(mode)<>vmode))or@|
+ ((cur_cmd=vskip)and(abs(mode)=vmode)) then
+ begin append_glue; subtype(tail):=box_context-(leader_flag-a_leaders);
+ leader_ptr(tail):=cur_box;
+ end
+else begin print_err("Leaders not followed by proper glue");
+@.Leaders not followed by...@>
+ help3("You should say `\leaders <box or rule><hskip or vskip>'.")@/
+ ("I found the <box or rule>, but there's no suitable")@/
+ ("<hskip or vskip>, so I'm ignoring these leaders."); back_error;
+ flush_node_list(cur_box);
+ end;
+end
+
+@ Now that we can see what eventually happens to boxes, we can consider
+the first steps in their creation. The |begin_box| routine is called when
+|box_context| is a context specification, |cur_chr| specifies the type of
+box desired, and |cur_cmd=make_box|.
+
+@<Declare act...@>=
+procedure begin_box(@!box_context:integer);
+label exit, done;
+var @!p,@!q:pointer; {run through the current list}
+@!m:quarterword; {the length of a replacement list}
+@!k:halfword; {0 or |vmode| or |hmode|}
+@!n:eight_bits; {a box number}
+begin case cur_chr of
+box_code: begin scan_eight_bit_int; cur_box:=box(cur_val);
+ box(cur_val):=null; {the box becomes void, at the same level}
+ end;
+copy_code: begin scan_eight_bit_int; cur_box:=copy_node_list(box(cur_val));
+ end;
+last_box_code: @<If the current list ends with a box node, delete it from
+ the list and make |cur_box| point to it; otherwise set |cur_box:=null|@>;
+vsplit_code: @<Split off part of a vertical box, make |cur_box| point to it@>;
+othercases @<Initiate the construction of an hbox or vbox, then |return|@>
+endcases;@/
+box_end(box_context); {in simple cases, we use the box immediately}
+exit:end;
+
+@ Note that the condition |not is_char_node(tail)| implies that |head<>tail|,
+since |head| is a one-word node.
+
+@<If the current list ends with a box node, delete it...@>=
+begin cur_box:=null;
+if abs(mode)=mmode then
+ begin you_cant; help1("Sorry; this \lastbox will be void."); error;
+ end
+else if (mode=vmode)and(head=tail) then
+ begin you_cant;
+ help2("Sorry...I usually can't take things from the current page.")@/
+ ("This \lastbox will therefore be void."); error;
+ end
+else begin if not is_char_node(tail) then
+ if (type(tail)=hlist_node)or(type(tail)=vlist_node) then
+ @<Remove the last box, unless it's part of a discretionary@>;
+ end;
+end
+
+@ @<Remove the last box...@>=
+begin q:=head;
+repeat p:=q;
+if not is_char_node(q) then if type(q)=disc_node then
+ begin for m:=1 to replace_count(q) do p:=link(p);
+ if p=tail then goto done;
+ end;
+q:=link(p);
+until q=tail;
+cur_box:=tail; shift_amount(cur_box):=0;
+tail:=p; link(p):=null;
+done:end
+
+@ Here we deal with things like `\.{\\vsplit 13 to 100pt}'.
+
+@<Split off part of a vertical box, make |cur_box| point to it@>=
+begin scan_eight_bit_int; n:=cur_val;
+if not scan_keyword("to") then
+@.to@>
+ begin print_err("Missing `to' inserted");
+@.Missing `to' inserted@>
+ help2("I'm working on `\vsplit<box number> to <dimen>';")@/
+ ("will look for the <dimen> next."); error;
+ end;
+scan_normal_dimen;
+cur_box:=vsplit(n,cur_val);
+end
+
+@ Here is where we enter restricted horizontal mode or internal vertical
+mode, in order to make a box.
+
+@<Initiate the construction of an hbox or vbox, then |return|@>=
+begin k:=cur_chr-vtop_code; saved(0):=box_context;
+if k=hmode then
+ if (box_context<box_flag)and(abs(mode)=vmode) then
+ scan_spec(adjusted_hbox_group,true)
+ else scan_spec(hbox_group,true)
+else begin if k=vmode then scan_spec(vbox_group,true)
+ else begin scan_spec(vtop_group,true); k:=vmode;
+ end;
+ normal_paragraph;
+ end;
+push_nest; mode:=-k;
+if k=vmode then
+ begin prev_depth:=ignore_depth;
+ if every_vbox<>null then begin_token_list(every_vbox,every_vbox_text);
+ end
+else begin space_factor:=1000;
+ if every_hbox<>null then begin_token_list(every_hbox,every_hbox_text);
+ end;
+return;
+end
+
+@ @<Declare act...@>=
+procedure scan_box(@!box_context:integer);
+ {the next input should specify a box or perhaps a rule}
+begin @<Get the next non-blank non-relax...@>;
+if cur_cmd=make_box then begin_box(box_context)
+else if (box_context>=leader_flag)and((cur_cmd=hrule)or(cur_cmd=vrule)) then
+ begin cur_box:=scan_rule_spec; box_end(box_context);
+ end
+else begin@t@>@;@/
+ print_err("A <box> was supposed to be here");@/
+@.A <box> was supposed to...@>
+ help3("I was expecting to see \hbox or \vbox or \copy or \box or")@/
+ ("something like that. So you might find something missing in")@/
+ ("your output. But keep trying; you can fix this later."); back_error;
+ end;
+end;
+
+@ When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or
+\.{\\vtop} construction, the |package| routine comes into action. We might
+also have to finish a paragraph that hasn't ended.
+
+@<Cases of |handle...@>=
+hbox_group: package(0);
+adjusted_hbox_group: begin adjust_tail:=adjust_head; package(0);
+ end;
+vbox_group: begin end_graf; package(0);
+ end;
+vtop_group: begin end_graf; package(vtop_code);
+ end;
+
+@ @<Declare action...@>=
+procedure package(@!c:small_number);
+var h:scaled; {height of box}
+@!p:pointer; {first node in a box}
+@!d:scaled; {max depth}
+begin d:=box_max_depth; unsave; save_ptr:=save_ptr-3;
+if mode=-hmode then cur_box:=hpack(link(head),saved(2),saved(1))
+else begin cur_box:=vpackage(link(head),saved(2),saved(1),d);
+ if c=vtop_code then @<Readjust the height and depth of |cur_box|,
+ for \.{\\vtop}@>;
+ end;
+pop_nest; box_end(saved(0));
+end;
+
+@ The height of a `\.{\\vtop}' box is inherited from the first item on its list,
+if that item is an |hlist_node|, |vlist_node|, or |rule_node|; otherwise
+the \.{\\vtop} height is zero.
+
+
+@<Readjust the height...@>=
+begin h:=0; p:=list_ptr(cur_box);
+if p<>null then if type(p)<=rule_node then h:=height(p);
+depth(cur_box):=depth(cur_box)-h+height(cur_box); height(cur_box):=h;
+end
+
+@ A paragraph begins when horizontal-mode material occurs in vertical mode,
+or when the paragraph is explicitly started by `\.{\\indent}' or
+`\.{\\noindent}'.
+
+@<Put each...@>=
+primitive("indent",start_par,1);
+@!@:indent_}{\.{\\indent} primitive@>
+primitive("noindent",start_par,0);
+@!@:no_indent_}{\.{\\noindent} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+start_par: if chr_code=0 then print_esc("noindent")@+ else print_esc("indent");
+
+@ @<Cases of |main_control| that build...@>=
+vmode+start_par: new_graf(cur_chr>0);
+vmode+letter,vmode+other_char,vmode+char_num,vmode+char_given,
+ vmode+math_shift,vmode+un_hbox,vmode+vrule,
+ vmode+accent,vmode+discretionary,vmode+hskip,vmode+valign,
+ vmode+ex_space,vmode+no_boundary:@t@>@;@/
+ begin back_input; new_graf(true);
+ end;
+
+@ @<Declare act...@>=
+function norm_min(@!h:integer):small_number;
+begin if h<=0 then norm_min:=1@+else if h>=63 then norm_min:=63@+
+else norm_min:=h;
+end;
+@#
+procedure new_graf(@!indented:boolean);
+begin prev_graf:=0;
+if (mode=vmode)or(head<>tail) then
+ tail_append(new_param_glue(par_skip_code));
+push_nest; mode:=hmode; space_factor:=1000; set_cur_lang; clang:=cur_lang;
+prev_graf:=(norm_min(left_hyphen_min)*@'100+norm_min(right_hyphen_min))
+ *@'200000+cur_lang;
+if indented then
+ begin tail:=new_null_box; link(head):=tail; width(tail):=par_indent;@+
+ end;
+if every_par<>null then begin_token_list(every_par,every_par_text);
+if nest_ptr=1 then build_page; {put |par_skip| glue on current page}
+end;
+
+@ @<Cases of |main_control| that build...@>=
+hmode+start_par,mmode+start_par: indent_in_hmode;
+
+@ @<Declare act...@>=
+procedure indent_in_hmode;
+var p,@!q:pointer;
+begin if cur_chr>0 then {\.{\\indent}}
+ begin p:=new_null_box; width(p):=par_indent;
+ if abs(mode)=hmode then space_factor:=1000
+ else begin q:=new_noad; math_type(nucleus(q)):=sub_box;
+ info(nucleus(q)):=p; p:=q;
+ end;
+ tail_append(p);
+ end;
+end;
+
+@ A paragraph ends when a |par_end| command is sensed, or when we are in
+horizontal mode when reaching the right brace of vertical-mode routines
+like \.{\\vbox}, \.{\\insert}, or \.{\\output}.
+
+@<Cases of |main_control| that build...@>=
+vmode+par_end: begin normal_paragraph;
+ if mode>0 then build_page;
+ end;
+hmode+par_end: begin if align_state<0 then off_save; {this tries to
+ recover from an alignment that didn't end properly}
+ end_graf; {this takes us to the enclosing mode, if |mode>0|}
+ if mode=vmode then build_page;
+ end;
+hmode+stop,hmode+vskip,hmode+hrule,hmode+un_vbox,hmode+halign: head_for_vmode;
+
+@ @<Declare act...@>=
+procedure head_for_vmode;
+begin if mode<0 then
+ if cur_cmd<>hrule then off_save
+ else begin print_err("You can't use `");
+ print_esc("hrule"); print("' here except with leaders");
+@.You can't use \\hrule...@>
+ help2("To put a horizontal rule in an hbox or an alignment,")@/
+ ("you should use \leaders or \hrulefill (see The TeXbook).");
+ error;
+ end
+else begin back_input; cur_tok:=par_token; back_input; token_type:=inserted;
+ end;
+end;
+
+@ @<Declare act...@>=
+procedure end_graf;
+begin if mode=hmode then
+ begin if head=tail then pop_nest {null paragraphs are ignored}
+ else line_break(widow_penalty);
+ normal_paragraph;
+ error_count:=0;
+ end;
+end;
+
+@ Insertion and adjustment and mark nodes are constructed by the following
+pieces of the program.
+
+@<Cases of |main_control| that build...@>=
+any_mode(insert),hmode+vadjust,mmode+vadjust: begin_insert_or_adjust;
+any_mode(mark): make_mark;
+
+@ @<Forbidden...@>=
+vmode+vadjust,
+
+@ @<Declare act...@>=
+procedure begin_insert_or_adjust;
+begin if cur_cmd=vadjust then cur_val:=255
+else begin scan_eight_bit_int;
+ if cur_val=255 then
+ begin print_err("You can't "); print_esc("insert"); print_int(255);
+@.You can't \\insert255@>
+ help1("I'm changing to \insert0; box 255 is special.");
+ error; cur_val:=0;
+ end;
+ end;
+saved(0):=cur_val; incr(save_ptr);
+new_save_level(insert_group); scan_left_brace; normal_paragraph;
+push_nest; mode:=-vmode; prev_depth:=ignore_depth;
+end;
+
+@ @<Cases of |handle...@>=
+insert_group: begin end_graf; q:=split_top_skip; add_glue_ref(q);
+ d:=split_max_depth; f:=floating_penalty; unsave; decr(save_ptr);
+ {now |saved(0)| is the insertion number, or 255 for |vadjust|}
+ p:=vpack(link(head),natural); pop_nest;
+ if saved(0)<255 then
+ begin tail_append(get_node(ins_node_size));
+ type(tail):=ins_node; subtype(tail):=qi(saved(0));
+ height(tail):=height(p)+depth(p); ins_ptr(tail):=list_ptr(p);
+ split_top_ptr(tail):=q; depth(tail):=d; float_cost(tail):=f;
+ end
+ else begin tail_append(get_node(small_node_size));
+ type(tail):=adjust_node;@/
+ subtype(tail):=0; {the |subtype| is not used}
+ adjust_ptr(tail):=list_ptr(p); delete_glue_ref(q);
+ end;
+ free_node(p,box_node_size);
+ if nest_ptr=0 then build_page;
+ end;
+output_group: @<Resume the page builder...@>;
+
+@ @<Declare act...@>=
+procedure make_mark;
+var p:pointer; {new node}
+begin p:=scan_toks(false,true); p:=get_node(small_node_size);
+type(p):=mark_node; subtype(p):=0; {the |subtype| is not used}
+mark_ptr(p):=def_ref; link(tail):=p; tail:=p;
+end;
+
+@ Penalty nodes get into a list via the |break_penalty| command.
+@^penalties@>
+
+@<Cases of |main_control| that build...@>=
+any_mode(break_penalty): append_penalty;
+
+@ @<Declare action...@>=
+procedure append_penalty;
+begin scan_int; tail_append(new_penalty(cur_val));
+if mode=vmode then build_page;
+end;
+
+@ The |remove_item| command removes a penalty, kern, or glue node if it
+appears at the tail of the current list, using a brute-force linear scan.
+Like \.{\\lastbox}, this command is not allowed in vertical mode (except
+internal vertical mode), since the current list in vertical mode is sent
+to the page builder. But if we happen to be able to implement it in
+vertical mode, we do.
+
+@<Cases of |main_control| that build...@>=
+any_mode(remove_item): delete_last;
+
+@ When |delete_last| is called, |cur_chr| is the |type| of node that
+will be deleted, if present.
+
+@<Declare action...@>=
+procedure delete_last;
+label exit;
+var @!p,@!q:pointer; {run through the current list}
+@!m:quarterword; {the length of a replacement list}
+begin if (mode=vmode)and(tail=head) then
+ @<Apologize for inability to do the operation now,
+ unless \.{\\unskip} follows non-glue@>
+else begin if not is_char_node(tail) then if type(tail)=cur_chr then
+ begin q:=head;
+ repeat p:=q;
+ if not is_char_node(q) then if type(q)=disc_node then
+ begin for m:=1 to replace_count(q) do p:=link(p);
+ if p=tail then return;
+ end;
+ q:=link(p);
+ until q=tail;
+ link(p):=null; flush_node_list(tail); tail:=p;
+ end;
+ end;
+exit:end;
+
+@ @<Apologize for inability to do the operation...@>=
+begin if (cur_chr<>glue_node)or(last_glue<>max_halfword) then
+ begin you_cant;
+ help2("Sorry...I usually can't take things from the current page.")@/
+ ("Try `I\vskip-\lastskip' instead.");
+ if cur_chr=kern_node then help_line[0]:=
+ ("Try `I\kern-\lastkern' instead.")
+ else if cur_chr<>glue_node then help_line[0]:=@|
+ ("Perhaps you can make the output routine do it.");
+ error;
+ end;
+end
+
+@ @<Put each...@>=
+primitive("unpenalty",remove_item,penalty_node);@/
+@!@:un_penalty_}{\.{\\unpenalty} primitive@>
+primitive("unkern",remove_item,kern_node);@/
+@!@:un_kern_}{\.{\\unkern} primitive@>
+primitive("unskip",remove_item,glue_node);@/
+@!@:un_skip_}{\.{\\unskip} primitive@>
+primitive("unhbox",un_hbox,box_code);@/
+@!@:un_hbox_}{\.{\\unhbox} primitive@>
+primitive("unhcopy",un_hbox,copy_code);@/
+@!@:un_hcopy_}{\.{\\unhcopy} primitive@>
+primitive("unvbox",un_vbox,box_code);@/
+@!@:un_vbox_}{\.{\\unvbox} primitive@>
+primitive("unvcopy",un_vbox,copy_code);@/
+@!@:un_vcopy_}{\.{\\unvcopy} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+remove_item: if chr_code=glue_node then print_esc("unskip")
+ else if chr_code=kern_node then print_esc("unkern")
+ else print_esc("unpenalty");
+un_hbox: if chr_code=copy_code then print_esc("unhcopy")
+ else print_esc("unhbox");
+un_vbox: if chr_code=copy_code then print_esc("unvcopy")
+ else print_esc("unvbox");
+
+@ The |un_hbox| and |un_vbox| commands unwrap one of the 256 current boxes.
+
+@<Cases of |main_control| that build...@>=
+vmode+un_vbox,hmode+un_hbox,mmode+un_hbox: unpackage;
+
+@ @<Declare act...@>=
+procedure unpackage;
+label exit;
+var p:pointer; {the box}
+@!c:box_code..copy_code; {should we copy?}
+begin c:=cur_chr; scan_eight_bit_int; p:=box(cur_val);
+if p=null then return;
+if (abs(mode)=mmode)or((abs(mode)=vmode)and(type(p)<>vlist_node))or@|
+ ((abs(mode)=hmode)and(type(p)<>hlist_node)) then
+ begin print_err("Incompatible list can't be unboxed");
+@.Incompatible list...@>
+ help3("Sorry, Pandora. (You sneaky devil.)")@/
+ ("I refuse to unbox an \hbox in vertical mode or vice versa.")@/
+ ("And I can't open any boxes in math mode.");@/
+ error; return;
+ end;
+if c=copy_code then link(tail):=copy_node_list(list_ptr(p))
+else begin link(tail):=list_ptr(p); box(cur_val):=null;
+ free_node(p,box_node_size);
+ end;
+while link(tail)<>null do tail:=link(tail);
+exit:end;
+
+@ @<Forbidden...@>=vmode+ital_corr,
+
+@ Italic corrections are converted to kern nodes when the |ital_corr| command
+follows a character. In math mode the same effect is achieved by appending
+a kern of zero here, since italic corrections are supplied later.
+
+@<Cases of |main_control| that build...@>=
+hmode+ital_corr: append_italic_correction;
+mmode+ital_corr: tail_append(new_kern(0));
+
+@ @<Declare act...@>=
+procedure append_italic_correction;
+label exit;
+var p:pointer; {|char_node| at the tail of the current list}
+@!f:internal_font_number; {the font in the |char_node|}
+begin if tail<>head then
+ begin if is_char_node(tail) then p:=tail
+ else if type(tail)=ligature_node then p:=lig_char(tail)
+ else return;
+ f:=font(p);
+ tail_append(new_kern(char_italic(f)(char_info(f)(character(p)))));
+ subtype(tail):=explicit;
+ end;
+exit:end;
+
+@ Discretionary nodes are easy in the common case `\.{\\-}', but in the
+general case we must process three braces full of items.
+
+@<Put each...@>=
+primitive("-",discretionary,1);
+@!@:Single-character primitives -}{\quad\.{\\-}@>
+primitive("discretionary",discretionary,0);
+@!@:discretionary_}{\.{\\discretionary} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+discretionary: if chr_code=1 then
+ print_esc("-")@+else print_esc("discretionary");
+
+@ @<Cases of |main_control| that build...@>=
+hmode+discretionary,mmode+discretionary: append_discretionary;
+
+@ The space factor does not change when we append a discretionary node,
+but it starts out as 1000 in the subsidiary lists.
+
+@<Declare act...@>=
+procedure append_discretionary;
+var c:integer; {hyphen character}
+begin tail_append(new_disc);
+if cur_chr=1 then
+ begin c:=hyphen_char[cur_font];
+ if c>=0 then if c<256 then pre_break(tail):=new_character(cur_font,c);
+ end
+else begin incr(save_ptr); saved(-1):=0; new_save_level(disc_group);
+ scan_left_brace; push_nest; mode:=-hmode; space_factor:=1000;
+ end;
+end;
+
+@ The three discretionary lists are constructed somewhat as if they were
+hboxes. A~subroutine called |build_discretionary| handles the transitions.
+(This is sort of fun.)
+
+@<Cases of |handle...@>=
+disc_group: build_discretionary;
+
+@ @<Declare act...@>=
+procedure build_discretionary;
+label done,exit;
+var p,@!q:pointer; {for link manipulation}
+@!n:integer; {length of discretionary list}
+begin unsave;
+@<Prune the current list, if necessary, until it contains only
+ |char_node|, |kern_node|, |hlist_node|, |vlist_node|, |rule_node|,
+ and |ligature_node| items; set |n| to the length of the list,
+ and set |q| to the list's tail@>;
+p:=link(head); pop_nest;
+case saved(-1) of
+0:pre_break(tail):=p;
+1:post_break(tail):=p;
+2:@<Attach list |p| to the current list, and record its length;
+ then finish up and |return|@>;
+end; {there are no other cases}
+incr(saved(-1)); new_save_level(disc_group); scan_left_brace;
+push_nest; mode:=-hmode; space_factor:=1000;
+exit:end;
+
+@ @<Attach list |p| to the current...@>=
+begin if (n>0)and(abs(mode)=mmode) then
+ begin print_err("Illegal math "); print_esc("discretionary");
+@.Illegal math \\disc...@>
+ help2("Sorry: The third part of a discretionary break must be")@/
+ ("empty, in math formulas. I had to delete your third part.");
+ flush_node_list(p); n:=0; error;
+ end
+else link(tail):=p;
+if n<=max_quarterword then replace_count(tail):=n
+else begin print_err("Discretionary list is too long");
+@.Discretionary list is too long@>
+ help2("Wow---I never thought anybody would tweak me here.")@/
+ ("You can't seriously need such a huge discretionary list?");
+ error;
+ end;
+if n>0 then tail:=q;
+decr(save_ptr); return;
+end
+
+@ During this loop, |p=link(q)| and there are |n| items preceding |p|.
+
+@<Prune the current list, if necessary...@>=
+q:=head; p:=link(q); n:=0;
+while p<>null do
+ begin if not is_char_node(p) then if type(p)>rule_node then
+ if type(p)<>kern_node then if type(p)<>ligature_node then
+ begin print_err("Improper discretionary list");
+@.Improper discretionary list@>
+ help1("Discretionary lists must contain only boxes and kerns.");@/
+ error;
+ begin_diagnostic;
+ print_nl("The following discretionary sublist has been deleted:");
+@.The following...deleted@>
+ show_box(p);
+ end_diagnostic(true);
+ flush_node_list(p); link(q):=null; goto done;
+ end;
+ q:=p; p:=link(q); incr(n);
+ end;
+done:
+
+@ We need only one more thing to complete the horizontal mode routines, namely
+the \.{\\accent} primitive.
+
+@<Cases of |main_control| that build...@>=
+hmode+accent: make_accent;
+
+@ The positioning of accents is straightforward but tedious. Given an accent
+of width |a|, designed for characters of height |x| and slant |s|;
+and given a character of width |w|, height |h|, and slant |t|: We will shift
+the accent down by |x-h|, and we will insert kern nodes that have the effect of
+centering the accent over the character and shifting the accent to the
+right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is
+absent from the font, we will simply use the other, without shifting.
+
+@<Declare act...@>=
+procedure make_accent;
+var s,@!t: real; {amount of slant}
+@!p,@!q,@!r:pointer; {character, box, and kern nodes}
+@!f:internal_font_number; {relevant font}
+@!a,@!h,@!x,@!w,@!delta:scaled; {heights and widths, as explained above}
+@!i:four_quarters; {character information}
+begin scan_char_num; f:=cur_font; p:=new_character(f,cur_val);
+if p<>null then
+ begin x:=x_height(f); s:=slant(f)/float_constant(65536);
+@^real division@>
+ a:=char_width(f)(char_info(f)(character(p)));@/
+ do_assignments;@/
+ @<Create a character node |q| for the next character,
+ but set |q:=null| if problems arise@>;
+ if q<>null then @<Append the accent with appropriate kerns,
+ then set |p:=q|@>;
+ link(tail):=p; tail:=p; space_factor:=1000;
+ end;
+end;
+
+@ @<Create a character node |q| for the next...@>=
+q:=null; f:=cur_font;
+if (cur_cmd=letter)or(cur_cmd=other_char)or(cur_cmd=char_given) then
+ q:=new_character(f,cur_chr)
+else if cur_cmd=char_num then
+ begin scan_char_num; q:=new_character(f,cur_val);
+ end
+else back_input
+
+@ The kern nodes appended here must be distinguished from other kerns, lest
+they be wiped away by the hyphenation algorithm or by a previous line break.
+
+The two kerns are computed with (machine-dependent) |real| arithmetic, but
+their sum is machine-independent; the net effect is machine-independent,
+because the user cannot remove these nodes nor access them via \.{\\lastkern}.
+
+@<Append the accent with appropriate kerns...@>=
+begin t:=slant(f)/float_constant(65536);
+@^real division@>
+i:=char_info(f)(character(q));
+w:=char_width(f)(i); h:=char_height(f)(height_depth(i));
+if h<>x then {the accent must be shifted up or down}
+ begin p:=hpack(p,natural); shift_amount(p):=x-h;
+ end;
+delta:=round((w-a)/float_constant(2)+h*t-x*s);
+@^real multiplication@>
+@^real addition@>
+r:=new_kern(delta); subtype(r):=acc_kern; link(tail):=r; link(r):=p;
+tail:=new_kern(-a-delta); subtype(tail):=acc_kern; link(p):=tail; p:=q;
+end
+
+@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner
+into |main_control|, it might be that the user has foolishly inserted
+one of them into something that has nothing to do with alignment. But it is
+far more likely that a left brace or right brace has been omitted, since
+|get_next| takes actions appropriate to alignment only when `\.{\\cr}'
+or `\.{\\span}' or tab marks occur with |align_state=0|. The following
+program attempts to make an appropriate recovery.
+
+@<Cases of |main_control| that build...@>=
+any_mode(car_ret), any_mode(tab_mark): align_error;
+any_mode(no_align): no_align_error;
+any_mode(omit): omit_error;
+
+@ @<Declare act...@>=
+procedure align_error;
+begin if abs(align_state)>2 then
+ @<Express consternation over the fact that no alignment is in progress@>
+else begin back_input;
+ if align_state<0 then
+ begin print_err("Missing { inserted");
+@.Missing \{ inserted@>
+ incr(align_state); cur_tok:=left_brace_token+"{";
+ end
+ else begin print_err("Missing } inserted");
+@.Missing \} inserted@>
+ decr(align_state); cur_tok:=right_brace_token+"}";
+ end;
+ help3("I've put in what seems to be necessary to fix")@/
+ ("the current column of the current alignment.")@/
+ ("Try to go on, since this might almost work."); ins_error;
+ end;
+end;
+
+@ @<Express consternation...@>=
+begin print_err("Misplaced "); print_cmd_chr(cur_cmd,cur_chr);
+@.Misplaced \&@>
+@.Misplaced \\span@>
+@.Misplaced \\cr@>
+if cur_tok=tab_token+"&" then
+ begin help6("I can't figure out why you would want to use a tab mark")@/
+ ("here. If you just want an ampersand, the remedy is")@/
+ ("simple: Just type `I\&' now. But if some right brace")@/
+ ("up above has ended a previous alignment prematurely,")@/
+ ("you're probably due for more error messages, and you")@/
+ ("might try typing `S' now just to see what is salvageable.");
+ end
+else begin help5("I can't figure out why you would want to use a tab mark")@/
+ ("or \cr or \span just now. If something like a right brace")@/
+ ("up above has ended a previous alignment prematurely,")@/
+ ("you're probably due for more error messages, and you")@/
+ ("might try typing `S' now just to see what is salvageable.");
+ end;
+error;
+end
+
+@ The help messages here contain a little white lie, since \.{\\noalign}
+and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'.
+
+@<Declare act...@>=
+procedure no_align_error;
+begin print_err("Misplaced "); print_esc("noalign");
+@.Misplaced \\noalign@>
+help2("I expect to see \noalign only after the \cr of")@/
+ ("an alignment. Proceed, and I'll ignore this case."); error;
+end;
+procedure omit_error;
+begin print_err("Misplaced "); print_esc("omit");
+@.Misplaced \\omit@>
+help2("I expect to see \omit only after tab marks or the \cr of")@/
+ ("an alignment. Proceed, and I'll ignore this case."); error;
+end;
+
+@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}.
+Let's take a look at what happens when they are used correctly.
+
+@<Cases of |main_control| that build...@>=
+vmode+halign,hmode+valign:init_align;
+mmode+halign: if privileged then
+ if cur_group=math_shift_group then init_align
+ else off_save;
+vmode+endv,hmode+endv: do_endv;
+
+@ An |align_group| code is supposed to remain on the |save_stack|
+during an entire alignment, until |fin_align| removes it.
+
+A devious user might force an |endv| command to occur just about anywhere;
+we must defeat such hacks.
+
+@<Declare act...@>=
+procedure do_endv;
+begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
+while (input_stack[base_ptr].index_field<>v_template) and
+ (input_stack[base_ptr].loc_field=null) and
+ (input_stack[base_ptr].state_field=token_list) do decr(base_ptr);
+if (input_stack[base_ptr].index_field<>v_template) or
+ (input_stack[base_ptr].loc_field<>null) or
+ (input_stack[base_ptr].state_field<>token_list) then
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+ if cur_group=align_group then
+ begin end_graf;
+ if fin_col then fin_row;
+ end
+else off_save;
+end;
+
+@ @<Cases of |handle_right_brace|...@>=
+align_group: begin back_input; cur_tok:=cs_token_flag+frozen_cr;
+ print_err("Missing "); print_esc("cr"); print(" inserted");
+@.Missing \\cr inserted@>
+ help1("I'm guessing that you meant to end an alignment here.");
+ ins_error;
+ end;
+
+@ @<Cases of |handle_right_brace|...@>=
+no_align_group: begin end_graf; unsave; align_peek;
+ end;
+
+@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|.
+
+@<Cases of |main_control| that build...@>=
+any_mode(end_cs_name): cs_error;
+
+@ @<Declare act...@>=
+procedure cs_error;
+begin print_err("Extra "); print_esc("endcsname");
+@.Extra \\endcsname@>
+help1("I'm ignoring this, since I wasn't doing a \csname.");
+error;
+end;
+
+@* \[48] Building math lists.
+The routines that \TeX\ uses to create mlists are similar to those we have
+just seen for the generation of hlists and vlists. But it is necessary to
+make ``noads'' as well as nodes, so the reader should review the
+discussion of math mode data structures before trying to make sense out of
+the following program.
+
+Here is a little routine that needs to be done whenever a subformula
+is about to be processed. The parameter is a code like |math_group|.
+
+@<Declare act...@>=
+procedure push_math(@!c:group_code);
+begin push_nest; mode:=-mmode; incompleat_noad:=null; new_save_level(c);
+end;
+
+@ We get into math mode from horizontal mode when a `\.\$' (i.e., a
+|math_shift| character) is scanned. We must check to see whether this
+`\.\$' is immediately followed by another, in case display math mode is
+called for.
+
+@<Cases of |main_control| that build...@>=
+hmode+math_shift:init_math;
+
+@ @<Declare act...@>=
+procedure init_math;
+label reswitch,found,not_found,done;
+var w:scaled; {new or partial |pre_display_size|}
+@!l:scaled; {new |display_width|}
+@!s:scaled; {new |display_indent|}
+@!p:pointer; {current node when calculating |pre_display_size|}
+@!q:pointer; {glue specification when calculating |pre_display_size|}
+@!f:internal_font_number; {font in current |char_node|}
+@!n:integer; {scope of paragraph shape specification}
+@!v:scaled; {|w| plus possible glue amount}
+@!d:scaled; {increment to |v|}
+begin get_token; {|get_x_token| would fail on \.{\\ifmmode}\thinspace!}
+if (cur_cmd=math_shift)and(mode>0) then @<Go into display math mode@>
+else begin back_input; @<Go into ordinary math mode@>;
+ end;
+end;
+
+@ @<Go into ordinary math mode@>=
+begin push_math(math_shift_group); eq_word_define(int_base+cur_fam_code,-1);
+if every_math<>null then begin_token_list(every_math,every_math_text);
+end
+
+@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or
+`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively;
+the value of |cur_chr| is placed onto |save_stack| for safe keeping.
+
+@<Cases of |main_control| that build...@>=
+mmode+eq_no: if privileged then
+ if cur_group=math_shift_group then start_eq_no
+ else off_save;
+
+@ @<Put each...@>=
+primitive("eqno",eq_no,0);
+@!@:eq_no_}{\.{\\eqno} primitive@>
+primitive("leqno",eq_no,1);
+@!@:leq_no_}{\.{\\leqno} primitive@>
+
+@ When \TeX\ is in display math mode, |cur_group=math_shift_group|,
+so it is not necessary for the |start_eq_no| procedure to test for
+this condition.
+
+@<Declare act...@>=
+procedure start_eq_no;
+begin saved(0):=cur_chr; incr(save_ptr);
+@<Go into ordinary math mode@>;
+end;
+
+@ @<Cases of |print_cmd_chr|...@>=
+eq_no:if chr_code=1 then print_esc("leqno")@+else print_esc("eqno");
+
+@ @<Forbidden...@>=non_math(eq_no),
+
+@ When we enter display math mode, we need to call |line_break| to
+process the partial paragraph that has just been interrupted by the
+display. Then we can set the proper values of |display_width| and
+|display_indent| and |pre_display_size|.
+
+@<Go into display math mode@>=
+begin if head=tail then {`\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}'}
+ begin pop_nest; w:=-max_dimen;
+ end
+else begin line_break(display_widow_penalty);@/
+ @<Calculate the natural width, |w|, by which the characters of the
+ final line extend to the right of the reference point,
+ plus two ems; or set |w:=max_dimen| if the non-blank information
+ on that line is affected by stretching or shrinking@>;
+ end;
+{now we are in vertical mode, working on the list that will contain the display}
+@<Calculate the length, |l|, and the shift amount, |s|, of the display lines@>;
+push_math(math_shift_group); mode:=mmode;
+eq_word_define(int_base+cur_fam_code,-1);@/
+eq_word_define(dimen_base+pre_display_size_code,w);
+eq_word_define(dimen_base+display_width_code,l);
+eq_word_define(dimen_base+display_indent_code,s);
+if every_display<>null then begin_token_list(every_display,every_display_text);
+if nest_ptr=1 then build_page;
+end
+
+@ @<Calculate the natural width, |w|, by which...@>=
+v:=shift_amount(just_box)+2*quad(cur_font); w:=-max_dimen;
+p:=list_ptr(just_box);
+while p<>null do
+ begin @<Let |d| be the natural width of node |p|;
+ if the node is ``visible,'' |goto found|;
+ if the node is glue that stretches or shrinks, set |v:=max_dimen|@>;
+ if v<max_dimen then v:=v+d;
+ goto not_found;
+ found: if v<max_dimen then
+ begin v:=v+d; w:=v;
+ end
+ else begin w:=max_dimen; goto done;
+ end;
+ not_found: p:=link(p);
+ end;
+done:
+
+@ @<Let |d| be the natural width of node |p|...@>=
+reswitch: if is_char_node(p) then
+ begin f:=font(p); d:=char_width(f)(char_info(f)(character(p)));
+ goto found;
+ end;
+case type(p) of
+hlist_node,vlist_node,rule_node: begin d:=width(p); goto found;
+ end;
+ligature_node:@<Make node |p| look like a |char_node|...@>;
+kern_node,math_node: d:=width(p);
+glue_node:@<Let |d| be the natural width of this glue; if stretching
+ or shrinking, set |v:=max_dimen|; |goto found| in the case of leaders@>;
+whatsit_node: @<Let |d| be the width of the whatsit |p|@>;
+othercases d:=0
+endcases
+
+@ We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set|
+values, since such values are subject to system-dependent rounding.
+System-dependent numbers are not allowed to infiltrate parameters like
+|pre_display_size|, since \TeX82 is supposed to make the same decisions on all
+machines.
+
+@<Let |d| be the natural width of this glue...@>=
+begin q:=glue_ptr(p); d:=width(q);
+if glue_sign(just_box)=stretching then
+ begin if (glue_order(just_box)=stretch_order(q))and@|
+ (stretch(q)<>0) then
+ v:=max_dimen;
+ end
+else if glue_sign(just_box)=shrinking then
+ begin if (glue_order(just_box)=shrink_order(q))and@|
+ (shrink(q)<>0) then
+ v:=max_dimen;
+ end;
+if subtype(p)>=a_leaders then goto found;
+end
+
+@ A displayed equation is considered to be three lines long, so we
+calculate the length and offset of line number |prev_graf+2|.
+
+@<Calculate the length, |l|, ...@>=
+if par_shape_ptr=null then
+ if (hang_indent<>0)and@|
+ (((hang_after>=0)and(prev_graf+2>hang_after))or@|
+ (prev_graf+1<-hang_after)) then
+ begin l:=hsize-abs(hang_indent);
+ if hang_indent>0 then s:=hang_indent@+else s:=0;
+ end
+ else begin l:=hsize; s:=0;
+ end
+else begin n:=info(par_shape_ptr);
+ if prev_graf+2>=n then p:=par_shape_ptr+2*n
+ else p:=par_shape_ptr+2*(prev_graf+2);
+ s:=mem[p-1].sc; l:=mem[p].sc;
+ end
+
+@ Subformulas of math formulas cause a new level of math mode to be entered,
+on the semantic nest as well as the save stack. These subformulas arise in
+several ways: (1)~A left brace by itself indicates the beginning of a
+subformula that will be put into a box, thereby freezing its glue and
+preventing line breaks. (2)~A subscript or superscript is treated as a
+subformula if it is not a single character; the same applies to
+the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive
+initiates a subformula that will be terminated by a matching \.{\\right}.
+The group codes placed on |save_stack| in these three cases are
+|math_group|, |math_group|, and |math_left_group|, respectively.
+
+Here is the code that handles case (1); the other cases are not quite as
+trivial, so we shall consider them later.
+
+@<Cases of |main_control| that build...@>=
+mmode+left_brace: begin tail_append(new_noad);
+ back_input; scan_math(nucleus(tail));
+ end;
+
+@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad are
+broken down into subfields called |math_type| and either |info| or
+|(fam,character)|. The job of |scan_math| is to figure out what to place
+in one of these principal fields; it looks at the subformula that
+comes next in the input, and places an encoding of that subformula
+into a given word of |mem|.
+
+@d fam_in_range==((cur_fam>=0)and(cur_fam<16))
+
+@<Declare act...@>=
+procedure scan_math(@!p:pointer);
+label restart,reswitch,exit;
+var c:integer; {math character code}
+begin restart:@<Get the next non-blank non-relax...@>;
+reswitch:case cur_cmd of
+letter,other_char,char_given: begin c:=ho(math_code(cur_chr));
+ if c=@'100000 then
+ begin @<Treat |cur_chr| as an active character@>;
+ goto restart;
+ end;
+ end;
+char_num: begin scan_char_num; cur_chr:=cur_val; cur_cmd:=char_given;
+ goto reswitch;
+ end;
+math_char_num: begin scan_fifteen_bit_int; c:=cur_val;
+ end;
+math_given: c:=cur_chr;
+delim_num: begin scan_twenty_seven_bit_int; c:=cur_val div @'10000;
+ end;
+othercases @<Scan a subformula enclosed in braces and |return|@>
+endcases;@/
+math_type(p):=math_char; character(p):=qi(c mod 256);
+if (c>=var_code)and fam_in_range then fam(p):=cur_fam
+else fam(p):=(c div 256) mod 16;
+exit:end;
+
+@ An active character that is an |outer_call| is allowed here.
+
+@<Treat |cur_chr|...@>=
+begin cur_cs:=cur_chr+active_base;
+cur_cmd:=eq_type(cur_cs); cur_chr:=equiv(cur_cs);
+x_token; back_input;
+end
+
+@ The pointer |p| is placed on |save_stack| while a complex subformula
+is being scanned.
+
+@<Scan a subformula...@>=
+begin back_input; scan_left_brace;@/
+saved(0):=p; incr(save_ptr); push_math(math_group); return;
+end
+
+@ The simplest math formula is, of course, `\.{\${ }\$}', when no noads are
+generated. The next simplest cases involve a single character, e.g.,
+`\.{\$x\$}'. Even though such cases may not seem to be very interesting,
+the reader can perhaps understand how happy the author was when `\.{\$x\$}'
+was first properly typeset by \TeX. The code in this section was used.
+@^Knuth, Donald Ervin@>
+
+@<Cases of |main_control| that build...@>=
+mmode+letter,mmode+other_char,mmode+char_given:
+ set_math_char(ho(math_code(cur_chr)));
+mmode+char_num: begin scan_char_num; cur_chr:=cur_val;
+ set_math_char(ho(math_code(cur_chr)));
+ end;
+mmode+math_char_num: begin scan_fifteen_bit_int; set_math_char(cur_val);
+ end;
+mmode+math_given: set_math_char(cur_chr);
+mmode+delim_num: begin scan_twenty_seven_bit_int;
+ set_math_char(cur_val div @'10000);
+ end;
+
+@ The |set_math_char| procedure creates a new noad appropriate to a given
+math code, and appends it to the current mlist. However, if the math code
+is sufficiently large, the |cur_chr| is treated as an active character and
+nothing is appended.
+
+@<Declare act...@>=
+procedure set_math_char(@!c:integer);
+var p:pointer; {the new noad}
+begin if c>=@'100000 then
+ @<Treat |cur_chr|...@>
+else begin p:=new_noad; math_type(nucleus(p)):=math_char;
+ character(nucleus(p)):=qi(c mod 256);
+ fam(nucleus(p)):=(c div 256) mod 16;
+ if c>=var_code then
+ begin if fam_in_range then fam(nucleus(p)):=cur_fam;
+ type(p):=ord_noad;
+ end
+ else type(p):=ord_noad+(c div @'10000);
+ link(tail):=p; tail:=p;
+ end;
+end;
+
+@ Primitive math operators like \.{\\mathop} and \.{\\underline} are given
+the command code |math_comp|, supplemented by the noad type that they
+generate.
+
+@<Put each...@>=
+primitive("mathord",math_comp,ord_noad);
+@!@:math_ord_}{\.{\\mathord} primitive@>
+primitive("mathop",math_comp,op_noad);
+@!@:math_op_}{\.{\\mathop} primitive@>
+primitive("mathbin",math_comp,bin_noad);
+@!@:math_bin_}{\.{\\mathbin} primitive@>
+primitive("mathrel",math_comp,rel_noad);
+@!@:math_rel_}{\.{\\mathrel} primitive@>
+primitive("mathopen",math_comp,open_noad);
+@!@:math_open_}{\.{\\mathopen} primitive@>
+primitive("mathclose",math_comp,close_noad);
+@!@:math_close_}{\.{\\mathclose} primitive@>
+primitive("mathpunct",math_comp,punct_noad);
+@!@:math_punct_}{\.{\\mathpunct} primitive@>
+primitive("mathinner",math_comp,inner_noad);
+@!@:math_inner_}{\.{\\mathinner} primitive@>
+primitive("underline",math_comp,under_noad);
+@!@:underline_}{\.{\\underline} primitive@>
+primitive("overline",math_comp,over_noad);@/
+@!@:overline_}{\.{\\overline} primitive@>
+primitive("displaylimits",limit_switch,normal);
+@!@:display_limits_}{\.{\\displaylimits} primitive@>
+primitive("limits",limit_switch,limits);
+@!@:limits_}{\.{\\limits} primitive@>
+primitive("nolimits",limit_switch,no_limits);
+@!@:no_limits_}{\.{\\nolimits} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+math_comp: case chr_code of
+ ord_noad: print_esc("mathord");
+ op_noad: print_esc("mathop");
+ bin_noad: print_esc("mathbin");
+ rel_noad: print_esc("mathrel");
+ open_noad: print_esc("mathopen");
+ close_noad: print_esc("mathclose");
+ punct_noad: print_esc("mathpunct");
+ inner_noad: print_esc("mathinner");
+ under_noad: print_esc("underline");
+ othercases print_esc("overline")
+ endcases;
+limit_switch: if chr_code=limits then print_esc("limits")
+ else if chr_code=no_limits then print_esc("nolimits")
+ else print_esc("displaylimits");
+
+@ @<Cases of |main_control| that build...@>=
+mmode+math_comp: begin tail_append(new_noad);
+ type(tail):=cur_chr; scan_math(nucleus(tail));
+ end;
+mmode+limit_switch: math_limit_switch;
+
+@ @<Declare act...@>=
+procedure math_limit_switch;
+label exit;
+begin if head<>tail then if type(tail)=op_noad then
+ begin subtype(tail):=cur_chr; return;
+ end;
+print_err("Limit controls must follow a math operator");
+@.Limit controls must follow...@>
+help1("I'm ignoring this misplaced \limits or \nolimits command."); error;
+exit:end;
+
+@ Delimiter fields of noads are filled in by the |scan_delimiter| routine.
+The first parameter of this procedure is the |mem| address where the
+delimiter is to be placed; the second tells if this delimiter follows
+\.{\\radical} or not.
+
+@<Declare act...@>=
+procedure scan_delimiter(@!p:pointer;@!r:boolean);
+begin if r then scan_twenty_seven_bit_int
+else begin @<Get the next non-blank non-relax...@>;
+ case cur_cmd of
+ letter,other_char: cur_val:=del_code(cur_chr);
+ delim_num: scan_twenty_seven_bit_int;
+ othercases cur_val:=-1
+ endcases;
+ end;
+if cur_val<0 then @<Report that an invalid delimiter code is being changed
+ to null; set~|cur_val:=0|@>;
+small_fam(p):=(cur_val div @'4000000) mod 16;
+small_char(p):=qi((cur_val div @'10000) mod 256);
+large_fam(p):=(cur_val div 256) mod 16;
+large_char(p):=qi(cur_val mod 256);
+end;
+
+@ @<Report that an invalid delimiter...@>=
+begin print_err("Missing delimiter (. inserted)");
+@.Missing delimiter...@>
+help6("I was expecting to see something like `(' or `\{' or")@/
+ ("`\}' here. If you typed, e.g., `{' instead of `\{', you")@/
+ ("should probably delete the `{' by typing `1' now, so that")@/
+ ("braces don't get unbalanced. Otherwise just proceed.")@/
+ ("Acceptable delimiters are characters whose \delcode is")@/
+ ("nonnegative, or you can use `\delimiter <delimiter code>'.");
+back_error; cur_val:=0;
+end
+
+@ @<Cases of |main_control| that build...@>=
+mmode+radical:math_radical;
+
+@ @<Declare act...@>=
+procedure math_radical;
+begin tail_append(get_node(radical_noad_size));
+type(tail):=radical_noad; subtype(tail):=normal;
+mem[nucleus(tail)].hh:=empty_field;
+mem[subscr(tail)].hh:=empty_field;
+mem[supscr(tail)].hh:=empty_field;
+scan_delimiter(left_delimiter(tail),true); scan_math(nucleus(tail));
+end;
+
+@ @<Cases of |main_control| that build...@>=
+mmode+accent,mmode+math_accent:math_ac;
+
+@ @<Declare act...@>=
+procedure math_ac;
+begin if cur_cmd=accent then
+ @<Complain that the user should have said \.{\\mathaccent}@>;
+tail_append(get_node(accent_noad_size));
+type(tail):=accent_noad; subtype(tail):=normal;
+mem[nucleus(tail)].hh:=empty_field;
+mem[subscr(tail)].hh:=empty_field;
+mem[supscr(tail)].hh:=empty_field;
+math_type(accent_chr(tail)):=math_char;
+scan_fifteen_bit_int;
+character(accent_chr(tail)):=qi(cur_val mod 256);
+if (cur_val>=var_code)and fam_in_range then fam(accent_chr(tail)):=cur_fam
+else fam(accent_chr(tail)):=(cur_val div 256) mod 16;
+scan_math(nucleus(tail));
+end;
+
+@ @<Complain that the user should have said \.{\\mathaccent}@>=
+begin print_err("Please use "); print_esc("mathaccent");
+print(" for accents in math mode");
+@.Please use \\mathaccent...@>
+help2("I'm changing \accent to \mathaccent here; wish me luck.")@/
+ ("(Accents are not the same in formulas as they are in text.)");
+error;
+end
+
+@ @<Cases of |main_control| that build...@>=
+mmode+vcenter: begin scan_spec(vcenter_group,false); normal_paragraph;
+ push_nest; mode:=-vmode; prev_depth:=ignore_depth;
+ if every_vbox<>null then begin_token_list(every_vbox,every_vbox_text);
+ end;
+
+@ @<Cases of |handle...@>=
+vcenter_group: begin end_graf; unsave; save_ptr:=save_ptr-2;
+ p:=vpack(link(head),saved(1),saved(0)); pop_nest;
+ tail_append(new_noad); type(tail):=vcenter_noad;
+ math_type(nucleus(tail)):=sub_box; info(nucleus(tail)):=p;
+ end;
+
+@ The routine that inserts a |style_node| holds no surprises.
+
+@<Put each...@>=
+primitive("displaystyle",math_style,display_style);
+@!@:display_style_}{\.{\\displaystyle} primitive@>
+primitive("textstyle",math_style,text_style);
+@!@:text_style_}{\.{\\textstyle} primitive@>
+primitive("scriptstyle",math_style,script_style);
+@!@:script_style_}{\.{\\scriptstyle} primitive@>
+primitive("scriptscriptstyle",math_style,script_script_style);
+@!@:script_script_style_}{\.{\\scriptscriptstyle} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+math_style: print_style(chr_code);
+
+@ @<Cases of |main_control| that build...@>=
+mmode+math_style: tail_append(new_style(cur_chr));
+mmode+non_script: begin tail_append(new_glue(zero_glue));
+ subtype(tail):=cond_math_glue;
+ end;
+mmode+math_choice: append_choices;
+
+@ The routine that scans the four mlists of a \.{\\mathchoice} is very
+much like the routine that builds discretionary nodes.
+
+@<Declare act...@>=
+procedure append_choices;
+begin tail_append(new_choice); incr(save_ptr); saved(-1):=0;
+push_math(math_choice_group); scan_left_brace;
+end;
+
+@ @<Cases of |handle_right_brace|...@>=
+math_choice_group: build_choices;
+
+@ @<Declare act...@>=
+@t\4@>@<Declare the function called |fin_mlist|@>@t@>@;@/
+procedure build_choices;
+label exit;
+var p:pointer; {the current mlist}
+begin unsave; p:=fin_mlist(null);
+case saved(-1) of
+0:display_mlist(tail):=p;
+1:text_mlist(tail):=p;
+2:script_mlist(tail):=p;
+3:begin script_script_mlist(tail):=p; decr(save_ptr); return;
+ end;
+end; {there are no other cases}
+incr(saved(-1)); push_math(math_choice_group); scan_left_brace;
+exit:end;
+
+@ Subscripts and superscripts are attached to the previous nucleus by the
+@^superscripts@>@^subscripts@>
+action procedure called |sub_sup|. We use the facts that |sub_mark=sup_mark+1|
+and |subscr(p)=supscr(p)+1|.
+
+@<Cases of |main_control| that build...@>=
+mmode+sub_mark,mmode+sup_mark: sub_sup;
+
+@ @<Declare act...@>=
+procedure sub_sup;
+var t:small_number; {type of previous sub/superscript}
+@!p:pointer; {field to be filled by |scan_math|}
+begin t:=empty; p:=null;
+if tail<>head then if scripts_allowed(tail) then
+ begin p:=supscr(tail)+cur_cmd-sup_mark; {|supscr| or |subscr|}
+ t:=math_type(p);
+ end;
+if (p=null)or(t<>empty) then @<Insert a dummy noad to be sub/superscripted@>;
+scan_math(p);
+end;
+
+@ @<Insert a dummy...@>=
+begin tail_append(new_noad);
+p:=supscr(tail)+cur_cmd-sup_mark; {|supscr| or |subscr|}
+if t<>empty then
+ begin if cur_cmd=sup_mark then
+ begin print_err("Double superscript");
+@.Double superscript@>
+ help1("I treat `x^1^2' essentially like `x^1{}^2'.");
+ end
+ else begin print_err("Double subscript");
+@.Double subscript@>
+ help1("I treat `x_1_2' essentially like `x_1{}_2'.");
+ end;
+ error;
+ end;
+end
+
+@ An operation like `\.{\\over}' causes the current mlist to go into a
+state of suspended animation: |incompleat_noad| points to a |fraction_noad|
+that contains the mlist-so-far as its numerator, while the denominator
+is yet to come. Finally when the mlist is finished, the denominator will
+go into the incompleat fraction noad, and that noad will become the
+whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}'
+delimiters.
+
+@d above_code=0 { `\.{\\above}' }
+@d over_code=1 { `\.{\\over}' }
+@d atop_code=2 { `\.{\\atop}' }
+@d delimited_code=3 { `\.{\\abovewithdelims}', etc.}
+
+@<Put each...@>=
+primitive("above",above,above_code);@/
+@!@:above_}{\.{\\above} primitive@>
+primitive("over",above,over_code);@/
+@!@:over_}{\.{\\over} primitive@>
+primitive("atop",above,atop_code);@/
+@!@:atop_}{\.{\\atop} primitive@>
+primitive("abovewithdelims",above,delimited_code+above_code);@/
+@!@:above_with_delims_}{\.{\\abovewithdelims} primitive@>
+primitive("overwithdelims",above,delimited_code+over_code);@/
+@!@:over_with_delims_}{\.{\\overwithdelims} primitive@>
+primitive("atopwithdelims",above,delimited_code+atop_code);
+@!@:atop_with_delims_}{\.{\\atopwithdelims} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+above: case chr_code of
+ over_code:print_esc("over");
+ atop_code:print_esc("atop");
+ delimited_code+above_code:print_esc("abovewithdelims");
+ delimited_code+over_code:print_esc("overwithdelims");
+ delimited_code+atop_code:print_esc("atopwithdelims");
+ othercases print_esc("above")
+ endcases;
+
+@ @<Cases of |main_control| that build...@>=
+mmode+above: math_fraction;
+
+@ @<Declare act...@>=
+procedure math_fraction;
+var c:small_number; {the type of generalized fraction we are scanning}
+begin c:=cur_chr;
+if incompleat_noad<>null then
+ @<Ignore the fraction operation and complain about this ambiguous case@>
+else begin incompleat_noad:=get_node(fraction_noad_size);
+ type(incompleat_noad):=fraction_noad;
+ subtype(incompleat_noad):=normal;
+ math_type(numerator(incompleat_noad)):=sub_mlist;
+ info(numerator(incompleat_noad)):=link(head);
+ mem[denominator(incompleat_noad)].hh:=empty_field;
+ mem[left_delimiter(incompleat_noad)].qqqq:=null_delimiter;
+ mem[right_delimiter(incompleat_noad)].qqqq:=null_delimiter;@/
+ link(head):=null; tail:=head;
+ @<Use code |c| to distinguish between generalized fractions@>;
+ end;
+end;
+
+@ @<Use code |c|...@>=
+if c>=delimited_code then
+ begin scan_delimiter(left_delimiter(incompleat_noad),false);
+ scan_delimiter(right_delimiter(incompleat_noad),false);
+ end;
+case c mod delimited_code of
+above_code: begin scan_normal_dimen;
+ thickness(incompleat_noad):=cur_val;
+ end;
+over_code: thickness(incompleat_noad):=default_code;
+atop_code: thickness(incompleat_noad):=0;
+end {there are no other cases}
+
+@ @<Ignore the fraction...@>=
+begin if c>=delimited_code then
+ begin scan_delimiter(garbage,false); scan_delimiter(garbage,false);
+ end;
+if c mod delimited_code=above_code then scan_normal_dimen;
+print_err("Ambiguous; you need another { and }");
+@.Ambiguous...@>
+help3("I'm ignoring this fraction specification, since I don't")@/
+ ("know whether a construction like `x \over y \over z'")@/
+ ("means `{x \over y} \over z' or `x \over {y \over z}'.");
+error;
+end
+
+@ At the end of a math formula or subformula, the |fin_mlist| routine is
+called upon to return a pointer to the newly completed mlist, and to
+pop the nest back to the enclosing semantic level. The parameter to
+|fin_mlist|, if not null, points to a |right_noad| that ends the
+current mlist; this |right_noad| has not yet been appended.
+
+@<Declare the function called |fin_mlist|@>=
+function fin_mlist(@!p:pointer):pointer;
+var q:pointer; {the mlist to return}
+begin if incompleat_noad<>null then @<Compleat the incompleat noad@>
+else begin link(tail):=p; q:=link(head);
+ end;
+pop_nest; fin_mlist:=q;
+end;
+
+@ @<Compleat...@>=
+begin math_type(denominator(incompleat_noad)):=sub_mlist;
+info(denominator(incompleat_noad)):=link(head);
+if p=null then q:=incompleat_noad
+else begin q:=info(numerator(incompleat_noad));
+ if type(q)<>left_noad then confusion("right");
+@:this can't happen right}{\quad right@>
+ info(numerator(incompleat_noad)):=link(q);
+ link(q):=incompleat_noad; link(incompleat_noad):=p;
+ end;
+end
+
+@ Now at last we're ready to see what happens when a right brace occurs
+in a math formula. Two special cases are simplified here: Braces are effectively
+removed when they surround a single Ord without sub/superscripts, or when they
+surround an accent that is the nucleus of an Ord atom.
+
+@<Cases of |handle...@>=
+math_group: begin unsave; decr(save_ptr);@/
+ math_type(saved(0)):=sub_mlist; p:=fin_mlist(null); info(saved(0)):=p;
+ if p<>null then if link(p)=null then
+ if type(p)=ord_noad then
+ begin if math_type(subscr(p))=empty then
+ if math_type(supscr(p))=empty then
+ begin mem[saved(0)].hh:=mem[nucleus(p)].hh;
+ free_node(p,noad_size);
+ end;
+ end
+ else if type(p)=accent_noad then if saved(0)=nucleus(tail) then
+ if type(tail)=ord_noad then @<Replace the tail of the list by |p|@>;
+ end;
+
+@ @<Replace the tail...@>=
+begin q:=head; while link(q)<>tail do q:=link(q);
+link(q):=p; free_node(tail,noad_size); tail:=p;
+end
+
+@ We have dealt with all constructions of math mode except `\.{\\left}' and
+`\.{\\right}', so the picture is completed by the following sections of
+the program.
+
+@<Put each...@>=
+primitive("left",left_right,left_noad);
+@!@:left_}{\.{\\left} primitive@>
+primitive("right",left_right,right_noad);
+@!@:right_}{\.{\\right} primitive@>
+text(frozen_right):="right"; eqtb[frozen_right]:=eqtb[cur_val];
+
+@ @<Cases of |print_cmd_chr|...@>=
+left_right: if chr_code=left_noad then print_esc("left")
+else print_esc("right");
+
+@ @<Cases of |main_control| that build...@>=
+mmode+left_right: math_left_right;
+
+@ @<Declare act...@>=
+procedure math_left_right;
+var t:small_number; {|left_noad| or |right_noad|}
+@!p:pointer; {new noad}
+begin t:=cur_chr;
+if (t=right_noad)and(cur_group<>math_left_group) then
+ @<Try to recover from mismatched \.{\\right}@>
+else begin p:=new_noad; type(p):=t;
+ scan_delimiter(delimiter(p),false);
+ if t=left_noad then
+ begin push_math(math_left_group); link(head):=p; tail:=p;
+ end
+ else begin p:=fin_mlist(p); unsave; {end of |math_left_group|}
+ tail_append(new_noad); type(tail):=inner_noad;
+ math_type(nucleus(tail)):=sub_mlist;
+ info(nucleus(tail)):=p;
+ end;
+ end;
+end;
+
+@ @<Try to recover from mismatch...@>=
+begin if cur_group=math_shift_group then
+ begin scan_delimiter(garbage,false);
+ print_err("Extra "); print_esc("right");
+@.Extra \\right.@>
+ help1("I'm ignoring a \right that had no matching \left.");
+ error;
+ end
+else off_save;
+end
+
+@ Here is the only way out of math mode.
+
+@<Cases of |main_control| that build...@>=
+mmode+math_shift: if cur_group=math_shift_group then after_math
+ else off_save;
+
+@ @<Declare act...@>=
+procedure after_math;
+var l:boolean; {`\.{\\leqno}' instead of `\.{\\eqno}'}
+@!danger:boolean; {not enough symbol fonts are present}
+@!m:integer; {|mmode| or |-mmode|}
+@!p:pointer; {the formula}
+@!a:pointer; {box containing equation number}
+@<Local variables for finishing a displayed formula@>@;
+begin danger:=false;
+@<Check that the necessary fonts for math symbols are present;
+ if not, flush the current math lists and set |danger:=true|@>;
+m:=mode; l:=false; p:=fin_mlist(null); {this pops the nest}
+if mode=-m then {end of equation number}
+ begin @<Check that another \.\$ follows@>;
+ cur_mlist:=p; cur_style:=text_style; mlist_penalties:=false;
+ mlist_to_hlist; a:=hpack(link(temp_head),natural);
+ unsave; decr(save_ptr); {now |cur_group=math_shift_group|}
+ if saved(0)=1 then l:=true;
+ danger:=false;
+ @<Check that the necessary fonts for math symbols are present;
+ if not, flush the current math lists and set |danger:=true|@>;
+ m:=mode; p:=fin_mlist(null);
+ end
+else a:=null;
+if m<0 then @<Finish math in text@>
+else begin if a=null then @<Check that another \.\$ follows@>;
+ @<Finish displayed math@>;
+ end;
+end;
+
+@ @<Check that the necessary fonts...@>=
+if (font_params[fam_fnt(2+text_size)]<total_mathsy_params)or@|
+ (font_params[fam_fnt(2+script_size)]<total_mathsy_params)or@|
+ (font_params[fam_fnt(2+script_script_size)]<total_mathsy_params) then
+ begin print_err("Math formula deleted: Insufficient symbol fonts");@/
+@.Math formula deleted...@>
+ help3("Sorry, but I can't typeset math unless \textfont 2")@/
+ ("and \scriptfont 2 and \scriptscriptfont 2 have all")@/
+ ("the \fontdimen values needed in math symbol fonts.");
+ error; flush_math; danger:=true;
+ end
+else if (font_params[fam_fnt(3+text_size)]<total_mathex_params)or@|
+ (font_params[fam_fnt(3+script_size)]<total_mathex_params)or@|
+ (font_params[fam_fnt(3+script_script_size)]<total_mathex_params) then
+ begin print_err("Math formula deleted: Insufficient extension fonts");@/
+ help3("Sorry, but I can't typeset math unless \textfont 3")@/
+ ("and \scriptfont 3 and \scriptscriptfont 3 have all")@/
+ ("the \fontdimen values needed in math extension fonts.");
+ error; flush_math; danger:=true;
+ end
+
+@ The |unsave| is done after everything else here; hence an appearance of
+`\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these
+particular \.\$'s. This is consistent with the conventions of
+`\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the
+space above that display.
+
+@<Finish math in text@>=
+begin tail_append(new_math(math_surround,before));
+cur_mlist:=p; cur_style:=text_style; mlist_penalties:=(mode>0); mlist_to_hlist;
+link(tail):=link(temp_head);
+while link(tail)<>null do tail:=link(tail);
+tail_append(new_math(math_surround,after));
+space_factor:=1000; unsave;
+end
+
+@ \TeX\ gets to the following part of the program when the first `\.\$' ending
+a display has been scanned.
+
+@<Check that another \.\$ follows@>=
+begin get_x_token;
+if cur_cmd<>math_shift then
+ begin print_err("Display math should end with $$");
+@.Display math...with \$\$@>
+ help2("The `$' that I just saw supposedly matches a previous `$$'.")@/
+ ("So I shall assume that you typed `$$' both times.");
+ back_error;
+ end;
+end
+
+@ We have saved the worst for last: The fussiest part of math mode processing
+occurs when a displayed formula is being centered and placed with an optional
+equation number.
+
+@<Local variables for finishing...@>=
+@!b:pointer; {box containing the equation}
+@!w:scaled; {width of the equation}
+@!z:scaled; {width of the line}
+@!e:scaled; {width of equation number}
+@!q:scaled; {width of equation number plus space to separate from equation}
+@!d:scaled; {displacement of equation in the line}
+@!s:scaled; {move the line right this much}
+@!g1,@!g2:small_number; {glue parameter codes for before and after}
+@!r:pointer; {kern node used to position the display}
+@!t:pointer; {tail of adjustment list}
+
+@ At this time |p| points to the mlist for the formula; |a| is either
+|null| or it points to a box containing the equation number; and we are in
+vertical mode (or internal vertical mode).
+
+@<Finish displayed math@>=
+cur_mlist:=p; cur_style:=display_style; mlist_penalties:=false;
+mlist_to_hlist; p:=link(temp_head);@/
+adjust_tail:=adjust_head; b:=hpack(p,natural); p:=list_ptr(b);
+t:=adjust_tail; adjust_tail:=null;@/
+w:=width(b); z:=display_width; s:=display_indent;
+if (a=null)or danger then
+ begin e:=0; q:=0;
+ end
+else begin e:=width(a); q:=e+math_quad(text_size);
+ end;
+if w+q>z then
+ @<Squeeze the equation as much as possible; if there is an equation
+ number that should go on a separate line by itself,
+ set~|e:=0|@>;
+@<Determine the displacement, |d|, of the left edge of the equation, with
+ respect to the line size |z|, assuming that |l=false|@>;
+@<Append the glue or equation number preceding the display@>;
+@<Append the display and perhaps also the equation number@>;
+@<Append the glue or equation number following the display@>;
+resume_after_display
+
+@ @<Declare act...@>=
+procedure resume_after_display;
+begin if cur_group<>math_shift_group then confusion("display");
+@:this can't happen display}{\quad display@>
+unsave; prev_graf:=prev_graf+3;
+push_nest; mode:=hmode; space_factor:=1000; set_cur_lang; clang:=cur_lang;
+prev_graf:=(norm_min(left_hyphen_min)*@'100+norm_min(right_hyphen_min))
+ *@'200000+cur_lang;
+@<Scan an optional space@>;
+if nest_ptr=1 then build_page;
+end;
+
+@ The user can force the equation number to go on a separate line
+by causing its width to be zero.
+
+@<Squeeze the equation as much as possible...@>=
+begin if (e<>0)and((w-total_shrink[normal]+q<=z)or@|
+ (total_shrink[fil]<>0)or(total_shrink[fill]<>0)or
+ (total_shrink[filll]<>0)) then
+ begin free_node(b,box_node_size);
+ b:=hpack(p,z-q,exactly);
+ end
+else begin e:=0;
+ if w>z then
+ begin free_node(b,box_node_size);
+ b:=hpack(p,z,exactly);
+ end;
+ end;
+w:=width(b);
+end
+
+@ We try first to center the display without regard to the existence of
+the equation number. If that would make it too close (where ``too close''
+means that the space between display and equation number is less than the
+width of the equation number), we either center it in the remaining space
+or move it as far from the equation number as possible. The latter alternative
+is taken only if the display begins with glue, since we assume that the
+user put glue there to control the spacing precisely.
+
+@<Determine the displacement, |d|, of the left edge of the equation...@>=
+d:=half(z-w);
+if (e>0)and(d<2*e) then {too close}
+ begin d:=half(z-w-e);
+ if p<>null then if not is_char_node(p) then if type(p)=glue_node then d:=0;
+ end
+
+@ If the equation number is set on a line by itself, either before or
+after the formula, we append an infinite penalty so that no page break will
+separate the display from its number; and we use the same size and
+displacement for all three potential lines of the display, even though
+`\.{\\parshape}' may specify them differently.
+
+@<Append the glue or equation number preceding the display@>=
+tail_append(new_penalty(pre_display_penalty));@/
+if (d+s<=pre_display_size)or l then {not enough clearance}
+ begin g1:=above_display_skip_code; g2:=below_display_skip_code;
+ end
+else begin g1:=above_display_short_skip_code;
+ g2:=below_display_short_skip_code;
+ end;
+if l and(e=0) then {it follows that |type(a)=hlist_node|}
+ begin shift_amount(a):=s; append_to_vlist(a);
+ tail_append(new_penalty(inf_penalty));
+ end
+else tail_append(new_param_glue(g1))
+
+@ @<Append the display and perhaps also the equation number@>=
+if e<>0 then
+ begin r:=new_kern(z-w-e-d);
+ if l then
+ begin link(a):=r; link(r):=b; b:=a; d:=0;
+ end
+ else begin link(b):=r; link(r):=a;
+ end;
+ b:=hpack(b,natural);
+ end;
+shift_amount(b):=s+d; append_to_vlist(b)
+
+@ @<Append the glue or equation number following the display@>=
+if (a<>null)and(e=0)and not l then
+ begin tail_append(new_penalty(inf_penalty));
+ shift_amount(a):=s+z-width(a);
+ append_to_vlist(a);
+ g2:=0;
+ end;
+if t<>adjust_head then {migrating material comes after equation number}
+ begin link(tail):=link(adjust_head); tail:=t;
+ end;
+tail_append(new_penalty(post_display_penalty));
+if g2>0 then tail_append(new_param_glue(g2))
+
+@ When \.{\\halign} appears in a display, the alignment routines operate
+essentially as they do in vertical mode. Then the following program is
+activated, with |p| and |q| pointing to the beginning and end of the
+resulting list, and with |aux_save| holding the |prev_depth| value.
+
+@<Finish an alignment in a display@>=
+begin do_assignments;
+if cur_cmd<>math_shift then @<Pontificate about improper alignment in display@>
+else @<Check that another \.\$ follows@>;
+pop_nest;
+tail_append(new_penalty(pre_display_penalty));
+tail_append(new_param_glue(above_display_skip_code));
+link(tail):=p;
+if p<>null then tail:=q;
+tail_append(new_penalty(post_display_penalty));
+tail_append(new_param_glue(below_display_skip_code));
+prev_depth:=aux_save.sc; resume_after_display;
+end
+
+@ @<Pontificate...@>=
+begin print_err("Missing $$ inserted");
+@.Missing {\$\$} inserted@>
+help2("Displays can use special alignments (like \eqalignno)")@/
+ ("only if nothing but the alignment itself is between $$'s.");
+back_error;
+end
+
+@* \[49] Mode-independent processing.
+The long |main_control| procedure has now been fully specified, except for
+certain activities that are independent of the current mode. These activities
+do not change the current vlist or hlist or mlist; if they change anything,
+it is the value of a parameter or the meaning of a control sequence.
+
+Assignments to values in |eqtb| can be global or local. Furthermore, a
+control sequence can be defined to be `\.{\\long}' or `\.{\\outer}', and
+it might or might not be expanded. The prefixes `\.{\\global}', `\.{\\long}',
+and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric
+codes, making it possible to accumulate the union of all specified prefixes
+by adding the corresponding codes. (\PASCAL's |set| operations could also
+have been used.)
+
+@<Put each...@>=
+primitive("long",prefix,1);
+@!@:long_}{\.{\\long} primitive@>
+primitive("outer",prefix,2);
+@!@:outer_}{\.{\\outer} primitive@>
+primitive("global",prefix,4);
+@!@:global_}{\.{\\global} primitive@>
+primitive("def",def,0);
+@!@:def_}{\.{\\def} primitive@>
+primitive("gdef",def,1);
+@!@:gdef_}{\.{\\gdef} primitive@>
+primitive("edef",def,2);
+@!@:edef_}{\.{\\edef} primitive@>
+primitive("xdef",def,3);
+@!@:xdef_}{\.{\\xdef} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+prefix: if chr_code=1 then print_esc("long")
+ else if chr_code=2 then print_esc("outer")
+ else print_esc("global");
+def: if chr_code=0 then print_esc("def")
+ else if chr_code=1 then print_esc("gdef")
+ else if chr_code=2 then print_esc("edef")
+ else print_esc("xdef");
+
+@ Every prefix, and every command code that might or might not be prefixed,
+calls the action procedure |prefixed_command|. This routine accumulates
+a sequence of prefixes until coming to a non-prefix, then it carries out
+the command.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(toks_register),
+any_mode(assign_toks),
+any_mode(assign_int),
+any_mode(assign_dimen),
+any_mode(assign_glue),
+any_mode(assign_mu_glue),
+any_mode(assign_font_dimen),
+any_mode(assign_font_int),
+any_mode(set_aux),
+any_mode(set_prev_graf),
+any_mode(set_page_dimen),
+any_mode(set_page_int),
+any_mode(set_box_dimen),
+any_mode(set_shape),
+any_mode(def_code),
+any_mode(def_family),
+any_mode(set_font),
+any_mode(def_font),
+any_mode(register),
+any_mode(advance),
+any_mode(multiply),
+any_mode(divide),
+any_mode(prefix),
+any_mode(let),
+any_mode(shorthand_def),
+any_mode(read_to_cs),
+any_mode(def),
+any_mode(set_box),
+any_mode(hyph_data),
+any_mode(set_interaction):prefixed_command;
+
+@ If the user says, e.g., `\.{\\global\\global}', the redundancy is
+silently accepted.
+
+@<Declare act...@>=
+@t\4@>@<Declare subprocedures for |prefixed_command|@>@t@>@;@/
+procedure prefixed_command;
+label done,exit;
+var a:small_number; {accumulated prefix codes so far}
+@!f:internal_font_number; {identifies a font}
+@!j:halfword; {index into a \.{\\parshape} specification}
+@!k:font_index; {index into |font_info|}
+@!p,@!q:pointer; {for temporary short-term use}
+@!n:integer; {ditto}
+@!e:boolean; {should a definition be expanded? or was \.{\\let} not done?}
+begin a:=0;
+while cur_cmd=prefix do
+ begin if not odd(a div cur_chr) then a:=a+cur_chr;
+ @<Get the next non-blank non-relax...@>;
+ if cur_cmd<=max_non_prefixed_command then
+ @<Discard erroneous prefixes and |return|@>;
+ end;
+@<Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant@>;
+@<Adjust \(f)for the setting of \.{\\globaldefs}@>;
+case cur_cmd of
+@t\4@>@<Assignments@>@;
+othercases confusion("prefix")
+@:this can't happen prefix}{\quad prefix@>
+endcases;
+done: @<Insert a token saved by \.{\\afterassignment}, if any@>;
+exit:end;
+
+@ @<Discard erroneous...@>=
+begin print_err("You can't use a prefix with `");
+@.You can't use a prefix with x@>
+print_cmd_chr(cur_cmd,cur_chr); print_char("'");
+help1("I'll pretend you didn't say \long or \outer or \global.");
+back_error; return;
+end
+
+@ @<Discard the prefixes...@>=
+if (cur_cmd<>def)and(a mod 4<>0) then
+ begin print_err("You can't use `"); print_esc("long"); print("' or `");
+ print_esc("outer"); print("' with `");
+@.You can't use \\long...@>
+ print_cmd_chr(cur_cmd,cur_chr); print_char("'");
+ help1("I'll pretend you didn't say \long or \outer here.");
+ error;
+ end
+
+@ The previous routine does not have to adjust |a| so that |a mod 4=0|,
+since the following routines test for the \.{\\global} prefix as follows.
+
+@d global==(a>=4)
+@d define(#)==if global then geq_define(#)@+else eq_define(#)
+@d word_define(#)==if global then geq_word_define(#)@+else eq_word_define(#)
+
+@<Adjust \(f)for the setting of \.{\\globaldefs}@>=
+if global_defs<>0 then
+ if global_defs<0 then
+ begin if global then a:=a-4;
+ end
+ else begin if not global then a:=a+4;
+ end
+
+@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or
+something similar, the |get_r_token| routine will substitute a special
+control sequence for a token that is not redefinable.
+
+@<Declare subprocedures for |prefixed_command|@>=
+procedure get_r_token;
+label restart;
+begin restart: repeat get_token;
+until cur_tok<>space_token;
+if (cur_cs=0)or(cur_cs>frozen_control_sequence) then
+ begin print_err("Missing control sequence inserted");
+@.Missing control...@>
+ help5("Please don't say `\def cs{...}', say `\def\cs{...}'.")@/
+ ("I've inserted an inaccessible control sequence so that your")@/
+ ("definition will be completed without mixing me up too badly.")@/
+ ("You can recover graciously from this error, if you're")@/
+ ("careful; see exercise 27.2 in The TeXbook.");
+@:TeXbook}{\sl The \TeX book@>
+ if cur_cs=0 then back_input;
+ cur_tok:=cs_token_flag+frozen_protection; ins_error; goto restart;
+ end;
+end;
+
+@ @<Initialize table entries...@>=
+text(frozen_protection):="inaccessible";
+
+@ Here's an example of the way many of the following routines operate.
+(Unfortunately, they aren't all as simple as this.)
+
+@<Assignments@>=
+set_font: define(cur_font_loc,data,cur_chr);
+
+@ When a |def| command has been scanned,
+|cur_chr| is odd if the definition is supposed to be global, and
+|cur_chr>=2| if the definition is supposed to be expanded.
+
+@<Assignments@>=
+def: begin if odd(cur_chr)and not global and(global_defs>=0) then a:=a+4;
+ e:=(cur_chr>=2); get_r_token; p:=cur_cs;
+ q:=scan_toks(true,e); define(p,call+(a mod 4),def_ref);
+ end;
+
+@ Both \.{\\let} and \.{\\futurelet} share the command code |let|.
+
+@<Put each...@>=
+primitive("let",let,normal);@/
+@!@:let_}{\.{\\let} primitive@>
+primitive("futurelet",let,normal+1);@/
+@!@:future_let_}{\.{\\futurelet} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+let: if chr_code<>normal then print_esc("futurelet")@+else print_esc("let");
+
+@ @<Assignments@>=
+let: begin n:=cur_chr;
+ get_r_token; p:=cur_cs;
+ if n=normal then
+ begin repeat get_token;
+ until cur_cmd<>spacer;
+ if cur_tok=other_token+"=" then
+ begin get_token;
+ if cur_cmd=spacer then get_token;
+ end;
+ end
+ else begin get_token; q:=cur_tok; get_token; back_input;
+ cur_tok:=q; back_input; {look ahead, then back up}
+ end; {note that |back_input| doesn't affect |cur_cmd|, |cur_chr|}
+ if cur_cmd>=call then add_token_ref(cur_chr);
+ define(p,cur_cmd,cur_chr);
+ end;
+
+@ A \.{\\chardef} creates a control sequence whose |cmd| is |char_given|;
+a \.{\\mathchardef} creates a control sequence whose |cmd| is |math_given|;
+and the corresponding |chr| is the character code or math code. A \.{\\countdef}
+or \.{\\dimendef} or \.{\\skipdef} or \.{\\muskipdef} creates a control
+sequence whose |cmd| is |assign_int| or \dots\ or |assign_mu_glue|, and the
+corresponding |chr| is the |eqtb| location of the internal register in question.
+
+@d char_def_code=0 {|shorthand_def| for \.{\\chardef}}
+@d math_char_def_code=1 {|shorthand_def| for \.{\\mathchardef}}
+@d count_def_code=2 {|shorthand_def| for \.{\\countdef}}
+@d dimen_def_code=3 {|shorthand_def| for \.{\\dimendef}}
+@d skip_def_code=4 {|shorthand_def| for \.{\\skipdef}}
+@d mu_skip_def_code=5 {|shorthand_def| for \.{\\muskipdef}}
+@d toks_def_code=6 {|shorthand_def| for \.{\\toksdef}}
+
+@<Put each...@>=
+primitive("chardef",shorthand_def,char_def_code);@/
+@!@:char_def_}{\.{\\chardef} primitive@>
+primitive("mathchardef",shorthand_def,math_char_def_code);@/
+@!@:math_char_def_}{\.{\\mathchardef} primitive@>
+primitive("countdef",shorthand_def,count_def_code);@/
+@!@:count_def_}{\.{\\countdef} primitive@>
+primitive("dimendef",shorthand_def,dimen_def_code);@/
+@!@:dimen_def_}{\.{\\dimendef} primitive@>
+primitive("skipdef",shorthand_def,skip_def_code);@/
+@!@:skip_def_}{\.{\\skipdef} primitive@>
+primitive("muskipdef",shorthand_def,mu_skip_def_code);@/
+@!@:mu_skip_def_}{\.{\\muskipdef} primitive@>
+primitive("toksdef",shorthand_def,toks_def_code);@/
+@!@:toks_def_}{\.{\\toksdef} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+shorthand_def: case chr_code of
+ char_def_code: print_esc("chardef");
+ math_char_def_code: print_esc("mathchardef");
+ count_def_code: print_esc("countdef");
+ dimen_def_code: print_esc("dimendef");
+ skip_def_code: print_esc("skipdef");
+ mu_skip_def_code: print_esc("muskipdef");
+ othercases print_esc("toksdef")
+ endcases;
+char_given: begin print_esc("char"); print_hex(chr_code);
+ end;
+math_given: begin print_esc("mathchar"); print_hex(chr_code);
+ end;
+
+@ We temporarily define |p| to be |relax|, so that an occurrence of |p|
+while scanning the definition will simply stop the scanning instead of
+producing an ``undefined control sequence'' error or expanding the
+previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'.
+
+@<Assignments@>=
+shorthand_def: begin n:=cur_chr; get_r_token; p:=cur_cs; define(p,relax,256);
+ scan_optional_equals;
+ case n of
+ char_def_code: begin scan_char_num; define(p,char_given,cur_val);
+ end;
+ math_char_def_code: begin scan_fifteen_bit_int; define(p,math_given,cur_val);
+ end;
+ othercases begin scan_eight_bit_int;
+ case n of
+ count_def_code: define(p,assign_int,count_base+cur_val);
+ dimen_def_code: define(p,assign_dimen,scaled_base+cur_val);
+ skip_def_code: define(p,assign_glue,skip_base+cur_val);
+ mu_skip_def_code: define(p,assign_mu_glue,mu_skip_base+cur_val);
+ toks_def_code: define(p,assign_toks,toks_base+cur_val);
+ end; {there are no other cases}
+ end
+ endcases;
+ end;
+
+@ @<Assignments@>=
+read_to_cs: begin scan_int; n:=cur_val;
+ if not scan_keyword("to") then
+@.to@>
+ begin print_err("Missing `to' inserted");
+@.Missing `to'...@>
+ help2("You should have said `\read<number> to \cs'.")@/
+ ("I'm going to look for the \cs now."); error;
+ end;
+ get_r_token;
+ p:=cur_cs; read_toks(n,p); define(p,call,cur_val);
+ end;
+
+@ The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive
+their values in the following way. (For safety's sake, we place an
+enclosing pair of braces around an \.{\\output} list.)
+
+@<Assignments@>=
+toks_register,assign_toks: begin q:=cur_cs;
+ if cur_cmd=toks_register then
+ begin scan_eight_bit_int; p:=toks_base+cur_val;
+ end
+ else p:=cur_chr; {|p=every_par_loc| or |output_routine_loc| or \dots}
+ scan_optional_equals;
+ @<Get the next non-blank non-relax non-call token@>;
+ if cur_cmd<>left_brace then @<If the right-hand side is a token parameter
+ or token register, finish the assignment and |goto done|@>;
+ back_input; cur_cs:=q; q:=scan_toks(false,false);
+ if link(def_ref)=null then {empty list: revert to the default}
+ begin define(p,undefined_cs,null); free_avail(def_ref);
+ end
+ else begin if p=output_routine_loc then {enclose in curlies}
+ begin link(q):=get_avail; q:=link(q);
+ info(q):=right_brace_token+"}";
+ q:=get_avail; info(q):=left_brace_token+"{";
+ link(q):=link(def_ref); link(def_ref):=q;
+ end;
+ define(p,call,def_ref);
+ end;
+ end;
+
+@ @<If the right-hand side is a token parameter...@>=
+begin if cur_cmd=toks_register then
+ begin scan_eight_bit_int; cur_cmd:=assign_toks; cur_chr:=toks_base+cur_val;
+ end;
+if cur_cmd=assign_toks then
+ begin q:=equiv(cur_chr);
+ if q=null then define(p,undefined_cs,null)
+ else begin add_token_ref(q); define(p,call,q);
+ end;
+ goto done;
+ end;
+end
+
+@ Similar routines are used to assign values to the numeric parameters.
+
+@<Assignments@>=
+assign_int: begin p:=cur_chr; scan_optional_equals; scan_int;
+ word_define(p,cur_val);
+ end;
+assign_dimen: begin p:=cur_chr; scan_optional_equals;
+ scan_normal_dimen; word_define(p,cur_val);
+ end;
+assign_glue,assign_mu_glue: begin p:=cur_chr; n:=cur_cmd; scan_optional_equals;
+ if n=assign_mu_glue then scan_glue(mu_val)@+else scan_glue(glue_val);
+ trap_zero_glue;
+ define(p,glue_ref,cur_val);
+ end;
+
+@ When a glue register or parameter becomes zero, it will always point to
+|zero_glue| because of the following procedure. (Exception: The tabskip
+glue isn't trapped while preambles are being scanned.)
+
+@<Declare subprocedures for |prefixed_command|@>=
+procedure trap_zero_glue;
+begin if (width(cur_val)=0)and(stretch(cur_val)=0)and(shrink(cur_val)=0) then
+ begin add_glue_ref(zero_glue);
+ delete_glue_ref(cur_val); cur_val:=zero_glue;
+ end;
+end;
+
+@ The various character code tables are changed by the |def_code| commands,
+and the font families are declared by |def_family|.
+
+@<Put each...@>=
+primitive("catcode",def_code,cat_code_base);
+@!@:cat_code_}{\.{\\catcode} primitive@>
+primitive("mathcode",def_code,math_code_base);
+@!@:math_code_}{\.{\\mathcode} primitive@>
+primitive("lccode",def_code,lc_code_base);
+@!@:lc_code_}{\.{\\lccode} primitive@>
+primitive("uccode",def_code,uc_code_base);
+@!@:uc_code_}{\.{\\uccode} primitive@>
+primitive("sfcode",def_code,sf_code_base);
+@!@:sf_code_}{\.{\\sfcode} primitive@>
+primitive("delcode",def_code,del_code_base);
+@!@:del_code_}{\.{\\delcode} primitive@>
+primitive("textfont",def_family,math_font_base);
+@!@:text_font_}{\.{\\textfont} primitive@>
+primitive("scriptfont",def_family,math_font_base+script_size);
+@!@:script_font_}{\.{\\scriptfont} primitive@>
+primitive("scriptscriptfont",def_family,math_font_base+script_script_size);
+@!@:script_script_font_}{\.{\\scriptscriptfont} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+def_code: if chr_code=cat_code_base then print_esc("catcode")
+ else if chr_code=math_code_base then print_esc("mathcode")
+ else if chr_code=lc_code_base then print_esc("lccode")
+ else if chr_code=uc_code_base then print_esc("uccode")
+ else if chr_code=sf_code_base then print_esc("sfcode")
+ else print_esc("delcode");
+def_family: print_size(chr_code-math_font_base);
+
+@ The different types of code values have different legal ranges; the
+following program is careful to check each case properly.
+
+@<Assignments@>=
+def_code: begin @<Let |n| be the largest legal code value, based on |cur_chr|@>;
+ p:=cur_chr; scan_char_num; p:=p+cur_val; scan_optional_equals;
+ scan_int;
+ if ((cur_val<0)and(p<del_code_base))or(cur_val>n) then
+ begin print_err("Invalid code ("); print_int(cur_val);
+@.Invalid code@>
+ if p<del_code_base then print("), should be in the range 0..")
+ else print("), should be at most ");
+ print_int(n);
+ help1("I'm going to use 0 instead of that illegal code value.");@/
+ error; cur_val:=0;
+ end;
+ if p<math_code_base then define(p,data,cur_val)
+ else if p<del_code_base then define(p,data,hi(cur_val))
+ else word_define(p,cur_val);
+ end;
+
+@ @<Let |n| be the largest...@>=
+if cur_chr=cat_code_base then n:=max_char_code
+else if cur_chr=math_code_base then n:=@'100000
+else if cur_chr=sf_code_base then n:=@'77777
+else if cur_chr=del_code_base then n:=@'77777777
+else n:=255
+
+@ @<Assignments@>=
+def_family: begin p:=cur_chr; scan_four_bit_int; p:=p+cur_val;
+ scan_optional_equals; scan_font_ident; define(p,data,cur_val);
+ end;
+
+@ Next we consider changes to \TeX's numeric registers.
+
+@<Assignments@>=
+register,advance,multiply,divide: do_register_command(a);
+
+@ We use the fact that |register<advance<multiply<divide|.
+
+@<Declare subprocedures for |prefixed_command|@>=
+procedure do_register_command(@!a:small_number);
+label found,exit;
+var l,@!q,@!r,@!s:pointer; {for list manipulation}
+@!p:int_val..mu_val; {type of register involved}
+begin q:=cur_cmd;
+@<Compute the register location |l| and its type |p|; but |return| if invalid@>;
+if q=register then scan_optional_equals
+else if scan_keyword("by") then do_nothing; {optional `\.{by}'}
+@.by@>
+arith_error:=false;
+if q<multiply then @<Compute result of |register| or
+ |advance|, put it in |cur_val|@>
+else @<Compute result of |multiply| or |divide|, put it in |cur_val|@>;
+if arith_error then
+ begin print_err("Arithmetic overflow");
+@.Arithmetic overflow@>
+ help2("I can't carry out that multiplication or division,")@/
+ ("since the result is out of range.");
+ if p>=glue_val then delete_glue_ref(cur_val);
+ error; return;
+ end;
+if p<glue_val then word_define(l,cur_val)
+else begin trap_zero_glue; define(l,glue_ref,cur_val);
+ end;
+exit: end;
+
+@ Here we use the fact that the consecutive codes |int_val..mu_val| and
+|assign_int..assign_mu_glue| correspond to each other nicely.
+
+@<Compute the register location |l| and its type |p|...@>=
+begin if q<>register then
+ begin get_x_token;
+ if (cur_cmd>=assign_int)and(cur_cmd<=assign_mu_glue) then
+ begin l:=cur_chr; p:=cur_cmd-assign_int; goto found;
+ end;
+ if cur_cmd<>register then
+ begin print_err("You can't use `"); print_cmd_chr(cur_cmd,cur_chr);
+@.You can't use x after ...@>
+ print("' after "); print_cmd_chr(q,0);
+ help1("I'm forgetting what you said and not changing anything.");
+ error; return;
+ end;
+ end;
+p:=cur_chr; scan_eight_bit_int;
+case p of
+int_val: l:=cur_val+count_base;
+dimen_val: l:=cur_val+scaled_base;
+glue_val: l:=cur_val+skip_base;
+mu_val: l:=cur_val+mu_skip_base;
+end; {there are no other cases}
+end;
+found:
+
+@ @<Compute result of |register| or |advance|...@>=
+if p<glue_val then
+ begin if p=int_val then scan_int@+else scan_normal_dimen;
+ if q=advance then cur_val:=cur_val+eqtb[l].int;
+ end
+else begin scan_glue(p);
+ if q=advance then @<Compute the sum of two glue specs@>;
+ end
+
+@ @<Compute the sum of two glue specs@>=
+begin q:=new_spec(cur_val); r:=equiv(l);
+delete_glue_ref(cur_val);
+width(q):=width(q)+width(r);
+if stretch(q)=0 then stretch_order(q):=normal;
+if stretch_order(q)=stretch_order(r) then stretch(q):=stretch(q)+stretch(r)
+else if (stretch_order(q)<stretch_order(r))and(stretch(r)<>0) then
+ begin stretch(q):=stretch(r); stretch_order(q):=stretch_order(r);
+ end;
+if shrink(q)=0 then shrink_order(q):=normal;
+if shrink_order(q)=shrink_order(r) then shrink(q):=shrink(q)+shrink(r)
+else if (shrink_order(q)<shrink_order(r))and(shrink(r)<>0) then
+ begin shrink(q):=shrink(r); shrink_order(q):=shrink_order(r);
+ end;
+cur_val:=q;
+end
+
+@ @<Compute result of |multiply| or |divide|...@>=
+begin scan_int;
+if p<glue_val then
+ if q=multiply then
+ if p=int_val then cur_val:=mult_integers(eqtb[l].int,cur_val)
+ else cur_val:=nx_plus_y(eqtb[l].int,cur_val,0)
+ else cur_val:=x_over_n(eqtb[l].int,cur_val)
+else begin s:=equiv(l); r:=new_spec(s);
+ if q=multiply then
+ begin width(r):=nx_plus_y(width(s),cur_val,0);
+ stretch(r):=nx_plus_y(stretch(s),cur_val,0);
+ shrink(r):=nx_plus_y(shrink(s),cur_val,0);
+ end
+ else begin width(r):=x_over_n(width(s),cur_val);
+ stretch(r):=x_over_n(stretch(s),cur_val);
+ shrink(r):=x_over_n(shrink(s),cur_val);
+ end;
+ cur_val:=r;
+ end;
+end
+
+@ The processing of boxes is somewhat different, because we may need
+to scan and create an entire box before we actually change the value of the old
+one.
+
+@<Assignments@>=
+set_box: begin scan_eight_bit_int;
+ if global then n:=256+cur_val@+else n:=cur_val;
+ scan_optional_equals;
+ if set_box_allowed then scan_box(box_flag+n)
+ else begin print_err("Improper "); print_esc("setbox");
+@.Improper \\setbox@>
+ help2("Sorry, \setbox is not allowed after \halign in a display,")@/
+ ("or between \accent and an accented character."); error;
+ end;
+ end;
+
+@ The |space_factor| or |prev_depth| settings are changed when a |set_aux|
+command is sensed. Similarly, |prev_graf| is changed in the presence of
+|set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of
+|set_page_int|. These definitions are always global.
+
+When some dimension of a box register is changed, the change isn't exactly
+global; but \TeX\ does not look at the \.{\\global} switch.
+
+@<Assignments@>=
+set_aux:alter_aux;
+set_prev_graf:alter_prev_graf;
+set_page_dimen:alter_page_so_far;
+set_page_int:alter_integer;
+set_box_dimen:alter_box_dimen;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+procedure alter_aux;
+var c:halfword; {|hmode| or |vmode|}
+begin if cur_chr<>abs(mode) then report_illegal_case
+else begin c:=cur_chr; scan_optional_equals;
+ if c=vmode then
+ begin scan_normal_dimen; prev_depth:=cur_val;
+ end
+ else begin scan_int;
+ if (cur_val<=0)or(cur_val>32767) then
+ begin print_err("Bad space factor");
+@.Bad space factor@>
+ help1("I allow only values in the range 1..32767 here.");
+ int_error(cur_val);
+ end
+ else space_factor:=cur_val;
+ end;
+ end;
+end;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+procedure alter_prev_graf;
+var p:0..nest_size; {index into |nest|}
+begin nest[nest_ptr]:=cur_list; p:=nest_ptr;
+while abs(nest[p].mode_field)<>vmode do decr(p);
+scan_optional_equals; scan_int;
+if cur_val<0 then
+ begin print_err("Bad "); print_esc("prevgraf");
+@.Bad \\prevgraf@>
+ help1("I allow only nonnegative values here.");
+ int_error(cur_val);
+ end
+else begin nest[p].pg_field:=cur_val; cur_list:=nest[nest_ptr];
+ end;
+end;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+procedure alter_page_so_far;
+var c:0..7; {index into |page_so_far|}
+begin c:=cur_chr; scan_optional_equals; scan_normal_dimen;
+page_so_far[c]:=cur_val;
+end;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+procedure alter_integer;
+var c:0..1; {0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}}
+begin c:=cur_chr; scan_optional_equals; scan_int;
+if c=0 then dead_cycles:=cur_val
+else insert_penalties:=cur_val;
+end;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+procedure alter_box_dimen;
+var c:small_number; {|width_offset| or |height_offset| or |depth_offset|}
+@!b:eight_bits; {box number}
+begin c:=cur_chr; scan_eight_bit_int; b:=cur_val; scan_optional_equals;
+scan_normal_dimen;
+if box(b)<>null then mem[box(b)+c].sc:=cur_val;
+end;
+
+@ Paragraph shapes are set up in the obvious way.
+
+@<Assignments@>=
+set_shape: begin scan_optional_equals; scan_int; n:=cur_val;
+ if n<=0 then p:=null
+ else begin p:=get_node(2*n+1); info(p):=n;
+ for j:=1 to n do
+ begin scan_normal_dimen;
+ mem[p+2*j-1].sc:=cur_val; {indentation}
+ scan_normal_dimen;
+ mem[p+2*j].sc:=cur_val; {width}
+ end;
+ end;
+ define(par_shape_loc,shape_ref,p);
+ end;
+
+@ Here's something that isn't quite so obvious. It guarantees that
+|info(par_shape_ptr)| can hold any positive~|n| for which |get_node(2*n+1)|
+doesn't overflow the memory capacity.
+
+@<Check the ``constant''...@>=
+if 2*max_halfword<mem_top-mem_min then bad:=41;
+
+@ New hyphenation data is loaded by the |hyph_data| command.
+
+@<Put each...@>=
+primitive("hyphenation",hyph_data,0);
+@!@:hyphenation_}{\.{\\hyphenation} primitive@>
+primitive("patterns",hyph_data,1);
+@!@:patterns_}{\.{\\patterns} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+hyph_data: if chr_code=1 then print_esc("patterns")
+ else print_esc("hyphenation");
+
+@ @<Assignments@>=
+hyph_data: if cur_chr=1 then
+ begin @!init new_patterns; goto done;@;@+tini@/
+ print_err("Patterns can be loaded only by INITEX");
+@.Patterns can be...@>
+ help0; error;
+ repeat get_token; until cur_cmd=right_brace; {flush the patterns}
+ return;
+ end
+ else begin new_hyph_exceptions; goto done;
+ end;
+
+@ All of \TeX's parameters are kept in |eqtb| except the font information,
+the interaction mode, and the hyphenation tables; these are strictly global.
+
+@<Assignments@>=
+assign_font_dimen: begin find_font_dimen(true); k:=cur_val;
+ scan_optional_equals; scan_normal_dimen; font_info[k].sc:=cur_val;
+ end;
+assign_font_int: begin n:=cur_chr; scan_font_ident; f:=cur_val;
+ scan_optional_equals; scan_int;
+ if n=0 then hyphen_char[f]:=cur_val@+else skew_char[f]:=cur_val;
+ end;
+
+@ @<Put each...@>=
+primitive("hyphenchar",assign_font_int,0);
+@!@:hyphen_char_}{\.{\\hyphenchar} primitive@>
+primitive("skewchar",assign_font_int,1);
+@!@:skew_char_}{\.{\\skewchar} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+assign_font_int: if chr_code=0 then print_esc("hyphenchar")
+ else print_esc("skewchar");
+
+@ Here is where the information for a new font gets loaded.
+
+@<Assignments@>=
+def_font: new_font(a);
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+procedure new_font(@!a:small_number);
+label common_ending;
+var u:pointer; {user's font identifier}
+@!s:scaled; {stated ``at'' size, or negative of scaled magnification}
+@!f:internal_font_number; {runs through existing fonts}
+@!t:str_number; {name for the frozen font identifier}
+@!old_setting:0..max_selector; {holds |selector| setting}
+@!flushable_string:str_number; {string not yet referenced}
+begin if job_name=0 then open_log_file;
+ {avoid confusing \.{texput} with the font name}
+@.texput@>
+get_r_token; u:=cur_cs;
+if u>=hash_base then t:=text(u)
+else if u>=single_base then
+ if u=null_cs then t:="FONT"@+else t:=u-single_base
+else begin old_setting:=selector; selector:=new_string;
+ print("FONT"); print(u-active_base); selector:=old_setting;
+@.FONTx@>
+ str_room(1); t:=make_string;
+ end;
+define(u,set_font,null_font); scan_optional_equals; scan_file_name;
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal
+ font number and |goto common_ending|@>;
+f:=read_font_info(u,cur_name,cur_area,s);
+common_ending: equiv(u):=f; eqtb[font_id_base+f]:=eqtb[u]; font_id_text(f):=t;
+end;
+
+@ @<Scan the font size specification@>=
+name_in_progress:=true; {this keeps |cur_name| from being changed}
+if scan_keyword("at") then @<Put the \(p)(positive) `at' size into |s|@>
+@.at@>
+else if scan_keyword("scaled") then
+@.scaled@>
+ begin scan_int; s:=-cur_val;
+ if (cur_val<=0)or(cur_val>32768) then
+ begin print_err("Illegal magnification has been changed to 1000");@/
+@.Illegal magnification...@>
+ help1("The magnification ratio must be between 1 and 32768.");
+ int_error(cur_val); s:=-1000;
+ end;
+ end
+else s:=-1000;
+name_in_progress:=false
+
+@ @<Put the \(p)(positive) `at' size into |s|@>=
+begin scan_normal_dimen; s:=cur_val;
+if (s<=0)or(s>=@'1000000000) then
+ begin print_err("Improper `at' size (");
+ print_scaled(s); print("pt), replaced by 10pt");
+@.Improper `at' size...@>
+ help2("I can only handle fonts at positive sizes that are")@/
+ ("less than 2048pt, so I've changed what you said to 10pt.");
+ error; s:=10*unity;
+ end;
+end
+
+@ When the user gives a new identifier to a font that was previously loaded,
+the new name becomes the font identifier of record. Font names `\.{xyz}' and
+`\.{XYZ}' are considered to be different.
+
+@<If this font has already been loaded...@>=
+flushable_string:=str_ptr-1;
+for f:=font_base+1 to font_ptr do
+ if str_eq_str(font_name[f],cur_name)and str_eq_str(font_area[f],cur_area) then
+ begin if cur_name=flushable_string then
+ begin flush_string; cur_name:=font_name[f];
+ end;
+ if s>0 then
+ begin if s=font_size[f] then goto common_ending;
+ end
+ else if font_size[f]=xn_over_d(font_dsize[f],-s,1000) then
+ goto common_ending;
+ end
+
+@ @<Cases of |print_cmd_chr|...@>=
+set_font:begin print("select font "); slow_print(font_name[chr_code]);
+ if font_size[chr_code]<>font_dsize[chr_code] then
+ begin print(" at "); print_scaled(font_size[chr_code]);
+ print("pt");
+ end;
+ end;
+
+@ @<Put each...@>=
+primitive("batchmode",set_interaction,batch_mode);
+@!@:batch_mode_}{\.{\\batchmode} primitive@>
+primitive("nonstopmode",set_interaction,nonstop_mode);
+@!@:nonstop_mode_}{\.{\\nonstopmode} primitive@>
+primitive("scrollmode",set_interaction,scroll_mode);
+@!@:scroll_mode_}{\.{\\scrollmode} primitive@>
+primitive("errorstopmode",set_interaction,error_stop_mode);
+@!@:error_stop_mode_}{\.{\\errorstopmode} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+set_interaction: case chr_code of
+ batch_mode: print_esc("batchmode");
+ nonstop_mode: print_esc("nonstopmode");
+ scroll_mode: print_esc("scrollmode");
+ othercases print_esc("errorstopmode")
+ endcases;
+
+@ @<Assignments@>=
+set_interaction: new_interaction;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+procedure new_interaction;
+begin print_ln;
+interaction:=cur_chr;
+@<Initialize the print |selector| based on |interaction|@>;
+if log_opened then selector:=selector+2;
+end;
+
+@ The \.{\\afterassignment} command puts a token into the global
+variable |after_token|. This global variable is examined just after
+every assignment has been performed.
+
+@<Glob...@>=
+@!after_token:halfword; {zero, or a saved token}
+
+@ @<Set init...@>=
+after_token:=0;
+
+@ @<Cases of |main_control| that don't...@>=
+any_mode(after_assignment):begin get_token; after_token:=cur_tok;
+ end;
+
+@ @<Insert a token saved by \.{\\afterassignment}, if any@>=
+if after_token<>0 then
+ begin cur_tok:=after_token; back_input; after_token:=0;
+ end
+
+@ Here is a procedure that might be called `Get the next non-blank non-relax
+non-call non-assignment token'.
+
+@<Declare act...@>=
+procedure do_assignments;
+label exit;
+begin loop begin @<Get the next non-blank non-relax...@>;
+ if cur_cmd<=max_non_prefixed_command then return;
+ set_box_allowed:=false; prefixed_command; set_box_allowed:=true;
+ end;
+exit:end;
+
+@ @<Cases of |main_control| that don't...@>=
+any_mode(after_group):begin get_token; save_for_after(cur_tok);
+ end;
+
+@ Files for \.{\\read} are opened and closed by the |in_stream| command.
+
+@<Put each...@>=
+primitive("openin",in_stream,1);
+@!@:open_in_}{\.{\\openin} primitive@>
+primitive("closein",in_stream,0);
+@!@:close_in_}{\.{\\closein} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+in_stream: if chr_code=0 then print_esc("closein")
+ else print_esc("openin");
+
+@ @<Cases of |main_control| that don't...@>=
+any_mode(in_stream): open_or_close_in;
+
+@ @<Declare act...@>=
+procedure open_or_close_in;
+var c:0..1; {1 for \.{\\openin}, 0 for \.{\\closein}}
+@!n:0..15; {stream number}
+begin c:=cur_chr; scan_four_bit_int; n:=cur_val;
+if read_open[n]<>closed then
+ begin a_close(read_file[n]); read_open[n]:=closed;
+ end;
+if c<>0 then
+ begin scan_optional_equals; scan_file_name;
+ if cur_ext="" then cur_ext:=".tex";
+ pack_cur_name;
+ if a_open_in(read_file[n]) then read_open[n]:=just_open;
+ end;
+end;
+
+@ The user can issue messages to the terminal, regardless of the
+current mode.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(message):issue_message;
+
+@ @<Put each...@>=
+primitive("message",message,0);
+@!@:message_}{\.{\\message} primitive@>
+primitive("errmessage",message,1);
+@!@:err_message_}{\.{\\errmessage} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+message: if chr_code=0 then print_esc("message")
+ else print_esc("errmessage");
+
+@ @<Declare act...@>=
+procedure issue_message;
+var old_setting:0..max_selector; {holds |selector| setting}
+@!c:0..1; {identifies \.{\\message} and \.{\\errmessage}}
+@!s:str_number; {the message}
+begin c:=cur_chr; link(garbage):=scan_toks(false,true);
+old_setting:=selector; selector:=new_string;
+token_show(def_ref); selector:=old_setting;
+flush_list(def_ref);
+str_room(1); s:=make_string;
+if c=0 then @<Print string |s| on the terminal@>
+else @<Print string |s| as an error message@>;
+flush_string;
+end;
+
+@ @<Print string |s| on the terminal@>=
+begin if term_offset+length(s)>max_print_line-2 then print_ln
+else if (term_offset>0)or(file_offset>0) then print_char(" ");
+slow_print(s); update_terminal;
+end
+
+@ If \.{\\errmessage} occurs often in |scroll_mode|, without user-defined
+\.{\\errhelp}, we don't want to give a long help message each time. So we
+give a verbose explanation only once.
+
+@<Glob...@>=
+@!long_help_seen:boolean; {has the long \.{\\errmessage} help been used?}
+
+@ @<Set init...@>=long_help_seen:=false;
+
+@ @<Print string |s| as an error message@>=
+begin print_err(""); slow_print(s);
+if err_help<>null then use_err_help:=true
+else if long_help_seen then help1("(That was another \errmessage.)")
+else begin if interaction<error_stop_mode then long_help_seen:=true;
+ help4("This error message was generated by an \errmessage")@/
+ ("command, so I can't give any explicit help.")@/
+ ("Pretend that you're Hercule Poirot: Examine all clues,")@/
+@^Poirot, Hercule@>
+ ("and deduce the truth by order and method.");
+ end;
+error; use_err_help:=false;
+end
+
+@ The |error| routine calls on |give_err_help| if help is requested from
+the |err_help| parameter.
+
+@p procedure give_err_help;
+begin token_show(err_help);
+end;
+
+@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by
+building a token list and then changing the cases of the letters in it.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(case_shift):shift_case;
+
+@ @<Put each...@>=
+primitive("lowercase",case_shift,lc_code_base);
+@!@:lowercase_}{\.{\\lowercase} primitive@>
+primitive("uppercase",case_shift,uc_code_base);
+@!@:uppercase_}{\.{\\uppercase} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case_shift:if chr_code=lc_code_base then print_esc("lowercase")
+ else print_esc("uppercase");
+
+@ @<Declare act...@>=
+procedure shift_case;
+var b:pointer; {|lc_code_base| or |uc_code_base|}
+@!p:pointer; {runs through the token list}
+@!t:halfword; {token}
+@!c:eight_bits; {character code}
+begin b:=cur_chr; p:=scan_toks(false,false); p:=link(def_ref);
+while p<>null do
+ begin @<Change the case of the token in |p|, if a change is appropriate@>;
+ p:=link(p);
+ end;
+back_list(link(def_ref)); free_avail(def_ref); {omit reference count}
+end;
+
+@ When the case of a |chr_code| changes, we don't change the |cmd|.
+We also change active characters, using the fact that
+|cs_token_flag+active_base| is a multiple of~256.
+@^data structure assumptions@>
+
+@<Change the case of the token in |p|, if a change is appropriate@>=
+t:=info(p);
+if t<cs_token_flag+single_base then
+ begin c:=t mod 256;
+ if equiv(b+c)<>0 then info(p):=t-c+equiv(b+c);
+ end
+
+@ We come finally to the last pieces missing from |main_control|, namely the
+`\.{\\show}' commands that are useful when debugging.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(xray): show_whatever;
+
+@ @d show_code=0 { \.{\\show} }
+@d show_box_code=1 { \.{\\showbox} }
+@d show_the_code=2 { \.{\\showthe} }
+@d show_lists=3 { \.{\\showlists} }
+
+@<Put each...@>=
+primitive("show",xray,show_code);
+@!@:show_}{\.{\\show} primitive@>
+primitive("showbox",xray,show_box_code);
+@!@:show_box_}{\.{\\showbox} primitive@>
+primitive("showthe",xray,show_the_code);
+@!@:show_the_}{\.{\\showthe} primitive@>
+primitive("showlists",xray,show_lists);
+@!@:show_lists_}{\.{\\showlists} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+xray: case chr_code of
+ show_box_code:print_esc("showbox");
+ show_the_code:print_esc("showthe");
+ show_lists:print_esc("showlists");
+ othercases print_esc("show")
+ endcases;
+
+@ @<Declare act...@>=
+procedure show_whatever;
+label common_ending;
+var p:pointer; {tail of a token list to show}
+begin case cur_chr of
+show_lists: begin begin_diagnostic; show_activities;
+ end;
+show_box_code: @<Show the current contents of a box@>;
+show_code: @<Show the current meaning of a token, then |goto common_ending|@>;
+othercases @<Show the current value of some parameter or register,
+ then |goto common_ending|@>
+endcases;@/
+@<Complete a potentially long \.{\\show} command@>;
+common_ending: if interaction<error_stop_mode then
+ begin help0; decr(error_count);
+ end
+else if tracing_online>0 then
+ begin@t@>@;@/
+ help3("This isn't an error message; I'm just \showing something.")@/
+ ("Type `I\show...' to show more (e.g., \show\cs,")@/
+ ("\showthe\count10, \showbox255, \showlists).");
+ end
+else begin@t@>@;@/
+ help5("This isn't an error message; I'm just \showing something.")@/
+ ("Type `I\show...' to show more (e.g., \show\cs,")@/
+ ("\showthe\count10, \showbox255, \showlists).")@/
+ ("And type `I\tracingonline=1\show...' to show boxes and")@/
+ ("lists on your terminal as well as in the transcript file.");
+ end;
+error;
+end;
+
+@ @<Show the current meaning of a token...@>=
+begin get_token;
+if interaction=error_stop_mode then wake_up_terminal;
+print_nl("> ");
+if cur_cs<>0 then
+ begin sprint_cs(cur_cs); print_char("=");
+ end;
+print_meaning; goto common_ending;
+end
+
+@ @<Cases of |print_cmd_chr|...@>=
+undefined_cs: print("undefined");
+call: print("macro");
+long_call: print_esc("long macro");
+outer_call: print_esc("outer macro");
+long_outer_call: begin print_esc("long"); print_esc("outer macro");
+ end;
+end_template: print_esc("outer endtemplate");
+
+@ @<Show the current contents of a box@>=
+begin scan_eight_bit_int; begin_diagnostic;
+print_nl("> \box"); print_int(cur_val); print_char("=");
+if box(cur_val)=null then print("void")
+else show_box(box(cur_val));
+end
+
+@ @<Show the current value of some parameter...@>=
+begin p:=the_toks;
+if interaction=error_stop_mode then wake_up_terminal;
+print_nl("> "); token_show(temp_head);
+flush_list(link(temp_head)); goto common_ending;
+end
+
+@ @<Complete a potentially long \.{\\show} command@>=
+end_diagnostic(true); print_err("OK");
+@.OK@>
+if selector=term_and_log then if tracing_online<=0 then
+ begin selector:=term_only; print(" (see the transcript file)");
+ selector:=term_and_log;
+ end
+
+@* \[50] Dumping and undumping the tables.
+After \.{INITEX} has seen a collection of fonts and macros, it
+can write all the necessary information on an auxiliary file so
+that production versions of \TeX\ are able to initialize their
+memory at high speed. The present section of the program takes
+care of such output and input. We shall consider simultaneously
+the processes of storing and restoring,
+so that the inverse relation between them is clear.
+@.INITEX@>
+
+The global variable |format_ident| is a string that is printed right
+after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this
+string says simply `\.{(INITEX)}'; for other versions of \TeX\ it says,
+for example, `\.{(preloaded format=plain 1982.11.19)}', showing the year,
+month, and day that the format file was created. We have |format_ident=0|
+before \TeX's tables are loaded.
+
+@<Glob...@>=
+@!format_ident:str_number;
+
+@ @<Set init...@>=
+format_ident:=0;
+
+@ @<Initialize table entries...@>=
+format_ident:=" (INITEX)";
+
+@ @<Declare act...@>=
+@!init procedure store_fmt_file;
+label found1,found2,done1,done2;
+var j,@!k,@!l:integer; {all-purpose indices}
+@!p,@!q: pointer; {all-purpose pointers}
+@!x: integer; {something to dump}
+@!w: four_quarters; {four ASCII codes}
+begin @<If dumping is not allowed, abort@>;
+@<Create the |format_ident|, open the format file,
+ and inform the user that dumping has begun@>;
+@<Dump constants for consistency check@>;
+@<Dump the string pool@>;
+@<Dump the dynamic memory@>;
+@<Dump the table of equivalents@>;
+@<Dump the font information@>;
+@<Dump the hyphenation tables@>;
+@<Dump a couple more things and the closing check word@>;
+@<Close the format file@>;
+end;
+tini
+
+@ Corresponding to the procedure that dumps a format file, we have a function
+that reads one in. The function returns |false| if the dumped format is
+incompatible with the present \TeX\ table sizes, etc.
+
+@d bad_fmt=6666 {go here if the format file is unacceptable}
+@d too_small(#)==begin wake_up_terminal;
+ wterm_ln('---! Must increase the ',#);
+@.Must increase the x@>
+ goto bad_fmt;
+ end
+
+@p @t\4@>@<Declare the function called |open_fmt_file|@>@;
+function load_fmt_file:boolean;
+label bad_fmt,exit;
+var j,@!k:integer; {all-purpose indices}
+@!p,@!q: pointer; {all-purpose pointers}
+@!x: integer; {something undumped}
+@!w: four_quarters; {four ASCII codes}
+begin @<Undump constants for consistency check@>;
+@<Undump the string pool@>;
+@<Undump the dynamic memory@>;
+@<Undump the table of equivalents@>;
+@<Undump the font information@>;
+@<Undump the hyphenation tables@>;
+@<Undump a couple more things and the closing check word@>;
+load_fmt_file:=true; return; {it worked!}
+bad_fmt: wake_up_terminal;
+ wterm_ln('(Fatal format file error; I''m stymied)');
+@.Fatal format file error@>
+load_fmt_file:=false;
+exit:end;
+
+@ The user is not allowed to dump a format file unless |save_ptr=0|.
+This condition implies that |cur_level=level_one|, hence
+the |xeq_level| array is constant and it need not be dumped.
+
+@<If dumping is not allowed, abort@>=
+if save_ptr<>0 then
+ begin print_err("You can't dump inside a group");
+@.You can't dump...@>
+ help1("`{...\dump}' is a no-no."); succumb;
+ end
+
+@ Format files consist of |memory_word| items, and we use the following
+macros to dump words of different types:
+
+@d dump_wd(#)==begin fmt_file^:=#; put(fmt_file);@+end
+@d dump_int(#)==begin fmt_file^.int:=#; put(fmt_file);@+end
+@d dump_hh(#)==begin fmt_file^.hh:=#; put(fmt_file);@+end
+@d dump_qqqq(#)==begin fmt_file^.qqqq:=#; put(fmt_file);@+end
+
+@<Glob...@>=
+@!fmt_file:word_file; {for input or output of format information}
+
+@ The inverse macros are slightly more complicated, since we need to check
+the range of the values we are reading in. We say `|undump(a)(b)(x)|' to
+read an integer value |x| that is supposed to be in the range |a<=x<=b|.
+
+@d undump_wd(#)==begin get(fmt_file); #:=fmt_file^;@+end
+@d undump_int(#)==begin get(fmt_file); #:=fmt_file^.int;@+end
+@d undump_hh(#)==begin get(fmt_file); #:=fmt_file^.hh;@+end
+@d undump_qqqq(#)==begin get(fmt_file); #:=fmt_file^.qqqq;@+end
+@d undump_end_end(#)==#:=x;@+end
+@d undump_end(#)==(x>#) then goto bad_fmt@+else undump_end_end
+@d undump(#)==begin undump_int(x); if (x<#) or undump_end
+@d undump_size_end_end(#)==too_small(#)@+else undump_end_end
+@d undump_size_end(#)==if x># then undump_size_end_end
+@d undump_size(#)==begin undump_int(x);
+ if x<# then goto bad_fmt; undump_size_end
+
+@ The next few sections of the program should make it clear how we use the
+dump/undump macros.
+
+@<Dump constants for consistency check@>=
+dump_int(@$);@/
+dump_int(mem_bot);@/
+dump_int(mem_top);@/
+dump_int(eqtb_size);@/
+dump_int(hash_prime);@/
+dump_int(hyph_size)
+
+@ Sections of a \.{WEB} program that are ``commented out'' still contribute
+strings to the string pool; therefore \.{INITEX} and \TeX\ will have
+the same strings. (And it is, of course, a good thing that they do.)
+@.WEB@>
+@^string pool@>
+
+@<Undump constants for consistency check@>=
+x:=fmt_file^.int;
+if x<>@$ then goto bad_fmt; {check that strings are the same}
+undump_int(x);
+if x<>mem_bot then goto bad_fmt;
+undump_int(x);
+if x<>mem_top then goto bad_fmt;
+undump_int(x);
+if x<>eqtb_size then goto bad_fmt;
+undump_int(x);
+if x<>hash_prime then goto bad_fmt;
+undump_int(x);
+if x<>hyph_size then goto bad_fmt
+
+@ @d dump_four_ASCII==
+ w.b0:=qi(so(str_pool[k])); w.b1:=qi(so(str_pool[k+1]));
+ w.b2:=qi(so(str_pool[k+2])); w.b3:=qi(so(str_pool[k+3]));
+ dump_qqqq(w)
+
+@<Dump the string pool@>=
+dump_int(pool_ptr);
+dump_int(str_ptr);
+for k:=0 to str_ptr do dump_int(str_start[k]);
+k:=0;
+while k+4<pool_ptr do
+ begin dump_four_ASCII; k:=k+4;
+ end;
+k:=pool_ptr-4; dump_four_ASCII;
+print_ln; print_int(str_ptr); print(" strings of total length ");
+print_int(pool_ptr)
+
+@ @d undump_four_ASCII==
+ undump_qqqq(w);
+ str_pool[k]:=si(qo(w.b0)); str_pool[k+1]:=si(qo(w.b1));
+ str_pool[k+2]:=si(qo(w.b2)); str_pool[k+3]:=si(qo(w.b3))
+
+@<Undump the string pool@>=
+undump_size(0)(pool_size)('string pool size')(pool_ptr);
+undump_size(0)(max_strings)('max strings')(str_ptr);
+for k:=0 to str_ptr do undump(0)(pool_ptr)(str_start[k]);
+k:=0;
+while k+4<pool_ptr do
+ begin undump_four_ASCII; k:=k+4;
+ end;
+k:=pool_ptr-4; undump_four_ASCII;
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr
+
+@ By sorting the list of available spaces in the variable-size portion of
+|mem|, we are usually able to get by without having to dump very much
+of the dynamic memory.
+
+We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid
+information even when it has not been gathering statistics.
+
+@<Dump the dynamic memory@>=
+sort_avail; var_used:=0;
+dump_int(lo_mem_max); dump_int(rover);
+p:=mem_bot; q:=rover; x:=0;
+repeat for k:=p to q+1 do dump_wd(mem[k]);
+x:=x+q+2-p; var_used:=var_used+q-p;
+p:=q+node_size(q); q:=rlink(q);
+until q=rover;
+var_used:=var_used+lo_mem_max-p; dyn_used:=mem_end+1-hi_mem_min;@/
+for k:=p to lo_mem_max do dump_wd(mem[k]);
+x:=x+lo_mem_max+1-p;
+dump_int(hi_mem_min); dump_int(avail);
+for k:=hi_mem_min to mem_end do dump_wd(mem[k]);
+x:=x+mem_end+1-hi_mem_min;
+p:=avail;
+while p<>null do
+ begin decr(dyn_used); p:=link(p);
+ end;
+dump_int(var_used); dump_int(dyn_used);
+print_ln; print_int(x);
+print(" memory locations dumped; current usage is ");
+print_int(var_used); print_char("&"); print_int(dyn_used)
+
+@ @<Undump the dynamic memory@>=
+undump(lo_mem_stat_max+1000)(hi_mem_stat_min-1)(lo_mem_max);
+undump(lo_mem_stat_max+1)(lo_mem_max)(rover);
+p:=mem_bot; q:=rover;
+repeat for k:=p to q+1 do undump_wd(mem[k]);
+p:=q+node_size(q);
+if (p>lo_mem_max)or((q>=rlink(q))and(rlink(q)<>rover)) then goto bad_fmt;
+q:=rlink(q);
+until q=rover;
+for k:=p to lo_mem_max do undump_wd(mem[k]);
+if mem_min<mem_bot-2 then {make more low memory available}
+ begin p:=llink(rover); q:=mem_min+1;
+ link(mem_min):=null; info(mem_min):=null; {we don't use the bottom word}
+ rlink(p):=q; llink(rover):=q;@/
+ rlink(q):=rover; llink(q):=p; link(q):=empty_flag;
+ node_size(q):=mem_bot-q;
+ end;
+undump(lo_mem_max+1)(hi_mem_stat_min)(hi_mem_min);
+undump(null)(mem_top)(avail); mem_end:=mem_top;
+for k:=hi_mem_min to mem_end do undump_wd(mem[k]);
+undump_int(var_used); undump_int(dyn_used)
+
+@ @<Dump the table of equivalents@>=
+@<Dump regions 1 to 4 of |eqtb|@>;
+@<Dump regions 5 and 6 of |eqtb|@>;
+dump_int(par_loc); dump_int(write_loc);@/
+@<Dump the hash table@>
+
+@ @<Undump the table of equivalents@>=
+@<Undump regions 1 to 6 of |eqtb|@>;
+undump(hash_base)(frozen_control_sequence)(par_loc);
+par_token:=cs_token_flag+par_loc;@/
+undump(hash_base)(frozen_control_sequence)(write_loc);@/
+@<Undump the hash table@>
+
+@ The table of equivalents usually contains repeated information, so we dump it
+in compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in the
+format file represents $n+m$ consecutive entries of |eqtb|, with |m| extra
+copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$.
+
+@<Dump regions 1 to 4 of |eqtb|@>=
+k:=active_base;
+repeat j:=k;
+while j<int_base-1 do
+ begin if (equiv(j)=equiv(j+1))and(eq_type(j)=eq_type(j+1))and@|
+ (eq_level(j)=eq_level(j+1)) then goto found1;
+ incr(j);
+ end;
+l:=int_base; goto done1; {|j=int_base-1|}
+found1: incr(j); l:=j;
+while j<int_base-1 do
+ begin if (equiv(j)<>equiv(j+1))or(eq_type(j)<>eq_type(j+1))or@|
+ (eq_level(j)<>eq_level(j+1)) then goto done1;
+ incr(j);
+ end;
+done1:dump_int(l-k);
+while k<l do
+ begin dump_wd(eqtb[k]); incr(k);
+ end;
+k:=j+1; dump_int(k-l);
+until k=int_base
+
+@ @<Dump regions 5 and 6 of |eqtb|@>=
+repeat j:=k;
+while j<eqtb_size do
+ begin if eqtb[j].int=eqtb[j+1].int then goto found2;
+ incr(j);
+ end;
+l:=eqtb_size+1; goto done2; {|j=eqtb_size|}
+found2: incr(j); l:=j;
+while j<eqtb_size do
+ begin if eqtb[j].int<>eqtb[j+1].int then goto done2;
+ incr(j);
+ end;
+done2:dump_int(l-k);
+while k<l do
+ begin dump_wd(eqtb[k]); incr(k);
+ end;
+k:=j+1; dump_int(k-l);
+until k>eqtb_size
+
+@ @<Undump regions 1 to 6 of |eqtb|@>=
+k:=active_base;
+repeat undump_int(x);
+if (x<1)or(k+x>eqtb_size+1) then goto bad_fmt;
+for j:=k to k+x-1 do undump_wd(eqtb[j]);
+k:=k+x;
+undump_int(x);
+if (x<0)or(k+x>eqtb_size+1) then goto bad_fmt;
+for j:=k to k+x-1 do eqtb[j]:=eqtb[k-1];
+k:=k+x;
+until k>eqtb_size
+
+@ A different scheme is used to compress the hash table, since its lower
+region is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output
+two words, |p| and |hash[p]|. The hash table is, of course, densely packed
+for |p>=hash_used|, so the remaining entries are output in a~block.
+
+@<Dump the hash table@>=
+dump_int(hash_used); cs_count:=frozen_control_sequence-1-hash_used;
+for p:=hash_base to hash_used do if text(p)<>0 then
+ begin dump_int(p); dump_hh(hash[p]); incr(cs_count);
+ end;
+for p:=hash_used+1 to undefined_control_sequence-1 do dump_hh(hash[p]);
+dump_int(cs_count);@/
+print_ln; print_int(cs_count); print(" multiletter control sequences")
+
+@ @<Undump the hash table@>=
+undump(hash_base)(frozen_control_sequence)(hash_used); p:=hash_base-1;
+repeat undump(p+1)(hash_used)(p); undump_hh(hash[p]);
+until p=hash_used;
+for p:=hash_used+1 to undefined_control_sequence-1 do undump_hh(hash[p]);
+undump_int(cs_count)
+
+@ @<Dump the font information@>=
+dump_int(fmem_ptr);
+for k:=0 to fmem_ptr-1 do dump_wd(font_info[k]);
+dump_int(font_ptr);
+for k:=null_font to font_ptr do
+ @<Dump the array info for internal font number |k|@>;
+print_ln; print_int(fmem_ptr-7); print(" words of font info for ");
+print_int(font_ptr-font_base); print(" preloaded font");
+if font_ptr<>font_base+1 then print_char("s")
+
+@ @<Undump the font information@>=
+undump_size(7)(font_mem_size)('font mem size')(fmem_ptr);
+for k:=0 to fmem_ptr-1 do undump_wd(font_info[k]);
+undump_size(font_base)(font_max)('font max')(font_ptr);
+for k:=null_font to font_ptr do
+ @<Undump the array info for internal font number |k|@>
+
+@ @<Dump the array info for internal font number |k|@>=
+begin dump_qqqq(font_check[k]);
+dump_int(font_size[k]);
+dump_int(font_dsize[k]);
+dump_int(font_params[k]);@/
+dump_int(hyphen_char[k]);
+dump_int(skew_char[k]);@/
+dump_int(font_name[k]);
+dump_int(font_area[k]);@/
+dump_int(font_bc[k]);
+dump_int(font_ec[k]);@/
+dump_int(char_base[k]);
+dump_int(width_base[k]);
+dump_int(height_base[k]);@/
+dump_int(depth_base[k]);
+dump_int(italic_base[k]);
+dump_int(lig_kern_base[k]);@/
+dump_int(kern_base[k]);
+dump_int(exten_base[k]);
+dump_int(param_base[k]);@/
+dump_int(font_glue[k]);@/
+dump_int(bchar_label[k]);
+dump_int(font_bchar[k]);
+dump_int(font_false_bchar[k]);@/
+print_nl("\font"); print_esc(font_id_text(k)); print_char("=");
+print_file_name(font_name[k],font_area[k],"");
+if font_size[k]<>font_dsize[k] then
+ begin print(" at "); print_scaled(font_size[k]); print("pt");
+ end;
+end
+
+@ @<Undump the array info for internal font number |k|@>=
+begin undump_qqqq(font_check[k]);@/
+undump_int(font_size[k]);
+undump_int(font_dsize[k]);
+undump(min_halfword)(max_halfword)(font_params[k]);@/
+undump_int(hyphen_char[k]);
+undump_int(skew_char[k]);@/
+undump(0)(str_ptr)(font_name[k]);
+undump(0)(str_ptr)(font_area[k]);@/
+undump(0)(255)(font_bc[k]);
+undump(0)(255)(font_ec[k]);@/
+undump_int(char_base[k]);
+undump_int(width_base[k]);
+undump_int(height_base[k]);@/
+undump_int(depth_base[k]);
+undump_int(italic_base[k]);
+undump_int(lig_kern_base[k]);@/
+undump_int(kern_base[k]);
+undump_int(exten_base[k]);
+undump_int(param_base[k]);@/
+undump(min_halfword)(lo_mem_max)(font_glue[k]);@/
+undump(0)(fmem_ptr-1)(bchar_label[k]);
+undump(min_quarterword)(non_char)(font_bchar[k]);
+undump(min_quarterword)(non_char)(font_false_bchar[k]);
+end
+
+@ @<Dump the hyphenation tables@>=
+dump_int(hyph_count);
+for k:=0 to hyph_size do if hyph_word[k]<>0 then
+ begin dump_int(k); dump_int(hyph_word[k]); dump_int(hyph_list[k]);
+ end;
+print_ln; print_int(hyph_count); print(" hyphenation exception");
+if hyph_count<>1 then print_char("s");
+if trie_not_ready then init_trie;
+dump_int(trie_max);
+for k:=0 to trie_max do dump_hh(trie[k]);
+dump_int(trie_op_ptr);
+for k:=1 to trie_op_ptr do
+ begin dump_int(hyf_distance[k]);
+ dump_int(hyf_num[k]);
+ dump_int(hyf_next[k]);
+ end;
+print_nl("Hyphenation trie of length "); print_int(trie_max);
+@.Hyphenation trie...@>
+print(" has "); print_int(trie_op_ptr); print(" op");
+if trie_op_ptr<>1 then print_char("s");
+print(" out of "); print_int(trie_op_size);
+for k:=255 downto 0 do if trie_used[k]>min_quarterword then
+ begin print_nl(" "); print_int(qo(trie_used[k]));
+ print(" for language "); print_int(k);
+ dump_int(k); dump_int(qo(trie_used[k]));
+ end
+
+@ Only ``nonempty'' parts of |op_start| need to be restored.
+
+@<Undump the hyphenation tables@>=
+undump(0)(hyph_size)(hyph_count);
+for k:=1 to hyph_count do
+ begin undump(0)(hyph_size)(j);
+ undump(0)(str_ptr)(hyph_word[j]);
+ undump(min_halfword)(max_halfword)(hyph_list[j]);
+ end;
+undump_size(0)(trie_size)('trie size')(j); @+init trie_max:=j;@+tini
+for k:=0 to j do undump_hh(trie[k]);
+undump_size(0)(trie_op_size)('trie op size')(j); @+init trie_op_ptr:=j;@+tini
+for k:=1 to j do
+ begin undump(0)(63)(hyf_distance[k]); {a |small_number|}
+ undump(0)(63)(hyf_num[k]);
+ undump(min_quarterword)(max_quarterword)(hyf_next[k]);
+ end;
+init for k:=0 to 255 do trie_used[k]:=min_quarterword;@+tini@;@/
+k:=256;
+while j>0 do
+ begin undump(0)(k-1)(k); undump(1)(j)(x);@+init trie_used[k]:=qi(x);@+tini@;@/
+ j:=j-x; op_start[k]:=qo(j);
+ end;
+@!init trie_not_ready:=false @+tini
+
+@ We have already printed a lot of statistics, so we set |tracing_stats:=0|
+to prevent them from appearing again.
+
+@<Dump a couple more things and the closing check word@>=
+dump_int(interaction); dump_int(format_ident); dump_int(69069);
+tracing_stats:=0
+
+@ @<Undump a couple more things and the closing check word@>=
+undump(batch_mode)(error_stop_mode)(interaction);
+undump(0)(str_ptr)(format_ident);
+undump_int(x);
+if (x<>69069)or eof(fmt_file) then goto bad_fmt
+
+@ @<Create the |format_ident|...@>=
+selector:=new_string;
+print(" (preloaded format="); print(job_name); print_char(" ");
+print_int(year); print_char(".");
+print_int(month); print_char("."); print_int(day); print_char(")");
+if interaction=batch_mode then selector:=log_only
+else selector:=term_and_log;
+str_room(1);
+format_ident:=make_string;
+pack_job_name(format_extension);
+while not w_open_out(fmt_file) do
+ prompt_file_name("format file name",format_extension);
+print_nl("Beginning to dump on file ");
+@.Beginning to dump...@>
+slow_print(w_make_name_string(fmt_file)); flush_string;
+print_nl(""); slow_print(format_ident)
+
+@ @<Close the format file@>=
+w_close(fmt_file)
+
+@* \[51] The main program.
+This is it: the part of \TeX\ that executes all those procedures we have
+written.
+
+Well---almost. Let's leave space for a few more routines that we may
+have forgotten.
+
+@p @<Last-minute procedures@>
+
+@ We have noted that there are two versions of \TeX82. One, called \.{INITEX},
+@.INITEX@>
+has to be run first; it initializes everything from scratch, without
+reading a format file, and it has the capability of dumping a format file.
+The other one is called `\.{VIRTEX}'; it is a ``virgin'' program that needs
+@.VIRTEX@>
+to input a format file in order to get started. \.{VIRTEX} typically has
+more memory capacity than \.{INITEX}, because it does not need the space
+consumed by the auxiliary hyphenation tables and the numerous calls on
+|primitive|, etc.
+
+The \.{VIRTEX} program cannot read a format file instantaneously, of course;
+the best implementations therefore allow for production versions of \TeX\ that
+not only avoid the loading routine for \PASCAL\ object code, they also have
+a format file pre-loaded. This is impossible to do if we stick to standard
+\PASCAL; but there is a simple way to fool many systems into avoiding the
+initialization, as follows:\quad(1)~We declare a global integer variable
+called |ready_already|. The probability is negligible that this
+variable holds any particular value like 314159 when \.{VIRTEX} is first
+loaded.\quad(2)~After we have read in a format file and initialized
+everything, we set |ready_already:=314159|.\quad(3)~Soon \.{VIRTEX}
+will print `\.*', waiting for more input; and at this point we
+interrupt the program and save its core image in some form that the
+operating system can reload speedily.\quad(4)~When that core image is
+activated, the program starts again at the beginning; but now
+|ready_already=314159| and all the other global variables have
+their initial values too. The former chastity has vanished!
+
+In other words, if we allow ourselves to test the condition
+|ready_already=314159|, before |ready_already| has been
+assigned a value, we can avoid the lengthy initialization. Dirty tricks
+rarely pay off so handsomely.
+@^dirty \PASCAL@>
+@^system dependencies@>
+
+On systems that allow such preloading, the standard program called \.{TeX}
+should be the one that has \.{plain} format preloaded, since that agrees
+with {\sl The \TeX book}. Other versions, e.g., \.{AmSTeX}, should also
+@:TeXbook}{\sl The \TeX book@>
+@.AmSTeX@>
+@.plain@>
+be provided for commonly used formats.
+
+@<Glob...@>=
+@!ready_already:integer; {a sacrifice of purity for economy}
+
+@ Now this is really it: \TeX\ starts and ends here.
+
+The initial test involving |ready_already| should be deleted if the
+\PASCAL\ runtime system is smart enough to detect such a ``mistake.''
+@^system dependencies@>
+
+@p begin @!{|start_here|}
+history:=fatal_error_stop; {in case we quit during initialization}
+t_open_out; {open the terminal for output}
+if ready_already=314159 then goto start_of_TEX;
+@<Check the ``constant'' values...@>@;
+if bad>0 then
+ begin wterm_ln('Ouch---my internal constants have been clobbered!',
+ '---case ',bad:1);
+@.Ouch...clobbered@>
+ goto final_end;
+ end;
+initialize; {set global variables to their starting values}
+@!init if not get_strings_started then goto final_end;
+init_prim; {call |primitive| for each primitive}
+init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr; fix_date_and_time;
+tini@/
+ready_already:=314159;
+start_of_TEX: @<Initialize the output routines@>;
+@<Get the first line of input and prepare to start@>;
+history:=spotless; {ready to go!}
+main_control; {come to life}
+final_cleanup; {prepare for death}
+end_of_TEX: close_files_and_terminate;
+final_end: ready_already:=0;
+end.
+
+@ Here we do whatever is needed to complete \TeX's job gracefully on the
+local operating system. The code here might come into play after a fatal
+error; it must therefore consist entirely of ``safe'' operations that
+cannot produce error messages. For example, it would be a mistake to call
+|str_room| or |make_string| at this time, because a call on |overflow|
+might lead to an infinite loop.
+@^system dependencies@>
+
+Actually there's one way to get error messages, via |prepare_mag|;
+but that can't cause infinite recursion.
+@^recursion@>
+
+This program doesn't bother to close the input files that may still be open.
+
+@<Last-minute...@>=
+procedure close_files_and_terminate;
+var k:integer; {all-purpose index}
+begin @<Finish the extensions@>;
+@!stat if tracing_stats>0 then @<Output statistics about this job@>;@;@+tats@/
+wake_up_terminal; @<Finish the \.{DVI} file@>;
+if log_opened then
+ begin wlog_cr; a_close(log_file); selector:=selector-2;
+ if selector=term_only then
+ begin print_nl("Transcript written on ");
+@.Transcript written...@>
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+end;
+
+@ The present section goes directly to the log file instead of using
+|print| commands, because there's no need for these strings to take
+up |str_pool| memory when a non-{\bf stat} version of \TeX\ is being used.
+
+@<Output statistics...@>=
+if log_opened then
+ begin wlog_ln(' ');
+ wlog_ln('Here is how much of TeX''s memory',' you used:');
+@.Here is how much...@>
+ wlog(' ',str_ptr-init_str_ptr:1,' string');
+ if str_ptr<>init_str_ptr+1 then wlog('s');
+ wlog_ln(' out of ', max_strings-init_str_ptr:1);@/
+ wlog_ln(' ',pool_ptr-init_pool_ptr:1,' string characters out of ',
+ pool_size-init_pool_ptr:1);@/
+ wlog_ln(' ',lo_mem_max-mem_min+mem_end-hi_mem_min+2:1,@|
+ ' words of memory out of ',mem_end+1-mem_min:1);@/
+ wlog_ln(' ',cs_count:1,' multiletter control sequences out of ',
+ hash_size:1);@/
+ wlog(' ',fmem_ptr:1,' words of font info for ',
+ font_ptr-font_base:1,' font');
+ if font_ptr<>font_base+1 then wlog('s');
+ wlog_ln(', out of ',font_mem_size:1,' for ',font_max-font_base:1);@/
+ wlog(' ',hyph_count:1,' hyphenation exception');
+ if hyph_count<>1 then wlog('s');
+ wlog_ln(' out of ',hyph_size:1);@/
+ wlog_ln(' ',max_in_stack:1,'i,',max_nest_stack:1,'n,',@|
+ max_param_stack:1,'p,',@|
+ max_buf_stack+1:1,'b,',@|
+ max_save_stack+6:1,'s stack positions out of ',@|
+ stack_size:1,'i,',
+ nest_size:1,'n,',
+ param_size:1,'p,',
+ buf_size:1,'b,',
+ save_size:1,'s');
+ end
+
+@ We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has
+been scanned and |its_all_over|\kern-2pt.
+
+@<Last-minute...@>=
+procedure final_cleanup;
+label exit;
+var c:small_number; {0 for \.{\\end}, 1 for \.{\\dump}}
+begin c:=cur_chr;
+if job_name=0 then open_log_file;
+while input_ptr>0 do
+ if state=token_list then end_token_list@+else end_file_reading;
+while open_parens>0 do
+ begin print(" )"); decr(open_parens);
+ end;
+if cur_level>level_one then
+ begin print_nl("("); print_esc("end occurred ");
+ print("inside a group at level ");
+@:end_}{\.{(\\end occurred...)}@>
+ print_int(cur_level-level_one); print_char(")");
+ end;
+while cond_ptr<>null do
+ begin print_nl("("); print_esc("end occurred ");
+ print("when "); print_cmd_chr(if_test,cur_if);
+ if if_line<>0 then
+ begin print(" on line "); print_int(if_line);
+ end;
+ print(" was incomplete)");
+ if_line:=if_line_field(cond_ptr);
+ cur_if:=subtype(cond_ptr); temp_ptr:=cond_ptr;
+ cond_ptr:=link(cond_ptr); free_node(temp_ptr,if_node_size);
+ end;
+if history<>spotless then
+ if ((history=warning_issued)or(interaction<error_stop_mode)) then
+ if selector=term_and_log then
+ begin selector:=term_only;
+ print_nl("(see the transcript file for additional information)");
+@.see the transcript file...@>
+ selector:=term_and_log;
+ end;
+if c=1 then
+ begin @!init for c:=top_mark_code to split_bot_mark_code do
+ if cur_mark[c]<>null then delete_token_ref(cur_mark[c]);
+ if last_glue<>max_halfword then delete_glue_ref(last_glue);
+ store_fmt_file; return;@+tini@/
+ print_nl("(\dump is performed only by INITEX)"); return;
+@:dump_}{\.{\\dump...only by INITEX}@>
+ end;
+exit:end;
+
+@ @<Last-minute...@>=
+@!init procedure init_prim; {initialize all the primitives}
+begin no_new_control_sequence:=false;
+@<Put each...@>;
+no_new_control_sequence:=true;
+end;
+tini
+
+@ When we begin the following code, \TeX's tables may still contain garbage;
+the strings might not even be present. Thus we must proceed cautiously to get
+bootstrapped in.
+
+But when we finish this part of the program, \TeX\ is ready to call on the
+|main_control| routine to do its work.
+
+@<Get the first line...@>=
+begin @<Initialize the input routines@>;
+if (format_ident=0)or(buffer[loc]="&") then
+ begin if format_ident<>0 then initialize; {erase preloaded format}
+ if not open_fmt_file then goto final_end;
+ if not load_fmt_file then
+ begin w_close(fmt_file); goto final_end;
+ end;
+ w_close(fmt_file);
+ while (loc<limit)and(buffer[loc]=" ") do incr(loc);
+ end;
+if end_line_char_inactive then decr(limit)
+else buffer[limit]:=end_line_char;
+fix_date_and_time;@/
+@<Compute the magic offset@>;
+@<Initialize the print |selector|...@>;
+if (loc<limit)and(cat_code(buffer[loc])<>escape) then start_input;
+ {\.{\\input} assumed}
+end
+
+@* \[52] Debugging.
+Once \TeX\ is working, you should be able to diagnose most errors with
+the \.{\\show} commands and other diagnostic features. But for the initial
+stages of debugging, and for the revelation of really deep mysteries, you
+can compile \TeX\ with a few more aids, including the \PASCAL\ runtime
+checks and its debugger. An additional routine called |debug_help|
+will also come into play when you type `\.D' after an error message;
+|debug_help| also occurs just before a fatal error causes \TeX\ to succumb.
+@^debugging@>
+@^system dependencies@>
+
+The interface to |debug_help| is primitive, but it is good enough when used
+with a \PASCAL\ debugger that allows you to set breakpoints and to read
+variables and change their values. After getting the prompt `\.{debug \#}', you
+type either a negative number (this exits |debug_help|), or zero (this
+goes to a location where you can set a breakpoint, thereby entering into
+dialog with the \PASCAL\ debugger), or a positive number |m| followed by
+an argument |n|. The meaning of |m| and |n| will be clear from the
+program below. (If |m=13|, there is an additional argument, |l|.)
+@.debug \#@>
+
+@d breakpoint=888 {place where a breakpoint is desirable}
+
+@<Last-minute...@>=
+@!debug procedure debug_help; {routine to display various things}
+label breakpoint,exit;
+var k,@!l,@!m,@!n:integer;
+begin loop begin wake_up_terminal;
+ print_nl("debug # (-1 to exit):"); update_terminal;
+@.debug \#@>
+ read(term_in,m);
+ if m<0 then return
+ else if m=0 then
+ begin goto breakpoint;@\ {go to every label at least once}
+ breakpoint: m:=0; @{'BREAKPOINT'@}@\
+ end
+ else begin read(term_in,n);
+ case m of
+ @t\4@>@<Numbered cases for |debug_help|@>@;
+ othercases print("?")
+ endcases;
+ end;
+ end;
+exit:end;
+gubed
+
+@ @<Numbered cases...@>=
+1: print_word(mem[n]); {display |mem[n]| in all forms}
+2: print_int(info(n));
+3: print_int(link(n));
+4: print_word(eqtb[n]);
+5: print_word(font_info[n]);
+6: print_word(save_stack[n]);
+7: show_box(n);
+ {show a box, abbreviated by |show_box_depth| and |show_box_breadth|}
+8: begin breadth_max:=10000; depth_threshold:=pool_size-pool_ptr-10;
+ show_node_list(n); {show a box in its entirety}
+ end;
+9: show_token_list(n,null,1000);
+10: slow_print(n);
+11: check_mem(n>0); {check wellformedness; print new busy locations if |n>0|}
+12: search_mem(n); {look for pointers to |n|}
+13: begin read(term_in,l); print_cmd_chr(n,l);
+ end;
+14: for k:=0 to n do print(buffer[k]);
+15: begin font_in_short_display:=null_font; short_display(n);
+ end;
+16: panicking:=not panicking;
+
+@* \[53] Extensions.
+The program above includes a bunch of ``hooks'' that allow further
+capabilities to be added without upsetting \TeX's basic structure.
+Most of these hooks are concerned with ``whatsit'' nodes, which are
+intended to be used for special purposes; whenever a new extension to
+\TeX\ involves a new kind of whatsit node, a corresponding change needs
+to be made to the routines below that deal with such nodes,
+but it will usually be unnecessary to make many changes to the
+other parts of this program.
+
+In order to demonstrate how extensions can be made, we shall treat
+`\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}',
+`\.{\\special}', and `\.{\\setlanguage}' as if they were extensions.
+These commands are actually primitives of \TeX, and they should
+appear in all implementations of the system; but let's try to imagine
+that they aren't. Then the program below illustrates how a person
+could add them.
+
+Sometimes, of course, an extension will require changes to \TeX\ itself;
+no system of hooks could be complete enough for all conceivable extensions.
+The features associated with `\.{\\write}' are almost all confined to the
+following paragraphs, but there are small parts of the |print_ln| and
+|print_char| procedures that were introduced specifically to \.{\\write}
+characters. Furthermore one of the token lists recognized by the scanner
+is a |write_text|; and there are a few other miscellaneous places where we
+have already provided for some aspect of \.{\\write}. The goal of a \TeX\
+extender should be to minimize alterations to the standard parts of the
+program, and to avoid them completely if possible. He or she should also
+be quite sure that there's no easy way to accomplish the desired goals
+with the standard features that \TeX\ already has. ``Think thrice before
+extending,'' because that may save a lot of work, and it will also keep
+incompatible extensions of \TeX\ from proliferating.
+@^system dependencies@>
+@^extensions to \TeX@>
+
+@ First let's consider the format of whatsit nodes that are used to represent
+the data associated with \.{\\write} and its relatives. Recall that a whatsit
+has |type=whatsit_node|, and the |subtype| is supposed to distinguish
+different kinds of whatsits. Each node occupies two or more words; the
+exact number is immaterial, as long as it is readily determined from the
+|subtype| or other data.
+
+We shall introduce five |subtype| values here, corresponding to the
+control sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, \.{\\special}, and
+\.{\\setlanguage}. The second word of I/O whatsits has a |write_stream| field
+that identifies the write-stream number (0 to 15, or 16 for out-of-range and
+positive, or 17 for out-of-range and negative).
+In the case of \.{\\write} and \.{\\special}, there is also a field that
+points to the reference count of a token list that should be sent. In the
+case of \.{\\openout}, we need three words and three auxiliary subfields
+to hold the string numbers for name, area, and extension.
+
+@d write_node_size=2 {number of words in a write/whatsit node}
+@d open_node_size=3 {number of words in an open/whatsit node}
+@d open_node=0 {|subtype| in whatsits that represent files to \.{\\openout}}
+@d write_node=1 {|subtype| in whatsits that represent things to \.{\\write}}
+@d close_node=2 {|subtype| in whatsits that represent streams to \.{\\closeout}}
+@d special_node=3 {|subtype| in whatsits that represent \.{\\special} things}
+@d language_node=4 {|subtype| in whatsits that change the current language}
+@d what_lang(#)==link(#+1) {language number, in the range |0..255|}
+@d what_lhm(#)==type(#+1) {minimum left fragment, in the range |1..63|}
+@d what_rhm(#)==subtype(#+1) {minimum right fragment, in the range |1..63|}
+@d write_tokens(#) == link(#+1) {reference count of token list to write}
+@d write_stream(#) == info(#+1) {stream number (0 to 17)}
+@d open_name(#) == link(#+1) {string number of file name to open}
+@d open_area(#) == info(#+2) {string number of file area for |open_name|}
+@d open_ext(#) == link(#+2) {string number of file extension for |open_name|}
+
+@ The sixteen possible \.{\\write} streams are represented by the |write_file|
+array. The |j|th file is open if and only if |write_open[j]=true|. The last
+two streams are special; |write_open[16]| represents a stream number
+greater than 15, while |write_open[17]| represents a negative stream number,
+and both of these variables are always |false|.
+
+@<Glob...@>=
+@!write_file:array[0..15] of alpha_file;
+@!write_open:array[0..17] of boolean;
+
+@ @<Set init...@>=
+for k:=0 to 17 do write_open[k]:=false;
+
+@ Extensions might introduce new command codes; but it's best to use
+|extension| with a modifier, whenever possible, so that |main_control|
+stays the same.
+
+@d immediate_code=4 {command modifier for \.{\\immediate}}
+@d set_language_code=5 {command modifier for \.{\\setlanguage}}
+
+@<Put each...@>=
+primitive("openout",extension,open_node);@/
+@!@:open_out_}{\.{\\openout} primitive@>
+primitive("write",extension,write_node); write_loc:=cur_val;@/
+@!@:write_}{\.{\\write} primitive@>
+primitive("closeout",extension,close_node);@/
+@!@:close_out_}{\.{\\closeout} primitive@>
+primitive("special",extension,special_node);@/
+@!@:special_}{\.{\\special} primitive@>
+primitive("immediate",extension,immediate_code);@/
+@!@:immediate_}{\.{\\immediate} primitive@>
+primitive("setlanguage",extension,set_language_code);@/
+@!@:set_language_}{\.{\\setlanguage} primitive@>
+
+@ The variable |write_loc| just introduced is used to provide an
+appropriate error message in case of ``runaway'' write texts.
+
+@<Glob...@>=
+@!write_loc:pointer; {|eqtb| address of \.{\\write}}
+
+@ @<Cases of |print_cmd_chr|...@>=
+extension: case chr_code of
+ open_node:print_esc("openout");
+ write_node:print_esc("write");
+ close_node:print_esc("closeout");
+ special_node:print_esc("special");
+ immediate_code:print_esc("immediate");
+ set_language_code:print_esc("setlanguage");
+ othercases print("[unknown extension!]")
+ endcases;
+
+@ When an |extension| command occurs in |main_control|, in any mode,
+the |do_extension| routine is called.
+
+@<Cases of |main_control| that are for extensions...@>=
+any_mode(extension):do_extension;
+
+@ @<Declare act...@>=
+@t\4@>@<Declare procedures needed in |do_extension|@>@;
+procedure do_extension;
+var i,@!j,@!k:integer; {all-purpose integers}
+@!p,@!q,@!r:pointer; {all-purpose pointers}
+begin case cur_chr of
+open_node:@<Implement \.{\\openout}@>;
+write_node:@<Implement \.{\\write}@>;
+close_node:@<Implement \.{\\closeout}@>;
+special_node:@<Implement \.{\\special}@>;
+immediate_code:@<Implement \.{\\immediate}@>;
+set_language_code:@<Implement \.{\\setlanguage}@>;
+othercases confusion("ext1")
+@:this can't happen ext1}{\quad ext1@>
+endcases;
+end;
+
+@ Here is a subroutine that creates a whatsit node having a given |subtype|
+and a given number of words. It initializes only the first word of the whatsit,
+and appends it to the current list.
+
+@<Declare procedures needed in |do_extension|@>=
+procedure new_whatsit(@!s:small_number;@!w:small_number);
+var p:pointer; {the new node}
+begin p:=get_node(w); type(p):=whatsit_node; subtype(p):=s;
+link(tail):=p; tail:=p;
+end;
+
+@ The next subroutine uses |cur_chr| to decide what sort of whatsit is
+involved, and also inserts a |write_stream| number.
+
+@<Declare procedures needed in |do_ext...@>=
+procedure new_write_whatsit(@!w:small_number);
+begin new_whatsit(cur_chr,w);
+if w<>write_node_size then scan_four_bit_int
+else begin scan_int;
+ if cur_val<0 then cur_val:=17
+ else if cur_val>15 then cur_val:=16;
+ end;
+write_stream(tail):=cur_val;
+end;
+
+@ @<Implement \.{\\openout}@>=
+begin new_write_whatsit(open_node_size);
+scan_optional_equals; scan_file_name;@/
+open_name(tail):=cur_name; open_area(tail):=cur_area; open_ext(tail):=cur_ext;
+end
+
+@ When `\.{\\write 12\{...\}}' appears, we scan the token list `\.{\{...\}}'
+without expanding its macros; the macros will be expanded later when this
+token list is rescanned.
+
+@<Implement \.{\\write}@>=
+begin k:=cur_cs; new_write_whatsit(write_node_size);@/
+cur_cs:=k; p:=scan_toks(false,false); write_tokens(tail):=def_ref;
+end
+
+@ @<Implement \.{\\closeout}@>=
+begin new_write_whatsit(write_node_size); write_tokens(tail):=null;
+end
+
+@ When `\.{\\special\{...\}}' appears, we expand the macros in the token
+list as in \.{\\xdef} and \.{\\mark}.
+
+@<Implement \.{\\special}@>=
+begin new_whatsit(special_node,write_node_size); write_stream(tail):=null;
+p:=scan_toks(false,true); write_tokens(tail):=def_ref;
+end
+
+@ Each new type of node that appears in our data structure must be capable
+of being displayed, copied, destroyed, and so on. The routines that we
+need for write-oriented whatsits are somewhat like those for mark nodes;
+other extensions might, of course, involve more subtlety here.
+
+@<Basic printing...@>=
+procedure print_write_whatsit(@!s:str_number;@!p:pointer);
+begin print_esc(s);
+if write_stream(p)<16 then print_int(write_stream(p))
+else if write_stream(p)=16 then print_char("*")
+@.*\relax@>
+else print_char("-");
+end;
+
+@ @<Display the whatsit...@>=
+case subtype(p) of
+open_node:begin print_write_whatsit("openout",p);
+ print_char("="); print_file_name(open_name(p),open_area(p),open_ext(p));
+ end;
+write_node:begin print_write_whatsit("write",p);
+ print_mark(write_tokens(p));
+ end;
+close_node:print_write_whatsit("closeout",p);
+special_node:begin print_esc("special");
+ print_mark(write_tokens(p));
+ end;
+language_node:begin print_esc("setlanguage");
+ print_int(what_lang(p)); print(" (hyphenmin ");
+ print_int(what_lhm(p)); print_char(",");
+ print_int(what_rhm(p)); print_char(")");
+ end;
+othercases print("whatsit?")
+endcases
+
+@ @<Make a partial copy of the whatsit...@>=
+case subtype(p) of
+open_node: begin r:=get_node(open_node_size); words:=open_node_size;
+ end;
+write_node,special_node: begin r:=get_node(write_node_size);
+ add_token_ref(write_tokens(p)); words:=write_node_size;
+ end;
+close_node,language_node: begin r:=get_node(small_node_size);
+ words:=small_node_size;
+ end;
+othercases confusion("ext2")
+@:this can't happen ext2}{\quad ext2@>
+endcases
+
+@ @<Wipe out the whatsit...@>=
+begin case subtype(p) of
+open_node: free_node(p,open_node_size);
+write_node,special_node: begin delete_token_ref(write_tokens(p));
+ free_node(p,write_node_size); goto done;
+ end;
+close_node,language_node: free_node(p,small_node_size);
+othercases confusion("ext3")
+@:this can't happen ext3}{\quad ext3@>
+endcases;@/
+goto done;
+end
+
+@ @<Incorporate a whatsit node into a vbox@>=do_nothing
+
+@ @<Incorporate a whatsit node into an hbox@>=do_nothing
+
+@ @<Let |d| be the width of the whatsit |p|@>=d:=0
+
+@ @d adv_past(#)==@+if subtype(#)=language_node then
+ begin cur_lang:=what_lang(#); l_hyf:=what_lhm(#); r_hyf:=what_rhm(#);@+end
+
+@<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>=@+
+adv_past(cur_p)
+
+@ @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>=@+
+adv_past(s)
+
+@ @<Prepare to move whatsit |p| to the current page, then |goto contribute|@>=
+goto contribute
+
+@ @<Process whatsit |p| in |vert_break| loop, |goto not_found|@>=
+goto not_found
+
+@ @<Output the whatsit node |p| in a vlist@>=
+out_what(p)
+
+@ @<Output the whatsit node |p| in an hlist@>=
+out_what(p)
+
+@ After all this preliminary shuffling, we come finally to the routines
+that actually send out the requested data. Let's do \.{\\special} first
+(it's easier).
+
+@<Declare procedures needed in |hlist_out|, |vlist_out|@>=
+procedure special_out(@!p:pointer);
+var old_setting:0..max_selector; {holds print |selector|}
+@!k:pool_pointer; {index into |str_pool|}
+begin synch_h; synch_v;@/
+old_setting:=selector; selector:=new_string;
+show_token_list(link(write_tokens(p)),null,pool_size-pool_ptr);
+selector:=old_setting;
+str_room(1);
+if cur_length<256 then
+ begin dvi_out(xxx1); dvi_out(cur_length);
+ end
+else begin dvi_out(xxx4); dvi_four(cur_length);
+ end;
+for k:=str_start[str_ptr] to pool_ptr-1 do dvi_out(so(str_pool[k]));
+pool_ptr:=str_start[str_ptr]; {erase the string}
+end;
+
+@ To write a token list, we must run it through \TeX's scanner, expanding
+macros and \.{\\the} and \.{\\number}, etc. This might cause runaways,
+if a delimited macro parameter isn't matched, and runaways would be
+extremely confusing since we are calling on \TeX's scanner in the middle
+of a \.{\\shipout} command. Therefore we will put a dummy control sequence as
+a ``stopper,'' right after the token list. This control sequence is
+artificially defined to be \.{\\outer}.
+@:end_write_}{\.{\\endwrite}@>
+
+@<Initialize table...@>=
+text(end_write):="endwrite"; eq_level(end_write):=level_one;
+eq_type(end_write):=outer_call; equiv(end_write):=null;
+
+@ @<Declare procedures needed in |hlist_out|, |vlist_out|@>=
+procedure write_out(@!p:pointer);
+var old_setting:0..max_selector; {holds print |selector|}
+@!old_mode:integer; {saved |mode|}
+@!j:small_number; {write stream number}
+@!q,@!r:pointer; {temporary variables for list manipulation}
+begin @<Expand macros in the token list
+ and make |link(def_ref)| point to the result@>;
+old_setting:=selector; j:=write_stream(p);
+if write_open[j] then selector:=j
+else begin {write to the terminal if file isn't open}
+ if (j=17)and(selector=term_and_log) then selector:=log_only;
+ print_nl("");
+ end;
+token_show(def_ref); print_ln;
+flush_list(def_ref); selector:=old_setting;
+end;
+
+@ The final line of this routine is slightly subtle; at least, the author
+didn't think about it until getting burnt! There is a used-up token list
+@^Knuth, Donald Ervin@>
+on the stack, namely the one that contained |end_write_token|. (We
+insert this artificial `\.{\\endwrite}' to prevent runaways, as explained
+above.) If it were not removed, and if there were numerous writes on a
+single page, the stack would overflow.
+
+@d end_write_token==cs_token_flag+end_write
+
+@<Expand macros in the token list and...@>=
+q:=get_avail; info(q):=right_brace_token+"}";@/
+r:=get_avail; link(q):=r; info(r):=end_write_token; ins_list(q);@/
+begin_token_list(write_tokens(p),write_text);@/
+q:=get_avail; info(q):=left_brace_token+"{"; ins_list(q);
+{now we're ready to scan
+ `\.\{$\langle\,$token list$\,\rangle$\.{\} \\endwrite}'}
+old_mode:=mode; mode:=0;
+ {disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip}, \.{\\prevgraf}}
+cur_cs:=write_loc; q:=scan_toks(false,true); {expand macros, etc.}
+get_token;@+if cur_tok<>end_write_token then
+ @<Recover from an unbalanced write command@>;
+mode:=old_mode;
+end_token_list {conserve stack space}
+
+@ @<Recover from an unbalanced write command@>=
+begin print_err("Unbalanced write command");
+@.Unbalanced write...@>
+help2("On this page there's a \write with fewer real {'s than }'s.")@/
+("I can't handle that very well; good luck."); error;
+repeat get_token;
+until cur_tok=end_write_token;
+end
+
+@ The |out_what| procedure takes care of outputting whatsit nodes for
+|vlist_out| and |hlist_out|\kern-.3pt.
+
+@<Declare procedures needed in |hlist_out|, |vlist_out|@>=
+procedure out_what(@!p:pointer);
+var j:small_number; {write stream number}
+begin case subtype(p) of
+open_node,write_node,close_node:@<Do some work that has been queued up
+ for \.{\\write}@>;
+special_node:special_out(p);
+language_node:do_nothing;
+othercases confusion("ext4")
+@:this can't happen ext4}{\quad ext4@>
+endcases;
+end;
+
+@ We don't implement \.{\\write} inside of leaders. (The reason is that
+the number of times a leader box appears might be different in different
+implementations, due to machine-dependent rounding in the glue calculations.)
+@^leaders@>
+
+@<Do some work that has been queued up...@>=
+if not doing_leaders then
+ begin j:=write_stream(p);
+ if subtype(p)=write_node then write_out(p)
+ else begin if write_open[j] then a_close(write_file[j]);
+ if subtype(p)=close_node then write_open[j]:=false
+ else if j<16 then
+ begin cur_name:=open_name(p); cur_area:=open_area(p);
+ cur_ext:=open_ext(p);
+ if cur_ext="" then cur_ext:=".tex";
+ pack_cur_name;
+ while not a_open_out(write_file[j]) do
+ prompt_file_name("output file name",".tex");
+ write_open[j]:=true;
+ end;
+ end;
+ end
+
+@ The presence of `\.{\\immediate}' causes the |do_extension| procedure
+to descend to one level of recursion. Nothing happens unless \.{\\immediate}
+is followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'.
+@^recursion@>
+
+@<Implement \.{\\immediate}@>=
+begin get_x_token;
+if (cur_cmd=extension)and(cur_chr<=close_node) then
+ begin p:=tail; do_extension; {append a whatsit node}
+ out_what(tail); {do the action immediately}
+ flush_node_list(tail); tail:=p; link(p):=null;
+ end
+else back_input;
+end
+
+@ The \.{\\language} extension is somewhat different.
+We need a subroutine that comes into play when a character of
+a non-|clang| language is being appended to the current paragraph.
+
+@<Declare action...@>=
+procedure fix_language;
+var @!l:ASCII_code; {the new current language}
+begin if language<=0 then l:=0
+else if language>255 then l:=0
+else l:=language;
+if l<>clang then
+ begin new_whatsit(language_node,small_node_size);
+ what_lang(tail):=l; clang:=l;@/
+ what_lhm(tail):=norm_min(left_hyphen_min);
+ what_rhm(tail):=norm_min(right_hyphen_min);
+ end;
+end;
+
+@ @<Implement \.{\\setlanguage}@>=
+if abs(mode)<>hmode then report_illegal_case
+else begin new_whatsit(language_node,small_node_size);
+ scan_int;
+ if cur_val<=0 then clang:=0
+ else if cur_val>255 then clang:=0
+ else clang:=cur_val;
+ what_lang(tail):=clang;
+ what_lhm(tail):=norm_min(left_hyphen_min);
+ what_rhm(tail):=norm_min(right_hyphen_min);
+ end
+
+@ @<Finish the extensions@>=
+for k:=0 to 15 do if write_open[k] then a_close(write_file[k])
+
+@* \[54] System-dependent changes.
+This section should be replaced, if necessary, by any special
+modifications of the program
+that are necessary to make \TeX\ work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the published program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* \[55] Index.
+Here is where you can find all uses of each identifier in the program,
+with underlined entries pointing to where the identifier was defined.
+If the identifier is only one letter long, however, you get to see only
+the underlined entries. {\sl All references are to section numbers instead of
+page numbers.}
+
+This index also lists error messages and other aspects of the program
+that you might want to look up some day. For example, the entry
+for ``system dependencies'' lists all sections that should receive
+special attention from people who are installing \TeX\ in a new
+operating environment. A list of various things that can't happen appears
+under ``this can't happen''. Approximately 40 sections are listed under
+``inner loop''; these account for about 60\pct! of \TeX's running time,
+exclusive of input and output.
diff --git a/systems/knuth/dist/tex/texbook.tex b/systems/knuth/dist/tex/texbook.tex
new file mode 100644
index 0000000000..23d626ed6d
--- /dev/null
+++ b/systems/knuth/dist/tex/texbook.tex
@@ -0,0 +1,28136 @@
+% This manual is copyright (C) 1984 by the American Mathematical Society.
+% All rights are reserved!
+% The file is distributed only for people to see its examples of TeX input,
+% not for use in the preparation of books like The TeXbook.
+% Permission for any other use of this file must be obtained in writing
+% from the copyright holder and also from the publisher (Addison-Wesley).
+\loop\iftrue
+ \errmessage{This manual is copyrighted and should not be TeXed}\repeat
+\pausing1 \input manmac
+\ifproofmode\message{Proof mode is on!}\pausing1\fi
+ % halftitle
+\titlepage
+\pageno=-1983
+\null\bigskip
+\line{\cmman The\hfill T\kern-10pt\lower13pt\hbox{E}\kern-5pt Xbook}
+\vfill
+\ifproofmode
+\rightline{The fine print in the upper right-hand}
+\rightline{corner of each page is a draft of intended}
+\rightline{index entries; it won't appear in the real book.}
+\rightline{Some index entries will be in |typewriter type|}
+\rightline{and/or preceded by {\tt\char`\\} or enclosed in \<$\ldots$>, etc;}
+\rightline{such typographic distinctions aren't shown here.}
+\rightline{An index entry often extends for several pages;}
+\rightline{the actual scope will be determined later.}
+\rightline{Please note things that should be indexed but aren't.}
+\fi
+\eject
+\titlepage\null\vfill\eject % blank page
+ % title
+\pageno=-1 % the front matter is numbered with roman numerals
+\font\auth=cmssdc10 scaled\magstep4 % used only on the title page
+\font\elevenbf=cmbx10 scaled\magstephalf % ditto
+\font\elevenit=cmti10 scaled\magstephalf % ditto
+\font\elevenrm=cmr10 scaled\magstephalf % ditto
+\titlepage
+\null\bigskip
+\line{\cmman The\hfill T\kern-10pt\lower13pt\hbox{E}\kern-5pt Xbook}
+^^{Knuth, Donald Ervin}
+^^{Bibby, Duane Robert}
+\vskip 1pc
+\baselineskip 13pt \elevenbf
+\halign to\hsize{#\hfil\tabskip 0pt plus 1fil&#\hfil\tabskip0pt\cr
+\kern5.5mm\auth DONALD \kern-1pt E. \kern-1pt KNUTH&
+ \elevenit Stanford University\cr
+\noalign{\vskip 12pc}
+&\elevenit I\kern.7ptllustrations by\cr
+&DU\kern-1ptANE BIBBY\cr
+\noalign{\vfill}
+&\setbox0=\hbox{\manual77}%
+\setbox2=\hbox to\wd0{\hss\manual6\hss}%
+\raise2.3mm\box2\kern-\wd0\box0\cr % A-W logo
+&ADDISON\kern.1em--WESLEY\cr
+%&PUBLISHING COMP\kern-.13emANY\kern-1.5mm\cr
+\noalign{\vskip.5pc \global\elevenrm}
+&Upper Saddle River, NJ\cr
+&Boston\enspace$\cdot$\enspace Indianapolis\cr
+&San Francisco\enspace$\cdot$\enspace New York\cr
+&Toronto\enspace$\cdot$\enspace Montr\'eal\cr
+&London\enspace$\cdot$\enspace Munich\cr
+&Paris\enspace$\cdot$\enspace Madrid\cr
+&Capetown\enspace$\cdot$\enspace Sydney\enspace$\cdot$\enspace Tokyo\cr
+&Singapore\enspace$\cdot$\enspace Mexico City\cr}
+\kern24pt
+\eject
+ % copyright
+\titlepage
+\eightpoint
+\vbox to 8pc{}
+\noindent\strut
+This manual describes \TeX\ Version 3.0. Some
+of the advanced features mentioned here are absent from earlier versions.
+\medskip
+\noindent
+The quotation on page \sesame\ is copyright $\copyright$ 1970 by Sesame
+Street, Inc., and used by permission of the Children's Television Workshop.
+\medskip
+\noindent
+\TeX\ is a trademark of the American Mathematical Society.
+\medskip
+\noindent
+{\manual opqrstuq} is a trademark of Addison\kern.1em--Wesley
+ Publishing Company.
+\bigskip\medskip
+\noindent
+{\bf Library of Congress cataloging in publication data}
+\medskip
+{\tt\halign{#\hfil\cr
+Knuth, Donald Ervin, 1938-\cr
+\ \ \ The TeXbook.\cr
+\noalign{\medskip}
+\ \ \ (Computers \& Typesetting ; A)\cr
+\ \ \ Includes index.\cr
+\ \ \ 1.~TeX (Computer system).\ \ 2.~Computerized\cr
+typesetting.\ \ 3.~Mathematics printing.\ \ I.~Title.\cr
+II.\ Series:\ \ Knuth, Donald Ervin, 1938-\ \ \ \ .\cr
+Computers \& typesetting ; A.\cr
+Z253.4.T47K58\ \ 1986\ \ \ \ \ \ \ \ \ 686.2\char13 2544\ \ \ \ \ \ 85-30845\cr
+ISBN 0-201-13447-0\cr
+ISBN 0-201-13448-9 (soft)\cr}}
+\vfill
+\noindent
+%{\sl \kern-1pt First hardcover edition, January 1986}
+%{\sl \kern-1pt Incorporates the final corrections made in 1996, and a few dozen more.}
+{\sl \kern-1pt Incorporates all corrections known in 2013.}
+\smallskip
+\noindent
+Internet page {\tt http://www-cs-faculty.stanford.edu/\char`\~
+ knuth/abcde.html}
+contains current information about this book and related books.
+\smallskip
+\noindent
+Copyright $\copyright$ 1984, 1986 by the American Mathematical Society
+\smallskip
+\noindent
+This book is published jointly by the American Mathematical Society
+and Addison\kern.1em--Wesley Publishing Company.
+All rights reserved. No part of this publication may be reproduced, stored in
+a retrieval system, or transmitted, in any form or by any means,
+electronic, mechanical, photocopying, recording, or otherwise, without
+the prior written permission of the publishers. Printed in the United
+States of America. % Published simultaneously in Canada.
+\medskip
+\noindent
+%ISBN 0-201-13448-9\par % paperback
+%ISBN 0-201-13447-0\par % hardcover
+ISBN-13 \enspace 978-0-201-13447-6\par\noindent
+ISBN-10 \enspace\phantom{978-}0-201-13447-0\par
+%33 34 35 36 37 38 39 DOC 09 08 07 06 % paperback
+%18 19 20 21 22 23 24 DOC 10 09 08 07 06 % hardcover
+\smallskip\noindent
+Text printed in the United States
+ at Courier Westford in Westford, Massachusetts.\par\noindent
+Nineteenth Printing, February 2012
+^^{Knuth, Donald Ervin}
+^^|\copyright|
+\eject
+ % dedication
+\titlepage
+\vbox to 8pc{}
+\rightline{\strut\eightssi To Jill:}
+^^{Knuth, Jill Carter}
+\vskip2pt
+\rightline{\eightssi For your books and brochures}
+\vfill
+\eject
+ % blank page
+\titlepage
+\null\vfill
+\eject
+ % the preface
+\titlepage
+\def\rhead{Preface}
+\vbox to 8pc{
+\rightline{\titlefont Preface}\vss}
+{\topskip 9pc % this makes equal sinkage throughout the Preface
+\vskip-\parskip
+\tenpoint
+\noindent\hang\hangafter-2
+\smash{\lower12pt\hbox to 0pt{\hskip-\hangindent\cmman G\hfill}}\hskip-16pt
+{\sc ENTLE} R{\sc EADER}: \strut This is a handbook about
+\TeX, a new typesetting system intended for the creation
+of beautiful books---and especially for books that contain a lot of
+mathematics. By preparing a manuscript in \TeX\ format, you will be
+telling a computer exactly how the manuscript is to be transformed into
+pages whose typographic quality is comparable to that of the world's
+finest printers; yet you won't need to do much more work than would be
+involved if you were simply typing the manuscript on an ordinary
+typewriter. In fact, your total work will probably be significantly less,
+if you consider the time it ordinarily takes to revise a typewritten manuscript,
+since computer text files are so easy to change and to reprocess. \
+(If such claims sound too good to be true, keep in mind that they were made
+by \TeX's designer, on a day when \TeX\ happened to
+be working, so the statements may be biased; but read on anyway.)
+
+This manual is intended for people who have never used \TeX\ before, as
+well as for experienced \TeX\ hackers. In other words, it's supposed to
+be a panacea that satisfies everybody, at the risk of satisfying nobody.
+Everything you need to know about \TeX\ is explained
+here somewhere, and so are a lot of things that most users don't care about.
+If you are preparing a simple manuscript, you won't need to
+learn much about \TeX\ at all; on the other hand, some
+things that go into the printing of technical books are inherently
+difficult, and if you wish to achieve more complex effects you
+will want to penetrate some of \TeX's darker corners. In order
+to make it possible for many types of users to read this manual
+effectively, a special sign is used to designate material that is
+for wizards only: When the symbol
+$$\vbox{\hbox{\dbend}\vskip 11pt}$$
+appears at the beginning of a paragraph, it warns of a ``^{dangerous bend}''
+in the train of thought; don't read the paragraph unless you need to.
+Brave and experienced drivers at the controls of \TeX\ will gradually enter
+more and more of these hazardous areas, but for most applications the
+details won't matter.
+
+All that you really ought to know, before reading on, is how to get a
+file of text into your computer using a standard editing program. This
+manual explains what that file ought to look like so that \TeX\ will
+understand it, but basic computer usage is not explained here.
+Some previous experience with technical typing will be quite helpful
+if you plan to do heavily mathematical work with \TeX, although it
+is not absolutely necessary. \TeX\ will do most of the necessary
+formatting of equations automatically; but users with more experience
+will be able to obtain better results, since there are so many ways
+to deal with formulas.
+
+Some of the paragraphs in this manual are so esoteric that they are rated
+$$\vcenter{\hbox{\dbend\kern1pt\dbend}\vskip 11pt}\;;$$
+everything that was said about single dangerous-bend signs goes double
+for these. You should probably have at least a month's experience with
+\TeX\ before you attempt to fathom such doubly dangerous depths
+of the system; in fact, most people will never need to know \TeX\
+in this much detail, even if they use it every day. After all, it's
+possible to drive a car without knowing how the engine works.
+Yet the whole story is here in case you're curious. \ (About \TeX, not cars.)
+
+The reason for such different levels of complexity is that people change
+as they grow accustomed to any powerful tool. When you first try to use \TeX,
+you'll find that some parts of it are very easy, while other things will take
+some getting used to. A day or so later, after you have successfully typeset a
+few pages, you'll be a different person; the concepts that used to bother you
+will now seem natural, and you'll be able to picture the final result in
+your mind before it comes out of the machine. But you'll probably run into
+challenges of a different kind. After another week your perspective will
+change again, and you'll grow in yet another way; and so on. As years go by,
+you might become involved with many different kinds of typesetting; and
+you'll find that your usage of \TeX\ will keep changing as your experience
+builds. That's the way it is with any powerful tool: There's always more
+to learn, and there are always better ways to do what you've done before.
+At every stage in the development you'll want a slightly different sort of
+manual. You may even want to write one yourself. By paying attention to
+the dangerous bend signs in this book you'll be better able to focus on
+the level that interests you at a particular time.
+
+Computer system manuals usually make dull reading, but take heart:
+This one contains {\sc ^{JOKES}} every once in a while, so you might actually
+enjoy reading it. \ (However, most of the jokes can only be appreciated
+properly if you understand a technical point that is being made---so
+read {\sl carefully}.)
+
+Another noteworthy characteristic of this manual is that it doesn't
+always tell the ^{truth}. When certain concepts of \TeX\ are introduced
+informally, general rules will be stated; afterwards you will find that the
+rules aren't strictly true. In general, the later chapters contain more
+reliable information than the earlier ones do. The author feels that this
+technique of deliberate lying will actually make it easier for you to
+learn the ideas. Once you understand a simple but false rule, it will not
+be hard to supplement that rule with its exceptions.
+
+In order to help you internalize what you're reading,
+{\sc ^{EXERCISES}} are sprinkled through this manual. It is generally intended
+that every reader should try every exercise, except for questions that appear
+in the ``dangerous bend'' areas. If you can't solve a problem, you
+can always look up the answer.
+But please, try first to solve it by yourself; then you'll learn more
+and you'll learn faster. Furthermore, if you think you do know the solution,
+you should turn to Appendix~A and check it out, just to make sure.
+
+The \TeX\ language described in this book is similar to the author's first
+attempt at a document formatting language, but the new system differs
+from the old~one in literally thousands of details. Both languages have
+been called \TeX; but henceforth the old language should be called
+\TeX78, and its use should rapidly fade away. Let's keep the name \TeX\
+for the language described here, since it is so much better, and since
+it is not going to change any more. ^^{TeX78}
+
+I wish to thank the hundreds of people who have helped me to formulate
+this ``definitive edition'' of the \TeX\ language, based on their
+experiences with preliminary versions of the system. My work at Stanford
+has been generously supported by the ^{National Science Foundation}, the
+^{Office of Naval Research}, the ^{IBM Corporation}, and the ^{System
+Development Foundation}. I also wish to thank the ^{American Mathematical
+Society} for its encouragement, for establishing the \TeX\ Users Group,
+and for publishing the {\sl ^{TUGboat}\/} newsletter (see Appendix~J).
+
+\medskip
+\line{{\sl Stanford, California}\hfil--- D. E. K.}^^{Knuth, Don}
+\line{\sl June 1983\hfil}
+
+} % end of the special \topskip
+\endchapter
+
+`Tis pleasant, sure, to see one's name in print;
+A book's a book, although there's nothing in 't.
+\author ^{BYRON}, {\sl English Bards and Scotch Reviewers\/} (1809)
+
+\bigskip
+
+A question arose as to whether we were covering the field
+that it was intended we should fill with this manual.
+\author RICHARD R. ^{DONNELLEY}, {\sl Proceedings, United %
+ Typothet{\ae} of America\/} (1897)
+
+\eject
+ % the table of contents
+\titlepage
+\vbox to 8pc{
+\rightline{\titlefont Contents}
+\vfill}
+^^{Contents of this manual, table}
+\def\rhead{Contents}
+\tenpoint
+\begingroup
+\countdef\counter=255
+\def\diamondleaders{\global\advance\counter by 1
+ \ifodd\counter \kern-10pt \fi
+ \leaders\hbox to 20pt{\ifodd\counter \kern13pt \else\kern3pt \fi
+ .\hss}}
+\baselineskip 15pt plus 5pt
+\def\\#1. #2. #3.{\line{\strut
+ \hbox to\parindent{\bf\hbox to 1em{\hss#1}\hss}%
+ \rm#2\diamondleaders\hfil\hbox to 2em{\hss#3}}}
+\\1. The Name of the Game. 1.
+\\2. Book Printing versus Ordinary Typing. 3.
+\\3. Controlling \TeX. 7.
+\\4. Fonts of Type. 13.
+\\5. Grouping. 19.
+\\6. Running \TeX. 23.
+\\7. How \TeX\ Reads What You Type. 37.
+\\8. The Characters You Type. 43.
+\\9. \TeX's Roman Fonts. 51.
+\\10. Dimensions. 57.
+\\11. Boxes. 63.
+\\12. Glue. 69.
+\\13. Modes. 85.
+\\14. How \TeX\ Breaks Paragraphs into Lines. 91.
+\\15. How \TeX\ Makes Lines into Pages. 109.
+\\16. Typing Math Formulas. 127.
+\\17. More about Math. 139.
+\\18. Fine Points of Mathematics Typing. 161.
+\\19. Displayed Equations. 185.
+\\20. Definitions (also called Macros). 199.
+\\21. Making Boxes. 221.
+\\22. Alignment. 231.
+\\23. Output Routines. 251.
+\eject
+\vbox to 8pc{}
+\\24. Summary of Vertical Mode. 267.
+\\25. Summary of Horizontal Mode. 285.
+\\26. Summary of Math Mode. 289.
+\\27. Recovery from Errors. 295.
+\null
+\leftline{\indent\bf Appendices}
+\\A. Answers to All the Exercises. 305.
+\\B. Basic Control Sequences. 339.
+\\C. Character Codes. 367.
+\\D. Dirty Tricks. 373.
+\\E. Example Formats. 403.
+\\F. Font Tables. 427.
+\\G. Generating Boxes from Formulas. 441.
+\\H. Hyphenation. 449.
+\\I\hskip 1pt. Index. 457.
+\\J\hskip 1pt. Joining the \TeX\ Community. 483.
+\null % 17 lines so far to balance the 23 on the other page
+\null % 18
+\null % 19
+\null % 20
+\null % 21
+\null % 22
+\null % 23
+\eject
+\endgroup
+ \beginchapter Chapter 1. The Name of\\the Game
+
+\pageno=1 % This is page number 1, number 1,
+English words like `technology' stem from a Greek root beginning with
+the letters $\tau\epsilon\chi\ldots\,$; and this same Greek word means {\sl
+art\/} as well as technology. Hence the name \TeX, which is an
+uppercase form of $\tau\epsilon\chi$.^^{TeX (actually \TeX), meaning of}
+^^|\tau|^^|\epsilon|^^|\chi|
+
+Insiders pronounce the $\chi$ of \TeX\ as a Greek chi, not as an `x', so that
+\TeX\ rhymes with the word blecchhh. It's the `ch' sound in Scottish words
+like {\sl loch\/} or German words like {\sl ach\/}; it's a Spanish `j' and a
+Russian `kh'. When you say it correctly to your computer, the terminal
+may become slightly moist.
+
+The purpose of this pronunciation exercise is to remind you that \TeX\ is
+primarily concerned with high-quality technical manuscripts: Its emphasis is
+on art and technology, as in the underlying Greek word. If you merely want
+to produce a passably good document---something acceptable and basically
+readable but not really beautiful---a simpler system will usually suffice.
+With \TeX\ the goal is to produce the {\sl finest\/} quality; this requires
+more attention to detail, but you will not find it much harder to go the
+extra distance, and you'll be able to take special pride in the finished
+product. ^^{beauty} % since truth is indexed, ...
+
+On the other hand, it's important to notice another thing about \TeX's name:
+The `E' is out of kilter. This ^^{logo}
+displaced `E' is a reminder that \TeX\ is about typesetting, and it
+distinguishes \TeX\ from other system names. In fact, ^{TEX} (pronounced
+{\sl tecks\/}) is the admirable {\sl Text EXecutive\/} processor developed by
+^{Honeywell Information Systems}. Since these two system names are
+^^{Bemer, Robert, see TEX, ASCII}
+pronounced quite differently, they should also be spelled differently. The
+correct way to refer to \TeX\ in a computer file, or when using some other
+medium that doesn't allow lowering of the `E', is to type `^|TeX|'. Then
+there will be no confusion with similar names, and people will be
+primed to pronounce everything properly.
+
+\exercise After you have mastered the material in this book, what will
+you be: a \TeX pert, or a \TeX nician?
+\answer A \TeX nician (underpaid); sometimes also called a \TeX acker.
+
+\endchapter
+
+They do certainly give
+very strange and new-fangled names to diseases.
+\author ^{PLATO}, {\sl The Republic}, Book 3 (c.\ 375 B.C.) % 405c
+
+\bigskip
+
+Technique! The very word is like the shriek
+Of outraged Art. It is the idiot name
+Given to effort by those who are too weak,
+Too weary, or too dull to play the game.
+\author LEONARD ^{BACON}, {\sl Sophia Trenton\/} (1920) % composed at Stanford
+
+\eject
+ \beginchapter Chapter 2. Book Printing\\versus\\Ordinary Typing
+
+When you first started using a computer terminal, you probably had to adjust
+to the difference between the digit `1' and the lowercase letter `l'.
+When you take the next step to the level of typography that is common in
+book publishing, a few more adjustments of the same kind need to be made;
+your eyes and your fingers need to learn to make a few more distinctions.
+
+In the first place, there are two kinds of ^{quotation marks} in books, but
+only one kind on the typewriter. Even your computer terminal, which has
+more characters than an ordinary typewriter, probably has only a
+non-oriented double-quote mark (|"|), because the standard ^{ASCII} code
+for computers was not invented with book publishing in mind. However, your
+terminal probably does have two flavors of single-quote marks, namely |`|
+and |'|; the second of these is useful also as an ^{apostrophe}.
+American keyboards usually contain a left-quote character that shows up
+as something like {\tt\char'22}, and an apostrophe or right-quote that
+looks like {\tt\char'15} or {\tt\char'23}.
+
+To produce double-quote marks with \TeX, you simply type two single-quote marks
+of the appropriate kind. For example, to get the phrase
+\begindisplay
+``I understand.''
+\enddisplay
+(including the quotation marks) you should type
+\begintt
+``I understand.''
+\endtt
+to your computer.
+
+A typewriter-like style of type will be used throughout this manual to indicate
+\TeX\ constructions that you might type on your terminal, so that the
+symbols actually typed are readily distinguishable from the output \TeX\ would
+produce and from the comments in the manual itself. Here are the symbols to be
+used in the examples:
+\begintt
+ABCDEFGHIJKLMNOPQRSTUVWXYZ
+abcdefghijklmnopqrstuvwxyz
+0123456789"#$%&@*+-=,.:;?!
+()<>[]{}`'\||/_^~
+\endtt
+If your computer terminal doesn't happen to have all of these, don't
+despair; \TeX\ can make~do with the ones you have. An additional symbol
+\begindisplay
+\]
+\enddisplay
+is used to stand for a {\sl ^{blank space}}, in case it is important
+to emphasize that a blank space is being typed; thus, what you {\sl
+really\/} type in the example above is
+\begintt
+``I|]understand.''
+\endtt
+Without such a symbol you would have
+difficulty seeing the invisible parts of certain constructions. But we
+won't be using `\]' very often, because spaces are usually visible enough.
+
+Book printing differs significantly from ordinary typing with respect to
+^{dashes}, ^{hyphens}, and ^{minus signs}. In good math books,
+these symbols are all different; in fact there usually are at least four
+different symbols:
+\begindisplay
+a hyphen (-);\cr
+an en-dash (--);\cr
+an em-dash (---);\cr
+a minus sign ($-$).\cr
+\enddisplay
+Hyphens are used for compound words like `daughter-in-law' and `X-rated'.
+^{En-dash}es are used for number ranges like `pages 13--34', and also in
+contexts like `exercise 1.2.6--52'. ^{Em-dash}es are used for punctuation in
+sentences---they are what we often call simply dashes. And minus signs are
+used in formulas. A conscientious user of \TeX\ will be careful to distinguish
+these four usages, and here is how to do it:
+\begindisplay
+for a hyphen, type a hyphen (|-|);\cr
+for an en-dash, type two hyphens (|--|);\cr
+for an em-dash, type three hyphens (|---|);\cr
+for a minus sign, type a hyphen in mathematics mode (|$-$|).\cr
+\enddisplay
+(Mathematics mode occurs between dollar signs; it is discussed later, so you
+needn't worry about it now.)
+
+\exercise Explain how to type the following sentence to \TeX: Alice said,
+``I always use an en-dash instead of a hyphen when specifying page numbers
+like `480--491' in a ^{bibliography}.''
+\answer |Alice said, ``I always use an en-dash instead of a hyphen when|\break
+|specifying page numbers like `480--491' in a bibliography.''| \
+(The wrong answer to this question ends with |'480-49l' in a bibliography."|)
+
+\exercise What do you think happens when you type four hyphens in a row?
+\answer You get em-dash and hyphen (----), which looks awful.
+
+If you look closely at most well-printed books, you will find that certain
+combinations of letters are treated as a unit. For example, this is true
+of the `f' and the `i' of `find'. Such combinations are called {\sl
+^{ligatures}}, and professional typesetters have traditionally been
+trained to watch for letter combinations such as |ff|, |fi|, |fl|, |ffi|, and
+|ffl|. \ (The reason is that words like `f{}ind' don't look very good in
+most styles of type unless a ligature is substituted for the letters that
+clash. It's somewhat surprising how often the traditional ligatures appear
+in English; other combinations are important in other languages.)
+
+\exercise Think of an English word that contains two ligatures.
+\answer fluffier firefly fisticuffs, flagstaff fireproofing,
+chiffchaff and riffraff.
+
+The good news is that you do
+{\sl not\/} have to concern yourself with ligatures: \TeX\ is perfectly
+capable of handling such things by itself, using the
+same mechanism that converts `|--|' into `--'. In fact, \TeX\ will also look
+for combinations of adjacent letters (like `|A|' next to `|V|'\thinspace)
+that ought to be moved closer together for better appearance; this is
+called {\sl ^{kerning}}.
+
+\medbreak
+To summarize this chapter: When using \TeX\ for straight copy, you type
+the copy as on an ordinary typewriter, except that you need to be careful
+about quotation marks, the number 1, and various kinds of hyphens/dashes.
+\TeX\ will automatically take care of other niceties like ligatures and
+kerning.
+
+\danger (Are you sure you should be reading this paragraph? The
+``^{dangerous bend}'' sign here is meant to warn you about material that
+ought to be skipped on first reading. And maybe also on second reading.
+The reader-beware paragraphs sometimes refer to concepts that aren't
+explained until later chapters.)
+
+\danger If your keyboard does not contain a left-quote symbol, you can
+type ^|\lq|, followed by a space if the next character is a letter, or
+followed by a |\| if the next character is a space. Similarly, ^|\rq|
+yields a right-quote character. Is that clear?
+\begintt
+\lq\lq|]I|]understand.\rq\rq\|]
+\endtt
+
+\danger In case you need to type ^{quotes within quotes}, for example a
+single quote followed by a double quote, you can't simply type
+\thinspace|'''|\thinspace\ because \TeX\ will interpret this as '''
+(namely, double quote followed by single quote). If you have already read
+Chapter~5, you might expect that the solution will be to use
+grouping---namely, to type something like \thinspace|{'}''|. But it turns
+out that this doesn't produce the desired result, because there is usually
+less space following a single right quote than there is following a double
+right quote: What you get is {'}'', which is indeed a single quote
+followed by a double quote (if you look at it closely enough), but it
+looks almost like three equally spaced single quotes. On the other hand,
+you certainly won't want to type \thinspace|'|\]|''|, because that space
+is much too large---it's just as large as the space between words---and
+\TeX\ might even start a new line at such a space when making up a
+paragraph! The solution is to type \thinspace|'\thinspace''|, which
+produces '\thinspace'' as desired.^^|\thinspace|
+
+\dangerexercise OK, now you know how to produce ''' and '\thinspace'';
+how do you get ``\thinspace` and `{}``\thinspace?
+\answer |``\thinspace`|; and either |`{}``| or |{`}``| or something similar.
+Reason: There's usually less space {\sl preceding\/} a single left quote than
+there is preceding a double left quote. \ (Left and right are opposites.)
+
+\dangerexercise Why do you think the author introduced the control
+sequence |\thinspace| to solve the adjacent-quotes problem, instead of
+recommending the trickier construction |'$|^|\,||$''| (which also works)?
+\answer Eliminating ^|\thinspace| would mean that a user need not learn
+the term; but it is not advisable to minimize terminology by ``overloading''
+math mode with tricky constructions. For example, a user who wishes to
+take advantage of \TeX's ^|\mathsurround| feature would be thwarted by
+non-mathematical uses of dollar signs. \ (Incidentally, neither |\thinspace|
+nor ^|\,| are built into \TeX; both are defined in terms of more
+primitive features, in Appendix~B.)
+
+\endchapter
+
+In modern Wit all printed Trash, is
+Set off with num'rous\/ {\rm Breaks}\raise.5ex\vbox{\hrule width 2em}%
+ and\/ {\rm Dashes}\raise.5ex\vbox{\hrule width 1em}
+% no period after the em-dash: stet!
+% Sir Walter Scott ruined this quote in his edition of Swift!
+\author JONATHAN ^{SWIFT}, {\sl On Poetry: A Rapsody\/} (1733) % lines 93--94
+% Rapsody: stet!
+
+\bigskip
+
+Some compositors still object to work
+in offices where type-composing machines are introduced.
+\author WILLIAM STANLEY ^{JEVONS}, {\sl Political Economy\/} (1878) % sec 55
+% "They are all afraid that if the work is done too easily and rapidly,
+% they will not be wanted to do it."
+% Jevons goes on to say that justifying and page makeup can't be done
+% profitably by machines, so the employees needn't fear losing their jobs.
+
+\eject
+ \beginchapter Chapter 3. Controlling\\\TeX
+
+Your keyboard has very few keys compared to the large number of symbols that you
+may want to specify. In order to make a limited keyboard sufficiently versatile,
+one of the characters that you can type is reserved for special use, and
+it is called the {\sl ^{escape character}}. Whenever you want to type
+something that controls the format of your manuscript, or something that
+doesn't use the keyboard in the ordinary way, you should type the escape
+character followed by an indication of what you want to do.
+
+Note: Some computer terminals have a key marked `|ESC|', but that is {\sl not\/}
+your escape character! It is a key that sends a special message to the operating
+system, so don't confuse it with what this manual calls ``escape.''
+
+\TeX\ allows any character to be used for escapes, but the ``^{backslash}''
+character `|\|' is usually adopted for this purpose, since backslashes are
+reasonably convenient to type and they are rarely needed in ordinary text.
+Things work out best when different \TeX\ users do things consistently,
+so we shall escape via backslashes in all the examples of this manual.
+
+Immediately after typing `|\|' (i.e., immediately after an escape
+character) you type a coded command telling \TeX\ what you have in mind.
+Such commands are called {\sl ^{control sequences}}. For example, you might type
+^^{markup commands, see control sequences}
+\begintt
+\input MS
+\endtt
+which (as we will see later) causes \TeX\ to begin reading a file called
+`|MS.tex|'; the string of characters `^|\input|' is a control sequence.
+Here's another example:
+\begintt
+George P\'olya and Gabor Szeg\"o.
+\endtt
+% sic; this is the spelling used in opening pages of their famous books
+% but I give the Hungarian spellings in the index
+\TeX\ converts this to `George P\'olya and Gabor Szeg\"o.' There are two
+^^{Polya}^^{Szego}^^{acute}^^{umlaut}
+control sequences, ^|\'| and ^|\"|, here; these control sequences
+have been used to place ^{accents} over some of the letters.
+
+Control sequences come in two flavors. The first kind, like |\input|,
+is called a {\sl^{control word}\/}; it
+consists of an escape character followed by one or more {\sl letters}, followed
+by a space or by something besides a letter. \ (\TeX\ has to know where the
+control sequence ends, so you must put a space after a control word if
+the next character is a letter. For example, if you type `|\inputMS|',
+\TeX\ will naturally interpret this as a control word with seven
+letters.) \ In case you're wondering what a ``^{letter}'' is, the answer
+is that \TeX\ normally regards the 52 symbols |A...Z| and |a...z| as
+letters. The digits |0...9| are {\sl not\/} considered to be
+letters, so they don't appear in control sequences of the first kind.
+
+A control sequence of the other kind, like |\'|, is called a {\sl
+^{control symbol}\/}; it consists of the escape
+character followed by a single {\sl nonletter}. In this case you don't
+need a space to separate the control sequence from a letter that follows,
+since control sequences of the second kind always have exactly one
+symbol after the escape character.
+
+\exercise What are the control sequences in `|\I'm \exercise3.1\\!|'\thinspace?
+\answer |\I|, |\exercise|, and |\\|. (The last of these is of type~2, i.e.,
+a control symbol, since the second backslash is not a letter; the first
+backslash keeps the second one from starting its own control sequence.)
+
+\exercise We've seen that the input |P\'olya| yields `P\'olya'. Can
+you guess how the French words `math\'ematique' and `centim\`etre'
+should be specified?
+\answer |math\'ematique| and |centim\`etre|.^^|\'|^^|\`|
+
+When a space comes after a control word (an all-letter control
+sequence), it is ignored by
+\TeX; i.e., it is not considered to be a ``real'' space belonging to the
+manuscript that is being typeset. But when a space comes after a control
+symbol, it's truly a space.
+
+Now the question arises, what do you do if you actually {\sl want\/} a
+space to appear after a control word? We will see later that \TeX\
+treats two or more consecutive spaces as a single ^{space}, so the answer
+is {\sl not\/} going to be ``type two spaces.'' The correct answer is to
+type ``control space,'' ^^|\ | namely
+\begintt
+\|]
+\endtt
+(the escape character followed by a blank space); \TeX\ will treat this as
+a space that is not to be ignored. Notice that |\|\] is a control
+sequence of the second kind, namely a control symbol, since there is a
+single nonletter (\]) following the escape character. Two consecutive
+spaces are considered to be equivalent to a single space, so further
+spaces immediately following |\|\] will be ignored. But if you want to
+enter, say, three consecutive spaces into a manuscript you can type
+`|\|\]|\|\]|\|\]'. Incidentally, typists are often taught to put two
+spaces at the ends of sentences; but we will see later that \TeX\ has its
+own way to produce extra space in such cases. Thus you needn't be
+consistent in the number of spaces you type.
+
+\danger Nonprinting control characters like \<return> might follow
+an escape character, and these lead to distinct control sequences according
+to the rules. \TeX\ is initially set up to treat |\|\<return> and
+|\|\<tab> ^^|\<return>|^^|\<tab>|
+the same as |\|\] (control space); these special control sequences
+should probably not be redefined, because you can't see the difference
+between them when you look at them in a file.
+^^{carriage-return, see <return>}
+
+It is usually unnecessary for you to use ``control space,'' since control
+sequences aren't often needed at the ends of words. But here's an example
+that might shed some light on the matter: This manual itself has been
+typeset by \TeX, and one of the things that occurs fairly often is the
+tricky ^{logo} `\TeX', which requires backspacing and lowering the E.
+There's a special control word
+\begintt
+\TeX
+\endtt
+that produces the half-dozen or so instructions necessary to typeset `\TeX'.
+When a phrase like `\TeX\ ignores spaces after control words.' is
+desired, the manuscript renders it as follows:
+\begintt
+\TeX\ ignores spaces after control words.
+\endtt
+Notice the extra |\| following ^|\TeX|; this produces the control space
+that is necessary because \TeX\ ignores spaces after control words.
+Without this extra |\|, the result would have been
+\begindisplay
+\TeX ignores spaces after control words.
+\enddisplay
+On the other hand, you can't simply put |\| after |\TeX| in all contexts.
+For example, consider the phrase
+\begintt
+the logo `\TeX'.
+\endtt
+In this case an extra backslash doesn't work at all; in fact,
+you get a curious result if you type
+\begintt
+the logo `\TeX\'.
+\endtt
+Can you guess what happens? \ Answer: The |\'| is a control sequence denoting
+an acute accent, as in our |P\'olya| example above; the effect is
+therefore to put an accent over the next nonblank character,
+which happens to be a period. In other words, you get an accented
+period, and the result is
+\begindisplay
+the logo `\TeX\'.
+\enddisplay
+Computers are good at following instructions, but not at reading your mind.
+
+\TeX\ understands about 900 control sequences as part of its built-in
+vocabulary, and all of them are explained in this manual somewhere. But
+you needn't worry about learning so many different things, because you won't
+really be needing very many of them unless you are faced with unusually
+complicated copy. Furthermore, the ones you do need to learn actually fall into
+relatively few categories, so they can be assimilated without great difficulty.
+For example, many of the control sequences are simply the names of special
+characters used in math formulas; you type `^|\pi|'~to get~`$\pi$',
+`^|\Pi|'~to get~`$\Pi$',
+`^|\aleph|'~to get~`$\aleph$',
+`^|\infty|'~to get~`$\infty$',
+`^|\le|'~to get~`$\le$',
+`^|\ge|'~to get~`$\ge$',
+`^|\ne|'~to get~`$\ne$',
+`^|\oplus|'~to get~`$\oplus$',
+`^|\otimes|'~to get~`$\otimes$'.
+Appendix~F contains several tables of such symbols.
+
+\danger There's no built-in relationship between ^{uppercase} and ^{lowercase}
+letters in control sequence names. For example, `|\pi|' and `|\Pi|'
+and `|\PI|' and `|\pI|' are four different control words.
+
+The 900 or so control sequences that were just mentioned actually aren't
+the whole story, because it's easy to define more. For example, if you
+want to substitute your own favorite names for math symbols, so that you
+can remember them better, you're free to go right ahead and do it;
+Chapter~20 explains how.
+
+About 300 of \TeX's control sequences are called {\sl ^{primitive}\/}; these
+are the low-level atomic operations that are not decomposable into simpler
+functions. All other control sequences are defined, ultimately, in terms
+of the primitive ones. For example, ^|\input| is a primitive operation,
+but ^|\'| and ^|\"| are not; the latter are defined in terms of an
+^|\accent| primitive.
+
+People hardly ever use \TeX's primitive control sequences in their
+manuscripts, because the primitives are $\ldots$ well $\ldots$ so
+{\sl primitive}. You have to type a lot of instructions when you are
+trying to make \TeX\ do low-level things; this takes time and invites
+mistakes. It is generally better to make use of higher-level control
+sequences that state what functions are desired, instead of typing
+out the way to achieve each function each time. The higher-level control
+sequences need to be defined only once in terms of primitives. For
+example, |\TeX| is a control sequence that means ``typeset the \TeX\ logo'';
+|\'| is a control sequence that means ``put an acute accent over the
+next character''; and both of these control sequences might require different
+combinations of primitives when the style of type changes. If \TeX's logo
+were to change, the author would simply have to change one definition, and the
+changes would appear automatically wherever they were needed. By contrast,
+an enormous amount of work would be necessary to change the logo if it
+were specified as a sequence of primitives each time.
+
+At a still higher level, there are control sequences that govern the
+overall format of a document. For example, in the present book the author
+typed `^|\exercise|' just before stating each exercise; this |\exercise|
+command was programmed to make \TeX\ do all of the following things:
+
+\nobreak\medskip
+\item\bull compute the exercise number (e.g., `3.2' for the second
+exercise in Chapter~3);
+\smallskip
+\item\bull typeset `\thinspace{\manual\char'170\rm\kern.15em
+ \ninebf EXERCISE \bf3.2}' with the appropriate typefaces, on a line by
+ itself, and with the triangle sticking out in the left margin;
+\smallskip
+\item\bull leave a little extra space just before that line, or begin
+ a new page at that line if appropriate;
+\smallskip
+\item\bull prohibit beginning a new page just after that line;
+\smallskip
+\item\bull suppress indentation on the following line.
+\medbreak\noindent
+It is obviously advantageous to avoid typing all of these individual
+instructions each time. And since the manual is entirely described in
+terms of high-level control sequences, it could be printed in a radically
+different format simply by changing a dozen or so definitions.
+% and sweating over the page layout in the math and alignment chapters!
+
+\danger How can a person distinguish a \TeX\ primitive from a control sequence
+that has been defined at a higher level? There are two ways: \ (1)~The index
+to this manual lists all of the control sequences that are discussed, and each
+primitive is marked with an asterisk. \ (2)~You can display the meaning of a
+control sequence while running \TeX\null. If you type `^|\show||\cs|'
+where |\cs| is any control sequence, \TeX\ will respond with its current
+meaning. For example, `|\show\input|' results in \hbox{`|> \input=\input.|'},
+because |\input| is primitive. On the other hand, `|\show|^|\thinspace|' yields
+\begintt
+> \thinspace=macro:
+->\kern .16667em .
+\endtt
+This means that |\thinspace| has been defined as an abbreviation for
+`|\kern|~|.16667em|~'. By typing `|\show|\penalty0|\kern|' you can verify
+that ^|\kern| is primitive. The results of\/ |\show| appear on your
+terminal and in the ^{log file} that you get after running \TeX.
+
+\dangerexercise Which of the control sequences |\|\] and
+|\|\<return> is primitive?
+\answer According to the index, |\|\] is primitive but
+|\|\<return> isn't. The command `|\def\^^M{\ }|' in
+Appendix~B is what actually defines |\|\<return>, since a
+return is representable as |^^M|. Asking \TeX\ to |\show\^^M|
+\looseness-1
+produces the response `|>| |\^^M=macro:->\|\]|.|'.
+
+In the following chapters we shall frequently discuss ``^{plain \TeX}''
+format, which is a set of about 600 ^{basic control sequences} that are
+defined in Appendix~B\null. These control sequences, together with the 300
+or so primitives, are usually present when \TeX\ begins to process a
+manuscript; that is why \TeX\ claims to know roughly 900 control sequences
+when it starts. We shall see how plain \TeX\ can be used to create
+documents in a flexible format that meets many people's needs, using some
+typefaces that come with the \TeX\ system. However, you should keep in
+mind that plain \TeX\ is only one of countless ^{formats} that can be
+designed on top of \TeX's primitives; if you want some other format, it
+will usually be possible to adapt \TeX\ so that it will handle whatever
+you have in mind. The best way to learn is probably to start with plain
+\TeX\ and to change its definitions, little by little, as you gain more
+experience.
+
+\danger Appendix E contains examples of formats that can be added to
+Appendix~B for special applications; for example, there is a set of
+definitions suitable for business correspondence. A complete specification
+of the format used to typeset this manual also appears in Appendix~E\null.
+Thus, if your goal is to learn how to design \TeX\ formats, you will
+probably want to study Appendix~E while mastering Appendix~B\null. After you
+have become skilled in the lore of control-sequence definition, you
+will probably have developed some formats that other people will want
+to use; you should then write a supplement to this manual, explaining
+your style rules.
+
+The main point of these remarks, as far as novice \TeX\ users are concerned, is
+that it is indeed possible to define nonstandard \TeX\ control sequences.
+When this manual says that something is part of ``plain \TeX,'' it means
+that \TeX\ doesn't insist on doing things exactly that way; a person
+could change the rules by changing one or more of the definitions in
+Appendix~B\null. But you can safely rely on the control sequences of plain
+\TeX\ until you become an experienced \TeX nical~typist.
+
+\ddangerexercise How many different control sequences of length~2
+(including the escape character) are possible? How many of length~3?
+\answer There are 256 of length~2; most of these are undefined when \TeX\
+begins. \ (\TeX\ allows any character to be an escape, but it does not
+distinguish between control sequences that start with different escape
+characters.) \
+If we assume that there are 52 letters, there are exactly $52^2$
+possible control sequences of length~3 (one for each pair of letters, from
+|AA| to |zz|). But Chapter~7 explains how to use ^|\catcode| to change any
+character into a ``^{letter}''; therefore it's possible to use any of
+$256^2$ potential control sequences of length~3.
+
+\endchapter
+
+Syllables govern the world.
+\author JOHN ^{SELDEN}, {\sl Table Talk\/} (1689) % section on Power
+
+\bigskip
+
+I claim not to have controlled events,
+but confess plainly that events have controlled me.
+\author ABRAHAM ^{LINCOLN} (1864) % letter to A. G. Hodges, April 4
+
+\eject
+ \beginchapter Chapter 4. Fonts\\of Type
+
+Occasionally you will want to change from one ^{typeface} to another, for
+example if you wish to be {\bf ^{bold}} or to {\sl emphasize\/} something.
+\TeX\ deals with sets of up to 256 characters called ``^{fonts}'' of type,
+and control sequences are used to select a particular font. For example,
+you could specify the last few words of the first sentence above
+in the following way, using the plain \TeX\ format of Appendix~B:
+\begintt
+to be \bf bold \rm or to \sl emphasize \rm something.
+\endtt
+Plain \TeX\ provides the following control sequences for changing fonts:
+\begindisplay
+^|\rm| switches to the normal ``roman'' typeface:&Roman\cr
+^|\sl| switches to a slanted roman typeface:&\sl Slanted\cr
+^|\it| switches to italic style:&\it Italic\cr
+^|\tt| switches to a typewriter-like face:&\tt Typewriter\cr
+^|\bf| switches to an extended boldface style:&\bf Bold\cr
+\enddisplay
+^^{typewriter type}^^{face}
+At the beginning of a run you get ^{roman type} (|\rm|) unless you specify
+otherwise.
+
+Notice that two of these faces have an ``^{oblique}'' slope for emphasis:
+{\sl ^{Slanted type} is essentially the same as roman, but the letters are
+slightly skewed, \it while the letters in ^{italic type} are drawn in a
+different style.} \ (You can perhaps best appreciate the difference between
+the roman and italic styles by contemplating {\tenu letters that are
+in an unslanted italic face.}) \ Typographic conventions are presently
+in a state of transition, because new technology has made it possible to
+do things that used to be prohibitively expensive; people are wrestling
+with the question of how much to use their new-found typographic freedom.
+Slanted roman type was introduced in the 1930s, but it first became widely
+used as an alternative to the conventional italic during the late 1970s.
+It can be bene\-ficial in mathematical texts, since slanted
+letters are distinguishable from the italic letters in math formulas.
+The double use of italic type for two different purposes---for example,
+when statements of theorems are italicized as well as the names of variables in
+those theorems---has led to some confusion, which can now be
+avoided with slanted type. People are not generally agreed about the relative
+merits of slanted versus italic, but slanted type is rapidly becoming a
+favorite for the titles of books and journals in bibliographies.
+
+Special fonts are effective for emphasis, but not for sustained reading;
+your eyes would tire if long portions of this manual were entirely set in
+a bold or slanted or italic face. Therefore roman type accounts for the
+bulk of most typeset material. But it's a nuisance to say `|\rm|' every
+time you want to go back to the roman style, so \TeX\ provides an easier
+way to do it, using ``^{curly brace}^^{brace}'' symbols: You can switch
+fonts inside the special symbols |{| and |}|, without affecting the fonts
+outside. For example, the displayed phrase at the beginning of this
+chapter is usually rendered
+\begintt
+to be {\bf bold} or to {\sl emphasize} something.
+\endtt
+This is a special case of the general idea of ``^{grouping}'' that we shall
+discuss in the next chapter. It's best to forget about the first way of
+changing fonts, and to use grouping instead; then your \TeX\ manuscripts
+will look more natural, and you'll probably
+never\footnote*{Well \dots, hardly ever.} have to type `|\rm|'.
+
+\exercise Explain how to type the bibliographic reference `Ulrich ^{Dieter},
+{\sl Journal f\"ur die reine und angewandte Mathematik\/ \bf201} (1959),
+37--70.' [Use grouping.]
+\answer |Ulrich Dieter, {\sl Journal f\"ur die reine und angewandte|\parbreak
+ |Mathematik\/ \bf201} (1959), 37--70.|\par\nobreak\smallskip\noindent
+It's convenient to use a single group for both |\sl| and |\bf| here. The
+`|\/|' is a refinement that you might not understand until you read the
+rest of Chapter~4.
+
+We have glossed over an important aspect of quality in the preceding
+discussion. Look, for example, at the {\it italicized} and {\sl slanted}
+words in this sentence. Since italic and slanted styles slope to the right,
+the d's stick into the spaces that separate these words from the roman
+type that follows; as a result, the spaces appear to be too skimpy,
+although they are correct at the base of the letters. To equalize the
+effective white space, \TeX\ allows you to put the special control sequence
+`^|\/|' just before switching back to unslanted letters. When you type
+\begintt
+{\it italicized\/} and {\sl slanted\/} words
+\endtt
+you get {\it italicized\/} and {\sl slanted\/} words that look better.
+The `|\/|' tells \TeX\ to add an\break % makes the line tighter, to be fair
+``{\sl^{italic correction}\/}'' to the
+previous letter, depending on that letter; this correction is about four
+times as much for an `$f$' as for a `$c$', in a typical italic font.
+
+Sometimes the italic correction is not desirable, because other factors take
+up the visual slack. The standard rule of thumb is to use |\/| just before
+switching from slanted or italic to roman or bold, unless the next
+character is a period or comma. For example, type
+\begintt
+{\it italics\/} for {\it emphasis}.
+\endtt
+Old manuals of style say that the ^{punctuation} after a word should be in the
+{\it same\/} font as that {\it word;\/} but an italic semicolon often looks
+wrong, so this convention is changing. When an italicized word occurs
+just before a semicolon, the author recommends typing `|{\it word\/};|'.
+
+\exercise {\it Explain how to typeset a\/ {\rm roman} word in the midst
+of an italicized sentence.}
+\answer |{\it Explain ... typeset a\/ {\rm roman} word ... sentence.}|
+Note the position of the italic correction in this case.
+
+\danger Every letter of every font has an italic correction, which you can
+bring to life by typing |\/|. The correction is usually zero in unslanted
+styles, but there are exceptions: To typeset a bold `{\bf f\/}' in quotes,
+you should say |a| |bold| \hbox{|`{\bf f\/}'|}, lest you get a bold `{\bf f}'.
+
+\ddangerexercise Define a control sequence |\ic| such that `|\ic c|' puts the
+italic correction of character $c$ into \TeX's register |\dimen0|.
+\answer |\def\ic#1{\setbox0=\hbox{#1\/}\dimen0=\wd0|\parbreak
+|\setbox0=\hbox{#1}\advance\dimen0 by -\wd0}|.
+
+\ddanger The primitive control sequence ^|\nullfont| stands for a font that
+has no characters. This font is always present, in case you haven't
+specified any others.
+
+Fonts vary in size as well as in shape. For example, the font you are now
+reading is called a ``10-point'' font, because certain features of its
+design are 10 ^{points} apart, when measured in printers' units. \ (We
+will study the point system later; for now, it should suffice to point out
+that the parentheses around this sentence are exactly 10 points tall---and
+the em-dash is just 10 points wide.) \ The ``^{dangerous bend}''
+sections of this manual are set in 9-point type, the foot\-notes in 8-point,
+^{subscripts} in 7-point or 6-point, sub-subscripts in 5-point.
+
+Each font used in a \TeX\ manuscript is associated with a control sequence;
+for example, the 10-point font in this paragraph is called ^|\tenrm|, and
+the corresponding 9-point font is called ^|\ninerm|. The slanted fonts that
+match |\tenrm| and |\ninerm| are called ^|\tensl| and ^|\ninesl|. These
+control sequences are not built into \TeX, nor are they the actual names
+of the fonts; \TeX\ users are just supposed to make up convenient names,
+whenever new fonts are introduced into a manuscript. Such control
+sequences are used to change typefaces.
+
+When fonts of different sizes are used simultaneously, \TeX\ will line the
+letters up according to their ``^{baseline}s.'' For example, if you type
+\begintt
+\tenrm smaller \ninerm and smaller
+\eightrm and smaller \sevenrm and smaller
+\sixrm and smaller \fiverm and smaller \tenrm
+\endtt
+the result is {smaller \ninerm and smaller \eightrm and smaller
+\sevenrm and smaller \sixrm and smaller \fiverm and smaller}. Of course
+this is something that authors and readers aren't accustomed to, because
+printers couldn't do such things with traditional lead types. Perhaps
+poets who wish to speak in {\fiverm a still small voice} will cause future
+books to make use of frequent font variations, but nowadays it's only
+an occasional font freak {\fiverm(like the author of this manual)} who
+likes such experiments. One should not get too carried away by the prospect
+of font switching unless there is good reason.
+
+An alert reader might well be confused at this point because we started out
+this chapter by saying that `|\rm|' is the command that switches to roman
+type, but later on we said that `|\tenrm|' is the way to do it. The truth
+is that both ways work. But it has become customary to set things up so that
+|\rm| means ``switch to roman type in the current size'' while |\tenrm| means
+``switch to roman type in the 10-point size.'' In plain \TeX\ format, nothing
+but 10-point fonts are provided, so |\rm| will always get you |\tenrm|; but
+in more complicated formats the meaning of\/ |\rm| will change in different
+parts of the manuscript. For example, in the format used by the author to
+typeset this manual, there's a control sequence `^|\tenpoint|' that causes
+|\rm| to mean |\tenrm|, |\sl| to mean |\tensl|, and so on, while
+`^|\ninepoint|' changes the definitions so that |\rm| means |\ninerm|,
+etc. There's another control sequence used to introduce the quotations at
+the end of each chapter; when the quotations are typed, |\rm| and |\sl|
+temporarily stand for {\eightss 8-point unslanted sans-serif type} and
+{\eightssi 8-point slanted sans-serif type}, respectively. This device of
+constantly redefining the abbreviations |\rm| and |\sl|, behind the
+scenes, frees the typist from the need to remember what size or style of
+type is currently being used.
+
+\exercise Why do you think the author chose the names `|\tenpoint|' and
+`|\tenrm|', etc., instead of `|\10point|' and `|\10rm|'\thinspace?
+\answer Control word names are made of letters, not digits.
+
+\dangerexercise Suppose that you have typed a manuscript using slanted type for
+emphasis, but your editor suddenly tells you to change all the slanted to
+italic. What's an easy way to do this?
+\answer Say |\def\sl{\it}| at the beginning, and delete other definitions
+of\/ |\sl| that might be present in your format file (e.g., there might be
+one inside a |\tenpoint| macro).
+
+\danger Each font has an external name that identifies it with respect to
+all other fonts in a particular library. For example, the font in this
+sentence is called `|cmr9|', which is an abbreviation for ``^{Computer
+Modern} Roman 9~point.'' ^^{cm fonts} In order to prepare \TeX\ for
+using this font, the command
+\begintt
+\font\ninerm=cmr9
+\endtt
+appears in Appendix E\null. In general you say `^|\font||\cs=|\<external
+font name>' to load the information about a particular font into \TeX's
+memory; afterwards the control sequence |\cs| will select that font for
+typesetting. Plain \TeX\ makes only sixteen fonts available initially (see
+Appendix~B and Appendix~F\null), but you can use |\font| to access
+anything that exists in your system's font library.
+
+\danger It is often possible to use a font at several different sizes, by
+magnifying or shrinking the character images. Each font has a so-called
+^{design size}, which reflects the size it normally has by default; for
+example, the design size of |cmr9| is 9~points. But on many systems there is
+also a range of sizes at which you can use a particular font, by scaling its
+dimensions up or down. To load a scaled font into \TeX's memory, you
+simply say `|\font\cs=|\<external font name> ^|at| \<desired size>'.
+For example, the command
+\begintt
+\font\magnifiedfiverm=cmr5 at 10pt
+\endtt
+brings in 5-point Computer Modern Roman at twice its normal size. \ (Caution:
+Before using this `|at|' feature, you should check to make sure that your
+typesetter supports the font at the size in question; \TeX\ will accept any
+\<desired size> that is positive and less than 2048 points, but the final
+output will not be right unless the scaled font really is available on your
+printing device.)
+
+\danger What's the difference between |cmr5| |at| |10pt| and the normal
+10-point font, |cmr10|? Plenty; a well-designed font will be drawn
+differently at different point sizes, and the letters will often have
+different relative heights and widths, in order to enhance readability.
+\begindisplay
+\tenrm Ten-point type is different from%
+ \magnifiedfiverm\ magnif{}ied f{}ive-point type.
+\enddisplay
+It is usually best to scale fonts only slightly with respect to
+their design size, unless the final product is going to be photographically
+reduced after \TeX\ has finished with it, or unless you are trying for an
+unusual effect.^^{magnification}^^{reduction}
+
+\danger Another way to magnify a font is to specify a scale factor that is
+relative to the design size. For example, the command
+\begintt
+\font\magnifiedfiverm=cmr5 scaled 2000
+\endtt
+is another way to bring in the font ^|cmr5| at double size. The scale factor
+is specified as an integer that represents a magnification ratio times~1000.
+Thus, a scale factor of 1200 specifies magnification by 1.2, etc.
+
+\dangerexercise State two ways to load font |cmr10| into \TeX's memory
+at half its normal size.
+\answer |\font\squinttenrm=cmr10 at 5pt|\parbreak
+ |\font\squinttenrm=cmr10 scaled 500|
+
+\font\onerm=cmr10 scaled\magstep1
+\font\onett=cmtt10 scaled\magstep1
+\font\tworm=cmr10 scaled\magstep2
+\font\twott=cmtt10 scaled\magstep2
+%\font\threerm=cmr10 scaled\magstep3 % such large magnifications look ugly
+%\font\threett=cmtt10 scaled\magstep3 % in a book context!
+\danger At many computer centers it has proved convenient to supply fonts
+at magnifications that grow in geometric ratios---something like equal-tempered
+tuning on a ^{piano}. The idea is to have all fonts available at their true
+size as well as at magnifications 1.2 and~1.44 (which is $1.2\times1.2$);
+perhaps also at magnification~1.728 ($=1.2\times1.2\times1.2$) and even
+higher. Then you can magnify an entire document by 1.2 or~1.44 and still
+stay within the set of available fonts. Plain \TeX\ provides the
+abbreviations ^|\magstep||0| for a scale factor of 1000, |\magstep1| for a
+scaled factor of 1200, |\magstep2| for 1440, and so on up to |\magstep5|.
+You say, for example,
+\begintt
+\font\bigtenrm=cmr10 scaled\magstep2
+\endtt
+to load font |cmr10| at $1.2\times1.2$ times its normal size.
+\begindisplay \lineskip5pt
+\tenrm\llap{``}This is {\tentt cmr10} at normal size
+ ({\tentt \char`\\magstep0}).''\cr
+\onerm\llap{``}This is {\onett cmr10} scaled once by 1.2
+ ({\onett \char`\\magstep1}).''\cr
+\tworm\llap{``}This is {\twott cmr10} scaled twice by 1.2
+ ({\twott \char`\\magstep2}).''\cr
+%\threerm\llap{``}This is {\threett cmr10} scaled by
+% {\threett \char`\\magstep3}.''\cr
+\enddisplay
+(Notice that a little magnification goes a long way.) \
+There's also ^|\magstephalf|, which magnifies by $\sqrt{1.2}$,
+i.e., halfway between steps 0 and~1.
+
+\danger Chapter~10 explains how to apply magnification to an entire
+document, over and above any magnification that has been specified when
+fonts are loaded. For example, if you have loaded a font that is scaled
+by |\magstep1| and if you also specify ^|\magnification||=\magstep2|, the
+actual font used for printing will be scaled by |\magstep3|. Similarly, if
+you load a font scaled by |\magstephalf| and if you also say
+|\magnification=\magstephalf|, the printed results will be
+scaled by |\magstep1|.
+
+\endchapter
+
+Type faces---like people's faces---have distinctive features
+indicating aspects of character. % I don't think he was kidding
+\author MARSHALL ^{LEE}, {\sl Bookmaking\/} (1965) % page 83
+
+\bigskip
+
+This was the Noblest Roman of them all.
+\author WILLIAM ^{SHAKESPEARE}, {\sl The Tragedie %
+ of Julius C\ae sar\/} (1599) % Act V, Scene 5, line 68
+ % For Shakespeare I'm using the spelling from First Folio (1623)
+ % (titles not from the contents page, but the running heads in the plays)
+ % but act/line numbers from The Riverside Shakespeare (throughout)
+
+\eject
+ \beginchapter Chapter 5. Grouping
+
+Every once in a while it is necessary to treat part of a manuscript as a
+unit, so you need to indicate somehow where that part begins and where it
+ends. For this purpose \TeX\ gives special interpretation to two
+``^{grouping characters},'' which (like the escape character) are
+treated differently from the normal symbols that you type. We assume in
+this manual that |{| and |}| are the grouping characters, since they
+are the ones used in plain \TeX. ^^{curly braces, see braces}
+
+We saw examples of grouping in the previous chapter, where it was mentioned
+that font changes inside a group do not affect the fonts in force outside.
+The same principle applies to almost anything else that is defined inside
+a group, as we will see later; for example, if you define a control sequence
+within some group, that definition will disappear when the group ends.
+In this way you can conveniently instruct \TeX\ to do something unusual,
+by changing its normal conventions temporarily inside of a group; since
+the changes are invisible from outside the group, there is no need to worry
+about messing up the rest of a manuscript by forgetting to restore the
+normal conventions when the unusual construction has been finished.
+Computer scientists have a name for this aspect of grouping, because it's
+an important aspect of programming languages in general; they call it
+``^{block structure},'' and definitions that are in force only within
+a group are said to be ``^{local}'' to that group.
+
+You might want to use grouping even when you don't care about block
+structure, just to have better control over spacing. For example, let's
+consider once more the control sequence ^|\TeX| that produces
+the logo `\TeX' in this manual: We observed in Chapter~3 that a blank space
+after this control sequence will be gobbled up unless one types
+`\hbox{|\TeX\ |}', yet it is a mistake to say `|\TeX\|' when the following
+character is not a blank space. Well, in {\sl all\/} cases it would be
+correct to specify the simple group
+\begintt
+{\TeX}
+\endtt
+whether or not the following character is a ^{space}, because the |}| stops
+\TeX\ from absorbing an optional space into |\TeX|. This might come in
+handy when you're using a text editor (e.g., when replacing all occurrences
+of a particular word by a control sequence). Another thing you could do is
+type
+\begintt
+\TeX{}
+\endtt
+using an {\sl empty\/} group for the same purpose: The `|{}|' here is a
+group of no characters, so it produces no output, but it does have the
+effect of stopping \TeX\ from skipping blanks.
+^^{empty group} ^^{lbrace rbrace}
+
+\exercise Sometimes you run into a rare word like `shelfful' that looks
+better as `shelf{}ful' without the `ff' ^{ligature}. How can you fool
+\TeX\ into thinking that there aren't two consecutive f's in such a word?
+\answer |{shelf}ful| or |shelf{}ful|, etc.; or even |shelf\/ful|, which
+yields a shelf\/ful instead of a shelf{\kern0pt}ful.
+In fact, the latter idea---to
+insert an ^{italic correction}---is preferable because \TeX\ will ^^|\/|
+reinsert the ff ligature by itself after ^{hyphenating} |shelf{}ful|. \
+(Appendix~H points out that ligatures are put into a hyphenated word that
+contains no ``^{explicit kerns},'' and an italic correction is an
+explicit kern.) \ But the italic correction may be too much (especially in an
+italic font); |shelf{|^|\kern||0pt}ful| is often best.
+
+\dangerexercise Explain how to get three blank spaces in a row without
+using `|\|\]'.^^{control space}
+\answer `\]|{|\]|}|\]' or `\]|{}|\]|{}|\]', etc. Plain \TeX\ also has a
+^|\space| macro, so you can type |\space\space\space|. \ (These aren't
+strictly equivalent to `|\|\]|\|\]|\|\]', since they adjust the spaces by
+the current ``^{space factor},'' as explained later.)
+
+But \TeX\ also uses grouping for another, quite different, purpose, namely
+to determine how much of your text is to be governed by certain control
+sequences. For example, if you want to center something on a line you can type
+\begintt
+\centerline{This information should be centered.}
+\endtt
+using the control sequence ^|\centerline| defined in plain \TeX\ format.
+
+Grouping is used in quite a few of \TeX's more intricate instructions; and
+it's possible to have groups within groups within groups, as you can see
+by glancing at Appendix~B\null. Complex grouping is generally unnecessary,
+however, in ordinary manuscripts, so you needn't worry about it. Just
+don't forget to finish each group that you've started, because a lost
+`|}|' might cause trouble.
+
+Here's an example of two groups, one ^{nested} inside the other:
+\begintt
+\centerline{This information should be {\it centered}.}
+\endtt
+As you might expect, \TeX\ will produce a centered line that also contains
+italics:
+$$\hbox{This information should be {\it centered}.}$$
+But let's look at the example more closely: `|\centerline|' appears outside
+the curly braces, while `|\it|' appears inside. Why are the two cases
+different? And how can a beginner learn to remember which is which?
+Answer: |\centerline| is a control sequence that applies only to the very next
+thing that follows, so you want to put braces around the text that is to
+be centered (unless that text consists of a single symbol or control sequence).
+For example, to center the \TeX\ logo on a line, it would suffice to
+type `|\centerline\TeX|', but to center the phrase `\TeX\ has groups' you
+need braces: `|\centerline{\TeX\ has groups}|'. On the other hand, |\it| is
+a control sequence that simply means ``change the current font''; it acts
+without looking ahead, so it affects {\sl everything\/} that follows, at
+least potentially. The braces surround |\it| in order to confine the font
+change to a local region.
+
+In other words, the two sets of braces in this example actually have different
+functions: One serves to treat several words of the text as if they
+were a single object, while the other provides local block structure.
+
+\exercise What do you think happens if you type the following:
+\begintt
+\centerline{This information should be {centered}.}
+\centerline So should this.
+\endtt
+\answer In the first case, you get the same result as if the innermost
+braces had not appeared at all, because you haven't used the grouping to
+change fonts or to control spacing or anything. \TeX\ doesn't mind if you
+want to waste your time making groups for no particular reason.
+But in the second case, the necessary braces were forgotten. You get the
+letter `S' centered on a line by itself, followed by a paragraph that
+begins with `o should this.' on the next line.
+
+\exercise And how about this one?
+\begintt
+\centerline{This information should be \it centered.}
+\endtt
+\answer You get the same result as if another pair of braces were present
+around `|\it centered|', except that the period is typeset from the
+italic font. \ (Both periods look about the same.) \ The |\it| font
+will not remain in force after the |\centerline|, but this is
+something of a coincidence: \TeX\ uses the braces to determine what
+text is to be centered, but then it removes the braces. The
+|\centerline| operation, as defined in Appendix~B\null, puts the
+resulting braceless text inside {\sl another\/} group; and that's why
+|\it| disappears after |\centerline|. \ (If you don't understand this,
+just don't risk leaving out braces in tricky situations, and you'll be OK.)
+
+\smallskip
+\dangerexercise Define a control sequence |\ital| so that a user could type
+`|\ital{text}|' instead of `|{\it text\/}|'. Discuss the pros and cons of
+|\ital| versus |\it|.
+\answer |\def\ital#1{{\it#1\/}}|. \ Pro:~Users might find this easier to
+learn, because it works more like |\centerline| and they don't have to
+remember to make the italic correction. \ Con:~To avoid the italic correction
+just before a {\it comma} or {\it period}, users should probably be taught
+another control sequence; for example, with
+\begintt
+\def\nocorr{\kern0pt }
+\endtt
+a user could type `|\ital{comma} or \ital{period\nocorr},|'. The alternative
+of putting a period or comma in italics, to avoid the italic correction,
+doesn't look as good. A long sequence of italics would be inefficient for
+\TeX, since the entire text for the argument to |\ital| must be read into
+memory only to be scanned again.
+
+\ddanger Subsequent chapters describe many primitive operations of \TeX\ for
+which the locality of grouping is important. For example, if one of \TeX's
+internal parameters is changed within a group, the previous contents of
+that parameter will be restored when the group ends. Sometimes, however,
+it's desirable to make a definition that transcends its current group. This
+effect can be obtained by prefixing `^|\global|' to the definition. For
+example, \TeX\ keeps the current page number in a register called~|\count0|,
+and the routine that outputs a page wants to increase the
+^{page number}. ^{Output routines} are always protected by enclosing them
+in groups, so that they do not inadvertently mess up the rest of \TeX; but
+the change to |\count0| would disappear if it were kept local to the
+output group. The command
+\begintt
+\global\advance\count0 by 1
+\endtt
+^^|\advance|
+solves the problem; it increases |\count0| and makes this value stick around
+at the end of the output routine. In general, |\global| makes the immediately
+following definition pertain to all existing groups, not just to the
+innermost one.
+
+\ddangerexercise If you think you understand local and global definitions,
+here's a little test to make sure: Suppose |\c| stands for `|\count1=|',
+|\g| stands for `|\global\count1=|', and |\s| stands for
+`|\showthe\count1|'. What values will be shown?
+\begintt
+{\c1\s\g2{\s\c3\s\g4\s\c5\s}\s\c6\s}\s
+\endtt
+\answer |{1 {2 3 4 5} 4 6} 4|.
+
+\ddanger Another way to obtain block structure with \TeX\ is to use the
+primitives ^|\begingroup| and ^|\endgroup|. These control sequences make it
+easy to begin a group within one control sequence and end it within
+another. The text that \TeX\ actually executes, after control sequences
+have been expanded, must have properly ^{nested groups}, i.e., groups that
+don't overlap. For example,
+\begintt
+{ \begingroup } \endgroup
+\endtt
+is not legitimate.
+
+\ddangerexercise Define control sequences |\beginthe|\<block name> and
+|\endthe|\<block name> that provide a ``named'' block structure. In other
+words,
+\begintt
+\beginthe{beguine}\beginthe{waltz}\endthe{waltz}\endthe{beguine}
+\endtt
+should be permissible, but not
+\begintt
+\beginthe{beguine}\beginthe{waltz}\endthe{beguine}\endthe{waltz}.
+\endtt
+\answer |\def\beginthe#1{\begingroup\def\blockname{#1}}|\parbreak
+|\def\endthe#1{\def\test{#1}%|\parbreak
+| \ifx\test\blockname\endgroup|\parbreak
+| \else\errmessage{You should have said|\parbreak
+| \string\endthe{\blockname}}\fi}|
+
+\endchapter
+
+I have had recourse to varieties of type,
+and to braces.
+\author JAMES ^{MUIRHEAD}, {\sl The Institutes of Gaius\/} (1880) % p. xii
+% he actually said braces for what we call brackets
+
+\bigskip
+
+An encounter group is a gathering, for a few hours or a few days,
+of twelve or eighteen personable, responsible, certifiably normal
+and temporarily smelly people.
+\author JANE ^{HOWARD}, {\sl Please Touch\/} (1970)
+
+\eject
+ \beginchapter Chapter 6. Running\\\TeX
+
+The best way to learn how to use \TeX\ is to use it. Thus, it's high time
+for you to sit down at a computer terminal and interact with the \TeX\
+system, trying things out to see what happens. Here are some small but
+complete examples suggested for your first encounter.
+^^{Running the program}
+
+Caution: This chapter is rather a long one. Why don't you stop reading
+now, and come back fresh tomorrow?
+
+\smallskip OK, let's suppose that you're rested and excited about having a
+trial run of \TeX\null. Step-by-step instructions for using it appear in this
+chapter. First do this: Go to the lab where the graphic output device is,
+since you will be wanting to see the output that you get---it won't really
+be satisfactory to run \TeX\ from a remote location, where you can't hold
+the generated documents in your own hands. Then log in; and start \TeX. \
+(You may have to ask somebody how to do this on your local computer. Usually
+the operating system prompts you for a command and you type `|tex|' or
+`|run| |tex|' or something like that.)
+
+When you're successful, \TeX\ will welcome you with a message such as
+\begintt
+This is TeX, Version 3.141 (preloaded format=plain 89.7.15)
+**
+\endtt
+The `^|**|' is \TeX's way of asking you for an input file name.
+% Incidentally, 89.7.15 was Jill's 50th birthday.
+
+Now type `^|\relax|' (including the backslash), and ^\<return>
+(or whatever is used to mean ``end-of-line'' on your terminal).
+\TeX\ is all geared up for action, ready to read a long manuscript; but
+you're saying that it's all right to take things easy, since this is
+going to be a real simple run. In fact, |\relax| is a control sequence
+that means ``do nothing.''
+
+The machine will type another asterisk at you. This time type something
+like `|Hello?|'\ and wait for another ^{asterisk}. Finally type `^|\end|',
+and stand back to see what happens.
+
+\TeX\ should respond with `^|[1]|' (meaning that it has finished page~1
+of your output); then the program will halt, probably with some
+indication that it has created a file called `|texput.dvi|'. \ (\TeX\
+uses the name ^|texput| for its output when you haven't specified any
+better name in your first line of input; and ^|dvi| stands for
+``^{device independent},'' since |texput.dvi| is capable of
+being printed on almost any kind of typographic output device.)
+
+Now you're going to need some help again from your friendly local
+computer hackers. They will tell you how to produce hardcopy from
+|texput.dvi|. And when you see the hardcopy---Oh, glorious day!---you
+will see a magnificent `Hello?'\ and the page number `1' at the bottom.
+Congratulations on your first masterpiece of fine printing.
+
+\smallbreak
+The point is, you understand now how to get something through the whole cycle.
+It only remains to do the same thing with a somewhat longer document.
+So our next experiment will be to work from a file instead of typing
+the input online.
+
+Use your favorite text editor to create a file called ^|story.tex|
+that contains the following 18 lines of text (no more, no less):
+$$\halign{\hbox to\parindent{\hfil\sevenrm#\ \ }&#\hfil\cr
+1&|\hrule|\cr\noalign{^^|\hrule|}
+2&|\vskip 1in|\cr\noalign{^^|\vskip|^^{leading, see vskip}}
+3&|\centerline{\bf A SHORT STORY}|\cr\noalign{^^|\centerline|}
+4&|\vskip 6pt|\cr
+5&|\centerline{\sl by A. U. Thor}|\cr\noalign{^^{Thor}}
+6&|\vskip .5cm|\cr
+7&|Once upon a time, in a distant|\cr
+8&| galaxy called \"O\"o\c c,|\cr\noalign{^^|\"|^^|\c|}
+9&|there lived a computer|\cr
+10&|named R.~J. Drofnats.|\cr\noalign{^^{Drofnats}}
+11&||\cr
+12&|Mr.~Drofnats---or ``R. J.,'' as|\cr
+13&|he preferred to be called---|\cr
+14&|was happiest when he was at work|\cr
+15&|typesetting beautiful documents.|\cr
+16&|\vskip 1in|\cr
+17&|\hrule|\cr
+18&|\vfill\eject|\cr\noalign{^^|\vfill|^^|\eject|}}$$
+\write16{\ifnum\pageno=\storypage
+ \else Redefine \string\storypage to be \the\pageno\fi}%
+(Don't type the numbers at the left of these lines, of course; they are present
+only for reference.) \ This example is a bit long, and more than a bit silly;
+but it's no trick for a good typist like you and it will give you some
+worthwhile experience, so do it. For your own good. And think about what
+you're typing, as you go; the example introduces a few important features
+of \TeX\ that you can learn as you're making the file.
+
+Here is a brief explanation of what you have just typed: Lines 1 and~17
+put a horizontal ^{rule} (a thin line) across the page. Lines 2 and~16
+skip past one inch of space; `|\vskip|' means ``vertical skip,'' and this
+extra space will separate the horizontal rules from the rest of the copy.
+Lines 3 and~5 produce the title and the author name, centered, in boldface
+and in slanted type. Lines 4 and~6 put extra white space between those
+lines and their successors. \ (We shall discuss units of measure like
+`|6pt|' and `|.5cm|' in Chapter~10.)
+
+The main bulk of the story appears on lines 7--15, and it consists of
+two ^{paragraphs}. The fact that line~11 is blank informs \TeX\ that
+^^{blank line} ^^{empty line}
+line~10 is the end of the first paragraph; and the `|\vskip|' on line~16
+implies that the second paragraph ends on line~15, because vertical
+skips don't appear in paragraphs. Incidentally, this example seems
+to be quite full of \TeX\ commands; but it is atypical in that respect,
+because it is so short and because it is supposed to be teaching things.
+Messy constructions like |\vskip| and |\centerline| can be expected at the
+very beginning of a manuscript, unless you're using a canned format, but
+they don't last long; most of the time you will find yourself typing
+straight text, with relatively few control sequences.
+
+And now comes the good news, if you haven't used computer typesetting
+before: You don't have to worry about where to break lines in a paragraph
+(i.e., where to stop at the right margin and to begin a new line), because
+\TeX\ will do that for you. Your manuscript file can contain long lines or
+short lines, or both; it doesn't matter. This is especially helpful when
+you make changes, since you don't have to retype anything except the words
+that changed. {\sl Every time you begin a new line in your manuscript file
+it is essentially the same as typing a space.} When \TeX\ has read an
+entire paragraph---in this case lines 7 to~11---it will try to break up
+the text so that each line of output, except the last, contains about the
+same amount of copy; and it will hyphenate words if necessary to keep the
+spacing consistent, but only as a last resort.
+
+Line 8 contains the strange concoction
+\begintt
+\"O\"o\c c
+\endtt
+and you already know that |\"| stands for an ^{umlaut} accent. The
+|\c| stands for a ``^{cedilla},'' so you will get `\"O\"o\c c' as the
+name of that distant galaxy.
+
+The remaining text is simply a review of the conventions that we discussed
+long ago for dashes and quotation marks, except that the `|~|' signs in
+lines 10 and~12 are a new wrinkle. These are called {\sl ^{ties}}, because
+they tie words together; i.e., \TeX\ is supposed to treat `|~|' as a
+normal space but not to break between lines there.
+A good typist will use ties within names, as shown in our
+example; further discussion of ties appears in Chapter~14. ^^{tilde}
+
+Finally, line~18 tells \TeX\ to `^|\vfill|', i.e., to fill the rest of
+the page with white space; and to `^|\eject|' the page, i.e., to send it
+to the output file.
+
+\smallskip Now you're ready for Experiment~2: Get \TeX\ going again.
+This time when the machine says `|**|' you should answer `|story|', since
+that is the name of the file where your input resides. \ (The file
+could also be called by its full name `|story.tex|', but \TeX\ automatically
+supplies the suffix `|.tex|' if no suffix has been specified.)
+^^{file names}
+
+You might wonder why the first prompt was `^|**|', while the subsequent
+ones are `^|*|'; the reason is simply that the first thing you type to
+\TeX\ is slightly different from the rest: If the first character of your
+response to `|**|' is not a backslash, \TeX\ automatically inserts
+`^|\input|'. Thus you can usually run \TeX\ by merely naming your input
+file. \ (Previous \TeX\ systems required you to start by typing `|\input
+story|' instead of `|story|', and you can still do that; but most \TeX\
+users prefer to put all of their commands into a file instead of typing
+them online, so \TeX\ now spares them the nuisance of starting out with
+|\input| each time.) \ Recall that in Experiment~1 you typed `|\relax|';
+that started with a backslash, so |\input| was not implied.
+
+\danger There's actually another difference between `|**|' and `|*|': If the
+first character after |**| is an ^{ampersand} (\thinspace`|&|'\thinspace),
+\TeX\ will replace its memory with a precomputed ^{format file} before
+proceeding. Thus, for example, you can type `|&plain \input story|' or
+even `|&plain story|' in response to `|**|', if you are running some
+version of \TeX\ that might not have the plain format preloaded.
+^^{preloaded formats}
+
+\danger Incidentally, many systems allow you to invoke \TeX\ by typing a
+one-liner like `|tex story|' instead of waiting for the `|**|'; similarly,
+`|tex \relax|' works for Experiment~1, and `|tex &plain story|' loads the
+plain format before inputting the |story| file. You might want to try
+this, to see if it works on your computer, or you might ask somebody if
+there's a similar shortcut.
+
+As \TeX\ begins to read your story file, it types `|(story.tex|', possibly
+with a version number for more precise identification, depending on your
+local operating system. Then it types `|[1]|', meaning that page~1 is done;
+and `|)|', meaning that the file has been entirely input.
+
+\TeX\ will now prompt you with `|*|', because the file did not contain
+`^|\end|'. Enter |\end| into the computer now, and you should get a file
+|story.dvi| containing a typeset version of Thor's story. As in Experiment~1,
+you can proceed to convert |story.dvi| into hardcopy; go ahead and do that now.
+The typeset output won't be shown here, but you can see the results by
+doing the experiment personally. Please do so before reading on.
+
+\exercise Statistics show that only 7.43 of 10 people who read this manual
+actually type the |story.tex| file as recommended, but that those people
+learn \TeX\ best. So why don't you join them?
+\answer Laziness and/or obstinacy.
+
+\exercise Look closely at the output of Experiment~2, and compare it to
+|story.tex|\thinspace: If you followed the instructions carefully, you
+will notice a typographical error. What is it, and why did it sneak in?
+\answer There's an unwanted space after `called---', because (as the book
+says) \TeX\ treats the end of a line as if it were a blank space. That
+blank space is usually what you want, except when a line ends with a
+hyphen or a dash; so you should {\sc WATCH OUT} for lines that end with
+hyphens or dashes.
+
+With Experiment 2 under your belt, you know how to make a document from a
+file. The remaining experiments in this chapter are intended to help you
+cope with the inevitable anomalies that you will run into later; we will
+intentionally do things that will cause \TeX\ to ``squeak.''
+
+But before going on, it's best to fix the error revealed by the previous
+output (see exercise 6.2): Line~13 of the |story.tex| file should be changed to
+\begintt
+he preferred to be called---% error has been fixed!
+\endtt
+The `|%|' sign here ^^{percent} is a feature of plain \TeX\ that we haven't
+discussed before: It effectively terminates a line of your input file,
+without introducing the blank space that \TeX\ ordinarily inserts when
+moving to the next line of input. Furthermore, \TeX\ ignores everything
+that you type following a |%|, up to the end of that line in the file;
+you can therefore put ^{comments} into your manuscript, knowing that the
+comments are for your eyes only.
+
+Experiment 3 will be to make \TeX\ work harder, by asking it to set the
+story in narrower and narrower columns. Here's how: After starting the
+program, type
+\begintt
+\hsize=4in \input story
+\endtt
+in response to the `|**|'. This means, ``Set the story in a 4-inch column.''
+More precisely, ^|\hsize| is a primitive of \TeX\ that specifies the
+horizontal size, i.e., the width of each normal line in the output when a
+paragraph is being typeset; and ^|\input| is a primitive that causes \TeX\
+to read the specified file. Thus, you are instructing the machine to
+change the normal setting of\/ |\hsize| that was defined by plain \TeX, and
+then to process |story.tex| under this modification.
+
+\TeX\ should respond by typing something like `|(story.tex [1])|' as
+before, followed by `|*|'. Now you should type
+\begintt
+\hsize=3in \input story
+\endtt
+and, after \TeX\ says `|(story.tex [2])|' asking for more, type three more lines
+\begintt
+\hsize=2.5in \input story
+\hsize=2in \input story
+\end
+\endtt
+to complete this four-page experiment.
+
+Don't be alarmed when \TeX\ screams `|Overfull| |\hbox|' several times as
+it works at the 2-inch size; that's what was supposed to go wrong during
+Experiment~3. There simply is no good way to break the given
+paragraphs into lines that are exactly two inches wide, without making
+the spaces between words come out too large or too small. Plain \TeX\
+has been set up to ensure rather strict tolerances on all of the lines it
+produces:
+\begindisplay
+\hbox spread-1em{You don't get spaces between words narrower than this,\ and}\cr
+\hbox spread+1.679895em{you don't get spaces between words wider than this.}\cr
+\enddisplay
+If there's no way to meet these restrictions, you get an ^{overfull box}.
+And with the overfull box you also get (1)~a warning message, printed
+on your terminal, and (2)~a big black bar inserted at the right of the
+offending box, in your output. \ (Look at page~4 of the output from
+Experiment~3; the overfull boxes should stick out like sore thumbs.
+On the other hand, pages 1--3 should be perfect.)
+
+Of course you don't want overfull boxes in your output, so \TeX\ provides
+several ways to remove them; that will be the subject of our Experiment~4.
+But first let's look more closely at the results of Experiment~3, since
+\TeX\ reported some potentially valuable information when it was forced
+to make those boxes too full; you should learn how to read this data:
+\begintt
+Overfull \hbox (0.98807pt too wide) in paragraph at lines 7--11
+\tenrm tant galaxy called []O^^?o^^Xc, there lived||
+Overfull \hbox (0.4325pt too wide) in paragraph at lines 7--11
+\tenrm a com-puter named R. J. Drof-nats. ||
+Overfull \hbox (5.32132pt too wide) in paragraph at lines 12--16
+\tenrm he pre-ferred to be called---was hap-||
+\endtt
+Each overfull box is correlated with its location in your input file
+(e.g., the first two were generated when processing the paragraph on
+lines 7--11 of |story.tex|), and you also learn by how much the copy
+sticks out (e.g., 0.98807 points).
+
+Notice that \TeX\ also shows the contents of the overfull boxes in
+abbreviated form. For example, the last one has the words `he preferred
+to be called---was hap-', set in font |\tenrm| (10-point roman type);
+the first one has a somewhat curious rendering of `\"O\"o\c c', because the
+accents appear in strange places within that font. In general, when you
+see `^|[]|' in one of these messages, it stands either for
+the paragraph indentation or for some sort of complex construction;
+in this particular case it stands for an umlaut that has been raised
+up to cover an `O'.
+
+\dangerexercise Can you explain the `\|' that appears after
+`|lived|' in that message?
+\answer It represents the heavy bar that shows up in
+your output. \ (This bar wouldn't be present if\/ ^|\overfullrule| had been
+set to |0pt|, nor is it present in an underfull box.)
+
+\ddangerexercise Why is there a space before the `\|' in `|Drof-nats.
+|\|'\thinspace?
+\answer This is the ^|\parfillskip| space that ends the paragraph.
+In plain \TeX\ the parfillskip is zero when the last line of the paragraph
+is full; hence no space actually appears before the rule in the output
+of Experiment~3. But all hskips show up as spaces in an overfull box
+message, even if they're zero.
+
+You don't have to take out pencil and paper in order to write down the
+overfull box messages that you get before they disappear from view, since
+\TeX\ always writes a ``^{transcript}'' or ``^{log file}'' that records what
+happened during each session. For example, you should now have a file
+called |story.log| containing the transcript of Experiment~3, as well
+as a file called |texput.log| containing the transcript of Experiment~1. \
+(The transcript of Experiment~2 was probably overwritten when you did
+number~3.) \ Take a look at |story.log| now; you will see that the overfull
+box messages are accompanied not only by the abbreviated box contents,
+but also by some strange-looking data about hboxes and glue and kerns and
+such things. This data gives a precise description of what's in that
+overfull box; \TeX\ wizards will find such
+listings important, if they are called upon to diagnose some mysterious
+error, and you too may want to understand \TeX's internal code some day.
+
+The abbreviated forms of overfull boxes show the hyphenations that
+\TeX\ tried before it resorted to overfilling. The ^{hyphenation} algorithm,
+which is described in Appendix~H\null, is excellent but not perfect; for
+example, you can see from the messages in |story.log| that \TeX\ finds the
+hyphen in `pre-ferred', and it can even hyphenate `Drof-nats'. Yet it
+discovers no hyphen in `galaxy', and every once in
+a~while an overfull box problem can be cured simply by giving \TeX\ a hint
+about how to hyphenate some word more completely. \ (We will see later that
+there are two ways to do this, either by inserting ^{discretionary hyphens}
+each time as in `\hbox{|gal\-axy|}', or by saying
+`\hbox{|\hyphenation{gal-axy}|}' once at the beginning of your manuscript.)
+
+In the present example, hyphenation is not a problem, since \TeX\ found
+and tried all the hyphens that could possibly have helped. The only way to
+get rid of the overfull boxes is to change the tolerance, i.e., to allow
+wider spaces between words. Indeed, the tolerance that plain \TeX\ uses
+for wide lines is completely inappropriate for 2-inch columns; such narrow
+columns simply can't be achieved without loosening the constraints, unless
+you rewrite the copy to fit.
+
+\TeX\ assigns a numerical value called ``^{badness}'' to each line that
+it sets, in order to assess the quality of the spacing. The exact rules
+for badness are different for different fonts, and they will be discussed
+in Chapter~14; but here is the way badness works for the roman font
+of plain \TeX:
+\begindisplay \hbadness10000
+\hbox spread-.666667em{The badness of this line is 100.}&
+ \quad(very tight)\cr
+\hbox spread-.333333em{The badness of this line is 12.}&
+ \quad(somewhat tight)\cr
+\hbox{The badness of this line is 0.}&
+ \quad(perfect)\cr
+\hbox spread.5em{The badness of this line is 12.}&
+ \quad(somewhat loose)\cr
+%\hbox spread 1em{The badness of this line is 100.}&
+% \quad(loose)\cr % then "looser"
+\hbox spread 1.259921em{The badness of this line is 200.}&
+ \quad(loose)\cr
+%\hbox spread 1.713em{The badness of this line is 500.}&
+% \quad(bad)\cr % then "worse"
+\hbox spread 2.155em{The badness of this line is 1000.}&
+ \quad(bad)\cr
+\hbox spread 3.684em{The badness of this line is 5000.}& % actually 4995!
+ \quad(awful)\cr
+\enddisplay
+Plain \TeX\ normally stipulates that no line's badness should exceed 200;
+but in our case, the task would be impossible since
+\begindisplay \hbadness 10000
+`\hbox to 2in{tant galaxy called \"O\"o\c c, there}'\hskip 3em
+ has badness 1521;\cr
+`\hbox to 2in{he preferred to be called---was}'\hskip 3em
+ has badness 568.\cr
+\enddisplay
+So we turn now to Experiment~4, in which spacing variations that are
+more appropriate to narrow columns will be used.
+
+Run \TeX\ again, and begin this time by saying
+\begintt
+\hsize=2in \tolerance=1600 \input story
+\endtt
+so that lines with badness up to 1600 will be tolerated. Hurray! There are
+^^|\tolerance|
+no overfull boxes this time. \ (But you do get a message about an {\sl
+underfull\/} box, since \TeX\ reports all boxes whose badness exceeds
+a certain threshold called ^|\hbadness|; plain \TeX\ sets |\hbadness=1000|.) \
+^^{underfull box}
+Now make \TeX\ work still harder by trying
+\begintt
+\hsize=1.5in \input story
+\endtt
+(thus leaving the tolerance at 1600 but making the ^{column width} still
+^^{measure, see hsize}
+skimpier). Alas, overfull boxes return; so try typing
+\begintt
+\tolerance=10000 \input story
+\endtt
+in order to see what happens. \TeX\ treats 10000 as if it were ``infinite''
+tolerance, allowing arbitrarily wide space; thus, a tolerance of 10000 will
+{\sl never\/} produce an overfull box, unless something strange occurs like
+an unhyphenatable word that is wider than the column itself.
+
+The underfull box that \TeX\ produces in the 1.5-inch case is really bad;
+with such narrow limits, an occasional wide space is unavoidable. But try
+\begintt
+\raggedright \input story
+\endtt
+for a change. \ ^^|\raggedright|(This tells \TeX\ not to worry about keeping
+the right margin straight, and to keep the spacing uniform within each line.) \
+Finally, type
+\begintt
+\hsize=.75in \input story
+\endtt
+followed by `|\end|', to complete Experiment 4. This makes the columns
+almost impossibly narrow.
+
+\danger The output from this experiment will give you some feeling for the
+problem of ^{breaking a paragraph} into approximately equal lines. When the
+lines are relatively wide, \TeX\ will almost always find a good solution.
+But otherwise you will have to figure out some compromise, and several
+options are possible. Suppose you want to ensure that no lines have
+badness exceeding~500. Then you could set |\tolerance| to some high
+number, and |\hbadness=500|; \TeX\ would not produce overfull boxes, but
+it would warn you about the underfull ones. Or you could set
+|\tolerance=500|; then \TeX\ might produce overfull boxes. If you really
+want to take corrective action, the second alternative is better, because
+you can look at an overfull box to see how much sticks out; it becomes
+graphically clear what remedies are possible. On the other hand, if you
+don't have time to fix bad spacing---if you just want to know how bad it
+is---then the first alternative is better, although it may require more
+computer time.
+
+\dangerexercise When |\raggedright| has been specified, badness reflects
+the amount of space at the right margin, instead of the spacing between
+words. Devise an experiment by which you can easily determine what
+badness \TeX\ assigns to each line, when the |story| is set ragged-right
+in 1.5-inch columns.
+\answer Run \TeX\ with \hbox{|\hsize=1.5in|} \hbox{|\tolerance=10000|}
+\hbox{|\raggedright|} \hbox{|\hbadness=-1|} and then |\input story|. \TeX\ will
+report the badness of all lines (except the final lines of paragraphs, where
+fill glue makes the badness zero).
+
+\danger A parameter called ^|\hfuzz| allows you to ignore boxes that are only
+slightly overfull. For example, if you say |\hfuzz=1pt|, a box must stick
+out more than one point before it is considered erroneous. Plain \TeX\
+sets |\hfuzz=0.1pt|.
+
+\ddangerexercise Inspection of the output from Experiment~4, especially
+page~3, shows that with narrow columns it would be better to allow white
+space to appear before and after a dash, whenever other spaces in the
+same line are being stretched. Define a ^|\dash| macro that does this.
+\answer |\def\extraspace{\nobreak \hskip 0pt plus .15em\relax}|\parbreak
+|\def\dash{\unskip\extraspace---\extraspace}|\par\nobreak\smallskip\noindent
+(If you try this with the story at 2-inch and 1.5-inch sizes, you will
+notice a substantial improvement. The |\unskip| allows people to leave a
+space before typing |\dash|. \TeX\ will try to hyphenate before |\dash|,
+but not before `|---|'; cf.\ Appendix~H\null. The ^|\relax| at the end of
+|\extraspace| is a precaution in case the next word is `|minus|'.)
+
+You were warned that this is a long chapter. But take heart: There's only
+one more experiment to do, and then you will know enough about \TeX\ to
+run it fearlessly by yourself forever after. The only thing you are still
+missing is some information about how to cope with ^{error messages}---i.e.,
+not just with warnings about things like overfull boxes, but with cases
+where \TeX\ actually stops and asks you what to do next.
+
+Error messages can be terrifying when you aren't prepared for them;
+but they can be fun when you have the right attitude. Just remember that
+you really haven't hurt the computer's feelings, and that nobody will
+hold the errors against you. Then you'll find that running \TeX\ might
+actually be a creative experience instead of something to dread.
+
+The first step in Experiment 5 is to plant two intentional mistakes in the
+|story.tex| file. Change line~3 to
+\begintt
+\centerline{\bf A SHORT \ERROR STORY}
+\endtt
+and change `|\vskip|' to `|\vship|' on line~2.
+
+Now run \TeX\ again; but instead of `|story|' type `|sorry|'. The computer
+should respond by saying that it can't find file |sorry.tex|, and it will
+ask you to try again. Just hit \<return> this time; you'll see
+that you had better give the name of a real file. So type `|story|' and
+wait for \TeX\ to find one of the {\sl faux pas\/} in that file.
+
+Ah yes, the machine will soon stop,\footnote*{Some installations of \TeX\ do
+not allow interaction. In such cases all you can do is look at the error
+messages in your log file, where they will appear together with the ``help''
+information.} after typing something like this:
+\begintt
+! Undefined control sequence.
+l.2 \vship
+ 1in
+?
+\endtt
+\write16{\ifnum\pageno=\vshippage
+ \else Redefine \string\vshippage to be \the\pageno\fi}%
+\TeX\ begins its error messages with `|!|', and it shows what it was
+reading at the time of the error by displaying two lines of context. The
+top line of the pair (in this case `|\vship|'\thinspace) shows what \TeX\
+has looked at so far, and where it came from (`|l.2|', i.e., line number~2);
+the bottom line (in this case `|1in|'\thinspace) shows what \TeX\ has yet
+to read.
+
+The `^|?|'\ that appears after the context display means that \TeX\ wants
+advice about what to do next. If you've never seen an error message before,
+or if you've forgotten what sort of response is expected, you can type
+`|?|'\ now (go ahead and try it!); \TeX\ will respond as follows:
+\begintt
+Type <return> to proceed, S to scroll future error messages,
+R to run without stopping, Q to run quietly,
+I to insert something, E to edit your file,
+1 or ... or 9 to ignore the next 1 to 9 tokens of input,
+H for help, X to quit.
+\endtt
+This is your menu of options. You may choose to continue in various ways:
+
+\smallskip\item{1.}
+Simply type \<return>. \TeX\ will resume its processing, after
+attempting to recover from the error as best it can.
+
+\smallbreak\item{2.} Type `|S|'. \TeX\ will proceed without
+pausing for instructions if further errors arise. Subsequent error messages
+will flash by on your terminal, possibly faster than you can read them, and
+they will appear in your log file where you can scrutinize them at your
+leisure. Thus, `|S|' is sort of like typing \<return> to every message.
+
+\smallbreak\item{3.} Type `|R|'. This is like `|S|' but even stronger,
+since it tells \TeX\ not to stop for any reason, not even if a file name
+can't be found.
+
+\smallbreak\item{4.} Type `|Q|'. This is like `|R|' but even more so,
+since it tells \TeX\ not only to proceed without stopping but also to
+suppress all further output to your terminal. It is a fast, but somewhat
+reckless, way to proceed (intended for running \TeX\ with no operator in
+attendance).
+
+\smallbreak\item{5.} Type `|I|', followed by some text that you want to
+insert. \TeX\ will read this line of text before encountering what it
+would ordinarily see next. Lines inserted in this way are not assumed to
+end with a blank space. ^^{inserting text online}
+^^{online interaction, see interaction} ^^{interacting with TeX}
+
+\smallbreak\item{6.} Type a small number (less than 100). \TeX\ will
+delete this many characters and control sequences from whatever it is
+about to read next, and it will pause again to give you another chance to
+look things over. ^^{deleting tokens}
+
+\smallbreak\item{7.} Type `|H|'. This is what you should do now and whenever
+you are faced with an error message that you haven't seen for a~while. \TeX\
+has two messages built in for each perceived error: a formal one and an
+informal one. The formal message is printed first (e.g., `|! Undefined
+control sequence.|'\thinspace); the informal one is printed if you request
+more help by typing `|H|', and it also appears in your log file if you
+are scrolling error messages. The informal message tries to complement the
+formal one by explaining what \TeX\ thinks the trouble is, and often
+by suggesting a strategy for recouping your losses.^^{help messages}
+
+\smallbreak\item{8.} Type `|X|'. This stands for ``exit.'' It causes \TeX\
+to stop working on your job, after putting the finishing touches on your
+|log| file and on any pages that have already been output to your |dvi|
+file. The current (incomplete) page will not be output.
+
+\smallbreak\item{9.} Type `|E|'. This is like `|X|', but it also prepares
+the computer to edit the file that \TeX\ is currently reading, at the
+current position, so that you can conveniently make a change before
+trying again.
+
+\smallbreak\noindent
+After you type `|H|' (or `|h|', which also works), you'll get a message
+that tries to explain that the control sequence just read by \TeX\
+(i.e., |\vship|) has never been assigned a meaning, and that you should
+either insert the correct control sequence or you should go on as if the
+offending one had not appeared.
+
+In this case, therefore, your best bet is to type
+\begintt
+I\vskip
+\endtt
+(and \<return>), with no space after the `|I|'; this effectively replaces
+|\vship| by |\vskip|. \ (Do it.)
+
+If you had simply typed \<return> instead of
+inserting anything, \TeX\ would have gone ahead and read `|1in|', which
+it would have regarded as part of a paragraph to be typeset. Alternatively,
+you could have typed `|3|'\thinspace; that would have deleted
+`|1in|' from \TeX's input. Or you could have typed `|X|' or `|E|' in
+order to correct the spelling error in your file. But it's usually
+best to try to detect as many errors as you can, each time you run \TeX,
+since that increases your productivity while decreasing your computer bills.
+Chapter~27 explains more about the art of steering \TeX\ through
+troubled text.
+
+\dangerexercise What would have happened if you had typed `|5|' after
+the |\vship| error?
+\answer \TeX\ would have deleted five tokens: |1|, |i|, |n|, \],
+|\centerline|. (The space was at the end of line~2, the |\centerline| at the
+beginning of line~3.)
+
+\danger You can control the level of interaction by giving commands
+in your file as well as online: The \TeX\ primitives ^|\scrollmode|,
+^|\nonstopmode|, and ^|\batchmode| correspond respectively to typing
+`|S|', `|R|', or `|Q|' in response to an error message, and
+^|\errorstopmode| puts you back into the normal level of interaction. \
+(Such changes are global, whether or not they appear inside a group.) \
+Furthermore, many installations have implemented a way to ^{interrupt}
+\TeX\ while it is running; such an interruption causes the program to
+revert to |\errorstopmode|, after which it pauses and waits for
+further instructions.
+
+What happens next in Experiment 5? \TeX\ will hiccup on the other bug that
+we planted in the file. This time, however, the error message is more
+elaborate, since the context appears on six lines instead of two:
+\begintt
+! Undefined control sequence.
+<argument> \bf A SHORT \ERROR
+ STORY
+\centerline #1->\line {\hss #1
+ \hss }
+l.3 \centerline{\bf A SHORT \ERROR STORY}
+|null
+?
+\endtt
+You get multiline error messages like this when the error is detected
+while \TeX\ is processing some higher-level commands---in this case,
+while it is trying to carry out |\centerline|, which is not a primitive
+operation (it is defined in plain \TeX). At first, such error
+messages will appear to be complete nonsense to you, because much of what
+you see is low-level \TeX\ code that you never wrote. But you can overcome
+this hangup by getting a feeling for the way \TeX\ operates.
+
+First notice that the context information always appears in pairs of lines.
+As before, the top line shows what \TeX\ has just read (\thinspace
+`|\bf A SHORT \ERROR|'\thinspace), then comes what it is about to read
+(\thinspace`|STORY|'\thinspace). The next pair of lines shows the context
+of the first two; it indicates what \TeX\ was doing just before it began to
+read the others. In this case, we see that \TeX\ has just read `|#1|', which
+is a special code that tells the machine to ``read the first ^{argument} that
+is governed by the current control sequence''; i.e., ``now read the stuff that
+^|\centerline| is supposed to center on a line.'' The definition in Appendix~B
+says that |\centerline|, when applied to some text, is supposed to be carried
+out by sticking that text in place of the `|#1|' in `|\line{\hss#1\hss}|'.
+So \TeX\ is in the midst of this expansion of\/ |\centerline|, as well as being
+in the midst of the text that is to be centered.
+
+\looseness-1
+The bottom line shows how far \TeX\ has gotten until now in the |story| file.
+\ (Actually the bottom line is blank in this example; what appears to be
+the bottom line is really the first of two lines of context, and it
+indicates that \TeX\ has read everything including the `|}|' in line~3 of
+the file.) \ Thus, the context in this error message gives us a glimpse of
+how \TeX\ went about its business. First, it saw |\centerline| at the
+beginning of line~3. Then it looked at the definition of\/ |\centerline| and
+noticed that |\centerline| takes an ``argument,'' i.e., that |\centerline|
+applies to the next character or control sequence or group that follows.
+So \TeX\ read~on, and filed `|\bf A SHORT \ERROR STORY|' away as the
+argument to |\centerline|. Then it began to read the expansion, as
+defined in Appendix~B\null. When it reached the |#1|, it began to read
+the argument it had saved. And when it reached |\ERROR|, it complained
+about an undefined control sequence.
+
+\dangerexercise Why didn't \TeX\ complain about |\ERROR| being undefined
+when |\ERROR| was first encountered, i.e., before reading `|STORY}|' on line~3?
+\answer A control sequence like |\centerline| might well define a control
+sequence like |\ERROR| before telling \TeX\ to look at |#1|. Therefore
+\TeX\ doesn't interpret control sequences when it scans an argument.
+
+When you get a multiline error message like this, the best clues about the
+source of the trouble are usually on the bottom line (since that is what
+you typed) and on the top line (since that is what triggered the error
+message). Somewhere in there you can usually spot the problem.
+
+Where should you go from here? If you type `|H|' now, you'll just get
+the same help message about undefined control sequences that you saw
+before. If you respond by typing \<return>, \TeX\ will go
+on and finish the run, producing output virtually identical to that in
+Experiment~2. In other words, the conventional responses won't teach you
+anything new. So type `|E|' now; this terminates the run and prepares
+the way for you to fix the erroneous file. \ (On some systems, \TeX\ will
+actually start up the standard text editor, and you'll be positioned at
+the right place to delete `|\ERROR|'. On other systems, \TeX\ will simply
+tell you to edit line~3 of file |story.tex|.) ^^{editing}
+
+When you edit |story.tex| again, you'll notice that line~2 still contains
+|\vship|; the fact that you told \TeX\ to insert |\vskip| doesn't mean
+that your file has changed in any way. In general, you should correct all
+errors in the input file that were spotted by \TeX\ during a run; the
+log file provides a handy way to remember what those errors were.
+
+\smallskip
+Well, this has indeed been a long chapter, so let's summarize what has
+been accomplished. By doing the five experiments you have learned at first
+hand (1)~how to get a job printed via \TeX; (2)~how to make a file that
+contains a complete \TeX\ manuscript; (3)~how to change the plain \TeX\
+format to achieve columns with different widths; and (4)~how to avoid
+panic when \TeX\ issues stern~warnings.
+
+So you could now stop reading this book and go on to print a bunch of
+documents. It is better, however, to continue bearing with the author
+(after perhaps taking another rest), since you're just at the threshold
+of being able to do a lot more. And you ought to read Chapter~7
+at least, because it warns you about certain symbols that you must not
+type unless you want \TeX\ to do something special. While reading the
+remaining chapters it will, of course, be best for you to continue making
+trial runs, using experiments of your own design.
+
+\ddanger If you use \TeX\ format packages designed by others, your
+error messages may involve many inscrutable two-line levels of macro
+context. By setting ^|\errorcontextlines||=0| at the beginning of your file,
+you can reduce the amount of information that is reported;
+\TeX\ will show only the top and bottom pairs of context lines
+together with up to |\errorcontextlines| additional two-line items. \ (If
+anything has thereby been omitted, you'll also see `|...|'.) \ Chances
+are good that you can spot the source of an error even when most of a
+large context has been suppressed; if not, you can say
+`|I\errorcontextlines=100\oops|' and try again. \ (That will usually
+give you an undefined control sequence error and plenty of context.) \
+Plain \TeX\ sets |\errorcontextlines=5|.
+
+\endchapter
+
+What we have to learn to do we learn by doing.
+\author ^{ARISTOTLE}, {\sl Ethica Nicomachea\/} II (c.~325 B.C.)
+
+\bigskip
+
+He may run who reads.
+\author ^{HABAKKUK} 2\thinspace:\thinspace2 (c.~600 B.C.) % RSV
+\smallskip
+He that runs may read.
+\author WILLIAM ^{COWPER}, {\sl Tirocinium\/} (1785)
+
+\eject
+ \beginchapter Chapter 7. How \TeX\ Reads\\What You Type
+
+We observed in the previous chapter that an input manuscript is expressed
+in terms of ``lines,'' but that these lines of input are essentially
+independent of the lines of output that will appear on the finished pages.
+Thus you can stop typing a line of input at any place that's convenient for
+you, as you prepare or edit a file. A few other related rules have also
+been mentioned:
+
+\medskip
+\item\bull A $\langle\hbox{return}\rangle$ is like a space.
+
+\smallskip
+\item\bull Two spaces in a row count as one space.
+
+\smallskip
+\item\bull A blank line denotes the end of a paragraph.
+
+\medskip
+\noindent Strictly speaking, these rules are contradictory: A blank line
+is obtained by typing $\langle\hbox{return}\rangle$ twice in a row,
+and this is different from typing two spaces in a row. Some day you might want
+to know the {\sl real\/} rules. In this chapter and the next, we shall study
+the very first stage in the transition from input to output.
+
+\smallskip
+In the first place, it's wise to have a precise idea of what your keyboard
+sends to the machine. There are 256 characters that \TeX\ might encounter at
+each step, in a file or in a line of text typed directly on your terminal. These
+256~characters are classified into 16 categories numbered 0 to 15:
+\begindisplay \def\\{\hfill}
+\hfil\hidewidth\it Category\hidewidth&\it \qquad Meaning\hidewidth\cr
+\noalign{\smallskip}
+\\0&Escape character&(|\| in this manual)\cr
+\\1&Beginning of group&(|{| in this manual)\cr
+\\2&End of group&(|}| in this manual)\cr
+\\3&Math shift&(|$| in this manual)\cr
+\\4&Alignment tab&(|&| in this manual)\cr
+\\5&End of line&(\<return> in this manual)\cr
+\\6&Parameter&(|#| in this manual)\cr
+\\7&Superscript&(|^| in this manual)\cr
+\\8&Subscript&(|_| in this manual)\cr
+\\9&Ignored character&(\<null> in this manual)\cr
+10&Space&(\] in this manual)\cr
+11&Letter&(|A|, \dots, |Z| and |a|, \dots, |z|)\cr
+12&Other character&(none of the above or below)\cr
+13&Active character&(|~| in this manual)\cr
+14&Comment character&(|%| in this manual)\cr
+15&Invalid character&(\<delete> in this manual)\cr
+\enddisplay
+^^{escape character}
+^^{begin-group character}
+^^{end-group character}
+^^{math mode character}
+^^{alignment tab}
+^^{parameter}
+^^{superscript}
+^^{subscript}
+^^{ignored character}
+^^{space}
+^^{letter}
+^^{other character}
+^^{active character}
+^^{comment character}
+^^{invalid character}
+^^{category codes, table}
+It's not necessary for you to learn these code numbers; the point is only that
+\TeX\ responds to 16~different types of characters. At first this manual led
+you to believe that there were just two types---the escape character and the
+others---and then you were told about two more types, the grouping
+symbols |{| and~|}|. In Chapter~6 you learned two more: |~| and~|%|.
+Now you know that there are really~16. This is the whole truth of the
+matter; no more types remain to be revealed. The category code for any
+character can be changed at any time, but it is usually wise to stick to a
+^^{reserved character} ^^{special character table} ^^\<null> ^^\<delete>
+particular scheme.
+
+The main thing to bear in mind is that each \TeX\ format reserves certain
+characters for its own special purposes. For example, when you are using plain
+\TeX\ format (Appendix~B\null), you need to know that the ten characters
+\begintt
+\ { } $ & # ^ _ % ~
+\endtt
+cannot be used in the ordinary way when you are typing;
+^^{special characters}
+^^{backslash}^^{left brace}^^{right brace}^^{dollar sign}^^{ampersand}
+^^{hash mark}^^{hat}^^{underline}^^{percent}^^{tilde}
+^^{single-character control sequences}
+each of them will cause \TeX\ to do something special, as explained elsewhere
+in this book. If you really need these symbols as part of your manuscript,
+plain \TeX\ makes it possible for you to type
+\begindisplay
+|\$| for \$,\qquad |\%| for \%,\qquad |\&| for \&,\qquad
+|\#| for \#,\qquad |\_| for \_\thinspace;
+\enddisplay
+the |\_| symbol is useful for {\it compound\_identifiers\/} in computer
+^^{identifiers} ^^{computer programs}
+programs. In mathematics formulas you can use |\{| and |\}| for $\{$ and
+$\}$, while ^|\backslash| produces a ^{reverse slash}; for example,
+\begindisplay
+`|$\{a \backslash b\}$|'\quad yields\quad`$\{a\backslash b\}$'.
+\enddisplay
+Furthermore |\^| produces a circumflex accent (e.g., `|\^e|' yields
+`\^e'\thinspace); and |\~| yields a tilde accent (e.g., `|\~n|' yields
+`\~n'\thinspace).
+
+\exercise What horrible errors appear in the following sentence?
+^^{Procter} ^^{Gamble}
+\begintt
+Procter & Gamble's stock climbed to $2, a 10% gain.
+\endtt
+\answer Three forbidden characters were used. One should type
+\begintt
+Procter \& Gamble's ... \$2, a 10\% gain.
+\endtt
+(Also the facts are wrong.)
+
+\exercise Can you imagine why the designer of plain \TeX\ decided not
+to make `|\\|' the control sequence for reverse slashes?^^{backslash}
+\answer Reverse slashes (backslashes) are fairly uncommon in formulas or
+text, and |\\| is very easy to type; it was therefore felt best not to
+reserve |\\| for such limited use. Typists can define |\\| to be whatever
+they want (including |\backslash|).
+
+\danger When \TeX\ reads a line of text from a file, or a line of text that
+you entered directly on your terminal, it converts that text into a list of
+``^{tokens}.'' A token is either (a)~a single character with an attached
+category code, or (b)~a control sequence. For example, if the normal
+conventions of plain \TeX\ are in force, the text `|{\hskip 36 pt}|' is
+converted into a list of eight tokens:
+\begindisplay
+|{|$_1$\quad\cstok{hskip}\quad|3|$_{12}$\quad|6|$_{12}$\quad
+ \]$_{10}$\quad|p|$_{11}$\quad|t|$_{11}$\quad|}|$_{2}$
+\enddisplay
+The subscripts here are the category codes, as listed earlier: 1 for
+``beginning of group,'' 12 for ``other character,'' and so on. The
+\cstok{hskip} doesn't get a subscript, because it represents a control
+sequence token instead of a character token. Notice that the space after
+|\hskip| does not get into the token list, because it follows a
+^{control word}.
+
+\danger It is important to understand the idea of token lists, if you want
+to gain a thorough understanding of \TeX, and it is convenient to learn
+the concept by thinking of \TeX\ as if it were a living organism. The
+individual lines of input in your files are seen only by \TeX's ``eyes''
+and ``mouth''; but after that text has been gobbled up, it is sent to
+\TeX's ``stomach'' in the form of a token list, and the digestive processes
+that do the actual typesetting are based entirely on tokens. As far as the
+stomach is concerned, the input flows in as a stream of tokens, somewhat
+as if your \TeX\ manuscript had been typed all on one extremely long line.
+
+\danger You should remember two chief things about \TeX's tokens: (1)~A
+control sequence is considered to be a single object that is no longer
+composed of a sequence of symbols. Therefore long control sequence names
+are no harder for \TeX\ to deal with than short ones, after they have been
+replaced by tokens. Furthermore, spaces are not ignored after control
+sequences inside a token list; the ignore-space rule applies only in an
+input file, during the time that strings of characters are being
+tokenized. (2)~Once a category code has been attached to a character
+token, the attachment is permanent. For example, if character `|{|' were
+suddenly declared to be of category~12 instead of category~1, the
+characters `|{|$_1$' already inside token lists of \TeX\ would still
+remain of category 1; only newly made lists would contain `|{|$_{12}$'
+tokens. In other words, individual characters receive a fixed
+interpretation as soon as they have been read from a file, based on the
+category they have at the time of reading. Control sequences are
+different, since they can change their interpretation at any time. \TeX's
+digestive processes always know exactly what a character token signifies,
+because the category code appears in the token itself; but when the
+digestive processes encounter a control sequence token, they must look up
+the current definition of that control sequence in order to figure out
+what it means.
+
+\ddangerexercise Some of the category codes 0 to 15 will never appear as
+subscripts in character tokens, because they disappear in \TeX's mouth.
+For example, characters of category 0 (escapes) never get to be tokens.
+Which categories can actually reach \TeX's stomach?
+\answer 1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 13. ^{Active characters} (type 13)
+are somewhat special; they behave like control sequences in most cases
+(e.g., when you say `^|\let||\x=~|' or `^|\ifx||\x~|'), but they behave like
+character tokens when they appear in the token list of\/ ^|\uppercase|
+or ^|\lowercase|, and when unexpanded after ^|\if| or ^|\ifcat|.
+
+\ddanger There's a program called ^|INITEX| that is used to install
+\TeX, starting from scratch; |INITEX| is like \TeX\ except that it can
+do even more things. It can compress ^{hyphenation} patterns into special
+tables that facilitate rapid hyphenation, and it can produce ^{format}
+files like `|plain.fmt|' from `|plain.tex|'. But |INITEX| needs extra
+space to carry out such tasks, so it generally has less memory available
+for typesetting than you would expect to find in a production version of \TeX.
+
+\ddanger When |INITEX| begins, it knows nothing
+but \TeX's primitives. All 256~characters are initially of category~12,
+except that ^\<return> has category~5,
+^\<space> has category~10, ^\<null> has category~9, ^\<delete> has category~15,
+the 52 letters |A|$\,\ldots\,$|Z| and |a|$\,\ldots\,$|z| have category~11,
+|%| and~|\| have the respective categories 14 and~0. ^^{backslash}^^{percent}
+It follows that |INITEX| is initially incapable of carrying out some of
+\TeX's primitives that depend on grouping; you can't use |\def| or |\hbox|
+until there are characters of categories 1 and~2. The format in
+Appendix~B begins with ^|\catcode| commands to provide characters of the
+necessary categories; e.g.,
+\begintt
+\catcode`\{=1
+\endtt
+assigns category 1 to the |{| symbol. The |\catcode| operation is like
+many other primitives of \TeX\ that we shall study later; by modifying
+internal quantities like the category codes, you can adapt \TeX\ to a wide
+variety of applications.
+
+\ddangerexercise Suppose that the commands
+\begintt
+\catcode`\<=1 \catcode`\>=2
+\endtt
+appear near the beginning of a group that begins with `|{|'; these
+specifications instruct \TeX\ to treat |<| and |>| as group delimiters.
+According to \TeX's rules of locality, the characters |<| and |>| will
+revert to their previous categories when the ^{group} ends. But should the
+group end with |}| or~with~|>|\thinspace?
+\answer It ends with either |>| or |}| or any character of category 2;
+then the effects of all |\catcode| definitions within the group are wiped
+out, except those that were ^|\global|. \TeX\ doesn't have any built-in
+knowledge about how to pair up particular kinds of grouping characters.
+New category codes take effect as soon as a |\catcode| assignment has been
+digested. For example,
+\begintt
+{\catcode`\>=2 >
+\endtt
+is a complete group. But without the space after `|2|' it would not be
+complete, since \TeX\ would have read the~`|>|' and converted it to a
+token before knowing what category code was being specified; \TeX\ always
+reads the token following a constant before evaluating that ^{constant}.
+
+\ddanger Although control sequences are treated as single objects,
+\TeX\ does provide a way to break them into lists of character tokens:
+If you write ^|\string||\cs|,
+where |\cs| is any control sequence, you get the list of characters for that
+control sequence's name. For example, |\string\TeX| produces four tokens:
+|\|$_{12}$, |T|$_{12}$, |e|$_{12}$, |X|$_{12}$. Each character in this token
+list automatically gets category code~12 (``other''),
+including the ^{backslash} that |\string| inserts to represent an escape
+character. However, category~10 will be assigned to the character `\]'
+(blank ^{space}) if a space character somehow sneaks into the name of a
+control sequence.
+
+\ddanger Conversely, you can go from a list of character tokens to a
+control sequence by saying `^|\csname|\<tokens>^|\endcsname|'. The tokens
+that appear in this construction between |\csname| and |\endcsname| may
+include other control sequences, as long as those control sequences
+ultimately expand into characters instead of \TeX\ primitives; the final
+characters can be of any category, not necessarily letters. For example,
+`|\csname TeX\endcsname|' is essentially the same as `|\TeX|'; but
+`|\csname\TeX\endcsname|' is illegal, because |\TeX| expands into tokens
+containing the ^|\kern| primitive. Furthermore,
+`|\csname\string\TeX\endcsname|' will produce the unusual control sequence
+`|\\TeX|', i.e., the token \cstok{\char`\\TeX}, which you can't ordinarily
+write.
+
+\ddangerexercise Experiment with \TeX\ to see what |\string| does when it
+is followed by an ^{active character} like |~|. \ (Active characters behave
+like control sequences, but they are not prefixed by an escape.) \ What
+is an easy way to conduct such experiments online? What control sequence
+could you put after |\string| to~obtain the single character
+token~|\|$_{12}$?
+\answer If you type `|\message{\string~}|' and `|\message{\string\~}|', \TeX\
+responds with `|~|' and `|\~|', respectively. ^^|\message|
+To get |\|$_{12}$ from |\string| you therefore need to make backslash an
+active character. One way to do this is
+\begintt
+{\catcode`/=0 \catcode`\\=13 /message{/string\}}
+\endtt
+(The ``^{null control sequence}'' that you get when there are no
+tokens between |\csname| and |\endcsname| is not a solution to this exercise,
+because |\string| converts it to `|\csname\endcsname|'. There is, however,
+another solution: If \TeX's ^|\escapechar| parameter---which will be
+explained in one of the next dangerous bends---is negative or greater
+than~255, then `|\string\\|' works.)
+
+\ddangerexercise What tokens does
+`|\expandafter\string\csname a\string\ b\endcsname|' produce?
+(There are three spaces before the |b|. Chapter~20 explains ^|\expandafter|.)
+\answer |\|$_{12}$ |a|$_{12}$ |\|$_{12}$ \]$_{10}$ |b|$_{12}$.
+
+\ddangerexercise When |\csname| is used to define a control sequence for
+the first time, that control sequence is made equivalent to |\relax|
+until it is redefined. Use this fact to design a macro |\ifundefined#1|
+such that, for example,
+\begindisplay
+|\ifundefined{TeX}|\<true text>|\else|\<false text>|\fi|
+\enddisplay
+expands to the \<true text> if\/ |\TeX| hasn't previously been defined,
+or if\/ |\TeX| has been |\let| equal to |\relax|; it should expand
+to the \<false text> otherwise. ^^|\ifundefined|
+\answer |\def\ifundefined#1{\expandafter\ifx\csname#1\endcsname\relax}|%
+\hfil\break Note that a control sequence like this must be used with care;
+it cannot be included in ^{conditional} text, because the |\ifx| will not
+be seen when |\ifundefined| isn't expanded.
+
+\ddanger In the examples so far, |\string| has converted control sequences
+into lists of tokens that begin with |\|$_{12}$. But this backslash token isn't
+really hardwired into \TeX; there's a parameter called ^|\escapechar| that
+specifies what character should be used when control sequences are output
+as text. The value of\/ |\escapechar| is normally \TeX's internal code for
+backslash, but it can be changed if another convention is desired.
+
+\ddanger \TeX\ has two other token-producing operations similar to the
+|\string| command. If you write ^|\number|\<number>, you get the decimal
+equivalent of the \<number>; and if you write ^|\romannumeral|\<number>,
+you get the number expressed in lowercase ^{roman numerals}. For example,
+`|\romannumeral24|' produces `|xxiv|', a list of four tokens each having
+category~12. The |\number| operation is redundant when it is applied
+to an explicit constant (e.g., `|\number24|' produces `|24|'); but it does
+suppress leading zeros, and it can also be used with numbers that are in
+\TeX's internal registers or parameters. For example, `|\number-0015|'
+produces `|-15|'; and if register |\count5| holds the value 316, then
+`|\number\count5|' produces `|316|'.
+
+\ddanger The twin operations ^|\uppercase||{|\<token list>|}| and
+^|\lowercase||{|\<token list>|}| go through a given token list and convert
+all of the character tokens to their ``uppercase'' or ``lowercase''
+equivalents. Here's how: Each of the 256 possible characters
+has two associated values called the ^|\uccode| and the ^|\lccode|; these values
+are changeable just as a |\catcode| is. Conversion to uppercase means
+that a character is replaced by its |\uccode| value, unless the |\uccode|
+value is zero (when no change is made). Conversion to lowercase is
+similar, using the |\lccode|. The category codes aren't changed. When
+^|INITEX| begins, all |\uccode| and |\lccode| values are zero except that
+the ^{letters} |a| to~|z| and |A| to~|Z| have |\uccode| values |A| to~|Z|
+and |\lccode| values |a| to~|z|.
+
+\ddanger \TeX\ performs the |\uppercase| and |\lowercase| transformations
+in its stomach, but the |\string| and |\number| and |\romannumeral|
+and |\csname| operations are carried out en route to the stomach (like
+macro expansion), as explained in Chapter~20.
+
+\ddangerexercise What token list results from
+`|\uppercase{a\lowercase{bC}}|'\thinspace?
+\answer First |\uppercase| produces `|A\lowercase{BC}|'; then you get `|Abc|'.
+
+\ddangerexercise \TeX\ has an internal integer parameter called ^|\year| that is
+set equal to the current year number at the beginning of every job. Explain how
+to use |\year|, together with |\romannumeral| and |\uppercase|, to
+print a copyright notice like \year=1986
+`\copyright\ \uppercase\expandafter{\romannumeral\year}'
+for all jobs run in \number\year.
+\answer `\thinspace|\copyright\ \uppercase\expandafter{\romannumeral\year}|%
+\thinspace'. \ (This is admittedly tricky; the `^|\expandafter|' expands
+the token after the `|{|', not the token after the group.)
+
+\ddangerexercise Define a control sequence |\appendroman| with three parameters
+such that |\appendroman#1#2#3| defines control sequence |#1| to
+expand to a control sequence whose name is the name of control sequence
+|#2| followed by the value of the positive integer |#3| expressed in roman
+numerals. For example, suppose |\count20| equals 30; then
+`|\appendroman\a\TeX{\count20}|' should have the same effect as
+`|\def\a{\TeXxxx}|'.^^{tricky macros}
+\answer (We assume that parameter |#2| is not simply an active character,
+and that ^|\escapechar| is between 0 and~255.)
+\begintt
+\def\gobble#1{} % remove one token
+\def\appendroman#1#2#3{\expandafter\def\expandafter#1\expandafter
+ {\csname\expandafter\gobble\string#2\romannumeral#3\endcsname}}
+\endtt
+
+\endchapter
+
+Some bookes are to bee tasted,
+others to bee swallowed,
+and some few to bee chewed and disgested.
+\author FRANCIS ^{BACON}, {\sl Essayes\/} (1597) % p2 of orig edition
+
+\bigskip
+
+`Tis the good reader that makes the good book.
+\author RALPH WALDO ^{EMERSON}, {\sl Society \& Solitude\/} (1870) % Success
+
+\eject
+ \beginchapter Chapter 8. The Characters\\You Type
+
+A lot of different keyboards are used with \TeX, but few keyboards can
+produce 256 different symbols. Furthermore, as we have seen, some of the
+characters that you {\sl can\/} type on your ^{keyboard} are reserved for
+^^{terminal keyboard}
+special purposes like escaping and grouping. Yet when we studied fonts it
+was pointed out that there are 256 characters per font. So how can you
+refer to the characters that aren't on your keyboard, or that have been
+pre-empted for formatting?
+
+One answer is to use control sequences. For example, the plain format
+of Appendix B\null, which defines |%| to be a special kind of symbol so that you
+can use it for comments, defines the control sequence |\%| to mean
+a ^{percent sign}.
+
+To get access to any character whatsoever, you can type
+\begindisplay
+|\char|\<number>
+\enddisplay
+where \<number> is any number from 0 to 255 (optionally followed by a space);
+you will get the corresponding character from the current font. That's how
+Appendix~B handles |\%|; it defines `|\%|' to be an abbreviation for
+`|\char37|', since 37 is the character code for a percent sign.
+
+The codes that \TeX\ uses internally to represent characters are based on
+``^{ASCII},'' the American Standard Code for Information Interchange.
+^^{internal character codes} ^^{character codes}
+Appendix~C gives full details of this code, which assigns numbers to
+certain control functions as well as to ordinary letters and punctuation
+marks. For example, ^\<space>${}=32$ and ^\<return>${}=13$.
+There are 94~standard visible symbols, and they have been assigned code
+numbers from 33 to~126, inclusive.
+
+It turns out that `|b|' is character number 98 in ASCII. So you can
+typeset the word |bubble| in a strange way by putting
+\begintt
+\char98 u\char98\char98 le
+\endtt
+into your manuscript, if the |b|-key on your keyboard is broken. \
+(An optional space is ignored after constants like `|98|'.
+Of course you need the |\|, |c|, |h|, |a|, and~|r| keys to type `^|\char|',
+so let's hope that they are always working.)
+
+\danger \TeX\ always uses the internal character code of Appendix~C
+for the standard ASCII characters,
+regardless of what external coding scheme actually appears in the files
+being read. Thus, |b| is 98 inside of \TeX\ even when your computer
+normally deals with ^{EBCDIC} or some other non-ASCII scheme; the \TeX\
+software has been set up to convert text files to internal code, and to
+convert back to the external code when writing text files.
+Device-independent (^|dvi|) output files use \TeX's internal code. In
+this way, \TeX\ is able to give identical results on all computers.
+
+\danger Character code tables like those in Appendix~C often give the code
+numbers in {\sl ^{octal notation}}, i.e., the radix-8 number system, in which
+the digits are {\it0},~{\it1}, {\it2}, {\it3}, {\it4}, {\it5}, {\it6},
+and~{\it7}.\footnote*{The author of this manual likes to use italic digits
+for octal numbers, and typewriter type for hexadecimal numbers, in order
+to provide a typographic clue to the underlying radix whenever possible.}
+Sometimes {\sl^{hexadecimal notation}\/} is also used, in which case the
+digits are |0|,~|1|, |2|, |3|, |4|, |5|, |6|, |7|, |8|, |9|, |A|, |B|, |C|,
+|D|, |E|, and~|F|. For example, the octal code for `|b|' is {\it142}, and
+its hexadecimal code is |62|. A ^\<number> in \TeX's language can begin
+with~a~|'|, in which case it is regarded as octal, or with a |"|, when it is
+regarded as hexadecimal. Thus, |\char'142| and |\char"62| are equivalent
+to |\char98|. The legitimate character codes in octal notation run from
+\oct0 to \oct{377}; in hexadecimal, they run from \hex0 to \hex{FF}.
+^^{apostrophe}^^{doublequote}
+
+\danger But \TeX\ actually provides another kind of \<number> that makes it
+unnecessary for you to know ASCII at all! The token |`|$_{12}$ (^{left quote}),
+when followed by any character token or by any control sequence token
+whose name is a single character, stands for \TeX's internal code for the
+character in question. For example, |\char`b| and |\char`\b| are also
+equivalent to |\char98|. ^^{reverse apostrophe}
+If you look in Appendix~B to see how |\%| is defined, you'll notice that
+the definition is
+\begintt
+\def\%{\char`\%}
+\endtt
+instead of\/ |\char37| as claimed above.
+
+\dangerexercise What would be wrong with |\def\%{\char`%}|?
+\answer The |%| would be treated as a comment character, because its
+category code is~14; thus, no |%| token or |}| token would get through
+to the gullet of \TeX\ where numbers are treated. When a character is
+of category 0, 5, 9, 14, or~15, the extra |\| must be used; and the
+|\| doesn't hurt, so you can always use it to be safe.
+
+\ddanger The preface to this manual points out that the author
+tells little white lies from time to time. Well, if you actually
+check Appendix~B you'll find that
+\begintt
+\chardef\%=`\%
+\endtt
+is the true definition of\/ |\%|. Since format designers often want to
+associate a special character with a special control sequence name, \TeX\
+provides the construction `^|\chardef|\<control sequence>|=|\<number>'
+for numbers between 0 and 255, as an efficient alternative to
+`^|\def|\<control sequence>|{\char|\<number>|}|'.
+
+Although you can use |\char| to access any character in the current
+font, you can't use it in the middle of a control sequence. For example,
+if you type
+\begintt
+\\char98
+\endtt
+\TeX\ reads this as the control sequence |\\| followed by |c|, |h|, |a|,
+etc., not as the control sequence |\b|.
+
+You will hardly ever need to use |\char| when typing a manuscript, since
+the characters you want will probably be available as predefined control
+sequences; |\char| is primarily intended for the designers of book formats
+like those in the appendices. But some day you may require a ^{special
+symbol}, and you may have to hunt through a font catalog until you find
+it. Once you find it, you can use it by simply selecting the appropriate
+font and then specifying the character number with |\char|. For example,
+the ``^{dangerous bend}'' sign used in this manual appears as character
+number~127 of font ^|manfnt|, and that font is selected by the control
+sequence ^|\manual|. The macros in Appendix~E therefore display dangerous
+bends by saying `|{\manual\char127}|'.
+
+We have observed that the ASCII character set includes only 94 printable
+symbols; but \TeX\ works internally with 256 different character codes,
+from 0 to 255, each of which is assigned to one of the sixteen categories
+described in Chapter~7. If your keyboard has additional symbols, or if it
+doesn't have the standard~94, the people who installed your local \TeX\ system
+can tell you the correspondence between what you type and the character
+number that \TeX\ receives. Some people are fortunate enough to have keys
+marked `{\tentex\char'32}' and `{\tentex\char'34}' and `{\tentex\char'35}';
+it is possible to install \TeX\ so that it will recognize these handy symbols
+and make the typing of mathematics more pleasant. But if you do not have
+such keys, you can get by with the control sequences ^|\ne|, ^|\le|,
+and ^|\ge|. ^^{not-equal}^^{less-or-equal}^^{greater-or-equal}
+
+\danger \TeX\ has a standard way to refer to the invisible characters of ASCII:
+Code~0 can be typed as the sequence of three characters |^^@|, code~1 can
+be typed |^^A|, and so on up to code~31, which is |^^_| (see Appendix~C\null).
+If the character following |^^| has an internal code between 64 and 127, \TeX\
+subtracts 64 from the code; if the code is between 0 and 63, \TeX\
+adds~64. Hence code 127 can be typed |^^?|, and
+the dangerous bend sign can be obtained by saying
+|{\manual^^?}|. However, you must change the category code of character
+127 before using it, since this character ordinarily has category~15
+(^{invalid}); say, e.g., |\catcode`\^^?=12|.
+^^{double hat} ^^{hat hat}
+The |^^| notation is different from |\char|, because |^^| combinations are
+like single characters; for example, it would not be permissible to say
+|\catcode`\char127|, but |^^| symbols can even be used as letters within
+control words.
+
+\danger One of the overfull box messages in Chapter 6 illustrates the fact
+that \TeX\ sometimes uses the funny |^^| convention in its output:
+The umlaut character in that example appears as |^^?|, and the cedilla appears
+as~|^^X|, because `\thinspace\"{}\thinspace' and `\char'30' occur in
+positions \oct{177} and~\oct{30} of the ^|\tenrm| font.
+
+\danger There's also a special convention in which |^^| is
+followed by {\sl two\/} ``lowercase hexadecimal digits,'' |0|--|9| or |a|--|f|.
+With this convention, all 256 characters are obtainable in a uniform
+way, from |^^00| to |^^ff|. Character 127 is |^^7f|.
+
+\danger Most of the |^^| codes are unimportant except in unusual applications.
+But |^^M| is particularly noteworthy because it is code 13, the ASCII
+^\<return> that \TeX\ normally places at the right end of every line of
+your input file. By changing the category of~|^^M| you can obtain useful
+special effects, as we shall see later. ^^{hat hat M}
+
+\danger The control code |^^I| is also of potential interest, since it's
+the ASCII ^\<tab>. Plain \TeX\ makes \<tab> act like a blank space.
+
+\ddanger People who install \TeX\ systems for use with non-American alphabets
+can make \TeX\ conform to any desired standard. For example, suppose
+you have a ^{Norwegian keyboard} containing the letter {\tt\ae}, which
+^^{Scandinavian letters} ^^{foreign languages}
+comes in as code~241 (say). Your local format package should define
+|\catcode`|{\tt\ae}|=11|; then you could have control sequences like
+|\s|{\tt\ae}|rtrykk|. Your \TeX\ input files could be made readable by
+American installations of \TeX\ that don't have your keyboard, by
+substituting |^^f1| for character~241. \ (For example, the stated control
+sequence would appear as |\s^^f1rtrykk| in the file; your American
+friends should also be provided with the format that you used, with its
+|\catcode`^^f1=11|.) \ Of course you should also arrange your fonts
+so that \TeX's character 241 will print as {\ae}; and you should
+change \TeX's hyphenation algorithm so that it will do correct
+Norwegian hyphenation. The main point is that such changes are not
+extremely difficult; nothing in the design of \TeX\ limits it to the
+American alphabet. Fine printing is obtained by fine tuning to the
+language or languages being used.
+^^{keyboards, non-ASCII}
+
+\ddanger European languages can also be accommodated effectively with
+only a limited character set.
+For example, let's consider Norwegian again, but suppose that you
+want to use a keyboard without an {\tt\ae} character. You can arrange the
+^{font metric file} so that \TeX\ will interpret |ae|, |o/|, |aa|, |AE|,
+|O/|, and |AA| as ligatures that produce \ae, \o, \aa, \AE, \O, and \AA,
+respectively; and you could put the characters \aa\ and \AA\ into positions
+128 and~129 of the font. By setting |\catcode`/=11| you would be able to
+use the ligature |o/| in control sequences like `|\ho/yre|'. \TeX's
+hyphenation method is not confused by ligatures; so you could use this
+scheme to operate essentially as suggested before, but with two keystrokes
+occasionally replacing one. \ (Your typists would have to watch out for
+the occasional times when the adjacent characters |aa|, |ae|, and |o/|
+should not be treated as ligatures; also, `|\/|' would be a ^{control
+word}, not a ^{control symbol}.)
+
+\ddanger The rest of this chapter is devoted to \TeX's reading rules,
+which define the conversion from text to tokens. For example, the fact
+that \TeX\ ignores spaces after control words is a consequence of
+the rules below, which imply among other things that spaces after control
+words never become space tokens. The rules are intended to work the
+way you would expect them to, so you may not wish to bother reading them;
+but when you are communicating with a computer, it is nice to understand
+what the machine thinks it is doing, and here's your chance.
+
+\ddanger The input to \TeX\ is a sequence of ``^{lines}.''
+Whenever \TeX\ is reading a line of text from a file, or a line of
+text that you entered directly on your terminal, the computer's
+reading apparatus is in one of three so-called ^{states}:
+\begindisplay
+\noalign{\vskip1pt}
+State $N$&Beginning a new line;\cr
+State $M$&Middle of a line;\cr
+State $S$&Skipping blanks.\cr
+\noalign{\vskip-3pt}
+\enddisplay
+At the beginning of every line it's in state $N$; but most of the time it's
+in state $M$, and after a control word or a space it's in state $S$.
+Incidentally, ``states'' are different from the ``^{modes}'' that we will
+be studying later; the current {\sl state\/} refers to \TeX's eyes and
+mouth as they take in characters of new text, but the current {\sl mode\/}
+refers to the condition of \TeX's gastro-intestinal tract. Most of the
+things that \TeX\ does when it converts characters to ^{tokens} are independent
+of the current state, but there are differences when spaces or end-of-line
+characters are detected (categories 10 and 5).
+
+\ddanger \TeX\ deletes any ^\<space> characters (number 32) that occur at the
+right end of an input line. Then it inserts a ^\<return> character (number~13)
+at the right end of the line, except that it places nothing additional at the
+end of a line that you inserted with `|I|'
+during ^{error recovery}. Note that \<return> is considered to be an actual
+character that is part of the line; you can obtain special effects by
+changing its catcode.
+
+\ddanger If \TeX\ sees an escape character (category 0) in any state, it
+scans the entire ^{control sequence} name as follows. (a)~If there are no
+more characters in the line, the name is empty (like |\csname\endcsname|).
+^^{null control sequence} ^^{csname endcsname}
+Otherwise (b)~if the next character is not of category~11 (letter), the
+name consists of that single symbol. Otherwise (c)~the name consists of all
+letters beginning with the current one and ending just before the first
+nonletter, or at the end of the line. This name becomes a control sequence
+token. \TeX\ goes into state~$S$ in case~(c), or in case~(b) with respect
+to a character of category~10 (space); otherwise \TeX\ goes into state~$M$.
+
+\ddanger If \TeX\ sees a superscript character (category 7) in any state,
+and if that character is followed by another identical character, and if
+those two equal characters are followed by a character of code
+$c<128$, then they
+are deleted and 64 is added~to or subtracted from the code~$c$.
+\ (Thus, |^^A| is
+replaced by a single character whose code is~1, etc., as explained earlier.) \
+However, if the two superscript characters are immediately followed by two
+of the lowercase hexadecimal digits |0123456789abcdef|, the
+four-character sequence is replaced by a single character having the
+specified hexadecimal code.
+The replacement is carried out also if such a trio or quartet of
+characters is encountered during steps (b) or~(c) of the control-sequence-name
+scanning procedure described above. After the replacement is made, \TeX\
+begins again as if the new character had been present all the time.
+If a superscript character is not the first of such a trio or quartet, it is
+handled by the following rule.
+
+\ddanger If \TeX\ sees a character of categories 1, 2, 3, 4, 6, 8, 11, 12,
+or~13,
+or a character of category~7 that is not the first of a special
+sequence as just
+described, it converts the character to a token by attaching the category
+code, and goes into state~$M$. This is the normal case; almost every
+nonblank character is handled by this rule.
+
+\ddanger If \TeX\ sees an end-of-line character (category 5), it throws
+away any other information that might remain on the current line. Then if
+\TeX\ is in state~$N$ (new line), the end-of-line character is converted
+to the control sequence token `\cstok{par}' ^^|\par| (end of paragraph); if
+\TeX\ is in state~$M$ (mid-line), the end-of-line character is converted
+to a token for character~32 (`\]') of category~10 (^{space}); and if \TeX\
+is in state~$S$ (skipping blanks), the end-of-line character is simply dropped.
+
+\ddanger If \TeX\ sees a character to be ignored (category~9), it simply
+bypasses that character as if it weren't there, and remains in the same state.
+
+\ddanger If \TeX\ sees a character of category~10 (space), the action
+depends on the current state. If \TeX\ is in state $N$ or $S$, the
+character is simply passed by, and \TeX\ remains in the same state.
+Otherwise \TeX\ is in state $M$; the character is converted to a token
+of category~10 whose character code is~32, and \TeX\ enters state~$S$.
+The character code in a space token is always~32.
+
+\ddanger If \TeX\ sees a comment character (category~14), it throws away that
+character and any other information that might remain on the current line.
+
+\ddanger Finally, if \TeX\ sees an invalid character (category~15),
+it bypasses that character, prints an error message, and remains in the
+same state.
+
+\ddanger If \TeX\ has nothing more to read on the current line, it goes to
+the next line and enters state $N$. However, if\/ ^|\endinput| has been
+specified for a file being ^|\input|, or if an |\input| file has ended,
+\TeX\ returns to whatever it was reading when the |\input| command
+was originally given. \ (Further details of\/ |\input| and |\endinput| are
+discussed in Chapter~20.)
+
+\ddangerexercise Test your understanding of \TeX's reading rules by answering
+the following quickie questions: (a)~What is the difference between
+categories 5 and~14? (b)~What is the difference between categories 3
+and~4? (c)~What is the difference between categories 11 and~12? (d)~Are
+spaces ignored after active characters? (e)~When a line ends with a comment
+character like |%|, are spaces ignored at the beginning of the next line?
+(f)~Can an ignored character appear in the midst of a control sequence name?
+\answer (a)~Both characters terminate the current line; but a character of
+category~5 might be converted into \]$_{10}$ or a \cstok{par} token, while
+a character of category~14 never produces a token. (b)~They produce
+character tokens stamped with different category numbers. For example,
+|$|$_3$ is not the same token as |$|$_4$, so \TeX's digestive processes
+will treat them differently. (c)~Same as~(b), plus the fact that control
+sequence names treat letters differently. (d)~No. (e)~Yes; characters of
+category~10 are ignored at the beginning of every line, since every line
+starts in state~$N$. (f)~No.
+
+\ddangerexercise Look again at the error message that appears on page
+\vshippage. When \TeX\ reported that |\vship| was an undefined
+control sequence, it printed two lines of context, showing that
+it was in the midst of reading line~2 of the |story| file. At the
+time of that error message, what state was \TeX\ in? What character
+was it about to read next?
+\answer \TeX\ had just read the control sequence |\vship|, so it
+was in state~$S$, and it was just ready to read the space before `|1in|'.
+Afterwards it ignored that space, since it was in state~$S$; but if
+you had typed |I\obeyspaces| in response to that error message,
+you would have seen the space. Incidentally, when \TeX\ prints
+the ^{context of an error message}, the bottom pair of lines comes from
+a text file, but the other pairs of lines are portions of token lists
+that \TeX\ is reading (unless they begin with `|<*>|', when they
+represent text inserted during ^{error recovery}).
+
+\ddangerexercise Given the category codes of plain \TeX\ format,
+what tokens are produced from the input line
+`| $x^2$~ \TeX ^^62^^6|'\thinspace?
+\answer |$|$_{3}$ |x|$_{11}$ |^|$_7$ |2|$_{12}$ |$|$_{3}$ |~|$_{13}$ \]$_{10}$
+\cstok{TeX} |b|$_{11}$ |v|$_{11}$ \]$_{10}$. The final space comes from the
+\<return> placed at the end of the line. Code |^^6| yields |v| only
+when not followed by |0|--|9| or |a|--|f|.
+The initial space is ignored, because state~$N$
+governs the beginning of the line.
+
+\ddangerexercise Consider an input file that contains exactly
+three lines; the first line says `|Hi!|', while the other two lines
+are completely blank. What tokens are produced when \TeX\ reads
+this file, using the category codes of plain \TeX\ format?
+\answer |H|$_{11}$ |i|$_{11}$ |!|$_{12}$ \]$_{10}$ \cstok{par}
+\cstok{par}. The `\]' comes from the \<return> at the
+end of the first line; the second and third lines each contribute
+a \cstok{par}.
+
+\ddangerexercise Assume that the category codes of plain \TeX\ are in
+force, except that the characters |^^A|, |^^B|, |^^C|, |^^M| belong
+respectively to categories 0, 7, 10, and 11. What tokens are produced from
+the (rather ridiculous) input line `|^^B^^BM^^A^^B^^C^^M^^@\M|\]'?
+(Remember that this line is followed by \<return>, which is
+|^^M|; and recall that |^^@| denotes the ^\<null> character, which has
+category~9 when |INITEX| begins.)
+\answer The two |^^B|'s are not recognized as consecutive superscript
+characters, since the first |^^B| is converted to code~2 which doesn't
+equal the following character |^|. Hence
+the result is seven tokens: |^^B|$_7$ |^^B|$_7$
+|M|$_{11}$ \cstok{\^{}\^{}B} \]$_{10}$ |^^M|$_{11}$ \cstok{M\^{}\^{}M}.
+The last of these is a control word whose name has two letters.
+The \<space> after |\M| is deleted before \TeX\ inserts the \<return> token.
+
+\ddanger The special character inserted at the end of each line needn't
+be ^\<return>; \TeX\ actually inserts the current value of an integer
+parameter called ^|\endlinechar|, which normally equals~13 but it can
+be changed like any other parameter. If the value of\/ |\endlinechar| is
+negative or greater than~255, no character is appended, and the effect is
+as if every line ends with~|%| (i.e., with a comment character).
+
+\ddanger Since it is possible to change the category codes, \TeX\ might
+actually use several different categories for the same character on a single
+line. For example, Appendices D and~E contain several ways to coerce \TeX\ to
+process text ``^{verbatim},'' so that the author could prepare this manual
+without great difficulty. \ (Try to imagine typesetting a \TeX\ manual;
+backslashes and other special characters need to switch back and forth
+between their normal categories and category~12!) \ Some care is needed to
+get the timing right, but you can make \TeX\ behave in a variety of
+different ways by judiciously changing the categories. On the other hand,
+it is best not to play with the category codes very often, because you must
+remember that characters never change their categories once they have become
+tokens. For example, when the arguments to a macro are first scanned,
+they are placed into a token list, so their categories are fixed once and
+for all at that time. The author has intentionally kept the category
+codes numeric instead of mnemonic, in order to discourage people from
+making extensive use of\/ |\catcode| changes except in unusual
+circumstances.
+
+\ddangerexercise Appendix B defines ^|\lq| and ^|\rq| to be abbreviations
+for |`| and |'| (single left and right quotes, respectively). Explain why
+the definitions
+\begintt
+\chardef\lq=96 \chardef\rq=39
+\endtt
+would not be as good.
+\answer Both alternatives work fine in text; in particular, they combine
+as in |\lq\lq| to form ligatures. But the definition in Appendix~B works
+also in connection with constants; e.g., |\char\lq\%| and
+|\char\rq140| are valid. \ (Incidentally, the construction |\let\lq=`|
+would not work with constants, since the quotes in a ^\<number> must
+come from character tokens of category~12; after |\let\lq=`| the control
+sequence token |\lq| will not expand into a character token, nor {\sl is\/}
+it a character token!) ^^|\let| ^^{implicit character}
+
+\endchapter
+
+for life's not a paragraph
+\quad
+ % he left a blank line here, really
+And death i think is no parenthesis.
+\author e.~e.~^{cummings}, {\sl since feeling is first\/} (1926)
+
+\bigskip
+
+This coded character set is to facilitate
+the general interchange of information
+among information processing systems,
+communication systems, and
+associated equipment.
+$\ldots$ An 8-bit set was considered
+but the need for more than 128 codes
+in general applications was not yet evident.
+\author ASA SUBCOMMITTEE X3.2, {\sl American Standard\break %
+ Code for Information Interchange\/^^{ASCII}} (1963)
+% in {\sl Communications of the ACM\/}
+
+\eject
+ \beginchapter Chapter 9. \TeX's\\Roman Fonts
+
+When you're typing a manuscript for \TeX, you need to know what symbols
+are available. The plain \TeX\ format of Appendix~B is based on the
+Computer Modern fonts, which provide the characters needed to typeset a
+wide variety of documents. It's time now to discuss what a person can do
+with plain \TeX\ when typing straight text. We've already touched on some of
+the slightly subtle things---for example, dashes and quotation marks
+were considered in Chapter~2, and certain kinds of accents appeared in the
+examples of Chapters 3 and~6. The purpose of this chapter is to give a
+more systematic summary of the possibilities, by putting all the facts
+together.
+
+Let's begin with the rules for the normal roman font (|\rm| or |\tenrm|);
+plain \TeX\ will use this font for everything unless you specify
+otherwise. Most of the ordinary symbols that you need are readily available
+and you can type them in the ordinary way: There's nothing special about
+\begindisplay \openup1pt
+the ^{letters} |A| to |Z| and |a| to |z|\cr
+the ^{digits} |0| to |9|\cr
+common ^{punctuation} marks |: ; ! ? ( ) [ ] ` ' - * / . , @|\cr
+\enddisplay
+except that \TeX\ recognizes certain combinations as ^{ligatures}:
+$$\openup1pt\halign{\indent#\hfil\cr
+|ff| yields ff\thinspace;$\!$\quad |fi| yields fi\thinspace;$\!$\quad
+|fl| yields fl\thinspace;$\!$\quad
+|ffi| yields ffi\thinspace;$\!$\quad |ffl| yields ffl\thinspace;\cr
+|``| yields``\thinspace;\qquad |''| yields ''\thinspace;\qquad
+|!||`| yields !`\thinspace;\qquad |?||`| yields ?`\thinspace;\cr
+|--| yields --\thinspace;\qquad |---| yields ---\thinspace.\cr}$$
+^^{Spanish ligatures}
+You can also type |+| and |=|, to get the corresponding
+symbols + and~=; but it's much better to use such characters
+only in math mode, i.e., enclosed between two |$| signs, since that tells
+\TeX\ to insert the proper spacing for mathematics. Math mode is
+explained later; for now, it's just a good idea to remember that formulas
+and text should be segregated. A non-mathematical hyphen and a non-mathematical
+slash should be specified by typing `|-|' and `|/|' outside of mathematics
+mode, but subtraction and division should be specified by typing `|-|' and
+`|/|' between |$|~signs.
+^^{Colon}
+^^{Semicolon}
+^^{Exclamation point}^^{Shriek, see exclamation point}
+^^{Question mark}
+^^{Parentheses}
+^^{Brackets}
+^^{Apostrophe} ^^{Reverse apostrophe}
+^^{Hamza, see apostrophe} ^^{Ain, see reverse apostrophe}
+^^{Hyphen} ^^{Dash}
+^^{Asterisk}
+^^{At sign}
+^^{Virgule, see slash}
+^^{Solidus, see slash}
+^^{Shilling sign, see slash}
+^^{Slash}
+^^{Period} ^^{Full stop, see period}
+^^{Comma}
+^^{Plus sign}
+^^{Equals sign}
+
+The previous paragraph covers 80 of the 94 visible characters of standard
+ASCII; so your keyboard probably contains at least 14 more symbols, and
+you should learn to watch out for the remaining ones, since they are special.
+Four of these are pre\"empted by plain \TeX; if your manuscript requires
+the symbols
+\begintt
+$ # % &
+\endtt
+^^{dollar sign} ^^{sharp sign, see hash mark} ^^{number sign, see hash mark}
+^^{hash mark} ^^{percent sign} ^^{ampersand}
+you should remember to type them as
+\begintt
+\$ \# \% \&
+\endtt
+respectively. Plain \TeX\ also reserves the six symbols
+\begintt
+\ { } ^ _ ~
+\endtt
+^^{backslash} ^^{braces} ^^{curly braces, see braces} ^^{hat, see circumflex}
+^^{circumflex} ^^{underline} ^^{tilde}
+but you probably don't mind losing these, since they don't appear in
+normal copy. Braces and backslashes are available via control sequences
+in math mode.
+
+\goodbreak
+There are four remaining special characters in the standard ASCII set:
+\begintt
+" || < >
+\endtt
+Again, you don't really want them when you're typesetting text. \ (Double-quote
+marks should be replaced either by |``| or by |''|; vertical
+lines and relation signs are needed only in math mode.)
+^^{double-quote mark} ^^{vertical line, see norm} ^^{norm symbol}
+^^{less than sign} ^^{greater than sign}
+
+Scholarly publications in English often refer to other languages, so
+plain \TeX\ makes it possible to typeset the most commonly used ^{accents}:
+$$\halign{\indent\hbox to 50pt{#\hfil}&\hbox to 35pt{#\hfil}&#\hfil\cr
+\it\negthinspace Type&\it to get\cr
+\noalign{\smallskip}
+|\`o|&\`o&(grave accent)\cr
+|\'o|&\'o&(acute accent)\cr
+|\^o|&\^o&(circumflex or ``hat'')\cr
+|\"o|&\"o&(umlaut or dieresis)\cr
+|\~o|&\~o&(tilde or ``squiggle'')\cr
+|\=o|&\=o&(macron or ``bar'')\cr
+|\.o|&\.o&(dot accent)\cr
+|\u o|&\u o&(breve accent)\cr
+|\v o|&\v o&(h\'a\v cek or ``check'')\cr
+|\H o|&\H o&(long Hungarian umlaut)\cr
+|\t oo|&\t oo&(tie-after accent)\cr}$$
+^^|\`| ^^{grave accent}
+^^|\'| ^^{acute accent}
+^^{esc hat} ^^{circumflex accent} ^^{hat accent}
+^^|\"| ^^{umlaut accent} ^^{dieresis}
+^^{esc tilde} ^^{tilde accent} ^^{squiggle accent}
+^^|\=| ^^{macron accent} ^^{bar accent}
+^^|\.| ^^{dot accent}
+^^|\v| ^^{h\'a\v cek accent} ^^{check accent}
+^^|\u| ^^{breve accent}
+^^|\H| ^^{Hungarian umlaut}
+^^|\t| ^^{tie-after accent}
+^^{embellished letters, see accents}
+Within the font, such accents are designed to appear at the right height
+for the letter `o'; but you can use them over any letter, and \TeX\ will
+raise an accent that is supposed to be taller. Notice that spaces are needed
+in the last four cases, to separate the control sequences from the letters
+that follow. You could, however, type `|\H{o}|' in order to avoid putting a
+space in the midst of a word.
+
+\medbreak
+Plain \TeX\ also provides three accents that go underneath:
+$$\halign{\indent\hbox to 50pt{#\hfil}&\hbox to 35pt{#\hfil}&#\hfil\cr
+\it\negthinspace Type&\it to get\cr
+\noalign{\smallskip}
+|\c o|&\c o&(cedilla accent)\cr
+|\d o|&\d o&(dot-under accent)\cr
+|\b o|&\b o&(bar-under accent)\cr}$$
+^^|\c| ^^{cedilla accent}
+^^|\d| ^^{dot-under accent} ^^{emphatics, see dot-under}
+^^|\b| ^^{bar-under accent}
+And there are a few special letters:
+$$\halign{\indent\hbox to 50pt{#\hfil}&\hbox to 35pt{#\hfil}&#\hfil\cr
+\it\negthinspace Type&\it to get\cr
+\noalign{\smallskip}
+|\oe,\OE|&\oe,\thinspace\OE&(French ligature OE)\cr
+|\ae,\AE|&\ae,\thinspace\AE&(Latin ligature and Scandinavian letter AE)\cr
+|\aa,\AA|&\aa,\thinspace\AA&(Scandinavian A-with-circle)\cr
+|\o,\O|&\o,\thinspace\O&(Scandinavian O-with-slash)\cr
+|\l,\L|&\l,\thinspace\L&(Polish suppressed-L)\cr
+|\ss|&\ss&(German ``es-zet'' or sharp S)\cr}$$
+^^{Scandinavian letters} ^^{sharp S} ^^{es-zet} ^^{German} ^^{Polish}
+^^{Norwegian} ^^{Danish} ^^{Swedish} ^^{suppressed-L}
+^^{diphthongs, see \ae, \oe}
+The |\rm| font contains also the ^{dotless letters} `\i' and `\j',
+which you can obtain by typing `^|\i|' and `^|\j|'. These are needed because
+`i' and `j' should lose their dots when they gain an accent. For example,
+the right way to obtain `m\=\i n\u us' is to type \hbox{`|m\=\i n\u us|'}
+or `|m\={\i}n\u{u}s|'.
+
+This completes our summary of the |\rm| font. Exactly the same conventions
+apply to |\bf|, |\sl|, and |\it|, so you don't have to do things differently
+when you're using a different typeface. For example, |\bf\"o| yields
+{\bf\"o} and |\it\&| yields {\it\&}. Isn't that nice?
+
+\danger However, |\tt| is slightly different. You will be glad to know that
+|ff|, |fi|, and so on are not treated as ligatures when you're using
+^{typewriter type}; nor do you get ligatures from dashes and quote marks.
+That's fine, because ordinary dashes and ordinary double-quotes are
+appropriate when you're trying to imitate a typewriter. Most of the
+accents are available too. But |\H|, |\.|, |\l|, and |\L| cannot be
+used---the typewriter font contains other symbols in their place.
+Indeed, you are suddenly allowed to type |"|, \|, |<|, and |>|;
+^^{doublequote} ^^{vertical line} ^^{less than sign} ^^{greater than sign}
+see Appendix~F\null. All of the letters, spaces, and other symbols in
+|\tt| have the same width.
+
+\exercise What's the non-naive way to type `na\"\i ve'\thinspace?
+\answer |na\"\i ve| or |na{\"\i}ve| or |na\"{\i}ve|.
+
+\exercise List some English words that contain accented letters.
+\answer Belov\`ed prot\'eg\'e; r\^ole co\"ordinator; souffl\'es, cr\^epes,
+p\^at\'es, etc.
+
+\exercise How would you type `\AE sop's \OE uvres en fran\c cais'\thinspace?
+\answer |\AE sop's \OE uvres en fran\c cais|.
+
+\exercise Explain what to type in order to get this sentence:
+{\sl Commentarii Academi\ae\ scientiarum imperialis petropolitan\ae\/}
+became {\sl Akademi\t\i a Nauk SSSR, Doklady}.
+\answer |{\sl Commentarii Academi\ae\ scientiarum imperialis|\hfil\break
+|petropolitan\ae\/} became {\sl Akademi\t\i a Nauk SSSR, Doklady}.|
+
+\exercise And how would you specify the names
+Ernesto ^{Ces\`aro},
+P\'al ^{Erd\H os},
+\O ystein ^{Ore},
+Stanis\l aw \'Swierczkowski, ^^{Swiercz...}
+Serge\u\i\ \t Iur'ev, ^^{Iur'ev}
+Mu\d hammad ibn M\^us\^a ^{al-Khw\^arizm\^\i}?
+\answer |Ernesto Ces\`aro,
+P\'al Erd\H os,
+\O ystein Ore,
+Stanis\l aw \'Swier%|\break|czkowski,
+Serge\u\i\ \t Iur'ev,
+Mu\d hammad ibn M\^us\^a al-Khw\^arizm\^\i.|
+
+\dangerexercise Devise a way to typeset {\tt P\'al Erd{\bf\H{\tt o}}s}
+in typewriter type.
+\answer The proper umlaut is |\H|, which isn't available in |\tt|, so
+it's necessary to borrow the accent from another font. For example,
+\hbox{|{\tt P\'al Erd{\bf\H{\tt o}}s}|} uses a bold accent, which
+is suitably dark.
+
+The following symbols come out looking exactly the same whether you are using
+|\rm|, |\sl|, |\bf|, |\it|, or |\tt|:
+$$\halign{\indent#\hfil\ &\hfil#\hfil&#\hfil\cr
+\it\negthinspace Type&\it to get\cr
+\noalign{\smallskip}
+|\dag|&\dag&(dagger or obelisk)\cr
+|\ddag|&\ddag&(double dagger or diesis)\cr
+|\S|&\S&(section number sign)\cr
+|\P|&\P&(paragraph sign or pilcrow)\cr}$$
+^^{dagger} ^^{double dagger} ^^{obelisk} ^^{obelus, see obelisk} ^^{diesis}
+^^{section number sign} ^^{paragraph sign} ^^{pilcrow, see paragraph sign}
+(They appear in just one style because plain \TeX\ gets them from the
+math symbols font. Lots of other symbols are needed for mathematics;
+we shall study them later. See Appendix~B for a few more non-math symbols.)
+
+\exercise In plain \TeX's italic font, the `\$' sign comes out as
+`{\it\$}\thinspace'.
+^^{dollar sign} ^^{British pound sign} ^^{pound sterling} ^^{sterling}
+This gives you a way to refer to pounds sterling, but you might want an
+italic dollar sign. Can you think of a way to typeset a reference to
+the book {\it Europe on {\sl\$}15.00 a day}\thinspace?
+\answer |{\it Europe on {\sl\$}15.00 a day\/}|
+
+\ddanger Appendix B shows that plain \TeX\ handles most of the accents
+by using \TeX's ^|\accent| primitive. For example, |\'#1| is equivalent
+to |{\accent19 #1}|, where |#1| is the argument being accented.
+The general rule is that |\accent|\<number> puts
+an accent over the next character; the \<number> tells where that accent
+appears in the current font. The accent is assumed to be properly
+positioned for a character whose height equals the ^{x-height} of the
+current font; taller or shorter characters cause the accent to be raised
+or lowered, taking due account of the slantedness of the fonts of accenter
+and accentee. The width of the final construction is the width of the
+character being accented, regardless of the width of the accent.
+Mode-independent commands like font changes may appear between the accent
+number and the character to be accented, but grouping operations must not
+intervene. If it turns out that no suitable character is present, the
+accent will appear by itself as if you had said |\char|\<number> instead
+of\/ |\accent|\<number>. For example, |\'{}| produces \'{}.
+
+\ddangerexercise Why do you think plain \TeX\ defines |\'#1| to be
+`|{\accent19 #1}|' instead of simply letting |\'| be an abbreviation
+for `|\accent19 |'\thinspace? \ (Why the extra
+braces, and why the argument |#1|?)
+\answer The extra braces keep font changes local. An argument makes the
+use of\/ |\'| more consistent with the use of other accents like |\d|, which
+are manufactured from other characters without using the |\accent|
+primitive.
+
+\ddanger It's important to remember that these conventions we have discussed
+for accents and special letters are not built into \TeX\ itself; they belong
+only to the plain \TeX\ format, which uses the Computer Modern fonts. Quite
+different conventions will be appropriate when other fonts are involved;
+format designers should provide rules for how to obtain accents and
+special characters in their particular systems. Plain \TeX\ works well
+enough when accents are infrequent, but the conventions of this chapter
+are by no means recommended for large-scale applications of \TeX\ to
+other languages. For example, a well-designed \TeX\ font for ^{French}
+might well treat accents as ligatures, so that one could |e'crire
+de cette manie`re nai"ve en franc/ais| without backslashes. (See the
+remarks about Norwegian in Chapter~8.)
+^^{foreign languages}
+
+\endchapter
+
+Let's doo't after the high Roman fashion.
+\author WILLIAM ^{SHAKESPEARE}, {\sl The Tragedie of %
+ Anthony and Cleopatra\/} (1606) % Act IV, Scene 13, line 87
+
+\bigskip
+
+English is a straightforward, frank, honest, open-hearted, no-nonsense language,
+which has little truck with such devilish devious devices as accents;
+indeed U.S. editors and printers are often thrown into a dither
+when a foreign word insinuates itself into the language.
+However there is one word on which Americans seem to have closed ranks,
+printing it confidently, courageously, and almost invariably
+complete with accent---the cheese presented to us as M\"unster.
+\smallskip
+Unfortunately, ^{Munster} doesn't take an accent.
+\author WAVERLEY ^{ROOT}, in the {\sl International Herald Tribune\/} (1982)
+ % Tuesday 18 May 82 page 8
+
+\eject
+ \beginchapter Chapter 10. Dimensions
+
+Sometimes you want to tell \TeX\ how big to make a space, or how wide to
+make a line. For example, the short story of Chapter~6 used the instruction
+`|\vskip .5cm|' to skip vertically by half a centimeter, and we also
+said `|\hsize=4in|' to specify a horizontal size of 4~inches. It's time now
+to consider the various ways such ^{dimensions} can be communicated to \TeX.
+
+``^{Points}'' and ``^{picas}'' are the traditional units of measure for
+printers and compositors in English-speaking countries, so \TeX\
+understands points and picas. \TeX\ also understands inches and metric
+units, as well as the continental European versions of points and picas.
+Each unit of measure is given a two-letter abbreviation, as follows:
+^^{units of measure, table}
+$$\halign{\indent\tt#&\quad#\hfil\cr
+pt&point (baselines in this manual are $12\pt$ apart)\cr
+pc&pica ($\rm1\,pc=12\,pt$)\cr
+in&inch ($\rm1\,in=72.27\,pt$)\cr
+bp&big point ($\rm72\,bp=1\,in$)\cr
+cm&centimeter ($\rm2.54\,cm=1\,in$)\cr
+mm&millimeter ($\rm10\,mm=1\,cm$)\cr
+dd&didot point ($\rm1157\,dd=1238\,pt$)\cr
+cc&cicero ($\rm1\,cc=12\,dd$)\cr
+sp&scaled point ($\rm65536\,sp=1\,pt$)\cr}$$
+^^|pt|^^{point}
+^^|pc|^^{pica}
+^^|in|^^{inch}
+^^|bp|^^{big point}
+^^|cm|^^{centimeter}
+^^|mm|^^{millimeter}
+^^|dd|^^{didot point}^^{Didot, F. A.}
+^^|cc|^^{cicero}
+^^|sp|^^{scaled point}
+The output of \TeX\ is firmly grounded in the metric system, using the
+conversion factors shown here as exact ratios.
+
+\exercise How many points are there in 254 centimeters?
+\answer Exactly $7227\pt$.
+
+When you want to express some physical dimension to \TeX, type it as
+\begindisplay
+\<optional sign>\<number>\<unit of measure>\cr
+\noalign{\hbox{or}}
+\<optional sign>\<digit string>|.|\<digit string>\<unit of measure>\cr
+\enddisplay
+where an ^\<optional sign> is either a `|+|' or a `|-|' or nothing at all,
+and where a ^\<digit string> consists of zero or more consecutive
+decimal digits. The `|.|'\ can also be a `|,|'\null.
+For example, here are six typical dimensions:
+$$\halign{\indent#\hfil&\hskip 6em#\hfil\cr
+|3 in|&|29 pc|\cr
+|-.013837in|&|+ 42,1 dd|\cr
+|0.mm|&|123456789sp|\cr}$$
+A plus sign is redundant, but some people occasionally like extra
+redundancy once in a~while. Blank spaces are optional before the signs and the
+numbers and the units of measure, and you can also put an optional space
+after the dimension; but you should not put spaces within the digits
+of a number or between the letters of the unit of measure.
+
+\exercise Arrange those six ``typical dimensions'' into order,
+from smallest to largest.
+\answer $\rm-.013837\,in$, $\rm0.\,mm$, $\rm+42.1\,dd$, $\rm3\,in$,
+$\rm29\,pc$, $\rm123456789\,sp$.
+\ (The lines of text in this manual are 29~picas wide.)
+
+\dangerexercise Two of the following three dimensions are legitimate
+according to \TeX's rules. Which two are they? What do they mean?
+Why is the other one incorrect?
+\begintt
+'.77pt
+"Ccc
+-,sp
+\endtt
+\answer The first is not allowed, since octal notation cannot be used with
+a decimal point. The second is, however, legal, since a \<number> can be
+hexadecimal according to the rule mentioned in Chapter~8; it means
+$\rm12\,cc$, which is $\rm144\,dd\approx154.08124\,pt$. The third is also
+accepted, since a \<digit string> can be empty; it is a complicated
+way to say $\rm0\,sp$.
+
+\smallskip
+The following ``rulers'' have been typeset by \TeX\ so that you can get
+some idea of how different units compare to each other. If no distortion
+has been introduced during the camera work and printing processes that
+have taken place after \TeX\ did its work, these rulers are highly accurate.
+$$ \abovedisplayskip 15pt plus 4pt minus 4pt
+\belowdisplayskip 15pt plus 4pt minus 4pt
+\vbox{
+\def\1{\vrule height 0pt depth 2pt}
+\def\2{\vrule height 0pt depth 4pt}
+\def\3{\vrule height 0pt depth 6pt}
+\def\4{\vrule height 0pt depth 8pt}
+\def\ruler#1#2#3{\leftline{$\vcenter{\hrule\hbox{\4#1}}\,\,\rm#2\,{#3}$}}
+\def\\#1{\hbox to .125in{\hfil#1}}
+\def\8{\\\1\\\2\\\1\\\3\\\1\\\2\\\1\\\4}
+\ruler{\8\8\8\8}4{in}
+\vskip 18pt
+\def\\#1{\hbox to 10pt{\hfil#1}}
+\def\8{\\\1\\\1\\\1\\\1\\\2\\\1\\\1\\\1\\\1\\\4}
+\ruler{\8\8\8}{300}{pt}
+\vskip 18pt
+\def\\#1{\hbox to 10dd{\hfil#1}}
+\def\8{\\\1\\\1\\\1\\\1\\\2\\\1\\\1\\\1\\\1\\\4}
+\ruler{\8\8\8}{300}{dd}
+\vskip 18pt
+\def\\#1{\hbox to 5mm{\hfil#1}}
+\def\8{\\\2\\\4}
+\ruler{\8\8\8\8\8\8\8\8\8\8}{10}{cm}
+\vskip 6pt}$$
+
+\dangerexercise (To be worked after you know about boxes and glue and have
+read Chapter~21.) \ Explain how to typeset such a $\rm10\,cm$ ^{ruler},
+using \TeX.
+\answer {\obeylines|\def\tick#1{\vrule height 0pt depth #1pt}|
+|\def\\{\hbox to 1cm{\hfil\tick4\hfil\tick8}}|
+|\vbox{\hrule\hbox{\tick8\\\\\\\\\\\\\\\\\\\\}}|
+\noindent(You might also try putting ticks at every millimeter, in order %
+to see how good your system is; %
+some output devices can't handle 101~rules all at once.)}
+
+\danger \TeX\ represents all dimensions internally as an integer multiple
+of the tiny units called sp. Since the wavelength of visible light is
+approximately $\rm100\,sp$, % in fact: violet=75sp, red=135sp!
+rounding errors of a few sp make no difference to the eye.
+However, \TeX\ does all of its arithmetic very carefully so that
+identical results will be obtained on different computers. Different
+implementations of \TeX\ will produce the same line breaks and the same
+page breaks when presented with the same document, because the integer
+arithmetic will be the same.
+^^{machine-independence} ^^{rounding}
+
+\danger The units have been defined here so that precise conversion to~sp
+is~efficient on a wide variety of machines. In order to achieve this,
+\TeX's ``pt'' has been made slightly larger than the official printer's
+point, which was defined to equal exactly $\rm.013837\,in$ by the American
+Typefounders Association in~1886 [cf.~National Bureau of Standards
+Circular~570 (1956)]. In fact, one classical point is exactly
+$.99999999\pt$, so the ``error'' is essentially one part in $10^8$.
+This is more than two orders of magnitude less than the amount by which
+the inch itself changed during 1959, when it shrank to $\rm2.54\,cm$ from
+its former value of $\rm(1/0.3937)\,cm$; so there is no point in worrying
+about the difference. The new definition $\rm72.27\,pt=1\,in$ is not only
+better for calculation, it~is also easier to remember.
+
+\danger \TeX\ will not deal with dimensions whose absolute value is
+$\rm2^{30}\,sp$ or more. In other words, the ^{maximum legal dimension} is
+slightly less than $16384\pt$. This is a distance of about 18.892 feet
+(5.7583 meters), so it won't cramp your style.
+
+In a language manual like this it is convenient to use ``^{angle brackets}''
+in abbreviations for various constructions like \<number> and \<optional
+sign> and \<digit string>. Henceforth we shall use the term ^\<dimen> to
+stand for a legitimate \TeX\ dimension. For example,
+\begindisplay
+|\hsize=|\<dimen>
+\enddisplay
+will be the general way to define the column width that \TeX\ is supposed
+to use. The idea is that \<dimen> can be replaced by any quantity like
+`|4in|' that satisfies \TeX's grammatical rules for dimensions;
+abbreviations in angle brackets make it easy to state such laws of grammar.
+
+When a dimension is zero, you have to specify a unit of measure even
+though the unit is irrelevant. Don't just say `|0|'\thinspace; say `|0pt|' or
+`|0in|' or something.
+
+\smallbreak
+The 10-point size of type that you are now reading is normal in textbooks,
+but you probably will often find yourself wanting a larger font. Plain \TeX\
+makes it easy to do this by providing {\magnifiedfiverm ^{magnif{}ied
+output}.} If you say
+\begintt
+\magnification=1200
+\endtt
+at the beginning of your manuscript, everything will be enlarged by 20\%;
+i.e., it will come out at 1.2 times the normal size. Similarly,
+`|\magnification=2000|' doubles everything; this actually quadruples the area of
+each letter, since heights and widths are both doubled. To magnify a
+document by the factor $f$, you say ^|\magnification||=|\<number>, where
+the \<number> is 1000~times~$f$. This instruction must be given before the
+first page of output has been completed. You cannot apply two different
+magnifications to the same document.
+
+Magnification has obvious advantages: You'll have less ^{eyestrain} when
+you're ^{proofreading}; you can easily make ^{transparencies} ^^{slides}
+for lectures; and you can photo-reduce magnified output, in order to minimize
+the deficiencies of a ^{low-resolution printer}. Conversely, you might
+even want `|\magnification=500|' in order to create a ^{pocket-size}
+version of some book. ^^{squint print} But there's a slight catch:
+You can't use magnification unless your printing device happens to have the
+fonts that you need at the magnification you desire. In other words, you need
+to find out what sizes are available before you can magnify. Most
+installations of \TeX\ make it possible to print all the fonts of plain
+\TeX\ if you magnify by ^|\magstep||0|,~|1|, |2|,~|3|, and perhaps~|4| or
+even~|5| (see Chapter~4); but the use of large fonts can be expensive
+because a lot of system memory space is often required to store the shapes.
+
+\exercise Try printing the short story of Chapter 6 at 1.2, 1.44, and 1.728
+times the normal size. What should you type to get \TeX\ to do this?
+\answer For example, say `|\magnification=\magstep1 \input story \end|'
+to get magnification 1200; |\magstep2| and |\magstep3| are 1440 and 1728.
+Three separate runs are needed, since there can be at most one
+magnification per job. The output may look funny if the fonts don't exist
+at the stated magnifications.
+
+\danger When you say |\magnification=2000|, an operation like
+`|\vskip.5cm|' will actually skip $\rm1.0\,cm$ of space in the final
+document. If you want to specify a dimension in terms of the final size,
+\TeX\ allows you to say `^|true|' just before |pt|, |pc|, |in|, |bp|,
+|cm|, |mm|, |dd|, |cc|, and |sp|. This unmagnifies the units, so that the
+subsequent magnification will cancel out. For example, `|\vskip.5truecm|'
+is equivalent to `|\vskip.25cm|' if you have previously said
+`|\magnification=2000|'. Plain \TeX\ uses this feature in the
+|\magnification| command itself: Appendix~B includes the instruction
+\begintt
+\hsize = 6.5 true in
+\endtt
+just after a new magnification has taken effect. This adjusts the line width
+so that the material on each page will be $6{1\over2}$ inches wide when it
+is finally printed, regardless of the magnification factor.
+There will be an inch of margin at both left and right,
+assuming that the paper is $8{1\over2}$ inches wide.
+
+\danger If you use no `|true|' dimensions, \TeX's internal computations are not
+affected by the presence or absence of magnification; line breaks and page
+breaks will be the same, and the ^|dvi| file will change in only two places.
+\TeX\ simply tells the printing routine that you want a certain magnification,
+and the printing routine will do the actual enlargement when it reads the
+|dvi| file.
+
+\dangerexercise Chapter 4 mentions that fonts of different magnifications
+can be used in the same job, by loading them `^|at|' different sizes.
+Explain what fonts will be used when you give the commands
+^^{magnified fonts} ^^|scaled|
+\begintt
+\magnification=\magstep1
+\font\first=cmr10 scaled\magstep1
+\font\second=cmr10 at 12truept
+\endtt
+\answer Magnification is by a factor of 1.2. Since font |\first| is |cmr10|
+at $12\pt$, it will be |cmr10| at $14.4\pt$ after magnification;
+font |\second| will be |cmr10| at $12\pt$. \ (\TeX\ changes
+`|12truept|' into `|10pt|', and the final output magnifies it back to
+$12\pt$.)
+
+\ddanger Magnification is actually governed by \TeX's ^|\mag| primitive,
+which is an integer parameter that should be positive and at~most~32768.
+The value of\/ |\mag| is examined in three cases: (1)~just before the
+first page is shipped to the |dvi| file; (2)~when computing a |true|
+dimension; (3)~when the |dvi| file is being closed. Alternatively,
+some implementations of \TeX\ produce non-|dvi| output; they examine
+|\mag| in case~(2) and also when shipping out each page. Since each
+document has only one magnification, the value of\/ |\mag| must not change
+after it has first been examined.
+
+\danger \TeX\ also recognizes two units of measure that are relative
+rather than absolute; i.e., they depend on the current context:
+\begindisplay
+^|em| is the width of a ``^{quad}'' in the current font;\cr
+^|ex| is the ``^{x-height}'' of the current font.\cr
+\enddisplay
+Each font defines its own em and ex values. In olden days, an ``em'' was
+the width of an `M', but this is no longer true; ems are simply arbitrary
+units that come with a font, and so are exes. The Computer Modern fonts
+have the property that an em-dash is one em wide, each of the ^{digits} 0
+to~9 is half an em wide, and lowercase `x' is one ex high; but these are
+not hard-and-fast rules for all fonts.
+The |\rm| font (^|cmr10|) of plain \TeX\ has $\rm1\,em=10\,pt$
+and $\rm1\,ex\approx4.3\,pt$; the |\bf| font (^|cmbx10|) has
+$\rm1\,em=11.5\,pt$ and $\rm1\,ex\approx4.44\,pt$; and the |\tt| font
+(^|cmtt10|) has $\rm1\,em=10.5\,pt$ and $\rm1\,ex\approx4.3\,pt$. All of
+these are ``10-point'' fonts, yet they have different em and ex values.
+It~is generally best to use |em| for horizontal measurements and |ex| for
+vertical measurements that depend on the current font.
+
+\danger A \<dimen> can also refer to \TeX's internal registers or parameters.
+We shall discuss registers later, and a complete definition of everything that
+a ^\<dimen> can be will be given in Chapter~24. For now it will suffice to
+give some hints about what is to come:
+`|\hsize|' stands for the current horizontal line size,
+and `|.5\hsize|' is half that amount;
+`|2\wd3|' denotes twice the width of register~|\box3|;
+`|-\dimen100|' is the negative of register~|\dimen100|.
+
+\ddanger Notice that the unit names in dimensions
+are not preceded by backslashes. The same is true of other so-called
+^{keywords} of the \TeX\ language. Keywords can be given in uppercase letters
+or in a mixture of upper and lower case; e.g., `|Pt|' is equivalent to `|pt|'.
+The category codes of these letters are irrelevant; you may, for example,
+be using a |p| of category~12 (other) that was generated by expanding
+`|\the\hsize|' as explained in Chapter~20.
+\TeX\ gives a special interpretation to keywords only when they
+appear in certain very restricted contexts. For example, `|pt|' is a
+keyword only when it appears after a number in a \<dimen>;
+`|at|' is a keyword only when it appears after the external name of a
+font in a |\font| declaration.
+Here is a complete list of \TeX's keywords, in case you are wondering about
+the full set: |at|, |bp|, |by|, |cc|, |cm|, |dd|, |depth|, |em|, |ex|,
+|fil|, |height|, |in|, |l|, |minus|, |mm|, |mu|, |pc|, |plus|,
+|pt|, |scaled|, |sp|, |spread|, |to|, |true|, |width|. ^^{reserved words}
+\ (See Appendix~I for references to the contexts in which each of these is
+recognized as a keyword.)
+
+\endchapter
+
+The methods that have hitherto been taken
+to discover the measure of the Roman foot,
+will, upon examination, be found so unsatisfactory, that
+it is no wonder the learned are not yet agreed on that point.
+$\ldots$
+9 London inches are equal to 8,447 Paris inches.
+\author MATTHEW ^{RAPER}, in {\sl Philosophical Transactions\/} (1760)
+% ``An Enquiry into the Measure of the {\sl Roman\/} Foot,''
+% {\sl Philos.\ Trans.\ \bf51} (1760), 774--823.
+
+\bigskip
+
+\checkequals\sesame\pageno %
+Without the letter U,
+units would be nits.
+\author ^{SESAME STREET}{^^{Children's Television Workshop}} (1970)
+
+\eject
+ \beginchapter Chapter 11. Boxes
+
+\TeX\ makes complicated pages by starting with simple individual characters
+and putting them together in larger units, and putting these together in still
+larger units, and so on. Conceptually, it's a big paste-up job. The \TeX nical
+terms used to describe such page construction are {\sl ^{boxes}\/} and
+{\sl ^{glue}}.
+
+Boxes in \TeX\ are two-dimensional things with a rectangular shape, having
+three associated measurements called {\sl^{height}}, {\sl^{width}}, and
+{\sl^{depth}}. Here is a picture of a typical box, showing its so-called
+^{reference point} and ^{baseline}:
+
+{\eightpoint
+\setbox0=\hbox{$\uparrow$}
+\setbox1=\hbox to \wd0{$\hss\mid\hss$} % with luck, they'll line up
+\setbox2=\vbox{\copy0
+ \nointerlineskip \kern-.5pt \copy1
+ \nointerlineskip \kern-.5pt \copy1
+ \moveleft 1em\hbox{height}
+ \copy1 \nointerlineskip \kern-.5pt
+ \copy1 \nointerlineskip \kern-.5pt
+ \hbox{$\downarrow$}
+ \kern.2pt}
+\setbox3=\vbox{\kern.2pt\copy0
+ \moveleft 1em\hbox{depth}
+ \hbox{$\downarrow$}
+ \kern0pt}
+\setbox4=\vtop{\kern-3pt % this cancels the null text above the samplebox
+ \hbox{\samplebox{\ht2}{\ht3}{6em}{}%
+ \kern-6em
+ \raise3pt\hbox to 6em{\hss Baseline\hss}}
+ \kern3pt
+ \arrows{6em}{width}}
+\medskip\indent
+\setbox0=\hbox{$\vcenter{}$}% \ht0 is the axis height
+\lower\ht0\hbox{Reference point$-$\kern-.2em$\rightarrow$\kern2pt}%
+\raise\ht2\box4
+\kern1.5em
+\raise\ht2\vtop{\kern0pt\box2\nointerlineskip\box3}}
+
+\medskip\noindent
+From \TeX's viewpoint, a single character from a font is a box; it's one
+of the simplest kinds of boxes. The font designer has decided what the
+height, width, and depth of the character are, and what the symbol will
+look like when it is in the box; \TeX\ uses these dimensions to paste
+boxes together, and ultimately to determine the locations of the reference
+points for all characters on a page. In plain \TeX's |\rm| font (|cmr10|), for
+example, the letter `h' has a height of 6.9444 points, a width of 5.5555
+points, and a depth of zero; the letter `g' has a height of 4.3055
+points, a width of 5 points, and a depth of 1.9444 points. Only certain
+special characters like parentheses have height plus depth actually equal
+to 10 points, although ^|cmr10| is said to be a ``10-point'' font. You needn't
+bother to learn these measurements yourself, but it's good to be aware of
+the fact that \TeX\ deals with such information; then you can better
+understand what the computer does to your manuscript.
+
+The character shape need not fit inside the boundaries of its box. For example,
+some characters that are used to build up larger math symbols like matrix
+brackets intentionally protrude a little bit, so that they overlap
+properly with the rest of the symbol. Slanted letters frequently extend a
+little to the right of the box, as if the box were skewed right at the top
+and left at the bottom, keeping its baseline fixed. For example, compare
+the letter `g' in the |cmr10| and ^|cmsl10| fonts (|\rm| and |\sl|):
+\begindisplay
+\vbox to 40pt{\ifproofmode\hrule\vfill
+ \hsize=2.5in \baselineskip 6pt \fiverm\noindent
+ (A figure will be inserted here; too bad you can't see it now.
+ It shows two g's, as claimed.)
+ \vfill\hrule\fi}
+\enddisplay
+In both cases \TeX\ thinks that the box is 5 points wide, so both letters get
+exactly the same treatment. \TeX\ doesn't have any idea where the ink will
+go---only the output device knows this. But the slanted letters will be
+spaced properly in spite of \TeX's lack of knowledge, because the baselines
+will match up.
+
+Actually the font designer also tells \TeX\ one other thing, the so-called
+{\sl^{italic correction}\/}: A number is specified for each character,
+telling roughly how far that character extends to the right of its box
+boundary, plus a little to spare. For example,
+the italic correction for `g' in |cmr10| is $0.1389\pt$, while in |cmsl10|
+it is $0.8565\pt$. Chapter~4 points out that this correction is added to the
+normal width if you type `^|\/|' just after the character. You should remember
+to use |\/| when shifting from a slanted font to an unslanted one, especially
+in cases like
+\begintt
+the so-called {\sl italic correction\/}:
+\endtt
+since no space intervenes here to compensate for the loss of slant.
+
+\smallbreak
+\TeX\ also deals with another simple kind of box, which might be called
+a~``^{black box},'' namely, a rectangle like
+`\thinspace \vrule width 4pt height 6pt depth 1.5pt \thinspace'
+that is to be entirely filled with ink at printing time. You can specify any
+height, width, and depth you like for such boxes---but they had better not have
+too much area, or the printer might get upset. \ (Printers generally
+prefer white space to black space.)
+
+Usually these black boxes are made very skinny, so that they appear as
+horizontal lines or vertical lines. Printers traditionally call such lines
+``^{horizontal rules}'' and ``^{vertical rules},'' so the terms \TeX\ uses
+to stand for black boxes are ^|\hrule| and ^|\vrule|. Even when the box is
+square, as in `\thinspace\bull\thinspace', you must call it either an~|\hrule|
+or a~|\vrule|. We shall discuss the use of ^{rule boxes} in greater
+detail later. \ (See Chapter~21.)
+
+\smallbreak
+Everything on a page that has been typeset by \TeX\ is made up of simple
+character boxes or rule boxes, pasted together in combination. \TeX\
+pastes boxes together in two ways, either {\sl horizontally\/} or {\sl
+vertically}. When \TeX\ builds a ^{horizontal list} of boxes, it lines
+them up so that their reference points appear in the same horizontal row;
+therefore the baselines of adjacent characters will match up as they
+should. Similarly, when \TeX\ builds a ^{vertical list} of boxes, it lines
+them up so that their reference points appear in the same vertical column.
+
+% Here are some macros for making blank boxes
+\def\dolist{\afterassignment\dodolist\let\next= }
+\def\dodolist{\ifx\next\endlist \let\next\relax
+ \else \\\let\next\dolist \fi
+ \next}
+\def\endlist{\endlist}
+\def\\{\expandafter\if\space\next\ \else \setbox0=\hbox{\next}\maketypebox\fi}
+\def\demobox#1{\setbox0=\hbox{\dolist#1\endlist}%
+ \copy0\kern-\wd0\makelightbox}
+
+Let's take a look at what \TeX\ does behind the scenes, by comparing the
+computer's methods with what you would do if you were setting metal type
+by hand. In the time-tested traditional method, you choose the letters that
+you~need out of a type case---the uppercase letters are in the ^{upper
+case}---and you put them into a ``^{composing stick}.'' When a line is
+complete, you adjust the spacing and transfer the result to the ``chase,''
+where it joins the other rows of type. Eventually you lock the type up
+tightly by adjusting external wedges called ``quoins.'' This isn't much
+different from what \TeX\ does, except that different words are used; when
+\TeX\ locks up a line, it creates what is called an ``^{hbox}''
+(^{horizontal box}), because the components of the line are pieced
+together horizontally. You can give an instruction like
+\begintt
+\hbox{A line of type.}
+\endtt
+in a \TeX\ manuscript; this tells the computer to take boxes for the appropriate
+letters in the current font and to lock them up in an hbox. As far as \TeX\ is
+concerned, the letter `A' is a box
+`\thinspace\setbox0\hbox{A}\maketypebox\thinspace'
+and the letter `p' is a box
+`\thinspace\setbox0\hbox{p}\maketypebox\thinspace'.
+So the given instruction causes \TeX\ to form the hbox
+\begindisplay
+\demobox{A line of type.}
+\enddisplay
+representing `A line of type.' The hboxes for individual lines of type are
+eventually joined together by putting them into a ``^{vbox}'' (^{vertical
+box}). For example, you can say
+\begintt
+\vbox{\hbox{Two lines}\hbox{of type.}}
+\endtt
+and \TeX\ will convert this into
+\begindisplay%
+ \setbox0=\vbox{\hbox{\demobox{Two lines}}\hbox{\demobox{of type.}}}
+$\vcenter{\hbox{\makelightbox\kern-\wd0\box0}}$\qquad
+ i.e.,\qquad$\vcenter{\vbox{\hbox{Two lines}\hbox{of type.}}}$
+\enddisplay
+The principal difference between \TeX's method and the old way is that metal
+types are generally cast so that each character has the same height and
+depth; this makes it easy to line them up by hand. \TeX's types have
+variable height and depth, because the computer has no trouble lining
+characters up by their baselines, and because the extra information about
+height and depth helps in the positioning of accents and mathematical
+symbols.
+
+Another important difference between \TeX\ setting and hand setting is, of
+course, that \TeX\ will choose line divisions automatically; you don't
+have to insert ^|\hbox| and ^|\vbox| instructions unless you want to
+retain complete control over where each letter goes. On the other hand,
+if you do use |\hbox| and |\vbox|, you can make \TeX\ do almost everything
+that Ben ^{Franklin} could do in his printer's shop. You're only giving
+up the ability to make the letters come out charmingly crooked or badly
+inked; for such effects you need to make a new font. \ (And of course you
+lose the tactile and olfactory sensations, and the thrill of
+doing everything by yourself. \TeX\ will never completely replace the
+good~old~ways.)
+
+A page of text like the one you're reading is itself a box, in \TeX's view:
+It is a largish box made from a vertical list of smaller boxes representing
+the lines of text. Each line of text, in turn, is a box made from a
+horizontal list of boxes representing the individual characters. In more
+complicated situations, involving mathematical formulas and/or complex
+tables, you can have boxes within boxes within boxes $\ldots$ to any level.
+But even these complicated situations arise from horizontal or vertical lists
+of boxes pasted together in a simple way; all that you and \TeX\ have to
+worry about is one list of boxes at a time. In fact, when you're typing
+straight text, you don't have to think about boxes at all, since \TeX\ will
+automatically take responsibility for assembling the character boxes into
+words and the words into lines and the lines into pages. You need to be
+aware of the box concept only when you want to do something out of the
+ordinary, e.g., when you want to center a heading.
+
+\danger From the standpoint of \TeX's digestive processes, a manuscript
+comes in as a sequence of tokens, and the tokens are to be transformed into
+a sequence of boxes. Each token of input is essentially an instruction or
+a piece of an instruction; for example, the token `|A|$_{11}$' normally means,
+``put a character box for the letter |A| at the end of the current hbox,
+using the current font''; the token `\cstok{vskip}' normally means, ``skip
+vertically in the current vbox by the \<dimen> specified in the
+following tokens.''
+
+\danger The height, width, or depth of a box might be negative, in which
+case it is a ``^{shadow box}'' that is somewhat hard to draw. \TeX\ doesn't
+balk at ^{negative dimensions}; it just does arithmetic as usual. For example,
+the combined width of two adjacent boxes is the sum of their widths, whether
+or not the widths are positive. A font designer can declare a character's
+width to be negative, in which case the character acts like a ^{backspace}. \
+(Languages that read from right to left could be
+handled in this way, but only to a limited extent, since \TeX's line-breaking
+^^{Hebrew} ^^{Arabic}
+algorithm is based on the assumption that words don't have negative widths.)
+
+\danger \TeX\ can raise or lower the individual boxes
+in a horizontal list; such adjustments take care of mathematical
+subscripts and superscripts, as well as the heights of accents and a few
+other things. For example, here is a way to make a box that contains
+the \TeX\ logo, putting it into \TeX's internal register |\box0|:
+\begintt
+\setbox0=\hbox{T\kern-.1667em\lower.5ex\hbox{E}\kern-.125em X}
+\endtt
+^^|\setbox|
+Here `^|\kern||-.1667em|' means to insert blank space of $-.1667$ ems in the
+current font, i.e., to back up a bit; and `^|\lower||.5ex|' means that
+the box |\hbox{E}| is to be lowered by half of the current x-height, thus
+offsetting that box with respect to the others. Instead of
+`|\lower.5ex|' one could also say `^|\raise||-.5ex|'. Chapters 12 and~21
+discuss the details of how to construct boxes for special effects;
+our goal in the present chapter is merely to get a taste of the
+possibilities.
+
+\danger \TeX\ will exhibit the contents of any ^{box register}, if you
+ask it to. For example, if you type `^|\showbox||0|' after setting
+|\box0| to the \TeX\ logo as above, your ^{log file} will contain
+the following mumbo jumbo: ^^{TeX logo}
+\begintt
+\hbox(6.83331+2.15277)x18.6108
+.\tenrm T
+.\kern -1.66702
+.\hbox(6.83331+0.0)x6.80557, shifted 2.15277
+..\tenrm E
+.\kern -1.25
+.\tenrm X
+\endtt
+^^{diagnostic format} ^^{internal box-and-glue representation} ^^{box displays}
+The first line means that |\box0| is an hbox whose height, depth, and width
+are respectively $6.83331\pt$, $2.15277\pt$, and $18.6108\pt$.
+Subsequent lines beginning with `|.|'\ indicate that they are {\sl inside\/}
+of a box. The first thing in this particular box is the letter~|T| in
+font |\tenrm|; then comes a kern. The next item is an hbox that contains
+only the letter~|E|; this box has the height, depth, and width of an |E|, and
+it has been shifted downward by $2.15277\pt$ (thereby accounting for
+the depth of the larger box).
+
+\dangerexercise Why are there two dots in the `|..\tenrm E|' line here?
+\answer This |E| is inside a box that's inside a box.
+
+\danger Such displays of box contents will be discussed further in
+Chapters 12 and~17.
+They are used primarily for diagnostic purposes, when you are trying to figure
+out exactly what \TeX\ thinks it's doing. The main reason for bringing them
+up in the present chapter is simply to provide a glimpse of how \TeX\ represents
+boxes in its guts. A computer program doesn't really move boxes around; it
+fiddles with lists of representations of boxes.
+
+\dangerexercise By running \TeX, figure out how it actually handles italic
+corrections to characters: How are the corrections represented inside a box?
+\answer The idea is to construct a box and to look inside. For example,
+\begintt
+\setbox0=\hbox{\sl g\/} \showbox0
+\endtt
+reveals that |\/| is implemented by placing a kern after the character.
+Further experiment shows that this kern is inserted even when the italic
+correction is zero.
+
+\dangerexercise The ``opposite'' of \TeX's logo---namely,
+T\kern+.1667em\raise.5ex\hbox{E}\kern+.125em X---is produced by
+\begintt
+\setbox1=\hbox{T\kern+.1667em\raise.5ex\hbox{E}\kern+.125em X}
+\endtt
+What would |\showbox1| show now? \ (Try to guess, without running the machine.)
+\answer The height, depth, and width of the enclosing box should be just large
+enough to enclose all of the contents, so the result is:
+\begintt
+\hbox(8.98608+0.0)x24.44484
+.\tenrm T
+.\kern 1.66702
+.\hbox(6.83331+0.0)x6.80557, shifted -2.15277
+..\tenrm E
+.\kern 1.25
+.\tenrm X
+\endtt
+(You probably predicted a height of |8.9861|; \TeX's internal calculations are
+in |sp|, not |pt|/100000, so the rounding in the fifth decimal place is not
+readily predictable.)
+
+\dangerexercise Why do you think the author of \TeX\ didn't make boxes more
+symmetrical between horizontal and vertical, by allowing reference points
+to be inside the boundary instead of insisting that the reference point
+must appear at the left edge of each box?
+\answer No applications of such symmetrical boxes to English-language
+printing were apparent; it seemed pointless to carry extra generality
+as useless baggage that would rarely if ever be used, merely for the sake of
+symmetry. In other words, the author wore a computer science cap instead
+of a mathematician's mantle on the day that \TeX's boxes were born.
+Time will tell whether or not this was a fundamental error!
+
+\ddangerexercise Construct a |\demobox| macro for use in writing manuals
+like this, so that an author can write `|\demobox{Tough exercise.}|'
+in order to typeset `\thinspace\demobox{Tough exercise.}\thinspace'.
+\answer The following solution is based on a general |\makeblankbox|
+macro that prints the edges of a box using rules of given thickness
+outside and inside that box; the box dimensions are those of\/ |\box0|.\par
+|\def\dolist{\afterassignment\dodolist\let\next= }|\parbreak
+|\def\dodolist{\ifx\next\endlist \let\next\relax|\parbreak
+| \else \\\let\next\dolist \fi|\parbreak
+| \next}|\par
+|\def\endlist{\endlist}|\par
+|\def\hidehrule#1#2{\kern-#1%|\parbreak
+| \hrule height#1 depth#2 \kern-#2 }|\par
+|\def\hidevrule#1#2{\kern-#1{\dimen0=#1|\parbreak
+| \advance\dimen0 by#2\vrule width\dimen0}\kern-#2 }|\par
+|\def\makeblankbox#1#2{\hbox{\lower\dp0\vbox{\hidehrule{#1}{#2}%|\parbreak
+| \kern-#1 % overlap the rules at the corners|\parbreak
+| \hbox to \wd0{\hidevrule{#1}{#2}%|\parbreak
+| \raise\ht0\vbox to #1{}% set the vrule height|\parbreak
+| \lower\dp0\vtop to #1{}% set the vrule depth|\parbreak
+| \hfil\hidevrule{#2}{#1}}%|\parbreak
+| \kern-#1\hidehrule{#2}{#1}}}}|\par
+|\def\maketypebox{\makeblankbox{0pt}{1pt}}|\par
+|\def\makelightbox{\makeblankbox{.2pt}{.2pt}}|\par
+|\def\\{\if\space\next\ % assume that \next is unexpandable|\parbreak
+| \else \setbox0=\hbox{\next}\maketypebox\fi}|\par
+|\def\demobox#1{\setbox0=\hbox{\dolist#1\endlist}%|\parbreak
+| \leavevmode\copy0\kern-\wd0\makelightbox}|\par
+
+\def\frac#1/#2{\leavevmode\kern.1em
+ \raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em
+ /\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}
+
+\ddangerexercise Construct a |\frac| macro such that `|\frac1/2|' yields
+`\frac1/2'. \checkequals\fracexno\exno
+\answer |\def\frac#1/#2{\leavevmode\kern.1em|\parbreak
+|\raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em|\parbreak
+|/\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}|
+
+\endchapter
+
+I have several boxes in my memory
+in which I will keep them all very safe,
+% he's talking about "instructions"
+there shall not a one of them be lost.
+\author IZAAK ^{WALTON}, {\sl The Compleat Angler\/} (1653) % beginning Chap12
+% in 1654 and subsequent editions, this quote comes in Chap17
+% the 1653 spelling agrees with 20th century conventions in this passage!
+
+\bigskip
+
+How very little does the amateur, dwelling at home at ease,
+comprehend the labours and perils of the author.
+\author R. L. ^{STEVENSON} and L. ^{OSBOURNE}, {\sl The Wrong Box\/} (1889)
+
+\eject
+ \beginchapter Chapter 12. Glue
+
+But there's more to the story than just boxes: There's also some magic mortar
+called {\sl ^{glue}\/} that
+\TeX\ uses to paste boxes together. For example, there is a little space between
+the lines of text in this manual; it has been calculated so that the baselines
+of consecutive lines within a paragraph are exactly 12~points apart. And
+there is space between words too; such space is not an ``empty'' box, it is
+part of the glue between boxes. This glue can stretch or shrink so that the
+right-hand margin of each page comes out looking straight.
+^^{leading, see baselineskip} ^^{skipping space, see glue}
+
+When \TeX\ makes a large box from a horizontal or vertical list of smaller
+boxes, there often is glue between the smaller boxes. Glue has three
+attributes, namely its natural {\sl space}, its ability to {\sl ^{stretch}}, and
+its ability to {\sl ^{shrink}}.
+
+In order to understand how this works, consider the following example of
+four boxes in a horizontal list separated by three globs of glue:
+\begindisplay\eightpoint
+\vbox{
+ \hbox{\samplebox{7mm}{8mm}{5\varunit}{width 5}%
+ \sampleglue{9\varunit}{space 9\cr stretch 3\cr shrink 1}%
+ \samplebox{3mm}{2mm}{6\varunit}{width 6}%
+ \sampleglue{9\varunit}{space 9\cr stretch 6\cr shrink 2}%
+ \samplebox{8mm}{3mm}{3\varunit}{width 3}%
+ \sampleglue{12\varunit}{space 12\cr stretch 0\cr shrink 0}%
+ \samplebox{4mm}{7mm}{8\varunit}{width 8}}
+ \kern6pt
+ \arrows{52\varunit}{width 52}}
+\enddisplay
+The first glue element has 9 units of space, 3 of stretch, and 1 of shrink;
+the next one also has 9 units of space, but 6 units of stretch and 2 of
+shrink; the last one has 12 units of space, but it is unable to stretch
+or to shrink, so it will remain 12 units of space no matter what.
+
+The total width of boxes and glue in this example, considering only the
+space components of the glue, is $5+9+6+9+3+12+8=52$ units. This is called
+the {\sl ^{natural width}\/} of the horizontal list; it's the preferred way to
+paste the boxes together. Suppose, however, that \TeX\ is told to make the
+horizontal list into a box that is 58~units wide; then the glue has to
+stretch by 6~units. Well, there are $3+6+0=9$ units of stretchability present,
+so \TeX\ multiplies each unit of stretchability by 6/9 in order to obtain the
+extra 6~units needed. The first glob of glue becomes $9+(6/9)\times3=11$
+units wide, the next becomes $9+(6/9)\times6=13$ units wide, the last remains
+12 units wide, and we obtain the desired box looking like this:
+\begindisplay\eightpoint
+\vbox{\kern-3pt
+ \hbox{\samplebox{7mm}{8mm}{5\varunit}{}%
+ \sampleglue{11\varunit}{$9+2$}%
+ \samplebox{3mm}{2mm}{6\varunit}{}%
+ \sampleglue{13\varunit}{$9+4$}%
+ \samplebox{8mm}{3mm}{3\varunit}{}%
+ \sampleglue{12\varunit}{$12+0$}%
+ \samplebox{4mm}{7mm}{8\varunit}{}}
+ \kern6pt
+ \arrows{58\varunit}{width 58}}
+\enddisplay
+
+On the other hand, if \TeX\ is supposed to make a box 51 units wide from the
+given list, it is necessary for the glue to shrink by a total of one unit. There
+are three units of shrinkability present, so the first glob of glue would
+shrink by 1/3 and the second by 2/3.
+
+\smallbreak
+The process of determining glue thickness when a box is being made from a
+horizontal or vertical list is called {\sl ^{setting the glue}}. Once glue has
+been set, it becomes rigid; it won't stretch or shrink any more, and the
+resulting box is essentially indecomposable.
+
+Glue will never shrink more than its stated shrinkability. For example,
+the first glob of glue in our illustration will never be allowed to become
+narrower than 8 units wide, and \TeX\ will never shrink the given
+horizontal list to make its total width less than 49 units. But glue is
+allowed to stretch arbitrarily far, whenever it has a positive stretch
+component.
+
+\exercise How wide would the glue globs be if the horizontal list in the
+illustration were to be made 100 units wide?
+\answer $9+16$ units, $9+32$ units, $12+0$ units. \ (But \TeX\ would
+consider so much stretching to be ``infinitely bad.'')
+
+Once you understand \TeX's concept of glue, you may well decide that it
+was misnamed; real glue doesn't stretch or shrink in such ways, nor does it
+contribute much space between boxes that it welds together. Another word
+like ``spring'' would be much closer to the essential idea, since ^{springs}
+have a natural width, and since different springs compress and expand at
+different rates under tension. But whenever the author has suggested
+changing \TeX's terminology, numerous people have said that they like the
+word ``glue'' in spite of its inappropriateness; so the original name has
+stuck.
+
+\danger \TeX\ is somewhat reluctant to stretch glue more than the stated
+stretchability; therefore you can decide how big to make each aspect of the
+glue by using the following rules: \ (a)~The natural
+glue space should be the amount of space that looks best. \ (b)~The glue
+stretch should be the maximum amount of space that can be added to the natural
+spacing before the layout begins to look bad. \ (c)~The glue shrink should
+be the maximum amount of space that can be subtracted from the natural spacing
+before the layout begins to look bad.
+
+In most cases the designer of a book layout will have specified all the kinds
+of glue that are to be used, so a typist will not need to decide how big any
+glue attributes should be. For example, users of the plain \TeX\ format of
+Appendix~B can type `|\smallskip|' when they want a little extra ^{space
+between paragraphs}; a ^|\smallskip| turns out to be $3\pt$ worth of
+vertical glue that can stretch or shrink by an additional~$1\pt$. Here is
+a |\smallskip|: \smallskip
+
+\noindent
+Instead of sprinkling various amounts of glue throughout a manuscript,
+expressing each of them explicitly in terms of points, you will find it
+much better to explain your intentions more clearly by typing something
+like `|\smallskip|' when you want abnormal spacing. The definition of\/
+|\smallskip| can readily be changed later, in case you want such spaces to
+be smaller or larger. Plain \TeX\ also provides you with `^|\medskip|',
+which is worth two smallskips, and `^|\bigskip|', which is worth two medskips.
+
+\danger A plain \TeX\ |\medskip| appears before and after each
+``^{dangerous bend}'' section of this manual, so you have already seen
+numerous examples of such spacing before you knew what it was called.
+Vertical glue is created by writing `|\vskip|\<glue>', where ^\<glue> is
+any glue specification. The usual way to specify \<glue> to \TeX\ is
+\begindisplay
+\<dimen> |plus|\<dimen> |minus|\<dimen>
+\enddisplay
+where the `|plus|\<dimen>' and `|minus|^\<dimen>' are optional and assumed
+to be zero if not present; `^|plus|' introduces the amount of
+stretchability, `^|minus|' introduces the amount of shrinkability.
+For example, Appendix~B defines |\medskip| to be an abbreviation for
+`|\vskip6pt plus2pt minus2pt|'. The normal-space component of glue must
+always be given as an explicit \<dimen>, even when it is zero.
+
+\danger Horizontal glue is created in the same way, but with ^|\hskip|
+instead of\/ ^|\vskip|. For example, plain \TeX\ defines ^|\enskip| as an
+abbreviation for the command `|\hskip.5em\relax|'; this skips horizontally by
+one ``^{en},'' i.e., by exactly half of an em in the current font. There is no
+stretching or shrinking in an |\enskip|. The control sequence ^|\relax|
+after `|.5em|' prevents \TeX\ from thinking that a ^{keyword}
+is present, in case the text following |\enskip| just happens to begin
+with `|plus|' or `|minus|'.
+
+One of the interesting things that can happen when glue stretches and shrinks
+at different rates is that there might be glue with {\sl ^{infinite}\/}
+stretchability. For example, consider again the four boxes we had at the
+beginning of this chapter, with the same glue as before except that the
+glue in the middle can stretch infinitely far. Now the total
+stretchability is infinite; and when the line has to grow, all of the
+additional space is put into the middle glue. If, for example, a box of
+width 58 is desired, the middle glue expands from 9 to~15 units, and the
+other spacing remains unchanged.
+
+If such infinitely stretchable glue is placed at the left of a row of
+boxes, the effect is to place them ``flush right,'' i.e., to move them
+over to the ^^{right justification} ^^{centering} ^^{flush right}
+rightmost boundary of the constructed box. And if you take {\sl two\/}
+globs of infinitely stretchable glue, putting one at the left and one
+at the right, the effect is to {\sl center\/} the list of boxes within
+a larger box. This in fact is how the ^|\centerline| instruction works
+in plain \TeX\null: It places infinite glue at both ends, then makes a
+box whose width is the current value of\/ |\hsize|.
+
+The short story example of Chapter 6 used infinite glue not only for
+centering, but also in the ^|\vfill| instruction at the end; `|\vfill|'
+essentially means ``skip vertically by zero, but with infinite stretchability.''
+In other words, |\vfill| fills up the rest of the current page with
+blank space.
+
+\danger \TeX\ actually recognizes several kinds of infinity, some of which
+are ``more infinite'' than others. You can say both ^|\vfil| and |\vfill|;
+the second is stronger than the first. In other words, if no other
+infinite stretchability is present, |\vfil| will expand to fill the remaining
+space; but if both |\vfil| and |\vfill| are present simultaneously,
+the |\vfill| effectively prevents |\vfil| from stretching. You can think
+of it as if\/ |\vfil| has one mile of stretchability, while |\vfill| has
+a trillion miles.
+
+\danger Besides |\vfil| and |\vfill|, \TeX\ has ^|\hfil| and ^|\hfill|,
+for stretching indefinitely in the horizontal direction. You can also say
+^|\hss| or ^|\vss|, in order to get glue that is infinitely shrinkable as
+well as infinitely stretchable. \ (The name `|\hss|' stands for ``horizontal
+stretch or shrink''; `|\vss|' is its vertical counterpart.) \ Finally, the
+primitives ^|\hfilneg| and ^|\vfilneg| will cancel the stretchability of
+|\hfil| and |\vfil|; we shall discuss applications of these curious glues later.
+
+\danger Here are some examples of\/ |\hfil|, using the ^|\line| macro of
+plain \TeX, which creates an hbox whose width is the current |\hsize|:
+^^{flush left}
+\begintt
+\line{This text will be flush left.\hfil}
+\line{\hfil This text will be flush right.}
+\line{\hfil This text will be centered.\hfil}
+\line{Some text flush left\hfil and some flush right.}
+\line{Alpha\hfil centered between Alpha and Omega\hfil Omega}
+\line{Five\hfil words\hfil equally\hfil spaced\hfil out.}
+\endtt
+
+\dangerexercise Describe the result of \xdef\linexno{\the\exno}%
+\begintt
+\line{\hfil\hfil What happens now?\hfil}
+\line{\hfill\hfil and now?\hfil}
+\endtt
+\answer `What happens now?' is placed in a line of width |\hsize|, with
+twice as much space at the left as at the right; `and now?' is put flush right
+on the following line.
+
+\ddangerexercise How do the following three macros behave differently?
+\begintt
+\def\centerlinea#1{\line{\hfil#1\hfil}}
+\def\centerlineb#1{\line{\hfill#1\hfill}}
+\def\centerlinec#1{\line{\hss#1\hss}}
+\endtt
+\answer The first two give an ``overfull box'' if the argument doesn't fit
+on a line; the third allows the argument to stick out into the margins
+instead. \ (Plain \TeX's ^|\centerline| is |\centerlinec|; the stickout effect
+shows up in the narrow-column experiment of Chapter~6.) \ If the argument
+contains no infinite glue, |\centerlinea| and |\centerlineb| produce the same
+effect; but |\centerlineb| will center an argument that contains `fil' glue.
+
+\danger In order to specify such infinities, you are allowed to use
+the special units `^|fil|', `^|fill|', and `^|filll|' in the \<dimen>
+parts of a stretchability or shrinkability component. For example,
+|\vfil|, |\vfill|, |\vss|, and |\vfilneg| are essentially equivalent
+to the glue specifications
+\begintt
+\vskip 0pt plus 1fil
+\vskip 0pt plus 1fill
+\vskip 0pt plus 1fil minus 1fil
+\vskip 0pt plus -1fil
+\endtt
+respectively. It's usually best to stick to the first order infinity
+(fil) as much as you can, resorting to second order (fill) only when
+you really need something extremely infinite. Then the ultimate order (filll) is
+always available as a last resort in emergencies. \ (\TeX\ does not
+provide a `^|\vfilll|' primitive, since the use of this highest infinity
+is not encouraged.) \ You can use fractional multiples of infinity like
+`|3.25fil|', as long as you stick to fewer than 16384 fil units. \TeX\
+actually does its calculations with integer multiples of $\rm2^{-16}\,fil$
+(or fill or filll); so |0.000007filll| turns out to be indistinguishable
+from |0pt|, but |0.00001filll| is infinitely greater than |16383.99999fill|.
+
+Now here's something important for all \TeX nical typists to know:
+Plain \TeX\ puts extra space at the end of a ^{sentence}; furthermore,
+it automatically increases the stretchability (and decreases
+the shrinkability) after ^{punctuation} marks. The reason is that it's
+usually better to put more space after punctuation than between two ordinary
+words, when spreading a line out to reach the desired margins. Consider, for
+example, the following sentences from a classic kindergarten pre-primer:
+^^{Dick and Jane}
+\begintt
+``Oh, oh!'' cried Baby Sally. Dick and Jane laughed.
+\endtt
+If \TeX\ sets this at its natural width, all the spaces will be the same,
+except after the quote and after `Baby Sally.':
+\begindisplay
+``Oh, oh!'' cried Baby Sally. Dick and Jane laughed.
+\enddisplay
+But if the line needs to be expanded by 5 points, 10 points, 15 points, or more,
+\TeX\ will set it as
+\begindisplay
+\hbox spread 5pt{``Oh, oh!'' cried Baby Sally. Dick and Jane laughed.}\cr
+\hbox spread 10pt{``Oh, oh!'' cried Baby Sally. Dick and Jane laughed.}\cr
+\hbox spread 15pt{``Oh, oh!'' cried Baby Sally. Dick and Jane laughed.}\cr
+\hbox spread 20pt{``Oh, oh!'' cried Baby Sally. Dick and Jane laughed.}\cr
+\enddisplay
+The glue after the comma stretches at 1.25 times the rate of the
+glue between adjacent words; the glue after the period and after the |!''|
+stretches at 3 times the rate. There is no glue between adjacent letters,
+so individual words will always look the same. If \TeX\ had to shrink
+this line to its minimum width, the result would be
+\begindisplay \hfuzz 1000pt
+\hbox to 0pt{``Oh, oh!'' cried Baby Sally. Dick and Jane laughed.}
+\enddisplay
+The glue after a ^{comma} shrinks only 80 percent as much as ordinary
+inter-word glue, and after a ^{period} or ^{exclamation point} or
+^{question mark} it shrinks by only one third as much.
+
+This all makes for nice-looking output, but it unfortunately adds a bit
+of a burden to your job as a typist, because \TeX's rule for determining
+the end of a sentence {\sl doesn't always work}. The problem is that
+a period sometimes comes in the middle of a sentence $\ldots$ like when it
+is used (as here) to make an ``^{ellipsis}'' of three dots.
+^^{three dots, see ellipsis} ^^{dot dot dot, see ellipsis}
+
+Moreover, if you try to specify `$\ldots$' by typing three periods in
+a row, you get `...'---the dots are too close together. One way to
+handle this is to go into {\sl mathematics\/} mode, using the ^|\ldots|
+control sequence defined in plain \TeX\ format. For example, if you type
+\begintt
+Hmmm $\ldots$ I wonder why?
+\endtt
+the result is `Hmmm $\ldots$ I wonder why?'. This works because
+math formulas are exempt from the normal text spacing rules.
+Chapter 18 has more to say about |\ldots| and related topics.
+
+^{Abbreviations} present problems too. For example, the short story in
+Chapter~6 referred to `Mr.~^{Drofnats}'; \TeX\ must be told somehow that the
+period after `Mr.'\ or `Mrs.'\ or `Ms.' or `Prof.'\ or `Dr.' or `Rt.~Hon.',
+etc., doesn't count as a sentence-ending ^{full stop}.
+
+We avoided that embarrassment in Chapter~6 by typing `|Mr.~Drofnats|';
+the ``^{tie}'' mark |~| ^^{tilde} tells plain \TeX\ to insert a normal
+space, and to refrain from breaking between lines at that space. Another way
+to get \TeX\ to put out a normal space is to type `|\|\]' (^{control
+space}); e.g., `|Mr.\ Drofnats|' would be almost the same as `|Mr.~Drofnats|',
+except that a line might end after the `Mr.'.
+
+The tie mark is best for abbreviations within a name, and after several
+other common abbreviations like `Fig.'\ and `cf.'\ and `vs.'\ and `resp.';
+you will find that it's easy to train yourself to type `|cf.~Fig.~5|'.
+In fact, it's usually wise to type |~| (instead of a space) just after a common
+abbreviation that occurs in the middle of a sentence.
+Manuals of style will tell you that the abbreviations `e.g.'\ and `i.e.'\
+should always be followed by commas, never by spaces, so those particular
+cases shouldn't need any special treatment.
+
+The only remaining abbreviations that arise with significant frequency
+occur in bibliographic references; ^{control spaces} are appropriate here.
+^^{interword spacing}
+If, for example, you are typing a manuscript that refers to
+`Proc.\ Amer.\ Math.\ Soc.', you should say
+\begintt
+Proc.\ Amer.\ Math.\ Soc.
+\endtt
+Granted that this input looks a bit ugly, it makes the output look right.
+It's one of the things we occasionally must do when dealing with a computer
+that tries to be smart.
+
+\exercise Explain how to type the following sentence: ``Mr.~\& Mrs.~^{User}
+were married by Rev.~^{Drofnats}, who preached on
+Matt.~19\thinspace:\thinspace3--9.''
+\answer |Mr.~\& Mrs.~User
+were married by Rev.~Drofnats, who preached on
+Matt.~19\thinspace:\thinspace3--9.| \ (Such thin spaces are traditional
+for ^{Biblical references} to chapter and verse, but you weren't really
+expected to know that. Plain \TeX\ defines ^|\thinspace| to be a kern,
+not glue; hence no break between lines will occur at a thinspace.)
+
+\exercise Put the following bibliographic reference into plain \TeX\
+language: Donald~E. ^{Knuth}, ``Mathematical typography,'' {\sl Bull.\
+Amer.\ Math.\ Soc.\ \bf1} (1979), 337--372.
+\answer |Donald~E.\ Knuth, ``Mathematical typography,'' {\sl Bull.\
+Amer.\ Math.\ Soc.\ \bf1} (1979), 337--372.| \ (But the `|\|' after `|E.|'
+isn't necessary, because of a rule you will learn if you venture
+around the next dangerous bend.)
+
+On the other hand, if you don't care about such refinements of spacing
+you can tell plain \TeX\ to make all spaces the same, regardless of
+punctuation marks, by simply typing `^|\frenchspacing|' at the beginning
+of your manuscript. French spacing looks like this:
+\begindisplay \frenchspacing
+``Oh, oh!'' cried Baby Sally. Dick and Jane laughed.
+\enddisplay
+You can also shift back and forth between the two styles, either by saying
+`^|\nonfrenchspacing|' to establish ^{sophisticated spacing}, or by making
+your use of\/ |\frenchspacing| local to some group. For example, you might
+want to use French spacing only when typing the bibliography of some document.
+
+\danger \TeX\ doesn't consider a period or question mark or exclamation point
+to be the end of a sentence if the preceding character is an uppercase
+letter, since \TeX\ assumes that such uppercase letters are most likely
+somebody's initials. Thus, for example, the `|\|' is unnecessary after
+the~`|I.|' in `|Dr.~Livingstone~I.\ Presume|'; that particular period is
+not assumed to be a full stop. ^^{Presume}
+
+\dangerexercise What can you do to make \TeX\ recognize the ends of sentences
+that do end with uppercase letters (e.g., `$\ldots$ launched by NASA.\null' or
+`Did I?' or `$\ldots$ see Appendix~A.')?
+\answer There are several ways; perhaps the easiest are to type
+`|\hbox{NASA}.|'\ or `|NASA\null.|' \ (The ^|\null| macro is an abbreviation
+for `|\hbox{}|'.)
+
+\danger You can see the glue that \TeX\ puts between words by looking at
+the contents of hboxes in the internal ^{diagnostic format} that we
+discussed ^^{internal box-and-glue representation} briefly in Chapter~11.
+For example, Baby Sally's exclamation begins as follows, after \TeX\ has
+digested it and put it into a box, assuming |\nonfrenchspacing|:
+\begintt
+.\tenrm \ (ligature ``)
+.\tenrm O
+.\tenrm h
+.\tenrm ,
+.\glue 3.33333 plus 2.08331 minus 0.88889
+.\tenrm o
+.\tenrm h
+.\tenrm !
+.\tenrm " (ligature '')
+.\glue 4.44444 plus 4.99997 minus 0.37036
+.\tenrm c
+.\tenrm r
+.\tenrm i
+.\tenrm e
+.\tenrm d
+.\glue 3.33333 plus 1.66666 minus 1.11111
+.\tenrm B
+.\tenrm a
+.\tenrm b
+.\kern-0.27779
+.\tenrm y
+.\glue 3.33333 plus 1.66666 minus 1.11111
+.\tenrm S
+.\tenrm a
+.\tenrm l
+.\tenrm l
+.\tenrm y
+.\kern-0.83334
+.\tenrm .
+.\glue 4.44444 plus 4.99997 minus 0.37036
+\endtt
+The normal ^{interword glue} in font |\tenrm| is $3.33333\pt$, plus
+$1.66666\pt$ of stretchability, minus $1.11111\pt$ of shrinkability.
+Notice that the interword |\glue| in this list stretches more, and shrinks
+less, after the punctuation marks; and the natural space is in fact larger
+at the end of each sentence. This example also shows several other things
+that \TeX\ does while it processes the sample line of text: It converts
+|``| and |''| into single characters, i.e., ^{ligatures}; and it inserts
+small ^{kerns} in two places to improve the spacing. A ^|\kern| is similar
+to glue, but it is not the same, because kerns cannot stretch or shrink;
+furthermore, \TeX\ will never break a line at a kern, unless that kern is
+immediately followed by glue.
+
+\ddanger You may be wondering what \TeX's rules for interword glue really
+are, exactly. For example, how did \TeX\ remember the effect of Baby Sally's
+exclamation point, when quotation marks intervened before the next space?
+The details are slightly tricky, but not incomprehensible. When \TeX\
+is processing a horizontal list of boxes and glue, it keeps track of a positive
+integer called the current ``^{space factor}.'' The space factor is normally
+1000, which means that the interword glue should not be modified. If the
+space factor $f$ is different from 1000, the interword glue is computed as
+follows: Take the normal space glue for the current font, and add the
+extra space if $f\ge2000$. \ (Each font specifies a normal space, normal
+stretch, normal shrink, and extra space; for example, these quantities are
+$3.33333\pt$, $1.66666\pt$, $1.11111\pt$, and $1.11111\pt$, respectively, in
+^|cmr10|. We'll discuss such font parameters in greater detail later.) \
+^^|\fontdimen|
+Then the stretch component is multiplied by $f/1000$, while the shrink
+component is multiplied by $1000/f$.
+
+\ddanger However, \TeX\ has two parameters ^|\spaceskip| and ^|\xspaceskip|
+that allow you to override the normal spacing of the current font. If
+$f\ge2000$ and if\/ |\xspaceskip| is nonzero, the |\xspaceskip| glue is
+used for an ^{interword space}. Otherwise if\/ |\spaceskip| is nonzero,
+the |\spaceskip| glue is used, with stretch and shrink components
+multiplied by $f/1000$ and $1000/f$. For example, the ^|\raggedright|
+macro of plain \TeX\ uses |\spaceskip| and |\xspaceskip| to suppress all
+stretching and shrinking of interword spaces.
+
+\ddanger The space factor $f$ is 1000 at the beginning of a horizontal list,
+and it is set to 1000 just after a non-character box or a math formula
+has been put onto the current horizontal list. You can say
+`^|\spacefactor||=|\<number>' to assign any particular value to the space
+factor; but ordinarily, $f$ gets set to a number other than 1000 only when
+a simple character box goes on the list. Each character has a ^{space factor
+code}, and when a character whose space factor code is $g$ enters the current
+list the normal procedure is simply to assign $g$ as the new space factor.
+However, if $g$ is zero, $f$ is not changed; and if $f<1000<g$, the
+space factor is set to~1000. \ (In other words, $f$ doesn't jump from a
+value less than~1000 to a value greater than~1000 in a single step.) \
+The maximum space factor is 32767 (which is much higher
+than anybody would ever want to use).
+
+\ddanger When ^|INITEX| creates a brand new \TeX, all characters have
+a space factor code of~1000, except that the uppercase letters `|A|'
+through~`|Z|' have code~999. \ (This slight difference is what makes
+punctuation act differently after an uppercase letter; do you see why?)
+\ Plain \TeX\ redefines a few of these codes using the ^|\sfcode|
+primitive, which is similar to |\catcode| (see Appendix~B\null); for
+example, the instructions
+\begintt
+\sfcode`)=0 \sfcode`.=3000
+\endtt
+make right parentheses ``transparent'' to the space factor, while tripling
+the stretchability after periods. The |\frenchspacing| operation resets
+|\sfcode`.| to 1000.
+
+\ddanger When ligatures are formed, or when a special character is
+specified via ^|\char|, the space factor code is computed from the
+individual characters that generated the ligature. For example,
+plain \TeX\ sets the space factor code for single-right-quote to zero, so
+that the effects of punctuation will be propagated. Two adjacent
+characters |''| combine to form a ligature that is in character position
+\oct{042}; but the space factor code of this double-right-quote ligature
+is never examined by \TeX, so plain \TeX\ does not assign any value to
+|\sfcode'042|.
+
+\ddangerexercise What are the space factors after each token
+of the Dick-and-Jane example?
+\answer 1000, except: 999 after |O|, |B|, |S|, |D|, and |J|; 1250 after the
+comma; 3000 after the exclamation point, the right-quote marks, and the
+periods. If a period had come just after the |B| (i.e., if the text had
+said `|B. Sally|'), the space factor after that period would have
+been~1000, not~3000.
+
+\danger Here's the way \TeX\ goes about ^{setting the glue} when an hbox
+is being wrapped up: The natural width, $x$, of the box contents is
+determined by adding up the widths of the boxes and kerns inside, together
+with the natural widths of all the glue inside. Furthermore the total
+amount of glue stretchability and shrinkability in the box is computed;
+let's say that there's a total of $y_0+y_1\,{\rm fil}+y_2\,{\rm
+fill}+y_3\,{\rm filll}$ available for stretching and $z_0+z_1\,{\rm
+fil}+z_2\,{\rm fill}+z_3\,{\rm filll}$ available for shrinking. Now the
+natural width~$x$ is compared to the desired width~$w$. If $x=w$,
+all glue gets its natural width.
+Otherwise the glue will be modified, by computing a ``^{glue set
+ratio}''~$r$ and a ``^{glue set order}''~$i$ in the following way: \
+(a)~If $x<w$, \TeX\ attempts to stretch the contents of the box; the glue
+order is the highest subscript~$i$ such that $y_i$ is nonzero, and the
+glue ratio is $r=(w-x)/y_i$. (If $y_0=y_1=y_2=y_3=0$, there's no
+stretchability; both $i$ and $r$ are set to zero.) \ (b)~If $x>w$, \TeX\
+attempts to shrink the contents of the box in a similar way; the glue
+order is the highest subscript~$i$ such that $z_i\ne0$, and the glue ratio
+is normally $r=(x-w)/z_i$. However, $r$ is set to 1.0 in the case $i=0$
+and $x-w>z_0$, because the maximum shrinkability must not be exceeded. \
+(c)~Finally, every glob of glue in the horizontal list being boxed is
+modified. Suppose the glue has natural width~$u$, stretchability~$y$, and
+shrinkability~$z$, where $y$~is a $j$th order infinity and $z$~is a $k$th
+order infinity. Then if $x<w$ (stretching), this glue takes the new width
+$u+ry$ if $j=i$; it keeps its natural width~$u$ if $j\ne i$. If $x>w$
+(shrinking), this glue takes the new width $u-rz$ if $k=i$; it keeps
+its natural width~$u$ if $k\ne i$. Notice that stretching or shrinking
+occurs only when the glue has the highest order of infinity that doesn't
+cancel out.
+
+\danger \TeX\ will construct an hbox that has a given width $w$ if you issue
+the command `\hbox{|\hbox to |\<dimen>|{|\<contents of box>|}|}', where
+$w$ is the value of the \<dimen>. For example, the ^|\line| macro discussed
+earlier in this chapter is simply an abbreviation for `|\hbox to\hsize|'.
+^^|to|^^|\hbox|
+\TeX\ also allows you to specify the exact amount of stretching or shrinking;
+the command `\hbox{|\hbox spread|\<dimen>|{|\<contents of box>|}|}'
+creates a box whose width~$w$ is a given amount more ^^|spread|
+than the natural width of the contents. For example, one of the boxes
+displayed earlier in this chapter was generated by
+\begintt
+\hbox spread 5pt{``Oh, oh!'' ... laughed.}
+\endtt
+In the simplest case, when you just want a box to have its natural width,
+you don't have to write `|\hbox spread 0pt|'; you can simply say
+`|\hbox{|\<contents of box>|}|'.
+
+\danger The ^{baseline} of a constructed hbox is the common baseline of the
+boxes inside. \ (More precisely, it's the common baseline that they would
+share if they weren't raised or lowered.) \ The height and depth of a
+constructed hbox are determined by the maximum distances by which the
+interior boxes reach above and below the baseline, respectively. The
+result of\/ |\hbox| never has negative height or negative depth, but the
+width can be negative.
+
+\dangerexercise Assume that
+ |\box1| is $1\pt$~high, $1\pt$~deep, and $1\pt$~wide;
+ |\box2| is $2\pt$~high, $2\pt$~deep, and $2\pt$~wide.
+A third box is formed by saying ^^|\setbox|
+\begintt
+\setbox3=\hbox to3pt{\hfil\lower3pt\box1\hskip-3pt plus3fil\box2}
+\endtt
+What are the height, depth, and width of\/ |\box3|? Describe the position
+of the reference points of boxes 1 and~2 with respect to the reference
+point of box~3.
+\answer |\box3| is $2\pt$ high, $4\pt$ deep, $3\pt$ wide.
+Starting at the reference point of\/ |\box3|, go right $.75\pt$ and down
+$3\pt$ to reach the reference point of\/ |\box1|; or go right $1\pt$
+to reach the reference point of\/ |\box2|.
+
+\danger The process of setting glue for vboxes is similar to that for
+hboxes; but before we study the |\vbox| operation, we need to discuss how
+\TeX\ stacks boxes up vertically so that their baselines tend to be
+a fixed distance apart. The boxes in a horizontal list often touch each
+other, but it's usually wrong to do this in a vertical list; imagine
+how awful a page would look if its lines of type were brought closer
+together whenever they didn't contain tall letters, or whenever they
+didn't contain any letters that descended below the baseline.
+
+\danger \TeX's solution to this problem involves three primitives called
+^|\baselineskip|, ^|\lineskip|, and ^|\lineskiplimit|. A format designer
+chooses values of these three quantities by writing
+\begindisplay
+|\baselineskip=|\<glue>\cr
+|\lineskip=|\<glue>\cr
+|\lineskiplimit=|\<dimen>\cr
+\enddisplay
+and the interpretation is essentially this: Whenever a box is added to a
+vertical list, \TeX\ inserts ``^{interline glue}'' intended to make
+the distance between the baseline of the new box and the baseline of
+the previous box exactly equal to the value of\/ |\baselineskip|.
+But if the interline glue calculated by this rule would cause the
+top edge of the new box to be closer than |\lineskiplimit| to the
+bottom edge of the previous box, then |\lineskip| is used as the
+interline glue. In other words, the distance between adjacent baselines
+will be the |\baselineskip| setting, unless that would bring the boxes
+too close together; the |\lineskip| glue will separate adjacent boxes
+in the latter case.
+
+\danger The rules for interline glue in the previous paragraph are
+carried out without regard to other kinds of glue that might be present;
+all vertical spacing due to explicit appearances of\/ |\vskip| and |\kern|
+acts independently of the interline glue. Thus, for example, a
+^|\smallskip| between two lines always makes their baselines further apart
+than usual, by the amount of a |\smallskip|; it does not
+affect the decision about whether |\lineskip| glue is used between
+those lines.
+
+\danger For example, let's suppose that |\baselineskip=12pt plus 2pt|,
+|\lineskip=|\penalty0|3pt minus 1pt|, and |\lineskiplimit=2pt|. \ (These values
+aren't particularly useful; they have simply been chosen to illustrate
+the rules.) \ Suppose further that a box whose depth is $3\pt$
+was most recently added to the current vertical list; we are about to
+add a new box whose height is~$h$. If $h=5\pt$, the interline glue
+will be $4\pt$~plus~$2\pt$, since this will make the baselines
+$12\pt$~plus~$2\pt$ apart when we add $h$ and the previous depth to
+the interline glue. But if $h=8\pt$, the interline
+glue will be $3\pt$~minus~$1\pt$, since |\lineskip| will be
+chosen in order to keep from violating the given |\lineskiplimit|
+when stretching and shrinking are ignored.
+
+\danger When you are typesetting a document that spans several pages,
+it's generally best to define the |\baselineskip| so that it cannot stretch
+or shrink, because this will give more uniformity to the pages. A small
+variation in the distance between baselines---say only half a point---can
+make a substantial difference in the appearance of the type, since it
+significantly affects the proportion of white to black. On the other hand,
+if you are preparing a one-page document, you might want to give the
+baselineskip some stretchability, so that \TeX\ will help you fit the copy
+on the page.
+
+\dangerexercise What settings of\/ |\baselineskip|, |\lineskip|, and
+|\lineskiplimit| will cause the interline glue to be a ``continuous''
+function of the next box height (i.e., the interline glue will never
+change a lot when the box height changes only a little)?
+\answer The stretch and shrink components of\/ |\baselineskip| and
+|\lineskip| should be equal, and the |\lineskiplimit| should
+equal the normal |\lineskip| spacing, to guarantee continuity.
+
+\danger A study of \TeX's ^{internal box-and-glue representation} should
+help to firm up some of these ideas. Here is an excerpt from the vertical
+list that \TeX\ constructed when it was typesetting this very paragraph:
+\begintt
+\glue 6.0 plus 2.0 minus 2.0
+\glue(\parskip) 0.0 plus 1.0
+\glue(\baselineskip) 1.25
+\hbox(7.5+1.93748)x312.0, glue set 0.80154, shifted 36.0 []
+\penalty 10000
+\glue(\baselineskip) 2.81252
+\hbox(6.25+1.93748)x312.0, glue set 0.5816, shifted 36.0 []
+\penalty 50
+\glue(\baselineskip) 2.81252
+\hbox(6.25+1.75)x348.0, glue set 116.70227fil []
+\penalty 10000
+\glue(\abovedisplayskip) 6.0 plus 3.0 minus 1.0
+\glue(\lineskip) 1.0
+\hbox(149.25+0.74998)x348.0 []
+\endtt
+{\showboxdepth0\showboxbreadth9999\batchmode\showlists\errorstopmode}%
+% The log file now contains lines like these; I copied them
+% into this MS! But I deleted an `insert' for the index...
+The first |\glue| in this example is the ^|\medskip| that precedes each
+dangerous-bend paragraph. Then comes the ^|\parskip| glue, which is
+automatically supplied before the first line of a new paragraph. Then
+comes some interline glue of $1.25\pt$; it was calculated to make
+a total of $11\pt$ when the height of the next box ($7.5\pt$)
+and the depth of the previous box were added. \ (The previous box is not
+shown---it's the bottom line of exercise~\chapno.\the\exno---but
+we can deduce that its depth was $2.25\pt$.) \ The |\hbox| that
+follows is the first line of this paragraph; it has been shifted right
+$36\pt$ because of ^{hanging indentation}. The glue set ratio
+for this hbox is 0.80154; i.e., the glue inside is stretched by 80.154\%\
+of its stretchability. \ (In the case of shrinking, the ratio following
+`^|glue set|' would have been preceded by `|- |'; hence we know that
+stretching is involved here.) \ \TeX\ has put `|[]|' at the end of each
+hbox line to indicate that there's something in the box that isn't shown.
+\ (The box contents would have been displayed completely, if
+^|\showboxdepth| had been set higher.) \ The ^|\penalty| indications are
+used to discourage bad breaks between pages, as we will see later. The
+third hbox has a glue ratio of 116.70227, which applies to
+first-order-infinite stretching (i.e., fil); this results from an |\hfil|
+that was implicitly inserted just before the displayed material, to fill
+up the third line of the paragraph. Finally the big hbox whose height is
+$149.25\pt$ causes |\lineskip| to be the interline glue. This large box
+contains the individual lines of typewriter type that are displayed; they
+have been packaged into a single box so that they cannot be split between
+pages. Careful study of this example will teach you a lot about \TeX's
+inner workings.
+
+\danger Exception: No interline glue is inserted before or after a rule
+box. You can also inhibit interline glue by saying ^|\nointerlineskip|
+between boxes.
+
+\ddanger \TeX's implementation of interline glue involves another primitive
+quantity called ^|\prevdepth|, which usually contains the depth of the
+most recent box on the current vertical list. However, |\prevdepth| is set
+to the sentinel value $-1000\pt$ at the beginning of a vertical list, or
+just after a rule box; this serves to suppress the next interline glue.
+The user can change the value of\/ |\prevdepth| at any time when building
+a vertical list; thus, for example, the |\nointerlineskip| macro of
+Appendix~B simply expands to `|\prevdepth=-1000pt|'.
+
+\ddanger Here are the exact rules by which \TeX\ calculates the interline
+glue between boxes: Assume that a new box of height~$h$ (not a rule box)
+is about to be appended to the bottom of the current vertical list, and
+let $\hbox{|\prevdepth|}=p$, $\hbox{|\lineskiplimit|}=l$,
+\hbox{$\hbox{|\baselineskip|}=(b$ plus~$y$ minus~$z)$}. If $p\le-1000\pt$,
+no interline glue is added. Otherwise if $b-p-h\ge l$, the interline glue
+`$(b-p-h)$ plus~$y$ minus~$z$' will be appended just above the new box.
+Otherwise the |\lineskip| glue will be appended. Finally, |\prevdepth| is
+set to the depth of the new box.
+
+\ddangerexercise Mr.~B. L. ^{User} had an application in which he wanted
+to put a number of boxes together in a vertical list, with no space
+between them. He didn't want to say |\nointerlineskip| after each box;
+so he decided to set |\baselineskip|, |\lineskip|, and |\lineskiplimit|
+all equal to |0pt|. Did this work?
+\answer Yes it did, but only because none of his boxes had a negative
+height or depth. He would have been safer if he had set
+|\baselineskip=-1000pt|, |\lineskip=0pt|, and
+|\lineskiplimit=16383pt|. \ (Plain \TeX's ^|\offinterlineskip| macro does this.)
+
+\danger The vertical analog of\/ |\hbox| is ^|\vbox|, and \TeX\ will obey
+the commands `|\vbox to|\<dimen>' and `|\vbox spread|\<dimen>' in about
+the way you would expect, by analogy with the horizontal case. However,
+there's a slight complication because boxes have both height and depth
+in the vertical direction, while they have only width in the
+horizontal direction. The dimension in a |\vbox| command refers to
+the final height of the vbox, so that, for example, `|\vbox to 50pt{...}|'
+produces a box that is $50\pt$ high; this is appropriate because everything
+that can stretch or shrink inside a vbox appears in the part that
+contributes to the height, while the depth is unaffected by glue setting.
+
+\danger The depth of a constructed |\vbox| is best thought of as the depth
+^^{depth of box} ^^{height of box}
+of the bottom box inside. Thus, a vbox is conceptually built by taking
+a bunch of boxes and arranging them so that their reference
+points are lined up vertically; then the reference point of the lowest
+box is taken as the reference point of the whole, and the glue is set
+so that the final height has some desired value.
+
+\danger However, this description of vboxes glosses over some
+technicalities that come up when you consider unusual cases. For example,
+\TeX\ allows you to shift boxes in a vertical list to the right or to
+the left by saying
+`^|\moveright|\<dimen>\<box>' or `^|\moveleft|\<dimen>\<box>'; this is
+like the ability to ^|\raise| or ^|\lower| boxes in a horizontal list, and
+it implies that the reference points inside a vbox need not always lie in
+a vertical line. Furthermore, it is necessary to guard against boxes that
+have too much depth, lest they extend too far into the bottom margin of a
+page; and later chapters will point out that vertical lists can contain
+other things like penalties and marks, in addition to boxes and glue.
+
+\ddanger Therefore, the actual rules for the depth of a constructed vbox
+are somewhat \TeX nical. Here they are: Given a vertical list that
+is being wrapped up via |\vbox|, the problem is to determine its
+natural depth. \ (1)~If the vertical list contains no boxes, the depth
+is zero. \ (2)~If there's at least one box, but if the final box is
+followed by kerning or glue, possibly with intervening penalties or
+other things, the depth is zero. \ (3)~If there's at least
+one box, and if the final box is not followed by kerning or glue, the
+depth is the depth of that box. \ (4)~However, if the depth computed
+by rules (1), (2), or~(3) exceeds ^|\boxmaxdepth|, the depth will be
+the current value of\/ |\boxmaxdepth|. \ (Plain \TeX\ sets |\boxmaxdepth|
+to the largest possible dimension; therefore rule~(4) won't apply unless
+you specify a smaller value. When rule~(4) does decrease the depth,
+\TeX\ adds the excess depth to the box's natural height, essentially
+moving the reference point down until the depth has been reduced to the
+stated maximum.)
+
+\danger The glue is set in a vbox just as in an hbox, by determining a
+^{glue set ratio} and a ^{glue set order}, based on the difference between
+the natural height~$x$ and the desired height~$w$, and based on the amounts
+of stretchability and shrinkability that happen to be present.
+
+\danger The width of a computed |\vbox| is the maximum distance by which
+an enclosed box extends to the right of the reference point, taking
+possible shifting into account. This width is always nonnegative.
+
+\dangerexercise Assume that |\box1| is $1\pt$ high, $1\pt$ deep, and
+$1\pt$ wide; |\box2| is $2\pt$ high, $2\pt$ deep, and $2\pt$ wide; the
+baselineskip, lineskip, and lineskiplimit are all zero; and the |\boxmaxdepth|
+is very large. A third box is formed by saying
+\begintt
+\setbox3=\vbox to3pt{\moveright3pt\box1\vskip-3pt plus3fil\box2}
+\endtt
+What are the height, depth, and width of\/ |\box3|? Describe the position
+of the reference points of boxes 1 and~2 with respect to the reference point
+of box~3.
+\answer The interline glue will be zero, and the natural height is
+$1+1-3+2=1\pt$ (because the depth of\/ |\box2| isn't included in the natural
+height); so the glue will ultimately become |\vskip-1pt| when it's set.
+Thus, |\box3| is $3\pt$ high, $2\pt$ deep, $4\pt$ wide. Its reference
+point coincides with that of\/ |\box2|; to get to the reference point
+of\/ |\box1| you go up $2\pt$ and right $3\pt$.
+
+\ddangerexercise Under the assumptions of the previous exercise, but
+with |\baselineskip=9pt minus3fil|, describe |\box4| after
+\begintt
+\setbox4=\vbox to4pt{\vss\box1\moveleft4pt\box2\vss}
+\endtt
+\answer The interline glue will be $6\pt$ minus $3\,{\rm fil}$; the final
+depth will be zero, since |\box2| is followed by glue; the natural
+height is $12\pt$; and the shrinkability is $5\,{\rm fil}$. So |\box4|
+will be $4\pt$ high, $0\pt$ deep, $1\pt$ wide, and it will contain
+five items: |\vskip|\penalty0\hbox{|-1.6pt|}, |\box1|, |\vskip1.2pt|,
+|\moveleft4pt\box2|, |\vskip-1.6pt|. Starting at the reference point of
+|\box4|, you get to the reference point of\/ |\box1| by going up $4.6\pt$,
+or to the reference point of\/ |\box2| by going up $.4\pt$ and left $4\pt$.
+\ (For example, you go up $4\pt$ to get to the upper left corner of
+|\box4|; then down $-1.6\pt$, i.e., up $1.6\pt$, to get to the upper left
+corner of\/ |\box1|; then down $1\pt$ to reach its reference point. This
+problem is clearly academic, since it's rather ridiculous to include
+infinite shrinkability in the baselineskip.)
+
+\ddangerexercise Solve the previous problem but with |\boxmaxdepth=-4pt|.
+\answer Now |\box4| will be $4\pt$ high, $-4\pt$ deep, $1\pt$ wide, and it
+will contain |\vskip|\penalty0\hbox{|-2.4pt|}, |\box1|, |\vskip-1.2pt|,
+|\moveleft4pt\box2|, |\vskip-2.4pt|. From the baseline of\/ |\box4|, go up
+exactly $5.4\pt$ to reach the baseline of\/ |\box1|, or exactly $3.6\pt$
+to reach the baseline of\/ |\box2|.
+
+\danger We have observed that |\vbox| combines a bunch of boxes into
+a larger box that has the same baseline as the bottom box inside.
+\TeX\ has another operation called ^|\vtop|, which gives you a box like |\vbox|
+but with the same baseline as the top box inside. For example,
+\begintt
+\hbox{Here are \vtop{\hbox{two lines}\hbox{of text.}}}
+\endtt
+produces
+\begindisplay
+Here are \vtop{\hbox{two lines}\hbox{of text.}}
+\enddisplay
+
+\ddanger You can say `|\vtop to|\<dimen>' and `|\vtop spread|\<dimen>'
+just as with |\vbox|, but you should realize what such a construction
+means. \TeX\ implements |\vtop| as follows: \ (1)~First a~vertical box
+is formed as if\/ |\vtop| had been |\vbox|, using all of the rules for
+|\vbox| as given above. \ (2)~The final height~$x$ is defined to
+be zero unless the very first item inside the new vbox is a box;
+in the latter case, $x$ is the height of that box. \ (3)~Let $h$
+and~$d$ be the height and depth of the vbox in step~(1). \TeX\ completes
+the |\vtop| by moving the reference point up or down, if necessary, so
+that the box has height~$x$ and depth $h+d-x$.
+
+\ddangerexercise Describe the empty boxes that you get from
+`|\vbox to|\<dimen>|{}|' and
+`|\vtop to|\<dimen>|{}|'. What are their heights, depths, and widths?
+\answer |\vbox to| $x$|{}| produces height $x$;
+|\vtop to| $x$|{}| produces depth $x$; the other dimensions are zero.
+\ (This holds even when $x$ is negative.)
+
+\ddangerexercise Define a macro |\nullbox#1#2#3| that produces a box
+whose height, depth, and width are given by the three parameters.
+The box should contain nothing that will show up in print.
+\answer There are several possibilities:
+\begintt
+\def\nullbox#1#2#3{\vbox to#1{\vss\hrule height-#2depth#2width#3}}
+\endtt
+works because the rule will be of zero thickness. Less tricky is
+\begintt
+\def\nullbox#1#2#3{\vbox to#1{\vss\vtop to#2{\vss\hbox to#3{}}}}
+\endtt
+Both of these are valid with negative height and/or depth, but they do
+not produce negative width. If the width might be negative, but not the
+height or depth, you can use, e.g.,
+|\def\nullbox#1#2#3{\hbox to#3{\hss\raise#1\null\lower#2\null}}|.
+It's impossible for |\hbox| to construct a box
+whose height or depth is negative; it's impossible for |\vbox| or
+|\vtop| to construct a box whose width is negative.\par
+However, there's actually a trivial solution to the general problem, based on
+features that will be discussed later:
+\begintt
+\def\nullbox#1#2#3{\setbox0=\null
+ \ht0=#1 \dp0=#2 \wd0=#3 \box0 }
+\endtt
+
+\danger The |\vbox| operation tends to produce boxes with large height and
+small depth, while |\vtop| tends to produce small height and large depth.
+If you're trying to make a vertical list out of big vboxes, however, you
+may not be satisfied with either |\vbox| or |\vtop|; you might well wish
+that a box had two reference points simultaneously, one for the top and
+one for the bottom. If such a dual-reference-point scheme were in use, one
+could define interline glue based on the distance between the lower
+reference point of one box and the upper reference point of its successor
+in a vertical list. But alas, \TeX\ gives you only one reference point per box.
+
+\danger There's a way out of this dilemma, using an important idea
+called a ``^{strut}.'' Plain \TeX\ defines ^|\strut| to be an invisible
+box of width zero that extends just enough above and below the baseline
+so that you would need no interline glue at all if every line contained
+a strut. \ (Baselines are $12\pt$ apart in plain \TeX; it turns out that
+|\strut| is a vertical rule, $8.5\pt$ high and $3.5\pt$ deep and
+$0\pt$ wide.) \ If you contrive to put a strut on the top line and
+another on the bottom line, inside your large vboxes, then it's possible to
+obtain the correct spacing in a larger assembly by simply letting the
+boxes butt together. For example, the ^|\footnote| macro in Appendix~B
+puts struts at the beginning and end of every footnote, so that the spacing
+will be right when several footnotes occur together at the bottom
+of some page. ^^{fitting boxes together}
+
+\danger If you understand boxes and glue, you're ready to learn
+the ^|\rlap| and ^|\llap| macros of plain \TeX; these names are abbreviations
+for ``right ^{overlap}'' and ``left overlap.'' Saying `|\rlap{|\<something>|}|'
+is like typesetting \<something> and then backing up as if you hadn't
+typeset anything. More precisely, `|\rlap{|\<something>|}|' creates a box
+of width zero, with `\<something>' appearing just at the right of that box
+(but not taking up any space). The |\llap| macro is similar, but it does
+the ^{backspacing} first; in other words, `|\llap{|\<something>|}|'
+creates a box of width zero, with `\<something>' extending just to the
+left of that box. Using typewriter type, for example, you can typeset
+`{\tt\rlap/=}' by saying either `|\rlap/=|' or `|/\llap=|'. It's possible
+to put text into the left margin using |\llap|, or into the right margin
+using |\rlap|, because \TeX\ does not insist that the contents of a box
+must be strictly confined within that box's boundaries. ^^{marginal notes}
+
+\danger The interesting thing about |\rlap| and |\llap| is that they can be
+done so simply with infinite glue. One way to define |\rlap| would be
+\begintt
+\def\rlap#1{{\setbox0=\hbox{#1}\copy0\kern-\wd0}}
+\endtt
+but there's no need to do such a lengthy computation. The actual definition
+in Appendix~B is much more elegant, namely,
+\begintt
+\def\rlap#1{\hbox to 0pt{#1\hss}}
+\endtt
+and it's worth pondering why this works. Suppose, for example, that you're
+doing |\rlap{g}| where the letter `g' is $5\pt$ wide. Since |\rlap| makes
+an hbox of width $0\pt$, the glue represented by ^|\hss| must shrink by
+$5\pt$. Well, that glue has $0\pt$ as its natural width, but it has
+infinite shrinkability, so it can easily shrink to $-5\pt$; and
+`|\hskip-5pt|' is exactly what |\rlap| wants in this case.
+
+\dangerexercise Guess the definition of\/ |\llap|, without peeking at
+Appendices A or~B.
+\answer |\def\llap#1{\hbox to 0pt{\hss#1}}|
+
+\dangerexercise (This is a sequel to exercise 12.\linexno,
+but it's trickier.) \ Describe the result of
+\begintt
+\line{\hfil A puzzle.\hfilneg}
+\endtt
+\answer You get `A' at the extreme left and `puzzle.\null' at the extreme right,
+because the space between words has the only stretchability that is finite;
+the infinite stretchability cancels out. \ (In this case, \TeX's rule
+about ^{infinite glue} differs from what you would get in the limit if the
+value of $1\,{\rm fil}$ were finite but getting larger and larger.
+The true limiting behavior would stretch the text `A~puzzle.\null' in the
+same way, but it would also move that text infinitely far away beyond the right
+edge of the page.)
+
+\endchapter
+
+There was things which he stretched,
+but mainly he told the truth.
+\author MARK ^{TWAIN}{^^{Clemens}}, {\sl Huckleberry Finn\/} (1884) % chap1
+
+\bigskip
+
+Every shape exists only because of the space around it.
+% Jeder Formwert lebt nur dank seiner Umgebung.
+$\ldots$ Hence there is a `right' position for every shape in every situation.
+% ... Daraus geht hervor, ein bestimmter Formvert in einer bestimmten Situation
+% einen bestimmten Platz verlangt, der ihm gem\"a\ss\ ist.
+If we succeed in finding that position, we have done our job.
+% Gelingt es uns, diese Pl\"atze zu finden,
+% so nennen wir die Arbeit vollendet.
+\author JAN ^{TSCHICHOLD}, {\sl Typographische Gestaltung\/} (1935) % p64
+% I've borrowed from the free translation in {\sl Asymmetric Typography}
+
+\eject
+ \beginchapter Chapter 13. Modes
+
+Just as people get into different moods, \TeX\ gets into different ``modes.'' \
+(Except that \TeX\ is more predictable than people.) \ There are six ^{modes}:
+
+\medskip
+\item\bull^{Vertical mode}. [Building the main vertical list, from which
+the pages of output are derived.]
+
+\smallskip\item\bull
+^{Internal vertical mode}. [Building a vertical list for a vbox.]
+
+\smallskip\item\bull
+^{Horizontal mode}. [Building a horizontal list for a paragraph.]
+
+\smallskip\item\bull
+^{Restricted horizontal mode}. [Building a horizontal list for an hbox.]
+
+\smallskip\item\bull
+^{Math mode}. [Building a mathematical formula to be placed in
+a horizontal list.]
+
+\smallskip\item\bull
+^{Display math mode}. [Building a mathematical formula to be placed
+on a line by itself, temporarily interrupting the current paragraph.]
+
+\medskip\noindent In simple situations, you don't need to be aware of what
+mode \TeX\ is in, because the computer just does the right thing. But
+when you get an error message that says `\thinspace|!|~|You| |can't| |do|
+|such-and-such| |in| |restricted| |horizontal| |mode|\thinspace', a
+knowledge of modes helps to explain why \TeX\ thinks you goofed.
+
+Basically \TeX\ is in one of the vertical modes when it is preparing a list of
+boxes and glue that will be placed vertically above and below one another on
+the page; it's in one of the horizontal modes when it is preparing a list
+of boxes and glue that will be strung out horizontally next to each other
+with baselines aligned; and it's in one of the math modes when it is
+reading a formula.
+
+A play-by-play account of a typical \TeX\ job should make the mode idea clear:
+At the beginning, \TeX\ is in vertical mode, ready to construct pages. If you
+specify glue or a box when \TeX\ is in vertical mode, the glue or the box gets
+placed on the current page below what has already been specified. For example,
+the ^|\vskip| instructions in the sample run we discussed in Chapter~6
+contributed vertical glue to the page; and the ^|\hrule| instructions
+contributed horizontal rules at the top and bottom of the story. The
+^|\centerline| commands also produced boxes that were included in the main
+vertical list; but those boxes required a bit more work than the rule boxes:
+\TeX\ was in vertical mode when it encountered
+`|\centerline{\bf A SHORT STORY}|', and it went temporarily into restricted
+horizontal mode while processing the words `|A SHORT STORY|'; then
+the digestive process returned to vertical mode, after setting the
+glue in the~|\centerline|~box.
+
+Continuing with the example of Chapter 6, \TeX\ switched into horizontal
+mode as soon as it read the `|O|' of `|Once upon a time|'. Horizontal mode
+is the mode for making ^{paragraphs}. The entire paragraph (lines 7 to~11
+of the |story| file) was input in horizontal mode; then the text was
+divided into output lines of the appropriate width, those lines were
+put in boxes and appended to the page (with appropriate interline glue between
+them), and \TeX\ was back in vertical mode. The `|M|' on line~12 started up
+horizontal mode again.
+
+When \TeX\ is in vertical mode or internal vertical mode,
+the first token of a new paragraph changes the mode to horizontal for the
+duration of a paragraph. In other words, things that do not have a vertical
+orientation cause the mode to switch automatically from vertical to
+horizontal. This occurs when you type any character, or ^|\char| or ^|\accent|
+or ^|\hskip| or |\|\] ^^{control space} or ^|\vrule| or math shift (|$|);
+\TeX\ inserts the current paragraph ^{indentation} and rereads the
+horizontal token as if it had occurred in horizontal mode.
+
+\danger You can also tell \TeX\ explicitly to go into horizontal mode,
+instead of relying on such implicit mode-switching, by saying `^|\indent|'
+or `^|\noindent|'. For example, if line~7 of the |story| file in Chapter~6
+had begun
+\begintt
+\indent Once upon a time, ...
+\endtt
+the same output would have been obtained, because `|\indent|' would have
+instructed \TeX\ to begin the paragraph. And if that line had begun with
+\begintt
+\noindent Once upon a time, ...
+\endtt
+the first paragraph of the story would not have been indented. The
+|\noindent| command simply tells \TeX\ to enter horizontal mode if the
+current mode is vertical or internal vertical; |\indent| is similar,
+but it also creates an empty box whose width is the current value of
+^|\parindent|, and it puts this empty box into the current horizontal list.
+Plain \TeX\ sets |\parindent=20pt|.
+If you say |\indent\indent|, you get double indentation; if you say
+|\noindent\noindent|, the second |\noindent| does nothing.
+
+\dangerexercise If you say `^|\hbox||{...}|' in horizontal mode, \TeX\ will
+construct the specified box and it will contribute the result to the
+current paragraph. Similarly, if you say `|\hbox{...}|' in vertical mode,
+\TeX\ will construct a box and contribute it to the current page.
+What can you do if you want to begin a paragraph with an |\hbox|?
+\answer Simply saying |\hbox{...}| won't work, since that box will just
+continue the previous vertical list without switching modes. You need
+to start the paragraph explicitly, and the straightforward way to
+do that is to say |\indent\hbox{...}|.
+But suppose you want to define
+a macro that expands to an hbox, where this macro is to be used in the
+midst of a paragraph as well as at the beginning; then you don't want
+to force users to type |\indent| before calling your macro at the
+beginning of a paragraph, nor do you want to say |\indent| in the
+macro itself (since that might insert unwanted indentations). One
+solution to this more general problem is to say
+`|\|\]^|\unskip||\hbox{...}|', since |\|\] makes the mode
+horizontal while |\unskip| removes the unwanted space. Plain \TeX\
+provides a ^|\leavevmode| macro, which solves this problem in what is
+probably the most efficient way: |\leavevmode| is an abbreviation for
+`|\unhbox\voidbox|', where |\voidbox| is a permanently empty box register.
+
+When handling simple manuscripts, \TeX\ spends almost all of its time in
+horizontal mode (making paragraphs), with brief excursions into vertical
+mode (between paragraphs). A paragraph is completed when you type ^|\par|
+or when your manuscript has a blank line, since a blank line is converted
+to |\par| by the reading rules of Chapter~8. A paragraph also ends when
+you type certain things that are incompatible with horizontal mode.
+For example, the command `|\vskip 1in|' on line~16 of Chapter~6's |story|
+file was enough to terminate the paragraph about `|...beautiful
+documents.|'; no |\par| was necessary, since |\vskip| introduced
+vertical glue that couldn't belong to the~paragraph.
+
+If a begin-math token (|$|) appears in
+horizontal mode, \TeX\ plunges into math mode and processes the formula up until
+the closing `|$|', then appends the text of this formula to the current
+paragraph and returns to horizontal mode. Thus, in the ``I wonder why?''\
+example of Chapter~12, \TeX\ went into math mode temporarily
+while processing |\ldots|, treating the dots as a formula.
+
+However, if two consecutive begin-math tokens appear in a paragraph (|$$|),
+\TeX\ interrupts the paragraph where it is, contributes the paragraph-so-far
+to the enclosing vertical list, then processes a math formula in display
+math mode, then contributes this formula to the enclosing list, then
+returns to horizontal mode for more of the paragraph. \ (The formula to be
+displayed should end with `|$$|'.) \ For example, suppose you type
+\begintt
+the number $$\pi \approx 3.1415926536$$ is important.
+\endtt
+\TeX\ goes into display math mode between the |$$|'s, and the output you
+get states that the number $$\pi \approx 3.1415926536$$ is important.
+^^|\pi|
+
+\smallskip
+\TeX\ ignores blank spaces and blank lines (or ^|\par| commands) when it's
+in vertical or internal vertical mode, so you need not worry that
+such things might change the mode or affect a printed document.
+A ^{control space} (|\|\]) will, however, be regarded as the
+beginning of a paragraph; the paragraph will start with a blank space
+after the indentation.
+
+\smallskip
+At the end of a \TeX\ manuscript it's usually best to finish everything off
+by typing `^|\bye|', which is plain \TeX's abbreviation for
+`|\vfill\eject\end|'. The `|\vfill|' gets \TeX\ into vertical
+mode and inserts enough space to fill up the last page; `|\eject|' outputs
+that last page; and `|\end|' sends the computer into its ^{endgame} routine.
+
+\danger \TeX\ gets into internal vertical mode when you ask it to construct
+something from a vertical list of boxes (using |\vbox| or |\vtop| or |\vcenter|
+or |\valign| or |\vadjust| or |\insert|). It gets into restricted
+horizontal mode when you ask it to construct something from a horizontal list
+of boxes (using |\hbox| or |\halign|). Box construction is discussed
+in Chapters 12 and~21. We will see later that there is very little difference
+between internal vertical mode and ordinary vertical mode, and very little
+difference between restricted horizontal mode and ordinary horizontal mode;
+but they aren't quite identical, because they have different goals.
+
+\danger Whenever \TeX\ looks at a token of input to decide what should be
+done next, the current mode has a potential influence on what that token
+means. For example, ^|\kern| specifies vertical spacing in vertical mode,
+but it specifies horizontal spacing in horizontal mode; a math shift
+character like `|$|' causes entry to math mode from horizontal mode, but
+it causes exit from math mode when it occurs in math mode; two consecutive
+math shifts (|$$|) appearing in horizontal mode will initiate display
+math mode, but in restricted horizontal mode they simply denote an empty
+math formula. \TeX\ uses the fact that some operations are inappropriate
+in certain modes to help you recover from errors that might have crept
+into your manuscript. Chapters 24 to~26 explain exactly what
+happens to every possible token in every possible mode.
+
+\danger \TeX\ often interrupts its work in one mode to do some task in
+another mode, after which the original mode is resumed again.
+For example, you can say `|\hbox{|' in any mode; when \TeX\ digests this,
+it suspends whatever else it was doing and enters restricted horizontal
+mode. The matching `|}|' will eventually cause the hbox to be completed,
+whereupon the postponed task will be taken up anew. In this sense \TeX\ can
+be in many modes simultaneously, but only the innermost
+mode influences the calculations at any time; the other modes have
+been pushed out of \TeX's consciousness.
+
+\goodbreak
+\danger One way to become familiar with \TeX's modes is to consider the
+following curious test file called |modes.tex|, which exercises all the
+modes at once:
+$$\halign{\hbox to\parindent{\hfil\sevenrm#\ \ }&#\hfil\cr
+1&|\tracingcommands=1|\cr\noalign{^^|\tracingcommands|}
+2&|\hbox{|\cr
+3&|$|\cr
+4&|\vbox{|\cr
+5&|\noindent$$|\cr
+6&|x\showlists|\cr
+7&|$$}$}\bye|\cr}$$
+The first line of ^|modes.tex| tells \TeX\ to log every command it
+receives; \TeX\ will produce diagnostic data whenever
+|\tracingcommands| is positive. Indeed, if you run \TeX\ on |modes.tex|
+you will get a |modes.log| file that includes the following information:
+\begintt
+{vertical mode: \hbox}
+{restricted horizontal mode: blank space }
+{math shift character $}
+{math mode: blank space }
+{\vbox}
+{internal vertical mode: blank space }
+{\noindent}
+{horizontal mode: math shift character $}
+{display math mode: blank space }
+{the letter x}
+\endtt
+The meaning is that \TeX\ first saw an |\hbox| token in vertical mode;
+this caused it to go ahead and read the `|{|' behind the scenes.
+Then \TeX\ entered restricted horizontal mode, and
+saw the blank space token that resulted from the end of line~2 in the
+file. Then it saw a math shift character token (still in restricted
+horizontal mode), which caused a shift to math mode; another
+blank space came through. Then |\vbox| inaugurated internal
+vertical mode, and |\noindent| instituted horizontal mode within that; two
+subsequent |$| signs led to display math mode. \ (Only the first |$| was
+shown by |\tracingcommands|, because that one caused \TeX\ to look ahead
+for another.)
+
+\danger The next thing in |modes.log| after the output above is
+`|{|^|\showlists||}|'. This is another handy diagnostic command that
+you can use to find out things that \TeX\ ordinarily keeps to itself; it
+causes \TeX\ to display the lists that are being worked on, in the current
+mode and in all enclosing modes where the work has been suspended:
+\begintt
+### display math mode entered at line 5
+\mathord
+.\fam1 x
+### internal vertical mode entered at line 4
+prevdepth ignored
+### math mode entered at line 3
+### restricted horizontal mode entered at line 2
+\glue 3.33333 plus 1.66666 minus 1.11111
+spacefactor 1000
+### vertical mode entered at line 0
+prevdepth ignored
+\endtt
+In this case the lists represent five levels of activity, all
+present at the end of line~6 of |modes.tex|.
+The current mode is shown first, namely, display math mode, which began
+on line~5. The current math list contains one ``^{mathord}''
+object, consisting of the letter~|x| in family~1. \ (Have patience and you
+will understand what that means, when you learn about \TeX's math formulas.) \
+Outside of display math mode comes internal vertical mode, to which \TeX\
+will return when the paragraph containing the
+displayed formula is complete. The vertical list ^^|prevdepth ignored|
+on that level is empty; `|prevdepth ignored|' means that |\prevdepth|
+has a value $\le-1000\pt$, so that the next interline glue will be omitted
+(cf.~Chapter~12). The math mode outside of this internal vertical mode has
+an empty list, likewise, but the restricted horizontal mode enclosing the math
+mode contains some glue. Finally, we see the main vertical mode that
+encloses everything; this mode was `|entered at line 0|', i.e., before
+the file |modes.tex| was input; nothing has been contributed
+so far to the vertical list on this outermost level.
+
+\dangerexercise Why is there glue in one of these lists but not in the
+others?
+\answer The output of\/ |\tracingcommands| shows that four blank space tokens
+were digested; these originated at the ends of lines 2,~3, 4, and~5. Only
+the first had any effect, since blank spaces are ignored in math formulas
+and in vertical modes.
+
+\dangerexercise After this output of\/ |\showlists|, the |modes.log| file
+contains further output from |\tracingcommands|. In fact, the next two
+lines of that file are
+\begintt
+{math shift character $}
+{horizontal mode: end-group character }}
+\endtt
+because the `|$$|' on line 7 finishes the displayed formula, and this
+resumes horizontal mode for the paragraph that was interrupted.
+What do you think are the next three lines of |modes.log|\thinspace?
+\answer The |end-group character| finishes the paragraph and the |\vbox|,
+and |\bye| stands for `|\par\vfill...|', so the next three commands are
+\begintt
+{math mode: math shift character $}
+{restricted horizontal mode: end-group character }}
+{vertical mode: \par}
+\endtt
+
+\dangerexercise Suppose \TeX\ has generated a document without ever
+leaving vertical mode. What can you say about that document?
+\answer It contains only mixtures of vertical glue and horizontal rules
+whose reference points appear at the left of the page; there's no text.
+
+\ddangerexercise Some of \TeX's modes cannot immediately enclose other modes;
+for example, display math mode is never directly enclosed by horizontal
+mode, even though displays occur within paragraphs, because an interrupted
+paragraph-so-far of horizontal mode is always completed and
+removed from \TeX's memory before the processing of a displayed formula
+begins. Give a complete characterization of all pairs of consecutive
+modes that can occur in the output of\/ |\showlists|.
+\answer Vertical mode can occur only as the outermost mode; horizontal
+mode and display math mode can occur only when immediately enclosed by
+vertical or internal vertical mode; ordinary math mode cannot be
+immediately enclosed by vertical or internal vertical mode; all other
+cases are possible.
+
+\endchapter
+
+Every mode of life has its conveniences.
+\author SAMUEL ^{JOHNSON}, {\sl The Idler\/} (1758)
+
+\bigskip
+
+[Hindu musicians] have eighty-four modes,
+of which thirty-six are in general use,
+and each of which, it appears, has a peculiar expression,
+and the power of moving some particular sentiment or affection.
+\author MOUNTSTUART ^{ELPHINSTONE}, {\sl History of India\/} (1841)
+% III.vii.I.297
+
+\eject
+ \beginchapter Chapter 14. How \TeX\ Breaks\\Paragraphs into\\Lines
+
+One of a typesetting system's chief duties is to take a long sequence of words
+and to break it up into individual lines of the appropriate size.
+For example, every paragraph of this manual has been broken into lines
+that are 29~picas wide, but the author didn't have to worry about such
+details when he composed the manuscript. \TeX\ chooses breakpoints
+in an interesting way that considers each paragraph in its entirety; the
+closing words of a paragraph can actually influence the appearance of the
+first line. As a result, the spacing between words is as uniform as
+possible, and the computer is able to reduce the number of times that
+words must be hyphenated or formulas must be split between lines.
+^^{H\&J, see hyphenation, line breaking, setting glue}
+^^{justification, see setting glue, line breaking}
+^^{quad left, see flush left}
+^^{quad right, see flush right}
+^^{quad middle, see :break}
+
+The experiments of Chapter 6 have already illustrated the general ideas:
+We discussed the notion of ``badness,'' and we ran into ``overfull'' and
+``underfull'' boxes in difficult situations. We also observed that different
+settings of \TeX's ^|\tolerance| parameter
+will produce different effects; a higher tolerance
+means that wider spaces are acceptable.
+
+\TeX\ will find the absolutely best way to typeset any given paragraph,
+according to its ideas of minimum badness. But such ``badness'' doesn't
+account for everything, and if you rely entirely on
+an automatic scheme you will occasionally encounter line breaks that are
+not really the best on psychological grounds; this is inevitable, because
+computers don't understand things the way people~do (at least not yet).
+Therefore you'll sometimes want to tell the machine that certain places
+are not good breakpoints. Conversely, you will sometimes want to force a
+break at a particular spot. \TeX\ provides a convenient way to avoid
+psychologically bad breaks, so that you will be able to obtain results of
+the finest quality by simply giving a few hints to the machine.
+
+``^{Ties}''---denoted by `|~|' in plain \TeX---are the key to
+successful line breaking. ^^{auxiliary space, see tie} ^^{tilde}
+Once you learn how to insert them, you will have graduated from the ranks
+of ordinary \TeX nical typists to the select group of Distinguished
+\TeX nicians. And it's really not difficult to train yourself to
+insert occasional ties, almost without thinking, as you type a manuscript.
+^^{line breaks, avoiding} ^^{breaks, avoiding bad}
+
+When you type |~| it's the same as typing a space, except that \TeX\
+won't break a line at this space. Furthermore, you shouldn't leave
+any blanks next to the |~|, since they will count as additional spaces.
+If you put |~| at the very end of a line in your input file, you'll get a
+wider space than you want, because the \<return> that follows the |~|
+produces an extra space.
+
+We have already observed in Chapter~12 that it's generally a good idea to
+type |~| after an abbreviation that does not come at the end of a sentence.
+Ties also belong in several other places:
+
+\smallskip
+\item\bull In references to named parts of a document:
+$$\halign{#\hfil&\hskip 80pt#\hfil\cr
+|Chapter~12|&|Theorem~1.2|\cr
+|Appendix~A|&|Table~\hbox{B-8}|\cr
+|Figure~3|&|Lemmas 5 and~6|\cr}$$
+
+\noindent(No |~| appears after `|Lemmas|' in the final example, since there's
+no harm in having `5~and~6' at the beginning of a line. The use of\/ |\hbox|
+is explained below.)
+
+\smallbreak
+\item\bull Between a person's forenames and between multiple surnames:
+$$\halign{#\hfil&\hskip 40pt#\hfil\cr
+|Donald~E. Knuth|&|Luis~I. Trabb~Pardo|\cr
+|Bartel~Leendert van~der~Waerden|&|Charles~XII|\cr}$$
+
+^^{Knuth} ^^{Trabb Pardo} ^^{van der Waerden} ^^{Charles XII}
+\noindent
+Note that it is sometimes better to hyphenate a name than to break it
+between words; e.g., `Don-' and `ald~E.~Knuth' is more tolerable
+than `Donald' and `E.~Knuth'. The previous rule can be regarded as a
+special case of this one, since we may think of `Chapter~12' as a
+compound name; another example is `|register~X|'. Sometimes a name is
+so long that we dare not tie it all together, lest there be no way to
+break the line: ^^{Vall\'ee Poussin}
+\begintt
+Charles Louis Xavier~Joseph de~la Vall\'ee~Poussin.
+\endtt
+
+\item\bull Between math symbols in apposition with nouns:
+$$\halign{#\hfil\cr
+|dimension~$d$ width~$w$ function~$f(x)$|\cr
+|string~$s$ of length~$l$|\cr}$$
+
+\noindent However, the last example should be compared with
+\begintt
+string~$s$ of length $l$~or more.
+\endtt
+
+\item\bull Between symbols in series:
+$$\halign{#\hfil\cr
+|1,~2, or~3|\cr
+|$a$,~$b$, and~$c$.|\cr
+|1,~2, \dots,~$n$.|\cr}$$
+
+\item\bull When a symbol is a tightly bound object of a preposition:
+$$\halign{#\hfil\cr
+|of~$x$|\cr
+|from 0 to~1|\cr
+|increase $z$ by~1|\cr
+|in common with~$m$.|\cr}$$
+
+\noindent The rule does not, however, apply to compound objects:
+\begintt
+of $u$~and~$v$.
+\endtt
+
+\item\bull When mathematical phrases are rendered in words:
+$$\halign{#\hfil&\hskip20pt#\hfil&\hskip20pt#\hfil\cr
+|equals~$n$|&|less than~$\epsilon$|&|(given~$X$)|\cr
+|mod~2|&|modulo~$p^e$|&|for all large~$n$|\cr}$$
+
+\noindent Compare `|is~15|' with `|is 15~times the height|'.
+
+\medbreak
+\item\bull When cases are being enumerated within a paragraph:
+^^{enumerated cases within a paragraph}
+$$\halign{#\hfil\cr
+|(b)~Show that $f(x)$ is (1)~continuous; (2)~bounded.|\cr}$$
+
+\noindent It would be nice to boil all of these rules down to one or two
+simple principles, and~it would be even nicer if the rules could be
+automated so that keyboarding could be done without them; but subtle
+semantic considerations seem to be involved. Therefore it's best to use
+your own judgment with respect to ties. The computer needs your help.
+
+A tie keeps \TeX\ from breaking at a space, but sometimes you want to
+prevent the machine from breaking at a ^{hyphen} or a ^{dash}. This can be
+done by using ^|\hbox|, because \TeX\ will not split up the contents of a
+box; boxes are indecomposable units, once they have been constructed. We
+have already illustrated this principle in the `|Table~\hbox{B-8}|'
+example considered earlier. Another example occurs when you are typing
+the page numbers in a ^{bibliographic reference}: It doesn't look good to
+put \hbox{`22.'} on a line by itself, so you can type `|\hbox{13--22}.|'
+to prohibit breaking `\hbox{13--22}.' On the other hand, \TeX\ doesn't
+often choose line breaks at hyphens, so you needn't bother to insert
+|\hbox| commands unless you need to correct a bad break that \TeX\ has
+already made on a previous run.
+
+\exercise Here are some phrases culled from previous chapters of this
+manual. How do you think the author typed them?
+\begindisplay
+(cf.~Chapter~12).\cr
+Chapters 12 and~21.\cr
+line~16 of Chapter~6's {\tt story}\cr
+lines 7 to~11\cr
+lines 2,~3, 4, and~5.\cr
+(2)~a big black bar\cr
+All 256~characters are initially of category~12,\cr
+letter~{\tt x} in family~1.\cr
+the factor~$f$, where $n$~is 1000~times~$f$.\cr
+\enddisplay
+\answer|(cf.~Chapter~12).|\parbreak
+|Chapters 12 and~21.|\parbreak
+|line~16 of Chapter~6's {\tt story}|\parbreak
+|lines 7 to~11|\parbreak
+|lines 2,~3, 4, and~5.|\parbreak
+|(2)~a big black bar|\parbreak
+|All 256~characters are initially of category~12,|\parbreak
+|letter~{\tt x} in family~1.|\parbreak
+|the factor~$f$, where $n$~is 1000~times~$f$.|
+
+\exercise How would you type the phrase `for all $n$ greater than
+$n_0$'\thinspace?
+\answer `|for all $n$~greater than~$n_0$|' avoids distracting breaks.
+
+\exercise And how would you type `exercise 4.3.2--15'\thinspace?
+\answer `|exercise \hbox{4.3.2--15}|' guarantees that there is no break
+after the ^{en-dash}. But this precaution is rarely necessary, so
+`|exercise 4.3.2--15|' is an acceptable answer. No |~| is needed;
+`\hbox{4.3.2--15}' is so long that it causes no offense
+at the beginning of a line.
+
+\exercise Why is it better to type `|Chapter~12|' than to type
+`|\hbox{Chapter 12}|'\thinspace?
+\answer The space you get from |~| will stretch or shrink with the
+other spaces in the same line, but the space inside an hbox has
+a fixed width since that glue has already been set once and for all.
+Furthermore the first alternative permits the word Chap-\break
+ter to be ^{hyphenate}d.
+
+\dangerexercise \TeX\ will sometimes break a math formula after an
+equals sign. How can you stop the computer from breaking the formula
+`$x=0$'\thinspace?
+\answer `|\hbox{$x=0$}|' is unbreakable, and we will see later that
+`|${x=0}$|' cannot be broken. Both of these solutions set the glue
+surrounding the equals sign to some fixed value, but such glue normally
+wants to stretch; furthermore, the |\hbox| solution might include undesirable
+blank space at the beginning or end of a line, if\/ ^|\mathsurround| is
+nonzero. A third solution `|$x=\nobreak0$|' avoids both defects.
+
+\ddangerexercise Explain how you could instruct \TeX\ not to make any
+breaks after explicit hyphens and dashes. \ (This is useful in
+lengthy ^{bibliographies}.)
+\answer |\exhyphenpenalty=10000| prohibits all such breaks, according
+to the rules found later in this chapter. Similarly, |\hyphenpenalty=10000|
+prevents breaks after implicit (discretionary) hyphens.
+
+Sometimes you want to permit a line break after a `/' just as if it were
+a hyphen. For this purpose plain \TeX\ allows you to say `^|\slash|';
+for example, `|input\slash output|' produces `input\slash output' with
+an optional break.
+
+{\hbadness=10000
+If you want to force \TeX\ to break between lines at a certain point in
+^^{line breaks, forcing} ^^{breaks, forcing good}
+the middle of a paragraph, just say `^|\break|'. However, that might cause the
+line to be really ^^{underfull}spaced out.\break
+If you want \TeX\ to fill up the right-hand part of a line
+with blank space just before a forced line break,\hfil\break
+without indenting the next line, say `|\hfil\break|'.\par}
+
+\danger You may have several consecutive lines of input
+for which you want the output to appear line-for-line in the same way.
+One solution is to type `|\par|' at the end of each input line; but that's
+somewhat of a nuisance, so plain \TeX\ provides the abbreviation
+`^|\obeylines|', which causes each end-of-line in the input to be
+like |\par|. After you say |\obeylines| you will get one line of
+output per line of input, unless an input line ends with `|%|' or
+unless it is so long that it must be broken. For example, you
+probably want to use |\obeylines| if you are typesetting a ^{poem}.
+Be sure to enclose |\obeylines| in a group, unless you want this
+``poetry mode'' to continue to the end of your document.
+\begintt
+{\obeylines\smallskip
+Roses are red,
+\quad Violets are blue;
+Rhymes can be typeset
+\quad With boxes and glue.
+\smallskip}
+\endtt
+
+\dangerexercise Explain the uses of\/ ^|\quad| in this poem. What would
+have happened if `|\quad|' had been replaced by `^|\indent|' in both places?
+\answer The second and fourth lines are indented by an additional ``quad''
+of space, i.e., by one extra em in the current type style.
+\ (The control sequence |\quad| does an ^|\hskip|; when \TeX\ is in
+vertical mode, |\hskip| begins a new paragraph and puts glue after the
+indentation.) \ If\/ |\indent| had been used instead, those lines wouldn't
+have been indented any more than the first and third, because |\indent| is
+implicit at the beginning of every paragraph. Double indentation on the
+second and fourth lines could have been achieved by `|\indent\indent|'.
+
+Roughly speaking, \TeX\ breaks paragraphs into lines in the following way:
+Breakpoints are inserted between words or after hyphens so as to produce lines
+whose badnesses do not exceed the current ^|\tolerance|. If there's no
+way to insert such breakpoints, an ^{overfull box} is set. Otherwise the
+breakpoints are chosen so that the paragraph is mathematically optimal, i.e.,
+best possible, in
+the sense that it has no more ``^{demerits}'' than you could obtain by any
+other sequence of breakpoints. Demerits are based on the badnesses of
+individual lines and on the existence of such things as consecutive lines
+that end with hyphens, or tight lines that occur next to loose ones.
+
+\danger But the informal description of line breaking in the previous
+paragraph is an oversimplification of what really happens. The remainder
+of this chapter explains the details precisely, for people who want to
+apply \TeX\ in nonstandard ways. \TeX's line-breaking algorithm
+has proved to be general enough to handle a surprising variety of
+different applications; this, in fact, is probably the most interesting
+aspect of the whole \TeX\ system. However, every paragraph from now on
+until the end of the chapter is prefaced by at least one dangerous bend
+sign, so you may want to learn the following material in easy stages
+instead of all at once.
+
+\ninepoint
+\danger Before the lines have been broken, a paragraph inside of \TeX\
+is actually a {\sl ^{horizontal list}}, i.e., a sequence of items that
+\TeX\ has gathered while in horizontal mode. We have been saying
+informally that a horizontal list consists of boxes and glue; the truth
+is that boxes and glue aren't the whole story. Each item in a horizontal
+list is one of the following types of things:\enddanger
+
+\smallskip
+\item\bull a box (a character or ligature or rule or hbox or vbox);
+
+\item\bull a ^{discretionary break} (to be explained momentarily);
+^^{break, discretionary}
+
+\item\bull a ``^{whatsit}'' (something special to be explained later);
+
+\item\bull vertical material (from ^|\mark| or ^|\vadjust| or ^|\insert|);
+
+\item\bull a glob of ^{glue} (or ^|\leaders|, as we will see later);
+
+\item\bull a ^{kern} (something like glue that doesn't stretch or shrink);
+
+\item\bull a ^{penalty} (representing the undesirability of breaking here);
+
+\item\bull ``^{math-on}'' (beginning a formula) or ``^{math-off}'' (ending a
+ formula).
+
+\smallskip\noindent
+The last four types (glue, kern, penalty, and math items)
+are called {\sl ^{discardable}}, since they
+may change or disappear at a line break; the first four types are
+called non-discardable, since they always remain intact. Many of the
+things that can appear in horizontal lists have not been touched on yet
+in this manual, but it isn't necessary to understand them in order to
+understand line breaking. Sooner or later you'll learn how each of the
+gismos listed above can infiltrate a horizontal list; and if you want to
+get a thorough understanding of \TeX's internal processes, you can always
+use ^|\showlists| with various features of the language, in
+order to see exactly what \TeX\ is doing.
+
+\danger A discretionary break consists of three sequences of characters
+called the {\sl pre-break}, {\sl post-break}, and {\sl no-break\/}
+texts. The idea is that if a line break occurs here, the ^{pre-break text}
+will appear at the end of the current line and the ^{post-break text} will
+occur at the beginning of the next line; but if no break occurs, the
+^{no-break text} will appear in the current line. Users can specify
+^^|\discretionary|
+discretionary breaks in complete generality by writing
+\begindisplay
+|\discretionary{|\<pre-break text>|}{|\<post-break text>|}{|\<no-break text>|}|
+\enddisplay
+where the three texts consist entirely of characters, boxes, and kerns.
+For example, \TeX\ can hyphenate the word
+`difficult' between the f's, even though this requires breaking the
+`ffi' ligature into `f-' followed by an `fi' ligature, if the horizontal
+list contains
+\begintt
+di\discretionary{f-}{fi}{ffi}cult.
+\endtt
+Fortunately you need not type such a mess yourself; \TeX's hyphenation algorithm
+works behind the scenes, taking ^{ligatures} apart and putting them
+into discretionary breaks when necessary.
+
+\danger The most common case of a discretionary break is a simple
+discretionary hyphen
+\begintt
+\discretionary{-}{}{}
+\endtt
+for which \TeX\ accepts the abbreviation `^|\-|'. The next most common case is
+\begintt
+\discretionary{}{}{}
+\endtt
+(an ``^{empty discretionary}''), which \TeX\ automatically inserts after
+`|-|' and after every ligature that ends with `|-|'. In the case of plain
+\TeX, empty discretionaries are therefore inserted after ^{hyphens} and
+^{dashes}. \ (Each font has an associated ^|\hyphenchar|, which we can
+assume for simplicity is equal to `|-|'.)
+
+\danger When \TeX\ ^{hyphenates} words, it simply inserts discretionary
+breaks into the horizontal list. For example, the words `|discretionary
+hyphens|' are transformed into the equivalent of
+\begintt
+dis\-cre\-tionary hy\-phens
+\endtt
+if hyphenation becomes necessary. But \TeX\ doesn't apply its hyphenation
+algorithm to any word that already contains a discretionary break;
+therefore you can use explicit discretionaries to override \TeX's automatic
+method, in an emergency.
+
+\dangerexercise Before 1998, some ^{German} words changed their spelling
+when split between lines. For example, `backen' became `bak-ken'
+and `Bettuch' sometimes became `Bett-tuch'.
+How can you instruct \TeX\ to produce such effects?
+\answer |ba\ck/en| and |Be\ttt/uch|, where the macros |\ck/| and |\ttt/|
+are defined by
+\begintt
+\def\ck/{\discretionary{k-}{k}{ck}}
+\def\ttt/{tt\discretionary{-}{t}{}}
+\endtt
+The English word `eighteen' might deserve similar treatment.
+\TeX's hyphenation algorithm will not make such spelling changes automatically.
+
+\danger In order to save time, \TeX\ tries first to break a paragraph
+into lines without inserting any discretionary hyphens. This first pass
+will succeed if a sequence of breakpoints is found for which none
+of the resulting lines has a badness exceeding the current value of
+^|\pretolerance|. If the first pass fails, the method of Appendix~H is
+used to hyphenate each word of the paragraph by inserting discretionary
+breaks into the horizontal list, and a second attempt is
+made using ^|\tolerance| instead of\/ |\pretolerance|. When the lines
+are fairly wide, as they are in this manual, experiments show that
+the first pass succeeds more than 90\% of the time, and that fewer than
+2~words per paragraph need to be subjected to the hyphenation algorithm,
+on the average. But when the lines are very narrow the
+first pass usually fails rather quickly. Plain \TeX\ sets |\pretolerance=100|
+and |\tolerance=200| as the default values. If you make |\pretolerance=10000|,
+the first pass will essentially always succeed, so hyphenations will not
+be tried (and the spacing may be terrible); on the other hand if you make
+|\pretolerance=-1|, \TeX\ will omit the first pass and will try to
+hyphenate immediately.
+
+\danger Line breaks can occur only in certain places within a horizontal
+list. Roughly speaking, they occur between words and after hyphens, but in
+actuality they are permitted in the following five cases:\enddanger
+
+\smallskip
+\item{a)} at glue, provided that this glue is immediately preceded by a
+non-discardable item, and that it is not part of a math formula (i.e., not
+between math-on and math-off). A break ``at glue'' occurs at the left edge
+of the glue space.
+
+\smallskip
+\item{b)} at a kern, provided that this kern is immediately followed by
+glue, and that it is not part of a math formula.
+
+\smallskip
+\item{c)} at a math-off that is immediately followed by glue.
+
+\smallskip
+\item{d)} at a penalty (which might have been inserted automatically in a
+formula).
+
+\smallskip
+\item{e)} at a discretionary break.
+
+\smallskip\noindent
+Notice that if two globs of glue occur next to each other, the second one
+will never be selected as a breakpoint, since it is preceded by glue (which
+is discardable).
+
+\danger Each potential breakpoint has an associated ``penalty,'' which
+represents the ``aesthetic cost'' of breaking at that place. In cases
+(a), (b),~(c), the penalty is zero; in case~(d) an explicit penalty
+has been specified; and in case~(e) the penalty is the current value of
+^|\hyphenpenalty| if the pre-break text is nonempty, or the current value of
+^|\exhyphenpenalty| if the pre-break text is empty.
+Plain \TeX\ sets |\hyphenpenalty=50| and |\exhyphenpenalty=50|.
+
+\danger For example, if you say `^|\penalty| |100|' at some point in a
+paragraph, that position will be a legitimate place to break between
+lines, but a penalty of 100 will be charged. If you say `\hbox{|\penalty-100|}'
+you are telling \TeX\ that this is a rather good place to break, because
+a negative penalty is really a ``^{bonus}''; a line that ends with a bonus
+might even have ``merits'' (negative demerits).
+
+\danger Any penalty that is 10000 or more is considered to be so large
+^^{infinite penalty} that \TeX\ will never break there. At the other
+extreme, any penalty that is $-10000$ or less is considered to be so small
+that \TeX\ will always break there. The ^|\nobreak| macro of plain \TeX\
+is simply an abbreviation for `|\penalty10000|', because this prohibits a
+line break. A tie in plain \TeX\ is equivalent to `|\nobreak\|\]';
+there will be no break at the glue represented by |\|\] in this
+case, because glue is never a legal breakpoint when it is preceded by a
+discardable item like a penalty.
+
+\dangerexercise Guess how the ^|\break| macro is defined in plain \TeX.
+\answer |\def\break{\penalty-10000 }|
+
+\dangerexercise What happens if you say |\nobreak\break| or
+|\break\nobreak|?
+\answer You get a forced break as if\/ |\nobreak| were not present, because
+|\break| cannot be cancelled by another penalty. In general if you
+have two penalties in a row, their combined effect is the same as a single
+penalty whose value is the minimum of the two original values, unless
+both of those values force breaks. \ (You get two breaks from
+|\break\break|; the second one creates an empty line.)
+
+\danger When a line break actually does occur, \TeX\ removes all discardable
+items that follow the break, until coming to something non-discardable,
+or until coming to another chosen breakpoint. For example, a sequence of
+glue and penalty items will vanish as a unit, if no boxes intervene,
+unless the optimum breakpoint sequence includes one or more of the penalties.
+Math-on and math-off items act essentially as kerns that contribute the spacing
+specified by ^|\mathsurround|; such spacing will disappear into the line
+break if a formula comes at the very end or the very beginning of a line,
+because of the way the rules have been formulated above.
+
+\ddanger The ^{badness} of a line is an integer that is approximately 100
+times the cube of the ratio by which the glue inside the line must stretch
+or shrink to make an hbox of the required size. For example, if the line
+has a total shrinkability of 10 points, and if the glue is being
+compressed by a total of 9 points, the badness is computed to be~73 (since
+$100\times(9/10)^3=72.9$); similarly, a line that stretches by twice its
+total stretchability has a badness of 800. But if the badness obtained by
+this method turns out to be more than 10000, the value 10000 is used. \
+$\bigl($See the discussion of ``^{glue set ratio}''~$r$ and ``^{glue set
+order}''~$i$ in Chapter~12; if $i\ne0$, there is infinite stretchability or
+shrinkability, so the badness is zero, otherwise the badness is
+approximately $\min(100r^3,10000)$.$\bigr)$ \ Overfull boxes are
+considered to be infinitely bad; they are avoided whenever possible.
+^^{infinite badness}
+
+\ddanger A line whose badness is 13 or more has a glue set ratio exceeding
+50\%. We call such a line {\sl ^{tight}\/} if its glue had to shrink,
+{\sl ^{loose}\/} if its glue had to stretch, and {\sl ^{very loose}\/}
+if it had to stretch so much that the badness is 100 or more. But if the
+badness is 12 or less we say that the line is {\sl ^{decent}}. Two
+adjacent lines are said to be {\sl {visually incompatible}\/} if their
+classifications are not adjacent, i.e., if a tight line is next to a
+loose or very loose line, or if a decent line is next to a very loose one.
+
+\ddanger \TeX\ rates each potential sequence of breakpoints by totalling
+up {\sl ^{demerits}\/} that are assessed to individual lines. The goal
+is to choose breakpoints that yield the fewest total demerits. Suppose that a
+line has badness~$b$, and suppose that the penalty~$p$ is associated with
+the breakpoint at the end of this line. As stated above, \TeX\ will not
+even consider such a line if $p\ge10000$, or if $b$~exceeds the current
+tolerance or pretolerance. Otherwise the demerits of such a line are
+defined by the formula
+\begindisplay
+$\displaystyle{d=\cases{
+ (l+b)^2+p^2,&if $0\le p<10000$;\cr
+ (l+b)^2-p^2,&if $-10000<p<0$;\cr
+ (l+b)^2,&if $p\le-10000$.\cr}}$
+\enddisplay
+Here $l$ is the current value of\/ ^|\linepenalty|, a parameter that can be
+increased if you want \TeX\ to try harder to keep all paragraphs to the
+minimum number of lines; plain \TeX\ sets |\linepenalty=10|. For example,
+a line with badness~20 ending at glue will have $(10+20)^2=900$ demerits,
+if $l=10$, since there's no penalty for a break at glue. Minimizing the
+total demerits of a paragraph is roughly the same as minimizing the
+sum of the squares of the badnesses and penalties; this usually means
+that the maximum badness of any individual line is also minimized, over
+all sequences of breakpoints.
+
+\ddangerexercise The formula for demerits has a strange discontinuity: It
+seems more reasonable at first to define $d=(l+b)^2-10000^2$, in the
+case $p\le-10000$. Can you account for this apparent discrepancy?
+\answer Breaks are forced when $p\le-10000$, so there's no point in
+subtracting a large constant whose effect on the total demerits is
+known {\sl a priori}, especially when that might cause arithmetic overflow.
+
+\ddanger Additional demerits are assessed based on pairs of adjacent lines.
+If two consecutive lines are visually incompatible, in the sense explained
+a minute ago, the current value of\/ ^|\adjdemerits| is added to~$d$. If two
+consecutive lines end with discretionary breaks, the ^|\doublehyphendemerits|
+are added. And if the second-last line of the entire paragraph ends with
+a discretionary, the ^|\finalhyphendemerits| are added. Plain \TeX\ sets
+up the values |\adjdemerits=10000|, |\doublehyphendemerits=10000|,
+and |\finalhyphendemerits=5000|. Demerits are in units of
+``badness squared,'' so the demerit-oriented parameters need to be rather
+large if they are to have much effect; but tolerances and
+penalties are given in the same units as badness.
+
+\ddanger If you set ^|\tracingparagraphs||=1|, your log file will contain a
+summary of \TeX's line-breaking calculations, so you can watch the tradeoffs
+that occur when parameters like |\linepenalty| and |\hyphenpenalty| and
+|\adjdemerits| are twiddled. The line-break data looks pretty scary at first,
+but you can learn to read it with a little practice; this, in fact, is the
+best way to get a solid understanding of line breaking. Here is the
+trace that results from the second paragraph of the |story| file in
+Chapter~6, when |\hsize=2.5in| and |\tolerance=1000|:
+\begindisplay
+|[]\tenrm Mr. Drofnats---or ``R. J.,'' as he pre-|\cr
+|@\discretionary via @@0 b=0 p=50 d=2600|\cr
+|@@1: line 1.2- t=2600 -> @@0|\cr
+|ferred to be called---was hap-pi-est when |\cr
+|@ via @@1 b=131 p=0 d=29881|\cr
+|@@2: line 2.0 t=32481 -> @@1|\cr
+|he|\cr
+|@ via @@1 b=25 p=0 d=1225|\cr
+|@@3: line 2.3 t=3825 -> @@1|\cr
+|was at work type-set-ting beau-ti-ful doc-|\cr
+|@\discretionary via @@2 b=1 p=50 d=12621|\cr
+|@\discretionary via @@3 b=291 p=50 d=103101|\cr
+|@@4: line 3.2- t=45102 -> @@2|\cr
+|u-|\cr
+|@\discretionary via @@3 b=44 p=50 d=15416|\cr
+|@@5: line 3.1- t=19241 -> @@3|\cr
+|ments.|\cr
+|@\par via @@4 b=0 p=-10000 d=5100|\cr
+|@\par via @@5 b=0 p=-10000 d=5100|\cr
+|@@6: line 4.2- t=24341 -> @@5|\cr
+\enddisplay
+Lines that begin with `|@@|' ^^{atsign atsign} represent {\sl^{feasible
+breakpoints}}, i.e., breakpoints that can be reached without any badness
+exceeding the tolerance. Feasible breakpoints are numbered consecutively,
+starting with |@@1|; the beginning of the paragraph is considered to be
+feasible too, and it is number |@@0|. Lines that begin with `|@|' but
+not `|@@|' are candidate ways to reach the feasible breakpoint that
+follows; \TeX\ will select only the best candidate, when there is a choice.
+Lines that do not begin with `|@|' indicate how far \TeX\ has gotten in the
+paragraph. Thus, for example, we find `|@@2: line 2.0 t=32481 -> @@1|'
+after `|...hap-pi-est when|' and before `|he|', so we know that feasible
+breakpoint~|@@2| occurs at the space between the words |when| and |he|.
+The notation `|line 2.0|' means that this feasible break comes at the end
+of line~2, and that this line will be very loose. \ (The suffixes
+|.0|, |.1|, |.2|, |.3| stand respectively for very loose, loose, decent,
+and tight.) \ A hyphen is suffixed to the line number if that line
+ends with a discretionary break, or if it is the final line of the
+paragraph; for example, `|line 1.2-|' is a decent line that was hyphenated.
+The notation `|t=32481|' means that the total demerits from the beginning
+of the paragraph to~|@@2| are 32481, and `|-> @@1|' means that the best
+way to get to |@@2| is to come from |@@1|. On the preceding line of trace
+data we see the calculations for a typeset line to this point from |@@1|:
+The badness is~131, the penalty is~0, hence there are 29881 demerits.
+Similarly, breakpoint |@@3| presents an alternative for the second line of
+the paragraph, obtained by breaking between `|he|' and `|was|'; this one
+makes the second line tight, and it has only 3825 demerits when the
+demerits of line~1 are added, so it appears that |@@3| will work much
+better than |@@2|. However, the next feasible breakpoint (|@@4|) occurs
+after `|doc-|', and the line from |@@2| to~|@@4| has only 12621 demerits,
+while the line from |@@3| to~|@@4| has a whopping 103101; therefore
+the best way to get from |@@0| to~|@@4| is via~|@@2|. If we regard
+demerits as distances, \TeX\ is finding the ``^{shortest paths}'' from
+|@@0| to each feasible breakpoint (using a variant of a well-known
+algorithm for shortest paths in an acyclic graph). Finally the end of
+the paragraph comes at breakpoint |@@6|, and the shortest path from
+|@@0| to~|@@6| represents the best sequence of breakpoints. Following
+the arrows back from~|@@6|, we deduce that~the best breaks in this
+particular paragraph go through |@@5|, |@@3|, and~|@@1|.
+
+\ddangerexercise Explain why there are 29881 demerits from |@@1| to |@@2|,
+and 12621 demerits from~|@@2| to~|@@4|.
+\answer $(10+131)^2+0^2+10000=29881$ and $(10+1)^2+50^2+10000=12621$.
+In both cases the ^|\adjdemerits| were added because the lines were
+visually incompatible (decent, then very loose, then decent); plain
+\TeX's values for ^|\linepenalty| and |\adjdemerits| were used.
+
+\ddanger If `|b=*|' ^^|*| appears in such trace data, it means that an
+infeasible breakpoint had to be chosen because there was no feasible
+way to keep total demerits small.
+
+\danger We still haven't discussed the special trick that allows the
+final line of a paragraph to be shorter than the others. Just before
+\TeX\ begins to choose breakpoints, it does two important things: \
+(1)~If the final item of the current horizontal list is glue,
+^^|\unskip|
+that glue is discarded. \ (The reason is that a blank space
+often gets into a token list just before ^|\par| or just before |$$|,
+and this blank space should not be part of the paragraph.) \ (2)~Three more
+items are put at the end of the current horizontal list: |\penalty10000|
+(which prohibits a line break); |\hskip\parfillskip| (which adds
+``^{finishing glue}'' to the paragraph); and |\penalty-10000| (which
+forces the final break). Plain \TeX\ sets ^|\parfillskip||=0pt plus1fil|,
+so that the last line of each paragraph will be filled with white space
+if necessary; but other settings of\/ |\parfillskip| are appropriate in
+special applications. For example, the present paragraph ends flush with
+the right margin, because it was typeset with |\parfillskip=0pt|;
+the author didn't have to rewrite any of the text in order to make this
+possible, since a long paragraph generally allows so much flexibility that
+a line break can be forced at almost any point. You can have some fun
+playing with paragraphs, because the algorithm for line breaking
+occasionally appears to be clairvoyant. Just write paragraphs
+that are long enough.\parfillskip=0pt
+% the \danger macro makes this \parfillskip local!
+
+\dangerexercise Ben ^{User} decided to say `|\hfilneg\par|' at the end of
+a paragraph, intending that the negative stretchability of\/ ^|\hfilneg|
+would cancel with the |\parfillskip| of plain \TeX\null. Why didn't his
+bright idea work? ^^{paragraph, ending}
+\answer Because \TeX\ discards a glue item that occurs just before
+|\par|. Ben should have said, e.g., `|\hfilneg\ \par|'.
+
+\dangerexercise How can you set |\parfillskip| so that the last line
+of a paragraph has exactly as much white space at the right as the
+first line has indentation at the left?\nobreak\hskip\parindent\hfilneg\
+\answer Just say |\parfillskip|\stretch|=|\stretch|\parindent|. Of course,
+\TeX\ will not be able to find appropriate line breaks unless each
+paragraph is sufficiently long or sufficiently lucky; but with an
+appropriate text, your output will be immaculately
+symmetrical.{\parfillskip=\parindent\par}
+
+\ddangerexercise Since \TeX\ reads an entire paragraph before it makes
+any decisions about line breaks, the computer's memory capacity might
+^^{capacity exceeded} be exceeded if you are typesetting the works of some
+^^{Joyce, James} ^{philosopher} or modernistic novelist who writes
+200-line paragraphs. Suggest a way to cope with such authors.
+\answer Assuming that the author is deceased and/or set in his or her
+ways, the remedy is to insert `|{\parfillskip=0pt\par\parskip=0pt\noindent}|'
+in random places, after each 50 lines or so of text. \ (Every space
+between words is usually a feasible breakpoint, when you get sufficiently
+far from the beginning of a paragraph.)
+
+\danger \TeX\ has two parameters called ^|\leftskip| and ^|\rightskip| that
+specify glue to be inserted at the left and right of every line in a
+paragraph; this glue is taken into account when badnesses and demerits are
+computed. Plain \TeX\ normally keeps |\leftskip| and |\rightskip| zero,
+but it has a `^|\narrower|' macro that increases both of their values by
+the current ^|\parindent|. You may want to use |\narrower| when ^{quoting}
+lengthy passages from a book.
+\begintt
+{\narrower\smallskip\noindent
+This paragraph will have narrower lines than
+the surrounding paragraphs do, because it
+uses the ``narrower'' feature of plain \TeX.
+The former margins will be restored after
+this group ends.\smallskip}
+\endtt
+(Try it.) \ The second `^|\smallskip|' in this example ends the paragraph.
+It's important to end the paragraph before ending the group, for otherwise
+the effect of\/ |\narrower| will disappear before \TeX\ begins to choose
+line breaks.
+
+\dangerexercise When an entire paragraph is typeset in ^{italic} or ^{slanted}
+type, it sometimes appears to be offset on the page with respect to
+other paragraphs. Explain how you could use |\leftskip| and |\rightskip|
+to shift all lines of a paragraph left by $1\pt$.
+\answer |{\leftskip=-1pt \rightskip=1pt| \<text> |\par}|\par
+\nobreak\medskip\noindent
+(This applies to a full paragraph; if you want to correct only
+isolated lines, you have to do it by hand.)
+
+\dangerexercise The ^|\centerline|, ^|\leftline|, ^|\rightline|, and ^|\line|
+macros of plain \TeX\ don't take |\leftskip| and |\rightskip| into
+account. How could you make them do so?
+\answer `|\def\line#1{\hbox to\hsize{\hskip\leftskip#1\hskip\rightskip}}|'
+is the only change needed. \ (Incidentally,
+^{displayed equations} don't take account of\/ |\leftskip| and |\rightskip|
+either; it's more difficult to change that, because so many variations
+are possible.)
+
+\ddanger If you suspect that ^|\raggedright| setting is accomplished by
+some appropriate manipulation of\/ |\rightskip|, you are correct. But some
+care is necessary. For example, a person can
+set |\rightskip=0pt plus1fil|, and every
+line will be filled with space at the right. But this isn't a particularly
+good way to make ragged-right margins, because the infinite stretchability
+will assign zero badness to lines that are very short.
+To do a decent job of ragged-right setting, the trick is to set
+|\rightskip| so that it will stretch enough to make line breaks possible,
+yet not too much, because short lines should be considered bad. Furthermore
+the spaces between words should be fixed so that they do not stretch
+or shrink. \ (See the definition of\/ |\raggedright| in Appendix~B\null.) \
+It would also be possible to allow a little variability in the interword
+glue, so that the right margin would not be quite so ragged but the
+paragraphs would still have an informal appearance.
+
+\danger \TeX\ looks at the parameters that affect
+line breaking only when it is breaking lines. For example, you shouldn't
+try to change the ^|\hyphenpenalty| in the middle of a paragraph,
+if you want \TeX\ to penalize the hyphens in one word more than it does in
+another word. The relevant values of
+|\hyphenpenalty|, |\rightskip|, |\hsize|, and so on,
+are the ones that are current at the end of the paragraph.
+On the other hand, the width of indentation that you get
+implicitly at the beginning of a paragraph or when you say `^|\indent|'
+is determined by the value of\/ ^|\parindent| at the time the
+indentation is contributed to the current horizontal list,
+not by its value at the end of the paragraph. Similarly, penalties
+that are inserted into math formulas within a paragraph are based
+on the values of\/ ^|\binoppenalty| and ^|\relpenalty| that are current
+at the end of each particular formula. Appendix~D contains an example
+that shows how to have both ragged-right and ragged-left margins within
+a single paragraph, without using |\leftskip| or |\rightskip|.
+
+\varunit=0.989pt % getting ready to make circular insert
+% \varunit=1.078pt was used with amr5: it had more letterspacing
+\setbox0=\vtop{\null
+\baselineskip6\varunit
+\parfillskip0pt
+\parshape 19
+-18.25\varunit 36.50\varunit
+-30.74\varunit 61.48\varunit
+-38.54\varunit 77.07\varunit
+-44.19\varunit 88.39\varunit
+-48.47\varunit 96.93\varunit
+-51.70\varunit 103.40\varunit
+-54.08\varunit 108.17\varunit
+-55.72\varunit 111.45\varunit
+-56.68\varunit 113.37\varunit
+-57.00\varunit 114.00\varunit
+-56.68\varunit 113.37\varunit
+-55.72\varunit 111.45\varunit
+-54.08\varunit 108.17\varunit
+-51.70\varunit 103.40\varunit
+-48.47\varunit 96.93\varunit
+-44.19\varunit 88.39\varunit
+-38.54\varunit 77.07\varunit
+-30.74\varunit 61.48\varunit
+-18.25\varunit 36.50\varunit
+\fiverm
+\frenchspacing
+\noindent
+\hbadness 6000
+\tolerance 9999
+\pretolerance 0
+\hyphenation{iso-peri-met-ric}
+The area of a circle is a mean proportional
+between any two regular and similar polygons of which one
+circumscribes it and the other is isoperimetric with it.
+In addition, the area of the circle is less than that of any
+circumscribed polygon and greater than that of any
+isoperimetric polygon. And further, of these
+circumscribed polygons, the one that has the greater number of sides
+has a smaller area than the one that has a lesser number;
+but, on the other hand, the isoperimetric polygon that
+has the greater number of sides is the larger.
+\hbox to 36.50\varunit{\hss[Galileo,\thinspace1638]\hss}
+}
+
+\danger
+\parshape 16
+3pc 26pc
+3pc 26pc
+0pc 24.69pc
+0pc 23.51pc
+0pc 22.73pc
+0pc 22.20pc
+0pc 21.85pc
+0pc 21.65pc
+0pc 21.58pc
+0pc 21.65pc
+0pc 21.85pc
+0pc 22.20pc
+0pc 22.73pc
+0pc 23.51pc
+0pc 24.69pc
+0pc 29pc
+\vadjust{\moveright 28pc\vbox to 0pt{\vskip88pt\vskip-60\varunit
+ \vskip-3pt\box0\vss}}%
+\strut It's possible to control the length of lines in a much more general
+way, if simple changes to |\leftskip| and |\rightskip| aren't
+flexible enough for your purposes. For example, a semicircular
+^{hole} has been cut out of the present paragraph, in order to make
+room for a circular illustration that contains some of ^{Galileo}'s
+immortal words about ^{circle}s; all of the line breaks in this paragraph
+and in the circular quotation were found by \TeX's line-breaking
+algorithm. You can specify an essentially arbitrary paragraph
+shape by saying ^|\parshape||=|\<number>, where the \<number> is
+a positive integer $n$, followed by $2n$ \<dimen> specifications.
+In general, `|\parshape=|$n$ $i_1$~$l_1$ $i_2$~$l_2$ $\ldots$ $i_n$~$l_n$'
+specifies a paragraph whose first $n$ lines will have lengths
+$l_1$, $l_2$, \dots,~$l_n$, respectively, and they will be
+indented from the left margin by the respective amounts
+$i_1$, $i_2$, \dots,~$i_n$. If the paragraph has fewer than
+$n$ lines, the additional specifications will be ignored;
+if it has more than $n$ lines, the specifications for line $n$ will
+be repeated ad infinitum. You can cancel the effect of a previously
+specified |\parshape| by saying `|\parshape=0|'.\parfillskip0pt
+ ^^{illustrations, fitting copy around}
+
+\ddangerexercise Typeset the following ^{Pascal}ian quotation in
+the shape of an isosceles ^{triangle}:
+``I turn, in the following treatises, to various uses of those
+ triangles whose generator is unity. But I leave out many more
+ than I include; it is extraordinary how fertile in properties
+ this triangle is. Everyone can try his hand.''
+\answer The author's best solution is based on a variable |\dimen|
+register |\x|:
+\begintt
+\setbox1=\hbox{I}
+\setbox0=\vbox{\parshape=11 -0\x0\x -1\x2\x -2\x4\x -3\x6\x
+ -4\x8\x -5\x10\x -6\x12\x -7\x14\x -8\x16\x -9\x18\x -10\x20\x
+ \ifdim \x>2em \rightskip=-\wd1
+ \else \frenchspacing \rightskip=-\wd1 plus1pt minus1pt
+ \leftskip=0pt plus 1pt minus1pt \fi
+ \parfillskip=0pt \tolerance=1000 \noindent I turn, ... hand.}
+\centerline{\hbox to \wd1{\box0\hss}}
+\endtt
+Satisfactory results are obtained with font |cmr10| when |\x| is set to
+$8.9\pt$, $13.4\pt$, $18.1\pt$, $22.6\pt$, $32.6\pt$, and $47.2\pt$,
+yielding triangles that are respectively 11,~9, 8, 7, 6, and~5 lines tall.
+
+\danger You probably won't need unusual parshapes very often. But there's
+a special case that occurs rather frequently, so \TeX\ provides a special
+abbreviation for~it in terms of two parameters called ^|\hangindent| and
+^|\hangafter|. The command `|\hangindent=|\<dimen>' specifies a so-called
+^{hanging indentation}, and the command `|\hangafter=|\<number>' specifies
+the duration of that indentation. Let $x$ and $n$ be the respective values
+of\/ |\hangindent| and |\hangafter|, and let $h$ be the value of\/
+^|\hsize|; then if $n\ge0$, hanging indentation will occur on lines $n+1$,
+$n+2$, $\ldots$ of the paragraph, but if $n<0$ it will occur on lines
+1,~2, \dots,~$\vert n\vert$. Hanging indentation means that lines will be
+of width $h-\vert x\vert$ instead of their normal width~$h$; if $x\ge0$,
+the lines will be indented at the left margin, otherwise they will be
+indented at the right margin. For example, the ``dangerous bend''
+paragraphs of this manual have a hanging indentation of 3~picas that lasts
+for two lines; they were set with |\hangindent=3pc| and |\hangafter=-2|.
+
+\danger Plain \TeX\ uses hanging indentation in its `^|\item|' macro, which
+produces a paragraph in which every line has the same indentation as a
+normal |\indent|. Furthermore, |\item| takes a parameter that is placed
+into the position of the indentation on the first line. Another macro called
+`^|\itemitem|' does the same thing but with double indentation.
+For example, suppose you type
+\begintt
+\item{1.} This is the first of several cases that are being
+enumerated, with hanging indentation applied to entire paragraphs.
+\itemitem{a)} This is the first subcase.
+\itemitem{b)} And this is the second subcase. Notice
+that subcases have twice as much hanging indentation.
+\item{2.} The second case is similar.
+\endtt
+{\let\par=\endgraf Then you get the following output:
+\medskip
+\item{1.} This is the first of several cases that are being
+enumerated, with hanging indentation applied to entire paragraphs.
+\itemitem{a)} This is the first subcase.
+\itemitem{b)} And this is the second subcase. Notice
+that subcases have twice as much hanging indentation.
+\item{2.} The second case is similar.
+\medskip}\noindent\hangindent0pt
+(Indentations in plain \TeX\ are not actually as dramatic as those
+displayed here; Appendix~B
+says `|\parindent=20pt|', but this manual has been set with
+|\parindent=36pt|.) \ It is customary to put ^|\medskip| before and after
+a group of itemized paragraphs, and to say |\noindent|
+before any closing remarks that apply to all of the cases.
+^^{enumerated cases in separate paragraphs}
+Blank lines are not needed before |\item| or |\itemitem|, since those macros
+begin with |\par|.
+
+\dangerexercise Suppose one of the enumerated cases continues for two
+or more paragraphs. How can you use |\item| to get hanging indentation
+on the subsequent paragraphs?
+\answer |\item{}| at the beginning of each paragraph that wants hanging
+indentation.
+
+\dangerexercise Explain how to make a ``^{bullet}ed'' item that says `$\bullet$'
+instead of `1.'.
+\answer |\item{$\bullet$}|
+
+\ddangerexercise The `|\item|' macro doesn't alter the right-hand margin. How
+could you indent at both sides?
+\answer Either change |\hsize| or |\rightskip|. The trick is to change it back
+again at the end of a paragraph. Here's one way, without grouping:
+\begintt
+\let\endgraf=\par \edef\restorehsize{\hsize=\the\hsize}
+\def\par{\endgraf \restorehsize \let\par=\endgraf}
+\advance\hsize by-\parindent
+\endtt
+
+\ddangerexercise Explain how you could specify a hanging indentation
+of $-2$ ems (i.e., the lines should project into the left margin),
+after the first two lines of a paragraph.
+\answer |\dimen0=\hsize \advance\dimen0 by 2em|\parbreak
+|\parshape=3 0pt\hsize 0pt\hsize -2em\dimen0|
+
+\danger If\/ |\parshape| and hanging indentation have both been specified,
+|\parshape| takes precedence and |\hangindent| is ignored. You get the
+normal paragraph shape, in which every line width is |\hsize|, when
+|\parshape=0|, |\hangindent=0pt|, and |\hangafter=1|. \TeX\ automatically
+restores these normal values at the end of every paragraph, and (by
+local definitions) whenever it enters internal vertical mode. For example,
+hanging indentation that might be present outside of a ^|\vbox| construction
+won't occur inside that vbox, unless you ask for it inside.
+^^{paragraph shape reset} ^^{hanging indentation reset}
+
+\ddangerexercise Suppose you want to leave room at the right margin for
+a rectangular illustration that takes up 15 lines, and you expect that
+three paragraphs will go by before you have typeset enough text to get
+past that illustration. Suggest a good way to do this without trial and error,
+given the fact that \TeX\ resets hanging indentation.
+\answer The three paragraphs can be combined into a single paragraph, if
+you use `|\hfil\vadjust{\vskip\parskip}\break\indent|' instead of
+`|\par|' after the first two. Then of course you say, e.g.,
+|\hangindent=-50pt \hangafter=-15|. \ (The same idea can be applied in
+connection with |\looseness|, if you want \TeX\ to make one of three
+paragraphs looser but if you don't want to choose which one it will be.
+However, long paragraphs fill \TeX's memory; please use restraint.) \
+See also the next exercise.
+
+\ddanger If ^{displayed equations} occur in a paragraph that has a nonstandard
+shape, \TeX\ always assumes that the display takes up exactly three lines.
+For example, a paragraph that has four lines of text, then a display, then
+two more lines of text, is considered to be $4+3+2=9$ lines long; the
+displayed equation will be indented and centered using the paragraph shape
+information appropriate to line~6.
+
+\ddanger \TeX\ has an internal integer variable called ^|\prevgraf| that
+records the number of lines in the most recent paragraph that has been
+completed or partially completed. You can use |\prevgraf| in the context of
+a \<number>, and you can set |\prevgraf| to any desired nonnegative value
+if you want to make \TeX\ think that it is in some particular part of the
+current paragraph shape. For example, let's consider again a paragraph
+that contains four lines plus a display plus two more lines. When \TeX\
+starts the paragraph, it sets |\prevgraf=0|; when it starts the display,
+|\prevgraf| will be~4; when it finishes the display, |\prevgraf| will
+be~7; and when it ends the paragraph, |\prevgraf| will be~9. If the
+display is actually one line taller than usual, you could set
+|\prevgraf=8| at the beginning of the two final lines; then \TeX\ will
+think that a 10-line paragraph is being made. The value of\/ |\prevgraf|
+affects line breaking only when \TeX\ is dealing with nonstandard
+|\parshape| or |\hangindent|.
+
+\edef\lastex{\chapno.\the\exno}
+\ddangerexercise Solve exercise \lastex\ using |\prevgraf|.
+\answer Use |\hangcarryover| between paragraphs, defined as follows:
+\begintt
+\def\hangcarryover{\edef\next{\hangafter=\the\hangafter
+ \hangindent=\the\hangindent}
+ \par\next
+ \edef\next{\prevgraf=\the\prevgraf}
+ \indent\next}
+\endtt
+
+\ddanger You are probably convinced by now that \TeX's line-breaking algorithm
+has plenty of bells and whistles, perhaps even too many.
+But there's one more feature,
+called ``looseness''; some day you might find yourself needing it,
+when you are fine-tuning the pages of a book. If you set |\looseness=1|,
+\TeX\ will try to make the current paragraph one line longer than its
+optimum length, provided that there is a way to choose such breakpoints
+without exceeding the tolerance you have specified for the badnesses
+of individual lines. Similarly, if you set |\looseness=2|, \TeX\ will
+try to make the paragraph two lines longer; and |\looseness=-1| causes an
+attempt to make it shorter. The general idea is that \TeX\ first finds
+breakpoints as usual; then if the optimum breakpoints produce
+$n$~lines, and if the current ^|\looseness| is~$l$, \TeX\ will choose
+the final breakpoints so as to make the final number of lines as close
+as possible to $n+l$ without exceeding the current tolerance. Furthermore,
+the final breakpoints will have fewest total demerits, considering all ways
+to achieve the same number of~lines.
+
+\ddanger For example, you can set |\looseness=1| if you want to avoid
+a lonely ``^{club line}'' or ``^{widow line}'' on some page that does not
+have sufficiently flexible glue, or if you want the total number of
+lines in some two-column document to come out to be an even number.
+It's usually best to choose a paragraph that is already pretty ``full,''
+i.e., one whose last line doesn't have much white space, since such
+paragraphs can generally be loosened without much harm. You might
+also want to insert a ^{tie} between the last two words of that paragraph,
+so that the loosened version will not end with only one ``^{widow word}'' on the
+^^{orphans, see widow words}
+line; this tie will cover your tracks, so that people will find it hard to
+detect the fact that you have tampered with the spacing. On the other
+hand, \TeX\ can take almost any sufficiently long paragraph and stretch
+it a bit, without substantial harm; the present paragraph is, in fact,
+one line looser than \hbox{its optimum length}.\looseness=1
+
+\ddanger \TeX\ resets the looseness to zero at the same time as it resets
+|\hangindent|, |\hangafter|, and |\parshape|.
+
+\ddangerexercise Explain what \TeX\ will do if you set |\looseness=-1000|.
+\answer It will set the current paragraph in the minimum number of lines
+that can be achieved without violating the tolerance; and, given that
+number of lines, it will break them optimally. \ (However, nonzero
+looseness makes \TeX\ work harder, so this is not recommended if you
+don't want to pay for the extra computation. You can achieve almost the
+same result much more efficiently by setting ^|\linepenalty||=100|, say.)
+
+\danger Just before switching to horizontal mode to begin scanning a
+paragraph, \TeX\ inserts the glue specified by ^|\parskip| into the vertical
+list that will contain the paragraph, unless that vertical list is empty so
+far. For example, `|\parskip=3pt|' will cause 3~points of extra space
+to be placed between paragraphs. Plain \TeX\ sets |\parskip=0pt plus1pt|;
+this gives a little stretchability, but no extra space.
+
+\danger After line breaking is complete, \TeX\ appends the lines to the
+current vertical list that encloses the current paragraph, inserting
+interline glue as explained in Chapter~12; this interline glue will
+depend on the values of\/ ^|\baselineskip|, ^|\lineskip|, and ^|\lineskiplimit|
+that are currently in force. \TeX\ will also insert penalties into
+the vertical list, just before each glob of ^{interline glue}, in order to
+help control page breaks that might have to be made later. For example, a
+special penalty will be assessed for breaking a page between the first two
+lines of a paragraph, or just before the last line, so that ``club'' or
+``widow'' lines that are detached from the rest of a paragraph will not
+appear all alone on a page unless the alternative is worse.
+
+\danger Here's how interline penalties are calculated: \TeX\ has just
+chosen the breakpoints for some paragraph, or for some partial paragraph
+that precedes a displayed equation; and $n$~lines have been formed.
+The penalty between lines $j$ and $j+1$, given a value of $j$ in the
+range $1\le j<n$, is the value of\/ ^|\interlinepenalty| plus
+additional charges made in special cases: The ^|\clubpenalty| is
+added if $j=1$, i.e., just after the first line; then the
+^|\displaywidowpenalty| or the ^|\widowpenalty| is added if $j=n-1$,
+i.e., just before the last line, depending on whether or not
+the current lines immediately precede a display; and finally the
+^|\brokenpenalty| is added, if the $j$th line ended at a discretionary break.
+(Plain \TeX\ sets |\clubpenalty=150|, |\widowpenalty=150|,
+|\displaywidowpenalty=50|, and |\brokenpenalty=100|; the value of\/
+|\interlinepenalty| is normally zero, but it is increased to 100 within
+^{footnotes}, so that long footnotes will tend not to be broken between
+pages.)
+
+\dangerexercise Consider a five-line paragraph in which the second and fourth
+lines end with hyphens. What penalties does plain \TeX\ put between the lines?
+\answer 150, 100, 0, 250. \ (When the total penalty is zero, as between lines
+3 and~4 in this case, no penalty is actually inserted.)
+
+\dangerexercise What penalty goes between the lines of a two-line paragraph?
+\answer |\interlinepenalty| plus |\clubpenalty| plus |\widowpenalty| (and
+also plus |\brokenpenalty|, if the first line ends with a discretionary break).
+
+\ddanger If you say ^|\vadjust||{|\<vertical list>|}| within a paragraph,
+\TeX\ will insert the specified internal vertical list into the vertical
+list that encloses the paragraph, immediately after whatever line
+contained the position of the |\vadjust|. For example, you can say
+`|\vadjust{\kern1pt}|' to increase the amount of space between lines of a
+paragraph if those lines would otherwise come out too close together. \ (The
+\vadjust{\vskip1pt}author
+did it in the previous line, just to illustrate what happens.) \ Also,
+if you want to make sure that a page break will occur immediately after a
+certain line, you can say `|\vadjust{\eject}|' ^^|\eject| anywhere in that line.
+
+\ddanger Later chapters discuss |\insert| and |\mark| commands that are
+relevant to \TeX's page builder. If such commands appear within a
+paragraph, they are removed from whatever horizontal lines contain them
+and placed into the enclosing vertical list, together with other vertical
+material from |\vadjust| commands that might be present. In the final
+vertical list, each horizontal line of text is an hbox that is immediately
+preceded by interline glue and immediately followed by vertical material
+that has ``^{migrate}d out'' from that line (with left to right order
+preserved, if there are several instances of vertical material); then
+comes the interline penalty, if it is nonzero. Inserted vertical material
+does not influence the ^{interline glue}.
+
+\ddangerexercise Design a |\marginalstar| macro ^^{marginal notes}
+that can be used anywhere in a paragraph. It should use |\vadjust| to
+place an asterisk in the margin just to the left of the line where
+|\marginalstar| occurs.
+\answer The tricky part is to avoid ``opening up'' the paragraph by
+adding anything to its height; yet this star is to be contributed after
+a line having an unknown depth, because the depth of the line depends
+on details of line breaking that aren't known until afterwards.
+The following solution uses ^|\strut|, and assumes that the line containing
+the marginal star does not have depth exceeding ^|\dp||\strutbox|, the
+depth of a ^|\strut|.
+\begintt
+\def\strutdepth{\dp\strutbox}
+\def\marginalstar{\strut\vadjust{\kern-\strutdepth\specialstar}}
+\endtt
+Here |\specialstar| is a box of height zero and depth |\strutdepth|,
+and it puts an asterisk in the left margin:
+\begintt
+\def\specialstar{\vtop to \strutdepth{
+ \baselineskip\strutdepth
+ \vss\llap{* }\null}}
+\endtt
+
+\ddanger When \TeX\ enters ^{horizontal mode}, it will interrupt its normal
+scanning to read tokens that were predefined by the command
+^|\everypar||={|\<token list>|}|. For example, suppose you have said
+`|\everypar={A}|'. If you type `|B|' in vertical mode, \TeX\ will shift
+to horizontal mode (after contributing ^|\parskip| glue to the current
+page), and a horizontal list will be initiated by inserting an empty box
+of width ^|\parindent|. Then \TeX\ will read `|AB|', since it reads the
+|\everypar| tokens before getting back to the `|B|' that triggered the
+new paragraph. Of course, this is not a very useful illustration of
+|\everypar|; but if you let your imagination run you will think of
+better applications.
+
+\ddangerexercise Use |\everypar| to define an |\insertbullets| macro: All
+paragraphs in a group of the form `|{\insertbullets ...\par}|' should have a
+bullet symbol `$\bullet$' as part of their indentation.
+^^{bulleted lists}
+\answer |\def\insertbullets{\everypar={\llap{$\bullet$\enspace}}}|\par
+\nobreak\smallskip\noindent
+(A similar device can be used to insert hanging indentation,
+and/or to number the paragraphs automatically.)
+
+\ddanger A paragraph of zero lines is formed if you say `|\noindent\par|'.
+If\/ |\everypar| is null, such a paragraph contributes nothing except
+|\parskip| glue to the current vertical list.
+
+\ddangerexercise Guess what happens if you say `|\noindent$$...$$ \par|'.
+\answer First comes |\parskip| glue (but you might not see it on the current
+page if you say |\showlists|, since glue disappears at the top of each
+page). Then comes the result of\/ |\everypar|, but let's assume that
+|\everypar| doesn't add anything to the horizontal list, so that
+you get an empty horizontal list; then there's no partial paragraph
+before the display. The displayed equation follows the normal rules
+(it occupies lines 1--3 of the paragraph, and uses the indentation and
+length of line~2, if there's a nonstandard shape). Nothing follows the
+display, since a blank space is ignored after a closing `|$$|'.\par
+Incidentally, the behavior is different if you start a paragraph with
+`|$$|' instead of with |\noindent$$|, ^^{display at beginning of paragraph}
+since \TeX\ inserts a paragraph indentation that will appear on a line by
+itself (with |\leftskip| and |\parfillskip| and |\rightskip| glue).
+
+\ddanger Experience has shown that \TeX's line-breaking algorithm can be
+harnessed to a surprising variety of tasks. Here, for example, is an application
+that indicates one of the possibilities: Articles that are published in
+{\sl^{Mathematical Reviews}\/\null} are generally signed with the reviewer's
+name and address, and this information is typeset flush right, i.e., at
+the right-hand margin. ^^{flush right}
+If there is sufficient space to put such a name and address at the right of
+the final line of the paragraph, the publishers can save space, and at the same
+time the results look better because there are no strange gaps on the page.
+\def\signed #1 (#2){{\unskip\nobreak\hfil\penalty50\hskip2em
+ \hbox{}\nobreak\hfil\sl#1\/ \rm(#2)
+ \parfillskip=0pt \finalhyphendemerits=0 \endgraf}}
+\begindisplay
+\vbox{\hsize 3.0in \parindent0pt
+ This is a case where the name and address fit in nicely with the review.
+ \signed A. Reviewer (Ann Arbor, Mich.)
+ \medskip
+ But sometimes an extra line must be added. \signed N. Bourbaki (Paris)}
+\enddisplay
+^^{Reviewer} ^^{Bourbaki}
+Let's suppose that a space of at least two ems should separate the reviewer's
+name from the text of the review, if they occur on the same line. We would
+like to design a macro so that the examples shown above could be typed
+as follows in an input file:
+\begintt
+... with the review. \signed A. Reviewer (Ann Arbor, Mich.)
+... an extra line must be added. \signed N. Bourbaki (Paris)
+\endtt
+Here is one way to solve the problem:
+\begintt
+\def\signed #1 (#2){{\unskip\nobreak\hfil\penalty50
+ \hskip2em\hbox{}\nobreak\hfil\sl#1\/ \rm(#2)
+ \parfillskip=0pt \finalhyphendemerits=0 \par}}
+\endtt
+If a line break occurs at the |\penalty50|, the |\hskip2em| will disappear
+and the empty |\hbox| will occur at the beginning of a line, followed by
+|\hfil| glue. This yields two lines whose badness is zero; the first of these
+lines is assessed a penalty of~50. But if no line break occurs at the
+|\penalty50|, there will be glue of $2\em$ plus $2\,{\rm fil}$ between
+the review and the name; this yields one line of badness zero. \TeX\ will
+try both alternatives, to see which leads to the fewest total demerits.
+The one-line solution will usually be preferred if it is feasible.
+
+\ddangerexercise Explain what would happen if `|\hbox{}|' were left out
+of the ^|\signed| macro.
+\answer A break at |\penalty50| would cancel |\hskip2em\nobreak\hfil|,
+so the next line would be forced to start with the reviewer's name flush left.
+\ (But ^|\vadjust||{}| would actually be better than |\hbox{}|; it
+uses \TeX\ more efficiently.)
+
+\ddangerexercise Why does the |\signed| macro say
+`^|\finalhyphendemerits||=0|'\thinspace?
+\answer Otherwise the line-breaking algorithm might prefer two final lines to
+one final line, simply in order to move a hyphen from the second-last line up
+to the third-last line where it doesn't cause demerits. This in fact caused
+some surprises when the |\signed| macro was being tested; |\tracingparagraphs=1|
+was used to diagnose the problem.
+
+{\hbadness=10000
+\ddangerexercise In one of the paragraphs earlier in this chapter, the author
+used ^|\break| to force a line break in a specific place; as a result, the
+third line of that particular paragraph was really spaced out.\break
+Explain why all the extra space went into the third line, instead of being
+distributed impartially among the first three lines.
+\answer Distributing the extra space evenly would lead to three lines of
+the maximum badness (10000). It's better to have just one bad line
+instead of three, since \TeX\ doesn't distinguish degrees of badness when
+lines are really awful. In this particular case the ^|\tolerance| was 200,
+so \TeX\ didn't try any line breaks that would stretch the first two lines;
+but even if the tolerance had been raised to 10000, the optimum setting would
+have had only one underfull line. If you really want to spread the
+space evenly you can do so by using ^|\spaceskip| to increase the
+amount of stretchability between words.
+
+}
+\ddanger If you want to avoid overfull boxes at all costs without
+trying to fix them manually, you might be tempted to set
+|\tolerance=10000|; this allows arbitrarily bad lines to be acceptable
+in tough situations. But infinite tolerance is a bad idea, because
+\TeX\ doesn't distinguish between terribly bad and preposterously
+horrible lines. Indeed, a tolerance of 10000 encourages \TeX\ to
+concentrate all the badness in one place, making one truly unsightly
+line instead of two moderately bad ones, because a single
+``write-off'' produces fewest total demerits according to the rules.
+There's a much better way to get the desired effect: \TeX\ has a
+parameter called ^|\emergencystretch| that is added to the assumed
+stretchability of every line when badness and demerits are computed,
+in cases where overfull boxes are otherwise unavoidable. If
+|\emergencystretch| is positive, \TeX\ will make a third pass over a
+paragraph before choosing the line breaks, when the first passes did
+not find a way to satisfy the ^|\pretolerance| and ^|\tolerance|.
+The effect of\/ |\emergencystretch| is to scale down the badnesses so
+that large infinities are distinguishable from smaller ones. By
+setting |\emergencystretch| high enough (based on |\hsize|) you can be
+sure that the |\tolerance| is never exceeded; hence overfull boxes
+will never occur unless the line-breaking task is truly impossible.
+
+\ddangerexercise Devise a ^|\raggedcenter| macro (analogous to ^|\raggedright|)
+that partitions the words of a paragraph into as few as possible lines
+of approximately equal size and centers each individual line. Hyphenation
+should be avoided if possible.
+\answer |\def\raggedcenter{\leftskip=0pt plus4em \rightskip=\leftskip|%
+\parbreak|\parfillskip=0pt \spaceskip=.3333em \xspaceskip=.5em|\parbreak
+ |\pretolerance=9999 \tolerance=9999 \parindent=0pt|\parbreak
+ |\hyphenpenalty=9999 \exhyphenpenalty=9999 }|
+
+\endchapter
+
+When the author objects to [a hyphenation]\/
+he should be asked to add or cancel or substitute
+a word or words that will prevent the breakage.
+\smallskip
+Authors who insist on even spacing always,
+with sightly divisions always,
+do not clearly understand the rigidity of types.
+\author T. L. ^{DE VINNE}, {\sl Correct Composition\/} (1901) % p138, p206
+
+\bigskip
+
+In reprinting his own works, whenever [William ^{Morris}]\/
+found a line that justified awkwardly, he altered the wording
+solely for the sake of making it look well in print.
+\smallskip
+When a proof has been sent me with two or three
+lines so widely spaced as to make a grey band across the page,
+I have often rewritten the passage so as to fill up the lines better;
+but I am sorry to say that my object has generally been so little
+understood that the compositor has spoilt all the rest
+of the paragraph instead of mending his former bad work.
+\author GEORGE BERNARD ^{SHAW}, in {\sl The Dolphin\/} (1940) % v4.1 p80
+
+\eject
+ \beginchapter Chapter 15. How \TeX\ Makes\\Lines into Pages
+
+\tracingpages=1
+\TeX\ attempts to choose desirable places to divide your document into
+individual pages, and its technique for doing this usually works pretty
+well. But the problem of ^{page make-up} is considerably more difficult
+than the problem of line breaking that we considered in the previous chapter,
+because pages often have much less flexibility than lines do. If the
+vertical glue on a page has little or no ability to stretch or to shrink,
+\TeX\ usually has no choice about where to start a new page; conversely, if
+there is too much variability in the glue, the result will look bad because
+different pages will be too irregular. Therefore if you are fussy about the
+appearance of pages, you can expect to do some rewriting of the manuscript
+until you achieve an appropriate balance, or you might need to fiddle
+with the ^|\looseness| as described in Chapter~14; no automated system will
+be able to do this as well as you.
+
+Mathematical papers that contain a lot of displayed equations have an
+advantage in this regard, because the glue that surrounds a display tends to
+be quite flexible. \TeX\ also gets valuable room to maneuver when you
+have occasion to use ^|\smallskip| or ^|\medskip| or ^|\bigskip| spacing
+between certain paragraphs. For example, consider a page that contains
+a dozen or so exercises, and suppose that there is $3\pt$ of additional
+space between exercises, where this space can stretch to $4\pt$ or
+shrink to $2\pt$. Then there is a chance to squeeze an extra line on the page,
+or to open up the page by removing one line, in order to avoid splitting
+an exercise between pages. Similarly, it is possible to use flexible
+glue in special publications like membership rosters or company telephone
+directories, so that individual entries need not be split between columns
+or pages, yet every column appears to be the same height.
+
+For ordinary purposes you will probably find that \TeX's automatic method
+of page breaking is satisfactory. And when it occasionally gives
+unpleasant results, you can force the machine to break at your favorite
+place by typing `^|\eject|'. But be careful: |\eject| will cause \TeX\ to
+stretch the page out, if necessary, so that the top and bottom baselines
+agree with those on other pages. If you want to eject a short page,
+filling it with blank space at the bottom, type `|\vfill\eject|' instead.
+
+\danger If you say `|\eject|' in the middle of a paragraph, the paragraph
+will end first, as if you typed `|\par\eject|'. But Chapter~14 mentions
+that you can say `^|\vadjust||{\eject}|' in mid-paragraph, if you want to
+force a page break after whatever line contains your current position
+when the full paragraph is eventually broken up into lines; the rest of the
+paragraph will go on the following page.
+
+\danger To prevent a page break, you can say `^|\nobreak|' in vertical
+mode, just as |\nobreak| in horizontal mode prevents breaks between lines.
+For example, it is wise to say |\nobreak| between the title of a subsection
+and the first line of text in that subsection. But |\nobreak| does not
+cancel the effect of other commands like |\eject| that tell \TeX\ to
+break; it only inhibits a break at glue that immediately follows. You
+should become familiar with \TeX's rules for line breaks and page breaks
+if you want to maintain fine control over everything. The remainder of
+this chapter is devoted to the intimate details of page breaking.
+
+\ninepoint
+\danger \TeX\ breaks lists of lines into pages by computing badness ratings
+and penalties, more or less as it does when breaking paragraphs into lines.
+But pages are made up one at a time and removed from \TeX's memory; there is
+no looking ahead to see how one page break will affect the next one.
+In other words, \TeX\ uses a special method to find the optimum
+breakpoints for the lines in an entire paragraph, but it doesn't attempt
+to find the optimum breakpoints for the pages in an entire document. The
+computer doesn't have enough high-speed memory capacity to remember the
+contents of several pages, so \TeX\ simply chooses each page break as best
+it can, by a process of ``local'' rather than ``global'' optimization.
+
+\tracingpages=0
+\danger Let's look now at the details of \TeX's page-making process.
+Everything you contribute to the pages of your document is placed on
+the {\sl ^{main vertical list}}, which is the sequence of items that
+\TeX\ has accumulated while in vertical mode. Each item in a ^{vertical
+list} is one of the following types of things:\enddanger
+
+\smallskip
+\item\bull a box (an hbox or vbox or rule);
+
+\item\bull a ``^{whatsit}'' (something special to be explained later);
+
+\item\bull a ^{mark} (another thing that will be explained later);
+
+\item\bull an ^{insertion} (yet another thing that we will get to);
+
+\item\bull a glob of ^{glue} (or ^|\leaders|, as we will see later);
+
+\item\bull a ^{kern} (something like glue that doesn't stretch or shrink);
+
+\item\bull a ^{penalty} (representing the undesirability of breaking here).
+
+\smallskip\noindent
+The last three types (glue, kern, and penalty items) are called ^{discardable},
+for the same reason that we called them discardable in horizontal lists.
+You might want to compare these specifications with the analogous rules
+for the horizontal case, found in Chapter~14; it turns out that vertical
+lists are just like horizontal ones except that character boxes, discretionary
+breaks, ^|\vadjust| items, and math shifts cannot appear in vertical lists.
+Chapter~12 exhibits a typical vertical list in \TeX's internal box-and-glue
+representation.
+
+\danger Page breaks can occur only at certain places within a vertical list.
+The permissible breakpoints are exactly the same as in the horizontal case,
+namely\enddanger
+
+\smallskip
+\item{a)} at glue, provided that this glue is immediately preceded by a
+non-discardable item (i.e., by a box, whatsit, mark, or insertion);
+
+\smallskip
+\item{b)} at a kern, provided that this kern is immediately followed by glue;
+
+\smallskip
+\item{c)} at a penalty (which might have been inserted automatically in a
+paragraph).
+
+\smallskip\noindent
+Interline glue is usually inserted automatically between the boxes of a
+vertical list, as explained in Chapter~12, so there is usually a valid
+breakpoint between boxes.
+
+\danger As in horizontal lists, each potential breakpoint has an
+associated penalty, which is high for undesirable breakpoints and negative
+for desirable ones. The penalty is zero at glue and kern breaks, so it is
+nonzero only at explicit penalty breaks. If you say `^|\penalty||-100|'
+between two paragraphs, you are indicating that \TeX\ should try to break
+here because the penalty is negative; a bonus of 100 points for breaking
+at this place will essentially cancel up to 100 units of badness that
+might be necessary to achieve such a break. A penalty of 10000 or more is
+so large that it inhibits breaking; a penalty of $-10000$ or less is so
+small that it forces breaking.
+
+\danger Plain \TeX\ provides several control sequences that help to
+control page breaks. For example, ^|\smallbreak|, ^|\medbreak|, and
+^|\bigbreak| specify increasingly desirable places to break, having
+respective penalties of $-50$, $-100$, and~$-200$; furthermore, they will
+insert a ^|\smallskip|, ^|\medskip|, or ^|\bigskip| of space,
+respectively, if a break is not taken. However, |\smallbreak|,
+|\medbreak|, and |\bigbreak| do not increase existing glue unnecessarily;
+for example, if you say |\smallbreak| just after a displayed
+equation, you won't get a |\smallskip| of space in addition to the glue that
+already follows a display. Therefore these commands can conveniently
+be used before and after the statements of ^{theorems}, in a format for
+mathematical papers. In the present manual the author has used a macro
+that puts |\medbreak| before and after every dangerous-bend paragraph;
+|\medbreak\medbreak| is equivalent to a single |\medbreak|, so you don't
+see two medskips when one such paragraph ends and another one~begins.
+
+\danger The ^|\goodbreak| macro is an abbreviation for `|\par\penalty-500|'.
+This is a good thing to insert in your manuscript when proofreading, if
+you are willing to stretch some page a little bit extra in order to improve the
+following one. Later on if you make another change so that this |\goodbreak|
+command does not appear near the bottom of a page, it will have no effect;
+thus it is not as drastic as |\eject|.
+
+\danger The most interesting macro that plain \TeX\ provides for page make-up
+is called ^|\filbreak|. It means, roughly, ``Break the page here and fill the
+bottom with blank space, unless there is room for more
+copy that is itself followed by |\filbreak|.'' Thus if you put |\filbreak|
+at the end of every paragraph, and if your paragraphs aren't too long,
+every page break will occur between paragraphs, and \TeX\ will fit as many
+paragraphs as possible on each page. The precise meaning of\/ |\filbreak|~is
+\begintt
+\vfil\penalty-200\vfilneg
+\endtt
+according to Appendix B\null; and this simple combination of \TeX's primitives
+produces the desired result: If a break is taken at the |\penalty-200|,
+the preceding |\vfil| will fill the bottom of the page with blank space,
+and the ^|\vfilneg| will be discarded after the break; but if no break
+is taken at the penalty, the |\vfil| and |\vfilneg| will cancel each other
+and have no effect.
+
+\danger Plain \TeX\ also provides a ^|\raggedbottom| command, which is a
+vertical analog of\/ ^|\raggedright|: It tells \TeX\ to permit a small
+amount of variability in the bottom margins on different pages, in order
+to make the other spacing uniform.
+
+\ddanger We saw in Chapter 14 that breakpoints for paragraphs are chosen
+by computing ``demerits'' for each line and summing them over all lines.
+The situation for pages is simpler because each page is considered separately.
+\TeX\ figures the ``^{cost}'' of a page break by using the following formula:
+\begindisplay
+$\displaystyle{c=\cases{
+ p,&if $b<\infty$ and $p\le-10000$ and $q<10000$;\cr
+ b+p+q,&if $b<10000$ and $-10000<p<10000$ and $q<10000$;\cr
+ 100000,&if $b=10000$ and $-10000<p<10000$ and $q<10000$;\cr
+ \infty,&if ($b=\infty$ or $q\ge10000$) and $p<10000$.\cr}}$
+\enddisplay
+Here $b$ is the ^{badness} of the page that would be formed if a break
+were chosen here; $p$~is the penalty associated with the current
+breakpoint; and $q$~is `^|\insertpenalties|', the sum of all penalties for
+split insertions on the page, as explained below. Vertical badness is
+computed by the same rules as horizontal badness; it is an integer between
+0 and~10000, inclusive, except when the box is overfull, when it
+is~$\infty$ (infinity).
+^^{infinite badness}
+
+\ddanger When a page is completed, it is removed from the main vertical
+list and passed to an ``^{output routine},'' as we will see later; so its
+boxes and glue eventually disappear from \TeX's memory. The remainder of
+the main vertical list exists in two parts: First comes the ``^{current
+page},'' which contains all the material that \TeX\ has considered so far
+as a candidate for the next page to be broken off; then there are
+``^{recent contributions},'' i.e., items that will be moved to the current
+page as soon as \TeX\ finds it convenient to do so. If you say
+^|\showlists|, \TeX\ will display the contents of the current page and the
+recent contributions, if any, on your log file. \ (The example in
+Chapter~13 doesn't show any such lists because they were both empty in
+that case. Chapter~24 explains more about \TeX's timing.)
+
+\ddanger Whenever \TeX\ is moving an item from the top of the ``recent
+contributions'' to the bottom of the ``current page,'' it discards a
+^{discardable item} (glue, kern, or penalty) if the current page does not
+contain any boxes. This is how glue disappears at a page break. Otherwise
+if a discardable item is a legitimate breakpoint, \TeX\ calculates the
+cost~$c$ of breaking at this point, using the formula that we have just
+discussed. If the resulting~$c$ is less than or equal to the smallest cost
+seen so far on the current page, \TeX\ remembers the current
+breakpoint as the best so far. And if $c=\infty$ or if $p\le-10000$, \TeX\
+seizes the initiative and breaks the page at the best remembered
+breakpoint. Any material on the current page following that best
+breakpoint is moved back onto the list of recent contributions, where it
+will be considered again; thus the ``current page'' typically gets more
+than one page's worth of material before the breakpoint is chosen.
+
+\ddanger This procedure may seem mysterious until you see it in action.
+Fortunately, there is a convenient way to watch it;
+you can set ^|\tracingpages||=1|, thereby instructing \TeX\ to put its
+page-cost calculations into your log file. For example, here is what
+appeared on the log file when the author used |\tracingpages=1| at the
+beginning of the present chapter:
+\begintt
+%% goal height=528.0, max depth=2.2
+% t=10.0 g=528.0 b=10000 p=150 c=100000#
+% t=22.0 g=528.0 b=10000 p=0 c=100000#
+% t=34.0 g=528.0 b=10000 p=0 c=100000#
+|kern-3pt
+|qquad|hbox|bgroup|rm|vdots|quad(25 similar lines are being omitted here)|egroup
+% t=346.0 plus 2.0 g=528.0 b=10000 p=0 c=100000#
+% t=358.0 plus 2.0 g=528.0 b=10000 p=150 c=100000#
+% t=370.02223 plus 2.0 g=528.0 b=10000 p=-100 c=100000#
+% t=398.0 plus 5.0 minus 2.0 g=528.0 b=10000 p=0 c=100000#
+% t=409.0 plus 5.0 minus 2.0 g=528.0 b=10000 p=0 c=100000#
+% t=420.0 plus 5.0 minus 2.0 g=528.0 b=10000 p=150 c=100000#
+% t=431.0 plus 5.0 minus 2.0 g=528.0 b=10000 p=-100 c=100000#
+% t=459.0 plus 8.0 minus 4.0 g=528.0 b=10000 p=0 c=100000#
+% t=470.0 plus 8.0 minus 4.0 g=528.0 b=10000 p=0 c=100000#
+% t=481.0 plus 8.0 minus 4.0 g=528.0 b=10000 p=0 c=100000#
+% t=492.0 plus 8.0 minus 4.0 g=528.0 b=10000 p=0 c=100000#
+% t=503.0 plus 8.0 minus 4.0 g=528.0 b=3049 p=0 c=3049#
+% t=514.0 plus 8.0 minus 4.0 g=528.0 b=533 p=150 c=683#
+% t=525.0 plus 8.0 minus 4.0 g=528.0 b=5 p=-100 c=-95#
+% t=553.0 plus 11.0 minus 6.0 g=528.0 b=* p=0 c=*
+\endtt
+This trace output is admittedly not ``user-friendly'' in appearance, but
+after all it comes from deep inside \TeX's bowels where things have been
+reduced to numeric calculations. You can learn to read it with a little
+practice, but you won't need to do so very often unless you need to
+plunge into page-breaking for special applications. Here's what it
+means: The first line, which starts with `|%%|', ^^{percent percent}
+is written when the first box or insertion enters the current page list;
+it shows the ``^{goal height}'' and the ``^{max depth}'' that will be
+used for that page (namely, the current values of\/ ^|\vsize| and
+^|\maxdepth|). In the present manual we have |\vsize=44pc| and
+|\maxdepth=2.2pt|; dimensions in the log file are always displayed in
+points. The subsequent lines, which start with a single `|%|', ^^{percent}
+are written whenever a legal breakpoint is being moved from the list of
+recent contributions to the current page list. Every |%|~line shows $t$,
+which is the total height so far if a page break were to occur,
+and $g$, which is the goal height; in this example $g$~stays fixed at
+$528\pt$, but $g$ would have decreased if insertions such as footnotes had
+occurred on the page. The values of~$t$ are steadily increasing from 10 to
+22 to~34, etc.; baselines are $12\pt$ apart at the top of the page and
+$11\pt$ apart at the bottom (where material is set in nine-point type).
+We are essentially seeing one |%|~line per hbox of text being placed on
+the current page. However, the |%|~lines are generated by the penalty or
+glue items that follow the hboxes, not by the boxes themselves. Each
+|%|~line shows also the badness~$b$, the penalty~$p$, and the cost~$c$
+associated with a breakpoint; if this cost is the best so far, it is
+marked with a `|#|' sign, ^^{sharp} meaning that ``this breakpoint will be
+used for the current page if nothing better comes along.'' Notice that the
+first 40 or so breaks all have $b=10000$, since they are so bad that \TeX\
+considers them indistinguishable; in such cases $c=100000$, so \TeX\ simply
+accumulates material until the page is full enough to
+have $b<10000$. A penalty of 150 reflects the ^|\clubpenalty| or the
+^|\widowpenalty| that was inserted as described in Chapter~14. The three
+lines that say |p=-100| are the breakpoints between ``dangerous bend''
+paragraphs; these came from ^|\medbreak| commands. The notation |b=|^|*| and
+|c=*| on the final line means that $b$ and~$c$ are infinite; the total
+height of $553\pt$ cannot be reduced to $528\pt$ by shrinking the
+available glue. Therefore the page is ejected at the best previous place,
+which turns out to be a pretty good break: |b=5| and |p=-100| yield a
+net cost of $-95$.
+
+\ddangerexercise Suppose the paragraph at the bottom of the example page
+had been one line shorter; what page break would have been chosen?
+\answer The last three page-break calculations would have been
+\begintt
+% t=503.0 plus 8.0 minus 4.0 g=528.0 b=3049 p=150 c=3199#
+% t=514.0 plus 8.0 minus 4.0 g=528.0 b=533 p=-100 c=433#
+% t=542.0 plus 11.0 minus 6.0 g=528.0 b=* p=0 c=*
+\endtt
+so the break would have occurred at the same place. The badness would have
+been~533, but the page would still have looked tolerable. \ (On the other
+hand if that paragraph had been two lines shorter instead of one,
+the first two lines of the next ``dangerous bend'' paragraph
+would have appeared on that page; the natural height $t=531\pt$ would have
+been able to shrink to $g=528\pt$ because the three ``medskips'' on
+the page would have had a total shrinkability of $6\pt$. This would certainly
+have been preferable to a stretched-out page whose badness was~3049; but the
+author might have seen it and written another sentence or two, so that
+the paragraph would not have been broken up. After all, this manual is supposed
+to be an example of good practice.)
+
+\ddangerexercise The last two ``\kern.5pt|%| lines'' of this example show
+the natural height of~$t$ jumping by $28\pt$, from 525.0 to~553.0. Explain
+why there was such a big jump.
+\answer The next legal break after the beginning of a dangerous bend
+paragraph occurs $28\pt$ later, because there is $6\pt$ additional space for
+a |\medskip|, followed by two lines of $11\pt$ each. \TeX\ does not
+allow breaking between those two lines; the ^|\clubpenalty| is set briefly
+to 10000 in Appendix~E\null, since the dangerous bend symbol is two lines tall.
+
+\ddanger The ^|\maxdepth| parameter tells \TeX\ to raise the bottom box
+on the page if that box has too much depth, so that the depth of the
+constructed page will not exceed a specified value. \ (See the discussion of
+^|\boxmaxdepth| in Chapter~12.) \ In our example |\maxdepth=2.2pt|, and
+the influence of this parameter can be seen in the line that says
+`|% t=370.02223|'. Ordinarily $t$~would have been 370.0 at that breakpoint;
+but the hbox preceding it was unusual because it contained the letter
+|j| in ^|\tt|, and a 10-point typewriter-style |j| descends $2.22223\pt$
+below the baseline. Therefore \TeX\ figured badness as if the hbox were
+$.02223\pt$ higher and only $2.2\pt$ deep.
+
+\ddanger Notice that the first ``\kern.5pt|%| line'' of our example says
+|t=10.0|; this is a consequence of another parameter, called ^|\topskip|.
+Glue disappears at a page break, but it is desirable to produce pages
+whose top and bottom baselines occur in predetermined positions, whenever
+possible; therefore \TeX\ inserts special glue just before the first box
+on each page. This special glue is equal to |\topskip|, except that the
+natural space has been decreased by the height of the first box, or it has
+been set to zero in lieu of a negative value. For example, if\/
+|\topskip=20pt plus2pt|, and if the first box on the current page is
+$13\pt$ tall, \TeX\ inserts `|\vskip7pt plus2pt|' just above that box.
+Furthermore, if the first box is more than $20\pt$ tall, `|\vskip0pt
+plus2pt|' is inserted. But this example is atypical, since the |\topskip|
+glue usually has no stretchability or shrinkability; plain \TeX\ sets
+|\topskip=10pt|.
+
+\ddangerexercise Assume that |\vsize=528pt|, |\maxdepth=2.2pt|,
+|\topskip=10pt|, and that no |\insert| commands are being used. \TeX\ will
+make pages that are $528\pt$ high, and the following two statements will
+normally be true: (a)~The baseline of the topmost box on the page will be
+$10\pt$ from the top, i.e., $518\pt$ above the baseline of the page itself.
+(b)~The baseline of the bottommost box on the page will coincide with the
+baseline of the page itself. Explain under what circumstances (a) and~(b)
+will fail.
+\answer A page always contains at least one box, if there are no
+insertions, since the legal breakpoints are discarded otherwise.
+Statement~(a) fails if the height of the topmost box exceeds $10\pt$.
+Statement~(b) fails if the depth of the bottommost box exceeds $2.2\pt$, or
+if some glue or kern comes between the bottommost box and the page break
+(unless that glue or kern exactly cancels the depth of the box).
+
+\ddanger Since |\vsize|, |\maxdepth|, and |\topskip| are
+parameters, you can change them at any time; what happens if you do?
+Well, \TeX\ salts away the values of\/ |\vsize| and |\maxdepth| when
+it prints the ``\kern.5pt|%%|~line,'' i.e., when the first box or insertion
+occurs on the current page; subsequent changes to those two parameters
+have no effect until the next current page is started. On the other hand,
+\TeX\ looks at |\topskip| only when the first box is being contributed
+to the current page. If insertions occur before the first box, the
+|\topskip| glue before that box is considered to be a valid breakpoint;
+this is the only case in which a completed page might not contain a box.
+
+\ddanger You can look at the $t$ and $g$ values that are used in page
+breaking by referring to the \<dimen> values `^|\pagetotal|' and
+`^|\pagegoal|', respectively. You can even change them (but let's hope
+that you know what you are doing). For example, the command
+|\pagegoal=500pt| overrides the previously saved value of\/ |\vsize|.
+Besides |\pagetotal|, which represents the accumulated natural height,
+\TeX\ maintains the quantities ^|\pagestretch|, ^|\pagefilstretch|,
+^|\pagefillstretch|, ^|\pagefilllstretch|, ^|\pageshrink|, and
+^|\pagedepth|. When the current page contains no boxes, |\pagetotal| and
+its relatives are zero and |\pagegoal| is $16383.99998\pt$ (\TeX's largest
+\<dimen>); changing their values has no effect at such times. The integer
+$q$ in the formula for page costs is also available for inspection and
+change; it is called ^|\insertpenalties|.
+
+\ddanger Page breaking differs from line breaking in one small respect
+that deserves mention here: If you say |\eject\eject|, the second |\eject|
+is ignored, because it is equivalent to |\penalty-10000| and penalties are
+discarded after a page break. But if you say |\break\break| in a
+paragraph, the second ^|\break| causes an empty line, because penalties
+are discarded after a break in a paragraph only if they do not belong to
+the final sequence of breakpoints. This technicality is unimportant in
+practice, because |\break\break| isn't a good way to make an empty line;
+that line will usually be an underfull hbox, since it has only the
+|\leftskip| and |\rightskip| glue in it. Similarly, `|\eject\eject|'
+would not be a good way to make an empty page, even if \TeX\ were to
+change its rules somehow so that an |\eject| would never be ignored. The
+best way to eject an ^{empty page} is to say `|\eject\line{}\vfil\eject|',
+and the best way to create an ^{empty line} is
+`|\break\hbox{}\hfil\break|'. Both of these avoid underfull boxes.
+
+\danger You are probably wondering how page numbers and such things
+get attached to pages. The answer is that \TeX\ allows you to do further
+processing after each page break has been chosen; a special ``output
+routine'' goes into action before pages actually receive their final
+form. Chapter~23 explains how to construct output routines and how to
+modify the output routine of plain~\TeX.
+
+\danger Every once in a~while, \TeX\ will produce a really awful-looking
+page and you will wonder what happened. For example, you might get just
+one paragraph and a lot of white space, when some of the text on the
+following page would easily fit into the white space. The reason for such
+apparently anomalous behavior is almost always that no good page break
+is possible; even the alternative that looks better to you is quite terrible
+as far as \TeX\ is concerned! \TeX\ does not distinguish between two
+choices that both have 10000 units of badness or more, even though some bad
+breaks do look much worse than others. The solution in such cases is to
+insert |\eject| or |\vfill\eject| in some acceptable spot, or to
+revise the manuscript. If this problem arises frequently, however,
+you probably are using a format that sets overly strict limitations
+on page format; try looking at the output of\/ |\tracingpages| and
+modifying some of \TeX's parameters, until you have better luck.
+
+\danger The remainder of this chapter is about insertions: things like
+footnotes and ^{illustrations}, and how they interact with page breaks.
+Before we discuss the primitive operations by which \TeX\ deals with
+insertions, we will take a look at the facilities that plain \TeX\ provides
+at a higher level.
+
+\danger Illustrations can be inserted in several ways using plain \TeX.
+The simplest of these is called a ``^{floating topinsert}''; you say
+\begindisplay
+^|\topinsert|\<vertical mode material>^|\endinsert|
+\enddisplay
+and \TeX\ will attempt to put the vertical mode material at the top of the
+current page. If there's no room for such an insertion on this page, \TeX\
+will insert it at the top of the next page. The \<vertical mode material>
+can contain embedded paragraphs that temporarily interrupt vertical mode
+in the usual way; for example:
+\begintt
+\topinsert \vskip 2in
+\hsize=3in \raggedright
+\noindent{\bf Figure 3.} This is the caption to the
+third illustration of my paper. I have left two inches
+of space above the caption so that there will be room
+to introduce special artwork. \endinsert
+\endtt
+The ^{caption} in this example will be set ^{ragged-right} in a 3-inch
+column at the left of the page. Plain \TeX\ automatically adds a
+``^{bigskip}'' below each topinsert; this will separate the caption from
+the text. The effects of\/ |\hsize=3in| and |\raggedright| do not extend
+past the |\endinsert|, since ^{grouping} is implied.
+
+\dangerexercise Modify this example so that the caption is moved over next to
+the right margin, instead of appearing at the left.
+\answer |\topinsert\vskip2in\rightline{\vbox{\hsize|\stretch|...|\stretch
+|artwork.}}\endinsert|
+does the job. But it's slightly more efficient to avoid ^|\rightline| by
+changing ^|\leftskip| as follows:
+`|\leftskip=\hsize \advance\leftskip by-3in|'.
+Then \TeX\ doesn't have to read the text of the caption twice.
+
+\danger Similarly, if you say `^|\pageinsert|\stretch\<vertical mode
+material>\stretch |\endinsert|', the vertical mode material will be
+justified to the size of a full page (without a bigskip below it);
+the result will appear on the following page.
+
+\danger There's also `^|\midinsert|\stretch\<vertical mode material>\stretch
+|\endinsert|', which tries first to insert the material in place, wherever
+you happen to be, in the middle of the current page. If there is enough
+room, you get the effect of^^|\bigskip|^^|\bigbreak|
+\begindisplay
+|\bigskip\vbox{|\<vertical mode material>|}\bigbreak|
+\enddisplay
+otherwise the |\midinsert| is effectively converted to a |\topinsert|.
+There is a slight probability that |\midinsert| will not find the
+best placement, because \TeX\ is sometimes processing text ahead of the
+current page. You may want to say `^|\goodbreak|' just before |\midinsert|.
+
+\danger You should use the commands |\topinsert|, |\pageinsert|,
+|\midinsert| in vertical mode (i.e., between paragraphs), not inside of
+boxes or other insertions.
+
+\danger If you have two or more |\topinsert| or |\pageinsert| commands in
+quick succession, \TeX\ may need to carry them over to several subsequent
+pages; but they will retain their relative order when they are
+carried over. For example, suppose you have pages that are nine inches
+tall, and suppose you have already specified 4~inches of text for some
+page, say page~25. Then suppose you make seven topinserts in a row, of
+respective sizes $1,2,3,9,3,2,1$ inches; the 9-inch one is actually a
+|\pageinsert|. What happens? Well, the first and second will appear at
+the top of page 25, followed by the 4~inches of copy you have already
+typed; that copy will immediately be followed by two more inches that you
+type after the seven inserts. The third topinsert will appear at the top
+of page~26, followed by six more inches of text; the fourth will fill
+page~27; and the remaining three will appear at the top of page~28.
+
+\dangerexercise What would happen in the example just discussed if the final
+1-inch insertion were a |\midinsert| instead of a |\topinsert|?
+\answer It would appear on page~25, since it does fit there. A |\midinsert|
+will jump ahead of other insertions only if it is not carried over to another
+page; for example, if the second 3-inch insertion were a |\midinsert|, it would
+not appear on page~26, because it is converted to a |\topinsert| as soon as the
+|\midinsert| macro notices that the insertion is too big for page~25.
+
+\danger At the end of a paper, you probably want to make sure that no
+insertions are lost; and at the end of a chapter, you probably want to make
+sure that no insertions float into the following chapter. Plain \TeX\ will
+flush out all remaining insertions, with blank space filling the bottom of
+incomplete pages, if you say `|\vfill|^|\supereject|'.
+
+\danger Besides illustrations that are inserted at the top of a page,
+plain \TeX\ will also insert ^{footnotes} at the bottom of a page. The
+^|\footnote| macro is provided for use within paragraphs;\footnote*{Like
+this.} for example, the footnote in the present sentence was typed
+in the following way:
+\begintt
+... paragraphs;\footnote*{Like this.} for example, ...
+\endtt
+There are two parameters to a |\footnote|; first comes the ^{reference mark},
+which will appear both in the paragraph\footnote{**}{The author typed
+`|paragraph\footnote{**}{The author ...}|' here.} and in the footnote itself,
+and then comes the text of the footnote.\footnote{$^{45}$}{And
+`|footnote.\footnote{$^{45}$}{And ...}|' here. The footnotes in this manual
+appear in smaller type, and they are set with hanging indentation;
+furthermore a smallskip occurs between footnotes on the same page. But
+in plain \TeX, footnotes are typeset with the normal size of type, with
+|\textindent| used for the reference mark, and without extra smallskips.
+The |\textindent| macro is like |\item|, but it omits hanging
+indentation.}
+The latter text may be several paragraphs long, and it may contain
+displayed equations and such things, but it should not involve other
+^^|\textindent| ^^|\item| % these are in a split footnote, after the split!
+insertions. \TeX\ will ensure that each footnote occurs at the bottom of
+the same page as its reference.\footnote\dag{Printers often use the
+symbols |\dag| (\dag), |\ddag| (\ddag), |\S|~(\S), and |\P|~(\P) as
+reference marks; sometimes also |$\|\||$| ($\Vert$).
+You can say, e.g., `|\footnote\dag{...}|'.} A long
+footnote will be split, if necessary, and continued at the bottom of the
+following page, as you can see in the ^^|\dag|^^|\ddag|^^|\S|^^|\P|^^|\Vert|
+somewhat contrived example that appears here. Authors who are interested
+in good exposition should avoid footnotes whenever possible, since
+footnotes tend to be ^^{Gibbon} distracting.\footnote\ddag{Yet Gibbon's
+{\sl Decline and Fall\/} would not have been the same without footnotes.}
+
+\danger The |\footnote| macro should be used only in paragraphs or hboxes
+that are contributed to \TeX's main vertical list; insertions will be lost
+if they occur inside of boxes that are inside of boxes. Thus, for example,
+you should not try to put a |\footnote| into a subformula of a math formula.
+But it's OK to use footnotes within ^|\centerline|, e.g., ^^{Thor}
+\begintt
+\centerline{A paper by A. U. Thor%
+ \footnote*{Supported by NSF.}}
+\endtt
+or even on the outer level of a table entry inside an ^|\halign|.
+
+\ddanger Topinserts work fine by themselves, and footnotes work fine by
+themselves, but complications can arise when you try to mix them in
+devious ways. For example, if a |\pageinsert| floats to the page that
+follows a long footnote that had to be broken, both of the held-over
+insertions may try to force themselves onto the same page, and an overfull
+vbox may result. Furthermore, insertions cannot appear within insertions,
+so you can't use |\footnote| within a |\topinsert|. If you really need a
+footnote in some caption, there's a ^|\vfootnote| macro that can be used
+in vertical mode. To use it, you put a reference mark like~`|*|' in the
+caption, and then you say `|\vfootnote*{The footnote}|' somewhere on the
+page where you guess that the caption will finally fall. In such complex
+circumstances you might want to rethink whether or not you are really
+using the most appropriate format for the exposition of your ideas.
+
+\ddanger Chapter 24 explains the exact rules about ^{migration} of
+vertical-mode material (like footnotes) from horizontal lists to the
+enclosing vertical list. Insertions, marks, and the results of\/ ^|\vadjust|
+all migrate in the same fashion.
+
+\danger Now let's study the primitives of \TeX\ that are used to construct
+macros like |\topinsert| and |\footnote|. We are about to enter behind the
+scenes into a sublanguage of \TeX\ that permits users to do complex
+manipulations with boxes and glue. Our discussion will be in two parts:
+First we shall consider \TeX's ``^{registers},'' with which a user can
+do ^{arithmetic} related to typesetting; and then we shall discuss the insertion
+items that can appear in horizontal and vertical lists. Our discussion of the
+first topic (registers) will be marked with single dangerous-bend signs,
+since registers are of general use in advanced applications of \TeX,
+whether or not they relate to insertions. But the second topic will be marked
+with double dangerous-bend signs, since insertions are rather esoteric.
+
+\danger \TeX\ has 256 registers called ^|\count||0| to |\count255|, each capable
+of containing integers between $-2147483647$ and $+2147483647$, inclusive;
+^^\<number>
+i.e., the magnitudes should be less than $2^{31}$. \TeX\ also has 256 registers
+called ^|\dimen||0| to |\dimen255|, each capable of containing a ^\<dimen>
+(see Chapter~10). There are another 256 registers called ^|\skip||0| to
+|\skip255|, each containing ^\<glue> (see Chapter~12); and ^|\muskip||0| to
+|\muskip255|, each containing ^\<muglue> (see Chapter~18). You can assign
+new values to these registers by saying
+\begindisplay
+|\count|\<number> |=| \<number>\cr
+|\dimen|\<number> |=| \<dimen>\cr
+|\skip|\<number> |=| \<glue>\cr
+|\muskip|\<number> |=| \<muglue>\cr
+\enddisplay
+and then you can add or subtract values of the same type by saying^^|\advance|
+\begindisplay
+|\advance\count|\<number> |by| \<number>\cr
+|\advance\dimen|\<number> |by| \<dimen>\cr
+|\advance\skip|\<number> |by| \<glue>\cr
+|\advance\muskip|\<number> |by| \<muglue>\cr
+\enddisplay
+For example, `|\dimen8=\hsize \advance\dimen8 by 1in|' sets register
+|\dimen8| to an inch more than the current value of the normal line size.
+
+\danger If infinite glue components are added, lower order infinities disappear.
+For example, after the two commands
+\begintt
+\skip2 = 0pt plus 2fill minus 3fill
+\advance\skip2 by 4pt plus 1fil minus 2filll
+\endtt
+the value of\/ |\skip2| will be $4\pt$ plus $2\,{\rm fill}$ minus
+$2\,{\rm filll}$.
+
+\danger Multiplication and division are possible too, but only by integers.
+For example, `^|\multiply||\dimen4 by 3|' triples the value of\/ |\dimen4|, and
+`^|\divide||\skip5 by 2|' cuts in half all three components of the glue that is
+currently registered in |\skip5|.
+You shouldn't divide by zero, nor should you multiply by numbers that
+will make the results exceed the register capacities. Division of a positive
+integer by a positive integer discards the remainder, and the sign of the
+result changes if you change the sign of either operand. For example,
+14~divided by~3 yields~4; $-14$~divided by~3 yields~$-4$;
+$-14$~divided by~$-3$ yields~4. Dimension values are integer multiples
+of~^{sp} (scaled points).
+
+\danger You can use any |\count| register in the context of a ^\<number>,
+any |\dimen| register in the context of a ^\<dimen>, any |\skip| register
+in the context of ^\<glue>, and any |\muskip| register in the context of
+^\<muglue>. For example, `|\hskip\skip1|' puts horizontal glue into a
+list, using the value of\/ |\skip1|; and if\/ |\count5| is 20,
+the~command `|\advance\dimen20 by\dimen\count5|' is equivalent to
+`|\multiply\dimen20 by 2|'.
+
+\danger A |\dimen| register can be used also in the context of a \<number>,
+and a |\skip| register can be used as a \<dimen> or a \<number>.
+\TeX\ converts \<glue> to \<dimen> by omitting the stretch and shrink
+components, and it converts \<dimen> to \<number> by assuming units
+of~sp (scaled points). For example, if\/ |\skip1| holds the value
+$1\pt$ plus~$2\pt$, then `|\dimen1=\skip1|' sets |\dimen1| equal
+to~$1\pt$; and the commands `|\count2=\dimen1|' or `|\count2=\skip1|' will
+set |\count2| equal to~65536. These rules also apply to \TeX's internal
+parameters; for example, `|\dimen2=\baselineskip|' will set |\dimen2| to
+the natural space component of the current baselineskip glue.
+
+\dangerexercise Test your knowledge of \TeX's registers by stating the
+results of each of the following commands when they are performed in sequence:
+\begintt
+\count1=50 \dimen2=\count1pt \divide\count1 by 8
+\skip2=-10pt plus\count1fil minus\dimen2
+\multiply\skip2 by-\count1 \divide\skip2 by \dimen2 \count6=\skip2
+\skip1=.5\dimen2 plus\skip2 minus\count\count1fill
+\multiply\skip2 by\skip1 \advance\skip1 by-\skip2
+\endtt
+\answer Set |\count1| to 50,
+then |\dimen2| to~$50\pt$,
+then |\count1| to~6,
+then |\skip2| to~$-10\pt$ plus~$6\,{\rm fil}$ minus~$50\pt$,
+then |\skip2| to~$60\pt$ plus~$-36\,{\rm fil}$ minus~$-300\pt$,
+then |\skip2| to~$1\,{\rm sp}$ minus~$-6\,{\rm sp}$,
+then |\count6| to~1,
+then |\skip1| to~$25\pt$ plus~$1\,{\rm sp}$ minus~$1\,{\rm fill}$,
+then |\skip2| to~$25\pt$ minus~$-150\pt$,
+then |\skip1| to~$0\pt$ plus~$1\,{\rm sp}$ minus~$1\,{\rm fill}$.
+
+\dangerexercise What is in |\skip5| after the following three commands have
+acted?
+\begintt
+\skip5=0pt plus 1pt
+\advance\skip5 by \skip4 \advance\skip5 by -\skip4
+\endtt
+\answer If\/ |\skip4| has infinite stretchability, |\skip5| will be zero;
+otherwise it will be $0\pt$ plus~$1\pt$.
+
+\dangerexercise (For mathematicians.) Explain how to ^{round} |\dimen2|
+to the nearest multiple of\/ |\dimen3|, assuming that |\dimen3| is positive.
+\answer |\advance\dimen2 by\ifnum\dimen2<0 -\fi.5\dimen3|\parbreak
+|\divide\dimen2 by\dimen3 \multiply\dimen2 by\dimen3|
+
+\danger The registers obey \TeX's ^{group structure}. For example,
+changes to |\count3| inside |{...}| will not affect the value of
+|\count3| outside. Therefore \TeX\ effectively has more than 256
+registers of each type. If you want the effect of a register command
+to transcend its group, you must say ^|\global| when you change the value.
+
+\dangerexercise What is in |\count1| after the following sequence of commands?
+\begintt
+\count1=5 {\count1=2 \global\advance\count1by\count1
+ \advance\count1by\count1}
+\endtt
+\answer |\count1| takes the values 5, then~2 (the old 5 is saved),
+then~4 (which is made global), then~8 (and 4~is saved); finally the value~4 is
+restored, and that is the answer. \ (For further remarks, see the discussion
+of\/ |\tracingrestores| in Chapter~27.)
+
+\danger The first ten |\count| registers, |\count0| through |\count9|,
+are reserved for a special purpose:
+\TeX\ displays these ten counts on your terminal whenever outputting
+a page, and it transmits them to the output file as an identification
+of that page. The counts are separated by decimal points on your terminal,
+with trailing `|.0|' patterns suppressed. Thus, for example, if
+|\count0=5| and |\count2=7| when a page is being shipped out to the
+^|dvi| file, and if the other count registers are zero, \TeX\ will
+type `|[5.0.7]|'. Plain \TeX\ uses |\count0| for the page number, and it
+keeps |\count1| through~|\count9| equal to zero; that is why you see just
+`^|[1]|' when page~1 is being output. In more complex applications the
+page numbers can have further structure; ten counts are shipped out so
+that there will be plenty of identification.
+
+\danger It's usually desirable to have symbolic names for registers.
+\TeX\ provides a ^|\countdef| command (similar to |\chardef|,
+cf.~Chapter~8), which makes it easy to do this: You just say
+\begintt
+\countdef\chapno=28
+\endtt
+and |\chapno| is henceforth an abbreviation for |\count28|. Similar
+commands ^|\dimendef|, ^|\skipdef|, and ^|\muskipdef| are available
+for the other types of numeric registers. After a control sequence
+has been defined by |\countdef|, it can be used in \TeX\ commands exactly
+as if it were an integer ^{parameter} like |\tolerance|. Similarly,
+|\dimendef| effectively creates a new dimension parameter, |\skipdef|
+effectively creates a new glue parameter, and |\muskipdef| effectively
+creates a new muglue parameter.
+
+\danger Besides the numerical registers, \TeX\ also has 256 box registers
+called ^|\box||0| to |\box255|. A~box register gets a value when you say
+^|\setbox|\<number>|=|^\<box>; for example, `|\setbox3=\hbox{A}|' sets
+|\box3| to an hbox that contains the single letter~|A|. Several other
+examples of\/ |\setbox| have already appeared in Chapter~12. Chapter~10
+points out that `|2\wd3|' is a \<dimen> that represents twice the width
+of\/ |\box3|; similarly, ^|\ht|\<number> and ^|\dp|\<number> can be used to
+refer to the height and depth of a given box register. ^^|\wd|
+
+\danger Box registers are local to groups just as arithmetic registers are.
+But there's a big difference between box registers and all the rest: When
+you use a |\box|, it loses its value. For example, the construction
+`|\raise2pt\box3|' in a horizontal list not only puts the contents of
+|\box3| into the list after raising it by~$2\pt$, it also makes |\box3|
+void. \TeX\ does this for efficiency, since it is desirable to avoid copying
+the contents of potentially large boxes. If you want to use a box register
+without wiping out its contents, just say `^|\copy|' instead of `|\box|';
+for example, `|\raise2pt\copy3|'.
+
+\danger Another way to use a box register is to extract the inside of
+an hbox by saying `^|\unhbox|'. This annihilates the contents of the
+register, like `|\box|' does, and it also removes one level of boxing. For
+example, the commands
+\begintt
+\setbox3=\hbox{A} \setbox3=\hbox{\box3 B}
+\setbox4=\hbox{A} \setbox4=\hbox{\unhbox4 B}
+\endtt
+put |\hbox{\hbox{A}B}| into |\box3| and |\hbox{AB}| into |\box4|.
+Similarly, ^|\unvbox| unwraps a vbox. If you want to construct a large
+box by accretion (e.g., a ^{table of contents}), it is best to use
+|\unhbox| or |\unvbox| as in the |\setbox4| example; otherwise you use
+more of \TeX's memory space, and you might even obtain boxes inside boxes
+nested to such a deep level that hardware or software limits are exceeded.
+
+\danger The operations ^|\unhcopy| and ^|\unvcopy| are related to
+|\unhbox| and |\unvbox| as |\copy| is to |\box|. \ (But their
+names are admittedly peculiar.)
+
+\danger An unboxing operation ``unsets'' any glue that was set at the box's
+outer level. For example, consider the sequence of commands
+\begintt
+\setbox5=\hbox{A \hbox{B C}} \setbox6=\hbox to 1.05\wd5{\unhcopy5}
+\endtt
+This makes |\box6| five percent wider than |\box5|;
+the glue between |A| and |\hbox{B C}| stretches to
+make the difference, but the glue inside the inner hbox does not change.
+
+\danger A box register is either ``^{void}'' or it contains an hbox or a vbox.
+There is a difference between a void register and one that contains an
+empty box whose height, width, and depth are zero; for example, if\/ |\box3|
+is void, you can say |\unhbox3| or |\unvbox3| or |\unhcopy3| or |\unvcopy3|,
+but if\/ |\box3| is equal to |\hbox{}| you can say only |\unhbox3| or
+|\unhcopy3|. If you say `|\global\setbox3=|\<box>', register |\box3| will
+become ``globally void'' when it is subsequently used or unboxed.
+
+\dangerexercise What is in register |\box5| after the following commands?
+\begintt
+\setbox5=\hbox{A} \setbox5=\hbox{\copy5\unhbox5\box5\unhcopy5}
+\endtt
+\answer |\hbox{\hbox{A}A}|. After `|\unhbox5|', |\box5| is void; |\unhcopy5|
+yields nothing.
+
+\dangerexercise And what's in |\box3| after ^^{grouping with box registers}
+`|{\global\setbox3=\hbox{A}\setbox3=\hbox{}}|'?\kern-1pt
+\answer |\hbox{A}|. But after `|{\global\setbox3=\hbox{A}\setbox3=\box3}|',
+|\box3| will be void.
+
+\danger If you are unsure about how \TeX\ operates on its registers, you can
+experiment online by using certain `|\show|' commands. For example,
+\begintt
+\showthe\count1 \showthe\dimen2 \showthe\skip3
+\endtt
+will display the contents of\/ |\count1|, |\dimen2|, and |\skip3|; and
+`^|\showbox||4|' will display the contents of\/ |\box4|. ^^|\showthe|
+Box contents will appear only in the log file, unless you say
+`|\tracingonline=1|'. Plain \TeX\ provides a macro `^|\tracingall|'
+that turns on every possible mode of interaction, including
+^|\tracingonline|. The author used these features to check the answers to
+several of the exercises above.
+
+\danger Large applications of \TeX\ make use of different sets of macros
+written by different groups of people. Chaos would reign if a register
+like |\count100|, say, were being used simultaneously for different purposes
+in different macros. Therefore plain \TeX\ provides an ^{allocation} facility;
+cooperation will replace confusion if each ^{macro writer} uses these
+conventions. The idea is to say, e.g., `^|\newcount|' when you want
+to dedicate a |\count| register to a special purpose. For example, the
+author designed a macro called `|\exercise|' to format the exercises
+in this manual, and one of the features of\/ |\exercise| is that it
+computes the number of the current exercise. The format
+macros in Appendix~E reserve a |\count| register for this purpose by saying
+\begintt
+\newcount\exno
+\endtt
+and then the command `|\exno=0|'
+is used at the beginning of each chapter. Similarly, `|\advance\exno by1|'
+is used whenever a new exercise comes along, and `|\the\exno|'
+is used to typeset the current exercise number. The |\newcount| operation
+assigns a unique count register to its argument |\exno|, and it defines
+|\exno| with a ^|\countdef| command. All of the other format macros
+are written without the knowledge of exactly which |\count| register
+actually corresponds to |\exno|.
+
+\danger Besides |\newcount|, plain \TeX\ provides ^|\newdimen|,
+^|\newskip|, ^|\newmuskip|, and ^|\newbox|; there also are ^|\newtoks|,
+^|\newread|, ^|\newwrite|, ^|\newfam|, and ^|\newinsert|, for
+features we haven't discussed yet. Appendices~B and~E contain
+several examples of the proper use of allocation. In the cases of
+|\newbox|, |\newread|, etc., the allocated number is defined by |\chardef|.
+For example, if the command `|\newbox\abstract|' is used to define a
+box register that will contain an abstract, and if the |\newbox| operation
+decides to allocate |\box45| for this purpose, then it defines the
+meaning of\/ |\abstract| by saying `|\chardef\abstract=45|'. \TeX\ allows
+^|\chardef|'d quantities to be used as integers, so that you can say
+|\box\abstract| and |\copy\abstract|, etc. \ (There is no |\boxdef|
+command.)
+
+\newcount\notenumber
+\def\clearnotenumber{\notenumber=0\relax}
+\def\note{\advance\notenumber by1 \footnote{$^{\the\notenumber}$}}
+\clearnotenumber
+\dangerexercise Design a |\note| macro that produces footnotes numbered
+sequentially. For example,\note{First note.} it should produce
+the footnotes here\note{Second note.} if you type
+\begintt
+... example,\note{First note.} it should produce
+the footnotes here\note{Second note.} if ...
+\endtt
+(Use |\newcount| to allocate a |\count| register for the footnotes.)
+\answer |\newcount\notenumber|\parbreak
+|\def\clearnotenumber{\notenumber=0\relax}|\parbreak
+|\def\note{\advance\notenumber by 1|\parbreak
+| \footnote{$^{\the\notenumber}$}}|
+
+\danger Sometimes, however, you want to use a register just for temporary
+storage, and you know that it won't conflict with anybody else's macros.
+Registers |\count255|, |\dimen255|, |\skip255|, and |\muskip255| are
+traditionally kept available for such purposes. Furthermore, plain \TeX\
+reserves |\dimen0| to |\dimen9|, |\skip0| to |\skip9|, |\muskip0| to
+|\muskip9|, and |\box0| to |\box9| for ``scratchwork''; these registers
+are never allocated by the |\new...|\null\ operations. We have seen that
+|\count0| through |\count9| are special, and |\box255| also turns out to
+be special; so those registers should be avoided unless you know what you
+are doing.
+
+\ddanger Of course any register can be used for short-term purposes inside
+a group (including |\count0| to |\count9| and |\box255|, and including
+registers that have been allocated for other purposes), since register
+changes are local to ^{groups}. However, you should be sure that \TeX\
+will not output any pages before the group has ended, because output
+routines might otherwise be invoked at unfortunate times. \TeX\ is
+^^{output routines, when invoked}^^{page builder, when exercised}
+liable to invoke an output routine whenever it tries to move something
+from the list of recent contributions to the current page, because it
+might discover a page break with $c=\infty$ then. Here is a list of the
+times when that can happen: (a)~At the beginning or end of a paragraph,
+provided that this paragraph is being contributed to the main vertical
+list. (b)~At the beginning or end of a displayed equation within such a
+paragraph. (c)~After completing an |\halign| in vertical mode. (d)~After
+contributing a box or penalty or insertion to the main vertical list.
+(e)~After an |\output| routine has ended.
+
+\ddanger Now that we are armed with the knowledge of \TeX's flexible
+registers, we can plunge into the details of insertions. There are 255
+classes of insertions, |\insert0| to |\insert254|, and they are tied
+to other registers of the same number. For example, |\insert100| is
+connected with |\count100|, |\dimen100|, |\skip100|, and |\box100|.
+Therefore plain \TeX\ provides an allocation function for insertions
+as it does for registers; Appendix~B includes the command
+\begintt
+\newinsert\footins
+\endtt
+^^|\newinsert|
+which defines |\footins| as the number for footnote insertions. Other
+commands that deal with footnotes refer to |\count\footins|, |\dimen\footins|,
+and so on. The macros for floating topinserts are similarly prefaced by
+`|\newinsert\topins|', which defines |\topins| as the number of their
+class. Each class of insertions is independent, but \TeX\ preserves the
+order of insertions within a class. It turns out that |\footins| is class~254,
+and |\topins| is class~253, but the macros do not use such numbers directly.
+
+\def\n{\thinspace$n$}
+\ddanger For our purposes let's consider a particular class of insertions
+called class $n$; we will then be dealing with \TeX's primitive command
+^^|\insert|
+\begindisplay
+|\insert|\n|{|\<vertical mode material>|}|
+\enddisplay
+which puts an insertion
+item into a horizontal or vertical list. For this class of insertions
+\begindisplay
+|\box|\n\ is where the material appears when a page is output;\cr
+|\count|\n\ is the magnification factor for page breaking;\cr
+|\dimen|\n\ is the maximum insertion size per page;\cr
+|\skip|\n\ is the extra space to allocate on a page.\cr
+\enddisplay
+For example, material inserted with |\insert100| will
+eventually appear in |\box100|.
+
+\ddanger Let the natural height plus depth of\/ |\insert|\n\ be $x$; then
+|\count|\n\ is 1000 times the factor by which $x$~affects the page
+goal. For example, plain \TeX\ sets |\count\footins=1000|, since
+there is a one-to-one relationship:
+A 10-point footnote effectively makes a page $10\pt$ shorter. But if we
+have an application where footnotes appear in double columns, a count
+value of 500 would be appropriate. One of the insertion classes in Appendix~E
+makes marginal notes for proofreading purposes; in that case the
+count value is zero. No actual magnification is done; |\count|\n\
+is simply a number used for bookkeeping, when estimating the
+costs of various page breaks.
+
+\ddanger The first footnote on a page requires extra space, since we want
+to separate the footnotes from the text, and since we want to output a
+horizontal rule. Plain \TeX\ sets
+`|\skip\footins=|^|\bigskipamount|'; this means that a bigskip of
+extra space is assumed to be added by the output routine to any page that
+contains at least one insertion of class |\footins|.
+
+\ddanger Sometimes it is desirable to put a maximum limitation on the size
+of insertions; for example, people usually don't want an entire page to
+consist of footnotes. Plain \TeX\ sets |\dimen\footins=8in|; this means
+that |\box\footins| is not supposed to accumulate more than 8~inches of
+footnotes for any one page.
+
+\ddanger You might want to review the page-breaking algorithm explained
+at the beginning of this chapter, before reading further.
+On the other hand, maybe you don't really want to read the rest of
+this chapter at all, ever.
+
+\ddanger Here now is the algorithm that \TeX\ performs when an |\insert|\n\
+is moved from the ``recent contributions'' to the ``current page.'' \
+(Remember that such a move does not mean that the insertion will actually take
+place; the current page will be backed up later, to the breakpoint of
+least cost, and only the insertions preceding that breakpoint will
+actually be performed.) \ Let $g$ and $t$ be the current ^|\pagegoal| and
+^|\pagetotal|; let $q$ be the ^|\insertpenalties| accumulated for the
+current page; and let $d$ and $z$ be the current ^|\pagedepth| and
+^|\pageshrink|. \ (The value of~$d$ is at most ^|\maxdepth|; this value
+has not yet been incorporated into $t$.) \ Finally, let $x$ be the
+natural height plus depth of the |\insert|\n\ that we are moving to the
+current page; and let $f$ be the corresponding magnification factor,
+i.e., |\count|\n\ divided by 1000.
+
+\ninepoint
+\textindent{\bf Step 1.} If there is no previous |\insert|\n\ on the
+current page, decrease $g$ by $hf+w$, where $h$ is the current height plus
+depth of\/ |\box|\n, and where $w$ is the natural space component of
+|\skip|\n; also include the stretch and shrink components of
+|\skip|\n\ in the totals for the current page (in particular, this
+affects~$z$).
+
+\medbreak
+\textindent{\bf Step 2.} If a previous |\insert|\n\ on the current page
+has been split, add the parameter called
+^|\floatingpenalty| to~$q$, and omit Steps 3 and~4.
+
+\medbreak
+\textindent{\bf Step 3.} Test if the current insertion will fit on the page
+without splitting. This means that it won't make the natural height-plus-depth
+of\/ |\box|\n\ surpass |\dimen|\n, when it is added to |\box|\n\ together with
+all previous |\insert|\n\ amounts on the current page; furthermore, it
+means that either $xf\le0$ or $t+d+xf-z\le g$. If both tests are passed,
+subtract $xf$ from~$g$ and omit Step~4.
+
+\medbreak
+\textindent{\bf Step 4.} (The current insertion will be split, at least
+tentatively; but the split will not actually take place if the least-cost
+page turns out to have occurred earlier than the present insertion.) \
+First compute the largest amount~$v$ such that a height plus depth of $v$
+will not make the total insertions into |\box|\n\ bigger than |\dimen|\n,
+and such that $t+d+vf\le g$. \ (Notice that $z$ is omitted from the latter
+formula, but the available shrinkability was considered in Step~3 when we
+tried to avoid splitting.) \ Then find the least-cost way to split the
+beginning of the vertical list of the insertion so as to obtain a box of
+height~$v$. \ (Use an algorithm just like page-breaking, but without the
+complexity of insertion; an additional `|\penalty-10000|' item is assumed
+to be present at the end of the vertical list, to ensure that a legal
+breakpoint exists.) \ Let $u$ be the natural height plus depth of that
+least-cost box, and let $r$ be the penalty associated with the optimum
+breakpoint. Decrease $g$ by~$uf$, and increase $q$ by~$r$. \ (If
+|\tracingpages||=1|, the log file should now get a cryptic message that says
+`|% split|\n\ |to| $v$|,|$u$ |p=|$r$'. For~example,^^{split insertion penalty}
+\begintt
+% split254 to 180.2,175.3 p=100
+\endtt
+^^{percent split}
+means that \TeX\ has tried to split an |\insert254| to height $180.2\pt$;
+the natural height-plus-depth of the best such split is $175.3\pt$,
+and the penalty for breaking there is~100.)
+
+\ddanger This algorithm is admittedly complicated, but no simpler mechanism
+seems to do nearly as much. Notice that penalties of $-10000$ inside
+insertions will make certain splits very attractive in Step~4, so the
+user can provide hints about where to break, in difficult situations.
+The algorithm provides a variety of different behaviors:
+Floating insertions can be accommodated
+as a special case of split insertions, by making each floating topinsert
+start with a small penalty, and by having
+zero as the associated |\floatingpenalty|; non-floating insertions
+like footnotes are accommodated by associating larger penalties with
+split insertions (see Appendix~B).
+
+\ddanger The splitting operation mentioned in Step 4 is also available as
+a primitive: `^|\vsplit|\<number> |to|\<dimen>' produces a vbox obtained
+by splitting off a speci\-fied amount of material from a box register.
+For example,
+\begintt
+\setbox200=\vsplit100 to 50pt
+\endtt
+sets |\box200| to a vbox whose height is $50\pt$; it goes through the
+vertical list inside |\box100| (which should be a vbox) and finds the
+least-cost break assuming a goal height of~$50\pt$, considering badnesses and
+penalties just as in the case of page-breaking (but with $q=0$). The
+algorithm uses ^|\splitmaxdepth| instead of\/ ^|\maxdepth| to govern the
+maximum depth of boxes. Then it prunes the top of\/ |\box100| by removing
+everything up to and including any ^{discardable} items that immediately
+follow the optimum breakpoint; and it uses ^|\splittopskip| to insert new
+glue before the first box inside |\box100|, just as
+^|\topskip| glue appears at the top of a page. However, if the optimum
+breakpoint occurs at the end of the vertical list inside |\box100|---a
+`|\penalty-10000|' item is assumed to be present there---or if all items
+after the optimum breakpoint are discarded, |\box100| will
+be void after the |\vsplit|. And if\/ |\box100| was void before the
+|\vsplit|, both |\box100| and |\box200| will be void afterwards.
+
+\ddanger \looseness=-1
+You'd better not change |\box|\n, |\count|\n, |\dimen|\n,
+or |\skip|\n\ while \TeX\ is contributing insertions to the current page,
+since \TeX's algorithm assumes that those quantities are static.
+But you can change ^|\floatingpenalty|, |\splittopskip|, and |\splitmaxdepth|;
+\TeX\ will use the values that were current just inside the closing right
+brace of `|\insert|\n|{...}|' when it splits and floats insertions.
+For example, Appendix~B uses |\floatingpenalty=20000| in footnote
+insertions, to discourage footnotes that split before others
+can start, \vadjust{\break}but |\floatingpenalty=0| in floating topinserts.
+Appendix~B also uses special values of\/ |\splittopskip| and |\splitmaxdepth|,
+together with ^{struts}, so that split footnotes will be typeset with
+the same spacing as unsplit ones.
+
+\ddanger The |\footnote| macro puts an |\insert| into the horizontal list
+of a paragraph. After the paragraph has been broken into lines, this
+insertion will move out into the vertical list just after the line that
+contained it (see Chapter~14). Since there is no legal breakpoint between
+that box (i.e., that line) and the insertion, \TeX\ will put the insertion
+onto the page that contains the line that contains the insertion.
+
+\ddangerexercise Study the page-breaking algorithm carefully. Is it
+possible that a footnote might not appear on the same page as its reference?
+\answer Yes, in severe circumstances. (1)~Previous footnotes might
+ have left no room for any more footnotes on the page.
+(2)~If |\vadjust{\eject}| occurs on the same line as a footnote, before that
+footnote, the reference will be forcibly detached. (3)~Other |\vadjust|
+commands on that line could also interpose breakpoints before the insertion.
+
+\ddanger \looseness=-1
+When the best page break is finally chosen, \TeX\ removes everything after
+the chosen breakpoint from the bottom of the ``current page,'' and puts it
+all back at the top of the ``recent contributions.'' The
+chosen breakpoint itself is placed at the very top of the recent contributions.
+If it is a penalty item, the value of the penalty is recorded in
+^|\outputpenalty| and the penalty in the contribution list is changed
+to $10000$; otherwise |\outputpenalty| is set to 10000.
+The insertions that remain on the current page are of three kinds: For
+each class~$n$ there are unsplit insertions, followed possibly by
+a single split insertion, followed possibly by others. If
+^|\holdinginserts|$\null>0$, all insertions remain in place (so that
+they might be contributed again); otherwise they are all removed from
+the current page list as follows: The unsplit insertions
+are appended to |\box|\n, with no ^{interline glue} between them. \
+(^{Struts} should be used, as in the |\vfootnote| macro of
+Appendix~B\null.) \
+If a split insertion is present, it is effectively |\vsplit| to the size
+that was computed previously in Step~4; the top part is treated as an
+unsplit insertion, and the remainder (if any) is converted to an insertion
+as if it had not been split. This remainder, followed by any other floating
+insertions of the same class, is held
+over in a separate place. \ (They will show up on the ``current page'' if
+^|\showlists| is used while an ^|\output| routine is active; the total
+number of such insertions appears in ^|\insertpenalties| during an
+|\output| routine.) %\
+Finally, the remaining items before the best break on the current page are put
+together in a |\vbox| of height~$g$, where $g$ was the |\pagegoal| at the
+time of the break, using the saved value of\/ ^|\maxdepth|; this box becomes
+|\box255|. Now the user's ^|\output| ^^|\box255|
+routine enters \TeX's scanner (see Chapter~23); its duty is to assemble the
+final pages based on the contents of\/ |\box255| and any insertion boxes
+that it knows about. The output routine will probably unbox those boxes,
+so that their glue can be reset; the glue in insertion boxes usually
+cooperates nicely with the glue on the rest of the page, when it is
+given a chance. After the |\output| routine is finished, ^{held-over
+insertion} items are placed first on the list of recent contributions, followed
+by the vertical list constructed by |\output|, followed by the recent
+contributions beginning with the page break. \ (Deep breath.) \ You got that?
+
+\endchapter
+
+Since it is impossible to foresee how [footnotes] will happen to come out
+in the make-up, it is impracticable to number them from 1 up on each page.
+The best way is to number them consecutively throughout an article
+or by chapters in a book.
+\author UNIVERSITY OF ^{CHICAGO} PRESS, {\sl Manual of Style\/} (1910) % p102
+
+\bigskip
+
+Don't use footnotes in your books, Don.
+\author JILL ^{KNUTH} (1962)
+
+\eject
+ \beginchapter Chapter 16. Typing\\Math Formulas
+
+\TeX\ is designed to handle complex ^{mathematical expressions} in such a way
+that most of them are easy to input. The basic idea is that a complicated
+formula is composed of less complicated ^{formulas} put together in a
+simple way; the less complicated formulas are, in turn, made up of simple
+combinations of formulas that are even less complicated; and so on. Stating
+this another way, if you know how to type simple formulas and how to combine
+formulas into larger ones, you will be able to handle virtually any formula
+at all. So let's start with simple ones and work our way up.
+
+The simplest formula is a single letter, like `$x$', or a single number,
+like `2'. In order to put these into a \TeX\ text, you type `|$x$|' and
+`|$2$|', respectively. Notice that all mathematical formulas are enclosed
+in special math brackets; we are using |$| as the math bracket in this
+manual, in accord with the plain \TeX\ format defined in Appendix~B\null,
+because mathematics is supposedly expensive.
+
+When you type `|$x$|' the `$x$' comes out in italics, but when you
+type `|$2$|' the `$2$' comes out in roman type. In general, all
+characters on your keyboard have a special interpretation in math
+formulas, according to the normal conventions of mathematics printing:
+Letters now denote ^{italic} letters, while digits and punctuation
+denote ^{roman} digits and punctuation; a hyphen ({\tt-}) now denotes a ^{minus
+sign} ($-$), which is almost the same as an em-dash but not quite (see
+Chapter~2). The first |$| that you type puts you into ``^{math mode}'' and the
+second takes you out (see Chapter~13). So if you forget one |$| or type
+one |$| too many, \TeX\ will probably become thoroughly confused and you
+will probably get some sort of error message. ^^{dollarsign}
+
+Formulas that have been typeset by a printer who is unaccustomed to
+mathematics usually look quite strange to a mathematician, because a
+novice printer usually gets the spacing all wrong. In order to alleviate
+this problem, \TeX\ does most of its own spacing in math formulas; and it
+{\sl ignores\/} any ^{spaces} that you yourself put between |$|'s. For
+example, if you type `|$ x$|' and `|$ 2 $|', they will mean the same thing
+as `|$x$|' and `|$2$|'. You can type `\hbox{|$(x + y)/(x - y)$|}' or
+`|$(x+y) / (x-y)$|', but both will result in `$(x+y)/(x-y)$', a formula in
+which there is a bit of extra space surrounding the $+$ and~$-$ signs but
+none around the~/~sign. Thus, you do not have to memorize the complicated
+rules of math spacing, and you are free to use blank spaces in any way you
+like. Of course, spaces are still used in the normal way to mark the end
+of control sequences, as explained in Chapter~3. In most circumstances
+\TeX's spacing will be what a mathematician is accustomed to; but we will
+see in Chapter~18 that there are control sequences by which you can
+override \TeX's spacing rules if you want to.
+
+One of the things mathematicians like to do is make their formulas look
+like ^{Greek} to the uninitiated. In plain \TeX\ language you can type
+`|$$\alpha, \beta, \gamma, \delta;$$|' and you will get the first four
+Greek letters ^^|\alpha|^^|\beta|^^|\gamma|^^|\delta|
+$$\alpha,\beta,\gamma,\delta;$$ furthermore there are uppercase Greek
+letters like `$\Gamma$', which you can get by typing `|$\Gamma$|'.
+^^|\Gamma| Don't feel intimidated if you aren't already familiar with Greek
+letters; they will be easy to learn if you need them. The only difficulty
+is that some symbols that look nearly the same must be carefully
+distinguished. For example, the Greek letters ^|\nu|~($\nu$) and
+^|\kappa|~($\kappa$) should not be confused with the italic letters $v$
+and~$x$; the Greek ^|\phi|~($\phi$) is different from the slashed zero
+called ^|\emptyset|~($\emptyset$). A~lowercase epsilon ($\epsilon$) is
+quite different from the symbol used to denote membership in a set
+($\in$); type `|$\epsilon$|' for $\epsilon$ and `|$\in$|' for $\in$.
+^^|\epsilon| ^^|\in| Some of the lowercase Greek letters have variant
+forms in plain \TeX's math italic fonts:
+`|$(\phi,\theta,\epsilon,\rho)$|' yields
+`$(\phi,\theta,\epsilon,\rho)$' while
+`|$(\varphi,\vartheta,\varepsilon,\varrho)$|' yields
+`$(\varphi,\vartheta,\varepsilon,\varrho)$'.
+^^|\phi|^^|\theta|^^|\rho|^^|\varphi|^^|\vartheta|^^|\varrho|^^|\varepsilon|
+
+Besides Greek letters, there are a lot of ^{funny symbols} like `$\approx$'
+(which you get by typing `|$\approx$|') ^^|\approx|^^{special symbols for math}
+and `$\mapsto$' (which you ^^|\mapsto|^^{math symbols}
+get by typing `|$\mapsto$|').
+ A complete list of these control sequences
+and the characters they correspond to appears in Appendix~F\null.
+Such control sequences are allowed only in math mode, i.e., between
+|$|'s, because the corresponding symbols appear in the math fonts.
+
+\exercise What should you type to get the formula
+`$\gamma+\nu\in\Gamma$'\thinspace?
+\answer |$\gamma+\nu\in\Gamma$|.
+
+\exercise Look at Appendix F to discover the control sequences for
+`$\le$', `$\ge$', and~`$\ne$'. \ (These are probably the three most
+commonly used math symbols that are not present on your keyboard.)
+\ What does plain \TeX\ call them?
+\answer ^|\le|, ^|\ge|, and ^|\ne|. \ (These are short for ``less-or-equal,''
+``greater-or-equal,'' and ``not-equal.'') \ You can also use the names
+^|\leq|, ^|\geq|, and ^|\neq|. \ (The fourth most common symbol is, perhaps,
+`$\infty$', which stands for ``^{infinity}'' and is called `^|\infty|'.)
+
+Now let's see how the more complex formulas get built up from simple ones.
+In the first place, you can get ^{superscripts} $\rm^{(up\,high)}$ and
+^{subscripts} $\rm_{(down\,low)}$
+^^{indices, see subscripts}
+^^{superiors, see superscripts} ^^{inferiors, see subscripts}
+by using `|^|' and `|_|', as shown in the following examples:
+\beginmathdemo
+\it Input&\it Output\cr
+\noalign{\vskip2pt}
+|$x^2$|&x^2\cr
+|$x_2$|&x_2\cr
+|$2^x$|&2^x\cr
+|$x^2y^2$|&x^2y^2\cr
+|$x ^ 2y ^ 2$|&x ^ 2y ^ 2\cr
+|$x_2y_2$|&x_2y_2\cr
+|$_2F_3$|&_2F_3\cr
+\endmathdemo
+Notice that |^| and |_| apply only to the next single character.
+If you want several things to be superscripted or subscripted, just enclose
+them in braces:
+\beginmathdemo
+|$x^{2y}$|&x^{2y}\cr
+|$2^{2^x}$|&2^{2^x}\cr
+|$2^{2^{2^x}}$|&2^{2^{2^x}}\cr
+|$y_{x_2}$|&y_{x_2}\cr
+|$y_{x^2}$|&y_{x^2}\cr
+\endmathdemo
+The braces in these examples have been used to specify ``^{subformulas},''
+i.e., simpler parts of a larger formula. \TeX\ makes a box for each
+subformula, and treats that box as if it were a single symbol. Braces
+also serve their usual purpose of grouping, as discussed in Chapter~5.
+
+It is illegal to type `|x^y^z|' or `|x_y_z|'; \TeX\ will
+complain of a ``double superscript'' or ``double subscript.'' You must type
+`|x^{y^z}|' or `|x^{yz}|' or
+`|x_{y_z}|' or `|x_{yz}|' in order to make your intention clear.
+
+A superscript or subscript following a character applies to that character
+only; but when following a subformula it applies to that whole subformula,
+and it will be raised or lowered accordingly. For example,
+\beginmathdemo
+|$((x^2)^3)^4$|&((x^2)^3)^4\cr
+|${({(x^2)}^3)}^4$|&{({(x^2)}^3)}^4\cr
+\endmathdemo
+In the first formula the `|^3|' and `|^4|' are superscripts on the ^{right
+parentheses}, i.e., on the `|)|' characters that immediately precede them,
+but in the second formula they are superscripts on the subformulas that
+are enclosed in braces. The first alternative is preferable, because it is
+much easier to type and it is just as easy to read.
+
+\danger A subscript or superscript following nothing (as in the `|_2F_3|'
+example on the preceding page, where the `|_2|' follows nothing) is taken
+to mean a subscript or superscript of an empty subformula. Such notations
+are (fortunately) rare in mathematics; but if you do encounter them it is
+better to make your intention clear by showing the empty subformula
+explicitly with braces. In other words, the best way to get `${}_2F_3$'
+in a formula is to type `|{}_2F_3|' or `|{_2}F_3|' or `|{_2F_3}|'.
+
+\dangerexercise What difference, if any, is there between the output of
+`|$x + _2F_3$|' and the output of `|$x + {}_2F_3$|'\thinspace?
+\answer In the former, the `|_2|' applies to the plus sign ($x + _2F_3$);
+but in the latter, it applies to an empty subformula ($x + {}_2F_3$).
+
+\dangerexercise Describe the differences between the outputs of `|${x^y}^z$|'
+and `|$x^{y^z}$|'.
+\answer The results are `${x^y}^z$' and `$x^{y^z}$'; the $z$ in the first
+alternative is the same size as the $y$, but in the second it is smaller.
+Furthermore, the $y$ and $z$ in the first case aren't quite at the same height.
+\ (Good typists never even think of the first construction, because
+mathematicians never want it.)
+
+You can have simultaneous subscripts and superscripts, and you can specify them
+in any order:
+\beginmathdemo
+|$x^2_3$|&x^2_3\cr
+|$x_3^2$|&x_3^2\cr
+|$x^{31415}_{92}+\pi$|&x^{31415}_{92}+\pi\cr
+\noalign{\smallskip}
+|$x_{y^a_b}^{z_c^d}$|&x_{y^a_b}^{z_c^d}\cr
+\endmathdemo
+Notice that simultaneous su$\rm_b^{per\kern-1pt}$scripts are positioned
+over each other. However, a subscript will be ``tucked in'' slightly when it
+follows certain letters; for example, `|$P_2^2$|' produces `$P_2^2$'.
+If for some reason you want the left edges of both subscript and superscript
+to be aligned, you can fool \TeX\ by inserting a null subformula:
+`|$P{}_2^2$|' produces `$P{}_2^2$'.
+
+The control sequence ^|\prime| stands for the symbol `$\prime$', which
+is used mostly in superscripts. In fact, `$\prime$' is so big as it stands
+that you would never want to use it except in a subscript or superscript,
+where it occurs in a smaller size. Here are some typical examples:
+\beginmathdemo
+\it Input&\it Output\cr
+\noalign{\vskip2pt}
+|$y_1^\prime$|&y_1^\prime\cr
+|$y_2^{\prime\prime}$|&y_2^{\prime\prime}\cr
+|$y_3^{\prime\prime\prime}$|&y_3^{\prime\prime\prime}\cr
+\endmathdemo
+Since single and double primes occur rather frequently, plain \TeX\
+provides a convenient abbreviation: You can simply type |'| instead
+of |^\prime|, and |''| instead of |^{\prime\prime}|, and so on.
+\beginmathdemo
+|$f'[g(x)]g'(x)$|&f'[g(x)]g'(x)\cr
+|$y_1'+y_2''$|&y_1'+y_2''\cr
+|$y'_1+y''_2$|&y'_1+y''_2\cr
+|$y'''_3+g'^2$|&y'''_3+g'{}^2\cr
+\endmathdemo
+
+\dangerexercise Why do you think \TeX\ treats |\prime| as a large symbol
+that appears only in superscripts, instead of making it a smaller
+symbol that has already been shifted up into the superscript position?
+\answer The second alternative doesn't work properly when there's a
+subscript at the same time as a prime. Furthermore, some mathematicians
+use |\prime| also in the subscript position; they write, for example,
+$F'(w,z)=\partial F(w,z)/\partial z$ and $F_\prime(w,z)=\partial F(w,z)/
+\partial w$.
+
+\dangerexercise Mathematicians sometimes use ``^{tensor notation}''
+in which subscripts and superscripts are staggered, as in `$R_i{}^{jk}{}_l$'.
+Explain how to achieve such an effect.
+\answer |$R_i{}^{jk}{}_l$|.
+
+Another way to get complex formulas from simple ones is to use the control
+sequences ^|\sqrt|, ^|\underline|, or ^|\overline|.
+^^{surds, see sqrt} ^^{vinculum, see overline}
+Like |^| and |_|, these operations apply to the character or subformula
+that follows them:
+\beginmathdemo
+|$\sqrt2$|&\sqrt2\cr
+|$\sqrt{x+2}$|&\sqrt{x+2}\cr
+|$\underline4$|&\underline4\cr
+|$\overline{x+y}$|&\overline{x+y}\cr
+|$\overline x+\overline y$|&\overline x+\overline y\cr
+|$x^{\underline n}$|&x^{\underline n}\cr
+|$x^{\overline{m+n}}$|&x^{\overline{m+n}}\cr
+|$\sqrt{x^3+\sqrt\alpha}$|&\sqrt{x^3+\sqrt\alpha}\cr
+\endmathdemo
+You can also get cube roots `$\root3\of{\phantom{h}}$' and similar things
+by using ^|\root|:
+\beginmathdemo
+|$\root 3 \of 2$|&\root 3 \of 2\cr
+|$\root n \of {x^n+y^n}$|&\root n \of {x^n+y^n}\cr
+|$\root n+1 \of a$|&\root n+1 \of a\cr
+\endmathdemo
+
+\danger The |\sqrt| and |\underline| and |\overline| operations are able to
+place lines above or below subformulas of any size or shape; the bar lines
+change their size and position, so that they are long enough to cover the
+subformula, and high enough or low enough not to bump into it. For example,
+consider `|\overline|~|l|' ($\,\overline l\,$) versus `|\overline|~|m|'
+($\,\overline m\,$): The first has a shorter bar line, and this line has
+been raised higher than the bar in the second. Similarly, the bar in
+`|\underline|~|y|' ($\,\underline y\,$) is lower than the bar in
+`|\underline|~|x|' ($\,\underline x\,$); and square root signs appear in a
+variety of positions based on the height and depth of what is being
+|\sqrt|'d: $\sqrt a + \sqrt d + \sqrt y$. \TeX\ knows the height, depth, and
+width of every letter and every subformula, because it considers them to be
+boxes, as explained in Chapter~11. If you have a formula in which there is
+only one |\sqrt|, or only one |\overline| or |\underline|, the normal
+positioning rules work fine; but sometimes you want to have uniformity
+between different members of a complex formula. For example, you might want to
+typeset `$\sqrt{\mathstrut a}+\sqrt{\mathstrut d}+\sqrt{\mathstrut y}$',
+putting all square roots in the same vertical position. There's an easy way
+to do this, using the control sequence ^|\mathstrut| as follows:
+\begintt
+$\sqrt{\mathstrut a}+\sqrt{\mathstrut d}+\sqrt{\mathstrut y}$.
+\endtt
+A |\mathstrut| is an invisible box whose width is zero; its height and depth
+are the height and depth of a parenthesis `('. Therefore subformulas
+that contain |\mathstrut| will always have the same height and depth,
+unless they involve more complicated constructions like subscripts and
+superscripts. Chapter~18 discusses more powerful operations called ^|\smash|
+and ^|\phantom| by which you can obtain complete control over the positioning
+of roots and similar signs.
+
+\exercise Test your understanding of what you have read so far in this chapter
+by explaining what should be typed to get the following formulas. \ (Be
+sure to check your answer with Appendix~A to confirm that you're right.)
+$$\hbox to\hsize{\indent
+ $\displaystyle
+ 10^{10}\hfil 2^{n+1}\hfil (n+1)^2\hfil \sqrt{1-x^2}\hfil
+ \overline{w+\overline z}\hfil p_1^{e_1}\hfil a_{b_{c_{d_e}}}\hfil
+ \root3\of{h''_n(\alpha x)}\hfil$}$$
+\answer |10^{10}|; \stretch|2^{n+1}|; \stretch|(n+1)^2|; \stretch|\sqrt{1-x^2}|;
+\stretch|\overline{w+\overline z}|; \stretch|p_1^{e_1}|; \stretch
+|a_{b_{c_{d_e}}}|; \stretch|\root3\of{h''_n(\alpha x)}|.
+\ (Of course, you should enclose these formulas in dollar signs so that
+\TeX\ will process them in math mode. Superscripts and subscripts can be
+given in either order; for example, |h''_n| and |h_n''| both work the
+same. You should not leave out any of the braces shown here; for example,
+`|$10^10$|' would yield `$10^10$'. But it doesn't hurt to insert
+additional braces around letters or numbers, as in `|({n}+{1})^{2}|'. The
+indicated blank spaces are necessary unless you use extra braces;
+otherwise \TeX\ will complain about undefined control sequences
+|\overlinez| and |\alphax|.)
+
+\exercise What mistake did B. C. ^{Dull} discover after he typed the
+following?
+\begintt
+If$ x = y$, then $x$ is equal to $y.$
+\endtt
+\answer He got `If$ x = y\ldots$' because he forgot to leave a space
+after `|If|'; ^{spaces} disappear between dollar signs. He should
+also have ended the sentence with `|$y$.|'; punctuation that belongs
+to a sentence should not be included in a formula, as we will see
+in Chapter~18. \ (But you aren't expected to know that yet.)
+
+\exercise Explain how to type the following sentence:
+\begindisplay
+Deleting an element from an $n$-tuple leaves an $(n-1)$-tuple.
+\enddisplay
+\answer |Deleting an element from an $n$-tuple leaves an $(n-1)$-tuple.|
+
+\exercise List all the italic letters that descend below the baseline.
+\ (These are the letters for which |\underline| will lower its bar line.)
+\answer $Q,f,g,j,p,q,y$. \ (The analogous ^{Greek} letters are
+^^{italic letters with descenders} ^^{descenders}
+$\beta,\gamma,\zeta,\eta,\mu,\xi,\rho,\phi,\varphi,\chi,\psi$.)
+
+We have discussed the fact that the characters you type have special meanings
+in math mode, but the examples so far are incomplete; they don't reveal all
+the power that is at your fingertips just after you press the `|$|' key.
+It's time now to go back to basics: Let us make a systematic survey of
+what each character does, when it is used in a formula.
+
+The 52 ^{letters} (|A| to |Z| and |a| to |z|) denote italic symbols
+($A$~to~$Z$ and $a$~to~$z$), which a mathematician would call ``^{variables}.''
+\TeX\ just calls them ``^{ordinary symbols},'' because they make up the
+bulk of math formulas. There are two variants of lowercase L in plain \TeX,
+namely `$l$' (which you get by simply typing `|l|') and `$\ell$'
+(which you get by typing `^|\ell|'). Although mathematicians commonly
+write something that looks like `$\ell$' in their manuscripts, they
+do so only to distinguish it from the numeral~`1'. This
+distinguishability problem is not present in printed mathematics, since an
+italic `$l$' is quite different from a~`1'; therefore it is traditional to
+use `$l$' unless `$\ell$' has been specifically requested.
+
+Plain \TeX\ also treats the 18 characters
+\begintt
+0 1 2 3 4 5 6 7 8 9 ! ? . || / ` @ "
+\endtt
+^^{digits}^^{numerals}
+as ordinary symbols; i.e., it doesn't insert any extra space when these
+symbols occur next to each other or next to letters. Unlike the letters,
+these 18 characters remain in roman type when they appear in formulas.
+There's nothing special for you to remember about them, except that the
+^{vertical line} `\|' has special uses that we shall discuss later.
+Furthermore, you should be careful to distinguish between `oh' and `zero':
+The italic letter~$O$ is almost never used in formulas unless it appears
+just before a left parenthesis, as in `$O(n)$'; ^^{big-$O$ notation}
+and the numeral~$0$ is almost never used just before a left parenthesis
+unless it is preceded by another digit, as in `$10(n-1)$'. Watch for
+left parentheses and you'll be $0K$. \ (Lowercase o's also tend to
+appear only before left parentheses; type `|x_0|' instead of `|x_o|',
+since the formula `$x_0$' is generally more correct than `$x_o$'.)
+
+The three characters |+|, |-|, and |*|
+are called ``^{binary operations},'' because they operate on two parts of
+a formula. For example, |+|~is a ^{plus sign}, which is used for the sum
+of two numbers; |-|~is a ^{minus sign}. The ^{asterisk}~(|*|) is rarer
+^^{star, see asterisk}
+in mathematics, but it also behaves as a binary operation. Here are
+some examples of how \TeX\ typesets binary operations when they appear
+next to ordinary symbols:
+\beginmathdemo
+\it Input&\it Output\cr
+\noalign{\vskip2pt}
+|$x+y-z$|&x+y-z\cr
+|$x+y*z$|&x+y*z\cr
+|$x*y/z$|&x*y/z\cr
+\endmathdemo
+Notice that |-| and |*| produce quite different math symbols from what you
+get in normal text: The ^{hyphen}~(-) becomes a minus sign~($-$), and
+the raised asterisk~(*) drops down to a lower level~($*$).
+
+\danger \TeX\ does not treat |/| as a binary operation, even though a
+^{slash} stands for division (which qualifies as a binary operation on
+mathematical grounds). The reason is that printers traditionally put extra
+space around the symbols $+$, $-$, and~$*$, but not around~$/$. If \TeX\ were to
+typeset |/| as a binary operation, the formula `|$1/2$|' would come out
+`$1\mathbin/2$', which is wrong; so \TeX\ considers |/| to be an ordinary
+symbol.
+
+\danger Appendix F lists many more binary operations, for which you type
+control sequences instead of single characters. Here are some examples:
+\beginmathdemo
+|$x\times y\cdot z$|&x\times y\cdot z\cr
+|$x\circ y\bullet z$|&x\circ y\bullet z\cr
+|$x\cup y\cap z$|&x\cup y\cap z\cr
+|$x\sqcup y\sqcap z$|&x\sqcup y\sqcap z\cr
+|$x\vee y\wedge z$|&x\vee y\wedge z\cr
+|$x\pm y\mp z$|&x\pm y\mp z\cr
+\endmathdemo
+It is important to distinguish $\times$ (^|\times|) from $X$ (|X|)
+and from $x$ (|x|); to distinguish $\cup$ (^|\cup|) from $U$ (|U|)
+and from $u$ (|u|); to distinguish $\vee$ (^|\vee|) from $V$ (|V|)
+and from $v$ (|v|); to distinguish $\circ$ (^|\circ|) from $O$ (|O|)
+and from $o$ (|o|). ^^|\cdot|^^|\bullet|^^|\cap|^^|\sqcup|^^|\sqcap|^^|\wedge|
+^^{cross, see dagger, times}
+^^|\pm|^^|\mp| The symbols `$\lor$' and `$\land$' can also be called
+^|\lor| and ^|\land|, since they frequently stand for binary operations
+that are called ``^{logical~or}'' and ``^{logical~and}.''
+
+\danger Incidentally, binary operations are treated as ordinary symbols
+if they don't occur between two quantities that they can operate on.
+For example, no extra space is inserted next to the $+$, $-$, and~$*$
+in cases like the following:
+\beginmathdemo
+|$x=+1$|&x=+1\cr
+|$3.142-$|&3.142-\cr
+|$(D*)$|&(D*)\cr
+\endmathdemo
+Consider also the following examples, which show that binary
+operations can be used as ordinary symbols in superscripts and subscripts:
+\beginmathdemo
+|$K_n^+,K_n^-$|&K_n^+,K_n^-\cr
+|$z^*_{ij}$|&z^*_{ij}\cr
+|$g^\circ \mapsto g^\bullet$|&g^\circ \mapsto g^\bullet\cr
+|$f^*(x) \cap f_*(y)$|&f^*(x) \cap f_*(y)\cr
+\endmathdemo
+
+\dangerexercise How would you obtain the formulas `$z^{*2}$'
+and `$h_*'(z)$'\thinspace?
+\answer |$z^{*2}$| and |$h_*'(z)$|.
+
+Plain \TeX\ treats the four characters |=|, |<|, |>|, and |:|\ as
+``^{relations}'' because they express a relationship between two
+quantities. For example, `${x<y}$' means that $x$~is less than~$y$.
+Such relationships have a rather different meaning from binary
+operations like $+$, and the symbols are typeset somewhat differently:
+\beginmathdemo
+|$x=y>z$|&x=y>z\cr
+|$x:=y$|&x:=y\cr
+|$x\le y\ne z$|&x\le y\ne z\cr
+|$x\sim y\simeq z$|&x\sim y\simeq z\cr
+|$x\equiv y\not\equiv z$|&x\equiv y\not\equiv z\cr
+|$x\subset y\subseteq z$|&x\subset y\subseteq z\cr
+\endmathdemo
+^^|\le|^^|\ne|^^|\simeq|^^{colon}^^{equals}^^{lessthan}^^{greaterthan}
+^^{colonequals}^^|\equiv|^^|\not|^^|\subset|^^|\subseteq|^^|\sim|
+^^{hooks, see subset, supset} ^^{wiggle, see sim}
+(The last several examples show some of the many other
+relational symbols that plain \TeX\ makes available via control sequences;
+see Appendix~F.)
+
+The two characters `|,|' (^{comma}) and `|;|' (^{semicolon}) are treated
+as ^{punctuation marks in formulas}; this means that \TeX\ puts a little
+extra space after them, but not before them.
+\beginmathdemo
+|$f(x,y;z)$|&f(x,y;z)\cr
+\endmathdemo
+It isn't customary to put extra space after a `|.|'\ (^{period}) in
+math formulas, so \TeX\ treats a period as an ordinary symbol.
+If you want the `|:|'\ character to be treated as a punctuation mark
+instead of as a relation, just call it ^|\colon|:
+\beginmathdemo
+|$f:A\to B$|&f:A\to B\cr
+|$f\colon A\to B$|&f\colon A\to B\cr
+\endmathdemo
+^^|\to|
+If you want to use a comma as an ordinary symbol (e.g., when it
+appears in a large number), just put it in braces; \TeX\ treats
+anything in braces as an ordinary symbol. For instance,
+\beginmathdemo
+|$12,345x$|&12,345x\qquad\rm(wrong)\cr
+|$12{,}345x$|&12{,}345x\qquad\,\rm(right)\cr
+\endmathdemo
+
+\dangerexercise What's an easy way to get a raised dot in a decimal
+constant (e.g., `$3{\cdot}1416$')?
+\answer |$3{\cdot}1416$|. \ (One of the earlier examples in this
+chapter showed that ^|\cdot| is a binary operation; putting it in braces
+makes it act like an ordinary symbol.)\par
+If you have lots of constants like this, for example in a table, there's a way
+to make ordinary periods act like |\cdot| symbols: Just define
+^|\mathcode||`.| to be |"0201|, assuming that the fonts of plain \TeX\ are
+being used. However, this could be dangerous, since ordinary
+periods are used frequently in displayed equations; the |\mathcode| change
+should be confined to places where every period is to be a |\cdot|.
+
+So far we have considered letters, other ordinary symbols, binary operations,
+relations, and punctuation marks; hence we have covered almost every key on
+the typewriter. There are just a few more: The characters `|(|' ^^{lparen}
+and `|[|' ^^{lbracket} are called ``^{openings},'' while `|)|' ^^{rparen} and
+^^{fences, see opening, closing, delimiters}
+`|]|' ^^{rbracket} are called ``^{closings}''; these act pretty much like
+ordinary symbols, but they help \TeX\ to decide when a binary operation is
+not really being used in a binary way. Then there is the character~|'|,
+which we know is used as an abbreviation for |\prime| superscripts.
+Finally, we know that plain \TeX\ reserves the other ten characters:
+\begintt
+\ $ % # & ~ { } _ ^
+\endtt
+These are not usable for symbols in math mode unless their ^|\catcode|
+values are changed (see Chapter~7). Although |{| and |}| specify
+grouping, the control sequences `|\{|' and `|\}|' ^^|\lbrace|^^|\rbrace| can
+be used to get `$\{$' as an opening and `$\}$' as a closing.
+
+\ddanger All of these math mode interpretations are easily changeable, since
+each character has a ^|\mathcode|, as explained in Chapter~17; none of
+the conventions are permanently built into \TeX\null. However, most of them are
+so standard that it is usually unwise to make many changes, except perhaps
+in the interpretations of |`|, |"|, and |@|. ^^{at sign} ^^{leftquote}
+^^{doublequote}
+
+The special characters |^| and |_| that designate superscripts
+^^{circumflex}^^{underbar}
+and subscripts should not be used except in formulas. Similarly,
+the names of math symbols like |\alpha| and |\approx|, and the
+control sequences for math operations like |\overline|, must not
+invade ordinary text. \TeX\ uses these facts to detect ^{missing dollar
+signs} in your input, before such mistakes cause too much trouble. For
+example, suppose you were to type
+\begintt
+The smallest $n such that $2^n>1000$ is~10.
+\endtt
+\TeX\ doesn't know that you forgot a `|$|' after the first `|n|', because
+it doesn't understand English; so it finds a ``formula'' between the
+first two |$| signs:
+\begindisplay
+The smallest $n such that $
+\enddisplay
+after which it thinks that `|2|' is part of the text. But then the |^|
+reveals an inconsistency; \TeX\ will automatically insert a~|$| before
+the~|^|, and you will get an error message. In this way the computer has
+gotten back into synch, and the rest of the document can be typeset as if
+nothing had happened.
+
+\danger Conversely, a blank line or ^|\par| is not permitted in math mode.
+This gives \TeX\ another way to recover from a missing~|$|; such
+errors will be confined to the paragraph in which they occur.
+
+\danger If for some reason you cannot use |^| and |_| for superscripts
+and subscripts, because you have an unusual keyboard or because you need
+|^| for French accents or something, plain \TeX\ lets you type ^|\sp|
+and ^|\sb| instead. For example, `|$x\sp2$|' is another way to get `$x\sp2$'.
+On the other hand, some people are lucky enough to have keyboards that
+contain additional symbols besides those of standard ASCII.
+^^{character set}
+When such symbols are available, \TeX\ can be set up to make math
+typing a bit more pleasant. For example, at the author's installation there
+are keys labeled \up\ and~\dn\ that produce visible symbols
+(these make superscripts and subscripts look much nicer
+on the screen); there are keys for the relations {\tentex\char'34},
+{\tentex\char'35}, and {\tentex\char'32} (these save time); and there are
+about two dozen more keys that occasionally come in handy. \ (See Appendix~C.)
+^^{uparrow}^^{downarrow}^^{leq}^^{geq}^^{neq}
+
+\danger Mathematicians are fond of using ^{accents} over letters, because
+this is often an effective way to indicate relationships between
+mathematical objects, and because it greatly extends the number of available
+symbols without increasing the number of necessary fonts.
+Chapter~9 discusses the use of accents in ordinary text, but mathematical
+accents are somewhat different, because spacing is not the same; \TeX\ uses
+special conventions for accents in formulas, so that the two sorts of
+accents will not be confused with each other. The following math accents
+are provided by plain~\TeX:
+\beginmathdemo
+|$\hat a$|&\hat a\cr
+|$\check a$|&\check a\cr
+|$\tilde a$|&\tilde a\cr
+|$\acute a$|&\acute a\cr
+|$\grave a$|&\grave a\cr
+|$\dot a$|&\dot a\cr
+|$\ddot a$|&\ddot a\cr
+|$\breve a$|&\breve a\cr
+|$\bar a$|&\bar a\cr
+|$\vec a$|&\vec a\cr
+\endmathdemo
+^^|\hat|^^|\check|^^|\tilde|^^|\acute|^^|\grave|
+^^|\dot|^^|\ddot|^^|\breve|^^|\bar|^^|\vec|
+The first nine of these are called |\^|, |\v|, |\~|, |\'|, |\`|, |\.|,
+|\"|, |\u|, and |\=|, respectively, when they appear in text; |\vec| is an
+accent that appears only in formulas. \TeX\ will complain if you try to
+use |\^| or |\v|, etc., in formulas, or if you try to use |\hat| or
+|\check|, etc., in ordinary text.
+
+\danger It's usually a good idea to define special control sequences for
+accented letters that you need frequently. For example, you can put
+^^|\def|
+\begintt
+\def\Ahat{{\hat A}}
+\def\chat{{\hat c}}
+\def\scheck{{\check s}}
+\def\xtilde{{\tilde x}}
+\def\zbar{{\bar z}}
+\endtt
+at the beginning of a manuscript that uses the symbols $\hat A$, $\hat c$,
+$\check s$, $\tilde x$, and $\bar z$ more than, say, five times. This
+saves you a lot of keystrokes, and it makes the manuscript easier to
+read. Chapter~20 explains how to define control sequences.
+
+\danger When the letters $i$ and $j$ are accented in math formulas, ^{dotless}
+symbols $\imath$ and $\jmath$ should be used under the accents. These
+symbols are called ^|\imath| and ^|\jmath| in plain \TeX. Thus, for example,
+a paper that uses `$\hat\imath$' and `$\hat\jmath$' ought to begin with
+the following definitions:
+\begintt
+\def\ihat{{\hat\imath}}
+\def\jhat{{\hat\jmath}}
+\endtt
+
+\def\Ahat{{\hat A}}
+\danger You can put ^{accents on top of accents}, making symbols like
+$\skew6\hat\Ahat$ that might cause a mathematician to
+squeal with ecstasy. However, it takes a bit of finesse to get the
+upper accent into a position that looks right, because the designer of a
+font for mathematics usually tells \TeX\ to position math accents in
+special ways for special letters. Plain \TeX\ provides a control sequence
+called ^|\skew| that makes it fairly easy to shift superaccents into their
+proper place. For example, `|\skew6\hat\Ahat|' was used to produce the
+symbol above. The number `|6|' in this example was chosen by trial and
+error; `|5|'~seems to put the upper accent a bit too far left, while
+`|7|'~makes it a bit too far right, at least in the author's opinion.
+The idea is to fiddle with the amount of skew until you find what
+pleases you best.
+
+\danger It's possible, in fact, to put math accents on any subformula, not
+just on single characters or accented characters. But there's usually not
+much point in doing so, because \TeX\ just centers the accent over the
+whole subformula. For example, `|$\hat{I+M}$|' yields `$\hat{I+M}$'. In
+particular, a |\bar| accent always stays the same size; it's not like
+^|\overline|, which grows with the formula under it. Some people prefer
+the longer line from |\overline| even when it applies to only a single
+letter; for example, `|$\bar z+\overline z$|' produces `$\bar z+\overline
+z$', and you can take your pick when you define |\zbar|. However, plain
+\TeX\ does provide two accents that grow; they are called ^|\widehat| and
+^|\widetilde|:
+\beginmathdemo
+|$\widehat x,\widetilde x$|&\tenmath\widehat x,\widetilde x\cr
+|$\widehat{xy},\widetilde{xy}$|&\tenmath\widehat{xy},\widetilde{xy}\cr
+|$\widehat{xyz},\widetilde{xyz}$|&\tenmath\widehat{xyz},\widetilde{xyz}\cr
+\endmathdemo
+The third example here shows the maximum size available.
+
+\def\ghat{{\hat g}}
+\exercise This has been another long chapter; but cheer up, you have learned
+a lot! Prove it by explaining what to type in order to get the formulas
+$e^{-x^2}$, $D\sim p^\alpha M+l$, and $\ghat\in(H^{\pi_1^{-1}})'$. \ (In
+the last example, assume that a control sequence |\ghat| has already been
+defined, so that |\ghat| produces the accented letter $\ghat$.)
+\answer |$e^{-x^2}$|, |$D\sim p^\alpha M+l$|, and |$\ghat\in(H^{\pi_1^{-1}})'$|.
+\ (If you are reading the dangerous bend sections, you know that the
+recommended way to define |\ghat| is `|\def\ghat{{\hat g}}|'.)
+
+\endchapter
+
+Producing ^{Greek} letters is as easy as $\pi$.
+You just type |... as easy as $\pi$.|
+\author LESLIE ^{LAMPORT}, {\sl The ^{L\kern-.2em\raise.6ex\hbox{a}%
+ \kern-.1em\TeX} Document Preparation System\/} (1983)
+ % Note: the final manual has a slightly different wording on p43.
+ % It's now called "LaTeX: A Document Preparation System" (1986)
+ % But I decided to cite the original, partly because I have
+ % no smallcaps sans-serif `A' to match the new LaTeX logo!
+
+\bigskip
+
+\TeX\ has no regard for the glories of the Greek tongue---\/
+as far as it is concerned, Greek letters are just additional weird symbols,
+and they are allowed\/ {\rm only} in math mode.
+In a pinch you can get the output $\tau\epsilon\chi$ by typing %
+ |$\tau\epsilon\chi$|,
+but if you're actually setting Greek text, you will be using
+a different version of \TeX, designed for a keyboard with Greek letters on it,
+and you shouldn't even be reading this manual,
+which is undoubtedly all English to you.
+\author MICHAEL ^{SPIVAK}, {\sl The Joy of \TeX\/} (1982)
+
+\eject
+ \beginchapter Chapter 17. More about Math
+
+Another thing mathematicians like to do is make fractions---and they
+like to build symbols up on top of each other in a variety of different ways:
+\begindisplay
+$\displaystyle
+{1\over2}\qquad{\rm and}\qquad{n+1\over3}\qquad{\rm and}\qquad
+{n+1\choose3}\qquad{\rm and}\qquad\sum_{n=1}^3 Z_n^2\,.$
+\enddisplay
+You can get these four formulas as displayed equations by typing
+`|$$1\over2$$|' and
+`|$$n+1\over3$$|' and
+`|$$n+1\choose3$$|' and
+`|$$\sum_{n=1}^3 Z_n^2$$|';
+we shall study the simple rules for such constructions in this chapter.
+^^|\sum|^^|\choose|
+
+First let's look at ^{fractions}, which use the `^|\over|' notation. The
+control sequence |\over| applies to everything in the formula unless you
+use braces to enclose it in a specific subformula; in the latter
+^^{stacked fractions, see over}
+case, |\over| applies to everything in that subformula.
+\begindisplaymathdemo
+\it Input&\it Output\cr
+\noalign{\vskip-3pt}
+|$$x+y^2\over k+1$$|&x+y^2\over k+1\cr
+\noalign{\vskip2pt}
+|$${x+y^2\over k}+1$$|&{x+y^2\over k}+1\cr
+\noalign{\vskip-1pt}
+|$$x+{y^2\over k}+1$$|&x+{y^2\over k}+1\cr
+\noalign{\vskip-1pt}
+|$$x+{y^2\over k+1}$$|&x+{y^2\over k+1}\cr
+\noalign{\vskip-3pt}
+|$$x+y^{2\over k+1}$$|&x+y^{2\over k+1}\cr
+\endmathdemo
+You aren't allowed to use |\over| twice in the same subformula; instead of
+typing something like
+`|a \over b \over 2|', you must specify what goes over what:
+\begindisplaymathdemo
+\noalign{\vskip3pt}
+|$${a\over b}\over 2$$|&{a\over b}\over 2\cr
+|$$a\over{b\over 2}$$|&a\over{b\over 2}\cr
+\endmathdemo
+Unfortunately, both of these alternatives look pretty awful. Mathematicians
+tend to ``overuse'' |\over| when they first begin to typeset their own work
+on a system like \TeX. A good typist or copy editor will convert fractions
+to a ``^{slashed form},'' whenever a built-up construction would be too
+small or too crowded. For example, the last two cases should be treated
+as follows:
+\begindisplaymathdemo
+\noalign{\vskip3pt}
+|$$a/b \over 2$$|&a/b \over 2\cr
+|$$a \over b/2$$|&a \over b/2\cr
+\endmathdemo
+Conversion to slashed form takes a little bit of mathematical knowhow, since
+^{parentheses} sometimes need to be inserted in order to preserve the meaning
+of the formula. Besides substituting `|/|' for~`|\over|', the two parts
+of the fraction should be put in parentheses unless they are single
+symbols; for example, $a\over b$~becomes simply~$a/b$, but
+$a+1\over b$ becomes $(a+1)/b$, and $a+1\over b+1$ becomes
+${(a+1)/(b+1)}$. Furthermore, the entire fraction should generally
+be enclosed in parentheses if it appears next to something else;
+for example, ${a\over b}x$ becomes $(a/b)x$. If you are a typist without
+mathematical training, it's best to ask the author of the manuscript
+for help, in doubtful cases; you might also tactfully suggest that
+unsightly fractions be avoided altogether in future manuscripts.
+
+\exercise What's a better way to render the formula $x+y^{2\over k+1}$?
+\answer $x+y^{2/(k+1)}$\quad(|$x+y^{2/(k+1)}$|).
+
+\exercise Convert `${a+1\over b+1}x$' to slashed form.
+\answer $((a+1)/(b+1))x$\quad(|$((a+1)/(b+1))x$|).
+
+\exercise What surprise did B. L. ^{User} get when he typed `|$$x = (y^2\over
+k+1)$$|'\thinspace?
+\answer He got the displayed formula$$x=(y^2\over k+1)$$ because he forgot
+that an unconfined |\over| applies to everything. \ (He should probably
+have typed `|$$x=\left(y^2\over k+1\right)$$|', using ideas that will be
+presented later in this chapter; this not only makes the parentheses
+larger, it keeps the `$x=$' out of the fraction, because |\left| and
+|\right| introduce subformulas.)
+
+\def\cents{\hbox{\rm\rlap/c}}
+\exercise How can you make `$7{1\over2}\cents$'? \ (Assume that
+the control sequence |\cents| yields~`$\cents$'.)^^{money}^^{cents}
+\answer `|$7{1\over2}\cents$|' or `|7$1\over2$\cents|'. \ (Incidentally,
+the definition used here was |\def\cents{\hbox{\rm\rlap/c}}|.)
+^^|\rlap|^^|\cents|
+
+The examples above show that letters and other symbols sometimes get
+smaller when they appear in fractions, just as they get smaller when they
+are used as exponents. It's about time that we studied \TeX's method for
+choosing the sizes of things. \TeX\ actually has eight different
+^{styles} in which it can treat formulas, namely
+$$\halign{\indent#\hfil\quad&#\hfil\cr
+display style&(for formulas displayed on lines by themselves)\cr
+text style&(for formulas embedded in the text)\cr
+script style&(for formulas used as superscripts or subscripts)\cr
+scriptscript style&(for second-order superscripts or subscripts)\cr}$$
+^^{display style}^^{text style}^^{script style}^^{scriptscript style}
+and four other ``^{cramped}'' styles that are almost the same except that
+exponents aren't raised quite so much. For brevity we shall refer to the
+eight styles as
+\begindisplay
+$\displaystyle D,\ D',\ T,\ T',\ S,\ S',\ \SS,\ \SS',$
+\enddisplay
+where $D$ is display style, $D'$ is cramped display style, $T$~is text style,
+etc. \TeX\ also uses three different ^{sizes of type for mathematics};
+they are called ^{text size}, ^{script size}, and ^{scriptscript size}.
+
+The normal way to typeset a formula with \TeX\ is to enclose it in dollar
+signs |$|$\,\ldots\,$|$|; this yields the formula in text style
+(style~$T$). Or you can enclose it in double dollar signs |$$|$\,\ldots\,$|$$|;
+this displays the formula in display style (style~$D$). The subformulas of
+a formula might, of course, be in different styles. Once you know
+the style, you can determine the size of type that \TeX\ will use:
+$$\everycr{\noalign{\penalty10000}}
+\halign{\indent#\hfil\qquad&#\hfil&\quad#\llap(like this)\hfil\cr
+If a letter is in style&then it will be set in\cr
+\noalign{\vskip 2pt}
+$D,D',T,T'$&text size&\cr
+$S,S'$&script size&\sevenrm\cr
+$\SS,\SS'$&scriptscript size&\fiverm\cr}$$
+There is no ``$\it SSS$'' style or ``scriptscriptscript'' size; such tiny
+symbols would be even less readable than the scriptscript ones. Therefore
+\TeX\ stays with scriptscript size as the minimum:
+$$\halign{\indent\hbox to 1.3in{#\hfil}&\hbox to 1.2in{#\hfil}&#\hfil\cr
+In a formula&the superscript&and the subscript\cr
+of style&style is&style is\cr
+\noalign{\vskip 2pt}
+$D,T$&$S$&$S'$\cr
+$D',T'$&$S'$&$S'$\cr
+$S,\SS$&$\SS$&$\SS'$\cr
+$S',\SS'$&$\SS'$&$\SS'$\cr}$$
+For example, if |x^{a_b}| is to be typeset in style $D$, then |a_b| will
+be set in style~$S$, and {\tt b}~in style~$\SS'$; the result is
+`$\displaystyle x^{a_b}$'.
+
+So far we haven't seen any difference between styles $D$ and $T$. Actually
+there is a slight difference in the positioning of exponents, although
+script size is used in each case: You get
+$\displaystyle x^2$~in $D$~style and $x^2$~in $T$~style and \vbox to 0pt{
+\vss\hbox{$\displaystyle{\atop x^2}$}\kern0pt}~in $D'$ or $T'$~style---do
+you see the difference? But there is a big distinction between $D$ style and
+$T$ style when it comes to fractions:
+$$\halign{\indent\hbox to 1.3in{#\hfil}&\hbox to 1.2in{#\hfil}&#\hfil\cr
+In a formula&the style of the&and the style of the\cr
+$\alpha$|\over|$\,\beta$ of style&numerator $\alpha$ is&denominator
+$\beta$ is\cr
+\noalign{\vskip 2pt}
+$D$&$T$&$T'$\cr
+$D'$&$T'$&$T'$\cr
+$T$&$S$&$S'$\cr
+$T'$&$S'$&$S'$\cr
+$S,\SS$&$\SS$&$\SS'$\cr
+$S',\SS'$&$\SS'$&$\SS'$\cr}$$
+^^{numerator}^^{denominator}
+Thus if you type `|$1\over2$|' (in a text) you get $1\over2$, namely style
+$S$ over style~$S'$; but if you type
+`|$$1\over2$$|' you get $$1\over2$$ (a displayed formula), which is style
+$T$ over style $T'$.
+
+\danger While we're at it, we might as well finish the style rules:
+^|\underline| does not change the style. ^{Math accents}, and the operations
+^|\sqrt| and ^|\overline|, change uncramped styles to their cramped
+counterparts; for example, $D$ changes to $D'$, but $D'$ stays as it was.
+
+\dangerexercise State the style and size of each part of the formula
+$\displaystyle \sqrt{p_2^{e'}}$, assuming that the formula itself is in
+style~$D$.
+\answer Style $D'$ is used for the subformula $p_2^{e'}$, hence style~$S'$
+is used for the superscript~$e'$ and the subscript~2, and style~$\SS'$
+is used for the supersuperscript prime. The square root sign and the $p$
+appear in text size; the 2 and the~$e$ appear in script size; and the
+$\prime$ is in scriptscript size.
+
+Suppose you don't like the style that \TeX\ selects by its automatic style
+rules. Then you can specify the style you want by typing ^|\displaystyle|
+or ^|\textstyle| or ^|\scriptstyle| or ^|\scriptscriptstyle|; the style
+that you select will apply until the end of the formula or subformula, or
+until you select another style. For example,
+`|$$n+\scriptstyle n+\scriptscriptstyle n.$$|' produces the display
+$$n+\scriptstyle n+\scriptscriptstyle n.$$
+This is a rather silly example, but it does show
+that the plus signs get smaller too, as the style changes. \TeX\ puts no
+space around + signs in script styles.
+
+Here's a more useful example of style changes: Sometimes you need to
+typeset a ``^{continued fraction}'' made up of many other fractions,
+all of which are supposed to be in display style:
+$$a_0+{1\over\displaystyle a_1+
+ {\strut 1\over\displaystyle a_2+
+ {\strut 1\over\displaystyle a_3+
+ {\strut 1\over a_4}}}}$$
+In order to get this effect, the idea is to type
+\begintt
+$$a_0+{1\over\displaystyle a_1+
+ {\strut 1\over\displaystyle a_2+
+ {\strut 1\over\displaystyle a_3+
+ {\strut 1\over a_4}}}}$$
+\endtt
+(The control sequence ^|\strut| has been used to make the denominators
+taller; this is a refinement that will be discussed in
+Chapter~18. Our concern now is with the style commands.) \
+Without the appearances of\/ |\strut| and |\displaystyle| in this formula,
+the result would be completely different:
+$$a_0+{1\over a_1+{1\over
+ a_2+{1\over a_3+{1\over a_4}}}}$$
+
+\danger These examples show that the numerator and denominator of a fraction
+are generally centered with respect to each other. If you prefer to have
+the numerator or denominator appear ^{flush left}, put `^|\hfill|' after
+it; or if you prefer ^{flush right}, put `|\hfill|' at the left. For
+example, if the first three appearances of `|1\over|' in the previous
+example are replaced by `|1\hfill\over|', you get the display
+$$a_0+{1\hfill\over\displaystyle a_1+
+ {\strut1\hfill\over\displaystyle a_2+
+ {\strut1\hfill\over\displaystyle a_3+
+ {\strut1\over a_4}}}}$$
+(a format for continued fractions that many authors prefer). This works
+because |\hfill| stretches at a faster rate than the glue that is
+actually used internally by \TeX\ when it centers the numerators
+and denominators.
+
+\TeX\ has another operation `^|\atop|', which is like |\over| except that
+it leaves out the fraction line:
+\begindisplaymathdemo
+|$$x\atop y+2$$|&x\atop y+2\cr
+\endmathdemo
+The plain \TeX\ format in Appendix B also defines `^|\choose|', which is
+like |\atop| but it encloses the result in parentheses:
+\begindisplaymathdemo
+|$$n\choose k$$|&n\choose k\cr
+\endmathdemo
+It is called |\choose| because it's
+a common notation for the so-called ^{binomial coefficient}
+that tells how many ways there are to choose $k$~things out of $n$~things.
+
+You can't mix |\over| and |\atop| and |\choose| with each other.
+For example, `|$$n \choose k \over 2$$|' is illegal; you must use
+grouping, to get either `|$${n\choose k}\over2$$|' or
+`|$$n\choose{k\over2}$$|', i.e.,
+\begindisplay
+$\displaystyle{{n\choose k}\over2}\qquad{\rm or}\qquad {n\choose{k\over2}}.$
+\enddisplay
+The latter formula, incidentally, would look better as
+`|$$n\choose k/2$$|' or `|$$n\choose{1\over2}k$$|', yielding
+\begindisplay
+$\displaystyle{n\choose k/2}\qquad{\rm or}\qquad{n\choose{1\over2}k}.$
+\enddisplay
+
+\medskip
+\exercise As alternatives to $\displaystyle{{n\choose k}\over2}$,
+discuss how you could obtain the two displays
+\begindisplay\abovedisplayskip=0pt\belowdisplayskip=0pt
+$\displaystyle
+{1\over2}{n\choose k}
+\qquad{\rm and}\qquad
+{\displaystyle{n\choose k}\over2}.$
+\enddisplay
+\answer |$${1\over2}{n\choose k}$$|;
+|$$\displaystyle{n\choose k}\over2$$|.
+All of these braces are necessary.
+
+\bigbreak
+\exercise Explain how to specify the displayed formula
+$${p \choose 2}x^2 y^{p-2} - {1 \over 1-x}{1 \over 1-x^2}.$$
+\answer |$${p \choose 2} x^2 y^{p-2} - {1 \over 1-x}{1 \over 1-x^2}.$$|
+
+\danger \TeX\ has a generalized version of\/ |\over| and |\atop| in which you
+specify the exact thickness of the line rule by typing
+`^|\above|\<dimen>'. For example,
+\begintt
+$$\displaystyle{a\over b}\above1pt\displaystyle{c\over d}$$
+\endtt
+will produce a ^{compound fraction} with a heavier ($1\pt$ thick) rule as
+its main bar:
+$${\displaystyle{a\over b}\above 1pt\displaystyle{c\over d}}.$$
+This sort of thing occurs primarily in textbooks on elementary mathematics.
+
+\goodbreak
+Mathematicians often use the sign $\sum$ to stand for ``^{summation}''
+and the sign $\int$ to stand for ``^{integration}.'' If you're a typist but not
+a mathematician, all you need to remember is that ^|\sum| stands for
+$\sum$ and ^|\int| for $\int$; these abbreviations appear in Appendix~F
+together with all the other symbols, in case you forget. Symbols like
+$\sum$ and $\int$ (and a few others like $\bigcup$ and $\prod$ and $\oint$
+and~$\bigotimes$, all listed in Appendix~F) are called {\sl ^{large operators}},
+^^{collective signs, see large operators} ^^{sigma signs, see sum}
+and you type them just as you type ordinary symbols or letters. The
+difference is that \TeX\ will choose a {\sl larger\/} large operator in
+display style than it will in text style. For example,
+$$\halign{\indent#\hfil\qquad yields\qquad&$#\hfil$\qquad&#\hfil\cr
+|$\sum x_n$|&\sum x_n&($T$ style)\cr
+\noalign{\vskip3pt}
+|$$\sum x_n$$|&\displaystyle\sum x_n&($D$ style).\cr}$$
+
+A displayed |\sum| usually occurs with ``^{limits},'' i.e., with
+subformulas that are to appear above and below it. You type limits just
+as if they were superscripts and subscripts; for example, if you want
+$$\sum_{n=1}^m$$
+you type either `|$$\sum_{n=1}^m$$|' or `|$$\sum^m_{n=1}$$|'. According
+to the normal conventions of mathematical typesetting, \TeX\ will change
+this to `$\sum_{n=1}^m$' (i.e., without limits) if it occurs in text
+style rather than in display style.
+
+Integrations are slightly different from summations, in that the superscripts
+and subscripts are not set as limits even in display style:
+$$\halign{\indent\hbox to2.3in{#\hfil}\hbox to.6in{yields\hfil}&
+ $#\hfil$\qquad&#\hfil\cr
+|$\int_{-\infty}^{+\infty}$|&\int_{-\infty}^{+\infty}&($T$ style)\cr
+\noalign{\vskip3pt}
+|$$\int_{-\infty}^{+\infty}$$|&\displaystyle\int_{-\infty}^{+\infty}&
+ ($D$ style).\cr}$$
+
+\danger Some printers prefer to set limits above and below $\int$ signs;
+this takes more space on the page, but it
+gives a better appearance if the subformulas are complex, because it
+keeps them out of the way of the rest of the formula. Similarly, limits
+are occasionally desirable in text style or script style; but some
+printers prefer not to set limits on displayed $\sum$ signs. You can change
+\TeX's convention by simply typing `^|\limits|' or `^|\nolimits|' immediately
+after the large operator.
+For example,
+$$\halign{\indent\hbox to2.3in{#\hfil}\hbox to.6in{yields\hfil}&
+ $\displaystyle{#}$\hfil\cr
+|$$\int\limits_0^{\pi\over2}$$|&\int\limits_0^{\pi\over2}\cr
+\noalign{\vskip 4pt}
+|$$\sum\nolimits_{n=1}^m$$|&\sum\nolimits_{n=1}^m\cr}$$
+
+\ddanger If you say `|\nolimits\limits|' (presumably because some macro
+like |\int| specifies |\nolimits|, but you do want them), the last word
+takes precedence. There's also a command `^|\displaylimits|' that can be
+used to restore \TeX's normal conventions; i.e., the limits will be
+displayed only in styles $D$ and $D'$.
+
+\danger Sometimes you need to put two or more rows of limits under a large
+operator; you can do this with `^|\atop|'. For example, if you want
+the displayed formula
+$$\sum_{\scriptstyle0\le i\le m\atop\scriptstyle0<j<n}P(i,j)$$
+the correct way to type it is
+\begintt
+$$\sum_{\scriptstyle0\le i\le m\atop\scriptstyle0<j<n}P(i,j)$$
+\endtt
+(perhaps with a few more spaces to make it look nicer in the manuscript
+file). The instruction `^|\scriptstyle|' was necessary here,
+twice---otherwise the lines `$0\le i\le m$' and `$0<j<n$' would have been in
+scriptscript size, which is too small. This is another instance of a rare
+case where \TeX's automatic style rules need to be overruled.
+
+\exercise How would you type the displayed formula $\displaystyle
+\sum_{i=1}^p\sum_{j=1}^q\sum_{k=1}^ra_{ij}b_{jk}c_{ki}$\enspace?
+\answer |$$\sum_{i=1}^p\sum_{j=1}^q\sum_{k=1}^ra_{ij}b_{jk}c_{ki}$$|.
+
+\dangerexercise And how would you handle $\displaystyle
+\sum_{{\scriptstyle1\le i\le p\atop\scriptstyle1\le j\le q}
+ \atop\scriptstyle1\le k\le r}a_{ij}b_{jk}c_{ki}$\enspace?
+\answer |$$\sum_{{\scriptstyle 1\le i\le p \atop \scriptstyle 1\le j\le q}
+ \atop \scriptstyle 1\le k\le r} a_{ij} b_{jk} c_{ki}$$|.
+
+Since mathematical formulas can get horribly large, \TeX\ has to have some
+way to make ever-larger symbols. For example, if you type
+\begintt
+$$\sqrt{1+\sqrt{1+\sqrt{1+
+ \sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+x}}}}}}}$$
+\endtt
+the result shows a variety of available ^{square-root signs}:
+\begindisplay
+$\displaystyle\sqrt{1+\sqrt{1+\sqrt{1+
+ \sqrt{1+\sqrt{1+\sqrt{1+\sqrt{1+x}}}}}}}$
+\enddisplay
+The three largest signs here are all essentially the same, except for a
+vertical segment `\vbox{\hbox{\tenex\char'165}\vss}' that gets repeated as
+often as necessary to reach the desired size; but the smaller signs are
+distinct characters found in \TeX's math fonts.
+
+A similar thing happens with parentheses and other so-called
+``^{delimiter}'' symbols. For example, here are some of the different sizes of
+^^{fences, see delimiters}
+^{parentheses} and ^{braces} that plain \TeX\ might use in formulas:
+\begindisplay
+$\displaystyle
+\left(\vbox to 27pt{}\left(\vbox to 24pt{}\left(\vbox to 21pt{}
+\Biggl(\biggl(\Bigl(\bigl(({\scriptstyle({\scriptscriptstyle(\hskip3pt
+)})})\bigr)\Bigr)\biggr)\Biggr)\right)\right)\right)
+\left\{\vbox to 27pt{}\left\{\vbox to 24pt{}\left\{\vbox to 21pt{}
+\Biggl\{\biggl\{\Bigl\{\bigl\{\{{\scriptstyle\{{\scriptscriptstyle\{\hskip3pt
+\}}\}}\}\bigr\}\Bigr\}\biggr\}\Biggr\}\right\}\right\}\right\}$
+\enddisplay
+The three largest pairs in each case are made with repeatable extensions,
+so they can become as large as necessary.
+^^{pieces of symbols}
+
+Delimiters are important to mathematicians, because they provide good
+visual clues to the underlying structure of complex expressions; they delimit
+the boundaries of individual subformulas. Here is a list of the 22~basic
+delimiters provided by plain \TeX:
+\begindisplay
+\it Input&\it Delimiter\cr
+\noalign{\vskip2pt}
+|(|&left parenthesis: $($\cr
+|)|&right parenthesis: $)$\cr
+|[| or ^|\lbrack|&left bracket: $[$\cr
+|]| or ^|\rbrack|&right bracket: $]$\cr
+|\{| or ^|\lbrace|&left curly brace: $\{$\cr
+|\}| or ^|\rbrace|&right curly brace: $\}$\cr
+^|\lfloor|&left floor bracket: $\lfloor$\cr
+^|\rfloor|&right floor bracket: $\rfloor$\cr
+^|\lceil|&left ceiling bracket: $\lceil$\cr
+^|\rceil|&right ceiling bracket: $\rceil$\cr
+^|\langle|&left angle bracket: $\langle$\cr
+^|\rangle|&right angle bracket: $\rangle$\cr
+|/|&slash: $/$\cr
+^|\backslash|&reverse slash: $\backslash$\cr
+\| or ^|\vert|&vertical bar: $\vert$\cr
+|\|\| or ^|\Vert|&double vertical bar: $\Vert$\cr
+^|\uparrow|&upward arrow: $\uparrow$\cr
+^|\Uparrow|&double upward arrow: $\Uparrow$\cr
+^|\downarrow|&downward arrow: $\downarrow$\cr
+^|\Downarrow|&double downward arrow: $\Downarrow$\cr
+^|\updownarrow|&up-and-down arrow: $\updownarrow$\cr
+^|\Updownarrow|&double up-and-down arrow: $\Updownarrow$\cr
+\enddisplay
+^^{bent bars, see langle, rangle} ^^{curly braces, see lbrace, rbrace}
+^^{leftbracket}^^{rightbracket}^^{leftbrace}^^{rightbrace}^^{/}
+In some cases, there are two ways to get the same delimiter; for example,
+you can specify a left bracket by typing either `|[|' or `|\lbrack|'. The
+latter alternative has been provided because the symbol `|[|' is not
+readily available on all computer keyboards. Remember, however,
+that you should never try to specify a left brace or right brace simply by
+typing `|{|' or `|}|'; the |{| and |}| symbols are reserved for grouping.
+The right way is to type `|\{|' or `|\}|' or `|\lbrace|' or `|\rbrace|'.
+
+In order to get a slightly larger version of any of these symbols, just
+precede them by `^|\bigl|' (for opening delimiters) or `^|\bigr|' (for
+closing ones). This makes it easier to read formulas that contain
+delimiters inside delimiters:
+\beginlongmathdemo
+\it Input&\it Output\cr
+\noalign{\vskip2pt}
+|$\bigl(x-s(x)\bigr)\bigl(y-s(y)\bigr)$|&
+ \bigl(x-s(x)\bigr)\bigl(y-s(y)\bigr)\cr
+|$\bigl[x-s[x]\bigr]\bigl[y-s[y]\bigr]$|&
+ \bigl[x-s[x]\bigr]\bigl[y-s[y]\bigr]\cr
+|$\bigl|\|| |\||x|\||-|\||y|\|| \bigr|\||$|&
+ \bigl\vert\vert x\vert-\vert y\vert\bigr\vert\cr
+|$\bigl\lfloor\sqrt A\bigr\rfloor$|&
+ \bigl\lfloor\sqrt A\bigr\rfloor\cr
+\endmathdemo
+The |\big| delimiters are just enough bigger than ordinary ones so that
+the difference can be perceived, yet small enough to be used in the text
+of a paragraph. Here are all~22 of them, in the ordinary size and in
+the |\big| size:
+\begindisplay
+$(\,)\,[\,]\,\{\,\}\,\lfloor\,\rfloor\,\lceil\,\rceil\,\langle\,\rangle
+ \,/\,\backslash\,\vert\,\Vert\,\uparrow\,\Uparrow\,\downarrow\,\Downarrow
+ \,\updownarrow\,\Updownarrow$\cr
+\noalign{\smallskip}
+$\bigl(\,\bigr)\,\bigl[\,\bigr]\,\bigl\{\,\bigr\}\,\bigl\lfloor
+ \,\bigr\rfloor\,\bigl\lceil\,\bigr\rceil\,\bigl\langle\,\bigr\rangle
+ \,\big/\,\big\backslash\,\big\vert\,\big\Vert\,\bigm\uparrow\,\bigm\Uparrow
+ \,\bigm\downarrow\,\bigm\Downarrow\,\bigm\updownarrow\,\bigm\Updownarrow$\cr
+\enddisplay
+You can also type ^|\Bigl| and ^|\Bigr| to get larger symbols suitable for
+displays:
+\begindisplay
+$\Bigl(\,\Bigr)\,\Bigl[\,\Bigr]\,\Bigl\{\,\Bigr\}\,\Bigl\lfloor
+ \,\Bigr\rfloor\,\Bigl\lceil\,\Bigr\rceil\,\Bigl\langle\,\Bigr\rangle
+ \,\Big/\,\Big\backslash\,\Big\vert\,\Big\Vert\,\Bigm\uparrow\,\Bigm\Uparrow
+ \,\Bigm\downarrow\,\Bigm\Downarrow\,\Bigm\updownarrow\,\Bigm\Updownarrow$
+\enddisplay
+These are 50\% taller than their |\big| counterparts. Displayed formulas
+most often use delimiters that are even taller (twice the size of\/ |\big|);
+such delimiters are constructed by ^|\biggl| and ^|\biggr|, and they
+look like this:
+\begindisplay
+$\biggl(\,\biggr)\,\biggl[\,\biggr]\,\biggl\{\,\biggr\}\,\biggl\lfloor
+ \,\biggr\rfloor\,\biggl\lceil\,\biggr\rceil\,\biggl\langle\,\biggr\rangle
+ \,\bigg/\,\bigg\backslash\,\bigg\vert\,\bigg\Vert\,\biggm\uparrow
+ \,\biggm\Uparrow\,\biggm\downarrow\,\biggm\Downarrow\,\biggm\updownarrow
+ \,\biggm\Updownarrow$
+\enddisplay
+Finally, there are ^|\Biggl| and ^|\Biggr| versions, 2.5 times as tall
+as the |\bigl| and |\bigr| delimiters:
+\begindisplay
+$\Biggl(\,\Biggr)\,\Biggl[\,\Biggr]\,\Biggl\{\,\Biggr\}\,\Biggl\lfloor
+ \,\Biggr\rfloor\,\Biggl\lceil\,\Biggr\rceil\,\Biggl\langle\,\Biggr\rangle
+ \,\Bigg/\,\Bigg\backslash\,\Bigg\vert\,\Bigg\Vert\,\Biggm\uparrow
+ \,\Biggm\Uparrow\,\Biggm\downarrow\,\Biggm\Downarrow\,\Biggm\updownarrow
+ \,\Biggm\Updownarrow$
+\enddisplay
+
+\medskip
+\exercise Guess how to type the formula $\displaystyle
+\biggl({\partial^2\over\partial x^2}+{\partial^2\over\partial y^2}
+ \biggr)\bigl\vert\varphi(x+iy)\bigr\vert^2=0$, in display style,
+using |\bigg| delimiters for the large parentheses. \ (The symbols $\partial$
+and $\varphi$ that appear here are called ^|\partial| and ^|\varphi|.)
+\answer |$\displaystyle\biggl({\partial^2\over\partial x^2}+|\hfil\break
+|{\partial^2\over\partial y^2}\biggr)\bigl|\||\varphi(x+iy)\bigr|\||^2=0$|.
+
+\dangerexercise In practice, |\big| and |\bigg| delimiters are used much
+more often than |\Big| and |\Bigg| ones. Why do you think this is true?
+\answer Formulas that are more than one line tall are usually two lines tall,
+not 1$1\over2$ or 2$1\over2$ lines tall.
+
+\danger A |\bigl| or |\Bigl| or |\biggl| or |\Biggl| delimiter is an
+^{opening}, like a left parenthesis;
+a |\bigr| or |\Bigr| or |\biggr| or |\Biggr| delimiter is a
+^{closing}, like a right parenthesis. Plain \TeX\ also provides
+^|\bigm| and ^|\Bigm| and ^|\biggm| and ^|\Biggm| delimiters, for use
+in the middle of formulas; such a delimiter plays the r\^ole of a ^{relation},
+like an equals sign, so \TeX\ puts a bit of space on either side of it.
+\beginlongmathdemo
+|$\bigl(x\in A(n)\bigm|\||x\in B(n)\bigr)$|&
+ \tenmath\bigl(x\in A(n)\bigm\vert x\in B(n)\bigr)\cr
+\noalign{\vskip2pt}
+|$\bigcup_n X_n\bigm\|\||\bigcap_n Y_n$|&
+ \tenmath\bigcup_n X_n\bigm\Vert\bigcap_n Y_n\cr
+\endmathdemo
+^^|\bigcup|^^|\bigcap|^^|\verticalline|^^|\in|
+You can also say just ^|\big| or ^|\Big| or ^|\bigg| or ^|\Bigg|; this produces
+a delimiter that acts as an ordinary variable. It is used primarily with
+slashes and backslashes, as in the following example.
+\beginlongmathdemo
+\noalign{\vskip-2pt}
+|$${a+1\over b}\bigg/{c+1\over d}$$|&
+ \tenmath\displaystyle{a+1\over b}\bigg/{c+1\over d}\cr
+\endmathdemo
+
+\dangerexercise What's the professional way to type
+$\tenmath\bigl(x+f(x)\bigr)\big/\bigl(x-f(x)\bigr)$? \ (Look closely.)
+\answer |$\bigl(x+f(x)\bigr) \big/ \bigl(x-f(x)\bigr)$|. \ Notice especially
+the `|\big/|'; an ordinary ^{slash} would look too small between the
+|\big| parentheses.
+
+\TeX\ has a built-in mechanism that figures out how tall a pair of delimiters
+needs to be, in order to enclose a given subformula; so you can use this
+method, instead of deciding whether a delimiter should be |\big| or
+|\bigg| or whatever. All you do is say
+\begindisplay
+^|\left|\<delim$_1$>\<subformula>^|\right|\<delim$_2$>
+\enddisplay
+and \TeX\ will typeset the subformula, putting the specified delimiters at
+the left and the right. The size of the delimiters will be just big enough
+to cover the subformula. For example, in the display
+\beginlongdisplaymathdemo
+|$$1+\left(1\over1-x^2\right)^3$$|&1+\left(1\over1-x^2\right)^3\cr
+\endmathdemo
+\TeX\ has chosen |\biggl(| and |\biggr)|, because smaller delimiters
+would be too small for this particular fraction. A simple formula like
+`|$\left(x\right)$|' yields just `$\left(x\right)$'; thus, |\left| and
+|\right| sometimes choose delimiters that are smaller than |\bigl| and |\bigr|.
+
+Whenever you use |\left| and |\right| they must pair up with each other,
+just as braces do in groups. You can't have |\left| in one formula
+and |\right| in another, nor are you allowed to type things like
+`|\left(...{...\right)...}|' or
+`|\left(...\begingroup...\right)...\endgroup|'.
+This restriction makes sense, because \TeX\ needs to typeset the
+subformula that appears between |\left| and |\right| before it can decide
+how big to make the delimiters. But it is worth explicit mention here,
+because you do {\sl not\/} have to match ^{parentheses} and ^{brackets}, etc.,
+^^{crotchets, see brackets}
+when you are not using |\left| and |\right|: \TeX\ will not complain if
+you input a formula like `|$[0,1)$|' or even `|$)($|' or just `|$)$|'.\
+(And it's a good thing \TeX\ doesn't, for such unbalanced formulas occur
+surprisingly often in mathematics papers.) \ Even when you do use |\left|
+and |\right|, \TeX\ doesn't look closely at the particular delimiters that
+you happen to choose; thus, you can type strange things like `|\left)|'
+and/or `|\right(|' if you know what you're doing. Or even if you don't.
+
+The |\over| operation in the example displayed above does not involve the
+`|1+|' at the beginning of the formula; this happens because |\left| and
+|\right| have the function of ^{grouping}, in addition to their function
+of delimiter-making. Any definitions that you happen to make between
+|\left| and |\right| will be local, as if braces had appeared around the
+enclosed subformula.
+
+\exercise Use |\left| and |\right| to typeset the following display
+(with ^|\phi| for $\phi$):
+$$\pi(n)=\sum_{k=2}^n\left\lfloor\phi(k)\over k-1\right\rfloor.$$
+\answer |$$\pi(n)=\sum_{k=2}^n\left\lfloor\phi(k)\over k-1\right\rfloor.$$|
+
+At this point you are probably wondering why you should bother learning about
+|\bigl| and |\bigr| and their relatives, when |\left| and |\right| are there
+to calculate sizes for you automatically. Well, it's true that |\left|
+and |\right| are quite handy, but there are at least three situations in which
+you will want to use your own wisdom when selecting the proper delimiter size:
+\ (1)~Sometimes |\left| and |\right| choose a smaller delimiter than you want.
+For example, we used |\bigl| and |\bigr| to produce $\bigl\vert\vert x\vert-
+\vert y\vert\bigr\vert$ in one of the previous illustrations; |\left| and
+|\right| don't make things any bigger than necessary, so
+`|$\left|\||\left|\||x\right|\||-\left|\||y\right|\||\right|\||$|'
+yields only
+`$\left\vert \left\vert x\right\vert -\left\vert y\right\vert \right\vert$'.
+\ (2)~Sometimes |\left| and |\right| choose a larger delimiter than you want.
+This happens most frequently when they enclose a large operator in a display;
+for example, compare the following two formulas:
+\beginlongdisplaymathdemo
+\noalign{\vskip 6pt}
+|$$\left( \sum_{k=1}^n A_k \right)$$|&\left( \sum_{k=1}^n A_k \right)\cr
+\noalign{\vskip 3pt}
+|$$\biggl( \sum_{k=1}^n A_k \biggr)$$|&\biggl( \sum_{k=1}^n A_k \biggr)\cr
+\endmathdemo
+The rules of\/ |\left| and |\right| cause them to enclose the ^|\sum| together
+with its ^{limits}, but in special cases like this it looks better to let
+the limits hang out a~bit; |\bigg| delimiters are better here.
+\ (3)~Sometimes you need to break a huge displayed
+formula into two or more separate lines, and you want to make sure that
+its opening and closing delimiters have the same size; but you can't use
+|\left| on the first line and |\right| on the last, since |\left| and
+|\right| must occur in pairs. The solution is to use |\Biggl| (say) on
+the first line and |\Biggr| on the last.
+
+\danger Of course, one of the advantages of\/ |\left| and |\right| is that
+they can make arbitrarily large delimiters---much bigger than |\biggggg|!
+The slashes and angle brackets do have a maximum size, however; if you
+ask for really big versions of those symbols you will get the largest
+ones available.
+
+\exercise Prove that you have mastered delimiters: Coerce \TeX\ into
+producing the formula
+$$\pi(n)=\sum_{m=2}^n\left\lfloor\biggl(\sum_{k=1}^{m-1}\bigl\lfloor
+ (m/k)\big/\lceil m/k\rceil\bigr\rfloor\biggr)^{-1}\right\rfloor.$$
+\answer |$$\pi(n)=\sum_{m=2}^n\left\lfloor\biggl(\sum_{k=1}^{m-1}\bigl|
+\hfil\break
+|\lfloor(m/k)\big/\lceil m/k\rceil\bigr\rfloor\biggr)^{-1}\right\rfloor.$$|
+
+\danger If you type `|.|'\ after |\left| or |\right|, instead of
+specifying one of the basic delimiters, you get a so-called ^{null
+delimiter} (which is blank). Why on earth would anybody want that, you may
+ask. Well, you sometimes need to produce formulas that contain only one
+large delimiter. For example, the display
+$$\vert x\vert=\cases{x,&if $x\ge0$\cr
+ -x,&if $x<0$\cr}$$
+has a `$\{$' but no `$\}$'. It can be produced by a construction of the form
+\begindisplay
+|$$|\||x|\||=\left\{ ... \right.$$|
+\enddisplay
+Chapter 18 explains how to fill in the `\hbox{|...|}' to finish this
+construction; let's just notice for now that the `|\right.|'\ makes it
+possible to have an invisible right delimiter to go with the visible
+left brace.
+
+\ddanger A null delimiter isn't completely void; it is an empty box
+whose width is a \TeX\ parameter called ^|\nulldelimiterspace|.
+We will see later that null delimiters are inserted next to fractions.
+Plain \TeX\ sets |\nulldelimiterspace=1.2pt|.
+
+You can type `|<|' or `|>|' as convenient abbreviations for ^|\langle| and
+^|\rangle|, when \TeX\ is looking for a delimiter. For example,
+`|\bigl<|' is equivalent to `|\bigl\langle|', and `|\right>|' is
+equivalent to `|\right\rangle|'. Of course `|<|' and `|>|' ordinarily
+produce the ^{less-than} and ^{greater-than} relations `${<}\,{>}$', which
+are quite different from ^{angle brackets} `$\langle\,\rangle$'.
+
+\danger Plain \TeX\ also makes available a few more delimiters, which were
+not listed in the basic set of~22 because they are sort of special.
+The control sequences ^|\arrowvert|, ^|\Arrowvert|, and ^|\bracevert| produce
+delimiters made from the repeatable parts of the vertical arrows, double
+vertical arrows, and large braces, respectively, without the arrowheads
+or the curly parts of the braces. They produce results similar to
+^|\vert| or ^|\Vert|, but they are surrounded by more white space and
+they have a different weight. You can also use ^|\lgroup| and ^|\rgroup|,
+which are constructed from braces without the middle parts; and
+^|\lmoustache| and ^|\rmoustache|, ^^{moustaches}
+which give you the top and bottom halves of large braces. For example,
+here are the |\Big| and |\bigg| versions of\/ |\vert|, |\Vert|, and
+these seven special delimiters:
+$$\halign{\indent$#\hfil$\cr
+\ldots\Big\vert\ldots\Big\Vert
+\ldots\Big\arrowvert\ldots\Big\Arrowvert\ldots\Big\bracevert
+\ldots\Big\lgroup\ldots\Big\rgroup\ldots\Big\lmoustache\ldots\Big\rmoustache
+\ldots\,;\cr
+\noalign{\smallskip}
+\ldots\bigg\vert\ldots\bigg\Vert
+\ldots\bigg\arrowvert\ldots\bigg\Arrowvert\ldots\bigg\bracevert
+\ldots\bigg\lgroup\ldots\bigg\rgroup\ldots\bigg\lmoustache\ldots\bigg\rmoustache
+\ldots\,.\cr}$$
+Notice that |\lgroup| and |\rgroup| are rather like bold parentheses, with
+sharper bends at the corners; this makes them attractive for certain large
+displays. But you cannot use them exactly like parentheses, because
+they are available only in large sizes (|\Big|~or~more).
+
+\ddanger Question: What happens if a ^{subscript} or ^{superscript}
+follows a large delimiter? Answer:~That's a good question. After a |\left|
+delimiter, it is the first subscript or superscript of the enclosed
+subformula, so it is effectively preceded by |{}|. After a |\right|
+delimiter, it is a subscript or superscript of the entire |\left...\right|
+subformula. And after a |\bigl| or |\bigr| or |\bigm| or |\big| delimiter,
+it applies only to that particular delimiter. Thus, `|\bigl(_2|' works
+quite differently from `|\left(_2|'.
+
+\danger If you look closely at the examples of math typesetting in this
+chapter, you will notice that large parentheses and brackets are
+symmetric with respect to an invisible horizontal line that runs a little
+bit above the ^{baseline}; when a delimiter gets larger, its height and
+depth both grow by the same amount. This horizontal line is called the
+{\sl^{axis}\/} of the formula; for example, a formula in the text of the
+present paragraph would have an axis at this level: $\hskip 2em\over$. The
+bar line in every fraction is centered on the axis, regardless of the size
+of the numerator or denominator.
+
+\danger Sometimes it is necessary to create a special box that should be
+centered vertically with respect to the axis. \ (For example, the
+`$\vert x\vert=\bigl\{\,\ldots$' example above was done with such a box.) \
+\TeX\ provides a simple way to do this: You just say
+\begindisplay
+|\vcenter{|\<vertical mode material>|}|
+\enddisplay
+and the vertical mode material will be packed into a box just as if
+^|\vcenter| had been ^|\vbox|. Then the box will be raised or lowered until
+its top edge is as far above the axis as the bottom edge is below.
+
+\ddanger The concept of ``axis'' is meaningful for \TeX\ only in math
+formulas, not in ordinary text; therefore \TeX\ allows you to use
+|\vcenter| only in math mode. If you really need to center something
+vertically in horizontal mode, the solution is to say `|$\vcenter{...}$|'.
+ \ (Incidentally, the constructions `|\vcenter| |to|\<dimen>'
+and `|\vcenter| |spread|\<dimen>' are legal too, in math mode;
+vertical glue is always set by the rules for |\vbox| in
+Chapter~12. But |\vcenter| by itself is usually sufficient.)
+
+\danger Any box can be put into a formula by simply saying ^|\hbox| or
+|\vbox| or ^|\vtop| or ^|\box| or ^|\copy| in the normal way, even when
+you are in math mode. Furthermore you can use ^|\raise| or ^|\lower|, as
+if you were in horizontal mode, and you can insert vertical rules with
+^|\vrule|. Such constructions, like |\vcenter|, produce boxes that can be
+used like ordinary symbols in math formulas.
+
+\ddanger Sometimes you need to make up your own symbols, when you run across
+something unusual that doesn't occur in the fonts. If the new symbol
+occurs only in one place, you can use |\hbox| or |\vcenter| or something
+to insert exactly what you want; but if you are defining a macro for
+general use, you may want to use different constructions in different
+styles. \TeX\ has a special feature called ^|\mathchoice| that comes
+to the rescue in such situations: You write
+\begindisplay
+|\mathchoice{|\<math>|}{|\<math>|}{|\<math>|}{|\<math>|}|
+\enddisplay
+where each \<math> specifies a subformula. \TeX\ will choose the first
+subformula in style $D$ or~$D'$, the second in style $T$ or~$T'$, the
+third in style $S$ or~$S'$, the fourth in style $\SS$ or $\SS'$.
+\ (\TeX\ actually typesets all four subformulas, before it chooses the
+final one, because the actual style is not always known at the time a
+|\mathchoice| is encountered; for example, when you type `|\over|' you often
+change the style of everything that has occurred earlier in the formula.
+Therefore |\mathchoice| is somewhat expensive in terms of time and space,
+and you should use it only when you're willing to pay the price.)
+
+\ddangerexercise Guess what output is produced by the following commands:
+\begintt
+\def\puzzle{{\mathchoice{D}{T}{S}{SS}}}
+$$\puzzle{\puzzle\over\puzzle^{\puzzle^\puzzle}}$$
+\endtt
+\answer A displayed formula equivalent to |$${D}{{T}\over{T}^{{S}^{SS}}}$$|.
+
+\ddangerexercise Devise a `^|\square|' macro that produces a
+\def\sqr#1#2{{\vcenter{\vbox{\hrule height.#2pt
+ \hbox{\vrule width.#2pt height#1pt \kern#1pt \vrule}
+ \hrule height.#2pt}}}}%
+`$\,\sqr34\,$' for use in math formulas. The box should be symmetrical
+with respect to the axis, and its inside dimensions should be $3\pt$ in
+display and text styles, $2.1\pt$ in script styles, and $1.5\pt$ in
+scriptscript styles. The rules should be $0.4\pt$ thick in
+display and text styles, $0.3\pt$ thick otherwise.
+\answer |\def\sqr#1#2{{\vcenter{\vbox{\hrule height.#2pt|\parbreak
+ | \hbox{\vrule width.#2pt height#1pt \kern#1pt|\parbreak
+ | \vrule width.#2pt}|\parbreak
+ | \hrule height.#2pt}}}}|\parbreak
+ |\def\square{\mathchoice\sqr34\sqr34\sqr{2.1}3\sqr{1.5}3}|
+
+\ddanger Plain \TeX\ has a macro called ^|\mathpalette| that is useful
+for |\mathchoice| constructions; `|\mathpalette\a{xyz}|' expands to
+the four-pronged array of choices
+`|\mathchoice|\stretch|{\a|\stretch|\displaystyle|\stretch|{xyz}}|\stretch
+|...|\stretch|{\a|\stretch|\scriptscriptstyle|\stretch|{xyz}}|\stretch'.
+Thus the first argument to |\mathpalette| is a control sequence whose
+first argument is a style selection. Appendix~B contains several examples
+that show how |\mathpalette| can be applied. \ (See in particular the
+definitions of\/ |\phantom|, |\root|, and |\smash|; the ^{congruence sign}
+^|\cong| ($\cong$) is also constructed from $=$ and $\sim$ using
+|\mathpalette|.)
+^^{constructing new math symbols}
+^^{math symbols, construction of}
+
+\ddanger At the beginning of this chapter we discussed the commands
+|\over|, |\atop|, |\choose|, and |\above|. These are special cases of
+\TeX's ``^{generalized fraction}'' feature, which includes also the
+three primitives
+\begindisplay
+|\overwithdelims|\<delim$_1$>\<delim$_2$>\cr
+|\atopwithdelims|\<delim$_1$>\<delim$_2$>\cr
+|\abovewithdelims|\<delim$_1$>\<delim$_2$>\<dimen>\cr
+\enddisplay
+The third of these is the most general, as it encompasses all of the other
+generalized fractions: ^|\overwithdelims| uses a ^{fraction} bar whose
+thickness is the default for the current size, and ^|\atopwithdelims| uses
+an invisible fraction bar whose thickness is zero, while
+^|\abovewithdelims| uses a bar whose thickness is specified explicitly.
+\TeX\ places the immediately preceding subformula (the ^{numerator}) over
+the immediately following subformula (the ^{denominator}), separated by a
+bar line of the desired thickness; then it puts \<delim$_1$> at the left
+and \<delim$_2$> at the right. For example, `^|\choose|' is equivalent to
+`|\atopwithdelims()|'. If you define |\legendre| to be
+`|\overwithdelims()|', you can typeset the ^{Legendre symbol}
+\def\legendre{\overwithdelims()}%
+`$a\legendre b$' by saying `|{a\legendre b}|'. The size of the surrounding
+delimiters depends only on the style, not on the size of the fractions;
+larger delimiters are used in styles $D$ and~$D'$ (see Appendix~G\null). The
+simple commands ^|\over|, ^|\atop|, and ^|\above| are equivalent to the
+corresponding `|withdelims|' commands when the delimiters are null; for
+example, `|\over|' is an abbreviation for `|\overwithdelims..|'.
+
+\def\euler{\atopwithdelims<>}
+\ddangerexercise Define a control sequence |\euler| so that the
+^{Eulerian number} $n\euler k$ will be produced when you type `|{n\euler k}|'
+in a formula.
+\answer|\def\euler{\atopwithdelims<>}|.
+
+\ddanger Appendix G explains exactly how \TeX\ computes the desired size
+of delimiters for |\left| and~|\right|. The general idea is that delimiters
+are vertically centered with respect to the ^{axis}; hence, if we want
+to cover a subformula between |\left| and |\right| that extends $y_1$~units
+above the axis and $y_2$~units below, we need to make a delimiter whose
+height plus depth is at least $y$~units, where $y=2\max(y_1,y_2)$.
+It is usually best not to cover the formula completely, however,
+but just to come close; so \TeX\ allows you to specify
+two parameters, the ^|\delimiterfactor|~$f$ (an~integer) and the
+^|\delimitershortfall|~$\delta$ (a~dimension). The minimum delimiter size
+is taken to be at least $y\cdot f/1000$, and at least $y-\delta$. Appendix~B
+sets $f=901$ and $\delta=5\pt$. Thus, if $y=30\pt$, the plain \TeX\ format
+causes the delimiter to be more than $27\pt$ tall; if $y=100\pt$, the
+corresponding delimiter will be at least $95\pt$ tall.
+
+\danger So far we have been discussing the rules for typing math formulas,
+but we haven't said much about how \TeX\ actually goes about converting
+its input into lists of boxes and glue. Almost all of the control
+sequences that have been mentioned in Chapters 16 and~17 are ``high level''
+features of the plain \TeX\ format; they are not built into \TeX\ itself.
+Appendix~B defines those control sequences in terms of more primitive
+commands that \TeX\ actually deals with. For example, `|\choose|' is
+an abbreviation for `|\atopwithdelims()|'; Appendix~B not only introduces
+|\choose|, it also tells \TeX\ where to find the delimiters |(| and~|)|
+in various sizes. The plain \TeX\ format defines all of the special
+characters like |\alpha| and~|\mapsto|, all of the special accents like
+|\tilde| and~|\widehat|, all of the large operators like |\sum| and~|\int|,
+and all of the delimiters like |\lfloor| and~|\vert|. Any of these things
+can be redefined, in order to adapt \TeX\ to other mathematical styles
+and/or to other fonts.
+
+\danger The remainder of this chapter discusses the low-level commands
+that \TeX\ actually obeys behind the scenes. Every paragraph on the next
+few pages is marked with double dangerous bends, so you should skip to
+Chapter~18 unless you are a glutton for \TeX nicalities.
+
+\ninepoint
+\ddanger All characters that are typeset in math mode belong to one of
+sixteen {\sl^{families} of fonts}, numbered internally from 0 to~15. Each
+of these families consists of three fonts: one for text size, one for
+script size, and one for scriptscript size. The commands ^|\textfont|,
+^|\scriptfont|, and ^|\scriptscriptfont| are used to specify the members
+of each family. For example, ^{family~0} in the plain \TeX\ format is
+used for roman letters, and Appendix~B contains the instructions
+\begintt
+\textfont0=\tenrm
+\scriptfont0=\sevenrm
+\scriptscriptfont0=\fiverm
+\endtt
+to set up this family: The 10-point roman font (^|\tenrm|) is used for
+normal symbols, 7-point roman (^|\sevenrm|) is used for subscripts, and
+5-point roman (^|\fiverm|) is used for sub-subscripts. Since there are up to
+256~characters per font, and 3~fonts per family, and 16~families, \TeX\ can
+access up to 12,288 characters in any one formula (4096 in~each of
+the three sizes). Imagine that.
+
+\ddanger A definition like |\textfont|\<family number>|=|\<font identifier>
+is local to the group that contains it, so you can easily change family
+membership from one set of conventions to another and back again. Furthermore
+you can put any font into any family; for example, the command
+\begintt
+\scriptscriptfont0=\scriptfont0
+\endtt
+makes sub-subscripts in family~0 the same size as the subscripts currently
+are. \TeX\ doesn't check to see if the families are sensibly organized; it
+just follows instructions. \ (However, fonts cannot be used in families
+2 and~3 unless they contain a certain number of special parameters, as we
+shall see later.) \ Incidentally, \TeX\ uses ^|\nullfont|, which contains
+no characters, for each family member that has not been defined.
+
+\ddanger During the time that a math formula is being read,
+\TeX\ remembers each symbol as being ``character position so-and-so in
+family number such-and-such,'' but it does not take note of what fonts
+are actually in the families until reaching the end of the formula.
+Thus, if you have loaded a font called |\Helvetica| that contains Swiss-style
+numerals, and if you say something like
+\begintt
+$\textfont0=\tenrm 9 \textfont0=\Helvetica 9$
+\endtt
+you will get two 9's in font |\Helvetica|, assuming that \TeX\ has been
+set up to take 9's from family~0. The reason is that |\textfont0|
+is~|\Helvetica| at the end of the formula, and that's when it counts. On
+the other hand, if you say
+\begintt
+$\textfont0=\tenrm 9 \hbox{$9\textfont0=\Helvetica$}$
+\endtt
+the first 9 will be from |\tenrm| and the second from |\Helvetica|, because
+the formula in the hbox will be typeset before it is incorporated into
+the surrounding formula.
+
+\ddangerexercise If you say `|${\textfont0=\Helvetica 9}$|', what
+font will be used for the~9?
+\answer The |\textfont0| that was current at the beginning of the formula
+will be used, because this redefinition is local to the braces. \
+(It would be a different story if `^|\global||\textfont|' had appeared instead;
+that would have changed the meaning of\/ |\textfont0| at all levels.)
+
+\ddanger Every ^{math character} is given an identifying code
+number between 0 and~4095, obtained by adding 256~times the family number
+to the position number. This is easily expressed in ^{hexadecimal
+notation}, using one hexadecimal digit for the family and two for the
+character; for example, \hex{24A} stands for character~\hex{4A} in
+family~2. Each character is also assigned to one of eight classes,
+^^{classes of math characters, table} ^^{math codes} ^^{table of ...}
+numbered 0 to~7, as follows:
+$$\halign{\indent#\hfil&\quad#\hfil&\quad#\hfil&
+\hskip4em#\hfil&\quad#\hfil&\quad#\hfil\cr
+\it \kern-2pt Class&\it Meaning&\kern-2pt\it Example&
+\it \kern-2pt Class&\it Meaning&\kern-2pt\it Example\cr
+\noalign{\vskip2pt}
+0&Ordinary&|/|&
+4&Opening&|(|\cr
+1&Large operator&|\sum|&
+5&Closing&|)|\cr
+2&Binary operation&|+|&
+6&Punctuation&|,|\cr
+3&Relation&|=|&
+7&Variable family&|x|\cr
+}$$
+^^{large operator}^^{binary operation}^^{relation}^^{opening}^^{closing}
+^^{punctuation}^^{variable family}
+Classes 0 to 6 tell what ``part of speech'' the character belongs to, in
+math-printing language; class~7 is a special case discussed below. The class
+number is multiplied by 4096 and added to the character number, and this
+is the same as making it the leading digit of a four-digit hexadecimal
+number. For example, Appendix~B defines |\sum| to be the math character
+\hex{1350}, meaning that it is a large operator (class~1) found in position
+\hex{50} of family~3.
+
+\ddangerexercise The ^|\oplus| and ^|\bullet| symbols ($\oplus$ and $\bullet$)
+are binary operations that appear in positions 8 and~15 (decimal)
+of family~2, when the fonts of plain~\TeX\ are being used. Guess
+what their math character codes are. \ (This is too easy.)
+\answer \hex{2208} and \hex{220F}.
+
+\ddanger Class 7 is a special case that allows math symbols to change families.
+It behaves exactly like class~0, except that the specified family is
+replaced by the current value of an integer parameter called ^|\fam|,
+provided that |\fam| is a legal family number (i.e., if it lies between
+0 and~15). \TeX\ automatically sets |\fam=-1| whenever math mode is entered;
+therefore class~7 and class~0 are equivalent unless |\fam| has been
+given a new value. Plain \TeX\ changes |\fam| to~0 when the user
+types `^|\rm|'; this makes it convenient to get roman letters in formulas,
+as we will see in Chapter~18, since letters belong to class~7. \ (The
+control sequence |\rm| is an abbreviation for `|\fam=0 \tenrm|'; thus,
+|\rm| causes
+|\fam| to become zero, and it makes |\tenrm| the ``^{current font}.''
+In horizontal mode, the |\fam| value is irrelevant and the current font
+governs the typesetting of letters; but in math mode, the current font is
+irrelevant and the |\fam| value governs the letters. The current font
+affects math mode only if\/ |\|\] is used ^^{control space} or if
+dimensions are given in ^|ex| or ^|em| units;
+it also has an effect if an |\hbox| appears inside a formula, since
+the contents of an hbox are typeset in horizontal mode.)
+
+\ddanger The interpretation of characters in math mode is defined by a
+table of~256 ``mathcode'' values; these table entries can be changed
+by the ^|\mathcode| command, just as the category codes are changed
+by ^|\catcode| (see Chapter~7). Each mathcode specifies class, family, and
+character position, as described above. For example, Appendix~B contains
+the commands
+\begintt
+\mathcode`<="313C
+\mathcode`*="2203
+\endtt
+which cause \TeX\ to treat the character `|<|' in math mode as a relation
+^^{less than} (class~3) found in position \hex{3C} of family~1, and to treat an
+^{asterisk} `|*|' as a binary operation found in position~3 of family~2.
+The initial value of\/ |\mathcode`b| is \hex{7162}; thus, |b|~is character
+\hex{62} in ^{family~1} (italics), and its family will vary with |\fam|.
+\ (|INITEX| starts out with |\mathcode|$\,x=x$ for all characters~$x$
+that are neither
+letters nor digits. The ten digits have |\mathcode|$\,x=x+\hbox{\hex{7000}}$;
+the 52 letters have |\mathcode|$\,x=x+\hbox{\hex{7100}}$.) \
+\TeX\ looks at the mathcode only when it is typesetting a character whose
+catcode is 11~(letter) or 12~(other), or when it encounters a character that
+is given explicitly as ^|\char|\<number>.
+
+\ddanger A |\mathcode| can also have the special value \hex{8000}, which
+causes the character to behave as if it has catcode~13 (active). Appendix~B
+uses this feature to make |'| ^^{apostrophe} expand to |^{|^|\prime||}| in a
+slightly tricky way. The mathcode of |'| does not ^^{active math character}
+interfere with the use of |'| in ^{octal} constants.
+
+\ddanger The mathcode table allows you to refer indirectly to any character in
+any family, with the touch of a single key. You can also specify a math
+character code directly, by typing ^|\mathchar|, which is analogous to
+^|\char|. For example, the command `|\mathchar"1ABC|' specifies a
+character of class~1, family~10 (\hex A), and position \hex{BC}. A~hundred
+or so definitions like
+\begintt
+\def\sum{\mathchar"1350 }
+\endtt
+would therefore suffice to define the special symbols of plain \TeX\null. But
+there is a better way: \TeX\ has a primitive command ^|\mathchardef|,
+which relates to |\mathchar| just as ^|\chardef| does to |\char|.
+Appendix~B has a hundred or so definitions like
+\begintt
+\mathchardef\sum="1350
+\endtt
+to define the special symbols. A |\mathchar| must be between 0 and 32767
+(\hex{7FFF}).
+
+\ddanger A character of class~1, i.e., a ^{large operator} like |\sum|, will
+be vertically centered with respect to the axis when it is typeset. Thus,
+the large operators can be used with different sizes of type. This vertical
+adjustment is not made for symbols of the other classes.
+
+\ddanger \TeX\ associates classes with subformulas as well as with individual
+characters. Thus, for example, you can treat a complex construction as if
+it were a binary operation or a relation, etc., if you want to. The
+commands ^|\mathord|, ^|\mathop|, ^|\mathbin|, ^|\mathrel|, ^|\mathopen|,
+^|\mathclose|, and ^|\mathpunct| are used for this purpose; each of them
+is followed either by a single character or by a subformula in braces.
+For example, |\mathopen\mathchar"1234| is equivalent to |\mathchar"4234|,
+because |\mathopen| forces class~4 (opening). In the formula
+`|$G\mathbin:H$|', the ^{colon} is~treated as a binary operation.
+And Appendix~B constructs large opening symbols by
+defining ^|\bigl||#1| to be an abbreviation for
+\begintt
+\mathopen{\hbox{$\left#1 ...\right.$}}
+\endtt
+There's also an eighth classification, ^|\mathinner|, which is not
+normally used for individual symbols; fractions and ^|\left||...|^|\right|
+constructions are treated as ``inner'' subformulas, which means that
+they will be surrounded by additional space in certain circumstances.
+All other subformulas are generally treated as ordinary symbols,
+whether they are formed by |\overline| or |\hbox| or |\vcenter| or
+by simply being enclosed in braces. Thus, |\mathord| isn't really
+a necessary part of the \TeX\ language; instead of typing
+`|$1\mathord,234$|' you can get the same effect from `|$1{,}234$|'.
+
+\ddangerexercise Commands like |\mathchardef\alpha="010B| are used in
+Appendix~B to define the lowercase ^{Greek} letters. Suppose that you want
+to extend plain \TeX\ by putting ^{boldface math italic} letters
+in family~9, analogous to the normal math italic letters in family~1.
+\ (Such fonts aren't available in stripped down versions of \TeX, but
+let's assume that they exist.) \ Assume that the control sequence
+|\bmit| has been defined as an abbreviation for `|\fam=9|'; hence
+`|{\bmit b}|' will give a boldface math italic~|b|. What change to the
+definition of\/ |\alpha| will make |{\bmit\alpha}| produce a boldface~alpha?
+\checkequals\bmiexno\exno
+\answer |\mathchardef\alpha="710B|. Incidentally, |{\rm\alpha}| will
+then give a spurious result, because character position \hex{0B} of
+roman fonts does not contain an alpha; you should warn
+your users about what characters they are allowed to type under the
+influence of special conventions like ^|\rm|.
+
+\ddanger ^{Delimiters} are specified in a similar but more complicated
+way. Each character has not only a~|\catcode| and a~|\mathcode| but also
+a~^|\delcode|, which is either negative (for characters that should not
+act as delimiters) or less than \hex{1000000}. In other words,
+nonnegative delcodes consist of six hexadecimal digits. The first three
+digits specify a ``small'' variant of the delimiter, and the last three
+specify a ``large'' variant. For example, the command
+\begintt
+\delcode`x="123456
+\endtt
+means that if the letter |x| is used as a delimiter, its small variant
+is found in position \hex{23} of family~1, and its large variant is found
+in position \hex{56} of family~4. If the small or large variant is
+given as |000|, however (position~0 of ^{family~0}), that variant is ignored.
+\TeX\ looks at the delcode when a character follows ^|\left| or ^|\right|,
+or when a character follows one of the ^|withdelims| commands; a
+negative delcode leads to an error message, but otherwise \TeX\ finds
+a suitable delimiter by first trying the small variant and then
+the large. \ (Appendix~G discusses this process in more detail.) \
+For example, Appendix~B contains the commands
+\begintt
+\delcode`(="028300 \delcode`.=0
+\endtt
+which specify that the small variant of a left parenthesis is found in
+position \hex{28} of family~0, and that the large variant is in position~0
+of family~3; also, a period has no variants, hence `|\left.|'\ will produce
+a ^{null delimiter}. There actually are several different left parenthesis
+symbols in family~3; the smallest is in position~0, and the others are
+linked together by information that comes with the font. All delcodes
+are~$-1$ until they are changed by a |\delcode| command.
+
+\ddangerexercise Appendix~B defines |\delcode`<| so that there is a
+shorthand notation for ^{angle brackets}. Why do you think Appendix~B
+doesn't go further and define |\delcode`{|?
+\answer If\/ |\delcode`{| were set to some nonnegative delimiter code, you
+would get no error message when you wrote something like `|\left{|'.
+This would be bad because strange effects would happen when certain
+subformulas were given as arguments to macros, or when they appeared
+in alignments. But it has an even worse defect, because a user who
+gets away with `|\left{|' is likely to try also `|\bigl{|', which
+fails miserably.
+
+\ddanger A delimiter can also be given directly, as `^|\delimiter|\<number>'.
+In this case the number can be as high as \hex{7FFFFFF}, i.e., seven
+hexadecimal digits; the leading digit specifies a class, from 0 to~7,
+as in a |\mathchar|. For example, Appendix~B contains the definition
+\begintt
+\def\langle{\delimiter"426830A }
+\endtt
+and this means that ^|\langle| is an opening (class 4) whose small
+variant is \hex{268} and whose large variant is \hex{30A}. When |\delimiter|
+appears after |\left| or |\right|, the class digit is ignored; but
+when |\delimiter| occurs in other contexts, i.e., when \TeX\ isn't
+looking for a delimiter, the three rightmost digits are dropped and
+the remaining four digits act as a |\mathchar|. For example, the expression
+`|$\langle x$|' is treated as if it were `|$\mathchar"4268 x$|'.
+
+\ddangerexercise What goes wrong if you type
+`|\bigl\delimiter"426830A|'\thinspace?
+\answer Since |\bigl| is defined as a macro with one parameter,
+it gets just `|\delimiter|' as the argument. You have to write
+`|\bigl{\delimiter"426830A}|' to make this work. On the other hand,
+|\left| will balk if the following character is a left brace. Therefore
+it's best to have control sequence names for all delimiters.
+
+\ddanger Granted that these numeric conventions for |\mathchar| and
+|\delimiter| are not beautiful, they sure do pack a lot of information into
+a small space. That's why \TeX\ uses them for low-level definitions inside
+formats. Two other low-level primitives also deserve to be mentioned:
+^|\radical| and ^|\mathaccent|. Plain \TeX\ makes ^{square root signs}
+and math accents available by giving the commands
+\begintt
+\def\sqrt{\radical"270370 }
+\def\widehat{\mathaccent"362 }
+\endtt
+and several more like them. The idea is that |\radical| is followed by
+a delimiter code and |\mathaccent| is followed by a math character code,
+so that \TeX\ knows the family and character positions for the symbols
+used in radical and accent constructions. Appendix~G gives precise
+information about the positioning of these characters. By changing the
+definitions, \TeX\ could easily be extended so that it would typeset a
+variety of different radical signs and a variety of different accent
+signs, if such symbols were available in the fonts.
+^^{surd signs, see radical}
+
+\ddanger Plain \TeX\ uses ^{family~1} for math italic letters, ^{family~2} for
+ordinary math symbols, and ^{family~3} for large symbols. \TeX\ insists that
+^^{math fonts}
+the fonts in families 2 and~3 have special ^|\fontdimen| parameters,
+which govern mathematical spacing according to the rules in Appendix~G\null;
+the ^|cmsy| and ^|cmex| ^{symbol fonts} have these parameters, so
+their assignment to families 2 and~3 is almost mandatory. \ (There is, however,
+a way to modify the parameters of any font, using the ^|\fontdimen| command.) \
+^|INITEX| initializes the mathcodes of all ^{letters} |A| to~|Z| and |a| to~|z|
+so that they are symbols of class~7 and family~1; that's why
+it is natural to use family~1 for math italics. Similarly, the digits |0|
+to~|9| are class~7 and family~0. None of the other families
+is treated in any special way by \TeX. Thus, for example, plain \TeX\ puts
+^{text italic} in family~4, slanted roman in family~5, bold roman in family~6,
+and typewriter type in family~7, but any of these numbers could be
+switched around. There is a macro ^|\newfam|, analogous to |\newbox|,
+that will assign symbolic names to families that aren't already used.
+
+\ddanger When \TeX\ is in horizontal mode, it is making a horizontal list;
+in vertical mode, it is making a vertical list. Therefore it should come
+as no great surprise that \TeX\ is making a ^{math list} when it is in
+^{math mode}. The contents of horizontal lists were explained in Chapter~14,
+and the contents of vertical lists were explained in Chapter~15; it's time
+now to describe what math lists are made of. Each item in a math list
+is one of the following types of things:\enddanger
+
+\smallskip
+\item\bull an ^{atom} (to be explained momentarily);
+
+\item\bull horizontal material (a rule or discretionary or penalty or
+``whatsit'');
+
+\item\bull vertical material (from |\mark| or |\insert| or |\vadjust|);
+
+\item\bull a glob of ^{glue} (from |\hskip| or |\mskip| or |\nonscript|);
+
+\item\bull a ^{kern} (from |\kern| or |\mkern|);
+
+\item\bull a ^{style change} (from |\displaystyle|, |\textstyle|, etc.);
+
+\item\bull a ^{generalized fraction} (from |\above|, |\over|, etc.);
+
+\item\bull a ^{boundary} (from |\left| or |\right|);
+
+\item\bull a four-way ^{choice} (from ^|\mathchoice|).
+
+\ddanger The most important items are called {\sl atoms}, and they have
+three parts: a {\sl^{nucleus}}, a {\sl^{superscript}}, and a {\sl^{subscript}}.
+For example, if you type
+\begintt
+(x_i+y)^{\overline{n+1}}
+\endtt
+in math mode, you get a math list consisting of five atoms:
+$($, $x_i$, $+$, $y$, and~$)^{\overline{n+1}}$. The nuclei of these atoms
+are $($, $x$, $+$, $y$, and~$)$; the subscripts are empty except for the
+second atom, which has subscript~$i$; the superscripts are empty except for the
+last atom, whose superscript is~$\overline{n+1}$. This superscript is
+itself a math list consisting of one atom, whose nucleus is~$n+1$; and that
+nucleus is a math list consisting of three atoms.
+
+\ddanger There are thirteen kinds of atoms, each of which might act
+differently in a formula; for example, `$($' is an Open atom because
+^^{atomic types, table}
+it comes from an opening. Here is a complete list of the different kinds:
+$$\halign{\indent#\hfil&\enskip#\hfil\cr
+Ord&is an ordinary atom like `$x$'\thinspace;\cr
+Op&is a large operator atom like `$\sum$'\thinspace;\cr
+Bin&is a binary operation atom like `$+$'\thinspace;\cr
+Rel&is a relation atom like `$=$'\thinspace;\cr
+Open&is an opening atom like `$($'\thinspace;\cr
+Close&is a closing atom like `$)$'\thinspace;\cr
+Punct&is a punctuation atom like `$,$'\thinspace;\cr
+Inner&is an inner atom like `$1\over2$'\thinspace;\cr
+Over&is an overline atom like `$\overline x$'\thinspace;\cr
+Under&is an underline atom like `$\underline x$'\thinspace;\cr
+Acc&is an accented atom like `$\hat x$'\thinspace;\cr
+Rad&is a radical atom like `$\sqrt2$'\thinspace;\cr
+Vcent&is a vbox to be centered, produced by |\vcenter|.\cr
+}$$
+
+\ddanger An atom's nucleus, superscript, and subscript are called its
+{\sl ^{fields}}, and there are four possibilities for each of these fields.
+A field can be\enddanger
+
+\smallskip
+\item\bull empty;
+
+\item\bull a math symbol (specified by family and position number);
+
+\item\bull a box; or
+
+\item\bull a math list.
+
+\smallskip\noindent
+For example, the Close atom $)^{\overline{n+1}}$ considered above has an
+empty subscript field; its nucleus is the symbol `$)$', which is
+character~\hex{28} of family~0 if the conventions of plain \TeX\ are
+in force; and its superscript field is the math list $\overline{n+1}$.
+The latter math list consists of an Over atom whose nucleus
+is the math list $n+1$; and that math list, in turn, consists of
+three atoms of types Ord, Bin, Ord.
+
+\ddanger You can see \TeX's view of a math list by typing ^|\showlists|
+in math mode. ^^{internal list format}
+For example, after `|$(x_i+y)^{\overline{n+1}}\showlists|' your log
+file gets the following curious data:
+\begindisplay
+|\mathopen|\cr
+|.\fam0 (|\cr
+|\mathord|\cr
+|.\fam1 x|\cr
+|_\fam1 i|\cr
+\noalign{\penalty-500}
+|\mathbin|\cr
+|.\fam0 +|\cr
+\noalign{\penalty-500}
+|\mathord|\cr
+|.\fam1 y|\cr
+\noalign{\penalty-500}
+|\mathclose|\cr
+|.\fam0 )|\cr
+|^\overline|\cr
+|^.\mathord|\cr
+|^..\fam1 n|\cr
+|^.\mathbin|\cr
+|^..\fam0 +|\cr
+|^.\mathord|\cr
+|^..\fam0 1|\cr
+\enddisplay
+In our previous experiences with |\showlists| we observed that there can
+be boxes within boxes, and that each line in the log file is
+prefixed by dots to indicate its position in the hierarchy. Math lists
+have a slightly more complex structure; therefore a dot is used to denote
+the nucleus of an atom, a~`|^|' is used for the superscript field, and
+a~`|_|' is used for the subscript field. Empty fields are not shown. Thus,
+for example, the Ord atom~$x_i$ is represented here by three lines
+`|\mathord|', `|.\fam1 x|', and `|_\fam1 i|'.
+
+\ddanger Certain kinds of atoms carry additional information besides their
+nucleus, subscript, and superscript fields: An Op atom will be marked
+`^|\limits|' or `^|\nolimits|' if the normal ^|\displaylimits|
+convention has been overridden; a Rad atom contains
+a delimiter field to specify what radical sign is to be used; and an Acc atom
+contains the family and character codes of the accent symbol.
+
+\ddanger When you say ^|\hbox||{...}| in math mode, an Ord atom is placed
+on the current math list, with the hbox as its nucleus. Similarly,
+^|\vcenter||{...}| produces a Vcent atom whose nucleus is a box. But in
+most cases the nucleus of an atom will be either a symbol or a math list.
+You can experiment with |\showlists| to discover how other things like
+fractions and mathchoices are represented internally.
+
+\ddanger Chapter~26 contains complete details of how math lists are
+constructed. As soon as math mode ends (i.e., when the closing `|$|'
+occurs), \TeX\ dismantles the current math list and converts it into a
+horizontal list. The rules for this conversion are spelled out in
+Appendix~G\null. You can see ``before and after'' representations of such math
+typesetting by ending a formula with `|\showlists$\showlists|'; the first
+|\showlists| will display the math list, and the second will show the
+(possibly complex) horizontal list that is manufactured from it.
+
+\endchapter
+
+The learning time is short. A few minutes gives the general flavor, and
+typing a page or two of a paper generally uncovers most of the misconceptions.
+\author ^{KERNIGHAN} and ^{CHERRY}, {\sl A System for %
+ Typesetting Mathematics\/} (1975)
+ % in {\sl Communications of the ACM\/} p152
+
+\bigskip
+
+Within a few hours (a few days at most)
+a typist with no math or typesetting experience
+can be taught to input even the most complex equations.
+\author PETER J. ^{BOEHM}, {\sl Software and Hardware Considerations %
+ for a\break Technical Typesetting System\/} (1976)
+% in {\sl IEEE Transactions on Professional Communication\/} PC-19, pp15--19
+
+\eject
+ \beginchapter Chapter 18. Fine Points of\\Mathematics\\Typing
+
+We have discussed most of the facilities needed to construct math
+formulas, but there are several more things a good mathematical typist
+will want to watch for. After you have typed a dozen or so formulas using
+the basic ideas of Chapters 16 and~17, you will find that it's easy to
+visualize the final appearance of a mathematical expression as you type
+it. And once you have gotten to that level, there's only a little bit
+more to learn before you are producing formulas as beautiful as any the
+world has ever seen; tastefully applied touches of \TeX nique will add a
+professional polish that works wonders for the appearance and readability
+of the books and papers that you type. This chapter talks about such
+tricks, and it also fills in a few gaps by mentioning some aspects of math
+that didn't fit comfortably into~Chapters~16~and~17.
+
+\subsection Punctuation. When a formula is followed by a ^{period}, ^{comma},
+^{semicolon}, ^{colon}, ^{question mark}, ^{exclamation point}, etc., put the
+^{punctuation} {\sl after\/} the |$|, when the formula is in the text; but
+put the punctuation {\sl before\/} the |$$| when the formula is displayed.
+For example,
+\begintt
+If $x<0$, we have shown that $$y=f(x).$$
+\endtt
+\TeX's spacing rules within paragraphs work best when the
+^{punctuation marks} are not considered to be part of the formulas.
+
+Similarly, don't ever type anything like
+\begintt
+for $x = a, b$, or $c$.
+\endtt
+It should be
+\begintt
+for $x = a$, $b$, or $c$.
+\endtt
+(Better yet, use a ^{tie}: `|or~$c$|'.) \ The reason is that \TeX\ will
+typeset expression `|$x|~|=|~|a,|~|b$|' as a single formula, so it will
+put a ``^{thin space}'' between the comma and the $b$. This space will
+not be the same as the space that \TeX\ puts after the
+comma {\sl after\/} the $b$, since spaces between words are always bigger than
+thin spaces. Such unequal spacing looks bad, but when you type things right
+the spacing will look good.
+
+Another reason for not typing `|$x| |=| |a,| |b$|' is that it inhibits the
+possibilities for breaking lines in a paragraph: \TeX\ will never break at
+the space between the comma and the |b| because breaks after commas in
+formulas are usually wrong. For example, in the equation
+`|$x|~|=|~|f(a,|~|b)$|' we certainly don't want to put `$x=f(a,$' on one
+line and `$b)$' on the next.
+
+Thus, when typing formulas in the text of a paragraph, keep the math properly
+segregated: Don't take operators like $-$ and $=$ outside of the |$|'s,
+and keep commas inside the formula if they are truly part of the formula.
+But if a comma or period or other punctuation mark belongs linguistically
+to the sentence rather than to the formula, leave it outside the |$|'s.
+
+\exercise Type this: $R(n,t)=O(t^{n/2})$, as $t\to0^+$.
+\answer |$R(n,t)=O(t^{n/2})$, as $t\to0^+$.| \
+(N.B.: `|O(|', not `|0('|.)
+
+\danger Some mathematical styles insert a bit of extra space around
+formulas to separate them from the text. For example, when copy is
+being produced on an ordinary typewriter that doesn't have italic
+letters, the best technical typists have traditionally put an extra
+blank space before and after each formula, because this provides a
+useful visual distinction. You might find it helpful to think of
+each |$| as a symbol that has the potential of adding a little space
+to the printed output; then the rule about excluding sentence
+punctuation from formulas may be easier to remember.
+
+\ddanger \TeX\ does, in fact, insert additional ^{space} before and after each
+formula; the amount of such space is called ^|\mathsurround|, which is
+a \<dimen>-valued parameter. For example, if you set |\mathsurround=1pt|,
+each formula will effectively be 2~points wider ($1\pt$ at each side):
+$$\baselineskip13pt\halign{\indent\mathsurround=#pt
+ For $x=a$, $b$, or $c$.\hfil&\quad(#)\hfil\cr
+1&|\mathsurround=1pt|\cr 0&|\mathsurround=0pt|\cr}$$
+This extra space will disappear into the left or right margin if the formula
+occurs at the beginning or end of a line. The value of\/ |\mathsurround| that
+is in force when \TeX\ reads the closing |$| of a formula is used at both
+left and right of that formula. Plain \TeX\ takes |\mathsurround=0pt|, so
+you won't see any extra space unless you are using some other format, or
+unless you change |\mathsurround| yourself.
+
+\subsection Non-italic letters in formulas. The names of algebraic variables
+are usually italic or Greek letters, but common mathematical functions
+like `log' are always set in ^{roman type}. The best way to deal with such
+constructions is to make use of the following 32~control sequences (all of
+which are defined in plain \TeX\ format, see Appendix~B):
+\begintt
+\arccos \cos \csc \exp \ker \limsup \min \sinh
+\arcsin \cosh \deg \gcd \lg \ln \Pr \sup
+\arctan \cot \det \hom \lim \log \sec \tan
+\arg \coth \dim \inf \liminf \max \sin \tanh
+\endtt
+^^|\arccos| ^^|\cos| ^^|\csc| ^^|\exp| ^^|\ker| ^^|\limsup| ^^|\min|
+^^|\sinh| ^^|\arcsin| ^^|\cosh| ^^|\deg| ^^|\gcd| ^^|\lg| ^^|\ln| ^^|\Pr|
+^^|\sup| ^^|\arctan| ^^|\cot| ^^|\det| ^^|\hom| ^^|\lim| ^^|\log| ^^|\sec|
+^^|\tan| ^^|\arg| ^^|\coth| ^^|\dim| ^^|\inf| ^^|\liminf| ^^|\max|
+^^|\sin| ^^|\tanh|
+These control sequences lead to roman type with appropriate
+spacing:\def\sep{&\hskip-1em}
+\beginlongmathdemo
+\it Input\sep\it Output\cr
+\noalign{\vskip2pt}
+|$\sin2\theta=2\sin\theta\cos\theta$|\sep\sin2\theta=2\sin\theta\cos\theta\cr
+|$O(n\log n\log\log n)$|\sep O(n\log n\log\log n)\cr
+|$\Pr(X>x)=\exp(-x/\mu)$|\sep\Pr(X>x)=\exp(-x/\mu)\cr
+|$$\max_{1\le n\le m}\log_2P_n$$|\sep
+ \displaystyle{\max_{1\le n\le m}\log_2P_n}\cr
+\noalign{\vskip2pt}
+|$$\lim_{x\to0}{\sin x\over x}=1$$|\sep\displaystyle{\lim_{x\to0}
+ {\sin x\over x}=1}\cr
+\endmathdemo
+^^|\mu|
+The last two formulas, which are displays, show that some of the special
+control sequences are treated by \TeX\ as ``large operators'' with limits
+just like $\sum$: The subscript on |\max| is not treated like the subscript
+on |\log|. Subscripts and superscripts will become limits
+when they are attached to |\det|, |\gcd|, |\inf|, |\lim|, |\liminf|,
+|\limsup|, |\max|, |\min|, |\Pr|, and |\sup|, in display style.
+
+\exercise Express the following display in plain \TeX\ language, using
+`^|\nu|' for `$\nu$':
+$$p_1(n)=\lim_{m\to\infty}\sum_{\nu=0}^\infty
+ \bigl(1-\cos^{2m}(\nu!^n\pi/n)\bigr).$$
+\answer |$$p_1(n)=\lim_{m\to\infty}\sum_{\nu=0}^\infty|\parbreak
+| \bigl(1-\cos^{2m}(\nu!^n\pi/n)\bigr).$$|\par
+\smallskip\noindent $\bigl[$Mathematicians may enjoy
+interpreting this formula; cf.~G.~H. ^{Hardy},
+{\sl Messenger of Mathematics\/ \bf35} (1906), 145--146.$\bigr]$
+
+\danger If you need roman type for some mathematical function or operator
+that isn't included in plain \TeX's list of~32, it is easy to define a
+new control sequence by mimicking the definitions in Appendix~B\null. Or, if
+you need roman type just for a ``one shot'' use, it is even easier to
+get what you want by switching to ^|\rm| type, as follows:
+\beginlongmathdemo
+|$\sqrt{{\rm Var}(X)}$|&\sqrt{{\rm Var}(X)}\cr
+|$x_{\rm max}-x_{\rm min}$|&x_{\rm max}-x_{\rm min}\cr
+|${\rm LL}(k)\Rightarrow{\rm LR}(k)$|&{\rm LL}(k)\Rightarrow{\rm LR}(k)\cr
+|$\exp(x+{\rm constant})$|&\exp(x+{\rm constant})\cr
+|$x^3+{\rm lower\ order\ terms}$|&x^3+{\rm lower\ order\ terms}\cr
+\endmathdemo
+Notice the uses of `|\|\]' ^^{control space} in the last case;
+without them, the result would have been `$x^3+{\rm lower order terms}$',
+because ordinary blank ^{spaces} are ignored in math mode.
+
+\danger You can also use ^|\hbox| instead of\/ |\rm| to get roman letters
+into formulas. For example, four of the last five formulas can be
+generated by
+\beginlongmathdemo
+|$\sqrt{\hbox{Var}(X)}$|&\sqrt{\hbox{Var}(X)}\cr
+|$\hbox{LL}(k)\Rightarrow\hbox{LR}(k)$|&\hbox{LL}(k)\Rightarrow\hbox{LR}(k)\cr
+|$\exp(x+\hbox{constant})$|&\exp(x+\hbox{constant})\cr
+|$x^3+\hbox{lower order terms}$|&x^3+\hbox{lower order terms}\cr
+\endmathdemo
+In this case `|\|\]' isn't necessary, because the material in an
+|\hbox| is processed in horizontal mode, when spaces are significant.
+But such uses of\/ |\hbox| have two disadvantages: \ (1)~The contents of the
+box will be typeset in the same size, whether or not the box occurs as a
+subscript; for example, `|$x_{\hbox{max}}$|' yields `$x_{\hbox{max}}$'.
+\ (2)~The font that's used inside |\hbox| will be the ``^{current font},''
+so it might not be roman. For example, if you are typesetting the statement
+of some theorem that is in slanted type, and if that theorem refers
+to `|$\sqrt{\hbox{Var}(X)}$|', you will get the unintended result
+`{\sl$\sqrt{\hbox{Var}(X)}$}'. In order to make sure that an |\hbox| uses
+roman type, you need to specify |\rm|, e.g., `|$\sqrt{\hbox{\rm Var}(X)}$|';
+and then the |\hbox| serves no purpose. We will see later, however, that
+|\hbox| can be very useful in displayed formulas.
+
+\ddangerexercise When the displayed formula
+`|$$\lim_{n\to\infty}x_n {\rm\ exists} \iff|\break
+|\limsup_{n\to\infty}x_n = \liminf_{n\to\infty}x_n.$$|' is typeset with
+the standard macros of plain \TeX, you get
+$$\lim_{n\to\infty}x_n{\rm\ exists}\iff
+ \limsup_{n\to\infty}x_n=\liminf_{n\to\infty}x_n.$$
+But some people prefer a different notation: Explain how you could change
+the definitions of\/ ^|\limsup| and ^|\liminf| so that the display would be
+$$
+\def\limsup{\mathop{\overline{\rm lim}}}
+\def\liminf{\mathop{\underline{\rm lim}}}
+\lim_{n\to\infty}x_n{\rm\ exists}\iff
+ \limsup_{n\to\infty}x_n=\liminf_{n\to\infty}x_n.$$
+\answer |\def\limsup{\mathop{\overline{\rm lim}}}|\parbreak
+|\def\liminf{\mathop{\underline{\rm lim}}}|\par
+\smallskip\noindent
+[Notice that the limits `$n\to\infty$' appear at different levels, in both
+of the displays, because `sup' and the underbar descend below the baseline.
+It is possible to unify the limit positions by using ^{phantoms}, as explained
+later in this chapter. For example,
+\begintt
+\def\limsup{\mathop{\vphantom{\underline{}}\overline{\rm lim}}}
+\endtt
+would give lower limits in the same position as |\liminf|.]
+
+\danger The word `mod' is also generally set in roman type, when it occurs
+in formulas; but this word needs more care, because it is used in two
+different ways that require two different treatments.
+Plain \TeX\ provides two different control sequences,
+^|\bmod| and ^|\pmod|, for the two cases: |\bmod| is to be used when
+`mod' is a ^{binary operation} (i.e., when it occurs between two quantities,
+like a plus sign usually does), and |\pmod| is to be used when
+`mod' occurs parenthetically at the end of a formula. For example,
+\beginmathdemo
+|$\gcd(m,n)=\gcd(n,m\bmod n)$|&\gcd(m,n)=\gcd(n,m\bmod n)\cr
+|$x\equiv y+1\pmod{m^2}$|&x\equiv y+1\pmod{m^2}\cr
+\endmathdemo
+The `|b|' in `|\bmod|' stands for ``binary''; the `|p|' in `|\pmod|' stands
+for ``parenthesized.'' Notice that |\pmod| inserts its own parentheses;
+the quantity that appears after `mod' in the parentheses should be
+enclosed in braces, if it isn't a single symbol.
+
+\dangerexercise What did poor B. L. ^{User} get when he typed
+`|$x\equiv0 (\pmod y^n)$|'\thinspace?
+\answer $x\equiv0(\pmod y^n)$. He should have typed
+`|$x\equiv0\pmod{y^n}$|'.
+
+\dangerexercise Explain how to produce \lower12pt\null\
+$\smash{\displaystyle{n\choose k}\equiv{\lfloor n/p\rfloor\choose
+\lfloor k/p\rfloor}{n\bmod p\choose k\bmod p}\pmod p.}$
+\answer |$${n\choose k}\equiv{\lfloor n/p\rfloor\choose|\parbreak
+| \lfloor k/p\rfloor}{n\bmod p\choose k\bmod p}\pmod p.$$|
+
+\danger The same mechanism that works for roman type in formulas can be used
+to get other styles of type as well. For example, ^|\bf| yields ^{boldface}:
+\beginmathdemo
+|$\bf a+b=\Phi_m$|&\bf a+b=\Phi_m\cr
+\endmathdemo
+Notice that whole formula didn't become emboldened in this example; the
+`$+$' and `$=$' stayed the same. Plain \TeX\ sets things up so
+that commands like |\rm| and |\bf| will affect only the uppercase letters
+|A|~to~|Z|, the lowercase letters |a|~to~|z|, the digits |0|~to~|9|,
+the uppercase Greek letters |\Gamma| to~|\Omega|, and math ^{accents}
+like ^|\hat| and ^|\tilde|. Incidentally, no braces were used in this
+example, because |$|'s have the effect of grouping; |\bf| changes the
+current font, but the change is local, so it does not affect the font that
+was current outside the formula.
+
+\ddanger The bold fonts available in plain \TeX\ are ``bold roman,'' rather
+than ``bold italic,'' because the latter are rarely needed. However, \TeX\
+could readily be set up to make use of bold math italics, if desired
+(see Exercise 17.\bmiexno). A more extensive set of math fonts would also
+include ^{script}, ^{Fraktur}, and ``^{blackboard bold}'' styles; plain
+\TeX\ doesn't have these, but other formats like \AmSTeX\ do. ^^{AMS-TeX}
+^^{German black letters}
+
+\danger Besides |\rm| and |\bf|, you can say ^|\cal| in formulas to get
+uppercase letters in a ``^{calligraphic}'' style. For example, `|$\cal
+A$|' produces `$\cal A$' and `|$\cal Z$|' produces `$\cal Z$'. But beware:
+This works only with the letters |A| to |Z|; you'll get weird results if
+you apply |\cal| to lowercase or Greek letters.
+
+\danger There's also ^|\mit|, which stands for ``^{math italic}.'' This
+affects ^{uppercase Greek}, so that you get
+$\mit(\Gamma,\Delta,\Theta,\Lambda,\Xi,\Pi,\Sigma,\Upsilon,\Phi,\Psi,\Omega)$
+instead of $(\Gamma,\ldots,\Omega)$. When~|\mit| is in effect, the
+ordinary letters |A| to |Z| and |a| to |z| are not changed; they are set
+in italics as usual, because they ordinarily come from the math italic
+font. Conversely, uppercase Greek letters and math accents are unaffected
+by |\rm|, because they ordinarily come from the roman font. Math accents
+should not be used when the |\mit| family has been selected, because the
+math italic font contains no accents.
+
+\dangerexercise Type the formula $\bf\bar x^{\rm T}Mx={\rm0}\iff x=0$,
+using as few keystrokes as possible. ^^{boldface numbers in math}
+\ (The first `0' is roman, the second is bold. The superscript `T' is roman.)
+\answer |$\bf\bar x^{\rm T}Mx={\rm0}\iff x=0$|. \ (If you typed a space between
+|\rm| and~|0|, you wasted a keystroke; but don't feel guilty about it.)
+
+\dangerexercise Figure out how to typeset
+`$S\subseteq\mit\Sigma\iff S\in\cal S$'.
+\answer |$S\subseteq{\mit\Sigma}\iff S\in{\cal S}$|. In this case the
+braces are redundant and could be eliminated; but you shouldn't try to do
+{\sl everything\/} with fewest keystrokes, or you'll outsmart yourself
+some day.
+
+\danger Plain \TeX\ also allows you to type ^|\it|, ^|\sl|, or ^|\tt|, if
+you want text italic, slanted, or typewriter letters to occur in a math
+formula. However, these fonts are available only in text size, so you
+should not try to use them in subscripts.
+
+\danger If you're paying attention, you probably wonder why both
+|\mit| and |\it| are provided; the answer is that |\mit| is ``math italic''
+(which is normally best for formulas), and |\it| is ``^{text italic}'' (which
+is normally best for running text).
+\beginmathdemo
+|$This\ is\ math\ italic.$|&This\ is\ math\ italic.\cr
+|{\it This is text italic.}|&\hbox{\it This is text italic.}\cr
+\endmathdemo
+The math italic letters are a little wider, and the spacing is different;
+this works better in most formulas, but it fails spectacularly when
+you try to type certain italic words like `$different$' using math mode
+(`|$different$|'). A wide `$f$' is usually desirable in formulas, but it
+is undesirable in text. Therefore wise typists
+use |\it| in a math formula that is supposed
+to contain an actual italic word. Such cases almost never occur in
+classical mathematics, but they are common when ^{computer programs}
+are being typeset, since programmers often use multi-letter ``^{identifiers}'':
+\beginmathdemo
+|$\it last:=first$|&\it last:=first\cr
+|$\it x\_coord(point\_2)$|&\it x\_coord(point\_2)\cr
+\endmathdemo
+The first of these examples shows that \TeX\ recognizes the ^{ligature}
+`{\it fi\/}' when text italic occurs in a math formula;
+the other example illustrates the use of short ^{underlines} to break
+up identifier names. ^^{control-underline}
+When the author typeset this manual, he used `|$\it SS$|' to refer to
+style~$\SS$, since `|$SS$|' makes the $S$'s too far apart: $SS$.
+
+\dangerexercise What plain \TeX\ commands will produce the following display?
+$$\tenmath
+{\it available}+\sum_{i=1}^n\max\bigl({\it full}(i),{\it reserved}(i)\bigr)
+ ={\it capacity}.$$
+\answer |$${\it available}+\sum_{i=1}^n\max\bigl({\it full}(i),|\parbreak
+ |{\it reserved}(i)\bigr)={\it capacity}.$$|
+\smallskip\noindent [If\/ |\it| had been used throughout
+the formula, the subscript~$i$ and superscript~$n$ would have caused error
+messages saying `^|\scriptfont| |4| |is| |undefined|',
+since plain \TeX\ makes |\it| available only in text size.]
+
+\ddangerexercise How would you go about typesetting the following computer
+program, using the macros of plain \TeX?
+$$\vbox{\let\par=\endgraf
+\obeylines\sfcode`;=3000
+{\bf for $j:=2$ step $1$ until $n$ do}
+\quad {\bf begin} ${\it accum}:=A[j]$; $k:=j-1$; $A[0]:=\it accum$;
+\quad {\bf while $A[k]>\it accum$ do}
+\qquad {\bf begin} $A[k+1]:=A[k]$; $k:=k-1$;
+\qquad {\bf end};
+\quad $A[k+1]:=\it accum$;
+\quad {\bf end}.
+}$$
+\answer |{\obeylines \sfcode`;=3000|^^|\sfcode|\parbreak
+|{\bf for $j:=2$ step $1$ until $n$ do}|\parbreak
+|\quad {\bf begin} ${\it accum}:=A[j]$; $k:=j-1$; $A[0]:=\it accum$;|\parbreak
+|\quad {\bf while $A[k]>\it accum$ do}|\parbreak
+|\qquad {\bf begin} $A[k+1]:=A[k]$; $k:=k-1$;|\parbreak
+|\qquad {\bf end};|\parbreak
+|\quad $A[k+1]:=\it accum$;|\parbreak
+|\quad {\bf end}.\par}|\par
+\smallskip\noindent
+[This is something like the ``poetry'' example in Chapter~14, but much
+more difficult. Some manuals of style say that ^{punctuation} should inherit
+the font of the preceding character, so that three kinds of semicolons
+should be typeset; e.g., these experts recommend `$k:=j-1$; \
+$A[0]:={}${\it accum;} \ {\bf end;}'. The author heartily disagrees.]
+
+\subsection Spacing between formulas. ^{Displays} often contain more than one
+formula; for example, an equation is frequently accompanied by a ^{side
+condition}:
+$$F_n=F_{n-1}+F_{n-2},\qquad n\ge2.$$
+In such cases you need to tell \TeX\ how much space to put after the comma,
+because \TeX's normal spacing conventions would bunch things together;
+without special precautions you would get
+$$F_n=F_{n-1}+F_{n-2}, n\ge2.$$
+
+The traditional hot-metal technology for printing has led to some ingrained
+standards for situations like this, based on what printers call a ``^{quad}''
+of space. Since these standards seem to work well in practice, \TeX\ makes
+it easy for you to continue the tradition: When you type `^|\quad|' in plain
+\TeX\ format, you get a printer's quad of space in the horizontal direction.
+Similarly, `^|\qquad|' gives you a double quad (twice as much); this
+is the normal spacing for situations like
+the $F_n$ example above. Thus, the recommended procedure is to type
+\begintt
+$$ F_n = F_{n-1} + F_{n-2}, \qquad n \ge 2. $$
+\endtt
+It is perhaps worth reiterating that \TeX\ ignores all the spaces in math
+mode (except, of course, the space after `|\qquad|', which is needed
+to distinguish between `|\qquad|~|n|' and `|\qquadn|'); so the same result
+would be obtained if you were to leave out all but one space:
+\begintt
+$$F_n=F_{n-1}+F_{n-2},\qquad n\ge2.$$
+\endtt
+Whenever you want spacing that differs from the normal conventions, you must
+specify it explicitly by using control sequences such as |\quad| and |\qquad|.
+
+\danger A quad used to be a square piece of blank type, $1\em$ wide and $1\em$
+tall---approximately the size of a capital M, as explained in Chapter~10. This
+tradition has not been fully retained: The control sequence |\quad| in plain
+\TeX\ is simply an abbreviation for `|\hskip|~|1|^|em||\relax|', so \TeX's
+quad has width but no height.
+
+\danger You can use |\quad| in text as well as in formulas; for example,
+Chapter~14 illustrates how |\quad| applies to poetry. When |\quad| appears
+in a formula it stands for one~em in the current text font, independent of the
+current math size or style or family. Thus, for example, |\quad| is just
+as wide in a subscript as it is on the main line of a formula.
+
+Sometimes a careless author will put two formulas next to each other in
+the text of a paragraph. For example, you might find a sentence like this:
+\begindisplay
+The ^{Fibonacci} numbers satisfy $F_n=F_{n-1}+F_{n-2}$, \ $n\ge2$.
+\enddisplay
+Everybody who teaches proper ^{mathematical} ^{style} is agreed that formulas
+ought to be separated by words, not just by commas; the author of that
+sentence should at least have said `for $n\ge2$', not simply `$n\ge2$'.
+But alas, such lapses are commonplace, and many prominent mathematicians
+are hopelessly addicted to clusters of formulas. If we are not allowed to change
+their writing style, we can at least insert extra space where they
+neglected to insert an appropriate word. An additional interword space
+generally works well in such cases; for example, the sentence above was
+typeset thus:
+\begintt
+... $F_n=F_{n-1}+F_{n-2}$, \ $n\ge2$.}$$
+\endtt
+The `|\|\]' ^^{control space} here gives a visual separation that
+partly compensates for the bad style.
+
+\exercise Put the following paragraph into \TeX\ form, treating punctuation
+and spacing carefully; also insert ^{ties} to prevent bad line breaks.
+\begindisplay\baselineskip13pt
+\vbox{\raggedright\hsize=310pt\parindent=0pt
+Let $H$~be a Hilbert space, \
+$C$~a closed bounded convex subset of~$H$, \
+$T$~a nonexpansive self map of~$C$.
+Suppose that as $n\to\infty$, \ $a_{n,k}\to0$ for each~$k$,
+and $\gamma_n=\sum_{k=0}^\infty(a_{n,k+1}-a_{n,k})^+\to0$.
+Then for each $x$~in~$C$, \
+$A_nx=\sum_{k=0}^\infty a_{n,k}T^kx$ converges weakly
+to a fixed point of~$T$.
+} % taken from Bull. AMS 82 (1976), p 959; chosen by AMS in '78 for demo
+\enddisplay
+\answer |Let $H$~be a Hilbert space, \
+$C$~a closed bounded convex subset of~$H$, \
+$T$~a nonexpansive self map of~$C$.
+Suppose that as $n\to\infty$, \ $a_{n,k}\to0$ for each~$k$,
+and $\gamma_n=\sum_{k=0}^\infty(a_{n,k+1}-|\allowbreak|a_{n,k})^+\to0$.
+ Then for each $x$~in~$C$, \
+$A_nx=\sum_{k=0}^\infty a_{n,k}T^kx$ converges weakly
+to a fixed point of~$T$.|\par
+[If any mathematicians are reading this, they might either appreciate
+or resent the following attempt to edit the given paragraph
+into a more acceptable style: ``%
+Let $C$~be a closed, bounded, convex subset of a Hilbert space~$H$,
+and let $T$~be a nonexpansive self map of~$C$.
+Suppose that as $n\to\infty$, we have $a_{n,k}\to0$ for each~$k$,
+and $\gamma_n=\sum_{k=0}^\infty(a_{n,k+1}-a_{n,k})^+\to0$.
+Then for each $x$~in~$C$, the infinite sum
+$A_nx=\sum_{k=0}^\infty a_{n,k}T^kx$ converges weakly
+to a fixed point of~$T$.'']
+
+\subsection Spacing within formulas. Chapter 16 says that \TeX\ does
+automatic ^{spacing} of math formulas so that they look right, and this is
+almost true. But occasionally you must give \TeX\ some help. The number of
+possible math formulas is vast, and \TeX's spacing rules are rather
+simple, so it is natural that exceptions should arise. Of course, it is
+desirable to have fine units of spacing for this purpose, instead of the
+big chunks that arise from |\|\], |\quad| and |\qquad|.
+
+The basic elements of space that \TeX\
+puts into formulas are called {\sl ^{thin spaces}}, {\sl ^{medium
+spaces}}, and {\sl ^{thick spaces}}. In order to get a feeling for these
+units, let's take a look at the $F_n$ example again: Thick spaces occur
+just before and after the = sign, and also before and after the $\ge$\thinspace;
+medium spaces occur just before and after the $+$ sign. Thin spaces are
+slightly smaller, but noticeable; it's a thin space that makes the
+difference between `loglog' and `$\log\log$'. The normal space between
+words of a paragraph is approximately equal to two thin spaces.
+
+\TeX\ inserts thin spaces, medium spaces, and thick spaces into formulas
+automatically, but you can add your own spacing whenever you want to,
+by using the control sequences ^^|\,|^^|\!|^^|\;|^^|\>|
+$$\halign{\indent#\hfil&\quad#\hfil\cr
+|\,|&thin space \ (normally 1/6 of a quad);\cr
+|\>|&medium space \ (normally 2/9 of a quad);\cr
+|\;|&thick space \ (normally 5/18 of a quad);\cr
+|\!|&negative thin space \ (normally $-1/6$ of a quad).\cr}$$
+In most cases you can rely on \TeX's spacing while you are typing a manuscript,
+and you'll want to insert or delete space with these four control sequences
+only in rare circumstances after you see what comes out.
+
+\ddanger We observed a minute ago that |\quad| spacing does not
+change with the style of formula, nor does it depend on the math font
+families that are being used. But thin spaces, medium spaces, and thick
+spaces do get bigger and smaller as the size of type gets bigger and
+smaller; this is because they are defined in terms of ^\<muglue>, a~special
+brand of glue intended for math spacing. You specify \<muglue> just
+as if it were ordinary glue, except that the units are given in terms of
+`^|mu|' (math units) instead of~|pt| or~|cm| or something else. For
+example, Appendix~B contains the definitions
+\begintt
+\thinmuskip = 3mu
+\medmuskip = 4mu plus 2mu minus 4mu
+\thickmuskip = 5mu plus 5mu
+\endtt
+^^|\thinmuskip|^^|\medmuskip|^^|\thickmuskip|
+and this defines the thin, medium, and thick spaces that \TeX\ inserts
+into formulas. According to these specifications, thin spaces in plain
+\TeX\ do not stretch or shrink; medium spaces can stretch a little, and
+they can shrink to zero; thick spaces can stretch a lot, but they never shrink.
+
+\ddanger There are 18 mu to an em, where the em is taken from family~2
+(the math symbols family). In other words, ^|\textfont|~|2| defines the em
+value for |mu| in display and text styles; ^|\scriptfont|~|2| defines the
+em for script size material; and ^|\scriptscriptfont|~|2| defines it for
+scriptscript size.
+
+\ddanger You can insert math glue into any formula just by giving
+the command `^|\mskip|\<muglue>'. For example, `|\mskip 9mu plus 2mu|'
+inserts one half em of space, in the current size, together with some
+stretchability. Appendix~B defines `|\,|' to be an abbreviation for
+`|\mskip\thinmuskip|'. Similarly, you can use the command `^|\mkern|'
+when there is no stretching or shrinking; `|\mkern18mu|' gives one em of
+horizontal space in the current size. \TeX\ insists that |\mskip| and
+|\mkern| be used only with |mu|; conversely, ^|\hskip| and ^|\kern| (which
+are also allowed in formulas) must never give units in |mu|.
+
+Formulas involving ^{calculus} look best when an extra thin space appears
+before $dx$ ^^{dx} or~$dy$ or~$d\,$whatever; but \TeX\ doesn't do this
+automatically. Therefore a well-trained typist will remember to insert
+`|\,|' in examples like the following:
+\beginmathdemo
+\it Input&\it Output\cr
+\noalign{\vskip2pt}
+|$\int_0^\infty f(x)\,dx$|&\int_0^\infty f(x)\,dx\cr
+|$y\,dx-x\,dy$|&y\,dx-x\,dy\cr
+|$dx\,dy=r\,dr\,d\theta$|&dx\,dy=r\,dr\,d\theta\cr
+|$x\,dy/dx$|&x\,dy/dx\cr \endmathdemo Notice that no `|\,|' was desirable
+after the `|/|' in the last example. Similarly, there's no need for
+`|\,|' in cases like
+\begindisplaymathdemo
+|$$\int_1^x{dt\over t}$$|&\int_1^x{dt\over t}\cr
+\endmathdemo
+since the $dt$ appears all by itself in the numerator of a fraction; this
+detaches it visually from the rest of the formula.
+
+\exercise Explain how to handle the display
+$$\int_0^\infty{t-ib\over t^2+b^2}e^{iat}\,dt=e^{ab}E_1(ab),\qquad a,b>0.$$
+\answer |$$\int_0^\infty{t-ib\over t^2+b^2}e^{iat}\,dt=|\parbreak
+ | e^{ab}E_1(ab),\qquad a,b>0.$$|
+
+\danger When physical ^{units} appear in a formula, they should be set in roman
+type and separated from the preceding material by a thin space:
+\beginmathdemo
+|$55\rm\,mi/hr$|&55\rm\,mi/hr\cr
+|$g=9.8\rm\,m/sec^2$|&g=9.8\rm\,m/sec^2\cr
+|$\rm1\,ml=1.000028\,cc$|&\rm1\,ml=1.000028\,cc\cr
+\endmathdemo
+
+\dangerexercise Typeset the following display, assuming that `^|\hbar|'
+generates `$\hbar$':
+$$\hbar=1.0545\times10^{-27}\rm\,erg\,sec.$$
+\answer |$$\hbar=1.0545\times10^{-27}\rm\,erg\,sec.$$|
+
+\danger Thin spaces should also be inserted after ^{exclamation points}
+(which stand for the ``^{factorial}'' operation in a formula), if the next
+character is a letter or a number or an opening delimiter:
+\beginmathdemo
+|$(2n)!/\bigl(n!\,(n+1)!\bigr)$|&(2n)!/\bigl(n!\,(n+1)!\bigr)\cr
+\noalign{\vskip6pt}
+|$${52!\over13!\,13!\,26!}$$|&\displaystyle{52!\over13!\,13!\,26!}\cr
+\endmathdemo
+
+Besides these cases, you will occasionally encounter formulas in which
+the symbols are bunched up too tightly, or where too much white space
+appears, because of certain unlucky combinations of shapes. It's usually
+impossible to anticipate optical glitches like this until you see the first
+proofs of what you have typed; then you get to use your judgment about how
+to add finishing touches that provide extra beauty, clarity, and finesse.
+A tastefully applied `|\,|' or `|\!|'\ will open things up or close things
+together so that the reader won't be distracted from the mathematical
+significance of the formula. ^{Square root} signs and ^{multiple integrals} are
+often candidates for such fine tuning. Here are some examples of situations
+to look out for:
+\beginmathdemo
+|$\sqrt2\,x$|&\sqrt2\,x\cr
+|$\sqrt{\,\log x}$|&\sqrt{\,\log x}\cr
+|$O\bigl(1/\sqrt n\,\bigr)$|&O\bigl(1/\sqrt n\,\bigr)\cr
+|$[\,0,1)$|&[\,0,1)\cr
+|$\log n\,(\log\log n)^2$|&\log n\,(\log\log n)^2\cr
+|$x^2\!/2$|&x^2\!/2\cr
+|$n/\!\log n$|&n/\!\log n\cr
+|$\Gamma_{\!2}+\Delta^{\!2}$|&\Gamma_{\!2}+\Delta^{\!2}\cr
+|$R_i{}^j{}_{\!kl}$|&R_i{}^j{}_{\!kl}\cr
+|$\int_0^x\!\int_0^y dF(u,v)$|&\int_0^x\!\int_0^y dF(u,v)\cr
+\noalign{\vskip6pt}
+|$$\int\!\!\!\int_D dx\,dy$$|&\displaystyle{\int\!\!\!\int_D dx\,dy}\cr
+\endmathdemo
+^^|\Gamma|^^|\Delta|^^|\intint|
+In each of these formulas the omission of\/ |\,| or |\!|\ would lead to
+somewhat less satisfactory results.
+
+\ddanger Most of these examples where thin-space corrections are desirable
+arise because of chance coincidences. For example, the superscript in
+|$x^2/2$| leaves a hole before the slash ($x^2/2$); a negative thin
+space helps to fill that hole. The positive thin space in |$\sqrt{\,\log x}$|
+compensates for the fact that `$\log x$' begins with a tall, unslanted
+letter; and so on. But two of the examples involve corrections that
+were necessary because \TeX\ doesn't really know a great deal about
+mathematics: \ (1)~In the formula
+|$\log n(\log\log n)^2$|, \TeX\ inserts no thin space before the left
+parenthesis, because there are similar formulas like |$\log n(x)$| where
+no such space is desired. \ (2)~In the formula |$n/\log n$|, \TeX\
+automatically inserts an unwanted thin~space before |\log|, since the slash is
+treated as an ordinary symbol, and since a~thin space is usually desirable
+between an ordinary symbol and an operator like |\log|.
+
+\ddanger In fact, \TeX's rules for spacing in formulas are fairly simple.
+A formula is converted to a math list as described at the end of Chapter~17,
+and the math list consists chiefly of ``^{atoms}'' of eight basic types:
+^{Ord}~(^{ordinary}), ^{Op}~(^{large operator}), ^{Bin}~(^{binary operation}),
+^{Rel}~(^{relation}), ^{Open}~(^{opening}), ^{Close}~(^{closing}),
+^{Punct}~(punctuation), ^^{punctuation} and ^{Inner}~(a delimited
+subformula). Other kinds of atoms, which arise from commands like
+^|\overline| or ^|\mathaccent| or ^|\vcenter|, etc., are all treated as
+type~Ord; ^{fractions} are treated as type~Inner. The following table is
+used to determine the spacing between pairs of adjacent atoms:
+$$\baselineskip0pt\lineskip0pt
+\halign to\hsize
+ {\strut\hbox to\parindent{\it#\hfil}& % for the legend "Left atom"
+ #\hfil\quad& % for the row labels
+ #\hfil\tabskip 0pt plus 10pt& % for the rule at the left
+ \hbox to 25pt{\tt\hss#\hss}& % for column 1
+ \hbox to 25pt{\tt\hss#\hss}& % for column 2
+ \hbox to 25pt{\tt\hss#\hss}& % for column 3
+ \hbox to 25pt{\tt\hss#\hss}& % for column 4
+ \hbox to 25pt{\tt\hss#\hss}& % for column 5
+ \hbox to 25pt{\tt\hss#\hss}& % for column 6
+ \hbox to 25pt{\tt\hss#\hss}& % for column 7
+ \hbox to 25pt{\tt\hss#\hss}& % for column 8
+ #\hfil\tabskip0pt\cr % for the rule at the right
+\noalign{\vskip-6pt} % it just happens that there's extra white space
+&&&&\multispan7\hss\it Right atom\hss\cr
+\noalign{\vskip3pt}
+&&&\rm Ord&\rm Op&\rm Bin&\rm Rel&\rm Open&\rm Close&\rm Punct&\rm Inner\cr
+\noalign{\vskip2pt}
+\omit&&\multispan{10}\leaders\hrule\hfil\cr
+\omit\vbox to 2pt{}&&\vrule&&&&&&&&&\vrule\cr
+&Ord&\vrule&0&1&(2)&(3)&0&0&0&(1)&\vrule\cr
+&Op&\vrule&1&1&*&(3)&0&0&0&(1)&\vrule\cr
+&Bin&\vrule&(2)&(2)&*&*&(2)&*&*&(2)&\vrule\cr
+Left&Rel&\vrule&(3)&(3)&*&0&(3)&0&0&(3)&\vrule\cr
+atom&Open&\vrule&0&0&*&0&0&0&0&0&\vrule\cr
+&Close&\vrule&0&1&(2)&(3)&0&0&0&(1)&\vrule\cr
+&Punct&\vrule&(1)&(1)&*&(1)&(1)&(1)&(1)&(1)&\vrule\cr
+&Inner&\vrule&(1)&1&(2)&(3)&(1)&0&(1)&(1)&\vrule\cr
+\omit\vbox to 2pt{}&&\vrule&&&&&&&&&\vrule\cr
+\omit&&\multispan{10}\leaders\hrule\hfil\cr}$$
+^^{spacing table} ^^{math spacing table}
+Here 0, 1, 2, and 3 stand for no space, thin space, medium space, and
+thick space, respectively; the table entry is parenthesized if the space
+is to be inserted only in display and text styles, not in script and
+scriptscript styles. For example, many of the entries in the Rel row
+and the Rel column are `{\tt(3)}'; this means that thick spaces are normally
+inserted before and after relational symbols like `=', but not in
+subscripts. Some of the entries in the table are `{\tt*}'; such cases
+never arise, because Bin atoms must be preceded and followed by atoms
+compatible with the nature of binary operations. Appendix~G contains
+precise details about how math lists are converted to horizontal lists;
+this conversion is done whenever \TeX\ is about to leave math mode, and the
+inter-atomic spacing is inserted at that time.
+
+\ddanger For example, the displayed formula specification
+\begintt
+$$x+y=\max\{x,y\}+\min\{x,y\}$$
+\endtt
+will be transformed into the sequence of atoms
+\def\\#1{\vbox to 33pt{\vbox to 22pt{\vfill\hrule
+ \hbox{\vrule\hskip-.4pt$#1$\hskip-.4pt\vrule}}\hrule\vfill}}%
+\begindisplay
+\vbox{\vskip-11pt\hbox{$
+\\x\;\;\\+\;\;\\y\;\;\\=\;\;\\\max\;\;\\\{\;\;
+\\x\;\;\\,\;\;\\y\;\;\\\}\;\;\\+\;\;\\\min\;\;\\\{
+\;\;\\x\;\;\\,\;\;\\y\;\;\\\}$}\vskip-11pt}
+\enddisplay
+of respective types Ord, Bin, Ord, Rel, Op, Open, Ord, Punct, Ord, Close,
+Bin, Op, Open, Ord, Punct, Ord, and Close.
+Inserting spaces according to the table gives
+$$\def\0{\thinspace}
+\def\1{\thinspace{\tt\bslash,}\thinspace}
+\def\2{\thinspace{\tt\bslash>}\thinspace}
+\def\3{\thinspace{\tt\bslash;}\thinspace}
+\halign{\indent\hfil#\cr
+ Ord\2Bin\2Ord\3Rel\3Op\0Open\0Ord\0Punct\1Ord\0Close\2\qquad\cr
+ Bin\2Op\0Open\0Ord\0Punct\1Ord\0Close\cr}$$
+and the resulting formula is
+$$\vbox{\vskip-11pt\hbox{$
+\\x\>\\+\>\\y\;\\=\;\\\max\\\{
+\\x\\,\,\\y\\\}\>\\+\>\\\min\\\{
+\\x\\,\,\\y\\\}$}\vskip-11pt}$$
+i.e.,$$x+y=\max\{x,y\}+\min\{x,y\}\rlap{\quad.}$$
+This example doesn't involve subscripts or superscripts; but subscripts and
+superscripts merely get attached to atoms without changing the atomic type.
+
+\ddangerexercise Use the table to determine what spacing \TeX\
+will insert between the atoms of the formula `|$f(x,y)<x^2+y^2$|'.
+\answer There are ten atoms (the first is $f$ and last is $y^2$); their types,
+and the interatomic spacing, are respectively
+\begindisplay \def\0{\thinspace}%
+ \def\1{\thinspace{\tt\bslash,}\thinspace}%
+ \def\2{\thinspace{\tt\bslash>}\thinspace}%
+ \def\3{\thinspace{\tt\bslash;}\thinspace}
+Ord\0Open\0Ord\0Punct\1Ord\0Close\3Rel\3Ord\2Bin\2Ord.
+\enddisplay
+
+\ddanger The plain \TeX\ macros ^|\bigl|, ^|\bigr|, ^|\bigm|, and ^|\big|
+all produce identical delimiters; the only difference between them is that
+they may lead to different spacing, because they make the delimiter into
+different types of atoms: |\bigl| produces an Open atom, |\bigr| a~Close,
+|\bigm| a~Rel, and |\big| an~Ord. On the other hand, when a subformula
+appears between ^|\left| and ^|\right|, it is typeset by itself and placed
+into an Inner atom. Therefore it is possible that a subformula enclosed by
+|\left| and |\right| will be surrounded by more space than there would be
+if that subformula were enclosed by |\bigl| and~|\bigr|. For example, Ord
+followed by Inner (from |\left|) gets a thin space, but Ord followed by
+Open (from |\bigl|) does not. The rules in Chapter~17 imply that the
+construction `^|\mathinner||{\bigl({|\<subformula>|}\bigr)}|' within any
+formula produces a result exactly equivalent to
+`|\left(|\<subformula>|\right)|', when the \<subformula> doesn't end
+with Punct, except that the ^{delimiters} are forced to
+be of the |\big| size regardless of the height and depth of the subformula.
+
+\danger \TeX's spacing rules sometimes fail when `\|' and `|\|\|'
+appear in a formula, because $\vert$ and $\Vert$ are treated as ordinary symbols
+^^{verticalline}^^|\verticalline|
+instead of as delimiters. For example, consider the formulas
+\beginlongmathdemo
+|$|\||-x|\||=|\||+x|\||$|&\vert-x\vert=\vert+x\vert\cr
+|$\left|\||-x\right|\||=\left|\||+x\right|\||$|&
+ \left\vert-x\right\vert=\left\vert+x\right\vert\cr
+|$\lfloor-x\rfloor=-\lceil+x\rceil$|&\lfloor-x\rfloor=-\lceil+x\rceil\cr
+\endmathdemo
+In the first case the spacing is wrong because \TeX\ thinks that the plus
+sign is computing the sum of `$\vert$' and `$x$'. The use of\/ |\left| and
+|\right| in the second example puts \TeX\ on the right track. The third
+example shows that no such corrections are needed with other delimiters,
+because \TeX\ knows whether they are openings or closings.
+
+\ddangerexercise Some perverse mathematicians use ^{brackets} backwards,
+to denote ``^{open intervals}.''
+Explain how to type the following bizarre formula: % from MR53 #3451
+$\left]-\infty,T\right[\times\left]-\infty,T\right[$.
+\answer |$\left]-\infty,T\right[\times\left]-\infty,T\right[$|. \ (Or one could
+say ^|\mathopen| and ^|\mathclose| instead of\/ |\left| and |\right|;
+then \TeX\ would not choose the size of the delimiters, nor would it consider
+the subformulas to be of type Inner.) \
+% that formula was quoted from MR review of paper by Mario Marino
+% in Ricerche Mat. 24 (1975), no.~1, 152--171
+Open intervals are more clearly expressed in print
+by using parentheses instead of reversed brackets; for example,
+compare `$(-\infty,T)\times(-\infty,T)$' to the given formula.
+
+\ddangerexercise Study Appendix G and determine what spacing will be used
+in the formula `|$x++1$|'. Which of the plus signs will be regarded
+as a ^{binary operation}?
+\answer The first |+| will become a Bin atom, the second an Ord; hence
+the result is $x$, medium space, $+$, medium space, $+$, no space, 1.
+
+\subsection Ellipses\/ {\rm(``three dots'')}. ^^{ellipses}
+Mathematical copy looks much nicer if you are careful about how groups of
+^{three dots} are typed in formulas and text. Although it looks fine to
+type `|...|'\ on a typewriter that has fixed spacing, the result looks too
+crowded when you're using a printer's fonts: `|$x...y$|' results in
+`$x...y$', and such close spacing is undesirable except in subscripts or
+superscripts.
+
+An ellipsis can be indicated by two different kinds of dots, one higher
+than the other; the best mathematical traditions distinguish between
+these two possibilities. It is generally correct to produce formulas like
+\begindisplay
+$\displaystyle x_1+\cdots+x_n\qquad {\rm and}\qquad (x_1,\ldots,x_n),$
+\enddisplay
+but wrong to produce formulas like
+\begindisplay
+$\displaystyle x_1+\ldots+x_n\qquad {\rm and}\qquad (x_1,\cdots,x_n).$
+\enddisplay
+The plain \TeX\ format of Appendix B allows you to solve the ``three dots''
+problem very simply, and everyone will be envious of the beautiful formulas
+that you produce. The idea is simply to type ^|\ldots| when you want three
+low dots~($\,\ldots\,$), and ^|\cdots| when you want three vertically centered
+dots~($\,\cdots\,$).
+
+In general, it is best to use |\cdots| between $+$ and $-$ and~$\times$
+signs, and also between $=$~signs or $\le$~signs or $\subset$ signs or other
+similar relations. Low dots are used between ^{commas}, and when things
+are juxtaposed with no signs between them at all. For example:
+\beginmathdemo
+|$x_1+\cdots+x_n$|&x_1+\cdots+x_n\cr
+|$x_1=\cdots=x_n=0$|&x_1=\cdots=x_n=0\cr
+|$A_1\times\cdots\times A_n$|&A_1\times\cdots\times A_n\cr
+|$f(x_1,\ldots,x_n)$|&f(x_1,\ldots,x_n)\cr
+|$x_1x_2\ldots x_n$|&x_1x_2\ldots x_n\cr
+|$(1-x)(1-x^2)\ldots(1-x^n)$|&(1-x)(1-x^2)\ldots(1-x^n)\cr
+|$n(n-1)\ldots(1)$|&n(n-1)\ldots(1)\cr
+\endmathdemo
+
+\exercise Type the formulas `$x_1+x_1x_2+\cdots+x_1x_2\ldots x_n$' and
+`$(x_1,\ldots,x_n)\cdot(y_1,\ldots,y_n)=x_1y_1+\cdots+x_ny_n$'.
+\ [{\sl Hint:\/} A single raised dot is called `^|\cdot|'.]
+\answer |$x_1+x_1x_2+\cdots+x_1x_2\ldots x_n$| \ and\hfil\break
+|$(x_1,\ldots,x_n)\cdot(y_1,\ldots,y_n)=x_1y_1+\cdots+x_ny_n$|.
+
+But there's an important special case in which |\ldots| and |\cdots|
+don't give the correct spacing, namely when they appear at the very end
+of a formula, or when they appear just before a closing delimiter like~`|)|'.
+In such situations an extra ^{thin space} is needed. For example, consider
+sentences like this:
+\begindisplay
+Prove that $(1-x)^{-1}=1+x+x^2+\cdots\,$.\cr
+Clearly $a_i<b_i$ for $i=1$,~2, $\ldots\,$,~$n$.\cr
+The coefficients $c_0$,~$c_1$, \dots,~$c_n$ are positive.\cr
+\enddisplay
+To get the first sentence, the author typed
+\begintt
+Prove that $(1-x)^{-1}=1+x+x^2+\cdots\,$.
+\endtt
+Without the `^|\,|' the period would have come too close to the |\cdots|.
+Similarly, the second sentence was typed thus:
+\begintt
+Clearly $a_i<b_i$ for $i=1$,~2, $\ldots\,$,~$n$.
+\endtt
+Notice the use of ^{ties}, which prevent bad line breaks as explained in
+Chapter~14. Such ellipses are extremely common in some forms of mathematical
+writing, so plain \TeX\ allows you to say just `^|\dots|' as an abbreviation
+for `|$\ldots\,$|' in the text of a paragraph. The third sentence
+can therefore be typed
+\begintt
+The coefficients $c_0$,~$c_1$, \dots,~$c_n$ are positive.
+\endtt
+
+\exercise B. C. ^{Dull} tried to take a shortcut by typing the
+second example this way:
+\begintt
+Clearly $a_i<b_i$ for~$i=1, 2, \ldots, n$.
+\endtt
+What's so bad about that?
+\answer The commas belong to the sentence, not to the formula; his
+decision to put them into math mode meant that \TeX\ didn't put large
+enough spaces after them. Also, his formula `$i=1, 2, \ldots, n$' allows
+no breaks between lines, except after the $=$, so he's risking
+overfull box problems. But suppose the sentence had been more terse:
+\begindisplay
+Clearly $a_i<b_i$ \ ($i=1,2,\ldots,n$).
+\enddisplay
+Then his idea would be basically correct:
+\begintt
+Clearly $a_i<b_i$ \ ($i=1,2,\ldots,n$).
+\endtt
+
+\exercise How do you think the author typed the ^{footnote} in Chapter 4
+of this book?
+\answer $\ldots$ |never\footnote*{Well \dots, hardly ever.} have| $\ldots$
+
+\subsection Line breaking. When you have formulas in a paragraph, \TeX\
+may have to break them between lines. This is a necessary evil, something
+like the hyphenation of words; we want to avoid it unless the alternative
+is worse. ^^{line breaking in math} ^^{breaking formulas between lines}
+
+A formula will be broken only after a relation symbol like $=$
+or~$<$ or $\to$, or after a binary operation symbol like $+$ or $-$ or
+$\times$, where the relation or binary operation is on the ``outer level'' of
+the formula (i.e., not enclosed in |{...}| and not part of an `|\over|'
+construction). For example, if you type
+\begintt
+$f(x,y) = x^2-y^2 = (x+y)(x-y)$
+\endtt
+in mid-paragraph, there's a chance that \TeX\ will break after either of the
+|=|~signs (it prefers this) or after the~|-| or~|+| or~|-| (in an emergency).
+But there won't be a break after the comma in any case---commas
+after which breaks are desirable shouldn't appear between |$|'s.
+
+If you don't want to permit breaking in this example except after the
+|=|~signs, you could type
+\begintt
+$f(x,y) = {x^2-y^2} = {(x+y)(x-y)}$
+\endtt
+because these additional braces ``freeze'' the ^{subformulas}, putting them
+into unbreakable boxes in which the glue has been set to its natural width.
+But it isn't necessary to bother worrying about such things unless \TeX\
+actually does break a formula badly, since the chances of this are
+pretty slim.
+
+\danger A ``^{discretionary multiplication sign}'' is allowed in formulas:
+If you type `|$(x+y)\*(x-y)$|', \TeX\ will treat the ^|\*| something like
+the way it treats \hbox{|\-|}; namely, a line break will be allowed at
+that place, with the hyphenation penalty. However, instead of inserting a
+hyphen, \TeX\ will insert a $\times$ sign in text size.
+
+\danger If you do want to permit a break at some point in the outer level
+of a formula, you can say ^|\allowbreak|. For example, if the formula
+\begintt
+$(x_1,\ldots,x_m,\allowbreak y_1,\ldots,y_n)$
+\endtt
+appears in the text of a paragraph, \TeX\ will allow it to be broken into the
+two pieces `$(x_1,\ldots,x_m,$' and `$y_1,\ldots,y_n)$'.
+
+\ddanger The penalty for breaking after a Rel atom is called ^|\relpenalty|,
+and the penalty for breaking after a Bin atom is called ^|\binoppenalty|.
+Plain \TeX\ sets |\relpenalty=500| and |\binoppenalty=700|. You can change
+the penalty for breaking in any particular case by typing `^|\penalty|\<number>'
+immediately after the atom in question; then the number you have specified
+will be used instead of the ordinary penalty. For example, you can prohibit
+breaking in the formula `$x=0$' by typing `|$x=\nobreak0$|', since
+^|\nobreak| is an abbreviation for `|\penalty10000 |'.
+
+\ddangerexercise Is there any difference between the results of
+`|$x=\nobreak0$|' and `|${x=0}$|'?
+\answer Neither formula will be broken between lines, but the thick spaces
+in the second formula will be set to their natural width while the thick
+spaces in the first formula will retain their stretchability.
+
+\ddangerexercise How could you prohibit all breaks in formulas, by making only
+a few changes to the macros of plain \TeX?
+\answer Set ^|\relpenalty||=10000| and ^|\binoppenalty||=10000|.
+And you also need to change the definitions of\/ ^|\bmod| and ^|\pmod|,
+which insert their own penalties.
+
+\subsection Braces. A variety of different notations have sprung up involving
+the symbols `$\{$' and `$\}$'; plain \TeX\ includes several control
+sequences that help you cope with formulas involving such things.
+^^{braces} ^^{leftbrace} ^^{rightbrace}
+
+In simple situations, braces are used to indicate a ^{set} of objects;
+for example, `$\{a,b,c\}$' stands for the set of three objects $a$, $b$,
+and~$c$. There's nothing special about typesetting such formulas, except
+that you must remember to use |\{| and |\}| for the braces:
+^^|\leftbrace| ^^|\rightbrace|
+\beginmathdemo
+|$\{a,b,c\}$|&\{a,b,c\}\cr
+|$\{1,2,\ldots,n\}$|&\{1,2,\ldots,n\}\cr
+|$\{\rm red,white,blue\}$|&\{\rm red,white,blue\}\cr
+\endmathdemo
+A slightly more complex case arises when a set is indicated by giving a
+generic element followed by a specific condition; for example, `$\{\,x\mid
+x>5\,\}$' stands for the set of all objects $x$ that are greater than~5.
+In such situations the control sequence ^|\mid| should be used for the
+^{vertical bar}, and thin spaces should be inserted inside the braces:
+\beginmathdemo
+|$\{\,x\mid x>5\,\}$|&\{\,x\mid x>5\,\}\cr
+|$\{\,x:x>5\,\}$|&\{\,x:x>5\,\}\cr
+\endmathdemo
+(Some authors prefer to use a ^{colon} instead of `$\mid$', as in the second
+example here.) \ When the delimiters get larger, as in
+\begindisplay
+$\displaystyle\bigl\{\,\bigl(x,f(x)\bigr)\bigm\vert x\in D\,\bigr\}$
+\enddisplay
+they should be called ^|\bigl|, ^|\bigm|, and~^|\bigr|; for example,
+the formula just given would be typed
+\begintt
+\bigl\{\,\bigl(x,f(x)\bigr)\bigm|char`||x\in D\,\bigr\}
+\endtt
+and formulas that involve still larger delimiters would use ^|\Big| or
+^|\bigg| or~even ^|\Bigg|, as explained in Chapter~17.
+
+\exercise How would you typeset the formula
+$\bigl\{\,x^3\bigm\vert h(x)\in\{-1,0,+1\}\,\bigr\}$?
+\answer |$\bigl\{\,x^3\bigm|\||h(x)\in\{-1,0,+1\}\,\bigr\}$|.
+
+\dangerexercise Sometimes the condition that defines a set is given as
+a fairly long English description, not as a formula; for example, consider
+`$\{\,p\mid p$~and $p+2$ are prime$\,\}$'. An hbox would do the job:
+\begintt
+$\{\,p\mid\hbox{$p$ and $p+2$ are prime}\,\}$
+\endtt
+but a long formula like this is troublesome in a paragraph, since an hbox
+cannot be broken between lines, and since the glue inside the |\hbox| does
+not vary with the interword glue in the line that contains it. Explain how
+the given formula could be typeset with line breaks allowed. [{\sl Hint:\/}
+Go back and forth between math ^{mode} and horizontal mode.]
+\answer |$\{\,p\mid p$~and $p+2$ are prime$\,\}$|, assuming that
+^|\mathsurround| is zero. The more difficult alternative
+`|$\{\,p\mid p\ {\rm and}\ p+2\rm\ are\ prime\,\}$|' is not a solution,
+because line breaks do not occur at |\|\] ^^|\space|
+(or at glue of any kind) within math formulas. Of course it may be best to
+display a formula like this, instead of breaking it between lines.
+
+Displayed formulas often involve another sort of brace, to indicate a choice
+between various alternatives, as in the construction
+\begindisplay
+$\displaystyle\vert x\vert=\cases{x,&if $x\ge0$;\cr -x,&otherwise.\cr}$
+\enddisplay
+^^{selection, see cases} ^^{alternatives, see cases} ^^{choices, see cases}
+You can typeset it with the control sequence ^|\cases|:
+\begintt
+$$|char`||x||=\cases{x,&if $x\ge0$;\cr
+ -x,&otherwise.\cr}$$
+\endtt
+Look closely at this example and notice that it uses the character |&|,
+^^{ampersand} which we said in Chapter~7 was reserved for special purposes.
+Here for the first time in this manual we have an example of why |&|~is
+so special: Each of the cases has two parts, and the~|&| separates those
+parts. To the left of the~|&| is a math formula that is implicitly
+enclosed in |$...$|; to the right of the~|&| is ordinary text, which is
+{\sl not\/} implicitly enclosed in |$...$|. For example, the `|-x,|' in
+the second line will be typeset in math mode, but the `|otherwise|' will
+be typeset in horizontal mode. Blank spaces after the~|&| are ignored.
+There can be any number of cases, but there usually are at least two.
+Each case should be followed by ^|\cr|. Notice that the |\cases| construction
+typesets its own `$\{$'; there is no corresponding `$\}$'.
+
+\exercise Typeset the display \lower12pt\null\
+$\smash{\displaystyle
+f(x)=\cases{1/3&if $0\le x\le1$;\cr 2/3&if $3\le x\le4$;\cr 0&elsewhere.\cr}
+}$
+\answer |$$f(x)=\cases{1/3&if $0\le x\le1$;\cr 2/3&if $3\le x\le4$;\cr|\hfil
+\break|0&elsewhere.\cr}$$|
+
+\danger You can insert `^|\noalign||{|$\langle$vertical mode
+material$\rangle$|}|' just after any \kern-1pt|\cr| within |\cases|, as
+explained in Chapter~22, because |\cases| is an application of the general
+alignment constructions considered in that chapter. For example, the
+command `|\noalign{\vskip2pt}|' can be used to put a little extra space
+between two of the cases.
+
+\danger ^{Horizontal braces} will be set over or under parts of a displayed
+formula if you use the control sequences ^|\overbrace| or ^|\underbrace|.
+Such constructions are considered to be large operators like |\sum|, so you
+can put limits above them or below them by specifying superscripts or
+subscripts, as in the following examples:
+\beginlongdisplaymathdemo
+\noalign{\vskip9pt}
+|$$\overbrace{x+\cdots+x}^{k\rm\;times}$$|&
+ \overbrace{x+\cdots+x}^{k\rm\;times}\cr
+\noalign{\vskip-6pt}
+|$$\underbrace{x+y+z}_{>\,0}.$$|&
+ \underbrace{x+y+z}_{>\,0}.\cr
+\endmathdemo
+
+\subsection Matrices. Now comes the fun part. Mathematicians in many different
+disciplines like to construct rectangular arrays of formulas that have been
+arranged in rows and columns; such an ^{array} is called a {\sl^{matrix}}.
+Plain \TeX\ provides a ^|\matrix| control sequence that makes it convenient
+to deal with the most common types of matrices.
+
+For example, suppose that you want to specify the display
+$$A=\left(\matrix{x-\lambda&1&0\cr
+ 0&x-\lambda&1\cr
+ 0&0&x-\lambda\cr}\right).$$
+All you do is type
+\begintt
+$$A=\left(\matrix{x-\lambda&1&0\cr
+ 0&x-\lambda&1\cr
+ 0&0&x-\lambda\cr}\right).$$
+\endtt
+^^|\lambda|
+This is very much like the |\cases| construction we looked at earlier;
+each row of the matrix is followed by~|\cr|, and `|&|'~signs are used
+between the individual entries of each row. Notice, however, that you are
+supposed to put your own |\left| and |\right| delimiters around the matrix;
+this makes |\matrix| different from |\cases|, which inserts a big `$\{$'
+automatically. The reason is that |\cases| always involves a left brace,
+but different delimiters are used in different matrix constructions. On
+the other hand, parentheses are used more often than other delimiters, so
+you can write ^|\pmatrix| if you want plain \TeX\ to fill in the
+parentheses for you; the example above then reduces to
+\begintt
+$$A=\pmatrix{x-\lambda&...&x-\lambda\cr}.$$
+\endtt
+
+\dangerexercise Typeset the display \ \lower12pt\null
+$\tenpoint\smash{\displaystyle
+\left\lgroup\matrix{a&b&c\cr d&e&f\cr}\right\rgroup
+ \left\lgroup\matrix{u&x\cr v&y\cr w&z\cr}\right\rgroup
+}$, \
+using ^|\lgroup| and ^|\rgroup|.
+\answer |$$\left\lgroup\matrix{a&b&c\cr d&e&f\cr}\right\rgroup|\hfil\break
+|\left\lgroup\matrix{u&x\cr v&y\cr w&z\cr}\right\rgroup$$|.
+
+\danger The individual entries of a matrix are normally centered in columns.
+Each column is made as wide as necessary to accommodate the entries it
+contains, and there's a ^{quad} of space between columns.
+If you want to put something ^{flush right} in its column, precede it
+by ^|\hfill|; if you want to put something ^{flush left} in its column,
+follow it by~|\hfill|.
+
+\danger Each entry of a matrix is treated separately from the others,
+and it is typeset as a math formula in text style. Thus, for example,
+if you say |\rm| in one entry, it does not affect the others.
+Don't try to say `|{\rm x&y}|'.
+
+Matrices often appear in the form of generic patterns that use ^{ellipses}
+(i.e., dots) to indicate rows or columns that are left out. You can typeset
+such matrices by putting the ellipses into rows and/or columns of their own.
+Plain \TeX\ provides ^|\vdots| (vertical dots) and ^|\ddots| (diagonal dots)
+as companions to ^|\ldots| for constructions like this. For example, the
+^{generic matrix}
+$$A=\pmatrix{a_{11}&a_{12}&\ldots&a_{1n}\cr
+ a_{21}&a_{22}&\ldots&a_{2n}\cr
+ \vdots&\vdots&\ddots&\vdots\cr
+ a_{m1}&a_{m2}&\ldots&a_{mn}\cr}$$
+is easily specified:
+\begintt
+$$A=\pmatrix{a_{11}&a_{12}&\ldots&a_{1n}\cr
+ a_{21}&a_{22}&\ldots&a_{2n}\cr
+ \vdots&\vdots&\ddots&\vdots\cr
+ a_{m1}&a_{m2}&\ldots&a_{mn}\cr}$$
+\endtt
+
+\medskip
+\exercise How can you get \TeX\ to produce the ^{column vector} ^^{vector}
+\lower18pt\null\ $\smash{\displaystyle
+\pmatrix{y_1\cr \vdots\cr y_k\cr}
+}$\quad?
+\answer |\pmatrix{y_1\cr \vdots\cr y_k\cr}|.
+
+\danger Sometimes a matrix is bordered at the top and left by formulas
+that give labels to the rows and columns. Plain \TeX\ provides a special
+macro called ^|\bordermatrix| for this situation. For example, the display
+$$\tenmath
+M=\bordermatrix{&C&I&C'\cr C&1&0&0\cr I&b&1-b&0\cr C'&0&a&1-a\cr}$$
+is obtained when you type
+\begintt
+$$M=\bordermatrix{&C&I&C'\cr
+ C&1&0&0\cr I&b&1-b&0\cr C'&0&a&1-a\cr}$$
+\endtt
+The first row gives the upper labels, which appear above the big left
+and right parentheses; the first column gives the left labels, which are
+typeset flush left, just before the matrix itself. The first column in
+the first row is normally blank. Notice that |\bordermatrix| inserts
+its own parentheses, like |\pmatrix| does.
+
+\danger It's usually inadvisable to put matrices into the text of a paragraph,
+because they are so big that they are better displayed. But occasionally
+you may want to specify a small matrix like $1\,1\choose0\,1$, which you can
+^^|\choose| ^^{matrix, small}
+typeset for example as `|$1\,1\choose0\,1$|'. Similarly, the small matrix
+$\bigl({a\atop l}{b\atop m}{c\atop n}\bigr)$ can be typeset as
+\begintt
+$\bigl({a\atop l}{b\atop m}{c\atop n}\bigr)$
+\endtt
+^^|\atop| The |\matrix| macro does not produce small arrays of this sort.
+
+\subsection Vertical spacing. If you want to tidy up an unusual formula,
+you know already how to move things farther apart or closer together, by
+using positive or negative thin spaces. But such spaces affect only the
+horizontal dimension; what if you want something to be moved higher
+or lower? That's an advanced topic.
+
+\danger Appendix B provides a few macros that can be used to fool \TeX\
+into thinking that certain formulas are larger or smaller than they really
+are; such tricks can be used to move other parts of the formula up or down
+or left or right. For example, we have already discussed the use of
+^|\mathstrut| in Chapter~16 and ^|\strut| in Chapter~17; these invisible
+boxes caused \TeX\ to put square root signs and the denominators of
+continued fractions into different positions than usual.
+
+\danger If you say `^|\phantom||{|\<subformula>|}|' in any formula, plain
+\TeX\ will do its spacing as if you had said simply
+`|{|\<subformula>|}|', but the subformula itself will be invisible. Thus,
+for example, `|\phantom{0}2|' takes up just as much space as `|02|' in the
+current style, but only the~|2| will actually appear on the page. If you
+want to leave blank space for a ^{new symbol} that has exactly the same
+size as $\sum$, but if you are forced to put that symbol in by hand for
+some reason, `|\mathop{\phantom\sum}|' will leave exactly the right amount
+of blank space. \ (The `^|\mathop|' here makes this phantom behave like
+|\sum|, i.e., as a large operator.)
+
+\danger Even more useful than |\phantom| is ^|\vphantom|, which makes
+an invisible box whose height and depth are the same as those of
+the corresponding |\phantom|, but
+the width is zero. Thus, |\vphantom| makes a vertical ^{strut} that can
+increase a formula's effective height or depth. Plain \TeX\ defines
+|\mathstrut| to be an abbreviation for `|\vphantom(|'. There's also
+^|\hphantom|, which has the width of a |\phantom|, but its height
+and depth are zero.
+
+\danger Plain \TeX\ also provides `^|\smash||{|\<subformula>|}|', a macro
+that yields the same result as `|{|\<subformula>|}|' but makes the height and
+depth zero. By using both |\smash| and |\vphantom| you can typeset any
+subformula and give it any desired nonnegative height and depth. For example,
+\begintt
+\mathop{\smash\limsup\vphantom\liminf}
+\endtt
+produces a large operator that says `$\limsup$', but its height and depth
+are those of\/ ^|\liminf| (i.e., the depth is zero). ^^|\limsup|
+
+\def\undertext#1{$\underline{\hbox{#1}}$}
+\ddangerexercise If you want to underline some text, you could use a macro like
+\begintt
+\def\undertext#1{$\underline{\hbox{#1}}$}
+\endtt
+to do the job. \undertext{But} \undertext{this} \undertext{doesn't}
+\undertext{always} \undertext{work} \undertext{right}. Discuss better
+alternatives. ^^{underlined text}
+\answer |\def|\stretch|\undertext|\stretch|#1{$\underline|\stretch
+|{\smash|\stretch|{\hbox|\stretch|{#1}}}$}| will underline the
+\def\undertext#1{$\underline{\smash{\hbox{#1}}}$}%
+words and cross \undertext{through} the descenders; or you could insert
+|\vphantom{y}| before the |\hbox|, thereby lowering all of the underlines
+to a position below all descenders. Neither of these gives exactly what is
+wanted. \ (See also ^|\underbar| in Appendix~B\null.) \ Underlining is actually
+not very common in fine typography, since font changes usually work just
+as well or better, when you want to emphasize something. If you really want
+underlined text, it's best to have a special font in which all
+the letters are underlined.
+
+\ddanger You can also use ^|\raise| and ^|\lower| to adjust the vertical
+positions of boxes in formulas. For example, the formula
+`|$2^{\raise1pt\hbox{$\scriptstyle n$}}$|' will have its superscript~$n$
+one point higher than usual ($2^{\raise1pt\hbox{$\scriptstyle n$}}$ instead of
+$2^n$). Note that it was necessary to say ^|\scriptstyle| in this example,
+since the contents of an ^|\hbox| will normally be in text style even when
+that hbox appears in a superscript, and since |\raise| can be used only in
+connection with a box. This method of positioning is not used extremely
+often, but it is sometimes helpful if the ^|\root| macro doesn't put its
+argument in a suitable place. For example,
+\begindisplay
+|\root\raise|\<dimen>|\hbox{$\scriptscriptstyle|\<argument>|$}\of...|
+\enddisplay
+will move the argument up by a given amount.
+
+\ddanger Instead of changing the sizes of subformulas, or using |\raise|,
+you can also control vertical spacing by changing the parameters
+that \TeX\ uses when it is converting math lists to horizontal lists.
+These parameters are described in Appendix~G\null; you need to be careful when
+changing them, because such changes are ^{global} (i.e., not local to groups).
+Here is an example of how such a change might be made: Suppose that you
+are designing a format for ^{chemical typesetting}, and that you expect to be
+setting a lot of formulas like `$\rm Fe_2^{+2}Cr_2O_4$'. You may not like the
+fact that the subscript in~$\rm Fe_2^{+2}$ is lower than the subscript
+in~$\rm Cr_2$; and you don't want to force users to type monstrosities like
+\begintt
+$\rm Fe_2^{+2}Cr_2^{\vphantom{+2}}O_4^{\vphantom{+2}}$
+\endtt
+just to get the formula
+$\rm Fe_2^{+2}Cr_2^{\vphantom{+2}}O_4^{\vphantom{+2}}$
+with all subscripts at the same level. Well, all you need to do is set
+`^|\fontdimen||16\tensy=2.7pt|' and `|\fontdimen17\tensy=2.7pt|', assuming
+that ^|\tensy| is your main symbol font (|\textfont2|); this lowers all
+normal ^{subscripts} to a position $2.7\pt$ below the baseline, which is
+enough to make room for a possible superscript that contains a plus sign.
+Similarly, you can adjust the positioning of ^{superscripts} by changing
+|\fontdimen14\tensy|. There are parameters for the position of the ^{axis line},
+the positions of ^{numerator} and ^{denominator} in a generalized ^{fraction},
+the spacing above and below ^{limits}, the default ^{rule thickness}, and so
+on. Appendix~G gives precise details.
+
+\subsection Special features for math hackers. \TeX\ has a few more primitive
+operations for math mode that haven't been mentioned yet. They are
+occasionally useful if you are designing special formats.
+
+\ddanger If a glue or kern specification is immediately preceded by
+`^|\nonscript|', \TeX\ will not use that glue or kern in
+script or scriptscript styles. Thus, for example, the sequence
+`|\nonscript\;|' produces exactly the amount of space specified by
+`{\tt(3)}' in the spacing table for mathematics that appeared earlier
+in this chapter.
+
+\ddanger Whenever \TeX\ has scanned a |$| and is about to read a math formula
+that appears in text, it will first read another list of tokens that has
+been predefined by the command ^|\everymath||={|\<token list>|}|. \ (This is
+analogous to |\everypar|, which was described in Chapter~14.) \ Similarly,
+you can say ^|\everydisplay||={|\<token list>|}| to predefine a list of tokens
+for \TeX\ to read just after it has scanned an opening |$$|, i.e., just
+before reading a formula that is to be displayed. With |\everymath| and
+|\everydisplay|, you can set up special conventions that you wish to apply
+to all formulas.
+
+\subsection Summary. We have discussed more different kinds of formulas in
+this chapter than you will usually find in any one book of mathematics.
+If you have faithfully done the exercises so far, you can face almost
+any formula with confidence.
+
+\newcount\chalcount \chalcount=0
+\outer\def\challenge{\d@nger\chall}
+\outer\def\cchallenge{\dd@nger\chall}
+\def\chall{\global\advance\chalcount by1
+ \dexercise \hbox{Challenge number \the\chalcount:\enskip}\ignorespaces}
+
+\danger But here are a few more exercises, to help you review what you
+have learned. Each of the following ``challenge formulas'' illustrates one
+or more of the principles already discussed in this chapter. The author
+confesses that he is trying to trip you~up on several of these.
+Nevertheless, if you try each one before looking at the answer, and if
+you're alert for traps, you should find that these formulas provide a good
+way to consolidate and complete your knowledge.
+
+\challenge Explain how to type the phrase `$n^{\rm th}$ root', where
+`$n^{\rm th}$' is treated as a mathematical formula with a superscript in
+roman type.
+\answer |$n^{\rm th}$ root|. \ (Incidentally, it is also acceptable
+to type `|$n$th|', getting `$n$th', in such situations; the fact that
+the $n$ is in italics distinguishes it from the suffix. Typed manuscripts
+generally render this with a hyphen, but `$n$-th' is frowned on nowadays
+when an italic~$n$ is available.) ^^{nth}
+
+\challenge $\qquad\tenmath{\bf S^{\rm-1}TS=dg}(\omega_1,\ldots,\omega_n)
+=\bf\Lambda$.
+\answer |${\bf S^{\rm-1}TS=dg}(\omega_1,|\stretch|\ldots,|\stretch|\omega_n)
+=\bf\Lambda$|.
+\ $\bigl($Did you notice the difference between ^|\omega| ($\omega$)
+and~|w| ($w$)?$\bigr)$
+
+\challenge $\qquad\tenmath\Pr(\,m=n\mid m+n=3\,)$.
+\answer |$\Pr(\,m=n\mid m+n=3\,)$|. \ (Analogous to a set.) ^^|\Pr|
+
+\challenge $\qquad\tenmath\sin18^\circ={1\over4}(\sqrt5-1)$.^^{degrees}
+\answer |$\sin18^\circ={1\over4}(\sqrt5-1)$|. ^^|\circ|
+
+\challenge $\qquad\tenmath k=1.38065\times10^{-16}\rm\,erg\,K^{-1}$.
+\answer |$k=1.38065\times10^{-16}\rm\,erg\,K^{-1}$|.
+
+\challenge $\qquad\tenmath \bar\Phi\subset NL_1^*/N=\bar L_1^*
+ \subseteq\cdots\subseteq NL_n^*/N=\bar L_n^*$.
+\answer |$\bar\Phi\subset NL_1^*/N=\bar L_1^*|\parbreak
+ | \subseteq\cdots\subseteq NL_n^*/N=\bar L_n^*$|.
+
+\challenge $\qquad\tenmath I(\lambda)=\int\!\!\int_Dg(x,y)e^{i\lambda h(x,y)}
+ \,dx\,dy$. % cf. Math. Comp. 37 (1981), 509
+\answer |$I(\lambda)=\int\!\!\int_Dg(x,y)e^{i\lambda h(x,y)}\,dx\,dy$|.\hfil
+\break
+(Although three |\!|'s work out best between consecutive integral signs in
+displays, the text style seems to want only two.) ^^{double integral}
+^^{integral, multiple}
+
+\challenge $\qquad\tenmath
+ \int_0^1\!\cdots\int_0^1f(x_1,\ldots,x_n)\,dx_1\ldots\,dx_n$.
+\answer |$\int_0^1\!\cdots\int_0^1f(x_1,\ldots,x_n)\,dx_1\ldots\,dx_n$|.
+
+\challenge Here's a display.
+$$\tenmath x_{2m}\equiv\cases{Q(X_m^2-P_2W_m^2)-2S^2&($m$ odd)\cr
+ \noalign{\vskip2pt}
+ P_2^2(X_m^2-P_2W_m^2)-2S^2&($m$ even)\cr}\pmod N.$$
+\answer |$$x_{2m}\equiv\cases{Q(X_m^2-P_2W_m^2)-2S^2&($m$ odd)\cr|\parbreak
+ | \noalign{\vskip2pt} % spread the lines apart a little|\parbreak
+ | P_2^2(X_m^2-P_2W_m^2)-2S^2&($m$ even)\cr}\pmod N.$$|
+
+\challenge And another. % ACP Eq. 1.2.9--33
+$$\tenmath (1+x_1z+x_1^2z^2+\cdots\,)\ldots(1+x_nz+x_n^2z^2+\cdots\,)
+ ={1\over(1-x_1z)\ldots(1-x_nz)}.$$
+\answer |$$(1+x_1z+x_1^2z^2+\cdots\,)\ldots(1+x_nz+x_n^2z^2+\cdots\,)|\parbreak
+ | ={1\over(1-x_1z)\ldots(1-x_nz)}.$$| \ (Notice the uses of\/ |\,|.)
+
+\challenge And another. % Eq. 1.2.9--9
+$$\tenmath \prod_{j\ge0}\biggl(\sum_{k\ge0}a_{jk}z^k\biggr)
+ =\sum_{n\ge0}z^n\,\Biggl(\sum_
+ {\scriptstyle k_0,k_1,\ldots\ge0\atop
+ \scriptstyle k_0+k_1+\cdots=n}
+ a_{0k_0}a_{1k_1}\ldots\,\Biggr).$$
+\answer |$$\prod_{j\ge0}\biggl(\sum_{k\ge0}a_{jk}z^k\biggr)|\parbreak
+ | =\sum_{n\ge0}z^n\,\Biggl(\sum_|\parbreak
+ | {\scriptstyle k_0,k_1,\ldots\ge0\atop|\parbreak
+ | \scriptstyle k_0+k_1+\cdots=n}|\parbreak
+ | a_{0k_0}a_{1k_1}\ldots\,\Biggr).$$|\par
+\nobreak\smallskip\noindent Some people would prefer to have the latter
+parentheses larger; but |\left| and |\right| come out a bit too large in this
+case. It's not difficult to define ^|\bigggl| and ^|\bigggr| macros, analogous
+to the definitions of\/ |\biggl| and |\biggr| in Appendix~B.
+
+\challenge And, % cf ACP vol1 p64
+$$\tenmath {(n_1+n_2+\cdots+n_m)!\over n_1!\,n_2!\ldots n_m!}
+ ={n_1+n_2\choose n_2}{n_1+n_2+n_3\choose n_3}
+ \ldots{n_1+n_2+\cdots+n_m\choose n_m}.$$
+\answer |$${(n_1+n_2+\cdots+n_m)!\over n_1!\,n_2!\ldots n_m!}|\parbreak
+ | ={n_1+n_2\choose n_2}{n_1+n_2+n_3\choose n_3}|\parbreak
+ | \ldots{n_1+n_2+\cdots+n_m\choose n_m}.$$|
+
+\challenge Yet another display. % found in Chaundy et al
+$$\tenmath \def\\#1#2{(1-q^{#1_#2+n})} % to save typing
+\Pi_R{a_1,a_2,\ldots,a_M\atopwithdelims[]b_1,b_2,\ldots,b_N}
+ =\prod_{n=0}^R{\\a1\\a2\ldots\\aM\over\\b1\\b2\ldots\\bN}.$$
+\answer |$$\def\\#1#2{(1-q^{#1_#2+n})} % to save typing|\parbreak
+ |\Pi_R{a_1,a_2,\ldots,a_M\atopwithdelims[]b_1,b_2,\ldots,b_N}|\parbreak
+ | =\prod_{n=0}^R{\\a1\\a2\ldots\\aM\over\\b1\\b2\ldots\\bN}.$$|
+^^|\atopwithdelims|
+
+\challenge And another.
+$$\tenmath \sum_{p\rm\;prime}f(p)=\int_{t>1}f(t)\,d\pi(t).$$
+\answer |$$\sum_{p\rm\;prime}f(p)=\int_{t>1}f(t)\,d\pi(t).$$|
+
+\challenge Still another.
+$$\tenmath \{\underbrace{\overbrace{\mathstrut a,\ldots,a}
+ ^{k\;a\mathchar`'\rm s},
+ \overbrace{\mathstrut b,\ldots,b}
+ ^{l\;b\mathchar`'\rm s}}_{k+l\rm\;elements}\}.$$
+\answer |$$\{\underbrace{\overbrace{\mathstrut a,\ldots,a}|\parbreak
+ | ^{k\;a\mathchar`'\rm s},|\parbreak
+ | \overbrace{\mathstrut b,\ldots,b}|\parbreak
+ | ^{l\;b\mathchar`'\rm s}}_{k+l\rm\;elements}\}.$$|\par
+\smallskip\noindent Notice how ^{apostrophes} (instead of primes) were obtained.
+
+\challenge Put a ^|\smallskip| between the rows of matrices in the
+compound matrix ^^{compound matrix}
+$$\tenmath \pmatrix{\pmatrix{a&b\cr c&d\cr}&
+ \pmatrix{e&f\cr g&h\cr}\cr
+ \noalign{\smallskip}
+ 0&\pmatrix{i&j\cr k&l\cr}\cr}.$$
+\answer |$$\pmatrix{\pmatrix{a&b\cr c&d\cr}&|\parbreak
+ | \pmatrix{e&f\cr g&h\cr}\cr|\parbreak
+ | \noalign{\smallskip}|\parbreak
+ | 0&\pmatrix{i&j\cr k&l\cr}\cr}.$$|
+
+\challenge Make the columns ^{flush left} here. % cf Polya/Szego VII.43.2
+$$\tenmath \det\left\vert\,\matrix{
+ c_0&c_1\hfill&c_2\hfill&\ldots&c_n\hfill\cr
+ c_1&c_2\hfill&c_3\hfill&\ldots&c_{n+1}\hfill\cr
+ c_2&c_3\hfill&c_4\hfill&\ldots&c_{n+2}\hfill\cr
+ \,\vdots\hfill&\,\vdots\hfill&
+ \,\vdots\hfill&&\,\vdots\hfill\cr
+ c_n&c_{n+1}\hfill&c_{n+2}\hfill&\ldots&c_{2n}\hfill\cr
+ }\right\vert>0.$$
+\answer |$$\det\left|\||\,\matrix{|\parbreak
+ | c_0&c_1\hfill&c_2\hfill&\ldots&c_n\hfill\cr|\parbreak
+ | c_1&c_2\hfill&c_3\hfill&\ldots&c_{n+1}\hfill\cr|\parbreak
+ | c_2&c_3\hfill&c_4\hfill&\ldots&c_{n+2}\hfill\cr|\parbreak
+ | \,\vdots\hfill&\,\vdots\hfill&|\parbreak
+ | \,\vdots\hfill&&\,\vdots\hfill\cr|\parbreak
+ | c_n&c_{n+1}\hfill&c_{n+2}\hfill&\ldots&c_{2n}\hfill\cr|\parbreak
+ | }\right|\||>0.$$|
+
+\cchallenge The main problem here is to prime the $\sum$.
+^^|\sum prime| ^^{=def}
+$$\tenmath \mathop{{\sum}'}_{x\in A}f(x)\mathrel{\mathop=^{\rm def}}
+ \sum_{\scriptstyle x\in A\atop\scriptstyle x\ne0}f(x).$$
+\answer |$$\mathop{{\sum}'}_{x\in A}f(x)\mathrel{\mathop=^{\rm def}}|\parbreak
+ | \sum_{\scriptstyle x\in A\atop\scriptstyle x\ne0}f(x).$$|\par
+\smallskip\noindent
+This works because |{\sum}| is type Ord (so its superscript is not set
+above), but ^|\mathop||{{\sum}'}| is type Op (so its subscript is set below).
+The limits are centered on $\sum'$, however, not on $\sum$. If you don't
+like that, the remedy is more difficult; one solution is to use
+|\sumprime_{x\in A}| where ^|\sumprime| is defined as follows:
+\par\nobreak\medskip
+|\def\sumprime_#1{\setbox0=\hbox{$\scriptstyle{#1}$}|\parbreak
+| \setbox2=\hbox{$\displaystyle{\sum}$}|\parbreak
+| \setbox4=\hbox{${}'\mathsurround=0pt$}|\parbreak
+| \dimen0=.5\wd0 \advance\dimen0 by-.5\wd2|\parbreak
+| \ifdim\dimen0>0pt|\parbreak
+| \ifdim\dimen0>\wd4 \kern\wd4 \else\kern\dimen0\fi\fi|\parbreak
+| \mathop{{\sum}'}_{\kern-\wd4 #1}}|
+
+\cchallenge You may be ready now for this display.
+$$\tenmath 2\uparrow\uparrow k\mathrel{\mathop=^{\rm def}}
+ 2^{2^{2^{\cdot^{\cdot^{\cdot^2}}}}}
+ \vbox{\hbox{$\Big\}\scriptstyle k$}\kern0pt}.$$
+\answer |$$2\uparrow\uparrow k\mathrel{\mathop=^{\rm def}}|\parbreak
+ | 2^{2^{2^{\cdot^{\cdot^{\cdot^2}}}}}|\parbreak
+ | \vbox{\hbox{$\Big\}\scriptstyle k$}\kern0pt}.$$|\par
+
+\cchallenge And finally, when you have polished off all the other examples,
+here's the ultimate test. Explain how to obtain the ^{commutative diagram}
+% from Invent. Math. 70 (1982), 34 % with "typo" corrected 99.12.03
+$$\tenmath\def\normalbaselines{\baselineskip20pt\lineskip1pt\lineskiplimit0pt }
+\def\mapright#1{\smash{
+ \mathop{\longrightarrow}\limits^{#1}}}
+\def\mapdown#1{\Big\downarrow
+ \rlap{$\vcenter{\hbox{$\scriptstyle#1$}}$}}
+\matrix{\noalign{\vskip6pt}&&&&&&0\cr
+ &&&&&&\mapdown{}\cr
+ 0&\mapright{}&{\cal O}_C&\mapright\iota&
+ \cal E&\mapright\rho&\cal L&\mapright{}&0\cr
+ &&\Big\Vert&&\mapdown\phi&&\mapdown\psi\cr
+ 0&\mapright{}&{\cal O}_C&\mapright\pi&
+ \pi_*{\cal O}_D&\mapright\delta&
+ R^1f_*{\cal O}_V(-D)&\mapright{}&0\cr
+ &&&&&&\mapdown{\theta_i\otimes\gamma^{-1}}\cr
+ &&&&&&\hidewidth R^1f_*\bigl({\cal O}
+ _V(-iM)\bigr)\otimes\gamma^{-1}\hidewidth\cr
+ &&&&&&\mapdown{}\cr
+ &&&&&&0\cr\noalign{\vskip6pt}}$$
+using ^|\matrix|. \ (Many of the entries are blank.)
+\answer If you have to do a lot of commutative diagrams, you will want to
+define some macros like those in the first few lines of this solution.
+The ^|\matrix| macro resets the baselines to ^|\normalbaselines|, because
+other commands like |\openup| might have changed them, so
+we redefine |\normalbaselines| in this solution. Some of the things
+shown here haven't been explained yet, but Chapter~22 will reveal all.
+\smallskip
+|$$\def\normalbaselines{\baselineskip20pt|\parbreak
+| \lineskip3pt \lineskiplimit3pt }|\parbreak
+|\def\mapright#1{\smash{|\parbreak
+| \mathop{\longrightarrow}\limits^{#1}}}|\parbreak
+|\def\mapdown#1{\Big\downarrow|\parbreak
+| \rlap{$\vcenter{\hbox{$\scriptstyle#1$}}$}}|\parbreak
+|\matrix{&&&&&&0\cr|\parbreak
+| &&&&&&\mapdown{}\cr|\parbreak
+| 0&\mapright{}&{\cal O}_C&\mapright\iota&|\parbreak
+| \cal E&\mapright\rho&\cal L&\mapright{}&0\cr|\parbreak
+| &&\Big\Vert&&\mapdown\phi&&\mapdown\psi\cr|\parbreak
+| 0&\mapright{}&{\cal O}_C&\mapright\pi&|\parbreak
+| \pi_*{\cal O}_D&\mapright\delta&|\parbreak
+| R^1f_*{\cal O}_V(-D)&\mapright{}&0\cr|\parbreak
+| &&&&&&\mapdown{\theta_i\otimes\gamma^{-1}}\cr|\parbreak
+| &&&&&&\hidewidth R^1f_*\bigl({\cal O}|\parbreak
+| _V(-iM)\bigr)\otimes\gamma^{-1}|^|\hidewidth||\cr|\parbreak
+| &&&&&&\mapdown{}\cr|\parbreak
+| &&&&&&0\cr}$$|
+
+\subsection Words of advice. The number of different notations is
+enormous and still growing, so you will probably continue to find
+new challenges as you continue to type mathematical papers. It's a
+good idea to keep a personal notebook in which you record all of
+the non-obvious formulas that you have handled successfully,
+showing both the final output and what you typed to get it.
+Then you'll be able to refer back to those solutions when you
+discover that you need to do something similar, a few months later.
+
+If you're a mathematician who types your own papers, you have now learned
+^^{author, typesetting by}
+how to get enormously complex formulas into print, and you can do so
+without going through an intermediary who may somehow distort their
+meaning. But please, don't get too carried away by your newfound talent;
+the fact that you are able to typeset your formulas with \TeX\ doesn't
+necessarily mean that you have found the best notation for communicating
+with the readers of your work. Some notations will be unfortunate even
+when they are beautifully formatted.
+
+\endchapter
+
+Mathematicians are like Frenchmen:\/
+% Die Mathematiker sind eine Art Franzosen:
+whenever you say something to them, they translate it into their own language,
+% redet man zu ihnen, so u"bersetzen sie es in ihre Sprache,
+and at once it is something entirely different.
+% und dann ist es alsobald ganz etwas Anders.
+\author ^{GOETHE}, {\sl Maxims and Reflexions\/} (1829)
+% see Schriften der Goethe-Gesellschaft, vol 21, pp 266 and 389
+
+\bigskip
+
+The best notation is no notation;
+whenever it is possible to avoid the use of a complicated alphabetic apparatus,
+avoid it.
+A good attitude to the preparation of written mathematical exposition
+is to pretend that it is spoken.
+Pretend that you are explaining the subject to a friend
+on a long walk in the woods, with no paper available;
+fall back on symbolism only when it is really necessary.
+\author PAUL ^{HALMOS}, {\sl How to Write Mathematics\/} (1970)
+ % in {\sl L'Enseignement Math\'ematique\/}
+ % vol 16, 123--152; section 15; reprinted in AMS pub "How to Write Math"
+
+\eject
+ \beginchapter Chapter 19. Displayed Equations
+
+By now you know how to type mathematical formulas so that \TeX\ will handle
+them with supreme elegance; your knowledge of math typing is nearly complete.
+But there is one more part to the story, and the purpose of this chapter
+is to present the happy ending. We have discussed how to deal with individual
+formulas; but ^{displays} often involve a whole bunch of different formulas,
+or different pieces of a huge formula, and it's a bit of a problem to
+lay them out so that they line up properly with each other. Fortunately,
+large displays generally fall into a few simple patterns.
+
+\subsection One-line displays. Before plunging into the general question
+of display layout, let's recapitulate what we have already covered. If
+you type `|$$|\<formula>|$$|', \TeX\ will display the formula
+^^{dollardollar} in flamboyant display style, centering it on a line by
+itself. We have also noted in Chapter~18 that it's possible to display two
+short formulas at once, by typing
+`|$$|\<formula$_1$>^|\qquad|\<formula$_2$>|$$|'; this reduces the
+two-formula problem to a one-formula problem. You get the two formulas
+separated by two quads of space, the whole being centered on a line.
+
+Displayed equations often involve ordinary text. Chapter~18 explains
+how to get roman type into formulas without leaving math mode, but the
+best way to get text into a display is to put it into an ^|\hbox|.
+There needn't even be any math at all; to typeset
+$$\hbox{Displayed Text}$$
+you can simply say `|$$\hbox{Displayed Text}$$|'. But here's a more interesting
+example:
+$$X_n=X_k \qquad\hbox{if and only if}\qquad
+ Y_n=Y_k \quad\hbox{and}\quad Z_n=Z_k.$$
+Formulas and text were combined in this case by typing
+\begintt
+$$X_n=X_k \qquad\hbox{if and only if}\qquad
+ Y_n=Y_k \quad\hbox{and}\quad Z_n=Z_k.$$
+\endtt
+Notice that |\qquad| appears around `if and only if', but a single ^|\quad|
+surrounds `and'; this helps to indicate that the $Y$ and~$Z$ parts of the
+display are related more closely to each other than to the $X$~part.
+
+Consider now the display
+$$Y_n=X_n\bmod p \quad\hbox{and}\quad Z_n=X_n\bmod q
+ \qquad\hbox{for all }n\ge0.$$
+Can you figure out how to type this? One solution is
+\begintt
+$$Y_n=X_n\bmod p \quad\hbox{and}\quad Z_n=X_n\bmod q
+ \qquad\hbox{for all }n\ge0.$$
+\endtt
+Notice that a space has been left after `|all|' in the hbox here, since spaces
+disappear when they are out in formula-land. But there's a simpler and more
+logical way to proceed, once you get used to \TeX's idea of modes: You can type
+\begintt
+... \qquad\hbox{for all $n\ge0$.}$$
+\endtt
+Wow---that's math mode inside of horizontal mode inside of display
+math mode. But in this way your manuscript mirrors what you are trying to
+accomplish, while the previous solution (with the space after `|all|')
+looks somewhat forced.
+
+\exercise Typeset the following four displays (one at a time):
+$$\openup1\jot\displaylines{
+\sum_{n=0}^\infty a_nz^n\qquad\hbox{converges if}\qquad
+ \vert z\vert<\Bigl(\limsup_{n\to\infty}
+ \root n\!\of{\vert a_n\vert}\,\Bigr)^{-1}.\cr
+{f(x+\Delta x)-f(x)\over\Delta x}\to f'(x)\qquad\hbox{as $\Delta x\to0$.}\cr
+\noalign{\vskip2pt}
+\Vert u_i\Vert=1,\qquad u_i\cdot u_j=0\quad\hbox{if $i\ne j$.}\cr
+\it\hbox{The confluent image of}\quad\left\{
+ \matrix{\hbox{an arc}\hfill\cr\hbox{a circle}\hfill\cr
+ \hbox{a fan}\hfill\cr}
+ \right\}\quad\hbox{is}\quad\left\{
+ \matrix{\hbox{an arc}\hfill\cr\hbox{an arc or a circle}\hfill\cr
+ \hbox{a fan or an arc}\hfill\cr}\right\}.\cr
+\noalign{\vskip-8pt}}$$
+^^|\Delta| ^^|\Vert|
+% the last example comes from Proc AMS 55 (1976), 410, with typos corrected
+\answer |$$\sum_{n=0}^\infty a_nz^n\qquad\hbox{converges if}\qquad|\parbreak
+| |\||z|\||<\Bigl(\limsup_{n\to\infty}\root n\!\of{|\||a_n|\|^^|\root|
+ |}\,\Bigr)^{-1}.$$|\kern-.33pt\par
+\smallskip
+|$${f(x+\Delta x)-f(x)\over\Delta x}\to f'(x)|\parbreak
+| \qquad\hbox{as $\Delta x\to0$.}$$|\par
+\smallskip
+|$$\|\||u_i\|\||=1,\qquad u_i\cdot u_j=0\quad\hbox{if $i\ne j$.}$$|\par
+\smallskip
+|$$\it\hbox{The confluent image of}\quad\left\{|\parbreak
+| \matrix{\hbox{an arc}\hfill\cr\hbox{a circle}\hfill\cr|\parbreak
+| \hbox{a fan}\hfill\cr}|\parbreak
+| \right\}\quad\hbox{is}\quad\left\{|\parbreak
+| \matrix{\hbox{an arc}\hfill\cr|\parbreak
+| \hbox{an arc or a circle}\hfill\cr|\parbreak
+| \hbox{a fan or an arc}\hfill\cr}\right\}.$$|\par
+\smallskip\noindent
+The first example includes |\!| and |\,| to give slightly refined spacing;
+but the point of the problem was to illustrate the hbox, not to fuss over
+such extra details.
+The last example can be done much more simply using the ideas of
+Chapter~22, if you don't mind descending to the level of \TeX\ primitives;
+for example, the first matrix could be replaced by ^^|\halign|
+\begintt
+\,\vcenter{\halign{#\hfil\cr an arc\cr a circle\cr a fan\cr}}\,
+\endtt
+and the second is similar.
+
+\dangerexercise Sometimes display style is too grandiose, when the formula
+being displayed is
+$$y={1\over2}x$$
+or something equally simple. One day B. L. ^{User} tried to remedy this by
+typing it as `|$$y={\scriptstyle1\over\scriptstyle2}x$$|', but the
+resulting formula
+$$y={\scriptstyle1\over\scriptstyle2}x$$
+wasn't at all what he had in mind. What's the right way to get ^^{one half}
+^^{1/2--unslashed form} simply `$y={1\over2}x$' when you don't want big
+^{fractions in displays}?
+\answer |$$\textstyle y={1\over2}x$$|. \ (Switching to text style is
+especially common in multiline formulas. For example, you will probably
+find occasions to use ^|\textstyle| on both sides of the |&|'s within
+an ^|\eqalign|.)
+
+\dangerexercise What difference, if any, is there between the result of
+typing `|$$|\<formula>|$$|' and the result of typing
+`|$$\hbox{$|\<formula>|$}$$|'\thinspace?
+\answer The latter formula will be in text style, not display style.
+And even if you do type `|$$\hbox{$\displaystyle{|\<formula>|}$}$$|', the
+results are not quite the same, as we will see later: \TeX\ will compress
+the glue in `|$$|\<formula>|$$|' if the formula is too wide to fit on
+a line at its natural width, but the glue inside |\hbox{...}| is frozen
+at its natural width. The |\hbox| version also invokes |\everymath|.
+
+\dangerexercise You may have noticed that most of the displays in this
+manual are not centered; displayed material is usually aligned at the
+left with the paragraph indentation, as part of the book design, because
+this is an unusual book. Explain how you could typeset a formula like
+$$\leftline{\indent$\displaystyle
+ 1-{1\over2}+{1\over3}-{1\over4}+\cdots=\ln2$}$$
+^^{displays, non-centered}
+that is off-center in this way.
+\answer One solution is to put the formula in an hbox that occupies a full line:
+\begintt
+$$\leftline{\indent$\displaystyle
+ 1-{1\over2}+{1\over3}-{1\over4}+\cdots=\ln2$}$$
+\endtt
+But this takes a bit of typing. If you make the definitions
+\begintt
+\def\leftdisplay#1$${\leftline{\indent$\displaystyle{#1}$}$$}
+\everydisplay{\leftdisplay}
+\endtt
+you can type `|$$|\<formula>|$$|' as usual, and the formatting will be
+inserted automatically. \ (This doesn't work with equation numbers;
+Appendix~D illustrates how to handle them as well.)
+
+If you've had previous experience typing mathematical papers, you probably
+have been thinking, ``What about ^{equation numbers}? When is this book
+going to talk about them?'' Ah yes, now is the time to discuss those sneaky
+little labels that appear off to the side of displays. If you type
+\begindisplay
+|$$|\<formula>|\eqno|\<formula>|$$|
+\enddisplay
+\TeX\ will display the first formula and it will also put an equation number
+(the second formula) at the right-hand margin. For example,
+\begintt
+$$x^2-y^2 = (x+y)(x-y).\eqno(15)$$
+\endtt
+^^|\eqno|
+will produce this:
+$$x^2-y^2 = (x+y)(x-y).\eqno(15)$$
+You can also get equation numbers at the left-hand margin, with ^|\leqno|.
+For example,
+\begintt
+$$x^2-y^2 = (x+y)(x-y).\leqno(16)$$
+\endtt
+will produce this:
+$$x^2-y^2 = (x+y)(x-y).\leqno(16)$$
+Notice that you always give the equation number second, even when it is going
+to appear at the left. Everything from the |\eqno| or |\leqno| command to
+the |$$| that ends the display is the equation number. Thus, you're not
+allowed to have two equation numbers in the same display; but there's a
+way to get around that restriction, as we'll see later.
+
+\danger Nowadays people are using right-hand equation numbers more and more,
+because a display most often comes at the end of a sentence or clause, and
+the right-hand convention keeps the number from intruding into the clause.
+Furthermore, it's often possible to save space when a displayed equation
+follows a short text line, since less space is needed above the display;
+such savings are not possible with |\leqno|, because there's no room for
+overlap. For example, there is less space above display~(15) than there is
+above~(16) in our illustrations of\/ |\eqno| and |\leqno|, although the
+formulas and text are otherwise identical.
+
+\danger If you look closely at (15) and (16) above, you can see that the
+displayed formulas have been centered without regard to the presence of
+the equation numbers. But when a formula is large, \TeX\ makes sure that
+it does not interfere with its number; the equation number may even be
+placed on a line by itself.
+
+\exercise How would you produce the following display?
+$$\prod_{k\ge0}{1\over(1-q^kz)}=
+ \sum_{n\ge0}z^n\bigg/\!\!\prod_{1\le k\le n}(1-q^k).\eqno(16')$$
+\answer |$$\prod_{k\ge0}{1\over(1-q^kz)}=|\parbreak
+ | \sum_{n\ge0}z^n\bigg/\!\!\prod_{1\le k\le n}(1-q^k).\eqno(16')$$|
+
+\dangerexercise Equation numbers are math formulas, typeset in text style.
+So how can you get an equation number like `\hbox{(3--1)}'
+(with an ^{en-dash})?
+\answer |\eqno\hbox{(3--1)}|.
+
+\ddangerexercise B. L. ^{User} tried typing `|\eqno(*)|' and `|\eqno(**)|',
+and he was pleased to discover that this produced the equation numbers
+`$(*)$' and `$(**)$'. \ [He had been a bit worried that they would come out
+`(*)' and `(**)' instead.] \ But then a few months later he tried
+`|\eqno(***)|' and got a surprise. What was it?
+\answer When you type an ^{asterisk} in math mode, plain \TeX\ considers
+|*| to be a binary operation. In the cases `|(*)|' and `|(**)|', the
+binary operations are converted to type~Ord, because they don't appear in
+a binary context; but the middle asterisk in `|(***)|' remains of type~Bin.
+So the result was `$(***)$'. To avoid the extra medium spaces, you can
+type `|\eqno(*{*}*)|'; or you can change ^|\mathcode||`*|, if you never use
+|*| as a binary operation.
+
+\ddanger Somewhere in this manual there ought to be a description of exactly
+how \TeX\ displays formulas; i.e., how it centers them, how it places the
+equation numbers, how it inserts extra space above and below, and so on.
+Well, now is the time for those rules to be stated. They are somewhat
+complex, because they interact with things like |\parshape|, and because
+they involve several parameters that haven't been discussed yet. The purpose
+of the rules is to explain exactly what sorts of boxes, glue, and
+penalties are placed onto the current ^{vertical list} when a display occurs.
+
+\ddanger If a display occurs after, say, four lines of a paragraph, \TeX's
+internal register called ^|\prevgraf| will be equal to~4 when the display
+starts. The display will be assumed to take three lines, so |\prevgraf|
+will become~7 when the paragraph is resumed at the end of the display
+(unless you have changed |\prevgraf| in the meantime). \TeX\ assigns
+special values to three \<dimen> parameters immediately after the opening
+|$$| is sensed: ^|\displaywidth| and ^|\displayindent| are set to the
+line width~$z$ and the shift amount~$s$ for line number |\prevgraf|${}+2$,
+based on the current paragraph shape or hanging indentation. \ (Usually
+|\displaywidth| is the same as ^|\hsize|, and |\displayindent| is zero,
+but the paragraph shape can vary as described in Chapter~14.) \ Furthermore,
+^|\predisplaysize| is set to the effective width~$p$ of the line preceding
+the display, as follows: If there was no previous line (e.g., if the |$$|
+was preceded by ^|\noindent| or by the closing |$$| of another display),
+$p$~is set to $-16383.99999\pt$ (i.e., to the smallest legal dimension,
+$-$^|\maxdimen|). Otherwise \TeX\ looks inside the hbox that was formed
+by the previous line, and sets $p$ to the position of the right edge of
+the rightmost box inside that hbox, plus the indentation by which the
+enclosing hbox has been moved right, plus two ems in the current font.
+However, if this value of~$p$ depends on the fact that glue in that hbox
+was stretching or shrinking---for example, if the ^|\parfillskip| glue is
+finite, so that the material preceding it has not been set at its natural
+width---then $p$~is set to |\maxdimen|. \ (This doesn't happen
+often, but it keeps \TeX\ machine independent, since $p$~never depends on
+quantities that may be rounded differently on different computers.) \
+Notice that |\displaywidth| and |\displayindent| are not
+affected by |\leftskip| and |\rightskip|, but
+|\predisplaysize| is. The values of\/ |\displaywidth|,
+|\displayindent|, and |\predisplaysize|
+will be used by \TeX\ after the displayed formula has been
+read, as explained below; your program can examine them and/or change
+them, if you want the typesetting to be done differently.
+
+\ddanger After a display has been read, \TeX\ converts it from a math list
+to a horizontal list~$h$ in display style, as explained in
+Appendix~G\null. An equation number, if present, is processed in text
+style and put into an hbox~$a$ with its natural width. Now the fussy
+processing begins: Let $z$, $s$, and~$p$ be the current values of\/\
+|\displaywidth|, |\displayindent|, and
+|\predisplaysize|. Let $q$ and~$e$ be zero if there is no equation
+number; otherwise let~$e$ be the width of the equation number, and let~$q$
+be equal to $e$~plus one quad in the symbols font (i.e., in
+^|\textfont||2|). Let $w_0$ be the natural width of the displayed
+formula~$h$. If $w_0+q\le z$, list~$h$~is packaged in an hbox~$b$ having
+its natural width~$w_0$. But if $w_0+q>z$ (i.e., if the display is too
+wide to fit at its natural width), \TeX\ performs the following ``^{squeeze
+routine}'': If $e\ne0$ and if there is enough shrinkability in the
+displayed formula~$h$ to reduce its width to $z-q$, then list $h$ is
+packaged in an hbox~$b$ of width~$z-q$. Otherwise $e$ is set to zero, and
+list~$h$ is packaged in a (possibly overfull) hbox~$b$ of width $\min(w_0,z)$.
+
+\ddanger (Continuation.) \ \TeX\ tries now to center the display without
+regard to the equation number. But if such centering would make it too close
+to that number (where ``too close'' means that the space between them is less
+than the width~$e$), the equation is either centered in the remaining space
+or placed as far from the equation number as possible. The latter alternative
+is chosen only if the first item on list~$h$ is glue, since \TeX\ assumes that
+such glue was placed there in order to control the spacing precisely.
+But let's state the rules more formally: Let~$w$ be the width of box~$b$.
+\TeX\ computes a displacement~$d$, to be used later when positioning box~$b$,
+by first setting $d={1\over2}(z-w)$. If $e>0$ and if $d<2e$, then $d$~is
+reset to ${1\over2}(z-w-e)$ or to zero, where zero is chosen if list~$h$
+begins with a glue item.
+
+\ddanger (Continuation.) \ \TeX\ is now ready to put things onto the current
+vertical list, just after the material previously constructed for the
+paragraph-so-far. First comes a ^{penalty} item, whose cost is an integer
+parameter called ^|\predisplaypenalty|. Then comes glue. If $d+s\le p$,
+or if there was a left equation number (|\leqno|), \TeX\ sets $g_a$ and
+$g_b$ to glue items specified by the parameters ^|\abovedisplayskip|
+and ^|\belowdisplayskip|, respectively; otherwise $g_a$ and $g_b$ become
+glue items corresponding to ^|\abovedisplayshortskip| and
+^|\belowdisplayshortskip|. \ [Translation: If the predisplaysize is short
+enough so that it doesn't overlap the displayed formula, the glue above and
+below the display will be ``short'' by comparison with the glue that is
+used when there is an overlap.] \ If $e=0$ and if there is an |\leqno|,
+the equation number is appended as an hbox by itself, shifted right~$s$ and
+preceded by interline glue as usual; an infinite penalty is also appended,
+to prevent a page break between this number and the display. Otherwise
+a glue item~$g_a$ is placed on the vertical list.
+
+\ddanger (Continuation.) \ Now comes the displayed equation itself. If
+$e\ne0$, the equation number box~$a$ is combined with the formula box~$b$ as
+follows: Let~$k$ be a kern of width $z-w-e-d$. In the |\eqno| case, box~$b$
+is replaced by an hbox containing $(b,k,a)$; in the |\leqno| case, box~$b$
+is replaced by an hbox containing $(a,k,b)$, and $d$~is set to zero. In all
+cases, box~$b$ is then appended to the vertical list, shifted right by~$s+d$.
+
+\ddanger (Continuation.) \ The final task is to append the glue or the
+equation number that follows the display. If there was an |\eqno| and if
+$e=0$, an infinite penalty is placed on the vertical list, followed by the
+equation number box~$a$ shifted right by $s+z$ minus its width, followed
+by a penalty item whose cost is the value of\/ ^|\postdisplaypenalty|.
+Otherwise a penalty item for the |\postdisplaypenalty| is appended
+first, followed by a glue item for~$g_b$ as specified above. \TeX\ now
+adds~3 to |\prevgraf| and returns to horizontal mode, ready to resume the
+paragraph.
+
+\ddanger One consequence of these rules is that you can force an equation
+number to appear on a line by itself by making its width zero, i.e.,
+by saying either `|\eqno|^|\llap||{$|\<formula>|$}|' or
+`|\leqno|^|\rlap||{$|\<formula>|$}|'. This makes $e=0$, and
+the condition $e=0$ controls \TeX's positioning logic, as explained
+in the rules just given.
+
+\ddanger Plain \TeX\ sets |\predisplaypenalty=10000|, because fine
+printers traditionally shun displayed formulas at the very top of a page.
+You can change |\predisplaypenalty| and
+|\postdisplaypenalty| if you want to encourage or discourage
+page breaks just before or just after a display. For example,
+`\hbox{|$$\postdisplaypenalty=|}\allowbreak\hbox{|-10000|\<formula>|$$|}'
+will force a page break, putting the formula at the bottom line. It is better
+to force a ^{page break} this way than to say ^|\eject| right after |$$...$$|;
+such an eject (which follows the |\belowdisplayskip| glue below the
+display) causes the page to be short, because it leaves unwanted glue at
+the bottom.
+
+\ddangerexercise Read the rules carefully and deduce the final position
+of `$x=y$' in the formula
+\begintt
+$$\quad x=y \hskip10000pt minus 1fil \eqno(5)$$
+\endtt
+assuming that there is no hanging indentation. Also consider |\leqno|
+instead of\/ |\eqno|.
+\answer Assuming that |\hsize| is less than $10000\pt$, the natural width of
+this equation will be too large to fit on a line; also, |\quad| specifies
+glue at the left. Therefore `$x=y$' will appear exactly $1\em$ from
+the left, and `(5)' will appear flush right. \ (The widths will satisfy
+^^{displays, non-centered} $w=z-q$, $d=0$, $k=q-e=18\rm\,mu$.) \
+In the case of\/ |\leqno|, `(5)' will appear flush left, followed by
+one quad of space in |\textfont2|, followed by one quad of space in the
+current text font, followed by `$x=y$'.
+
+\ddanger \TeX\ also allows ``^{alignment displays},'' which are not
+processed in math mode because they contain no formulas at the outer
+level. An alignment display is created by commands of the general form
+\begindisplay
+|$$|\<assignments>|\halign{|\<alignment>|}|\<assignments>|$$|
+\enddisplay
+where the \<assignments> are optional things like parameter changes that do not
+produce any math lists. In such displays, the |\halign| is processed exactly
+as if it had appeared in vertical mode, and it will construct a vertical
+list~$v$ as usual, except that each row of the alignment will be shifted
+right by the ^|\displayindent|. After the alignment and the closing
+assignments have been processed, \TeX\ will put a ^|\predisplaypenalty|
+item and some ^|\abovedisplayskip| glue on the main vertical list, followed
+by~$v$, followed by a ^|\postdisplaypenalty| item and ^|\belowdisplayskip|
+glue. Thus, alignment displays are essentially like ordinary alignments,
+except that they can interrupt paragraphs; furthermore, they are embedded in
+glue and penalties just like other displays. The ^|\displaywidth| and
+^|\predisplaysize| do not affect the result, although you could use
+those parameters in your ^|\halign|. An entire alignment display is considered
+to be only three lines long, as far as ^|\prevgraf| is concerned.
+
+\subsection Multi-line displays. OK, the use of displayed formulas is
+very nice. But when you try typing a lot of manuscripts you will run into
+some displays that don't fit the simple pattern of a one-line formula with
+or without an equation number. Plain \TeX\ provides special control
+sequences that will cover most of the remaining cases.
+
+Multi-line displays usually consist of several equations that should be
+lined up by their `$=$'~signs, as in
+$$\eqalign{X_1+\cdots+X_p&=m,\cr
+ Y_1+\cdots+Y_q&=n.\cr}$$
+The recommended procedure for such a display is to use ^|\eqalign|,
+which works with special markers |&| ^^{ampersand} and ^|\cr| that we
+have already encountered in connection with |\cases| and |\matrix|
+in Chapter~18. Here's how to type this particular one:
+\begintt
+$$\eqalign{X_1+\cdots+X_p&=m,\cr
+ Y_1+\cdots+Y_q&=n.\cr}$$
+\endtt
+There can be any number of equations in an |\eqalign|; the general pattern is
+$$\halign{\indent#&#\hfil\cr
+|\eqalign{|&\<left-hand side$_1$>|&|\<right-hand side$_1$>|\cr|\cr
+ &\<left-hand side$_2$>|&|\<right-hand side$_2$>|\cr|\cr
+\noalign{\vskip-2pt}
+ &\qquad\vdots\cr
+ &\<left-hand side$_n$>|&|\<right-hand side$_n$>|\cr}|\cr}$$
+where each \<right-hand side> starts with the symbol on which you want
+alignment to occur. For example, every right-hand side often begins
+with an $=$~sign. The equations will be typeset in display style.
+
+\exercise In practice, the left-hand sides of aligned formulas are often
+blank, and the alignment is often done with respect to other symbols
+as well as~$=$. For example, the following display is typical; see if you
+can guess how the author typed it:
+$$\eqalign{T(n)\le T(2^{\lceil\lg n\rceil})
+ &\le c(3^{\lceil\lg n\rceil}-2^{\lceil\lg n\rceil})\cr
+ &<3c\cdot3^{\lg n}\cr
+ &=3c\,n^{\lg3}.\cr}$$ % from v2 p279
+\answer (Note in particular that the final `|.|'\ comes {\sl before\/} the
+final `|\cr|'.)
+\begintt
+$$\eqalign{T(n)\le T(2^{\lceil\lg n\rceil})
+ &\le c(3^{\lceil\lg n\rceil}-2^{\lceil\lg n\rceil})\cr
+ &<3c\cdot3^{\lg n}\cr
+ &=3c\,n^{\lg3}.\cr}$$
+\endtt
+
+The result of\/ |\eqalign| is a vertically centered box. This makes it easy to
+get a formula like
+$$\left\{
+\eqalign{\alpha&=f(z)\cr \beta&=f(z^2)\cr \gamma&=f(z^3)\cr}
+\right\}\qquad\left\{
+\eqalign{x&=\alpha^2-\beta\cr y&=2\gamma\cr}\right\}.$$ % meaningless
+You simply use |\eqalign| twice in the same line:
+\begintt
+$$\left\{
+\eqalign{\alpha&=f(z)\cr \beta&=f(z^2)\cr \gamma&=f(z^3)\cr}
+\right\}\qquad\left\{
+\eqalign{x&=\alpha^2-\beta\cr y&=2\gamma\cr}\right\}.$$
+\endtt
+
+\exercise Try your hand at the numbered two-line display % Polya/Szego V.29
+$$\eqalign{P(x)&=a_0+a_1x+a_2x^2+\cdots+a_nx^n,\cr
+ P(-x)&=a_0-a_1x+a_2x^2-\cdots+(-1)^na_nx^n.\cr}\eqno(30)$$
+[{\sl Hint:\/} Use the fact that |\eqalign| produces a vertically centered
+box; the equation number `(30)' is supposed to appear halfway between
+the two lines.]
+\answer |$$\eqalign{P(x)&=a_0+a_1x+a_2x^2+\cdots+a_nx^n,\cr|\parbreak
+ | P(-x)&=a_0-a_1x+a_2x^2-\cdots+(-1)^na_nx^n.\cr}\eqno(30)$$|\par
+
+\exercise What happens if you forget the |&| in one equation of an |\eqalign|?
+\answer Both sides of that equation are considered to be on the left, so
+you get results that look like this:
+$$\openup-\jot
+\left\{\eqalign{\alpha&=f(z)\cr \beta&=f(z^2)\cr \gamma=f(z^3)\cr}
+ \right\}.$$
+
+\danger Multi-line formulas sometimes fit together in odd ways, and you'll
+find that every once in a~while you will want to move certain lines farther
+apart or closer together. If you type `^|\noalign||{|^|\vskip|\<glue>|}|'
+after any |\cr|, \TeX\ will insert the given amount of extra glue just
+after that particular line. For example,
+\begintt
+\noalign{\vskip3pt}
+\endtt
+will put $3\pt$ of additional space between lines. You can also change the
+amount of space before the first line, in the same way.
+
+The next level of complexity occurs when you have several aligned
+equations with several equation numbers. Or perhaps some of the
+lines are numbered and others are not:
+$$\eqalignno{(x+y)(x-y)&=x^2-xy+yx-y^2\cr
+ &=x^2-y^2;&(4)\cr
+ (x+y)^2&=x^2+2xy+y^2.&(5)\cr}$$
+For this situation plain \TeX\ provides ^|\eqalignno|; you use it like
+|\eqalign|, but on each line that you want an equation number you add
+`|&|\<equation number>' just before the |\cr|. The example above was
+generated by
+\begintt
+$$\eqalignno{(x+y)(x-y)&=x^2-xy+yx-y^2\cr
+ &=x^2-y^2;&(4)\cr
+ (x+y)^2&=x^2+2xy+y^2.&(5)\cr}$$
+\endtt
+Notice that the second |&| is omitted unless there's an equation number.
+
+And there's also ^|\leqalignno|, which puts equation numbers at the left.
+In this case it is appropriate to move the `(4)' to the beginning
+of its equation:
+$$\leqalignno{(x+y)(x-y)&=x^2-xy+yx-y^2&(4)\cr
+ &=x^2-y^2;\cr
+ (x+y)^2&=x^2+2xy+y^2.&(5)\cr}$$
+Although the equation numbers appear at the left, you are still supposed to
+input them at the right, just as you do with |\leqno|; in other words,
+you should type
+`|$$\leqalignno{(x+y)(x-y)&...&(4)\cr...}$$|' to get the previous display.
+
+Caution: |\eqalignno| and |\leqalignno| both center the set of equations
+without regard to the widths of the equation numbers. If the equations or
+their numbers get too wide, they might overlap, yet no error message will
+be given.
+
+\exercise Typeset the following display: ^^|\gcd|
+$$\leqalignno{\gcd(u,v)&=\gcd(v,u);&(9)\cr
+ \gcd(u,v)&=\gcd(-u,v).&(10)\cr}$$ % v2 p316
+\answer |$$\leqalignno{\gcd(u,v)&=\gcd(v,u);&(9)\cr|\parbreak
+ | \gcd(u,v)&=\gcd(-u,v).&(10)\cr}$$|
+
+\exercise And here's another one to try, just to keep in practice: ^^|\int|
+$$\vbox{
+\eqalignno{\biggl(\int_{-\infty}^\infty e^{-x^2}\,dx\biggr)^2
+ &=\int_{-\infty}^\infty\int_{-\infty}^\infty
+ e^{-(x^2+y^2)}\,dx\,dy\cr
+ &=\int_0^{2\pi}\int_0^\infty e^{-r^2}r\,dr\,d\theta\cr
+ &=\int_0^{2\pi}\biggl(-{e^{-r^2}\over2}
+ \bigg\vert_{r=0}^{r=\infty}\,\biggr)\,d\theta\cr
+ &=\pi.&(11)\cr}
+}$$ % cf Joy of TeX
+\answer %
+|$$\eqalignno{\biggl(\int_{-\infty}^\infty e^{-x^2}\,dx\biggr)^2|\parbreak
+ | &=\int_{-\infty}^\infty\int_{-\infty}^\infty|\parbreak
+ | e^{-(x^2+y^2)}\,dx\,dy\cr|\parbreak
+ | &=\int_0^{2\pi}\int_0^\infty e^{-r^2}r\,dr\,d\theta\cr|\parbreak
+ | &=\int_0^{2\pi}\biggl(-{e^{-r^2}\over2}|\parbreak
+ | \bigg|\||_{r=0}^{r=\infty}\,\biggr)\,d\theta\cr|\parbreak
+ | &=\pi.&(11)\cr}$$| ^^|\bigg|
+
+\danger Although |\eqalign| and |\eqalignno| look nearly the same, there's
+really a fundamental distinction between them: |\eqalign| makes a single,
+vertically centered box, which is no wider than it needs to be; but
+|\eqalignno| generates a set of lines that have the full display width
+(reaching all the way to both margins). Thus, for example, you can use
+|\eqalign| several times in a display, but |\eqalignno| can appear only
+once. If you try to use ^|\eqno| in conjunction with |\eqalign|,
+you get a decent result, but if you try to use |\eqno| in connection
+with |\eqalignno| you'll get some sort of weird error message(s).
+
+\ddanger The definitions in Appendix~B reveal why |\eqalign| and |\eqalignno|
+behave differently: |\eqalign| is an
+abbreviation for ^|\vcenter||{|^|\halign||{...}}|, while
+|\eqalignno| is an abbreviation for |\halign to\displaywidth{...}|;
+thus the |\eqalignno| macro generates an ``^{alignment display}.''
+
+\ddanger This difference between |\eqalign| and |\eqalignno| has two
+interesting consequences. \ (1)~It's impossible to break an |\eqalign|
+between pages, but an |\eqalignno| can be broken. In fact, you can
+{\sl force\/} a ^{page break} after a particular line if you insert
+`^|\noalign||{|^|\break||}|'
+after the |\cr| for that line. You can prohibit {\sl all\/} breaks
+in an |\eqalignno| if you set ^|\interdisplaylinepenalty||=10000|; or you
+can enclose the whole works in a ^|\vbox|:
+\begintt
+$$\vbox{\eqalignno{...}}$$
+\endtt
+(2) You can also insert a line of text between two equations, without
+losing the alignment. For example, consider the two displays
+$$\eqalignno{x&=y+z\cr
+ \noalign{\hbox{and}}
+ x^2&=y^2+z^2.\cr}$$
+These were actually generated as a single display by typing
+\begintt
+$$\eqalignno{x&=y+z\cr
+ \noalign{\hbox{and}}
+ x^2&=y^2+z^2.\cr}$$
+\endtt
+Therefore the fact that their $=$ signs line up is not just a lucky
+coincidence. Sometimes you will want to adjust the spacing above or below
+such a line of inserted text, by putting a |\vskip| or two inside of the
+|\noalign{...}|. Incidentally, this example also shows that it is
+possible to use |\eqalignno| without giving any equation numbers.
+
+\ddangerexercise What happens if\/ |\eqalign| is substituted for
+|\eqalignno| in this last example?
+\answer You get the displayed box
+$$\eqalign{x&=y+z\cr
+ \noalign{\hbox{and}}
+ x^2&=y^2+z^2.\cr}$$
+Reason: The `and' occurs at the left of the |\eqalign| box, not at the
+left of the whole display, and the |\eqalign| box is centered as usual.
+
+\ddangerexercise Our friend Ben ^{User} got into trouble again when he tried to
+move an equation number up higher than its usual position, by typing this:
+^^|\raise|
+\begintt
+$$\eqalignno{...&\raise6pt\hbox{(5)}\cr}$$
+\endtt
+What was his oversight, and what could he have done instead?
+\answer By raising the equation number, he increased the line height,
+so \TeX\ put extra space between that line and the previous line
+when it calculated the inter-line glue. If he had said
+`^|\smash||{\raise...}|', he wouldn't have had that problem.
+
+\danger For other types of displays, plain \TeX\ provides ^|\displaylines|,
+which lets you display any number of formulas in any way you want,
+without any alignment. The general form is
+$$\halign{\indent\hfil#&#\hfil\cr
+|$$\displaylines{|&\<displayed formula$_1$>|\cr|\cr
+ &\<displayed formula$_2$>|\cr|\cr
+\noalign{\vskip-2pt}
+ &\qquad\vdots\cr
+ &\<displayed formula$_n$>|\cr}$$|\cr}$$
+Each formula will be centered, because |\displaylines| puts ^|\hfil| at
+the left and the right of each line; you can override this centering to
+get things flush left or flush right by inserting ^|\hfill|, which takes
+precedence over |\hfil|.
+
+\dangerexercise Use |\displaylines| to typeset the three-line display
+$$\displaylines{\hfill x\equiv x;\hfill\llap{(1)}\cr
+ \hfill\hbox{if}\quad x\equiv y\quad\hbox{then}\quad
+ y\equiv x;\hfill\llap{(2)}\cr
+ \hfill\hbox{if}\quad x\equiv y\quad\hbox{and}\quad
+ y\equiv z\quad\hbox{then}\quad
+ x\equiv z.\hfill\llap{(3)}\cr}$$
+\answer |$$\displaylines{\hfill x\equiv x;\hfill\llap{(1)}\cr|\parbreak
+ | \hfill\hbox{if}\quad x\equiv y\quad\hbox{then}\quad|\parbreak
+ | y\equiv x;\hfill\llap{(2)}\cr|\parbreak
+ | \hfill\hbox{if}\quad x\equiv y\quad\hbox{and}\quad|\parbreak
+ | y\equiv z\quad\hbox{then}\quad|\parbreak
+ | x\equiv z.\hfill\llap{(3)}\cr}$$|\par\medskip\noindent
+There's also a trickier solution, which begins with
+\begintt
+$$\displaylines{x\equiv x;\hfil\llap{(1)}\hfilneg\cr
+\endtt
+
+\danger If you look closely at the multi-line displays in this chapter,
+you'll see that the baselines are farther apart than they are in normal
+text; mathematics publishers generally do this in order to make the
+displays easier to read. In accordance with this tradition, |\eqalign| and
+its relatives automatically increase the ^|\baselineskip|. If~you are
+making a multi-line display with \TeX's primitive ^|\halign| command,
+instead of using one of the plain \TeX\ macros, you might want to make
+this same baseline adjustment, and you can do it easily by saying
+`|$$\openup1\jot \halign{...}$$|'. The ^|\openup| macro increases
+^|\lineskip| and ^|\lineskiplimit| as well as |\baselineskip|. If
+you say `|\openup2\jot|', the lines are spread apart 2 extra units, where
+plain \TeX\ opens things up in units of $3\pt$. Since |$$...$$| acts as a
+^{group}, the effect of\/ |\openup| will disappear when the display is
+finished. Any \<dimen> can follow |\openup|, but it's customary to express the
+amount symbolically in terms of a ^|\jot| instead of using absolute units;
+^^{generic coding}
+then your manuscript can be used with a variety of different formats.
+
+\ddanger Plain \TeX's ^|\displaylines|, ^|\eqalignno|, and ^|\leqalignno|
+macros begin with `|\openup1\jot|'. If you don't want the lines to be
+opened up, you can cancel this by saying, e.g.,
+`|$$\openup-1\jot \eqalignno{...}$$|', because |\openup| has a cumulative
+effect.
+
+\ddanger Suppose that you have decided to make a homegrown display having
+the general form `|$$\openup1\jot \halign{...}$$|'; and for convenience,
+let's suppose that the normal conventions of plain \TeX\ are in force, so
+that |\jot=3pt| and |\baselineskip=12pt|. Then the |\openup| macro
+changes the baselineskip distance to $15\pt$. It follows that the baseline
+of the text line that immediately precedes the display will be $15\pt$
+above the topmost baseline of the display, plus the ^|\abovedisplayskip|.
+But when the paragraph resumes, its next baseline will be only $12\pt$
+below the bottom baseline of the display, plus the ^|\belowdisplayskip|,
+because the |\baselineskip| parameter will have reverted to its normal
+value. The |\eqalignno| and |\displaylines| macros say
+`|\noalign{\vskip|$-d$|}|' before their first lines, where $d$ is
+the net amount of opening-up, in order to compensate for this difference.
+
+\subsection Long formulas. Our discussion of mathematics typing is almost
+complete; we need to deal with just one more problem: What should be
+done when a formula is so long that it doesn't fit on a single line?
+
+For example, suppose that you encounter the equation
+$$\hfuzz=20pt % overfull box tolerated here
+\sigma(2^{34}-1,2^{35},1)=
+ -3+(2^{34}-1)/2^{35}+2^{35}\!/(2^{34}-1)+7/2^{35}(2^{34}-1)
+ -\sigma(2^{35},2^{34}-1,1).$$ % from v2, 1st ed, p76
+You'll have to break it up somehow; \TeX\ has done its best to squeeze
+everything together by shrinking the spaces next to the $+$ and~$-$ signs
+to zero, but still the line has come out overfull.
+
+Let's try to break that equation just before the `$+7$'. One common way to
+do this is to type
+\begintt
+$$\eqalign{\sigma(2^{34}-1,2^{35},1)
+&=-3+(2^{34}-1)/2^{35}+2^{35}\!/(2^{34}-1)\cr
+&\qquad+7/2^{35}(2^{34}-1)-\sigma(2^{35},2^{34}-1,1).\cr}$$
+\endtt
+which yields
+$$\eqalign{\sigma(2^{34}-1,2^{35},1)
+ &=-3+(2^{34}-1)/2^{35}+2^{35}\!/(2^{34}-1)\cr
+ &\qquad+7/2^{35}(2^{34}-1)-\sigma(2^{35},2^{34}-1,1).\cr}$$
+The idea is to treat a long one-line formula as a two-line formula,
+using |\qquad| on the second line so that the second part of the formula
+appears well to the right of the $=$~sign on the first line.
+
+\exercise Explain how to deal with the following display. % v2 p107
+$$\eqalignno{x_nu_1+\cdots+x_{n+t-1}u_t
+ &=x_nu_1+(ax_n+c)u_2+\cdots\cr
+ &\qquad+\bigl(a^{t-1}x_n+c(a^{t-2}+\cdots+1)\bigr)u_t\cr
+ &=(u_1+au_2+\cdots+a^{t-1}u_t)x_n+h(u_1,\ldots,u_t).
+ \quad&(47)\cr}$$
+\answer |$$\eqalignno{x_nu_1+\cdots+x_{n+t-1}u_t|\parbreak
+ | &=x_nu_1+(ax_n+c)u_2+\cdots\cr|\parbreak
+ | &\qquad+\bigl(a^{t-1}x_n+c(a^{t-2}+\cdots+1)\bigr)u_t\cr|\parbreak
+ | &=(u_1+au_2+\cdots+a^{t-1}u_t)x_n+h(u_1,\ldots,u_t).|\parbreak
+ | \quad&(47)\cr}$$|\par\noindent
+You weren't expected to insert the `|\quad|' on the last line; such
+refinements usually can't be anticipated until you see the first proofs.
+But without that |\quad| the `(47)' would occur half a quad closer to the
+formula.
+
+\danger It's quite an art to decide how to ^{break long displayed formulas}
+into several lines; \TeX\ never attempts to break them, because no set of
+rules is really adequate. The author of a mathematical manuscript is
+generally the best judge of what to do, since break positions depend on
+subtle factors of mathematical exposition. For example, it is often
+desirable to emphasize some of the symmetry or other structure that
+underlies a formula, and such things require a solid understanding of
+exactly what is going on in that formula.
+
+\begingroup\ninepoint
+\danger Nevertheless, it is possible to state a few rules of thumb about
+how to deal with long formulas in displays, since there are some
+principles that the best mathematical typesetters tend to follow:\enddanger
+
+\smallskip
+\textindent{a)}Although formulas within a paragraph always break {\sl after\/}
+binary operations and relations, displayed formulas always break {\sl before\/}
+binary operations and relations. Thus, we didn't end the first line of
+our $\sigma(\,\ldots\,)$ example with `|(2^{34}-1)+|'; we ended it with
+`|(2^{34}-1)|' and began the second line with `|+|'.
+
+\smallskip
+\textindent{b)}When an equation is broken before a binary operation, the second
+line should start at least two quads to the right of where the innermost
+subformula containing that binary operation begins on the first line.
+For example, if you wish to break
+\begindisplay
+|$$\sum_{0<k<n}\left(|\<formula$_1$>|+|\<formula$_2$>|\right)$$|
+\enddisplay
+at the plus sign between \<formula$_1$> and \<formula$_2$>, it is almost
+mandatory to have the plus sign on the second line appear somewhat to the
+right of the large left parenthesis that corresponds to `|\left(|'.
+
+\endgroup
+
+\danger In the example just considered, special care is needed to break the
+formula into two lines, because ^|\left| and ^|\right| delimiters cannot be
+used in isolation; you can't have only |\left| in one line of a formula
+and only |\right| in the second. Furthermore, you'll want the two delimiters
+to be of the same size, even though they occur in different lines. The best
+solution is usually to choose the delimiter size yourself; for example,
+you could type
+\begindisplay
+|$$\eqalign{\sum_{0<k<n}\biggl(&|\<formula$_1$>|\cr|\cr
+| &\qquad{}+|\<formula$_2$>|\biggr)\cr}$$|\cr
+\enddisplay
+if\/ ^|\bigg| delimiters are best. Notice that the |&|~markers don't occur
+at $=$~signs in this example, they just mark a point of alignment.
+
+\danger There's another way to break long formulas, sometimes called the
+{\sl^{two-line}\/} form. The idea is to put the first part of the formula
+almost ^{flush left}, and to put the second part almost ^{flush right},
+where ``almost flush'' means ``one quad away.'' Thus, the two-line form of
+the long $\sigma(\,\ldots\,)$ equation considered earlier is
+$$\displaylines{\quad\sigma(2^{34}-1,2^{35},1)
+ =-3+(2^{34}-1)/2^{35}+2^{35}\!/(2^{34}-1)\hfill\cr
+\hfill{}+7/2^{35}(2^{34}-1)-\sigma(2^{35},2^{34}-1,1).\quad\cr}$$
+It isn't difficult to get this two-line effect with ^|\displaylines|:
+\begintt
+$$\displaylines{\quad\sigma(2^{34}-1,2^{35},1)
+ =-3+(2^{34}-1)/2^{35}+2^{35}\!/(2^{34}-1)\hfill\cr
+\hfill{}+7/2^{35}(2^{34}-1)-\sigma(2^{35},2^{34}-1,1).\quad\cr}$$
+\endtt
+An extra `|{}|' was typed on the second line here so that \TeX\ would know
+that the `|+|' is a binary operation. The two-line form is especially
+recommended for equations that have a long left-hand side; in that case the
+break generally comes just before the~$=$~sign.
+
+\dangerexercise Typeset the following display:
+$$\displaylines{\quad\sum_{1\le j\le n}{1\over
+ (x_j-x_1)\ldots(x_j-x_{j-1})(x-x_j)(x_j-x_{j+1})
+ \ldots(x_j-x_n)}\hfill\cr
+ \hfill={1\over(x-x_1)\ldots(x-x_n)}.\quad(27)\cr}$$ % v2 p80
+\answer |$$\displaylines{\quad\sum_{1\le j\le n}{1\over|\parbreak
+ | (x_j-x_1)\ldots(x_j-x_{j-1})(x-x_j)(x_j-x_{j+1})|\parbreak
+ | \ldots(x_j-x_n)}\hfill\cr|\parbreak
+ | \hfill={1\over(x-x_1)\ldots(x-x_n)}.\quad(27)\cr}$$|
+
+\ddangerexercise If it is necessary to typeset a huge fraction like
+^^{fraction, huge}
+$$\def\\#1;{(#1;q^2)_\infty}
+q^{{1\over2}n(n+1)}\\ea;\\eq/a;\\caq/e;\\cq^2\!/ae;
+\over(e;q)_\infty(cq/e;q)_\infty$$
+in a single narrow column, you might have to break up the numerator and
+resort to
+\begindisplay
+$\displaystyle{\def\\#1;{(#1;q^2)_\infty}
+\displaystyle{q^{{1\over2}n(n+1)}\\ea;\\eq/a;\qquad\atop
+ \hfill\\caq/e;\\cq^2\!/ae;}
+\over(e;q)_\infty(cq/e;q)_\infty}$
+\enddisplay
+How would you specify the latter fraction to \TeX?
+% cf SIAM J Math Anal 7 (1976) p333; even longer ones appear on p334
+\answer |$$\def\\#1;{(#1;q^2)_\infty} % to save typing|\parbreak
+ |\displaystyle{q^{{1\over2}n(n+1)}\\ea;\\eq/a;\qquad\atop|\parbreak
+ | \hfill\\caq/e;\\cq^2\!/ae;}|\parbreak
+ |\over(e;q)_\infty(cq/e;q)_\infty$$|
+
+\endchapter
+
+When a formula is too long for the page-width
+and has to be broken into successive lines
+(and we are now, of course, speaking of displayed formulae),
+it should be broken, if possible, at the end of a natural `phrase';
+if, for example, it is a much-bracketed formula,
+it should be broken at the end of one of the major brackets
+and not at an inner symbol.
+This natural phrasing (as in music or speech)
+makes for intelligibility between writer and reader
+and should not be left to the compositor.
+An author, when he finds himself writing a longish formula,
+should indicate a convenient point of fracture in case of need.
+\author ^{CHAUNDY}, ^{BARRETT}, and ^{BATEY}, %
+ {\sl The Printing of Mathematics\/} (1954) % p38
+
+\bigskip
+
+Some authors use display with discretion,
+some run even extremely long, complicated equations into the text,
+while others tend to display every equation in the paper.
+The tendency to overdisplay is probably more predominant
+than the tendency to underdisplay;
+for this reason it is possible for the copy editor to shorten
+(and even improve) papers by running displayed material into text. $\ldots$
+On the other hand, there are occasions when the copy editor needs
+to suggest the display of complicated expressions that have been run into text,
+particularly when it would involve a bad break at the end of a text line.
+\author ELLEN ^{SWANSON}, {\sl Mathematics into Type\/} (1971) % p41
+
+\eject
+ \beginchapter Chapter 20. Definitions\\(also called Macros)
+
+You can often save time typing math formulas by letting ^{control sequences}
+stand for constructions that occur frequently in a particular manuscript.
+For example, if some document uses the vector `$(x_1,\ldots,x_n)$' a lot,
+you can type
+\begintt
+\def\xvec{(x_1,\ldots,x_n)}
+\endtt
+and |\xvec| will henceforth be an abbreviation for `|(x_1,\ldots,x_n)|'.
+Complex displays \rlap{like}
+$$\def\xvec{(x_1,\ldots,x_n)}
+\sum_{\xvec\ne(0,\ldots,0)} \bigl(f\xvec+g\xvec\bigr)$$
+can then be typed simply as
+\begintt
+$$\sum_{\xvec\ne(0,\ldots,0)} \bigl(f\xvec+g\xvec\bigr)$$
+\endtt
+instead of in a tedious long form. By ^{defining a control sequence}
+like |\xvec|, you not only cut down on the number of keystrokes that you need
+to make, you also reduce your chances of introducing typographical errors
+and inconsistencies. ^^{abbreviations, see macros}
+
+Of course, you usually won't be making a definition just to speed up the
+typing of one isolated formula; that doesn't gain anything, because time goes by
+when you're deciding whether or not to make a definition, and when you're
+typing the definition itself. The real payoff comes when some cluster of
+symbols is used dozens of times throughout a manuscript. A wise typist will
+look through a document before typing anything, thereby getting a feeling
+for what sorts of problems will arise and what sorts of definitions will
+be helpful. For example, Chapter~16 recommends that the control sequence
+|\Ahat| be defined at the beginning of any manuscript that makes frequent
+use of the symbol~$\hat A$.
+
+Abbreviations like |\xvec| turn out to be useful in many applications of
+computers, and they have come to be known as {\sl^{macros}\/} because they
+are so powerful; one little macro can represent an enormous amount of
+material, so it has a sort of macroscopic effect. System programs like
+\TeX\ that are designed to deal with macro definitions are said to {\sl
+expand\/} the user's macros; for example, |\xvec| expands into
+|(x_1,\ldots,x_n)|, and ^|\ldots| in turn is a macro that expands into
+|\mathinner{\ldotp\ldotp\ldotp}|. Thus, |\xvec| is actually an
+abbreviation for `|(x_1,\mathinner{\ldotp\ldotp\ldotp},x_n)|'. \ (The
+expansion stops here, because ^|\mathinner| is a primitive control
+sequence of \TeX, and because |\ldotp| has been defined with
+^|\mathchardef|; thus |\mathinner| and |\ldotp| are not macros.)
+
+\TeX\ users generally build up their own personal ^{library of macros} for
+things that they want to do in different documents. For example, it is common
+to have a file called |macros.tex| that contains definitions of your favorite
+special control sequences, perhaps together with commands that load your
+favorite special fonts, etc. If you begin a document with the command
+\begintt
+\input macros
+\endtt
+then \TeX\ will read all those definitions, ^^|\input|
+saving you all the trouble of retyping them. Of course, \TeX's memory is
+limited, and it takes time to read a file, so you shouldn't put thousands
+of definitions into |macros.tex|. A large collection of macro definitions
+(e.g., the set of definitions in Appendix~B) is called a {\sl^{format}\/}
+(e.g., ``plain \TeX\ format''); \TeX\ has a special way to input
+a format at high speed, assuming that the format doesn't change very often.
+
+The |\xvec| and |\Ahat| examples apply to math formulas, but you can make
+good use of macro definitions even when you aren't doing any math at all.
+For example, if you are using \TeX\ for ^{business correspondence},
+you can have a |\yours| macro that stands for `Sincerely yours, A.~U.
+^{Thor}'. If you often write ^{form letters} you can have macros that
+generate entire sentences or paragraphs or groups of paragraphs.
+The ^{Internal Revenue Service} could, for example, make use of the
+following two macros:
+\begintt
+\def\badcheck{A penalty has been added because your
+ check to us was not honored by your bank.\par}
+\def\cheater{A penalty of 50\% of the underpaid tax
+ has been added for fraud.\par}
+\endtt
+Simple macro definitions, like these, start with `|\def|'; then comes the
+control sequence name, e.g., `|\badcheck|'; and then comes the replacement
+text enclosed in `|{|' and~`|}|'. The ^{braces} do not represent
+^{grouping} in this case; they simply show the extent of the replacement
+text in the definition. You could, of course, define a macro that
+includes actual braces in its replacement text, as long as those braces
+match each other properly. For example, `|\def\xbold{{\bf x}}|'
+makes |\xbold| an abbreviation for `|{\bf x}|'.
+
+\exercise
+Write a |\punishment| macro that prints 100 lines containing the message
+`I must not talk in class.' \ [{\sl Hint:\/} First write a macro |\mustnt|
+that prints the message once; then write a macro |\five| that prints it
+five times.] \checkequals\punishexno\exno
+\answer |\def\mustnt{I must not talk in class.\par}|\parbreak
+ |\def\five{\mustnt\mustnt\mustnt\mustnt\mustnt}|\parbreak
+ |\def\twenty{\five\five\five\five}|\parbreak
+ |\def\punishment{\twenty\twenty\twenty\twenty\twenty}|\par
+\smallskip\noindent Solutions to more complicated problems of this type are
+discussed later.
+
+\dangerexercise What is the expansion of\/ |\puzzle|, given the following
+definitions?
+\begintt
+\def\a{\b}
+\def\b{A\def\a{B\def\a{C\def\a{\b}}}}
+\def\puzzle{\a\a\a\a\a}
+\endtt
+\answer |ABCAB|. \ (The first |\a| expands into |A\def\a{B...}|; this redefines
+|\a|, so the second |\a| expands into |B...|, etc.) \ At least, that's what
+happens if\/ |\puzzle| is encountered when \TeX\ is building a list. But if
+|\puzzle| is expanded in an ^|\edef| or ^|\message| or something like that,
+we will see later that the interior |\def| commands are not performed
+while the expansion is taking place, and the control sequences following
+|\def| are expanded; so the result is an infinite string
+\begintt
+A\def A\def A\def A\def A\def A\def A\def A\def A...
+\endtt
+which causes \TeX\ to abort because the program's input stack is finite.
+This example points out that a control sequence (e.g., |\b|) need not be
+defined when it appears in the replacement text of a definition. The example
+also shows that \TeX\ doesn't expand a macro until it needs to.
+
+\danger As soon as you get the hang of simple macros like those illustrated
+above, you will probably begin to think, ``Boy, wouldn't it be nice if I could
+have a macro in which some of the text in the expansion is changeable? I'd like
+to be able to stick different things into the middle of that text.'' Well,
+\TeX\ has good news for you: Control sequences can be defined in terms of
+{\sl^{parameters}}, and you can supply {\sl^{arguments}\/} that will be
+substituted for the parameters.
+
+\danger For example, let's consider |\xvec| again. Suppose that you not only
+refer to `$(x_1,\ldots,x_n)$', but you also make frequent use of
+`$(y_1,\ldots,y_n)$' and other similar things. Then you might want to type
+\begintt
+\def\row#1{(#1_1,\ldots,#1_n)}
+\endtt
+\def\row#1{(#1_1,\ldots,#1_n)}%
+after which |\row x| will produce `$\row x$' and |\row y| will produce
+`$\row y$'. The symbol |#1| ^^{sharpsign} stands for the first parameter to
+the macro, and when you say `|\row|~|x|' the |x| is a so-called argument that
+will be inserted in place of the |#1|'s in the replacement text. In this
+case the argument consists of a single letter, |x|. You can also say
+|\row\alpha|, in which case the argument will be the control sequence
+^|\alpha|, and the result will be `$\row\alpha$'. If you want the argument
+to contain more than one symbol or control sequence, you can simply enclose
+it in ^{braces}; for example, |\row{x'}| yields $\row{x'}$. The argument
+in this case is |x'| (without the braces). Incidentally, if you say
+|\row{{x'}}|, you get $\row{{x'}}$; the reason is that only one pair of
+braces is stripped off when the argument is collected, and
+$({x'}_1,\ldots,{x'}_n)$ is what you get from
+|({x'}_1,\ldots,{x'}_n)| in math mode, according to the rules
+of Chapter~16. ^^{apostrophe}
+
+\dangerexercise Continuing this example, what is the result of
+|$\row{\bf x}$|?
+\answer \def\row#1{(#1_1,\ldots,#1_n)}$\row{\bf x}$. Note that the
+subscripts are bold here, because the expansion |(\bf x_1,\ldots,\bf x_n)|
+doesn't ``turn off'' ^|\bf|. To prevent this, one should write
+|\row{{\bf x}}|; or (better), |\row\xbold|, in conjunction with
+|\def\xbold{{\bf x}}|.
+
+\danger The notation `|#1|' suggests that there might be an opportunity to have
+more than one parameter, and indeed there is. You can write, for example,
+\begintt
+\def\row#1#2{(#1_1,\ldots,#1_#2)}
+\endtt
+\def\row#1#2{(#1_1,\ldots,#1_#2)}%
+after which `|\row xn|' would be the proper protocol for `$\row xn$'. There can
+be as many as nine parameters, |#1| to~|#9|, and when you use them you must
+number them~in order. For example, you can't use |#5| in a definition unless
+the previous parameter in~that definition was called |#4|. \ (This restriction
+applies only to the initial statement of parameters, before the replacement
+text starts; the stated parameters can be used any number of times, in any
+order, in the replacement text itself.)
+
+\danger A control sequence has only one definition at a
+time, so the second definition of\/ |\row| would supersede the first one if
+both had appeared in the same document. Whenever \TeX\ encounters a macro
+that it wants to expand, it uses the most recent definition. However,
+definitions are ^{local} to the group that contains them; old definitions
+will be restored in the usual way when a ^{group} ends.
+
+\danger Caution: When you define a macro with simple parameters, as in
+these examples, you must be careful not to put blank spaces before the
+`|{|' that begins the replacement text. For example, `|\def\row #1 #2 {...}|'
+will not give the same result as `|\def\row#1#2{...}|', because the spaces
+after |#1| and~|#2| tell \TeX\ to look for arguments that are followed by
+spaces. \ (Arguments can be ``delimited'' in a fairly general way, as
+explained below.) \ But the space after |\row| is optional, as usual,
+because \TeX\ always disregards spaces after control words. After
+you have said `|\def\row#1#2{...}|', you are allowed to put spaces
+between the arguments (e.g., `|\row x n|'), because \TeX\ doesn't
+use single spaces as undelimited arguments.
+
+\danger The following exercise is particularly recommended for people who
+want to learn to write \TeX\ macros. Even if you have gotten into the
+dangerous habit of skimming other exercises, you should try your hand
+at this one.
+
+\dangerexercise Extending exercise 20.\punishexno, write a ``generalized
+punishment'' macro that has two parameters, so that |\punishment{run}{the
+halls}| will produce 100 paragraphs that say `I~must not run in the halls.'
+\answer The catch is that the parameters have to percolate down to the
+|\mustnt| macro, if you extend the previous answer:
+\begintt
+\def\mustnt#1#2{I must not #1 in #2.\par}
+\def\five#1#2{\mustnt{#1}{#2}...\mustnt{#1}{#2}}
+\def\twenty#1#2{\five{#1}{#2}...\five{#1}{#2}}
+\def\punishment#1#2{\twenty{#1}{#2}...\twenty{#1}{#2}}
+\endtt
+When you pass parameters from one macro to another in this way, you need to
+enclose them in braces as shown. But actually this particular solution
+punishes \TeX\ much more than it needs to, because it takes a lot of
+time to copy the parameters and read them again and again. There's a
+much more efficient way to do the job, by defining control sequences:
+\begintt
+\def\mustnt{I must not \doit\ in \thatplace.\par}
+\def\punishment#1#2{\def\doit{#1}\def\thatplace{#2}%
+ \twenty\twenty\twenty\twenty\twenty}
+\endtt
+and by defining |\five| and |\twenty| without parameters as before.
+You can also delve more deeply into \TeX nicalities, constructing solutions
+that are more efficient yet; \TeX\ works even faster when macros
+communicate with each other via ^{boxes}.
+^^{efficient macros} ^^{communication between macros}
+For example,
+\begintt
+\def\mustnt{\copy0 }
+\def\punishment#1#2{\setbox0=
+ \vbox{\strut I must not #1 in #2.\strut}%
+ \twenty\twenty\twenty\twenty\twenty}
+\endtt
+sets 100 identical paragraphs at high speed, because \TeX\ has to
+process the paragraph and break it into lines only once. It's much faster
+to ^{copy a box} than to build it up from scratch. \ (The ^{struts} in
+this example keep the interbaseline distances correct between boxed
+paragraphs, as explained in Chapter~12. Two struts are used, for if the
+message takes more than one line there will be a strut at both top
+and bottom. If it were known that each sentence will occupy only a single
+line, no struts would be needed, because interline glue is added as
+usual when a box created by |\copy| is appended to the current vertical list.)
+
+\ninepoint % the rest of this chapter is all dangerous
+\ddanger \TeX\ also allows you to define macros whose parameters are delimited
+in quite a general way; you needn't always enclose arguments in braces.
+For example,
+\begintt
+\def\cs #1. #2\par{...}
+\endtt
+defines a control sequence |\cs| with two parameters, and its two arguments
+will be determined as follows: |#1| will consist of all tokens between
+|\cs| and the next subsequent appearance of `|.|\]' (period and space);
+|#2| will consist of all tokens between that `|.|\]' and the next
+|\par| token. \ (The ^|\par| might be given explicitly, or it might be
+generated by a blank line as explained in Chapter~8.) \ For example, when
+\TeX\ expands
+\begintt
+\cs You owe \$5.00. Pay it.\par
+\endtt
+the first argument is `|You owe \$5.00|' and the second is `|Pay it.|'.
+The period in `|\$5.00|' doesn't stop |#1|, in this example,
+because \TeX\ keeps going until finding a period that is followed immediately
+by a space.
+
+\ddanger Furthermore, an argument will not stop when its delimiter is enclosed
+in braces, because that would produce unbalanced braces. For example, in
+\begintt
+\def\cs #1.#2\par{...}
+\endtt
+the first argument is now delimited by a single period, so |#1| would be
+`|You owe \$5|' and the |#2| would be `|00. Pay it.|' if\/ |\cs| were
+invoked as above. But
+\begintt
+\cs You owe {\$5.00}. Pay it.\par
+\endtt
+satisfactorily hides the first period, making it part of argument |#1|,
+which becomes \hbox{`|You owe {\$5.00}|'}.
+
+\ddanger If you are designing a format for mathematical papers, you will
+probably want to include a macro for the statement of ^{theorems},
+definitions, lemmas, corollaries, and such things. For example, you might
+want to typeset a statement like\enddanger
+
+\proclaim Theorem 1. \TeX\ has a powerful macro capability.\par
+\noindent from the input
+\begintt
+\proclaim Theorem 1. \TeX\ has a powerful macro capability.\par
+\endtt
+In fact, plain \TeX\ includes a ^|\proclaim| macro that does just that;
+its definition is
+\begintt
+\def\proclaim #1. #2\par{\medbreak
+ \noindent{\bf#1.\enspace}{\sl#2}\par\medbreak}
+\endtt
+^^{enunciations, see proclaim} ^^{enspace}
+so the arguments are delimited exactly as in our first |\cs| example. The
+replacement text here uses |\medbreak| to separate the proclaimed
+paragraph from what precedes and follows; the title of the proclamation is
+set in bold face type, while the text itself is set slanted. \ (The
+actual definition of\/ |\proclaim| in Appendix~B is not quite the same as
+this; the final |\medbreak| has been modified so that a break between
+pages will be discouraged immediately following the statement of a
+theorem. Hence a short theorem will tend to appear at the top of a page
+rather than at the bottom.)
+
+\ddanger By making changes to the |\proclaim| macro, you can change the
+format of all the proclamations in your paper, without changing the text
+of the paper itself. For example, you could produce something like\enddanger
+
+\medbreak
+\font\tencsc=cmcsc10
+\noindent {\tencsc Theorem 1:}\enspace
+{\it\TeX\ has a powerful macro capability.}
+\goodbreak\medbreak\noindent
+by making simple alterations to the replacement text of\/ |\proclaim|,
+assuming that you have a ``^{caps and small caps}'' font. \TeX\ is
+intended to support ^{higher-level languages for composition} in which all of
+the control sequences that a user actually types are macros rather than
+\TeX\ primitives. The ideal is to be able to describe important classes of
+documents in terms of their components, without mentioning actual fonts or
+point sizes or details of spacing; a single ^{style-independent document}
+^^{format-independent document}^^{generic coding}
+can then be set in many different styles.
+
+\ddanger Now that we have seen a number of examples, let's look at the
+precise rules that govern \TeX\ macros. Definitions have the general form
+\begindisplay
+|\def|\<control sequence>\<parameter text>|{|\<replacement text>|}|
+\enddisplay
+where the \<parameter text> contains no ^{braces}, and where all occurrences
+of |{| and |}| in the \<replacement text> are properly nested. Furthermore
+the |#| symbol has a special significance: In the \<parameter text>, the
+first appearance of |#| must be followed by~|1|, the next by~|2|, and
+so on; up to nine |#|'s are allowed. In the \<replacement text> each~|#|
+must be followed by a digit that appeared after~|#| in the \<parameter text>,
+or else the~|#| should be followed by another~|#|. The latter case stands
+for a single~|#| token when the macro is expanded; the former case stands
+for insertion of the corresponding argument.
+
+\ddanger For example, let's consider a ``random'' definition that doesn't
+do anything useful except that it does exhibit \TeX's rules. The definition
+\begintt
+\def\cs AB#1#2C$#3\$ {#3{ab#1}#1 c##\x #2}
+\endtt
+says that the control sequence |\cs| is to have a parameter text consisting of
+nine tokens
+\begindisplay
+|A|$_{11}$, \ |B|$_{11}$, \ |#1|, \ |#2|, \ |C|$_{11}$, \
+ |$|$_3$, \ |#3|, \ \cstok{\char`$}, \ \]$_{10}$
+\enddisplay
+(assuming the ^{category codes} of plain \TeX), and a replacement text
+of twelve tokens
+\begindisplay
+|#3|, \ |{|$_1$, \ |a|$_{11}$, \ |b|$_{11}$, \ |#1|, \
+ |}|$_2$, \ |#1|, \ \]$_{10}$, \ |c|$_{11}$, \ |#|$_6$, \ \cstok{x}, \ |#2|.
+\enddisplay
+Henceforth when \TeX\ reads the control sequence |\cs| it will expect that
+the next two tokens will be |A|$_{11}$ and |B|$_{11}$ (otherwise you will
+get the error message `|Use| |of| |\cs| |doesn't| |match| |its|
+|definition|'); then comes argument~|#1|, followed by argument~|#2|,
+then~|C|$_{11}$, then~|$|$_3$, then argument~|#3|, then |\$|, and finally
+a space token. It is customary to use the word ``argument'' to mean the
+string of tokens that gets substituted for a parameter; parameters appear
+in a definition, and arguments appear when that definition is used. \ (For
+the purposes of these rules, we are extending Chapter~7's definition of
+^{token}: In addition to control sequences and (character code, category
+code) pairs, \TeX\ also recognizes ``^{parameter tokens},'' denoted here
+by |#1|~to~|#9|. Parameter tokens can appear only in token lists for macros.)
+
+\ddanger How does \TeX\ determine where an argument stops, you ask.
+Answer: There are two cases. A {\sl^{delimited parameter}\/} is followed
+in the \<parameter text> by one or more non-parameter tokens, before
+reaching the end of the parameter text or the next parameter token;
+in this case the corresponding argument is the shortest (possibly empty)
+sequence of tokens with properly nested |{...}| groups that is followed in
+the input by this particular list of non-parameter tokens. \ (Category
+codes and character codes must both match, and control sequence names
+must be the same.) \ An {\sl^{undelimited parameter}\/} is followed immediately
+in the \<parameter text> by a parameter token, or it occurs at the very end
+of the parameter text; in this case the corresponding argument is the
+next nonblank token, unless that token is `|{|', when the argument will
+be the entire |{...}| group that follows. In both cases, if the argument
+found in this way has the form `|{|\<nested tokens>|}|', where \<nested
+tokens> stands for any sequence of tokens that is properly nested with
+respect to braces, the outermost braces enclosing the argument are removed
+and the \<nested tokens> will remain. For example, let's continue with
+|\cs| as defined above and suppose that the subsequent text contains
+\begintt
+\cs AB {\Look}C${And\$ }{look}\$ 5.
+\endtt
+Argument |#1| will be the token \cstok{Look}, since |#1| is an undelimited
+parameter (it is followed immediately by~|#2| in the definition);
+in this case \TeX\ ignores the blank space after |B|, and strips the
+braces off~of |{\Look}|. Argument~|#2| will be empty, since |C$| follows
+immediately. And argument~|#3| will be the thirteen tokens corresponding
+to the text |{And\$|\]|}{look}|, because |#3| is to be followed by `|\$|\]',
+and because the first occurrence of `|\$|\]' is within braces. Even though
+argument~|#3| begins with a left brace and ends with a right brace, the
+braces are not removed, since that would leave the unnested tokens `|And\$
+}{look|'. The net effect then, after substituting arguments for
+parameters in the replacement text, will be that \TeX\ will next read the
+token list
+\begintt
+{And\$ }{look}{ab\Look}\Look|]c#\x5.
+\endtt
+The space \] here will be part of the resulting token list, even though
+it follows the control word |\Look|, because ^{spaces} are removed
+after ^{control word} tokens only when \TeX\ first converts input lines to
+token lists as described in Chapter~8.
+
+\ddangerexercise The example definition of\/ |\cs| includes a |##| in
+its replacement text, but the way |##| is actually used in that example
+is rather pointless. Give an example of a definition where |##| serves
+a useful purpose. ^^{sharp sharp}
+\answer The |##| feature is indispensable when the replacement text of
+a definition contains other definitions. For example, consider
+\begintt
+\def\a#1{\def\b##1{##1#1}}
+\endtt
+after which `|\a!|'\ will expand to `|\def\b#1{#1!}|'. We will see later
+that |##| is also important for alignments; see, for example, the definition
+of\/ |\matrix| in Appendix~B.
+
+\ddanger A special extension is allowed to these rules: If the very
+last character of the \<parameter text> is~|#|, so that this~|#| is
+immediately followed by~|{|, \TeX\ will behave as if the~|{| had been
+inserted at the right end of both the parameter text and the replacement text.
+For example, if you say `|\def\a#1#{\hbox to #1}|', the
+subsequent text `|\a3pt{x}|' will expand to `|\hbox to 3pt{x}|', because
+the argument of\/ |\a| is delimited by a left brace.
+^^{dimensions as arguments}
+
+\ddanger Tokens that precede the first parameter token in the \<parameter
+text> of a definition are required to follow the control sequence; in
+effect, they become part of the control sequence name. For example, the author
+might have said
+\begintt
+\def\TeX/{...}
+\endtt
+instead of defining ^|\TeX| without the slash. Then it would be necessary
+to type |\TeX/| each time the \TeX\ logo is desired, but the new definition
+would have the advantage that spaces are {\sl not\/} ignored after
+`|\TeX/|'. You can use this idea to define macros that are intended to
+be used in sentences, so that users don't have to worry about the possible
+disappearance of ^{spaces}.
+
+\ddangerexercise Define a control sequence |\a| such that |\a{...}| expands
+to |\b{...}|, and such that \TeX\ gives an error message if\/ |\a| is not
+immediately followed by a left brace.
+\answer |\def\a#{\b}|.
+
+\ddanger Complicated macros have a habit of behaving differently from what
+you expect, when you first define them, even though \TeX's rules are
+not especially complicated. If you have trouble understanding why some
+|\def| doesn't work the way you think it should, help is available:
+You can set |\tracingmacros=1|, whereupon \TeX\ will write something in
+your log file whenever it expands a macro, and whenever it has read a
+macro argument. For example, if\/ ^|\tracingmacros| is positive when
+\TeX\ processes the |\cs| example above, it will put the following
+four lines into the log: ^^{debugging macros}
+\begintt
+\cs AB#1#2C$#3\$ ->#3{ab#1}#1 c##\x #2
+#1<-\Look
+#2<-
+#3<-{And\$ }{look}
+\endtt
+
+\ddanger In all of the rules stated above, `|{|' and `|}|' and `|#|' stand
+for any characters whose ^{category codes} are respectively 1, 2, and 6 in
+the token list when \TeX\ reads the macro definition; there's nothing
+sacred about the particular symbols that plain \TeX\ uses to denote
+grouping and parameters. You can even make use of several different
+characters with these category codes, all at the same time.
+
+\ddangerexercise Suppose that `|[|', `|]|', and `|!|'\ have the respective
+catcodes 1,~2, and~6, as do `|{|',~`|}|', and~`|#|'. See if you can guess
+what the following definition means:
+\begintt
+\def\!!1#2![{!#]#!!2}
+\endtt
+What token list will result when `|\! x{[y]][z}|' is expanded?
+\answer Let's go slowly on this one, so that the answer will give enough
+background to answer all similar questions. The \<parameter text> of the
+definition consists of the three tokens |#1|, |#2|, |[|$_1$; the
+\<replacement text> consists of the six tokens |{|$_1$, |#|$_6$, |]|$_2$,
+|!|$_6$, |#2|, |[|$_1$. \ (When two tokens of category~6 occur in the
+replacement text, the character code of the second one survives; the
+character code of a category-6 character is otherwise irrelevant. Thus,
+`|\def\!#1!2#[{##]!!#2]|' would produce an essentially identical
+definition.) \ When expanding the given token list, argument~|#1| is
+|x|$_{11}$, since it is undelimited. Argument~|#2| is delimited by~|[|$_1$,
+which is different from~|{|$_1$, so it is set provisionally to |{[y]]|;
+but the outer ``braces'' are stripped off, so |#2|~reduces to the
+three tokens |[|$_1$, |y|$_{11}$,~|]|$_2$. The result of the expansion
+is therefore
+\begindisplay
+|{|$_1$ |#|$_6$ |]|$_2$ |!|$_6$ |[|$_1$ |y|$_{11}$ |]|$_2$
+ |[|$_1$ |z|$_{11}$ |}|$_2$.
+\enddisplay
+Incidentally, if you display this with ^|\tracingmacros||=1|, \TeX\ says
+\begintt
+\!!1#2[->{##]!!#2[
+#1<-x
+#2<-[y]
+\endtt
+Category codes are not shown, but a character of category~6 always
+appears twice in succession. A parameter token in the replacement text
+uses the character code of the final parameter in the parameter text.
+^^{token lists, as displayed by TeX}
+
+\ddanger In practice, we all make mistakes. And one of the most common
+typographic errors is to forget a~`|}|', or to insert an extra~`|{|',
+somewhere in an argument to a macro. If \TeX\ were to follow the rules
+blindly in such a case, it would have to keep absorbing more and
+more tokens in hopes of finding the end of the argument. But a mistyped
+argument is unending, like so many arguments in real life (sigh); so
+\TeX\ would have to go on until the end of the file, or (more likely)
+until tokens completely fill the computer's memory. In either case, a
+single typographical error would have ruined the run, and the user would
+be forced to start over. Therefore \TeX\ has another rule, intended
+to confine such errors to the paragraph in which they occur: {\sl The
+token `\thinspace^|\par|' is not allowed to occur as part of an argument},
+unless you explicitly tell \TeX\ that |\par| is OK. Whenever \TeX\ is
+about to include |\par| as part of an argument, it will abort the current
+macro expansion and report that a ``^{runaway} argument'' has been found.
+
+\ddanger If you actually want a control sequence to allow arguments with
+|\par| tokens, you can define it to be a ``long'' macro by saying
+`^|\long|' just before `|\def|'. For example, the |\bold| macro
+defined by
+\begintt
+\long\def\bold#1{{\bf#1}}
+\endtt
+is capable of setting several paragraphs in boldface type. \ (However,
+such a macro is not an especially good way to typeset bold text.
+It would be better to say, e.g.,
+\begintt
+\def\beginbold{\begingroup\bf}
+\def\endbold{\endgroup}
+\endtt
+because this doesn't fill \TeX's memory with a long argument.)
+
+\ddanger The |\par|-forbidding mechanism doesn't catch all conceivable
+missing-brace errors, however; you might forget the |}| at the end of a
+|\def|, and the same problem would arise. In this case it's harder to
+confine the error, because |\par| is a useful thing in replacement
+texts; we wouldn't want to forbid |\par| there, so \TeX\ has another
+mechanism: When a macro definition is preceded by `^|\outer|', the
+corresponding control sequence will not be allowed to appear in any place
+where tokens are being absorbed at high speed. ^^{forbidden control sequence}
+An |\outer| macro cannot appear in an argument (not even when |\par| is
+allowed), nor can it appear in the parameter text or the replacement text
+of a definition, nor in the ^{preamble} to an alignment, nor in ^{conditional
+text} that is being skipped over. If an |\outer| macro does show up in
+such places, \TeX\ stops what it is doing and reports either a
+``runaway'' situation or an ``^{incomplete}'' conditional. The ^{end of an
+input file} or alignment template ^^{endtemplate}
+is also considered to be |\outer| in this sense; for example, a
+file shouldn't end in the middle of a definition. If you are designing a
+format for others to use, you can help them detect errors before too much
+harm is done, by using |\outer| with all control sequences that should
+appear only at ``quiet times'' within a document. For example, Appendix~B
+defines ^|\proclaim| to be |\outer|, since a user shouldn't be stating a
+theorem as part of a definition or argument or preamble.
+
+\ddanger We have now seen that |\def| can be preceded by |\long|
+or~|\outer|, and it can also be preceded by ^|\global| if the definition
+is supposed to transcend its group. These three prefixes can be applied
+to |\def| in any order, and they can even appear more than once.
+\TeX\ also has a ^|\gdef| primitive that is equivalent to |\global\def|.
+Thus, for example,
+\begintt
+\long\outer\global\long\def
+\endtt
+means the same thing as `|\outer\long\gdef|'.
+
+\ddanger So far in this manual we have encountered several ways to
+assign a meaning to a control sequence. For example,
+$$\halign{\indent#\hfil\quad&#\hfil\cr
+|\font\cs=|\<external font name>&makes |\cs| a font identifier;\cr
+|\chardef\cs=|\<number>&makes |\cs| a character code;\cr
+|\countdef\cs=|\<number>&makes |\cs| a |\count| register;\cr
+|\def\cs...{...}|&makes |\cs| a macro.\cr
+\noalign{\medskip \hbox{It's time now to reveal another important command
+ of this type:} \medskip}
+|\let\cs=|\<token>&gives |\cs| the token's current meaning.\cr}$$
+^^|\let|
+If the \<token> is another control sequence, |\cs| will acquire the
+same significance as that control sequence. For example, if you say
+`|\let\a=\def|', you could then say `|\a\b...{...}|' to define a macro~|\b|,
+because |\a| would behave like \TeX's primitive |\def| command.
+If you say
+\begintt
+\let\a=\b \let\b=\c \let\c=\a
+\endtt
+you have interchanged the former meanings of\/ |\b| and |\c|. And if you say
+\begintt
+\outer\def\a#1.{#1:}
+\let\b=\a
+\endtt
+the effect is exactly the same as `|\outer\def\b#1.{#1:} \let\a=\b|'.
+
+\ddanger If the \<token> in a |\let| is a single character---i.e.,
+if it is a (character code, category code) pair---then
+the control sequence will behave to a certain extent like that
+character; but there are some differences. For example, after
+`|\let\zero=0|' you can't use |\zero| in a numerical constant, because
+\TeX\ requires the tokens in a numerical constant to be digits, after
+macro expansion; |\zero| is not a macro, so it doesn't expand. However,
+such uses of\/ |\let| have their value, as we will see later.
+
+\ddangerexercise Is there a significant difference between `|\let\a=\b|'
+and `|\def\a{\b}|'?
+\answer Yes indeed. In the first case, |\a| receives the meaning of\/~|\b|
+that is current at the time of the |\let|. In the second case, |\a|~becomes
+a~macro that will expand into the token~|\b| whenever |\a|~is used,
+so it has the meaning of\/~|\b| that is current at the time of use.
+You need |\let|, if you want to interchange the meanings of\/ |\a| and~|\b|.
+
+\ddangerexercise Experiment with \TeX\ to discover the answers to the
+following questions: (a)~If the control sequence ^|\par| has been redefined
+(e.g., `|\def\par{\endgroup\par}|'\thinspace), is |\par| still forbidden
+to appear in an argument? \ (b)~If you say |\let\xpar=\par|, is |\xpar|
+also forbidden in an argument?
+\answer (a) Yes. \ (b) No; any other control sequence can appear
+(except those declared as |\outer| macros).
+
+\ddanger \TeX\ also allows the construction
+`^|\futurelet||\cs|\<token$_1$>\<token$_2$>', which has the effect of
+`|\let\cs = |\<token$_2$>\<token$_1$>\<token$_2$>'. The idea is that you can
+say, for example, `|\futurelet\a\b|' at the end of the replacement text of
+a macro; \TeX~will set |\a| to the token that follows the macro, after
+which |\b| will be expanded. The control sequence~|\b| can continue the
+processing, and it can examine |\a| to see what's coming up next.
+^^{looking ahead}
+
+\danger The next thing a person wants, after getting used to macros with
+parameters, is the ability to write macros that change their behavior
+depending on current conditions. \TeX\ provides a variety of primitive
+commands for this purpose. The general form of such ``^{conditional text}'' is
+\begindisplay
+|\if|\<condition>\<true text>|\else|\<false text>|\fi|
+\enddisplay
+where the \<true text> is skipped unless the \<condition> is true, and
+the \<false text> is skipped unless the \<condition> is false.
+If the \<false text> is empty, you can omit the~^|\else|. The
+`|\if|\<condition>' part of this construction begins with a control
+sequence whose first two letters are `|if|'; for example,
+\begintt
+\ifodd\count0 \rightpage \else\leftpage \fi
+\endtt
+^^|\ifodd|
+specifies a condition that is true when \TeX's integer register ^|\count||0|
+is odd. Since \TeX\ generally keeps the current ^{page number} in
+|\count0|, the macro |\rightpage| will be expanded in this example if
+the page number is odd, while |\leftpage| will be expanded if the
+page number is even. Conditional commands always end with a final `^|\fi|'.
+
+\danger Conditionals are primarily intended for experienced \TeX\ users,
+who want to define high-level macros; therefore
+the remaining paragraphs in this chapter are headed by ``double
+dangerous bends.'' Do not feel guilty about skipping right to Chapter~21;
+in other words, imagine that the manual says `|\ifexperienced|' right
+here, and that there is a matching `|\fi|' at the end of the present chapter.
+
+\ddanger Before we discuss \TeX's repertoire of\/ |\if...|\ commands, let's
+look at another example, so that the general ideas will be clear. Suppose
+that the |\count| register |\balance| holds an amount that somebody
+has paid in excess of his or her income tax; this amount is given in
+pennies, and it might be positive, negative, or zero. Our immediate goal
+will be to write a \TeX\ macro that generates a suitable statement for the
+^{Internal Revenue Service} to include as part of a letter to that person,
+based on the amount of the balance. The statement will be quite different
+for positive balances than for negative ones, so we can exploit \TeX's ability
+to act conditionally:
+\begintt
+\def\statement{\ifnum\balance=0 \fullypaid
+ \else\ifnum\balance>0 \overpaid
+ \else\underpaid
+ \fi
+ \fi}
+\endtt
+Here ^|\ifnum| is a conditional command that compares two numbers; the
+|\statement| macro reduces to |\fullypaid| if the balance is zero, and so on.
+
+\ddanger It is vastly important to notice the spaces after the |0|'s in
+this construction. If the example had said
+\begintt
+...=0\fullypaid...
+\endtt
+then \TeX\ would have begun to expand `|\fullypaid|' before it knew the
+value of the constant |0|, because |\fullypaid| might start with a~|1| or
+something that would change the number. \ (After all, `|01|' is a perfectly
+acceptable \<number>, in \TeX's eyes.) \ In this particular case the
+program would still have worked, because we will see in a moment that
+|\fullypaid| begins with the letter~|Y|; thus, the only problem caused by
+the missing space would be that \TeX\ would go slower, since it would have
+to skip over the whole expansion of\/ |\fullypaid| instead of just skipping
+|\fullypaid| as a single, unexpanded token. But in other situations a
+missing space like this might cause \TeX\ to expand macros when you don't
+want any expansion, and such anomalies can cause subtle and confusing errors.
+For best results, {\sl always put a blank space after a numeric constant\/};
+this blank space tells \TeX\ that the constant is complete, and
+such a space will never ``get through'' to the output. In fact, when you
+don't have a blank ^{space after a constant}, \TeX\ actually has to do
+more work, because each constant continues until a non-digit has been
+read; if this non-digit is not a space, \TeX\ takes the token you did have
+and backs it up, ready to be read again. \ (On the other hand, the author
+often omits the space when a constant is immediately followed by some
+other character, because extra spaces do look funny in the file;
+aesthetics are more important than~efficiency.)
+
+\ddangerexercise Continuing the IRS example, assume that |\fullypaid|
+and |\underpaid| are defined as follows:
+\begintt
+\def\fullypaid{Your taxes are fully paid---thank you.}
+\def\underpaid{{\count0=-\balance
+ \ifnum\count0<100
+ You owe \dollaramount, but you need not pay it, because
+ our policy is to disregard amounts less than \$1.00.
+ \else Please remit \dollaramount\ within ten days,
+ or additional interest charges will be due.\fi}}
+\endtt
+Write a macro |\overpaid| to go with these, assuming that |\dollaramount|
+is a macro that generates the contents of\/ |\count0| in dollars and cents.
+Your macro should say that a check will be mailed under separate cover,
+unless the amount is less than \$1.00, in which case the person must
+specifically request a check.
+\answer |\def\overpaid{{\count0=\balance|\parbreak
+ | You have overpaid your tax by \dollaramount.|\parbreak
+ | \ifnum\count0<100 It is our policy to refund|\parbreak
+ | such a small amount only if you ask for it.|\parbreak
+ | \else A check for this amount is being mailed|\parbreak
+ | under separate cover.\fi}}|
+
+\ddangerexercise Write a |\dollaramount| macro, to complete the Internal
+Revenue |\statement|.
+\answer The tricky part is to get the zero in an amount like `|$2.01|'.
+\begintt
+\def\dollaramount{\count2=\count0 \divide\count2 by100
+ \$\number\count2.%
+ \multiply\count2 by-100 \advance\count2 by\count0
+ \ifnum \count2<10 0\fi
+ \number\count2 }
+\endtt
+
+\ddanger Now let's make a complete survey of \TeX's conditional commands.
+Some of them involve features that have not yet been introduced in this
+manual.\enddanger
+
+\nobreak\medskip
+\item\bull^|\ifnum|\<number$_1$>\<relation>\<number$_2$>\quad
+ (compare two integers)
+\nobreak\smallskip\noindent
+The ^\<relation> must be either `|<|$_{12}$' or `|=|$_{12}$' or `|>|$_{12}$'.
+The two integer numbers are compared to each other in the usual way, and
+the result is true or false accordingly.
+
+\medbreak
+\item\bull^|\ifdim|\<dimen$_1$>\<relation>\<dimen$_2$>\quad
+ (compare two dimensions)
+\nobreak\smallskip\noindent
+This is like |\ifnum|, but it compares two \<dimen> values. For example,
+to test whether the value of\/ |\hsize| exceeds $100\pt$, you can say
+`|\ifdim\hsize>100pt|'.
+
+\medbreak
+\item\bull^|\ifodd|\<number>\quad(test for odd integer)
+\nobreak\smallskip\noindent
+The condition is true if the \<number> is odd, false if it is even.
+
+\medbreak
+\item\bull^|\ifvmode|\quad(test for vertical mode)
+\nobreak\smallskip\noindent
+True if \TeX\ is in vertical mode or internal vertical mode (see Chapter~13).
+
+\medbreak
+\item\bull^|\ifhmode|\quad(test for horizontal mode)
+\nobreak\smallskip\noindent
+True if \TeX\ is in horizontal mode or restricted horizontal mode
+(see Chapter~13).
+
+\medbreak
+\item\bull^|\ifmmode|\quad(test for math mode)
+\nobreak\smallskip\noindent
+True if \TeX\ is in math mode or display math mode (see Chapter~13).
+
+\medbreak
+\item\bull^|\ifinner|\quad(test for an internal mode)
+\nobreak\smallskip\noindent
+True if \TeX\ is in internal vertical mode, or restricted
+horizontal mode, or (nondisplay) math mode (see Chapter~13).
+
+\medbreak
+\item\bull^|\if|\<token$_1$>\<token$_2$>\quad(test if character codes agree)
+\nobreak\smallskip\noindent
+\TeX\ will expand macros following |\if| until two unexpandable tokens are
+found. If either token is a control sequence, \TeX\ considers it to have
+character code~256 and category code~16, unless the current equivalent of
+that control sequence has been |\let| equal to a non-active character
+token. In this way, each token specifies a (character~code,
+\hbox{category}~code) pair. The condition is true if the character codes
+are equal, independent of the category codes. For example, after
+|\def\a{*}| and |\let\b=*| and |\def\c{/}|, the tests `|\if*\a|' and
+`|\if\a\b|' will be true, but `|\if\a\c|' will be false. Also
+`|\if\a\par|' will be false, but `|\if\par\let|' will be true. % Beresford=true
+
+\medbreak
+\item\bull^|\ifcat|\<token$_1$>\<token$_2$>\quad(test if category codes agree)
+\nobreak\smallskip\noindent
+This is just like |\if|, but it tests the ^{category codes}, not the character
+codes. ^{Active characters} have category~13, but you have to say
+`^|\noexpand|\<active character>' in order to suppress expansion when you
+are looking at such characters with |\if| or |\ifcat|. For example, after
+\begintt
+\catcode`[=13 \catcode`]=13 \def[{*}
+\endtt
+the tests `|\ifcat\noexpand[\noexpand]|' and `|\ifcat[*|' will be true,
+but the test `|\ifcat\noexpand[*|' will be false.
+
+\medbreak
+\item\bull^|\ifx|\<token$_1$>\<token$_2$>\quad(test if tokens agree)
+\nobreak\smallskip\noindent
+In this case, \TeX\ does {\sl not\/} expand control sequences when it
+looks at the two tokens. The condition is true if (a)~the two tokens are
+not macros, and they both represent the same (character code, category
+code) pair or the same \TeX\ primitive or the same ^|\font| or ^|\chardef|
+or ^|\countdef|, etc.; or if (b)~the two tokens are macros, and they both
+have the same status with respect to ^|\long| and ^|\outer|, and they both
+have the same parameters and ``top level'' expansion. For example, after
+`|\def\a{\c}| |\def\b{\d}| |\def\c{\e}| |\def\d{\e}| |\def\e{A}|', an
+|\ifx| test will find |\c| and |\d| equal, but not |\a| and~|\b|, nor |\d|
+and~|\e|, nor any other combinations of\/ |\a|, |\b|, |\c|, |\d|, |\e|.
+
+\medbreak
+\item\bull^|\ifvoid|\<number>, ^|\ifhbox|\<number>, ^|\ifvbox|\<number>\quad
+(test a box register)
+\nobreak\smallskip\noindent
+The \<number> should be between 0 and 255. The condition is true if that
+|\box| is void or contains an hbox or a vbox, respectively (see Chapter~15).
+
+\medbreak
+\item\bull^|\ifeof|\<number>\quad(test for end of file)
+\nobreak\smallskip\noindent
+The \<number> should be between 0 and 15. The condition is true unless the
+corresponding input stream is open and not fully read. \ (See the command
+^|\openin| below.)
+
+\medbreak
+\item\bull^|\iftrue|, ^|\iffalse|\quad(always true or always false)
+\nobreak\smallskip\noindent
+These conditions have a predetermined outcome. But they turn out to be
+useful in spite of this, as explained below.
+
+\medbreak
+Finally, there's one more conditional construction, which is somewhat different
+from the rest because it is capable of making a many-way branch:
+\begindisplay
+\llap{\bull\enspace}^|\ifcase|\<number>\<text for case 0>|\or|\<text
+ for case 1>|\or|$\;\cdots$\cr
+| \or|\<text for case $n$>|\else|\<text for all other cases>|\fi|\cr
+\enddisplay
+Here there are $n+1$ cases separated by $n$ ^|\or|'s, where $n$ can be any
+nonnegative number. The \<number> selects the text that will be used. Once
+again the ^|\else| part is optional, if you don't want to specify any text
+for cases when the \<number> is negative or greater than~$n$.
+
+\ddangerexercise Design a |\category| macro that prints a character's current
+category code symbolically, given a one-character control sequence for
+that character. For example, if the category codes of plain \TeX\ are in
+force, `|\category\\|' should expand to `|escape|', and `|\category\a|'
+should expand to `|letter|'.
+\answer |\def\category#1{\ifcase\catcode`#1|\parbreak
+ | escape\or begingroup\or endgroup\or math\or|\parbreak
+ | align\or endline\or parameter\or superscript\or|\parbreak
+ | subscript\or ignored\or space\or letter\or|\parbreak
+ | otherchar\or active\or comment\or invalid\fi}|\par
+
+\ddangerexercise Test yourself on the following questions to see if you
+understand certain borderline situations: After the definitions
+`|\def\a{}| |\def\b{**}| |\def\c{True}|',
+which of the following are true?
+(a)~`|\if\a\b|';
+(b)~`|\ifcat\a\b|';
+(c)~`|\ifx\a\b|';
+(d)~`|\if\c|';
+(e)~`|\ifcat\c|';
+(f)~`|\ifx\ifx\ifx|'.
+(g)~`|\if\ifx\a\b\c\else\if\a\b\c\fi\fi|'.
+\answer (a,b)~True. (c,d)~False. (e,f)~True. In case~(e), the \<true text>
+starts with `|ue|'. (g)~The |\ifx| is false and the inner |\if| is true;
+so the outer |\if| becomes `|\if True...|', which is false. \
+(Interestingly, \TeX\ knows that the outer |\if| is false even before it
+has looked at the |\fi|'s that close the |\ifx| and the inner |\if|.)
+
+\ddanger Notice that all of the control sequences for conditionals
+begin with |\if...|, and they all have a matching~|\fi|. This convention---that
+|\if...|\ pairs up with |\fi|---makes it easier to see the nesting
+of conditionals within your program. The ^{nesting} of\/ |\if...\fi| is
+independent of the nesting of |{...}|; thus, you can begin or end a ^{group}
+in the middle of a conditional, and you can begin or end a conditional
+in the middle of a group. Extensive experience with macros has shown that
+such independence is important in applications; but it can also lead to
+confusion if you aren't careful.
+
+\ddanger It's sometimes desirable to pass information from one macro to
+another, and there are several ways to do this: ^^{communication between
+macros} by passing it as an argument, by putting it into a register, or by
+defining a control sequence that contains the information. For example,
+the macros |\hphantom|, |\vphantom|, and ^|\phantom| in Appendix~B are
+quite similar, so the author ^^{Knuth} wanted to do most of the work in
+another macro |\phant| that would be common to all three. Somehow |\phant|
+was to be told what kind of phantom was desired. The first approach was to
+define control sequences |\hph| and |\vph| something like this:
+\begintt
+\def\hphantom{\ph YN} \def\vphantom{\ph NY} \def\phantom{\ph YY}
+\def\ph#1#2{\def\hph{#1}\def\vph{#2}\phant}
+\endtt
+after which |\phant| could test `|\if Y\hph|' and `|\if Y\vph|'. This
+worked, but there were various ways to make it more efficient; for example,
+`|\def\hph{#1}|' could be replaced by `|\let\hph=#1|', avoiding macro expansion.
+An even better idea then suggested itself:
+\begintt
+\def\yes{\if00} \def\no{\if01}
+\def\hphantom{\ph\yes\no}...\def\phantom{\ph\yes\yes}
+\def\ph#1#2{\let\ifhph=#1\let\ifvph=#2\phant}
+\endtt
+after which |\phant| could test `|\ifhph|' and `|\ifvph|'. \ (This
+construction was tried before |\iftrue| and |\iffalse| were part of the
+\TeX\ language.) \ The idea worked fine, so the author started to use
+|\yes| and |\no| in a variety of other situations. But then one day a
+complex conditional failed, because it contained an |\ifhph|-like test
+inside another conditional:
+\begintt
+\if... \ifhph...\fi ... \else ... \fi
+\endtt
+Do you see the problem that developed? When the \<true text> of the
+outermost conditional was executed, everything worked fine, because
+|\ifhph| was either |\yes| or |\no| and it expanded into either |\if00| or
+|\if01|. But when the \<true text> was skipped, the |\ifhph| was not
+expanded, so the first |\fi| was mistakenly paired with the first |\if|;
+everything soon went haywire. That's when ^|\iftrue| and ^|\iffalse|
+were put into the language, in place of\/ |\yes| and |\no|; now
+|\ifhph| is either |\iftrue| or |\iffalse|, so \TeX\ will match it
+properly with a closing~|\fi|, whether or not it is being skipped over.
+
+\ddanger To facilitate |\if...|~constructions, plain \TeX\ has a
+^|\newif| macro, such that after you say `|\newif\ifabc|' three
+control sequences will be defined: |\ifabc| (for testing the switch),
+|\abctrue| (for making the switch true), and |\abcfalse| (for making
+it false). The |\phantom| problem is now solved in Appendix~B by writing
+\begintt
+\newif\ifhph \newif\ifvph
+\def\hphantom{\hphtrue\vphfalse\phant}
+\endtt
+and with similar definitions of\/ |\vphantom| and |\phantom|. There is no
+longer any need for a |\ph| macro; again |\phant| tests |\ifhph| and
+|\ifvph|. Appendix~E contains other examples of conditionals created
+by~|\newif|. New conditionals are initially false.
+
+\ddanger Caution: Don't say anything like `|\let\ifabc=\iftrue|' in
+conditional text. If \TeX\ skips over this command, it will think that
+both |\ifabc| and |\iftrue| require a matching |\fi|, since the |\let| is
+not being executed! Keep such commands buried inside macros, so that \TeX\
+will see the `|\if...|'\ only when it is not skipping over the text that it
+is reading.
+
+\ddanger \TeX\ has 256 ``^{token list registers}'' called ^|\toks||0|
+through |\toks255|, so that token lists can easily be shuffled around
+without passing them through \TeX's reading apparatus.
+There's also a ^|\toksdef| instruction so that, e.g.,
+\begintt
+\toksdef\catch=22
+\endtt
+makes |\catch| equivalent to |\toks22|. Plain \TeX\ provides a ^|\newtoks|
+macro that allocates a new token list register; it is analogous to
+|\newcount|. Token list registers behave like the ^{token list parameters}
+|\everypar|, |\everyhbox|, |\output|, |\errhelp|, etc. To assign
+a new value to a token list parameter or register, you say either
+\begindisplay
+\<token variable>|={|\<replacement text>|}|\cr
+\llap{or }\<token variable>|=|\<token variable>\cr
+\enddisplay
+where ^\<token variable> means either a token list parameter or a
+control sequence defined by |\toksdef| or |\newtoks|, or an
+explicit register designation `|\toks|\<number>'.
+
+\ddanger Everyone who makes extensive use of a powerful macro facility
+encounters situations when the macros do surprising things. We have
+already mentioned the possibility of setting |\tracingmacros=1|, in order
+to see when \TeX\ expands macros and what arguments it finds. There's also
+another helpful way to watch what \TeX\ is doing: If you set
+^|\tracingcommands||=1|, \TeX\ will show every command that it executes,
+as we saw in Chapter~13. Furthermore, if you set
+|\tracingcommands=2|, \TeX\ will show all conditional commands and
+their outcomes, as well as the unconditional commands that are actually
+performed or expanded. This diagnostic information goes into your log
+file. You can also see it on your terminal, if you say
+^|\tracingonline||=1|. \ (Incidentally, if you make |\tracingcommands|
+greater than~2, you get the same information as when it equals~2.) \
+Similarly, ^|\tracingmacros||=2| will trace |\output|, |\everypar|, etc.
+
+\ddanger One way to understand the occasional strangeness of macro
+operation is to use the tracing features just described, so that you can
+watch what \TeX\ does in slow motion. Another way is to learn the rules for
+how macros are expanded; we shall now discuss those rules.
+
+\ddanger \TeX's mastication process converts your input to a long token
+list, as explained in Chapter~8; and its digestive processes work
+strictly on this token list. When \TeX\ encounters a control sequence in
+the token list, it looks up the current meaning, and in certain cases
+it will expand that token into a sequence of other tokens before
+continuing to read. The expansion process applies to macros and to
+certain other special primitives like |\number| and |\if| that we shall
+consider momentarily. Sometimes, however, the ^{expansion} is not carried out;
+for example, when \TeX\ is taking care of a |\def|, the \<control sequence>,
+the \<parameter text>, and the \<replacement text> of that |\def| are
+not subject to expansion. Similarly, the two tokens after |\ifx| are
+never expanded. A complete list of occasions when tokens are not expanded
+appears later in this chapter; you can use it for reference in an emergency.
+
+\ddanger Now let's consider the control sequences that are expanded whenever
+expansion has not been inhibited. Such control sequences
+fall into several classes:\enddanger
+
+\nobreak\medskip
+\textindent\bull Macros. When a macro is expanded, \TeX\ first determines its
+arguments (if any), as explained earlier in this chapter. Each argument
+is a token list; the tokens are not expanded when they are being
+accepted as arguments. Then \TeX\ replaces the macro and its arguments
+by the replacement text.
+
+\smallbreak
+\textindent\bull Conditionals. When an |\if...| is expanded, \TeX\ reads
+ahead as far as necessary to determine whether the condition is true or
+false; and if false, it skips ahead (keeping track of\/ |\if...\fi| nesting)
+until finding the |\else|, |\or|, or~|\fi| that ends the skipped text.
+Similarly, when |\else|, |\or|, or~|\fi| is expanded, \TeX\ reads to the
+end of any text that ought to be skipped. The ``expansion'' of a conditional
+is empty. \ (Conditionals always reduce the number of tokens that are seen by
+later stages of the digestive process, while macros usually increase the
+number of tokens.)
+
+\smallbreak
+\textindent\bull ^|\number|\<number>. When \TeX\ expands |\number|, it reads
+the \<number> that follows (expanding tokens as it goes); the final
+expansion consists of the ^{decimal representation} of that number,
+preceded by `|-|' if negative.
+
+\smallbreak
+\textindent\bull ^|\romannumeral|\<number>. This is like |\number|, but
+the expansion consists of lowercase roman numerals. For example,
+`|\romannumeral 1984|' produces `|mcmlxxxiv|'. The expansion is
+empty if the number is zero or negative.
+
+\smallbreak
+\textindent\bull ^|\string|\<token>. \TeX\ first reads the \<token> without
+expansion. If a control sequence token appears, its |\string| expansion
+consists of the control sequence name (including ^|\escapechar| as an escape
+character, if the control sequence isn't simply an active character).
+Otherwise the \<token> is a character token, and its character code
+is retained as the expanded result.
+
+\smallbreak
+\textindent\bull ^|\jobname|. The expansion is the name that \TeX\ has chosen
+for this job. For example, if \TeX\ is putting its output on files
+|paper.dvi| and |paper.log|, ^^|dvi| then |\jobname| expands to `|paper|'.
+
+\smallbreak
+\textindent\bull ^|\fontname|\<font>. The expansion is the external file name
+corresponding to the given font; e.g., `|\fontname\tenrm|' might expand
+to `|cmr10|' (five tokens). If the font is not being used at its design size,
+the ``^{at size}'' also appears in the expansion. A ^\<font> is either an
+identifier defined by ^|\font|; or |\textfont|\<number>,
+|\scriptfont|\<number>, or |\scriptscriptfont|\<number>; or |\font|, which
+denotes the current font.
+
+\smallbreak
+\textindent\bull ^|\meaning|\<token>. \TeX\ expands this to the sequence of
+characters that would be displayed on your terminal by the commands
+`|\let\test=|\<token> |\show\test|'. For example, `|\meaning A|' usually
+expands to `|the letter A|'; `|\meaning\A|' after `|\def\A#1B{\C}|'
+expands to `|macro:#1B->\C |'.
+
+\smallbreak
+\textindent\bull ^|\csname||...|^|\endcsname|. When \TeX\ expands |\csname|
+it reads to the matching |\endcsname|, expanding tokens as it goes;
+only character tokens should remain after this expansion has taken place.
+Then the ``expansion'' of the entire |\csname...\endcsname| text
+will be a single control sequence token, defined to be like |\relax| if
+its meaning is currently undefined.
+
+\smallbreak
+\textindent\bull ^|\expandafter|\<token>. \TeX\ first reads the token that
+comes immediately after |\expandafter|, without expanding it;
+let's call this token~$t$. Then \TeX\ reads the token that comes after~$t$
+(and possibly more tokens, if that token has an argument), replacing it by
+its expansion. Finally \TeX\ puts~$t$ back in front of that expansion.
+
+\smallbreak
+\textindent\bull ^|\noexpand|\<token>. The expansion is the token itself;
+but that token is interpreted as if its meaning were `|\relax|' if it is a
+control sequence that would ordinarily be expanded by \TeX's expansion rules.
+
+\smallbreak
+\textindent\bull ^|\topmark|, ^|\firstmark|, ^|\botmark|, ^|\splitfirstmark|,
+and ^|\splitbotmark|. \kern-1.7pt % This saves an overfull box (March 27, 1983)
+The expansion is the token list in the corresponding ``^{mark}'' register
+(see Chapter~23).
+
+\smallbreak
+\textindent\bull ^|\input|\<file name>. The expansion is null; but \TeX\
+prepares to read from the specified file before looking at any more
+tokens from its current source.
+
+\smallbreak
+\textindent\bull ^|\endinput|. The expansion is null. The next time \TeX\ gets
+to the end of an |\input| line, it will stop reading from the file containing
+that line.
+
+\smallbreak
+\textindent\bull ^|\the|\<internal quantity>. The expansion is a list of
+tokens representing the current value of one of \TeX's variables, as
+explained below. For example, `|\the\skip5|' might expand into
+`|5.0pt plus 2.0fil|' (17~tokens).
+
+\ddanger The powerful |\the| operation has many subcases, so we shall discuss
+them one at a time. A variety of internal numeric quantities
+can be brought up front:\enddanger
+
+\nobreak\medskip
+\textindent\bull |\the|\<parameter>, where \<parameter> is the name of
+one of \TeX's ^{integer parameters} (e.g., |\the\widowpenalty|),
+^{dimension parameters} (e.g., |\the\parindent|), ^{glue parameters}
+(e.g., |\the\leftskip|), or ^{muglue parameters} (e.g., |\the\thinmuskip|).
+
+\smallbreak
+\textindent\bull |\the|\<register>, where \<register> is the name of
+one of \TeX's integer ^{registers} (e.g., |\the\count|\stretch|0|),
+dimension registers (e.g., |\the\dimen169|), glue registers (e.g.,
+|\the\skip255|), or muglue registers (e.g., |\the\muskip\count2|).
+
+\smallbreak
+\textindent\bull |\the|\<codename>\<8-bit number>, where \<codename>
+stands for either ^|\catcode|, ^|\mathcode|, ^|\lccode|, ^|\uccode|,
+^|\sfcode|, or ^|\delcode|. For example, |\the\mathcode`/| produces the
+current (integer) math code value for a slash.
+
+\smallbreak
+\textindent\bull |\the|\<special register>, where \<special register> is
+one of the integer quantities ^|\prevgraf|, ^|\deadcycles|, ^|\insertpenalties|,
+^|\inputlineno|, ^|\badness|,
+or ^|\parshape| (denoting only the number of lines of\/ |\parshape|); or
+one of the dimensions ^|\pagetotal|, ^|\pagegoal|, ^|\pagestretch|,
+^|\pagefilstretch|, ^|\pagefillstretch|, ^|\pagefilllstretch|, ^|\pageshrink|,
+^|\pagedepth|. In horizontal modes you can also refer to a special integer,
+|\the\spacefactor|; in vertical modes there's a special dimension,
+|\the\prevdepth|.
+
+\smallbreak
+\textindent\bull |\the|^|\fontdimen|\<parameter number>\<font>.
+This produces a dimension; for example, parameter~6 of a font is its
+``^{em}'' value, so `|\the\fontdimen6\tenrm|' yields `|10.0pt|' (six tokens).
+
+\smallbreak
+\textindent\bull |\the|^|\hyphenchar|\<font>, |\the|^|\skewchar|\<font>.
+These produce the corresponding integer values defined for the specified font.
+
+\smallbreak
+\textindent\bull |\the|^|\lastpenalty|, |\the|^|\lastkern|, |\the|^|\lastskip|.
+These yield the amount of penalty, kerning, glue, or
+muglue in the final item on the current list, provided that the item is a
+penalty, kern, or glue, respectively; otherwise they yield `|0|' or `|0.0pt|'.
+
+\smallbreak
+\textindent\bull |\the|\<defined character>, where \<defined character> is
+a control sequence that has been given an integer value with ^|\chardef| or
+^|\mathchardef|; the result is that integer value, in decimal notation.
+
+\ddanger In all of the cases listed so far, |\the| produces a result that
+is a sequence of ^{ASCII} character tokens. Category code~12 (``other'') is
+assigned to each token, except that character code~32 gets category~10
+(``space''). The same rule is used to assign ^{category codes} to the
+tokens produced by ^|\number|, ^|\romannumeral|, ^|\string|, ^|\meaning|,
+^|\jobname|, and ^|\fontname|.
+
+\ddanger There also are cases in which |\the| produces non-character tokens,
+either a font identifier like |\tenrm|, or an arbitrary token list:
+
+\textindent\bull |\the|\<font> produces a font identifier that selects the
+specified font. For example, `|\the\font|' is a control sequence corresponding
+to the current font.
+
+\goodbreak\smallskip
+\textindent\bull |\the|\<token variable> produces a copy of the token list that
+is the current value of the variable. For example, you can expand
+`|\the\everypar|' and `|\the\toks5|'.
+
+\ddanger \TeX's primitive command `^|\showthe|' will display on your
+terminal exactly what `|\the|' would produce in an expanded definition;
+the expansion is preceded by `|> |' and followed by a period. For example,
+`|\showthe\parindent|' will display
+\begintt
+> 20.0pt.
+\endtt
+if the plain \TeX\ paragraph indentation is being used.
+
+\ddanger Here now is the promised list of all cases when expandable tokens
+are not expanded. Some of the situations involve primitives that
+haven't been discussed yet, but we'll get to them
+eventually. Expansion is suppressed at the following times:\enddanger
+
+\nobreak\medskip
+\item\bull When tokens are being deleted during ^{error recovery} (see
+Chapter~6).
+
+\smallskip
+\item\bull When tokens are being skipped because conditional text is being
+ignored.
+
+\smallskip
+\item\bull When \TeX\ is reading the arguments of a macro.
+
+\smallskip
+\item\bull When \TeX\ is reading a control sequence to be defined by
+^|\let|, ^|\futurelet|, ^|\def|, ^|\gdef|, ^|\edef|, ^|\xdef|, ^|\chardef|,
+^|\mathchardef|, ^|\countdef|, ^|\dimendef|, ^|\skipdef|, ^|\muskipdef|,
+^|\toksdef|, ^|\read|, and ^|\font|.
+
+\smallskip
+\item\bull When \TeX\ is reading argument tokens for ^|\expandafter|,
+^|\noexpand|, ^|\string|, ^|\meaning|, ^|\let|, ^|\futurelet|, ^|\ifx|,
+^|\show|, ^|\afterassignment|, ^|\aftergroup|.
+
+\smallskip
+\item\bull When \TeX\ is absorbing the parameter text of a |\def|, |\gdef|,
+|\edef|, or |\xdef|.
+
+\smallskip
+\item\bull When \TeX\ is absorbing the replacement text of a |\def| or
+|\gdef| or ^|\read|; or the text of a ^{token variable} like ^|\everypar|
+or ^|\toks||0|; or the token list for ^|\uppercase| or ^|\lowercase| or
+^|\write|. \ (The token list for |\write| will be expanded later, when it
+is actually output to a file.)
+
+\smallskip
+\item\bull When \TeX\ is reading the preamble of an alignment, except after
+a token for the primitive command |\span| or when reading the \<glue>
+after ^|\tabskip|.
+
+\smallskip
+\item\bull Just after a |$|$_3$ token that begins math mode, to see if
+another~|$|$_3$ follows.
+
+\smallskip
+\item\bull Just after a |`|$_{12}$ token that begins an ^{alphabetic constant}.
+
+\ddanger Sometimes you will find yourself wanting to define new macros
+whose replacement text has been expanded, based on current conditions,
+instead of simply copying the replacement text verbatim. \TeX\ provides
+the ^|\edef| (expanded definition) command for this purpose, and also
+^|\xdef| (which is equivalent to |\global\edef|). The general format
+is the same as for |\def| and |\gdef|, but \TeX\ blindly expands the tokens
+of the replacement text according to the expansion rules above. For
+example, consider
+\begintt
+\def\double#1{#1#1}
+\edef\a{\double{xy}} \edef\a{\double\a}
+\endtt
+Here the first |\edef| is equivalent to `|\def\a{xyxy}|' and the second is
+equivalent to `|\def\a{xyxyxyxy}|'. All of the other kinds of expansion
+will take place too, including conditionals; for example,
+\begintt
+\edef\b#1#2{\ifmmode#1\else#2\fi}
+\endtt
+gives a result equivalent to `|\def\b#1#2{#1}|' if \TeX\ is in math
+mode at the time of the |\edef|, otherwise the result is equivalent to
+`|\def\b#1#2{#2}|'.
+
+\ddanger Expanded definitions that are made with |\edef| or |\xdef| continue
+to expand tokens until only unexpandable tokens remain, except that
+token lists produced by `^|\the|' are not expanded further. Furthermore
+a token following `^|\noexpand|' will not be expanded, since its ability
+to expand has been nullified. These two operations can be used to control
+^^{expansion, avoiding} what gets expanded and what doesn't.
+
+\ddanger Suppose, for example, that you
+want to define |\a| to be equal to~|\b| (expanded) followed by~|\c|
+(not expanded) followed by |\d| (expanded), assuming that |\b| and
+|\d| are simple macros without parameters. There are two easy ways to do it:
+\begintt
+\edef\a{\b\noexpand\c\d}
+\toks0={\c} \edef\a{\b\the\toks0 \d}
+\endtt
+And it's even possible to achieve the same effect without using either
+|\noexpand| or |\the|; a reader who wants to learn more about \TeX's
+expansion mechanism is encouraged to try the next three exercises.
+
+\ddangerexercise Figure out a way to define |\a| as in the previous paragraph,
+without using \TeX's primitives `|\noexpand|' and `|\the|'.
+\answer One idea is to say
+\begintt
+\let\save=\c \let\c=0 \edef\a{\b\c\d} \let\c=\save
+\endtt
+because control sequences equivalent to characters are not expandable.
+However, this doesn't expand occurrences of~|\c|
+that might be present in the expansions of\/ |\b| and~|\d|. Another way,
+which is free of this defect, is
+\begintt
+\edef\next#1#2{\def#1{\b#2\d}} \next\a\c
+\endtt
+(and it's worth a close look!).
+
+\ddangerexercise Continuing the example of expansion avoidance, suppose that
+you want to expand |\b| completely until only unexpandable tokens are left,
+but you don't want to expand |\c| at all, and you want to expand |\d|
+only one level. For example, after |\def\b{\c\c}| and |\def\c{*}| and
+|\def\d{\b\c}| the goal would be to get the effect of\/ |\def\a{**\c\b\c}|.
+How can such a partial expansion be achieved, using ^|\the|?
+\answer |\toks0={\c} \toks2=\expandafter{\d}|\parbreak
+ |\edef\a{\b\the\toks0 \the\toks2 }|
+\smallskip\noindent
+(Notice that ^|\expandafter| expands the token after the left brace here.)
+
+\ddangerexercise Solve the previous exercise without |\the| or |\noexpand|.
+\ (This is difficult.)
+\answer The following shouldn't be taken too seriously, but it does work:
+^^|\span|
+\begintt
+{\setbox0=\vbox{\halign{#{\c\span\d}\cr
+ \let\next=0\edef\next#1{\gdef\next{\b#1}}\next\cr}}}
+\let\a=\next
+\endtt
+
+\ddanger \TeX's primitive commands ^|\mark||{...}|, ^|\message||{...}|,
+^|\errmessage||{...}|, ^|\special||{...}|, and ^|\write|\<number>|{...}|
+all expand the token lists in braces almost exactly as |\edef| and |\xdef| do.
+However, a macro parameter
+character like~|#| should not be duplicated in such commands; you need
+to say |##| within an |\edef|, but only |#| within a |\mark|. The
+|\write| command is somewhat special, because its token list is first
+read without expansion; expansion occurs later, when the tokens
+are actually being written to a file.
+
+\ddangerexercise Compare the following two definitions:
+\begintt
+\def\a{\iftrue{\else}\fi}
+\edef\b{\iftrue{\else}\fi}
+\endtt
+Which of them yields an ^{unmatched left brace}? (This is tricky.)
+\answer Neither one, although |\a| will behave like an unmatched left
+brace when it is expanded. The definition of\/ |\b| is {\sl not complete},
+because it expands to `|\def\b{{}|'; \TeX\ will continue to read ahead,
+looking for another right brace, possibly discovering a runaway
+definition! It's impossible to define a macro that has unmatched braces.
+But you {\sl can\/} say |\let\a={|; Appendix~D discusses several
+other ^{brace tricks}.
+
+\def\rhead{Chapter \chapno: Definitions (aka Macros)% my little joke
+ \gdef\rhead{Chapter \chapno: Definitions (also called Macros)}}
+
+\ddanger \TeX\ has the ability to read individual lines of text from up
+to~16 files at once, in addition to the files that are being |\input|.
+To initiate reading such an auxiliary file, you should say
+\begindisplay
+^|\openin|\<number>|=|\<file name>
+\enddisplay
+where the \<number> is between 0 and 15. \ (Plain \TeX\ allocates
+input stream numbers 0~through~15 with the ^|\newread| command, which
+is analogous to |\newbox|.) \ In most installations
+of \TeX, the extension `^|.tex|' will be appended to the file name,
+as with ^|\input|, if no extension is given explicitly. If the file
+cannot be found, \TeX\ will give no error message; it will simply consider
+that the input stream is not open, and you can test this condition
+with ^|\ifeof|. When you're done with a file, you can say
+\begindisplay
+^|\closein|\<number>
+\enddisplay
+and the file associated with that input stream number will be closed,
+i.e., returned to its initial condition, if such a file was open.
+To get input from an open file, you say
+\begindisplay
+^|\read|\<number>^|to|\<control sequence>
+\enddisplay
+and the control sequence is defined to be a parameterless macro whose
+replacement text is the contents of the next line read from the
+designated file. This line is converted to a token list, using the
+procedure of Chapter~8, based on the current category codes.
+Additional lines are read, if necessary, until an equal number of
+left and right braces has been found. An empty line is implicitly appended
+to the end of a file that is being |\read|. ^^{empty line at end of file}
+If the \<number> is not between 0 and~15, or if no such file is open, or if
+the file has ended, input will be from the terminal; \TeX\ will prompt
+the user unless the \<number> is negative. The macro definition will be
+local unless you say |\global\read|.
+
+\ddanger For example, it's easy to have ^{dialogs with the user}, by
+using |\read| together with the ^|\message| command (which
+writes an expanded token list on the terminal and in the log file):
+\begintt
+\message{Please type your name:}
+\read16 to\myname
+\message{Hello, \myname!}
+\endtt
+The |\read| command in this case will print `|\myname=|' and it will wait
+for a response; the response will be echoed on the log file.
+The `|\myname=|' would have been omitted if `|\read16|' had been
+`|\read-1|'.
+
+\ddangerexercise The |\myname| example just given doesn't work quite right,
+because the \<return> at the end of the line gets translated into a
+space. Figure out how to fix that glitch.
+\answer One way is to redefine |\catcode`\^^M=9| (ignored) just before
+the |\read|, so that the \<return> will be ignored. Another solution is
+to redefine ^|\endlinechar||=-1|, so that no character is put at the
+end of the line. Or you could try to be tricky by stripping off the
+space with macro expansion as follows:
+\begintt
+\def\stripspace#1 \next{#1}
+\edef\myname{\expandafter\stripspace\myname\next}
+\endtt
+The latter solution doesn't work if the user types `|%|' at the end of
+his~or her name, or if the name contains control sequences.
+
+\ddangerexercise Continuing the previous example, define a macro
+|\MYNAME| that contains the letters of\/ |\myname| all in ^{uppercase
+letters}. For example, if\/ |\myname| expands to |Arthur|, |\MYNAME|
+should expand to |ARTHUR|. Assume that |\myname| contains only
+letters and spaces in its expansion.
+\answer Here are two solutions:
+\begintt
+\def\next#1\endname{\uppercase{\def\MYNAME{#1}}}
+\expandafter\next\myname\endname
+|smallskip\edef\next{\def\noexpand\MYNAME{\myname}}
+\uppercase\expandafter{\next}
+\endtt
+
+\ddanger Appendices B, D, and E contain numerous examples of how to make
+macros do useful things. Let's close this chapter by presenting a few
+examples that show how \TeX\ can actually be used as a primitive ^{programming}
+language, if you want to achieve special effects, and if you don't care very
+much about computer costs.
+
+\ddanger Plain \TeX\ contains a |\loop...\repeat| construction, which works
+like this: You say `^|\loop|~$\alpha$~|\if...|~$\beta$~|\repeat|', where
+$\alpha$ and~$\beta$ are any sequences of commands, and where |\if...|\
+is any conditional test (without a matching |\fi|). \TeX\ will first
+do~$\alpha$; then if the condition is true, \TeX\ will do~$\beta$ and
+repeat the whole process again starting with~$\alpha$. If the
+condition ever turns out to be false, the loop will stop. For example,
+here is a program that carries out a little dialog in which \TeX\ waits for
+the user to type `|Yes|' or `|No|': ^^{repeating commands, see :loop}
+\begintt
+\def\yes{Yes } \def\no{No } \newif\ifgarbage
+\loop\message{Are you happy? }
+ \read-1 to\answer
+ \ifx\answer\yes\garbagefalse % the answer is Yes
+ \else\ifx\answer\no\garbagefalse % the answer is No
+ \else\garbagetrue\fi\fi % the answer is garbage
+ \ifgarbage\message{(Please type Yes or No.)}
+\repeat
+\endtt
+
+\ddangerexercise Use the |\loop...\repeat| mechanism to construct a
+general |\punishment| macro that repeats any given paragraph any given number of
+times. For example,
+\begintt
+\punishment{I must not talk in class.}{100}
+\endtt
+should produce the results desired in exercise 20.\punishexno.
+\answer (Here's a solution that also numbers the lines, so that the number of
+repetitions is easily verifiable.
+The only tricky part about this answer is the use of\/ ^|\endgraf|,
+which is a substitute for |\par| because |\loop| is not a ^|\long| macro.)
+\begintt
+\newcount\n
+\def\punishment#1#2{\n=0
+ \loop\ifnum\n<#2 \advance\n by1
+ \item{\number\n.}#1\endgraf\repeat}
+\endtt
+
+\let\plaind=\d
+\newif\ifprime \newif\ifunknown
+\newcount\n \newcount\p \newcount\d \newcount\a
+\def\primes#1{2,~3% assume that #1 is at least 3
+ \n=#1 \advance\n by-2 % n more to go
+ \p=5 % odd primes starting with p
+ \loop\ifnum\n>0 \printifprime\advance\p by2 \repeat}
+\def\printp{, % we will invoke \printp if p is prime
+ \ifnum\n=1 and~\fi % this precedes the last value
+ \number\p \advance\n by -1 }
+\def\printifprime{\testprimality \ifprime\printp\fi}
+\def\testprimality{{\d=3 \global\primetrue
+ \loop\trialdivision \ifunknown\advance\d by2 \repeat}}
+\def\trialdivision{\a=\p \divide\a by\d
+ \ifnum\a>\d \unknowntrue\else\unknownfalse\fi
+ \multiply\a by\d
+ \ifnum\a=\p \global\primefalse\unknownfalse\fi}
+\ddanger The first thirty prime numbers are \primes{30}. You may not
+find this fact very startling; but you may be surprised to learn that
+the previous sentence was typeset by saying
+\begintt
+The first thirty prime numbers are \primes{30}.
+\endtt
+\TeX\ did all of the calculation by expanding the |\primes| macro, so the author
+is pretty sure that the list of ^{prime numbers} given above is quite free
+of typographic errors. Here is the set of macros that did it:
+\begindisplay
+^|\newif||\ifprime \newif\ifunknown % boolean variables|\cr
+^|\newcount||\n \newcount\p \newcount\d \newcount\a % integer variables|\cr
+|\def\primes#1{2,~3% assume that #1 is at least 3|\cr
+| \n=#1 \advance\n by-2 % n more to go|\cr
+| \p=5 % odd primes starting with p|\cr
+| \loop\ifnum\n>0 \printifprime\advance\p by2 \repeat}|\cr
+|\def\printp{, % we will invoke \printp if p is prime|\cr
+| \ifnum\n=1 and~\fi % `and' precedes the last value|\cr
+| \number\p \advance\n by -1 }|\cr
+|\def\printifprime{\testprimality \ifprime\printp\fi}|\cr
+|\def\testprimality{{\d=3 \global\primetrue|\cr
+| \loop\trialdivision \ifunknown\advance\d by2 \repeat}}|\cr
+\noalign{\penalty-500}
+|\def\trialdivision{\a=\p \divide\a by\d|\cr
+| \ifnum\a>\d \unknowntrue\else\unknownfalse\fi|\cr
+| \multiply\a by\d|\cr
+| \ifnum\a=\p \global\primefalse\unknownfalse\fi}|\cr
+\enddisplay
+^^|\multiply|^^|\divide|^^|\advance|^^|\newcount|
+The computation is fairly straightforward, except that it involves
+a loop inside a loop; therefore |\testprimality| introduces an extra
+set of braces, to keep the inner loop control from interfering with
+the outer loop. The braces make it necessary to say `^|\global|' when
+|\ifprime| is being set true or false. \TeX\ spent more time constructing
+that sentence than it usually spends on an entire page; the
+|\trialdivision| macro was expanded 132 times. % cpu time was 4 sec
+
+\let\d=\plaind
+\ddanger The |\loop| macro that does all these wonderful things is
+actually quite simple. It puts the code that's supposed to be repeated
+into a control sequence called |\body|, and then another control
+sequence iterates until the condition is false:
+\begintt
+\def\loop#1\repeat{\def\body{#1}\iterate}
+\def\iterate{\body\let\next=\iterate\else\let\next=\relax\fi\next}
+\endtt
+The expansion of\/ |\iterate| ends with the expansion of\/ |\next|; therefore
+\TeX\ is able to remove |\iterate| from its memory before
+invoking |\next|, and the memory does not fill up during a long loop.
+Computer scientists call this ``^{tail recursion}.''
+
+\ddanger The |\hex| macro below, which converts count register |\n| to
+^{hexadecimal notation}, illustrates a {\sl recursive\/} control structure
+in which many copies of\/ |\hex| can be active simultaneously.
+^{Recursion} works better than simple |\loop| ^{iteration} in this
+application because the hexadecimal digits are discovered from right to
+left, while they must be output from left to right.
+\ (The number in |\n| should be $\ge0$.)
+\begintt
+\def\hex{{\count0=\n \divide\n by16
+ \ifnum\n>0 \hex\fi \count2=\n \multiply\count2 by-16
+ \advance\count0 by\count2 \hexdigit}}
+\def\hexdigit{\ifnum\count0<10 \number\count0
+ \else\advance\count0 by-10 \advance\count0 by`A \char\count0 \fi}
+\endtt
+
+\ddanger Our final example is a macro that computes the number of nonblank
+tokens in its argument; for example, `|\length{argument}|' expands to
+`|8|'. This illustrates yet another aspect of macro technique.
+\begintt
+\def\length#1{{\count0=0 \getlength#1\end \number\count0}}
+\def\getlength#1{\ifx#1\end \let\next=\relax
+ \else\advance\count0 by1 \let\next=\getlength\fi \next}
+\endtt
+
+\endchapter
+
+By this time [37 A.D.] the influence of ^{Macro} had become supreme.
+\author ^{TACITUS}, {\sl Annals\/} (c.\thinspace120 A.D.) % book VI, ch 45
+
+\bigskip
+
+% Oh, you want a definition.
+I hate definitions.
+\author BENJAMIN ^{DISRAELI}, {\sl Vivian Grey\/} (1826) % Book II, Chapter 6
+
+\eject
+ \beginchapter Chapter 21. Making Boxes
+
+In Chapters 11 and 12 we discussed the principles of boxes and glue, and by now
+we have seen many applications of those concepts. You can get by in most cases
+with the boxes that \TeX\ manufactures automatically with its paragraph
+builder, its page builder, and its math formula processor; but if you want to
+do nonstandard things, you have the option of making boxes by yourself.
+For example, Chapter~14 points out that you can keep something from being
+hyphenated or split between lines if you enclose it in an ^|\hbox|;
+Chapter~19 points out that |\hbox| allows you to get ordinary text into
+a displayed equation. ^^|\vbox|
+
+\danger The purpose of the present chapter is to nail down whatever details
+about boxes haven't been covered yet. Fortunately, there isn't much more
+to discuss; we have already mentioned most of the rules, so this chapter is
+fairly short. In fact, the previous chapters have dealt with almost
+everything except the rules about rules.
+
+\danger To make a ^{rule box} (i.e., a solid ^{black rectangle}), you
+type `^|\hrule|' in vertical mode or `^|\vrule|' in horizontal mode,
+followed by any or all of the specifications `^|width|\<dimen>',
+`^|height|\<dimen>', `^|depth|\<dimen>', in any order. For example, if
+\begintt
+\vrule height4pt width3pt depth2pt
+\endtt
+appears in the middle of a paragraph, \TeX\ will typeset the black box
+`\thinspace\vrule height4pt width3pt depth2pt\thinspace'. If you
+specify a dimension twice, the second specification overrules the first.
+If you leave a dimension unspecified, you get the following by default:
+$$\halign{\indent#\hfil&\qquad\hfil#\hfil&\qquad\hfil#\hfil\cr
+&|\hrule|&|\vrule|\cr
+width&\tt*&$0.4\pt$\cr
+height&$0.4\pt$&\tt*\cr
+depth&$0.0\pt$&\tt*\cr}$$
+Here `{\tt*}' means that the actual dimension depends on the context;
+the rule will extend to the boundary of the smallest box or alignment
+that encloses it.
+
+\hrule
+
+\danger For example, the author typed `|\hrule|' just before typing this
+paragraph, and you can see what happened: A horizontal rule, $0.4\pt$
+thick, was extended across the page, because the vertical box that
+encloses it turned out to be just that wide. \ (In fact, the
+vertical box that encloses it is the page itself.) \ Another example
+appears immediately after this paragraph, where you can see the result
+of
+\begintt
+\hrule width5cm height1pt \vskip1pt \hrule width6cm
+\endtt
+\TeX\ does not put ^{interline glue} between rule boxes and their neighbors
+in a vertical list, so these two rules are exactly $1\pt$ apart.
+
+\hrule width5cm height1pt \vskip1pt \hrule width6cm
+
+\dangerexercise B. L. ^{User} didn't want one of his horizontal rules to
+touch the left margin, so he put it in a box and moved it right, like this:
+\begintt
+\moveright 1in \vbox{\hrule width3in}
+\endtt
+^^|\moveright|
+But he found that this produced more space above and below the rule
+than when he had simply said `|\hrule width 4in|' with no |\vbox|.
+Why did \TeX\ insert more space, and what should he have done to
+avoid it?
+\answer The interline skip is added for vboxes, but not for rules; he
+forgot to say ^|\nointerlineskip|, before and after the |\moveright|
+construction.
+
+\danger If you specify all three dimensions of a rule, there's no essential
+difference between |\hrule| and |\vrule|, since both will produce exactly
+the same black box. But you must call it an |\hrule| if you want to put
+it in a vertical list, and you must call it a |\vrule| if you want to put
+it in a horizontal list, regardless of whether it actually looks like
+a horizontal rule or a vertical rule or neither. If you say |\vrule| in
+vertical mode, \TeX\ starts a new paragraph; if you say |\hrule| in
+horizontal mode, \TeX\ stops the current paragraph and returns to
+vertical mode.
+
+\danger The dimensions of a rule can be negative; for example, here's
+a rule whose height is $3\pt$ and whose depth is $-2\pt$:
+`\thinspace\vrule height3pt depth-2pt width1in\thinspace'.
+However, a rule is invisible unless its height plus depth is positive
+and its width is positive. A rule whose width is negative cannot be
+seen, but it acts like a ^{backspace} when it appears in a horizontal list.
+
+\dangerexercise Explain how the author probably obtained the rule
+`\thinspace\vrule height3pt depth-2pt width1in\thinspace' in the
+previous paragraph. [{\sl Hint:\/} It's one inch long.]
+\answer |\vrule height3pt depth-2pt width1in|. Notice that it was necessary
+to call it a |\vrule| since it appeared in horizontal mode.
+
+\ddanger Now let's summarize all of the ways there are to specify boxes
+explicitly to \TeX. \ (1)~A character by itself makes a character box, in
+horizontal mode; this character is taken from the current font. \
+(2)~The commands |\hrule| and |\vrule| make rule boxes, as just explained.
+\ (3)~Otherwise you can make hboxes and vboxes, which fall under the
+generic term ^\<box>. A \<box> has one of the following seven forms:
+$$\halign{\indent#\hfil&\quad(see Chapter #)\hfil\cr
+^|\hbox|\<box specification>|{|\<horizontal material>|}|&12\cr
+^|\vbox|\<box specification>|{|\<vertical material>|}|&12\cr
+^|\vtop|\<box specification>|{|\<vertical material>|}|&12\cr
+^|\box|\<register number>&15\cr
+^|\copy|\<register number>&15\cr
+^|\vsplit|\<register number>|to|\<dimen>&15\cr
+^|\lastbox|&21\cr}$$
+Here a ^\<box specification> is either `^|to|\<dimen>' or
+`^|spread|\<dimen>' or empty; this governs the setting of glue in
+the horizontal or vertical lists inside the box, as explained in
+Chapter~12. A \<register number> is between 0 and 255; after you say
+|\box|, that register becomes void, but after |\copy| the register
+is unchanged, as explained in Chapter~15. The |\vsplit| operation
+is also explained in Chapter~15. In math modes an additional
+type of box is available:
+^|\vcenter|\<box specification>|{|\<vertical material>|}| (see Chapter~17).
+
+\ddanger The bottom line of the table above refers to ^|\lastbox|,
+a primitive operation that hasn't been mentioned before. If the last item
+on the current horizontal list or vertical list is an hbox or vbox, it
+is removed from the list and it becomes the |\lastbox|; otherwise
+|\lastbox| is void. This operation is allowed in ^{internal vertical
+mode}, horizontal mode, and restricted horizontal mode, but you cannot use
+it to take a box from the current page in vertical mode. In math modes,
+|\lastbox| is always void. At the beginning of a paragraph,
+`|{\setbox0=\lastbox}|' removes the ^{indentation box}.
+
+\ddanger The operation ^|\unskip| is something like |\lastbox|, except that
+it applies to glue instead of to boxes. If the last thing on the current list
+is a glue item (or ^{leaders}, as explained below), it is removed. You
+can't remove glue from the current page by using |\unskip| in vertical
+mode, but you can say `|\vskip-|^|\lastskip|', which has almost
+the same effect.
+
+\ddanger Chapters 24 to 26 present summaries of all \TeX's operations
+in all modes, and when those summaries mention a `\<box>' they mean one
+of the seven possibilities just listed. For example, you can say
+`|\setbox|\<register number>|=|\<box>' in any mode, and you can say
+`|\moveright|\<dimen>\<box>' in vertical modes. But you can't say
+`|\setbox|\<register number>|=C|' or `|\moveright|\<dimen>|\hrule|';
+if you try either of these, \TeX\ will complain that a \<box> was
+supposed to be present. Characters and rules are so special, they
+aren't regarded as \<box>es.
+
+\ddangerexercise Define a control sequence |\boxit| so that
+`|\boxit{|\<box>|}|' yields the given box surrounded by 3~points of space and
+by ruled lines on all four sides.
+$$\def\boxit#1{\vbox{\hrule\hbox{\vrule\kern3pt
+ \vbox{\kern3pt#1\kern3pt}\kern3pt\vrule}\hrule}}
+\setbox4=\vbox{\hsize 23pc \noindent \strut For example, the sentence you
+are now reading was typeset as part of the displayed formula
+|$$\boxit{\boxit{\box4}}$$|, where box~4 was created by typing
+`|\setbox4=\vbox{\hsize 23pc \noindent \strut For example,
+the sentence you are now reading ... \strut}|'.\strut}
+\boxit{\boxit{\box4}}$$
+\answer |\def\boxit#1{\vbox{\hrule\hbox{\vrule\kern3pt|\parbreak
+ | \vbox{\kern3pt#1\kern3pt}\kern3pt\vrule}\hrule}}|\par
+\smallskip\noindent
+(The resulting box does not have the baseline of the original one;
+you have to work a little bit harder to get that.)
+
+\danger Let's look also at what can go inside a box. An hbox contains
+a horizontal list; a vbox contains a vertical list. Both kinds of lists
+are made up primarily of things like boxes, glue, kerns, and penalties,
+as we have seen in Chapters 14 and~15. But you can also include some
+special things that we haven't discussed yet, namely ``leaders''
+and ``whatsits.'' Our goal in the rest of this chapter will be to
+study how to make use of such exotic items.
+
+\danger The dots you see before your eyes here
+\xleaders\hbox to 6pt{$\hss\cdot\hss$}\hfil\
+are called {\sl^{leaders}\/}\break
+because they lead your eyes across the page; such things are often
+used in indexes or tables of contents. The general idea is to repeat a
+box as many times as necessary to fill up some given space. \TeX\ treats
+leaders as a special case of glue; no, wait, it's the other way
+around: \TeX\ treats glue as a special case of leaders. Ordinary glue
+fills space with nothing, while leaders fill space with any
+desired thing. In horizontal mode you can say
+\begindisplay
+^|\leaders|\<box or rule>|\hskip|\<glue>
+\enddisplay
+and the effect will be the same as if you had said just `|\hskip|\<glue>',
+except that the space will be occupied by copies of the specified
+\<box or rule>. The glue stretches or shrinks in the usual way. For example,
+\begintt
+\def\leaderfill{\leaders\hbox to 1em{\hss.\hss}\hfill}
+\line{Alpha\leaderfill Omega}
+\line{The Beginning\leaderfill The Ending}
+\endtt
+will produce the following two lines:
+$$\vbox{
+\def\leaderfill{\leaders\hbox to 1em{\hss.\hss}\hfill}
+\line{Alpha\leaderfill Omega}
+\line{The Beginning\leaderfill The Ending}
+}$$
+Here `|\hbox to 1em{\hss.\hss}|' specifies a box one em wide, with a
+period in its center; the control sequence |\leaderfill| then causes this
+box to be replicated when filling space in the |\line| box. \ (Plain \TeX's
+^|\line| macro makes an hbox whose width is the |\hsize|.)
+
+\danger Notice that the ^{dots} in the two example lines appear exactly
+above each other. This is not a coincidence; it's a consequence of the
+fact that the |\leaders| operation acts something like a window that lets
+you see part of an infinite row of boxes. If the words `Alpha' and `Omega'
+are replaced by longer words, the number of dots might be different
+but the ones that you see will be in the same places as before. The infinitely
+replicated boxes are lined up so that they touch each other, and so that,
+if you could see them all, one of them would have the same reference point as
+the smallest enclosing box. Thus, |\leaders| will put a box flush with the
+left edge of an enclosing box, if you start the leaders there; but you
+won't get a box flush right unless the width of the enclosing box is
+exactly divisible by the width of the repeated box. If~the repeated box
+has width~$w$, and if the space to be filled is at least~$2w$, then you
+will always see at least one copy of the box; but if the space is less
+than~$2w$ the box may not appear, because boxes in the infinite
+row are typeset only when their entire width falls into the available space.
+
+\ddanger When leaders are isolated from each other, you might not
+want them to be aligned as just described, so \TeX\ also provides
+for ^{nonaligned leaders}. In this case a box of width~$w$ will be
+copied $q$~times when the space to be filled is at least~$qw$ and
+less than $(q+1)w$; furthermore, the results will be centered in the
+available space. There are two kinds of nonaligned leaders in \TeX,
+namely ^|\cleaders| (centered leaders) and ^|\xleaders| (expanded
+leaders). Centered leaders pack the boxes tightly next to each other
+leaving equal amounts of blank space at the left and right; expanded
+leaders distribute the extra space equally between the $q+1$ positions
+adjacent to the $q$~boxes. For example, let's suppose that a $10\pt$-wide
+box is being used in leaders that are supposed to fill $56\pt$ of space.
+Five copies of the box will be used; |\cleaders| will produce $3\pt$ of
+space, then the five boxes, then another $3\pt$ of space. But
+|\xleaders| will produce $1\pt$ of space, then the first box, then
+another $1\pt$ of space, then the second box, \dots, then the fifth box,
+and $1\pt$ of space.
+
+\ddangerexercise Suppose that a $10\pt$-wide box is to fill $38\pt$ of
+space starting $91\pt$ from the left of its enclosing box. How many
+copies of the box will be produced by |\leaders|, |\cleaders|, and
+|\xleaders|? Where will the boxes be positioned, relative to the
+left edge of the enclosing box, in each of the three cases?
+\answer |\leaders|: two boxes starting at $100\pt$, $110\pt$.\par
+|\cleaders|: three boxes starting at $95\pt$, $105\pt$, $115\pt$.\par
+|\xleaders|: three boxes starting at $93\pt$, $105\pt$, $117\pt$.
+
+\ddangerexercise Assuming that the `|.|'\ in the |\leaderfill| macro on the
+previous page is only $0.2\em$ wide, there is $0.4\em$ of blank space
+at both sides of the one-em box. Therefore the |\leaders| construction
+will leave between $0.4\em$ and $1.4\em$ of blank space between the
+periods and the text at either end. Redefine |\leaderfill| so that
+the amount of blank space at either end will be between $0.1\em$ and
+$1.1\em$, but the leaders on adjacent lines will still be aligned
+with each other.
+\answer |\def\leaderfill{\kern-0.3em\leaders\hbox to 1em{\hss.\hss}%|\parbreak
+ | \hskip0.6em plus1fill \kern-0.3em }|
+
+\danger Instead of giving a \<box> in the leaders construction, you can
+give a \<rule>, which means either |\hrule| or |\vrule|, followed
+by optional ^|height|, ^|width|, and ^|depth| specifications as
+usual. The rule will then be made as wide as the corresponding~\<glue>.
+This is a case where |\hrule| makes sense in horizontal mode, because
+it gives a ^{horizontal rule in text}. For example, if the
+|\leaderfill| macro in our earlier illustration is changed to
+\begintt
+\def\leaderfill{ \leaders\hrule\hfill\ }
+\endtt
+then the results look like this:
+$$\vbox{
+\def\leaderfill{ \leaders\hrule\hfill\ }
+\line{Alpha\leaderfill Omega}
+\line{The Beginning\leaderfill The Ending}
+}$$
+When a rule is used instead of a box, it fills the space completely, so
+there's no difference between |\leaders|, |\cleaders|, and |\xleaders|.
+
+\ddangerexercise What does |\leaders\vrule\hfill| produce?
+\answer Since no |height| or |depth| specification follows the |\vrule|,
+the height and depth are `|*|'; i.e., the rule extends to the smallest
+enclosing box. This usually makes a heavy black band, which is too
+horrible to demonstrate here. However, it does work if you know that the
+enclosing box is sufficiently small; and |\leaders\vrule\vfill| works fine in
+vertical mode.
+
+\ddanger Leaders work in vertical mode as well as in horizontal mode. In this
+case vertical glue (e.g., |\vskip|\<glue> or |\vfill|) is used instead of
+horizontal glue, and |\leaders| produces boxes that are aligned so that the
+top of each repeated box has the same vertical position as the top
+of the smallest enclosing box, plus a multiple of the height-plus-depth
+of the repeated box. No interlineskip glue is placed between boxes in
+vertical leaders; the boxes are just stacked right on top of each other.
+
+\ddanger If you specify horizontal leaders with a box whose width isn't
+positive, or if you specify vertical leaders with a box whose
+height-plus-depth isn't positive, \TeX\ silently ignores the leaders
+and produces ordinary glue instead.
+
+\ddangerexercise Explain how you can end a paragraph with a rule that
+is at least $10\pt$ long and extends all the way to the right
+margin, like this:
+\null\nobreak\leaders\hrule\hskip10pt plus1filll\ \par
+\answer For example, say
+\begintt
+\null\nobreak\leaders\hrule\hskip10pt plus1filll\ \par
+\endtt
+The `|\|\]' provides extra glue that is wiped out by the implied |\unskip|
+at the end of every paragraph (see Chapter~14), and the `|\null\nobreak|'
+makes sure that the leaders do not disappear at a line break; `^|filll|'
+overtakes the ^|\parfillskip| glue.
+
+\ddanger Horizontal leaders differ slightly from horizontal glue, because
+they have height and depth when \TeX\ calculates the size of the enclosing
+box (even though the number of replications might be zero). Similarly,
+vertical leaders have width.
+
+\ddangerexercise Demonstrate how to produce the following `\TeX ture'
+$$\hbox to 2.5in{\cleaders\vbox to .5in{\cleaders\hbox{\TeX}\vfil}\hfil}$$
+by using vertical leaders inside of horizontal leaders. \ (The \TeX\ logo
+has been put into a rectangular box, and copies of this box have been packed
+together tightly.) ^^{TeX logo}
+\answer |$$\hbox to 2.5in{\cleaders|\parbreak
+ | \vbox to .5in{\cleaders\hbox{\TeX}\vfil}\hfil}$$|
+
+\ddangerexercise Use vertical leaders to solve exercise 20.\punishexno.
+\answer We assume that a strut is $12\pt$ tall, and that 50 lines
+fit on a page:
+\begintt
+\setbox0=\hbox{\strut I must not talk in class.}
+\null\cleaders\copy0\vskip600pt\vfill\eject % 50 times on page 1;
+\null\cleaders\box0\vskip600pt\bye % 50 more on page 2.
+\endtt
+The ^|\null| keeps glue (and leaders) from disappearing at the top of
+the page.
+
+\danger The ^|\overbrace| and ^|\underbrace| macros of plain \TeX\ are
+constructed by combining characters with rules. Font ^|cmex10| contains
+four symbols $\bracelu\;\braceld\;\bracerd\;\braceru$, each of which has
+depth zero and height equal to the thickness of a rule that joins them
+properly. Therefore it's easy to define ^|\upbracefill| and ^|\downbracefill|
+macros so that you can obtain, e.g., ^^{braces}
+\begindisplay
+\vbox{\kern4pt
+ \hbox to 100pt{\downbracefill}\hbox to 50pt{\upbracefill}
+ }
+\enddisplay
+by saying `|\hbox to 100pt{\downbracefill}\hbox to 50pt{\upbracefill}|'
+in vertical mode. Details of those macro definitions appear in Appendix~B.
+
+\ddanger The definition of\/ ^|\overrightarrow| in Appendix B is more complex
+than that of\/ |\overbrace|, because it involves a box instead of a rule. The
+fonts of plain \TeX\ are designed so that symbols like $\leftarrow$ and
+$\to$ can be extended with ^{minus signs}; similarly, $\Leftarrow$ and
+^^{arrows}
+$\Rightarrow$ can be extended with ^{equals signs}. However, you can't
+simply put the characters next to each other, because that leaves
+gaps (`${\leftarrow}{-}{-}$' and `${\Leftarrow}{=}{=}$'); it is necessary
+to backspace a little between characters. An additional complication
+arises because the extension line in a long arrow might need to be some
+non-integer number of minus signs long. To solve this problem, the
+^|\rightarrowfill| macro in Appendix~B uses |\cleaders| with a repeatable
+box consisting of the middle 10 units of a minus sign, where one unit
+is ${1\over18}\em$. The leaders are preceded and followed by $-$ and~$\to$;
+there's enough backspacing to compensate for up to 5~units of extra space,
+fore and aft, that |\cleaders| might leave blank. In this way a macro is
+obtained such that
+\begintt
+\hbox to 100pt{\rightarrowfill}
+\endtt
+yields `\hbox to 100pt{\rightarrowfill}'.
+
+\ddanger Now we know all about leaders. What about ^{whatsits}? Well,
+whatsits have been provided as a general mechanism by which important
+special printing applications can be handled as extensions to \TeX\null.
+It's possible for system wizards to modify the \TeX\ program, without
+changing too much of the code, so that new features can be accommodated
+at high speed instead of encoding them in macros. The author hopes that
+such extensions will not be made very often, because he doesn't want
+incompatible pseudo-\TeX\ systems to proliferate; yet he realizes that
+certain special books deserve a special treatment. Whatsits make it
+possible to incorporate new things into boxes without bending the existing
+conventions too much. But they make applications less portable from
+one machine to another.
+
+\ddanger Two kinds of whatsits are defined as part of all \TeX\
+implementations. They aren't really ^{extensions to \TeX}, but
+they are coded as if they were, so that they provide a model of how
+other extensions could be made. The first of these is connected with
+output to text files, and it involves the \TeX\ primitive commands
+^|\openout|, ^|\closeout|, ^|\write|, and ^|\immediate|. The second is
+connected with special instructions that can be transmitted to
+printing devices, via \TeX's ^|\special| command.
+
+\ddanger The ability to write text files that can later be input by other
+programs (including \TeX) makes it possible to take care of tables of
+contents, indexes, and many other things. You can say
+`|\openout|\<number>|=|\<file name>' and `|\closeout|\<number>' by analogy
+with the |\openin| and |\closein| commands of Chapter~20; the
+\<number> must be between 0 and~15. The filename is usually extended with
+`^|.tex|' if it has no extension. There is a |\write| command that writes
+one line to a file, analogous to the |\read| command that reads one line;
+you say
+\begindisplay
+|\write|\<number>|{|\<token list>|}|
+\enddisplay
+and the material goes out to the file that corresponds to the given stream
+number. If the \<number> is negative or greater than~15, or if the
+specified stream has no file open for output, the output goes to the
+user's ^{log file}, and to the ^{terminal} unless the number is negative.
+Plain \TeX\ has a ^|\newwrite| command that allocates output stream numbers
+from 0 to~15. Output streams are completely independent of input streams.
+
+\ddanger However, the output actually takes place in a delayed fashion;
+the |\openout|, |\closeout|, and |\write| commands that you give are not
+performed when \TeX\ sees them. Instead, \TeX\ puts these commands into
+whatsit items, and places them into the current horizontal
+or vertical or math list that is being built. No actual output will occur
+until this whatsit is eventually shipped out to the |dvi| file, as part
+of a larger box. The reason for this delay is that |\write| is often
+used to make an index or table of contents, and the exact page on which
+a particular item will appear is generally unknown when the |\write|
+instruction occurs in mid-paragraph. \TeX\ is usually working ahead,
+reading an entire paragraph before breaking it into lines, and accumulating
+more than enough lines to fill a page before deciding what goes on
+the page, as explained in Chapters 14 and~15. Therefore a deferred writing
+mechanism is the only safe way to ensure the validity of page number
+references.
+
+\ddanger The \<token list> of a |\write| command is first stored in a
+whatsit without performing any macro expansion; the macro expansion takes
+place later, when \TeX\ is in the middle of a ^|\shipout| operation. For
+example, suppose that some paragraph in your document contains the text
+\begintt
+... For \write\inx{example: \the\count0}example, suppose ...
+\endtt
+Then the horizontal list for the paragraph will have a whatsit just before
+the word `|example|', and just after the interword space following~`|For|'.
+This whatsit item contains the unexpanded token list
+`|example: \the\count0|'. It sits dormant while the paragraph is being
+broken into lines and put on the current page. Let's suppose that
+this word `|example|' (or some hyphenated initial part of it, like
+`|ex-|') is shipped out on page~256. Then \TeX\ will write the line
+\begintt
+example: 256
+\endtt
+on output stream |\inx|, because the `|\the\count0|' will be expanded
+at that time. Of course, |\write| commands are usually generated by
+macros; they are rarely typed explicitly in mid-paragraph.
+
+\ddanger \TeX\ defers |\openout| and |\closeout| commands by putting them
+into whatsits too; thus, the relative order of output commands will be
+preserved, unless boxes are shipped out in some other order due to
+insertions or such things.
+
+\ddanger Sometimes you don't want \TeX\ to defer a |\write| or
+|\openout| or |\closeout|. You could say, e.g., `|\shipout\hbox{\write...}|',
+but that would put an unwanted empty page in your |dvi| file. So \TeX\ has
+another feature that gets around this problem: If you type `^|\immediate|'
+just before |\write| or |\openout| or |\closeout|, the operation will
+be performed immediately, and no whatsit will be made. For example,
+\begintt
+\immediate\write16{Goodbye}
+\endtt
+prints `|Goodbye|' on your terminal. Without the |\immediate|, you wouldn't
+see the `|Goodbye|' until the current list was output. \ (In fact, you
+might never see it; or you may see it more than once, if the
+current list goes into a box that was copied.) \ An `|\immediate\write16|'
+differs from ^|\message| in that |\write| prints the text on a line by itself;
+the results of several |\message| commands might appear on the same line,
+separated by spaces.
+
+\ddanger The \<token list> of a |\write| ought to be rather short, since
+it makes one line of output. Some implementations of \TeX\ are unable to
+write long lines; if you want to write a lot of stuff, just give
+several |\write| commands. Alternatively, you can set \TeX's ^|\newlinechar|
+parameter to the ASCII code number of some character that you want to
+stand for ``begin a new line''; then \TeX\ will begin a new line whenever it
+would ordinarily output that character to a file.
+For example, one way to output two lines to the terminal
+in a single |\write| command is to say
+\begintt
+\newlinechar=`\^^J
+\immediate\write16{Two^^Jlines.}
+\endtt
+Each |\write| command produces output in the form that \TeX\ always uses
+to display ^{token lists symbolically}: Characters represent
+themselves (except that you get duplicated characters like |##| for macro
+parameter characters^^{hash}); unexpandable control sequence tokens
+produce their names, preceded by the ^|\escapechar| and followed by a
+space (unless the name is an active character or a control sequence formed
+from a single nonletter).
+
+\ddanger \TeX\ ignores |\write|, |\openout|, and |\closeout| whatsits that
+appear within boxes governed by ^{leaders}. If you are upset about this,
+you shouldn't be.
+
+\ddanger Since the \<token list> of a deferred |\write| is expanded at a
+fairly random time (when |\shipout| occurs), you should be careful about
+what control sequences it is allowed to contain. The techniques of Chapter~20
+for controlling macro expansion often come in handy with respect to |\write|.
+
+\ddangerexercise Suppose that you want to |\write| a token list that
+involves a macro |\chapno|, containing the current chapter number, as well as
+`|\the\count0|' which refers to the current page. You want |\chapno| to
+be expanded immediately, because it might change before the token list
+is written; but you want |\the\count0| to be expanded at the time of
+|\shipout|. How can you manage this?
+\answer |{\let|\stretch|\the=0\edef|\stretch|\next|\stretch
+|{\write|\stretch|\cont|\stretch|{|\<token list>|}}\next|\stretch|}| will
+expand everything but |\the| when the |\write| command is given.
+
+\ddanger Now let's wrap up our study of boxes by considering one more feature.
+The command `^|\special||{|\<token list>|}|' can be given in any
+mode. Like |\write|, it puts its token list into a whatsit; and like
+|\message|, it expands the token list immediately. This token list will be
+output to the |dvi| file with the other typesetting commands that
+\TeX\ produces. Therefore it is implicitly associated with a particular
+position on the page, namely the reference point that would have been present
+if a box of height, depth, and width zero had appeared in place of the
+whatsit. The \<token list> in a |\special| command should consist of a
+keyword followed if necessary by a space and appropriate arguments. For example,
+\begintt
+\special{halftone pic1}
+\endtt
+might mean that a ^{picture} on file |pic1| should be inserted ^^{halftones}
+on the current page, with its reference point at the current position.
+\TeX\ doesn't look at the token list to see if it makes any sense; the
+list is simply copied to the output. However, you should be careful not to
+make the list too long, or you might overflow \TeX's string memory.
+The |\special| command enables you to make use of special equipment that
+might be available to you, e.g., for printing books in glorious
+\TeX ni^{color}.
+
+\ddanger Software programs that convert |dvi| files to printed or
+displayed output should be able to fail gracefully when they don't
+recognize your special keywords. Thus, |\special| operations should
+never do anything that changes the current position. Whenever you use
+|\special|, you are taking a chance that your output file will not be
+printable on all output devices, because all |\special| functions are
+extensions to \TeX. However, the author anticipates that certain standards
+for common graphic operations will emerge in the \TeX\ user community,
+after careful experiments have been made by different groups of people;
+then there will be a chance for some uniformity in the use of
+|\special| extensions.
+
+\ddanger \TeX\ will report the badness of glue setting in a box if
+you ask for the numeric quantity
+^|\badness| after making a box. For example, you might say
+\begintt
+\setbox0=\line{\trialtexta}
+\ifnum\badness>250 \setbox0=\line{\trialtextb}\fi
+\endtt
+The badness is between 0 and 10000
+unless the box is overfull, when |\badness=1000000|.
+
+
+\endchapter
+
+If age or weaknes doe prohibyte bloudletting,
+you must use boxing.
+\author PHILIP ^{BARROUGH}, {\sl The Methode of Phisicke\/} (1583) % page 6
+
+\bigskip
+
+The only thing that never looks right is a rule.
+There is not in existence a page with a rule on it
+that cannot be instantly and obviously improved
+by taking the rule out.
+% "Even dashes, cherished as they are by authors who cannot punctuate,
+% spoil a page. They are generally merely ignorant substitutes for colons."
+\author GEORGE BERNARD ^{SHAW}, in {\sl The Dolphin\/} (1940) % v4.1 p81
+
+\eject
+ \beginchapter Chapter 22. Alignment
+
+Printers charge extra when you ask them to typeset ^{tables}, and they do so
+for good reason: Each table tends to have its own peculiarities, so it's
+necessary to give some thought to each one, and to fiddle with alternative
+approaches until finding something that looks good and communicates well.
+However, you needn't be too frightened of doing tables with \TeX, since plain
+\TeX\ has a ``tab'' feature that handles simple situations pretty much like
+you would do them on a typewriter. Furthermore, \TeX\ has a powerful
+alignment mechanism that makes it possible to cope with extremely complex
+tabular arrangements. Simple cases of these ^{alignment} operations will
+suffice for the vast majority of applications.
+
+Let's consider ^{tabbing} first. If you say `^|\settabs| $n$ ^|\columns|',
+plain \TeX\ makes it easy to produce lines that are divided into $n$~equal-size
+^{columns}. Each line is specified by typing
+\begindisplay
+|\+|\<text$_1$>|&|\<text$_2$>|&|$\,\cdots\,$|\cr|
+\enddisplay
+where \<text$_1$> will start flush with the left margin,
+\<text$_2$> will start at the left of the second column, and so on. Notice
+that `^|\+|' starts the line. The final column is followed by `^|\cr|',
+which old-timers will recognize as an abbreviation for the ``^{carriage
+return}'' operation on typewriters that had carriages. For example,
+consider the following specification:
+\begintt
+\settabs 4 \columns
+\+&&Text that starts in the third column\cr
+\+&Text that starts in the second column\cr
+\+\it Text that starts in the first column, and&&&
+ the fourth, and&beyond!\cr
+\endtt
+After `|\settabs|\stretch|4\columns|' each |\+| line is divided into quarters,
+so the result~is
+\medskip
+\settabs 4 \columns
+\+&&Text that starts in the third column\cr
+\+&Text that starts in the second column\cr
+\+\it Text that starts in the first column, and&&&
+ the fourth, and&beyond!\cr
+\def\tick{\kern-0.2pt % that's half the rule width
+ \vbox to 0pt{\kern-36pt\leaders\hbox{\vrule height1pt\vbox to4pt{}}\vfil}}
+\vskip-\baselineskip
+\+\tick&\tick&\tick&\tick&\tick\cr
+\medskip
+
+This example merits careful study because it illustrates several things.
+(1)~The `|&|' ^^{ampersand} is like the {\sc TAB} key on many typewriters;
+it tells \TeX\ to advance to the next tab position, where there's a tab at
+the right edge of each column. In this example, \TeX\ has set up four tabs,
+indicated by the dashed lines; a dashed line is also shown at the left
+margin, although there isn't really a tab there. (2)~But `|&|' isn't
+exactly like a mechanical typewriter {\sc TAB}, because it first backs up
+to the beginning of the current column before advancing to the next. In
+this way you can always tell what column you're tabbing to, by counting
+the number of |&|'s; that's handy, because variable-width type otherwise
+makes it difficult to know whether you've passed a tab position or not.
+Thus, on the last line of our example, three |&|'s were typed in order to
+get to column~4, even though the text had already extended into column~2
+and perhaps into column~3. (3)~You can say `|\cr|' before you have
+specified a complete set of columns, if the remaining columns are blank.
+(4)~The |&|'s are different from tabs in another way, too: \TeX\ ignores
+^{spaces} after~`|&|', hence you can conveniently finish a column by typing
+`|&|'~at the end of a line in your input file, without worrying that an
+extra blank space will be introduced there. \ (The second-last line of the
+example ends with~`|&|', and there is an implicit blank space following that
+symbol; if \TeX\ hadn't ignored that space, the words `the fourth'
+wouldn't have started exactly at the beginning of the fourth column.) \
+Incidentally, plain \TeX\ also ignores spaces after `|\+|', so that the
+first column is treated like the others. (5) The `^|\it|' in the last line
+of the example causes only the first column to be italicized, even though
+no ^{braces} were used to confine the range of italics, because \TeX\
+implicitly inserts braces around each individual entry of an alignment.
+
+\danger Once you have issued a |\settabs| command, the tabs remain set until you
+reset them, even though you go ahead and type ordinary paragraphs as usual.
+But if you enclose |\settabs| in |{...}|, the tabs defined inside a group
+don't affect the tabs outside; `^|\global||\settabs|' is not permitted.
+
+\danger Tabbed lines usually are used between paragraphs, in the same
+places where you would type ^|\line| or ^|\centerline| to get lines with
+a special format. But it's also useful to put |\+|~lines inside a |\vbox|;
+this makes it convenient to specify ^{displays} that contain aligned
+material. For example, if you type
+\begintt
+$$\vbox{\settabs 3 \columns
+ \+This is&a strange&example\cr
+ \+of displayed&three-column&format.\cr}$$
+\endtt
+you get the following display:
+$$\vbox{\settabs 3 \columns
+ \+This is&a strange&example\cr
+ \+of displayed&three-column&format.\cr}$$
+In this case the first column doesn't appear flush left, because \TeX\
+centers a box that is being displayed. Columns that end with |\cr| in
+a |\+|~line are put into a box with their natural width; so the first
+and second columns here are one-third of the |\hsize|, but the third column
+is only as wide as the word `example'. We have used |$$| ^^{dollardollar}
+in this construction even though no mathematics is involved, because |$$|
+does other useful things; for example, it centers the box, and it inserts
+space above and below.
+
+People don't always want tabs to be equally spaced, so there's another
+way to set them, by typing `|\+|\<sample line>|\cr|' immediately after
+`|\settabs|'. In this case tabs are placed at the positions
+of the |&|'s in the ^{sample line}, and the sample line itself does not appear
+in the output. For example,
+\begintt
+\settabs\+\indent&Horizontal lists\quad&\cr % sample line
+\+&Horizontal lists&Chapter 14\cr
+\+&Vertical lists&Chapter 15\cr
+\+&Math lists&Chapter 17\cr
+\endtt
+causes \TeX\ to typeset the following three lines of material:
+\nobreak\medskip
+\settabs\+\indent&Horizontal lists\quad&\cr
+\+&Horizontal lists&Chapter 14\cr
+\+&Vertical lists&Chapter 15\cr
+\+&Math lists&Chapter 17\cr
+\medbreak\noindent
+The |\settabs| command in this example makes column~1 as wide as a paragraph
+^^{indention, see indentation}
+indentation; and column~2 is as wide as `Horizontal lists' plus one quad of
+space. ^^|\quad| Only two tabs are set in this case, because only two |&|'s
+appear in the sample line. \ (A sample line might as well end with~|&|,
+because the text following the last tab isn't used for anything.)
+
+The first line of a table can't always be used as a sample line, because it
+won't necessarily give the correct tab positions. In a large table you have
+to look ahead and figure out the biggest entry in each column; the sample
+line is then constructed by typing the widest first column, the widest
+second column, etc., omitting the last column. Be sure to include some
+extra space between columns in the sample line, so that the columns
+won't touch each other.
+
+\def\frac#1/#2{\leavevmode\kern.1em
+ \raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em
+ /\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}
+\exercise Explain how to typeset the following table [from Beck,
+Bertholle, and Child, {\sl Mastering the Art of French Cooking\/}
+(New York: Knopf, 1961)]: % p283
+^^{Beck, Simone} ^^{Bertholle, Louisette} ^^{Child, Julia}
+\nobreak\medskip
+\settabs\+\indent&10\frac1/2 lbs.\qquad&\it Servings\qquad&\cr
+\+&\negthinspace\it Weight&\it Servings&
+ {\it Approximate Cooking Time\/}*\cr
+\smallskip
+\+&8 lbs.&6&1 hour and 50 to 55 minutes\cr
+\+&9 lbs.&7 to 8&About 2 hours\cr
+\+&9\frac1/2 lbs.&8 to 9&2 hours and 10 to 15 minutes\cr
+\+&10\frac1/2 lbs.&9 to 10&2 hours and 15 to 20 minutes\cr
+\smallskip
+\+&* For a stuffed goose,
+ add 20 to 40 minutes to the times given.\cr
+\answer Notice the uses of `|\smallskip|' here to separate the table heading
+and footing from the table itself; such refinements are often worthwhile.
+\begintt
+\settabs\+\indent&10\frac1/2 lbs.\qquad&\it Servings\qquad&\cr
+\+&\negthinspace\it Weight&\it Servings&
+ {\it Approximate Cooking Time\/}*\cr
+\smallskip
+\+&8 lbs.&6&1 hour and 50 to 55 minutes\cr
+\+&9 lbs.&7 to 8&About 2 hours\cr
+\+&9\frac1/2 lbs.&8 to 9&2 hours and 10 to 15 minutes\cr
+\+&10\frac1/2 lbs.&9 to 10&2 hours and 15 to 20 minutes\cr
+\smallskip
+\+&* For a stuffed goose,
+ add 20 to 40 minutes to the times given.\cr
+\endtt
+\def\frac#1/#2{\leavevmode\kern.1em
+ \raise.5ex\hbox{\the\scriptfont0 #1}\kern-.1em
+ /\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}%
+The title line specifies `|\it|' three times, because each entry between
+tabs is treated as a group by \TeX; you would get error messages galore
+if you tried to say something like \hbox{`|\+&{\it Weight&Servings&...}\cr|'}.
+The `^|\negthinspace|' in the title line is a small backspace that
+compensates for the slant in an italic {\it W\/}; the author inserted
+this somewhat unusual correction after seeing how the table looked
+without it, on the first proofs. \ (You weren't supposed to think of this,
+but it has to be mentioned.) \ See exercise 11.\fracexno\ for the `|\frac|'
+macro; it's better to say `\frac1/2' than `$1\over2$', in a cookbook.\par
+Another way to treat this table would be to display it in a vbox, instead
+of including a first column whose sole purpose is to specify indentation.
+
+\ninepoint % it's all dangerous from here to end of chapter
+\danger If you want to put something ^{flush right} in its column, just type
+`^|\hfill|' before it; and be sure to type `|&|' after it, so that
+\TeX\ will be sure to move the information all the way until it touches
+the next tab. Similarly, if you want to ^{center} something in its
+column, type `|\hfill|' before it and `|\hfill&|' after it. For example,
+\begintt
+\settabs 2 \columns
+\+\hfill This material is set flush right&
+ \hfill This material is centered\hfill&\cr
+\+\hfill in the first half of the line.&
+ \hfill in the second half of the line.\hfill&\cr
+\endtt
+produces the following little table:\enddanger
+\nobreak\medskip
+\settabs 2 \columns
+\+\hfill This material is set flush right&
+ \hfill This material is centered\hfill&\cr
+\+\hfill in the first half of the line.&
+ \hfill in the second half of the line.\hfill&\cr
+
+\danger The |\+| macro in Appendix~B works
+by putting the \<text> for each column that's followed by~|&|
+into an hbox as follows:
+\begindisplay
+|\hbox to |\<column width>|{|\<text>|\hss}|
+\enddisplay
+The ^|\hss| means that the text is normally flush left, and that it can
+extend to the right of its box. Since |\hfill| is ``more infinite'' than
+|\hss| in its ability to stretch, it has the effect of right-justifying or
+centering as stated above. Note that |\hfill| doesn't shrink, but |\hss|
+does; if the text doesn't fit in its column, it will stick out at the right.
+You could avoid this by adding |\hskip| |0pt| |minus-1fil|; then
+an oversize text would produce an overfull box.
+You could also center some text by putting `|\hss|' before it and just
+`|&|' after it; in that case the text would be allowed to extend to the
+left and right of its column.
+ The last column of a |\+|~line (i.e., the column entry that is
+followed by |\cr|) is treated differently: The
+\<text> is simply put into an hbox with its natural~width.\looseness=-1
+
+\danger ^{Computer programs} present difficulties of a different kind, since
+some people like to adopt a style in which the tab positions change from
+line to line. For example, consider the following program fragment:
+$$\vbox{\+\bf if $n<r$ \cleartabs&\bf then $n:=n+1$\cr
+ \+&\bf else &{\bf begin} ${\it print\_totals}$; $n:=0$;\cr
+ \+&&{\bf end};\cr
+ \+\bf while $p>0$ do\cr
+ \+\quad\cleartabs&{\bf begin} $q:={\it link}(p)$;
+ ${\it free\_node}(p)$; $p:=q$;\cr
+ \+&{\bf end};\cr}$$
+Special tabs have been set up so that `{\bf then}' and `{\bf else}' appear
+one above the other, and so do `{\bf begin}' and `{\bf end}'. It's possible
+to achieve this by setting up a new sample line whenever a new tab position
+is needed; but that's a tedious job, so plain \TeX\ makes it a little simpler.
+Whenever you type |&| to the right of all existing tabs, the effect is to
+set a new tab there, in such a way that the column just completed will have
+its natural width. Furthermore, there's an operation `^|\cleartabs|' that
+resets all tab positions to the right of the current column. Therefore the
+computer program above can be \TeX ified as follows:
+\begindisplay
+|$$\vbox{\+\bf if $n<r$ \cleartabs&\bf then $n:=n+1$\cr|\cr
+| \+&\bf else &{\bf begin} ${\it print\_totals}$; $n:=0$;\cr|\cr
+| \+&&{\bf end};\cr|\cr
+| |\<The remaining part is left as an exercise>|}$$|\cr
+\enddisplay
+
+\dangerexercise Complete the example computer program by specifying three more
+|\+|~lines.
+\answer In such programs it seems best to type |\cleartabs| just before |&|,
+whenever it is desirable to reset the old tabs. Multiletter identifiers look
+best when set in ^{text italics} with ^|\it|, as explained in Chapter~18.
+Thus, the following is recommended:
+\begintt
+\+\bf while $p>0$ do\cr
+ \+\quad\cleartabs&{\bf begin} $q:={\it link}(p)$;
+ ${\it free\_node}(p)$; $p:=q$;\cr
+ \+&{\bf end};\cr
+\endtt
+
+\danger Although |\+| lines can be used in vertical boxes, you must never
+use |\+| inside of another |\+| line. The |\+| macro is intended for
+simple applications only.
+
+\ddanger The |\+| and |\settabs| macros of Appendix B keep track of tabs by
+maintaining register |\box|^|\tabs| as a box full of empty boxes whose
+widths are the column widths in reverse order. Thus you can examine the
+tabs that are currently set, by saying `^|\showbox||\tabs|'; this puts
+the column widths into your log file, from right to left. For example,
+after `|\settabs\+\hskip100pt&\hskip200pt&\cr\showbox\tabs|', \TeX\
+will show the lines
+\begintt
+\hbox(0.0+0.0)x300.0
+.\hbox(0.0+0.0)x200.0
+.\hbox(0.0+0.0)x100.0
+\endtt
+
+\ddangerexercise Study the |\+| macro in Appendix B and figure out how to
+change it so that tabs work as they do on a mechanical typewriter
+(i.e., so that `|&|' always moves to the next tab that lies strictly
+to the right of the current position). Assume that the user
+doesn't backspace past previous tab positions; for example, if the input is
+\hbox{`|\+&&\hskip-2em&x\cr|'}, do not bother to put `x' in the first or
+second column, just put it at the beginning of the third column. \
+(This exercise is a bit difficult.)
+\answer Here we retain the idea that |&| inserts a new tab, when there
+are no tabs to the right of the current position. Only one of the macros
+that are used to process |\+|~lines needs to be changed; but
+(unfortunately) it's the most complex one:
+\begintt
+\def\t@bb@x{\if@cr\egroup % now \box0 holds the column
+ \else\hss\egroup \dimen@=0\p@
+ \dimen@ii=\wd0 \advance\dimen@ii by1sp
+ \loop\ifdim \dimen@<\dimen@ii
+ \global\setbox\tabsyet=\hbox{\unhbox\tabsyet
+ \global\setbox1=\lastbox}%
+ \ifvoid1 \advance\dimen@ii by-\dimen@
+ \advance\dimen@ii by-1sp \global\setbox1
+ =\hbox to\dimen@ii{}\dimen@ii=-1pt\fi
+ \advance\dimen@ by\wd1 \global\setbox\tabsdone
+ =\hbox{\box1\unhbox\tabsdone}\repeat
+ \setbox0=\hbox to\dimen@{\unhbox0}\fi
+ \box0}
+\endtt
+
+\danger \TeX\ has another important way to make tables, using an operation
+called ^|\halign| (``horizontal alignment''). In this case the table format
+is based on the notion of a {\sl^{template}}, not on tabbing; the idea
+is to specify a separate environment for the text in each column.
+Individual entries are inserted into their templates, and presto, the
+table is complete.
+
+\danger For example, let's go back to the Horizontal/Vertical/Math list
+example that appeared earlier in this chapter; we can specify it with
+|\halign| instead of with tabs. The new specification is
+\begintt
+\halign{\indent#\hfil&\quad#\hfil\cr
+ Horizontal lists&Chapter 14\cr
+ Vertical lists&Chapter 15\cr
+ Math lists&Chapter 17\cr}
+\endtt
+and it produces exactly the same result as the old one. This example
+deserves careful study, because |\halign| is really quite simple once
+you get the hang of it. The first line contains the {\sl ^{preamble}\/} to
+the alignment, which is something like the sample line used to set tabs
+for~|\+|. In this case the preamble contains two templates, namely
+`|\indent#\hfil|' for the first column and `|\quad#\hfil|' for the
+second. Each template contains exactly one appearance of `|#|', ^^{sharp}
+and it means ``stick the text of each column entry in this place.''
+Thus, the first column of the line that follows the preamble becomes
+\begintt
+\indent Horizontal lists\hfil
+\endtt
+when `|Horizontal lists|' is stuffed into its template; and the second
+column, similarly, becomes `|\quad Chapter 14\hfil|'. The question is,
+why |\hfil|? Ah, now we get to the interesting point of the whole thing:
+\TeX\ reads an entire |\halign{...}| specification into its memory
+before typesetting anything, and it keeps track of the maximum width
+of each column, assuming that each column is set without stretching or
+shrinking the glue. Then it goes back and puts every entry into a box,
+setting the glue so that each box has the maximum column width. That's
+where the |\hfil| comes in; it stretches to fill up the extra space in
+narrower entries.
+
+\dangerexercise What table would have resulted if the template for the
+first column in this example had been `|\indent\hfil#|' instead of
+`|\indent#\hfil|'?
+\answer \par\nobreak\vskip-\baselineskip
+\halign{\indent\hfil#&\quad#\hfil\cr
+Horizontal lists&Chapter 14\cr\noalign{\nobreak}
+Vertical lists&Chapter 15\cr\noalign{\nobreak}
+Math lists&Chapter 17\qquad (i.e., the first column would be
+ right-justified)\cr}
+
+\danger Before reading further, please make sure that you understand the
+idea of templates in the example just presented. There are several
+important differences between |\halign| and~|\+|: (1)~|\halign| calculates
+^^{halign compared to tabbing}
+the maximum column widths automatically; you don't have to guess what the
+longest entries will be, as you do when you set tabs with a sample line.
+(2)~Each |\halign| does its own calculation of column widths; you have to
+do something special if you want two different |\halign| operations to
+produce identical alignments. By contrast, the |\+| operation remembers tab
+positions until they are specifically reset; any number of paragraphs and
+even |\halign| operations can intervene between |\+|'s, without affecting
+the tabs. (3)~Because |\halign| reads an entire table in order to
+determine the maximum column widths, it is unsuitable for huge tables
+that fill several pages of a book. By contrast, the~|\+|~operation deals
+with one line at a time, so it places no special demands on \TeX's memory.
+\ (However, if you have a huge table, you should probably define your own
+special-purpose macro for each line instead of relying on the general
+|\+|~operation.) (4)~|\halign| takes less computer time than |\+|~does,
+because |\halign| is a built-in command of \TeX, while |\+|~is a macro
+that has been coded in terms of\/ |\halign| and various other primitive
+operations. (5)~Templates are much more versatile than tabs, and they can
+save you a lot of typing. For example, the Horizontal/Vertical/Math list
+table could be specified more briefly by noticing that there's common
+information in the columns:
+\begintt
+\halign{\indent# lists\hfil&\quad Chapter #\cr
+ Horizontal&14\cr Vertical&15\cr Math&17\cr}
+\endtt
+You could even save two more keystrokes by noting that the chapter numbers
+all start with `|1|'\thinspace! \ (Caution: It takes more time to think of
+optimizations like this than to type things in a straightforward way;
+do it only if you're bored and need something amusing to keep up
+your interest.)\ (6)~On the other hand, templates are no substitute for
+tabs when the tab positions are continually varying, as in the
+computer program example.
+
+\danger Let's do a more interesting table, to get more
+experience with |\halign|. Here is another example based on the
+^{Beck}/^{Bertholle}/^{Child} book cited earlier:
+$$\vbox{\openup2pt
+\halign{\hfil\bf#&\quad\hfil\it#\hfil&\quad\hfil#\hfil&
+ \quad\hfil#\hfil&\quad#\hfil\cr
+\sl American&\sl French&\sl Age&\sl Weight&\sl Cooking\cr
+\noalign{\vskip-2pt}
+\sl Chicken&\sl Connection&\sl(months)&\sl(lbs.)&\sl Methods\cr
+\noalign{\smallskip}
+Squab&Poussin&2&\frac3/4 to 1&Broil, Grill, Roast\cr
+Broiler&Poulet Nouveau&2 to 3&1\frac1/2 to 2\frac1/2&Broil, Grill, Roast\cr
+Fryer&Poulet Reine&3 to 5&2 to 3&Fry, Saut\'e, Roast\cr
+Roaster&Poularde&5\frac1/2 to 9&Over 3&Roast, Poach, Fricassee\cr
+Fowl&Poule de l'Ann\'ee&10 to 12&Over 3&Stew, Fricassee\cr
+Rooster&Coq&Over 12&Over 3&Soup stock, Forcemeat\cr}}$$
+Note that, except for the title lines, the first column is set right-justified
+in boldface type; the middle columns are centered; the second column
+is centered and in italics; the final column is left-justified. We would
+like to be able to type the rows of the table as simply as possible; hence,
+for example, it would be nice to be able to specify the bottom row by
+typing only
+\begintt
+Rooster&Coq&Over 12&Over 3&Soup stock, Forcemeat\cr
+\endtt
+without worrying about type styles, centering, and so on. This not only
+cuts down on keystrokes, it also reduces the chances for making typographical
+errors. Therefore the template for the first column should be
+`|\hfil\bf#|'; for the second column it should be `|\hfil\it#\hfil|' to
+get the text centered and italicized; and so on. We also need to allow
+for space between the columns, say one quad. {\it Voil\`a! La typographie
+est sur la table:\/}\looseness=-1
+\begindisplay
+|\halign{\hfil\bf#&\quad\hfil\it#\hfil&\quad\hfil#\hfil&|\cr
+| \quad\hfil#\hfil&\quad#\hfil\cr|\cr
+\ \<the title lines>\cr
+| Squab&Poussin&2&\frac3/4 to 1&Broil, Grill, Roast\cr|\cr
+| ... Forcemeat\cr}|\cr
+\enddisplay
+As with the |\+| operation, spaces are ignored after |&|, in the preamble
+as well as in the individual rows of the table. Thus, it is convenient
+to end a long row with `|&|' when the~row takes up more than one line
+in your input file.
+
+\dangerexercise How was the `{\bf Fowl}' line typed? \ (This is too easy.)
+\answer |Fowl&Poule de l'Ann\'ee&10 to 12&Over 3&Stew, Fricassee\cr|
+
+\danger The only remaining problem in this example is to specify the title
+lines, which have a different format from the others. In this case the style
+is different only because the typeface is slanted, so there's no special
+difficulty; we just type
+\begintt
+\sl American&\sl French&\sl Age&\sl Weight&\sl Cooking\cr
+\sl Chicken&\sl Connection&\sl(months)&\sl(lbs.)&\sl Methods\cr
+\endtt
+It is necessary to say `|\sl|' each time, because each individual entry
+of a table is implicitly enclosed in braces.
+
+\danger The author used `^|\openup||2pt|' to increase the distance between
+baselines in the ^{poultry} table; a discriminating reader will notice
+that there's also a bit of extra space between the title line and the
+other lines. This extra space was inserted by typing
+`^|\noalign||{\smallskip}|' just after the title line. In general, you can say
+\begindisplay
+|\noalign{|\<vertical mode material>|}|
+\enddisplay
+just after any |\cr| in an |\halign|; \TeX\ will simply copy the vertical
+mode material, without subjecting it to alignment, and it will appear
+in place when the |\halign| is finished. You can use |\noalign| to
+insert extra space, as here, or to insert penalties that affect page
+breaking, or even to insert lines of text (see Chapter~19). Definitions
+inside the braces of\/ |\noalign{...}| are local to that group.
+
+\danger The |\halign| command also makes it possible for you to adjust
+the spacing between columns so that a table will fill a specified area.
+You don't have to decide that the ^{inter-column space} is a quad; you can
+let \TeX\ make the decisions, based on how wide the columns come out,
+because \TeX\ puts ``^{tabskip glue}'' between columns. This tabskip glue
+is usually zero, but you can set it to any value you like by saying
+`^|\tabskip||=|\<glue>'. For example,
+let's do the poultry table again, but with the beginning of the
+specification changed as follows:
+\begintt
+\tabskip=1em plus2em minus.5em
+\halign to\hsize{\hfil\bf#&\hfil\it#\hfil&\hfil#\hfil&
+ \hfil#\hfil&#\hfil\cr
+\endtt
+The main body of the table is unchanged, but the |\quad| spaces have been
+removed from the preamble, and a nonzero |\tabskip| has been specified
+instead. Furthermore `|\halign|' has been changed to `|\halign
+to\hsize|'; this means that each line of the table will be put into a
+box whose width is the current value of\/ ^|\hsize|, i.e., the horizontal
+line width usually used in paragraphs. The resulting table looks like this:
+$$\vbox{\openup2pt
+\tabskip=1em plus2em minus.5em
+\halign to\hsize{\hfil\bf#&\hfil\it#\hfil&\hfil#\hfil&
+ \hfil#\hfil&#\hfil\cr
+\sl American&\sl French&\sl Age&\sl Weight&\sl Cooking\cr
+\noalign{\vskip-2pt}
+\sl Chicken&\sl Connection&\sl(months)&\sl(lbs.)&\sl Methods\cr
+\noalign{\smallskip}
+Squab&Poussin&2&\frac3/4 to 1&Broil, Grill, Roast\cr
+Broiler&Poulet Nouveau&2 to 3&1\frac1/2 to 2\frac1/2&Broil, Grill, Roast\cr
+Fryer&Poulet Reine&3 to 5&2 to 3&Fry, Saut\'e, Roast\cr
+Roaster&Poularde&5\frac1/2 to 9&Over 3&Roast, Poach, Fricassee\cr
+Fowl&Poule de l'Ann\'ee&10 to 12&Over 3&Stew, Fricassee\cr
+Rooster&Coq&Over 12&Over 3&Soup stock, Forcemeat\cr}}$$
+
+\danger In general, \TeX\ puts tabskip glue before the first column, after
+the last column, and between the columns of an alignment. You can specify
+the final aligned size by saying `|\halign to|\<dimen>' or
+`|\halign spread|\<dimen>', ^^|to| ^^|spread|
+just as you can say `|\hbox to|\<dimen>' and `|\hbox spread|\<dimen>'.
+This specification governs the setting of the tabskip glue; but it does
+not affect the setting of the glue within column entries. \ (Those
+entries have already been packaged into boxes having the maximum
+natural width for their columns, as described earlier.)
+
+\ddanger Therefore `|\halign| |to| |\hsize|' will do nothing if the
+tabskip glue has no stretchability or shrinkability, except that it will
+cause \TeX\ to report an ^{underfull} or ^{overfull} box. An overfull box
+occurs if the tabskip glue can't shrink to meet the
+given specification; in this case you get a warning on the terminal
+and in your log file, but there is no ``^{overfull rule}'' to mark the
+oversize table on the printed output. The warning message shows a
+``^{prototype row}'' (see Chapter~27).
+
+\danger The poultry example just given used the same tabskip glue
+everywhere, but you can vary it by resetting ^|\tabskip| within the
+preamble. The tabskip glue that is in force when \TeX\ reads the
+`|{|' following |\halign| will be used before the first column;
+the tabskip glue that is in force when \TeX\ reads the `|&|' after
+the first template will be used between the first and second
+columns; and so on. The tabskip glue that is in force when \TeX\
+reads the |\cr| after the last template will be used after the
+last column. For example, in
+\begintt
+\tabskip=3pt
+\halign{\hfil#\tabskip=4pt& #\hfil&
+ \hbox to 10em{\hss\tabskip=5pt # \hss}\cr ...}
+\endtt
+the preamble specifies aligned lines that will consist of the following
+seven parts:
+\begindisplay
+tabskip glue $3\pt$;\cr
+first column, with template `|\hfil#|';\cr
+tabskip glue $4\pt$;\cr
+second column, with template `|#\hfil|';\cr
+tabskip glue $4\pt$;\cr
+third column, with template `|\hbox to 10em{\hss# \hss}|';\cr
+tabskip glue $5\pt$.\cr
+\enddisplay
+
+\ddanger \TeX\ copies the templates without interpreting them except to
+remove any |\tabskip| glue specifications. More precisely, the tokens of the
+preamble are passed directly to the templates without macro expansion;
+\TeX\ looks only for `|\cr|' commands, `|&|', `|#|', `|\span|', and
+`|\tabskip|'. The \<glue> following `|\tabskip|' is scanned in~the usual
+way (with macro expansion), and the corresponding tokens are not included~in
+the current template. Notice that, in the example above, the space
+after `|5pt|' also disappeared. The fact that |\tabskip=5pt| occurred
+inside an extra level of braces did not make the definition local, since
+\TeX\ didn't ``see'' those braces; similarly, if\/ |\tabskip| had been
+preceded by `|\global|', \TeX\ wouldn't have made a global definition, it
+would just have put `|\global|' into the template. All assignments to
+|\tabskip| within the preamble are local to the |\halign| (unless
+^|\globaldefs| is positive), so the value of\/ |\tabskip| will be $3\pt$ again
+when this particular |\halign| is completed.
+
+\ddanger When `^|\span|' appears in a preamble, it causes the next token
+to be expanded, i.e., ``ex-span-ded,'' before \TeX\ reads on.
+
+\def\\{{\it c\/}}
+\dangerexercise Design a preamble for the following table:
+$$\halign to\hsize{\sl#\hfil\tabskip=.5em plus.5em&
+ #\hfil\tabskip=0pt plus.5em&
+ \hfil#\tabskip=1em plus2em&
+ \sl#\hfil\tabskip=.5em plus.5em&
+ #\hfil\tabskip=0pt plus.5em&
+ \hfil#\tabskip=0pt\cr
+England&P. Philips&1560--1628&
+ Netherlands&J. P. Sweelinck&1562--1621\cr
+&J. Bull&\\1563--1628&
+ &P. Cornet&\\1570--1633\cr
+Germany&H. L. Hassler&1562--1612&
+ Italy&G. Frescobaldi&1583--1643\cr
+&M. Pr\ae torius&1571--1621&
+ Spain&F. Correa de Arauxo&\\1576--1654\cr
+France&J. Titelouze&1563--1633&
+ Portugal&M. R. Coelho&\\1555--\\1635\cr}$$
+The tabskip glue should be zero at the left and right of each line; it should be
+$1\em$ plus $2\em$ in the center; and it should be $.5\em$
+plus $.5\em$ before the names, $0\em$ plus $.5\em$ before
+the dates. Assume that the lines of the table will be specified by, e.g.,
+\begintt
+France&J. Titelouze&1563--1633&
+ Portugal&M. R. Coelho&\\1555--\\1635\cr
+\endtt
+where `|\\|' has been predefined by `|\def\\{{\it c\/}}|'.
+^^{organists}
+^^{Cornet, Peeter} ^^{Philips, Peter} ^^{Sweelinck, Jan Pieterszoon}
+^^{Bull, John} ^^{Titelouze, Jehan} ^^{Hassler, Hans Leo}
+^^{Pr\ae torius [Schultheiss], Michael} ^^{Frescobaldi, Girolamo}
+^^{Coelho, Manuel Rodrigues} % so listed in Lisbon & Rio, contrary to Groves!
+^^{Correa de Arauxo, Francisco}
+% The idea for this table came from The Organ and its Music, by Peeters and
+% Vente (Antwerp, 1971); but their data was so flaky, I'm not citing them...
+\answer |$$\halign to\hsize{\sl#\hfil\tabskip=.5em plus.5em&|\parbreak
+ | #\hfil\tabskip=0pt plus.5em&|\parbreak
+ | \hfil#\tabskip=1em plus2em&|\parbreak
+ | \sl#\hfil\tabskip=.5em plus.5em&|\parbreak
+ | #\hfil\tabskip=0pt plus.5em&|\parbreak
+ | \hfil#\tabskip=0pt\cr ...}$$|
+
+\medskip
+\ddangerexercise Design a preamble so that the table ^^{Welsh conjugation}
+$$\def\welshverb#1={{\bf#1} = }
+\halign to\hsize{\welshverb#\hfil\tabskip=1em plus1em&
+ \welshverb#\hfil&\welshverb#\hfil\tabskip=0pt\cr
+rydw i=I am&ydw i=am I&roeddwn i=I was\cr
+rwyt ti=thou art&wyt ti=art thou&roeddet ti=thou wast\cr
+mae e=he is&ydy e=is he&roedd e=he was\cr
+mae hi=she is&ydy hi=is she&roedd hi=she was\cr
+rydyn ni=we are&ydyn ni=are we&roedden ni=we were\cr
+rydych chi=you are&ydych chi=are you&roeddech chi=you were\cr
+maen nhw=they are&ydyn nhw=are they&roedden nhw=they were\cr}$$
+can be specified by typing lines like
+\begintt
+mae hi=she is&ydy hi=is she&roedd hi=she was\cr
+\endtt
+\answer The trick is to define a new macro for the preamble:
+\begintt
+$$\def\welshverb#1={{\bf#1} = }
+\halign to\hsize{\welshverb#\hfil\tabskip=1em plus1em&
+ \welshverb#\hfil&\welshverb#\hfil\tabskip=0pt\cr ...}$$
+\endtt
+
+\setbox0=\vbox{\lineskip0pt
+\tabskip=0pt plus1fil\halign to\hsize{\tabskip=0pt\strut
+\hfil#: &\vtop{\parindent=0pt\hsize=16em\hangindent.5em\strut#\strut}\cr
+\omit\hfil\sevenrm B.C.&\cr
+397&War between Syracuse and Carthage\cr
+396&Aristippus of Cyrene and An\-tis\-the\-nes of Athens (philosophers)\cr
+395&Athens rebuilds the Long Walls\cr
+394&Battles of Coronea and Cnidus\cr
+\\393&Plato's
+ {\sl Apology\/};
+ Xenophon's
+ {\sl Memorabilia\/};
+ Aristophanes'
+ {\sl Ecclesiazus\ae\/}\cr
+391--87&Dionysius subjugates south Italy\cr
+391&Isocrates opens his school\cr
+390&Evagoras Hellenizes Cyprus\cr
+387&``King's Peace''; Plato visits Ar\-chy\-tas of Taras (mathematician)
+ and Dionysius I\cr
+386&Plato founds the Academy\cr
+383&Spartans occupy Cadmeia at Thebes\cr
+380&Isocrates' {\sl Panegyricus\/}\cr}}
+
+\medskip
+\ddangerexercise \hsize=13pc
+The line breaks in the second column of the table at the right were chosen
+by \TeX\ so that the second column was exactly 16~ems wide. Furthermore,
+the author specified one of the rows of the table by typing
+$$\halign{#\hfil\cr
+|\\393&Plato's {\sl Apology\/};|\cr
+| Xenophon's|\cr
+| {\sl Memorabilia\/};|\cr
+| Aristophanes'|\cr
+| {\sl Ecclesiazus\ae\/}\cr|\cr}$$
+Can you guess what preamble was used in the alignment? \ [The data comes
+from Will ^{Durant}'s {\sl The Life of Greece\/} (Simon \& Schuster, 1939).]
+^^{Aristippus of Cyrene} ^^{Antisthenes of Athens}
+^^{Plato} ^^{Xenophon} ^^{Aristophanes} ^^{Dionysius I of Syracuse}
+^^{Isocrates} ^^{Evagoras of Salamis} ^^{Archytas of Taras}
+\strut\vadjust{\vbox to 0pt{\vss\box0\kern0pt}}% insert the aligned table
+\answer |\hfil#: &\vtop{\parindent=0pt\hsize=16em|\parbreak
+ | \hangindent.5em\strut#\strut}\cr|\par\nobreak\medskip\noindent
+With such narrow measure and such long words, the ^|\tolerance| should probably
+also have been increased to, say, 1000 inside the ^|\vtop|; luckily it turned
+^^|\strut| out that a higher tolerance wasn't needed.\par
+{\sl Note:\/} The stated preamble solves the problem and demonstrates
+that \TeX's line-breaking capability can be used within tables. But this
+particular table is not really a good example of the use of\/ |\halign|,
+because \TeX\ could typeset it directly, using ^|\everypar| in an
+appropriate manner to set up the hanging indentation, and using |\par|
+instead of\/ |\cr|. For example, one could say
+\begintt
+\hsize20em \parindent0pt \clubpenalty10000 \widowpenalty10000
+\def\history#1&{\hangindent4.5em
+ \hbox to4em{\hss#1: }\ignorespaces}
+\everypar={\history} \def\\{\leavevmode{\it c\/}}
+\endtt
+which spares \TeX\ all the work of\/ |\halign| but yields essentially the
+same result. ^^|\leavevmode|
+
+\danger Sometimes a template will apply perfectly to all but one or two of
+the entries in a column. For example, in the exercise just given, the
+colons in the first column of the alignment were supplied by the
+template `|\hfil#:|\]'; but the very first entry in that column,
+`{\sevenrm B.C.}', did not have a colon. \TeX\ allows you to escape from
+the stated template in the following way: If the very first token
+of an alignment entry is `^|\omit|' (after macro expansion), then
+the template of the preamble is omitted; the trivial template `|#|'
+is used instead. For example, `{\sevenrm B.C.}' was put into the table above
+by typing `|\omit\hfil\sevenrm B.C.|' immediately after the preamble.
+You can use |\omit| in any column, but it must come first; otherwise \TeX\
+will insert the template that was defined in the preamble.
+
+\ddanger If you think about what \TeX\ has to do when it's processing
+|\halign|, you'll realize that the timing of certain actions is critical.
+Macros are not expanded when the preamble is being read, except as
+described earlier; but once the |\cr| at the end of the preamble has been
+sensed, \TeX\ must look ahead to see if the next token is |\noalign| or
+|\omit|, and macros are expanded until the next non-space token is found.
+If the token doesn't turn out to be |\noalign| or |\omit|, it is put
+back to be read again, and \TeX\ begins to read the template (still
+expanding macros). The template has two parts, called the $u$ and~$v$ parts,
+where $u$~precedes the~`|#|' and $v$~follows~it. When \TeX\ has finished
+the $u$~part, its reading mechanism goes back to the token that was
+neither |\noalign| nor |\omit|, and continues to read the entry until
+getting to the |&| or~|\cr| that ends the entry; then the $v$~part of
+the template is read. A special internal operation called ^|\endtemplate|
+is always placed at the end of the $v$~part; this causes \TeX\ to put
+the entry into an ``^{unset box}'' whose glue will be set later when
+the final column width is known. Then \TeX\ is ready for another entry;
+it looks ahead for |\omit| (and also for |\noalign|, after~|\cr|) and
+the process continues in the same way.
+
+\ddanger One consequence of the process just described is that it may be
+dangerous to begin an entry of an alignment with |\if...|, ^^{conditionals}
+or with any macro that will expand into a replacement text whose first token
+is |\if...|; the reason is that the condition will be evaluated before the
+template has been read. \ (\TeX\ is still looking to see whether an |\omit|
+will occur, when the |\if| is being expanded.) \ For example, if\/ ^|\strut|
+has been defined to be an abbreviation for
+\begindisplay
+^|\ifmmode|\<text for math modes>|\else|\<text for nonmath modes>|\fi|
+\enddisplay
+and if\/ |\strut| appears as the first token in some alignment entry,
+then \TeX\ will expand it into the \<text for nonmath modes> even though
+the template might be `|$#$|', because \TeX\ will not yet be in math
+mode when it is looking for a possible |\omit|. Chaos will
+probably ensue. Therefore the replacement text for |\strut| in
+Appendix~B is actually
+\begintt
+\relax\ifmmode...
+\endtt
+and `|\relax|' has also been put into all other macros that might suffer
+from such timing problems. Sometimes you do want \TeX\ to expand a
+conditional before a template is inserted, but careful macro designers
+watch out for cases where this could cause trouble.
+
+\newdimen\digitwidth \setbox0=\hbox{\sixrm0} \digitwidth=\wd0
+\danger When you're typesetting ^{numerical tables}, it's common practice
+to line up the ^{decimal points} in a column. For example, if two numbers
+like `0.2010' and `297.1' both appear in the same column, you're supposed
+to produce `$\catcode`?=13 \def?{\kern\digitwidth}
+??0.2010 \atop 297.1???$'. This result isn't especially pleasing to the
+eye, but that's what people do, so you might have to conform to the practice.
+One way to handle this is to treat the column as two columns, somewhat as
+|\eqalign| treats one formula as two formulas; the `.'\ can be placed at
+the beginning of the second half-column. But the author usually prefers to
+use another, less sophisticated method, which takes advantage of the fact
+that the digits 0,~1, \dots,~9 have the same width in most fonts: You can
+choose a character that's not used elsewhere in the table, say `|?|',
+and change it to an ^{active character} that produces a blank space exactly
+equal to the width of a digit. Then it's usually no chore to put such nulls
+into the table entries so that each column can be regarded as either
+centered or right-justified or left-justified. For example, `|??0.2010|'
+and `|297.1???|'\ have the same width, so their decimal points will line
+up easily. Here is one way to set up `|?|'\ for this purpose:
+\begintt
+\newdimen\digitwidth
+\setbox0=\hbox{\rm0}
+\digitwidth=\wd0
+\catcode`?=\active
+\def?{\kern\digitwidth}
+\endtt
+The last two definitions should be local to some ^{group}, e.g., inside a
+|\vbox|, so that `|?|'\ will resume its normal behavior when the table is
+finished. ^^|\active|
+
+\danger Let's look now at some applications to mathematics. Suppose first
+that you want to typeset the small table
+$$\vbox{\halign{$\hfil#$ =&&\ \hfil#\hfil\cr
+n\phantom)&0&1&2&3&4&5&6&7&8&9&10&11&12&13&14&15&16&17&18&19&20&\dots\cr
+{\cal G}(n)&1&2&4&3&6&7&8&16&18&25&32&11&64&31&128&10&256&5&512&28&
+1024&\dots\cr}}$$ % The Grundy function for SYM [cf. Winning Ways p441]
+as a ^{display}ed equation. A brute force approach using |\eqalign| or
+|\atop| is cumbersome because ${\cal G}(n)$ and $n$ don't always have the
+same number of digits. It would be much nicer to type
+\begindisplay
+|$$\vbox{\halign{|\<preamble>|\cr|\cr
+| n\phantom)&0&1&2&3& ... &20&\dots\cr|\cr
+| {\cal G}(n)&1&2&4&3& ... &1024&\dots\cr}}$$|\cr
+\enddisplay
+for some \<preamble>. On the other hand, the \<preamble> is sure to be
+long, since this table has 23 columns; so it looks as though |\settabs|
+and |\+| will be easier. \TeX\ has a handy feature that helps a lot
+in cases like this: Preambles often have a periodic structure,
+^^{periodic preambles} ^^{cyclic preambles} ^^{ampersand ampersand}
+and if you put an extra `|&|' ^^{ampersand} just before one of the templates,
+\TeX\ will consider the preamble to be an infinite sequence that begins
+again at the marked template when the |\cr| is reached. For example,
+\begindisplay
+$t_1\,$|&|$\,t_2\,$|&|$\,t_3\,$|&&|$\,t_4\,$|&|$\,t_5\,$|\cr|
+\ is treated like \
+$t_1\,$|&|$\,t_2\,$|&|$\,t_3\,$|&|$\,t_4\,$|&|$\,t_5\,$%
+ |&|$\,t_4\,$|&|$\,t_5\,$|&|$\,t_4\,$|&|$\,\,\cdots\,$\cr
+\noalign{\hbox{and}}
+|&|$t_1\,$|&|$\,t_2\,$|&|$\,t_3\,$|&|$\,t_4\,$|&|$\,t_5\,$|\cr|
+\ is treated like \
+$t_1\,$|&|$\,t_2\,$|&|$\,t_3\,$|&|$\,t_4\,$|&|$\,t_5\,$%
+ |&|$\,t_1\,$|&|$\,t_2\,$|&|$\,t_3\,$|&|$\,\,\cdots\,.$\cr
+\enddisplay
+The tabskip glue following each template is copied with that template.
+The preamble will grow as long as needed, based on the number of columns
+actually used by the subsequent alignment entries. Therefore all it takes is
+\begintt
+$\hfil#$ =&&\ \hfil#\hfil\cr
+\endtt
+to make a suitable \<preamble> for the ${\cal G}(n)$ problem.
+
+\ddanger Now suppose that the task is to typeset three pairs of displayed
+formulas, with all of the =~signs lined up: % cf. ACP Section 3.3.4
+$$\vcenter{\openup1\jot \halign{
+$\hfil#$&&${}#\hfil$&\qquad$\hfil#$\cr
+V_i&=v_i-q_iv_j,&X_i&=x_i-q_ix_j,&
+ U_i&=u_i,\qquad\hbox{for $i\ne j$};\cr
+V_j&=v_j,&X_j&=x_j,&
+ U_j&=u_j+\sum_{i\ne j}q_iu_i.\cr}}\eqno(23)$$
+It's not easy to do this with three ^|\eqalign|'s, because the $\sum$ with
+a subscript `$i\ne j$' makes the right-hand pair of formulas bigger than the
+others; the baselines won't agree unless ``^{phantoms}'' are put into the
+other two |\eqalign|'s (see Chapter~19). Instead of using |\eqalign|,
+which is defined in Appendix~B to be a macro that uses |\halign|, let's
+try to use |\halign| directly. The natural way to approach this display is
+^^|\jot| to type
+\begindisplay
+|$$\vcenter{\openup1\jot \halign{|\<preamble>|\cr|\cr
+| |\<first line>|\cr |\<second line>|\cr}}\eqno(23)$$|\cr
+\enddisplay
+because the ^|\vcenter| puts the lines into a box that is properly centered
+with respect to the equation number `(23)'; the ^|\openup| macro puts a
+bit of extra space between the lines, as mentioned in Chapter~19.
+
+\ddanger OK, now let's figure out how to type the \<first line> and \<second
+line>. The usual convention is to put `|&|' before the symbols that we want
+to line up, so the obvious solution is to type
+\begintt
+V_i&=v_i-q_iv_j,&X_i&=x_i-q_ix_j,&
+ U_i&=u_i,\qquad\hbox{for $i\ne j$};\cr
+V_j&=v_j,&X_j&=x_j,&
+ U_j&=u_j+\sum_{i\ne j}q_iu_i.\cr
+\endtt
+Thus the alignment has six columns. We could take common elements into
+the preamble (e.g., `|V_|' and `|=v_|'), but that would be too error-prone
+and too tricky.
+
+\ddanger The remaining problem is to construct a preamble to
+support those lines. To the left of the =~signs we want the column to
+be filled at the left; to the right of the =~signs we want it to be filled
+at the right. There's a slight complication because we are breaking a math
+formula into two separate pieces, yet we want the result to have the same
+spacing as if it were one formula. Since we're putting the `|&|' just before
+a relation, the solution is to insert `|{}|' ^^{lbrace rbrace}
+at the beginning of the right-hand formula; \TeX\ will put the proper
+space before the equals sign in `|${}=...$|', but it puts no space before
+the equals sign in `|$=...$|'. Therefore the desired \<preamble> is
+\begintt
+$\hfil#$&${}#\hfil$&
+ \qquad$\hfil#$&${}#\hfil$&
+ \qquad$\hfil#$&${}#\hfil$
+\endtt
+The third and fourth columns are like the first and second, except for
+the |\qquad| that separates the equations; the fifth and sixth columns
+are like the third and fourth. Once again we can use the handy `|&&|'
+shortcut ^^{ampersand ampersand} to reduce the preamble to
+\begintt
+$\hfil#$&&${}#\hfil$&\qquad$\hfil#$
+\endtt
+With a little practice you'll find that it becomes easy to compose
+preambles as you are typing a manuscript that needs them. However, most
+manuscripts don't need them, so it may be a~while before you acquire even
+a little practice in this regard.
+
+\ddangerexercise Explain how to produce the following display:
+$$\openup1\jot \tabskip=0pt plus1fil
+\halign to\displaywidth{\tabskip=0pt
+ $\hfil#$&$\hfil{}#{}$&
+ $\hfil#$&$\hfil{}#{}$&
+ $\hfil#$&$\hfil{}#{}$&
+ $\hfil#$&${}#\hfil$\tabskip=0pt plus1fil&
+ \llap{#}\tabskip=0pt\cr
+10w&+&3x&+&3y&+&18z&=1,&(9)\cr
+6w&-&17x&&&-&5z&=2.&(10)\cr}$$ % cf. ACP Eqs. 4.5.2-17,18
+\answer The equation is divided into separate parts for terms and
+plus/minus signs, and tabskip glue is used to center it:
+\begintt
+$$\openup1\jot \tabskip=0pt plus1fil
+\halign to\displaywidth{\tabskip=0pt
+ $\hfil#$&$\hfil{}#{}$&
+ $\hfil#$&$\hfil{}#{}$&
+ $\hfil#$&$\hfil{}#{}$&
+ $\hfil#$&${}#\hfil$\tabskip=0pt plus1fil&
+ \llap{#}\tabskip=0pt\cr
+10w&+&3x&+&3y&+&18z&=1,&(9)\cr
+6w&-&17x&&&-&5z&=2.&(10)\cr}$$
+\endtt
+
+\ddanger The next level of complexity occurs when some entries of a table
+span two or more columns. \TeX\ provides two ways to handle this. First
+^^{spanned columns in tables}
+there's ^|\hidewidth|, which plain \TeX\ defines to be equivalent to
+\begintt
+\hskip-1000pt plus 1fill
+\endtt
+In other words, |\hidewidth| has an extremely negative ``natural width,''
+but it will stretch without limit. If you put |\hidewidth| at the right of
+some entry in an alignment, the effect is to ignore the width of this
+entry and to let it stick out to the right of its box. \ (Think about it;
+this entry won't be the widest one, when |\halign| figures the column
+width.) \ Similarly, if you put |\hidewidth| at the left of an entry, it will
+stick out to the left; and you can put |\hidewidth| at both left and right,
+as we'll see later.
+
+\ddanger The second way to handle table entries that span columns is to use
+the ^|\span| primitive, which can be used instead of `|&|' in any
+line of the table. \ (We've already seen that |\span| means ``expand'' in
+preambles; but outside of preambles its use is
+completely different.) \ When `|\span|' appears in place of `|&|',
+the material before and after the |\span| is processed in the ordinary
+way, but afterward it is placed into a single box instead of two boxes.
+The width of this combination box is the sum of the individual column
+widths plus the width of the tabskip glue between them; therefore the
+spanning box will line up with non-spanning boxes in other rows.
+
+\ddanger For example, suppose that there are three columns, with the
+respective templates $u_1\,$|#|$\,v_1\,$|&| $u_2\,$|#|$\,v_2\,$|&|
+$u_3\,$|#|$\,v_3$; suppose that the column widths are $w_1$, $w_2$,~$w_3$;
+suppose that $g_0$,~$g_1$, $g_2$,~$g_3$ are the tabskip glue widths after
+the glue has been set; and suppose that the line
+\begindisplay
+$a_1$|\span|$\,\,a_2$|\span|$\,\,a_3$|\cr|
+\enddisplay
+has appeared in the alignment. Then the material for
+`$u_1a_1v_1u_2a_2v_2u_3a_3v_3$' (i.e., the result `$u_1a_1v_1$' of
+column~1 followed by the results of columns 2 and~3) will be placed into
+an hbox of width $w_1+g_1+ w_2+g_2+w_3$. That hbox will be preceded by
+glue of width~$g_0$ and it will be followed by glue of width~$g_3$, in the
+larger hbox that contains the entire aligned line.
+
+\ddanger You can use ^|\omit| in conjunction with |\span|. For example,
+if we continue with the notation of the previous paragraph, the line
+\begindisplay
+|\omit|$\,a_1\,$|\span|$\,a_2\,$|\span\omit|$\,a_3\,$|\cr|
+\enddisplay
+would put the material for `$a_1u_2a_2v_2a_3$' into the hbox just considered.
+
+\ddanger It's fairly common to span several columns and to omit all their
+templates, so plain \TeX\ provides a ^|\multispan| macro that spans
+a given number of columns. For example, `|\multispan3|' expands into
+`|\omit\span\omit\span\omit|'. If the number of spanned columns is
+greater than~9, you must put it in braces, e.g., `|\multispan{13}|'.
+
+\ddanger The preceding paragraphs are rather abstract, so let's look at
+an example that shows what |\span| actually does. Suppose you type
+\begintt
+$$\tabskip=3em
+\vbox{\halign{&\hrulefill#\hrulefill\cr
+ first&second&third\cr
+ first-and-second\span\omit&\cr
+ &second-and-third\span\omit\cr
+ first-second-third\span\omit\span\omit\cr}}$$
+\endtt
+The preamble specifies arbitrarily many templates equal to
+`|\hrulefill#\hrulefill|'; the ^|\hrulefill| macro is like |\hfill|
+except that the blank space is filled with a horizontal rule. Therefore
+you can see the filling in the resulting alignment, which shows the
+spanned columns:
+$$\tabskip=3em
+\vbox{\halign{&\hrulefill#\hrulefill\cr
+ first&second&third\cr
+ first-and-second\span\omit&\cr
+ &second-and-third\span\omit\cr
+ first-second-third\span\omit\span\omit\cr}}$$
+The rules stop where the tabskip glue separates columns. You don't see
+rules in the first line, since the entries in that line were the widest
+in their columns. However, if the tabskip glue had been $1\em$ instead
+of $3\em$, the table would have looked like this:
+$$\tabskip=1em
+\vbox{\halign{&\hrulefill#\hrulefill\cr
+ first&second&third\cr
+ first-and-second\span\omit&\cr
+ &second-and-third\span\omit\cr
+ first-second-third\span\omit\span\omit\cr}}$$
+
+\ddangerexercise Consider the following table, which is called
+^{Walter's worksheet}: ^^{IRS}
+^^{Green, Walter} % from instructions to form 1040 (1982), p13
+$$\halign{\indent
+\hfil# &#\hfil&\quad#&\ \hfil#&\ \hfil#\cr
+1&Adjusted gross income\dotfill\span\omit\span&\$4,000\cr
+2&Zero bracket amount for&\cr
+ &a single individual\dotfill\span\omit&\$2,300\cr
+3&Earned income\dotfill\span\omit&\underbar{ 1,500}\cr
+4&Subtract line 3 from line 2\dotfill\span\omit\span&\underbar{ 800}\cr
+5&Add lines 1 and 4. Enter here\span\omit\span\cr
+ &and on Form 1040, line 35\dotfill\span\omit\span&\$4,800\cr}
+$$
+Define a preamble so that the following specification will produce
+Walter's worksheet.
+$$\halign{\indent#\hfil\cr
+|\halign{|\<preamble>|\cr|\cr
+| 1&Adjusted gross income\dotfill\span\omit\span&\$4,000\cr|\cr
+| 2&Zero bracket amount for&\cr|\cr
+| &a single individual\dotfill\span\omit&\$2,300\cr|\cr
+| 3&Earned income\dotfill\span\omit&\underbar{ 1,500}\cr|\cr
+| 4&Subtract line 3 from line 2\dotfill|\cr
+| \span\omit\span&\underbar{ 800}\cr|\cr
+| 5&Add lines 1 and 4. Enter here\span\omit\span\cr|\cr
+| &and on Form 1040, line 35\dotfill\span\omit\span&\$4,800\cr}|\cr
+}$$
+(The macro ^|\dotfill| is like |\hrulefill| but it fills with dots;
+the macro ^|\underbar| puts its argument into an hbox and underlines it.)
+\answer |\hfil# &#\hfil&\quad#&\ \hfil#&\ \hfil#\cr|
+
+\ddanger Notice the ``early'' appearance of\/ ^|\cr| in line~2 of the
+previous exercise. You needn't have the same number of columns in every
+line of an alignment; `|\cr|' means that there are no more columns
+in the current line.
+
+\medskip
+\ddangerexercise Explain how to typeset the ^{generic matrix}
+$\smash{\pmatrix{a_{11}&a_{12}&\ldots&a_{1n}\cr
+ a_{21}&a_{22}&\ldots&a_{2n}\cr
+ \multispan4\dotfill\cr
+ a_{m1}&a_{m2}&\ldots&a_{mn}\cr}.}$
+\answer |\pmatrix{a_{11}&a_{12}&\ldots&a_{1n}\cr|\parbreak
+ | a_{21}&a_{22}&\ldots&a_{2n}\cr|\parbreak
+ | \multispan4\dotfill\cr|\parbreak
+ | a_{m1}&a_{m2}&\ldots&a_{mn}\cr}|
+
+\bigskip\medskip
+\ddanger The presence of spanned columns adds a complication to \TeX's
+rules for calculating column widths; instead of simply choosing the
+maximum natural width of the column entries, it's also necessary to
+make sure that the sum of certain widths is big enough to accommodate
+spanned entries. So here is what \TeX\ actually does: First, if
+any pair of adjacent columns is always spanned as a unit (i.e., if
+there's a |\span| between them whenever either one is used), these
+two columns are effectively merged into one and the tabskip glue between
+them is set to zero. This reduces the problem to the case that
+every tab position actually occurs at a boundary. Let there be $n$
+columns remaining after such reductions, and for $1\le i\le j\le n$ let
+$w_{ij}$ be the maximum natural width of all entries that span columns $i$
+through~$j$, inclusive; if there are no such spanned entries, let
+$w_{ij}=-\infty$. \ (The merging of dependent columns guarantees that, for
+each~$j$, there exists $i\le j$ such that $w_{ij}>-\infty$.) \ Let $t_k$
+be the natural width of the tabskip glue between columns $k$ and~$k+1$,
+for $1\le k<n$. Now the final width $w_j$ of column~$j$ is determined by
+the formula
+\begindisplay
+$\displaystyle w_j=\max_{1\le i\le j}\textstyle\bigl(w_{ij}
+ -\sum_{i\le k<j}(w_k+t_k)\bigr)$
+\enddisplay
+for $j=1$, 2, \dots, $n$ (in this order). It follows that
+$w_{ij}\le w_i+t_i+\cdots+t_{j-1}+w_j$, for all $i\le j$,
+as desired. After the widths~$w_j$ are determined, the tabskip amounts
+may have to stretch or shrink; if they shrink, $w_{ij}$ might turn out to
+be more than the final width of a box that spans columns $i$ through~$j$,
+hence the glue in such a box might shrink.
+
+\ddanger These formulas usually work fine, but sometimes they produce
+undesirable effects. For example, suppose that $n=3$, $w_{11}=w_{22}=w_{33}
+=10$, $w_{12}=w_{23}=-\infty$, and $w_{13}=100$; in other words, the columns
+by themselves are quite narrow, but there's a big wide entry that's
+supposed to span all three columns. In this case \TeX's formula makes
+$w_1=w_2=10$ but $w_3=80-t_1-t_2$, so all the excess width is allocated
+to the third column. If that's not what you want, the remedy is~to use
+^|\hidewidth|, or to increase the natural width of the tabskip glue
+between columns.
+
+\ddanger The next level of complexity that occurs in tables is the
+appearance of horizontal and vertical ruled lines. People who know
+how to make ^{ruled tables} are generally known as \TeX\ Masters.
+^^{TeX Masters} Are you ready?
+
+\ddanger If you approach vertical rules in the wrong manner, they can be
+difficult; but there {\sl is\/} a decent way to get them into tables
+without shedding too many tears. The first step is to say
+`^|\offinterlineskip|', which means that there will be no blank space
+between lines; \TeX\ cannot be allowed to insert ^{interline glue} in its
+normal clever way, because each line is supposed to contain a ^|\vrule|
+that abuts another ^|\vrule| in the neighboring lines above and/or below.
+We will put a strut into every line, by including one in the preamble;
+then each line will have the proper height and depth, and there will be no
+need for interline glue. \TeX\ puts every column entry of an alignment into an
+hbox whose height and depth are set equal to the height and depth of the
+entire line; therefore |\vrule| commands will extend to the top and bottom
+of the lines even when their height and/or depth are not specified.
+
+\ddanger A ``column'' should be allocated to every vertical rule, and such
+a column can be assigned the template `|\vrule#|'. Then you obtain a
+vertical rule by simply leaving the column entries blank, in the normal
+lines of the alignment; or you can say `|\omit|' if you want to omit the
+rule in some line; or you can say `|height 10pt|' if you want a
+nonstandard height; and so on.
+
+\ddanger Here is a small table that illustrates the points just made.
+\ [The data appeared in an article by A. H. ^{Westing}, {\sl BioScience\/
+\bf31} (1981), 523--524.]
+\def\BC{\hbox to2em{ \sc B.C.\hss}}%
+\def\AD{\hbox to2em{ \sc A.D.\hss}}%
+$$\hbox to\hsize{\vbox{\halign{\indent#\hfil\cr
+|\vbox{\offinterlineskip|\cr
+|\hrule|\cr
+|\halign{&\vrule#&|\cr
+| \strut\quad\hfil#\quad\cr|\cr
+|height2pt&\omit&&\omit&\cr|\cr
+|&Year\hfil&&World Population&\cr|\cr
+|height2pt&\omit&&\omit&\cr|\cr
+|\noalign{\hrule}|\cr
+|height2pt&\omit&&\omit&\cr|\cr
+|&8000\BC&&5,000,000&\cr|\cr
+|&50\AD&&200,000,000&\cr|\cr
+|&1650\AD&&500,000,000&\cr|\cr
+|&1850\AD&&1,000,000,000&\cr|\cr
+|&1945\AD&&2,300,000,000&\cr|\cr
+|&1980\AD&&4,400,000,000&\cr|\cr
+|height2pt&\omit&&\omit&\cr}|\cr
+|\hrule}|\cr
+}}\hfill\vbox{\offinterlineskip
+\halign{&\vrule#&
+ \strut\quad\hfil#\quad\cr
+\multispan5\hrulefill\cr
+height2pt&\omit&&\omit&\cr
+&Year\hfil&&World Population&\cr
+height2pt&\omit&&\omit&\cr
+\multispan5\hrulefill\cr
+height2pt&\omit&&\omit&\cr
+&8000\BC&&5,000,000&\cr
+&50\AD&&200,000,000&\cr
+&1650\AD&&500,000,000&\cr
+&1850\AD&&1,000,000,000&\cr
+&1945\AD&&2,300,000,000&\cr
+&1980\AD&&4,400,000,000&\cr
+height2pt&\omit&&\omit&\cr
+\multispan5\hrulefill\cr}}}$$
+In this example the first, third, and fifth columns are reserved for vertical
+rules. Horizontal rules are obtained by saying `^|\hrule|' outside the
+|\halign| or `^|\noalign||{\hrule}|' inside it, because the |\halign| appears
+in a vbox whose width is the full table width. The horizontal
+rules could also have been specified by saying `^|\multispan||5\hrulefill|'
+inside the |\halign|, since that would produce a rule that spans all
+five columns.
+
+\ddanger The only other nonobvious thing about this table is the
+inclusion of several lines that say
+`|height2pt&\omit&&\omit&\cr|'; can you see what they do? The |\omit|
+instructions mean that there's no numerical information, and they
+also suppress the ^|\strut| from the line; the `|height2pt|' makes the
+first |\vrule| $2\pt$ high, and the other two rules will follow suit.
+Thus, the effect is to extend the vertical rules by two points, where
+they touch the horizontal rules. This is a little touch that improves
+the appearance of boxed tables; look for it as a mark of quality.
+
+\ddangerexercise Explain why the lines of this table say `|&\cr|' instead of
+just `|\cr|'.
+\answer `|\cr|' would have omitted the final column, which is a vertical rule.
+
+\ddanger Another way to get vertical rules into tables is to typeset without
+them, then back up (using negative glue) and insert them.
+
+\ddanger Here is another table; this one has become a classic, ever since
+Michael ^{Lesk} published it as one of the first examples in his report
+on a program to format tables [Bell Laboratories Computing Science
+Technical Report {\bf 49} (1976)]. It illustrates several typical
+problems that arise in connection with boxed information. In order to
+demonstrate \TeX's ability to adapt a table to different circumstances,
+tabskip glue is used here to adjust the column widths; the table appears
+twice, once generated by `|\halign|~|to125pt|' and once by
+`|\halign|~|to200pt|', with nothing else changed. ^^{AT\&T}
+$$\hbox to\hsize{%
+\vbox{\tabskip=0pt \offinterlineskip
+\def\tablerule{\noalign{\hrule}}
+\halign to125pt{\strut#&\vrule#\tabskip=1em plus2em&
+ \hfil#&\vrule#&\hfil#\hfil&\vrule#&
+ \hfil#&\vrule#\tabskip=0pt\cr\tablerule
+&&\multispan5\hfil AT\&T Common Stock\hfil&\cr\tablerule
+&&\omit\hidewidth Year\hidewidth&&
+ \omit\hidewidth Price\hidewidth&&
+ \omit\hidewidth Dividend\hidewidth&\cr\tablerule
+&&1971&&41--54&&\$2.60&\cr\tablerule
+&& 2&&41--54&&2.70&\cr\tablerule
+&& 3&&46--55&&2.87&\cr\tablerule
+&& 4&&40--53&&3.24&\cr\tablerule
+&& 5&&45--52&&3.40&\cr\tablerule
+&& 6&&51--59&&.95\rlap*&\cr\tablerule
+\noalign{\smallskip}
+&\multispan7* (first quarter only)\hfil\cr
+}}\hfil
+\vbox{\tabskip=0pt \offinterlineskip
+\def\tablerule{\noalign{\hrule}}
+\halign to200pt{\strut#&\vrule#\tabskip=1em plus2em&
+ \hfil#&\vrule#&\hfil#\hfil&\vrule#&
+ \hfil#&\vrule#\tabskip=0pt\cr\tablerule
+&&\multispan5\hfil AT\&T Common Stock\hfil&\cr\tablerule
+&&\omit\hidewidth Year\hidewidth&&
+ \omit\hidewidth Price\hidewidth&&
+ \omit\hidewidth Dividend\hidewidth&\cr\tablerule
+&&1971&&41--54&&\$2.60&\cr\tablerule
+&& 2&&41--54&&2.70&\cr\tablerule
+&& 3&&46--55&&2.87&\cr\tablerule
+&& 4&&40--53&&3.24&\cr\tablerule
+&& 5&&45--52&&3.40&\cr\tablerule
+&& 6&&51--59&&.95\rlap*&\cr\tablerule
+\noalign{\smallskip}
+&\multispan7* (first quarter only)\hfil\cr}}}$$
+The following specification did the job:
+\begindisplay
+|\vbox{\tabskip=0pt \offinterlineskip|\cr
+|\def\tablerule{\noalign{\hrule}}|\cr
+|\halign to|\<dimen>|{\strut#& \vrule#\tabskip=1em plus2em&|\cr
+| \hfil#& \vrule#& \hfil#\hfil& \vrule#&|\cr
+| \hfil#& \vrule#\tabskip=0pt\cr\tablerule|\cr
+|&&\multispan5\hfil AT\&T Common Stock\hfil&\cr\tablerule|\cr
+|&&\omit\hidewidth Year\hidewidth&&|\cr
+| \omit\hidewidth Price\hidewidth&&|\cr
+| \omit\hidewidth Dividend\hidewidth&\cr\tablerule|\cr
+|&&1971&&41--54&&\$2.60&\cr\tablerule|\cr
+|&& 2&&41--54&&2.70&\cr\tablerule|\cr
+|&& 3&&46--55&&2.87&\cr\tablerule|\cr
+|&& 4&&40--53&&3.24&\cr\tablerule|\cr
+|&& 5&&45--52&&3.40&\cr\tablerule|\cr
+|&& 6&&51--59&&.95\rlap*&\cr\tablerule \noalign{\smallskip}|\cr
+|&\multispan7* (first quarter only)\hfil\cr}}|\cr
+\enddisplay
+Points of interest are: (1)~The first column contains a strut; otherwise
+it would have been necessary to put a strut on the lines that say
+`AT\&T' and `(first quarter only)', since those lines omit the templates
+of all other columns that might have a built-in strut. (2)~`^|\hidewidth|'
+is used in the title line so that the width of columns will be affected
+only by the width of the numeric data. (3)~`^|\rlap|' is used so that
+the asterisk doesn't affect the alignment of the numbers. (4)~If the
+tabskip specification had been `|0em plus3em|' instead of `|1em plus2em|',
+the alignment wouldn't have come out right, because `AT\&T Common Stock'
+would have been wider than the natural width of everything it spanned; the
+excess width would all have gone into the `Dividend' column.
+
+\ddangerexercise Explain how to add $2\pt$ more space above and below
+`AT\&T Common Stock'.
+\answer One way is to include two lines just before and after the title
+line, saying `|\omit&height2pt&\multispan5&\cr|'. Another way is to
+put |\bigstrut| into some column of the title line, for some appropriate
+invisible box |\bigstrut| of width zero. Either way makes the table
+look better.
+
+\ddangerexercise Typeset the following chart, making it exactly 36em wide:
+^^{family tree}
+^^{Bohning [Knuth], Louise Marie}
+^^{Ehlert [Bohning], Pauline Anna Marie}
+^^{B\"ohning, Martin John Henry}
+^^{Wischmeyer [Ehlert], Clara Louise}
+^^{Ehlert, Ernst Fred}
+^^{Blase [B\"ohning], Maria Dorothea}
+^^{B\"ohning, Jobst Heinrich}
+$$\vbox{\tabskip=0pt \offinterlineskip
+\halign to 36em{\tabskip=0pt plus1em#&
+ #\hfil&#&#\hfil&#&#\hfil&#\tabskip=0pt\cr
+&&&&&\strut J. H. B\"ohning, 1838&\cr
+&&&&\multispan3\hrulefill\cr
+&&&\strut M. J. H. B\"ohning, 1882&\vrule\cr
+&&\multispan3\hrulefill\cr
+&&\vrule&&\vrule&\strut M. D. Blase, 1840&\cr
+&&\vrule&&\multispan3\hrulefill\cr
+&\strut L. M. Bohning, 1912&\vrule\cr
+\multispan3\hrulefill\cr
+&&\vrule&&&\strut E. F. Ehlert, 1845&\cr
+&&\vrule&&\multispan3\hrulefill\cr
+&&\vrule&\strut P. A. M. Ehlert, 1884&\vrule\cr
+&&\multispan3\hrulefill\cr
+&&&&\vrule&\strut C. L. Wischmeyer, 1850&\cr
+&&&&\multispan3\hrulefill\cr
+}}$$
+\answer The trick is to have ``empty'' columns at the extreme left and right;
+then the |\hrulefill|'s are able to span the tabskip glue.
+\begintt
+$$\vbox{\tabskip=0pt \offinterlineskip
+\halign to 36em{\tabskip=0pt plus1em#&
+ #\hfil&#&#\hfil&#&#\hfil&#\tabskip=0pt\cr
+&&&&&\strut J. H. B\"ohning, 1838&\cr
+&&&&\multispan3\hrulefill\cr
+&&&\strut M. J. H. B\"ohning, 1882&\vrule\cr
+&&\multispan3\hrulefill\cr
+&&\vrule&&\vrule&\strut M. D. Blase, 1840&\cr
+&&\vrule&&\multispan3\hrulefill\cr
+&\strut L. M. Bohning, 1912&\vrule\cr
+\multispan3\hrulefill\cr
+&&\vrule&&&\strut E. F. Ehlert, 1845&\cr
+&&\vrule&&\multispan3\hrulefill\cr
+&&\vrule&\strut P. A. M. Ehlert, 1884&\vrule\cr
+&&\multispan3\hrulefill\cr
+&&&&\vrule&\strut C. L. Wischmeyer, 1850&\cr
+&&&&\multispan3\hrulefill\cr}}$$
+\endtt
+
+\ddanger If you're having trouble ^{debugging} an alignment, it sometimes
+helps to put `^|\ddt|' at the beginning and end of the templates in your
+preamble. This is an undefined control sequence that causes \TeX\ to stop,
+displaying the rest of the template. When \TeX\ stops, you can use
+|\showlists| and other commands to see what the machine thinks it's doing.
+If \TeX\ doesn't stop, you know that it never reached that part of the
+template.
+
+\ddanger It's possible to have alignments within alignments. Therefore when
+\TeX\ sees a `|&|' or `|\span|' or `|\cr|', it needs some way to decide which
+alignment is involved. The rule is that an entry ends when `|&|' or `|\span|'
+or `|\cr|' occurs at the same level of braces that was current when the
+entry began; i.e., there must be an equal number of left and right ^{braces}
+in every entry. For example, in the line
+\begintt
+\matrix{1&1\cr 0&1\cr}&\matrix{0&1\cr 0&0\cr}\cr
+\endtt
+\TeX\ will not resume the template for the first column when it is
+scanning the argument to |\matrix|, because the |&|'s and |\cr|'s in
+that argument are enclosed in braces. Similarly, |&|'s and |\cr|'s in
+the preamble do not denote the end of a template unless the resulting
+template would have an equal number of left and right braces.
+
+\ddanger You have to be careful with the use of |&| and ^|\span| and ^|\cr|,
+^^{ampersand}
+because these tokens are intercepted by \TeX's scanner even when it is
+not expanding macros. For example, if you say `|\let\x=\span|' in the
+midst of an alignment entry, \TeX\ will think that the `|\span|' ends
+the entry, so |\x| will become equal to the first token following the
+`|#|' in the template. You can hide this |\span| by putting it in
+braces; e.g., `|{\global\let\x=\span}|'. \ (And Appendix~D explains how to
+avoid |\global| here.)
+
+\ddanger Sometimes people forget the |\cr| on the last line of an
+alignment. This can cause mysterious effects, because \TeX\ is not
+clairvoyant. For example, consider the following apparently simple case:
+\begintt
+\halign{\centerline{#}\cr
+ A centered line.\cr
+ And another?}
+\endtt
+(Notice the missing |\cr|.) \ A curious thing happens here when \TeX\
+processes the erroneous line, so please pay attention. The template
+begins with `|\centerline{|', so \TeX\ starts to scan the argument to
+|\centerline|. Since there's no `|\cr|' after the question mark, the `|}|'
+after the question mark is treated as the end of the argument to
+|\centerline|, {\sl not\/} as the end of the |\halign|. \TeX\ isn't going
+to be able to finish the alignment unless the subsequent text has
+the form `|...{...\cr|'. Indeed, an entry like `|a}b{c|' is legitimate
+with respect to the template `|\centerline{#}|', since it yields
+`|\centerline{a}b{c}|'; \TeX\ is correct when it gives no error message in
+this case. But the computer's idea of the current situation is
+different from the user's, so a puzzling error message will probably occur
+a few lines later.
+
+\ddanger To help avoid such situations, there's a primitive command
+^|\crcr| that acts exactly like |\cr| except that it does nothing
+when it immediately follows a |\cr| or a |\noalign{...}|. Thus, when
+you write a macro like |\matrix|, you can safely insert |\crcr|
+at the end of the user's argument; this will cover up an error if the
+user forgot the final |\cr|, and it will cause no harm if the final
+|\cr| was present.
+
+\ddanger Are you tired of typing |\cr|? ^^{cr, avoiding}
+You can get plain \TeX\ to insert an automatic |\cr| at the end of
+each input line in the following way: ^^|\begingroup|
+\begindisplay
+|\begingroup \let\par=\cr \obeylines %|\cr
+|\halign{|\<preamble>\cr
+\ \ \ \<first line of alignment>\cr
+\ \ \ \ \dots\cr
+\ \ \ \<last line of alignment>\cr
+| }\endgroup|\cr
+\enddisplay
+This works because ^|\obeylines| makes the ASCII ^\<return> into
+an active character that uses the current meaning of\/ ^|\par|, and
+plain \TeX\ puts \<return> at the end of an input line (see Chapter~8).
+If you don't want a~|\cr| at the end of a certain line,
+just type `|%|' and the corresponding |\cr|
+will be ``commented out.'' ^^{percent} \ (This special mode doesn't
+work with ^|\+| lines, since |\+| is a macro whose argument is delimited
+by the token `|\cr|', not simply by a token that has the same meaning
+as~|\cr|. ^^{delimited arguments} But you can redefine |\+| to overcome
+this hurdle, if you want to. For example, define a macro |\alternateplus|
+that is just like |\+| except that its argument is delimited by the active
+character |^^M|; then include the command `|\let\+=\alternateplus|' as
+part of\/ |\obeylines|.)
+
+\danger The control sequence ^|\valign| is analogous to |\halign|, but
+rows and columns change r\^oles. In this case |\cr| marks the bottom of
+a column, and the aligned columns are vboxes that are put together in
+horizontal mode. The individual entries of each column are vboxed with
+depth zero (i.e., as if\/ ^|\boxmaxdepth| were zero, as explained in
+Chapter~12); the entry heights for each row of a |\valign| are maximized
+in the same fashion as the entry widths for each column of an~|\halign|
+are maximized. The ^|\noalign| operation can now be used to insert
+horizontal mode material between columns; the ^|\span| operation now
+spans rows. ^^{spanned rows in tables} People usually work with \TeX\
+at least a year before they find their first application for |\valign|;
+and then it's usually a one-row `|\valign{\vfil#\vfil\cr...}|'.
+But the general mechanism is there if you need it.
+
+\endchapter
+
+If sixteen pennies are arranged in the form of a square
+there will be the same number of pennies in every row, every column,
+and each of the two long diagonals.
+Can you do the same with twenty pennies?
+\author HENRY ERNEST ^{DUDENEY}, {\sl The Best Coin Problems\/} (1909)
+ % Strand Magazine, July 1909, page 83; answer in August 1909, page 240
+\immediate\write\ans{}
+\immediate\write\ans{\string\ansno\chapno.$\infty$:}
+\copytoblankline (Solution to Dudeney's problem.) \
+Let |\one| and |\two| be macros that produce a vertical list
+denoting one and two pennies, respectively. The problem can be
+solved with ^|\valign| as follows:
+\begintt
+\valign{\vfil#&\vfil#&\vfil#&\vfil#\cr
+ \two&\one&\one&\one\cr
+ \one&\one&\two&\one\cr
+ \one&\one&\one&\two\cr
+ \one&\two&\one&\one\cr}
+\endtt
+Since |\valign| transposes rows and columns, the result is\quad
+\setbox0=\hbox{\vbox{
+ \def\pennytop{\hbox to 24pt{\manual\char'130\hfil}}%
+ \def\pennyedge{\hbox{\manual\char'133}}%
+ \def\one{\pennytop\pennyedge}%
+ \def\two{\one\pennyedge}%
+ \baselineskip0pt\lineskip0pt\tabskip=14pt
+ \hbox{\valign{\vfil#&\vfil#&\vfil#&\vfil#\cr
+ \two&\one&\one&\one\cr
+ \one&\one&\two&\one\cr
+ \one&\one&\one&\two\cr
+ \one&\two&\one&\one\cr}}\kern-11pt}}%
+\ht0=0pt \dp0=11pt \box0.
+
+\bigskip
+
+It was she who controlled the whole of the Fifth Column.
+\author AGATHA ^{CHRISTIE}, {\sl N or M?\/} (1941) % chapter 5, part 1
+
+\eject
+ \beginchapter Chapter 23. Output Routines
+
+We investigated \TeX's page-building technique in Chapter 15, where we
+discussed the basic two-stage strategy that is used: \TeX\ gathers
+material until it has accumulated more than will fit on a page; then it
+spews out one page of data, based on what it thinks is the best breakpoint
+between pages; then it returns to gather material for the next page in
+the same way. Page numbers, headings, and similar things are attached
+after each page has been ejected, by a special sequence of \TeX\ commands
+called the current {\sl^{output routine}}.
+
+Plain \TeX\ has an output routine that takes care of ordinary jobs. It
+handles the simple things that most manuscripts require, and it also
+copes with more complicated things like the insertions made with
+^|\footnote| and ^|\topinsert|, as described in the dangerous bends of
+Chapter~15. We shall begin the present chapter by discussing how to make
+simple changes to the behavior of plain \TeX's output routine; then we
+shall turn to the details of how to define output routines that do more
+complex tasks.
+
+If you run \TeX\ without modifying the ^{plain \TeX\ format}, you get
+^^{page format, modifying}
+pages that are numbered at the bottom; and each page will be approximately
+8$1\over2$~inches wide and 11~inches tall, including 1-inch margins at
+all four sides. This format is suitable for preprints of
+technical papers, but you might well want to change it, especially if
+you are not using \TeX\ to make a preprint of a technical paper.
+
+For example, we saw in the experiments of Chapter 6 that the width of the
+material on a page can be changed by giving a different value to the
+horizontal line size, ^|\hsize|. Plain \TeX\ format says `|\hsize=6.5in|',
+in order to obtain 8.5-inch pages with 1-inch margins; you can change
+|\hsize| to whatever you want. Similarly, you can control the vertical
+size of a page by changing ^|\vsize|. Plain \TeX\ sets |\vsize=8.9in|
+(not |9in|, since |\vsize| doesn't include the space for page numbers at the
+bottom of each page); if you say `|\vsize=4in|' you will get shorter pages,
+with only 4 inches of copy per sheet. It's best not to monkey with
+|\hsize| and |\vsize| except at the very beginning of a job, or after you
+have ejected all pages from \TeX's memory.
+
+If you want your output to be positioned differently when it is ultimately
+printed, you can offset it by giving nonzero values to ^|\hoffset| and
+^|\voffset|. For example,
+\begintt
+\hoffset=.5in \voffset=1.5in
+\endtt
+will move the output half an inch to the right of its normal position, and
+1.5 inches down. You should be careful not to offset the output so much
+that it falls off the edge of the physical medium on which it is being
+printed, unless you know that such out-of-bounds activity won't cause trouble.
+
+\TeX\ is often used to typeset announcements, ^{brochures}, or other
+documents for which ^{page numbers} are inappropriate. If you say
+\begintt
+\nopagenumbers
+\endtt
+at the beginning of your manuscript, plain \TeX\ will refrain from inserting
+numbers at the bottom of each page.
+
+\danger In fact, ^|\nopagenumbers| is a special case of a much more general
+mechanism by which you can control headings and footings. The plain \TeX\
+output routine puts out a special line of text called the {\sl^{headline}\/}
+at the top of each page, and another special line of text called the
+{\sl^{footline}\/} at the bottom. The headline is normally blank, and
+the footline is normally a centered page number, but you can specify any
+headline and footline that you want by redefining the control sequences
+^|\headline| and ^|\footline|. For example, ^^|\hrulefill|
+\begintt
+\headline={\hrulefill}
+\endtt
+will put a horizontal rule at the top of every page. The basic idea is
+that plain \TeX\ puts `|\line{\the\headline}|' at the top and
+`|\line{\the\footline}|' at the bottom, with blank lines separating these
+extra lines from the other material. \ (Recall that ^|\line| is an
+abbreviation for `|\hbox to\hsize|'; hence the headline and footline are
+put into boxes as wide as the normal lines on the page itself.) \ The
+normal value of\/ |\headline| is `|\hfil|', so that no heading is visible.
+The |\nopagenumbers| macro described earlier is simply an abbreviation for
+`|\footline={\hfil}|'.
+
+\danger The normal value of\/ |\footline| is `|\hss\tenrm\folio\hss|';
+this centers the page number on a line, using font ^|\tenrm|, because
+^|\folio| is a control sequence that produces the number of the current
+page in text form.
+
+\danger The page number appears in \TeX's internal register |\count0|,
+as explained in Chapter~15, and plain \TeX\ makes ^|\pageno| an
+abbreviation for ^|\count0|. Thus you can say `|\pageno=100|' if you want
+the next page of your output to be number~100. The |\folio| macro
+converts negative page numbers to ^{roman numerals}; if your manuscript
+begins with `|\pageno=-1|', the pages will be numbered i, ii, iii, iv,
+v,~etc. In fact, Appendix~B defines |\folio| to be an abbreviation for
+^^|\romannumeral|^^|\number|
+\begintt
+\ifnum\pageno<0 \romannumeral-\pageno \else\number\pageno \fi
+\endtt
+
+\danger It is important to include the name of each font explicitly
+whenever you are defining a headline or footline, because an output routine
+in \TeX\ can come into action at somewhat unpredictable times. For example,
+suppose that |\footline| had been set to `|\hss\folio\hss|', without
+specifying |\tenrm|; then the page number would be typeset in whatever
+font happens to be current when \TeX\ decides to output a page.
+Mysterious effects can occur in such cases, because \TeX\ is typically
+in the midst of page~101 when it is outputting page~100.
+
+\dangerexercise Explain how to put ^{en-dashes} around the page numbers in a
+plain \TeX\ job. For example, `\hbox{ -- 4 -- }' should appear at the bottom
+of page~4.
+\answer |\footline={\hss\tenrm-- \folio\ --\hss}|
+
+\danger Here is an example of a headline in which the page numbers
+appear at the top. Furthermore, odd-numbered and even-numbered pages are
+treated differently:
+\begintt
+\nopagenumbers % suppress footlines
+\headline={\ifodd\pageno\rightheadline \else\leftheadline\fi}
+\def\rightheadline{\tenrm\hfil RIGHT RUNNING HEAD\hfil\folio}
+\def\leftheadline{\tenrm\folio\hfil LEFT RUNNING HEAD\hfil}
+\voffset=2\baselineskip
+\endtt
+English-language books traditionally have ^{odd-numbered pages} on the
+right and ^{even-numbered pages} on the left. Text that appears as a
+headline on several pages is often called a ``^{running head}.'' When
+you use headlines, it is generally wise to set ^|\voffset| to the
+equivalent of two lines of text, as shown in this example, so that there
+will still be a margin of one inch at the top of your output pages.
+
+\dangerexercise Suppose that you're using \TeX\ to typeset your ^{r\'esum\'e},
+which is several pages long. Explain how to define |\headline| so that
+the first page is headed by `{\bf R\'ESUM\'E}', centered in boldface type,
+while each subsequent page has a headline like this: ^^{Thor}
+$$\line{R\'esum\'e of A. U. Thor \dotfill\ Page 2}$$
+\answer |\headline={\ifnum\pageno=1 \hss\tenbf R\'ESUM\'E\hss|\parbreak
+ | \else\tenrm R\'esum\'e of A. U. Thor \dotfill\ Page \folio\fi}|
+\smallskip\noindent (You should also say |\nopagenumbers| and
+|\voffset=2\baselineskip|.)
+
+\danger If you don't change the |\vsize|, all of the headlines and footlines
+will occur in the same place regardless of the contents of the page
+between them. Thus, for example, if you are using ^|\raggedbottom| as
+explained in Chapter~15, so that pages do not always contain the same
+amount of text, the raggedness will occur above the footline; the
+footline won't move up. If you do change ^|\vsize|, the footline position
+will change correspondingly, while the headline will stay put.
+
+\ddanger The rest of this chapter is intended for people who want an
+output format that is substantially different from what plain \TeX\ provides.
+Double dangerous bends are used in all of the subsequent paragraphs, because
+you should be familiar with the rest of \TeX\ before you plunge into these
+final mysteries of the language. Chapter~22 taught you how to be a \TeX\
+Master, i.e., a person who can produce complicated tables using |\halign|
+and |\valign|; the following material will take you all the way to the
+rank of ^{Grandmaster}, i.e., a person who can design output routines.
+When you are ready for this rank, you will be pleased to discover
+that---like alignments---output routines are not really so mysterious as
+they may seem at first.
+
+\ninepoint % it's all \ddangerous from here on
+\ddanger Let's begin by recapping some of the rules at the end of Chapter~15.
+\TeX\ periodically chooses to output a page of information, by breaking
+its main vertical list at what it thinks is the best place, and at such times
+it enters internal vertical mode and begins to read the commands in the
+current |\output| routine. When the output routine begins, ^|\box255|
+contains the page that \TeX\ has completed; the output routine is supposed to
+do something with this vbox. When the output routine ends, the list of
+items that it has constructed in internal vertical mode is placed just
+before the material that follows the page break. In this way \TeX's
+page-break decisions can effectively be changed: Some or all of the material
+on the broken-off page can be removed and carried forward to the next page.
+
+\ddanger The current ^|\output| routine is defined as a token list
+parameter, just like ^|\everypar| or ^|\errhelp|, except that \TeX\
+automatically inserts a begin-group symbol~`|{|' at the beginning
+and an end-group symbol~`|}|' at the end. These ^{grouping characters}
+help to keep the output routine from interfering with what
+\TeX\ was doing when the page break was chosen; for example, an output
+routine often changes the ^|\baselineskip| when it puts a headline
+or footline on a page, and the extra ^{braces} keep this change local.
+If no |\output| routine has been specified, or if the user has said
+`|\output={}|', \TeX\ supplies its own routine, which is essentially
+equivalent to `|\output={\shipout\box255}|'; this outputs the page without
+any headline or footline, and without changing the page number.
+^^{default output routine}^^|\shipout|
+
+\ddanger \TeX's primitive command |\shipout|\<box> is what actually
+causes output. It sends the contents of the box to the |dvi| file,
+which is \TeX's main output file; after \TeX\ has finished,
+the ^|dvi| file will contain a compact device-independent encoding of
+instructions that specify exactly what should be printed. When a
+box is shipped out, \TeX\ displays the values of\/ |\count0| through
+|\count9| on your terminal, ^^|\count0| as explained in Chapter~15; these
+ten counters are also recorded in the |dvi| file, where they can be used
+to identify the page. All of the ^|\openout|, ^|\closeout|, and ^|\write|
+commands that appear inside of the \<box> are performed in their natural
+order as that box is being shipped out. Since a |\write| command
+expands macros, as explained in Chapter~21, \TeX's scanning mechanism
+might detect syntax errors while a |\shipout| is in progress. If
+^|\tracingoutput| is nonzero at the time of a |\shipout|, the contents
+of the \<box> being shipped are written into your log file in symbolic
+form. You can say |\shipout| anywhere, not only in an output routine.
+
+\ddanger The delayed aspect of\/ |\write| imposes a noteworthy restriction:
+It is necessary to be sure that all macros that might appear within the
+text of a |\write| are properly defined when a |\shipout| command is
+given. For example, the plain \TeX\ format in Appendix~B temporarily makes
+spaces active and says `|\global\let|\]|=|^|\space|'; the reason is that
+^|\obeyspaces| might be in force during a |\write| command, so a definition
+for \] as an active character should exist during the next |\shipout|,
+even though \TeX\ might no longer be making ^{spaces active} at that time.
+
+\ddanger Chapter 15 points out that \TeX\ gives special values to
+certain internal registers and parameters, in addition to |\box255|,
+just before the output routine begins. Insertions are put into their
+own vboxes, and ^|\insertpenalties| is set equal to the total number of
+heldover insertions; furthermore the ^|\outputpenalty| parameter is
+set to the value of the penalty at the current breakpoint. An output
+routine can be made to do special things when these quantities have
+special values. For example, the output routine of plain \TeX\ recognizes
+a ^|\supereject| (which ejects all held-over insertions) by the fact that
+|\supereject| causes |\outputpenalty| to be $-20000$, and by using
+|\insertpenalties| to decide if any insertions are being held over.
+
+\ddanger The default output routine, `|\shipout\box255|', illustrates one
+extreme in which nothing is put into the vertical list that is carried over
+to the next page. The other extreme is
+\begintt
+\output={\unvbox255
+ \ifnum\outputpenalty<10000 \penalty\outputpenalty\fi}
+\endtt
+which ships nothing out and puts {\sl everything\/} back onto the main
+vertical list. \ (The command `^|\unvbox||255|' takes the completed page
+out of its box, and the command `|\penalty\outputpenalty|' reinserts the
+penalty at the chosen breakpoint.) \ This makes a seamless join between
+the completed page and the subsequent material, because \TeX\ has still
+not discarded glue and penalties at the breakpoint when it invokes
+an |\output| routine; hence \TeX\ will go back and reconsider the page
+break. If the |\vsize| hasn't changed, and if all insertions have been
+held in place, the same page break will be found; but it will be found
+much faster than before, because the vertical list has already been
+constructed---the paragraphing doesn't need to be done again. Of course,
+an output routine like this makes \TeX\ spin its wheels endlessly, so
+it~is of no use except as an example of an extreme case.
+
+\ddanger To prevent such looping, your output routine should always
+make progress of some sort whenever it comes into play. If you make a mistake,
+\TeX\ may be able to help you diagnose the error, because a special
+loop-detection mechanism has been built in:
+There is an internal integer variable called ^|\deadcycles|,
+which is cleared to zero after every |\shipout| and increased by~1
+just before every |\output|. Thus, |\deadcycles| keeps track of how
+many times an output routine has been initiated since the most recent
+|\shipout|, unless you change the value of\/ |\deadcycles| yourself.
+There's also an integer parameter called |\maxdeadcycles|, which plain
+\TeX\ sets to~25. If\/ |\deadcycles| is greater than or equal to
+|\maxdeadcycles| when your output routine is about to be started (i.e.,
+when |\deadcycles| is about to be increased), \TeX\ issues an error
+message and performs the ^{default output routine} instead of yours.
+
+\ddanger When your output routine is finished, |\box255| should be void.
+In other words, you must do something with the information in that
+box; it should either be shipped out or put into some other place.
+Similarly, ^|\box255| should be void when \TeX\ is getting ready to
+fill it with a new page of material, just before starting an output
+routine. If\/ |\box255| is nonvoid at either of those times, \TeX\ will
+complain that you are misusing this special register, and the
+register contents will be destroyed.
+
+\ddanger But let's not talk forever about borderline cases and special
+parameters; let's look at some real examples. The output routine of
+plain \TeX, found in Appendix~B\null, is set up by saying
+`|\output={\plainoutput}|', where ^|\plainoutput| is an abbreviation for
+\begintt
+\shipout\vbox{\makeheadline
+ \pagebody
+ \makefootline}
+\advancepageno
+\ifnum\outputpenalty>-20000 \else\dosupereject\fi
+\endtt
+Let us consider this ``program'' one line at a time:\enddanger
+
+\medskip
+\textindent{1)} The ^|\makeheadline| macro constructs a vbox of height
+and depth zero in such a way that the headline is properly positioned
+above the rest of the page. Its actual code is ^^|\headline|^^|\nointerlineskip|
+\begintt
+\vbox to 0pt{\vskip-22.5pt
+ \line{\vbox to8.5pt{}\the\headline}\vss}
+\nointerlineskip
+\endtt
+The magic constant $-22.5\pt$ is equal to $\bigl(\hbox{topskip}-\hbox{height of
+strut}-2(\hbox{baselineskip})\bigr)$, i.e., $10\pt-8.5\pt-24\pt$;
+^^{strut}^^|\vss| ^^|\line| this places the reference point of the headline
+exactly $24\pt$ above the reference point of the top line on the page,
+unless the headline or the top line are excessively large.
+
+\medbreak
+\textindent{2)} The ^|\pagebody| macro is an abbreviation for
+\begintt
+\vbox to\vsize{\boxmaxdepth=\maxdepth \pagecontents}
+\endtt
+The value of\/ ^|\boxmaxdepth| is set to ^|\maxdepth| so that the vbox
+will be constructed under the assumptions that \TeX's page builder has
+used to set up |\box255|.
+
+\medbreak
+\textindent{3)} The ^|\pagecontents| macro produces a vertical list for
+everything that belongs on the main body of the page, namely the contents
+of\/ |\box255| together with illustrations (inserted at the top)
+and footnotes (inserted at the bottom): ^^|\topins| ^^|\footnote|
+\begintt
+\ifvoid\topins \else\unvbox\topins\fi
+\dimen0=\dp255 \unvbox255
+\ifvoid\footins\else % footnote info is present
+ \vskip\skip\footins
+ \footnoterule
+ \unvbox\footins\fi
+\ifraggedbottom \kern-\dimen0 \vfil \fi
+\endtt
+Here ^|\topins| and ^|\footins| are the insertion class numbers for
+the two kinds of insertions used in plain \TeX; if more classes of
+^{insertions} are added, |\pagecontents| should be changed accordingly.
+Notice that the boxes are unboxed so that the glue coming from
+insertions can help out the glue on the main page. The ^|\footnoterule|
+macro in Appendix~B places a dividing line between the page and its
+footnotes; it makes a net contribution of $0\pt$ to the height of
+the vertical list. ^{Ragged-bottom setting} is achieved by inserting ^^|\vfil|
+^{infinite glue}, which overpowers the stretchability of\/ ^|\topskip|.
+
+\medbreak
+\textindent{4)} The ^|\makefootline| macro puts ^|\footline| into
+its proper position:
+\begintt
+\baselineskip=24pt
+\line{\the\footline}
+\endtt
+
+\medbreak
+\textindent{5)} The ^|\advancepageno| macro normally advances ^|\pageno|
+by~$+1$; but if\/ |\pageno| is negative (for roman numerals), the advance is
+by~$-1$. The new value of\/ |\pageno| will be appropriate for the next time
+the output routine is called into action. ^^|\global|^^|\advance|
+\begintt
+\ifnum\pageno<0 \global\advance\pageno by-1
+\else \global\advance\pageno by 1 \fi
+\endtt
+
+\medbreak
+\textindent{6)} Finally, the ^|\dosupereject| macro is designed to clear
+out any insertions that have been held over, whether they are illustrations
+or footnotes or both: ^^|\insertpenalties| ^^|\supereject|
+\begintt
+\ifnum\insertpenalties>0
+ \line{} \kern-\topskip \nobreak
+ \vfill\supereject\fi
+\endtt
+The mysterious negative ^|\kern| here cancels out the natural space of the
+^|\topskip| glue that goes above the empty |\line|; that empty line box
+prevents the ^|\vfill| from disappearing into a page break. The vertical
+list that results from |\dosupereject| is placed on \TeX's list of things
+to put out next, just after the straggling insertions have been
+reconsidered as explained in Chapter~15. Hence another super-eject will
+occur, and the process will continue until no insertions remain.
+
+\ddangerexercise Explain how to change the output routine of plain \TeX\
+so that it will produce twice as many pages. The material that would
+ordinarily go on pages 1,~2, 3,~etc., should go onto pages 1,~3, 5,~\dots;
+and the even-numbered pages should be entirely blank except for the
+headline and footline. \ (Imagine that photographs will be mounted
+on those blank pages later.)
+\answer |\output={\plainoutput\blankpageoutput}|\parbreak
+ |\def\blankpageoutput{\shipout\vbox{\makeheadline|\parbreak
+ | \vbox to\vsize{}\makefootline}\advancepageno}|
+
+\ddanger Suppose now that ^{double-column} format is desired.
+More precisely, let's attempt to modify plain \TeX\ so that it sets type
+in columns whose width is ^|\hsize||=3.2in|. Each actual page of
+output should contain two such columns separated by $0.1\rm\,in$ of
+space; thus the text area of each page will still be 6.5~inches wide.
+The headlines and footlines should span both columns, but the columns
+themselves should contain independent insertions as if they were the
+facing pages of a book. In other words, each column should contain
+its own footnotes and its own illustrations; we do not have to change
+the ^|\pagebody| macro. ^^{two-column format} ^^{multicolumn format}
+
+\ddanger In order to solve this problem, let us first introduce a
+new dimension register called ^|\fullhsize| that represents the
+width of an entire page.
+\begintt
+\newdimen\fullhsize
+\fullhsize=6.5in \hsize=3.2in
+\def\fullline{\hbox to\fullhsize}
+\endtt
+The ^|\makeheadline| and ^|\makefootline| macros should be modified
+so that they use `^|\fullline|' instead of `^|\line|'.
+
+\ddanger The new output routine will make use of a control sequence
+|\lr| that is set to either `|L|' or `|R|', according as the
+next column belongs at the left or at the right of the next page.
+When a left column has been completed, the output routine simply
+saves it in a box register; when a right column has been completed,
+the routine outputs both columns and increases the page number.
+^^|\advancepageno|
+\begintt
+\let\lr=L \newbox\leftcolumn
+\output={\if L\lr
+ \global\setbox\leftcolumn=\columnbox \global\let\lr=R
+ \else \doubleformat \global\let\lr=L\fi
+ \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
+\def\doubleformat{\shipout\vbox{\makeheadline
+ \fullline{\box\leftcolumn\hfil\columnbox}
+ \makefootline}
+ \advancepageno}
+\def\columnbox{\leftline{\pagebody}}
+\endtt
+The |\columnbox| macro uses |\leftline| in order to ensure that it produces a
+box whose width is |\hsize|. The width of\/ |\box255| is usually, but not
+always, equal to |\hsize| at the beginning of an output routine; any other
+width would louse up the format.
+
+\ddanger When double-column setting ends, there's a 50-50 chance that the
+final column has fallen at the left, so it will not yet have been output.
+The code
+\begintt
+\supereject
+\if R\lr \null\vfill\eject\fi
+\endtt
+supplies an empty right-hand column in this case, ensuring that all
+of the accumulated material will be printed. It's possible to do
+fancier column balancing on the last page, but the details are tricky
+if footnotes and other insertions need to be accommodated as well.
+Appendix~E includes the macros that were used to balance the columns at the
+end of the index in Appendix~I\null, and to start two-column format in mid-page.
+
+\ddangerexercise How should the example above be modified if you want
+^{three-column output}?
+\answer Set |\hsize=2.1in|, allocate `|\newbox\midcolumn|', and use the
+following code:
+\begintt
+\output={\if L\lr
+ \global\setbox\leftcolumn=\columnbox \global\let\lr=M
+ \else\if M\lr
+ \global\setbox\midcolumn=\columnbox \global\let\lr=R
+ \else \tripleformat \global\let\lr=L\fi\fi
+ \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
+\def\tripleformat{\shipout\vbox{\makeheadline
+ \fullline{\box\leftcolumn\hfil\box\midcolumn\hfil\columnbox}
+ \makefootline}
+ \advancepageno}
+\endtt
+At the end, |\supereject| and say `|\if L\lr \else\null\vfill\eject\fi|'
+twice.
+
+\ddanger Since \TeX's output routine lags behind its page-construction
+activity, you can get erroneous results if you change the |\headline| or the
+|\footline| in an uncontrolled way. For example, suppose that you are
+typesetting a book, and that the format you are using allows chapters
+to start in the middle of a page; then it would be a mistake to change
+the ^{running headline} at the moment you begin a new chapter, since the
+next actual page of output might not yet include anything from the new
+chapter. Consider also the task of typesetting a dictionary or a
+membership roster; a well-designed reference book displays the current range
+of entries at the top of each page or pair of pages, so that it is
+easy for readers to thumb through the book when they are searching for
+isolated words or names. But \TeX's asynchronous output mechanism
+makes it difficult, if not impossible, to determine just what range
+of entries is actually present on a page.
+
+\ddanger Therefore \TeX\ provides a way to put ``^{marks}'' into a list;
+these marks inform the output routine about the range of information on
+each page. The general idea is that you can say ^^|\mark|
+\begindisplay
+|\mark{|\<mark text>|}|
+\enddisplay
+in the midst of the information you are typesetting, where the
+\<mark text> is a token list that is expanded as in the commands
+|\edef|, |\message|, etc. \TeX\ puts an internal representation of
+the mark text into the list it is building; then later on, when a
+completed page is packed into |\box255|, \TeX\ allows the output
+routine to refer to the first and last mark texts on that page.
+
+\ddanger The best way to think of this is probably to imagine that
+\TeX\ generates an arbitrarily long vertical list of boxes, glue,
+and other items such as penalties and marks. Somehow that long vertical
+list gets divided up into pages, and the pages are made available to
+the output routine, one at a time. Whenever a page is put in |\box255|,
+\TeX\ sets up the value of three quantities that act essentially
+like macros:\enddanger
+\smallskip
+\item\bull ^|\botmark| is the mark text most recently encountered on the
+page that was just boxed;
+
+\item\bull ^|\firstmark| is the mark text that was first encountered on
+the page that was just boxed;
+
+\item\bull ^|\topmark| has the value that |\botmark| had just before the
+current page was boxed.
+\smallbreak\noindent
+Before the first page, all three of these are null, i.e., they expand
+to nothing. When there is no mark on a page, all three are equal to
+the previous |\botmark|.
+
+\ddanger For example, suppose that your manuscript includes exactly four
+marks, and that the pages are broken in such a way that |\mark{|$\alpha$|}|
+happens to fall on page~2, |\mark{|$\beta$|}| and |\mark{|$\gamma$|}|
+on page~4, and |\mark{|$\delta$|}| on page~5. Then
+$$\halign{\indent\hfil#\hfil&&\qquad\hfil#\hfil\cr
+On page&|\topmark| is&|\firstmark| is&|\botmark| is\cr
+\noalign{\vskip2pt}
+1&null&null&null\cr
+2&null&$\alpha$&$\alpha$\cr
+3&$\alpha$&$\alpha$&$\alpha$\cr
+4&$\alpha$&$\beta$&$\gamma$\cr
+5&$\gamma$&$\delta$&$\delta$\cr
+6&$\delta$&$\delta$&$\delta$\cr}$$
+
+\ddanger When you use a |\mark| command in vertical mode, \TeX\ puts a
+mark into the main vertical list. When you use a |\mark| command in
+horizontal mode, \TeX\ treats it as vertical mode material like
+^|\vadjust| and ^|\insert|; i.e., after the paragraph has been broken
+into lines, each mark will go into the main vertical list just after the
+box for the line where that mark originally appeared. If
+you use |\mark| in restricted horizontal mode, the mark may migrate out to
+the enclosing vertical list in the same way that |\insert| and |\vadjust|
+items do (see Chapter~24); but a mark that is locked too deeply inside
+a box will not ^{migrate}, so it will never appear as a |\firstmark|
+or |\botmark|. Similarly, a |\mark| that occurs in internal vertical mode
+goes into a vbox, and it is not accessible in the main vertical list.
+
+\ddanger Chapter 15 discusses the ^|\vsplit| command, which allows you to
+break up vertical lists by yourself. This operation sometimes provides
+a useful alternative to \TeX's ordinary page-building mechanism.
+For example, if you simply want to typeset some material in two columns
+of equal height, you can put that material into a vbox, then |\vsplit|
+the box into two pieces; no output routine is needed at all. The
+|\vsplit| operation sets up the values of two macro-like quantities
+that were not mentioned in Chapter~15: ^|\splitfirstmark| and
+^|\splitbotmark| expand to the mark texts of the first and last
+marks that appear in the vertical list that was split off by the most
+recent |\vsplit| command. Both quantities are null if there were no
+such marks. The values of\/ |\topmark|, |\firstmark|, |\botmark|,
+|\splitfirstmark|, and |\splitbotmark| are global; i.e., they are
+not affected by \TeX's ^{grouping} mechanism.
+
+\ddanger Most dictionaries use the equivalent of\/ |\firstmark| and |\botmark|
+to give ^{guide words} at the top of each pair of facing pages. For example,
+if the definition of the word `type' starts on page~1387 and continues
+onto page~1388, the guide word on page~1387 (a right-hand page)
+will be `type'; but the guide word at the top of page~1388 (a left-hand page)
+will be the next word in the dictionary (e.g., `typecast') even though
+the top of page~1388 is about `type'.
+
+\ddanger The dictionary scheme works fine for dictionaries, since a reader
+should start reading each dictionary entry at its beginning. But a different
+scheme is appropriate for a technical book like the author's {\sl^{Art
+of Computer Programming}}, ^^{Knuth} where Section~1.2.8 (for example)
+starts in the middle of page~78, but the top of page~78 contains
+exercises 19--24 of Section~1.2.7. The headline at the top of page~78
+refers to `1.2.7', because that will help somebody who is searching
+for exercise 1.2.7--22. Notice that the dictionary convention would put
+`1.2.8' at the top of page~78, but that would be appropriate only if
+Section~1.2.8 had begun exactly at the top of that page.
+
+\ddanger Continuing this example from {\sl The Art of Computer Programming},
+let's suppose that the \TeX\ manuscript for Section~1.2.8 begins with
+a macro call like
+\begintt
+\beginsection 1.2.8. Fibonacci Numbers.
+\endtt
+How should |\beginsection| be defined? Here is one attempt:
+\begintt
+\def\beginsection #1. #2.
+ {\sectionbreak
+ \leftline{\sectionfont #1. #2}
+ \mark{#1}
+ \nobreak\smallskip\noindent}
+\endtt
+The |\sectionbreak| macro should encourage \TeX\ either to break the page at the
+current position, or to leave a goodly amount of blank space; e.g.,
+|\sectionbreak| might be an abbreviation for `|\penalty-200| |\vskip18pt|
+|plus4pt| |minus6pt|'. The |\beginsection| macro ends with commands that
+suppress indentation of the first paragraph in the section. But the thing
+that concerns us with respect to output routines is the |\mark| command
+that follows |\leftline|. In the example we have been considering, the
+beginning of Section~1.2.8 would insert `|\mark{1.2.8}|' into the main
+vertical list just after the box containing the title of that section.
+
+\ddanger Is such a |\mark| adequate? Unfortunately, no, not even if we
+assume for simplicity that at most one section begins on each page.
+The page that contains the beginning of Section~1.2.8 will then have
+|\topmark=1.2.7| and |\firstmark=1.2.8|, regardless of whether or not the
+section starts at the very top of the page. What we want in this application
+is a cross between |\topmark| and |\firstmark|: something that will reflect
+the mark text that represents the state of affairs just after the first
+line of the page. And \TeX\ doesn't provide that.
+
+\ddanger The solution is to emit the |\mark| just before the |\sectionbreak|,
+instead of just after the |\leftline|. Then |\topmark| will always reflect
+the truth about the section that is current at the top line. \ (Think about it.)
+
+\ddanger However, the format for {\sl The Art of Computer Programming\/}
+is more complex than this. On left-hand pages, the section number in the
+headline is supposed to reflect the situation at the top of the page,
+as we have just discussed, but on right-hand pages it is supposed to refer
+to the bottom of the page. Our solution to the previous problem made
+|\topmark| correct for the top, but it can make |\botmark| incorrect
+at the bottom. In order to satisfy both requirements, it is necessary
+to pack more information into the marks. Here's one way to solve the problem:
+\begintt
+\def\beginsection #1. #2.
+ {\mark{\currentsection \noexpand\else #1}
+ \sectionbreak
+ \leftline{\sectionfont #1. #2}
+ \mark{#1\noexpand\else #1} \def\currentsection{#1}
+ \nobreak\smallskip\noindent}
+\def\currentsection{} % the current section number
+\endtt
+The idea is to introduce two marks, one just before the section break
+and one just after the section has begun. Furthermore each mark has two
+parts; the mark just before the potential break between Sections 1.2.7
+and~1.2.8 is `|1.2.7\else 1.2.8|', while the one just after that potential
+break is `|1.2.8\else 1.2.8|'. It follows that the section number corresponding
+to the bottom of a page is the left component of\/ |\botmark|; the section
+number corresponding to the top of a page is the right component of\/
+|\topmark|. The |\rightheadline| macro can make use of
+`^|\iftrue||\botmark\fi|' to read the left component, and the
+|\leftheadline| macro can say `^|\expandafter|^|\iffalse||\topmark\fi|' to
+read the right component.
+
+\ddangerexercise B. C. ^{Dull} used a construction very much like the one above,
+but he put the second |\mark| just before the |\leftline| instead of
+just after it. What went wrong?
+\answer He forgot that ^{interline glue} is inserted automatically before
+the |\leftline|; this permits a legal breakpoint between the |\mark| and
+the |\leftline| box, according to the rules of page breaking in Chapter~15.
+One cure would be to say ^|\nobreak| just after the |\mark|; but it's
+usually best to put marks and ^{insertions} just {\sl after\/} boxes.
+
+\ddangerexercise The marks in the previous construction have the form
+`$\alpha$|\else|$\,\beta$', where $\alpha$ and $\beta$ are two independent
+pieces of information. The `|\else|' makes it possible to select
+either $\alpha$ or $\beta$ by means of\/ ^|\iftrue| and ^|\iffalse|.
+Generalize this idea: Suppose that you have an application in which
+marks are supposed to carry five independent pieces of information,
+and that each mark has the form `$\alpha_0$|\or|$\,\alpha_1$|\or|%
+$\,\alpha_2$|\or|$\,\alpha_3$|\or|$\,\alpha_4$'. Explain how to select
+any one of the five $\alpha$'s from such a mark.
+\answer Say, for example, |\ifcase2\expandafter\relax\botmark\fi| to
+read part $\alpha_2$ of\/ |\botmark|. Another solution puts the five
+components into five parameters of a macro, analogous to the method
+used by |\inxcheck| later in this chapter; but the |\ifcase| approach
+is usually more efficient, because it lets \TeX\ pass over the unselected
+components at high speed.
+
+\ddanger Let's conclude our discussion of output routines by considering an
+application to indexes, such as the index to this manual that appears
+^^{index marks} ^^{index example}
+in Appendix~I\null. The most complicated entries in such an index will
+look something like this:
+\begindisplay
+Main entry, 4, 6, 8--10, 12, 14--16,\cr
+\qquad 18--22, 24--28, 30.\cr
+\quad first subsidiary entry, 1--3, 6, 10--11,\cr
+\qquad 15, 21, 24, 28.\cr
+\quad second subsidiary entry, 1, 3, 6--7,\cr
+\qquad 10, 15, 21, 25, 28, 31.\cr
+\enddisplay
+Main entries and subsidiary entries are typeset ^{ragged-right},
+with two ems of hanging indentation after the first line; subsidiary entries
+are indented one em on the first line. Our goal will be to typeset
+such material from input that looks like this:
+\begintt
+\beginindex
+...
+Main entry, 4, 6, 8--10, 12, 14--16, 18--22, 24--28, 30.
+\sub first subsidiary entry, 1--3, 6, 10--11, 15, 21, 24, 28.
+\sub second subsidiary entry, 1, 3, 6--7, 10, 15, 21, 25, %
+ 28, 31.
+...
+\endindex
+\endtt
+where `|...|'\ stands for other entries. Each line of input normally specifies
+one main entry or one subsidiary entry; if an entry is so long that it doesn't
+fit on a single input line, `\]|%|' is typed at the end of the line so
+that it merges with the following one.
+
+\ddanger The interesting thing about this index problem is that it is
+desirable to set up a system of marks so that the output routine can insert
+special lines of text when an entry has been broken between columns or
+pages. For example, if a page break occurs between any of the six lines
+of typeset output shown above, the output routine should emit the special
+line
+\begindisplay
+Main entry ({\it continued}\thinspace):
+\enddisplay
+and if a page break occurs within a subsidiary entry, an additional special
+line
+\begindisplay
+\quad subsidiary entry ({\it continued}\thinspace):
+\enddisplay
+should also appear. The solution below produces marks so that |\botmark|
+will be null if a break occurs between main entries; it will be
+`|Main entry|' if a break occurs after lines 1, 2, or~4 of the six
+example output lines; it will be `|Main entry\sub first subsidiary entry|'
+if a break occurs after line~3 (within the first subsidiary entry); and it will
+be `|Main entry\sub second subsidiary entry|' if a break occurs after line~5.
+
+\ddanger The reader may wish to try solving this problem before looking at
+the solution, because it will then be easier to appreciate the subtler
+issues that are involved. \ (Go ahead: Try to define a macro |\beginindex|
+that does the ragged-right setting and produces the specified marks. Turn
+back to the previous page to study the problem carefully, before peeking
+at the answer.)
+^^|\everypar| ^^|\futurelet| ^^|\exhyphenpenalty| ^^|\raggedright|
+^^|\hangindent|
+\begintt
+\def\beginindex{\begingroup
+ \parindent=1em \maxdepth=\maxdimen
+ \def\par{\endgraf \futurelet\next\inxentry}
+ \obeylines \everypar={\hangindent 2\parindent}
+ \exhyphenpenalty=10000 \raggedright}
+\def\inxentry{\ifx\next\sub \let\next=\subentry
+ \else\ifx\next\endindex \let\next=\vfill
+ \else\let\next=\mainentry \fi\fi \next}
+\def\endindex{\mark{}\break\endgroup}
+\let\sub=\indent \newtoks\maintoks \newtoks\subtoks
+\def\mainentry#1,{\mark{}\noindent
+ \maintoks={#1}\mark{\the\maintoks}#1,}
+\def\subentry\sub#1,{\mark{\the\maintoks}\indent
+ \subtoks={#1}\mark{\the\maintoks\sub\the\subtoks}#1,}
+\endtt
+Even if you have read this solution, you probably want an explanation of
+what it does, because it uses ``\TeX tics'' that have not appeared before
+in this manual.\enddanger
+\smallskip
+\textindent{1)} The |\beginindex| macro uses ^|\begingroup| to keep other
+changes local; thus, it won't be necessary to restore ^|\parindent| and
+^|\maxdepth|, etc., to their former values when the index is finished.
+The |\maxdepth| parameter is set to ^|\maxdimen|, which is essentially
+infinite, so that |\box255| will have the true depth of the last box that
+it contains; we will use this fact below. \ (It is safe to disable
+|\maxdepth| in this way, since the entries in an index can be assumed
+to have reasonably small depth.) \ Notice that ^|\obeylines| is used, so
+that ^|\par| will effectively be inserted at the end of every line of
+input. The meaning of\/ |\par| is changed so that it does more than usual:
+First it does ^|\endgraf|, which is \TeX's ordinary |\par| operation;
+then it sets |\next| to the first token of the next line, after which the
+macro |\inxentry| will be expanded.
+
+\smallskip
+\textindent{2)} When |\inxentry| comes into play it looks at |\next|
+to decide what to do. There are three cases: If\/ |\next| is `|\sub|',
+the line will be treated as a subsidiary entry; if\/ |\next| is `|\endindex|',
+the next commands executed will be `|\vfill|\allowbreak
+|\mark{}|\allowbreak|\break|\allowbreak|\endgroup|';
+otherwise the line will be treated as a main entry.
+
+\smallskip
+\textindent{3)} The text of a main entry is put into parameter |#1| of
+|\mainentry|; this parameter is delimited by a comma. The first thing that
+|\mainentry| does is `|\mark{}|', which clears the mark in case of a
+break between entries. Then comes `^|\noindent|', which causes \TeX\ to
+go into horizontal mode and to emit ^|\parskip| glue. \ (The |\parskip|
+glue will be a legal breakpoint between lines; it will later be followed
+by interline glue, when the first line of the main entry has been typeset
+by \TeX's paragraphing routine.) \ Then another |\mark| is put into
+the paragraph itself; this one contains the text of the main entry,
+and a ^|\toks| register called ^|\maintoks| is used to ^{inhibit
+expansion} of the mark text. When the paragraph is completed and broken
+into lines, this particular mark will immediately follow the box for the
+paragraph's first line, so it will be the |\botmark| if a page break
+occurs anywhere within the paragraph.
+
+\smallskip
+\textindent{4)} A similar construction is used for |\subentry|, but the
+mark is more complicated. The |\maintoks| register will still contain the
+main entry. The text for the subsidiary entry is added using another
+token list register, |\subtoks|. Since |\sub| has been defined to equal
+^|\indent|, it will not be expanded in this |\mark|.
+
+\ddanger The macros just defined will typeset entries that contain the
+necessary marks; now we must construct an output routine that uses
+these marks in the desired way, to insert new lines that say `({\it
+continued}\thinspace)' as mentioned above. Again, the reader is advised to
+try solving this problem before looking at the following solution.
+\begintt
+\output={\dimen0=\dp255 \normaloutput
+ \expandafter\inxcheck\botmark\sub\end}
+\def\inxcheck#1\sub#2\end{\def\next{#1}%
+ \ifx\next\empty % do nothing if \botmark is null
+ \else\noindent #1\continued % `Main entry (continued):'
+ \def\next{#2}%
+ \ifx\next\empty % nothing more if \botmark has no \sub
+ \else\let\sub=\continued \indent #2\fi
+ \advance\dimen0 by-\prevdepth \kern\dimen0 \fi}
+\def\continued{ ({\it continued}\thinspace):\endgraf}
+\endtt
+This coding is a bit more subtle than usual. It assumes that |\normaloutput|
+takes care of shipping out |\box255| (possibly putting it into multicolumn
+format) and advancing the page number; then comes new stuff, which is
+performed by |\inxcheck|. The |\inxcheck| macro is invoked in an interesting
+way that allows |\botmark| to be separated into its components. If\/ |\botmark|
+^^{macro arguments}
+is null, argument~|#1| to |\inxcheck| will be null; hence |\next| will be
+found equivalent to ^|\empty|. \ (Plain \TeX\ says `|\def\empty{}|' in
+order to accommodate situations like this.) \ If\/ |\botmark| doesn't contain
+the token~|\sub|, argument~|#1| will be the contents of\/ |\botmark| while
+|#2|~will be null. Otherwise, if\/ |\botmark| has the form $\alpha$|\sub|$\,
+\beta$, argument~|#1| will be $\alpha$ and |#2|~will be `$\beta$|\sub|'.
+
+\ddanger If\/ |\botmark| isn't null, the |\inxcheck| macro produces one or
+more lines of text that will be contributed to \TeX's main vertical list
+at the position of the page break. And here's where the most subtle point
+arises: There will be ^{interline glue} at the page break, computed on
+the basis of the depth of the box that preceded the break. That depth
+is known to the output routine, since it's the depth of\/ |\box255|. \ (The
+value of\/ ^|\maxdepth| was made infinite for precisely this reason.) \
+Therefore the |\inxcheck| macro can insert a ^|\kern| to compensate for
+the difference in depth between the old box and the one that will be
+inserted before the interline glue that has already been computed. Without
+this |\kern|, the spacing would be wrong. The reader should study this
+example carefully, to understand the reasoning behind the |\kern| command,
+before designing an output routine that inserts new boxes between
+random lines of output.
+
+\ddangerexercise Modify this construction so that continuation lines are
+inserted only in the left columns of even-numbered pages, assuming
+two-column format.
+\answer |\output={\dimen0=\dp255 \normaloutput|\parbreak
+ | \ifodd\pageno\else\if L\lr|\parbreak
+ | \expandafter\inxcheck\botmark\sub\end\fi\fi}|
+\smallskip\noindent In this case the |\normaloutput| macro should be the
+two-column output routine that was described earlier in this chapter, beginning
+with `|\if L\lr|' and ending with `|\let\lr=L\fi|'. \ (There is no need
+to test for |\supereject|.)
+
+\ddangerexercise True or false: The |\inxcheck| macro in this
+example contributes at most two lines of output to the main vertical list.
+\answer False. If the text of the main and/or subsidiary entry is lengthy,
+a continuation line may actually become two or more lines. \ (Incidentally,
+hanging indentation will then occur, because the |\everypar| command---which
+was set up outside the |\output| routine---is effective inside.) \ The
+|\vsize| must be large enough to accommodate all continuation lines plus
+at least one more line of index material, or else infinite looping will occur.
+
+\ddanger When \TeX\ sees an ^|\end| command, it terminates the job only
+if the main vertical list has been entirely output and if\/ ^|\deadcycles||=0|.
+Otherwise it inserts the equivalent of
+\begintt
+\line{} \vfill \penalty-'10000000000
+\endtt
+into the main vertical list, and prepares to read the `|\end|' token again.
+This has the effect of invoking the output routine repeatedly until
+everything has been shipped out. In particular, the last column of two-column
+format will not be lost.
+
+\ddanger It is possible to devise output routines that always leave a residue
+on the main vertical list, yet they never allow |\deadcycles| to
+increase. In such a case \TeX\ will never come to an end! An output
+routine can recognize that it is being invoked by \TeX's endgame, because
+of the highly negative |\outputpenalty| caused by the special
+^|\penalty-'10000000000|. At such times the output routine should modify
+its behavior, if necessary, so that a happy ending will ensue.
+
+\endchapter
+
+I think you will like them,
+when you shall see them on a beautiful quarto page,
+where a neat rivulet of text
+shall meander through a meadow of margin.
+'Fore Gad they will be the most elegant things of their kind!
+\author RICHARD BRINSLEY ^{SHERIDAN}, %
+ {\sl The School for Scandal\/} (1777) % Act I Sc 1
+
+\bigskip
+
+The influence of technical changes upon outputs
+through variation in the general investment level {\cmman\char'14}
+is so small that actually it could have been neglected.
+\author WASSILY W. ^{LEONTIEF}\kern-1pt, %
+ {\sl The Structure of American Economy, 1919--1929\/} (1941)
+
+\eject
+ \beginchapter Chapter 24. Summary of\\Vertical\\Mode
+
+^^{vertical mode}
+The whole \TeX\ language has been presented in the previous chapters;
+we have finally reached the end of our journey into previously
+uncharted territory. Hurray! Victory! Now it is time to take a more
+systematic look at what we have encountered: to consider the facts in an
+orderly manner, rather than to mix them up with informal examples and
+applications as we have been doing. A child learns to speak a language
+before learning formal rules of grammar, but the rules of grammar come
+in handy later on when the child reaches adulthood. The purpose of
+this chapter---and of the two chapters that follow---is to present a
+precise and concise summary of the language that \TeX\ understands, so
+that mature users will be able to communicate as effectively as possible
+with the machine.
+
+We will be concerned in these chapters solely with \TeX's {\sl^{primitive}\/}
+operations, rather than with the higher-level features of plain \TeX\ format
+that most people deal with. Therefore novice users should put off reading
+Chapters 24--26 until they feel a need to know what goes on inside the
+computer. Appendix~B contains a summary of plain \TeX, together with a
+ready-reference guide to the things that most people want to know about
+\TeX\ usage. The best way to get an overview of \TeX\ from a high level
+is to turn to the opening pages of Appendix~B.
+
+\medskip\ninepoint
+Our purpose here, however, is to survey the low-level parts of \TeX\ on
+which higher-level superstructures have been built, in order to provide
+a detailed reference for people who do need to know the details.
+The remainder of this chapter is set in small type, like that of the
+present paragraph, since it is analogous to material that is marked
+``doubly dangerous'' in other chapters. Instead of using dangerous bend
+signs repeatedly, let us simply agree that Chapters 24--26 are dangerous
+by definition.
+
+\medbreak
+\TeX\ actually has a few features that didn't seem to be worth mentioning
+in previous chapters, so they will be introduced here as part of our
+complete survey. If there is any disagreement between something that was
+said previously and something that will be said below, the facts in the
+present chapter and its successors should be regarded as better
+approximations to the ^{truth}.
+
+\medbreak
+We shall study \TeX's digestive processes, i.e., what \TeX\ does with
+the lists of tokens that arrive in its ``stomach.'' Chapter~7 has
+described the process by which input files are converted to lists of
+tokens in \TeX's ``mouth,'' and Chapter~20 explained how expandable tokens
+^^{anatomy of TeX}
+are converted to unexpandable ones in \TeX's ``gullet'' by a process
+similar to regurgitation. When unexpandable tokens finally reach \TeX's
+gastro-intestinal tract, the real activity of typesetting begins, and
+that is what we are going to survey in these summary chapters.
+
+Each token that arrives in \TeX's tummy is considered to be a {\sl^{command}\/}
+that the computer will obey. For example, the letter `{\tt L}' is a command
+to typeset an `L' in the current font; `|\par|' tells \TeX\ to
+finish a paragraph. \TeX\ is always in one of six modes, as
+described in Chapter~13, and a command sometimes means different things
+in different modes. The present chapter is about vertical mode (and
+internal vertical mode, which is almost the same): We shall
+discuss \TeX's response to every primitive command, when that command occurs
+in vertical mode. Chapters 25 and~26 characterize horizontal mode and math mode
+in a similar way, but those chapters are shorter than this one because
+many commands have the same behavior in all modes; the rules for such
+commands will not be repeated thrice, they will appear only once.
+
+Some commands have ^{arguments}. In other words, one or more of the tokens
+that follow a command might be used to modify that command's behavior,
+and those tokens are not considered to be commands themselves. For example,
+when \TeX\ processes the sequence of tokens that corresponds to
+`|\dimen2=2.5pt|', it considers only the first token `|\dimen|' to be a
+command; the next tokens are swept up as part of the operation, because \TeX\
+needs to know what |\dimen| register is to be set equal to what \<dimen> value.
+
+We shall define \TeX's parts of speech by using a modified form of the
+grammatical notation that was introduced about 1960 by John ^{Backus} and
+Peter ^{Naur} for the definition of computer languages. Quantities in
+^{angle brackets} will either be explained in words or they will be defined
+by {\sl^{syntax rules}\/} that show exactly how they are formed from other
+quantities. For example,
+\beginsyntax
+<unit of measure>\is<optional spaces><internal unit>
+ \alt<optional {\tt true}><physical unit>
+\endsyntax
+defines a \<unit of measure> to be either an occurrence of
+\<optional spaces> followed by an \<internal unit>, or \<optional {\tt true}>
+followed by \<physical unit>. The symbol `\is' in a syntax rule
+means ``is defined to be,'' and `\alt' means ``or.''
+
+Sometimes a syntax rule is ^{recursive}, in the sense that the right-hand
+side of the definition involves the quantity being defined. For example,
+the rule
+\beginsyntax
+<optional spaces>\is<empty>\alt<space token><optional spaces>
+\endsyntax
+defines the grammatical quantity called \<optional spaces> to be either
+\<empty>, or a \<space token> followed by \<optional spaces>. The quantity
+^\<empty> stands for ``nothing,'' i.e., for no tokens at all; hence the
+syntax rule just given is a formalized way of saying that \<optional
+spaces> stands for a sequence of zero or more spaces.
+
+The alternatives on the right-hand side of a syntax rule need not consist
+entirely of quantities in angle brackets. Explicit tokens can be used
+as well. For example, the rule
+\beginsyntax
+<plus or minus>\is|+|$_{12}$\alt|-|$_{12}$
+\endsyntax
+says that \<plus or minus> stands for a ^{character token} that is either
+a plus sign or a~minus sign, with category code~12.
+
+We shall use a special convention for ^{keywords}, since the actual syntax
+of a keyword is somewhat technical. Letters in typewriter type like
+`\[pt]' will stand for
+\begindisplay
+\<optional spaces>\<p or P>\<t or T>,
+\enddisplay
+where \<p or P> denotes any non-active character token for either |p| or~|P|
+(independent of the category code), and where \<t or T> is similar.
+
+When a control sequence like `|\dimen|' is used in the syntax rules below,
+it stands for any token whose current meaning is the same as the meaning
+that |\dimen| had when \TeX\ started up. Other tokens can be given this
+same meaning, using |\let| or |\futurelet|, and the meaning of the
+control sequence |\dimen| itself may be redefined by the user, but
+the syntax rules take no note of this; they just use `|\dimen|'
+as a way of referring to a particular primitive command of \TeX. \ (This
+notation is to be distinguished from `\cstok{dimen}', which stands
+for the control sequence token whose actual name is |dimen|; see
+Chapter~7.) ^^{boxed words}
+
+Control sequences sometimes masquerade as characters, if their meaning has
+been assigned by |\let| or |\futurelet|. For example, Appendix~B says
+\begintt
+\let\bgroup={ \let\egroup=}
+\endtt
+and these commands make ^|\bgroup| and ^|\egroup| act somewhat like left
+and right ^{curly} ^{braces}. Such control sequences are called ``^{implicit
+characters}''; they are interpreted in the same way as characters, when
+\TeX\ acts on them as commands, but not always when they appear in
+arguments to commands. For example, the command `|\let\plus=+|' does not
+make |\plus| an acceptable substitute for the character token `|+|$_{12}$'
+in the syntax rule for \<plus or minus> given above, nor does the command
+`|\let\p=p|' make |\p| acceptable as part of the keyword \[pt]. When
+\TeX's syntax allows both explicit and implicit characters, the rules
+below will be careful to say so, explicitly.
+
+The quantity ^\<space token>, which was used in the syntax of \<optional
+spaces> above, stands for an explicit or implicit space. In other words,
+it denotes either a character token of category~10, or a control sequence
+or active character whose current meaning has been made equal to such a
+token by |\let| or |\futurelet|.
+
+It will be convenient to use the symbols `|{|', `|}|', and `|$|' to stand
+for any explicit or implicit character tokens of the respective categories
+1, 2, and~3, whether or not the actual character codes are braces or dollar
+signs. Thus, for example, plain~\TeX's |\bgroup| is an example of a `|{|',
+and so are the tokens `|{|$_1$' and `|(|$_1$'; but `|{|$_{12}$' is not.
+
+The last few paragraphs can be summarized by saying that the alternatives
+on the right-hand sides of \TeX's formal syntax rules are made from one
+or more of the following things: (1)~syntactic quantities like
+ \<optional spaces>; (2)~explicit character tokens like |+|$_{12}$;
+(3)~keywords like \[pt]; (4)~control sequence names like |\dimen|;
+or (5)~the special symbols |{|, |}|, |$|.
+
+\medbreak
+Let us begin our study of \TeX's syntax by discussing the precise meanings
+of quantities like \<number>, \<dimen>, and \<glue> that occur frequently
+as arguments to commands. The most important of these is \<number>,
+which specifies an integer value. Here's exactly what a \<number> is:
+\beginsyntax
+<number>\is<optional signs><unsigned number>
+<optional signs>\is<optional spaces>
+ \alt<optional signs><plus or minus><optional spaces>
+<unsigned number>\is<normal integer>\alt<coerced integer>
+<normal integer>\is<internal integer>
+ \alt<integer constant><one optional space>
+ \alt|'|$_{12}$<octal constant><one optional space>
+ \alt|"|$_{12}$<hexadecimal constant><one optional space>
+ \alt|`|$_{12}$<character token><one optional space>
+<integer constant>\is<digit>\alt<digit><integer constant>
+<octal constant>\is<octal digit>\alt<octal digit><octal constant>
+<hexadecimal constant>\is<hex digit>\alt<hex digit><hexadecimal constant>
+<octal digit>\is|0|$_{12}$\alt|1|$_{12}$\alt|2|$_{12}$\alt|3|$_{12}$\alt%
+ |4|$_{12}$\alt|5|$_{12}$\alt|6|$_{12}$\alt|7|$_{12}$
+<digit>\is<octal digit>\alt|8|$_{12}$\alt|9|$_{12}$
+<hex digit>\is<digit>\alt|A|$_{11}$\alt|B|$_{11}$\alt|C|$_{11}$\alt%
+ |D|$_{11}$\alt|E|$_{11}$\alt|F|$_{11}$
+ \alt|A|$_{12}$\alt|B|$_{12}$\alt|C|$_{12}$\alt%
+ |D|$_{12}$\alt|E|$_{12}$\alt|F|$_{12}$
+<one optional space>\is<space token>\alt<empty>
+<coerced integer>\is<internal dimen>\alt<internal glue>
+\endsyntax
+The value of a \<number> is the value of the corresponding \<unsigned number>,
+times~$-1$ for every minus sign in the \<optional signs>.
+An ^{alphabetic constant} denotes the character code in a
+^\<character token>; \TeX\ does not expand this token, which should either
+be a (character~code, category~code) pair, or an active character, or
+a control sequence whose name consists of a single character.
+\ (See Chapter~20 for a complete list of all situations in which \TeX\ does
+not expand tokens.) \ An \<integer constant> must not be immediately followed by
+a \<digit>; in other words, if several digits appear consecutively, they
+are all considered to be part of the same \<integer constant>. A similar
+remark applies to the quantities \<octal constant> and \<hexadecimal constant>.
+The quantity ^\<one optional space> is \<empty> only if it has to be;
+i.e., \TeX\ looks for \<one optional space> by reading a token and backing
+up if a \<space token> wasn't there.
+
+\ddangerexercise Can you think of a reason why you might want `|A|$_{12}$'
+to be a \<hex digit> even though the letter {\tt A} has category~11? \
+(Don't worry if your answer is ``no.''\thinspace)
+\answer If\/ |\cs| has been defined by ^|\chardef| or ^|\mathchardef|, \TeX\
+uses ^{hexadecimal notation} when it expands ^|\meaning||\cs|, and it
+assigns category~12 to each digit of the expansion. You might have an
+application in which you want the last part of the expansion to be treated
+as a \<number>. \ (This is admittedly an obscure reason.)
+
+The definition of \<number> is now complete except for the three quantities
+called \<internal integer>, \<internal dimen>, and \<internal glue>, which
+will be explained later; they represent things like parameters and registers.
+For example, |\count1| and |\tolerance| and |\hyphenchar\tenrm| are
+internal integers; |\dimen10| and |\hsize| and |\fontdimen6\tenrm| are
+internal dimensions; |\skip100| and |\baselineskip| and |\lastskip| are
+internal glue values. An internal dimension can be ``coerced'' to be an
+integer by assuming units of scaled points. For example, if\/ |\hsize=100pt|
+^^{coerce <dimen> to <number>}
+^^{coerce <glue> to <dimen>}
+and if\/ |\hsize| is used in the context of a \<number>, it denotes the
+integer value 6553600. Similarly, an internal glue value can be coerced to
+be an integer by first coercing it to be a dimension (omitting the
+stretchability and shrinkability), then coercing that dimension.
+
+\smallskip
+Let's turn now to the syntax for \<dimen>, and for \<mudimen> its cousin:
+\beginsyntax
+<dimen>\is<optional signs><unsigned dimen>
+<unsigned dimen>\is<normal dimen>\alt<coerced dimen>
+<coerced dimen>\is<internal glue>
+<normal dimen>\is<internal dimen>\alt<factor><unit of measure>
+<factor>\is<normal integer>\alt<decimal constant>
+<decimal constant>\is|.|$_{12}$\alt|,|$_{12}$
+ \alt<digit><decimal constant>
+ \alt<decimal constant><digit>
+<unit of measure>\is<optional spaces><internal unit>
+ \alt<optional {\tt true}><physical unit><one optional space>
+<internal unit>\is[em]<one optional space>\alt[ex]<one optional space>
+ \alt<internal integer>\alt<internal dimen>\alt<internal glue>
+<optional {\tt true}>\is[true]\alt<empty>
+<physical unit>\is[pt]\alt[pc]\alt[in]\alt[bp]\alt[cm]\alt[mm]\alt[dd]\alt%
+ [cc]\alt[sp]\vadjust{\vskip 3pt minus 2pt}
+<mudimen>\is<optional signs><unsigned mudimen>
+<unsigned mudimen>\is<normal mudimen>\alt<coerced mudimen>
+<coerced mudimen>\is<internal muglue>
+<normal mudimen>\is<factor><mu unit>
+<mu unit>\is<optional spaces><internal muglue>\alt[mu]<one optional space>
+\endsyntax
+When `|true|' is present, the factor is multiplied by~1000 and divided by
+the ^|\mag| parameter. Physical units are defined in Chapter~10; |mu| is
+explained in Chapter~18.
+
+\goodbreak
+Encouraged by our success in mastering the precise syntax of the quantities
+\<number>, \<dimen>, and \<mudimen>, let's tackle \<glue> and \<muglue>:
+\beginsyntax
+<glue>\is<optional signs><internal glue>
+ \alt<dimen><stretch><shrink>
+<stretch>\is[plus]<dimen>\alt[plus]<fil dimen>\alt<optional spaces>
+<shrink>\is[minus]<dimen>\alt[minus]<fil dimen>\alt<optional spaces>
+<fil dimen>\is<optional signs><factor><fil unit><optional spaces>
+<fil unit>\is[fil]\alt<fil unit>[l]
+<muglue>\is<optional signs><internal muglue>
+ \alt<mudimen><mustretch><mushrink>
+<mustretch>\is[plus]<mudimen>\alt[plus]<fil dimen>\alt<optional spaces>
+<mushrink>\is[minus]<mudimen>\alt[minus]<fil dimen>\alt<optional spaces>
+\endsyntax
+
+\TeX\ makes a large number of internal quantities accessible so that a
+format designer can influence \TeX's behavior. Here is a list of all
+these quantities, except for the parameters (which will be listed later).
+\beginsyntax
+<internal integer>\is<integer parameter>\alt<special integer>\alt^|\lastpenalty|
+ \alt<countdef token>\alt^|\count|<8-bit number>\alt<codename><8-bit number>
+ \alt<chardef token>\alt<mathchardef token>\alt^|\parshape|\alt^|\inputlineno|
+ \alt^|\hyphenchar|<font>\alt^|\skewchar|<font>\alt^|\badness|
+<special integer>\is^|\spacefactor|\alt^|\prevgraf|
+ \alt^|\deadcycles|\alt^|\insertpenalties|
+<codename>\is^|\catcode|\alt^|\mathcode|
+ \alt^|\lccode|\alt^|\uccode|\alt^|\sfcode|\alt^|\delcode|
+<font>\is<fontdef token>\alt^|\font|\alt<family member>
+<family member>\is<font range><4-bit number>
+<font range>\is^|\textfont|\alt^|\scriptfont|\alt^|\scriptscriptfont|
+<internal dimen>\is<dimen parameter>\alt<special dimen>\alt^|\lastkern|
+ \alt<dimendef token>\alt^|\dimen|<8-bit number>
+ \alt<box dimension><8-bit number>\alt^|\fontdimen|<number><font>
+<special dimen>\is^|\prevdepth|\alt^|\pagegoal|\alt^|\pagetotal|
+ \alt^|\pagestretch|\alt^|\pagefilstretch|\alt^|\pagefillstretch|
+ \alt^|\pagefilllstretch|\alt^|\pageshrink|\alt^|\pagedepth|
+<box dimension>\is^|\ht|\alt^|\wd|\alt^|\dp|
+<internal glue>\is<glue parameter>\alt^|\lastskip|
+ \alt<skipdef token>\alt^|\skip|<8-bit number>
+<internal muglue>\is<muglue parameter>\alt^|\lastskip|
+ \alt<muskipdef token>\alt^|\muskip|<8-bit number>
+\endsyntax
+A ^\<countdef token> is a control sequence token in which the control
+sequence's current meaning has been defined by ^|\countdef|; the other
+quantities ^\<dimendef token>, etc.,
+^^\<skipdef token>^^\<muskipdef token>^^\<chardef token>^^\<mathchardef token>
+^^\<toksdef token> are defined similarly. A \<fontdef
+token> refers to a definition by ^|\font|, or it can be the predefined font
+identifier called ^|\nullfont|. When a \<countdef token> is used as an
+internal integer, it denotes the value of the corresponding ^|\count|
+register, and similar statements hold for \<dimendef token>, \<skipdef
+token>, \<muskipdef token>. When a \<chardef token> or \<mathchardef
+token> is used as an internal integer, it denotes the value in the
+^|\chardef| or ^|\mathchardef| itself. An ^\<8-bit number> is a \<number>
+whose value is between 0~and $2^8-1=255$; a ^\<4-bit number> is similar.
+^^\<15-bit number> ^^\<27-bit number>
+
+\TeX\ allows |\spacefactor| to be an internal integer only in horizontal
+modes; |\prevdepth| can be an internal dimension only in vertical modes;
+|\lastskip| can be \<internal muglue> only in math mode when the current
+math list ends with a muglue item; and |\lastskip| cannot be \<internal
+glue> in such a case. When |\parshape| is used as an internal integer, it
+denotes only the number of controlled lines, not their sizes or
+indentations. The seven special dimensions |\pagetotal|, |\pagestretch|,
+and so on are all zero when the current page contains no boxes, and
+|\pagegoal| is |\maxdimen| at such times (see Chapter~15).
+
+\smallskip
+From the syntax rules just given, it's possible to deduce exactly what
+happens to ^{spaces} when they are in the vicinity of numerical quantities:
+\TeX\ allows a \<number> or \<dimen> to be preceded by arbitrarily many
+spaces, and to be followed by at most one space; however, there is no
+optional space after a \<number> or \<dimen> that ends with an unexpandable
+control sequence. For example, if \TeX\ sees `|\space\space24\space\space|' when
+it is looking for a \<number>, it gobbles up the first three spaces, but
+the fourth one survives; similarly, one space remains when `|24pt\space\space|'
+and `|\dimen24\space\space|' and `|\pagegoal\space|' are treated as
+\<dimen> values.
+
+\ddangerexercise Is `|24\space\space pt|' a legal \<dimen>?
+\answer Yes; any number of spaces can precede any keyword.
+
+\ddangerexercise Is there any difference between `|+\baselineskip|',
+`|- -\baselineskip|', and `|1\baselineskip|', when \TeX\ reads
+them as \<glue>?
+\answer The first two have the same meaning; but the third coerces
+|\baselineskip| to a \<dimen> by suppressing the stretchability
+and shrinkability that might be present.
+
+\ddangerexercise What \<glue> results from |"DD DDPLUS2,5 \spacefactor\space|,
+assuming the conventions of plain \TeX, when |\spacefactor| equals 1000?
+\answer The natural width is $221\rm\,dd$ (which \TeX\ rounds to
+$15497423\rm\,sp$ and displays as |236.47191pt|).
+The stretchability is $2500\rm\,sp$, since an
+internal integer is coerced to a dimension when it appears as an
+^^{coerce <number> to <dimen>}
+\<internal unit>. The shrinkability is zero. Notice that the final |\space|
+is swallowed up as part of the optional ^{spaces} of the \<shrink> part
+in the syntax for \<glue>. \ (If |PLUS| had been |MINUS|, the final |\space|
+would {\sl not\/} have been part of this \<glue>!)
+
+Let's turn now to \TeX's ^{parameters}, which the previous chapters have
+introduced one at a time; it will be convenient to assemble them
+all together. An ^\<integer parameter> is one of the following tokens:
+\begindisplay\belowdisplayskip=3pt plus 6pt \abovedisplayskip=3pt plus 1pt%
+\openup.15pt
+^|\pretolerance|\quad(badness tolerance before hyphenation)\cr
+^|\tolerance|\quad(badness tolerance after hyphenation)\cr
+^|\hbadness|\quad(badness above which bad hboxes will be shown)\cr
+^|\vbadness|\quad(badness above which bad vboxes will be shown)\cr
+^|\linepenalty|\quad(amount added to badness of every line in a paragraph)\cr
+^|\hyphenpenalty|\quad(penalty for line break after discretionary hyphen)\cr
+^|\exhyphenpenalty|\quad(penalty for line break after explicit hyphen)\cr
+^|\binoppenalty|\quad(penalty for line break after binary operation)\cr
+^|\relpenalty|\quad(penalty for line break after math relation)\cr
+^|\clubpenalty|\quad(penalty for creating a club line at bottom of page)\cr
+^|\widowpenalty|\quad(penalty for creating a widow line at top of page)\cr
+^|\displaywidowpenalty|\quad(ditto, before a display)\cr
+^|\brokenpenalty|\quad(penalty for page break after a hyphenated line)\cr
+^|\predisplaypenalty|\quad(penalty for page break just before a display)\cr
+^|\postdisplaypenalty|\quad(penalty for page break just after a display)\cr
+^|\interlinepenalty|\quad(additional penalty for page break between lines)\cr
+^|\floatingpenalty|\quad(penalty for insertions that are split)\cr
+\noalign{\goodbreak}
+^|\outputpenalty|\quad(penalty at the current page break)\cr
+^|\doublehyphendemerits|\quad(demerits for consecutive broken lines)\cr
+^|\finalhyphendemerits|\quad(demerits for a penultimate broken line)\cr
+^|\adjdemerits|\quad(demerits for adjacent incompatible lines)\cr
+^|\looseness|\quad(change to the number of lines in a paragraph)\cr
+^|\pausing|\quad(positive if pausing after each line is read from a file)\cr
+^|\holdinginserts|\quad(positive if insertions remain dormant in output box)\cr
+^|\tracingonline|\quad(positive if showing diagnostic info on the terminal)\cr
+^|\tracingmacros|\quad(positive if showing macros as they are expanded)\cr
+^|\tracingstats|\quad(positive if showing statistics about memory usage)\cr
+^|\tracingparagraphs|\quad(positive if showing line-break calculations)\cr
+^|\tracingpages|\quad(positive if showing page-break calculations)\cr
+^|\tracingoutput|\quad(positive if showing boxes that are shipped out)\cr
+^|\tracinglostchars|\quad(positive if showing characters not in the font)\cr
+^|\tracingcommands|\quad(positive if showing commands
+ before they are executed)\cr
+^|\tracingrestores|\quad(positive if showing deassignments when groups end)\cr
+^|\language|\quad(the current set of hyphenation rules)\cr
+^|\uchyph|\quad(positive if hyphenating words beginning with capital letters)\cr
+^|\lefthyphenmin|\quad(smallest fragment at beginning of hyphenated word)\cr
+^|\righthyphenmin|\quad(smallest fragment at end of hyphenated word)\cr
+^|\globaldefs|\quad(nonzero if overriding |\global| specifications)\cr
+^|\defaulthyphenchar|\quad(^|\hyphenchar| value when a font is loaded)\cr
+^|\defaultskewchar|\quad(^|\skewchar| value when a font is loaded)\cr
+^|\escapechar|\quad(escape character in the output of
+ control sequence tokens)\cr
+^|\endlinechar|\quad(character placed at the right end of an input line)\cr
+^|\newlinechar|\quad(character that starts a new output line)\cr
+^|\maxdeadcycles|\quad(upper bound on |\deadcycles|)\cr
+^|\hangafter|\quad(hanging indentation changes after this many lines)\cr
+^|\fam|\quad(the current family number)\cr
+^|\mag|\quad(magnification ratio, times 1000)\cr
+^|\delimiterfactor|\quad(ratio for variable delimiters, times 1000)\cr
+^|\time|\quad(current time of day in minutes since midnight)\cr
+^|\day|\quad(current day of the month)\cr
+^|\month|\quad(current month of the year)\cr
+^|\year|\quad(current year of our Lord)\cr
+^|\showboxbreadth|\quad(maximum items per level when boxes are shown)\cr
+^|\showboxdepth|\quad(maximum level when boxes are shown)\cr
+^|\errorcontextlines|\quad(maximum extra context shown when errors occur)\cr
+\enddisplay
+The first few of these parameters have values in units of ``badness'' and
+``penalties'' that affect line breaking and page breaking. Then come
+demerit-oriented parameters; demerits are essentially given in units of
+``badness squared,'' so those parameters tend to have larger values.
+By contrast, the next few parameters (|\looseness|, |\pausing|, etc.)\
+generally have quite small values (either $-1$ or 0 or 1 or~2).
+Miscellaneous parameters complete the set. \TeX\ computes the date and time
+when it begins a job, if the operating system provides such information; but
+afterwards the clock does not keep ticking: The user can change |\time|
+just like any ordinary parameter. Chapter~10 points out that |\mag| must
+not be changed after \TeX\ is committed to a particular magnification.
+
+\goodbreak\noindent
+A ^\<dimen parameter> is one of the following:
+\begindisplay\openup.15pt
+^|\hfuzz|\quad(maximum overrun before overfull hbox messages occur)\cr
+^|\vfuzz|\quad(maximum overrun before overfull vbox messages occur)\cr
+^|\overfullrule|\quad(width of rules appended to overfull boxes)\cr
+^|\emergencystretch|\quad(reduces badnesses on final pass of line-breaking)\cr
+^|\hsize|\quad(line width in horizontal mode)\cr
+^|\vsize|\quad(page height in vertical mode)\cr
+^|\maxdepth|\quad(maximum depth of boxes on main pages)\cr
+^|\splitmaxdepth|\quad(maximum depth of boxes on split pages)\cr
+^|\boxmaxdepth|\quad(maximum depth of boxes on explicit pages)\cr
+^|\lineskiplimit|\quad(threshold where |\baselineskip| changes
+ to |\lineskip|)\cr
+^|\delimitershortfall|\quad(maximum space not covered by a delimiter)\cr
+^|\nulldelimiterspace|\quad(width of a null delimiter)\cr
+^|\scriptspace|\quad(extra space after subscript or superscript)\cr
+^|\mathsurround|\quad(kerning before and after math in text)\cr
+^|\predisplaysize|\quad(length of text preceding a display)\cr
+^|\displaywidth|\quad(length of line for displayed equation)\cr
+^|\displayindent|\quad(indentation of line for displayed equation)\cr
+^|\parindent|\quad(width of\/ |\indent|)\cr
+^|\hangindent|\quad(amount of hanging indentation)\cr
+^|\hoffset|\quad(horizontal offset in |\shipout|)\cr
+^|\voffset|\quad(vertical offset in |\shipout|)\cr
+\enddisplay
+And the possibilities for ^\<glue parameter> are:
+\begindisplay\openup.15pt
+^|\baselineskip|\quad(desired glue between baselines)\cr
+^|\lineskip|\quad(interline glue if\/ |\baselineskip| isn't feasible)\cr
+^|\parskip|\quad(extra glue just above paragraphs)\cr
+^|\abovedisplayskip|\quad(extra glue just above displays)\cr
+^|\abovedisplayshortskip|\quad(ditto, following short lines)\cr
+^|\belowdisplayskip|\quad(extra glue just below displays)\cr
+^|\belowdisplayshortskip|\quad(ditto, following short lines)\cr
+^|\leftskip|\quad(glue at left of justified lines)\cr
+^|\rightskip|\quad(glue at right of justified lines)\cr
+^|\topskip|\quad(glue at top of main pages)\cr
+^|\splittopskip|\quad(glue at top of split pages)\cr
+^|\tabskip|\quad(glue between aligned entries)\cr
+^|\spaceskip|\quad(glue between words, if nonzero)\cr
+^|\xspaceskip|\quad(glue between sentences, if nonzero)\cr
+^|\parfillskip|\quad(additional |\rightskip| at end of paragraphs)\cr
+\enddisplay
+Finally, there are three permissible ^\<muglue parameter> tokens:
+\begindisplay\openup.15pt
+^|\thinmuskip|\quad(thin space in math formulas)\cr
+^|\medmuskip|\quad(medium space in math formulas)\cr
+^|\thickmuskip|\quad(thick space in math formulas)\cr
+\enddisplay
+All of these quantities are explained in more detail somewhere else in
+this book, and you can use Appendix~I to find out where.
+
+\TeX\ also has parameters that are token lists. Such parameters do not
+enter into the definitions of \<number> and such things, but we might as
+well list them now so that our tabulation of parameters is complete.
+A ^\<token parameter> is any of:
+\begindisplay\openup.15pt
+^|\output|\quad(the user's output routine)\cr
+^|\everypar|\quad(tokens to insert when a paragraph begins)\cr
+^|\everymath|\quad(tokens to insert when math in text begins)\cr
+^|\everydisplay|\quad(tokens to insert when display math begins)\cr
+^|\everyhbox|\quad(tokens to insert when an hbox begins)\cr
+^|\everyvbox|\quad(tokens to insert when a vbox begins)\cr
+^|\everyjob|\quad(tokens to insert when the job begins)\cr
+^|\everycr|\quad(tokens
+ to insert after every ^|\cr| or nonredundant ^|\crcr|)\cr
+^|\errhelp|\quad(tokens that supplement an |\errmessage|)\cr
+\enddisplay
+That makes a total of 103 parameters of all five kinds.
+
+\ddangerexercise Explain how |\everyjob| can be non-null when a job begins.
+\answer If it was non-null when a ^|\dump| operation occurred. Here's
+^^|\jobname|
+a nontrivial example, which sets up ^|\batchmode| and puts ^|\end| at the
+end of the input file:
+\begintt
+\everyjob={\batchmode\input\jobname\end}
+\endtt
+
+It's time now to return to our original goal, namely to study the commands
+that are obeyed by \TeX's digestive organs. Many commands are
+carried out in the same way regardless of the current mode. The most
+important commands of this type are called {\sl^{assignments}}, since
+they assign new values to the meaning of control sequences or to \TeX's
+internal quantities. For example, `|\def\a{a}|' and `|\parshape=1 5pt 100pt|'
+and `|\advance\count20 by-1|' and `|\font\ff = cmff at 20pt|' are all
+assignments, and they all have the same effect in all modes.
+Assignment commands often include an~^|=|~sign, ^^{equals sign}
+but in all cases this sign is optional; you can leave it out if you
+don't mind the fact that the resulting \TeX\ code might not look quite
+like an assignment.
+\beginsyntax
+<assignment>\is<non-macro assignment>\alt<macro assignment>
+<non-macro assignment>\is<simple assignment>
+ \alt^|\global|<non-macro assignment>
+<macro assignment>\is<definition>\alt<prefix><macro assignment>
+<prefix>\is|\global|\alt^|\long|\alt^|\outer|
+<equals>\is<optional spaces>\alt<optional spaces>\thinspace|=|$_{12}$
+\endsyntax
+This syntax shows that every assignment can be prefixed by |\global|, but
+only macro-definition assignments are allowed to be prefixed by |\long|
+or |\outer|. Incidentally, if the |\globaldefs| parameter is positive at the
+time of the assignment, a prefix of\/ |\global| is automatically implied;
+but if\/ ^|\globaldefs| is negative at the time of the assignment, a prefix
+of\/ |\global| is ignored. If\/ |\globaldefs| is zero (which it usually~is),
+the appearance or nonappearance of\/ |\global| determines whether or not
+a global assignment is made.
+\beginsyntax
+<definition>\is<def><control sequence><definition text>
+<def>\is^|\def|\alt^|\gdef|\alt^|\edef|\alt^|\xdef|
+<definition text>\is<parameter text><left brace><balanced text><right brace>
+\endsyntax
+Here ^\<control sequence> denotes a token that is either a control sequence
+or an active character; ^\<left brace> and ^\<right brace> are explicit
+character tokens whose category codes are respectively of types 1 and~2.
+The ^\<parameter text> contains no \<left brace> or \<right brace> tokens,
+and it obeys the rules of Chapter~20. All occurrences of \<left brace>
+and \<right brace> tokens within the ^\<balanced text> must be properly nested
+like \hbox{parentheses}. A |\gdef| command is equivalent to |\global\def|,
+and |\xdef| is equivalent to |\global\edef|. \TeX\ reads the
+\<control sequence> and \<parameter text> tokens and the opening \<left brace>
+without expanding them; it expands the \<balanced text>\allowbreak\<right
+brace> tokens only in the case of\/ |\edef| and |\xdef|.
+
+Several commands that we will study below have a syntax somewhat like
+that of a definition, but the \<parameter text> is replaced by an
+arbitrary sequence of spaces and `|\relax|' commands, and the
+\<left brace> token can be implicit:
+\beginsyntax
+<filler>\is<optional spaces>\alt<filler>^|\relax|<optional spaces>
+<general text>\is<filler>|{|<balanced text><right brace>
+\endsyntax
+The main purpose of a \<general text> is to specify the \<balanced text>
+inside.
+
+Many different kinds of assignments are possible, but they fall into
+comparatively few patterns, as indicated by the following syntax rules:
+\beginsyntax
+<simple assignment>\is<variable assignment>\alt<arithmetic>
+ \alt<code assignment>\alt<let assignment>\alt<shorthand definition>
+ \alt<fontdef token>\alt<family assignment>\alt<shape assignment>
+ \alt^|\read|<number>[to]<optional spaces><control sequence>
+ \alt^|\setbox|<8-bit number><equals><filler><box>
+ \alt^|\font|<control sequence><equals><file name><at clause>
+ \alt<global assignment>
+<variable assignment>\is<integer variable><equals><number>
+ \alt<dimen variable><equals><dimen>
+ \alt<glue variable><equals><glue>
+ \alt<muglue variable><equals><muglue>
+ \alt<token variable><equals><general text>
+ \alt<token variable><equals><filler><token variable>
+<arithmetic>\is^|\advance|<integer variable><optional {\tt by}><number>
+ \alt|\advance|<dimen variable><optional {\tt by}><dimen>
+ \alt|\advance|<glue variable><optional {\tt by}><glue>
+ \alt|\advance|<muglue variable><optional {\tt by}><muglue>
+ \alt^|\multiply|<numeric variable><optional {\tt by}><number>
+ \alt^|\divide|<numeric variable><optional {\tt by}><number>
+<optional {\tt by}>\is[by]\alt\<optional spaces>
+<integer variable>\is<integer parameter>\alt<countdef token>
+ \alt^|\count|<8-bit number>
+<dimen variable>\is<dimen parameter>\alt<dimendef token>
+ \alt^|\dimen|<8-bit number>
+<glue variable>\is<glue parameter>\alt<skipdef token>
+ \alt^|\skip|<8-bit number>
+<muglue variable>\is<muglue parameter>\alt<muskipdef token>
+ \alt^|\muskip|<8-bit number>
+<token variable>\is<token parameter>\alt<toksdef token>
+ \alt^|\toks|<8-bit number>
+<numeric variable>\is<integer variable>\alt<dimen variable>
+ \alt<glue variable>\alt<muglue variable>% I want to force a page break here!
+\endgraf\penalty-500\syntaxrule% this defeats the \beginsyntax trickery
+<at clause>\is[at]<dimen>\alt[scaled]<number>\alt<optional spaces>
+<code assignment>\is<codename><8-bit number><equals><number>
+<let assignment>\is^|\futurelet|<control sequence><token><token>
+ \alt^|\let|<control sequence><equals><one optional space><token>
+<shorthand definition>\is^|\chardef|<control sequence><equals><8-bit number>
+ \alt^|\mathchardef|<control sequence><equals><15-bit number>
+ \alt<registerdef><control sequence><equals><8-bit number>
+<registerdef>\is^|\countdef|\alt^|\dimendef|\alt^|\skipdef|\alt%
+ ^|\muskipdef|\alt^|\toksdef|
+<family assignment>\is<family member><equals><font>
+<shape assignment>\is^|\parshape|<equals><number><shape dimensions>
+\endsyntax
+The \<number> at the end of a \<code assignment> must not be negative, except in
+the case that a |\delcode| is being assigned. Furthermore, that \<number> should
+be at most 15~for |\catcode|, 32768~for |\mathcode|, 255~for |\lccode| or
+|\uccode|, 32767~for |\sfcode|, and $2^{24}-1$~for |\delcode|. In a
+\<shape assignment> for which the \<number> is $n$, the ^\<shape dimensions>
+are \<empty> if $n\le0$, otherwise they consist of $2n$ consecutive
+occurrences of \<dimen>. \TeX\ does not expand tokens when it scans the
+arguments of\/ |\let| and |\futurelet|.
+
+\ddangerexercise We discussed the distinction between explicit and ^{implicit
+character tokens} earlier in this chapter. Explain how you can make the
+control sequence |\cs| into an implicit space, using (a)~|\futurelet|,
+(b)~|\let|.
+\answer (a) |\def\\#1\\{}\futurelet\cs\\|\]|\\|. (b) |\def\\{\let\cs= }\\|\].
+\ (There are many other solutions.)
+
+All of the assignments mentioned so far will obey \TeX's grouping structure;
+i.e., the changed quantities will be restored to their former values
+when the current group ends, unless the change was global. The remaining
+assignments are different, since they affect \TeX's global font tables
+or hyphenation tables, or they affect certain control variables of such
+^^{global parameters}
+an intimate nature that grouping would be inappropriate. In all of the
+following cases, the presence or absence of\/ |\global| as a prefix has no
+effect.
+\beginsyntax
+<global assignment>\is<font assignment>
+ \alt<hyphenation assignment>
+ \alt<box size assignment>
+ \alt<interaction mode assignment>
+ \alt<intimate assignment>
+<font assignment>\is^|\fontdimen|<number><font><equals><dimen>
+ \alt^|\hyphenchar|<font><equals><number>
+ \alt^|\skewchar|<font><equals><number>
+<hyphenation assignment>\is^|\hyphenation|<general text>
+ \alt^|\patterns|<general text>
+<box size assignment>\is<box dimension><8-bit number><equals><dimen>
+<interaction mode assignment>\is^|\errorstopmode|\alt^|\scrollmode|
+ \alt^|\nonstopmode|\alt^|\batchmode|
+<intimate assignment>\is<special integer><equals><number>
+ \alt<special dimen><equals><dimen>
+\endsyntax
+When a |\fontdimen| value is assigned, the \<number> must be positive and
+not greater than the number of parameters in the font's metric information
+file, unless that font information has just been loaded into \TeX's
+memory; in the latter case, you are allowed to increase the number of
+parameters (see Appendix~F\null). The \<special integer> and \<special
+dimen> quantities were listed above when we discussed internal integers
+and dimensions. When |\prevgraf| is set to a \<number>, the number must
+not be negative.
+
+The syntax for ^\<file name> is not standard in \TeX, because different
+operating systems have different conventions. You should ask your local
+system wizards for details on just how they have decided to implement file
+names. However, the following principles should hold universally:
+A~\<file name> should consist of \<optional spaces> followed by explicit
+character tokens (after expansion). A sequence of six or fewer ordinary
+letters and/or digits followed by a space should be a file name that works
+in essentially the same way on all installations of\/ \TeX\null. Uppercase
+letters are not considered equivalent to their lowercase counterparts in
+file names; for example, if you refer to fonts |cmr10| and |CMR10|, \TeX\
+will not notice any similarity between them, although it might input the
+same font metric file for both fonts.
+
+\TeX\ takes precautions so that constructions like `|\chardef\cs=10\cs|' and
+`|\font\cs=name\cs|' won't expand the second |\cs| until
+the assignments are done.
+
+Our discussion of assignments is complete except that the |\setbox|
+assignment involves a quantity called \<box> that has not yet been
+defined. Here is its syntax:
+\beginsyntax
+<box>\is^|\box|<8-bit number>\alt^|\copy|<8-bit number>
+ \alt^|\lastbox|\alt^|\vsplit|<8-bit number>[to]<dimen>
+ \alt^|\hbox|<box specification>|{|<horizontal mode material>|}|
+ \alt^|\vbox|<box specification>|{|<vertical mode material>|}|
+ \alt^|\vtop|<box specification>|{|<vertical mode material>|}|
+<box specification>\is[to]<dimen><filler>
+ \alt[spread]<dimen><filler>\alt<filler>
+\endsyntax
+The |\lastbox| operation is not permitted in math modes, nor is it allowed
+in vertical mode when the main vertical list has been entirely contributed
+to the current page. But it is allowed in horizontal modes and in
+internal vertical mode; in such modes it refers to (and removes) the
+last item of the current list, provided that the last item is an hbox or~vbox.
+
+The three last alternatives for a \<box> present us with a new situation:
+The ^\<horizontal mode material> in an |\hbox| and the
+^\<vertical mode material> in a |\vbox| can't simply be swallowed up
+in one command like an \<8-bit number> or a \<dimen>; thousands of
+commands may have to be executed before that box is constructed and
+before the |\setbox| command can be completed.
+
+Here's what really happens: A command like
+\begindisplay
+|\setbox|\<number>|=\hbox to|\<dimen>|{|\<horizontal mode material>|}|
+\enddisplay
+causes \TeX\ to evaluate the \<number> and the \<dimen>, and to put those
+values on a ``stack'' for safe keeping. Then \TeX\ reads the `|{|' (which
+stands for an explicit or implicit begin-group character, as explained
+earlier), and this initiates a new level of grouping. At this point
+\TeX\ enters restricted horizontal mode and proceeds to execute commands
+in that mode. An arbitrarily complex box can now be constructed;
+the fact that this box is eventually destined for a |\setbox| command
+has no effect on \TeX's behavior while the box is being built. Eventually,
+when the matching `|}|' appears, \TeX\ restores values that were
+changed by assignments in the group just ended; then it packages the hbox
+(using the size that was saved on the stack), and completes the
+|\setbox| command, returning to the mode it was in at the time of
+the |\setbox|.
+
+\smallbreak
+Let us now consider other commands that, like assignments, are
+obeyed in basically the same way regardless of \TeX's current mode.
+
+\def\\{\smallbreak\textindent{$\bull$}}
+\\^|\relax|.\enskip
+This is an easy one: \TeX\ does nothing.
+
+\\|}|.\enskip
+This one is harder, because it depends on the current group. \TeX\ should
+now be working on a group that began with |{|; and it knows why it
+started that group. So it does the appropriate finishing actions, undoes
+the effects of non-global assignments, and leaves the group.
+At this point \TeX\ might leave its current mode and return to a mode that
+was previously in effect.
+
+\\^|\begingroup|.\enskip
+When \TeX\ sees this command, it enters a group that must be terminated
+by |\endgroup|, not by |}|. The mode doesn't change.
+
+\\^|\endgroup|.\enskip
+\TeX\ should currently be processing a group that began with |\begingroup|.
+Quantities that were changed by non-global assignments in that group
+are restored to their former values. \TeX\ leaves the group,
+but stays in the same mode.
+
+\\^|\show|\stretch\<token>,
+\stretch\stretch\stretch^|\showbox|\stretch\<8-bit number>,
+\stretch\stretch\stretch^|\showlists|,
+\stretch\stretch\stretch^|\showthe|$\langle$internal quantity$\rangle$.\enskip
+These commands are intended to help you figure out what \TeX\ thinks it
+is doing. The tokens following |\showthe| should be anything that can
+follow |\the|, as explained in Chapter~20.
+
+\ddangerexercise Review the rules for what can follow |\the| in
+Chapter~20, and construct a formal syntax that defines ^\<internal
+quantity> in a way that fits with the other syntax rules we have been
+discussing.
+\answer \<internal quantity>\is\<internal integer>\alt
+\<internal dimen>\parbreak\qquad\alt\<internal glue>\alt\<internal muglue>\alt
+\<internal nonnumeric>\parbreak \<internal nonnumeric>\is\<token
+variable>\alt \<font>
+
+\\^|\shipout|\<box>.\enskip
+After the \<box> is formed---possibly by constructing it explicitly and
+changing modes during the construction, as explained for |\hbox| earlier---its
+contents are sent to the ^|dvi|~file (see Chapter~23).
+
+\\^|\ignorespaces|\stretch$\langle$optional spaces$\rangle$.
+\TeX\ reads (and expands) tokens, doing nothing until reaching one that is
+not a \<space token>.
+
+\\^|\afterassignment|\<token>.\enskip
+The \<token> is saved in a special place; it will be inserted
+back into the input just after the next assignment command has been
+performed. An assignment need not follow immediately; if another
+|\afterassignment| is performed before the next assignment, the second one
+overrides the first. If the next assignment is a ^|\setbox|, and if the
+assigned \<box> is |\hbox| or |\vbox| or |\vtop|, the \<token> will be
+inserted just after the |{| in the box construction, not after the |}|;
+it will also come just before any tokens inserted by ^|\everyhbox| or
+^|\everyvbox|.
+
+\\^|\aftergroup|\<token>.\enskip
+The \<token> is saved on \TeX's stack; it will be inserted back into the
+input just after the current group has been completed and its local
+assignments have been undone. If several |\aftergroup| commands occur
+in the same group, the corresponding commands will be scanned in the
+same order; for example, `|{\aftergroup\a\aftergroup\b}|' yields `|\a\b|'.
+
+\\^|\uppercase|\<general text>, ^|\lowercase|\<general text>.\enskip
+The \<balanced text> in the general text is converted to uppercase form
+or to lowercase form using the |\uccode| or |\lccode| table,
+as explained in Chapter~7; no expansion is done. Then \TeX\ will
+read that \<balanced text> again.
+
+\\^|\message|\<general text>, ^|\errmessage|\<general text>.\enskip
+The balanced text (with expansion) is written on the user's terminal,
+using the format of error messages in the case of\/ |\errmessage|.
+In the latter case the ^|\errhelp| tokens will be shown if they are nonempty
+and if the user asks for help.
+
+\\^|\openin|\stretch$\langle$4-bit number$\rangle$\stretch\<equals>\stretch
+\<filename>, \ ^|\closein|\stretch$\langle$4-bit number$\rangle$.
+These commands open or close the specified input stream, for use in
+|\read| assignments as explained in Chapter~20.
+
+\\|\immediate\openout|\<4-bit number>\<equals>\<filename>\kern-.4pt,
+|\immediate\closeout|\allowbreak\<4-bit number>.\enskip ^^|\immediate|
+The specified output stream is opened or closed, for use in |\write|
+commands, as explained in Chapter~21.
+
+\\|\immediate\write|\<number>\<general text>.\enskip
+The balanced text is written on the file that corresponds to the
+specified stream number, provided that such a file is open. Otherwise it
+is written on the user's terminal and on the log file. \ (See Chapter~21;
+the terminal is omitted if the \<number> is negative.)
+
+\medbreak
+That completes the list of mode-independent commands, i.e., the commands
+that do not directly affect the lists that \TeX\ is building. When \TeX\
+is in vertical mode or internal vertical mode, it is constructing a
+vertical list; when \TeX\ is in horizontal mode or restricted horizontal
+mode, it is constructing a horizontal list; when \TeX\ is in math mode
+or display math mode, it is constructing---guess what---a math list. In
+each of these cases we can speak of the ``current list''; and there are
+some commands that operate in essentially the same way, regardless of the
+mode, except that they deal with different sorts of lists:
+
+\\^|\openout|\<4-bit number>\<equals>\<filename>,
+^|\closeout|\<4-bit number>,
+^|\write|\allowbreak\<number>\<general text>.\enskip
+These commands are recorded into a ``whatsit'' item, which is appended to
+the current list. The command will be performed later, during any
+|\shipout| that applies to this list, unless the list is part of
+a box inside ^{leaders}.
+
+\\^|\special|\<general text>.\enskip
+The balanced text is expanded and put into a ``whatsit'' item, which is
+appended to the current list. The text will eventually appear in the
+^|dvi|~file as an instruction to subsequent software (see Chapter~21).
+
+\\^|\penalty|\<number>.\enskip
+A penalty item carrying the specified number is appended to the current list.
+In vertical mode, \TeX\ also exercises the page builder (see~below).
+
+\\^|\kern|\<dimen>, ^|\mkern|\<mudimen>.\enskip
+A kern item carrying the specified dimension is appended to the current list.
+In vertical modes this denotes a vertical space; otherwise it denotes a
+horizontal space. An |\mkern| is allowed only in math modes.
+
+\\^|\unpenalty|, ^|\unkern|, ^|\unskip|.\enskip
+If the last item on the current list is respectively of type penalty,
+kern, or glue (possibly including ^{leaders}), that item is removed from
+the list. However, like |\lastbox|, these commands are not permitted in
+vertical mode if the main vertical list-so-far has been entirely contributed
+to the current page, since \TeX\ never removes items from the current page.
+
+\\^|\mark|\<general text>.\enskip
+The balanced text is expanded and put into a mark item, which is appended to
+the current list. The text may eventually become the replacement text
+for ^|\topmark|, ^|\firstmark|, ^|\botmark|, ^|\splitfirstmark|, and/or
+^|\splitbotmark|, if this mark item ever gets into a vertical list. \ (Mark
+items can appear in horizontal lists and math lists, but they have no
+effect until they ``migrate'' out of their list. The ^{migration process}
+is discussed below and in Chapter~25.)
+
+\\^|\insert|\<8-bit number>\<filler>|{|\<vertical mode material>|}|;
+the \<8-bit number> must not be~255. \ The `|{|' causes \TeX\ to enter
+internal vertical mode and a new level of grouping. When the matching~`|}|'
+is sensed, the vertical list is put into an insertion item that is
+appended to the current list using the values of\/ ^|\splittopskip|,
+^|\splitmaxdepth|, and ^|\floatingpenalty| that were current in the group
+just ended. \ (See Chapter~15.) \ This insertion item leads ultimately to
+a page insertion only if it appears in \TeX's main vertical list, so it
+will have to ``^{migrate}'' there if it starts out in a horizontal list or
+a math list. \TeX\ also exercises the page builder (see below), after an
+|\insert| has been appended in vertical~mode.
+
+\\^|\vadjust|\<filler>|{|\<vertical mode material>|}|.\enskip
+This is similar to |\insert|; the constructed vertical list goes into an
+adjustment item that is appended to the current list. However,
+|\vadjust| is not allowed in vertical modes. When an adjustment item
+migrates from a horizontal list to a vertical list, the vertical list
+inside the adjustment item is ``unwrapped'' and put directly into
+the enclosing list.
+
+\medbreak
+\centerline{$*\qquad*\qquad*$}
+\medskip\noindent
+Almost everything we have discussed so far in this chapter could equally
+well have appeared in a chapter entitled ``Summary of Horizontal Mode''
+or a chapter entitled ``Summary of Math Mode,'' because \TeX\ treats
+all of the commands considered so far in essentially the same way
+regardless of the current mode. Chapters 25 and~26 are going to be
+a lot shorter than the present one, since it will be unnecessary
+to repeat all of the mode-independent rules.
+
+But now we come to commands that are mode-dependent;
+we shall conclude this chapter by discussing what \TeX\ does with the
+remaining commands, when in vertical mode or internal vertical mode.
+
+One of the things characteristic of vertical mode is the page-building
+operation described in Chapter~15. \TeX\ periodically takes material
+that has been put on the main vertical list and moves it from the
+``contribution list'' to the ``current page.'' At such times the output
+routine might be invoked. We shall say that \TeX\ {\sl exercises
+the ^{page builder}\/} whenever it tries to empty the current
+contribution list. The concept of contribution list exists only in
+the outermost vertical mode, so nothing happens when \TeX\ exercises
+the page builder in internal vertical mode.
+
+Another thing characteristic of vertical modes is the ^{interline glue}
+that is inserted before boxes, based on the values of\/ |\prevdepth|
+and ^|\baselineskip| and ^|\lineskip| and ^|\lineskiplimit| as explained
+in Chapter~12. If a command changes ^|\prevdepth|, that fact is
+specifically mentioned below. The |\prevdepth| is initially set to
+$-1000\pt$, a special value that inhibits interline glue, whenever \TeX\
+begins to form a vertical list, except in the case of\/ |\halign| and
+|\noalign| when the interline glue conventions of the outer list continue
+inside the inner one.
+
+\\^|\vskip|\<glue>, ^|\vfil|, ^|\vfill|, ^|\vss|, ^|\vfilneg|.\enskip
+A glue item is appended to the current vertical list.
+
+\\\<leaders>\<box or rule>\<vertical skip>.\enskip
+Here ^\<vertical skip> refers to one of the five glue-appending commands
+just mentioned. The formal syntax for \<leaders> and for \<box or rule> is
+\beginsyntax
+<leaders>\is|\leaders|\alt|\cleaders|\alt|\xleaders|
+<box or rule>\is<box>\alt<vertical rule>\alt<horizontal rule>
+<vertical rule>\is|\vrule|<rule specification>
+<horizontal rule>\is|\hrule|<rule specification>
+<rule specification>\is<optional spaces>\alt<rule dimension><rule specification>
+<rule dimension>\is[width]<dimen>\alt[height]<dimen>\alt[depth]<dimen>
+\endsyntax
+A glue item that produces ^{leaders} is appended to the current list.
+
+\\^\<space token>.\enskip
+Spaces have no effect in vertical modes.
+
+\\^\<box>.\enskip
+The box is constructed, and if the result is void nothing happens.
+Otherwise the current vertical list receives (1)~interline glue, followed by
+(2)~the new box, followed by (3)~vertical material that ^{migrates} out of the
+new box (if the \<box> was an ^|\hbox| command). Then ^|\prevdepth| is
+set to the new box's depth, and \TeX\ exercises the page builder.
+
+\\^|\moveleft|\<dimen>\<box>, ^|\moveright|\<dimen>\<box>.\enskip
+This acts exactly like an unadorned \<box> command, except that the new box
+being appended to the vertical list is also shifted left or right by the
+specified amount.
+
+\\^|\unvbox|\<8-bit number>, ^|\unvcopy|\<8-bit number>.\enskip
+If the specified box register is void, nothing happens. Otherwise that
+register must contain a vbox. The vertical list inside that box is
+appended to the current vertical list, without changing it in any way.
+The value of\/ |\prevdepth| is not affected. The box register becomes void
+after |\unvbox|, but it remains unchanged by |\unvcopy|.
+
+\\\<horizontal rule>.\enskip
+The specified ^{rule} is appended to the current list. Then |\prevdepth|
+is set to $-1000\pt$; this will prohibit interline glue when the next box
+is appended to the list.
+
+\\^|\halign|\<box specification>|{|\<alignment material>|}|.\enskip
+The ^\<alignment material> consists of a preamble followed by zero or more
+lines to be aligned; see Chapter~22. \TeX\ enters a new level of grouping,
+represented by the `|{|' and `|}|', within which changes to ^|\tabskip|
+will be confined. The alignment material can also contain optional
+occurrences of `^|\noalign|\<filler>|{|\<vertical mode material>|}|'
+between lines; this adds another level of grouping. \TeX\ operates in
+internal vertical mode while it works on the material in |\noalign| groups
+and when it appends lines of the alignment; the resulting internal
+vertical list will be appended to the enclosing vertical list after the
+alignment is completed, and the page builder will be exercised. The value
+of\/ |\prevdepth| at the time of the |\halign| is used at the beginning of
+the internal vertical list, and the final value of\/ |\prevdepth| is carried
+to the enclosing vertical list when the alignment is completed, so that the
+interline glue is calculated properly at the beginning and end of the
+alignment. \TeX\ also enters an additional level of grouping when it
+works on each individual entry of the alignment, during which time it acts
+in restricted horizontal mode; the individual entries will be hboxed as
+part of the final alignment, and their vertical material will ^{migrate}
+to the enclosing vertical list. The commands |\noalign|, ^|\omit|,
+^|\span|, ^|\cr|, ^|\crcr|, and |&| (where |&| denotes an explicit or
+implicit character of category~4) are intercepted by the alignment
+process, en route to \TeX's stomach, so they will not appear as commands in
+the stomach unless \TeX\ has lost track of what alignment they belong to.
+
+\\^|\indent|.\enskip
+The ^|\parskip| glue is appended to the current list, unless \TeX\ is
+in internal vertical mode and the current list is empty.
+Then \TeX\ enters unrestricted horizontal mode, starting the horizontal
+list with an empty hbox whose width is ^|\parindent|. The ^|\everypar|
+tokens are inserted into \TeX's input. The page builder is~exercised.
+When the paragraph is eventually completed, horizontal mode will come to
+an end as described in Chapter~25.
+
+\\^|\noindent|.\enskip
+This is exactly like |\indent|, except that \TeX\ starts out in horizontal
+mode with an empty list instead of with an indentation.
+
+\\^|\par|.\enskip
+The primitive |\par| command has no effect when \TeX\ is in vertical mode,
+except that the page builder is exercised in case something is present
+on the contribution list, and the paragraph shape parameters are cleared.
+
+\\|{|.\enskip
+A character token of category 1, or a control sequence like~|\bgroup|
+that has been |\let| equal to such a character token, causes \TeX\ to
+start a new level of ^{grouping}. When such a group ends---with `|}|'---\TeX\
+will undo the effects of non-global assignments without leaving whatever
+mode it is in at that time.
+
+\\Some commands are incompatible with vertical mode because they are
+intrinsically horizontal. When the following commands appear in
+vertical modes they cause \TeX\ to begin a ^{new paragraph}:
+\beginsyntax
+<horizontal command>\is<letter>\alt<otherchar>\alt^|\char|\alt<chardef token>
+ \alt^|\noboundary|\alt^|\unhbox|\alt^|\unhcopy|\alt^|\valign|\alt^|\vrule|
+ \alt^|\hskip|\alt^|\hfil|\alt^|\hfill|\alt^|\hss|\alt^|\hfilneg|
+ \alt^|\accent|\alt^|\discretionary|\alt^|\-|\alt^|\|\]\alt|$|
+\endsyntax
+Here \<letter> and \<otherchar> stand for explicit or implicit character
+tokens of categories 11 and~12. If any of these tokens occurs as a
+command in vertical mode or internal vertical mode, \TeX\ automatically
+performs an |\indent| command as explained above. This leads into
+horizontal mode with the |\everypar| tokens in the input, after
+which \TeX\ will see the \<horizontal command> again.
+
+\\^|\end|.\enskip
+This command is not allowed in internal vertical mode. In regular vertical
+mode it terminates \TeX\ if the main vertical list is empty and
+^|\deadcycles=0|. Otherwise \TeX\ backs up the |\end| command so that it can
+be read again; then it exercises the page builder, after appending a
+box/glue/penalty combination that will force the output routine to act.
+\ (See the end of Chapter~23.)
+
+\\^|\dump|.\enskip
+(Allowed only in ^|INITEX|, not in production versions of \TeX.) \
+This command is treated exactly like |\end|, but it must not appear
+inside a group. It outputs a format file that can be loaded into \TeX's
+memory at comparatively high speed to restore the current status.
+
+\\None of the above: If any other primitive command of \TeX\ occurs
+in vertical mode, an error message will be given, and \TeX\ will try
+to recover in a reasonable way. For example, if a superscript or
+subscript symbol appears, or if any other inherently mathematical
+command is given, \TeX\ will try to insert a `|$|' (which will start a
+paragraph and enter math mode). On the other hand if a totally
+misplaced token like ^|\endcsname| or |\omit| or |\eqno| or |#| appears
+in vertical mode, \TeX\ will simply ignore~it, after reporting
+the error. You might enjoy trying to type some really stupid input,
+just to see what happens. \ (Say `|\tracingall|' first, as explained
+in Chapter~27, in order to get maximum information.)
+
+\endchapter
+
+The first and most striking feature is the Verticality of composition,
+as opposed to the Horizontality of all anterior structural modes.
+\author COCKBURN ^{MUIR}, {\sl Pagan or Christian?\/} (1860) % p61
+
+\bigskip
+
+Sometimes when I have finished a book I give a summary of the whole of it.
+\author ROBERT WILLIAM ^{DALE}, {\sl Nine Lectures on Preaching} (1878)
+ % viii.231
+
+\eject
+ \beginchapter Chapter 25. Summary of\\Horizontal Mode
+
+^^{horizontal mode}
+Continuing the survey that was begun in Chapter 24, let us investigate
+exactly what \TeX's digestive processes can do, when \TeX\ is building
+lists in horizontal mode or in restricted horizontal mode.
+
+\ninepoint
+\def\\{\smallbreak\textindent{$\bull$}}
+\medbreak
+\centerline{$*\qquad*\qquad*$}
+\medskip\noindent
+Three asterisks, just like those that appear here, can be found near the
+end of Chapter~24.
+Everything preceding the three asterisks in that chapter applies to
+horizontal mode as well as to vertical mode, so we need not repeat
+all those rules. In particular, Chapter~24 explains assignment commands,
+and it tells how kerns, penalties, marks, insertions, adjustments,
+and ``whatsits'' are put into horizontal lists. Our present goal
+is to consider the commands that have an intrinsically horizontal
+flavor, in the sense that they behave differently in horizontal
+mode than they do in vertical or math modes.
+
+One of the things characteristic of horizontal mode is the ``^{space
+factor},'' which modifies the width of spaces as described in Chapter~12.
+If a command changes the value of\/ ^|\spacefactor|, that fact is
+specifically noted here. The space factor is initially set to~1000, when
+\TeX\ begins to form a horizontal list, except in the case of\/ |\valign| and
+|\noalign| when the space factor of the outer list continues inside the
+inner one.
+
+\\^|\hskip|\<glue>, ^|\hfil|, ^|\hfill|, ^|\hss|, ^|\hfilneg|.\enskip
+A glue item is appended to the current horizontal list.
+
+\\\<leaders>\<box or rule>\<horizontal skip>.\enskip
+Here ^\<horizontal skip> refers to one of the five glue-appending commands
+just mentioned; the formal syntax for \<leaders> and for \<box or rule> is
+given in Chapter~24. A glue item that produces ^{leaders} is appended.
+
+\\^\<space token>.\enskip
+Spaces append glue to the current list; the exact amount of glue depends on
+|\spacefactor|, the current font, and the |\spaceskip| and |\xspaceskip|
+parameters, as described in Chapter~12.
+
+\\|\|\].\enskip ^^{control space}
+A control-space command appends glue to the current list, using the same amount
+that a \<space token> inserts when the space factor is 1000.
+
+\\^\<box>.\enskip
+The box is constructed, and if the result is void nothing happens.
+Otherwise the new box is appended to the current list, and
+the space factor is~set~to~1000.
+
+\\^|\raise|\<dimen>\<box>, ^|\lower|\<dimen>\<box>.\enskip
+This acts just like an unadorned \<box> command, except that the new box
+being appended to the horizontal list is also shifted up or down by the
+specified amount.
+
+\\^|\unhbox|\<8-bit number>, ^|\unhcopy|\<8-bit number>.\enskip
+If the specified box register is void, nothing happens. Otherwise that
+register must contain an hbox. The horizontal list inside that box is
+appended to the current horizontal list, without changing it in any way.
+The value of\/ |\spacefactor| is not affected. The box register becomes void
+after |\unhbox|, but it remains unchanged by |\unhcopy|.
+
+\\\<vertical rule>.\enskip
+The specified ^{rule} is appended to the current list, and the |\spacefactor|
+is set to 1000.
+
+\\^|\valign|\<box specification>|{|\<alignment material>|}|.\enskip
+The ^\<alignment material> consists of a preamble followed by zero or more
+columns to be aligned; see Chapter~22. \TeX\ enters a new level of grouping,
+represented by the `|{|' and `|}|', within which changes to ^|\tabskip|
+will be confined. The alignment material can also contain optional
+occurrences of `|\noalign|\<filler>|{|\<horizontal mode material>|}|'
+between columns; this adds another level of grouping. \TeX\ operates in
+restricted horizontal mode while it works on the material in ^|\noalign|
+\vadjust{\eject}% squeeze another line onto page 285, we need it on page 286!
+groups and when it appends columns of the \hbox{alignment}; the resulting
+internal horizontal list will be appended to the enclosing horizontal list
+after the alignment is completed. The value of\/ |\spacefactor| at the time
+of the |\valign| is used at the beginning of the internal horizontal list,
+and the final value of\/ |\spacefactor| is carried to the enclosing
+horizontal list when the alignment is completed. The space factor is set
+to 1000 after each column; hence it affects the results only in |\noalign|
+groups. \TeX\ also enters an additional level of grouping when it works
+on each individual entry of the alignment, during which time it acts in
+internal vertical mode; the individual entries will be vboxed as part of
+the final alignment.
+%No room for the following redundant remarks:
+% The commands |\noalign|, |\omit|, |\span|, |\cr|,
+%|\crcr|, and |&| (where |&| denotes an explicit or implicit character of
+%category~4) are intercepted by the alignment process, en route to \TeX's
+%stomach, so they will not appear as commands in the stomach unless \TeX\
+%has lost track of what alignment they belong to.
+
+\\^|\indent|.\enskip
+An empty box of width ^|\parindent| is appended to the current list, and
+the space factor is set to 1000.
+
+\\^|\noindent|.\enskip
+This command has no effect in horizontal modes.
+
+\\^|\par|.\enskip
+The primitive |\par| command, also called ^|\endgraf| in plain \TeX,
+does nothing in restricted horizontal mode. But it terminates horizontal
+mode: The current list is finished off by doing ^|\unskip| ^|\penalty10000|
+^^|\parfillskip|
+|\hskip\parfillskip|, then it is broken into lines as explained in Chapter~14,
+and \TeX\ returns to the enclosing vertical or internal vertical mode.
+The lines of the paragraph are appended to the enclosing vertical list,
+interspersed with interline glue and interline penalties, and with
+the ^{migration} of vertical material that was in the horizontal list.
+Then \TeX\ exercises the page builder.
+
+\\|{|.\enskip
+A character token of category 1, or a control sequence like~|\bgroup|
+that has been |\let| equal to such a character token, causes \TeX\ to
+start a new level of ^{grouping}. When such a group ends---with `|}|'---\TeX\
+will undo the effects of non-global assignments without leaving whatever
+mode it is in at that time.
+
+\\Some commands are incompatible with horizontal mode because they are
+intrinsically vertical. When the following commands appear in unrestricted
+horizontal mode, they cause \TeX\ to conclude the current paragraph:
+^^{paragraph end, implied}
+\beginsyntax
+<vertical command>\is^|\unvbox|\alt^|\unvcopy|\alt^|\halign|\alt^|\hrule|
+ \alt^|\vskip|\alt^|\vfil|\alt^|\vfill|\alt^|\vss|\alt^|\vfilneg|%
+ \alt^|\end|\alt^|\dump|
+\endsyntax
+The appearance of a \<vertical command> in restricted horizontal mode is
+forbidden, but in regular horizontal mode it causes \TeX\ to insert the
+token \cstok{par} into the input; after reading and expanding this \cstok{par}
+token, \TeX\ will see the \<vertical command> token again. \ (The current
+meaning of the control sequence ^|\par| will be used; \cstok{par} might no
+longer stand for \TeX's |\par| primitive.)
+
+\\\<letter>, \<otherchar>, \kern-1pt^|\char|\<8-bit number>, \<chardef token>,
+\kern-1pt^|\noboundary|.\enskip
+The most common commands of all are the character commands that tell
+\TeX\ to append a character to the current horizontal
+list, using the current font.
+If two or more commands of this type occur in succession, \TeX\ processes
+them all as a unit, converting to ligatures and/or
+inserting kerns as directed by the font information. \ (Ligatures and
+kerns may be influenced by invisible ``boundary'' characters at the left
+and right, unless |\noboundary| appears.) \ Each character
+command adjusts ^|\spacefactor|, using
+the ^|\sfcode| table as described in Chapter~12.
+In unrestricted horizontal mode, a
+`|\discretionary{}{}{}|' item is appended after a character whose code is
+the ^|\hyphenchar| of its font, or after a ligature formed from a sequence
+that ends with such a character. ^^|\discretionary|
+
+\\^|\accent|\<8-bit number>\<optional assignments>.
+Here ^\<optional assignments> stands for zero or more \<assignment>
+commands other than ^|\setbox|.
+If the assignments are not followed by a \<character>, where
+\<character> stands for any of the commands just discussed in the previous
+paragraph, \TeX\ treats |\accent| as if it were |\char|, except that
+the space factor is set to 1000. Otherwise the character that follows
+the assignment is accented by the character that corresponds to the
+\<8-bit number>. \ (The purpose of the intervening assignments is to
+allow the accenter and accentee to be in different fonts.) \ If the
+accent must be moved up or down, it is put into an hbox that is
+raised or lowered. Then the accent is effectively superposed on the
+character by means of kerns, in such a way that the width of the accent
+does not influence the width of the resulting horizontal list.
+Finally, \TeX\ sets |\spacefactor=1000|.
+
+\\^|\/|.\enskip
+If the last item on the current list is a character or ligature, an
+explicit kern for its ^{italic correction} is appended.
+
+\\^|\discretionary|\<general text>\<general text>\<general text>.\enskip
+The three general texts are processed in restricted horizontal mode. They
+should contain only fixed-width things; hence they aren't really very
+general in this case. More precisely, the horizontal list formed by each
+discretionary general text must consist only of characters, ligatures,
+kerns, boxes, and rules; there should be no glue or penalty items, etc.
+This command appends a discretionary item to the current list; see
+Chapter~14 for the meaning of a discretionary item. The space factor is
+not changed.
+
+\\^|\-|.\enskip
+This ``discretionary hyphen'' command is defined in Appendix H.
+
+\\^|\setlanguage|\<number>.\enskip
+See the conclusion of Appendix H.
+
+\\|$|.\enskip
+A ``^{math shift}'' character causes \TeX\ to enter math mode or display math
+mode in the following way:
+\TeX\ looks at the following token without expanding it. If that token
+is a~|$| and if \TeX\ is currently in unrestricted horizontal mode,
+then \TeX\ breaks the current paragraph into lines as explained above
+(unless the current list is empty), returns to the enclosing vertical
+mode or internal vertical mode, calculates values like |\prevgraf|
+and |\displaywidth| and |\predisplaysize|, enters a new level of grouping,
+inserts the |\everydisplay| tokens into the input, exercises the page
+builder, processes `\<math mode material>|$$|' in display math mode, puts
+the display into the enclosing vertical list as explained in Chapter~19
+(letting vertical material ^{migrate}), exercises the page builder again,
+increases |\prevgraf| by~3, and resumes horizontal mode again, with an
+empty list and with the space factor equal to~1000. \ (You got that?) \
+Otherwise \TeX\ puts the looked-at token back into the
+input, enters a new level of grouping, inserts the |\everymath| tokens,
+and processes `\<math mode material>|$|'; the math mode material
+is converted to a horizontal list and appended to the current list,
+surrounded by ``math-on'' and ``math-off'' items, and the space factor
+is set to~1000. One consequence of these rules is that `|$$|' in
+restricted horizontal mode simply yields an empty math formula.
+
+\\None of the above: If any other primitive command of \TeX\ occurs
+in horizontal mode, an error message will be given, and \TeX\ will try
+to recover in a reasonable way. For example, if a superscript or
+subscript symbol appears, or if any other inherently mathematical
+command is given, \TeX\ will try to insert a `|$|' just before the
+offending token; this will enter math mode.
+%No room for the following redundant remarks:
+%On the other hand if a totally
+%misplaced token like |\endcsname| or |\omit| or |\eqno| or |#| appears
+%in horizontal mode, \TeX\ will simply ignore it, after reporting
+%the error. You might enjoy trying to type some really stupid input,
+%just to see what happens. \ (Say `|\tracingall|' first, as explained
+%in Chapter~27, in order to get maximum information.)
+
+\endchapter
+
+\strut{\rm Otherwise.} %
+ You may reduce all\/ \kern.5pt{\rm Verticals} into\/ {\rm Horizontals}.
+\author JOSEPH ^{MOXON}, {\sl A Tutor to Astronomie and Geographie\/} (1659)
+
+\bigskip
+
+\strut\tt!~You can't use `\bslash moveleft' in horizontal mode.
+\author \TeX\ (1982)
+
+\eject
+ \beginchapter Chapter 26. Summary of\\Math Mode
+
+^^{math mode}
+To conclude the survey that was begun in Chapter 24, let us investigate
+exactly what \TeX's digestive processes can do when \TeX\ is building
+lists in math mode or in display math mode.
+
+\ninepoint
+\def\\{\smallbreak\textindent{$\bull$}}
+\medbreak
+\centerline{$*\qquad*\qquad*$}
+\medskip\noindent
+Three asterisks, just like those that appear here, can be found near the
+end of Chapter~24.
+Everything preceding the three asterisks in that chapter applies to
+math mode as well as to vertical mode, so we need not repeat
+all those rules. In particular, Chapter~24 explains assignment commands,
+and it tells how kerns, penalties, marks, insertions, adjustments,
+and ``whatsits'' are put into math lists. Our present goal
+is to consider the commands that have an intrinsically mathematical
+flavor, in the sense that they behave differently in math
+mode than they do in vertical or horizontal modes.
+
+Math lists are somewhat different from \TeX's other lists because they
+contain three-pronged ``^{atoms}'' (see Chapter~17). Atoms come in thirteen
+flavors: Ord, Op, Bin, Rel, Open, Close, Punct, Inner, Over, Under, Acc,
+Rad, and Vcent. Each atom contains three ``^{fields}'' called its ^{nucleus},
+^{superscript}, and ^{subscript}; and each field is either empty or is filled
+with a math symbol, a box, or a subsidiary math list. Math symbols, in turn,
+have two components: a family number and a position number.
+
+It's convenient to introduce a few more rules of syntax, in order to specify
+what goes into a math list:
+\beginsyntax
+<character>\is<letter>\alt<otherchar>\alt^|\char|<8-bit number>\alt%
+ <chardef token>
+<math character>\is^|\mathchar|<15-bit number>\alt<mathchardef token>
+ \alt|\delimiter|<27-bit number>
+<math symbol>\is<character>\alt<math character>
+<math field>\is<filler><math symbol>\alt<filler>|{|<math mode material>|}|
+<delim>\is<filler>^|\delimiter|<27-bit number>
+ \alt<filler><letter>\alt<filler><otherchar>
+\endsyntax
+We have already seen the concept of \<character> in Chapter~25.
+Indeed, characters are \TeX's staple food: The vast majority of all
+commands that reach \TeX's digestive processes in horizontal mode are instances
+of the \<character> command, which specifies a number between 0 and~255 that
+causes \TeX\ to typeset the corresponding character in the current font.
+When \TeX\ is in math mode or display math mode, a \<character> command
+takes on added significance: It specifies a number between 0 and~$32767=
+2^{15}-1$. This is done by replacing the character number by its
+^|\mathcode| value. If the
+|\mathcode| value turns out to be $32768=\null$\hex{8000}, however,
+the \<character>
+is replaced by an ^{active character} token having the original character
+code (0 to~255); \TeX\ forgets the original \<character> and expands this
+active character according to the rules of Chapter~20.
+
+A \<math character> defines a 15-bit number either by specifying it
+directly with ^|\mathchar| or in a previous ^|\mathchardef|, or by
+specifying a 27-bit |\delimiter| value; in the latter case, the least
+significant 12~bits are discarded.
+
+It follows that every \<math symbol>, as defined by the syntax above,
+specifies a 15-bit number, i.e., a number between 0 and~32767.
+Such a number can be represented in the form $4096c+256f+a$, where
+$0\le c<8$, $0\le f<16$, and $0\le a<256$. If $c=7$, \TeX\ changes $c$~to~0;
+and in this case if the current
+value of\/ ^|\fam| is between 0 and~15, \TeX\ also replaces $f$~by~|\fam|.
+This procedure yields, in all cases, a class number~$c$ between 0~and~6,
+a family number~$f$ between 0~and~15, and a position number~$a$ between
+0 and~255. \ (\TeX\ initializes the value of\/ |\fam| by implicitly
+putting the assignment `|\fam=-1|' at the very beginning of\/ |\everymath|
+and |\everydisplay|. Thus, the substitution of\/ |\fam| for~$f$ will occur
+only if the user has explicitly changed~|\fam| within the formula.)
+
+A \<math field> is used to specify the nucleus, superscript, or subscript
+of an atom. When a \<math field> is a \<math symbol>, the $f$ and~$a$
+numbers of that symbol go into the atomic field. Otherwise the \<math field>
+begins with a~`|{|', which causes \TeX\ to enter a new level
+of grouping and to begin a new math list; the ensuing \<math mode material>
+is terminated by a~`|}|', at which point the group ends and the resulting
+math list goes into the atomic field. If the math list turns out to be
+simply a single Ord atom without subscripts or superscripts,
+or an Acc whose nucleus is an Ord, the
+enclosing braces are effectively removed.
+
+A \<delim> is used to define both a ``small character'' $a$ in family~$f$
+and a ``large character''~$b$ in family~$g$, where $0\le a,b\le255$
+and $0\le f,g\le15$; these character codes are used to construct variable-size
+^{delimiters}, as explained in Appendix~G\null. If the \<delim> is given
+explicitly in terms of a 27-bit number, the desired codes are obtained
+by interpreting that number as
+$c\cdot2^{24}+f\cdot2^{20}+a\cdot2^{12}+g\cdot2^8+b$, ignoring the value
+of~$c$. Otherwise the delimiter is specified as a \<letter> or
+\<otherchar> token, and the 24-bit ^|\delcode| value of that character is
+interpreted as $f\cdot2^{20}+a\cdot2^{12}+g\cdot2^8+b$.
+
+\smallskip
+Now let's study the individual commands as \TeX\ obeys them in math mode,
+considering first the ones that have analogs in vertical and/or horizontal
+mode:
+
+\\^|\hskip|\<glue>, ^|\hfil|, ^|\hfill|, ^|\hss|, ^|\hfilneg|,
+^|\mskip|\<muglue>.\enskip
+A glue item is appended to the current math list.
+
+\\\<leaders>\<box or rule>\<horizontal skip>.\enskip
+Here ^\<horizontal skip> refers to one of the first five glue-appending
+commands just mentioned; the formal syntax for \<leaders> and
+for \<box or rule> is given in Chapter~24. A glue item that produces
+^{leaders} is appended to the current list.
+
+\\^|\nonscript|. A special glue item of width zero is appended; it will
+have the effect of cancelling the following item on the list, if that item
+is glue and if the |\nonscript| is eventually typeset in ``script style''
+or in ``scriptscript style.''
+
+\\^|\noboundary|. This command is redundant and therefore has no
+effect; boundary ligatures are automatically disabled in math modes.
+
+\\^\<space token>.\enskip
+Spaces have no effect in math modes.
+
+\\|\|\].\enskip ^^{control space}
+A control-space command appends glue to the current list, using the same amount
+that a \<space token> inserts in horizontal mode when the space factor is 1000.
+
+\\^\<box>.\enskip
+The box is constructed, and if the result is void nothing happens.
+Otherwise a new Ord atom is appended to the current math list, and the
+box becomes its nucleus.
+
+\\^|\raise|\<dimen>\<box>, ^|\lower|\<dimen>\<box>.\enskip
+This acts just like an unadorned \<box> command, except that the new box
+being put into the nucleus is also shifted up or down by the specified amount.
+
+\\^|\vcenter|\stretch\<box specification>\stretch
+|{|\<vertical mode material>|}|.\enskip
+A vbox is formed as if `|\vcenter|' had been `|\vbox|'. Then a new
+^{Vcent} atom is appended to the current math list, and the box becomes
+its nucleus.
+
+\\\<vertical rule>.\enskip
+A ^{rule} is appended to the current list (not as an atom).
+
+\\^|\halign|\<box specification>|{|\<alignment material>|}|.\enskip
+This command is allowed only in display math mode, and only when the current
+math list is empty. The alignment is carried out exactly as if it were done in
+the enclosing vertical mode (see Chapter~24), except that the lines are shifted
+right by the ^|\displayindent|. The closing `|}|' may be followed by
+optional \<assignment> commands other than ^|\setbox|,
+after which `|$$|'~must conclude
+the display. \TeX\ will insert the ^|\abovedisplayskip| and
+^|\belowdisplayskip| glue before and after the result of the alignment.
+
+\\^|\indent|.\enskip
+An empty box of width ^|\parindent| is appended to the current list, as the
+nucleus of a new Ord atom.
+
+\\^|\noindent|.\enskip
+This command has no effect in math modes.
+
+\\|{|\<math mode material>|}|.\enskip
+A character token of category 1, or a control sequence like~|\bgroup|
+that has been |\let| equal to such a character token, causes \TeX\ to
+start a new level of ^{grouping} and also to begin work on a new
+math list. When such a group ends---with `|}|'---\TeX\
+uses the resulting math list as the nucleus of a new Ord atom that
+is appended to the current list. If the resulting math list is a
+single Acc atom, however (i.e., an accented quantity),
+that atom itself is appended.
+
+\\\<math symbol>.\enskip
+(This is the most common command in math mode; see the syntax near the
+beginning of this chapter.) \ A math symbol determines three values,
+$c$,~$f$, and~$a$, as explained earlier. \TeX\ appends an atom to the current
+list, where the atom is of type Ord, Op, Bin, Rel, Open, Close, or Punct,
+according as the value of~$c$ is 0,~1, 2, 3, 4, 5, or~6. The nucleus of
+this atom is the math symbol defined by $f$ and~$a$.
+
+\\^\<math atom>\<math field>.\enskip
+A \<math atom> command is any of the following:
+\begindisplay
+|\mathord|\alt|\mathop|\alt|\mathbin|\alt|\mathrel|\alt|\mathopen|\cr
+\qquad\alt|\mathclose|\alt|\mathpunct|\alt|\mathinner|%
+ \alt|\underline|\alt|\overline|\cr
+\enddisplay
+\TeX\ processes the \<math field>, then appends a new atom of the
+specified type to the current list; the nucleus of this atom contains
+the specified field.
+
+\\^|\mathaccent|\<15-bit number>\<math field>.\enskip
+\TeX\ converts the \<15-bit number> into $c$, $f$, and~$a$ as it does
+with any |\mathchar|. Then it processes the \<math field> and appends a
+new Acc atom to the current list. The nucleus of this atom contains
+the specified field; the accent character in this atom contains $(a,f)$.
+
+\\^|\radical|\<27-bit number>\<math field>.\enskip
+\TeX\ converts the \<27-bit number> into $a$, $f$, $b$, and~$g$ as it does
+with any |\delimiter|. Then it processes the \<math field> and appends a
+new Rad atom to the current list. The nucleus of this atom contains
+the specified field; the delimiter field in this atom contains
+$(a,f)$ and $(b,g)$.
+
+\\^\<superscript>\<math field>.\enskip
+A \<superscript> command is an explicit or implicit character token of
+category~7. If the current list does not end with an atom, a new Ord atom
+with all fields empty is appended; thus the current list will end with an
+atom, in~all cases. The superscript field of this atom should be empty; it
+is made nonempty by changing it to the result of the specified \<math field>.
+
+\\^\<subscript>\<math field>.\enskip
+A \<subscript> command is an explicit or implicit character token of
+category~8. It acts just like a \<superscript> command, except, of course,
+that it affects the subscript field instead of the superscript field.
+
+\\^|\displaylimits|, ^|\limits|, ^|\nolimits|.\enskip These commands are
+allowed only if the current list ends with an Op atom. They modify a
+special field in that Op atom, specifying what conventions should be used
+with respect to limits. The normal value of that field is |\displaylimits|.
+
+\\^|\/|.\enskip
+A kern of width zero is appended to the current list. \ (This will have the
+effect of adding the italic correction to the previous character, if the
+italic correction wouldn't normally have been added.)
+
+\\^|\discretionary|\<general text>\<general text>\<general text>.
+This command is treated just as in horizontal mode (see Chapter~25), but the
+third \<general text> must produce an empty list.
+
+\\^|\-|.\enskip
+This command is usually equivalent to `|\discretionary{-}{}{}|';
+the `|-|' is therefore interpreted as a ^{hyphen}, not as a minus sign.
+\ (See Appendix~H.)
+
+\def\s{\hskip0pt plus1pt}
+\\^|\mathchoice|\s
+$\langle$filler$\rangle$\s|{|\s$\langle$math mode material$\rangle$\s|}|\s
+$\langle$filler$\rangle$\s|{|\s$\langle$math mode material$\rangle$\s|}|\break
+$\langle$filler$\rangle$|{|$\langle$math mode material$\rangle$|}|
+$\langle$filler$\rangle$|{|$\langle$math mode material$\rangle$|}|.
+Four math lists, which are defined as in the
+second alternative of a \<math field>, are
+recorded in a ``choice item'' that is appended to the current list.
+
+\\^|\displaystyle|, ^|\textstyle|, ^|\scriptstyle|,
+^|\scriptscriptstyle|.\enskip A style-change item that corresponds to the
+specified style is appended to the current list.
+
+\\^|\left|\<delim>\<math mode material>^|\right|\<delim>.\enskip
+\TeX\ begins a new group, and processes the \<math mode material>
+by starting out with a new math list that begins with a left boundary
+item containing the first delimiter. This group must be terminated by
+`|\right|', at which time the internal math list is completed with a
+right boundary item containing the second delimiter. Then \TeX\
+appends an Inner atom to the current list; the nucleus of this atom
+contains the internal math list.
+
+\\\<generalized fraction command>.\enskip
+This command takes one of six forms:
+\begindisplay
+^|\over|\alt^|\atop|\alt^|\above|\<dimen>\cr
+\qquad\alt^|\overwithdelims|\<delim>\<delim>\cr
+\qquad\alt^|\atopwithdelims|\<delim>\<delim>\cr
+\qquad\alt^|\abovewithdelims|\<delim>\<delim>\<dimen>\cr
+\enddisplay
+(See Chapter 17.) \ When \TeX\ sees a \<generalized fraction command> it
+takes the entire current list and puts it into the numerator field of a
+generalized fraction item. The denominator field of this new item is
+temporarily empty; the left and right delimiter fields are set equal
+to the specified delimiter codes. \TeX\ saves this generalized fraction
+item in a special place associated with the current level of math mode
+processing. \ (There should be no other generalized fraction item in
+that special place, because constructions like `|a\over b\over c|' are
+illegal.) \ Then \TeX\ makes the current list empty and continues to
+process commands in math mode. Later on, when the current level of math
+mode is completed (either by coming to a~`|$|' or a~`|}|' or a~|\right|,
+depending on the nature of the current group), the current list will be
+moved into the denominator field of the generalized fraction item that
+was saved; then that item, all by itself, will take the place of the
+entire list. However, in the special case that the current list began
+with |\left| and will end with |\right|, the boundary items will be
+extracted from the numerator and denominator of the generalized fraction,
+and the final list will consist of three items: left boundary,
+generalized fraction, right boundary. \ (If you want to watch the process
+by which math lists are built, you might find it helpful to type `^|\showlists|'
+while \TeX\ is processing the denominator of a generalized fraction.)
+
+\\^\<eqno>\<math mode material>|$|.\enskip
+Here \<eqno> stands for either ^|\eqno| or ^|\leqno|; these commands
+are allowed only in display math mode. Upon reading \<eqno>, \TeX\
+enters a new level of grouping, inserts the ^|\everymath| tokens,
+and enters non-display math mode to put the \<math mode material>
+into a math list. When that math list is completed, \TeX\ converts it
+to a horizontal list and puts the result into a box that will be used
+as the equation number of the current display. The closing |$|~token
+will be put back into the input, where it will terminate the display.
+
+\\|$|.\enskip
+If \TeX\ is in display math mode, it reads one more token, which must
+also be~|$|. In either case, the math-shift command terminates the current
+level of math mode processing and ends the current group, which should
+have begun with either~|$| or~\<eqno>. Once the math list is finished, it
+is converted into a horizontal list as explained in Appendix~G\null. \TeX\
+scans \<one optional space> after completing a displayed formula; this is
+usually the implicit space at the end of a line in the input file.
+
+\\^|\unhbox|\<8-bit number>, ^|\unhcopy|\<8-bit number>.\enskip
+The specified box register must be void. Nothing happens.
+
+\\None of the above: If any other primitive command of \TeX\ occurs in
+math mode, an error message will be given, and \TeX\ will try to recover
+in a reasonable way. For example, if a |\par| command appears, or if any
+other inherently non-mathematical command is given, \TeX\ will try to
+insert a `|$|' just before the offending token; this will lead out of math
+mode. On the other hand if a totally misplaced token like |\endcsname| or
+|\omit| or |#| appears in math mode, \TeX\ will simply ignore it, after
+reporting the error. You might enjoy trying to type some really stupid
+input, just to see what happens. \ (Say `|\tracingall|' first, as
+explained in Chapter~27, in order to get maximum information.)
+
+\ddangerexercise ^{Powers of ten}: The whole \TeX\ language has now been
+summarized completely. To~demonstrate how much you know, name all of the ways
+you can think of in which the numbers 10, ^^{Derek, Bo} 100, 1000, 10000,
+and 100000 have special significance to \TeX.
+\answer Radix 10 notation is used for numeric constants and for the output
+of numeric data. The first 10 |\count| registers are displayed at each
+|\shipout|, and their values are recorded on the |dvi| file at such times.
+% Also, TeX was first implemented on a DEC-10.
+% The \catcode for <space> is 10.
+% My birthday is January 10.
+% Can this all be just a coincidence?
+A box whose glue has stretched or shrunk to its stated stretchability
+or shrinkability has badness 100; this badness value separates ``loose''
+boxes from ``very loose'' or ``underfull'' ones. \TeX\ will scroll up
+to 100 errors in a single paragraph before giving up (see Chapter~27).
+The normal values of\/ |\spacefactor| and |\mag| are 1000. A~|\prevdepth|
+value of $-1000\pt$ suppresses interline glue. The badness rating of a
+box is at most 10000, except that the |\badness| of
+an overfull box is 1000000. |INITEX| initializes |\tolerance| to
+10000, thereby making all line breaks feasible. Penalties of 10000 or more
+prohibit breaks; penalties of $-10000$ or less make breaks mandatory. The
+cost of a page break is 100000, if the badness is 10000 and if the
+associated penalties are less than 10000 in magnitude (see Chapter~15).
+
+\ddangerexercise ^{Powers of two}: Name all of the ways
+you can think of in which the numbers 8, 16, 32, 64, 128, 256, \dots\
+have special significance to \TeX.
+\answer \TeX\ allows constants to be expressed in radix 8 (octal)
+or radix~16 (hexadecimal) notation, and it uses hexadecimal notation to
+display |\char| and |\mathchar| codes. There are 16 families for math
+fonts, 16 input streams for |\read|, 16 output streams for |\write|.
+A |\catcode| value must be less than~16. The notation |^^?|, |^^@|,
+|^^A| specifies characters whose codes differ by~64
+from the codes of |?|, |@|, |A|; this convention applies only to
+characters with ASCII codes less than~128. There are 256 possible characters,
+hence 256 entries in each of the |\catcode|, |\mathcode|,
+|\lccode|, |\uccode|, |\sfcode|, and |\delcode| tables. All
+|\lccode|, |\uccode|, and |\char| values
+must be less than~256. A font has at most 256 characters. There are
+256~|\box| registers, 256~|\count| registers, 256~|\dimen| registers,
+256~|\skip| registers, 256~|\muskip| registers, 256~|\toks| registers,
+256~hyphenation tables.
+The ``at size'' of a font must be less than~$2048\pt$, i.e.,~$2^{11}\pt$.
+Math delimiters are encoded by multiplying the math~code of the ``small
+character'' by~$2^{12}$. The magnitude of
+a~\<dimen> value must be less than~$16384\pt$, i.e.,~$2^{14}\pt$;
+similarly, the \<factor> in a~\<fil dimen> must be less than~$2^{14}$.
+A~|\mathchar| or |\spacefactor| or |\sfcode| value must be less than~$2^{15}$;
+a~|\mathcode| or |\mag| value must be less than or equal to~$2^{15}$,
+and $2^{15}$ denotes an ``active'' math character. There
+are $2^{16}\rm\,sp$ per~pt. A~|\delcode| value
+must be less than~$2^{24}$; a~|\delimiter|, less than $2^{27}$.
+The |\end| command sometimes contributes
+a penalty of $-2^{30}$ to the current page. A~\<dimen> must be less than
+$2^{30}\rm\,sp$ in absolute value; a~\<number> must be
+less than $2^{31}$ in absolute value.
+
+\endchapter
+
+Mathematics is known in the trade as {\rm difficult,} or {\rm penalty, copy}
+because it is slower, more difficult, and more expensive to set in type
+than any other kind of copy normally occurring in books and journals.
+\author UNIVERSITY OF ^{CHICAGO} PRESS, {\sl A Manual of Style\/} %
+ (1969) % 12th edition, page 295
+
+\bigskip
+
+The tale of Math is a complex one,
+and it resists both a simple plot summary
+and a concise statement of its meaning.
+\author PATRICK K. ^{FORD}, {\sl The Mabinogi\/} (1977) % p89
+ % from his introduction to "Math Son of Mathonwy"
+
+\eject
+ \beginchapter Chapter 27. Recovery from\\Errors
+
+OK, everything you need to know about \TeX\ has been explained---unless you
+happen to be fallible. If you don't plan to make any errors, don't bother to
+read this chapter. Otherwise you might find it helpful to make use of some
+of the ways that \TeX\ tries to pinpoint bugs in your manuscript.
+
+In the trial runs you did when reading Chapter 6, you learned the general
+form of ^{error messages}, and you also learned the various ways in which
+you can respond to \TeX's complaints. With practice, you will be able to
+correct most errors ``online,'' as soon as \TeX\ has detected them, by
+inserting and deleting a few things. The right way to go about this is to
+be in a mellow mood when you approach \TeX, and to regard the error
+messages that you get as amusing puzzles---``Why did the machine do
+that?''---rather than as personal insults.
+
+\TeX\ knows how to issue more than a hundred different sorts of error messages,
+and you probably never will encounter all of them, because some types of
+mistakes are very hard to make. We discussed the ``undefined control sequence''
+error in Chapter~6; let's take a look at a few of the others now.
+
+If you misspell the name of some unit of measure---for example, if you
+type `|\hsize=4im|' instead of `|\hsize=4in|'---you'll get an error
+message that looks something like this:
+\begintt
+! Illegal unit of measure (pt inserted).
+<to be read again>
+ i
+<to be read again>
+ m
+<*> \hsize=4im
+ \input story
+?
+\endtt
+^^|Illegal unit|
+\TeX\ needs to see a legal unit before it can proceed; so in this case it has
+implicitly inserted `|pt|' at the current place in the input, and it has
+set |\hsize=4pt|.
+
+What's the best way to ^{recover} from such an error? Well, you should
+always type `|H|' or `|h|' to see the help message, if you aren't sure
+what the error message means. Then you can look at the lines of context
+and see that \TeX\ will read `|i|' and then `|m|' and then `~|\input|
+|story|~' if you simply hit \<return> and carry on. Unfortunately, this
+easy solution isn't very good, because the `|i|' and~`|m|' will be typeset
+as part of the text of a new paragraph. A much more graceful recovery is
+possible in this case, by first typing~`|2|'. This tells \TeX\ to discard
+the next two tokens that it reads; and after \TeX\ has done so, it will
+stop again in order to give you a chance to look over the new situation.
+Here is what you will see:
+\begintt
+<recently read> m
+|indent
+<*> \hsize=4im
+ \input story
+?
+\endtt
+Good; the `|i|' and `|m|' are read and gone. But if you hit \<return> now,
+\TeX\ will `|\input story|' and try to typeset the |story.tex| file with
+|\hsize=4pt|; that won't be an especially exciting experiment, because it
+will simply produce dozens of overfull boxes, one for every syllable
+of the story. Once again there's a better way: You can insert the command
+that you had originally intended, by typing
+\begintt
+I\hsize=4in
+\endtt
+now. This instructs \TeX\ to change |\hsize| to the correct value, after
+which it will |\input story| and you'll be on your way.
+
+\exercise Ben ^{User} typed `|8|', not `|2|', in response to the
+error message just considered; his idea was to delete `|i|', `|m|',
+`|\input|', and the five letters of `|story|'. But \TeX's response was
+\begintt
+<*> \hsize=4im \input stor
+ y
+\endtt
+Explain what happened.
+\answer He forgot to count the space; \TeX\ deleted `|i|', `|m|',
+`\]', `|\input|', and four letters. (But all is not lost; he can
+type `|1|' or `|2|', then \<return>, and after being prompted by `|*|' he
+can enter a new line of input.)
+
+\TeX\ usually tries to recover from errors either by ignoring a command
+that it doesn't understand, or by inserting something that will keep it
+happy. For example, we saw in Chapter~6 that \TeX\ ignores an
+undefined control sequence; and we just observed that \TeX\ inserts
+`|pt|' when it needs a physical unit of measure.
+Here's another example where \TeX\ puts something in:
+\begintt
+! Missing $ inserted.
+<inserted text>
+ $
+<to be read again>
+ ^
+l.11 the fact that 32768=2^
+ {15} wasn't interesting
+? H
+I've inserted a begin-math/end-math symbol since I think
+you left one out. Proceed, with fingers crossed.
+\endtt
+^^{help message} ^^|Missing|
+(The user has forgotten to enclose a formula in |$| signs, and \TeX\
+has tried to recover by inserting one.) \ In this case the \<inserted text>
+is explicitly shown, and it has not yet been read; by contrast, our
+previous example illustrated a case where \TeX\ had already internalized
+the `|pt|' that it had inserted. Thus the user has a chance here to
+remove the inserted~`|$|' before \TeX\ really sees it.
+
+What should be done? The error in this example occurred before \TeX\ noticed
+anything wrong; the characters `32768=2' have already been typeset in
+horizontal mode. There's no way to go back and cancel the past, so
+the lack of proper spacing around the `=' cannot be fixed. Our goal of
+error recovery in this case is therefore not to produce perfect
+output; we want rather to proceed in some way so that \TeX\ will pass by
+the present error and detect subsequent ones. If we were simply to hit
+\<return> now, our aim would not be achieved, because \TeX\ would
+typeset the ensuing text as a math formula: `$^{15} wasn't interesting
+\ldots$'\thinspace; another error would be detected when the paragraph is
+found to end before any closing~`|$|' has appeared. On the other hand,
+there's a more elaborate way to recover, namely to type~`|6|' and then
+`|I$^{15}$|'; this deletes `|$^{15}|' and inserts a
+correct partial formula. But that's more complicated than necessary.
+The best solution in this case is to type just~`|2|' and then go on;
+\TeX\ will typeset the incorrect equation `32768=215', but the important
+thing is that you will be able to check out the rest of the document as if
+this error hadn't occurred.
+
+\dangerexercise Here's a case in which a backslash was inadvertently omitted:
+\begintt
+! Missing control sequence inserted.
+<inserted text>
+ \inaccessible
+<to be read again>
+ m
+l.10 \def m
+ acro{replacement}
+\endtt
+% The help message issued by TeX refers to "exercise 27.2 in The TeXbook"!
+\TeX\ needs to see a control sequence after `|\def|', so it has inserted one
+that will allow the processing to continue. \ (This control sequence is
+shown as `^|\inaccessible|', but it has no relation to any control
+sequence that you can actually specify in an error-free manuscript.) \
+If you simply hit \<return> at this point, \TeX\ will define the
+inaccessible control sequence, but that won't do you much good;
+later references to |\macro| will be undefined. Explain how to
+recover from this error so that the effect will be the same as if
+line~10 of the input file had said `|\def\macro{replacement}|'.
+\answer First delete the unwanted tokens, then insert what you want:
+Type `|6|' and then `|I\macro|'. \ (Incidentally, there's a sneaky
+way to get at the |\inaccessible| control sequence by typing
+\begintt
+I\garbage{}\let\accessible=
+\endtt
+in response to an error message like this. The author ^^{Knuth} designed
+\TeX\ in such a way that you can't destroy anything by playing such
+nasty tricks.)
+
+\dangerexercise When you use the `|I|' option to respond to an error message,
+the rules of Chapter~8 imply that \TeX\ removes all spaces from the
+right-hand end of the line. Explain how you can use the `|I|'~option
+to insert a ^{space}, in spite of this fact.
+\answer `|I|\]|%|' does the trick, if |%| is a ^{comment character}.
+
+Some of the toughest errors to deal with are those in which you make a
+mistake on line~20 (say), but \TeX\ cannot tell that anything is amiss
+until it reaches line~25 or so. For example, if you forget a `|}|' that
+completes the argument to some macro, \TeX\ won't notice any problem
+until reaching the end of the next paragraph. In such cases you probably
+have lost the whole paragraph; but \TeX\ will usually be able to get
+straightened out in time to do the subsequent paragraphs as if
+nothing had happened. A ``^{runaway argument}'' will be displayed,
+and by looking at the beginning of that text you should be able to
+figure out where the missing `|}|' belongs.
+
+It's wise to remember that the first error in your document may well spawn
+spurious ``errors'' later on, because anomalous commands can inflict
+serious injury on \TeX's ability to cope with the subsequent material.
+But most of the time you will find that a single run through the
+machine will locate all of the places in which your input conflicts
+with \TeX's rules.
+
+When your error is due to misunderstanding rather than mistyping,
+the situation is even more serious: \TeX's error messages will probably
+not be very helpful, even if you ask \TeX\ for help.
+If you have unknowingly redefined an important control sequence---for
+example, if you have said `|\def\box{...}|'---all sorts of strange
+disasters might occur. Computers
+aren't clairvoyant, and \TeX\ can only explain what looks wrong from
+its own viewpoint; such an explanation is bound to be mysterious
+unless you can understand the machine's attitude. The solution
+to this problem is, of course, to seek human counsel and advice;
+or, as a last resort, to read the instructions in Chapters 2, 3, \dots, 26.
+
+\newcount\serialnumber
+\def\firstnumber{\serialnumber=0 }
+\def\nextnumber{\advance \serialnumber by 1
+ \number\serialnumber)\nobreak\hskip.2em }
+\dangerexercise J. H. ^{Quick} (a student) once defined the following
+set of macros:
+\begintt
+\newcount\serialnumber
+\def\firstnumber{\serialnumber=0 }
+\def\nextnumber{\advance \serialnumber by 1
+ \number\serialnumber)\nobreak\hskip.2em }
+\endtt
+Thus he could type, for example,
+\begintt
+\firstnumber
+\nextnumber xx, \nextnumber yy, and \nextnumber zz
+\endtt
+and \TeX\ would typeset
+\firstnumber
+`\nextnumber xx, \nextnumber yy, and \nextnumber zz'.
+Well, this worked fine, and he showed the macros to his buddies.
+But several months later he received a frantic phone call; one of his
+friends had just encountered a really ^{weird error} message:
+\begintt
+! Missing number, treated as zero.
+<to be read again>
+ c
+l.107 \nextnumber minusc
+ ule chances of error
+?
+\endtt
+Explain what happened, and advise Quick what to do.
+\answer The `^|minus|' of `|minuscule|' was treated as part of
+the |\hskip| command in |\nextnumber|. Quick should put `|\relax|'
+at the end of his macro. \ (The ^{keywords} ^|l|, ^|plus|, |minus|,
+^|width|, ^|depth|, or ^|height| might just happen to occur in text
+when \TeX\ is reading a glue specification or a rule specification;
+designers of general-purpose macros should guard against this.
+If you get a `^|Missing number|' error and you can't guess
+why \TeX\ is looking for a number, plant the instruction
+`|\tracingcommands=1|' shortly before the error point; your log file
+will show what command \TeX\ is working~on.)
+
+Sooner or later---hopefully sooner---you'll get \TeX\ to process your
+whole file without stopping once to complain. But maybe the output
+still won't be right; the mere fact that \TeX\ didn't stop doesn't mean
+that you can avoid proofreading. At this stage it's usually easy to
+see how to fix typographic errors by correcting the input. Errors of
+layout can be overcome by using methods we have discussed before:
+Overfull boxes can be cured as described in Chapter~6; bad breaks can
+be avoided by using ties or |\hbox| commands as discussed in Chapter~14;
+math formulas can be improved by applying the principles of
+Chapters 16--19.
+
+But your output may contain seemingly inexplicable errors. For example,
+if you have specified a font at some magnification that is not supported
+by your printing software, \TeX\ will not know that there is any
+problem, but the program that converts your |dvi| file to hardcopy
+might not tell you that it has substituted an ``approximate'' font
+for the real one; the resultant spacing may look quite horrible.
+
+If you can't find out what went wrong, try the old trick of simplifying
+your program: Remove all the things that do work, until you obtain
+the shortest possible input file that fails in the same way as the
+original. The shorter the file, the easier it will be for you or somebody
+else to pinpoint the problem.
+
+Perhaps you'll wonder why \TeX\ didn't put a blank space in some
+position where you think you typed a space. Remember that \TeX\ ignores
+^{spaces} that follow control words, when it reads your file.
+\ (\TeX\ also ignores a space after a \<number> or a \<unit of
+measure> that appears as an argument to a primitive command; but if
+you are using properly designed macros, such rules will not concern
+you, because you will probably not be using primitive commands~directly.)
+
+\ddanger On the other hand, if you are designing macros, the task of
+troubleshooting can be a lot more complicated. For example, you may
+discover that \TeX\ has emitted three blank spaces when it processed some
+long sequence of complicated code, consisting of several dozen commands.
+How can you find out where those spaces crept in? The answer is to set
+`^|\tracingcommands||=1|', as mentioned in Chapter~13. This tells \TeX\ to
+put an entry in your log file whenever it begins to execute a primitive
+command; you'll be able to see when the command is `|blank| |space|'.
+
+\danger Most implementations of \TeX\ allow you to ^{interrupt} the program
+in some way. This makes it possible to diagnose the causes of ^{infinite
+loops}. \TeX\ switches to ^|\errorstopmode| when interrupted; hence
+you have a chance to insert commands into the input: You can abort the
+run, or you can ^|\show| or change the current contents of control sequences,
+registers, etc. You can also get a feeling for where \TeX\ is spending most
+of its time, if you happen to be using an inefficient macro, since random
+interrupts will tend to occur in whatever place \TeX\ visits most often.
+
+\danger Sometimes an error is so bad that \TeX\ is forced to quit
+prematurely. For example, if you are running in ^|\batchmode| or
+^|\nonstopmode|, \TeX\ makes an ``^{emergency stop}'' if it needs
+input from the terminal; this happens when a necessary file cannot
+be opened, or when no ^|\end| command was found in the input
+document. Here are some of the messages you might get just before
+\TeX\ gives up the ghost: \enddanger
+
+{\ninepoint
+\def\fatal#1. {\medbreak{\tt#1.}\par\nobreak\smallskip\noindent\ignorespaces}
+\fatal
+Fatal format file error; I'm stymied.
+^^|Fatal format file error|
+This means that the preloaded format you have specified cannot be used,
+because it was prepared for a different version of \TeX.
+\fatal
+That makes 100 errors; please try again.
+\TeX\ has scrolled past 100 errors since the last paragraph ended, so
+it's probably in an~endless ^{loop}. ^^{infinite loop}
+\fatal
+Interwoven alignment preambles are not allowed.
+^^|Interwoven alignment preambles|
+If you have been so devious as to get this message, you will understand
+it, and you will deserve no sympathy.
+\fatal
+I can't go on meeting you like this.
+^^|I can't go on|
+A previous error has gotten \TeX\ out of whack. Fix it and try again.
+\fatal
+This can't happen.
+^^|This can't happen|
+Something is wrong with the \TeX\ you are using. Complain fiercely.
+\goodbreak}
+
+\danger There's also a dreadful message that \TeX\ issues only with
+great reluctance. But it can happen:
+\begintt
+TeX capacity exceeded, sorry.
+\endtt
+^^|TeX capacity exceeded|
+This, alas, means that you have tried to stretch \TeX\ too far. The
+message will tell you what part of \TeX's memory has become overloaded;
+one of the following fourteen things will be mentioned:
+\begindisplay
+|number of strings|\qquad(names of control sequences and files)\cr
+|pool size|\qquad(the characters in such names)\cr
+|main memory size|\qquad(boxes, glue, breakpoints, token lists,
+ characters, etc.)\cr
+|hash size|\qquad(control sequence names)\cr
+|font memory|\qquad(font metric data)\cr
+|exception dictionary|\qquad(hyphenation exceptions)\cr
+|input stack size|\qquad(simultaneous input sources)\cr
+|semantic nest size|\qquad(unfinished lists being constructed)\cr
+|parameter stack size|\qquad(macro parameters)\cr
+|buffer size|\qquad(characters in lines being read from files)\cr
+|save size|\qquad(values to restore at group ends)\cr
+|text input levels|\qquad(|\input| files and error insertions)\cr
+|grouping levels|\qquad(unfinished groups)\cr
+|pattern memory|\qquad(hyphenation pattern data)\cr
+\enddisplay
+The current amount of memory available will also be shown.
+
+\danger If you have a job that doesn't overflow \TeX's capacity, yet
+you want to see just how closely you have approached the limits,
+just set ^|\tracingstats| to a positive value before the end of your
+job. The log file will then conclude with a report on your actual
+usage of the first eleven things named above (i.e., the number of strings,
+\dots, the save size), in that order. ^^{stack positions}
+Furthermore, if you set |\tracingstats| equal to 2~or~more, \TeX\
+will show its current memory usage whenever it
+does a ^|\shipout| command. Such statistics are broken into two
+parts; `|490&5950|' means, for example, that 490 words are being used
+for ``large'' things like boxes, glue, and
+breakpoints, while 5950 words are being used for ``small'' things like
+tokens and characters.
+
+\danger What can be done if \TeX's capacity is exceeded? All of the
+above-listed components of the capacity can be increased, provided
+that your computer is large enough; in fact, the space necessary to
+increase one component can usually be obtained by decreasing some
+other component, without increasing the total size of \TeX\null.
+If you have an especially important application, you may be able
+to convince your local system people to provide you with a special
+\TeX\ whose capacities have been hand-tailored to your needs.
+But before taking such a drastic step, be sure that you are using
+\TeX\ properly. If you have specified a gigantic paragraph or
+a gigantic alignment that spans more than one page, you should
+change your approach, because \TeX\ has to read all the way to the
+end before it can complete the line-breaking or the alignment
+calculations; this consumes huge amounts of memory space. If you have
+built up an enormous macro library, you should remember that \TeX\
+has to remember all of the replacement texts that you define; therefore
+if memory space is in short supply, you should load only the macros
+that you need. \ (See Appendices B and~D\null, for ideas on how to make
+macros more compact.)
+
+\danger Some erroneous \TeX\ programs will overflow any finite
+memory capacity. For example, after `|\def\recurse{(\recurse)}|', the
+^^{recursion} use of\/ |\recurse| will immediately bomb out:
+\begintt
+! TeX capacity exceeded, sorry [input stack size=80].
+\recurse ->(\recurse
+ )
+\recurse ->(\recurse
+ )
+...
+\endtt
+The same sort of error will obviously occur no matter how much you increase
+\TeX's input stack size.
+
+\ddanger The special case of ``^|save size|'' capacity exceeded is one
+of the most troublesome errors to correct, especially if you run into
+the error only on long jobs. \TeX\ generally uses up two words of save
+size whenever it performs a non-global assignment to some quantity
+whose previous value was not assigned at the same level of ^{grouping}.
+When macros are written properly, there will rarely be a need for
+more than 100 or~so things on the ``^{save stack}''; but it's possible to
+make save stack usage grow without limit if you make both local and
+^{global assignments} to the same variable. You can figure out what \TeX\
+puts on the save stack by setting ^|\tracingrestores||=1|; then your
+log file will record information about whatever is removed from the
+stack at the end of a group. For example, let |\a| stand for the
+command `|\advance\day|~|by|~|1|'; let |\g| stand for
+`|\global\advance\day|~|by|~|1|'; and consider the following commands:
+\begintt
+\day=1 {\a\g\a\g\a}
+\endtt
+The first |\a| sets |\day=2| and remembers the old value |\day=1| by putting
+it on the save stack. The first |\g| sets |\day=3|, globally; nothing needs
+to go on the save stack at the time of a global assignment. The next |\a|
+sets |\day=4| and remembers the old value |\day=3| on the save stack.
+Then |\g| sets |\day=5|; then |\a| sets |\day=6| and remembers |\day=5|.
+^^{right brace}
+Finally the `|}|' causes \TeX\ to go back through the save stack; if
+|\tracingrestores=1| at this point, the log file will get the following
+data:
+\begintt
+{restoring \day=5}
+{retaining \day=5}
+{retaining \day=5}
+\endtt
+Explanation: The |\day| parameter is first restored to its global value~5.
+Since this value is global, it will be retained, so the other saved values
+(|\day=3| and |\day=1|) are essentially ignored. Moral: If you find \TeX\
+retaining a lot of values, you have a set of macros that could cause
+the save stack to overflow in large enough jobs. To prevent this, it's
+usually wise to be consistent in your assignments to each variable that
+you use; the assignments should either be global always or local always.
+
+\ddanger \TeX\ provides several other kinds of tracing in addition to
+|\tracingstats| and |\tracingrestores|: We have already discussed
+|\tracingcommands| in Chapters 13 and~20, |\tracingparagraphs| in
+Chapter~14, |\tracingpages| in Chapter~15, and |\tracingmacros| in
+Chapter~20. There is also ^|\tracinglostchars|, which (if positive)
+causes \TeX\ to record each time a character has been dropped because
+it does not appear in the current font; and ^|\tracingoutput|, which
+(if positive) causes \TeX\ to display in symbolic form the contents
+of every box that is being shipped out to the ^|dvi|~file. ^^|\shipout|
+The latter allows you to see if things have been typeset properly, if
+you're trying to decide whether some anomaly was caused by \TeX\ or
+by some other software that acts on \TeX's output.
+
+\danger When \TeX\ displays a box as part of diagnostic output, the amount
+of data is controlled by two parameters called ^|\showboxbreadth| and
+^|\showboxdepth|. The first of these, which plain \TeX\ sets equal to~5,
+tells the maximum number of items shown per level; the second, which plain
+\TeX\ sets to~3, tells the deepest level. For example, a small box
+whose full contents are ^^{internal box format} ^^{symbolic box format}
+\begintt
+\hbox(4.30554+1.94444)x21.0, glue set 0.5
+.\hbox(4.30554+1.94444)x5.0
+..\tenrm g
+.\glue 5.0 plus 2.0
+.\tenrm || (ligature ---)
+\endtt
+will be abbreviated as follows when |\showboxbreadth=1| and |\showboxdepth=1|:
+^^{ligature} ^^{em-dash}
+\begintt
+\hbox(4.30554+1.94444)x21.0, glue set 0.5
+.\hbox(4.30554+1.94444)x5.0 []
+.etc.
+\endtt
+And if you set |\showboxdepth=0|, you get only the top level:
+\begintt
+\hbox(4.30554+1.94444)x21.0, glue set 0.5 []
+\endtt
+(Notice how `^|[]|' and `^|etc.|'~indicate that the data has been
+truncated.)
+
+\danger A nonempty hbox is considered ``^{overfull}'' if its ^{glue}
+cannot shrink to achieve the specified size, provided that ^|\hbadness| is
+less than~100 or that the excess width (after shrinking by the maximum
+amount) is more than ^|\hfuzz|. It is ``^{tight}'' if its glue shrinks and
+the ^{badness} exceeds |\hbadness|; it is ``^{loose}'' if its glue
+stretches and the badness exceeds |\hbadness| but is not greater than~100;
+it is ``^{underfull}'' if its glue stretches and the badness is greater
+than |\hbadness| and greater than~100. Similar remarks apply to nonempty
+vboxes. \TeX\ prints a warning message and displays the offending box,
+whenever such anomalies are discovered. Empty boxes are never considered
+to be anomalous.
+
+\ddanger When an ^{alignment} is ``overfull'' or ``tight'' or ``loose'' or
+``underfull,'' you don't get a warning message for every aligned line;
+you get only one message, and \TeX\ displays a {\sl^{prototype row}\/}
+(or, with ^|\valign|, a {\sl prototype column\/}).
+For example, suppose you say `|\tabskip=0pt plus10pt
+\halign to200pt{&#\hfil\cr...\cr}|', ^^|\halign| and suppose that the aligned
+material turns out to make two columns of widths $50\pt$ and $60\pt$,
+respectively. Then you get the following message:
+\begintt
+Underfull \hbox (badness 2698) in alignment at lines 11--18
+ [] []
+\hbox(0.0+0.0)x200.0, glue set 3.0
+.\glue(\tabskip) 0.0 plus 10.0
+.\unsetbox(0.0+0.0)x50.0
+.\glue(\tabskip) 0.0 plus 10.0
+.\unsetbox(0.0+0.0)x60.0
+.\glue(\tabskip) 0.0 plus 10.0
+\endtt
+The ``unset boxes'' in a prototype row show the individual column widths.
+In this case the ^{tabskip glue} has to stretch 3.0 times its stretchability,
+in order to reach the $200\pt$ goal, so the box is underfull. \ (According
+to the formula in Chapter~14, the badness of this situation is~2700; \TeX\
+actually uses a similar but more efficient formula, so it computes a
+badness of~2698.) \ Every line of the alignment will be underfull, but
+only the prototype row will be displayed in a warning message.
+``^{Overfull rules}'' are never appended to the lines of overfull alignments.
+
+\ddanger The |\tracing...|\ commands put all of their output into your log
+file, unless the ^|\tracingonline| parameter is positive; in the latter
+case, all diagnostic information goes to the terminal as well as to the
+log file. Plain \TeX\ has a ^|\tracingall| macro that turns on the
+maximum amount of tracing of all kinds. It not only sets~up
+|\tracingcommands|, |\tracingrestores|, |\tracingparagraphs|, and so on,
+it also sets |\tracingonline=1|, and it sets ^|\showboxbreadth| and
+^|\showboxdepth| to extremely high values, so that the entire contents of
+all boxes will be displayed.
+
+\ddanger Some production versions of \TeX\ have been streamlined for
+speed. These implementations don't look at the values of the parameters
+|\tracingparagraphs|, |\tracingpages|, |\tracingstats|, and
+|\tracingrestores|, because \TeX\ runs faster when it doesn't have
+to maintain statistics or keep tabs on whether tracing is required.
+If you want all of \TeX's diagnostic tools, you should be sure to
+use the right version.
+
+\ddanger If you set ^|\pausing||=1|, \TeX\ will give you a chance to edit
+each line of input as it is read from the file. In this way you can
+make temporary patches (e.g., you can insert |\show...|\ commands)
+while you're troubleshooting, without changing the actual contents
+of the file, and you can keep \TeX\ running at human speed.
+
+Final hint: When working on a long manuscript, it's best to prepare
+only a few pages at a time. Set up a ``^{galley}'' file and a ``^{book}''
+file, and enter your text on the galley file. \ (Put control
+information that sets up your basic format at the beginning of this
+file; an example of |galley.tex| appears in Appendix~E\null.) \
+After the galleys come out looking right, you can append them to the
+book file; then you can run the book file through \TeX\ occasionally,
+in order to see how the pages really fit together. For example,
+when the author prepared this manual, he did one chapter at a time,
+and the longer chapters were split into subchapters.
+
+\ddangerexercise Final exercise: Find all of the ^{lies} in this
+manual, and all of the ^{jokes}.
+\answer If this exercise isn't just a joke, the title of this
+appendix is a lie.
+
+\line{Final exhortation: G{\sc O} {\sc FORTH} now and create
+{\sl masterpieces of the publishing art!\/}}
+
+\endchapter
+
+Who can understand his errors?
+\author ^^{Biblical}{\sl Psalm 19\thinspace:\thinspace12\/} (c.~1000 B.C.)
+
+\bigskip
+
+It is one thing, to shew a Man that he is in an Error,
+and another, to put him in possession of Truth.
+\author JOHN ^{LOCKE}, {\sl An Essay Concerning Humane Understanding\/} (1690)
+ % bk 4 ch 7 sec 11
+
+\eject
+ \beginchapter Appendix A. Answers to\\All the\\Exercises
+
+The preface to this manual points out the wisdom of trying to figure out
+each exercise before you look up the answer here. But these answers are intended
+to be read, since they occasionally provide additional information that
+you are best equipped to understand when you have just worked on a problem.
+
+\immediate\closeout\ans % this makes the answers file ready
+\ninepoint
+\input answers
+
+\endchapter
+
+If you can't solve a problem,
+you can always look up the answer.
+But please, try first to solve it by yourself;
+then you'll learn more and you'll learn faster.
+\author DONALD E. ^{KNUTH}, {\sl The \TeX book\/} (1984)
+
+\bigskip
+
+How answer you for your selues?
+\author WILLIAM ^{SHAKESPEARE}, {\sl Much Adoe About Nothing\/} (1598)
+ % Act IV, Scene 2, line 25
+
+\eject
+ \beginchapter Appendix B. Basic\\Control\\Sequences
+
+Let's begin this appendix with a chart that summarizes plain \TeX's
+^^{summary of plain TeX} ^^|\+| ^^{tabbing} ^^{accents}
+conventions.
+
+\def\sep{\medskip\hrule width\hsize\medskip}
+\medskip\smallskip
+\hrule height .61803pt
+\kern 1pt
+\hrule
+\medskip
+\line{\strut Characters that are reserved for special purposes:\hfil
+ |\|\hfil|{|\hfil|}|\hfil|$|\hfil|&|\hfil|#|\hfil|%|\hfil|^|\hfil|_|\hfil|~|}
+\sep
+\halign to\hsize{\strut\hfil#\hfil\tabskip\z@ plus10pt&
+\hfil#\hfil&\hfil#\hfil&
+\hfil#\hfil\tabskip\z@skip\cr
+|\rm roman,|&|{\sl slanted},|&|{\bf boldface},|&|{\it italic\/} type|\cr
+roman,&{\sl slanted},&{\bf boldface},&{\it italic\/} type\cr}
+\sep
+\halign to\hsize{\strut\hfil#\hfil\tabskip\z@ plus10pt&
+\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&
+\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&
+\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&
+\hfil#\hfil&\hfil#\hfil&
+\hfil#\hfil\tabskip\z@skip\cr
+|``|&|''|&|--|&|---|&|?||`|&|!||`|&|\$|&|\#|&|\&|&|\%|&
+ |\ae|&|\AE|&|\oe|&|\OE|&|\aa|&|\AA|&|\ss|&|\o|&|\O|\cr
+``&''&--&---&?`&!`&\$&\#&\&&\%&\ae&\AE&\oe&\OE&\aa&\AA&\ss&\o&\O\cr}
+\sep
+\halign to\hsize{\strut\hfil#\hfil\tabskip\z@ plus10pt&
+\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&
+\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&
+\hfil#\hfil&\hfil#\hfil&
+\hfil#\hfil\tabskip\z@skip\cr
+|\`a|&|\'e|&|\^o|&|\"u|&|\=y|&|\~n|&|\.p|&|\u\i|&
+ |\v s|&|\H\j|&|\t\i u|&|\b k|&|\c c|&|\d h|\cr
+\`a&\'e&\^o&\"u&\=y&\~n&\.p&\u\i&\v s&\H\j&\t\i u&\b k&\c c&\d h\cr}
+\sep
+\halign to\hsize{\strut\hfil#\hfil\tabskip\z@ plus10pt&
+\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&
+\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&\hfil#\hfil&
+\hfil#\hfil\tabskip\z@skip\cr
+|\l|&|\L|&|\dag|&|\ddag|&|\S|&|\P|&|{\it\$|&
+ |\&}|&|\copyright|&|\TeX|&|\dots|\cr
+\l&\L&\dag&\ddag&\S&\P&\phantom{\tt////}\it\$&
+ \it\&\phantom{\tt/}&\copyright&\TeX&\dots\cr}
+\sep
+\line{\strut Line break controls:\hfil
+ |\break|\hfil|\nobreak|\hfil|\allowbreak|\hfil|\hbox{unbreakable}|}
+\line{\strut|dis\-cre\-tion\-ary hy\-phens|\hfil|virgule\slash breakpoint|}
+\sep
+\settabs 2\columns
+\+\strut Breakable horizontal spaces:& Unbreakable horizontal spaces:\cr
+\+|\|\] \ normal interword space& |~| \ normal interword space\cr
+\+|\enskip| \ this\enskip much& |\enspace | \ this\enspace much\cr
+\+|\quad | \ this\quad much& |\thinspace | \ this\thinspace much\cr
+\+|\qquad | \ this\qquad much& |\negthinspace| \ this\negthinspace much\cr
+\+\strut|\hskip| \<arbitrary dimen>& |\kern| \<arbitrary dimen>\cr
+\sep
+\smallskip
+\+\strut Vertical spaces:\hfill
+ |\smallskip| $\vcenter{\hrule width2em\smallskip\hrule}$\hfill&
+ |\medskip| $\vcenter{\hrule width3em\medskip\hrule}$\hfill
+ |\bigskip| $\vcenter{\hrule width4em\bigskip\hrule}$&\cr
+\smallskip
+\sep
+\line{\strut Page break controls:\hfil|\eject|\hfil|\supereject|\hfil
+ |\nobreak|\hfil|\goodbreak|\hfil|\filbreak|}
+\+\strut Vertical spaces and good breakpoints:&
+ |\smallbreak|\hfill|\medbreak|\hfill|\bigbreak|&\cr
+\sep
+\settabs 4 \columns
+\hbox{\strut|\settabs 4 \columns|}
+\hbox{|\+Here's an example&of\hfill some &tabbing:&\hrulefill&\cr|}
+\+Here's an example&of\hfill some &tabbing:&\hrulefill&\cr
+\+\hfill|\hrulefill |&\hrulefill&\hfill|\dotfill |&\dotfill&\cr
+\+\hfill|\leftarrowfill |&\leftarrowfill&
+ \hfill|\rightarrowfill |&\rightarrowfill&\cr
+\+\hfill|\upbracefill |&\upbracefill&
+ \hfill|\downbracefill |&\downbracefill&\cr
+\smallskip
+\line{\strut More general alignments use |\halign|, |\valign|, |\omit|,
+ |\span|, and |\multispan|.}
+\sep
+\line{\strut Examples of the principal conventions for text layout
+ appear on the next page.}
+\sep\unskip
+\kern 1pt
+\hrule height .61803pt
+\eject
+\begingroup\parindent\z@ \obeylines \baselineskip12pt plus 1pt
+|% This test file generates the output shown on the opposite page.|
+|% It's a bit complex because it tries to illustrate lots of stuff.|
+|% TeX ignores commentary (like this) that follows a `%' sign.|
+| |
+|% First the standard output style is changed slightly:|
+^|\hsize||=29pc % The lines in this book are 29 picas wide.|
+^|\vsize||=42pc % The page body is 42 picas (not counting footlines).|
+^|\footline||={\tenrm Footline\quad\dotfill\quad Page \folio}|
+^|\pageno||=1009 % This is the starting page number (don't ask why).|
+|% See Chapter 23 for the way to make other page format changes via|
+|% \hoffset, \voffset, \nopagenumbers, \headline, or \raggedbottom.|
+| |
+^|\topglue|| 1in % This makes an inch of blank space (1in=2.54cm).|
+^|\centerline||{\bf A Bold, Centered Title}|
+^|\smallskip|| % This puts a little extra space after the title line.|
+^|\rightline||{\it avec un sous-titre \`a la fran\c caise}|
+|% Now we use \beginsection to introduce part 1 of the document.|
+^|\beginsection|| 1. Plain \TeX nology % The next line must be blank!|
+| |^^{blank line}
+|The first paragraph of a new section is not indented.|
+^|\TeX||\ recognizes the end of a paragraph when it comes to a blank|
+|line in your manuscript file. % or to a `\par': See below.|^^|\par|
+| |^^|\footnote|^^|\tt|^^|\char|
+|Subsequent paragraphs {\it are\/} indented.\footnote*{The amount|
+| of indentation can be changed by changing a parameter called|
+|{\tt\char`\\parindent}. Turn the page for a summary of \TeX's most|
+|important parameters.} (See?) The computer breaks a paragraph's|
+|text into lines in an interesting way---see reference~[1]---and h%|
+| yphenates words automatically when necessary.|^^{percent}
+| |
+^|\midinsert|| % This begins inserted material, e.g., a figure.|
+|\narrower\narrower % This brings the margins in (see Chapter 14).|
+^|\noindent|| |^|\llap||{``}If there hadn't been room for this material on|
+|the present page, it would have been inserted on the next one.''|
+|\endinsert % This ends the insertion and the effect of \narrower.|
+| |
+^|\proclaim|| Theorem T. The typesetting of $math$ is discussed in|
+|Chapters 16--19, and math symbols are summarized in Appendix~F.|
+| |
+|\beginsection 2. Bibliography\par % `\par' acts like a blank line.|
+^|\frenchspacing|| % (Chapter 12 recommends this for bibliographies.)|
+^|\item||{[1]} D.~E. Knuth and M.~F. Plass, ``Breaking paragraphs|^^{Plass}
+|into lines,'' {\sl Softw. pract. exp. \bf11} (1981), 1119--1184.|^^{Knuth}
+^|\bye|| % This is the way the file ends, not with a \bang but a \bye.|
+\eject\endgroup
+{\parindent 20pt
+\topglue 1in % This makes an inch of blank space (1in=2.54cm).
+\centerline{\bf A Bold, Centered Title}
+\smallskip % This puts a little extra space between lines here.
+\rightline{\it avec un sous-titre \`a la fran\c caise}
+% Now we use \beginsection to introduce part 1 of the document.
+\beginsection 1. Plain \TeX nology % The next line must be blank!
+
+The first paragraph of a new section is not indented.
+\TeX\ recognizes the end of a paragraph when it comes to a blank
+line in your manuscript file. % or to a `\par': See below.
+
+Subsequent paragraphs {\it are\/} indented.*{\spacefactor=3000}
+(See?) The computer breaks a paragraph's
+text into lines in an interesting way---see reference~[1]---and h%
+ yphenates words automatically when necessary.
+
+\midinsert % This begins inserted material, e.g., a figure.
+\narrower\narrower % This brings the margins in (see Chapter 14).
+\noindent \llap{``}If there hadn't been room for this material on
+the present page, it would have been inserted on the next one.''
+\endinsert % This ends the insertion and the effect of \narrower.
+
+\proclaim Theorem T. The typesetting of $math$ is discussed in
+Chapters 16--19, and math symbols are summarized in Appendix~F.
+
+\beginsection 2. Bibliography\par% `\par' is just like blank line.
+\frenchspacing % (Chapter 12 recommends this for bibliographies.)
+\item{[1]} D.~E. Knuth and M.~F. Plass, ``Breaking paragraphs
+into lines,'' {\sl Softw. pract. exp. \bf11} (1981), 1119--1184.
+\vfill
+\hrule width 2in \kern 2.6pt
+\textindent{*}\strut
+The amount of indentation can be changed by changing a parameter called
+{\tt\char`\\parindent}. Turn the page for a summary of \TeX's most
+important parameters.\par
+\baselineskip 24pt
+\line{Footline\quad\dotfill\quad Page 1009}
+\eject}
+
+The preceding example illustrates most of the basic things
+that you can do directly with plain \TeX, but it does not provide an
+exhaustive list. Thus, it uses |\centerline| and |\rightline|,
+but not |\leftline| or |\line|; it uses |\midinsert|, but not
+|\topinsert| or |\pageinsert|; it uses |\smallskip|, but not
+|\medskip| or |\bigskip|; it uses |\llap| but not |\rlap|, |\item|
+but not |\itemitem|, |\topglue| but not |\hglue|. It does not
+illustrate |\raggedright| setting of paragraphs; it does not use
+^|\obeylines| or ^|\obeyspaces| to shut off \TeX's automatic
+formatting. ^^{as is, see obeylines, obeyspaces}
+All such control sequences are explained later in this appendix, and
+further information can be found by looking them up in the index.
+The main purpose of the example is to serve as a reminder of the
+repertoire of possibilities.
+
+Most of the control sequences used in the example are defined by
+macros of plain \TeX\ format, but three of them are ^{primitive}, i.e.,
+built~in: `|\par|' (end of paragraph), `|\noindent|' (beginning of
+non-indented paragraph), and `|\/|' (italic correction). The example
+also assigns values to two of \TeX's primitive parameters, namely
+|\hsize| and |\vsize|. \TeX\ has scores of parameters, all of which
+are listed in Chapter~24, but only a few of them are of special
+concern to the majority of \TeX\ users. Here are examples of how
+you might want to give new values to the most important ^{parameters}
+other than |\hsize| and |\vsize|:
+\par\begingroup\nobreak\medskip\parindent\z@ \obeylines
+^|\tolerance||=500|\quad(\TeX\ will tolerate lines whose badness is %
+ rated 500 or less.)
+^|\looseness||=1|\quad(The next paragraph will be one line longer than %
+ usual.)
+^|\parindent||=4mm|\quad(Paragraphs will be indented by four millimeters.)
+^|\hoffset||=1.5in|\quad(All output will be shifted right by one and a %
+ half inches.)
+^|\voffset||=24pt|\quad(All output will be shifted down by 24 points.)
+^|\baselineskip||=11pt plus.1pt|\quad(Baselines will be $11\pt$ apart, %
+ or a bit more.)
+^|\parskip||=3pt plus1pt minus.5pt|\quad(Extra space will %
+ precede each paragraph.)
+\endgroup\medbreak\noindent
+Plain \TeX\ uses |\parindent| also to control the amount of
+indentation provided by ^|\item|, ^|\itemitem|, and ^|\narrower|.
+
+\danger The remainder of this appendix is devoted to the details of the
+plain \TeX\ format, which is a set of macros that come with normal
+implementations of \TeX. These macros serve three basic purposes:
+\ (1)~They make \TeX\ usable, because \TeX's primitive capabilities
+operate at a very low level. A~``virgin'' \TeX\ system that has no
+macros is like a newborn baby that has an immense amount to learn about
+the real world; but it is capable of learning fast. \ (2)~The plain
+\TeX\ macros provide a basis for more elaborate and powerful formats
+tailored to individual tastes and applications. You can do a lot with
+plain \TeX, but pretty soon you'll want to do even more. \ (3)~The macros
+also serve to illustrate how additional formats can be designed.
+
+\ninepoint
+Somewhere in your computer system you should be able to find a file
+called ^|plain.tex| that contains exactly what has been preloaded into
+the running \TeX\ system that you use. Our purpose in the rest of this
+appendix will be to discuss the contents of |plain.tex|. However, we
+will not include a verbatim description, because some parts of that file
+are too boring, and because the actual macros have been ``^{optimized}''
+^^{efficiency}
+with respect to memory space and running time. Unoptimized versions of the
+macros are easier for humans to understand, so we shall deal with those;
+|plain.tex| contains equivalent constructions that work better on a machine.
+
+So here's the plan for the rest of Appendix~B\null: We shall go through
+the contents of |plain.tex|, interspersing an edited transcription of
+that file with comments about noteworthy details. When we come to
+macros whose usage has not yet been explained---for example, somehow
+|\vglue| and |\beginsection| never made it into Chapters 1 through~27---we
+shall consider them from a user's viewpoint; but most of the time we shall
+be addressing the issues from the standpoint of a macro designer.
+
+\subsection The code tables. A format's first duty is to establish
+^|\catcode| values. This is necessary because, for example, a |\def|
+command can't be used until there are characters like |{| and~|}| of
+categories 1 and~2. The ^|INITEX| program (which reads |plain.tex|
+so that \TeX\ can be initialized) begins without knowing any grouping
+characters; hence |plain.tex| starts out as follows:
+\beginlines
+|% This is the plain TeX format that's described in The TeXbook.|
+|% N.B.: A version number is defined at the very end of this file;|
+|% please change that number whenever the file is modified!|
+|% And don't modify the file under any circumstances.|
+\smallbreak
+|\catcode`\{=1 % left brace is begin-group character|
+|\catcode`\}=2 % right brace is end-group character|
+|\catcode`\$=3 % dollar sign is math shift|
+|\catcode`\&=4 % ampersand is alignment tab|
+|\catcode`\#=6 % hash mark is macro parameter character|
+|\catcode`\^=7 \catcode`\^^K=7 % circumflex and uparrow for superscripts|
+|\catcode`\_=8 \catcode`\^^A=8 % underline and downarrow for subscripts|
+|\catcode`\^^I=10 % ASCII tab is treated as a blank space|
+|\chardef\active=13 \catcode`\~=\active % tilde is active|
+|\catcode`\^^L=\active \outer\def^^L{\par} % ASCII form-feed is \outer\par|
+\smallskip
+|\message{Preloading the plain format: codes,}|
+\endlines
+These instructions set up the nonstandard characters
+|^^K| and |^^A| for ^{superscripts} and ^{subscripts}, in addition to~|^|
+and~|_|, so that people with extended character sets can use
+{\tentex\char'13} and {\tentex\char1} as recommended in Appendix~C\null.
+^^{uparrow char} ^^{downarrow char}
+Furthermore |^^I| (^{ASCII} ^\<tab>) is given category~10 (space);
+and |^^L| (ASCII
+\<formfeed>) becomes an active character that will detect runaways on
+files that have been divided into ``^{file pages}'' by ^\<formfeed>
+characters. The control sequence ^|\active| is defined to yield the
+constant~13; this is the one category code that seems to deserve a
+symbolic name, in view of its frequent use in constructing
+special-purpose macros.
+
+When |INITEX| begins, category 12 (other) has been assigned to all~256
+possible characters, except that the 52~letters |A...Z| and |a...z| are
+category~11 (letter), and a few other assignments equivalent to the
+following have been made:
+\begintt
+\catcode `\\ =0 \catcode`\ =10 \catcode `\% =14
+\catcode`\^^@=9 \catcode`\^^M=5 \catcode`\^^?=15
+\endtt
+Thus `|\|' is already an escape character, ^^{backslash}
+`\]' is a space, ^^\<space>
+and `|%|' is available for comments on the first line of the file;
+ASCII ^\<null> is ignored, ASCII ^\<return> is an end-of-line character,
+and ASCII ^\<delete> is invalid.
+
+The ^|\message| command shown above prints a progress report on the terminal
+when |plain.tex| is being input by |INITEX|. Later on comes
+`|\message{registers,}|' and several other messages, but we won't mention
+them specifically. The terminal will eventually display something like
+this when initialization is complete:
+\begintt
+** plain
+(plain.tex Preloading the plain format: codes, registers,
+parameters, fonts, more fonts, macros, math definitions,
+output routines, hyphenation (hyphen.tex))
+* \dump
+Beginning to dump on file plain.fmt
+\endtt
+followed by a variety of statistics about what fonts were loaded, etc.
+If you want to make a new format |super.tex| that adds more features
+to |plain.tex|, it's best not to make a new file containing all the
+plain stuff, or even to |\input plain|; just type `|&plain super|' in
+response to |INITEX|'s ^|**| prompt, ^^{ampersand} ^^|fmt|
+to input |plain.fmt| at high speed.
+
+After the opening |\message|, |plain.tex| goes on to define a control
+sequence ^|\dospecials| that lists all the characters whose catcodes should
+probably be changed to~12 (other) when copying things verbatim:
+\beginlines
+|\def\dospecials{\do\ \do\\\do\{\do\}\do\$\do\&%|
+| \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~}|
+\endlines
+(Appendix E illustrates how to use |\dospecials|.) \ The ASCII codes for
+\<null>, \<tab>, \<linefeed>, \<formfeed>, \<return>, and \<delete>
+have not been included in the list.
+
+At this point |plain.tex| completes its initialization of category codes
+by setting |\catcode`\@=11|, thereby making the character `|@|' behave
+temporarily like a letter. The command |\catcode`\@=12| will appear later,
+hence ^{at-sign characters} will act just like ordinary punctuation
+marks when \TeX\ is running. The idea is to make it easy for plain \TeX\
+to have ^{private control sequences} that cannot be redefined by ordinary
+users; all such control sequences will have at least one `|@|' in
+their names.
+
+The next job is to set up the ^|\mathcode| table:
+\beginlines
+|\mathcode`\^^@="2201 \mathcode`\^^A="3223 \mathcode`\^^B="010B|
+|\mathcode`\^^C="010C \mathcode`\^^D="225E \mathcode`\^^E="023A|
+|\mathcode`\^^F="3232 \mathcode`\^^G="0119 \mathcode`\^^H="0115|
+|\mathcode`\^^I="010D \mathcode`\^^J="010E \mathcode`\^^K="3222|
+|\mathcode`\^^L="2206 \mathcode`\^^M="2208 \mathcode`\^^N="0231|
+|\mathcode`\^^O="0140 \mathcode`\^^P="321A \mathcode`\^^Q="321B|
+|\mathcode`\^^R="225C \mathcode`\^^S="225B \mathcode`\^^T="0238|
+|\mathcode`\^^U="0239 \mathcode`\^^V="220A \mathcode`\^^W="3224|
+|\mathcode`\^^X="3220 \mathcode`\^^Y="3221 \mathcode`\^^Z="8000|
+|\mathcode`\^^[="2205 \mathcode`\^^\="3214 \mathcode`\^^]="3215|
+|\mathcode`\^^^="3211 \mathcode`\^^_="225F \mathcode`\^^?="1273|
+|\mathcode`\ ="8000 \mathcode`\!="5021 \mathcode`\'="8000|
+|\mathcode`\(="4028 \mathcode`\)="5029 \mathcode`\*="2203|
+|\mathcode`\+="202B \mathcode`\,="613B \mathcode`\-="2200|
+|\mathcode`\.="013A \mathcode`\/="013D \mathcode`\:="303A|
+|\mathcode`\;="603B \mathcode`\<="313C \mathcode`\=="303D|
+|\mathcode`\>="313E \mathcode`\?="503F \mathcode`\[="405B|
+|\mathcode`\\="026E \mathcode`\]="505D \mathcode`\_="8000|
+|\mathcode`\{="4266 \mathcode`\|\||="026A \mathcode`\}="5267|
+\endlines
+A mathcode is relevant only when the corresponding category code is
+11 or~12; therefore many of these codes will rarely be looked at. For
+example, the math code for |^^M| specifies the character |\oplus|,
+but it's hard to imagine a user who would want |^^M| (ASCII ^\<return>)
+to produce an~$\oplus$ sign in the middle of a math formula, since plain
+\TeX\ appends |^^M| to the end of every line of input. The math codes
+have been set up here, however, to be entirely consistent with the
+extended character set presented in Appendix~C and the Computer Modern
+fonts described in Appendix~F\null. |INITEX| has done the rest of the
+work, as far as mathcodes are concerned: It has set |\mathcode|$\,x=
+x+\hex{7000}$ for each of the ten digits $x={}$|`0| to |`9|;
+|\mathcode|$\,x=x+\hex{7100}$ for each of the 52~letters; and
+|\mathcode|$\,x=x$ for all other values of~$x$.
+
+There's no need to change the ^|\uccode| and ^|\lccode| tables. |INITEX|
+has made |\uccode`X=`X|, |\uccode`x=`X|, |\lccode`X=`x|, |\lccode`x=`x|,
+and it has made similar assignments for all other letters. The codes are
+zero for all nonletters. These tables are used by \TeX's ^|\uppercase|
+and |\lowercase| operations, and the hyphenation algorithm also looks
+at |\lccode| (see Appendix~H\null). Changes should be made only in format
+packages that set \TeX\ up for languages with more than~26 letters
+(see Chapter~8).
+
+Next comes the ^|\sfcode| table, which |INITEX| has initialized entirely
+to 1000, except that |\sfcode`X=999| for each of the 26 uppercase letters.
+Some characters are made ``transparent'' by setting
+\beginlines
+|\sfcode`\)=0 \sfcode`\'=0 \sfcode`\]=0 % won't change the space factor|
+\endlines
+and the |\nonfrenchspacing| macro will be used later to change the sfcodes
+of special punctuation marks. \ (Chapter~12 explains what an |\sfcode| does.)
+
+The last code table is called ^|\delcode|, and again it's necessary to
+change only a few values. |INITEX| has made all delimiter codes equal to
+$-1$, which means that no characters are recognized as delimiters in
+formulas. But there's an exception: The value |\delcode`\.=0| has been
+prespecified, so that `|.|'\ ^^{period} stands for a ``^{null delimiter}.''
+\ (See Chapter~17.) \ Plain format sets up the following nine values,
+based on the delimiters available in Computer Modern:
+\beginlines
+|\delcode`\(="028300 \delcode`\/="02F30E \delcode`\)="029301|
+|\delcode`\[="05B302 \delcode`\|\||="26A30C \delcode`\]="05D303|
+|\delcode`\<="26830A \delcode`\\="26E30F \delcode`\>="26930B|
+\endlines
+It's important to note that |\delcode`\{| and |\delcode`\}| have been left
+equal to~$-1$. If those codes were set to certain values, a user would
+^^{left brace} ^^{right brace} be able to type, e.g.,
+`|\big{|' to get a big left brace; but it would be a big mistake.
+The reason is that ^{braces} are used for grouping, when supplying
+arguments to macros; all sorts of strange things can happen if you try
+to use them both as math delimiters and group delimiters.
+
+At this point the |plain.tex| file contains several definitions
+\beginlines
+|\chardef\@ne=1 \chardef\tw@=2 \chardef\thr@@=3 \chardef\sixt@@n=16|
+|\chardef\@cclv=255 \mathchardef\@cclvi=256|
+|\mathchardef\@m=1000 \mathchardef\@M=10000 \mathchardef\@MM=20000|
+\endlines
+which allow ``private'' control sequences ^|\@ne|, |\tw@|, etc.,
+to be used as abbreviations for commonly used constants 1,~2,~\dots; this
+convention makes \TeX\ run a little faster, ^^{optimization} ^^{efficiency}
+and it means that the macros will consume slightly less memory space.
+The usage of these abbreviations will not, however, be shown below
+unless necessary; we shall pretend that `|1|\]' appears instead of\/ |\@ne|,
+`|10000|\]' instead of\/ |\@M|, and so on, since that makes the programs
+more readable. \ (Notice that the long form of\/ |\@ne| is `|1|\]' including
+a space, because \TeX\ looks for and removes a space following a constant.)
+
+\subsection Allocation of registers. The second major part of the
+|plain.tex| file provides a foundation on which systems of independently
+developed macros can coexist peacefully without interfering in their
+usage of ^{registers}. The idea is that macro writers should abide by the
+^^{macro conventions}
+following ground rules: (1)~Registers numbered 0~to~9 are always free for
+temporary ``^{scratch}'' use, but their values are always assumed to be
+clobbered whenever any other macro might get into control. \ (This applies
+to registers like |\dimen0|, |\toks0|, |\skip1|, |\box3|, etc.; but \TeX\
+has already reserved |\count0| through |\count9| for page number
+identification.) \ (2)~The registers |\count255|, |\dimen255|, and
+|\skip255| are freely available in the same way.
+\ (3)~All assignments to the scratch registers whose numbers are
+1,~3, 5, 7, and~9 should be ^|\global|; all assignments to the
+other scratch registers (0,~2, 4, 6, 8,~255) should be non-|\global|.
+\ (This prevents the phenomenon of ``^{save stack buildup}'' discussed
+in Chapter~27.)
+\ (4)~Furthermore, it's possible to
+use any register in a group, if you ensure that \TeX's grouping
+mechanism will restore the register when you're done with the group, and
+if you are certain that other macros will not make global assignments
+to that register when you need it. \ (5)~But when a register is used
+by several macros, or over long spans of time, it should be allocated
+by |\newcount|, |\newdimen|, |\newbox|, etc. \ (6)~Similar remarks
+apply to ^{input/output streams} used by ^|\read| and ^|\write|, to math
+^{families} used by ^|\fam|, to sets of hyphenation rules used by
+^|\language|, and to insertions (which require
+^|\box|, ^|\count|, ^|\dimen|, and ^|\skip| registers all having the
+same number).
+
+Some handy abbreviations are introduced at this point so that the macros
+below will have easy access to scratch registers:
+\beginlines
+|\countdef\count@=255 \toksdef\toks@=0 \skipdef\skip@=0|
+|\dimendef\dimen@=0 \dimendef\dimen@i=1 \dimendef\dimen@ii=2|
+\endlines
+
+Here now are the macros that provide allocation for quantities of more
+permanent value. These macros use registers
+|\count10| through |\count20| to hold the numbers that were allocated
+most recently; for example, if\/ |\newdimen| has just reserved |\dimen15|,
+the value of\/ |\count11| will be~15. However, the rest of the world
+is not supposed to ``know'' that |\count11| has anything to do with
+|\dimen| registers. There's a special counter called ^|\allocationnumber|
+that will be equal to the most recently allocated number, after every
+|\newcount|, |\newdimen|, \dots, |\newinsert| operation; macro packages
+are supposed to refer to |\allocationnumber| if they want to find out what
+number was allocated. The inside story of how allocation is actually performed
+should be irrelevant when the allocation macros are used at a higher level;
+you mustn't assume that |plain.tex| really does allocation in any
+particular way.
+\beginlines
+|\count10=22 % this counter allocates \count registers 23, 24, 25, ...|
+|\count11=9 % this counter allocates \dimen registers 10, 11, 12, ...|
+|\count12=9 % this counter allocates \skip registers 10, 11, 12, ...|
+|\count13=9 % this counter allocates \muskip registers 10, 11, 12, ...|
+|\count14=9 % this counter allocates \box registers 10, 11, 12, ...|
+|\count15=9 % this counter allocates \toks registers 10, 11, 12, ...|
+|\count16=-1 % this counter allocates input streams 0, 1, 2, ...|
+|\count17=-1 % this counter allocates output streams 0, 1, 2, ...|
+|\count18=3 % this counter allocates math families 4, 5, 6, ...|
+|\count19=0 % this counter allocates language codes 1, 2, 3, ...|
+|\count20=255 % this counter allocates insertions 254, 253, 252, ...|
+|\countdef\insc@unt=20 % nickname for the insertion counter|
+|\countdef\allocationnumber=21 % the most recent allocation|
+|\countdef|^|\m@ne||=22 \m@ne=-1 % a handy constant|
+|\def|^|\wlog||{\immediate\write-1} % this will write on log file (only)|
+\smallbreak
+|\outer\def|^|\newcount||{\alloc@0\count\countdef\insc@unt}|
+|\outer\def|^|\newdimen||{\alloc@1\dimen\dimendef\insc@unt}|
+|\outer\def|^|\newskip||{\alloc@2\skip\skipdef\insc@unt}|
+|\outer\def|^|\newmuskip||{\alloc@3\muskip\muskipdef\@cclvi}|
+|\outer\def|^|\newbox||{\alloc@4\box\chardef\insc@unt}|
+|\let\newtoks=\relax % this allows plain.tex to be read in twice|
+|\outer\def\newhelp#1#2{\newtoks#1#1=\expandafter{\csname#2\endcsname}}|
+|\outer\def|^|\newtoks||{\alloc@5\toks\toksdef\@cclvi}|
+|\outer\def|^|\newread||{\alloc@6\read\chardef\sixt@@n}|
+|\outer\def|^|\newwrite||{\alloc@7\write\chardef\sixt@@n}|
+|\outer\def|^|\newfam||{\alloc@8\fam\chardef\sixt@@n}|
+|\outer\def|^|\newlanguage||{\alloc@9\language\chardef\@cclvi}|
+\smallbreak
+|\def\alloc@#1#2#3#4#5{\global\advance\count1#1 by 1|\parbreak%
+| \ch@ck#1#4#2% make sure there's still room|\parbreak%
+| \allocationnumber=\count1#1 \global#3#5=\allocationnumber|\parbreak%
+| \wlog{\string#5=\string#2\the\allocationnumber}}|
+\smallbreak
+|\outer\def|^|\newinsert||#1{\global\advance\insc@unt by-1|\parbreak%
+| \ch@ck0\insc@unt\count \ch@ck1\insc@unt\dimen|\parbreak%
+| \ch@ck2\insc@unt\skip \ch@ck4\insc@unt\box|\parbreak%
+| \allocationnumber=\insc@unt|\parbreak%
+| \global\chardef#1=\allocationnumber|\parbreak%
+| \wlog{\string#1=\string\insert\the\allocationnumber}}|
+\smallbreak
+|\def\ch@ck#1#2#3{\ifnum\count1#1<#2%|\parbreak%
+| \else\errmessage{No room for a new #3}\fi}|^^|No room|
+\endlines
+The `|\alloc@|' macro does most of the work of allocation. It puts
+a message like `|\maxdimen=\dimen10|' into the log file after
+|\newdimen| has allocated a place for the |\dimen| register that
+will be called |\maxdimen|; such information might be useful when
+difficult macros are being ^{debugged}.
+
+A ^|\newhelp| macro has been provided to aid in creating home-made help
+texts: You can say, e.g., |\newhelp\helpout{This is a help message.}|, and
+then give the command `^|\errhelp||=\helpout|' just before issuing an
+^|\errmessage|. This method of creating help texts makes efficient
+use of \TeX's ^{memory}, because it puts the text into a control sequence
+name where it doesn't take up space that is needed for tokens.
+
+The |plain| file now goes ahead and allocates registers for important constants:
+\beginlines
+|\newdimen\maxdimen \maxdimen=16383.99999pt|
+|\newskip\hideskip \hideskip=-1000pt plus1fill|
+|\newskip\centering \centering=0pt plus 1000pt minus 1000pt|
+|\newdimen\p@ \p@=1pt % this saves macro space and time|
+|\newdimen\z@ \z@=0pt| % likewise
+|\newskip\z@skip \z@skip=0pt plus0pt minus0pt|
+|\newbox\voidb@x % permanently void box register|
+\endlines
+The control sequence ^|\maxdimen| stands for the largest permissible
+\<dimen>. Alignment macros that appear below will make use of special glue
+values called ^|\hideskip| and ^|\centering|. {\sl N.B.: These three constants
+must not be changed under any circumstances\/}; you should either ignore
+them completely or just use them and enjoy them.
+In fact, the next four constant registers (^|\p@|, ^|\z@|, ^|\z@skip|,
+and ^|\voidb@x|) have been given private names so that they are untouchable.
+The control sequence |\p@| is used several dozen times as an abbreviation
+for `|pt |', and |\z@| is used quite often to stand for either `|0pt |' or
+`|0 |'; the use of such abbreviations saves almost 10\% of the space
+needed to store the tokens in plain \TeX's macros. But we shall stick to
+^^{optimization}
+the unabbreviated forms below, so that the macros are more readable.
+
+A different sort of allocation comes next:
+^^|\string|^^|\escapechar|^^|\uppercase|^^|\iftrue|^^|\iffalse|
+\beginlines
+|\outer\def\newif#1{\count@=\escapechar \escapechar=-1|\parbreak%
+| |^|\expandafter||\expandafter\expandafter|\parbreak%
+| \def\@if#1{true}{\let#1=\iftrue}%|\parbreak%
+| \expandafter\expandafter\expandafter|\parbreak%
+| \def\@if#1{false}{\let#1=\iffalse}%|\parbreak%
+| \@if#1{false}\escapechar=\count@} % the condition starts out false|\parbreak%
+|\def\@if#1#2{|^|\csname||\expandafter\if@\string#1#2\endcsname}|\parbreak%
+|{\uccode`1=`i \uccode`2=`f \uppercase{\gdef\if@12{}}} % `if' is required|
+\endlines
+For example, the command |\newif\ifalpha| creates a trio of control
+sequences called |\alphatrue|, |\alphafalse|, and |\ifalpha|
+(see Chapter~20).
+
+\subsection Parameters. |INITEX| sets almost all of the numeric registers
+and parameters equal to zero; it makes all of the token registers and parameters
+empty; and it makes all of the box registers void. But there are a few
+^^{parameters, default values} ^^{default values of parameters}
+exceptions: ^|\mag| is set initially to~1000, ^|\tolerance| to~10000,
+^|\maxdeadcycles| to~25, ^|\hangafter| to~1, ^|\escapechar| to~|`\\|, and
+^|\endlinechar| to~|`\^^M|. Plain \TeX\ assigns new parameter values as
+follows:
+\beginlines
+^|\pretolerance||=100 |^|\tolerance||=200 |^|\hbadness||=1000 |%
+ ^|\vbadness||=1000|
+^|\linepenalty||=10 |^|\hyphenpenalty||=50 |^|\exhyphenpenalty||=50|
+^|\binoppenalty||=700 |^|\relpenalty||=500|
+^|\clubpenalty||=150 |^|\widowpenalty||=150 |^|\displaywidowpenalty||=50|
+^|\brokenpenalty||=100 |^|\predisplaypenalty||=10000|
+^|\doublehyphendemerits||=10000 |^|\finalhyphendemerits||=5000 |%
+ ^|\adjdemerits||=10000|
+^|\tracinglostchars||=1 |^|\uchyph||=1 |^|\delimiterfactor||=901|
+^|\defaulthyphenchar||=`\- |^|\defaultskewchar||=-1 |%
+ ^|\newlinechar||=-1|
+^|\showboxbreadth||=5 |^|\showboxdepth||=3 |^|\errorcontextlines||=5|
+\smallbreak
+^|\hfuzz||=0.1pt |^|\vfuzz||=0.1pt |^|\overfullrule||=5pt|
+^|\hsize||=6.5in |^|\vsize||=8.9in |^|\parindent||=20pt|
+^|\maxdepth||=4pt |^|\splitmaxdepth||=\maxdimen |^|\boxmaxdepth||=\maxdimen|
+^|\delimitershortfall||=5pt |^|\nulldelimiterspace||=1.2pt |%
+ ^|\scriptspace||=0.5pt|
+\smallbreak
+^|\parskip||=0pt plus 1pt|
+^|\abovedisplayskip||=12pt plus 3pt minus 9pt|
+^|\abovedisplayshortskip||=0pt plus 3pt|
+^|\belowdisplayskip||=12pt plus 3pt minus 9pt|
+^|\belowdisplayshortskip||=7pt plus 3pt minus 4pt|
+^|\topskip||=10pt |^|\splittopskip||=10pt|
+^|\parfillskip||=0pt plus 1fil|
+\smallbreak
+^|\thinmuskip||=3mu|
+^|\medmuskip||=4mu plus 2mu minus 4mu|
+^|\thickmuskip||=5mu plus 5mu|
+\endlines
+
+(Some parameters are set by \TeX\ itself as it runs, so it is inappropriate
+to initialize them: ^|\time|, ^|\day|, ^|\month|, and ^|\year| are established
+at the beginning of a job; ^|\outputpenalty| is given a value when an
+|\output| routine is invoked; ^|\predisplaysize|, ^|\displaywidth|,
+and ^|\displayindent| get values just before a display is processed;
+and the values ^|\looseness||=0|, ^|\hangindent||=0pt|, ^|\hangafter||=1|,
+^|\parshape||=0| are assigned at the end of a paragraph and when \TeX\ enters
+internal vertical mode.)
+
+The parameters ^|\baselineskip|, ^|\lineskip|, and ^|\lineskiplimit| have not
+been initialized here, but a macro called ^|\normalbaselines| is defined below;
+this macro sets |\baselineskip=\normalbaselineskip|,
+|\lineskip=\normallineskip|, and |\lineskiplimit=\normallineskiplimit|.
+An indirect approach like this has been used so that several different
+type sizes may be handled, as illustrated in Appendix~E\null. Plain \TeX\
+deals exclusively with $10\pt$ type, but it supports extension to other styles.
+
+Some ``^{pseudo parameters}'' come next. These quantities behave just like
+internal parameters of \TeX, and users are free to change them in the same
+way, but they are part of the plain \TeX\ format
+rather than primitives of the language. ^^{parameters, pseudo}
+\beginlines
+|\newskip|^|\smallskipamount|| % the amount of a \smallskip|
+| \smallskipamount=3pt plus1pt minus1pt|
+|\newskip|^|\medskipamount|| % the amount of a \medskip|
+| \medskipamount=6pt plus2pt minus2pt|
+|\newskip|^|\bigskipamount|| % the amount of a \bigskip|
+| \bigskipamount=12pt plus4pt minus4pt|
+|\newskip|^|\normalbaselineskip|| % normal value of \baselineskip|
+| \normalbaselineskip=12pt|
+|\newskip|^|\normallineskip|| % normal value of \lineskip|
+| \normallineskip=1pt|
+|\newdimen|^|\normallineskiplimit|| % normal value of \lineskiplimit|
+| \normallineskiplimit=0pt|
+|\newdimen|^|\jot|| % unit of measure for opening up displays|
+| \jot=3pt|
+|\newcount|^|\interdisplaylinepenalty|| % interline penalty in \displaylines|
+| \interdisplaylinepenalty=100|
+|\newcount|^|\interfootnotelinepenalty|| % interline penalty in footnotes|
+| \interfootnotelinepenalty=100|
+\endlines
+
+\subsection Font information. Now |plain.tex| brings in the data that \TeX\
+needs to know about how to typeset lots of characters in lots of different
+fonts. First the ^|\magstep| macros are defined, to support font scaling:
+\beginlines
+|\def|^|\magstephalf||{1095 }|
+|\def\magstep#1{\ifcase#1 1000\or|
+| 1200\or 1440\or 1728\or 2074\or 2488\fi\relax}|
+\weakendlines
+(Incidentally, |\magstep| doesn't use ^|\multiply| to compute values,
+since it is supposed to expand to a ^\<number> en route to \TeX's
+``stomach''; ^^{anatomy} |\multiply| wouldn't work, because
+it is an assignment command, performed only in the stomach.)
+
+One of the main things that distinguishes one format from another
+is the fact that each format gives \TeX\ the necessary knowledge about
+a certain family of typefaces. In this case the ^{Computer Modern} types
+described in Appendix~F are taken as a basis, although there is a provision
+for incorporating other styles.
+\beginlines
+|\font|^|\tenrm||=cmr10 \font\preloaded=cmr9 \font\preloaded=cmr8|
+\nobreak
+|\font\sevenrm=cmr7 \font\preloaded=cmr6 \font\fiverm=cmr5|
+\smallskip
+|\font\teni=cmmi10 \font\preloaded=cmmi9 \font\preloaded=cmmi8|
+\nobreak
+|\font\seveni=cmmi7 \font\preloaded=cmmi6 \font\fivei=cmmi5|
+\smallbreak
+|\font\tensy=cmsy10 \font\preloaded=cmsy9 \font\preloaded=cmsy8|
+\nobreak
+|\font\sevensy=cmsy7 \font\preloaded=cmsy6 \font\fivesy=cmsy5|
+\nobreak\smallskip
+|\font\tenex=cmex10|
+\smallbreak
+|\font\tenbf=cmbx10 \font\preloaded=cmbx9 \font\preloaded=cmbx8|
+\nobreak
+|\font\sevenbf=cmbx7 \font\preloaded=cmbx6 \font\fivebf=cmbx5|
+\smallskip
+|\font\tensl=cmsl10 \font\preloaded=cmsl9 \font\preloaded=cmsl8|
+\nobreak
+|\font\tentt=cmtt10 \font\preloaded=cmtt9 \font\preloaded=cmtt8|
+\nobreak
+|\font\tenit=cmti10 \font\preloaded=cmti9 \font\preloaded=cmti8|
+\nobreak
+|\font\preloaded=cmss10 \font\preloaded=cmssq8|
+\nobreak
+|\font\preloaded=cmssi10 \font\preloaded=cmssqi8|
+\smallskip
+|\font\preloaded=cmr7 scaled \magstep4 % for titles|
+\nobreak
+|\font\preloaded=cmtt10 scaled \magstep2|
+\nobreak
+|\font\preloaded=cmssbx10 scaled \magstep2|
+\smallskip
+|% Additional \preloaded fonts can be specified here.|
+|% (And those that were \preloaded above can be eliminated.)|
+|\let\preloaded=\undefined % preloaded fonts must be declared anew later.|
+\endlines
+Notice that most of the fonts have been called ^|\preloaded|; but the
+control sequence |\preloaded| is made undefined at the very end, so those
+fonts cannot be used directly. There are two reasons for this strange
+approach: First, it is desirable to keep the total number of fonts
+of plain \TeX\ relatively small, because plain \TeX\ is a sort of
+standard format; it shouldn't cost much for someone to acquire all the
+fonts of plain \TeX\ in addition to those he really wants. Second, it
+is desirable on many computer systems to preload the information for
+most of the fonts that people will actually be using, since this saves
+a lot of machine time. The |\preloaded| font
+information goes into \TeX's memory, where it will come alive instantly
+if the user defines the corresponding |\font| again. For example, the
+book format in Appendix~E says `|\font\ninerm=cmr9|'; after that
+assignment has been obeyed, the control sequence |\ninerm| will
+identify the |cmr9| font, whose information does not have to be loaded again.
+
+The exact number and nature of fonts that are preloaded is unimportant;
+the only important thing needed for standardization between machines
+is that sixteen basic fonts (|cmr10|, |cmr7|, \dots,~|cmti10|) should
+actually be loaded. The |plain.tex| files used on different machines
+can be expected to differ widely with respect to preloaded fonts,
+since the choice of how many fonts to preload and the selection of the
+most important fonts depends on local conditions.
+For example, at the author's university it is useful to preload a
+font that contains the Stanford seal, but that particular font is not
+very popular at Berkeley.
+
+Most of these fonts have the default values of\/ ^|\hyphenchar| and
+^|\skewchar|, namely |`-| and |-1|; but the math italic and math symbol fonts
+have special |\skewchar| values, which are defined next:
+\beginlines
+|\skewchar\teni='177 \skewchar\seveni='177 \skewchar\fivei='177|
+|\skewchar\tensy='60 \skewchar\sevensy='60 \skewchar\fivesy='60|
+\endlines
+
+Once the fonts are loaded, they are also grouped into families for
+use in math setting, and shorthand names like ^|\rm| and~^|\it| are defined:
+\beginlines
+|\textfont0=\tenrm \scriptfont0=\sevenrm |^|\scriptscriptfont||0=\fiverm|
+\nobreak
+|\def\rm{\fam0 \tenrm}|
+^|\textfont||1=\teni |^|\scriptfont||1=\seveni \scriptscriptfont1=\fivei|
+\nobreak
+|\def|^|\mit||{\fam1 } \def|^|\oldstyle||{\fam1 \teni}|
+|\textfont2=\tensy \scriptfont2=\sevensy \scriptscriptfont2=\fivesy|
+\nobreak
+|\def|^|\cal||{\fam2 }|
+|\textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex|
+\smallbreak
+|\newfam\itfam \def\it{\fam\itfam\tenit} \textfont\itfam=\tenit|
+|\newfam\slfam \def|^|\sl||{\fam\slfam\tensl} \textfont\slfam=\tensl|
+|\newfam\bffam \def|^|\bf||{\fam\bffam\tenbf} \textfont\bffam=\tenbf|
+\nobreak
+| \scriptfont\bffam=\sevenbf \scriptscriptfont\bffam=\fivebf|
+^|\newfam||\ttfam \def|^|\tt||{\fam\ttfam\tentt} \textfont\ttfam=\tentt|
+\endlines
+
+\subsection Macros for text. The fifth part of |plain.tex| introduces
+macros that do basic formatting unrelated to mathematics. First come
+some macros that were promised above:
+\beginlines
+|\def|^|\frenchspacing||{\sfcode`\.=1000 \sfcode`\?=1000 \sfcode`\!=1000|
+\nobreak
+| \sfcode`\:=1000 \sfcode`\;=1000 |^|\sfcode||`\,=1000 }|
+|\def|^|\nonfrenchspacing||{\sfcode`\.=3000 \sfcode`\?=3000 \sfcode`\!=3000|
+\nobreak
+| \sfcode`\:=2000 \sfcode`\;=1500 \sfcode`\,=1250 }|
+|\def|^|\normalbaselines||{\lineskip=\normallineskip|
+\nobreak
+| \baselineskip=\normalbaselineskip \lineskiplimit=\normallineskiplimit}|
+\endlines
+
+The next macros are simple but vital. ^^{control tab}^^{control return}
+^^{control space} First |\|\<tab> and |\|\<return> are defined so that they
+expand to |\|\<space>; this helps to prevent confusion, since all three
+cases look identical when displayed on most computer terminals.
+Then the macros ^|\lq|, ^|\rq|, ^|\lbrack|, and ^|\rbrack| are defined,
+for people who have difficulty typing quotation marks and/or brackets. The
+control sequences ^|\endgraf|
+and ^|\endline| are made equivalent to \TeX's primitive ^|\par| and~^|\cr|
+operations, since it is often useful to redefine the meanings of\/ |\par|
+and~|\cr| themselves. Then come the definitions of\/ ^|\space| (a blank space),
+^|\empty| (a~list of no tokens), and ^|\null| (an~empty hbox). Finally,
+^|\bgroup| and ^|\egroup| are made to provide ``implicit'' grouping characters
+that turn out to be especially useful in macro definitions. \ (See Chapters
+24--26 and Appendix~D for information about ^{implicit characters}.)
+\beginlines
+|\def\^^I{\ } \def\^^M{\ }|
+|\def\lq{`} \def\rq{'} \def\lbrack{[} \def\rbrack{]}|
+|\let\endgraf=\par \let\endline=\cr|
+|\def\space{ } \def\empty{} \def\null{\hbox{}}|
+|\let\bgroup={ \let\egroup=}|
+\endlines
+
+Something a bit tricky comes up now in the definitions of\/ ^|\obeyspaces|
+and ^|\obeylines|, since \TeX\ is only ``half obedient'' while these
+definitions are half finished:
+\beginlines
+|\def\obeyspaces{\catcode`\ =\active}|
+|{\obeyspaces\global\let =\space}|
+|{\catcode`\^^M=\active % these lines must end with `%'|
+\nobreak
+| \gdef\obeylines{\catcode`\^^M=\active \let^^M=\par}%|
+\nobreak
+| \global\let^^M=\par} % this is in case ^^M appears in a \write|
+\endlines
+The |\obeylines| macro says `|\let^^M=\par|' instead of `|\def^^M{\par}|'
+because the |\let| technique allows constructions such as
+`|\let\par=\cr| |\obeylines| ^|\halign||{...}|' in which ^|\cr|'s need
+not be given within the alignment.
+
+The ^|\loop||...|^|\repeat| macro provides for iterative operations
+as illustrated at the end of Chapter~20. In this macro and several others,
+the control sequence `^|\next|' is given a temporary value that is not
+going to be needed later; thus, |\next| acts like a ``^{scratch control
+sequence}.''
+\beginlines
+|\def\loop#1\repeat{\def\body{#1}\iterate}|
+|\def\iterate{\body \let\next=\iterate \else\let\next=\relax\fi \next}|
+|\let\repeat=\fi % this makes \loop...\if...\repeat skippable|
+\endlines
+
+Spacing is the next concern. The macros ^|\enskip|, ^|\quad|, and ^|\qquad|
+provide spaces that are legitimate breakpoints within a paragraph;
+^|\enspace|, ^|\thinspace|, and ^|\negthinspace| produce space that cannot
+cause a break (although the space will disappear if it occurs just
+next to certain kinds of breaks). All six of these spaces are relative
+to the current font. You can get horizontal space that
+never disappears by saying `^|\hglue|\<glue>'; this space is able to
+stretch or shrink. Similarly, there's a vertical analog,
+`^|\vglue|\<glue>'.
+The ^|\nointerlineskip| macro suppresses interline glue that would
+ordinarily be inserted before the next box in vertical mode; this is a
+``one shot'' macro, but ^|\offinterlineskip| is more drastic---it sets
+things up so that future ^{interline glue} will be present, but zero.
+There also are macros for potentially breakable vertical spaces:
+^|\smallskip|, ^|\medskip|, and ^|\bigskip|.^^|\topglue|
+\beginlines
+|\def\enskip{\hskip.5em\relax} \def\enspace{\kern.5em }|
+|\def\quad{\hskip1em\relax} \def\qquad{\hskip2em\relax}|
+|\def\thinspace{\kern .16667em } \def\negthinspace{\kern-.16667em }|
+\smallbreak
+|\def\hglue{|^|\afterassignment||\hgl@\skip@=}|
+|\def\hgl@{\leavevmode \count@=\spacefactor \vrule width0pt|
+| \nobreak\hskip\skip@ \spacefactor=\count@}|
+|\def\vglue{\afterassignment\vgl@\skip@=}|
+|\def\vgl@{\par \dimen@=\prevdepth \hrule height0pt|
+| \nobreak\vskip\skip@ \prevdepth=\dimen@}|
+|\def\topglue{\nointerlineskip \vglue-\topskip \vglue} % for top of page|
+\smallbreak
+|\def\nointerlineskip{\prevdepth=-1000pt }|
+|\def\offinterlineskip{\baselineskip=-1000pt|
+| \lineskip=0pt \lineskiplimit=\maxdimen}|
+\smallbreak
+|\def\smallskip{\vskip\smallskipamount}|
+|\def\medskip{\vskip\medskipamount}|
+|\def\bigskip{\vskip\bigskipamount}|
+\endlines
+
+Speaking of breakpoints, the following macros introduce ^{penalty} markers
+that make breaking less, or more, desirable. The ^|\break|, ^|\nobreak|,
+and ^|\allowbreak| macros are intended for use in any mode; the |~|~(tie)
+and ^|\slash| (hyphen-like~`/') macros are intended for horizontal mode.
+The others are intended only for vertical mode, i.e., between paragraphs,
+so they begin with |\par|.
+\beginlines
+|\def\break{\penalty-10000 } \def\nobreak{\penalty10000 }|
+|\def\allowbreak{\penalty0 }|
+|\def~{\penalty10000\ }|
+|\def\slash{/\penalty\exhyphenpenalty}|
+\smallbreak
+|\def|^|\filbreak||{\par\vfil\penalty-200\vfilneg}|
+|\def|^|\goodbreak||{\par\penalty-500 }|
+|\def|^|\eject||{\par\penalty-10000 }|
+|\def|^|\supereject||{\par\penalty-20000 }|
+\smallbreak
+|\def|^|\removelastskip||{\ifdim\lastskip=0pt \else\vskip-\lastskip\fi}|
+|\def|^|\smallbreak||{\par \ifdim\lastskip<\smallskipamount|\parbreak%
+| \removelastskip \penalty-50 \smallskip \fi}|
+|\def|^|\medbreak||{\par \ifdim\lastskip<\medskipamount|\parbreak%
+| \removelastskip \penalty-100 \medskip \fi}|
+|\def|^|\bigbreak||{\par \ifdim\lastskip<\bigskipamount|\parbreak%
+| \removelastskip \penalty-200 \bigskip \fi}|
+\endlines
+
+Boxes are next: ^|\line|, ^|\leftline|, ^|\rightline|, and ^|\centerline|
+produce boxes of the full line width, while ^|\llap| and ^|\rlap| make
+boxes whose effective width is zero. The ^|\underbar| macro puts its
+argument into an hbox with a straight line at a fixed distance under it.
+\beginlines
+|\def\line{\hbox to\hsize}|
+|\def\leftline#1{\line{#1\hss}} \def\rightline#1{\line{\hss#1}}|
+|\def\centerline#1{\line{\hss#1\hss}}|
+\smallskip
+|\def\llap#1{\hbox to 0pt{\hss#1}} \def\rlap#1{\hbox to 0pt{#1\hss}}|
+\smallskip
+|\def\m@th{\mathsurround=0pt }|
+|\def\underbar#1{$\setbox0=\hbox{#1} \dp0=0pt|\parbreak%
+| \m@th \underline{\box0}$}|
+\endlines
+(Notice that |\underbar| uses math mode to do its job, although the
+operation is essentially non-mathematical in nature. A few of the other
+macros below use math mode in similar ways; thus, \TeX's mathematical
+abilities prove to be useful even when no mathematical typesetting
+is actually being done. A special control sequence ^|\m@th| is
+used to ``turn off'' ^|\mathsurround| when such constructions are
+being performed.)
+
+\smallbreak
+A ^|\strut| is implemented here as a rule of width zero, since this
+takes minimum space and time in applications where numerous struts are
+present.
+\beginlines
+|\newbox\strutbox|
+|\setbox\strutbox=\hbox{\vrule height8.5pt depth3.5pt width0pt}|
+|\def\strut{\relax\ifmmode\copy\strutbox\else\unhcopy\strutbox\fi}|
+\weakendlines
+The `^|\relax|' in this macro and in others below is necessary in case
+|\strut| appears first in an alignment entry, because \TeX\ is in a
+somewhat unpredictable mode at such times (see Chapter~22).
+
+The ^|\ialign| macro provides for ^{alignments} when it is necessary to
+be sure that ^|\tabskip| is initially zero. The ^|\hidewidth| macro can be
+used essentially as |\hfill| in alignment entries that are permitted to
+``stick out'' of their column. There's also ^|\multispan|, which permits
+alignment entries to span one or more columns.
+\beginlines
+|\def\ialign{\everycr={}\tabskip=0pt \halign} % initialized \halign|
+|\def\hidewidth{\hskip\hideskip}|
+\smallbreak
+|\newcount\mscount|
+|\def\multispan#1{\omit\mscount=#1\relax\loop\ifnum\mscount>1 \sp@n\repeat}|%
+\kern-10pt\null
+|\def\sp@n{\span\omit \advance\mscount by -1 }|
+\endlines
+
+Now we get to the ``^{tabbing}'' macros, which are more complicated than
+anything else in plain \TeX. They keep track of the tab positions by
+maintaining boxes full of empty boxes having the specified widths.
+\ (The best way to understand these macros is probably to watch
+them in action on simple examples, using |\tracingall|.)
+\beginlines
+|\newif\ifus@ \newif\if@cr|
+|\newbox\tabs \newbox\tabsyet \newbox\tabsdone|
+\smallbreak
+|\def|^|\cleartabs||{\global\setbox\tabsyet=\null \setbox\tabs=\null}|
+|\def|^|\settabs||{\setbox\tabs=\null \futurelet\next\sett@b}|
+|\let\+=\relax % in case this file is being read in twice|
+|\def\sett@b{\ifx\next\+\def\nxt{\afterassignment\s@tt@b\let\nxt}%|\parbreak%
+| \else\let\nxt=\s@tcols\fi|\parbreak%
+| \let\next=\relax \nxt} % turn off \outerness|
+|\def\s@tt@b{\let\nxt=\relax \us@false\m@ketabbox}|
+|\outer\def|^|\+||{\tabalign} \def\tabalign{\us@true \m@ketabbox}|
+|\def\s@tcols#1\columns{\count@=#1 \dimen@=\hsize|\parbreak%
+| \loop \ifnum\count@>0 \@nother \repeat}|
+|\def\@nother{\dimen@ii=\dimen@ \divide\dimen@ii by\count@|\parbreak%
+| \setbox\tabs=\hbox{\hbox to\dimen@ii{}\unhbox\tabs}%|\parbreak%
+| \advance\dimen@ by-\dimen@ii \advance\count@ by -1 }|
+\smallbreak
+|\def\m@ketabbox{\begingroup|\parbreak%
+| \global\setbox\tabsyet=\copy\tabs \global\setbox\tabsdone=\null|\parbreak%
+| \def\cr{\@crtrue\crcr\egroup\egroup|\parbreak%
+| \ifus@ \unvbox0 |^|\lastbox||\fi \endgroup|\parbreak%
+| \setbox\tabs=\hbox{\unhbox\tabsyet|^|\unhbox||\tabsdone}}%|\parbreak%
+| \setbox0=\vbox\bgroup\@crfalse \ialign\bgroup&\t@bbox##\t@bb@x\crcr}|
+\smallbreak
+|\def\t@bbox{\setbox0=\hbox\bgroup}|
+|\def\t@bb@x{\if@cr\egroup % now \box0 holds the column|\parbreak%
+| \else\hss\egroup \global\setbox\tabsyet=\hbox{\unhbox\tabsyet|\parbreak%
+| \global\setbox1=\lastbox}% now \box1 holds its size|\parbreak%
+| \ifvoid1 \global\setbox1=\hbox to\wd0{}%|\parbreak%
+| \else\setbox0=\hbox to\wd1{\unhbox0}\fi|\parbreak%
+| \global\setbox\tabsdone=\hbox{\box1\unhbox\tabsdone}\fi|\parbreak%
+| \box0}|
+\endlines
+The macro |\+| has been declared `^|\outer|' here, so that \TeX\ will
+be better able to detect runaway arguments and definitions (see Chapter~20).
+A non-|\outer| version, called ^|\tabalign|, has also been provided
+in case it is necessary to use |\+| in some ``inner'' place.
+You can use |\tabalign| just like |\+|, except after ^|\settabs|.
+
+\textindent{$\bullet$} Paragraph shapes of a limited but important kind
+are provided by ^|\item|, ^|\itemitem|, and ^|\narrower|. There are also
+two macros that haven't been mentioned before: \ (1)~^|\hang| causes
+hanging indentation by the normal amount of\/ ^|\parindent|, after the
+first line; thus, the entire paragraph will be indented by the same
+amount (unless it began with ^|\noindent|). \ (2)~^|\textindent||{stuff}|
+is like ^|\indent|, but it puts the `|stuff|' into the indentation, flush
+right except for an en~space; it also removes spaces that might follow
+the right brace in `|{stuff}|'. For example, the present paragraph
+was typeset by the commands `|\textindent{$\bullet$} Paragraph shapes
+...|'; the opening `P' occurs at the normal position for a paragraph's
+first letter. ^^|\bullet| ^^|\ignorespaces|
+\beginlines
+|\def\hang{\hangindent\parindent}|
+|\def\item{\par\hang\textindent}|
+|\def\itemitem{\par\indent \hangindent2\parindent \textindent}|
+|\def\textindent#1{\indent\llap{#1\enspace}\ignorespaces}|
+|\def\narrower{\advance\leftskip by\parindent|\parbreak%
+| \advance\rightskip by\parindent}|
+\endlines
+
+The ^|\beginsection| macro is intended to mark the beginning of a
+new major subdivision in a document; to use it, you say
+`|\beginsection|\<section title>' followed by a blank line (or~|\par|).
+The macro first emits glue and penalties, designed to start a new page
+if the present page is nearly full; then it makes a ^|\bigskip| and
+puts the section title flush left on a line by itself, in boldface type.
+The section title is also displayed on the terminal.
+After a ^|\smallskip|, with page break prohibited, a ^|\noindent|
+command is given; this suppresses indentation in the next paragraph,
+i.e., in the first paragraph of the new section.
+\ (However, the next ``paragraph'' will be empty if vertical mode
+material immediately follows the |\beginsection| command.) ^^|\message|
+\beginlines
+|\outer\def\beginsection#1\par{\vskip0pt plus.3\vsize\penalty-250|\parbreak%
+| \vskip0pt plus-.3\vsize\bigskip\vskip\parskip|\parbreak%
+| \message{#1}\leftline{\bf#1}\nobreak\smallskip\noindent}|
+\endlines
+
+Special statements in a mathematical paper are often called ^{theorems},
+lemmas, definitions, axioms, postulates, remarks, corollaries, algorithms,
+facts, conjectures, or some such things, and they generally are
+given special typographic treatment. The ^|\proclaim| macro, which
+was illustrated earlier in this appendix and also in Chapter~20,
+puts the title of the proclamation in boldface, then sets the rest
+of the paragraph in slanted type. The paragraph is followed by
+something similar to ^|\medbreak|, except that the amount of penalty
+is different so that page breaks are discouraged:
+\beginlines
+|\outer\def\proclaim #1. #2\par{\medbreak|\parbreak%
+| \noindent{\bf#1.\enspace}{\sl#2\par}%|\parbreak%
+| \ifdim\lastskip<\medskipamount \removelastskip\penalty55\medskip\fi}|
+\endlines
+
+^{Ragged-right setting} is initiated by restricting the spaces between
+words to have a fixed width, and by putting variable space at the right
+of each line. You should not call ^|\raggedright| until your text font
+has already been specified; it is assumed that the ragged-right material
+will not be in a variety of different sizes. \ (If this assumption is
+not valid, a different approach should be used: ^|\fontdimen| parameters
+3 and~4 of the fonts you will be using should be set to zero, by saying,
+e.g., `|\fontdimen3\tenrm=0pt|'. These parameters specify the stretchability
+and shrinkability of ^{interword spaces}.) \ ^^|\spaceskip| ^^|xspaceskip|
+A special macro ^|\ttraggedright| should be used for ragged-right setting
+in ^{typewriter type}, since the spaces between words are generally
+bigger in that style. \ (Spaces are already unstretchable and
+unshrinkable in font~|cmtt|.)
+\beginlines
+|\def\raggedright{\rightskip=0pt plus2em|\parbreak%
+| \spaceskip=.3333em \xspaceskip=.5em\relax}|
+|\def\ttraggedright{\tt\rightskip=0pt plus2em\relax}|
+\endlines
+
+Now we come to special symbols and accents, which depend primarily on
+the characters available in the Computer Modern fonts. Different
+constructions will be necessary if other styles of type are used.
+When a symbol is built up by forming a box, the ^|\leavevmode| macro
+is called first; this starts a new paragraph, if \TeX\ is in vertical
+mode, but does nothing if \TeX\ is in horizontal mode or math mode.
+\beginlines
+|\chardef\%=`\% \chardef\&=`\& \chardef\#=`\# \chardef\$=`\$|%
+ ^^{control percent}^^{control ampersand}^^{control hash}^^{control dollar}
+|\chardef|^|\ae||="1A \chardef|^|\oe||="1B \chardef|^|\o||="1C| %
+ |\chardef|^|\ss||="19|
+|\chardef|^|\AE||="1D \chardef|^|\OE||="1E \chardef|^|\O||="1F|
+|\chardef|^|\i||="10 \chardef|^|\j||="11 % dotless letters|
+|\def|^|\aa||{\accent'27a} \def|^|\l||{\char'40l}|
+\smallbreak
+|\def\leavevmode{\unhbox\voidb@x} % begins a paragraph, if necessary|
+|\def\_{\leavevmode \kern.06em \vbox{\hrule width0.3em}}|^^{control underline}
+|\def|^|\L||{\leavevmode\setbox0=\hbox{L}\hbox to\wd0{\hss\char'40L}}|
+|\def|^|\AA||{\leavevmode\setbox0=\hbox{!}\dimen@=\ht0 |%
+ |\advance\dimen@ by-1ex|\parbreak%
+| \rlap{\raise.67\dimen@\hbox{\char'27}}A}|
+\smallbreak
+|\def|^|\mathhexbox||#1#2#3{\leavevmode \hbox{$\m@th \mathchar"#1#2#3$}}|
+|\def|^|\dag||{\mathhexbox279} \def|^|\ddag||{\mathhexbox27A}|
+|\def|^|\S||{\mathhexbox278} \def|^|\P||{\mathhexbox27B} |%
+ |\def|^|\Orb||{\mathhexbox20D}|
+\smallbreak
+|\def|^|\oalign||#1{\leavevmode\vtop{\baselineskip0pt \lineskip.25ex|\parbreak%
+| \ialign{##\crcr#1\crcr}}} \def\o@lign{\lineskiplimit=0pt \oalign}|
+|\def|^|\ooalign||{\lineskiplimit=-\maxdimen \oalign} % chars over each other|
+|{\catcode`p=12 \catcode`t=12 \gdef\\#1pt{#1}} \let|^|\getf@ctor||=\\|
+|\def\sh@ft#1{\dimen@=#1 \kern\expandafter\getf@ctor\the\fontdimen1\font|\parbreak%
+| \dimen@} % kern by #1 times the current slant|
+|\def|^|\d||#1{{\o@lign{\relax#1\crcr\hidewidth\sh@ft{-1ex}.\hidewidth}}}|
+|\def|^|\b||#1{{\o@lign{\relax#1\crcr\hidewidth\sh@ft{-3ex}%|\parbreak%
+| \vbox to.2ex{\hbox{\char'26}\vss}\hidewidth}}}|
+|\def|^|\c||#1{{\setbox0=\hbox{#1}\ifdim\ht0=1ex \accent'30 #1%|\parbreak%
+| \else\ooalign{\unhbox0\crcr\hidewidth\char'30\hidewidth}\fi}}|
+|\def|^|\copyright||{{\ooalign{\hfil\raise.07ex\hbox{c}\hfil\crcr\Orb}}}|
+\smallbreak
+|\def|^|\dots||{\relax\ifmmode\ldots\else$\m@th \ldots\,$\fi}|
+|\def|^|\TeX||{T\kern-.1667em \lower.5ex\hbox{E}\kern-.125em X}|
+\smallbreak
+|\def|^|\`||#1{{\accent"12 #1}} \def|^|\'||#1{{\accent"13 #1}}|
+|\def|^|\v||#1{{\accent"14 #1}} \def|^|\u||#1{{\accent"15 #1}}|
+^^{esc hat}|\def|^|\=||#1{{\accent"16 #1}} \def\^#1{{\accent"5E #1}}|
+|\def|^|\.||#1{{\accent"5F #1}} \def|^|\H||#1{{\accent"7D #1}}|
+^^{esc tilde}|\def\~||#1{{\accent"7E #1}} \def|^|\"||#1{{\accent"7F #1}}|
+|\def|^|\t||#1{{\edef\next{\the\font}\the\textfont1\accent"7F\next#1}}|
+\endlines
+
+At this point three alternative control-symbol ^{accents} are defined,
+suitable for keyboards with extended character sets (cf.~Appendix~C):
+\beginlines
+|\let\^^_=\v \let\^^S=\u \let\^^D=\^|
+\endlines
+
+Various ways to fill space with ^{leaders} are provided next.
+\beginlines
+|\def|^|\hrulefill||{\leaders\hrule\hfill}|
+|\def|^|\dotfill||{\cleaders\hbox{$\m@th \mkern1.5mu . \mkern1.5mu$}\hfill}|
+|\def|^|\rightarrowfill||{$\m@th \smash- \mkern-7mu|\parbreak%
+| \cleaders\hbox{$\mkern-2mu \smash- \mkern-2mu$}\hfill|\parbreak%
+| \mkern-7mu \mathord\rightarrow$}|
+|\def|^|\leftarrowfill|%
+ |{$\m@th \mathord\leftarrow \mkern-7mu|\parbreak%
+| \cleaders\hbox{$\mkern-2mu \smash- \mkern-2mu$}\hfill|\parbreak%
+| \mkern-7mu \smash-$}|
+\smallbreak
+|\mathchardef\braceld="37A \mathchardef\bracerd="37B|
+|\mathchardef\bracelu="37C \mathchardef\braceru="37D|
+|\def|^|\upbracefill||{$\m@th|\parbreak%
+| \setbox0=\hbox{$\braceld$}%|\parbreak%
+| \bracelu\leaders\vrule height\ht0 depth0pt\hfill\bracerd|\parbreak%
+| \braceld\leaders\vrule height\ht0 depth0pt\hfill\braceru$}|
+|\def|^|\downbracefill||{$\m@th|\parbreak%
+| \setbox0=\hbox{$\braceld$}%|\parbreak%
+| \braceld\leaders\vrule height\ht0 depth0pt\hfill\braceru|\parbreak%
+| \bracelu\leaders\vrule height\ht0 depth0pt\hfill\bracerd$}|
+\endlines
+
+\smallbreak
+Finally, the fifth section of |plain.tex| closes by defining ^|\bye|:
+\beginlines
+^|\outer||\def\bye{\par\vfill\supereject\end} % the recommended way to stop|
+\endlines
+
+\subsection Macros for math. The sixth section of |plain.tex| is the
+longest; but it will suffice to give only excerpts here, because most
+of it is simply a tedious listing of special symbols together with their
+font locations, and the same information appears in Appendix~F.
+
+Some rudimentary things come first: The control sequences ^|\sp| and ^|\sb|
+are provided for people who can't easily type |^| and |_|; there are
+four control symbols that provide spacing corrections; a ``^{discretionary
+times sign}'' ^|\*| is defined; and then there's an interesting set of
+macros that convert |f'''| into |f^{\prime\prime\prime}|:
+\beginlines
+|\let\sp=^ \let\sb=_ {\catcode`\_=\active \global\let_=\_}|\parbreak%
+|\def|^|\,||{\mskip\thinmuskip} \def|^|\!||{\mskip-\thinmuskip}|\parbreak%
+|\def|^|\>||{\mskip\medmuskip} \def|^|\;||{\mskip\thickmuskip}|
+|\def\*{\discretionary{\thinspace\the\textfont2\char2}{}{}}|
+|{\catcode`\^^Z=\active \gdef^^Z{\not=}} % ^^Z is like \ne in math|
+\smallbreak
+|{\catcode`\'=\active \gdef'{^\bgroup\prim@s}}|\parbreak%
+|\def\prim@s{\prime\futurelet\next\pr@m@s}|\parbreak%
+|\def\pr@m@s{\ifx'\next\let\nxt\pr@@@s|%
+ | \else\ifx^\next\let\nxt\pr@@@t|\parbreak%
+| \else\let\nxt\egroup\fi\fi \nxt}|\parbreak%
+|\def\pr@@@s#1{\prim@s} \def\pr@@@t#1#2{#2\egroup}|
+\endlines
+
+The next job is to define ^{Greek letters} and other symbols of type ^{Ord}.
+Uppercase Greek letters are assigned hexadecimal codes of the form \hex{7xxx},
+so that they will change families when ^|\fam| changes.
+Three dots `$\,\cdots\,$' are used here and below to indicate that
+additional symbols, having similar definitions, are listed in Appendix~F.
+\beginlines
+|\mathchardef|^|\alpha||="010B|\qquad$\cdots$\qquad|\mathchardef\omega="0121|
+|\mathchardef|^|\Gamma||="7000|\qquad$\cdots$\qquad|\mathchardef\Omega="700A|
+|\mathchardef|^|\aleph||="0240|\qquad$\cdots$\qquad%
+ |\mathchardef\spadesuit="027F|
+|\def|^|\hbar||{{\mathchar'26\mkern-9muh}}|
+|\def|^|\surd||{{\mathchar"1270}}|
+|\def|^|\angle||{{\vbox{\ialign{$\m@th\scriptstyle##$\crcr|\parbreak%
+| \not\mathrel{\mkern14mu}\crcr \noalign{\nointerlineskip}|\parbreak%
+| \mkern2.5mu\leaders\hrule height.34pt\hfill\mkern2.5mu\crcr}}}}|
+\endlines
+
+Large operators are assigned hexadecimal codes of the form \hex{1xxx}:
+\beginlines
+|\mathchardef\smallint="1273|
+|\mathchardef|^|\sum||="1350|\qquad$\cdots$\qquad|\mathchardef\biguplus="1355|
+|\mathchardef\intop="1352 \def|^|\int||{\intop\nolimits}|
+|\mathchardef\ointop="1348 \def\oint{\ointop\nolimits}|
+\endlines
+Integral signs get special treatment so that their limits won't be set
+above and below.
+
+\smallskip
+Binary operations are next; nothing exciting here.
+\beginlines
+|\mathchardef\pm="2206|\qquad$\cdots$\qquad|\mathchardef\amalg="2271|
+\endlines
+
+Relations are also fairly straightforward, except for the ones that are
+constructed from other characters. The ^|\mapstochar| is a character
+`$\mapstochar\mskip5mu$' of width zero that is quite useless by itself,
+but it combines with right arrows to make ^|\mapsto| `$\mapsto$' and
+^|\longmapsto| `$\longmapsto$'. Similarly, ^|\not| is a relation character of
+width zero that puts a slash over the character that follows. When two
+relations are adjacent in a math formula, \TeX\ puts no space between them.
+\beginlines
+|\mathchardef\leq="3214|\qquad$\cdots$\qquad|\mathchardef\perp="323F|
+|\def|^|\joinrel||{\mathrel{\mkern-3mu}}|
+|\def|^|\relbar||{\mathrel{\smash-}} \def|^|\Relbar||{\mathrel=}|
+|\def|^|\longrightarrow||{\relbar\joinrel\rightarrow}|\parbreak%
+|\def|^|\Longrightarrow||{\Relbar\joinrel\Rightarrow}|
+|\def|^|\longleftarrow||{\leftarrow\joinrel\relbar}|\parbreak%
+|\def|^|\Longleftarrow||{\Leftarrow\joinrel\Relbar}|
+|\def|^|\longleftrightarrow||{\leftarrow\joinrel\rightarrow}|\parbreak%
+|\def|^|\Longleftrightarrow||{\Leftarrow\joinrel\Rightarrow}|
+|\mathchardef\mapstochar="3237 \def\mapsto{\mapstochar\rightarrow}|\parbreak%
+|\def\longmapsto{\mapstochar\longrightarrow}|
+|\mathchardef\lhook="312C \def|^|\hookrightarrow||{\lhook\joinrel\rightarrow}|
+|\mathchardef\rhook="312D \def|^|\hookleftarrow||{\leftarrow\joinrel\rhook}|
+\smallbreak
+|\def|^|\neq||{\not=} \def|^|\models||{\mathrel|\||\joinrel=}|
+|\def|^|\bowtie||{\mathrel\triangleright\joinrel\mathrel\triangleleft}|
+\weakendlines
+
+After defining characters ^|\ldotp| and ^|\cdotp| that act as math
+punctuation, it is easy to define ^|\ldots| and ^|\cdots| macros that
+\vadjust{\penalty-50}% fairly good breakpoint (on July 27, 1983)
+give the proper spacing in most circumstances. Vertical and diagonal
+dots (^|\vdots| and ^|\ddots|) are also provided here:
+\beginlines
+|\mathchardef\ldotp="613A\mathchardef\cdotp="6201\mathchardef\colon="603A|
+|\def\ldots{\mathinner{\ldotp\ldotp\ldotp}}|
+|\def\cdots{\mathinner{\cdotp\cdotp\cdotp}}|
+|\def\vdots{\vbox{\baselineskip=4pt \lineskiplimit=0pt|
+| \kern6pt \hbox{.}\hbox{.}\hbox{.}}}|
+|\def\ddots{\mathinner{\mkern1mu\raise7pt\vbox{\kern7pt\hbox{.}}\mkern2mu|
+| \raise4pt\hbox{.}\mkern2mu\raise1pt\hbox{.}\mkern1mu}}|
+\endlines
+
+Most of the math accents are handled entirely by the ^|\mathaccent|
+primitive, but a few of the variable-width ones are constructed the
+hard way:
+\beginlines
+|\def\acute{\mathaccent"7013 }|\qquad$\cdots$\qquad%
+ |\def\ddot{\mathaccent"707F }|
+|\def|^|\widetilde||{\mathaccent"0365 } \def|^|\widehat||{\mathaccent"0362 }|
+|\def|^|\overrightarrow||#1{\vbox{\m@th\ialign{##\crcr|\parbreak%
+| \rightarrowfill\crcr\noalign{\kern-1pt\nointerlineskip}|\parbreak%
+| $\hfil\displaystyle{#1}\hfil$\crcr}}}|
+|\def|^|\overleftarrow||#1{\vbox{\m@th\ialign{##\crcr|\parbreak%
+| \leftarrowfill\crcr\noalign{\kern-1pt\nointerlineskip}|\parbreak%
+| $\hfil\displaystyle{#1}\hfil$\crcr}}}|
+|\def|^|\overbrace|%
+ |#1{\mathop{\vbox{\m@th\ialign{##\crcr\noalign{\kern3pt}|\parbreak%
+| \downbracefill\crcr\noalign{\kern3pt\nointerlineskip}|\parbreak%
+| $\hfil\displaystyle{#1}\hfil$\crcr}}}|^|\limits||}|
+|\def|^|\underbrace||#1{\mathop{\vtop{\m@th\ialign{##\crcr|\parbreak%
+| $\hfil\displaystyle{#1}\hfil$\crcr|%
+ |\noalign{\kern3pt\nointerlineskip}|\parbreak%
+| \upbracefill\crcr\noalign{\kern3pt}}}}\limits}|
+|\def|^|\skew||#1#2#3{{\muskip0=#1mu \mkern.5\muskip0|\parbreak%
+| #2{\mkern-.5\muskip0{#3}\mkern.5\muskip0}\mkern-.5\muskip0}{}}|
+\endlines
+
+Now we come to 24 delimiters that can change their size: ^^|\delimiter|
+\beginlines
+|\def\langle{\delimiter"426830A } \def\rangle{\delimiter"526930B }|
+|\def\lbrace{\delimiter"4266308 } \def\rbrace{\delimiter"5267309 }|
+|\def\lceil{\delimiter"4264306 } \def\rceil{\delimiter"5265307 }|
+|\def\lfloor{\delimiter"4262304 } \def\rfloor{\delimiter"5263305 }|
+|\def\lgroup{\delimiter"462833A } \def\rgroup{\delimiter"562933B }|
+|\def\lmoustache{\delimiter"437A340 } \def\rmoustache{\delimiter"537B341 }|%
+ \kern-2pt
+|\def\uparrow{\delimiter"3222378 } \def\Uparrow{\delimiter"322A37E }|
+|\def\downarrow{\delimiter"3223379 } \def\Downarrow{\delimiter"322B37F }|
+|\def\updownarrow{\delimiter"326C33F } \def\arrowvert{\delimiter"026A33C }|
+|\def\Updownarrow{\delimiter"326D377 } \def\Arrowvert{\delimiter"026B33D } |
+|\def\vert{\delimiter"026A30C } \def\Vert{\delimiter"026B30D } |
+|\def\backslash{\delimiter"026E30F } \def\bracevert{\delimiter"077C33E }|
+\endlines
+
+The `^|\big||...|^|\Bigg|' macros produce specific sizes: ^^|\Big|^^|\bigg|
+\beginlines
+|\def\bigl{\mathopen\big} \def\bigm{\mathrel\big} \def\bigr{\mathclose\big}|
+|\def\Bigl{\mathopen\Big} \def\Bigm{\mathrel\Big} \def\Bigr{\mathclose\Big}|
+|\def\biggl{\mathopen\bigg} \def\Biggl{\mathopen\Bigg}|
+|\def\biggr{\mathclose\bigg} \def\Biggr{\mathclose\Bigg}|
+|\def\biggm{\mathrel\bigg} \def\Biggm{\mathrel\Bigg}|
+|\def\big#1{{\hbox{$\left#1\vbox to 8.5pt{}\right.\n@space$}}}|
+|\def\Big#1{{\hbox{$\left#1\vbox to 11.5pt{}\right.\n@space$}}}|
+|\def\bigg#1{{\hbox{$\left#1\vbox to 14.5pt{}\right.\n@space$}}}|
+|\def\Bigg#1{{\hbox{$\left#1\vbox to 17.5pt{}\right.\n@space$}}}|
+|\def\n@space{\nulldelimiterspace=0pt \m@th}|
+\endlines
+
+There are a few other simple abbreviations related to delimiters:
+\beginlines
+|\def|^|\choose||{\atopwithdelims()}|
+|\def|^|\brack||{\atopwithdelims[]}|
+|\def|^|\brace||{\atopwithdelims\{\}}|
+|\def|^|\sqrt||{\radical"270370 }|
+\endlines
+
+And now we come to something more interesting. The ^|\mathpalette| operation
+constructs a formula in all four styles; it is applied here in the
+implementation of\/ ^|\phantom|, ^|\smash|, ^|\root|, and other
+operations. (Actually |\phantom| and |\smash| are not perfect: They
+assume that the current style is uncramped.)
+\beginlines
+|\def\mathpalette#1#2{\mathchoice{#1\displaystyle{#2}}|\parbreak%
+| {#1\textstyle{#2}}{#1\scriptstyle{#2}}{#1\scriptscriptstyle{#2}}}|
+\smallbreak
+|\newbox\rootbox|
+|\def\root#1\of{\setbox\rootbox=|\parbreak%
+| \hbox{$\m@th \scriptscriptstyle{#1}$}\mathpalette\r@@t}|
+|\def\r@@t#1#2{\setbox0=\hbox{$\m@th #1\sqrt{#2}$}|\parbreak%
+| \dimen@=\ht0 \advance\dimen@ by-\dp0|\parbreak%
+| \mkern5mu \raise.6\dimen@\copy\rootbox \mkern-10mu \box0}|
+\smallbreak
+|\newif\ifv@ \newif\ifh@|
+|\def|^|\vphantom||{\v@true\h@false\ph@nt}|
+|\def|^|\hphantom||{\v@false\h@true\ph@nt}|
+|\def\phantom{\v@true\h@true\ph@nt}|
+|\def\ph@nt{\ifmmode\def\next{\mathpalette\mathph@nt}%|\parbreak%
+| \else\let\next=\makeph@nt\fi \next}|
+|\def\makeph@nt#1{\setbox0=\hbox{#1}\finph@nt}|
+|\def\mathph@nt#1#2{\setbox0=\hbox{$\m@th#1{#2}$}\finph@nt}|
+|\def\finph@nt{\setbox2=\null \ifv@ \ht2=\ht0 \dp2=\dp0 \fi|\parbreak%
+| \ifh@ \wd2=\wd0 \fi \box2 }|
+|\def\mathstrut{\vphantom(}|
+\smallbreak
+|\def\smash{\relax % \relax, in case this comes first in \halign|\parbreak%
+| \ifmmode\def\next{\mathpalette\mathsm@sh}\else\let\next\makesm@sh|\parbreak%
+| \fi \next}|
+|\def\makesm@sh#1{\setbox0=\hbox{#1}\finsm@sh}|
+|\def\mathsm@sh#1#2{\setbox0=\hbox{$\m@th#1{#2}$}\finsm@sh}|
+|\def\finsm@sh{\ht0=0pt \dp0=0pt \box0 }|
+\smallbreak
+|\def|^|\cong||{\mathrel{\mathpalette\@vereq\sim}} % \sim over =|\parbreak%
+|\def\@vereq#1#2{\lower.5pt\vbox{\lineskiplimit\maxdimen|%
+ | \lineskip-.5pt|\parbreak%
+| \ialign{$\m@th#1\hfil##\hfil$\crcr#2\crcr=\crcr}}}|
+|\def|^|\notin||{\mathrel{\mathpalette\c@ncel\in}}|\parbreak%
+|\def\c@ncel#1#2{\m@th\ooalign{$\hfil#1\mkern1mu/\hfil$\crcr$#1#2$}}|\parbreak%
+|\def|^|\rightleftharpoons||{\mathrel{\mathpalette\rlh@{}}}|\parbreak%
+|\def\rlh@#1{\vcenter{\m@th\hbox{\ooalign{\raise2pt|\parbreak%
+| \hbox{$#1\rightharpoonup$}\crcr $#1\leftharpoondown$}}}}|
+|\def|^|\buildrel||#1\over#2{\mathrel{\mathop{\kern0pt #2}\limits^{#1}}}|
+|\def|^|\doteq||{\buildrel\textstyle.\over=}|
+\endlines
+These definitions illustrate how other built-up
+symbol combinations could be defined to work in all four styles.
+
+Alternate names are defined now:
+\beginlines
+|\let|^|\ne||=\neq \let|^|\le||=\leq \let|^|\ge||=\geq|
+^^{esc vert}^^{esc lbrace}^^{esc rbrace}%
+|\let\{=\lbrace \let\|\||=\Vert \let\}=\rbrace|
+|\let|^|\to||=\rightarrow \let|^|\gets||=\leftarrow \let|^|\owns||=\ni|
+|\let|^|\land||=\wedge \let|^|\lor||=\vee \let|^|\lnot||=\neg|
+|\def|^|\iff||{\;\Longleftrightarrow\;}|
+\endlines
+
+The 32 common functions whose names generally appear in roman letters are listed
+in Chapter~18. Only a few of the definitions need to be shown here:
+^^|\arccos| ^^|\cos| ^^|\csc| ^^|\exp| ^^|\ker| ^^|\limsup| ^^|\min|
+^^|\sinh| ^^|\arcsin| ^^|\cosh| ^^|\deg| ^^|\gcd| ^^|\lg| ^^|\ln| ^^|\Pr|
+^^|\sup| ^^|\arctan| ^^|\cot| ^^|\det| ^^|\hom| ^^|\lim| ^^|\log| ^^|\sec|
+^^|\tan| ^^|\arg| ^^|\coth| ^^|\dim| ^^|\inf| ^^|\liminf| ^^|\max|
+^^|\sin| ^^|\tanh|
+\beginlines
+|\def\arccos{|^|\mathop||{\rm arccos}|^|\nolimits||}|
+ \hskip100pt$\cdots$\qquad|\def\tanh{|^|\mathop||{\rm tanh}|^|\nolimits||}|
+|\def\det{\mathop{\rm det}}|\qquad$\cdots$\qquad|\def\sup{\mathop{\rm sup}}|
+|\def\liminf{\mathop{\rm lim\,inf}} \def\limsup{\mathop{\rm lim\,sup}}|
+\smallskip
+|\def|^|\bmod||{\nonscript\mskip-\medmuskip \mkern5mu|\parbreak%
+| |^|\mathbin||{\rm mod} \penalty900 \mkern5mu \nonscript\mskip-\medmuskip}|
+|\def|^|\pmod||#1{\allowbreak \mkern18mu ({\rm mod}\,\,#1)}|
+\endlines
+
+The definition of\/ ^|\matrix| goes to some pains to ensure that two
+$n$-rowed matrices will have the same height and the same depth, unless
+at least one of their rows is unusually big. The definition of
+^|\bordermatrix| is even more complicated, but it seems to work
+reasonably well; it uses a constant ^|\p@renwd| that represents the
+width of a big extensible left parenthesis.
+\beginlines
+|\def\matrix#1{\null\,|^|\vcenter||{\normalbaselines\m@th|\parbreak%
+| \ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr|\parbreak%
+| \mathstrut\crcr\noalign{\kern-\baselineskip}|\parbreak%
+| #1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}\,}|
+\smallbreak
+|\newdimen\p@renwd \setbox0=\hbox{\tenex B} \p@renwd=\wd0|
+|\def\bordermatrix#1{\begingroup \m@th|\parbreak%
+| \setbox0=\vbox{\def\cr|%
+ |{\crcr\noalign{\kern2pt\global\let\cr=\endline}}|\parbreak%
+| \ialign{$##$\hfil\kern2pt\kern\p@renwd|%
+ |&\thinspace\hfil$##$\hfil|\parbreak%
+| &&\quad\hfil$##$\hfil\crcr|\parbreak%
+| \omit\strut\hfil\crcr\noalign{\kern-\baselineskip}|\parbreak%
+| #1\crcr\omit\strut\cr}}|\parbreak%
+| \setbox2=\vbox{\unvcopy0 \global\setbox1=\lastbox}|\parbreak%
+| \setbox2=\hbox{\unhbox1 \unskip \global\setbox1=\lastbox}|\parbreak%
+| \setbox2=\hbox{$\kern\wd1\kern-\p@renwd \left( \kern-\wd1|\parbreak%
+| \global\setbox1=\vbox{\box1\kern2pt}|\parbreak%
+| \vcenter{\kern-\ht1 \unvbox0 \kern-\baselineskip} \,\right)$}|\parbreak%
+| \null\;\vbox{\kern\ht1\box2}\endgroup}|
+\endlines
+
+The next macros are much simpler:
+\beginlines
+|\def|^|\cases||#1{\left\{\,\vcenter{\normalbaselines\m@th|\parbreak%
+| \ialign{$##\hfil$&\quad##\hfil\crcr#1\crcr}}\right.}|
+|\def|^|\pmatrix||#1{\left( \matrix{#1} \right)}|
+\endlines
+
+Finally there are macros for displayed equations:
+\beginlines
+|\def|^|\openup||{\afterassignment\@penup\dimen@=}|\parbreak%
+|\def\@penup{\advance\lineskip\dimen@|\parbreak%
+| \advance\baselineskip\dimen@ \advance\lineskiplimit\dimen@}|
+|\def|^|\eqalign||#1{\null\,\vcenter{\openup1\jot \m@th|\parbreak%
+| \ialign{\strut\hfil$\displaystyle{##}$&$\displaystyle{{}##}$\hfil|\parbreak%
+| \crcr#1\crcr}}\,}|
+\smallbreak
+|\newif\ifdt@p|
+|\def\displ@y{\global\dt@ptrue \openup1\jot \m@th|\parbreak%
+| |^|\everycr||{\noalign{\ifdt@p \global\dt@pfalse|%
+ | \ifdim\prevdepth>-1000pt|\parbreak%
+| \vskip-\lineskiplimit \vskip\normallineskiplimit \fi|\parbreak%
+| \else \penalty|^|\interdisplaylinepenalty|| \fi}}}|
+|\def\@lign{\tabskip=0pt\everycr={}} % restore inside \displ@y|
+|\def|^|\displaylines||#1{\displ@y \tabskip=0pt|\parbreak%
+| \halign{\hbox to\displaywidth{|%
+ |$\hfil\@lign\displaystyle##\hfil$}\crcr|\parbreak%
+| #1\crcr}}|
+\smallbreak
+|\def|^|\eqalignno||#1{\displ@y \tabskip=|^|\centering|\parbreak%
+| \halign to\displaywidth{\hfil$\@lign\displaystyle{##}$\tabskip=0pt|\parbreak%
+| &$\@lign\displaystyle{{}##}$\hfil\tabskip=\centering|\parbreak%
+| &\llap{$\@lign##$}\tabskip=0pt\crcr|\parbreak%
+| #1\crcr}}|
+|\def|^|\leqalignno||#1{\displ@y \tabskip=\centering|\parbreak%
+| \halign to\displaywidth{\hfil$\@lign\displaystyle{##}$\tabskip=0pt|\parbreak%
+| &$\@lign\displaystyle{{}##}$\hfil\tabskip=\centering|\parbreak%
+| &\kern-\displaywidth\rlap{$\@lign##$}\tabskip=\displaywidth\crcr|\parbreak%
+| #1\crcr}}|
+\endlines
+The value of\/ |\lineskiplimit| is assumed to be |\normallineskiplimit|
+plus the accumulated amount of ``opening up.'' Thus, the |\vskip|
+instructions in |\displ@y| will compensate for the fact that the
+first baseline of an alignment is separated by an opened-up baselineskip
+from the last line preceding the display.
+
+\subsection Macros for output. The |plain.tex| file also contains the output
+routine described in Chapters 15 and~23. First there are simple
+facilities related to page numbers, headings, and footings:
+\beginlines
+|\countdef|^|\pageno||=0 \pageno=1 % first page is number 1|
+|\newtoks|^|\headline|| \headline={\hfil} % headline is normally blank|
+|\newtoks|^|\footline|| \footline={\hss\tenrm\folio\hss}|\parbreak%
+| % footline is normally a centered page number in font \tenrm|
+|\def|^|\folio||{\ifnum\pageno<0 \romannumeral-\pageno \else\number\pageno \fi}|
+|\def|^|\nopagenumbers||{\footline={\hfil}} % blank out the footline|
+|\def|^|\advancepageno||{\ifnum\pageno<0 \global\advance\pageno by -1|\parbreak%
+| \else\global\advance\pageno by 1 \fi} % increase |\||pageno|\|
+\smallbreak
+|\newif\ifr@ggedbottom|
+|\def|^|\raggedbottom||{\topskip10pt plus60pt \r@ggedbottomtrue}|
+|\def|^|\normalbottom||{\topskip10pt \r@ggedbottomfalse} % undoes \raggedbottom|
+\endlines
+
+The ^|\footnote| macro has a few subtle features that can best be appreciated
+by someone who reads Chapter~15 very carefully. It also uses some
+^|\bgroup| and ^|\futurelet| and ^|\aftergroup| trickery, so that the footnote
+text does not need to be a ^{parameter} to |\vfootnote|:
+\beginlines
+^|\newinsert||\footins|
+|\def\footnote#1{\let\@sf=\empty|%
+ | % parameter #2 (the text) is read later|\parbreak%
+| \ifhmode\edef\@sf{\spacefactor=\the\spacefactor}\/\fi|\parbreak%
+| #1\@sf\vfootnote{#1}}|
+|\def|^|\vfootnote||#1{\insert\footins\bgroup|\parbreak%
+| \interlinepenalty=\interfootnotelinepenalty|\parbreak%
+| \splittopskip=\ht\strutbox % top baseline for broken footnotes|\parbreak%
+| \splitmaxdepth=\dp\strutbox \floatingpenalty=20000|\parbreak%
+| \leftskip=0pt \rightskip=0pt \spaceskip=0pt \xspaceskip=0pt|\parbreak%
+| \textindent{#1}\footstrut\futurelet\next\fo@t}|
+|\def\fo@t{\ifcat\bgroup\noexpand\next \let\next\f@@t|\parbreak%
+| \else\let\next\f@t\fi \next}|
+|\def\f@@t{\bgroup\aftergroup\@foot\let\next}|
+|\def\f@t#1{#1\@foot}|
+|\def\@foot{\strut\egroup}|
+|\def\footstrut{\vbox to\splittopskip{}}|
+|\skip\footins=\bigskipamount % space added when footnote is present|
+|\count\footins=1000 % footnote magnification factor (1 to 1)|
+|\dimen\footins=8in % maximum footnotes per page|
+\endlines
+
+^{Floating insertions} are handled by doing an ^|\insert| whose
+vertical list consists of a penalty item followed by a single box:
+\beginlines
+|\newinsert\topins \newif\ifp@ge \newif\if@mid|
+|\def|^|\topinsert||{\@midfalse\p@gefalse\@ins}|
+|\def|^|\midinsert||{\@midtrue\@ins}|
+|\def|^|\pageinsert||{\@midfalse\p@getrue\@ins}|
+|\skip\topins=0pt % no space added when a topinsert is present|
+|\count\topins=1000 % magnification factor (1 to 1)|
+|\dimen\topins=\maxdimen % no limit per page|
+|\def\@ins{\par\begingroup\setbox0=\vbox\bgroup} % start a \vbox|
+|\def\endinsert{\egroup % finish the \vbox|\parbreak%
+| \if@mid \dimen@=\ht0 \advance\dimen@ by\dp\z@ |%
+ |\advance\dimen@ by12\p@|\parbreak%
+| \advance\dimen@ by\pagetotal \advance\dimen@ by-\pageshrink|\parbreak%
+| \ifdim\dimen@>\pagegoal \@midfalse\p@gefalse\fi\fi|\parbreak%
+| \if@mid \bigskip \box0 \bigbreak|\parbreak%
+| \else\insert\topins{\penalty100 % floating insertion|\parbreak%
+| |^|\splittopskip||=0pt |^|\splitmaxdepth||=\maxdimen |%
+ ^|\floatingpenalty||=0|\parbreak%
+| \ifp@ge \dimen@=\dp0|\parbreak%
+| \vbox to\vsize{\unvbox0 \kern-\dimen@} % depth is zero|\parbreak%
+| \else \box0 \nobreak\bigskip\fi}\fi\endgroup}|
+\endlines
+
+Most of the ^|\output| routine appears in Chapter 23; it is given here
+in full:
+\beginlines
+|\output={\plainoutput}|
+|\def|^|\plainoutput|%
+ |{\shipout\vbox{\makeheadline\pagebody\makefootline}%|\parbreak%
+| \advancepageno|\parbreak%
+| \ifnum\outputpenalty>-20000 \else\dosupereject\fi}|
+|\def|^|\pagebody||{\vbox to\vsize{\boxmaxdepth=\maxdepth \pagecontents}}|
+|\def|^|\makeheadline||{\vbox to 0pt{\vskip-22.5pt|\parbreak%
+| \line{\vbox to8.5pt{}\the\headline}\vss}\nointerlineskip}|
+|\def|^|\makefootline||{\baselineskip=24pt \lineskiplimit=0pt|\parbreak%
+| \line{\the\footline}}|
+|\def|^|\dosupereject|%
+ |{\ifnum\insertpenalties>0 % something is being held over|\parbreak%
+| \line{}\kern-\topskip\nobreak\vfill\supereject\fi}|
+\smallbreak
+|\def|^|\pagecontents||{\ifvoid\topins\else\unvbox\topins\fi|\parbreak%
+| \dimen@=\dp255 \unvbox255|\parbreak%
+| \ifvoid\footins\else % footnote info is present|\parbreak%
+| \vskip\skip\footins \footnoterule \unvbox\footins\fi|\parbreak%
+| \ifr@ggedbottom \kern-\dimen@ \vfil \fi}|
+|\def|^|\footnoterule||{\kern-3pt|\parbreak%
+| \hrule width 2truein \kern 2.6pt} % the \hrule is .4pt high|
+\endlines
+
+\subsection Hyphenation and everything else. The last part of |plain.tex|
+reads the hyphenation patterns and exceptions found on file ^|hyphen.tex|
+(see Appendix~H\null); then it defines a few miscellaneous macros, sets
+up ^|\rm| type, and that's all!
+\beginlines
+^|\lefthyphenmin||=2 |^|\righthyphenmin||=3 % disallow x- or -xx breaks|
+|\input hyphen % the hyphenation patterns and exceptions|
+\nobreak\smallskip
+|\def|^|\magnification||{|^|\afterassignment||\m@g\count@}|
+|\def\m@g{\mag=\count@|\parbreak%
+| \hsize6.5truein\vsize8.9truein\dimen\footins8truein}|
+\smallbreak
+|\def|^|\loggingall|%
+ |{\tracingcommands=2 \tracingstats=2|\parbreak%
+| \tracingpages=1 \tracingoutput=1 \tracinglostchars=1 |\parbreak%
+| \tracingmacros=2 \tracingparagraphs=1 \tracingrestores=1 |\parbreak%
+| \showboxbreadth=\maxdimen \showboxdepth=\maxdimen}|
+|\def|^|\tracingall||{\tracingonline=1 \loggingall}|
+\smallbreak
+|\def|^|\showhyphens|%
+ |#1{\setbox0=\vbox{\parfillskip0pt \hsize=\maxdimen \tenrm|\parbreak%
+| \pretolerance=-1 \tolerance=-1 \hbadness=0 \showboxdepth=0 \ #1}}|
+\smallbreak
+|\normalbaselines\rm % select roman font|
+|\nonfrenchspacing % punctuation affects the spacing|
+|\catcode`@=12 % at signs are no longer letters|
+\nobreak\smallskip
+|\def|^|\fmtname||{plain}|
+|\def\fmtversion{3.141592653} % identifies the current format|
+\endlines
+The format name and version number are recorded in control sequences,
+in order to help the people who might have to explain why something
+doesn't work. Macro files like |plain.tex| should not be changed in any way,
+except with respect to preloaded fonts, unless the changes are authorized
+by the author of the macros.
+
+\endchapter
+
+The purpose of a programming system is to make a computer easy to use.
+To do this, it furnishes languages and various facilities
+that are in fact programs invoked and controlled by language features.
+But these facilities are bought at a price:
+the external description of a programming system is ten to twenty times
+as large as the external description of the computer system itself.
+The user finds it far easier to specify any particular function,
+but there are far more to choose from,
+and far more options and formats to remember.
+\author FREDERICK P. ^{BROOKS}, JR., {\sl The Mythical Man Month\/} (1975)
+ % p 43
+
+\bigskip
+
+When someone says, ``I want a programming language
+in which I need only say what I wish done,''
+give him a lollipop.
+\author ALAN ^{PERLIS}, {\sl Epigrams on Programming\/} (1982)
+ % SIGPLAN Notices 17,9 (September 82), 7--13.
+ % There are many more, like "Editing is a rewording activity."
+
+\eject
+ \beginchapter Appendix C. Character\\Codes
+
+\ninepoint
+Different computers tend to have different ways of representing the
+characters in files of text, but \TeX\ gives the same results on
+all machines, because it converts everything to a standard internal
+code when it reads a file. \TeX\ also converts back from its internal
+representation to the appropriate external code, when it writes
+a file of text; therefore most users need not be aware of the fact
+that the ^{codes} have actually switched back and forth inside the machine.
+
+The purpose of this appendix is to define \TeX's internal code,
+which has the same characteristics on all implementations of \TeX.
+The existence of such a code is important, because it
+makes \TeX\ constructions ``portable.'' For example, \TeX\ allows
+^{alphabetic constants} like |`b| to be used as numbers; the fact
+that |`b| always denotes the integer~98 means that we can
+write machine-independent macros that decide, for instance, whether
+a given character is a digit between |0| and |9|.
+Furthermore the internal code of \TeX\ also survives in its ^|dvi|
+output files, which can be printed by software that knows nothing
+about where the |dvi| data originated; essentially the same
+output will be obtained from all implementations of \TeX, regardless
+of the host computer, because the |dvi| data is expressed in a
+machine-independent code.
+
+\smallskip
+\TeX's internal code is based on the American Standard Code for
+Information Interchange, known popularly as ``^{ASCII}.'' There are
+128 codes, numbered 0~to~127; we conventionally express the numbers
+in octal notation, from \oct{000} to \oct{177}, or in
+hexadecimal notation, from \hex{00} to \hex{7F}. Thus, the value of
+|`b| is normally called \oct{142} or \hex{62}, not 98. In the
+ASCII scheme, codes \oct{000} through \oct{040} and
+code \oct{177} are~assigned to special functions; for example,
+code \oct{007} is called |BEL|, and it means ``Ring the bell.''
+The other 94 codes are assigned to visible symbols. Here is a
+chart that shows ASCII codes in such a way that octal and hexadecimal
+equivalents can easily be read off:
+\beginchart{\global\count@='41\tentt
+ \def\chartstrut{\lower4.3pt\vbox to13.6pt{}}}
+&\oct{00x}&&NUL&&SOH&&STX&&ETX&&EOT&&ENQ&&ACK&&BEL&&\oddline0
+&\oct{01x}&&BS&&HT&&LF&&VT&&FF&&CR&&SO&&SI&\evenline
+&\oct{02x}&&DLE&&DC1&&DC2&&DC3&&DC4&&NAK&&SYN&&ETB&&\oddline1
+&\oct{03x}&&CAN&&EM&&SUB&&ESC&&FS&&GS&&RS&&US&\evenline
+&\oct{04x}&&SP&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline2
+&\oct{05x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{06x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline3
+&\oct{07x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{10x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline4
+&\oct{11x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{12x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline5
+&\oct{13x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{14x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline6
+&\oct{15x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{16x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline7
+&\oct{17x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&DEL&\evenline
+\endchart
+
+Ever since ASCII was established in the early 1960s, people have had
+different ideas about what to do with positions \oct{000}--\oct{037} and
+\oct{177}, because most of the functions assigned to those codes are
+appropriate only for special purposes like file transmission, not for
+applications to printing or to interactive computing. It turned out that
+manufacturers soon started producing line printers that were capable of
+generating 128 characters, 33~of~which were tailored to the special needs
+of particular customers; part of the advantage of a standard code was
+therefore lost. On the other hand, the remaining 95~codes (including
+\oct{40}=|SP|, a blank space) have become widely adopted, and they are now
+implanted within most of today's computer terminals. When an ASCII
+keyboard is available, you can specify each of the 128 codes to \TeX\ in
+terms of the 95 standard characters, as follows:
+\beginchart{\def\?{\char`^\char`^\:}\global\count@='100
+ \postdisplaypenalty=0 \tentt}
+&\oct{00x}&&\?&&\?&&\?&&\?&&\?&&\?&&\?&&\?&&\oddline0
+&\oct{01x}&&\?&&\?&&\?&&\?&&\?&&\?&&\?&&\?&\evenline
+&\oct{02x}&&\?&&\?&&\?&&\?&&\?&&\?&&\?&&\?&&\oddline1
+&\oct{03x}&&\?&&\?&&\?&&\?&&\?&&\?&&\?&&\?&\evenline
+&\global\count@='40
+\oct{04x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline2
+&\oct{05x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{06x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline3
+&\oct{07x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{10x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline4
+&\oct{11x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{12x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline5
+&\oct{13x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{14x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline6
+&\oct{15x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&\evenline
+&\oct{16x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\oddline7
+&\oct{17x}&&\:&&\:&&\:&&\:&&\:&&\:&&\:&&\char`^\char`^?&\evenline
+\endchart
+(Here `|^^|' ^^{uparrow uparrow} doesn't necessarily mean two
+circumflex characters; it means two identical characters whose
+current |\catcode| is~7. In such cases \TeX\ simply adds or
+subtracts \oct{100} from the internal code of the character
+that immediately follows. For example, |*|~can also be typed as |^^j|;
+|j|~can also be typed as |^^*|.)
+
+An extended ASCII code intended for text editing and interactive computing
+ was developed at several universities about 1965, and
+for many years there have been terminals in use at Stanford, MIT,
+Carnegie-Mellon, and elsewhere that have 120 or~121 symbols, not just~95.
+Aficionados of these keyboards (like the author of this book)
+are loath to give up their extra characters; it seems that
+such people make heavy use of about 5~of the extra~25, and occasional
+use of the other~20, although
+different people have different groups of five. For example, the
+author developed \TeX\ on a keyboard that includes the symbols
+{\tentex\char'30},~{\tentex\char1}, {\tentex\char'32}, {\tentex\char'34},
+and~{\tentex\char'35}, and he finds that this makes it much more pleasant
+to type class notes,
+technical papers, and computer programs of the kind he likes to write;
+his logician friends make heavy use of the {\tentex\char'24} and
+{\tentex\char'25} keys; and so on. It is recommended that \TeX\
+implementations on systems with large character sets be consistent with the
+following codes:
+\beginchart{\tentex\postdisplaypenalty=0}
+\normalchart
+\endchart
+Of course, designers of \TeX\ macro packages that are intended to be
+widely used should stick to the standard ASCII characters.
+
+Incidentally, the ASCII character |^| that appears in position \oct{136}
+is sometimes called a ``^{caret},'' but dictionaries of English tell us
+that a caret is a larger symbol, more like character \oct{004} in the
+extended set above. The correct name for |^| is ``^{circumflex},'' but
+this is quite a mouthful, so a shorter name like ``^{hat}'' is preferable.
+It seems desirable to preserve the traditional distinction between
+caret and hat.
+
+The extended code shown above was developed at MIT; it is similar to, but
+slightly better than, the code implemented at Stanford. Seven of the codes
+are conventionally assigned to the standard ASCII control functions
+|NUL| (^\<null>), |HT| (^\<tab>), |LF| (^\<linefeed>), |FF|
+(^\<formfeed>), |CR| (^\<return>), |ESC| (^\<escape>), and
+|DEL| (^\<delete>), and they appear in the standard ASCII positions; hence the
+corresponding seven characters
+{\tentex\char0~\char'11~\char'12~\char'14~\char'15~\char'33~\char'177}
+do not actually appear on the keyboard. These seven ``hidden''
+characters show up only on certain output devices.
+
+Modern keyboards allow 256 codes to be input, not just 128; so \TeX\
+represents characters internally as numbers in the range 0--255 (i.e.,
+\oct{000}--\oct{377}, or \hex{00}--\hex{FF}). Implementations of \TeX\
+differ in which characters they will accept in input files and which
+they will transmit to output files; these subsets can be specified
+independently. A completely permissive version of \TeX\ allows full
+256-character input and output; other versions might ignore all
+but the visible characters of ASCII; still other versions might
+distinguish the tab character (code \oct{011}) from a space on input,
+but might output each tab as a sequence of three characters |^^I|.
+
+Many people, unfortunately, have the opposite problem:\ Instead of the
+95~standard characters and some others, they have fewer than~95 symbols
+actually available. What can be done in such cases? Well, it's possible
+to use \TeX\ with fewer symbols, by invoking more control
+sequences; for example, plain \TeX\ defines ^|\lq|, ^|\rq|, ^|\lbrack|,
+^|\rbrack|, ^|\sp|, and ^|\sb|, so that you need not type |`|,~|'|, |[|, |]|,
+|^|, and~|_|, respectively.
+
+A person who implements \TeX\ on computer systems that do not have
+95~externally representable symbols should adhere to the following
+guidelines: \ (a)~Stay as close as possible to the ASCII conventions.
+\ (b)~Make sure that codes \oct{041}--\oct{046}, \oct{060}--\oct{071},
+\oct{141}--\oct{146}, and \oct{160}--\oct{171} are present and that
+each unrepresentable
+internal code $<\null$\oct{200} leads to a representable code when \oct{100} is
+added or subtracted; then all 256 codes can be input and output.
+\ (c)~Cooperate with everyone else who shares
+the same constraints, so that you all adopt the same policy.
+\ (See Appendix~J for information about the \TeX\ Users Group.)
+
+\def\Russiantt#1{{\tt\hbox to.5em{\hss\eighttt\char#1\hss}}}
+Very few conventions about character codes are hardwired into \TeX:
+Almost everything can be changed by a format package that changes parameters
+like |\escapechar| and sets up the |\catcode|, |\mathcode|, |\uccode|,
+|\lccode|, |\sfcode|, and |\delcode| tables.
+Thus a \TeX\ manuscript that has been written in ^{Denmark}, say,
+can be run in California, and vice versa, even though quite different
+conventions might be used in different countries. The only character codes
+that \TeX\ actually ``knows'' are these: \ (1)~|INITEX| initializes
+the code tables as described in Appendix~B\null; the same initialization is
+done by all implementations of \TeX. \ (2)~\TeX\ uses the character codes
+\]|+-.,`'"<=>0123456789ABCDEF| in its syntax rules (Chapters 20, 24, and
+Appendix~H\null), and it
+uses most of the uppercase and lowercase letters in its ^{keywords}
+|pt|, |to|, |plus|, etc. These same codes and keywords are used in
+all implementations of \TeX\null. For example, if \TeX\ is implemented for
+a purely ^{Cyrillic} ^^{Russian} keyboard, the letter `\Russiantt5' should be
+assigned to code \oct{160} and `\Russiantt{`T}' to code \oct{164}, so that
+`\Russiantt5\Russiantt{`T}' still means `|pt|'; or else control sequences
+should be defined so that what \TeX\ sees is equivalent to the keywords
+it needs. \ (3)~The operations |\number|,
+|\romannumeral|, |\the|, and |\meaning| can generate letters, digits, spaces,
+decimal points, minus signs, double quotes, colons, and `|>|' signs; these same
+codes are generated in all implementations of \TeX.
+\ (4)~The |\hyphenation| and |\pattern| commands described in Appendix~H
+give special interpretation to the ten digits and to the characters
+`|.|'\ and~`|-|'. \ (5)~The codes for the four characters |$|~|.|~|{|~|}|
+are inserted when \TeX\ recovers from certain errors, and braces are
+inserted around an ^|\output| routine; appropriate catcodes are attached
+to these tokens, so it doesn't matter if these symbols have their plain
+\TeX\ meanings or not. \ (6)~There is a special convention for
+representing characters 0--255 in the hexadecimal forms
+|^^00|--|^^ff|, explained in Chapter~8. This convention is always
+acceptable as input, when |^| is any character of catcode~7. Text
+output is produced with this convention only when representing
+characters of code $\ge128$ that a \TeX\ installer has chosen not to
+output directly.
+
+\endchapter
+
+Code sets obtained by modifying the standard as shown above
+or by other replacements are nonstandard.
+\author ASA SUBCOMMITTEE X3.2, {\sl American Standard\break %
+ Code for Information Interchange\/^^{ASCII}} (1963)
+% in {\sl Communications of the ACM\/}
+
+\bigskip
+
+Both the Stanford and DEC uses of the ASCII control characters
+are in violation of the USA Standard Code,
+but no Federal Marshal is likely to come running out
+and arrest people who type control-T to their computers.
+\author BRIAN ^{REID}, {\sl SCRIBE Introductory User's Manual\/} (1978) % p82
+
+\eject
+ \beginchapter Appendix D. Dirty Tricks
+
+\TeX\ was designed to do the ordinary tasks of typesetting: to make paragraphs
+and pages. But the underlying mechanisms that facilitate ordinary
+typesetting---e.g., boxes, glue, penalties, and macros---are extremely
+versatile; hence people have discovered sneaky ways to coerce \TeX\ into
+doing tricks quite different from what its author originally had in mind.
+Such clever constructions are not generally regarded as examples of
+``high \TeX''; but many of them have turned out to be useful and
+instructive, worthy of being known (at least by a few wizards). The purpose
+of this appendix is to introduce crafty and/or courageous readers
+to the nether world of \TeX arcana.
+
+\ninepoint\medskip
+\setbox0=\hbox spread-6\fontdimen4\font % that removes all the shrinkability
+ {\strut Please don't read this material until you've}
+\setbox1=\hbox to\wd0{\strut had plenty of experience with plain \TeX.}
+\line{\leaders\hbox{\dbend\kern2pt}\hfil\vtop{\box0\box1}}
+\nointerlineskip
+\noindent\strut After you have read and understood the secrets below, you'll
+know all sorts of devious combinations of \TeX\ commands,
+and you will often be tempted to write inscrutable macros. Always remember,
+however, that there's usually a simpler and better way to do something
+than the first way that pops into your head. You may not have to
+resort to any subterfuge at all, since \TeX\ is able to do lots of things
+in a straightforward way. Try for simple solutions first.
+
+\subsection Macro madness. If you need to write complicated ^{macros}, you'll
+need to be familiar with the fine points in Chapter~20. \TeX's control
+sequences are divided into two main categories, ``expandable'' and
+``unexpandable''; the former category includes all macros and |\if...\fi|
+tests, as well as special operations like |\the| and |\input|, while the
+latter category includes the primitive commands listed in Chapter~24.
+The expansion of expandable tokens takes place in \TeX's ``^{mouth},''
+but primitive commands (including assignments) are done in \TeX's
+``^{stomach}.'' One important consequence of this structure is that
+it is impossible to redefine a control sequence or to advance a register
+while \TeX\ is expanding the token list of, say, a |\message| or |\write|
+command; assignment operations are done only when \TeX\ is building a
+vertical or horizontal or math list.
+
+For example, it's possible to put |\n| asterisks into a paragraph,
+by saying simply `|{|^|\loop||\ifnum\n>0 *\advance\n-1 \repeat}|'.
+But it's much more difficult to define a
+control sequence |\asts| to consist of exactly |\n| consecutive asterisks.
+If\/ |\n| were known to be at most~5, say, it would be possible to write
+\begintt
+\edef\asts{\ifcase\n\or*\or**\or***\or****\or*****\else\bad\fi}
+\endtt
+since \TeX\ handles |\ifcase| in its mouth. But for general |\n| it would
+be impossible to use a construction like
+`|\edef\asts{\loop\ifnum\n>0 *\advance\n-1 \repeat}|', since |\n| doesn't change
+during an ^|\edef|. A more elaborate program is needed; e.g.,
+\begintt
+{\xdef\asts{}
+ \loop\ifnum\n>0 \xdef\asts{\asts*}\advance\n-1 \repeat}
+\endtt
+And here's another solution (which may be faster, because
+^{token list registers} can be expanded more quickly than macros
+on some implementations, using ^|\the|):
+\begintt
+\newcount\m \newtoks\t \m=\n \t={}
+\loop \ifnum\m>0 \t=\expandafter{\the\t*}\advance\m-1 \repeat
+\edef\asts{\the\t}
+\endtt
+
+However, both of these solutions have a running time proportional to the
+square of\/~|\n|. There's a much quicker way to do the job:
+\begintt
+\begingroup\aftergroup\def\aftergroup\asts\aftergroup{
+\loop \ifnum\n>0 \aftergroup*\advance\n-1 \repeat
+\aftergroup}\endgroup
+\endtt
+Get it? The ^|\aftergroup| commands cause a whole list of other tokens to be
+saved up for after the group! This method has only one flaw, namely
+that it takes up |\n| cells of space on \TeX's ^{input stack} and |\n|
+more on \TeX's ^{save stack}; hence a special version of \TeX\ may be
+required when |\n| is larger than 150 or~so.
+
+(Incidentally, there's a completely different way to put |\n| asterisks
+into a paragraph, namely to say `|\setbox0=\hbox{*}|%
+^|\cleaders|^|\copy||0\hskip\n\wd0|'. This may seem to be the fastest
+solution of all; but actually it is not so fast, when all things are
+considered, since it generates four bytes of ^|dvi| output per asterisk,
+compared to only one byte per asterisk in the other methods. Input/output
+time takes longer than computation time, both in \TeX\ itself and in
+the later stages of the printing process.)
+
+The problem just solved may seem like a rather special application; after
+all, who needs a control sequence that contains a variable number of
+asterisks? But the same principles apply in other similar cases,
+e.g., when you want to construct a variable-length ^|\parshape|
+specification. Similarly, many of the ``toy problems'' solved below are
+meant to illustrate paradigms that can be used in real-life situations.
+
+The precise rules for expansion are explained in Chapter~20; and the
+best way to get familiar with \TeX's expansion mechanism is to watch it
+in action, looking at the log file when |\tracingmacros=2| and
+|\tracingcommands=2|. One of the important ways to change the normal
+order of expansion is to use ^|\expandafter|; the construction
+\begintt
+\expandafter\a\b
+\endtt
+causes |\b| to be expanded first, then~|\a|.
+And since |\expandafter| is itself expandable, the construction
+\begintt
+\expandafter\expandafter\expandafter\a\expandafter\b\c
+\endtt
+causes |\c| to be expanded first, then |\b|, then |\a|. \ (The next step,
+\begintt
+\expandafter\expandafter\expandafter\expandafter
+ \expandafter\expandafter\expandafter\a
+ \expandafter\expandafter\expandafter\b\expandafter\c\d
+\endtt
+is probably too lengthy to be of any use.)
+
+It's possible to make good use of\/ |\expandafter\a\b| even when |\a|
+isn't expandable. For example, the token list assignment
+`|\t=\expandafter{\the\t|~|*}|' in the example on the previous page
+was able to invade territory where expansion is normally suppressed,
+by expanding after a left brace. Similarly,
+\begintt
+\t=\expandafter{\expandafter*\the\t}
+\endtt
+would have worked; and ^^|\uppercase|
+\begintt
+\uppercase\expandafter{\romannumeral\n}
+\endtt
+yields the value of register |\n| in ^{uppercase roman numerals}.
+^^{roman numerals, uppercase}
+
+Here's a more interesting example: Recall that ^|\fontdimen||1| is the
+amount of ``^{slant} per point'' of a font; hence, for example,
+`^|\the||\fontdimen1\tenit|' expands to `{\tt\the\fontdimen1\tenit}',
+where the characters `|pt|' are of category~12. After the macro definitions
+\begintt
+{\catcode`p=12 \catcode`t=12 \gdef\\#1pt{#1}}
+\let\getfactor=\\
+\def\kslant#1{\kern\expandafter\getfactor\the\fontdimen1#1\ht0}
+\endtt
+one can write, e.g., `|\kslant\tenit|' and this will expand to
+`|\kern0.25\ht0|'. If the boundary of\/ |\box0| is considered to be
+slanted by 0.25 horizontal units per vertical unit, this kern measures
+the horizontal distance by which the top edge of the box is skewed with
+respect to an edge at the baseline. All of the computation of\/
+|\kslant| is done in \TeX's mouth; thus, the mouth can do some rather
+complicated things even though it cannot assign new values. \ (Incidentally,
+an indirect method was used here to define the control sequence ^|\getfactor|
+when the character~|t| had category~12, since control words normally consist
+only of letters. The alternative construction
+\begintt
+{\catcode`p=12 \catcode`t=12
+ \csname expandafter\endcsname\gdef
+ \csname getfactor\endcsname#1pt{#1}}
+\endtt
+would also have worked, since ^|\csname| and ^|\endcsname| don't
+contain `|p|' or `|t|'!)
+
+The mechanism by which \TeX\ determines the ^{arguments} of a macro
+can be applied in unexpected ways. Suppose, for example, that |\t|~is
+a token list register that contains some text; we wish to determine if
+at least one asterisk (|*|$_{12}$) appears in that text. Here's
+one way to do it:
+\begintt
+\newif\ifresult % for the result of a computed test
+\def\atest#1{\expandafter\a\the#1*\atest\a}
+\long\def\a#1*#2#3\a{\ifx\atest#2\resultfalse\else\resulttrue\fi}
+\endtt
+^^|\long|^^|\newif|
+Now after `|\atest\t|', the control sequence |\ifresult|
+will be |\iftrue| or |\iffalse|, depending on whether or not |\t|~contains
+an asterisk. \ (Do you see why?) \ And here's a slightly more elegant way
+to do the same thing, using ^|\futurelet| to look ahead:
+\begintt
+\def\btest#1{\expandafter\b\the#1*\bb}
+\long\def\b#1*{\futurelet\next\bb}
+\long\def\bb#1\bb{\ifx\bb\next\resultfalse\else\resulttrue\fi}
+\endtt
+In both cases the solution works if\/ |\t| contains control sequence tokens
+as well as character tokens, provided that the special control sequences
+|\atest|, |\a|, and |\bb| don't appear. Notice, however, that an asterisk
+is ``hidden'' if it appears within a ^{group}~|{...}|; the test is limited
+to asterisks at nesting level zero. A token list register is always balanced
+with respect to grouping, so there is no danger of the test leading
+to error messages concerning missing braces or extra braces.
+
+We can apply the ideas in the preceding paragraph to solve a problem
+related to generalized math formatting: ^^{displays, non-centered}
+The goal is to set \TeX\ up so that the respective constructions
+`\thinspace|$$|$\,\alpha\,$|$$|\thinspace', ^^{dollar dollar}
+`\thinspace|$$|$\,\alpha\,$^|\eqno|$\,\beta\,$|$$|\thinspace', and
+`\thinspace|$$|$\,\alpha\,$^|\leqno|$\,\beta\,$|$$|\thinspace' will cause
+a macro |$$\generaldisplay$$| to be invoked, with |\eq| defined to be $\alpha$;
+^^{communication between macros}
+furthermore, the test |\ifeqno| should be true when an equation
+number~$\beta$ is present, and |\ifleqno| should be true in the case
+of\/ |\leqno|. When $\beta$ is present, it should be stored in~|\eqn|.
+Here $\alpha$~and~$\beta$ are arbitrary balanced token lists that don't
+contain either |\eqno| or |\leqno| at nesting level zero. The
+following macros do the required maneuvers:
+\begintt
+\newif\ifeqno \newif\ifleqno \everydisplay{\displaysetup}
+\def\displaysetup#1$${\displaytest#1\eqno\eqno\displaytest}
+\def\displaytest#1\eqno#2\eqno#3\displaytest{%
+ \if!#3!\ldisplaytest#1\leqno\leqno\ldisplaytest
+ \else\eqnotrue\leqnofalse\def\eqn{#2}\def\eq{#1}\fi
+ \generaldisplay$$}
+\def\ldisplaytest#1\leqno#2\leqno#3\ldisplaytest{\def\eq{#1}%
+ \if!#3!\eqnofalse\else\eqnotrue\leqnotrue\def\eqn{#2}\fi}
+\endtt
+An examination of the three cases
+|$$|$\,\alpha\,$|$$|,
+|$$|$\,\alpha$|\eqno|$\,\beta\,$|$$|,
+|$$|$\,\alpha$|\leqno|$\,\beta\,$|$$| shows that the correct actions will
+ensue. Parameter |#3| in the tests `|\if!#3!|'\ will be either empty or
+|\eqno| or |\leqno|; thus, the condition will be false (and the second
+`|!|'\ will be skipped) unless |#3| is empty.
+
+Returning to the problem of |*|'s in |\t|, suppose that it's
+necessary to consider |*|'s at all levels of nesting. Then a slower
+routine must be used:
+\begintt
+\def\ctest#1{\resultfalse\expandafter\c\the#1\ctest}
+\def\c{\afterassignment\cc\let\next= }
+\def\cc{\ifx\next\ctest \let\next\relax
+ \else\ifx\next*\resulttrue\fi\let\next\c\fi \next}
+\endtt
+Here ^|\afterassignment| has been used to retain control after a
+non-future ^|\let|; the `|= |' ensures that exactly one token is ^^{equals}
+swallowed per use of\/~|\c|. This routine could be modified in an obvious
+way to count the total number of |*|'s and/or tokens in~|\t|. Notice the
+`|\let\next|' instructions in~|\cc|; it should be clear why the alternative
+\begintt
+\def\cc{\ifx\next\ctest\else\ifx\next*\resulttrue\fi\c\fi}
+\endtt
+would not work. \ (The latter |\c| would always swallow a `|\fi|'.)
+
+^{Space tokens} are sometimes anomalous, so they deserve special care. The
+following macro ^|\futurenonspacelet| behaves essentially like
+|\futurelet| except that it discards any implicit or explicit space tokens
+that intervene before a nonspace is scanned:
+\begintt
+\def\futurenonspacelet#1{\def\cs{#1}%
+ \afterassignment\stepone\let\nexttoken= }
+\def\\{\let\stoken= } \\ % now \stoken is a space token
+\def\stepone{\expandafter\futurelet\cs\steptwo}
+\def\steptwo{\expandafter\ifx\cs\stoken\let\next=\stepthree
+ \else\let\next=\nexttoken\fi \next}
+\def\stepthree{\afterassignment\stepone\let\next= }
+\endtt
+An operation like |\futurenonspacelet| is useful, for example,
+when implementing macros that have a variable number of arguments.
+
+Notice that `|\def\stepthree#1{\stepone}|' would not work here, because of
+\TeX's rule that a \]$_{10}$ token is bypassed if it would otherwise be treated
+as an ^{undelimited} ^{argument}. Because of this rule it is
+difficult to distinguish explicit space tokens from implicit ones.
+The situation is surprisingly complex, because it's possible to
+use ^|\uppercase| to create ``^{funny space}'' tokens like |*|$_{10}$;
+^^|\uccode| for example, the commands
+\begintt
+\uccode` =`* \uppercase{\uppercase{\def\fspace{ }\let\ftoken= } }
+\endtt
+make |\fspace| a macro that expands to a funny space, and they make
+|\ftoken| an implicit funny space. \ (The tests ^|\if||\fspace*|,
+|\if\ftoken*|, ^|\ifcat||\fspace\stoken|, and |\ifcat\ftoken\stoken|
+will all be true, assuming that |*| has category~12; but if |*| has
+category~10, |\if\fspace*| will be false, because \TeX\ normalizes
+all newly created space tokens to \]$_{10}$, as explained in Chapter~8.) \
+Since the various forms of space tokens are almost identical in
+^^|\noexpand|^^|\escapechar|^^|\string|
+behavior, there's no point in dwelling on the details.\footnote\dag{The
+following little program is for \TeX\ exegetes who insist on
+learning the whole story: Macro |\stest| decides whether or not
+a given token list register begins with
+a \<space token> as defined in Chapter~24. If so, the macro
+decides whether the token is explicit and/or funny and/or active.
+\belowdisplayskip=-\baselineskip % cancel unwanted strut at end of footnote
+\begintt|global|displayindent=0pt|global|displaywidth=|hsize
+\newif\ifspace \newif\iffunny \newif\ifexplicit \newif\ifactive
+\def\stest#1{\funnyfalse \expandafter\s\the#1! \stest}
+\def\s{\global\explicitfalse \global\activefalse \futurelet\next\ss}
+\def\ss{\ifcat\noexpand\next\stoken\let\nxt\sx\else\let\nxt\ns\fi\nxt}
+\def\sx{\spacetrue\ifx\next\stoken\let\nxt\sss\else\let\nxt=\ssss\fi\nxt}
+\long\def\sss#1 #2\stest{\def\next{#1}%
+ \ifx\next\empty \global\explicittrue \else\testactive#1\s\fi}
+\long\def\ssss#1#2\stest{\funnytrue{\escapechar=\if*#1`?\else`*\fi\relax
+ \if#1\string#1\uccode`#1=`~ % we assume that ~ is an active character
+ \uppercase{\ifcat\noexpand#1}\noexpand~\global\activetrue
+ \else\global\explicittrue\fi
+ \else\testactive#1\s\fi}}
+\long\def\ns#1\stest{\spacefalse}
+\long\def\testactive#1#2\s{\expandafter\tact\string#1\s\tact}
+\long\def\tact#1#2\tact{\def\next{#2}\ifx\next\xs\global\activetrue
+ \else\ifx\next\empty \global\activetrue\fi\fi} \def\xs{\s}
+\endtt}
+
+\smallbreak
+The argument to ^|\write| is expanded when a |\shipout| occurs, but
+sometimes expansion isn't desired. Here's a macro (suggested by Todd
+^{Allen}) that suppresses all expansion, by inserting ^|\noexpand| before
+each control sequence or ^{active character}. The macro assumes that
+|~|~is an active character, and that the tokens being written
+do not include implicit spaces or braces. Funny spaces are changed
+to ordinary ones.
+\begintt
+\long\def\unexpandedwrite#1#2{\def\finwrite{\write#1}%
+ {\aftergroup\finwrite\aftergroup{\sanitize#2\endsanity}}}
+\def\sanitize{\futurelet\next\sanswitch}
+\def\sanswitch{\let\n@xt\endsanity \ifx\next\endsanity
+ \else\ifcat\noexpand\next\stoken\aftergroup\space\let\n@xt=\eat
+ \else\ifcat\noexpand\next\bgroup\aftergroup{\let\n@xt=\eat
+ \else\ifcat\noexpand\next\egroup\aftergroup}\let\n@xt=\eat
+ \else\let\n@xt=\copytok\fi\fi\fi\fi \n@xt}
+\def\eat{\afterassignment\sanitize \let\next= }
+\long\def\copytok#1{\ifcat\noexpand#1\relax\aftergroup\noexpand\fi
+ \ifcat\noexpand#1\noexpand~\aftergroup\noexpand\fi
+ \aftergroup#1\sanitize}
+\def\endsanity\endsanity{}
+\endtt
+As before, the heavy use of\/ ^|\aftergroup| in |\unexpandedwrite| means
+that parameter |#2| should not include more than about 150~tokens.
+
+\subsection List macros. The next several macros we shall discuss can be
+used to maintain lists of information in the form ^^{backslash backslash}
+\begindisplay
+|\\{|\<item$_1$>|}\\{|\<item$_2$>|}| $\ldots$ |\\{|\<item$_n$>|}|
+\enddisplay
+where each \<item> is a balanced list of tokens. A parameterless control
+sequence whose replacement text has this form may be called a {\sl ^{list
+macro}}. The empty list macro has $n=0$ and it is called ^|\empty|.
+
+It's easy to add new items at either end of a list macro, and to
+concatenate list macros, for example as follows: ^^|\toksdef| ^^|\long|
+\begintt
+\toksdef\ta=0 \toksdef\tb=2 % token list registers for temp use
+\long\def\leftappenditem#1\to#2{\ta={\\{#1}}\tb=\expandafter{#2}%
+ \edef#2{\the\ta\the\tb}}
+\long\def\rightappenditem#1\to#2{\ta={\\{#1}}\tb=\expandafter{#2}%
+ \edef#2{\the\tb\the\ta}}
+\def\concatenate#1=#2&#3{\ta=\expandafter{#2}\tb=\expandafter{#3}%
+ \edef#1{\the\ta\the\tb}}
+\endtt
+Conversely, the left item of a list can be removed and placed in a
+control sequence by the |\lop| macro defined in the following curious way:
+\begintt
+\def\lop#1\to#2{\expandafter\lopoff#1\lopoff#1#2}
+\long\def\lopoff\\#1#2\lopoff#3#4{\def#4{#1}\def#3{#2}}
+\endtt
+For example, if\/ |\l| expands to the list `|\\{a\b}\\{c}\\{{d}}|', the
+macro invocation |\lop\l\to\z| makes |\l| expand to `|\\{c}\\{{d}}|'
+and |\z|~expand to `|a\b|'. The |\lop| operation should be used only when
+|\l|~is nonempty, otherwise an error will occur; to test if\/ |\l|~is
+empty, one simply says `|\ifx\l\empty|'.
+
+The programming details of the |\lop| macro indicate why individual
+items have been enclosed in |{...}| ^{groups}. A simpler kind of list,
+in which grouping is omitted and an extra |\\| appears at the end,
+suffices for many purposes; one could define, for instance,
+\begintt
+\long\def\lopoff\\#1\\#2\lopoff#3#4{\def#4{#1}\def#3{\\#2}}
+\endtt
+and the results would be almost the same as before. In this case an empty
+list macro expands to `|\\|'. However, the new |\lop| resulting from this
+new |\lopoff| macro also removes a pair
+of braces, if the leftmost item happens to be a group; extra braces are
+included in our general scheme to prevent such anomalies.
+
+So far the examples we've considered haven't revealed why the |\\|'s
+appear in the general scheme; it appears that grouping by itself
+should be enough. But in fact, the |\\| separators are enormously
+useful, because we can define |\\| to be any desired one-argument macro,
+and then we can {\sl execute\/} the list! For example, here's a
+way to count the number of items: ^^{hash hash}
+\begintt
+\def\cardinality#1\to#2{#2=0 \long\def\\##1{\advance#2 by1 }#1}
+\endtt
+(Parameter |#2| is supposed to be the name of a count register.) \
+And here's a way to take a list macro and center all its items
+on individual lines within a |\vbox|:
+\begintt
+\def\centerlist#1{\def\\##1{\relax##1\cr}%
+ \vbox{\halign{\hfil##\hfil\cr#1}}}
+\endtt
+A particular item can be selected by its position number from the left:
+\begintt
+\def\select#1\of#2\to#3{\def#3{\outofrange}%
+ \long\def\\##1{\advance#1-1 \ifnum#1=0 \def#3{##1}\fi}#2}
+\endtt
+(Here |#1| is a count register, |#2| is a list macro, and |#3| is a
+control sequence.) \ And so on; hundreds of other applications can
+be imagined.\footnote\dag{The concept of a list macro is strongly related
+to the concept of a list procedure in a programming language;
+see {\sl Communications of the ACM\/ \bf7} (1964), 280.}
+
+\TeX\ does all of the preceding operations efficiently, in the sense
+that the running time will be proportional to the length of the list
+macro involved. It's natural to ask if the rightmost item can be
+removed with equal efficiency, since the final item of a list is
+somewhat hard to isolate. There is apparently no way to delete the
+$n$th item of an $n$-item list in order~$n$ steps, maintaining
+complete generality, unless the ^|\aftergroup| trick (by which
+we created a macro that expands to $n$~asterisks) is used; and the
+|\aftergroup| trick is somewhat unattractive in the list application,
+because the list might be quite long.\footnote\ddag{The interested reader may
+enjoy constructing a macro that removes the $k$th item of an $n$-item list
+macro~|\l| in $O(n\log n)$ steps, given $k$ and~|\l|,
+without using |\aftergroup|.} However, if we restrict list items to
+unexpandable tokens, it turns out to be possible to remove the
+^^|\xyzzy| rightmost item quite efficiently:
+\begintt
+\def\deleterightmost#1{\edef#1{\expandafter\xyzzy#1\xyzzy}}
+\long\def\xyzzy\\#1#2{\ifx#2\xyzzy\yzzyx
+ \else\noexpand\\{#1}\fi\xyzzy#2}
+\long\def\yzzyx#1\xyzzy\xyzzy{\fi}
+\endtt
+Careful study of this example shows that \TeX's ^{mouth} is capable of
+doing ^{recursive} operations, given sufficiently tricky macros.
+
+The contents of a ^|\count| register can easily be converted to decimal
+and stored in a control sequence; for example, if\/ |\n| is a register,
+`|\edef\csn{\the\n}|' puts its value into~|\csn|. Conversely, a value
+from |\csn| can be put back into |\n| by saying simply `|\n=\csn|'.
+There's usually no point in doing this transformation just to minimize
+the usage of\/ |\count| registers, since \TeX\ has 256 of them; but a
+decimal representation like the expansion of\/ |\csn| can be stored in a
+list macro, and that might be useful in some applications. Incidentally,
+there's a neat way to test if such a control-sequence-number is zero:
+`^|\if||0\csn|\<true text>|\else|\<false text>|\fi|' works because
+extra digits of a nonzero number will be ignored with the \<true text>.
+
+A technique something like list macros can be used to maintain
+unordered sets of control sequences. In this case it's
+convenient to leave off the braces; for example,
+\begintt
+\def\l{\\\alpha\\\beta\\\gamma}
+\endtt
+defines a ``^{set macro}'' |\l| that represents the control
+sequences $\{\,\hbox{|\alpha|},\hbox{|\beta|},\hbox{|\gamma|}\,\}$.
+A straightforward construction tests whether a given control sequence
+is in the set:
+\begintt
+\def\ismember#1\of#2{\resultfalse\def\given{#1}%
+ \def\\##1{\def\next{##1}\ifx\next\given\resulttrue\fi}#2}
+\endtt
+And an efficient but not-so-straightforward construction removes all
+occurrences of control sequences that are
+|\ifx|-equivalent to a given control sequence:
+\begintt
+\def\remequivalent#1\from#2{\let\given=#1%
+ \ifx#2\empty\else\edef#2{\expandafter\plugh#2\plugh}\fi}
+\def\plugh\\#1#2{\ifx#1\given\else\noexpand\\\noexpand#1\fi
+ \ifx#2\plugh\hgulp\fi\plugh#2}
+\def\hgulp\fi\plugh\plugh{\fi}
+\endtt
+
+\subsection ^{Verbatim} listing. Plain \TeX\ includes a macro called
+^|\dospecials| that is essentially a set macro, representing the set
+of all characters that have a special category code. \ (The control
+sequence ^|\do| plays the r\^ole of\/ |\\| in the discussion above.) \
+Therefore it's easy to change all of the special characters to
+category~12~(other):
+\begintt
+\def\uncatcodespecials{\def\do##1{\catcode`##1=12 }\dospecials}
+\endtt
+This works even when the set of special characters has been changed,
+provided that |\dospecials| has been updated to represent the current set.
+
+The operation |\uncatcodespecials| just defined is important, of course,
+when \TeX's automatic features need to be temporarily disabled. Let's
+suppose that we want to create a listing of some computer file,
+reproducing the characters and the spacing exactly as they appear in the
+file. To make the problem more interesting, let's also print line
+numbers in front of each line, as in the listing of |story.tex| on
+page~\storypage. To make the problem simpler, let's assume that the file
+contains only standard ASCII printing characters: no tab marks or
+form feeds or such things. Our goal is to devise a |\listing| macro
+such that, e.g., `|\listing{story}|' will insert a listing of
+the |story.tex| file into a manuscript, after which \TeX's normal
+conventions will be restored. The listing should be in ^|\tt| type.
+A macro of the following form meets the desired specifications:
+\begintt
+\def\listing#1{\par\begingroup\setupverbatim\input#1 \endgroup}
+\endtt
+Notice that the ^|\endgroup| command here will nicely ``turn off'' all
+the weird things that ^|\setupverbatim| turns on. Notice also that the
+commands `|\input#1 \endgroup|' will not be listed verbatim, even though
+they follow |\setupverbatim|, since they entered \TeX's reading mechanism
+when the |\listing| macro was expanded (i.e., before the verbatim business
+was actually set up).
+
+But what should |\setupverbatim| do? Well, it ought to include
+^|\obeylines|, since this automatically inserts a ^|\par| at the end of
+each line that is input; it ought to include ^|\uncatcodespecials|, so
+that special characters print as themselves; and it ought to include
+^|\obeyspaces|, so that each space counts. But we need to look carefully
+at each of these things to see exactly what they do: \
+(1)~Plain \TeX's |\obeylines| macro changes the ^|\catcode| of |^^M|
+to |\active|, and then it says `|\let^^M=\par|'. Since |^^M| is placed
+at the end of each line, this effectively ends each line with |\par|;
+however, |\obeylines| doesn't say `|\def^^M{\par}|', so we must make
+any desired changes to |\par| before invoking |\obeylines|. \
+(2)~The |\uncatcodespecials| operation changes a space to category~12;
+but the |\tt| font has the character `\]' in the \<space> position,
+so we don't really want \]$_{12}$. \
+(3)~The |\obeyspaces| macro in Appendix~B merely changes the \<space>
+character to category~13; ^{active character} \]$_{13}$ has been
+defined to be the same as ^|\space|, a macro that expands to \]$_{10}$.
+This is usually what is desired; for example, it means that spaces in
+constructions like `|\hbox to 10 pt {...}|' won't cause any trouble.
+But in our application it has an undesirable effect, because it
+produces spaces that are affected by the ^{space factor}. To defeat this
+feature, it's necessary either to say ^|\frenchspacing| or to
+redefine \]$_{13}$ to be the same as |\|\]. ^^{control space}
+The latter alternative is better, because the former will discard spaces
+at the beginning of each line.
+
+The |\setupverbatim| macro should also take care of putting a line
+number into the position of the paragraph indentation. We can take care
+of this by introducing a counter variable and using ^|\everypar|, as follows:
+\begintt
+\newcount\lineno % the number of file lines listed
+\def\setupverbatim{\tt \lineno=0
+ \obeylines \uncatcodespecials \obeyspaces
+ \everypar{\advance\lineno by1 \llap{\sevenrm\the\lineno\ \ }}}
+{\obeyspaces\global\let =\ } % let active space = control space
+\endtt
+^^|\llap|
+In theory, this seems like it ought to work; but in practice, it fails
+in two ways. One rather obvious failure---at least, it becomes obvious
+when the macro is tested---is that all the empty lines of the file
+are omitted. The reason is that the |\par| command at the end of an empty
+line doesn't start up a new paragraph, because it occurs in vertical mode.
+The other failure is not as obvious, because it occurs much less often:
+The |\tt| fonts contain ^{ligatures} for Spanish punctuation, so the
+sequences |?||`| and~|!||`| will be printed as {\tt?`} and~{\tt!`}
+respectively. Both of these defects can be cured by inserting
+\begintt
+\def\par{\leavevmode\endgraf} \catcode`\`=\active
+\endtt
+before |\obeylines| in the |\setupverbatim| macro, and by defining |`|$_{13}$
+as follows:
+\begintt
+{\catcode`\`=\active \gdef`{\relax\lq}}
+\endtt
+A similar scheme could be used to produce verbatim listings in other fonts;
+but more characters would have to be made active, in order to break
+ligatures and to compensate for ASCII characters that aren't present.
+
+\def\uncatcodespecials{\def\do##1{\catcode`##1=12 }\dospecials}
+\def\verbatim{\begingroup\tt\uncatcodespecials\obeyspaces\doverbatim}
+\newcount\balance
+{\catcode`<=1 \catcode`>=2 \catcode`\{=12 \catcode`\}=12
+ \gdef\doverbatim{<\balance=1\verbatimloop>
+ \gdef\verbatimloop#1<\def\next<#1\verbatimloop>%
+ \if#1{\advance\balance by1
+ \else\if#1}\advance\balance by-1
+ \ifnum\balance=0\let\next=\endgroup\fi\fi\fi\next>>
+Instead of listing a file verbatim, you might want to define a
+|\verbatim| macro such that `|\verbatim{$this$|{\tt\ is }|{\it!}}|'
+yields `\verbatim{$this$ is {\it!}}'. It's somewhat dangerous to change
+^{category codes}, because \TeX\ stamps the category on each character
+when that character is first read from a file. Thus, if\/ |\verbatim|
+were defined by a construction of the form
+|\long\def\verbatim#1{|\<something>|}|, argument~|#1| would already be
+converted to a list of tokens when \<something> starts; |\catcode| changes
+would not affect the argument. The alternative is to change category
+codes before scanning the argument to |\verbatim|:
+\begintt
+\def\verbatim{\begingroup\tt\uncatcodespecials
+ \obeyspaces\doverbatim}
+\newcount\balance
+{\catcode`<=1 \catcode`>=2 \catcode`\{=12 \catcode`\}=12
+ \gdef\doverbatim{<\balance=1\verbatimloop>
+ \gdef\verbatimloop#1<\def\next<#1\verbatimloop>%
+ \if#1{\advance\balance by1
+ \else\if#1}\advance\balance by-1
+ \ifnum\balance=0\let\next=\endgroup\fi\fi\fi\next>>
+\endtt
+This works; but it's slow, and it allows verbatim setting only of text
+that has balanced braces. It would not be suitable for typesetting
+the examples in a book like {\sl The \TeX book}. \ (Appendix~E contains
+the verbatim macros that were actually used.) \ Note also that if this
+|\verbatim{...}| macro appears in the argument to another macro
+like |\centerline|, it will fail because the category codes can no longer be
+changed. The ^|\footnote| macro in Appendix~B is careful to avoid
+scanning its argument prematurely; it uses ^|\bgroup| and ^|\egroup| in a
+somewhat tricky way, so that category code changes are permitted inside
+plain \TeX\ footnotes.
+
+On the other hand, there is a fairly fast way to convert a token list
+to an almost-verbatim transcript:
+\begintt
+\long\def\verbatim#1{\def\next{#1}%
+ {\tt\frenchspacing\expandafter\strip\meaning\next}}
+\def\strip#1>{}
+\endtt
+Tokens are stripped off in this construction since, for example,
+^|\meaning||\next| might be `\def\next{$this$ is {\it!}}%
+\expandafter|\meaning\next|'. Notice that a space will be inserted after
+the control word |\it|, but no space might actually have occurred there in
+the argument to |\verbatim|; such information has been irretrievably lost.
+
+One of the problems with verbatim mode is that it's hard to stop;
+if we turn off all of \TeX's normal control capabilities, we end up
+``painting ourselves into a corner'' and reaching a point of no return.
+The |\listing| macro was able to solve this problem because the end of
+a file brings an old token list back to life. Another solution would be
+to specify a certain number of lines, after which verbatim mode should
+end. Otherwise it's necessary to put some constraint on the text,
+i.e., to make certain texts unprintable in verbatim mode. For example,
+here's an approach that typesets everything between |\beginverbatim| and
+|\endverbatim|, assuming only that the control sequence |\endverbatim|
+does not need to be set:
+\begintt
+\def\beginverbatim{\par\begingroup\setupverbatim\doverbatim}
+{\catcode`\||=0 \catcode`\\=12 % || is temporary escape character
+ ||obeylines||gdef||doverbatim^^M#1\endverbatim{#1||endgroup}}
+\endtt
+This construction assumes that |\beginverbatim| appears at the end of a
+line in the manuscript file. Argument |#1| will be read entirely into
+\TeX's memory before anything happens, so the total amount of verbatim
+material had better not be too voluminous. Incidentally, it isn't
+necessary to say that this macro is ^|\long|, because the |\par|'s
+inserted by ^|\obeylines| are really |^^M|'s.
+
+Another approach is to keep one character untouchable. For example,
+it's possible to define things so that `|\verbatim|\<char>\<text>\<char>'
+will typeset the \<text> verbatim, where the \<text> is not supposed
+to contain any occurrences of the repeated delimiter \<char>:
+\begintt
+\def\verbatim{\begingroup\setupverbatim\doverbatim}
+\def\doverbatim#1{\def\next##1#1{##1\endgroup}\next}
+\endtt
+
+\subsection Selective loading of macros. Some interesting problems arise
+when a computer system acquires a large library of macro files. For
+example, suppose that a file |macs.tex| contains the lines
+\begintt
+\let\italcorr=\/
+\def\/{\unskip\italcorr}
+\endtt
+because somebody thought it would be nice to allow an optional space
+before \TeX's primitive ^|\/| command. That's fine, except if |macs.tex|
+is input twice; for example, two other macro files might both say
+^|\input|~|macs|. When those lines are processed the second time,
+|\italcorr| will be |\let| equal to a macro that expands to
+`|\unskip\italcorr|',
+and you can guess what will happen: \TeX\ will get into an infinite loop,
+stoppable only by ^{interrupt}ing the program manually.
+^^{recursion, infinite}
+
+Fortunately there's an easy way to prevent this problem, by placing
+a suitable interlock near the beginning of every macro file that
+might introduce such anomalies:
+\begintt
+\ifx\macsisloaded\relax\endinput\else\let\macsisloaded=\relax\fi
+\endtt
+Then |\macsisloaded| will be undefined at the time of the first~|\ifx|,
+but the file will not be read twice. A different control sequence
+should, of course, be used for each file.
+
+Another difficulty with large sets of macros is that they take up space.
+It would be nice to preload every macro that every \TeX\ user has ever
+dreamed up; but there might not be enough room,
+because \TeX's memory capacity is finite. You might find it necessary
+to hold back and to load only the macros that are really needed.
+
+How much memory ^{space} ^^{efficiency} does a macro require?
+Well, there are four kinds of memory involved: token memory,
+name memory, string memory, and character memory. \ (If any of these
+becomes too full, it will be necessary to increase what \TeX\ calls the
+macro memory size, the hash size, the number of strings, and/or the
+pool size, respectively; see Chapter~27.) \ The token memory is
+most important; a macro takes one cell of token memory for each token
+in its definition, including the `|{|' and the `|}|'. For example,
+the comparatively short definition
+\begintt
+\def\example#1\two{\four}
+\endtt
+takes five tokens: |#1|, \cstok{two}, |{|$_1$, \cstok{four}, and |}|$_2$.
+Each control sequence also takes up one cell of name memory, one cell
+of string memory, and as many cells of character memory as there are
+characters in the name (seven in the case of\/ |\example|). Character
+memory is comparatively cheap; four characters, or in some cases five,
+will fit in the same number of bits as a single cell of token memory,
+inside the machine. Therefore you don't save much by choosing short
+macro names.
+
+\TeX\ will tell you how close you come to exceeding its current
+memory capacity if you say ^|\tracingstats||=1|. For example, one of the
+runs that the author made while testing galley proofs of this appendix
+reported the following statistics:
+\begintt
+Here is how much of TeX's memory you used:
+ 209 strings out of 1685
+ 1659 string characters out of 17636
+ 27618 words of memory out of 52821
+ 1172 multiletter control sequences out of 2500
+\endtt
+Consequently there was plenty of room for more macros: $52821-27618=
+25203$ unused cells of main memory, $2500-1172=1328$ of name memory,
+$1685-209=1476$ of string memory, and $17636-1659=15977$ of character memory.
+But a fairly large \TeX\ was being used, and only the macros of
+Appendices B and~E were loaded; in other circumstances it might have
+been necessary to conserve space.
+
+One obvious way to keep from loading too many macros is to keep
+the macro files short and to |\input| only the ones that you need.
+But short files can be a nuisance; sometimes there's a better way.
+For example, let's suppose that a file contains five optional classes
+of macros called |A|, |B|, |C|, |D|,~|E|, and that a typical user
+will probably want only at most two or three of these five; let's
+design a |\load| macro so that, for example,
+`|\load{macs}{AC}|'
+will load file |macs.tex| including options |A| and |C| but not options
+|B|, |D|, or |E|. The following |\load| macro converts its second argument
+into a set~macro called |\options|:
+\begintt
+\def\load#1#2{\let\options=\empty \addoptions#2\end \input#1 }
+\def\addoptions#1{\ifx#1\end \let\next=\relax
+ \else\let\\=\relax\edef\options{\options\\#1}%
+ \let\next=\addoptions \fi \next}
+\endtt
+Inside the file |macs.tex|, a portion of code that should be loaded only
+under option~|B|, say, can be enclosed by `|\ifoption B ... \fi|', where
+|\ifoption| is defined thus:
+\begintt
+\def\ifoption#1{\def\\##1{\if##1#1\resulttrue\fi}%
+ \resultfalse \options \ifresult}
+\endtt
+(This is a simple application of ideas presented earlier in this appendix.)
+
+However, the |\ifoption...\fi| scheme isn't very robust, because it requires
+all of the macros in the optional part to be well nested with
+respect to |\if...| and |\fi|; a macro like |\ifoption| itself couldn't
+easily be defined in such a place! There's a better scheme that also
+runs faster, based on ^{category code} changes. This idea (due to
+Max ^{D\'\i az}) requires that the leftmost nonblank character on each
+line be either `|\|' or~`|{|'; it's usually easy to arrange this.
+Furthermore, one other symbol, say |~|, is reserved. Then the text
+material that is to be loaded only under option~|B| is preceded by
+the line `|\beginoption|~|B|' and followed by a line that says
+`|~endoptionalcode|'. The ^|\catcode| for~|~| is set to~14 (comment
+character), hence the |~endoptionalcode| line will have no effect if
+code is not being skipped. The |\beginoption| macro works like this:
+\begintt
+\def\beginoption#1{\ifoption#1\else\begingroup\swapcategories\fi}
+\def\swapcategories{\catcode`\\=14 \catcode`\{=14 \catcode`\~=0 }
+\let\endoptionalcode=\endgroup
+\catcode`\~=14
+\endtt
+Once the categories have been swapped, all lines will be skipped at high speed
+until the control sequence |~endoptionalcode| is encountered; then
+everything will be restored to its former state. Under this scheme,
+material that should be loaded only under both options |B| and~|D|
+can be prefaced by both `|\beginoption|~|B|' and `|\beginoption|~|D|';
+material that should be loaded under either option~|B| or option~|D|
+(or both) can be prefaced by
+\begintt
+\beginoption B
+~oroption D
+\endtt
+if we define |\oroption#1| to be an abbreviation for
+`|\ifoption#1\endgroup\fi|'.
+
+Another kind of selective loading is sometimes appropriate, based on
+whether or not a particular control sequence is defined. In this
+scheme, if the control sequence is undefined, it should remain
+undefined and it should take up no space whatever in \TeX's memory.
+There's a slick way to do this, namely to say
+\begintt
+\ifx\cs\undefined ... \fi
+\endtt
+(assuming that ^|\undefined| has never been defined). \TeX\ does not
+put undefined control sequences into its internal tables if they follow
+^|\ifx| or if they are encountered while skipping ^{conditional text}.
+You can use this idea, for example, to prepare a bibliography for a paper,
+by reading a suitably arranged bibliography file; only the entries
+that correspond to defined control sequences will be loaded.
+
+\subsection Brace hacks. Several of \TeX's operations depend on
+^{grouping}, and you'll want to know exactly what this means if
+you try to do certain tricky things. For example, plain \TeX's
+control sequences ^|\bgroup| and ^|\egroup| are ``^{implicit braces}''
+because they have been defined by
+\begintt
+\let\bgroup={ \let\egroup=}
+\endtt
+This means that you can include them in the replacement texts of
+definitions without worrying about how they nest; for example, the
+macros
+\begintt
+\def\beginbox{\setbox0=\hbox\bgroup}
+\def\endbox{\egroup\copy0 }
+\endtt
+allow you to make a box between |\beginbox| and |\endbox|; the behavior
+is almost the same as
+\begintt
+\def\beginbox#1\endbox{\setbox0=\hbox{#1}\copy0 }
+\endtt
+but different in three important ways: (1)~The first alternative allows
+category codes to change inside the box. (2)~The first alternative
+is faster, because it doesn't need to scan the box contents both as an
+argument and as a sequence of actual commands. (3)~The first alternative
+takes less memory space, because no argument needs to be stored.
+Thus, the first alternative is usually superior.
+
+For the purposes of this discussion we shall assume that only `|{|'
+has category~1 and that only `|}|' has category~2, although any
+characters can actually be used as group delimiters. ^^{braces}
+Group nesting is crucial during two of \TeX's main activities:
+(a)~when \TeX\ is scanning a ^\<balanced text>, e.g., when \TeX\ is
+forming the replacement text of a macro, a parameter, or a token list
+variable; (b)~when \TeX\ must determine whether the token
+|&|~or ^|\span| ^^{ampersand}
+or ^|\cr| or~^|\crcr| is the end of an entry within an ^{alignment}.
+
+\TeX's mouth has two internal counting mechanisms to deal with nesting:
+The ``master counter'' goes up by~1 for each |{|$_1$ scanned by
+\TeX, and down by~1 for each~|}|$_2$; the ``balance counter'' is
+similar, but it is affected only by explicit |{|$_1$ and |}|$_2$ tokens
+that are actually contributed to a token list that is being formed.
+The master counter decreases by~1 when \TeX\ evaluates the
+^{alphabetic constant} |`{|, and it increases by~1 when \TeX\ evaluates
+|`}|, hence the net change is zero when such constants are evaluated.
+As a consequence of these rules, certain constructions produce
+the following effects:
+$$\halign{\indent\hfil#\hfil&\quad\hfil#\hfil&
+ \quad\hfil#\hfil&\qquad\hfil#\hfil&\quad\hfil#\hfil\cr
+&\it Master counter change\span\omit&\it Balance counter change\span\omit\cr
+\it Input&\it expanded&\it unexpanded&\it expanded&\it unexpanded\cr
+|{|&1&1&1&1\cr
+|\bgroup|&0&0&0&0\cr
+|\iffalse{\fi|&1&1&0&1\cr
+|\ifnum0=`{\fi|&0&1&0&1\cr}$$
+The last two cases produce no begin-group tokens when expanded, but they
+do affect the master counter as shown. Thus, for example,
+\begintt
+\def\eegroup{\ifnum0=`{\fi}}
+\endtt
+makes |\eegroup| behave rather like |\egroup|, but the expansion of\/
+|\eegroup| also decreases the master counter.
+
+Alignment processing uses only the master counter, not the balance counter.
+An alignment entry ends with the first |&|~or |\span| or |\cr| or~|\crcr|
+that appears when the master counter has the value that was present
+in the counter at the beginning of the entry. Thus, for example,
+the curious construction
+\begintt
+\halign{\show\par#\relax\cr
+ \global\let\par=\cr
+ {\global\let\par=\cr}\cr
+ \par}
+\endtt
+causes \TeX\ to perform three |\show| instructions, in which the
+respective values of\/ |\par| shown are |\par|, |\relax|, and~|\cr|.
+Similarly, each template in the preamble to an alignment ends with
+the first |&| or |\cr| or |\crcr| that appears at the master counter level
+that was in effect at the beginning of the entry; hence |&|~and |\cr| and
+|\crcr| tokens can appear within a template of an alignment, if they
+are hidden by braces (e.g., if they appear in a definition).
+
+These facts allow us to draw two somewhat surprising conclusions: (1)~If
+an alignment entry has the form `$\,\alpha\,$^|\iffalse||{\fi|$\,\beta\,$%
+|\iffalse}\fi|$\,\gamma\,$', it's possible for $\beta$ to include
+|&|~and~|\cr| tokens that aren't local to a group.\footnote*{The
+token list $\alpha$ should not be empty, however, because \TeX\ expands
+the first token of an alignment entry before looking at the template,
+in order to see if the entry begins with |\noalign| or |\omit|. The master
+counter value that is considered to be present at the beginning of an
+entry is the value in the counter just after the ``$u$~part'' of the
+template has been entirely read.} (2)~The construction
+\begintt
+{\span\iffalse}\fi
+\endtt
+appearing in a preamble contributes `|{|' to the template without
+any net change to the master counter; thus, it's very much like
+|\bgroup|, except that it produces |{|$_1$ explicitly.
+If you understand (1) and~(2), you'll agree that the present appendix
+deserves its name.
+
+\def\pmb#1{\setbox0=\hbox{#1}%
+ \kern-.025em\copy0\kern-\wd0\kern.05em\copy0
+ \kern-\wd0\kern-.025em\raise.0433em\box0 }
+\subsection ^{Box maneuvers}. Let's turn now from syntax to semantics,
+i.e., from \TeX's mouth to its gastro-intestinal tract. Sometimes an odd
+symbol is needed in boldface type, but it's available only in a normal
+weight. In such cases you can sometimes get by with ``^{poor man's bold},''
+obtained by overprinting the normal weight symbol with slight offsets.
+The following macro typesets its argument three times in three
+slightly different places, equidistant from each other; but the result
+takes up just as much space as if\/ |\pmb| had been simply |\hbox|:
+\begintt
+\def\pmb#1{\setbox0=\hbox{#1}%
+ \kern-.025em\copy0\kern-\wd0
+ \kern.05em\copy0\kern-\wd0
+ \kern-.025em\raise.0433em\box0 }
+\endtt
+For example, `|\pmb{$\infty$}|' yields `\pmb{$\infty$}'. The results
+are somewhat \pmb{fuzzy}, and they certainly are no match for the
+real thing if it's available; but poor man's bold is better than nothing,
+and once in a~while you can get away with it.
+
+When you put something into a box register, you don't need to put
+the contents of that register into your document. Thus, you can write
+macros that do experiments behind the scenes, trying different
+possibilities before making a commitment to a particular decision.
+For example, suppose you are typesetting a text in two languages, and
+you would like to choose the column widths so that the same number of
+lines is obtained in both cases. For example, the following texts
+balance perfectly when the first column is $157.1875\pt$ wide and the
+second column is $166.8125\pt$ wide; but the second column would be
+one line longer than the first if they were both $162\pt$ wide:
+^^{Alice} ^^{Bill}
+\medskip
+\hrule
+\smallskip
+\begingroup
+\eightpoint
+\tolerance=9999
+\hbadness=2300
+\finalhyphendemerits=3000000
+\doublehyphendemerits=1000000
+\parskip=1pt
+\parindent=1.5em
+\frenchspacing
+
+\def\firstcol{ % from Surreal Numbers, pp. 46--47
+\item{A.}\strut The creative part is really more interesting than the
+deductive part. Instead of concentrating just on finding good
+answers to questions, it's more important to learn how to find
+good questions!
+
+\item{B.} You've got something there. I wish our teachers would
+give us problems like, ``Find something interesting about $x$,''
+instead of ``Prove $x$.''
+
+\item{A.}Exactly. But teachers are so conservative, they'd be
+afraid of scaring off the ``grind'' type of students who
+obediently and mechanically do all the homework. Besides, they
+wouldn't like the extra work of grading the answers to
+nondirected questions.
+
+\item{}The traditional way is to put off all creative aspects
+until the last part of graduate school. For seventeen or more
+years, a student is taught exams\-man\-ship, then suddenly after
+passing enough exams in graduate school he's told to do
+something original.
+}
+\def\\#1{\leavevmode\raise.5pt\hbox{$\scriptscriptstyle
+ \ifx#1`\langle\!\langle\else\rangle\!\rangle\fi$}% Spanish quote marks
+ \ifx#1`\nobreak\hskip0pt \fi} % allow hyphenation
+%\showhyphens{\\`Demuestre\\' \\`apisonadora\\' \\`Encuentren\\'}
+\def\secondcol{ % from N\'umeros Surreales, pp. 39--40
+\item{A.} \strut La parte creativa es mucho mejor que la deductiva.
+En vez de concentrarse en buscar buenas respuestas a ciertas
+cuestiones es m\'as importante aprender a proponerse buenas
+preguntas.
+
+\item{B.} Me parece una buena ocurrencia. Me gustar\'\i a
+que los profesores propusieran problemas del estilo de
+\\`Encuentren algo interesante sobre $x$\\' en vez de
+\\`Demuestre que $x\ldots\,$\\'.
+
+\item{A.} Exactamente. Pero los profesores son tan conservadores
+que temer\'\i an espantar al tipo de estudiante \\`apisonadora\\'
+que hace lo que le proponen para casa, obe\-dien\-te\-mente y de forma
+mec\'anica. Adem\'as, no creo que les gustase el trabajo adicional
+de calificar respuestas a preguntas abiertas.
+
+\item{}La forma tradicional es dejar la parte creativa para los cursos
+altos. Durante diecisiete a\~nos o m\'as se ense\~na al es\-tu\-diante a
+aprobar, luego de golpe, cerca de la graduaci\'on, se le pide que haga
+algo original.
+}
+\newif\iffail \newdimen\doublewidth \newdimen\delheight
+\newcount\n
+\newdimen\trialwidth \newdimen\lowwidth \newdimen\highwidth
+\wlog{Beginning the binary search for balanced columns:}
+\def\balancetwocols{\lowwidth=10em % the lowest feasible \trialwidth
+ \highwidth=\doublewidth \advance\highwidth-10em % the highest ditto
+ {\n=1 \hbadness=10000 \hfuzz=\maxdimen
+ \loop \maketrial
+ \wlog{trial \the\n, \the\trialwidth: (\the\ht0,\the\ht2)}
+ \testfailure \iffail \preparenewtrial \repeat}
+ \maketrial} % now under/overfull boxes will be shown
+\def\maketrial{%
+ \trialwidth=.5\lowwidth \advance\trialwidth by.5\highwidth
+ \setbox0=\vbox{\hsize=\trialwidth \firstcol}
+ \setbox2=\vbox{\hsize=\doublewidth
+ \advance\hsize-\trialwidth \secondcol}}
+\def\testfailure{\dimen0=\ht0 \advance\dimen0-\ht2
+ \ifnum\dimen0<0 \dimen0=-\dimen0 \fi
+ \ifdim\dimen0>\delheight \ifnum\n=10 \failfalse\else\failtrue\fi
+ \else\failfalse\fi}
+\def\preparenewtrial{\ifdim\ht0>\ht2 \global\lowwidth=\trialwidth
+ \else\global\highwidth=\trialwidth\fi \advance\n by1 }
+\doublewidth=27pc \delheight=0pt
+\balancetwocols
+\line{\box0\hfil\box2}
+\endgroup
+\smallskip
+\hrule
+\medskip
+Some implementations of \TeX\ display the output as you are running,
+so that you can choose column widths interactively until a suitable
+balance is obtained. It's fun to play with such systems, but it's also
+possible to ask \TeX\ to compute the column widths automatically. The
+following code tries up to ten times to find a solution in which the
+natural heights of the two columns are different by less than
+a given value, |\delheight|. The macros |\firstcol| and
+|\secondcol| are supposed to generate the columns, and the sum
+of column widths is supposed to be |\doublewidth|.
+\beginlines
+|\newdimen\doublewidth \newdimen\delheight \newif\iffail \newcount\n|
+|\newdimen\trialwidth \newdimen\lowwidth \newdimen\highwidth|
+|\def\balancetwocols{\lowwidth=10em % lower bound on \trialwidth|
+| \highwidth=\doublewidth \advance\highwidth-10em % upper bound|
+| {\n=1 \hbadness=10000 \hfuzz=\maxdimen % disable warnings|
+| \loop \maketrial \testfailure \iffail \preparenewtrial \repeat}|
+| \maketrial} % now under/overfull boxes will be shown|
+|\def\maketrial{\trialwidth=.5\lowwidth \advance\trialwidth by.5\highwidth|
+| \setbox0=\vbox{\hsize=\trialwidth \firstcol}|
+| \setbox2=\vbox{\hsize=\doublewidth\advance\hsize-\trialwidth\secondcol}}|
+|\def\testfailure{\dimen0=\ht0 \advance\dimen0-\ht2|
+| \ifnum\dimen0<0 \dimen0=-\dimen0 \fi|
+| \ifdim\dimen0>\delheight \ifnum\n=10 \failfalse\else\failtrue\fi|
+| \else\failfalse\fi}|
+|\def\preparenewtrial{\ifdim\ht0>\ht2 \global\lowwidth=\trialwidth|
+| \else\global\highwidth=\trialwidth\fi \advance\n by1 }|
+\endlines
+Neither column will be less than 10~ems wide. This code does a ``^{binary
+search},'' assuming that a column will not increase in height when it is
+made wider. If no solution is found in 10~trials, there probably is no way
+to obtain the desired balance, because a tiny increase in the width of the
+taller column will make it shorter than the other one. The values of\/
+^|\hbadness| and ^|\hfuzz| are made infinite during the trial settings,
+because warning messages that relate to unused boxes are irrelevant; after
+a solution is found, it is computed again, so that any relevant warnings
+will be issued.
+
+When a box has been put into a box register, you can change its
+height, width, or depth by assigning a new value to the |\ht|, |\wd|,
+or |\dp|. Such assignments don't change anything inside the box;
+in particular, they don't affect the setting of the glue.
+
+But changes to a box's dimensions can be confusing if you don't understand
+exactly how \TeX\ deals with boxes in lists. The rules are stated in
+Chapter~12, but it may be helpful to restate them here in a different way.
+Given a box and the location of its reference point, \TeX\ assigns
+locations to interior boxes as follows: \ (1)~If the box is an hbox,
+\TeX\ starts at the reference point and walks through the horizontal list
+inside. When the list contains a box, \TeX\ puts the reference point of
+the enclosed box at the current position, and moves right by the width of
+that box. When the list contains glue or kerning, etc., \TeX\ moves right
+by the appropriate amount. \ (2)~If the box is a vbox, \TeX\ starts at the
+upper left corner (i.e., \TeX\ first moves up from the reference point, by
+the height of the box) and walks through the vertical list inside. When
+the list contains a box, \TeX\ puts the upper left corner of that
+box at the current position; i.e., \TeX\ moves down by the height of that
+box, then puts the box's reference point at the current position, then
+moves down by the depth of the box. When the list contains glue or
+kerning, etc., \TeX\ moves down by the appropriate amount.
+
+As a consequence of these rules, we can work out what happens when
+the dimensions of a box are changed. Let |\delta| be a \<dimen>
+register, and let |\h| and |\hh| specify horizontal lists that
+don't depend on |\box0|. Consider the following macro:
+\begintt
+\newdimen\temp \newdimen\delta
+\def\twohboxes#1{\setbox1=\hbox{\h \copy0 \hh}
+ \temp=#10 \advance\temp by \delta #10=\temp
+ \setbox2=\hbox{\h \copy0 \hh}}
+\endtt
+For example, |\twohboxes\wd| makes two hboxes, |\box1| and |\box2|,
+that are identical except that the width of\/ |\box0| has been
+increased by $\delta$ in |\box2|. What difference does this make?
+There are several cases, depending on whether |#1| is |\wd|, |\ht|,
+or |\dp|, and depending on whether |\box0| is an hbox or a vbox.
+\ {\it Case~1}, |\twohboxes\wd|: The material from |\hh| is
+moved right by $\delta$ in |\box2|, compared to its position
+in |\box1|. Also |\wd2| is $\delta$ more than~|\wd1|.
+\ {\it Case~2}, |\twohboxes\ht|: If\/ |\box0| is an hbox, everything
+remains in the same position; but if\/ |\box0| is a vbox, everything
+in |\copy0| moves up by $\delta$. Also |\ht2| may differ from |\ht1|.
+\ {\it Case~3}, |\twohboxes\dp|: Everything remains in the
+same position, but |\dp2| may differ from |\dp1|.
+
+Similarly, we can work out the changes when box dimensions are
+changed for boxes within vertical lists. In this case we shall
+ignore the influence of interline glue by defining |\twovboxes| as follows:
+\begintt
+\def\twovboxes#1{
+ \setbox1=\vbox{\v\nointerlineskip\copy0\nointerlineskip\vv}
+ \temp=#10 \advance\temp by \delta #10=\temp
+ \setbox2=\vbox{\v\nointerlineskip\copy0\nointerlineskip\vv}}
+\endtt
+What is the difference between |\box1| and |\box2| now?
+\ {\it Case~1}, |\twovboxes\wd|: Everything remains in the same
+position, but |\wd2| may differ from |\wd1|.
+\ {\it Case~2}, |\twovboxes\ht|: If\/ |\box0| is an hbox, everything
+in |\v| moves up by~$\delta$ in |\box2|, compared to
+the corresponding positions in |\box1|, if we make the reference
+points of the two boxes identical; but if\/ |\box0| is a vbox,
+everything in it moves up by~$\delta$, together with the material
+in |\v|. Also, |\ht2| is $\delta$~more than |\ht1|.
+\ {\it Case~3}, |\twovboxes\dp|:
+If\/ |\vv| is empty, |\dp2| is $\delta$~more than~|\dp1|, and
+nothing else changes. Otherwise everything in |\v| and in |\copy0|
+moves up by~$\delta$, and |\ht2| is $\delta$ more than~|\ht1|.
+%\ (If you don't believe it, try it.)
+
+\newdimen\unit
+\def\point#1 #2 {\rlap{\kern#1\unit
+ \raise#2\unit\hbox{$
+ \scriptstyle\bullet\;(#1,#2)$}}}
+\unit=\baselineskip
+\setbox0=\vtop{\hrule
+ \hbox{\vrule height10\unit depth9.4\unit \kern2\unit
+ \hbox{\unit=\baselineskip
+ \point 0 0 % Alioth (Epsilon Urs\ae\ Majoris), magnitude 1.79
+ \point 0 8 % Dubhe (Alpha Urs\ae\ Majoris), magnitude 1.81
+ \point 0 -8 % Alkaid(Eta Urs\ae\ Majoris), magnitude 1.87
+ \point -1 -2.5 % Mizar (Zeta Urs\ae\ Majoris), magnitude 2.26
+ \point 4 7 % Merak (Beta Urs\ae\ Majoris), magnitude 2.37
+ \point 4 2 % Phekda (Gamma Urs\ae\ Majoris), magnitude 2.44
+ \point 1 1.5 % Megrez (Delta Urs\ae\ Majoris), magnitude 3.30
+ }% Sources: Atlas of the Universe; Astronomy Data Book
+ \kern7\unit \vrule}\hrule}
+\dp0=0pt
+\dimen@=240pt
+\parshape=21
+\z@\hsize \z@\hsize
+\z@\dimen@ \z@\dimen@ \z@\dimen@ \z@\dimen@ \z@\dimen@ \z@\dimen@
+\z@\dimen@ \z@\dimen@ \z@\dimen@ \z@\dimen@ \z@\dimen@ \z@\dimen@
+\z@\dimen@ \z@\dimen@ \z@\dimen@ \z@\dimen@ \z@\dimen@ \z@\dimen@
+\z@\hsize
+\TeX\ is designed to put boxes together either horizontally
+or vertically, not diagonally. But that's not a serious
+limitation, because the use of negative spacing makes it
+possible to put things anywhere on a page. ^^{Ursa Major}
+^^{points with arbitrary coordinates} ^^{coordinates}
+\vadjust{\kern-\ht0\kern-2pt\rightline{\box0}\kern2pt}%
+For example, the seven points in the diagram at the right of this
+paragraph were typeset by saying simply
+\begintt|hsize=|displaywidth
+\hbox{\unit=\baselineskip
+ \point 0 0
+ \point 0 8
+ \point 0 -8
+ \point -1 -2.5
+ \point 4 7
+ \point 4 2
+ \point 1 1.5
+ }
+\endtt
+{\count0=\prevgraf \advance\count0 by7 \prevgraf=\count0}%
+The |\point| macro makes a box of width zero; hence the individual |\point|
+specifications can be given in any order, and there's no restriction on
+the coordinates:
+\begintt|hsize=|displaywidth
+\newdimen\unit
+\def\point#1 #2 {\rlap{\kern#1\unit
+ \raise#2\unit\hbox{$
+ \scriptstyle\bullet\;(#1,#2)$}}}
+\endtt
+^^|\kern|{\count0=\prevgraf \advance\count0 by3 \prevgraf=\count0}%
+If the |\point| specifications are not enclosed in an ^|\hbox|---i.e., if
+they occur in vertical mode---a similar construction can be used. In
+this case |\point| should create a box whose height and depth are zero:
+\begintt
+\def\point#1 #2 {\vbox to0pt{\kern-#2\unit
+ \hbox{\kern#1\unit$\scriptstyle\bullet\;(#1,#2)$}\vss}
+ \nointerlineskip}
+\endtt
+(The ^|\nointerlineskip| is necessary to prevent interline glue from
+messing things up.)
+
+\begingroup
+\let\qc=\manual
+\catcode`\ =9 \endlinechar=-1 % ignore all spaces (temporarily)
+\newcount\dir \newdimen\y \newdimen\w
+\newif\ifvisible \let\B=\visibletrue \let\W=\visiblefalse
+\newbox\NE \newbox\NW \newbox\SE \newbox\SW \newbox\NS \newbox\EW
+\setbox\SW=\hbox{\qc a} \setbox\NW=\hbox{\qc b}
+\setbox\NE=\hbox{\qc c} \setbox\SE=\hbox{\qc d}
+\w=\wd\SW \dimen0=\fontdimen8\qc
+\setbox\EW=\hbox{\kern-\dp\SW \vrule height\dimen0 width\wd\SW} \wd\EW=\w
+\setbox\NS=\hbox{\vrule height\ht\SW depth\dp\SW width\dimen0} \wd\NS=\w
+\def\L{\ifcase\dir \dy+\NW \or\dx-\SW \or\dy-\SE \or\dx+\NE\dd-4\fi \dd+1}
+\def\S{\ifcase\dir \dx+\EW \or \dy+\NS \or \dx-\EW \or \dy-\NS \fi}
+\def\R{\ifcase\dir \dy-\SW\dd+4 \or\dx+\SE \or\dy+\NE \or\dx-\NW\fi \dd-1}
+\def\T{\ifcase\dir\kern-\w\dd+2\or\ey-\dd+2\or\kern\w\dd-2\or\ey+\dd-2\fi}
+\edef\dd#1#2{\global\advance\dir#1#2\space}
+\def\dx#1#2{\ifvisible\raise\y\copy#2 \if#1-\kern-2\w\fi\else\kern#1\w\fi}
+\def\dy#1#2{\ifvisible\raise\y\copy#2 \kern-\w \fi \global\advance\y#1\w}
+\def\ey#1{\global\advance\y#1\w}
+\def\path#1{\hbox{\B \dir=0 \y=0pt #1}}
+\catcode`\ =10 \endlinechar='15 % resume normal spacing conventions
+
+If you enjoy fooling around making pictures, instead of typesetting
+ordinary text, \TeX\ will be a source of endless frustration/amusement
+for you, because almost anything is possible if you have suitable fonts.
+For example, suppose you have a font |\qc| that contains four ^{quarter
+circles}:
+\begindisplay
+\def\\#1{\kern\dp#1\copy#1\kern-\dp#1}
+\tt a = \\\SW \qquad b = \\\NW \qquad c = \\\NE \qquad d = \\\SE
+\enddisplay
+Each of these characters has the same height, the same width, and the same
+depth; the width and the height-plus-depth are equal to the diameter of
+the corresponding full circle. Furthermore, the reference point of each
+character is in a somewhat peculiar place: Each quarter arc has a
+horizontal endpoint such that the lower edge of the curve is at the
+baseline, and a vertical endpoint such that the left edge is directly
+above or below the reference point. This convention makes it possible to
+guarantee perfect alignment between these characters and rules that meet
+them at the endpoints; the thickness of such rules should be
+^|\fontdimen||8\qc|.
+
+Given those characters, it's possible to devise macros |\path|, |\L|,
+^^{turtle commands}
+|\R|, |\S|, and~|\T| such that ^|\path||{|$\langle$any string of\/ |\L|'s, |\R|'s,
+|\S|'s, and |\T|'s$\rangle$|}| produces a path that starts traveling East,
+but it turns left for each~|\L|, right for each~|\R|, goes straight
+for each~|\S|, and turns backward for each~|\T|. Thus, for example,
+%|\path{\S\T\R\R\R\L\S\T\R\R\R\L}| yields `\kern\dp\SW
+%\smash{\path{\S\T\R\R\R\L\S\T\R\R\R\L}}\kern-\dp\SW',
+|\path{\L\T\S\T\R\L\T\S\T\R}| yields `\kern\dp\SW
+\smash{\path{\L\T\S\T\R\L\T\S\T\R}}\kern-\dp\SW', and you
+can also get the following effects:
+\def\sm#1{\smash{$\vcenter{#1}$}}%
+\begindisplay
+\noalign{\kern5pt}
+|\path{\L\R\S\R\S\R\S\S\R\R}|\kern30pt
+ \sm{\path{\L\R\S\R\S\R\S\S\R\R}}\cr
+\noalign{\kern16pt}
+|\path{\R\R\R\R\T\S\S\L\L\L\L\L\S\S}|\kern47pt
+ \sm{\path{\R\R\R\R\T\S\S\L\L\L\L\L\S\S}}\cr
+\noalign{\kern16pt}
+|\def\X{\L\T\L\L\T\L\L\T} \path{\X\X\X\X}|\kern20pt
+ \sm{\def\X{\L\T\L\L\T\L\L\T} \path{\X\X\X\X}}\cr
+\noalign{\kern10pt}
+%|\path{\S\S\S\S\L\R\R\L\S\R\R\L\R\R\L\S\S\S\L\R\R\L\R\R}|\kern20pt
+% \sm{\path{\S\S\S\S\L\R\R\L\S\R\R\L\R\R\L\S\S\S\L\R\R\L\R\R}}\cr
+\enddisplay
+Furthermore, there are operations |\B| and |\W| that make the path
+black (visible) and white (invisible), respectively:
+\begindisplay \openup1pt
+\noalign{\kern14pt}
+|\path{\R\R\S|\cr
+| \W\S\S\S\R\R|\cr
+| \B\R\R\S\R\S\R\S\S\S\R\S\S\S\S\S\R\S\R|
+ \kern100pt\raise4pt\hbox{\sm{\path{\R\R\S
+ \W\S\S\S\R\R
+ \B\R\R\S\R\S\R\S\S\S\R\S\S\S\S\S\R\S\R
+ \W\R\R\R\S\L\S
+ \B\L\S\S\S\S}}}\cr
+| \W\R\R\R\S\L\S|\cr
+| \B\L\S\S\S\S}|\cr
+\noalign{\kern15pt}
+\enddisplay
+(It may be necessary to put kerns before and after the path, since the
+box produced by |\path| may not be as wide as the actual path itself.)
+
+The |\path| macros work differently from |\point|, since the boxes need
+not have zero width in this application:
+\beginlines
+^|\catcode||`\ =9 |^|\endlinechar||=-1 % ignore all spaces (temporarily)|
+|\newcount\dir \newdimen\y \newdimen\w|
+|\newif\ifvisible \let\B=\visibletrue \let\W=\visiblefalse|
+|\newbox\NE \newbox\NW \newbox\SE \newbox\SW \newbox\NS \newbox\EW|
+|\setbox\SW=\hbox{\qc a} \setbox\NW=\hbox{\qc b}|
+|\setbox\NE=\hbox{\qc c} \setbox\SE=\hbox{\qc d}|
+|\w=\wd\SW \dimen0=\fontdimen8\qc|
+|\setbox\EW=\hbox{\kern-\dp\SW \vrule height\dimen0 width\wd\SW} \wd\EW=\w|
+|\setbox\NS=\hbox{\vrule height\ht\SW depth\dp\SW width\dimen0} \wd\NS=\w|
+|\def\L{\ifcase|%
+ |\dir \dy+\NW \or\dx-\SW \or\dy-\SE \or\dx+\NE\dd-4\fi \dd+1}|\kern-2pt
+|\def\S{\ifcase\dir \dx+\EW \or \dy+\NS \or \dx-\EW \or \dy-\NS \fi}|
+|\def\R{\ifcase|%
+ |\dir \dy-\SW\dd+4 \or\dx+\SE \or\dy+\NE \or\dx-\NW\fi \dd-1}|\kern-2pt
+|\def\T{|^|\ifcase|%
+ |\dir\kern-\w\dd+2\or\ey-\dd+2\or\kern\w\dd-2\or\ey+\dd-2\fi}|\kern-2pt
+\goodbreak
+|\edef\dd#1#2{\global\advance\dir#1#2\space}|
+|\def\dx#1#2|%
+ |{\ifvisible\raise\y\copy#2 \if#1-\kern-2\w\fi\else\kern#1\w\fi}|\kern-2pt
+|\def\dy#1#2{\ifvisible\raise\y\copy#2 \kern-\w \fi \global\advance\y#1\w}|
+|\def\ey#1{\global\advance\y#1\w}|
+|\def\path#1{\hbox{\B \dir=0 \y=0pt #1}}|
+|\catcode`\ =10 |^|\endlinechar||=`\^^M % resume normal spacing conventions|
+\smallskip
+|\newcount\n % the current order in the \dragon and \nogard macros|
+|\def\dragon{\ifnum\n>0{\advance\n-1 \dragon\L\nogard}\fi}|
+|\def\nogard{\ifnum\n>0{\advance\n-1 \dragon\R\nogard}\fi}|
+\endlines
+(The last three lines are not part of the |\path| macros, but they can
+be used as an interesting test case. To get the famous ``^{dragon curve}''
+of order~9, all you have to say is `|\path{\dir=3 \n=9 \dragon}|'.)^^{recursion}
+\endgroup
+
+\smallbreak
+Let's turn now to another box-oriented problem. The ^|\listing| macro
+discussed earlier in this appendix was restricted to listing files
+that contain only visible ASCII characters. Sometimes it's desirable to
+deal with ASCII ^\<tab> marks too, where a \<tab> is equivalent to
+1~or~2~or $\cdots$ or~8 spaces (whatever is necessary to make the
+current line length a multiple of~8). How can this be done?
+
+We shall assume that files can contain a special symbol that \TeX\ will
+input as character number~9, the ASCII \<tab> code; some implementations
+can't actually do this. If a file contains the three symbols |^^I|, plain
+\TeX\ will normally input them as a single character, number~9; but in a
+verbatim listing of the file we naturally want such symbols to print as
+themselves, i.e., as |^^I|.
+
+The following construction redefines |\setupverbatim| so that the previous
+|\listing| macro will work with \<tab> characters. The idea is to keep the
+line-so-far in an hbox, which can be ``measured'' in order to find out how
+many characters have appeared since the beginning of the line or since the
+most recent \<tab>.
+\begintt
+\def\setupverbatim{\tt \lineno=0
+ \def\par{\leavevmode\egroup\box0\endgraf}
+ \obeylines \uncatcodespecials \obeyspaces
+ \catcode`\`=\active \catcode`\^^I=\active
+ \everypar{\advance\lineno by1
+ \llap{\sevenrm\the\lineno\ \ }\startbox}}
+\newdimen\w \setbox0=\hbox{\tt\space} \w=8\wd0 % tab amount
+\def\startbox{\setbox0=\hbox\bgroup}
+{\catcode`\^^I=\active
+ \gdef^^I{\leavevmode\egroup
+ \dimen0=\wd0 % the width so far, or since the previous tab
+ \divide\dimen0 by\w
+ \multiply\dimen0 by\w % compute previous multiple of \w
+ \advance\dimen0 by\w % advance to next multiple of \w
+ \wd0=\dimen0 \box0 \startbox}}
+\endtt
+^^|\divide|^^|\multiply|
+(The new things in |\setupverbatim| are the `|\egroup\box0|' in the
+redefinition of\/ |\par|; the `|\catcode`\^^I=\active|'; and the
+`|\startbox|' in |\everypar|.) \ The |\settabs| and~|\+| macros of
+Appendix~B provide another example of how tab operations can be simulated
+by boxing and unboxing.
+
+\smallbreak
+\def\beginvrulealign{\setbox0=\vbox\bgroup}
+\def\endvrulealign{\egroup % now \box0 holds the alignment}
+ \setbox0=\vbox{\setbox2=\hbox{\vrule height\ht0 depth\dp0 width0pt}
+ \unvbox0 \setbox0=\lastbox % now \box0 is the bottom row
+ \nointerlineskip \copy0 % put it back
+ \global\setbox1=\hbox{}
+ \setbox4=\hbox{\unhbox0
+ \loop \skip0=\lastskip \unskip % remove tabskip glue
+ \advance\skip0 by-.4pt % rules are .4pt wide
+ \divide\skip0 by 2
+ \global\setbox1=\hbox{\hskip\skip0\vrule\hskip\skip0
+ \unhbox2\unhbox1}%
+ \setbox2=\lastbox % remove alignment entry
+ \ifhbox2 \setbox2=\hbox{\kern\wd2}\repeat}}%
+ \hbox{\rlap{\box0}\box1}}
+\setbox8=\vbox{
+\beginvrulealign
+\tabskip=10pt
+\halign{&\strut#\hfil\cr
+These& after\cr
+vertical& the\cr
+rules& alignment\cr
+were& was\cr
+inserted& completed!\cr}
+\endvrulealign
+}
+Chapter 22 explains how to put vertical ^{rules in tables} by considering the
+rules to be separate columns. There's also another way, provided that
+the rules extend all the way from the top of the table to the bottom.
+For example,
+\begindisplay
+|\beginvrulealign|\cr
+|\tabskip=10pt|\cr
+|\halign{&\strut#\hfil\cr|\cr
+|These& after\cr|\cr
+|vertical& the\cr|\kern70pt yields\qquad\smash{$\vcenter{\box8}$}\cr
+|rules& alignment\cr|\cr
+|were& was\cr|\cr
+|inserted& completed!\cr}|\cr
+|\endvrulealign|\cr
+\enddisplay
+The magic macros in this case examine the bottom row of the
+^{alignment}, which consists of alternating ^{tabskip glue} and boxes;
+each item of tabskip glue in that bottom row will be bisected by
+a vertical rule. Here's how:
+\beginlines
+|\def\beginvrulealign{\setbox0=\vbox\bgroup}|
+|\def\endvrulealign{\egroup % now \box0 holds the entire alignment|
+| \setbox0=\vbox{\setbox2=\hbox{\vrule height\ht0 depth\dp0 width0pt}|
+| |^|\unvbox||0 \setbox0=\lastbox % now \box0 is the bottom row|
+| \nointerlineskip \copy0 % put it back|
+| \global\setbox1=\hbox{} % initialize box that will contain rules|
+| \setbox4=\hbox{\unhbox0 % now open up the bottom row|
+| \loop \skip0=|^|\lastskip|| |^|\unskip|| % remove tabskip glue|
+| \advance\skip0 by-.4pt % rules are .4pt wide|
+| \divide\skip0 by 2|
+| \global\setbox1=\hbox{\hskip\skip0\vrule\hskip\skip0|
+| \unhbox2\unhbox1}%|
+| \setbox2=|^|\lastbox|| % remove alignment entry|
+| \ifhbox2 \setbox2=\hbox{\kern\wd2}\repeat}}%|
+| \hbox{\rlap{\box0}\box1}} % superimpose the alignment on the rules|
+\endlines
+This method works with all alignments created by ^|\halign||{...}|. For
+alignments created by, say, |\halign to100pt{...}|, the method works
+only if the bottom row of the alignment contains all of the columns,
+and only if `|\box1|' is replaced by `|\hbox to100pt{\unhbox1}|'
+at the end of\/ |\endvrulealign|.
+
+\begingroup
+\eightpoint
+\hyphenpenalty10000 \exhyphenpenalty10000 \pretolerance10000 % no hyphens
+\newbox\dbox \setbox\dbox=\hbox to .4em{\hss.\hss} % dot box for leaders
+\newskip\rrskipb \rrskipb=.5em plus3em % ragged right space before break
+\newskip\rrskipa \rrskipa=-.17em plus-3em minus.11em % ditto, after
+\newskip\rlskipa \rlskipa=0pt plus3em % ragged left space after break
+\newskip\rlskipb \rlskipb=.33em plus-3em minus .11em % ditto, before
+\newskip\lskip \lskip=3.3\wd\dbox plus1fil minus.3\wd\dbox % for leaders
+\newskip\lskipa \lskipa=-2.67em plus-3em minus.11em % after leaders
+\mathchardef\rlpen=1000 \mathchardef\leadpen=600 % constants used
+\def\rrspace{\nobreak\hskip\rrskipb\penalty0\hskip\rrskipa}
+\def\rlspace{\penalty\rlpen\hskip\rlskipb\vadjust{}\nobreak\hskip\rlskipa}
+\uccode`~=` \uppercase{
+ \def\:{\nobreak\hskip\rrskipb \penalty\leadpen \hskip\rrskipa
+ \vadjust{}\nobreak\leaders\copy\dbox\hskip\lskip
+ \kern3em \penalty\leadpen \hskip\lskipa
+ \vadjust{}\nobreak\hskip\rlskipa \let~=\rlspace}
+ \everypar{\hangindent=1.5em \hangafter=1 \let~=\rrspace}}
+\parindent=0pt
+\parfillskip=0pt
+\obeyspaces
+\def\text{ACM Symposium on Theory of Computing, Eighth Annual (Hershey, %
+Pa., 1976)\:1879, 4813, 5414, 6918, 6936, 6937, 6946, 6951, %
+6970, 7619, 9605, 10148, 11676, 11687, 11692, 11710, 13869}
+\catcode`\ =10
+\def\test#1 {\vbox{\hsize=#1em\hrule\strut\text\strut\par\hrule}}
+\global\setbox5=\test 12.5
+\global\setbox7=\test 17.5
+%\global\setbox9=\test 22.5
+\global\setbox9=\test 30
+\endgroup
+\subsection Paragraph maneuvers. Chapter 14 promised that Appendix D would
+present an example where ^{ragged right} and ^{ragged left} setting
+occur in the same paragraph. The following interesting example was
+suggested by the ``^{Key Index}'' in {\sl ^{Mathematical Reviews}},
+^^{index macros} where the entries consist of a possibly long title
+followed by dot ^{leaders} followed by a possibly long list of
+review numbers. If the title doesn't fit on one line, it should be set
+ragged right, with hanging indentation on all lines after the first;
+if the references don't all fit on one line, they should be set
+ragged left. For example, given the input
+\begintt
+ACM Symposium on Theory of Computing, Eighth Annual (Hershey, %
+Pa., 1976)\:1879, 4813, 5414, 6918, 6936, 6937, 6946, 6951, %
+6970, 7619, 9605, 10148, 11676, 11687, 11692, 11710, 13869
+\endtt
+the following three types of output are desired, depending on the
+column width:
+\begindisplay
+\box5&\box7\cr
+\noalign{\vskip6pt}
+\box9\span\cr
+\enddisplay
+Notice that the dot leaders are treated in three different ways, depending
+on which works out best: They may occur at the left of the first line after
+the title, or they may appear at the end of the last line of the title
+(in which case they stop well before the right margin), or they may occur
+in the middle of a line. Furthermore, the ragged-right lines are supposed
+to end at least $0.5\em$ from the right margin. Our goal is to achieve
+all this as a special case of \TeX's general paragraphing method. The
+simple approach of Appendix~B won't work, because |\raggedright| is
+achieved there by adjusting ^|\rightskip|; \TeX\ uses the same |\rightskip|
+value in all lines of a paragraph.
+
+The solution to this problem requires an understanding of the ^{line-breaking}
+algorithm; it depends on how demerits are calculated, and on how items
+are removed at the breakpoints, so the reader should review Chapter~14
+until those concepts are firmly understood. Basically, we need to
+specify a sequence of box/glue/penalty items for the spaces in the
+title portion, another sequence for the spaces in the reference portion,
+and another sequence for the dot leaders. In the title portion of each
+index entry, interword spaces can be represented by the sequence
+\begintt
+\penalty10000 \hskip.5em plus3em \penalty0
+\hskip-.17em plus-3em minus.11em
+\endtt
+Thus, there is a stretchability of $3\em$ if a line break occurs
+at the |\penalty0|; otherwise the net interword space will be $.33\em$,
+shrinkable to $.22\em$. This gives ragged right margins. The interword
+spaces in the reference portion are designed to produce ragged left margins
+and to minimize the number of lines devoted to references:
+\begintt
+\penalty1000 \hskip.33em plus-3em minus.11em
+\vadjust{}\penalty10000 \hskip0pt plus3em
+\endtt
+The ^|\vadjust||{}| does nothing, but it doesn't disappear at a line break.
+Thus, if a break occurs at the |\penalty1000|, the following line will
+begin with stretchability $3\em$; but if no break occurs, the net space
+will be $.33\em$ minus $.11\em$. Finally, the transition between title
+and references can be specified by
+\begintt
+\penalty10000 \hskip.5em plus3em \penalty600
+\hskip-.17em plus-3em minus.11em
+\vadjust{}\penalty10000
+\leaders\copy\dbox\hskip3.3\wd\dbox plus1fil minus.3\wd\dbox
+\kern3em \penalty600 \hskip-2.67em plus-3em minus.11em
+\vadjust{}\penalty10000 \hskip0pt plus3em
+\endtt
+(Quite a mouthful.) \
+This long sequence of penalty and glue items begins rather like the interword
+spaces in the first part, and it ends rather like the interword spaces in
+the last part. It has two permissible breakpoints, namely at the
+`|\penalty600|' items. The first breakpoint causes the leaders to
+appear at the beginning of a line; the second causes them to appear
+at the end, but 3~ems away. The leader width will always be at least
+three times the width of\/ |\dbox|, so at least two copies of\/ |\dbox|
+will always appear. Here is the actual \TeX\ code that can be used
+to set up the desired behavior:
+\beginlines
+|\hyphenpenalty10000 \exhyphenpenalty10000 |^|\pretolerance||10000 % no hyphens|
+^|\newbox||\dbox \setbox\dbox=\hbox to .4em{\hss.\hss} % dot box for leaders|
+^|\newskip||\rrskipb \rrskipb=.5em plus3em % ragged right space before break|
+|\newskip\rrskipa \rrskipa=-.17em plus-3em minus.11em % ditto, after|
+|\newskip\rlskipa \rlskipa=0pt plus3em % ragged left space after break|
+|\newskip\rlskipb \rlskipb=.33em plus-3em minus .11em % ditto, before|
+|\newskip\lskip \lskip=3.3\wd\dbox plus1fil minus.3\wd\dbox % for leaders|
+|\newskip\lskipa \lskipa=-2.67em plus-3em minus.11em % after leaders|
+^|\mathchardef||\rlpen=1000 \mathchardef\leadpen=600 % constants used|
+|\def\rrspace{|^|\nobreak||\hskip\rrskipb\penalty0\hskip\rrskipa}|
+|\def\rlspace{|%
+ |\penalty\rlpen\hskip\rlskipb\vadjust{}\nobreak\hskip\rlskipa}|\kern-2pt
+|\uccode`~=` \uppercase{|
+| \def\:{\nobreak\hskip\rrskipb \penalty\leadpen \hskip\rrskipa|
+| \vadjust{}\nobreak\leaders\copy\dbox\hskip\lskip|
+| \kern3em \penalty\leadpen \hskip\lskipa|
+| \vadjust{}\nobreak\hskip\rlskipa \let~=\rlspace}|
+| \everypar{\hangindent=1.5em \hangafter=1 \let~=\rrspace}}|
+|\uccode`~=0 |^|\parindent||=0pt |^|\parfillskip||=0pt |^|\obeyspaces|
+\endlines
+Putting the interword glue into |\skip| registers saves a great deal
+of time and memory space when \TeX\ works with such paragraphs;
+`|\hskip|\<explicit glue>' occupies six cells of \TeX's ^{box memory},
+but `|\hskip|\<skip register>' occupies only two. ^^{efficiency}
+Notice the tricky use of\/ ^|\uppercase| here to convert |~|$_{13}$ into
+\]$_{13}$; ^^{active spaces} ``random'' ^{active characters} can be
+obtained in a similar way.
+
+\medbreak
+Let's turn now to a much simpler problem: {\sl^{hanging punctuation}}.
+\medskip
+\setbox0=\vbox{\vfil
+\hyphenpenalty=10000
+\eightpoint
+\hsize=14pc
+\newdimen\commahang \setbox0=\hbox{,} \commahang=\wd0
+\newdimen\periodhang \setbox0=\hbox{.} \periodhang=\wd0
+\newdimen\quotehang \setbox0=\hbox{`} \quotehang=\wd0
+\newdimen\qquotehang \setbox0=\hbox{``} \qquotehang=\wd0
+\newskip\zzz \def\allowhyphens{\nobreak\hskip\zzz}
+\def\lqq{``} \def\rqq{''} \def\pnt{.}
+\def\comma{,\kern-\commahang\kern\commahang}
+\def\period{.\kern-\periodhang\kern\periodhang}
+\def\rquote{'\kern-\quotehang\kern\quotehang}
+\def\lquote{\ifhmode\kern\quotehang\vadjust{}\else\leavevmode\fi
+ \kern-\quotehang`\allowhyphens}
+\catcode`,=\active \let,=\comma
+\catcode`.=\active \let.=\period
+\catcode`'=\active \def'{\futurelet\next\rqtest}
+\catcode``=\active \def`{\futurelet\next\lqtest}
+\def\rqtest{\ifx\next'\let\next=\rquotes\else\let\next=\rquote\fi\next}
+\def\lqtest{\ifx\next`\let\next=\lquotes\else\let\next=\lquote\fi\next}
+\def\rquotes'{\rqq\kern-\qquotehang\kern\qquotehang}
+\def\lquotes`{\ifhmode\kern\qquotehang\vadjust{}\else\leavevmode\fi
+ \kern-\qquotehang\lqq\allowhyphens}
+\parindent=0pt
+``What is hanging punctuation?'' asked Alice, with a puzzled frown.
+`Well, y'know, actually,' answered Bill, `I'd rather demonstrate it than
+explain it.' ``Oh, now I see. Commas, periods, and quotes are allowed to
+stick out into the margins, if they occur next to a line break.'' `Yeah, I
+guess.' ``Really! But why do all your remarks have single quotes, while
+mine are double?'' `I haven't the foggiest; it's weird. Ask the author of
+this crazy book.'
+}\setbox2=\vsplit0 to.5\ht0 \line{\box2\hfil\box0}
+\medskip\noindent
+Each comma in Alice and Bill's demonstration paragraph
+was represented inside of \TeX\ by
+the sequence of three items `|,\kern-\commahang|^|\kern||\commahang|',
+and there were similar replacements for periods and for closing quotes;
+opening quotes were represented by the longer sequence
+\begintt
+\kern\qquotehang\vadjust{}\kern-\qquotehang``\allowhyphens
+\endtt
+where ^|\allowhyphens| allows the following word to be ^{hyphenate}d.
+This construction works because kerns disappear into ^{line breaks} in the
+proper way; the relevant rules from Chapter~14 are: (1)~A line break can
+occur at a kern that is immediately followed by glue. (2)~Consecutive
+glue, kern, and penalty items disappear at a break.
+
+To set \TeX\ up for hanging punctuation, you can say
+\beginlines
+|\newdimen\commahang \setbox0=\hbox{,} \commahang=\wd0|
+|\newdimen\periodhang \setbox0=\hbox{.} \periodhang=\wd0|
+|\newdimen\quotehang \setbox0=\hbox{`} \quotehang=\wd0|
+|\newdimen\qquotehang \setbox0=\hbox{``} \qquotehang=\wd0|
+|\newskip\zzz \def\allowhyphens{\nobreak\hskip\zzz}|
+|\def\lqq{``} \def\rqq{''} \def\pnt{.}|
+|\def\comma{,\kern-\commahang\kern\commahang}|
+|\def\period{.\kern-\periodhang\kern\periodhang}|
+|\def\rquote{'\kern-\quotehang\kern\quotehang}|
+|\def\lquote{\ifhmode\kern\quotehang\vadjust{}\else\leavevmode\fi|
+| \kern-\quotehang`\allowhyphens}|
+|\catcode`,=\active \let,=\comma \catcode`.=\active \let.=\period|
+|\catcode`'=\active \def'{\futurelet\next\rqtest}|
+|\catcode``=\active \def`{\futurelet\next\lqtest}|
+|\def\rqtest{\ifx\next'\let\next=\rquotes\else\let\next=\rquote\fi\next}|
+|\def\lqtest{\ifx\next`\let\next=\lquotes\else\let\next=\lquote\fi\next}|
+|\def\rquotes'{\rqq\kern-\qquotehang\kern\qquotehang}|
+|\def\lquotes`{\ifhmode\kern\qquotehang\vadjust{}\else\leavevmode\fi|
+| \kern-\qquotehang\lqq\allowhyphens}|
+\endlines
+Notice that the macros need to do their own checking for ligatures, and
+they also take appropriate actions when a paragraph begins with an opening
+quote. Since |\kern| does not affect the ^{space factor}, hanging
+punctuation doesn't affect \TeX's spacing conventions within a line.
+Partially hanging punctuation can be obtained by decreasing the amounts of\/
+|\commahang|, etc. The macros ^|\pnt|, ^|\lq|, and ^|\rq| should be used
+in constants; for example, a dimension of $6.5\,$in must be written
+`|6\pnt5in|' when hanging punctuation is in effect, and
+`|\catcode\lq,=12|' makes commas inactive again. A special font with
+zero-width ^|\hyphenchar| should be used for ``hanging hyphenation.''
+
+\medbreak
+And now for our next trick, let's consider an application to short footnotes.
+^^{footnotes, short} The footnotes at the bottom of this page%
+\footnote{$^1$}{First footnote.}$^,$%
+\footnote{$^2$}{Second footnote. (Every once in a~while a long
+ footnote might occur, just to make things difficult.)}$^,$%
+\footnote{$^3$}{Third footnote.}$^,$%
+\footnote{$^4$}{Fourth footnote.}$^,$%
+\footnote{$^5$}{Fifth footnote. (This is incredibly boring,
+ but it's just an example.)}$^,$%
+\footnote{$^6$}{Another.}$^,$%
+\footnote{$^7$}{And another.}$^,$%
+\footnote{$^8$}{Ho hum.}$^,$%
+\footnote{$^9$}{Umpteenth footnote.}$^,$%
+\footnote{$^{10}$}{Oodles of them.}
+look funny, because most of them are quite short. When a document has lots
+of footnotes, and when most of them take up only a small part of a line,
+the output routine ought to reformat them in some more appropriate way.
+
+\newcount\footno
+For example, one approach would be to typeset the footnotes in narrow
+columns and to put, say, three columns of footnotes at the bottom of
+each page. The ten example footnotes might then look like this:
+\medskip
+\begingroup
+\def\vfootnote#1{\insert\footins{
+ \eightpoint \hsize=9pc \parindent=1pc
+ \leftskip=0pt \raggedright \pretolerance=10000
+ \interlinepenalty=\interfootnotelinepenalty
+ \floatingpenalty=20000
+ \splittopskip=\ht\strutbox \splitmaxdepth=\dp\strutbox
+ \global\advance\footno by 1
+ \item{$^{\the\footno}$}\strut#1\strut\par\allowbreak}}
+\output={\showboxbreadth=9999 \showboxdepth=1
+ \nonstopmode \showbox\footins \errorstopmode
+ \rigidbalance\footins 3 7pt
+ \global\setbox1=\lastbox
+ \unvbox255}
+\footno=0
+\vfootnote{First footnote.}
+\vfootnote{Second footnote. (Every once in a~while a long
+ footnote might occur, just to make things difficult.)}
+\vfootnote{Third footnote.}
+\vfootnote{Fourth footnote.}
+\vfootnote{Fifth footnote.
+ (This is incredibly boring, but it's just an example.)}
+\vfootnote{Another.}
+\vfootnote{And another.}
+\vfootnote{Ho hum.}
+\vfootnote{Umpteenth footnote.}
+\vfootnote{Oodles of them.}
+\newcount\k \newdimen\h
+\def\rigidbalance#1#2 #3 {\setbox0=\box#1 \k=#2 \h=#3
+ \line{\splittopskip=\h \vbadness=10000 \hfilneg
+ \valign{##\vfil\cr\dosplits}}}
+\def\dosplits{\ifnum\k>0 \noalign{\hfil}\splitoff
+ \global\advance\k-1\cr\dosplits\fi}
+\def\splitoff{\dimen0=\ht0 \divide\dimen0 by\k \advance\dimen0 by\h
+ \vsplit0 to \dimen0 }
+\eject
+\box1\endgroup
+\medskip\noindent
+In this case, the footnotes could be generated by
+\begindisplay
+|\insert\footins{\eightpoint \hsize=9pc \parindent=1pc|\cr
+| \leftskip=0pt |^|\raggedright|| \pretolerance=10000|\cr
+| \hyphenpenalty=10000 \exhyphenpenalty=10000|\cr
+| \interlinepenalty=\interfootnotelinepenalty|\cr
+| \floatingpenalty=20000|\cr
+| \splittopskip=\ht\strutbox \splitmaxdepth=\dp\strutbox|\cr
+| \item{$^{\the\footno}$}\strut|\<text of footnote>^|\strut|\cr
+| \par|^|\allowbreak||}|\cr
+\enddisplay
+and |\count\footins| would be set to 333 so that each footnote line
+would be considered to occupy about one third of a line on the page.
+The output routine would then see a |\box\footins| that looks like
+this:
+\begindisplay
+|\vbox(142.0+2.0)x108.0|\cr
+|.\hbox(7.0+2.0)x108.0, glue set 42.23425fil []|\cr
+|.\penalty 0|\cr
+|.\hbox(7.0+2.0)x108.0, glue set 0.29266 []|\cr
+|.\penalty 250|\cr
+|.\glue(\baselineskip) 1.44444|\cr
+|.\hbox(5.55556+1.55556)x96.0, glue set 0.8693, shifted 12.0 []|\cr
+|.\penalty 100|\cr
+|.\glue(\baselineskip) 1.88889|\cr
+|.\hbox(5.55556+1.55556)x96.0, glue set 0.92438, shifted 12.0 []|\cr
+\qquad\vdots\cr
+|.\hbox(7.0+2.0)x108.0, glue set 18.56308fil []|\cr
+|.\penalty 0|\cr
+|.\hbox(7.0+2.0)x108.0, glue set 36.92476fil []|\cr
+|.\penalty 0|\cr
+\enddisplay
+The individual footnotes each end with `|\penalty 0|'; footnotes that
+take up more than one line have larger penalties between the lines,
+and interline glue appears there too.
+
+How should the output routine break such a box up into three roughly
+equal pieces? Notice that the contents of the box are completely rigid;
+i.e., there is no glue that can stretch or shrink. Furthermore, we can
+assume that the contents of the box are regular, i.e., that the
+inter-baseline distances are all the same. In such circumstances
+a fairly simple ^{balancing} routine can be used to trisect the box.
+
+Let's consider a more general problem: Suppose that a rigid vbox is given,
+$n$~lines tall, where adjacent baselines are $b$~units apart. Suppose
+also that the top baseline is $h$~units from the top of the
+vbox, where $0<h<b$. \ (In our footnote example, $b=9\pt$ and $h=7\pt$;
+in the standard settings of plain \TeX, $b=12\pt$ and $h=10\pt$. We might
+as well work the problem for general $b$ and $h$.) \ It follows that
+the height of the vbox is $H=h+b(n-1)=bn+h-b$.
+
+If $n$ lines are to be distributed evenly into $k$ columns, the first
+column should contain $\lceil n/k\rceil$ lines. \ (This denotes the
+smallest integer greater than or equal to $n/k$.) \ For example,
+our application to footnotes has $n=16$ and $k=3$, hence the first
+column should contain 6~lines. After forming the first column,
+we have reduced the problem to $n=10$ and $k=2$, so two 5-line
+columns will complete the operation. \ (Notice that it is better
+to divide 16 into $6+5+5$ instead of $6+6+4$.) \ Once we have
+found the first column, it's always possible to reduce the $k$-column
+problem to a $(k-1)$-column problem, so we need only concentrate
+on finding the first column.
+
+Let $m=\lceil n/k\rceil$. The height of the given box is $bn+h-b$,
+and the height of the first column should be $bm+h-b$; hence
+we want to do a ^|\vsplit| to that height. However, it isn't
+necessary to calculate $bm+h-b$ exactly, since a bit of
+arithmetic proves that
+\begindisplay
+$\displaystyle bm+h-b\;<\;{bn+h-b\over k}+h\;<\;b(m+1)+h-b.$
+\enddisplay
+Therefore it suffices to |\vsplit| to height $H'=H/k+h$; under the
+assumptions of rigidity, and assuming that a valid break is possible
+after each line, |\vsplit| to $H'$ will split after the maximum
+number of lines that yield a box of height $\le H'$. \ (We have
+observed that $m$~lines produce a box of height~$<H'$ while
+$m+1$~lines produce a box of height~$>H'$.) \ The following \TeX\ code
+does this:
+\beginlines
+|\newcount\k \newdimen\h % registers used by the \rigidbalance routine|
+|\def\rigidbalance#1#2 #3 {\setbox0=\box#1 \k=#2 \h=#3|
+| \line{\splittopskip=\h \vbadness=10000 \hfilneg|
+| \valign{##\vfil\cr\dosplits}}}|
+|\def\dosplits{\ifnum\k>0 \noalign{\hfil}\splitoff|
+| \global\advance\k-1\cr\dosplits\fi}|
+|\def\splitoff{\dimen0=\ht0|
+| |^|\divide||\dimen0 by\k \advance\dimen0 by\h|
+| \vsplit0 to \dimen0 }|
+\endlines
+This code is interesting on a number of counts. First, notice that
+the calculation does not depend on~$b$, only on~$h$ and the height of
+the given box; hence |\rigidbalance| has three parameters: a box register
+number, the number of columns~$k$, and the top baseline height~$h$.
+The routine splits the given vbox into $k$~nearly equal pieces and
+justifies the result in a |\line|. The value of\/ ^|\splittopskip| is
+set to~$h$ so that subsequent vboxes will satisfy the ground rules of the
+original vbox, as the problem is reduced from~$k$ to~$k-1$. Each column will
+be preceded by ^|\hfil|, hence ^|\hfilneg| is used to cancel the |\hfil|
+before the first column. A ^|\valign| is used to align all of the columns
+at the top. Notice that the preamble to this |\valign| is
+quite simple; and the body of the |\valign| is generated by a ^{recursive
+macro} |\dosplits| that produces the $k$~columns. The value of\/
+^|\vbadness| is set to~10000 because each |\vsplit| operation
+will produce an ^{underfull} vbox whose badness is 10000.
+
+In our application to footnotes, the |\output| routine can reformat
+the contents of\/ |\box\footins| by saying, for example,
+\begintt
+\rigidbalance\footins 3 7pt
+\setbox\footins=\lastbox
+\endtt
+since ^|\lastbox| will be the result of\/ |\rigidbalance|.
+
+This solution to the problem of short footnotes might result in
+^{club lines} or ^{widow lines}, since the balancing routine we have
+described simply trisects the total number of lines. For example,
+if the tenth footnote of our example had not been present,
+the fifteen remaining lines would have been split $5+5+5$;
+the second column would have been headed by the lonely
+word `{\eightrm difficult.)}', and the third column would have
+started with `{\eightrm just an example.)}'. The rigid balancing
+procedure could be replaced by one that allows ^{ragged-bottom}
+columns, but there's also another approach: The entire set of footnotes
+could be combined into a single paragraph, with generous spacing
+between the individual items. For example, the ten footnotes we
+have been considering might appear as follows:
+\medskip
+\begingroup
+{\catcode`p=12 \catcode`t=12 \gdef\\#1pt{#1}}
+\let\getfactor=\\
+\eightpoint
+\newskip\footglue \footglue=1.5em plus.3em minus.3em
+\newdimen\footnotebaselineskip \footnotebaselineskip=10pt
+\dimen0=\footnotebaselineskip \multiply\dimen0 by 1024
+\divide \dimen0 by \hsize \multiply\dimen0 by 64
+\xdef\fudgefactor{\expandafter\getfactor\the\dimen0 }
+\def\vfootnote#1{\insert\footins{\floatingpenalty=20000
+ \global\advance\footno by 1
+ \eightpoint \setbox0=\hbox{%
+ $^{\the\footno}$#1\penalty-10\hskip\footglue}
+ \dp0=0pt \ht0=\fudgefactor\wd0 \box0}}
+\output={\showboxbreadth=9999 \showboxdepth=1
+ \nonstopmode \showbox\footins \errorstopmode
+ \global\setbox1=\vbox{\makefootnoteparagraph}
+ \unvbox255}
+\footno=0
+\vfootnote{First footnote.}
+\vfootnote{Second footnote. (Every once in a~while a long
+ footnote might occur, just to make things difficult.)}
+\vfootnote{Third footnote.}
+\vfootnote{Fourth footnote.}
+\vfootnote{Fifth footnote.
+ (This is incredibly boring, but it's just an example.)}
+\vfootnote{Another.}
+\vfootnote{And another.}
+\vfootnote{Ho hum.}
+\vfootnote{Umpteenth footnote.}
+\vfootnote{Oodles of them.}
+%\def\makefootnoteparagraph{\unvbox\footins \makehboxofhboxes
+% \showboxbreadth=9999 \showboxdepth=1
+% \nonstopmode \showbox0 \errorstopmode
+% \setbox0=\hbox{\unhbox0 \removehboxes}
+% \showboxbreadth=30
+% \nonstopmode \showbox0 \errorstopmode
+% \baselineskip=10pt\noindent\unhbox0 }
+%\def\makehboxofhboxes{\setbox0=\hbox{}
+% \loop\setbox2=\lastbox
+% \ifhbox2 \setbox0=\hbox{\box2\unhbox0}\repeat}
+%\def\removehboxes{\setbox0=\lastbox
+% \ifhbox0{\removehboxes}\unhbox0 \fi}
+\def\makefootnoteparagraph{\unvbox\footins
+ \baselineskip=\footnotebaselineskip \removehboxes}
+\def\removehboxes{\unskip\setbox0=\lastbox
+ \ifhbox0{\removehboxes}\unhbox0 \else\noindent \fi}
+\eject
+\box1\endgroup
+\medskip
+
+It would be possible to take the contents of\/ |\box\footins| shown
+previously and to reformat everything into a paragraph, but such an operation
+would be needlessly complicated. If footnotes are to be paragraphed by the
+output routine, it's better simply to prepare them in unjustified
+hboxes. Each of these hboxes will be unboxed later, so we are free to play
+with their heights, widths, and depths. It's convenient to set the
+depth to zero and the height to an estimate of how much a particular
+footnote will contribute to the final paragraph. For example, if
+a footnote takes up exactly half of the |\hsize|, and if the final
+footnote is going to be set with |\baselineskip=10pt|, then the
+height of the footnote hbox should be set to $5\pt$. By letting
+|\count\footins=1000|, we'll have a pretty good estimate of the size
+of the final footnote paragraph. In other words, the following
+insertion scheme is suggested:
+\begindisplay
+|\insert\footins{\floatingpenalty=20000|\cr
+| \eightpoint \setbox0=\hbox{%|\cr
+| $^{\the\footno}$|\<text of footnote>|\penalty-10\hskip\footglue}|\cr
+| \dp0=0pt \ht0=\fudgefactor\wd0 \box0}|\cr
+\enddisplay
+The penalty of $-10$ tends to favor line breaks between footnotes;
+|\footglue| is the amount of glue between footnotes in the final
+footnote paragraph; and |\fudgefactor| is the ratio of\/ |\baselineskip|
+to |\hsize| in that paragraph. The author defined the necessary quantities
+as follows in his experiments:
+\begintt
+\eightpoint \newskip\footglue \footglue=1.5em plus.3em minus.3em
+\newdimen\footnotebaselineskip \footnotebaselineskip=10pt
+\dimen0=\footnotebaselineskip \multiply\dimen0 by 1024
+\divide \dimen0 by \hsize \multiply\dimen0 by 64
+\xdef\fudgefactor{\expandafter\getfactor\the\dimen0 }
+\endtt
+^^|\multiply| ^^|\divide| ^^|sp|
+(The computation of\/ |\fudgefactor| uses the fact that $1\pt=1024\times64\,$sp,
+and it assumes that the |\footnotebaselineskip| is less than $16\pt$.)
+
+Inside the output routine, |\box\footins| will now be a vbox of hboxes, and the
+height of this vbox will be an estimate of the height of the final paragraph.
+For example, our ten footnotes produce
+\begintt
+\vbox(34.48158+0.0)x386.4221
+.\hbox(2.00175+0.0)x70.68285 []
+.\hbox(10.94359+0.0)x386.4221 []
+.\hbox(2.09749+0.0)x74.06345 []
+.\hbox(2.2077+0.0)x77.95517 []
+.\hbox(7.6296+0.0)x269.40376 []
+.\hbox(1.40851+0.0)x49.73532 []
+.\hbox(1.87659+0.0)x66.26334 []
+.\hbox(1.38826+0.0)x49.02003 []
+.\hbox(2.67213+0.0)x94.35402 []
+.\hbox(2.25597+0.0)x79.65926 []
+\endtt
+and the height of $34.48158\pt$ corresponds to an estimate of about
+three and a~half lines. \ (\TeX's page builder has also added |\skip\footins|
+when estimating the total contribution due to footnotes.)
+
+Finally, the reformatting of\/ |\box\footins| can be achieved easily with
+an elegant technique suggested by David ^{Kastrup}, using the following
+\TeX\ code within the |\output| routine:
+\begindisplay
+|\def\makefootnoteparagraph{\unvbox\footins|\cr
+| \baselineskip=\footnotebaselineskip \removehboxes}|\cr
+|\def\removehboxes{\unskip\setbox0=\lastbox|\cr
+| \ifhbox0{\removehboxes}\unhbox0 \else\noindent \fi}|\cr
+\enddisplay
+^^|\unhbox|^^|\unvbox|
+The key idea here is |\removehboxes|, a macro that has the magical ability to
+take a vertical box such as `|\vbox{\box1\box2\box3\removehboxes}|' and
+transform it into
+`|\vbox{\noindent\unhbox1\unhbox2\unhbox3}|'\kern-1pt,
+if\/ |\box1|, \kern-2pt|\box2|, and
+|\box3| are hboxes. Notice how |\removehboxes| introduces braces so that
+\TeX's ^{save stack} will hold all of the hboxes before they are unboxed. Each
+level of recursion in this routine uses one cell of input stack space and
+three cells of save stack space; thus, it is generally safe to do more than
+100 footnotes without exceeding \TeX's capacity.
+
+In our application there is no ^{interline glue} within |\box\footins|,
+so the ^|\unskip| command could be deleted from |\removehboxes|.
+
+Incidentally, the |\unskip| and ^|\lastbox| operations have running
+times of the approximate form
+$a+mb$, where $m$~is the number of items ^^{efficiency} on the
+list preceding the glue or box that is removed. Hence |\removehboxes| has a
+running time of order $n^2$ when it removes $n$~boxes.
+But the constant~$b$ is so small that
+for practical purposes it's possible to think of\/ |\unskip| and
+|\lastbox| as almost instantaneous.
+
+\subsection Communication with output routines. It would be possible to
+write an entire book about \TeX\ output routines; but the present
+appendix is already too long, so it will suffice to mention only one
+or two sneaky tricks that a person might not readily think~of. \ (Appendix~E
+gives some less sneaky examples.)
+
+Sometimes an output routine needs to know why it was invoked, so there's
+a problem of communicating information from the rest of the program.
+\TeX\ provides general |\mark| operations, but marks don't always yield
+the right sorts of clues. Then there's ^|\outputpenalty|, which can be
+tested to see what penalty occurred at a breakpoint; any penalty of
+$-10000$, $-10001$, $-10002$, or less,
+forces the output routine to act, hence different penalty values
+can be used to pass different messages. \ (When the output routine puts
+material back on the list of contributions, it need not restore the
+penalty at the breakpoint.) \ If output has been forced by a
+highly negative value of\/ |\outputpenalty|, the output routine can
+use |\vbox{\unvcopy255}| to discover how full the page-so-far actually is.
+Underfull and overfull boxes are not reported when |\box255| is
+packaged for use by the output routine, so there's no harm in ejecting
+a page prematurely if you want to pass a signal. \ (Set
+^|\holdinginserts| positive to pass a signal when the contents of\/
+|\box255| will be sent back through the page builder again, if any
+insertions are present.)
+
+Perhaps the dirtiest trick of all is to communicate with the output
+routine via the {\sl depth\/} of\/ |\box255|. For example, suppose that
+you want to know whether or not the current page ends with the last
+line of a paragraph. If each paragraph ends with `|\specialstrut|', where
+|\specialstrut| is like ^|\strut| but $1\,$sp deeper, then |\dp255|
+will have a recognizable value if a page ends simultaneously
+with a paragraph. \ (Of course, ^|\maxdepth| must be suitably large; plain
+\TeX\ takes |\maxdepth=4pt|, while struts are normally $3.5\pt$ deep, so
+there's no problem.) \ A distance of~$1000\,$sp is invisible to the
+naked eye, so a variety of messages can be passed in this way.
+
+If the value of\/ ^|\vsize| is very small, \TeX\ will construct paragraphs
+as usual but it will send them to the output routine one line at a time.
+In this way the output routine could attach marginal notes, etc., based
+on what occurs in the line. Paragraphs that have been rebuilt in this
+way can also be sent back from the output routine to the page builder;
+normal page breaks will then be found, if\/ |\vsize| has been restored.
+
+An output routine can also write notes on a file, based on what occurs in
+a manuscript. A two-pass system can be devised where \TeX\ simply gathers
+information during the first pass; the actual typesetting can be done during
+the second pass, using |\read| to recover information that was written
+during the first.
+
+\subsection Syntax checking. Suppose you want to run a manuscript through
+\TeX\ just to check for errors, without getting any output. Is there a
+way to make \TeX\ run significantly faster while doing this? Yes; here's how:
+(1)~Say `|\font|^|\dummy||=dummy|'\kern1pt; your system should include a
+file |dummy.tfm| that defines a font with no characters (but with
+enough |\fontdimen| parameters to qualify as a math symbol font).
+(2)~Set all the font identifiers you are using equal to |\dummy|. For
+example, |\let\tenrm=\dummy|, |\let\tenbf=\dummy|, \dots, |\textfont0=\dummy|,
+etc. (3)~Say `|\dummy|' to select the dummy font (since plain \TeX\ may
+have selected the real |\tenrm|). (4)~Set ^|\tracinglostchars||=0|, so that
+\TeX\ won't complain when characters aren't present in the dummy font. (5)~Set
+\begintt
+\output={\setbox0=\box255\deadcycles=0}
+\endtt
+^^|\deadcycles|
+so that nothing will be shipped out, yet \TeX\ will not think that your
+output routine is flaky. (6)~Say ^|\newtoks||\output|, so that no other
+output routine will be defined. (7)~Say ^|\frenchspacing| so that \TeX\
+will not have to do space factor calculations. (8)~Say
+^|\hbadness||=10000| so that underfull boxes will not be reported.
+(9)~And if you want to disable |\write| commands, use the following
+trick due to Frank ^{Yellin}:
+\begintt
+\let\immediate=\relax \def\write#1#{{\afterassignment}\toks0=}
+\endtt
+These changes usually make \TeX\ run more than four times as fast.
+^^|\afterassignment|
+
+\endchapter
+
+^{Wolfe}, who had moved around the desk and into his chair,
+^^{Hombert, Humbert} put up a palm at him: ``Please, Mr.~Hombert.
+I think it is always advisable to take a short-cut when it is feasible.\rlap{''}
+\author REX ^{STOUT}, {\sl The Rubber Band\/} (1936)
+
+\bigskip
+
+``My dear ^{Watson}, try a little analysis yourself,\rlap{''}
+said he, ^^{Holmes} with a touch of impatience.
+``You know my methods. Apply them,
+and it will be instructive to compare results.\rlap{''}
+\author CONAN ^{DOYLE}, {\sl The Sign of the Four\/} (1890)
+
+\eject
+ \beginchapter Appendix E. Example Formats
+
+Although the plain \TeX\ format of Appendix B is oriented to technical
+reports, it can readily be adapted to quite different applications. Examples of
+three such adaptations are provided in this appendix: (1)~a~format for
+business letters; (2)~a~format for concert programs; (3)~the~format used
+to typeset this book.
+
+Let's consider ^{business letters} first. Suppose that you want \TeX\ to
+format your ^{correspondence}, and that you have $n$~letters to send. ^^{mail}
+If your computer system contains a file ^|letterformat.tex| like the one
+described later in this appendix, it's easy to do the job by applying \TeX\ to
+a file that looks like this:
+\begindisplay
+\<optional magnification>\cr
+^|\input|| letterformat|\cr
+\<business letter$_1$>\cr
+\noalign{\vskip-1pt}
+\qquad\vdots\cr
+\<business letter$_n$>\cr
+^|\end|\cr
+\enddisplay
+Here each of the $n$ business letters has the form
+\begindisplay
+\<letterhead>\cr
+^|\address|\cr
+\<one or more lines of address>\cr
+^|\body|\cr
+\<one or more paragraphs of text>\cr
+^|\closing|\cr
+\<one or more lines for salutation and signature>\cr
+\<optional annotations>\cr
+\<optional postscripts>\cr
+^|\endletter|\cr
+^|\makelabel|| % omit this if you don't want an address label|\cr
+\enddisplay
+The \<letterhead> at the beginning of this construction is usually a
+control sequence like |\rjdletterhead| for letters by R.~J.~D.; each
+letter writer can have a personalized letterhead that is stored with the
+|letterformat| macros. The \<optional annotations> at the end are any number
+of one-line notes preceded by `^|\annotations|'; the \<optional postscripts>
+are any number of paragraphs preceded by `^|\ps|'. When \TeX\ is processing the
+|\address| and the |\closing| and the optional |\annotations|, it produces
+output line-for-line just as the lines appear in the input file; but when
+\TeX\ is processing the |\body| of the letter and the optional |\ps|, it
+chooses line breaks and justifies lines as it normally does when
+typesetting paragraphs in books.
+
+A complete example, together with the resulting output, appears on the
+next two pages. This example starts with `^|\magnification||=|^|\magstep||1|'
+because the letter is rather short. Magnification is usually omitted if the
+letters are long-winded; `|\magnification=|^|\magstephalf|' is
+appropriate when they are medium-size. The same magnification applies to
+all~$n$ letters, so you must run \TeX\ more than once if you want more than
+one magnification.
+
+\eject
+\begingroup \obeylines
+|\magnification=\magstep1|
+|\input letterformat|
+\bigskip
+|\rjdletterhead % (see the output on the next page)|
+\bigskip
+|\address|
+|Prof.~Brian~K. Reid|^^{Reid}
+|Department of Electrical Engineering|
+|Stanford University|
+|Stanford, CA 94305|
+\bigskip
+|\body|
+|Dear Prof.~Reid:|
+\bigskip
+|I understand that you are having difficulties with|
+|Alka-Seltzer tablets. Since there are 25~pills|^^{Alka-Seltzer}^^{ties}
+|per bottle, while the manufacturer's directions|
+|recommend ``plop,~plop, fizz,~fizz,'' my colleagues|
+|tell me that you have accumulated a substantial|
+|number of bottles in which there is one tablet|
+|left. % (See the 1978 SCRIBE User Manual, page 90.)|^^{Scribe}
+\bigskip
+|At present I am engaged in research on the potential|
+|applications of isolated analgesics. If you would|
+|be so kind as to donate your Alka-Seltzer collection|
+|to our project, I would be more than happy to send|
+|you preprints of any progress reports that we may|
+|publish concerning this critical problem.|
+\bigskip
+|\closing|
+|Sincerely,|
+|R. J. Drofnats|^^{Drofnats}
+|Professor|
+\bigskip
+|\annotations|
+|RJD/dek|
+|cc: {\sl The \TeX book}|
+\bigskip
+|\ps|
+|P. S. \ If you like, I will check into the|
+|possibility that your donation and the meals that|
+|you have been eating might be tax-deductible, in|^^{IRS}
+|connection with our research.|
+|\endletter|
+|\makelabel|
+\eject\endgroup
+\def\proofcopy(#1){\ifproofmode\raise.5in\hbox{\sevenrm(#1)}\else\hfil\fi}
+\vglue-\topskip \nointerlineskip
+\dimen0=\vsize \advance\dimen0 by-1.2pt \advance\dimen0 by-2.1in
+\vbox{\hrule
+ \hbox{\vrule height \dimen0
+ \hbox to 4.25in{\hss\proofcopy(Output page goes here, reduced 50\%.)\hss}
+ \vrule}}
+\nointerlineskip
+\hrule
+\line{\vrule height 2.1in
+ \hss\proofcopy(Label and stamp go here, reduced 50\%.)\hss\vrule}
+\hrule
+\vskip 0pt plus .001pt minus .001pt % in case of rounding errors
+\eject
+If the letter is more than one page long, the addressee, date, and
+page number will appear at the top of subsequent pages. For example,
+the previous letter comes out as follows, if additional paragraphs are
+added to the text:
+\medskip
+\hrule
+\line{\vrule height 3.1127in
+ \hfil\proofcopy(First page, reduced to 28.3\%.)\hfil\vrule\hfil
+ \proofcopy(Second page, reduced to 28.3\%.)\hfil\vrule}
+\hrule
+\bigskip
+\ninepoint
+The macro package |letterformat.tex| that produces this format begins
+with a simple macro that expands to the current ^{date}.
+\beginlines
+|\def|^|\today||{|^|\ifcase|^|\month|^|\or|
+| January\or February\or March\or April\or May\or June\or|
+| July\or August\or September\or October\or November\or December\fi|
+| |^|\space|^|\number|^|\day||, \number|^|\year||}|
+\endlines
+
+Then comes the specification of page layout, which is ``ragged'' at the
+bottom. A rather large ^|\interlinepenalty| is used so that page
+breaks will tend to occur between paragraphs.
+\beginlines
+^|\raggedbottom|
+|\interlinepenalty=1000|
+^|\hsize||=6.25truein|
+^|\voffset||=24pt|
+|\advance|^|\vsize|| by-\voffset|
+^|\parindent||=0pt|
+^|\parskip||=0pt|
+^|\nopagenumbers|
+^|\headline||={\ifnum|^|\pageno||>1|
+| \tenrm To \addressee\hfil\today\hfil Page |^|\folio|
+| \else\hfil\fi}|
+\endlines
+
+The contents of a letter are typeset either in ``line mode'' (obeying lines)
+or in ``paragraph mode'' (producing paragraphs in ^{block style}). Control
+sequences |\beginlinemode| and |\beginparmode| are defined to initiate these
+modes; and another control sequence, |\endmode|, is defined and redefined so
+that the current mode will terminate properly:
+\beginlines
+|\def\beginlinemode{\endmode|
+| |^|\begingroup|^|\obeylines||\def\endmode{\par|^|\endgroup||}}|
+|\def\beginparmode{\endmode|
+| \begingroup\parskip=\medskipamount \def\endmode{\par\endgroup}}|
+|\let\endmode=\par|
+|\def\endletter{\endmode\vfill|^|\supereject||}|
+\endlines
+
+One of the chief characteristics of this particular business letter format
+is a parameter called ^|\longindentation|, which is used to indent the
+closing material, the date, and certain aspects of the letterhead. The
+^|\address| macro creates a box that will be used both in the letter and in
+the label on the envelope. If individual lines of the address exceed
+|\longindentation|, they are broken, and hanging indentation is used for any
+material that must be carried over.
+\beginlines
+|\newdimen\longindentation \longindentation=4|^|true||in|
+|\newbox\theaddress|
+|\def\address{\beginlinemode\getaddress}|
+|{\obeylines|^|\gdef||\getaddress #1|
+| #2|
+| {#1\gdef\addressee{#2}%|
+| \global\setbox\theaddress=\vbox|^|\bgroup|^|\raggedright||%|
+| \hsize=\longindentation |^|\everypar||{|^|\hangindent||2em}#2|
+| \def\endmode{\egroup\endgroup |^|\copy||\theaddress |^|\bigskip||}}}|
+\endlines
+(Parameter |#2| to |\getaddress| ^^{parameters, delimited}
+will be the contents of the line following |\address|, i.e., the
+name of the addressee.)
+
+The closing macros are careful not to allow a page break anywhere between the
+end of the ^|\body| and the beginning of a ^|\ps|.
+\beginlines
+|\def\body{\beginparmode}|
+|\def\closing{\beginlinemode\getclosing}|
+|{\obeylines\gdef\getclosing #1|
+| #2|
+| {#1|^|\nobreak||\bigskip |^|\leftskip||=\longindentation #2|
+| \nobreak\bigskip\bigskip\bigskip % space for signature|
+| \def|
+| {|^|\endgraf||\nobreak}}}|
+|\def\annotations{\beginlinemode\def\par{\endgraf\nobreak}\obeylines\par}|
+|\def\ps{\beginparmode\nobreak|
+| \interlinepenalty5000\def\par{\endgraf\penalty5000}}|
+\endlines
+
+The remaining portion of |letterformat.tex| deals with ^{letterheads} and
+labels, which of course will be different for different organizations.
+The following macros were used to generate the examples in this
+appendix; they can be modified in more-or-less obvious ways to produce
+suitable letterheads of other kinds. Special fonts are generally
+needed, and they should be loaded at `^|true|' sizes so that they are not
+affected by magnification. One tiny refinement worth noting here is the
+^|\up| macro, which raises ^{brackets} so that they look better in a
+^{telephone number}.
+\beginlines
+|\def\up#1{|^|\leavevmode|| |^|\raise||.16ex\hbox{#1}}|
+|\font\smallheadfont=cmr8 |^|at|| 8truept|
+|\font\largeheadfont=cmdunh10 at 14.4truept|
+|\font\logofont=manfnt at 14.4truept|
+\smallbreak
+|\def\rjdletterhead{|
+| \def\sendingaddress{R. J. DROFNATS, F.T.U.G.\par|^^{TeX Users Group}
+| PROFESSOR OF FARM ECOLOGY\par|
+| TEX.RJD @ SU-SCORE.ARPA\par|^^{atsign}
+| \up[415\up]\thinspace 497-4975\par}|
+| \def\returnaddress{R. J. Drofnats, Dept.~of Farm Ecology\par|
+| The University of St.~Anford\par|
+| P. O. Box 1009, Haga Alto, CA 94321 USA}|
+| \letterhead}|
+\smallbreak
+|\def\letterhead{\pageno=1 \def\addressee{} \univletterhead|
+| {\leftskip=\longindentation|
+| {\baselineskip9truept\smallheadfont\sendingaddress}|
+| \bigskip\bigskip\rm\today\bigskip}}|
+\smallbreak
+|\def\univletterhead{\vglue-\voffset|
+| \hbox{\hbox to\longindentation{\raise4truemm\hbox{\logofont|
+| \kern2truept X\kern-1.667truept|
+| \lower2truept\hbox{X}\kern-1.667truept X}\hfil|
+| \largeheadfont The University of St.~Anford\hfil}%|
+| \kern-\longindentation|
+| \vbox{\smallheadfont\baselineskip9truept|
+| \leftskip=\longindentation BOX 1009\par HAGA ALTO, CA 94321}}|
+| \vskip2truept\hrule\vskip4truept }|
+\smallbreak
+|\def|^|\makelabel||{\endletter\hbox{\vrule|
+| \vbox{\hrule \kern6truept|
+| \hbox{\kern6truept\vbox to 2truein{\hsize=\longindentation|
+| \smallheadfont\baselineskip9truept\returnaddress|
+| \vfill\moveright 2truein\copy\theaddress\vfill}%|
+| \kern6truept}\kern6truept\hrule}\vrule}|
+| \pageno=0\vfill\eject}|
+\endlines
+
+\medbreak
+Our second example is a format for ^{concert programs}, to be used in
+^^{music} ^^{programs, for music}
+connection with orchestra performances, recitals, and the like. We shall
+assume that the entire program fits on a single page, and that the
+copy is to be 4~inches wide. Comparatively large type ($12\pt$) will
+normally be used, but there is a provision for $10\pt$ and even ^^{sizes
+of type} $8\pt$ type in case the program includes pieces with a lot of
+subparts (e.g., ^{Bach}'s Mass in B~minor, or ^{Beethoven}'s ^{Diabelli}
+Variations). To select the ^{type size}, a user says ^|\bigtype|,
+^|\medtype|, or ^|\smalltype|, respectively. These macros for ^{size switching}
+are comparatively simple because concert programs don't require any
+mathematics; hence the math fonts don't need to be changed. On the other
+hand, the format does take sharp and flat signs from the ``^{math italic}''
+font, which it calls `|\mus|':
+\beginlines
+|\font\twelverm=cmr12|
+|\font\twelvebf=cmbx12|
+|\font\twelveit=cmti12|
+|\font\twelvesl=cmsl12|
+|\font\twelvemus=cmmi12|
+\smallskip
+|\font\eightrm=cmr8|
+|\font\eightbf=cmbx8|
+|\font\eightit=cmti8|
+|\font\eightsl=cmsl8|
+|\font\eightmus=cmmi8|
+\smallskip
+|\def\bigtype{\let|^|\rm||=\twelverm \let|^|\bf||=\twelvebf|
+| \let|^|\it||=\twelveit \let|^|\sl||=\twelvesl \let\mus=\twelvemus|
+| \baselineskip=14pt minus 1pt|
+| \rm}|
+|\def\medtype{\let\rm=\tenrm \let\bf=\tenbf|
+| \let\it=\tenit \let\sl=\tensl \let\mus=\teni|
+| \baselineskip=12pt minus 1pt|
+| \rm}|
+|\def\smalltype{\let\rm=\eightrm \let\bf=\eightbf|
+| \let\it=\eightit \let\sl=\eightsl \let\mus=\eightmus|
+| \baselineskip=9.5pt minus .75pt|
+| \rm}|
+\smallskip
+|\hsize=4in|
+|\nopagenumbers|
+|\bigtype|
+\endlines
+Notice the ^{shrinkability} in the ^|\baselineskip| settings. This would be
+undesirable in a book format, because different ^{spacing between lines}
+^^{leading} on different pages would look bad; but in a one-page document
+it helps squeeze the copy to fit the page, in an emergency. \ (There's no
+need for ^{stretchability} in the baselineskip here, because a |\vfill| will
+be used at the bottom of the page.)
+
+Musical programs have a specialized vocabulary, and it is desirable to define
+a few control sequences for things that plain \TeX\ doesn't make as convenient
+as they could be for this particular application:
+\beginlines
+|\def\(#1){{\rm(}#1\/{\rm)}}|
+|\def\sharp{\raise.4ex\hbox{\mus\char"5D}}|
+|\def\flat{\raise.2ex\hbox{\mus\char"5B}}|
+|\let\,=|^|\thinspace|
+\endlines
+The ^|\(| macro produces roman ^{parentheses} in the midst of ^{italicized
+text}; the ^|\sharp| and ^|\flat| macros produce musical signs in the
+current type size. The ^|\,|~macro makes it easy to specify
+the ^{thin space} that is used in constructions like
+`K.\thinspace550' ^^{K\"ochel}^^{Mozart} % his 40th symphony
+and `Op.\thinspace59'. ^^{Dvo\v r\'ak} % his Legends
+\ (Plain \TeX\ has already defined |\,| and |\sharp| and |\flat| in a different
+way; but those definitions apply only to math formulas, so they aren't
+relevant in this application.)
+
+Before discussing the rest of the music macros, let's take a look at a complete
+example. The next two pages show the input and output for a typical concert
+program.
+
+\eject
+\begingroup \parindent=0pt \obeylines
+|\input concert|
+\medskip
+|\tsaologo|
+|\medskip|
+|\centerline{Friday, November 19, 1982, 8:00 p.m.}|
+|\bigskip|
+|\centerline{\bf PROGRAM}|
+|\medskip|
+\medskip
+|\composition{Variations on a Theme by Tchaikovsky}|%
+ ^^{Tchaikovsky, see Cha\u\i...}
+|\composer{Anton S. Arensky (1861--1906)}|^^{Arenski\u\i}
+|\smallskip|
+|{\medtype|
+|\movements{Tema: Moderato\cr|
+| Var.~I: Un poco pi\`u mosso&Var.~V: Andante\cr|
+| Var.~II: Allegro non troppo&Var.~VI: Allegro con spirito\cr|
+| Var.~III: Andantino tranquillo&Var.~VII: Andante con moto\cr|
+| Var.~IV: Vivace&Coda: Moderato\cr}|
+|}|
+\medskip
+|\bigskip|
+\medskip
+|\composition{Concerto for Horn and Hardart, S.\,27}|
+|\composer{P. D. Q. Bach (1807--1742)?}|^^{Bach, PDQ}
+|\smallskip|
+|\movements{Allegro con brillo\cr|
+| Tema con variazione \(su un tema differente)\cr|
+| Menuetto con panna e zucchero\cr}|
+|\medskip|
+|\soloists{Ben Lee User, horn\cr|^^{User}
+| Peter Schickele, hardart\cr}|^^{Schickele}
+\medskip
+|\bigskip|
+|\centerline{INTERMISSION}|
+|\bigskip|
+\medskip
+|\composition{Symphony No.\,3 in E\flat\ Major\cr|
+| Op.\,55, ``The Eroica''\cr}|
+|\composer{Ludwig van Beethoven (1770--1827)}|^^{Beethoven}
+|\smallskip|
+|\movements{Allegro con brio\cr|
+| Marcia funebre: Adagio assai\cr|
+| Scherzo: Allegro vivace\cr|
+| Finale: Allegro molto\cr}|
+\medskip
+|\bigskip|
+|\smalltype \noindent|
+|Members of the audience are kindly requested to turn off the|
+|alarms on their digital watches, and to cough only between movements.|
+\medskip
+|\bye|
+\eject\endgroup
+\hbox to\hsize{\hss \vbox{ % \centerline is not a \long macro!
+\font\twelverm=cmr12
+\font\twelvebf=cmbx12
+\font\twelveit=cmti12
+\font\twelvesl=cmsl12
+\font\twelvemus=cmmi12
+
+\def\bigtype{\let\rm=\twelverm \let\bf=\twelvebf \let\it=\twelveit
+ \let\sl=\twelvesl \let\mus=\twelvemus \baselineskip=14pt minus 1pt \rm}
+\def\medtype{\let\rm=\tenrm \let\bf=\tenbf \let\it=\tenit
+ \let\sl=\tensl \let\mus=\teni \baselineskip=12pt minus 1pt \rm}
+\def\smalltype{\let\rm=\eightrm \let\bf=\eightbf \let\it=\eightit
+ \let\sl=\eightsl \let\mus=\eighti \baselineskip=9.5pt minus .75pt \rm}
+
+\def\flat{\raise.2ex\hbox{\mus\char"5B}}
+\def\sharp{\raise.4ex\hbox{\mus\char"5D}}
+\let\,=\thinspace
+\def\(#1){{\rm(}#1\/{\rm)}}
+
+\hsize=4in
+\bigtype
+
+\def\composition#1{\halign{\bf\quad##\hfil\cr
+ \kern-1em#1\crcr}} % use \cr's if more than one line
+\def\composer#1{\rightline{\bf#1}}
+\def\movements#1{\halign{\quad\it##\hfil&&\qquad\it##\hfil\cr#1\crcr}}
+\def\soloists#1{\centerline{\bf\vbox{\halign{##\hfil\cr#1\crcr}}}}
+
+\def\tsaologo{\vbox{\bigtype\bf
+ \line{\hrulefill}
+ \kern-.5\baselineskip
+ \line{\hrulefill\phantom{ THE ST.\,ANFORD ORCHESTRA }\hrulefill}
+ \kern-.5\baselineskip
+ \line{\hrulefill\hbox{ THE ST.\,ANFORD ORCHESTRA }\hrulefill}
+ \kern-.5\baselineskip
+ \line{\hrulefill\phantom{ R. J. Drofnats, Conductor }\hrulefill}
+ \kern-.5\baselineskip
+ \line{\hrulefill\hbox{ R. J. Drofnats, Conductor }\hrulefill}
+ }}
+\tsaologo
+\medskip
+\centerline{Friday, November 19, 1982, 8:00 p.m.}
+\bigskip
+\centerline{\bf PROGRAM}
+\medskip
+
+\composition{Variations on a Theme by Tchaikovsky}
+\composer{Anton S. Arensky (1861--1906)}
+\smallskip
+{\medtype
+\movements{Tema: Moderato\cr
+ Var.~I: Un poco pi\`u mosso&Var.~V: Andante\cr
+ Var.~II: Allegro non troppo&Var.~VI: Allegro con spirito\cr
+ Var.~III: Andantino tranquillo&Var.~VII: Andante con moto\cr
+ Var.~IV: Vivace&Coda: Moderato\cr}
+}
+
+\bigskip
+
+\composition{Concerto for Horn and Hardart, S.\,27}
+\composer{P. D. Q. Bach (1807--1742)?}
+\smallskip
+\movements{Allegro con brillo\cr
+ Tema con variazione \(su un tema differente)\cr
+ Menuetto con panna e zucchero\cr}
+\medskip
+\soloists{Ben Lee User, horn\cr
+ Peter Schickele, hardart\cr}
+
+\bigskip
+\centerline{INTERMISSION}
+\bigskip
+
+\composition{Symphony No.\,3 in E\flat\ Major\cr
+ Op.\,55, ``The Eroica''\cr}
+\composer{Ludwig van Beethoven (1770--1827)}
+\smallskip
+\movements{Allegro con brio\cr
+ Marcia funebre: Adagio assai\cr
+ Scherzo: Allegro vivace\cr
+ Finale: Allegro molto\cr}
+
+\bigskip
+\smalltype \noindent
+Members of the audience are kindly requested to turn off the
+alarms on their digital watches, and to cough only between movements.
+}\hss}^^{Drofnats}\vfill\eject
+
+\edef\\{\hskip\the\parindent} % I'm emphasizing this "design element" here!
+Most of the macros in |concert.tex| have already been defined. Plain \TeX\
+takes care of things like |\centerline| and |\bigskip|, so only
+|\composition|, |\composer|, |\movements|, and |\soloists| remain to
+be specified:
+\beginlines
+|\def\composition#1{\halign{\bf\quad##\hfil\cr|
+\\|\kern-1em#1\crcr}} % use \cr's if more than one line|
+|\def\composer#1{\rightline{\bf#1}}|
+|\def\movements#1{\halign{\quad\it##\hfil&&\qquad\it##\hfil\cr#1\crcr}}|
+|\def\soloists#1{\centerline{\bf\vbox{\halign{##\hfil\cr#1\crcr}}}}|
+\endlines
+The |\composition| macro is set up to put the title of the composition on
+two or more lines, if needed, but a single line usually suffices. Notice
+that ^|\crcr| has been used so that the final ^|\cr| in the argument to
+|\composition| is not needed. Similarly, |\movements| might be used to
+produce only a single line, and |\soloists| might be used when there
+is only one soloist.
+
+There's also a |\tsaologo| macro. It applies only to one particular
+orchestra, but the definition is somewhat interesting nonetheless:
+\beginlines
+|\def\tsaologo{\vbox{\bigtype\bf|
+\\^|\line||{|^|\hrulefill||}|
+\\|\kern-.5\baselineskip|
+\\|\line{\hrulefill|^|\phantom||{ THE ST.\,ANFORD ORCHESTRA }\hrulefill}|
+\\|\kern-.5\baselineskip|
+\\|\line{\hrulefill\hbox{ THE ST.\,ANFORD ORCHESTRA }\hrulefill}|
+\\|\kern-.5\baselineskip|
+\\|\line{\hrulefill\phantom{ R. J. Drofnats, Conductor }\hrulefill}|
+\\|\kern-.5\baselineskip|
+\\|\line{\hrulefill\hbox{ R. J. Drofnats, Conductor }\hrulefill}|
+\\|}}|
+\endlines
+
+The author ^^{Knuth} has extended these macros to a more elaborate format
+that includes special features for listing the members of the orchestra and
+for program notes, etc.; in this way it becomes fairly easy to typeset little
+booklets for concert patrons. Such extensions need not be discussed further
+in this appendix, because they don't illustrate any essentially new ideas.
+
+Notice that the |\composition| and |\movements| and |\soloists| macros do
+not include any special provision for vertical ^{spacing}; the user is
+supposed to insert ^|\smallskip|, ^|\medskip|, and ^|\bigskip| as
+desired. This was done deliberately, because different concert programs
+seem to demand different spacing; no automatic scheme actually works very
+well in practice, since musical literature is so varied.
+
+\medbreak
+Let's turn now to the design of a format for an entire book, using this book
+itself as an example. How did the author prepare the computer file
+that generated {\sl The \TeX book\/}? We have already seen several hundred
+pages of output produced from that file; our goal in the remainder of this
+appendix will be to examine the input that was used behind the scenes.
+
+In the first place, the author prepared ^{sample pages} and showed them to
+the publisher's book designer. \ (The importance of this step cannot be
+overemphasized. There is a danger that authors---who are now able to
+typeset their own books with \TeX---will attempt to do their own designs,
+^^{author, typesetting by}
+without professional help. ^{Book design} is an art that requires considerable
+creativity, skill, experience, and taste; it is one of the most important
+services that a publisher traditionally provides to an author.)
+
+Sample pages that are used as the basis of a design should show each of
+the elements in the book. In this case the elements included chapter titles,
+illustrations, subchapter headings, footnotes, displayed formulas,
+typewriter type, dangerous bends, exercises, answers, quotations,
+tables, numbered lists, bulleted lists, etc.; the author also expressed
+a desire for generous margins, so that readers could make marginal notes.
+
+The designer, Herb ^{Caswell}, faced a difficult problem of bringing all
+those disparate elements into a consistent framework. He decided to achieve
+this by using a uniform indentation of 3~^{picas} for normal paragraph openings
+as well as for dangerous bends; and to establish this element of the design
+by using it also for all the displayed material, instead of centering the
+displays.
+
+He decided to put the page numbers in bold type, out in the margins (where
+there was plenty of room, thanks to the author's request for white space);
+and he decided to use italic type with caps and lower case for the running
+headlines, so that the pages would have a somewhat informal flavor.
+
+He chose 10-point type (on a 12-point base) for the main text, and 9-point type
+(on an 11-point base) for the dangerous bends; the typeface was predetermined.
+He~chose an ^|\hsize| of 29~picas and a ^|\vsize| of 44~picas. He decided
+to give subheadings like `\kern1pt{\manual\char'170}\kern.15em
+{\eightbf EXERCISE \bf13.8}' in boldface caps before the statement of each
+exercise. He~specified the amount of vertical space before and after such
+things as exercises, dangerous-bend paragraphs, and displayed equations.
+He decided to devote an entire left-hand page to each chapter illustration.
+And so on; each decision influenced the others, so that the final book would
+appear to be as coherent and attractive as possible under the circumstances.
+After the main portion of the book was designed, he worked out a format for
+the ^{front matter} (i.e., the pages that precede page~1); he arranged to
+have the same amount of ``^{sinkage}'' (white space) at the top of each page
+there, so that the opening pages of the book would look unified and ``open.''
+
+The author hasn't actually followed the designer's specifications in every
+detail. For example, nothing about stretching or shrinking of vertical spaces
+appeared in the design specs; the author introduced the notion of flexible
+glue on his own initiative, based on his observations of cut-and-paste
+operations often used in page makeup. If this book has any beauties, they
+should be ascribed to Herb Caswell; if it has any blemishes, they should be
+ascribed to Don Knuth, who wrote the formatting
+macros that we are now about to discuss.
+
+The computer file |manual.tex| that generated {\sl The \TeX book\/} begins
+with a copyright notice, and then it says `|\input| |manmac|'. The auxiliary
+file ^|manmac.tex| contains the formatting macros, and it begins by
+loading 9-point, 8-point, and 6-point~fonts:
+\beginlines
+|\font\ninerm=cmr9 \font\eightrm=cmr8 \font\sixrm=cmr6|
+|\font\ninei=cmmi9 \font\eighti=cmmi8 \font\sixi=cmmi6|
+|\font\ninesy=cmsy9 \font\eightsy=cmsy8 \font\sixsy=cmsy6|
+|\font\ninebf=cmbx9 \font\eightbf=cmbx8 \font\sixbf=cmbx6|
+|\font\ninett=cmtt9 \font\eighttt=cmtt8|
+|\font\nineit=cmti9 \font\eightit=cmti8|
+|\font\ninesl=cmsl9 \font\eightsl=cmsl8|
+\endlines
+(These fonts had been ^|\preloaded| in Appendix B\null; now they're officially
+loaded.)
+
+The fonts intended for math formulas need to have a nonstandard ^|\skewchar|.
+The typewriter fonts are given ^|\hyphenchar||=-1| so that ^{hyphenation}
+is inhibited when control sequence names and keywords appear in the text
+of a paragraph.
+\beginlines
+|\skewchar\ninei='177 \skewchar\eighti='177 \skewchar\sixi='177|
+|\skewchar\ninesy='60 \skewchar\eightsy='60 \skewchar\sixsy='60|
+|\hyphenchar\ninett=-1 \hyphenchar\eighttt=-1 \hyphenchar\tentt=-1|
+\endlines
+
+A few more fonts are needed for special purposes:
+\beginlines
+|\font\tentex=cmtex10 % TeX character set as in Appendix C|
+|\font\inchhigh=cminch % inch-high caps for chapter openings|
+|\font\titlefont=cmssdc10 at 40pt % titles in chapter openings|
+|\font\eightss=cmssq8 % quotations in chapter closings|
+|\font\eightssi=cmssqi8 % ditto, slanted|
+|\font\tenu=cmu10 % unslanted text italic|
+|\font\manual=manfnt % METAFONT logo and special symbols|
+|\font\magnifiedfiverm=cmr5 at 10pt % to demonstrate magnification|
+\endlines
+
+Now we come to the ^{size-switching} macros, which are much more
+elaborate than they were in the previous example because mathematics
+needs to be supported in three different sizes. The format also
+provides for a pseudo ``^{small caps}'' (|\sc|); a true caps-and-small-caps
+font was not really necessary in the few cases that |\sc| was used.
+A~dimension variable ^|\ttglue| is set equal to the desired spacing for
+the typewriter-like text that occasionally appears in paragraphs; the
+|\tt| fonts have fixed spacing, which doesn't mix well with
+variable spacing, hence the macros below use |\ttglue| between words in
+appropriate places.
+\beginlines
+|\catcode`@=11 % we will access private macros of plain TeX (carefully)|
+^|\newskip||\ttglue|^^{atsign}
+\smallbreak
+|\def|^|\tenpoint||{\def|^|\rm||{|^|\fam||0\tenrm}% switch to 10-point type|
+| |^|\textfont||0=\tenrm \scriptfont0=\sevenrm \scriptscriptfont0=\fiverm|
+| \textfont1=\teni |^|\scriptfont||1=\seveni \scriptscriptfont1=\fivei|
+| \textfont2=\tensy \scriptfont2=\sevensy |^|\scriptscriptfont||2=\fivesy|
+| \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex|
+| \textfont|^|\itfam||=\tenit \def|^|\it||{\fam\itfam\tenit}%|
+| \textfont|^|\slfam||=\tensl \def|^|\sl||{\fam\slfam\tensl}%|
+| \textfont|^|\ttfam||=\tentt \def|^|\tt||{\fam\ttfam\tentt}%|
+| \textfont|^|\bffam||=\tenbf \scriptfont\bffam=\sevenbf|
+| \scriptscriptfont\bffam=\fivebf \def|^|\bf||{\fam\bffam\tenbf}%|
+| \tt \ttglue=.5em plus.25em minus.15em|
+| |^|\normalbaselineskip=12pt|
+| \setbox|^|\strutbox||=\hbox{\vrule height8.5pt depth3.5pt width0pt}%|
+| \let|^|\sc||=\eightrm \let|^|\big||=\tenbig \normalbaselines\rm}|
+\smallbreak
+|\def|^|\ninepoint||{\def\rm{\fam0\ninerm}% switch to 9-point type|
+| \textfont0=\ninerm \scriptfont0=\sixrm \scriptscriptfont0=\fiverm|
+| \textfont1=\ninei \scriptfont1=\sixi \scriptscriptfont1=\fivei|
+| \textfont2=\ninesy \scriptfont2=\sixsy \scriptscriptfont2=\fivesy|
+| \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex|
+| \textfont\itfam=\nineit \def\it{\fam\itfam\nineit}%|
+| \textfont\slfam=\ninesl \def\sl{\fam\slfam\ninesl}%|
+| \textfont\ttfam=\ninett \def\tt{\fam\ttfam\ninett}%|
+| \textfont\bffam=\ninebf \scriptfont\bffam=\sixbf|
+| \scriptscriptfont\bffam=\fivebf \def\bf{\fam\bffam\ninebf}%|
+| \tt \ttglue=.5em plus.25em minus.15em|
+| \normalbaselineskip=11pt|
+| \setbox\strutbox=\hbox{\vrule height8pt depth3pt width0pt}%|
+| \let\sc=\sevenrm \let\big=\ninebig \normalbaselines\rm}|
+\smallbreak
+|\def|^|\eightpoint||{\def\rm{\fam0\eightrm}% switch to 8-point type|
+| \textfont0=\eightrm \scriptfont0=\sixrm \scriptscriptfont0=\fiverm|
+| \textfont1=\eighti \scriptfont1=\sixi \scriptscriptfont1=\fivei|
+| \textfont2=\eightsy \scriptfont2=\sixsy \scriptscriptfont2=\fivesy|
+| \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex|
+| \textfont\itfam=\eightit \def\it{\fam\itfam\eightit}%|
+| \textfont\slfam=\eightsl \def\sl{\fam\slfam\eightsl}%|
+| \textfont\ttfam=\eighttt \def\tt{\fam\ttfam\eighttt}%|
+| \textfont\bffam=\eightbf \scriptfont\bffam=\sixbf|
+| \scriptscriptfont\bffam=\fivebf \def\bf{\fam\bffam\eightbf}%|
+| \tt \ttglue=.5em plus.25em minus.15em|
+| \normalbaselineskip=9pt|
+| \setbox\strutbox=\hbox{\vrule height7pt depth2pt width0pt}%|
+| \let\sc=\sixrm \let\big=\eightbig \normalbaselines\rm}|
+\smallbreak
+|\def\tenbig#1{{\hbox{$\left#1\vbox to8.5pt{}\right.\n@space$}}}|
+|\def\ninebig#1{{\hbox{$\textfont0=\tenrm\textfont2=\tensy|
+| \left#1\vbox to7.25pt{}\right.\n@space$}}}|
+|\def\eightbig#1{{\hbox{$\textfont0=\ninerm\textfont2=\ninesy|
+| \left#1\vbox to6.5pt{}\right.\n@space$}}}|
+|\def\tenmath{\tenpoint\fam-1 } % for 10-point math in 9-point territory|
+\endlines
+
+Issues of page layout are dealt with next. First, the basics:^^|pc|
+\beginlines
+^|\newdimen||\pagewidth \newdimen\pageheight \newdimen\ruleht|
+^|\hsize||=29pc |^|\vsize||=44pc |^|\maxdepth||=2.2pt |^|\parindent||=3pc|
+|\pagewidth=\hsize \pageheight=\vsize \ruleht=.5pt|
+^|\abovedisplayskip||=6pt plus 3pt minus 1pt|
+^|\belowdisplayskip||=6pt plus 3pt minus 1pt|
+^|\abovedisplayshortskip||=0pt plus 3pt|
+^|\belowdisplayshortskip||=4pt plus 3pt|
+\endlines
+(The curious value of\/ |\maxdepth| was chosen only to provide an example
+in Chapter~15; there's no deep reason behind it.)
+
+When the author prepared this book, he made notes about what things ought
+to go into the index from each page. These notes were shown in small type
+on his proofsheets, like the words `^{marginal hacks}' in the right
+\insert\margin{\hbox{\marginstyle mar-}}%
+\insert\margin{\hbox{\marginstyle ginal}}%
+\insert\margin{\hbox{\marginstyle hacks}}%
+margin of this page. The |manmac| format uses an insertion class called
+^|\margin| to handle such notes.
+\beginlines
+^|\newinsert||\margin|
+|\dimen\margin=\maxdimen % no limit on the number of marginal notes|
+|\count\margin=0 \skip\margin=0pt % marginal inserts take up no space|
+\endlines
+
+The ^|\footnote| macro of plain \TeX\ needs to be changed because
+^{footnotes} are indented and set in 8-point type. Some simplifications have
+also been made, since footnotes are used so infrequently in this book.
+\beginlines
+|\def\footnote#1{\edef\@sf{\spacefactor\the\spacefactor}#1\@sf|
+| \insert\footins\bgroup\eightpoint|
+| \interlinepenalty100 \let\par=|^|\endgraf|
+| \leftskip=0pt \rightskip=0pt|
+| \splittopskip=10pt plus 1pt minus 1pt \floatingpenalty=20000|
+| \smallskip|^|\item||{#1}\bgroup\strut\aftergroup\@foot\let\next}|
+|\skip\footins=12pt plus 2pt minus 4pt % space added when footnote exists|
+|\dimen\footins=30pc % maximum footnotes per page|
+\endlines
+
+The text of ^{running headlines} will be kept in a control sequence
+called |\rhead|. Some pages should not have headlines; the |\titlepage|
+macro suppresses the headline on the next page that is output.
+\beginlines
+^|\newif||\iftitle|
+|\def\titlepage{\global\titletrue} % for pages without headlines|
+|\def\rhead{} % \rhead contains the running headline|
+\smallskip
+|\def\leftheadline{\hbox to \pagewidth{|^|\spaceskip||=0pt|
+| \vbox to 10pt{}% strut to position the baseline|^^{strut}
+| |^|\llap||{\tenbf|^|\folio|^|\kern||1pc}% folio to left of text|
+| \tenit\rhead\hfil}} % running head flush left|
+|\def\rightheadline{\hbox to \pagewidth{\spaceskip=0pt\vbox to 10pt{}%|
+| \hfil\tenit\rhead\/% running head flush right|
+| |^|\rlap||{\kern1pc\tenbf\folio}}} % folio to right of text|
+\endlines
+
+Pages are shipped to the output by the |\onepageout| macro, which
+attaches headlines, marginal notes, and/or footnotes, as appropriate.
+Special ^{registration marks} are typeset at the top of title pages, so
+that the pages will line up properly on printing plates that are made
+photographically from \TeX's ``camera-ready'' output. ^^{camera alignment}
+A small page number is also printed next to the corner markings; such
+auxiliary information will, of course, be erased before the pages are
+actually printed.
+\beginlines
+|\def\onepageout#1{\shipout\vbox{ % here we define one page of output|
+| |^|\offinterlineskip|| % butt the boxes together|
+| \vbox to 3pc{ % this part goes on top of the 44pc pages|
+| \iftitle \global\titlefalse \setcornerrules|
+| \else|^|\ifodd||\pageno\rightheadline\else\leftheadline\fi\fi \vfill}|
+| \vbox to \pageheight{|
+| |^|\ifvoid||\margin\else % marginal info is present|
+| |^|\rlap||{\kern31pc\vbox to0pt{\kern4pt\box\margin \vss}}\fi|
+| #1 % now insert the main information|
+| \ifvoid\footins\else % footnote info is present|
+| \vskip\skip\footins \kern-3pt|
+| \hrule height\ruleht width\pagewidth \kern-\ruleht \kern3pt|
+| \unvbox\footins\fi|
+| \boxmaxdepth=\maxdepth}}|
+| |^|\advancepageno||}|
+\smallbreak
+|\def\setcornerrules{\hbox to \pagewidth{% for camera alignment|
+| \vrule width 1pc height\ruleht \hfil \vrule width 1pc}|
+| \hbox to \pagewidth{|^|\llap||{\sevenrm(page \folio)\kern1pc}%|
+| \vrule height1pc width\ruleht depth0pt|
+| \hfil \vrule width\ruleht depth0pt}}|
+\smallbreak
+^|\output||{\onepageout{|^|\unvbox||255}}|
+\endlines
+
+A different ^{output routine} is needed for Appendix~I (the index), because
+most of that appendix appears in ^{two-column format}. Instead of handling
+double columns with an `|\lr|' switch, as discussed in Chapter~23,
+|manmac| does the job with ^|\vsplit|, after collecting more than
+enough material to fill a page. This approach makes it comparatively
+easy to ^{balance the columns} on the last page of the index. A~more
+difficult approach would be necessary if the index contained
+insertions (e.g., footnotes); fortunately, it doesn't.
+Furthermore, there is no need to use ^|\mark| as suggested in the
+index example of Chapter~23, since the entries in Appendix~I tend to
+be quite short. The only real complication that |manmac| faces
+is the fact that Appendix~I begins and ends with single-column format;
+partial pages need to be juggled carefully as the format changes back and forth.
+\beginlines
+^|\newbox||\partialpage|
+|\def\begindoublecolumns{\begingroup|
+| \output={\global\setbox\partialpage=\vbox{\unvbox255\bigskip}}\eject|
+| \output={\doublecolumnout} |^|\hsize||=14pc |^|\vsize||=89pc}|
+|\def\enddoublecolumns{\output={\balancecolumns}\eject|
+| \endgroup \pagegoal=\vsize}|
+\smallbreak
+|\def\doublecolumnout{\splittopskip=\topskip |^|\splitmaxdepth||=\maxdepth|
+| \dimen@=44pc \advance\dimen@ by-\ht\partialpage|
+| \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@|
+| \onepageout\pagesofar \unvbox255 \penalty|^|\outputpenalty||}|
+|\def\pagesofar{\unvbox\partialpage|
+| |^|\wd||0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}}|
+|\def\balancecolumns{\setbox0=\vbox{\unvbox255} \dimen@=\ht0|
+| \advance\dimen@ by\topskip \advance\dimen@ by-\baselineskip|
+| |^|\divide||\dimen@ by2 |^|\splittopskip||=\topskip|
+| {\vbadness=10000 |^|\loop|| \global\setbox3=\copy0|
+| \global\setbox1=\vsplit3 to\dimen@|
+| |^|\ifdim|^|\ht||3>\dimen@ \global\advance\dimen@ by1pt \repeat}|
+| \setbox0=\vbox to\dimen@{\unvbox1} \setbox2=\vbox to\dimen@{\unvbox3}|
+| \pagesofar}|
+\endlines
+The balancing act sets ^|\vbadness| infinite while it is searching for
+a suitable column height, so that ^{underfull} vboxes won't be reported
+unless the actual columns are bad after balancing. The columns in Appendix~I
+have a lot of stretchability, since there's a ^|\parskip|
+of |0pt| |plus|~|.8pt| between adjacent entries, and since there is room for
+more than 50 lines per column; therefore the |manmac| balancing routine tries
+to make both the top and bottom baselines agree at the end of the index.
+In applications where the glue is not so flexible it would be more
+appropriate to let the right-hand column be a little short; the best
+way to do this is probably to replace the command `|\unvbox3|' by
+`|\dimen2=|^|\dp||3| |\unvbox3| |\kern-\dimen2| ^|\vfil|'.
+
+The next macros are concerned with chapter formatting. Each chapter in the
+manuscript file starts out with the macro ^|\beginchapter|; it ends
+with ^|\endchapter| and two ^{quotations}, ^^{epigraphs} followed
+by ^|\eject|. For example, Chapter~15 was generated by \TeX\ commands
+that look like this in the file |manual.tex|:
+\beginlines
+|\beginchapter Chapter 15. How \TeX\ Makes\\Lines into Pages|
+\medskip
+|\TeX\ attempts to choose desirable places to divide your document into|
+\qquad\dots\quad (about 1100 lines of the manuscript are omitted here)
+|break. \ (Deep breath.) \ You got that?|
+\medskip
+|\endchapter|
+\medskip
+|Since it is impossible to foresee how [footnotes] will happen to come out|
+|in the make-up, it is |%
+ |impracticable to number them from 1 up on each page.|\kern-1em
+|The best way is to number them consecutively throughout an article|
+|or by chapters in a book.|
+|\author UNIVERSITY OF CHICAGO PRESS, {\sl Manual of Style\/} (1910)|
+\medskip^^{CHICAGO}^^{KNUTH}
+|\bigskip|
+\medskip
+|Don't use footnotes in your books, Don.|
+|\author JILL KNUTH (1962)|
+\medskip
+|\eject|
+\endlines
+The `|\\|' in the title line specifies a line break to be used on the
+left-hand title page that faces the beginning of the chapter. Most of the
+|\beginchapter| macro is devoted to preparing that title page; the
+^|\TeX| logo needs somewhat different spacing when it is typeset in
+|\titlefont|, and the |\inchhigh| digits need to be brought closer
+together in order to look right in a title.
+\beginlines
+^|\newcount||\exno % for the number of exercises in the current chapter|
+|\newcount\subsecno % for the number of subsections in the current chapter|
+\smallbreak
+^|\outer||\def\beginchapter#1 #2#3. #4\par{\def\chapno{#2#3}|
+| \global\exno=0 \subsecno=0|
+| \ifodd\pageno|
+| |^|\errmessage||{You had too much text on that last page; I'm backing up}|
+| \advance\pageno by-1 \fi|
+| \def\\{ } % \\'s in the title will be treated as spaces|
+| |^|\message||{#1 #2#3:} % show the chapter title on the terminal|
+| |^|\xdef||\rhead{#1 #2#3: #4\unskip} % establish a new running headline|
+| {\def\TeX{T\kern-.2em\lower.5ex\hbox{E}\kern-.06em X}|
+| \def\\{#3} \advance\hsize by -18mm|
+| |^|\ifx||\empty\\ \rightline{\inchhigh #2\kern-.04em}|
+| \else\rightline{\inchhigh #2\kern-.06em#3\kern-.04em}\fi|
+| \vskip1.75pc \baselineskip=36pt \lineskiplimit=1pt \lineskip=12pt|
+| \let\\=|^|\cr|| % now the \\'s are line dividers|
+| \halign{\line{\titlefont\hss##}\\#4\unskip\\}|
+| \titlepage\vfill\eject} % output the chapter title page|
+| \tenpoint\noindent\ignorespaces} % the first paragraph is not indented|
+\endlines
+
+An extra page is ejected at the end of a chapter, if necessary, so
+that the closing quotations will occur on a right-hand page. \ (The
+logic for doing this is not perfect, but it doesn't need to be, because
+it fails only when the chapter has to be shortened or lengthened anyway;
+book preparation with \TeX, as with type, encourages interaction between
+humans and machines.) \
+The lines of the quotations are set ^{flush right} by using
+^|\obeylines| together with a stretchable ^|\leftskip|:
+\beginlines
+|\outer\def\endchapter{\ifodd\pageno \else\vfill|^|\eject||\null\fi|
+| \begingroup\bigskip\vfill % beginning of the quotes|
+| \def\eject{|^|\endgroup||\eject} % ending of the quotes|
+| \def\par{\ifhmode\/\endgraf\fi}\obeylines|
+| \def|^|\TeX||{T\kern-.2em\lower.5ex\hbox{E}X}|
+| \eightpoint \let\tt=\ninett \baselineskip=10pt |^|\interlinepenalty||=10000|
+| \leftskip=0pt plus 40pc minus \parindent |^|\parfillskip||=0pt|
+| \let|^|\rm||=\eightss \let|^|\sl||=\eightssi \everypar{\sl}}|
+|\def\author#1(#2){\smallskip\noindent\rm--- #1|^|\unskip|^|\enspace||(#2)}|
+\endlines
+
+We come now to what goes on inside the chapters themselves. Dangerous and
+doubly dangerous bends are specified by typing `^|\danger|' or `^|\ddanger|'
+just before a paragraph that is supposed to display a warning symbol:
+\beginlines
+|\def\dbend{{\manual\char127}} % "dangerous bend" sign|^^{dangerous bend}
+|\def\d@nger{\medbreak|^|\begingroup|^|\clubpenalty||=10000|
+| \def\par{|^|\endgraf|^|\endgroup|^|\medbreak||} |%
+ ^|\noindent|^|\hang|^|\hangafter||=-2|
+| \hbox to0pt{\hskip-\hangindent\dbend\hfill}|^|\ninepoint||}|
+^|\outer||\def\danger{\d@nger}|
+|\def\dd@nger{\medbreak\begingroup\clubpenalty=10000|
+| \def\par{\endgraf\endgroup\medbreak} \noindent\hang\hangafter=-2|
+| \hbox to0pt{\hskip-\hangindent\dbend\kern1pt\dbend\hfill}\ninepoint}|
+|\outer\def\ddanger{\dd@nger}|
+|\def\enddanger{\endgraf\endgroup} % omits the \medbreak|
+\endlines
+(It's necessary to type `|\enddanger|' at the end of a dangerous bend
+only in rare cases that a medium space is not desired after the paragraph;
+e.g., `|\smallskip|^|\item|' might be used to give an itemized list within
+the scope of the dangerous bend sign.)
+
+A few chapters and appendices of this book (e.g., Chapter~18 and Appendix~B)
+are divided into numbered subsections. Such subsections are specified
+in the manuscript by typing, for example,
+\begintt
+\subsection Allocation of registers.
+\endtt
+Appendix A is subdivided in another way, by paragraphs that have answer numbers:
+\beginlines
+|\outer\def\subsection#1. {\medbreak\advance\subsecno by 1|
+| \noindent{\it \the\subsecno.\enspace#1.\enspace}}|
+|\def\ansno#1.#2:{\medbreak\noindent|
+| \hbox to\parindent{\bf\hss#1.#2.\enspace}\ignorespaces}|
+\endlines
+We will see below that the manuscript doesn't actually specify an |\ansno|
+directly; each call of\/ |\ansno| is generated automatically by the
+|\answer| macro.
+
+Appendix H points out {\sl The \TeX book\/} calls for
+three hyphenation exceptions:
+\beginlines
+^|\hyphenation||{man-u-script man-u-scripts ap-pen-dix}|
+\endlines
+
+A few macros in |manmac| provide special constructions that are occasionally
+needed in paragraphs: |\MF| for ^^{METAFONT}`\MF', |\AmSTeX| for ^^{AmSTeX}
+`\AmSTeX', ^|\bull| for ^^{square bullet} `\bull', |\dn| and |\up| for
+`\dn' and `\up', |\|\| and |\]| for `\|' and ^^{visible space} `\]'. ^^{the
+visible space symbol} To typeset
+\begindisplay
+$3\pt$ of \<stuff>, $\oct{105}=69$, $\hex{69}=105$, \cstok{wow}
+\enddisplay
+the manuscript says ^^{lxix}^^{octal constant}^^{hexadecimal constant}
+^^{angle brackets} % cf. The Hacker's Dictionary
+\begintt
+$3\pt$ of \<stuff>, $\oct{105}=69$, $\hex{69}=105$, \cstok{wow}
+\endtt
+using the macros |\pt|, |\<|, |\oct|, |\hex|, and |\cstok|.
+\beginlines
+|\def\MF{{\manual META}\-{\manual FONT}}|
+|\def\AmSTeX{$\cal A\kern-.1667em\lower.5ex\hbox{$\cal M$}\kern-.075em|
+| S$-\TeX}|
+|\def\bull{\vrule height .9ex width .8ex depth -.1ex } % square bullet|
+|\def\SS{{\it SS}} % scriptscript style|
+|\def\dn{\leavevmode\hbox{\tt\char'14}} % downward arrow|
+|\def\up{|^|\leavevmode||\hbox{\tt\char'13}} % upward arrow|
+|\def\|\||{\leavevmode\hbox{\tt\char`\|\||}} % vertical line|
+|\def\]{\leavevmode\hbox{\tt\char`\ }} % visible space|
+\smallbreak
+|\def\pt{\,{\rm pt}} % units of points, in math formulas|
+|\def\<#1>{\leavevmode\hbox{$\langle$#1\/$\rangle$}} % syntactic quantity|
+|\def\oct#1{\hbox{\rm\'{}\kern-.2em\it#1\/\kern.05em}} % octal constant|
+|\def\hex#1{\hbox{\rm\H{}\tt#1}} % hexadecimal constant|
+|\def\cstok#1{\leavevmode\thinspace\hbox{|^|\vrule||\vtop{\vbox{\hrule\kern1pt|
+| \hbox{\vphantom{\tt/}\thinspace{\tt#1}\thinspace}}|
+| \kern1pt|^|\hrule||}\vrule}\thinspace} % control sequence token|
+\endlines
+
+^{Displays} in this book are usually indented rather than centered,
+and they usually involve text rather than mathematics. The
+|manmac| format makes such displays convenient by introducing
+two macros called |\begindisplay| and |\enddisplay|; there's
+also a pair of macros |\begintt| and |\endtt| for displays that
+are entirely in ^{typewriter type}. The latter displays are copied
+^{verbatim} from the manuscript file, without interpreting
+symbols like |\| or |$| in any special way. For example,
+part of the paragraph above was typed as follows:
+\begintt
+... To typeset
+\begindisplay
+$3\pt$ of \<stuff>, $\oct{105}=69$, $\hex{69}=105$, \cstok{wow}
+\enddisplay
+the manuscript says
+\begintt
+$3\pt$ of \<stuff>, $\oct{105}=69$, $\hex{69}=105$, \cstok{wow}
+|char`|\endtt
+using the macros ||\pt||, ||\<||, ||\oct||, ||\hex||, and ||\cstok||.
+\endtt
+(The last line of this example illustrates the fact
+that verbatim typewriter text can be obtained within a
+paragraph by using vertical lines as brackets.) \ The |\begindisplay|
+macro is actually more general than you might expect from this example;
+it allows multiline displays, with ^|\cr| following each line, and
+it also allows local definitions (which apply only within the display) to
+be specified immediately after |\begindisplay|.
+\beginlines
+|\outer\def\begindisplay{\obeylines\startdisplay}|
+|{\obeylines\gdef\startdisplay#1|
+| {\catcode`\^^M=5$$#1\halign|^|\bgroup||\indent##\hfil&&\qquad##\hfil\cr}}|
+|\outer\def\enddisplay{|^|\crcr|^|\egroup||$$}|^^{dollardollar}
+\smallbreak
+^|\chardef|^|\other||=12|
+|\def\ttverbatim{\begingroup \catcode`\\=\other \catcode`\{=\other|
+| \catcode`\}=\other \catcode`\$=\other \catcode`\&=\other|
+| \catcode`\#=\other \catcode`\%=\other \catcode`\~=\other|
+| \catcode`\_=\other \catcode`\^=\other|
+| \obeyspaces \obeylines \tt}|
+|{|^|\obeyspaces||\gdef {\ }} % \obeyspaces now gives \ , not \space|
+\smallbreak
+|\outer\def\begintt{$$\let\par=\endgraf \ttverbatim \parskip=0pt|
+| \catcode`\|\||=0 |^|\rightskip||=-5pc \ttfinish}|
+|{\catcode`\|\||=0 |\||catcode`|\||\=\other % |\|%
+ | is temporary escape character|
+| |\||obeylines % end of line is active|
+| |\||gdef|\||ttfinish#1^^M#2\endtt{#1|\||vbox{#2}|\||endgroup$$}}|
+\smallbreak
+^|\catcode||`\|\||=|^|\active|
+|{\obeylines\gdef|\||{\ttverbatim\spaceskip=\ttglue\let^^M=\ \let|\|%
+ |=\endgroup}}|\kern-.3pt
+\endlines
+These macros are more subtle than the others in this appendix, and they
+deserve careful study because they illustrate how to disable \TeX's
+normal formatting. The `\|' character is normally active (category~13)
+in |manmac| format, and its appearance causes the |\ttverbatim| macro to
+make all of the other unusual characters into normal symbols (category~12).
+However, within the scope of\/ |\begintt...\endtt| a vertical line is an
+^{escape character} (category~0); this permits an escape out of verbatim mode.
+
+The |\begintt| macro assumes that a comparatively small amount of
+text will be displayed; the verbatim lines are put into a vbox, so
+that they cannot be broken between pages. A different approach has
+been used for most of the typewriter copy in this appendix and in
+Appendix~B\null: Material that is quoted from a format file is
+delimited by |\beginlines| and |\endlines|, between which it
+is possible to give commands like `^|\smallbreak|' to help with
+spacing and page breaking. The |\beginlines| and |\endlines| macros
+also insert rules, fore and aft:
+\beginlines
+|\def\beginlines{\par\begingroup\nobreak\medskip\parindent=0pt|
+\nobreak
+| |^|\hrule||\kern1pt\nobreak \obeylines |^|\everypar||{\strut}}|
+|\def\endlines{\kern1pt\hrule\endgroup\medbreak\noindent}|
+\endlines
+For example, the previous three lines were typeset by the specification
+\begintt
+\beginlines
+||\def\beginlines{\par\begingroup\nobreak\medskip\parindent=0pt||
+\nobreak
+|| \hrule\kern1pt\nobreak \obeylines \everypar{\strut}}||
+||\def\endlines{\kern1pt\hrule\endgroup\medbreak\noindent}||
+\endlines
+\endtt
+A ^{strut} is placed in each line so that the rules will be positioned
+properly. The |manmac| format also has macros |\beginmathdemo...\endmathdemo|
+that were used to produce examples of mathematics in Chapters 16--19,
+|\beginsyntax...\endsyntax| for the formal syntax in Chapters 24--26,
+|\beginchart...\endchart| for the font tables in Appendices C and~F\null, etc.;
+those macros are comparatively simple and they need not be shown here.
+
+Exercises are specified by an |\exercise| macro; for example, the first
+exercise in Chapter~1 was generated by the following lines in the
+manuscript:
+\begintt
+\exercise After you have mastered the material in this book,
+what will you be: a \TeX pert, or a \TeX nician?
+\answer A \TeX nician (underpaid); sometimes also called
+a \TeX acker.
+\endtt
+Notice that the |\answer| is given immediately after each exercise;
+that makes it easy to insert new exercises or to delete old ones,
+without keeping track of exercise numbers. Exercises that are dangerous
+or doubly dangerous are introduced by the macros |\dangerexercise|
+and~|\ddangerexercise|.
+\beginlines
+|\outer\def\exercise{\medbreak \global\advance\exno by 1|
+| \noindent|^|\llap||{\manual\char'170\rm\kern.15em}% triangle in margin|
+| {\ninebf EXERCISE \bf\chapno.\the\exno}\par\nobreak\noindent}|
+|\def\dexercise{\global\advance\exno by 1|
+| \llap{\manual\char'170\rm\kern.15em}% triangle in indented space|
+| {\eightbf EXERCISE \bf\chapno.\the\exno}\hfil\break}|
+|\outer\def\dangerexercise{\d@nger \dexercise}|
+|\outer\def\ddangerexercise{\dd@nger \dexercise}|
+\endlines
+(The last two lines use |\d@nger| and |\dd@nger|, which are non-|\outer|
+equivalents of\/ |\danger| and |\ddanger|; such duplication is necessary
+because control sequences of type ^|\outer| cannot appear within a |\def|.)
+
+The |\answer| macro copies an answer into a file called
+|answers.tex|; then Appendix~A reads this
+file by saying `^|\immediate|^|\closeout||\ans| |\ninepoint|
+^|\input| |answers|'. Each individual answer ends with a blank line;
+thus, |\par| must be used between the paragraphs of a long answer.
+\beginlines
+^|\newwrite||\ans|
+|\immediate|^|\openout||\ans=answers % file for answers to exercises|
+|\outer\def\answer{\par\medbreak|
+| \immediate|^|\write||\ans{}|
+| \immediate\write\ans{\string\ansno\chapno.|^|\the||\exno:}|
+| \copytoblankline}|
+|\def\copytoblankline{\begingroup\setupcopy\copyans}|
+|\def\setupcopy{\def\do##1{\catcode`##1=\other}\dospecials|
+| \catcode`\|\||=\other \obeylines}|
+|{\obeylines \gdef\copyans#1|
+| {\def\next{#1}%|
+| \ifx\next\empty\let\next=\endgroup %|
+| \else\immediate\write\ans{\next} \let\next=\copyans\fi\next}}|
+\endlines
+Notice the use of\/ ^|\dospecials| here, to set up the ^{verbatim copying}.
+The |\ttverbatim| macro could have invoked |\dospecials| in the same way;
+^^{efficiency}
+but |\ttverbatim| is used quite frequently, so it was streamlined for speed.
+
+The remaining macros in |manmac| format are designed to help in producing
+a good ^{index}. When a paragraph contains a word or group of words that
+deserve to be indexed, the manuscript indicates this by inserting
+|^{...}|; for example, the first sentence of the present paragraph
+actually ends with `|a good ^{index}|'. This caused an appropriate
+entry to be written onto a file |index.tex| when \TeX\ was typesetting
+the page; it also put the word `{\sevenrm index}' into the margin
+of the proofsheets, so that the author could remember what
+had been marked for indexing without looking into the manuscript file.
+Indexing with the |^{...}| notation doesn't change \TeX's behavior in
+any essential way; thus, the word `index' appears
+in the text as well as in the index. ^^{strut}
+\beginlines
+^|\newwrite||\inx|
+^|\immediate|^|\openout||\inx=index % file for index reminders|
+|\def\marginstyle{\sevenrm % marginal notes are in 7-point type|
+| \vrule height6pt depth2pt width0pt } % a strut for \insert\margin|
+\endlines
+
+Sometimes it is desirable to index words that don't actually appear on the
+page; the notation |^^{...}| stands for a ``silent'' index entry, and
+spaces are ignored after the closing `|}|' in such a case. For example,
+Appendix~I lists page~1 under `beauty', even though page~1 contains only
+the word `beautiful'; the manuscript achieves this by saying
+`|beautiful ^^{beauty}|'. \ (The author felt that it was important to
+index `beauty' because he had already indexed `truth'.)
+
+It's not difficult to make |^| into an active character that produces
+such index entries, while still retaining its use for superscripts in
+math formulas, because ^|\ifmmode| can be used to test whether a
+control sequence is being used in math mode. However, |manmac|'s use
+of\/ |^| as an active character means that |^^M| cannot be used to refer
+^^{hat as an active character} ^^{hat hat}
+to a \<return> character. Fortunately the |^^M| notation isn't needed
+except when the formatting macros themselves are being defined.
+
+The following macros set things up so that |^| and |^^| are respectively
+converted to |\silentfalse\xref| and |\silenttrue\xref|, outside of
+math mode:
+\beginlines
+^|\newif||\ifsilent|
+|\def\specialhat{\ifmmode\def\next{^}\else\let\next=\beginxref\fi\next}|
+|\def\beginxref{|^|\futurelet||\next\beginxrefswitch}|
+|\def\beginxrefswitch{\ifx\next\specialhat\let\next=\silentxref|
+| \else\silentfalse\let\next=\xref\fi \next}|
+|\catcode`\^=\active \let ^=\specialhat|
+|\def\silentxref^{\silenttrue\xref}|
+\endlines
+
+Entries in the index aren't always words in roman type; they might require
+special typesetting conventions. For example, there are
+hundreds of items in Appendix~I that are preceded by a backslash and
+set in typewriter type. The |manmac| format makes it easy to produce
+such entries by typing, e.g., `|^|\||\immediate|\|' instead of
+`|^{|\||\immediate|\||}|'. In this case the backslash is not written
+onto the |index| file, because it would interfere with alphabetization
+of the entries; a code number is written out so that the backslash can
+be reinstated after the index has been sorted. The code number also
+is used to put the entry in typewriter type.
+
+The indexing macros of |manmac| produce entries of four kinds, which are
+assigned to codes 0,~1, 2, and~3. Code~0 applies when the argument is enclosed
+in braces, e.g., `|^{word}|'\thinspace; code~1 applies when the argument
+is enclosed in vertical lines and there's no backslash, e.g.,
+`|^|\||plus|\|'\thinspace; code~2 is similar but with a backslash, e.g.,
+`|^|\||\par|\|'\thinspace; code~3 applies when the argument is enclosed in
+angle brackets, e.g., `|^\<stuff>|'. The four example entries in the previous
+sentence will be written on file |index.tex| in the form\strut
+\begintt
+word !0 123.
+plus !1 123.
+par !2 123.
+stuff !3 123.
+|kern-3pt
+\endtt
+if they appear on page 123 of the book.
+\beginlines
+|\chardef\bslash=`\\ % \bslash makes a backslash (in tt fonts)|
+|\def\xref{\futurelet\next\xrefswitch} % branch on the next character|
+|\def\xrefswitch{\begingroup\ifx\next|\||\aftergroup\vxref|
+| \else\ifx\next\<\aftergroup\anglexref|
+| \else\aftergroup\normalxref \fi\fi \endgroup}|
+|\def\vxref|\||{\catcode`\\=\active \futurelet\next\vxrefswitch}|
+|\def\vxrefswitch#1|\||{\catcode`\\=0|
+| \ifx\next\empty\def\xreftype{2}%|
+| \def\next{{\tt\bslash\text}}% code 2, |\||\arg|\|
+| \else\def\xreftype{1}\def\next{{\tt\text}}\fi % code 1, |\||arg|\|
+| \edef\text{#1}\makexref}|
+|{|^|\catcode||`\|\||=0 \catcode`\\=\active |\||gdef\{}}|
+|\def\anglexref\<#1>{\def\xreftype{3}\def\text{#1}%|
+| \def\next{\<\text>}\makexref} % code 3, \<arg>|
+|\def\normalxref#1{\def\xreftype{0}\def\text{#1}\let\next=\text\makexref}|
+\endlines
+
+Indexing is suppressed unless the |proofmode| switch is set to true,
+since material is gathered for the index only during trial runs---not
+on the triumphant occasion when the book is finally being printed.
+\beginlines
+|\newif\ifproofmode|
+|\proofmodetrue % this should be false when making camera-ready copy|
+|\def\makexref{\ifproofmode\insert\margin{\hbox{\marginstyle\text}}%|
+| |^|\xdef||\writeit{|^|\write||\inx{\text\space!\xreftype\space|
+| |^|\noexpand|^|\number||\pageno.}}\writeit|
+| \else\ifhmode\kern0pt \fi\fi|
+| \ifsilent|^|\ignorespaces||\else\next\fi}|
+\endlines
+(The ^|\insert| suppresses ^{hyphenation} when proofs are being checked;
+a ^|\kern||0pt| is therefore emitted to provide consistent behavior
+in the other case.)
+
+The material that accumulates on file |index.tex| gives a good first
+approximation to an index, but it doesn't contain enough information to do
+the whole job; a topic often occurs on several pages, but only the first
+of those pages is typically listed in the file. The author ^^{Knuth}
+prefers not to generate indexes automatically; he likes to reread his
+books as he checks the cross-references, thereby having the opportunity to
+rethink everything and to catch miscellaneous errors before it is too
+late. As a result, his books tend to be delayed, but the indexes tend to
+be pretty good. Therefore he designed the indexing scheme of |manmac| to
+provide only the clues needed to make a real index. On the other hand,
+it would be possible to extend the macros above and to obtain a comprehensive
+system that generates an excellent index with no subsequent human intervention;
+see, for example, ``An indexing facility for \TeX'' by Terry ^{Winograd}
+and Bill ^{Paxton}, in {\sl ^{TUGboat}\/ \bf1} (1980), A1--A12.
+
+The |manmac| macros have now been fully presented; we shall close this
+appendix by presenting one more example of their use. Chapter~27 mentions
+the desirability of creating a long book in small parts, by using a
+``^{galley}'' file. The author adopted that strategy for {\sl The \TeX book},
+entering each chapter into a small file |galley.tex| that looked like this:
+\beginlines
+|\input manmac|
+|\tenpoint|
+|\pageno=800|
+|\def\rhead{Experimental Pages for The \TeX book}|
+|\def\chapno{ X}|
+|{\catcode`\%=12 \immediate\write\ans{% Answers for galley proofs:}}|
+\qquad\vdots
+\<new text being tested, usually an entire chapter>
+\qquad\vdots
+| |
+|% that blank line will stop an unfinished \answer|
+|\immediate\closeout\ans|
+|\vfill\eject|
+|\ninepoint \input answers % typeset the new answers, if any|
+|\bye|
+\endlines
+
+\endchapter
+
+It is much easier to use macros than to define them.
+$\ldots$
+The use of macro libraries, in fact, mirrors almost exactly
+the use of subroutine libraries for programming languages.
+There are the same levels of specialization,
+from publicly shared subroutines
+to special subroutines within a single program,
+and there is the same need for a programmer
+with particular skills to define the subroutines.
+\author PETER ^{BROWN}, {\sl Macro Processors\/} (1974) % p10
+
+\bigskip
+
+The ^{epigraph} is among the most delightful of scholarly habits.
+Donald ^{Knuth}'s work on fundamental algorithms would be
+just as important if he hadn't begun with a quotation
+from Betty ^{Crocker}, but not so enjoyable.
+Part of the fun of an epigraph is turning a source to an unexpected use.
+\author MARY-CLAIRE ^{VAN LEUNEN}, {\sl A Handbook for Scholars\/} (1978)
+ % page 53. [But it was McCall's, not Betty Crocker]
+
+\eject
+ \beginchapter Appendix F. Font Tables
+
+The purpose of this appendix is to summarize the chief characteristics of
+the ^{Computer Modern} typefaces. \TeX\ is able to typeset
+documents with any fonts, having any arrangement of characters; the
+fonts and layouts to be described here are the particular ones that
+correspond to plain~\TeX\ format, i.e., to the macros in Appendix~B\null.
+\ (Complete information about the Computer Modern family, including the
+\MF\ programs that draw the characters, can be found in the author's book
+{\sl Computer Modern Typefaces}.)
+^^{METAFONT}
+
+The first pages of this appendix show what the fonts contain; the last
+pages show what the symbols are called when they're used in math formulas.
+\ (See Appendix~B for the conventions that apply in non-mathematical text.)
+
+There are exactly 128 different characters in each of the Computer
+Modern fonts, although \TeX\ can work up to~256 characters per font.
+The text fonts are laid out as shown in the table below, which illustrates
+font |cmr10| (Computer Modern Roman 10~point). Thus, for example,
+if you ask for ^|\char||'35| when ^|cmr10| is the current font, you
+get the symbol \char'35. These text fonts include the ^{ligatures} and
+^{accents} described in Chapter~9; each symbol that happens to be
+a visible ASCII character appears in its ASCII position.
+ Some of the ASCII symbols (namely
+|"|~|<|~|>|~|\|~|_|~|{|~\|~|}|) are not included because
+they don't occur in normal printer's fonts. If you mistakenly
+type |"|, you get "; and |<| outside of math mode yields <\thinspace!
+\ Incidentally, the ten ^{digits} all have width |0.5em|.
+
+\medskip\vfill
+\noindent {\bf Figure 1.\enspace}Text font layout,
+showing |cmr10| (^|\rm|, |\textfont0|).
+\beginchart\tenrm
+\normalchart
+\endchart
+
+\noindent Plain \TeX\ makes use of sixteen basic fonts:
+$$\halign{\indent$\nulldelimiterspace=0pt
+ \left.\ninepoint\vcenter{#}\,\hfil\right\}$ &#\hfil\cr
+\halign{\strut\tt cm# \ \hfil&(Computer Modern #)\hfil&\hidebrace{#}\hfil\cr
+r10&Roman 10 point\cr
+r7&Roman 7 point\cr
+r5&Roman 5 point\cr
+bx10&Bold Extended 10 point\cr
+bx7&Bold Extended 7 point\cr
+bx5&Bold Extended 5 point\cr
+sl10&Slanted Roman 10 point\cr
+ti10&Text Italic 10 point\cr}&text\cr
+\halign{\strut\tt cm# \ \hfil&(Computer Modern #)\hfil&\hidebrace{#}\hfil\cr
+tt10&Typewriter Type 10 point\cr
+mi10&Math Italic 10 point\cr
+mi7&Math Italic 7 point\cr
+mi5&Math Italic 5 point\cr
+sy10&Math Symbols 10 point\cr
+sy7&Math Symbols 7 point\cr
+sy5&Math Symbols 5 point\cr
+ex10&Math Extension 10 point\cr}&special\cr}$$
+The first eight of these all have essentially the same layout;
+but |cmr5| needs no ligatures, and many of the symbols of |cmti10|
+have different shapes.
+For example, the ^{ampersand} becomes an `^{E.T.}', and the
+^{dollar} changes to ^{pound} ^{sterling}:
+
+\medskip\vfill
+\noindent {\bf Figure 2.\enspace}Text font layout,
+showing |cmti10| (^|\it|).
+\beginchart\tenit
+\normalchart
+\endchart
+
+\noindent The ^{typewriter font} ^|cmtt10| is almost like the fonts for
+ordinary text, but it includes all of the visible ASCII characters, in
+their correct positions. It also has vertical arrows \up\ and
+\dn, as well as an undirected single quote mark, {\tt\char'15}.
+Fourteen of the~128 positions are changed from the normal text layout
+conventions,
+namely codes \oct{013}--\oct{017}, \oct{040}, \oct{042}, \oct{074},
+\oct{076}, \oct{134}, \oct{137}, and \oct{173}--\oct{175}. All of the
+ligatures are absent, except for the Spanish |!`| and |?`|. \ (The
+characters for Spanish
+ligatures appear in different positions, but that makes no difference
+to the user, because each font tells \TeX\ where to locate its own
+ligatures.) \ The Polish \l, the d\.ot accent, and the
+long Hung\H arian umlaut have disappeared to make room for new symbols.
+In a sense, positions \oct{052} and \oct{055} also differ from the normal
+text conventions: The asterisk is not up as high as usual, and the hyphen is
+just like a minus sign.
+
+Each character in |cmtt10| has the same width, namely |0.5em|;
+the spaces between words also have this width, and they will not
+stretch or shrink. \TeX\ puts two spaces at the end of each sentence
+when you are typesetting with a typewriter font. \ (These spacing
+conventions can be changed by assigning nonzero values to
+^|\spaceskip| and ^|\xspaceskip|; or you can assign new values to the
+|\fontdimen| parameters, which will be described shortly.)
+
+\medskip\vfill
+\noindent {\bf Figure 3.\enspace}Typewriter text font layout,
+showing |cmtt10| (^|\tt|).
+\beginchart\tentt
+\normalchart
+\endchart
+
+\noindent You can see at a glance that the math italic font, ^|cmmi10|,
+is quite different from ^{text italic}. It contains lowercase Greek letters
+as well as uppercase ones; this, of course, is mathematicians' ^{Greek},
+not a text font that would be suitable for typesetting classical
+Greek literature. And if you look closely at the non-Greek italic
+letters, you will find that their proportions and spacing have been
+changed from |cmti10| to make them work better in \TeX's mathematics mode.
+
+Some special unslanted characters appear in positions \oct{050}--\oct{077}
+and \oct{133}--\oct{137}, including ``^{oldstyle numerals}'':
+`|$\mit1984$|' and `|$\oldstyle1984$|' both yield `$\mit1984$'. Some of the
+characters are intended to be combined with others; for example, \oct{054}
+forms the first part of the symbol `$\hookrightarrow$'. \ (See the definition
+of\/ ^|\hookrightarrow| in Appendix~B\null.) \ This portion of the font doesn't
+deserve the name math italic; it's really a resting place for characters
+that don't fit anywhere else. \ (The author didn't want to leave any places
+unfilled, since that would tempt people to create incompatible
+ways to fill them.)
+
+Plain \TeX\ takes its ^{comma}, ^{period}, and ^{slash} from |cmmi10| in
+math mode, so that appropriate kerning will be computed in certain
+formulas that would otherwise be spaced poorly. For the correct
+positioning of math accents with this font, you should set its
+^|\skewchar| to \oct{177}.
+
+\medskip\vfill
+\noindent {\bf Figure 4.\enspace}Math italic font layout,
+showing |cmmi10| (^|\mit|, |\textfont1|).
+\beginchart\teni
+\normalchart
+\endchart
+
+\noindent When \TeX\ typesets mathematics it assumes that family~0 contains
+normal roman fonts and that families 1, 2, and~3 contain math italic,
+math symbol, and math extension fonts. The special characters in these
+fonts are usually given symbolic names by a |\mathchardef|
+instruction, which assigns a hexadecimal code to the symbol.
+This code has four digits, where the first tells what kind of symbol
+is involved, the second specifies the family, and the other two give the font
+position. For example,
+\begintt
+\mathchardef\ll="321C
+\endtt
+says that ^|\ll| is character \hex{1C} of the math symbol font (family~2),
+and that it's a ``relation'' (class~3). A complete list of the symbolic names
+provided by the plain \TeX\ format appears later in this appendix.
+
+\smallskip
+Font ^|cmsy10| is plain \TeX's math symbol font, and it contains 128
+symbols laid out as shown below. Its ^|\skewchar| should be set to
+\oct{060} so that math accents will be positioned properly over the
+^{calligraphic capital letters}.
+
+\medskip\vfill
+\noindent {\bf Figure 5.\enspace}Math symbol font layout,
+showing |cmsy10| (^|\cal|, |\textfont2|).
+\beginchart\tensy
+\normalchart
+\endchart
+
+The final font of plain \TeX\ is ^|cmex10|, which includes large symbols
+and pieces that can be used to build even larger ones. For example,
+arbitrarily large left parentheses can be constructed by putting
+\oct{060} at the top and \oct{100} at the bottom,
+and by using as many copies of \oct{102} as necessary in the middle.
+Large ^{square root signs} are made from \oct{164}, \oct{165}, and
+\oct{166}; large left braces have four component parts:
+\oct{070}, \oct{072}, \oct{074}, \oct{076}.
+
+\medskip\vfill
+\noindent {\bf Figure 6.\enspace}^{Math extension font} layout,
+showing |cmex10| (|\textfont3|).
+\beginchart\tenex
+\normalchart
+\endchart
+
+\noindent When \TeX\ ``loads'' a font into its memory, it doesn't look at
+the actual shapes of the characters; it only loads the {\sl ^{font metric}\/}
+information (e.g., |cmr10.tfm|), which includes the heights, widths,
+depths, and italic corrections, together with information about ligatures
+and kerning. Furthermore, the metric information that comes with a font
+like |cmex10| tells \TeX\ that certain characters form a series; for
+example, all of the left parentheses are linked together in order of
+increasing size: \oct{000}, \oct{020}, \oct{022}, and \oct{040}, followed
+by the extensible left parenthesis, which is $\oct{060}+[\oct{102}]^n+
+\oct{100}$. Similarly, the two ^{summation signs} (\oct{120}, \oct{130})
+and the three ^|\widehat| accents (\oct{142}, \oct{143}, \oct{144}) are
+linked together. Appendix~G explains how \TeX\ goes about choosing
+particular sizes for math delimiters, math operators, and math accents.
+
+\smallskip
+Each font also has at least seven ^|\fontdimen| parameters, which have the
+following significance and typical values (rounded to two decimal places):
+$$\halign to\hsize{#\tabskip0pt plus 10pt&#\hfil&
+$\hfil#\pt$&
+$\hfil#\pt$&
+$\hfil#\pt$&
+$\hfil#\pt$&
+$\hfil#\pt$&
+$\hfil#\pt$\tabskip0pt\cr
+\hidewidth\#\hidewidth&Meaning&
+\omit\hidewidth Value in |cmr10| &
+\omit\hfil|cmbx10| &
+\omit\hfil|cmsl10| &
+\omit\hfil|cmti10| &
+\omit\hfil|cmtt10| &
+\omit\hfil|cmmi10| \cr
+\noalign{\vskip2pt}
+1&slant per pt&0.00&0.00&0.17&0.25&0.00&0.25\cr
+2&^{interword space}&3.33&3.83&3.33&3.58&5.25&0.00\cr
+3&interword stretch&1.67&1.92&1.67&1.53&0.00&0.00\cr
+4&interword shrink&1.11&1.28&1.11&1.02&0.00&0.00\cr
+5&^{x-height}&4.31&4.44&4.31&4.31&4.31&4.31\cr
+6&^{quad} width&10.00&11.50&10.00&10.22&10.50&10.00\cr
+7&extra space&1.11&1.28&1.11&1.02&5.25&0.00\cr}$$
+The ^{slant} parameter is used to position accents;
+the next three parameters define interword spaces when text is being typeset;
+the next two define the font-oriented dimensions |1ex| and |1em|;
+^^|ex| ^^|em|
+and the last is the additional amount that is added to interword spaces
+at the end of sentences
+(i.e., when ^|\spacefactor| is 2000 or~more and ^|\xspaceskip| is zero).
+When a font is ^{magnified} (using `^|at|' or `^|scaled|'), all of the
+parameters except the slant are subject to magnification at the time the
+font is loaded into \TeX's memory.
+
+Notice that |cmmi10| has zero spacing. This is the mark of a font
+that is intended only for mathematical typesetting; ^^{math fonts}
+the rules in Appendix~G state that the italic correction is added
+between adjacent characters from such fonts.
+
+Math symbol fonts (i.e., fonts in family 2) are required to have at least
+22~^|\fontdimen| parameters instead of the usual seven; similarly, math
+extension fonts must have at least~13. The significance of these
+additional parameters is explained in Appendix~G\null. If you want to increase
+the number of parameters past the number that actually appear in a font's
+metric information file, you can assign new values immediately after that font
+has been loaded. For example, if some font |\ff| with seven parameters
+has just entered \TeX's memory, the command |\fontdimen13\ff=5pt| will set
+parameter number~13 to $5\pt$; the intervening parameters, numbers 8--12,
+will be set to zero. You can even give more than seven parameters to
+^|\nullfont|, provided that you assign the values before any actual fonts
+have been loaded.
+
+\goodbreak
+Now that the font layouts have all been displayed, it's time to
+consider the names of the various mathematical symbols.
+^^{symbols in math, table}
+Plain \TeX\ defines more than 200 control sequences by which you can refer
+to math symbols without having to find their numerical positions in the
+layouts. It's generally best to call a symbol by its name, for then you can
+easily adapt your manuscripts to other fonts, and your manuscript will
+be much more readable.
+
+The symbols divide naturally into groups based on their mathematical
+class (Ord, Op, Bin, Rel, Open, Close, or Punct), so we shall follow
+that order as we discuss them. N.B.: Unless otherwise stated, math symbols
+are available only in math modes. For example, if you say `|\alpha|' in
+horizontal mode, \TeX\ will report an error and try to insert a |$| sign.
+
+\def\beginsymbols{$$\displayindent=16pt
+ \halign\bgroup
+ &\qquad\hbox to10pt{\hss$##$\hss}\enspace&\hbox to80pt{##\hss}\cr}
+\let\endsymbols=\enddisplay
+
+\subsection Lowercase Greek letters.
+^^|\alpha|^^|\iota|^^|\varrho|
+^^|\beta|^^|\kappa|^^|\sigma|
+^^|\gamma|^^|\lambda|^^|\varsigma|
+^^|\delta|^^|\mu|^^|\tau|
+^^|\epsilon|^^|\nu|^^|\upsilon|
+^^|\varepsilon|^^|\xi|^^|\phi|
+^^|\zeta|^^|\varphi|
+^^|\eta|^^|\pi|^^|\chi|
+^^|\theta|^^|\varpi|^^|\psi|
+^^|\vartheta|^^|\rho|^^|\omega|
+\beginsymbols
+\alpha&|\alpha|&\iota&|\iota|&\varrho&|\varrho|\cr
+\beta&|\beta|&\kappa&|\kappa|&\sigma&|\sigma|\cr
+\gamma&|\gamma|&\lambda&|\lambda|&\varsigma&|\varsigma|\cr
+\delta&|\delta|&\mu&|\mu|&\tau&|\tau|\cr
+\epsilon&|\epsilon|&\nu&|\nu|&\upsilon&|\upsilon|\cr
+\varepsilon&|\varepsilon|&\xi&|\xi|&\phi&|\phi|\cr
+\zeta&|\zeta|&o&|o|&\varphi&|\varphi|\cr
+\eta&|\eta|&\pi&|\pi|&\chi&|\chi|\cr
+\theta&|\theta|&\varpi&|\varpi|&\psi&|\psi|\cr
+\vartheta&|\vartheta|&\rho&|\rho|&\omega&|\omega|\cr
+\endsymbols
+There's no ^|\omicron|, because it would look the same as |o|. Notice
+that the letter |\upsilon|~($\upsilon$) is a bit wider than |v|~($v$); both
+of them should be distinguished from |\nu|~($\nu$). Similarly,
+|\varsigma|~($\varsigma$) should not be confused with |\zeta|~($\zeta$).
+It turns out that |\varsigma| and |\upsilon| are almost never used in
+math formulas; they are included in plain \TeX\ primarily because they are
+sometimes needed in short Greek citations (cf.~Appendix~J).
+
+\subsection Uppercase Greek letters.
+^^|\Gamma|^^|\Xi|^^|\Phi|
+^^|\Delta|^^|\Pi|^^|\Psi|
+^^|\Theta|^^|\Sigma|^^|\Omega|
+^^|\Lambda|^^|\Upsilon|
+\beginsymbols
+\Gamma&|\Gamma|&\Xi&|\Xi|&\Phi&|\Phi|\cr
+\Delta&|\Delta|&\Pi&|\Pi|&\Psi&|\Psi|\cr
+\Theta&|\Theta|&\Sigma&|\Sigma|&\Omega&|\Omega|\cr
+\Lambda&|\Lambda|&\Upsilon&|\Upsilon|\cr
+\endsymbols
+The other Greek capitals appear in the roman alphabet
+(^|\Alpha|${}\equiv{}$|{\rm A}|, ^|\Beta|${}\equiv{}$|{\rm B}|, etc.).
+It's conventional to use unslanted letters for uppercase Greek, and
+slanted letters for lowercase Greek; but you can obtain
+$(\mit\Gamma,\Delta,\ldots,\Omega)$ by typing
+|$({\mit\Gamma},| |{\mit\Delta},| |\ldots,| |{\mit\Omega})$|. ^^|\mit|
+
+\subsection Calligraphic capitals. To get the letters ^^{calligraphic letters}
+$\cal A\ldots Z$ that appear in Figure~5, type |${\cal A}\ldots{\cal Z}$|.
+Several other alphabets are also used with mathematics (notably ^{Fraktur},
+^{script}, and ``^{blackboard bold}''); they don't come with plain \TeX,
+but more elaborate formats like \AmSTeX\ do provide them.
+
+\subsection Miscellaneous symbols of type Ord.
+^^|\aleph|^^|\prime|^^|\forall|
+^^|\hbar|^^|\emptyset|^^|\exists|
+^^|\imath|^^|\nabla|^^|\neg|
+^^|\jmath|^^|\surd|^^|\flat|
+^^|\ell|^^|\top|^^|\natural|
+^^|\wp|^^|\bot|^^|\sharp|
+^^|\Re|^^{escvert}^^|\clubsuit|
+^^|\Im|^^|\angle|^^|\diamondsuit|
+^^|\partial|^^|\triangle|^^|\heartsuit|
+^^|\infty|^^|\backslash|^^|\spadesuit|
+^^{Weierstrass, see wp}
+\beginsymbols
+\aleph&|\aleph|&\prime&|\prime|&\forall&|\forall|\cr
+\hbar&|\hbar|&\emptyset&|\emptyset|&\exists&|\exists|\cr
+\imath&|\imath|&\nabla&|\nabla|&\neg&|\neg|\cr
+\jmath&|\jmath|&\surd&|\surd|&\flat&|\flat|\cr
+\ell&|\ell|&\top&|\top|&\natural&|\natural|\cr
+\wp&|\wp|&\bot&|\bot|&\sharp&|\sharp|\cr
+\Re&|\Re|&\Vert&|\|\|&\clubsuit&|\clubsuit|\cr
+\Im&|\Im|&\angle&|\angle|&\diamondsuit&|\diamondsuit|\cr
+\partial&|\partial|&\triangle&|\triangle|&\heartsuit&|\heartsuit|\cr
+\infty&|\infty|&\backslash&|\backslash|&\spadesuit&|\spadesuit|\cr
+\endsymbols
+The ^{dotless letters} |\imath| and |\jmath| should be used when $i$ and~$j$
+are ^{accent}ed; for example, |$\hat\imath$| yields $\hat\imath$.
+The |\prime| symbol is intended for use in subscripts and superscripts,
+as explained in Chapter~16, so you usually see it in a smaller size.
+On the other hand, the |\angle| symbol has been built up from other pieces;
+it does not get smaller when it appears in a subscript or superscript.
+
+\subsection Digits. To get italic ^{digits} {\it0123456789}, say
+|{\it0123456789}|; to get boldface digits {\bf0123456789}, say
+|{\bf0123456789}|; to get oldstyle digits {\oldstyle0123456789}, say
+|{\oldstyle0123456789}|. These conventions work also outside of math mode.
+
+\subsection ``Large'' operators. The following symbols come in two sizes,
+for text and display styles:
+^^|\sum|^^|\bigcap|^^|\bigodot|
+^^|\prod|^^|\bigcup|^^|\bigotimes|
+^^|\coprod|^^|\bigsqcup|^^|\bigoplus|
+^^|\int|^^|\bigvee|^^|\biguplus|
+^^|\oint|^^|\bigwedge|
+$$\displayindent=16pt \openup3pt
+\halign{&\qquad\hbox to10pt{\hss$#$\hss}\enspace&
+ \hbox to10pt{\hss$\displaystyle#$\hss}\enspace&
+ \hbox to60pt{#\hss}\cr
+\sum&\sum&|\sum|&\bigcap&\bigcap&|\bigcap|&
+ \bigodot&\bigodot&|\bigodot|\cr
+\prod&\prod&|\prod|&\bigcup&\bigcup&|\bigcup|&
+ \bigotimes&\bigotimes&|\bigotimes|\cr
+\coprod&\coprod&|\coprod|&\bigsqcup&\bigsqcup&|\bigsqcup|&
+ \bigoplus&\bigoplus&|\bigoplus|\cr
+\int&\int&|\int|&\bigvee&\bigvee&|\bigvee|&
+ \biguplus&\biguplus&|\biguplus|\cr
+\oint&\oint&|\oint|&\bigwedge&\bigwedge&|\bigwedge|\cr
+}$$
+It is important to distinguish these large Op symbols from the similar but
+smaller Bin symbols whose names are the same except for a `|big|' prefix.
+Large operators usually occur at the beginning of a formula or subformula,
+and they usually are subscripted; ^{binary operations} usually occur between
+two symbols or subformulas, and they rarely are subscripted. For example,
+\begindisplay
+|$\bigcup_{n=1}^m(x_n\cup y_n)$|\qquad yields \qquad
+$\bigcup_{n=1}^m(x_n\cup y_n)$
+\enddisplay
+The large operators |\sum|, |\prod|, |\coprod|, and |\int| should also be
+distinguished from smaller symbols called |\Sigma|~($\Sigma$),
+|\Pi|~($\Pi$), |\amalg|~($\amalg$), and |\smallint|~($\smallint$),
+respectively; the ^|\smallint| operator is rarely used.
+
+\subsection Binary operations. Besides $+$ and $-$, you can type
+^^|\pm|^^|\cap|^^|\vee|
+^^|\mp|^^|\cup|^^|\wedge|
+^^|\setminus|^^|\uplus|^^|\oplus|
+^^|\cdot|^^|\sqcap|^^|\ominus|
+^^|\times|^^|\sqcup|^^|\otimes|
+^^|\ast|^^|\triangleleft|^^|\oslash|
+^^|\star|^^|\triangleright|^^|\odot|
+^^|\diamond|^^|\wr|^^|\dagger|
+^^|\circ|^^|\bigcirc|^^|\ddagger|
+^^|\bullet|^^|\bigtriangleup|^^|\amalg|
+^^|\div|^^|\bigtriangledown|
+\beginsymbols
+\pm&|\pm|&\cap&|\cap|&\vee&|\vee|\cr
+\mp&|\mp|&\cup&|\cup|&\wedge&|\wedge|\cr
+\setminus&|\setminus|&\uplus&|\uplus|&\oplus&|\oplus|\cr
+\cdot&|\cdot|&\sqcap&|\sqcap|&\ominus&|\ominus|\cr
+\times&|\times|&\sqcup&|\sqcup|&\otimes&|\otimes|\cr
+\ast&|\ast|&\triangleleft&|\triangleleft|&\oslash&|\oslash|\cr
+\star&|\star|&\triangleright&|\triangleright|&\odot&|\odot|\cr
+\diamond&|\diamond|&\wr&|\wr|&\dagger&|\dagger|\cr
+\circ&|\circ|&\bigcirc&|\bigcirc|&\ddagger&|\ddagger|\cr
+\bullet&|\bullet|&\bigtriangleup&|\bigtriangleup|&\amalg&|\amalg|\cr
+\div&|\div|&\bigtriangledown&|\bigtriangledown|\cr
+\endsymbols
+It's customary to say |$G\backslash| |H$| to denote double cosets
+of $G$ by~$H$~($G\backslash H$), and |$p\backslash n$| to mean that
+$p$ divides~$n$~($p\backslash n$); but |$X\setminus Y$| denotes
+the elements of set~$X$ minus those of set~$Y$~($X\setminus Y$).
+Both operations use the same symbol, but |\backslash| is
+type Ord, while |\setminus| is type Bin (so \TeX\ puts
+more space around it).
+
+\subsection Relations. Besides $<$, $>$, and $=$, you can type
+^^|\leq|^^|\geq|^^|\equiv|
+^^|\prec|^^|\succ|^^|\approx|
+^^|\preceq|^^|\succeq|^^|\propto|
+^^|\ll|^^|\gg|^^|\asymp|
+^^|\subset|^^|\supset|^^|\sim|
+^^|\subseteq|^^|\supseteq|^^|\simeq|
+^^|\sqsubseteq|^^|\sqsupseteq|^^|\cong|
+^^|\in|^^|\ni|^^|\bowtie|
+^^|\vdash|^^|\dashv|^^|\models|
+^^|\smile|^^|\mid|^^|\doteq|
+^^|\frown|^^|\parallel|^^|\perp|
+\beginsymbols
+\leq&|\leq|&\geq&|\geq|&\equiv&|\equiv|\cr
+\prec&|\prec|&\succ&|\succ|&\sim&|\sim|\cr
+\preceq&|\preceq|&\succeq&|\succeq|&\simeq&|\simeq|\cr
+\ll&|\ll|&\gg&|\gg|&\asymp&|\asymp|\cr
+\subset&|\subset|&\supset&|\supset|&\approx&|\approx|\cr
+\subseteq&|\subseteq|&\supseteq&|\supseteq|&\cong&|\cong|\cr
+\sqsubseteq&|\sqsubseteq|&\sqsupseteq&|\sqsupseteq|&\bowtie&|\bowtie|\cr
+\in&|\in|&\ni&|\ni|&\propto&|\propto|\cr
+\vdash&|\vdash|&\dashv&|\dashv|&\models&|\models|\cr
+\smile&|\smile|&\mid&|\mid|&\doteq&|\doteq|\cr
+\frown&|\frown|&\parallel&|\parallel|&\perp&|\perp|\cr
+\endsymbols
+The symbols |\mid| and |\parallel| define relations that use the same
+characters as you get from \| and |\|\|; \TeX\ puts space around
+them when they are relations.
+
+\subsection Negated relations. Many of the relations just listed can be
+negated or ``crossed out'' by prefixing them with ^|\not|, as follows:
+\beginsymbols
+\not<&|\not<|&\not>&|\not>|&\not=&|\not=|\cr
+\not\leq&|\not\leq|&\not\geq&|\not\geq|&
+ \not\equiv&|\not\equiv|\cr
+\not\prec&|\not\prec|&\not\succ&|\not\succ|&
+ \not\sim&|\not\sim|\cr
+\not\preceq&|\not\preceq|&\not\succeq&|\not\succeq|&
+ \not\simeq&|\not\simeq|\cr
+\not\subset&|\not\subset|&\not\supset&|\not\supset|&
+ \not\approx&|\not\approx|\cr
+\not\subseteq&|\not\subseteq|&\not\supseteq&|\not\supseteq|&
+ \not\cong&|\not\cong|\cr
+\not\sqsubseteq&|\not\sqsubseteq|&\not\sqsupseteq&|\not\sqsupseteq|&
+ \not\asymp&|\not\asymp|\cr
+\endsymbols
+The symbol |\not| is a relation character of width zero, so it will
+overlap a relation that comes immediately after it. The positioning
+isn't always ideal, because some relation symbols are wider than others;
+for example, |\not\in| gives `$\not\in$', but it is preferable to
+have a steeper cancellation, `$\notin$'. The latter symbol is
+available as a special control sequence called ^|\notin|. The definition
+of\/ |\notin| in Appendix~B indicates how similar symbols can be constructed.
+
+\subsection Arrows. There's also another big class of relations, namely
+^^{arrows}
+^^|\leftarrow| ^^|\longleftarrow| ^^|\uparrow| ^^|\Leftarrow|
+^^|\Longleftarrow| ^^|\Uparrow| ^^|\rightarrow| ^^|\longrightarrow|
+^^|\downarrow| ^^|\Rightarrow| ^^|\Longrightarrow| ^^|\Downarrow|
+^^|\leftrightarrow| ^^|\longleftrightarrow| ^^|\updownarrow|
+^^|\Leftrightarrow| ^^|\Longleftrightarrow| ^^|\Updownarrow| ^^|\mapsto|
+^^|\longmapsto| ^^|\nearrow| ^^|\hookleftarrow| ^^|\hookrightarrow|
+^^|\searrow| ^^|\leftharpoonup| ^^|\rightharpoonup| ^^|\swarrow|
+^^|\leftharpoondown| ^^|\rightharpoondown| ^^|\nwarrow|
+^^|\rightleftharpoons|
+those that point:
+$$\displayindent=16pt
+ \halign\bgroup
+ \qquad\hbox to10pt{\hss$#$\hss}\enspace&\hbox to75pt{#\hss}&
+ \qquad\hbox to10pt{\hss$#$\hss}\enspace&\hbox to85pt{#\hss}&
+ \qquad\hbox to10pt{\hss$#$\hss}\enspace&\hbox to80pt{#\hss}\cr
+\leftarrow&|\leftarrow|&\longleftarrow&|\longleftarrow|&
+ \uparrow&|\uparrow|\cr
+\Leftarrow&|\Leftarrow|&\Longleftarrow&|\Longleftarrow|&
+ \Uparrow&|\Uparrow|\cr
+\rightarrow&|\rightarrow|&\longrightarrow&|\longrightarrow|&
+ \downarrow&|\downarrow|\cr
+\Rightarrow&|\Rightarrow|&\Longrightarrow&|\Longrightarrow|&
+ \Downarrow&|\Downarrow|\cr
+\leftrightarrow&|\leftrightarrow|&\longleftrightarrow&|\longleftrightarrow|&
+ \updownarrow&|\updownarrow|\cr
+\Leftrightarrow&|\Leftrightarrow|&\Longleftrightarrow&|\Longleftrightarrow|&
+ \Updownarrow&|\Updownarrow|\cr
+\mapsto&|\mapsto|&\longmapsto&|\longmapsto|&
+ \nearrow&|\nearrow|\cr
+\hookleftarrow&|\hookleftarrow|&\hookrightarrow&|\hookrightarrow|&
+ \searrow&|\searrow|\cr
+\leftharpoonup&|\leftharpoonup|&\rightharpoonup&|\rightharpoonup|&
+ \swarrow&|\swarrow|\cr
+\leftharpoondown&|\leftharpoondown|&\rightharpoondown&|\rightharpoondown|&
+ \nwarrow&|\nwarrow|\cr
+\rightleftharpoons&|\rightleftharpoons|\cr
+\enddisplay
+Up and down arrows will grow larger, like delimiters (see Chapter~17).
+To put symbols over left and right arrows, plain \TeX\ provides
+a ^|\buildrel| macro: You type |\buildrel|\<superscript>^|\over|\<relation>,
+and the superscript is placed on top of the relation just as limits are
+placed over large operators. For example,
+\begindisplay
+\hbox to 30pt{$\buildrel \alpha\beta \over \longrightarrow$\hfil}%
+|\buildrel \alpha\beta \over \longrightarrow|\cr
+\hbox to 30pt{$\buildrel \rm def \over =$\hfil}%
+|\buildrel \rm def \over =|\cr
+\enddisplay
+\ (In this context, `|\over|' does not define a fraction.)
+
+\subsection Openings. The following ^{left delimiters} are available,
+besides `(':
+^^|\lbrack| ^^|\lbrace| ^^|\langle| ^^|\lfloor| ^^|\lceil|
+\beginsymbols
+\lbrack&|\lbrack|&\lfloor&|\lfloor|&\lceil&|\lceil|\cr
+\lbrace&|\lbrace|&\langle&|\langle|\cr
+\endsymbols
+You can also type simply `|[|' to get |\lbrack|. All of these will grow if
+you prefix them by ^|\bigl|, ^|\Bigl|, ^|\biggl|, ^|\Biggl|, or ^|\left|.
+Chapter~17 also mentions ^|\lgroup| and ^|\lmoustache|, which are
+available in sizes greater than |\big|. If you need more delimiters,
+the following combinations work reasonably well in the normal text size:
+\beginsymbols
+\lbrack\!\lbrack&|\lbrack\!\lbrack|&
+\langle\!\langle&|\langle\!\langle|&
+(\!(&|(\!(|\cr
+\endsymbols
+
+\subsection Closings. The corresponding right delimiters are present too:
+^^|\rbrack| ^^|\rbrace| ^^|\rangle| ^^|\rfloor| ^^|\rceil|
+\beginsymbols
+\rbrack&|\rbrack|&\rfloor&|\rfloor|&\rceil&|\rceil|\cr
+\rbrace&|\rbrace|&\rangle&|\rangle|\cr
+\endsymbols
+Everything that works for openings works also for closings, but reversed.
+\goodbreak
+
+\subsection Punctuation. \TeX\ puts a thin space after commas and semicolons
+that appear in mathematical formulas, and it does the same for a colon
+that is called ^|\colon|. \ (Otherwise a ^{colon} is considered to be a
+relation, as in `$x:=y$' and `$a:b::c:d$', which you type by saying
+`|$x:=y$|' and `|$a:b::c:d$|'.) \ Examples of\/ |\colon| are
+\begindisplay
+\hbox to 115pt{$f\colon A\rightarrow B$\hfil}\enspace
+|$f\colon A\rightarrow B$|\cr
+\hbox to 115pt{$L(a,b;c\colon x,y;z)$\hfil}\enspace
+|$L(a,b;c\colon x,y;z)$|\cr
+\enddisplay
+Plain \TeX\ also defines ^|\ldotp| and ^|\cdotp| to be `.' and `$\cdot$'
+with the spacing of commas and semicolons. These symbols don't
+occur directly in formulas, but they are useful in the definition of\/
+^|\ldots| and ^|\cdots|.
+
+\subsection Alternate names. If you don't like plain \TeX's name for
+some math symbol---for example, if there's another name that looks
+better or that you can remember more easily---the remedy is simple:
+You just say, e.g., `|\let\cupcap=\asymp|'. Then you can type
+`|f(n)\cupcap n|' instead of `|f(n)\asymp n|'.
+
+Some symbols have alternate names that are so commonly used that plain
+\TeX\ provides two or more equivalent control sequences:
+^^|\ne| ^^|\neq| ^^|\le| ^^|\ge| ^^|\to| ^^|\gets| ^^|\owns| ^^|\land|
+^^|\lor| ^^|\lnot| ^^|\vert|
+$$\halign{\indent$\hfil#\hfil$\enspace&#\hfil\enspace&
+ (same as #)\hfil\cr
+\ne&|\ne| or |\neq|&|\not=|\cr
+\le&|\le|&|\leq|\cr
+\ge&|\ge|&|\geq|\cr
+\{&|\{|&|\lbrace|\cr
+\}&|\}|&|\rbrace|\cr
+\to&|\to|&|\rightarrow|\cr
+\gets&|\gets|&|\leftarrow|\cr
+\owns&|\owns|&|\ni|\cr
+\land&|\land|&|\wedge|\cr
+\lor&|\lor|&|\vee|\cr
+\lnot&|\lnot|&|\neg|\cr
+\vert&|\vert|&\|\cr
+\Vert&|\Vert|&|\|\|\cr
+}$$
+There's also ^|\iff| ($\iff$), which is just like |\Longleftrightarrow|
+except that it puts an extra thick space at each side.
+
+\subsection Non-math symbols. Plain \TeX\ makes four special symbols
+available outside of math mode, although the characters themselves
+are actually typeset from the math symbols font:
+$$\halign{\indent$\hfil#\hfil$\enspace&#\hfil\enspace\cr
+\S&|\S|\cr
+\P&|\P|\cr
+\dag&|\dag|\cr
+\ddag&|\ddag|\cr
+\phantom{\gets}\cr\noalign{\kern-\baselineskip}
+}$$
+These control sequences do not act like ordinary math symbols; they don't
+change their size when they appear in subscripts or superscripts, and you
+must say, e.g., |$x^{\P}$| instead of |$x^\P$| when you use them in
+formulas. However, the ^|\dag| and ^|\ddag| symbols are available in
+math mode under the names ^|\dagger| and ^|\ddagger|. It would be easy
+to define mathematical equivalents of\/ ^|\S| and ^|\P|, if~these symbols
+suddenly caught a mathematician's fancy.
+
+\endchapter
+
+Seek not for fresher founts afar,
+Just drop your bucket where you are.
+\author SAM WALTER ^{FOSS}, {\sl Back Country Poems\/} (1892)
+
+\bigskip
+
+No one compositor will have all the signs and symbols available.
+The number of special signs and symbols is almost limitless,
+with new ones being introduced all the time.
+\author UNIVERSITY OF ^{CHICAGO} PRESS, {\sl Manual of Style\/} (1969) % p296
+
+\eject
+ \beginchapter Appendix G. Generating Boxes\\from Formulas
+
+\ninepoint
+People who define new math fonts and/or macros sometimes need to know
+exactly how \TeX\ manipulates the constituents of formulas. The purpose
+of this appendix is to explain the precise positioning rules by which
+\TeX\ converts a math list into a horizontal list. \ (It is a good idea
+to review the introduction to ^{math lists} in Chapter~17 before
+reading further; ``double dangerous bends'' are implied throughout
+this appendix.)
+
+\TeX\ relies on lots of parameters when it typesets formulas, and you have
+the option of changing any or all of them. But of course you will want to
+know what each parameter means, before you change it. Therefore each rule
+below is numbered, and a table appears at the end to show which rules
+depend on which parameters.
+
+The most important parameters appear in the ^{symbol fonts} (family~2) and
+the ^{extension fonts} (family~3). \TeX\ will not typeset a formula unless
+^|\textfont||2|, ^|\scriptfont||2|, and ^|\scriptscriptfont||2| each contain
+at least 22~^|\fontdimen| parameters. For brevity we shall call these parameters
+$\sigma_1$ to $\sigma_{22}$, where the parameter is taken from
+|\textfont2| if the current style is display or text ($D$ or $D'$ or $T$
+or $T'$), from |\scriptfont2| if the current style is $S$ or $S'$, and from
+|\scriptscriptfont2| otherwise. Similarly, the three fonts in family~3
+must each have at least 13~|\fontdimen| parameters, and we will denote
+them by $\xi_1$ to $\xi_{13}$. The notation $\xi_9$, for example, stands for
+the ninth parameter of\/ |\scriptfont3|, if \TeX\ is typesetting something
+in |\scriptstyle|.
+
+A math list is a sequence of items of the various kinds listed in Chapter~17,
+and \TeX\ typesets a formula by converting a math list to a horizontal
+list. When such typesetting begins, \TeX\ has two other pieces of
+information in addition to the math list itself. \ (a)~The starting style
+tells what style should be used for the math list, unless another style
+is specified by a style item. For example, the starting style for a
+displayed formula is $D$, but for an equation in the text or an equation
+number it is $T$; and for a subformula it can be any one of the eight
+^{styles} defined in Chapter~17. We shall use $C$ to stand for the current
+style, and we shall say that the math list is being typeset in style~$C$.
+\ (b)~The typesetting is done either with or without penalties. Formulas
+in the text of a paragraph are converted to horizontal lists in which
+additional penalty items are inserted after binary operations and relations,
+in order to aid in line breaking. Such penalties are not inserted in
+other cases, because they would serve no useful function.
+
+The eight styles are considered to be $D>D'>T>T'>S>S'>\it SS>SS'$, in
+decreasing order. Thus, $C\le S$ means that the current style is $S$,
+$S'$, $\it SS$, or~$\it SS'$. Style $C'$ means the current style with a
+prime added if one isn't there; for example, we have $C'=T'$ if and only
+if $C=T$ or $C=T'$. Style~$C\mathord\uparrow$ is the superscript style
+for $C$; this means style~$S$ if $C$~is $D$ or~$T$, style~$S'$ if $C$~is
+$D'$ or $T'$, style~$\it SS$ if $C$~is $S$ or $\it SS$, and style~$\it
+SS'$ if $C$~is $S'$ or $\it SS'$. Finally, style~$C\mathord\downarrow$ is
+the subscript style, which is~$(C\mathord\uparrow)'$.
+
+Chapter 17 stated that the most important components of math lists are
+called atoms, and that each atom has three fields called its nucleus,
+subscript, and superscript. We frequently need to execute a subroutine
+called ``Set box~$x$ to the so-and-so field in style such-and-such.'' This
+means (a)~if the specified field is empty, $x$~is set equal to a null box;
+(b)~if the field contains a symbol, $x$~is set to an hbox containing that
+symbol in the appropriate size, and the ^{italic correction} for the
+character is included in the width of the box; (c)~if the field contains a
+math list or horizontal list, $x$~is set to an hbox containing the result
+of typesetting that list with the specified starting style. In case~(c),
+the glue is set with no stretching or shrinking, and an additional level
+of hboxing is omitted if it turns out to be redundant.
+
+Another subroutine sets box $x$ to a specified variable ^{delimiter},
+having a specified minimum height plus depth. This means that a search is
+conducted as follows: The delimiter is defined by two symbols, a ``small
+character''~$a$ in family~$f$ and a ``large character''~$b$ in family~$g$.
+The search looks first at character $a$ in scriptscriptfont~$f$, if $C\le
+\it SS$; then it looks at $a$ in scriptfont~$f$, if $C\le S$; then it looks at
+$a$ in textfont~$f$. If nothing suitable is found from $a$ and~$f$, the
+larger alternative $b$ and $g$ is examined in the same way. Either
+$(a,f)$ or $(b,g)$ may be $(0,0)$, which means that the corresponding part
+of the search is to be bypassed. When looking at a character in a
+font, the search stops immediately if that character has sufficient height
+plus depth, or if the character is ^{extensible}; furthermore, if the
+character does not stop the search, and if it has a ^{successor} in the
+font, the successor is looked at next. \ (See the \MF\ manual or the
+^^{METAFONT}
+system documentation of |tfm| files for further information about
+successors and extensible characters.) \ If the search runs all the way to
+completion without finding a suitable character, the one with greatest
+height plus depth is chosen. If no characters at all were found (either
+because $a=f=b=g=0$ or because the characters did not exist in the fonts),
+$x$~is set to an empty box whose width is ^|\nulldelimiterspace|. If an
+extensible character was found, $x$~is set to a vbox containing enough
+pieces to build up a character of sufficient size; the height of this vbox
+is the height of the topmost piece, and the width is the width of the
+repeatable piece. ^^{built-up characters} Otherwise
+$x$~is set to an hbox containing the character that was found; the italic
+correction of the character is included in the width of this box.
+
+There's also a subroutine that ``reboxes'' a given box to a given~width.
+If the box doesn't already have the desired width, \TeX\ unpackages it
+(unless it was a vbox), then adds a kern for an italic correction if one was
+implied, and inserts ^|\hss| glue at both left and right; the resulting
+horizontal list is packaged into an hbox. This process is used, for
+example, to give a common width to the numerator and denominator of a
+fraction; it centers whichever is smaller, unless infinite glue is present
+in addition to the newly added |\hss|.
+
+If $x$ is a box, we shall use the abbreviations $h(x)$, $d(x)$, and $w(x)$ for
+its height, depth, and width, respectively.
+
+Here now are the rules for typesetting a given math list in starting
+style~$C$. The process applies from left to right, translating each
+item in turn. Two passes are made over the list;
+most of the work is done by the first pass, which compiles individual
+translations of the math items. We shall consider this part of the
+task first:
+
+\def\rule#1.{\smallskip\textindent{\bf#1.}\ignorespaces}
+\rule 1. If the current item is a rule or discretionary or penalty or
+``whatsit'' or boundary item, simply leave it unchanged and move to the
+next item.
+
+\rule 2. If the current item is glue or a kern, translate it as follows:
+If it is glue from ^|\nonscript|, check if the immediately following item
+is glue or a kern; and if so, remove that item if $C\le S$. Otherwise, if the
+current item is from ^|\mskip| or ^|\mkern|, convert from |mu| to absolute
+units by multiplying each finite dimension by ${1\over18}\sigma_6$. Then
+move on to the next item.
+
+\rule 3. If the current item is a style change, set $C$ to the specified
+style and move on to the next item.
+
+\rule 4. If the current item is a four-way choice, it contains four math
+lists for the four main styles. Replace it by the math list that corresponds
+to the current style $C$, then move to the first unprocessed item.
+
+\rule 5. If the current item is a Bin atom, and if this was the first atom
+in the list, or if the most recent previous atom was Bin, Op, Rel, Open,
+or Punct, change the current Bin to Ord and continue with Rule~14.
+Otherwise continue with Rule~17.
+
+\rule 6. If the current item is a Rel or Close or Punct atom, and if the most
+recent previous atom was Bin, change that previous Bin to Ord. Continue
+with Rule~17.
+
+\rule 7. If the current item is an Open or Inner atom, go directly to Rule~17.
+
+\rule 8. If the current item is a Vcent atom (from ^|\vcenter|), let its
+nucleus be a vbox of height-plus-depth~$v$. Change the height to
+${1\over2}v+a$ and the depth to ${1\over2}v-a$, where $a$~is the
+axis height, $\sigma_{22}$. Change this atom to type Ord and continue
+with Rule~17.
+
+\rule 9. If the current item is an Over atom (from ^|\overline|), set
+box~$x$ to the nucleus in style $C'$. Then replace the nucleus by a vbox
+containing kern~$\theta$, hrule of~height~$\theta$, kern~$3\theta$,
+and box~$x$, from top to bottom, where $\theta=\xi_8$ is the ^{default
+rule thickness}. \ (This puts a rule over the nucleus, with $3\theta$ clearance,
+and with $\theta$~units of extra white space assumed to be present above
+the rule.) \ Continue with Rule~16.
+
+\rule 10. If the current item is an Under atom (from ^|\underline|), set
+box~$x$ to the nucleus in style $C$. Then replace the nucleus by a vtop
+made from box~$x$, kern~$3\theta$, and hrule of~height~$\theta$,
+where $\theta=\xi_8$ is the default rule thickness; and add $\theta$ to the
+depth of the box. \ (This puts a rule under the nucleus, with $3\theta$
+clearance, and with $\theta$~units of extra white space assumed to be
+present below the rule.) \ Continue with Rule~16.
+
+\rule 11. If the current item is a Rad atom (from ^|\radical|, e.g.,
+^|\sqrt|), set box~$x$ to the nucleus in style $C'$. Let $\theta=\xi_8$;
+and let $\varphi=\sigma_5$ if $C>T$, otherwise $\varphi=\theta$.
+Set $\psi=\theta+{1\over4}\vert\varphi\vert$; this is the minimum
+clearance that will be allowed between box~$x$ and the rule that will go
+above it. Set box~$y$ to a variable delimiter for this radical atom, having
+height plus depth $h(x)+d(x)+\psi+\theta$ or more. Then set $\theta\leftarrow
+h(y)$; this is the thickness of the rule to be used in the radical
+construction. \ (Note that the font designer specifies the thickness of
+the rule by making it the height of the radical character; the baseline
+of the character should be precisely at the bottom of the rule.) \
+If $d(y)>h(x)+d(x)+\psi$, increase $\psi$ by half of the excess; i.e.,
+set $\psi\leftarrow{1\over2}\bigl(\psi+d(y)-h(x)-d(x)\bigr)$. Construct
+a vbox consisting of kern~$\theta$, hrule of~height~$\theta$, kern~$\psi$,
+and box~$x$, from top to bottom. The nucleus of the radical atom is now
+replaced by box~$y$ raised by $h(x)+\psi$, followed by the new vbox.
+Continue with Rule~16.
+
+\rule 12. If the current item is an Acc atom (from ^|\mathaccent|), just
+go to Rule~16 if the accent character doesn't exist in the current size.
+Otherwise set box~$x$ to the nucleus in style $C'$, and set $u$ to the
+width of this box. If the nucleus is not a single character, let $s=0$;
+otherwise set $s$ to the kern amount for the nucleus followed by the
+^|\skewchar| of its font. If the accent character has a successor in its
+font whose width is $\le u$, change it to the successor and repeat this
+sentence. Now set $\delta\leftarrow \min\bigl(h(x),\chi\bigr)$, where
+$\chi$ is |\fontdimen5| (the ^{x-height}) in the accent font. If the
+nucleus is a single character, replace box~$x$ by a box containing the
+nucleus together with the superscript and subscript of the Acc atom, in
+style~$C$, and make the sub/superscripts of the Acc atom empty; also
+increase $\delta$ by the difference between the new and old values
+of~$h(x)$. Put the accent into a new box~$y$, including the italic
+correction. Let~$z$ be a vbox consisting of: box~$y$ moved right
+$s+{1\over2}\bigl(u-w(y)\bigr)$, kern $-\delta$, and box~$x$. If
+$h(z)<h(x)$, add a kern of $h(x)-h(z)$ above box~$y$ and set $h(z)\gets
+h(x)$. Finally set $w(z)\gets w(x)$, replace the nucleus of the Acc atom
+by box~$z$, and continue with Rule~16.
+
+\rule 13. If the current item is an Op atom, mark this atom as having
+limits if it has been marked with ^|\limits|, or if it has been marked
+with ^|\displaylimits| and $C>T$. If the nucleus is not a symbol, set
+$\delta\leftarrow0$ and go to Rule~13a. Otherwise if $C>T$ and if the
+nucleus symbol has a successor in its font, move to the successor. \ (This
+is where operators like $\sum$ and $\int$ change to a larger size in
+display styles.) \ Put the symbol into a new box~$x$, in the current size,
+and set $\delta$ to the italic correction for the character; include $\delta$
+in the width of box~$x$ if and only if limits are to be set or
+there is no subscript. Shift box~$x$ down by ${1\over2}\bigl(h(x)-d(x)\bigr)
+-a$, where $a=\sigma_{22}$, so that the operator character is centered
+vertically on the axis; this shifted box becomes the nucleus of the Op atom.
+
+\rule 13a. If limits are not to be typeset for this Op atom, go to
+Rule~17; otherwise the limits are attached as follows:
+Set box~$x$ to the superscript field in style $C\mathord\uparrow$; set box~$y$
+to the nucleus field in style~$C$; and set box~$z$ to the subscript field
+in style $C\mathord\downarrow$. Rebox all three of these boxes to width
+$\max\bigl(w(x),w(y),w(z)\bigr)$. If the superscript field was not empty,
+attach box~$x$ above box~$y$, separated by a kern of size
+$\max\bigl(\xi_9,\xi_{11}-d(x)\bigr)$, and shift box~$x$ right by
+${1\over2}\delta$; also put a kern of size~$\xi_{13}$ above box~$x$.
+If the subscript field was not empty,
+attach box~$z$ below box~$y$, separated by a kern of size
+$\max\bigl(\xi_{10},\xi_{12}-h(z)\bigr)$, and shift box~$z$ left by
+${1\over2}\delta$; also put a kern of size~$\xi_{13}$ below box~$z$.
+The resulting vbox becomes the nucleus of the current Op atom; move to
+the next item.
+
+\rule 14. If the current item is an Ord atom, go directly to Rule~17 unless
+all of the following are true: The nucleus is a symbol; the subscript
+and superscript are both empty; the very next item in the math list is an
+atom of type Ord, Op, Bin, Rel, Open, Close, or Punct; and the nucleus of the
+next item is a symbol whose family is the same as the family in the present
+Ord atom. In such cases the present symbol is marked as a text symbol.
+If the font information shows a ligature between this symbol and the
+following one, using the specified family and the current size, then
+insert the ligature character and continue as specified by the font;
+in this process, two characters may collapse into a single Ord
+text symbol, and/or new Ord text characters may appear. If the font information
+shows a kern between the current symbol and the next, insert a kern item
+following the current atom.
+As soon as an Ord atom has been fully processed for ligatures and kerns,
+go to Rule~17.
+
+\rule 15. If the current item is a generalized fraction (and it had better
+be, because that's the only possibility left if Rules 1--14 don't apply),
+let $\theta$ be the thickness of the bar line and let $(\lambda,\rho)$ be
+the left and right delimiters. \ If this fraction was generated by
+^|\over| or ^|\overwithdelims|, then $\theta=\xi_8$; if it was generated by
+^|\atop| or ^|\atopwithdelims|, $\theta=0$; otherwise it was generated by
+^|\above| or ^|\abovewithdelims|, and a specific value of~$\theta$ was
+given at that time. The values of $\lambda$ and $\rho$ are null unless
+the fraction is ``with delims.''
+
+\rule 15a. Put the numerator into box~$x$, using style $T$ or $T'$ if
+$C$~is $D$ or $D'$, otherwise using style $C\mathord\uparrow$.
+Put the denominator into box~$z$, using style $T'$ if
+$C>T$, otherwise using $C\mathord\downarrow$.
+If $w(x)<w(z)$, rebox $x$ to width~$w(z)$;
+if $w(z)<w(x)$, rebox $z$ to width~$w(x)$.
+
+\rule 15b. If $C>T$, set $u\leftarrow\sigma_8$ and $v\leftarrow\sigma_{11}$.
+Otherwise set $u\leftarrow\sigma_9$ or $\sigma_{10}$, according as $\theta\ne0$
+or $\theta=0$, and set $v\leftarrow\sigma_{12}$. \ (The fraction will be
+typeset with its numerator shifted up by an amount~$u$ with respect to
+the current baseline, and with the denominator shifted down by~$v$,
+unless the boxes are unusually large.)
+
+\rule 15c. If $\theta=0$ (|\atop|), the numerator and denominator are
+combined as follows: Set $\varphi\leftarrow7\xi_8$ or
+$3\xi_8$, according as $C>T$ or $C\le T$; $\varphi$ is the minimum
+clearance that will be tolerated between numerator and denominator.
+Let $\psi=\bigl(u-d(x)\bigr)-\bigl(h(z)-v\bigr)$ be the actual clearance
+that would be obtained with the current values of $u$ and~$v$; if
+$\psi<\varphi$, add ${1\over2}(\varphi-\psi)$ to both $u$ and~$v$.
+Then construct a vbox of height $h(x)+u$ and depth $d(z)+v$, consisting
+of box~$x$ followed by an appropriate kern followed by box~$z$.
+
+\rule 15d. If $\theta\ne0$ (|\over|), the numerator and denominator are
+combined as follows: Set $\varphi\leftarrow3\theta$ or $\theta$, according
+as $C>T$ or $C\le T$; $\varphi$ is the minimum clearance that will be
+tolerated between numerator or denominator and the bar line. Let
+$a=\sigma_{22}$ be the current axis height; the middle of the bar line
+will be placed at this height. If
+$\bigl(u-d(x)\bigr)-(a+{1\over2}\theta)<\varphi$, increase~$u$ by the
+difference between these quantities; and if $(a-{1\over2}\theta)-
+\bigl(h(z)-v\bigr)<\varphi$, increase~$v$ by the difference. Finally
+construct a vbox of height $h(x)+u$ and depth $d(z)+v$, consisting of
+box~$x$ followed by a kern followed by an hrule of height~$\theta$
+followed by another kern followed by box~$z$, where the kerns are figured
+so that the bottom of the hrule occurs at $a-{1\over2}\theta$ above the
+baseline.
+
+\rule 15e. Enclose the vbox that was constructed in Rule 15c or 15d by
+delimiters whose height plus depth is at least $\sigma_{20}$, if $C>T$, and at
+least $\sigma_{21}$ otherwise. Shift the delimiters up or down so that they are
+vertically centered with respect to the axis. Replace the generalized
+fraction by an Inner atom whose nucleus is the resulting sequence of three boxes
+(left delimiter, vbox, right delimiter).
+
+\bigbreak\noindent
+Rules 1--15 account for the preliminary processing of math list items;
+but we still haven't specified how subscripts and superscripts are to be
+typeset. Therefore some of those rules lead to the following post-process:
+
+\rule 16. Change the current item to an Ord atom, and continue with Rule~17.
+
+\rule 17. If the nucleus of the current item is a math list, replace it by
+a box obtained by typesetting that list in the current style. Then if the
+nucleus is not simply a symbol, go on to Rule~18. Otherwise we are in the
+common case that a math symbol is to be translated to its horizontal-list
+equivalent: Convert the symbol to a character box for the specified family
+in the current size. If the symbol was not marked by Rule~14 above as a
+text symbol, or if\/ |\fontdimen| parameter number~2 of its font is zero, set
+$\delta$ to the italic correction; otherwise set $\delta$ to zero. If
+$\delta$ is nonzero and if the subscript field of the current atom is
+empty, insert a kern of width~$\delta$ after the character box, and set
+$\delta$ to zero. Continue with Rule~18.
+
+\rule 18. (The remaining task for the current atom is to attach a possible
+subscript and superscript.) \ If both subscript and superscript fields
+are empty, move to the next item. Otherwise continue with the following
+subrules:
+
+\rule 18a. If the translation of the nucleus is a character box, possibly
+followed by a kern, set $u$ and~$v$ equal to zero; otherwise set
+$u\leftarrow h-q$ and $v\leftarrow d+r$, where $h$ and~$d$ are the height
+and depth of the translated nucleus, and where $q$ and~$r$ are the values
+of $\sigma_{18}$ and $\sigma_{19}$ in the font corresponding to styles
+$C\mathord\uparrow$ and~$C\mathord\downarrow$. \ (The quantities $u$
+and~$v$ represent minimum amounts by which the superscript and subscript
+will be shifted up and down; these preliminary values of $u$ and~$v$ may
+be increased later.)
+
+\rule 18b. If the superscript field is empty (so that there is a subscript
+only), set box~$x$ to the subscript in style~$C\mathord\downarrow$, and add
+^|\scriptspace| to $w(x)$. Append this box to the translation of the
+current item, shifting it down by $\max\bigl(v,\sigma_{16},h(x)-{4\over5}
+\vert\sigma_5\vert\bigr)$, and move to the next item. \ (The idea is
+to make sure that the subscript is shifted by at least~$v$ and by at
+least $\sigma_{16}$; furthermore, the top of the subscript should not extend
+above $4\over5$~of the current x-height.)
+
+\rule 18c. Set box~$x$ to the superscript field in style~$C\mathord\uparrow$,
+and add |\scriptspace| to~$w(x)$. Then set $u\leftarrow\max\bigl(u,p,
+d(x)+{1\over4}\vert\sigma_5\vert\bigr)$, where $p=\sigma_{13}$ if $C=D$,
+$p=\sigma_{15}$ if $C=C'$, and $p=\sigma_{14}$ otherwise; this gives a
+tentative position for the superscript.
+
+\rule 18d. If the subscript field is empty (so that there is a
+superscript only), append box~$x$ to the translation of the current atom,
+shifting it up by $u$, and move to the next item. Otherwise (i.e.,
+both subscript and superscript are present), set box~$y$ to the
+subscript in style~$C\mathord\downarrow$, add |\scriptspace| to~$w(y)$,
+and set $v\leftarrow\max(v,\sigma_{17})$.
+
+\rule 18e. (The remaining task is to position a joint
+subscript/superscript combination.) \ Let $\theta=\xi_8$ be the default
+rule thickness. If $\bigl(u-d(x)\bigr)-\bigl(h(y)-v\bigr)\ge4\theta$, go
+to Rule~18f. \ (This means that the white space between subscript and
+superscript is at least $4\theta$.) \ Otherwise reset $v$ so that
+$\bigl(u-d(x)\bigr)-\bigl(h(y)-v\bigr)=4\theta$. Let $\psi={4\over5}
+\vert\sigma_5\vert-\bigl(u-d(x)\bigr)$. If $\psi>0$, increase $u$
+by~$\psi$ and decrease $v$ by~$\psi$. \ (This means that the bottom of the
+superscript will be at least as high above the baseline as $4\over5$ of
+the x-height.)
+
+\rule 18f. Finally, let $\delta$ be zero unless it was set to a nonzero
+value by Rules 13 or 17. \ (This is the amount of horizontal displacement
+between subscript and superscript.) Make a vbox of height $h(x)+u$ and
+depth $d(y)+v$, consisting of box~$x$ shifted right by~$\delta$, followed
+by an appropriate kern, followed by box~$y$. Append this vbox to the
+translation of the current item and move to the next.
+
+\bigbreak\noindent
+After the entire math list has been processed by Rules 1--18, \TeX\ looks
+at the last atom (if there was one), and changes its type from
+Bin to Ord (if it was of type Bin). Then the following rule is performed:
+
+\rule 19. If the math list begins and ends with boundary items, compute
+the maximum height~$h$ and depth~$d$ of the boxes in the translation of
+the math list that was made on the first pass, taking into account the
+fact that some boxes may be raised or lowered. Let $a=\sigma_{22}$ be the
+axis height, and let $\delta=\max(h-a,d+a)$ be the amount by which the
+formula extends away from the axis. Replace the boundary items by
+delimiters whose height plus depth is at least $\max(\lfloor\delta/500\rfloor
+f,2\delta-l)$, where $f$ is the ^|\delimiterfactor|
+and $l$ is the ^|\delimitershortfall|. Shift the delimiters up or down so
+that they are vertically centered with respect to the axis. Change the left
+boundary item to an Open atom and the right boundary item to
+a Close atom. \ (All of the calculations in this step are done with
+$C$ equal to the starting style of the math list; style items in the
+middle of the list do not affect the style of the right boundary item.)
+
+\medbreak \rule 20. Rules 1--19 convert the math list into a sequence of
+items in which the only remaining atoms are of types Ord, Op, Bin, Rel,
+Open, Close, Punct, and Inner. After that conversion is complete, a second pass
+is made through the entire list, replacing all of the atoms by the boxes
+and kerns in their translations. Furthermore, additional inter-element
+spacing is inserted just before each atom except the first, based on the
+type of that atom and the preceding one. Inter-element spacing is defined
+by the three parameters ^|\thinmuskip|, ^|\medmuskip|, and
+^|\thickmuskip|; the |mu| units are converted to absolute units as in
+Rule~2 above. Chapter~18 has a chart that defines the inter-element
+spacing, some of which is ^|\nonscript|, i.e., it is inserted only in
+styles $>S$. The list might also contain style items, which are removed
+during the second pass; they are used to change the current style just as
+in the first pass, so that both passes have the same value of~$C$ when
+they work on any particular atom.
+
+\rule 21. Besides the inter-element spacing, penalties are placed after
+the translation of each atom of type Bin or Rel, if the math list was part
+of a paragraph. The penalty after a Bin is ^|\binoppenalty|, and the
+penalty after a Rel is ^|\relpenalty|. However, the penalty is not
+inserted after the final item in the entire list, or if it has a numeric
+value~$\ge10000$, or if the very next item in the list is already a
+penalty item, or after a Rel atom that is immediately followed by another
+Rel atom.
+
+\rule 22. After all of the preceding actions have been performed, the math
+list has been totally converted to a horizontal list. If the result is
+being inserted into a larger horizontal list, in horizontal mode or
+restricted horizontal mode, it is enclosed by ``^{math-on}'' and
+``^{math-off}'' items that each record the current value of\/
+^|\mathsurround|. Or if this list is a displayed formula, it
+is processed further as explained in Chapter~19.
+
+\medbreak
+\def\\{\kern1pt}
+\noindent{\bf Summary of parameter usage.} \ Here is the promised index
+that refers to everything affected by the mysterious parameters
+in the symbol fonts. Careful study of the rules allows you to get the
+best results by appropriately setting the parameters for new fonts
+that you may wish to use in mathematical typesetting. Each font parameter
+has an external name that is used in supporting software packages;
+for example, $\sigma_{14}$ is generally referred to as `sup2' and $\xi_8$ as
+`default\_\\ rule\_thickness'. These external names are indicated in the table.
+$$\vbox{
+\halign to\hsize{$#\hfil$\tabskip10pt plus 10pt minus 3pt&
+#\hfil&$\rm#\hfil$\quad&
+$#\hfil$&#\hfil&$\rm#\hfil$\tabskip0pt\cr
+\omit\span\omit\it Parameter&\omit\it Used in\hfil&
+\omit\span\omit\it Parameter&\omit\it Used in\hfil\cr
+\noalign{\medskip\hrule\smallskip}
+\sigma_5&x\_\\height&11, 18b, 18c, 18e&
+\sigma_{18}&sup\_drop&18a\cr
+\sigma_6&quad&2, 20&
+\sigma_{19}&sub\_drop&18a\cr
+\sigma_8&num1&15b&
+\sigma_{20}&delim1&15e\cr
+\sigma_9&num2&15b&
+\sigma_{21}&delim2&15e\cr
+\sigma_{10}&num3&15b&
+\sigma_{22}&axis\_\\height&8, 13, 15d, 19\cr
+\sigma_{11}&denom1&15b&
+\xi_8&default\_\\rule\_thickness&9, 10, 11, 15, 15c, 18e\cr
+\sigma_{12}&denom2&15b&
+\xi_9&big\_op\_\\spacing1&13a\cr
+\sigma_{13}&sup1&18c&
+\xi_{10}&big\_op\_\\spacing2&13a\cr
+\sigma_{14}&sup2&18c&
+\xi_{11}&big\_op\_\\spacing3&13a\cr
+\sigma_{15}&sup3&18c&
+\xi_{12}&big\_op\_\\spacing4&13a\cr
+\sigma_{16}&sub1&18b&
+\xi_{13}&big\_op\_\\spacing5&13a\cr
+\sigma_{17}&sub2&18d\cr
+\noalign{\smallskip\hrule\medskip}}}$$
+Besides the symbol and extension fonts (families 2 and~3), the
+rules above also refer to parameters in arbitrary families:
+Rule~17 uses |\fontdimen| parameter~2 (space) to determine whether to insert
+an italic correction between adjacent letters, and Rule~12 uses parameter~5
+(x\_\\height) to position an accent character. Several non-font parameters
+also affect mathematical typesetting:
+dimension parameters
+ \hbox{|\delimitershortfall|} (Rule~19),
+ \hbox{|\nulldelimiterspace|} (in
+the construction of variable delimiters for Rules 11, 15e,~19),
+ \hbox{|\mathsurround|} (Rule~22),
+and
+ \hbox{|\scriptspace|} (Rules 18bcd);
+integer parameters
+ \hbox{|\delimiterfactor|} (Rule~19),
+ \hbox{|\binoppenalty|} (Rule~21),
+and
+ \hbox{|\relpenalty|} (Rule~21);
+muglue parameters
+ \hbox{|\thinmuskip|},
+ \hbox{|\medmuskip|,} and
+ \hbox{|\thickmuskip|}
+(Rule~20).
+
+\endchapter
+
+Woe to the author who always wants to teach!\/
+% Mais malheur a` l'auteur qui veut toujours instruire!
+The secret of being a bore is to tell everything.
+% Le secret d'ennuyer est celui de tout dire.
+\author ^{VOLTAIRE}, {\sl De la Nature de l'Homme\/} (1737)
+% 6th discours en vers sur l'homme
+
+\bigskip
+
+Very few Compositors are fond of Algebra,
+and rather chuse to be employed upon plain work.
+%\author PHILIP ^{LUCKOMBE}, {\sl The History and Art of Printing\/} (1770)
+% page 472 ... but I discovered on 3 June 1998 that Luckombe plagiarized!
+\author JOHN ^{SMITH}, {\sl The Printer's Grammar\/} (1755)
+% page 308
+\eject
+ \beginchapter Appendix H. Hyphenation
+
+It's better to break a word with a hyphen than to stretch interword spaces
+too much. Therefore \TeX\ tries to divide words into syllables when
+there's no good alternative available.
+
+But computers are notoriously bad at ^{hyphenation}. When the type\-setting of
+newspapers began to be fully automated, ^{jokes} about ``the-rapists who
+pre-ached on wee-knights'' soon began to circulate.
+
+It's not hard to understand why machines have behaved poorly at this task,
+because hyphenation is quite a difficult problem. For example, the word
+`record' is supposed to be broken as `rec-ord' when it is a noun, but
+`re-cord' when it is a verb. The word `hyphenation' itself is somewhat
+exceptional; if `hy-phen-a-tion' is compared to similar words like
+`con-cat-e-na-tion', it's not immediately clear why the `n' should
+be attached to the `e' in one case but not the other. Examples like
+`dem-on-stra-tion' vs.~`de-mon-stra-tive' show that the alteration of
+two letters can actually affect hyphens that are nine positions away.
+
+A good solution to the problem was discovered by Frank M. ^{Liang} during
+1980--1982, and \TeX\ incorporates the new method. Liang's algorithm
+works quickly and finds nearly all of the legitimate places to insert
+hyphens; yet it makes few if any errors, and it takes up comparatively
+little space in the computer. Moreover, the method is flexible enough
+to be adapted to any language, and it can also be used to hyphenate
+words in two languages simultaneously. Liang's Ph.D. thesis,
+published by Stanford University's Department of Computer Science in~1983,
+explains how to take a dictionary of hyphenated words and teach it to \TeX;
+i.e., it explains how to compute tables by which \TeX\ will be able to
+reconstruct most of the hyphens in the given dictionary, without error.
+
+\def\\#1{$_{\kern\scriptspace#1}$}
+\TeX\ hyphenates a given word by first looking for it in an ``^{exception
+dictionary},'' which specifies the hyphen positions for words that deserve
+special treatment. If the word isn't there, \TeX\ looks for {\sl^{patterns}\/}
+in the word, and this is the key idea underlying Liang's method. Here's how
+it works, using the word `hyphenation' as an example, when \TeX\ is
+operating with the English-oriented patterns of plain \TeX\ format:
+The given word is first extended by special markers at either end; in this
+case we obtain
+\begintt
+.hyphenation.
+\endtt
+if `|.|'\ denotes the special marker. The extended word has subwords
+\begintt
+. h y p h e n a t i o n .
+\endtt
+of length one,
+\begintt
+.h hy yp ph he en na at ti io on n.
+\endtt
+of length two,
+\begintt
+.hy hyp yph phe hen ena nat ati tio ion on.
+\endtt
+of length three,
+and so on. Each subword of length $k$ is a pattern that defines $k+1$ small
+integer values relating to the desirability of hyphens in the positions
+between and adjacent to its letters. We can show these values by attaching
+them as subscripts; for example,
+`{\tt\\0h\\0e\\2n\\0}' means that the values corresponding to the
+subword `|hen|' are 0,~0, 2, and~0, where the~2 relates to hyphens
+between the `|e|' and the `|n|'. The interletter values are entirely zero
+for all subwords except those that match an entry in \TeX's current
+``^{pattern dictionary}''; and in this case, only the subwords
+\begindisplay
+\tt\\0h\\0y$_3$p\\0h\\0 \\0h\\0e\\2n\\0 \\0h\\0e\\0n\\0a\\4\cr
+\qquad\tt\\0h\\0e\\0n\\5a\\0t\\0 \\1n\\0a\\0 \\0n\\2a\\0t\\0
+ \\1t\\0i\\0o\\0 \\2i\\0o\\0 \\0o\\2n\\0\cr
+\enddisplay
+occur as special patterns. \TeX\ now computes the maximum interletter
+value that occurs at each subword touching each interletter position.
+For example, between `|e|' and `|n|' there are five relevant values
+in this case (2~from {\tt\\0h\\0e\\2n\\0},
+0~from {\tt\\0h\\0e\\0n\\0a\\4},
+0~from {\tt\\0h\\0e\\0n\\5a\\0t\\0},
+1~from {\tt\\1n\\0a\\0},
+and 0~from {\tt\\0n\\2a\\0t\\0}); the maximum of these is~2.
+The result of all the maximizations is
+\begindisplay
+\tt.\\0h\\0y$_3$p\\0h\\0e\\2n\\5a\\4t\\2i\\0o\\2n\\0.
+\enddisplay
+Now comes the final step: A hyphen is considered to be acceptable
+between two letters if the associated interletter value is {\sl odd}.
+Thus, two potential breakpoints have been found: `|hy-phen-ation|'.
+Similarly, the word `|concatenation|' contains the patterns
+\begindisplay
+\tt\\0o\\2n\\0
+\\0o\\0n\\1c\\0
+\\1c\\0a\\0
+\\1n\\0a\\0
+\\0n\\2a\\0t\\0
+\\1t\\0i\\0o\\0
+\\2i\\0o\\0
+\\0o\\2n\\0
+\enddisplay
+and this yields `{\tt\\0c\\0o\\2n\\1c\\0a\\0t\\0e\\1n\\2a\\1t\\2i\\0o\\2n\\0}',
+i.e., `|con-cate-na-tion|'.
+
+Let's try a 34-letter word:
+`|supercalifragilisticexpialidocious|' matches the plain \TeX\ patterns
+\begindisplay
+\tt u\\1pe r\\1c \\1ca al\\1i ag\\1i gil\\4 il\\1i il\\4ist is\\1ti st\\2i\cr
+\qquad\tt s\\1tic
+ \\1exp x\\3p pi\\3a \\2i\\1a i\\2al \\2id \\1do \\1ci \\2io ou\\2 \\2us\cr
+\enddisplay
+(where subscripts that aren't shown are zero), and this yields
+$$\centerline{%
+\tt.\\0s\\0u\\1p\\0e\\0r\\1c\\0a\\0l\\1i\\0f\\0r\\0a\\0g\\1i\\0l\\4i%
+\\0s\\1t\\2i\\0c\\1e\\0x\\3p\\2i\\3a\\0l\\2i\\1d\\0o\\1c\\2i\\0o\\2u\\2s\\0.}$$
+The \stretch resulting \stretch hyphens \stretch
+`|su-|\stretch|per-|\stretch|cal-|\stretch|ifrag-|\stretch|ilis-|\stretch
+|tic-|\stretch|ex-|\stretch|pi-|\stretch|ali-|\stretch|do-|\stretch|cious|'
+agree with Random House's {\sl Unabridged Dictionary\/} (which also
+shows a few more: `|su-per-cal-i-frag-i-lis-tic-ex-pi-al-i-do-cious|').
+
+Plain \TeX\ loads exactly 4447 patterns into \TeX's memory, beginning with
+`{\tt\\0.\\0a\\0c\\0h\\4}' and ending with `{\tt\\4z\\1z\\2}' and
+`{\tt\\0z\\4z\\0y$_0$}'. The interletter values in these patterns are all
+between 0 and~5; a large odd value like the 5 in `{\tt\\0h\\5e\\0l\\0o\\0}'
+forces desirable hyphen points in words like `|bach-e-lor|' and
+`|ech-e-lon|', while a large even value like the 4 in `{\tt\\0h\\0a\\0c\\0h\\4}'
+suppresses undesirable hyphens in words like `|tooth-aches|'. Liang
+derived these patterns by preparing a special version of {\sl Webster's
+Pocket Dictionary\/} (Merriam, 1966) that contains about 50,000 words including
+derived forms. Then he checked a preliminary set of patterns obtained from
+this data against an up-to-date hyphenation dictionary of about
+115,000 words obtained from a publisher; errors found in this run
+led to the addition of about 1000 words like |camp-fire|,
+|Af-ghan-i-stan|, and |bio-rhythm| to the pocket dictionary list. He
+weighted a few thousand common words more heavily so that they would be more
+likely to be hyphenated; as a result, the patterns of plain \TeX\ guarantee
+complete hyphenation of the 700 or so most common words of English,
+as well as common technical words like |al-go-rithm|. These patterns
+find 89.3\% of the hyphens in Liang's dictionary as a whole, and
+they insert no hyphens that are not present.
+
+Patterns derived from the common words of a language tend to work well
+on uncommon or newly coined words that are not in the original dictionary.
+For example, Liang's patterns find a correct subset of the hyphens in the
+word that all of today's unabridged dictionaries agree is the longest
+in English, namely
+\begintt
+pneu-monoul-tra-mi-cro-scop-ic-sil-i-co-vol-canoco-nio-sis.
+\endtt
+They even do fairly well on words from other languages that aren't
+too distant from English; for example, the pseudo-^{German} utterances
+of Mark ^{Twain}'s {\sl Connecticut Yankee\/} come out with only nine or
+ten bad hyphens: % see Chapter 23 of Twain's novel
+\begintt
+Con-stanti-nop-o-li-tanis-cher-
+ dudel-sack-spfeifen-mach-ers-ge-sellschafft;
+Ni-hilis-ten-dy-na-mitthe-
+ aterkaestchensspren-gungsat-ten-taetsver-suchun-gen;
+Transvaal-trup-pen-tropen-trans-port-
+ tram-pelth-iertreib-er-trau-ungsthrae-nen-tra-goedie;
+Mekka-musel-man-nen-massen-menchen-
+ mo-er-der-mohren-mut-ter-mar-mor-mon-u-menten-macher.
+\endtt
+% Note: I've faithfully copied Twain's errors, like "menchen" for "menschen"
+% and "attentaets" for "attentats"; also, TeX might suppress the final
+% hyphens in ultralong words, but I'm showing what the patterns say
+But when plain \TeX\ is tried on the name of a famous ^{Welsh} village,
+^^{Llanfair P. G.}
+\begintt
+Llan-fair-p-wll-gwyn-gyll-gogerych-
+ wyrn-drob-wl-l-l-lan-tysil-i-o-gogogoch,
+\endtt
+linguistic differences became quite evident, since the correct hyphens are
+\begintt
+Llan-fair-pwll-gwyn-gyll-go-ger-y-
+ chwyrn-dro-bwll-llan-ty-sil-i-o-go-go-goch.
+\endtt
+Appropriate pattern values for other languages can be derived by applying
+Liang's method to suitable dictionaries of hyphen points.
+
+^{Dictionaries} of English do not always agree on where syllable boundaries
+occur. For example, the {\sl American Heritage Dictionary\/} says
+`|in-de-pend-ent|' while {\sl Webster's\/} says `|in-de-pen-dent|'.
+Plain \TeX\ generally follows Webster except in a few cases where other
+authorities seem preferable.
+
+\begingroup
+\hyphenpenalty=-1000 \pretolerance=-1 \tolerance=1000
+\doublehyphendemerits=-100000 \finalhyphendemerits=-100000
+[From here to the end of this appendix, \TeX\ will be typesetting with
+\begindisplay
+^|\hyphenpenalty||=-1000 |^|\pretolerance||=-1 |^|\tolerance||=1000|\cr
+^|\doublehyphendemerits||=-100000 |^|\finalhyphendemerits||=-100000|\cr
+\enddisplay
+so that hyphens will be inserted much more often than usual.]
+
+\looseness-1
+The fact that plain \TeX\ finds only 90\% of the permissible hyphen points
+in a large dictionary is, of course, no cause for alarm. When word
+frequency is taken into account, the probability rises to well over 95\%.
+Since \TeX's line-breaking algorithm often succeeds in finding a way to
+break a paragraph without needing hyphens at all, and since there's a good
+chance of finding a different hyphen point near to one that is missed by
+\TeX's patterns, it is clear that manual intervention to correct or insert
+hyphenations in \TeX\ output is rarely needed, and that such refinements
+take a negligible amount of time compared to the normal work of
+keyboarding and proofreading.
+
+But you can always insert words into \TeX's ^{exception dictionary}, if
+you find that the patterns aren't quite right for your application.
+For example, this book was typeset with three exceptional words added:
+The format in Appendix~E includes the command
+\begintt
+\hyphenation{man-u-script man-u-scripts ap-pen-dix}
+\endtt
+which tells \TeX\ how to hyphenate the words `manuscript', `manuscripts',
+and `appendix'. Notice that both singular and plural forms of
+`manuscript' were entered, since the exception dictionary affects
+hyphenation only when a word agrees completely with an exceptional
+entry. \ (Precise rules for the ^|\hyphenation| command are
+discussed below.)
+
+If you want to see all of the hyphens that plain \TeX\ will find in some
+random text, you can say `^|\showhyphens||{|\<random text>|}|' and the
+results will appear on your terminal (and in the log file). For example,
+\begintt
+*\showhyphens{random manuscript manuscripts appendix}
+|smallskip
+Underfull \hbox (badness 10000) detected at line 0
+[] \tenrm ran-dom manuscript manuscripts ap-pendix
+\endtt
+shows the hyphen positions that would have been found in this book
+without the addition of any ^|\hyphenation| exceptions. Somehow the word
+`manuscript' slips through all of the ordinary patterns; the author added it
+as an exception for this particular job because he used it 80~times (not
+counting its appearances in this appendix).
+
+The |\showhyphens| macro creates an hbox that is intentionally
+^{underfull}, so you should ignore the warning about `|badness 10000|';
+this spurious message comes out because \TeX\ displays hyphens in compact
+form only when it is displaying the contents of anomalous
+hboxes. \ (\TeX\ wizards may enjoy studying the way |\showhyphens|
+is defined in Appendix~B.)
+
+\danger If you want to add one or more words to the exception dictionary,
+just say ^|\hyphenation||{|\<words>|}| where \<words> consists of one
+or more \<word> items separated by spaces. A \<word> must consist entirely
+of letters and hyphens; more precisely, a ``hyphen'' in this context is
+the token |-|\\{12}. A ``letter'' in this context is a character token whose
+category code is 11 or~12, or a control sequence defined by ^|\chardef|,
+or ^|\char|\<8-bit number>, such that the corresponding character has a nonzero
+^|\lccode|. \TeX\ uses the |\lccode| to convert each letter to ``^{lowercase}''
+form; a word-to-be-hyphenated will match an entry in the exception
+dictionary if and only if both words have the same lowercase form after
+conversion to lowercase.
+
+\danger \TeX\ will henceforth insert discretionary hyphens in the specified
+positions, whenever it attempts to hyphenate a word that matches an entry
+in the exception dictionary, except that plain \TeX\ blocks hyphens after the
+very first letter or before the last or second-last letter of a word.
+You must insert your own discretionary hyphens if you want to allow them
+in such positions. A |\hyphenation| entry might contain no hyphens at all;
+then \TeX\ will insert no hyphens in the word.
+
+\danger The exception dictionary is global; i.e., exceptions do not disappear
+at the end of a ^{group}. If you specify |\hyphenation| of the same word more
+than once, its most recently specified hyphen positions are used.
+
+\ddanger The exception dictionary is dynamic, but the pattern dictionary
+is static: To change \TeX's current set of hyphenation patterns, you
+must give an entirely new set, and \TeX\ will spend a little time putting them
+into a form that makes the hyphenation algorithm efficient. The
+command format is ^|\patterns||{|\<patterns>|}|, where \<patterns> is a
+sequence of \<pattern> items separated by spaces. This command
+is available only in ^|INITEX|, not in production versions of \TeX, since
+the process of pattern compression requires extra memory that can be put
+to better use in a production system. |INITEX| massages the patterns and
+outputs a format file that production versions can load at high speed.
+
+\ddanger A \<pattern> in the |\patterns| list has a more restricted form
+than a \<word> in the |\hyphenation| list, since patterns are supposed to
+be prepared by experts who are paid well for their expertise. Each
+\<pattern> consists of one or more occurrences of \<value>\<letter>,
+followed by \<value>. Here \<value> is either a digit (|0|\\{12} to |9|\\{12})
+or empty; an empty \<value> stands for zero. For example, the pattern
+`{\tt\\0a\\1b\\0}' can be represented as |0a1b0| or |a1b0| or |0a1b| or
+simply |a1b|. A \<letter> is a character token of category 11 or~12 whose
+|\lccode| is nonzero. If you want to use a ^{digit} as a \<letter>, you
+must precede it by a nonempty \<value>; for example, if for some reason you
+want the pattern `{\tt\\1a\\01\\2}' you can obtain it by typing
+`|1a012|', assuming that |\lccode`1| is nonzero. Exception: The character
+`|.|'~is treated as if it were a \<letter> of code~0
+when it appears in a pattern. Code~0 (which obviously cannot match a nonzero
+|\lccode|) is used by \TeX\ to represent the
+left or right edge of a word when it is being hyphenated.
+
+\ddanger Plain \TeX\ inputs a file called |hyphen.tex| that sets up the
+pattern dictionary and the initial exception dictionary. The file has the
+form
+\begindisplay
+|\patterns{.ach4 .ad4der .af1t .al3t|\quad$\cdots$\quad|zte4 4z1z2 z4zy}|\cr
+|\hyphenation{as-so-ciate as-so-ciates dec-li-na-tion oblig-a-tory|\cr
+| phil-an-thropic present presents project projects reci-procity|\cr
+| re-cog-ni-zance ref-or-ma-tion ret-ri-bu-tion ta-ble}|\cr
+\enddisplay
+The first thirteen exceptions keep \TeX\ from inserting incorrect
+hyphens; for example, `|pro-ject|' and `|pre-sent|' are words like
+`|re-cord|', that cannot be hyphenated without knowing the context.
+The other exception, `|ta-ble|', is included just to meet the claim
+that plain \TeX\ fully hyphenates the 700 or so most common words
+of English.
+
+\looseness-1
+\ddanger But how does \TeX\ decide what sequences of letters are ``words''
+that should be hyphenated? Let's recall that \TeX\ is working on a
+horizontal list that contains boxes, glue, rules, ^{ligatures}, ^{kerns},
+discretionaries, marks, whatsits, etc., in addition to simple characters;
+somehow it has to pick out things to hyphenate when it is unable to
+find suitable breakpoints without hyphenation. The presence of
+punctuation marks before and/or after a word should not make a word
+unrecognizable or unhyphenatable; neither should the presence of
+ligatures and kerns within a word. On the other hand, it is desirable to
+do hyphenation quickly, not spending too much time trying to handle
+unusual situations that might be hyphenatable but hard to recognize
+\hbox{mechanically}.
+
+\ddanger \TeX\ looks for potentially hyphenatable words by searching
+ahead from each glue item that is not in a math formula. The search
+bypasses characters whose |\lccode| is zero, or ligatures that begin
+with such characters; it also bypasses whatsits and {\sl^{implicit kern}\/}
+items, i.e., kerns that were inserted by \TeX\ itself because of information
+stored with the font. If the search
+finds a character with nonzero |\lccode|, or if it finds a ligature
+that begins with such a character, that character is called the
+{\sl starting letter}. But if any other type of item occurs before a suitable
+starting letter is found, hyphenation is abandoned (until after the next
+glue item). Thus, a box or rule or mark, or a kern that was explicitly
+inserted by ^|\kern| or~|\/|, must not intervene between glue and a
+hyphenatable word. If the starting letter is not lowercase (i.e.,
+if it doesn't equal its own |\lccode|), hyphenation is abandoned unless
+^|\uchyph| is positive.
+
+\ddanger If a suitable starting letter is found, let it be in font~$f$.
+Hyphenation is abandoned unless the ^|\hyphenchar| of~$f$ is a number
+between 0 and~255, inclusive.
+If this test is passed, \TeX\ continues to scan forward
+until coming to something that's not one of the following three
+``admissible items'': (1)~a character in font~$f$ whose |\lccode|
+is nonzero; (2)~a ligature formed entirely from characters of type~(1);
+(3)~an implicit kern. The first inadmissible item terminates this part of
+the process; the trial word consists of all the letters found in admissible
+items. Notice that all of these letters are in font~$f$.
+
+\ddanger If a trial word $l_1\ldots l_n$ has been found by this process,
+hyphenation will still be abandoned unless $n\ge\lambda+\rho$, where
+$\lambda=\max(1,\hbox{|\lefthyphenmin|})$ and
+$\rho=\max(1,\hbox{|\righthyphenmin|})$.
+^^|\lefthyphenmin| ^^|\righthyphenmin|
+\ (Plain \TeX\ takes $\lambda=2$ and $\rho=3$.) \ Furthermore, the items
+immediately following the trial word must consist of zero or more
+characters, ligatures, and implicit kerns, followed immediately by
+either glue or an explicit kern or a penalty item or a whatsit or an
+item of vertical mode material from ^|\mark|, ^|\insert|, or ^|\vadjust|.
+Thus, a box or rule or math formula or discretionary following too closely
+upon the trial word will inhibit hyphenation. (Since \TeX\ inserts
+empty discretionaries after ^{explicit hyphens}, these rules imply that
+already-hyphenated compound words will not be further hyphenated by
+the algorithm.)
+
+\ddanger Trial words $l_1\ldots l_n$ that pass all these tests are submitted
+to the hyphenation algorithm described earlier. Hyphens are not inserted
+before $l_\lambda$ or after $l_{n+1-\rho}$.
+If other hyphenation points are found,
+one or more discretionary items are inserted in the word; ligatures
+and implicit kerns are reconstituted at the same time.
+
+\ddanger Since ligatures and kerns are treated in quite a general manner,
+it's possible that one hyphenation point might preclude another because the
+ligatures that occur with hyphenation might overlap the ligatures
+that occur without hyphenation. This anomaly probably won't occur in real-life
+situations; therefore \TeX's interesting approach to the problem will
+not be discussed here.
+
+\ddanger According to the rules above, there's an important distinction
+between implicit and explicit kerns, because \TeX\ recomputes implicit
+kerns when it finds at least one hyphen point in a word. You can
+see the difference between these two types of kerns when \TeX\ displays
+lists of items in its ^{internal format}, if you look closely:
+`|\kern2.0|' denotes an implicit kern of $2\pt$, and `|\kern 2.0|'
+denotes an explicit kern of the same magnitude. The ^{italic correction}
+command~|\/| inserts an explicit kern.
+
+\ddanger The control sequence ^|\-| is equivalent to
+|\discretionary{\char|$\,h$|}{}{}|, where $h$ is the
+^|\hyphenchar| of the current font, provided that $h$ lies
+between 0 and~255. Otherwise |\-| is equivalent to
+|\discretionary{}{}{}|.
+
+\ddanger \looseness=-1
+So far we have assumed that \TeX\ knows only one style of
+hyphenation at a time; but in fact \TeX\ can remember up to 256
+distinct sets of rules, if you have enough memory in your computer. An
+integer parameter called ^|\language| selects the rules actually used;
+every ^|\hyphenation| and ^|\patterns| specification appends new rules
+to those previously given for the current value of\/ |\language|.
+\ (If\/ |\language| is negative or greater than 255, \TeX\ acts as if
+|\language|$\null=0$.) \ All |\patterns| for all languages must be
+given before a paragraph is typeset, if |INITEX| is used for
+typesetting.
+
+\ddanger \TeX\ is able to work with several languages in the same
+paragraph, because it operates as follows. At the beginning of a
+paragraph the ``current language'' is set equal to |\language|. Whenever a
+character is added to the current paragraph (i.e., in unrestricted
+horizontal mode), the current language is compared to |\language|; if
+they differ, the current language is reset and a whatsit node
+specifying the new current language is inserted before the character.
+Thus, if you say `|\def\french{\language1...}|' and `|mix| |{\french
+franc/ais}| |with| |English|', \TeX\ will put whatsits before the |f|
+and the~|w|; hence it will use language~1 rules when hyphenating
+|franc/ais|, after which it will revert to language~0. You can insert
+the whatsit yourself (even in restricted horizontal mode) by saying
+^|\setlanguage|\<number>; this changes the current language but it
+does not change |\language|. Each whatsit records the current
+|\lefthyphenmin| and |\righthyphenmin|.
+^^{French}
+
+\endgroup
+\endchapter
+
+If all problems of hyphenation have not been solved,
+at least some progress has been made since that night,
+when according to legend, an RCA Marketing Manager received
+a phone call from a disturbed customer. His 301 had just hyphenated ``God.''
+\author PAUL E. ^{JUSTUS}, {\sl There's More to Typesetting Than %
+ Setting Type\/} (1972)
+% in {\sl IEEE Transactions on Professional Commun. vol PC-15, pp. 13-15
+
+\bigskip
+
+The committee skeptically re-
+commended more study for a bill
+to require warning labels on rec-
+ords with subliminal messages re-
+corded backward.
+\author THE PENINSULA ^{TIMES TRIBUNE} (April 28, 1982)
+
+\eject
+ \beginchapter Appendix I. Index
+
+\maxdepth=4pt
+The author has tried to provide as complete an index as possible, so that
+people will be able to find things that are tucked away in obscure
+corners of this long book. Therefore the index itself is rather long. A
+short summary of the simpler aspects of \TeX\ appears at the beginning of
+Appendix~B; a summary of special symbols appears at the end of Appendix~F;
+a summary of other special things appears under `tables' below.
+
+\medskip\ninepoint
+Page numbers are \underbar{underlined} in the index when they represent
+the definition or the main source of information about whatever is being
+indexed. \ (Underlined entries are the most definitive, but not
+necessarily the easiest for a beginner to understand.) \ A page number is
+given in italics (e.g., `{\it123\/}') when that page contains an instructive
+example of how the concept in question might be used. Sometimes both
+underlining and italics are appropriate. When an index entry refers to a
+page containing a relevant exercise, the answer to that exercise (in
+Appendix~A) might divulge further information; an answer page is not
+indexed here unless it refers to a topic that isn't included in the
+statement of the relevant exercise.
+
+\smallskip
+Control sequence names that are preceded by an asterisk (*) in this index are
+primitives of \TeX; i.e., they are built in. It may be dangerous to
+redefine them.
+
+\begindoublecolumns
+\eightpoint \baselineskip=9.8pt minus .2pt % shooting for 53 or 54 lines/page
+\parskip=0pt plus .8pt
+\newdimen\sqht \sqht=2.4pt % \square
+\newbox\astbox \setbox\astbox=\hbox to0pt{\hss\lower1pt\hbox{*}}
+\raggedright \tolerance=5000 \hbadness=5000 \parfillskip 0pt plus 3em
+\ttglue=.4em
+\def\<#1>{\leavevmode\hbox{$\mkern-2mu\langle$#1\/$\rangle$}}
+\let\oldcstok=\cstok
+\def\cstok{\leavevmode\kern-2pt\oldcstok}
+\def\vdots{\vbox{\baselineskip 3\p@\kern2\p@\hbox{.}\hbox{.}\hbox{.}}}
+\def\ddots{\mathinner{\mskip1mu\raise5\p@\vbox{\kern2\p@\hbox{.}}\mskip2mu
+ \raise3\p@\hbox{.}\mskip2mu\raise\p@\hbox{.}\mskip1mu}}
+\def\cong{\mathrel{\mathpalette\@v@req\sim}} % congruence sign
+\def\@v@req#1#2{\lower.5\p@\vbox{\baselineskip\z@skip\lineskip-.0\p@
+ \ialign{$\m@th#1\hfil##\hfil$\crcr#2\crcr=\crcr}}}
+\def\see{{\sl see\/}~\ignorespaces}
+\def\also{\hfil\penalty50\hfilneg{\sl see~also\/}~\ignorespaces}
+\let\oldttv=\ttverbatim
+\let\+=\relax
+\def\sub{\penalty100 \vskip -\parskip \quad}
+\def\LaTeX{L\kern -.36em\raise.6ex\hbox{\sixrm A}\kern-.15em\TeX}
+\def\ttverbatim{\oldttv \catcode`\*=\other \catcode`\,=\other
+ \catcode`\.=\other \catcode`\;=\other \catcode`\@=\other \catcode`\+=\other}
+\let\comma=, \let\period=. \let\asterisk=* \let\semicolon=;
+\def\backup{\leavevmode\kern-1pt}\catcode`\-=11
+\catcode`\*=\active \def*{\leavevmode\copy\astbox}
+\catcode`\,=\active \def,{\eightrm\comma}
+\catcode`\;=\active \def;{\/\eightrm\semicolon}
+\catcode`\.=\active \def.{\eightrm\period\par\hangindent 2em }
+\catcode`\+=\active \def+#1{\ifcat0\noexpand#1$\setbox\z@\hbox{#1}\dp\z@=\z@
+ \underline{\box\z@}$\let\next=+%
+ \else\let\next=#1\fi \next}
+\catcode`\@=\active \let@=\eightit
+\parindent=0pt
+\hyphenpenalty=10000 \exhyphenpenalty=10000
+\def\newletter{\medbreak\hangindent 2em}
+\hangindent 2em
+\] (visible space), +3, 420, 429; \also \<space>, spaces.
+*|\|\] (control space), @8, 10, 19, @73, @74, 86--87, 154, @163, @167, 283,
+ +285, +290, 323, 351, @381.
+|#| (hash mark), 38, 51, 113, @200--202, @+203, @204--205, 228, +235, @236--240.
+|\#| ( \#\ ), 38, 51, +356.
+|##|, 203--205, 228, @359--362, @378--379.
+|###|, 88.
+|#{|, 204, @400.
+|$| (dollar sign), 4, 38, 51, 54, 86--88, @92, 127, @134--135, @185--186,
+ +269, 283, +287, +293.
+|\$| ( \$\ ), 38, 51, @202, @309, +356.
+|$$|, 86--89, +185, @186--196, @232, +287, +293, @375--376, @421.
+|%| (percent sign), @26, 38, 39, 43, 48, 51, 113, 124, @249, @337, @340, +343.
+|\%| ( \%\ ), 38, 43--44, 51, +356.
+|%%|, 112--113.
+|&| (ampersand), 38, 51, @175--177, @190--196, @231--248, +282, 385--386.
+\sub for preloaded formats, +25, 26, 344.
+|\&| ( \&\ ), 38, 51, 53, +356.
+|&&|, 241--242, @361, @412.
+|'| (apostrophe or right quote), 3--5, 51, @130, 155, 201, @305, 324,
+ +357, 394--395; \also octal.
+|\'| (acute accent), @7--9, 52--53, @305, @335, +356, @420.
+|''| ( '' ), @3--5, @24, 394--395.
+|`| (reverse apostrophe or left quote), 3--5, 51, 132, 134, @305, 391, 394--395;
+ \also alphabetic constant.
+|\`| (grave accent), 8, 52--53, @305, +356.
+|``| ( `` ), @3--5, @24, 394--395.
+|"| (double quote or ditto mark), 52, 53, 134; \also hexadecimal.
+|\"| (dieresis or umlaut accent), @7, 9, @24, @25, 52--53, 55, +356.
+|(| (left parenthesis), 51, 134, @140, @145--150, 345.
+|\(|, 409.
+|)| (right parenthesis), 51, 134, @140, @145--150, 345.
+|[| (left bracket), 51, 134, @146--148, 171, @408, @437.
+|[]|, +28, @+79, @302.
+|[1]|, 23, +119.
+|]| (right bracket), 51, 134, @146--147, 171, 345, @408, @437.
+|{| (left brace), @13--14, @19--21, 38, 51, @200--202, +203--+204, @205--206,
+ 216, +269, 275--276, +283, +286, +291, @330.
+|\{| ( $\{$ ), 134, @146--147, @174--175, +361.
+|{}|, @19, @54, 82, 95, 114, @129, 130, 150, 169, @196, @242, 253, 262,
+ @305, @315, @318, 351, 393.
+|}| (right brace), @13--14, @19--21, 38, 51, @200--202, +203--+204, @205--206,
+ +269, 275--276, +279, 301, @330.
+|\}| ( $\}$ ), 134, @146--147, @174--175, +361.
+\goodbreak
+|+| (plus sign), 51, 132, 268.
+|\+| (begin tabbed line), @231--234, 249, 339, +354.
+|-| (hyphen or minus), @4, 51, 93, 95, @127, 132, 268.
+*|\-| (discretionary hyphen), 95, 283, 287, 292, +455.
+|--|, |---|, \see en-dash, em-dash.
+$\pm$, \see |\pm|.
+$\mp$, \see |\mp|.
+|*| (asterisk), 23, 25, 51, 99, 113, 116, @132--133, 154, @326.
+|\*| (discretionary $\times$), 173, +357.
+|**|, 23, 25, 344.
+|/| (slash), 51, @132, @146--147, @320, 430.
+*|\/| (italic correction), @14, @64, +287, +292, @306, 382, 455.
+\| (vertical line), 52, 53, 132, @146--147, @171, @174, 438.
+|\|\| ( $\Vert$ ), @146--147, @171, +361, 435, 438.
+|\| (backslash), @7, 38, 39, 40, 51, 146--147, +343, @436.
+|\\|, 38, @356, @378, @418.
+|<| (less than sign), 52, 53, @133, 150, 154, 209.
+\leavevmode{\tt\rlap<\char`\_}, 45, 135, 368--369; \also |\le|.
+|=| (equals sign), 51, @133, 209, 226, 275, @376.
+|\=| (macron accent), 52, @53, +356.
+$\buildrel \rm def \over=$, 181, @437.
+\leavevmode{\tt\rlap/=}, 45, 135, 368--369; \also |\ne|.
+|>| (greater than sign), 52, 53, @133, 150, 209.
+|\>| (medium space), 167, 171, +357.
+\leavevmode{\tt\rlap>\char`\_}, 45, 135, 368--369; \also |\ge|.
+\<>, \see angle brackets.
+|,| (comma), 51, @72--73, @134, @161--162, @172--174, @394--395, 430.
+|\,| (thin space), 5, @167--173, 305, +357, @409--410.
+|.|\ (period), 51, @72--73, 133--134, 149, @161, +345, @394--395, 430.
+\sub space after, @73--75, +76.
+|\.|\ (dot accent), 52, +356.
+$\ldots\,$, \see |\ldots|, ellipses.
+$\cdots\,$, \see |\cdots|, ellipses.
+\leavevmode\smash{$\vdots\,$}, \see |\vdots|, ellipses.
+|;| (semicolon), 51, @134, 161.
+|\;| (thick space), 167, 171, +357.
+|:|\ (colon), 51, @133--134, @155, 161, @174, @438.
+$:=$, 133.
+|?|\hbox{ }(question mark), 31, 51, 73, 161.
+|?`|\hbox{ }(open question), 51.
+|!|\hbox{ }(exclamation point), 51, @72, 73, 75, @169.
+|!`|\hbox{ }(open exclamation), 51.
+|\!|\ (negative thin space), +167, @169, +357.
+|_| (underscore), 38, 51, @128--130, 134.
+|\_| ( \_ ), 38, @165, +356.
+|^| (hat), 38, 51, @128--130, 134, 369, @423.
+|\^| (circumflex accent), 52--53, +356.
+|^^|, 45, +47, @48, +368, 370, 423.
+|^^M| (ASCII \<return>), 45, 249, 331, +343, 345, @348, @352, 380, @390--391,
+ @421, 423.
+|\^^M|, 8, @305, +351.
+|~| (tilde), 38, 51, 343, +353; \also ties.
+|\~| (tilde accent), 52, +356, 387.
+{\tt\char'13} and {\tt\char'14}, 135, 343, 368--369, 429; \also |\uparrow|, |\downarrow|.
+|@| (at sign), 51, 98--99, 132, 134, @344, @364, @408, 414.
+|@@|, 98--99.
+|\@ne|, +345.
+\newletter
+|\aa| ( \aa\ ), +356.
+|\AA| ( \AA\ ), +356.
+abbreviations, @73--74, @340; \also macros.
+*|\above| (general fraction), @143, 152, +292, 444--445.
+*|\abovedisplayshortskip|, +189, 274, @348, @415.
+*|\abovedisplayskip|, +189, +190, 194, 274, 291, @348, @415.
+*|\abovewithdelims|, 152, +292, 444--445.
+absolute value, @146, @149, @171, @175.
+Acc atom, 158, 289, 443.
+*|\accent| (general accent), 9, 54, 86, 283, +286.
+accents (\'{} \`{} \"{} etc\period), 7, @52--53, @339, +356, 357, 427--429.
+\sub as ligatures, 46, 54.
+\sub in math, @135--137, 141, 164--165, +359, 435, +443.
+\sub on top of accents, 136.
+\sub table, 52, 135, 339.
+|\active| (category 13), @241, +343, @395, @421.
+active character, 37, 40, 209, 241, 307, 377, 380--381, 394--395.
+active math character, 155, +289.
+active spaces, 381, 394, 421.
+|\acute| (math accent: $\acute x$), 135, +359.
+acute accent (\'{}), \see |\'|, |\acute|.
+|\address|, @403--404, +407.
+*|\adjdemerits|, +98, 273, @314, @348.
+*|\advance|, @21, @118--119, @218, @256, +276, @355.
+|\advancepageno|, 256, @257, +362, @416.
+|\ae| ( \ae\ ), vii, 17, 45--46, @52--53, @239, +356.
+|\AE| ( \AE\ ), @52--53, +356.
+*|\afterassignment|, 215, +279, @352, @364, @376, @400.
+*|\aftergroup|, 215, +279, @363, @374, @377, 379.
+`ain, \see reverse apostrophe.
+al-Khw\^arizm\^\i, abu `Abd All\^ah Mu\d{h}ammad ibn M\^us\^a, 53.
+|\aleph| ( $\aleph$ ), 9, +358, 435.
+Alice, 4, 387, 394.
+alignment displays, 190, 193, +291.
+\<alignment material>, 282, 285.
+alignments, 231--249, +282, 302--303, 385--386, 392; \also tabbing.
+Alka-Seltzer, 404--405.
+all caps, \see |\uppercase|.
+Allen, Todd Andrew, 377.
+allocation, 121--122, +346, +347.
+|\allocationnumber|, 346.
+|\allowbreak|, @174, +353, @396.
+|\allowhyphens|, @394, +395.
+|\alpha| ( $\alpha$ ), @127, @201, +358, 434.
+|\Alpha|, 434.
+alphabetic constants, @44--46, 215, +269, 270, 309, +367, 385.
+alternatives, \see |\cases|.
+|\amalg| ( $\amalg$ ), +358, 436.
+American Mathematical Society, ii, vii.
+ampersand, +25, 38, 51, @175--177, @190--196, @231--248, +282,
+ @339, 344, 385--386, 428.
+\AmSTeX, 164, +420, 434.
+anatomy of \TeX, 38--39, +46, 85, +267, 349, 373, 379, 385, 386, +456.
+|\angle| ( $\angle$ ), +358, 435.
+angle brackets ( $\langle\,\rangle$ ), 59, @146--147, 150,~156, +268, 420, 437;
+ \also \hbox to0pt{|\langle|, |\rangle|\period\hss}\par\hangindent2em
+angstrom unit, \see |\AA|.
+|\annotations|, 403, @404, +407.
+answers to the exercises, 305--337.
+Antisthenes of Athens, 239.
+apostrophe, 3--5, 51, @130, 155, 201, 324; \also octal.
+|\approx| ( $\approx$ ), 128, 436.
+Arabic, 66.
+|\arccos| ( $\arccos$ ), 162, +361.
+Archytas of Taras, 239.
+|\arcsin| ( $\arcsin$ ), 162, 361.
+|\arctan| ( $\arctan$ ), 162, 361.
+Arenski\u\i, Anton Stepanovich, 410.
+|\arg| ( $\arg$ ), 162, 361.
+arguments, 33, 200--205, @263, 268, @375--380.
+Aristippus of Cyrene, 239.
+Aristophanes, 239.
+Aristotle, 35.
+arithmetic, 117--119, \see |\advance|, |\multiply|, |\divide|.
+\<arithmetic>, +276.
+arrays, @176--178, \see matrices.
+arrows, @146--147, @182, @226, +437.
+|\arrowvert| ( $\big\arrowvert$ ), 150, +359.
+|\Arrowvert| ( $\big\Arrowvert$ ), 150, +359.
+{\sl\backup The Art of Computer Programming}, 259--260.
+as is, \see |\obeylines|, |\obeyspaces|, verbatim.
+ASCII, 3, 43--45, 49, 214, 343, +367, 371.
+\<assignment>, +275.
+assignments, 275--278, 373.
+|\ast| ( $\ast$ ), 436.
+asterisk, 23, 25, 51, 99, 113, 116, @132--133, 154, @326.
+|\asymp| ( $\asymp$ ), 436.
+|at|, 16--17, 60, 213, 277, @408, @414, 433.
+\<at clause>, +277.
+at sign, 51, 98--99, 132, 134, @344, @364, @408, 414.
+AT\&T, 247.
+atoms, 157--159, 170--171, 289--290, 441--447.
+\sub table of atomic types, 158.
+*|\atop|, @143, @145, 152, @178, +292, +444.
+*|\atopwithdelims|, @152, +292, @324, @360, +444.
+author, typesetting by, 182, 412--413.
+auxiliary spaces, \see ties.
+axis line, 150--152, 179, 443--447.
+\newletter
+|\b| (bar-under accent), 52, +356.
+Bach, Johann Sebastian, 408.
+Bach, P\period\ D\period\ Q\period, 410--411, 481.
+backslash, @7, 38, 39, 40, 51, 146--147, 343, @436.
+|\backslash| ( \backup$\backslash$ \backup), @38, @146--147, +359, 435, @436.
+backspacing, @66, 82--83, 222, @394--395, @418.
+Backus, John Warner, 268.
+Bacon, Francis, viscount St\period\ Albans, 41.
+Bacon, Leonard, 1.
+bad breaks, avoiding, 27--30, 91--94, 173--174, 197.
+badness, @28--30, 97--99, 111--113, 302.
+*|\badness|, 214, @229, 271.
+\<balanced text>, 275, +276, 385.
+balancing columns, 386--388, @396--397, @417.
+|\bar| (math accent: $\bar x$), 135, @136.
+bar accent (\={}), \see |\=|, |\bar|.
+bar-under accent (\b{\hbox{ }}), \see |\b|.
+Barrett, Percy Reginald, 197.
+Barrough, Philip, 229.
+baseline, 15, +63, 77, 80--81, 150.
+*|\baselineskip| (normal vertical distance between baselines), @78--79, +80,
+ 104, 194, 253, @256, 274, 281, 342, 349, @351--352, @409, @414--415.
+*|\batchmode|, +32, +277, 299, @336.
+Batey, Charles, 197.
+beauty, 1.
+Beck, Simone, 233, 236.
+Beethoven, Ludwig van, 408, 410--411.
+Beeton, Barbara Ann Neuhaus Friend Smith, 483.
+|\beginchapter|, 418.
+*|\begingroup|, 21, @249, @262, +279, @380, @407, @419.
+|\beginsection|, @340--341, +355.
+*|\belowdisplayshortskip|, +189, 274, @348, @415.
+*|\belowdisplayskip|, +189, +190, 194, 274, 291, @348, @415.
+Bemer, Robert William, \see TEX, ASCII.
+bent bars, \see angle brackets.
+Bertholle, Louisette, 233, 236.
+|\beta| ( $\beta$ ), @127, 434.
+|\Beta|, 434.
+|\bf| (use {\bf boldface} type), @13--14, @164--165, @328, +351, @409,
+ @414--415.
+|\bffam|, +351, @414--415.
+|\bgroup| (implicit |{|), 269, +351, @363, 382, +385, @407, @421.
+Bibby, Duane Robert, i.
+Biblical references, 303, @311.
+bibliographies, 4, @74, 93, @340--341.
+|\big| (largish delimiter), 147, 171, @320, @359, +360, @414--415.
+|\Big| (between |\big| and |\bigg|), 147, 175, @359, +360.
+big-$O$ notation, 132, @161--162, @169.
+big point, 57, \see |bp|.
+|\bigbreak|, 111, 116, +353, @363.
+|\bigcap| (large $\cap$), 147, 435.
+|\bigcirc| ( $\bigcirc$ ), 436.
+|\bigcup| (large $\cup$), 147, 435.
+|\bigg| (large delimiter), @147, 175, 196, 327, @359, +360.
+|\Bigg| (larger than |\bigg|), 147, 175, @359, +360.
+|\bigggl|, |\bigggr|, 324.
+|\biggl| (|\bigg| left delimiter), 147, @149, +359, 437.
+|\Biggl| (|\Bigg| left delimiter), 147, 149, +359, 437.
+|\biggm| (|\bigg| middle delimiter), 147, +360.
+|\Biggm| (|\Bigg| middle delimiter), 147, +360.
+|\biggr| (|\bigg| right delimiter), 147, @149, +359.
+|\Biggr| (|\Bigg| right delimiter), 147, 149, +359.
+|\bigl| (|\big| left delimiter), @146--147, 149--150, 155, 171, @175, +359, 437.
+|\Bigl| (|\Big| left delimiter), 147, +359, 437.
+|\bigm| (|\big| middle delimiter), 147, 171, @175, +359.
+|\Bigm| (|\Big| middle delimiter), 147, +359.
+|\bigodot| (large $\odot$), 435.
+|\bigoplus| (large $\oplus$), 435.
+|\bigotimes| (large $\otimes$), 435.
+|\bigr| (|\big| right delimiter), @146--147, 149--150, 171, @175, +359.
+|\Bigr| (|\Big| right delimiter), 147, +359.
+|\bigskip|, 70, 109, 111, 115--116, +352, @355, @407, @410--412.
+|\bigskipamount|, 123, +349, @352--353, @363.
+|\bigsqcup| (large $\sqcup$), 435.
+|\bigtriangledown| ( $\bigtriangledown$ ), 436.
+|\bigtriangleup| ( $\bigtriangleup$ ), 436.
+|\bigtype|, 408--409, 411.
+|\biguplus| (large $\uplus$), +358, 435.
+|\bigvee| (large $\vee$), 435.
+|\bigwedge| (large $\wedge$), 435.
+Bill, 387, 394.
+Bin atom, 158, 170--171, 289, 442--444, 446.
+binary operations, @132--133, 154--155, 164, 196, 358, 435, +436;
+ \also Bin atom.
+binary search, 387--388.
+binomial coefficient, 143, \see |\choose|.
+*|\binoppenalty|, 101, 174, 272, 322, @348, +446.
+black box, 64, +221, +222.
+blackboard bold (e\period g\period, $\rm I\!R$), 164, 434.
+blank line in input file, 24, 37, +47, @340--341, 381.
+blank space, \see spaces.
+Blase B\"ohning, Maria Dorothea, 248.
+block structure, \see grouping.
+block style, 405--407.
+|\bmit| (boldface math italic), 156.
+|\bmod| ( $\bmod$ ), @164, 322, +361.
+|\body|, @403--404, +407.
+Boehm, Peter James, 159.
+B\"ohning, Jobst Heinrich, 248.
+B\"ohning, Martin John Henry, 248.
+Bohning Knuth, Louise Marie, 248.
+boldface, 13, 156, 164--165, 386.
+book design, 412.
+book preparation, 303, 425.
+|\bordermatrix|, @177, +361.
+|\bot| ( $\bot$ ), 435.
+*|\botmark|, 213, +258, 259--260, @262--263, 280.
+boundary item, 157, 442, 446.
+Bourbaki, Nicolas, 106.
+|\bowtie| ( $\bowtie$ ), +358, 436.
+\<box>, 120, 222, +278, 282, 285, 290.
+*|\box| (use box register), 120--122, 151, 222, +278, 346, @354, @386, @387.
+|\box255|, 125, 253--258.
+\<box dimension>, +271, 277.
+box displays, 66, 75, 79, 158--159, 302, 455.
+box memory, 300, 394.
+\<box or rule>, +281.
+\<box size assignment>, +277.
+\<box specification>, 222, +278.
+\cstok{boxed} control sequence names, 38.
+boxed material, @223, @420.
+boxes, 63--67, 77--83, 221--229.
+|\boxit|, 223, 331.
+*|\boxmaxdepth|, +81, 113, 249, @255, 274, @348.
+|bp| (big point), +57, 270.
+|\brace| (notation like $n\brace k$), +360.
+|\braceld|, |\bracelu|, |\bracerd|, |\braceru| (pieces of horizontal braces),
+ @+357.
+braces, 51, 216, +269, 275--276, 279, 283, 286, 289--291, 330, 345, 385--386.
+\sub for arguments to macros, @20, @200--202, +203, +204, @205--206, 385--386.
+\sub for grouping, @13--14, @19--21, 232, 248, 253.
+\sub horizontal, @176, 225--226, @339.
+\sub implicit, +269, \see |\bgroup|, |\egroup|.
+\sub in math formulas, @145--147, @174--176.
+|\bracevert| ( $\big\bracevert$ ), 150, +359.
+|\brack| (notation like $n\brack k$), +360.
+brackets, 51, 134, @146--148, 171, @408, @437.
+|\break| (force line or page break), 94, 97, 106, 114, 193, +353.
+breakpoints, +96, 97--100, +110, 111--114, 394.
+\sub avoiding bad, 27--30, 91--94, 109--111, 173--174, 197.
+\sub discretionary, @95--96, 173, +287, +292, @357.
+\sub forcing good, 94, 105, 109--111, 114.
+\sub in displays, 195--197.
+\sub in formulas, 173--174, 446--447.
+|\breve| (math accent: $\breve x$), 135.
+breve accent (\u{}), \see |\u|, |\breve|.
+British pound sign, 54.
+brochures, 251.
+*|\brokenpenalty|, +104, 105, 272, @348.
+Brooks, Frederick Phillips, Jr\period, 365.
+Brown, Peter John, 425.
+|\buildrel|, +361, @437.
+built-up (extensible) characters, 442.
+built-up fractions, \see |\over|.
+|\bull| ( $\bull$ ), +420.
+Bull, John, 239.
+|\bullet| ( $\bullet$ ), @133, 154, @355, 436.
+bulleted lists, 102, 105.
+business correspondence, 200, 403--408.
+|by|, @118, +276.
+|\bye|, 87--88, @340, +357.
+Byron, George Gordon No\"el Byron, baron, vii.
+\newletter
+|\c| (cedilla accent), @24--25, 52, +356.
+|\cal| (calligraphic caps), 164, +351, 431, 434.
+calculus, 168--169, 180--181.
+camera alignment, 416--417.
+|\cap| ( $\cap$ ), 133, 436.
+capacity of \TeX, 100, 300--301, 383.
+caps and small caps, 203.
+captions, 115.
+caret, 369.
+caron, \see h\'a\v cek.
+carriage return, 231, \see \<return>, |\cr|.
+|\cases| ( $\bigl\{{\cdots\atop\cdots}$ ), 175, +362.
+Caswell, Herbert Ernest, 413.
+*|\catcode|, @39, 134, 214, +271, 305, @343, @380--382, @384, @390--391,
+ @421, @424.
+category codes, 37--40, +47, 48, 203--205, 209--210, 214, 381.
+\sub table, 37.
+|cc| (cicero), 57, 270.
+|\cdot| ( $\cdot$ ), @133, @172, 319, 436.
+|\cdotp|, 358, +359, 438.
+|\cdots| ( $\cdots$ ), @172, @176, @180--181, +359, 438.
+cedilla accent (\c{\ }), 25, 52, 54, \see |\c|.
+ceiling brackets ( $\lceil\,\rceil$ ), 146--147, \see |\lceil|, |\rceil|.
+centering, 71, 233, 236.
+|\centering|, +347, 348, 362.
+|\centerline| (make a centered line), @20, @24, 33, 71, 85, 101, @117, 232,
+ 311, @340, +353.
+|\cents| ( \hbox{\rm\rlap/c} ), 140, +319.
+Ces\`aro, Ernesto, 53.
+Cha\u\i kovski\u\i, P\"etr Il'ich, 410--411.
+*|\char|, @43--45, 76, 86, 155, 283, +286, 289, @340, @427, 452.
+\<character>, +289.
+character codes, 43--46, 367--370; \also category codes.
+\<character token>, +270.
+*|\chardef|, 44, 121, 155, 210, 214, 215, 271, +277, 336, @343, @345, @356, 452.
+\<chardef token>, +271, 283, 286, 289.
+Charles XII of Sweden, 92.
+Chaundy, Theodore William, 197.
+|\check| (math accent: $\check x$), 135.
+check accent (\v{}), \see |\v|, |\check|.
+chemical typesetting, 179.
+Cherry, Lorinda Landgraf, 159.
+|\chi| ( $\chi$ ), 1, 434.
+Chicago, University of, Press, 125, 293, 418, 439.
+Child, Julia, 233, 236.
+Children's Television Workshop, ii.
+choice, four-way, 157, +292, +442.
+|\choose| (notation like \setbox0=\hbox{$n\choose k$}\dp0=0pt\box0),
+ 139, 143, 152, 178, +360.
+Christie Mallowan, Dame Agatha Mary Clarissa (Miller), 249.
+cicero, \see |cc|.
+|\circ| ( $\circ$ ), @133, @323, 436.
+circles, \see |\circ|, |\bigcirc|.
+circular quotation, 101.
+circumflex, 369, \see hat.
+circumflex accent (\^{}), 52, +356, \see |\^|.
+classes of math characters, table, 154.
+*|\cleaders|, +224, 225--226, @357, @374.
+|\cleartabs|, @234, +354.
+Close atom, 158, 170--171, 289, 443--444, 446.
+*|\closein|, 217, 280.
+*|\closeout|, 226--228, 254, +280, @422.
+|\closing|, @403--404, +407.
+closings, 134, 147, 154--155, 359, +437; \also Close atom.
+club lines, 104, 272, 398.
+*|\clubpenalty|, 104, 113, 272, 317, @348, @419.
+|\clubsuit| ( $\clubsuit$ ), 435.
+|cm| (centimeter), @24, +57, 270.
+|cmbx| fonts, 60, 350, 413, 428, 433.
+|cmex| fonts, 157, 225, 350, 432--433.
+|cmmi| fonts, 350--351, 413--414, 430, 433.
+|cmr| fonts, 16--17, 60, 63--64, 76, 350, 413, 427, 433.
+|cmsl| fonts, 63--64, 350, 413, 428, 433.
+|cmsy| fonts, 157, 350--351, 413--414, 431.
+|cmti| fonts, 350, 413, 428, 433.
+|cmtt| fonts, 60, 350, 413--414, 429, 433.
+\<code assignment>, +277.
+\<codename>, +271.
+codes for characters, 43--46, 367--370.
+Coelho, Manuel Rodrigues, 239.
+coerce \<dimen> to \<number>, 270.
+coerce \<glue> to \<dimen>, 270.
+coerce \<number> to \<dimen>, 336.
+\<coerced dimen>, +270.
+\<coerced integer>, +269.
+\<coerced mudimen>, +270.
+collective signs, \see large operators.
+colon, 51, @133--134, @155, 161, @174, @438.
+|\colon| ( $\colon$ ), @134, +359, @438.
+color, 229.
+column vector, 177.
+column width, 29, 231, @257, @387, 417.
+|\columns|, 231, 354.
+comma, 51, @72--73, @134, @161--162, @172--174, @394--395, 430.
+commands, 267--293.
+comments, 26, 47, 337, @340.
+communication between macros, 211, 328--329, 375--376, 407--408.
+commutative diagram, @182.
+composing stick, 64--65.
+compound fraction, @143.
+compound matrix, @181.
+Computer Modern fonts, 16, 350, 427--438.
+computer programs, 38, 165, @234.
+concert programs, 408--412.
+conditionals, 206--208, +209, +210, 211, 240, @308, @384.
+|\cong| ( $\cong$ ), 151, +360, 436.
+constants, +269, 270, 308.
+continued fractions, @142.
+\<control sequence>, +275, 277.
+control sequences, 7--11, +46, 199, 457.
+\sub misspelled, 31--32.
+control space (|\|\]), @8, 10, 19, @73, @74, 86--87, 154, @163, @167, 283,
+ +285, +290, 351, @381.
+control symbols, 7--8, 46--47.
+control words, 7--8, 38, 46--47, 204.
+coordinates, 389.
+|\coprod| (large $\amalg$), 435.
+*|\copy| (copy a box), 120, 151, +222, 278, @329, @374, @386, @407.
+|\copyright| ( \copyright\ ), ii, @308, 339, +356.
+Cornet, Peeter, 239.
+Correa de Arauxo, Francisco, 239.
+correspondence, 200, 403--408.
+|\cos| ( $\cos$ ), 162, 361.
+|\cosh| ( $\cosh$ ), 162, 361.
+cost of a page break, 111--113, 124.
+|\cot| ( $\cot$ ), 162, 361.
+|\coth| ( $\coth$ ), 162, 361.
+*|\count| registers, 118--122, 207--208, +271, +276, @346--347, @379.
+\sub |\count0|, +119, @207, 252--254, +362.
+*|\countdef|, 119, 121, 210, 215, 271, +277, @346--347.
+\<countdef token>, +271.
+Cowper, William, 35.
+*|\cr| (end of aligned row), @175--177, @190--197, @231--238, +245, +248,
+ 275, 282, 351, 352, 385--386, @412, @418, @421.
+\sub avoiding, 249.
+cramped styles, 140--141, 445.
+*|\crcr| (force |\cr|), 249, 275, 282, @361--362, 385, @412, @421.
+Crocker, Betty, 425.
+cross, \see |\dag| (~\dag~), |\times| (~$\times$~).
+crotchets, \see brackets.
+|\csc| ( $\csc$ ), 162, 361.
+*|\csname|, 40--41, +213, @348, @375.
+|\csname\endcsname|, 46, 308.
+cube root, @130--131.
+Cummings, Edward Estlin, 49.
+|\cup| ( $\cup$ ), 133, 436.
+curly braces, \see braces.
+current font, 13, 20, 154, 163, 213--214.
+current page, 112, 122--125, 278, 280.
+cyclic preambles, +241, @242, @246, @361, @412.
+Cyrillic characters, 370.
+\newletter
+|\d| (dot-under accent), 52--53, +356.
+|\dag| ( \dag\ ), 53, 117, +356, 438--439.
+|\dagger| ($\dagger$ as binary operator), 436, 439.
+Dale, Robert William, 283.
+|\danger|, 419.
+dangerous bend, v--vi, 5, 15, 44--45, 70, 419.
+Danish characters, 45--46, 52--53, 370.
+|\dash|, 30.
+dashes, 4, @26, @30, 51, 93, 95.
+|\dashv| ( $\dashv$ ), 436.
+date, today's, 406.
+*|\day|, 273, 349, @406.
+|dd| (didot point), +57, 270, 272.
+|\ddag| ( \ddag\ ), 117, +356, 438--439.
+|\ddagger| ($\ddagger$ as binary operator), 436, 439.
+|\ddot| (math accent: $\ddot x$), 135, +359.
+|\ddots| ( \smash{$\ddots$} ), 177, +359.
+|\ddt| (debugging aid), 248.
+De Vinne, Theodore Low, 107.
+*|\deadcycles|, 214, +255, 264, 271, 283, @400.
+debugging, 205, 248, 298--303, 347.
+decent lines, 97, 99.
+\<decimal constant>, +270.
+decimal points, 57, 134, 240.
+\<def>, +275.
+*|\def|, 44, 136, 199--208, 215, 275--276.
+default output routine, 253--255.
+default rule thickness, 443--447.
+default values of parameters, 348--349.
+*|\defaulthyphenchar|, 273, @348.
+*|\defaultskewchar|, 273, @348.
+defining a control sequence, 199--208.
+\<definition>, +275.
+\<definition text>, +275.
+|\deg| ( $\deg$ ), 162, 361.
+degrees ( $^\circ$ ), @180.
+*|\delcode|, +156, 214, 271, +290, @345.
+\<delete> (ASCII code 127), 37, 39, 343, 369.
+deleting tokens, 32, 215, @295--297.
+\<delim>, 289--290.
+delimited arguments and parameters, 203--205, 249, @263, @375--377, @407.
+*|\delimiter|, +156, 289--290, @359.
+*|\delimiterfactor|, 152, 273, @348, +446.
+delimiters, 145--150, 156--157, @171, +290, 437, +442.
+*|\delimitershortfall|, 152, 274, @348, +446.
+|\delta| ( $\delta$ ), @127, 434.
+|\Delta| ( $\Delta$ ), @169, @186, 434.
+demerits, 94, 97--99, 273, @451.
+denominator, 141, 152, 179, 444--445.
+|depth|, 221, 224, +282, 337.
+depth of a box, 63--67, 77, 80--82, 225.
+Derek, Bo, 293.
+descenders, 63, 113, 319, 323.
+design size, 16--17, 213.
+|\det| ( $\det$ ), 162, +361.
+device-independent output, 23; \also |.dvi|.
+Diabelli, Antonio, 408.
+diagnostic form of lists, 66, 75, 79, 158--159, 302, 455.
+dialogs with the user, 217--218.
+|\diamond| ( $\diamond$ ), 436.
+diamond leaders, viii.
+|\diamondsuit| ( $\diamondsuit$ ), 435.
+D\'\i az de la Pe\~na, Maximiliano Antonio Tem\'\i stocles, 384.
+Dick and Jane, 72--74, 76.
+Dictionaries, 259, 449--453.
+Didot, Fran\c cois Ambroise, 57.
+didot point, 57, \see |dd|.
+dieresis (\"{}), +52, 53, 356, \see |\"|.
+diesis ( \ddag\ ), \see |\ddag|.
+Dieter, Ulrich Otto, 14.
+\<digit>, +269.
+\<digit string>, 57.
+digits, 51, 132, 435, 453.
+\sub width of, 60, 241, 427.
+|\dim| ( $\dim$ ), 162, 361.
+\<dimen>, 59, 61, 71, 118, +270, 271.
+\<dimen parameter>, 271, +274, 276.
+*|\dimen| registers, 118--122, +271, +276, @346--347, @349, @360, @363, @395.
+\<dimen variable>, +276.
+*|\dimendef|, 119, 215, +277, @346--347.
+\<dimendef token>, +271.
+dimensions, 57--61.
+\sub as arguments, 204, 362.
+Dionysius I of Syracuse, 239.
+diphthongs, \see |\ae|, |\oe|.
+direct sum, \see |\oplus|.
+discardable items, 95, 110--112, 124, @393.
+*|\discretionary|, 95--96, 283, 286, +287, +292.
+discretionary hyphens, 28, 95--96, 453, +455.
+discretionary multiplication signs, 173, +357.
+display math mode, 85--89, 289--293.
+display style, 140--142, 441--447.
+*|\displayindent|, 188, 190, 274, 291, 349.
+*|\displaylimits|, 144, 159, +292, 443.
+|\displaylines|, 194, @196, +362.
+displays, 87, 103, @139--145, @166--167, 185--197, @232, @241, 315.
+\sub at beginning of paragraph, 316.
+\sub non-centered, 186, 326, 375--376, 420--421.
+\sub positioning of, +188--+190.
+*|\displaystyle|, 141--142, +292, @362.
+*|\displaywidowpenalty|, 104, 272, @348.
+*|\displaywidth|, 188, 190, 274, 349.
+Disraeli [Beaconsfield], Benjamin, earl, 219.
+ditto mark, 53, 441.
+|\div| ( $\div$ ), 436.
+*|\divide|, @118--119, @218--219, +276, @391, @397, @398, @417.
+|\do|, 344, 380, 423.
+dollar sign, 4, 38, 51, 54, 86--88, @92, 127, @134--135, @185--186, 283,
+ +287, +293.
+Donnelley, Richard Robert, vii.
+|\dospecials|, +344, 380, 422--423.
+|\dosupereject|, 256, +364.
+|\dot| (math accent: $\dot x$), 135.
+dot accent (\.{}), \see |\.|, |\dot|.
+dot-under accent (\d{\hbox{ }}), \see |\d|.
+|\doteq| ( $\doteq$ ), +361, 436.
+|\dotfill| (\hbox to 4em{\dotfill}), 244, @334, @335, @340--341, +357.
+dotless letters, 52--53, 136, 435.
+|\dots| ( \dots\ ), 173, +356; \also ellipses.
+double-column format, @257, 386--388, @417.
+double dagger, \see |\ddag|, |\ddagger|.
+double dangerous-bend signs, vi, 419.
+double integrals, @169, @180.
+double quote mark, 52, 53, 134; \also hexadecimal.
+*|\doublehyphendemerits|, 98, 273, @348, @451.
+|\downarrow| ( $\downarrow$ ), @146--147, @182, +359, 437.
+|\Downarrow| ( $\Downarrow$ ), @146--147, +359, 437.
+|\downbracefill| (\hbox to 4em{\downbracefill}), 225--226, +357.
+Doyle, Sir Arthur Conan, 401.
+*|\dp|, 120, 271, @316, +388--+389, @417.
+dragon curve, 391.
+Drofnats, Revinu Jitis, 24, 27--28, 73--74, 404--406, 408, 410--412.
+Dudeney, Henry Ernest, 249, 334.
+Dull, Brutus Cyclops, 131, 173, 260.
+|\dummy|, 400.
+*|\dump|, +283, +286, 336, @344.
+Durant, William James, 239.
+|.dvi|, 23, 43, 60, 119, 213, 228, 254, 279, 280, 302, 367, 374.
+Dvo\v r\'ak, Anton\'\i n Leopold, 409.
+$dx$, @168.
+\newletter
+EBCDIC, 43.
+*|\edef|, @215--216, 275, @328, @373--374.
+editing, 34, 139, 197.
+efficiency, 329, 333, 342, 345, 347, 383, 384, 394, 399, 423.
+|\egroup| (implicit |}|), 269, +351, @363, 382, +385, @407, @421.
+Ehlert, Ernst Fred, 248.
+Ehlert Bohning, Pauline Anna Marie, 248.
+|\eightpoint|, +415, @416.
+|\eject| (force page break), @24--25, 105, 109, 189, +353, @418, @419.
+elbows, \see angle brackets.
+|\ell| ( $\ell$ ), 132, 435.
+ellipses ( $\cdots$ ), 73, 172--174, 176--177, 180--182, 245.
+Elphinstone, Mountstuart, 89.
+*|\else|, 207, 210, +213.
+|em|, 60, 154, 166, 214, +270, @352, @414, 433.
+em-dash ( --- ), 4, 302.
+em quad, \see |\quad|.
+embellished letters, \see accents.
+emergency stops, 299--300.
+*|\emergencystretch|, +107, 274.
+Emerson, Ralph Waldo, 41.
+emphatics, \see dot-under.
+\<empty>, +268.
+|\empty|, @263, +351, @378.
+empty discretionary, 95, 286.
+empty group, 19, 253, 305.
+empty line in input file, 24, 37, +47, @340--341, 381.
+\sub at end of file, 217.
+empty line in output, 114, 316.
+empty page, 114.
+|\emptyset| ( $\emptyset$ ), 128, 435.
+en-dash ( -- ), 4, 187, 252, 314.
+en quad, 71, \see |\enspace|.
+*|\end|, 23, 26, @27, 87, +264, +283, 286, 299, @336, @403.
+end of an input file, 206, 214, 217.
+end of file line, \see \<return>, |\cr|.
+end of file page, \see \<formfeed>.
+end of paragraph, 286, \see |\par|.
+|\endchapter|, 418--419.
+*|\endcsname|, 40--41, +213, 283, @348, @375.
+endgame, 87, 264, 283.
+|\endgraf|, @262, 286, @331, +351, @407, @416, @419.
+*|\endgroup|, 21, @249, @262, +279, @380, @407, @419.
+*|\endinput|, 47, +214.
+|\endinsert|, 115--116, +363.
+|\endletter|, @403--404, +407.
+|\endline|, +351.
+*|\endlinechar|, +48, 273, @331, +348, @390--391.
+|\endtemplate|, 206, +240.
+|\enskip|, 71, +352.
+|\enspace|, @202, +352, @419.
+enumerated cases, in formulas, 175.
+\sub in separate paragraphs, @102, @340.
+\sub within a paragraph, 92.
+enunciations, \see |\proclaim|.
+epigraphs, 418--419, 425.
+|\epsilon| ( $\epsilon$ ), 1, 128, 434.
+|\eqalign|, @190--191, 193, 242, 326, +362.
+|\eqalignno|, @192--193, 194, +362.
+\<eqno>, +293.
+*|\eqno|, @186--187, 189--191, 193, +293, 375--376.
+\<equals>, +275, 276--277.
+equals sign, 51, @133, 209, 226, 275, @376.
+equation numbers, 186--196.
+|\equiv| ( $\equiv$ ), @133, 436.
+Erd\H os, P\'al (= Paul), 53.
+*|\errhelp|, 275, +280, @347.
+*|\errmessage|, 216, +279--+280, @347, @418.
+error messages, 30--33, 295--301, 308--309.
+error recovery, 31--32, 46, 215, 295--303, 309.
+*|\errorcontextlines|, +34, 273, @348.
+*|\errorstopmode|, +32, +33, +277, 299.
+es-zet ( \ss\ ), \see |\ss|.
+\<escape> (ASCII code 27), 369.
+escape character, 7, 37, 421.
+*|\escapechar|, 40, 213, +228, 273, @308, +348, @377.
+|\eta| ( $\eta$ ), 434.
+|etc.|, 302.
+Eulerian numbers, 152.
+Evagoras of Salamis, 239.
+even-numbered pages, 252--253, @416.
+*|\everycr|, 275, @362.
+*|\everydisplay|, 179, 275, +287, @326.
+*|\everyhbox|, 275, 279.
+*|\everyjob|, 275.
+*|\everymath|, 179, 275, +287, +293, 326.
+*|\everypar|, @105, 215, 253, @262, 275, +282, +283, @333, @381, @407, @421.
+*|\everyvbox|, 275, 279.
+|ex|, 60, 154, +270, @356, 433.
+exception dictionary, 449, 452--453.
+exclamation point, 51, @72, 73, 75, 161, @169.
+|\exercise|, 10, +422.
+exercises, vii, 1--303.
+*|\exhyphenpenalty|, +96, @262, 272, @348.
+|\exists| ( $\exists$ ), 435.
+|\exp| ( $\exp$ ), 162, 361.
+*|\expandafter|, @40, +213, 215, @260, @308, @330, @348, @374--380.
+expansion of expandable tokens, 212--216, 238, 267, 373--374.
+\sub avoiding, 216, 262--263, 377.
+explicit hyphens, 4, 93, 454.
+explicit kerns, 40, 280, 306, 454--455.
+exponents, \see superscripts.
+extensible characters, 442.
+extension fonts, 157, @351, 432--433, 441, 447.
+extensions to \TeX, 226, 228--229.
+eyestrain, reducing, 59.
+\newletter
+faces, 13, 17, 390.
+\<factor>, 270.
+factorial, @169, @181.
+*|\fam|, 154--159, 273, +289--+290, 346--347, @351, 358, @414--415.
+families, 153--159, 289--290, 346, 431, 442.
+\<family assignment>, +277.
+\<family member>, +271, 277.
+family tree, 248.
+family 0 (math roman fonts), 153--157, @351.
+family 1 (math italic fonts), 155--157, @351.
+family 2 (math symbol fonts), 157, @351, 431, 441, 447.
+family 3 (math extension fonts), 157, @351, 432--433, 441, 447.
+{\tt Fatal format file error}, 299.
+feasible breakpoints, 99, 315.
+fences, \see openings, closings, delimiters.
+ffl, \see ligatures.
+*|\fi|, 207, 210, +213.
+Fibonacci, Leonardo, of Pisa, 166.
+fields of atoms, 158--159, 289--291.
+|fil|, 72, 118--119, +271, @348, @394.
+\<fil dimen>, +271.
+\<fil unit>, +271.
+|\filbreak|, 111, +353.
+\<file name>, 214, 216, 226, 277, +278.
+file names, 25, 214, 216--217, 226, 278.
+file pages, 343.
+file types, \see |.dvi|, |.fmt|, |.tfm|, |.tex|, log file, terminal.
+|fill|, 72, 118--119, +271, @347.
+fill page with blank space, \see |\vfill|.
+\<filler>, +276, 278, 280--282, 289.
+|filll|, 72, 118, +271, @332.
+*|\finalhyphendemerits|, 98, @106, 273, @348, @451.
+*|\firstmark|, 213, +258, 259--260, 280.
+|\fiverm|, 153, +350, @351, @414--415.
+|\flat| ( $\flat$ ), @409, 435.
+floating insertions, 115--116, 125, @363.
+*|\floatingpenalty|, +123--+124, 272, 281, @363.
+floor brackets ( $\lfloor\,\rfloor$ ), 146--147, \see |\lfloor|, |\rfloor|.
+flush left, 72, 142, 177, 181, 196.
+flush right, 71--72, 106, 142, 177, 196, 233, 419.
+|.fmt|, 39, 344.
+|\fmtname| and |\fmtversion|, 364.
+|\folio| (typeset page number), 252--253, +362, @406, @416.
+\<font>, +213, 214, 271, 277.
+*|\font|, 16--17, 60, 210, +213, 214--215, 271, +276.
+\<font assignment>, +277.
+font metric files, 46, 433.
+\<font range>, +271.
+\<fontdef token>, +271.
+*|\fontdimen|, 76, 157, @179, 214, +271, +277, @355--356, @375, @390, +433,
+ 441, 447.
+*|\fontname|, 213, 214.
+fonts, 13--17; \also Computer Modern fonts.
+|\footins|, 256, +363, 396--399, @416.
+|\footline|, 252, 256, @340--341, +362.
+|\footnote|, 82, 116, 251, 256, 340, +363, 382, 416.
+|\footnoterule|, 256, +364.
+footnotes, 105, @116--117, 121, 125, @173, 416--417.
+\sub short, 395--399.
+|\forall| ( $\forall$ ), 435.
+forbidden control sequence, 206.
+Ford, Patrick Kildea, 293.
+foreign languages, 45--46, 52--54, 370, 387, 449, 451.
+form letters, 200, 207--209.
+format file, 25--26, 39, 283, 344.
+format-independent documents, 194, 203.
+formats, 11, 39, 200, 403--425, 434.
+\<formfeed> (ASCII code 12), 343, 369.
+formulas, 127--199.
+Foss, Sam Walter, 439.
+fractions, 67, 139--143, 152, 170, 179, 186, 332, 444--445.
+\sub huge, 196.
+\sub slashed form, 67, 139--140, 233, 236.
+Fraktur, 164, 434.
+Franklin, Benjamin, 65.
+French, 54, 340--341, 455.
+|\frenchspacing|, 74, @340, +351, 381, 400.
+Frescobaldi, Girolamo, 239.
+front matter, 413.
+|\frown| ( $\frown$ ), 436.
+full stop, \see period.
+|\fullhsize|, 257.
+|\fullline|, 257.
+funny space, 377.
+*|\futurelet|, +207, 215, @262, 277, @363, @375--377, @423.
+|\futurenonspacelet|, 376.
+\newletter
+Galilei, Galileo, 101.
+galley file, 303, 425.
+Gamble, James, 38.
+|\gamma| ( $\gamma$ ), @127, 434.
+|\Gamma| ( $\Gamma$ ), @127, @169, +358, 434.
+|\gcd| ( $\gcd$ ), 162, @192, 361.
+*|\gdef|, +206, 215, 275, @352, @407.
+|\ge| ( $\ge$ ), 9, 45, @175, 318, +361, 438.
+\<general text>, +276, 277, 279, 280, 287, 292.
+generalized fraction, 152, 157, +292, +444--+445.
+generic coding, 194, 203.
+generic matrix, 177, 245.
+|\geq| ( $\geq$ ), 318, 436.
+German, 52, 96, 451.
+German black letters, \see Fraktur.
+|\getfactor|, @356, +375, @398.
+|\gets| ( $\gets$ ), +361, 438.
+|\gg| ( $\gg$ ), 436.
+Gibbon, Edward, 117.
+*|\global|, @21, 119, 206, @218, 232, 256, +275, 301, 307, 320, 346.
+\<global assignment>, 179, +277.
+*|\globaldefs|, 238, 273, +275.
+glue, 63, 69--83, 95, 110, 157, 222--225, 302, 412.
+\sub above and below displays, 189--190, 194.
+\sub at top of page, 113--114, 124, 256.
+\sub between aligned columns, 237--239, 247, 392.
+\sub between lines, \see interline glue.
+\sub between paragraphs, 79, 104--105, 262, 282, 342, 406, 417.
+\sub between words, 74--76, 356, 393--394, 433.
+\<glue>, 71, 118, +271.
+|\glue|, 75, 79, 302.
+\<glue parameter>, 271, +274, 276.
+|glue set|, 79, 302.
+glue set order, 77, 79, 81, 97.
+glue set ratio, 77, 79, 81, 97.
+\<glue variable>, +276.
+goal height, 112--114, 123--125.
+Goethe, Johann Wolfgang von, 183.
+|\goodbreak|, 111, 116, +353.
+Grandmaster, 253.
+|\grave| (math accent: $\grave x$), 135.
+grave accent (\`{}), \see |\`|, |\grave|.
+greater than or equal, \see |\ge|.
+greater than sign, 52, 53, @133, 150, 209.
+Greek, 127--128, 137, 156, 164, 319, 358, 430, 434.
+Green, Walter, 244.
+grouping, 13--14, 19--21, 119--120, 122, 200, 201, 241, 259, +279, 283,
+ 286, 291, 301, 375, 378, 385, 453.
+\sub characters for, 39--40, @381--382.
+\sub implicit, 115, 148, 194, 253, +287, +292, +293.
+guide words, 259.
+\newletter
+|\H| (long Hungarian umlaut), 52, @53, +356, @420.
+H\&J, \see hyphenation, line breaking, setting glue.
+h\'a\v{c}ek accent (\v{}), \see |\v|, |\check|.
+halftones, 228.
+*|\halign|, 117, 190, 193, 194, 235--249, +282, 286, +291, 302, @326, 352,
+ @361--362, @386, @392.
+\sub compared to tabbing, 235.
+Halmos, Paul Richard, 183.
+Hamza, \see apostrophe.
+|\hang|, +355, @419.
+*|\hangafter|, +102, 103--104, 273, 348--349, @419.
+*|\hangindent|, +102, @262, 274, 349, @407.
+hanging indentation, 79, 102--103.
+hanging punctuation, 394--395.
+Hardy, Godfrey Harold, 321.
+harpoons, 437.
+hash mark, 38, 51, 113, @200--202, @+203, @204--205, 228, +235, @236--240.
+Hassler, Hans Leo, 239.
+hat, 38, 51, @128--130, 134, 369, @423.
+|\hat| (math accent: $\hat x$), @135--136, 164.
+hat accent (\^{}), \see |\^|, |\hat|, |\widehat|.
+*|\hbadness|, 29, 272, +302, @348, @387--388, 400.
+|\hbar| ( $\hbar$ ), @169, +358, 435.
+hbox (box with horizontal list inside), 64.
+*|\hbox|, 64--67, 77, 86, 93, 151, 159, 163, 175, 179, 185--186, 221, 222,
+ 278, 282, 388--389.
+|\headline|, 252--253, 255, +362, @406.
+|\heartsuit| ( $\heartsuit$ ), 435.
+Hebrew, 66.
+|height|, 221, 224, +282, 337.
+height of a box, 63--67, 77, 80--82, 225.
+help messages, 32, 280, 295--296.
+\<hex digit>, +269.
+\<hexadecimal constant>, +269.
+hexadecimal notation, +43, @44, 45, 47--48, @154, 219, 336, 420.
+*|\hfil|, 71--72, 194, 235--237, 283, 285, 290, 397.
+*|\hfill|, 71--72, 142, 177, 194, 233, 283, 285, 290.
+*|\hfilneg|, 72, 100, 283, 285, 290, 397.
+*|\hfuzz|, 30, 274, +302, @348, @387--388.
+|\hglue|, +352.
+|\hideskip|, +347, 348, 354.
+|\hidewidth|, 243, 245, 247, 325, +354.
+higher-level languages for composition, 203.
+Highton, Albert Henry, 481.
+Hilbert, David, 167.
+*|\hoffset|, +251, 274, @342.
+*|\holdinginserts|, +125, 273, 400.
+Holmes, Thomas Sherlock Scott, 401.
+|\hom| ( $\hom$ ), 162, 361.
+Hombert, Humbert, 401.
+Honeywell Information Systems, 1.
+|\hookleftarrow| ( $\hookleftarrow$ ), 358, 437.
+|\hookrightarrow| ( $\hookrightarrow$ ), 358, 430, 437.
+hooks, \see |\subset|, |\supset|.
+horizontal braces, @176, 225--226, @339.
+\<horizontal command>, +283.
+horizontal lists, 64, +94--+95.
+horizontal mode, 85--89, 105, +285--+287.
+\<horizontal mode material>, 278.
+\<horizontal rule>, 281.
+horizontal rules, 24, 64, 221--226, 246, +282.
+\<horizontal skip>, 285, 290.
+Hornschuch, Hieronymus, 483.
+Howard, Jane Temple, 21.
+|\hphantom|, 178, 211, +360.
+*|\hrule|, @24, 64, 85, 221--225, @246, +281--+282, 286, @357, @420, @421.
+|\hrulefill| (\hbox to4em{\hrulefill}), @244, @252, +357, @412.
+*|\hsize|, @26--27, @60, 102, 188, 237, 251, @257, 274, @340--341, @348,
+ @387, @406, @407, 413, 415, @417.
+*|\hskip|, 71, 86, 168, 283, +285, 290, @314.
+*|\hss|, 71--72, @82--83, 233, 283, 285, 290, 442.
+*|\ht|, 120, 271, +388--+389, 417.
+Hungarian umlaut (\H{}), \see |\H|.
+hyphen, 4, 51, 93, 95, 132, 292.
+|hyphen.tex|, 364, +453.
+hyphenation, 28, 39, 96, 306, 314, 394--395, 414, 424, +449--+455.
+\sub suppressing, 93, 414, 424, 454.
+*|\hyphenation|, 277, @419, +452--+453, 455.
+\<hyphenation assignment>, +277.
+*|\hyphenchar|, 95, 214, +271, 273, +277, 286, 351, 395, @414, 454, 455.
+*|\hyphenpenalty|, 96, 101, 272, @348, @451.
+\newletter
+|\i| ( \i\ ), @52--53, +356.
+|I can't go on|, 299.
+|\ialign|, +354.
+IBM Corporation, vii.
+identifiers in programs, 38, 165, @234.
+*|\if|, +209, 210--211, 307, 377, @379.
+*|\ifcase|, +210, @349, @373, @390, @406.
+*|\ifcat|, +209, 210, 307, @377.
+*|\ifdim|, +209, @353, @387, @417.
+*|\ifeof|, +210, 217.
+|\iff| ( $\iff$ ), 163, +361, 438.
+*|\iffalse|, +210, 211, @260--261, @348, @385--386.
+*|\ifhbox|, +210, @392, @399.
+*|\ifhmode|, +209, @363.
+*|\ifinner|, +209.
+*|\ifmmode|, +209, @215, @240, @353, @356, @360, @423.
+*|\ifnum|, @208, +209, @218--219.
+*|\ifodd|, @207, +209, @416, @418--419.
+*|\iftrue|, +210, 211, @260--261, @348.
+|\ifundefined|, 40.
+*|\ifvbox|, +210.
+*|\ifvmode|, +209.
+*|\ifvoid|, +210, @256.
+*|\ifx|, +210, 215, 307, @375--377, +384, @418.
+ignored characters, 37, 390.
+*|\ignorespaces|, +279, @333, @355, @424.
+|! Illegal unit|, 295.
+illustrations, 115--116.
+\sub fitting copy around, 101.
+|\Im| ( $\Im$ ), 435.
+|\imath| ( $\imath$ ), @136, 435.
+*|\immediate|, 226--228, +280, @422, @423.
+implicit braces, +269, \see |\bgroup|, |\egroup|.
+implicit characters, 269, 277, 309, 351, 376--377.
+implicit kerns, 306, +454, 455.
+|in| (inch), @24, +57, 270.
+|\in| ( $\in$ ), @128, @147, 436.
+|\inaccessible|, 297.
+inch, 57--58.
+incomplete conditionals, 206.
+*|\indent|, 86, 94, 101, 263, +282, +286, +291, @355.
+indentation or indention, 86, 222, 282.
+\sub hanging, 79, 102--103.
+indexes, 261--263, 392--394, 423--425, 481.
+indices, \see subscripts.
+|\inf| ( $\inf$ ), 162, 361.
+inferiors, \see subscripts.
+infinite badness, 97, 107, 111, 229, 317.
+infinite glue, 71--72, 118--119, 256, 313, 332.
+infinite loop, 299, 301, 383.
+infinite penalty, 97, 111, 254--256, 264, 286, 400.
+|\infty| ( $\infty$ ), 9, 318, 435.
+inhibiting expansion, 216, 262--263, 377.
+|INITEX|, 39, 41, 76, 157, 283, 336, 343--345, 453.
+Inner atom, 158, 170, 289, 443, 445--446.
+*|\input|, 7, 9, @25--27, 47, 199, +214, 217, @380, 382--383, @403, @422.
+input/output commands, \see |\input|, |\read|, |\write|, |\message|, |\dump|.
+input/output streams, 346, \see |\openin|, |\openout|.
+input stack, 300, 374.
+*|\inputlineno|, 214, 271.
+*|\insert|, 95, +122--+125, 259, 280--281, @363, 416, 424, 454.
+inserting text online, 31.
+insertions, 110, 115--117, 122--125, 256, 335.
+*|\insertpenalties|, 111, 114, +123--+125, 214, 254, @256, 271.
+|\int| (large $\smallint$), 144, @168--169, @192, 358, 435.
+\<integer constant>, +269--+270.
+\<integer parameter>, 271, +272--+273, 276.
+\<integer variable>, +276.
+integral signs, \see |\int|, |\smallint|.
+\sub multiple, @169, @180.
+inter-column spacing, 237--239, 247, 392.
+interacting with \TeX, 31--34, 217--218, 228, 295--299.
+\<interaction mode assignment>, +277.
+|\interdisplaylinepenalty|, @193, 349, 362.
+|\interfootnotelinepenalty|, 349, 363.
+interline glue, 78--79, +80, 104, 105, 125, 221, 245, 263, 281--282, 335, 352, 399, 409.
+*|\interlinepenalty|, +104, 272, @363, @406, @419.
+internal box-and-glue representation, 66, 75, 79, 158--159, 302, 455.
+internal character codes, 43--46, 367--370.
+\<internal dimen>, +271.
+\<internal glue>, +271.
+\<internal integer>, +271.
+\<internal muglue>, +271.
+\<internal quantity>, +279.
+Internal Revenue Service, 200, 208--209, 244, 404.
+\<internal unit>, +270.
+internal vertical mode, 85, 87--89, 222, 278--283.
+interrupts, 33, 299, 383.
+interword spacing, +74--+76, @356, @393--394, 433.
+|Interwoven alignment preambles|, 299.
+\<intimate assignment>, +277.
+invalid characters, 37, 45.
+|\iota| ( $\iota$ ), @325, 434.
+Isocrates, 239.
+|\it| (use {\it italic\/} type), 13--14, 165, 231--232, @332, +351, @409,
+ @414--415, @419, 428.
+italic corrections, @14, @64, +287, @306, 441, 455.
+italic letters with descenders, 319.
+italic type, 13--14, 100, 127, 165, 409, 428, 430.
+|\item|, @102--103, 117, @340--342, +355, @416, 419.
+|\itemitem|, @102, 342, +355.
+iteration, \see |\loop|.
+|\itfam|, +351, @414--415.
+\t Iur'ev, Serge\u\i\ Petrovich, 53.
+\newletter
+|\j| ( \j\ ), 52, +356.
+Jevons, William Stanley, 5.
+|\jmath| ( $\jmath$ ), @136, 435.
+*|\jobname|, +213, 214, @336.
+Johnson, Samuel, 89.
+|\joinrel|, +358.
+jokes, vi, 303, 449.
+|\jot|, @194, @242, +349, @362.
+Joyce, James Augustine, 100.
+justification, \see setting glue, line breaking.
+Justus, Paul E\period, 455.
+\newletter
+|\kappa| ( $\kappa$ ), @128, 434.
+Kastrup, David Friedrich, 399.
+|\ker| ( $\ker$ ), 162, 361.
+*|\kern|, 10, 40, @66, 75, 87, 168, @256, @263, +280, @306, @389, @394--395,
+ @416, @424, 454--455.
+Kernighan, Brian Wilson, 159.
+kerns, 4, 66, 75, 95--97, 110, 157, 168, 280, 286, 306, 444, 454--455.
+Key Index, 392--394.
+keyboards, 3, 5, 43--46, 368--370.
+keywords, +61, 71, +268, 337, 370.
+Knuth, Donald Ervin, i, ii, vii, 74, 92, 211, 259, 337, 340--341, 412--413,
+ 424--425.
+Knuth, Nancy Jill Carter, iii, 125, 418.
+K\"ochel, Ludwig, Ritter von, 409.
+\newletter
+|l| after |fil|, +271, 337.
+{\it\$}, \see pound sterling.
+|\l| ( \l\ ), @52--53, +356.
+|\L| ( \L\ ), @52--53, +356.
+|\lambda| ( $\lambda$ ), @176, 434.
+|\Lambda| ( $\Lambda$ ), @323, 434.
+Lamport, Leslie B\period, 137.
+|\land| ( $\land$ ), 133, +361, 438.
+|\langle| ( $\langle$ ), @146--147, 150, 156, +359, 437.
+*|\language| (hyphenation method), 273, 346, +455.
+large delimiters, 145--150, 442.
+large operators, @144--145, 154--155, 358, +435; \also Op atom.
+*|\lastbox|, 222, +278, @354, @392, @398, @399.
+*|\lastkern|, 214, 271.
+*|\lastpenalty|, 214, 271.
+*|\lastskip|, 214, @223, 271, 272, @392.
+\LaTeX, 137.
+|\lbrace| ( $\lbrace$ ), @146--147, +359, 437.
+|\lbrack| ( $\lbrack$ ), @146--147, +351, 370, 437.
+*|\lccode|, 41, 214, 271, 345, 452--454.
+|\lceil| ( $\lceil$ ), @146--147, +359, 437.
+|\ldotp|, 358, +359, 438.
+|\ldots| ( $\ldots$ ), @73, @172--174, 177, @180--181, @199--201, +359, 438.
+|\le| ( $\le$ ), 9, 45, @133, @162, 318, +361, 438.
+leaders, 222, 223--226, 228, 280--282, 285, 290, @357, @392--394.
+\<leaders>, +281.
+*|\leaders|, 95, 110, 223, +224, 225, @357, @392--394.
+leading, \see |\baselineskip|, |\vskip|.
+|\leavevmode|, 313, @333, @+356, @408, @420.
+Lee, Marshall, 17.
+*|\left|, @148--150, 155--157, @171, 196, +292, 437.
+left brace, @13--14, @19--21, 38, 51, @200--202, +203--+204, @205--206,
+ 216, +269, 275--276, +283, +286, +291, @330.
+\<left brace>, +275.
+left bracket, 51, 134, @146--148, 171, @408, @437.
+left delimiters, \see openings.
+left-hand pages, 252--253, @416.
+left parenthesis, 51, 134, @140, @145--150, 345.
+left quote, 3--5, 132, 134, @305, 394--395; \also alphabetic constant.
+|\leftarrow| ( $\leftarrow$ ), 226, 437.
+|\Leftarrow| ( $\Leftarrow$ ), 226, 437.
+|\leftarrowfill| (\hbox to4em{\leftarrowfill}), +357.
+|\leftharpoondown| ( $\leftharpoondown$ ), 437.
+|\leftharpoonup| ( $\leftharpoonup$ ), 437.
+*|\lefthyphenmin|, 273, @364, +454, 455.
+|\leftline|, 101, @257, @259--260, @326, +353.
+|\leftrightarrow| ( $\leftrightarrow$ ), 437.
+|\Leftrightarrow| ( $\Leftrightarrow$ ), 437.
+*|\leftskip|, 100, 274, @317, @407, @419.
+Legendre symbol, 152.
+Leontief, Wassily Wassily, 265.
+|\leq| ( $\leq$ ), 318, +358, 436.
+|\leqalignno|, 192, 194, +362.
+*|\leqno|, @187, 189, +293, 375--376.
+Lesk, Michael Edward, 247.
+less than or equal, \see |\le|.
+less than sign, 52, 53, @133, 150, 209.
+*|\let|, 206--207, 215, +277, 307, 309, @352, @376.
+\<let assignment>, +277.
+|letterformat.tex|, 403, +406--+408.
+letterheads, 407.
+letters, 7--8, +37, 41, 45--46, 51, 132, 157, 344, 370.
+|\lfloor| ( $\lfloor$ ), @146--147, +359, 437.
+|\lg| ( $\lg$ ), 162, 361.
+|\lgroup|, 150, @176, +359, 437.
+|\lhook|, +358.
+Liang, Franklin Mark, 449.
+library of macros, 199, 382--384, 425.
+lies, vii, 303.
+ligatures, 4, 19, 46, 51, 54, 75, 95, 165, 286, 302, 381, 427, 444, 454.
+|\lim| ( $\lim$ ), 162, @163, 361.
+|\liminf| ( $\liminf$ ), 162, @163, @178, +361.
+*|\limits|, 144, 159, +292, @359, 443.
+limits above and below operators, 144--145, 149, 179.
+|\limsup| ( $\limsup$ ), 162, @163, @178, +361.
+Lincoln, Abraham, 11.
+|\line|, @72, 77, 101, 224, 232, 252, @255--257, +353, @412.
+line breaking, 97--100, 173--174, 392--395, 398--400.
+line breaks, avoiding bad, 27--30, 91--94, 173--174, 197.
+\sub forcing good, 94, 114.
+line rules, \see |\hrule|, |\vrule|.
+\<linefeed> (ASCII code 10), 369.
+*|\linepenalty|, +98, 272, 314, @316, @348.
+lines of input, @24, 46--48, @340.
+*|\lineskip|, 78--80, 104, 194, 274, 281, 349, @351--352.
+*|\lineskiplimit|, 78--80, 104, 194, 274, 281, 349, @351--352, @362.
+list macros, 378--380.
+|\listing|, 380, 391.
+|\ll| ( $\ll$ ), 431, 436.
+Llanfair P\period\ G\period, 451.
+|\llap|, 82--83, @189, @340--341, +353, @355, @381, @416--417, @422.
+|\lmoustache|, 150, +359, 437.
+|\ln| ( $\ln$ ), 162, @186, 361.
+|\lnot| ( $\lnot$ ), +361, 438.
+local, 19--21, \see grouping.
+Locke, John, 303.
+|\log| ( $\log$ ), 162, @169--170, 361.
+log file, 10, 28, 66, 226, 303, 347.
+|\loggingall|, +364.
+logical operators, \see |\land|, |\lor|, |\lnot|.
+logo, 1, 8, 412, \see |\TeX|.
+*|\long|, 205--206, 210, 275, 331, @375, @378, @382.
+long formulas, 195--197.
+|\longindentation|, 407.
+|\longleftarrow| ( $\longleftarrow$ ), +358, 437.
+|\Longleftarrow| ( $\Longleftarrow$ ), +358, 437.
+|\longleftrightarrow| ( $\longleftrightarrow$ ), +358, 437.
+|\Longleftrightarrow| ( $\Longleftrightarrow$ ), +358, 437.
+|\longmapsto| ( $\longmapsto$ ), +358, 437.
+|\longrightarrow| ( $\longrightarrow$ ), @325, +358, 437.
+|\Longrightarrow| ( $\Longrightarrow$ ), +358, 437.
+looking ahead, 207, 376--377.
+|\loop|, @217--219, +352, @373--374, @387, @417.
+loose lines, 97, 99, 302.
+*|\looseness|, 103--104, 109, 273, @342, 349.
+|\lor| ( $\lor$ ), 133, +361, 438.
+low-resolution printer, 59.
+*|\lower|, @66, 80, 151, 179, +285, +290.
+*|\lowercase|, 41, 215, +279, 307, 345.
+lowercase letters, 9, 268, 370, 453.
+|\lq| ( \lq\ ), 5, 48, +351, 370, @395.
+lxix, 420.
+\newletter
+|\m@ne|, +347.
+|\m@th|, +353.
+machine-independence, 58; \also |.dvi|.
+Macro, N\ae vius Sertorius, 219.
+macro arguments and parameters, 33, 200--205, 249, @263, @362, @363, @375--380,
+ @407.
+\<macro assignment>, +275.
+macro conventions, 121, 346, 364.
+macron accent (\={}), \see |\=|, |\bar|.
+macros, 199--219, 373--400.
+\sub to save typing in math, 136, 199--200, @324.
+*|\mag|, +60, 270, 273, 348.
+|\magnification|, 17, 59--60, +364, @403--404.
+magnified output, 16--17, 59--60, 403, 433.
+|\magstep|, 17, 59--60, +349, @403--404.
+|\magstephalf|, 17, +349, 403.
+mail, 403--408.
+main vertical list, 85, 110, +112, 125, 253--254, 281.
+|\maintoks|, 262.
+|\makefootline|, 255--257, +364.
+|\makeheadline|, 255, 257, +364.
+|\makelabel|, 403, @404--405, +408.
+|manfnt|, 44, 408, 414.
+|manmac.tex|, 413--425.
+|\manual|, 44, +414, @419--420.
+|\mapsto| ( $\mapsto$ ), 128, +358, 437.
+|\mapstochar| ( $\mapstochar$ ), 358.
+|\margin|, 415, 424.
+marginal hacks, 82, 105, 400, 415, 424.
+margins, \see |\hoffset|, |\hsize|, |\narrower|.
+*|\mark|, 95, 157, 216, 258--263, +280, 417, 454.
+marks, 95, 110, 157, 213, +258--+263.
+markup commands, \see control sequences.
+math accents, @135--137, 141, 164--165, +359, 435, +443.
+\<math atom>, +291.
+\<math character>, +289.
+math character codes, 154--157.
+\<math field>, +289.
+math fonts, 157, @351, 430--433, 441, 447.
+math formulas, how to type, 127--197.
+math italic, 164--165, 409, 430, 433.
+math lists, 157--159, 441--446.
+math mode, 85--89, 127, 157, +289--+293.
+math spacing table, 170--171.
+\<math symbol>, +289.
+math symbols, 127--128.
+\sub construction of, 151, 178, @358--361.
+\sub table of, 434--438.
+math-off, 95--97, 287, 447.
+math-on, 95, 97, 287, 447.
+*|\mathaccent|, 157, 170, +291, @359, +443.
+*|\mathbin|, 155, 291, @361.
+*|\mathchar|, 155, 289.
+*|\mathchardef|, 155, 199, 214, 215, 271, 277, 289, 336, @358, @394.
+\<mathchardef token>, +271, 289.
+*|\mathchoice|, 151, 157, 292.
+*|\mathclose|, 155, 291, @322, @359.
+*|\mathcode|, 134, +154--+155, 214, 271, 289, 319, 326, @344.
+mathematical expressions, 127--197.
+Mathematical Reviews, 106, 392--394.
+mathematical style, 166--167, 182--183.
+|\mathhexbox|, +356.
+*|\mathinner|, 155, 171, 199, 291, @359.
+*|\mathop|, 155, 178, 291, @324--325, @361.
+*|\mathopen|, 155, 291, @322, @359.
+*|\mathord|, 88--89, 155, 291.
+|\mathpalette|, 151, @+360.
+*|\mathpunct|, 155, 291.
+*|\mathrel|, 155, 291, @359--361.
+|\mathstrut|, 131, 178, +360.
+*|\mathsurround|, 97, @162, 274, 305, 314, 323, @353, 447.
+matrices, 176--178, 181; \also alignments.
+|\matrix|, @176--178, 182, @325, +361.
+|\max| ( $\max$ ), 162--163, @170--171, 361.
+*|\maxdeadcycles|, +255, 273, @348.
+*|\maxdepth|, @112--114, 123--125, @255, @262--263, 274, @348, 400, @415.
+|\maxdimen|, 58, 188, @262--263, +347, 348.
+maximum legal dimension, 58.
+maximum legal integer, 118.
+*|\meaning|, +213--+215, 336, @382.
+measure, \see |\hsize|.
+|\medbreak|, 111, 113, +353, @355, @419, @422.
+*|\medmuskip| (medium math space), 167--168, 274, @349, 446.
+|\medskip| (medium extra vertical space), 70, 79, 102, 109, 111, +352,
+ @410--412.
+|\medskipamount|, +349, @352--353, @355, @407.
+|\medtype|, 408--411.
+membership, \see |\in|, |\ni|, |\notin|.
+memory space, 100, 300--301, 342, 345, 347, 383, 384, 394, 399.
+*|\message|, 216, @217--218, 227--228, +279, @308, 328, @343--344, @355, @418.
+{\manual opqrstuq}, 420, 427, 442, 483. % METAFONT (eight-point type)
+metric units, 57--60.
+|\mid| ( $\mid$ ), @174, 436.
+|\midinsert|, 116, @340--341, +363.
+migration, 105, 117, 259, 280--281, 282, 286, 287.
+|\min| ( $\min$ ), 162--163, @170--171, 361.
+|minus|, 71, 271, 337.
+minus sign, 4, 51, @127, 132, 226, 268.
+|! Missing| something, 296--297, 337.
+|\mit| (math italic family), 164, +351, 430, @434.
+*|\mkern|, 168, +280, +442.
+|mm| (millimeter), +57, 270.
+mod, @164, 322, +361.
+|\models| ( $\models$ ), +358, 436.
+modes, 46, 85--89, 175, 267--293.
+|modes.tex|, 88--89.
+money, 54, 140, @208--209.
+*|\month|, 273, 349, @406.
+Morris, William, 107.
+moustaches, 150.
+mouth, \see anatomy of \TeX.
+*|\moveleft|, 80--81, +282, 287.
+*|\moveright|, 80--81, @221, +282.
+Moxon, Joseph, 287.
+Mozart, Johann Chrysostom Wolfgang\rlap{ Gott-}\break % kludge to save a line
+ lieb (= Theophilus = Amadeus), 409.
+|\mp| ( $\mp$ ), @133, 436.
+*|\mskip|, 168, +290, 442.
+|mu| (math unit), 168, 270, 442.
+|\mu| ( $\mu$ ), @162, 434.
+\<mu unit>, +270.
+\<mudimen>, +270.
+\<muglue>, 118, 167--168, +271.
+\<muglue parameter>, 271, +274, 276.
+\<muglue variable>, +276.
+Muir, Cockburn, 283.
+Muirhead, James, 21.
+multicolumn format, 257, 396--397, 417.
+multiple integrals, @169, @180.
+*|\multiply|, @118--119, @218, +276, 349, @391, @398.
+|\multispan|, 243, 246--247, @334, +354.
+Munster, 55.
+\<mushrink>, +271.
+music, 408--412.
+*|\muskip| registers, 118, 168, +271, +276.
+*|\muskipdef|, 119, 215, +277.
+\<muskipdef token>, +271.
+\<mustretch>, +271.
+\newletter
+|\nabla| ( $\nabla$ ), 435.
+names, 73, 92.
+|\narrower|, 100, @340--341, +355.
+National Science Foundation, vii.
+|\natural| ( $\natural$ ), 435.
+natural width, 69.
+Naur, Peter, 268.
+|\ne| ( $\ne$ ), 9, 45, @133, 318, +361, 438.
+|\nearrow| ( $\nearrow$ ), 437.
+|\neg| ( $\neg$ ), 435.
+negated relations, 436--437.
+negative dimensions, 66, 222.
+|\negthinspace|, 332, +352.
+|\neq| ( $\neq$ ), 318, +358, 438.
+nesting (i\period e\period, groups inside groups), 20--21, 210, 385.
+new symbols, 151, 178, @358--361.
+|\newbox|, 121, 346, +347, @353, @394, @417.
+|\newcount|, 121, @218, 346, +347, @349, @418.
+|\newdimen|, 121, 346, +347, @349, @415.
+|\newfam|, 121, 157, 346, +347, @351.
+|\newhelp|, 346, +347.
+|\newif|, 211, @218, +348, @354, @375, @416, @423.
+|\newinsert|, 121, 122, 346, +347, @363, @415.
+|\newlanguage|, 346, +347.
+*|\newlinechar|, +228, 273, @348.
+|\newmuskip|, 121, 346, +347.
+|\newread|, 121, 216, 346, +347.
+|\newskip|, 121, 346, +347, @349, @394, @414.
+|\newtoks|, 121, 212, @262, 346, +347, @400.
+|\newwrite|, 121, 227, 346, +347, @422--423.
+|\next|, +352.
+|\ni| ( $\ni$ ), 436.
+|\ninepoint|, 15, +414, @419.
+|\ninerm|, 15, 413.
+|\ninesl|, 15, 413.
+|No room|, 347.
+*|\noalign|, 176, 191, @193, +237, @246, 249, +282, +285, 286.
+*|\noboundary|, 283, +286, 290.
+|\nobreak| (inhibit line or page break), 97, 109, @174, @193, 335,
+ +353, @394, @407.
+*|\noexpand|, +209, +213, 215, 216, @377, @424.
+*|\noindent|, 86, 188, @262--263, +283, 286, 291, @340--341, @355, @419.
+|\nointerlineskip|, 79--80, @255, 331, +352, @389.
+*|\nolimits|, 144, 159, +292, @358, @361.
+\<non-macro assignment>, +275.
+nonaligned leaders, 224--226.
+|\nonfrenchspacing|, 74, +351.
+*|\nonscript|, 179, +290, 442, 446.
+*|\nonstopmode|, +32, +277, 299.
+|\nopagenumbers|, 251--252, +362, @406, @409.
+norm symbol, \see absolute value, vertical line.
+\<normal dimen>, +270.
+\<normal integer>, +269.
+\<normal mudimen>, +270.
+|\normalbaselines|, @325, 349, +351, @414--415.
+|\normalbaselineskip|, +349, @414--415.
+|\normalbottom|, +363.
+|\normallineskip|, +349, @351.
+|\normallineskiplimit|, +349, @351, @362.
+Norwegian characters, 45--46, 52--53, 370.
+|\not| ( $\not$ ), @133, +358, @436--437.
+|\notin| ( $\notin$ ), +360, 437.
+$n$th, 323.
+|\nu| ( $\nu$ ), @128, @163, 434.
+nucleus, 158--159, 289--292, 441--446.
+|\null|, 311, @332, +351.
+\<null> (ASCII code 0), 37, 39, 48, 343, 369.
+null control sequence, 46, 308.
+null delimiter, 149--150, 152, 156, 345, @360, @362.
+null set, \see |\emptyset|.
+*|\nulldelimiterspace|, 150, 274, @348, +442.
+*|\nullfont|, 14, 153, 271, 433.
+\<number>, 44, 118, +269--+270, 272, 309, 349.
+*|\number|, 40--41, +213, 214, @252, @406, @424.
+number sign, \see hash mark.
+numbered footnotes, 121, 125.
+numerals, \see digits, roman numerals.
+numerator, 141, 152, 179, 444--445.
+\<numeric variable>, +276.
+numerical tables, 240--241.
+|\nwarrow| ( $\nwarrow$ ), 437.
+\newletter
+|O| versus |0|, 132.
+|\o| ( \o\ ), +356.
+|\O| ( \O\ ), +356.
+|\oalign|, +356.
+obelisk {\sl or\/} obelus, 53.
+|\obeylines|, @94, @249, @262, 342, +352, @380--382, @407, @419.
+|\obeyspaces|, 254, 308, 342, +352, @380--381, @394, @421.
+oblique, \see slanted.
+\<octal constant>, +269.
+\<octal digit>, +269.
+octal notation, +43, @44, 155, 420.
+odd-numbered pages, 252--253, @416.
+|\odot| ( $\odot$ ), 436.
+|\oe| ( \oe\ ), 52, +356.
+|\OE| ( \OE\ ), 52, @53, +356.
+Office of Naval Research, vii.
+|\offinterlineskip|, @245--247, 312, +352, @416.
+|\oint| (large \ooalign{$\smallint$\cr\hidewidth$\circ$\hidewidth}), +358, 435.
+|\oldstyle|, +351.
+oldstyle numerals, 430, 435.
+|\omega| ( $\omega$ ), @323, +358, 434.
+|\Omega| ( $\Omega$ ), +358, 434.
+|\omicron|, 434.
+|\ominus| ( $\ominus$ ), 436.
+*|\omit|, 240, 243--244, @246--247, 282.
+one half, 141, 186.
+\<one optional space>, +269--+270.
+online interaction, \see interacting with \TeX.
+|\ooalign|, +356.
+Op atom, 158--159, 170--171, 289, 442--444.
+Open atom, 158, 170--171, 289, 442--444, 446.
+open intervals, 171.
+openface, \see blackboard bold.
+*|\openin|, +216--217, 280.
+openings, 134, 147, 154--155, 359, +437; \also Open atom.
+*|\openout|, 226--228, 254, +280, @422, @423.
+|\openup|, 194, 237, 242, +362.
+|\oplus| ( $\oplus$ ), 9, 154, 436.
+optimization of macros, 342, 345, 348.
+\<optional assignments>, +286.
+\<optional {\tt by}>, +276.
+\<optional sign>, +57.
+\<optional signs>, +269.
+\<optional spaces>, +268, 269--271.
+\<optional {\tt true}>, +270.
+*|\or|, 210, +213, @406.
+|\Orb| ( \Orb\ ), +356.
+Ord atom, 158, 170--171, 289, 358, 442--446.
+ordinary symbols, 132, \see Ord atom.
+Ore, \O ystein, 53.
+organists, 239.
+organs, 38--39, 46, 85, +267, 373, +456.
+orphans, \see widow words.
+Osbourne, Lloyd, 67.
+|\oslash| ( $\oslash$ ), 436.
+|\other|, 421.
+other character, 37.
+|\otimes| ( $\otimes$ ), 9, 436.
+*|\outer|, +206, 210, 275, @354, @357, @418--419, @422.
+*|\output|, 125, +253, @254--257, 275, @364, 370, @417.
+output routines, 21, 112, 251--264, 417.
+\sub when invoked, 122, 125, 281.
+*|\outputpenalty|, +125, @254--255, 273, 349, 400, @417.
+*|\over|, @139--141, @148, 152, 292, 437, 444--445.
+Over atom, 158, 289, 443.
+|\overbrace|, @176, 225, +359.
+overfull boxes, 27--30, 94, 229, 238, 302--303, 307, 400.
+\sub avoiding, 107.
+*|\overfullrule|, 274, 307, @348.
+overlaps, 82--83, 386.
+|\overleftarrow|, +359.
+*|\overline|, @130--131, @136, 141, 170, 291, 443.
+overloading, 54, 243.
+|\overrightarrow|, 226, +359.
+*|\overwithdelims|, @152, 292, 444--445.
+|\owns| ( $\owns$ ), +361, 438.
+\newletter
+|\P| ( \P\ ), 53, 117, +356, 438--439.
+|\p@|, +347, 348.
+|\p@renwd|, +361.
+page breaks, avoiding bad, 109--111, 189, 193.
+\sub forcing good, 109--111; \also |\eject|.
+page builder, 110--114, 122--125, 281.
+\sub when exercised, 122, 280--283, 286--287.
+page format, modifying, 251--253.
+page make-up, 109, \see output routines.
+page numbers, 21, 23, 119, 207, 251--253.
+|\pagebody|, 255--257, +364.
+|\pagecontents|, 256, +364.
+*|\pagedepth|, +114, 123, 214, 271, 272.
+*|\pagefilllstretch|, +114, 214, 271, 272.
+*|\pagefillstretch|, +114, 214, 271, 272.
+*|\pagefilstretch|, +114, 214, 271, 272.
+*|\pagegoal|, +114, 123, 214, 271, 272.
+|\pageinsert|, 115, +363.
+|\pageno|, @252, @256, @340, +362, @406.
+*|\pageshrink|, +114, 123, 214, 271, 272.
+*|\pagestretch|, +114, 214, 271, 272.
+*|\pagetotal|, +114, 123, 214, 271, 272.
+*|\par|, +47, 86--87, 100, 135, 202, 249, 262, +283, +286, @340, 351, @380--381.
+\sub forbidden in arguments, 205, 207.
+paragraph, implied beginning of, 85--86, +283.
+\sub implied end of, @24, 86, +286.
+\sub last line of, 99--100.
+\sub shape parameters reset, 103.
+paragraph sign, 53.
+|\parallel| ( $\parallel$ ), 436.
+\<parameter text>, 203, 275.
+parameters, \see macro arguments and parameters.
+parameters, numeric, 119, 342; \also |\fontdimen|.
+\sub default values, 348--349.
+\sub table, 272--274.
+parentheses, 51, 129, 134, @140, @145--150, 345, 437.
+\sub roman, in italic text, 409--411.
+*|\parfillskip|, 100, 188, 274, +286, 307, @315, 332, @348, @394, @419.
+*|\parindent|, 86, 100, 101--102, 105, @262, 274, 282, 286, 291, @342, @348,
+ @355, @394, @406, @415.
+*|\parshape|, 101--102, 214, 271, 272, 277, 283, @315, 349, 374.
+*|\parskip|, 79, 104--105, 262, 274, 282, @342, @348, @355, @406, @417.
+|\partial| ( $\partial$ ), @147, 435.
+Pascal, Blaise, 101--102.
+|\path|, 390--391.
+*|\patterns|, 277, +453, 455.
+patterns for hyphenation, 449--453.
+*|\pausing|, 273, +303.
+Paxton, William Hamilton, 425.
+|pc| (pica), +57, 270, @415.
+penalties, 95--100, 110--114, 189.
+\sub infinite, 97, 111, 286, 400.
+\sub negatively infinite, 97, 111--112, 114, 254--256, 264, 400.
+*|\penalty|, 79, 97, 110--111, 174, +280, @353.
+|\penalty-'10000000000|, 264.
+percent sign, @26, 38, 39, 43, 48, 51, 113, 124, @249, @337, @340.
+period, 51, @72--73, 133--134, 149, @161, 345, @394--395, 430.
+\sub space after, @73--75, +76.
+periodic preambles, 241--242, @244, @246, @361, @412.
+Perlis, Alan Jay, 365.
+|\perp| ( $\perp$ ), +358, 436.
+|\phantom|, 131, 178, 211, +360, @412.
+|\phi| ( $\phi$ ), @128, @148, 434.
+|\Phi| ( $\Phi$ ), @323, 434.
+Philips, Peter, 239.
+philosophers, 100, 239.
+\<physical unit>, +270.
+|\pi| ( $\pi$ ), 9, @87, @137, @148--149, 434.
+|\Pi| ( $\Pi$ ), 9, @324, 434, 435.
+piano, 17.
+pica, 57, 413, \see |pc|.
+pictures, 228--229.
+pieces of symbols, 145, 432, 442.
+pilcrow, \see |\P|.
+|plain.tex|, 342--364.
+plain \TeX\ format, 10--11, +343--+364.
+\sub summary, 339--342.
+|\plainoutput|, 255, +364.
+Plass, Michael Frederick, 340--341.
+Plato, 1, 239.
+|plus|, 71, 271, 337.
+\<plus or minus>, +268.
+plus sign, 51, 132, 268.
+|\pm| ( $\pm$ ), @133, +358, 436.
+|\pmatrix|, 176, @323, +362.
+|\pmod| (notation like (mod~$p$)), @164, 322, +361.
+|\pnt|, 395.
+pocket-size, 59.
+poem, 94.
+points (printers' units), 15, 57--58.
+points with arbitrary coordinates, 389.
+Polish characters, 52--53.
+P\'olya, Gy\"orgy (= George), 7.
+poor man's bold, 386.
+*|\postdisplaypenalty|, 189--190, 272.
+poultry, 236--237.
+pound sign, \see hash mark.
+pound sterling, 54, @339, 428.
+powers of ten, 293.
+powers of two, 293.
+|\Pr| ( $\Pr$ ), 162, @323, 361.
+Pr\ae torius [Schultheiss], Michael, 239.
+preambles, 206, 235--249.
+|\prec| ( $\prec$ ), 436.
+|\preceq| ( $\preceq$ ), 436.
+*|\predisplaypenalty|, 189--190, 272, @348.
+*|\predisplaysize|, 188, 190, 274, 349.
+\<prefix>, +275.
+|\preloaded|, +350, 413.
+preloaded formats, 25--26, 39, 283, 344.
+Presume, Livingstone Irving, 74.
+*|\pretolerance|, +96, 107, 272, @317, @348, @364, @394, @451.
+pretty-printed programs, 165, 234.
+*|\prevdepth|, 79--80, 89, 271, 272, 281, +282.
+|prevdepth ignored|, 88--89.
+*|\prevgraf|, 103, 188, 190, 214, 271.
+|\prime| ( $\prime$ ), @130, 155, 357, 435.
+prime numbers, 148--149, 218.
+primitive, 9--11, 267, 342, 457.
+private control sequences, 344, 364, 414.
+|\proclaim|, 202--203, 206, @340--341, +355.
+Procter, William Alexander, 38.
+|\prod| (large $\Pi$), @180--181, 435.
+programming with \TeX, 217--219, 387--388.
+programs, for computers, 38, 165, 234.
+\sub for music, 408--412.
+proofreading, 59, 303.
+proper names, 73, 92.
+|\propto| ( $\propto$ ), 436.
+prototype row, 238, 302--303.
+|\ps|, 403, @404, +407.
+pseudo parameters, 119, +349.
+|\psi| ( $\psi$ ), @325, 434.
+|\Psi| ( $\Psi$ ), 434.
+psychologically bad breaks, @91--93.
+|pt| (printer's point), @24, +57--+58, 268--270.
+Punct atom, 158, 170--171, 289, 442--443, 446.
+punctuation, 14, 51, 72--76, 321, 394--395.
+\sub in formulas, 134, 154--155, 161, 358--359, 438; \also Punct atom.
+\newletter
+|\qquad|, @166, @185, +352.
+quad, 60, 166--168, 177, 433.
+|\quad|, @94, 166--167, @185, @232--233, +352.
+quad left, \see flush left.
+quad middle, \see |\break|.
+quad right, \see flush right.
+quarter circles, 389--391.
+question mark, 51, 73, 161.
+Quick, Jonathan Horatio, 298.
+quotation marks, @3--5, @24, 394--395.
+quotations, 100, 418--419, 425.
+quotes within quotes, 5.
+\newletter
+$\rm I\!R$, \see blackboard bold.
+Rad atom, 158, 289, 443.
+*|\radical|, 157--159, 291, 443.
+ragged bottom margins, 111, 253, 256, 398.
+ragged left margins, 392--394.
+ragged right margins, 29--30, 101, 261--262, 355--356, 392--394.
+|\raggedbottom|, 111, 253, +363, @406.
+|\raggedcenter|, 107.
+|\raggedright|, 29--30, 76, 101, 107, @115, @262, +356, @396, @407.
+*|\raise|, @66--67, 80, 151, 179, @193, +285, +290, @408.
+|\rangle| ( $\rangle$ ), @146--147, 150, +359, 437.
+Raper, Matthew, 61.
+|\rbrace| ( $\rbrace$ ), @146--147, +359, 437.
+|\rbrack| ( $\rbrack$ ), @146--147, +351, 370, 437.
+|\rceil| ( $\rceil$ ), @146--147, +359, 437.
+|\Re| ( $\Re$ ), 435.
+*|\read|, 215, 217--218, 276, 346, 400.
+recent contributions, 112, 125, 281.
+recovery from errors, 30--34, 295--303.
+recursion, 219, 268, 301, 379, 391, 397.
+\sub infinite, 299, 301, 383.
+reduction, 16.
+reference marks, 116--117.
+reference point of a box, 63--64, 77, 80--82, 388--389.
+\<registerdef>, +277.
+registers, 117--122, 212, 214, 346--348.
+registration marks, 416.
+Reid, Brian Keith, 371, 404--406.
+Rel atom, 158, 170--171, 289, 442--444, 446--447.
+\<relation>, +209.
+relations, @133--134, 147, 154--155, 358, +436, +437; \also Rel atom.
+*|\relax|, 23, 25, 71, 240, 276, +279, @307, @353.
+|\relbar| ( $\relbar$ ), +358.
+|\Relbar| ( $\Relbar$ ), +358.
+*|\relpenalty|, 101, 174, 272, 322, @348, +446.
+|\removelastskip|, +353.
+|\repeat|, @217--219, +352.
+repeating commands, \see |\loop|.
+reserved characters, 37--38, 51--52, 134.
+reserved words, +61, 71, +268, 337, 370.
+restricted horizontal mode, 85, 87--89, +285--+287.
+r\'esum\'e, 253.
+\<return> (ASCII code 13), 23, 39, 43, 45, 46, 48, 249, 331, 343, 345, 369, 380.
+|\|\<return>, 8, @305, +351.
+reverse apostrophe, 3--5, 51, 132, 134, 391, 394--395;
+ \also alphabetic constant.
+reverse slash, \see backslash.
+Reviewer, Ann Arbor, 106.
+|\rfloor| ( $\rfloor$ ), @146--147, +359, 437.
+|\rgroup|, 150, @176, +359, 437.
+|\rho| ( $\rho$ ), 128, @325, 434.
+|\rhook|, +358.
+*|\right|, @148--150, 155--157, @171, 196, +292, 437.
+right brace, @13--14, @19--21, 38, 51, @200--202, +203--+204, @205--206,
+ +269, 275--276, +279, 301, @330.
+\<right brace>, +275.
+right bracket, 51, 134, @146--147, 171, 345, @408, @437.
+right delimiters, \see openings.
+right-hand pages, 252--253, @416.
+right justification, 71.
+right parenthesis, 51, 134, @140, @145--150, 345.
+right quote, 3--5, 51, @130, 155, 201, @305, 324, 394--395; \also octal.
+|\rightarrow| ( $\rightarrow$ ), 226, 437.
+|\Rightarrow| ( $\Rightarrow$ ), 226, 437.
+|\rightarrowfill| (\hbox to4em{\rightarrowfill}), 226, +357.
+|\rightharpoondown| ( $\rightharpoondown$ ), 437.
+|\rightharpoonup| ( $\rightharpoonup$ ), 437.
+*|\righthyphenmin|, 273, @364, +454, 455.
+|\rightleftharpoons| ( $\rightleftharpoons$ ), +361, 437.
+|\rightline|, 101, @317, @340--341, +353.
+*|\rightskip|, 100--101, 274, @317, @356, 393, @421.
+|\rlap|, 82--83, @189, @247, @319, +353, @389, @416.
+|\rm| (use roman type), 13--15, 154, @163, @320, +351, @364, @409, @414--415,
+ @419, 427.
+|\rmoustache|, 150, +359.
+roman letters in math, 162--164.
+roman numerals, 40--41, 252, 256.
+\sub uppercase, 374.
+roman parentheses in italic text, 409--411.
+roman type, 13--17, 51--55, 127, 162--165, 427.
+*|\romannumeral|, 40--41, 213, 214, @252.
+Root, Waverley Lewis, 55.
+|\root|, @130--131, @179, @325, +360.
+rounding, 58, 119.
+|\rq| ( \rq\ ), 5, 48, +351, 370, @395.
+rule boxes, 24, 64, 221--225, 281--282, 285, 291.
+\<rule dimension>, +282.
+\<rule specification>, +282.
+rule thickness, 143, 179, 221, 447.
+ruled tables, 245--248, 392.
+rulers, 58.
+runaways, 205--206, 297.
+running headlines, 253, 258--260, 416.
+running the program, 23--35.
+Russian characters, 370.
+\newletter
+|\S| ( \S\ ), 53, 117, +356, 438--439.
+Sally, Baby, 72--76.
+sample line for tabbing, 232--234.
+sample pages for book design, 412--413.
+|save size|, 300--301, 374, 399.
+save stack buildup, 301, 346.
+|\sb|, 135, +357, 370.
+|\sc| (use {\sc SMALL CAPS} type), 414--415.
+|scaled|, 16--17, 60, 277, @350, 433.
+scaled points, 57--58, 270.
+Scandinavian letters, 45--46, 52--53.
+Schickele, Prof\period~Peter, 410--411, 481.
+scratch control sequence, 352.
+scratch registers, 122, 346.
+Scribe, 371, 404.
+script letters, 164, 434.
+script size, 140, 153, 442.
+script style, 140--142, 441--447.
+*|\scriptfont|, 153, 168, 213, 271, 321, 351, @414--415, 441--442.
+scriptscript size, 140, 145, 153, 442.
+scriptscript style, 140--142, 441--447.
+*|\scriptscriptfont|, 153, 168, 213, 271, 351, @414--415, 441--442.
+*|\scriptscriptstyle|, 141--142, @179, 292.
+*|\scriptspace|, 274, 348, 445--446.
+*|\scriptstyle|, 141--142, @145, @179, 292.
+*|\scrollmode|, +32, +277.
+|\searrow| ( $\searrow$ ), 437.
+|\sec| ( $\sec$ ), 162, 361.
+section number sign, \see |\S|.
+Selden, John, 11.
+selection, \see |\cases|.
+semicolon, 51, @134, 161.
+sentences, 72--76.
+Sesame Street, 61.
+set macro, 379.
+set notation, @147, @174--175.
+*|\setbox|, 66--67, 77, 81, +120, 276, 279, 286, 291, @386--392.
+*|\setlanguage|, 287, +455.
+|\setminus| ( $\setminus$ ), @436.
+|\settabs|, 231--234, +354, 355.
+setting the glue, 70, 77, 81, 388.
+|\setupverbatim|, 380--381, 391.
+|\sevenrm|, 15, 153, +350, 351, @414--415.
+*|\sfcode|, +76, 214, 271, 286, @321, @345, @351.
+shadow boxes, 66.
+Shakespeare, William, 17, 55, 337.
+\<shape assignment>, +277.
+\<shape dimensions>, +277.
+|\sharp| ( $\sharp$ ), @409, 435.
+sharp S ( \ss\ ), \see |\ss|.
+sharp sign, \see hash mark.
+Shaw, George Bernard, 107, 229.
+Sheridan, Richard Brinsley Butler, 265.
+shifted output, \see |\hoffset|, |\voffset|.
+shilling sign, \see slash.
+*|\shipout|, 227, 253--254, 279, 300, 302.
+shortest paths, 99.
+\<shorthand definition>, +277.
+*|\show|, @10, 215, 279, 299.
+*|\showbox|, @66--67, 121, 234, 279.
+*|\showboxbreadth|, 273, +302, 303, @348.
+*|\showboxdepth|, 79, 273, +302, 303, @348.
+|\showhyphens|, +364, @452.
+*|\showlists|, @88--89, 95, 112, 125, @158--159, 279, 293.
+*|\showthe|, 121, 215, 279.
+shriek, \see exclamation point.
+\<shrink>, +271.
+shrinkability, 69--71, @75, 409.
+side conditions, @166--167, @185--186.
+|\sigma| ( $\sigma$ ), @195--196, 434.
+|\Sigma| ( $\Sigma$ ), @165, 434, 435.
+sigma signs, \see |\sum|.
+|\signed|, 106.
+|\sim| ( $\sim$ ), @133, 436.
+|\simeq| ( $\simeq$ ), @133, 436.
+\<simple assignment>, +276.
+|\sin| ( $\sin$ ), 162, 361.
+|\sinh| ( $\sinh$ ), 162, 361.
+sinkage, 413.
+size switching, 15, 408, 414--415.
+sizes of type for mathematics, 140, 153.
+|\skew|, @136, +359.
+*|\skewchar|, 214, 271, 273, 277, @351, @414, 430, 431, +443.
+*|\skip| registers, 118--122, +271, +276, @346--347, @349, @352, @363, @394.
+*|\skipdef|, 119, 215, +277, @346--347.
+\<skipdef token>, +271.
+skipping space, \see glue.
+|\sl| (use {\sl slanted\/} type), 13--15, 165, +351, @409, @414--415, @419.
+slant of a font, 375, 433.
+slanted type, 13, 63--64, 100.
+slash, 51, @132, 146--147, @320, 430.
+|\slash| (/ with break allowed), @93, +353.
+slashed form of fractions, 139--140.
+Slavic h\'a\v cek accent, \see |\v|.
+|\slfam|, +351, @414--415.
+slides, 59.
+slurs, \see |\smile|, |\frown|.
+small caps, 414.
+|\smallbreak|, 111, +353, 421.
+|\smallint| ( $\smallint$ ), +358, 435.
+|\smallskip|, 70, 78, @100, 109, 111, @181, @340--341, +352, @355, @410--412.
+|\smallskipamount|, +349, @352--353.
+|\smalltype|, 408--411.
+|\smash|, 131, 178, @327, +360.
+|\smile| ( $\smile$ ), 436.
+Smith, John, 447.
+solidus, \see slash.
+|sp| (scaled point), +57, 118--119, 270, 398, 400.
+|\sp| (superscript), 135, +357, 370.
+\<space> (ASCII code 32), 39, 43, 46, 343.
+|\|\<space>, \see |\|\] (near the beginning).
+|\space|, 254, 272, @306, +351, 380, @406.
+space after a constant, 208, 272.
+space factor, 76, 285--287, 306, 380, 395.
+\<space token>, 268, +269, 282, 285, 290, @376--377.
+*|\spacefactor|, 76, 271, 272, 285, @363, 433.
+spaces, 3, 5, 8--9, 19, 37, 40, 47--48, 127, 204, 232, 272, 297, 299, 319, 336.
+\sub as active characters, 254, 380--381.
+*|\spaceskip|, 76, 274, @317, @356, @416, 429.
+spacing, \see glue.
+\sub in formulas, 162, 167--171.
+|\spadesuit| ( $\spadesuit$ ), +358, 435.
+*|\span|, 215, 238, +243, @244, +245, 248, 249, 282, @330, 385.
+Spanish ligatures, 51, 381, 427.
+spanned columns in tables, 243--245.
+spanned rows in tables, 249.
+*|\special|, 216, 226, +228--+229, 280.
+special characters, 37--38, 43--46, 51--52, 134, 367--371.
+\<special dimen>, +271, 277.
+\<special integer>, +271, 277.
+special symbols for math, 128, 434--438.
+Spivak, Michael David, 137.
+|% split|, 124.
+split insertion penalty, 124.
+*|\splitbotmark|, 213, +259, 280.
+*|\splitfirstmark|, 213, +259, 280.
+*|\splitmaxdepth|, 124, 274, 281, @348, @363, @417.
+*|\splittopskip|, 124, 274, 281, @348, @363, @397, @417.
+|spread|, 77, 222, 238, 278.
+springs, 70.
+|\sqcap| ( $\sqcap$ ), @133, 436.
+|\sqcup| ( $\sqcup$ ), @133, 436.
+|\sqrt|, @130--131, @141, @145, 157, @169--170, +360, +443.
+|\sqsubseteq| ( $\sqsubseteq$ ), 436.
+|\sqsupseteq| ( $\sqsupseteq$ ), 436.
+|\square| ( $\vcenter{\hrule
+ \hbox{\vrule height\sqht \kern\sqht \vrule}
+ \hrule}$ ), 151.
+square brackets, \see brackets.
+square bullet ( \bull\ ), +420.
+square root, \see |\sqrt|.
+squeeze routine, 188.
+squiggle accent (\~{}), \see |\~|, |\tilde|, |\widetilde|.
+squint print, 59.
+|\ss| ( \ss\ ), 52, +356.
+stack positions, 300--301, 374, 399.
+stacked fractions, \see |\over|.
+|\star| ( $\star$ ), 436; {\sl cf\period}~asterisk.
+states, 46--48.
+sterling, 54, @339, 428.
+Stevenson, Robert Louis Balfour, 67.
+Stirling numbers, \see |\brace|, |\brack|.
+stomach, \see anatomy of \TeX.
+stopping \TeX, \see |\end|.
+|story.tex|, 24, 26, 30--31.
+Stout, Rex Todhunter, 401.
+\<stretch>, +271.
+stretchability, 69--71, @75, 409.
+*|\string|, 40--41, +213--+214, 215, @348, @377.
+|\strut|, 82, @142, 178, 240, @246--247, @316, @329, @333, +353, @396, @400,
+ @421.
+|\strutbox|, @316, +353, @396, @414--415.
+struts, +82, 125, 131, 142, 178, 245--247, 255, 329, 416, 422, 423.
+style change items, 157, 442.
+style-independent documents, 194, 203.
+styles of math formatting, 140--141, 441--447.
+styles of math writing, 166--167, 182--183.
+subformulas, 129, 171, 173.
+\<subscript>, +291.
+subscripts, 15, @128--131, @133, 150, 158--159, @163, 179, +289--+291, 343.
+|\subset| ( $\subset$ ), @133, 436.
+|\subseteq| ( $\subseteq$ ), @133, 436.
+|\succ| ( $\succ$ ), 436.
+|\succeq| ( $\succeq$ ), 436.
+|\sum| (large $\Sigma$), @139, @144--145, @148--149, +358, 432--433, 435.
+|\sum|$'$, 181, @324--325.
+summary of plain \TeX, 339--342.
+summation, \see |\sum|.
+|\sup| ( $\sup$ ), 162, +361.
+|\supereject|, 116, 254, 256, 257, +353, @407.
+superiors, \see superscripts.
+\<superscript>, +291.
+superscripts, @128--131, @133, 150, 158--159, 179, +289--+291, 343.
+suppressed-L, 52--53.
+|\supset| ( $\supset$ ), 436.
+|\supseteq| ( $\supseteq$ ), 436.
+|\surd| ( $\surd$ ), +358, 435.
+surd signs, \see |\radical|, |\sqrt|.
+Swanson, Ellen Esther, 197.
+|\swarrow| ( $\swarrow$ ), 437.
+Swedish characters, 52--53, 370.
+Sweelinck, Jan Pieterszoon, 239.
+\'Swierczkowski, Stanis\l aw S\l awomir, 53.
+Swift, Jonathan, 5.
+symbol fonts, 157, @351, 431, 433, 441, 447.
+symbolic box format, 66, 75, 79, 158--159, 302, 455.
+symbols in math, table, 434--438.
+syntax rules, 268--269.
+System Development Foundation, vii.
+Szeg\H o, G\'abor, 7.
+\newletter
+|\t| (tie-after accent), 52--53, +356.
+\<tab> (ASCII code 9), 8, 45, 343, 369, 391.
+|\|\<tab>, 8, 351.
+|\tabalign|, +354, 355.
+tabbing, 231--234, @339, +354.
+\sub compared to |\halign|, 235.
+tables, \see alignments, tabbing.
+tables of contents, 120, 226.
+tables of \TeX\ trivia:
+\sub accents (non-math), 52, 339.
+\sub atomic types, 158.
+\sub category codes, 37.
+\sub character codes, 367--369.
+\sub default values, 343--345, 348--349.
+\sub |\fontdimen| parameters, 433, 447.
+\sub keywords, 61.
+\sub math accents, 135.
+\sub math classes, 154.
+\sub math spacing, 170--171.
+\sub math symbols, 434--438.
+\sub parameters, 272--275.
+\sub units of measure, 57.
+|\tabs|, 234, +354.
+*|\tabskip|, 215, +237--+239, @244, @247, 274, 282, 285, @354.
+tabskip glue, +237--+239, 245, 302--303, 392.
+Tacitus, Publius Cornelius, 219.
+tags, \see equation numbers.
+tail recursion, 219.
+|\tan| ( $\tan$ ), 162, 361.
+|\tanh| ( $\tanh$ ), 162, +361.
+|\tau| ( $\tau$ ), 1, 434.
+Tchaikovsky, \see Cha\u\i kovski\u\i.
+telephone numbers, 408.
+templates, 206, 235--236, 240--243.
+|\tenex|, +350, @351, @361, @414--415.
+|\tenpoint|, 15, +414, @418.
+|\tenrm|, 15, 27--28, 45, 153, 252, @335, +350, @351, @414.
+|\tensl|, 15, +350, @351, @414.
+tensor notation, @130, @169.
+|\tensy|, @179, +350, @351, @414.
+terminal, input from, 217--218.
+\sub output to, 217--218, 226--228, 279--280.
+TEX, 1.
+\TeX, bad puns on, 1, 11, 63, 153, 161, 225, 229, 262, 305, @340--341, +373.
+\sub pronunciation of, 1.
+|TeX|, 1.
+|\TeX| ( \TeX\ ), 8--10, 19, 66--67, 204, 225, @340--341, +356, +418, +419.
+|.tex|, 25, 217, 226.
+|TeX capacity exceeded|, 300--301.
+\TeX\ Grandmasters, 253.
+\TeX\ Users Group, vii, 408, 483.
+\TeX78, vii.
+|texput|, 23.
+text between aligned displays, 193.
+text italic, 13--14, 157, 165, 332, 430.
+text size, 140, 153, 442.
+text style, 140--142, 441--447.
+*|\textfont|, 153, 168, 188, 213, 271, +351, @414--415, 441--442.
+|\textindent|, 117, +355.
+*|\textstyle|, 141--142, +292, @326.
+|.tfm| (font metric files), 400, 433, 442.
+*|\the|, +214--+215, @216, @373, @375, @422.
+theorems, 111, 202--203, @340--341, +355.
+|\theta| ( $\theta$ ), 128, @162, @325, 434.
+|\Theta| ( $\Theta$ ), 434.
+*|\thickmuskip| (thick math space), 167--168, 274, @349, 446.
+thin spaces, 161, 167--173, 305, 409.
+*|\thinmuskip| (thin math space), 167--168, 274, @349, 446.
+|\thinspace|, @5, 10, 305, @311, +352, 409.
+|This can't happen|, 299.
+Thor, Arthur Uther, 24, 117, 200, 253.
+three-column output, 257, 396--398.
+three dots, \see ellipses.
+tie-after accent, \see |\t|.
+ties, 25, 73--74, +91--+93, 104, @161, 167, @173, 353, @404.
+tight lines, 97, 99, 302.
+tilde, 38, 51, @91--92, \see ties.
+|\tilde| (math accent: $\tilde x$), 135, 164.
+tilde accent (\~{}), \see |\~|, |\tilde|, |\widetilde|.
+*|\time|, 273, 349.
+|\times| ( $\times$ ), @133, 436.
+Times Tribune, Peninsula, 455.
+Titelouze, Jehan, 239.
+|to|, 77, 217, 222, 238, +276, +278.
+|\to|, @134, +361, 438.
+|\today|, @406.
+token list parameters and registers, 212, 215, 275, 373.
+token lists, as displayed by \TeX, 228, 329, 382.
+\<token parameter>, +275.
+\<token variable>, 212, +276.
+tokens, 38--41, 46--48, 203--207.
+*|\toks|, 212, 215, 262, 276.
+*|\toksdef|, 212, 215, 277, @347, @378.
+\<toksdef token>, +271.
+*|\tolerance|, @29--30, 91, 94, +96, 107, 272, @317, @333, @342,
+ @348, @364, @451.
+|\top| ( $\top$ ), 435.
+|\topglue|, @340, +352.
+|\topins|, 256, +363, @364.
+|\topinsert|, 115--116, 251, +363.
+*|\topmark|, 213, +258, 259--260, 280.
+*|\topskip|, +113--+114, 124, 256, 274, @348.
+Trabb Pardo, Luis Isidoro, 92.
+|\tracingall|, 121, 303, +364.
+*|\tracingcommands|, 88--89, +212, 273, 299.
+*|\tracinglostchars|, 273, +301, @348, @400.
+*|\tracingmacros|, +205, +212, 273, @329.
+*|\tracingonline|, 121, 212, 273, +303.
+*|\tracingoutput|, +254, 273, @301--302.
+*|\tracingpages|, @112--114, 124, 273, +303.
+*|\tracingparagraphs|, @98--99, 273, +303.
+*|\tracingrestores|, 273, +301, +303.
+*|\tracingstats|, 273, +300, +303, @383.
+transcript, \see log file.
+transparencies, 59.
+|\triangle| ( $\triangle$ ), 435.
+|\triangleleft| ( $\triangleleft$ ), 436.
+|\triangleright| ( $\triangleright$ ), 436.
+triangular quotation, 101--102.
+tricky macros, 41, 261--263, 354, 360, 361, 373--400, 421.
+|true|, 59--60, +270, @407--408.
+truth, vi, 267.
+Tschichold, Jan, 83.
+|\tt| (use {\tt typewriter} type), 13, 53, 113, 165, @340--341, +351,
+ @380--382, @414--415, +429.
+|\ttfam|, +351, @414--415.
+|\ttglue|, 414--415, 421.
+|\ttraggedright|, +356.
+{\sl\backup TUGboat}, vii, 425, 483.
+turtle commands, 390--391.
+Twain, Mark (= Clemens, Samuel Langhorne), 83, 451.
+two-column format, @257, 386--388, @417.
+two-line displays, 196.
+type size switching, 15, 408, 414--415.
+typefaces, 13, 17, 427.
+typewriter type, 13, 53, 165, 356, 420--421, 429.
+\newletter
+|\u| (breve accent), 52--53, +356.
+*|\uccode|, 41, 214, 271, 345, @348, @377, @394.
+*|\uchyph|, 273, @348, +454.
+umlaut accent (\"{}), \see |\"|, |\ddot|.
+unbreakable spaces, \see ties.
+|\uncatcodespecials|, 380.
+|\undefined|, 350, 384.
+undelimited parameters, 203--204, 376--377.
+Under atom, 158, 289, 443.
+|\underbar|, @244, @323, +353.
+|\underbrace|, @176, 225--226, +359.
+underfull box, 29, 94, 238, 302--303, 397, 400, 417, 452.
+*|\underline|, @130--131, 141, 291, +443.
+underlined text, 178; \also |\underbar|.
+underscore (the character `|_|'), 38, 51, @128--130, 134, @165.
+*|\unhbox|, 120, 283, +285, 293, @354, @356, @399.
+*|\unhcopy|, 120, 283, +285, 293, @353.
+\<unit of measure>, 268, +270.
+units of measure, 57--61, 270.
+\sub in formulas, 169.
+\sub table, 57.
+*|\unkern|, +280.
+unmatched left brace, 216; \also runaways.
+*|\unpenalty|, +280.
+unset box, 240, 302--303.
+\<unsigned dimen>, +270.
+\<unsigned mudimen>, +270.
+\<unsigned number>, +269.
+*|\unskip|, 222--223, +280, 286, @313, @392, @418--419.
+*|\unvbox|, 120, 254, +282, 286, @354, @361, @363, @364, @392, @399, @417.
+*|\unvcopy|, 120, +282, 286, @361.
+|\up|, 408.
+|\uparrow| ( $\uparrow$ ), @146--147, @182, +359, 437.
+|\Uparrow| ( $\Uparrow$ ), @146--147, +359, 437.
+|\upbracefill| (\hbox to4em{\upbracefill}), 225--226, +357.
+|\updownarrow| ( $\updownarrow$ ), @146--147, +359, 437.
+|\Updownarrow| ( $\Updownarrow$ ), @146--147, +359, 437.
+|\uplus| ( $\uplus$ ), 436.
+*|\uppercase|, 41, 215, 217, +279, 307, 345, @348, @374, @377, @394.
+uppercase letters, 9, 64, 268, 370.
+\sub Greek, 127, 164, 434.
+\sub roman numerals, 374.
+|\upsilon| ( $\upsilon$ ), 434, 483.
+|\Upsilon| ( $\Upsilon$ ), 434.
+Ursa Major, 389.
+User, Ben Lee, 74, 80, 100, 140, 164, 186, 187, 193, 221, 296, 410--411.
+\newletter
+|\v| (check accent), 52, +356.
+*|\vadjust|, 95, 105, 109, 110, 117, 259, +281, @317, 393, 454.
+*|\valign|, 249, 283, +285--+286, 302, @335, @397.
+Vall\'ee Poussin, Charles Louis Xavier Joseph de la, 92.
+van der Waerden, Bartel Leendert, 92.
+van Leunen, Mary-Claire, 425.
+|\varepsilon| ( $\varepsilon$ ), 128, 434.
+\<variable assignment>, +276.
+variable family, 154, 289, @358--359.
+variables in formulas, 132, 358, 434--435; \also Ord atom.
+|\varphi| ( $\varphi$ ), 128, @147, 434.
+|\varpi| ( $\varpi$ ), 434.
+|\varrho| ( $\varrho$ ), 128, 434.
+|\varsigma| ( $\varsigma$ ), 434, 483.
+|\vartheta| ( $\vartheta$ ), 128, 434.
+*|\vbadness|, 272, @348, @397, @417.
+vbox (box with vertical list inside), 65.
+*|\vbox|, 65, 80--82, 103, 151, 193, 222, 278, 388--389.
+Vcent atom, 158, 290, 443.
+*|\vcenter|, 150--151, 159, 170, 193, 222, 242, +290, @361, +443.
+|\vdash| ( $\vdash$ ), 436.
+|\vdots| ( $\vdots$ ), @177, +359.
+|\vec| (math accent: $\vec x\,$), 135; \also |\overrightarrow|.
+vectors, 177, 199--201; \also |\vec|.
+|\vee| ( $\vee$ ), @133, 436.
+verbatim copying, 422--423.
+verbatim listing, 48, 380--382, 391, 420--421.
+|\vert| ( $\vert$ ), @146--147, 150, +359, 438; {\sl cf\period}~|\mid|.
+|\Vert| ( $\Vert$ ), 117, @146--147, 150, 186, +359.
+\<vertical command>, +286.
+vertical line (the character `\|'), 52, 53, 132, @146--147, @171, @174, 438;
+ \also vertical rules.
+vertical lists, 64, +110.
+vertical mode, 85--89, +267--+283.
+\<vertical mode material>, 278.
+\<vertical rule>, +281.
+vertical rules, 64, 151, 221--226, +285, +291, 392.
+\<vertical skip>, +281.
+very loose lines, 97, 99.
+*|\vfil|, 71, +72, 111, 256, 281, 286, 417.
+*|\vfill|, 24, 25, 71, +72, 256--257, 281, 286.
+|\vfilll|, 72.
+*|\vfilneg|, +72, 111, 281, 286.
+|\vfootnote|, 117, +363.
+*|\vfuzz|, 274, @348.
+|\vglue|, +352, @408.
+vinculum, \see |\overline|.
+virgule, \see slash, 51.
+visible space, +3, +420, 429.
+*|\voffset|, +251, 252--253, 274, @342, @406.
+void, 120, 210.
+|\voidb@x|, +347, 348.
+Voltaire, de (= Arouet, \hfil\penalty0\hfilneg Fran\c cois Marie), 447.
+|\vphantom|, @178--179, 211, @321, +360.
+*|\vrule|, 64, 86, 151, 221--222, 224, @245--247, +281--+282, 283, @357, @392,
+ @420.
+*|\vsize|, 113--114, 251, 253, 255, 274, @340--341, @348, 400, @406, 413, @415,
+ @417.
+*|\vskip|, @24, 71, 85, 191, +281, 286.
+*|\vsplit|, +124, 222, 259, 278, @397, @417.
+*|\vss|, 71, +72, @255, 281, 286.
+*|\vtop|, 81--82, 151, 222, 278, @333.
+\newletter
+Walter's worksheet, 244.
+Walton, Izaak, 67.
+Watson, John Hamish, M\period D\period, 401.
+*|\wd|, 120, 271, +388--+389, @391, @417.
+|\wedge| ( $\wedge$ ), @133, 436.
+Weierstrass $p$, \see |\wp|.
+weird error, 298.
+Welsh, 239, 451.
+Westing, Arthur Herbert, 246.
+whatsits, 95, 110, 157, 226--229, 455.
+|\widehat| (math accent: $\widehat x\,$), @136, +359, 433.
+|\widetilde| (math accent: $\widetilde x\,$), @136, +359.
+widow lines, 104, 272, 398.
+widow words, 104.
+*|\widowpenalty|, 104, 113, 272, @348.
+|width|, 221, 224, +282, 337.
+width of a box, 63--67, 77, 80--82, 225.
+wiggle, \see |\sim|.
+Winograd, Terry Allen, 425.
+Wischmeyer Ehlert, Clara Louise, 248.
+|withdelims|, 152, 156.
+|\wlog|, +347.
+Wolfe [Holmes], Nero, 401.
+|\wp| ( $\wp$ ), 435.
+|\wr| ( $\wr$ ), 436.
+wreath product, \see |\wr|.
+*|\write|, 215, 216, 226--228, 254, +280, 346, @377, @422, @424.
+\newletter
+$\times$, \see |\times|, |\*|.
+x-height, 54, 60, 433, 443, 445--447.
+*|\xdef|, @215--216, 275, @373, @418, @424.
+Xenophon, 239.
+|\xi| ( $\xi$ ), 434, 447.
+|\Xi| ( $\Xi$ ), 434.
+*|\xleaders|, +224.
+*|\xspaceskip|, 76, 274, @317, 356, 429, 433.
+|\xyzzy|, 379.
+\newletter
+*|\year|, 41, 273, 349, @406.
+Yellin, Frank Nathan, 400.
+\newletter
+|\z@|, +347, 348.
+|\z@skip|, +347, 348.
+|\zeta| ( $\zeta$ ), 434.
+\newletter
+\frac1/2, 67, 332.
+1/2, in unslashed form, 141, 186.
+\<4-bit number>, +271.
+\<8-bit number>, +271, 276--278.
+\<15-bit number>, +271, 277, 289, 291.
+\<27-bit number>, +271, 289, 291.
+\enddoublecolumns
+\endchapter
+
+Important works such as histories, biographies,
+scientific and technical text-books, etc., should contain indexes.
+Indeed, such works are scarcely to be considered complete without indexes.
+\smallskip
+An index is almost invariably placed at the end of a volume
+and is set in smaller type than the text-matter.
+Its subjects should be thoroughly alphabetized.
+\smallskip
+The compiling of an index is interesting work, though
+some authors are apt to find it tedious and delegate the work to others.
+The proofreader who undertakes it will find that it is splendid mental exercise
+and brings out his latent editorial capability.
+\author ALBERT H. ^{HIGHTON}, {\sl Practical Proofreading\/} (1926)
+
+\bigskip
+
+Important references are given in boldface.
+Italicized numbers indicate fleeting references,
+whereas numbers in parentheses refer to
+mere implications or unwarranted extrapolations.
+Asterisks are used to identify particularly distasteful passages.
+\author PROF.~PETER ^{SCHICKELE}, {\sl The Definitive Biography of %
+ P. D. Q. ^{Bach}\/} (1976)
+
+\eject
+ \beginchapter Appendix J. Joining the\\\TeX\ Community
+
+This appendix is about grouping of another kind: \TeX\ users from around the
+world have banded together to form the \TeX\ Users Group (TUG), in order
+to exchange information about common problems and solutions.
+
+A newsletter/journal called {\sl TUGboat\/} has been published
+since 1980, featuring articles about all aspects of \TeX\ and \MF\null.
+^^{METAFONT}
+TUG has a network of ``site coordinators'' who serve as focal points of
+communication for people with the same computer configurations.
+Occasional short courses are given in order
+to provide concentrated training in special topics; videotapes of
+these courses are available for rental.
+Meetings of the entire TUG membership are held at least once a year.
+You can buy \TeX\ T-shirts at these meetings.
+
+Information about membership in TUG and subscription to {\sl TUGboat\/}
+is available from
+\begintt
+{\obeylines
+\TeX\ Users Group
+email: {\tt TUG@tug.org}
+internet: {\tt http://www.tug.org/}
+}
+\endtt
+Don't delay, subscribe today! That address again is
+
+\smallskip
+{\obeylines
+\TeX\ Users Group
+email: {\tt TUG\char`\@ tug.org}
+internet: {\tt http://www.tug.org/}
+}
+
+\endchapter
+
+[The printer] should refuse to employ wandering men,
+foreigners who, after having committed some grievous error,
+can easily disappear and return to their own country.
+\author HIERONYMUS ^{HORNSCHUCH}, %
+ {\eightrm'}$O\mkern-1mu\rho\mkern1mu\vartheta o\mkern1mu %
+ \tau\upsilon\pi o\gamma\mkern-1mu %
+ \rho\alpha\phi\acute\iota\alpha\varsigma$ (1608)
+
+^^|\upsilon| ^^|\varsigma|
+\bigskip
+
+An author writing an article for publication in TUGboat
+is encouraged to create it on a computer file and submit it on magnetic tape.
+\author BARBARA ^{BEETON}, {\sl {\tt\bslash title} How to Prepare a File\/%
+ {\tt\bslash cr}\phantom{\rm\enskip(1981)}\break %
+ For Publication in TUGboat\/{\tt\bslash cr}} (1981) % TUGboat 2,1 p53
+
+\eject
+ \end
diff --git a/systems/knuth/dist/tex/trip.fot b/systems/knuth/dist/tex/trip.fot
new file mode 100644
index 0000000000..7ae8e89d87
--- /dev/null
+++ b/systems/knuth/dist/tex/trip.fot
@@ -0,0 +1,83 @@
+This is TeX, Version 3.14159265 (INITEX)
+** &trip trip
+(trip.tex ##
+! Bad number (-7).
+<to be read again>
+ 8
+l.94 \openout-'78
+ terminal \openout10=tr\romannumeral1 \gobble...
+
+Completed box being shipped out [0.0.0.0.11]
+! Missing number, treated as zero.
+<to be read again>
+ {
+...
+l.106 \penalty-10000
+ % now we'll compute silently for awhile,...
+Memory usage before: 159&313; after: 102&278; still untouched: 1613
+
+! OK (see the transcript file).
+l.441 ...se unbal}\fi}\showlists
+ \tracingonline1%
+{|escapechar}
+{^^?global}
+{^^?global}
+{end}
+! Missing } inserted.
+<inserted text>
+ }
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+{end-group character }}
+{retaining escapechar=256}
+> 3.
+<output> {showthe deadcycles
+ global advance countz by1global ...
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+! You can't use `end' in internal vertical mode.
+<recently read> end
+
+<output> ...cal {}unvbox 255end
+ rb }
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+! Unbalanced output routine.
+<output> ... {}unvbox 255end rb
+ }
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+! Output loop---3 consecutive dead cycles.
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+
+Completed box being shipped out [-1.2.-1118806.0.11.196608.327680.157286
+4.1073741823]
+! Unbalanced write command.
+<write> if 01{else unbal}fi
+
+<inserted text>
+ }endwrite
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+Memory usage before: 334&431; after: 292&418; still untouched: 175
+ )
+(end occurred inside a group at level 1)
+(end occurred when if on line 442 was incomplete)
+(end occurred when ifcase on line 419 was incomplete)
+(end occurred when iftrue on line 413 was incomplete)
+(see the transcript file for additional information)
+Output written on trip.dvi (16 pages, 2920 bytes).
+Transcript written on trip.log.
diff --git a/systems/knuth/dist/tex/trip.log b/systems/knuth/dist/tex/trip.log
new file mode 100644
index 0000000000..585bf9b1fa
--- /dev/null
+++ b/systems/knuth/dist/tex/trip.log
@@ -0,0 +1,7304 @@
+This is TeX, Version 3.14159265 (preloaded format=trip 2014.1.7) 7 JAN 2014 09:59
+** &trip trip
+(trip.tex ##
+{vertical mode: \tracingrestores}
+{\write}
+{blank space }
+{\openout}
+
+! Bad number (-7).
+<to be read again>
+ 8
+l.94 \openout-'78
+ terminal \openout10=tr\romannumeral1 \gobble...
+Since I expected to read a number between 0 and 15,
+I changed this one to zero.
+
+{\openout}
+{\romannumeral}
+{\write}
+{blank space }
+{\write}
+{blank space }
+{\write}
+{blank space }
+{\vsize}
+{\vbadness}
+{\topskip}
+{\penalty}
+{\maxdepth}
+{\tracingoutput}
+{\moveleft}
+%% goal height=2000.0, max depth=2.0
+% t=0.0 g=2000.0 b=10000 p=0 c=100000#
+{\moveright}
+{restricted horizontal mode: \vrule}
+{end-group character }}
+% t=20.0 plus 1.0fil g=2000.0 b=0 p=0 c=0#
+{vertical mode: blank space }
+{\penalty}
+% t=48.0 plus 41.0 plus 1.0fil g=2000.0 b=0 p=-10000 c=-10000#
+
+Completed box being shipped out [0.0.0.0.11]
+\vbox(2000.0+2.0)x21.0, glue set 1952.0fil
+.\write-{log file only\the \prevgraf }
+.\openout0=8terminal
+.\openout10=tripos
+.\write10{}
+.\write10{\uppercase {\number {\outputpenalty }}}
+.\write10{[\uppercase {\romannumeral -\the \outputpenalty }[}
+.\glue(\topskip) 9.6 plus 1.0fil
+.\vbox(10.4+0.0)x-2.0, shifted -20.0
+..\rule(0.4+0.0)x*
+..\glue 10.0 plus 5.0filll
+.\glue(\baselineskip) 10.0 plus 41.0
+.\hbox(0.0+20.0)x1.0, shifted 20.0
+..\rule(-19.0+20.0)x1.0
+
+log file only0
+{no mode: \number}
+
+! Missing number, treated as zero.
+<to be read again>
+ {
+...
+l.106 \penalty-10000
+ % now we'll compute silently for awhile,...
+A number should have been here; I inserted `0'.
+(If you can't figure out why I needed to see a number,
+look up `weird error' in the index to The TeXbook.)
+
+{\romannumeral}
+{\the}
+Memory usage before: 159&313; after: 102&278; still untouched: 1613
+{vertical mode: \batchmode}
+
+{\output}
+{blank space }
+{\setbox}
+{internal vertical mode: end-group character }}
+{vertical mode: blank space }
+{\dimen}
+{begin-group character {}
+{\output}
+{blank space }
+{\insert}
+{internal vertical mode: \def}
+{blank space }
+{\vskip}
+{\baselineskip}
+{\lineskip}
+{\vbox}
+{end-group character }}
+{\penalty}
+{\vbox}
+{end-group character }}
+{\penalty}
+{\vbox}
+{end-group character }}
+{\penalty}
+{end-group character }}
+{restoring \lineskip=0.0pt plus 40.0pt}
+{restoring \baselineskip=10.0pt plus 41.0pt}
+{restoring \box=\box}
+%% goal height=2000.0, max depth=2.0
+! Insertions can only be added to a vbox.
+l.125 }
+ % since \dimen100=803pt<3*267.7pt, the insertion ...
+Tut tut: You're trying to \insert into a
+\box register that now contains an \hbox.
+Proceed, and I'll discard its present contents.
+
+The following box has been deleted:
+\hbox(7.0+1.0)x2.0
+.\rip A
+
+! Infinite glue shrinkage inserted from \skip100.
+l.125 }
+ % since \dimen100=803pt<3*267.7pt, the insertion ...
+The correction glue for page breaking with insertions
+must have finite shrinkability. But you may proceed,
+since the offensive shrinkability has been made finite.
+
+% split100 to 803.0,267.7 p=-101
+{vertical mode: blank space }
+{\topskip}
+{\vbox}
+{internal vertical mode: end-group character }}
+% t=0.0 plus 3.0filll minus 9.0 g=1470.63013 b=0 p=0 c=-101#
+{vertical mode: blank space }
+{\insertpenalties}
+{\penalty}
+{\cleaders}
+{restricted horizontal mode: \lower}
+{internal vertical mode: end-group character }}
+{restricted horizontal mode: blank space }
+{\leaders}
+{\cleaders}
+{the letter A}
+{end-group character }}
+{\leaders}
+{the letter A}
+{end-group character }}
+{\xleaders}
+{the letter A}
+{end-group character }}
+{\write}
+{blank space }
+{end-group character }}
+{vertical mode: \mark}
+{blank space }
+{the letter A}
+% t=1050.0 plus 44.0 plus 3.0filll minus 19.0 g=1470.63013 b=0 p=0 c=-50
+{horizontal mode: the letter A}
+{\insert}
+{internal vertical mode: \baselineskip}
+{\splittopskip}
+{\hbox}
+{restricted horizontal mode: \vadjust}
+{internal vertical mode: \penalty}
+{end-group character }}
+{restricted horizontal mode: end-group character }}
+{internal vertical mode: \hbox}
+{restricted horizontal mode: end-group character }}
+{internal vertical mode: end-group character }}
+{restoring \splittopskip=1.0pt plus 43.0pt}
+{restoring \baselineskip=10.0pt plus 41.0pt}
+{horizontal mode: \showthe}
+> 1050.0pt.
+<everypar> ...howthe \pagetotal
+ \showthe \pagegoal \advance \...
+...
+l.140 A
+ AA\everypar=\errhelp % because of previous \everypar...
+
+{\showthe}
+> 1470.63013pt.
+<everypar> ...showthe \pagegoal
+ \advance \count 15by1\mark {\...
+...
+l.140 A
+ AA\everypar=\errhelp % because of previous \everypar...
+
+{\advance}
+{\mark}
+{\splitmaxdepth}
+{\par}
+@firstpass
+@secondpass
+[]\rip A[][]
+@\par via @@0 b=* p=-10000 d=*
+@@1: line 1.3- t=0 -> @@0
+
+
+Overfull \hbox (2.0pt too wide) in paragraph at lines 140--140
+[]\rip A|
+
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+
+{vertical mode: the letter A}
+% t=1060.0 plus 127.0 plus 8.0filll minus 27.0 g=1255.43756 b=0 p=0 c=-5
+0
+{horizontal mode: the letter A}
+{\insert}
+{internal vertical mode: \baselineskip}
+{\splittopskip}
+{\hbox}
+{restricted horizontal mode: \vadjust}
+{internal vertical mode: \penalty}
+{end-group character }}
+{restricted horizontal mode: end-group character }}
+{internal vertical mode: \hbox}
+{restricted horizontal mode: end-group character }}
+{internal vertical mode: end-group character }}
+{restoring \splittopskip=1.0pt plus 43.0pt}
+{restoring \baselineskip=10.0pt plus 41.0pt}
+{horizontal mode: \showthe}
+> 1061.0pt.
+<everypar> ...howthe \pagetotal
+ \showthe \pagegoal \advance \...
+...
+l.140 AA
+ A\everypar=\errhelp % because of previous \everypar...
+
+{\showthe}
+> 1255.43756pt.
+<everypar> ...showthe \pagegoal
+ \advance \count 15by1\mark {\...
+...
+l.140 AA
+ A\everypar=\errhelp % because of previous \everypar...
+
+{\advance}
+{\mark}
+{\splitmaxdepth}
+{\par}
+@firstpass
+@secondpass
+[]\rip A[][]
+@\par via @@0 b=* p=-10000 d=*
+@@1: line 1.3- t=0 -> @@0
+
+
+Overfull \hbox (2.0pt too wide) in paragraph at lines 140--140
+[]\rip A|
+
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+
+{vertical mode: the letter A}
+% t=1070.0 plus 210.0 plus 8.0filll minus 35.0 g=1055.44061 b=7 p=0 c=-4
+3
+{horizontal mode: the letter A}
+{\insert}
+{internal vertical mode: \baselineskip}
+{\splittopskip}
+{\hbox}
+{restricted horizontal mode: \vadjust}
+{internal vertical mode: \penalty}
+{end-group character }}
+{restricted horizontal mode: end-group character }}
+{internal vertical mode: \hbox}
+{restricted horizontal mode: end-group character }}
+{internal vertical mode: end-group character }}
+{restoring \splittopskip=1.0pt plus 43.0pt}
+{restoring \baselineskip=10.0pt plus 41.0pt}
+{horizontal mode: \showthe}
+> 1071.0pt.
+<everypar> ...howthe \pagetotal
+ \showthe \pagegoal \advance \...
+...
+l.140 AAA
+ \everypar=\errhelp % because of previous \everypar...
+
+{\showthe}
+> 1055.44061pt.
+<everypar> ...showthe \pagegoal
+ \advance \count 15by1\mark {\...
+...
+l.140 AAA
+ \everypar=\errhelp % because of previous \everypar...
+
+{\advance}
+{\mark}
+{\splitmaxdepth}
+{\par}
+@firstpass
+@secondpass
+[]\rip A[][]
+@\par via @@0 b=* p=-10000 d=*
+@@1: line 1.3- t=0 -> @@0
+
+
+Overfull \hbox (2.0pt too wide) in paragraph at lines 140--140
+[]\rip A|
+
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+
+% split200 to -51.11694,0.0 p=999
+{vertical mode: \everypar}
+{\kern}
+{the letter A}
+% t=1080.0 plus 293.0 plus 8.0filll minus 43.0 g=1055.44061 b=18 p=0 c=9
+67
+{horizontal mode: the letter A}
+{\hfill}
+{\vadjust}
+{internal vertical mode: \newlinechar}
+{\special}
+{\penalty}
+{end-group character }}
+{restoring \newlinechar=0}
+{horizontal mode: \penalty}
+{the letter A}
+{\par}
+@firstpass
+@secondpass
+[]\rip A
+@ via @@0 b=* p=0 d=*
+@@1: line 1.3 t=0 -> @@0
+[]
+@\penalty via @@1 b=0 p=-10000 d=*
+@@2: line 2.2 t=0 -> @@1
+A
+@\par via @@2 b=* p=-10000 d=*
+@@3: line 3.3- t=0 -> @@2
+
+
+Overfull \hbox (2.0pt too wide) in paragraph at lines 145--148
+[]\rip A|
+
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+
+
+Overfull \hbox (2.0pt too wide) in paragraph at lines 145--148
+\rip A|
+
+\hbox(7.0+1.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+
+% t=1040.0 plus 376.0 plus 8.0filll minus 51.0 g=1055.44061 b=0 p=0 c=94
+9
+% t=1050.0 plus 417.0 plus 8.0filll minus 51.0 g=1055.44061 b=0 p=-5000
+c=-4051#
+{vertical mode: \insert}
+{internal vertical mode: \vskip}
+{\floatingpenalty}
+{end-group character }}
+{restoring \floatingpenalty=100}
+{vertical mode: \pagefilstretch}
+{\showthe}
+> 952.
+l.149 ...showthe\insertpenalties
+ \penalty99999999\showlists
+
+{\penalty}
+{\showlists}
+
+### vertical mode entered at line 0
+### current page:
+\insert100, natural size 803.09999; split(1.0 plus 43.0,-2.0); float cos
+t 100
+.\glue 0.0 plus 1.0fil
+.\vbox(267.7+0.0)x0.0
+.\penalty -101
+.\glue(\lineskip) 0.0 minus 0.4
+.\vbox(267.7+0.0)x0.0
+.\penalty -100
+.\glue(\lineskip) 0.0 minus 0.4
+.\vbox(267.7+0.0)x0.0
+.\penalty -1000
+\glue(\topskip) 0.0 plus 44.0
+\vbox(1000.0+0.0)x0.0
+\penalty 12345
+\cleaders 50.0 minus 10.0
+.\hbox(15.0+2.0)x45.0
+..\vbox(17.0+0.0)x0.0, shifted 2.0
+..\glue 4.0 plus 2.0 minus 1.0
+..\leaders 10.0
+...\rule(0.4+0.0)x*
+..\cleaders 9.0
+...\hbox(7.0+1.0)x2.0
+....\rip A
+..\leaders 9.0
+...\hbox(7.0+1.0)x2.0
+....\rip A
+..\xleaders 9.0
+...\hbox(7.0+1.0)x2.0
+....\rip A
+..\write*{\help }
+..\glue 4.0 plus 2.0 minus 1.0
+\mark{alpha}
+\glue(\parskip) 0.0 plus 42.0 minus 8.0
+\glue(\baselineskip) 3.0 plus 41.0
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+\insert200, natural size 400.0; split(0.0,-2.0); float cost 100
+.\hbox(0.0+0.0)x0.0
+.\penalty 999
+.\glue(\baselineskip) 400.0
+.\hbox(0.0+0.0)x-10.0
+\mark{1}
+\glue(\parskip) 0.0 plus 42.0 minus 8.0
+\glue(\baselineskip) 2.0 plus 41.0
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+\insert200, natural size 400.0; split(1.0,-1.0); float cost 100
+.\hbox(0.0+0.0)x0.0
+.\penalty 999
+.\glue(\baselineskip) 400.0
+.\hbox(0.0+0.0)x-10.0
+\mark{2}
+\glue(\parskip) 0.0 plus 42.0 minus 8.0
+\glue(\baselineskip) 2.0 plus 41.0
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+\insert200, natural size 400.0; split(2.0,-1.0); float cost 100
+.\hbox(0.0+0.0)x0.0
+.\penalty 999
+.\glue(\baselineskip) 400.0
+.\hbox(0.0+0.0)x-10.0
+\mark{3}
+\kern -50.0
+\glue(\parskip) 0.0 plus 42.0 minus 8.0
+\glue(\baselineskip) 2.0 plus 41.0
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+\glue(\baselineskip) 9.0 plus 41.0
+\hbox(0.0+0.0)x0.0
+.\penalty -1000000000
+.\glue(\rightskip) 0.0
+\special{^^80-1000.0pt}
+\penalty -5000
+\glue(\baselineskip) 3.0 plus 41.0
+\hbox(7.0+1.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+\insert200, natural size 10000.0; split(1.0 plus 43.0,-1.0); float cost
+3
+.\glue 10000.0
+\penalty 99999999
+total height 1060.0 plus 458.0 plus -1.0fil plus 8.0filll minus 51.0
+ goal height 1055.44061
+\insert100 adds 535.36987, #1 might split
+\insert200 adds 405.19714, #3 might split
+prevdepth 1.0, prevgraf 3 lines
+
+! OK.
+<recently read> \showlists
+
+l.149 ...nalty99999999\showlists
+
+
+{\showthe}
+> 8.0pt.
+l.150 ...howthe\pagefilllstretch
+ \vskip 1000pt\penalty-333\hbo...
+
+{\vskip}
+{\penalty}
+% t=2061.0 plus 458.0 plus -1.0fil plus 8.0filll minus 51.0 g=1055.44061
+ b=* p=-333 c=*
+! \box255 is not void.
+<to be read again>
+ \hbox
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+You shouldn't use \box255 except in \output routines.
+Proceed, and I'll discard its present contents.
+
+The following box has been deleted:
+\vbox(0.0+0.0)x0.0
+
+{internal vertical mode: \dimen}
+{\count}
+{\global}
+{\ifnum}
+{true}
+{\global}
+{\unvbox}
+{\penalty}
+{\else}
+{\message}
+{\topmark}
+{\firstmark}
+{\botmark}
+{\splitfirstmark}
+{\splitbotmark}
+:alpha:3::
+{end-group character }}
+{restoring \count5=0}
+{restoring \dimen9=0.0pt}
+%% goal height=2000.0, max depth=2.0
+! Infinite glue shrinkage inserted from \skip100.
+<to be read again>
+ \hbox
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+The correction glue for page breaking with insertions
+must have finite shrinkability. But you may proceed,
+since the offensive shrinkability has been made finite.
+
+% split100 to 803.0,267.7 p=-101
+% t=0.0 plus 3.0filll minus 9.0 g=1470.63013 b=0 p=0 c=-101#
+% t=1050.0 plus 44.0 plus 3.0filll minus 19.0 g=1470.63013 b=0 p=0 c=-10
+1#
+% t=1060.0 plus 127.0 plus 8.0filll minus 27.0 g=1255.43756 b=0 p=0 c=-1
+01#
+% t=1070.0 plus 210.0 plus 8.0filll minus 35.0 g=1055.44061 b=7 p=0 c=-9
+4
+% split200 to -51.11694,0.0 p=999
+% t=1080.0 plus 293.0 plus 8.0filll minus 43.0 g=1055.44061 b=18 p=0 c=9
+16
+% t=1040.0 plus 376.0 plus 8.0filll minus 51.0 g=1055.44061 b=0 p=0 c=89
+8
+% t=1050.0 plus 417.0 plus 8.0filll minus 51.0 g=1055.44061 b=0 p=-5000
+c=-4102#
+% t=2061.0 plus 458.0 plus 8.0filll minus 51.0 g=1055.44061 b=* p=-333 c
+=*
+{\dimen}
+{\count}
+{\global}
+{\ifnum}
+{false}
+{\setbox}
+{\shipout}
+{restricted horizontal mode: \box}
+{\box}
+{\vsplit}
+
+Overfull \vbox (986.0pt too high) has occurred while \output is active
+\vbox(55.0+-1.0)x45.0, glue set - 1.0
+.\glue(\topskip) 0.0 plus 44.0
+.\vbox(1000.0+0.0)x0.0
+.\penalty 12345
+.\cleaders 50.0 minus 10.0
+..\hbox(15.0+2.0)x45.0
+...\vbox(17.0+0.0)x0.0, shifted 2.0
+...\glue 4.0 plus 2.0 minus 1.0
+...\leaders 10.0
+....\rule(0.4+0.0)x*
+...\cleaders 9.0
+....\hbox(7.0+1.0)x2.0
+.....\rip A
+...\leaders 9.0
+....\hbox(7.0+1.0)x2.0
+.....\rip A
+...\xleaders 9.0
+....\hbox(7.0+1.0)x2.0
+.....\rip A
+...\write*{\help }
+...\glue 4.0 plus 2.0 minus 1.0
+.\mark{alpha}
+
+{end-group character }}
+
+Completed box being shipped out [-5000.0.0.0.11.53110374]
+\hbox(810.4+0.0)x45.0
+.\vbox(267.7+0.0)x0.0
+..\glue 0.0 plus 1.0fil
+..\vbox(267.7+0.0)x0.0
+.\vbox(810.4+0.0)x0.0
+..\rule(0.4+0.0)x*
+..\glue 10.0 plus 5.0filll
+..\hbox(0.0+0.0)x0.0
+..\penalty 999
+..\glue(\baselineskip) 400.0
+..\hbox(0.0+0.0)x-10.0
+..\hbox(0.0+0.0)x0.0
+..\penalty 999
+..\glue(\baselineskip) 400.0
+..\hbox(0.0+0.0)x-10.0
+..\hbox(0.0+0.0)x0.0
+.\vbox(55.0+-1.0)x45.0, glue set - 1.0
+..\glue(\topskip) 0.0 plus 44.0
+..\vbox(1000.0+0.0)x0.0
+..\penalty 12345
+..\cleaders 50.0 minus 10.0
+...\hbox(15.0+2.0)x45.0
+....\vbox(17.0+0.0)x0.0, shifted 2.0
+....\glue 4.0 plus 2.0 minus 1.0
+....\leaders 10.0
+.....\rule(0.4+0.0)x*
+....\cleaders 9.0
+.....\hbox(7.0+1.0)x2.0
+......\rip A
+....\leaders 9.0
+.....\hbox(7.0+1.0)x2.0
+......\rip A
+....\xleaders 9.0
+.....\hbox(7.0+1.0)x2.0
+......\rip A
+....\write*{\help }
+....\glue 4.0 plus 2.0 minus 1.0
+..\mark{alpha}
+
+Memory usage before: 747&484; after: 581&481; still untouched: 1492
+{internal vertical mode: blank space }
+{\unvcopy}
+{\showlists}
+
+### internal vertical mode entered at line 150 (\output routine)
+\glue(\splittopskip) 0.0 plus 43.0
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+\mark{1}
+\glue(\parskip) 0.0 plus 42.0 minus 8.0
+\glue(\baselineskip) 2.0 plus 41.0
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+\mark{2}
+\glue(\parskip) 0.0 plus 42.0 minus 8.0
+\glue(\baselineskip) 2.0 plus 41.0
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+\mark{3}
+\kern -50.0
+\glue(\parskip) 0.0 plus 42.0 minus 8.0
+\glue(\baselineskip) 2.0 plus 41.0
+\hbox(7.0+1.0)x0.0
+.\hbox(0.0+0.0)x0.0
+.\rip A
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+\glue(\baselineskip) 9.0 plus 41.0
+\hbox(0.0+0.0)x0.0
+.\penalty -1000000000
+.\glue(\rightskip) 0.0
+\special{^^80-1000.0pt}
+prevdepth ignored
+### vertical mode entered at line 0
+### current page: (held over for next output)
+\insert100, natural size 535.4; split(1.0 plus 43.0,-2.0); float cost 10
+0
+.\glue(\splittopskip) 0.0 plus 43.0
+.\vbox(267.7+0.0)x0.0
+.\penalty -100
+.\glue(\lineskip) 0.0 minus 0.4
+.\vbox(267.7+0.0)x0.0
+.\penalty -1000
+\insert200, natural size 2.0; split(2.0,-1.0); float cost 100
+.\glue(\splittopskip) 2.0
+.\hbox(0.0+0.0)x-10.0
+### recent contributions:
+\penalty 10000
+\penalty 10000
+\glue(\baselineskip) 3.0 plus 41.0
+\hbox(7.0+1.0)x0.0
+.\rip A
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0
+.\rule(*+*)x5.0
+\insert200, natural size 10000.0; split(1.0 plus 43.0,-1.0); float cost
+3
+.\glue 10000.0
+\penalty 99999999
+\glue 1000.0
+\penalty -333
+prevdepth 1.0, prevgraf 3 lines
+
+! OK.
+<recently read> \showlists
+
+...
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+
+{\showthe}
+> 2.
+<output> ...he \insertpenalties
+ \showthe \pageshrink \globald...
+...
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+
+{\showthe}
+> 51.0pt.
+<output> ...showthe \pageshrink
+ \globaldefs 1\halign {##\tabs...
+...
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+
+{\globaldefs}
+{\halign}
+{blank space }
+{\showboxdepth}
+{\showboxbreadth}
+{\fi}
+{\message}
+{\topmark}
+{\firstmark}
+{\botmark}
+{\splitfirstmark}
+{\splitbotmark}
+3:alpha:3:alpha:alpha
+{end-group character }}
+{restoring \globaldefs=0}
+{restoring \box255=
+\vbox(1055.44061+0.0)x45.0, glue set 0.01305 []}
+{restoring \count5=0}
+{restoring \dimen9=0.0pt}
+! Output routine didn't use all of \box255.
+<to be read again>
+ \hbox
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+Your \output commands should empty \box255,
+e.g., by saying `\shipout\box255'.
+Proceed; I'll discard its present contents.
+
+The following box has been deleted:
+\vbox(1055.44061+0.0)x45.0, glue set 0.01305
+.\glue(\topskip) 0.0 plus 44.0
+.\vbox(1000.0+0.0)x0.0
+.etc.
+
+%% goal height=2000.0, max depth=2.0
+! Infinite glue shrinkage inserted from \skip100.
+<to be read again>
+ \hbox
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+The correction glue for page breaking with insertions
+must have finite shrinkability. But you may proceed,
+since the offensive shrinkability has been made finite.
+
+% t=0.0 plus 8.0filll minus 9.0 g=924.23029 b=0 p=0 c=0#
+% t=7.0 plus 44.0 plus 8.0filll minus 9.0 g=924.23029 b=0 p=0 c=0#
+% t=17.0 plus 127.0 plus 8.0filll minus 17.0 g=924.23029 b=0 p=0 c=0#
+% t=27.0 plus 210.0 plus 8.0filll minus 25.0 g=924.23029 b=0 p=0 c=0#
+% t=-13.0 plus 293.0 plus 8.0filll minus 33.0 g=924.23029 b=0 p=0 c=0#
+% split200 to 1832.4585,10000.0 p=-10000
+% t=1008.0 plus 375.0 plus 8.0filll minus 33.0 g=-4075.76971 b=* p=-333
+c=*
+{\dimen}
+{\count}
+{\global}
+{\ifnum}
+{false}
+{\setbox}
+{\shipout}
+{restricted horizontal mode: \box}
+{\box}
+{\vsplit}
+{end-group character }}
+
+Completed box being shipped out [10000.0.0.0.11.131072]
+\hbox(535.4+0.0)x0.0
+.\vbox(535.4+0.0)x0.0 []
+.\vbox(2.0+0.0)x0.0 []
+.etc.
+
+Memory usage before: 475&470; after: 290&466; still untouched: 1473
+{internal vertical mode: blank space }
+{\unvcopy}
+{\showlists}
+
+### internal vertical mode entered at line 150 (\output routine)
+prevdepth ignored
+### vertical mode entered at line 0
+### recent contributions:
+\glue(\baselineskip) 9.0 plus 41.0
+\hbox(0.0+0.0)x0.0
+.\penalty -1000000000
+.\glue(\rightskip) 0.0
+etc.
+prevdepth 1.0, prevgraf 3 lines
+
+! OK.
+<recently read> \showlists
+
+...
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+
+{\showthe}
+> 0.
+<output> ...he \insertpenalties
+ \showthe \pageshrink \globald...
+...
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+
+{\showthe}
+> 33.0pt.
+<output> ...showthe \pageshrink
+ \globaldefs 1\halign {##\tabs...
+...
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+
+{\globaldefs}
+{\halign}
+{blank space }
+{\showboxdepth}
+{\showboxbreadth}
+{\fi}
+{\message}
+{\topmark}
+{\firstmark}
+{\botmark}
+{\splitfirstmark}
+{\splitbotmark}
+3:1:3:1:3
+{end-group character }}
+{restoring \globaldefs=0}
+{restoring \box255=
+\vbox(924.23029+1.0)x0.0, glue set 3.19875 []}
+{restoring \count5=0}
+{restoring \dimen9=0.0pt}
+! Output routine didn't use all of \box255.
+<to be read again>
+ \hbox
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+Your \output commands should empty \box255,
+e.g., by saying `\shipout\box255'.
+Proceed; I'll discard its present contents.
+
+The following box has been deleted:
+\vbox(924.23029+1.0)x0.0, glue set 3.19875
+.\glue(\topskip) 0.0 plus 44.0
+.\hbox(7.0+1.0)x0.0 []
+.etc.
+
+%% goal height=2000.0, max depth=2.0
+% split200 to 3955.99365,10000.0 p=-10000
+% t=1012.0 plus 85.0 plus 5.0filll g=-3010.0 b=* p=-333 c=*#
+{\dimen}
+{\count}
+{\global}
+{\ifnum}
+{false}
+{\setbox}
+{\shipout}
+{restricted horizontal mode: \box}
+{\box}
+{\vsplit}
+
+Overfull \vbox (958.0pt too high) has occurred while \output is active
+\vbox(55.0+-1.0)x0.0
+.\glue(\topskip) 1.0 plus 44.0
+.\hbox(0.0+0.0)x0.0 []
+.etc.
+
+{end-group character }}
+
+Completed box being shipped out [-333.0.0.0.11.655360000]
+\hbox(10000.0+0.0)x0.0
+.\vbox(10000.0+0.0)x0.0 []
+.\vbox(55.0+-1.0)x0.0 []
+
+Memory usage before: 218&459; after: 149&458; still untouched: 1473
+{internal vertical mode: blank space }
+{\unvcopy}
+{\showlists}
+
+### internal vertical mode entered at line 150 (\output routine)
+prevdepth ignored
+### vertical mode entered at line 0
+### recent contributions:
+\penalty 10000
+prevdepth 1.0, prevgraf 3 lines
+
+! OK.
+<recently read> \showlists
+
+...
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+
+{\showthe}
+> 0.
+<output> ...he \insertpenalties
+ \showthe \pageshrink \globald...
+...
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+
+{\showthe}
+> 0.0pt.
+<output> ...showthe \pageshrink
+ \globaldefs 1\halign {##\tabs...
+...
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+
+{\globaldefs}
+{\halign}
+{blank space }
+{\showboxdepth}
+{\showboxbreadth}
+{\fi}
+{\message}
+{\topmark}
+{\firstmark}
+{\botmark}
+{\splitfirstmark}
+{\splitbotmark}
+3:3:3::
+{end-group character }}
+{restoring \globaldefs=0}
+{restoring \box255=
+\vbox(-3010.0+0.0)x0.0 []}
+{restoring \count5=0}
+{restoring \dimen9=0.0pt}
+! Output routine didn't use all of \box255.
+<to be read again>
+ \hbox
+l.150 ...1000pt\penalty-333\hbox
+ to 23pt{} % output now
+Your \output commands should empty \box255,
+e.g., by saying `\shipout\box255'.
+Proceed; I'll discard its present contents.
+
+The following box has been deleted:
+\vbox(-3010.0+0.0)x0.0
+.\glue(\topskip) 1.0 plus 44.0
+.\hbox(0.0+0.0)x0.0 []
+.etc.
+
+{vertical mode: \hbox}
+{restricted horizontal mode: end-group character }}
+%% goal height=2000.0, max depth=2.0
+{vertical mode: blank space }
+{\vsize}
+{\global}
+! Dimension too large.
+l.151 ...83.99999237060546875pt
+ % page size \approx infinity
+I can't work with sizes bigger than about 19 feet.
+Continue and I'll use the largest value I can.
+
+{end-group character }}
+{retaining \vsize=16383.99998pt}
+{restoring \everypar=A\insert 200{\baselineskip 400pt\ETC.}
+{restoring \splitmaxdepth=-2.0pt}
+{restoring \count15=0}
+{restoring \topskip=20.0pt plus 1.0fil}
+{restoring \output={\tracingcommands 0\showthe \outputpenalty \ETC.}
+{blank space }
+{begin-group character {}
+{\tracingoutput}
+{\tracingstats}
+{\shipout}
+{restricted horizontal mode: \closeout}
+{\closeout}
+{end-group character }}
+[-333.0.0.0.11]
+{vertical mode: end-group character }}
+{restoring \tracingstats=4}
+{restoring \tracingoutput=1}
+{blank space }
+{\showthe}
+> A\insert 200{\baselineskip 400pt\splittopskip \count 15pt\hbox {\vadju
+st {\penalty 999}}\hbox to -10pt{}}\showthe \pagetotal \showthe \pagegoa
+l \advance \count 15by1\mark {\the \count 15}\splitmaxdepth -1pt \paR \g
+obble .
+l.154 \showthe\everypar
+
+
+{\everypar}
+{\showthe}
+> .
+l.155 ...ypar{}\showthe\everypar
+
+
+{\def}
+{blank space }
+{\tracingmacros}
+{\def}
+{blank space }
+{\let}
+{\dimendef}
+{\dimen222}
+{\ifdim}
+{true}
+{\fi}
+{\ifdim}
+
+\t 12#101001{->-.#1pt{
+#1<-01001010
+{true}
+{begin-group character {}
+{\relax}
+{end-group character }}
+{\else}
+{\count0}
+{\ifodd}
+{true}
+{\advance}
+{\fi}
+{\penalty}
+% t=1.0 plus 44.0 g=2000.0 b=10000 p=-12345 c=-12345#
+{internal vertical mode: \tracingcommands}
+> -12345.
+<output> ...wthe \outputpenalty
+ \showboxbreadth 9999 \showbox...
+l.164 \penalty -12345
+ % output the remaining stuff
+
+
+Completed box being shipped out [-2.0.0.0.11]
+\vbox(2000.0+0.0)x23.0, glue set 45.43182
+.\glue(\topskip) 1.0 plus 44.0
+.\hbox(0.0+0.0)x23.0
+
+Memory usage before: 118&328; after: 102&328; still untouched: 1473
+{restoring \box254=void}
+{restoring \hoffset=0.0pt}
+{restoring \showboxdepth=1}
+{restoring \showboxbreadth=2}
+{restoring \tracingcommands=2}
+{vertical mode: \tracingmacros}
+{\adjdemerits}
+{\linepenalty}
+{\def}
+{blank space }
+{\valign}
+{horizontal mode: \valign}
+{restricted horizontal mode: \spacefactor}
+{\global}
+{end-group character }}
+{blank space }
+{\vrule}
+{begin-group character {}
+{blank space }
+{end-group character }}
+{end-group character }}
+{internal vertical mode: \baselineskip}
+{\global}
+{\global}
+{\rightskip}
+{\global}
+{\setbox}
+
+\1#1->\hbox to#1pt{}
+#1<-2
+{restricted horizontal mode: end-group character }}
+{internal vertical mode: blank space }
+{\noindent}
+{horizontal mode: \copy}
+{\hskip}
+{\copy}
+{\hskip}
+{\lower}
+
+\1#1->\hbox to#1pt{}
+#1<-1
+{restricted horizontal mode: end-group character }}
+{horizontal mode: \hskip}
+{\copy}
+{\hskip}
+{\box}
+{end of alignment template}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+[]
+@ via @@0 b=51 p=0 d=2704
+@ via @@1 b=10000 p=0 d=100000000
+@@2: line 1.1 t=2704 -> @@0
+[]
+@ via @@0 b=30 p=0 d=961
+@ via @@1 b=10000 p=0 d=100000000
+@ via @@2 b=10000 p=0 d=100000000
+@@3: line 1.3 t=961 -> @@0
+[]
+@ via @@2 b=10000 p=0 d=100000000
+@ via @@3 b=10000 p=0 d=100000782
+@@4: line 2.0 t=100001743 -> @@3
+[]
+@\par via @@2 b=0 p=-10000 d=1
+@\par via @@3 b=30 p=-10000 d=1743
+@\par via @@4 b=10000 p=-10000 d=100000000
+@@5: line 2.1- t=2704 -> @@3
+@@6: line 2.2- t=2705 -> @@2
+
+
+Tight \hbox (badness 30) in paragraph at lines 183--183
+ [] [] []
+
+\hbox(0.0+2.0)x13.0, glue set - 0.66667
+.\glue(\leftskip) 4.0
+.\hbox(0.0+0.0)x2.0
+.etc.
+
+
+Loose \hbox (badness 30) in paragraph at lines 183--183
+ [] []
+
+\hbox(0.0+0.0)x13.0, glue set 0.66667
+.\glue(\leftskip) 4.0
+.\hbox(0.0+0.0)x2.0
+.etc.
+
+{restoring \box2=void}
+{restoring \rightskip=0.0pt}
+{restoring \baselineskip=10.0pt plus 41.0pt}
+{internal vertical mode: \baselineskip}
+{\global}
+{\global}
+{\rightskip}
+{\global}
+{\setbox}
+
+\1#1->\hbox to#1pt{}
+#1<-2
+{restricted horizontal mode: end-group character }}
+{internal vertical mode: blank space }
+{\noindent}
+{horizontal mode: \copy}
+{\hskip}
+{\copy}
+{\hskip}
+{\lower}
+
+\1#1->\hbox to#1pt{}
+#1<-1
+{restricted horizontal mode: end-group character }}
+{horizontal mode: \hskip}
+{\copy}
+{\hskip}
+{\box}
+{\adjdemerits}
+{end of alignment template}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000784
+@@1: line 1.0 t=100000784 -> @@0
+[]
+@ via @@0 b=51 p=0 d=2704
+@ via @@1 b=10000 p=0 d=100000000
+@@2: line 1.1 t=2704 -> @@0
+[]
+@ via @@0 b=30 p=0 d=961
+@ via @@1 b=10000 p=0 d=100000000
+@ via @@2 b=10000 p=0 d=100000000
+@@3: line 1.3 t=961 -> @@0
+[]
+@ via @@2 b=10000 p=0 d=100000000
+@ via @@3 b=10000 p=0 d=100000784
+@@4: line 2.0 t=100001745 -> @@3
+[]
+@\par via @@2 b=0 p=-10000 d=1
+@\par via @@3 b=30 p=-10000 d=1745
+@\par via @@4 b=10000 p=-10000 d=100000000
+@@5: line 2.1- t=2706 -> @@3
+@@6: line 2.2- t=2705 -> @@2
+
+
+Loose \hbox (badness 51) in paragraph at lines 184--184
+ [] []
+
+\hbox(0.0+0.0)x13.0, glue set 0.8
+.\glue(\leftskip) 4.0
+.\hbox(0.0+0.0)x2.0
+.etc.
+
+{restoring \adjdemerits=782}
+{restoring \box2=void}
+{restoring \rightskip=0.0pt}
+{restoring \baselineskip=10.0pt plus 41.0pt}
+{internal vertical mode: \baselineskip}
+{\global}
+{\global}
+{\rightskip}
+{\global}
+{\setbox}
+
+\1#1->\hbox to#1pt{}
+#1<-2
+{restricted horizontal mode: end-group character }}
+{internal vertical mode: blank space }
+{\noindent}
+{horizontal mode: \copy}
+{\hskip}
+{\copy}
+{\hskip}
+{\lower}
+
+\1#1->\hbox to#1pt{}
+#1<-1
+{restricted horizontal mode: end-group character }}
+{horizontal mode: \hskip}
+{\copy}
+{\hskip}
+{\box}
+{\linepenalty}
+{\hbadness}
+{end of alignment template}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+[]
+@ via @@0 b=51 p=0 d=2809
+@ via @@1 b=10000 p=0 d=100000000
+@@2: line 1.1 t=2809 -> @@0
+[]
+@ via @@0 b=30 p=0 d=1024
+@ via @@1 b=10000 p=0 d=100000000
+@ via @@2 b=10000 p=0 d=100000000
+@@3: line 1.3 t=1024 -> @@0
+[]
+@ via @@2 b=10000 p=0 d=100000000
+@ via @@3 b=10000 p=0 d=100000782
+@@4: line 2.0 t=100001806 -> @@3
+[]
+@\par via @@2 b=0 p=-10000 d=4
+@\par via @@3 b=30 p=-10000 d=1806
+@\par via @@4 b=10000 p=-10000 d=100000000
+@@5: line 2.1- t=2830 -> @@3
+@@6: line 2.2- t=2813 -> @@2
+
+{restoring \hbadness=0}
+{restoring \linepenalty=1}
+{restoring \box2=void}
+{restoring \rightskip=0.0pt}
+{restoring \baselineskip=10.0pt plus 41.0pt}
+{restricted horizontal mode: blank space }
+{\spacefactor}
+{end-group character }}
+{horizontal mode: \message}
+1
+{blank space }
+{begin-group character {}
+{\hsize}
+{\par}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+|
+@ via @@0 b=10000 p=0 d=100000782
+@ via @@1 b=10000 p=0 d=100000000
+@@2: line 1.0 t=100000782 -> @@0
+[][][]
+@ via @@0 b=10000 p=0 d=100000782
+@ via @@1 b=10000 p=0 d=100000000
+@ via @@2 b=10000 p=0 d=100000000
+@@3: line 1.0 t=100000782 -> @@0
+@\par via @@0 b=10000 p=-10000 d=100000782
+@\par via @@1 b=10000 p=-10000 d=100000000
+@\par via @@2 b=10000 p=-10000 d=100000000
+@\par via @@3 b=10000 p=-10000 d=100000000
+@@4: line 1.0- t=100000782 -> @@0
+
+
+Underfull \hbox (badness 10000) in paragraph at lines 171--187
+ [] | [][][]
+
+\hbox(22.0+0.0)x1000.0, glue set 237.5
+.\glue(\leftskip) 4.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+%% goal height=16383.99998, max depth=2.0
+{vertical mode: \parindent}
+{\indent}
+% t=22.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+{horizontal mode: end-group character }}
+{restoring \parindent=0.0pt}
+{restoring \hsize=13.0pt}
+{\leftskip}
+{\def}
+{\noindent}
+{\indent}
+{\hbox}
+{restricted horizontal mode: \hskip}
+{end-group character }}
+
+Underfull \hbox (badness 10000) detected at line 188
+
+
+\hbox(0.0+0.0)x2.0, glue set -1.99255
+.\glue 0.0 plus -1.00374
+
+{horizontal mode: \discretionary}
+
+\?->\vrule width-2pt \hbox spread2pt{}
+{restricted horizontal mode: \vrule}
+{\hbox}
+{end-group character }}
+{the letter A}
+{end-group character }}
+
+\?->\vrule width-2pt \hbox spread2pt{}
+{\vrule}
+{\hbox}
+{end-group character }}
+{the letter B}
+{end-group character }}
+
+\?->\vrule width-2pt \hbox spread2pt{}
+{\vrule}
+{\hbox}
+{end-group character }}
+{the character /}
+{\kern}
+{end-group character }}
+{horizontal mode: \unkern}
+{\showthe}
+> 2.0pt.
+l.190 \showthe\lastkern
+ \vbox{\hrule width 6pt} \par % should ...
+
+{\vbox}
+{internal vertical mode: \hrule}
+{end-group character }}
+{horizontal mode: blank space }
+{\par}
+@firstpass
+[][][]|[]\rip AAAB|[]B-
+@\discretionary via @@0 b=0 p=88 d=7745
+@@1: line 1.2- t=7745 -> @@0
+[]
+@\par via @@1 b=0 p=-10000 d=100001
+@@2: line 2.2- t=107746 -> @@1
+
+% t=32.0 plus 83.0 plus 1.0fil minus 8.0 g=16383.99998 b=0 p=162 c=162
+{vertical mode: \penalty}
+% t=42.0 plus 124.0 plus 1.0fil minus 8.0 g=16383.99998 b=0 p=-22222 c=-
+22222#
+\output->{\tracingcommands 0\showthe \outputpenalty \showboxbreadth 9999
+ \showboxdepth 9999 \hoffset 1sp {\setbox 254=\box 255\shipout \ifvbox 2
+\ifhbox 254 \error \fi 54\copy 25\fi 4} \ifvoid 254\relax \else \error \
+fi }
+{internal vertical mode: \tracingcommands}
+> -22222.
+<output> ...wthe \outputpenalty
+ \showboxbreadth 9999 \showbox...
+l.191 \penalty-22222
+ % end of demerits test, hyphenation is next
+
+
+Completed box being shipped out [-2.0.0.0.11]
+\vbox(16383.99998+0.0)x1000.0, glue set 16342.0fil
+.\glue(\topskip) 0.0 plus 1.0fil
+.\hbox(22.0+0.0)x1000.0, glue set 237.5
+..\glue(\leftskip) 4.0
+..\hbox(0.0+0.0)x0.0
+..\glue(\xspaceskip) -1.0
+..\rule(22.0+0.0)x0.0
+..\glue 4.0 plus 2.0 minus 1.0
+..\vbox(22.0+0.0)x13.0
+...\glue(\tabskip) 0.0 plus 40.0
+...\vbox(22.0+0.0)x13.0, glue set 2.0
+....\hbox(0.0+2.0)x13.0, glue set - 0.66667
+.....\glue(\leftskip) 4.0
+.....\hbox(0.0+0.0)x2.0
+.....\glue 2.0 plus 5.0 minus 1.0
+.....\hbox(0.0+0.0)x2.0
+.....\glue 5.0 minus 2.0
+.....\hbox(0.0+0.0)x1.0, shifted 2.0
+.....\glue(\rightskip) -1.0
+....\penalty 125
+....\glue(\baselineskip) 18.0 plus 1.0
+....\hbox(0.0+0.0)x13.0, glue set 0.66667
+.....\glue(\leftskip) 4.0
+.....\hbox(0.0+0.0)x2.0
+.....\glue 2.0 plus 6.0
+.....\hbox(0.0+0.0)x2.0
+.....\penalty 10000
+.....\glue(\parfillskip) 0.0
+.....\glue(\rightskip) -1.0
+...\glue(\tabskip) 0.0 plus 40.0
+..\vbox(22.0+0.0)x13.0
+...\glue(\tabskip) 0.0 plus 40.0
+...\vbox(22.0+0.0)x13.0
+....\hbox(0.0+0.0)x13.0, glue set 0.8
+.....\glue(\leftskip) 4.0
+.....\hbox(0.0+0.0)x2.0
+.....\glue 2.0 plus 5.0 minus 1.0
+.....\hbox(0.0+0.0)x2.0
+.....\glue(\rightskip) -1.0
+....\penalty 125
+....\glue(\baselineskip) 20.0 plus 1.0
+....\hbox(0.0+2.0)x13.0
+.....\glue(\leftskip) 4.0
+.....\hbox(0.0+0.0)x1.0, shifted 2.0
+.....\glue 3.0
+.....\hbox(0.0+0.0)x2.0
+.....\glue 2.0 plus 6.0
+.....\hbox(0.0+0.0)x2.0
+.....\penalty 10000
+.....\glue(\parfillskip) 0.0
+.....\glue(\rightskip) -1.0
+...\glue(\tabskip) 0.0 plus 40.0
+..\vbox(22.0+0.0)x13.0
+...\glue(\tabskip) 0.0 plus 40.0
+...\vbox(22.0+0.0)x13.0
+....\hbox(0.0+0.0)x13.0, glue set 0.8
+.....\glue(\leftskip) 4.0
+.....\hbox(0.0+0.0)x2.0
+.....\glue 2.0 plus 5.0 minus 1.0
+.....\hbox(0.0+0.0)x2.0
+.....\glue(\rightskip) -1.0
+....\penalty 125
+....\glue(\baselineskip) 20.0 plus 1.0
+....\hbox(0.0+2.0)x13.0
+.....\glue(\leftskip) 4.0
+.....\hbox(0.0+0.0)x1.0, shifted 2.0
+.....\glue 3.0
+.....\hbox(0.0+0.0)x2.0
+.....\glue 2.0 plus 6.0
+.....\hbox(0.0+0.0)x2.0
+.....\penalty 10000
+.....\glue(\parfillskip) 0.0
+.....\glue(\rightskip) -1.0
+...\glue(\tabskip) 0.0 plus 40.0
+..\glue 4.0 plus 2.0 minus 1.0
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0
+.\glue(\parskip) 0.0 plus 42.0 minus 8.0
+.\glue(\baselineskip) 2.0 plus 41.0
+.\hbox(8.0+2.0)x13.0
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x1.0
+..\hbox(0.0+0.0)x0.0
+..\hbox(0.0+0.0)x2.0, glue set -1.99255
+...\glue 0.0 plus -1.00374
+..\discretionary
+..\rule(*+*)x-2.0
+..\hbox(0.0+0.0)x2.0
+..\rip A (ligature AAA)
+..\kern2.0
+..\rip B
+..\glue(\rightskip) 0.0
+.\penalty 162
+.\glue(\baselineskip) 7.6 plus 41.0
+.\hbox(0.4+0.0)x13.0
+..\glue(\leftskip) 3.0
+..\rule(*+*)x-2.0
+..\hbox(0.0+0.0)x2.0
+..\rip C (ligature B-)
+..\vbox(0.4+0.0)x6.0
+...\rule(0.4+0.0)x6.0
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0
+
+Memory usage before: 990&376; after: 584&370; still untouched: 735
+{restoring \box254=void}
+{restoring \hoffset=0.0pt}
+{restoring \showboxdepth=1}
+{restoring \showboxbreadth=2}
+{restoring \tracingcommands=2}
+{vertical mode: \looseness}
+{\uchyph}
+{\hsize}
+{the letter A}
+{horizontal mode: the letter A}
+{blank space }
+{the character /}
+{\vadjust}
+{internal vertical mode: \uchyph}
+{\ }
+{horizontal mode: \ }
+{the letter B}
+{end-group character }}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+\rip BBBBBB
+@\par via @@0 b=10000 p=-10000 d=100000782
+@\par via @@1 b=10000 p=-10000 d=100000000
+@@2: line 1.0- t=100000782 -> @@0
+
+
+Underfull \hbox (badness 10000) in paragraph at lines 200--200
+ [] \rip BBBBBB
+
+\hbox(7.0+1.0)x100.0, glue set 41.5
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{restoring \uchyph=1}
+{restoring \looseness=-10}
+{\vadjust}
+{internal vertical mode: \ }
+{horizontal mode: \ }
+{\closeout}
+{the letter B}
+{end-group character }}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+[]\rip BB-
+@\discretionary via @@0 b=10000 p=88 d=100008526
+@\discretionary via @@1 b=10000 p=88 d=100007744
+@@2: line 1.0- t=100008526 -> @@0
+B-BBB
+@\discretionary via @@0 b=10000 p=88 d=100008526
+@\discretionary via @@1 b=10000 p=88 d=100007744
+@\discretionary via @@2 b=10000 p=88 d=100008744
+@@3: line 1.0- t=100008526 -> @@0
+@\par via @@0 b=10000 p=-10000 d=100000782
+@\par via @@1 b=10000 p=-10000 d=100000000
+@\par via @@2 b=10000 p=-10000 d=100100000
+@\par via @@3 b=10000 p=-10000 d=100100000
+@@4: line 1.0- t=100000782 -> @@0
+
+
+Underfull \hbox (badness 10000) in paragraph at lines 201--201
+ [] []\rip BB-B-BBB
+
+\hbox(7.0+1.0)x100.0, glue set 41.5
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{restoring \looseness=-10}
+{begin-group character {}
+{\hyphenchar}
+{end-group character }}
+{\hyphenation}
+{\vadjust}
+{internal vertical mode: \ }
+{horizontal mode: \ }
+{the letter B}
+{\kern}
+{the letter B}
+{end-group character }}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+\rip BBBCBBB
+@\discretionary via @@0 b=10000 p=88 d=100008526
+@\discretionary via @@1 b=10000 p=88 d=100007744
+@@2: line 1.0- t=100008526 -> @@0
+B
+@\par via @@0 b=10000 p=-10000 d=100000782
+@\par via @@1 b=10000 p=-10000 d=100000000
+@\par via @@2 b=10000 p=-10000 d=100100000
+@@3: line 1.0- t=100000782 -> @@0
+
+
+Underfull \hbox (badness 10000) in paragraph at lines 202--202
+ [] \rip BBBCBBBB
+
+\hbox(8.0+2.0)x100.0, glue set 40.0
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{restoring \looseness=-10}
+{\hyphenchar}
+{\vadjust}
+{internal vertical mode: \def}
+{\ }
+{horizontal mode: \ }
+{\pretolerance}
+{the letter B}
+
+\B ->B
+{end-group character }}
+@firstpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+\rip BBBBBB
+@\par via @@0 b=10000 p=-10000 d=100000782
+@\par via @@1 b=10000 p=-10000 d=100000000
+@@2: line 1.0- t=100000782 -> @@0
+
+
+Underfull \hbox (badness 10000) in paragraph at lines 203--203
+ [] \rip BBBBBB
+
+\hbox(7.0+1.0)x100.0, glue set 41.5
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{restoring \pretolerance=0}
+{restoring \B=undefined}
+{restoring \looseness=-10}
+{blank space }
+{\par}
+@firstpass
+@secondpass
+[]\rip A
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+/AA-
+@\discretionary via @@0 b=10000 p=88 d=100008526
+@@2: line 1.0- t=100008526 -> @@0
+@\discretionary via @@1 b=10000 p=88 d=100007744
+@@3: line 2.0- t=200008526 -> @@1
+B-BBB
+@\discretionary via @@0 b=10000 p=88 d=100008526
+@@4: line 1.0- t=100008526 -> @@0
+@\discretionary via @@2 b=10000 p=88 d=100008744
+@\discretionary via @@1 b=10000 p=88 d=100007744
+@@5: line 2.0- t=200008526 -> @@1
+@\discretionary via @@3 b=10000 p=88 d=100008744
+@@6: line 3.0- t=300017270 -> @@3
+-
+@\discretionary via @@0 b=10000 p=88 d=100008526
+@@7: line 1.0- t=100008526 -> @@0
+@\discretionary via @@4 b=10000 p=88 d=100008744
+@\discretionary via @@2 b=10000 p=88 d=100008744
+@\discretionary via @@1 b=10000 p=88 d=100007744
+@@8: line 2.0- t=200008526 -> @@1
+@\discretionary via @@5 b=10000 p=88 d=100008744
+@\discretionary via @@3 b=10000 p=88 d=100008744
+@@9: line 3.0- t=300017270 -> @@3
+@\discretionary via @@6 b=10000 p=88 d=100008744
+@@10: line 4.0- t=400026014 -> @@6
+C-A
+@\discretionary via @@0 b=10000 p=88 d=100008526
+@@11: line 1.0- t=100008526 -> @@0
+@\discretionary via @@7 b=10000 p=88 d=100008744
+@\discretionary via @@4 b=10000 p=88 d=100008744
+@\discretionary via @@2 b=10000 p=88 d=100008744
+@\discretionary via @@1 b=10000 p=88 d=100007744
+@@12: line 2.0- t=200008526 -> @@1
+@\discretionary via @@8 b=10000 p=88 d=100008744
+@\discretionary via @@5 b=10000 p=88 d=100008744
+@\discretionary via @@3 b=10000 p=88 d=100008744
+@@13: line 3.0- t=300017270 -> @@3
+@\discretionary via @@9 b=10000 p=88 d=100008744
+@\discretionary via @@6 b=10000 p=88 d=100008744
+@@14: line 4.0- t=400026014 -> @@6
+@\discretionary via @@10 b=10000 p=88 d=100008744
+@@15: line 5.0- t=500034758 -> @@10
+CAC//[][][][]
+@\par via @@0 b=10000 p=-10000 d=100000782
+@@16: line 1.0- t=100000782 -> @@0
+@\par via @@11 b=10000 p=-10000 d=100100000
+@\par via @@7 b=10000 p=-10000 d=100100000
+@\par via @@4 b=10000 p=-10000 d=100100000
+@\par via @@2 b=10000 p=-10000 d=100100000
+@\par via @@1 b=10000 p=-10000 d=100000000
+@@17: line 2.0- t=200000782 -> @@1
+@\par via @@12 b=10000 p=-10000 d=100100000
+@\par via @@8 b=10000 p=-10000 d=100100000
+@\par via @@5 b=10000 p=-10000 d=100100000
+@\par via @@3 b=10000 p=-10000 d=100100000
+@@18: line 3.0- t=300108526 -> @@3
+@\par via @@13 b=10000 p=-10000 d=100100000
+@\par via @@9 b=10000 p=-10000 d=100100000
+@\par via @@6 b=10000 p=-10000 d=100100000
+@@19: line 4.0- t=400117270 -> @@6
+@\par via @@14 b=10000 p=-10000 d=100100000
+@\par via @@10 b=10000 p=-10000 d=100100000
+@@20: line 5.0- t=500126014 -> @@10
+@\par via @@15 b=10000 p=-10000 d=100100000
+@@21: line 6.0- t=600134758 -> @@15
+
+
+Underfull \hbox (badness 10000) in paragraph at lines 195--204
+ []\rip A /AA-B-BBB-C-ACAC//
+
+\hbox(8.0+2.0)x100.0, glue set 33.53377
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+%% goal height=16383.99998, max depth=2.0
+{vertical mode: \hbox}
+{restricted horizontal mode: \sfcode}
+{the letter A}
+{blank space }
+{the letter a}
+{blank space }
+{end-group character }}
+{restoring \sfcode66=999}
+% t=55.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+{vertical mode: \noindent}
+% t=64.0 plus 41.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+{horizontal mode: \scriptscriptfont}
+{math shift character $}
+{display math mode: \eqno}
+{math mode: superscript character ^}
+{end-group character }}
+{math shift character $}
+! Math formula deleted: Insufficient extension fonts.
+l.206 $$\eqno^{}$
+ \scriptfont3=\rip\fontdimen2\smalltrip=0pt
+Sorry, but I can't typeset math unless \textfont 3
+and \scriptfont 3 and \scriptscriptfont 3 have all
+the \fontdimen values needed in math extension fonts.
+
+! Display math should end with $$.
+<to be read again>
+ \scriptfont
+l.206 $$\eqno^{}$\scriptfont
+ 3=\rip\fontdimen2\smalltrip=0pt
+The `$' that I just saw supposedly matches a previous `$$'.
+So I shall assume that you typed `$$' both times.
+
+{restoring \fam=-1}
+! Math formula deleted: Insufficient extension fonts.
+<to be read again>
+ \scriptfont
+l.206 $$\eqno^{}$\scriptfont
+ 3=\rip\fontdimen2\smalltrip=0pt
+Sorry, but I can't typeset math unless \textfont 3
+and \scriptfont 3 and \scriptscriptfont 3 have all
+the \fontdimen values needed in math extension fonts.
+
+{restoring \displayindent=0.0pt}
+{restoring \displaywidth=0.0pt}
+{restoring \predisplaysize=0.0pt}
+{restoring \fam=0}
+% t=66.0 plus 83.0 plus 1.0fil minus 8.0 g=16383.99998 b=0 p=0 c=0#
+% t=85.0 plus 210.0 plus 1.0fil minus 811.0 g=16383.99998 b=0 p=0 c=0#
+{horizontal mode: \scriptfont}
+{\fontdimen}
+{begin-group character {}
+{\rightskip}
+{\looseness}
+{\spaceskip}
+{the letter A}
+{\spacefactor}
+{\discretionary}
+{restricted horizontal mode: end-group character }}
+{\kern}
+{the character -}
+{end-group character }}
+{the letter B}
+{\kern}
+{end-group character }}
+{horizontal mode: blank space }
+{the letter C}
+{math shift character $}
+{math mode: blank space }
+{\scriptfont}
+{\mathsurround}
+{math shift character $}
+! Math formula deleted: Insufficient symbol fonts.
+<recently read> $
+
+l.210 \mathsurround143pt$
+ C $\mathsurround40pt$$\mathsurrou...
+Sorry, but I can't typeset math unless \textfont 2
+and \scriptfont 2 and \scriptscriptfont 2 have all
+the \fontdimen values needed in math symbol fonts.
+
+{restoring \mathsurround=0.0pt}
+{restoring \scriptfont2=\smalltrip}
+{restoring \fam=0}
+{horizontal mode: blank space }
+{the letter C}
+{blank space }
+{math shift character $}
+{math mode: \mathsurround}
+{math shift character $}
+{restoring \mathsurround=0.0pt}
+{restoring \fam=0}
+{horizontal mode: math shift character $}
+{math mode: \mathsurround}
+{\hbox}
+{restricted horizontal mode: math shift character $}
+{math mode: math shift character $}
+{restoring \fam=-1}
+{restricted horizontal mode: end-group character }}
+{math mode: math shift character $}
+{restoring \mathsurround=0.0pt}
+{restoring \fam=0}
+{horizontal mode: \par}
+! Infinite glue shrinkage found in a paragraph.
+l.210 ...round60pt\hbox{$$}$\par
+ }
+The paragraph just ended includes some glue that has
+infinite shrinkability, e.g., `\hskip 0pt minus 1fil'.
+Such glue doesn't belong there---it allows a paragraph
+of any length to fit on one line. But it's safe to proceed,
+since the offensive shrinkability has been made finite.
+
+@firstpass
+@secondpass
+\rip A-
+@\discretionary via @@0 b=76 p=89 d=13850
+@@1: line 4.1- t=13850 -> @@0
+
+@ via @@0 b=65 p=0 d=4356
+@@2: line 4.1 t=4356 -> @@0
+@ via @@1 b=91 p=0 d=8464
+@@3: line 5.1 t=22314 -> @@1
+C$$
+@\math via @@0 b=17 p=0 d=324
+@@4: line 4.3 t=324 -> @@0
+@\math via @@2 b=12 p=0 d=169
+@\math via @@1 b=9 p=0 d=100
+@@5: line 5.2 t=4525 -> @@2
+@\math via @@3 b=12 p=0 d=169
+@@6: line 6.2 t=22483 -> @@3
+ C
+@ via @@4 b=71 p=0 d=5966
+@@7: line 5.1 t=6290 -> @@4
+@ via @@5 b=71 p=0 d=5184
+@@8: line 6.1 t=9709 -> @@5
+@ via @@6 b=71 p=0 d=5184
+@@9: line 7.1 t=27667 -> @@6
+$$$[]$
+@\par via @@7 b=57 p=-10000 d=4146
+@@10: line 6.3- t=10436 -> @@7
+@\par via @@8 b=57 p=-10000 d=4146
+@@11: line 7.3- t=13855 -> @@8
+@\par via @@9 b=57 p=-10000 d=4146
+@@12: line 8.3- t=31813 -> @@9
+
+
+Loose \hbox (badness 76) in paragraph at lines 206--210
+ \rip A
+
+\hbox(7.0+1.0)x100.0, glue set 0.91347
+.\glue(\leftskip) 3.0
+.\rip A
+.etc.
+
+
+Loose \hbox (badness 91) in paragraph at lines 206--210
+ \rip -
+
+\hbox(0.0+0.0)x100.0, glue set 0.97116
+.\glue(\leftskip) 3.0
+.\kern 2.0
+.etc.
+
+
+Tight \hbox (badness 12) in paragraph at lines 206--210
+ \rip C$$
+
+\hbox(0.0+0.0)x100.0, glue set - 0.5
+.\glue(\leftskip) 3.0
+.\rip C
+.etc.
+
+
+Loose \hbox (badness 71) in paragraph at lines 206--210
+ \rip C
+
+\hbox(0.0+0.0)x100.0, glue set 0.89423
+.\glue(\leftskip) 3.0
+.\rip C
+.etc.
+
+
+Tight \hbox (badness 57) in paragraph at lines 206--210
+ []$
+
+\hbox(0.0+0.0)x100.0, glue set - 0.83
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x120.0 []
+.etc.
+
+% t=95.0 plus 251.0 plus 1.0fil minus 811.0 g=16383.99998 b=0 p=37 c=37
+% t=105.0 plus 292.0 plus 1.0fil minus 811.0 g=16383.99998 b=0 p=-125 c=
+-125#
+% t=115.0 plus 333.0 plus 1.0fil minus 811.0 g=16383.99998 b=0 p=-125 c=
+-125#
+% t=125.0 plus 374.0 plus 1.0fil minus 811.0 g=16383.99998 b=0 p=0 c=0
+{vertical mode: end-group character }}
+{restoring \spaceskip=0.0pt}
+{restoring \looseness=0}
+{restoring \rightskip=0.0pt}
+{blank space }
+{\uccode}
+{\font}
+{\input}
+! Font \mumble=mumble not loadable: Metric (TFM) file not found.
+<to be read again>
+ \relax
+...
+l.211 ...ont\mumble=mumble\input
+ tripos % "AAAAAAAAAA"+errors
+I wasn't able to read the size data for this font,
+so I will ignore the font specification.
+[Wizards can fix TFM files using TFtoPL/PLtoTF.]
+You might try inserting a different font spec;
+e.g., type `I\font<same font id>=<substitute font name>'.
+
+{\relax}
+{\input}
+(tripos.tex
+{\par}
+{\uppercase}
+{the character 0}
+% t=135.0 plus 415.0 plus 1.0fil minus 811.0 g=16383.99998 b=0 p=0 c=0
+{horizontal mode: the character 0}
+Missing character: There is no 0 in font trip!
+{begin-group character {}
+{\outputpenalty}
+! Missing number, treated as zero.
+<to be read again>
+ }
+l.2 ...case {0{\outputpenalty }}
+
+A number should have been here; I inserted `0'.
+(If you can't figure out why I needed to see a number,
+look up `weird error' in the index to The TeXbook.)
+
+{end-group character }}
+{restoring \outputpenalty=-22222}
+{blank space }
+{the character [}
+Missing character: There is no [ in font trip!
+{\uppercase}
+{the letter A}
+Missing character: There is no [ in font trip!
+{blank space }
+)
+{\par}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+\rip AAAAAAAAAA
+@\par via @@0 b=10000 p=-10000 d=100000782
+@\par via @@1 b=10000 p=-10000 d=100000000
+@@2: line 1.0- t=100000782 -> @@0
+
+
+Underfull \hbox (badness 10000) in paragraph at lines 2--212
+ [] \rip AAAAAAAAAA
+
+\hbox(7.0+1.0)x100.0, glue set 45.5
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{vertical mode: \penalty}
+% t=145.0 plus 498.0 plus 1.0fil minus 819.0 g=16383.99998 b=0 p=-33333
+c=-33333#
+\output->{\tracingcommands 0\showthe \outputpenalty \showboxbreadth 9999
+ \showboxdepth 9999 \hoffset 1sp {\setbox 254=\box 255\shipout \ifvbox 2
+\ifhbox 254 \error \fi 54\copy 25\fi 4} \ifvoid 254\relax \else \error \
+fi }
+{internal vertical mode: \tracingcommands}
+> -33333.
+<output> ...wthe \outputpenalty
+ \showboxbreadth 9999 \showbox...
+l.212 \par\penalty-33333
+ % end hyphenation, math is next
+
+
+Completed box being shipped out [-2.0.0.0.11]
+\vbox(16383.99998+1.0)x100.0, glue set 16239.0fil
+.\glue(\topskip) 12.0 plus 1.0fil
+.\hbox(8.0+2.0)x100.0, glue set 33.53377
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x0.0
+..\rip A
+..\glue 4.0 plus 1.99799 minus 1.00099
+..\rip /
+..\kern1.0
+..\discretionary replacing 2
+...\rip A (ligature AA)
+...\kern3.0
+...\rip -
+..\rip A (ligature AA)
+..\kern2.0
+..\discretionary replacing 3
+...\rip C (ligature B-)
+..|\rip A (ligature BB)
+..|\kern2.0
+..|\rip B
+..|\kern4.0
+..\rip A (ligature BB)
+..\kern2.0
+..\rip A (ligature BB)
+..\discretionary
+...\rip -
+..\discretionary replacing 1
+...\rip C
+...\rip -
+..|\rip A
+..\rip ^^82 (ligature CA)
+..\rip ^^82 (ligature CA)
+..\rip B (ligature C/)
+..\rip /
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0
+.\hbox(7.0+1.0)x100.0, glue set 41.5
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x0.0
+..\glue 4.0 plus 2.0 minus 1.0
+..\rip A (ligature BB)
+..\kern2.0
+..\rip A (ligature BB)
+..\kern2.0
+..\rip A (ligature BB)
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0
+.\hbox(7.0+1.0)x100.0, glue set 41.5
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x0.0
+..\glue 4.0 plus 2.0 minus 1.0
+..\closeout1
+..\discretionary replacing 2
+...\rip A (ligature BB)
+...\kern3.0
+...\rip -
+..\rip A (ligature BB)
+..\kern2.0
+..\discretionary replacing 3
+...\rip C (ligature B-)
+..|\rip A (ligature BB)
+..|\kern2.0
+..|\rip B
+..\rip A (ligature BB)
+..\kern2.0
+..\rip A (ligature BB)
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0
+.\hbox(8.0+2.0)x100.0, glue set 40.0
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x0.0
+..\glue 4.0 plus 2.0 minus 1.0
+..\rip A (ligature BB)
+..\kern2.0
+..\discretionary replacing 3
+...\rip B
+...\kern4.0
+...\rip C
+..|\rip A (ligature BB)
+..|\kern2.0
+..|\rip B
+..\rip A (ligature BB)
+..\kern2.0
+..\rip A (ligature BB)
+..\kern 0.0
+..\rip B
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0
+.\hbox(7.0+1.0)x100.0, glue set 41.5
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x0.0
+..\glue 4.0 plus 2.0 minus 1.0
+..\rip A (ligature BB)
+..\kern2.0
+..\rip A (ligature BB)
+..\kern2.0
+..\rip A (ligature BB)
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0
+.\glue(\baselineskip) 0.0 plus 41.0
+.\hbox(8.0+2.0)x20.0
+..\rip A
+..\kern2.0
+..\rip B
+..\glue 4.0 plus 2.0 minus 1.0
+..\rip a
+..\rip B
+..\glue 4.0 plus 2.46799 minus 0.81036
+.\glue(\parskip) 0.0 plus 42.0 minus 8.0
+.\penalty 0
+.\glue(\abovedisplayshortskip) 1.0 plus 45.0 minus 803.0
+.\glue(\baselineskip) 8.0 plus 41.0
+.\hbox(0.0+0.0)x0.0, shifted 50.0
+.\penalty 10000
+.\glue(\baselineskip) 10.0 plus 41.0
+.\hbox(0.0+0.0)x0.0, shifted 100.0
+.\penalty 0
+.\glue(\baselineskip) 3.0 plus 41.0
+.\hbox(7.0+1.0)x100.0, glue set 0.91347
+..\glue(\leftskip) 3.0
+..\rip A
+..\discretionary
+..\glue(\rightskip) 0.0 plus 104.0 minus 100.0
+.\penalty 37
+.\glue(\baselineskip) 9.0 plus 41.0
+.\hbox(0.0+0.0)x100.0, glue set 0.97116
+..\glue(\leftskip) 3.0
+..\kern 2.0
+..\kern-1.00002
+..\rip -
+..\glue(\rightskip) 0.0 plus 104.0 minus 100.0
+.\penalty -125
+.\glue(\baselineskip) 10.0 plus 41.0
+.\hbox(0.0+0.0)x100.0, glue set - 0.5
+..\glue(\leftskip) 3.0
+..\rip C
+..\mathon, surrounded 143.0
+..\mathoff
+..\glue(\rightskip) 0.0 plus 104.0 minus 100.0
+.\penalty -125
+.\glue(\baselineskip) 10.0 plus 41.0
+.\hbox(0.0+0.0)x100.0, glue set 0.89423
+..\glue(\leftskip) 3.0
+..\rip C
+..\glue(\rightskip) 0.0 plus 104.0 minus 100.0
+.\glue(\baselineskip) 10.0 plus 41.0
+.\hbox(0.0+0.0)x100.0, glue set - 0.83
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x120.0
+...\mathon, surrounded 60.0
+...\mathoff, surrounded 60.0
+..\mathoff, surrounded 60.0
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0 plus 104.0 minus 100.0
+.\glue(\parskip) 0.0 plus 42.0 minus 8.0
+.\glue(\baselineskip) 3.0 plus 41.0
+.\hbox(7.0+1.0)x100.0, glue set 45.5
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x0.0
+..\glue 4.0 plus 2.0 minus 1.0
+..\rip A (ligature AAAAAAAAAA)
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0
+
+Memory usage before: 950&531; after: 546&448; still untouched: 706
+{restoring \box254=void}
+{restoring \hoffset=0.0pt}
+{restoring \showboxdepth=1}
+{restoring \showboxbreadth=2}
+{restoring \tracingcommands=2}
+{vertical mode: begin-group character {}
+{\catcode}
+{\font}
+! Improper `at' size (0.0pt), replaced by 10pt.
+<to be read again>
+ \font
+l.213 ... \font?xyzzy at0pt\font
+ ? xyzzy scaled1?} % nonexistent
+I can only handle fonts at positive sizes that are
+less than 2048pt, so I've changed what you said to 10pt.
+
+! Font ?=xyzzy at 10.0pt not loadable: Metric (TFM) file not found.
+<to be read again>
+ \font
+l.213 ... \font?xyzzy at0pt\font
+ ? xyzzy scaled1?} % nonexistent
+I wasn't able to read the size data for this font,
+so I will ignore the font specification.
+[Wizards can fix TFM files using TFtoPL/PLtoTF.]
+You might try inserting a different font spec;
+e.g., type `I\font<same font id>=<substitute font name>'.
+
+{\font}
+! Font ?=xyzzy scaled 1 not loadable: Metric (TFM) file not found.
+<to be read again>
+ ?
+l.213 ...t\font ? xyzzy scaled1?
+ } % nonexistent
+I wasn't able to read the size data for this font,
+so I will ignore the font specification.
+[Wizards can fix TFM files using TFtoPL/PLtoTF.]
+You might try inserting a different font spec;
+e.g., type `I\font<same font id>=<substitute font name>'.
+
+{select font nullfont}
+{end-group character }}
+{restoring current font=\rip}
+{restoring ?=undefined}
+{restoring \catcode63=12}
+{blank space }
+{\font}
+{\font}
+! Improper `at' size (-10.0pt), replaced by 10pt.
+l.215 \font\ip trip at -10pt
+ % through the looking glass
+I can only handle fonts at positive sizes that are
+less than 2048pt, so I've changed what you said to 10pt.
+
+{\showthe}
+! You can't use `math shift character $' after \the.
+l.216 \showthe$
+
+I'm forgetting what you said and using zero instead.
+
+> 0.
+l.216 \showthe$
+
+
+{blank space }
+{\showthe}
+> \ip .
+<recently read> \font
+
+l.217 \showthe\font
+
+
+{\message}
+{\fontname}
+trip
+{blank space }
+{select font trip}
+{\textfont}
+{\scriptfont}
+{\scriptscriptfont}
+{\def}
+{blank space }
+{begin-group character {}
+{\tracingmacros}
+{\global}
+{\global}
+{\global}
+{\global}
+{\global}
+{\global}
+{\global}
+{\global}
+{\global}
+{\global}
+{\global}
+{\global}
+{\global}
+{\global}
+{end-group character }}
+{restoring \tracingmacros=4}
+{blank space }
+{\mathcode}
+{\mathcode}
+{\delcode}
+{\catcode}
+{\catcode}
+{\mathcode}
+{\mathcode}
+{\def}
+{begin-group character {}
+{\catcode}
+{\global}
+{end-group character }}
+{restoring \catcode122=11}
+{blank space }
+{\parshape}
+
+\a ->1pt 11.0pt2pt 12.0pt3pt 13.0pt4pt 14.0pt5pt 15.0pt6pt 16.0pt7pt 17.
+0pt8pt 18.0pt9pt 19.0pt10pt 20.0pt
+{\chardef}
+{\hangindent}
+{\hangafter}
+{\begingroup}
+{\looseness}
+{\rightskip}
+{\-}
+{horizontal mode: \-}
+{the character -}
+{\-}
+{the character -}
+{\-}
+{blank space }
+{math shift character $}
+@firstpass
+[]\ip -
+@\discretionary via @@0 b=0 p=88 d=7745
+@@1: line 1.2- t=7745 -> @@0
+-
+@\discretionary via @@0 b=0 p=89 d=7922
+@@2: line 1.2- t=7922 -> @@0
+@\discretionary via @@1 b=0 p=89 d=8922
+@@3: line 2.2- t=16667 -> @@1
+-
+@\discretionary via @@0 b=0 p=88 d=7745
+@@4: line 1.2- t=7745 -> @@0
+@\discretionary via @@2 b=0 p=88 d=8745
+@\discretionary via @@1 b=0 p=88 d=8745
+@@5: line 2.2- t=16490 -> @@1
+@\discretionary via @@3 b=0 p=88 d=8745
+@@6: line 3.2- t=25412 -> @@3
+-
+-
+@\discretionary via @@0 b=0 p=89 d=7922
+@@7: line 1.2- t=7922 -> @@0
+@\discretionary via @@4 b=0 p=89 d=8922
+@\discretionary via @@2 b=0 p=89 d=8922
+@\discretionary via @@1 b=0 p=89 d=8922
+@@8: line 2.2- t=16667 -> @@1
+@\discretionary via @@5 b=0 p=89 d=8922
+@\discretionary via @@3 b=0 p=89 d=8922
+@@9: line 3.2- t=25412 -> @@5
+@\discretionary via @@6 b=0 p=89 d=8922
+@@10: line 4.2- t=34334 -> @@6
+A-
+@\discretionary via @@0 b=0 p=88 d=7745
+@@11: line 1.2- t=7745 -> @@0
+@\discretionary via @@7 b=0 p=88 d=8745
+@\discretionary via @@4 b=0 p=88 d=8745
+@\discretionary via @@2 b=0 p=88 d=8745
+@\discretionary via @@1 b=0 p=88 d=8745
+@@12: line 2.2- t=16490 -> @@1
+@\discretionary via @@8 b=0 p=88 d=8745
+@\discretionary via @@5 b=0 p=88 d=8745
+@\discretionary via @@3 b=0 p=88 d=8745
+@@13: line 3.2- t=25235 -> @@5
+@\discretionary via @@9 b=0 p=88 d=8745
+@\discretionary via @@6 b=0 p=88 d=8745
+@@14: line 4.2- t=34157 -> @@6
+@\discretionary via @@10 b=0 p=88 d=8745
+@@15: line 5.2- t=43079 -> @@10
+@\par via @@0 b=0 p=-10000 d=1
+@@16: line 1.2- t=1 -> @@0
+@\par via @@11 b=0 p=-10000 d=100001
+@\par via @@7 b=0 p=-10000 d=100001
+@\par via @@4 b=0 p=-10000 d=100001
+@\par via @@2 b=0 p=-10000 d=100001
+@\par via @@1 b=0 p=-10000 d=100001
+@@17: line 2.2- t=107746 -> @@1
+@\par via @@12 b=0 p=-10000 d=100001
+@\par via @@8 b=0 p=-10000 d=100001
+@\par via @@5 b=0 p=-10000 d=100001
+@\par via @@3 b=0 p=-10000 d=100001
+@@18: line 3.2- t=116491 -> @@5
+@\par via @@13 b=0 p=-10000 d=100001
+@\par via @@9 b=0 p=-10000 d=100001
+@\par via @@6 b=0 p=-10000 d=100001
+@@19: line 4.2- t=125236 -> @@13
+@\par via @@14 b=0 p=-10000 d=100001
+@\par via @@10 b=0 p=-10000 d=100001
+@@20: line 5.2- t=134158 -> @@14
+@\par via @@15 b=0 p=-10000 d=100001
+@@21: line 6.2- t=143080 -> @@15
+
+%% goal height=16383.99998, max depth=2.0
+% t=20.0 plus 1.0fil g=16383.99998 b=0 p=37 c=37#
+% t=30.0 plus 41.0 plus 1.0fil g=16383.99998 b=0 p=-213 c=-213#
+{display math mode: \number}
+{\the}
+! Improper alphabetic constant.
+<to be read again>
+ \relax
+l.249 ...mber\the\delcode`\relax
+ \over{{{}}}}\pagestretch=-1\p...
+A one-character control sequence belongs after a ` mark.
+So I'm essentially inserting \0 here.
+
+{the character -}
+{the character 1}
+{\relax}
+{\over}
+{begin-group character {}
+{math mode: begin-group character {}
+{begin-group character {}
+{end-group character }}
+{end-group character }}
+{end-group character }}
+{display math mode: end-group character }}
+! Extra }, or forgotten $.
+l.249 ...code`\relax\over{{{}}}}
+ \pagestretch=-1\pagetotal\sho...
+I've deleted a group-closing symbol because it seems to be
+spurious, as in `$x}$'. But perhaps the } is legitimate and
+you forgot something else, as in `\hbox{$x}'. In such cases
+the way to recover is to insert both the forgotten and the
+deleted material, e.g., by typing `I$}'.
+
+{\pagestretch}
+{\showlists}
+
+### display math mode entered at line 249
+\mathord
+.{}
+this will be denominator of:
+\fraction, thickness = default
+\\mathord []
+\\mathord []
+### vertical mode entered at line 0
+### current page:
+\glue(\topskip) 20.0 plus 1.0fil
+\hbox(0.0+0.0)x11.0, glue set 1.3fil, shifted 1.0
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+etc.
+total height 40.0 plus -40.0 plus 1.0fil
+ goal height 16383.99998
+prevdepth 1.0, prevgraf 3 lines
+
+! OK.
+l.249 ...=-1\pagetotal\showlists
+
+
+{\begingroup}
+{\halign}
+! Missing \endgroup inserted.
+<inserted text>
+ \endgroup
+...
+l.250 \begingroup\halign
+ to\the\displaywidth{#&#\crcr\crcr\cr...
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{\endgroup}
+{\halign}
+! Improper \halign inside $$'s.
+<recently read> \halign
+
+l.250 \begingroup\halign
+ to\the\displaywidth{#&#\crcr\crcr\cr...
+Displays can use special alignments (like \eqalignno)
+only if nothing but the alignment itself is between $$'s.
+So I've deleted the formulas that preceded this alignment.
+
+{internal vertical mode: \the}
+{restricted horizontal mode: end of alignment template}
+
+Loose \hbox (badness 1) in alignment at lines 250--250
+ [] []
+
+\hbox(0.0+0.0)x15.0, glue set 0.1875
+.\glue(\tabskip) 0.0 plus 40.0
+.\unsetbox(0.0+0.0)x0.0
+.etc.
+
+! Missing number, treated as zero.
+<to be read again>
+ =
+l.253 ...aysize* \global\setbox=
+
+A number should have been here; I inserted `0'.
+(If you can't figure out why I needed to see a number,
+look up `weird error' in the index to The TeXbook.)
+
+! Improper \setbox.
+<recently read> =
+
+l.253 ...aysize* \global\setbox=
+
+Sorry, \setbox is not allowed after \halign in a display,
+or between \accent and an accented character.
+
+! Missing $$ inserted.
+<to be read again>
+ \eqno
+l.254 \eqno
+ % another error (actually causes two error messag...
+Displays can use special alignments (like \eqalignno)
+only if nothing but the alignment itself is between $$'s.
+
+{restoring \predisplaypenalty=0}
+{restoring \displayindent=0.0pt}
+{restoring \displaywidth=0.0pt}
+{restoring \predisplaysize=0.0pt}
+{restoring \fam=0}
+% t=40.0 plus -40.0 plus 1.0fil g=16383.99998 b=0 p=101 c=101
+% t=53.0 plus 1.0 plus 1.0fil g=16383.99998 b=0 p=-1179647 c=-1179647#
+\output->{\tracingcommands 0\showthe \outputpenalty \showboxbreadth 9999
+ \showboxdepth 9999 \hoffset 1sp {\setbox 254=\box 255\shipout \ifvbox 2
+\ifhbox 254 \error \fi 54\copy 25\fi 4} \ifvoid 254\relax \else \error \
+fi }
+{internal vertical mode: \tracingcommands}
+> -1179647.
+<output> ...wthe \outputpenalty
+ \showboxbreadth 9999 \showbox...
+...
+l.254 \eqno
+ % another error (actually causes two error messag...
+
+
+Completed box being shipped out [-2.0.0.0.11.0.327680]
+\vbox(16383.99998+0.0)x20.0, glue set 16331.0fil
+.\glue(\topskip) 20.0 plus 1.0fil
+.\hbox(0.0+0.0)x11.0, glue set 1.3fil, shifted 1.0
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x0.0
+..\discretionary
+..\ip -
+..\glue(\rightskip) 0.0 plus 10.0fil minus 0.00002
+.\penalty 37
+.\glue(\baselineskip) 10.0 plus 41.0
+.\hbox(0.0+0.0)x12.0, glue set 2.0fil, shifted 2.0
+..\glue(\leftskip) 3.0
+..\kern-1.00002
+..\ip -
+..\discretionary
+..\discretionary
+..\ip -
+..\glue(\rightskip) 0.0 plus 10.0fil minus 0.00002
+.\penalty -213
+.\glue(\baselineskip) 3.0 plus 41.0
+.\hbox(7.0+1.0)x13.0, glue set 1.8fil, shifted 3.0
+..\glue(\leftskip) 3.0
+..\kern-1.00002
+..\ip - (ligature -
+)
+..\ip -
+..\discretionary
+..\kern1.0
+..\ip A
+..\discretionary
+...\ip -
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0 plus 10.0fil minus 0.00002
+.\penalty 101
+.\glue(\abovedisplayskip) 3.0
+.\glue(\baselineskip) 9.0 plus 41.0
+.\hbox(0.0+0.0)x15.0, glue set 0.1875, shifted 5.0
+..\glue(\tabskip) 0.0 plus 40.0
+..\hbox(0.0+0.0)x0.0
+..\glue(\tabskip) 0.0 plus 40.0
+
+Memory usage before: 339&413; after: 236&405; still untouched: 706
+{restoring \box254=void}
+{restoring \hoffset=0.0pt}
+{restoring \showboxdepth=1}
+{restoring \showboxbreadth=2}
+{restoring \tracingcommands=2}
+{restoring \parshape=10}
+{restoring \hangafter=-12}
+{restoring \hangindent=-10.0pt}
+{restoring \looseness=2}
+{horizontal mode: \eqno}
+! You can't use `\eqno' in horizontal mode.
+<recently read> \eqno
+
+l.254 \eqno
+ % another error (actually causes two error messag...
+Sorry, but I'm not programmed to handle this case;
+I'll just pretend that you didn't ask for it.
+If you're in the wrong mode, you might be able to
+return to the right one by typing `I}' or `I$' or `I\par'.
+
+{\looseness}
+{math shift character $}
+{math mode: \right}
+! Missing delimiter (. inserted).
+<to be read again>
+ \mathchardef
+l.256 $\right\relax\mathchardef
+ \minus="322D % locally \minus ...
+I was expecting to see something like `(' or `\{' or
+`\}' here. If you typed, e.g., `{' instead of `\{', you
+should probably delete the `{' by typing `1' now, so that
+braces don't get unbalanced. Otherwise just proceed.
+Acceptable delimiters are characters whose \delcode is
+nonnegative, or you can use `\delimiter <delimiter code>'.
+
+! Extra \right.
+<to be read again>
+ \mathchardef
+l.256 $\right\relax\mathchardef
+ \minus="322D % locally \minus ...
+I'm ignoring a \right that had no matching \left.
+
+{\mathchardef}
+{\left}
+{the letter A}
+{\over}
+{the letter A}
+{\abovewithdelims}
+! Missing delimiter (. inserted).
+<to be read again>
+ ?
+l.257 ...ver A\abovewithdelims.?
+ \right(+\mskip1A\minus=A+\pen...
+I was expecting to see something like `(' or `\{' or
+`\}' here. If you typed, e.g., `{' instead of `\{', you
+should probably delete the `{' by typing `1' now, so that
+braces don't get unbalanced. Otherwise just proceed.
+Acceptable delimiters are characters whose \delcode is
+nonnegative, or you can use `\delimiter <delimiter code>'.
+
+! Missing number, treated as zero.
+<to be read again>
+ ?
+l.257 ...ver A\abovewithdelims.?
+ \right(+\mskip1A\minus=A+\pen...
+A number should have been here; I inserted `0'.
+(If you can't figure out why I needed to see a number,
+look up `weird error' in the index to The TeXbook.)
+
+! Illegal unit of measure (pt inserted).
+<to be read again>
+ ?
+l.257 ...ver A\abovewithdelims.?
+ \right(+\mskip1A\minus=A+\pen...
+Dimensions can be in units of em, ex, in, pt, pc,
+cm, mm, dd, cc, bp, or sp; but yours is a new one!
+I'll assume that you meant to say pt, for printer's points.
+To recover gracefully from this error, it's best to
+delete the erroneous units; e.g., type `2' to delete
+two letters. (See Chapter 27 of The TeXbook.)
+
+! Ambiguous; you need another { and }.
+<to be read again>
+ ?
+l.257 ...ver A\abovewithdelims.?
+ \right(+\mskip1A\minus=A+\pen...
+I'm ignoring this fraction specification, since I don't
+know whether a construction like `x \over y \over z'
+means `{x \over y} \over z' or `x \over {y \over z}'.
+
+{the character ?}
+{\right}
+
+(->\delimiter "4162362
+{the character +}
+{\mskip}
+! Illegal unit of measure (mu inserted).
+<to be read again>
+ A
+l.257 ...elims.?\right(+\mskip1A
+ \minus=A+\penalty+1000A
+The unit of measurement in math glue must be mu.
+To recover gracefully from this error, it's best to
+delete the erroneous units; e.g., type `2' to delete
+two letters. (See Chapter 27 of The TeXbook.)
+
+{the letter A}
+{\mathchar"322D}
+{the character =}
+{the letter A}
+{the character +}
+{\penalty}
+{the letter A}
+{blank space }
+{\relpenalty}
+{\binoppenalty}
+{\mathsurround}
+{math shift character $}
+! \scriptfont 0 is undefined (character ?).
+<recently read> $
+
+l.260 \mathsurround.11em$
+ \x % this formula goes on line 7
+Somewhere in the math formula just ended, you used the
+stated character from an undefined font family. For example,
+plain TeX doesn't allow \it or \sl in subscripts. Proceed,
+and I'll try to forget that I needed that character.
+
+{restoring \mathsurround=0.0pt}
+{restoring \binoppenalty=0}
+{restoring \relpenalty=0}
+{restoring \minus=\mathchar"232D}
+{restoring \fam=0}
+{horizontal mode: \char"C8}
+Missing character: There is no ^^c8 in font trip!
+{math shift character $}
+@firstpass
+$[] \ip /
+@\penalty via @@0 b=0 p=-3333 d=-11108888
+@@1: line 7.2 t=-11108888 -> @@0
+ A \smalltrip --
+@\penalty via @@0 b=0 p=-2222 d=-4937283
+@@2: line 7.2 t=-4937283 -> @@0
+@\penalty via @@1 b=0 p=-2222 d=-4937283
+@@3: line 8.2 t=-16046171 -> @@1
+ \ip A /
+@\penalty via @@0 b=0 p=1000 d=1000001
+@@4: line 7.2 t=1000001 -> @@0
+@\penalty via @@2 b=0 p=1000 d=1000001
+@\penalty via @@1 b=0 p=1000 d=1000001
+@@5: line 8.2 t=-10108887 -> @@1
+@\penalty via @@3 b=0 p=1000 d=1000001
+@@6: line 9.2 t=-15046170 -> @@3
+ A$
+@\par via @@0 b=0 p=-10000 d=1
+@@7: line 7.2- t=1 -> @@0
+@\par via @@4 b=0 p=-10000 d=1
+@\par via @@2 b=0 p=-10000 d=1
+@\par via @@1 b=0 p=-10000 d=1
+@@8: line 8.2- t=-11108887 -> @@1
+@\par via @@5 b=0 p=-10000 d=1
+@\par via @@3 b=0 p=-10000 d=1
+@@9: line 9.2- t=-16046170 -> @@3
+@\par via @@6 b=0 p=-10000 d=1
+@@10: line 10.2- t=-15046169 -> @@6
+
+%% goal height=16383.99998, max depth=2.0
+{display math mode: blank space }
+{\vadjust}
+{internal vertical mode: \penalty}
+{end-group character }}
+{restoring \parshape=10}
+{restoring \hangafter=-12}
+{restoring \hangindent=-10.0pt}
+{restoring \looseness=-2}
+{display math mode: \mkern}
+{\the}
+{the character 7}
+{\prevgraf}
+{\insert}
+! You can't \insert255.
+<to be read again>
+ {
+l.262 ...\prevgraf=8 \insert255{
+ \penalty999}
+I'm changing to \insert0; box 255 is special.
+
+{internal vertical mode: \penalty}
+{end-group character }}
+{restoring \parshape=10}
+{restoring \hangafter=-12}
+{restoring \hangindent=-10.0pt}
+{restoring \looseness=-2}
+{display math mode: blank space }
+{\char"C8}
+{\vcenter}
+{internal vertical mode: end-group character }}
+{restoring \parshape=10}
+{restoring \hangafter=-12}
+{restoring \hangindent=-10.0pt}
+{restoring \looseness=-2}
+{display math mode: blank space }
+{begin-group character {}
+{math mode: \mathaccent}
+{the letter A}
+{end-group character }}
+{end-group character }}
+{display math mode: subscript character |}
+{blank space }
+{superscript character ^}
+{math mode: \raise}
+{restricted horizontal mode: the letter a}
+{end-group character }}
+{math mode: \displaystyle}
+{\char}
+{\textstyle}
+{end-group character }}
+{display math mode: blank space }
+{\overline}
+{math mode: superscript character ^}
+{blank space }
+{the letter A}
+{subscript character |}
+{\mathinner}
+{end-group character }}
+{superscript character ^}
+{the letter A}
+{blank space }
+{\mathchar}
+{\char}
+{subscript character ^^A}
+{\mathaccent}
+{\mathop}
+{blank space }
+{\mathbin}
+{blank space }
+{\mathopen}
+{blank space }
+{\mathpunct}
+{\mathclose}
+{blank space }
+{\mathrel}
+{blank space }
+{\global}
+{\mathaccent}
+{\fam}
+{the letter A}
+{the character 9}
+{\the}
+! Bad number (-1).
+<to be read again>
+ }
+l.269 ...the\scriptscriptfont-1}
+ }}}}
+Since I expected to read a number between 0 and 15,
+I changed this one to zero.
+
+{select font trip}
+{end-group character }}
+{restoring current font=\ip}
+{restoring \fam=-1}
+{end-group character }}
+{end-group character }}
+{end-group character }}
+{end-group character }}
+{display math mode: blank space }
+{\mathop}
+{superscript character ^}
+{\mathop}
+{\nolimits}
+{\limits}
+{subscript character |}
+{blank space }
+{\mathord}
+! Missing { inserted.
+<to be read again>
+ \radical
+l.272 \mathord \radical
+ "161 % missing { will be inserted
+A left brace was mandatory here, so I've put one in.
+You might want to delete and/or insert some corrections
+so that I will find a matching right brace soon.
+(If you're confused by all this, try typing `I}' now.)
+
+{math mode: \radical}
+{\textstyle}
+{\radical}
+{\left}
+
+(->\delimiter "4162362
+{\scriptscriptstyle}
+{\mathop}
+{\underline}
+{blank space }
+{the letter A}
+{\atop}
+{\displaystyle}
+{the letter A}
+{subscript character |}
+{the letter A}
+{\hfil}
+{\over}
+{the letter B}
+{\nonscript}
+{\kern}
+{end-group character }}
+{superscript character ^}
+{end-group character }}
+{end-group character }}
+{blank space }
+{\nolimits}
+{subscript character |}
+{\mathop}
+{\nonscript}
+{\textstyle}
+{\nonscript}
+{\mskip}
+{\showthe}
+> 9.0mu minus 1.0fil.
+l.276 \showthe\lastskip
+ B\abovewithdelims(.2pt\displa...
+
+{the letter B}
+{\abovewithdelims}
+
+(->\delimiter "4162362
+{\displaylimits}
+! Limit controls must follow a math operator.
+<recently read> \displaylimits
+
+l.276 ...lims(.2pt\displaylimits
+ }^z
+I'm ignoring this misplaced \limits or \nolimits command.
+
+{end-group character }}
+{superscript character ^}
+
+z->\delimiter "4162362
+{blank space }
+{\discretionary}
+{restricted horizontal mode: \showthe}
+> 1000.
+l.277 ...ry{\showthe\spacefactor
+ -}{\smalltrip A\hss}{\smalltr...
+
+{the character -}
+{end-group character }}
+{select font trip at 5.0pt}
+{the letter A}
+{\hss}
+{end-group character }}
+{restoring current font=\ip}
+! Improper discretionary list.
+l.277 ...tor-}{\smalltrip A\hss}
+ {\smalltrip A}
+Discretionary lists must contain only boxes and kerns.
+
+The following discretionary sublist has been deleted:
+\glue 0.0 plus 1.0fil minus 1.0fil
+
+{select font trip at 5.0pt}
+{the letter A}
+{end-group character }}
+{restoring current font=\ip}
+! Illegal math \discretionary.
+l.277 ...ip A\hss}{\smalltrip A}
+
+Sorry: The third part of a discretionary break must be
+empty, in math formulas. I had to delete your third part.
+
+{math mode: blank space }
+{\right}
+{the letter A}
+{end-group character }}
+{end-group character }}
+{end-group character }}
+{display math mode: blank space }
+{\let}
+{\aftergroup}
+{\eqno}
+{math mode: \aftergroup}
+{\scriptstyle}
+{\mathchar"232D}
+
+(->\delimiter "4162362
+{\delimiter}
+{\mathpunct}
+{the letter A}
+{the letter A}
+{end-group character }}
+{blank space }
+{subscript character |}
+{the letter B}
+{\fam}
+{the character -}
+{end-group character }}
+{restoring \fam=-1}
+{superscript character ^}
+{\hbox}
+{restricted horizontal mode: the letter A}
+{end-group character }}
+{math mode: end-group character }}
+{begin-group character {}
+{\above}
+{begin-group character {}
+{the letter v}
+{\overwithdelims}
+{blank space }
+{\displaystyle}
+{begin-group character {}
+{the letter p}
+{the letter q}
+{\atopwithdelims}
+
+(->\delimiter "4162362
+
+(->\delimiter "4162362
+{\vrule}
+{end-group character }}
+{end-group character }}
+{blank space }
+{\show}
+> \penalty=\mathchar"232D.
+l.284 \show\penalty
+ \showlonglists
+
+
+\showlonglists ->{\tracingcommands 0\pagefillstretch -1\dimen 100 \showb
+oxbreadth 9999 \showboxdepth 9999 \showlists \pagegoal =10000pt}
+{begin-group character {}
+{\tracingcommands}
+
+### math mode entered at line 284
+### math mode entered at line 282
+\mathord
+.\fraction, thickness = default
+.\\mathord
+.\.\fam1 v
+./\displaystyle
+./\mathord
+./.\fraction, thickness 0.0, left-delimiter "162362, right-delimiter "16
+2362
+./.\\mathord
+./.\.\fam1 p
+./.\\mathrel
+./.\.\fam1 q
+././\rule(9.0+*)x0.4
+\mathord
+this will be denominator of:
+\fraction, thickness 9.0
+\{}
+### math mode entered at line 280
+\scriptstyle
+\mathbin
+.\fam3 -
+\mathopen
+.\fam1 b
+\mathpunct
+.\mathord
+..\fam1 A
+.\mathord
+..\fam1 A
+^\hbox(7.0+1.0)x2.0
+^.\ip A
+_\mathord
+_.\fam1 B
+_\mathord
+_.\fam0 -
+\mathord
+### display math mode entered at line 261
+\vadjust
+.\penalty 7
+\mkern-9.0mu
+\mathord
+.\fam0 7
+\insert0, natural size 0.0; split(1.0 plus 43.0,-2.0); float cost 100
+.\penalty 999
+\mathord
+.\fam0 ^^c8
+\vcenter
+.\vbox(-2.0+0.0)x0.0
+\accent\fam3 -
+.\fam1 A
+^\mathord
+^.\hbox(7.0+1.0)x2.0, shifted -2.0
+^..\ip a
+^\displaystyle
+^\mathbin
+^.\fam1 /
+^\textstyle
+_\fam0 -
+\overline
+.\mathord
+.^\fam1 A
+.\mathord
+..\fam1 A
+._\fam3 -
+.\mathinner
+..{}
+.^\mathord
+.^.\fam1 A
+.^\mathord
+.^.\fam1 A
+.^\mathord
+.^.\fam1 B
+.^_\accent\fam1 a
+.^_.\mathop
+.^_..\fam1 A
+.^_.\mathbin
+.^_..\fam1 A
+.^_.\mathopen
+.^_..\fam1 A
+.^_.\mathpunct
+.^_..\fam1 A
+.^_.\mathclose
+.^_..\fam1 A
+.^_.\mathrel
+.^_..\fam1 A
+.^_.\accent\fam1 a
+.^_..\mathord
+.^_...\fam13 A
+.^_..\mathord
+.^_...\fam13 9
+\mathop
+.\fam1 B
+^\fam1 C
+\mathop\limits
+.\fam1 b
+_\fam1 C
+\mathord
+.\radical"161
+..\textstyle
+..\radical"282382
+...\mathinner
+....\left"162362
+....\scriptscriptstyle
+....\mathop\nolimits
+.....\underline
+......\fraction, thickness 0.0
+......\\mathord
+......\.\fam1 A
+....../\displaystyle
+....../\mathord
+....../.\fam1 A
+....../^\fam2 -
+....../_\fraction, thickness = default
+....../_\\mathord
+....../_\.\fam1 A
+....../_\\glue 0.0 plus 1.0fil
+....../_/\mathord
+....../_/.\fam1 B
+....../_/\glue(\nonscript)
+....../_/\kern 1.0
+....^\fam1 b
+...._\fraction, thickness 2.0, left-delimiter "162362
+...._\\mathop
+...._\.\fam3
+...._\\glue(\nonscript)
+...._\\textstyle
+...._\\glue(\nonscript)
+...._\\glue(\mskip) 9.0mu minus 1.0fil
+...._\\mathord
+...._\.\fam1 B
+...._/{}
+....\discretionary
+.....\kern-1.00002
+.....\ip -
+....|\smalltrip A
+....\right"161361
+...\mathord
+....\fam1 A
+### vertical mode entered at line 0
+### current page:
+\glue(\topskip) 7.3 plus 1.0fil
+\hbox(12.7+3.7)x17.0, glue set 0.17223fil, shifted 7.0
+.\glue(\leftskip) 3.0
+.\mathon, surrounded 1.1
+.\hbox(12.7+3.7)x4.80002
+..\hbox(0.0+0.0)x0.1, shifted -7.0
+..\hbox(12.7+3.7)x1.70001
+...\hbox(0.0+0.0)x0.1, shifted -7.0
+...\vbox(12.7+3.7)x1.5
+....\hbox(3.5+0.5)x1.5
+.....\smalltrip A
+....\kern1.2
+....\rule(1.0+0.0)x*
+....\kern6.2
+....\hbox(3.5+0.5)x1.5
+.....\smalltrip A
+...\hbox(0.0+0.0)x0.1, shifted -7.0
+..\hbox(8.0+2.0)x3.0, shifted -4.0
+...\ip b
+.\glue(\medmuskip) 0.55554 minus 0.83331
+.\ip /
+.\penalty -3333
+.\glue 0.27777
+.\glue(\medmuskip) 0.55554 minus 0.83331
+.\ip A
+.\kern1.0
+.\glue(\thickmuskip) -1.11108
+.\smalltrip -
+.\smalltrip -
+.\penalty -2222
+.\glue(\thickmuskip) -1.11108
+.\ip A
+.\glue(\medmuskip) 0.55554 minus 0.83331
+.\ip /
+.\penalty 1000
+.\glue(\medmuskip) 0.55554 minus 0.83331
+.\ip A
+.\kern1.0
+.\mathoff, surrounded 1.1
+.\penalty 10000
+.\glue(\parfillskip) 0.0
+.\glue(\rightskip) 0.0 plus 10.0fil minus 0.00002
+total height 21.7 plus 1.0fil plus -803.0fill
+ goal height 16383.99998
+prevdepth 3.7, prevgraf 8 lines
+
+! OK.
+\showlonglists ...99 \showlists
+ \pagegoal =10000pt}
+l.284 ...\penalty \showlonglists
+
+
+{restoring \showboxdepth=1}
+{restoring \showboxbreadth=2}
+{restoring \tracingcommands=2}
+{math shift character $}
+! Missing } inserted.
+<inserted text>
+ }
+...
+l.285 $
+ \expandafter$\csname!\endcsname % end of hairy displ...
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{end-group character }}
+{math shift character $}
+{display math mode: \expandafter}
+{\csname}
+Missing character: There is no v in font trip!
+{restoring \!=undefined}
+{restoring \fam=-1}
+! \textfont 0 is undefined (character 7).
+<to be read again>
+ \relax
+...
+l.285 ...fter$\csname!\endcsname
+ % end of hairy display, miss...
+Somewhere in the math formula just ended, you used the
+stated character from an undefined font family. For example,
+plain TeX doesn't allow \it or \sl in subscripts. Proceed,
+and I'll try to forget that I needed that character.
+
+! \textfont 0 is undefined (character ^^c8).
+<to be read again>
+ \relax
+...
+l.285 ...fter$\csname!\endcsname
+ % end of hairy display, miss...
+Somewhere in the math formula just ended, you used the
+stated character from an undefined font family. For example,
+plain TeX doesn't allow \it or \sl in subscripts. Proceed,
+and I'll try to forget that I needed that character.
+
+! \scriptfont 0 is undefined (character -).
+<to be read again>
+ \relax
+...
+l.285 ...fter$\csname!\endcsname
+ % end of hairy display, miss...
+Somewhere in the math formula just ended, you used the
+stated character from an undefined font family. For example,
+plain TeX doesn't allow \it or \sl in subscripts. Proceed,
+and I'll try to forget that I needed that character.
+
+! \scriptscriptfont 13 is undefined (character A).
+<to be read again>
+ \relax
+...
+l.285 ...fter$\csname!\endcsname
+ % end of hairy display, miss...
+Somewhere in the math formula just ended, you used the
+stated character from an undefined font family. For example,
+plain TeX doesn't allow \it or \sl in subscripts. Proceed,
+and I'll try to forget that I needed that character.
+
+! \scriptscriptfont 13 is undefined (character 9).
+<to be read again>
+ \relax
+...
+l.285 ...fter$\csname!\endcsname
+ % end of hairy display, miss...
+Somewhere in the math formula just ended, you used the
+stated character from an undefined font family. For example,
+plain TeX doesn't allow \it or \sl in subscripts. Proceed,
+and I'll try to forget that I needed that character.
+
+Missing character: There is no in font trip!
+
+Overfull \hbox (48.4746pt too wide) detected at line 285
+[][][] [] [] []|
+
+\hbox(68.26251+98.15005)x19.0, glue set - 1.0
+.\kern -2.49994
+.\vbox(6.0+-8.0)x0.0
+.etc.
+
+{restoring \penalty=\penalty}
+{restoring \displayindent=0.0pt}
+{restoring \displaywidth=0.0pt}
+{restoring \predisplaysize=0.0pt}
+{restoring \fam=0}
+{horizontal mode: \expandafter}
+{undefined}
+! Undefined control sequence.
+<recently read> \!
+
+l.285 ...fter$\csname!\endcsname
+ % end of hairy display, miss...
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+% t=21.7 plus 1.0fil plus -803.0fill g=10000.0 b=0 p=0 c=0#
+% t=262.41258 plus 80.0 plus 1.0fil plus -803.0fill g=10000.0 b=0 p=7 c=
+7
+% t=262.41258 plus 80.0 plus 1.0fil plus -803.0fill g=10000.0 b=0 p=-117
+9647 c=-1179647#
+\output->{\tracingcommands 0\showthe \outputpenalty \showboxbreadth 9999
+ \showboxdepth 9999 \hoffset 1sp {\setbox 254=\box 255\shipout \ifvbox 2
+\ifhbox 254 \error \fi 54\copy 25\fi 4} \ifvoid 254\relax \else \error \
+fi }
+{internal vertical mode: \tracingcommands}
+> -1179647.
+<output> ...wthe \outputpenalty
+ \showboxbreadth 9999 \showbox...
+...
+l.285 ...fter$\csname!\endcsname
+ % end of hairy display, miss...
+
+
+Completed box being shipped out [-2.0.0.0.11.0.327680]
+\vbox(10000.0+2.0)x28.0, glue set 9737.58789fil
+.\glue(\topskip) 7.3 plus 1.0fil
+.\hbox(12.7+3.7)x17.0, glue set 0.17223fil, shifted 7.0
+..\glue(\leftskip) 3.0
+..\mathon, surrounded 1.1
+..\hbox(12.7+3.7)x4.80002
+...\hbox(0.0+0.0)x0.1, shifted -7.0
+...\hbox(12.7+3.7)x1.70001
+....\hbox(0.0+0.0)x0.1, shifted -7.0
+....\vbox(12.7+3.7)x1.5
+.....\hbox(3.5+0.5)x1.5
+......\smalltrip A
+.....\kern1.2
+.....\rule(1.0+0.0)x*
+.....\kern6.2
+.....\hbox(3.5+0.5)x1.5
+......\smalltrip A
+....\hbox(0.0+0.0)x0.1, shifted -7.0
+...\hbox(8.0+2.0)x3.0, shifted -4.0
+....\ip b
+..\glue(\medmuskip) 0.55554 minus 0.83331
+..\ip /
+..\penalty -3333
+..\glue 0.27777
+..\glue(\medmuskip) 0.55554 minus 0.83331
+..\ip A
+..\kern1.0
+..\glue(\thickmuskip) -1.11108
+..\smalltrip -
+..\smalltrip -
+..\penalty -2222
+..\glue(\thickmuskip) -1.11108
+..\ip A
+..\glue(\medmuskip) 0.55554 minus 0.83331
+..\ip /
+..\penalty 1000
+..\glue(\medmuskip) 0.55554 minus 0.83331
+..\ip A
+..\kern1.0
+..\mathoff, surrounded 1.1
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0 plus 10.0fil minus 0.00002
+.\penalty 0
+.\glue(\abovedisplayskip) 3.0
+.\glue(\lineskip) 0.0 plus 40.0
+.\hbox(68.26251+98.15005)x19.0, glue set - 1.0, shifted 9.0
+..\kern -2.49994
+..\vbox(6.0+-8.0)x0.0
+..\vbox(17.1+4.2)x5.99
+...\kern17.1
+...\hbox(0.0+0.0)x0.0, shifted 8.5
+....\bigtr
+p -
+...\kern-17.1
+...\hbox(17.1+4.2)x5.99
+....\ip A
+....\vbox(21.3+0.0)x3.99, shifted 4.2
+.....\hbox(9.0+0.0)x2.99, shifted 1.0
+......\hbox(7.0+1.0)x2.0, shifted -2.0
+.......\ip a
+......\ip /
+.....\kern12.3
+.....\hbox(0.0+0.0)x-0.01
+..\vbox(25.76251+4.1)x15.0156
+...\kern1.0
+...\rule(1.0+0.0)x*
+...\kern3.0
+...\hbox(20.76251+4.1)x15.0156
+....\hbox(3.5+0.5)x1.49, shifted -8.3
+.....\smalltrip A
+....\ip A
+....\hbox(0.0+0.0)x-5.01, shifted 4.1
+.....\ip -
+....\glue(\thinmuskip) 0.27777 plus 2.0fill minus 0.83331
+....\hbox(0.0+0.0)x0.0
+....\hbox(4.0+16.20003)x16.25781, shifted -16.76251
+.....\smalltrip A
+.....\kern0.5
+.....\kern1.0
+.....\smalltrip B
+.....\vbox(16.00002+2.0)x12.2678, shifted 14.20003
+......\kern8.99998
+......\hbox(0.0+0.0)x0.0, shifted 2.1389
+.......\bigtr
+p ^^82
+......\kern-8.99998
+......\hbox(16.00002+2.0)x12.2778
+.......\hbox(14.00002+2.0)x6.00002, shifted -0.99998
+........\bigtr
+p A
+.......\glue(\thinmuskip) 0.27777 plus 2.0fill minus 0.83331
+.......\bigtr
+p A
+.......\kern2.0
+.......\vbox(16.00002+0.0)x0.0
+........\hbox(14.00002+2.0)x0.0, shifted -3.0
+.........\bigtr
+p a
+........\kern0.0
+........\hbox(0.0+0.0)x0.0
+..\glue(\thinmuskip) 0.27777 plus 2.0fill minus 0.83331
+..\vbox(28.0+0.0)x3.0
+...\kern10.0
+...\hbox(0.0+0.0)x3.0, glue set 0.5fil
+....\glue 0.0 plus 1.0fil minus 1.0fil
+....\smalltrip C
+....\glue 0.0 plus 1.0fil minus 1.0fil
+...\kern6.0
+...\hbox(12.0+0.0)x3.0
+....\hbox(8.0+2.0)x3.0, shifted -4.0
+.....\ip B
+..\glue(\thinmuskip) 0.27777 plus 2.0fill minus 0.83331
+..\vbox(7.0+18.0)x8.0
+...\hbox(7.0+0.0)x8.0
+....\hbox(0.0+0.0)x8.0, shifted -7.0
+.....\ip M
+...\kern8.0
+...\hbox(0.0+0.0)x8.0, glue set 3.0fil, shifted -1.0
+....\glue 0.0 plus 1.0fil minus 1.0fil
+....\smalltrip C
+....\glue 0.0 plus 1.0fil minus 1.0fil
+...\kern10.0
+..\glue(\thinmuskip) 0.27777 plus 2.0fill minus 0.83331
+..\hbox(68.26251+98.15005)x39.63556
+...\hbox(68.26251+98.15005)x39.63556
+....\hbox(7.0+1.0)x3.0, shifted -54.26251
+.....\ip a
+....\vbox(68.26251+98.15005)x36.63556
+.....\kern7.0
+.....\rule(7.0+0.0)x*
+.....\kern1.56248
+.....\hbox(52.70003+98.15005)x36.63556
+......\hbox(0.0+0.0)x0.1, shifted -52.70003
+......\vbox(52.70003+98.15005)x36.53555
+.......\kern0.0
+.......\rule(0.0+0.0)x*
+.......\kern1.25
+.......\hbox(51.45003+98.15005)x36.53555
+........\hbox(51.45003+98.15005)x33.25778
+.........\vbox(-1.00002+22.99995)x7.0, shifted -18.99998
+..........\hbox(-1.00002+2.0)x7.0
+...........\ip
+
+..........\hbox(-1.00002+2.0)x7.0
+...........\ip
+
+..........\hbox(7.0+1.0)x3.0
+...........\ip A
+..........\hbox(-1.00002+2.0)x7.0
+...........\ip
+
+..........\hbox(-1.00002+2.0)x7.0
+...........\ip
+
+..........\hbox(8.0+2.0)x3.0
+...........\ip B
+.........\hbox(35.75003+94.55006)x8.39005
+..........\vbox(35.75003+94.55006)x8.39005
+...........\hbox(35.75003+49.05003)x8.39005
+............\hbox(0.0+0.0)x0.1, shifted -7.0
+............\vbox(35.75003+49.05003)x8.19003
+.............\hbox(14.00002+2.0)x8.19003, glue set 1.09502fil
+..............\glue 0.0 plus 1.0fil minus 1.0fil
+..............\bigtr
+p A
+..............\kern2.0
+..............\glue 0.0 plus 1.0fil minus 1.0fil
+.............\kern27.30002
+.............\hbox(8.1+33.40002)x8.19003
+..............\ip A
+..............\vbox(27.75002+13.75002)x6.19003, shifted 19.65001
+...............\hbox(0.0+0.0)x-2.51, shifted 1.0
+................\smalltrip -
+...............\kern4.0
+...............\hbox(23.75002+13.75002)x6.19003
+................\hbox(0.0+0.0)x0.1, shifted -7.0
+................\vbox(23.75002+13.75002)x6.00002
+.................\hbox(14.00002+2.0)x6.00002
+..................\bigtr
+p A
+..................\kern2.0
+..................\glue 0.0 plus 1.0fil
+.................\kern0.5
+.................\rule(0.5+0.0)x*
+.................\kern0.5
+.................\hbox(16.0+4.00002)x6.00002, glue set 0.00002fil
+..................\glue 0.0 plus 1.0fil minus 1.0fil
+..................\bigtr
+p B
+..................\glue(\nonscript)
+..................\glue 0.0 plus 1.0fil minus 1.0fil
+................\hbox(0.0+0.0)x0.1, shifted -7.0
+............\hbox(0.0+0.0)x0.1, shifted -7.0
+...........\kern27.30002
+...........\rule(9.1+0.0)x*
+.........\vbox(146.40009+3.2)x11.86772, shifted 94.95006
+..........\hbox(16.0+4.00002)x5.99
+...........\bigtr
+p b
+..........\kern106.40007
+..........\hbox(20.0+3.2)x11.86772
+...........\hbox(16.0+4.00002)x6.0, shifted -1.0
+............\bigtr
+p b
+...........\vbox(20.0+3.2)x5.77771
+............\hbox(8.0+2.0)x5.77771
+.............\hbox(0.0+0.0)x0.0, shifted -7.0
+.............\glue(\nonscript)
+.............\glue(\nonscript)
+.............\glue 2.49994 minus 1.0fil
+.............\glue(\thinmuskip) 0.27777 plus 2.0fill minus 0.83331
+.............\ip B
+............\kern2.0
+............\rule(2.0+0.0)x*
+............\kern9.2
+............\hbox(0.0+0.0)x5.77771
+...........\hbox(0.0+0.0)x0.1, shifted -7.0
+.........\discretionary
+..........\kern-1.00002
+..........\ip -
+.........|\smalltrip A
+.........\hbox(14.00002+2.0)x6.00002, shifted -0.99998
+..........\bigtr
+p a
+........\glue(\thinmuskip) 0.27777 plus 2.0fill minus 0.83331
+........\ip A
+........\kern1.0
+..\rule(*+*)x5.0
+.\penalty 10000
+.\glue(\lineskip) 0.0 plus 40.0
+.\hbox(20.5+50.80002)x8.27895, shifted 19.72105
+..\ip -
+..\smalltrip b
+..\hbox(3.5+0.5)x1.5
+...\smalltrip A
+...\kern0.5
+..\vbox(26.0+4.00002)x1.99, shifted 10.8
+...\hbox(7.0+1.0)x1.99
+....\ip A
+...\kern2.0
+...\hbox(16.0+4.00002)x0.99
+....\bigtr
+p B
+....\ip -
+..\hbox(20.5+50.80002)x8.28894
+...\hbox(20.5+50.80002)x8.28894
+....\hbox(0.0+0.0)x0.1, shifted -7.0
+....\vbox(20.5+50.80002)x8.08893
+.....\hbox(0.0+0.0)x8.08893
+.....\kern9.0
+.....\rule(9.0+0.0)x*
+.....\kern9.0
+.....\hbox(20.65001+23.65001)x8.08893
+......\hbox(20.65001+23.65001)x8.08893
+.......\hbox(20.65001+23.65001)x8.08893
+........\hbox(0.0+0.0)x0.1, shifted -7.0
+........\vbox(20.65001+23.65001)x7.88892
+.........\hbox(0.0+0.0)x7.88892
+.........\kern9.1
+.........\rule(9.1+0.0)x*
+.........\kern9.1
+.........\hbox(12.0+5.0)x7.88892
+..........\hbox(12.0+5.0)x7.88892
+...........\hbox(8.0+2.0)x3.0, shifted -4.0
+............\ip b
+...........\vbox(11.0+5.0)x1.88892
+............\hbox(0.0+0.0)x1.88892
+.............\ip p
+.............\ip t
+.............\ip s
+.............\ip q
+.............\ip t
+.............\kern3.0
+.............\ip q
+.............\ip p
+.............\glue(\thickmuskip) -1.11108
+.............\ip u
+............\kern7.0
+............\hbox(9.0+0.0)x1.88892, glue set 0.74446fil
+.............\glue 0.0 plus 1.0fil minus 1.0fil
+.............\rule(9.0+*)x0.4
+.............\glue 0.0 plus 1.0fil minus 1.0fil
+...........\hbox(8.0+2.0)x3.0, shifted -4.0
+............\ip b
+........\hbox(0.0+0.0)x0.1, shifted -7.0
+......\hbox(0.0+0.0)x0.0
+....\hbox(0.0+0.0)x0.1, shifted -7.0
+.\penalty 7
+
+Memory usage before: 2170&521; after: 1181&459; still untouched: 175
+{restoring \box254=void}
+{restoring \hoffset=0.0pt}
+{restoring \showboxdepth=1}
+{restoring \showboxbreadth=2}
+{restoring \tracingcommands=2}
+{restoring \parshape=10}
+{restoring \hangafter=-12}
+{restoring \hangindent=-10.0pt}
+{restoring \looseness=-2}
+{horizontal mode: \relax}
+{\parshape}
+{\leftskip}
+{\spacefactor}
+{\raise}
+{restricted horizontal mode: \special}
+{blank space }
+{end-group character }}
+{horizontal mode: blank space }
+{\penalty}
+{\showbox}
+> \box0=
+\vbox(0.0+0.0)x0.0
+.\penalty 999
+
+! OK.
+<to be read again>
+ \spacefactor
+l.289 \showbox0\spacefactor
+ =0
+
+{\spacefactor}
+! Bad space factor (0).
+l.289 \showbox0\spacefactor=0
+
+I allow only values in the range 1..32767 here.
+
+{\write}
+{\par}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 12.0 t=100000782 -> @@0
+@\penalty via @@0 b=10000 p=-10000 d=100000782
+@@2: line 12.0 t=100000782 -> @@0
+@\penalty via @@1 b=10000 p=-10000 d=100000000
+@@3: line 13.0 t=200000782 -> @@1
+[]
+@\par via @@2 b=10000 p=-10000 d=100000000
+@@4: line 13.0- t=200000782 -> @@2
+@\par via @@3 b=10000 p=-10000 d=100000000
+@@5: line 14.0- t=300000782 -> @@3
+
+
+Underfull \hbox (badness 10000) in paragraph at lines 285--290
+ []
+
+\hbox(1.0+0.0)x90.0, glue set 41.0
+.\glue(\leftskip) 0.0 plus -10.0fil
+.\hbox(0.0+0.0)x4.0, shifted -1.0 []
+.etc.
+
+
+Underfull \hbox (badness 10000) in paragraph at lines 285--290
+ []
+
+\hbox(0.0+0.0)x100.0
+.\glue(\leftskip) 0.0 plus -10.0fil
+.\write10{\the \spacefactor }
+.etc.
+
+%% goal height=16383.99998, max depth=2.0
+% t=20.0 plus 1.0fil g=16383.99998 b=0 p=125 c=125#
+{vertical mode: end-group character }}
+! Extra }, or forgotten \endgroup.
+l.291 }
+ % this fails to match \begingroup
+I've deleted a group-closing symbol because it seems to be
+spurious, as in `$x}$'. But perhaps the } is legitimate and
+you forgot something else, as in `\hbox{$x}'. In such cases
+the way to recover is to insert both the forgotten and the
+deleted material, e.g., by typing `I$}'.
+
+{blank space }
+{\aftergroup}
+{\aftergroup}
+{\endgroup}
+{restoring \hangafter=-12}
+{restoring \hangindent=-10.0pt}
+{restoring \leftskip=3.0pt}
+{restoring \parshape=10}
+{restoring \rightskip=0.0pt}
+{restoring \looseness=0}
+{\lccode}
+{\mark}
+! Improper \spacefactor.
+l.293 \mark{\the\spacefactor
+ } % \spacefactor: not in vertical...
+You can refer to \spacefactor only in horizontal mode;
+you can refer to \prevdepth only in vertical mode; and
+neither of these is meaningful inside \write. So
+I'm forgetting what you said and using zero instead.
+
+{blank space }
+{math shift character $}
+% t=30.0 plus 41.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+{horizontal mode: math shift character $}
+@firstpass
+@secondpass
+[]
+@\par via @@0 b=10000 p=-10000 d=*
+@@1: line 1.0- t=0 -> @@0
+
+
+Underfull \hbox (badness 10000) in paragraph at lines 294--294
+ []
+
+\hbox(0.0+0.0)x11.0
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{display math mode: \global}
+{\mskip}
+{\catcode}
+{\catcode}
+{\the}
+{\def}
+{blank space }
+{\vtop}
+{internal vertical mode: \everydisplay}
+{\vbox}
+{end-group character }}
+{\noindent}
+{horizontal mode: math shift character $}
+\everydisplay->\global
+{display math mode: \global}
+{\lowercase}
+{the letter a}
+{the letter a}
+
+j->\relax
+{\relax}
+{math shift character $}
+{internal vertical mode: \ifvmode}
+{true}
+{restoring \displayindent=3.0pt}
+{restoring \displaywidth=13.0pt}
+{restoring \predisplaysize=24.0pt}
+{restoring \fam=-1}
+{horizontal mode: \fi}
+{end-group character }}
+{restoring \everydisplay=}
+{restoring \parshape=10}
+{restoring \hangafter=-12}
+{restoring \hangindent=-10.0pt}
+{display math mode: \hss}
+{\leqno}
+{math mode: \mathchardef}
+! Missing control sequence inserted.
+<inserted text>
+ \inaccessible
+...
+l.298 \leqno\mathchardef A
+ \/\left(\over\left(\global\errorcon...
+Please don't say `\def cs{...}', say `\def\cs{...}'.
+I've inserted an inaccessible control sequence so that your
+definition will be completed without mixing me up too badly.
+You can recover graciously from this error, if you're
+careful; see exercise 27.2 in The TeXbook.
+
+! Missing number, treated as zero.
+<to be read again>
+ A
+l.298 \leqno\mathchardef A
+ \/\left(\over\left(\global\errorcon...
+A number should have been here; I inserted `0'.
+(If you can't figure out why I needed to see a number,
+look up `weird error' in the index to The TeXbook.)
+
+{the letter A}
+{\/}
+{\left}
+
+(->\delimiter "4162362
+{\over}
+{\left}
+
+(->\delimiter "4162362
+{\global}
+{math shift character $}
+! Missing \right. inserted.
+<inserted text>
+ \right .
+<to be read again>
+ $
+l.298 ...bal\errorcontextlines5$
+ $
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{\right}
+{math shift character $}
+! Missing \right. inserted.
+<inserted text>
+ \right .
+<to be read again>
+ $
+l.298 ...bal\errorcontextlines5$
+ $
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{\right}
+{math shift character $}
+{restoring \inaccessible=undefined}
+{restoring \fam=-1}
+{restoring j=undefined}
+{restoring \catcode106=11}
+{restoring \catcode74=11}
+{restoring \displayindent=0.0pt}
+{restoring \displaywidth=0.0pt}
+{restoring \predisplaysize=0.0pt}
+{restoring \fam=0}
+% t=40.0 plus 124.0 plus 1.0fil minus 8.0 g=16383.99998 b=0 p=0 c=0#
+% t=66.00002 plus 164.0 plus 1.0fil minus 8.0 g=16383.99998 b=0 p=-11796
+47 c=-1179647#
+\output->{\tracingcommands 0\showthe \outputpenalty \showboxbreadth 9999
+ \showboxdepth 9999 \hoffset 1sp {\setbox 254=\box 255\shipout \ifvbox 2
+\ifhbox 254 \error \fi 54\copy 25\fi 4} \ifvoid 254\relax \else \error \
+fi }
+{internal vertical mode: \tracingcommands}
+> -1179647.
+<output> ...wthe \outputpenalty
+ \showboxbreadth 9999 \showbox...
+l.298 ...al\errorcontextlines5$$
+
+
+
+Completed box being shipped out [-2.0.0.0.11.0.327680.1572864.0.-1073741
+823]
+\vbox(16383.99998+2.0)x100.0, glue set 16318.0fil
+.\glue(\topskip) 19.0 plus 1.0fil
+.\hbox(1.0+0.0)x90.0, glue set 41.0
+..\glue(\leftskip) 0.0 plus -10.0fil
+..\hbox(0.0+0.0)x4.0, shifted -1.0
+...\special{-12}
+...\glue 4.0 plus 2.0 minus 1.0
+..\glue 4.0 plus 2.0 minus 1.0
+..\penalty -10000
+..\glue(\rightskip) 0.0 plus 10.0fil minus 0.00002
+.\penalty 125
+.\glue(\baselineskip) 10.0 plus 41.0
+.\hbox(0.0+0.0)x100.0
+..\glue(\leftskip) 0.0 plus -10.0fil
+..\write10{\the \spacefactor }
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0 plus 10.0fil minus 0.00002
+.\mark{0}
+.\glue(\parskip) 0.0 plus 42.0 minus 8.0
+.\glue(\baselineskip) 10.0 plus 41.0
+.\hbox(0.0+0.0)x11.0, shifted 1.0
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x0.0
+..\penalty 10000
+..\glue(\parfillskip) 0.0
+..\glue(\rightskip) 0.0
+.\penalty 0
+.\glue(\abovedisplayskip) 3.0
+.\glue(\lineskip) 0.0 plus 40.0
+.\hbox(12.0+13.00002)x13.0, shifted 3.0
+..\hbox(12.0+4.0)x8.1778
+...\ip A
+...\kern1.0
+...\kern0.0
+...\glue(\thinmuskip) 0.27777 plus 2.0fill minus 0.83331
+...\hbox(12.0+4.0)x4.90002
+....\hbox(8.0+2.0)x3.0, shifted -4.0
+.....\ip b
+....\hbox(9.2+4.0)x1.80002
+.....\hbox(0.0+0.0)x0.1, shifted -7.0
+.....\vbox(9.2+4.0)x1.6
+......\hbox(0.0+0.0)x1.6
+......\kern1.7
+......\rule(1.0+0.0)x*
+......\kern1.0
+......\hbox(9.5+0.0)x1.6
+.......\hbox(4.0+1.0)x1.5, shifted -5.5
+........\smalltrip b
+.......\hbox(0.0+0.0)x0.1, shifted -7.0
+.....\hbox(0.0+0.0)x0.1, shifted -7.0
+....\hbox(0.0+0.0)x0.1, shifted -7.0
+..\kern5.0
+..\hbox(0.0+13.00002)x-0.1778, glue set - 57.67767fil
+...\glue 4.99988 minus 4.99988
+...\vbox(-0.00002+13.00002)x52.5, glue set - 0.00247
+....\vbox(-0.00002+0.0)x0.0
+....\glue(\parskip) 0.0 plus 42.0 minus 8.0
+....\penalty 0
+....\glue(\abovedisplayshortskip) 1.0 plus 45.0 minus 803.0
+....\glue(\baselineskip) 3.0 plus 41.0
+....\hbox(7.0+1.0)x5.0, shifted 47.5
+.....\ip a
+.....\ip a
+.....\kern1.0
+....\penalty -1179647
+....\glue(\belowdisplayshortskip) 3.0 plus 46.0
+...\glue 0.0 plus 1.0fil minus 1.0fil
+
+\write->\the \spacefactor
+! Improper \spacefactor.
+<write> \the \spacefactor
+
+<inserted text>
+ }\endwrite
+<to be read again>
+ }
+<output> ...\fi 54\copy 25\fi 4}
+ \ifvoid 254\relax \else \err...
+l.298 ...al\errorcontextlines5$$
+
+You can refer to \spacefactor only in horizontal mode;
+you can refer to \prevdepth only in vertical mode; and
+neither of these is meaningful inside \write. So
+I'm forgetting what you said and using zero instead.
+
+0
+Memory usage before: 640&415; after: 397&410; still untouched: 175
+{restoring \box254=void}
+{restoring \hoffset=0.0pt}
+{restoring \showboxdepth=1}
+{restoring \showboxbreadth=2}
+{restoring \tracingcommands=2}
+{restoring \parshape=10}
+{restoring \hangafter=-12}
+{restoring \hangindent=-10.0pt}
+{horizontal mode: \par}
+{vertical mode: \hangindent}
+{\par}
+{\showthe}
+> 0.0pt.
+l.300 ...\par\showthe\hangindent
+ \hangindent 254cm
+
+{\hangindent}
+{\parfillskip}
+{\fontdimen}
+{\the}
+{\the}
+{the character 0}
+{horizontal mode: the character 0}
+Missing character: There is no 0 in font trip!
+{the letter A}
+{blank space }
+{\char}
+{math shift character $}
+@firstpass
+@secondpass
+[]\ip A
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+^^82
+@\par via @@0 b=62 p=-10000 d=3969
+@@2: line 1.1- t=3969 -> @@0
+
+
+Loose \hbox (badness 62) in paragraph at lines 302--303
+ []\ip A ^^82
+
+\hbox(7.0+1.0)x100.0, glue set 0.85295
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+%% goal height=16383.99998, max depth=2.0
+{display math mode: \global}
+{\leqno}
+{math mode: \kern}
+{math shift character $}
+! Display math should end with $$.
+<to be read again>
+ \par
+l.303 ...e\leqno\kern1009pt$\par
+
+The `$' that I just saw supposedly matches a previous `$$'.
+So I shall assume that you typed `$$' both times.
+
+{restoring \fam=-1}
+{restoring \displayindent=0.0pt}
+{restoring \displaywidth=0.0pt}
+{restoring \predisplaysize=0.0pt}
+{restoring \fam=0}
+% t=20.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+% t=40.0 plus 82.0 plus 1.0fil g=16383.99998 b=0 p=-1179647 c=-1179647#
+\output->{\tracingcommands 0\showthe \outputpenalty \showboxbreadth 9999
+ \showboxdepth 9999 \hoffset 1sp {\setbox 254=\box 255\shipout \ifvbox 2
+\ifhbox 254 \error \fi 54\copy 25\fi 4} \ifvoid 254\relax \else \error \
+fi }
+{internal vertical mode: \tracingcommands}
+> -1179647.
+<output> ...wthe \outputpenalty
+ \showboxbreadth 9999 \showbox...
+<to be read again>
+ \par
+l.303 ...e\leqno\kern1009pt$\par
+
+
+
+Completed box being shipped out [-2.0.0.0.11.0.327680.1572864.1073741823
+.-1073741823]
+\vbox(16383.99998+0.0)x8236.0, glue set 16344.0fil
+.\glue(\topskip) 13.0 plus 1.0fil
+.\hbox(7.0+1.0)x100.0, glue set 0.85295
+..\glue(\leftskip) 3.0
+..\hbox(0.0+0.0)x0.0
+..\ip A
+..\glue 4.0 plus 1.99799 minus 1.00099
+..\ip ^^82
+..\penalty 10000
+..\glue(\parfillskip) 0.0 plus 100.0
+..\glue(\rightskip) 0.0
+.\penalty 0
+.\glue(\baselineskip) 9.0 plus 41.0
+.\hbox(0.0+0.0)x1009.0, shifted 7227.0
+..\kern 1009.0
+.\penalty 10000
+.\glue(\baselineskip) 10.0 plus 41.0
+.\hbox(0.0+0.0)x-7127.0, shifted 7227.0
+
+Memory usage before: 235&401; after: 178&399; still untouched: 175
+{restoring \box254=void}
+{restoring \hoffset=0.0pt}
+{restoring \showboxdepth=1}
+{restoring \showboxbreadth=2}
+{restoring \tracingcommands=2}
+{restoring \hangindent=7227.0pt}
+{horizontal mode: \par}
+{vertical mode: \showlists}
+
+### vertical mode entered at line 0
+prevdepth 0.0, prevgraf 4 lines
+
+! OK.
+l.304 \showlists
+ {\catcode`!13\global\everyhbox{\def!{}}}
+
+{begin-group character {}
+{\catcode}
+{\global}
+{end-group character }}
+{restoring \catcode33=12}
+{blank space }
+{\count}
+{\baselineskip}
+{begin-group character {}
+{\sfcode}
+{\vfuzz}
+{\everyvbox}
+{\vbox}
+\everyvbox->
+{internal vertical mode: blank space }
+{\hsize}
+{\tolerance}
+{the letter A}
+{horizontal mode: the letter A}
+{blank space }
+{the letter A}
+{blank space }
+{the letter A}
+{blank space }
+{the letter A}
+{blank space }
+{the letter A}
+{\clubpenalty}
+{\par}
+@firstpass
+@secondpass
+[]\ip A A A
+@ via @@0 b=* p=0 d=*
+@@1: line 1.3 t=0 -> @@0
+A A
+@\par via @@1 b=12 p=-10000 d=*
+@@2: line 2.2- t=0 -> @@1
+
+
+Overfull \hbox (3.0pt too wide) in paragraph at lines 308--308
+ []\ip A A A|
+
+\hbox(7.0+1.0)x10.0, glue set - 1.0
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+
+Tight \hbox (badness 12) in paragraph at lines 308--308
+ \ip A A
+
+\hbox(7.0+1.0)x10.0, glue set - 0.5
+.\glue(\leftskip) 3.0
+.\ip A
+.etc.
+
+{internal vertical mode: \hbadness}
+{\hfuzz}
+{the letter A}
+{horizontal mode: the letter A}
+{blank space }
+{the letter A}
+{blank space }
+{the letter A}
+{blank space }
+{the letter A}
+{blank space }
+{the letter A}
+{\leaders}
+{\par}
+@firstpass
+@secondpass
+[]\ip A A A
+@ via @@0 b=* p=0 d=*
+@@1: line 1.3 t=0 -> @@0
+A A
+@\par via @@1 b=12 p=-10000 d=*
+@@2: line 2.2- t=0 -> @@1
+
+{internal vertical mode: end-group character }}
+{restoring \hfuzz=0.0pt}
+{restoring \hbadness=0}
+{restoring \clubpenalty=125}
+{restoring \tolerance=10000}
+{restoring \hsize=100.0pt}
+
+Overfull \vbox (18.0pt too high) detected at line 309
+
+\vbox(11.0+1.0)x10.0, glue set - 1.0
+.\hbox(7.0+1.0)x10.0, glue set - 1.0 []
+.\penalty 10000
+.etc.
+
+%% goal height=16383.99998, max depth=2.0
+{vertical mode: blank space }
+{\message}
+1000000
+{end-group character }}
+{restoring \everyvbox=}
+{restoring \vfuzz=0.0pt}
+{restoring \sfcode65=999}
+{blank space }
+{\vbox}
+{internal vertical mode: \hbadness}
+{\hfuzz}
+{\hbox}
+\everyhbox->\def !{}
+{restricted horizontal mode: \def}
+{\hskip}
+{end-group character }}
+{restoring !=undefined}
+
+Overfull \hbox (1.0pt too wide) detected at line 311
+
+
+\hbox(0.0+0.0)x0.0, glue set - 1.0
+.\glue 10.0 minus 9.0
+
+{internal vertical mode: blank space }
+{\hbadness}
+{\hbox}
+\everyhbox->\def !{}
+{restricted horizontal mode: \def}
+{\hskip}
+{end-group character }}
+{restoring !=undefined}
+
+Underfull \hbox (badness 101) detected at line 312
+
+
+\hbox(0.0+0.0)x10.03749, glue set 1.00375
+.\glue 0.0 plus 10.0
+
+{internal vertical mode: \tracingcommands}
+{\message}
+101
+{end-group character }}
+{restoring \tracingcommands=2}
+{restoring \hfuzz=0.0pt}
+{restoring \hbadness=0}
+% t=20.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+{vertical mode: \lineskiplimit}
+{\everyhbox}
+{blank space }
+{\def}
+{blank space }
+{\dimendef}
+{\dimen}
+{\vbox}
+{internal vertical mode: \tracinglostchars}
+{the letter A}
+{horizontal mode: the letter A}
+{\/}
+
+\space ->
+{blank space }
+
+\space ->
+{blank space }
+{\ignorespaces}
+
+\space ->
+
+\space ->
+{the letter J}
+{blank space }
+{\vskip}
+{\par}
+@firstpass
+@secondpass
+[]\ip A
+@\kern via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+
+@\par via @@0 b=56 p=-10000 d=3249
+@\par via @@1 b=10000 p=-10000 d=100000000
+@@2: line 1.1- t=3249 -> @@0
+
+
+Loose \hbox (badness 56) in paragraph at lines 315--316
+ []\ip A
+
+\hbox(7.0+1.0)x100.0, glue set 0.82695
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{internal vertical mode: \vskip}
+{\moveleft}
+{\boxmaxdepth}
+{\mark}
+{end-group character }}
+{restoring \boxmaxdepth=1000.0pt}
+
+Underfull \vbox (badness 10000) detected at line 316
+
+\vbox(10.0+-1.0)x0.0
+.\mark{vii}
+
+{\vskip}
+{\unskip}
+{\setbox}
+{\showthe}
+> -1.0pt.
+l.317 ...astbox\showthe\lastskip
+ % \lastskip=-1pt (\baselines...
+
+{\unskip}
+{\vskip}
+{\kern}
+{\penalty}
+{\showbox}
+> \box22=
+\vbox(10.0+-1.0)x0.0
+.\mark{vii}
+
+! OK.
+<to be read again>
+ }
+l.318 ...lty\lastkern\showbox22}
+
+
+{end-group character }}
+{restoring \box22=void}
+{restoring \tracinglostchars=2}
+
+Underfull \vbox (badness 10000) detected at line 318
+
+\vbox(11.0+0.0)x100.0
+.\hbox(7.0+1.0)x100.0, glue set 0.82695 []
+.\glue 2.0
+.etc.
+
+% t=31.0 plus 40.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+{vertical mode: blank space }
+{\showbox}
+> \box22=void
+
+! OK.
+<to be read again>
+ \kern
+l.319 \showbox22\kern
+ 3pt\message{\the\lastkern}\unkern
+
+{\kern}
+{\message}
+3.0pt
+{\unkern}
+{\show}
+> \botmark=\botmark:
+0.
+l.320 \show\botmark
+ \catcode`;13\def;{\setbox`; }
+
+{\catcode}
+{\def}
+{blank space }
+{\lineskiplimit}
+
+\space ->
+
+\space ->
+{blank space }
+{\count}
+{\vbox}
+
+\space ->
+{internal vertical mode: \accent}
+{horizontal mode: \accent}
+Missing character: There is no ^^c8 in font trip!
+
+\space ->
+{blank space }
+{\accent}
+
+\space ->
+
+;->\setbox `;
+! Improper \setbox.
+<to be read again>
+ \char
+l.322 ...2 \def\^^M{\ } ; \char
+ '101
+Sorry, \setbox is not allowed after \halign in a display,
+or between \accent and an accented character.
+
+{the letter A}
+{\ }
+{\fontdimen}
+{\ }
+{\spaceskip}
+
+\^^M->\
+{\ }
+{\vskip}
+{\par}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+[]\ip AA
+@ via @@0 b=10000 p=0 d=100000782
+@ via @@1 b=10000 p=0 d=100000000
+@@2: line 1.0 t=100000782 -> @@0
+
+@\par via @@0 b=44 p=-10000 d=2025
+@\par via @@1 b=54 p=-10000 d=3025
+@\par via @@2 b=10000 p=-10000 d=100000000
+@@3: line 1.1- t=2025 -> @@0
+
+
+Loose \hbox (badness 44) in paragraph at lines 322--324
+ [] []\ip AA
+
+\hbox(10.50002+1.0)x100.0, glue set 0.76414
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{internal vertical mode: \vskip}
+{end-group character }}
+{restoring \spaceskip=0.0pt}
+{restoring \^^M=undefined}
+
+Overfull \vbox (0.50002pt too high) detected at line 324
+
+\vbox(11.0+0.0)x100.0, glue set - 1.0
+.\hbox(10.50002+1.0)x100.0, glue set 0.76414 []
+.\glue 10.0 minus 10.0
+
+% t=41.0 plus 40.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+{vertical mode: blank space }
+{\penalty}
+% t=52.0 plus 80.0 plus 1.0fil g=16383.99998 b=0 p=-2147483647 c=-214748
+3647#
+\output->{\tracingcommands 0\showthe \outputpenalty \showboxbreadth 9999
+ \showboxdepth 9999 \hoffset 1sp {\setbox 254=\box 255\shipout \ifvbox 2
+\ifhbox 254 \error \fi 54\copy 25\fi 4} \ifvoid 254\relax \else \error \
+fi }
+{internal vertical mode: \tracingcommands}
+> -2147483647.
+<output> ...wthe \outputpenalty
+ \showboxbreadth 9999 \showbox...
+l.325 \penalty-2147483647
+ % that's the largest value TeX will...
+
+
+Completed box being shipped out [-2.0.0.0.11.196608.327680.1572864.10737
+41823]
+\vbox(16383.99998+0.0)x100.0, glue set 16332.0fil
+.\glue(\topskip) 9.0 plus 1.0fil
+.\vbox(11.0+1.0)x10.0, glue set - 1.0
+..\hbox(7.0+1.0)x10.0, glue set - 1.0
+...\glue(\leftskip) 3.0
+...\hbox(0.0+0.0)x0.0
+...\ip A
+...\glue 4.0 plus 1.0 minus 2.0
+...\ip A
+...\glue 4.0 plus 1.0 minus 2.0
+...\ip A
+...\glue(\rightskip) 0.0
+...\rule(*+*)x5.0
+..\penalty 10000
+..\glue(\baselineskip) 2.0
+..\hbox(7.0+1.0)x10.0, glue set - 0.5
+...\glue(\leftskip) 3.0
+...\ip A
+...\glue 4.0 plus 1.0 minus 2.0
+...\ip A
+...\penalty 10000
+...\glue(\parfillskip) 0.0 plus 100.0
+...\glue(\rightskip) 0.0
+..\glue(\parskip) 0.0 plus 42.0 minus 8.0
+..\glue(\baselineskip) 2.0
+..\hbox(7.0+1.0)x10.0, glue set - 1.0
+...\glue(\leftskip) 3.0
+...\hbox(0.0+0.0)x0.0
+...\ip A
+...\glue 4.0 plus 1.0 minus 2.0
+...\ip A
+...\glue 4.0 plus 1.0 minus 2.0
+...\ip A
+...\glue(\rightskip) 0.0
+..\penalty 10000
+..\glue(\baselineskip) 2.0
+..\hbox(7.0+1.0)x10.0, glue set - 0.5
+...\glue(\leftskip) 3.0
+...\ip A
+...\glue 4.0 plus 1.0 minus 2.0
+...\ip A
+...\penalty 10000
+...\glue(\parfillskip) 0.0 plus 100.0
+...\glue(\rightskip) 0.0
+.\glue(\lineskip) 0.0 plus 40.0
+.\vbox(10.0+0.0)x10.03749
+..\hbox(0.0+0.0)x0.0, glue set - 1.0
+...\glue 10.0 minus 9.0
+..\glue(\baselineskip) 10.0
+..\hbox(0.0+0.0)x10.03749, glue set 1.00375
+...\glue 0.0 plus 10.0
+.\glue(\baselineskip) -1.0
+.\vbox(11.0+0.0)x100.0
+..\hbox(7.0+1.0)x100.0, glue set 0.82695
+...\glue(\leftskip) 3.0
+...\hbox(0.0+0.0)x0.0
+...\ip A
+...\kern 1.0
+...\glue 4.0 plus 1.99799 minus 1.00099
+...\glue 4.0 plus 1.99799 minus 1.00099
+...\penalty 10000
+...\glue(\parfillskip) 0.0 plus 100.0
+...\glue(\rightskip) 0.0
+..\glue 2.0
+..\glue -2.0
+..\kern 0.0
+..\penalty 0
+.\glue(\lineskip) 0.0 plus 40.0
+.\vbox(11.0+0.0)x100.0, glue set - 1.0
+..\hbox(10.50002+1.0)x100.0, glue set 0.76414
+...\glue(\leftskip) 3.0
+...\hbox(0.0+0.0)x0.0
+...\glue 4.0 plus 2.0 minus 1.0
+...\kern -5.50003 (for accent)
+...\hbox(8.0+2.0)x3.0, shifted -2.50002
+....\ip B
+...\kern 2.50003 (for accent)
+...\ip A
+...\ip A
+...\glue 4.0 plus 2.0 minus 1.0
+...\glue 4.0 plus 2.0 minus 88.0
+...\penalty 10000
+...\glue(\parfillskip) 0.0 plus 100.0
+...\glue(\rightskip) 0.0
+..\glue 10.0 minus 10.0
+
+Memory usage before: 651&432; after: 417&418; still untouched: 175
+{restoring \box254=void}
+{restoring \hoffset=0.0pt}
+{restoring \showboxdepth=1}
+{restoring \showboxbreadth=2}
+{restoring \tracingcommands=2}
+{vertical mode: \penalty}
+! Number too big.
+l.326 \penalty-2147483648
+ % see?
+I can only go up to 2147483647='17777777777="7FFFFFFF,
+so I'm using that number instead of yours.
+
+{\tabskip}
+{\let}
+{\count}
+{begin-group character {}
+{\errhelp}
+{\errmessage}
+! .
+l.328 ...l is lost}\errmessage{}
+ }
+all is lost
+
+{end-group character }}
+{restoring \errhelp=}
+{blank space }
+{\def}
+{blank space }
+{\looseness}
+{\setbox}
+{internal vertical mode: \vskip}
+{end-group character }}
+{restoring \looseness=-1}
+{vertical mode: blank space }
+{\halign}
+{internal vertical mode: \iftrue}
+{true}
+{\else}
+
+\d #1\d ->#1#1
+#1<-##
+! Only one # is allowed per tab.
+<argument> ##
+
+\d #1\d ->#1#1
+
+l.333 ...7200bp minus 4\wd4\d#\d
+ \cr % \d#\d becomes (erroneou...
+There should be exactly one # between &'s, when an
+\halign or \valign is being set up. In this case you had
+more than one, so I'm ignoring all but the first.
+
+{restricted horizontal mode: \global}
+{\spaceskip}
+{\def}
+{\def}
+{\expandafter}
+
+\A ->B
+
+\xx ->\global \gdef \A {\global \count \count 1=####\cr \omit \cr \tabsk
+ip }
+{\global}
+{the letter B}
+{end of alignment template}
+{\vbox}
+{internal vertical mode: \halign}
+
+\A ->\global \count \count 1=##\cr \omit \cr \tabskip
+{restricted horizontal mode: end of alignment template}
+{\global}
+{end of alignment template}
+{restoring \tabskip=4.49998pt plus 7227.0pt}
+{internal vertical mode: the letter A}
+{horizontal mode: the letter A}
+{end-group character }}
+@firstpass
+@secondpass
+[]\ip AA
+@\par via @@0 b=86 p=-10000 d=*
+@@1: line 1.1- t=0 -> @@0
+
+
+Loose \hbox (badness 86) in paragraph at lines 337--337
+ []\ip AA
+
+\hbox(7.0+1.0)x100.0, glue set 0.95
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{restoring \looseness=-1}
+{restricted horizontal mode: blank space }
+{end of alignment template}
+{restoring \xx=undefined}
+{retaining \A=macro:->\global \count \count 1=##\cr \ETC.}
+{restoring \spaceskip=0.0pt}
+{\valign}
+{internal vertical mode: the letter A}
+{horizontal mode: the letter A}
+Missing character: There is no } in font trip!
+{end of alignment template}
+@firstpass
+@secondpass
+[]\ip A
+@\par via @@0 b=86 p=-10000 d=*
+@@1: line 1.1- t=0 -> @@0
+
+
+Loose \hbox (badness 86) in paragraph at lines 337--337
+ []\ip A
+
+\hbox(7.0+1.0)x100.0, glue set 0.95
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{internal vertical mode: end of alignment template}
+{restoring \looseness=-1}
+{begin-group character {}
+{blank space }
+{end-group character }}
+{end of alignment template}
+{end of alignment template}
+{restoring \looseness=-1}
+
+Overfull \vbox (21.99997pt too high) in alignment at lines 337--337
+
+\vbox(-5.0+0.0)x0.0
+.\glue(\tabskip) 4.49998 plus 7227.0
+.\unsetbox(0.0+0.0)x0.0
+.etc.
+
+{restricted horizontal mode: end of alignment template}
+{\global}
+{\fontname}
+{\romannumeral}
+{blank space }
+{\lccode}
+
+\A ->\uppercase { \message {trip at 5.0pt\ip mix}\lowercase {vq}}
+{\uppercase}
+{blank space }
+{\message}
+TRIP AT 5.0PT\ip AIX
+{\lowercase}
+{the letter v}
+Missing character: There is no v in font trip!
+{the letter b}
+{blank space }
+{end of alignment template}
+{math shift character $}
+{math mode: math shift character $}
+{restoring \fam=0}
+{restricted horizontal mode: end of alignment template}
+
+\A ->\uppercase { \message {trip at 5.0pt\ip mix}\lowercase {vq}}
+{\hss}
+{\uppercase}
+{blank space }
+{\message}
+TRIP AT 5.0PT\ip AIX
+{\lowercase}
+{the letter v}
+Missing character: There is no v in font trip!
+{the letter b}
+{blank space }
+{end of alignment template}
+{restoring \lccode81=113}
+{\show}
+> \A=macro:
+->\uppercase { \message {trip at 5.0pt\ip mix}\lowercase {vq}} .
+<template> \A
+ \endtemplate
+l.340 ...\omit$$\span\A&\show\cr
+ \omit\cr
+
+{end of alignment template}
+{end of alignment template}
+{internal vertical mode: \global}
+{\errmessage}
+! \count 2=-1118806.
+<recently read> }
+
+l.341 ...ge{\count2=\the\count2}
+ }
+This error message was generated by an \errmessage
+command, so I can't give any explicit help.
+Pretend that you're Hercule Poirot: Examine all clues,
+and deduce the truth by order and method.
+
+{end-group character }}
+{restoring \looseness=-1}
+{restricted horizontal mode: \mark}
+{end of alignment template}
+{\mark}
+{end of alignment template}
+{restoring \tabskip=0.0154pt minus 3.21002pt}
+
+Tight \hbox (badness 12) in alignment at lines 331--342
+ [] [] [] []
+
+\hbox(0.0+0.0)x205.12613, glue set - 0.5
+.\glue(\tabskip) 0.0154 minus 3.21002
+.\unsetbox(0.0+0.0)x0.0
+.etc.
+
+%% goal height=16383.99998, max depth=2.0
+% t=20.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+% t=30.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+% t=40.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+{vertical mode: blank space }
+{\errmessage}
+! \prevdepth =0.0pt.
+l.343 ...evdepth=\the\prevdepth}
+
+(That was another \errmessage.)
+
+{blank space }
+{\penalty}
+% t=40.0 plus 40.0 plus 1.0fil g=16383.99998 b=0 p=-88888 c=-88888#
+\output->{\tracingcommands 0\showthe \outputpenalty \showboxbreadth 9999
+ \showboxdepth 9999 \hoffset 1sp {\setbox 254=\box 255\shipout \ifvbox 2
+\ifhbox 254 \error \fi 54\copy 25\fi 4} \ifvoid 254\relax \else \error \
+fi }
+{internal vertical mode: \tracingcommands}
+> -88888.
+<output> ...wthe \outputpenalty
+ \showboxbreadth 9999 \showbox...
+l.344 \penalty-88888
+ % end alignment test, now miscellaneous ...
+
+
+Completed box being shipped out [-2.2.-1118806.0.11.196608.327680.157286
+4.1073741823]
+\vbox(16383.99998+0.0)x205.12613, glue set 16344.0fil
+.\glue(\topskip) 0.0 plus 1.0fil
+.\hbox(20.0+2.0)x205.12613, glue set - 0.5
+..\glue(\tabskip) 0.0154 minus 3.21002
+..\hbox(20.0+2.0)x0.0, glue set - 1.0
+...\ip B
+...\vbox(20.0+1.0)x100.0
+....\hbox(0.0+0.0)x0.0, glue set 0.00174
+.....\glue(\tabskip) 4.49998 plus 7227.0
+.....\hbox(0.0+0.0)x0.0
+.....\glue(\tabskip) -17.07162
+....\glue(\baselineskip) 10.0
+....\hbox(0.0+0.0)x0.0, glue set 0.00174
+.....\glue(\tabskip) 4.49998 plus 7227.0
+.....\hbox(0.0+0.0)x0.0
+.....\glue(\tabskip) -17.07162
+....\glue(\parskip) 0.0 plus 42.0 minus 8.0
+....\glue(\baselineskip) 3.0
+....\hbox(7.0+1.0)x100.0, glue set 0.95
+.....\glue(\leftskip) 3.0
+.....\hbox(0.0+0.0)x0.0
+.....\ip A (ligature AA)
+.....\penalty 10000
+.....\glue(\parfillskip) 0.0 plus 100.0
+.....\glue(\rightskip) 0.0
+...\glue(\spaceskip) 4.0 minus 0.00002
+..\glue(\tabskip) 0.0154 minus 3.21002
+..\hbox(0.0+0.0)x106.9846
+..\glue(\tabskip) 0.0154 minus 3.21002
+..\hbox(20.0+2.0)x100.0
+...\vbox(-5.0+0.0)x100.0
+....\glue(\tabskip) 4.49998 plus 7227.0
+....\vbox(0.0+0.0)x100.0
+.....\hbox(7.0+1.0)x100.0, glue set 0.95
+......\glue(\leftskip) 3.0
+......\hbox(0.0+0.0)x0.0
+......\ip A
+......\penalty 10000
+......\glue(\parfillskip) 0.0 plus 100.0
+......\glue(\rightskip) 0.0
+....\glue(\tabskip) 0.0
+....\vbox(8.0+0.0)x0.0
+....\glue(\tabskip) 4.49998 plus 7227.0
+...\vbox(-5.0+0.0)x0.0
+....\glue(\tabskip) 4.49998 plus 7227.0
+....\vbox(0.0+0.0)x0.0
+....\glue(\tabskip) 0.0
+....\vbox(8.0+0.0)x0.0
+....\glue(\tabskip) 4.49998 plus 7227.0
+..\glue(\tabskip) 4.49998 plus 7227.0
+.\glue(\baselineskip) 0.0
+.\hbox(8.0+2.0)x205.12613, glue set - 0.5
+..\glue(\tabskip) 0.0154 minus 3.21002
+..\hbox(8.0+2.0)x0.0, glue set 177.80537fil
+...\glue 4.0 plus 2.0 minus 88.0
+...\glue 4.0 plus 2.0 minus 88.0
+...\ip b
+...\glue 4.0 plus 2.0 minus 88.0
+...\mathon
+...\mathoff
+...\glue 0.0 plus 1.0fil minus 1.0fil
+...\glue 4.0 plus 2.0 minus 88.0
+...\ip b
+...\glue 4.0 plus 2.0 minus 88.0
+..\glue(\tabskip) 0.0154 minus 3.21002
+..\hbox(0.0+0.0)x106.9846
+..\glue(\tabskip) 0.0154 minus 3.21002
+..\hbox(0.0+0.0)x100.0
+..\glue(\tabskip) 4.49998 plus 7227.0
+..\hbox(8.0+2.0)x0.0
+..\glue(\tabskip) 0.0154 minus 3.21002
+.\glue(\baselineskip) 8.0
+.\hbox(0.0+0.0)x205.12613, glue set - 0.5
+..\glue(\tabskip) 0.0154 minus 3.21002
+..\hbox(0.0+0.0)x0.0
+..\glue(\tabskip) 0.0154 minus 3.21002
+.\glue(\lineskip) 0.0 plus 40.0
+.\hbox(0.0+0.0)x205.12613, glue set - 0.5
+..\glue(\tabskip) 0.0154 minus 3.21002
+..\hbox(0.0+0.0)x0.0
+..\glue(\tabskip) 0.0154 minus 3.21002
+..\hbox(0.0+0.0)x106.9846
+..\glue(\tabskip) 0.0154 minus 3.21002
+.\mark{a}
+.\mark{b}
+
+Memory usage before: 776&458; after: 464&452; still untouched: 175
+{restoring \box254=void}
+{restoring \hoffset=0.0pt}
+{restoring \showboxdepth=1}
+{restoring \showboxbreadth=2}
+{restoring \tracingcommands=2}
+{restoring \looseness=-1}
+{vertical mode: \newlinechar}
+{\global}
+!
+ou can't use a prefix with `\unskip'.
+<to be read again>
+ \unskip
+l.345 ...inechar`
+\global\unskip
+ \show^^
+\newlinechar\lastpena...
+I'll pretend you didn't say \long or \outer or \global.
+
+{\unskip}
+{\show}
+> the character ^^Y.
+l.345 ...
+\global\unskip\show^^
+
+ \newlinechar\lastpenalty\unpe...
+
+{\newlinechar}
+{\unpenalty}
+! You can't use `\unpenalty' in vertical mode.
+l.345 ...r\lastpenalty\unpenalty
+ \unkern
+Sorry...I usually can't take things from the current page.
+Perhaps you can make the output routine do it.
+
+{\unkern}
+! You can't use `\unkern' in vertical mode.
+l.345 ...enalty\unpenalty\unkern
+
+Sorry...I usually can't take things from the current page.
+Try `I\kern-\lastkern' instead.
+
+{\lastbox}
+! You can't use `\lastbox' in vertical mode.
+l.346 \lastbox
+ \penalty5\message{\the\lastpenalty\the\newlinec...
+Sorry...I usually can't take things from the current page.
+This \lastbox will therefore be void.
+
+{\penalty}
+{\message}
+510000
+{\textfont}
+! Bad number (16).
+<to be read again>
+ =
+l.346 ...ewlinechar}\textfont16=
+ \relax
+Since I expected to read a number between 0 and 15,
+I changed this one to zero.
+
+! Missing font identifier.
+<to be read again>
+ \relax
+l.346 ...char}\textfont16=\relax
+
+I was looking for a control sequence whose
+current meaning has been defined by \font.
+
+{\relax}
+{\outer}
+! Missing control sequence inserted.
+<inserted text>
+ \inaccessible
+<to be read again>
+ {
+l.347 \outer\def{
+ }?
+Please don't say `\def cs{...}', say `\def\cs{...}'.
+I've inserted an inaccessible control sequence so that your
+definition will be completed without mixing me up too badly.
+You can recover graciously from this error, if you're
+careful; see exercise 27.2 in The TeXbook.
+
+{the character ?}
+{horizontal mode: the character ?}
+Missing character: There is no ? in font trip!
+{blank space }
+{\dimen}
+{\showthe}
+> -16383.99998pt.
+l.348 ...77777sp\showthe\dimen5
+ % this should be OK
+
+{\dimen}
+! Dimension too large.
+<to be read again>
+ \showthe
+l.349 \dimen6=-'40000pt\showthe
+ \dimen6 % this should overflow
+I can't work with sizes bigger than about 19 feet.
+Continue and I'll use the largest value I can.
+
+{\showthe}
+> -16383.99998pt.
+l.349 ...40000pt\showthe\dimen6
+ % this should overflow
+
+{\dimen}
+{\showthe}
+> -8355.74998pt.
+l.350 ...\dimen5\showthe\dimen7
+ \multiply\dimen7 2\showthe\di...
+
+{\multiply}
+! Arithmetic overflow.
+<to be read again>
+ \showthe
+l.350 ...ltiply\dimen7 2\showthe
+ \dimen7
+I can't carry out that multiplication or division,
+since the result is out of range.
+
+{\showthe}
+> -8355.74998pt.
+l.350 ...dimen7 2\showthe\dimen7
+
+
+{undefined}
+! Undefined control sequence.
+l.351 \a^^@^^@a
+ @ % an undefined control sequence followed by ...
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Text line contains an invalid character.
+l.351 \a^^@^^@a@
+ % an undefined control sequence followed by ...
+A funny symbol that I can't read has just been input.
+Continue, and I'll forget that it ever happened.
+
+{begin-group character {}
+{\aftergroup}
+{\aftergroup}
+{\gdef}
+{blank space }
+{\def}
+{blank space }
+
+\b ->\c
+
+\c ->
+{end-group character }}
+{restoring \c=undefined}
+
+\gobble #1->
+#1<-\c
+{blank space }
+{\def}
+{blank space }
+{\outer}
+{\toks0}
+Runaway text?
+! Forbidden control sequence found while scanning text of \tokens.
+<inserted text>
+ }
+<to be read again>
+ \a^^@^^@a
+l.354 ...ar#2{}\tokens{\a^^@^^@a
+ \par!
+I suspect you have forgotten a `}', causing me
+to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+
+\a^^@^^@a #1\par #2->
+#1<-
+#2<-!
+{blank space }
+{\long}
+{blank space }
+{\outer}
+! Parameters must be numbered consecutively.
+<to be read again>
+ 8
+l.356 ...ef\lo#1#2U3#4#5#6#7#8#8
+ #9#{\relax}
+I've inserted the digit you should have used after the #.
+Type `1' to delete what you did use.
+
+! You already have nine parameters.
+l.356 ...\lo#1#2U3#4#5#6#7#8#8#9
+ #{\relax}
+I'm going to ignore the # sign you just used.
+
+{blank space }
+{\ifcase}
+{case 1}
+
+\l #1->
+#1<-\par
+
+\b #1\par ->
+Runaway argument?
+{
+! Paragraph ended before \b was complete.
+<to be read again>
+ \par
+l.357 ...defined\or\l\par\b{\par
+ % occurrence of \par aborts \b
+I suspect you've forgotten a `}', causing me to apply this
+control sequence to too much text. How can we recover?
+My plan is to forget the whole thing and hope for the best.
+
+{\par}
+@firstpass
+@secondpass
+[]
+@ via @@0 b=10000 p=0 d=100000782
+@@1: line 1.0 t=100000782 -> @@0
+
+@\par via @@0 b=22 p=-10000 d=529
+@@2: line 1.1- t=529 -> @@0
+@\par via @@1 b=10000 p=-10000 d=100000000
+@@3: line 2.0- t=200000782 -> @@1
+
+
+Loose \hbox (badness 22) in paragraph at lines 347--357
+ []
+
+\hbox(0.0+0.0)x100.0, glue set 0.60527
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+%% goal height=16383.99998, max depth=2.0
+
+\b #1\par ->
+#1<-\l \undefined
+{vertical mode: \else}
+{\ifcase}
+{\iftrue}
+{true}
+{case -1}
+{\ifcase}
+{\fi}
+{case 5}
+{\fi}
+{\catcode}
+{\let}
+{\def}
+{blank space }
+{\halign}
+{internal vertical mode: \ifcase}
+{case 3}
+! Incomplete \ifcase; all text was ignored after line 363.
+<inserted text>
+ \fi
+<to be read again>
+ \lo
+l.363 \^^C{{\span\ifcase3 \lo
+ #\cr............89{}\cr} % runaw...
+A forbidden control sequence occurred in skipped text.
+This kind of error happens when you say `\if...' and forget
+the matching `\fi'. I've inserted a `\fi'; this might work.
+
+Runaway preamble?
+{
+! Forbidden control sequence found while scanning preamble of \^^C.
+<inserted text>
+ \cr }
+<to be read again>
+ \lo
+l.363 \^^C{{\span\ifcase3 \lo
+ #\cr............89{}\cr} % runaw...
+I suspect you have forgotten a `}', causing me
+to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+! Missing # inserted in alignment preamble.
+<to be read again>
+ \cr
+<inserted text> \cr
+ }
+<to be read again>
+ \lo
+l.363 \^^C{{\span\ifcase3 \lo
+ #\cr............89{}\cr} % runaw...
+There should be exactly one # between &'s, when an
+\halign or \valign is being set up. In this case you had
+none, so I've put one in; maybe that will work.
+
+
+\lo #1#2U3#4#5#6#7#8#989{->\relax {
+#1<-##
+#2<-\cr
+U3<-.
+#4<-.
+#5<-.
+#6<-.
+#7<-.
+#8<-.
+#9<-......
+{vertical mode: \relax}
+{begin-group character {}
+{end-group character }}
+{\cr}
+! Misplaced \cr.
+l.363 ...#\cr............89{}\cr
+ } % runaway preamble?
+I can't figure out why you would want to use a tab mark
+or \cr or \span just now. If something like a right brace
+up above has ended a previous alignment prematurely,
+you're probably due for more error messages, and you
+might try typing `S' now just to see what is salvageable.
+
+{end-group character }}
+! Too many }'s.
+l.363 ...\cr............89{}\cr}
+ % runaway preamble?
+You've closed more groups than you opened.
+Such booboos are generally harmless, so keep going.
+
+{blank space }
+{\def}
+! Illegal parameter number in definition of \a.
+<to be read again>
+ 2
+l.364 \def\a^^C1{\d#1\d\l{#2
+ }\l#1\par\a^^@^^@a#1\par# % runaw...
+You meant to type ## instead of #, right?
+Or maybe a } was forgotten somewhere earlier, and things
+are all screwed up? I'm going to assume that you meant ##.
+
+Runaway definition?
+^^C1->\d ^^C1\d \l {##2}\l ^^C1\par
+! Forbidden control sequence found while scanning definition of \a.
+<inserted text>
+ }
+<to be read again>
+ \a^^@^^@a
+l.364 ...\l{#2}\l#1\par\a^^@^^@a
+ #1\par# % runaway in definiti...
+I suspect you have forgotten a `}', causing me
+to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+
+\a^^@^^@a #1\par #2->
+#1<-##1
+#2<-##
+{blank space }
+{\xdef}
+
+\d #1\d ->#1#1
+#1<-##1
+
+\l #1->
+#1<-##2
+
+\l #1->
+#1<-##
+Runaway definition?
+^^C1->^^C1^^C11\par
+! Forbidden control sequence found while scanning definition of \a.
+<inserted text>
+ }
+<to be read again>
+ \a^^@^^@a
+l.365 ...\l{#2}\l#1\par\a^^@^^@a
+ #1\par# % runaway in definiti...
+I suspect you have forgotten a `}', causing me
+to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+
+\a^^@^^@a #1\par #2->
+#1<-##1
+#2<-##
+{blank space }
+
+\T 12#101001{->-.#1pt{
+Runaway argument?
+! Forbidden control sequence found while scanning use of \T.
+<inserted text>
+ \par
+<to be read again>
+ \a^^@^^@a
+l.366 \T^^?a^^@^^@a
+ \par{\lo\par % runaway in use
+I suspect you have forgotten a `}', causing me
+to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+! Use of \T doesn't match its definition.
+<inserted text>
+ \par
+<to be read again>
+ \a^^@^^@a
+l.366 \T^^?a^^@^^@a
+ \par{\lo\par % runaway in use
+If you say, e.g., `\def\a1{...}', then you must always
+put `1' after `\a', since control sequence names are
+made up of letters only. The macro here has not been
+followed by the required stuff, so I'm ignoring it.
+
+{\par}
+
+\a^^@^^@a #1\par #2->
+#1<-
+Runaway argument?
+{
+! Forbidden control sequence found while scanning use of \a^^@^^@a.
+<inserted text>
+ \par
+<to be read again>
+ \lo
+l.366 \T^^?a^^@^^@a\par{\lo
+ \par % runaway in use
+I suspect you have forgotten a `}', causing me
+to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+
+\lo #1#2U3#4#5#6#7#8#989{->\relax {
+#1<-\par
+Runaway argument?
+! Forbidden control sequence found while scanning use of \lo.
+<inserted text>
+ \par
+<to be read again>
+ \lo
+l.367 \lo
+ \par\par\par P \par\par\par\par\par\par89{} \muski...
+I suspect you have forgotten a `}', causing me
+to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+
+\lo #1#2U3#4#5#6#7#8#989{->\relax {
+#1<-\par
+#2<-\par
+U3<-\par
+#4<-P
+#5<-\par
+#6<-\par
+#7<-\par
+#8<-\par
+#9<-\par \par
+{\relax}
+{begin-group character {}
+{end-group character }}
+{blank space }
+{\muskip}
+{\muskipdef}
+{\muskip3}
+{\showthe}
+> 5.0mu plus 4.0mu minus 2.0mu.
+l.368 ....5\shmip \showthe\shmip
+
+
+{begin-group character {}
+{\advance}
+! Incompatible glue units.
+l.369 {\advance\shmip by \shkip
+ \endlinechar-1
+I'm going to assume that 1mu=1pt when they're mixed.
+
+{\endlinechar}
+{\divide}
+{\endlinechar}
+{\global}
+{\showthe}
+> 0.0mu minus -0.00003fil.
+l.372 \showthe\shmip
+
+
+{end-group character }}
+{restoring \endlinechar=13}
+{retaining \muskip3=0.0mu minus -0.00003fil}
+{\divide}
+! Arithmetic overflow.
+l.374 By ^^p
+ \toks1={\a\test}
+I can't carry out that multiplication or division,
+since the result is out of range.
+
+{\toks}
+{blank space }
+{superscript character ^}
+! Missing $ inserted.
+<inserted text>
+ $
+<to be read again>
+ ^
+l.375 ^
+ \leaders\vrule\mskip\shmip M\leaders\hrule\nonscript\h...
+I've inserted a begin-math/end-math symbol since I think
+you left one out. Proceed, with fingers crossed.
+
+{math shift character $}
+% t=20.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+{horizontal mode: math shift character $}
+{math mode: superscript character ^}
+! Missing { inserted.
+<to be read again>
+ \leaders
+l.375 ^\leaders
+ \vrule\mskip\shmip M\leaders\hrule\nonscript\h...
+A left brace was mandatory here, so I've put one in.
+You might want to delete and/or insert some corrections
+so that I will find a matching right brace soon.
+(If you're confused by all this, try typing `I}' now.)
+
+{\leaders}
+! Leaders not followed by proper glue.
+<to be read again>
+ \mskip
+l.375 ^\leaders\vrule\mskip
+ \shmip M\leaders\hrule\nonscript\h...
+You should say `\leaders <box or rule><hskip or vskip>'.
+I found the <box or rule>, but there's no suitable
+<hskip or vskip>, so I'm ignoring these leaders.
+
+{\mskip}
+{the letter M}
+{\leaders}
+! Leaders not followed by proper glue.
+<to be read again>
+ \nonscript
+l.375 ...leaders\hrule\nonscript
+ \hskip\thinmuskip
+You should say `\leaders <box or rule><hskip or vskip>'.
+I found the <box or rule>, but there's no suitable
+<hskip or vskip>, so I'm ignoring these leaders.
+
+{\nonscript}
+{\hskip}
+! Incompatible glue units.
+l.375 ...script\hskip\thinmuskip
+
+I'm going to assume that 1mu=1pt when they're mixed.
+
+{\par}
+! Missing $ inserted.
+<inserted text>
+ $
+<to be read again>
+ \par
+l.376
+
+I've inserted a begin-math/end-math symbol since I think
+you left one out. Proceed, with fingers crossed.
+
+{math shift character $}
+! Missing } inserted.
+<inserted text>
+ }
+<to be read again>
+ $
+<to be read again>
+ \par
+l.376
+
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{end-group character }}
+{math shift character $}
+{restoring \fam=0}
+{horizontal mode: \par}
+@firstpass
+@secondpass
+[]$[]$
+@\par via @@0 b=80 p=-10000 d=*
+@@1: line 1.1- t=0 -> @@0
+
+
+Loose \hbox (badness 80) in paragraph at lines 375--376
+ []$[]$
+
+\hbox(8.2+0.0)x100.0, glue set 0.9301
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{vertical mode: begin-group character {}
+{\setbox}
+{restricted horizontal mode: \vfill}
+! Missing } inserted.
+<inserted text>
+ }
+<to be read again>
+ \vfill
+l.377 {\setbox3\hbox{\vfill
+ \vsplit 3 0pt}
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{end-group character }}
+{vertical mode: \vfill}
+{\vsplit}
+! Missing `to' inserted.
+<to be read again>
+ 0
+l.377 ...\hbox{\vfill\vsplit 3 0
+ pt}
+I'm working on `\vsplit<box number> to <dimen>';
+will look for the <dimen> next.
+
+! \vsplit needs a \vbox.
+<to be read again>
+ }
+l.377 ...ox{\vfill\vsplit 3 0pt}
+
+The box you are trying to split is an \hbox.
+I can't split such a box, so I'll leave it alone.
+
+{end-group character }}
+{restoring \box3=
+\vbox(0.0+-8.53581)x0.0 []}
+{blank space }
+{\def}
+! Parameters must be numbered consecutively.
+<to be read again>
+ 2
+l.378 \def\a#2
+ {}
+I've inserted the digit you should have used after the #.
+Type `1' to delete what you did use.
+
+{blank space }
+{\show}
+> the letter A.
+l.379 \show A
+
+
+{blank space }
+{\show}
+> \a^^@^^@a=\outer macro:
+#1\par #2->.
+l.380 \show\a^^@^^@a
+
+
+{\show}
+> (=macro:
+->\delimiter "4162362 .
+l.381 \show (
+
+
+{blank space }
+{\message}
+{\meaning}
+{\noexpand}
+\long\outer macro:#1#2U3#4#5#6#7#8#989{->\relax {\lo
+{blank space }
+{\show}
+> \^^C=\halign.
+l.383 \show\^^C
+
+
+{blank space }
+{\show}
+> \batchmode=\batchmode.
+l.384 \show\batchmode
+
+
+{\show}
+> \error=undefined.
+l.385 \show\error
+
+
+{\showthe}
+> {\tracingcommands 0\showthe \outputpenalty \showboxbreadth 9999 \showb
+oxdepth 9999 \hoffset 1sp {\setbox 254=\box 255\shipout \ifvbox 2\ifhbox
+ 254 \error \fi 54\copy 25\fi 4} \ifvoid 254\relax \else \error \fi }.
+l.386 \showthe\output
+
+
+{\showthe}
+> 1.0mu plus 2.0fill minus 3.0mu.
+l.387 \showthe\thinmuskip
+
+
+{\showthe}
+> -2.0pt.
+<recently read> \enorm
+
+l.388 \showthe\fontdimen1\enorm
+
+
+{\ifx}
+{false}
+{\par}
+% t=30.0 plus 42.0 plus 1.0fil minus 8.0 g=16383.99998 b=0 p=0 c=0#
+{\if}
+{\else}
+{true}
+{\else}
+{\fi}
+{\ifdim}
+{\iftrue}
+{true}
+! Missing = inserted for \ifdim.
+<to be read again>
+ 1
+l.390 \ifdim72p\iftrue t1
+ i\fi n\fi\fi \message{\jobname\ifx\l...
+I was expecting to see `<', `=', or `>'. Didn't.
+
+{\fi}
+{\fi}
+{false}
+{\fi}
+! Extra \fi.
+l.390 ...p\iftrue t1i\fi n\fi\fi
+ \message{\jobname\ifx\lo\lo ...
+I'm ignoring this; it doesn't match any \if.
+
+{\message}
+{\jobname}
+{\ifx}
+{true}
+tripOK
+{\fi}
+{\hangindent}
+{begin-group character {}
+{\if}
+{true}
+{blank space }
+{\prevgraf}
+{\if}
+{false}
+! Bad \prevgraf (-1).
+<to be read again>
+ \relax
+l.392 ...f 0123\error\else\relax
+ \fi\else\error\fi
+I allow only nonnegative values here.
+
+{\relax}
+{\fi}
+{\else}
+{\prevgraf}
+{\global}
+{end-group character }}
+{\showthe}
+> 2.
+l.393 ...er=2}\showthe\hangafter
+ \showthe\prevgraf
+
+{\showthe}
+> 1.
+l.393 ...gafter\showthe\prevgraf
+
+
+{\char}
+{horizontal mode: \char}
+Missing character: There is no ^^83 in font trip!
+{\showthe}
+> 0.
+l.394 \char'203\showthe\prevgraf
+ $\indent\mark{twain}
+
+{math shift character $}
+{math mode: \indent}
+{\mark}
+{blank space }
+{\setbox}
+{restricted horizontal mode: \vrule}
+{end-group character }}
+{math mode: alignment tab character &}
+! Misplaced alignment tab character &.
+l.395 \setbox3\hbox{\vrule}&
+ \moveleft\lastbox % can't do that...
+I can't figure out why you would want to use a tab mark
+here. If you just want an ampersand, the remedy is
+simple: Just type `I\&' now. But if some right brace
+up above has ended a previous alignment prematurely,
+you're probably due for more error messages, and you
+might try typing `S' now just to see what is salvageable.
+
+{\moveleft}
+! You can't use `\moveleft' in math mode.
+l.395 ...\hbox{\vrule}&\moveleft
+ \lastbox % can't do that in m...
+Sorry, but I'm not programmed to handle this case;
+I'll just pretend that you didn't ask for it.
+If you're in the wrong mode, you might be able to
+return to the right one by typing `I}' or `I$' or `I\par'.
+
+{\lastbox}
+! You can't use `\lastbox' in math mode.
+l.395 ...rule}&\moveleft\lastbox
+ % can't do that in math mode
+Sorry; this \lastbox will be void.
+
+{\unhbox}
+{\unhcopy}
+! Incompatible list can't be unboxed.
+<to be read again>
+ \accent
+l.396 \unhbox234\unhcopy3\accent
+ \x\vfill\vfil\vfilneg\vss % \...
+Sorry, Pandora. (You sneaky devil.)
+I refuse to unbox an \hbox in vertical mode or vice versa.
+And I can't open any boxes in math mode.
+
+{\accent}
+! Please use \mathaccent for accents in math mode.
+<recently read> \accent
+
+l.396 \unhbox234\unhcopy3\accent
+ \x\vfill\vfil\vfilneg\vss % \...
+I'm changing \accent to \mathaccent here; wish me luck.
+(Accents are not the same in formulas as they are in text.)
+
+! Missing { inserted.
+<to be read again>
+ \vfill
+l.396 ...unhcopy3\accent\x\vfill
+ \vfil\vfilneg\vss % \vfill ex...
+A left brace was mandatory here, so I've put one in.
+You might want to delete and/or insert some corrections
+so that I will find a matching right brace soon.
+(If you're confused by all this, try typing `I}' now.)
+
+{\vfill}
+! Missing $ inserted.
+<inserted text>
+ $
+<to be read again>
+ \vfill
+l.396 ...unhcopy3\accent\x\vfill
+ \vfil\vfilneg\vss % \vfill ex...
+I've inserted a begin-math/end-math symbol since I think
+you left one out. Proceed, with fingers crossed.
+
+{math shift character $}
+! Missing } inserted.
+<inserted text>
+ }
+<to be read again>
+ $
+<to be read again>
+ \vfill
+l.396 ...unhcopy3\accent\x\vfill
+ \vfil\vfilneg\vss % \vfill ex...
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{end-group character }}
+{math shift character $}
+! \textfont 0 is undefined (character ^^c8).
+<recently read> $
+
+<to be read again>
+ \vfill
+l.396 ...unhcopy3\accent\x\vfill
+ \vfil\vfilneg\vss % \vfill ex...
+Somewhere in the math formula just ended, you used the
+stated character from an undefined font family. For example,
+plain TeX doesn't allow \it or \sl in subscripts. Proceed,
+and I'll try to forget that I needed that character.
+
+{restoring \box3=
+\vbox(0.0+-8.53581)x0.0 []}
+{restoring \fam=0}
+{horizontal mode: \vfill}
+{\par}
+@firstpass
+@secondpass
+[]$[][][]$
+@\par via @@0 b=91 p=-10000 d=*
+@@1: line 1.1- t=0 -> @@0
+
+
+Loose \hbox (badness 91) in paragraph at lines 394--396
+ []$[][]$
+
+\hbox(0.0+0.0)x100.0, glue set 0.97
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{vertical mode: \vfill}
+{\vfil}
+{\vfilneg}
+{\vss}
+{\def}
+! Missing { inserted.
+l.397 \def\a}
+ {\let\a\xyzzy\csname a\endcsname}
+Where was the left brace? You said something like `\def\a}',
+which I'm going to interpret as `\def\a{}'.
+
+{begin-group character {}
+{\let}
+{\csname}
+{\relax}
+{end-group character }}
+{restoring \a=macro:->}
+{blank space }
+{\def}
+{\def}
+{\let}
+{\def}
+{blank space }
+{\ifx}
+{false}
+{\expandafter}
+
+\b ->\a \c
+{\ifx}
+{true}
+{\ifinner}
+{false}
+{\relax}
+{\fi}
+{\else}
+{\fi}
+{\ifvmode}
+{true}
+{math shift character $}
+% t=40.0 plus 84.0 plus 1.0fil plus 1.0fill minus 16.0 g=16383.99998 b=0
+ p=0 c=0#
+! Infinite glue shrinkage found on current page.
+<to be read again>
+ $
+l.402 \ifvmode$
+ \ifmmode\hbox tt\ifhmode\hfilneg\else\error\fi...
+The page about to be output contains some infinitely
+shrinkable glue, e.g., `\vss' or `\vskip 0pt minus 1fil'.
+Such glue doesn't belong there; but you can safely proceed,
+since the offensive shrinkability has been made finite.
+
+{horizontal mode: math shift character $}
+{math mode: \ifmmode}
+{true}
+{\hbox}
+! Missing { inserted.
+<to be read again>
+ t
+<to be read again>
+ t
+l.402 \ifvmode$\ifmmode\hbox tt
+ \ifhmode\hfilneg\else\error\fi...
+A left brace was mandatory here, so I've put one in.
+You might want to delete and/or insert some corrections
+so that I will find a matching right brace soon.
+(If you're confused by all this, try typing `I}' now.)
+
+{restricted horizontal mode: the letter t}
+{\ifhmode}
+{true}
+{\hfilneg}
+{\else}
+{end-group character }}
+{math mode: math shift character $}
+{restoring \fam=0}
+{horizontal mode: \fi}
+{\fi}
+{\noalign}
+! Misplaced \noalign.
+l.403 \noalign
+ \omit\endcsname % these are extra
+I expect to see \noalign only after the \cr of
+an alignment. Proceed, and I'll ignore this case.
+
+{\omit}
+! Misplaced \omit.
+l.403 \noalign\omit
+ \endcsname % these are extra
+I expect to see \omit only after tab marks or the \cr of
+an alignment. Proceed, and I'll ignore this case.
+
+{\endcsname}
+! Extra \endcsname.
+l.403 \noalign\omit\endcsname
+ % these are extra
+I'm ignoring this, since I wasn't doing a \csname.
+
+{\fontdimen}
+! Missing font identifier.
+<to be read again>
+ =
+l.404 \fontdimen 1000=
+ 20\varunit\showthe\fontdimen1000\trip\l...
+I was looking for a control sequence whose
+current meaning has been defined by \font.
+
+! Font \FONT? has only 12 fontdimen parameters.
+<to be read again>
+ =
+l.404 \fontdimen 1000=
+ 20\varunit\showthe\fontdimen1000\trip\l...
+To increase the number of font parameters, you must
+use \fontdimen immediately after the \font is loaded.
+
+{\showthe}
+! Font \ip has only 13 fontdimen parameters.
+<recently read> \trip
+
+l.404 ...wthe\fontdimen1000\trip
+ \let\PAR=\par
+To increase the number of font parameters, you must
+use \fontdimen immediately after the \font is loaded.
+
+> 0.0pt.
+<recently read> \trip
+
+l.404 ...wthe\fontdimen1000\trip
+ \let\PAR=\par
+
+{\let}
+{\gdef}
+{\expandafter}
+{\csname}
+{\ifx}
+{true}
+{\mag}
+
+\par ->\relax \PAR
+{\relax}
+{\par}
+@firstpass
+@secondpass
+[]$[]$
+@\par via @@0 b=91 p=-10000 d=*
+@@1: line 1.1- t=0 -> @@0
+
+
+Loose \hbox (badness 91) in paragraph at lines 402--406
+ []$[]$
+
+\hbox(0.0+0.0)x100.0, glue set 0.97
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{vertical mode: \fi}
+{\noindent}
+% t=50.0 plus 126.0 plus 2.0fil plus 2.0fill minus 25.0 g=16383.99998 b=
+0 p=0 c=0#
+{horizontal mode: begin-group character {}
+{\halign}
+
+\par ->\relax \PAR
+{\relax}
+{\par}
+{vertical mode: \halign}
+! Incompatible magnification (1999);
+ the previous value will be retained (2000).
+l.407 ...indent{\halign to 1true
+ mm\expandafter{\csname#\endcs...
+I can handle only one magnification ratio per job. So I've
+reverted to the magnification you used earlier on this run.
+
+{internal vertical mode: \expandafter}
+{\csname}
+{restricted horizontal mode: \relax}
+{\global}
+{undefined}
+! Undefined control sequence.
+<recently read> \foo
+
+<to be read again>
+ \endtemplate
+<template> \endtemplate
+
+l.408 ...bal\futurelet\endt\foo&
+ \show\endt&$&&&.}
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+{end of alignment template}
+{\show}
+> \endt=\outer endtemplate:
+.
+l.408 ...let\endt\foo&\show\endt
+ &$&&&.}
+
+{end of alignment template}
+
+\l #1->
+#1<-$
+{end of alignment template}
+! Extra alignment tab has been changed to \cr.
+<template> }\endtemplate
+
+l.408 ...\endt\foo&\show\endt&$&
+ &&.}
+You have given more \span or & marks than there were
+in the preamble to the \halign or \valign now in progress.
+So I'll assume that you meant to type \cr instead.
+
+{\relax}
+{end of alignment template}
+{end of alignment template}
+
+\l #1->
+#1<-.
+{blank space }
+
+\par ->\relax \PAR
+{\relax}
+{\par}
+! Missing } inserted.
+<inserted text>
+ }
+<to be read again>
+ \PAR
+l.409
+
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{end-group character }}
+! Missing \cr inserted.
+<inserted text>
+ \cr
+<to be read again>
+ }
+<to be read again>
+ \PAR
+l.409
+
+I'm guessing that you meant to end an alignment here.
+
+{\cr}
+! Missing { inserted.
+<inserted text>
+ {
+<to be read again>
+ \cr
+<to be read again>
+ }
+<to be read again>
+ \PAR
+l.409
+
+I've put in what seems to be necessary to fix
+the current column of the current alignment.
+Try to go on, since this might almost work.
+
+{begin-group character {}
+{end-group character }}
+{end of alignment template}
+
+Tight \hbox (badness 1) in alignment at lines 407--409
+ [] [] []
+
+\hbox(0.0+0.0)x1.42262, glue set - 0.20552
+.\glue(\tabskip) 0.0154 minus 3.21002
+.\unsetbox(0.0+0.0)x0.0
+.etc.
+
+% t=60.0 plus 168.0 plus 2.0fil plus 2.0fill minus 33.0 g=16383.99998 b=
+0 p=0 c=0#
+{vertical mode: \par}
+{\hbox}
+{restricted horizontal mode: \/}
+{\hrule}
+! You can't use `\hrule' here except with leaders.
+l.410 \hbox{\/\hrule
+ \textfont3=\enorm\prevdepth\advance\xspac...
+To put a horizontal rule in an hbox or an alignment,
+you should use \leaders or \hrulefill (see The TeXbook).
+
+{\textfont}
+{\prevdepth}
+! You can't use `\prevdepth' in restricted horizontal mode.
+l.410 ...tfont3=\enorm\prevdepth
+ \advance\xspaceskip by-\xspac...
+Sorry, but I'm not programmed to handle this case;
+I'll just pretend that you didn't ask for it.
+If you're in the wrong mode, you might be able to
+return to the right one by typing `I}' or `I$' or `I\par'.
+
+{\advance}
+{\spacefactor}
+{begin-group character {}
+{blank space }
+{end-group character }}
+{\everymath}
+{\fontdimen}
+{math shift character $}
+\everymath->\radical "3
+{math mode: \radical}
+{end-group character }}
+{\delimiterfactor}
+{\left}
+
+(->\delimiter "4162362
+{the letter A}
+{the letter a}
+{\right}
+{math shift character $}
+{restoring \delimiterfactor=10}
+{restoring \fam=0}
+{restricted horizontal mode: the letter A}
+{\/}
+{end-group character }}
+{restoring \everymath=}
+{restoring \xspaceskip=-1.0pt}
+{restoring \textfont3=\bigtr^^@p}
+% t=70.0 plus 168.0 plus 2.0fil plus 2.0fill minus 33.0 g=16383.99998 b=
+0 p=0 c=0#
+{vertical mode: blank space }
+{\openin}
+{\closein}
+{\iftrue}
+{true}
+{begin-group character {}
+{\ifeof}
+{true}
+{\openin}
+! Bad number (100).
+l.413 ...e{\ifeof 15\openin 100
+ tripos
+Since I expected to read a number between 0 and 15,
+I changed this one to zero.
+
+{\def}
+{blank space }
+{\catcode}
+{\catcode}
+{\outer}
+
+\loop ->\ifeof 0\let \loop =\relax \else {\global \read 0to \a }\show \a
+ \fi \loop
+{\ifeof}
+{false}
+{begin-group character {}
+{\global}
+{end-group character }}
+{\show}
+> \a=macro:
+->\par .
+\loop ...\read 0to \a }\show \a
+ \fi \loop
+l.415 ...er\def\uppercase{}\loop
+ }\else\fi
+
+{\fi}
+
+\loop ->\ifeof 0\let \loop =\relax \else {\global \read 0to \a }\show \a
+ \fi \loop
+{\ifeof}
+{false}
+{begin-group character {}
+{\global}
+Runaway definition?
+->
+! Forbidden control sequence found while scanning definition of \a.
+<inserted text>
+ }
+<read 0> \uppercase
+ {0{\outputpenalty }}
+\loop ... {\global \read 0to \a
+ }\show \a \fi \loop
+l.415 ...er\def\uppercase{}\loop
+ }\else\fi
+I suspect you have forgotten a `}', causing me
+to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+! Text line contains an invalid character.
+<read 0> \uppercase {0
+ {\outputpenalty }}
+\loop ... {\global \read 0to \a
+ }\show \a \fi \loop
+l.415 ...er\def\uppercase{}\loop
+ }\else\fi
+A funny symbol that I can't read has just been input.
+Continue, and I'll forget that it ever happened.
+
+{end-group character }}
+{\show}
+> \a=macro:
+-> .
+\loop ...\read 0to \a }\show \a
+ \fi \loop
+l.415 ...er\def\uppercase{}\loop
+ }\else\fi
+
+{\fi}
+
+\loop ->\ifeof 0\let \loop =\relax \else {\global \read 0to \a }\show \a
+ \fi \loop
+{\ifeof}
+{false}
+{begin-group character {}
+{\global}
+Runaway definition?
+->[
+! Forbidden control sequence found while scanning definition of \a.
+<inserted text>
+ }
+<read 0> [\uppercase
+ {mmmmmmmmmm}[
+\loop ... {\global \read 0to \a
+ }\show \a \fi \loop
+l.415 ...er\def\uppercase{}\loop
+ }\else\fi
+I suspect you have forgotten a `}', causing me
+to read past where you wanted me to stop.
+I'll try to recover; but if the error is serious,
+you'd better type `E' or `X' now and fix your file.
+
+Runaway definition?
+->[ }{mmmmmmmmmm}[
+! File ended within \read.
+<read 0>
+
+\loop ... {\global \read 0to \a
+ }\show \a \fi \loop
+l.415 ...er\def\uppercase{}\loop
+ }\else\fi
+This \read has unbalanced braces.
+
+{end-group character }}
+{\show}
+> \a=macro:
+->[ }{mmmmmmmmmm}[ \par .
+\loop ...\read 0to \a }\show \a
+ \fi \loop
+l.415 ...er\def\uppercase{}\loop
+ }\else\fi
+
+{\fi}
+
+\loop ->\ifeof 0\let \loop =\relax \else {\global \read 0to \a }\show \a
+ \fi \loop
+{\ifeof}
+{true}
+{\let}
+{\else}
+{\relax}
+{end-group character }}
+{restoring \uppercase=\uppercase}
+{restoring \catcode91=12}
+{restoring \catcode48=12}
+{restoring \loop=undefined}
+{\else}
+{end-group character }}
+{restoring \#=undefined}
+{\def}
+
+\test #1->\let \test =
+#1<-.
+{\let}
+{\show}
+> \test=blank space .
+l.416 ...est= }\test. \show\test
+
+
+{\def}
+{blank space }
+{\pretolerance}
+{\toks0}
+{\unhbox}
+% t=3754.40189 plus 208.0 plus 2.0fil plus 2.0fill minus 33.0 g=16383.99
+998 b=0 p=0 c=0#
+{horizontal mode: \unhbox}
+
+\par ->\relax \PAR
+{\relax}
+{\par}
+[]
+@\par via @@0 b=91 p=-10000 d=*
+@@1: line 1.1- t=0 -> @@0
+
+
+Loose \hbox (badness 91) in paragraph at lines 418--418
+ []
+
+\hbox(0.0+0.0)x100.0, glue set 0.97
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{vertical mode: \everycr}
+{blank space }
+{\the}
+
+\a #1->\ifcat #1 \message \ifx #1 {\iffalse \fi \the \tokens \fi \fi }
+#1<-\test
+{\ifcat}
+{true}
+{\message}
+{\ifx}
+{true}
+{\iffalse}
+{false}
+{\fi}
+{\fi}
+\a \test
+{\ifcase}
+{\or}
+{case 1}
+{\ifeof}
+{\fi}
+! Missing number, treated as zero.
+<to be read again>
+ \relax
+<to be read again>
+ \fi
+l.419 ...ens\ifcase1\or\ifeof\fi
+ \def\stopinput{\error\let\inp...
+A number should have been here; I inserted `0'.
+(If you can't figure out why I needed to see a number,
+look up `weird error' in the index to The TeXbook.)
+
+{true}
+{\relax}
+{\fi}
+{\def}
+{blank space }
+{\let}
+{\let}
+{\halign}
+{internal vertical mode: \iffalse}
+{false}
+\everycr->\noalign {\penalty 97}
+{\penalty}
+{end-group character }}
+{restricted horizontal mode: \cr}
+! Misplaced \cr.
+<template> \cr
+
+<to be read again>
+ \cr
+l.420 ...cr#&\ifnum0=`{\fi\cr\cr
+ }
+I can't figure out why you would want to use a tab mark
+or \cr or \span just now. If something like a right brace
+up above has ended a previous alignment prematurely,
+you're probably due for more error messages, and you
+might try typing `S' now just to see what is salvageable.
+
+{alignment tab character &}
+! Misplaced alignment tab character &.
+<template> &
+ \ifnum 0=`{\fi \endtemplate
+l.420 ...cr#&\ifnum0=`{\fi\cr\cr
+ }
+I can't figure out why you would want to use a tab mark
+here. If you just want an ampersand, the remedy is
+simple: Just type `I\&' now. But if some right brace
+up above has ended a previous alignment prematurely,
+you're probably due for more error messages, and you
+might try typing `S' now just to see what is salvageable.
+
+{\ifnum}
+{\fi}
+{false}
+{end of alignment template}
+\everycr->\noalign {\penalty 97}
+{internal vertical mode: \penalty}
+{end-group character }}
+% t=3756.40189 plus 290.0 plus 2.0fil plus 2.0fill minus 41.0 g=16383.99
+998 b=0 p=97 c=97
+% t=3766.40189 plus 290.0 plus 2.0fil plus 2.0fill minus 41.0 g=16383.99
+998 b=0 p=97 c=97
+{vertical mode: blank space }
+{\let}
+{\def}
+{\def}
+
+\unbalanced ->\halign \lb
+{\halign}
+\everycr->\noalign {\penalty 97}
+{internal vertical mode: \penalty}
+{end-group character }}
+{restricted horizontal mode: \relax}
+{\expandafter}
+{\expandafter}
+{\expandafter}
+{\expandafter}
+{\expandafter}
+{\expandafter}
+{\expandafter}
+
+\trap #1->
+#1<-\endtemplate
+{undefined}
+! Undefined control sequence.
+<recently read> \err
+
+<to be read again>
+ \endt
+<template> \endtemplate
+
+l.422 ...rr\e\e\e\endt\e\trap\cr
+ \noexpand\cr}
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+{end of alignment template}
+\everycr->\noalign {\penalty 97}
+{internal vertical mode: \penalty}
+{end-group character }}
+{\noexpand}
+{restricted horizontal mode: end of alignment template}
+\everycr->\noalign {\penalty 97}
+{internal vertical mode: \penalty}
+{end-group character }}
+% t=3766.40189 plus 290.0 plus 2.0fil plus 2.0fill minus 41.0 g=16383.99
+998 b=0 p=97 c=97
+% t=3776.40189 plus 290.0 plus 2.0fil plus 2.0fill minus 41.0 g=16383.99
+998 b=0 p=97 c=97
+% t=3786.40189 plus 290.0 plus 2.0fil plus 2.0fill minus 41.0 g=16383.99
+998 b=0 p=97 c=97
+{vertical mode: blank space }
+
+\par ->\relax \PAR
+{\relax}
+{\par}
+{\expandafter}
+{\input}
+{\endinput}
+{\input}
+(tripos.tex
+\stopinput ->\error \let \input \die
+{undefined}
+! Undefined control sequence.
+\stopinput ->\error
+ \let \input \die
+l.1
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+{\let}
+
+\par ->\relax \PAR
+{\relax}
+{\par}
+)
+{\relax}
+{undefined}
+! Undefined control sequence.
+<recently read> \input
+
+l.424 ...t tripos\endinput\input
+ % one line of tripos
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+{\setbox}
+{internal vertical mode: \hbox}
+{restricted horizontal mode: \hbox}
+{\vadjust}
+{internal vertical mode: the letter A}
+{horizontal mode: the letter A}
+{end-group character }}
+[]\ip A
+@\par via @@0 b=86 p=-10000 d=*
+@@1: line 1.1- t=0 -> @@0
+
+
+Loose \hbox (badness 86) in paragraph at lines 425--425
+ []\ip A
+
+\hbox(7.0+1.0)x100.0, glue set 0.95
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{restricted horizontal mode: end-group character }}
+{end-group character }}
+{internal vertical mode: end-group character }}
+
+Underfull \vbox (badness 10000) detected at line 425
+
+\vbox(8192.0+0.0)x0.0
+.\hbox(0.0+0.0)x0.0 []
+
+{vertical mode: \vrule}
+{horizontal mode: \vrule}
+{\unhbox}
+! Incompatible list can't be unboxed.
+<to be read again>
+ \hrule
+l.425 ...}}\vrule\unhbox10\hrule
+
+Sorry, Pandora. (You sneaky devil.)
+I refuse to unbox an \hbox in vertical mode or vice versa.
+And I can't open any boxes in math mode.
+
+{\hrule}
+
+\par ->\relax \PAR
+{\relax}
+{\par}
+[]|
+@\par via @@0 b=89 p=-10000 d=*
+@@1: line 1.1- t=0 -> @@0
+
+
+Loose \hbox (badness 89) in paragraph at lines 425--425
+ []|
+
+\hbox(0.0+0.0)x100.0, glue set 0.966
+.\glue(\leftskip) 3.0
+.\hbox(0.0+0.0)x0.0
+.etc.
+
+{vertical mode: \hrule}
+{\output}
+{\futurelet}
+{\maxdeadcycles}
+{\show}
+> \dump=the character =.
+l.427 ...xdeadcycles=3\show\dump
+
+
+{\catcode}
+{\catcode}
+{\expandafter}
+{\csname}
+{\let}
+{\relax}
+
+\csname\endcsname {->{
+! Use of \csname\endcsname doesn't match its definition.
+l.429 \relax
+ \catcode`\qq1qM=13 \defqqM{\relax}#\begingroup{\s...
+If you say, e.g., `\def\a1{...}', then you must always
+put `1' after `\a', since control sequence names are
+made up of letters only. The macro here has not been
+followed by the required stuff, so I'm ignoring it.
+
+{\catcode}
+{\def}
+{macro parameter character #}
+! You can't use `macro parameter character #' in vertical mode.
+l.429 ...^^M=13 \def^^M{\relax}#
+ \begingroup{\showboxdepth=4\s...
+Sorry, but I'm not programmed to handle this case;
+I'll just pretend that you didn't ask for it.
+If you're in the wrong mode, you might be able to
+return to the right one by typing `I}' or `I$' or `I\par'.
+
+{\begingroup}
+{begin-group character {}
+{\showboxdepth}
+{\showbox}
+> \box10=
+\vbox(8192.0+0.0)x0.0
+.\hbox(0.0+0.0)x0.0
+..\hbox(0.0+0.0)x0.0
+...\vadjust
+....\hbox(7.0+1.0)x100.0, glue set 0.95 []
+
+! OK.
+<to be read again>
+ }
+l.429 ...owboxdepth=4\showbox10}
+
+
+{end-group character }}
+{restoring \showboxdepth=1}
+
+^^M->\relax
+{\relax}
+
+^^M->\relax
+{\relax}
+{\long}
+{\immediate}
+\write->\string \caution \l
+{no mode: \string}
+
+\l #1\l ->#1
+! Argument of \l has an extra }.
+<inserted text>
+ \par
+<to be read again>
+ }
+<inserted text> }
+ \endwrite
+l.431 ...e10{\string\caution \l}
+ % living dangerously
+I've run across a `}' that doesn't seem to match anything.
+For example, `\def\a#1{...}' and `\a}' would produce
+this error. If you simply proceed now, the `\par' that
+I've just inserted will cause me to report a runaway
+argument that might be the root of the problem. But if
+your `}' was spurious, just type `2' and it will go away.
+
+Runaway argument?
+! Paragraph ended before \l was complete.
+<to be read again>
+ \par
+<to be read again>
+ }
+<inserted text> }
+ \endwrite
+l.431 ...e10{\string\caution \l}
+ % living dangerously
+I suspect you've forgotten a `}', causing me to apply this
+control sequence to too much text. How can we recover?
+My plan is to forget the whole thing and hope for the best.
+
+
+\par ->\relax \PAR
+\caution\relax \PAR
+{vertical mode: blank space }
+{\escapechar}
+{|tracingoutput}
+{|shipout}
+{internal vertical mode: |copy}
+{|box}
+{end-group character }}
+[-2.2.-1118806.0.11.196608.327680.1572864.1073741823
+! Huge page cannot be shipped out.
+<recently read> }
+
+l.432 ...ox{\copy10qq5e^5cbox10}
+
+The page just created is more than 18 feet tall or
+more than 18 feet wide, so I suspect something went wrong.
+
+The following box has been deleted:
+|vbox(16384.0+0.0)x0.0
+.|vbox(8192.0+0.0)x0.0 []
+.|glue(|lineskip) 0.0 plus 40.0
+.etc.
+
+]
+Memory usage before: 819&428; after: 720&426; still untouched: 175
+
+^^M->|relax
+{vertical mode: |relax}
+{|setbox}
+{restricted horizontal mode: |fontdimen}
+{|afterassignment}
+{|advance}
+! You can't use `|prevdepth' after |advance.
+l.434 ...relax\advance\prevdepth
+ \afterassignment\relax\future...
+I'm forgetting what you said and not changing anything.
+
+{|relax}
+{|afterassignment}
+{|futurelet}
+{|relax}
+
+^^M->|relax
+{|relax}
+{|message}
+{|noexpand}
+{|meaning}
+|l |long macro:#1|l ->#166
+{|vbox}
+{internal vertical mode: |hyphenchar}
+{|-}
+{horizontal mode: |-}
+{| }
+{the letter B}
+
+|par ->|relax |PAR
+{|relax}
+{|par}
+[]
+@|discretionary via @@0 b=10000 p=89 d=100008703
+@@1: line 1.0- t=100008703 -> @@0
+
+@ via @@0 b=10000 p=0 d=100000782
+@ via @@1 b=10000 p=0 d=100000000
+@@2: line 1.0 t=100000782 -> @@0
+|ip BBBBBB
+@|par via @@0 b=62 p=-10000 d=3969
+@|par via @@1 b=66 p=-10000 d=104489
+@|par via @@2 b=66 p=-10000 d=4489
+@@3: line 1.1- t=3969 -> @@0
+
+
+Loose \hbox (badness 62) in paragraph at lines 436--436
+ [] |ip BBBBBB
+
+|hbox(7.0+1.0)x100.0, glue set 0.85294
+.|glue(|leftskip) 3.0
+.|hbox(0.0+0.0)x0.0
+.etc.
+
+{internal vertical mode: |hyphenchar}
+{|-}
+{horizontal mode: |-}
+Missing character: There is no ? in font trip!
+{| }
+{the letter B}
+{end-group character }}
+[]
+@|discretionary via @@0 b=10000 p=89 d=100008703
+@@1: line 1.0- t=100008703 -> @@0
+
+@ via @@0 b=10000 p=0 d=100000782
+@ via @@1 b=10000 p=0 d=100000000
+@@2: line 1.0 t=100000782 -> @@0
+Missing character: There is no ? in font trip!
+|ip BBBBBB
+@|discretionary via @@0 b=10000 p=88 d=100008526
+@|discretionary via @@1 b=10000 p=88 d=100008744
+@|discretionary via @@2 b=10000 p=88 d=100007744
+@@3: line 1.0- t=100008526 -> @@0
+
+@|par via @@0 b=62 p=-10000 d=3969
+@|par via @@1 b=66 p=-10000 d=104489
+@|par via @@2 b=66 p=-10000 d=4489
+@|par via @@3 b=73 p=-10000 d=105476
+@@4: line 1.1- t=3969 -> @@0
+
+
+Loose \hbox (badness 62) in paragraph at lines 436--436
+ [] |ip BBBBBB
+
+|hbox(7.0+1.0)x100.0, glue set 0.85294
+.|glue(|leftskip) 3.0
+.|hbox(0.0+0.0)x0.0
+.etc.
+
+{restricted horizontal mode: |if}
+{|expandafter}
+
+|dol ->$
+{|noexpand}
+{true}
+{|fi}
+{|expandafter}
+{|noexpand}
+{|expandafter}
+{|noexpand}
+{undefined}
+! Undefined control sequence.
+<recently read> |undefined
+
+<to be read again>
+ |notexpanded: |expandafter
+l.437 ...ed\noexpand\expandafter
+ %
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+{|relax}
+{math shift character $}
+{math mode: |begingroup}
+{|mathop}
+{|vbox}
+{internal vertical mode: |vss}
+{end-group character }}
+{math mode: end-group character }}
+{|limits}
+{superscript character ^}
+! Missing { inserted.
+<to be read again>
+ |mathchoice
+l.438 ...ss}}\limits^\mathchoice
+ {}a}{A|{}}{\mathchoice}
+A left brace was mandatory here, so I've put one in.
+You might want to delete and/or insert some corrections
+so that I will find a matching right brace soon.
+(If you're confused by all this, try typing `I}' now.)
+
+{|mathchoice}
+{end-group character }}
+! Missing { inserted.
+<to be read again>
+ a
+l.438 ...}\limits^\mathchoice{}a
+ }{A|{}}{\mathchoice}
+A left brace was mandatory here, so I've put one in.
+You might want to delete and/or insert some corrections
+so that I will find a matching right brace soon.
+(If you're confused by all this, try typing `I}' now.)
+
+{the letter a}
+{end-group character }}
+{the letter A}
+{subscript character |}
+{end-group character }}
+{end-group character }}
+{|mathchoice}
+! Missing { inserted.
+<to be read again>
+ }
+l.438 ...{}a}{A|{}}{\mathchoice}
+
+A left brace was mandatory here, so I've put one in.
+You might want to delete and/or insert some corrections
+so that I will find a matching right brace soon.
+(If you're confused by all this, try typing `I}' now.)
+
+{end-group character }}
+
+^^M->|relax
+{end-group character }}
+{|relax}
+{begin-group character {}
+{end-group character }}
+{begin-group character {}
+{the letter B}
+{|over}
+{end-group character }}
+{|endgroup}
+! Missing } inserted.
+<inserted text>
+ }
+<to be read again>
+ |endgroup
+l.439 ...elax{}{B\over}\endgroup
+ \showlonglists$}\showboxbread...
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{end-group character }}
+! Missing { inserted.
+<to be read again>
+ |endgroup
+l.439 ...elax{}{B\over}\endgroup
+ \showlonglists$}\showboxbread...
+A left brace was mandatory here, so I've put one in.
+You might want to delete and/or insert some corrections
+so that I will find a matching right brace soon.
+(If you're confused by all this, try typing `I}' now.)
+
+{|endgroup}
+! Missing } inserted.
+<inserted text>
+ }
+<to be read again>
+ |endgroup
+l.439 ...elax{}{B\over}\endgroup
+ \showlonglists$}\showboxbread...
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{end-group character }}
+{|endgroup}
+! Missing } inserted.
+<inserted text>
+ }
+<to be read again>
+ |endgroup
+l.439 ...elax{}{B\over}\endgroup
+ \showlonglists$}\showboxbread...
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{end-group character }}
+{|endgroup}
+! Missing } inserted.
+<inserted text>
+ }
+<to be read again>
+ |endgroup
+l.439 ...elax{}{B\over}\endgroup
+ \showlonglists$}\showboxbread...
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{end-group character }}
+{|endgroup}
+
+|showlonglists ->{|tracingcommands 0|pagefillstretch -1|dimen 100 |showb
+oxbreadth 9999 |showboxdepth 9999 |showlists |pagegoal =10000pt}
+{begin-group character {}
+{|tracingcommands}
+
+### math mode entered at line 439
+### math mode entered at line 438
+|mathop|limits
+.|vbox(0.0+0.0)x0.0
+..|glue 0.0 plus 1.0fil minus 1.0fil
+^|mathchoice
+^T|mathord
+^T.|fam1 a
+^S|mathord
+^S.|fam1 A
+^S_{}
+^s|mathchoice
+^sS|mathord
+^sS.{}
+^sS|mathord
+^sS.|fraction, thickness = default
+^sS.\|mathord
+^sS.\.|fam1 B
+^sS./{}
+|mathord
+### restricted horizontal mode entered at line 433
+|vbox(17.0+1.0)x100.0
+.|hbox(7.0+1.0)x100.0, glue set 0.85294
+..|glue(|leftskip) 3.0
+..|hbox(0.0+0.0)x0.0
+..|discretionary
+..|glue 0.0 plus 2.0 minus 88.0
+..|ip A (ligature BB)
+..|kern2.0
+..|ip A (ligature BB)
+..|kern2.0
+..|ip A (ligature BB)
+..|penalty 10000
+..|glue(|parfillskip) 0.0 plus 100.0
+..|glue(|rightskip) 0.0
+.|glue(|parskip) 0.0 plus 42.0 minus 8.0
+.|glue(|baselineskip) 2.0
+.|hbox(7.0+1.0)x100.0, glue set 0.85294
+..|glue(|leftskip) 3.0
+..|hbox(0.0+0.0)x0.0
+..|discretionary
+..|glue 0.0 plus 2.0 minus 88.0
+..|ip A (ligature BB)
+..|kern2.0
+..|discretionary replacing 3
+...|ip B
+..||ip A (ligature BB)
+..||kern2.0
+..||ip B
+..|ip A (ligature BB)
+..|kern2.0
+..|ip A (ligature BB)
+..|penalty 10000
+..|glue(|parfillskip) 0.0 plus 100.0
+..|glue(|rightskip) 0.0
+spacefactor 1000
+### vertical mode entered at line 0
+### current page:
+|glue(|topskip) 20.0 plus 1.0fil
+|hbox(0.0+0.0)x100.0, glue set 0.60527
+.|glue(|leftskip) 3.0
+.|hbox(0.0+0.0)x0.0
+.|glue 4.0 plus 2.0 minus 88.0
+.|glue 4.0 plus 2.0 minus 88.0
+.|glue 4.0 plus 2.0 minus 88.0
+.|glue 4.0 plus 2.0 minus 88.0
+.|glue 4.0 plus 2.0 minus 88.0
+.|glue 4.0 plus 2.0 minus 88.0
+.|glue 4.0 plus 2.0 minus 88.0
+.|penalty 10000
+.|glue(|parfillskip) 0.0 plus 100.0
+.|glue(|rightskip) 0.0
+|glue(|parskip) 0.0 plus 42.0 minus 8.0
+|glue(|baselineskip) 1.8
+|hbox(8.2+0.0)x100.0, glue set 0.9301
+.|glue(|leftskip) 3.0
+.|hbox(0.0+0.0)x0.0
+.|mathon
+.|hbox(0.0+0.0)x3.99, shifted -8.2
+..|glue 0.0 minus -0.00003fil
+..|smalltrip M
+..|kern1.0
+..|glue(|nonscript)
+.|mathoff
+.|penalty 10000
+.|glue(|parfillskip) 0.0 plus 100.0
+.|glue(|rightskip) 0.0
+|glue 0.0 plus 1.0fill
+|glue(|parskip) 0.0 plus 42.0 minus 8.0
+|glue(|baselineskip) 10.0
+|hbox(0.0+0.0)x100.0, glue set 0.97
+.|glue(|leftskip) 3.0
+.|hbox(0.0+0.0)x0.0
+.|mathon
+.|hbox(0.0+0.0)x0.0
+.|hbox(0.0+0.0)x0.0
+.|mathoff
+.|penalty 10000
+.|glue(|parfillskip) 0.0 plus 100.0
+.|glue(|rightskip) 0.0
+|mark{twain}
+|glue 0.0 plus 1.0fill
+|glue 0.0 plus 1.0fil
+|glue 0.0 plus -1.0fil
+|glue 0.0 plus 1.0fil minus 1.0
+|glue(|parskip) 0.0 plus 42.0 minus 8.0
+|glue(|baselineskip) 10.0
+|hbox(0.0+0.0)x100.0, glue set 0.97
+.|glue(|leftskip) 3.0
+.|hbox(0.0+0.0)x0.0
+.|mathon
+.|hbox(0.0+0.0)x0.0
+..|ip t
+..|ip t
+..|glue 0.0 plus -1.0fil
+.|mathoff
+.|penalty 10000
+.|glue(|parfillskip) 0.0 plus 100.0
+.|glue(|rightskip) 0.0
+|glue(|parskip) 0.0 plus 42.0 minus 8.0
+|glue(|baselineskip) 10.0
+|hbox(0.0+0.0)x1.42262, glue set - 0.20552
+.|glue(|tabskip) 0.0154 minus 3.21002
+.|hbox(0.0+0.0)x0.0
+.|glue(|tabskip) 0.0154 minus 3.21002
+.|hbox(0.0+0.0)x0.0
+.|glue(|tabskip) 0.0154 minus 3.21002
+.|hbox(0.0+0.0)x4.0
+.|glue(|tabskip) 0.0154 minus 3.21002
+|glue(|baselineskip) 10.0
+|hbox(0.0+0.0)x1.42262, glue set - 0.20552
+.|glue(|tabskip) 0.0154 minus 3.21002
+.|hbox(0.0+0.0)x0.0
+.|glue(|tabskip) 0.0154 minus 3.21002
+.|hbox(0.0+0.0)x0.0
+.|glue(|tabskip) 0.0154 minus 3.21002
+.|hbox(0.0+0.0)x4.0
+..|glue 4.0 plus 2.0 minus 88.0
+.|glue(|tabskip) 0.0154 minus 3.21002
+|glue(|lineskip) 0.0 plus 40.0
+|hbox(3545.60136+140.80052)x2070.00168
+.|glue 6.0 plus 4.0 minus 44.0
+.|mathon
+.|hbox(3545.60136+140.80052)x614.40031
+..|hbox(1638.40019+409.6015)x614.40031, shifted -268.80098
+...|enorm b
+..|vbox(3545.60136+0.0)x0.0
+...|kern1638.40019
+...|rule(1638.40019+0.0)x*
+...|kern268.80098
+...|hbox(0.0+0.0)x0.0
+.|glue(|thinmuskip) -0.00002 plus 2.0fill minus -0.00005
+.|hbox(109.40036+95.40038)x1446.60138
+..|vbox(-1.00002+26.9999)x7.0, shifted -20.99995
+...|hbox(-1.00002+2.0)x7.0
+....|ip ^^@
+...|hbox(-1.00002+2.0)x7.0
+....|ip ^^@
+...|hbox(-1.00002+2.0)x7.0
+....|ip ^^@
+...|hbox(-1.00002+2.0)x7.0
+....|ip ^^@
+...|hbox(7.0+1.0)x3.0
+....|ip A
+...|hbox(-1.00002+2.0)x7.0
+....|ip ^^@
+...|hbox(-1.00002+2.0)x7.0
+....|ip ^^@
+...|hbox(-1.00002+2.0)x7.0
+....|ip ^^@
+...|hbox(-1.00002+2.0)x7.0
+....|ip ^^@
+...|hbox(8.0+2.0)x3.0
+....|ip B
+..|ip A
+..|kern1.0
+..|ip a
+..|kern1.0
+..|hbox(-204.80077+409.6015)x1433.60138, shifted -314.20113
+...|enorm ^^@
+.|mathoff
+.|ip A (ligature AA)
+.|kern 1.0
+|glue(|parskip) 0.0 plus 42.0 minus 8.0
+|glue(|lineskip) 0.0 plus 40.0
+|hbox(0.0+0.0)x100.0, glue set 0.97
+.|glue(|leftskip) 3.0
+.|hbox(0.0+0.0)x0.0
+.|penalty 10000
+.|glue(|parfillskip) 0.0 plus 100.0
+.|glue(|rightskip) 0.0
+|penalty 97
+|glue(|baselineskip) 10.0
+|hbox(0.0+0.0)x0.03079
+.|glue(|tabskip) 0.0154 minus 3.21002
+.|hbox(0.0+0.0)x0.0
+.|glue(|tabskip) 0.0154 minus 3.21002
+|penalty 97
+|penalty 97
+|glue(|baselineskip) 10.0
+|hbox(0.0+0.0)x0.03079
+.|glue(|tabskip) 0.0154 minus 3.21002
+.|hbox(0.0+0.0)x0.0
+.|glue(|tabskip) 0.0154 minus 3.21002
+|penalty 97
+|glue(|baselineskip) 10.0
+|hbox(0.0+0.0)x0.03079
+.|glue(|tabskip) 0.0154 minus 3.21002
+.|hbox(0.0+0.0)x0.0
+.|glue(|tabskip) 0.0154 minus 3.21002
+|penalty 97
+|glue(|parskip) 0.0 plus 42.0 minus 8.0
+|glue(|baselineskip) 10.0
+|hbox(0.0+0.0)x100.0, glue set 0.966
+.|glue(|leftskip) 3.0
+.|hbox(0.0+0.0)x0.0
+.|rule(*+*)x0.4
+.|penalty 10000
+.|glue(|parfillskip) 0.0 plus 100.0
+.|glue(|rightskip) 0.0
+total height 3796.40189 plus 332.0 plus 2.0fil plus -803.0fill minus 49.
+0
+ goal height 16383.99998
+### recent contributions:
+|rule(0.4+0.0)x*
+prevdepth ignored, prevgraf 1 line
+
+! OK.
+|showlonglists ...99 |showlists
+ |pagegoal =10000pt}
+l.439 ...\endgroup\showlonglists
+ $}\showboxbreadth9\showboxdepth9
+
+{restoring |showboxdepth=1}
+{restoring |showboxbreadth=2}
+{restoring |tracingcommands=2}
+{math shift character $}
+{restoring |fam=0}
+{restricted horizontal mode: end-group character }}
+{restoring |x=|char"C8}
+{vertical mode: |showboxbreadth}
+{|showboxdepth}
+
+^^M->|relax
+{|relax}
+{|showbox}
+> \box9=
+|hbox(19.6+1.0)x100.98999
+.|vbox(17.0+1.0)x100.0
+..|hbox(7.0+1.0)x100.0, glue set 0.85294
+...|glue(|leftskip) 3.0
+...|hbox(0.0+0.0)x0.0
+...|discretionary
+...|glue 0.0 plus 2.0 minus 88.0
+...|ip A (ligature BB)
+...|kern2.0
+...|ip A (ligature BB)
+...|kern2.0
+...|ip A (ligature BB)
+...etc.
+..|glue(|parskip) 0.0 plus 42.0 minus 8.0
+..|glue(|baselineskip) 2.0
+..|hbox(7.0+1.0)x100.0, glue set 0.85294
+...|glue(|leftskip) 3.0
+...|hbox(0.0+0.0)x0.0
+...|discretionary
+...|glue 0.0 plus 2.0 minus 88.0
+...|ip A (ligature BB)
+...|kern2.0
+...|discretionary replacing 3
+....|ip B
+...||ip A (ligature BB)
+...||kern2.0
+...||ip B
+...|ip A (ligature BB)
+...|kern2.0
+...etc.
+.|mathon
+.|vbox(19.6+0.0)x0.99
+..|kern10.0
+..|hbox(3.5+4.1)x0.99
+...|smalltrip A
+...|hbox(0.0+0.0)x-0.01, shifted 4.1
+..|kern2.0
+..|hbox(0.0+0.0)x0.99, glue set 0.49501fil
+...|glue 0.0 plus 1.0fil minus 1.0fil
+...|vbox(0.0+0.0)x0.0
+....|glue 0.0 plus 1.0fil minus 1.0fil
+...|glue 0.0 plus 1.0fil minus 1.0fil
+.|glue(|thinmuskip) -0.00002 plus 2.0fill minus -0.00005
+.|hbox(0.0+0.0)x0.0
+.|mathoff
+
+! OK.
+<to be read again>
+ |PAR
+l.440 \showbox9\PAR
+ {\output{}\penalty-10001\deadcycles=2}\scr...
+
+{|par}
+{begin-group character {}
+{|output}
+{|penalty}
+% t=3796.80188 plus 332.0 plus 2.0fil plus -803.0fill minus 49.0 g=10000
+.0 b=0 p=-10001 c=-10001#
+[-2.2.-1118806.0.11.196608.327680.1572864.1073741823]
+Memory usage before: 895&439; after: 290&420; still untouched: 175
+{|deadcycles}
+{end-group character }}
+{restoring |output={|showthe |deadcycles |global |advance |ETC.}
+{|scrollmode}
+
+{|hbox}
+{restricted horizontal mode: |write}
+{|showlists}
+
+### restricted horizontal mode entered at line 441
+|write-{|if 01{|else unbal}|fi }
+spacefactor 1000
+### vertical mode entered at line 0
+prevdepth ignored, prevgraf 1 line
+
+! OK.
+l.441 ...se unbal}\fi}\showlists
+ \tracingonline1%
+
+{|tracingonline}
+{|escapechar}
+{^^?global}
+{^^?global}
+{end}
+! Missing } inserted.
+<inserted text>
+ }
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+I've inserted something that you may have forgotten.
+(See the <inserted text> above.)
+With luck, this will get me unwedged. But if you
+really didn't forget anything, try typing `2' now; then
+my insertion and my current dilemma will both disappear.
+
+{end-group character }}
+{retaining escapechar=256}
+{restoring tracingonline=0}
+%% goal height=16383.99998, max depth=2.0
+{vertical mode: end}
+% t=20.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+% t=20.0 plus 1.0fil plus 1.0fill g=16383.99998 b=0 p=-1073741824 c=-107
+3741824#
+output->{showthe deadcycles global advance countz by1global globaldefs -
+1 gdef local {}unvbox 255end rb }
+{internal vertical mode: showthe}
+> 3.
+<output> {showthe deadcycles
+ global advance countz by1global ...
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+
+{global}
+{global}
+{gdef}
+{unvbox}
+{end}
+! You can't use `end' in internal vertical mode.
+<recently read> end
+
+<output> ...cal {}unvbox 255end
+ rb }
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+Sorry, but I'm not programmed to handle this case;
+I'll just pretend that you didn't ask for it.
+If you're in the wrong mode, you might be able to
+return to the right one by typing `I}' or `I$' or `I\par'.
+
+{end-group character }}
+! Unbalanced output routine.
+<output> ... {}unvbox 255end rb
+ }
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+Your sneaky output routine has problematic {'s and/or }'s.
+I can't handle that very well; good luck.
+
+{restoring local=undefined}
+%% goal height=16383.99998, max depth=2.0
+% t=20.0 plus 1.0fil g=16383.99998 b=0 p=0 c=0#
+{vertical mode: end}
+% t=20.0 plus 1.0fil plus 1.0fill g=16383.99998 b=0 p=0 c=0#
+% t=20.0 plus 1.0fil plus 2.0fill g=16383.99998 b=0 p=-1073741824 c=-107
+3741824#
+! Output loop---3 consecutive dead cycles.
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+I've concluded that your \output is awry; it never does a
+\shipout, so I'm shipping \box255 out myself. Next time
+increase \maxdeadcycles if you want me to be more patient!
+
+
+Completed box being shipped out [-1.2.-1118806.0.11.196608.327680.157286
+4.1073741823]
+vbox(16383.99998+0.0)x100.0, glue set 8182.0fill
+.glue(topskip) 20.0 plus 1.0fil
+.hbox(0.0+0.0)x0.0
+..write-{if 01{else unbal}fi }
+.hbox(0.0+0.0)x100.0
+.glue 0.0 plus 1.0fill
+.penalty 10000
+.hbox(0.0+0.0)x100.0
+.glue 0.0 plus 1.0fill
+
+write->if 01{else unbal}fi
+{no mode: if}
+{false}
+
+! Unbalanced write command.
+<write> if 01{else unbal}fi
+
+<inserted text>
+ }endwrite
+<to be read again>
+ end
+l.442 ...lobal\escapechar256\end
+
+On this page there's a \write with fewer real {'s than }'s.
+I can't handle that very well; good luck.
+
+unbal
+Memory usage before: 334&431; after: 292&418; still untouched: 175
+{vertical mode: end}
+ )
+(end occurred inside a group at level 1)
+(end occurred when if on line 442 was incomplete)
+(end occurred when ifcase on line 419 was incomplete)
+(end occurred when iftrue on line 413 was incomplete)
+Here is how much of TeX's memory you used:
+ 47 strings out of 1674
+ 253 string characters out of 8354
+ 2825 words of memory out of 3000
+ 372 multiletter control sequences out of 2100
+ 3041 words of font info for 4 fonts, out of 20000 for 75
+ 2 hyphenation exceptions out of 307
+ 7i,7n,9p,113b,38s stack positions out of 200i,40n,60p,500b,600s
+
+Output written on trip.dvi (16 pages, 2920 bytes).
diff --git a/systems/knuth/dist/tex/trip.pl b/systems/knuth/dist/tex/trip.pl
new file mode 100644
index 0000000000..6a38a92316
--- /dev/null
+++ b/systems/knuth/dist/tex/trip.pl
@@ -0,0 +1,833 @@
+(FAMILY UNSPECIFIED)
+(FACE F MRR)
+(CODINGSCHEME TEX TEST: NOT A REAL FONT)
+(DESIGNSIZE R 10.0)
+(COMMENT DESIGNSIZE IS IN POINTS)
+(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)
+(CHECKSUM O 32107654321)
+(FONTDIMEN
+ (SLANT R -2.0)
+ (SPACE R 0.400001)
+ (STRETCH R 0.200001)
+ (SHRINK R 0.1)
+ (XHEIGHT R 0.45)
+ (QUAD R 1.0)
+ (EXTRASPACE R 0.200001)
+ (PARAMETER D 8 R 0.05)
+ (PARAMETER D 9 R 0.1)
+ (PARAMETER D 10 R 0.200001)
+ (PARAMETER D 11 R 0.3)
+ (PARAMETER D 12 R 0.400001)
+ (PARAMETER D 13 R 0.5)
+ )
+(BOUNDARYCHAR C Z)
+(LIGTABLE
+ (LABEL C t)
+ (KRN O 0 R -1.0)
+ (KRN O 0 R -0.01)
+ (KRN O 0 R -0.02)
+ (KRN O 0 R -0.03)
+ (KRN O 0 R -0.04)
+ (KRN O 0 R -0.05)
+ (KRN O 0 R -0.06)
+ (KRN O 0 R -0.07)
+ (KRN O 0 R -0.08)
+ (KRN O 0 R -0.09)
+ (KRN O 0 R -0.1)
+ (KRN O 0 R -0.11)
+ (KRN O 0 R -0.12)
+ (KRN O 0 R -0.13)
+ (KRN O 0 R -0.14)
+ (KRN O 0 R -0.15)
+ (KRN O 0 R -0.16)
+ (KRN O 0 R -0.17)
+ (KRN O 0 R -0.18)
+ (KRN O 0 R -0.19)
+ (KRN O 0 R -0.2)
+ (KRN O 0 R -0.21)
+ (KRN O 0 R -0.22)
+ (KRN O 0 R -0.23)
+ (KRN O 0 R -0.24)
+ (KRN O 0 R -0.25)
+ (KRN O 0 R -0.26)
+ (KRN O 0 R -0.27)
+ (KRN O 0 R -0.28)
+ (KRN O 0 R -0.29)
+ (KRN O 0 R -0.3)
+ (KRN O 0 R -0.31)
+ (KRN O 0 R -0.32)
+ (KRN O 0 R -0.33)
+ (KRN O 0 R -0.34)
+ (KRN O 0 R -0.35)
+ (KRN O 0 R -0.36)
+ (KRN O 0 R -0.37)
+ (KRN O 0 R -0.38)
+ (KRN O 0 R -0.39)
+ (KRN O 0 R -0.4)
+ (KRN O 0 R -0.41)
+ (KRN O 0 R -0.42)
+ (KRN O 0 R -0.43)
+ (KRN O 0 R -0.44)
+ (KRN O 0 R -0.45)
+ (KRN O 0 R -0.46)
+ (KRN O 0 R -0.47)
+ (KRN O 0 R -0.48)
+ (KRN O 0 R -0.49)
+ (KRN O 0 R -0.5)
+ (KRN O 0 R -0.51)
+ (KRN O 0 R -0.52)
+ (KRN O 0 R -0.53)
+ (KRN O 0 R -0.54)
+ (KRN O 0 R -0.55)
+ (KRN O 0 R -0.56)
+ (KRN O 0 R -0.57)
+ (KRN O 0 R -0.58)
+ (KRN O 0 R -0.59)
+ (KRN O 0 R -0.6)
+ (KRN O 0 R -0.61)
+ (KRN O 0 R -0.62)
+ (KRN O 0 R -0.63)
+ (KRN O 0 R -0.64)
+ (KRN O 0 R -0.65)
+ (KRN O 0 R -0.66)
+ (KRN O 0 R -0.67)
+ (KRN O 0 R -0.68)
+ (KRN O 0 R -0.69)
+ (KRN O 0 R -0.7)
+ (KRN O 0 R -0.71)
+ (KRN O 0 R -0.72)
+ (KRN O 0 R -0.73)
+ (KRN O 0 R -0.74)
+ (KRN O 0 R -0.75)
+ (KRN O 0 R -0.76)
+ (KRN O 0 R -0.77)
+ (KRN O 0 R -0.78)
+ (KRN O 0 R -0.79)
+ (KRN O 0 R -0.8)
+ (KRN O 0 R -0.81)
+ (KRN O 0 R -0.82)
+ (KRN O 0 R -0.83)
+ (KRN O 0 R -0.84)
+ (KRN O 0 R -0.85)
+ (KRN O 0 R -0.86)
+ (KRN O 0 R -0.87)
+ (KRN O 0 R -0.88)
+ (KRN O 0 R -0.89)
+ (KRN O 0 R -0.9)
+ (KRN O 0 R -0.91)
+ (KRN O 0 R -0.92)
+ (KRN O 0 R -0.93)
+ (KRN O 0 R -0.94)
+ (KRN O 0 R -0.95)
+ (KRN O 0 R -0.96)
+ (KRN O 0 R -0.97)
+ (KRN O 0 R -0.98)
+ (KRN O 0 R -0.99)
+ (KRN O 0 R -1.0)
+ (KRN O 0 R -1.01)
+ (KRN O 0 R -1.02)
+ (KRN O 0 R -1.03)
+ (KRN O 0 R -1.04)
+ (KRN O 0 R -1.05)
+ (KRN O 0 R -1.06)
+ (KRN O 0 R -1.07)
+ (KRN O 0 R -1.08)
+ (KRN O 0 R -1.09)
+ (KRN O 0 R -1.1)
+ (KRN O 0 R -1.11)
+ (KRN O 0 R -1.12)
+ (KRN O 0 R -1.13)
+ (KRN O 0 R -1.14)
+ (KRN O 0 R -1.15)
+ (KRN O 0 R -1.16)
+ (KRN O 0 R -1.17)
+ (KRN O 0 R -1.18)
+ (KRN O 0 R -1.19)
+ (KRN O 0 R -1.2)
+ (KRN O 0 R -1.21)
+ (KRN O 0 R -1.22)
+ (KRN O 0 R -1.23)
+ (KRN O 0 R -1.24)
+ (KRN O 0 R -1.25)
+ (KRN O 0 R -1.26)
+ (KRN O 0 R -1.27)
+ (KRN O 0 R -1.28)
+ (KRN O 0 R -1.29)
+ (KRN O 0 R -1.3)
+ (KRN O 0 R -1.31)
+ (KRN O 0 R -1.32)
+ (KRN O 0 R -1.33)
+ (KRN O 0 R -1.34)
+ (KRN O 0 R -1.35)
+ (KRN O 0 R -1.36)
+ (KRN O 0 R -1.37)
+ (KRN O 0 R -1.38)
+ (KRN O 0 R -1.39)
+ (KRN O 0 R -1.4)
+ (KRN O 0 R -1.41)
+ (KRN O 0 R -1.42)
+ (KRN O 0 R -1.43)
+ (KRN O 0 R -1.44)
+ (KRN O 0 R -1.45)
+ (KRN O 0 R -1.46)
+ (KRN O 0 R -1.47)
+ (KRN O 0 R -1.48)
+ (KRN O 0 R -1.49)
+ (KRN O 0 R -1.5)
+ (KRN O 0 R -1.51)
+ (KRN O 0 R -1.52)
+ (KRN O 0 R -1.53)
+ (KRN O 0 R -1.54)
+ (KRN O 0 R -1.55)
+ (KRN O 0 R -1.56)
+ (KRN O 0 R -1.57)
+ (KRN O 0 R -1.58)
+ (KRN O 0 R -1.59)
+ (KRN O 0 R -1.6)
+ (KRN O 0 R -1.61)
+ (KRN O 0 R -1.62)
+ (KRN O 0 R -1.63)
+ (KRN O 0 R -1.64)
+ (KRN O 0 R -1.65)
+ (KRN O 0 R -1.66)
+ (KRN O 0 R -1.67)
+ (KRN O 0 R -1.68)
+ (KRN O 0 R -1.69)
+ (KRN O 0 R -1.7)
+ (KRN O 0 R -1.71)
+ (KRN O 0 R -1.72)
+ (KRN O 0 R -1.73)
+ (KRN O 0 R -1.74)
+ (KRN O 0 R -1.75)
+ (KRN O 0 R -1.76)
+ (KRN O 0 R -1.77)
+ (KRN O 0 R -1.78)
+ (KRN O 0 R -1.79)
+ (KRN O 0 R -1.8)
+ (KRN O 0 R -1.81)
+ (KRN O 0 R -1.82)
+ (KRN O 0 R -1.83)
+ (KRN O 0 R -1.84)
+ (KRN O 0 R -1.85)
+ (KRN O 0 R -1.86)
+ (KRN O 0 R -1.87)
+ (KRN O 0 R -1.88)
+ (KRN O 0 R -1.89)
+ (KRN O 0 R -1.9)
+ (KRN O 0 R -1.91)
+ (KRN O 0 R -1.92)
+ (KRN O 0 R -1.93)
+ (KRN O 0 R -1.94)
+ (KRN O 0 R -1.95)
+ (KRN O 0 R -1.96)
+ (KRN O 0 R -1.97)
+ (KRN O 0 R -1.98)
+ (KRN O 0 R -1.99)
+ (KRN O 0 R -2.0)
+ (KRN O 0 R -2.01)
+ (KRN O 0 R -2.02)
+ (KRN O 0 R -2.03)
+ (KRN O 0 R -2.04)
+ (KRN O 0 R -2.05)
+ (KRN O 0 R -2.06)
+ (KRN O 0 R -2.07)
+ (KRN O 0 R -2.08)
+ (KRN O 0 R -2.09)
+ (KRN O 0 R -2.1)
+ (KRN O 0 R -2.11)
+ (KRN O 0 R -2.12)
+ (KRN O 0 R -2.13)
+ (KRN O 0 R -2.14)
+ (KRN O 0 R -2.15)
+ (KRN O 0 R -2.16)
+ (KRN O 0 R -2.17)
+ (KRN O 0 R -2.18)
+ (KRN O 0 R -2.19)
+ (KRN O 0 R -2.2)
+ (KRN O 0 R -2.21)
+ (KRN O 0 R -2.22)
+ (KRN O 0 R -2.23)
+ (KRN O 0 R -2.24)
+ (KRN O 0 R -2.25)
+ (KRN O 0 R -2.26)
+ (KRN O 0 R -2.27)
+ (KRN O 0 R -2.28)
+ (KRN O 0 R -2.29)
+ (KRN O 0 R -2.3)
+ (SKIP D 3)
+ (LABEL O 55)
+ (LIG O 0 O 55)
+ (/LIG> C q C p)
+ (LABEL O 57)
+ (KRN C A R 0.1)
+ (STOP)
+ (KRN O 0 R -2.31)
+ (KRN O 0 R -2.32)
+ (KRN O 0 R -2.33)
+ (KRN O 0 R -2.34)
+ (KRN O 0 R -2.35)
+ (KRN O 0 R -2.36)
+ (KRN O 0 R -2.37)
+ (KRN O 0 R -2.38)
+ (KRN O 0 R -2.39)
+ (KRN O 0 R -2.4)
+ (KRN O 0 R -2.41)
+ (KRN O 0 R -2.42)
+ (KRN O 0 R -2.43)
+ (KRN O 0 R -2.44)
+ (KRN O 0 R -2.45)
+ (KRN O 0 R -2.46)
+ (KRN O 0 R -2.47)
+ (KRN O 0 R -2.48)
+ (KRN O 0 R -2.49)
+ (KRN O 0 R -2.5)
+ (KRN O 0 R -2.51)
+ (KRN O 0 R -2.52)
+ (KRN O 0 R -2.53)
+ (KRN O 0 R -2.54)
+ (KRN O 0 R -2.55)
+ (KRN O 0 R -2.56)
+ (KRN O 0 R -2.57)
+ (/LIG/>> C r C s)
+ (SKIP D 9)
+ (LABEL C p)
+ (/LIG/ C q C r)
+ (/LIG/> C r C t)
+ (LIG C t C t)
+ (STOP)
+ (LABEL C q)
+ (/LIG/ C u C p)
+ (/LIG/ C s C r)
+ (KRN C t R 0.1)
+ (/LIG> C r C t)
+ (SKIP D 2)
+ (LABEL C r)
+ (LIG/ C u C q)
+ (/LIG C q C u)
+ (STOP)
+ (/LIG/ C p C s)
+ (KRN C s R 0.3)
+ (STOP)
+ (LABEL C s)
+ (LIG/> C p C q)
+ (KRN C r R 0.1)
+ (STOP)
+ (LABEL C u)
+ (/LIG C Z C p)
+ (LIG C p C r)
+ (STOP)
+ (LABEL BOUNDARYCHAR)
+ (LIG/ C q C q)
+ (/LIG C 1 C 5)
+ (KRN O 55 R -0.1)
+ (STOP)
+ (LABEL C 1)
+ (/LIG/ C 1 C 2)
+ (/LIG/ C 2 C 3)
+ (/LIG> O 55 C 1)
+ (SKIP D 1)
+ (LABEL C 2)
+ (/LIG/ C 1 C 3)
+ (/LIG/ C 3 C 4)
+ (SKIP D 2)
+ (LABEL C 3)
+ (/LIG/ C 1 C 4)
+ (/LIG/ C 2 C 4)
+ (/LIG/ C 4 C 5)
+ (SKIP D 3)
+ (LABEL C 4)
+ (/LIG/ C 1 C 5)
+ (/LIG/ C 2 C 5)
+ (/LIG/ C 3 C 5)
+ (/LIG/ C 5 C 6)
+ (SKIP D 4)
+ (LABEL C 5)
+ (/LIG/ C 1 C 6)
+ (/LIG/ C 2 C 6)
+ (/LIG/ C 3 C 6)
+ (/LIG/ C 4 C 6)
+ (/LIG/ C 6 C 7)
+ (SKIP D 5)
+ (LABEL C 6)
+ (/LIG/ C 1 C 7)
+ (/LIG/ C 2 C 7)
+ (/LIG/ C 3 C 7)
+ (/LIG/ C 4 C 7)
+ (/LIG/ C 5 C 7)
+ (/LIG/>> C Z C 7)
+ (STOP)
+ (LABEL C 7)
+ (KRN C Z R 0.1)
+ (LABEL C A)
+ (LIG C A C A)
+ (KRN C B R 0.200001)
+ (KRN O 55 R 0.3)
+ (KRN C 1 R -0.200001)
+ (STOP)
+ (LABEL C B)
+ (LIG C B C A)
+ (LIG O 55 C C)
+ (KRN C C R 0.400001)
+ (STOP)
+ (LABEL C C)
+ (LIG C A O 202)
+ (LIG O 57 C B)
+ (STOP)
+ )
+(CHARACTER O 0
+ (CHARWD R 0.700001)
+ (CHARHT R -0.1)
+ (CHARDP R 0.200001)
+ )
+(CHARACTER O 55
+ (CHARWD R -0.5)
+ (COMMENT
+ (LIG O 0 O 55)
+ (/LIG> C q C p)
+ (KRN C A R 0.1)
+ )
+ )
+(CHARACTER O 57
+ (CHARWD R 0.1)
+ (COMMENT
+ (KRN C A R 0.1)
+ )
+ )
+(CHARACTER C 1
+ (CHARWD R 0.0)
+ (COMMENT
+ (/LIG/ C 1 C 2)
+ (/LIG/ C 2 C 3)
+ (/LIG> O 55 C 1)
+ (/LIG/ C 3 C 4)
+ (/LIG/ C 4 C 5)
+ (/LIG/ C 5 C 6)
+ (/LIG/ C 6 C 7)
+ (/LIG/>> C Z C 7)
+ )
+ )
+(CHARACTER C 2
+ (CHARWD R 0.0)
+ (COMMENT
+ (/LIG/ C 1 C 3)
+ (/LIG/ C 3 C 4)
+ (/LIG/ C 4 C 5)
+ (/LIG/ C 5 C 6)
+ (/LIG/ C 6 C 7)
+ (/LIG/>> C Z C 7)
+ )
+ )
+(CHARACTER C 3
+ (CHARWD R 0.0)
+ (COMMENT
+ (/LIG/ C 1 C 4)
+ (/LIG/ C 2 C 4)
+ (/LIG/ C 4 C 5)
+ (/LIG/ C 5 C 6)
+ (/LIG/ C 6 C 7)
+ (/LIG/>> C Z C 7)
+ )
+ )
+(CHARACTER C 4
+ (CHARWD R 0.0)
+ (COMMENT
+ (/LIG/ C 1 C 5)
+ (/LIG/ C 2 C 5)
+ (/LIG/ C 3 C 5)
+ (/LIG/ C 5 C 6)
+ (/LIG/ C 6 C 7)
+ (/LIG/>> C Z C 7)
+ )
+ )
+(CHARACTER C 5
+ (CHARWD R 0.0)
+ (COMMENT
+ (/LIG/ C 1 C 6)
+ (/LIG/ C 2 C 6)
+ (/LIG/ C 3 C 6)
+ (/LIG/ C 4 C 6)
+ (/LIG/ C 6 C 7)
+ (/LIG/>> C Z C 7)
+ )
+ )
+(CHARACTER C 6
+ (CHARWD R 0.0)
+ (COMMENT
+ (/LIG/ C 1 C 7)
+ (/LIG/ C 2 C 7)
+ (/LIG/ C 3 C 7)
+ (/LIG/ C 4 C 7)
+ (/LIG/ C 5 C 7)
+ (/LIG/>> C Z C 7)
+ )
+ )
+(CHARACTER C 7
+ (CHARWD R 0.0)
+ (COMMENT
+ (KRN C Z R 0.1)
+ (LIG C A C A)
+ (KRN C B R 0.200001)
+ (KRN O 55 R 0.3)
+ (KRN C 1 R -0.200001)
+ )
+ )
+(CHARACTER C A
+ (CHARWD R 0.200001)
+ (CHARHT R 0.700001)
+ (CHARDP R 0.1)
+ (CHARIC R 0.1)
+ (COMMENT
+ (LIG C A C A)
+ (KRN C B R 0.200001)
+ (KRN O 55 R 0.3)
+ (KRN C 1 R -0.200001)
+ )
+ )
+(CHARACTER C B
+ (CHARWD R 0.3)
+ (CHARHT R 0.8)
+ (CHARDP R 0.200001)
+ (COMMENT
+ (LIG C B C A)
+ (LIG O 55 C C)
+ (KRN C C R 0.400001)
+ )
+ )
+(CHARACTER C C
+ (CHARWD R 0.400001)
+ (COMMENT
+ (LIG C A O 202)
+ (LIG O 57 C B)
+ )
+ )
+(CHARACTER C M
+ (CHARWD R 0.6)
+ (CHARIC R 0.200001)
+ (VARCHAR
+ (MID C A)
+ (BOT C B)
+ (REP O 0)
+ )
+ )
+(CHARACTER C a
+ (CHARWD R 0.200001)
+ (CHARHT R 0.700001)
+ (CHARDP R 0.1)
+ (CHARIC R 0.1)
+ (NEXTLARGER O 202)
+ )
+(CHARACTER C b
+ (CHARWD R 0.3)
+ (CHARHT R 0.8)
+ (CHARDP R 0.200001)
+ (NEXTLARGER C M)
+ )
+(CHARACTER C p
+ (CHARWD R 0.0)
+ (COMMENT
+ (/LIG/ C q C r)
+ (/LIG/> C r C t)
+ (LIG C t C t)
+ )
+ )
+(CHARACTER C q
+ (CHARWD R 0.0)
+ (COMMENT
+ (/LIG/ C u C p)
+ (/LIG/ C s C r)
+ (KRN C t R 0.1)
+ (/LIG> C r C t)
+ (/LIG/ C p C s)
+ (KRN C s R 0.3)
+ )
+ )
+(CHARACTER C r
+ (CHARWD R 0.0)
+ (COMMENT
+ (LIG/ C u C q)
+ (/LIG C q C u)
+ )
+ )
+(CHARACTER C s
+ (CHARWD R 0.0)
+ (COMMENT
+ (LIG/> C p C q)
+ (KRN C r R 0.1)
+ )
+ )
+(CHARACTER C t
+ (CHARWD R 0.0)
+ (COMMENT
+ (KRN O 0 R -1.0)
+ (KRN O 0 R -0.01)
+ (KRN O 0 R -0.02)
+ (KRN O 0 R -0.03)
+ (KRN O 0 R -0.04)
+ (KRN O 0 R -0.05)
+ (KRN O 0 R -0.06)
+ (KRN O 0 R -0.07)
+ (KRN O 0 R -0.08)
+ (KRN O 0 R -0.09)
+ (KRN O 0 R -0.1)
+ (KRN O 0 R -0.11)
+ (KRN O 0 R -0.12)
+ (KRN O 0 R -0.13)
+ (KRN O 0 R -0.14)
+ (KRN O 0 R -0.15)
+ (KRN O 0 R -0.16)
+ (KRN O 0 R -0.17)
+ (KRN O 0 R -0.18)
+ (KRN O 0 R -0.19)
+ (KRN O 0 R -0.2)
+ (KRN O 0 R -0.21)
+ (KRN O 0 R -0.22)
+ (KRN O 0 R -0.23)
+ (KRN O 0 R -0.24)
+ (KRN O 0 R -0.25)
+ (KRN O 0 R -0.26)
+ (KRN O 0 R -0.27)
+ (KRN O 0 R -0.28)
+ (KRN O 0 R -0.29)
+ (KRN O 0 R -0.3)
+ (KRN O 0 R -0.31)
+ (KRN O 0 R -0.32)
+ (KRN O 0 R -0.33)
+ (KRN O 0 R -0.34)
+ (KRN O 0 R -0.35)
+ (KRN O 0 R -0.36)
+ (KRN O 0 R -0.37)
+ (KRN O 0 R -0.38)
+ (KRN O 0 R -0.39)
+ (KRN O 0 R -0.4)
+ (KRN O 0 R -0.41)
+ (KRN O 0 R -0.42)
+ (KRN O 0 R -0.43)
+ (KRN O 0 R -0.44)
+ (KRN O 0 R -0.45)
+ (KRN O 0 R -0.46)
+ (KRN O 0 R -0.47)
+ (KRN O 0 R -0.48)
+ (KRN O 0 R -0.49)
+ (KRN O 0 R -0.5)
+ (KRN O 0 R -0.51)
+ (KRN O 0 R -0.52)
+ (KRN O 0 R -0.53)
+ (KRN O 0 R -0.54)
+ (KRN O 0 R -0.55)
+ (KRN O 0 R -0.56)
+ (KRN O 0 R -0.57)
+ (KRN O 0 R -0.58)
+ (KRN O 0 R -0.59)
+ (KRN O 0 R -0.6)
+ (KRN O 0 R -0.61)
+ (KRN O 0 R -0.62)
+ (KRN O 0 R -0.63)
+ (KRN O 0 R -0.64)
+ (KRN O 0 R -0.65)
+ (KRN O 0 R -0.66)
+ (KRN O 0 R -0.67)
+ (KRN O 0 R -0.68)
+ (KRN O 0 R -0.69)
+ (KRN O 0 R -0.7)
+ (KRN O 0 R -0.71)
+ (KRN O 0 R -0.72)
+ (KRN O 0 R -0.73)
+ (KRN O 0 R -0.74)
+ (KRN O 0 R -0.75)
+ (KRN O 0 R -0.76)
+ (KRN O 0 R -0.77)
+ (KRN O 0 R -0.78)
+ (KRN O 0 R -0.79)
+ (KRN O 0 R -0.8)
+ (KRN O 0 R -0.81)
+ (KRN O 0 R -0.82)
+ (KRN O 0 R -0.83)
+ (KRN O 0 R -0.84)
+ (KRN O 0 R -0.85)
+ (KRN O 0 R -0.86)
+ (KRN O 0 R -0.87)
+ (KRN O 0 R -0.88)
+ (KRN O 0 R -0.89)
+ (KRN O 0 R -0.9)
+ (KRN O 0 R -0.91)
+ (KRN O 0 R -0.92)
+ (KRN O 0 R -0.93)
+ (KRN O 0 R -0.94)
+ (KRN O 0 R -0.95)
+ (KRN O 0 R -0.96)
+ (KRN O 0 R -0.97)
+ (KRN O 0 R -0.98)
+ (KRN O 0 R -0.99)
+ (KRN O 0 R -1.0)
+ (KRN O 0 R -1.01)
+ (KRN O 0 R -1.02)
+ (KRN O 0 R -1.03)
+ (KRN O 0 R -1.04)
+ (KRN O 0 R -1.05)
+ (KRN O 0 R -1.06)
+ (KRN O 0 R -1.07)
+ (KRN O 0 R -1.08)
+ (KRN O 0 R -1.09)
+ (KRN O 0 R -1.1)
+ (KRN O 0 R -1.11)
+ (KRN O 0 R -1.12)
+ (KRN O 0 R -1.13)
+ (KRN O 0 R -1.14)
+ (KRN O 0 R -1.15)
+ (KRN O 0 R -1.16)
+ (KRN O 0 R -1.17)
+ (KRN O 0 R -1.18)
+ (KRN O 0 R -1.19)
+ (KRN O 0 R -1.2)
+ (KRN O 0 R -1.21)
+ (KRN O 0 R -1.22)
+ (KRN O 0 R -1.23)
+ (KRN O 0 R -1.24)
+ (KRN O 0 R -1.25)
+ (KRN O 0 R -1.26)
+ (KRN O 0 R -1.27)
+ (KRN O 0 R -1.28)
+ (KRN O 0 R -1.29)
+ (KRN O 0 R -1.3)
+ (KRN O 0 R -1.31)
+ (KRN O 0 R -1.32)
+ (KRN O 0 R -1.33)
+ (KRN O 0 R -1.34)
+ (KRN O 0 R -1.35)
+ (KRN O 0 R -1.36)
+ (KRN O 0 R -1.37)
+ (KRN O 0 R -1.38)
+ (KRN O 0 R -1.39)
+ (KRN O 0 R -1.4)
+ (KRN O 0 R -1.41)
+ (KRN O 0 R -1.42)
+ (KRN O 0 R -1.43)
+ (KRN O 0 R -1.44)
+ (KRN O 0 R -1.45)
+ (KRN O 0 R -1.46)
+ (KRN O 0 R -1.47)
+ (KRN O 0 R -1.48)
+ (KRN O 0 R -1.49)
+ (KRN O 0 R -1.5)
+ (KRN O 0 R -1.51)
+ (KRN O 0 R -1.52)
+ (KRN O 0 R -1.53)
+ (KRN O 0 R -1.54)
+ (KRN O 0 R -1.55)
+ (KRN O 0 R -1.56)
+ (KRN O 0 R -1.57)
+ (KRN O 0 R -1.58)
+ (KRN O 0 R -1.59)
+ (KRN O 0 R -1.6)
+ (KRN O 0 R -1.61)
+ (KRN O 0 R -1.62)
+ (KRN O 0 R -1.63)
+ (KRN O 0 R -1.64)
+ (KRN O 0 R -1.65)
+ (KRN O 0 R -1.66)
+ (KRN O 0 R -1.67)
+ (KRN O 0 R -1.68)
+ (KRN O 0 R -1.69)
+ (KRN O 0 R -1.7)
+ (KRN O 0 R -1.71)
+ (KRN O 0 R -1.72)
+ (KRN O 0 R -1.73)
+ (KRN O 0 R -1.74)
+ (KRN O 0 R -1.75)
+ (KRN O 0 R -1.76)
+ (KRN O 0 R -1.77)
+ (KRN O 0 R -1.78)
+ (KRN O 0 R -1.79)
+ (KRN O 0 R -1.8)
+ (KRN O 0 R -1.81)
+ (KRN O 0 R -1.82)
+ (KRN O 0 R -1.83)
+ (KRN O 0 R -1.84)
+ (KRN O 0 R -1.85)
+ (KRN O 0 R -1.86)
+ (KRN O 0 R -1.87)
+ (KRN O 0 R -1.88)
+ (KRN O 0 R -1.89)
+ (KRN O 0 R -1.9)
+ (KRN O 0 R -1.91)
+ (KRN O 0 R -1.92)
+ (KRN O 0 R -1.93)
+ (KRN O 0 R -1.94)
+ (KRN O 0 R -1.95)
+ (KRN O 0 R -1.96)
+ (KRN O 0 R -1.97)
+ (KRN O 0 R -1.98)
+ (KRN O 0 R -1.99)
+ (KRN O 0 R -2.0)
+ (KRN O 0 R -2.01)
+ (KRN O 0 R -2.02)
+ (KRN O 0 R -2.03)
+ (KRN O 0 R -2.04)
+ (KRN O 0 R -2.05)
+ (KRN O 0 R -2.06)
+ (KRN O 0 R -2.07)
+ (KRN O 0 R -2.08)
+ (KRN O 0 R -2.09)
+ (KRN O 0 R -2.1)
+ (KRN O 0 R -2.11)
+ (KRN O 0 R -2.12)
+ (KRN O 0 R -2.13)
+ (KRN O 0 R -2.14)
+ (KRN O 0 R -2.15)
+ (KRN O 0 R -2.16)
+ (KRN O 0 R -2.17)
+ (KRN O 0 R -2.18)
+ (KRN O 0 R -2.19)
+ (KRN O 0 R -2.2)
+ (KRN O 0 R -2.21)
+ (KRN O 0 R -2.22)
+ (KRN O 0 R -2.23)
+ (KRN O 0 R -2.24)
+ (KRN O 0 R -2.25)
+ (KRN O 0 R -2.26)
+ (KRN O 0 R -2.27)
+ (KRN O 0 R -2.28)
+ (KRN O 0 R -2.29)
+ (KRN O 0 R -2.3)
+ (KRN O 0 R -2.31)
+ (KRN O 0 R -2.32)
+ (KRN O 0 R -2.33)
+ (KRN O 0 R -2.34)
+ (KRN O 0 R -2.35)
+ (KRN O 0 R -2.36)
+ (KRN O 0 R -2.37)
+ (KRN O 0 R -2.38)
+ (KRN O 0 R -2.39)
+ (KRN O 0 R -2.4)
+ (KRN O 0 R -2.41)
+ (KRN O 0 R -2.42)
+ (KRN O 0 R -2.43)
+ (KRN O 0 R -2.44)
+ (KRN O 0 R -2.45)
+ (KRN O 0 R -2.46)
+ (KRN O 0 R -2.47)
+ (KRN O 0 R -2.48)
+ (KRN O 0 R -2.49)
+ (KRN O 0 R -2.5)
+ (KRN O 0 R -2.51)
+ (KRN O 0 R -2.52)
+ (KRN O 0 R -2.53)
+ (KRN O 0 R -2.54)
+ (KRN O 0 R -2.55)
+ (KRN O 0 R -2.56)
+ (KRN O 0 R -2.57)
+ (/LIG/>> C r C s)
+ (/LIG/ C p C s)
+ (KRN C s R 0.3)
+ )
+ )
+(CHARACTER C u
+ (CHARWD R 0.0)
+ (COMMENT
+ (/LIG C Z C p)
+ (LIG C p C r)
+ )
+ )
+(CHARACTER O 202
+ (CHARWD R 0.400001)
+ )
diff --git a/systems/knuth/dist/tex/trip.tex b/systems/knuth/dist/tex/trip.tex
new file mode 100644
index 0000000000..a9205e52d5
--- /dev/null
+++ b/systems/knuth/dist/tex/trip.tex
@@ -0,0 +1,448 @@
+% This is a diabolical test file for TeX82. Watch your step.
+\immediate\catcode `{ = 1 \endlinechar=13
+\catcode `} = 2
+\catcode `$ = 3 {\catcode`$13\gdef\dol{$}}
+\catcode `& = 4
+\let\paR=\par
+\let\%=\relax
+\outer\xdef\par{\catcode `\% 14}
+ % this line should change % from type 5 to type 14
+\let\par=\paR \defaulthyphenchar=`- \defaultskewchar=256
+\ifx\initex\undefined \def\initex{} % next lines are skipped if format loaded
+ \catcode `# = 6 \catcode `U=\catcode`# % # for parameters
+ \catcode `^ = 7 \catcode `| = 8 % ^ for superscripts and | for subscripts
+ \catcode `~ = 9 % ~ will be ignored
+ \catcode `* = 10 % * will be like a space
+ \catcode `E = 12 % E is not a letter
+ \catcode`\@ = 15 % @ will be invalid
+ \catcode `^^A = 0008 % this is another way to get a subscript
+ \catcode `\^^@ = 11 % a strange letter will be allowed
+ \catcode `\^^? = \badness % and so will a strange escape delimiter
+ \fontdimen12\nullfont=13pt % give the null font more parameters
+ \font\trip = trip\relax % see TRIP.PL for details of this font
+ ^^?trip \font\smalltrip=trip scaled 500 % this will be our symbols font
+ \global\fontdimen22\smalltrip 7pt % the axis height
+ \textfont2=\smalltrip \scriptfont2 \smalltrip \scriptscriptfont2 \smalltrip
+ \nonstopmode\lccode256-0\mathchardef\a="8000\def\a{ SCALED 3~2769}
+ \font\rip=trip\a % font \rip will be the same as \trip
+ \skewchar\rip=`B \countdef\countz % \countz will be \count0
+ \def\on{1} \toksdef\tokens=256 \show\errorstopmode
+ \showthe\font \showthe\pageshrink \showthe\pagegoal
+ \font\bigtr^^@p=trip at20pt\textfont3=\bigtr^^@p % this will be extension font
+ \skip200 = 10pt plUs5fil\ifdim\hsize<\hsize\fi lllminus 0 fill
+ \setbox200=\vbox{\hrule\vskip\skip200} \wd200-2pt \setbox100=\hbox{A}
+ \skipdef\shkip100\shkip -18pt plus\catcode`\}fil minus 10fil
+ \advance\shkip by \skip200 \dimen33=3pt \count33=-\dimen33
+ \divide\shkip by \count33
+ \multiply\shkip by \count33 % so \skip100=-6pt plus 3filll minus 9fil
+ \count200 -5 \multiply\count200 by -100 % \count200 is 500
+ \count100=1000000 \divide\count100 by \count200 % \count100 is 2000
+ \dimen100=,00152587890625in % (100/65536)in = 7227sp
+ \multiply\dimen100 by 65536 \divide\dimen100 by 9 % \dimen100 is 803pt
+ \lineskip 0pt plus 40pt
+ \baselineskip=10pt plus 41pt
+ \parskip -0pt plus 42pt minus 8pt
+ \splittopskip 1pt plus 43pt
+ \splitmaxdepth -2pt \boxmaxdepth 1000pt
+ \belowdisplayskip 3pt plus 44pt minus\baselineskip \abovedisplayskip3pt
+ \abovedisplayshortskip 1pt plus 45pt minus\dimen100
+ \belowdisplayshortskip -\count33sp plus 46pt
+ \global\mathchardef\minus"232D % mathbin, family 3, character "2D (-)
+ \thinmuskip 1mu plus 2fill minus 3mu
+ \medmuskip 2mu minus 3mu
+ \thickmuskip -4mu
+ \def\gobble#1{} \floatingpenalty 100 \holdinginserts1
+ \everypar{A\insert200{\baselineskip400pt\splittopskip\count15pt\hbox{\vadjust
+ {\penalty999}}\hbox to -10pt{}}\showthe\pagetotal\showthe\pagegoal
+ \advance\count15by1\mark{\the\count15}\splitmaxdepth-1pt
+ \paR\gobble} % this aborts every paragraph abruptly
+ \def\weird#1{\csname\expandafter\gobble\string#1 \string\csname\endcsname}
+ \message{\the\output\weird\one on line \the\inputlineno}
+ \hyphenpenalty 88 \exhyphenpenalty 89 \badness
+ \clubpenalty 125 \widowpenalty 125 \displaywidowpenalty -125
+ \brokenpenalty 37
+ \interlinepenalty -125
+ \doublehyphendemerits 1000
+ \finalhyphendemerits 100000
+ \mag 2000 \righthyphenmin=1000000000
+ \delimiterfactor 10 \delimitershortfall 190pt
+ \showboxbreadth 55 \showboxdepth 9999 \chardef\nul0\def\0{\nul}
+ \tracingstats=4 \tracinglostchars=2 \tracingparagraphs\day \tracingpages\year
+ \chardef\?=`b \lccode`A=1 \let\^^bbb \hyphenchar\trip=1
+ \language-1\hyphenation\relax{b-\?-\char`b -\^^bb-^^62-^^" -t- }\lccode`149
+ {\everypar{\parindent\\\looseness-1}\skipdef\\8\language\?\\.01014pt\patterns
+ {0111}\emergencystretch9pt\language255\patterns{\the\\} % \patterns{.01015pt}
+ {\language256\patterns{0111 \?50AA1b3 *1AcA. bb bb1 0B2B0 b1c}} % *==space
+ \pretolerance-1\setbox0=\hbox{11}\setbox0=\hbox{\hbadness100\valign{#\cr
+ \hskip-9pt7A\righthyphenmin0\setlanguage\?\unhbox0{*\language`b11\noboundary}
+ 1Z1pts\patterns{q9q} -\0qq \showlists{\language\?\noboundary111}%
+ \hyphenchar\rip=`-\cr}}\patterns{toolate}\showbox0}
+ \showboxbreadth 9999\lefthyphenmin=2\righthyphenmin=3
+ \nulldelimiterspace --+.1pt \mathcode`q="3171
+ \scriptspace\if00-0.\fi\ifnum'\ifnum10=10 12="\fi
+ A 01p\ifdim1,0pt<`^^Abpt\fi\fi % this boils down to -0.01pt
+ \overfullrule 5pt \voffset-2pt
+ \def\sh{\ifnum\count4>10\else\dimen5=\count4pt
+ \advance\dimen5 by 10pt
+ \xdef\a{\a\the\count4pt \the\dimen5}
+ \advance\count4 by 1 \sh\fi}
+ \count4=1 \def\a{} \sh % \def\a{1pt 11pt 2pt 12pt ... 10pt 20pt}
+ \let\next=\dump \everyjob{\message{#}}
+\else\let\next=\relax\fi
+\next % if no format was preloaded, this will dump the trip.fmt file and halt
+\tracingcommands2\tracingrestores+2\write-1{log file only\the\prevgraf}
+\openout-'78terminal \openout10=tr\romannumeral1 \gobble\newcs pos
+\write10{} % writing three lines on tripos.tex (the first line is empty)
+\write10{\uppercase{\number{\outputpenalty}}} % 0{\outputpenalty} + error
+\write10{[\uppercase{\romannumeral-\the\outputpenalty}[} % "mmmmmmmmmm" (-10000)
+\vsize 2000pt
+\vbadness=1
+\topskip 20pt plus 1fil
+\penalty -12345 % this will be ignored since the page is still empty
+\maxdepth=2pt
+\tracingoutput\on
+\moveleft20pt\copy200
+\moveright20pt\hbox{\vrule depth20pt height-19pt width1pt}
+\penalty-10000 % now we'll compute silently for awhile, after default output
+\batchmode\output={\tracingcommands0\showthe\outputpenalty
+ \showboxbreadth 9999 \showboxdepth 9999 \hoffset1sp
+ {\setbox 254=\box255\shipout\ifvbox2\ifhbox254 \error\fi54\copy25\fi4}
+ \ifvoid 254\relax\else\error\fi
+ }
+\setbox255\vbox{}
+\dimen200=10000pt
+{\output{\dimen 9=\ht200\count5=\dimen9\global\countz=\outputpenalty
+ \ifnum\holdinginserts>0\global\holdinginserts0\unvbox255\penalty\countz
+ \else\setbox255\copy255 % at end of group, \box255 reverts to former value
+ \shipout\hbox{\box100\box200\vsplit 255 to 55pt}
+ \unvcopy255\showlists\showthe\insertpenalties\showthe\pageshrink
+ \globaldefs1\halign{#\tabskip\lineskip\cr}
+ \showboxdepth1\showboxbreadth2\fi
+ \message{\topmark:\firstmark:\botmark:\splitfirstmark:\splitbotmark}}
+ \insert100{\def\box{\vbox to 267.7pt{}} \vskip0pt plus 1fil
+ \baselineskip 0pt \lineskip 0pt minus .4pt
+ \box \penalty-101 \box \penalty-100 \box \penalty-1000
+ } % since \dimen100=803pt<3*267.7pt, the insertion splits;
+ % and the natural height+depth of the split-off part is 267.7pt;
+ % now since \count100=2000,
+ % this insertion adds about 535.4pt to the current page
+ \topskip1pt plus 44pt
+ \vbox spread 1000pt{} % beginning of new page
+ \insertpenalties=-50\penalty12345
+ \cleaders\hbox{\lower2pt\vbox to 17pt{}
+ \leaders\hrule\hskip10pt
+ \cleaders\hbox{A}\hskip 9pt % the A is 2pt wide
+ \leaders\hbox{A}\hskip 9pt
+ \xleaders\hbox{A}\hskip 9pt
+ \write111{\help} % \write will be ignored in leaders
+ }\vskip50pt minus 10pt
+ \mark{alpha}
+ AAA\everypar=\errhelp % because of previous \everypar, this makes 3 paragraphs
+ % and each paragraph consists of A\insert 200{400pt of stuff}\mark{n}
+ % but \count200=500 so the inserts are rated 200pt each
+ % so the third insertion will be split
+ \kern-50pt
+ A\hfill\vadjust{\newlinechar128\special{^^80\the\prevdepth}\penalty-5000}%
+ \penalty-1000000000 % forces line break in paragraph
+ % this is not the end of paragraph
+ A\par\insert200{\vskip10000pt\floatingpenalty3}% this insert will be held over
+ \pagefilstretch-1pt\showthe\insertpenalties\penalty99999999\showlists
+ \showthe\pagefilllstretch\vskip 1000pt\penalty-333\hbox to 23pt{} % output now
+ \vsize.pt\global\vsize=16383.99999237060546875pt % page size \approx infinity
+ } % now we revert to the former output routine
+{\tracingoutput-2\tracingstats1\shipout\hbox{\closeout10\closeout-10}}
+\showthe\everypar
+\everypar{}\showthe\everypar
+\def\showlonglists{{\tracingcommands0\pagefillstretch-1\dimen100
+ \showboxbreadth 9999 \showboxdepth 9999 \showlists \pagegoal=10000pt}}
+\tracingmacros=1
+\def\t12#101001#{-.#1pt} \let\T=\t
+\dimendef\varunit=222\varunit=+1,001\ifdim.5\mag>0cc0\fi1pt
+\ifdim -0.01001\varunit=\t120100101001001{\relax}\else\error\fi
+\countz=-1
+\ifodd\count0\advance\countz by -1\fi
+\penalty -12345 % output the remaining stuff
+\tracingmacros\tracingstats % the next part tests line-break computations
+% the two competing ways to set the paragraph have respective demerits
+% (30+l)^2+(30+l)^2+a and (51+l)^2+l^2, where a=adjdemerits, l=linepenalty
+\adjdemerits=782
+\linepenalty=1
+\def\1#1{\hbox to#1pt{}}
+\valign{\baselineskip20ptplus1pt\global\parfillskip0pt
+ \global\global\leftskip4pt
+ \rightskip-1pt
+ \global\hsize13pt
+ \setbox2\12
+ \noindent\copy2\hskip2pt plus5pt minus1pt
+ \copy2\hskip5pt minus2pt
+ \lower2pt\11\hskip3pt % this affects depth of the second line
+ \copy2 \hskip2pt plus.5pc
+ \box2#\cr
+ \noalign{\spacefactor=2000\global\xspaceskip=-1pt}
+ \noalign{ \vrule width0pt{ }}
+ \cr % set that paragraph with a=782, l=1 (demerits 2704 vs 2705)
+ \adjdemerits=784 \cr % increase a, so the second alternative is better
+ \linepenalty=2\hbadness=51\cr % increase l by 1, suppress diagnostic typeout
+ \noalign{ \spacefactor=1}}\message{\the\spacefactor}
+{\hsize1000pt\par\parindent1pt\indent}\leftskip3pt\def\?{\vrule width-2pt
+ \hbox spread2pt{}}\noindent\indent\hbox spread2pt{\hskip0pt plus-1bp}%
+\discretionary{\?AAAB}{\?B-}{\?/A\kern2pt}\unkern % the widths are 7pt, 4pt, 6pt
+\showthe\lastkern\vbox{\hrule width 6pt} \par % should set with nothing overfull
+\penalty-22222 % end of demerits test, hyphenation is next
+\looseness-10
+\uchyph=1
+\hsize 100pt
+A /A\char`A BBBBCACAC//% that becomes /k[AA]k[BB]k[BB][CA][CA][C/]/,
+ % where [] means a ligature and k means a kern.
+ % the word "aabbbbcaca" should be hyphenated to "aa1b3b2b2b1c1aca",
+ % which becomes {[AA]k-||[AA]k}{B-|[BB]kBk|[BB][BB]}{-||}{C-|A|[CA]}[CA]
+ % if I use the notation {x|y|z} for \discretionary{x}{y}{z}.
+\vadjust{\uchyph=0\ BBBBBB}% underfull box will show no hyphens
+\vadjust{\ \closeout1BBBBBB}{\hyphenchar\trip`C}% this time we get hyphens
+\hyphenation{BbB-BbB}\vadjust{\ BBBBBB\kern0ptB}% different hyphens
+\hyphenchar\rip`-\vadjust{\def\B{B}\ \pretolerance10000 B\B BBBB} % no hyphens
+
+\hbox{\sfcode`B=1234AB aB }\noindent \scriptscriptfont3 \smalltrip
+$$\eqno^{}$\scriptfont3=\rip\fontdimen2\smalltrip=0pt
+{\rightskip0pt plus 104pt minus 100fil
+ \looseness 5 \spaceskip 4pt plus 2pt minus 1fil
+ A\spacefactor32767\discretionary{}{\kern2pt-}{B\kern2pt} C$ \scriptfont2=\trip
+ \mathsurround143pt$ C $\mathsurround40pt$$\mathsurround60pt\hbox{$$}$\par}
+\uccode`m=`A\font\mumble=mumble\input tripos % "AAAAAAAAAA"+errors
+\par\penalty-33333 % end hyphenation, math is next
+{\catcode`?=13 \font?xyzzy at0pt\font ? xyzzy scaled1?} % nonexistent
+\font\enorm=trip at 2047.999992370605468749999 pt
+\font\ip trip at -10pt % through the looking glass
+\showthe$
+\showthe\font
+\message{\fontname\ip}
+\rip
+\textfont1=\font \scriptfont1=\smalltrip \scriptscriptfont1=\bigtr^^@p % [sick]
+\def\symbolpar #1*#2*#3*{\global\fontdimen#1\smalltrip = #3 pt}
+{\tracingmacros-1
+ \symbolpar8 num1 9.1
+ \symbolpar9 num2 9.2
+ \symbolpar10 num3 9.3
+ \symbolpar11 denom1 3.1
+ \symbolpar12 denom2 3.2
+ \symbolpar13 sup1 8.1
+ \symbolpar"E sup2 8.2
+ \symbolpar15 sup3 8.3
+ \symbolpar16 sub1 4.1
+ \symbolpar17 sub2 4.2
+ \symbolpar18 supdrop 0.3
+ \symbolpar19 subdrop 0.4
+ \symbolpar20 delim1 10
+ \symbolpar21 delim2 20
+ }
+\mathcode`+='20457 % mathbin, family 1, character '57 (/)
+\mathcode`=="322D % mathrel, family 2, character "2D (-)
+\delcode`["161361 % small (family 1, character "61 (a)), large (3,"61)
+\catcode`(=13 \catcode`(=13 \mathcode`y"7320\mathcode`z"8000
+\def({\delimiter"4162362 }{\catcode`z=13\global\let z=(}
+\parshape 10 \a \chardef\x200
+\hangindent- \parshape pt\hangafter-12% \parshape will take precedence
+\begingroup
+\looseness 2
+\rightskip 0pt plus 10fil minus 1sp
+\--\--\char-0-A\- % this makes lines 1 to 3
+$$\number\the\delcode`\relax\over{{{}}}}\pagestretch=-1\pagetotal\showlists
+\begingroup\halign to\the\displaywidth{#&#\crcr\crcr\cr} % makes lines 4--6
+\global\count6=\displayindent
+\predisplaypenalty=101
+\global\postdisplaypenalty-\predisplaysize* \global\setbox=
+\eqno % another error (actually causes two error messages and inserts $$)
+\looseness-2
+$\right\relax\mathchardef\minus="322D % locally \minus is the same as =
+\left.A\over A\abovewithdelims.?\right(+\mskip1A\minus=A+\penalty+1000A
+\relpenalty-2222
+\binoppenalty-3333
+\mathsurround.11em$\x % this formula goes on line 7
+$$ % here we begin a hairy display that covers lines 8 to 10
+\vadjust{\penalty7}\mkern-9mu\the\prevgraf \prevgraf=8 \insert255{\penalty999}
+\x\vcenter spread-2pt{} {\mathaccent"32D {A}}|-
+^{\raise 2pt\hbox{a}\displaystyle\char`+\textstyle}
+\overline{^A A|\minus\mathinner{}^
+ {A \mathchar"141 \char`B^^A{\mathaccent"7161
+ {\mathop A \mathbin A \mathopen A \mathpunct A\mathclose A \mathrel A
+ \global\scriptscriptfont0=\trip
+ \mathaccent"161 {\fam13A9\the\scriptscriptfont-1}}}}}
+\mathop\char`B^\mathchar"143
+\mathop b\nolimits\limits|C
+\mathord \radical"161 % missing { will be inserted
+ {\textstyle\radical"282382{\left(\scriptscriptstyle\mathop{\underline{
+ A\atop\displaystyle A|{A\hfil\over B\nonscript\kern1pt}^=}}
+ \nolimits|{\mathop y\nonscript\textstyle\nonscript\mskip9mu minus1fil
+ \showthe\lastskip B\abovewithdelims(.2pt\displaylimits}^z
+ \discretionary{\showthe\spacefactor-}{\smalltrip A\hss}{\smalltrip A}
+ \right[A}}}
+\let\penalty=\minus \aftergroup\expandafter
+\eqno\aftergroup\relax\scriptstyle\penalty % reader, be alert
+(\mathpunct{AA}
+|{B\fam1-}^{\hbox{A}}{\above9pt{v\overwithdelims..
+ \displaystyle{pq\atopwithdelims((\vrule height 9pt}}
+ \show\penalty \showlonglists
+ $\expandafter$\csname!\endcsname % end of hairy display, missing } inserted
+\parshape=-1 % now the hanging indentation is relevant
+\leftskip \parshape pt plus -10fil
+\spacefactor1\raise1pt\hbox{\special{\the\hangafter} } \penalty-10000
+\showbox0\spacefactor=0
+\write10{\the\spacefactor}\par % it's illegal to \write the space factor
+} % this fails to match \begingroup
+\aftergroup\lccode\aftergroup`\endgroup A`a % this restores \parshape
+\mark{\the\spacefactor} % \spacefactor: not in vertical mode
+$$\global\count7=\predisplaysize
+\mskip18mu minus 18mu \catcode`J=13 \catcode`j=\the\catcode`J \def j{\relax}
+\vtop to\displaywidth{\everydisplay{\global}\vbox to -1sp{}\noindent$$
+ \count9=\predisplaysize\lowercase{AaJ}$\ifvmode$\fi}\hss
+\leqno\mathchardef A\/\left(\over\left(\global\errorcontextlines5$$
+
+\hangindent1pt\par\showthe\hangindent\hangindent 254cm
+\parfillskip 0pt plus 100pt \fontdimen6\the\scriptfont2=-19sp
+\the\fam % begins a paragraph, but there's no 0 in the font
+A \char'202$$\global\count8=\predisplaysize\leqno\kern1009pt$\par
+\showlists {\catcode`!13\global\everyhbox{\def!{}}}
+\count5=\lastskip % \lastskip=3pt (\belowdisplayskip)
+\baselineskip 10pt
+{\sfcode`A=500\vfuzz18pt\everyvbox{ }% overfull \vbox won't be shown: 37-8=11+18
+ \vbox to 11pt{\hsize 10pt\tolerance 1 A A A A A\clubpenalty10000\par
+ \hbadness100\hfuzz 3pt A A A A A\leaders\vrule\hskip5pt\par}
+ \message{\the\badness}}
+\vbox to 10pt{\hbadness 99\hfuzz1pt\hbox to 0pt{\hskip 10pt minus 9pt}
+ \hbadness100\hbox to 10bp{\hskip 0pt plus 10pt}\tracingcommands1
+ \if\the\badness\fi\message{\the\badness}}\lineskiplimit-1pt\everyhbox{}
+\def\space{ } \dimendef\df=188 \dimen188=1pt
+\vbox to 11pt{\tracinglostchars-9 A\/\space\space\ignorespaces\space\space J
+ \vskip2pt\moveleft1pt\vbox to10pt{\boxmaxdepth=-1pt\mark{vii}}\vskip3pt
+ \unskip\setbox22=\lastbox\showthe\lastskip % \lastskip=-1pt (\baselineskip)
+ \unskip\vskip-\lastskip\kern\lastkern\penalty\lastkern\showbox22}
+\showbox22\kern3pt\message{\the\lastkern}\unkern
+\show\botmark \catcode`;13\def;{\setbox`; }
+\lineskiplimit-0.9999 \space\df\space\count9 0
+\vbox\space to 11pt{\accent\x\space\accent\space"42 \def\^^M{\ } ; \char'101
+ A\ \fontdimen 4 \trip = 88 pt\ \spaceskip 2 pt \
+ \vskip 10pt minus 10pt}
+\penalty-2147483647 % that's the largest value TeX will scan
+\penalty-2147483648 % see?
+\tabskip 1009.9sp minus .25cc % and now for alignment tests
+\let\A=\relax\count1=2{\errhelp{all is lost}\errmessage{}}
+\def\d#1\d{#1#1} \looseness-1
+\setbox3=\vtop{\vskip-3mm} % this box has a depth of -3mm
+\halign spread-12.truedd{&#\span\iftrue\A\span\else\span\fi\span&
+ \vbox{\halign to 0pt{\t2\dp3\A\crcr}#A}
+ &\hss\tabskip1ex plus7200bp minus 4\wd4\d#\d\cr % \d#\d becomes (erroneous) ##
+ \global\let\t=\tabskip \spaceskip=4pt minus 1sp
+ \def\A{B}\def\xx{\global\gdef\A{\global\count\count1=####\cr
+ \omit\cr\tabskip}}\expandafter\xx\span % please don't ask what this does
+ A&\omit\valign to -5pt{#&#\cr A\char`}\span\cr{ }\span\cr}\cr
+ \global\edef\A{\uppercase{
+ \message{\fontname\smalltrip\the\font\romannumeral1009}\lowercase{vq}} }
+ \lccode`Q=`b \span\omit$$\span\A&\show\cr\omit\cr
+ \noalign{\global\prevdepth20pt\errmessage{\count2=\the\count2}}
+ \omit\mark{a}&\omit\mark{b}\cr} % \count2 was set to -6mm=-1118806sp
+\errmessage{\prevdepth=\the\prevdepth}
+\penalty-88888 % end alignment test, now miscellaneous error messages
+\newlinechar`Y\global\unskip\show^^Y\newlinechar\lastpenalty\unpenalty\unkern
+\lastbox\penalty5\message{\the\lastpenalty\the\newlinechar}\textfont16=\relax
+\outer\def{}?
+\dimen5=-'7777777777sp\showthe\dimen5 % this should be OK
+\dimen6=-'40000pt\showthe\dimen6 % this should overflow
+\dimen7=.51\dimen5\showthe\dimen7 \multiply\dimen7 2\showthe\dimen7
+\a^^@^^@a@ % an undefined control sequence followed by invalid character
+{\aftergroup\gobble\aftergroup\c\gdef\b{\c} \def\c{} \b} % \c undefined
+\def\b#1\par{}
+\outer\gdef\a^^@^^@a#1\par#2{}\tokens{\a^^@^^@a\par!
+\long\gdef\l#1{}
+\outer\global\long\edef\lo#1#2U3#4#5#6#7#8#8#9#{\relax}
+\ifcase 1 \undefined\or\l\par\b{\par % occurrence of \par aborts \b
+\b{\l\undefined}\par\else\b{\par}\fi % but not there!
+\ifcase\iftrue-1a\else\fi \ifcase0\fi\else\ifcase5\fi\fi
+\catcode`^^C = 6 % another parameter symbol
+\let\^^C=\halign
+\def\^^@^^C{}
+\^^C{{\span\ifcase3 \lo#\cr............89{}\cr} % runaway preamble?
+\def\a^^C1{\d#1\d\l{#2}\l#1\par\a^^@^^@a#1\par# % runaway in definition; #2 bad
+\xdef\a^^C1{\d#1\d\l{#2}\l#1\par\a^^@^^@a#1\par# % runaway in definition; #2 OK!
+\T^^?a^^@^^@a\par{\lo\par % runaway in use
+\lo\par\par\par P \par\par\par\par\par\par89{} \muskip3=-\thickmuskip
+\muskipdef\shmip=3 \shmip=5mu plus \muskip3minus.5\shmip \showthe\shmip
+{\advance\shmip by \shkip\endlinechar-1
+\divide\shmip by \shkip\endlinechar`}
+\global\multiply\shmip by 2
+\showthe\shmip
+\div^^)de\count88
+By ^^p \toks1={\a\test}
+^\leaders\vrule\mskip\shmip M\leaders\hrule\nonscript\hskip\thinmuskip
+
+{\setbox3\hbox{\vfill\vsplit 3 0pt}
+\def\a#2{}
+\show A
+\show\a^^@^^@a
+\show (
+\message{\meaning\lo\noexpand\lo}
+\show\^^C
+\show\batchmode
+\show\error
+\showthe\output
+\showthe\thinmuskip
+\showthe\fontdimen1\enorm
+\ifx T\span\else\par\if\span\else\else\else\fi\fi
+\ifdim72p\iftrue t1i\fi n\fi\fi \message{\jobname\ifx\lo\lo OK}\fi
+\hangindent 2pt
+{\if 11 \prevgraf=-1\if 0123\error\else\relax\fi\else\error\fi
+ \prevgraf1\global\hangafter=2}\showthe\hangafter\showthe\prevgraf
+\char'203\showthe\prevgraf$\indent\mark{twain}
+\setbox3\hbox{\vrule}&\moveleft\lastbox % can't do that in math mode
+\unhbox234\unhcopy3\accent\x\vfill\vfil\vfilneg\vss % \vfill exits, \vss bad
+\def\a}{\let\a\xyzzy\csname a\endcsname}
+\def\a{ab
+
+ \c}\def\b{ab*\par\c}\let\c\b \def\b{\a\c} \ifx\a\ifx \.
+\else\expandafter\ifx\b \ifinner\error\else\relax\fi\else\error\fi\fi
+\ifvmode$\ifmmode\hbox tt\ifhmode\hfilneg\else\error\fi}$\fi\fi % missing {
+\noalign\omit\endcsname % these are extra
+\fontdimen 1000=20\varunit\showthe\fontdimen1000\trip\let\PAR=\par
+\gdef\par{\relax\PAR}\expandafter\ifx\csname xyzzy\endcsname\relax \mag=1999
+
+\fi\noindent{\halign to 1truemm\expandafter{\csname#\endcsname#&#&\l{#}\cr
+ \global\futurelet\endt\foo&\show\endt&$&&&.}
+
+\hbox{\/\hrule\textfont3=\enorm\prevdepth\advance\xspaceskip by-\xspaceskip
+ \spacefactor2000{ }\everymath{\radical"3}\fontdimen2\rip=0pt
+ $62{}\delimiterfactor1600\left(Aa\right\delimiter"300$AA\/}
+\openin 15 tripos\closein 15\iftrue{\ifeof 15\openin 100 tripos
+ \def\loop{\ifeof 0\let\loop=\relax\else{\global\read0to \a}\show\a\fi\loop}
+ \catcode`015\catcode`[1\outer\def\uppercase{}\loop}\else\fi
+}\def\test#1{\let\test= }\test. \show\test
+\def\a#1{\ifcat#1 \message\ifx#1 {\iffalse\fi\the\tokens\fi\fi}}
+\pretolerance-1\tokens\toks1\unhbox16\par\everycr{\noalign{\penalty97}}
+\the\tokens\ifcase1\or\ifeof\fi\def\stopinput{\error\let\input\die}
+\let\lb={\let\rb=}\halign\relax{\span\iffalse}\fi\cr#&\ifnum0=`{\fi\cr\cr}
+\let\e\expandafter\def\trap#1{}\def\unbalanced{\halign\lb}\unbalanced#\cr
+ \relax\e\e\e\err\e\e\e\endt\e\trap\cr\noexpand\cr}
+
+\expandafter\stopinput\input tripos\endinput\input % one line of tripos
+\setbox10=\vbox to8192pt{\hbox{\hbox{\vadjust{A}}}}\vrule\unhbox10\hrule
+\output{\showthe\deadcycles\global\advance\countz by1\global\globaldefs-1
+ \gdef\local{}\unvbox255\end\rb}\futurelet\dump\maxdeadcycles=3\show\dump
+\catcode`q=7 \catcode`\qqM=0 \expandafter\let\csname^^Mendcsname=\^^@\relax
+\relax\catcode`\qq1qM=13 \defqqM{\relax}#\begingroup{\showboxdepth=4\showbox10}
+
+\long\def\l#1\l{#1}\immediate\write10{\string\caution \l} % living dangerously
+\escapechar`|\tracingoutput0\shipout\vbox{\copy10qq5e^5cbox10}
+\setbox9\hbox{\fontdimen8\rip 0pt % \over becomes \atop in \scriptstyle
+ \afterassignment\relax\advance\prevdepth\afterassignment\relax\futurelet\x
+ \message{\noexpand\l\meaning\l\the\skewchar\ip}\vbox{\hyphenchar\ip-1%
+ \-\ BBBBBB\par\hyphenchar\ip`?\-\ BBBBBB}\if$\expandafter\noexpand\dol\fi%
+ \expandafter\expandafter\noexpand\undefined\noexpand\expandafter%
+ $\begingroup\mathop{\vbox{\vss}}\limits^\mathchoice{}a}{A|{}}{\mathchoice}
+ {}{\relax{}{B\over}\endgroup\showlonglists$}\showboxbreadth9\showboxdepth9
+\showbox9\PAR{\output{}\penalty-10001\deadcycles=2}\scrollmode%
+\hbox{\write-100000{\if01{\else unbal}\fi}\showlists\tracingonline1%
+\escapechar127\global\tracingoutput1\global\escapechar256\end
+% things not tested:
+% interaction (error insertion/deletion, interrupts, \pausing, files not there)
+% system-dependent parsing of file names, areas, extensions
+% certain error messages, especially fatal ones
+% things that can't happen in INITEX
+% unusual cases of fixed-point arithmetic
diff --git a/systems/knuth/dist/tex/trip.tfm b/systems/knuth/dist/tex/trip.tfm
new file mode 100644
index 0000000000..a509d55834
--- /dev/null
+++ b/systems/knuth/dist/tex/trip.tfm
Binary files differ
diff --git a/systems/knuth/dist/tex/trip.typ b/systems/knuth/dist/tex/trip.typ
new file mode 100644
index 0000000000..2ef2781289
--- /dev/null
+++ b/systems/knuth/dist/tex/trip.typ
@@ -0,0 +1,1214 @@
+This is DVItype, Version 3.6
+Options selected:
+ Starting page = *.*.*.*.*.*.*.*.*.*
+ Maximum number of pages = 1000000
+ Output level = 2 (mnemonics)
+ Resolution = 72.27000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=2000; 0.00003052 pixels per DVI unit
+' TeX output 2014.01.07:0959'
+
+42: beginning of page 0.0.0.0.11.0.0.0.0.0
+87: down4 129105920
+92: down3 655360
+96: push
+97: right3 1310720
+[ ]
+101: down3 1310720
+105: setrule height 65536, width 65536
+114: pop
+115: eop
+
+116: beginning of page -5000.0.0.0.11.53110374.0.0.0.0
+161: push
+162: down4 116090475
+167: push
+168: right3 262144
+[ ]
+172: setrule height 26214, width 655360
+181: right3 32773
+[ ]
+185: push
+186: fntdef1 0: trip---loaded at size 655360 DVI units
+ (this font is magnified 200%)
+206: fntnum0
+207: setchar65
+[A]
+208: pop
+209: w3 131072
+[ ]
+213: push
+214: setchar65
+[A]
+215: pop
+216: w0 131072
+[ ]
+217: push
+218: setchar65
+[A]
+219: pop
+220: w0 131072
+[ ]
+221: push
+222: setchar65
+[A]
+223: pop
+224: right3 229371
+[ ]
+228: push
+229: setchar65
+[A]
+230: pop
+231: w0 131072
+[ ]
+232: push
+233: setchar65
+[A]
+234: pop
+235: w0 131072
+[ ]
+236: push
+237: setchar65
+[A]
+238: pop
+239: w0 131072
+[ ]
+240: push
+241: setchar65
+[A]
+242: pop
+243: w3 144181
+[ ]
+247: push
+248: setchar65
+[A]
+249: pop
+250: w0 144181
+[ ]
+251: push
+252: setchar65
+[A]
+253: pop
+254: w0 144181
+[ ]
+255: push
+256: setchar65
+[A]
+257: pop
+258: w0 144181
+[ ]
+259: push
+260: setchar65
+[A]
+261: pop
+262: pop
+263: down3 1114112
+267: push
+268: right3 262144
+[ ]
+272: setrule height 26214, width 655360
+281: right3 32773
+285: push
+286: setchar65
+[A]
+287: pop
+288: w3 131072
+[ ]
+292: push
+293: setchar65
+[A]
+294: pop
+295: w0 131072
+[ ]
+296: push
+297: setchar65
+[A]
+298: pop
+299: w0 131072
+[ ]
+300: push
+301: setchar65
+[A]
+302: pop
+303: right3 229371
+[ ]
+307: push
+308: setchar65
+[A]
+309: pop
+310: w0 131072
+[ ]
+311: push
+312: setchar65
+[A]
+313: pop
+314: w0 131072
+[ ]
+315: push
+316: setchar65
+[A]
+317: pop
+318: w0 131072
+[ ]
+319: push
+320: setchar65
+[A]
+321: pop
+322: w3 144181
+[ ]
+326: push
+327: setchar65
+[A]
+328: pop
+329: w0 144181
+[ ]
+330: push
+331: setchar65
+[A]
+332: pop
+333: w0 144181
+[ ]
+334: push
+335: setchar65
+[A]
+336: pop
+337: w0 144181
+[ ]
+338: push
+339: setchar65
+[A]
+340: pop
+341: pop
+342: pop
+343: eop
+
+344: beginning of page 10000.0.0.0.11.131072.0.0.0.0
+389: push
+390: down4 32460719
+395: push
+396: fntnum0
+397: setchar65
+[A]
+398: down3 65536
+402: setrule height 524288, width 327680
+411: pop
+412: y3 1880637
+416: push
+417: setchar65
+[A]
+418: down3 65536
+422: setrule height 524288, width 327680
+431: pop
+432: y0 1880637
+433: push
+434: setchar65
+[A]
+435: down3 65536
+439: setrule height 524288, width 327680
+448: pop
+449: down3 -1396163
+453: push
+454: setchar65
+[A]
+455: down3 65536
+459: setrule height 524288, width 327680
+468: pop
+469: pop
+470: eop
+
+471: beginning of page -333.0.0.0.11.655360000.0.0.0.0
+516: push
+517: down4 651689984
+522: xxx '?-1000.0pt' non-ASCII character in xxx command!
+534: down3 655360
+538: push
+539: fntnum0
+540: setchar65
+[A]
+541: down3 65536
+545: setrule height 524288, width 327680
+554: pop
+555: pop
+556: eop
+
+557: beginning of page -333.0.0.0.11.0.0.0.0.0
+602: eop
+
+603: beginning of page -2.0.0.0.11.0.0.0.0.0
+648: eop
+
+649: beginning of page -2.0.0.0.11.0.0.0.0.0
+694: down4 1001310720
+699: push
+700: push
+701: push
+702: down3 -1441792
+706: down3 1441792
+710: pop
+711: pop
+712: push
+713: push
+714: down3 -1441792
+718: down3 1310720
+722: pop
+723: pop
+724: push
+725: push
+726: down3 -1441792
+730: down3 1310720
+734: pop
+735: pop
+736: pop
+737: y3 655360
+741: push
+742: right3 393217
+[ ]
+746: fntnum0
+747: setchar65
+748: right3 131072
+752: setchar66
+[A B]
+753: pop
+754: y0 655360
+755: push
+756: right3 196609
+760: setchar67
+[ C]
+761: push
+762: putrule height 26214, width 393216
+771: pop
+772: pop
+773: eop
+
+774: beginning of page -2.0.0.0.11.0.0.0.0.0
+819: down4 1001179648
+824: push
+825: right3 196609
+[ ]
+829: fntnum0
+830: setchar65
+831: right3 4653056
+835: setchar47
+836: right3 65536
+840: setchar65
+841: w3 131072
+845: setchar65
+846: w0 131072
+847: setchar65
+[A /A A A]
+848: set1 130
+850: set1 130
+852: setchar66
+853: setchar47
+[B/]
+854: pop
+855: y3 589824
+859: push
+860: right3 5898241
+864: setchar65
+865: w3 131072
+869: setchar65
+870: w0 131072
+871: setchar65
+[ A A A]
+872: pop
+873: down3 524288
+877: push
+878: right3 5898241
+882: setchar65
+883: w3 131072
+887: setchar65
+888: w0 131072
+889: setchar65
+[ A A A]
+890: pop
+891: y0 589824
+892: push
+893: right3 5701633
+897: setchar65
+898: w3 131072
+902: setchar65
+903: w0 131072
+904: setchar65
+905: setchar66
+[ A A AB]
+906: pop
+907: y0 589824
+908: push
+909: right3 5898241
+913: setchar65
+914: w3 131072
+918: setchar65
+919: w0 131072
+920: setchar65
+[ A A A]
+921: pop
+922: y0 589824
+923: push
+924: right1 1
+926: setchar65
+927: right3 131072
+931: setchar66
+932: right3 262144
+936: setchar97
+937: setchar66
+[A B aB]
+938: pop
+939: down3 2031616
+943: push
+944: right3 196609
+948: setchar65
+[ A]
+949: pop
+950: y3 655360
+954: push
+955: right3 262144
+959: setchar45
+[ -]
+960: pop
+961: y0 655360
+962: push
+963: right3 196609
+967: setchar67
+[ C]
+968: pop
+969: y0 655360
+970: push
+971: right3 196609
+975: setchar67
+[ C]
+976: pop
+977: y0 655360
+978: y0 655360
+979: push
+980: right3 6422529
+984: setchar65
+[ A]
+985: pop
+986: eop
+
+987: beginning of page -2.0.0.0.11.0.327680.0.0.0
+1032: down4 1001179648
+1037: push
+1038: right3 262145
+[ ]
+1042: fntnum0
+1043: setchar45
+[-]
+1044: pop
+1045: y3 655360
+1049: push
+1050: right3 262144
+1054: setchar45
+1055: setchar45
+[ --]
+1056: pop
+1057: y0 655360
+1058: push
+1059: right3 327680
+1063: setchar45
+1064: setchar45
+1065: right3 65536
+1069: setchar65
+[ --A]
+1070: pop
+1071: down3 851968
+1075: eop
+
+1076: beginning of page -2.0.0.0.11.0.327680.0.0.0
+1121: down4 639342208
+1126: push
+1127: push
+1128: push
+1129: push
+1130: down3 -602931
+1134: push
+1135: right3 740559
+[ ]
+1139: fntdef1 1: trip---loaded at size 327680 DVI units
+1159: fntnum1
+1160: setchar65
+[A]
+1161: pop
+1162: right3 740559
+[ ]
+1166: down3 176947
+1170: putrule height 65536, width 98304
+1179: down3 635699
+1183: push
+1184: setchar65
+[A]
+1185: pop
+1186: pop
+1187: pop
+1188: push
+1189: right3 845417
+[ ]
+1193: down3 -262144
+1197: fntnum0
+1198: setchar98
+[b]
+1199: pop
+1200: pop
+1201: right3 1078433
+1205: setchar47
+1206: right3 54612
+1210: setchar65
+1211: right2 -7280
+[ /A]
+1214: fntnum1
+1215: setchar45
+1216: setchar45
+1217: right3 -72816
+[--]
+1221: fntnum0
+1222: setchar65
+1223: w3 36408
+1227: setchar47
+1228: w0 36408
+1229: setchar65
+[A/A]
+1230: pop
+1231: down3 4912743
+1235: push
+1236: push
+1237: push
+1238: right3 983045
+[ ]
+1242: fntdef1 2: trip---loaded at size 1310720 DVI units
+ (this font is magnified 400%)
+1262: fntnum2
+1263: setchar45
+[-]
+1264: pop
+1265: push
+1266: right3 425989
+[ ]
+1270: fntnum0
+1271: setchar65
+[A]
+1272: push
+1273: down3 -530842
+1277: push
+1278: push
+1279: right3 65536
+1283: down3 -131072
+1287: setchar97
+[a]
+1288: pop
+1289: right3 196608
+1293: setchar47
+[ /]
+1294: pop
+1295: pop
+1296: pop
+1297: pop
+1298: push
+1299: right3 818550
+[ ]
+1303: down3 -1557300
+1307: putrule height 65536, width 984062
+1316: down3 1557300
+1320: push
+1321: push
+1322: down3 -543949
+1326: fntnum1
+1327: setchar65
+[A]
+1328: pop
+1329: right3 97649
+[ ]
+1333: fntnum0
+1334: setchar65
+[A]
+1335: push
+1336: down3 268698
+1340: setchar45
+[-]
+1341: pop
+1342: push
+1343: right3 -310131
+1347: down3 -1098548
+1351: fntnum1
+1352: setchar65
+1353: right3 98304
+1357: setchar66
+[A B]
+1358: push
+1359: down3 471859
+1363: push
+1364: right3 140175
+[ ]
+1368: fntnum2
+1369: set1 130
+1371: pop
+1372: down3 458754
+1376: push
+1377: push
+1378: down3 -65535
+1382: setchar65
+[A]
+1383: pop
+1384: right3 411421
+1388: setchar65
+[ A]
+1389: push
+1390: down3 -131072
+1394: push
+1395: right3 -65536
+1399: setchar97
+[a]
+1400: pop
+1401: pop
+1402: pop
+1403: pop
+1404: pop
+1405: pop
+1406: pop
+1407: push
+1408: down3 -1179648
+1412: push
+1413: right3 1798972
+[ ]
+1417: fntnum1
+1418: setchar67
+[C]
+1419: pop
+1420: down3 1179648
+1424: push
+1425: push
+1426: right3 1766204
+[ ]
+1430: down3 -262144
+1434: fntnum0
+1435: setchar66
+[B]
+1436: pop
+1437: pop
+1438: pop
+1439: push
+1440: push
+1441: push
+1442: right3 1926404
+[ ]
+1446: down3 -458752
+1450: setchar77
+[M]
+1451: pop
+1452: pop
+1453: down3 524288
+1457: push
+1458: right3 2057476
+[ ]
+1462: fntnum1
+1463: setchar67
+[C]
+1464: pop
+1465: pop
+1466: push
+1467: push
+1468: push
+1469: right3 2414284
+[ ]
+1473: down3 -3556148
+1477: fntnum0
+1478: setchar97
+[a]
+1479: pop
+1480: push
+1481: right3 2610892
+[ ]
+1485: down3 -3556148
+1489: putrule height 458752, width 2400948
+1498: down3 3556148
+1502: push
+1503: push
+1504: push
+1505: push
+1506: push
+1507: down3 -1245183
+1511: push
+1512: right2 6554
+1515: setchar0
+1516: pop
+1517: y3 65535
+1521: push
+1522: right2 6554
+1525: setchar0
+1526: pop
+1527: down3 589824
+1531: push
+1532: right2 6554
+1535: setchar65
+[A]
+1536: pop
+1537: down1 -1
+1539: push
+1540: right2 6554
+1543: setchar0
+1544: pop
+1545: y0 65535
+1546: push
+1547: right2 6554
+1550: setchar0
+1551: pop
+1552: down3 655360
+1556: push
+1557: right2 6554
+1560: setchar66
+[B]
+1561: pop
+1562: pop
+1563: push
+1564: push
+1565: push
+1566: push
+1567: down3 -1425409
+1571: push
+1572: right3 543623
+[ ]
+1576: fntnum2
+1577: setchar65
+[A]
+1578: pop
+1579: down3 2451048
+1583: push
+1584: right3 471860
+[ ]
+1588: fntnum0
+1589: setchar65
+[A]
+1590: push
+1591: down3 -530842
+1595: push
+1596: right3 65536
+1600: fntnum1
+1601: setchar45
+[-]
+1602: pop
+1603: down3 1818625
+1607: push
+1608: push
+1609: down3 -638976
+1613: push
+1614: right2 6554
+1617: fntnum2
+1618: setchar65
+[A]
+1619: pop
+1620: right2 6554
+1623: down3 196608
+1627: putrule height 32768, width 393217
+1636: down3 1081344
+1640: push
+1641: right1 1
+1643: setchar66
+[B]
+1644: pop
+1645: pop
+1646: pop
+1647: pop
+1648: pop
+1649: pop
+1650: pop
+1651: right3 465306
+[ ]
+1655: down3 5600055
+1659: putrule height 596378, width 549850
+1668: pop
+1669: pop
+1670: push
+1671: down3 -2323253
+1675: push
+1676: right3 1015156
+1680: setchar98
+[ b]
+1681: pop
+1682: down4 8545900
+1687: push
+1688: push
+1689: right3 1015156
+[ ]
+1693: down3 -65536
+1697: setchar98
+[b]
+1698: pop
+1699: push
+1700: down3 -786432
+1704: push
+1705: right3 1590412
+[ ]
+1709: fntnum0
+1710: setchar66
+[B]
+1711: pop
+1712: right3 1408372
+[ ]
+1716: down3 393216
+1720: putrule height 131072, width 378648
+1729: pop
+1730: pop
+1731: pop
+1732: push
+1733: right3 1792919
+[ ]
+1737: down3 -65535
+1741: fntnum2
+1742: setchar97
+[a]
+1743: pop
+1744: pop
+1745: right3 2204340
+[ ]
+1749: fntnum0
+1750: setchar65
+[A]
+1751: pop
+1752: pop
+1753: pop
+1754: pop
+1755: pop
+1756: pop
+1757: right3 5011840
+[ ]
+1761: down3 6432362
+1765: setrule height 10906014, width 327680
+1774: pop
+1775: down3 7775850
+1779: push
+1780: right3 1292440
+1784: setchar45
+[ -]
+1785: fntnum1
+1786: setchar98
+[b]
+1787: push
+1788: setchar65
+[A]
+1789: pop
+1790: push
+1791: down3 -537395
+1795: push
+1796: right3 98304
+[ ]
+1800: fntnum0
+1801: setchar65
+[A]
+1802: pop
+1803: down3 1245184
+1807: push
+1808: right3 98304
+1812: fntnum2
+1813: setchar66
+[B]
+1814: fntnum0
+1815: setchar45
+[-]
+1816: pop
+1817: pop
+1818: push
+1819: push
+1820: push
+1821: right3 235275
+[ ]
+1825: down3 -163840
+1829: putrule height 589824, width 530116
+1838: down3 1943143
+1842: push
+1843: push
+1844: push
+1845: push
+1846: right2 6554
+1849: down3 -160563
+1853: putrule height 596378, width 517008
+1862: down3 1382810
+1866: push
+1867: push
+1868: push
+1869: down3 -262144
+1873: setchar98
+[b]
+1874: pop
+1875: push
+1876: down3 -720896
+1880: push
+1881: w3 196608
+1885: setchar112
+1886: setchar116
+1887: setchar115
+1888: setchar113
+1889: setchar116
+1890: w0 196608
+1891: setchar113
+1892: setchar112
+1893: right3 -72816
+1897: setchar117
+[ ptsqt qpu]
+1898: pop
+1899: down3 1048576
+1903: push
+1904: right3 245397
+[ ]
+1908: setrule height 589824, width 26214
+1917: pop
+1918: pop
+1919: push
+1920: right3 320400
+[ ]
+1924: down3 -262144
+1928: setchar98
+[b]
+1929: pop
+1930: pop
+1931: pop
+1932: pop
+1933: pop
+1934: pop
+1935: pop
+1936: pop
+1937: pop
+1938: pop
+1939: pop
+1940: eop
+
+1941: beginning of page -2.0.0.0.11.0.327680.1572864.0.-1073741823
+1986: down4 1001179648
+1991: push
+1992: push
+1993: right1 1
+[ ]
+1995: down3 -65536
+1999: xxx '-12'
+2004: pop
+2005: pop
+2006: y3 655360
+2010: y0 655360
+2011: down3 983040
+2015: push
+2016: push
+2017: right3 196609
+[ ]
+2021: fntnum0
+2022: setchar65
+[A]
+2023: push
+2024: push
+2025: right3 83740
+2029: down3 -262144
+2033: setchar98
+[b]
+2034: pop
+2035: push
+2036: push
+2037: right3 286902
+[ ]
+2041: down3 -425984
+2045: putrule height 65536, width 104858
+2054: down3 688128
+2058: push
+2059: push
+2060: down3 -360448
+2064: fntnum1
+2065: setchar98
+[b]
+2066: pop
+2067: pop
+2068: pop
+2069: pop
+2070: pop
+2071: pop
+2072: push
+2073: push
+2074: down3 589825
+2078: push
+2079: right3 4500861
+[ ]
+2083: fntnum0
+2084: setchar97
+2085: setchar97
+[aa]
+2086: pop
+2087: pop
+2088: pop
+2089: pop
+2090: eop
+
+2091: beginning of page -2.0.0.0.11.0.327680.1572864.1073741823.-1073741823
+2136: down4 1001179648
+2141: push
+2142: right3 196609
+[ ]
+2146: fntnum0
+2147: setchar65
+2148: right3 373830
+[A ]
+2152: set1 130
+2154: pop
+2155: down3 655360
+2159: eop
+
+2160: beginning of page -2.0.0.0.11.196608.327680.1572864.1073741823.0
+2205: down4 1001179648
+2210: push
+2211: down3 -262144
+2215: push
+2216: right3 196609
+[ ]
+2220: fntnum0
+2221: setchar65
+2222: w3 131072
+2226: setchar65
+2227: w0 131072
+2228: setchar65
+[A A A]
+2229: down3 65536
+2233: setrule height 524288, width 327680
+2242: pop
+2243: y3 655360
+2247: push
+2248: right3 196609
+2252: setchar65
+2253: right3 196608
+2257: setchar65
+[ A A]
+2258: pop
+2259: down3 131072
+2263: push
+2264: right3 196609
+2268: setchar65
+2269: w3 131072
+2273: setchar65
+2274: w0 131072
+2275: setchar65
+[ A A A]
+2276: pop
+2277: y0 655360
+2278: push
+2279: right3 196609
+2283: setchar65
+2284: right3 196608
+2288: setchar65
+[ A A]
+2289: pop
+2290: pop
+2291: y3 720896
+2295: push
+2296: down3 -655360
+2300: down3 655360
+2304: pop
+2305: down3 655360
+2309: push
+2310: down3 -262144
+2314: push
+2315: right3 196609
+2319: setchar65
+[ A]
+2320: pop
+2321: pop
+2322: y0 720896
+2323: push
+2324: down2 -32767
+2327: push
+2328: push
+2329: right3 198462
+[ ]
+2333: down3 -163841
+2337: setchar66
+[B]
+2338: pop
+2339: right3 558912
+2343: setchar65
+2344: setchar65
+[ AA]
+2345: pop
+2346: pop
+2347: eop
+
+2348: beginning of page -2.2.-1118806.0.11.196608.327680.1572864.1073741823.0
+2393: down4 1001179648
+2398: push
+2399: push
+2400: right3 -104176
+[ ]
+2404: fntnum0
+2405: setchar66
+[B]
+2406: push
+2407: down3 -1310720
+2411: y3 655360
+2415: y0 655360
+2416: push
+2417: right3 196608
+2421: setchar65
+[ A]
+2422: pop
+2423: pop
+2424: pop
+2425: push
+2426: push
+2427: down3 622591
+2431: push
+2432: down3 458752
+2436: push
+2437: right3 6895421
+2441: setchar65
+[ A]
+2442: pop
+2443: pop
+2444: pop
+2445: pop
+2446: pop
+2447: y3 655360
+2451: push
+2452: push
+2453: right3 420112
+2457: setchar98
+2458: right4 12176941
+2463: setchar98
+[ b b]
+2464: pop
+2465: pop
+2466: y0 655360
+2467: eop
+
+2468: beginning of page -2.2.-1118806.0.11.196608.327680.1572864.1073741823.0
+2513: down3 1179648
+2517: z3 655360
+2521: push
+2522: push
+2523: right3 196608
+[ ]
+2527: down3 -537395
+2531: fntnum1
+2532: setchar77
+[M]
+2533: pop
+2534: pop
+2535: y4 203921760
+2540: y0 203921760
+2541: push
+2542: push
+2543: right3 196608
+[ ]
+2547: fntnum0
+2548: setchar116
+2549: setchar116
+[tt]
+2550: pop
+2551: pop
+2552: z0 655360
+2553: z0 655360
+2554: down4 232364531
+2559: push
+2560: push
+2561: push
+2562: right3 393216
+[ ]
+2566: down4 -17616141
+2571: fntdef1 3: trip---loaded at size 134217727 DVI units
+ (this font is magnified 40960%)
+2591: fntnum3
+2592: setchar98
+[b]
+2593: pop
+2594: pop
+2595: push
+2596: push
+2597: down3 -1376253
+2601: push
+2602: right4 40658554
+[ ]
+2607: fntnum0
+2608: setchar0
+2609: pop
+2610: y3 65535
+2614: push
+2615: right4 40658554
+[ ]
+2620: setchar0
+2621: pop
+2622: y0 65535
+2623: push
+2624: right4 40658554
+[ ]
+2629: setchar0
+2630: pop
+2631: y0 65535
+2632: push
+2633: right4 40658554
+[ ]
+2638: setchar0
+2639: pop
+2640: down3 589824
+2644: push
+2645: right4 40658554
+2650: setchar65
+[ A]
+2651: pop
+2652: down1 -1
+2654: push
+2655: right4 40658554
+[ ]
+2660: setchar0
+2661: pop
+2662: y0 65535
+2663: push
+2664: right4 40658554
+[ ]
+2669: setchar0
+2670: pop
+2671: y0 65535
+2672: push
+2673: right4 40658554
+[ ]
+2678: setchar0
+2679: pop
+2680: y0 65535
+2681: push
+2682: right4 40658554
+[ ]
+2687: setchar0
+2688: pop
+2689: z0 655360
+2690: push
+2691: right4 40658554
+2696: setchar66
+[ B]
+2697: pop
+2698: pop
+2699: right4 41117306
+2704: setchar65
+2705: w3 65536
+2709: setchar97
+[ Aa]
+2710: push
+2711: w0 65536
+2712: down4 -20591485
+2717: fntnum3
+2718: setchar0
+2719: pop
+2720: pop
+2721: right4 135463022
+[ ]
+2726: fntnum0
+2727: setchar65
+[A]
+2728: pop
+2729: down4 9227503
+2734: z0 655360
+2735: z0 655360
+2736: z0 655360
+2737: z0 655360
+2738: down2 26214
+2741: putrule height 26214, width 135659630
+2750: eop
+
+2751: beginning of page -1.2.-1118806.0.11.196608.327680.1572864.1073741823.0
+2796: down3 1179648
+2800: eop
+Postamble starts at byte 2801.
+maxv=1073741823, maxh=539754497, maxstackdepth=17, totalpages=16
+Font 3: trip scaled 409600
+Font 2: trip scaled 4000
+Font 1: trip
+Font 0: trip scaled 2000
diff --git a/systems/knuth/dist/tex/tripin.log b/systems/knuth/dist/tex/tripin.log
new file mode 100644
index 0000000000..1d98379456
--- /dev/null
+++ b/systems/knuth/dist/tex/tripin.log
@@ -0,0 +1,465 @@
+This is TeX, Version 3.14159265 (INITEX) 7 JAN 2014 09:09
+**\input trip
+(trip.tex
+! Bad character code (256).
+<to be read again>
+ -
+l.26 \nonstopmode\lccode256-
+ 0\mathchardef\a="8000\def\a{ SC...
+A character number must be between 0 and 255.
+I changed this one to zero.
+
+! Bad mathchar (32768).
+<to be read again>
+ \def
+l.26 ...\mathchardef\a="8000\def
+ \a{ SCALED 3~2769}
+A mathchar number must be between 0 and 32767.
+I changed this one to zero.
+
+! Illegal magnification has been changed to 1000 (32769).
+<to be read again>
+ \skewchar
+l.28 \skewchar
+ \rip=`B \countdef\countz % \countz will be \c...
+The magnification ratio must be between 1 and 32768.
+
+! Missing number, treated as zero.
+<to be read again>
+ \def
+l.29 \def
+ \on{1} \toksdef\tokens=256 \show\errorstopmode
+A number should have been here; I inserted `0'.
+(If you can't figure out why I needed to see a number,
+look up `weird error' in the index to The TeXbook.)
+
+! Bad register code (256).
+l.29 ...{1} \toksdef\tokens=256
+ \show\errorstopmode
+A register number must be between 0 and 255.
+I changed this one to zero.
+
+> \errorstopmode=\errorstopmode.
+l.29 ...=256 \show\errorstopmode
+
+
+> \rip .
+<recently read> \font
+
+l.30 \showthe\font
+ \showthe\pageshrink \showthe\pagegoal
+
+> 0.0pt.
+l.30 ...font \showthe\pageshrink
+ \showthe\pagegoal
+
+> 16383.99998pt.
+l.30 ...shrink \showthe\pagegoal
+
+
+! Illegal unit of measure (replaced by filll).
+l.32 ...fdim\hsize<\hsize\fi lll
+ minus 0 fill
+I dddon't go any higher than filll.
+
+\one \csname on line 60
+! You can't use `\badness' in vertical mode.
+l.61 ...yphenpenalty 89 \badness
+
+Sorry, but I'm not programmed to handle this case;
+I'll just pretend that you didn't ask for it.
+If you're in the wrong mode, you might be able to
+return to the right one by typing `I}' or `I$' or `I\par'.
+
+! Bad \patterns.
+l.75 ...uage256\patterns{0111 \?
+ 50AA1b3 *1AcA. bb bb1 0B2B0 b...
+(See Appendix H.)
+
+! Nonletter.
+l.75 ...ge256\patterns{0111 \?50
+ AA1b3 *1AcA. bb bb1 0B2B0 b1c...
+(See Appendix H.)
+
+! Duplicate pattern.
+l.75 ...1b3 *1AcA. bb bb1 0B2B0
+ b1c}} % *==space
+(See Appendix H.)
+
+Missing character: There is no Z in font trip!
+
+### horizontal mode entered at line 77 (language255:hyphenmin1,63)
+\hbox(0.0+0.0)x0.0
+\glue -9.0
+\rip A (ligature 7A)
+\setlanguage98 (hyphenmin 1,1)
+\rip 5 (ligature |1)
+\rip 7 (ligature )
+\rip 6 (ligature )
+\rip 7 (ligature )
+\kern-2.00002
+\rip 1
+\rip 7 (ligature |)
+\glue 4.0 plus 1.99799 minus 1.00099
+\rip 5 (ligature |1)
+\rip 7 (ligature )
+\rip 6 (ligature )
+\rip 7 (ligature )
+\kern-2.00002
+\rip 1
+\glue 4.0 plus 2.0 minus 1.0
+\setlanguage255 (hyphenmin 1,1)
+\rip 5 (ligature |1)
+\rip 5 (ligature |1)
+\rip t (ligature pt)
+\kern3.0
+\rip s
+\glue 4.0 plus 2.0 minus 1.0
+\kern-1.00002
+\rip - (ligature -
+)
+\rip p (ligature q)
+\rip t (ligature )
+\rip s (ligature )
+\rip q (ligature )
+\rip t (ligature )
+\kern3.0
+\rip q (ligature )
+\rip p (ligature )
+\rip r (ligature q|)
+\glue 4.0 plus 2.0 minus 1.0
+spacefactor 1000, current language 255
+### internal vertical mode entered at line 77
+prevdepth ignored
+### internal vertical mode entered at line 77
+\glue(\tabskip) 0.0
+prevdepth 0.0
+### restricted horizontal mode entered at line 76
+spacefactor 1000
+### restricted horizontal mode entered at line 76
+spacefactor 1000
+### vertical mode entered at line 0
+prevdepth ignored
+
+! OK.
+l.78 ...ns{q9q} -\0qq \showlists
+ {\language\?\noboundary111}%
+
+[]
+@ via @@0 b=0 p=0 d=0
+@@1: line 1.2 t=0 -> @@0
+\rip 7A[]1-1
+@\discretionary via @@0 b=10000 p=88 d=100007744
+@@2: line 1.0- t=100007744 -> @@0
+@\discretionary via @@1 b=10000 p=88 d=100007744
+@@3: line 2.0- t=100007744 -> @@1
+
+@ via @@0 b=10000 p=0 d=100000000
+@@4: line 1.0 t=100000000 -> @@0
+@ via @@2 b=0 p=0 d=0
+@ via @@1 b=10000 p=0 d=100000000
+@@5: line 2.0 t=100000000 -> @@1
+@ via @@3 b=0 p=0 d=0
+@@6: line 3.2 t=100007744 -> @@3
+1-1
+@\discretionary via @@0 b=10000 p=88 d=100007744
+@@7: line 1.0- t=100007744 -> @@0
+@\discretionary via @@4 b=10000 p=88 d=100007744
+@\discretionary via @@2 b=12 p=88 d=8888
+@\discretionary via @@1 b=12 p=88 d=7888
+@@8: line 2.2- t=7888 -> @@1
+@\discretionary via @@5 b=10000 p=88 d=100007744
+@\discretionary via @@3 b=12 p=88 d=8888
+@@9: line 3.2- t=100016632 -> @@3
+@\discretionary via @@6 b=10000 p=88 d=100007744
+@@10: line 4.0- t=200015488 -> @@6
+
+@ via @@0 b=4291 p=0 d=18412681
+@@11: line 1.0 t=18412681 -> @@0
+@ via @@7 b=0 p=0 d=0
+@ via @@4 b=10000 p=0 d=100000000
+@@12: line 2.2 t=100007744 -> @@7
+@ via @@8 b=0 p=0 d=0
+@ via @@5 b=10000 p=0 d=100000000
+@@13: line 3.2 t=7888 -> @@8
+@ via @@9 b=0 p=0 d=0
+@ via @@6 b=10000 p=0 d=100000000
+@@14: line 4.2 t=100016632 -> @@9
+@ via @@10 b=0 p=0 d=0
+@@15: line 5.2 t=200015488 -> @@10
+[]11-
+@\discretionary via @@0 b=195 p=88 d=45769
+@@16: line 1.0- t=45769 -> @@0
+@\discretionary via @@11 b=10000 p=88 d=100007744
+@\discretionary via @@4 b=0 p=88 d=7744
+@@17: line 2.2- t=100007744 -> @@4
+@\discretionary via @@12 b=10000 p=88 d=100007744
+@\discretionary via @@5 b=0 p=88 d=7744
+@@18: line 3.2- t=100007744 -> @@5
+@\discretionary via @@13 b=10000 p=88 d=100007744
+@\discretionary via @@6 b=0 p=88 d=7744
+@@19: line 4.2- t=100015488 -> @@6
+@\discretionary via @@14 b=10000 p=88 d=100007744
+@@20: line 5.0- t=200024376 -> @@14
+@\discretionary via @@15 b=10000 p=88 d=100007744
+@@21: line 6.0- t=300023232 -> @@15
+pts
+@ via @@0 b=12 p=0 d=144
+@@22: line 1.2 t=144 -> @@0
+-
+q-q
+@\discretionary via @@0 b=336 p=88 d=120640
+@@23: line 1.0- t=120640 -> @@0
+@\discretionary via @@22 b=10000 p=88 d=100007744
+@@24: line 2.0- t=100007888 -> @@22
+
+@ via @@0 b=0 p=0 d=0
+@@25: line 1.2 t=0 -> @@0
+@ via @@23 b=0 p=0 d=0
+@ via @@22 b=10000 p=0 d=100000000
+@@26: line 2.2 t=120640 -> @@23
+@ via @@24 b=0 p=0 d=0
+@@27: line 3.2 t=100007888 -> @@24
+[]11-1
+@\discretionary via @@0 b=42 p=88 d=9508
+@@28: line 1.1- t=9508 -> @@0
+@\discretionary via @@25 b=10000 p=88 d=100007744
+@\discretionary via @@23 b=1558 p=88 d=2436108
+@\discretionary via @@22 b=6396 p=88 d=40916560
+@@29: line 2.0- t=2556748 -> @@23
+@\discretionary via @@26 b=10000 p=88 d=100007744
+@\discretionary via @@24 b=1558 p=88 d=2436108
+@@30: line 3.0- t=100128384 -> @@26
+@\discretionary via @@27 b=10000 p=88 d=100007744
+@@31: line 4.0- t=200015632 -> @@27
+@\par via @@0 b=0 p=-10000 d=0
+@@32: line 1.2- t=0 -> @@0
+@\par via @@28 b=0 p=-10000 d=100000
+@\par via @@25 b=10000 p=-10000 d=100000000
+@\par via @@23 b=0 p=-10000 d=100000
+@\par via @@22 b=336 p=-10000 d=112896
+@@33: line 2.2- t=109508 -> @@28
+@\par via @@29 b=0 p=-10000 d=100000
+@\par via @@26 b=10000 p=-10000 d=100000000
+@\par via @@24 b=0 p=-10000 d=100000
+@@34: line 3.2- t=2656748 -> @@29
+@\par via @@30 b=0 p=-10000 d=100000
+@\par via @@27 b=10000 p=-10000 d=100000000
+@@35: line 4.2- t=100228384 -> @@30
+@\par via @@31 b=0 p=-10000 d=100000
+@@36: line 5.2- t=200115632 -> @@31
+@emergencypass
+[]
+@ via @@0 b=0 p=0 d=0
+@@1: line 1.2 t=0 -> @@0
+\rip 7A[]1-1
+@\discretionary via @@0 b=237 p=88 d=63913
+@@2: line 1.0- t=63913 -> @@0
+@\discretionary via @@1 b=4 p=88 d=7760
+@@3: line 2.2- t=7760 -> @@1
+
+@ via @@0 b=100 p=0 d=10000
+@@4: line 1.0 t=10000 -> @@0
+@ via @@2 b=0 p=0 d=0
+@ via @@1 b=0 p=0 d=0
+@@5: line 2.2 t=0 -> @@1
+@ via @@3 b=0 p=0 d=0
+@@6: line 3.2 t=7760 -> @@3
+1-1
+@\discretionary via @@0 b=75 p=88 d=13369
+@@7: line 1.1- t=13369 -> @@0
+@\discretionary via @@4 b=17 p=88 d=8033
+@\discretionary via @@2 b=0 p=88 d=8744
+@\discretionary via @@1 b=0 p=88 d=7744
+@@8: line 2.2- t=7744 -> @@1
+@\discretionary via @@5 b=17 p=88 d=8033
+@\discretionary via @@3 b=0 p=88 d=8744
+@@9: line 3.1- t=8033 -> @@5
+@\discretionary via @@6 b=17 p=88 d=8033
+@@10: line 4.1- t=15793 -> @@6
+
+@ via @@0 b=26 p=0 d=676
+@@11: line 1.1 t=676 -> @@0
+@ via @@7 b=0 p=0 d=0
+@ via @@4 b=1 p=0 d=1
+@@12: line 2.2 t=10001 -> @@4
+@ via @@8 b=0 p=0 d=0
+@ via @@5 b=1 p=0 d=1
+@@13: line 3.2 t=1 -> @@5
+@ via @@9 b=0 p=0 d=0
+@ via @@6 b=1 p=0 d=1
+@@14: line 4.2 t=7761 -> @@6
+@ via @@10 b=0 p=0 d=0
+@@15: line 5.2 t=15793 -> @@10
+[]11-
+@\discretionary via @@0 b=6 p=88 d=7780
+@@16: line 1.2- t=7780 -> @@0
+@\discretionary via @@11 b=1 p=88 d=7745
+@\discretionary via @@4 b=0 p=88 d=7744
+@@17: line 2.2- t=8421 -> @@11
+@\discretionary via @@12 b=1 p=88 d=7745
+@\discretionary via @@5 b=0 p=88 d=7744
+@@18: line 3.2- t=7744 -> @@5
+@\discretionary via @@13 b=1 p=88 d=7745
+@\discretionary via @@6 b=0 p=88 d=7744
+@@19: line 4.2- t=7746 -> @@13
+@\discretionary via @@14 b=1 p=88 d=7745
+@@20: line 5.2- t=15506 -> @@14
+@\discretionary via @@15 b=1 p=88 d=7745
+@@21: line 6.2- t=23538 -> @@15
+pts
+@ via @@0 b=0 p=0 d=0
+@@22: line 1.2 t=0 -> @@0
+-
+q-q
+@\discretionary via @@0 b=22 p=88 d=8228
+@@23: line 1.1- t=8228 -> @@0
+@\discretionary via @@22 b=182 p=88 d=40868
+@@24: line 2.0- t=40868 -> @@22
+
+@ via @@0 b=0 p=0 d=0
+@@25: line 1.2 t=0 -> @@0
+@ via @@23 b=0 p=0 d=0
+@ via @@22 b=4 p=0 d=16
+@@26: line 2.2 t=16 -> @@22
+@ via @@24 b=0 p=0 d=0
+@@27: line 3.2 t=40868 -> @@24
+[]11-1
+@\discretionary via @@0 b=4 p=88 d=7760
+@@28: line 1.2- t=7760 -> @@0
+@\discretionary via @@25 b=100 p=88 d=17744
+@\discretionary via @@23 b=9 p=88 d=8825
+@\discretionary via @@22 b=38 p=88 d=9188
+@@29: line 2.1- t=9188 -> @@22
+@\discretionary via @@26 b=100 p=88 d=17744
+@\discretionary via @@24 b=9 p=88 d=8825
+@@30: line 3.0- t=17760 -> @@26
+@\discretionary via @@27 b=100 p=88 d=17744
+@@31: line 4.0- t=58612 -> @@27
+@\par via @@0 b=0 p=-10000 d=0
+@@32: line 1.2- t=0 -> @@0
+@\par via @@28 b=0 p=-10000 d=100000
+@\par via @@25 b=9 p=-10000 d=81
+@\par via @@23 b=0 p=-10000 d=100000
+@\par via @@22 b=2 p=-10000 d=4
+@@33: line 2.2- t=4 -> @@22
+@\par via @@29 b=0 p=-10000 d=100000
+@\par via @@26 b=9 p=-10000 d=81
+@\par via @@24 b=0 p=-10000 d=100000
+@@34: line 3.2- t=97 -> @@26
+@\par via @@30 b=0 p=-10000 d=100000
+@\par via @@27 b=9 p=-10000 d=81
+@@35: line 4.2- t=40949 -> @@27
+@\par via @@31 b=0 p=-10000 d=100000
+@@36: line 5.2- t=158612 -> @@31
+
+! Too late for \patterns.
+l.79 ...har\rip=`-\cr}}\patterns
+ {toolate}\showbox0}
+All patterns must be given before typesetting begins.
+
+> \box0=
+\hbox(8.0+0.0)x0.0
+.\vbox(8.0+0.0)x0.0
+..\glue(\tabskip) 0.0
+..\vbox(8.0+0.0)x0.0
+...\hbox(7.0+1.0)x0.0, glue set 0.12505
+....\hbox(0.0+0.0)x0.0
+....\glue -9.0
+....\rip A (ligature 7A)
+....\setlanguage98 (hyphenmin 1,1)
+....\discretionary replacing 7
+.....\rip 5 (ligature |1)
+.....\rip -
+....|\rip 5 (ligature |1)
+....|\rip 7 (ligature |)
+....\rip 5 (ligature |1)
+....\rip 7 (ligature )
+....\rip 6 (ligature )
+....\rip 7 (ligature )
+....\kern-2.00002
+....\rip 1
+....\rip 7 (ligature |)
+....\glue 4.0 plus 1.99799 minus 1.00099
+....\discretionary replacing 6
+.....\rip 5 (ligature |1)
+.....\rip -
+....|\rip 5 (ligature |1)
+....\rip 5 (ligature |1)
+....\rip 7 (ligature )
+....\rip 6 (ligature )
+....\rip 7 (ligature )
+....\kern-2.00002
+....\rip 1
+....\glue 4.0 plus 2.0 minus 1.0
+....\setlanguage255 (hyphenmin 1,1)
+....\rip 5 (ligature |1)
+....\rip 7 (ligature )
+....\rip 6 (ligature )
+....\rip 7 (ligature )
+....\kern-2.00002
+....\discretionary replacing 1
+.....\rip 1
+.....\rip 1 (ligature -)
+.....\rip 7 (ligature |)
+....\rip 1
+....\rip t (ligature pt)
+....\kern3.0
+....\rip s
+....\glue 4.0 plus 2.0 minus 1.0
+....\kern-1.00002
+....\discretionary replacing 10
+.....\rip - (ligature -
+)
+.....\rip p (ligature q)
+.....\rip -
+....|\rip q (ligature |)
+....|\rip q
+....\rip - (ligature -
+)
+....\rip p (ligature q)
+....\rip t (ligature )
+....\rip s (ligature )
+....\rip q (ligature )
+....\rip t (ligature )
+....\kern3.0
+....\rip q (ligature )
+....\rip p (ligature )
+....\rip r (ligature q|)
+....\glue 4.0 plus 2.0 minus 1.0
+....\setlanguage98 (hyphenmin 1,1)
+....\rip 1
+....\rip 7 (ligature )
+....\rip 6 (ligature )
+....\rip 7 (ligature )
+....\rip 5 (ligature )
+....\rip 7 (ligature )
+....\rip 6 (ligature )
+....\rip 7 (ligature )
+....etc.
+..\glue(\tabskip) 0.0
+
+! OK.
+<to be read again>
+ }
+l.79 ...terns{toolate}\showbox0}
+
+
+ )
+Beginning to dump on file trip.fmt
+ (preloaded format=trip 2014.1.7)
+1326 strings of total length 23646
+471 memory locations dumped; current usage is 96&278
+341 multiletter control sequences
+\font\nullfont=nullfont
+\font\rip=trip
+\font\smalltrip=trip at 5.0pt
+\font\bigtr
+p=trip at 20.0pt
+2279 words of font info for 3 preloaded fonts
+1 hyphenation exception
+Hyphenation trie of length 273 has 9 ops out of 500
+ 2 for language 255
+ 1 for language 98
+ 6 for language 0
+No pages of output.
diff --git a/systems/knuth/dist/tex/tripman.tex b/systems/knuth/dist/tex/tripman.tex
new file mode 100644
index 0000000000..7634c8c9e7
--- /dev/null
+++ b/systems/knuth/dist/tex/tripman.tex
@@ -0,0 +1,413 @@
+% The TRIP manual: How to validate TeX --- last updated by D E Knuth on 4 Dec 89
+\font\eighttt= cmtt8
+\font\eightrm= cmr8
+\font\titlefont= cmr7 scaled\magstep5
+\let\mc=\eightrm
+\rm
+\let\mainfont=\tenrm
+
+\def\.#1{\hbox{\tt#1}}
+\def\\#1{\hbox{\it#1\/\hskip.05em}} % italic type for identifiers
+
+\parskip 2pt plus 1pt
+\baselineskip 12pt plus .25pt
+
+\def\verbatim#1{\begingroup \frenchspacing
+ \def\do##1{\catcode`##1=12 } \dospecials
+ \parskip 0pt \parindent 0pt
+ \catcode`\ =\active \catcode`\^^M=\active
+ \tt \def\par{\ \endgraf} \obeylines \obeyspaces
+ \input #1 \endgroup}
+% a blank line will be typeset at the end of the file;
+% if you're unlucky it will appear on a page by itself!
+{\obeyspaces\global\let =\ }
+
+\output{\shipout\box255\global\advance\pageno by 1} % for the title page only
+\null
+\vfill
+\centerline{\titlefont A torture test for \TeX}
+\vskip 18pt
+\centerline{by Donald E. Knuth}
+\centerline{Stanford University}
+\vskip 6pt
+\centerline{({\sl Version 3, January 1990\/})}
+\vfill
+\centerline{\vbox{\hsize 4in
+\noindent Programs that claim to be implementations of \TeX82 are
+supposed to be able to process the test routine contained in this
+report, producing the outputs contained in this report.}}
+\vskip 24pt
+{\baselineskip 9pt
+\eightrm\noindent
+The preparation of this report was supported in part by the National Science
+Foundation under grants IST-8201926 and MCS-8300984,
+and by the System Development Foundation.
+`\TeX' is a trademark of the American Mathematical Society.
+
+}\pageno=0\eject
+
+\output{\shipout\vbox{ % for subsequent pages
+ \baselineskip0pt\lineskip0pt
+ \hbox to\hsize{\strut
+ \ifodd\pageno \hfil\eightrm\firstmark\hfil
+ \mainfont\the\pageno
+ \else\mainfont\the\pageno\hfil
+ \eightrm\firstmark\hfil\fi}
+ \vskip 10pt
+ \box255}
+ \global\advance\pageno by 1}
+\let\runninghead=\mark
+\outer\def\section#1.{\noindent{\bf#1.}\quad
+ \runninghead{\uppercase{#1} }\ignorespaces}
+
+\section Introduction.
+People often think that their programs are ``debugged'' when large applications
+have been run successfully. But system programmers know that a typical large
+application tends to use at most about 50 per cent of the instructions
+in a typical compiler. Although the other half of the code---which tends
+to be the ``harder half''---might be riddled with errors, the system seems
+to be working quite impressively until an unusual case shows up on the
+next day. And on the following day another error manifests itself, and so on;
+months or years go by before certain parts of the compiler are even
+activated, much less tested in combination with other portions of the system,
+if user applications provide the only tests.
+
+How then shall we go about testing a compiler? Ideally we would like to
+have a formal proof of correctness, certified by a computer.
+This would give us a lot of confidence,
+although of course the formal verification program might itself be incorrect.
+A more serious drawback of automatic verification is that the formal
+specifications of the compiler are likely to be wrong, since they aren't
+much easier to write than the compiler itself. Alternatively, we can
+substitute an informal proof of correctness: The programmer writes his or
+her code in a structured manner and checks that appropriate relations
+remain invariant, etc. This helps greatly to reduce errors, but it cannot
+be expected to remove them completely; the task of checking a large
+system is sufficiently formidable that human beings cannot do it without
+making at least a few slips here and there.
+
+Thus, we have seen that test programs are unsatisfactory if they are simply
+large user applications; yet some sort of test program is needed because
+proofs of correctness aren't adequate either. People have proposed schemes
+for constructing test data automatically from a program text, but such
+approaches run the risk of circularity, since they cannot assume that a
+given program has the right structure.
+
+I have been having good luck with a somewhat different approach,
+first used in 1960 to debug an {\mc ALGOL} compiler. The idea is to
+construct a test file that is about as different from a typical user
+application as could be imagined. Instead of testing things that people
+normally want to do, the file tests complicated things that people would
+never dare to think of, and it embeds these complexities in still
+more arcane constructions. Instead of trying to make the compiler do the
+right thing, the goal is to make it fail (until the bugs have all been found).
+
+To write such a fiendish test routine, one simply gets into a nasty frame
+of mind and tries to do everything in the unexpected way. Parameters
+that are normally positive are set negative or zero; borderline cases
+are pushed to the limit; deliberate errors are made in hopes that the
+compiler will not be able to recover properly from them.
+
+A user's application tends to exercise 50\%\ of a compiler's logic,
+but my first fiendish tests tend to improve this to about 90\%. As the
+next step I generally make use of frequency-counting software to identify
+the instructions that have still not been called upon. Then I add ever more
+fiendishness to the test routine, until more than 99\%\ of the code
+has been used at least once. (The remaining bits are things that
+can occur only if the source program is really huge, or if certain
+fatal errors are detected; or they are cases so similar to other well-tested
+things that there can be little doubt of their validity.)
+
+Of course, this is not guaranteed to work. But my experience in 1960 was
+that only two bugs were ever found in that {\mc ALGOL} compiler after it
+correctly translated that original fiendish test. And one of those bugs
+was actually present in the results of the test; I simply had failed to
+notice that the output was incorrect. Similar experiences occurred later
+during the 60s and 70s, with respect to a few assemblers, compilers,
+and simulators that I wrote.
+
+This method of debugging, combined with the methodology of structured
+programming and informal proofs (otherwise known as careful desk checking),
+leads to greater reliability of production software than any other
+method I know. Therefore I have used it in developing \TeX82, and the
+main bulk of this report is simply a presentation of the test program
+that was used to get the bugs out of \TeX.
+
+Such a test file is useful also after a program has been debugged, since
+it can be used to give some assurance that subsequent modifications don't
+mess things up.
+
+The test file is called \.{TRIP.TEX}, because of my warped sense of humor:
+\TeX\ is pronounced ``techhh'', so the name reminded me of a
+triptych (and besides, I wanted to take a trip through the program while
+tripping it up, etc.).
+
+The contents of this test file are so remote from what people actually
+do with \TeX, I feel apologetic if I have to explain the correct
+translation of \.{TRIP.TEX}; nobody really cares about most of the
+nitty-gritty rules that are involved. Yet I believe \.{TRIP} exemplifies
+the sort of test program that has outstanding diagnostic ability, as
+explained above.
+
+If somebody claims to have a correct implementation of \TeX, I will not
+believe it until I see that \.{TRIP.TEX} is translated properly.
+I propose, in fact, that a program must meet two criteria before it
+can justifiably be called \TeX: (1)~The person who wrote it must be
+happy with the way it works at his or her installation; and (2)~the
+program must produce the correct results from \.{TRIP.TEX}.
+
+\TeX\ is in the public domain, and its algorithms are published;
+I've done this since I do not want to discourage its use by placing
+proprietary restrictions on the software. However, I don't want
+faulty imitations to masquerade as \TeX\ processors, since users
+want \TeX\ to produce identical results on different machines.
+Hence I am planning to do whatever I can to suppress any systems that
+call themselves \TeX\ without meeting conditions (1) and~(2).
+I have copyrighted the programs so that I have some chance to forbid
+unauthorized copies; I explicitly authorize copying of correct
+\TeX\ implementations, and not of incorrect ones!
+
+The remainder of this report consists of appendices, whose contents ought
+to be described briefly here:
+
+Appendix A explains in detail how to carry out a test of \TeX, given
+a tape that contains copies of the other appendices.
+
+Appendix B is \.{TRIP.TEX}, the fiendish test file that has already
+been mentioned. People who think that they understand \TeX\ are challenged
+to see if they know what \TeX\ is supposed to do with this file.
+People who know only a little about \TeX\ might still find it
+interesting to study Appendix~B, just to get some insights into the
+methodology advocated here.
+
+Appendix C is \.{TRIP.PL}, the property-list description of a
+special font called \.{trip}. This is the only font used by \.{TRIP.TEX}.
+There are no graphic characters associated with \.{trip} that could
+possibly be printed; indeed, \.{TRIP.PL} describes the properties of a font
+that is as weird as the ``document'' described by \.{TRIP.TEX}.
+
+Appendix D is \.{TRIPIN.LOG}, a correct transcript file \.{TRIP.LOG}
+that results if \.{INITEX} is applied to \.{TRIP.TEX}. (\.{INITEX} is
+the name of a version of \TeX\ that does certain initializations;
+this run of \.{INITEX} also creates a binary format file called \.{TRIP.FMT}.)
+
+Appendix E is a correct transcript file \.{TRIP.LOG} that results if
+\.{INITEX} or any other version of \TeX\ is applied to \.{TRIP.TEX}
+with format \.{TRIP.FMT}.
+
+Appendix F is \.{TRIP.TYP}, the symbolic version of a correct output
+file \.{TRIP.DVI} that was produced at the same time as the \.{TRIP.LOG}
+file of Appendix~E.
+
+Appendix G is \.{TRIPOS.TEX}, a short file written out and read in
+by \TeX\ when it processes \.{TRIP.TEX}.
+
+Appendix H is \.{TRIP.FOT}, an abbreviated version of Appendix E that
+appears on the user's terminal during the run that produces \.{TRIP.LOG}
+and \.{TRIP.DVI}.
+
+The debugging of \TeX\ and the testing of the adequacy of \.{TRIP.TEX}
+could not have been done nearly as well as reported here except for
+the magnificent software support provided by my colleague David R. Fuchs.
+In particular, he extended our local Pascal compiler so that
+frequency counting and a number of other important features were added
+to its online debugging abilities.
+
+The method of testing advocated here has one chief difficulty that deserves
+comment: I had to verify by hand that \TeX\ did the right things
+to \.{TRIP.TEX}. This took many hours, and perhaps I have missed
+something (as I did in 1960); I must confess that I have not checked
+every single number in Appendices E and~F. However, I'm willing to pay
+\$327.68 to the first finder of any remaining bug in \TeX, and I will
+be surprised if that bug doesn't show up also in Appendix~E. (I plan to
+write a technical report about all of the errors ultimately found in \TeX; that
+report will tell whether any bugs are discovered between now and~then!)
+
+\vfill\eject
+
+\section Appendix A: How to test \TeX.
+
+\item{0.} Let's assume that you have a tape containing \.{TRIP.TEX},
+\.{TRIP.PL}, \.{TRIPIN.LOG}, \.{TRIP.LOG}, \.{TRIP.TYP}, and \.{TRIP.FOT},
+as in Appendices B, C, D, E, F, and~G. Furthermore, let's suppose that you
+have a working \.{WEB} system, and that you have working programs \.{TFtoPL},
+\.{PLtoTF}, \.{DVItype}, as described in the \TeX ware report.
+
+\item{1.} Use \.{PLtoTF} to convert \.{TRIP.PL} into \.{TRIP.TFM}.
+Then use \.{TFtoPL} to convert \.{TRIP.TFM} into \.{TMP.PL}. Check that
+\.{TMP.PL} is identical to \.{TRIP.PL} (this is a partial test of \.{PLtoTF}
+and \.{TFtoPL}). Install \.{TRIP.TFM} in the standard file area for
+\TeX\ font metric files.
+
+\item{2.} Prepare a special version of \.{INITEX}. (This means that your \.{WEB}
+change file should have {\bf init} and {\bf tini} defined to be null.)
+The {\bf stat} and {\bf tats} macros should also be null, so that
+statistics are kept and other special features are enabled. Set
+\\{mem\_min} and \\{mem\_bot} equal to~1, and set \\{mem\_top} and
+\\{mem\_max} equal to~3000, for purposes of this test version. Also set
+$\\{error\_line}=64$, $\\{half\_error\_line}=32$, and
+$\\{max\_print\_line}=72$; these parameters affect many of the lines of
+the test output, so your job will be much easier if you use the same
+settings that were used to produce Appendix~E. You probably should also
+use the ``normal'' settings of other parameters found in \.{TEX.WEB}
+(e.g., $\\{stack\_size}=200$, $\\{font\_max}=75$, etc.), since these show
+up in a few lines of the test output. Your test version should not
+change the default definition of unprintable characters (\S49 of the program).
+
+\item{3.} Run the \.{INITEX} prepared in step 2. In response to the first
+`\.{**}' prompt, type carriage return (thus getting another `\.{**}').
+Then type `\.{\char`\\input trip}'. You should get an output that matches
+the file \.{TRIPIN.LOG} (Appendix~D). Don't be alarmed by the error
+messages that you see, unless they are different from those in Appendix~D.
+
+\def\sp{{\char'40}}
+\item{4.} Run \.{INITEX} again. This time type `\.{\sp\&trip\sp\sp trip\sp}'.
+(The spaces in this input help to check certain parts of \TeX\ that
+aren't otherwise used.) You should get outputs \.{TRIP.LOG}, \.{TRIP.DVI},
+and \.{TRIPOS.TEX}; there will also be an empty file \.{8TERMINAL.TEX}.
+Furthermore, your terminal should receive output that matches \.{TRIP.FOT}
+(Appendix~H). During the middle part of this test, however, the terminal
+will not be getting output, because \.{\char'134batchmode} is being
+tested; don't worry if nothing seems to be happening for a while---nothing
+is supposed to.
+
+\item{5.} Compare the \.{TRIP.LOG} file from step 4 with the ``master''
+\.{TRIP.LOG} file of step~0. (Let's hope you put that master file in a
+safe place so that it wouldn't be clobbered.) There should be perfect
+agreement between these files except in the following respects:
+
+\itemitem{a)} The dates and possibly the file names will
+naturally be different.
+
+\itemitem{b)} Glue settings in the displays of \TeX\ boxes are subject
+to system-dependent rounding, so slight deviations are permissible. However,
+such deviations apply only to the `\.{glue set}' values that appear at the
+end of an \.{\char'134hbox} or \.{\char'134vbox} line;
+all other numbers should agree exactly, since they are computed with
+integer arithmetic in a prescribed system-independent manner.
+
+\itemitem{c)} The amount of space in kerns that are marked ``for accent''
+are, similarly, subject to system-dependent rounding.
+
+\itemitem{d)} If you had different values for \\{stack\_size}, \\{buf\_size},
+etc., the corresponding capacity values will be different when they
+are printed out at the end.
+
+\itemitem{e)} Help messages may be different; indeed, the author encourages
+non-English help messages in versions of \TeX\ for people who don't
+understand English as well as some other language.
+
+\itemitem{f)} The total number and length of strings at the end may well
+be different.
+
+\itemitem{g)} If your \TeX\ uses a different memory allocation or
+packing scheme or \.{DVI} output logic, the memory usage statistics may change.
+
+\item{6.} Use \.{DVItype} to convert your file \.{TRIP.DVI} to a file
+\.{TRIP.TYP}. The following options should be set when using \.{DVItype}:
+$$\vbox{\halign{#\hfil&\hfil#\cr
+Output level = 2\cr
+Starting page = \.{*.*.*.*.*.*.*.*.*.*}\hskip-20pt\cr
+Number of pages = 1000000&(this is the default)\cr
+Resolution = 7227/100&(this is one point per pixel)\cr
+New magnification = 0&(this is the default)\cr}}$$
+The resulting file should agree with the master \.{TRIP.TYP} file of step~0,
+except that some of the values might be a little off due to floating-point
+rounding discrepancies. Furthermore there may be differences between
+`\\{right}' and `\\w' and `\\x' commands, and between `\\{down}' and `\\y'
+and `\\z'; the key thing is that all characters and rules and \\{xxx}'s should
+be in almost the same positions as specified in Appendix~F.
+(If your \.{DVI}-writing routines differ substantially from those in
+\.{TEX.WEB}, you may want to write a \.{DVIcompare} program that
+detects any substantive differences between two given \.{DVI} files. Such
+a routine would be of general use besides.
+On the other hand, if you have set \\{dvi\_buf\_size} to 800, then your
+\.{DVI} file should be virtually identical to the one supplied.)
+
+\item{7.} You might also wish to test \.{TRIP} with other versions of
+\TeX\ (i.e., \.{VIRTEX} or a production version with other fonts and
+macros preloaded). It should work unless \TeX's primitives have been
+redefined. However, this step isn't essential, since all the code of
+\.{VIRTEX} appears in \.{INITEX}; you probably won't catch any more errors
+this way, unless they would already become obvious from normal use of
+the~system.
+
+\vfill\eject
+
+\section Appendix B: The \.{TRIP.TEX} file.
+The contents of the test routine are prefixed here with line numbers, for
+ease in comparing this file with the error messages printed later; the
+line numbers aren't actually present.
+\runninghead{APPENDIX B: \.{TRIP.TEX} (CONTINUED)}
+
+\vskip 8pt
+\begingroup\count255=0
+\everypar{\global\advance\count255 by 1
+ \hbox to 20pt{\sevenrm\hfil\the\count255\ \ }}
+\verbatim{trip.tex}
+\endgroup
+\vfill\eject
+
+\section Appendix C: The \.{TRIP.PL} file.
+The ``font'' defined here has only a few characters, but they include all
+the complexities that \TeX\ must deal with: ligatures, kerns,
+lists of characters, and extensible characters. Some of the dimensions
+are negative, just to make things worse yet. (The format of property-list
+files like this is explained in the documentation to \.{PLtoTF}, in
+the \TeX ware report.)
+\runninghead{APPENDIX C: \.{TRIP.PL} (CONTINUED)}
+
+\vskip8pt
+\verbatim{trip.pl}
+\vfill\eject
+
+\section Appendix D: The \.{TRIPIN.LOG} file.
+When \.{INITEX} makes the \.{TRIP.FMT} file, it also creates a file called
+\.{TRIP.LOG} that looks like this.
+\runninghead{APPENDIX D: \.{TRIPIN.LOG} (CONTINUED)}
+
+\vskip8pt
+\verbatim{tripin.log}
+\vfill\eject
+
+\section Appendix E: The \.{TRIP.LOG} file.
+Here is the major output of the \.{TRIP} test; it is generated by running
+\.{INITEX} and loading \.{TRIP.FMT}, then reading \.{TRIP.TEX}.
+\runninghead{APPENDIX E: \.{TRIP.LOG} (CONTINUED)}
+
+{\let\tt=\eighttt\leftskip 1in\baselineskip 9pt plus .1pt minus .1pt
+\vskip8pt
+\verbatim{trip.log}
+}
+\vfill\eject
+
+\section Appendix F: The \.{TRIP.TYP} file.
+Here is another major component of the test. It shows the output of \.{DVItype}
+applied to the file \.{TRIP.DVI} that was created at the same time
+Appendix E was produced.
+\runninghead{APPENDIX F: \.{TRIP.TYP} (CONTINUED)}
+
+{\let\tt=\eighttt\leftskip 1in\baselineskip 9pt plus .1pt minus .1pt
+\vskip8pt
+\verbatim{trip.typ}
+}
+\vfill\eject
+
+\section Appendix G: The \.{TRIPOS.TEX} file.
+This short file was written out once and read in twice, during the time
+Appendix E was being produced. There are only three lines, the first of
+which is blank.
+\runninghead{APPENDIX G: \.{TRIPOS.TEX} (CONTINUED)}
+
+\vskip8pt
+\verbatim{tripos.tex}
+\vfill\eject
+
+\section Appendix H: The \.{TRIP.FOT} file.
+This shows what appeared on the terminal while Appendix E was being produced.
+\runninghead{APPENDIX H: \.{TRIP.FOT} (CONTINUED)}
+
+\vskip8pt
+\verbatim{trip.fot}
+
+\vfill\end
diff --git a/systems/knuth/dist/tex/tripos.tex b/systems/knuth/dist/tex/tripos.tex
new file mode 100644
index 0000000000..a0e25a24d8
--- /dev/null
+++ b/systems/knuth/dist/tex/tripos.tex
@@ -0,0 +1,3 @@
+
+\uppercase {0{\outputpenalty }}
+[\uppercase {mmmmmmmmmm}[
diff --git a/systems/knuth/dist/texware/dvitype.web b/systems/knuth/dist/texware/dvitype.web
new file mode 100644
index 0000000000..a0c5dba2c7
--- /dev/null
+++ b/systems/knuth/dist/texware/dvitype.web
@@ -0,0 +1,2506 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Version 1 was completed in September, 1982.
+% Slight changes were made in October, 1982, for version 0.7 of TeX.
+% Version 1.1 corrected minor bugs (May, 1983).
+% Version 2 was released with version 0.999 of TeX (July, 1983).
+% Version 2.1 corrected a bug when no fonts are present (September, 1983).
+% Version 2.2 corrected bugs in max_*_so_far and put1 (October, 1983).
+% Version 2.3 corrected spacing of accents (March, 1984).
+% Version 2.4 fixed rounding, changed oriental font conventions (April, 1984).
+% Version 2.5 improved the case of zero pages (May, 1984).
+% Version 2.6 introduced max_drift (June, 1984).
+% Version 2.7 had minor editorial changes (August, 1984).
+% Version 2.8 made default resolution 300/1 (April, 1985).
+% Version 2.9 changed negative scaling as in TeX version 2.7 (November, 1987).
+% Version 3 introduced an intermediate level of verbosity (October,1989).
+% Version 3.1 gave magnification info in final font list (November, 1989).
+% Version 3.2 doublechecked design size of each font (January, 1990).
+% Version 3.3 had more robust for loops in print_font, define_font (May, 1990).
+% Version 3.4 more robustness in presence of bad fonts (September, 1990).
+% Version 3.5 checked skipped material more strictly (March, 1995).
+% Version 3.6 gives a better help message (December, 1995).
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+
+\def\title{DVI$\,$\lowercase{type}}
+\def\contentspagenumber{401}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont DVItype} processor}
+ \vskip 15pt
+ \centerline{(Version 3.6, December 1995)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926 and MCS-8300984,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+The \.{DVItype} utility program reads binary device-independent (``\.{DVI}'')
+files that are produced by document compilers such as \TeX, and converts them
+into symbolic form. This program has two chief purposes: (1)~It can be used to
+determine whether a \.{DVI} file is valid or invalid, when diagnosing
+compiler errors; and (2)~it serves as an example of a program that reads
+\.{DVI} files correctly, for system programmers who are developing
+\.{DVI}-related software.
+
+Goal number (2) needs perhaps a bit more explanation. Programs for
+typesetting need to be especially careful about how they do arithmetic; if
+rounding errors accumulate, margins won't be straight, vertical rules
+won't line up, and so on. But if rounding is done everywhere, even in the
+midst of words, there will be uneven spacing between the letters, and that
+looks bad. Human eyes notice differences of a thousandth of an inch in the
+positioning of lines that are close together; on low resolution devices,
+where rounding produces effects four times as great as this, the problem
+is especially critical. Experience has shown that unusual care is needed
+even on high-resolution equipment; for example, a mistake in the sixth
+significant hexadecimal place of a constant once led to a difficult-to-find
+bug in some software for the Alphatype CRS, which has a resolution of 5333
+pixels per inch (make that 5333.33333333 pixels per inch). The document
+compilers that generate \.{DVI} files make certain assumptions about the
+arithmetic that will be used by \.{DVI}-reading software, and if these
+assumptions are violated the results will be of inferior quality.
+Therefore the present program is intended as a guide to proper procedure
+in the critical places where a bit of subtlety is involved.
+
+The first \.{DVItype} program was designed by David Fuchs in 1979, and it
+@^Fuchs, David Raymond@>
+went through several versions on different computers as the format of
+\.{DVI} files was evolving to its present form. Peter Breitenlohner
+helped with the latest revisions.
+@^Breitenlohner, Peter@>
+
+The |banner| string defined here should be changed whenever \.{DVItype}
+gets modified.
+
+@d banner=='This is DVItype, Version 3.6' {printed when the program starts}
+
+@ This program is written in standard \PASCAL, except where it is necessary
+to use extensions; for example, \.{DVItype} must read files whose names
+are dynamically specified, and that would be impossible in pure \PASCAL.
+All places where nonstandard constructions are used have been listed in
+the index under ``system dependencies.''
+@!@^system dependencies@>
+
+One of the extensions to standard \PASCAL\ that we shall deal with is the
+ability to move to a random place in a binary file; another is to
+determine the length of a binary file. Such extensions are not necessary
+for reading \.{DVI} files, and they are not important for efficiency
+reasons either---an infrequently used program like \.{DVItype} does not
+have to be efficient. But they are included there because of \.{DVItype}'s
+r\^^Dole as a model of a \.{DVI} reading routine, since other \.{DVI}
+processors ought to be highly efficient. If \.{DVItype} is being used with
+\PASCAL s for which random file positioning is not efficiently available,
+the following definition should be changed from |true| to |false|; in such
+cases, \.{DVItype} will not include the optional feature that reads the
+postamble first.
+
+Another extension is to use a default |case| as in \.{TANGLE}, \.{WEAVE},
+etc.
+
+@d random_reading==true {should we skip around in the file?}
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The binary input comes from |dvi_file|, and the symbolic output is written
+on \PASCAL's standard |output| file. The term |print| is used instead of
+|write| when this program writes on |output|, so that all such output
+could easily be redirected if desired.
+
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+
+@p program DVI_type(@!dvi_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ If the program has to stop prematurely, it goes to the
+`|final_end|'. Another label, |done|, is used when stopping normally.
+
+@d final_end=9999 {label for the end of it all}
+@d done=30 {go here when finished with a subtask}
+
+@<Labels...@>=final_end,done;
+
+@ The following parameters can be changed at compile time to extend or
+reduce \.{DVItype}'s capacity.
+
+@<Constants...@>=
+@!max_fonts=100; {maximum number of distinct fonts per \.{DVI} file}
+@!max_widths=10000; {maximum number of different characters among all fonts}
+@!line_length=79; {bracketed lines of output will be at most this long}
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!stack_size=100; {\.{DVI} files shouldn't |push| beyond this depth}
+@!name_size=1000; {total length of all font file names}
+@!name_length=50; {a file name shouldn't be longer than this}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d do_nothing == {empty statement}
+
+@ If the \.{DVI} file is badly malformed, the whole process must be aborted;
+\.{DVItype} will give up, after issuing an error message about the symptoms
+that were noticed.
+
+Such errors might be discovered inside of subroutines inside of subroutines,
+so a procedure called |jump_out| has been introduced. This procedure, which
+simply transfers control to the label |final_end| at the end of the program,
+contains the only non-local |goto| statement in \.{DVItype}.
+@^system dependencies@>
+
+@d abort(#)==begin print(' ',#); jump_out;
+ end
+@d bad_dvi(#)==abort('Bad DVI file: ',#,'!')
+@.Bad DVI file@>
+
+@p procedure jump_out;
+begin goto final_end;
+end;
+
+@* The character set.
+Like all programs written with the \.{WEB} system, \.{DVItype} can be
+used with any character set. But it uses ASCII code internally, because
+the programming for portable input-output is easier when a fixed internal
+code is used, and because \.{DVI} files use ASCII code for file names
+and certain other strings.
+
+The next few sections of \.{DVItype} have therefore been copied from the
+analogous ones in the \.{WEB} system routines. They have been considerably
+simplified, since \.{DVItype} need not deal with the controversial
+ASCII codes less than @'40 or greater than @'176.
+If such codes appear in the \.{DVI} file,
+they will be printed as question marks.
+
+@<Types...@>=
+@!ASCII_code=" ".."~"; {a subrange of the integers}
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lower case
+letters. Nowadays, of course, we need to deal with both upper and lower case
+alphabets in a convenient way, especially in a program like \.{DVItype}.
+So we shall assume that the \PASCAL\ system being used for \.{DVItype}
+has a character set containing at least the standard visible characters
+of ASCII code (|"!"| through |"~"|).
+
+Some \PASCAL\ compilers use the original name |char| for the data type
+associated with the characters in text files, while other \PASCAL s
+consider |char| to be a 64-element subrange of a larger data type that has
+some other name. In order to accommodate this difference, we shall use
+the name |text_char| to stand for the data type of the characters in the
+output file. We shall also assume that |text_char| consists of
+the elements |chr(first_text_char)| through |chr(last_text_char)|,
+inclusive. The following definitions should be adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=127 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+
+@ The \.{DVItype} processor converts 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...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [0..255] of text_char;
+ {specifies conversion of output characters}
+
+@ Under our assumption that the visible characters of standard ASCII are
+all present, the following assignment statements initialize the
+|xchr| array properly, without needing any system-dependent changes.
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:='?';
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';
+for i:=@'177 to 255 do xchr[i]:='?';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
+for i:=" " to "~" do xord[xchr[i]]:=i;
+
+@* Device-independent file format.
+Before we get into the details of \.{DVItype}, we need to know exactly
+what \.{DVI} files are. The form of such files was designed by David R.
+@^Fuchs, David Raymond@>
+Fuchs in 1979. Almost any reasonable typesetting device can be driven by
+a program that takes \.{DVI} files as input, and dozens of such
+\.{DVI}-to-whatever programs have been written. Thus, it is possible to
+print the output of document compilers like \TeX\ on many different kinds
+of equipment.
+
+A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
+series of commands in a machine-like language. The first byte of each command
+is the operation code, and this code is followed by zero or more bytes
+that provide parameters to the command. The parameters themselves may consist
+of several consecutive bytes; for example, the `|set_rule|' command has two
+parameters, each of which is four bytes long. Parameters are usually
+regarded as nonnegative integers; but four-byte-long parameters,
+and shorter parameters that denote distances, can be
+either positive or negative. Such parameters are given in two's complement
+notation. For example, a two-byte-long distance parameter has a value between
+$-2^{15}$ and $2^{15}-1$.
+@.DVI {\rm files}@>
+
+A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
+or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that define the dimensions used in the
+file; this must come first. Each ``page'' consists of a |bop| command,
+followed by any number of other commands that tell where characters are to
+be placed on a physical page, followed by an |eop| command. The pages
+appear in the order that they were generated, not in any particular
+numerical order. If we ignore |nop| commands and \\{fnt\_def} commands
+(which are allowed between any two commands in the file), each |eop|
+command is immediately followed by a |bop| command, or by a |post|
+command; in the latter case, there are no more pages in the file, and the
+remaining bytes form the postamble. Further details about the postamble
+will be explained later.
+
+Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first byte is number~0, then comes number~1, and so on. For example,
+one of the parameters of a |bop| command points to the previous |bop|;
+this makes it feasible to read the pages in backwards order, in case the
+results are being directed to a device that stacks its output face up.
+Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
+first page occupies bytes 100 to 999, say, and if the second
+page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
+points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
+very first |bop|, i.e., the one that starts in byte 100, has a pointer of $-1$.)
+
+@ The \.{DVI} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+implicit instead of explicit. When a \.{DVI}-reading program reads the
+commands for a page, it keeps track of several quantities: (a)~The current
+font |f| is an integer; this value is changed only
+by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
+is given by two numbers called the horizontal and vertical coordinates,
+|h| and |v|. Both coordinates are zero at the upper left corner of the page;
+moving to the right corresponds to increasing the horizontal coordinate, and
+moving down corresponds to increasing the vertical coordinate. Thus, the
+coordinates are essentially Cartesian, except that vertical directions are
+flipped; the Cartesian version of |(h,v)| would be |(h,-v)|. (c)~The
+current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
+where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
+are used for vertical spacing. (d)~There is a stack containing
+|(h,v,w,x,y,z)| values; the \.{DVI} commands |push| and |pop| are used to
+change the current level of operation. Note that the current font~|f| is
+not pushed and popped; the stack contains only information about
+positioning.
+
+The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
+to 32 bits, including the sign. Since they represent physical distances,
+there is a small unit of measurement such that increasing |h| by~1 means
+moving a certain tiny distance to the right. The actual unit of
+measurement is variable, as explained below.
+
+@ Here is a list of all the commands that may appear in a \.{DVI} file. Each
+command is specified by its symbolic name (e.g., |bop|), its opcode byte
+(e.g., 139), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|p[4]|' means that parameter |p| is four bytes long.
+
+\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
+such that the reference point of the character is at |(h,v)|. Then
+increase |h| by the width of that character. Note that a character may
+have zero or negative width, so one cannot be sure that |h| will advance
+after this command; but |h| usually does increase.
+
+\yskip\hang|set_char_1| through |set_char_127| (opcodes 1 to 127).
+Do the operations of |set_char_0|; but use the character whose number
+matches the opcode, instead of character~0.
+
+\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
+number~|c| is typeset. \TeX82 uses this command for characters in the
+range |128<=c<256|.
+
+\yskip\hang|set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
+bytes long, so it is in the range |0<=c<65536|. \TeX82 never uses this
+command, which is intended for processors that deal with oriental languages;
+but \.{DVItype} will allow character codes greater than 255, assuming that
+they all have the same width as the character whose code is $c \bmod 256$.
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+\yskip\hang|set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
+bytes long, so it can be as large as $2^{24}-1$.
+
+\yskip\hang|set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
+bytes long, possibly even negative. Imagine that.
+
+\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
+of height |a| and width |b|, with its bottom left corner at |(h,v)|. Then
+set |h:=h+b|. If either |a<=0| or |b<=0|, nothing should be typeset. Note
+that if |b<0|, the value of |h| will decrease even though nothing else happens.
+Programs that typeset from \.{DVI} files should be careful to make the rules
+line up carefully with digitized characters, as explained in connection with
+the |rule_pixels| subroutine below.
+
+\yskip\hang|put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
+such that the reference point of the character is at |(h,v)|. (The `put'
+commands are exactly like the `set' commands, except that they simply put out a
+character or a rule without moving the reference point afterwards.)
+
+\yskip\hang|put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
+
+\yskip\hang|put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
+
+\yskip\hang|put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
+
+\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
+|h| is not changed.
+
+\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
+may occur between \.{DVI} commands, but a |nop| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
+of a page: Set |(h,v,w,x,y,z):=(0,0,0,0,0,0)| and set the stack empty. Set
+the current font |f| to an undefined value. The ten $c_i$ parameters can
+be used to identify pages, if a user wants to print only part of a \.{DVI}
+file; \TeX82 gives them the values of \.{\\count0} $\ldots$ \.{\\count9}
+at the time \.{\\shipout} was invoked for this page. The parameter |p|
+points to the previous |bop| command in the file, where the first |bop|
+has $p=-1$.
+
+\yskip\hang|eop| 140. End of page: Print what you have read since the
+previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading
+programs that drive most output devices will have kept a buffer of the
+material that appears on the page that has just ended. This material is
+largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
+|h|~coordinate; so it usually needs to be sorted into some order that is
+appropriate for the device in question. \.{DVItype} does not do such sorting.)
+
+\yskip\hang|push| 141. Push the current values of |(h,v,w,x,y,z)| onto the
+top of the stack; do not change any of these values. Note that |f| is
+not pushed.
+
+\yskip\hang|pop| 142. Pop the top six values off of the stack and assign
+them to |(h,v,w,x,y,z)|. The number of pops should never exceed the number
+of pushes, since it would be highly embarrassing if the stack were empty
+at the time of a |pop| command.
+
+\yskip\hang|right1| 143 |b[1]|. Set |h:=h+b|, i.e., move right |b| units.
+The parameter is a signed number in two's complement notation, |-128<=b<128|;
+if |b<0|, the reference point actually moves left.
+
+\yskip\hang|right2| 144 |b[2]|. Same as |right1|, except that |b| is a
+two-byte quantity in the range |-32768<=b<32768|.
+
+\yskip\hang|right3| 145 |b[3]|. Same as |right1|, except that |b| is a
+three-byte quantity in the range |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|right4| 146 |b[4]|. Same as |right1|, except that |b| is a
+four-byte quantity in the range |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|w0| 147. Set |h:=h+w|; i.e., move right |w| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|w| gets particular values.
+
+\yskip\hang|w1| 148 |b[1]|. Set |w:=b| and |h:=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128<=b<128|. This command
+changes the current |w|~spacing and moves right by |b|.
+
+\yskip\hang|w2| 149 |b[2]|. Same as |w1|, but |b| is a two-byte-long
+parameter, |-32768<=b<32768|.
+
+\yskip\hang|w3| 150 |b[3]|. Same as |w1|, but |b| is a three-byte-long
+parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|w4| 151 |b[4]|. Same as |w1|, but |b| is a four-byte-long
+parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|x0| 152. Set |h:=h+x|; i.e., move right |x| units. The `|x|'
+commands are like the `|w|' commands except that they involve |x| instead
+of |w|.
+
+\yskip\hang|x1| 153 |b[1]|. Set |x:=b| and |h:=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128<=b<128|. This command
+changes the current |x|~spacing and moves right by |b|.
+
+\yskip\hang|x2| 154 |b[2]|. Same as |x1|, but |b| is a two-byte-long
+parameter, |-32768<=b<32768|.
+
+\yskip\hang|x3| 155 |b[3]|. Same as |x1|, but |b| is a three-byte-long
+parameter, |@t$-2^{23}$@><=b<@t$2^{23}$@>|.
+
+\yskip\hang|x4| 156 |b[4]|. Same as |x1|, but |b| is a four-byte-long
+parameter, |@t$-2^{31}$@><=b<@t$2^{31}$@>|.
+
+\yskip\hang|down1| 157 |a[1]|. Set |v:=v+a|, i.e., move down |a| units.
+The parameter is a signed number in two's complement notation, |-128<=a<128|;
+if |a<0|, the reference point actually moves up.
+
+\yskip\hang|down2| 158 |a[2]|. Same as |down1|, except that |a| is a
+two-byte quantity in the range |-32768<=a<32768|.
+
+\yskip\hang|down3| 159 |a[3]|. Same as |down1|, except that |a| is a
+three-byte quantity in the range |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|down4| 160 |a[4]|. Same as |down1|, except that |a| is a
+four-byte quantity in the range |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|y0| 161. Set |v:=v+y|; i.e., move down |y| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|y| gets particular values.
+
+\yskip\hang|y1| 162 |a[1]|. Set |y:=a| and |v:=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128<=a<128|. This command
+changes the current |y|~spacing and moves down by |a|.
+
+\yskip\hang|y2| 163 |a[2]|. Same as |y1|, but |a| is a two-byte-long
+parameter, |-32768<=a<32768|.
+
+\yskip\hang|y3| 164 |a[3]|. Same as |y1|, but |a| is a three-byte-long
+parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|y4| 165 |a[4]|. Same as |y1|, but |a| is a four-byte-long
+parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|z0| 166. Set |v:=v+z|; i.e., move down |z| units. The `|z|' commands
+are like the `|y|' commands except that they involve |z| instead of |y|.
+
+\yskip\hang|z1| 167 |a[1]|. Set |z:=a| and |v:=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128<=a<128|. This command
+changes the current |z|~spacing and moves down by |a|.
+
+\yskip\hang|z2| 168 |a[2]|. Same as |z1|, but |a| is a two-byte-long
+parameter, |-32768<=a<32768|.
+
+\yskip\hang|z3| 169 |a[3]|. Same as |z1|, but |a| is a three-byte-long
+parameter, |@t$-2^{23}$@><=a<@t$2^{23}$@>|.
+
+\yskip\hang|z4| 170 |a[4]|. Same as |z1|, but |a| is a four-byte-long
+parameter, |@t$-2^{31}$@><=a<@t$2^{31}$@>|.
+
+\yskip\hang|fnt_num_0| 171. Set |f:=0|. Font 0 must previously have been
+defined by a \\{fnt\_def} instruction, as explained below.
+
+\yskip\hang|fnt_num_1| through |fnt_num_63| (opcodes 172 to 234). Set
+|f:=1|, \dots, |f:=63|, respectively.
+
+\yskip\hang|fnt1| 235 |k[1]|. Set |f:=k|. \TeX82 uses this command for font
+numbers in the range |64<=k<256|.
+
+\yskip\hang|fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
+bytes long, so it is in the range |0<=k<65536|. \TeX82 never generates this
+command, but large font numbers may prove useful for specifications of
+color or texture, or they may be used for special fonts that have fixed
+numbers in some external coding scheme.
+
+\yskip\hang|fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
+bytes long, so it can be as large as $2^{24}-1$.
+
+\yskip\hang|fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
+bytes long; this is for the really big font numbers (and for the negative ones).
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
+programs are being used. \TeX82 generates |xxx1| when a short enough
+\.{\\special} appears, setting |k| to the number of bytes being sent. It
+is recommended that |x| be a string having the form of a keyword followed
+by possible parameters relevant to that keyword.
+
+\yskip\hang|xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0<=k<65536|.
+
+\yskip\hang|xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
+large. \TeX82 uses |xxx4| when |xxx1| would be incorrect.
+
+\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<256|; font definitions will be explained shortly.
+
+\yskip\hang|fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<65536|.
+
+\yskip\hang|fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0<=k<@t$2^{24}$@>|.
+
+\yskip\hang|fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |@t$-2^{31}$@><=k<@t$2^{31}$@>|.
+
+\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@ @d set_char_0=0 {typeset character 0 and move right}
+@d set1=128 {typeset a character and move right}
+@d set_rule=132 {typeset a rule and move right}
+@d put1=133 {typeset a character}
+@d put_rule=137 {typeset a rule}
+@d nop=138 {no operation}
+@d bop=139 {beginning of page}
+@d eop=140 {ending of page}
+@d push=141 {save the current positions}
+@d pop=142 {restore previous positions}
+@d right1=143 {move right}
+@d w0=147 {move right by |w|}
+@d w1=148 {move right and set |w|}
+@d x0=152 {move right by |x|}
+@d x1=153 {move right and set |x|}
+@d down1=157 {move down}
+@d y0=161 {move down by |y|}
+@d y1=162 {move down and set |y|}
+@d z0=166 {move down by |z|}
+@d z1=167 {move down and set |z|}
+@d fnt_num_0=171 {set current font to 0}
+@d fnt1=235 {set current font}
+@d xxx1=239 {extension to \.{DVI} primitives}
+@d xxx4=242 {potentially long extension to \.{DVI} primitives}
+@d fnt_def1=243 {define the meaning of a font number}
+@d pre=247 {preamble}
+@d post=248 {postamble beginning}
+@d post_post=249 {postamble ending}
+@d undefined_commands==250,251,252,253,254,255
+
+@ The preamble contains basic information about the file as a whole. As
+stated above, there are six parameters:
+$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
+The |i| byte identifies \.{DVI} format; currently this byte is always set
+to~2. (The value |i=3| is currently used for an extended format that
+allows a mixture of right-to-left and left-to-right typesetting.
+Some day we will set |i=4|, when \.{DVI} format makes another
+incompatible change---perhaps in the year 2048.)
+
+The next two parameters, |num| and |den|, are positive integers that define
+the units of measurement; they are the numerator and denominator of a
+fraction by which all dimensions in the \.{DVI} file could be multiplied
+in order to get lengths in units of $10^{-7}$ meters. (For example, there are
+exactly 7227 \TeX\ points in 254 centimeters, and \TeX82 works with scaled
+points where there are $2^{16}$ sp in a point, so \TeX82 sets |num=25400000|
+and $|den|=7227\cdot2^{16}=473628672$.)
+@^sp@>
+
+The |mag| parameter is what \TeX82 calls \.{\\mag}, i.e., 1000 times the
+desired magnification. The actual fraction by which dimensions are
+multiplied is therefore $mn/1000d$. Note that if a \TeX\ source document
+does not call for any `\.{true}' dimensions, and if you change it only by
+specifying a different \.{\\mag} setting, the \.{DVI} file that \TeX\
+creates will be completely unchanged except for the value of |mag| in the
+preamble and postamble. (Fancy \.{DVI}-reading programs allow users to
+override the |mag|~setting when a \.{DVI} file is being printed.)
+
+Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
+interpreted further. The length of comment |x| is |k|, where |0<=k<256|.
+
+@d id_byte=2 {identifies the kind of \.{DVI} files described here}
+
+@ Font definitions for a given font number |k| contain further parameters
+$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
+The four-byte value |c| is the check sum that \TeX\ (or whatever program
+generated the \.{DVI} file) found in the \.{TFM} file for this font;
+|c| should match the check sum of the font found by programs that read
+this \.{DVI} file.
+@^check sum@>
+
+Parameter |s| contains a fixed-point scale factor that is applied to the
+character widths in font |k|; font dimensions in \.{TFM} files and other
+font files are relative to this quantity, which is always positive and
+less than $2^{27}$. It is given in the same units as the other dimensions
+of the \.{DVI} file. Parameter |d| is similar to |s|; it is the ``design
+size,'' and (like~|s|) it is given in \.{DVI} units. Thus, font |k| is to be
+used at $|mag|\cdot s/1000d$ times its normal size.
+
+The remaining part of a font definition gives the external name of the font,
+which is an ASCII string of length |a+l|. The number |a| is the length
+of the ``area'' or directory, and |l| is the length of the font name itself;
+the standard local system font area is supposed to be used when |a=0|.
+The |n| field contains the area in its first |a| bytes.
+
+Font definitions must appear before the first use of a particular font number.
+Once font |k| is defined, it must not be defined again; however, we
+shall see below that font definitions appear in the postamble as well as
+in the pages, so in this sense each font number is defined exactly twice,
+if at all. Like |nop| commands, font definitions can
+appear before the first |bop|, or between an |eop| and a |bop|.
+
+@ The last page in a \.{DVI} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \TeX\ has
+accumulated about the file, making it possible to print subsets of the data
+with reasonable efficiency. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
+ $\langle\,$font definitions$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the final |bop| in the file. The next three
+parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
+appeared in the preamble.
+
+Parameters |l| and |u| give respectively the height-plus-depth of the tallest
+page and the width of the widest page, in the same units as other dimensions
+of the file. These numbers might be used by a \.{DVI}-reading program to
+position individual ``pages'' on large sheets of film or paper; however,
+the standard convention for output on normal size paper is to position each
+page so that the upper left-hand corner is exactly one inch from the left
+and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer
+software that attempts cleverly to center the output; a fixed position of
+the upper left corner is easiest for users to understand and to work with.
+Therefore |l| and~|u| are often ignored.
+
+Parameter |s| is the maximum stack depth (i.e., the largest excess of
+|push| commands over |pop| commands) needed to process this file. Then
+comes |t|, the total number of pages (|bop| commands) present.
+
+The postamble continues with font definitions, which are any number of
+\\{fnt\_def} commands as described above, possibly interspersed with |nop|
+commands. Each font number that is used in the \.{DVI} file must be defined
+exactly twice: Once before it is first selected by a \\{fnt} command, and once
+in the postamble.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the font definitions, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~2, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., @'337 in octal). \TeX\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{DVI} file makes it feasible for
+\.{DVI}-reading programs to find the postamble first, on most computers,
+even though \TeX\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{DVI} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{DVI} reader discovers all the information needed for typesetting the
+pages. Note that it is also possible to skip through the \.{DVI} file at
+reasonably high speed to locate a particular page, if that proves
+desirable. This saves a lot of time, since \.{DVI} files used in production
+jobs tend to be large.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{DVI}
+format has been designed to work most efficiently with modern operating systems.
+As noted above, \.{DVItype} will limit itself to the restrictions of standard
+\PASCAL\ if |random_reading| is defined to be |false|.
+
+@* Input from binary files.
+We have seen that a \.{DVI} file is a sequence of 8-bit bytes. The bytes
+appear physically in what is called a `|packed file of 0..255|'
+in \PASCAL\ lingo.
+
+Packing is system dependent, and many \PASCAL\ systems fail to implement
+such files in a sensible way (at least, from the viewpoint of producing
+good production software). For example, some systems treat all
+byte-oriented files as text, looking for end-of-line marks and such
+things. Therefore some system-dependent code is often needed to deal with
+binary files, even though most of the program in this section of
+\.{DVItype} is written in standard \PASCAL.
+@^system dependencies@>
+
+One common way to solve the problem is to consider files of |integer|
+numbers, and to convert an integer in the range $-2^{31}\L x<2^{31}$ to
+a sequence of four bytes $(a,b,c,d)$ using the following code, which
+avoids the controversial integer division of negative numbers:
+$$\vbox{\halign{#\hfil\cr
+|if x>=0 then a:=x div @'100000000|\cr
+|else begin x:=(x+@'10000000000)+@'10000000000; a:=x div @'100000000+128;|\cr
+\quad|end|\cr
+|x:=x mod @'100000000;|\cr
+|b:=x div @'200000; x:=x mod @'200000;|\cr
+|c:=x div @'400; d:=x mod @'400;|\cr}}$$
+The four bytes are then kept in a buffer and output one by one. (On 36-bit
+computers, an additional division by 16 is necessary at the beginning.
+Another way to separate an integer into four bytes is to use/abuse
+\PASCAL's variant records, storing an integer and retrieving bytes that are
+packed in the same place; {\sl caveat implementor!\/}) It is also desirable
+in some cases to read a hundred or so integers at a time, maintaining a
+larger buffer.
+
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+
+@ The program deals with two binary file variables: |dvi_file| is the main
+input file that we are translating into symbolic form, and |tfm_file| is
+the current font metric file from which character-width information is
+being read.
+
+@<Glob...@>=
+@!dvi_file:byte_file; {the stuff we are \.{DVI}typing}
+@!tfm_file:byte_file; {a font metric file}
+
+@ To prepare these files for input, we |reset| them. An extension of
+\PASCAL\ is needed in the case of |tfm_file|, since we want to associate
+it with external files whose names are specified dynamically (i.e., not
+known at compile time). The following code assumes that `|reset(f,s)|'
+does this, when |f| is a file variable and |s| is a string variable that
+specifies the file name. If |eof(f)| is true immediately after
+|reset(f,s)| has acted, we assume that no file named |s| is accessible.
+@^system dependencies@>
+
+@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+begin reset(dvi_file);
+cur_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin reset(tfm_file,cur_name);
+end;
+
+@ If you looked carefully at the preceding code, you probably asked,
+``What are |cur_loc| and |cur_name|?'' Good question. They're global
+variables: |cur_loc| is the number of the byte about to be read next from
+|dvi_file|, and |cur_name| is a string variable that will be set to the
+current font metric file name before |open_tfm_file| is called.
+
+@<Glob...@>=
+@!cur_loc:integer; {where we are about to look, in |dvi_file|}
+@!cur_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+
+@ It turns out to be convenient to read four bytes at a time, when we are
+inputting from \.{TFM} files. The input goes into global variables
+|b0|, |b1|, |b2|, and |b3|, with |b0| getting the first byte and |b3|
+the fourth.
+
+@<Glob...@>=
+@!b0,@!b1,@!b2,@!b3: eight_bits; {four bytes input at once}
+
+@ The |read_tfm_word| procedure sets |b0| through |b3| to the next
+four bytes in the current \.{TFM} file.
+@^system dependencies@>
+
+@p procedure read_tfm_word;
+begin read(tfm_file,b0); read(tfm_file,b1);
+read(tfm_file,b2); read(tfm_file,b3);
+end;
+
+@ We shall use another set of simple functions to read the next byte or
+bytes from |dvi_file|. There are seven possibilities, each of which is
+treated as a separate function in order to minimize the overhead for
+subroutine calls.
+@^system dependencies@>
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(dvi_file) then get_byte:=0
+else begin read(dvi_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:eight_bits;
+begin read(dvi_file,b); incr(cur_loc);
+if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function signed_pair:integer; {returns the next two bytes, signed}
+var a,@!b:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b);
+cur_loc:=cur_loc+2;
+if a<128 then signed_pair:=a*256+b
+else signed_pair:=(a-256)*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_trio:integer; {returns the next three bytes, signed}
+var a,@!b,@!c:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
+cur_loc:=cur_loc+3;
+if a<128 then signed_trio:=(a*256+b)*256+c
+else signed_trio:=((a-256)*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+
+@ Finally we come to the routines that are used only if |random_reading| is
+|true|. The driver program below needs two such routines: |dvi_length| should
+compute the total number of bytes in |dvi_file|, possibly also
+causing |eof(dvi_file)| to be true; and |move_to_byte(n)|
+should position |dvi_file| so that the next |get_byte| will read byte |n|,
+starting with |n=0| for the first byte in the file.
+@^system dependencies@>
+
+Such routines are, of course, highly system dependent. They are implemented
+here in terms of two assumed system routines called |set_pos| and |cur_pos|.
+The call |set_pos(f,n)| moves to item |n| in file |f|, unless |n| is
+negative or larger than the total number of items in |f|; in the latter
+case, |set_pos(f,n)| moves to the end of file |f|.
+The call |cur_pos(f)| gives the total number of items in |f|, if
+|eof(f)| is true; we use |cur_pos| only in such a situation.
+
+@p function dvi_length:integer;
+begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file);
+end;
+@#
+procedure move_to_byte(n:integer);
+begin set_pos(dvi_file,n); cur_loc:=n;
+end;
+
+@* Reading the font information.
+\.{DVI} file format does not include information about character widths, since
+that would tend to make the files a lot longer. But a program that reads
+a \.{DVI} file is supposed to know the widths of the characters that appear
+in \\{set\_char} commands. Therefore \.{DVItype} looks at the font metric
+(\.{TFM}) files for the fonts that are involved.
+@.TFM {\rm files}@>
+
+The character-width data appears also in other files (e.g., in \.{GF} files
+that specify bit patterns for digitized characters);
+thus, it is usually possible for \.{DVI} reading programs to get by with
+accessing only one file per font. \.{DVItype} has a comparatively easy
+task in this regard, since it needs only a few words of information from
+each font; other \.{DVI}-to-printer programs may have to go to some pains to
+deal with complications that arise when a large number of large font files
+all need to be accessed simultaneously.
+
+@ For purposes of this program, we need to know only two things about a
+given character |c| in a given font |f|: (1)~Is |c| a legal character
+in~|f|? (2)~If so, what is the width of |c|? We also need to know the
+symbolic name of each font, so it can be printed out, and we need to know
+the approximate size of inter-word spaces in each font.
+
+The answers to these questions appear implicitly in the following data
+structures. The current number of known fonts is |nf|. Each known font has
+an internal number |f|, where |0<=f<nf|; the external number of this font,
+i.e., its font identification number in the \.{DVI} file, is
+|font_num[f]|, and the external name of this font is the string that
+occupies positions |font_name[f]| through |font_name[f+1]-1| of the array
+|names|. The latter array consists of |ASCII_code| characters, and
+|font_name[nf]| is its first unoccupied position. A horizontal motion
+in the range |-4*font_space[f]<h<font_space[f]|
+will be treated as a `kern' that is not
+indicated in the printouts that \.{DVItype} produces between brackets. The
+legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive; more
+precisely, a given character |c| is valid in font |f| if and only if
+|font_bc[f]<=c<=font_ec[f]| and |char_width(f)(c)<>invalid_width|.
+Finally, |char_width(f)(c)=width[width_base[f]+c]|, and |width_ptr| is the
+first unused position of the |width| array.
+
+@d char_width_end(#)==#]
+@d char_width(#)==width[width_base[#]+char_width_end
+@d invalid_width==@'17777777777
+@d invalid_font==max_fonts
+
+@<Glob...@>=
+@!font_num:array [0..max_fonts] of integer; {external font numbers}
+@!font_name:array [0..max_fonts] of 1..name_size; {starting positions
+ of external font names}
+@!names:array [1..name_size] of ASCII_code; {characters of names}
+@!font_check_sum:array [0..max_fonts] of integer; {check sums}
+@!font_scaled_size:array [0..max_fonts] of integer; {scale factors}
+@!font_design_size:array [0..max_fonts] of integer; {design sizes}
+@!font_space:array [0..max_fonts] of integer; {boundary between ``small''
+ and ``large'' spaces}
+@!font_bc:array [0..max_fonts] of integer; {beginning characters in fonts}
+@!font_ec:array [0..max_fonts] of integer; {ending characters in fonts}
+@!width_base:array [0..max_fonts] of integer; {index into |width| table}
+@!width:array [0..max_widths] of integer; {character widths, in \.{DVI} units}
+@!nf:0..max_fonts; {the number of known fonts}
+@!width_ptr:0..max_widths; {the number of known character widths}
+
+@ @<Set init...@>=
+nf:=0; width_ptr:=0; font_name[0]:=1;@/
+font_space[invalid_font]:=0; {for |out_space| and |out_vmove|}
+font_bc[invalid_font]:=1; font_ec[invalid_font]:=0;
+
+@ It is, of course, a simple matter to print the name of a given font.
+
+@p procedure print_font(@!f:integer); {|f| is an internal font number}
+var k:0..name_size; {index into |names|}
+begin if f=invalid_font then print('UNDEFINED!')
+@.UNDEFINED@>
+else begin for k:=font_name[f] to font_name[f+1]-1 do
+ print(xchr[names[k]]);
+ end;
+end;
+
+@ An auxiliary array |in_width| is used to hold the widths as they are
+input. The global variables |tfm_check_sum| and |tfm_design_size| are
+set to the check sum and design size that
+appear in the current \.{TFM} file.
+
+@<Glob...@>=
+@!in_width:array[0..255] of integer; {\.{TFM} width data in \.{DVI} units}
+@!tfm_check_sum:integer; {check sum found in |tfm_file|}
+@!tfm_design_size:integer; {design size found in |tfm_file|, in \.{DVI} units}
+@!tfm_conv:real; {\.{DVI} units per absolute \.{TFM} unit}
+
+@ Here is a procedure that absorbs the necessary information from a
+\.{TFM} file, assuming that the file has just been successfully reset
+so that we are ready to read its first byte. (A complete description of
+\.{TFM} file format appears in the documentation of \.{TFtoPL} and will
+not be repeated here.) The procedure does not check the \.{TFM} file
+for validity, nor does it give explicit information about what is
+wrong with a \.{TFM} file that proves to be invalid; \.{DVI}-reading
+programs need not do this, since \.{TFM} files are almost always valid,
+and since the \.{TFtoPL} utility program has been specifically designed
+to diagnose \.{TFM} errors. The procedure simply returns |false| if it
+detects anything amiss in the \.{TFM} data.
+
+There is a parameter, |z|, which represents the scaling factor being
+used to compute the font dimensions; it must be in the range $0<z<2^{27}$.
+
+@p function in_TFM(@!z:integer):boolean; {input \.{TFM} data or return |false|}
+label 9997, {go here when the format is bad}
+ 9998, {go here when the information cannot be loaded}
+ 9999; {go here to exit}
+var k:integer; {index for loops}
+@!lh:integer; {length of the header data, in four-byte words}
+@!nw:integer; {number of words in the width table}
+@!wp:0..max_widths; {new value of |width_ptr| after successful input}
+@!alpha,@!beta:integer; {quantities used in the scaling computation}
+begin @<Read past the header data; |goto 9997| if there is a problem@>;
+@<Store character-width indices at the end of the |width| table@>;
+@<Read and convert the width values, setting up the |in_width| table@>;
+@<Move the widths from |in_width| to |width|, and append |pixel_width| values@>;
+width_ptr:=wp; in_TFM:=true; goto 9999;
+9997: print_ln('---not loaded, TFM file is bad');
+@.TFM file is bad@>
+9998: in_TFM:=false;
+9999: end;
+
+@ @<Read past the header...@>=
+read_tfm_word; lh:=b2*256+b3;
+read_tfm_word; font_bc[nf]:=b0*256+b1; font_ec[nf]:=b2*256+b3;
+if font_ec[nf]<font_bc[nf] then font_bc[nf]:=font_ec[nf]+1;
+if width_ptr+font_ec[nf]-font_bc[nf]+1>max_widths then
+ begin print_ln('---not loaded, DVItype needs larger width table');
+@.DVItype needs larger...@>
+ goto 9998;
+ end;
+wp:=width_ptr+font_ec[nf]-font_bc[nf]+1;
+read_tfm_word; nw:=b0*256+b1;
+if (nw=0)or(nw>256) then goto 9997;
+for k:=1 to 3+lh do
+ begin if eof(tfm_file) then goto 9997;
+ read_tfm_word;
+ if k=4 then
+ if b0<128 then tfm_check_sum:=((b0*256+b1)*256+b2)*256+b3
+ else tfm_check_sum:=(((b0-256)*256+b1)*256+b2)*256+b3
+ else if k=5 then
+ if b0<128 then
+ tfm_design_size:=round(tfm_conv*(((b0*256+b1)*256+b2)*256+b3))
+ else goto 9997;
+ end;
+
+@ @<Store character-width indices...@>=
+if wp>0 then for k:=width_ptr to wp-1 do
+ begin read_tfm_word;
+ if b0>nw then goto 9997;
+ width[k]:=b0;
+ end;
+
+@ The most important part of |in_TFM| is the width computation, which
+involves multiplying the relative widths in the \.{TFM} file by the
+scaling factor in the \.{DVI} file. This fixed-point multiplication
+must be done with precisely the same accuracy by all \.{DVI}-reading programs,
+in order to validate the assumptions made by \.{DVI}-writing programs
+like \TeX82.
+
+Let us therefore summarize what needs to be done. Each width in a \.{TFM}
+file appears as a four-byte quantity called a |fix_word|. A |fix_word|
+whose respective bytes are $(a,b,c,d)$ represents the number
+$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
+-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
+(No other choices of $a$ are allowed, since the magnitude of a \.{TFM}
+dimension must be less than 16.) We want to multiply this quantity by the
+integer~|z|, which is known to be less than $2^{27}$.
+If $|z|<2^{23}$, the individual multiplications $b\cdot z$, $c\cdot z$,
+$d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, 4, 8, or
+16, to obtain a multiplier less than $2^{23}$, and we can compensate for
+this later. If |z| has thereby been replaced by $|z|^\prime=|z|/2^e$, let
+$\beta=2^{4-e}$; we shall compute
+$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ if $a=0$,
+or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$.
+This calculation must be
+done exactly, for the reasons stated above; the following program does the
+job in a system-independent way, assuming that arithmetic is exact on
+numbers less than $2^{31}$ in magnitude.
+
+@<Read and convert the width values...@>=
+@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
+for k:=0 to nw-1 do
+ begin read_tfm_word;
+ in_width[k]:=(((((b3*z)div@'400)+(b2*z))div@'400)+(b1*z))div beta;
+ if b0>0 then if b0<255 then goto 9997
+ else in_width[k]:=in_width[k]-alpha;
+ end
+
+@ @<Replace |z|...@>=
+begin alpha:=16;
+while z>=@'40000000 do
+ begin z:=z div 2; alpha:=alpha+alpha;
+ end;
+beta:=256 div alpha; alpha:=alpha*z;
+end
+
+@ A \.{DVI}-reading program usually works with font files instead of
+\.{TFM} files, so \.{DVItype} is atypical in that respect. Font files
+should, however, contain exactly the same character width data that is
+found in the corresponding \.{TFM}s; check sums are used to help
+ensure this. In addition, font files usually also contain the widths of
+characters in pixels, since the device-independent character widths of
+\.{TFM} files are generally not perfect multiples of pixels.
+
+The |pixel_width| array contains this information; when |width[k]| is the
+device-independent width of some character in \.{DVI} units, |pixel_width[k]|
+is the corresponding width of that character in an actual font.
+The macro |char_pixel_width| is set up to be analogous to |char_width|.
+
+@d char_pixel_width(#)==pixel_width[width_base[#]+char_width_end
+
+@<Glob...@>=
+@!pixel_width:array[0..max_widths] of integer; {actual character widths,
+ in pixels}
+@!conv:real; {converts \.{DVI} units to pixels}
+@!true_conv:real; {converts unmagnified \.{DVI} units to pixels}
+@!numerator,@!denominator:integer; {stated conversion ratio}
+@!mag:integer; {magnification factor times 1000}
+
+@ The following code computes pixel widths by simply rounding the \.{TFM}
+widths to the nearest integer number of pixels, based on the conversion factor
+|conv| that converts \.{DVI} units to pixels. However, such a simple
+formula will not be valid for all fonts, and it will often give results that
+are off by $\pm1$ when a low-resolution font has been carefully
+hand-fitted. For example, a font designer often wants to make the letter `m'
+a pixel wider or narrower in order to make the font appear more consistent.
+\.{DVI}-to-printer programs should therefore input the correct pixel width
+information from font files whenever there is a chance that it may differ.
+A warning message may also be desirable in the case that at least one character
+is found whose pixel width differs from |conv*width| by more than a full pixel.
+@^system dependencies@>
+
+@d pixel_round(#)==round(conv*(#))
+
+@<Move the widths from |in_width| to |width|, and append |pixel_width| values@>=
+if in_width[0]<>0 then goto 9997; {the first width should be zero}
+width_base[nf]:=width_ptr-font_bc[nf];
+if wp>0 then for k:=width_ptr to wp-1 do
+ if width[k]=0 then
+ begin width[k]:=invalid_width; pixel_width[k]:=0;
+ end
+ else begin width[k]:=in_width[width[k]];
+ pixel_width[k]:=pixel_round(width[k]);
+ end
+
+@* Optional modes of output.
+\.{DVItype} will print different quantities of information based on some
+options that the user must specify: The |out_mode| level is set to one of
+five values (|errors_only|, |terse|, |mnemonics_only|,
+|verbose|, |the_works|), giving
+different degrees of output; and the typeout can be confined to a
+restricted subset of the pages by specifying the desired starting page and
+the maximum number of pages. Furthermore there is an option to specify the
+resolution of an assumed discrete output device, so that pixel-oriented
+calculations will be shown; and there is an option to override the
+magnification factor that is stated in the \.{DVI} file.
+
+The starting page is specified by giving a sequence of 1 to 10 numbers or
+asterisks separated by dots. For example, the specification `\.{1.*.-5}'
+can be used to refer to a page output by \TeX\ when $\.{\\count0}=1$
+and $\.{\\count2}=-5$. (Recall that |bop| commands in a \.{DVI} file
+are followed by ten `count' values.) An asterisk matches any number,
+so the `\.*' in `\.{1.*.-5}' means that \.{\\count1} is ignored when
+specifying the first page. If several pages match the given specification,
+\.{DVItype} will begin with the earliest such page in the file. The
+default specification `\.*' (which matches all pages) therefore denotes
+the page at the beginning of the file.
+
+When \.{DVItype} begins, it engages the user in a brief dialog so that the
+options will be specified. This part of \.{DVItype} requires nonstandard
+\PASCAL\ constructions to handle the online interaction; so it may be
+preferable in some cases to omit the dialog and simply to stick to the
+default options (|out_mode=the_works|, starting page `\.*',
+|max_pages=1000000|, |resolution=300.0|, |new_mag=0|). On other hand, the
+system-dependent routines that are needed are not complicated, so it will
+not be terribly difficult to introduce them.
+@^system dependencies@>
+
+@d errors_only=0 {value of |out_mode| when minimal printing occurs}
+@d terse=1 {value of |out_mode| for abbreviated output}
+@d mnemonics_only=2 {value of |out_mode| for medium-quantity output}
+@d verbose=3 {value of |out_mode| for detailed tracing}
+@d the_works=4 {|verbose|, plus check of postamble if |random_reading|}
+
+@<Glob...@>=
+@!out_mode:errors_only..the_works; {controls the amount of output}
+@!max_pages:integer; {at most this many |bop..eop| pages will be printed}
+@!resolution:real; {pixels per inch}
+@!new_mag:integer; {if positive, overrides the postamble's magnification}
+
+@ The starting page specification is recorded in two global arrays called
+|start_count| and |start_there|. For example, `\.{1.*.-5}' is represented
+by |start_there[0]=true|, |start_count[0]=1|, |start_there[1]=false|,
+|start_there[2]=true|, |start_count[2]=-5|.
+We also set |start_vals=2|, to indicate that count 2 was the last one
+mentioned. The other values of |start_count| and |start_there| are not
+important, in this example.
+
+@<Glob...@>=
+@!start_count:array[0..9] of integer; {count values to select starting page}
+@!start_there:array[0..9] of boolean; {is the |start_count| value relevant?}
+@!start_vals:0..9; {the last count considered significant}
+@!count:array[0..9] of integer; {the count values on the current page}
+
+@ @<Set init...@>=
+out_mode:=the_works; max_pages:=1000000; start_vals:=0; start_there[0]:=false;
+
+@ Here is a simple subroutine that tests if the current page might be the
+starting page.
+
+@p function start_match:boolean; {does |count| match the starting spec?}
+var k:0..9; {loop index}
+@!match:boolean; {does everything match so far?}
+begin match:=true;
+for k:=0 to start_vals do
+ if start_there[k]and(start_count[k]<>count[k]) then match:=false;
+start_match:=match;
+end;
+
+@ The |input_ln| routine waits for the user to type a line at his or her
+terminal; then it puts ASCII-code equivalents for the characters on that line
+into the |buffer| array. The |term_in| file is used for terminal input,
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!term_in:text_file; {the terminal, considered as an input file}
+@!term_out:text_file; {the terminal, considered as an output file}
+
+@ Since the terminal is being used for both input and output, some systems
+need a special routine to make sure that the user can see a prompt message
+before waiting for input based on that message. (Otherwise the message
+may just be sitting in a hidden buffer somewhere, and the user will have
+no idea what the program is waiting for.) We shall invoke a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+
+@ During the dialog, \.{DVItype} will treat the first blank space in a
+line as the end of that line. Therefore |input_ln| makes sure that there
+is always at least one blank space in |buffer|.
+@^system dependencies@>
+
+@p procedure input_ln; {inputs a line from the terminal}
+var k:0..terminal_line_length;
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+k:=0;
+while (k<terminal_line_length)and not eoln(term_in) do
+ begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
+ end;
+buffer[k]:=" ";
+end;
+
+@ The global variable |buf_ptr| is used while scanning each line of input;
+it points to the first unread character in |buffer|.
+
+@<Glob...@>=
+@!buf_ptr:0..terminal_line_length; {the number of characters read}
+
+@ Here is a routine that scans a (possibly signed) integer and computes
+the decimal value. If no decimal integer starts at |buf_ptr|, the
+value 0 is returned. The integer should be less than $2^{31}$ in
+absolute value.
+
+@p function get_integer:integer;
+var x:integer; {accumulates the value}
+@!negative:boolean; {should the value be negated?}
+begin if buffer[buf_ptr]="-" then
+ begin negative:=true; incr(buf_ptr);
+ end
+else negative:=false;
+x:=0;
+while (buffer[buf_ptr]>="0")and(buffer[buf_ptr]<="9") do
+ begin x:=10*x+buffer[buf_ptr]-"0"; incr(buf_ptr);
+ end;
+if negative then get_integer:=-x @+ else get_integer:=x;
+end;
+
+@ The selected options are put into global variables by the |dialog|
+procedure, which is called just as \.{DVItype} begins.
+@^system dependencies@>
+
+@p procedure dialog;
+label 1,2,3,4,5;
+var k:integer; {loop variable}
+begin rewrite(term_out); {prepare the terminal for output}
+write_ln(term_out,banner);
+@<Determine the desired |out_mode|@>;
+@<Determine the desired |start_count| values@>;
+@<Determine the desired |max_pages|@>;
+@<Determine the desired |resolution|@>;
+@<Determine the desired |new_mag|@>;
+@<Print all the selected options@>;
+end;
+
+@ @<Determine the desired |out_mode|@>=
+1: write(term_out,'Output level (default=4, ? for help): ');
+out_mode:=the_works; input_ln;
+if buffer[0]<>" " then
+ if (buffer[0]>="0")and(buffer[0]<="4") then out_mode:=buffer[0]-"0"
+ else begin write(term_out,'Type 4 for complete listing,');
+ write(term_out,' 0 for errors and fonts only,');
+ write_ln(term_out,' 1 or 2 or 3 for something in between.');
+ goto 1;
+ end
+
+@ @<Determine the desired |start...@>=
+2: write(term_out,'Starting page (default=*): ');
+start_vals:=0; start_there[0]:=false;
+input_ln; buf_ptr:=0; k:=0;
+if buffer[0]<>" " then
+ repeat if buffer[buf_ptr]="*" then
+ begin start_there[k]:=false; incr(buf_ptr);
+ end
+ else begin start_there[k]:=true; start_count[k]:=get_integer;
+ end;
+ if (k<9)and(buffer[buf_ptr]=".") then
+ begin incr(k); incr(buf_ptr);
+ end
+ else if buffer[buf_ptr]=" " then start_vals:=k
+ else begin write(term_out,'Type, e.g., 1.*.-5 to specify the ');
+ write_ln(term_out,'first page with \count0=1, \count2=-5.');
+ goto 2;
+ end;
+ until start_vals=k
+
+@ @<Determine the desired |max_pages|@>=
+3: write(term_out,'Maximum number of pages (default=1000000): ');
+max_pages:=1000000; input_ln; buf_ptr:=0;
+if buffer[0]<>" " then
+ begin max_pages:=get_integer;
+ if max_pages<=0 then
+ begin write_ln(term_out,'Please type a positive number.');
+ goto 3;
+ end;
+ end
+
+@ @<Determine the desired |resolution|@>=
+4: write(term_out,'Assumed device resolution');
+write(term_out,' in pixels per inch (default=300/1): ');
+resolution:=300.0; input_ln; buf_ptr:=0;
+if buffer[0]<>" " then
+ begin k:=get_integer;
+ if (k>0)and(buffer[buf_ptr]="/")and
+ (buffer[buf_ptr+1]>"0")and(buffer[buf_ptr+1]<="9") then
+ begin incr(buf_ptr); resolution:=k/get_integer;
+ end
+ else begin write(term_out,'Type a ratio of positive integers;');
+ write_ln(term_out,' (1 pixel per mm would be 254/10).');
+ goto 4;
+ end;
+ end
+
+@ @<Determine the desired |new_mag|@>=
+5: write(term_out,'New magnification (default=0 to keep the old one): ');
+new_mag:=0; input_ln; buf_ptr:=0;
+if buffer[0]<>" " then
+ if (buffer[0]>="0")and(buffer[0]<="9") then new_mag:=get_integer
+ else begin write(term_out,'Type a positive integer to override ');
+ write_ln(term_out,'the magnification in the DVI file.');
+ goto 5;
+ end
+
+@ After the dialog is over, we print the options so that the user
+can see what \.{DVItype} thought was specified.
+
+@<Print all the selected options@>=
+print_ln('Options selected:');
+@.Options selected@>
+print(' Starting page = ');
+for k:=0 to start_vals do
+ begin if start_there[k] then print(start_count[k]:1)
+ else print('*');
+ if k<start_vals then print('.')
+ else print_ln(' ');
+ end;
+print_ln(' Maximum number of pages = ',max_pages:1);
+print(' Output level = ',out_mode:1);
+case out_mode of
+errors_only: print_ln(' (showing bops, fonts, and error messages only)');
+terse: print_ln(' (terse)');
+mnemonics_only: print_ln(' (mnemonics)');
+verbose: print_ln(' (verbose)');
+the_works: if random_reading then print_ln(' (the works)')
+ else begin out_mode:=verbose;
+ print_ln(' (the works: same as level 3 in this DVItype)');
+ end;
+end;@/
+print_ln(' Resolution = ',resolution:12:8,' pixels per inch');
+if new_mag>0 then print_ln(' New magnification factor = ',new_mag/1000:8:3)
+
+@* Defining fonts.
+When |out_mode=the_works|, \.{DVItype} reads the postamble first and loads
+all of the fonts defined there; then it processes the pages. In this
+case, a \\{fnt\_def} command should match a previous definition if and only
+if the \\{fnt\_def} being processed is not in the postamble. But if
+|out_mode<the_works|, \.{DVItype} reads the pages first and the postamble
+last, so the conventions are reversed: a \\{fnt\_def} should match a previous
+\\{fnt\_def} if and only if the current one is a part of the postamble.
+
+A global variable |in_postamble| is provided to tell whether we are
+processing the postamble or not.
+
+@<Glob...@>=
+@!in_postamble:boolean; {are we reading the postamble?}
+
+@ @<Set init...@>=
+in_postamble:=false;
+
+@ The following subroutine does the necessary things when a \\{fnt\_def}
+command is being processed.
+
+@p procedure define_font(@!e:integer); {|e| is an external font number}
+var f:0..max_fonts;
+@!p:integer; {length of the area/directory spec}
+@!n:integer; {length of the font name proper}
+@!c,@!q,@!d,@!m:integer; {check sum, scaled size, design size, magnification}
+@!r:0..name_length; {index into |cur_name|}
+@!j,@!k:0..name_size; {indices into |names|}
+@!mismatch:boolean; {do names disagree?}
+begin if nf=max_fonts then abort('DVItype capacity exceeded (max fonts=',
+ max_fonts:1,')!');
+@.DVItype capacity exceeded...@>
+font_num[nf]:=e; f:=0;
+while font_num[f]<>e do incr(f);
+@<Read the font parameters into position for font |nf|, and
+ print the font name@>;
+if ((out_mode=the_works)and in_postamble)or@|
+ ((out_mode<the_works)and not in_postamble) then
+ begin if f<nf then print_ln('---this font was already defined!');
+@.this font was already defined@>
+ end
+else begin if f=nf then print_ln('---this font wasn''t loaded before!');
+@.this font wasn't loaded before@>
+ end;
+if f=nf then @<Load the new font, unless there are problems@>
+else @<Check that the current font definition matches the old one@>;
+end;
+
+@ @<Check that the current...@>=
+begin if font_check_sum[f]<>c then
+ print_ln('---check sum doesn''t match previous definition!');
+@.check sum doesn't match@>
+if font_scaled_size[f]<>q then
+ print_ln('---scaled size doesn''t match previous definition!');
+@.scaled size doesn't match@>
+if font_design_size[f]<>d then
+ print_ln('---design size doesn''t match previous definition!');
+@.design size doesn't match@>
+j:=font_name[f]; k:=font_name[nf];
+if font_name[f+1]-j<>font_name[nf+1]-k then mismatch:=true
+else begin mismatch:=false;
+ while j<font_name[f+1] do
+ begin if names[j]<>names[k] then mismatch:=true;
+ incr(j); incr(k);
+ end;
+ end;
+if mismatch then print_ln('---font name doesn''t match previous definition!');
+@.font name doesn't match@>
+end
+
+@ @<Read the font parameters into position for font |nf|...@>=
+c:=signed_quad; font_check_sum[nf]:=c;@/
+q:=signed_quad; font_scaled_size[nf]:=q;@/
+d:=signed_quad; font_design_size[nf]:=d;@/
+if (q<=0) or (d<=0) then m:=1000
+else m:=round((1000.0*conv*q)/(true_conv*d));
+p:=get_byte; n:=get_byte;
+if font_name[nf]+n+p>name_size then
+ abort('DVItype capacity exceeded (name size=',name_size:1,')!');
+@.DVItype capacity exceeded...@>
+font_name[nf+1]:=font_name[nf]+n+p;
+if showing then print(': ')
+ {when |showing| is true, the font number has already been printed}
+else print('Font ',e:1,': ');
+if n+p=0 then print('null font name!')
+@.null font name@>
+else for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=get_byte;
+print_font(nf);
+if not showing then if m<>1000 then print(' scaled ',m:1)
+@.scaled@>
+
+@ @<Load the new font, unless there are problems@>=
+begin @<Move font name into the |cur_name| string@>;
+open_tfm_file;
+if eof(tfm_file) then
+ print('---not loaded, TFM file can''t be opened!')
+@.TFM file can\'t be opened@>
+else begin if (q<=0)or(q>=@'1000000000) then
+ print('---not loaded, bad scale (',q:1,')!')
+@.bad scale@>
+ else if (d<=0)or(d>=@'1000000000) then
+ print('---not loaded, bad design size (',d:1,')!')
+@.bad design size@>
+ else if in_TFM(q) then @<Finish loading the new font info@>;
+ end;
+if out_mode=errors_only then print_ln(' ');
+end
+
+@ @<Finish loading...@>=
+begin font_space[nf]:=q div 6; {this is a 3-unit ``thin space''}
+if (c<>0)and(tfm_check_sum<>0)and(c<>tfm_check_sum) then
+ begin print_ln('---beware: check sums do not agree!');
+@.beware: check sums do not agree@>
+@.check sums do not agree@>
+ print_ln(' (',c:1,' vs. ',tfm_check_sum:1,')');
+ print(' ');
+ end;
+if abs(tfm_design_size-d)>2 then
+ begin print_ln('---beware: design sizes do not agree!');
+@.beware: design sizes do not agree@>
+@.design sizes do not agree@>
+ print_ln(' (',d:1,' vs. ',tfm_design_size:1,')');
+ print(' ');
+ end;
+print('---loaded at size ',q:1,' DVI units');
+d:=round((100.0*conv*q)/(true_conv*d));
+if d<>100 then
+ begin print_ln(' '); print(' (this font is magnified ',d:1,'%)');
+ end;
+@.this font is magnified@>
+incr(nf); {now the new font is officially present}
+end
+
+@ If |p=0|, i.e., if no font directory has been specified, \.{DVItype}
+is supposed to use the default font directory, which is a
+system-dependent place where the standard fonts are kept.
+The string variable |default_directory| contains the name of this area.
+@^system dependencies@>
+
+@d default_directory_name=='TeXfonts:' {change this to the correct name}
+@d default_directory_name_length=9 {change this to the correct length}
+
+@<Glob...@>=
+@!default_directory:packed array[1..default_directory_name_length] of char;
+
+@ @<Set init...@>=
+default_directory:=default_directory_name;
+
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font. This usually means that we need to
+prepend the name of the default directory, and
+to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
+to upper case, since |cur_name| is a \PASCAL\ string.
+@^system dependencies@>
+
+@<Move font name into the |cur_name| string@>=
+for k:=1 to name_length do cur_name[k]:=' ';
+if p=0 then
+ begin for k:=1 to default_directory_name_length do
+ cur_name[k]:=default_directory[k];
+ r:=default_directory_name_length;
+ end
+else r:=0;
+for k:=font_name[nf] to font_name[nf+1]-1 do
+ begin incr(r);
+ if r+4>name_length then
+ abort('DVItype capacity exceeded (max font name length=',
+ name_length:1,')!');
+@.DVItype capacity exceeded...@>
+ if (names[k]>="a")and(names[k]<="z") then
+ cur_name[r]:=xchr[names[k]-@'40]
+ else cur_name[r]:=xchr[names[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='T'; cur_name[r+3]:='F'; cur_name[r+4]:='M'
+
+@* Low level output routines.
+Simple text in the \.{DVI} file is saved in a buffer until |line_length-2|
+characters have accumulated, or until some non-simple \.{DVI} operation
+occurs. Then the accumulated text is printed on a line, surrounded by
+brackets. The global variable |text_ptr| keeps track of the number of
+characters currently in the buffer.
+
+@<Glob...@>=
+@!text_ptr:0..line_length; {the number of characters in |text_buf|}
+@!text_buf:array[1..line_length] of ASCII_code; {saved characters}
+
+@ @<Set init...@>=
+text_ptr:=0;
+
+@ The |flush_text| procedure will empty the buffer if there is something in it.
+
+@p procedure flush_text;
+var k:0..line_length; {index into |text_buf|}
+begin if text_ptr>0 then
+ begin if out_mode>errors_only then
+ begin print('[');
+ for k:=1 to text_ptr do print(xchr[text_buf[k]]);
+ print_ln(']');
+ end;
+ text_ptr:=0;
+ end;
+end;
+
+@ And the |out_text| procedure puts something in it.
+
+@p procedure out_text(c:ASCII_code);
+begin if text_ptr=line_length-2 then flush_text;
+incr(text_ptr); text_buf[text_ptr]:=c;
+end;
+
+@* Translation to symbolic form.
+The main work of \.{DVItype} is accomplished by the |do_page| procedure,
+which produces the output for an entire page, assuming that the |bop|
+command for that page has already been processed. This procedure is
+essentially an interpretive routine that reads and acts on the \.{DVI}
+commands.
+
+@ The definition of \.{DVI} files refers to six registers,
+$(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units. In practice,
+we also need registers |hh| and |vv|, the pixel analogs of $h$ and $v$,
+since it is not always true that |hh=pixel_round(h)| or
+|vv=pixel_round(v)|.
+
+The stack of $(h,v,w,x,y,z)$ values is represented by eight arrays
+called |hstack|, \dots, |zstack|, |hhstack|, and |vvstack|.
+
+@<Glob...@>=
+@!h,@!v,@!w,@!x,@!y,@!z,@!hh,@!vv:integer; {current state values}
+@!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack:
+ array [0..stack_size] of integer; {pushed down values in \.{DVI} units}
+@!hhstack,@!vvstack:
+ array [0..stack_size] of integer; {pushed down values in pixels}
+
+@ Three characteristics of the pages (their |max_v|, |max_h|, and
+|max_s|) are specified in the postamble, and a warning message
+is printed if these limits are exceeded. Actually |max_v| is set to
+the maximum height plus depth of a page, and |max_h| to the maximum width,
+for purposes of page layout. Since characters can legally be set outside
+of the page boundaries, it is not an error when |max_v| or |max_h| is
+exceeded. But |max_s| should not be exceeded.
+
+The postamble also specifies the total number of pages; \.{DVItype}
+checks to see if this total is accurate.
+
+@<Glob...@>=
+@!max_v:integer; {the value of |abs(v)| should probably not exceed this}
+@!max_h:integer; {the value of |abs(h)| should probably not exceed this}
+@!max_s:integer; {the stack depth should not exceed this}
+@!max_v_so_far,@!max_h_so_far,@!max_s_so_far:integer; {the record high levels}
+@!total_pages:integer; {the stated total number of pages}
+@!page_count:integer; {the total number of pages seen so far}
+
+@ @<Set init...@>=
+max_v:=@'17777777777-99; max_h:=@'17777777777-99; max_s:=stack_size+1;@/
+max_v_so_far:=0; max_h_so_far:=0; max_s_so_far:=0; page_count:=0;
+
+@ Before we get into the details of |do_page|, it is convenient to
+consider a simpler routine that computes the first parameter of each
+opcode.
+
+@d four_cases(#)==#,#+1,#+2,#+3
+@d eight_cases(#)==four_cases(#),four_cases(#+4)
+@d sixteen_cases(#)==eight_cases(#),eight_cases(#+8)
+@d thirty_two_cases(#)==sixteen_cases(#),sixteen_cases(#+16)
+@d sixty_four_cases(#)==thirty_two_cases(#),thirty_two_cases(#+32)
+
+@p function first_par(o:eight_bits):integer;
+begin case o of
+sixty_four_cases(set_char_0),sixty_four_cases(set_char_0+64):
+ first_par:=o-set_char_0;
+set1,put1,fnt1,xxx1,fnt_def1: first_par:=get_byte;
+set1+1,put1+1,fnt1+1,xxx1+1,fnt_def1+1: first_par:=get_two_bytes;
+set1+2,put1+2,fnt1+2,xxx1+2,fnt_def1+2: first_par:=get_three_bytes;
+right1,w1,x1,down1,y1,z1: first_par:=signed_byte;
+right1+1,w1+1,x1+1,down1+1,y1+1,z1+1: first_par:=signed_pair;
+right1+2,w1+2,x1+2,down1+2,y1+2,z1+2: first_par:=signed_trio;
+set1+3,set_rule,put1+3,put_rule,right1+3,w1+3,x1+3,down1+3,y1+3,z1+3,
+ fnt1+3,xxx1+3,fnt_def1+3: first_par:=signed_quad;
+nop,bop,eop,push,pop,pre,post,post_post,undefined_commands: first_par:=0;
+w0: first_par:=w;
+x0: first_par:=x;
+y0: first_par:=y;
+z0: first_par:=z;
+sixty_four_cases(fnt_num_0): first_par:=o-fnt_num_0;
+end;
+end;
+
+@ Here is another subroutine that we need: It computes the number of
+pixels in the height or width of a rule. Characters and rules will line up
+properly if the sizes are computed precisely as specified here. (Since
+|conv| is computed with some floating-point roundoff error, in a
+machine-dependent way, format designers who are tailoring something for a
+particular resolution should not plan their measurements to come out to an
+exact integer number of pixels; they should compute things so that the
+rule dimensions are a little less than an integer number of pixels, e.g.,
+4.99 instead of 5.00.)
+
+@p function rule_pixels(x:integer):integer;
+ {computes $\lceil|conv|\cdot x\rceil$}
+var n:integer;
+begin n:=trunc(conv*x);
+if n<conv*x then rule_pixels:=n+1 @+ else rule_pixels:=n;
+end;
+
+@ Strictly speaking, the |do_page| procedure is really a function with
+side effects, not a `\&{procedure}'\thinspace; it returns the value |false|
+if \.{DVItype} should be aborted because of some unusual happening. The
+subroutine is organized as a typical interpreter, with a multiway branch
+on the command code followed by |goto| statements leading to routines that
+finish up the activities common to different commands. We will use the
+following labels:
+
+@d fin_set=41 {label for commands that set or put a character}
+@d fin_rule=42 {label for commands that set or put a rule}
+@d move_right=43 {label for commands that change |h|}
+@d move_down=44 {label for commands that change |v|}
+@d show_state=45 {label for commands that change |s|}
+@d change_font=46 {label for commands that change |cur_font|}
+
+@ Some \PASCAL\ compilers severely restrict the length of procedure bodies,
+so we shall split |do_page| into two parts, one of which is
+called |special_cases|. The different parts communicate with each other
+via the global variables mentioned above, together with the following ones:
+
+@<Glob...@>=
+@!s:integer; {current stack size}
+@!ss:integer; {stack size to print}
+@!cur_font:integer; {current internal font number}
+@!showing:boolean; {is the current command being translated in full?}
+
+@ Here is the overall setup.
+
+@p @t\4@>@<Declare the function called |special_cases|@>@;
+function do_page:boolean;
+label fin_set,fin_rule,move_right,show_state,done,9998,9999;
+var o:eight_bits; {operation code of the current command}
+@!p,@!q:integer; {parameters of the current command}
+@!a:integer; {byte number of the current command}
+@!hhh:integer; {|h|, rounded to the nearest pixel}
+begin cur_font:=invalid_font; {set current font undefined}
+s:=0; h:=0; v:=0; w:=0; x:=0; y:=0; z:=0; hh:=0; vv:=0;
+ {initialize the state variables}
+while true do @<Translate the next command in the \.{DVI} file;
+ |goto 9999| with |do_page=true| if it was |eop|;
+ |goto 9998| if premature termination is needed@>;
+9998: print_ln('!'); do_page:=false;
+9999: end;
+
+@ Commands are broken down into ``major'' and ``minor'' categories:
+A major command is always shown in full, while a minor one is
+put into the buffer in abbreviated form. Minor commands, which
+account for the bulk of most \.{DVI} files, involve horizontal spacing
+and the typesetting of characters in a line; these are shown in full
+only if |out_mode>=verbose|.
+
+@d show(#)==begin flush_text; showing:=true; print(a:1,': ',#);
+ end
+@d major(#)==if out_mode>errors_only then show(#)
+@d minor(#)==if out_mode>terse then
+ begin showing:=true; print(a:1,': ',#);
+ end
+@d error(#)==if not showing then show(#) else print(' ',#)
+
+@<Translate the next command...@>=
+begin a:=cur_loc; showing:=false;
+o:=get_byte; p:=first_par(o);
+if eof(dvi_file) then bad_dvi('the file ended prematurely');
+@.the file ended prematurely@>
+@<Start translation of command |o| and |goto| the appropriate label to
+ finish the job@>;
+fin_set: @<Finish a command that either sets or puts a character, then
+ |goto move_right| or |done|@>;
+fin_rule: @<Finish a command that either sets or puts a rule, then
+ |goto move_right| or |done|@>;
+move_right: @<Finish a command that sets |h:=h+q|, then |goto done|@>;
+show_state: @<Show the values of |ss|, |h|, |v|, |w|, |x|, |y|, |z|,
+ |hh|, and |vv|; then |goto done|@>;
+done: if showing then print_ln(' ');
+end
+
+@ The multiway switch in |first_par|, above, was organized by the length
+of each command; the one in |do_page| is organized by the semantics.
+
+@<Start translation...@>=
+if o<set_char_0+128 then @<Translate a |set_char| command@>
+else case o of
+ four_cases(set1): begin major('set',o-set1+1:1,' ',p:1); goto fin_set;
+ end;
+ four_cases(put1): begin major('put',o-put1+1:1,' ',p:1); goto fin_set;
+ end;
+ set_rule: begin major('setrule'); goto fin_rule;
+ end;
+ put_rule: begin major('putrule'); goto fin_rule;
+ end;
+ @t\4@>@<Cases for commands |nop|, |bop|, \dots, |pop|@>@;
+ @t\4@>@<Cases for horizontal motion@>@;
+ othercases if special_cases(o,p,a) then goto done@+else goto 9998
+ endcases
+
+@ @<Declare the function called |special_cases|@>=
+function special_cases(@!o:eight_bits;@!p,@!a:integer):boolean;
+label change_font,move_down,done,9998;
+var q:integer; {parameter of the current command}
+@!k:integer; {loop index}
+@!bad_char:boolean; {has a non-ASCII character code appeared in this \\{xxx}?}
+@!pure:boolean; {is the command error-free?}
+@!vvv:integer; {|v|, rounded to the nearest pixel}
+begin pure:=true;
+case o of
+@t\4@>@<Cases for vertical motion@>@;
+@t\4@>@<Cases for fonts@>@;
+four_cases(xxx1): @<Translate an |xxx| command and |goto done|@>;
+pre: begin error('preamble command within a page!'); goto 9998;
+ end;
+@.preamble command within a page@>
+post,post_post: begin error('postamble command within a page!'); goto 9998;
+@.postamble command within a page@>
+ end;
+othercases begin error('undefined command ',o:1,'!');
+ goto done;
+@.undefined command@>
+ end
+endcases;
+move_down: @<Finish a command that sets |v:=v+p|, then |goto done|@>;
+change_font: @<Finish a command that changes the current font,
+ then |goto done|@>;
+9998: pure:=false;
+done: special_cases:=pure;
+end;
+
+@ @<Cases for commands |nop|, |bop|, \dots, |pop|@>=
+nop: begin minor('nop'); goto done;
+ end;
+bop: begin error('bop occurred before eop!'); goto 9998;
+@.bop occurred before eop@>
+ end;
+eop: begin major('eop');
+ if s<>0 then error('stack not empty at end of page (level ',
+ s:1,')!');
+@.stack not empty...@>
+ do_page:=true; print_ln(' '); goto 9999;
+ end;
+push: begin major('push');
+ if s=max_s_so_far then
+ begin max_s_so_far:=s+1;
+ if s=max_s then error('deeper than claimed in postamble!');
+@.deeper than claimed...@>
+@.push deeper than claimed...@>
+ if s=stack_size then
+ begin error('DVItype capacity exceeded (stack size=',
+ stack_size:1,')'); goto 9998;
+ end;
+ end;
+ hstack[s]:=h; vstack[s]:=v; wstack[s]:=w;
+ xstack[s]:=x; ystack[s]:=y; zstack[s]:=z;
+ hhstack[s]:=hh; vvstack[s]:=vv; incr(s); ss:=s-1; goto show_state;
+ end;
+pop: begin major('pop');
+ if s=0 then error('(illegal at level zero)!')
+ else begin decr(s); hh:=hhstack[s]; vv:=vvstack[s];
+ h:=hstack[s]; v:=vstack[s]; w:=wstack[s];
+ x:=xstack[s]; y:=ystack[s]; z:=zstack[s];
+ end;
+ ss:=s; goto show_state;
+ end;
+
+@ Rounding to the nearest pixel is best done in the manner shown here, so as
+to be inoffensive to the eye: When the horizontal motion is small, like a
+kern, |hh| changes by rounding the kern; but when the motion is large, |hh|
+changes by rounding the true position |h| so that accumulated rounding errors
+disappear. We allow a larger space in the negative direction than in
+the positive one, because \TeX\ makes comparatively
+large backspaces when it positions accents.
+
+@d out_space(#)==if (p>=font_space[cur_font])or(p<=-4*font_space[cur_font]) then
+ begin out_text(" "); hh:=pixel_round(h+p);
+ end
+ else hh:=hh+pixel_round(p);
+ minor(#,' ',p:1); q:=p; goto move_right
+
+@<Cases for horizontal motion@>=
+four_cases(right1):begin out_space('right',o-right1+1:1);
+ end;
+w0,four_cases(w1):begin w:=p; out_space('w',o-w0:1);
+ end;
+x0,four_cases(x1):begin x:=p; out_space('x',o-x0:1);
+ end;
+
+@ Vertical motion is done similarly, but with the threshold between
+``small'' and ``large'' increased by a factor of five. The idea is to make
+fractions like ``$1\over2$'' round consistently, but to absorb accumulated
+rounding errors in the baseline-skip moves.
+
+@d out_vmove(#)==if abs(p)>=5*font_space[cur_font] then vv:=pixel_round(v+p)
+ else vv:=vv+pixel_round(p);
+ major(#,' ',p:1); goto move_down
+
+@<Cases for vertical motion@>=
+four_cases(down1):begin out_vmove('down',o-down1+1:1);
+ end;
+y0,four_cases(y1):begin y:=p; out_vmove('y',o-y0:1);
+ end;
+z0,four_cases(z1):begin z:=p; out_vmove('z',o-z0:1);
+ end;
+
+@ @<Cases for fonts@>=
+sixty_four_cases(fnt_num_0): begin major('fntnum',p:1);
+ goto change_font;
+ end;
+four_cases(fnt1): begin major('fnt',o-fnt1+1:1,' ',p:1);
+ goto change_font;
+ end;
+four_cases(fnt_def1): begin major('fntdef',o-fnt_def1+1:1,' ',p:1);
+ define_font(p); goto done;
+ end;
+
+@ @<Translate an |xxx| command and |goto done|@>=
+begin major('xxx '''); bad_char:=false;
+if p<0 then error('string of negative length!');
+@.string of negative length@>
+for k:=1 to p do
+ begin q:=get_byte;
+ if (q<" ")or(q>"~") then bad_char:=true;
+ if showing then print(xchr[q]);
+ end;
+if showing then print('''');
+if bad_char then error('non-ASCII character in xxx command!');
+@.non-ASCII character...@>
+goto done;
+end
+
+@ @<Translate a |set_char|...@>=
+begin if (o>" ")and(o<="~") then
+ begin out_text(p); minor('setchar',p:1);
+ end
+else major('setchar',p:1);
+goto fin_set;
+end
+
+@ @<Finish a command that either sets or puts a character...@>=
+if p<0 then p:=255-((-1-p) mod 256)
+else if p>=256 then p:=p mod 256; {width computation for oriental fonts}
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+if (p<font_bc[cur_font])or(p>font_ec[cur_font]) then q:=invalid_width
+else q:=char_width(cur_font)(p);
+if q=invalid_width then
+ begin error('character ',p:1,' invalid in font ');
+@.character $c$ invalid...@>
+ print_font(cur_font);
+ if cur_font<>invalid_font then
+ print('!'); {the invalid font has `\.!' in its name}
+ end;
+if o>=put1 then goto done;
+if q=invalid_width then q:=0
+else hh:=hh+char_pixel_width(cur_font)(p);
+goto move_right
+
+@ @<Finish a command that either sets or puts a rule...@>=
+q:=signed_quad;
+if showing then
+ begin print(' height ',p:1,', width ',q:1);
+ if out_mode>mnemonics_only then
+ if (p<=0)or(q<=0) then print(' (invisible)')
+ else print(' (',rule_pixels(p):1,'x',rule_pixels(q):1,' pixels)');
+ end;
+if o=put_rule then goto done;
+if showing then if out_mode>mnemonics_only then print_ln(' ');
+hh:=hh+rule_pixels(q); goto move_right
+
+@ A sequence of consecutive rules, or consecutive characters in a fixed-width
+font whose width is not an integer number of pixels, can cause |hh| to drift
+far away from a correctly rounded value. \.{DVItype} ensures that the
+amount of drift will never exceed |max_drift| pixels.
+
+Since \.{DVItype} is intended to diagnose strange errors, it checks
+carefully to make sure that |h| and |v| do not get out of range.
+Normal \.{DVI}-reading programs need not do this.
+
+@d infinity==@'17777777777 {$\infty$ (approximately)}
+@d max_drift=2 {we insist that abs|(hh-pixel_round(h))<=max_drift|}
+
+@<Finish a command that sets |h:=h+q|, then |goto done|@>=
+if (h>0)and(q>0) then if h>infinity-q then
+ begin error('arithmetic overflow! parameter changed from ',
+@.arithmetic overflow...@>
+ q:1,' to ',infinity-h:1);
+ q:=infinity-h;
+ end;
+if (h<0)and(q<0) then if -h>q+infinity then
+ begin error('arithmetic overflow! parameter changed from ',
+ q:1, ' to ',(-h)-infinity:1);
+ q:=(-h)-infinity;
+ end;
+hhh:=pixel_round(h+q);
+if abs(hhh-hh)>max_drift then
+ if hhh>hh then hh:=hhh-max_drift
+ else hh:=hhh+max_drift;
+if showing then if out_mode>mnemonics_only then
+ begin print(' h:=',h:1);
+ if q>=0 then print('+');
+ print(q:1,'=',h+q:1,', hh:=',hh:1);
+ end;
+h:=h+q;
+if abs(h)>max_h_so_far then
+ begin if abs(h)>max_h+99 then
+ begin error('warning: |h|>',max_h:1,'!');
+@.warning: |h|...@>
+ max_h:=abs(h);
+ end;
+ max_h_so_far:=abs(h);
+ end;
+goto done
+
+@ @<Finish a command that sets |v:=v+p|, then |goto done|@>=
+if (v>0)and(p>0) then if v>infinity-p then
+ begin error('arithmetic overflow! parameter changed from ',
+@.arithmetic overflow...@>
+ p:1,' to ',infinity-v:1);
+ p:=infinity-v;
+ end;
+if (v<0)and(p<0) then if -v>p+infinity then
+ begin error('arithmetic overflow! parameter changed from ',
+ p:1, ' to ',(-v)-infinity:1);
+ p:=(-v)-infinity;
+ end;
+vvv:=pixel_round(v+p);
+if abs(vvv-vv)>max_drift then
+ if vvv>vv then vv:=vvv-max_drift
+ else vv:=vvv+max_drift;
+if showing then if out_mode>mnemonics_only then
+ begin print(' v:=',v:1);
+ if p>=0 then print('+');
+ print(p:1,'=',v+p:1,', vv:=',vv:1);
+ end;
+v:=v+p;
+if abs(v)>max_v_so_far then
+ begin if abs(v)>max_v+99 then
+ begin error('warning: |v|>',max_v:1,'!');
+@.warning: |v|...@>
+ max_v:=abs(v);
+ end;
+ max_v_so_far:=abs(v);
+ end;
+goto done
+
+@ @<Show the values of |ss|, |h|, |v|, |w|, |x|, |y|, |z|...@>=
+if showing then if out_mode>mnemonics_only then
+ begin print_ln(' ');
+ print('level ',ss:1,':(h=',h:1,',v=',v:1,
+ ',w=',w:1,',x=',x:1,',y=',y:1,',z=',z:1,
+ ',hh=',hh:1,',vv=',vv:1,')');
+ end;
+goto done
+
+@ @<Finish a command that changes the current font...@>=
+font_num[nf]:=p; cur_font:=0;
+while font_num[cur_font]<>p do incr(cur_font);
+if cur_font=nf then
+ begin cur_font:=invalid_font;
+ error('invalid font selection: font ',p:1,' was never defined!');
+ end;
+if showing then if out_mode>mnemonics_only then
+ begin print(' current font is '); print_font(cur_font);
+ end;
+goto done
+
+@* Skipping pages.
+A routine that's much simpler than |do_page| is used to pass over
+pages that are not being translated. The |skip_pages| subroutine
+is assumed to begin just after the preamble has been read, or just
+after a |bop| has been processed. It continues until either finding a
+|bop| that matches the desired starting page specifications, or until
+running into the postamble.
+
+@p @t\4@>@<Declare the procedure called |scan_bop|@>@;
+procedure skip_pages(@!bop_seen:boolean);
+label 9999; {end of this subroutine}
+var p:integer; {a parameter}
+@!k:0..255; {command code}
+@!down_the_drain:integer; {garbage}
+begin showing:=false;
+while true do
+ begin if not bop_seen then
+ begin scan_bop;
+ if in_postamble then goto 9999;
+ if not started then if start_match then
+ begin started:=true; goto 9999;
+ end;
+ end;
+ @<Skip until finding |eop|@>;
+ bop_seen:=false;
+ end;
+9999:end;
+
+@ @<Skip until finding |eop|@>=
+repeat if eof(dvi_file) then bad_dvi('the file ended prematurely');
+@.the file ended prematurely@>
+ k:=get_byte;
+ p:=first_par(k);
+ case k of
+ set_rule,put_rule: down_the_drain:=signed_quad;
+ four_cases(fnt_def1): begin define_font(p);
+ print_ln(' ');
+ end;
+ four_cases(xxx1): while p>0 do
+ begin down_the_drain:=get_byte; decr(p);
+ end;
+ bop,pre,post,post_post,undefined_commands:
+ bad_dvi('illegal command at byte ',cur_loc-1:1);
+@.illegal command at byte n@>
+ othercases do_nothing
+ endcases;
+until k=eop;
+
+@ Global variables called |old_backpointer| and |new_backpointer|
+are used to check whether the back pointers are properly set up.
+Another one tells whether we have already found the starting page.
+
+@<Glob...@>=
+@!old_backpointer:integer; {the previous |bop| command location}
+@!new_backpointer:integer; {the current |bop| command location}
+@!started:boolean; {has the starting page been found?}
+
+@ @<Set init...@>=
+old_backpointer:=-1; started:=false;
+
+@ The |scan_bop| procedure reads \.{DVI} commands following the preamble
+or following |eop|, until finding either |bop| or the postamble.
+
+@<Declare the procedure called |scan_bop|@>=
+procedure scan_bop;
+var k:0..255; {command code}
+begin repeat if eof(dvi_file) then bad_dvi('the file ended prematurely');
+@.the file ended prematurely@>
+ k:=get_byte;
+ if (k>=fnt_def1)and(k<fnt_def1+4) then
+ begin define_font(first_par(k)); k:=nop;
+ end;
+until k<>nop;
+if k=post then in_postamble:=true
+else begin if k<>bop then bad_dvi('byte ',cur_loc-1:1,' is not bop');
+@.byte n is not bop@>
+ new_backpointer:=cur_loc-1; incr(page_count);
+ for k:=0 to 9 do count[k]:=signed_quad;
+ if signed_quad<>old_backpointer
+ then print_ln('backpointer in byte ',cur_loc-4:1,
+ ' should be ',old_backpointer:1,'!');
+@.backpointer...should be p@>
+ old_backpointer:=new_backpointer;
+ end;
+end;
+
+@* Using the backpointers.
+The routines in this section of the program are brought into play only
+if |random_reading| is |true| (and only if |out_mode=the_works|).
+First comes a routine that illustrates how to find the postamble quickly.
+
+@<Find the postamble, working back from the end@>=
+n:=dvi_length;
+if n<53 then bad_dvi('only ',n:1,' bytes long');
+@.only n bytes long@>
+m:=n-4;
+repeat if m=0 then bad_dvi('all 223s');
+@.all 223s@>
+move_to_byte(m); k:=get_byte; decr(m);
+until k<>223;
+if k<>id_byte then bad_dvi('ID byte is ',k:1);
+@.ID byte is wrong@>
+move_to_byte(m-3); q:=signed_quad;
+if (q<0)or(q>m-33) then bad_dvi('post pointer ',q:1,' at byte ',m-3:1);
+@.post pointer is wrong@>
+move_to_byte(q); k:=get_byte;
+if k<>post then bad_dvi('byte ',q:1,' is not post');
+@.byte n is not post@>
+post_loc:=q; first_backpointer:=signed_quad
+
+@ Note that the last steps of the above code save the locations of the
+the |post| byte and the final |bop|. We had better declare these global
+variables, together with two more that we will need shortly.
+
+@<Glob...@>=
+@!post_loc:integer; {byte location where the postamble begins}
+@!first_backpointer:integer; {the pointer following |post|}
+@!start_loc:integer; {byte location of the first page to process}
+@!after_pre:integer; {byte location immediately following the preamble}
+
+@ The next little routine shows how the backpointers can be followed
+to move through a \.{DVI} file in reverse order. Ordinarily a \.{DVI}-reading
+program would do this only if it wants to print the pages backwards or
+if it wants to find a specified starting page that is not necessarily the
+first page in the file; otherwise it would of course be simpler and faster
+just to read the whole file from the beginning.
+
+@<Count the pages and move to the starting page@>=
+q:=post_loc; p:=first_backpointer; start_loc:=-1;
+if p<0 then in_postamble:=true
+else begin repeat
+ {now |q| points to a |post| or |bop| command; |p>=0| is prev pointer}
+ if p>q-46 then
+ bad_dvi('page link ',p:1,' after byte ',q:1);
+@.page link wrong...@>
+ q:=p; move_to_byte(q); k:=get_byte;
+ if k=bop then incr(page_count)
+ else bad_dvi('byte ',q:1,' is not bop');
+@.byte n is not bop@>
+ for k:=0 to 9 do count[k]:=signed_quad;
+ p:=signed_quad;
+ if start_match then
+ begin start_loc:=q; old_backpointer:=p;
+ end;
+ until p<0;
+ if start_loc<0 then abort('starting page number could not be found!');
+@.starting page number...@>
+ if old_backpointer<0 then start_loc:=after_pre; {we want to check everything}
+ move_to_byte(start_loc);
+ end;
+if page_count<>total_pages then
+ print_ln('there are really ',page_count:1,' pages, not ',total_pages:1,'!')
+@.there are really n pages@>
+
+@* Reading the postamble.
+Now imagine that we are reading the \.{DVI} file and positioned just
+four bytes after the |post| command. That, in fact, is the situation,
+when the following part of \.{DVItype} is called upon to read, translate,
+and check the rest of the postamble.
+
+@p procedure read_postamble;
+var k:integer; {loop index}
+@!p,@!q,@!m:integer; {general purpose registers}
+begin showing:=false; post_loc:=cur_loc-5;
+print_ln('Postamble starts at byte ',post_loc:1,'.');
+@.Postamble starts at byte n@>
+if signed_quad<>numerator then
+ print_ln('numerator doesn''t match the preamble!');
+@.numerator doesn't match@>
+if signed_quad<>denominator then
+ print_ln('denominator doesn''t match the preamble!');
+@.denominator doesn't match@>
+if signed_quad<>mag then if new_mag=0 then
+ print_ln('magnification doesn''t match the preamble!');
+@.magnification doesn't match@>
+max_v:=signed_quad; max_h:=signed_quad;@/
+print('maxv=',max_v:1,', maxh=',max_h:1);@/
+max_s:=get_two_bytes; total_pages:=get_two_bytes;@/
+print_ln(', maxstackdepth=',max_s:1,', totalpages=',total_pages:1);
+if out_mode<the_works then
+ @<Compare the \\{lust} parameters with the accumulated facts@>;
+@<Process the font definitions of the postamble@>;
+@<Make sure that the end of the file is well-formed@>;
+end;
+
+@ No warning is given when |max_h_so_far| exceeds |max_h| by less than~100,
+since 100 units is invisibly small; it's approximately the wavelength of
+visible light, in the case of \TeX\ output. Rounding errors can be expected
+to make |h| and |v| slightly more than |max_h| and |max_v|, every once in
+a~while; hence small discrepancies are not cause for alarm.
+
+@<Compare the \\{lust}...@>=
+begin if max_v+99<max_v_so_far then
+ print_ln('warning: observed maxv was ',max_v_so_far:1);
+@.warning: observed maxv...@>
+@.observed maxv was x@>
+if max_h+99<max_h_so_far then
+ print_ln('warning: observed maxh was ',max_h_so_far:1);
+@.warning: observed maxh...@>
+@.observed maxh was x@>
+if max_s<max_s_so_far then
+ print_ln('warning: observed maxstackdepth was ',max_s_so_far:1);
+@.warning: observed maxstack...@>
+@.observed maxstackdepth was x@>
+if page_count<>total_pages then
+ print_ln('there are really ',page_count:1,' pages, not ',total_pages:1,'!');
+end
+@.there are really n pages@>
+
+@ When we get to the present code, the |post_post| command has
+just been read.
+
+@<Make sure that the end of the file is well-formed@>=
+q:=signed_quad;
+if q<>post_loc then
+ print_ln('bad postamble pointer in byte ',cur_loc-4:1,'!');
+@.bad postamble pointer@>
+m:=get_byte;
+if m<>id_byte then print_ln('identification in byte ',cur_loc-1:1,
+@.identification...should be n@>
+ ' should be ',id_byte:1,'!');
+k:=cur_loc; m:=223;
+while (m=223)and not eof(dvi_file) do m:=get_byte;
+if not eof(dvi_file) then bad_dvi('signature in byte ',cur_loc-1:1,
+@.signature...should be...@>
+ ' should be 223')
+else if cur_loc<k+4 then
+ print_ln('not enough signature bytes at end of file (',
+@.not enough signature bytes...@>
+ cur_loc-k:1,')');
+
+@ @<Process the font definitions...@>=
+repeat k:=get_byte;
+if (k>=fnt_def1)and(k<fnt_def1+4) then
+ begin p:=first_par(k); define_font(p); print_ln(' '); k:=nop;
+ end;
+until k<>nop;
+if k<>post_post then
+ print_ln('byte ',cur_loc-1:1,' is not postpost!')
+@.byte n is not postpost@>
+
+@* The main program.
+Now we are ready to put it all together. This is where \.{DVItype} starts,
+and where it ends.
+
+@p begin initialize; {get all variables initialized}
+dialog; {set up all the options}
+@<Process the preamble@>;
+if out_mode=the_works then {|random_reading=true|}
+ begin @<Find the postamble, working back from the end@>;
+ in_postamble:=true; read_postamble; in_postamble:=false;
+ @<Count the pages and move to the starting page@>;
+ end;
+skip_pages(false);
+if not in_postamble then @<Translate up to |max_pages| pages@>;
+if out_mode<the_works then
+ begin if not in_postamble then skip_pages(true);
+ if signed_quad<>old_backpointer then
+ print_ln('backpointer in byte ',cur_loc-4:1,
+ ' should be ',old_backpointer:1,'!');
+@.backpointer...should be p@>
+ read_postamble;
+ end;
+final_end:end.
+
+@ The main program needs a few global variables in order to do its work.
+
+@<Glob...@>=
+@!k,@!m,@!n,@!p,@!q:integer; {general purpose registers}
+
+@ A \.{DVI}-reading program that reads the postamble first need not look at the
+preamble; but \.{DVItype} looks at the preamble in order to do error
+checking, and to display the introductory comment.
+
+@<Process the preamble@>=
+open_dvi_file;
+p:=get_byte; {fetch the first byte}
+if p<>pre then bad_dvi('First byte isn''t start of preamble!');
+@.First byte isn't...@>
+p:=get_byte; {fetch the identification byte}
+if p<>id_byte then
+ print_ln('identification in byte 1 should be ',id_byte:1,'!');
+@.identification...should be n@>
+@<Compute the conversion factors@>;
+p:=get_byte; {fetch the length of the introductory comment}
+print('''');
+while p>0 do
+ begin decr(p); print(xchr[get_byte]);
+ end;
+print_ln('''');
+after_pre:=cur_loc
+
+@ The conversion factor |conv| is figured as follows: There are exactly
+|n/d| decimicrons per \.{DVI} unit, and 254000 decimicrons per inch,
+and |resolution| pixels per inch. Then we have to adjust this
+by the stated amount of magnification.
+
+@<Compute the conversion factors@>=
+numerator:=signed_quad; denominator:=signed_quad;
+if numerator<=0 then bad_dvi('numerator is ',numerator:1);
+@.numerator is wrong@>
+if denominator<=0 then bad_dvi('denominator is ',denominator:1);
+@.denominator is wrong@>
+print_ln('numerator/denominator=',numerator:1,'/',denominator:1);
+tfm_conv:=(25400000.0/numerator)*(denominator/473628672)/16.0;
+conv:=(numerator/254000.0)*(resolution/denominator);
+mag:=signed_quad;
+if new_mag>0 then mag:=new_mag
+else if mag<=0 then bad_dvi('magnification is ',mag:1);
+@.magnification is wrong@>
+true_conv:=conv; conv:=true_conv*(mag/1000.0);
+print_ln('magnification=',mag:1,'; ',conv:16:8,' pixels per DVI unit')
+
+@ The code shown here uses a convention that has proved to be useful:
+If the starting page was specified as, e.g., `\.{1.*.-5}', then
+all page numbers in the file are displayed by showing the values of
+counts 0, 1, and~2, separated by dots. Such numbers can, for example,
+be displayed on the console of a printer when it is working on that
+page.
+
+@<Translate up to...@>=
+begin while max_pages>0 do
+ begin decr(max_pages);
+ print_ln(' '); print(cur_loc-45:1,': beginning of page ');
+ for k:=0 to start_vals do
+ begin print(count[k]:1);
+ if k<start_vals then print('.')
+ else print_ln(' ');
+ end;
+ if not do_page then bad_dvi('page ended unexpectedly');
+@.page ended unexpectedly@>
+ scan_bop;
+ if in_postamble then goto done;
+ end;
+done:end
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{DVItype} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/systems/knuth/dist/texware/pltotf.web b/systems/knuth/dist/texware/pltotf.web
new file mode 100644
index 0000000000..d68e1e1b23
--- /dev/null
+++ b/systems/knuth/dist/texware/pltotf.web
@@ -0,0 +1,2518 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Version 0 was implemented in January 1982.
+% In February 1982 a new restriction on ligature steps was added.
+% In June 1982 the routines were divided into smaller pieces for IBM people.
+% Hex was added in September 1982, and the result became "Version 1".
+% Version 1.1 fixed a bug in section 28 (since eoln is undefined after eof).
+% Slight changes were made in October, 1982, for version 0.6 of TeX.
+% Version 1.2 fixed a bug in section 115 (TOP, MID, and BOT can be zero)
+% Version 1.3 (April 1983) blanked out unused BCPL header bytes
+% Version 2 (July 1983) was released with TeX version 0.999.
+% Version 2.1 (September 1983) changed TEXINFO to FONTDIMEN.
+% Version 2.2 (May 1985) added checksum computation to match METAFONT.
+% Version 2.3 (August 1985) introduced `backup' to fix a minor bug.
+% Version 3 (October 1989) introduced extended ligature features.
+% Version 3.1 (November 1989) fixed two bugs (notably min_nl:=0).
+% Version 3.2 (December 1989) improved `shorten', increased max_letters.
+% Version 3.3 (September 1990) fixed `nonexistent char 0' (John Gourlay).
+% Version 3.4 (March 1991) has more robust `out_scaled' (Wayne Sullivan).
+% Version 3.5 (March 1995) initialized lk_step_ended (Armin K\"ollner).
+% Version 3.6 (January 2014) corrected possible end-of-line glitch (Ken Nakano),
+% and get_fix now treats -- as + (Peter Breitenlohner).
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+\font\logo=logo10 % for the METAFONT logo
+\def\MF{{\logo METAFONT}}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+
+\def\title{PL\lowercase{to}TF}
+\def\contentspagenumber{301}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont PLtoTF} processor}
+ \vskip 15pt
+ \centerline{(Version 3.6, January 2014)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926 and MCS-8300984,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+The \.{PLtoTF} utility program converts property-list (``\.{PL}'')
+files into equivalent \TeX\ font metric (``\.{TFM}'') files. It also
+makes a thorough check of the given \.{PL} file, so that the \.{TFM}
+file should be acceptable to \TeX.
+
+The first \.{PLtoTF} program was designed by Leo Guibas in the summer of
+1978. Contributions by Frank Liang, Doug Wyatt, and Lyle Ramshaw
+also had a significant effect on the evolution of the present code.
+
+Extensions for an enhanced ligature mechanism were added by the author in 1989.
+
+The |banner| string defined here should be changed whenever \.{PLtoTF}
+gets modified.
+
+@d banner=='This is PLtoTF, Version 3.6' {printed when the program starts}
+
+@ This program is written entirely in standard \PASCAL, except that
+it has to do some slightly system-dependent character code conversion
+on input. Furthermore, lower case letters are used in error messages;
+they could be converted to upper case if necessary. The input is read
+from |pl_file|, and the output is written on |tfm_file|; error messages and
+other remarks are written on the |output| file, which the user may
+choose to assign to the terminal if the system permits it.
+@^system dependencies@>
+
+The term |print| is used instead of |write| when this program writes on
+the |output| file, so that all such output can be easily deflected.
+
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+
+@p program PLtoTF(@!pl_file,@!tfm_file,@!output);
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var @<Local variables for initialization@>@/
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ The following parameters can be changed at compile time to extend or
+reduce \.{PLtoTF}'s capacity.
+
+@<Constants...@>=
+@!buf_size=60; {length of lines displayed in error messages}
+@!max_header_bytes=100; {four times the maximum number of words allowed in
+ the \.{TFM} file header block, must be 1024 or less}
+@!max_param_words=30; {the maximum number of \.{fontdimen} parameters allowed}
+@!max_lig_steps=5000;
+ {maximum length of ligature program, must be at most $32767-257=32510$}
+@!max_kerns=500; {the maximum number of distinct kern values}
+@!hash_size=5003; {preferably a prime number, a bit larger than the number
+ of character pairs in lig/kern steps}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d do_nothing == {empty statement}
+
+@* Property list description of font metric data.
+The idea behind \.{PL} files is that precise details about fonts, i.e., the
+facts that are needed by typesetting routines like \TeX, sometimes have to
+be supplied by hand. The nested property-list format provides a reasonably
+convenient way to do this.
+
+A good deal of computation is necessary to parse and process a
+\.{PL} file, so it would be inappropriate for \TeX\ itself to do this
+every time it loads a font. \TeX\ deals only with the compact descriptions
+of font metric data that appear in \.{TFM} files. Such data is so compact,
+however, it is almost impossible for anybody but a computer to read it.
+The purpose of \.{PLtoTF} is to convert from a human-oriented file of text
+to a computer-oriented file of binary numbers.
+
+@<Glob...@>=
+@!pl_file:text;
+
+@ @<Set init...@>=
+reset(pl_file);
+
+@ A \.{PL} file is a list of entries of the form
+$$\.{(PROPERTYNAME VALUE)}$$
+where the property name is one of a finite set of names understood by
+this program, and the value may itself in turn be a property list.
+The idea is best understood by looking at an example, so let's consider
+a fragment of the \.{PL} file for a hypothetical font.
+$$\vbox{\halign{\.{#}\hfil\cr
+(FAMILY NOVA)\cr
+(FACE F MIE)\cr
+(CODINGSCHEME ASCII)\cr
+(DESIGNSIZE D 10)\cr
+(DESIGNUNITS D 18)\cr
+(COMMENT A COMMENT IS IGNORED)\cr
+(COMMENT (EXCEPT THIS ONE ISN'T))\cr
+(COMMENT (ACTUALLY IT IS, EVEN THOUGH\cr
+\qquad\qquad IT SAYS IT ISN'T))\cr
+(FONTDIMEN\cr
+\qquad (SLANT R -.25)\cr
+\qquad (SPACE D 6)\cr
+\qquad (SHRINK D 2)\cr
+\qquad (STRETCH D 3)\cr
+\qquad (XHEIGHT R 10.55)\cr
+\qquad (QUAD D 18)\cr
+\qquad )\cr
+(LIGTABLE\cr
+\qquad (LABEL C f)\cr
+\qquad (LIG C f O 200)\cr
+\qquad (SKIP D 1)\cr
+\qquad (LABEL O 200)\cr
+\qquad (LIG C i O 201)\cr
+\qquad (KRN O 51 R 1.5)\cr
+\qquad (/LIG C ? C f)\cr
+\qquad (STOP)\cr
+\qquad )\cr
+(CHARACTER C f\cr
+\qquad (CHARWD D 6)\cr
+\qquad (CHARHT R 13.5)\cr
+\qquad (CHARIC R 1.5)\cr
+\qquad )\cr}}$$
+This example says that the font whose metric information is being described
+belongs to the hypothetical
+\.{NOVA} family; its face code is medium italic extended;
+and the characters appear in ASCII code positions. The design size is 10 points,
+and all other sizes in this \.{PL} file are given in units such that 18 units
+equals the design size. The font is slanted with a slope of $-.25$ (hence the
+letters actually slant backward---perhaps that is why the family name is
+\.{NOVA}). The normal space between words is 6 units (i.e., one third of
+the 18-unit design size), with glue that shrinks by 2 units or stretches by 3.
+The letters for which accents don't need to be raised or lowered are 10.55
+units high, and one em equals 18 units.
+
+The example ligature table is a bit trickier. It specifies that the
+letter \.f followed by another \.f is changed to code @'200, while
+code @'200 followed by \.i is changed to @'201; presumably codes @'200
+and @'201 represent the ligatures `ff' and `ffi'. Moreover, in both cases
+\.f and @'200, if the following character is the code @'51 (which is a
+right parenthesis), an additional 1.5 units of space should be inserted
+before the @'51. (The `\.{SKIP}~\.D~\.1' skips over one \.{LIG} or
+\.{KRN} command, which in this case is the second \.{LIG}; in this way
+two different ligature/kern programs can come together.)
+Finally, if either \.f or @'200 is followed by a question mark,
+the question mark is replaced by \.f and the ligature program is
+started over. (Thus, the character pair `\.{f?}' would actually become
+the ligature `ff', and `\.{ff?}' or `\.{f?f}' would become `fff'. To
+avoid this restart procedure, the \.{/LIG} command could be replaced
+by \.{/LIG>}; then `\.{f?} would become `f\kern0ptf' and `\.{f?f}'
+would become `f\kern0ptff'.)
+
+Character \.f itself is 6 units wide and 13.5 units tall, in this example.
+Its depth is zero (since \.{CHARDP} is not given), and its italic correction
+is 1.5 units.
+
+@ The example above illustrates most of the features found in \.{PL} files.
+Note that some property names, like \.{FAMILY} or \.{COMMENT}, take a
+string as their value; this string continues until the first unmatched
+right parenthesis. But most property names, like \.{DESIGNSIZE} and \.{SLANT}
+and \.{LABEL}, take a number as their value. This number can be expressed in
+a variety of ways, indicated by a prefixed code; \.D stands for decimal,
+\.H for hexadecimal, \.O for octal, \.R for real, \.C for character, and
+\.F for ``face.'' Other property names, like \.{LIG}, take two numbers as
+their value. And still other names, like \.{FONTDIMEN} and \.{LIGTABLE} and
+\.{CHARACTER}, have more complicated values that involve property lists.
+
+A property name is supposed to be used only in an appropriate property
+list. For example, \.{CHARWD} shouldn't occur on the outer level or
+within \.{FONTDIMEN}.
+
+The individual property-and-value pairs in a property list can appear in
+any order. For instance, `\.{SHRINK}' precedes `\.{STRETCH}' in the above
+example, although the \.{TFM} file always puts the stretch parameter first.
+One could even give the information about characters like `\.f' before
+specifying the number of units in the design size, or before specifying the
+ligature and kerning table. However, the \.{LIGTABLE} itself is an exception
+to this rule; the individual elements of the \.{LIGTABLE} property list
+can be reordered only to a certain extent without changing the meaning
+of that table.
+
+If property-and-value pairs are omitted, a default value is used. For example,
+we have already noted that the default for \.{CHARDP} is zero. The default
+for {\sl every\/} numeric value is, in fact, zero, unless otherwise stated
+below.
+
+If the same property name is used more than once, \.{PLtoTF} will not notice
+the discrepancy; it simply uses the final value given. Once again, however, the
+\.{LIGTABLE} is an exception to this rule; \.{PLtoTF} will complain if there
+is more than one label for some character. And of course many of the
+entries in the \.{LIGTABLE} property list have the same property name.
+
+From these rules, you can guess (correctly) that \.{PLtoTF} operates in four
+main steps. First it assigns the default values to all properties; then it scans
+through the \.{PL} file, changing property values as new ones are seen; then
+it checks the information and corrects any problems; and finally it outputs
+the \.{TFM} file.
+
+@ Instead of relying on a hypothetical example, let's consider a complete
+grammar for \.{PL} files. At the outer level, the following property names
+are valid:
+
+\yskip\hang\.{CHECKSUM} (four-byte value). The value, which should be a
+nonnegative integer less than $2^{32}$, is used to identify a particular
+version of a font; it should match the check sum value stored with the font
+itself. An explicit check sum of zero is used to bypass
+check sum testing. If no checksum is specified in the \.{PL} file,
+\.{PLtoTF} will compute the checksum that \MF\ would compute from the
+same data.
+
+\yskip\hang\.{DESIGNSIZE} (numeric value, default is 10). The value, which
+should be a real number in the range |1.0<=x<2048|, represents the default
+amount by which all quantities will be scaled if the font is not loaded
+with an `\.{at}' specification. For example, if one says
+`\.{\\font\\A=cmr10 at 15pt}' in \TeX\ language, the design size in the \.{TFM}
+file is ignored and effectively replaced by 15 points; but if one simply
+says `\.{\\font\\A=cmr10}' the stated design size is used. This quantity is
+always in units of printer's points.
+
+\yskip\hang\.{DESIGNUNITS} (numeric value, default is 1). The value
+should be a positive real number; it says how many units equals the design
+size (or the eventual `\.{at}' size, if the font is being scaled). For
+example, suppose you have a font that has been digitized with 600 pixels per
+em, and the design size is one em; then you could say `\.{(DESIGNUNITS R 600)}'
+if you wanted to give all of your measurements in units of pixels.
+
+\yskip\hang\.{CODINGSCHEME} (string value, default is `\.{UNSPECIFIED}').
+The string should not contain parentheses, and its length must be less than 40.
+It identifies the correspondence between the numeric codes and font characters.
+(\TeX\ ignores this information, but other software programs make use of it.)
+
+\yskip\hang\.{FAMILY} (string value, default is `\.{UNSPECIFIED}').
+The string should not contain parentheses, and its length must be less than 20.
+It identifies the name of the family to which this font belongs, e.g.,
+`\.{HELVETICA}'. (\TeX\ ignores this information; but it is needed, for
+example, when converting \.{DVI} files to \.{PRESS} files for Xerox
+equipment.)
+
+\yskip\hang\.{FACE} (one-byte value). This number, which must lie between
+0 and 255 inclusive, is a subsidiary ident\-ifi\-ca\-tion of the font within its
+family. For example, bold italic condensed fonts might have the same family name
+as light roman extended fonts, differing only in their face byte. (\TeX\
+ignores this information; but it is needed, for example, when converting
+\.{DVI} files to \.{PRESS} files for Xerox equipment.)
+
+\yskip\hang\.{SEVENBITSAFEFLAG} (string value, default is `\.{FALSE}'). The
+value should start with either `\.T' (true) or `\.F' (false). If true, character
+codes less than 128 cannot lead to codes of 128 or more via ligatures or
+charlists or extensible characters. (\TeX82 ignores this flag, but older
+versions of \TeX\ would only accept \.{TFM} files that were seven-bit safe.)
+\.{PLtoTF} computes the correct value of this flag and gives an error message
+only if a claimed ``true'' value is incorrect.
+
+\yskip\hang\.{HEADER} (a one-byte value followed by a four-byte value).
+The one-byte value should be between 18 and a maximum limit that can be
+raised or lowered depending on the compile-time setting of |max_header_bytes|.
+The four-byte value goes into the header word whose index is the one-byte
+value; for example, to set |header[18]:=1|, one may write
+`\.{(HEADER D 18 O 1)}'. This notation is used for header information that
+is presently unnamed. (\TeX\ ignores it.)
+
+\yskip\hang\.{FONTDIMEN} (property list value). See below for the names
+allowed in this property list.
+
+\yskip\hang\.{LIGTABLE} (property list value). See below for the rules
+about this special kind of property list.
+
+\yskip\hang\.{BOUNDARYCHAR} (one-byte value). If this character appears in
+a \.{LIGTABLE} command, it matches ``end of word'' as well as itself.
+If no boundary character is given and no \.{LABEL} \.{BOUNDARYCHAR} occurs
+within \.{LIGTABLE}, word boundaries will not affect ligatures or kerning.
+
+\yskip\hang\.{CHARACTER}. The value is a one-byte integer followed by
+a property list. The integer represents the number of a character that is
+present in the font; the property list of a character is defined below.
+The default is an empty property list.
+
+@ Numeric property list values can be given in various forms identified by
+a prefixed letter.
+
+\yskip\hang\.C denotes an ASCII character, which should be a standard visible
+character that is not a parenthesis. The numeric value will therefore be
+between @'41 and @'176 but not @'50 or @'51.
+
+\yskip\hang\.D denotes a decimal integer, which must be nonnegative and
+less than 256. (Use \.R for larger values or for negative values.)
+
+\yskip\hang\.F denotes a three-letter Xerox face code; the admissible codes
+are \.{MRR}, \.{MIR}, \.{BRR}, \.{BIR}, \.{LRR}, \.{LIR}, \.{MRC}, \.{MIC},
+\.{BRC}, \.{BIC}, \.{LRC}, \.{LIC}, \.{MRE}, \.{MIE}, \.{BRE}, \.{BIE},
+\.{LRE}, and \.{LIE}, denoting the integers 0 to 17, respectively.
+
+\yskip\hang\.O denotes an unsigned octal integer, which must be less than
+$2^{32}$, i.e., at most `\.{O 37777777777}'.
+
+\yskip\hang\.H denotes an unsigned hexadecimal integer, which must be less than
+$2^{32}$, i.e., at most `\.{H FFFFFFFF}'.
+
+\yskip\hang\.R denotes a real number in decimal notation, optionally preceded
+by a `\.+' or `\.-' sign, and optionally including a decimal point. The
+absolute value must be less than 2048.
+
+@ The property names allowed in a \.{FONTDIMEN} property list correspond to
+various \TeX\ parameters, each of which has a (real) numeric value. All
+of the parameters except \.{SLANT} are in design units. The admissible
+names are \.{SLANT}, \.{SPACE}, \.{STRETCH}, \.{SHRINK}, \.{XHEIGHT},
+\.{QUAD}, \.{EXTRASPACE}, \.{NUM1}, \.{NUM2}, \.{NUM3}, \.{DENOM1},
+\.{DENOM2}, \.{SUP1}, \.{SUP2}, \.{SUP3}, \.{SUB1}, \.{SUB2}, \.{SUPDROP},
+\.{SUBDROP}, \.{DELIM1}, \.{DELIM2}, and \.{AXISHEIGHT}, for parameters
+1~to~22. The alternate names \.{DEFAULTRULETHICKNESS},
+\.{BIGOPSPACING1}, \.{BIGOPSPACING2}, \.{BIGOPSPACING3},
+\.{BIGOPSPACING4}, and \.{BIGOPSPACING5}, may also be used for parameters
+8 to 13.
+
+The notation `\.{PARAMETER} $n$' provides another way to specify the
+$n$th parameter; for example, `\.{(PARAMETER} \.{D 1 R -.25)}' is another way
+to specify that the \.{SLANT} is $-0.25$. The value of $n$ must be positive
+and less than |max_param_words|.
+
+@ The elements of a \.{CHARACTER} property list can be of six different types.
+
+\yskip\hang\.{CHARWD} (real value) denotes the character's width in
+design units.
+
+\yskip\hang\.{CHARHT} (real value) denotes the character's height in
+design units.
+
+\yskip\hang\.{CHARDP} (real value) denotes the character's depth in
+design units.
+
+\yskip\hang\.{CHARIC} (real value) denotes the character's italic correction in
+design units.
+
+\yskip\hang\.{NEXTLARGER} (one-byte value), specifies the character that
+follows the present one in a ``charlist.'' The value must be the number of a
+character in the font, and there must be no infinite cycles of supposedly
+larger and larger characters.
+
+\yskip\hang\.{VARCHAR} (property list value), specifies an extensible character.
+This option and \.{NEXTLARGER} are mutually exclusive; i.e., they cannot
+both be used within the same \.{CHARACTER} list.
+
+\yskip\noindent
+The elements of a \.{VARCHAR} property list are either \.{TOP}, \.{MID},
+\.{BOT} or \.{REP}; the values are integers, which must be zero or the number
+of a character in the font. A zero value for \.{TOP}, \.{MID}, or \.{BOT} means
+that the corresponding piece of the extensible character is absent. A nonzero
+value, or a \.{REP} value of zero, denotes the character code used to make
+up the top, middle, bottom, or replicated piece of an extensible character.
+
+@ A \.{LIGTABLE} property list contains elements of four kinds, specifying a
+program in a simple command language that \TeX\ uses for ligatures and kerns.
+If several \.{LIGTABLE} lists appear, they are effectively concatenated into
+a single list.
+
+\yskip\hang\.{LABEL} (one-byte value) means that the program for the
+stated character value starts here. The integer must be the number of a
+character in the font; its \.{CHARACTER} property list must not have a
+\.{NEXTLARGER} or \.{VARCHAR} field. At least one \.{LIG} or \.{KRN} step
+must follow.
+
+\yskip\hang\.{LABEL} \.{BOUNDARYCHAR} means that the program for
+beginning-of-word ligatures starts here.
+
+\yskip\hang\.{LIG} (two one-byte values). The instruction `\.{(LIG} $c$ $r$\.)'
+means, ``If the next character is $c$, then insert character~$r$ and
+possibly delete the current character and/or~$c$;
+otherwise go on to the next instruction.''
+Characters $r$ and $c$ must be present in the font. \.{LIG} may be immediately
+preceded or followed by a slash, and then immediately followed by \.>
+characters not exceeding the number of slashes. Thus there are eight
+possible forms:
+$$\hbox to .8\hsize{\.{LIG}\hfil\.{/LIG}\hfil\.{/LIG>}\hfil
+\.{LIG/}\hfil\.{LIG/>}\hfil\.{/LIG/}\hfil\.{/LIG/>}\hfil\.{/LIG/>>}}$$
+The slashes specify retention of the left or right original character; the
+\.> signs specify passing over the result without further ligature processing.
+
+\yskip\hang\.{KRN} (a one-byte value and a real value). The instruction
+`\.{(KRN} $c$ $r$\.)' means, ``If the next character is $c$, then insert
+a blank space of width $r$ between the current character character and $c$;
+otherwise go on to the next intruction.'' The value of $r$, which is in
+design units, is often negative. Character code $c$ must exist
+in the font.
+
+\yskip\hang\.{STOP} (no value). This instruction ends a ligature/kern program.
+It must follow either a \.{LIG} or \.{KRN} instruction, not a \.{LABEL}
+or \.{STOP} or \.{SKIP}.
+
+\yskip\hang\.{SKIP} (value in the range |0..127|). This instruction specifies
+continuation of a ligature/kern program after the specified number of \.{LIG}
+or \.{KRN} steps has been skipped over. The number of subsequent \.{LIG} and
+\.{KRN} instructions must therefore exceed this specified amount.
+
+@ In addition to all these possibilities, the property name \.{COMMENT} is
+allowed in any property list. Such comments are ignored.
+
+@ So that is what \.{PL} files hold. The next question is, ``What about
+\.{TFM} files?'' A complete answer to that question appears in the
+documentation of the companion program, \.{TFtoPL}, so it will not
+be repeated here. Suffice it to say that a \.{TFM} file stores all of the
+relevant font information in a sequence of 8-bit bytes. The number of
+bytes is always a multiple of 4, so we could regard the \.{TFM} file
+as a sequence of 32-bit words; but \TeX\ uses the byte interpretation,
+and so does \.{PLtoTF}. Note that the bytes are considered to be unsigned
+numbers.
+
+@<Glob...@>=
+@!tfm_file:packed file of 0..255;
+
+@ On some systems you may have to do something special to write a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(tfm_file);
+
+@* Basic input routines.
+For the purposes of this program, a |byte| is an unsigned eight-bit quantity,
+and an |ASCII_code| is an integer between @'40 and @'177. Such ASCII codes
+correspond to one-character constants like \.{"A"} in \.{WEB} language.
+
+@<Types...@>=
+@!byte=0..255; {unsigned eight-bit quantity}
+@!ASCII_code=@'40..@'177; {standard ASCII code numbers}
+
+@ One of the things \.{PLtoTF} has to do is convert characters of strings
+to ASCII form, since that is the code used for the family name and the
+coding scheme in a \.{TFM} file. An array |xord| is used to do the
+conversion from |char|; the method below should work with little or no change
+on most \PASCAL\ systems.
+@^system dependencies@>
+
+@d first_ord=0 {ordinal number of the smallest element of |char|}
+@d last_ord=127 {ordinal number of the largest element of |char|}
+
+@<Global...@>=
+@!xord:array[char] of ASCII_code; {conversion table}
+
+@ @<Local variables for init...@>=
+@!k:integer; {all-purpose initialization index}
+
+@ Characters that should not appear in \.{PL} files (except in comments)
+are mapped into @'177.
+
+@d invalid_code=@'177 {code deserving an error message}
+
+@<Set init...@>=
+for k:=first_ord to last_ord do xord[chr(k)]:=invalid_code;
+xord[' ']:=" "; xord['!']:="!"; xord['"']:=""""; xord['#']:="#";
+xord['$']:="$"; xord['%']:="%"; xord['&']:="&"; xord['''']:="'";
+xord['(']:="("; xord[')']:=")"; xord['*']:="*"; xord['+']:="+"; xord[',']:=",";
+xord['-']:="-"; xord['.']:="."; xord['/']:="/"; xord['0']:="0"; xord['1']:="1";
+xord['2']:="2"; xord['3']:="3"; xord['4']:="4"; xord['5']:="5"; xord['6']:="6";
+xord['7']:="7"; xord['8']:="8"; xord['9']:="9"; xord[':']:=":"; xord[';']:=";";
+xord['<']:="<"; xord['=']:="="; xord['>']:=">"; xord['?']:="?";
+xord['@@']:="@@"; xord['A']:="A"; xord['B']:="B"; xord['C']:="C";
+xord['D']:="D"; xord['E']:="E"; xord['F']:="F"; xord['G']:="G"; xord['H']:="H";
+xord['I']:="I"; xord['J']:="J"; xord['K']:="K"; xord['L']:="L"; xord['M']:="M";
+xord['N']:="N"; xord['O']:="O"; xord['P']:="P"; xord['Q']:="Q"; xord['R']:="R";
+xord['S']:="S"; xord['T']:="T"; xord['U']:="U"; xord['V']:="V"; xord['W']:="W";
+xord['X']:="X"; xord['Y']:="Y"; xord['Z']:="Z"; xord['[']:="["; xord['\']:="\";
+xord[']']:="]"; xord['^']:="^"; xord['_']:="_"; xord['`']:="`"; xord['a']:="a";
+xord['b']:="b"; xord['c']:="c"; xord['d']:="d"; xord['e']:="e"; xord['f']:="f";
+xord['g']:="g"; xord['h']:="h"; xord['i']:="i"; xord['j']:="j"; xord['k']:="k";
+xord['l']:="l"; xord['m']:="m"; xord['n']:="n"; xord['o']:="o"; xord['p']:="p";
+xord['q']:="q"; xord['r']:="r"; xord['s']:="s"; xord['t']:="t"; xord['u']:="u";
+xord['v']:="v"; xord['w']:="w"; xord['x']:="x"; xord['y']:="y"; xord['z']:="z";
+xord['{']:="{"; xord['|']:="|"; xord['}']:="}"; xord['~']:="~";
+
+@ In order to help catch errors of badly nested parentheses, \.{PLtoTF}
+assumes that the user will begin each line with a number of blank spaces equal
+to some constant times the number of open parentheses at the beginning of
+that line. However, the program doesn't know in advance what the constant
+is, nor does it want to print an error message on every line for a user
+who has followed no consistent pattern of indentation.
+
+Therefore the following strategy is adopted: If the user has been consistent
+with indentation for ten or more lines, an indentation error will be
+reported. The constant of indentation is reset on every line that should
+have nonzero indentation.
+
+@<Glob...@>=
+@!line:integer; {the number of the current line}
+@!good_indent:integer; {the number of lines since the last bad indentation}
+@!indent: integer; {the number of spaces per open parenthesis, zero if unknown}
+@!level: integer; {the current number of open parentheses}
+
+@ @<Set init...@>=
+line:=0; good_indent:=0; indent:=0; level:=0;
+
+@ The input need not really be broken into lines of any maximum length, and
+we could read it character by character without any buffering. But we shall
+place it into a small buffer so that offending lines can be displayed in error
+messages.
+
+@<Glob...@>=
+@!left_ln,@!right_ln:boolean; {are the left and right ends of the buffer
+ at end-of-line marks?}
+@!limit:0..buf_size; {position of the last character present in the buffer}
+@!loc:0..buf_size; {position of the last character read in the buffer}
+@!buffer:array[1..buf_size] of char;
+@!input_has_ended:boolean; {there is no more input to read}
+
+@ @<Set init...@>=
+limit:=0; loc:=0; left_ln:=true; right_ln:=true; input_has_ended:=false;
+
+@ Just before each \.{CHARACTER} property list is evaluated, the character
+code is printed in octal notation. Up to eight such codes appear on a line;
+so we have a variable to keep track of how many are currently there.
+
+@<Glob...@>=
+@!chars_on_line:0..8; {the number of characters printed on the current line}
+
+@ @<Set init...@>=
+chars_on_line:=0;
+
+@ The following routine prints an error message and an indication of
+where the error was detected. The error message should not include any
+final punctuation, since this procedure supplies its own.
+
+@d err_print(#)==begin if chars_on_line>0 then print_ln(' ');
+ print(#); show_error_context;
+ end
+
+@p procedure show_error_context; {prints the current scanner location}
+var k:0..buf_size; {an index into |buffer|}
+begin print_ln(' (line ',line:1,').');
+if not left_ln then print('...');
+for k:=1 to loc do print(buffer[k]); {print the characters already scanned}
+print_ln(' ');
+if not left_ln then print(' ');
+for k:=1 to loc do print(' '); {space out the second line}
+for k:=loc+1 to limit do print(buffer[k]); {print the characters yet unseen}
+if right_ln then print_ln(' ')@+else print_ln('...');
+chars_on_line:=0;
+end;
+
+@ Here is a procedure that does the right thing when we are done
+reading the present contents of the buffer. It keeps |buffer[buf_size]|
+empty, in order to avoid range errors on certain \PASCAL\ compilers.
+
+An infinite sequence of right parentheses is placed at the end of the
+file, so that the program is sure to get out of whatever level of nesting
+it is in.
+
+On some systems it is desirable to modify this code so that tab marks
+in the buffer are replaced by blank spaces. (Simply setting
+|xord[chr(@'11)]:=" "| would not work; for example, two-line
+error messages would not come out properly aligned.)
+@^system dependencies@>
+
+@p procedure fill_buffer;
+begin left_ln:=right_ln; limit:=0; loc:=0;
+if left_ln then
+ begin if line>0 then read_ln(pl_file);
+ incr(line);
+ end;
+if eof(pl_file) then
+ begin limit:=1; buffer[1]:=')'; right_ln:=false; input_has_ended:=true;
+ end
+else begin while (limit<buf_size-2)and(not eoln(pl_file)) do
+ begin incr(limit); read(pl_file,buffer[limit]);
+ end;
+ buffer[limit+1]:=' '; right_ln:=eoln(pl_file);
+ if right_ln then begin incr(limit); buffer[limit+1]:=' ';
+ end;
+ if left_ln then @<Set |loc| to the number of leading blanks in
+ the buffer, and check the indentation@>;
+ end;
+end;
+
+@ The interesting part about |fill_buffer| is the part that learns what
+indentation conventions the user is following, if any.
+
+@d bad_indent(#)==begin if good_indent>=10 then err_print(#);
+ good_indent:=0; indent:=0;
+ end
+
+@<Set |loc|...@>=
+begin while (loc<limit)and(buffer[loc+1]=' ') do incr(loc);
+if loc<limit then
+ begin if level=0 then
+ if loc=0 then incr(good_indent)
+ else bad_indent('Warning: Indented line occurred at level zero')
+@.Warning: Indented line...@>
+ else if indent=0 then
+ if loc mod level=0 then
+ begin indent:=loc div level; good_indent:=1;
+ end
+ else good_indent:=0
+ else if indent*level=loc then incr(good_indent)
+ else bad_indent('Warning: Inconsistent indentation; ',
+@.Warning: Inconsistent indentation...@>
+ 'you are at parenthesis level ',level:1);
+ end;
+end
+
+@* Basic scanning routines.
+The global variable |cur_char| holds the ASCII code corresponding to the
+character most recently read from the input buffer, or to a character that
+has been substituted for the real one.
+
+@<Global...@>=
+@!cur_char:ASCII_code; {we have just read this}
+
+@ Here is a procedure that sets |cur_char| to an ASCII code for the
+next character of input, if that character is a letter or digit or slash
+or \.>. Otherwise
+it sets |cur_char:=" "|, and the input system will be poised to reread the
+character that was rejected, whether or not it was a space.
+Lower case letters are converted to upper case.
+
+@p procedure get_keyword_char;
+begin while (loc=limit)and(not right_ln) do fill_buffer;
+if loc=limit then cur_char:=" " {end-of-line counts as a delimiter}
+else begin cur_char:=xord[buffer[loc+1]];
+ if cur_char>="a" then cur_char:=cur_char-@'40;
+ if ((cur_char>="0")and(cur_char<="9")) then incr(loc)
+ else if ((cur_char>="A")and(cur_char<="Z")) then incr(loc)
+ else if cur_char="/" then incr(loc)
+ else if cur_char=">" then incr(loc)
+ else cur_char:=" ";
+ end;
+end;
+
+@ The following procedure sets |cur_char| to the next character code,
+and converts lower case to upper case. If the character is a left or
+right parenthesis, it will not be ``digested''; the character will
+be read again and again, until the calling routine does something
+like `|incr(loc)|' to get past it. Such special treatment of parentheses
+insures that the structural information they contain won't be lost in
+the midst of other error recovery operations.
+
+@d backup==begin if (cur_char>")")or(cur_char<"(") then decr(loc);
+ end {undoes the effect of |get_next|}
+
+@p procedure get_next; {sets |cur_char| to next, balks at parentheses}
+begin while loc=limit do fill_buffer;
+incr(loc); cur_char:=xord[buffer[loc]];
+if cur_char>="a" then
+ if cur_char<="z" then cur_char:=cur_char-@'40 {uppercasify}
+ else begin if cur_char=invalid_code then
+ begin err_print('Illegal character in the file');
+@.Illegal character...@>
+ cur_char:="?";
+ end;
+ end
+else if (cur_char<=")")and(cur_char>="(") then decr(loc);
+end;
+
+@ The next procedure is used to ignore the text of a comment, or to pass over
+erroneous material. As such, it has the privilege of passing parentheses.
+It stops after the first right parenthesis that drops the level below
+the level in force when the procedure was called.
+
+@p procedure skip_to_end_of_item;
+var l:integer; {initial value of |level|}
+begin l:=level;
+while level>=l do
+ begin while loc=limit do fill_buffer;
+ incr(loc);
+ if buffer[loc]=')' then decr(level)
+ else if buffer[loc]='(' then incr(level);
+ end;
+if input_has_ended then err_print('File ended unexpectedly: No closing ")"');
+@.File ended unexpectedly...@>
+cur_char:=" "; {now the right parenthesis has been read and digested}
+end;
+
+@ Sometimes we merely want to skip past characters in the input until we
+reach a left or a right parenthesis. For example, we do this whenever we
+have finished scanning a property value and we hope that a right parenthesis
+is next (except for possible blank spaces).
+
+@d skip_to_paren==repeat get_next@;@+ until (cur_char="(")or(cur_char=")")
+@d skip_error(#)==begin err_print(#); skip_to_paren;
+ end {this gets to the right parenthesis if something goes wrong}
+@d flush_error(#)==begin err_print(#); skip_to_end_of_item;
+ end {this gets past the right parenthesis if something goes wrong}
+
+@ After a property value has been scanned, we want to move just past the
+right parenthesis that should come next in the input (except for possible
+blank spaces).
+
+@p procedure finish_the_property; {do this when the value has been scanned}
+begin while cur_char=" " do get_next;
+if cur_char<>")" then err_print('Junk after property value will be ignored');
+@.Junk after property value...@>
+skip_to_end_of_item;
+end;
+
+@* Scanning property names.
+We have to figure out the meaning of names that appear in the \.{PL} file,
+by looking them up in a dictionary of known keywords. Keyword number $n$
+appears in locations |start[n]| through |start[n+1]-1| of an array called
+|dictionary|.
+
+@d max_name_index=88 {upper bound on the number of keywords}
+@d max_letters=600 {upper bound on the total length of all keywords}
+
+@<Global...@>=
+@!start:array[1..max_name_index] of 0..max_letters;
+@!dictionary:array[0..max_letters] of ASCII_code;
+@!start_ptr:0..max_name_index; {the first available place in |start|}
+@!dict_ptr:0..max_letters; {the first available place in |dictionary|}
+
+@ @<Set init...@>=
+start_ptr:=1; start[1]:=0; dict_ptr:=0;
+
+@ When we are looking for a name, we put it into the |cur_name| array.
+When we have found it, the corresponding |start| index will go into
+the global variable |name_ptr|.
+
+@d longest_name=20 {length of \.{DEFAULTRULETHICKNESS}}
+
+@<Glob...@>=
+@!cur_name:array[1..longest_name] of ASCII_code; {a name to look up}
+@!name_length:0..longest_name; {its length}
+@!name_ptr:0..max_name_index; {its ordinal number in the dictionary}
+
+@ A conventional hash table with linear probing (cf.\ Algorithm 6.4L
+in {\sl The Art of Computer Pro\-gram\-ming\/}) is used for the dictionary
+operations. If |nhash[h]=0|, the table position is empty, otherwise |nhash[h]|
+points into the |start| array.
+
+@d hash_prime=101 {size of the hash table}
+
+@<Glob...@>=
+@!nhash:array[0..hash_prime-1] of 0..max_name_index;
+@!cur_hash:0..hash_prime-1; {current position in the hash table}
+
+@ @<Local...@>=
+@!h:0..hash_prime-1; {runs through the hash table}
+
+@ @<Set init...@>=
+for h:=0 to hash_prime-1 do nhash[h]:=0;
+
+@ Since there is no chance of the hash table overflowing, the procedure
+is very simple. After |lookup| has done its work, |cur_hash| will point
+to the place where the given name was found, or where it should be inserted.
+
+@p procedure lookup; {finds |cur_name| in the dictionary}
+var k:0..longest_name; {index into |cur_name|}
+@!j:0..max_letters; {index into |dictionary|}
+@!not_found:boolean; {clumsy thing necessary to avoid |goto| statement}
+begin @<Compute the hash code, |cur_hash|, for |cur_name|@>;
+not_found:=true;
+while not_found do
+ begin if cur_hash=0 then cur_hash:=hash_prime-1@+else decr(cur_hash);
+ if nhash[cur_hash]=0 then not_found:=false
+ else begin j:=start[nhash[cur_hash]];
+ if start[nhash[cur_hash]+1]=j+name_length then
+ begin not_found:=false;
+ for k:=1 to name_length do
+ if dictionary[j+k-1]<>cur_name[k] then not_found:=true;
+ end;
+ end;
+ end;
+name_ptr:=nhash[cur_hash];
+end;
+
+@ @<Compute the hash...@>=
+cur_hash:=cur_name[1];
+for k:=2 to name_length do
+ cur_hash:=(cur_hash+cur_hash+cur_name[k]) mod hash_prime
+
+@ The ``meaning'' of the keyword that begins at |start[k]| in the
+dictionary is kept in |equiv[k]|. The numeric |equiv| codes are given
+symbolic meanings by the following definitions.
+
+@d comment_code=0
+@d check_sum_code=1
+@d design_size_code=2
+@d design_units_code=3
+@d coding_scheme_code=4
+@d family_code=5
+@d face_code=6
+@d seven_bit_safe_flag_code=7
+@d header_code= 8
+@d font_dimen_code=9
+@d lig_table_code=10
+@d boundary_char_code=11
+@d character_code=12
+@d parameter_code=20
+@d char_info_code=50
+@d width=1
+@d height=2
+@d depth=3
+@d italic=4
+@d char_wd_code=char_info_code+width
+@d char_ht_code=char_info_code+height
+@d char_dp_code=char_info_code+depth
+@d char_ic_code=char_info_code+italic
+@d next_larger_code=55
+@d var_char_code=56
+@d label_code=70
+@d stop_code=71
+@d skip_code=72
+@d krn_code=73
+@d lig_code=74
+
+@<Glo...@>=
+@!equiv:array[0..max_name_index] of byte;
+@!cur_code:byte; {equivalent most recently found in |equiv|}
+
+@ We have to get the keywords into the hash table and into the dictionary in
+the first place (sigh). The procedure that does this has the desired
+|equiv| code as a parameter. In order to facilitate \.{WEB} macro writing
+for the initialization, the keyword being initialized is placed into the
+last positions of |cur_name|, instead of the first positions.
+
+@p procedure enter_name(v:byte); {|cur_name| goes into the dictionary}
+var k:0..longest_name;
+begin for k:=1 to name_length do
+ cur_name[k]:=cur_name[k+longest_name-name_length];
+{now the name has been shifted into the correct position}
+lookup; {this sets |cur_hash| to the proper insertion place}
+nhash[cur_hash]:=start_ptr; equiv[start_ptr]:=v;
+for k:=1 to name_length do
+ begin dictionary[dict_ptr]:=cur_name[k]; incr(dict_ptr);
+ end;
+incr(start_ptr); start[start_ptr]:=dict_ptr;
+end;
+
+@ Here are the macros to load a name of up to 20 letters into the
+dictionary. For example, the macro |load5| is used for five-letter keywords.
+
+@d tail(#)==enter_name(#)
+@d t20(#)==cur_name[20]:=#;tail
+@d t19(#)==cur_name[19]:=#;t20
+@d t18(#)==cur_name[18]:=#;t19
+@d t17(#)==cur_name[17]:=#;t18
+@d t16(#)==cur_name[16]:=#;t17
+@d t15(#)==cur_name[15]:=#;t16
+@d t14(#)==cur_name[14]:=#;t15
+@d t13(#)==cur_name[13]:=#;t14
+@d t12(#)==cur_name[12]:=#;t13
+@d t11(#)==cur_name[11]:=#;t12
+@d t10(#)==cur_name[10]:=#;t11
+@d t9(#)==cur_name[9]:=#;t10
+@d t8(#)==cur_name[8]:=#;t9
+@d t7(#)==cur_name[7]:=#;t8
+@d t6(#)==cur_name[6]:=#;t7
+@d t5(#)==cur_name[5]:=#;t6
+@d t4(#)==cur_name[4]:=#;t5
+@d t3(#)==cur_name[3]:=#;t4
+@d t2(#)==cur_name[2]:=#;t3
+@d t1(#)==cur_name[1]:=#;t2
+@d load3==name_length:=3;t18
+@d load4==name_length:=4;t17
+@d load5==name_length:=5;t16
+@d load6==name_length:=6;t15
+@d load7==name_length:=7;t14
+@d load8==name_length:=8;t13
+@d load9==name_length:=9;t12
+@d load10==name_length:=10;t11
+@d load11==name_length:=11;t10
+@d load12==name_length:=12;t9
+@d load13==name_length:=13;t8
+@d load14==name_length:=14;t7
+@d load15==name_length:=15;t6
+@d load16==name_length:=16;t5
+@d load17==name_length:=17;t4
+@d load18==name_length:=18;t3
+@d load19==name_length:=19;t2
+@d load20==name_length:=20;t1
+
+@ (Thank goodness for keyboard macros in the text editor used to create this
+\.{WEB} file.)
+
+@<Enter all of the names and their equivalents, except the parameter names@>=
+equiv[0]:=comment_code; {this is used after unknown keywords}
+load8("C")("H")("E")("C")("K")("S")("U")("M")(check_sum_code);@/
+load10("D")("E")("S")("I")("G")("N")("S")("I")("Z")("E")(design_size_code);@/
+load11("D")("E")("S")("I")("G")("N")
+ ("U")("N")("I")("T")("S")(design_units_code);@/
+load12("C")("O")("D")("I")("N")("G")
+ ("S")("C")("H")("E")("M")("E")(coding_scheme_code);@/
+load6("F")("A")("M")("I")("L")("Y")(family_code);@/
+load4("F")("A")("C")("E")(face_code);@/
+load16("S")("E")("V")("E")("N")("B")("I")("T")@/@t\hskip2em@>
+ ("S")("A")("F")("E")("F")("L")("A")("G")(seven_bit_safe_flag_code);@/
+load6("H")("E")("A")("D")("E")("R")(header_code);@/
+load9("F")("O")("N")("T")("D")("I")("M")("E")("N")(font_dimen_code);@/
+load8("L")("I")("G")("T")("A")("B")("L")("E")(lig_table_code);@/
+load12("B")("O")("U")("N")("D")("A")("R")("Y")("C")("H")("A")("R")
+ (boundary_char_code);@/
+load9("C")("H")("A")("R")("A")("C")("T")("E")("R")(character_code);@/
+load9("P")("A")("R")("A")("M")("E")("T")("E")("R")(parameter_code);@/
+load6("C")("H")("A")("R")("W")("D")(char_wd_code);@/
+load6("C")("H")("A")("R")("H")("T")(char_ht_code);@/
+load6("C")("H")("A")("R")("D")("P")(char_dp_code);@/
+load6("C")("H")("A")("R")("I")("C")(char_ic_code);@/
+load10("N")("E")("X")("T")("L")("A")("R")("G")("E")("R")(next_larger_code);@/
+load7("V")("A")("R")("C")("H")("A")("R")(var_char_code);@/
+load3("T")("O")("P")(var_char_code+1);@/
+load3("M")("I")("D")(var_char_code+2);@/
+load3("B")("O")("T")(var_char_code+3);@/
+load3("R")("E")("P")(var_char_code+4);@/
+load3("E")("X")("T")(var_char_code+4); {compatibility with older \.{PL} format}
+load7("C")("O")("M")("M")("E")("N")("T")(comment_code);@/
+load5("L")("A")("B")("E")("L")(label_code);@/
+load4("S")("T")("O")("P")(stop_code);@/
+load4("S")("K")("I")("P")(skip_code);@/
+load3("K")("R")("N")(krn_code);@/
+load3("L")("I")("G")(lig_code);@/
+load4("/")("L")("I")("G")(lig_code+2);@/
+load5("/")("L")("I")("G")(">")(lig_code+6);@/
+load4("L")("I")("G")("/")(lig_code+1);@/
+load5("L")("I")("G")("/")(">")(lig_code+5);@/
+load5("/")("L")("I")("G")("/")(lig_code+3);@/
+load6("/")("L")("I")("G")("/")(">")(lig_code+7);@/
+load7("/")("L")("I")("G")("/")(">")(">")(lig_code+11);@/
+
+@ @<Enter the parameter names@>=
+load5("S")("L")("A")("N")("T")(parameter_code+1);@/
+load5("S")("P")("A")("C")("E")(parameter_code+2);@/
+load7("S")("T")("R")("E")("T")("C")("H")(parameter_code+3);@/
+load6("S")("H")("R")("I")("N")("K")(parameter_code+4);@/
+load7("X")("H")("E")("I")("G")("H")("T")(parameter_code+5);@/
+load4("Q")("U")("A")("D")(parameter_code+6);@/
+load10("E")("X")("T")("R")("A")("S")("P")("A")("C")("E")(parameter_code+7);@/
+load4("N")("U")("M")("1")(parameter_code+8);@/
+load4("N")("U")("M")("2")(parameter_code+9);@/
+load4("N")("U")("M")("3")(parameter_code+10);@/
+load6("D")("E")("N")("O")("M")("1")(parameter_code+11);@/
+load6("D")("E")("N")("O")("M")("2")(parameter_code+12);@/
+load4("S")("U")("P")("1")(parameter_code+13);@/
+load4("S")("U")("P")("2")(parameter_code+14);@/
+load4("S")("U")("P")("3")(parameter_code+15);@/
+load4("S")("U")("B")("1")(parameter_code+16);@/
+load4("S")("U")("B")("2")(parameter_code+17);@/
+load7("S")("U")("P")("D")("R")("O")("P")(parameter_code+18);@/
+load7("S")("U")("B")("D")("R")("O")("P")(parameter_code+19);@/
+load6("D")("E")("L")("I")("M")("1")(parameter_code+20);@/
+load6("D")("E")("L")("I")("M")("2")(parameter_code+21);@/
+load10("A")("X")("I")("S")("H")("E")("I")("G")("H")("T")(parameter_code+22);@/
+load20("D")("E")("F")("A")("U")("L")("T")("R")("U")("L")("E")@/@t\hskip2em@>
+ ("T")("H")("I")("C")("K")("N")("E")("S")("S")(parameter_code+8);@/
+load13("B")("I")("G")("O")("P")
+ ("S")("P")("A")("C")("I")("N")("G")("1")(parameter_code+9);@/
+load13("B")("I")("G")("O")("P")
+ ("S")("P")("A")("C")("I")("N")("G")("2")(parameter_code+10);@/
+load13("B")("I")("G")("O")("P")
+ ("S")("P")("A")("C")("I")("N")("G")("3")(parameter_code+11);@/
+load13("B")("I")("G")("O")("P")
+ ("S")("P")("A")("C")("I")("N")("G")("4")(parameter_code+12);@/
+load13("B")("I")("G")("O")("P")
+ ("S")("P")("A")("C")("I")("N")("G")("5")(parameter_code+13);@/
+
+@ When a left parenthesis has been scanned, the following routine
+is used to interpret the keyword that follows, and to store the
+equivalent value in |cur_code|.
+
+@p procedure get_name;
+begin incr(loc); incr(level); {pass the left parenthesis}
+cur_char:=" ";
+while cur_char=" " do get_next;
+if (cur_char>")")or(cur_char<"(") then decr(loc); {back up one character}
+name_length:=0; get_keyword_char; {prepare to scan the name}
+while cur_char<>" " do
+ begin if name_length=longest_name then cur_name[1]:="X" {force error}
+ else incr(name_length);
+ cur_name[name_length]:=cur_char;
+ get_keyword_char;
+ end;
+lookup;
+if name_ptr=0 then err_print('Sorry, I don''t know that property name');
+@.Sorry, I don't know...@>
+cur_code:=equiv[name_ptr];
+end;
+
+@* Scanning numeric data.
+The next thing we need is a trio of subroutines to read the one-byte,
+four-byte, and real numbers that may appear as property values.
+These subroutines are careful to stick to numbers between $-2^{31}$
+and $2^{31}-1$, inclusive, so that a computer with two's complement
+32-bit arithmetic will not be interrupted by overflow.
+
+@ The first number scanner, which returns a one-byte value, surely has
+no problems of arithmetic overflow.
+
+@p function get_byte:byte; {scans a one-byte property value}
+var acc:integer; {an accumulator}
+@!t:ASCII_code; {the type of value to be scanned}
+begin repeat get_next;
+until cur_char<>" "; {skip the blanks before the type code}
+t:=cur_char; acc:=0;
+repeat get_next;
+until cur_char<>" "; {skip the blanks after the type code}
+if t="C" then @<Scan an ASCII character code@>
+else if t="D" then @<Scan a small decimal number@>
+else if t="O" then @<Scan a small octal number@>
+else if t="H" then @<Scan a small hexadecimal number@>
+else if t="F" then @<Scan a face code@>
+else skip_error('You need "C" or "D" or "O" or "H" or "F" here');
+@.You need "C" or "D" ...here@>
+cur_char:=" "; get_byte:=acc;
+end;
+
+@ The |get_next| routine converts lower case to upper case, but it leaves
+the character in the buffer, so we can unconvert it.
+
+@<Scan an ASCII...@>=
+if (cur_char>=@'41)and(cur_char<=@'176)and
+ ((cur_char<"(")or(cur_char>")")) then
+ acc:=xord[buffer[loc]]
+else skip_error('"C" value must be standard ASCII and not a paren')
+@:C value}\.{"C" value must be...@>
+
+@ @<Scan a small dec...@>=
+begin while (cur_char>="0")and(cur_char<="9") do
+ begin acc:=acc*10+cur_char-"0";
+ if acc>255 then
+ begin skip_error('This value shouldn''t exceed 255');
+@.This value shouldn't...@>
+ acc:=0; cur_char:=" ";
+ end
+ else get_next;
+ end;
+backup;
+end
+
+@ @<Scan a small oct...@>=
+begin while (cur_char>="0")and(cur_char<="7") do
+ begin acc:=acc*8+cur_char-"0";
+ if acc>255 then
+ begin skip_error('This value shouldn''t exceed ''377');
+@.This value shouldn't...@>
+ acc:=0; cur_char:=" ";
+ end
+ else get_next;
+ end;
+backup;
+end
+
+@ @<Scan a small hex...@>=
+begin while ((cur_char>="0")and(cur_char<="9"))or
+ ((cur_char>="A")and(cur_char<="F")) do
+ begin if cur_char>="A" then cur_char:=cur_char+"0"+10-"A";
+ acc:=acc*16+cur_char-"0";
+ if acc>255 then
+ begin skip_error('This value shouldn''t exceed "FF');
+@.This value shouldn't...@>
+ acc:=0; cur_char:=" ";
+ end
+ else get_next;
+ end;
+backup;
+end
+
+@ @<Scan a face...@>=
+begin if cur_char="B" then acc:=2
+else if cur_char="L" then acc:=4
+else if cur_char<>"M" then acc:=18;
+get_next;
+if cur_char="I" then incr(acc)
+else if cur_char<>"R" then acc:=18;
+get_next;
+if cur_char="C" then acc:=acc+6
+else if cur_char="E" then acc:=acc+12
+else if cur_char<>"R" then acc:=18;
+if acc>=18 then
+ begin skip_error('Illegal face code, I changed it to MRR');
+@.Illegal face code...@>
+ acc:=0;
+ end;
+end
+
+@ The routine that scans a four-byte value puts its output into |cur_bytes|,
+which is a record containing (yes, you guessed it) four bytes.
+
+@<Types...@>=
+@!four_bytes=record @!b0:byte;@+@!b1:byte;@+@!b2:byte;@+@!b3:byte;@+end;
+
+@ @d c0==cur_bytes.b0
+@d c1==cur_bytes.b1
+@d c2==cur_bytes.b2
+@d c3==cur_bytes.b3
+
+@<Glob...@>=
+@!cur_bytes:four_bytes; {a four-byte accumulator}
+
+@ Since the |get_four_bytes| routine is used very infrequently, no attempt
+has been made to make it fast; we only want it to work.
+
+@p procedure get_four_bytes; {scans an octal constant and sets |four_bytes|}
+var c:integer; {leading byte}
+@!r:integer; {radix}
+@!q:integer; {|256/r|}
+begin repeat get_next;
+until cur_char<>" "; {skip the blanks before the type code}
+r:=0; c0:=0; c1:=0; c2:=0; c3:=0; {start with the accumulator zero}
+if cur_char="H" then r:=16
+else if cur_char="O" then r:=8
+else skip_error('An octal ("O") or hex ("H") value is needed here');
+@.An octal ("O") or hex ("H")...@>
+if r>0 then
+ begin q:=256 div r;
+ repeat get_next;
+ until cur_char<>" "; {skip the blanks after the type code}
+ while ((cur_char>="0")and(cur_char<="9"))or@|
+ ((cur_char>="A")and(cur_char<="F")) do
+ @<Multiply by |r|, add |cur_char-"0"|, and |get_next|@>;
+ end;
+end;
+
+@ @<Multiply by |r|...@>=
+begin if cur_char>="A" then cur_char:=cur_char+"0"+10-"A";
+c:=(r*c0)+(c1 div q);
+if c>255 then
+ begin c0:=0; c1:=0; c2:=0; c3:=0;
+ if r=8 then
+ skip_error('Sorry, the maximum octal value is O 37777777777')
+@.Sorry, the maximum octal...@>
+ else skip_error('Sorry, the maximum hex value is H FFFFFFFF');
+@.Sorry, the maximum hex...@>
+ end
+else if cur_char>="0"+r then skip_error('Illegal digit')
+@.Illegal digit@>
+else begin c0:=c;
+ c1:=(r*(c1 mod q))+(c2 div q);
+ c2:=(r*(c2 mod q))+(c3 div q);
+ c3:=(r*(c3 mod q))+cur_char-"0";
+ get_next;
+ end;
+end
+
+@ The remaining scanning routine is the most interesting. It scans a real
+constant and returns the nearest |fix_word| approximation to that constant.
+A |fix_word| is a 32-bit integer that represents a real value that
+has been multiplied by $2^{20}$. Since \.{PLtoTF} restricts the magnitude
+of reals to 2048, the |fix_word| will have a magnitude less than $2^{31}$.
+
+@d unity==@'4000000 {$2^{20}$, the |fix_word| 1.0}
+
+@<Types...@>=
+@!fix_word=integer; {a scaled real value with 20 bits of fraction}
+
+@ When a real value is desired, we might as well treat `\.D' and `\.R'
+formats as if they were identical.
+
+@p function get_fix:fix_word; {scans a real property value}
+var negative:boolean; {was there a minus sign?}
+@!acc:integer; {an accumulator}
+@!int_part:integer; {the integer part}
+@!j:0..7; {the number of decimal places stored}
+begin repeat get_next;
+until cur_char<>" "; {skip the blanks before the type code}
+negative:=false; acc:=0; {start with the accumulators zero}
+if (cur_char<>"R")and(cur_char<>"D") then
+ skip_error('An "R" or "D" value is needed here')
+@.An "R" or "D" ... needed here@>
+else begin @<Scan the blanks and/or signs after the type code@>;
+ while (cur_char>="0") and (cur_char<="9") do
+ @<Multiply by 10, add |cur_char-"0"|, and |get_next|@>;
+ int_part:=acc; acc:=0;
+ if cur_char="." then @<Scan the fraction part and put it in |acc|@>;
+ if (acc>=unity)and(int_part=2047) then
+ skip_error('Real constants must be less than 2048')
+@.Real constants must be...@>
+ else acc:=int_part*unity+acc;
+ end;
+if negative then get_fix:=-acc@+else get_fix:=acc;
+end;
+
+@ @<Scan the blanks...@>=
+repeat get_next;
+if cur_char="-" then
+ begin cur_char:=" "; negative:=not negative;
+ end
+else if cur_char="+" then cur_char:=" ";
+until cur_char<>" "
+
+@ @<Multiply by 10...@>=
+begin acc:=acc*10+cur_char-"0";
+if acc>=2048 then
+ begin skip_error('Real constants must be less than 2048');
+@.Real constants must be...@>
+ acc:=0; cur_char:=" ";
+ end
+else get_next;
+end
+
+@ To scan the fraction $.d_1d_2\ldots\,$, we keep track of up to seven
+of the digits $d_j$. A correct result is obtained if we first compute
+$f^\prime=\lfloor 2^{21}(d_1\ldots d_j)/10^j\rfloor$, after which
+$f=\lfloor(f^\prime+1)/2\rfloor$. It is possible to have $f=1.0$.
+
+@<Glob...@>=
+@!fraction_digits:array[1..7] of integer; {$2^{21}$ times $d_j$}
+
+@ @<Scan the frac...@>=
+begin j:=0; get_next;
+while (cur_char>="0")and(cur_char<="9") do
+ begin if j<7 then
+ begin incr(j); fraction_digits[j]:=@'10000000*(cur_char-"0");
+ end;
+ get_next;
+ end;
+acc:=0;
+while j>0 do
+ begin acc:=fraction_digits[j]+(acc div 10); decr(j);
+ end;
+acc:=(acc+10) div 20;
+end
+
+@* Storing the property values.
+When property values have been found, they are squirreled away in a bunch
+of arrays. The header information is unpacked into bytes in an array
+called |header_bytes|. The ligature/kerning program is stored in an array
+of type |four_bytes|.
+Another |four_bytes| array holds the specifications of extensible characters.
+The kerns and parameters are stored in separate arrays of |fix_word| values.
+
+Instead of storing the design size in the header array, we will keep it
+in a |fix_word| variable until the last minute. The number of units in the
+design size is also kept in a |fix_word|.
+
+@<Glob...@>=
+@!header_bytes:array[header_index] of byte; {the header block}
+@!header_ptr:header_index; {the number of header bytes in use}
+@!design_size:fix_word; {the design size}
+@!design_units:fix_word; {reciprocal of the scaling factor}
+@!seven_bit_safe_flag:boolean; {does the file claim to be seven-bit-safe?}
+@!lig_kern:array[0..max_lig_steps] of four_bytes; {the ligature program}
+@!nl:0..32767; {the number of ligature/kern instructions so far}
+@!min_nl:0..32767; {the final value of |nl| must be at least this}
+@!kern:array[0..max_kerns] of fix_word; {the distinct kerning amounts}
+@!nk:0..max_kerns; {the number of entries of |kern|}
+@!exten:array[0..255] of four_bytes; {extensible character specs}
+@!ne:0..256; {the number of extensible characters}
+@!param:array[1..max_param_words] of fix_word; {\.{FONTDIMEN} parameters}
+@!np:0..max_param_words; {the largest parameter set nonzero}
+@!check_sum_specified:boolean; {did the user name the check sum?}
+@!bchar:0..256; {the right boundary character, or 256 if unspecified}
+
+@ @<Types...@>=
+@!header_index=0..max_header_bytes;
+@!indx=0..@'77777;
+
+@ @<Local...@>=
+@!d:header_index; {an index into |header_bytes|}
+
+@ We start by setting up the default values.
+
+@d check_sum_loc=0
+@d design_size_loc=4
+@d coding_scheme_loc=8
+@d family_loc=coding_scheme_loc+40
+@d seven_flag_loc=family_loc+20
+@d face_loc=seven_flag_loc+3
+
+@<Set init...@>=
+for d:=0 to 18*4-1 do header_bytes[d]:=0;
+header_bytes[8]:=11; header_bytes[9]:="U";
+header_bytes[10]:="N";
+header_bytes[11]:="S";
+header_bytes[12]:="P";
+header_bytes[13]:="E";
+header_bytes[14]:="C";
+header_bytes[15]:="I";
+header_bytes[16]:="F";
+header_bytes[17]:="I";
+header_bytes[18]:="E";
+header_bytes[19]:="D";
+@.UNSPECIFIED@>
+for d:=family_loc to family_loc+11 do header_bytes[d]:=header_bytes[d-40];
+design_size:=10*unity; design_units:=unity; seven_bit_safe_flag:=false;@/
+header_ptr:=18*4; nl:=0; min_nl:=0; nk:=0; ne:=0; np:=0;@/
+check_sum_specified:=false; bchar:=256;
+
+@ Most of the dimensions, however, go into the |memory| array. There are
+at most 257 widths, 257 heights, 257 depths, and 257 italic corrections,
+since the value 0 is required but it need not be used. So |memory| has room
+for 1028 entries, each of which is a |fix_word|. An auxiliary table called
+|link| is used to link these words together in linear lists, so that
+sorting and other operations can be done conveniently.
+
+We also add four ``list head'' words to the |memory| and |link| arrays;
+these are in locations |width| through |italic|, i.e., 1 through 4.
+For example, |link[height]| points to the smallest element in
+the sorted list of distinct heights that have appeared so far, and
+|memory[height]| is the number of distinct heights.
+
+@d mem_size=1028+4 {number of nonzero memory addresses}
+
+@<Types...@>=
+@!pointer=0..mem_size; {an index into memory}
+
+@ The arrays |char_wd|, |char_ht|, |char_dp|, and |char_ic| contain
+pointers to the |memory| array entries where the corresponding dimensions
+appear. Two other arrays, |char_tag| and |char_remainder|, hold
+the other information that \.{TFM} files pack into a |char_info_word|.
+
+@d no_tag=0 {vanilla character}
+@d lig_tag=1 {character has a ligature/kerning program}
+@d list_tag=2 {character has a successor in a charlist}
+@d ext_tag=3 {character is extensible}
+@d bchar_label==char_remainder[256]
+ {beginning of ligature program for left boundary}
+
+@<Glob...@>=
+@!memory:array[pointer] of fix_word; {character dimensions and kerns}
+@!mem_ptr:pointer; {largest |memory| word in use}
+@!link:array[pointer] of pointer; {to make lists of |memory| items}
+@!char_wd:array[byte] of pointer; {pointers to the widths}
+@!char_ht:array[byte] of pointer; {pointers to the heights}
+@!char_dp:array[byte] of pointer; {pointers to the depths}
+@!char_ic:array[byte] of pointer; {pointers to italic corrections}
+@!char_tag:array[byte] of no_tag..ext_tag; {character tags}
+@!char_remainder:array[0..256] of 0..65535; {pointers to ligature labels,
+ next larger characters, or extensible characters}
+
+@ @<Local...@>=
+@!c:byte; {runs through all character codes}
+
+@ @<Set init...@>=
+bchar_label:=@'77777;
+for c:=0 to 255 do
+ begin char_wd[c]:=0; char_ht[c]:=0; char_dp[c]:=0; char_ic[c]:=0;@/
+ char_tag[c]:=no_tag; char_remainder[c]:=0;
+ end;
+memory[0]:=@'17777777777; {an ``infinite'' element at the end of the lists}
+memory[width]:=0; link[width]:=0; {width list is empty}
+memory[height]:=0; link[height]:=0; {height list is empty}
+memory[depth]:=0; link[depth]:=0; {depth list is empty}
+memory[italic]:=0; link[italic]:=0; {italic list is empty}
+mem_ptr:=italic;
+
+@ As an example of these data structures, let us consider the simple
+routine that inserts a potentially new element into one of the dimension
+lists. The first parameter indicates the list head (i.e., |h=width| for
+the width list, etc.); the second parameter is the value that is to be
+inserted into the list if it is not already present. The procedure
+returns the value of the location where the dimension appears in |memory|.
+The fact that |memory[0]| is larger than any legal dimension makes the
+algorithm particularly short.
+
+We do have to handle two somewhat subtle situations. A width of zero must be
+put into the list, so that a zero-width character in the font will not appear
+to be nonexistent (i.e., so that its |char_wd| index will not be zero), but
+this does not need to be done for heights, depths, or italic corrections.
+Furthermore, it is necessary to test for memory overflow even though we
+have provided room for the maximum number of different dimensions in any
+legal font, since the \.{PL} file might foolishly give any number of
+different sizes to the same character.
+
+@p function sort_in(@!h:pointer;@!d:fix_word):pointer; {inserts into list}
+var p:pointer; {the current node of interest}
+begin if (d=0)and(h<>width) then sort_in:=0
+else begin p:=h;
+ while d>=memory[link[p]] do p:=link[p];
+ if (d=memory[p])and(p<>h) then sort_in:=p
+ else if mem_ptr=mem_size then
+ begin err_print('Memory overflow: more than 1028 widths, etc');
+@.Memory overflow...@>
+ print_ln('Congratulations! It''s hard to make this error.');
+ sort_in:=p;
+ end
+ else begin incr(mem_ptr); memory[mem_ptr]:=d;
+ link[mem_ptr]:=link[p]; link[p]:=mem_ptr; incr(memory[h]);
+ sort_in:=mem_ptr;
+ end;
+ end;
+end;
+
+@ When these lists of dimensions are eventually written to the \.{TFM}
+file, we may have to do some rounding of values, because the \.{TFM} file
+allows at most 256 widths, 16 heights, 16 depths, and 64 italic
+corrections. The following procedure takes a given list head |h| and a
+given dimension |d|, and returns the minimum $m$ such that the elements of
+the list can be covered by $m$ intervals of width $d$. It also sets
+|next_d| to the smallest value $d^\prime>d$ such that the covering found
+by this procedure would be different. In particular, if $d=0$ it computes
+the number of elements of the list, and sets |next_d| to the smallest
+distance between two list elements. (The covering by intervals of width
+|next_d| is not guaranteed to have fewer than $m$ elements, but in practice
+this seems to happen most of the time.)
+
+@<Glob...@>=
+@!next_d:fix_word; {the next larger interval that is worth trying}
+
+@ Once again we can make good use of the fact that |memory[0]| is ``infinite.''
+
+@p function min_cover(@!h:pointer;@!d:fix_word):integer;
+var p:pointer; {the current node of interest}
+@!l:fix_word; {the least element covered by the current interval}
+@!m:integer; {the current size of the cover being generated}
+begin m:=0; p:=link[h]; next_d:=memory[0];
+while p<>0 do
+ begin incr(m); l:=memory[p];
+ while memory[link[p]]<=l+d do p:=link[p];
+ p:=link[p];
+ if memory[p]-l<next_d then next_d:=memory[p]-l;
+ end;
+min_cover:=m;
+end;
+
+@ The following procedure uses |min_cover| to determine the smallest $d$
+such that a given list can be covered with at most a given number of
+intervals.
+
+@p function shorten(@!h:pointer;m:integer):fix_word; {finds best way to round}
+var d:fix_word; {the current trial interval length}
+@!k:integer; {the size of a minimum cover}
+begin if memory[h]>m then
+ begin excess:=memory[h]-m;
+ k:=min_cover(h,0); d:=next_d; {now the answer is at least |d|}
+ repeat d:=d+d; k:=min_cover(h,d);
+ until k<=m; {first we ascend rapidly until finding the range}
+ d:=d div 2; k:=min_cover(h,d); {now we run through the feasible steps}
+ while k>m do
+ begin d:=next_d; k:=min_cover(h,d);
+ end;
+ shorten:=d;
+ end
+else shorten:=0;
+end;
+
+@ When we are nearly ready to output the \.{TFM} file, we will set
+|index[p]:=k| if the dimension in |memory[p]| is being rounded to the
+|k|th element of its list.
+
+@<Glob...@>=
+@!index:array[pointer] of byte;
+@!excess:byte; {number of words to remove, if list is being shortened}
+
+@ Here is the procedure that sets the |index| values. It also shortens
+the list so that there is only one element per covering interval;
+the remaining elements are the midpoints of their clusters.
+
+@p procedure set_indices(@!h:pointer;@!d:fix_word); {reduces and indexes a list}
+var p:pointer; {the current node of interest}
+@!q:pointer; {trails one step behind |p|}
+@!m:byte; {index number of nodes in the current interval}
+@!l:fix_word; {least value in the current interval}
+begin q:=h; p:=link[q]; m:=0;
+while p<>0 do
+ begin incr(m); l:=memory[p]; index[p]:=m;
+ while memory[link[p]]<=l+d do
+ begin p:=link[p]; index[p]:=m; decr(excess);
+ if excess=0 then d:=0;
+ end;
+ link[q]:=p; memory[p]:=l+(memory[p]-l) div 2; q:=p; p:=link[p];
+ end;
+memory[h]:=m;
+end;
+
+@* The input phase.
+We're ready now to read and parse the \.{PL} file, storing property
+values as we go.
+
+@<Glob...@>=
+@!c:byte; {the current character or byte being processed}
+
+@ @<Read all the input@>=
+cur_char:=" ";
+repeat while cur_char=" " do get_next;
+if cur_char="(" then @<Read a font property value@>
+else if (cur_char=")")and not input_has_ended then
+ begin err_print('Extra right parenthesis');
+ incr(loc); cur_char:=" ";
+ end
+@.Extra right parenthesis@>
+else if not input_has_ended then junk_error;
+until input_has_ended
+
+@ The |junk_error| routine just referred to is called when something
+appears in the forbidden area between properties of a property list.
+
+@p procedure junk_error; {gets past no man's land}
+begin err_print('There''s junk here that is not in parentheses');
+@.There's junk here...@>
+skip_to_paren;
+end;
+
+@ For each font property, we are supposed to read the data from the
+left parenthesis that is the current value of |cur_char| to the right
+parenthesis that matches it in the input. The main complication is
+to recover with reasonable grace from various error conditions that might arise.
+
+@<Read a font property value@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if cur_code>character_code then
+ flush_error('This property name doesn''t belong on the outer level')
+@.This property name doesn't belong...@>
+else begin @<Read the font property value specified by |cur_code|@>;
+ finish_the_property;
+ end;
+end
+
+@ @<Read the font property value spec...@>=
+case cur_code of
+check_sum_code: begin check_sum_specified:=true; read_four_bytes(check_sum_loc);
+ end;
+design_size_code: @<Read the design size@>;
+design_units_code: @<Read the design units@>;
+coding_scheme_code: read_BCPL(coding_scheme_loc,40);
+family_code: read_BCPL(family_loc,20);
+face_code:header_bytes[face_loc]:=get_byte;
+seven_bit_safe_flag_code: @<Read the seven-bit-safe flag@>;
+header_code: @<Read an indexed header word@>;
+font_dimen_code: @<Read font parameter list@>;
+lig_table_code: read_lig_kern;
+boundary_char_code: bchar:=get_byte;
+character_code: read_char_info;
+end
+
+@ The |case| statement just given makes use of two subroutines that we
+haven't defined yet. The first of these puts a 32-bit octal quantity
+into four specified bytes of the header block.
+
+@p procedure read_four_bytes(l:header_index);
+begin get_four_bytes;
+header_bytes[l]:=c0;
+header_bytes[l+1]:=c1;
+header_bytes[l+2]:=c2;
+header_bytes[l+3]:=c3;
+end;
+
+@ The second little procedure is used to scan a string and to store it in
+the ``{\mc BCPL} format'' required by \.{TFM} files. The string is supposed
+to contain at most |n| bytes, including the first byte (which holds the
+length of the rest of the string).
+
+@p procedure read_BCPL(l:header_index;n:byte);
+var k:header_index;
+begin k:=l;
+while cur_char=" " do get_next;
+while (cur_char<>"(")and(cur_char<>")") do
+ begin if k<l+n then incr(k);
+ if k<l+n then header_bytes[k]:=cur_char;
+ get_next;
+ end;
+if k=l+n then
+ begin err_print('String is too long; its first ',n-1:1,
+@.String is too long...@>
+ ' characters will be kept'); decr(k);
+ end;
+header_bytes[l]:=k-l;
+while k<l+n-1 do {tidy up the remaining bytes by setting them to nulls}
+ begin incr(k); header_bytes[k]:=0;
+ end;
+end;
+
+@ @<Read the design size@>=
+begin next_d:=get_fix;
+if next_d<unity then
+ err_print('The design size must be at least 1')
+@.The design size must...@>
+else design_size:=next_d;
+end
+
+@ @<Read the design units@>=
+begin next_d:=get_fix;
+if next_d<=0 then
+ err_print('The number of units per design size must be positive')
+@.The number of units...@>
+else design_units:=next_d;
+end
+
+@ @<Read the seven-bit-safe...@>=
+begin while cur_char=" " do get_next;
+if cur_char="T" then seven_bit_safe_flag:=true
+else if cur_char="F" then seven_bit_safe_flag:=false
+else err_print('The flag value should be "TRUE" or "FALSE"');
+@.The flag value should be...@>
+skip_to_paren;
+end
+
+@ @<Read an indexed header word@>=
+begin c:=get_byte;
+if c<18 then skip_error('HEADER indices should be 18 or more')
+@.HEADER indices...@>
+else if 4*c+4>max_header_bytes then
+ skip_error('This HEADER index is too big for my present table size')
+@.This HEADER index is too big...@>
+else begin while header_ptr<4*c+4 do
+ begin header_bytes[header_ptr]:=0; incr(header_ptr);
+ end;
+ read_four_bytes(4*c);
+ end;
+end
+
+@ The remaining kinds of font property values that need to be read are
+those that involve property lists on higher levels. Each of these has a
+loop similar to the one that was used at level zero. Then we put the
+right parenthesis back so that `|finish_the_property|' will be happy;
+there is probably a more elegant way to do this.
+
+@d finish_inner_property_list==begin decr(loc); incr(level); cur_char:=")";
+ end
+
+@<Read font parameter list@>=
+begin while level=1 do
+ begin while cur_char=" " do get_next;
+ if cur_char="(" then @<Read a parameter value@>
+ else if cur_char=")" then skip_to_end_of_item
+ else junk_error;
+ end;
+finish_inner_property_list;
+end
+
+@ @<Read a parameter value@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if (cur_code<parameter_code)or(cur_code>=char_wd_code) then
+ flush_error('This property name doesn''t belong in a FONTDIMEN list')
+@.This property name doesn't belong...@>
+else begin if cur_code=parameter_code then c:=get_byte
+ else c:=cur_code-parameter_code;
+ if c=0 then flush_error('PARAMETER index must not be zero')
+@.PARAMETER index must not...@>
+ else if c>max_param_words then
+ flush_error('This PARAMETER index is too big for my present table size')
+@.This PARAMETER index is too big...@>
+ else begin while np<c do
+ begin incr(np); param[np]:=0;
+ end;
+ param[c]:=get_fix;
+ finish_the_property;
+ end;
+ end;
+end
+
+@ @<Read ligature/kern list@>=
+begin lk_step_ended:=false;
+while level=1 do
+ begin while cur_char=" " do get_next;
+ if cur_char="(" then @<Read a ligature/kern command@>
+ else if cur_char=")" then skip_to_end_of_item
+ else junk_error;
+ end;
+finish_inner_property_list;
+end
+
+@ @<Read a ligature/kern command@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if cur_code<label_code then
+ flush_error('This property name doesn''t belong in a LIGTABLE list')
+@.This property name doesn't belong...@>
+else begin case cur_code of
+ label_code:@<Read a label step@>;
+ stop_code:@<Read a stop step@>;
+ skip_code:@<Read a skip step@>;
+ krn_code:@<Read a kerning step@>;
+ lig_code,lig_code+1,lig_code+2,lig_code+3,lig_code+5,lig_code+6,lig_code+7,
+ lig_code+11:@<Read a ligature step@>;
+ end; {there are no other cases |>=label_code|}
+ finish_the_property;
+ end;
+end
+
+@ When a character is about to be tagged, we call the following
+procedure so that an error message is given in case of multiple tags.
+
+@p procedure check_tag(c:byte); {print error if |c| already tagged}
+begin case char_tag[c] of
+no_tag: do_nothing;
+lig_tag: err_print('This character already appeared in a LIGTABLE LABEL');
+@.This character already...@>
+list_tag: err_print('This character already has a NEXTLARGER spec');
+ext_tag: err_print('This character already has a VARCHAR spec');
+end;
+end;
+
+@ @<Read a label step@>=
+begin while cur_char=" " do get_next;
+if cur_char="B" then
+ begin bchar_label:=nl; skip_to_paren; {\.{LABEL BOUNDARYCHAR}}
+ end
+else begin backup; c:=get_byte;
+ check_tag(c); char_tag[c]:=lig_tag; char_remainder[c]:=nl;
+ end;
+if min_nl<=nl then min_nl:=nl+1;
+lk_step_ended:=false;
+end
+
+@ @d stop_flag=128 {value indicating `\.{STOP}' in a lig/kern program}
+@d kern_flag=128 {op code for a kern step}
+
+@<Globals...@>=
+@!lk_step_ended:boolean;
+ {was the last \.{LIGTABLE} property \.{LIG} or \.{KRN}?}
+@!krn_ptr:0..max_kerns; {an index into |kern|}
+
+@ @<Read a stop step@>=
+if not lk_step_ended then
+ err_print('STOP must follow LIG or KRN')
+@.STOP must follow LIG or KRN@>
+else begin lig_kern[nl-1].b0:=stop_flag; lk_step_ended:=false;
+ end
+
+@ @<Read a skip step@>=
+if not lk_step_ended then
+ err_print('SKIP must follow LIG or KRN')
+@.SKIP must follow LIG or KRN@>
+else begin c:=get_byte;
+ if c>=128 then err_print('Maximum SKIP amount is 127')
+@.Maximum SKIP amount...@>
+ else if nl+c>=max_lig_steps then
+ err_print('Sorry, LIGTABLE too long for me to handle')
+@.Sorry, LIGTABLE too long...@>
+ else begin lig_kern[nl-1].b0:=c;
+ if min_nl<=nl+c then min_nl:=nl+c+1;
+ end;
+ lk_step_ended:=false;
+ end
+
+@ @<Read a ligature step@>=
+begin lig_kern[nl].b0:=0;
+lig_kern[nl].b2:=cur_code-lig_code;
+lig_kern[nl].b1:=get_byte;
+lig_kern[nl].b3:=get_byte;
+if nl>=max_lig_steps-1 then
+ err_print('Sorry, LIGTABLE too long for me to handle')
+@.Sorry, LIGTABLE too long...@>
+else incr(nl);
+lk_step_ended:=true;
+end
+
+@ @<Read a kerning step@>=
+begin lig_kern[nl].b0:=0; lig_kern[nl].b1:=get_byte;
+kern[nk]:=get_fix; krn_ptr:=0;
+while kern[krn_ptr]<>kern[nk] do incr(krn_ptr);
+if krn_ptr=nk then
+ begin if nk<max_kerns then incr(nk)
+ else begin err_print('Sorry, too many different kerns for me to handle');
+@.Sorry, too many different kerns...@>
+ decr(krn_ptr);
+ end;
+ end;
+lig_kern[nl].b2:=kern_flag+(krn_ptr div 256);
+lig_kern[nl].b3:=krn_ptr mod 256;
+if nl>=max_lig_steps-1 then
+ err_print('Sorry, LIGTABLE too long for me to handle')
+@.Sorry, LIGTABLE too long...@>
+else incr(nl);
+lk_step_ended:=true;
+end
+
+@ Finally we come to the part of \.{PLtoTF}'s input mechanism
+that is used most, the processing of individual character data.
+
+@<Read character info list@>=
+begin c:=get_byte; {read the character code that is being specified}
+@<Print |c| in octal notation@>;
+while level=1 do
+ begin while cur_char=" " do get_next;
+ if cur_char="(" then @<Read a character property@>
+ else if cur_char=")" then skip_to_end_of_item
+ else junk_error;
+ end;
+if char_wd[c]=0 then char_wd[c]:=sort_in(width,0); {legitimatize |c|}
+finish_inner_property_list;
+end
+
+@ @<Read a character prop...@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if (cur_code<char_wd_code)or(cur_code>var_char_code) then
+ flush_error('This property name doesn''t belong in a CHARACTER list')
+@.This property name doesn't belong...@>
+else begin case cur_code of
+ char_wd_code:char_wd[c]:=sort_in(width,get_fix);
+ char_ht_code:char_ht[c]:=sort_in(height,get_fix);
+ char_dp_code:char_dp[c]:=sort_in(depth,get_fix);
+ char_ic_code:char_ic[c]:=sort_in(italic,get_fix);
+ next_larger_code:begin check_tag(c); char_tag[c]:=list_tag;
+ char_remainder[c]:=get_byte;
+ end;
+ var_char_code:@<Read an extensible recipe for |c|@>;
+ end;@/
+ finish_the_property;
+ end;
+end
+
+@ @<Read an extensible r...@>=
+begin if ne=256 then
+ err_print('At most 256 VARCHAR specs are allowed')
+@.At most 256 VARCHAR specs...@>
+else begin check_tag(c); char_tag[c]:=ext_tag; char_remainder[c]:=ne;@/
+ exten[ne].b0:=0; exten[ne].b1:=0; exten[ne].b2:=0; exten[ne].b3:=0;
+ while level=2 do
+ begin while cur_char=" " do get_next;
+ if cur_char="(" then @<Read an extensible piece@>
+ else if cur_char=")" then skip_to_end_of_item
+ else junk_error;
+ end;
+ incr(ne);
+ finish_inner_property_list;
+ end;
+end
+
+@ @<Read an extensible p...@>=
+begin get_name;
+if cur_code=comment_code then skip_to_end_of_item
+else if (cur_code<var_char_code+1)or(cur_code>var_char_code+4) then
+ flush_error('This property name doesn''t belong in a VARCHAR list')
+@.This property name doesn't belong...@>
+else begin case cur_code-(var_char_code+1) of
+ 0:exten[ne].b0:=get_byte;
+ 1:exten[ne].b1:=get_byte;
+ 2:exten[ne].b2:=get_byte;
+ 3:exten[ne].b3:=get_byte;
+ end;@/
+ finish_the_property;
+ end;
+end
+
+@ The input routine is now complete except for the following code,
+which prints a progress report as the file is being read.
+
+@p procedure print_octal(c:byte); {prints three octal digits}
+begin print('''',(c div 64):1,((c div 8) mod 8):1,(c mod 8):1);
+end;
+
+@ @<Print |c| in octal...@>=
+begin if chars_on_line=8 then
+ begin print_ln(' '); chars_on_line:=1;
+ end
+else begin if chars_on_line>0 then print(' ');
+ incr(chars_on_line);
+ end;
+print_octal(c); {progress report}
+end
+
+@* The checking and massaging phase.
+Once the whole \.{PL} file has been read in, we must check it for consistency
+and correct any errors. This process consists mainly of running through
+the characters that exist and seeing if they refer to characters that
+don't exist. We also compute the true value of |seven_unsafe|; we make sure
+that the charlists and ligature programs contain no loops; and we
+shorten the lists of widths, heights, depths, and italic corrections,
+if necessary, to keep from exceeding the required maximum sizes.
+
+@<Glob...@>=
+@!seven_unsafe:boolean; {do seven-bit characters generate eight-bit ones?}
+
+@ @<Correct and check the information@>=
+if nl>0 then @<Make sure the ligature/kerning program ends appropriately@>;
+seven_unsafe:=false;
+for c:=0 to 255 do if char_wd[c]<>0 then
+ @<For all characters |g| generated by |c|,
+ make sure that |char_wd[g]| is nonzero, and
+ set |seven_unsafe| if |c<128<=g|@>;
+if bchar_label<@'77777 then
+ begin c:=256; @<Check ligature program of |c|@>;
+ end;
+if seven_bit_safe_flag and seven_unsafe then
+ print_ln('The font is not really seven-bit-safe!');
+@.The font is not...safe@>
+@<Check for infinite ligature loops@>;
+@<Doublecheck the lig/kern commands and the extensible recipes@>;
+for c:=0 to 255 do
+ @<Make sure that |c| is not the largest element of a charlist cycle@>;
+@<Put the width, height, depth, and italic lists into final form@>
+
+@ The checking that we need in several places is accomplished by three
+macros that are only slightly tricky.
+
+@d existence_tail(#)==begin char_wd[g]:=sort_in(width,0);
+ print(#,' '); print_octal(c);
+ print_ln(' had no CHARACTER spec.');
+ end;
+ end
+@d check_existence_and_safety(#)==begin g:=#;
+ if (g>=128)and(c<128) then seven_unsafe:=true;
+ if char_wd[g]=0 then existence_tail
+@d check_existence(#)==begin g:=#;
+ if char_wd[g]=0 then existence_tail
+
+@<For all characters |g| generated by |c|...@>=
+case char_tag[c] of
+no_tag: do_nothing;
+lig_tag: @<Check ligature program of |c|@>;
+list_tag: check_existence_and_safety(char_remainder[c])
+ ('The character NEXTLARGER than');
+@.The character NEXTLARGER...@>
+ext_tag:@<Check the pieces of |exten[c]|@>;
+end
+
+@ @<Check the pieces...@>=
+begin if exten[char_remainder[c]].b0>0 then
+ check_existence_and_safety(exten[char_remainder[c]].b0)
+ ('TOP piece of character');
+@.TOP piece of character...@>
+if exten[char_remainder[c]].b1>0 then
+ check_existence_and_safety(exten[char_remainder[c]].b1)
+ ('MID piece of character');
+@.MID piece of character...@>
+if exten[char_remainder[c]].b2>0 then
+ check_existence_and_safety(exten[char_remainder[c]].b2)
+ ('BOT piece of character');
+@.BOT piece of character...@>
+check_existence_and_safety(exten[char_remainder[c]].b3)
+ ('REP piece of character');
+@.REP piece of character...@>
+end
+
+@ @<Make sure that |c| is not the largest element of a charlist cycle@>=
+if char_tag[c]=list_tag then
+ begin g:=char_remainder[c];
+ while (g<c)and(char_tag[g]=list_tag) do g:=char_remainder[g];
+ if g=c then
+ begin char_tag[c]:=no_tag;
+ print('A cycle of NEXTLARGER characters has been broken at ');
+@.A cycle of NEXTLARGER...@>
+ print_octal(c); print_ln('.');
+ end;
+ end
+
+@ @<Glob...@>=
+@!delta:fix_word; {size of the intervals needed for rounding}
+
+@ @d round_message(#)==if delta>0 then print_ln('I had to round some ',
+@.I had to round...@>
+ #,'s by ',(((delta+1) div 2)/@'4000000):1:7,' units.')
+
+@<Put the width, height, depth, and italic lists into final form@>=
+delta:=shorten(width,255); set_indices(width,delta); round_message('width');@/
+delta:=shorten(height,15); set_indices(height,delta); round_message('height');@/
+delta:=shorten(depth,15); set_indices(depth,delta); round_message('depth');@/
+delta:=shorten(italic,63); set_indices(italic,delta);
+ round_message('italic correction');
+
+@ @d clear_lig_kern_entry== {make an unconditional \.{STOP}}
+ lig_kern[nl].b0:=255; lig_kern[nl].b1:=0;
+ lig_kern[nl].b2:=0; lig_kern[nl].b3:=0
+
+@<Make sure the ligature/kerning program ends...@>=
+begin if bchar_label<@'77777 then {make room for it}
+ begin clear_lig_kern_entry; incr(nl);
+ end; {|bchar_label| will be stored later}
+while min_nl>nl do
+ begin clear_lig_kern_entry; incr(nl);
+ end;
+if lig_kern[nl-1].b0=0 then lig_kern[nl-1].b0:=stop_flag;
+end
+
+@ It's not trivial to check for infinite loops generated by repeated
+insertion of ligature characters. But fortunately there is a nice
+algorithm for such testing, copied here from the program \.{TFtoPL}
+where it is explained further.
+
+@d simple=0 {$f(x,y)=z$}
+@d left_z=1 {$f(x,y)=f(z,y)$}
+@d right_z=2 {$f(x,y)=f(x,z)$}
+@d both_z=3 {$f(x,y)=f(f(x,z),y)$}
+@d pending=4 {$f(x,y)$ is being evaluated}
+
+
+@ @<Glo...@>=
+@!lig_ptr:0..max_lig_steps; {an index into |lig_kern|}
+@!hash:array[0..hash_size] of 0..66048; {$256x+y+1$ for $x\le257$ and $y\le255$}
+@!class:array[0..hash_size] of simple..pending;
+@!lig_z:array[0..hash_size] of 0..257;
+@!hash_ptr:0..hash_size; {the number of nonzero entries in |hash|}
+@!hash_list:array[0..hash_size] of 0..hash_size; {list of those nonzero entries}
+@!h,@!hh:0..hash_size; {indices into the hash table}
+@!tt:indx; {temporary register}
+@!x_lig_cycle,@!y_lig_cycle:0..256; {problematic ligature pair}
+
+@ @<Set init...@>=
+hash_ptr:=0; y_lig_cycle:=256;
+for k:=0 to hash_size do hash[k]:=0;
+
+@ @d lig_exam==lig_kern[lig_ptr].b1
+@d lig_gen==lig_kern[lig_ptr].b3
+
+@<Check lig...@>=
+begin lig_ptr:=char_remainder[c];
+repeat if hash_input(lig_ptr,c) then
+ begin if lig_kern[lig_ptr].b2<kern_flag then
+ begin if lig_exam<>bchar then
+ check_existence(lig_exam)('LIG character examined by');
+@.LIG character examined...@>
+ check_existence(lig_gen)('LIG character generated by');
+@.LIG character generated...@>
+ if lig_gen>=128 then if(c<128)or(c=256) then
+ if(lig_exam<128)or(lig_exam=bchar) then seven_unsafe:=true;
+ end
+ else if lig_exam<>bchar then
+ check_existence(lig_exam)('KRN character examined by');
+@.KRN character examined...@>
+ end;
+if lig_kern[lig_ptr].b0>=stop_flag then lig_ptr:=nl
+else lig_ptr:=lig_ptr+1+lig_kern[lig_ptr].b0;
+until lig_ptr>=nl;
+end
+
+@ The |hash_input| procedure is copied from \.{TFtoPL}, but it is made
+into a boolean function that returns |false| if the ligature command
+was masked by a previous one.
+
+@p function hash_input(@!p,@!c:indx):boolean;
+ {enter data for character |c| and command in location |p|, unless it isn't new}
+label 30; {go here for a quick exit}
+var @!cc:simple..both_z; {class of data being entered}
+@!zz:0..255; {function value or ligature character being entered}
+@!y:0..255; {the character after the cursor}
+@!key:integer; {value to be stored in |hash|}
+@!t:integer; {temporary register for swapping}
+begin if hash_ptr=hash_size then
+ begin hash_input:=false; goto 30;@+end;
+@<Compute the command parameters |y|, |cc|, and |zz|@>;
+key:=256*c+y+1; h:=(1009*key) mod hash_size;
+while hash[h]>0 do
+ begin if hash[h]<=key then
+ begin if hash[h]=key then
+ begin hash_input:=false; goto 30; {unused ligature command}
+ end;
+ t:=hash[h]; hash[h]:=key; key:=t; {do ordered-hash-table insertion}
+ t:=class[h]; class[h]:=cc; cc:=t; {namely, do a swap}
+ t:=lig_z[h]; lig_z[h]:=zz; zz:=t;
+ end;
+ if h>0 then decr(h)@+else h:=hash_size;
+ end;
+hash[h]:=key; class[h]:=cc; lig_z[h]:=zz;
+incr(hash_ptr); hash_list[hash_ptr]:=h;
+hash_input:=true;
+30:end;
+
+@ @<Compute the command param...@>=
+y:=lig_kern[p].b1; t:=lig_kern[p].b2; cc:=simple;
+zz:=lig_kern[p].b3;
+if t>=kern_flag then zz:=y
+else begin case t of
+ 0,6:do_nothing; {\.{LIG},\.{/LIG>}}
+ 5,11:zz:=y; {\.{LIG/>}, \.{/LIG/>>}}
+ 1,7:cc:=left_z; {\.{LIG/}, \.{/LIG/>}}
+ 2:cc:=right_z; {\.{/LIG}}
+ 3:cc:=both_z; {\.{/LIG/}}
+ end; {there are no other cases}
+ end
+
+@ (More good stuff from \.{TFtoPL}.)
+
+@p function f(@!h,@!x,@!y:indx):indx; forward;@t\2@>
+ {compute $f$ for arguments known to be in |hash[h]|}
+function eval(@!x,@!y:indx):indx; {compute $f(x,y)$ with hashtable lookup}
+var @!key:integer; {value sought in hash table}
+begin key:=256*x+y+1; h:=(1009*key) mod hash_size;
+while hash[h]>key do
+ if h>0 then decr(h)@+else h:=hash_size;
+if hash[h]<key then eval:=y {not in ordered hash table}
+else eval:=f(h,x,y);
+end;
+
+@ Pascal's beastly convention for |forward| declarations prevents us from
+saying |function f(h,x,y:indx):indx| here.
+
+@p function f;
+begin case class[h] of
+simple: do_nothing;
+left_z: begin class[h]:=pending; lig_z[h]:=eval(lig_z[h],y); class[h]:=simple;
+ end;
+right_z: begin class[h]:=pending; lig_z[h]:=eval(x,lig_z[h]); class[h]:=simple;
+ end;
+both_z: begin class[h]:=pending; lig_z[h]:=eval(eval(x,lig_z[h]),y);
+ class[h]:=simple;
+ end;
+pending: begin x_lig_cycle:=x; y_lig_cycle:=y; lig_z[h]:=257; class[h]:=simple;
+ end; {the value 257 will break all cycles, since it's not in |hash|}
+end; {there are no other cases}
+f:=lig_z[h];
+end;
+
+@ @<Check for infinite...@>=
+if hash_ptr<hash_size then for hh:=1 to hash_ptr do
+ begin tt:=hash_list[hh];
+ if class[tt]>simple then {make sure $f$ is well defined}
+ tt:=f(tt,(hash[tt]-1)div 256,(hash[tt]-1)mod 256);
+ end;
+if(hash_ptr=hash_size)or(y_lig_cycle<256) then
+ begin if hash_ptr<hash_size then
+ begin print('Infinite ligature loop starting with ');
+@.Infinite ligature loop...@>
+ if x_lig_cycle=256 then print('boundary')@+else print_octal(x_lig_cycle);
+ print(' and '); print_octal(y_lig_cycle); print_ln('!');
+ end
+ else print_ln('Sorry, I haven''t room for so many ligature/kern pairs!');
+@.Sorry, I haven't room...@>
+ print_ln('All ligatures will be cleared.');
+ for c:=0 to 255 do if char_tag[c]=lig_tag then
+ begin char_tag[c]:=no_tag; char_remainder[c]:=0;
+ end;
+ nl:=0; bchar:=256; bchar_label:=@'77777;
+ end
+
+@ The lig/kern program may still contain references to nonexistent characters,
+if parts of that program are never used. Similarly, there may be extensible
+characters that are never used, because they were overridden by
+\.{NEXTLARGER}, say. This would produce an invalid \.{TFM} file; so we
+must fix such errors.
+
+@d double_check_tail(#)==@t\1@>if char_wd[0]=0
+ then char_wd[0]:=sort_in(width,0);
+ print('Unused ',#,' refers to nonexistent character ');
+ print_octal(c); print_ln('!');
+ end;
+ end
+@d double_check_lig(#)==begin c:=lig_kern[lig_ptr].#;
+ if char_wd[c]=0 then if c<>bchar then
+ begin lig_kern[lig_ptr].#:=0; double_check_tail
+@d double_check_ext(#)==begin c:=exten[g].#;
+ if c>0 then if char_wd[c]=0 then
+ begin exten[g].#:=0; double_check_tail
+@d double_check_rep(#)==begin c:=exten[g].#;
+ if char_wd[c]=0 then
+ begin exten[g].#:=0; double_check_tail
+
+@<Doublecheck...@>=
+if nl>0 then for lig_ptr:=0 to nl-1 do
+ if lig_kern[lig_ptr].b2<kern_flag then
+ begin if lig_kern[lig_ptr].b0<255 then
+ begin double_check_lig(b1)('LIG step'); double_check_lig(b3)('LIG step');
+ end;
+ end
+ else double_check_lig(b1)('KRN step');
+@.Unused LIG step...@>
+@.Unused KRN step...@>
+if ne>0 then for g:=0 to ne-1 do
+ begin double_check_ext(b0)('VARCHAR TOP');
+ double_check_ext(b1)('VARCHAR MID');
+ double_check_ext(b2)('VARCHAR BOT');
+ double_check_rep(b3)('VARCHAR REP');
+@.Unused VARCHAR...@>
+ end
+
+@* The output phase.
+Now that we know how to get all of the font data correctly stored in
+\.{PLtoTF}'s memory, it only remains to write the answers out.
+
+First of all, it is convenient to have an abbreviation for output to the
+\.{TFM} file:
+
+@d out(#)==write(tfm_file,#)
+
+@ The general plan for producing \.{TFM} files is long but simple:
+
+@<Do the output@>=
+@<Compute the twelve subfile sizes@>;
+@<Output the twelve subfile sizes@>;
+@<Output the header block@>;
+@<Output the character info@>;
+@<Output the dimensions themselves@>;
+@<Output the ligature/kern program@>;
+@<Output the extensible character recipes@>;
+@<Output the parameters@>
+
+@ A \.{TFM} file begins with 12 numbers that tell how big its subfiles are.
+We already know most of these numbers; for example, the number of distinct
+widths is |memory[width]+1|, where the $+1$ accounts for the zero width that
+is always supposed to be present. But we still should compute the beginning
+and ending character codes (|bc| and |ec|), the number of header words (|lh|),
+and the total number of words in the \.{TFM} file (|lf|).
+
+@<Gl...@>=
+@!bc:byte; {the smallest character code in the font}
+@!ec:byte; {the largest character code in the font}
+@!lh:byte; {the number of words in the header block}
+@!lf:0..32767; {the number of words in the entire \.{TFM} file}
+@!not_found:boolean; {has a font character been found?}
+@!temp_width:fix_word; {width being used to compute a check sum}
+
+@ It might turn out that no characters exist at all. But \.{PLtoTF} keeps
+going and writes the \.{TFM} anyway. In this case |ec| will be~0 and |bc|
+will be~1.
+
+@<Compute the twelve...@>=
+lh:=header_ptr div 4;@/
+not_found:=true; bc:=0;
+while not_found do
+ if (char_wd[bc]>0)or(bc=255) then not_found:=false
+ else incr(bc);
+not_found:=true; ec:=255;
+while not_found do
+ if (char_wd[ec]>0)or(ec=0) then not_found:=false
+ else decr(ec);
+if bc>ec then bc:=1;
+incr(memory[width]); incr(memory[height]); incr(memory[depth]);
+incr(memory[italic]);@/
+@<Compute the ligature/kern program offset@>;
+lf:=6+lh+(ec-bc+1)+memory[width]+memory[height]+memory[depth]+
+memory[italic]+nl+lk_offset+nk+ne+np;
+
+@ @d out_size(#)==out((#) div 256); out((#) mod 256)
+
+@<Output the twelve subfile sizes@>=
+out_size(lf); out_size(lh); out_size(bc); out_size(ec);
+out_size(memory[width]); out_size(memory[height]);
+out_size(memory[depth]); out_size(memory[italic]);
+out_size(nl+lk_offset); out_size(nk); out_size(ne); out_size(np);
+
+@ The routines that follow need a few temporary variables of different types.
+
+@<Gl...@>=
+@!j:0..max_header_bytes; {index into |header_bytes|}
+@!p:pointer; {index into |memory|}
+@!q:width..italic; {runs through the list heads for dimensions}
+@!par_ptr:0..max_param_words; {runs through the parameters}
+
+@ The header block follows the subfile sizes. The necessary information all
+appears in |header_bytes|, except that the design size and the seven-bit-safe
+flag must still be set.
+
+@<Output the header block@>=
+if not check_sum_specified then @<Compute the check sum@>;
+header_bytes[design_size_loc]:=design_size div @'100000000;
+ {this works since |design_size>0|}
+header_bytes[design_size_loc+1]:=(design_size div @'200000) mod 256;
+header_bytes[design_size_loc+2]:=(design_size div 256) mod 256;
+header_bytes[design_size_loc+3]:=design_size mod 256;
+if not seven_unsafe then header_bytes[seven_flag_loc]:=128;
+for j:=0 to header_ptr-1 do out(header_bytes[j]);
+
+@ @<Compute the check sum@>=
+begin c0:=bc; c1:=ec; c2:=bc; c3:=ec;
+for c:=bc to ec do if char_wd[c]>0 then
+ begin temp_width:=memory[char_wd[c]];
+ if design_units<>unity then
+ temp_width:=round((temp_width/design_units)*1048576.0);
+ temp_width:=temp_width + (c+4)*@'20000000; {this should be positive}
+ c0:=(c0+c0+temp_width) mod 255;
+ c1:=(c1+c1+temp_width) mod 253;
+ c2:=(c2+c2+temp_width) mod 251;
+ c3:=(c3+c3+temp_width) mod 247;
+ end;
+header_bytes[check_sum_loc]:=c0;
+header_bytes[check_sum_loc+1]:=c1;
+header_bytes[check_sum_loc+2]:=c2;
+header_bytes[check_sum_loc+3]:=c3;
+end
+
+@ The next block contains packed |char_info|.
+
+@<Output the character info@>=
+index[0]:=0;
+for c:=bc to ec do
+ begin out(index[char_wd[c]]);
+ out(index[char_ht[c]]*16+index[char_dp[c]]);
+ out(index[char_ic[c]]*4+char_tag[c]);
+ out(char_remainder[c]);
+ end
+
+@ When a scaled quantity is output, we may need to divide it by |design_units|.
+The following subroutine takes care of this, using floating point arithmetic
+only if |design_units<>1.0|.
+
+@p procedure out_scaled(x:fix_word); {outputs a scaled |fix_word|}
+var @!n:byte; {the first byte after the sign}
+@!m:0..65535; {the two least significant bytes}
+begin if abs(x/design_units)>=16.0 then
+ begin print_ln('The relative dimension ',x/@'4000000:1:3,
+ ' is too large.');
+@.The relative dimension...@>
+ print(' (Must be less than 16*designsize');
+ if design_units<>unity then print(' =',design_units/@'200000:1:3,
+ ' designunits');
+ print_ln(')'); x:=0;
+ end;
+if design_units<>unity then x:=round((x/design_units)*1048576.0);
+if x<0 then
+ begin out(255); x:=x+@'100000000;
+ if x<=0 then x:=1;
+ end
+else begin out(0);
+ if x>=@'100000000 then x:=@'77777777;
+ end;
+n:=x div @'200000; m:=x mod @'200000;
+out(n); out(m div 256); out(m mod 256);
+end;
+
+@ We have output the packed indices for individual characters.
+The scaled widths, heights, depths, and italic corrections are next.
+
+@<Output the dimensions themselves@>=
+for q:=width to italic do
+ begin out(0); out(0); out(0); out(0); {output the zero word}
+ p:=link[q]; {head of list}
+ while p>0 do
+ begin out_scaled(memory[p]);
+ p:=link[p];
+ end;
+ end;
+
+@ One embarrassing problem remains: The ligature/kern program might be very
+long, but the starting addresses in |char_remainder| can be at most~255.
+Therefore we need to output some indirect address information; we want to
+compute |lk_offset| so that addition of |lk_offset| to all remainders makes
+all but |lk_offset| distinct remainders less than~256.
+
+For this we need a sorted table of all relevant remainders.
+
+@<Glob...@>=
+@!label_table:array[0..256] of record
+ @!rr: -1..@'77777; {sorted label values}
+ @!cc: byte; {associated characters}
+ end;
+@!label_ptr:0..256; {index of highest entry in |label_table|}
+@!sort_ptr:0..256; {index into |label_table|}
+@!lk_offset:0..256; {smallest offset value that might work}
+@!t:0..@'77777; {label value that is being redirected}
+@!extra_loc_needed:boolean; {do we need a special word for |bchar|?}
+
+@ @<Compute the ligature/kern program offset@>=
+@<Insert all labels into |label_table|@>;
+if bchar<256 then
+ begin extra_loc_needed:=true; lk_offset:=1;
+ end
+else begin extra_loc_needed:=false; lk_offset:=0;
+ end;
+@<Find the minimum |lk_offset| and adjust all remainders@>;
+if bchar_label<@'77777 then
+ begin lig_kern[nl-1].b2:=(bchar_label+lk_offset)div 256;
+ lig_kern[nl-1].b3:=(bchar_label+lk_offset)mod 256;
+ end
+
+@ @<Insert all labels...@>=
+label_ptr:=0; label_table[0].rr:=-1; {sentinel}
+for c:=bc to ec do if char_tag[c]=lig_tag then
+ begin sort_ptr:=label_ptr; {there's a hole at position |sort_ptr+1|}
+ while label_table[sort_ptr].rr>char_remainder[c] do
+ begin label_table[sort_ptr+1]:=label_table[sort_ptr];
+ decr(sort_ptr); {move the hole}
+ end;
+ label_table[sort_ptr+1].cc:=c;
+ label_table[sort_ptr+1].rr:=char_remainder[c];
+ incr(label_ptr);
+ end
+
+@ @<Find the minimum |lk_offset| and adjust all remainders@>=
+begin sort_ptr:=label_ptr; {the largest unallocated label}
+if label_table[sort_ptr].rr+lk_offset > 255 then
+ begin lk_offset:=0; extra_loc_needed:=false; {location 0 can do double duty}
+ repeat char_remainder[label_table[sort_ptr].cc]:=lk_offset;
+ while label_table[sort_ptr-1].rr=label_table[sort_ptr].rr do
+ begin decr(sort_ptr); char_remainder[label_table[sort_ptr].cc]:=lk_offset;
+ end;
+ incr(lk_offset); decr(sort_ptr);
+ until lk_offset+label_table[sort_ptr].rr<256;
+ {N.B.: |lk_offset=256| satisfies this when |sort_ptr=0|}
+ end;
+if lk_offset>0 then while sort_ptr>0 do
+ begin char_remainder[label_table[sort_ptr].cc]:=
+ char_remainder[label_table[sort_ptr].cc]+lk_offset;
+ decr(sort_ptr);
+ end;
+end
+
+@ @<Output the ligature/kern program@>=
+if extra_loc_needed then {|lk_offset=1|}
+ begin out(255); out(bchar); out(0); out(0);
+ end
+else for sort_ptr:=1 to lk_offset do {output the redirection specs}
+ begin t:=label_table[label_ptr].rr;
+ if bchar<256 then
+ begin out(255); out(bchar);
+ end
+ else begin out(254); out(0);
+ end;
+ out_size(t+lk_offset);
+ repeat decr(label_ptr); until label_table[label_ptr].rr<t;
+ end;
+if nl>0 then for lig_ptr:=0 to nl-1 do
+ begin out(lig_kern[lig_ptr].b0);
+ out(lig_kern[lig_ptr].b1);
+ out(lig_kern[lig_ptr].b2);
+ out(lig_kern[lig_ptr].b3);
+ end;
+if nk>0 then for krn_ptr:=0 to nk-1 do out_scaled(kern[krn_ptr])
+
+@ @<Output the extensible character recipes@>=
+if ne>0 then for c:=0 to ne-1 do
+ begin out(exten[c].b0);
+ out(exten[c].b1);
+ out(exten[c].b2);
+ out(exten[c].b3);
+ end;
+
+@ For our grand finale, we wind everything up by outputting the parameters.
+
+@<Output the parameters@>=
+for par_ptr:=1 to np do
+ begin if par_ptr=1 then
+ @<Output the slant (|param[1]|) without scaling@>
+ else out_scaled(param[par_ptr]);
+ end
+
+@ @<Output the slant...@>=
+begin if param[1]<0 then
+ begin param[1]:=param[1]+@'10000000000;
+ out((param[1] div @'100000000)+256-64);
+ end
+else out(param[1] div @'100000000);
+out((param[1] div @'200000) mod 256);
+out((param[1] div 256) mod 256);
+out(param[1] mod 256);
+end
+
+@* The main program.
+The routines sketched out so far need to be packaged into separate procedures,
+on some systems, since some \PASCAL\ compilers place a strict limit on the
+size of a routine. The packaging is done here in an attempt to avoid some
+system-dependent changes.
+
+@p procedure param_enter;
+begin @<Enter the parameter names@>;
+end;
+@#
+procedure name_enter; {enter all names and their equivalents}
+begin @<Enter all of the names...@>;
+param_enter;
+end;
+@#
+procedure read_lig_kern;
+var @!krn_ptr:0..max_kerns; {an index into |kern|}
+@!c:byte; {runs through all character codes}
+begin @<Read ligature/kern list@>;
+end;
+@#
+procedure read_char_info;
+var @!c:byte; {the char}
+begin @<Read character info list@>;
+end;
+@#
+procedure read_input;
+var @!c:byte; {header or parameter index}
+begin @<Read all the input@>;
+end;
+@#
+procedure corr_and_check;
+var @!c:0..256; {runs through all character codes}
+@!hh:0..hash_size; {an index into |hash_list|}
+@!lig_ptr:0..max_lig_steps; {an index into |lig_kern|}
+@!g:byte; {a character generated by the current character |c|}
+begin @<Correct and check the information@>
+end;
+
+@ Here is where \.{PLtoTF} begins and ends.
+
+@p begin initialize;@/
+name_enter;@/
+read_input; print_ln('.');@/
+corr_and_check;@/
+@<Do the output@>;
+end.
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{PLtoTF} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/systems/knuth/dist/texware/pooltype.web b/systems/knuth/dist/texware/pooltype.web
new file mode 100644
index 0000000000..a7f7617abf
--- /dev/null
+++ b/systems/knuth/dist/texware/pooltype.web
@@ -0,0 +1,430 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Version 1 was implemented in June 1982.
+% Slight changes were made in October, 1982, for version 0.6 of TeX.
+% Version 2 (July 1983) is consistent with TeX version 0.999.
+% Version 3 (September 1989) is consistent with 8-bit TeX.
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+
+\def\title{POOL\lowercase{type}}
+\def\contentspagenumber{101}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont POOLtype} processor}
+ \vskip 15pt
+ \centerline{(Version 3, September 1989)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926 and MCS-8300984,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+The \.{POOLtype} utility program converts string pool files output
+by \.{TANGLE} into a slightly more symbolic format that may be useful
+when \.{TANGLE}d programs are being debugged.
+
+It's a pretty trivial routine, but people may want to try transporting
+this program before they get up enough courage to tackle \TeX\ itself.
+The first 256 strings are treated as \TeX\ treats them, using routines
+copied from \TeX82.
+
+@ \.{POOLtype} is written entirely in standard \PASCAL, except that it has
+to do some slightly system-dependent character code conversion on input
+and output. The input is read from |pool_file|, and the output is written
+on |output|. If the input is erroneous, the |output| file will describe
+the error.
+@^system dependencies@>
+
+@p program POOLtype(@!pool_file,@!output);
+label 9999; {this labels the end of the program}
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var @<Local variables for initialization@>@;
+ begin @<Set initial values of key variables@>@/
+ end;
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d do_nothing == {empty statement}
+
+@* The character set.
+(The following material is copied verbatim from \TeX82.
+Thus, the same system-dependent changes should be made to both programs.)
+
+In order to make \TeX\ readily portable between a wide variety of
+computers, all of its input text is converted to an internal eight-bit
+code that includes standard ASCII, the ``American Standard Code for
+Information Interchange.'' This 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 to a
+text file.
+
+Such an internal code is relevant to users of \TeX\ primarily because it
+governs the positions of characters in the fonts. For example, the
+character `\.A' has ASCII code $65=@'101$, and when \TeX\ typesets
+this letter it specifies character number 65 in the current font.
+If that font actually has `\.A' in a different position, \TeX\ doesn't
+know what the real position is; the program that does the actual printing from
+\TeX's device-independent files is responsible for converting from ASCII to
+a particular font encoding.
+@^ASCII code@>
+
+\TeX's internal code is relevant also with respect to constants
+that begin with a reverse apostrophe; and it provides an index to the
+\.{\\catcode}, \.{\\mathcode}, \.{\\uccode}, \.{\\lccode}, and \.{\\delcode}
+tables.
+
+@ Characters of text that have been converted to \TeX's internal form
+are said to be of type |ASCII_code|, which is a subrange of the integers.
+
+@<Types...@>=
+@!ASCII_code=0..255; {eight-bit numbers}
+
+@ 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, especially in a program for typesetting;
+so the present specification of \TeX\ has been written under the assumption
+that the \PASCAL\ compiler and run-time system permit the use of text files
+with more than 64 distinguishable characters. More precisely, we assume that
+the character set contains at least the letters and symbols associated
+with ASCII codes @'40 through @'176; all of these characters are now
+available on most computer terminals.
+
+Since we are dealing with more characters than were present in the first
+\PASCAL\ compilers, we have to decide what to call the associated data
+type. Some \PASCAL s use the original name |char| for the
+characters in text files, even though there now are more than 64 such
+characters, while other \PASCAL s consider |char| to be a 64-element
+subrange of a larger data type that has some other name.
+
+In order to accommodate this difference, we shall use the name |text_char|
+to stand for the data type of the characters that are converted to and
+from |ASCII_code| when they are input and output. We shall also assume
+that |text_char| consists of the elements |chr(first_text_char)| through
+|chr(last_text_char)|, inclusive. The following definitions should be
+adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Local variables for init...@>=
+@!i:integer;
+
+@ The \TeX\ processor converts 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.
+
+@<Glob...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+
+@ Since we are assuming that our \PASCAL\ system 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 the standard part of the |xchr| array
+properly, without needing any system-dependent changes. On the other
+hand, it is possible to implement \TeX\ with less complete character
+sets, and in such cases it will be necessary to change something here.
+@^system dependencies@>
+
+@<Set init...@>=
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';@/
+
+@ Some of the ASCII codes without visible characters have been given symbolic
+names in this program because they are used with a special meaning.
+
+@d null_code=@'0 {ASCII code that might disappear}
+@d carriage_return=@'15 {ASCII code used at end of line}
+@d invalid_code=@'177 {ASCII code that many systems prohibit in text files}
+
+@ The ASCII code is ``standard'' only to a certain extent, since many
+computer installations have found it advantageous to have ready access
+to more than 94 printing characters. Appendix~C of {\sl The \TeX book\/}
+gives a complete specification of the intended correspondence between
+characters and \TeX's internal representation.
+@:TeXbook}{\sl The \TeX book@>
+
+If \TeX\ is being used
+on a garden-variety \PASCAL\ for which only standard ASCII
+codes will appear in the input and output files, it doesn't really matter
+what codes are specified in |xchr[0..@'37]|, but the safest policy is to
+blank everything out by using the code shown below.
+
+However, other settings of |xchr| will make \TeX\ more friendly on
+computers that have an extended character set, so that users can type things
+like `\.^^Z' instead of `\.{\\ne}'. People with extended character sets can
+assign codes arbitrarily, giving an |xchr| equivalent to whatever
+characters the users of \TeX\ are allowed to have in their input files.
+It is best to make the codes correspond to the intended interpretations as
+shown in Appendix~C whenever possible; but this is not necessary. For
+example, in countries with an alphabet of more than 26 letters, it is
+usually best to map the additional letters into codes less than~@'40.
+To get the most ``permissive'' character set, change |' '| on the
+right of these assignment statements to |chr(i)|.
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|. Note that if |xchr[i]=xchr[j]|
+where |i<j<@'177|, the value of |xord[xchr[i]]| will turn out to be
+|j| or more; hence, standard ASCII code numbers will be used instead of
+codes below @'40 in case there is a coincidence.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=invalid_code;
+for i:=@'200 to @'377 do xord[xchr[i]]:=i;
+for i:=0 to @'176 do xord[xchr[i]]:=i;
+
+@* String handling.
+(The following material is copied from the \\{get\_strings\_started} procedure
+of \TeX82, with slight changes.)
+
+@<Glob...@>=
+@!k,@!l:0..255; {small indices or counters}
+@!m,@!n:text_char; {characters input from |pool_file|}
+@!s:integer; {number of strings treated so far}
+
+@ The global variable |count| keeps track of the total number of characters
+in strings.
+
+@<Glob...@>=
+@!count:integer; {how long the string pool is, so far}
+
+@ @<Set init...@>=
+count:=0;
+
+@ This is the main program, where \.{POOLtype} starts and ends.
+
+@d abort(#)==begin write_ln(#); goto 9999;
+ end
+
+@p begin initialize;@/
+@<Make the first 256 strings@>;
+s:=256;@/
+@<Read the other strings from the \.{POOL} file,
+ or give an error message and abort@>;
+write_ln('(',count:1,' characters in all.)');
+9999:end.
+
+@ @d lc_hex(#)==l:=#;
+ if l<10 then l:=l+"0" @+else l:=l-10+"a"
+
+@<Make the first 256...@>=
+for k:=0 to 255 do
+ begin write(k:3,': "'); l:=k;
+ if (@<Character |k| cannot be printed@>) then
+ begin write(xchr["^"],xchr["^"]);
+ if k<@'100 then l:=k+@'100
+ else if k<@'200 then l:=k-@'100
+ else begin lc_hex(k div 16); write(xchr[l]); lc_hex(k mod 16); incr(count);
+ end;
+ count:=count+2;
+ end;
+ if l="""" then write(xchr[l],xchr[l])
+ else write(xchr[l]);
+ incr(count); write_ln('"');
+ end
+
+@ The first 128 strings will contain 95 standard ASCII characters, and the
+other 33 characters will be printed in three-symbol form like `\.{\^\^A}'
+unless a system-dependent change is made here. Installations that have
+an extended character set, where for example |xchr[@'32]=@t\.{\'^^Z\'}@>|,
+would like string @'32 to be the single character @'32 instead of the
+three characters @'136, @'136, @'132 (\.{\^\^Z}). On the other hand,
+even people with an extended character set will want to represent string
+@'15 by \.{\^\^M}, since @'15 is |carriage_return|; the idea is to
+produce visible strings instead of tabs or line-feeds or carriage-returns
+or bell-rings or characters that are treated anomalously in text files.
+
+Unprintable characters of codes 128--255 are, similarly, rendered
+\.{\^\^80}--\.{\^\^ff}.
+
+The boolean expression defined here should be |true| unless \TeX\
+internal code number~|k| corresponds to a non-troublesome visible
+symbol in the local character set. An appropriate formula for the
+extended character set recommended in {\sl The \TeX book\/} would, for
+example, be `|k in [0,@'10..@'12,@'14,@'15,@'33,@'177..@'377]|'.
+If character |k| cannot be printed, and |k<@'200|, then character |k+@'100| or
+|k-@'100| must be printable; moreover, ASCII codes |[@'41..@'46,
+@'60..@'71, @'141..@'146, @'160..@'171]| must be printable.
+Thus, at least 80 printable characters are needed.
+@:TeXbook}{\sl The \TeX book@>
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Character |k| cannot be printed@>=
+ (k<" ")or(k>"~")
+
+@ When the \.{WEB} system program called \.{TANGLE} processes a source file,
+it outputs a \PASCAL\ program and also a string pool file. The present
+program reads the latter file, where each string appears as a two-digit decimal
+length followed by the string itself, and the information is output with its
+associated index number. The strings are surrounded by double-quote marks;
+double-quotes in the string itself are repeated.
+
+@<Glob...@>=
+@!pool_file:packed file of text_char;
+ {the string-pool file output by \.{TANGLE}}
+@!xsum:boolean; {has the check sum been found?}
+
+@ @<Read the other strings...@>=
+reset(pool_file); xsum:=false;
+if eof(pool_file) then abort('! I can''t read the POOL file.');
+repeat @<Read one string, but abort if there are problems@>;
+until xsum;
+if not eof(pool_file) then abort('! There''s junk after the check sum')
+
+@ @<Read one string...@>=
+if eof(pool_file) then abort('! POOL file contained no check sum');
+read(pool_file,m,n); {read two digits of string length}
+if m<>'*' then
+ begin if (xord[m]<"0")or(xord[m]>"9")or(xord[n]<"0")or(xord[n]>"9") then
+ abort('! POOL line doesn''t begin with two digits');
+ l:=xord[m]*10+xord[n]-"0"*11; {compute the length}
+ write(s:3,': "'); count:=count+l;
+ for k:=1 to l do
+ begin if eoln(pool_file) then
+ begin write_ln('"'); abort('! That POOL line was too short');
+ end;
+ read(pool_file,m); write(xchr[xord[m]]);
+ if xord[m]="""" then write(xchr[""""]);
+ end;
+ write_ln('"'); incr(s);
+ end
+else xsum:=true;
+read_ln(pool_file)
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{POOLtype} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* Index.
+Indications of system dependencies appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/systems/knuth/dist/texware/tftopl.web b/systems/knuth/dist/texware/tftopl.web
new file mode 100644
index 0000000000..65095d4327
--- /dev/null
+++ b/systems/knuth/dist/texware/tftopl.web
@@ -0,0 +1,1596 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Version 0 was implemented in January 1982.
+% In February 1982 a new restriction on ligature steps was added.
+% In June 1982 the routines were divided into smaller pieces for IBM people,
+% and the result was designated "Version 1" in September 1982.
+% Slight changes were made in October, 1982, for version 0.6 of TeX.
+% Version 2 (July 1983) was released with TeX version 0.999.
+% Version 2.1 (September 1983) changed TEXINFO to FONTDIMEN.
+% Version 2.2 (February 1984) simplified decimal fraction output.
+% Version 2.3 (May 1984) fixed a bug when lh=17.
+% Version 2.4 (July 1984) fixed a bug involving unused ligature code.
+% Version 2.5 (September 1985) updated the standard codingscheme names.
+% Version 3 (October 1989) introduced new ligature capabilities.
+% Version 3.1 (November 1989) renamed z[] to lig_z[] for better portability.
+% Version 3.2 (February 2008) added a newline after a warning message.
+% Version 3.3 (January 2014) added a space to an error message (Breitenlohner),
+% and tests nl>lig_size not 4*lig_size (C. M. Connelly, Melissa O'Neill).
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+
+\def\title{TF\lowercase{to}PL}
+\def\contentspagenumber{201}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont TFtoPL} processor}
+ \vskip 15pt
+ \centerline{(Version 3.3, January 2014)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926 and MCS-8300984,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+The \.{TFtoPL} utility program converts \TeX\ font metric (``\.{TFM}'')
+files into equivalent property-list (``\.{PL}'') files. It also
+makes a thorough check of the given \.{TFM} file, using essentially the
+same algorithm as \TeX. Thus if \TeX\ complains that a \.{TFM}
+file is ``bad,'' this program will pinpoint the source or sources of
+badness. A \.{PL} file output by this program can be edited with
+a normal text editor, and the result can be converted back to \.{TFM}
+format using the companion program \.{PLtoTF}.
+
+The first \.{TFtoPL} program was designed by Leo Guibas in the summer of
+1978. Contributions by Frank Liang, Doug Wyatt, and Lyle Ramshaw
+also had a significant effect on the evolution of the present code.
+
+Extensions for an enhanced ligature mechanism were added by the author in 1989.
+
+The |banner| string defined here should be changed whenever \.{TFtoPL}
+gets modified.
+
+@d banner=='This is TFtoPL, Version 3.3' {printed when the program starts}
+
+@ This program is written entirely in standard \PASCAL, except that
+it occasionally has lower case letters in strings that are output.
+Such letters can be converted to upper case if necessary. The input is read
+from |tfm_file|, and the output is written on |pl_file|; error messages and
+other remarks are written on the |output| file, which the user may
+choose to assign to the terminal if the system permits it.
+@^system dependencies@>
+
+The term |print| is used instead of |write| when this program writes on
+the |output| file, so that all such output can be easily deflected.
+
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+
+@p program TFtoPL(@!tfm_file,@!pl_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ If the program has to stop prematurely, it goes to the
+`|final_end|'.
+
+@d final_end=9999 {label for the end of it all}
+
+@<Labels...@>=final_end;
+
+@ The following parameters can be changed at compile time to extend or
+reduce \.{TFtoPL}'s capacity.
+
+@<Constants...@>=
+@!tfm_size=30000; {maximum length of |tfm| data, in bytes}
+@!lig_size=5000; {maximum length of |lig_kern| program, in words}
+@!hash_size=5003; {preferably a prime number, a bit larger than the number
+ of character pairs in lig/kern steps}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d do_nothing == {empty statement}
+
+@* Font metric data.
+The idea behind \.{TFM} files is that typesetting routines like \TeX\
+need a compact way to store the relevant information about several
+dozen fonts, and computer centers need a compact way to store the
+relevant information about several hundred fonts. \.{TFM} files are
+compact, and most of the information they contain is highly relevant,
+so they provide a solution to the problem.
+
+The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
+Since the number of bytes is always a multiple of 4, we could
+also regard the file as a sequence of 32-bit words; but \TeX\ uses the
+byte interpretation, and so does \.{TFtoPL}. Note that the bytes
+are considered to be unsigned numbers.
+
+@<Glob...@>=
+@!tfm_file:packed file of 0..255;
+
+@ On some systems you may have to do something special to read a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+reset(tfm_file);
+
+@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
+integers that give the lengths of the various subsequent portions
+of the file. These twelve integers are, in order:
+$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
+|@!lf|&length of the entire file, in words;\cr
+|@!lh|&length of the header data, in words;\cr
+|@!bc|&smallest character code in the font;\cr
+|@!ec|&largest character code in the font;\cr
+|@!nw|&number of words in the width table;\cr
+|@!nh|&number of words in the height table;\cr
+|@!nd|&number of words in the depth table;\cr
+|@!ni|&number of words in the italic correction table;\cr
+|@!nl|&number of words in the lig/kern table;\cr
+|@!nk|&number of words in the kern table;\cr
+|@!ne|&number of words in the extensible character table;\cr
+|@!np|&number of font parameter words.\cr}}$$
+They are all nonnegative and less than $2^{15}$. We must have |bc-1<=ec<=255|,
+|ne<=256|, and
+$$\hbox{|lf=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
+Note that a font may contain as many as 256 characters (if |bc=0| and |ec=255|),
+and as few as 0 characters (if |bc=ec+1|).
+
+Incidentally, when two or more 8-bit bytes are combined to form an integer of
+16 or more bits, the most significant bytes appear first in the file.
+This is called BigEndian order.
+
+@<Glob...@>=
+@!lf,@!lh,@!bc,@!ec,@!nw,@!nh,@!nd,@!ni,@!nl,@!nk,@!ne,@!np:0..@'77777;
+ {subfile sizes}
+
+@ The rest of the \.{TFM} file may be regarded as a sequence of ten data
+arrays having the informal specification
+$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
+\vbox{\halign{\hfil\\{#}&$\,:\,$\arr#\hfil\cr
+header&|[0..lh-1]stuff|\cr
+char\_info&|[bc..ec]char_info_word|\cr
+width&|[0..nw-1]fix_word|\cr
+height&|[0..nh-1]fix_word|\cr
+depth&|[0..nd-1]fix_word|\cr
+italic&|[0..ni-1]fix_word|\cr
+lig\_kern&|[0..nl-1]lig_kern_command|\cr
+kern&|[0..nk-1]fix_word|\cr
+exten&|[0..ne-1]extensible_recipe|\cr
+param&|[1..np]fix_word|\cr}}$$
+The most important data type used here is a |@!fix_word|, which is
+a 32-bit representation of a binary fraction. A |fix_word| is a signed
+quantity, with the two's complement of the entire word used to represent
+negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
+binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
+the smallest is $-2048$. We will see below, however, that all but one of
+the |fix_word| values will lie between $-16$ and $+16$.
+
+@ The first data array is a block of header information, which contains
+general facts about the font. The header must contain at least two words,
+and for \.{TFM} files to be used with Xerox printing software it must
+contain at least 18 words, allocated as described below. When different
+kinds of devices need to be interfaced, it may be necessary to add further
+words to the header block.
+
+\yskip\hang|header[0]| is a 32-bit check sum that \TeX\ will copy into the
+\.{DVI} output file whenever it uses the font. Later on when the \.{DVI}
+file is printed, possibly on another computer, the actual font that gets
+used is supposed to have a check sum that agrees with the one in the
+\.{TFM} file used by \TeX. In this way, users will be warned about
+potential incompatibilities. (However, if the check sum is zero in either
+the font file or the \.{TFM} file, no check is made.) The actual relation
+between this check sum and the rest of the \.{TFM} file is not important;
+the check sum is simply an identification number with the property that
+incompatible fonts almost always have distinct check sums.
+@^check sum@>
+
+\yskip\hang|header[1]| is a |fix_word| containing the design size of the
+font, in units of \TeX\ points (7227 \TeX\ points = 254 cm). This number
+must be at least 1.0; it is fairly arbitrary, but usually the design size
+is 10.0 for a ``10 point'' font, i.e., a font that was designed to look
+best at a 10-point size, whatever that really means. When a \TeX\ user
+asks for a font `\.{at} $\delta$ \.{pt}', the effect is to override the
+design size and replace it by $\delta$, and to multiply the $x$ and~$y$
+coordinates of the points in the font image by a factor of $\delta$
+divided by the design size. {\sl All other dimensions in the\/\ \.{TFM}
+file are |fix_word|\kern-1pt\ numbers in design-size units.} Thus, for example,
+the value of |param[6]|, one \.{em} or \.{\\quad}, is often the |fix_word|
+value $2^{20}=1.0$, since many fonts have a design size equal to one em.
+The other dimensions must be less than 16 design-size units in absolute
+value; thus, |header[1]| and |param[1]| are the only |fix_word| entries in
+the whole \.{TFM} file whose first byte might be something besides 0 or
+255. @^design size@>
+
+\yskip\hang|header[2..11]|, if present, contains 40 bytes that identify
+the character coding scheme. The first byte, which must be between 0 and
+39, is the number of subsequent ASCII bytes actually relevant in this
+string, which is intended to specify what character-code-to-symbol
+convention is present in the font. Examples are \.{ASCII} for standard
+ASCII, \.{TeX text} for fonts like \.{cmr10} and \.{cmti9}, \.{TeX math
+extension} for \.{cmex10}, \.{XEROX text} for Xerox fonts, \.{GRAPHIC} for
+special-purpose non-alphabetic fonts, \.{UNSPECIFIED} for the default case
+when there is no information. Parentheses should not appear in this name.
+(Such a string is said to be in {\mc BCPL} format.)
+@^coding scheme@>
+
+\yskip\hang|header[12..16]|, if present, contains 20 bytes that name the
+font family (e.g., \.{CMR} or \.{HELVETICA}), in {\mc BCPL} format.
+This field is also known as the ``font identifier.''
+@^family name@>
+@^font identifier@>
+
+\yskip\hang|header[17]|, if present, contains a first byte called the
+|seven_bit_safe_flag|, then two bytes that are ignored, and a fourth byte
+called the |face|. If the value of the fourth byte is less than 18, it has
+the following interpretation as a ``weight, slope, and expansion'': Add 0
+or 2 or 4 (for medium or bold or light) to 0 or 1 (for roman or italic) to
+0 or 6 or 12 (for regular or condensed or extended). For example, 13 is
+0+1+12, so it represents medium italic extended. A three-letter code
+(e.g., \.{MIE}) can be used for such |face| data.
+
+\yskip\hang|header[18..@twhatever@>]| might also be present; the individual
+words are simply called |header[18]|, |header[19]|, etc., at the moment.
+
+@ Next comes the |char_info| array, which contains one |char_info_word|
+per character. Each |char_info_word| contains six fields packed into
+four bytes as follows.
+
+\yskip\hang first byte: |width_index| (8 bits)\par
+\hang second byte: |height_index| (4 bits) times 16, plus |depth_index|
+ (4~bits)\par
+\hang third byte: |italic_index| (6 bits) times 4, plus |tag|
+ (2~bits)\par
+\hang fourth byte: |remainder| (8 bits)\par
+\yskip\noindent
+The actual width of a character is |width[width_index]|, in design-size
+units; this is a device for compressing information, since many characters
+have the same width. Since it is quite common for many characters
+to have the same height, depth, or italic correction, the \.{TFM} format
+imposes a limit of 16 different heights, 16 different depths, and
+64 different italic corrections.
+
+Incidentally, the relation |width[0]=height[0]=depth[0]=italic[0]=0|
+should always hold, so that an index of zero implies a value of zero.
+The |width_index| should never be zero unless the character does
+not exist in the font, since a character is valid if and only if it lies
+between |bc| and |ec| and has a nonzero |width_index|.
+
+@ The |tag| field in a |char_info_word| has four values that explain how to
+interpret the |remainder| field.
+
+\yskip\hang|tag=0| (|no_tag|) means that |remainder| is unused.\par
+\hang|tag=1| (|lig_tag|) means that this character has a ligature/kerning
+program starting at |lig_kern[remainder]|.\par
+\hang|tag=2| (|list_tag|) means that this character is part of a chain of
+characters of ascending sizes, and not the largest in the chain. The
+|remainder| field gives the character code of the next larger character.\par
+\hang|tag=3| (|ext_tag|) means that this character code represents an
+extensible character, i.e., a character that is built up of smaller pieces
+so that it can be made arbitrarily large. The pieces are specified in
+|exten[remainder]|.\par
+
+@d no_tag=0 {vanilla character}
+@d lig_tag=1 {character has a ligature/kerning program}
+@d list_tag=2 {character has a successor in a charlist}
+@d ext_tag=3 {character is extensible}
+
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word is a
+|lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+ step if the byte is 128 or more, otherwise the next step is obtained by
+ skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+ then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+ a kern step otherwise.\par
+\hang fourth byte: |remainder|.\par
+\yskip\noindent
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+remainder]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|remainder| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+Notice that if $a=0$ and $b=1$, the current character is unchanged; if
+$a=b$ and $c=1$, the current character is changed but the next character is
+unchanged. \.{TFtoPL} will check to see that infinite loops are avoided.
+
+If the very first instruction of the |lig_kern| array has |skip_byte=255|,
+the |next_char| byte is the so-called right boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte=255|,
+there is a special ligature/kerning program for a left boundary character,
+beginning at location |256*op_byte+remainder|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte>128|, the program actually begins in location
+|256*op_byte+remainder|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location |<=255|.
+
+Any instruction with |skip_byte>128| in the |lig_kern| array must have
+|256*op_byte+remainder<nl|. If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+command is performed.
+
+@d stop_flag=128 {value indicating `\.{STOP}' in a lig/kern program}
+@d kern_flag=128 {op code for a kern step}
+
+@ Extensible characters are specified by an |extensible_recipe|,
+which consists of four bytes called |top|, |mid|,
+|bot|, and |rep| (in this order). These bytes are the character codes
+of individual pieces used to build up a large symbol.
+If |top|, |mid|, or |bot| are zero,
+they are not present in the built-up result. For example, an extensible
+vertical line is like an extensible bracket, except that the top and
+bottom pieces are missing.
+
+
+@ The final portion of a \.{TFM} file is the |param| array, which is another
+sequence of |fix_word| values.
+
+\yskip\hang|param[1]=@!slant| is the amount of italic slant, which is used
+to help position accents. For example, |slant=.25| means that when you go
+up one unit, you also go .25 units to the right. The |slant| is a pure
+number; it's the only |fix_word| other than the design size itself that is
+not scaled by the design size.
+
+\hang|param[2]=space| is the normal spacing between words in text.
+Note that character |" "| in the font need not have anything to do with
+blank spaces.
+
+\hang|param[3]=space_stretch| is the amount of glue stretching between words.
+
+\hang|param[4]=space_shrink| is the amount of glue shrinking between words.
+
+\hang|param[5]=x_height| is the height of letters for which accents don't
+have to be raised or lowered.
+
+\hang|param[6]=quad| is the size of one em in the font.
+
+\hang|param[7]=extra_space| is the amount added to |param[2]| at the
+ends of sentences.
+
+When the character coding scheme is \.{TeX math symbols}, the font is
+supposed to have 15 additional parameters called |num1|, |num2|, |num3|,
+|denom1|, |denom2|, |sup1|, |sup2|, |sup3|, |sub1|, |sub2|, |supdrop|,
+|subdrop|, |delim1|, |delim2|, and |axis_height|, respectively. When the
+character coding scheme is \.{TeX math extension}, the font is supposed to
+have six additional parameters called |default_rule_thickness| and
+|big_op_spacing1| through |big_op_spacing5|.
+
+@ So that is what \.{TFM} files hold. The next question is, ``What about
+\.{PL} files?'' A complete answer to that question appears in the
+documentation of the companion program, \.{PLtoTF}, so it will not
+be repeated here. Suffice it to say that a \.{PL} file is an ordinary
+\PASCAL\ text file, and that the output of \.{TFtoPL} uses only a
+subset of the possible constructions that might appear in a \.{PL} file.
+Furthermore, hardly anybody really wants to look at the formal
+definition of \.{PL} format, because it is almost self-explanatory when
+you see an example or two.
+
+@<Glob...@>=
+@!pl_file:text;
+
+@ @<Set init...@>=
+rewrite(pl_file);
+
+@* Unpacked representation.
+The first thing \.{TFtoPL} does is read the entire |tfm_file| into an array of
+bytes, |tfm[0..(4*lf-1)]|.
+
+@<Types...@>=
+@!byte=0..255; {unsigned eight-bit quantity}
+@!index=0..tfm_size; {address of a byte in |tfm|}
+
+@ @<Glob...@>=
+@!tfm:array [-1000..tfm_size] of byte; {the input data all goes here}
+ {the negative addresses avoid range checks for invalid characters}
+
+@ The input may, of course, be all screwed up and not a \.{TFM} file
+at all. So we begin cautiously.
+
+@d abort(#)==begin print_ln(#);
+ print_ln('Sorry, but I can''t go on; are you sure this is a TFM?');
+ goto final_end;
+ end
+
+@<Read the whole input file@>=
+read(tfm_file,tfm[0]);
+if tfm[0]>127 then abort('The first byte of the input file exceeds 127!');
+@.The first byte...@>
+if eof(tfm_file) then abort('The input file is only one byte long!');
+@.The input...one byte long@>
+read(tfm_file,tfm[1]); lf:=tfm[0]*@'400+tfm[1];
+if lf=0 then
+ abort('The file claims to have length zero, but that''s impossible!');
+@.The file claims...@>
+if 4*lf-1>tfm_size then abort('The file is bigger than I can handle!');
+@.The file is bigger...@>
+for tfm_ptr:=2 to 4*lf-1 do
+ begin if eof(tfm_file) then
+ abort('The file has fewer bytes than it claims!');
+@.The file has fewer bytes...@>
+ read(tfm_file,tfm[tfm_ptr]);
+ end;
+if not eof(tfm_file) then
+ begin print_ln('There''s some extra junk at the end of the TFM file,');
+@.There's some extra junk...@>
+ print_ln('but I''ll proceed as if it weren''t there.');
+ end
+
+@ After the file has been read successfully, we look at the subfile sizes
+to see if they check out.
+
+@d eval_two_bytes(#)==begin if tfm[tfm_ptr]>127 then
+ abort('One of the subfile sizes is negative!');
+@.One of the subfile sizes...@>
+ #:=tfm[tfm_ptr]*@'400+tfm[tfm_ptr+1];
+ tfm_ptr:=tfm_ptr+2;
+ end
+
+@<Set subfile sizes |lh|, |bc|, \dots, |np|@>=
+begin tfm_ptr:=2;@/
+eval_two_bytes(lh);
+eval_two_bytes(bc);
+eval_two_bytes(ec);
+eval_two_bytes(nw);
+eval_two_bytes(nh);
+eval_two_bytes(nd);
+eval_two_bytes(ni);
+eval_two_bytes(nl);
+eval_two_bytes(nk);
+eval_two_bytes(ne);
+eval_two_bytes(np);
+if lh<2 then abort('The header length is only ',lh:1,'!');
+@.The header length...@>
+if nl>lig_size then
+ abort('The lig/kern program is longer than I can handle!');
+@.The lig/kern program...@>
+if (bc>ec+1)or(ec>255) then abort('The character code range ',
+@.The character code range...@>
+ bc:1,'..',ec:1,' is illegal!');
+if (nw=0)or(nh=0)or(nd=0)or(ni=0) then
+ abort('Incomplete subfiles for character dimensions!');
+@.Incomplete subfiles...@>
+if ne>256 then abort('There are ',ne:1,' extensible recipes!');
+@.There are ... recipes@>
+if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then
+ abort('Subfile sizes don''t add up to the stated total!');
+@.Subfile sizes don't add up...@>
+end
+
+@ Once the input data successfully passes these basic checks,
+\.{TFtoPL} believes that it is a \.{TFM} file, and the conversion
+to \.{PL} format will take place. Access to the various subfiles
+is facilitated by computing the following base addresses. For example,
+the |char_info| for character |c| will start in location
+|4*(char_base+c)| of the |tfm| array.
+
+@<Globals...@>=
+@!char_base,@!width_base,@!height_base,@!depth_base,@!italic_base,
+@!lig_kern_base,@!kern_base,@!exten_base,@!param_base:integer;
+ {base addresses for the subfiles}
+
+@ @<Compute the base addresses@>=
+begin char_base:=6+lh-bc;
+width_base:=char_base+ec+1;
+height_base:=width_base+nw;
+depth_base:=height_base+nh;
+italic_base:=depth_base+nd;
+lig_kern_base:=italic_base+ni;
+kern_base:=lig_kern_base+nl;
+exten_base:=kern_base+nk;
+param_base:=exten_base+ne-1;
+end
+
+@ Of course we want to define macros that suppress the detail of how the
+font information is actually encoded. Each word will be referred to by
+the |tfm| index of its first byte. For example, if |c| is a character
+code between |bc| and |ec|, then |tfm[char_info(c)]| will be the
+first byte of its |char_info|, i.e., the |width_index|; furthermore
+|width(c)| will point to the |fix_word| for |c|'s width.
+
+@d check_sum=24
+@d design_size=check_sum+4
+@d scheme=design_size+4
+@d family=scheme+40
+@d random_word=family+20
+@d char_info(#)==4*(char_base+#)
+@d width_index(#)==tfm[char_info(#)]
+@d nonexistent(#)==((#<bc)or(#>ec)or(width_index(#)=0))
+@d height_index(#)==(tfm[char_info(#)+1] div 16)
+@d depth_index(#)==(tfm[char_info(#)+1] mod 16)
+@d italic_index(#)==(tfm[char_info(#)+2] div 4)
+@d tag(#)==(tfm[char_info(#)+2] mod 4)
+@d reset_tag(#)==tfm[char_info(#)+2]:=4*italic_index(#)+no_tag
+@d remainder(#)==tfm[char_info(#)+3]
+@d width(#)==4*(width_base+width_index(#))
+@d height(#)==4*(height_base+height_index(#))
+@d depth(#)==4*(depth_base+depth_index(#))
+@d italic(#)==4*(italic_base+italic_index(#))
+@d exten(#)==4*(exten_base+remainder(#))
+@d lig_step(#)==4*(lig_kern_base+(#))
+@d kern(#)==4*(kern_base+#) {here \#\ is an index, not a character}
+@d param(#)==4*(param_base+#) {likewise}
+
+@ One of the things we would like to do is take cognizance of fonts whose
+character coding scheme is \.{TeX math symbols} or \.{TeX math extension};
+we will set the |font_type| variable to one of the three choices
+|vanilla|, |mathsy|, or |mathex|.
+
+@d vanilla=0 {not a special scheme}
+@d mathsy=1 {\.{TeX math symbols} scheme}
+@d mathex=2 {\.{TeX math extension} scheme}
+
+@<Glob...@>=
+@!font_type:vanilla..mathex; {is this font special?}
+
+@* Basic output subroutines.
+Let us now define some procedures that will reduce the rest of \.{TFtoPL}'s
+work to a triviality.
+
+First of all, it is convenient to have an abbreviation for output to the
+\.{PL} file:
+
+@d out(#)==write(pl_file,#)
+
+@ In order to stick to standard \PASCAL, we use three strings called
+|ASCII_04|, |ASCII_10|, and |ASCII_14|, in terms of which we can do the
+appropriate conversion of ASCII codes. Three other little strings are
+used to produce |face| codes like \.{MIE}.
+
+@<Glob...@>=
+@!ASCII_04,@!ASCII_10,@!ASCII_14: packed array [1..32] of char;
+ {strings for output in the user's external character set}
+@!MBL_string,@!RI_string,@!RCE_string:packed array [1..3] of char;
+ {handy string constants for |face| codes}
+
+@ @<Set init...@>=
+ASCII_04:=' !"#$%&''()*+,-./0123456789:;<=>?';@/
+ASCII_10:='@@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_';@/
+ASCII_14:='`abcdefghijklmnopqrstuvwxyz{|}~ ';@/
+MBL_string:='MBL'; RI_string:='RI '; RCE_string:='RCE';
+
+@ The array |dig| will hold a sequence of digits to be output.
+
+@<Glob...@>=
+@!dig:array[0..11] of 0..9;
+
+@ Here, in fact, are two procedures that output |dig[j-1]|$\,\ldots\,$|dig[0]|,
+given $j>0$.
+
+@p procedure out_digs(j:integer); {outputs |j| digits}
+begin repeat decr(j); out(dig[j]:1);
+until j=0;
+end;
+@#
+procedure print_digs(j:integer); {prints |j| digits}
+begin repeat decr(j); print(dig[j]:1);
+until j=0;
+end;
+
+@ The |print_octal| procedure indicates how |print_digs| can be used.
+Since this procedure is used only to print character codes, it always
+produces three digits.
+
+@p procedure print_octal(c:byte); {prints octal value of |c|}
+var j:0..2; {index into |dig|}
+begin print(''''); {an apostrophe indicates the octal notation}
+for j:=0 to 2 do
+ begin dig[j]:=c mod 8; c:=c div 8;
+ end;
+print_digs(3);
+end;
+
+@ A \.{PL} file has nested parentheses, and we want to format the output
+so that its structure is clear. The |level| variable keeps track of the
+depth of nesting.
+
+@<Glob...@>=
+@!level:0..5;
+
+@ @<Set init...@>=
+level:=0;
+
+@ Three simple procedures suffice to produce the desired structure in the
+output.
+
+@p procedure out_ln; {finishes one line, indents the next}
+var l:0..5;
+begin write_ln(pl_file);
+for l:=1 to level do out(' ');
+end;
+@#
+procedure left; {outputs a left parenthesis}
+begin incr(level); out('(');
+end;
+@#
+procedure right; {outputs a right parenthesis and finishes a line}
+begin decr(level); out(')'); out_ln;
+end;
+
+@ The value associated with a property can be output in a variety of
+ways. For example, we might want to output a {\mc BCPL} string that
+begins in |tfm[k]|:
+
+@p procedure out_BCPL(@!k:index); {outputs a string, preceded by a blank space}
+var l:0..39; {the number of bytes remaining}
+begin out(' '); l:=tfm[k];
+while l>0 do
+ begin incr(k); decr(l);
+ case tfm[k] div @'40 of
+ 1: out(ASCII_04[1+(tfm[k] mod @'40)]);
+ 2: out(ASCII_10[1+(tfm[k] mod @'40)]);
+ 3: out(ASCII_14[1+(tfm[k] mod @'40)]);
+ end;
+ end;
+end;
+
+@ The property value might also be a sequence of |l| bytes, beginning
+in |tfm[k]|, that we would like to output in octal notation.
+The following procedure assumes that |l<=4|, but larger values of |l|
+could be handled easily by enlarging the |dig| array and increasing
+the upper bounds on |b| and |j|.
+
+@p procedure out_octal(@!k,@!l:index); {outputs |l| bytes in octal}
+var a:0..@'1777; {accumulator for bits not yet output}
+@!b:0..32; {the number of significant bits in |a|}
+@!j:0..11; {the number of digits of output}
+begin out(' O '); {specify octal format}
+a:=0; b:=0; j:=0;
+while l>0 do @<Reduce \(1)|l| by one, preserving the invariants@>;
+while (a>0)or(j=0) do
+ begin dig[j]:=a mod 8; a:=a div 8; incr(j);
+ end;
+out_digs(j);
+end;
+
+@ @<Reduce \(1)|l|...@>=
+begin decr(l);
+if tfm[k+l]<>0 then
+ begin while b>2 do
+ begin dig[j]:=a mod 8; a:=a div 8; b:=b-3; incr(j);
+ end;
+ case b of
+ 0: a:=tfm[k+l];
+ 1:a:=a+2*tfm[k+l];
+ 2:a:=a+4*tfm[k+l];
+ end;
+ end;
+b:=b+8;
+end
+
+@ The property value may be a character, which is output in octal
+unless it is a letter or a digit. This procedure is the only place
+where a lowercase letter will be output to the \.{PL} file.
+@^system dependencies@>
+
+@p procedure out_char(@!c:byte); {outputs a character}
+begin if font_type>vanilla then
+ begin tfm[0]:=c; out_octal(0,1)
+ end
+else if (c>="0")and(c<="9") then
+ out(' C ',c-"0":1)
+else if (c>="A")and(c<="Z") then
+ out(' C ',ASCII_10[c-"A"+2])
+else if (c>="a")and(c<="z") then
+ out(' C ',ASCII_14[c-"a"+2])
+else begin tfm[0]:=c; out_octal(0,1);
+ end;
+end;
+
+@ The property value might be a ``face'' byte, which is output in the
+curious code mentioned earlier, provided that it is less than 18.
+
+@p procedure out_face(@!k:index); {outputs a |face|}
+var s:0..1; {the slope}
+@!b:0..8; {the weight and expansion}
+begin if tfm[k]>=18 then out_octal(k,1)
+else begin out(' F '); {specify face-code format}
+ s:=tfm[k] mod 2; b:=tfm[k] div 2;
+ out(MBL_string[1+(b mod 3)]);
+ out(RI_string[1+s]);
+ out(RCE_string[1+(b div 3)]);
+ end;
+end;
+
+@ And finally, the value might be a |fix_word|, which is output in
+decimal notation with just enough decimal places for \.{PLtoTF}
+to recover every bit of the given |fix_word|.
+
+All of the numbers involved in the intermediate calculations of
+this procedure will be nonnegative and less than $10\cdot2^{24}$.
+
+@p procedure out_fix(@!k:index); {outputs a |fix_word|}
+var a:0..@'7777; {accumulator for the integer part}
+@!f:integer; {accumulator for the fraction part}
+@!j:0..12; {index into |dig|}
+@!delta:integer; {amount if allowable inaccuracy}
+begin out(' R '); {specify real format}
+a:=(tfm[k]*16)+(tfm[k+1] div 16);
+f:=((tfm[k+1] mod 16)*@'400+tfm[k+2])*@'400+tfm[k+3];
+if a>@'3777 then @<Reduce \(2)negative to positive@>;
+@<Output the integer part, |a|, in decimal notation@>;
+@<Output the fraction part, $|f|/2^{20}$, in decimal notation@>;
+end;
+
+@ The following code outputs at least one digit even if |a=0|.
+
+@<Output the integer...@>=
+begin j:=0;
+repeat dig[j]:=a mod 10; a:=a div 10; incr(j);
+until a=0;
+out_digs(j);
+end
+
+@ And the following code outputs at least one digit to the right
+of the decimal point.
+
+@<Output the fraction...@>=
+begin out('.'); f:=10*f+5; delta:=10;
+repeat if delta>@'4000000 then f:=f+@'2000000-(delta div 2);
+out(f div @'4000000:1); f:=10*(f mod @'4000000); delta:=delta*10;
+until f<=delta;
+end;
+
+@ @<Reduce \(2)negative to positive@>=
+begin out('-'); a:=@'10000-a;
+if f>0 then
+ begin f:=@'4000000-f; decr(a);
+ end;
+end
+
+@* Doing it.
+\TeX\ checks the information of a \.{TFM} file for validity as the
+file is being read in, so that no further checks will be needed when
+typesetting is going on. And when it finds something wrong, it justs
+calls the file ``bad,'' without identifying the nature of the problem,
+since \.{TFM} files are supposed to be good almost all of the time.
+
+Of course, a bad file shows up every now and again, and that's where
+\.{TFtoPL} comes in. This program wants to catch at least as many errors as
+\TeX\ does, and to give informative error messages besides.
+All of the errors are corrected, so that the \.{PL} output will
+be correct (unless, of course, the \.{TFM} file was so loused up
+that no attempt is being made to fathom it).
+
+@ Just before each character is processed, its code is printed in octal
+notation. Up to eight such codes appear on a line; so we have a variable
+to keep track of how many are currently there. We also keep track of
+whether or not any errors have had to be corrected.
+
+@<Glob...@>=
+@!chars_on_line:0..8; {the number of characters printed on the current line}
+@!perfect:boolean; {was the file free of errors?}
+
+@ @<Set init...@>=
+chars_on_line:=0;@/
+perfect:=true; {innocent until proved guilty}
+
+@ Error messages are given with the help of the |bad| and |range_error|
+and |bad_char| macros:
+
+@d bad(#)==begin perfect:=false; if chars_on_line>0 then print_ln(' ');
+ chars_on_line:=0; print_ln('Bad TFM file: ',#);
+ end
+@.Bad TFM file@>
+@d range_error(#)==begin perfect:=false; print_ln(' ');
+ print(#,' index for character ');
+ print_octal(c); print_ln(' is too large;');
+ print_ln('so I reset it to zero.');
+ end
+@d bad_char_tail(#)==print_octal(#); print_ln('.');
+ end
+@d bad_char(#)==begin perfect:=false; if chars_on_line>0 then print_ln(' ');
+ chars_on_line:=0; print('Bad TFM file: ',#,' nonexistent character ');
+ bad_char_tail
+@d correct_bad_char_tail(#)==print_octal(tfm[#]); print_ln('.'); tfm[#]:=bc;
+ end
+@d correct_bad_char(#)== begin perfect:=false;
+ if chars_on_line>0 then print_ln(' ');
+ chars_on_line:=0; print('Bad TFM file: ',#,' nonexistent character ');
+ correct_bad_char_tail
+
+@<Glob...@>=
+@!i:0..@'77777; {an index to words of a subfile}
+@!c:0..256; {a random character}
+@!d:0..3; {byte number in a word}
+@!k:index; {a random index}
+@!r:0..65535; {a random two-byte value}
+@!count:0..127; {for when we need to enumerate a small set}
+
+@ There are a lot of simple things to do, and they have to be done one
+at a time, so we might as well get down to business. The first things
+that \.{TFtoPL} will put into the \.{PL} file appear in the header part.
+
+@<Do the header@>=
+begin font_type:=vanilla;
+if lh>=12 then
+ begin @<Set the true |font_type|@>;
+ if lh>=17 then
+ begin @<Output the family name@>;
+ if lh>=18 then @<Output the rest of the header@>;
+ end;
+ @<Output the character coding scheme@>;
+ end;
+@<Output the design size@>;
+@<Output the check sum@>;
+@<Output the |seven_bit_safe_flag|@>;
+end
+
+@ @<Output the check sum@>=
+left; out('CHECKSUM'); out_octal(check_sum,4);
+right
+
+@ Incorrect design sizes are changed to 10 points.
+
+@d bad_design(#)==begin bad('Design size ',#,'!');
+@.Design size wrong@>
+ print_ln('I''ve set it to 10 points.');
+ out(' D 10');
+ end
+
+@ @<Output the design size@>=
+left; out('DESIGNSIZE');
+if tfm[design_size]>127 then bad_design('negative')
+else if (tfm[design_size]=0)and(tfm[design_size+1]<16) then
+ bad_design('too small')
+else out_fix(design_size);
+right;
+out('(COMMENT DESIGNSIZE IS IN POINTS)'); out_ln;
+out('(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)'); out_ln
+@.DESIGNSIZE IS IN POINTS@>
+
+@ Since we have to check two different {\mc BCPL} strings for validity,
+we might as well write a subroutine to make the check.
+
+@p procedure check_BCPL(@!k,@!l:index); {checks a string of length |<l|}
+var j:index; {runs through the string}
+@!c:byte; {character being checked}
+begin if tfm[k]>=l then
+ begin bad('String is too long; I''ve shortened it drastically.');
+@.String is too long...@>
+ tfm[k]:=1;
+ end;
+for j:=k+1 to k+tfm[k] do
+ begin c:=tfm[j];
+ if (c="(")or(c=")") then
+ begin bad('Parenthesis in string has been changed to slash.');
+@.Parenthesis...changed to slash@>
+ tfm[j]:="/";
+ end
+ else if (c<" ")or(c>"~") then
+ begin bad('Nonstandard ASCII code has been blotted out.');
+@.Nonstandard ASCII code...@>
+ tfm[j]:="?";
+ end
+ else if (c>="a")and(c<="z") then tfm[j]:=c+"A"-"a"; {upper-casify letters}
+ end;
+end;
+
+@ The |font_type| starts out |vanilla|; possibly we need to reset it.
+
+@<Set the true |font_type|@>=
+begin check_BCPL(scheme,40);
+if (tfm[scheme]>=11)and@|(tfm[scheme+1]="T")and@|
+ (tfm[scheme+2]="E")and@|(tfm[scheme+3]="X")and@|
+ (tfm[scheme+4]=" ")and@|(tfm[scheme+5]="M")and@|
+ (tfm[scheme+6]="A")and@|(tfm[scheme+7]="T")and@|
+ (tfm[scheme+8]="H")and@|(tfm[scheme+9]=" ") then
+ begin if (tfm[scheme+10]="S")and(tfm[scheme+11]="Y") then font_type:=mathsy
+ else if (tfm[scheme+10]="E")and(tfm[scheme+11]="X") then font_type:=mathex;
+ end;
+end
+
+@ @<Output the character coding scheme@>=
+left; out('CODINGSCHEME');
+out_BCPL(scheme);
+right
+
+@ @<Output the family name@>=
+left; out('FAMILY');
+check_BCPL(family,20);
+out_BCPL(family);
+right
+
+@ @<Output the rest of the header@>=
+begin left; out('FACE'); out_face(random_word+3); right;
+for i:=18 to lh-1 do
+ begin left; out('HEADER D ',i:1);
+ out_octal(check_sum+4*i,@,4); right;
+ end;
+end
+
+@ This program does not check to see if the |seven_bit_safe_flag| has the
+correct setting, i.e., if it really reflects the seven-bit-safety of
+the \.{TFM} file; the stated value is merely put into the \.{PL} file.
+The \.{PLtoTF} program will store a correct value and give a warning
+message if a file falsely claims to be safe.
+
+@<Output the |seven_bit_safe_flag|@>=
+if (lh>17) and (tfm[random_word]>127) then
+ begin left; out('SEVENBITSAFEFLAG TRUE'); right;
+ end
+
+@ The next thing to take care of is the list of parameters.
+
+@<Do the parameters@>=
+if np>0 then
+ begin left; out('FONTDIMEN'); out_ln;
+ for i:=1 to np do @<Check and output the $i$th parameter@>;
+ right;
+ end;
+@<Check to see if |np| is complete for this font type@>;
+
+@ @<Check to see if |np|...@>=
+if (font_type=mathsy)and(np<>22) then
+ print_ln('Unusual number of fontdimen parameters for a math symbols font (',
+@.Unusual number of fontdimen...@>
+ np:1,' not 22).')
+else if (font_type=mathex)and(np<>13) then
+ print_ln('Unusual number of fontdimen parameters for an extension font (',
+ np:1,' not 13).')
+
+@ All |fix_word| values except the design size and the first parameter
+will be checked to make sure that they are less than 16.0 in magnitude,
+using the |check_fix| macro:
+
+@d check_fix_tail(#)==bad(#,' ',i:1,' is too big;');
+ print_ln('I have set it to zero.');
+ end
+@d check_fix(#)==if (tfm[#]>0)and(tfm[#]<255) then
+ begin tfm[#]:=0; tfm[(#)+1]:=0; tfm[(#)+2]:=0; tfm[(#)+3]:=0;
+ check_fix_tail
+
+@<Check and output the $i$th parameter@>=
+begin left;
+if i=1 then out('SLANT') {this parameter is not checked}
+else begin check_fix(param(i))('Parameter');@/
+@.Parameter n is too big@>
+ @<Output the name of parameter $i$@>;
+ end;
+out_fix(param(i)); right;
+end
+
+@ @<Output the name...@>=
+if i<=7 then case i of
+ 2:out('SPACE');@+3:out('STRETCH');@+4:out('SHRINK');
+ 5:out('XHEIGHT');@+6:out('QUAD');@+7:out('EXTRASPACE')@+end
+else if (i<=22)and(font_type=mathsy) then case i of
+ 8:out('NUM1');@+9:out('NUM2');@+10:out('NUM3');
+ 11:out('DENOM1');@+12:out('DENOM2');
+ 13:out('SUP1');@+14:out('SUP2');@+15:out('SUP3');
+ 16:out('SUB1');@+17:out('SUB2');
+ 18:out('SUPDROP');@+19:out('SUBDROP');
+ 20:out('DELIM1');@+21:out('DELIM2');
+ 22:out('AXISHEIGHT')@+end
+else if (i<=13)and(font_type=mathex) then
+ if i=8 then out('DEFAULTRULETHICKNESS')
+ else out('BIGOPSPACING',i-8:1)
+else out('PARAMETER D ',i:1)
+
+@ We need to check the range of all the remaining |fix_word| values,
+and to make sure that |width[0]=0|, etc.
+
+@d nonzero_fix(#)==(tfm[#]>0)or(tfm[#+1]>0)or(tfm[#+2]>0)or(tfm[#+3]>0)
+
+@<Check the |fix_word| entries@>=
+if nonzero_fix(4*width_base) then bad('width[0] should be zero.');
+@.should be zero@>
+if nonzero_fix(4*height_base) then bad('height[0] should be zero.');
+if nonzero_fix(4*depth_base) then bad('depth[0] should be zero.');
+if nonzero_fix(4*italic_base) then bad('italic[0] should be zero.');
+for i:=0 to nw-1 do check_fix(4*(width_base+i))('Width');
+@.Width n is too big@>
+for i:=0 to nh-1 do check_fix(4*(height_base+i))('Height');
+@.Height n is too big@>
+for i:=0 to nd-1 do check_fix(4*(depth_base+i))('Depth');
+@.Depth n is too big@>
+for i:=0 to ni-1 do check_fix(4*(italic_base+i))('Italic correction');
+@.Italic correction n is too big@>
+if nk>0 then for i:=0 to nk-1 do check_fix(kern(i))('Kern');
+@.Kern n is too big@>
+
+@ The ligature/kerning program comes next. Before we can put it out in
+\.{PL} format, we need to make a table of ``labels'' that will be inserted
+into the program. For each character |c| whose |tag| is |lig_tag| and
+whose starting address is |r|, we will store the pair |(c,r)| in the
+|label_table| array. If there's a boundary-char program starting at~|r|,
+we also store the pair |(256,r)|.
+This array is sorted by its second components, using the
+simple method of straight insertion.
+
+@<Glob...@>=
+@!label_table:array[0..258] of record@t@>@/@!cc:0..256;@!rr:0..lig_size;end;
+@!label_ptr: 0..257; {the largest entry in |label_table|}
+@!sort_ptr:0..257; {index into |label_table|}
+@!boundary_char:0..256; {boundary character, or 256 if none}
+@!bchar_label:0..@'77777; {beginning of boundary character program}
+
+@ @<Set init...@>=
+boundary_char:=256; bchar_label:=@'77777;@/
+label_ptr:=0; label_table[0].rr:=0; {a sentinel appears at the bottom}
+
+@ We'll also identify and remove inaccessible program steps, using the
+|activity| array.
+
+@d unreachable=0 {a program step not known to be reachable}
+@d pass_through=1 {a program step passed through on initialization}
+@d accessible=2 {a program step that can be relevant}
+
+@<Glob...@>=
+@!activity:array[0..lig_size] of unreachable..accessible;
+@!ai,@!acti:0..lig_size; {indices into |activity|}
+
+@ @<Do the ligatures and kerns@>=
+if nl>0 then
+ begin for ai:=0 to nl-1 do activity[ai]:=unreachable;
+ @<Check for a boundary char@>;
+ end;
+@<Build the label table@>;
+if nl>0 then
+ begin left; out('LIGTABLE'); out_ln;@/
+ @<Compute the |activity| array@>;
+ @<Output and correct the ligature/kern program@>;
+ right;
+ @<Check for ligature cycles@>;
+ end
+
+@ We build the label table even when |nl=0|, because this catches errors
+that would not otherwise be detected.
+
+@<Build...@>=
+for c:=bc to ec do if tag(c)=lig_tag then
+ begin r:=remainder(c);
+ if r<nl then
+ begin if tfm[lig_step(r)]>stop_flag then
+ begin r:=256*tfm[lig_step(r)+2]+tfm[lig_step(r)+3];
+ if r<nl then if activity[remainder(c)]=unreachable then
+ activity[remainder(c)]:=pass_through;
+ end;
+ end;
+ if r>=nl then
+ begin perfect:=false; print_ln(' ');
+ print('Ligature/kern starting index for character '); print_octal(c);
+ print_ln(' is too large;'); print_ln('so I removed it.'); reset_tag(c);
+@.Ligature/kern starting index...@>
+ end
+ else @<Insert |(c,r)| into |label_table|@>;
+ end;
+label_table[label_ptr+1].rr:=lig_size; {put ``infinite'' sentinel at the end}
+
+@ @<Insert |(c,r)|...@>=
+begin sort_ptr:=label_ptr; {there's a hole at position |sort_ptr+1|}
+while label_table[sort_ptr].rr>r do
+ begin label_table[sort_ptr+1]:=label_table[sort_ptr];
+ decr(sort_ptr); {move the hole}
+ end;
+label_table[sort_ptr+1].cc:=c;
+label_table[sort_ptr+1].rr:=r; {fill the hole}
+incr(label_ptr); activity[r]:=accessible;
+end
+
+@ @<Check for a bound...@>=
+if tfm[lig_step(0)]=255 then
+ begin left; out('BOUNDARYCHAR');
+ boundary_char:=tfm[lig_step(0)+1]; out_char(boundary_char); right;
+ activity[0]:=pass_through;
+ end;
+if tfm[lig_step(nl-1)]=255 then
+ begin r:=256*tfm[lig_step(nl-1)+2]+tfm[lig_step(nl-1)+3];
+ if r>=nl then
+ begin perfect:=false; print_ln(' ');
+ print('Ligature/kern starting index for boundarychar is too large;');
+ print_ln('so I removed it.');
+@.Ligature/kern starting index...@>
+ end
+ else begin label_ptr:=1; label_table[1].cc:=256; label_table[1].rr:=r;
+ bchar_label:=r; activity[r]:=accessible;
+ end;
+ activity[nl-1]:=pass_through;
+ end
+
+@ @<Compute the |activity| array@>=
+for ai:=0 to nl-1 do if activity[ai]=accessible then
+ begin r:=tfm[lig_step(ai)];
+ if r<stop_flag then
+ begin r:=r+ai+1;
+ if r>=nl then
+ begin bad('Ligature/kern step ',ai:1,' skips too far;');
+@.Lig...skips too far@>
+ print_ln('I made it stop.'); tfm[lig_step(ai)]:=stop_flag;
+ end
+ else activity[r]:=accessible;
+ end;
+ end
+
+@ We ignore |pass_through| items, which don't need to be mentioned in
+the \.{PL} file.
+
+@<Output and correct the ligature...@>=
+sort_ptr:=1; {point to the next label that will be needed}
+for acti:=0 to nl-1 do if activity[acti]<>pass_through then
+ begin i:=acti; @<Take care of commenting out unreachable steps@>;
+ @<Output any labels for step $i$@>;
+ @<Output step $i$ of the ligature/kern program@>;
+ end;
+if level=2 then right {the final step was unreachable}
+
+@ @<Output any labels...@>=
+while i=label_table[sort_ptr].rr do
+ begin left; out('LABEL');
+ if label_table[sort_ptr].cc=256 then out(' BOUNDARYCHAR')
+ else out_char(label_table[sort_ptr].cc);
+ right; incr(sort_ptr);
+ end
+
+@ @<Take care of commenting out...@>=
+if activity[i]=unreachable then
+ begin if level=1 then
+ begin left; out('COMMENT THIS PART OF THE PROGRAM IS NEVER USED!'); out_ln;
+ end
+ end
+else if level=2 then right
+
+@ @<Output step $i$...@>=
+begin k:=lig_step(i);
+if tfm[k]>stop_flag then
+ begin if 256*tfm[k+2]+tfm[k+3]>=nl then
+ bad('Ligature unconditional stop command address is too big.');
+@.Ligature unconditional stop...@>
+ end
+else if tfm[k+2]>=kern_flag then @<Output a kern step@>
+else @<Output a ligature step@>;
+if tfm[k]>0 then
+ if level=1 then @<Output either \.{SKIP} or \.{STOP}@>;
+end
+
+@ The \.{SKIP} command is a bit tricky, because we will be omitting all
+inaccessible commands.
+
+@<Output either...@>=
+begin if tfm[k]>=stop_flag then out('(STOP)')
+else begin count:=0;
+ for ai:=i+1 to i+tfm[k] do if activity[ai]=accessible then incr(count);
+ out('(SKIP D ',count:1,')'); {possibly $count=0$, so who cares}
+ end;
+out_ln;
+end
+
+@ @<Output a kern step@>=
+begin if nonexistent(tfm[k+1]) then if tfm[k+1]<>boundary_char then
+ correct_bad_char('Kern step for')(k+1);
+@.Kern step for nonexistent...@>
+left; out('KRN'); out_char(tfm[k+1]);
+r:=256*(tfm[k+2]-kern_flag)+tfm[k+3];
+if r>=nk then
+ begin bad('Kern index too large.');
+@.Kern index too large@>
+ out(' R 0.0');
+ end
+else out_fix(kern(r));
+right;
+end
+
+@ @<Output a ligature step@>=
+begin if nonexistent(tfm[k+1]) then if tfm[k+1]<>boundary_char then
+ correct_bad_char('Ligature step for')(k+1);
+@.Ligature step for nonexistent...@>
+if nonexistent(tfm[k+3]) then
+ correct_bad_char('Ligature step produces the')(k+3);
+@.Ligature step produces...@>
+left; r:=tfm[k+2];
+if (r=4)or((r>7)and(r<>11)) then
+ begin print_ln('Ligature step with nonstandard code changed to LIG');
+ r:=0; tfm[k+2]:=0;
+ end;
+if r mod 4>1 then out('/');
+out('LIG');
+if odd(r) then out('/');
+while r>3 do
+ begin out('>'); r:=r-4;
+ end;
+out_char(tfm[k+1]); out_char(tfm[k+3]); right;
+end
+
+@ The last thing on \.{TFtoPL}'s agenda is to go through the
+list of |char_info| and spew out the information about each individual
+character.
+
+@<Do the characters@>=
+sort_ptr:=0; {this will suppress `\.{STOP}' lines in ligature comments}
+for c:=bc to ec do if width_index(c)>0 then
+ begin if chars_on_line=8 then
+ begin print_ln(' '); chars_on_line:=1;
+ end
+ else begin if chars_on_line>0 then print(' ');
+ incr(chars_on_line);
+ end;
+ print_octal(c); {progress report}
+ left; out('CHARACTER'); out_char(c); out_ln;
+ @<Output the character's width@>;
+ if height_index(c)>0 then @<Output the character's height@>;
+ if depth_index(c)>0 then @<Output the character's depth@>;
+ if italic_index(c)>0 then @<Output the italic correction@>;
+ case tag(c) of
+ no_tag: do_nothing;
+ lig_tag: @<Output the applicable part of the ligature/kern
+ program as a comment@>;
+ list_tag: @<Output the character link unless there is a problem@>;
+ ext_tag: @<Output an extensible character recipe@>;
+ end; {there are no other cases}
+ right;
+ end
+
+@ @<Output the character's width@>=
+begin left; out('CHARWD');
+if width_index(c)>=nw then range_error('Width')
+else out_fix(width(c));
+right;
+end
+
+@ @<Output the character's height@>=
+if height_index(c)>=nh then range_error('Height')
+@.Height index for char...@>
+else begin left; out('CHARHT'); out_fix(height(c)); right;
+ end
+
+@ @<Output the character's depth@>=
+if depth_index(c)>=nd then range_error('Depth')
+@.Depth index for char@>
+else begin left; out('CHARDP'); out_fix(depth(c)); right;
+ end
+
+@ @<Output the italic correction@>=
+if italic_index(c)>=ni then range_error('Italic correction')
+@.Italic correction index for char...@>
+else begin left; out('CHARIC'); out_fix(italic(c)); right;
+ end
+
+@ @<Output the applicable part of the ligature...@>=
+begin left; out('COMMENT'); out_ln;@/
+i:=remainder(c); r:=lig_step(i);
+if tfm[r]>stop_flag then i:=256*tfm[r+2]+tfm[r+3];
+repeat @<Output step...@>;
+if tfm[k]>=stop_flag then i:=nl
+else i:=i+1+tfm[k];
+until i>=nl;
+right;
+end
+
+@ We want to make sure that there is no cycle of characters linked together
+by |list_tag| entries, since \TeX\ doesn't want to risk endless loops.
+If such a cycle exists, the routine here detects it when processing
+the largest character code in the cycle.
+
+@<Output the character link unless there is a problem@>=
+begin r:=remainder(c);
+if nonexistent(r) then
+ begin bad_char('Character list link to')(r); reset_tag(c);
+@.Character list link...@>
+ end
+else begin while (r<c)and(tag(r)=list_tag) do r:=remainder(r);
+ if r=c then
+ begin bad('Cycle in a character list!');
+@.Cycle in a character list@>
+ print('Character '); print_octal(c);
+ print_ln(' now ends the list.');
+ reset_tag(c);
+ end
+ else begin left; out('NEXTLARGER'); out_char(remainder(c));
+ right;
+ end;
+ end;
+end
+
+@ @<Output an extensible character recipe@>=
+if remainder(c)>=ne then
+ begin range_error('Extensible'); reset_tag(c);
+@.Extensible index for char@>
+ end
+else begin left; out('VARCHAR'); out_ln;
+ @<Output the extensible pieces that exist@>;
+ right;
+ end
+
+@ @<Output the extensible pieces that...@>=
+for k:=0 to 3 do if (k=3)or(tfm[exten(c)+k]>0) then
+ begin left;
+ case k of
+ 0:out('TOP');@+1:out('MID');@+2:out('BOT');@+3:out('REP')@+end;
+ if nonexistent(tfm[exten(c)+k]) then out_char(c)
+ else out_char(tfm[exten(c)+k]);
+ right;
+ end
+
+@ Some of the extensible recipes may not actually be used, but \TeX\ will
+complain about them anyway if they refer to nonexistent characters.
+Therefore \.{TFtoPL} must check them too.
+
+@<Check the extensible recipes@>=
+if ne>0 then for c:=0 to ne-1 do for d:=0 to 3 do
+ begin k:=4*(exten_base+c)+d;
+ if (tfm[k]>0)or(d=3) then
+ begin if nonexistent(tfm[k]) then
+ begin bad_char('Extensible recipe involves the')(tfm[k]);
+@.Extensible recipe involves...@>
+ if d<3 then tfm[k]:=0;
+ end;
+ end;
+ end
+
+@* Checking for ligature loops.
+We have programmed almost everything but the most interesting calculation of
+all, which has been saved for last as a special treat. \TeX's extended ligature
+mechanism allows unwary users to specify sequences of ligature replacements
+that never terminate. For example, the pair of commands
+$$\.{(/LIG $x$ $y$) (/LIG $y$ $x$)}$$
+alternately replaces character $x$ by character $y$ and vice versa. A similar
+loop occurs if \.{(LIG/ $z$ $y$)} occurs in the program for $x$ and
+ \.{(LIG/ $z$ $x$)} occurs in the program for $y$.
+
+More complicated loops are also possible. For example, suppose the ligature
+programs for $x$ and $y$ are
+$$\vcenter{\halign{#\hfil\cr
+\.{(LABEL $x$)(/LIG/ $z$ $w$)(/LIG/> $w$ $y$)} \dots,\cr
+\.{(LABEL $y$)(LIG $w$ $x$)} \dots;\cr}}$$
+then the adjacent characters $xz$ change to $xwz$, $xywz$, $xxz$, $xxwz$,
+\dots, ad infinitum.
+
+@ To detect such loops, \.{TFtoPL} attempts to evaluate the function
+$f(x,y)$ for all character pairs $x$ and~$y$, where $f$ is defined as
+follows: If the current character is $x$ and the next character is
+$y$, we say the ``cursor'' is between $x$ and $y$; when the cursor
+first moves past $y$, the character immediately to its left is
+$f(x,y)$. This function is defined if and only if no infinite loop is
+generated when the cursor is between $x$ and~$y$.
+
+The function $f(x,y)$ can be defined recursively. It turns out that all pairs
+$(x,y)$ belong to one of five classes. The simplest class has $f(x,y)=y$; this
+happens if there's no ligature between $x$ and $y$, or in the cases
+\.{LIG/>} and \.{/LIG/>>}. Another simple class arises when there's a
+\.{LIG} or \.{/LIG>} between $x$ and~$y$, generating the character~$z$;
+then $f(x,y)=z$. Otherwise we always have $f(x,y)$ equal to
+either $f(x,z)$ or $f(z,y)$ or $f(f(x,z),y)$, where $z$ is the inserted
+ligature character.
+
+The first two of these classes can be merged; we can also consider
+$(x,y)$ to belong to the simple class when $f(x,y)$ has been evaluated.
+For technical reasons we allow $x$ to be 256 (for the boundary character
+at the left) or 257 (in cases when an error has been detected).
+
+For each pair $(x,y)$ having a ligature program step, we store
+$(x,y)$ in a hash table from which the values $z$ and $class$ can be read.
+
+@d simple=0 {$f(x,y)=z$}
+@d left_z=1 {$f(x,y)=f(z,y)$}
+@d right_z=2 {$f(x,y)=f(x,z)$}
+@d both_z=3 {$f(x,y)=f(f(x,z),y)$}
+@d pending=4 {$f(x,y)$ is being evaluated}
+
+@<Glob...@>=
+@!hash:array[0..hash_size] of 0..66048; {$256x+y+1$ for $x\le257$ and $y\le255$}
+@!class:array[0..hash_size] of simple..pending;
+@!lig_z:array[0..hash_size] of 0..257;
+@!hash_ptr:0..hash_size; {the number of nonzero entries in |hash|}
+@!hash_list:array[0..hash_size] of 0..hash_size; {list of those nonzero entries}
+@!h,@!hh:0..hash_size; {indices into the hash table}
+@!x_lig_cycle,@!y_lig_cycle:0..256; {problematic ligature pair}
+
+@ @<Check for ligature cycles@>=
+hash_ptr:=0; y_lig_cycle:=256;
+for hh:=0 to hash_size do hash[hh]:=0; {clear the hash table}
+for c:=bc to ec do if tag(c)=lig_tag then
+ begin i:=remainder(c);
+ if tfm[lig_step(i)]>stop_flag then
+ i:=256*tfm[lig_step(i)+2]+tfm[lig_step(i)+3];
+ @<Enter data for character $c$ starting at location |i| in the hash table@>;
+ end;
+if bchar_label<nl then
+ begin c:=256; i:=bchar_label;
+ @<Enter data for character $c$ starting at location |i| in the hash table@>;
+ end;
+if hash_ptr=hash_size then
+ begin print_ln('Sorry, I haven''t room for so many ligature/kern pairs!');
+@.Sorry, I haven't room...@>
+ goto final_end;
+ end;
+for hh:=1 to hash_ptr do
+ begin r:=hash_list[hh];
+ if class[r]>simple then {make sure $f$ is defined}
+ r:=f(r,(hash[r]-1)div 256,(hash[r]-1)mod 256);
+ end;
+if y_lig_cycle<256 then
+ begin print('Infinite ligature loop starting with ');
+@.Infinite ligature loop...@>
+ if x_lig_cycle=256 then print('boundary')@+else print_octal(x_lig_cycle);
+ print(' and '); print_octal(y_lig_cycle); print_ln('!');
+ out('(INFINITE LIGATURE LOOP MUST BE BROKEN!)'); goto final_end;
+ end
+
+@ @<Enter data for character $c$...@>=
+repeat hash_input; k:=tfm[lig_step(i)];
+if k>=stop_flag then i:=nl
+else i:=i+1+k;
+until i>=nl
+
+@ We use an ``ordered hash table'' with linear probing, because such a table
+is efficient when the lookup of a random key tends to be unsuccessful.
+
+@p procedure hash_input; {enter data for character |c| and command |i|}
+label 30; {go here for a quick exit}
+var @!cc:simple..both_z; {class of data being entered}
+@!zz:0..255; {function value or ligature character being entered}
+@!y:0..255; {the character after the cursor}
+@!key:integer; {value to be stored in |hash|}
+@!t:integer; {temporary register for swapping}
+begin if hash_ptr=hash_size then goto 30;
+@<Compute the command parameters |y|, |cc|, and |zz|@>;
+key:=256*c+y+1; h:=(1009*key) mod hash_size;
+while hash[h]>0 do
+ begin if hash[h]<=key then
+ begin if hash[h]=key then goto 30; {unused ligature command}
+ t:=hash[h]; hash[h]:=key; key:=t; {do ordered-hash-table insertion}
+ t:=class[h]; class[h]:=cc; cc:=t; {namely, do a swap}
+ t:=lig_z[h]; lig_z[h]:=zz; zz:=t;
+ end;
+ if h>0 then decr(h)@+else h:=hash_size;
+ end;
+hash[h]:=key; class[h]:=cc; lig_z[h]:=zz;
+incr(hash_ptr); hash_list[hash_ptr]:=h;
+30:end;
+
+@ We must store kern commands as well as ligature commands, because the former
+might make the latter inapplicable.
+
+@<Compute the command param...@>=
+k:=lig_step(i); y:=tfm[k+1]; t:=tfm[k+2]; cc:=simple; zz:=tfm[k+3];
+if t>=kern_flag then zz:=y
+else begin case t of
+ 0,6:do_nothing; {\.{LIG},\.{/LIG>}}
+ 5,11:zz:=y; {\.{LIG/>}, \.{/LIG/>>}}
+ 1,7:cc:=left_z; {\.{LIG/}, \.{/LIG/>}}
+ 2:cc:=right_z; {\.{/LIG}}
+ 3:cc:=both_z; {\.{/LIG/}}
+ end; {there are no other cases}
+ end
+
+@ Evaluation of $f(x,y)$ is handled by two mutually recursive procedures.
+Kind of a neat algorithm, generalizing a depth-first search.
+
+@p function f(@!h,@!x,@!y:index):index; forward;@t\2@>
+ {compute $f$ for arguments known to be in |hash[h]|}
+function eval(@!x,@!y:index):index; {compute $f(x,y)$ with hashtable lookup}
+var @!key:integer; {value sought in hash table}
+begin key:=256*x+y+1; h:=(1009*key) mod hash_size;
+while hash[h]>key do
+ if h>0 then decr(h)@+else h:=hash_size;
+if hash[h]<key then eval:=y {not in ordered hash table}
+else eval:=f(h,x,y);
+end;
+
+@ Pascal's beastly convention for |forward| declarations prevents us from
+saying |function f(h,x,y:index):index| here.
+
+@p function f;
+begin case class[h] of
+simple: do_nothing;
+left_z: begin class[h]:=pending; lig_z[h]:=eval(lig_z[h],y); class[h]:=simple;
+ end;
+right_z: begin class[h]:=pending; lig_z[h]:=eval(x,lig_z[h]); class[h]:=simple;
+ end;
+both_z: begin class[h]:=pending; lig_z[h]:=eval(eval(x,lig_z[h]),y);
+ class[h]:=simple;
+ end;
+pending: begin x_lig_cycle:=x; y_lig_cycle:=y; lig_z[h]:=257; class[h]:=simple;
+ end; {the value 257 will break all cycles, since it's not in |hash|}
+end; {there are no other cases}
+f:=lig_z[h];
+end;
+
+@* The main program.
+The routines sketched out so far need to be packaged into separate procedures,
+on some systems, since some \PASCAL\ compilers place a strict limit on the
+size of a routine. The packaging is done here in an attempt to avoid some
+system-dependent changes.
+
+First comes the |organize| procedure, which reads the input data and
+gets ready for subsequent events. If something goes wrong, the routine
+returns |false|.
+
+@p function organize:boolean;
+label final_end, 30;
+var tfm_ptr:index; {an index into |tfm|}
+begin @<Read the whole input file@>;@/
+@<Set subfile sizes |lh|, |bc|, \dots, |np|@>;@/
+@<Compute the base addresses@>;@/
+organize:=true; goto 30;
+final_end: organize:=false;
+30: end;
+
+@ Next we do the simple things.
+
+@p procedure do_simple_things;
+var i:0..@'77777; {an index to words of a subfile}
+begin @<Do the header@>;@/
+@<Do the parameters@>;@/
+@<Check the |fix_word| entries@>@/
+end;
+
+@ And then there's a routine for individual characters.
+
+@p procedure do_characters;
+var @!c:byte; {character being done}
+@!k:index; {a random index}
+@!ai:0..lig_size; {index into |activity|}
+begin @<Do the characters@>;@/
+end;
+
+@ Here is where \.{TFtoPL} begins and ends.
+@p begin initialize;@/
+if not organize then goto final_end;
+do_simple_things;@/
+@<Do the ligatures and kerns@>;
+@<Check the extensible recipes@>;
+do_characters; print_ln('.');@/
+if level<>0 then print_ln('This program isn''t working!');
+@.This program isn't working@>
+if not perfect then
+ begin out('(COMMENT THE TFM FILE WAS BAD, SO THE DATA HAS BEEN CHANGED!)');
+@.THE TFM FILE WAS BAD...@>
+ write_ln(pl_file);
+ end;
+final_end:end.
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{TFtoPL} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/systems/knuth/dist/web/tangle.web b/systems/knuth/dist/web/tangle.web
new file mode 100644
index 0000000000..8fa1c395f0
--- /dev/null
+++ b/systems/knuth/dist/web/tangle.web
@@ -0,0 +1,3325 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Version 0 was released in December, 1981.
+% Version 1 was released in September, 1982, with version 0 of TeX.
+% Slight changes were made in October, 1982, for version 0.6 of TeX.
+% Version 1.2 introduced {:nnn} comments, added @@= and @@\ (December, 1982).
+% Version 1.4 added "history" (February, 1983).
+% Version 1.5 conformed to TeX version 0.96 and fixed @@\ (March, 1983).
+% Version 1.7 introduced the new change file format (June, 1983).
+% Version 2.0 was released in July, 1983, with version 0.999 of TeX.
+% Version 2.5 was released in November, 1983, with version 1.0 of TeX.
+% Version 2.6 fixed a bug: force-line-break after a constant (August, 1984).
+% Version 2.7 fixed the definition of check_sum_prime (May, 1985).
+% Version 2.8 fixed a bug in change_buffer movement (August, 1985).
+% Version 2.9 allows nonnumeric macros before their def (December, 1988).
+% Version 3, for Sewell's book, fixed long-line bug in input_ln (March, 1989).
+% Version 4 was major change to allow 8-bit input (September, 1989).
+% Version 4.1 conforms to ANSI standard for-loop rules (September, 1990).
+% Version 4.2 fixes stat report if phase one dies (March, 1991).
+% Version 4.3 fixes @@ bug in verbatim, catches extra } (September, 1991).
+% Version 4.4 activates debug_help on errors as advertised (February, 1993).
+% Version 4.5 prevents modno-comments from being split across lines (Dec 2002).
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+\def\pb{$\.|\ldots\.|$} % Pascal brackets (|...|)
+\def\v{\.{\char'174}} % vertical (|) in typewriter font
+\mathchardef\BA="3224 % double arrow
+\def\({} % kludge for alphabetizing certain module names
+
+\def\title{TANGLE}
+\def\contentspagenumber{123} % should be odd
+\def\topofcontents{\null\vfill
+ \titlefalse % include headline on the contents page
+ \def\rheader{\mainfont Appendix E\hfil \contentspagenumber}
+ \centerline{\titlefont The {\ttitlefont TANGLE} processor}
+ \vskip 15pt
+ \centerline{(Version 4.5)}
+ \vfill}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+This program converts a \.{WEB} file to a \PASCAL\ file. It was written
+by D. E. Knuth in September, 1981; a somewhat similar {\mc SAIL} program had
+been developed in March, 1979. Since this program describes itself, a
+bootstrapping process involving hand-translation had to be used to get started.
+
+For large \.{WEB} files one should have a large memory, since \.{TANGLE} keeps
+all the \PASCAL\ text in memory (in an abbreviated form). The program uses
+a few features of the local \PASCAL\ compiler that may need to be changed in
+other installations:
+
+\yskip\item{1)} Case statements have a default.
+\item{2)} Input-output routines may need to be adapted for use with a particular
+character set and/or for printing messages on the user's terminal.
+
+\yskip\noindent
+These features are also present in the \PASCAL\ version of \TeX, where they
+are used in a similar (but more complex) way. System-dependent portions
+of \.{TANGLE} can be identified by looking at the entries for `system
+dependencies' in the index below.
+@!@^system dependencies@>
+
+The ``banner line'' defined here should be changed whenever \.{TANGLE}
+is modified.
+
+@d banner=='This is TANGLE, Version 4.5'
+
+@ The program begins with a fairly normal header, made up of pieces that
+@^system dependencies@>
+will mostly be filled in later. The \.{WEB} input comes from files |web_file|
+and |change_file|, the \PASCAL\ output goes to file |Pascal_file|,
+and the string pool output goes to file |pool|.
+
+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_TANGLE|.
+
+@d end_of_TANGLE = 9999 {go here to wrap it up}
+
+@p @t\4@>@<Compiler directives@>@/
+program TANGLE(@!web_file,@!change_file,@!Pascal_file,@!pool);
+label end_of_TANGLE; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@<Error handling procedures@>@/
+procedure initialize;
+ var @<Local variables for initialization@>@/
+ begin @<Set initial values@>@/
+ end;
+
+@ Some of this code is optional for use when debugging only;
+such material is enclosed between the delimiters |debug| and $|gubed|$.
+Other parts, delimited by |stat| and $|tats|$, are optionally included if
+statistics about \.{TANGLE}'s memory usage are desired.
+
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@f debug==begin
+@f gubed==end
+@#
+@d stat==@{ {change this to `$\\{stat}\equiv\null$'
+ when gathering usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$'
+ when gathering usage statistics}
+@f stat==begin
+@f tats==end
+
+@ The \PASCAL\ compiler used to develop this system has ``compiler
+directives'' that can appear in comments whose first character is a dollar sign.
+In production versions of \.{TANGLE} these directives tell the compiler that
+@^system dependencies@>
+it is safe to avoid range checks and to leave out the extra code it inserts
+for the \PASCAL\ debugger's benefit, although interrupts will occur if
+there is arithmetic overflow.
+
+@<Compiler directives@>=
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+
+@ Labels are given symbolic names by the following definitions. We insert
+the label `|exit|:' just before the `\ignorespaces|end|\unskip' of a
+procedure in which we have used the `|return|' statement defined below;
+the label `|restart|' is occasionally used at the very beginning of a
+procedure; and the label `|reswitch|' is occasionally used just prior to
+a \&{case} statement in which some cases change the conditions and we wish to
+branch to the newly applicable case.
+Loops that are set up with the \&{loop} construction defined below are
+commonly exited by going to `|done|' or to `|found|' or to `|not_found|',
+and they are sometimes repeated by going to `|continue|'.
+
+@d exit=10 {go here to leave a procedure}
+@d restart=20 {go here to start a procedure again}
+@d reswitch=21 {go here to start a case statement again}
+@d continue=22 {go here to resume a loop}
+@d done=30 {go here to exit a loop}
+@d found=31 {go here when you've found it}
+@d not_found=32 {go here when you've found something else}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
+@d do_nothing == {empty statement}
+@d return == goto exit {terminate a procedure call}
+@f return == nil
+@f loop == xclause
+
+@ We assume that |case| statements may include a default case that applies
+if no matching label is found. Thus, we shall use constructions like
+@^system dependencies@>
+$$\vbox{\halign{#\hfil\cr
+|case x of|\cr
+1: $\langle\,$code for $x=1\,\rangle$;\cr
+3: $\langle\,$code for $x=3\,\rangle$;\cr
+|othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr
+|endcases|\cr}}$$
+since most \PASCAL\ compilers have plugged this hole in the language by
+incorporating some sort of default mechanism. For example, the compiler
+used to develop \.{WEB} and \TeX\ allows `|others|:' as a default label,
+and other \PASCAL s allow syntaxes like `\ignorespaces|else|\unskip' or
+`\&{otherwise}' or `\\{otherwise}:', etc. The definitions of |othercases|
+and |endcases| should be changed to agree with local conventions. (Of
+course, if no default mechanism is available, the |case| statements of
+this program must be extended by listing all remaining cases. The author
+would have taken the trouble to modify \.{TANGLE} so that such extensions
+were done automatically, if he had not wanted to encourage \PASCAL\
+compiler writers to make this important change in \PASCAL, where it belongs.)
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The following parameters are set big enough to handle \TeX, so they
+should be sufficient for most applications of \.{TANGLE}.
+
+@<Constants...@>=
+@!buf_size=100; {maximum length of input line}
+@!max_bytes=45000; {|1/ww| times the number of bytes in identifiers,
+ strings, and module names; must be less than 65536}
+@!max_toks=50000; {|1/zz| times the number of bytes in compressed \PASCAL\ code;
+ must be less than 65536}
+@!max_names=4000; {number of identifiers, strings, module names;
+ must be less than 10240}
+@!max_texts=2000; {number of replacement texts, must be less than 10240}
+@!hash_size=353; {should be prime}
+@!longest_name=400; {module names shouldn't be longer than this}
+@!line_length=72; {lines of \PASCAL\ output have at most this many characters}
+@!out_buf_size=144; {length of output buffer, should be twice |line_length|}
+@!stack_size=50; {number of simultaneous levels of macro expansion}
+@!max_id_length=12; {long identifiers are chopped to this length, which must
+ not exceed |line_length|}
+@!unambig_length=7; {identifiers must be unique if chopped to this length}
+ {note that 7 is more strict than \PASCAL's 8, but this can be varied}
+
+@ A global variable called |history| will contain one of four values
+at the end of every run: |spotless| means that no unusual messages were
+printed; |harmless_message| means that a message of possible interest
+was printed but no serious errors were detected; |error_message| means that
+at least one error was found; |fatal_message| means that the program
+terminated abnormally. The value of |history| does not influence the
+behavior of the program; it is simply computed for the convenience
+of systems that might want to use such information.
+
+@d spotless=0 {|history| value for normal jobs}
+@d harmless_message=1 {|history| value when non-serious info was printed}
+@d error_message=2 {|history| value when an error was noted}
+@d fatal_message=3 {|history| value when we had to stop prematurely}
+@#
+@d mark_harmless==@t@>@+if history=spotless then history:=harmless_message
+@d mark_error==history:=error_message
+@d mark_fatal==history:=fatal_message
+
+@<Glob...@>=@!history:spotless..fatal_message; {how bad was this run?}
+
+@ @<Set init...@>=history:=spotless;
+
+@* The character set.
+One of the main goals in the design of \.{WEB} has been to make it readily
+portable between a wide variety of computers. Yet \.{WEB} by its very
+nature must use a greater variety of characters than most computer
+programs deal with, and character encoding is one of the areas in which
+existing machines differ most widely from each other.
+
+To resolve this problem, all input to \.{WEAVE} and \.{TANGLE} is converted
+to an internal eight-bit code that is essentially standard ASCII, the ``American
+Standard Code for Information Interchange.'' The conversion is done
+immediately when each character is read in. Conversely, characters are
+converted from ASCII to the user's external representation just before
+they are output. (The original ASCII code was seven bits only; \.{WEB} now
+allows eight bits in an attempt to keep up with modern times.)
+
+Such an internal code is relevant to users of \.{WEB} only because it is
+the code used for preprocessed constants like \.{"A"}. If you are writing
+a program in \.{WEB} that makes use of such one-character constants, you
+should convert your input to ASCII form, like \.{WEAVE} and \.{TANGLE} do.
+Otherwise \.{WEB}'s internal coding scheme does not affect you.
+@^ASCII code@>
+
+Here is a table of the standard visible ASCII codes:
+$$\def\:{\char\count255\global\advance\count255 by 1}
+\count255='40
+\vbox{
+\hbox{\hbox to 40pt{\it\hfill0\/\hfill}%
+\hbox to 40pt{\it\hfill1\/\hfill}%
+\hbox to 40pt{\it\hfill2\/\hfill}%
+\hbox to 40pt{\it\hfill3\/\hfill}%
+\hbox to 40pt{\it\hfill4\/\hfill}%
+\hbox to 40pt{\it\hfill5\/\hfill}%
+\hbox to 40pt{\it\hfill6\/\hfill}%
+\hbox to 40pt{\it\hfill7\/\hfill}}
+\vskip 4pt
+\hrule
+\def\^{\vrule height 10.5pt depth 4.5pt}
+\halign{\hbox to 0pt{\hskip -24pt\O{#0}\hfill}&\^
+\hbox to 40pt{\tt\hfill#\hfill\^}&
+&\hbox to 40pt{\tt\hfill#\hfill\^}\cr
+04&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+05&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+06&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+07&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+10&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+11&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+12&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+13&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+14&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+15&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+16&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+17&\:&\:&\:&\:&\:&\:&\:\cr}
+\hrule width 280pt}$$
+(Actually, of course, code @'040 is an invisible blank space.) Code @'136
+was once an upward arrow (\.{\char'13}), and code @'137 was
+once a left arrow (\.^^X), in olden times when the first draft
+of ASCII code was prepared; but \.{WEB} works with today's standard
+ASCII in which those codes represent circumflex and underline as shown.
+
+@<Types...@>=
+@!ASCII_code=0..255; {eight-bit numbers, a subrange of the integers}
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lowercase
+letters. Nowadays, of course, we need to deal with both capital and small
+letters in a convenient way, so \.{WEB} assumes that it is being used
+with a \PASCAL\ whose character set contains at least the characters of
+standard ASCII as listed above. Some \PASCAL\ compilers use the original
+name |char| for the data type associated with the characters in text files,
+while other \PASCAL s consider |char| to be a 64-element subrange of a larger
+data type that has some other name.
+
+In order to accommodate this difference, we shall use the name |text_char|
+to stand for the data type of the characters in the input and output
+files. We shall also assume that |text_char| consists of the elements
+|chr(first_text_char)| through |chr(last_text_char)|, inclusive. The
+following definitions should be adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+
+@ The \.{WEAVE} and \.{TANGLE} 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...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+
+@ If we assume that every system using \.{WEB} is able to read and write the
+visible characters of standard ASCII (although not necessarily using the
+ASCII codes to represent them), the following assignment statements initialize
+most of the |xchr| array properly, without needing any system-dependent
+changes. For example, the statement \.{xchr[@@\'101]:=\'A\'} that appears
+in the present \.{WEB} file might be encoded in, say, {\mc EBCDIC} code
+on the external medium on which it resides, but \.{TANGLE} will convert from
+this external code to ASCII and back again. Therefore the assignment
+statement \.{XCHR[65]:=\'A\'} will appear in the corresponding \PASCAL\ file,
+and \PASCAL\ will compile this statement so that |xchr[65]| receives the
+character \.A in the external (|char|) code. Note that it would be quite
+incorrect to say \.{xchr[@@\'101]:="A"}, because |"A"| is a constant of
+type |integer|, not |char|, and because we have $|"A"|=65$ regardless of
+the external character set.
+
+@<Set init...@>=
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';@/
+xchr[0]:=' '; xchr[@'177]:=' '; {these ASCII codes are not used}
+
+@ Some of the ASCII codes below @'40 have been given symbolic names in
+\.{WEAVE} and \.{TANGLE} because they are used with a special meaning.
+
+@d and_sign=@'4 {equivalent to `\.{and}'}
+@d not_sign=@'5 {equivalent to `\.{not}'}
+@d set_element_sign=@'6 {equivalent to `\.{in}'}
+@d tab_mark=@'11 {ASCII code used as tab-skip}
+@d line_feed=@'12 {ASCII code thrown away at end of line}
+@d form_feed=@'14 {ASCII code used at end of page}
+@d carriage_return=@'15 {ASCII code used at end of line}
+@d left_arrow=@'30 {equivalent to `\.{:=}'}
+@d not_equal=@'32 {equivalent to `\.{<>}'}
+@d less_or_equal=@'34 {equivalent to `\.{<=}'}
+@d greater_or_equal=@'35 {equivalent to `\.{>=}'}
+@d equivalence_sign=@'36 {equivalent to `\.{==}'}
+@d or_sign=@'37 {equivalent to `\.{or}'}
+
+@ 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 init...@>=
+@!i:0..255;
+
+@ Here now is the system-dependent part of the character set.
+If \.{WEB} is being implemented on a garden-variety \PASCAL\ for which
+only standard ASCII codes will appear in the input and output files, you
+don't need to make any changes here. But if you have, for example, an extended
+character set like the one in Appendix~C of {\sl The \TeX book}, the first
+line of code in this module should be changed to
+$$\hbox{|for i:=1 to @'37 do xchr[i]:=chr(i);|}$$
+\.{WEB}'s character set is essentially identical to \TeX's, even with respect to
+characters less than @'40.
+@^system dependencies@>
+
+Changes to the present module will make \.{WEB} more friendly on computers
+that have an extended character set, so that one can type things like
+\.^^Z\ instead of \.{<>}. If you have an extended set of characters that
+are easily incorporated into text files, you can assign codes arbitrarily
+here, giving an |xchr| equivalent to whatever characters the users of
+\.{WEB} are allowed to have in their input files, provided that unsuitable
+characters do not correspond to special codes like |carriage_return|
+that are listed above.
+
+(The present file \.{TANGLE.WEB} does not contain any of the non-ASCII
+characters, because it is intended to be used with all implementations of
+\.{WEB}. It 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 init...@>=
+for i:=1 to @'37 do xchr[i]:=' ';
+for i:=@'200 to @'377 do xchr[i]:=' ';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=" ";
+for i:=1 to @'377 do xord[xchr[i]]:=i;
+xord[' ']:=" ";
+
+@* 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 \.{WEAVE}.
+Therefore people who need to make modifications to all three systems
+should be able to do so without too many headaches.
+
+We use the standard \PASCAL\ input/output procedures in several places that
+\TeX\ cannot, since \.{TANGLE} does not have to deal with files that are named
+dynamically by the user, and since there is no input from the terminal.
+
+@ Terminal output is done by writing on file |term_out|, which is assumed to
+consist of characters of type |text_char|:
+@^system dependencies@>
+
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@d print_ln(#)==write_ln(term_out,#) {`|print|' and then start new line}
+@d new_line==write_ln(term_out) {start new line}
+@d print_nl(#)== {print information starting on a new line}
+ begin new_line; print(#);
+ end
+
+@<Globals...@>=
+@!term_out:text_file; {the terminal as an output file}
+
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal. Here is one way to do this
+on the \PASCAL\ system that was used in \.{TANGLE}'s initial development:
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
+
+@ The |update_terminal| procedure is called when we want
+to make sure that everything we have output to the terminal so far has
+actually left the computer's internal buffers and been sent.
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+
+@ The main input comes from |web_file|; this input may be overridden
+by changes in |change_file|. (If |change_file| is empty, there are no changes.)
+
+@<Globals...@>=
+@!web_file:text_file; {primary input}
+@!change_file:text_file; {updates}
+
+@ The following code opens the input files. Since these files were listed
+in the program header, we assume that the \PASCAL\ runtime system has
+already checked that suitable file names have been given; therefore no
+additional error checking needs to be done.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read |web_file| and |change_file|}
+begin reset(web_file); reset(change_file);
+end;
+
+@ The main output goes to |Pascal_file|, and string pool constants are
+written to the |pool| file.
+
+@<Globals...@>=
+@!Pascal_file: text_file;
+@!pool: text_file;
+
+@ The following code opens |Pascal_file| and |pool|.
+Since these files were listed in the program header, we assume that the
+\PASCAL\ runtime system has checked that suitable external file names have
+been given.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(Pascal_file); rewrite(pool);
+
+@ Input goes into an array called |buffer|.
+
+@<Globals...@>=@!buffer: array[0..buf_size] of ASCII_code;
+
+@ The |input_ln| procedure brings the next line of input from the specified
+file into the |buffer| array and returns the value |true|, unless the file has
+already been entirely read, in which case it returns |false|. The conventions
+of \TeX\ are followed; i.e., |ASCII_code| numbers representing the next line
+of the file are input into |buffer[0]|, |buffer[1]|, \dots,
+|buffer[limit-1]|; trailing blanks are ignored;
+and the global variable |limit| is set to the length of the
+@^system dependencies@>
+line. The value of |limit| must be strictly less than |buf_size|.
+
+We assume that none of the |ASCII_code| values
+of |buffer[j]| for |0<=j<limit| is equal to 0, @'177, |line_feed|, |form_feed|,
+or |carriage_return|.
+
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+var final_limit:0..buf_size; {|limit| without trailing blanks}
+begin limit:=0; final_limit:=0;
+if eof(f) then input_ln:=false
+else begin while not eoln(f) do
+ begin buffer[limit]:=xord[f^]; get(f);
+ incr(limit);
+ if buffer[limit-1]<>" " then final_limit:=limit;
+ if limit=buf_size then
+ begin while not eoln(f) do get(f);
+ decr(limit); {keep |buffer[buf_size]| empty}
+ if final_limit>limit then final_limit:=limit;
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ end;
+ read_ln(f); limit:=final_limit; input_ln:=true;
+ end;
+end;
+
+@* Reporting errors to the user.
+The \.{TANGLE} processor operates in two phases: first it inputs the source
+file and stores a compressed representation of the program, then it produces
+the \PASCAL\ output from the compressed representation.
+
+The global variable |phase_one| tells whether we are in Phase I or not.
+
+@<Globals...@>=
+@!phase_one: boolean; {|true| in Phase I, |false| in Phase II}
+
+@ If an error is detected while we are debugging,
+we usually want to look at the contents of memory.
+A special procedure will be declared later for this purpose.
+
+@<Error handling...@>=
+@!debug @+ procedure debug_help; forward;@+ gubed
+
+@ During the first phase, syntax errors are reported to the user by saying
+$$\hbox{`|err_print('! Error message')|'},$$
+followed by `|jump_out|' if no recovery from the error is provided.
+This will print the error message followed by an indication of where the error
+was spotted in the source file. Note that no period follows the error message,
+since the error routine will automatically supply a period.
+
+Errors that are noticed during the second phase are reported to the user
+in the same fashion, but the error message will be
+followed by an indication of where the error was spotted in the output file.
+
+The actual error indications are provided by a procedure called |error|.
+
+@d err_print(#)==begin new_line; print(#); error;
+ end
+
+@<Error handling...@>=
+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 if phase_one then @<Print error location based on input buffer@>
+else @<Print error location based on output buffer@>;
+update_terminal; mark_error;
+@!debug debug_skipped:=debug_cycle; debug_help;@+gubed
+end;
+
+@ The error locations during Phase I can be indicated by using the global
+variables |loc|, |line|, and |changing|, which tell respectively the first
+unlooked-at position in |buffer|, the current line number, and whether or not
+the current line is from |change_file| or |web_file|.
+This routine should be modified on systems whose standard text editor
+has special line-numbering conventions.
+@^system dependencies@>
+
+@<Print error location based on input buffer@>=
+begin if changing then print('. (change file ')@+else print('. (');
+print_ln('l.', 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}
+print(' '); {this space separates the message from future asterisks}
+end
+
+@ The position of errors detected during the second phase can be indicated
+by outputting the partially-filled output buffer, which contains |out_ptr|
+entries.
+
+@<Print error location based on output...@>=
+begin print_ln('. (l.',line:1,')');
+for j:=1 to out_ptr do print(xchr[out_buf[j-1]]); {print current partial line}
+print('... '); {indicate that this information is partial}
+end
+
+@ The |jump_out| procedure just cuts across all active procedure levels
+and jumps out of the program. This is the only non-local |goto| statement
+in \.{TANGLE}. It is used when no recovery from a particular error has
+been provided.
+
+Some \PASCAL\ compilers do not implement non-local |goto| statements.
+@^system dependencies@>
+In such cases the code that appears at label |end_of_TANGLE| should be
+copied into the |jump_out| procedure, followed by a call to a system procedure
+that terminates the program.
+
+@d fatal_error(#)==begin new_line; print(#); error; mark_fatal; jump_out;
+ end
+
+@<Error handling...@>=
+procedure jump_out;
+begin goto end_of_TANGLE;
+end;
+
+@ Sometimes the program's behavior is far different from what it should be,
+and \.{TANGLE} prints an error message that is really for the \.{TANGLE}
+maintenance person, not the user. In such cases the program says
+|confusion('indication of where we are')|.
+
+@d confusion(#)==fatal_error('! This can''t happen (',#,')')
+@.This can't happen@>
+
+@ An overflow stop occurs if \.{TANGLE}'s tables aren't large enough.
+
+@d overflow(#)==fatal_error('! Sorry, ',#,' capacity exceeded')
+@.Sorry, x capacity exceeded@>
+
+
+@* Data structures.
+Most of the user's \PASCAL\ code is packed into eight-bit integers
+in two large arrays called |byte_mem| and |tok_mem|.
+The |byte_mem| array holds the names of identifiers, strings, and modules;
+the |tok_mem| array holds the replacement texts
+for macros and modules. Allocation is sequential, since things are deleted only
+during Phase II, and only in a last-in-first-out manner.
+
+Auxiliary arrays |byte_start| and |tok_start| are used as directories to
+|byte_mem| and |tok_mem|, and the |link|, |ilk|, |equiv|, and |text_link|
+arrays give further information about names. These auxiliary arrays
+consist of sixteen-bit items.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!sixteen_bits=0..65535; {unsigned two-byte quantity}
+
+@ \.{TANGLE} has been designed to avoid the need for indices that are more
+than sixteen bits wide, so that it can be used on most computers. But
+there are programs that need more than 65536 tokens, and some programs
+even need more than 65536 bytes; \TeX\ is one of these. To get around
+this problem, a slight complication has been added to the data structures:
+|byte_mem| and |tok_mem| are two-dimensional arrays, whose first index is
+either 0 or 1. (For generality, the first index is actually allowed to run
+between 0 and |ww-1| in |byte_mem|, or between 0 and |zz-1| in |tok_mem|,
+where |ww| and |zz| are set to 2 and~3; the program will work for any
+positive values of |ww| and |zz|, and it can be simplified in obvious ways
+if |ww=1| or |zz=1|.)
+
+@d ww=2 {we multiply the byte capacity by approximately this amount}
+@d zz=3 {we multiply the token capacity by approximately this amount}
+
+@<Globals...@>=
+@!byte_mem: packed array [0..ww-1,0..max_bytes] of ASCII_code;
+ {characters of names}
+@!tok_mem: packed array [0..zz-1,0..max_toks] of eight_bits; {tokens}
+@!byte_start: array [0..max_names] of sixteen_bits; {directory into |byte_mem|}
+@!tok_start: array [0..max_texts] of sixteen_bits; {directory into |tok_mem|}
+@!link: array [0..max_names] of sixteen_bits; {hash table or tree links}
+@!ilk: array [0..max_names] of sixteen_bits; {type codes or tree links}
+@!equiv: array [0..max_names] of sixteen_bits; {info corresponding to names}
+@!text_link: array [0..max_texts] of sixteen_bits; {relates replacement texts}
+
+@ The names of identifiers are found by computing a hash address |h| and
+then looking at strings of bytes signified by |hash[h]|, |link[hash[h]]|,
+|link[link[hash[h]]]|, \dots, until either finding the desired name
+or encountering a zero.
+
+A `|name_pointer|' variable, which signifies a name, is an index into
+|byte_start|. The actual sequence of characters in the name pointed to by
+|p| appears in positions |byte_start[p]| to |byte_start[p+ww]-1|, inclusive,
+in the segment of |byte_mem| whose first index is |p mod ww|. Thus, when
+|ww=2| the even-numbered name bytes appear in |byte_mem[0,@t$*$@>]|
+and the odd-numbered ones appear in |byte_mem[1,@t$*$@>]|.
+The pointer 0 is used for undefined module names; we don't
+want to use it for the names of identifiers, since 0 stands for a null
+pointer in a linked list.
+
+Strings are treated like identifiers; the first character (a double-quote)
+distinguishes a string from an alphabetic name, but for \.{TANGLE}'s purposes
+strings behave like numeric macros. (A `string' here refers to the
+strings delimited by double-quotes that \.{TANGLE} processes. \PASCAL\
+string constants delimited by single-quote marks are not given such special
+treatment; they simply appear as sequences of characters in the \PASCAL\
+texts.) The total number of strings in the string
+pool is called |string_ptr|, and the total number of names in |byte_mem|
+is called |name_ptr|. The total number of bytes occupied in
+|byte_mem[w,@t$*$@>]| is called |byte_ptr[w]|.
+
+We usually have |byte_start[name_ptr+w]=byte_ptr[(name_ptr+w) mod ww]|
+for |0<=w<ww|, since these are the starting positions for the next |ww|
+names to be stored in |byte_mem|.
+
+@d length(#)==byte_start[#+ww]-byte_start[#] {the length of a name}
+
+@<Types...@>=
+@!name_pointer=0..max_names; {identifies a name}
+
+@ @<Global...@>=
+@!name_ptr:name_pointer; {first unused position in |byte_start|}
+@!string_ptr:name_pointer; {next number to be given to a string of length |<>1|}
+@!byte_ptr:array [0..ww-1] of 0..max_bytes;
+ {first unused position in |byte_mem|}
+@!pool_check_sum:integer; {sort of a hash for the whole string pool}
+
+@ @<Local variables for init...@>=
+@!wi: 0..ww-1; {to initialize the |byte_mem| indices}
+
+@ @<Set init...@>=
+for wi:=0 to ww-1 do
+ begin byte_start[wi]:=0; byte_ptr[wi]:=0;
+ end;
+byte_start[ww]:=0; {this makes name 0 of length zero}
+name_ptr:=1; string_ptr:=256; pool_check_sum:=271828;
+
+@ Replacement texts are stored in |tok_mem|, using similar conventions.
+A `|text_pointer|' variable is an index into |tok_start|, and the
+replacement text that corresponds to |p| runs from positions
+|tok_start[p]| to |tok_start[p+zz]-1|, inclusive, in the segment of
+|tok_mem| whose first index is |p mod zz|. Thus, when |zz=2| the
+even-numbered replacement texts appear in |tok_mem[0,@t$*$@>]| and the
+odd-numbered ones appear in |tok_mem[1,@t$*$@>]|. Furthermore,
+|text_link[p]| is used to connect pieces of text that have the same name,
+as we shall see later. The pointer 0 is used for undefined replacement
+texts.
+
+The first position of |tok_mem[z,@t$*$@>]| that is unoccupied by
+replacement text is called |tok_ptr[z]|, and the first unused location of
+|tok_start| is called |text_ptr|. We usually have the identity
+|tok_start[text_ptr+z]=tok_ptr[(text_ptr+z) mod zz]|, for |0<=z<zz|, since
+these are the starting positions for the next |zz| replacement texts to
+be stored in |tok_mem|.
+
+@<Types...@>=
+@!text_pointer=0..max_texts; {identifies a replacement text}
+
+@ It is convenient to maintain a variable |z| that is equal to |text_ptr
+mod zz|, so that we always insert tokens into segment |z| of |tok_mem|.
+
+@<Glob...@>=
+@t\hskip1em@>@!text_ptr:text_pointer; {first unused position in |tok_start|}
+@t\hskip1em@>@!tok_ptr:array[0..zz-1] of 0..max_toks;
+ {first unused position in a given segment of |tok_mem|}
+@t\hskip1em@>@!z:0..zz-1; {current segment of |tok_mem|}
+stat @!max_tok_ptr:array[0..zz-1] of 0..max_toks;
+ {largest values assumed by |tok_ptr|}
+tats
+
+@ @<Local variables for init...@>=
+@!zi:0..zz-1; {to initialize the |tok_mem| indices}
+
+@ @<Set init...@>=
+for zi:=0 to zz-1 do
+ begin tok_start[zi]:=0; tok_ptr[zi]:=0;
+ end;
+tok_start[zz]:=0; {this makes replacement text 0 of length zero}
+text_ptr:=1; z:=1 mod zz;
+
+@ Four types of identifiers are distinguished by their |ilk|:
+
+\yskip\hang |normal| identifiers will appear in the \PASCAL\ program as
+ordinary identifiers since they have not been defined to be macros; the
+corresponding value in the |equiv| array
+for such identifiers is a link in a secondary hash table that
+is used to check whether any two of them agree in their first |unambig_length|
+characters after underline symbols are removed and lowercase letters are
+changed to uppercase.
+
+\yskip\hang |numeric| identifiers have been defined to be numeric macros;
+their |equiv| value contains the corresponding numeric value plus $2^{15}$.
+Strings are treated as numeric macros.
+
+\yskip\hang |simple| identifiers have been defined to be simple macros;
+their |equiv| value points to the corresponding replacement text.
+
+\yskip\hang |parametric| identifiers have been defined to be parametric macros;
+like simple identifiers, their |equiv| value points to the replacement text.
+
+@d normal=0 {ordinary identifiers have |normal| ilk}
+@d numeric=1 {numeric macros and strings have |numeric| ilk}
+@d simple=2 {simple macros have |simple| ilk}
+@d parametric=3 {parametric macros have |parametric| ilk}
+
+@ The names of modules are stored in |byte_mem| together
+with the identifier names, but a hash table is not used for them because
+\.{TANGLE} needs to be able to recognize a module name when given a prefix of
+that name. A conventional binary seach tree is used to retrieve module names,
+with fields called |llink| and |rlink| in place of |link| and |ilk|. The
+root of this tree is |rlink[0]|. If |p| is a pointer to a module name,
+|equiv[p]| points to its replacement text, just as in simple and parametric
+macros, unless this replacement text has not yet been defined (in which case
+|equiv[p]=0|).
+
+@d llink==link {left link in binary search tree for module names}
+@d rlink==ilk {right link in binary search tree for module names}
+
+@<Set init...@>=
+rlink[0]:=0; {the binary search tree starts out with nothing in it}
+equiv[0]:=0; {the undefined module has no replacement text}
+
+@ Here is a little procedure that prints the text of a given name.
+
+@p procedure print_id(@!p:name_pointer); {print identifier or module name}
+var k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {segment of |byte_mem|}
+begin if p>=name_ptr then print('IMPOSSIBLE')
+else begin w:=p mod ww;
+ for k:=byte_start[p] to byte_start[p+ww]-1 do print(xchr[byte_mem[w,k]]);
+ end;
+end;
+
+@* Searching for identifiers.
+The hash table described above is updated by the |id_lookup| procedure,
+which finds a given identifier and returns a pointer to its index in
+|byte_start|. If the identifier was not already present, it is inserted with
+a given |ilk| code; and an error message is printed if the identifier is being
+doubly defined.
+
+Because of the way \.{TANGLE}'s scanning mechanism works, it is most convenient
+to let |id_lookup| search for an identifier that is present in the |buffer|
+array. Two other global variables specify its position in the buffer: the
+first character is |buffer[id_first]|, and the last is |buffer[id_loc-1]|.
+Furthermore, if the identifier is really a string, the global variable
+|double_chars| tells how many of the characters in the buffer appear
+twice (namely \.{@@@@} and \.{""}), since this additional information makes
+it easy to calculate the true length of the string. The final double-quote
+of the string is not included in its ``identifier,'' but the first one is,
+so the string length is |id_loc-id_first-double_chars-1|.
+
+We have mentioned that |normal| identifiers belong to two hash tables,
+one for their true names as they appear in the \.{WEB} file and the other
+when they have been reduced to their first |unambig_length| characters.
+The hash tables are kept by the method of simple chaining, where the
+heads of the individual lists appear in the |hash| and |chop_hash| arrays.
+If |h| is a hash code, the primary hash table list starts at |hash[h]| and
+proceeds through |link| pointers; the secondary hash table list starts at
+|chop_hash[h]| and proceeds through |equiv| pointers. Of course, the same
+identifier will probably have two different values of |h|.
+
+The |id_lookup| procedure uses an auxiliary array called |chopped_id| to
+contain up to |unambig_length| characters of the current identifier, if
+it is necessary to compute the secondary hash code. (This array could be
+declared local to |id_lookup|, but in general we are making all array
+declarations global in this program, because some compilers and some machine
+architectures make dynamic array allocation inefficient.)
+
+@<Glob...@>=
+@!id_first:0..buf_size; {where the current identifier begins in the buffer}
+@!id_loc:0..buf_size; {just after the current identifier in the buffer}
+@!double_chars:0..buf_size; {correction to length in case of strings}
+@#
+@!hash,@!chop_hash:array [0..hash_size] of sixteen_bits; {heads of hash lists}
+@!chopped_id:array [0..unambig_length] of ASCII_code; {chopped identifier}
+
+@ Initially all the hash lists are empty.
+
+@<Local variables for init...@>=
+@!h:0..hash_size; {index into hash-head arrays}
+
+@ @<Set init...@>=
+for h:=0 to hash_size-1 do
+ begin hash[h]:=0; chop_hash[h]:=0;
+ end;
+
+@ Here now is the main procedure for finding identifiers (and strings).
+The parameter |t| is set to |normal| except when the identifier is
+a macro name that is just being defined; in the latter case, |t| will be
+|numeric|, |simple|, or |parametric|.
+
+@p function id_lookup(@!t:eight_bits):name_pointer; {finds current identifier}
+label found, not_found;
+var c:eight_bits; {byte being chopped}
+@!i:0..buf_size; {index into |buffer|}
+@!h:0..hash_size; {hash code}
+@!k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {segment of |byte_mem|}
+@!l:0..buf_size; {length of the given identifier}
+@!p,@!q:name_pointer; {where the identifier is being sought}
+@!s:0..unambig_length; {index into |chopped_id|}
+begin l:=id_loc-id_first; {compute the length}
+@<Compute the hash code |h|@>;
+@<Compute the name location |p|@>;
+if (p=name_ptr)or(t<>normal) then
+ @<Update the tables and check for possible errors@>;
+id_lookup:=p;
+end;
+
+@ A simple hash code is used: If the sequence of
+ASCII codes is $c_1c_2\ldots c_m$, its hash value will be
+$$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,|hash_size|.$$
+
+@<Compute the hash...@>=
+h:=buffer[id_first]; i:=id_first+1;
+while i<id_loc do
+ begin h:=(h+h+buffer[i]) mod hash_size; incr(i);
+ end
+
+@ If the identifier is new, it will be placed in position |p=name_ptr|,
+otherwise |p| will point to its existing location.
+
+@<Compute the name location...@>=
+p:=hash[h];
+while p<>0 do
+ begin if length(p)=l then
+ @<Compare name |p| with current identifier, |goto found| if equal@>;
+ p:=link[p];
+ end;
+p:=name_ptr; {the current identifier is new}
+link[p]:=hash[h]; hash[h]:=p; {insert |p| at beginning of hash list}
+found:
+
+@ @<Compare name |p|...@>=
+begin i:=id_first; k:=byte_start[p]; w:=p mod ww;
+while (i<id_loc)and(buffer[i]=byte_mem[w,k]) do
+ begin incr(i); incr(k);
+ end;
+if i=id_loc then goto found; {all characters agree}
+end
+
+@ @<Update the tables...@>=
+begin if ((p<>name_ptr)and(t<>normal)and(ilk[p]=normal)) or
+ ((p=name_ptr)and(t=normal)and(buffer[id_first]<>"""")) then
+ @<Compute the secondary hash code |h| and put the first characters
+ into the auxiliary array |chopped_id|@>;
+if p<>name_ptr then
+ @<Give double-definition error, if necessary, and change |p| to type |t|@>
+else @<Enter a new identifier into the table at position |p|@>;
+end
+
+@ The following routine, which is called into play when it is necessary to
+look at the secondary hash table, computes the same hash function as before
+(but on the chopped data), and places a zero after the chopped identifier
+in |chopped_id| to serve as a convenient sentinel.
+
+@<Compute the secondary...@>=
+begin i:=id_first; s:=0; h:=0;
+while (i<id_loc)and(s<unambig_length) do
+ begin if buffer[i]<>"_" then
+ begin if buffer[i]>="a" then chopped_id[s]:=buffer[i]-@'40
+ else chopped_id[s]:=buffer[i];
+ h:=(h+h+chopped_id[s]) mod hash_size; incr(s);
+ end;
+ incr(i);
+ end;
+chopped_id[s]:=0;
+end
+
+@ If a nonnumeric macro has appeared before it was defined, \.{TANGLE}
+will still work all right; after all, such behavior is typical of the
+replacement texts for modules, which act very much like macros.
+However, an undefined numeric macro may not be used on the right-hand
+side of another numeric macro definition, so \.{TANGLE} finds it
+simplest to make a blanket rule that numeric macros should be defined
+before they are used. The following routine gives an error message and
+also fixes up any damage that may have been caused.
+
+@<Give double...@>= {now |p<>name_ptr| and |t<>normal|}
+begin if ilk[p]=normal then
+ begin if t=numeric then err_print('! This identifier has already appeared');
+@.This identifier has already...@>
+ @<Remove |p| from secondary hash table@>;
+ end
+else err_print('! This identifier was defined before');
+@.This identifier was defined...@>
+ilk[p]:=t;
+end
+
+@ When we have to remove a secondary hash entry, because a |normal| identifier
+is changing to another |ilk|, the hash code |h| and chopped identifier have
+already been computed.
+
+@<Remove |p| from secondary...@>=
+q:=chop_hash[h];
+if q=p then chop_hash[h]:=equiv[p]
+else begin while equiv[q]<>p do q:=equiv[q];
+ equiv[q]:=equiv[p];
+ end
+
+@ The following routine could make good use of a generalized |pack| procedure
+that puts items into just part of a packed array instead of the whole thing.
+
+@<Enter a new identifier...@>=
+begin if (t=normal)and(buffer[id_first]<>"""") then
+ @<Check for ambiguity and update secondary hash@>;
+w:=name_ptr mod ww; k:=byte_ptr[w];
+if k+l>max_bytes then overflow('byte memory');
+if name_ptr>max_names-ww then overflow('name');
+i:=id_first; {get ready to move the identifier into |byte_mem|}
+while i<id_loc do
+ begin byte_mem[w,k]:=buffer[i]; incr(k); incr(i);
+ end;
+byte_ptr[w]:=k; byte_start[name_ptr+ww]:=k; incr(name_ptr);
+if buffer[id_first]<>"""" then ilk[p]:=t
+else @<Define and output a new string of the pool@>;
+end
+
+@ @<Check for ambig...@>=
+begin q:=chop_hash[h];
+while q<>0 do
+ begin @<Check if |q| conflicts with |p|@>;
+ q:=equiv[q];
+ end;
+equiv[p]:=chop_hash[h]; chop_hash[h]:=p; {put |p| at front of secondary list}
+end
+
+@ @<Check if |q| conflicts...@>=
+begin k:=byte_start[q]; s:=0; w:=q mod ww;
+while (k<byte_start[q+ww]) and (s<unambig_length) do
+ begin c:=byte_mem[w,k];
+ if c<>"_" then
+ begin if c>="a" then c:=c-@'40; {merge lowercase with uppercase}
+ if chopped_id[s]<>c then goto not_found;
+ incr(s);
+ end;
+ incr(k);
+ end;
+if (k=byte_start[q+ww])and(chopped_id[s]<>0) then goto not_found;
+print_nl('! Identifier conflict with ');
+@.Identifier conflict...@>
+for k:=byte_start[q] to byte_start[q+ww]-1 do print(xchr[byte_mem[w,k]]);
+error; q:=0; {only one conflict will be printed, since |equiv[0]=0|}
+not_found:
+end
+
+@ We compute the string pool check sum by working modulo a prime number
+that is large but not so large that overflow might occur.
+
+@d check_sum_prime==@'3777777667 {$2^{29}-73$}
+@^preprocessed strings@>
+
+@<Define and output a new string...@>=
+begin ilk[p]:=numeric; {strings are like numeric macros}
+if l-double_chars=2 then {this string is for a single character}
+ equiv[p]:=buffer[id_first+1]+@'100000
+else begin equiv[p]:=string_ptr+@'100000;
+ l:=l-double_chars-1;
+ if l>99 then err_print('! Preprocessed string is too long');
+@.Preprocessed string is too long@>
+ incr(string_ptr);
+ write(pool,xchr["0"+l div 10],xchr["0"+l mod 10]); {output the length}
+ pool_check_sum:=pool_check_sum+pool_check_sum+l;
+ while pool_check_sum>check_sum_prime do
+ pool_check_sum:=pool_check_sum-check_sum_prime;
+ i:=id_first+1;
+ while i<id_loc do
+ begin write(pool,xchr[buffer[i]]); {output characters of string}
+ pool_check_sum:=pool_check_sum+pool_check_sum+buffer[i];
+ while pool_check_sum>check_sum_prime do
+ pool_check_sum:=pool_check_sum-check_sum_prime;
+ if (buffer[i]="""") or (buffer[i]="@@") then
+ i:=i+2 {omit second appearance of doubled character}
+ else incr(i);
+ end;
+ write_ln(pool);
+ end;
+end
+
+@* Searching for module names.
+The |mod_lookup| procedure finds the module name |mod_text[1..l]| in the
+search tree, after inserting it if necessary, and returns a pointer to
+where it was found.
+
+@<Glob...@>=
+@!mod_text:array [0..longest_name] of ASCII_code; {name being sought for}
+
+@ According to the rules of \.{WEB}, no module name
+should be a proper prefix of another, so a ``clean'' comparison should
+occur between any two names. The result of |mod_lookup| is 0 if this
+prefix condition is violated. An error message is printed when such violations
+are detected during phase two of \.{WEAVE}.
+
+@d less=0 {the first name is lexicographically less than the second}
+@d equal=1 {the first name is equal to the second}
+@d greater=2 {the first name is lexicographically greater than the second}
+@d prefix=3 {the first name is a proper prefix of the second}
+@d extension=4 {the first name is a proper extension of the second}
+
+@p function mod_lookup(@!l:sixteen_bits):name_pointer; {finds module name}
+label found;
+var c:less..extension; {comparison between two names}
+@!j:0..longest_name; {index into |mod_text|}
+@!k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {segment of |byte_mem|}
+@!p:name_pointer; {current node of the search tree}
+@!q:name_pointer; {father of node |p|}
+begin c:=greater; q:=0; p:=rlink[0]; {|rlink[0]| is the root of the tree}
+while p<>0 do
+ begin @<Set \(|c| to the result of comparing the given name to
+ name |p|@>;
+ q:=p;
+ if c=less then p:=llink[q]
+ else if c=greater then p:=rlink[q]
+ else goto found;
+ end;
+@<Enter a new module name into the tree@>;
+found: if c<>equal then
+ begin err_print('! Incompatible section names'); p:=0;
+@.Incompatible module names@>
+ end;
+mod_lookup:=p;
+end;
+
+@ @<Enter a new module name...@>=
+w:=name_ptr mod ww; k:=byte_ptr[w];
+if k+l>max_bytes then overflow('byte memory');
+if name_ptr>max_names-ww then overflow('name');
+p:=name_ptr;
+if c=less then llink[q]:=p else rlink[q]:=p;
+llink[p]:=0; rlink[p]:=0; c:=equal; equiv[p]:=0;
+for j:=1 to l do byte_mem[w,k+j-1]:=mod_text[j];
+byte_ptr[w]:=k+l; byte_start[name_ptr+ww]:=k+l; incr(name_ptr);
+
+@ @<Set \(|c|...@>=
+begin k:=byte_start[p]; w:=p mod ww; c:=equal; j:=1;
+while (k<byte_start[p+ww]) and (j<=l) and (mod_text[j]=byte_mem[w,k]) do
+ begin incr(k); incr(j);
+ end;
+if k=byte_start[p+ww] then
+ if j>l then c:=equal
+ else c:=extension
+else if j>l then c:=prefix
+else if mod_text[j]<byte_mem[w,k] then c:=less
+else c:=greater;
+end
+
+@ The |prefix_lookup| procedure is supposed to find exactly one module
+name that has |mod_text[1..l]| as a prefix. Actually the algorithm silently
+accepts also the situation that some module name is a prefix of
+|mod_text[1..l]|, because the user who painstakingly typed in more than
+necessary probably doesn't want to be told about the wasted effort.
+
+@p function prefix_lookup(@!l:sixteen_bits):name_pointer; {finds name extension}
+var c:less..extension; {comparison between two names}
+@!count:0..max_names; {the number of hits}
+@!j:0..longest_name; {index into |mod_text|}
+@!k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {segment of |byte_mem|}
+@!p:name_pointer; {current node of the search tree}
+@!q:name_pointer; {another place to resume the search after one branch is done}
+@!r:name_pointer; {extension found}
+begin q:=0; p:=rlink[0]; count:=0; r:=0; {begin search at root of tree}
+while p<>0 do
+ begin @<Set \(|c|...@>;
+ if c=less then p:=llink[p]
+ else if c=greater then p:=rlink[p]
+ else begin r:=p; incr(count); q:=rlink[p]; p:=llink[p];
+ end;
+ if p=0 then
+ begin p:=q; q:=0;
+ end;
+ end;
+if count<>1 then
+ if count=0 then err_print('! Name does not match')
+@.Name does not match@>
+ else err_print('! Ambiguous prefix');
+@.Ambiguous prefix@>
+prefix_lookup:=r; {the result will be 0 if there was no match}
+end;
+
+@* Tokens.
+Replacement texts, which represent \PASCAL\ code in a compressed format,
+appear in |tok_mem| as mentioned above. The codes in
+these texts are called `tokens'; some tokens occupy two consecutive
+eight-bit byte positions, and the others take just one byte.
+
+If $p>0$ points to a replacement text, |tok_start[p]| is the |tok_mem| position
+of the first eight-bit code of that text. If |text_link[p]=0|,
+this is the replacement text for a macro, otherwise it is the replacement
+text for a module. In the latter case |text_link[p]| is either equal to
+|module_flag|, which means that there is no further text for this module, or
+|text_link[p]| points to a
+continuation of this replacement text; such links are created when
+several modules have \PASCAL\ texts with the same name, and they also
+tie together all the \PASCAL\ texts of unnamed modules.
+The replacement text pointer for the first unnamed module
+appears in |text_link[0]|, and the most recent such pointer is |last_unnamed|.
+
+@d module_flag==max_texts {final |text_link| in module replacement texts}
+
+@<Glob...@>=
+@!last_unnamed:text_pointer; {most recent replacement text of unnamed module}
+
+@ @<Set init...@>= last_unnamed:=0; text_link[0]:=0;
+
+@ If the first byte of a token is less than @'200, the token occupies a
+single byte. Otherwise we make a sixteen-bit token by combining two consecutive
+bytes |a| and |b|. If |@'200<=a<@'250|, then $(a-@'200)\times2^8+b$ points
+to an identifier; if |@'250<=a<@'320|, then
+$(a-@'250)\times2^8+b$ points to a module name; otherwise, i.e., if
+|@'320<=a<@'400|, then $(a-@'320)\times2^8+b$ is the number of the module
+in which the current replacement text appears.
+
+Codes less than @'200 are 7-bit ASCII codes that represent themselves.
+In particular, a single-character identifier like `|x|' will be a one-byte
+token, while all longer identifiers will occupy two bytes.
+
+Some of the 7-bit ASCII codes will not be present, however, so we can
+use them for special purposes. The following symbolic names are used:
+
+\yskip\hang |param| denotes insertion of a parameter. This occurs only in
+the replacement texts of parametric macros, outside of single-quoted strings
+in those texts.
+
+\hang |begin_comment| denotes \.{@@\{}, which will become either
+\.{\{} or \.{[}.
+
+\hang |end_comment| denotes \.{@@\}}, which will become either
+\.{\}} or \.{]}.
+
+\hang |octal| denotes the \.{@@\'} that precedes an octal constant.
+
+\hang |hex| denotes the \.{@@"} that precedes a hexadecimal constant.
+
+\hang |check_sum| denotes the \.{@@\char'44} that denotes the string pool
+check sum.
+
+\hang |join| denotes the concatenation of adjacent items with no
+space or line breaks allowed between them (the \.{@@\&} operation of \.{WEB}).
+
+\hang |double_dot| denotes `\.{..}' in \PASCAL.
+
+\hang |verbatim| denotes the \.{@@=} that begins a verbatim \PASCAL\ string.
+It is also used for the end of the string.
+
+\hang |force_line| denotes the \.{@@\\} that forces a new line in the
+\PASCAL\ output.
+@^ASCII code@>
+
+@d param=0 {ASCII null code will not appear}
+@d verbatim=@'2 {extended ASCII alpha should not appear}
+@d force_line=@'3 {extended ASCII beta should not appear}
+@d begin_comment=@'11 {ASCII tab mark will not appear}
+@d end_comment=@'12 {ASCII line feed will not appear}
+@d octal=@'14 {ASCII form feed will not appear}
+@d hex=@'15 {ASCII carriage return will not appear}
+@d double_dot=@'40 {ASCII space will not appear except in strings}
+@d check_sum=@'175 {will not be confused with right brace}
+@d join=@'177 {ASCII delete will not appear}
+
+@ The following procedure is used to enter a two-byte value into
+|tok_mem| when a replacement text is being generated.
+
+@p procedure store_two_bytes(@!x:sixteen_bits);
+ {stores high byte, then low byte}
+begin if tok_ptr[z]+2>max_toks then overflow('token');
+tok_mem[z,tok_ptr[z]]:=x div@'400; {this could be done by a shift command}
+tok_mem[z,tok_ptr[z]+1]:=x mod@'400; {this could be done by a logical and}
+tok_ptr[z]:=tok_ptr[z]+2;
+end;
+
+@ When \.{TANGLE} is being operated in debug mode, it has a procedure to display
+a replacement text in symbolic form. This procedure has not been spruced up to
+generate a real great format, but at least the results are not as bad as
+a memory dump.
+
+@p @!debug procedure print_repl(@!p:text_pointer);
+var k:0..max_toks; {index into |tok_mem|}
+@!a: sixteen_bits; {current byte(s)}
+@!zp: 0..zz-1; {segment of |tok_mem| being accessed}
+begin if p>=text_ptr then print('BAD')
+else begin k:=tok_start[p]; zp:=p mod zz;
+ while k<tok_start[p+zz] do
+ begin a:=tok_mem[zp,k];
+ if a>=@'200 then @<Display two-byte token starting with |a|@>
+ else @<Display one-byte token |a|@>;
+ incr(k);
+ end;
+ end;
+end;
+gubed
+
+@ @<Display two-byte...@>=
+begin incr(k);
+if a<@'250 then {identifier or string}
+ begin a:=(a-@'200)*@'400+tok_mem[zp,k]; print_id(a);
+ if byte_mem[a mod ww,byte_start[a]]="""" then print('"')
+ else print(' ');
+ end
+else if a<@'320 then {module name}
+ begin print('@@<'); print_id((a-@'250)*@'400+tok_mem[zp,k]);
+ print('@@>');
+ end
+else begin a:=(a-@'320)*@'400+tok_mem[zp,k]; {module number}
+ print('@@',xchr["{"],a:1,'@@',xchr["}"]); {can't use right brace
+ between \&{debug} and \&{gubed}}
+ end;
+end
+
+@ @<Display one-byte...@>=
+case a of
+begin_comment: print('@@',xchr["{"]);
+end_comment: print('@@',xchr["}"]); {can't use right brace
+ between \&{debug} and \&{gubed}}
+octal: print('@@''');
+hex: print('@@"');
+check_sum: print('@@$');
+param: print('#');
+"@@": print('@@@@');
+verbatim: print('@@=');
+force_line: print('@@\');
+othercases print(xchr[a])
+endcases
+
+@* Stacks for output.
+Let's make sure that our data structures contain enough information to
+produce the entire \PASCAL\ program as desired, by working next on the
+algorithms that actually do produce that program.
+
+@ The output process uses a stack to keep track of what is going on at
+different ``levels'' as the macros are being expanded.
+Entries on this stack have five parts:
+
+\yskip\hang |end_field| is the |tok_mem| location where the replacement
+text of a particular level will end;
+
+\hang |byte_field| is the |tok_mem| location from which the next token
+on a particular level will be read;
+
+\hang |name_field| points to the name corresponding to a particular level;
+
+\hang |repl_field| points to the replacement text currently being read
+at a particular level;
+
+\hang |mod_field| is the module number, or zero if this is a macro.
+
+\yskip\noindent The current values of these five quantities are referred to
+quite frequently, so they are stored in a separate place instead of in
+the |stack| array. We call the current values |cur_end|, |cur_byte|,
+|cur_name|, |cur_repl|, and |cur_mod|.
+
+The global variable |stack_ptr| tells how many levels of output are
+currently in progress. The end of all output occurs when the stack is
+empty, i.e., when |stack_ptr=0|.
+
+@<Types...@>=
+@t\4@>@!output_state=record
+ @!end_field: sixteen_bits; {ending location of replacement text}
+ @!byte_field: sixteen_bits; {present location within replacement text}
+ @!name_field: name_pointer; {|byte_start| index for text being output}
+ @!repl_field: text_pointer; {|tok_start| index for text being output}
+ @!mod_field: 0..@'27777; {module number or zero if not a module}
+ end;
+
+@ @d cur_end==cur_state.end_field {current ending location in |tok_mem|}
+@d cur_byte==cur_state.byte_field {location of next output byte in |tok_mem|}
+@d cur_name==cur_state.name_field {pointer to current name being expanded}
+@d cur_repl==cur_state.repl_field {pointer to current replacement text}
+@d cur_mod==cur_state.mod_field {current module number being expanded}
+
+@<Globals...@>=
+@!cur_state : output_state; {|cur_end|, |cur_byte|, |cur_name|,
+ |cur_repl|, |cur_mod|}
+@!stack : array [1..stack_size] of output_state; {info for non-current levels}
+@!stack_ptr: 0..stack_size; {first unused location in the output state stack}
+
+@ It is convenient to keep a global variable |zo| equal to |cur_repl mod zz|.
+
+@<Glob...@>=
+@!zo:0..zz-1; {the segment of |tok_mem| from which output is coming}
+
+@ Parameters must also be stacked. They are placed in
+|tok_mem| just above the other replacement texts, and dummy parameter
+`names' are placed in |byte_start| just after the other names.
+The variables |text_ptr| and |tok_ptr[z]| essentially serve as parameter
+stack pointers during the output phase, so there is no need for a separate
+data structure to handle this problem.
+
+@ There is an implicit stack corresponding to meta-comments that are output
+via \.{@@\{} and \.{@@\}}. But this stack need not be represented in detail,
+because we only need to know whether it is empty or not. A global variable
+|brace_level| tells how many items would be on this stack if it were present.
+
+@<Globals...@>=
+@!brace_level: eight_bits; {current depth of $\.{@@\{}\ldots\.{@@\}}$ nesting}
+
+@ To get the output process started, we will perform the following
+initialization steps. We may assume that |text_link[0]| is nonzero, since it
+points to the \PASCAL\ text in the first unnamed module that generates
+code; if there are no such modules, there is nothing to output, and an
+error message will have been generated before we do any of the initialization.
+
+@<Initialize the output stacks@>=
+stack_ptr:=1; brace_level:=0; cur_name:=0; cur_repl:=text_link[0];
+zo:=cur_repl mod zz; cur_byte:=tok_start[cur_repl];
+cur_end:=tok_start[cur_repl+zz]; cur_mod:=0;
+
+@ When the replacement text for name |p| is to be inserted into the output,
+the following subroutine is called to save the old level of output and get
+the new one going.
+
+@p procedure push_level(@!p:name_pointer); {suspends the current level}
+begin if stack_ptr=stack_size then overflow('stack')
+else begin stack[stack_ptr]:=cur_state; {save |cur_end|, |cur_byte|, etc.}
+ incr(stack_ptr);
+ cur_name:=p; cur_repl:=equiv[p]; zo:=cur_repl mod zz;
+ cur_byte:=tok_start[cur_repl]; cur_end:=tok_start[cur_repl+zz];
+ cur_mod:=0;
+ end;
+end;
+
+@ When we come to the end of a replacement text, the |pop_level| subroutine
+does the right thing: It either moves to the continuation of this replacement
+text or returns the state to the most recently stacked level. Part of this
+subroutine, which updates the parameter stack, will be given later when we
+study the parameter stack in more detail.
+
+@p procedure pop_level; {do this when |cur_byte| reaches |cur_end|}
+label exit;
+begin if text_link[cur_repl]=0 then {end of macro expansion}
+ begin if ilk[cur_name]=parametric then
+ @<Remove a parameter from the parameter stack@>;
+ end
+else if text_link[cur_repl]<module_flag then {link to a continuation}
+ begin cur_repl:=text_link[cur_repl]; {we will stay on the same level}
+ zo:=cur_repl mod zz;
+ cur_byte:=tok_start[cur_repl]; cur_end:=tok_start[cur_repl+zz];
+ return;
+ end;
+decr(stack_ptr); {we will go down to the previous level}
+if stack_ptr>0 then
+ begin cur_state:=stack[stack_ptr]; zo:=cur_repl mod zz;
+ end;
+exit: end;
+
+@ The heart of the output procedure is the |get_output| routine, which produces
+the next token of output that is not a reference to a macro. This procedure
+handles all the stacking and unstacking that is necessary. It returns the
+value |number| if the next output has a numeric value (the value of a
+numeric macro or string), in which case |cur_val| has been set to the
+number in question. The procedure also returns the value |module_number|
+if the next output begins or ends the replacement text of some module,
+in which case |cur_val| is that module's number (if beginning) or the
+negative of that value (if ending). And it returns the value |identifier|
+if the next output is an identifier of length two or more, in which case
+|cur_val| points to that identifier name.
+
+@d number=@'200 {code returned by |get_output| when next output is numeric}
+@d module_number=@'201 {code returned by |get_output| for module numbers}
+@d identifier=@'202 {code returned by |get_output| for identifiers}
+
+@<Globals...@>=
+@!cur_val:integer; {additional information corresponding to output token}
+
+@ If |get_output| finds that no more output remains, it returns the value zero.
+
+@p function get_output:sixteen_bits; {returns next token after macro expansion}
+label restart, done, found;
+var a:sixteen_bits; {value of current byte}
+@!b:eight_bits; {byte being copied}
+@!bal:sixteen_bits; {excess of \.( versus \.) while copying a parameter}
+@!k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {segment of |byte_mem|}
+begin restart: if stack_ptr=0 then
+ begin a:=0; goto found;
+ end;
+if cur_byte=cur_end then
+ begin cur_val:=-cur_mod; pop_level;
+ if cur_val=0 then goto restart;
+ a:=module_number; goto found;
+ end;
+a:=tok_mem[zo,cur_byte]; incr(cur_byte);
+if a<@'200 then {one-byte token}
+ if a=param then
+ @<Start scanning current macro parameter, |goto restart|@>
+ else goto found;
+a:=(a-@'200)*@'400+tok_mem[zo,cur_byte]; incr(cur_byte);
+if a<@'24000 then {|@'24000=(@'250-@'200)*@'400|}
+ @<Expand macro |a| and |goto found|, or |goto restart| if no output found@>;
+if a<@'50000 then {|@'50000=(@'320-@'200)*@'400|}
+ @<Expand module |a-@'24000|, |goto restart|@>;
+cur_val:=a-@'50000; a:=module_number; cur_mod:=cur_val;
+found:
+@!debug if trouble_shooting then debug_help;@;@+gubed@/
+get_output:=a;
+end;
+
+@ The user may have forgotten to give any \PASCAL\ text for a module name,
+or the \PASCAL\ text may have been associated with a different name by mistake.
+
+@<Expand module |a-...@>=
+begin a:=a-@'24000;
+if equiv[a]<>0 then push_level(a)
+else if a<>0 then
+ begin print_nl('! Not present: <'); print_id(a); print('>'); error;
+@.Not present: <section name>@>
+ end;
+goto restart;
+end
+
+@ @<Expand macro ...@>=
+begin case ilk[a] of
+normal: begin cur_val:=a; a:=identifier;
+ end;
+numeric: begin cur_val:=equiv[a]-@'100000; a:=number;
+ end;
+simple: begin push_level(a); goto restart;
+ end;
+parametric: begin @<Put a parameter on the parameter stack,
+ or |goto restart| if error occurs@>;
+ push_level(a); goto restart;
+ end;
+othercases confusion('output')
+endcases;@/
+goto found;
+end
+
+@ We come now to the interesting part, the job of putting a parameter on
+the parameter stack. First we pop the stack if necessary until getting to
+a level that hasn't ended. Then the next character must be a `\.(';
+and since parentheses are balanced on each level, the entire parameter must
+be present, so we can copy it without difficulty.
+
+@<Put a parameter...@>=
+while (cur_byte=cur_end)and(stack_ptr>0) do pop_level;
+if (stack_ptr=0)or(tok_mem[zo,cur_byte]<>"(") then
+ begin print_nl('! No parameter given for '); print_id(a); error;
+@.No parameter given for macro@>
+ goto restart;
+ end;
+@<Copy the parameter into |tok_mem|@>;
+equiv[name_ptr]:=text_ptr; ilk[name_ptr]:=simple; w:=name_ptr mod ww;
+k:=byte_ptr[w];
+@!debug if k=max_bytes then overflow('byte memory');
+byte_mem[w,k]:="#"; incr(k); byte_ptr[w]:=k;
+gubed {this code has set the parameter identifier for debugging printouts}
+if name_ptr>max_names-ww then overflow('name');
+byte_start[name_ptr+ww]:=k; incr(name_ptr);
+if text_ptr>max_texts-zz then overflow('text');
+text_link[text_ptr]:=0; tok_start[text_ptr+zz]:=tok_ptr[z];
+incr(text_ptr);
+z:=text_ptr mod zz
+
+@ The |pop_level| routine undoes the effect of parameter-pushing when
+a parameter macro is finished:
+
+@<Remove a parameter...@>=
+begin decr(name_ptr); decr(text_ptr);
+z:=text_ptr mod zz;
+stat if tok_ptr[z]>max_tok_ptr[z] then max_tok_ptr[z]:=tok_ptr[z];
+tats {the maximum value of |tok_ptr| occurs just before parameter popping}
+tok_ptr[z]:=tok_start[text_ptr];
+@!debug decr(byte_ptr[name_ptr mod ww]);@+gubed
+end
+
+@ When a parameter occurs in a replacement text, we treat it as a simple
+macro in position (|name_ptr-1|):
+
+@<Start scanning...@>=
+begin push_level(name_ptr-1); goto restart;
+end
+
+@ Similarly, a |param| token encountered as we copy a parameter is converted
+into a simple macro call for |name_ptr-1|.
+Some care is needed to handle cases like \\{macro}|(#; print('#)'))|; the
+\.{\#} token will have been changed to |param| outside of strings, but we
+still must distinguish `real' parentheses from those in strings.
+
+@d app_repl(#)==begin if tok_ptr[z]=max_toks then overflow('token');
+ tok_mem[z,tok_ptr[z]]:=#; incr(tok_ptr[z]); end
+
+@<Copy the parameter...@>=
+bal:=1; incr(cur_byte); {skip the opening `\.('}
+loop@+ begin b:=tok_mem[zo,cur_byte]; incr(cur_byte);
+ if b=param then store_two_bytes(name_ptr+@'77777)
+ else begin if b>=@'200 then
+ begin app_repl(b);
+ b:=tok_mem[zo,cur_byte]; incr(cur_byte);
+ end
+ else case b of
+ "(": incr(bal);
+ ")": begin decr(bal);
+ if bal=0 then goto done;
+ end;
+ "'": repeat app_repl(b);
+ b:=tok_mem[zo,cur_byte]; incr(cur_byte);
+ until b="'"; {copy string, don't change |bal|}
+ othercases do_nothing
+ endcases;
+ app_repl(b);
+ end;
+ end;
+done:
+
+@* Producing the output.
+The |get_output| routine above handles most of the complexity of output
+generation, but there are two further considerations that have a nontrivial
+effect on \.{TANGLE}'s algorithms.
+
+First, we want to make sure that the output is broken into lines not
+exceeding |line_length| characters per line, where these breaks occur at
+valid places (e.g., not in the middle of a string or a constant or an
+identifier, not between `\.<' and `\.>', not at a `\.{@@\&}' position
+where quantities are being joined together). Therefore we assemble the
+output into a buffer before deciding where the line breaks will appear.
+However, we make very little attempt to make ``logical'' line breaks that
+would enhance the readability of the output; people are supposed to read
+the input of \.{TANGLE} or the \TeX ed output of \.{WEAVE}, but not the
+tangled-up output. The only concession to readability is that a break after
+a semicolon will be made if possible, since commonly used ``pretty
+printing'' routines give better results in such cases.
+
+Second, we want to decimalize non-decimal constants, and to combine integer
+quantities that are added or subtracted, because \PASCAL\ doesn't allow
+constant expressions in subrange types or in case labels. This means we
+want to have a procedure that treats a construction like \.{(E-15+17)}
+as equivalent to `\.{(E+2)}', while also leaving `\.{(1E-15+17)}' and
+`\.{(E-15+17*y)}' untouched. Consider also `\.{-15+17.5}' versus
+`\.{-15+17..5}'. We shall not combine integers preceding or following
+\.*, \./, \.{div}, \.{mod}, or \.{@@\&}. Note that if |y| has been defined
+to equal $-2$, we must expand `\.{x*y}' into `\.{x*(-2)}'; but `\.{x-y}'
+can expand into `\.{x+2}' and we can even change `\.{x - y mod z}' to
+@^mod@>
+`\.{x + 2 mod z}' because \PASCAL\ has a nonstandard \&{mod} operation!
+
+The following solution to these problems has been adopted: An array
+|out_buf| contains characters that have been generated but not yet output,
+and there are three pointers into this array. One of these, |out_ptr|, is
+the number of characters currently in the buffer, and we will have
+|1<=out_ptr<=line_length| most of the time. The second is |break_ptr|,
+which is the largest value |<=out_ptr| such that we are definitely entitled
+to end a line by outputting the characters |out_buf[1..(break_ptr-1)]|;
+we will always have |break_ptr<=line_length|. Finally, |semi_ptr| is either
+zero or the largest known value of a legal break after a semicolon or comment
+on the current line; we will always have |semi_ptr<=break_ptr|.
+
+@<Globals...@>=
+@!out_buf: array [0..out_buf_size] of ASCII_code; {assembled characters}
+@!out_ptr: 0..out_buf_size; {first available place in |out_buf|}
+@!break_ptr: 0..out_buf_size; {last breaking place in |out_buf|}
+@!semi_ptr: 0..out_buf_size; {last semicolon breaking place in |out_buf|}
+
+@ Besides having those three pointers,
+the output process is in one of several states:
+
+\yskip\hang |num_or_id| means that the last item in the buffer is a number or
+identifier, hence a blank space or line break must be inserted if the next
+item is also a number or identifier.
+
+\yskip\hang |unbreakable| means that the last item in the buffer was followed
+by the \.{@@\&} operation that inhibits spaces between it and the next item.
+
+\yskip\hang |sign| means that the last item in the buffer is to be followed
+by \.+ or \.-, depending on whether |out_app| is positive or negative.
+
+\yskip\hang |sign_val| means that the decimal equivalent of
+$\vert|out_val|\vert$ should be appended to the buffer. If |out_val<0|,
+or if |out_val=0| and |last_sign<0|, the number should be preceded by a minus
+sign. Otherwise it should be preceded by the character |out_sign| unless
+|out_sign=0|; the |out_sign| variable is either 0 or \.{"\ "} or \.{"+"}.
+
+\yskip\hang |sign_val_sign| is like |sign_val|, but also append \.+ or \.-
+afterwards, depending on whether |out_app| is positive or negative.
+
+\yskip\hang |sign_val_val| is like |sign_val|, but also append the decimal
+equivalent of |out_app| including its sign, using |last_sign| in case
+|out_app=0|.
+
+\yskip\hang |misc| means none of the above.
+
+\yskip\noindent
+For example, the output buffer and output state run through the following
+sequence as we generate characters from `\.{(x-15+19-2)}':
+$$\vbox{\halign{$\hfil#\hfil$\quad&#\hfil&\quad\hfil#\hfil&\quad
+\hfil#\hfil&\quad\hfil#\hfil&\quad\hfil#\hfil\quad&\hfil#\hfil\cr
+output&|out_buf|&|out_state|&|out_sign|&|out_val|&|out_app|&|last_sign|\cr
+\noalign{\vskip 3pt}
+(&\.(&|misc|\cr
+x&\.{(x}&|num_or_id|\cr
+-&\.{(x}&|sign|&&&$-1$&$-1$\cr
+15&\.{(x}&|sign_val|&\.{"+"}&$-15$&&$-15$\cr
++&\.{(x}&|sign_val_sign|&\.{"+"}&$-15$&$+1$&$+1$\cr
+19&\.{(x}&|sign_val_val|&\.{"+"}&$-15$&$+19$&$+1$\cr
+-&\.{(x}&|sign_val_sign|&\.{"+"}&$+4$&$-1$&$-1$\cr
+2&\.{(x}&|sign_val_val|&\.{"+"}&$+4$&$-2$&$-2$\cr
+)&\.{(x+2)}&|misc|\cr}}$$
+At each stage we have put as much into the buffer as possible without
+knowing what is coming next. Examples like `\.{x-0.1}' indicate why
+|last_sign| is needed to associate the proper sign with an output of zero.
+
+In states |num_or_id|, |unbreakable|, and |misc| the last item in the buffer
+lies between |break_ptr| and |out_ptr-1|, inclusive; in the other states we
+have |break_ptr=out_ptr|.
+
+The numeric values assigned to |num_or_id|, etc., have been chosen to
+shorten some of the program logic; for example, the program makes use of
+the fact that |sign+2=sign_val_sign|.
+
+@d misc=0 {state associated with special characters}
+@d num_or_id=1 {state associated with numbers and identifiers}
+@d sign=2 {state associated with pending \.+ or \.-}
+@d sign_val=num_or_id+2 {state associated with pending sign and value}
+@d sign_val_sign=sign+2 {|sign_val| followed by another pending sign}
+@d sign_val_val=sign_val+2 {|sign_val| followed by another pending value}
+@d unbreakable=sign_val_val+1 {state associated with \.{@@\&}}
+
+@<Globals...@>=
+@!out_state:eight_bits; {current status of partial output}
+@!out_val,@!out_app:integer; {pending values}
+@!out_sign:ASCII_code; {sign to use if appending |out_val>=0|}
+@!last_sign:-1..+1; {sign to use if appending a zero}
+
+@ During the output process, |line| will equal the number of the next line
+to be output.
+
+@<Initialize the output buffer@>=
+out_state:=misc; out_ptr:=0; break_ptr:=0; semi_ptr:=0; out_buf[0]:=0; line:=1;
+
+@ Here is a routine that is invoked when |out_ptr>line_length|
+or when it is time to flush out the final line. The |flush_buffer| procedure
+often writes out the line up to the current |break_ptr| position, then moves the
+remaining information to the front of |out_buf|. However, it prefers to
+write only up to |semi_ptr|, if the residual line won't be too long.
+
+@d check_break==if out_ptr>line_length then flush_buffer
+
+@p procedure flush_buffer; {writes one line to output file}
+var k:0..out_buf_size; {index into |out_buf|}
+@!b:0..out_buf_size; {value of |break_ptr| upon entry}
+begin b:=break_ptr;
+if (semi_ptr<>0)and(out_ptr-semi_ptr<=line_length) then break_ptr:=semi_ptr;
+for k:=1 to break_ptr do write(Pascal_file,xchr[out_buf[k-1]]);
+write_ln(Pascal_file); incr(line);
+if line mod 100 = 0 then
+ begin print('.');
+ if line mod 500 = 0 then print(line:1);
+ update_terminal; {progress report}
+ end;
+if break_ptr<out_ptr then
+ begin if out_buf[break_ptr]=" " then
+ begin incr(break_ptr); {drop space at break}
+ if break_ptr>b then b:=break_ptr;
+ end;
+ for k:=break_ptr to out_ptr-1 do out_buf[k-break_ptr]:=out_buf[k];
+ end;
+out_ptr:=out_ptr-break_ptr; break_ptr:=b-break_ptr; semi_ptr:=0;
+if out_ptr>line_length then
+ begin err_print('! Long line must be truncated'); out_ptr:=line_length;
+@.Long line must be truncated@>
+ end;
+end;
+
+@ @<Empty the last line from the buffer@>=
+break_ptr:=out_ptr; semi_ptr:=0; flush_buffer;
+if brace_level<>0 then
+ err_print('! Program ended at brace level ',brace_level:1);
+@.Program ended at brace level n@>
+
+@ Another simple and useful routine appends the decimal equivalent of
+a nonnegative integer to the output buffer.
+
+@d app(#)==begin out_buf[out_ptr]:=#; incr(out_ptr); {append a single character}
+ end
+
+@p procedure app_val(@!v:integer); {puts |v| into buffer, assumes |v>=0|}
+var k:0..out_buf_size; {index into |out_buf|}
+begin k:=out_buf_size; {first we put the digits at the very end of |out_buf|}
+repeat out_buf[k]:=v mod 10; v:=v div 10; decr(k);
+until v=0;
+repeat incr(k); app(out_buf[k]+"0");
+until k=out_buf_size; {then we append them, most significant first}
+end;
+
+@ The output states are kept up to date by the output routines, which are
+called |send_out|, |send_val|, and |send_sign|. The |send_out| procedure
+has two parameters: |t| tells the type of information being sent and
+|v| contains the information proper. Some information may also be passed
+in the array |out_contrib|.
+
+\yskip\hang If |t=misc| then |v| is a character to be output.
+
+\hang If |t=str| then |v| is the length of a string or something like `\.{<>}'
+in |out_contrib|.
+
+\hang If |t=ident| then |v| is the length of an identifier in |out_contrib|.
+
+\hang If |t=frac| then |v| is the length of a fraction and/or exponent in
+|out_contrib|.
+
+@d str=1 {|send_out| code for a string}
+@d ident=2 {|send_out| code for an identifier}
+@d frac=3 {|send_out| code for a fraction}
+
+@<Glob...@>=
+@!out_contrib:array[1..line_length] of ASCII_code; {a contribution to |out_buf|}
+
+@ A slightly subtle point in the following code is that the user may ask
+for a |join| operation (i.e., \.{@@\&}) following whatever is being sent
+out. We will see later that |join| is implemented in part by calling
+|send_out(frac,0)|.
+
+@p procedure send_out(@!t:eight_bits; @!v:sixteen_bits);
+ {outputs |v| of type |t|}
+label restart;
+var k: 0..line_length; {index into |out_contrib|}
+begin @<Get the buffer ready for appending the new information@>;
+if t<>misc then for k:=1 to v do app(out_contrib[k])
+else app(v);
+check_break;
+if (t=misc)and((v=";")or(v="}")) then
+ begin semi_ptr:=out_ptr; break_ptr:=out_ptr;
+ end;
+if t>=ident then out_state:=num_or_id {|t=ident| or |frac|}
+else out_state:=misc {|t=str| or |misc|}
+end;
+
+@ Here is where the buffer states for signs and values collapse into simpler
+states, because we are about to append something that doesn't combine with
+the previous integer constants.
+
+We use an ASCII-code trick: Since |","-1="+"| and |","+1="-"|, we have
+|","-c=@t sign of $c$@>|, when $\vert c\vert=1$.
+
+@<Get the buffer ready...@>=
+restart: case out_state of
+num_or_id: if t<>frac then
+ begin break_ptr:=out_ptr;
+ if t=ident then app(" ");
+ end;
+sign: begin app(","-out_app); check_break; break_ptr:=out_ptr;
+ end;
+sign_val,sign_val_sign: begin @<Append \(|out_val| to buffer@>;
+ out_state:=out_state-2; goto restart;
+ end;
+sign_val_val: @<Reduce |sign_val_val| to |sign_val| and |goto restart|@>;
+misc: if t<>frac then break_ptr:=out_ptr;@/
+othercases do_nothing {this is for |unbreakable| state}
+endcases
+
+@ @<Append \(|out_val|...@>=
+if (out_val<0)or((out_val=0)and(last_sign<0)) then app("-")
+else if out_sign>0 then app(out_sign);
+app_val(abs(out_val)); check_break;
+
+@ @<Reduce |sign_val_val|...@>=
+begin if (t=frac)or(@<Contribution is \.* or \./ or \.{DIV} or \.{MOD}@>) then
+ begin @<Append \(|out_val| to buffer@>;
+ out_sign:="+"; out_val:=out_app;
+ end
+else out_val:=out_val+out_app;
+out_state:=sign_val; goto restart;
+end
+
+@ @<Contribution is \.*...@>=
+((t=ident)and(v=3)and@|
+ (((out_contrib[1]="D")and(out_contrib[2]="I")and(out_contrib[3]="V")) or@|
+ ((out_contrib[1]="M")and(out_contrib[2]="O")and(out_contrib[3]="D")) ))or@|
+@^uppercase@>
+ ((t=misc)and((v="*")or(v="/")))
+
+@ The following routine is called with $v=\pm1$ when a plus or minus sign is
+appended to the output. It extends \PASCAL\ to allow repeated signs
+(e.g., `\.{--}' is equivalent to `\.+'), rather than to give an error message.
+The signs following `\.E' in real constants are treated as part of a fraction,
+so they are not seen by this routine.
+
+@p procedure send_sign(@!v:integer);
+begin case out_state of
+sign, sign_val_sign: out_app:=out_app*v;
+sign_val:begin out_app:=v; out_state:=sign_val_sign;
+ end;
+sign_val_val: begin out_val:=out_val+out_app; out_app:=v;
+ out_state:=sign_val_sign;
+ end;
+othercases begin break_ptr:=out_ptr; out_app:=v; out_state:=sign;
+ end
+endcases;@/
+last_sign:=out_app;
+end;
+
+@ When a (signed) integer value is to be output, we call |send_val|.
+
+@d bad_case=666 {this is a label used below}
+
+@p procedure send_val(@!v:integer); {output the (signed) value |v|}
+label bad_case, {go here if we can't keep |v| in the output state}
+ exit;
+begin case out_state of
+num_or_id: begin @<If previous output was \.{DIV} or \.{MOD}, |goto bad_case|@>;
+ out_sign:=" "; out_state:=sign_val; out_val:=v; break_ptr:=out_ptr;
+ last_sign:=+1;
+ end;
+misc: begin @<If previous output was \.* or \./, |goto bad_case|@>;
+ out_sign:=0; out_state:=sign_val; out_val:=v; break_ptr:=out_ptr;
+ last_sign:=+1;
+ end;
+@t\4@>@<Handle cases of |send_val| when |out_state| contains a sign@>@;
+othercases goto bad_case
+endcases;@/
+return;
+bad_case: @<Append the decimal value of |v|, with parentheses if negative@>;
+exit: end;
+
+@ @<Handle cases of |send_val|...@>=
+sign: begin out_sign:="+"; out_state:=sign_val; out_val:=out_app*v;
+ end;
+sign_val: begin out_state:=sign_val_val; out_app:=v;
+ err_print('! Two numbers occurred without a sign between them');
+ end;
+sign_val_sign: begin out_state:=sign_val_val; out_app:=out_app*v;
+ end;
+sign_val_val: begin out_val:=out_val+out_app; out_app:=v;
+ err_print('! Two numbers occurred without a sign between them');
+@.Two numbers occurred...@>
+ end;
+
+@ @<If previous output was \.*...@>=
+if (out_ptr=break_ptr+1)and((out_buf[break_ptr]="*")or(out_buf[break_ptr]="/"))
+ then goto bad_case
+
+@ @<If previous output was \.{DIV}...@>=
+if (out_ptr=break_ptr+3)or
+ ((out_ptr=break_ptr+4)and(out_buf[break_ptr]=" ")) then
+@^uppercase@>
+ if ((out_buf[out_ptr-3]="D")and(out_buf[out_ptr-2]="I")and
+ (out_buf[out_ptr-1]="V"))or @/
+ ((out_buf[out_ptr-3]="M")and(out_buf[out_ptr-2]="O")and
+ (out_buf[out_ptr-1]="D")) then@/ goto bad_case
+
+@ @<Append the decimal value...@>=
+if v>=0 then
+ begin if out_state=num_or_id then
+ begin break_ptr:=out_ptr; app(" ");
+ end;
+ app_val(v); check_break; out_state:=num_or_id;
+ end
+else begin app("("); app("-"); app_val(-v); app(")"); check_break;
+ out_state:=misc;
+ end
+
+@* The big output switch.
+To complete the output process, we need a routine that takes the results
+of |get_output| and feeds them to |send_out|, |send_val|, or |send_sign|.
+This procedure `|send_the_output|' will be invoked just once, as follows:
+
+@<Phase II: Output the contents of the compressed tables@>=
+if text_link[0]=0 then
+ begin print_nl('! No output was specified.'); mark_harmless;
+@.No output was specified@>
+ end
+else begin print_nl('Writing the output file'); update_terminal;@/
+ @<Initialize the output stacks@>;
+ @<Initialize the output buffer@>;
+ send_the_output;@/
+ @<Empty the last line...@>;
+ print_nl('Done.');
+ end
+
+@ A many-way switch is used to send the output:
+
+@d get_fraction=2 {this label is used below}
+
+@p procedure send_the_output;
+label get_fraction, {go here to finish scanning a real constant}
+ reswitch, continue;
+var cur_char:eight_bits; {the latest character received}
+ @!k:0..line_length; {index into |out_contrib|}
+ @!j:0..max_bytes; {index into |byte_mem|}
+ @!w:0..ww-1; {segment of |byte_mem|}
+ @!n:integer; {number being scanned}
+begin while stack_ptr>0 do
+ begin cur_char:=get_output;
+ reswitch: case cur_char of
+ 0: do_nothing; {this case might arise if output ends unexpectedly}
+ @t\4@>@<Cases related to identifiers@>@;
+ @t\4@>@<Cases related to constants, possibly leading to
+ |get_fraction| or |reswitch|@>@;
+ "+","-": send_sign(","-cur_char);
+ @t\4@>@<Cases like \.{<>} and \.{:=}@>@;
+ "'": @<Send a string, |goto reswitch|@>;
+ @<Other printable characters@>: send_out(misc,cur_char);
+ @t\4@>@<Cases involving \.{@@\{} and \.{@@\}}@>@;
+ join: begin send_out(frac,0); out_state:=unbreakable;
+ end;
+ verbatim: @<Send verbatim string@>;
+ force_line: @<Force a line break@>;
+ othercases err_print('! Can''t output ASCII code ',cur_char:1)
+@.Can't output ASCII code n@>
+ endcases;@/
+ goto continue;
+ get_fraction: @<Special code to finish real constants@>;
+ continue: end;
+end;
+
+@ @<Cases like \.{<>}...@>=
+and_sign: begin out_contrib[1]:="A"; out_contrib[2]:="N"; out_contrib[3]:="D";
+@^uppercase@>
+ send_out(ident,3);
+ end;
+not_sign: begin out_contrib[1]:="N"; out_contrib[2]:="O"; out_contrib[3]:="T";
+ send_out(ident,3);
+ end;
+set_element_sign: begin out_contrib[1]:="I"; out_contrib[2]:="N";
+ send_out(ident,2);
+ end;
+or_sign: begin out_contrib[1]:="O"; out_contrib[2]:="R"; send_out(ident,2);
+ end;
+left_arrow: begin out_contrib[1]:=":"; out_contrib[2]:="="; send_out(str,2);
+ end;
+not_equal: begin out_contrib[1]:="<"; out_contrib[2]:=">"; send_out(str,2);
+ end;
+less_or_equal: begin out_contrib[1]:="<"; out_contrib[2]:="="; send_out(str,2);
+ end;
+greater_or_equal: begin out_contrib[1]:=">"; out_contrib[2]:="=";
+ send_out(str,2);
+ end;
+equivalence_sign: begin out_contrib[1]:="="; out_contrib[2]:="=";
+ send_out(str,2);
+ end;
+double_dot: begin out_contrib[1]:="."; out_contrib[2]:="."; send_out(str,2);
+ end;
+
+@ Please don't ask how all of the following characters can actually get
+through \.{TANGLE} outside of strings. It seems that |""""| and |"{"|
+cannot actually occur at this point of the program, but they have
+been included just in case \.{TANGLE} changes.
+
+If \.{TANGLE} is producing code for a \PASCAL\ compiler that uses `\.{(.}'
+and `\.{.)}' instead of square brackets (e.g., on machines with {\mc EBCDIC}
+code), one should remove |"["| and |"]"| from this list and put them into
+the preceding module in the appropriate way. Similarly, some compilers
+want `\.\^' to be converted to `\.{@@}'.
+@^system dependencies@>@^EBCDIC@>
+
+@<Other printable characters@>=
+"!","""","#","$","%","&","(",")","*",",","/",":",";","<","=",">","?",
+"@@","[","\","]","^","_","`","{","|"
+
+@ Single-character identifiers represent themselves, while longer ones
+appear in |byte_mem|. All must be converted to uppercase,
+with underlines removed. Extremely long identifiers must be chopped.
+
+(Some \PASCAL\ compilers work with lowercase letters instead of
+uppercase. If this module of \.{TANGLE} is changed, it's also necessary
+to change from uppercase to lowercase in the modules that are
+listed in the index under ``uppercase''.)
+@^system dependencies@>
+@^uppercase@>
+
+@d up_to(#)==#-24,#-23,#-22,#-21,#-20,#-19,#-18,#-17,#-16,#-15,#-14,
+ #-13,#-12,#-11,#-10,#-9,#-8,#-7,#-6,#-5,#-4,#-3,#-2,#-1,#
+
+@<Cases related to identifiers@>=
+"A",up_to("Z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
+ end;
+"a",up_to("z"): begin out_contrib[1]:=cur_char-@'40; send_out(ident,1);
+ end;
+identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
+ while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
+ begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
+ if out_contrib[k]>="a" then out_contrib[k]:=out_contrib[k]-@'40
+ else if out_contrib[k]="_" then decr(k);
+ end;
+ send_out(ident,k);
+ end;
+
+@ After sending a string, we need to look ahead at the next character, in order
+to see if there were two consecutive single-quote marks. Afterwards we go to
+|reswitch| to process the next character.
+
+@<Send a string...@>=
+begin k:=1; out_contrib[1]:="'";
+repeat if k<line_length then incr(k);
+out_contrib[k]:=get_output;
+until (out_contrib[k]="'")or(stack_ptr=0);
+if k=line_length then err_print('! String too long');
+@.String too long@>
+send_out(str,k); cur_char:=get_output;
+if cur_char="'" then out_state:=unbreakable;
+goto reswitch;
+end
+
+@ Sending a verbatim string is similar, but we don't have to look ahead.
+
+@<Send verbatim string@>=
+begin k:=0;
+repeat if k<line_length then incr(k);
+out_contrib[k]:=get_output;
+until (out_contrib[k]=verbatim)or(stack_ptr=0);
+if k=line_length then err_print('! Verbatim string too long');
+@.Verbatim string too long@>
+send_out(str,k-1);
+end
+
+@ In order to encourage portable software, \.{TANGLE} complains
+if the constants get dangerously close to the largest value representable
+on a 32-bit computer ($2^{31}-1$).
+
+@d digits=="0","1","2","3","4","5","6","7","8","9"
+
+@<Cases related to constants...@>=
+digits: begin n:=0;
+ repeat cur_char:=cur_char-"0";
+ if n>=@'1463146314 then err_print('! Constant too big')
+@.Constant too big@>
+ else n:=10*n+cur_char;
+ cur_char:=get_output;
+ until (cur_char>"9")or(cur_char<"0");
+ send_val(n); k:=0;
+ if cur_char="e" then cur_char:="E";
+@^uppercase@>
+ if cur_char="E" then goto get_fraction
+ else goto reswitch;
+ end;
+check_sum: send_val(pool_check_sum);
+octal: begin n:=0; cur_char:="0";
+ repeat cur_char:=cur_char-"0";
+ if n>=@'2000000000 then err_print('! Constant too big')
+ else n:=8*n+cur_char;
+ cur_char:=get_output;
+ until (cur_char>"7")or(cur_char<"0");
+ send_val(n); goto reswitch;
+ end;
+hex: begin n:=0; cur_char:="0";
+ repeat if cur_char>="A" then cur_char:=cur_char+10-"A"
+ else cur_char:=cur_char-"0";
+ if n>=@"8000000 then err_print('! Constant too big')
+ else n:=16*n+cur_char;
+ cur_char:=get_output;
+ until (cur_char>"F")or(cur_char<"0")or@|
+ ((cur_char>"9")and(cur_char<"A"));
+ send_val(n); goto reswitch;
+ end;
+number: send_val(cur_val);
+".": begin k:=1; out_contrib[1]:="."; cur_char:=get_output;
+ if cur_char="." then
+ begin out_contrib[2]:="."; send_out(str,2);
+ end
+ else if (cur_char>="0")and(cur_char<="9") then goto get_fraction
+ else begin send_out(misc,"."); goto reswitch;
+ end;
+ end;
+
+@ The following code appears at label `|get_fraction|', when we want to
+scan to the end of a real constant. The first |k| characters of a fraction
+have already been placed in |out_contrib|, and |cur_char| is the next character.
+
+@<Special code...@>=
+repeat if k<line_length then incr(k);
+out_contrib[k]:=cur_char; cur_char:=get_output;
+if (out_contrib[k]="E")and((cur_char="+")or(cur_char="-")) then
+@^uppercase@>
+ begin if k<line_length then incr(k);
+ out_contrib[k]:=cur_char; cur_char:=get_output;
+ end
+else if cur_char="e" then cur_char:="E";
+until (cur_char<>"E")and((cur_char<"0")or(cur_char>"9"));
+if k=line_length then err_print('! Fraction too long');
+@.Fraction too long@>
+send_out(frac,k); goto reswitch
+
+@ Some \PASCAL\ compilers do not recognize comments in braces, so the
+comments must be delimited by `\.{(*}' and `\.{*)}'.
+@^system dependencies@>
+In such cases the statement `|out_contrib[1]:="{"|' that appears here should
+be replaced by `\ignorespaces|begin out_contrib[1]:="("; out_contrib[2]:="*";
+incr(k); end|', and a similar change should be made to
+`|out_contrib[k]:="}"|'.
+
+@<Cases involving \.{@@\{} and \.{@@\}}@>=
+begin_comment: begin if brace_level=0 then send_out(misc,"{")
+ else send_out(misc,"[");
+ incr(brace_level);
+ end;
+end_comment: if brace_level>0 then
+ begin decr(brace_level);
+ if brace_level=0 then send_out(misc,"}")
+ else send_out(misc,"]");
+ end
+ else err_print('! Extra @@}');
+@.Extra \AT!\}@>
+module_number: begin k:=2;
+ if brace_level=0 then out_contrib[1]:="{"
+ else out_contrib[1]:="[";
+ if cur_val<0 then
+ begin out_contrib[k]:=":"; cur_val:=-cur_val; incr(k);
+ end;
+ n:=10;
+ while cur_val>=n do n:=10*n;
+ repeat n:=n div 10;
+ out_contrib[k]:="0"+(cur_val div n); cur_val:=cur_val mod n; incr(k);
+ until n=1;
+ if out_contrib[2]<>":" then
+ begin out_contrib[k]:=":"; incr(k);
+ end;
+ if brace_level=0 then out_contrib[k]:="}"
+ else out_contrib[k]:="]";
+ send_out(str,k);
+ end;
+
+@ @<Force a line break@>=
+begin send_out(str,0); {normalize the buffer}
+while out_ptr>0 do
+ begin if out_ptr<=line_length then break_ptr:=out_ptr;
+ flush_buffer;
+ end;
+out_state:=misc;
+end
+
+@* Introduction to the input phase.
+We have now seen that \.{TANGLE} will be able to output the full
+\PASCAL\ program, if we can only get that program into the byte memory in
+the proper format. The input process is something like the output process
+in reverse, since we compress the text as we read it in and we expand it
+as we write it out.
+
+There are three main input routines. The most interesting is the one that gets
+the next token of a \PASCAL\ text; the other two are used to scan rapidly past
+\TeX\ text in the \.{WEB} source code. One of the latter routines will jump to
+the next token that starts with `\.{@@}', and the other skips to the end
+of a \PASCAL\ comment.
+
+@ But first we need to consider the low-level routine |get_line|
+that takes care of merging |change_file| into |web_file|. The |get_line|
+procedure also updates the line numbers for error messages.
+
+@<Globals...@>=
+@!ii:integer; {general purpose |for| loop variable in the outer block}
+@!line:integer; {the number of the current line in the current file}
+@!other_line:integer; {the number of the current line in the input file that
+ is not currently being read}
+@!temp_line:integer; {used when interchanging |line| with |other_line|}
+@!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}
+@!changing: boolean; {if |true|, the current line is from |change_file|}
+
+@ As we change |changing| from |true| to |false| and back again, we must
+remember to swap the values of |line| and |other_line| so that the |err_print|
+routine will be sure to report the correct line number.
+
+@d change_changing==
+ changing := not changing;
+ temp_line:=other_line; other_line:=line; line:=temp_line
+ {|line @t$\null\BA\null$@> other_line|}
+
+@ 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.
+
+@<Globals...@>=
+@!change_buffer:array[0..buf_size] of ASCII_code;
+@!change_limit:0..buf_size; {the last position occupied in |change_buffer|}
+
+@ Here's a simple function that checks if the two buffers are different.
+
+@p 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 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;
+exit: end;
+
+@ Procedure |prime_the_change_buffer| sets |change_buffer| in preparation
+for the next matching operation. Since blank lines in the change file are
+not used for matching, we have |(change_limit=0)and not changing| if and
+only if the change file is exhausted. This procedure is called only
+when |changing| is true; hence error messages will be reported correctly.
+
+@p 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@>;
+@<Skip to the next nonblank line; |return| if end of file@>;
+@<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>;
+exit: end;
+
+@ While looking for a line that begins with \.{@@x} in the change file,
+we allow lines that begin with \.{@@}, as long as they don't begin with
+\.{@@y} or \.{@@z} (which would probably indicate that the change file is
+fouled up).
+
+@<Skip over comment lines in the change file...@>=
+loop@+ begin incr(line);
+ if not input_ln(change_file) then return;
+ if limit<2 then goto continue;
+ if buffer[0]<>"@@" then goto continue;
+ if (buffer[1]>="X")and(buffer[1]<="Z") then
+ buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
+ if buffer[1]="x" then goto done;
+ if (buffer[1]="y")or(buffer[1]="z") then
+ begin loc:=2; err_print('! Where is the matching @@x?');
+@.Where is the match...@>
+ end;
+continue: end;
+done:
+
+@ Here we are looking at lines following the \.{@@x}.
+
+@<Skip to the next nonblank line...@>=
+repeat incr(line);
+ if not input_ln(change_file) then
+ begin err_print('! Change file ended after @@x');
+@.Change file ended...@>
+ return;
+ end;
+until limit>0;
+
+@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>=
+begin change_limit:=limit;
+if limit>0 then for k:=0 to limit-1 do change_buffer[k]:=buffer[k];
+end
+
+@ 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 \.{@@y} 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|.
+
+@p procedure check_change; {switches to |change_file| if the buffers match}
+label exit;
+var n:integer; {the number of discrepancies found}
+@!k:0..buf_size; {index into the buffers}
+begin if lines_dont_match then return;
+n:=0;
+loop@+ begin change_changing; {now it's |true|}
+ incr(line);
+ if not input_ln(change_file) then
+ begin err_print('! Change file ended before @@y');
+@.Change file ended...@>
+ change_limit:=0; change_changing; {|false| again}
+ return;
+ end;
+ @<If the current line starts with \.{@@y},
+ report any discrepancies and |return|@>;
+ @<Move |buffer| and |limit|...@>;
+ change_changing; {now it's |false|}
+ incr(line);
+ if not input_ln(web_file) then
+ begin err_print('! WEB file ended during a change');
+@.WEB file ended...@>
+ input_has_ended:=true; return;
+ end;
+ if lines_dont_match then incr(n);
+ end;
+exit: end;
+
+@ @<If the current line starts with \.{@@y}...@>=
+if limit>1 then if buffer[0]="@@" then
+ begin if (buffer[1]>="X")and(buffer[1]<="Z") then
+ buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
+ if (buffer[1]="x")or(buffer[1]="z") then
+ begin loc:=2; err_print('! Where is the matching @@y?');
+@.Where is the match...@>
+ end
+ else if buffer[1]="y" then
+ begin if n>0 then
+ begin loc:=2; err_print('! Hmm... ',n:1,
+ ' of the preceding lines failed to match');
+@.Hmm... n of the preceding...@>
+ end;
+ return;
+ end;
+ end
+
+@ @<Initialize the input system@>=
+open_input; line:=0; other_line:=0;@/
+changing:=true; prime_the_change_buffer; change_changing;@/
+limit:=0; loc:=1; buffer[0]:=" "; input_has_ended:=false;
+
+@ The |get_line| procedure is called when |loc>limit|; it puts the next
+line of merged input into the buffer and updates the other variables
+appropriately. A space is placed at the right end of the line.
+
+@p procedure get_line; {inputs the next line}
+label restart;
+begin restart: if changing then
+ @<Read from |change_file| and maybe turn off |changing|@>;
+if not changing then
+ begin @<Read from |web_file| and maybe turn on |changing|@>;
+ if changing then goto restart;
+ end;
+loc:=0; buffer[limit]:=" ";
+end;
+
+@ @<Read from |web_file|...@>=
+begin incr(line);
+if not input_ln(web_file) then input_has_ended:=true
+else if limit=change_limit then
+ if buffer[0]=change_buffer[0] then
+ if change_limit>0 then check_change;
+end
+
+@ @<Read from |change_file|...@>=
+begin incr(line);
+if not input_ln(change_file) then
+ begin err_print('! Change file ended without @@z');
+@.Change file ended...@>
+ buffer[0]:="@@"; buffer[1]:="z"; limit:=2;
+ end;
+if limit>1 then {check if the change has ended}
+ if buffer[0]="@@" then
+ begin if (buffer[1]>="X")and(buffer[1]<="Z") then
+ buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
+ if (buffer[1]="x")or(buffer[1]="y") then
+ begin loc:=2; err_print('! Where is the matching @@z?');
+@.Where is the match...@>
+ end
+ else if buffer[1]="z" then
+ begin prime_the_change_buffer; change_changing;
+ end;
+ end;
+end
+
+@ 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@>=
+if change_limit<>0 then {|changing| is false}
+ begin for ii:=0 to change_limit do buffer[ii]:=change_buffer[ii];
+ limit:=change_limit; changing:=true; line:=other_line; loc:=change_limit;
+ err_print('! Change file entry did not match');
+@.Change file entry did not match@>
+ end
+
+@ Important milestones are reached during the input phase when certain
+control codes are sensed.
+
+Control codes in \.{WEB} begin with `\.{@@}', and the next character
+identifies the code. Some of these are of interest only to \.{WEAVE},
+so \.{TANGLE} ignores them; the others are converted by \.{TANGLE} into
+internal code numbers by the |control_code| function below. The ordering
+of these internal code numbers has been chosen to simplify the program logic;
+larger numbers are given to the control codes that denote more significant
+milestones.
+
+@d ignore=0 {control code of no interest to \.{TANGLE}}
+@d control_text=@'203 {control code for `\.{@@t}', `\.{@@\^}', etc.}
+@d format=@'204 {control code for `\.{@@f}'}
+@d definition=@'205 {control code for `\.{@@d}'}
+@d begin_Pascal=@'206 {control code for `\.{@@p}'}
+@d module_name=@'207 {control code for `\.{@@<}'}
+@d new_module=@'210 {control code for `\.{@@\ }' and `\.{@@*}'}
+
+@p function control_code(@!c:ASCII_code):eight_bits; {convert |c| after \.{@@}}
+begin case c of
+"@@": control_code:="@@"; {`quoted' at sign}
+"'": control_code:=octal; {precedes octal constant}
+"""": control_code:=hex; {precedes hexadecimal constant}
+"$": control_code:=check_sum; {string pool check sum}
+" ",tab_mark: control_code:=new_module; {beginning of a new module}
+"*": begin print('*',module_count+1:1);
+ update_terminal; {print a progress report}
+ control_code:=new_module; {beginning of a new module}
+ end;
+"D","d": control_code:=definition; {macro definition}
+"F","f": control_code:=format; {format definition}
+"{": control_code:=begin_comment; {begin-comment delimiter}
+"}": control_code:=end_comment; {end-comment delimiter}
+"P","p": control_code:=begin_Pascal; {\PASCAL\ text in unnamed module}
+"T","t","^",".",":": control_code:=control_text; {control text to be ignored}
+"&": control_code:=join; {concatenate two tokens}
+"<": control_code:=module_name; {beginning of a module name}
+"=": control_code:=verbatim; {beginning of \PASCAL\ verbatim mode}
+"\": control_code:=force_line; {force a new line in \PASCAL\ output}
+othercases control_code:=ignore {ignore all other cases}
+endcases;
+end;
+
+@ The |skip_ahead| procedure reads through the input at fairly high speed
+until finding the next non-ignorable control code, which it returns.
+
+@p function skip_ahead:eight_bits; {skip to next control code}
+label done;
+var c:eight_bits; {control code found}
+begin loop begin if loc>limit then
+ begin get_line;
+ if input_has_ended then
+ begin c:=new_module; goto done;
+ end;
+ end;
+ buffer[limit+1]:="@@";
+ while buffer[loc]<>"@@" do incr(loc);
+ if loc<=limit then
+ begin loc:=loc+2; c:=control_code(buffer[loc-1]);
+ if (c<>ignore)or(buffer[loc-1]=">") then goto done;
+ end;
+ end;
+done: skip_ahead:=c;
+end;
+
+@ The |skip_comment| procedure reads through the input at somewhat high speed
+until finding the first unmatched right brace or until coming to the end
+of the file. It ignores characters following `\.\\' characters, since all
+braces that aren't nested are supposed to be hidden in that way. For
+example, consider the process of skipping the first comment below,
+where the string containing the right brace has been typed as \.{\`\\.\\\}\'}
+in the \.{WEB} file.
+
+@p procedure skip_comment; {skips to next unmatched `\.\}'}
+label exit;
+var bal:eight_bits; {excess of left braces}
+@!c:ASCII_code; {current character}
+begin bal:=0;
+loop@+ begin if loc>limit then
+ begin get_line;
+ if input_has_ended then
+ begin err_print('! Input ended in mid-comment');
+@.Input ended in mid-comment@>
+ return;
+ end;
+ end;
+ c:=buffer[loc]; incr(loc);
+ @<Do special things when |c="@@", "\", "{", "}"|; |return| at end@>;
+ end;
+exit:end;
+
+@ @<Do special things when |c="@@"...@>=
+if c="@@" then
+ begin c:=buffer[loc];
+ if (c<>" ")and(c<>tab_mark)and(c<>"*")and(c<>"z")and(c<>"Z") then incr(loc)
+ else begin err_print('! Section ended in mid-comment');
+@.Section ended in mid-comment@>
+ decr(loc); return;
+ end
+ end
+else if (c="\")and(buffer[loc]<>"@@") then incr(loc)
+else if c="{" then incr(bal)
+else if c="}" then
+ begin if bal=0 then return;
+ decr(bal);
+ end
+
+@* Inputting the next token.
+As stated above, \.{TANGLE}'s most interesting input procedure is the
+|get_next| routine that inputs the next token. However, the procedure
+isn't especially difficult.
+
+In most cases the tokens output by |get_next| have the form used in
+replacement texts, except that two-byte tokens are not produced.
+An identifier that isn't one letter long is represented by the
+output `|identifier|', and in such a case the global variables
+|id_first| and |id_loc| will have been set to the appropriate values
+needed by the |id_lookup| procedure. A string that begins with a
+double-quote is also considered an |identifier|, and in such a case
+the global variable |double_chars| will also have been set appropriately.
+Control codes produce the corresponding output of the |control_code|
+function above; and if that code is |module_name|, the value of |cur_module|
+will point to the |byte_start| entry for that module name.
+
+Another global variable, |scanning_hex|, is |true| during the time that
+the letters \.A through \.F should be treated as if they were digits.
+
+@<Globals...@>=
+@!cur_module: name_pointer; {name of module just scanned}
+@!scanning_hex: boolean; {are we scanning a hexadecimal constant?}
+
+@ @<Set init...@>=
+scanning_hex:=false;
+
+@ At the top level, |get_next| is a multi-way switch based on the next
+character in the input buffer. A |new_module| code is inserted at the
+very end of the input file.
+
+@p function get_next:eight_bits; {produces the next input token}
+label restart,done,found;
+var c:eight_bits; {the current character}
+@!d:eight_bits; {the next character}
+@!j,@!k:0..longest_name; {indices into |mod_text|}
+begin restart: if loc>limit then
+ begin get_line;
+ if input_has_ended then
+ begin c:=new_module; goto found;
+ end;
+ end;
+c:=buffer[loc]; incr(loc);
+if scanning_hex then @<Go to |found| if |c| is a hexadecimal digit,
+ otherwise set |scanning_hex:=false|@>;
+case c of
+"A",up_to("Z"),"a",up_to("z"): @<Get an identifier@>;
+"""": @<Get a preprocessed string@>;
+"@@": @<Get control code and possible module name@>;
+@t\4@>@<Compress two-symbol combinations like `\.{:=}'@>@;
+" ",tab_mark: goto restart; {ignore spaces and tabs}
+"{": begin skip_comment; goto restart;
+ end;
+"}": begin err_print('! Extra }'); goto restart;
+@.Extra \}@>
+ end;
+othercases if c>=128 then goto restart {ignore nonstandard characters}
+ else do_nothing
+endcases;
+found:@!debug if trouble_shooting then debug_help;@;@+gubed@/
+get_next:=c;
+end;
+
+@ @<Go to |found| if |c| is a hexadecimal digit...@>=
+if ((c>="0")and(c<="9"))or((c>="A")and(c<="F")) then goto found
+else scanning_hex:=false
+
+@ Note that the following code substitutes \.{@@\{} and \.{@@\}} for the
+respective combinations `\.{(*}' and `\.{*)}'. Explicit braces should be used
+for \TeX\ comments in \PASCAL\ text.
+
+@d compress(#)==begin if loc<=limit then begin c:=#; incr(loc); end; end
+
+@<Compress two-symbol...@>=
+".": if buffer[loc]="." then compress(double_dot)
+ else if buffer[loc]=")" then compress("]");
+":": if buffer[loc]="=" then compress(left_arrow);
+"=": if buffer[loc]="=" then compress(equivalence_sign);
+">": if buffer[loc]="=" then compress(greater_or_equal);
+"<": if buffer[loc]="=" then compress(less_or_equal)
+ else if buffer[loc]=">" then compress(not_equal);
+"(": if buffer[loc]="*" then compress(begin_comment)
+ else if buffer[loc]="." then compress("[");
+"*": if buffer[loc]=")" then compress(end_comment);
+
+@ We have to look at the preceding character to make sure this isn't part
+of a real constant, before trying to find an identifier starting with
+`\.e' or `\.E'.
+
+@<Get an identifier@>=
+begin if ((c="e")or(c="E"))and(loc>1) then
+ if (buffer[loc-2]<="9")and(buffer[loc-2]>="0") then c:=0;
+if c<>0 then
+ begin decr(loc); id_first:=loc;
+ repeat incr(loc); d:=buffer[loc];
+ until ((d<"0")or((d>"9")and(d<"A"))or((d>"Z")and(d<"a"))or(d>"z")) and
+ (d<>"_");
+ if loc>id_first+1 then
+ begin c:=identifier; id_loc:=loc;
+ end;
+ end
+else c:="E"; {exponent of a real constant}
+end
+
+@ A string that starts and ends with double-quote marks is converted into
+an identifier that behaves like a numeric macro by means of the following
+piece of the program.
+@^preprocessed strings@>
+
+@<Get a preprocessed string@>=
+begin double_chars:=0; id_first:=loc-1;
+repeat d:=buffer[loc]; incr(loc);
+ if (d="""")or(d="@@") then
+ if buffer[loc]=d then
+ begin incr(loc); d:=0; incr(double_chars);
+ end
+ else begin if d="@@" then err_print('! Double @@ sign missing')
+@.Double \AT! sign missing@>
+ end
+ else if loc>limit then
+ begin err_print('! String constant didn''t end'); d:="""";
+@.String constant didn't end@>
+ end;
+until d="""";
+id_loc:=loc-1; c:=identifier;
+end
+
+@ After an \.{@@} sign has been scanned, the next character tells us
+whether there is more work to do.
+
+@<Get control code and possible module name@>=
+begin c:=control_code(buffer[loc]); incr(loc);
+if c=ignore then goto restart
+else if c=hex then scanning_hex:=true
+else if c=module_name then
+ @<Scan the \(module name and make |cur_module| point to it@>
+else if c=control_text then
+ begin repeat c:=skip_ahead;
+ until c<>"@@";
+ if buffer[loc-1]<>">" then
+ err_print('! Improper @@ within control text');
+@.Improper \AT! within control text@>
+ goto restart;
+ end;
+end
+
+@ @<Scan the \(module name...@>=
+begin @<Put module name into |mod_text[1..k]|@>;
+if k>3 then
+ begin if (mod_text[k]=".")and(mod_text[k-1]=".")and(mod_text[k-2]=".") then
+ cur_module:=prefix_lookup(k-3)
+ else cur_module:=mod_lookup(k);
+ end
+else cur_module:=mod_lookup(k);
+end
+
+@ Module names are placed into the |mod_text| array with consecutive spaces,
+tabs, and carriage-returns replaced by single spaces. There will be no
+spaces at the beginning or the end. (We set |mod_text[0]:=" "| to facilitate
+this, since the |mod_lookup| routine uses |mod_text[1]| as the first
+character of the name.)
+
+@<Set init...@>=mod_text[0]:=" ";
+
+@ @<Put module name...@>=
+k:=0;
+loop@+ begin if loc>limit then
+ begin get_line;
+ if input_has_ended then
+ begin err_print('! Input ended in section name');
+@.Input ended in section name@>
+ goto done;
+ end;
+ end;
+ d:=buffer[loc];
+ @<If end of name, |goto done|@>;
+ incr(loc); if k<longest_name-1 then incr(k);
+ if (d=" ")or(d=tab_mark) then
+ begin d:=" "; if mod_text[k-1]=" " then decr(k);
+ end;
+ mod_text[k]:=d;
+ end;
+done: @<Check for overlong name@>;
+if (mod_text[k]=" ")and(k>0) then decr(k);
+
+@ @<If end of name,...@>=
+if d="@@" then
+ begin d:=buffer[loc+1];
+ if d=">" then
+ begin loc:=loc+2; goto done;
+ end;
+ if (d=" ")or(d=tab_mark)or(d="*") then
+ begin err_print('! Section name didn''t end'); goto done;
+@.Section name didn't end@>
+ end;
+ incr(k); mod_text[k]:="@@"; incr(loc); {now |d=buffer[loc]| again}
+ end
+
+@ @<Check for overlong name@>=
+if k>=longest_name-2 then
+ begin print_nl('! Section name too long: ');
+@.Section name too long@>
+ for j:=1 to 25 do print(xchr[mod_text[j]]);
+ print('...'); mark_harmless;
+ end
+
+@* Scanning a numeric definition.
+When \.{TANGLE} looks at the \PASCAL\ text following the `\.=' of a numeric
+macro definition, it calls on the precedure |scan_numeric(p)|, where |p|
+points to the name that is to be defined. This procedure evaluates the
+right-hand side, which must consist entirely of integer constants and
+defined numeric macros connected with \.+ and \.- signs (no parentheses).
+It also sets the global variable |next_control| to the control code that
+terminated this definition.
+
+A definition ends with the control codes |definition|, |format|, |module_name|,
+|begin_Pascal|, and |new_module|, all of which can be recognized
+by the fact that they are the largest values |get_next| can return.
+
+@d end_of_definition(#)==(#>=format)
+ {is |#| a control code ending a definition?}
+
+@<Global...@>=
+@!next_control:eight_bits; {control code waiting to be acted upon}
+
+@ The evaluation of a numeric expression makes use of two variables called the
+|accumulator| and the |next_sign|. At the beginning, |accumulator| is zero and
+|next_sign| is $+1$. When a \.+ or \.- is scanned, |next_sign| is multiplied
+by the value of that sign. When a numeric value is scanned, it is multiplied by
+|next_sign| and added to the |accumulator|, then |next_sign| is reset to $+1$.
+
+@d add_in(#)==begin accumulator:=accumulator+next_sign*(#); next_sign:=+1;
+ end
+
+@p procedure scan_numeric(@!p:name_pointer); {defines numeric macros}
+label reswitch, done;
+var accumulator:integer; {accumulates sums}
+@!next_sign:-1..+1; {sign to attach to next value}
+@!q:name_pointer; {points to identifiers being evaluated}
+@!val:integer; {constants being evaluated}
+begin @<Set \(|accumulator| to the value of the right-hand side@>;
+if abs(accumulator)>=@'100000 then
+ begin err_print('! Value too big: ',accumulator:1); accumulator:=0;
+@.Value too big@>
+ end;
+equiv[p]:=accumulator+@'100000; {name |p| now is defined to equal |accumulator|}
+end;
+
+@ @<Set \(|accumulator| to the value of the right-hand side@>=
+accumulator:=0; next_sign:=+1;
+loop@+ begin next_control:=get_next;
+ reswitch: case next_control of
+ digits: begin @<Set |val| to value of decimal constant, and
+ set |next_control| to the following token@>;
+ add_in(val); goto reswitch;
+ end;
+ octal: begin @<Set |val| to value of octal constant, and
+ set |next_control| to the following token@>;
+ add_in(val); goto reswitch;
+ end;
+ hex: begin @<Set |val| to value of hexadecimal constant, and
+ set |next_control| to the following token@>;
+ add_in(val); goto reswitch;
+ end;
+ identifier: begin q:=id_lookup(normal);
+ if ilk[q]<>numeric then
+ begin next_control:="*"; goto reswitch; {leads to error}
+ end;
+ add_in(equiv[q]-@'100000);
+ end;
+ "+": do_nothing;
+ "-": next_sign:=-next_sign;
+ format, definition, module_name, begin_Pascal, new_module: goto done;
+ ";": err_print('! Omit semicolon in numeric definition');
+@.Omit semicolon in numeric def...@>
+ othercases @<Signal error, flush rest of the definition@>
+ endcases;
+ end;
+done:
+
+@ @<Signal error, flush rest...@>=
+begin err_print('! Improper numeric definition will be flushed');
+@.Improper numeric definition...@>
+repeat next_control:=skip_ahead
+until end_of_definition(next_control);
+if next_control=module_name then
+ begin {we want to scan the module name too}
+ loc:=loc-2; next_control:=get_next;
+ end;
+accumulator:=0; goto done;
+end
+
+@ @<Set |val| to value of decimal...@>=
+val:=0;
+repeat val:=10*val+next_control-"0"; next_control:=get_next;
+until (next_control>"9")or(next_control<"0")
+
+@ @<Set |val| to value of octal...@>=
+val:=0; next_control:="0";
+repeat val:=8*val+next_control-"0"; next_control:=get_next;
+until (next_control>"7")or(next_control<"0")
+
+@ @<Set |val| to value of hex...@>=
+val:=0; next_control:="0";
+repeat if next_control>="A" then next_control:=next_control+"0"+10-"A";
+val:=16*val+next_control-"0"; next_control:=get_next;
+until (next_control>"F")or(next_control<"0")or@|
+ ((next_control>"9")and(next_control<"A"))
+
+@* Scanning a macro definition.
+The rules for generating the replacement texts corresponding to simple
+macros, parametric macros, and \PASCAL\ texts of a module are almost
+identical, so a single procedure is used for all three cases. The
+differences are that
+
+\yskip\item{a)} The sign |#| denotes a parameter only when it appears
+outside of strings in a parametric macro; otherwise it stands for the
+ASCII character |#|. (This is not used in standard \PASCAL, but some
+\PASCAL s allow, for example, `\.{/\#}' after a certain kind of file name.)
+
+\item{b)}Module names are not allowed in simple macros or parametric macros;
+in fact, the appearance of a module name terminates such macros and denotes
+the name of the current module.
+
+\item{c)}The symbols \.{@@d} and \.{@@f} and \.{@@p} are not allowed after
+module names, while they terminate macro definitions.
+
+@ Therefore there is a procedure |scan_repl| whose parameter |t| specifies
+either |simple| or |parametric| or |module_name|. After |scan_repl| has
+acted, |cur_repl_text| will point to the replacement text just generated, and
+|next_control| will contain the control code that terminated the activity.
+
+@<Globals...@>=
+@!cur_repl_text:text_pointer; {replacement text formed by |scan_repl|}
+
+@ @p procedure scan_repl(@!t:eight_bits); {creates a replacement text}
+label continue, done, found, reswitch;
+var a:sixteen_bits; {the current token}
+@!b:ASCII_code; {a character from the buffer}
+@!bal:eight_bits; {left parentheses minus right parentheses}
+begin bal:=0;
+loop@+ begin continue: a:=get_next;
+ case a of
+ "(": incr(bal);
+ ")": if bal=0 then err_print('! Extra )')
+@.Extra )@>
+ else decr(bal);
+ "'": @<Copy a string from the buffer to |tok_mem|@>;
+ "#": if t=parametric then a:=param;
+ @t\4@>@<In cases that |a| is a non-ASCII token (|identifier|,
+ |module_name|, etc.), either process it and change |a| to a byte
+ that should be stored, or |goto continue| if |a| should be ignored,
+ or |goto done| if |a| signals the end of this replacement text@>@;
+ othercases do_nothing
+ endcases;@/
+ app_repl(a); {store |a| in |tok_mem|}
+ end;
+done: next_control:=a;
+@<Make sure the parentheses balance@>;
+if text_ptr>max_texts-zz then overflow('text');
+cur_repl_text:=text_ptr; tok_start[text_ptr+zz]:=tok_ptr[z];
+incr(text_ptr);
+if z=zz-1 then z:=0@+else incr(z);
+end;
+
+@ @<Make sure the parentheses balance@>=
+if bal>0 then
+ begin if bal=1 then err_print('! Missing )')
+ else err_print('! Missing ',bal:1,' )''s');
+@.Missing n )@>
+ while bal>0 do
+ begin app_repl(")"); decr(bal);
+ end;
+ end
+
+@ @<In cases that |a| is...@>=
+identifier: begin a:=id_lookup(normal); app_repl((a div @'400)+@'200);
+ a:=a mod @'400;
+ end;
+module_name: if t<>module_name then goto done
+ else begin app_repl((cur_module div @'400)+@'250);
+ a:=cur_module mod @'400;
+ end;
+verbatim: @<Copy verbatim string from the buffer to |tok_mem|@>;
+definition, format, begin_Pascal: if t<>module_name then goto done
+ else begin err_print('! @@',xchr[buffer[loc-1]],
+@.\AT!p is ignored in Pascal text@>
+@.\AT!d is ignored in Pascal text@>
+@.\AT!f is ignored in Pascal text@>
+ ' is ignored in Pascal text'); goto continue;
+ end;
+new_module: goto done;
+
+@ @<Copy a string...@>=
+begin b:="'";
+loop@+ begin app_repl(b);
+ if b="@@" then
+ if buffer[loc]="@@" then incr(loc) {store only one \.{@@}}
+ else err_print('! You should double @@ signs in strings');
+@.You should double \AT! signs@>
+ if loc=limit then
+ begin err_print('! String didn''t end');
+@.String didn't end@>
+ buffer[loc]:="'"; buffer[loc+1]:=0;
+ end;
+ b:=buffer[loc]; incr(loc);
+ if b="'" then
+ begin if buffer[loc]<>"'" then goto found
+ else begin incr(loc); app_repl("'");
+ end;
+ end;
+ end;
+found: end {now |a| holds the final |"'"| that will be stored}
+
+@ @<Copy verbatim string...@>=
+begin app_repl(verbatim);
+buffer[limit+1]:="@@";
+reswitch: if buffer[loc]="@@" then
+ begin if loc<limit then if buffer[loc+1]="@@" then
+ begin app_repl("@@");
+ loc:=loc+2;
+ goto reswitch;
+ end;
+ end
+else begin app_repl(buffer[loc]);
+ incr(loc);
+ goto reswitch;
+ end;
+if loc>=limit then err_print('! Verbatim string didn''t end')
+@.Verbatim string didn't end@>
+else if buffer[loc+1]<>">" then
+ err_print('! You should double @@ signs in verbatim strings');
+@.You should double \AT! signs@>
+loc:=loc+2;
+end {another |verbatim| byte will be stored, since |a=verbatim|}
+
+@ The following procedure is used to define a simple or parametric macro,
+just after the `\.{==}' of its definition has been scanned.
+
+@p procedure define_macro(@!t:eight_bits);
+var p:name_pointer; {the identifier being defined}
+begin p:=id_lookup(t); scan_repl(t);@/
+equiv[p]:=cur_repl_text; text_link[cur_repl_text]:=0;
+end;
+
+@* Scanning a module.
+The |scan_module| procedure starts when `\.{@@\ }' or `\.{@@*}' has been
+sensed in the input, and it proceeds until the end of that module. It
+uses |module_count| to keep track of the current module number; with luck,
+\.{WEAVE} and \.{TANGLE} will both assign the same numbers to modules.
+
+@<Globals...@>=
+@!module_count:0..@'27777; {the current module number}
+
+@ The top level of |scan_module| is trivial.
+@p procedure scan_module;
+label continue, done, exit;
+var p:name_pointer; {module name for the current module}
+begin incr(module_count);
+@<Scan the \(definition part of the current module@>;
+@<Scan the \PASCAL\ part of the current module@>;
+exit: end;
+
+@ @<Scan the \(definition part...@>=
+next_control:=0;
+loop@+ begin continue: while next_control<=format do
+ begin next_control:=skip_ahead;
+ if next_control=module_name then
+ begin {we want to scan the module name too}
+ loc:=loc-2; next_control:=get_next;
+ end;
+ end;
+ if next_control<>definition then goto done;
+ next_control:=get_next; {get identifier name}
+ if next_control<>identifier then
+ begin err_print('! Definition flushed, must start with ',
+@.Definition flushed...@>
+ 'identifier of length > 1'); goto continue;
+ end;
+ next_control:=get_next; {get token after the identifier}
+ if next_control="=" then
+ begin scan_numeric(id_lookup(numeric)); goto continue;
+ end
+ else if next_control=equivalence_sign then
+ begin define_macro(simple); goto continue;
+ end
+ else @<If the next text is `|(#)==|', call |define_macro|
+ and |goto continue|@>;
+ err_print('! Definition flushed since it starts badly');
+@.Definition flushed...@>
+ end;
+done:
+
+@ @<If the next text is `|(#)==|'...@>=
+if next_control="(" then
+ begin next_control:=get_next;
+ if next_control="#" then
+ begin next_control:=get_next;
+ if next_control=")" then
+ begin next_control:=get_next;
+ if next_control="=" then
+ begin err_print('! Use == for macros');
+@.Use == for macros@>
+ next_control:=equivalence_sign;
+ end;
+ if next_control=equivalence_sign then
+ begin define_macro(parametric); goto continue;
+ end;
+ end;
+ end;
+ end;
+
+@ @<Scan the \PASCAL...@>=
+case next_control of
+begin_Pascal:p:=0;
+module_name: begin p:=cur_module;
+ @<Check that |=| or |==| follows this module name, otherwise |return|@>;
+ end;
+othercases return
+endcases;@/
+@<Insert the module number into |tok_mem|@>;
+scan_repl(module_name); {now |cur_repl_text| points to the replacement text}
+@<Update the data structure so that the replacement text is accessible@>;
+
+@ @<Check that |=|...@>=
+repeat next_control:=get_next;
+until next_control<>"+"; {allow optional `\.{+=}'}
+if (next_control<>"=")and(next_control<>equivalence_sign) then
+ begin err_print('! Pascal text flushed, = sign is missing');
+@.Pascal text flushed...@>
+ repeat next_control:=skip_ahead;
+ until next_control=new_module;
+ return;
+ end
+
+@ @<Insert the module number...@>=
+store_two_bytes(@'150000+module_count); {|@'150000=@'320*@'400|}
+
+@ @<Update the data...@>=
+if p=0 then {unnamed module}
+ begin text_link[last_unnamed]:=cur_repl_text; last_unnamed:=cur_repl_text;
+ end
+else if equiv[p]=0 then equiv[p]:=cur_repl_text {first module of this name}
+else begin p:=equiv[p];
+ while text_link[p]<module_flag do p:=text_link[p]; {find end of list}
+ text_link[p]:=cur_repl_text;
+ end;
+text_link[cur_repl_text]:=module_flag;
+ {mark this replacement text as a nonmacro}
+
+@* Debugging.
+The \PASCAL\ debugger with which \.{TANGLE} was developed allows breakpoints
+to be set, and variables can be read and changed, but procedures cannot be
+executed. Therefore a `|debug_help|' procedure has been inserted in the main
+loops of each phase of the program; when |ddt| and |dd| are set to appropriate
+values, symbolic printouts of various tables will appear.
+
+The idea is to set a breakpoint inside the |debug_help| routine, at the
+place of `\ignorespaces|breakpoint:|\unskip' below. Then when
+|debug_help| is to be activated, set |trouble_shooting| equal to |true|.
+The |debug_help| routine will prompt you for values of |ddt| and |dd|,
+discontinuing this when |ddt<=0|; thus you type $2n+1$ integers, ending
+with zero or a negative number. Then control either passes to the
+breakpoint, allowing you to look at and/or change variables (if you typed
+zero), or to exit the routine (if you typed a negative value).
+
+Another global variable, |debug_cycle|, can be used to skip silently
+past calls on |debug_help|. If you set |debug_cycle>1|, the program stops
+only every |debug_cycle| times |debug_help| is called; however,
+any error stop will set |debug_cycle| to zero.
+
+@<Globals...@>=
+@!debug@!trouble_shooting:boolean; {is |debug_help| wanted?}
+@!ddt:integer; {operation code for the |debug_help| routine}
+@!dd:integer; {operand in procedures performed by |debug_help|}
+@!debug_cycle:integer; {threshold for |debug_help| stopping}
+@!debug_skipped:integer; {we have skipped this many |debug_help| calls}
+@!term_in:text_file; {the user's terminal as an input file}
+gubed
+
+@ The debugging routine needs to read from the user's terminal.
+@^system dependencies@>
+@<Set init...@>=
+@!debug trouble_shooting:=true; debug_cycle:=1; debug_skipped:=0;@/
+trouble_shooting:=false; debug_cycle:=99999; {use these when it almost works}
+reset(term_in,'TTY:','/I'); {open |term_in| as the terminal, don't do a |get|}
+gubed
+
+@ @d breakpoint=888 {place where a breakpoint is desirable}
+@^system dependencies@>
+
+@p @!debug procedure debug_help; {routine to display various things}
+label breakpoint,exit;
+var k:integer; {index into various arrays}
+begin incr(debug_skipped);
+if debug_skipped<debug_cycle then return;
+debug_skipped:=0;
+loop@+ begin print_nl('#'); update_terminal; {prompt}
+ read(term_in,ddt); {read a debug-command code}
+ if ddt<0 then return
+ else if ddt=0 then
+ begin goto breakpoint;@\ {go to every label at least once}
+ breakpoint: ddt:=0;@\
+ end
+ else begin read(term_in,dd);
+ case ddt of
+ 1: print_id(dd);
+ 2: print_repl(dd);
+ 3: for k:=1 to dd do print(xchr[buffer[k]]);
+ 4: for k:=1 to dd do print(xchr[mod_text[k]]);
+ 5: for k:=1 to out_ptr do print(xchr[out_buf[k]]);
+ 6: for k:=1 to dd do print(xchr[out_contrib[k]]);
+ othercases print('?')
+ endcases;
+ end;
+ end;
+exit:end;
+gubed
+
+@* 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 \.{TANGLE} starts, and where
+it ends.
+@^system dependencies@>
+
+@p begin initialize;
+@<Initialize the input system@>;
+print_ln(banner); {print a ``banner line''}
+@<Phase I: Read all the user's text and compress it into |tok_mem|@>;
+stat for ii:=0 to zz-1 do max_tok_ptr[ii]:=tok_ptr[ii];@+tats@;@/
+@<Phase II:...@>;
+end_of_TANGLE:
+if string_ptr>256 then @<Finish off the string pool file@>;
+stat @<Print statistics about memory usage@>;@+tats@;@/
+@t\4\4@>{here files should be closed if the operating system requires it}
+@<Print the job |history|@>;
+end.
+
+@ @<Phase I:...@>=
+phase_one:=true;
+module_count:=0;
+repeat next_control:=skip_ahead;
+until next_control=new_module;
+while not input_has_ended do scan_module;
+@<Check that all changes have been read@>;
+phase_one:=false;
+
+@ @<Finish off the string pool file@>=
+begin print_nl(string_ptr-256:1, ' strings written to string pool file.');
+write(pool,'*');
+for ii:=1 to 9 do
+ begin out_buf[ii]:=pool_check_sum mod 10;
+ pool_check_sum:=pool_check_sum div 10;
+ end;
+for ii:=9 downto 1 do write(pool,xchr["0"+out_buf[ii]]);
+write_ln(pool);
+end
+
+@ @<Glob...@>=
+stat @!wo:0..ww-1; {segment of memory for which statistics are being printed}
+tats
+
+@ @<Print statistics about memory usage@>=
+print_nl('Memory usage statistics:');
+print_nl(name_ptr:1, ' names, ', text_ptr:1, ' replacement texts;');
+print_nl(byte_ptr[0]:1);
+for wo:=1 to ww-1 do print('+',byte_ptr[wo]:1);
+if phase_one then
+ for ii:=0 to zz-1 do max_tok_ptr[ii]:=tok_ptr[ii];
+print(' bytes, ', max_tok_ptr[0]:1);
+for ii:=1 to zz-1 do print('+',max_tok_ptr[ii]:1);
+print(' tokens.');
+
+@ Some implementations may wish to pass the |history| value to the
+operating system so that it can be used to govern whether or not other
+programs are started. Here we simply report the history to the user.
+@^system dependencies@>
+
+@<Print the job |history|@>=
+case history of
+spotless: print_nl('(No errors were found.)');
+harmless_message: print_nl('(Did you see the warning message above?)');
+error_message: print_nl('(Pardon me, but I think I spotted something wrong.)');
+fatal_message: print_nl('(That was a fatal error, my friend.)');
+end {there are no other cases}
+
+@* System-dependent changes.
+This module should be replaced, if necessary, by changes to the program
+that are necessary to make \.{TANGLE} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous modules preserve the module numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new modules, can be inserted here; then only the index
+itself will get a new module number.
+@^system dependencies@>
+
+@* Index.
+Here is a cross-reference table for the \.{TANGLE} 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.
diff --git a/systems/knuth/dist/web/weave.web b/systems/knuth/dist/web/weave.web
new file mode 100644
index 0000000000..c1785c35cf
--- /dev/null
+++ b/systems/knuth/dist/web/weave.web
@@ -0,0 +1,4904 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Version 0 was released in December, 1981.
+% Version 1 was released in September, 1982, with version 0 of TeX.
+% Slight changes were made in October, 1982, for version 0.6 of TeX.
+% Version 1.1 changed "_" to "\_" if not within an identifier (November, 1982).
+% Version 1.2 added @@= and @@\ and marked changed modules (December, 1982).
+% Version 1.3 marked and indexed changed modules better (January, 1983).
+% Version 1.4 added "history" (February, 1983).
+% Version 1.5 conformed to TeX version 0.96 (March, 1983).
+% Version 1.6 conformed to TeX version 0.98 (May, 1983).
+% Version 1.7 introduced the new change file format (June, 1983).
+% Version 2 was released in July, 1983, with version 0.999 of TeX.
+% Version 2.1 corrected a bug in changed_module reckoning (August, 1983).
+% Version 2.2 corrected it better (August, 1983).
+% Version 2.3 starts the output with \input webmac (August, 1983).
+% Version 2.4 fixed a bug in compress(#) (September, 1983).
+% Version 2.5 cleared xrefswitch after module names (November, 1983).
+% Version 2.6 fixed a bug in declaration of trans array (January, 1984).
+% Version 2.7 fixed a bug in real constants (August, 1984).
+% Version 2.8 fixed a bug in change_buffer movement (August, 1985).
+% Version 2.9 increased max_refs and max_toks to 30000 each (January, 1987).
+% Version 3, for Sewell's book, fixed long-line bug in input_ln (March, 1989).
+% Version 3.1 fixed a bug for programs with only one module (April, 1989).
+% Version 4 was major change to allow 8-bit input (September, 1989).
+% Version 4.1, for Breitenlohner, avoids English-only output (March, 1990).
+% Version 4.2 conforms to ANSI standard for-loop rules (September, 1990).
+% Version 4.3 catches extra } in input (Breitenlohner, September, 1991).
+% Version 4.4 corrects changed_module logic, %-overflow (January, 1992).
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\indent\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+\def\pb{$\.|\ldots\.|$} % Pascal brackets (|...|)
+\def\v{\.{\char'174}} % vertical (|) in typewriter font
+\def\dleft{[\![} \def\dright{]\!]} % double brackets
+\mathchardef\RA="3221 % right arrow
+\mathchardef\BA="3224 % double arrow
+\def\({} % kludge for alphabetizing certain module names
+
+\def\title{WEAVE}
+\def\contentspagenumber{15} % should be odd
+\def\topofcontents{\null\vfill
+ \titlefalse % include headline on the contents page
+ \def\rheader{\mainfont Appendix D\hfil \contentspagenumber}
+ \centerline{\titlefont The {\ttitlefont WEAVE} processor}
+ \vskip 15pt
+ \centerline{(Version 4.4)}
+ \vfill}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+This program converts a \.{WEB} file to a \TeX\ file. It was written
+by D. E. Knuth in October, 1981; a somewhat similar {\mc SAIL} program had
+been developed in March, 1979, although the earlier program used a top-down
+parsing method that is quite different from the present scheme.
+
+The code uses a few features of the local \PASCAL\ compiler that may need
+to be changed in other installations:
+
+\yskip\item{1)} Case statements have a default.
+\item{2)} Input-output routines may need to be adapted for use with a particular
+character set and/or for printing messages on the user's terminal.
+
+\yskip\noindent
+These features are also present in the \PASCAL\ version of \TeX, where they
+are used in a similar (but more complex) way. System-dependent portions
+of \.{WEAVE} can be identified by looking at the entries for `system
+dependencies' in the index below.
+@!@^system dependencies@>
+
+The ``banner line'' defined here should be changed whenever \.{WEAVE}
+is modified.
+
+@d banner=='This is WEAVE, Version 4.4'
+
+@ The program begins with a fairly normal header, made up of pieces that
+@^system dependencies@>
+will mostly be filled in later. The \.{WEB} input comes from files |web_file|
+and |change_file|, and the \TeX\ output goes to file |tex_file|.
+
+If it is necessary to abort the job because of a fatal error, the program
+calls the `|jump_out|' procedure, which goes to the label |end_of_WEAVE|.
+
+@d end_of_WEAVE = 9999 {go here to wrap it up}
+
+@p @t\4@>@<Compiler directives@>@/
+program WEAVE(@!web_file,@!change_file,@!tex_file);
+label end_of_WEAVE; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@<Error handling procedures@>@/
+procedure initialize;
+ var @<Local variables for initialization@>@/
+ begin @<Set initial values@>@/
+ end;
+
+@ Some of this code is optional for use when debugging only;
+such material is enclosed between the delimiters |debug| and $|gubed|$.
+Other parts, delimited by |stat| and $|tats|$, are optionally included
+if statistics about \.{WEAVE}'s memory usage are desired.
+
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@f debug==begin
+@f gubed==end
+@#
+@d stat==@{ {change this to `$\\{stat}\equiv\null$'
+ when gathering usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$'
+ when gathering usage statistics}
+@f stat==begin
+@f tats==end
+
+@ The \PASCAL\ compiler used to develop this system has ``compiler
+directives'' that can appear in comments whose first character is a dollar sign.
+In production versions of \.{WEAVE} these directives tell the compiler that
+@^system dependencies@>
+it is safe to avoid range checks and to leave out the extra code it inserts
+for the \PASCAL\ debugger's benefit, although interrupts will occur if
+there is arithmetic overflow.
+
+@<Compiler directives@>=
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+
+@ Labels are given symbolic names by the following definitions. We insert
+the label `|exit|:' just before the `\ignorespaces|end|\unskip' of a
+procedure in which we have used the `|return|' statement defined below;
+the label `|restart|' is occasionally used at the very beginning of a
+procedure; and the label `|reswitch|' is occasionally used just prior to
+a \&{case} statement in which some cases change the conditions and we wish to
+branch to the newly applicable case.
+Loops that are set up with the \&{loop} construction defined below are
+commonly exited by going to `|done|' or to `|found|' or to `|not_found|',
+and they are sometimes repeated by going to `|continue|'.
+
+@d exit=10 {go here to leave a procedure}
+@d restart=20 {go here to start a procedure again}
+@d reswitch=21 {go here to start a case statement again}
+@d continue=22 {go here to resume a loop}
+@d done=30 {go here to exit a loop}
+@d found=31 {go here when you've found it}
+@d not_found=32 {go here when you've found something else}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d loop == @+ while true do@+ {repeat over and over until a |goto| happens}
+@d do_nothing == {empty statement}
+@d return == goto exit {terminate a procedure call}
+@f return == nil
+@f loop == xclause
+
+@ We assume that |case| statements may include a default case that applies
+if no matching label is found. Thus, we shall use constructions like
+@^system dependencies@>
+$$\vbox{\halign{#\hfil\cr
+|case x of|\cr
+1: $\langle\,$code for $x=1\,\rangle$;\cr
+3: $\langle\,$code for $x=3\,\rangle$;\cr
+|othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr
+|endcases|\cr}}$$
+since most \PASCAL\ compilers have plugged this hole in the language by
+incorporating some sort of default mechanism. For example, the compiler
+used to develop \.{WEB} and \TeX\ allows `|others|:' as a default label,
+and other \PASCAL s allow syntaxes like `\ignorespaces|else|\unskip' or
+`\&{otherwise}' or `\\{otherwise}:', etc. The definitions of |othercases|
+and |endcases| should be changed to agree with local conventions.
+(Of course, if no default mechanism is available, the |case| statements of
+this program must be extended by listing all remaining cases.)
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ The following parameters are set big enough to handle \TeX, so they
+should be sufficient for most applications of \.{WEAVE}.
+
+@<Constants...@>=
+@!max_bytes=45000; {|1/ww| times the number of bytes in identifiers,
+ index entries, and module names; must be less than 65536}
+@!max_names=5000; {number of identifiers, index entries, and module names;
+ must be less than 10240}
+@!max_modules=2000;{greater than the total number of modules}
+@!hash_size=353; {should be prime}
+@!buf_size=100; {maximum length of input line}
+@!longest_name=400; {module names shouldn't be longer than this}
+@!long_buf_size=500; {|buf_size+longest_name|}
+@!line_length=80; {lines of \TeX\ output have at most this many characters,
+ should be less than 256}
+@!max_refs=30000; {number of cross references; must be less than 65536}
+@!max_toks=30000; {number of symbols in \PASCAL\ texts being parsed;
+ must be less than 65536}
+@!max_texts=2000; {number of phrases in \PASCAL\ texts being parsed;
+ must be less than 10240}
+@!max_scraps=1000; {number of tokens in \PASCAL\ texts being parsed}
+@!stack_size=200; {number of simultaneous output levels}
+
+@ A global variable called |history| will contain one of four values
+at the end of every run: |spotless| means that no unusual messages were
+printed; |harmless_message| means that a message of possible interest
+was printed but no serious errors were detected; |error_message| means that
+at least one error was found; |fatal_message| means that the program
+terminated abnormally. The value of |history| does not influence the
+behavior of the program; it is simply computed for the convenience
+of systems that might want to use such information.
+
+@d spotless=0 {|history| value for normal jobs}
+@d harmless_message=1 {|history| value when non-serious info was printed}
+@d error_message=2 {|history| value when an error was noted}
+@d fatal_message=3 {|history| value when we had to stop prematurely}
+@#
+@d mark_harmless==@t@>@+if history=spotless then history:=harmless_message
+@d mark_error==history:=error_message
+@d mark_fatal==history:=fatal_message
+
+@<Glob...@>=@!history:spotless..fatal_message; {how bad was this run?}
+
+@ @<Set init...@>=history:=spotless;
+
+@* The character set.
+One of the main goals in the design of \.{WEB} has been to make it readily
+portable between a wide variety of computers. Yet \.{WEB} by its very
+nature must use a greater variety of characters than most computer
+programs deal with, and character encoding is one of the areas in which
+existing machines differ most widely from each other.
+
+To resolve this problem, all input to \.{WEAVE} and \.{TANGLE} is
+converted to an internal eight-bit code that is essentially standard
+ASCII, the ``American Standard Code for Information Interchange.''
+The conversion is done immediately when each character is read in.
+Conversely, characters are converted from ASCII to the user's external
+representation just before they are output. (The original ASCII code
+was seven bits only; \.{WEB} now allows eight bits in an attempt to
+keep up with modern times.)
+
+Such an internal code is relevant to users of \.{WEB} only because it is
+the code used for preprocessed constants like \.{"A"}. If you are writing
+a program in \.{WEB} that makes use of such one-character constants, you
+should convert your input to ASCII form, like \.{WEAVE} and \.{TANGLE} do.
+Otherwise \.{WEB}'s internal coding scheme does not affect you.
+@^ASCII code@>
+
+Here is a table of the standard visible ASCII codes:
+$$\def\:{\char\count255\global\advance\count255 by 1}
+\count255='40
+\vbox{
+\hbox{\hbox to 40pt{\it\hfill0\/\hfill}%
+\hbox to 40pt{\it\hfill1\/\hfill}%
+\hbox to 40pt{\it\hfill2\/\hfill}%
+\hbox to 40pt{\it\hfill3\/\hfill}%
+\hbox to 40pt{\it\hfill4\/\hfill}%
+\hbox to 40pt{\it\hfill5\/\hfill}%
+\hbox to 40pt{\it\hfill6\/\hfill}%
+\hbox to 40pt{\it\hfill7\/\hfill}}
+\vskip 4pt
+\hrule
+\def\^{\vrule height 10.5pt depth 4.5pt}
+\halign{\hbox to 0pt{\hskip -24pt\O{#0}\hfill}&\^
+\hbox to 40pt{\tt\hfill#\hfill\^}&
+&\hbox to 40pt{\tt\hfill#\hfill\^}\cr
+04&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+05&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+06&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+07&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+10&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+11&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+12&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+13&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+14&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+15&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+16&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule}
+17&\:&\:&\:&\:&\:&\:&\:\cr}
+\hrule width 280pt}$$
+(Actually, of course, code @'040 is an invisible blank space.) Code @'136
+was once an upward arrow (\.{\char'13}), and code @'137 was
+once a left arrow (\.^^X), in olden times when the first draft
+of ASCII code was prepared; but \.{WEB} works with today's standard
+ASCII in which those codes represent circumflex and underline as shown.
+
+@<Types...@>=
+@!ASCII_code=0..255; {eight-bit numbers, a subrange of the integers}
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lowercase
+letters. Nowadays, of course, we need to deal with both capital and small
+letters in a convenient way, so \.{WEB} assumes that it is being used
+with a \PASCAL\ whose character set contains at least the characters of
+standard ASCII as listed above. Some \PASCAL\ compilers use the original
+name |char| for the data type associated with the characters in text files,
+while other \PASCAL s consider |char| to be a 64-element subrange of a larger
+data type that has some other name.
+
+In order to accommodate this difference, we shall use the name |text_char|
+to stand for the data type of the characters in the input and output
+files. We shall also assume that |text_char| consists of the elements
+|chr(first_text_char)| through |chr(last_text_char)|, inclusive. The
+following definitions should be adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+
+@ The \.{WEAVE} and \.{TANGLE} 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...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+
+@ If we assume that every system using \.{WEB} is able to read and write the
+visible characters of standard ASCII (although not necessarily using the
+ASCII codes to represent them), the following assignment statements initialize
+most of the |xchr| array properly, without needing any system-dependent
+changes. For example, the statement \.{xchr[@@\'101]:=\'A\'} that appears
+in the present \.{WEB} file might be encoded in, say, {\mc EBCDIC} code
+on the external medium on which it resides, but \.{TANGLE} will convert from
+this external code to ASCII and back again. Therefore the assignment
+statement \.{XCHR[65]:=\'A\'} will appear in the corresponding \PASCAL\ file,
+and \PASCAL\ will compile this statement so that |xchr[65]| receives the
+character \.A in the external (|char|) code. Note that it would be quite
+incorrect to say \.{xchr[@@\'101]:="A"}, because |"A"| is a constant of
+type |integer|, not |char|, and because we have $|"A"|=65$ regardless of
+the external character set.
+
+@<Set init...@>=
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';@/
+xchr[0]:=' '; xchr[@'177]:=' '; {these ASCII codes are not used}
+
+@ Some of the ASCII codes below @'40 have been given symbolic names in
+\.{WEAVE} and \.{TANGLE} because they are used with a special meaning.
+
+@d and_sign=@'4 {equivalent to `\.{and}'}
+@d not_sign=@'5 {equivalent to `\.{not}'}
+@d set_element_sign=@'6 {equivalent to `\.{in}'}
+@d tab_mark=@'11 {ASCII code used as tab-skip}
+@d line_feed=@'12 {ASCII code thrown away at end of line}
+@d form_feed=@'14 {ASCII code used at end of page}
+@d carriage_return=@'15 {ASCII code used at end of line}
+@d left_arrow=@'30 {equivalent to `\.{:=}'}
+@d not_equal=@'32 {equivalent to `\.{<>}'}
+@d less_or_equal=@'34 {equivalent to `\.{<=}'}
+@d greater_or_equal=@'35 {equivalent to `\.{>=}'}
+@d equivalence_sign=@'36 {equivalent to `\.{==}'}
+@d or_sign=@'37 {equivalent to `\.{or}'}
+
+@ 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 init...@>=
+@!i:0..255;
+
+@ Here now is the system-dependent part of the character set.
+If \.{WEB} is being implemented on a garden-variety \PASCAL\ for which
+only standard ASCII codes will appear in the input and output files, you
+don't need to make any changes here. But if you have, for example, an extended
+character set like the one in Appendix~C of {\sl The \TeX book}, the first
+line of code in this module should be changed to
+$$\hbox{|for i:=1 to @'37 do xchr[i]:=chr(i);|}$$
+\.{WEB}'s character set is essentially identical to \TeX's, even with respect to
+characters less than @'40.
+@^system dependencies@>
+
+Changes to the present module will make \.{WEB} more friendly on computers
+that have an extended character set, so that one can type things like
+\.^^Z\ instead of \.{<>}. If you have an extended set of characters that
+are easily incorporated into text files, you can assign codes arbitrarily
+here, giving an |xchr| equivalent to whatever characters the users of
+\.{WEB} are allowed to have in their input files, provided that unsuitable
+characters do not correspond to special codes like |carriage_return|
+that are listed above.
+
+(The present file \.{WEAVE.WEB} does not contain any of the non-ASCII
+characters, because it is intended to be used with all implementations of
+\.{WEB}. It 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 init...@>=
+for i:=1 to @'37 do xchr[i]:=' ';
+for i:=@'200 to @'377 do xchr[i]:=' ';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=" ";
+for i:=1 to @'377 do xord[xchr[i]]:=i;
+xord[' ']:=" ";
+
+@* Input and output.
+The input conventions of this program are intended to be very much like those
+of \TeX\ (except, of course, that they are much simpler, because much less
+needs to be done). Furthermore they are identical to those of \.{TANGLE}.
+Therefore people who need to make modifications to all three systems
+should be able to do so without too many headaches.
+
+We use the standard \PASCAL\ input/output procedures in several places that
+\TeX\ cannot, since \.{WEAVE} does not have to deal with files that are named
+dynamically by the user, and since there is no input from the terminal.
+
+@ Terminal output is done by writing on file |term_out|, which is assumed to
+consist of characters of type |text_char|:
+@^system dependencies@>
+
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@d print_ln(#)==write_ln(term_out,#) {`|print|' and then start new line}
+@d new_line==write_ln(term_out) {start new line}
+@d print_nl(#)== {print information starting on a new line}
+ begin new_line; print(#);
+ end
+
+@<Globals...@>=
+@!term_out:text_file; {the terminal as an output file}
+
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal. Here is one way to do this
+on the \PASCAL\ system that was used in \.{TANGLE}'s initial development:
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
+
+@ The |update_terminal| procedure is called when we want
+to make sure that everything we have output to the terminal so far has
+actually left the computer's internal buffers and been sent.
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+
+@ The main input comes from |web_file|; this input may be overridden
+by changes in |change_file|. (If |change_file| is empty, there are no changes.)
+
+@<Globals...@>=
+@!web_file:text_file; {primary input}
+@!change_file:text_file; {updates}
+
+@ The following code opens the input 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. We will see below that
+\.{WEAVE} reads through the entire input twice.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read |web_file| and |change_file|}
+begin reset(web_file); reset(change_file);
+end;
+
+@ The main output goes to |tex_file|.
+
+@<Globals...@>=
+@!tex_file: text_file;
+
+@ The following code opens |tex_file|.
+Since this file was listed in the program header, we assume that the
+\PASCAL\ runtime system has checked that a suitable external file name has
+been given.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(tex_file);
+
+@ Input goes into an array called |buffer|.
+
+@<Globals...@>=@!buffer: array[0..long_buf_size] of ASCII_code;
+
+@ The |input_ln| procedure brings the next line of input from the specified
+file into the |buffer| array and returns the value |true|, unless the file has
+already been entirely read, in which case it returns |false|. The conventions
+of \TeX\ are followed; i.e., |ASCII_code| numbers representing the next line
+of the file are input into |buffer[0]|, |buffer[1]|, \dots,
+|buffer[limit-1]|; trailing blanks are ignored;
+and the global variable |limit| is set to the length of the
+@^system dependencies@>
+line. The value of |limit| must be strictly less than |buf_size|.
+
+We assume that none of the |ASCII_code| values
+of |buffer[j]| for |0<=j<limit| is equal to 0, @'177, |line_feed|, |form_feed|,
+or |carriage_return|. Since |buf_size| is strictly less than |long_buf_size|,
+some of \.{WEAVE}'s routines use the fact that it is safe to refer to
+|buffer[limit+2]| without overstepping the bounds of the array.
+
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+var final_limit:0..buf_size; {|limit| without trailing blanks}
+begin limit:=0; final_limit:=0;
+if eof(f) then input_ln:=false
+else begin while not eoln(f) do
+ begin buffer[limit]:=xord[f^]; get(f);
+ incr(limit);
+ if buffer[limit-1]<>" " then final_limit:=limit;
+ if limit=buf_size then
+ begin while not eoln(f) do get(f);
+ decr(limit); {keep |buffer[buf_size]| empty}
+ if final_limit>limit then final_limit:=limit;
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ end;
+ read_ln(f); limit:=final_limit; input_ln:=true;
+ end;
+end;
+
+@* Reporting errors to the user.
+The \.{WEAVE} processor operates in three phases: first it inputs the source
+file and stores cross-reference data, then it inputs the source once again and
+produces the \TeX\ output file, and finally it sorts and outputs the index.
+
+The global variables |phase_one| and |phase_three| tell which Phase we are in.
+
+@<Globals...@>=
+@!phase_one: boolean; {|true| in Phase I, |false| in Phases II and III}
+@!phase_three: boolean; {|true| in Phase III, |false| in Phases I and II}
+
+@ If an error is detected while we are debugging,
+we usually want to look at the contents of memory.
+A special procedure will be declared later for this purpose.
+
+@<Error handling...@>=
+@!debug@+ procedure debug_help; forward;@+gubed
+
+@ The command `|err_print('! Error message')|' will report a syntax error to
+the user, by printing the error message at the beginning of a new line and
+then giving 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|.
+However, error messages are not actually reported during phase one,
+since errors detected on the first pass will be detected again
+during the second.
+
+@d err_print(#)==
+ begin if not phase_one then
+ begin new_line; print(#); error;
+ end;
+ end
+
+@<Error handling...@>=
+procedure error; {prints `\..' and location of error message}
+var@!k,@!l: 0..long_buf_size; {indices into |buffer|}
+begin @<Print error location based on input buffer@>;
+update_terminal; mark_error;
+@!debug debug_skipped:=debug_cycle;debug_help;@+gubed
+end;
+
+@ The error locations can be indicated by using the global variables
+|loc|, |line|, and |changing|, which tell respectively the first
+unlooked-at position in |buffer|, the current line number, and whether or not
+the current line is from |change_file| or |web_file|.
+This routine should be modified on systems whose standard text editor
+has special line-numbering conventions.
+@^system dependencies@>
+
+@<Print error location based on input buffer@>=
+begin if changing then print('. (change file ')@+else print('. (');
+print_ln('l.', 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}
+if buffer[limit]="|" then print(xchr["|"]);
+ {end of \PASCAL\ text in module names}
+print(' '); {this space separates the message from future asterisks}
+end
+
+@ The |jump_out| procedure just cuts across all active procedure levels
+and jumps out of the program. This is the only non-local \&{goto} statement
+in \.{WEAVE}. It is used when no recovery from a particular error has
+been provided.
+
+Some \PASCAL\ compilers do not implement non-local |goto| statements.
+@^system dependencies@>
+In such cases the code that appears at label |end_of_WEAVE| should be
+copied into the |jump_out| procedure, followed by a call to a system procedure
+that terminates the program.
+
+@d fatal_error(#)==begin new_line; print(#); error; mark_fatal; jump_out;
+ end
+
+@<Error handling...@>=
+procedure jump_out;
+begin goto end_of_WEAVE;
+end;
+
+@ Sometimes the program's behavior is far different from what it should be,
+and \.{WEAVE} prints an error message that is really for the \.{WEAVE}
+maintenance person, not the user. In such cases the program says
+|confusion('indication of where we are')|.
+
+@d confusion(#)==fatal_error('! This can''t happen (',#,')')
+@.This can't happen@>
+
+@ An overflow stop occurs if \.{WEAVE}'s tables aren't large enough.
+
+@d overflow(#)==fatal_error('! Sorry, ',#,' capacity exceeded')
+@.Sorry, x capacity exceeded@>
+
+@* Data structures.
+During the first phase of its processing, \.{WEAVE} puts identifier names,
+index entries, and module names into the large |byte_mem| array, which is
+packed with eight-bit integers. Allocation is sequential, since names are
+never deleted.
+
+An auxiliary array |byte_start| is used as a directory for |byte_mem|,
+and the |link|, |ilk|, and |xref| arrays give further information about names.
+These auxiliary arrays consist of sixteen-bit items.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!sixteen_bits=0..65535; {unsigned two-byte quantity}
+
+@ \.{WEAVE} has been designed to avoid the need for indices that are more
+than sixteen bits wide, so that it can be used on most computers. But
+there are programs that need more than 65536 bytes; \TeX\ is one of these.
+To get around this problem, a slight complication has been added to the
+data structures: |byte_mem| is a two-dimensional array, whose first index
+is either 0 or 1. (For generality, the first index is actually allowed to
+run between 0 and |ww-1|, where |ww| is defined to be 2; the program will
+work for any positive value of |ww|, and it can be simplified in obvious
+ways if |ww=1|.)
+
+@d ww=2 {we multiply the byte capacity by approximately this amount}
+
+@<Globals...@>=
+@!byte_mem: packed array [0..ww-1,0..max_bytes] of ASCII_code;
+ {characters of names}
+@!byte_start: array [0..max_names] of sixteen_bits; {directory into |byte_mem|}
+@!link: array [0..max_names] of sixteen_bits; {hash table or tree links}
+@!ilk: array [0..max_names] of sixteen_bits; {type codes or tree links}
+@!xref: array [0..max_names] of sixteen_bits; {heads of cross-reference lists}
+
+@ The names of identifiers are found by computing a hash address |h| and
+then looking at strings of bytes signified by |hash[h]|, |link[hash[h]]|,
+|link[link[hash[h]]]|, \dots, until either finding the desired name
+or encountering a zero.
+
+A `|name_pointer|' variable, which signifies a name, is an index into
+|byte_start|. The actual sequence of characters in the name pointed to by
+|p| appears in positions |byte_start[p]| to |byte_start[p+ww]-1|, inclusive,
+in the segment of |byte_mem| whose first index is |p mod ww|. Thus, when
+|ww=2| the even-numbered name bytes appear in |byte_mem[0,@t$*$@>]|
+and the odd-numbered ones appear in |byte_mem[1,@t$*$@>]|.
+The pointer 0 is used for undefined module names; we don't
+want to use it for the names of identifiers, since 0 stands for a null
+pointer in a linked list.
+
+We usually have |byte_start[name_ptr+w]=byte_ptr[(name_ptr+w) mod ww]|
+for |0<=w<ww|, since these are the starting positions for the next |ww|
+names to be stored in |byte_mem|.
+
+@d length(#)==byte_start[#+ww]-byte_start[#] {the length of a name}
+
+@<Types...@>=
+@!name_pointer=0..max_names; {identifies a name}
+
+@ @<Global...@>=
+@!name_ptr:name_pointer; {first unused position in |byte_start|}
+@!byte_ptr:array [0..ww-1] of 0..max_bytes;
+ {first unused position in |byte_mem|}
+
+@ @<Local variables for init...@>=
+@!wi: 0..ww-1; {to initialize the |byte_mem| indices}
+
+@ @<Set init...@>=
+for wi:=0 to ww-1 do
+ begin byte_start[wi]:=0; byte_ptr[wi]:=0;
+ end;
+byte_start[ww]:=0; {this makes name 0 of length zero}
+name_ptr:=1;
+
+@ Several types of identifiers are distinguished by their |ilk|:
+
+\yskip\hang |normal| identifiers are part of the \PASCAL\ program and
+will appear in italic type.
+
+\yskip\hang |roman| identifiers are index entries that appear after
+\.{@@\^} in the \.{WEB} file.
+
+\yskip\hang |wildcard| identifiers are index entries that appear after
+\.{@@:} in the \.{WEB} file.
+
+\yskip\hang |typewriter| identifiers are index entries that appear after
+\.{@@.} in the \.{WEB} file.
+
+\yskip\hang |array_like|, |begin_like|, \dots, |var_like|
+identifiers are \PASCAL\ reserved words whose |ilk| explains how they are
+to be treated when \PASCAL\ code is being formatted.
+
+\yskip\hang Finally, if |c| is an ASCII code, an |ilk| equal to
+|char_like+c| denotes a reserved word that will be converted to character
+|c|.
+
+@d normal=0 {ordinary identifiers have |normal| ilk}
+@d roman=1 {normal index entries have |roman| ilk}
+@d wildcard=2 {user-formatted index entries have |wildcard| ilk}
+@d typewriter=3 {`typewriter type' entries have |typewriter| ilk}
+@d reserved(#)==(ilk[#]>typewriter) {tells if a name is a reserved word}
+@d array_like=4 {\&{array}, \&{file}, \&{set}}
+@d begin_like=5 {\&{begin}}
+@d case_like=6 {\&{case}}
+@d const_like=7 {\&{const}, \&{label}, \&{type}}
+@d div_like=8 {\&{div}, \&{mod}}
+@d do_like=9 {\&{do}, \&{of}, \&{then}}
+@d else_like=10 {\&{else}}
+@d end_like=11 {\&{end}}
+@d for_like=12 {\&{for}, \&{while}, \&{with}}
+@d goto_like=13 {\&{goto}, \&{packed}}
+@d if_like=14 {\&{if}}
+@d in_like=15 {\&{in}}
+@d nil_like=16 {\&{nil}}
+@d proc_like=17 {\&{function}, \&{procedure}, \&{program}}
+@d record_like=18 {\&{record}}
+@d repeat_like=19 {\&{repeat}}
+@d to_like=20 {\&{downto}, \&{to}}
+@d until_like=21 {\&{until}}
+@d var_like=22 {\&{var}}
+@d loop_like=23 {\&{loop}, \&{xclause}}
+@d char_like=24 {\&{and}, \&{or}, \&{not}, \&{in}}
+
+@ The names of modules are stored in |byte_mem| together
+with the identifier names, but a hash table is not used for them because
+\.{WEAVE} needs to be able to recognize a module name when given a prefix of
+that name. A conventional binary seach tree is used to retrieve module names,
+with fields called |llink| and |rlink| in place of |link| and |ilk|. The
+root of this tree is |rlink[0]|.
+
+@d llink==link {left link in binary search tree for module names}
+@d rlink==ilk {right link in binary search tree for module names}
+@d root==rlink[0] {the root of the binary search tree for module names}
+
+@<Set init...@>=
+root:=0; {the binary search tree starts out with nothing in it}
+
+@ Here is a little procedure that prints the text of a given name on the
+user's terminal.
+
+@p procedure print_id(@!p:name_pointer); {print identifier or module name}
+var k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {row of |byte_mem|}
+begin if p>=name_ptr then print('IMPOSSIBLE')
+else begin w:=p mod ww;
+ for k:=byte_start[p] to byte_start[p+ww]-1 do
+ print(xchr[byte_mem[w,k]]);
+ end;
+end;
+
+@ We keep track of the current module number in
+|module_count|, which is the total number of modules that have started.
+Modules which have been altered by a change file entry
+have their |changed_module| flag turned on during the first phase.
+
+@<Globals...@>=
+@!module_count:0..max_modules; {the current module number}
+@!changed_module: packed array [0..max_modules] of boolean; {is it changed?}
+@!change_exists: boolean; {has any module changed?}
+
+@ The other large memory area in \.{WEAVE} keeps the cross-reference data.
+All uses of the name |p| are recorded in a linked list beginning at
+|xref[p]|, which points into the |xmem| array. Entries in |xmem| consist
+of two sixteen-bit items per word, called the |num| and |xlink| fields.
+If |x| is an index into |xmem|, reached from name |p|, the value of |num(x)|
+is either a module number where |p| is used, or it is |def_flag| plus a
+module number where |p| is defined; and |xlink(x)| points to the next such
+cross reference for |p|, if any. This list of cross references is in
+decreasing order by module number. The current number of cross references
+is |xref_ptr|.
+
+The global variable |xref_switch| is set either to |def_flag| or to zero,
+depending on whether the next cross reference to an identifier is to be
+underlined or not in the index. This switch is set to |def_flag| when
+\.{@@!} or \.{@@d} or \.{@@f} is scanned, and it is cleared to zero when
+the next identifier or index entry cross reference has been made. Similarly,
+the global variable |mod_xref_switch| is either |def_flag| or zero, depending
+on whether a module name is being defined or used.
+
+@d num(#)==xmem[#].num_field
+@d xlink(#)==xmem[#].xlink_field
+@d def_flag=10240 {must be strictly larger than |max_modules|}
+
+@ @<Types...@>=
+@!xref_number=0..max_refs;
+
+@ @<Globals...@>=
+@!xmem:array[xref_number] of packed record@t@>@/
+ @!num_field: sixteen_bits; {module number plus zero or |def_flag|}
+ @!xlink_field: sixteen_bits; {pointer to the previous cross reference}
+ end;
+@!xref_ptr:xref_number; {the largest occupied position in |xmem|}
+@!xref_switch,@!mod_xref_switch:0..def_flag; {either zero or |def_flag|}
+
+@ @<Set init...@>=xref_ptr:=0; xref_switch:=0; mod_xref_switch:=0; num(0):=0;
+xref[0]:=0; {cross references to undefined modules}
+
+@ A new cross reference for an identifier is formed by calling |new_xref|,
+which discards duplicate entries and ignores non-underlined references
+to one-letter identifiers or \PASCAL's reserved words.
+
+@d append_xref(#)==if xref_ptr=max_refs then overflow('cross reference')
+ else begin incr(xref_ptr); num(xref_ptr):=#;
+ end
+
+@p procedure new_xref(@!p:name_pointer);
+label exit;
+var q:xref_number; {pointer to previous cross reference}
+@!m,@!n: sixteen_bits; {new and previous cross-reference value}
+begin if (reserved(p)or(byte_start[p]+1=byte_start[p+ww]))and
+ (xref_switch=0) then return;
+m:=module_count+xref_switch; xref_switch:=0; q:=xref[p];
+if q>0 then
+ begin n:=num(q);
+ if (n=m)or(n=m+def_flag) then return
+ else if m=n+def_flag then
+ begin num(q):=m; return;
+ end;
+ end;
+append_xref(m); xlink(xref_ptr):=q; xref[p]:=xref_ptr;
+exit: end;
+
+@ The cross reference lists for module names are slightly different. Suppose
+that a module name is defined in modules $m_1$, \dots, $m_k$ and used in
+modules $n_1$, \dots, $n_l$. Then its list will contain $m_1+|def_flag|$,
+$m_k+|def_flag|$, \dots, $m_2+|def_flag|$, $n_l$, \dots, $n_1$, in
+this order. After Phase II, however, the order will be
+$m_1+|def_flag|$, \dots, $m_k+|def_flag|$, $n_1$, \dots, $n_l$.
+
+@p procedure new_mod_xref(@!p:name_pointer);
+var q,@!r:xref_number; {pointers to previous cross references}
+begin q:=xref[p]; r:=0;
+if q>0 then
+ begin if mod_xref_switch=0 then while num(q)>=def_flag do
+ begin r:=q; q:=xlink(q);
+ end
+ else if num(q)>=def_flag then
+ begin r:=q; q:=xlink(q);
+ end;
+ end;
+append_xref(module_count+mod_xref_switch); xlink(xref_ptr):=q;
+mod_xref_switch:=0;
+if r=0 then xref[p]:=xref_ptr
+else xlink(r):=xref_ptr;
+end;
+
+@ A third large area of memory is used for sixteen-bit `tokens', which appear
+in short lists similar to the strings of characters in |byte_mem|. Token lists
+are used to contain the result of \PASCAL\ code translated into \TeX\ form;
+further details about them will be explained later. A |text_pointer| variable
+is an index into |tok_start|.
+
+@<Types...@>=
+@!text_pointer=0..max_texts; {identifies a token list}
+
+@ The first position of |tok_mem|
+that is unoccupied by replacement text is called |tok_ptr|, and the first
+unused location of |tok_start| is called |text_ptr|.
+Thus, we usually have |tok_start[text_ptr]=tok_ptr|.
+
+@<Glob...@>=
+@t\hskip1em@>@!tok_mem: packed array [0..max_toks] of sixteen_bits; {tokens}
+@t\hskip1em@>@!tok_start: array [text_pointer] of sixteen_bits;
+ {directory into |tok_mem|}
+@t\hskip1em@>@!text_ptr:text_pointer; {first unused position in |tok_start|}
+@t\hskip1em@>@!tok_ptr:0..max_toks; {first unused position in |tok_mem|}
+stat@!max_tok_ptr,@!max_txt_ptr:0..max_toks; {largest values occurring}
+tats
+
+@ @<Set init...@>=
+tok_ptr:=1; text_ptr:=1; tok_start[0]:=1; tok_start[1]:=1;
+stat max_tok_ptr:=1; max_txt_ptr:=1;@+tats
+
+@* Searching for identifiers.
+The hash table described above is updated by the |id_lookup| procedure,
+which finds a given identifier and returns a pointer to its index in
+|byte_start|. The identifier is supposed to match character by character
+and it is also supposed to have a given |ilk| code; the same name may be
+present more than once if it is supposed to appear in the index with
+different typesetting conventions.
+If the identifier was not already present, it is inserted into the table.
+
+Because of the way \.{WEAVE}'s scanning mechanism works, it is most convenient
+to let |id_lookup| search for an identifier that is present in the |buffer|
+array. Two other global variables specify its position in the buffer: the
+first character is |buffer[id_first]|, and the last is |buffer[id_loc-1]|.
+
+@<Glob...@>=
+@!id_first:0..long_buf_size; {where the current identifier begins in the buffer}
+@!id_loc:0..long_buf_size; {just after the current identifier in the buffer}
+@#
+@!hash:array [0..hash_size] of sixteen_bits; {heads of hash lists}
+
+@ Initially all the hash lists are empty.
+
+@<Local variables for init...@>=
+@!h:0..hash_size; {index into hash-head array}
+
+@ @<Set init...@>=
+for h:=0 to hash_size-1 do hash[h]:=0;
+
+@ Here now is the main procedure for finding identifiers (and index
+entries). The parameter |t| is set to the desired |ilk| code. The
+identifier must either have |ilk=t|, or we must have
+|t=normal| and the identifier must be a reserved word.
+
+@p function id_lookup(@!t:eight_bits):name_pointer; {finds current identifier}
+label found;
+var i:0..long_buf_size; {index into |buffer|}
+@!h:0..hash_size; {hash code}
+@!k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {row of |byte_mem|}
+@!l:0..long_buf_size; {length of the given identifier}
+@!p:name_pointer; {where the identifier is being sought}
+begin l:=id_loc-id_first; {compute the length}
+@<Compute the hash code |h|@>;
+@<Compute the name location |p|@>;
+if p=name_ptr then @<Enter a new name into the table at position |p|@>;
+id_lookup:=p;
+end;
+
+@ A simple hash code is used: If the sequence of
+ASCII codes is $c_1c_2\ldots c_m$, its hash value will be
+$$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,|hash_size|.$$
+
+@<Compute the hash...@>=
+h:=buffer[id_first]; i:=id_first+1;
+while i<id_loc do
+ begin h:=(h+h+buffer[i]) mod hash_size; incr(i);
+ end
+
+@ If the identifier is new, it will be placed in position |p=name_ptr|,
+otherwise |p| will point to its existing location.
+
+@<Compute the name location...@>=
+p:=hash[h];
+while p<>0 do
+ begin if (length(p)=l)and((ilk[p]=t)or((t=normal)and reserved(p))) then
+ @<Compare name |p| with current identifier,
+ |goto found| if equal@>;
+ p:=link[p];
+ end;
+p:=name_ptr; {the current identifier is new}
+link[p]:=hash[h]; hash[h]:=p; {insert |p| at beginning of hash list}
+found:
+
+@ @<Compare name |p|...@>=
+begin i:=id_first; k:=byte_start[p]; w:=p mod ww;
+while (i<id_loc)and(buffer[i]=byte_mem[w,k]) do
+ begin incr(i); incr(k);
+ end;
+if i=id_loc then goto found; {all characters agree}
+end
+
+@ When we begin the following segment of the program, |p=name_ptr|.
+
+@<Enter a new name...@>=
+begin w:=name_ptr mod ww;
+if byte_ptr[w]+l>max_bytes then overflow('byte memory');
+if name_ptr+ww>max_names then overflow('name');
+i:=id_first; k:=byte_ptr[w]; {get ready to move the identifier into |byte_mem|}
+while i<id_loc do
+ begin byte_mem[w,k]:=buffer[i]; incr(k); incr(i);
+ end;
+byte_ptr[w]:=k; byte_start[name_ptr+ww]:=k; incr(name_ptr);
+ilk[p]:=t; xref[p]:=0;
+end
+
+@* Initializing the table of reserved words.
+We have to get \PASCAL's reserved words into the hash table, and the
+simplest way to do this is to insert them every time \.{WEAVE} is run.
+A few macros permit us to do the initialization with a compact program.
+
+@d sid9(#)==buffer[9]:=#;cur_name:=id_lookup
+@d sid8(#)==buffer[8]:=#;sid9
+@d sid7(#)==buffer[7]:=#;sid8
+@d sid6(#)==buffer[6]:=#;sid7
+@d sid5(#)==buffer[5]:=#;sid6
+@d sid4(#)==buffer[4]:=#;sid5
+@d sid3(#)==buffer[3]:=#;sid4
+@d sid2(#)==buffer[2]:=#;sid3
+@d sid1(#)==buffer[1]:=#;sid2
+@d id2==id_first:=8; sid8
+@d id3==id_first:=7; sid7
+@d id4==id_first:=6; sid6
+@d id5==id_first:=5; sid5
+@d id6==id_first:=4; sid4
+@d id7==id_first:=3; sid3
+@d id8==id_first:=2; sid2
+@d id9==id_first:=1; sid1
+
+@<Globals...@>=
+@!cur_name:name_pointer; {points to the identifier just inserted}
+
+@ The intended use of the macros above might not be immediately obvious,
+but the riddle is answered by the following:
+
+@<Store all the reserved words@>=
+id_loc:=10;@/
+id3("a")("n")("d")(char_like+and_sign);@/
+id5("a")("r")("r")("a")("y")(array_like);@/
+id5("b")("e")("g")("i")("n")(begin_like);@/
+id4("c")("a")("s")("e")(case_like);@/
+id5("c")("o")("n")("s")("t")(const_like);@/
+id3("d")("i")("v")(div_like);@/
+id2("d")("o")(do_like);@/
+id6("d")("o")("w")("n")("t")("o")(to_like);@/
+id4("e")("l")("s")("e")(else_like);@/
+id3("e")("n")("d")(end_like);@/
+id4("f")("i")("l")("e")(array_like);@/
+id3("f")("o")("r")(for_like);@/
+id8("f")("u")("n")("c")("t")("i")("o")("n")(proc_like);@/
+id4("g")("o")("t")("o")(goto_like);@/
+id2("i")("f")(if_like);@/
+id2("i")("n")(char_like+set_element_sign);@/
+id5("l")("a")("b")("e")("l")(const_like);@/
+id3("m")("o")("d")(div_like);@/
+id3("n")("i")("l")(nil_like);@/
+id3("n")("o")("t")(char_like+not_sign);@/
+id2("o")("f")(do_like);@/
+id2("o")("r")(char_like+or_sign);@/
+id6("p")("a")("c")("k")("e")("d")(goto_like);@/
+id9("p")("r")("o")("c")("e")("d")("u")("r")("e")(proc_like);@/
+id7("p")("r")("o")("g")("r")("a")("m")(proc_like);@/
+id6("r")("e")("c")("o")("r")("d")(record_like);@/
+id6("r")("e")("p")("e")("a")("t")(repeat_like);@/
+id3("s")("e")("t")(array_like);@/
+id4("t")("h")("e")("n")(do_like);@/
+id2("t")("o")(to_like);@/
+id4("t")("y")("p")("e")(const_like);@/
+id5("u")("n")("t")("i")("l")(until_like);@/
+id3("v")("a")("r")(var_like);@/
+id5("w")("h")("i")("l")("e")(for_like);@/
+id4("w")("i")("t")("h")(for_like);@/
+id7("x")("c")("l")("a")("u")("s")("e")(loop_like);@/
+
+@* Searching for module names.
+The |mod_lookup| procedure finds the module name |mod_text[1..l]| in the
+search tree, after inserting it if necessary, and returns a pointer to
+where it was found.
+
+@<Glob...@>=
+@!mod_text:array [0..longest_name] of ASCII_code; {name being sought for}
+
+@ According to the rules of \.{WEB}, no module name
+should be a proper prefix of another, so a ``clean'' comparison should
+occur between any two names. The result of |mod_lookup| is 0 if this
+prefix condition is violated. An error message is printed when such violations
+are detected during phase two of \.{WEAVE}.
+
+@d less=0 {the first name is lexicographically less than the second}
+@d equal=1 {the first name is equal to the second}
+@d greater=2 {the first name is lexicographically greater than the second}
+@d prefix=3 {the first name is a proper prefix of the second}
+@d extension=4 {the first name is a proper extension of the second}
+
+@p function mod_lookup(@!l:sixteen_bits):name_pointer; {finds module name}
+label found;
+var c:less..extension; {comparison between two names}
+@!j:0..longest_name; {index into |mod_text|}
+@!k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {row of |byte_mem|}
+@!p:name_pointer; {current node of the search tree}
+@!q:name_pointer; {father of node |p|}
+begin c:=greater; q:=0; p:=root;
+while p<>0 do
+ begin @<Set variable |c| to the result of comparing the given name
+ to name |p|@>;
+ q:=p;
+ if c=less then p:=llink[q]
+ else if c=greater then p:=rlink[q]
+ else goto found;
+ end;
+@<Enter a new module name into the tree@>;
+found: if c<>equal then
+ begin err_print('! Incompatible section names'); p:=0;
+@.Incompatible section names@>
+ end;
+mod_lookup:=p;
+end;
+
+@ @<Enter a new module name...@>=
+w:=name_ptr mod ww; k:=byte_ptr[w];
+if k+l>max_bytes then overflow('byte memory');
+if name_ptr>max_names-ww then overflow('name');
+p:=name_ptr;
+if c=less then llink[q]:=p else rlink[q]:=p;
+llink[p]:=0; rlink[p]:=0; xref[p]:=0; c:=equal;
+for j:=1 to l do byte_mem[w,k+j-1]:=mod_text[j];
+byte_ptr[w]:=k+l; byte_start[name_ptr+ww]:=k+l; incr(name_ptr);
+
+@ @<Set variable |c|...@>=
+begin k:=byte_start[p]; w:=p mod ww; c:=equal; j:=1;
+while (k<byte_start[p+ww]) and (j<=l) and (mod_text[j]=byte_mem[w,k]) do
+ begin incr(k); incr(j);
+ end;
+if k=byte_start[p+ww] then
+ if j>l then c:=equal
+ else c:=extension
+else if j>l then c:=prefix
+else if mod_text[j]<byte_mem[w,k] then c:=less
+else c:=greater;
+end
+
+@ The |prefix_lookup| procedure is supposed to find exactly one module
+name that has |mod_text[1..l]| as a prefix. Actually the algorithm
+silently accepts also the situation that some module name is a prefix of
+|mod_text[1..l]|, because the user who painstakingly typed in more than
+necessary probably doesn't want to be told about the wasted effort.
+
+Recall that error messages are not printed during phase one. It is
+possible that the |prefix_lookup| procedure will fail on the first pass,
+because there is no match, yet the second pass might detect no error if a
+matching module name has occurred after the offending prefix. In such a
+case the cross-reference information will be incorrect and \.{WEAVE} will
+report no error. However, such a mistake will be detected by the
+\.{TANGLE} processor.
+
+@p function prefix_lookup(@!l:sixteen_bits):name_pointer; {finds name extension}
+var c:less..extension; {comparison between two names}
+@!count:0..max_names; {the number of hits}
+@!j:0..longest_name; {index into |mod_text|}
+@!k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {row of |byte_mem|}
+@!p:name_pointer; {current node of the search tree}
+@!q:name_pointer; {another place to resume the search after one branch is done}
+@!r:name_pointer; {extension found}
+begin q:=0; p:=root; count:=0; r:=0; {begin search at root of tree}
+while p<>0 do
+ begin @<Set variable |c| to the result of comparing...@>;
+ if c=less then p:=llink[p]
+ else if c=greater then p:=rlink[p]
+ else begin r:=p; incr(count); q:=rlink[p]; p:=llink[p];
+ end;
+ if p=0 then
+ begin p:=q; q:=0;
+ end;
+ end;
+if count<>1 then
+ if count=0 then err_print('! Name does not match')
+@.Name does not match@>
+ else err_print('! Ambiguous prefix');
+@.Ambiguous prefix@>
+prefix_lookup:=r; {the result will be 0 if there was no match}
+end;
+
+@* Lexical scanning.
+Let us now consider the subroutines that read the \.{WEB} source file
+and break it into meaningful units. There are four such procedures:
+One simply skips to the next `\.{@@\ }' or `\.{@@*}' that begins a
+module; another passes over the \TeX\ text at the beginning of a
+module; the third passes over the \TeX\ text in a \PASCAL\ comment;
+and the last, which is the most interesting, gets the next token of
+a \PASCAL\ text.
+
+@ But first we need to consider the low-level routine |get_line|
+that takes care of merging |change_file| into |web_file|. The |get_line|
+procedure also updates the line numbers for error messages.
+
+@<Globals...@>=
+@!ii:integer; {general purpose |for| loop variable in the outer block}
+@!line:integer; {the number of the current line in the current file}
+@!other_line:integer; {the number of the current line in the input file that
+ is not currently being read}
+@!temp_line:integer; {used when interchanging |line| with |other_line|}
+@!limit:0..long_buf_size; {the last character position occupied in the buffer}
+@!loc:0..long_buf_size; {the next character position to be read from the buffer}
+@!input_has_ended: boolean; {if |true|, there is no more input}
+@!changing: boolean; {if |true|, the current line is from |change_file|}
+@!change_pending: boolean; {if |true|, the current change is not yet
+ recorded in |changed_module[module_count]|}
+
+@ As we change |changing| from |true| to |false| and back again, we must
+remember to swap the values of |line| and |other_line| so that the |err_print|
+routine will be sure to report the correct line number.
+
+@d change_changing==
+ changing := not changing;
+ temp_line:=other_line; other_line:=line; line:=temp_line
+ {|line @t$\null\BA\null$@> other_line|}
+
+@ 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.
+
+@<Globals...@>=
+@!change_buffer:array[0..buf_size] of ASCII_code;
+@!change_limit:0..buf_size; {the last position occupied in |change_buffer|}
+
+@ Here's a simple function that checks if the two buffers are different.
+
+@p 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 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;
+exit: end;
+
+@ Procedure |prime_the_change_buffer| sets |change_buffer| in preparation
+for the next matching operation. Since blank lines in the change file are
+not used for matching, we have |(change_limit=0)and not changing| if and
+only if the change file is exhausted. This procedure is called only
+when |changing| is true; hence error messages will be reported correctly.
+
+@p 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@>;
+@<Skip to the next nonblank line; |return| if end of file@>;
+@<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>;
+exit: end;
+
+@ While looking for a line that begins with \.{@@x} in the change file,
+we allow lines that begin with \.{@@}, as long as they don't begin with
+\.{@@y} or \.{@@z} (which would probably indicate that the change file is
+fouled up).
+
+@<Skip over comment lines in the change file...@>=
+loop@+ begin incr(line);
+ if not input_ln(change_file) then return;
+ if limit<2 then goto continue;
+ if buffer[0]<>"@@" then goto continue;
+ if (buffer[1]>="X")and(buffer[1]<="Z") then
+ buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
+ if buffer[1]="x" then goto done;
+ if (buffer[1]="y")or(buffer[1]="z") then
+ begin loc:=2; err_print('! Where is the matching @@x?');
+@.Where is the match...@>
+ end;
+continue: end;
+done:
+
+@ Here we are looking at lines following the \.{@@x}.
+
+@<Skip to the next nonblank line...@>=
+repeat incr(line);
+ if not input_ln(change_file) then
+ begin err_print('! Change file ended after @@x');
+@.Change file ended...@>
+ return;
+ end;
+until limit>0;
+
+@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>=
+begin change_limit:=limit;
+if limit>0 then for k:=0 to limit-1 do change_buffer[k]:=buffer[k];
+end
+
+@ 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 \.{@@y} 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|.
+
+When a match is found, the current module is marked as changed unless
+the first line after the \.{@@x} and after the \.{@@y} both start with
+either |'@@*'| or |'@@ '| (possibly preceded by whitespace).
+
+@d if_module_start_then_make_change_pending(#)==
+ loc:=0; buffer[limit]:="!";
+ while (buffer[loc]=" ")or(buffer[loc]=tab_mark) do incr(loc);
+ buffer[limit]:=" ";
+ if buffer[loc]="@@" then
+ if (buffer[loc+1]="*") or
+ (buffer[loc+1]=" ") or (buffer[loc+1]=tab_mark) then
+ change_pending:=#
+
+@p procedure check_change; {switches to |change_file| if the buffers match}
+label exit;
+var n:integer; {the number of discrepancies found}
+@!k:0..buf_size; {index into the buffers}
+begin if lines_dont_match then return;
+change_pending:=false;
+if not changed_module[module_count] then
+ begin if_module_start_then_make_change_pending(true);
+ if not change_pending then changed_module[module_count]:=true;
+ end;
+n:=0;
+loop@+ begin change_changing; {now it's |true|}
+ incr(line);
+ if not input_ln(change_file) then
+ begin err_print('! Change file ended before @@y');
+@.Change file ended...@>
+ change_limit:=0; change_changing; {|false| again}
+ return;
+ end;
+ @<If the current line starts with \.{@@y},
+ report any discrepancies and |return|@>;
+ @<Move |buffer| and |limit|...@>;
+ change_changing; {now it's |false|}
+ incr(line);
+ if not input_ln(web_file) then
+ begin err_print('! WEB file ended during a change');
+@.WEB file ended...@>
+ input_has_ended:=true; return;
+ end;
+ if lines_dont_match then incr(n);
+ end;
+exit: end;
+
+@ @<If the current line starts with \.{@@y}...@>=
+if limit>1 then if buffer[0]="@@" then
+ begin if (buffer[1]>="X")and(buffer[1]<="Z") then
+ buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
+ if (buffer[1]="x")or(buffer[1]="z") then
+ begin loc:=2; err_print('! Where is the matching @@y?');
+@.Where is the match...@>
+ end
+ else if buffer[1]="y" then
+ begin if n>0 then
+ begin loc:=2; err_print('! Hmm... ',n:1,
+ ' of the preceding lines failed to match');
+@.Hmm... n of the preceding...@>
+ end;
+ return;
+ end;
+ end
+
+@ The |reset_input| procedure, which gets \.{WEAVE} ready to read the
+user's \.{WEB} input, is used at the beginning of phases one and two.
+
+@p procedure reset_input;
+begin open_input; line:=0; other_line:=0;@/
+changing:=true; prime_the_change_buffer; change_changing;@/
+limit:=0; loc:=1; buffer[0]:=" "; input_has_ended:=false;
+end;
+
+@ The |get_line| procedure is called when |loc>limit|; it puts the next
+line of merged input into the buffer and updates the other variables
+appropriately. A space is placed at the right end of the line.
+
+@p procedure get_line; {inputs the next line}
+label restart;
+begin restart:if changing then
+ @<Read from |change_file| and maybe turn off |changing|@>;
+if not changing then
+ begin @<Read from |web_file| and maybe turn on |changing|@>;
+ if changing then goto restart;
+ end;
+loc:=0; buffer[limit]:=" ";
+end;
+
+@ @<Read from |web_file|...@>=
+begin incr(line);
+if not input_ln(web_file) then input_has_ended:=true
+else if limit=change_limit then
+ if buffer[0]=change_buffer[0] then
+ if change_limit>0 then check_change;
+end
+
+@ @<Read from |change_file|...@>=
+begin incr(line);
+if not input_ln(change_file) then
+ begin err_print('! Change file ended without @@z');
+@.Change file ended...@>
+ buffer[0]:="@@"; buffer[1]:="z"; limit:=2;
+ end;
+if limit>0 then {check if the change has ended}
+ begin if change_pending then
+ begin if_module_start_then_make_change_pending(false);
+ if change_pending then
+ begin changed_module[module_count]:=true; change_pending:=false;
+ end;
+ end;
+ buffer[limit]:=" ";
+ if buffer[0]="@@" then
+ begin if (buffer[1]>="X")and(buffer[1]<="Z") then
+ buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
+ if (buffer[1]="x")or(buffer[1]="y") then
+ begin loc:=2; err_print('! Where is the matching @@z?');
+@.Where is the match...@>
+ end
+ else if buffer[1]="z" then
+ begin prime_the_change_buffer; change_changing;
+ end;
+ end;
+ end;
+end
+
+@ 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@>=
+if change_limit<>0 then {|changing| is false}
+ begin for ii:=0 to change_limit do buffer[ii]:=change_buffer[ii];
+ limit:=change_limit; changing:=true; line:=other_line; loc:=change_limit;
+ err_print('! Change file entry did not match');
+@.Change file entry did not match@>
+ end
+
+@ Control codes in \.{WEB}, which begin with `\.{@@}', are converted
+into a numeric code designed to simplify \.{WEAVE}'s logic; for example,
+larger numbers are given to the control codes that denote more significant
+milestones, and the code of |new_module| should be the largest of
+all. Some of these numeric control codes take the place of ASCII
+control codes that will not otherwise appear in the output of the
+scanning routines.
+@^ASCII code@>
+
+@d ignore=0 {control code of no interest to \.{WEAVE}}
+@d verbatim=@'2 {extended ASCII alpha will not appear}
+@d force_line=@'3 {extended ASCII beta will not appear}
+@d begin_comment=@'11 {ASCII tab mark will not appear}
+@d end_comment=@'12 {ASCII line feed will not appear}
+@d octal=@'14 {ASCII form feed will not appear}
+@d hex=@'15 {ASCII carriage return will not appear}
+@d double_dot=@'40 {ASCII space will not appear except in strings}
+@d no_underline=@'175 {this code will be intercepted without confusion}
+@d underline=@'176 {this code will be intercepted without confusion}
+@d param=@'177 {ASCII delete will not appear}
+@d xref_roman=@'203 {control code for `\.{@@\^}'}
+@d xref_wildcard=@'204 {control code for `\.{@@:}'}
+@d xref_typewriter=@'205 {control code for `\.{@@.}'}
+@d TeX_string=@'206 {control code for `\.{@@t}'}
+@d check_sum=@'207 {control code for `\.{@@\$}'}
+@d join=@'210 {control code for `\.{@@\&}'}
+@d thin_space=@'211 {control code for `\.{@@,}'}
+@d math_break=@'212 {control code for `\.{@@\char'174}'}
+@d line_break=@'213 {control code for `\.{@@/}'}
+@d big_line_break=@'214 {control code for `\.{@@\#}'}
+@d no_line_break=@'215 {control code for `\.{@@+}'}
+@d pseudo_semi=@'216 {control code for `\.{@@;}'}
+@d format=@'217 {control code for `\.{@@f}'}
+@d definition=@'220 {control code for `\.{@@d}'}
+@d begin_Pascal=@'221 {control code for `\.{@@p}'}
+@d module_name=@'222 {control code for `\.{@@<}'}
+@d new_module=@'223 {control code for `\.{@@\ }' and `\.{@@*}'}
+
+@ Control codes are converted from ASCII to \.{WEAVE}'s internal
+representation by the |control_code| routine.
+
+@p function control_code(@!c:ASCII_code):eight_bits; {convert |c|
+ after \.{@@}}
+begin case c of
+"@@": control_code:="@@"; {`quoted' at sign}
+"'": control_code:=octal; {precedes octal constant}
+"""": control_code:=hex; {precedes hexadecimal constant}
+"$": control_code:=check_sum; {precedes check sum constant}
+" ",tab_mark,"*": control_code:=new_module; {beginning of a new module}
+"=": control_code:=verbatim;
+"\": control_code:=force_line;
+"D","d": control_code:=definition; {macro definition}
+"F","f": control_code:=format; {format definition}
+"{": control_code:=begin_comment; {begin-comment delimiter}
+"}": control_code:=end_comment; {end-comment delimiter}
+"P","p": control_code:=begin_Pascal; {\PASCAL\ text in unnamed module}
+"&": control_code:=join; {concatenate two tokens}
+"<": control_code:=module_name; {beginning of a module name}
+">": begin err_print('! Extra @@>'); control_code:=ignore;
+@.Extra \AT!>@>
+ end; {end of module name should not be discovered in this way}
+"T","t": control_code:=TeX_string; {\TeX\ box within \PASCAL}
+"!": control_code:=underline; {set definition flag}
+"?": control_code:=no_underline; {reset definition flag}
+"^": control_code:=xref_roman; {index entry to be typeset normally}
+":": control_code:=xref_wildcard; {index entry to be in user format}
+".": control_code:=xref_typewriter; {index entry to be in typewriter type}
+",": control_code:=thin_space; {puts extra space in \PASCAL\ format}
+"|": control_code:=math_break; {allows a break in a formula}
+"/": control_code:=line_break; {forces end-of-line in \PASCAL\ format}
+"#": control_code:=big_line_break; {forces end-of-line and some space besides}
+"+": control_code:=no_line_break; {cancels end-of-line down to single space}
+";": control_code:=pseudo_semi; {acts like a semicolon, but is invisible}
+@t\4@>@<Special control codes allowed only when debugging@>@;
+othercases begin err_print('! Unknown control code'); control_code:=ignore;
+@.Unknown control code@>
+ end
+endcases;
+end;
+
+@ If \.{WEAVE} is compiled with debugging commands, one can write
+\.{@@2}, \.{@@1}, and \.{@@0} to turn tracing fully on, partly on,
+and off, respectively.
+@.\AT!2@>
+@.\AT!1@>
+
+@<Special control codes...@>=
+@!debug@t@>@/
+"0","1","2": begin tracing:=c-"0"; control_code:=ignore;
+ end;
+gubed
+
+@ The |skip_limbo| routine is used on the first pass to skip through
+portions of the input that are not in any modules, i.e., that precede
+the first module. After this procedure has been called, the value of
+|input_has_ended| will tell whether or not a new module has
+actually been found.
+
+@p procedure skip_limbo; {skip to next module}
+label exit;
+var c:ASCII_code; {character following \.{@@}}
+begin loop if loc>limit then
+ begin get_line;
+ if input_has_ended then return;
+ end
+ else begin buffer[limit+1]:="@@";
+ while buffer[loc]<>"@@" do incr(loc);
+ if loc<=limit then
+ begin loc:=loc+2; c:=buffer[loc-1];
+ if (c=" ")or(c=tab_mark)or(c="*") then return;
+ end;
+ end;
+exit: end;
+
+@ The |skip_TeX| routine is used on the first pass to skip through
+the \TeX\ code at the beginning of a module. It returns the next
+control code or `\v' found in the input. A |new_module| is
+assumed to exist at the very end of the file.
+
+@p function skip_TeX: eight_bits; {skip past pure \TeX\ code}
+label done;
+var c:eight_bits; {control code found}
+begin loop begin if loc>limit then
+ begin get_line;
+ if input_has_ended then
+ begin c:=new_module; goto done;
+ end;
+ end;
+ buffer[limit+1]:="@@";
+ repeat c:=buffer[loc]; incr(loc);
+ if c="|" then goto done;
+ until c="@@";
+ if loc<=limit then
+ begin c:=control_code(buffer[loc]); incr(loc); goto done;
+ end;
+ end;
+done:skip_TeX:=c;
+end;
+
+@ The |skip_comment| routine is used on the first pass to skip
+through \TeX\ code in \PASCAL\ comments. The |bal| parameter
+tells how many left braces are assumed to have been scanned when
+this routine is called, and the procedure returns a corresponding
+value of |bal| at the point that scanning has stopped. Scanning
+stops either at a `\v' that introduces \PASCAL\ text,
+in which case the returned value is positive, or it stops at the
+end of the comment, in which case the returned value is zero.
+The scanning also stops in anomalous situations when the comment
+doesn't end or when it contains an illegal use of \.{@@}.
+One should call |skip_comment(1)| when beginning to scan a comment.
+
+@p function skip_comment(@!bal:eight_bits):eight_bits; {skips \TeX\
+ code in comments}
+label done;
+var c:ASCII_code; {the current character}
+begin loop begin if loc>limit then
+ begin get_line;
+ if input_has_ended then
+ begin bal:=0; goto done;
+ end; {an error message will occur in phase two}
+ end;
+ c:=buffer[loc]; incr(loc);
+ if c="|" then goto done;
+ @<Do special things when |c="@@", "\", "{", "}"|; |goto done| at end@>;
+ end;
+done: skip_comment:=bal;
+end;
+
+@ @<Do special things when |c="@@"...@>=
+if c="@@" then
+ begin c:=buffer[loc];
+ if (c<>" ")and(c<>tab_mark)and(c<>"*") then incr(loc)
+ else begin decr(loc); bal:=0; goto done;
+ end {an error message will occur in phase two}
+ end
+else if (c="\")and(buffer[loc]<>"@@") then incr(loc)
+else if c="{" then incr(bal)
+else if c="}" then
+ begin decr(bal);
+ if bal=0 then goto done;
+ end
+
+@* Inputting the next token.
+As stated above, \.{WEAVE}'s most interesting lexical scanning routine is the
+|get_next| function that inputs the next token of \PASCAL\ input. However,
+|get_next| is not especially complicated.
+
+The result of |get_next| is either an ASCII code for some special character,
+or it is a special code representing a pair of characters (e.g., `\.{:=}'
+or `\.{..}'), or it is the numeric value computed by the |control_code|
+procedure, or it is one of the following special codes:
+
+\yskip\hang |exponent|: The `\.E' in a real constant.
+
+\yskip\hang |identifier|: In this case the global variables |id_first|
+and |id_loc| will have been set to the appropriate values needed by the
+|id_lookup| routine.
+
+\yskip\hang |string|: In this case the global variables |id_first| and
+|id_loc| will have been set to the beginning and ending-plus-one locations
+in the buffer. The string ends with the first reappearance of its initial
+delimiter; thus, for example, $$\.{\'This isn\'\'t a single string\'}$$
+will be treated as two consecutive strings, the first being \.{\'This
+isn\'}.
+
+\yskip\noindent Furthermore, some of the control codes cause
+|get_next| to take additional actions:
+
+\yskip\hang |xref_roman|, |xref_wildcard|,
+|xref_typewriter|, |TeX_string|: The values of
+|id_first| and |id_loc| will be set so that the string in question appears
+in |buffer[id_first..(id_loc-1)]|.
+
+\yskip\hang |module_name|: In this case the global variable |cur_module| will
+point to the |byte_start| entry for the module name that has just been scanned.
+
+\yskip\noindent If |get_next| sees `\.{@@!}' or `\.{@@?}',
+it sets |xref_switch| to |def_flag| or zero and goes on to the next token.
+
+A global variable called |scanning_hex| is set |true| during the time that
+the letters \.A through \.F should be treated as if they were digits.
+
+@d exponent=@'200 {\.E or \.e following a digit}
+@d string=@'201 {\PASCAL\ string or \.{WEB} precomputed string}
+@d identifier=@'202 {\PASCAL\ identifier or reserved word}
+
+@<Globals...@>=
+@!cur_module: name_pointer; {name of module just scanned}
+@!scanning_hex: boolean; {are we scanning a hexadecimal constant?}
+
+@ @<Set init...@>=
+scanning_hex:=false;
+
+@ As one might expect, |get_next| consists mostly of a big switch
+that branches to the various special cases that can arise.
+
+@d up_to(#)==#-24,#-23,#-22,#-21,#-20,#-19,#-18,#-17,#-16,#-15,#-14,
+ #-13,#-12,#-11,#-10,#-9,#-8,#-7,#-6,#-5,#-4,#-3,#-2,#-1,#
+
+@p function get_next:eight_bits; {produces the next input token}
+label restart,done,found;
+var c:eight_bits; {the current character}
+@!d:eight_bits; {the next character}
+@!j,@!k:0..longest_name; {indices into |mod_text|}
+begin restart: if loc>limit then
+ begin get_line;
+ if input_has_ended then
+ begin c:=new_module; goto found;
+ end;
+ end;
+c:=buffer[loc]; incr(loc);
+if scanning_hex then @<Go to |found| if |c| is a hexadecimal digit,
+ otherwise set |scanning_hex:=false|@>;
+case c of
+"A",up_to("Z"),"a",up_to("z"): @<Get an identifier@>;
+"'","""": @<Get a string@>;
+"@@": @<Get control code and possible module name@>;
+@t\4@>@<Compress two-symbol combinations like `\.{:=}'@>@;
+" ",tab_mark: goto restart; {ignore spaces and tabs}
+"}": begin err_print('! Extra }'); goto restart;
+@.Extra \}@>
+ end;
+othercases if c>=128 then goto restart {ignore nonstandard characters}
+ else do_nothing
+endcases;
+found:@!debug if trouble_shooting then debug_help;@;@+gubed@/
+get_next:=c;
+end;
+
+@ @<Go to |found| if |c| is a hexadecimal digit...@>=
+if ((c>="0")and(c<="9"))or((c>="A")and(c<="F")) then goto found
+else scanning_hex:=false
+
+@ Note that the following code substitutes \.{@@\{} and \.{@@\}} for the
+respective combinations `\.{(*}' and `\.{*)}'. Explicit braces should be used
+for \TeX\ comments in \PASCAL\ text.
+
+@d compress(#)==begin if loc<=limit then begin c:=#; incr(loc); end; end
+
+@<Compress two-symbol...@>=
+".": if buffer[loc]="." then compress(double_dot)
+ else if buffer[loc]=")" then compress("]");
+":": if buffer[loc]="=" then compress(left_arrow);
+"=": if buffer[loc]="=" then compress(equivalence_sign);
+">": if buffer[loc]="=" then compress(greater_or_equal);
+"<": if buffer[loc]="=" then compress(less_or_equal)
+ else if buffer[loc]=">" then compress(not_equal);
+"(": if buffer[loc]="*" then compress(begin_comment)
+ else if buffer[loc]="." then compress("[");
+"*": if buffer[loc]=")" then compress(end_comment);
+
+@ @<Get an identifier@>=
+begin if ((c="E")or(c="e"))and(loc>1) then
+ if (buffer[loc-2]<="9")and(buffer[loc-2]>="0") then c:=exponent;
+if c<>exponent then
+ begin decr(loc); id_first:=loc;
+ repeat incr(loc); d:=buffer[loc];
+ until ((d<"0")or((d>"9")and(d<"A"))or((d>"Z")and(d<"a"))or(d>"z"))and(d<>"_");
+ c:=identifier; id_loc:=loc;
+ end;
+end
+
+@ A string that starts and ends with single or double quote marks is
+scanned by the following piece of the program.
+
+@<Get a string@>=
+begin id_first:=loc-1;
+repeat d:=buffer[loc]; incr(loc);
+if loc>limit then
+ begin err_print('! String constant didn''t end');
+@.String constant didn't end@>
+ loc:=limit; d:=c;
+ end;
+until d=c;
+id_loc:=loc; c:=string;
+end
+
+@ After an \.{@@} sign has been scanned, the next character tells us
+whether there is more work to do.
+
+@<Get control code and possible module name@>=
+begin c:=control_code(buffer[loc]); incr(loc);
+if c=underline then
+ begin xref_switch:=def_flag; goto restart;
+ end
+else if c=no_underline then
+ begin xref_switch:=0; goto restart;
+ end
+else if (c<=TeX_string)and(c>=xref_roman) then
+ @<Scan to the next \.{@@>}@>
+else if c=hex then scanning_hex:=true
+else if c=module_name then
+ @<Scan the module name and make |cur_module| point to it@>
+else if c=verbatim then @<Scan a verbatim string@>;
+end
+
+@ The occurrence of a module name sets |xref_switch| to zero,
+because the module name might (for example) follow \&{var}.
+
+@<Scan the module name...@>=
+begin @<Put module name into |mod_text[1..k]|@>;
+if k>3 then
+ begin if (mod_text[k]=".")and(mod_text[k-1]=".")and(mod_text[k-2]=".") then
+ cur_module:=prefix_lookup(k-3)
+ else cur_module:=mod_lookup(k);
+ end
+else cur_module:=mod_lookup(k);
+xref_switch:=0;
+end
+
+@ Module names are placed into the |mod_text| array with consecutive spaces,
+tabs, and carriage-returns replaced by single spaces. There will be no
+spaces at the beginning or the end. (We set |mod_text[0]:=" "| to facilitate
+this, since the |mod_lookup| routine uses |mod_text[1]| as the first
+character of the name.)
+
+@<Set init...@>=mod_text[0]:=" ";
+
+@ @<Put module name...@>=
+k:=0;
+loop@+ begin if loc>limit then
+ begin get_line;
+ if input_has_ended then
+ begin err_print('! Input ended in section name');
+@.Input ended in section name@>
+ loc:=1; goto done;
+ end;
+ end;
+ d:=buffer[loc];
+ @<If end of name, |goto done|@>;
+ incr(loc); if k<longest_name-1 then incr(k);
+ if (d=" ")or(d=tab_mark) then
+ begin d:=" "; if mod_text[k-1]=" " then decr(k);
+ end;
+ mod_text[k]:=d;
+ end;
+done: @<Check for overlong name@>;
+if (mod_text[k]=" ")and(k>0) then decr(k)
+
+@ @<If end of name,...@>=
+if d="@@" then
+ begin d:=buffer[loc+1];
+ if d=">" then
+ begin loc:=loc+2; goto done;
+ end;
+ if (d=" ")or(d=tab_mark)or(d="*") then
+ begin err_print('! Section name didn''t end'); goto done;
+@.Section name didn't end@>
+ end;
+ incr(k); mod_text[k]:="@@"; incr(loc); {now |d=buffer[loc]| again}
+ end
+
+@ @<Check for overlong name@>=
+if k>=longest_name-2 then
+ begin print_nl('! Section name too long: ');
+@.Section name too long@>
+ for j:=1 to 25 do print(xchr[mod_text[j]]);
+ print('...'); mark_harmless;
+ end
+
+@ @<Scan to the next...@>=
+begin id_first:=loc; buffer[limit+1]:="@@";
+while buffer[loc]<>"@@" do incr(loc);
+id_loc:=loc;
+if loc>limit then
+ begin err_print('! Control text didn''t end'); loc:=limit;
+@.Control text didn't end@>
+ end
+else begin loc:=loc+2;
+ if buffer[loc-1]<>">" then
+ err_print('! Control codes are forbidden in control text');
+@.Control codes are forbidden...@>
+ end;
+end
+
+@ A verbatim \PASCAL\ string will be treated like ordinary strings, but
+with no surrounding delimiters. At the present point in the program we
+have |buffer[loc-1]=verbatim|; we must set |id_first| to the beginning
+of the string itself, and |id_loc| to its ending-plus-one location in the
+buffer. We also set |loc| to the position just after the ending delimiter.
+
+@<Scan a verbatim string@>=
+begin id_first:=loc; incr(loc);
+buffer[limit+1]:="@@"; buffer[limit+2]:=">";
+while (buffer[loc]<>"@@")or(buffer[loc+1]<>">") do incr(loc);
+if loc>=limit then err_print('! Verbatim string didn''t end');
+@.Verbatim string didn't end@>
+id_loc:=loc; loc:=loc+2;
+end
+
+@* Phase one processing.
+We now have accumulated enough subroutines to make it possible to carry out
+\.{WEAVE}'s first pass over the source file. If everything works right,
+both phase one and phase two of \.{WEAVE} will assign the same numbers to
+modules, and these numbers will agree with what \.{TANGLE} does.
+
+The global variable |next_control| often contains the most recent output of
+|get_next|; in interesting cases, this will be the control code that
+ended a module or part of a module.
+
+@<Glob...@>=@!next_control:eight_bits; {control code waiting to be acting upon}
+
+@ The overall processing strategy in phase one has the following
+straightforward outline.
+
+@<Phase I: Read all the user's text and store the cross references@>=
+phase_one:=true; phase_three:=false;
+reset_input;
+module_count:=0; skip_limbo; change_exists:=false;
+while not input_has_ended do
+ @<Store cross reference data for the current module@>;
+changed_module[module_count]:=change_exists;
+ {the index changes if anything does}
+phase_one:=false; {prepare for second phase}
+@<Print error messages about unused or undefined module names@>;
+
+@ @<Store cross reference data...@>=
+begin incr(module_count);
+if module_count=max_modules then overflow('section number');
+changed_module[module_count]:=changing;
+ {it will become |true| if any line changes}
+if buffer[loc-1]="*" then
+ begin print('*',module_count:1);
+ update_terminal; {print a progress report}
+ end;
+@<Store cross references in the \TeX\ part of a module@>;
+@<Store cross references in the \(definition part of a module@>;
+@<Store cross references in the \PASCAL\ part of a module@>;
+if changed_module[module_count] then change_exists:=true;
+end
+
+@ The |Pascal_xref| subroutine stores references to identifiers in
+\PASCAL\ text material beginning with the current value of |next_control|
+and continuing until |next_control| is `\.\{' or `\v', or until the next
+``milestone'' is passed (i.e., |next_control>=format|). If
+|next_control>=format| when |Pascal_xref| is called, nothing will happen;
+but if |next_control="|"| upon entry, the procedure assumes that this is
+the `\v' preceding \PASCAL\ text that is to be processed.
+
+The program uses the fact that our internal code numbers satisfy
+the relations |xref_roman=identifier+roman| and |xref_wildcard=identifier
++wildcard| and |xref_typewriter=identifier+
+typewriter| and |normal=0|. An implied `\.{@@!}' is inserted after
+\&{function}, \&{procedure}, \&{program}, and \&{var}.
+
+@p procedure Pascal_xref; {makes cross references for \PASCAL\ identifiers}
+label exit;
+var p:name_pointer; {a referenced name}
+begin while next_control<format do
+ begin if (next_control>=identifier)and
+ (next_control<=xref_typewriter) then
+ begin p:=id_lookup(next_control-identifier); new_xref(p);
+ if (ilk[p]=proc_like)or(ilk[p]=var_like) then
+ xref_switch:=def_flag; {implied `\.{@@!}'}
+ end;
+ next_control:=get_next;
+ if (next_control="|")or(next_control="{") then return;
+ end;
+exit:end;
+
+@ The |outer_xref| subroutine is like |Pascal_xref| but it begins
+with |next_control<>"|"| and ends with |next_control>=format|. Thus, it
+handles \PASCAL\ text with embedded comments.
+
+@p procedure outer_xref; {extension of |Pascal_xref|}
+var bal:eight_bits; {brace level in comment}
+begin while next_control<format do
+ if next_control<>"{" then Pascal_xref
+ else begin bal:=skip_comment(1); next_control:="|";
+ while bal>0 do
+ begin Pascal_xref;
+ if next_control="|" then bal:=skip_comment(bal)
+ else bal:=0; {an error will be reported in phase two}
+ end;
+ end;
+end;
+
+@ In the \TeX\ part of a module, cross reference entries are made only for
+the identifiers in \PASCAL\ texts enclosed in \pb, or for control texts
+enclosed in \.{@@\^}$\,\ldots\,$\.{@@>} or \.{@@.}$\,\ldots\,$\.{@@>}
+or \.{@@:}$\,\ldots\,$\.{@@>}.
+
+@<Store cross references in the \T...@>=
+repeat next_control:=skip_TeX;
+case next_control of
+underline: xref_switch:=def_flag;
+no_underline: xref_switch:=0;
+"|": Pascal_xref;
+xref_roman, xref_wildcard, xref_typewriter, module_name:
+ begin loc:=loc-2; next_control:=get_next; {scan to \.{@@>}}
+ if next_control<>module_name then
+ new_xref(id_lookup(next_control-identifier));
+ end;
+othercases do_nothing
+endcases;
+until next_control>=format
+
+@ During the definition and \PASCAL\ parts of a module, cross references
+are made for all identifiers except reserved words; however, the
+identifiers in a format definition are referenced even if they are
+reserved. The \TeX\ code in comments is, of course, ignored, except for
+\PASCAL\ portions enclosed in \pb; the text of a module name is skipped
+entirely, even if it contains \pb\ constructions.
+
+The variables |lhs| and |rhs| point to the respective identifiers involved
+in a format definition.
+
+@<Global...@>=
+@!lhs,@!rhs:name_pointer; {indices into |byte_start| for format identifiers}
+
+@ When we get to the following code we have |next_control>=format|.
+
+@<Store cross references in the \(d...@>=
+while next_control<=definition do {|format| or |definition|}
+ begin xref_switch:=def_flag; {implied \.{@@!}}
+ if next_control=definition then next_control:=get_next
+ else @<Process a format definition@>;
+ outer_xref;
+ end
+
+@ Error messages for improper format definitions will be issued in phase
+two. Our job in phase one is to define the |ilk| of a properly formatted
+identifier, and to fool the |new_xref| routine into thinking that the
+identifier on the right-hand side of the format definition is not a
+reserved word.
+
+@<Process a form...@>=
+begin next_control:=get_next;
+if next_control=identifier then
+ begin lhs:=id_lookup(normal); ilk[lhs]:=normal; new_xref(lhs);
+ next_control:=get_next;
+ if next_control=equivalence_sign then
+ begin next_control:=get_next;
+ if next_control=identifier then
+ begin rhs:=id_lookup(normal);
+ ilk[lhs]:=ilk[rhs]; ilk[rhs]:=normal; new_xref(rhs);
+ ilk[rhs]:=ilk[lhs]; next_control:=get_next;
+ end;
+ end;
+ end;
+end
+
+@ Finally, when the \TeX\ and definition parts have been treated, we have
+|next_control>=begin_Pascal|.
+
+@<Store cross references in the \P...@>=
+if next_control<=module_name then {|begin_Pascal| or |module_name|}
+ begin if next_control=begin_Pascal then mod_xref_switch:=0
+ else mod_xref_switch:=def_flag;
+ repeat if next_control=module_name then new_mod_xref(cur_module);
+ next_control:=get_next; outer_xref;
+ until next_control>module_name;
+ end
+
+@ After phase one has looked at everything, we want to check that each
+module name was both defined and used.
+The variable |cur_xref| will point to cross references for the
+current module name of interest.
+
+@<Glob...@>=@!cur_xref:xref_number; {temporary cross reference pointer}
+
+@ The following recursive procedure
+walks through the tree of module names and prints out anomalies.
+@^recursion@>
+
+@p procedure mod_check(@!p:name_pointer); {print anomalies in subtree |p|}
+begin if p>0 then
+ begin mod_check(llink[p]);@/
+ cur_xref:=xref[p];
+ if num(cur_xref)<def_flag then
+ begin print_nl('! Never defined: <'); print_id(p);
+@.Never defined: <section name>@>
+ print('>'); mark_harmless;
+ end;
+ while num(cur_xref)>=def_flag do cur_xref:=xlink(cur_xref);
+ if cur_xref=0 then
+ begin print_nl('! Never used: <'); print_id(p); print('>');
+@.Never used: <section name>@>
+ mark_harmless;
+ end;
+ mod_check(rlink[p]);
+ end;
+end;
+
+@ @<Print error messages about un...@>=@+mod_check(root)
+
+@* Low-level output routines.
+The \TeX\ output is supposed to appear in lines at most |line_length|
+characters long, so we place it into an output buffer. During the output
+process, |out_line| will hold the current line number of the line about to
+be output.
+
+@<Glo...@>=
+@!out_buf:array[0..line_length] of ASCII_code; {assembled characters}
+@!out_ptr:0..line_length; {number of characters in |out_buf|}
+@!out_line: integer; {coordinates of next line to be output}
+
+@ The |flush_buffer| routine empties the buffer up to a given breakpoint,
+and moves any remaining characters to the beginning of the next line.
+If the |per_cent| parameter is |true|, a |"%"| is appended to the line
+that is being output; in this case the breakpoint |b| should be strictly
+less than |line_length|. If the |per_cent| parameter is |false|,
+trailing blanks are suppressed.
+The characters emptied from the buffer form a new line of output;
+if the |carryover| parameter is true, a |"%"| in that line will be
+carried over to the next line (so that \TeX\ will ignore the completion
+of commented-out text).
+
+@p procedure flush_buffer(@!b:eight_bits;@!per_cent,@!carryover:boolean);
+ {outputs |out_buf[1..b]|, where |b<=out_ptr|}
+label done,found;
+var j,@!k:0..line_length;
+begin j:=b;
+if not per_cent then {remove trailing blanks}
+ loop@+ begin if j=0 then goto done;
+ if out_buf[j]<>" " then goto done;
+ decr(j);
+ end;
+done: for k:=1 to j do write(tex_file,xchr[out_buf[k]]);
+if per_cent then write(tex_file,xchr["%"]);
+write_ln(tex_file); incr(out_line);
+if carryover then
+ for k:=1 to j do
+ if out_buf[k]="%" then
+ if (k=1)or(out_buf[k-1]<>"\") then {comment mode should be preserved}
+ begin out_buf[b]:="%"; decr(b); goto found;
+ end;
+found: if (b<out_ptr) then
+ for k:=b+1 to out_ptr do out_buf[k-b]:=out_buf[k];
+out_ptr:=out_ptr-b;
+end;
+
+@ When we are copying \TeX\ source material, we retain line breaks
+that occur in the input, except that an empty line is not
+output when the \TeX\ source line was nonempty. For example, a line
+of the \TeX\ file that contains only an index cross-reference entry
+will not be copied. The |finish_line| routine is called just before
+|get_line| inputs a new line, and just after a line break token has
+been emitted during the output of translated \PASCAL\ text.
+
+@p procedure finish_line; {do this at the end of a line}
+label exit;
+var k:0..buf_size; {index into |buffer|}
+begin if out_ptr>0 then flush_buffer(out_ptr,false,false)
+else begin for k:=0 to limit do
+ if (buffer[k]<>" ")and(buffer[k]<>tab_mark) then return;
+ flush_buffer(0,false,false);
+ end;
+exit:end;
+
+@ In particular, the |finish_line| procedure is called near the very
+beginning of phase two. We initialize the output variables in a slightly
+tricky way so that the first line of the output file will be
+`\.{\\input webmac}'.
+@.\\input webmac@>
+@.webmac@>
+
+@<Set init...@>=
+out_ptr:=1; out_line:=1; out_buf[1]:="c"; write(tex_file,'\input webma');
+
+@ When we wish to append the character |c| to the output buffer, we write
+`$|out|(c)$'; this will cause the buffer to be emptied if it was already
+full. Similarly, `$|out2|(c_1)(c_2)$' appends a pair of characters.
+A line break will occur at a space or after a single-nonletter
+\TeX\ control sequence.
+
+@d oot(#)==@;@/
+ if out_ptr=line_length then break_out;
+ incr(out_ptr); out_buf[out_ptr]:=#;
+@d oot1(#)==oot(#)@+end
+@d oot2(#)==oot(#)@,oot1
+@d oot3(#)==oot(#)@,oot2
+@d oot4(#)==oot(#)@,oot3
+@d oot5(#)==oot(#)@,oot4
+@d out==@+begin oot1
+@d out2==@+begin oot2
+@d out3==@+begin oot3
+@d out4==@+begin oot4
+@d out5==@+begin oot5
+
+@ The |break_out| routine is called just before the output buffer is about
+to overflow. To make this routine a little faster, we initialize position
+0 of the output buffer to `\.\\'; this character isn't really output.
+
+@<Set init...@>=
+out_buf[0]:="\";
+
+@ A long line is broken at a blank space or just before a backslash that isn't
+preceded by another backslash. In the latter case, a |"%"| is output at
+the break.
+
+@p procedure break_out; {finds a way to break the output line}
+label exit;
+var k:0..line_length; {index into |out_buf|}
+@!d:ASCII_code; {character from the buffer}
+begin k:=out_ptr;
+loop@+ begin if k=0 then
+ @<Print warning message, break the line, |return|@>;
+ d:=out_buf[k];
+ if d=" " then
+ begin flush_buffer(k,false,true); return;
+ end;
+ if (d="\")and(out_buf[k-1]<>"\") then {in this case |k>1|}
+ begin flush_buffer(k-1,true,true); return;
+ end;
+ decr(k);
+ end;
+exit:end;
+
+@ We get to this module only in unusual cases that the entire output line
+consists of a string of backslashes followed by a string of nonblank
+non-backslashes. In such cases it is almost always safe to break the
+line by putting a |"%"| just before the last character.
+
+@<Print warning message...@>=
+begin print_nl('! Line had to be broken (output l.',out_line:1);
+@.Line had to be broken@>
+print_ln('):');
+for k:=1 to out_ptr-1 do print(xchr[out_buf[k]]);
+new_line; mark_harmless;
+flush_buffer(out_ptr-1,true,true); return;
+end
+
+@ Here is a procedure that outputs a module number in decimal notation.
+
+@<Glob...@>=@!dig:array[0..4] of 0..9; {digits to output}
+
+@ The number to be converted by |out_mod| is known to be less than
+|def_flag|, so it cannot have more than five decimal digits. If
+the module is changed, we output `\.{\\*}' just after the number.
+
+@p procedure out_mod(@!m:integer); {output a module number}
+var k:0..5; {index into |dig|}
+@!a:integer; {accumulator}
+begin k:=0; a:=m;
+repeat dig[k]:=a mod 10; a:=a div 10; incr(k);
+until a=0;
+repeat decr(k); out(dig[k]+"0");
+until k=0;
+if changed_module[m] then out2("\")("*");
+@.\\*@>
+end;
+
+@ The |out_name| subroutine is used to output an identifier or index
+entry, enclosing it in braces.
+
+@p procedure out_name(@!p:name_pointer); {outputs a name}
+var k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {row of |byte_mem|}
+begin out("{"); w:=p mod ww;
+for k:=byte_start[p] to byte_start[p+ww]-1 do
+ begin if byte_mem[w,k]="_" then out("\");
+@.\\_@>
+ out(byte_mem[w,k]);
+ end;
+out("}");
+end;
+
+@* Routines that copy \TeX\ material.
+During phase two, we use the subroutines |copy_limbo|, |copy_TeX|, and
+|copy_comment| in place of the analogous |skip_limbo|, |skip_TeX|, and
+|skip_comment| that were used in phase one.
+
+The |copy_limbo| routine, for example, takes \TeX\ material that is not
+part of any module and transcribes it almost verbatim to the output file.
+No `\.{@@}' signs should occur in such material except in `\.{@@@@}'
+pairs; such pairs are replaced by singletons.
+
+@p procedure copy_limbo; {copy \TeX\ code until the next module begins}
+label exit;
+var c:ASCII_code; {character following \.{@@} sign}
+begin loop if loc>limit then
+ begin finish_line; get_line;
+ if input_has_ended then return;
+ end
+ else begin buffer[limit+1]:="@@";
+ @<Copy up to control code, |return| if finished@>;
+ end;
+exit:end;
+
+@ @<Copy up to control...@>=
+while buffer[loc]<>"@@" do
+ begin out(buffer[loc]); incr(loc);
+ end;
+if loc<=limit then
+ begin loc:=loc+2; c:=buffer[loc-1];
+ if (c=" ")or(c=tab_mark)or(c="*") then return;
+ if (c<>"z")and(c<>"Z") then
+ begin out("@@");
+ if c<>"@@" then err_print('! Double @@ required outside of sections');
+@.Double \AT! required...@>
+ end;
+ end
+
+@ The |copy_TeX| routine processes the \TeX\ code at the beginning of a
+module; for example, the words you are now reading were copied in this
+way. It returns the next control code or `\v' found in the input.
+
+@p function copy_TeX:eight_bits; {copy pure \TeX\ material}
+label done;
+var c:eight_bits; {control code found}
+begin loop begin if loc>limit then
+ begin finish_line; get_line;
+ if input_has_ended then
+ begin c:=new_module; goto done;
+ end;
+ end;
+ buffer[limit+1]:="@@";
+ @<Copy up to `\v' or control code, |goto done| if finished@>;
+ end;
+done:copy_TeX:=c;
+end;
+
+@ We don't copy spaces or tab marks into the beginning of a line. This
+makes the test for empty lines in |finish_line| work.
+
+@<Copy up to `\v'...@>=
+repeat c:=buffer[loc]; incr(loc);
+if c="|" then goto done;
+if c<>"@@" then
+ begin out(c);
+ if (out_ptr=1)and((c=" ")or(c=tab_mark)) then decr(out_ptr);
+ end;
+until c="@@";
+if loc<=limit then
+ begin c:=control_code(buffer[loc]); incr(loc);
+ goto done;
+ end
+
+@ The |copy_comment| uses and returns a brace-balance value, following the
+conventions of |skip_comment| above. Instead of copying the \TeX\ material
+into the output buffer, this procedure copies it into the token memory.
+The abbreviation |app_tok(t)| is used to append token |t| to the current
+token list, and it also makes sure that it is possible to append at least
+one further token without overflow.
+
+@d app_tok(#)==begin if tok_ptr+2>max_toks then overflow('token');
+ tok_mem[tok_ptr]:=#; incr(tok_ptr);
+ end
+
+@p function copy_comment(@!bal:eight_bits):eight_bits; {copies \TeX\ code in
+ comments}
+label done;
+var c:ASCII_code; {current character being copied}
+begin loop begin if loc>limit then
+ begin get_line;
+ if input_has_ended then
+ begin err_print('! Input ended in mid-comment');
+@.Input ended in mid-comment@>
+ loc:=1; @<Clear |bal| and |goto done|@>;
+ end;
+ end;
+ c:=buffer[loc]; incr(loc);
+ if c="|" then goto done;
+ app_tok(c);
+ @<Copy special things when |c="@@", "\", "{", "}"|;
+ |goto done| at end@>;
+ end;
+done: copy_comment:=bal;
+end;
+
+@ @<Copy special things when |c="@@"...@>=
+if c="@@" then
+ begin incr(loc);
+ if buffer[loc-1]<>"@@" then
+ begin err_print('! Illegal use of @@ in comment');
+@.Illegal use of \AT!...@>
+ loc:=loc-2; decr(tok_ptr); @<Clear |bal|...@>;
+ end;
+ end
+else if (c="\")and(buffer[loc]<>"@@") then
+ begin app_tok(buffer[loc]); incr(loc);
+ end
+else if c="{" then incr(bal)
+else if c="}" then
+ begin decr(bal);
+ if bal=0 then goto done;
+ end
+
+@ When the comment has terminated abruptly due to an error, we output
+enough right braces to keep \TeX\ happy.
+
+@<Clear |bal|...@>=
+app_tok(" "); {this is done in case the previous character was `\.\\'}
+repeat app_tok("}"); decr(bal);
+until bal=0;
+goto done;
+
+@* Parsing.
+The most intricate part of \.{WEAVE} is its mechanism for converting
+\PASCAL-like code into \TeX\ code, and we might as well plunge into this
+aspect of the program now. A ``bottom up'' approach is used to parse the
+\PASCAL-like material, since \.{WEAVE} must deal with fragmentary
+constructions whose overall ``part of speech'' is not known.
+
+At the lowest level, the input is represented as a sequence of entities
+that we shall call {\it scraps}, where each scrap of information consists
+of two parts, its {\it category} and its {\it translation}. The category
+is essentially a syntactic class, and the translation is a token list that
+represents \TeX\ code. Rules of syntax and semantics tell us how to
+combine adjacent scraps into larger ones, and if we are lucky an entire
+\PASCAL\ text that starts out as hundreds of small scraps will join
+together into one gigantic scrap whose translation is the desired \TeX\
+code. If we are unlucky, we will be left with several scraps that don't
+combine; their translations will simply be output, one by one.
+
+The combination rules are given as context-sensitive productions that are
+applied from left to right. Suppose that we are currently working on the
+sequence of scraps $s_1\,s_2\ldots s_n$. We try first to find the longest
+production that applies to an initial substring $s_1\,s_2\ldots\,$; but if
+no such productions exist, we find to find the longest production
+applicable to the next substring $s_2\,s_3\ldots\,$; and if that fails, we
+try to match $s_3\,s_4\ldots\,$, etc.
+
+A production applies if the category codes have a given pattern. For
+example, one of the productions is
+$$|open|\;|math|\;|semi|\;\RA\;|open|\;|math|$$
+and it means that three consecutive scraps whose respective categories are
+|open|, |math|, and |semi| are con\-verted to two scraps whose categories
+are |open| and |math|. This production also has an associated rule that
+tells how to combine the translation parts:
+$$\eqalign{O_2&=O_1\cr
+M_2&=M_1\,S\,\.{\\,}\,\hbox{|opt|\thinspace\tt5}\cr}$$
+This means that the |open| scrap has not changed, while the new |math| scrap
+has a translation $M_2$ composed of the translation $M_1$ of the original
+|math| scrap followed by the translation |S| of the |semi| scrap followed
+by `\.{\\,}' followed by `|opt|' followed by `\.5'. (In the \TeX\ file,
+this will specify an additional thin space after the semicolon, followed
+by an optional line break with penalty 50.) Translation rules use subscripts
+to distinguish between translations of scraps whose categories have the
+same initial letter; these subscripts are assigned from left to right.
+
+$\.{WEAVE}$ also has the production rule
+$$|semi|\;\RA\;|terminator|$$
+(meaning that a semicolon can terminate a \PASCAL\ statement). Since
+productions are applied from left to right, this rule will be activated
+only if the |semi| is not preceded by scraps that match other productions;
+in particular, a |semi| that is preceded by `|open| |math|' will have
+disappeared because of the production above, and such semicolons do not
+act as statement terminators. This incidentally is how \.{WEAVE} is able
+to treat semicolons in two distinctly different ways, the first of which
+is intended for semicolons in the parameter list of a procedure
+declaration.
+
+The translation rule corresponding to $|semi|\;\RA\;|terminator|$ is
+$$T=S$$
+but we shall not mention translation rules in the common case that the
+translation of the new scrap on the right-hand side is simply the
+concatenation of the disappearing scraps on the left-hand side.
+
+@ Here is a list of the category codes that scraps can have.
+
+@d simp=1 {the translation can be used both in horizontal mode
+ and in math mode of \TeX}
+@d math=2 {the translation should be used only in \TeX\ math mode}
+@d intro=3 {a statement is expected to follow this, after a space and
+ an optional break}
+@d open=4 {denotes an incomplete parenthesized quantity to be used in
+ math mode}
+@d beginning=5 {denotes an incomplete compound statement to be used in
+ horizontal mode}
+@d close=6 {ends a parenthesis or compound statement}
+@d alpha=7 {denotes the beginning of a clause}
+@d omega=8 {denotes the ending of a clause and possible comment following}
+@d semi=9 {denotes a semicolon and possible comment following it}
+@d terminator=10 {something that ends a statement or declaration}
+@d stmt=11 {denotes a statement or declaration including its terminator}
+@d cond=12 {precedes an \&{if} clause that might have a matching \&{else}}
+@d clause=13 {precedes a statement after which indentation ends}
+@d colon=14 {denotes a colon}
+@d exp=15 {stands for the E in a floating point constant}
+@d proc=16 {denotes a procedure or program or function heading}
+@d case_head=17 {denotes a case statement or record heading}
+@d record_head=18 {denotes a record heading without indentation}
+@d var_head=19 {denotes a variable declaration heading}
+@d elsie=20 {\&{else}}
+@d casey=21 {\&{case}}
+@d mod_scrap=22 {denotes a module name}
+
+@p @!debug procedure print_cat(@!c:eight_bits);
+ {symbolic printout of a category}
+begin case c of
+simp: print('simp');
+math: print('math');
+intro: print('intro');
+open: print('open');
+beginning: print('beginning');
+close: print('close');
+alpha: print('alpha');
+omega: print('omega');
+semi: print('semi');
+terminator: print('terminator');
+stmt: print('stmt');
+cond: print('cond');
+clause: print('clause');
+colon: print('colon');
+exp: print('exp');
+proc: print('proc');
+case_head: print('casehead');
+record_head: print('recordhead');
+var_head: print('varhead');
+elsie: print('elsie');
+casey: print('casey');
+mod_scrap: print('module');
+othercases print('UNKNOWN')
+endcases;
+end;
+gubed
+
+@ The token lists for translated \TeX\ output contain some special control
+symbols as well as ordinary characters. These control symbols are
+interpreted by \.{WEAVE} before they are written to the output file.
+
+\yskip\hang |break_space| denotes an optional line break or an en space;
+
+\yskip\hang |force| denotes a line break;
+
+\yskip\hang |big_force| denotes a line break with additional vertical space;
+
+\yskip\hang |opt| denotes an optional line break (with the continuation
+line indented two ems with respect to the normal starting position)---this
+code is followed by an integer |n|, and the break will occur with penalty
+$10n$;
+
+\yskip\hang |backup| denotes a backspace of one em;
+
+\yskip\hang |cancel| obliterates any |break_space| or |force| or |big_force|
+tokens that immediately precede or follow it and also cancels any
+|backup| tokens that follow it;
+
+\yskip\hang |indent| causes future lines to be indented one more em;
+
+\yskip\hang |outdent| causes future lines to be indented one less em.
+
+\yskip\noindent All of these tokens are removed from the \TeX\ output that
+comes from \PASCAL\ text between \pb\ signs; |break_space| and |force| and
+|big_force| become single spaces in this mode. The translation of other
+\PASCAL\ texts results in \TeX\ control sequences \.{\\1}, \.{\\2},
+\.{\\3}, \.{\\4}, \.{\\5}, \.{\\6}, \.{\\7} corresponding respectively to
+|indent|, |outdent|, |opt|, |backup|, |break_space|, |force|, and
+|big_force|. However, a sequence of consecutive `\.\ ', |break_space|,
+|force|, and/or |big_force| tokens is first replaced by a single token
+(the maximum of the given ones).
+
+The tokens |math_rel|, |math_bin|, |math_op| will be translated into
+\.{\\mathrel\{}, \.{\\mathbin\{}, and \.{\\mathop\{}, respectively.
+Other control sequences in the \TeX\ output will be `\.{\\\\\{}$\,\ldots\,$\.\}'
+surrounding identifiers, `\.{\\\&\{}$\,\ldots\,$\.\}' surrounding
+reserved words, `\.{\\.\{}$\,\ldots\,$\.\}' surrounding strings,
+`\.{\\C\{}$\,\ldots\,$\.\}$\,$|force|' surrounding comments, and
+`\.{\\X$n$:}$\,\ldots\,$\.{\\X}' surrounding module names, where
+|n| is the module number.
+
+@d math_bin=@'203
+@d math_rel=@'204
+@d math_op=@'205
+@d big_cancel=@'206 {like |cancel|, also overrides spaces}
+@d cancel=@'207 {overrides |backup|, |break_space|, |force|, |big_force|}
+@d indent=cancel+1 {one more tab (\.{\\1})}
+@d outdent=cancel+2 {one less tab (\.{\\2})}
+@d opt=cancel+3 {optional break in mid-statement (\.{\\3})}
+@d backup=cancel+4 {stick out one unit to the left (\.{\\4})}
+@d break_space=cancel+5 {optional break between statements (\.{\\5})}
+@d force=cancel+6 {forced break between statements (\.{\\6})}
+@d big_force=cancel+7 {forced break with additional space (\.{\\7})}
+@d end_translation=big_force+1 {special sentinel token at end of list}
+
+@ The raw input is converted into scraps according to the following table,
+which gives category codes followed by the translations. Sometimes a single
+item of input produces more than one scrap.
+\def\stars {\.{**}}%
+(The symbol `\stars' stands for `\.{\\\&\{{\rm identifier}\}}',
+i.e., the identifier itself treated as a reserved word. In a few cases the
+category is given as `|@!comment|'; this is not an actual category code, it
+means that the translation will be treated as a comment, as explained
+below.)
+
+\yskip\halign{\quad#\hfil&\quad#\hfil\cr
+\.{<>}&|math:|\.{\\I}\cr
+\.{<=}&|math:|\.{\\L}\cr
+\.{>=}&|math:|\.{\\G}\cr
+\.{:=}&|math:|\.{\\K}\cr
+\.{==}&|math:|\.{\\S}\cr
+\.{(*}&|math:|\.{\\B}\cr
+\.{*)}&|math:|\.{\\T}\cr
+\.{(.}&|open:|\.[\cr
+\.{.)}&|close:|\.]\cr
+\."$\,$string$\,$\."&|simp:|\.{\\.\{"{\rm$\,$modified string$\,$}"\}}\cr
+\.\'$\,$string$\,$\.\'&|simp:|\.{\\.\{\\\'{\rm$\,$modified
+ string$\,$}\\\'\}}\cr
+\.{@@=}$\,$string$\,$\.{@@>}&|simp:|\.{\\=\{{\rm$\,$modified string$\,$}\}}\cr
+\#&|math:|\.{\\\#}\cr
+\.\$&|math:|\.{\\\$}\cr
+\.\_&|math:|\.{\\\_}\cr
+\.\%&|math:|\.{\\\%}\cr
+\.\^&|math:|\.{\\\^}\cr
+\.(&|open:|\.(\cr
+\.)&|close:|\.)\cr
+\.[&|open:|\.[\cr
+\.]&|close:|\.]\cr
+\.*&|math:|\.{\\ast}\cr
+\.,&|math:|\.,|@,opt@,|\.9\cr
+\.{..}&|math:|\.{\\to}\cr
+\..&|simp:|\..\cr
+\.:&|colon:|\.:\cr
+\.;&|semi:|\.;\cr
+identifier&|simp:|\.{\\\\\{{\rm$\,$identifier$\,$}\}}\cr
+\.E in constant&|exp:|\.{\\E\{}\cr
+digit $d$&|simp:|$d$\cr
+other character $c$&|math:|$c$\cr
+\.{and}&|math:|\.{\\W}\cr
+\.{array}&|alpha:|\stars\cr
+\.{begin}&|beginning:|$|force|\,\stars\,|cancel|$\qquad|intro:|\cr
+\.{case}&|casey:|\qquad|alpha:|$|force|\,\stars$\cr
+\.{const}&|intro:|$|force|\,|backup|\,\stars$\cr
+\.{div}&|math:|$|math_bin|\,\stars\,\.\}$\cr
+\.{do}&|omega:|\stars\cr
+\.{downto}&|math:|$|math_rel|\,\stars\,\.\}$\cr
+\.{else}&|terminator:|\qquad|elsie:|$|force|\,|backup|\,\stars$\cr
+\.{end}&|terminator:|\qquad|close:|$|force|\,\stars$\cr
+\.{file}&|alpha:|\stars\cr
+\.{for}&|alpha:|$|force|\,\stars$\cr
+\.{function}&|proc:|$|force|\,|backup|\,\stars\,|cancel|$\qquad
+ |intro:|$|indent|\,\.{\\\ }$\cr
+\.{goto}&|intro:|\stars\cr
+\.{if}&|cond:|\qquad|alpha:|$|force|\,\stars$\cr
+\.{in}&|math:|\.{\\in}\cr
+\.{label}&|intro:|$|force|\,|backup|\,\stars$\cr
+\.{mod}&|math:|$|math_bin|\,\stars\,\.\}$\cr
+\.{nil}&|simp:|\stars\cr
+\.{not}&|math:|\.{\\R}\cr
+\.{of}&|omega:|\stars\cr
+\.{or}&|math:|\.{\\V}\cr
+\.{packed}&|intro:|\stars\cr
+\.{procedure}&|proc:|$|force|\,|backup|\,\stars\,|cancel|$\qquad
+ |intro:|$|indent|\,\.{\\\ }$\cr
+\.{program}&|proc:|$|force|\,|backup|\,\stars\,|cancel|$\qquad
+ |intro:|$|indent|\,\.{\\\ }$\cr
+\.{record}&|record_head:|\stars\qquad|intro:|\cr
+\.{repeat}&|beginning:|$|force|\,|indent|\,\stars\,|cancel|$\qquad|intro:|\cr
+\.{set}&|alpha:|\stars\cr
+\.{then}&|omega:|\stars\cr
+\.{to}&|math:|$|math_rel|\,\stars\,\.\}$\cr
+\.{type}&|intro:|$|force|\,|backup|\,\stars$\cr
+\.{until}&|terminator:|\qquad|close:|$|force|\,|backup|\,\stars$\qquad
+ |clause:|\cr
+\.{var}&|var_head:|$|force|\,|backup|\,\stars\,|cancel|$\qquad|intro:|\cr
+\.{while}&|alpha:|$|force|\,\stars$\cr
+\.{with}&|alpha:|$|force|\,\stars$\cr
+\.{xclause}&|alpha:|$|force|\,\.{\\\~}$\qquad|omega:|\stars\cr
+\.{@@\'}$\,$const&|simp:|\.{\\O\{}\hbox{const}\.\}\cr
+\.{@@"}$\,$const&|simp:|\.{\\H\{}\hbox{const}\.\}\cr
+\.{@@\$}&|simp:|\.{\\)}\cr
+\.{@@\\}&|simp:|\.{\\]}\cr
+\.{@@,}&|math:|\.{\\,}\cr
+\.{@@t}$\,$stuff$\,$\.{@@>}&|simp:|\.{\\hbox\{{\rm$\,$stuff$\,$}\}}\cr
+\.{@@<}$\,$module$\,$\.{@@>}&|mod_scrap:|\.{\\X$n$:{\rm$\,$module$\,$}\\X}\cr
+\.{@@\#}&|comment:||big_force|\cr
+\.{@@/}&|comment:||force|\cr
+\.{@@\char'174}&|simp:|$|opt|\,\.0$\cr
+\.{@@+}&|comment:|$|big_cancel|\,\.{\\\ }\,|big_cancel|$\cr
+\.{@@;}&|semi:|\cr
+\.{@@\&}&|math:|\.{\\J}\cr
+\.{@@\{}&|math:|\.{\\B}\cr
+\.{@@\}}&|math:|\.{\\T}\cr}
+\yskip\noindent When a string is output, certain characters are preceded by
+`\.\\' signs so that they will print properly.
+
+A comment in the input will be combined with the preceding
+|omega| or |semi| scrap, or with the following |terminator| scrap, if
+possible; otherwise it will be inserted as a separate |terminator| scrap.
+An additional ``comment'' is effectively appended at the end of the
+\PASCAL\ text, just before translation begins; this consists of a |cancel|
+token in the case of \PASCAL\ text in \pb, otherwise it consists of a
+|force| token.
+
+From this table it is evident that \.{WEAVE} will parse a lot of non-\PASCAL\
+programs. For example, the reserved words `\.{for}' and `\.{array}' are
+treated in an identical way by \.{WEAVE} from a syntactic standpoint,
+and semantically they are equivalent except that a forced line break occurs
+just before `\&{for}'; \PASCAL\ programmers may well be surprised at this
+similarity. The idea is to keep \.{WEAVE}'s rules as simple as possible,
+consistent with doing a reasonable job on syntactically correct \PASCAL\
+programs. The production rules below have been formulated in the same
+spirit of ``almost anything goes.''
+
+@ Here is a table of all the productions. The reader can best get a feel for
+@^productions, table of@>
+how they work by trying them out by hand on small examples; no amount of
+explanation will be as effective as watching the rules in action.
+Parsing can also be watched by debugging with `\.{@@2}'.
+
+\def\[#1]{\quad$\dleft#1\dright$}
+\def\sp{\.{\ }}
+\yskip
+\halign to\the\hsize{\hfil\it# &
+ #\hfil\hskip-200pt\tabskip 0pt plus 100pt&
+ #\hfil\tabskip0pt\cr
+&Production categories\[\hbox{translations}]&Remarks\cr
+\noalign{\yskip}
+1&|alpha@,math@,colon| $\RA$ |alpha@,math|&e.g., |case v:boolean of|\cr
+2&|alpha@,math@,omega| $\RA$ |clause|\[C=A\,\sp\,\.\$\,M\,\.\$\,\sp\,|indent|\,
+O]&e.g., |while x>0 do|\cr
+3&|alpha@,omega| $\RA$ |clause|\[C=A\,\sp\,|indent|\,O]&e.g., |file of|\cr
+4&|alpha@,simp| $\RA$ |alpha@,math|&convert to math mode\cr
+5&|beginning@,close@,(terminator@t or @>stmt)| $\RA$ |stmt|&compound statement
+ends\cr
+6&|beginning@,stmt| $\RA$ |beginning|\[B_2=B_1\,|break_space|\,S]&compound
+statement grows\cr
+7&|case_head@,casey@,clause| $\RA$ |case_head|\[C_4=C_1\,|outdent|\,C_2\,C_3]&
+variant records\cr
+8&|case_head@,close@,terminator| $\RA$ |stmt|\[S=C_1\,|cancel|\,|outdent|\,
+C_2\,T]&end of case statement\cr
+9&|case_head@,stmt| $\RA$ |case_head|\[C_2=C_1\,|force|\,S]&case statement
+grows\cr
+10&|casey@,clause| $\RA$ |case_head|&beginning of case statement\cr
+11&|clause@,stmt| $\RA$ |stmt|\[S_2=C\,|break_space|\,S_1\,|cancel|\,|outdent|\,
+|force|]&end of controlled statement\cr
+12&|cond@,clause@,stmt@,elsie| $\RA$ |clause|\[C_3=C_1\,C_2\,|break_space|\,S\,
+E\,\sp\,|cancel|]&complete conditional\cr
+13&|cond@,clause@,stmt| $\RA$ |stmt|\cr
+&\qquad\[S_2=C_1\,C_2\,|break_space|\,S_1\,
+|cancel|\,|outdent|\,|force|]&incomplete conditional\cr
+14&|elsie| $\RA$ |intro|&unmatched else\cr
+15&|exp@,math@,simp|* $\RA$ |math|\[M_2=E\,M_1\,S\,\.\}]&signed exponent\cr
+16&|exp@,simp|* $\RA$ |math|\[M=E\,S\,\.\}]&unsigned exponent\cr
+17&|intro@,stmt| $\RA$ |stmt|\[S_2=I\,\sp\,|opt|\,\.7\,|cancel|\,S_1]&labeled
+statement, etc.\cr
+18&|math@,close| $\RA$ |stmt@,close|\[S=\.\$\,M\,\.\$]&end of field list\cr
+19&|math@,colon| $\RA$ |intro|\[I=|force|\,|backup|\,\.\$\,M\,\.\$\,C]&compound
+label\cr
+20&|math@,math| $\RA$ |math|&simple concatenation\cr
+21&|math@,simp| $\RA$ |math|&simple concatenation\cr
+22&|math@,stmt| $\RA$ |stmt|\cr
+&\qquad\[S_2=\.\$\,M\,\.\$\,|indent|\,|break_space|\,
+S_1\,|cancel|\,|outdent|\,|force|]&macro or type definition\cr
+23&|math@,terminator| $\RA$ |stmt|\[S=\.\$\,M\,\.\$\,T]&statement involving
+math\cr
+24&|mod_scrap@,(terminator@t or @>semi)| $\RA$ |stmt|\[S=M\,T\,|force|]&module
+like a statement\cr
+25&|mod_scrap| $\RA$ |simp|&module unlike a statement\cr
+26&|open@,case_head@,close| $\RA$ |math|\[M=O\,\.\$\,|cancel|\,C_1\,
+|cancel|\,|outdent|\,\.\$\,C_2]&case in field list\cr
+27&|open@,close| $\RA$ |math|\[M=O\,\.\\\,\.,\,C]&empty set |[]|\cr
+28&|open@,math@,case_head@,close| $\RA$ |math|\cr
+&\qquad\[M_2=O\,M_1\,\.\$\,|cancel|\,
+C_1\,|cancel|\,|outdent|\,\.\$\,C_2]&case in field list\cr
+29&|open@,math@,close| $\RA$ |math|&parenthesized group\cr
+30&|open@,math@,colon| $\RA$ |open@,math|&colon in parentheses\cr
+31&|open@,math@,proc@,intro| $\RA$ |open@,math|\[M_2=M_1\,|math_op|\,|cancel|\,
+P\,\.\}]&|procedure| in parentheses\cr
+32&|open@,math@,semi| $\RA$ |open@,math|\[M_2=M_1\,S\,\.\\\,\.,\,|opt|\,
+\.5]&semicolon in parentheses\cr
+33&|open@,math@,var_head@,intro| $\RA$ |open@,math|\[M_2=M_1\,|math_op|\,
+|cancel|\,V\,\.\}]&|var| in parentheses\cr
+34&|open@,proc@,intro| $\RA$ |open@,math|\[M=|math_op|\,|cancel|\,
+P\,\.\}]&|procedure| in parentheses\cr
+35&|open@,simp| $\RA$ |open@,math|&convert to math mode\cr
+36&|open@,stmt@,close| $\RA$ |math|\[M=O\,\.\$\,|cancel|\,S\,|cancel|\,
+\.\$\,C]&field list\cr
+37&|open@,var_head@,intro| $\RA$ |open@,math|\[M=|math_op|\,|cancel|\,V\,
+\.\}]&|var| in parentheses\cr
+38&|proc@,beginning@,close@,terminator| $\RA$ |stmt|\[S=P\,|cancel|\,
+|outdent|\,B\,C\,T]&end of procedure declaration\cr
+39&|proc@,stmt| $\RA$ |proc|\[P_2=P_1\,|break_space|\,S]&procedure declaration
+grows\cr
+40&|record_head@,intro@,casey| $\RA$ |casey|\[C_2=R\,I\,\sp\,|cancel|\,C_1]&
+\&{record case} $\ldots$\cr
+41&|record_head| $\RA$ |case_head|\[C=|indent|\,R\,|cancel|]&other \&{record}
+structures\cr
+42&|semi| $\RA$ |terminator|&semicolon after statement\cr
+43&|simp@,close| $\RA$ |stmt@,close|&end of field list\cr
+44&|simp@,colon| $\RA$ |intro|\[I=|force|\,|backup|\,S\,C]&simple label\cr
+45&|simp@,math| $\RA$ |math|&simple concatenation\cr
+46&|simp@,mod_scrap| $\RA$ |mod_scrap|&in emergencies\cr
+47&|simp@,simp| $\RA$ |simp|&simple concatenation\cr
+48&|simp@,terminator| $\RA$ |stmt|&simple statement\cr
+49&|stmt@,stmt| $\RA$ |stmt|\[S_3=S_1\,|break_space|\,S_2]&adjacent
+statements\cr
+50&|terminator| $\RA$ |stmt|&empty statement\cr
+51&|var_head@,beginning| $\RA$ |stmt@,beginning|&end of variable
+declarations\cr
+52&|var_head@,math@,colon| $\RA$ |var_head@,intro|\[I=\.\$\,M\,\.\$\,C]&
+variable declaration\cr
+53&|var_head@,simp@,colon| $\RA$ |var_head@,intro|&variable declaration\cr
+54&|var_head@,stmt| $\RA$ |var_head|\[V_2=V_1\,|break_space|\,S]&variable
+declarations grow\cr}
+\yskip\noindent
+Translations are not specified here when they are simple concatenations
+of the scraps that change. For example, the full translation of
+`|open@,math@,colon| $\RA$ |open@,math|' is $O_2=O_1$, $M_2=M_1C$.
+
+The notation `|simp|*', in the |exp|-related productions above,
+stands for a |simp| scrap that isn't followed by another |simp|.
+
+@* Implementing the productions.
+When \PASCAL\ text is to be processed with the grammar above, we put its
+initial scraps $s_1\ldots s_n$ into two arrays |cat[1..n]| and |trans[1..n]|.
+The value of |cat[k]| is simply a category code from the list above; the
+value of |trans[k]| is a text pointer, i.e., an index into |tok_start|.
+Our production rules have the nice property that the right-hand side is never
+longer than the left-hand side. Therefore it is convenient to use sequential
+allocation for the current sequence of scraps. Five pointers are used to
+manage the parsing:
+
+\yskip\hang |pp| (the parsing pointer) is such that we are trying to match
+the category codes |cat[pp]@,cat[pp+1]|$\,\ldots\,$ to the left-hand sides
+of productions.
+
+\yskip\hang |scrap_base|, |lo_ptr|, |hi_ptr|, and |scrap_ptr| are such that
+the current sequence of scraps appears in positions |scrap_base| through
+|lo_ptr| and |hi_ptr| through |scrap_ptr|, inclusive, in the |cat| and
+|trans| arrays. Scraps located between |scrap_base| and |lo_ptr| have
+been examined, while those in positions |>=hi_ptr| have not yet been
+looked at by the parsing process.
+
+\yskip\noindent Initially |scrap_ptr| is set to the position of the final
+scrap to be parsed, and it doesn't change its value. The parsing process
+makes sure that |lo_ptr>=pp+3|, since productions have as many as four terms,
+by moving scraps from |hi_ptr| to |lo_ptr|. If there are
+fewer than |pp+3| scraps left, the positions up to |pp+3| are filled with
+blanks that will not match in any productions. Parsing stops when
+|pp=lo_ptr+1| and |hi_ptr=scrap_ptr+1|.
+
+The |trans| array elements are declared to be of type |0..10239| instead
+of type |text_pointer|, because the final sorting phase of \.{WEAVE}
+uses this array to contain elements of type |name_pointer|. Both
+of these types are subranges of |0..10239|.
+
+@<Glo...@>=
+@!cat:array[0..max_scraps] of eight_bits; {category codes of scraps}
+@!trans:array[0..max_scraps] of 0..10239; {translation texts of scraps}
+@!pp:0..max_scraps; {current position for reducing productions}
+@!scrap_base:0..max_scraps; {beginning of the current scrap sequence}
+@!scrap_ptr:0..max_scraps; {ending of the current scrap sequence}
+@!lo_ptr:0..max_scraps; {last scrap that has been examined}
+@!hi_ptr:0..max_scraps; {first scrap that has not been examined}
+stat@!max_scr_ptr:0..max_scraps; {largest value assumed by |scrap_ptr|}
+tats
+
+@ @<Set init...@>=
+scrap_base:=1; scrap_ptr:=0;
+stat max_scr_ptr:=0; @+tats
+
+@ Token lists in |@!tok_mem| are composed of the following kinds of
+items for \TeX\ output.
+
+\yskip\item{$\bullet$}ASCII codes and special codes like |force| and
+|math_rel| represent themselves;
+
+\item{$\bullet$}|id_flag+p| represents \.{\\\\\{{\rm identifier $p$}\}};
+
+\item{$\bullet$}|res_flag+p| represents \.{\\\&\{{\rm identifier $p$}\}};
+
+\item{$\bullet$}|mod_flag+p| represents module name |p|;
+
+\item{$\bullet$}|tok_flag+p| represents token list number |p|;
+
+\item{$\bullet$}|inner_tok_flag+p| represents token list number |p|, to be
+translated without line-break controls.
+
+@d id_flag=10240 {signifies an identifier}
+@d res_flag=id_flag+id_flag {signifies a reserved word}
+@d mod_flag=res_flag+id_flag {signifies a module name}
+@d tok_flag==mod_flag+id_flag {signifies a token list}
+@d inner_tok_flag==tok_flag+id_flag {signifies a token list in `\pb'}
+@#
+@d lbrace==xchr["{"] {this avoids possible \PASCAL\ compiler confusion}
+@d rbrace==xchr["}"] {because these braces might occur within comments}
+
+@p @!debug procedure print_text(@!p:text_pointer); {prints a token list}
+var j:0..max_toks; {index into |tok_mem|}
+@!r:0..id_flag-1; {remainder of token after the flag has been stripped off}
+begin if p>=text_ptr then print('BAD')
+else for j:=tok_start[p] to tok_start[p+1]-1 do
+ begin r:=tok_mem[j] mod id_flag;
+ case tok_mem[j] div id_flag of
+ 1: begin print('\\',lbrace); print_id(r); print(rbrace);
+ end; {|id_flag|}
+ 2: begin print('\&',lbrace); print_id(r); print(rbrace);
+ end; {|res_flag|}
+ 3: begin print('<'); print_id(r); print('>');
+ end; {|mod_flag|}
+ 4: print('[[',r:1,']]'); {|tok_flag|}
+ 5: print('|[[',r:1,']]|'); {|inner_tok_flag|}
+ othercases @<Print token |r| in symbolic form@>
+ endcases;
+ end;
+end;
+gubed
+
+@ @<Print token |r|...@>=
+case r of
+math_bin: print('\mathbin',lbrace);
+math_rel: print('\mathrel',lbrace);
+math_op: print('\mathop',lbrace);
+big_cancel: print('[ccancel]');
+cancel: print('[cancel]');
+indent: print('[indent]');
+outdent: print('[outdent]');
+backup: print('[backup]');
+opt: print('[opt]');
+break_space: print('[break]');
+force: print('[force]');
+big_force: print('[fforce]');
+end_translation: print('[quit]');
+othercases print(xchr[r])
+endcases
+
+@ The production rules listed above are embedded directly into the \.{WEAVE}
+program, since it is easier to do this than to write an interpretive system
+that would handle production systems in general. Several macros are defined
+here so that the program for each production is fairly short.
+
+All of our productions conform to the general notion that some |k|
+consecutive scraps starting at some position |j| are to be replaced by a
+single scrap of some category |c| whose translation is composed from the
+translations of the disappearing scraps. After this production has been
+applied, the production pointer |pp| should change by an amount |d|. Such
+a production can be represented by the quadruple $(j,k,c,d)$. For example,
+the production `|simp@,math| $\RA$ |math|' would be represented by
+`$(|pp|,2,|math|,-1)$'; in this case the pointer $pp$ should decrease by 1
+after the production has been applied, because some productions with
+|math| in their second positions might now match, but no productions have
+|math| in the third or fourth position of their left-hand sides. Note that
+the value of |d| is determined by the whole collection of productions, not
+by an individual one. Consider the further example
+`|var_head@,math@,colon| $\RA$ |var_head@,intro|', which is represented by
+`$(|pp|+1,2,|intro|,+1)$'; the $+1$ here is deduced by looking at the
+grammar and seeing that no matches could possibly occur at positions |<=pp|
+after this production has been applied. The determination of |d| has been
+done by hand in each case, based on the full set of productions but not on
+the grammar of \PASCAL\ or on the rules for constructing the initial
+scraps.
+
+We also attach a serial number to each production, so that additional
+information is available when debugging. For example, the program below
+contains the statement `|reduce(pp+1,2,intro,+1)(52)|' when it implements
+the production just mentioned.
+
+Before calling |reduce|, the program should have appended the tokens of
+the new translation to the |tok_mem| array. We commonly want to append
+copies of several existing translations, and macros are defined to
+simplify these common cases. For example, |app2(pp)| will append the
+translations of two consecutive scraps, |trans[pp]| and |trans[pp+1]|, to
+the current token list. If the entire new translation is formed in this
+way, we write `$|squash|(j,k,c,d)$' instead of `$|reduce|(j,k,c,d)$'. For
+example, `|squash(pp,2,math,-1)|' is an abbreviation for `|app2(pp);
+reduce(pp,2,math,-1)|'.
+
+The code below is an exact translation of the production rules into
+\PASCAL, using such macros, and the reader should have no difficulty
+understanding the format by comparing the code with the symbolic
+productions as they were listed earlier.
+
+{\sl Caution:\/} The macros |app|, |app1|, |app2|, and |app3| are
+sequences of statements that are not enclosed with |begin| and $|end|$,
+because such delimiters would make the \PASCAL\ program much longer. This
+means that it is necessary to write |begin| and |end| explicitly when such
+a macro is used as a single statement. Several mysterious bugs in the
+original programming of \.{WEAVE} were caused by a failure to remember
+this fact. Next time the author will know better.
+
+@d production(#)==@!debug prod(#) gubed; goto found
+@d reduce(#)==red(#); production
+@d production_end(#)==@!debug prod(#) gubed; goto found;
+ end
+@d squash(#)==begin sq(#); production_end
+@d app(#)==tok_mem[tok_ptr]:=#; incr(tok_ptr) {this is like |app_tok|,
+ but it doesn't test for overflow}
+@d app1(#)==tok_mem[tok_ptr]:=tok_flag+trans[#]; incr(tok_ptr)
+@d app2(#)==app1(#);app1(#+1)
+@d app3(#)==app2(#);app1(#+2)
+
+@ Let us consider the big case statement for productions now, before looking
+at its context. We want to design the program so that this case statement
+works, so we might as well not keep ourselves in suspense about exactly what
+code needs to be provided with a proper environment.
+
+The code here is more complicated than it need be, since some popular
+\PASCAL\ compilers are unable to deal with procedures that contain a lot
+of program text. The |translate| procedure, which incorporates the |case|
+statement here, would become too long for those compilers if we did
+not do something to split the cases into parts. Therefore
+a separate procedure called |five_cases| has been introduced.
+@^split procedures@>
+This auxiliary procedure contains approximately half of the program text
+that |translate| would otherwise have had. There's also a procedure
+called |alpha_cases|, which turned out to be necessary because the best
+two-way split wasn't good enough. The procedure could be split further
+in an analogous manner, but the present scheme works on all compilers
+known to the author.
+
+@<Match a production at |pp|, or increase |pp| if there is no match@>=
+if cat[pp]<=alpha then
+ if cat[pp]<alpha then five_cases@+else alpha_cases
+else begin case cat[pp] of
+ case_head: @<Cases for |case_head|@>;
+ casey: @<Cases for |casey|@>;
+ clause: @<Cases for |clause|@>;
+ cond: @<Cases for |cond|@>;
+ elsie: @<Cases for |elsie|@>;
+ exp: @<Cases for |exp|@>;
+ mod_scrap: @<Cases for |mod_scrap|@>;
+ proc: @<Cases for |proc|@>;
+ record_head: @<Cases for |record_head|@>;
+ semi: @<Cases for |semi|@>;
+ stmt: @<Cases for |stmt|@>;
+ terminator: @<Cases for |terminator|@>;
+ var_head: @<Cases for |var_head|@>;
+ othercases do_nothing
+ endcases;@/
+ incr(pp); {if no match was found, we move to the right}
+ found: end
+
+@ Here are the procedures that need to be present for the reason just
+explained.
+
+@<Declaration of subprocedures for |translate|@>=
+procedure five_cases; {handles almost half of the syntax}
+label found;
+begin case cat[pp] of
+beginning: @<Cases for |beginning|@>;
+intro: @<Cases for |intro|@>;
+math: @<Cases for |math|@>;
+open: @<Cases for |open|@>;
+simp: @<Cases for |simp|@>;
+othercases do_nothing
+endcases;@/
+incr(pp); {if no match was found, we move to the right}
+found: end;
+@#
+procedure alpha_cases;
+label found;
+begin @<Cases for |alpha|@>;
+incr(pp); {if no match was found, we move to the right}
+found: end;
+
+@ Now comes the code that tries to match each production starting
+with a particular type of scrap. Whenever a match is discovered,
+the |squash| or |reduce| macro will cause the appropriate action
+to be performed, followed by |goto found|.
+
+@<Cases for |alpha|@>=
+if cat[pp+1]=math then
+ begin if cat[pp+2]=colon then squash(pp+1,2,math,0)(1)
+ else if cat[pp+2]=omega then
+ begin app1(pp); app(" "); app("$"); app1(pp+1);
+ app("$"); app(" "); app(indent); app1(pp+2);
+ reduce(pp,3,clause,-2)(2);
+ end;
+ end
+else if cat[pp+1]=omega then
+ begin app1(pp); app(" "); app(indent); app1(pp+1);
+ reduce(pp,2,clause,-2)(3);
+ end
+else if cat[pp+1]=simp then squash(pp+1,1,math,0)(4)
+
+@ @<Cases for |beginning|@>=
+if cat[pp+1]=close then
+ begin if (cat[pp+2]=terminator)or(cat[pp+2]=stmt) then
+ squash(pp,3,stmt,-2)(5);
+ end
+else if cat[pp+1]=stmt then
+ begin app1(pp); app(break_space); app1(pp+1);
+ reduce(pp,2,beginning,-1)(6);
+ end
+
+@ @<Cases for |case_head|@>=
+if cat[pp+1]=casey then
+ begin if cat[pp+2]=clause then
+ begin app1(pp); app(outdent); app2(pp+1);
+ reduce(pp,3,case_head,0)(7);
+ end;
+ end
+else if cat[pp+1]=close then
+ begin if cat[pp+2]=terminator then
+ begin app1(pp); app(cancel); app(outdent); app2(pp+1);
+ reduce(pp,3,stmt,-2)(8);
+ end;
+ end
+else if cat[pp+1]=stmt then
+ begin app1(pp); app(force); app1(pp+1);
+ reduce(pp,2,case_head,0)(9);
+ end
+
+@ @<Cases for |casey|@>=
+if cat[pp+1]=clause then squash(pp,2,case_head,0)(10)
+
+@ @<Cases for |clause|@>=
+if cat[pp+1]=stmt then
+ begin app1(pp); app(break_space); app1(pp+1);
+ app(cancel); app(outdent);
+ app(force); reduce(pp,2,stmt,-2)(11);
+ end
+
+@ @<Cases for |cond|@>=
+if (cat[pp+1]=clause)and(cat[pp+2]=stmt) then
+ if cat[pp+3]=elsie then
+ begin app2(pp); app(break_space); app2(pp+2); app(" ");
+ app(cancel); reduce(pp,4,clause,-2)(12);
+ end
+ else begin app2(pp); app(break_space); app1(pp+2); app(cancel);
+ app(outdent); app(force); reduce(pp,3,stmt,-2)(13);
+ end
+
+@ @<Cases for |elsie|@>=
+squash(pp,1,intro,-3)(14)
+
+@ @<Cases for |exp|@>=
+if cat[pp+1]=math then
+ begin if cat[pp+2]=simp then if cat[pp+3]<>simp then
+ begin app3(pp); app("}"); reduce(pp,3,math,-1)(15);
+ end;
+ end
+else if cat[pp+1]=simp then if cat[pp+2]<>simp then
+ begin app2(pp); app("}"); reduce(pp,2,math,-1)(16);
+ end
+
+@ @<Cases for |intro|@>=
+if cat[pp+1]=stmt then
+ begin app1(pp); app(" "); app(opt); app("7");
+ app(cancel); app1(pp+1); reduce(pp,2,stmt,-2)(17);
+ end
+
+@ @<Cases for |math|@>=
+if cat[pp+1]=close then
+ begin app("$"); app1(pp); app("$"); reduce(pp,1,stmt,-2)(18);
+ end
+else if cat[pp+1]=colon then
+ begin app(force); app(backup); app("$"); app1(pp);
+ app("$"); app1(pp+1); reduce(pp,2,intro,-3)(19);
+ end
+else if cat[pp+1]=math then squash(pp,2,math,-1)(20)
+else if cat[pp+1]=simp then squash(pp,2,math,-1)(21)
+else if cat[pp+1]=stmt then
+ begin app("$"); app1(pp); app("$"); app(indent);
+ app(break_space); app1(pp+1); app(cancel); app(outdent);
+ app(force); reduce(pp,2,stmt,-2)(22);
+ end
+else if cat[pp+1]=terminator then
+ begin app("$"); app1(pp); app("$"); app1(pp+1);
+ reduce(pp,2,stmt,-2)(23);
+ end
+
+@ @<Cases for |mod_scrap|@>=
+if (cat[pp+1]=terminator)or(cat[pp+1]=semi) then
+ begin app2(pp); app(force); reduce(pp,2,stmt,-2)(24);
+ end
+else squash(pp,1,simp,-2)(25)
+
+@ @<Cases for |open|@>=
+if (cat[pp+1]=case_head)and(cat[pp+2]=close) then
+ begin app1(pp); app("$"); app(cancel); app1(pp+1); app(cancel);
+ app(outdent); app("$"); app1(pp+2); reduce(pp,3,math,-1)(26);
+ end
+else if cat[pp+1]=close then
+ begin app1(pp); app("\"); app(","); app1(pp+1);
+@.\\,@>
+ reduce(pp,2,math,-1)(27);
+ end
+else if cat[pp+1]=math then @<Cases for |open@,math|@>
+else if cat[pp+1]=proc then
+ begin if cat[pp+2]=intro then
+ begin app(math_op); app(cancel); app1(pp+1); app("}");
+ reduce(pp+1,2,math,0)(34);
+ end;
+ end
+else if cat[pp+1]=simp then squash(pp+1,1,math,0)(35)
+else if (cat[pp+1]=stmt)and(cat[pp+2]=close) then
+ begin app1(pp); app("$"); app(cancel); app1(pp+1); app(cancel);
+ app("$"); app1(pp+2); reduce(pp,3,math,-1)(36);
+ end
+else if cat[pp+1]=var_head then
+ begin if cat[pp+2]=intro then
+ begin app(math_op); app(cancel); app1(pp+1); app("}");
+ reduce(pp+1,2,math,0)(37);
+ end;
+ end
+
+@ @<Cases for |open@,math|@>=
+begin if (cat[pp+2]=case_head)and(cat[pp+3]=close) then
+ begin app2(pp); app("$"); app(cancel); app1(pp+2); app(cancel);
+ app(outdent); app("$"); app1(pp+3); reduce(pp,4,math,-1)(28);
+ end
+else if cat[pp+2]=close then squash(pp,3,math,-1)(29)
+else if cat[pp+2]=colon then squash(pp+1,2,math,0)(30)
+else if cat[pp+2]=proc then
+ begin if cat[pp+3]=intro then
+ begin app1(pp+1); app(math_op); app(cancel);
+ app1(pp+2); app("}"); reduce(pp+1,3,math,0)(31);
+ end;
+ end
+else if cat[pp+2]=semi then
+ begin app2(pp+1); app("\"); app(","); app(opt); app("5");
+@.\\,@>
+ reduce(pp+1,2,math,0)(32);
+ end
+else if cat[pp+2]=var_head then
+ begin if cat[pp+3]=intro then
+ begin app1(pp+1); app(math_op); app(cancel);
+ app1(pp+2); app("}"); reduce(pp+1,3,math,0)(31);
+ end;
+ end;
+end
+
+@ @<Cases for |proc|@>=
+if cat[pp+1]=beginning then
+ begin if (cat[pp+2]=close)and(cat[pp+3]=terminator) then
+ begin app1(pp); app(cancel); app(outdent); app3(pp+1);
+ reduce(pp,4,stmt,-2)(38);
+ end;
+ end
+else if cat[pp+1]=stmt then
+ begin app1(pp); app(break_space); app1(pp+1);
+ reduce(pp,2,proc,-2)(39);
+ end
+
+@ @<Cases for |record_head|@>=
+if (cat[pp+1]=intro)and(cat[pp+2]=casey) then
+ begin app2(pp); app(" "); app(cancel); app1(pp+2);
+ reduce(pp,3,casey,-2)(40);
+ end
+else begin app(indent); app1(pp); app(cancel);
+ reduce(pp,1,case_head,0)(41);
+ end
+
+@ @<Cases for |semi|@>=
+squash(pp,1,terminator,-3)(42)
+
+@ @<Cases for |simp|@>=
+if cat[pp+1]=close then squash(pp,1,stmt,-2)(43)
+else if cat[pp+1]=colon then
+ begin app(force); app(backup); app2(pp); reduce(pp,2,intro,-3)(44);
+ end
+else if cat[pp+1]=math then squash(pp,2,math,-1)(45)
+else if cat[pp+1]=mod_scrap then squash(pp,2,mod_scrap,0)(46)
+else if cat[pp+1]=simp then squash(pp,2,simp,-2)(47)
+else if cat[pp+1]=terminator then squash(pp,2,stmt,-2)(48)
+
+@ @<Cases for |stmt|@>=
+if cat[pp+1]=stmt then
+ begin app1(pp); app(break_space); app1(pp+1);
+ reduce(pp,2,stmt,-2)(49);
+ end
+
+@ @<Cases for |terminator|@>=
+squash(pp,1,stmt,-2)(50)
+
+@ @<Cases for |var_head|@>=
+if cat[pp+1]=beginning then squash(pp,1,stmt,-2)(51)
+else if cat[pp+1]=math then
+ begin if cat[pp+2]=colon then
+ begin app("$"); app1(pp+1); app("$"); app1(pp+2);
+ reduce(pp+1,2,intro,+1)(52);
+ end;
+ end
+else if cat[pp+1]=simp then
+ begin if cat[pp+2]=colon then squash(pp+1,2,intro,+1)(53);
+ end
+else if cat[pp+1]=stmt then
+ begin app1(pp); app(break_space); app1(pp+1);
+ reduce(pp,2,var_head,-2)(54);
+ end
+
+@ The `|freeze_text|' macro is used to give official status to a token list.
+Before saying |freeze_text|, items are appended to the current token list,
+and we know that the eventual number of this token list will be the current
+value of |text_ptr|. But no list of that number really exists as yet,
+because no ending point for the current list has been
+stored in the |tok_start| array. After saying |freeze_text|, the
+old current token list becomes legitimate, and its number is the current
+value of |text_ptr-1| since |text_ptr| has been increased. The new
+current token list is empty and ready to be appended to.
+Note that |freeze_text| does not check to see that |text_ptr| hasn't gotten
+too large, since it is assumed that this test was done beforehand.
+
+@d freeze_text==incr(text_ptr); tok_start[text_ptr]:=tok_ptr
+
+@ The `|reduce|' macro used in our code for productions actually calls on
+a procedure named `|red|', which makes the appropriate changes to the
+scrap list.
+
+@p procedure red(@!j:sixteen_bits; @!k:eight_bits; @!c:eight_bits;
+ @!d:integer);
+var i:0..max_scraps; {index into scrap memory}
+begin cat[j]:=c; trans[j]:=text_ptr; freeze_text;
+if k>1 then
+ begin for i:=j+k to lo_ptr do
+ begin cat[i-k+1]:=cat[i]; trans[i-k+1]:=trans[i];
+ end;
+ lo_ptr:=lo_ptr-k+1;
+ end;
+@<Change |pp| to $\max(|scrap_base|,|pp+d|)$@>;
+end;
+
+@ @<Change |pp| to $\max(|scrap_base|,|pp+d|)$@>=
+if pp+d>=scrap_base then pp:=pp+d
+else pp:=scrap_base
+
+@ Similarly, the `|squash|' macro invokes a procedure called `|sq|'. This
+procedure takes advantage of the simplification that occurs when |k=1|.
+
+@p procedure sq(@!j:sixteen_bits; @!k:eight_bits; @!c:eight_bits;
+ @!d:integer);
+var i:0..max_scraps; {index into scrap memory}
+begin if k=1 then
+ begin cat[j]:=c; @<Change |pp|...@>;
+ end
+else begin for i:=j to j+k-1 do
+ begin app1(i);
+ end;
+ red(j,k,c,d);
+ end;
+end;
+
+@ Here now is the code that applies productions as long as possible. It
+requires two local labels (|found| and |done|), as well as a local
+variable (|i|).
+
+@<Reduce the scraps using the productions until no more rules apply@>=
+loop@+begin @<Make sure the entries |cat[pp..(pp+3)]| are defined@>;
+ if (tok_ptr+8>max_toks)or(text_ptr+4>max_texts) then
+ begin stat if tok_ptr>max_tok_ptr then max_tok_ptr:=tok_ptr;
+ if text_ptr>max_txt_ptr then max_txt_ptr:=text_ptr;
+ tats@;@/
+ overflow('token/text');
+ end;
+ if pp>lo_ptr then goto done;
+ @<Match a production...@>;
+ end;
+done:
+
+@ If we get to the end of the scrap list, category codes equal to zero are
+stored, since zero does not match anything in a production.
+
+@<Make sure the entries...@>=
+if lo_ptr<pp+3 then
+ begin repeat if hi_ptr<=scrap_ptr then
+ begin incr(lo_ptr);@/
+ cat[lo_ptr]:=cat[hi_ptr]; trans[lo_ptr]:=trans[hi_ptr];@/
+ incr(hi_ptr);
+ end;
+ until (hi_ptr>scrap_ptr)or(lo_ptr=pp+3);
+ for i:=lo_ptr+1 to pp+3 do cat[i]:=0;
+ end
+
+@ If \.{WEAVE} is being run in debugging mode, the production numbers and
+current stack categories will be printed out when |tracing| is set to 2;
+a sequence of two or more irreducible scraps will be printed out when
+|tracing| is set to 1.
+@.\AT!2@>
+@.\AT!1@>
+
+@<Glo...@>=
+@!debug@!tracing:0..2; {can be used to show parsing details}
+gubed
+
+@ The |prod| procedure is called in debugging mode just after |reduce| or
+|squash|; its parameter is the number of the production that has just
+been applied.
+
+@p @!debug procedure prod(@!n:eight_bits); {shows current categories}
+var k:1..max_scraps; {index into |cat|}
+begin if tracing=2 then
+ begin print_nl(n:1,':');
+ for k:=scrap_base to lo_ptr do
+ begin if k=pp then print('*') @+ else print(' ');
+ print_cat(cat[k]);
+ end;
+ if hi_ptr<=scrap_ptr then print('...'); {indicate that more is coming}
+ end;
+end;
+gubed
+
+@ The |translate| function assumes that scraps have been stored in
+positions |scrap_base| through |scrap_ptr| of |cat| and |trans|. It
+appends a |terminator| scrap and begins to apply productions as much as
+possible. The result is a token list containing the translation of
+the given sequence of scraps.
+
+After calling |translate|, we will have |text_ptr+3<=max_texts| and
+|tok_ptr+6<=max_toks|, so it will be possible to create up to three token
+lists with up to six tokens without checking for overflow. Before calling
+|translate|, we should have |text_ptr<max_texts| and |scrap_ptr<max_scraps|,
+since |translate| might add a new text and a new scrap before it checks
+for overflow.
+
+@p @<Declaration of subprocedures for |translate|@>@;
+function translate:text_pointer; {converts a sequence of scraps}
+label done,found;
+var i: 1..max_scraps; {index into |cat|}
+@!j:0..max_scraps; {runs through final scraps}
+@!k:0..long_buf_size; {index into |buffer|}
+begin pp:=scrap_base; lo_ptr:=pp-1; hi_ptr:=pp;
+@<If tracing, print an indication of where we are@>;
+@<Reduce the scraps...@>;
+if (lo_ptr=scrap_base)and(cat[lo_ptr]<>math) then translate:=trans[lo_ptr]
+else @<Combine the irreducible scraps that remain@>;
+end;
+
+@ If the initial sequence of scraps does not reduce to a single scrap,
+we concatenate the translations of all remaining scraps, separated by
+blank spaces, with dollar signs surrounding the translations of |math|
+scraps.
+
+@<Combine the irreducible...@>=
+begin @<If semi-tracing, show the irreducible scraps@>;
+for j:=scrap_base to lo_ptr do
+ begin if j<>scrap_base then
+ begin app(" ");
+ end;
+ if cat[j]=math then
+ begin app("$");
+ end;
+ app1(j);
+ if cat[j]=math then
+ begin app("$");
+ end;
+ if tok_ptr+6>max_toks then overflow('token');
+ end;
+freeze_text; translate:=text_ptr-1;
+end
+
+@ @<If semi-tracing, show the irreducible scraps@>=
+@!debug if (lo_ptr>scrap_base)and(tracing=1) then
+ begin print_nl('Irreducible scrap sequence in section ',module_count:1);
+ print_ln(':'); mark_harmless;
+ for j:=scrap_base to lo_ptr do
+ begin print(' '); print_cat(cat[j]);
+ end;
+ end;
+gubed
+
+@ @<If tracing,...@>=
+@!debug if tracing=2 then
+ begin print_nl('Tracing after l.',line:1,':'); mark_harmless;
+ if loc>50 then
+ begin print('...');
+ for k:=loc-50 to loc do print(xchr[buffer[k-1]]);
+ end
+ else for k:=1 to loc do print(xchr[buffer[k-1]]);
+ end
+gubed
+
+@* Initializing the scraps.
+If we are going to use the powerful production mechanism just developed, we
+must get the scraps set up in the first place, given a \PASCAL\ text. A table
+of the initial scraps corresponding to \PASCAL\ tokens appeared above in the
+section on parsing; our goal now is to implement that table. We shall do this
+by implementing a subroutine called |Pascal_parse| that is analogous to the
+|Pascal_xref| routine used during phase one.
+
+Like |Pascal_xref|, the |Pascal_parse| procedure starts with the current
+value of |next_control| and it uses the operation |next_control:=get_next|
+repeatedly to read \PASCAL\ text until encountering the next `\v' or
+`\.\{', or until |next_control>=format|. The scraps corresponding to what
+it reads are appended into the |cat| and |trans| arrays, and |scrap_ptr|
+is advanced.
+
+Like |prod|, this procedure has to split into pieces so that each
+part is short enough to be handled by \PASCAL\ compilers that discriminate
+against long subroutines. This time there are two split-off routines,
+called |easy_cases| and |sub_cases|.
+@^split procedures@>
+
+After studying |Pascal_parse|, we will look at the sub-procedures
+|app_comment|, |app_octal|, and |app_hex| that are used in some of its
+branches.
+
+@p @<Declaration of the |app_comment| procedure@>@;
+@<Declaration of the |app_octal| and |app_hex| procedures@>@;
+@<Declaration of the |easy_cases| procedure@>@;
+@<Declaration of the |sub_cases| procedure@>@;
+procedure Pascal_parse; {creates scraps from \PASCAL\ tokens}
+label reswitch, exit;
+var j:0..long_buf_size; {index into |buffer|}
+@!p:name_pointer; {identifier designator}
+begin while next_control<format do
+ begin @<Append the scrap appropriate to |next_control|@>;
+ next_control:=get_next;
+ if (next_control="|")or(next_control="{") then return;
+ end;
+exit:end;
+
+@ The macros defined here are helpful abbreviations for the operations
+needed when generating the scraps. A scrap of category |c| whose
+translation has three tokens $t_1$, $t_2$, $t_3$ is generated by
+|sc3|$(t_1)(t_2)(t_3)(c)$, etc.
+
+@d s0(#)==incr(scrap_ptr); cat[scrap_ptr]:=#; trans[scrap_ptr]:=text_ptr;
+ freeze_text;
+ end
+@d s1(#)==app(#);s0
+@d s2(#)==app(#);s1
+@d s3(#)==app(#);s2
+@d s4(#)==app(#);s3
+@d sc4==@+begin s4
+@d sc3==@+begin s3
+@d sc2==@+begin s2
+@d sc1==@+begin s1
+@d sc0(#)==begin incr(scrap_ptr); cat[scrap_ptr]:=#; trans[scrap_ptr]:=0;
+ end
+@d comment_scrap(#)==begin app(#); app_comment;
+ end
+
+@ @<Append the scr...@>=
+@<Make sure that there is room for at least four more scraps, six more
+tokens, and four more texts@>;
+reswitch: case next_control of
+string,verbatim: @<Append a \(string scrap@>;
+identifier: @<Append an identifier scrap@>;
+TeX_string: @<Append a \TeX\ string scrap@>;
+othercases easy_cases
+endcases
+
+@ The |easy_cases| each result in straightforward scraps.
+
+@<Declaration of the |easy_cases| procedure@>=
+procedure easy_cases; {a subprocedure of |Pascal_parse|}
+begin case next_control of
+set_element_sign: sc3("\")("i")("n")(math);
+@.\\in@>
+double_dot: sc3("\")("t")("o")(math);
+@.\\to@>
+"#","$","%","^","_": sc2("\")(next_control)(math);
+@.\\\#@>
+@.\\\$@>
+@.\\\%@>
+@.\\\^@>
+ignore,"|",xref_roman,xref_wildcard,xref_typewriter: do_nothing;
+"(","[": sc1(next_control)(open);
+")","]": sc1(next_control)(close);
+"*": sc4("\")("a")("s")("t")(math);
+@.\\ast@>
+",": sc3(",")(opt)("9")(math);
+".","0","1","2","3","4","5","6","7","8","9": sc1(next_control)(simp);
+";": sc1(";")(semi);
+":": sc1(":")(colon);
+@t\4@> @<Cases involving nonstandard ASCII characters@>@;
+exponent: sc3("\")("E")("{")(exp);
+@.\\E@>
+begin_comment: sc2("\")("B")(math);
+@.\\B@>
+end_comment: sc2("\")("T")(math);
+@.\\T@>
+octal: app_octal;
+hex: app_hex;
+check_sum: sc2("\")(")")(simp);
+@.\\)@>
+force_line: sc2("\")("]")(simp);
+@.\\]@>
+thin_space: sc2("\")(",")(math);
+@.\\,@>
+math_break: sc2(opt)("0")(simp);
+line_break: comment_scrap(force);
+big_line_break: comment_scrap(big_force);
+no_line_break: begin app(big_cancel); app("\"); app(" ");
+@.\\\ @>
+ comment_scrap(big_cancel);
+ end;
+pseudo_semi: sc0(semi);
+join: sc2("\")("J")(math);
+@.\\J@>
+othercases sc1(next_control)(math)
+endcases;
+end;
+
+@ @<Make sure that there is room for at least four...@>=
+if (scrap_ptr+4>max_scraps)or(tok_ptr+6>max_toks)or(text_ptr+4>max_texts) then
+ begin stat if scrap_ptr>max_scr_ptr then max_scr_ptr:=scrap_ptr;
+ if tok_ptr>max_tok_ptr then max_tok_ptr:=tok_ptr;
+ if text_ptr>max_txt_ptr then max_txt_ptr:=text_ptr;
+ tats@;@/
+ overflow('scrap/token/text');
+ end
+
+@ Some nonstandard ASCII characters may have entered \.{WEAVE} by means of
+standard ones. They are converted to \TeX\ control sequences so that it is
+possible to keep \.{WEAVE} from stepping beyond standard ASCII.
+
+@<Cases involving nonstandard...@>=
+not_equal: sc2("\")("I")(math);
+@.\\I@>
+less_or_equal: sc2("\")("L")(math);
+@.\\L@>
+greater_or_equal: sc2("\")("G")(math);
+@.\\G@>
+equivalence_sign: sc2("\")("S")(math);
+@.\\S@>
+and_sign: sc2("\")("W")(math);
+@.\\W@>
+or_sign: sc2("\")("V")(math);
+@.\\V@>
+not_sign: sc2("\")("R")(math);
+@.\\R@>
+left_arrow: sc2("\")("K")(math);
+@.\\K@>
+
+@ The following code must use |app_tok| instead of |app| in order to
+protect against overflow. Note that |tok_ptr+1<=max_toks| after |app_tok|
+has been used, so another |app| is legitimate before testing again.
+
+Many of the special characters in a string must be prefixed by `\.\\' so that
+\TeX\ will print them properly.
+@^special string characters@>
+
+@<Append a \(string scrap@>=
+begin app("\");
+if next_control=verbatim then
+ begin app("=");
+@.\\=@>
+ end
+else begin app(".");
+@.\\.@>
+ end;
+app("{"); j:=id_first;
+while j<id_loc do
+ begin case buffer[j] of
+ " ","\","#","%","$","^","'","`","{","}","~","&","_":
+ begin app("\");
+ end;
+@.\\\ @>
+@.\\\\@>
+@.\\\#@>
+@.\\\%@>
+@.\\\$@>
+@.\\\^@>
+@.\\\'@>
+@.\\\`@>
+@.\\\{@>
+@.\\\}@>
+@.\\\~@>
+@.\\\&@>
+@.\\_@>
+ "@@": if buffer[j+1]="@@" then incr(j)
+ else err_print('! Double @@ should be used in strings');
+@.Double \AT! should be used...@>
+ othercases do_nothing
+ endcases;@/
+ app_tok(buffer[j]); incr(j);
+ end;
+sc1("}")(simp);
+end
+
+@ @<Append a \TeX\ string scrap@>=
+begin app("\"); app("h"); app("b"); app("o"); app("x");
+app("{");
+for j:=id_first to id_loc-1 do app_tok(buffer[j]);
+sc1("}")(simp);
+end
+
+@ @<Append an identifier scrap@>=
+begin p:=id_lookup(normal);
+case ilk[p] of
+normal,array_like,const_like,div_like,
+ do_like,for_like,goto_like,nil_like,to_like: sub_cases(p);
+@t\4@>@<Cases that generate more than one scrap@>@;
+othercases begin next_control:=ilk[p]-char_like; goto reswitch;
+ end {\&{and}, \&{in}, \&{not}, \&{or}}
+endcases;
+end
+
+@ The |sub_cases| also result in straightforward scraps.
+
+@<Declaration of the |sub_cases| procedure@>=
+procedure sub_cases(@!p:name_pointer); {a subprocedure of |Pascal_parse|}
+begin case ilk[p] of
+normal: sc1(id_flag+p)(simp); {not a reserved word}
+array_like: sc1(res_flag+p)(alpha); {\&{array}, \&{file}, \&{set}}
+const_like: sc3(force)(backup)(res_flag+p)(intro);
+ {\&{const}, \&{label}, \&{type}}
+div_like: sc3(math_bin)(res_flag+p)("}")(math); {\&{div}, \&{mod}}
+do_like: sc1(res_flag+p)(omega); {\&{do}, \&{of}, \&{then}}
+for_like: sc2(force)(res_flag+p)(alpha); {\&{for}, \&{while}, \&{with}}
+goto_like: sc1(res_flag+p)(intro); {\&{goto}, \&{packed}}
+nil_like: sc1(res_flag+p)(simp); {\&{nil}}
+to_like: sc3(math_rel)(res_flag+p)("}")(math); {\&{downto}, \&{to}}
+end;
+end;
+
+@ @<Cases that generate more than one scrap@>=
+begin_like: begin sc3(force)(res_flag+p)(cancel)(beginning); sc0(intro);
+ end; {\&{begin}}
+case_like: begin sc0(casey); sc2(force)(res_flag+p)(alpha);
+ end; {\&{case}}
+else_like: begin @<Append |terminator| if not already present@>;
+ sc3(force)(backup)(res_flag+p)(elsie);
+ end; {\&{else}}
+end_like: begin @<Append |term...@>;
+ sc2(force)(res_flag+p)(close);
+ end; {\&{end}}
+if_like: begin sc0(cond); sc2(force)(res_flag+p)(alpha);
+ end; {\&{if}}
+loop_like: begin sc3(force)("\")("~")(alpha);
+@.\\\~@>
+ sc1(res_flag+p)(omega);
+ end; {\&{xclause}}
+proc_like: begin sc4(force)(backup)(res_flag+p)(cancel)(proc);
+ sc3(indent)("\")(" ")(intro);
+@.\\\ @>
+ end; {\&{function}, \&{procedure}, \&{program}}
+record_like: begin sc1(res_flag+p)(record_head); sc0(intro);
+ end; {\&{record}}
+repeat_like: begin sc4(force)(indent)(res_flag+p)(cancel)(beginning);
+ sc0(intro);
+ end; {\&{repeat}}
+until_like: begin @<Append |term...@>;
+ sc3(force)(backup)(res_flag+p)(close); sc0(clause);
+ end; {\&{until}}
+var_like: begin sc4(force)(backup)(res_flag+p)(cancel)(var_head); sc0(intro);
+ end; {\&{var}}
+
+@ If a comment or semicolon appears before the reserved words \&{end},
+\&{else}, or \&{until}, the |semi| or |terminator| scrap that is already
+present overrides the |terminator| scrap belonging to this reserved word.
+
+@<Append |termin...@>=
+if (scrap_ptr<scrap_base)or((cat[scrap_ptr]<>terminator)and
+ (cat[scrap_ptr]<>semi)) then sc0(terminator)
+
+@ A comment is incorporated into the previous scrap if that scrap is of type
+|omega| or |semi| or |terminator|. (These three categories have consecutive
+category codes.) Otherwise the comment is entered as a separate scrap
+of type |terminator|, and it will combine with a |terminator| scrap that
+immediately follows~it.
+
+The |app_comment| procedure takes care of placing a comment at the end of the
+current scrap list. When |app_comment| is called, we assume that the current
+token list is the translation of the comment involved.
+
+@<Declaration of the |app_comment|...@>=
+procedure app_comment; {append a comment to the scrap list}
+begin freeze_text;
+if (scrap_ptr<scrap_base)or(cat[scrap_ptr]<omega)or
+ (cat[scrap_ptr]>terminator) then sc0(terminator)
+else begin app1(scrap_ptr); {|cat[scrap_ptr]| is
+ |omega| or |semi| or |terminator|}
+ end;
+app(text_ptr-1+tok_flag); trans[scrap_ptr]:=text_ptr; freeze_text;
+end;
+
+@ We are now finished with |Pascal_parse|, except for two relatively
+trivial subprocedures that convert constants into tokens.
+
+@<Declaration of the |app_octal| and...@>=
+procedure app_octal;
+begin app("\"); app("O"); app("{");
+@.\\O@>
+while (buffer[loc]>="0")and(buffer[loc]<="7") do
+ begin app_tok(buffer[loc]); incr(loc);
+ end;
+sc1("}")(simp);
+end;
+@#
+procedure app_hex;
+begin app("\"); app("H"); app("{");
+@.\\H@>
+while ((buffer[loc]>="0")and(buffer[loc]<="9"))or@|
+ ((buffer[loc]>="A")and(buffer[loc]<="F")) do
+ begin app_tok(buffer[loc]); incr(loc);
+ end;
+sc1("}")(simp);
+end;
+
+
+@ When the `\v' that introduces \PASCAL\ text is sensed, a call on
+|Pascal_translate| will return a pointer to the \TeX\ translation of
+that text. If scraps exist in the |cat| and |trans| arrays, they are
+unaffected by this translation process.
+
+@p function Pascal_translate: text_pointer;
+var p:text_pointer; {points to the translation}
+@!save_base:0..max_scraps; {holds original value of |scrap_base|}
+begin save_base:=scrap_base; scrap_base:=scrap_ptr+1;
+Pascal_parse; {get the scraps together}
+if next_control<>"|" then err_print('! Missing "|" after Pascal text');
+@.Missing "|"...@>
+app_tok(cancel); app_comment; {place a |cancel| token as a final ``comment''}
+p:=translate; {make the translation}
+stat if scrap_ptr>max_scr_ptr then max_scr_ptr:=scrap_ptr;@;@+tats@;@/
+scrap_ptr:=scrap_base-1; scrap_base:=save_base; {scrap the scraps}
+Pascal_translate:=p;
+end;
+
+@ The |outer_parse| routine is to |Pascal_parse| as |outer_xref|
+is to |Pascal_xref|: It constructs a sequence of scraps for \PASCAL\ text
+until |next_control>=format|. Thus, it takes care of embedded comments.
+
+@p procedure outer_parse; {makes scraps from \PASCAL\ tokens and comments}
+var bal:eight_bits; {brace level in comment}
+@!p,@!q:text_pointer; {partial comments}
+begin while next_control<format do
+ if next_control<>"{" then Pascal_parse
+ else begin @<Make sure that there is room for at least seven more
+ tokens, three more texts, and one more scrap@>;
+ app("\"); app("C"); app("{");
+@.\\C@>
+ bal:=copy_comment(1); next_control:="|";
+ while bal>0 do
+ begin p:=text_ptr; freeze_text; q:=Pascal_translate;
+ {at this point we have |tok_ptr+6<=max_toks|}
+ app(tok_flag+p); app(inner_tok_flag+q);
+ if next_control="|" then bal:=copy_comment(bal)
+ else bal:=0; {an error has been reported}
+ end;
+ app(force); app_comment; {the full comment becomes a scrap}
+ end;
+end;
+
+@ @<Make sure that there is room for at least seven more...@>=
+if (tok_ptr+7>max_toks)or(text_ptr+3>max_texts)or(scrap_ptr>=max_scraps) then
+ begin stat if scrap_ptr>max_scr_ptr then max_scr_ptr:=scrap_ptr;
+ if tok_ptr>max_tok_ptr then max_tok_ptr:=tok_ptr;
+ if text_ptr>max_txt_ptr then max_txt_ptr:=text_ptr;
+ tats@;@/
+ overflow('token/text/scrap');
+ end
+
+@* Output of tokens.
+So far our programs have only built up multi-layered token lists in
+\.{WEAVE}'s internal memory; we have to figure out how to get them into
+the desired final form. The job of converting token lists to characters in
+the \TeX\ output file is not difficult, although it is an implicitly
+recursive process. Four main considerations had to be kept in mind when
+this part of \.{WEAVE} was designed. (a) There are two modes of output:
+|outer| mode, which translates tokens like |force| into line-breaking
+control sequences, and |inner| mode, which ignores them except that blank
+spaces take the place of line breaks. (b) The |cancel| instruction applies
+to adjacent token or tokens that are output, and this cuts across levels
+of recursion since `|cancel|' occurs at the beginning or end of a token
+list on one level. (c) The \TeX\ output file will be semi-readable if line
+breaks are inserted after the result of tokens like |break_space| and
+|force|. (d) The final line break should be suppressed, and there should
+be no |force| token output immediately after `\.{\\Y\\P}'.
+
+@ The output process uses a stack to keep track of what is going on at
+different ``levels'' as the token lists are being written out. Entries on
+this stack have three parts:
+
+\yskip\hang |end_field| is the |tok_mem| location where the token list of a
+particular level will end;
+
+\yskip\hang |tok_field| is the |tok_mem| location from which the next token
+on a particular level will be read;
+
+\yskip\hang |mode_field| is the current mode, either |inner| or |outer|.
+
+\yskip\noindent The current values of these quantities are referred to
+quite frequently, so they are stored in a separate place instead of in the
+|stack| array. We call the current values |cur_end|, |cur_tok|, and
+|cur_mode|.
+
+The global variable |stack_ptr| tells how many levels of output are
+currently in progress. The end of output occurs when an |end_translation|
+token is found, so the stack is never empty except when we first begin the
+output process.
+
+@d inner=0 {value of |mode| for \PASCAL\ texts within \TeX\ texts}
+@d outer=1 {value of |mode| for \PASCAL\ texts in modules}
+
+@<Types...@>=
+@!mode=inner..outer;@/
+@!output_state=record@!end_field:sixteen_bits; {ending location of token list}
+ @!tok_field:sixteen_bits; {present location within token list}
+ @!mode_field:mode; {interpretation of control tokens}
+ end;
+
+@ @d cur_end==cur_state.end_field {current ending location in |tok_mem|}
+@d cur_tok==cur_state.tok_field {location of next output token in |tok_mem|}
+@d cur_mode==cur_state.mode_field {current mode of interpretation}
+@d init_stack==stack_ptr:=0;cur_mode:=outer {do this to initialize the stack}
+
+@<Glob...@>=
+@!cur_state:output_state; {|cur_end|, |cur_tok|, |cur_mode|}
+@!stack:array[1..stack_size] of output_state; {info for non-current levels}
+@!stack_ptr:0..stack_size; {first unused location in the output state stack}
+stat@!max_stack_ptr:0..stack_size; {largest value assumed by |stack_ptr|}
+tats
+
+@ @<Set init...@>=stat max_stack_ptr:=0;@+tats
+
+@ To insert token-list |p| into the output, the |push_level| subroutine
+is called; it saves the old level of output and gets a new one going.
+The value of |cur_mode| is not changed.
+
+@p procedure push_level(@!p:text_pointer); {suspends the current level}
+begin if stack_ptr=stack_size then overflow('stack')
+else begin if stack_ptr>0 then
+ stack[stack_ptr]:=cur_state; {save |cur_end|$\,\ldots\,$|cur_mode|}
+ incr(stack_ptr);
+ stat if stack_ptr>max_stack_ptr then
+ max_stack_ptr:=stack_ptr;@;@+tats@;@/
+ cur_tok:=tok_start[p]; cur_end:=tok_start[p+1];
+ end;
+end;
+
+@ Conversely, the |pop_level| routine restores the conditions that were in
+force when the current level was begun. This subroutine will never be
+called when |stack_ptr=1|. It is so simple, we declare it as a macro:
+
+@d pop_level==begin decr(stack_ptr); cur_state:=stack[stack_ptr];
+ end {do this when |cur_tok| reaches |cur_end|}
+
+@ The |get_output| function returns the next byte of output that is not a
+reference to a token list. It returns the values |identifier| or |res_word|
+or |mod_name| if the next token is to be an identifier (typeset in
+italics), a reserved word (typeset in boldface) or a module name (typeset
+by a complex routine that might generate additional levels of output).
+In these cases |cur_name| points to the identifier or module name in
+question.
+
+@d res_word=@'201 {returned by |get_output| for reserved words}
+@d mod_name=@'200 {returned by |get_output| for module names}
+
+@p function get_output:eight_bits; {returns the next token of output}
+label restart;
+var a:sixteen_bits; {current item read from |tok_mem|}
+begin restart: while cur_tok=cur_end do pop_level;
+a:=tok_mem[cur_tok]; incr(cur_tok);
+if a>=@'400 then
+ begin cur_name:=a mod id_flag;
+ case a div id_flag of
+ 2: a:=res_word; {|a=res_flag+cur_name|}
+ 3: a:=mod_name; {|a=mod_flag+cur_name|}
+ 4: begin push_level(cur_name); goto restart;
+ end; {|a=tok_flag+cur_name|}
+ 5: begin push_level(cur_name); cur_mode:=inner; goto restart;
+ end; {|a=inner_tok_flag+cur_name|}
+ othercases a:=identifier {|a=id_flag+cur_name|}
+ endcases;
+ end;
+@!debug if trouble_shooting then debug_help; @+ gubed@/
+get_output:=a;
+end;
+
+@ The real work associated with token output is done by |make_output|.
+This procedure appends an |end_translation| token to the current token list,
+and then it repeatedly calls |get_output| and feeds characters to the output
+buffer until reaching the |end_translation| sentinel. It is possible for
+|make_output| to
+be called recursively, since a module name may include embedded \PASCAL\
+text; however, the depth of recursion never exceeds one level, since
+module names cannot be inside of module names.
+
+A procedure called |output_Pascal| does the scanning, translation, and
+output of \PASCAL\ text within `\pb' brackets, and this procedure uses
+|make_output| to output the current token list. Thus, the recursive call
+of |make_output| actually occurs when |make_output| calls |output_Pascal|
+while outputting the name of a module.
+@^recursion@>
+
+@p procedure make_output; forward; @t\2@>@#
+procedure output_Pascal; {outputs the current token list}
+var save_tok_ptr,@!save_text_ptr,@!save_next_control:sixteen_bits;
+ {values to be restored}
+p:text_pointer; {translation of the \PASCAL\ text}
+begin save_tok_ptr:=tok_ptr; save_text_ptr:=text_ptr;
+save_next_control:=next_control; next_control:="|"; p:=Pascal_translate;
+app(p+inner_tok_flag);
+make_output; {output the list}
+stat if text_ptr>max_txt_ptr then max_txt_ptr:=text_ptr;
+if tok_ptr>max_tok_ptr then max_tok_ptr:=tok_ptr;@;@+tats@;@/
+text_ptr:=save_text_ptr; tok_ptr:=save_tok_ptr; {forget the tokens}
+next_control:=save_next_control; {restore |next_control| to original state}
+end;
+
+@ Here is \.{WEAVE}'s major output handler.
+
+@p procedure make_output; {outputs the equivalents of tokens}
+label reswitch,exit,found;
+var a:eight_bits; {current output byte}
+@!b:eight_bits; {next output byte}
+@!k,@!k_limit:0..max_bytes; {indices into |byte_mem|}
+@!w:0..ww-1; {row of |byte_mem|}
+@!j:0..long_buf_size; {index into |buffer|}
+@!string_delimiter:ASCII_code; {first and last character of
+ string being copied}
+@!save_loc,@!save_limit:0..long_buf_size; {|loc| and |limit| to be restored}
+@!cur_mod_name:name_pointer; {name of module being output}
+@!save_mode:mode; {value of |cur_mode| before a sequence of breaks}
+begin app(end_translation); {append a sentinel}
+freeze_text; push_level(text_ptr-1);
+loop@+ begin a:=get_output;
+reswitch: case a of
+ end_translation: return;
+ identifier,res_word:@<Output an identifier@>;
+ mod_name:@<Output a module name@>;
+ math_bin,math_op,math_rel:@<Output a \.{\\math} operator@>;
+ cancel: begin repeat a:=get_output;
+ until (a<backup)or(a>big_force);
+ goto reswitch;
+ end;
+ big_cancel: begin repeat a:=get_output;
+ until ((a<backup)and(a<>" "))or(a>big_force);
+ goto reswitch;
+ end;
+ indent,outdent,opt,backup,break_space,force,big_force:@<Output a
+ \(control, look ahead in case of line breaks,
+ possibly |goto reswitch|@>;
+ othercases out(a) {otherwise |a| is an ASCII character}
+ endcases;
+ end;
+exit:end;
+
+@ An identifier of length one does not have to be enclosed in braces, and it
+looks slightly better if set in a math-italic font instead of a (slightly
+narrower) text-italic font. Thus we output `\.{\\\char'174a}' but
+`\.{\\\\\{aa\}}'.
+
+@<Output an identifier@>=
+begin out("\");
+if a=identifier then
+ if length(cur_name)=1 then out("|")
+@.\\|@>
+ else out("\")
+@.\\\\@>
+else out("&"); {|a=res_word|}
+@.\\\&@>
+if length(cur_name)=1 then out(byte_mem[cur_name mod ww,byte_start[cur_name]])
+else out_name(cur_name);
+end
+
+@ @<Output a \....@>=
+begin out5("\")("m")("a")("t")("h");
+if a=math_bin then out3("b")("i")("n")
+else if a=math_rel then out3("r")("e")("l")
+else out2("o")("p");
+out("{");
+end
+
+@ The current mode does not affect the behavior of \.{WEAVE}'s output routine
+except when we are outputting control tokens.
+
+@<Output a \(control...@>=
+if a<break_space then
+ begin if cur_mode=outer then
+ begin out2("\")(a-cancel+"0");
+@.\\1@>
+@.\\2@>
+@.\\3@>
+@.\\4@>
+@.\\5@>
+@.\\6@>
+@.\\7@>
+ if a=opt then out(get_output) {|opt| is followed by a digit}
+ end
+ else if a=opt then b:=get_output {ignore digit following |opt|}
+ end
+else @<Look ahead for strongest line break, |goto reswitch|@>
+
+@ If several of the tokens |break_space|, |force|, |big_force| occur in a
+row, possibly mixed with blank spaces (which are ignored),
+the largest one is used. A line break also occurs in the output file,
+except at the very end of the translation. The very first line break
+is suppressed (i.e., a line break that follows `\.{\\Y\\P}').
+
+@<Look ahead for st...@>=
+begin b:=a; save_mode:=cur_mode;
+loop@+ begin a:=get_output;
+ if (a=cancel)or(a=big_cancel) then goto reswitch;
+ {|cancel| overrides everything}
+ if ((a<>" ")and(a<break_space))or(a>big_force) then
+ begin if save_mode=outer then
+ begin if out_ptr>3 then
+ if (out_buf[out_ptr]="P")and
+ (out_buf[out_ptr-1]="\")and
+@.\\P@>
+@.\\Y@>
+ (out_buf[out_ptr-2]="Y")and
+ (out_buf[out_ptr-3]="\") then
+ goto reswitch;
+@.\\1@>
+@.\\2@>
+@.\\3@>
+@.\\4@>
+@.\\5@>
+@.\\6@>
+@.\\7@>
+ out2("\")(b-cancel+"0");
+ if a<>end_translation then finish_line;
+ end
+ else if (a<>end_translation)and(cur_mode=inner) then out(" ");
+ goto reswitch;
+ end;
+ if a>b then b:=a; {if |a=" "| we have |a<b|}
+ end;
+end
+
+@ The remaining part of |make_output| is somewhat more complicated. When we
+output a module name, we may need to enter the parsing and translation
+routines, since the name may contain \PASCAL\ code embedded in
+\pb\ constructions. This \PASCAL\ code is placed at the end of the active
+input buffer and the translation process uses the end of the active
+|tok_mem| area.
+
+@<Output a module name@>=
+begin out2("\")("X");
+@.\\X@>
+cur_xref:=xref[cur_name];
+if num(cur_xref)>=def_flag then
+ begin out_mod(num(cur_xref)-def_flag);
+ if phase_three then
+ begin cur_xref:=xlink(cur_xref);
+ while num(cur_xref)>=def_flag do
+ begin out2(",")(" ");
+ out_mod(num(cur_xref)-def_flag);
+ cur_xref:=xlink(cur_xref);
+ end;
+ end;
+ end
+else out("0"); {output the module number, or zero if it was undefined}
+out(":"); @<Output the text of the module name@>;
+out2("\")("X");
+end
+
+@ @<Output the text...@>=
+k:=byte_start[cur_name]; w:=cur_name mod ww; k_limit:=byte_start[cur_name+ww];
+cur_mod_name:=cur_name;
+while k<k_limit do
+ begin b:=byte_mem[w,k]; incr(k);
+ if b="@@" then @<Skip next character, give error if not `\.{@@}'@>;
+ if b<>"|" then out(b)
+ else begin @<Copy the \PASCAL\ text into |buffer[(limit+1)..j]|@>;
+ save_loc:=loc; save_limit:=limit; loc:=limit+2; limit:=j+1;
+ buffer[limit]:="|"; output_Pascal;
+ loc:=save_loc; limit:=save_limit;
+ end;
+ end
+
+@ @<Skip next char...@>=
+begin if byte_mem[w,k]<>"@@" then
+ begin print_nl('! Illegal control code in section name:');
+@.Illegal control code...@>
+ print_nl('<'); print_id(cur_mod_name); print('> '); mark_error;
+ end;
+incr(k);
+end
+
+@ The \PASCAL\ text enclosed in \pb\ should not contain `\v' characters,
+except within strings. We put a `\v' at the front of the buffer, so that an
+error message that displays the whole buffer will look a little bit sensible.
+The variable |string_delimiter| is zero outside of strings, otherwise it
+equals the delimiter that began the string being copied.
+
+@<Copy the \PASCAL\ text into...@>=
+j:=limit+1; buffer[j]:="|"; string_delimiter:=0;
+loop@+ begin if k>=k_limit then
+ begin print_nl('! Pascal text in section name didn''t end:');
+@.Pascal text...didn't end@>
+ print_nl('<'); print_id(cur_mod_name); print('> '); mark_error;
+ goto found;
+ end;
+ b:=byte_mem[w,k]; incr(k);
+ if b="@@" then @<Copy a control code into the buffer@>
+ else begin if (b="""")or(b="'") then
+ if string_delimiter=0 then string_delimiter:=b
+ else if string_delimiter=b then string_delimiter:=0;
+ if (b<>"|")or(string_delimiter<>0) then
+ begin if j>long_buf_size-3 then overflow('buffer');
+ incr(j); buffer[j]:=b;
+ end
+ else goto found;
+ end;
+ end;
+found:
+
+@ @<Copy a control code into the buffer@>=
+begin if j>long_buf_size-4 then overflow('buffer');
+buffer[j+1]:="@@"; buffer[j+2]:=byte_mem[w,k]; j:=j+2; incr(k);
+end
+
+@* Phase two processing.
+We have assembled enough pieces of the puzzle in order to be ready to specify
+the processing in \.{WEAVE}'s main pass over the source file. Phase two
+is analogous to phase one, except that more work is involved because we must
+actually output the \TeX\ material instead of merely looking at the
+\.{WEB} specifications.
+
+@<Phase II: Read all the text again and translate it to \TeX\ form@>=
+reset_input; print_nl('Writing the output file...');
+module_count:=0;
+copy_limbo;
+finish_line; flush_buffer(0,false,false); {insert a blank line, it looks nice}
+while not input_has_ended do @<Translate the \(current module@>
+
+@ The output file will contain the control sequence \.{\\Y} between non-null
+sections of a module, e.g., between the \TeX\ and definition parts if both
+are nonempty. This puts a little white space between the parts when they are
+printed. However, we don't want \.{\\Y} to occur between two definitions
+within a single module. The variables |out_line| or |out_ptr| will
+change if a section is non-null, so the following macros `|save_position|'
+and `|emit_space_if_needed|' are able to handle the situation:
+
+@d save_position==save_line:=out_line; save_place:=out_ptr
+@d emit_space_if_needed==if (save_line<>out_line)or(save_place<>out_ptr) then
+ out2("\")("Y")
+@.\\Y@>
+
+@<Glo...@>=
+@!save_line:integer; {former value of |out_line|}
+@!save_place:sixteen_bits; {former value of |out_ptr|}
+
+@ @<Translate the \(current module@>=
+begin incr(module_count);@/
+@<Output the code for the beginning of a new module@>;
+save_position;@/
+@<Translate the \TeX\ part of the current module@>;
+@<Translate the \(definition part of the current module@>;
+@<Translate the \PASCAL\ part of the current module@>;
+@<Show cross references to this module@>;
+@<Output the code for the end of a module@>;
+end
+
+@ Modules beginning with the \.{WEB} control sequence `\.{@@\ }' start in the
+output with the \TeX\ control sequence `\.{\\M}', followed by the module
+number. Similarly, `\.{@@*}' modules lead to the control sequence `\.{\\N}'.
+If this is a changed module, we put \.{*} just before the module number.
+
+@<Output the code for the beginning...@>=
+out("\");
+if buffer[loc-1]<>"*" then out("M")
+@.\\M@>
+else begin out("N"); print('*',module_count:1);
+@.\\N@>
+ update_terminal; {print a progress report}
+ end;
+out_mod(module_count); out2(".")(" ")
+
+@ In the \TeX\ part of a module, we simply copy the source text, except that
+index entries are not copied and \PASCAL\ text within \pb\ is translated.
+
+@<Translate the \T...@>=
+repeat next_control:=copy_TeX;
+case next_control of
+"|": begin init_stack; output_Pascal;
+ end;
+"@@": out("@@");
+octal: @<Translate an octal constant appearing in \TeX\ text@>;
+hex: @<Translate a hexadecimal constant appearing in \TeX\ text@>;
+TeX_string,xref_roman,xref_wildcard,xref_typewriter,module_name:
+ begin loc:=loc-2; next_control:=get_next; {skip to \.{@@>}}
+ if next_control=TeX_string then
+ err_print('! TeX string should be in Pascal text only');
+@.TeX string should be...@>
+ end;
+begin_comment,end_comment,check_sum,thin_space,math_break,line_break,
+ big_line_break,no_line_break,join,pseudo_semi:
+ err_print('! You can''t do that in TeX text');
+@.You can't do that...@>
+othercases do_nothing
+endcases;
+until next_control>=format
+
+@ @<Translate an octal constant appearing in \TeX\ text@>=
+begin out3("\")("O")("{");
+@.\\O@>
+while (buffer[loc]>="0")and(buffer[loc]<="7") do
+ begin out(buffer[loc]); incr(loc);
+ end; {since |buffer[limit]=" "|, this loop will end}
+out("}");
+end
+
+@ @<Translate a hexadecimal constant appearing in \TeX\ text@>=
+begin out3("\")("H")("{");
+@.\\H@>
+while ((buffer[loc]>="0")and(buffer[loc]<="9"))or@|
+ ((buffer[loc]>="A")and(buffer[loc]<="F")) do
+ begin out(buffer[loc]); incr(loc);
+ end;
+out("}");
+end
+@ When we get to the following code we have |next_control>=format|, and
+the token memory is in its initial empty state.
+
+@<Translate the \(d...@>=
+if next_control<=definition then {definition part non-empty}
+ begin emit_space_if_needed; save_position;
+ end;
+while next_control<=definition do {|format| or |definition|}
+ begin init_stack;
+ if next_control=definition then @<Start a macro definition@>
+ else @<Start a format definition@>;
+ outer_parse; finish_Pascal;
+ end
+
+@ The |finish_Pascal| procedure outputs the translation of the current
+scraps, preceded by the control sequence `\.{\\P}' and followed by the
+control sequence `\.{\\par}'. It also restores the token and scrap
+memories to their initial empty state.
+
+A |force| token is appended to the current scraps before translation
+takes place, so that the translation will normally end with \.{\\6} or
+\.{\\7} (the \TeX\ macros for |force| and |big_force|). This \.{\\6} or
+\.{\\7} is replaced by the concluding \.{\\par} or by \.{\\Y\\par}.
+
+@p procedure finish_Pascal; {finishes a definition or a \PASCAL\ part}
+var p:text_pointer; {translation of the scraps}
+begin out2("\")("P"); app_tok(force); app_comment; p:=translate;
+@.\\P@>
+app(p+tok_flag); make_output; {output the list}
+if out_ptr>1 then
+ if out_buf[out_ptr-1]="\" then
+@.\\6@>
+@.\\7@>
+@.\\Y@>
+ if out_buf[out_ptr]="6" then out_ptr:=out_ptr-2
+ else if out_buf[out_ptr]="7" then out_buf[out_ptr]:="Y";
+out4("\")("p")("a")("r"); finish_line;
+stat if text_ptr>max_txt_ptr then max_txt_ptr:=text_ptr;
+if tok_ptr>max_tok_ptr then max_tok_ptr:=tok_ptr;
+if scrap_ptr>max_scr_ptr then max_scr_ptr:=scrap_ptr;
+tats@;@/
+tok_ptr:=1; text_ptr:=1; scrap_ptr:=0; {forget the tokens and the scraps}
+end;
+
+@ @<Start a macro...@>=
+begin sc2("\")("D")(intro); {this will produce `\&{define }'}
+@.\\D@>
+next_control:=get_next;
+if next_control<>identifier then err_print('! Improper macro definition')
+@.Improper macro definition@>
+else sc1(id_flag+id_lookup(normal))(math);
+next_control:=get_next;
+end
+
+@ @<Start a format...@>=
+begin sc2("\")("F")(intro); {this will produce `\&{format }'}
+@.\\F@>
+next_control:=get_next;
+if next_control=identifier then
+ begin sc1(id_flag+id_lookup(normal))(math);
+ next_control:=get_next;
+ if next_control=equivalence_sign then
+ begin sc2("\")("S")(math); {output an equivalence sign}
+@.\\S@>
+ next_control:=get_next;
+ if next_control=identifier then
+ begin sc1(id_flag+id_lookup(normal))(math);
+ sc0(semi); {insert an invisible semicolon}
+ next_control:=get_next;
+ end;
+ end;
+ end;
+if scrap_ptr<>5 then err_print('! Improper format definition');
+@.Improper format definition@>
+end
+
+@ Finally, when the \TeX\ and definition parts have been treated, we have
+|next_control>=begin_Pascal|. We will make the global variable |this_module|
+point to the current module name, if it has a name.
+
+@<Glob...@>=@!this_module:name_pointer; {the current module name, or zero}
+
+@ @<Translate the \P...@>=
+this_module:=0;
+if next_control<=module_name then
+ begin emit_space_if_needed; init_stack;
+ if next_control=begin_Pascal then next_control:=get_next
+ else begin this_module:=cur_module;
+ @<Check that |=| or |==| follows this module name, and
+ emit the scraps to start the module definition@>;
+ end;
+ while next_control<=module_name do
+ begin outer_parse;
+ @<Emit the scrap for a module name if present@>;
+ end;
+ finish_Pascal;
+ end
+
+@ @<Check that |=|...@>=
+repeat next_control:=get_next;
+until next_control<>"+"; {allow optional `\.{+=}'}
+if (next_control<>"=")and(next_control<>equivalence_sign) then
+ err_print('! You need an = sign after the section name')
+@.You need an = sign...@>
+else next_control:=get_next;
+if out_ptr>1 then
+ if (out_buf[out_ptr]="Y")and(out_buf[out_ptr-1]="\") then
+@.\\Y@>
+ begin app(backup); {the module name will be flush left}
+ end;
+sc1(mod_flag+this_module)(mod_scrap);
+cur_xref:=xref[this_module];
+if num(cur_xref)<>module_count+def_flag then
+ begin sc3(math_rel)("+")("}")(math);
+ {module name is multiply defined}
+ this_module:=0; {so we won't give cross-reference info here}
+ end;
+sc2("\")("S")(math); {output an equivalence sign}
+@.\\S@>
+sc1(force)(semi); {this forces a line break unless `\.{@@+}' follows}
+
+@ @<Emit the scrap...@>=
+if next_control<module_name then
+ begin err_print('! You can''t do that in Pascal text');
+@.You can't do that...@>
+ next_control:=get_next;
+ end
+else if next_control=module_name then
+ begin sc1(mod_flag+cur_module)(mod_scrap); next_control:=get_next;
+ end
+
+@ Cross references relating to a named module are given after the module ends.
+
+@<Show cross...@>=
+if this_module>0 then
+ begin @<Rearrange the list pointed to by |cur_xref|@>;
+ footnote(def_flag); footnote(0);
+ end
+
+@ To rearrange the order of the linked list of cross references, we need
+four more variables that point to cross reference entries. We'll end up
+with a list pointed to by |cur_xref|.
+
+@<Glob...@>=
+@!next_xref,@!this_xref,@!first_xref,@!mid_xref:xref_number;
+ {pointer variables for rearranging a list}
+
+@ We want to rearrange the cross reference list so that all the entries with
+|def_flag| come first, in ascending order; then come all the other
+entries, in ascending order. There may be no entries in either one or both
+of these categories.
+
+@<Rearrange the list...@>=
+first_xref:=xref[this_module];
+this_xref:=xlink(first_xref); {bypass current module number}
+if num(this_xref)>def_flag then
+ begin mid_xref:=this_xref; cur_xref:=0; {this value doesn't matter}
+ repeat next_xref:=xlink(this_xref); xlink(this_xref):=cur_xref;
+ cur_xref:=this_xref; this_xref:=next_xref;
+ until num(this_xref)<=def_flag;
+ xlink(first_xref):=cur_xref;
+ end
+else mid_xref:=0; {first list null}
+cur_xref:=0;
+while this_xref<>0 do
+ begin next_xref:=xlink(this_xref); xlink(this_xref):=cur_xref;
+ cur_xref:=this_xref; this_xref:=next_xref;
+ end;
+if mid_xref>0 then xlink(mid_xref):=cur_xref
+else xlink(first_xref):=cur_xref;
+cur_xref:=xlink(first_xref)
+
+@ The |footnote| procedure gives cross reference information about
+multiply defined module names (if the |flag| parameter is |def_flag|), or about
+the uses of a module name (if the |flag| parameter is zero). It assumes that
+|cur_xref| points to the first cross-reference entry of interest, and it
+leaves |cur_xref| pointing to the first element not printed. Typical outputs:
+`\.{\\A101.}'; `\.{\\Us370\\ET1009.}'; `\.{\\As8, 27\\*, 51\\ETs64.}'.
+
+@p procedure footnote(@!flag:sixteen_bits); {outputs module cross-references}
+label done,exit;
+var q:xref_number; {cross-reference pointer variable}
+begin if num(cur_xref)<=flag then return;
+finish_line; out("\");
+@.\\A@>
+@.\\U@>
+if flag=0 then out("U")@+else out("A");
+@<Output all the module numbers on the reference list |cur_xref|@>;
+out(".");
+exit:end;
+
+@ The following code distinguishes three cases, according as the number
+of cross references is one, two, or more than two. Variable |q| points
+to the first cross reference, and the last link is a zero.
+
+@<Output all the module numbers...@>=
+q:=cur_xref; if num(xlink(q))>flag then out("s"); {plural}
+@.\\As@>
+@.\\Us@>
+loop@+ begin out_mod(num(cur_xref)-flag);
+ cur_xref:=xlink(cur_xref); {point to the next cross reference to output}
+ if num(cur_xref)<=flag then goto done;
+ if num(xlink(cur_xref))>flag then out2(",")(" ") {not the last}
+ else begin out3("\")("E")("T"); {the last}
+@.\\ET@>
+ if cur_xref<>xlink(q) then out("s"); {the last of more than two}
+@.\\ETs@>
+ end;
+ end;
+done:
+
+@ @<Output the code for the end of a module@>=
+out3("\")("f")("i"); finish_line;
+flush_buffer(0,false,false); {insert a blank line, it looks nice}
+@.\\fi@>
+
+@* Phase three processing.
+We are nearly finished! \.{WEAVE}'s only remaining task is to write out the
+index, after sorting the identifiers and index entries.
+
+@<Phase III: Output the cross-reference index@>=
+phase_three:=true; print_nl('Writing the index...');
+if change_exists then
+ begin finish_line; @<Tell about changed modules@>;
+ end;
+finish_line; out4("\")("i")("n")("x"); finish_line;
+@.\\inx@>
+@<Do the first pass of sorting@>;
+@<Sort and output the index@>;
+out4("\")("f")("i")("n"); finish_line;
+@.\\fin@>
+@<Output all the module names@>;
+out4("\")("c")("o")("n"); finish_line;
+@.\\con@>
+print('Done.');
+
+@ Just before the index comes a list of all the changed modules, including
+the index module itself.
+
+@<Glob...@>=
+@!k_module:0..max_modules; {runs through the modules}
+
+@ @<Tell about changed modules@>=
+begin {remember that the index is already marked as changed}
+k_module:=1;
+out4("\")("c")("h")(" ");
+while k_module<module_count do
+ begin if changed_module[k_module] then
+ begin out_mod(k_module); out2(",")(" ");
+ end;
+ incr(k_module);
+ end;
+out_mod(k_module);
+out(".");
+end
+
+@ A left-to-right radix sorting method is used, since this makes it easy to
+adjust the collating sequence and since the running time will be at worst
+proportional to the total length of all entries in the index. We put the
+identifiers into 230 different lists based on their first characters.
+(Uppercase letters are put into the same list as the corresponding lowercase
+letters, since we want to have `$t<\\{TeX}<\&{to}$'.) The
+list for character |c| begins at location |bucket[c]| and continues through
+the |blink| array.
+
+@<Glob...@>=
+@!bucket:array[ASCII_code] of name_pointer;
+@!next_name: name_pointer; {successor of |cur_name| when sorting}
+@!c:ASCII_code; {index into |bucket|}
+@!h:0..hash_size; {index into |hash|}
+@!blink:array[0..max_names] of sixteen_bits; {links in the buckets}
+
+@ To begin the sorting, we go through all the hash lists and put each entry
+having a nonempty cross-reference list into the proper bucket.
+
+@<Do the first pass...@>=
+for c:=0 to 255 do bucket[c]:=0;
+for h:=0 to hash_size-1 do
+ begin next_name:=hash[h];
+ while next_name<>0 do
+ begin cur_name:=next_name; next_name:=link[cur_name];
+ if xref[cur_name]<>0 then
+ begin c:=byte_mem[cur_name mod ww,byte_start[cur_name]];
+ if (c<="Z")and(c>="A") then c:=c+@'40;
+ blink[cur_name]:=bucket[c]; bucket[c]:=cur_name;
+ end;
+ end;
+ end
+
+@ During the sorting phase we shall use the |cat| and |trans| arrays from
+\.{WEAVE}'s parsing algorithm and rename them |depth| and |head|. They now
+represent a stack of identifier lists for all the index entries that have
+not yet been output. The variable |sort_ptr| tells how many such lists are
+present; the lists are output in reverse order (first |sort_ptr|, then
+|sort_ptr-1|, etc.). The |j|th list starts at |head[j]|, and if the first
+|k| characters of all entries on this list are known to be equal we have
+|depth[j]=k|.
+
+@d depth==cat {reclaims memory that is no longer needed for parsing}
+@d head==trans {ditto}
+@d sort_ptr==scrap_ptr {ditto}
+@d max_sorts==max_scraps {ditto}
+
+@<Globals...@>=
+@!cur_depth:eight_bits; {depth of current buckets}
+@!cur_byte:0..max_bytes; {index into |byte_mem|}
+@!cur_bank:0..ww-1; {row of |byte_mem|}
+@!cur_val:sixteen_bits; {current cross reference number}
+stat@!max_sort_ptr:0..max_sorts;@+tats {largest value of |sort_ptr|}
+
+@ @<Set init...@>=stat max_sort_ptr:=0;@+tats
+
+@ The desired alphabetic order is specified by the |collate| array; namely,
+|collate[0]<collate[1]<@t$\cdots$@><collate[229]|.
+
+@<Glob...@>=@!collate:array[0..229] of ASCII_code; {collation order}
+
+@ @<Local variables for init...@>=
+@!c:ASCII_code; {used to initialize |collate|}
+
+@ We use the order $\hbox{null}<\.\ <\hbox{other characters}<\.\_<
+\.A=\.a<\cdots<\.Z=\.z<\.0<\cdots<\.9.$
+
+@<Set init...@>=
+collate[0]:=0; collate[1]:=" ";
+for c:=1 to " "-1 do collate[c+1]:=c;
+for c:=" "+1 to "0"-1 do collate[c]:=c;
+for c:="9"+1 to "A"-1 do collate[c-10]:=c;
+for c:="Z"+1 to "_"-1 do collate[c-36]:=c;
+collate["_"-36]:="_"+1;
+for c:="z"+1 to 255 do collate[c-63]:=c;
+collate[193]:="_";
+for c:="a" to "z" do collate[c-"a"+194]:=c;
+for c:="0" to "9" do collate[c-"0"+220]:=c;
+
+@ Procedure |unbucket| goes through the buckets and adds nonempty lists
+to the stack, using the collating sequence specified in the |collate| array.
+The parameter to |unbucket| tells the current depth in the buckets.
+Any two sequences that agree in their first 255 character positions are
+regarded as identical.
+
+@d infinity=255 {$\infty$ (approximately)}
+
+@p procedure unbucket(@!d:eight_bits); {empties buckets having depth |d|}
+var c:ASCII_code; {index into |bucket|}
+begin for c:=229 downto 0 do if bucket[collate[c]]>0 then
+ begin if sort_ptr>max_sorts then overflow('sorting');
+ incr(sort_ptr);
+ stat if sort_ptr>max_sort_ptr then max_sort_ptr:=sort_ptr;@;@+tats@;@/
+ if c=0 then depth[sort_ptr]:=infinity else depth[sort_ptr]:=d;
+ head[sort_ptr]:=bucket[collate[c]]; bucket[collate[c]]:=0;
+ end;
+end;
+
+@ @<Sort and output...@>=
+sort_ptr:=0; unbucket(1);
+while sort_ptr>0 do
+ begin cur_depth:=cat[sort_ptr];
+ if (blink[head[sort_ptr]]=0)or(cur_depth=infinity) then
+ @<Output index entries for the list at |sort_ptr|@>
+ else @<Split the list at |sort_ptr| into further lists@>;
+ end
+
+@ @<Split the list...@>=
+begin next_name:=head[sort_ptr];
+repeat cur_name:=next_name; next_name:=blink[cur_name];
+ cur_byte:=byte_start[cur_name]+cur_depth; cur_bank:=cur_name mod ww;
+ if cur_byte=byte_start[cur_name+ww] then c:=0 {we hit the end of the name}
+ else begin c:=byte_mem[cur_bank,cur_byte];
+ if (c<="Z")and(c>="A") then c:=c+@'40;
+ end;
+ blink[cur_name]:=bucket[c]; bucket[c]:=cur_name;
+until next_name=0;
+decr(sort_ptr); unbucket(cur_depth+1);
+end
+
+@ @<Output index...@>=
+begin cur_name:=head[sort_ptr];
+@!debug if trouble_shooting then debug_help;@;@+gubed@/
+repeat out2("\")(":");
+@.\\:@>
+ @<Output the name at |cur_name|@>;
+ @<Output the cross-references at |cur_name|@>;
+ cur_name:=blink[cur_name];
+until cur_name=0;
+decr(sort_ptr);
+end
+
+@ @<Output the name...@>=
+case ilk[cur_name] of
+normal: if length(cur_name)=1 then out2("\")("|")@+else out2("\")("\");
+@.\\|@>
+@.\\\\@>
+roman: do_nothing;
+wildcard: out2("\")("9");
+@.\\9@>
+typewriter: out2("\")(".");
+@.\\.@>
+othercases out2("\")("&")
+@.\\\&@>
+endcases;@/
+out_name(cur_name)
+
+@ Section numbers that are to be underlined are enclosed in
+`\.{\\[}$\,\ldots\,$\.]'.
+
+@<Output the cross-references...@>=
+@<Invert the cross-reference list at |cur_name|, making |cur_xref| the head@>;
+repeat out2(",")(" "); cur_val:=num(cur_xref);
+if cur_val<def_flag then out_mod(cur_val)
+else begin out2("\")("["); out_mod(cur_val-def_flag); out("]");
+@.\\[@>
+ end;
+cur_xref:=xlink(cur_xref);
+until cur_xref=0;
+out("."); finish_line
+
+@ List inversion is best thought of as popping elements off one stack and
+pushing them onto another. In this case |cur_xref| will be the head of
+the stack that we push things onto.
+
+@<Invert the cross-reference list at |cur_name|, making |cur_xref| the head@>=
+this_xref:=xref[cur_name]; cur_xref:=0;
+repeat next_xref:=xlink(this_xref); xlink(this_xref):=cur_xref;
+cur_xref:=this_xref; this_xref:=next_xref;
+until this_xref=0
+
+@ The following recursive procedure walks through the tree of module names and
+prints them.
+@^recursion@>
+
+@p procedure mod_print(p:name_pointer); {print all module names in subtree |p|}
+begin if p>0 then
+ begin mod_print(llink[p]);@/
+ out2("\")(":");@/
+@.\\:@>
+ tok_ptr:=1; text_ptr:=1; scrap_ptr:=0; init_stack;
+ app(p+mod_flag); make_output;
+ footnote(0); {|cur_xref| was set by |make_output|}
+ finish_line;@/
+ mod_print(rlink[p]);
+ end;
+end;
+
+@ @<Output all the module names@>=@+mod_print(root)
+
+@* Debugging.
+The \PASCAL\ debugger with which \.{WEAVE} was developed allows breakpoints
+to be set, and variables can be read and changed, but procedures cannot be
+executed. Therefore a `|debug_help|' procedure has been inserted in the main
+loops of each phase of the program; when |ddt| and |dd| are set to appropriate
+values, symbolic printouts of various tables will appear.
+
+The idea is to set a breakpoint inside the |debug_help| routine, at the
+place of `\ignorespaces|breakpoint:|\unskip' below. Then when
+|debug_help| is to be activated, set |trouble_shooting| equal to |true|.
+The |debug_help| routine will prompt you for values of |ddt| and |dd|,
+discontinuing this when |ddt<=0|; thus you type $2n+1$ integers, ending
+with zero or a negative number. Then control either passes to the
+breakpoint, allowing you to look at and/or change variables (if you typed
+zero), or to exit the routine (if you typed a negative value).
+
+Another global variable, |debug_cycle|, can be used to skip silently
+past calls on |debug_help|. If you set |debug_cycle>1|, the program stops
+only every |debug_cycle| times |debug_help| is called; however,
+any error stop will set |debug_cycle| to zero.
+
+@<Globals...@>=
+@!debug@!trouble_shooting:boolean; {is |debug_help| wanted?}
+@!ddt:integer; {operation code for the |debug_help| routine}
+@!dd:integer; {operand in procedures performed by |debug_help|}
+@!debug_cycle:integer; {threshold for |debug_help| stopping}
+@!debug_skipped:integer; {we have skipped this many |debug_help| calls}
+@!term_in:text_file; {the user's terminal as an input file}
+gubed
+
+@ The debugging routine needs to read from the user's terminal.
+@^system dependencies@>
+@<Set init...@>=
+@!debug trouble_shooting:=true; debug_cycle:=1; debug_skipped:=0; tracing:=0;@/
+trouble_shooting:=false; debug_cycle:=99999; {use these when it almost works}
+reset(term_in,'TTY:','/I'); {open |term_in| as the terminal, don't do a |get|}
+gubed
+
+@ @d breakpoint=888 {place where a breakpoint is desirable}
+@^system dependencies@>
+
+@p @!debug procedure debug_help; {routine to display various things}
+label breakpoint,exit;
+var k:integer; {index into various arrays}
+begin incr(debug_skipped);
+if debug_skipped<debug_cycle then return;
+debug_skipped:=0;
+loop@+ begin print_nl('#'); update_terminal; {prompt}
+ read(term_in,ddt); {read a debug-command code}
+ if ddt<0 then return
+ else if ddt=0 then
+ begin goto breakpoint;@\ {go to every label at least once}
+ breakpoint: ddt:=0;@\
+ end
+ else begin read(term_in,dd);
+ case ddt of
+ 1: print_id(dd);
+ 2: print_text(dd);
+ 3: for k:=1 to dd do print(xchr[buffer[k]]);
+ 4: for k:=1 to dd do print(xchr[mod_text[k]]);
+ 5: for k:=1 to out_ptr do print(xchr[out_buf[k]]);
+ 6: for k:=1 to dd do
+ begin print_cat(cat[k]); print(' ');
+ end;
+ othercases print('?')
+ endcases;
+ end;
+ end;
+exit:end;
+gubed
+
+@* The main program.
+Let's put it all together now: \.{WEAVE} starts and ends here.
+@^system dependencies@>
+
+The main procedure has been split into three sub-procedures in order to
+keep certain \PASCAL\ compilers from overflowing their capacity.
+@^split procedures@>
+
+@p procedure Phase_I;
+begin @<Phase I:...@>;
+end;
+@#
+procedure Phase_II;
+begin @<Phase II:...@>;
+end;
+@#
+begin initialize; {beginning of the main program}
+print_ln(banner); {print a ``banner line''}
+@<Store all the reserved words@>;
+Phase_I; Phase_II;@/
+@<Phase III:...@>;
+@<Check that all changes have been read@>;
+end_of_WEAVE:
+stat @<Print statistics about memory usage@>;@+tats@;@/
+@t\4\4@>{here files should be closed if the operating system requires it}
+@<Print the job |history|@>;
+end.
+
+@ @<Print statistics about memory usage@>=
+print_nl('Memory usage statistics: ',
+ name_ptr:1,' names, ', xref_ptr:1,' cross references, ',
+ byte_ptr[0]:1);
+for cur_bank:=1 to ww-1 do print('+',byte_ptr[cur_bank]:1);
+ print(' bytes;');
+print_nl('parsing required ',max_scr_ptr:1,' scraps, ',max_txt_ptr:1,
+ ' texts, ',max_tok_ptr:1,' tokens, ', max_stack_ptr:1,' levels;');
+print_nl('sorting required ',max_sort_ptr:1, ' levels.')
+
+@ Some implementations may wish to pass the |history| value to the
+operating system so that it can be used to govern whether or not other
+programs are started. Here we simply report the history to the user.
+@^system dependencies@>
+
+@<Print the job |history|@>=
+case history of
+spotless: print_nl('(No errors were found.)');
+harmless_message: print_nl('(Did you see the warning message above?)');
+error_message: print_nl('(Pardon me, but I think I spotted something wrong.)');
+fatal_message: print_nl('(That was a fatal error, my friend.)');
+end {there are no other cases}
+
+@* System-dependent changes.
+This module should be replaced, if necessary, by changes to the program
+that are necessary to make \.{WEAVE} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous modules preserve the module numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new modules, can be inserted here; then only the index
+itself will get a new module number.
+@^system dependencies@>
+
+@* Index.
+If you have read and understood the code for Phase III above, you know what
+is in this index and how it got here. 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, control
+sequences put into the output, and a few
+other things like ``recursion'' are indexed here too.
diff --git a/systems/knuth/dist/web/webman.tex b/systems/knuth/dist/web/webman.tex
new file mode 100644
index 0000000000..61e12a2878
--- /dev/null
+++ b/systems/knuth/dist/web/webman.tex
@@ -0,0 +1,1672 @@
+% WEB user manual -- last updated by D E Knuth on 4 Dec 89
+\input webmac
+\parskip 0pt plus 1pt
+\def\RA{\char'31 } % right arrow
+\def\hang{\hangindent 4em\ignorespaces}
+\font\ninerm=cmr9
+\font\ninett=cmtt9
+\font\eighttt=cmtt8
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\PASCAL{Pascal}
+\font\quoterm=cmssq8
+\font\quoteit=cmssqi8
+\def\pb{\.{|...|}}
+\def\lpile{\def\cr{\hfill\endline}\matrix} % I only use \lpile by itself
+
+\outer\def\section #1.{\penalty-50\vskip 12pt plus 3pt minus 3pt
+ \noindent{\bf #1.}\quad\ignorespaces}
+
+\def\lheader{\mainfont\the\pageno\hfill\sc\runninghead\hfill}
+\def\rheader{\hfill\sc\runninghead\hfill\mainfont\the\pageno}
+\def\runninghead{{\tentt WEB} USER MANUAL}
+
+% This verbatim mode assumes that no ? appears in the text being copied.
+\def\verbatim{\begingroup
+ \def\do##1{\catcode`##1=12 } \dospecials
+ \parskip 0pt \parindent 0pt
+ \catcode`\ =13 \catcode`\^^M=13
+ \tt \catcode`\?=0 \verbatimdefs \verbatimgobble}
+{\catcode`\^^M=13{\catcode`\ =13\gdef\verbatimdefs{\def^^M{\ \par}\let =\ }} %
+ \gdef\verbatimgobble#1^^M{}}
+ \centerline{\titlefont The {\ttitlefont WEB} System
+ of Structured Documentation}
+
+\vskip 15pt plus 3pt minus 3pt
+\noindent This memo describes how to write programs in the
+\.{WEB} language; and it also includes the full \.{WEB} documentation for
+\.{WEAVE} and \.{TANGLE}, the programs that read \.{WEB} input and produce
+\TeX\ and \PASCAL\ output, respectively. The philosophy behind \.{WEB} is
+that an experienced system programmer, who wants to provide the best
+possible documentation of software products, needs two things
+simultaneously: a language like \TeX\ for formatting, and a language like
+\PASCAL\ for programming. Neither type of language can provide the best
+documentation by itself. But when both are appropriately combined, we
+obtain a system that is much more useful than either language separately.
+
+The structure of a software program may be thought of as a ``web'' that is
+made up of many interconnected pieces. To document such a program, we want
+to explain each individual part of the web and how it relates to its
+neighbors. The typographic tools provided by \TeX\ give us an opportunity
+to explain the local structure of each part by making that structure
+visible, and the programming tools provided by \PASCAL\ make it possible
+for us to specify the algorithms formally and unambiguously. By combining
+the two, we can develop a style of programming that maximizes our ability
+to perceive the structure of a complex piece of software, and at the same
+time the documented programs can be mechanically translated into a working
+software system that matches the documentation.
+
+Since \.{WEB} is an experimental system developed for internal use within
+the \TeX\ project at Stanford, this report is rather terse, and it assumes
+that the reader is an experienced programmer who is highly motivated to
+read a detailed description of \.{WEB}'s rules. Furthermore, even if a
+less terse manual were to be written, the reader would have to be warned
+in advance that \.{WEB} is not for beginners and it never will be: The
+user of \.{WEB} must be familiar with both \TeX\ and \PASCAL. When one
+writes a \.{WEB} description of a software system, it is possible to make
+mistakes by breaking the rules of \.{WEB} and/or the rules of \TeX\ and/or
+the rules of \PASCAL. In practice, all three types of errors will occur,
+and you will get different error messages from the different language
+processors. In compensation for the sophisticated expertise needed to cope
+with such a variety of languages, however, experience has shown that
+reliable software can be created quite rapidly by working entirely in
+\.{WEB} from the beginning; and the documentation of such programs seems
+to be better than the documentation obtained by any other known method.
+Thus, \.{WEB} users need to be highly qualified, but they can get some
+satisfaction and perhaps even a special feeling of accomplishment when
+they have successfully created a software system with this method.
+
+To use \.{WEB}, you prepare a file called \.{COB.WEB} (say), and then you
+apply a system program called \.{WEAVE} to this file, obtaining an output
+file called \.{COB.TEX}. When \TeX\ processes \.{COB.TEX}, your output
+will be a ``pretty printed'' version of \.{COB.WEB} that takes appropriate
+care of typographic details like page layout and the use of indentation,
+italics, boldface, etc.; this output will contain extensive cross-index
+information that is gathered automatically. You can also submit the same
+file \.{COB.WEB} to another system program called \.{TANGLE}, which will
+produce a file \.{COB.PAS} that contains the \PASCAL\ code of your \.{COB}
+program. The \PASCAL\ compiler will convert \.{COB.PAS} into
+machine-language instructions corresponding to the algorithms that were so
+nicely formatted by \.{WEAVE} and \TeX. Finally, you can (and should)
+delete the files \.{COB.TEX} and \.{COB.PAS}, because \.{COB.WEB} contains
+the definitive source code. Examples of the behavior of \.{WEAVE} and
+\.{TANGLE} are appended to this manual.
+
+Besides providing a documentation tool, \.{WEB} enhances the \PASCAL\
+language by providing a rudimentary macro capability together with the
+ability to permute pieces of the program text, so that a large system can
+be understood entirely in terms of small modules and their local
+interrelationships. The \.{TANGLE} program is so named because it takes a
+given web and moves the modules from their web structure into the order
+required by \PASCAL; the advantage of programming in \.{WEB} is that the
+algorithms can be expressed in ``untangled'' form, with each module
+explained separately. The \.{WEAVE} program is so named because it takes
+a given web and intertwines the \TeX\ and \PASCAL\ portions contained in
+each module, then it knits the whole fabric into a structured document.
+(Get it? Wow.) Perhaps there is some deep connection here with the fact
+that the German word for ``weave'' is ``{\it web\/}'', and the
+corresponding Latin imperative is ``{\it texe\/}''!
+
+It is impossible to list all of the related work that has influenced the
+design of \.{WEB}, but the key contributions should be mentioned
+here.\quad (1)~Myrtle Kellington, as executive editor for ACM
+publications, developed excellent typographic standards for the
+typesetting of Algol programs during the 1960s, based on the original
+designs of Peter Naur; the subtlety and quality of this influential work
+can be appreciated only by people who have seen what happens when other
+printers try to typeset Algol without the advice of ACM's copy
+editors.\quad(2)~Bill McKeeman introduced a program intended to automate
+some of this task [Algorithm 268, ``Algol~60 reference language editor,''
+{\sl CACM \bf8} (1965), 667--668]; and a considerable flowering of such
+programs has occurred in recent years [see especially Derek Oppen,
+``Prettyprinting,'' {\sl ACM TOPLAS \bf2} (1980), 465--483; G.~A. Rose and
+J. Welsh, ``Formatted programming languages,'' {\sl SOFTWARE Practice
+\char`\&\ Exper.\ \bf11} (1981), 651--669].\quad(3)~The top-down style of
+exposition encouraged by \.{WEB} was of course chiefly influenced by Edsger
+Dijkstra's essays on structured programming in the late 1960s. The less
+well known work of Pierre-Arnoul de Marneffe [``Holon programming: A
+survey,'' Univ.\ de Liege, Service Informatique, Liege, Belgium, 1973; 135
+pp.\null] also had a significant influence on the author as \.{WEB} was
+being formulated.\quad(4)~Edwin Towster has proposed a similar style of
+documentation in which the programmer is supposed to specify the relevant
+data structure environment in the name of each submodule [``A convention
+for explicit declaration of environments and top-down refinement of
+data,'' {\sl IEEE Trans.\ on Software Eng.\ \bf SE--5} (1979), 374--386];
+this requirement seems to make the documentation a bit too verbose,
+although experience with \.{WEB} has shown that any unusual control
+structure or data structure should definitely be incorporated into the
+module names on psychological grounds.\quad(5)~Discussions with Luis
+Trabb~Pardo in the spring of 1979 were extremely helpful for setting up a
+prototype version of \.{WEB} that was called \.{DOC}.\quad (6)~Ignacio
+Zabala's extensive experience with \.{DOC}, in which he created a full
+implementation of \TeX\ in \PASCAL\ that was successfully transported to
+many different computers, was of immense value while \.{WEB} was taking
+its present form.\quad(7)~David~R. Fuchs made several crucial suggestions
+about how to make \.{WEB} more portable; he and Arthur~L. Samuel
+coordinated the initial installations of \.{WEB} on dozens of computer
+systems, making changes to the code so that it would be acceptable to
+a wide variety of \PASCAL\ compilers.\quad(8)~The name \.{WEB} itself
+was chosen in honor of my wife's mother, Wilda Ernestine Bates.
+
+The appendices to this report contain complete \.{WEB} programs for the
+\.{WEAVE} and \.{TANGLE} processors. A study of these examples, together
+with an attempt to write \.{WEB} programs by yourself, is the best way
+to understand why \.{WEB} has come to be like it is.
+ \section General rules.
+A \.{WEB} file is a long string of text that has been divided into
+individual lines. The exact line boundaries are not terribly crucial, and
+a programmer can pretty much chop up the \.{WEB} file in whatever way seems
+to look best as the file is being edited; but string constants and control
+texts must end on the same line on which they begin, since this convention
+helps to keep errors from propagating. The end of a line means
+the same thing as a blank space.
+
+Two kinds of material go into \.{WEB} files: \TeX\ text and \PASCAL\ text.
+A programmer writing in \.{WEB} should be thinking both of the
+documentation and of the \PASCAL\ program that he or she is creating;
+i.e., the programmer should be instinctively aware of the different
+actions that \.{WEAVE} and \.{TANGLE} will perform on the \.{WEB} file.
+\TeX\ text is essentially copied without change by \.{WEAVE}, and it is
+entirely deleted by \.{TANGLE}, since the \TeX\ text is ``pure
+documentation.'' \PASCAL\ text, on the other hand, is formatted by
+\.{WEAVE} and it is shuffled around by \.{TANGLE}, according to rules that
+will become clear later. For now the important point to keep in mind is
+that there are two kinds of text. Writing \.{WEB} programs is something
+like writing \TeX\ documents, but with an additional ``\PASCAL\ mode''
+that is added to \TeX's horizontal mode, vertical mode, and math mode.
+
+A \.{WEB} file is built up from units called {\sl modules\/} that are more
+or less self-contained. Each module has three parts:
+
+\yskip\item{1)} A \TeX\ part, containing explanatory material about what
+is going on in the module.
+
+\item{2)} A definition part, containing macro definitions that serve as
+abbreviations for \PASCAL\ constructions that would be less comprehensible
+if written out in full each time.
+
+\item{3)} A \PASCAL\ part, containing a piece of the program that
+\.{TANGLE} will produce. This \PASCAL\ code should ideally be about a
+dozen lines long, so that it is easily comprehensible as a unit and so
+that its structure is readily perceived.
+
+\yskip\noindent The three parts of each module must appear in this order;
+i.e., the \TeX\ commentary must come first, then the definitions, and
+finally the \PASCAL\ code. Any of the parts may be empty.
+
+\eject % page break inserted Dec 88
+
+A module begins with the pair of symbols `\.{@\ }' or `\.{@*}', where
+`\.{\ }' denotes a blank space. A module ends
+at the beginning of the next module (i.e., at the next
+`\.{@\ }' or `\.{@*}'), or at the end of the file, whichever comes first.
+The \.{WEB} file may also contain material that is not part of any module
+at all, namely the text (if any) that occurs before the first module.
+Such text is said to be ``in limbo''; it is ignored by \.{TANGLE}
+and copied essentially verbatim by \.{WEAVE}, so its function is to
+provide any additional formatting instructions that may be desired in the
+\TeX\ output. Indeed, it is customary to begin a \.{WEB} file with
+\TeX\ code in limbo that loads special fonts, defines special macros,
+changes the page sizes, and/or produces a title page.
+
+Modules are numbered consecutively, starting with 1; these numbers appear
+at the beginning of each module of the \TeX\ documentation, and they appear
+as bracketed comments at the beginning of the code generated by that
+module in the \PASCAL\ program.
+
+Fortunately, you never mention these numbers yourself when you are writing
+in \.{WEB}. You just say `\.{@\ }' or `\.{@*}' at the beginning of each
+new module, and the numbers are supplied automatically by \.{WEAVE} and
+\.{TANGLE}. As far as you are concerned, a module has a
+{\sl name\/} instead of a number; such a name is specified by writing
+`\.{@<}' followed by \TeX\ text followed by `\.{@>}'. When \.{WEAVE}
+outputs a module name, it replaces the `\.{@<}' and `\.{@>}' by
+angle brackets and inserts the module number in small type. Thus, when you
+read the output of \.{WEAVE} it is easy to locate any module that is
+referred to in another module.
+
+For expository purposes, a module name should be a good description of the
+contents of that module; i.e., it should stand for the abstraction
+represented by the module. Then the module can be ``plugged into'' one or
+more other modules in such a way
+that unimportant details of its inner workings
+are suppressed. A module name therefore ought to be long enough to convey
+the necessary meaning. Unfortunately, however, it is laborious to type
+such long names over and over again, and it is also difficult to specify a
+long name twice in exactly the same way so that \.{WEAVE} and \.{TANGLE}
+will be able to match the names to the modules. To ameliorate this difficulty,
+\.{WEAVE} and \.{TANGLE} let you abbreviate a module name
+after its first appearance in the \.{WEB} file; you can type simply
+`\.{@<$\alpha$...@>}', where $\alpha$ is any string that is a prefix of
+exactly one module name appearing in the file. For example, `\.{@<Clear
+the arrays@>}' can be abbreviated to `\.{@<Clear...@>}' if no other module
+name begins with the five letters `\.{Clear}'. Module names must otherwise
+match character for character, except that consecutive blank spaces and/or
+tab marks are treated as equivalent to single spaces, and such spaces are
+deleted at the beginning and end of the name. Thus, `\.{@< Clear { }the
+arrays @>}' will also match the name in the previous example.
+
+We have said that a module begins with `\.{@\ }' or `\.{@*}', but we
+didn't say how it gets divided up into a \TeX\ part, a definition part,
+and a \PASCAL\ part. The definition part begins with the first appearance
+of `\.{@d}' or `\.{@f}' in the module, and the \PASCAL\ part begins with
+the first appearance of `\.{@p}' or `\.{@<}'. The latter option `\.{@<}'
+stands for the beginning of a module name, which is the name of the module
+itself. An equals sign (\.=) must follow the `\.{@>}' at the end of this
+module name; you are saying, in effect, that the module name stands for
+the \PASCAL\ text that follows, so you say `$\langle\,$module
+name$\,\rangle=\null$\PASCAL\ text'. Alternatively, if the \PASCAL\ part
+begins with `\.{@p}' instead of a module name, the current module is said
+to be {\sl unnamed}. Note that module names cannot appear in the
+definition part of a module, because the first `\.{@<}' in a module
+signals the beginning of its \PASCAL\ part. But any number of module names
+might appear in the \PASCAL\ part, once it has started.
+
+The general idea of \.{TANGLE} is to make a \PASCAL\ program out of these
+modules in the following way: First all the \PASCAL\ parts of unnamed
+modules are copied down, in order; this constitutes the initial
+approximation $T_0$ to the text of the program. (There should be at least
+one unnamed module, otherwise there will be no program.) Then all module
+names that appear in the initial text $T_0$ are replaced by the \PASCAL\
+parts of the corresponding modules, and this substitution process
+continues until no module names remain. Then all defined macros are
+replaced by their equivalents, according to certain rules that are
+explained later. The resulting \PASCAL\ code is ``sanitized'' so that it
+will be acceptable to an average garden-variety \PASCAL\ compiler; i.e.,
+lowercase letters are converted to uppercase, long identifiers are
+chopped, and the lines of the output file are constrained to be at most 72
+characters long. All comments will have been removed from this \PASCAL\
+program except for the meta-comments delimited by `\.{@\{}' and
+`\.{@\}}', as explained below, and except for the module-number comments
+that point to the source location where each piece of the program text
+originated in the \.{WEB} file.
+
+If the same name has been given to more than one module, the \PASCAL\ text
+for that name is obtained by putting together all of the \PASCAL\ parts in
+the corresponding modules. This feature is useful, for example, in a
+module named `Global variables in the outer block', since one can then
+declare global variables in whatever modules those variables are
+introduced. When several modules have the same name, \.{WEAVE} assigns the
+first module number as the number corresponding to that name, and it
+inserts a note at the bottom of that module telling the reader to `See
+also sections so-and-so'; this footnote gives the numbers of all the other
+modules having the same name as the present one. The \PASCAL\ text
+corresponding to a module is usually formatted by \.{WEAVE} so that the
+output has an equivalence sign in place of the equals sign in the \.{WEB}
+file; i.e., the output says `$\langle\,$module
+name$\,\rangle\equiv\null$\PASCAL\ text'. However, in the case of the second
+and subsequent appearances of a module with the same name, this `$\equiv$'
+sign is replaced by `$\mathrel+\equiv$', as an indication that the \PASCAL\
+text that follows is being appended to the \PASCAL\ text of another
+module.
+
+The general idea of \.{WEAVE} is to make a \.{TEX} file from the \.{WEB}
+file in the following way: The first line of the \.{TEX} file will be
+`\.{\\input webmac}'; this will cause \TeX\ to read in the macros that
+define \.{WEB}'s documentation conventions. The next lines of the file
+will be copied from whatever \TeX\ text is in limbo before the first
+module. Then comes the output for each module in turn, possibly
+interspersed with end-of-page marks. Finally, \.{WEAVE} will generate a
+cross-reference index that lists each module number in which each \PASCAL\
+identifier appears, and it will also generate an alphabetized list
+of the module names, as well as a table of contents that
+shows the page and module numbers for each ``starred'' module.
+
+What is a ``starred'' module, you ask? A module that begins with `\.{@*}'
+instead of `\.{@\ }' is slightly special in that it denotes a new major
+group of modules. The `\.{@*}' should be followed by the title of this
+group, followed by a period. Such modules will always start on a new page
+in the \TeX\ output, and the group title will appear as a running headline
+on all subsequent pages until the next starred module. The title will also
+appear in the table of contents, and in boldface type at the beginning of
+its module. Caution: Do not use \TeX\ control sequences in such titles,
+unless you know that the \.{webmac} macros will do the right thing with
+them. The reason is that these titles are converted to uppercase when
+they appear as running heads, and they are converted to boldface when they
+appear at the beginning of their modules, and they are also written out to
+a table-of-contents file used for temporary storage while \TeX\ is
+working; whatever control sequences you use must be meaningful in all
+three of these modes.
+
+The \TeX\ output produced by \.{WEAVE} for each module consists of
+the following: First comes the module number (e.g., `\.{\\M123.}'
+at the beginning of module 123, except that `\.{\\N}' appears in place of
+`\.{\\M}' at the beginning of a starred module). Then comes the
+\TeX\ part of the module, copied almost verbatim except as noted
+below. Then comes the definition part and the \PASCAL\ part, formatted
+so that there will be a little extra space between them if both are
+nonempty. The definition and \PASCAL\ parts are obtained by inserting
+a bunch of funny looking \TeX\ macros into the \PASCAL\ program; these
+macros handle typographic details about fonts and proper math spacing,
+as well as line breaks and indentation.
+
+When you are typing \TeX\ text, you will probably want to make frequent
+reference to variables and other quantities in your \PASCAL\ code, and you
+will want those variables to have the same typographic treatment
+when they appear in your text as when they appear in your
+program. Therefore the \.{WEB} language allows you to get the effect of
+\PASCAL\ editing within \TeX\ text, if you place `\.|' marks before and
+after the \PASCAL\ material. For example, suppose you want to say something
+like this:
+$$\hbox{The characters are placed into \\{buffer}, which is a
+\&{packed} \&{array} $[1\to\|n]$ \&{of} \\{char}.}$$
+The \TeX\ text would look like this in your \.{WEB} file:
+$$\.{The characters are placed into |buffer|, which is a |packed
+array [1..n] of char|.}$$
+And \.{WEAVE} translates this into something you are glad you didn't have
+to type:
+$$\lpile{\.{The characters are placed into \\\\\{buffer\},}\cr
+ \.{which is a \\\&\{packed\}{ }\\\&\{array\}{ }\$
+ [1\\to\\|n]\${ }\\\&\{of\}{ }\\\\\{char\}.}\cr}$$
+Incidentally, the cross-reference index that \.{WEAVE} would make, in
+the presence of a comment like this, would include
+the current module number as one of the index entries for \\{buffer}
+\vadjust{\eject}% page break inserted Dec 88
+and \\{char}, even though \\{buffer} and \\{char}
+might not appear in the \PASCAL\ part of
+this module. Thus, the index covers references to identifiers in
+the explanatory comments as well as in the program itself; you will
+soon learn to appreciate this feature. However, the identifiers
+\&{packed} and \&{array} and \|n\ and \&{of\/} would not be indexed,
+because \.{WEAVE} does not make index entries for reserved words or
+single-letter identifiers. Such identifiers are felt to be so ubiquitous
+that it would be pointless to mention every place where they occur.
+
+Speaking of identifiers, the author of \.{WEB} thinks that
+\\{IdentifiersSeveralWordsLong} look terribly ugly when they mix
+uppercase and lowercase letters. He recommends that
+\\{identifiers\_several\_words\_long} be written with underline characters
+to get a much better effect. The actual identifiers sent to the \PASCAL\
+compiler by \.{TANGLE} will have such underlines removed, and \.{TANGLE}
+will check to make sure that two different identifiers do not become
+identical when this happens. (In fact, \.{TANGLE} even checks that
+the first seven characters of identifiers are unique, when lowercase
+letters have been converted to uppercase; the number seven in this
+constraint is more strict than \PASCAL's eight, and it can
+be changed if desired.) The \.{WEAVE} processor will properly
+alphabetize identifiers that have embedded underlines
+when it makes the index.
+
+Although a module begins with \TeX\ text and ends with \PASCAL\ text, we
+have noted that the dividing line isn't sharp, since \PASCAL\ text can be
+included in \TeX\ text if it is enclosed in `\pb'. Conversely, \TeX\ text
+also appears frequently within \PASCAL\ text, because everything in
+comments (i.e., between left and right braces) is treated as \TeX\ text.
+Furthermore, a module name consists of \TeX\ text; thus, a \.{WEB} file
+typically involves constructions like `\.{if} \.x \.= \.0 \.{then}
+\.{@<Empty} \.{the} \.{|buffer|} \.{array@>}' where we go back and forth
+between \PASCAL\ and \TeX\ conventions in a natural way.
+ \section Macros.
+A \.{WEB} programmer can define three kinds of macros to make the programs
+shorter and more readable:
+
+\yskip\hang`\.{@d} \\{identifier} \.= \\{constant}' defines a {\sl numeric\/}
+macro, allowing \.{TANGLE} to do rudimentary arithmetic.
+
+\yskip\hang`\.{@d} \\{identifier} \.{==} \PASCAL\ text' defines a {\sl
+simple\/} macro, where the identifier will be replaced by the \PASCAL\ text
+when \.{TANGLE} produces its output.
+
+\yskip\hang`\.{@d} \\{identifier}\.{(\#) ==} \PASCAL\ text' defines a
+{\sl parametric\/} macro, where the identifier will be replaced by the \PASCAL\
+text and where occurrences of \.{\#} in that \PASCAL\ text will be
+replaced by an argument.
+
+\yskip\noindent In all three cases, the identifier must have length greater
+than one; it must not be a single letter.
+
+Numeric macros are subject to the following restrictions:\quad
+(1)~The identifier must
+be making its first appearance in the \.{WEB} file;
+a numeric macro must be defined before it is used.\quad
+(2)~The right-hand side of the numeric definition must be made entirely from
+integer constants, numeric macros, preprocessed strings (see below), and
+plus~signs or minus signs. No other operations or symbols are allowed,
+not even parentheses, except that \PASCAL-like comments (enclosed in
+braces) can appear. Indeed, comments are recommended, since it is usually
+wise to give a brief explanation of the significance of each identifier as
+it is defined.\quad
+(3)~The numeric value must be less than $2^{15}=32768$ in absolute value.
+(For larger values, you can use `\.{==}' in place of~`\.=', thus making use
+of a simple macro instead of a numeric one. Note, however, that simple
+macros sometimes have a different effect. For example, consider the three
+definitions `\.{@d n1=2 @d n2=2+n1 @d n3==2+n1}'; then `\.{x-n2}' will
+expand into `\.{x-4}', while `\.{x-n3}' will expand into `\.{x-2+2}' which
+is quite different! It is wise to include parentheses in non-numeric
+macros, e.g., `\.{@d n3==(2+n1)}', to avoid such errors.)
+
+When constants are connected by plus signs or minus
+signs in a \PASCAL\ program, \.{TANGLE} does the arithmetic before putting
+the constant into the output file. Therefore it is permissible to say, for
+example, `\&{array} $[0\,.\,.\,\\{size}-1]$' if \\{size} has been declared
+as a macro; note that \PASCAL\ doesn't allow this kind of compile-time
+arithmetic if \\{size} is a \&{constant} quantity in the program. Another
+use of \.{TANGLE}'s arithmetic is to make \&{case} statement labels such
+as `$\\{flag}+1$' legitimate. Of course, it is improper to change \.{2+2}
+into \.4 without looking at the surrounding context; many counterexamples
+exist, such as the phrases `\.{-2+2}', `\.{x/2+2}', and `\.{2+2E5}'. The
+program for \.{TANGLE}, in the appendix, gives precise details about this
+conversion, which \.{TANGLE} does only when it is safe.
+
+The right-hand sides of simple and parametric macros
+are required to have balanced parentheses, and the \PASCAL\ texts of
+modules must have balanced parentheses too. Therefore when the argument
+to a para\-metric macro appears in parentheses, both parentheses
+will belong to the same \PASCAL\ text.
+
+The appendices to this report contain hundreds of typical examples of the
+usefulness of \.{WEB} macros, so it is not necessary to dwell on the
+subject here. However, the reader should know that \.{WEB}'s apparently
+primitive macro capabilities can actually do a lot of rather surprising
+things. Here is a construction that sheds further light on what is
+possible: After making the definitions
+$$\catcode`\#=12
+\lpile{\.{@d two\_cases(#)==case j of 1:#(1); 2:#(2); end}\cr
+\.{@d reset\_file(#)==reset(input\_file@\&#)}\cr}$$
+one can write `\.{two\_cases(reset\_file)}' and the resulting \PASCAL\
+output will be
+$$\.{case j of 1:reset(input\_file1); 2:reset(input\_file2); end}$$
+(but in uppercase letters and with \.\_'s removed).
+The `\.{@\&}' operation used here joins together two adjacent tokens
+into a single token, as explained later; otherwise the \PASCAL\ file would
+contain a space between \.{input\_file} and the digit that followed it.
+This trick can be used to provide the effect of an array of files, if you
+are unfortunate enough to have a \PASCAL\ compiler that doesn't allow such
+arrays. Incidentally, the cross-reference index made by \.{WEAVE} from
+this example would contain the identifier \\{input\_file} but it would not
+contain \\{input\_file1} or \\{input\_file2}. Furthermore, \.{TANGLE}
+would not catch the error that \.{INPUTFILE1} and \.{INPUTFILE2} both
+begin with the same nine letters; one should be more careful when using
+`\.{@\&}'! But such aspects of the construction in this trick are
+peripheral to our main point, which is that a parametric macro name without
+arguments can be used as an argument to another parametric macro.
+
+Although \.{WEB}'s macros are allowed to have at most one parameter, the
+following example shows that this is not as much of a restriction as it
+may seem at first. Let \\{amac} and \\{bmac} be any parametric macros, and
+suppose that we want to get the effect of
+$$\catcode`\#=12
+\.{@d cmac(#1,#2) == amac(#1) bmac(#2)}$$
+which \.{WEB} doesn't permit. The solution is to make the definitions
+$$\catcode`\#=12
+\lpile{\.{@d cmac(#) == amac(#) dmac}\cr
+\.{@d dmac(#) == bmac(#)}\cr}$$
+and then to say `\.{cmac(x)(y)}'.
+
+There is one restriction in the generality of \.{WEB}'s parametric
+macros, however: the argument to a para\-metric macro must not come from
+the expansion of a macro that has not already been ``started.'' For
+example, here is one of the things \.{WEB} cannot handle:
+$$\catcode`\#=12
+\lpile{\.{@d arg == (p)}\cr
+\.{@d identity(#) == #}\cr
+\.{@p identity arg}\cr}$$
+In this case \.{TANGLE} will complain that the \.{identity} macro is not
+followed by an argument in parentheses.
+
+The \.{WEB} language has another feature that is somewhat similar to a
+numeric macro. A {\sl preprocessed string\/} is a string that is like
+a \PASCAL\ string but delimited by double-quote marks (\.") instead of
+single-quotes. Double-quote marks inside of such strings are indicated by
+giving two double-quotes in a row. If a preprocessed string is
+of length one (e.g., \.{"A"} or \.{""""}), it will be treated by \.{TANGLE}
+as equivalent to the corresponding ASCII-code integer (e.g., \.{65} or
+\.{34}). And if a preprocessed string is not of length one, it will be
+converted into an integer equal to 256 or more. A {\sl string pool\/}
+containing all such strings will be written out by the \.{TANGLE}
+processor; this string pool file consists of string 256, then string 257,
+etc., where each string is followed by an end-of-line and prefixed by two
+decimal digits that define its length. Thus, for example, the empty string
+\.{""} would be represented in the string pool file by a line containing
+the two characters `\.{00}', while the string \.{"""String"""} would be
+represented by `\.{08"String"}'. A given string appears at most once in
+the string pool; the use of such a pool makes it easier to cope with
+\PASCAL's restrictions on string manipulation. The string pool ends with
+`\.{*nnnnnnnnn}', where \.{nnnnnnnnn} is a decimal number
+called the {\sl string pool check sum}. If any string changes, the check
+sum almost surely changes too; thus, the `\.{@\$}' feature
+described below makes it possible for a program to assure itself that it
+is reading its own string pool.
+
+Here is a simple example that combines numeric macros with preprocessed
+strings of length one:
+$$\lpile{\.{@d upper\_case\_Y = "Y"}\cr
+\.{@d case\_difference = -"y"+upper\_case\_Y}\cr}$$
+The result is to define
+$\\{upper\_case\_Y}=89$, $\\{case\_difference}=-32$.
+ \section Control codes.
+We have seen several magic uses of `\.{@}' signs in \.{WEB} files, and it
+is time to make a systematic study of
+these special features. A \.{WEB} {\sl control code\/}
+is a two-character combination of which the first is `\.@'.
+
+Here is a complete list of the legal control codes. The letters $L$, $T$,
+$P$, $M$, $C$, and/or $S$ following each code indicate whether or not that
+code is allowable in limbo, in \TeX\ text, in \PASCAL\ text, in module
+names, in comments, and/or in strings. A bar over such a letter means
+that the control code terminates the present part of the \.{WEB} file; for
+example, $\overline L$ means that this control code ends the limbo material
+before the first module.
+
+\def\@#1[#2] {\yskip\hangindent 2em\noindent\.{@#1\unskip
+ \spacefactor1000{ }}$[#2]$\quad}
+\def\oP{\overline P}
+\def\oT{\overline T\mskip1mu}
+
+\@@ [C,L,M,P,S,T] A double \.@ denotes the single character `\.@'. This is
+the only control code that is legal in limbo, in comments, and in strings.
+
+\@\ [\overline L,\oP,\oT] This denotes the beginning of a new
+(unstarred) module. A tab mark or end-of-line (carriage return)
+is equivalent to a space when it follows an \.@ sign.
+
+\@* [\overline L,\oP,\oT] This denotes the beginning of a new starred
+module, i.e., a module that begins a new major group. The title of the new
+group should appear after the \.{@*}, followed by a period. As explained
+above, \TeX\ control sequences should be avoided in such titles unless
+they are quite simple. When \.{WEAVE} and \.{TANGLE} read a \.{@*}, they
+print an asterisk on the terminal
+followed by the current module number, so that the user
+can see some indication of progress. The very first module should be starred.
+
+\@d [\oP,\oT] Macro definitions begin with \.{@d} (or \.{@D}), followed by
+the \PASCAL\ text for one of the three kinds of macros, as explained
+earlier.
+
+\@f [\oP,\oT] Format definitions begin with \.{@f} (or \.{@F}); they cause
+\.{WEAVE} to treat identifiers in a special way when they appear in
+\PASCAL\ text. The general form of a format definition is `\.{@f} \|l \.{==}
+\|r', followed by an optional comment enclosed in braces, where \|l and \|r
+are identifiers; \.{WEAVE} will subsequently treat identifier \|l as it
+currently treats \|r. This feature allows a \.{WEB} programmer to invent
+new reserved words and/or to unreserve some of \PASCAL's reserved
+identifiers. The definition part of each module consists of any number of
+macro definitions (beginning with \.{@d}) and format definitions (beginning
+with \.{@f}), intermixed in any order.
+
+\@p [\oP,\oT] The \PASCAL\ part of an unnamed module begins with \.{@p}
+(or \.{@P}). This causes \.{TANGLE} to append the following \PASCAL\ code
+to the initial program text $T_0$ as explained above. The \.{WEAVE}
+processor does not cause a `\.{@p}' to appear explicitly in the \TeX\
+output, so if you are creating a \.{WEB} file based on a \TeX-printed
+\.{WEB} documentation you have to remember to insert \.{@p} in the
+appropriate places of the unnamed modules.
+
+\@< [P,\oT] A module name begins with \.{@<} followed by \TeX\ text followed
+by \.{@>}; the \TeX\ text should not contain any \.{WEB} control codes
+except \.{@@}, unless these control codes appear in \PASCAL\ text that
+is delimited by \pb. The module name may be abbreviated, after its first
+appearance in a \.{WEB} file, by giving any unique prefix followed by \.{...},
+where the three dots immediately precede the closing \.{@>}. No module name
+should be a prefix of another. Module names may not appear in \PASCAL\
+text that is enclosed in \pb, nor may they appear in the definition part
+of a module (since the appearance of a module name ends the definition
+part and begins the \PASCAL\ part).
+
+\@\' [P,T] This denotes an octal constant, to be formed from the
+succeeding digits. For example, if the \.{WEB} file contains `\.{@\'100}',
+the \.{TANGLE} processor will treat this an equivalent to `\.{64}';
+the constant will be formatted as ``\O{100}'' in the \TeX\ output
+produced via \.{WEAVE}. You should use octal notation only for positive
+constants; don't try to get, e.g., $-1$ by saying `\.{@\'777777777777}'.
+
+\@" [P,T] A hexadecimal constant; `\.{@"D0D0}' tangles to \.{53456} and
+weaves to `\H{D0D0}'.
+
+\@\$ [P] This denotes the string pool check sum.
+
+\@\{ [P] The beginning of a ``meta comment,'' i.e., a comment
+that is supposed to appear in the \PASCAL\ code, is indicated by
+\.{@\{} in the \.{WEB} file. Such delimiters can be used as
+isolated symbols in macros or modules, but they should be properly nested
+in the final \PASCAL\ program. The \.{TANGLE} processor will convert
+`\.{@\{}' into `\.\{' in the \PASCAL\ output file, unless
+the output is already part of a meta-comment; in the latter case
+`\.{@\{}' is converted into `\.[', since \PASCAL\ does not allow
+nested comments. Incidentally, module numbers are automatically inserted
+as meta-comments into the \PASCAL\ program, in order to help correlate the
+outputs of \.{WEAVE} and \.{TANGLE} (see Appendix~C\null). Meta-comments
+can be used to put conditional text into a \PASCAL\ program; this helps to
+overcome one of the limitations of \.{WEB}, since the simple macro
+processing routines of \.{TANGLE} do not include the dynamic evaluation of
+boolean expressions.
+
+\@\} [P] The end of a ``meta comment'' is indicated by `\.{@\}}'; this is
+converted either into `\.\}' or `\.{]}' in the \PASCAL\ output, according
+to the conventions explained for \.{@\{} above.
+
+\@\& [P] The \.{@\&} operation causes whatever is on its left to be
+adjacent to whatever is on its right, in the \PASCAL\ output. No spaces or
+line breaks will separate these two items. However, the thing on the left
+should not be a semicolon, since a line break might occur after a semicolon.
+
+\@\^ [P,T] The ``control text'' that follows, up to the next
+`\.{@>}', will be entered into the index together with the identifiers of
+the \PASCAL\ program; this text will appear in roman type. For example, to
+put the phrase ``system dependencies'' into the index, you can type
+`\.{@\^system dependencies@>}' in each module
+that you want to index as system dependent. A control text, like a string,
+must end on the same line of the \.{WEB} file as it began. Furthermore,
+no \.{WEB} control codes are allowed in a control text, not even
+\.{@@}. (If you need an \.{@} sign you can get around this restriction by
+typing `\.{\\AT!}'.)
+
+\@. [P,T] The ``control text'' that follows will be entered into the index
+in \.{typewriter} \.{type}; see the rules for `\.{@\^}', which is analogous.
+
+\@: [P,T] The ``control text'' that follows will be entered into the index
+in a format controlled by the \TeX\ macro `\.{\\9}', which the user
+should define as desired; see the rules for `\.{@\^}', which is analogous.
+
+\@t [P] The ``control text'' that follows, up to the next `\.{@>}', will
+be put into a \TeX\ \.{\\hbox} and formatted along with the neighboring
+\PASCAL\ program. This text is ignored by \.{TANGLE}, but it can be used
+for various purposes within \.{WEAVE}. For example, you can make comments
+that mix \PASCAL\ and classical mathematics, as in `$\\{size}<2^{15}$', by
+typing `\.{|size < @t\$2\^\{15\}\$@>|}'. A control text must end on the
+same line of the \.{WEB} file as it began, and it may not contain any
+\.{WEB} control codes.
+
+\@= [P] The ``control text'' that follows, up to the next `\.{@>}', will
+be passed verbatim to the \PASCAL\ program.
+
+\@\\ [P] Force end-of-line here in the \PASCAL\ program file.
+
+\@! [P,T] The module number in an index entry will be underlined if `\.{@!}'
+immediately precedes the identifier or control text being indexed. This
+convention is used to distinguish the modules where an identifier is
+defined, or where it is explained in some special way, from the modules
+where it is used. A~reserved word or an identifier of length one will not
+be indexed except for underlined entries. An `\.{@!}' is implicitly inserted
+by \.{WEAVE} just after the reserved words \&{function}, \&{procedure},
+\&{program}, and \&{var}, and just after \.{@d} and \.{@f}. But you should
+insert your own `\.{@!}' before the definitions of types, constants,
+variables, parameters, and components of records and enumerated types that
+are not covered by this implicit convention, if you want to improve the
+quality of the index that you get.
+
+\@? [P,T] This cancels an implicit (or explicit) `\.{@!}', so that the next
+index entry will not be underlined.
+
+\@, [P] This control code inserts a thin space in \.{WEAVE}'s output; it is
+ignored by \.{TANGLE}. Sometimes you need this extra space if you are using
+macros in an unusual way, e.g., if two identifiers are adjacent.
+
+\@/ [P] This control code causes a line break to occur within a \PASCAL\
+program formatted by \.{WEAVE}; it is ignored by \.{TANGLE}. Line breaks
+are chosen automatically by \TeX\ according to a scheme that works 99\%\
+of the time, but sometimes you will prefer to force a line break so that
+the program is segmented according to logical rather than visual criteria.
+Caution: `\.{@/}' should be used only after statements or clauses, not in
+the middle of an expression; use \.{@|} in the middle of expressions, in
+order to keep \.{WEAVE}'s parser happy.
+
+\@| [P] This control code specifies an optional line break in the midst of
+an expression. For example, if you have a long condition between \&{if} and
+\&{then}, or a long expression on the right-hand side of an assignment
+statement, you can use `\.{@|}' to specify breakpoints more logical than
+the ones that \TeX\ might choose on visual grounds.
+
+\@\# [P] This control code forces a line break, like \.{@/} does,
+and it also causes a little extra white space to appear between the lines at
+this break. You might use it, for example, between procedure definitions or
+between groups of macro definitions that are logically separate but within
+the same module.
+
+\@+ [P] This control code cancels a line break that might otherwise be
+inserted by \.{WEAVE}, e.g., before the word `\&{else}', if you want to
+put a short if-then-else construction on a single line. It is ignored by
+\.{TANGLE}.
+
+\@; [P] This control code is treated like a semicolon, for formatting
+purposes, except that it is invisible. You can use it, for example, after
+a module name when the \PASCAL\ text represented by that module name ends
+with a semicolon.
+
+\yskip\noindent
+The last six control codes (namely `\.{@,}', `\.{@/}', `\.{@|}',
+`\.{@\#}', `\.{@+}', and `\.{@;}') have no effect on the \PASCAL\
+program output by \.{TANGLE}; they merely help to improve the readability
+of the \TeX-formatted \PASCAL\ that is output by \.{WEAVE}, in unusual
+circumstances. \.{WEAVE}'s built-in formatting method is fairly good, but
+it is incapable of handling all possible cases, because it must deal with
+fragments of text involving macros and module names; these fragments do
+not necessarily obey \PASCAL's syntax. Although \.{WEB} allows you to
+override the automatic formatting, your best strategy is not to worry
+about such things until you have seen what \.{WEAVE} produces automatically,
+since you will probably need to make only a few corrections when you are
+touching up your documentation.
+
+Because of the rules by which every module is broken into three parts,
+the control codes `\.{@d}', `\.{@f}', and `\.{@p}' are not allowed to occur
+once the \PASCAL\ part of a module has begun.
+ \section Additional features and caveats.
+
+1. The character pairs `\.{(*}', `\.{*)}', `\.{(.}', and `\.{.)}' are
+converted automatically in \PASCAL\ text as though they were
+`\.{@\{}', `\.{@\}}', `\.[', and `\.]', respectively, except
+of course in strings. Furthermore in certain installations of \.{WEB} that
+{\def\\#1#2{`{\tentex\char'#1#2}'}%
+have an extended character set, the characters \\32, \\34, \\35, \\30,
+\\36, \\04, \\37, \\05, and \\06}
+can be typed as abbreviations for
+`\.{<>}', `\.{<=}', `\.{>=}', `\.{:=}', `\.{==}', `\.{and}', `\.{or}',
+`\.{not}', and `\.{in}', respectively. However, the latter abbreviations
+are not used in the standard versions of \.{WEAVE.WEB} and \.{TANGLE.WEB}
+that are distributed to people who are installing \.{WEB} on other
+computers, and the programs are designed to produce only standard ASCII
+characters as output if the input consists entirely of ASCII characters.
+
+2. If you have an extended character set, all of the characters listed
+in Appendix C of {\sl The \TeX book\/} can be used in strings. But you should
+stick to standard ASCII characters if you want to write programs that will
+be useful to all the poor souls out there who don't have extended
+character sets.
+
+3. The \TeX\ file output by \.{WEAVE} is broken into lines having at most
+80 characters each. The algorithm that does this line breaking is unaware
+of \TeX's convention about comments following `\.\%' signs on a line. When
+\TeX\ text is being copied, the existing line breaks are copied as well,
+so there is no problem with `\.\%' signs unless the original \.{WEB} file
+contains a line more than eighty characters long or a line with \PASCAL\
+text in \pb\ that expands to more than eighty characters long. Such lines
+should not have `\.\%' signs.
+
+4. \PASCAL\ text is translated by a ``bottom up'' procedure that
+identifies each token as a ``part of speech'' and combines parts of speech
+into larger and larger phrases as much as possible according to a special
+grammar that is explained in the documentation of \.{WEAVE}. It is easy to
+learn the translation scheme for simple constructions like single
+identifiers and short expressions, just by looking at a few examples of
+what \.{WEAVE} does, but the general mechanism is somewhat complex because
+it must handle much more than \PASCAL\ itself. Furthermore the output
+contains embedded codes that cause \TeX\ to indent and break lines as
+necessary, depending on the fonts used and the desired page width. For
+best results it is wise to adhere to the following restrictions:
+
+\yskip\itemitem{a)}Comments in \PASCAL\ text should appear only after
+statements or clauses; i.e., after semicolons, after reserved words like
+\&{then} and \&{do}, or before reserved words like \&{end} and \&{else}.
+Otherwise \.{WEAVE}'s parsing method may well get mixed up.
+
+\itemitem{b)}Don't enclose long \PASCAL\ texts in \pb, since the
+indentation and line breaking codes are omitted when the \pb\ text is
+translated from \PASCAL\ to \TeX. Stick to simple expressions or
+statements.
+
+\yskip
+5. Comments and module names are not permitted in \pb\ text. After a `\.|'
+signals the change from \TeX\ text to \PASCAL\ text, the next `\.|' that is
+not part of a string or control text ends the \PASCAL\ text.
+
+6. A comment must have properly nested occurrences of left and right
+braces, otherwise \.{WEAVE} and \.{TANGLE} will not know where the comment
+ends. However, the character pairs `\.{\\\{}' and `\.{\\\}}' do not count
+as left and right braces in comments, and the character pair `\.{\\|}'
+does not count as a delimiter that begins \PASCAL\ text. (The actual rule
+is that a character after `\.\\' is ignored; hence in `\.{\\\\\{}' the
+left brace {\sl does\/} count.) At present, \.{TANGLE} and \.{WEAVE} treat
+comments in slightly different ways, and it is necessary to satisfy both
+conventions: \.{TANGLE} ignores `\.|' characters entirely, while \.{WEAVE}
+uses them to switch between \TeX\ text and \PASCAL\ text. Therefore, a
+comment that includes a brace in a string in \pb---e.g., `\.{\{{ }look at
+this |"\{"| \}}'---will be handled correctly by \.{WEAVE}, but \.{TANGLE}
+will think there is an unmatched left brace. In order to satisfy both
+processors, one can write `\.{\{{ }look at this \\leftbrace\\{ }\}}', after
+setting up`\.{\\def\\leftbrace\{|"\{"|\}}'.
+
+7. Reserved words of \PASCAL\ must appear entirely in lowercase letters
+in the \.{WEB} file; otherwise their special nature will not be recognized
+by \.{WEAVE}. You could, for example, have a macro named \\{END} and it
+would not be confused with \PASCAL's \&{end}.
+
+However, you may not want to capitalize macro names just to distinguish them
+from other identifiers. Here is a way to unreserve \PASCAL's reserved word
+`\&{type}' and to substitute another word `\&{mtype}' in the \.{WEB} file.
+$$\vbox{\halign{\tt #\hfil\cr
+@d type(\char'43) == mem[\char'43].t\cr
+@d mtype == t \char'100\char'46{} y \char'100\char'46{} p
+ \char'100\char'46{} e\cr
+@f mtype == type\cr
+@f type == true\cr}}$$
+In the output of \.{TANGLE}, the macro \.{mtype} now produces `\.{TYPE}'
+and the macro \.{type(x)} now produces `\.{MEM[X].T}'. In the output of
+\.{WEAVE}, these same inputs produce \&{mtype} and \\{type}(\|x),
+respectively.
+
+8. The \.{@f} feature allows you to define one identifier to act like
+another, and these format definitions are carried out sequentially, as the
+example above indicates. However, a given identifier has only one printed format
+throughout the entire document (and this format will even be used before
+the \.{@f} that defines it). The reason is that \.{WEAVE} operates in two
+passes; it processes \.{@f}'s and cross-references on the first pass and
+it does the output on the second.
+
+9. You may want some \.{@f} formatting that doesn't correspond to any
+existing reserved word. In that case, \.{WEAVE} could be extended in a
+fairly obvious way to include new ``reserved words'' in its vocabulary.
+The identifier `\&{xclause}' has in fact been included already as a
+reserved word, so that it can be used to format the `\&{loop}' macro,
+where `\&{loop}' is defined to be equivalent to `\&{while \\{true} do}'.
+
+10. Sometimes it is desirable to insert spacing into \PASCAL\ code that is
+more general than the thin space provided by `\.{@,}'. The \.{@t} feature
+can be used for this purpose; e.g., `\.{@t\\hskip 1in@>}' will
+leave one inch of blank space. Furthermore, `\.{@t\\4@>}' can be
+used to backspace by one unit of indentation, since the control sequence
+\.{\\4} is defined in \.{webmac} to be such a backspace. (This
+control sequence is used, for example, at the beginning of lines that
+contain labeled statements, so that the label will stick out a little at
+the left.)
+
+11. \.{WEAVE} and \.{TANGLE} are designed to work with two input files,
+called \\{web\_file} and \\{change\_file}, where \\{change\_file} contains
+data that overrides selected portions of \\{web\_file}. The resulting merged
+text is actually what has been called the \.{WEB} file elsewhere in this
+report.
+
+Here's how it works: The change file consists of zero or more ``changes,''
+where a change has the form `\.{@x}$\langle$old lines$\rangle$\.{@y}$\langle$%
+new lines$\rangle$\.{@z}'. The special control codes \.{@x}, \.{@y}, \.{@z},
+which are allowed only in change files, must appear at the beginning of a line;
+the remainder of such a line is ignored.
+The $\langle$old lines$\rangle$ represent material that exactly matches
+consecutive lines of the \\{web\_file}; the $\langle$new lines$\rangle$
+represent zero or more lines that are supposed to replace the old. Whenever
+the first ``old line'' of a change is found to match a line in the
+\\{web\_file}, all the other lines in that change must match too.
+
+Between changes, before the first change, and after the last change,
+the change file can have any number of lines that do not begin with
+`\.{@x}', `\.{@y}', or~`\.{@z}'. Such lines are bypassed and not used for
+matching purposes.
+
+This dual-input feature is useful when working with a master \.{WEB} file
+that has been received from elsewhere (e.g., \.{TANGLE.WEB} or
+\.{WEAVE.WEB} or \.{TEX.WEB}), when changes are desirable to customize the
+program for your local computer system. You will be able to debug your
+system-dependent changes without clobbering the master web file; and once
+your changes are working, you will be able to incorporate them readily
+into new releases of the master web file that you might receive from time
+to time.
+ \section Appendices.
+The basic ideas of \.{WEB} can be understood most easily by looking at
+examples of ``real'' programs. Appendix~A shows the \.{WEB} input that
+generated modules 55--59 of the \.{WEAVE} program; Appendix~B shows the
+corresponding \TeX\ code output by \.{WEAVE}; and Appendix~C shows excerpts
+from the corresponding \PASCAL\ code output by \.{TANGLE}.
+
+The complete webs for \.{WEAVE} and \.{TANGLE} appear as the bulk of this
+report, in Appendices D and~E. The reader should first compare Appendix~A
+to the corresponding portion of Appendix~D; then the same material should
+be compared to Appendices B and~C. Finally, if time permits, the reader may
+enjoy studying the complete programs in Appendices D and~E, since \.{WEAVE}
+and \.{TANGLE} contain several interesting aspects, and since an attempt
+has been made in these appendices to evolve a style of programming that
+makes good use of the \.{WEB} language.
+
+Finally, Appendix F is the `\.{webmac}' file that sets \TeX\ up to accept
+the output of \.{WEAVE}; Appendix~G discusses how to use some of its macros
+to vary the output formats; and Appendix~H discusses what needs to be done
+when \.{WEAVE} and \.{TANGLE} are installed in a new operating environment.
+ \section Performance statistics.
+The programs in Appendices D and E will optionally keep statistics on
+how much memory they require. Here is what they once printed out when
+processing themselves:
+
+\def\pstat#1#2#3
+#4{\yskip\noindent\.{#1} applied to \.{#2} (cpu time #3 sec)\par
+\halign{\quad\tt##\hfil\cr#4}}
+
+\pstat{TANGLE}{TANGLE}{15}
+{Memory usage statistics:\cr
+456 names, 215 replacement texts;\cr
+3396+3361 bytes, 6685+7329+5805 tokens.\cr}
+
+\pstat{TANGLE}{WEAVE}{30}
+{Memory usage statistics:\cr
+692 names, 339 replacement texts;\cr
+4576+4294 bytes, 10184+9875+9150 tokens.\cr}
+
+\pstat{WEAVE}{TANGLE}{45}
+{Memory usage statistics: 478 names, 2045 cross references, 4159+3729 bytes;\cr
+parsing required 684 scraps, 1300 texts, 3766 tokens, 119 levels;\cr
+sorting required 34 levels.\cr}
+
+\pstat{WEAVE}{WEAVE}{65}
+{Memory usage statistics: 737 names, 3306 cross references, 4896+4962 bytes;\cr
+parsing required 684 scraps, 1300 texts, 3766 tokens, 119 levels;\cr
+sorting required 73 levels.\cr}
+
+\yskip\noindent The cpu time for \PASCAL\ to process \.{TANGLE.PAS} was
+approximately 13 seconds, and \.{WEAVE.PAS} took approximately 26 seconds;
+thus the tangling time was slightly more than the compiling time. The cpu
+time for \TeX\ to process \.{TANGLE.TEX} was approximately 500 seconds,
+and \.{WEAVE.TEX} took approximately 750 seconds (i.e., about 7
+seconds per printed page, where these pages are substantially larger than
+the pages in a normal book). All cpu times quoted are for a DECsystem-10.
+
+\def\K{{\mc K}}
+The file \.{TANGLE.WEB} is about 125\K\ characters long; \.{TANGLE}
+reduces it to a file \.{TANGLE.PAS} whose size is about 42\K\ characters,
+while \.{WEAVE} expands it to a file \.{TANGLE.TEX} of about 185\K\null.
+The corresponding file sizes for \.{WEAVE.WEB}, \.{WEAVE.PAS}, and
+\.{WEAVE.TEX} are 180\K, 89\K, and 265\K.
+
+The much larger file \.{TEX.WEB} led to the following numbers:
+
+\pstat{TANGLE}{TEX}{110}
+{Memory usage statistics:\cr
+3750 names, 1768 replacement texts;\cr
+41895+41053 bytes, 42378+45074+41091 tokens.\cr}
+
+\pstat{WEAVE}{TEX}{270}
+{Memory usage statistics: 3412 names, 19699 cross references,
+ 37900+40232 bytes;\cr
+parsing required 685 scraps, 1303 texts, 3784 tokens, 104 levels;\cr
+sorting required 52 levels.\cr}
+
+\yskip\noindent
+\PASCAL\ did \.{TEX.PAS} in about 75 seconds; \TeX\ did \.{TEX.TEX}
+in about 3600.
+ % Here is a quotation that could not really be omitted
+
+\vfill
+
+{\baselineskip9pt
+\halign to\hsize{\hfil\quoteit#\tabskip 0pt plus 100pt&
+ \hfil\quoteit#\tabskip 0pt\cr
+O, what a tangled web we weave&
+ O, what a tangled WEB we weave\cr
+When first we practise to deceive!&
+ When \TeX\ we practise to conceive!\cr
+\noalign{\vskip 2pt}
+\quoterm ---SIR WALTER SCOTT, {\quoteit Marmion} 6:17 (1808)&
+ \quoterm ---RICHARD PALAIS (1982)\cr
+}}
+\eject
+ \def\runninghead{APPENDIX A --- {\tentt WEB} FILE FORMAT}
+\section Appendix A.
+This excerpt from \.{WEAVE.WEB} produced modules 55--59 in Appendix~D.
+Note that some of the lines are indented to show the program structure.
+The indentation is ignored by \.{WEAVE} and \.{TANGLE}, but users find
+that \.{WEB} files are quite readable if they have some such indentation.
+
+\vskip 6pt
+\begingroup \def\tt{\eighttt} \baselineskip9pt
+% Note to myself: I had to remove SAIL characters from the file here!
+% Also tabs replaced by double-space. The changes were made in WEAVE source.
+\verbatim
+@* Searching for identifiers.
+The hash table described above is updated by the |id_lookup| procedure,
+which finds a given identifier and returns a pointer to its index in
+|byte_start|. The identifier is supposed to match character by character
+and it is also supposed to have a given |ilk| code; the same name may be
+present more than once if it is supposed to appear in the index with
+different typesetting conventions.
+If the identifier was not already present, it is inserted into the table.
+
+Because of the way \.{WEAVE}'s scanning mechanism works, it is most convenient
+to let |id_lookup| search for an identifier that is present in the |buffer|
+array. Two other global variables specify its position in the buffer: the
+first character is |buffer[id_first]|, and the last is |buffer[id_loc-1]|.
+
+@<Glob...@>=
+@!id_first:0..long_buf_size; {where the current identifier begins in the buffer}
+@!id_loc:0..long_buf_size; {just after the current identifier in the buffer}
+@#
+@!hash:array [0..hash_size] of sixteen_bits; {heads of hash lists}
+
+@ Initially all the hash lists are empty.
+
+@<Local variables for init...@>=
+@!h:0..hash_size; {index into hash-head array}
+
+@ @<Set init...@>=
+for h:=0 to hash_size-1 do hash[h]:=0;
+
+@ Here now is the main procedure for finding identifiers (and index
+entries). The parameter |t| is set to the desired |ilk| code. The
+identifier must either have |ilk=t|, or we must have
+|t=normal| and the identifier must be a reserved word.
+
+@p function id_lookup(@!t:eight_bits):name_pointer; {finds current identifier}
+label found;
+var i:0..long_buf_size; {index into |buffer|}
+@!h:0..hash_size; {hash code}
+@!k:0..max_bytes; {index into |byte_mem|}
+@!w:0..ww-1; {row of |byte_mem|}
+@!l:0..long_buf_size; {length of the given identifier}
+@!p:name_pointer; {where the identifier is being sought}
+begin l:=id_loc-id_first; {compute the length}
+@<Compute the hash code |h|@>;
+@<Compute the name location |p|@>;
+if p=name_ptr then @<Enter a new name into the table at position |p|@>;
+id_lookup:=p;
+end;
+
+@ A simple hash code is used: If the sequence of
+ASCII codes is $c_1c_2\ldots c_m$, its hash value will be
+$$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,|hash_size|.$$
+
+@<Compute the hash...@>=
+h:=buffer[id_first]; i:=id_first+1;
+while i<id_loc do
+ begin h:=(h+h+buffer[i]) mod hash_size; incr(i);
+ end
+?endgroup % end of verbatim mode
+\endgroup
+\vfill\eject
+ \def\runninghead{APPENDIX B --- TRANSLATION BY {\tentt WEAVE}}
+\section Appendix B.
+This excerpt from \.{WEAVE.TEX} corresponds to Appendix A.
+
+% I've inserted \vfill's here at the blank lines, to squeeze this on one page!
+\begingroup \def\tt{\eighttt} \baselineskip9pt
+\verbatim
+?vfill?verbatimgobble
+\N55. Searching for identifiers.
+The hash table described above is updated by the \\{id\_lookup} procedure,
+which finds a given identifier and returns a pointer to its index in
+\\{byte\_start}. The identifier is supposed to match character by character
+and it is also supposed to have a given \\{ilk} code; the same name may be
+present more than once if it is supposed to appear in the index with
+different typesetting conventions.
+If the identifier was not already present, it is inserted into the table.
+?vfill?verbatimgobble
+Because of the way \.{WEAVE}'s scanning mechanism works, it is most convenient
+to let \\{id\_lookup} search for an identifier that is present in the %
+\\{buffer}
+array. Two other global variables specify its position in the buffer: the
+first character is $\\{buffer}[\\{id\_first}]$, and the last is $\\{buffer}[%
+\\{id\_loc}-1]$.
+?vfill?verbatimgobble
+\Y\P$\4\X9:Globals in the outer block\X\mathrel{+}\S$\6
+\4\\{id\_first}: \37$0\to\\{long\_buf\_size}$;\C{where the current identifier
+begins in the buffer}\6
+\4\\{id\_loc}: \37$0\to\\{long\_buf\_size}$;\C{just after the current
+identifier in the buffer}\7
+\4\\{hash}: \37\&{array} $[0\to\\{hash\_size}]$ \1\&{of}\5
+\\{sixteen\_bits};\C{heads of hash lists}\2\par
+\fi
+?vfill?verbatimgobble
+\M56. Initially all the hash lists are empty.
+?vfill?verbatimgobble
+\Y\P$\4\X16:Local variables for initialization\X\mathrel{+}\S$\6
+\4\|h: \37$0\to\\{hash\_size}$;\C{index into hash-head array}\par
+\fi
+?vfill?verbatimgobble
+\M57. \P$\X10:Set initial values\X\mathrel{+}\S$\6
+\&{for} $\|h\K0\mathrel{\&{to}}\\{hash\_size}-1$ \1\&{do}\5
+$\\{hash}[\|h]\K0$;\2\par
+\fi
+?vfill?verbatimgobble
+\M58. Here now is the main procedure for finding identifiers (and index
+entries). The parameter \|t is set to the desired \\{ilk} code. The
+identifier must either have $\\{ilk}=\|t$, or we must have
+$\|t=\\{normal}$ and the identifier must be a reserved word.
+?vfill?verbatimgobble
+\Y\P\4\&{function}\1\ \37$\\{id\_lookup}(\|t:\\{eight\_bits})$: \37\\{name%
+\_pointer};\C{finds current identifier}\6
+\4\&{label} \37\\{found};\6
+\4\&{var} \37\|i: \37$0\to\\{long\_buf\_size}$;\C{index into \\{buffer}}\6
+\|h: \37$0\to\\{hash\_size}$;\C{hash code}\6
+\|k: \37$0\to\\{max\_bytes}$;\C{index into \\{byte\_mem}}\6
+\|w: \37$0\to\\{ww}-1$;\C{row of \\{byte\_mem}}\6
+\|l: \37$0\to\\{long\_buf\_size}$;\C{length of the given identifier}\6
+\|p: \37\\{name\_pointer};\C{where the identifier is being sought}\2\6
+\&{begin} \37$\|l\K\\{id\_loc}-\\{id\_first}$;\C{compute the length}\6
+\X59:Compute the hash code \|h\X;\6
+\X60:Compute the name location \|p\X;\6
+\&{if} $\|p=\\{name\_ptr}$ \1\&{then}\5
+\X62:Enter a new name into the table at position \|p\X;\2\6
+$\\{id\_lookup}\K\|p$;\6
+\&{end};\par
+\fi
+?vfill?verbatimgobble
+\M59. A simple hash code is used: If the sequence of
+ASCII codes is $c_1c_2\ldots c_m$, its hash value will be
+$$(2^{n-1}c_1+2^{n-2}c_2+\cdots+c_n)\,\bmod\,\\{hash\_size}.$$
+?vfill?verbatimgobble
+\Y\P$\4\X59:Compute the hash code \|h\X\S$\6
+$\|h\K\\{buffer}[\\{id\_first}]$;\5
+$\|i\K\\{id\_first}+1$;\6
+\&{while} $\|i<\\{id\_loc}$ \1\&{do}\6
+\&{begin} \37$\|h\K(\|h+\|h+\\{buffer}[\|i])\mathbin{\&{mod}}\\{hash\_size}$;\5
+$\\{incr}(\|i)$;\6
+\&{end}\2\par
+\U section~58.\fi
+?endgroup
+\endgroup
+\eject
+ \def\runninghead{APPENDIX C --- TRANSLATION BY {\tentt TANGLE}}
+\section Appendix C.
+The \.{TANGLE} processor converts \.{WEAVE.WEB} into a syntactically
+correct (but not very pretty) \PASCAL\ program \.{WEAVE.PAS}.
+The first three and last two lines of output are shown here, together with the
+lines of code generated by modules 55--62 and the environments of
+those lines. There are 1559 lines in all; the notation
+`\.. \\. \\.' stands for portions that are not shown.
+
+Note that, for example, the code corresponding to
+module 55 begins with `\.{\{55:\}}' and ends with `\.{\{:55\}}';
+the code from modules 59--62 has been tangled into the code from module 58.
+
+\vskip6pt
+\verbatim
+{2:}{4:}{$C-,A+,D-}{[$C+,D+]}{:4}
+PROGRAM WEAVE(WEBFILE,CHANGEFILE,TEXFILE);LABEL 9999;CONST{8:}
+MAXBYTES=45000;MAXNAMES=5000;MAXMODULES=2000;HASHSIZE=353;BUFSIZE=100;
+ . . .
+TOKPTR:0..MAXTOKS;{MAXTOKPTR,MAXTXTPTR:0..MAXTOKS;}{:53}{55:}
+IDFIRST:0..LONGBUFSIZE;IDLOC:0..LONGBUFSIZE;
+HASH:ARRAY[0..HASHSIZE]OF SIXTEENBITS;{:55}{63:}CURNAME:NAMEPOINTER;
+ . . .
+PROCEDURE INITIALIZE;VAR{16:}I:0..127;{:16}{40:}WI:0..1;{:40}{56:}
+H:0..HASHSIZE;{:56}{247:}C:ASCIICODE;{:247}BEGIN{10:}HISTORY:=0;{:10}
+ . . .
+TOKPTR:=1;TEXTPTR:=1;TOKSTART[0]:=1;TOKSTART[1]:=1;{MAXTOKPTR:=1;
+MAXTXTPTR:=1;}{:54}{57:}FOR H:=0 TO HASHSIZE-1 DO HASH[H]:=0;{:57}{94:}
+SCANNINGHEX:=FALSE;{:94}{102:}MODTEXT[0]:=32;{:102}{124:}OUTPTR:=1;
+ . . .
+IF R=0 THEN XREF[P]:=XREFPTR ELSE XMEM[R].XLINKFIELD:=XREFPTR;END;{:51}
+{58:}FUNCTION IDLOOKUP(T:EIGHTBITS):NAMEPOINTER;LABEL 31;
+VAR I:0..LONGBUFSIZE;H:0..HASHSIZE;K:0..MAXBYTES;W:0..1;
+L:0..LONGBUFSIZE;P:NAMEPOINTER;BEGIN L:=IDLOC-IDFIRST;{59:}
+H:=BUFFER[IDFIRST];I:=IDFIRST+1;
+WHILE I<IDLOC DO BEGIN H:=(H+H+BUFFER[I])MOD HASHSIZE;I:=I+1;END{:59};
+{60:}P:=HASH[H];
+WHILE P<>0 DO BEGIN IF(BYTESTART[P+2]-BYTESTART[P]=L)AND((ILK[P]=T)OR((T
+=0)AND(ILK[P]>3)))THEN{61:}BEGIN I:=IDFIRST;K:=BYTESTART[P];W:=P MOD 2;
+WHILE(I<IDLOC)AND(BUFFER[I]=BYTEMEM[W,K])DO BEGIN I:=I+1;K:=K+1;END;
+IF I=IDLOC THEN GOTO 31;END{:61};P:=LINK[P];END;P:=NAMEPTR;
+LINK[P]:=HASH[H];HASH[H]:=P;31:{:60};IF P=NAMEPTR THEN{62:}
+BEGIN W:=NAMEPTR MOD 2;
+IF BYTEPTR[W]+L>MAXBYTES THEN BEGIN WRITELN(TERMOUT);
+WRITE(TERMOUT,'! Sorry, ','byte memory',' capacity exceeded');ERROR;
+HISTORY:=3;JUMPOUT;END;
+IF NAMEPTR+2>MAXNAMES THEN BEGIN WRITELN(TERMOUT);
+WRITE(TERMOUT,'! Sorry, ','name',' capacity exceeded');ERROR;HISTORY:=3;
+JUMPOUT;END;I:=IDFIRST;K:=BYTEPTR[W];
+WHILE I<IDLOC DO BEGIN BYTEMEM[W,K]:=BUFFER[I];K:=K+1;I:=I+1;END;
+BYTEPTR[W]:=K;BYTESTART[NAMEPTR+2]:=K;NAMEPTR:=NAMEPTR+1;ILK[P]:=T;
+XREF[P]:=0;END{:62};IDLOOKUP:=P;END;{:58}{66:}
+FUNCTION MODLOOKUP(L:SIXTEENBITS):NAMEPOINTER;LABEL 31;VAR C:0..4;
+ . . .
+WRITE(TERMOUT,'(That was a fatal error, my friend.)');END;END{:263};
+END.{:261}
+?endgroup
+\vfill\eject
+ \pageno=197 % take account of the page numbers for App's D and E.
+ \def\runninghead{APPENDIX F --- MACROS FOR FORMATTING}
+\section Appendix F: The \.{webmac.tex} file.
+This is the file that extends ``plain \TeX'' format in order to support the
+features needed by the output of \.{WEAVE}.
+
+\vskip6pt
+\verbatim
+% standard macros for WEB listings (in addition to PLAIN.TEX)
+\xdef\fmtversion{\fmtversion+WEBMAC4.0} % identifies current set of macros
+\parskip 0pt % no stretch between paragraphs
+\parindent 1em % for paragraphs and for the first line of Pascal text
+
+\font\eightrm=cmr8 \let\sc=\eightrm % NOT a caps-and-small-caps font!
+\let\mainfont=\tenrm
+\font\titlefont=cmr7 scaled\magstep4 % title on the contents page
+\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title
+\font\tentex=cmtex10 % TeX extended character set (used in strings)
+
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\|#1{\hbox{$#1$}} % one-letter identifiers look a bit better this way
+\def\&#1{\hbox{\bf#1\/}} % boldface type for reserved words
+\def\.#1{\hbox{\tentex % typewriter type for strings
+ \let\\=\BS % backslash in a string
+ \let\'=\RQ % right quote in a string
+ \let\`=\LQ % left quote in a string
+ \let\{=\LB % left brace in a string
+ \let\}=\RB % right brace in a string
+ \let\~=\TL % tilde in a string
+ \let\ =\SP % space in a string
+ \let\_=\UL % underline in a string
+ \let\&=\AM % ampersand in a string
+ #1}}
+\def\#{\hbox{\tt\char`\#}} % parameter sign
+\def\${\hbox{\tt\char`\$}} % dollar sign
+\def\%{\hbox{\tt\char`\%}} % percent sign
+\def\^{\ifmmode\mathchar"222 \else\char`^ \fi} % pointer or hat
+% circumflex accents can be obtained from \^^D instead of \^
+\def\AT!{@} % at sign for control text
+
+\chardef\AM=`\& % ampersand character in a string
+\chardef\BS=`\\ % backslash in a string
+\chardef\LB=`\{ % left brace in a string
+\def\LQ{{\tt\char'22}} % left quote in a string
+\chardef\RB=`\} % right brace in a string
+\def\RQ{{\tt\char'23}} % right quote in a string
+\def\SP{{\tt\char`\ }} % (visible) space in a string
+\chardef\TL=`\~ % tilde in a string
+\chardef\UL=`\_ % underline character in a string
+
+\newbox\bak \setbox\bak=\hbox to -1em{} % backspace one em
+\newbox\bakk\setbox\bakk=\hbox to -2em{} % backspace two ems
+
+\newcount\ind % current indentation in ems
+\def\1{\global\advance\ind by1\hangindent\ind em} % indent one more notch
+\def\2{\global\advance\ind by-1} % indent one less notch
+\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement
+\def\4{\copy\bak} % backspace one notch
+\def\5{\hfil\penalty-1\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break
+\def\6{\ifmmode\else\par % forced break
+ \hangindent\ind em\noindent\kern\ind em\copy\bakk\ignorespaces\fi}
+\def\7{\Y\6} % forced break and a little extra space
+
+\let\yskip=\smallskip
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+\def\note#1#2.{\Y\noindent{\hangindent2em\baselineskip10pt\eightrm#1~#2.\par}}
+\def\lapstar{\rlap{*}}
+\def\startsection{\Q\noindent{\let\*=\lapstar\bf\modstar.\quad}}
+\def\defin#1{\global\advance\ind by 2 \1\&{#1 }} % begin `define' or `format'
+\def\A{\note{See also section}} % crossref for doubly defined section name
+\def\As{\note{See also sections}} % crossref for multiply defined section name
+\def\B{\mathopen{\.{@\{}}} % begin controlled comment
+\def\C#1{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % Pascal comments
+ \XX\hfil\penalty-1\hfilneg\quad$\{\,$#1$\,\}$\XX}
+\def\D{\defin{define}} % macro definition
+\def\E{\cdot10^} % exponent in floating point constant
+\def\ET{ and~} % conjunction between two section numbers
+\def\ETs{, and~} % conjunction between the last two of several section numbers
+\def\F{\defin{format}} % format definition
+\let\G=\ge % greater than or equal sign
+\def\H#1{\hbox{\rm\char"7D\tt#1}} % hexadecimal constant
+\let\I=\ne % unequal sign
+\def\J{\.{@\&}} % TANGLE's join operation
+\let\K=\gets % left arrow
+\let\L=\le % less than or equal sign
+\outer\def\M#1.{\MN#1.\ifon\vfil\penalty-100\vfilneg % beginning of section
+ \vskip12ptminus3pt\startsection\ignorespaces}
+\outer\def\N#1.#2.{\MN#1.\vfil\eject % beginning of starred section
+ \def\rhead{\uppercase{\ignorespaces#2}} % define running headline
+ \message{*\modno} % progress report
+ \edef\next{\write\cont{\Z{#2}{\modno}{\the\pageno}}}\next % to contents file
+ \ifon\startsection{\bf\ignorespaces#2.\quad}\ignorespaces}
+\def\MN#1.{\par % common code for \M, \N
+ {\xdef\modstar{#1}\let\*=\empty\xdef\modno{#1}}
+ \ifx\modno\modstar \onmaybe \else\ontrue \fi \mark{\modno}}
+\def\O#1{\hbox{\rm\char'23\kern-.2em\it#1\/\kern.05em}} % octal constant
+\def\P{\rightskip=0pt plus 100pt minus 10pt % go into Pascal mode
+ \sfcode`;=3000
+ \pretolerance 10000
+ \hyphenpenalty 10000 \exhyphenpenalty 10000
+ \global\ind=2 \1\ \unskip}
+\def\Q{\rightskip=0pt % get out of Pascal mode
+ \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 }
+\let\R=\lnot % logical not
+\let\S=\equiv % equivalence sign
+\def\T{\mathclose{\.{@\}}}} % terminate controlled comment
+\def\U{\note{This code is used in section}} % crossref for use of a section
+\def\Us{\note{This code is used in sections}} % crossref for uses of a section
+\let\V=\lor % logical or
+\let\W=\land % logical and
+\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % section name
+ \XX$\langle\,$#2{\eightrm\kern.5em#1}$\,\rangle$\XX}
+\def\Y{\par\yskip}
+\let\Z=\let % now you can \send the control sequence \Z
+\def\){\hbox{\.{@\$}}} % sign for string pool check sum
+\def\]{\hbox{\.{@\\}}} % sign for forced line break
+\def\=#1{\kern2pt\hbox{\vrule\vtop{\vbox{\hrule
+ \hbox{\strut\kern2pt\.{#1}\kern2pt}}
+ \hrule}\vrule}\kern2pt} % verbatim string
+\let\~=\ignorespaces
+\let\*=*
+
+\def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue
+\newif\ifon \newif\iftitle \newif\ifpagesaved
+\def\lheader{\mainfont\the\pageno\eightrm\qquad\rhead\hfill\title\qquad
+ \tensy x\mainfont\topmark} % top line on left-hand pages
+\def\rheader{\tensy x\mainfont\topmark\eightrm\qquad\title\hfill\rhead
+ \qquad\mainfont\the\pageno} % top line on right-hand pages
+\def\page{\box255 }
+\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi
+ \shipout\vbox{
+ \vbox to\fullpageheight{
+ \iftitle\global\titlefalse
+ \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi
+ \vfill#1}} % parameter #1 is the page itself
+ \global\advance\pageno by1}
+
+\def\rhead{\.{WEB} OUTPUT} % this running head is reset by starred sections
+\def\title{} % an optional title can be set by the user
+\def\topofcontents{\centerline{\titlefont\title}
+ \vfill} % this material will start the table of contents page
+\def\botofcontents{\vfill} % this material will end the table of contents page
+\def\contentspagenumber{0} % default page number for table of contents
+\newdimen\pagewidth \pagewidth=6.5in % the width of each page
+\newdimen\pageheight \pageheight=8.7in % the height of each page
+\newdimen\fullpageheight \fullpageheight=9in % page height including headlines
+\newdimen\pageshift \pageshift=0in % shift righthand pages wrt lefthand ones
+\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein
+ \fullpageheight=9truein\setpage}
+\def\setpage{\hsize\pagewidth\vsize\pageheight} % use after changing page size
+\def\contentsfile{CONTENTS} % file that gets table of contents info
+\def\readcontents{\input CONTENTS}
+
+\newwrite\cont
+\output{\setbox0=\page % the first page is garbage
+ \openout\cont=\contentsfile
+ \global\output{\normaloutput\page\lheader\rheader}}
+\setpage
+\vbox to \vsize{} % the first \topmark won't be null
+
+\def\ch{\note{The following sections were changed by the change file:}
+ \let\*=\relax}
+\newbox\sbox % saved box preceding the index
+\newbox\lbox % lefthand column in the index
+\def\inx{\par\vskip6pt plus 1fil % we are beginning the index
+ \write\cont{} % ensure that the contents file isn't empty
+ \closeout\cont % the contents information has been fully gathered
+ \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi
+ \global\setbox\sbox=\page \global\pagesavedtrue}
+ \pagesavedfalse \eject % eject the page-so-far and predecessors
+ \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box
+ \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height
+ \hsize=.5\pagewidth \advance\hsize by -10pt
+ % column width for the index (20pt between cols)
+ \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
+ \def\lr{L} % this tells whether the left or right column is next
+ \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
+ \else\normaloutput{\vbox to\pageheight{\box\sbox\vss
+ \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader
+ \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi}
+ \message{Index:}
+ \parskip 0pt plus .5pt
+ \outer\def\:##1, {\par\hangindent2em\noindent##1:\kern1em} % index entry
+ \let\ttentry=\. \def\.##1{\ttentry{##1\kern.2em}} % give \tt a little room
+ \def\[##1]{$\underline{##1}$} % underlined index item
+ \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar
+ \hyphenpenalty 10000 \parindent0pt}
+\def\fin{\par\vfill\eject % this is done when we are ending the index
+ \ifpagesaved\null\vfill\eject\fi % output a null index column
+ \if L\lr\else\null\vfill\eject\fi % finish the current page
+ \parfillskip 0pt plus 1fil
+ \def\rhead{NAMES OF THE SECTIONS}
+ \message{Section names:}
+ \output{\normaloutput\page\lheader\rheader}
+ \setpage
+ \def\note##1##2.{\hfil\penalty-1\hfilneg\quad{\eightrm##1 ##2.}}
+ \linepenalty=10 % try to conserve lines
+ \def\U{\note{Used in section}} % crossref for use of a section
+ \def\Us{\note{Used in sections}} % crossref for uses of a section
+ \def\:{\par\hangindent 2em}\let\*=*\let\.=\ttentry}
+\def\con{\par\vfill\eject % finish the section names
+ \rightskip 0pt \hyphenpenalty 50 \tolerance 200
+ \setpage
+ \output{\normaloutput\page\lheader\rheader}
+ \titletrue % prepare to output the table of contents
+ \pageno=\contentspagenumber \def\rhead{TABLE OF CONTENTS}
+ \message{Table of contents:}
+ \topofcontents
+ \line{\hfil Section\hbox to3em{\hss Page}}
+ \def\Z##1##2##3{\line{\ignorespaces##1
+ \leaders\hbox to .5em{.\hfil}\hfil\ ##2\hbox to3em{\hss##3}}}
+ \readcontents\relax % read the contents info
+ \botofcontents \end} % print the contents page(s) and terminate
+?endgroup
+\vfill\eject
+ \def\runninghead{APPENDIX G --- NOTES ON FORMATTING}
+\section Appendix G: How to use \.{WEB} macros.
+The macros in \.{webmac} make it possible to produce a variety of formats
+without editing the output of \.{WEAVE}, and the purpose of this appendix
+is to explain some of the possibilities.
+
+\def\point#1.{\yskip\indent#1.\quad\ignorespaces}
+
+\point 1. Three fonts have been declared in addition to the standard fonts of
+\.{PLAIN} format: You can say `\.{\{\\sc STUFF\}}' to get {\sc STUFF}
+in small caps; and you can select the largish fonts \.{\\titlefont}
+and \.{\\ttitlefont} in the title of your document, where \.{\\ttitlefont}
+is a typewriter style of type.
+
+\point 2. When you mention an identifier in \TeX\ text, you normally call
+it `\.{|identifier|}'. But you can also say `\.{\\\\\{identifier\}}'. The
+output will look the same in both cases, but the second alternative
+doesn't put \\{identifier} into the index, since
+it bypasses \.{WEAVE}'s translation from \PASCAL\ mode.
+
+\point 3. To get typewriter-like type, as when referring to `\.{WEB}', you
+can use the `\.{\\.}' macro (e.g., `\.{\\.\{WEB\}}'). In the argument to
+this macro you should insert an additional backslash before the symbols
+listed as `special string characters' in the index to \.{WEAVE}, i.e.,
+before backslashes and dollar signs and the like.
+A `\.{\\\ }' here will result in the visible space symbol; to get an
+invisible space following a control sequence you can say `\.{\{\ \}}'.
+
+\point 4. The three control sequences \.{\\pagewidth}, \.{\\pageheight},
+and \.{\\fullpageheight} can be redefined in the limbo section at the
+beginning of your \.{WEB} file, to change the dimensions of each page.
+The standard settings
+$$\lpile{\.{\\pagewidth=6.5in}\cr
+ \.{\\pageheight=8.7in}\cr
+ \.{\\fullpageheight=9in}\cr}$$
+were used to prepare the present report; \.{\\fullpageheight} is
+\.{\\pageheight} plus room for the additional heading and page numbers at
+the top of each page. If you change any of these quantities, you should
+call the macro \.{\\setpage} immediately after making the change.
+
+\point 5. The \.{\\pageshift} macro defines an amount by which right-hand
+pages (i.e., odd-numbered pages) are shifted right with respect to
+left-hand (even-numbered) ones. By adjusting this amount you may be
+able to get two-sided output in which the page numbers line up on
+opposite sides of each sheet.
+
+\point 6. The \.{\\title} macro will appear at the top of each page
+in small caps. For example, Appendix~D was produced after saying
+`\.{\\def\\title\{WEAVE\}}'.
+
+\point 7. The first page usually is assigned page number 1.
+To start on page 16, with contents
+on page 15, say this: `\.{\\def\\contentspagenumber\{15\}}
+\.{\\pageno=\\contentspagenumber} \.{\\advance\\pageno by 1}'. (Appendix~D
+was generated that way.)
+
+\point 8. The macro \.{\\iftitle} will suppress the header line if it is
+defined by `\.{\\titletrue}'. The normal value is \.{\\titlefalse}
+except for the table of contents; thus, the contents
+page is usually unnumbered.
+
+Two macros are provided to give flexibility to the table of
+contents: \.{\\topofcontents} is invoked just before the contents
+info is read, and \.{\\botofcontents} is invoked just after.
+For example, Appendix~D was produced with the following definitions:
+$$\lpile{\.{\\def\\topofcontents\{\\null\\vfill}\cr
+ \.{ { }\\titlefalse \% include headline on the contents page}\cr
+ \.{ { }\\def\\rheader\{\\mainfont Appendix D\\hfil 15\}}\cr
+ \.{ { }\\centerline\{\\titlefont The \{\\ttitlefont WEAVE\}{ }processor\}}\cr
+ \.{ { }\\vskip 15pt \\centerline\{(Version 4)\}{ }\\vfill\}}\cr}$$
+Redefining \.{\\rheader}, which is the headline for right-hand pages,
+suffices in this case to put the desired information at the top of the
+contents page.
+
+\point 9. Data for the table of contents is written to a file that
+is read after the indexes have been \TeX ed; there's one line of data
+for every starred module. For example, when Appendix~D was being generated,
+a file \.{CONTENTS.TEX} containing
+$$\lpile{\.{\\Z \{{ }Introduction\}\{1\}\{16\}}\cr
+ \.{\\Z \{{ }The character set\}\{11\}\{19\}}\cr}$$
+and similar lines was created. The \.{\\topofcontents} macro could
+redefine \.{\\Z} so that the information appears in another format.
+
+\point 10. Sometimes it is necessary or desirable to divide the output of
+\.{WEAVE} into subfiles that can be processed separately. For example,
+the listing of \TeX\ runs to more than 500 pages, and that is enough to
+exceed the capacity of many printing devices and/or their software.
+When an extremely large job isn't cut into smaller pieces, the entire
+process might be spoiled by a single error of some sort, making it
+necessary to start everything over.
+
+Here's a safe way to break a woven file into three parts:
+Say the pieces are $\alpha$,
+$\beta$, and $\gamma$, where each piece begins with a starred module.
+All macros should be defined in the opening limbo section of $\alpha$,
+and copies of this \TeX\ code should be placed at the
+beginning of $\beta$ and of $\gamma$. In order to process the parts
+separately, we need to take care of two things: The starting page
+numbers of $\beta$ and $\gamma$ need to be set up properly, and
+the table of contents data from all three runs needs to be
+accumulated.
+
+The \.{webmac} macros include two control sequences \.{\\contentsfile} and
+\.{\\readcontents} that facilitate the necessary processing. We include
+`\.{\\def\\contentsfile\{CONT1\}}' in the limbo section of $\alpha$, and
+we include `\.{\\def\\contentsfile\{CONT2\}}' in the limbo section of
+$\beta$; this causes \TeX\ to write the contents data for $\alpha$ and $\beta$
+into \.{CONT1.TEX} and \.{CONT2.TEX}. Now in $\gamma$ we say
+$$\.{\\def\\readcontents\{\\input CONT1 \\input CONT2 \\input CONTENTS\}};$$
+this brings in the data from all three pieces, in the proper order.
+
+However, we still need to solve the page-numbering problem. One way to
+do it is to include the following in the limbo material for $\beta$:
+$$\lpile{\.{\\message\{Please type the last page number of part 1: \}}\cr
+ \.{\\read -1 to \\temp \\pageno=\\temp \\advance\\pageno by 1}\cr}$$
+Then you simply provide the necessary data when \TeX\ requests
+it; a similar construction is used at the beginning of $\gamma$.
+
+This method can, of course, be used to divide a woven file into
+any number of pieces.
+
+\point 11. Sometimes it is nice to include things in the index that are
+typeset in a special way. For example, we might want to have an
+index entry for `\TeX'. \.{WEAVE} provides two simple ways to
+typeset an index entry (unless the entry is an identifier or a reserved word):
+`\.{@\^}' gives roman type, and `\.{@.}' gives typewriter type.
+But if we try to typeset `\TeX' in roman type by saying, e.g.,
+`\.{@\^\\TeX@>}', the backslash character gets in the way,
+and this entry wouldn't appear in the index with the T's.
+
+The solution is to use the `\.{@:}' feature, declaring a macro that
+simply removes a sort key as follows:
+$$\.{\\def\\9\#1\{\}}$$
+Now you can say, e.g., `\.{@:TeX\}\{\\TeX@>}' in your \.{WEB} file; \.{WEAVE}
+puts it into the index alphabetically, based on the sort key, and
+produces the macro call `\.{\\9\{TeX\}\{\\TeX\}}' which will ensure that
+the sort key isn't printed.
+
+A similar idea can be used to insert hidden material into module
+names so that they are alphabetized in whatever way you might wish.
+Some people call these tricks ``special refinements''; others call
+them ``kludges''.
+
+\point 12. The control sequence \.{\\modno} is set to the number of the
+module being typeset.
+
+\point 13. If you want to list only the modules that have changed,
+together with the index, put the command `\.{\\let\\maybe=\\iffalse}' in
+the limbo section before the first module of your \.{WEB} file. It's
+customary to make this the first change in your change file.
+
+\point 14. To get output in languages other than English, redefine the
+macros \.{\\A}, \.{\\As}, \.{\\ET}, \.{\\ETs}, \.{\\U}, \.{\\Us},
+\.{\\ch}, \.{\\fin}, and \.{\\con}. \.{WEAVE} itself need not be changed.
+
+\vfill\eject
+ \def\runninghead{APPENDIX H --- GETTING STARTED}
+\section Appendix H: Installing the \.{WEB} system.
+Suppose you want to use the \.{WEB} programs on your computer, and suppose
+that you can't simply borrow them from somebody else who has the same
+kind of machine. Here's what to do:
+
+\yskip
+\def\step(#1){\par\hangindent 2em\noindent\hbox to 2em{\hfil(#1) }\ignorespaces}
+\step(1) Get a tape that contains the files \.{WEAVE.WEB}, \.{TANGLE.WEB},
+\.{TANGLE.PAS}, and \.{WEBMAC.TEX}. The tape will probably also contain an
+example change file \.{TANGLE.CH}.
+\step(2) Look at the sections of \.{TANGLE} that are listed under ``system
+dependencies'' in the index of Appendix~E above, and figure out what changes
+(if any) will be needed for your system.
+\step(3) Make a change file \.{TANGLE.CH} that contains the changes of~(2);
+do not change your copy of \.{TANGLE.WEB}, leave it intact. (The
+rules for change files are explained at the end of the manual just before
+the appendices; you may want to look at the example change file that
+arrived with your copy of \.{TANGLE.WEB}. It's also a good idea to
+define all the ``switches'' like \&{debug} and \&{gubed} to be null in your
+first change files; then you can sure that your compiler will handle
+all of the code.)
+\step(4) Make the changes of (2) in your copy of \.{TANGLE.PAS}. (If these
+changes are extensive, you might be better off finding some computer that
+that already has \.{TANGLE} running, and making the new \.{TANGLE.PAS}
+from \.{TANGLE.WEB} and your \.{TANGLE.CH}.)
+\step(5) Use your \PASCAL\ compiler to convert your copy of \.{TANGLE.PAS}
+to a running program \.{TANGLE}.
+\step(6) Check your changes as follows: Run \.{TANGLE} on \.{TANGLE.WEB}
+and your \.{TANGLE.CH}, yielding $\.{TANGLE.PAS}'$; make a running
+program $\.{TANGLE}'$ by applying \PASCAL\ to
+$\.{TANGLE.PAS}'$; run $\.{TANGLE}'$ on \.{TANGLE.WEB} and
+your \.{TANGLE.CH}, yielding $\.{TANGLE.PAS}''$; and check
+that $\.{TANGLE.PAS}''$ is identical to
+$\.{TANGLE.PAS}'$. Once this test has been passed, you have got a
+working \.{TANGLE} program.
+\step(7) Make a change file \.{WEAVE.CH} analogous to (3), but this time
+consider the system-dependent parts of \.{WEAVE} that are listed in
+the index to Appendix~D.
+\step(8) Run \.{TANGLE} on \.{WEAVE.WEB} and your \.{WEAVE.CH}, obtaining
+\.{WEAVE.PAS}.
+\step(9) Use \PASCAL\ on \.{WEAVE.PAS} to make a running \.{WEAVE} program.
+\step(10) Run \.{WEAVE} on \.{TANGLE.WEB} and \.{TANGLE.CH} to produce
+\.{TANGLE.TEX}.
+\step(11) Run \TeX\ on \.{TANGLE.TEX}, obtaining a listing analogous to
+Appendix~E. This listing will incorporate your changes.
+\step(12) Run \.{WEAVE} on \.{WEAVE.WEB} and your \.{WEAVE.CH} to produce
+\.{WEAVE.TEX}.
+\step(13) Run \TeX\ on \.{WEAVE.TEX}, obtaining a listing analogous to
+Appendix~D that incorporates your changes.
+
+\yskip\noindent
+This description assumes that you already have a working \TeX82 system.
+But what if you don't have \TeX82? Then you start with a tape that also
+contains \.{TEX.WEB} and \.{plain.tex}, and you refer to a hardcopy
+listing of the \TeX82 program corresponding to \.{TEX.WEB}. Between steps
+(10) and (11) you do the following:
+
+\yskip
+\def\substep(10.#1){\par\hangindent 4em\noindent
+ \hbox to 4em{\hfil(10.#1) }\ignorespaces}
+\substep(10.1) Make a change file \.{TEX.CH} to fix the system dependent
+portions of \.{TEX.WEB}, in a manner analogous to step~(2). Since \TeX\ is
+a much more complex program than \.{WEAVE} or \.{TANGLE}, there are more
+system-dependent features to think about, but by now you will be good at
+making such modifications. Do not make any changes to \.{TEX.WEB}.
+\substep(10.2) Make an almost-copy of your \.{TEX.CH} called \.{INITEX.CH};
+this one will have the `\&{init}' and `\&{tini}' macros redefined in order
+to make the initialization version of \TeX. It also might have smaller
+font memory and dynamic memory areas, since \.{INITEX} doesn't need as
+much memory for such things; by setting the memory smaller in \.{INITEX},
+you guarantee that the production system will have a ``cushion.''
+\substep(10.3) Run \.{TANGLE} on \.{TEX.WEB} and \.{INITEX.CH}, obtaining
+\.{INITEX.PAS} and \.{TEX.POOL}.
+\substep(10.4) Run \PASCAL\ on \.{INITEX.PAS}, obtaining \.{INITEX}.
+\substep(10.5) Run \.{INITEX} on \.{TEX.POOL}, during which run you type
+`\.{plain}' and `\.{\\dump}'. This will produce a file \.{plain.fmt}
+containing the data needed to initialize \TeX's memory.
+\substep(10.6) Run \.{TANGLE} on \.{TEX.WEB} and the \.{TEX.CH} of (10.1),
+obtaining \.{TEX.PAS}.
+\substep(10.7) Run \PASCAL\ on \.{TEX.PAS}, obtaining \.{VIRTEX}.
+\substep(10.8) If your operating system supports programs whose core images
+have been saved, run \.{VIRTEX}, type `\.{\&plain}', then save the core image
+and call it \TeX. Otherwise, \.{VIRTEX} will be your \TeX, and it will
+read `\.{plain.fmt}' (or some other \.{fmt} file) each time you run.
+
+\yskip
+This 21-step process may seem long, but it is actually an oversimplification,
+since you also need fonts and a way to print the device-independent files
+that \TeX\ spews out. On the other hand, the total number of steps is not
+quite so large when you consider that \.{TANGLE}-followed-by-\PASCAL\ and
+\.{WEAVE}-followed-by-\TeX\ may be regarded as single operations.
+
+If you have only the present report, not a tape, you will have to prepare
+files \.{WEAVE.WEB} and \.{TANGLE.WEB} by hand, typing them into the
+computer by following Appendices D and E. Then you have to simulate the
+behavior of \.{TANGLE} by converting \.{TANGLE.WEB} manually into
+\.{TANGLE.PAS}; with a good text editor this takes about six hours. Then
+you have to correct errors that were made in all this hand work; but still
+the whole project is not impossibly difficult, because in fact the entire
+development of \.{WEAVE} and \.{TANGLE} (including the writing of the
+programs and this manual) took less than two months of work.
+ \vfill\end
diff --git a/systems/knuth/local/GPCtypes.h b/systems/knuth/local/GPCtypes.h
new file mode 100644
index 0000000000..59196bcfa8
--- /dev/null
+++ b/systems/knuth/local/GPCtypes.h
@@ -0,0 +1,108 @@
+/* some typedefs to allow C extensions in GPC programs */
+#include <stdio.h>
+
+typedef enum {FALSE, TRUE} bool;
+typedef struct Fdr* FDR;
+typedef int (*t_InFunc) (void *, char *, int, int *);
+typedef int (*t_OutFunc)(void *, const char *, int, int *);
+typedef enum { foReset, foRewrite, foAppend, foSeekRead, foSeekWrite, foSeekUpdate } TOpenMode;
+typedef void (*TOpenProc) (void *, TOpenMode);
+typedef size_t (*TReadFunc) (void *, char *, size_t);
+typedef size_t (*TWriteFunc) (void *, const char *, size_t);
+typedef void (*TFlushProc) (void *);
+typedef void (*TCloseProc) (void *);
+typedef void (*TDoneProc) (void *);
+
+typedef long long int UnixTimeType; /* This is hard-coded in the compiler. Do not change here. */
+#define FILE_BUFSIZE 1024
+
+typedef struct {
+ int Capacity;
+ int length;
+ char string [256]; /* the length is actually variable */
+} STRING;
+
+typedef struct
+{
+ char Bound;
+ char Force;
+ char Extensions_valid;
+ char Readable;
+ char Writable;
+ char Executable;
+ char Existing;
+ char Directory;
+ long long int Size;
+ UnixTimeType AccessTime;
+ UnixTimeType ModificationTime;
+ UnixTimeType ChangeTime;
+ int Error;
+ FILE *CFile;
+ STRING Name;
+} GPC_BINDING;
+
+struct Fdr {
+ unsigned char *FilBuf; /* address of the file buffer in heap */
+ int FilSta; /* status bits; see below */
+ int FilSiz; /* buffer size: if packed then in bits else bytes */
+ /* THE ABOVE FIELDS ACCESSED BY COMPILER */
+ /* The fields below are used only by RTS */
+ /* Internal buffering and used for ReadStr/WriteStr */
+ unsigned char *BufPtr; /* NOT the Standard Pascal file buffer, that is (*FilBuf) */
+ int BufSize;
+ int BufPos;
+ int Flags;
+ char *FilNam; /* Internal name of the file */
+ char *ExtNam; /* External name of the file */
+ FILE *FilJfn; /* FILE pointer */
+ int RtsSta; /* run time system status bits */
+ FDR NxtFdr; /* FDR chain pointer */
+ GPC_BINDING *Binding; /* Binding of the file */
+ char *BoundName; /* Name the binding refers to as a CString */
+ int BindingChanged;
+ t_InFunc hack_InFunc;
+ t_OutFunc hack_OutFunc;
+ void *PrivateData;
+ TOpenProc OpenProc;
+ TReadFunc ReadFunc;
+ TWriteFunc WriteFunc;
+ TFlushProc FlushProc;
+ TCloseProc CloseProc;
+ TDoneProc DoneProc;
+ unsigned char InternalBuffer [FILE_BUFSIZE]; /* NOT the Standard Pascal file buffer, that is (*FilBuf) */
+};
+
+/* FilSta bit definitions */
+
+#define FiUnd (1 << 0) /* File buffer is totally undefined */
+#define FiEof (1 << 2) /* End of file is true */
+#define FiEln (1 << 3) /* End of line is true. Textfiles only */
+#define FiTxt (1 << 4) /* It's a TEXT file */
+#define FiExt (1 << 5) /* External file */
+#define FiExtB (1 << 6) /* External or bound file */
+#define FiPck (1 << 7) /* Packed file */
+#define FiClr (1 << 8) /* Empty file */
+#define FiLzy (1 << 9) /* This file is lazy */
+#define FiEofOK (1 << 10) /* Internal flag if FiUnd is set: Accept EOF without EOLN */
+#define FiDacc (1 << 11) /* This is a direct access file */
+#define FiLget (1 << 12) /* Must do a get before buffer reference (Lazy I/O) */
+#define FiByte (1 << 13) /* File buffer is actually one byte size */
+#define FiBindable (1 << 15) /* File is bindable */
+
+/* RtsSta bit definitions */
+
+#define FiNOP 0 /* File has not been opened */
+#define FiRONLY (1 << 0) /* File opened but is read only */
+#define FiORE (1 << 1) /* File open for reading */
+#define FiWRI (1 << 2) /* File open for writing */
+#define FiRND (1 << 3) /* File open for random access */
+#define FiWONLY (1 << 4) /* File opened but is write only */
+/* RtsSta: Device specific bits */
+#define FiTTY (1 << 10) /* TTY: flush output before GET */
+#define FiSEEK (1 << 12) /* File is seekable */
+#define FiSIZEK (1 << 13) /* Size of file is known */
+#define FiFLUSH (1 << 14) /* flush after write */
+
+extern int _p_argc; /* argument count on command line */
+extern char **_p_argv; /* argument vector on command line */
+extern char **_p_envp; /* environment vector */
diff --git a/systems/knuth/local/Makefile b/systems/knuth/local/Makefile
new file mode 100644
index 0000000000..da0b13cc0e
--- /dev/null
+++ b/systems/knuth/local/Makefile
@@ -0,0 +1,12 @@
+# This is an example Makefile for the binarydemo program only
+
+PFLAGS= -g
+CFLAGS= -g
+PC= gpc
+
+bdemo: bdemo_ext.h bdemo_ext.o binarydemo.o
+ ${PC} ${PFLAGS} -o bdemo binarydemo.o bdemo_ext.o
+
+clean:
+ rm -f *.o *~ core btest bdemo
+
diff --git a/systems/knuth/local/Makefile-sparc b/systems/knuth/local/Makefile-sparc
new file mode 100644
index 0000000000..fb16ac3492
--- /dev/null
+++ b/systems/knuth/local/Makefile-sparc
@@ -0,0 +1,12 @@
+# This is an example Makefile for the binarydemo program only
+
+PFLAGS= -g
+CFLAGS= -g
+PC= time pc
+
+bdemo: bdemo_ext.h bdemo_ext.o binarydemo.o
+ pc ${PFLAGS} -o bdemo binarydemo.o bdemo_ext.o
+
+clean:
+ rm -f *.o *~ core btest bdemo
+
diff --git a/systems/knuth/local/bdemo_ext.c b/systems/knuth/local/bdemo_ext.c
new file mode 100644
index 0000000000..91fedf8f61
--- /dev/null
+++ b/systems/knuth/local/bdemo_ext.c
@@ -0,0 +1,81 @@
+/* External procedures for the binary demo program using GPC */
+
+#include "GPCtypes.h" /* defines GNU Pascal I/O structure */
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#define namelength 100 /* should agree with binarydemo.p */
+
+bool Argv(int n, char* str)
+{
+ register char *s,*t;
+ if (n<0 || n>=_p_argc) return FALSE;
+ s=_p_argv[n];
+ if (strlen(s) >= namelength) return FALSE;
+ for (t=str; *s; s++,t++) *t=*s;
+ *t='\0';
+ return TRUE;
+}
+
+void Bread(FDR filep, char *x)
+{
+ /* reads a character WITHOUT using the Pascal runtime buffers etc */
+ register char c;
+ c=getc(filep->FilJfn);
+ *x=c;
+}
+
+bool Testeof(FDR filep)
+{
+ register int c;
+ register FILE *iop; /* stdio-style FILE pointer */
+
+ iop = filep->FilJfn;
+ if (feof(iop)) return TRUE;
+ c = getc(iop);
+ if (c == EOF) return TRUE;
+ else {
+ ungetc(c,iop);
+ return FALSE;
+ }
+}
+
+bool Testreadaccess(char *fn)
+{
+ return access(fn,R_OK)? FALSE: TRUE;
+}
+
+bool Testwriteaccess(fn)
+ char *fn;
+{
+ int f;
+ f = creat(fn,0666); /* we try to create a new file, because
+ simply testing write access with access(fn,W_OK)
+ will fail when the file doesn't exist */
+ if (f >= 0) {
+ close(f); return TRUE;
+ } else return FALSE;
+}
+
+int Flength(FDR filep)
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+ register long pos; /* current file position */
+ register int len; /* the file length */
+
+ iop = filep->FilJfn;
+ pos = ftell(iop);
+ fseek(iop,0L,SEEK_END);
+ len = ftell(iop);
+ fseek(iop,pos,0);
+ return len;
+}
+
+void Movetobyte(FDR filep,int cnt)
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+
+ iop = filep->FilJfn;
+ fseek(iop,(long)cnt,SEEK_SET);
+}
diff --git a/systems/knuth/local/bdemo_ext.c-sparc b/systems/knuth/local/bdemo_ext.c-sparc
new file mode 100644
index 0000000000..c0b78334da
--- /dev/null
+++ b/systems/knuth/local/bdemo_ext.c-sparc
@@ -0,0 +1,84 @@
+/* External procedures for the binary demo program */
+
+#include "h00vars.h" /* defines Pascal I/O structure */
+
+#define namelength 100 /* should agree with binarydemo.p */
+
+movetobyte(filep,cnt)
+ register struct iorec *filep;
+ int cnt;
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+
+ iop = filep->fbuf;
+ fseek(iop,(long)cnt,0);
+}
+
+bool testeof(filep)
+ register struct iorec *filep;
+{
+ register char c;
+ register FILE *iop; /* stdio-style FILE pointer */
+
+ if (filep->funit & EOFF)
+ return(TRUE);
+ else { /* check to see if next is EOF */
+ iop = filep->fbuf;
+ c = getc(iop);
+ if (c == EOF)
+ return(TRUE);
+ else {
+ ungetc(c,iop);
+ return(FALSE);
+ }
+ }
+}
+
+bool testreadaccess(fn)
+ char *fn;
+{
+ register char *p;
+
+ fn[namelength-1] = ' ';
+ p = fn;
+ while (*p++ != ' ');
+ p--;
+ *p = '\0';
+ if (access(fn,4)==0) {
+ *p = ' '; return(TRUE);}
+ else {*p = ' '; return(FALSE);}
+}
+
+bool testwriteaccess(fn)
+ char *fn;
+{
+ register char *p;
+ int f;
+
+ fn[namelength-1] = ' ';
+ p=fn;
+ while (*p++ != ' ');
+ p--;
+ *p = '\0';
+ f = creat(fn,0666); /* we try to create a new file, because
+ simply testing write access with access(fn,2)
+ will fail when the file doesn't exist */
+ *p = ' ';
+ if (f >= 0) {close(f); return(TRUE);}
+ else return(FALSE);
+}
+
+int flength(filep)
+ register struct iorec *filep;
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+ register long pos; /* current file position */
+ register int len; /* the file length */
+
+ iop = filep->fbuf;
+ pos = ftell(iop);
+ fseek(iop,0L,2);
+ len = ftell(iop);
+ fseek(iop,pos,0);
+ return(len);
+}
diff --git a/systems/knuth/local/bdemo_ext.h b/systems/knuth/local/bdemo_ext.h
new file mode 100644
index 0000000000..f8eaa69bf2
--- /dev/null
+++ b/systems/knuth/local/bdemo_ext.h
@@ -0,0 +1,15 @@
+{names of external C routines to include in GNU Pascal binarydemo program}
+
+function argv(no:Integer;var str:UNIXfilename):boolean; external;
+
+procedure bread(var a:bytefile; var x:byte); external;
+
+function testeof(var f:bytefile):boolean; external;
+
+function testreadaccess(var a:UNIXfilename):boolean; external;
+
+function testwriteaccess(var a:UNIXfilename):boolean; external;
+
+function flength(var f:bytefile):integer; external;
+
+procedure movetobyte(var f:bytefile; n:integer); external;
diff --git a/systems/knuth/local/bdemo_ext.h-sparc b/systems/knuth/local/bdemo_ext.h-sparc
new file mode 100644
index 0000000000..97309edc0a
--- /dev/null
+++ b/systems/knuth/local/bdemo_ext.h-sparc
@@ -0,0 +1,11 @@
+{names of external C routines to include in Pascal binarydemo program}
+
+function testeof(var f:bytefile):boolean; external;
+
+function testreadaccess(var a:UNIXfilename):boolean; external;
+
+function testwriteaccess(var a:UNIXfilename):boolean; external;
+
+function flength(var f:bytefile):integer; external;
+
+procedure movetobyte(var f:bytefile; n:integer); external;
diff --git a/systems/knuth/local/binarydemo.p b/systems/knuth/local/binarydemo.p
new file mode 100644
index 0000000000..2430f9ac00
--- /dev/null
+++ b/systems/knuth/local/binarydemo.p
Binary files differ
diff --git a/systems/knuth/local/binarydemo.p-sparc b/systems/knuth/local/binarydemo.p-sparc
new file mode 100644
index 0000000000..13371e071c
--- /dev/null
+++ b/systems/knuth/local/binarydemo.p-sparc
@@ -0,0 +1,48 @@
+program bdemo(output); {demonstrates binary file I/O in Pascal,
+ using external C routines to help out}
+label 9999;
+const namelength=100;
+type byte=-128..127;
+ bytefile=packed file of byte;
+ UNIXfilename=packed array[1..namelength] of char;
+var fname:UNIXfilename;
+ bfile:bytefile;
+ k,l:integer;
+#include "bdemo_ext.h"
+procedure printstatus(n:integer);
+var x:byte;
+begin if testeof(bfile) then writeln('Byte ',n:1,' is EOF')
+else begin read(bfile,x); writeln('Byte ',n:1,' is ',x:1);
+ end;
+end;
+begin if argc=1 then {no file name argument given on command line}
+ begin writeln('Creating test file btest');
+ fname:='btest';
+ if not testwriteaccess(fname) then
+ begin writeln('I couldn''t create it!'); goto 9999;
+ end;
+ rewrite(bfile,fname);
+ for k:=-128 to 127 do write(bfile,k);
+ end
+else begin argv(1,fname); k:=1;
+ while (k<namelength)and(fname[k]<>' ') do k:=k+1;
+ writeln('Examining file ',fname:k,':');
+ end;
+if not testreadaccess(fname) then
+ begin writeln('I couldn''t read it!'); goto 9999;
+ end;
+reset(bfile,fname);
+l:=flength(bfile);
+writeln('The file is ',l:1,' bytes long');
+printstatus(1);
+if l>0 then
+ begin printstatus(2);
+ if l>1 then
+ begin movetobyte(bfile,l-2); printstatus(l-1);
+ end;
+ movetobyte(bfile,l-1); printstatus(l);
+ printstatus(l+1);
+ movetobyte(bfile,l div 2); printstatus(l div 2 + 1);
+ movetobyte(bfile,1); printstatus(2);
+ end;
+9999:end.
diff --git a/systems/knuth/local/cm/bible12.mf b/systems/knuth/local/cm/bible12.mf
new file mode 100644
index 0000000000..ce74383a7c
--- /dev/null
+++ b/systems/knuth/local/cm/bible12.mf
@@ -0,0 +1,75 @@
+% Computer Modern Bold Extended Text Italic Halfslanted 12 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMBIBL"; font_size 12pt#;
+
+u#:=25/36pt#; % unit width
+width_adj#:=13/36pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=8/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=13/36pt#; % extra space added to all sidebars
+
+body_height#:=324/36pt#; % height of tallest characters
+asc_height#:=300/36pt#; % height of lowercase ascenders
+cap_height#:=296.4/36pt#; % height of caps
+fig_height#:=278.4/36pt#; % height of numerals
+x_height#:=192/36pt#; % height of lowercase without ascenders
+math_axis#:=108/36pt#; % axis of symmetry for math symbols
+bar_height#:=102/36pt#; % height of crossbar in lowercase e
+comma_depth#:=84/36pt#; % depth of comma below baseline
+desc_depth#:=84/36pt#; % depth of lowercase descenders
+
+crisp#:=15/36pt#; % diameter of serif corners
+tiny#:=15/36pt#; % diameter of rounded corners
+fine#:=10/36pt#; % diameter of sharply rounded corners
+thin_join#:=11/36pt#; % width of extrafine details
+hair#:=23/36pt#; % lowercase hairline breadth
+stem#:=44/36pt#; % lowercase stem breadth
+curve#:=48/36pt#; % lowercase curve breadth
+ess#:=41/36pt#; % breadth in middle of lowercase s
+flare#:=45/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=57/36pt#; % diameter of dots
+cap_hair#:=24/36pt#; % uppercase hairline breadth
+cap_stem#:=53/36pt#; % uppercase stem breadth
+cap_curve#:=56/36pt#; % uppercase curve breadth
+cap_ess#:=54/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.66pt#; % thickness of lines in math symbols
+
+dish#:=1/36pt#; % amount erased at top or bottom of serifs
+bracket#:=12/36pt#; % vertical distance from serif base to tangent
+jut#:=33/36pt#; % protrusion of lowercase serifs
+cap_jut#:=46/36pt#; % protrusion of uppercase serifs
+beak_jut#:=12/36pt#; % horizontal protrusion of beak serifs
+beak#:=84/36pt#; % vertical protrusion of beak serifs
+vair#:=16/36pt#; % vertical diameter of hairlines
+notch_cut#:=12pt#; % maximum breadth above or below notches
+bar#:=18.5/36pt#; % lowercase bar thickness
+slab#:=18.5/36pt#; % serif and arm thickness
+cap_bar#:=18.5/36pt#; % uppercase bar thickness
+cap_band#:=18.5/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=12pt#; % max breadth above/below uppercase notches
+serif_drop#:=3.6/36pt#; % vertical drop of sloped serifs
+stem_corr#:=2/36pt#; % for small refinements of stem breadth
+vair_corr#:=1.5/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=6/36pt#; % amount of overshoot for curves
+apex_o#:=6/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/8; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=.4; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/8; % extra openness inside bowls
+beak_darkness:=.4; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate textit % switch to the driver file
diff --git a/systems/knuth/local/cm/cccsc10.mf b/systems/knuth/local/cm/cccsc10.mf
new file mode 100644
index 0000000000..3e82a4e05d
--- /dev/null
+++ b/systems/knuth/local/cm/cccsc10.mf
@@ -0,0 +1,110 @@
+% Concrete Math Roman Caps and Small Caps 10 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCCSC"; font_size 10pt#;
+
+% first come parameters used to set the uppercase letters
+% (including some for lowercase that aren't actually used)
+u#:=21/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=3/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=5/36pt#; % extra space added to all sidebars
+
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+cap_height#:=246/36pt#; % height of caps
+fig_height#:=232/36pt#; % height of numerals
+x_height#:=155/36pt#; % height of lowercase without ascenders
+math_axis#:=90/36pt#; % axis of symmetry for math symbols
+bar_height#:=87/36pt#; % height of crossbar in lowercase e
+comma_depth#:=70/36pt#; % depth of comma below baseline
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=11/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=17/36pt#; % width of extrafine details
+hair#:=21/36pt#; % lowercase hairline breadth
+stem#:=25/36pt#; % lowercase stem breadth
+curve#:=27/36pt#; % lowercase curve breadth
+ess#:=25/36pt#; % breadth in middle of lowercase s
+flare#:=29/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=38/36pt#; % diameter of dots
+cap_hair#:=21/36pt#; % uppercase hairline breadth
+cap_stem#:=27/36pt#; % uppercase stem breadth
+cap_curve#:=28/36pt#; % uppercase curve breadth
+cap_ess#:=27/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.4pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=5/36pt#; % vertical distance from serif base to tangent
+jut#:=30/36pt#; % protrusion of lowercase serifs
+cap_jut#:=32/36pt#; % protrusion of uppercase serifs
+beak_jut#:=10/36pt#; % horizontal protrusion of beak serifs
+beak#:=70/36pt#; % vertical protrusion of beak serifs
+vair#:=21/36pt#; % vertical diameter of hairlines
+notch_cut#:=30/36pt#; % maximum breadth above or below notches
+bar#:=21/36pt#; % lowercase bar thickness
+slab#:=21/36pt#; % serif and arm thickness
+cap_bar#:=21/36pt#; % uppercase bar thickness
+cap_band#:=21/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=1pt#; % max breadth above/below uppercase notches
+serif_drop#:=5/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=0; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=1; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+% now come replacements used to set the lowercase caps
+
+lower.u#:=16/36pt#; % unit width
+lower.width_adj#:=0pt#; % width adjustment for certain characters
+lower.cap_serif_fit#:=2/36pt#; % extra sidebar near uppercase serifs
+lower.letter_fit#:=2.4/36pt#; % extra space added to all sidebars
+
+lower.body_height#:=200/36pt#; % height of tallest characters
+lower.cap_height#:=185/36pt#; % height of caps
+lower.x_height#:=116/36pt#; % height of lowercase without ascenders
+lower.bar_height#:=65/36pt#; % height of crossbar in lowercase e
+lower.comma_depth#:=52/36pt#; % depth of comma below baseline
+
+lower.flare#:=22/36pt#; % diameter of bulbs or breadth of terminals
+lower.cap_hair#:=21/36pt#; % uppercase hairline breadth
+lower.stem#:=23/36pt#; % lowercase stem breadth
+lower.cap_stem#:=24/36pt#; % uppercase stem breadth
+lower.cap_curve#:=26/36pt#; % uppercase curve breadth
+lower.cap_ess#:=24/36pt#; % breadth in middle of uppercase s
+
+lower.cap_jut#:=24/36pt#; % protrusion of uppercase serifs
+lower.beak_jut#:=7/36pt#; % horizontal protrusion of beak serifs
+lower.beak#:=52/36pt#; % vertical protrusion of beak serifs
+lower.slab#:=21/36pt#; % serif and arm thickness
+lower.cap_bar#:=21/36pt#; % uppercase bar thickness
+lower.cap_band#:=21/36pt#; % uppercase thickness above/below lobes
+lower.cap_notch_cut#:=27/36pt#; % max breadth above/below uppercase notches
+
+lower.o#:=3/36pt#; % amount of overshoot for curves
+lower.apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+lower.fudge:=.93; % factor applied to weights of heavy characters
+
+generate csc % switch to the driver file
diff --git a/systems/knuth/local/cm/ccmi10.mf b/systems/knuth/local/cm/ccmi10.mf
new file mode 100644
index 0000000000..58e340fb11
--- /dev/null
+++ b/systems/knuth/local/cm/ccmi10.mf
@@ -0,0 +1,75 @@
+% Concrete Math Italic 10 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCMI"; font_size 10pt#;
+
+u#:=20/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=3/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+cap_height#:=246/36pt#; % height of caps
+fig_height#:=232/36pt#; % height of numerals
+x_height#:=165/36pt#; % height of lowercase without ascenders
+math_axis#:=90/36pt#; % axis of symmetry for math symbols
+bar_height#:=92/36pt#; % height of crossbar in lowercase e
+comma_depth#:=70/36pt#; % depth of comma below baseline
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=11/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=17/36pt#; % width of extrafine details
+hair#:=21/36pt#; % lowercase hairline breadth
+stem#:=25/36pt#; % lowercase stem breadth
+curve#:=27/36pt#; % lowercase curve breadth
+ess#:=25/36pt#; % breadth in middle of lowercase s
+flare#:=29/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=38/36pt#; % diameter of dots
+cap_hair#:=21/36pt#; % uppercase hairline breadth
+cap_stem#:=27/36pt#; % uppercase stem breadth
+cap_curve#:=28/36pt#; % uppercase curve breadth
+cap_ess#:=27/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.4pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=5/36pt#; % vertical distance from serif base to tangent
+jut#:=30/36pt#; % protrusion of lowercase serifs
+cap_jut#:=32/36pt#; % protrusion of uppercase serifs
+beak_jut#:=10/36pt#; % horizontal protrusion of beak serifs
+beak#:=70/36pt#; % vertical protrusion of beak serifs
+vair#:=21/36pt#; % vertical diameter of hairlines
+notch_cut#:=30/36pt#; % maximum breadth above or below notches
+bar#:=21/36pt#; % lowercase bar thickness
+slab#:=21/36pt#; % serif and arm thickness
+cap_bar#:=21/36pt#; % uppercase bar thickness
+cap_band#:=21/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=1pt#; % max breadth above/below uppercase notches
+serif_drop#:=5/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=.25; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=0; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate mathit % switch to the driver file
diff --git a/systems/knuth/local/cm/ccmic9.mf b/systems/knuth/local/cm/ccmic9.mf
new file mode 100644
index 0000000000..91e93d78b9
--- /dev/null
+++ b/systems/knuth/local/cm/ccmic9.mf
@@ -0,0 +1,75 @@
+% Concrete Slanted Condensed Oldstyle Digits 9 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCMIC"; font_size 9pt#;
+
+u#:=16/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=2/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=243/36pt#; % height of tallest characters
+asc_height#:=225/36pt#; % height of lowercase ascenders
+cap_height#:=221.4/36pt#; % height of caps
+fig_height#:=208.8/36pt#; % height of numerals
+x_height#:=155/36pt#; % height of lowercase without ascenders
+math_axis#:=81/36pt#; % axis of symmetry for math symbols
+bar_height#:=85/36pt#; % height of crossbar in lowercase e
+comma_depth#:=63/36pt#; % depth of comma below baseline
+desc_depth#:=63/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=9/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=13/36pt#; % width of extrafine details
+hair#:=16/36pt#; % lowercase hairline breadth
+stem#:=22/36pt#; % lowercase stem breadth
+curve#:=23/36pt#; % lowercase curve breadth
+ess#:=25/36pt#; % breadth in middle of lowercase s
+flare#:=28/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=35/36pt#; % diameter of dots
+cap_hair#:=16/36pt#; % uppercase hairline breadth
+cap_stem#:=23/36pt#; % uppercase stem breadth
+cap_curve#:=24/36pt#; % uppercase curve breadth
+cap_ess#:=23/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.38pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=5/36pt#; % vertical distance from serif base to tangent
+jut#:=15/36pt#; % protrusion of lowercase serifs
+cap_jut#:=16/36pt#; % protrusion of uppercase serifs
+beak_jut#:=9.2/36pt#; % horizontal protrusion of beak serifs
+beak#:=63/36pt#; % vertical protrusion of beak serifs
+vair#:=15/36pt#; % vertical diameter of hairlines
+notch_cut#:=27/36pt#; % maximum breadth above or below notches
+bar#:=15/36pt#; % lowercase bar thickness
+slab#:=15/36pt#; % serif and arm thickness
+cap_bar#:=15/36pt#; % uppercase bar thickness
+cap_band#:=15/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=.9pt#; % max breadth above/below uppercase notches
+serif_drop#:=3.6/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/6; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=.2; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=5/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate odigs % switch to the driver file
diff --git a/systems/knuth/local/cm/ccr10.mf b/systems/knuth/local/cm/ccr10.mf
new file mode 100644
index 0000000000..630a9edf45
--- /dev/null
+++ b/systems/knuth/local/cm/ccr10.mf
@@ -0,0 +1,75 @@
+% Concrete Roman 10 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCR"; font_size 10pt#;
+
+u#:=20/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=3/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+cap_height#:=246/36pt#; % height of caps
+fig_height#:=232/36pt#; % height of numerals
+x_height#:=165/36pt#; % height of lowercase without ascenders
+math_axis#:=90/36pt#; % axis of symmetry for math symbols
+bar_height#:=92/36pt#; % height of crossbar in lowercase e
+comma_depth#:=70/36pt#; % depth of comma below baseline
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=11/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=17/36pt#; % width of extrafine details
+hair#:=21/36pt#; % lowercase hairline breadth
+stem#:=25/36pt#; % lowercase stem breadth
+curve#:=27/36pt#; % lowercase curve breadth
+ess#:=25/36pt#; % breadth in middle of lowercase s
+flare#:=29/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=38/36pt#; % diameter of dots
+cap_hair#:=21/36pt#; % uppercase hairline breadth
+cap_stem#:=27/36pt#; % uppercase stem breadth
+cap_curve#:=28/36pt#; % uppercase curve breadth
+cap_ess#:=27/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.4pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=5/36pt#; % vertical distance from serif base to tangent
+jut#:=30/36pt#; % protrusion of lowercase serifs
+cap_jut#:=32/36pt#; % protrusion of uppercase serifs
+beak_jut#:=10/36pt#; % horizontal protrusion of beak serifs
+beak#:=70/36pt#; % vertical protrusion of beak serifs
+vair#:=21/36pt#; % vertical diameter of hairlines
+notch_cut#:=30/36pt#; % maximum breadth above or below notches
+bar#:=21/36pt#; % lowercase bar thickness
+slab#:=21/36pt#; % serif and arm thickness
+cap_bar#:=21/36pt#; % uppercase bar thickness
+cap_band#:=21/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=1pt#; % max breadth above/below uppercase notches
+serif_drop#:=5/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=0; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/ccr5.mf b/systems/knuth/local/cm/ccr5.mf
new file mode 100644
index 0000000000..f2927069be
--- /dev/null
+++ b/systems/knuth/local/cm/ccr5.mf
@@ -0,0 +1,75 @@
+% Concrete Roman 5 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCR"; font_size 5pt#;
+
+u#:=12.5/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=2/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=5/36pt#; % extra space added to all sidebars
+
+body_height#:=135/36pt#; % height of tallest characters
+asc_height#:=125/36pt#; % height of lowercase ascenders
+cap_height#:=123/36pt#; % height of caps
+fig_height#:=116/36pt#; % height of numerals
+x_height#:=82.5/36pt#; % height of lowercase without ascenders
+math_axis#:=45/36pt#; % axis of symmetry for math symbols
+bar_height#:=43.5/36pt#; % height of crossbar in lowercase e
+comma_depth#:=35/36pt#; % depth of comma below baseline
+desc_depth#:=35/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=6/36pt#; % diameter of rounded corners
+fine#:=5/36pt#; % diameter of sharply rounded corners
+thin_join#:=11/36pt#; % width of extrafine details
+hair#:=14/36pt#; % lowercase hairline breadth
+stem#:=16/36pt#; % lowercase stem breadth
+curve#:=17/36pt#; % lowercase curve breadth
+ess#:=12/36pt#; % breadth in middle of lowercase s
+flare#:=18/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=22/36pt#; % diameter of dots
+cap_hair#:=14/36pt#; % uppercase hairline breadth
+cap_stem#:=17/36pt#; % uppercase stem breadth
+cap_curve#:=18/36pt#; % uppercase curve breadth
+cap_ess#:=14/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.28pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=3/36pt#; % vertical distance from serif base to tangent
+jut#:=17/36pt#; % protrusion of lowercase serifs
+cap_jut#:=18/36pt#; % protrusion of uppercase serifs
+beak_jut#:=6/36pt#; % horizontal protrusion of beak serifs
+beak#:=35/36pt#; % vertical protrusion of beak serifs
+vair#:=14/36pt#; % vertical diameter of hairlines
+notch_cut#:=15/36pt#; % maximum breadth above or below notches
+bar#:=14/36pt#; % lowercase bar thickness
+slab#:=14/36pt#; % serif and arm thickness
+cap_bar#:=14/36pt#; % uppercase bar thickness
+cap_band#:=14/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=.5pt#; % max breadth above/below uppercase notches
+serif_drop#:=2/36pt#; % vertical drop of sloped serifs
+stem_corr#:=.7/36pt#; % for small refinements of stem breadth
+vair_corr#:=.5/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=3/36pt#; % amount of overshoot for curves
+apex_o#:=2/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=1; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=1; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/ccr6.mf b/systems/knuth/local/cm/ccr6.mf
new file mode 100644
index 0000000000..f6d0cebeef
--- /dev/null
+++ b/systems/knuth/local/cm/ccr6.mf
@@ -0,0 +1,75 @@
+% Concrete Roman 6 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCR"; font_size 6pt#;
+
+u#:=14/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=2.2/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=3/36pt#; % extra space added to all sidebars
+
+body_height#:=162/36pt#; % height of tallest characters
+asc_height#:=150/36pt#; % height of lowercase ascenders
+cap_height#:=147.6/36pt#; % height of caps
+fig_height#:=139.2/36pt#; % height of numerals
+x_height#:=99/36pt#; % height of lowercase without ascenders
+math_axis#:=54/36pt#; % axis of symmetry for math symbols
+bar_height#:=52.2/36pt#; % height of crossbar in lowercase e
+comma_depth#:=42/36pt#; % depth of comma below baseline
+desc_depth#:=42/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=7/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=12/36pt#; % width of extrafine details
+hair#:=15/36pt#; % lowercase hairline breadth
+stem#:=18/36pt#; % lowercase stem breadth
+curve#:=19/36pt#; % lowercase curve breadth
+ess#:=17/36pt#; % breadth in middle of lowercase s
+flare#:=20/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=26/36pt#; % diameter of dots
+cap_hair#:=15/36pt#; % uppercase hairline breadth
+cap_stem#:=19/36pt#; % uppercase stem breadth
+cap_curve#:=20/36pt#; % uppercase curve breadth
+cap_ess#:=19/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.31pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=3/36pt#; % vertical distance from serif base to tangent
+jut#:=19/36pt#; % protrusion of lowercase serifs
+cap_jut#:=20/36pt#; % protrusion of uppercase serifs
+beak_jut#:=6.8/36pt#; % horizontal protrusion of beak serifs
+beak#:=42/36pt#; % vertical protrusion of beak serifs
+vair#:=15/36pt#; % vertical diameter of hairlines
+notch_cut#:=18/36pt#; % maximum breadth above or below notches
+bar#:=15/36pt#; % lowercase bar thickness
+slab#:=15/36pt#; % serif and arm thickness
+cap_bar#:=15/36pt#; % uppercase bar thickness
+cap_band#:=15/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=.6pt#; % max breadth above/below uppercase notches
+serif_drop#:=2.4/36pt#; % vertical drop of sloped serifs
+stem_corr#:=.8/36pt#; % for small refinements of stem breadth
+vair_corr#:=.7/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=3/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=.8; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/ccr7.mf b/systems/knuth/local/cm/ccr7.mf
new file mode 100644
index 0000000000..8d6255249e
--- /dev/null
+++ b/systems/knuth/local/cm/ccr7.mf
@@ -0,0 +1,75 @@
+% Concrete Roman 7 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCR"; font_size 7pt#;
+
+u#:=15.5/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=2.4/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=2/36pt#; % extra space added to all sidebars
+
+body_height#:=189/36pt#; % height of tallest characters
+asc_height#:=175/36pt#; % height of lowercase ascenders
+cap_height#:=172.2/36pt#; % height of caps
+fig_height#:=162.4/36pt#; % height of numerals
+x_height#:=115.5/36pt#; % height of lowercase without ascenders
+math_axis#:=63/36pt#; % axis of symmetry for math symbols
+bar_height#:=60.9/36pt#; % height of crossbar in lowercase e
+comma_depth#:=49/36pt#; % depth of comma below baseline
+desc_depth#:=49/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=8/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=13/36pt#; % width of extrafine details
+hair#:=17/36pt#; % lowercase hairline breadth
+stem#:=20/36pt#; % lowercase stem breadth
+curve#:=21.5/36pt#; % lowercase curve breadth
+ess#:=20/36pt#; % breadth in middle of lowercase s
+flare#:=22/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=29/36pt#; % diameter of dots
+cap_hair#:=17/36pt#; % uppercase hairline breadth
+cap_stem#:=21.5/36pt#; % uppercase stem breadth
+cap_curve#:=22.5/36pt#; % uppercase curve breadth
+cap_ess#:=21.5/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.34pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=4/36pt#; % vertical distance from serif base to tangent
+jut#:=21/36pt#; % protrusion of lowercase serifs
+cap_jut#:=23/36pt#; % protrusion of uppercase serifs
+beak_jut#:=7.6/36pt#; % horizontal protrusion of beak serifs
+beak#:=49/36pt#; % vertical protrusion of beak serifs
+vair#:=17/36pt#; % vertical diameter of hairlines
+notch_cut#:=21/36pt#; % maximum breadth above or below notches
+bar#:=17/36pt#; % lowercase bar thickness
+slab#:=17/36pt#; % serif and arm thickness
+cap_bar#:=17/36pt#; % uppercase bar thickness
+cap_band#:=17/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=.7pt#; % max breadth above/below uppercase notches
+serif_drop#:=2.8/36pt#; % vertical drop of sloped serifs
+stem_corr#:=.9/36pt#; % for small refinements of stem breadth
+vair_corr#:=.9/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=3/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=.6; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/ccr8.mf b/systems/knuth/local/cm/ccr8.mf
new file mode 100644
index 0000000000..4b78beb62e
--- /dev/null
+++ b/systems/knuth/local/cm/ccr8.mf
@@ -0,0 +1,75 @@
+% Concrete Roman 8 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCR"; font_size 8pt#;
+
+u#:=17/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=2.6/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=216/36pt#; % height of tallest characters
+asc_height#:=200/36pt#; % height of lowercase ascenders
+cap_height#:=196.8/36pt#; % height of caps
+fig_height#:=185.6/36pt#; % height of numerals
+x_height#:=132/36pt#; % height of lowercase without ascenders
+math_axis#:=72/36pt#; % axis of symmetry for math symbols
+bar_height#:=69.6/36pt#; % height of crossbar in lowercase e
+comma_depth#:=56/36pt#; % depth of comma below baseline
+desc_depth#:=56/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=9/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=15/36pt#; % width of extrafine details
+hair#:=19/36pt#; % lowercase hairline breadth
+stem#:=22/36pt#; % lowercase stem breadth
+curve#:=24/36pt#; % lowercase curve breadth
+ess#:=22/36pt#; % breadth in middle of lowercase s
+flare#:=24/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=32/36pt#; % diameter of dots
+cap_hair#:=19/36pt#; % uppercase hairline breadth
+cap_stem#:=24/36pt#; % uppercase stem breadth
+cap_curve#:=25/36pt#; % uppercase curve breadth
+cap_ess#:=24/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.36pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=4/36pt#; % vertical distance from serif base to tangent
+jut#:=24/36pt#; % protrusion of lowercase serifs
+cap_jut#:=26/36pt#; % protrusion of uppercase serifs
+beak_jut#:=8.4/36pt#; % horizontal protrusion of beak serifs
+beak#:=56/36pt#; % vertical protrusion of beak serifs
+vair#:=19/36pt#; % vertical diameter of hairlines
+notch_cut#:=24/36pt#; % maximum breadth above or below notches
+bar#:=19/36pt#; % lowercase bar thickness
+slab#:=19/36pt#; % serif and arm thickness
+cap_bar#:=19/36pt#; % uppercase bar thickness
+cap_band#:=19/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=.8pt#; % max breadth above/below uppercase notches
+serif_drop#:=3.2/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=3/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=.4; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/ccr9.mf b/systems/knuth/local/cm/ccr9.mf
new file mode 100644
index 0000000000..03ca68075a
--- /dev/null
+++ b/systems/knuth/local/cm/ccr9.mf
@@ -0,0 +1,75 @@
+% Concrete Roman 9 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCR"; font_size 9pt#;
+
+u#:=18.5/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=2.8/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=243/36pt#; % height of tallest characters
+asc_height#:=225/36pt#; % height of lowercase ascenders
+cap_height#:=221.4/36pt#; % height of caps
+fig_height#:=208.8/36pt#; % height of numerals
+x_height#:=148.5/36pt#; % height of lowercase without ascenders
+math_axis#:=81/36pt#; % axis of symmetry for math symbols
+bar_height#:=78.3/36pt#; % height of crossbar in lowercase e
+comma_depth#:=63/36pt#; % depth of comma below baseline
+desc_depth#:=63/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=10/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=17/36pt#; % width of extrafine details
+hair#:=20/36pt#; % lowercase hairline breadth
+stem#:=24/36pt#; % lowercase stem breadth
+curve#:=26/36pt#; % lowercase curve breadth
+ess#:=24/36pt#; % breadth in middle of lowercase s
+flare#:=26/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=35/36pt#; % diameter of dots
+cap_hair#:=20/36pt#; % uppercase hairline breadth
+cap_stem#:=26/36pt#; % uppercase stem breadth
+cap_curve#:=27/36pt#; % uppercase curve breadth
+cap_ess#:=24/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.38pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=5/36pt#; % vertical distance from serif base to tangent
+jut#:=27/36pt#; % protrusion of lowercase serifs
+cap_jut#:=29/36pt#; % protrusion of uppercase serifs
+beak_jut#:=9.2/36pt#; % horizontal protrusion of beak serifs
+beak#:=63/36pt#; % vertical protrusion of beak serifs
+vair#:=20/36pt#; % vertical diameter of hairlines
+notch_cut#:=27/36pt#; % maximum breadth above or below notches
+bar#:=20/36pt#; % lowercase bar thickness
+slab#:=20/36pt#; % serif and arm thickness
+cap_bar#:=20/36pt#; % uppercase bar thickness
+cap_band#:=20/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=.9pt#; % max breadth above/below uppercase notches
+serif_drop#:=3.6/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=.2; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/ccsl10.mf b/systems/knuth/local/cm/ccsl10.mf
new file mode 100644
index 0000000000..9adc55ef9d
--- /dev/null
+++ b/systems/knuth/local/cm/ccsl10.mf
@@ -0,0 +1,75 @@
+% Concrete Slanted Roman 10 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCSL"; font_size 10pt#;
+
+u#:=20/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=3/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+cap_height#:=246/36pt#; % height of caps
+fig_height#:=232/36pt#; % height of numerals
+x_height#:=165/36pt#; % height of lowercase without ascenders
+math_axis#:=90/36pt#; % axis of symmetry for math symbols
+bar_height#:=92/36pt#; % height of crossbar in lowercase e
+comma_depth#:=70/36pt#; % depth of comma below baseline
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=11/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=17/36pt#; % width of extrafine details
+hair#:=21/36pt#; % lowercase hairline breadth
+stem#:=25/36pt#; % lowercase stem breadth
+curve#:=27/36pt#; % lowercase curve breadth
+ess#:=25/36pt#; % breadth in middle of lowercase s
+flare#:=29/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=38/36pt#; % diameter of dots
+cap_hair#:=21/36pt#; % uppercase hairline breadth
+cap_stem#:=27/36pt#; % uppercase stem breadth
+cap_curve#:=28/36pt#; % uppercase curve breadth
+cap_ess#:=27/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.4pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=5/36pt#; % vertical distance from serif base to tangent
+jut#:=30/36pt#; % protrusion of lowercase serifs
+cap_jut#:=32/36pt#; % protrusion of uppercase serifs
+beak_jut#:=10/36pt#; % horizontal protrusion of beak serifs
+beak#:=70/36pt#; % vertical protrusion of beak serifs
+vair#:=21/36pt#; % vertical diameter of hairlines
+notch_cut#:=30/36pt#; % maximum breadth above or below notches
+bar#:=21/36pt#; % lowercase bar thickness
+slab#:=21/36pt#; % serif and arm thickness
+cap_bar#:=21/36pt#; % uppercase bar thickness
+cap_band#:=21/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=1pt#; % max breadth above/below uppercase notches
+serif_drop#:=5/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/6; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=0; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/ccsl9.mf b/systems/knuth/local/cm/ccsl9.mf
new file mode 100644
index 0000000000..d848ad9758
--- /dev/null
+++ b/systems/knuth/local/cm/ccsl9.mf
@@ -0,0 +1,75 @@
+% Concrete Slanted Roman 9 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCSL"; font_size 9pt#;
+
+u#:=18.5/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=2.8/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=243/36pt#; % height of tallest characters
+asc_height#:=225/36pt#; % height of lowercase ascenders
+cap_height#:=221.4/36pt#; % height of caps
+fig_height#:=208.8/36pt#; % height of numerals
+x_height#:=148.5/36pt#; % height of lowercase without ascenders
+math_axis#:=81/36pt#; % axis of symmetry for math symbols
+bar_height#:=78.3/36pt#; % height of crossbar in lowercase e
+comma_depth#:=63/36pt#; % depth of comma below baseline
+desc_depth#:=63/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=10/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=17/36pt#; % width of extrafine details
+hair#:=20/36pt#; % lowercase hairline breadth
+stem#:=24/36pt#; % lowercase stem breadth
+curve#:=26/36pt#; % lowercase curve breadth
+ess#:=24/36pt#; % breadth in middle of lowercase s
+flare#:=26/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=35/36pt#; % diameter of dots
+cap_hair#:=20/36pt#; % uppercase hairline breadth
+cap_stem#:=26/36pt#; % uppercase stem breadth
+cap_curve#:=27/36pt#; % uppercase curve breadth
+cap_ess#:=24/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.38pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=5/36pt#; % vertical distance from serif base to tangent
+jut#:=27/36pt#; % protrusion of lowercase serifs
+cap_jut#:=29/36pt#; % protrusion of uppercase serifs
+beak_jut#:=9.2/36pt#; % horizontal protrusion of beak serifs
+beak#:=63/36pt#; % vertical protrusion of beak serifs
+vair#:=20/36pt#; % vertical diameter of hairlines
+notch_cut#:=27/36pt#; % maximum breadth above or below notches
+bar#:=20/36pt#; % lowercase bar thickness
+slab#:=20/36pt#; % serif and arm thickness
+cap_bar#:=20/36pt#; % uppercase bar thickness
+cap_band#:=20/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=.9pt#; % max breadth above/below uppercase notches
+serif_drop#:=3.6/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/6; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=.2; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/ccslc9.mf b/systems/knuth/local/cm/ccslc9.mf
new file mode 100644
index 0000000000..4f61534e7c
--- /dev/null
+++ b/systems/knuth/local/cm/ccslc9.mf
@@ -0,0 +1,75 @@
+% Concrete Slanted Condensed Roman 9 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCSLC"; font_size 9pt#;
+
+u#:=16/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=2/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=243/36pt#; % height of tallest characters
+asc_height#:=225/36pt#; % height of lowercase ascenders
+cap_height#:=221.4/36pt#; % height of caps
+fig_height#:=208.8/36pt#; % height of numerals
+x_height#:=155/36pt#; % height of lowercase without ascenders
+math_axis#:=81/36pt#; % axis of symmetry for math symbols
+bar_height#:=85/36pt#; % height of crossbar in lowercase e
+comma_depth#:=63/36pt#; % depth of comma below baseline
+desc_depth#:=63/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=9/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=13/36pt#; % width of extrafine details
+hair#:=16/36pt#; % lowercase hairline breadth
+stem#:=22/36pt#; % lowercase stem breadth
+curve#:=23/36pt#; % lowercase curve breadth
+ess#:=25/36pt#; % breadth in middle of lowercase s
+flare#:=28/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=35/36pt#; % diameter of dots
+cap_hair#:=16/36pt#; % uppercase hairline breadth
+cap_stem#:=23/36pt#; % uppercase stem breadth
+cap_curve#:=24/36pt#; % uppercase curve breadth
+cap_ess#:=23/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.38pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=5/36pt#; % vertical distance from serif base to tangent
+jut#:=15/36pt#; % protrusion of lowercase serifs
+cap_jut#:=16/36pt#; % protrusion of uppercase serifs
+beak_jut#:=9.2/36pt#; % horizontal protrusion of beak serifs
+beak#:=63/36pt#; % vertical protrusion of beak serifs
+vair#:=15/36pt#; % vertical diameter of hairlines
+notch_cut#:=27/36pt#; % maximum breadth above or below notches
+bar#:=15/36pt#; % lowercase bar thickness
+slab#:=15/36pt#; % serif and arm thickness
+cap_bar#:=15/36pt#; % uppercase bar thickness
+cap_band#:=15/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=.9pt#; % max breadth above/below uppercase notches
+serif_drop#:=3.6/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/6; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=.2; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=5/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/ccti10.mf b/systems/knuth/local/cm/ccti10.mf
new file mode 100644
index 0000000000..2b2bc504e5
--- /dev/null
+++ b/systems/knuth/local/cm/ccti10.mf
@@ -0,0 +1,75 @@
+% Concrete Text Italic 10 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CCTI"; font_size 10pt#;
+
+u#:=20/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=1/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=3/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=9/36pt#; % extra space added to all sidebars
+
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+cap_height#:=246/36pt#; % height of caps
+fig_height#:=232/36pt#; % height of numerals
+x_height#:=165/36pt#; % height of lowercase without ascenders
+math_axis#:=90/36pt#; % axis of symmetry for math symbols
+bar_height#:=92/36pt#; % height of crossbar in lowercase e
+comma_depth#:=70/36pt#; % depth of comma below baseline
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+
+crisp#:=11/36pt#; % diameter of serif corners
+tiny#:=11/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=17/36pt#; % width of extrafine details
+hair#:=21/36pt#; % lowercase hairline breadth
+stem#:=24/36pt#; % lowercase stem breadth
+curve#:=26/36pt#; % lowercase curve breadth
+ess#:=24/36pt#; % breadth in middle of lowercase s
+flare#:=28/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=38/36pt#; % diameter of dots
+cap_hair#:=21/36pt#; % uppercase hairline breadth
+cap_stem#:=26/36pt#; % uppercase stem breadth
+cap_curve#:=27/36pt#; % uppercase curve breadth
+cap_ess#:=26/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.4pt#; % thickness of lines in math symbols
+
+dish#:=0/36pt#; % amount erased at top or bottom of serifs
+bracket#:=5/36pt#; % vertical distance from serif base to tangent
+jut#:=30/36pt#; % protrusion of lowercase serifs
+cap_jut#:=32/36pt#; % protrusion of uppercase serifs
+beak_jut#:=10/36pt#; % horizontal protrusion of beak serifs
+beak#:=70/36pt#; % vertical protrusion of beak serifs
+vair#:=21/36pt#; % vertical diameter of hairlines
+notch_cut#:=30/36pt#; % maximum breadth above or below notches
+bar#:=21/36pt#; % lowercase bar thickness
+slab#:=21/36pt#; % serif and arm thickness
+cap_bar#:=21/36pt#; % uppercase bar thickness
+cap_band#:=21/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=1pt#; % max breadth above/below uppercase notches
+serif_drop#:=5/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=.25; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.95; % factor applied to weights of heavy characters
+math_spread:=0; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/15; % extra openness inside bowls
+beak_darkness:=4/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate textit % switch to the driver file
diff --git a/systems/knuth/local/cm/cmbxcd10.mf b/systems/knuth/local/cm/cmbxcd10.mf
new file mode 100644
index 0000000000..cd1f0f0b0e
--- /dev/null
+++ b/systems/knuth/local/cm/cmbxcd10.mf
@@ -0,0 +1,75 @@
+% Computer Modern Bold Extended Roman 10 point, Caps and Digits Only
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMBXCD"; font_size 10pt#;
+
+u#:=23/36pt#; % unit width
+width_adj#:=11/36pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=7/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+cap_height#:=247/36pt#; % height of caps
+fig_height#:=232/36pt#; % height of numerals
+x_height#:=160/36pt#; % height of lowercase without ascenders
+math_axis#:=90/36pt#; % axis of symmetry for math symbols
+bar_height#:=85/36pt#; % height of crossbar in lowercase e
+comma_depth#:=70/36pt#; % depth of comma below baseline
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=13/36pt#; % diameter of rounded corners
+fine#:=10/36pt#; % diameter of sharply rounded corners
+thin_join#:=10/36pt#; % width of extrafine details
+hair#:=17/36pt#; % lowercase hairline breadth
+stem#:=41/36pt#; % lowercase stem breadth
+curve#:=46/36pt#; % lowercase curve breadth
+ess#:=38/36pt#; % breadth in middle of lowercase s
+flare#:=45/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=56/36pt#; % diameter of dots
+cap_hair#:=19/36pt#; % uppercase hairline breadth
+cap_stem#:=51/36pt#; % uppercase stem breadth
+cap_curve#:=55/36pt#; % uppercase curve breadth
+cap_ess#:=53/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.6pt#; % thickness of lines in math symbols
+
+dish#:=1/36pt#; % amount erased at top or bottom of serifs
+bracket#:=10/36pt#; % vertical distance from serif base to tangent
+jut#:=25/36pt#; % protrusion of lowercase serifs
+cap_jut#:=39/36pt#; % protrusion of uppercase serifs
+beak_jut#:=11/36pt#; % horizontal protrusion of beak serifs
+beak#:=70/36pt#; % vertical protrusion of beak serifs
+vair#:=13/36pt#; % vertical diameter of hairlines
+notch_cut#:=10pt#; % maximum breadth above or below notches
+bar#:=17/36pt#; % lowercase bar thickness
+slab#:=17/36pt#; % serif and arm thickness
+cap_bar#:=17/36pt#; % uppercase bar thickness
+cap_band#:=17/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=10pt#; % max breadth above/below uppercase notches
+serif_drop#:=3/36pt#; % vertical drop of sloped serifs
+stem_corr#:=2/36pt#; % for small refinements of stem breadth
+vair_corr#:=1.5/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=.5; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/8; % extra openness inside bowls
+beak_darkness:=.4; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate title % switch to the driver file
diff --git a/systems/knuth/local/cm/cmbxti12.mf b/systems/knuth/local/cm/cmbxti12.mf
new file mode 100644
index 0000000000..299c83ad67
--- /dev/null
+++ b/systems/knuth/local/cm/cmbxti12.mf
@@ -0,0 +1,75 @@
+% Computer Modern Bold Extended Text Italic 12 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMBXTI"; font_size 12pt#;
+
+u#:=24.9/36pt#; % unit width
+width_adj#:=13/36pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=8/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=12.5/36pt#; % extra space added to all sidebars
+
+body_height#:=324/36pt#; % height of tallest characters
+asc_height#:=300/36pt#; % height of lowercase ascenders
+cap_height#:=296.4/36pt#; % height of caps
+fig_height#:=278.8/36pt#; % height of numerals
+x_height#:=192/36pt#; % height of lowercase without ascenders
+math_axis#:=108/36pt#; % axis of symmetry for math symbols
+bar_height#:=102/36pt#; % height of crossbar in lowercase e
+comma_depth#:=84/36pt#; % depth of comma below baseline
+desc_depth#:=84/36pt#; % depth of lowercase descenders
+
+crisp#:=13/36pt#; % diameter of serif corners
+tiny#:=13/36pt#; % diameter of rounded corners
+fine#:=10/36pt#; % diameter of sharply rounded corners
+thin_join#:=10.5/36pt#; % width of extrafine details
+hair#:=21.5/36pt#; % lowercase hairline breadth
+stem#:=44/36pt#; % lowercase stem breadth
+curve#:=49/36pt#; % lowercase curve breadth
+ess#:=41/36pt#; % breadth in middle of lowercase s
+flare#:=48/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=59/36pt#; % diameter of dots
+cap_hair#:=23/36pt#; % uppercase hairline breadth
+cap_stem#:=55/36pt#; % uppercase stem breadth
+cap_curve#:=59/36pt#; % uppercase curve breadth
+cap_ess#:=54/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.66pt#; % thickness of lines in math symbols
+
+dish#:=1/36pt#; % amount erased at top or bottom of serifs
+bracket#:=12/36pt#; % vertical distance from serif base to tangent
+jut#:=32/36pt#; % protrusion of lowercase serifs
+cap_jut#:=46/36pt#; % protrusion of uppercase serifs
+beak_jut#:=11.3/36pt#; % horizontal protrusion of beak serifs
+beak#:=84/36pt#; % vertical protrusion of beak serifs
+vair#:=14.3/36pt#; % vertical diameter of hairlines
+notch_cut#:=12pt#; % maximum breadth above or below notches
+bar#:=18.5/36pt#; % lowercase bar thickness
+slab#:=18.5/36pt#; % serif and arm thickness
+cap_bar#:=18.5/36pt#; % uppercase bar thickness
+cap_band#:=18.5/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=12pt#; % max breadth above/below uppercase notches
+serif_drop#:=3.6/36pt#; % vertical drop of sloped serifs
+stem_corr#:=2/36pt#; % for small refinements of stem breadth
+vair_corr#:=1.5/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=7/36pt#; % amount of overshoot for curves
+apex_o#:=7/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=.25; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=.4; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/8; % extra openness inside bowls
+beak_darkness:=.4; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate textit % switch to the driver file
diff --git a/systems/knuth/local/cm/cmbxti7.mf b/systems/knuth/local/cm/cmbxti7.mf
new file mode 100644
index 0000000000..984c903eed
--- /dev/null
+++ b/systems/knuth/local/cm/cmbxti7.mf
@@ -0,0 +1,75 @@
+% Computer Modern Bold Extended Text Italic 7 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMBXTI"; font_size 7pt#;
+
+u#:=16.5/36pt#; % unit width
+width_adj#:=8.6/36pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=4.6/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=9/36pt#; % extra space added to all sidebars
+
+body_height#:=189/36pt#; % height of tallest characters
+asc_height#:=175/36pt#; % height of lowercase ascenders
+cap_height#:=172.9/36pt#; % height of caps
+fig_height#:=162.4/36pt#; % height of numerals
+x_height#:=112/36pt#; % height of lowercase without ascenders
+math_axis#:=63/36pt#; % axis of symmetry for math symbols
+bar_height#:=58/36pt#; % height of crossbar in lowercase e
+comma_depth#:=49/36pt#; % depth of comma below baseline
+desc_depth#:=49/36pt#; % depth of lowercase descenders
+
+crisp#:=11/36pt#; % diameter of serif corners
+tiny#:=11/36pt#; % diameter of rounded corners
+fine#:=9/36pt#; % diameter of sharply rounded corners
+thin_join#:=9/36pt#; % width of extrafine details
+hair#:=17.8/36pt#; % lowercase hairline breadth
+stem#:=30/36pt#; % lowercase stem breadth
+curve#:=33/36pt#; % lowercase curve breadth
+ess#:=27/36pt#; % breadth in middle of lowercase s
+flare#:=33/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=39/36pt#; % diameter of dots
+cap_hair#:=20/36pt#; % uppercase hairline breadth
+cap_stem#:=36/36pt#; % uppercase stem breadth
+cap_curve#:=39/36pt#; % uppercase curve breadth
+cap_ess#:=34/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.51pt#; % thickness of lines in math symbols
+
+dish#:=.9/36pt#; % amount erased at top or bottom of serifs
+bracket#:=7/36pt#; % vertical distance from serif base to tangent
+jut#:=19/36pt#; % protrusion of lowercase serifs
+cap_jut#:=46/36pt#; % protrusion of uppercase serifs
+beak_jut#:=7.7/36pt#; % horizontal protrusion of beak serifs
+beak#:=84/36pt#; % vertical protrusion of beak serifs
+vair#:=11.5/36pt#; % vertical diameter of hairlines
+notch_cut#:=7pt#; % maximum breadth above or below notches
+bar#:=14/36pt#; % lowercase bar thickness
+slab#:=14/36pt#; % serif and arm thickness
+cap_bar#:=14/36pt#; % uppercase bar thickness
+cap_band#:=14/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=7pt#; % max breadth above/below uppercase notches
+serif_drop#:=2.1/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1.7/36pt#; % for small refinements of stem breadth
+vair_corr#:=1.2/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=4/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=.25; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=1.1; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/8; % extra openness inside bowls
+beak_darkness:=.4; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate textit % switch to the driver file
diff --git a/systems/knuth/local/cm/cmcscsl10.mf b/systems/knuth/local/cm/cmcscsl10.mf
new file mode 100644
index 0000000000..8c6280bb67
--- /dev/null
+++ b/systems/knuth/local/cm/cmcscsl10.mf
@@ -0,0 +1,110 @@
+% Computer Modern Roman Caps and Small Caps Slanted 10 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMCSCSL"; font_size 10pt#;
+
+% first come parameters used to set the uppercase letters
+% (including some for lowercase that aren't actually used)
+u#:=21/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=5/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=5/36pt#; % extra space added to all sidebars
+
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+cap_height#:=246/36pt#; % height of caps
+fig_height#:=232/36pt#; % height of numerals
+x_height#:=155/36pt#; % height of lowercase without ascenders
+math_axis#:=90/36pt#; % axis of symmetry for math symbols
+bar_height#:=87/36pt#; % height of crossbar in lowercase e
+comma_depth#:=70/36pt#; % depth of comma below baseline
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=8/36pt#; % diameter of rounded corners
+fine#:=7/36pt#; % diameter of sharply rounded corners
+thin_join#:=7/36pt#; % width of extrafine details
+hair#:=9/36pt#; % lowercase hairline breadth
+stem#:=25/36pt#; % lowercase stem breadth
+curve#:=30/36pt#; % lowercase curve breadth
+ess#:=27/36pt#; % breadth in middle of lowercase s
+flare#:=33/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=38/36pt#; % diameter of dots
+cap_hair#:=11/36pt#; % uppercase hairline breadth
+cap_stem#:=32/36pt#; % uppercase stem breadth
+cap_curve#:=37/36pt#; % uppercase curve breadth
+cap_ess#:=35/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.4pt#; % thickness of lines in math symbols
+
+dish#:=1/36pt#; % amount erased at top or bottom of serifs
+bracket#:=20/36pt#; % vertical distance from serif base to tangent
+jut#:=28/36pt#; % protrusion of lowercase serifs
+cap_jut#:=37/36pt#; % protrusion of uppercase serifs
+beak_jut#:=10/36pt#; % horizontal protrusion of beak serifs
+beak#:=70/36pt#; % vertical protrusion of beak serifs
+vair#:=8/36pt#; % vertical diameter of hairlines
+notch_cut#:=10pt#; % maximum breadth above or below notches
+bar#:=11/36pt#; % lowercase bar thickness
+slab#:=11/36pt#; % serif and arm thickness
+cap_bar#:=11/36pt#; % uppercase bar thickness
+cap_band#:=11/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=10pt#; % max breadth above/below uppercase notches
+serif_drop#:=4/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=8/36pt#; % amount of overshoot for curves
+apex_o#:=8/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/6; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=0; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=1/6; % extra openness inside bowls
+beak_darkness:=11/30; % fraction of triangle inside beak serifs
+ligs:=1; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+% now come replacements used to set the lowercase caps
+
+lower.u#:=16/36pt#; % unit width
+lower.width_adj#:=0pt#; % width adjustment for certain characters
+lower.cap_serif_fit#:=4/36pt#; % extra sidebar near uppercase serifs
+lower.letter_fit#:=2.4/36pt#; % extra space added to all sidebars
+
+lower.body_height#:=200/36pt#; % height of tallest characters
+lower.cap_height#:=185/36pt#; % height of caps
+lower.x_height#:=116/36pt#; % height of lowercase without ascenders
+lower.bar_height#:=65/36pt#; % height of crossbar in lowercase e
+lower.comma_depth#:=52/36pt#; % depth of comma below baseline
+
+lower.flare#:=25/36pt#; % diameter of bulbs or breadth of terminals
+lower.cap_hair#:=9.1/36pt#; % uppercase hairline breadth
+lower.stem#:=21/36pt#; % lowercase stem breadth
+lower.cap_stem#:=26/36pt#; % uppercase stem breadth
+lower.cap_curve#:=31/36pt#; % uppercase curve breadth
+lower.cap_ess#:=28/36pt#; % breadth in middle of uppercase s
+
+lower.cap_jut#:=26/36pt#; % protrusion of uppercase serifs
+lower.beak_jut#:=7/36pt#; % horizontal protrusion of beak serifs
+lower.beak#:=52/36pt#; % vertical protrusion of beak serifs
+lower.slab#:=9.1/36pt#; % serif and arm thickness
+lower.cap_bar#:=9.1/36pt#; % uppercase bar thickness
+lower.cap_band#:=9.1/36pt#; % uppercase thickness above/below lobes
+lower.cap_notch_cut#:=10pt#; % max breadth above/below uppercase notches
+
+lower.o#:=5/36pt#; % amount of overshoot for curves
+lower.apex_o#:=4/36pt#; % amount of overshoot for diagonal junctions
+
+lower.fudge:=1; % factor applied to weights of heavy characters
+
+generate csc % switch to the driver file
diff --git a/systems/knuth/local/cm/cmfibs8.mf b/systems/knuth/local/cm/cmfibs8.mf
new file mode 100644
index 0000000000..f4b9c09ba2
--- /dev/null
+++ b/systems/knuth/local/cm/cmfibs8.mf
@@ -0,0 +1,75 @@
+% Computer Modern Roman Fibonacci Slanted Font
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMFIBS"; font_size 8pt#;
+
+u#:=21/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=-2/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=0pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=233/36pt#; % height of tallest characters
+asc_height#:=233/36pt#; % height of lowercase ascenders
+cap_height#:=233/36pt#; % height of caps
+fig_height#:=233/36pt#; % height of numerals
+x_height#:=144/36pt#; % height of lowercase without ascenders
+math_axis#:=89/36pt#; % axis of symmetry for math symbols
+bar_height#:=89/36pt#; % height of crossbar in lowercase e
+comma_depth#:=55/36pt#; % depth of comma below baseline
+desc_depth#:=55/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=13/36pt#; % diameter of rounded corners
+fine#:=13/36pt#; % diameter of sharply rounded corners
+thin_join#:=13/36pt#; % width of extrafine details
+hair#:=21/36pt#; % lowercase hairline breadth
+stem#:=34/36pt#; % lowercase stem breadth
+curve#:=39/36pt#; % lowercase curve breadth
+ess#:=34/36pt#; % breadth in middle of lowercase s
+flare#:=34/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=34/36pt#; % diameter of dots
+cap_hair#:=21/36pt#; % uppercase hairline breadth
+cap_stem#:=34/36pt#; % uppercase stem breadth
+cap_curve#:=39/36pt#; % uppercase curve breadth
+cap_ess#:=34/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=21/36pt#; % thickness of lines in math symbols
+
+dish#:=1/36pt#; % amount erased at top or bottom of serifs
+bracket#:=21/36pt#; % vertical distance from serif base to tangent
+jut#:=21/36pt#; % protrusion of lowercase serifs
+cap_jut#:=21/36pt#; % protrusion of uppercase serifs
+beak_jut#:=13/36pt#; % horizontal protrusion of beak serifs
+beak#:=55/36pt#; % vertical protrusion of beak serifs
+vair#:=13/36pt#; % vertical diameter of hairlines
+notch_cut#:=8pt#; % maximum breadth above or below notches
+bar#:=13/36pt#; % lowercase bar thickness
+slab#:=13/36pt#; % serif and arm thickness
+cap_bar#:=13/36pt#; % uppercase bar thickness
+cap_band#:=13/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=8pt#; % max breadth above/below uppercase notches
+serif_drop#:=3/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=8/36pt#; % amount of overshoot for curves
+apex_o#:=8/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=sqrt5-2; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=3/8; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=1/13; % extra openness inside bowls
+beak_darkness:=5/13; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/cmitt12.mf b/systems/knuth/local/cm/cmitt12.mf
new file mode 100644
index 0000000000..44a3841c0d
--- /dev/null
+++ b/systems/knuth/local/cm/cmitt12.mf
@@ -0,0 +1,75 @@
+% Computer Modern Italic Typewriter Text for use with 12 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMITT"; font_size 12pt#;
+
+u#:=24.7/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=0pt#; % extra sidebar near uppercase serifs
+letter_fit#:=18/36pt#; % extra space added to all sidebars
+
+body_height#:=300/36pt#; % height of tallest characters
+asc_height#:=264/36pt#; % height of lowercase ascenders
+cap_height#:=264/36pt#; % height of caps
+fig_height#:=264/36pt#; % height of numerals
+x_height#:=186/36pt#; % height of lowercase without ascenders
+math_axis#:=132/36pt#; % axis of symmetry for math symbols
+bar_height#:=95/36pt#; % height of crossbar in lowercase e
+comma_depth#:=60/36pt#; % depth of comma below baseline
+desc_depth#:=96/36pt#; % depth of lowercase descenders
+
+crisp#:=25/36pt#; % diameter of serif corners
+tiny#:=25/36pt#; % diameter of rounded corners
+fine#:=22/36pt#; % diameter of sharply rounded corners
+thin_join#:=22/36pt#; % width of extrafine details
+hair#:=28/36pt#; % lowercase hairline breadth
+stem#:=28/36pt#; % lowercase stem breadth
+curve#:=28/36pt#; % lowercase curve breadth
+ess#:=25/36pt#; % breadth in middle of lowercase s
+flare#:=35/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=39/36pt#; % diameter of dots
+cap_hair#:=28/36pt#; % uppercase hairline breadth
+cap_stem#:=28/36pt#; % uppercase stem breadth
+cap_curve#:=28/36pt#; % uppercase curve breadth
+cap_ess#:=28/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=28/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=46/36pt#; % protrusion of lowercase serifs
+cap_jut#:=39/36pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=39/36pt#; % vertical protrusion of beak serifs
+vair#:=25/36pt#; % vertical diameter of hairlines
+notch_cut#:=28/36pt#; % maximum breadth above or below notches
+bar#:=25/36pt#; % lowercase bar thickness
+slab#:=25/36pt#; % serif and arm thickness
+cap_bar#:=25/36pt#; % uppercase bar thickness
+cap_band#:=25/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=28/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=0pt#; % vertical drop of sloped serifs
+stem_corr#:=0pt#; % for small refinements of stem breadth
+vair_corr#:=1.5pt#; % for small refinements of hairline height
+apex_corr#:=11/36pt#; % extra width at diagonal junctions
+
+o#:=5/36pt#; % amount of overshoot for curves
+apex_o#:=4/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/4; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=0.86; % factor applied to weights of heavy characters
+math_spread:=-1; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=0; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=0; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=true; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=true; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate textit % switch to the driver file
diff --git a/systems/knuth/local/cm/cmitt9.mf b/systems/knuth/local/cm/cmitt9.mf
new file mode 100644
index 0000000000..fdcd169893
--- /dev/null
+++ b/systems/knuth/local/cm/cmitt9.mf
@@ -0,0 +1,75 @@
+% Computer Modern Italic Typewriter Text for use with 9 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMITT"; font_size 9pt#;
+
+u#:=18.9/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=0pt#; % extra sidebar near uppercase serifs
+letter_fit#:=14/36pt#; % extra space added to all sidebars
+
+body_height#:=225/36pt#; % height of tallest characters
+asc_height#:=198/36pt#; % height of lowercase ascenders
+cap_height#:=198/36pt#; % height of caps
+fig_height#:=198/36pt#; % height of numerals
+x_height#:=139.5/36pt#; % height of lowercase without ascenders
+math_axis#:=99/36pt#; % axis of symmetry for math symbols
+bar_height#:=71/36pt#; % height of crossbar in lowercase e
+comma_depth#:=45/36pt#; % depth of comma below baseline
+desc_depth#:=72/36pt#; % depth of lowercase descenders
+
+crisp#:=21/36pt#; % diameter of serif corners
+tiny#:=21/36pt#; % diameter of rounded corners
+fine#:=20/36pt#; % diameter of sharply rounded corners
+thin_join#:=20/36pt#; % width of extrafine details
+hair#:=24/36pt#; % lowercase hairline breadth
+stem#:=24/36pt#; % lowercase stem breadth
+curve#:=24/36pt#; % lowercase curve breadth
+ess#:=21/36pt#; % breadth in middle of lowercase s
+flare#:=30/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=33/36pt#; % diameter of dots
+cap_hair#:=24/36pt#; % uppercase hairline breadth
+cap_stem#:=24/36pt#; % uppercase stem breadth
+cap_curve#:=24/36pt#; % uppercase curve breadth
+cap_ess#:=24/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=24/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=36/36pt#; % protrusion of lowercase serifs
+cap_jut#:=31/36pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=31/36pt#; % vertical protrusion of beak serifs
+vair#:=21/36pt#; % vertical diameter of hairlines
+notch_cut#:=24/36pt#; % maximum breadth above or below notches
+bar#:=21/36pt#; % lowercase bar thickness
+slab#:=21/36pt#; % serif and arm thickness
+cap_bar#:=21/36pt#; % uppercase bar thickness
+cap_band#:=21/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=24/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=0pt#; % vertical drop of sloped serifs
+stem_corr#:=0pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=9/36pt#; % extra width at diagonal junctions
+
+o#:=3.5/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/4; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=0.81; % factor applied to weights of heavy characters
+math_spread:=-1; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=0; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=0; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=true; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=true; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate textit % switch to the driver file
diff --git a/systems/knuth/local/cm/cmman.mf b/systems/knuth/local/cm/cmman.mf
new file mode 100644
index 0000000000..8cd476f622
--- /dev/null
+++ b/systems/knuth/local/cm/cmman.mf
@@ -0,0 +1,855 @@
+% Variants of Computer Modern, specially for the TeX/METAFONT manuals
+
+if unknown cmbase: input cmbase fi
+mode_setup;
+
+def generate suffix t= enddef;
+
+inner cmchar;
+
+font_size 10pt#;
+font_identifier "CMMAN";
+font_coding_scheme "PI";
+
+input cmtt10; font_setup;
+
+cmchar "Cross between 0 and O";
+beginchar("0",9u#,fig_height#,0);
+italcorr fig_height#*slant-.5u#;
+adjust_fit(0,0);
+penpos1(vair,90); penpos3(vair,-90); penpos2(curve,180); penpos4(curve,0);
+superness:=.77;
+x2r=round max(.7u,1.45u-.5curve); x4r=w-x2r; x1=x3=.5w;
+y1r=h+o; y3r=-o; y2=y4=.5h-vair_corr; y2l:=y4l:=.52h;
+penstroke pulled_arc.e(1,2) & pulled_arc.e(2,3)
+ & pulled_arc.e(3,4) & pulled_arc.e(4,1) & cycle; % bowl
+penlabels(1,2,3,4); endchar;
+
+input cmssq8; slant:=sind 12/cosd 12; font_setup;
+
+cmchar "Lowercase Greek beta";
+beginchar(oct"014",9.5u#,asc_height#,desc_depth#);
+italcorr .5[x_height#,asc_height#]*slant-u#;
+adjust_fit(0,0); pickup fine.nib;
+pos0(hair,180); pos1(hair,180); pos2(vair,90);
+pos3(stem,0); pos4(vair,-90); pos5(hair,-180);
+pos6(vair,-270); pos7(curve,-360); pos8(vair,-450); pos9(hair,-540);
+x0=x1=x9; lft x0l=hround(1.5u-.5hair); x2=x4=x6=x8=.5w+.25u;
+rt x3r=hround(w-1.5u); rt x7r=hround(w-1.5u+.5curve); rt x5l=hround(x4-u);
+bot y0=-d; y1=top y6r=x_height; top y2r=h+oo; y3=.5[y2,y4];
+y5=.5[y4,y6]; top y6r-bot y4r=vstem+eps; bot y8=-oo; y7=y9=.5[y6,y8];
+if y6l<y4l: y6l:=y4l:=y5; fi
+filldraw stroke z0e---z1e...pulled_arc.e(2,3)
+ & pulled_arc.e(3,4)...{up}z5e; % stem and upper bowl
+filldraw stroke z5e{up}...pulled_arc.e(6,7)
+ & pulled_arc.e(7,8)...{up}z9e; % lower bowl
+math_fit(desc_depth#*slant+.5hair#-u#,.7x_height#*slant+.5curve#-1.5u#);
+penlabels(0,1,2,3,4,5,6,7,8,9); endchar;
+
+cmchar "Lowercase thorn";
+beginchar(oct"015",10u#+serif_fit#,asc_height#,desc_depth#);
+italcorr .5x_height#*slant+min(.5curve#-.85u#,-.1u#);
+adjust_fit(serif_fit#,0);
+pickup tiny.nib; pos1(stem',0); pos2(stem,0);
+pos0'(stem',0); pos0(stem,0); z0l=z0'l; x0'=x1; x0=x2;
+lft x1l=hround(2.5u-.5stem'); top y1=h;
+numeric edge; edge=rt x2r;
+pickup fine.nib; pos3(if hefty:thin_join else: hair fi,180);
+pos4(vair',90); pos5(curve,0); pos6(vair,-90);
+rt x3l=1/3[rt x2,edge]; y3=1/8[bar_height,x_height];
+x4l=w-.5(w-serif_fit)+.5u; top y4r=x_height+oo;
+rt x5r=hround min(w-1.35u+.5curve,w-.6u); y5=.5x_height;
+x6=x3; bot y6r=-oo;
+(x,y4r)=whatever[z3l,z4l]; x4r:=min(x,.5[x5r,x4]);
+filldraw stroke z3e{up}...{right}z4e&super_arc.e(4,5)
+ &z5e{down}...{5(x6-x5),y6-y5}z6e; % bowl
+y0=ypart(((edge,h)--(edge,0))intersectionpoint(z3l{up}...{right}z4l));
+pickup tiny.nib; bot y2=-d;
+filldraw stroke z1e--z0'e--z0e--z2e; % stem
+pickup crisp.nib; pos8(hair,0); pos7'(stem,0);
+z7'=z2; x8l=x7'l; bot y8=0;
+filldraw stroke z7'e--z8e; % point
+if serifs: sloped_serif.l(1,0',a,1/3,jut,serif_drop); % upper serif
+ dish_serif(2,0,b,1/3,jut,c,1/3,0); fi % lower serif
+penlabels(0,1,2,3,4,5,6,8); endchar;
+
+input cmr10; font_setup;
+
+message "Parentheses";
+code:=ASCII "0"; % the characters will be 123456789:
+for c=0,1,2,4,infinity:
+cmchar "Left paren with curl "&decimal c;
+beginchar(incr code,7u#,body_height#,paren_depth#);
+italcorr body_height#*slant-.5u#;
+adjust_fit(0,0); pickup fine.nib;
+pos1(vair,0); pos2(.75[hair,stem],0); pos3(vair,0);
+rt x1r=rt x3r=round(w-u); lft x2l=round(x1-4u if monospace: +4/3u fi);
+top y1=h; y2=.5[y1,y3]=math_axis;
+filldraw stroke z1e{curl c}..z2e..{curl c}z3e; % arc
+penlabels(1,2,3); endchar;
+
+cmchar "Right paren with curl "&decimal c;
+beginchar(incr code,7u#,body_height#,paren_depth#);
+italcorr math_axis#*slant-.5u#;
+adjust_fit(0,0); pickup fine.nib;
+pos1(vair,0); pos2(.75[hair,stem],0); pos3(vair,0);
+lft x1l=lft x3l=round u; rt x2r=round(x1+4u if monospace: -4/3u fi);
+top y1=h; y2=.5[y1,y3]=math_axis;
+filldraw stroke z1e{curl c}..z2e..{curl c}z3e; % arc
+penlabels(1,2,3); endchar;
+endfor
+
+message "big G";
+u#:=50/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=0/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=12pt#+250/36pt#; % height of tallest characters
+asc_height#:=body_height#; % height of lowercase ascenders
+cap_height#:=body_height#; % height of caps
+fig_height#:=body_height#; % height of numerals
+x_height#:=155/250asc_height#; % height of lowercase without ascenders
+math_axis#:=90/250asc_height#; % axis of symmetry for math symbols
+bar_height#:=87/250asc_height#; % height of crossbar in lowercase e
+comma_depth#:=70/250asc_height#;% depth of comma below baseline
+desc_depth#:=comma_depth#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=1.2/36pt#; % diameter of rounded corners
+fine#:=1.1/36pt#; % diameter of sharply rounded corners
+thin_join#:=1.1/36pt#; % width of extrafine details
+hair#:=14/36pt#; % lowercase hairline breadth
+stem#:=75/36pt#; % lowercase stem breadth
+curve#:=90/36pt#; % lowercase curve breadth
+ess#:=81/36pt#; % breadth in middle of lowercase s
+flare#:=66/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=114/36pt#; % diameter of dots
+cap_hair#:=17/36pt#; % uppercase hairline breadth
+cap_stem#:=96/36pt#; % uppercase stem breadth
+cap_curve#:=111/36pt#; % uppercase curve breadth
+cap_ess#:=105/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=1pt#; % thickness of lines in math symbols
+
+dish#:=2/36pt#; % amount erased at top or bottom of serifs
+bracket#:=50/36pt#; % vertical distance from serif base to tangent
+jut#:=70/36pt#; % protrusion of lowercase serifs
+cap_jut#:=2.5pt#; % protrusion of uppercase serifs
+beak_jut#:=25/36pt#; % horizontal protrusion of beak serifs
+beak#:=comma_depth#; % vertical protrusion of beak serifs
+vair#:=12/36pt#; % vertical diameter of hairlines
+notch_cut#:=22pt#; % maximum breadth above or below notches
+bar#:=17/36pt#; % lowercase bar thickness
+slab#:=17/36pt#; % serif and arm thickness
+cap_bar#:=17/36pt#; % uppercase bar thickness
+cap_band#:=17/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=22pt#; % max breadth above/below uppercase notches
+serif_drop#:=10/36pt#; % vertical drop of sloped serifs
+stem_corr#:=3/36pt#; % for small refinements of stem breadth
+vair_corr#:=2/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=12/36pt#; % amount of overshoot for curves
+apex_o#:=12/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=0; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=1/6; % extra openness inside bowls
+beak_darkness:=11/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+font_setup;
+
+cmchar "The letter G";
+if serifs: beginchar("G",14u#,cap_height#,0);
+ italcorr cap_height#*slant-1.5u#-.5cap_serif_fit#;
+ adjust_fit(0,.5cap_serif_fit#);
+ pickup tiny.nib; pos0(cap_stem,0); pos7(cap_stem,0);
+ rt x0r=hround(w-2u); y0=good.y(.1[bar_height,x_height]+1); x7=x0;
+ pickup fine.nib;
+ if hefty: bot y7=0; pos5(cap_stem,0); x5=x0; top y5=tiny.top y0;
+ else: pos5(cap_hair,0); pos8(cap_stem,0);
+ y7=.5bar_height; z5l=z8l; z8=z7; x9=x8r; bot y9=0;
+ filldraw z8l{down}..{4(x9-x8),y9-y8}z9--z8r--cycle; fi % spur
+ pos1(cap_hair,0); pos2(cap_band,90);
+ pos3(cap_curve,180); pos4(cap_band,270);
+ rt x1r=hround(w-2u); lft x3r=hround u; x2=x4=.55[x3,x1];
+ top y2r=h+o; bot y4r=-o; y3=.5[y2,y4];
+ bot y1=vround max(.6h,x_height-.5vair);
+ (x2l',y2l)=whatever[z2r,z1l]; x2l:=x2l';
+ (x4l',y4l)=whatever[z4r,z5l]; x4l:=x4l';
+ filldraw stroke z1e{x2-x1,10(y2-y1)}
+ ...pulled_arc.e(2,3) & pulled_arc.e(3,4)...z5e{up}; % arc
+ x6=x1r; top y6=h+o; x1r-x1'=2cap_curve-fine; y1'=y1;
+ path upper_arc; upper_arc=z1{x2-x1,10(y2-y1)}..{left}z2;
+ numeric t; t=xpart(upper_arc intersectiontimes (z6--z1'));
+ filldraw subpath(0,t) of upper_arc--z6--z1r--z1--cycle; % barb
+ pickup tiny.nib; filldraw stroke z0e--z7e; % stem
+ dish_serif(0,7,a,1/3,max(cap_jut,2.25u),b,1/3,1.25u); % serif
+ math_fit(-.3cap_height#*slant-.5u#,ic#);
+else: beginchar("G",12u#,cap_height#,0);
+ italcorr cap_height#*slant-.5u#;
+ adjust_fit(0,0);
+ pickup fine.nib; pos1(1.2flare,80); pos2(slab,90);
+ pos3(cap_curve,180); pos4(slab,270); pos5(flare,270);
+ rt x1r=hround(w-1.35u); x2=x4=.5w+u;
+ lft x3r=hround max(u,2u-.5cap_curve); rt x5l=hround(w-1.2u);
+ top y1r=vround .93h+o; top y2r=h+o; y3=.5h;
+ bot y4r=-o; bot y5r=vround .07h-o;
+ filldraw stroke rterm.e(2,1,right,.9,4)&super_arc.e(2,3)
+ & super_arc.e(3,4) & term.e(4,5,right,1,4); % arc and terminals
+ pos0(stem,0); pos7(stem,0);
+ z7r=z5r; x0=x7; top y0=1+vround .35[bar_height,x_height];
+ filldraw stroke z0e--z7e; % stem
+ pos8(cap_bar,90); pos9(cap_bar,90);
+ z0r=z9r; y8=y9; lft x8=hround x4;
+ filldraw stroke z8e--z9e; % bar
+ math_fit(-.3cap_height#*slant-.5u#,.5ic#); fi
+penlabels(0,1,1',2,3,4,5,6,7,8,9); endchar;
+
+message "The TeXbook title";
+f:=20/3; % magnification factor (based mostly on cmssdc10)
+u#:=f*19/36pt#; % unit width
+width_adj#:=f*15/36pt#; % width adjustment for certain characters
+serif_fit#:=-f*8/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=-f*9/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=-.2u#; % extra space added to all sidebars
+
+body_height#:=f*270/36pt#; % height of tallest characters
+asc_height#:=f*265/36pt#; % height of lowercase ascenders
+cap_height#:=f*265/36pt#; % height of caps
+fig_height#:=f*265/36pt#; % height of numerals
+x_height#:=f*170/36pt#; % height of lowercase without ascenders
+math_axis#:=f*95/36pt#; % axis of symmetry for math symbols
+bar_height#:=f*95/36pt#; % height of crossbar in lowercase e
+comma_depth#:=f*40/36pt#; % depth of comma below baseline
+desc_depth#:=f*60/36pt#; % depth of lowercase descenders
+
+crisp#:=5*23/36pt#; % diameter of serif corners
+tiny#:=5*23/36pt#; % diameter of rounded corners
+fine#:=5*8/36pt#; % diameter of sharply rounded corners
+thin_join#:=12*8/36pt#; % width of extrafine details
+hair#:=5*40/36pt#; % lowercase hairline breadth
+stem#:=5*40/36pt#; % lowercase stem breadth
+curve#:=5*40/36pt#; % lowercase curve breadth
+ess#:=5*37/36pt#; % breadth in middle of lowercase s
+flare#:=5*31/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=5*38/36pt#; % diameter of dots
+cap_hair#:=5*40/36pt#; % uppercase hairline breadth
+cap_stem#:=5*44/36pt#; % uppercase stem breadth
+cap_curve#:=5*44/36pt#; % uppercase curve breadth
+cap_ess#:=5*42/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=5*25/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=0pt#; % protrusion of lowercase serifs
+cap_jut#:=0pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=f*1.5/36pt#; % vertical protrusion of beak serifs
+vair#:=6*23/36pt#; % vertical diameter of hairlines
+notch_cut#:=6*40/36pt#; % maximum breadth above or below notches
+bar#:=6*23/36pt#; % lowercase bar thickness
+slab#:=6*31/36pt#; % serif and arm thickness
+cap_bar#:=6*29/36pt#; % uppercase bar thickness
+cap_band#:=6*27/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=6*37/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=f*2/36pt#; % vertical drop of sloped serifs
+stem_corr#:=5*1/36pt#; % for small refinements of stem breadth
+vair_corr#:=5*2/36pt#; % for small refinements of hairline height
+apex_corr#:=f*14/36pt#; % extra width at diagonal junctions
+
+o#:=f*7/36pt#; % amount of overshoot for curves
+apex_o#:=0pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.91; % factor applied to weights of heavy characters
+math_spread:=-.3; % extra openness of math symbols
+superness:=.74; % parameter for superellipses
+superpull:=1/18; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=true; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=false; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+font_setup;
+
+cmchar "The letter T";
+beginchar("T",13u#-width_adj#,cap_height#,0);
+italcorr cap_height#*slant-beak_jut#-.25u#;
+adjust_fit(0,0);
+h:=vround(h-2stem_corr);
+pickup tiny.nib; pos1(cap_stem,0); pos2(cap_stem,0);
+lft x1l=lft x2l=hround(.5w-.5cap_stem); top y1=h; bot y2=0;
+filldraw stroke z1e--z2e; % stem
+pickup crisp.nib; pos3(slab,90); pos4(hair,0);
+top y3r=h; x3=x1; rt x4r=hround(w-.65u); y4=good.y(y3l-beak);
+arm(3,4,e,beak_darkness,.7beak_jut); % right arm and beak
+pos5(hair,180); x5=w-x4; y5=y4;
+arm(3,5,f,beak_darkness,-.7beak_jut); % left arm and beak
+if serifs: dish_serif(1,2,a,1/3,.5cap_jut,b,1/3,.5cap_jut); % upper bracketing
+ dish_serif(2,1,c,1/3,1.414cap_jut,d,1/3,1.414cap_jut); fi % lower serif
+math_fit(-.75cap_height#*slant,ic#-2.5u#); penlabels(1,2,3,4,5,6); endchar;
+
+cmchar "The letter h";
+beginchar("h",10u#,asc_height#,0);
+italcorr .5[bar_height#,x_height#]*slant-serif_fit#+.5stem#-2u#;
+adjust_fit(serif_fit#+stem_shift#,serif_fit#-stem_shift#);
+pickup tiny.nib; pos1(stem,0); pos2(stem,0);
+pos1'(stem',0); pos2'(stem',0); pos3(stem,0);
+lft x1l=hround(2.5u-.5stem); x1l=x1'l=x2l=x2'l; x3=w-x1;
+top y1=h; bot y2=0; y1=y1'; y2=y2';
+filldraw stroke z1'e--z2'e; % left stem
+h_stroke(2,a,3,4); % arch and right stem
+if serifs: sloped_serif.l(1',2',b,1/3,jut,serif_drop); % upper left serif
+ numeric inner_jut; pickup tiny.nib;
+ if rt x2r+jut+.5u+1<=lft x4l-jut: inner_jut=jut;
+ else: rt x2r+jut+.5u+1=lft x4l-inner_jut; fi
+ dish_serif(2,1,c,1/3,jut,d,1/3,jut); % lower left serif
+ dish_serif(4,3,e,1/3,inner_jut,f,1/3,jut); fi % lower right serif
+penlabels(1,2,3,4); endchar;
+
+cmchar "The letter e";
+beginchar("e",7.25u#+max(.75u#,.5curve#),x_height#,0);
+italcorr .5[bar_height#,x_height#]*slant+.5min(curve#-1.5u#,0);
+adjust_fit(if monospace: .25u#,.5u# else: 0,0 fi);
+numeric left_curve,right_curve;
+left_curve=right_curve+6stem_corr=curve if not serifs: -3stem_corr fi;
+if right_curve<tiny.breadth: right_curve:=tiny.breadth; fi
+if left_curve<tiny.breadth: left_curve:=tiny.breadth; fi
+pickup tiny.nib; pos1(right_curve,0);
+pos2(vair,90); pos3(left_curve,180);
+y1=good.y bar_height; top y2r=h+vround 1.5oo; y0l=bot y1;
+rt x1r=hround min(w-.5u,w-u+.5right_curve);
+lft x3r=hround max(.5u,1.25u-.5left_curve); x2=.5w+.25u;
+{{interim superness:=more_super;
+ filldraw stroke super_arc.e(1,2)}}; % right bowl
+y3=.5[y2,y4]; bot y4r=-oo; x4=x2+.25u;
+if serifs: pos4(vair',270); pos5(hair,360);
+ y5=max(good.y(.5bar_height-.9),y4l+vair); x5r=x1r;
+ (x,y4l)=whatever[z4r,z5]; x4l:=x;
+ filldraw stroke pulled_arc.e(2,3) & pulled_arc.e(3,4)
+ ...{x5-x4,5(y5-y4)}z5e; % left bowl, arc, and terminal
+else: pos4(vair,270);
+ filldraw stroke super_arc.e(2,3) & super_arc.e(3,4); % left bowl and arc
+ pickup fine.nib; pos4'(vair,270); z4=z4';
+ pos5(.5[vair,flare],275); rt x5r=hround(w-.6u);
+ y5r=good.y(y5r+1/3bar_height-y5); y5l:=good.y y5l; x5l:=good.x x5l;
+ filldraw stroke term.e(4',5,right,1,4); fi % terminal
+path testpath; testpath=super_arc.r(2,3) & super_arc.r(3,4);
+y1'r=y0r=y0l+.6[thin_join,vair]; y1'l=y0l; x1'l=x1'r=x1;
+forsuffixes $=l,r:
+ x0$=xpart(((0,y0$)--(x1,y0$)) intersectionpoint testpath); endfor
+fill stroke z0e--z1'e; % crossbar
+penlabels(0,1,2,3,4,5); endchar;
+
+cmchar "The letter E";
+beginchar("E",12u#-width_adj#,cap_height#,0);
+italcorr cap_height#*slant-beak_jut#-.5u#;
+adjust_fit(cap_serif_fit#,0);
+h:=vround(h-stem_corr);
+pickup tiny.nib; pos1(cap_stem,0); pos2(cap_stem,0);
+lft x1l=lft x2l=hround max(2u,3u-.5cap_stem); top y1=h; bot y2=0;
+filldraw stroke z1e--z2e; % stem
+pickup crisp.nib; pos3(slab,90); pos4(hair,0);
+top y3r=h; x3=x1; rt x4r=hround(w-u); y4=good.y(y3l-beak);
+arm(3,4,e,beak_darkness,beak_jut); % upper arm and beak
+pos5(cap_bar,-90); pos6(hair,0);
+top y5l=vround(.52[y2,y1]+.5cap_bar); x5=x1;
+pos0(cap_bar,90); pos7(hair,0);
+z0=z5; x6=x7; y6-y5l=y0l-y7;
+if serifs: rt x6r=hround(w-4.4u+.5hair); y6=good.y(y5l+.6beak);
+ rt x9r=hround(w-.5u);
+else: rt x6r=hround(w-1.5u); y6=y5l; rt x9r=hround(w-.75u); fi
+arm(5,6,f,beak_darkness,0); arm(0,7,g,beak_darkness,0); % middle arm and serif
+pos8(slab if not serifs:+2stem_corr fi,-90); pos9(hair,0);
+bot y8r=0; x8=x2; y9=y8l+7/6beak;
+arm(8,9,h,beak_darkness,1.5beak_jut); % lower arm and beak
+if serifs: dish_serif(1,2,a,1/3,cap_jut,b,1/3,.5cap_jut); % upper serif
+ dish_serif(2,1,c,1/3,cap_jut,d,1/3,.5cap_jut); fi % lower serif
+math_fit(0,.5ic#); penlabels(0,1,2,3,4,5,6,7,8,9); endchar;
+
+cmchar "The letter X";
+beginchar("X",13u#,cap_height#,0);
+italcorr cap_height#*slant-.25u#;
+adjust_fit(cap_serif_fit#,cap_serif_fit#);
+numeric stem[],outer_jut,xjut,alpha[];
+stem1=cap_stem-2stem_corr; stem2=min(cap_hair,stem1);
+outer_jut=.75cap_jut; xjut= if serifs: (stem1-stem2)/4 else: 0 fi;
+x1l=l+letter_fit+.5u+outer_jut; x2r=r-letter_fit-u-outer_jut-xjut;
+x3l=l+letter_fit+.25u+outer_jut+xjut; x4r=r-letter_fit-.25u-outer_jut;
+y1=y2=h; y3=y4=0;
+alpha1=diag_ratio(1,stem1,h,x4r-x1l);
+alpha2=diag_ratio(1,stem2,h,x2r-x3l);
+penpos1(alpha1*stem1,0); penpos2(alpha2*stem2,0);
+penpos3(alpha2*stem2,0); penpos4(alpha1*stem1,0);
+if hefty: z0=whatever[z1,z4]=whatever[z2,z3];
+ x12=x34=x0; y13=y24=y0;
+ z12=whatever[z2l,z3l]; z13=whatever[z2l,z3l];
+ z24=whatever[z2r,z3r]; z34=whatever[z2r,z3r];
+ forsuffixes $=13,24,34: z$'=.1[z$,z0]; endfor
+ fill diag_end(12,1r,.5,1,1l,13')--z13'--diag_end(13',3l,1,.5,3r,34')--z34'
+ --diag_end(34',4l,.5,1,4r,24')--z24'
+ --diag_end(24',2r,1,.5,2l,12)--z12--cycle; % diagonals
+else: fill diag_end(4r,1r,.5,1,1l,4l)
+ --diag_end(1l,4l,.5,1,4r,1r)--cycle; % left diagonal
+ fill diag_end(2l,3l,.5,1,3r,2r)
+ --diag_end(3r,2r,.5,1,2l,3l)--cycle; fi % right diagonal
+if serifs: numeric inner_jut[]; pickup tiny.nib;
+ prime_points_inside(1,4); prime_points_inside(2,3);
+ prime_points_inside(3,2); prime_points_inside(4,1);
+ if rt x1'r+cap_jut+.5u+1<=lft x2'l-cap_jut-xjut: inner_jut1=cap_jut;
+ else: rt x1'r+inner_jut1+.5u+1=lft x2'l-inner_jut1-xjut; fi
+ if rt x3'r+cap_jut+.5u+1<=lft x4'l-cap_jut-xjut: inner_jut2=cap_jut;
+ else: rt x3'r+inner_jut2+.5u+1=lft x4'l-inner_jut2-xjut; fi
+ dish_serif(1',4,a,1/3,outer_jut,b,2/3,inner_jut1); % upper left serif
+ dish_serif(4',1,c,2/3,inner_jut2,d,1/3,outer_jut); % lower right serif
+ dish_serif(2',3,e,2/3,inner_jut1+xjut,
+ f,1/2,outer_jut+xjut)(dark); % upper right serif
+ dish_serif(3',2,g,1/2,outer_jut+xjut,
+ h,2/3,inner_jut2+xjut)(dark); fi % lower left serif
+math_fit(0,.5ic#); penlabels(0,1,2,3,4,12,13,24,34); endchar;
+
+cmchar "The letter b";
+beginchar("b",10u#+serif_fit#,asc_height#,0);
+italcorr .5x_height#*slant+min(.5curve#-u#,-.25u#);
+adjust_fit(serif_fit#,0);
+pickup tiny.nib; pos1(stem',0); pos2(stem,0);
+pos0'(stem',0); pos0(stem,0); z0l=z0'l; x0'=x1; x0=x2;
+lft x1l=hround(2.5u-.5stem'); top y1=h;
+numeric edge; edge=rt x2r;
+pickup fine.nib; pos3(if hefty:thin_join else: hair fi,180);
+pos4(vair,90); pos5(curve,0);
+pos6(vair,-90); penpos7(x3l-x3r,-180);
+rt x3l=1/3[rt x2,edge]; y3=1/8[bar_height,x_height];
+x4l=w-.5(w-serif_fit)+.5u; top y4r=x_height+oo;
+rt x5r=hround min(w-1.35u+.5curve,w-.6u); y5=.5x_height;
+x6l=x4l-.2u; bot y6r=-oo;
+x7=x3; y7=min(y3,y6+y4-y3+.6vair);
+(x,y4r)=whatever[z3l,z4l]; x4r:=min(x,.5[x4,x5r]);
+(x',y6r)=whatever[z7l,z6l]; x6r:=min(x',.5[x6,x5r]);
+filldraw stroke z3e{up}...pulled_arc.e(4,5)&pulled_arc.e(5,6)...{up}z7e; % bowl
+y0=ypart(((edge,h)--(edge,0))intersectionpoint(z3l{up}...{right}z4l));
+y2=ypart(((edge,h)--(edge,0))intersectionpoint(z6l{left}...{up}z7l));
+pickup tiny.nib; filldraw stroke z1e--z0'e--z0e--z2e; % stem
+pickup crisp.nib; pos8(hair,0); pos7'(stem,0);
+z7'=z2; x8l=x7'l; bot y8=0;
+filldraw stroke z7'e--z8e; % point
+if serifs: sloped_serif.l(1,0',a,1/3,jut,serif_drop); fi % upper serif
+penlabels(0,1,2,3,4,5,6,7,8); endchar;
+
+cmchar "The letter o";
+beginchar("o",9u#,x_height#,0);
+italcorr .7x_height#*slant;
+adjust_fit(if monospace: .5u#,.5u# else: 0,0 fi);
+penpos1(vair,90); penpos3(vair',-90);
+penpos2(curve,180); penpos4(curve,0);
+x2r=hround max(.5u,1.25u-.5curve);
+x4r=w-x2r; x1=x3=.5w; y1r=h+vround 1.5oo; y3r=-oo;
+y2=y4=.5h-vair_corr; y2l:=y4l:=.52h;
+penstroke pulled_arc.e(1,2) & pulled_arc.e(2,3)
+ & pulled_arc.e(3,4) & pulled_arc.e(4,1) & cycle; % bowl
+penlabels(1,2,3,4); endchar;
+
+cmchar "The letter k";
+beginchar("k",9.5u#,asc_height#,0);
+italcorr x_height#*slant-.2u#;
+adjust_fit(serif_fit#,serif_fit#); pickup tiny.nib;
+numeric right_jut,stem[],alpha[];
+stem1=max(tiny.breadth,hround(fudged.stem-stem_corr));
+stem2=max(tiny.breadth,hround(fudged.stem-2stem_corr));
+stem3=max(tiny.breadth,hround(fudged.hair-4stem_corr));
+stem4=max(tiny.breadth,hround(fudged.stem-3stem_corr));
+if serifs: right_jut=.6jut; else: right_jut=.4tiny; fi
+pos1(stem1,0); pos2(stem2,0); top y1=h; bot y2=0;
+lft x1l=lft x2l=hround(2.5u-.5stem1);
+top y3=x_height; rt x3r=hround(r-letter_fit-.7u-right_jut);
+bot y6=0; rt x6r=hround(r-letter_fit-.3u-right_jut);
+x4=x11=x1; y4=.7bar_height; y11=y3;
+alpha1=diag_ratio(1,.5(stem3-tiny),y3-y4,x3r-x4);
+alpha2=diag_ratio(1,.5(stem4-tiny),y11-y6,x6r-x1);
+penpos3(alpha1*(stem3-tiny),0); penpos4(whatever,-90);
+z5=.5[z5l,z5r]; penpos6(alpha2*(stem4-tiny),0);
+forsuffixes $=l,r: y3'$=x_height; y6'$=0; z4$=z3'$+whatever*(z3-z4);
+ z5$=z6'$+whatever*(z11-z6)=whatever[z3,z4]; endfor
+z3'r=z3r+penoffset z3-z4 of currentpen+whatever*(z3-z4);
+% we have also |z3'l=z3l+penoffset z4-z3 of currentpen+whatever*(z3-z4)|;\]
+z6'r=z6r+penoffset z11-z6 of currentpen+whatever*(z11-z6);
+z6'l=z6l+penoffset z6-z11 of currentpen+whatever*(z11-z6);
+fill z4r--diag_end(4r,3'r,1,.5,3'l,4l)--z4l--cycle; % upper diagonal
+fill z5l--diag_end(5l,6'l,.5,1,6'r,5r)--z5r--cycle; % lower diagonal
+pos0(stem1,0); pos0'(stem2,0); y0=y0'; x0=x1; x0'=x2;
+rt z0r=whatever[z3,z4]; filldraw stroke z1e..z0e--z0'e..z2e; % stem
+if serifs: numeric inner_jut;
+ if rt x2r+jut+.5u+1<=lft x6l-jut: inner_jut=jut;
+ else: rt x2r+inner_jut+.5u+1=lft x6l-inner_jut; fi
+ sloped_serif.l(1,0,a,1/3,jut,serif_drop); % upper stem serif
+ dish_serif(2,0',b,1/3,jut,c,1/3,inner_jut); % lower stem serif
+ dish_serif(3,4,d,2/3,1.4jut,e,1/2,right_jut)(dark); % upper diagonal serif
+ dish_serif(6,5,f,1/2,inner_jut,g,1/3,right_jut)(dark);fi % lower diagonal serif
+penlabels(0,1,2,3,4,5,6,11); endchar;
+
+message "Typewriter type variant g"; % 9pt
+u#:=18.9/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=0pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=225/36pt#; % height of tallest characters
+asc_height#:=198/36pt#; % height of lowercase ascenders
+cap_height#:=198/36pt#; % height of caps
+fig_height#:=198/36pt#; % height of numerals
+x_height#:=139.5/36pt#; % height of lowercase without ascenders
+math_axis#:=99/36pt#; % axis of symmetry for math symbols
+bar_height#:=71/36pt#; % height of crossbar in lowercase e
+comma_depth#:=45/36pt#; % depth of comma below baseline
+desc_depth#:=72/36pt#; % depth of lowercase descenders
+
+crisp#:=21/36pt#; % diameter of serif corners
+tiny#:=21/36pt#; % diameter of rounded corners
+fine#:=20/36pt#; % diameter of sharply rounded corners
+thin_join#:=20/36pt#; % width of extrafine details
+hair#:=24/36pt#; % lowercase hairline breadth
+stem#:=24/36pt#; % lowercase stem breadth
+curve#:=24/36pt#; % lowercase curve breadth
+ess#:=21/36pt#; % breadth in middle of lowercase s
+flare#:=30/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=33/36pt#; % diameter of dots
+cap_hair#:=24/36pt#; % uppercase hairline breadth
+cap_stem#:=24/36pt#; % uppercase stem breadth
+cap_curve#:=24/36pt#; % uppercase curve breadth
+cap_ess#:=24/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=24/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=31/36pt#; % protrusion of lowercase serifs
+cap_jut#:=31/36pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=31/36pt#; % vertical protrusion of beak serifs
+vair#:=21/36pt#; % vertical diameter of hairlines
+notch_cut#:=24/36pt#; % maximum breadth above or below notches
+bar#:=21/36pt#; % lowercase bar thickness
+slab#:=21/36pt#; % serif and arm thickness
+cap_bar#:=21/36pt#; % uppercase bar thickness
+cap_band#:=21/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=24/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=0pt#; % vertical drop of sloped serifs
+stem_corr#:=0pt#; % for small refinements of stem breadth
+vair_corr#:=0pt#; % for small refinements of hairline height
+apex_corr#:=9/36pt#; % extra width at diagonal junctions
+
+o#:=3.5/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=0.81; % factor applied to weights of heavy characters
+math_spread:=-1; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=0; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=0; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=true; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=true; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+font_setup;
+
+cmchar "Variant letter g";
+beginchar("x",10u#+serif_fit#,x_height#,desc_depth#);
+italcorr x_height#*slant-serif_fit#+.5stem#-2u# if serifs:+.5u# fi;
+adjust_fit(0,serif_fit# if serifs: -.5u# fi);
+pickup tiny.nib; pos1(stem',0); pos2(stem,0);
+pos0'(stem',0); pos0(stem,0); z0r=z0'r; x0'=x1; x0=x2;
+rt x1r=hround(w-2.5u+.5stem');
+numeric edge; edge=lft x2l;
+path edge_path; edge_path=(edge,h)--(edge,0);
+pickup fine.nib; pos3(if hefty:thin_join else: hair fi,0);
+pos4(vair,90); pos5(curve,180); pos6(vair,270); penpos7(x3r-x3l,360);
+lft x3l=min(lft x3l-(rt x3r-tiny.rt x2r),2/3[lft x2,edge]); y3=bar_height;
+x4l=.5(w-serif_fit)-.3u; top y4r=x_height+oo;
+lft x5r=hround max(1.35u-.5curve,.6u); y5=.5x_height;
+x6l=x4l-.2u; bot y6r=vround 1/3vair;
+lft x7l=edge; y7=min(y3,y6+y4-y3+.6vair);
+(x,y4r)=whatever[z3l,z4l]; x4r:=max(x,.5[x5r,x4]);
+(x',y6r)=whatever[z7l,z6l]; x6r:=max(x',.5[x5r,x6]);
+filldraw stroke z3e{up}...{left}z4e&super_arc.e(4,5)
+ &super_arc.e(5,6)&z6e{right}...{up}z7e; % bowl
+y1=ypart(edge_path intersectionpoint(z3l{up}...{left}z4l));
+y0=ypart(edge_path intersectionpoint(z7l{down}...{left}z6l));
+pickup tiny.nib; bot y2=if serifs: -.25d else: 0 fi;
+filldraw stroke z1e--z0'e--z0e--z2e; % stem
+pickup crisp.nib;
+pos8(hround(hair-stem_corr),0); pos7'(stem',0);
+z7'=z1; x8r=x7'r; top y8=h+oo;
+filldraw stroke z7'e--z8e; % point
+if serifs: pickup tiny.nib;
+ pos9(vair,-90); x9=.5[x2,x10]; bot y9r=-d-o-1;
+ pos10(hair,-180); lft x10r=hround u; y10=-.75d+.5flare;
+ pos11(flare,-180); z11r=z10r;
+ bulb(9,10,11); filldraw stroke super_arc.e(2,9); % tail
+else: pickup fine.nib; pos2'(stem,0); z2'=z2;
+ z2''r=z2'r; z2''=z2'; z2''l=(x2'l,0);
+ pos9(vair,-90); x9=4.5u; bot y9r=-d-o-1;
+ pos10(.5[vair,flare],-90); lft x10=hround 1.25u;
+ y10r=good.y -5/6d; y10l:=good.y y10l;
+ filldraw stroke z2'e..z2''e&super_arc.e(2'',9)
+ & term.e(9,10,left,.9,4); fi % tail
+penlabels(0,1,2,3,4,5,6,7,8,9,10,11); endchar;
+
+message "Normal roman variant g"; % 10pt
+
+u#:=20/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=5/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+cap_height#:=246/36pt#; % height of caps
+fig_height#:=232/36pt#; % height of numerals
+x_height#:=155/36pt#; % height of lowercase without ascenders
+math_axis#:=90/36pt#; % axis of symmetry for math symbols
+bar_height#:=87/36pt#; % height of crossbar in lowercase e
+comma_depth#:=70/36pt#; % depth of comma below baseline
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=8/36pt#; % diameter of rounded corners
+fine#:=7/36pt#; % diameter of sharply rounded corners
+thin_join#:=7/36pt#; % width of extrafine details
+hair#:=9/36pt#; % lowercase hairline breadth
+stem#:=25/36pt#; % lowercase stem breadth
+curve#:=30/36pt#; % lowercase curve breadth
+ess#:=27/36pt#; % breadth in middle of lowercase s
+flare#:=33/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=38/36pt#; % diameter of dots
+cap_hair#:=11/36pt#; % uppercase hairline breadth
+cap_stem#:=32/36pt#; % uppercase stem breadth
+cap_curve#:=37/36pt#; % uppercase curve breadth
+cap_ess#:=35/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.4pt#; % thickness of lines in math symbols
+
+dish#:=1/36pt#; % amount erased at top or bottom of serifs
+bracket#:=20/36pt#; % vertical distance from serif base to tangent
+jut#:=28/36pt#; % protrusion of lowercase serifs
+cap_jut#:=37/36pt#; % protrusion of uppercase serifs
+beak_jut#:=10/36pt#; % horizontal protrusion of beak serifs
+beak#:=70/36pt#; % vertical protrusion of beak serifs
+vair#:=8/36pt#; % vertical diameter of hairlines
+notch_cut#:=10pt#; % maximum breadth above or below notches
+bar#:=11/36pt#; % lowercase bar thickness
+slab#:=11/36pt#; % serif and arm thickness
+cap_bar#:=11/36pt#; % uppercase bar thickness
+cap_band#:=11/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=10pt#; % max breadth above/below uppercase notches
+serif_drop#:=4/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=1/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=8/36pt#; % amount of overshoot for curves
+apex_o#:=8/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=0; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=1/6; % extra openness inside bowls
+beak_darkness:=11/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+font_setup;
+
+cmchar "Variant letter g";
+beginchar("y",10u#+serif_fit#,x_height#,desc_depth#);
+italcorr x_height#*slant-serif_fit#+.5stem#-2u# if serifs:+.5u# fi;
+adjust_fit(0,serif_fit# if serifs: -.5u# fi);
+pickup tiny.nib; pos1(stem',0); pos2(stem,0);
+pos0'(stem',0); pos0(stem,0); z0r=z0'r; x0'=x1; x0=x2;
+rt x1r=hround(w-2.5u+.5stem');
+numeric edge; edge=lft x2l;
+path edge_path; edge_path=(edge,h)--(edge,0);
+pickup fine.nib; pos3(if hefty:thin_join else: hair fi,0);
+pos4(vair,90); pos5(curve,180); pos6(vair,270); penpos7(x3r-x3l,360);
+lft x3l=min(lft x3l-(rt x3r-tiny.rt x2r),2/3[lft x2,edge]); y3=bar_height;
+x4l=.5(w-serif_fit)-.3u; top y4r=x_height+oo;
+lft x5r=hround max(1.35u-.5curve,.6u); y5=.5x_height;
+x6l=x4l-.2u; bot y6r=vround 1/3vair;
+lft x7l=edge; y7=min(y3,y6+y4-y3+.6vair);
+(x,y4r)=whatever[z3l,z4l]; x4r:=max(x,.5[x5r,x4]);
+(x',y6r)=whatever[z7l,z6l]; x6r:=max(x',.5[x5r,x6]);
+filldraw stroke z3e{up}...{left}z4e&super_arc.e(4,5)
+ &super_arc.e(5,6)&z6e{right}...{up}z7e; % bowl
+y1=ypart(edge_path intersectionpoint(z3l{up}...{left}z4l));
+y0=ypart(edge_path intersectionpoint(z7l{down}...{left}z6l));
+pickup tiny.nib; bot y2=if serifs: -.25d else: 0 fi;
+filldraw stroke z1e--z0'e--z0e--z2e; % stem
+pickup crisp.nib;
+pos8(hround(hair-stem_corr),0); pos7'(stem',0);
+z7'=z1; x8r=x7'r; top y8=h+oo;
+filldraw stroke z7'e--z8e; % point
+if serifs: pickup tiny.nib;
+ pos9(vair,-90); x9=.5[x2,x10]; bot y9r=-d-o-1;
+ pos10(hair,-180); lft x10r=hround u; y10=-.75d+.5flare;
+ pos11(flare,-180); z11r=z10r;
+ bulb(9,10,11); filldraw stroke super_arc.e(2,9); % tail
+else: pickup fine.nib; pos2'(stem,0); z2'=z2;
+ z2''r=z2'r; z2''=z2'; z2''l=(x2'l,0);
+ pos9(vair,-90); x9=4.5u; bot y9r=-d-o-1;
+ pos10(.5[vair,flare],-90); lft x10=hround 1.25u;
+ y10r=good.y -5/6d; y10l:=good.y y10l;
+ filldraw stroke z2'e..z2''e&super_arc.e(2'',9)
+ & term.e(9,10,left,.9,4); fi % tail
+penlabels(0,1,2,3,4,5,6,7,8,9,10,11); endchar;
+
+message "Bold roman variant g"; % 9pt
+
+u#:=21.3/36pt#; % unit width
+width_adj#:=10.2/36pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=6.2/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=243/36pt#; % height of tallest characters
+asc_height#:=225/36pt#; % height of lowercase ascenders
+cap_height#:=222.3/36pt#; % height of caps
+fig_height#:=208.8/36pt#; % height of numerals
+x_height#:=144/36pt#; % height of lowercase without ascenders
+math_axis#:=81/36pt#; % axis of symmetry for math symbols
+bar_height#:=76/36pt#; % height of crossbar in lowercase e
+comma_depth#:=63/36pt#; % depth of comma below baseline
+desc_depth#:=63/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=13/36pt#; % diameter of rounded corners
+fine#:=10/36pt#; % diameter of sharply rounded corners
+thin_join#:=10/36pt#; % width of extrafine details
+hair#:=16.6/36pt#; % lowercase hairline breadth
+stem#:=38/36pt#; % lowercase stem breadth
+curve#:=43/36pt#; % lowercase curve breadth
+ess#:=35/36pt#; % breadth in middle of lowercase s
+flare#:=42/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=51/36pt#; % diameter of dots
+cap_hair#:=19/36pt#; % uppercase hairline breadth
+cap_stem#:=47/36pt#; % uppercase stem breadth
+cap_curve#:=51/36pt#; % uppercase curve breadth
+cap_ess#:=48/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.57pt#; % thickness of lines in math symbols
+
+dish#:=1/36pt#; % amount erased at top or bottom of serifs
+bracket#:=9/36pt#; % vertical distance from serif base to tangent
+jut#:=22/36pt#; % protrusion of lowercase serifs
+cap_jut#:=35/36pt#; % protrusion of uppercase serifs
+beak_jut#:=10.2/36pt#; % horizontal protrusion of beak serifs
+beak#:=63/36pt#; % vertical protrusion of beak serifs
+vair#:=12.5/36pt#; % vertical diameter of hairlines
+notch_cut#:=9pt#; % maximum breadth above or below notches
+bar#:=16/36pt#; % lowercase bar thickness
+slab#:=16/36pt#; % serif and arm thickness
+cap_bar#:=16/36pt#; % uppercase bar thickness
+cap_band#:=16/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=9pt#; % max breadth above/below uppercase notches
+serif_drop#:=2.7/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1.9/36pt#; % for small refinements of stem breadth
+vair_corr#:=1.4/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=.7; % extra openness of math symbols
+superness:=8/11; % parameter for superellipses
+superpull:=1/8; % extra openness inside bowls
+beak_darkness:=.4; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+font_setup;
+
+cmchar "Variant letter g";
+beginchar("z",10u#+serif_fit#,x_height#,desc_depth#);
+italcorr x_height#*slant-serif_fit#+.5stem#-2u# if serifs:+.5u# fi;
+adjust_fit(0,serif_fit# if serifs: -.5u# fi);
+pickup tiny.nib; pos1(stem',0); pos2(stem,0);
+pos0'(stem',0); pos0(stem,0); z0r=z0'r; x0'=x1; x0=x2;
+rt x1r=hround(w-2.5u+.5stem');
+numeric edge; edge=lft x2l;
+path edge_path; edge_path=(edge,h)--(edge,0);
+pickup fine.nib; pos3(if hefty:thin_join else: hair fi,0);
+pos4(vair,90); pos5(curve,180); pos6(vair,270); penpos7(x3r-x3l,360);
+lft x3l=min(lft x3l-(rt x3r-tiny.rt x2r),2/3[lft x2,edge]); y3=bar_height;
+x4l=.5(w-serif_fit)-.3u; top y4r=x_height+oo;
+lft x5r=hround max(1.35u-.5curve,.6u); y5=.5x_height;
+x6l=x4l-.2u; bot y6r=vround 1/3vair;
+lft x7l=edge; y7=min(y3,y6+y4-y3+.6vair);
+(x,y4r)=whatever[z3l,z4l]; x4r:=max(x,.5[x5r,x4]);
+(x',y6r)=whatever[z7l,z6l]; x6r:=max(x',.5[x5r,x6]);
+filldraw stroke z3e{up}...{left}z4e&super_arc.e(4,5)
+ &super_arc.e(5,6)&z6e{right}...{up}z7e; % bowl
+y1=ypart(edge_path intersectionpoint(z3l{up}...{left}z4l));
+y0=ypart(edge_path intersectionpoint(z7l{down}...{left}z6l));
+pickup tiny.nib; bot y2=if serifs: -.25d else: 0 fi;
+filldraw stroke z1e--z0'e--z0e--z2e; % stem
+pickup crisp.nib;
+pos8(hround(hair-stem_corr),0); pos7'(stem',0);
+z7'=z1; x8r=x7'r; top y8=h+oo;
+filldraw stroke z7'e--z8e; % point
+if serifs: pickup tiny.nib;
+ pos9(vair,-90); x9=.5[x2,x10]; bot y9r=-d-o-1;
+ pos10(hair,-180); lft x10r=hround u; y10=-.75d+.5flare;
+ pos11(flare,-180); z11r=z10r;
+ bulb(9,10,11); filldraw stroke super_arc.e(2,9); % tail
+else: pickup fine.nib; pos2'(stem,0); z2'=z2;
+ z2''r=z2'r; z2''=z2'; z2''l=(x2'l,0);
+ pos9(vair,-90); x9=4.5u; bot y9r=-d-o-1;
+ pos10(.5[vair,flare],-90); lft x10=hround 1.25u;
+ y10r=good.y -5/6d; y10l:=good.y y10l;
+ filldraw stroke z2'e..z2''e&super_arc.e(2'',9)
+ & term.e(9,10,left,.9,4); fi % tail
+penlabels(0,1,2,3,4,5,6,7,8,9,10,11); endchar;
+
+font_identifier "CMMAN"; % restore the clobbered value
+
+bye
diff --git a/systems/knuth/local/cm/cmntex10.mf b/systems/knuth/local/cm/cmntex10.mf
new file mode 100644
index 0000000000..6266c5a40b
--- /dev/null
+++ b/systems/knuth/local/cm/cmntex10.mf
@@ -0,0 +1,78 @@
+% Computer Modern Narrow TeX extended ASCII characters for use with 10 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMNTEX"; font_size 10pt#;
+
+u#:=(26pc#/84)/9; % unit width (84 characters = 26 picas, for DT book)
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=0pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=250/36pt#; % height of tallest characters
+asc_height#:=220/36pt#; % height of lowercase ascenders
+cap_height#:=220/36pt#; % height of caps
+fig_height#:=220/36pt#; % height of numerals
+x_height#:=155/36pt#; % height of lowercase without ascenders
+math_axis#:=110/36pt#; % axis of symmetry for math symbols
+bar_height#:=79/36pt#; % height of crossbar in lowercase e
+comma_depth#:=50/36pt#; % depth of comma below baseline
+desc_depth#:=80/36pt#; % depth of lowercase descenders
+
+crisp#:=17/36pt#; % diameter of serif corners
+tiny#:=17/36pt#; % diameter of rounded corners
+fine#:=16/36pt#; % diameter of sharply rounded corners
+thin_join#:=16/36pt#; % width of extrafine details
+hair#:=20/36pt#; % lowercase hairline breadth
+stem#:=20/36pt#; % lowercase stem breadth
+curve#:=20/36pt#; % lowercase curve breadth
+ess#:=17/36pt#; % breadth in middle of lowercase s
+flare#:=25/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=28/36pt#; % diameter of dots
+cap_hair#:=20/36pt#; % uppercase hairline breadth
+cap_stem#:=20/36pt#; % uppercase stem breadth
+cap_curve#:=20/36pt#; % uppercase curve breadth
+cap_ess#:=20/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=20/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=25/36pt#; % protrusion of lowercase serifs
+cap_jut#:=25/36pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=25/36pt#; % vertical protrusion of beak serifs
+vair#:=17/36pt#; % vertical diameter of hairlines
+notch_cut#:=20/36pt#; % maximum breadth above or below notches
+bar#:=17/36pt#; % lowercase bar thickness
+slab#:=17/36pt#; % serif and arm thickness
+cap_bar#:=17/36pt#; % uppercase bar thickness
+cap_band#:=17/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=20/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=0pt#; % vertical drop of sloped serifs
+stem_corr#:=0pt#; % for small refinements of stem breadth
+vair_corr#:=0pt#; % for small refinements of hairline height
+apex_corr#:=7/36pt#; % extra width at diagonal junctions
+
+o#:=3/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=.25; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=0.81; % factor applied to weights of heavy characters
+math_spread:=-1; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=0; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=0; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=true; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=true; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+extra_beginchar:=extra_beginchar&"rule_thickness:=hair;" % for lowres
+ & "pickup pencircle scaled rule_thickness; rule.nib:=savepen; clearpen;";
+
+generate texset % switch to the driver file
diff --git a/systems/knuth/local/cm/cmntt10.mf b/systems/knuth/local/cm/cmntt10.mf
new file mode 100644
index 0000000000..85e965bb16
--- /dev/null
+++ b/systems/knuth/local/cm/cmntt10.mf
@@ -0,0 +1,78 @@
+% Computer Modern Narrow TeX extended ASCII characters for use with 10 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMNTT"; font_size 10pt#;
+
+u#:=(26pc#/84)/9; % unit width (84 characters = 26 picas, for DT book)
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=0pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=250/36pt#; % height of tallest characters
+asc_height#:=220/36pt#; % height of lowercase ascenders
+cap_height#:=220/36pt#; % height of caps
+fig_height#:=220/36pt#; % height of numerals
+x_height#:=155/36pt#; % height of lowercase without ascenders
+math_axis#:=110/36pt#; % axis of symmetry for math symbols
+bar_height#:=79/36pt#; % height of crossbar in lowercase e
+comma_depth#:=50/36pt#; % depth of comma below baseline
+desc_depth#:=80/36pt#; % depth of lowercase descenders
+
+crisp#:=17/36pt#; % diameter of serif corners
+tiny#:=17/36pt#; % diameter of rounded corners
+fine#:=16/36pt#; % diameter of sharply rounded corners
+thin_join#:=16/36pt#; % width of extrafine details
+hair#:=20/36pt#; % lowercase hairline breadth
+stem#:=20/36pt#; % lowercase stem breadth
+curve#:=20/36pt#; % lowercase curve breadth
+ess#:=17/36pt#; % breadth in middle of lowercase s
+flare#:=25/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=28/36pt#; % diameter of dots
+cap_hair#:=20/36pt#; % uppercase hairline breadth
+cap_stem#:=20/36pt#; % uppercase stem breadth
+cap_curve#:=20/36pt#; % uppercase curve breadth
+cap_ess#:=20/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=20/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=25/36pt#; % protrusion of lowercase serifs
+cap_jut#:=25/36pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=25/36pt#; % vertical protrusion of beak serifs
+vair#:=17/36pt#; % vertical diameter of hairlines
+notch_cut#:=20/36pt#; % maximum breadth above or below notches
+bar#:=17/36pt#; % lowercase bar thickness
+slab#:=17/36pt#; % serif and arm thickness
+cap_bar#:=17/36pt#; % uppercase bar thickness
+cap_band#:=17/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=20/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=0pt#; % vertical drop of sloped serifs
+stem_corr#:=0pt#; % for small refinements of stem breadth
+vair_corr#:=0pt#; % for small refinements of hairline height
+apex_corr#:=7/36pt#; % extra width at diagonal junctions
+
+o#:=3/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=0.81; % factor applied to weights of heavy characters
+math_spread:=-1; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=0; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=0; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=true; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=true; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+extra_beginchar:=extra_beginchar&"rule_thickness:=hair;" % for lowres
+ & "pickup pencircle scaled rule_thickness; rule.nib:=savepen; clearpen;";
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/cmsl6.mf b/systems/knuth/local/cm/cmsl6.mf
new file mode 100644
index 0000000000..73227738d2
--- /dev/null
+++ b/systems/knuth/local/cm/cmsl6.mf
@@ -0,0 +1,75 @@
+% Computer Modern Slanted Roman 6 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMSL"; font_size 6pt#;
+
+u#:=14/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=2.8/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=3/36pt#; % extra space added to all sidebars
+
+body_height#:=162/36pt#; % height of tallest characters
+asc_height#:=150/36pt#; % height of lowercase ascenders
+cap_height#:=147.6/36pt#; % height of caps
+fig_height#:=139.2/36pt#; % height of numerals
+x_height#:=93/36pt#; % height of lowercase without ascenders
+math_axis#:=54/36pt#; % axis of symmetry for math symbols
+bar_height#:=52.2/36pt#; % height of crossbar in lowercase e
+comma_depth#:=42/36pt#; % depth of comma below baseline
+desc_depth#:=42/36pt#; % depth of lowercase descenders
+
+crisp#:=0pt#; % diameter of serif corners
+tiny#:=7/36pt#; % diameter of rounded corners
+fine#:=6/36pt#; % diameter of sharply rounded corners
+thin_join#:=6/36pt#; % width of extrafine details
+hair#:=8/36pt#; % lowercase hairline breadth
+stem#:=18/36pt#; % lowercase stem breadth
+curve#:=20.5/36pt#; % lowercase curve breadth
+ess#:=17/36pt#; % breadth in middle of lowercase s
+flare#:=21/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=26/36pt#; % diameter of dots
+cap_hair#:=8.5/36pt#; % uppercase hairline breadth
+cap_stem#:=21.5/36pt#; % uppercase stem breadth
+cap_curve#:=24/36pt#; % uppercase curve breadth
+cap_ess#:=19/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=.31pt#; % thickness of lines in math symbols
+
+dish#:=.8/36pt#; % amount erased at top or bottom of serifs
+bracket#:=12/36pt#; % vertical distance from serif base to tangent
+jut#:=17/36pt#; % protrusion of lowercase serifs
+cap_jut#:=23/36pt#; % protrusion of uppercase serifs
+beak_jut#:=6.8/36pt#; % horizontal protrusion of beak serifs
+beak#:=42/36pt#; % vertical protrusion of beak serifs
+vair#:=7/36pt#; % vertical diameter of hairlines
+notch_cut#:=6pt#; % maximum breadth above or below notches
+bar#:=8.5/36pt#; % lowercase bar thickness
+slab#:=8.5/36pt#; % serif and arm thickness
+cap_bar#:=8.5/36pt#; % uppercase bar thickness
+cap_band#:=8.5/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=6pt#; % max breadth above/below uppercase notches
+serif_drop#:=2.4/36pt#; % vertical drop of sloped serifs
+stem_corr#:=.8/36pt#; % for small refinements of stem breadth
+vair_corr#:=.7/36pt#; % for small refinements of hairline height
+apex_corr#:=0pt#; % extra width at diagonal junctions
+
+o#:=4.5/36pt#; % amount of overshoot for curves
+apex_o#:=4.5/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/6; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=.8; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=1/6; % extra openness inside bowls
+beak_darkness:=11/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/cmsltt9.mf b/systems/knuth/local/cm/cmsltt9.mf
new file mode 100644
index 0000000000..324d8029d3
--- /dev/null
+++ b/systems/knuth/local/cm/cmsltt9.mf
@@ -0,0 +1,75 @@
+% Computer Modern Slanted Typewriter Text for use with 9 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMSLTT"; font_size 9pt#;
+
+u#:=18.9/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=0pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=225/36pt#; % height of tallest characters
+asc_height#:=198/36pt#; % height of lowercase ascenders
+cap_height#:=198/36pt#; % height of caps
+fig_height#:=198/36pt#; % height of numerals
+x_height#:=139.5/36pt#; % height of lowercase without ascenders
+math_axis#:=99/36pt#; % axis of symmetry for math symbols
+bar_height#:=71/36pt#; % height of crossbar in lowercase e
+comma_depth#:=45/36pt#; % depth of comma below baseline
+desc_depth#:=72/36pt#; % depth of lowercase descenders
+
+crisp#:=21/36pt#; % diameter of serif corners
+tiny#:=21/36pt#; % diameter of rounded corners
+fine#:=20/36pt#; % diameter of sharply rounded corners
+thin_join#:=20/36pt#; % width of extrafine details
+hair#:=24/36pt#; % lowercase hairline breadth
+stem#:=24/36pt#; % lowercase stem breadth
+curve#:=24/36pt#; % lowercase curve breadth
+ess#:=21/36pt#; % breadth in middle of lowercase s
+flare#:=30/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=33/36pt#; % diameter of dots
+cap_hair#:=24/36pt#; % uppercase hairline breadth
+cap_stem#:=24/36pt#; % uppercase stem breadth
+cap_curve#:=24/36pt#; % uppercase curve breadth
+cap_ess#:=24/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=24/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=31/36pt#; % protrusion of lowercase serifs
+cap_jut#:=31/36pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=31/36pt#; % vertical protrusion of beak serifs
+vair#:=21/36pt#; % vertical diameter of hairlines
+notch_cut#:=24/36pt#; % maximum breadth above or below notches
+bar#:=21/36pt#; % lowercase bar thickness
+slab#:=21/36pt#; % serif and arm thickness
+cap_bar#:=21/36pt#; % uppercase bar thickness
+cap_band#:=21/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=24/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=0pt#; % vertical drop of sloped serifs
+stem_corr#:=0pt#; % for small refinements of stem breadth
+vair_corr#:=0pt#; % for small refinements of hairline height
+apex_corr#:=9/36pt#; % extra width at diagonal junctions
+
+o#:=3.5/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/6; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=0.81; % factor applied to weights of heavy characters
+math_spread:=-1; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=0; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=0; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=true; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=true; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/cmssbxo10.mf b/systems/knuth/local/cm/cmssbxo10.mf
new file mode 100644
index 0000000000..d7d7345fe1
--- /dev/null
+++ b/systems/knuth/local/cm/cmssbxo10.mf
@@ -0,0 +1,75 @@
+% Computer Modern Slanted Sans Serif Bold Extended 10 pt
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMSSBXO"; font_size 10pt#; % "O" is for "oblique"
+
+u#:=22/36pt#; % unit width
+width_adj#:=22/36pt#; % width adjustment for certain characters
+serif_fit#:=-9/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=-11/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+cap_height#:=250/36pt#; % height of caps
+fig_height#:=250/36pt#; % height of numerals
+x_height#:=165/36pt#; % height of lowercase without ascenders
+math_axis#:=90/36pt#; % axis of symmetry for math symbols
+bar_height#:=90/36pt#; % height of crossbar in lowercase e
+comma_depth#:=38/36pt#; % depth of comma below baseline
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+
+crisp#:=26/36pt#; % diameter of serif corners
+tiny#:=26/36pt#; % diameter of rounded corners
+fine#:=9/36pt#; % diameter of sharply rounded corners
+thin_join#:=9/36pt#; % width of extrafine details
+hair#:=49/36pt#; % lowercase hairline breadth
+stem#:=49/36pt#; % lowercase stem breadth
+curve#:=49/36pt#; % lowercase curve breadth
+ess#:=43/36pt#; % breadth in middle of lowercase s
+flare#:=40/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=47/36pt#; % diameter of dots
+cap_hair#:=49/36pt#; % uppercase hairline breadth
+cap_stem#:=53/36pt#; % uppercase stem breadth
+cap_curve#:=53/36pt#; % uppercase curve breadth
+cap_ess#:=51/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=30/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=0pt#; % protrusion of lowercase serifs
+cap_jut#:=0pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=1.5/36pt#; % vertical protrusion of beak serifs
+vair#:=26/36pt#; % vertical diameter of hairlines
+notch_cut#:=49/36pt#; % maximum breadth above or below notches
+bar#:=26/36pt#; % lowercase bar thickness
+slab#:=36/36pt#; % serif and arm thickness
+cap_bar#:=34/36pt#; % uppercase bar thickness
+cap_band#:=31/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=45/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=2/36pt#; % vertical drop of sloped serifs
+stem_corr#:=1/36pt#; % for small refinements of stem breadth
+vair_corr#:=2/36pt#; % for small refinements of hairline height
+apex_corr#:=18/36pt#; % extra width at diagonal junctions
+
+o#:=8/36pt#; % amount of overshoot for curves
+apex_o#:=0pt#; % amount of overshoot for diagonal junctions
+
+slant:=sind 12/cosd 12; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=.88; % factor applied to weights of heavy characters
+math_spread:=.5; % extra openness of math symbols
+superness:=3/4; % parameter for superellipses
+superpull:=1/18; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=true; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=false; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/cmsslu30.mf b/systems/knuth/local/cm/cmsslu30.mf
new file mode 100644
index 0000000000..9e5ec2a031
--- /dev/null
+++ b/systems/knuth/local/cm/cmsslu30.mf
@@ -0,0 +1,75 @@
+% Computer Modern Sans Serif Light UltraCondensed for C+T title pages
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMSSLU"; font_size 30pt#;
+
+u#:=30/36pt#; % unit width
+width_adj#:=20/36pt#; % width adjustment for certain characters
+serif_fit#:=-12/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=-15/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=750/36pt#; % height of tallest characters
+asc_height#:=750/36pt#; % height of lowercase ascenders
+cap_height#:=750/36pt#; % height of caps
+fig_height#:=750/36pt#; % height of numerals
+x_height#:=510/36pt#; % height of lowercase without ascenders
+math_axis#:=.5cap_height#; % axis of symmetry for math symbols
+bar_height#:=285/36pt#; % height of crossbar in lowercase e
+comma_depth#:=120/36pt#; % depth of comma below baseline
+desc_depth#:=180/36pt#; % depth of lowercase descenders
+
+crisp#:=23/36pt#; % diameter of serif corners
+tiny#:=23/36pt#; % diameter of rounded corners
+fine#:=23/36pt#; % diameter of sharply rounded corners
+thin_join#:=23/36pt#; % width of extrafine details
+hair#:=27/36pt#; % lowercase hairline breadth
+stem#:=27/36pt#; % lowercase stem breadth
+curve#:=27/36pt#; % lowercase curve breadth
+ess#:=27/36pt#; % breadth in middle of lowercase s
+flare#:=29/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=30/36pt#; % diameter of dots
+cap_hair#:=27/36pt#; % uppercase hairline breadth
+cap_stem#:=27/36pt#; % uppercase stem breadth
+cap_curve#:=27/36pt#; % uppercase curve breadth
+cap_ess#:=27/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=25/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=0pt#; % protrusion of lowercase serifs
+cap_jut#:=0pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=1.5/36pt#; % vertical protrusion of beak serifs
+vair#:=23/36pt#; % vertical diameter of hairlines
+notch_cut#:=27/36pt#; % maximum breadth above or below notches
+bar#:=23/36pt#; % lowercase bar thickness
+slab#:=27/36pt#; % serif and arm thickness
+cap_bar#:=27/36pt#; % uppercase bar thickness
+cap_band#:=27/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=27/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=2/36pt#; % vertical drop of sloped serifs
+stem_corr#:=0/36pt#; % for small refinements of stem breadth
+vair_corr#:=0/36pt#; % for small refinements of hairline height
+apex_corr#:=0/36pt#; % extra width at diagonal junctions
+
+o#:=7/36pt#; % amount of overshoot for curves
+apex_o#:=0pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=.3; % extra openness of math symbols
+superness:=.75; % parameter for superellipses
+superpull:=0; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=false; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/cmssu30.mf b/systems/knuth/local/cm/cmssu30.mf
new file mode 100644
index 0000000000..7288820cd2
--- /dev/null
+++ b/systems/knuth/local/cm/cmssu30.mf
@@ -0,0 +1,75 @@
+% Computer Modern Sans Serif UltraCondensed for C+T boards
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMSSU"; font_size 30pt#;
+
+u#:=30/36pt#; % unit width
+width_adj#:=20/36pt#; % width adjustment for certain characters
+serif_fit#:=-12/36pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=-15/36pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=750/36pt#; % height of tallest characters
+asc_height#:=750/36pt#; % height of lowercase ascenders
+cap_height#:=750/36pt#; % height of caps
+fig_height#:=750/36pt#; % height of numerals
+x_height#:=510/36pt#; % height of lowercase without ascenders
+math_axis#:=.5cap_height#; % axis of symmetry for math symbols
+bar_height#:=285/36pt#; % height of crossbar in lowercase e
+comma_depth#:=120/36pt#; % depth of comma below baseline
+desc_depth#:=180/36pt#; % depth of lowercase descenders
+
+crisp#:=23/36pt#; % diameter of serif corners
+tiny#:=23/36pt#; % diameter of rounded corners
+fine#:=23/36pt#; % diameter of sharply rounded corners
+thin_join#:=46/36pt#; % width of extrafine details
+hair#:=54/36pt#; % lowercase hairline breadth
+stem#:=54/36pt#; % lowercase stem breadth
+curve#:=54/36pt#; % lowercase curve breadth
+ess#:=54/36pt#; % breadth in middle of lowercase s
+flare#:=58/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=60/36pt#; % diameter of dots
+cap_hair#:=54/36pt#; % uppercase hairline breadth
+cap_stem#:=54/36pt#; % uppercase stem breadth
+cap_curve#:=54/36pt#; % uppercase curve breadth
+cap_ess#:=54/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=50/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=0pt#; % protrusion of lowercase serifs
+cap_jut#:=0pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=1.5/36pt#; % vertical protrusion of beak serifs
+vair#:=50/36pt#; % vertical diameter of hairlines
+notch_cut#:=54/36pt#; % maximum breadth above or below notches
+bar#:=48/36pt#; % lowercase bar thickness
+slab#:=54/36pt#; % serif and arm thickness
+cap_bar#:=54/36pt#; % uppercase bar thickness
+cap_band#:=54/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=54/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=2/36pt#; % vertical drop of sloped serifs
+stem_corr#:=0/36pt#; % for small refinements of stem breadth
+vair_corr#:=0/36pt#; % for small refinements of hairline height
+apex_corr#:=0/36pt#; % extra width at diagonal junctions
+
+o#:=7/36pt#; % amount of overshoot for curves
+apex_o#:=0pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=.3; % extra openness of math symbols
+superness:=.75; % parameter for superellipses
+superpull:=0; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=false; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/cmsytt10.mf b/systems/knuth/local/cm/cmsytt10.mf
new file mode 100644
index 0000000000..1a28aefc6b
--- /dev/null
+++ b/systems/knuth/local/cm/cmsytt10.mf
@@ -0,0 +1,97 @@
+% Computer Modern Math Symbols 10 point Typewriter
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMSYTT"; font_size 10pt#;
+
+u#:=21/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=0pt#; % extra sidebar near uppercase serifs
+letter_fit#:=0pt#; % extra space added to all sidebars
+
+body_height#:=250/36pt#; % height of tallest characters
+asc_height#:=220/36pt#; % height of lowercase ascenders
+cap_height#:=220/36pt#; % height of caps
+fig_height#:=220/36pt#; % height of numerals
+x_height#:=155/36pt#; % height of lowercase without ascenders
+math_axis#:=110/36pt#; % axis of symmetry for math symbols
+bar_height#:=79/36pt#; % height of crossbar in lowercase e
+comma_depth#:=50/36pt#; % depth of comma below baseline
+desc_depth#:=80/36pt#; % depth of lowercase descenders
+
+crisp#:=22/36pt#; % diameter of serif corners
+tiny#:=22/36pt#; % diameter of rounded corners
+fine#:=21/36pt#; % diameter of sharply rounded corners
+thin_join#:=21/36pt#; % width of extrafine details
+hair#:=25/36pt#; % lowercase hairline breadth
+stem#:=25/36pt#; % lowercase stem breadth
+curve#:=25/36pt#; % lowercase curve breadth
+ess#:=22/36pt#; % breadth in middle of lowercase s
+flare#:=32/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=36/36pt#; % diameter of dots
+cap_hair#:=25/36pt#; % uppercase hairline breadth
+cap_stem#:=25/36pt#; % uppercase stem breadth
+cap_curve#:=25/36pt#; % uppercase curve breadth
+cap_ess#:=25/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=25/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=34/36pt#; % protrusion of lowercase serifs
+cap_jut#:=34/36pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=34/36pt#; % vertical protrusion of beak serifs
+vair#:=22/36pt#; % vertical diameter of hairlines
+notch_cut#:=25/36pt#; % maximum breadth above or below notches
+bar#:=22/36pt#; % lowercase bar thickness
+slab#:=22/36pt#; % serif and arm thickness
+cap_bar#:=22/36pt#; % uppercase bar thickness
+cap_band#:=22/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=25/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=0pt#; % vertical drop of sloped serifs
+stem_corr#:=0pt#; % for small refinements of stem breadth
+vair_corr#:=0pt#; % for small refinements of hairline height
+apex_corr#:=10/36pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=0.81; % factor applied to weights of heavy characters
+math_spread:=-1; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=0; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=0; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=true; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=true; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+% the following font parameters are explained in {\sl The \TeX book}, Appendix G
+% I've copied them from cmsy10 (maybe a bad idea, but for today it's OK...)
+
+subs:=7/10; % subscripts to 10pt will normally be in 7pt type
+rth#:=.4pt#; % assume that rules will come from cmex10
+fontdimen 8: % parameters $\sigma_8$ through $\sigma_{22}$ will now be listed
+ math_axis#+3.51rth#+54/36pt#+subs*desc_depth#, % num1
+ math_axis#+1.51rth#+30/36pt#, % num2
+ math_axis#+1.51rth#+48/36pt#, % num3
+ -(math_axis#-3.51rth#-subs*fig_height#-124/36pt#), % denom1
+ -(math_axis#-1.51rth#-subs*fig_height#-30/36pt#), % denom2
+ 8.99pt#-subs*asc_height#, % sup1
+ 8.49pt#-subs*asc_height#, % sup2
+ 104/36pt#, % sup3
+ 54/36pt#, % sub1
+ -(8.49pt#-2subs*asc_height#-3.1rth#), % sub2
+ subs*asc_height#-36/36pt#, % sup\_drop
+ 18/36pt#, % sub\_drop
+ 23.9pt#, % delim1
+ 10.1pt#, % delim2
+ math_axis#; % axis\_height
+
+generate mathsy % switch to the driver file
diff --git a/systems/knuth/local/cm/cmtim.mf b/systems/knuth/local/cm/cmtim.mf
new file mode 100644
index 0000000000..020b825d36
--- /dev/null
+++ b/systems/knuth/local/cm/cmtim.mf
@@ -0,0 +1,78 @@
+% crude approximation to Time Magazine font of 1980
+% based on cmr8 scaled 1050 but then twiddled
+
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMTIM"; font_size 9pt#;
+
+bpt#:=1.05pt#;
+u#:=1.02*17/36bpt#; % unit width
+width_adj#:=0bpt#; % width adjustment for certain characters
+serif_fit#:=0bpt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=4.1/36bpt#; % extra sidebar near uppercase serifs
+letter_fit#:=-1/36bpt#; % extra space added to all sidebars
+
+body_height#:=9/8*216/36bpt#; % height of tallest characters
+asc_height#:=9/8*200/36bpt#; % height of lowercase ascenders
+cap_height#:=9/8*196.8/36bpt#; % height of caps
+fig_height#:=9/8*185.6/36bpt#; % height of numerals
+x_height#:=8/7*124/36bpt#; % height of lowercase without ascenders
+math_axis#:=9/8*72/36bpt#; % axis of symmetry for math symbols
+bar_height#:=8/7*69.6/36bpt#; % height of crossbar in lowercase e
+comma_depth#:=7/8*56/36bpt#; % depth of comma below baseline
+desc_depth#:=7/8*56/36bpt#; % depth of lowercase descenders
+
+crisp#:=0bpt#; % diameter of serif corners
+tiny#:=8/36bpt#; % diameter of rounded corners
+fine#:=7/36bpt#; % diameter of sharply rounded corners
+thin_join#:=7/36bpt#; % width of extrafine details
+hair#:=9/36bpt#; % lowercase hairline breadth
+stem#:=22/36bpt#; % lowercase stem breadth
+curve#:=25.5/36bpt#; % lowercase curve breadth
+ess#:=23/36bpt#; % breadth in middle of lowercase s
+flare#:=27/36bpt#; % diameter of bulbs or breadth of terminals
+dot_size#:=32/36bpt#; % diameter of dots
+cap_hair#:=9.5/36bpt#; % uppercase hairline breadth
+cap_stem#:=26.5/36bpt#; % uppercase stem breadth
+cap_curve#:=30/36bpt#; % uppercase curve breadth
+cap_ess#:=27/36bpt#; % breadth in middle of uppercase s
+rule_thickness#:=.36bpt#; % thickness of lines in math symbols
+
+dish#:=1/36bpt#; % amount erased at top or bottom of serifs
+bracket#:=16/36bpt#; % vertical distance from serif base to tangent
+jut#:=22/36bpt#; % protrusion of lowercase serifs
+cap_jut#:=29/36bpt#; % protrusion of uppercase serifs
+beak_jut#:=8.4/36bpt#; % horizontal protrusion of beak serifs
+beak#:=56/36bpt#; % vertical protrusion of beak serifs
+vair#:=8/36bpt#; % vertical diameter of hairlines
+notch_cut#:=8bpt#; % maximum breadth above or below notches
+bar#:=9.5/36bpt#; % lowercase bar thickness
+slab#:=9.5/36bpt#; % serif and arm thickness
+cap_bar#:=9.5/36bpt#; % uppercase bar thickness
+cap_band#:=9.5/36bpt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=8bpt#; % max breadth above/below uppercase notches
+serif_drop#:=3.2/36bpt#; % vertical drop of sloped serifs
+stem_corr#:=1/36bpt#; % for small refinements of stem breadth
+vair_corr#:=1/36bpt#; % for small refinements of hairline height
+apex_corr#:=0bpt#; % extra width at diagonal junctions
+
+o#:=6/36bpt#; % amount of overshoot for curves
+apex_o#:=6/36bpt#; % amount of overshoot for diagonal junctions
+
+slant:=0; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=1; % factor applied to weights of heavy characters
+math_spread:=.4; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=1/6; % extra openness inside bowls
+beak_darkness:=11/30; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=false; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=false; % should an italic-style g be used?
+low_asterisk:=false; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate roman % switch to the driver file
diff --git a/systems/knuth/local/cm/cmvtti10.mf b/systems/knuth/local/cm/cmvtti10.mf
new file mode 100644
index 0000000000..535f4d87b2
--- /dev/null
+++ b/systems/knuth/local/cm/cmvtti10.mf
@@ -0,0 +1,75 @@
+% Computer Modern Variable-Width Typewriter Text Italic for use with 10 point
+if unknown cmbase: input cmbase fi
+
+font_identifier:="CMVTTI"; font_size 10pt#;
+
+u#:=21/36pt#; % unit width
+width_adj#:=0pt#; % width adjustment for certain characters
+serif_fit#:=0pt#; % extra sidebar near lowercase serifs
+cap_serif_fit#:=0pt#; % extra sidebar near uppercase serifs
+letter_fit#:=15/36pt#; % extra space added to all sidebars
+
+body_height#:=250/36pt#; % height of tallest characters
+asc_height#:=220/36pt#; % height of lowercase ascenders
+cap_height#:=220/36pt#; % height of caps
+fig_height#:=220/36pt#; % height of numerals
+x_height#:=155/36pt#; % height of lowercase without ascenders
+math_axis#:=110/36pt#; % axis of symmetry for math symbols
+bar_height#:=79/36pt#; % height of crossbar in lowercase e
+comma_depth#:=50/36pt#; % depth of comma below baseline
+desc_depth#:=80/36pt#; % depth of lowercase descenders
+
+crisp#:=22/36pt#; % diameter of serif corners
+tiny#:=22/36pt#; % diameter of rounded corners
+fine#:=21/36pt#; % diameter of sharply rounded corners
+thin_join#:=21/36pt#; % width of extrafine details
+hair#:=25/36pt#; % lowercase hairline breadth
+stem#:=25/36pt#; % lowercase stem breadth
+curve#:=25/36pt#; % lowercase curve breadth
+ess#:=22/36pt#; % breadth in middle of lowercase s
+flare#:=32/36pt#; % diameter of bulbs or breadth of terminals
+dot_size#:=36/36pt#; % diameter of dots
+cap_hair#:=25/36pt#; % uppercase hairline breadth
+cap_stem#:=25/36pt#; % uppercase stem breadth
+cap_curve#:=25/36pt#; % uppercase curve breadth
+cap_ess#:=25/36pt#; % breadth in middle of uppercase s
+rule_thickness#:=25/36pt#; % thickness of lines in math symbols
+
+dish#:=0pt#; % amount erased at top or bottom of serifs
+bracket#:=0pt#; % vertical distance from serif base to tangent
+jut#:=40/36pt#; % protrusion of lowercase serifs
+cap_jut#:=34/36pt#; % protrusion of uppercase serifs
+beak_jut#:=0pt#; % horizontal protrusion of beak serifs
+beak#:=34/36pt#; % vertical protrusion of beak serifs
+vair#:=22/36pt#; % vertical diameter of hairlines
+notch_cut#:=25/36pt#; % maximum breadth above or below notches
+bar#:=22/36pt#; % lowercase bar thickness
+slab#:=22/36pt#; % serif and arm thickness
+cap_bar#:=22/36pt#; % uppercase bar thickness
+cap_band#:=22/36pt#; % uppercase thickness above/below lobes
+cap_notch_cut#:=25/36pt#; % max breadth above/below uppercase notches
+serif_drop#:=0pt#; % vertical drop of sloped serifs
+stem_corr#:=0pt#; % for small refinements of stem breadth
+vair_corr#:=1.5/36pt#; % for small refinements of hairline height
+apex_corr#:=10/36pt#; % extra width at diagonal junctions
+
+o#:=4/36pt#; % amount of overshoot for curves
+apex_o#:=3/36pt#; % amount of overshoot for diagonal junctions
+
+slant:=1/4; % tilt ratio $(\Delta x/\Delta y)$
+fudge:=0.81; % factor applied to weights of heavy characters
+math_spread:=-1; % extra openness of math symbols
+superness:=1/sqrt2; % parameter for superellipses
+superpull:=0; % extra openness inside bowls
+beak_darkness:=0; % fraction of triangle inside beak serifs
+ligs:=2; % level of ligatures to be included
+
+square_dots:=false; % should dots be square?
+hefty:=true; % should we try hard not to be overweight?
+serifs:=true; % should serifs and bulbs be attached?
+monospace:=false; % should all characters have the same width?
+variant_g:=true; % should an italic-style g be used?
+low_asterisk:=true; % should the asterisk be centered at the axis?
+math_fitting:=false; % should math-mode spacing be used?
+
+generate textit % switch to the driver file
diff --git a/systems/knuth/local/cm/diam12.mf b/systems/knuth/local/cm/diam12.mf
new file mode 100644
index 0000000000..2f4494d8c3
--- /dev/null
+++ b/systems/knuth/local/cm/diam12.mf
@@ -0,0 +1,18 @@
+% make a solid diamond to use as a calligraphic-style bullet
+if unknown cmbase: input cmbase fi % omit if CMMF can be assumed
+mode_setup;
+
+def generate suffix t= enddef;
+input cmr12; font_setup;
+
+cmchar "Solid diamond"; % cf Diamond operator in sym.mf
+beginchar("d",9u#,v_center(7u#));
+italcorr math_axis#*slant;
+adjust_fit(0,0); pickup rule.nib;
+numeric a; a=(lft 3.5u)*sqrt(3.14159/2); % an attempt to match circle area
+x4=x8=good.x .5w; center_on(x4); x2=w-x6=good.x(x4+a);
+y2=y6=.5[y4,y8]=math_axis; y8=good.y(y2+a);
+fill (rt z2)--(top z8)--(lft z6)--(bot z4)--cycle; % bowl
+labels(2,4,6,8); endchar;
+
+end
diff --git a/systems/knuth/local/cm/ebigop.mf b/systems/knuth/local/cm/ebigop.mf
new file mode 100644
index 0000000000..05f3d808d3
--- /dev/null
+++ b/systems/knuth/local/cm/ebigop.mf
@@ -0,0 +1,912 @@
+% The Computer Modern Math-Extension family (by D. E. Knuth, 1979--1985)
+% Special version for characters redesigned to blend with Euler
+
+% This is the driver file for EUEX fonts, before AMS screwed it up
+font_coding_scheme:="euler substitutions only";
+mode_setup; font_setup;
+
+def t_ = transformed currenttransform enddef;
+
+def bulb(suffix $,$$,$$$) =
+ z$$$r=z$$r;
+pair trueup; (z5+trueup)t_=z5+(0,y$$r-y$r);
+ path_.l:=z$l{x$$r-x$r,0}...{trueup}z$$l;
+{{less_tense;
+ filldraw path_.l--z$$r{-trueup}...{x$r-x$$r,0}z$r--cycle}}; % link
+ path_.r:=z$$$l{-trueup}..z$$$r{trueup}; % near-circle
+ filldraw subpath(0,xpart(path_.r intersectiontimes path_.l)) of path_.r
+ --z$$r{trueup}..cycle; % bulb
+ enddef;
+
+% characters modified from BIGOP
+charlist oct"110": oct"111"; % contour integral signs
+charlist oct"122": oct"132"; % integral signs
+charlist oct"120": oct"130"; % summation signs
+charlist oct"121": oct"131"; % product signs
+charlist oct"140": oct"141"; % coproduct signs
+
+% characters modified from BIGDEL (braces in different positions)
+charlist oct"010": oct"012": oct"014": oct"016": oct"070"; % left braces
+charlist oct"011": oct"013": oct"015": oct"017": oct"071"; % right braces
+extensible oct"070": oct"070",oct"074",oct"072",oct"076"; % left brace
+extensible oct"071": oct"071",oct"075",oct"073",oct"076"; % right brace
+extensible oct"072": oct"070",0,oct"072",oct"076"; % sharp left parenthesis
+extensible oct"073": oct"071",0,oct"073",oct"076"; % sharp right parenthesis
+extensible oct"076": 0,0,0,oct"076"; % brace extension modules only
+extensible oct"100": oct"070",0,oct"073",oct"076"; % left moustache
+extensible oct"101": oct"071",0,oct"072",oct"076"; % right moustache
+% the horizontal braces are included too
+heavy_rule_thickness:=bold+dw; % this is the main change: big braces not so dark
+heavy_rule_thickness#:=curve#+(curve#-stem#);
+
+% arrows (taken from SYMBOL, SYM, and ROMSS, with longer and thicker arrowheads)
+bar:=rule_thickness; % this makes the arrowheads thicker
+% the half arrows from ROMSS are moved to different font positions
+% infinity (taken from SYMBOL, made heavier)
+
+cmchar "\textstyle integral sign";
+beginchar(oct"122",12u#,0,100/54dh#);
+italcorr 0.5u#; % \TeX\ kerning between upper limit and lower limit
+adjust_fit(-1u#,-1u#-ic#); pickup fine.nib;
+pos1(dot_size,0); pos2(.75[hair,stem],0); pos3(vair,90); pos4(stem,180);
+pos5(.4[stem,curve],180); pos5'(.4[stem,curve],0); z5'=z5; pos6(stem,0);
+pos7(vair,-90); pos8(.75[hair,stem],-180); pos9(dot_size,-180);
+rt x1r=hround(w-u); x9=w-x1; x7=w-x3=3u;
+x5=.5[x4,x6]; x4-x6=1.2u; lft x5r=hround(.5w-.5curve);
+top y3r=0; bot y7r=-d; y9-.5(.4[stem,curve])=vround(top y7l+.25(.4[stem,curve]));
+y3-y1=y9-y7; y5=.5[y3,y7]=.5[y4,y6]; y4-y6=.6(y3-y7);
+transform skewit;
+ z5 transformed skewit=z5; (z5+right) transformed skewit = z5+right;
+ z3 transformed skewit=z3-u*right;
+skewit:=skewit transformed currenttransform;
+save currenttransform; transform currenttransform; currenttransform:=skewit;
+bulb(3,2,1); bulb(7,8,9); % bulbs
+filldraw stroke z3e{left}...z4e{(z5e-z4e)xscaled 1.1}
+ ..tension atleast 1 and atleast .8..{z5-z4}z5e; % upper stem
+filldraw stroke z5'e{z6-z5}..tension atleast.8 and atleast 1
+ ..{(z6e-z5'e)xscaled 1.1}z6e...{left}z7e; % lower stem
+penlabels(1,2,3,4,5,6,7,8,9); endchar;
+
+cmchar "\displaystyle integral sign";
+beginchar(oct"132",18u#,0,200/54dh#);
+italcorr 2u#; % \TeX\ kerning between upper limit and lower limit
+adjust_fit(-4u#,-4u#-ic#); pickup fine.nib;
+numeric bulb_size, max_size;
+bulb_size=hround(.8[stem,curve]); max_size=hround(bold+.2dw);
+pos1(bulb_size,0); pos2(.75[hair,stem],0); pos3(vair,90); pos4(.4[stem,curve],180);
+pos5(max_size,180); pos5'(max_size,0); z5'=z5; pos6(.4[stem,curve],0);
+pos7(vair,-90); pos8(.75[hair,stem],-180); pos9(bulb_size,-180);
+rt x1r=hround(w-u); x9=w-x1; x7=w-x3=3u;
+x5=.5[x4,x6]; x4-x6=4.8u; lft x5r=hround(.5w-.5max_size);
+top y3r=0; bot y7r=-d;
+y9-.5bulb_size=vround(top y7l+.25bulb_size); y3-y1=y9-y7;
+y5=.5[y3,y7]=.5[y4,y6]; y4-y6=.6(y3-y7);
+transform skewit;
+ z5 transformed skewit=z5; (z5+right) transformed skewit = z5+right;
+ z3 transformed skewit=z3-4u*right;
+skewit:=skewit transformed currenttransform;
+save currenttransform; transform currenttransform; currenttransform:=skewit;
+bulb(3,2,1); bulb(7,8,9); % bulbs
+filldraw stroke z3e{left}...z4e{(z5e-z4e)xscaled 1.1}
+ ..tension atleast 1 and atleast .8..{z5-z4}z5e; % upper stem
+filldraw stroke z5'e{z6-z5}..tension atleast.8 and atleast 1
+ ..{(z6e-z5'e)xscaled 1.1}z6e...{left}z7e; % lower stem
+penlabels(1,2,3,4,5,6,7,8,9); endchar;
+
+cmchar "\textstyle contour integral sign";
+beginchar(oct"110",12u#,0,100/54dh#);
+italcorr 0.5u#; % \TeX\ kerning between upper limit and lower limit
+adjust_fit(-u#,-u#-ic#); pickup fine.nib;
+begingroup
+pos1(dot_size,0); pos2(.75[hair,stem],0); pos3(vair,90); pos4(stem,180);
+pos5(.4[stem,curve],180); pos5'(.4[stem,curve],0); z5'=z5; pos6(stem,0);
+pos7(vair,-90); pos8(.75[hair,stem],-180); pos9(dot_size,-180);
+rt x1r=hround(w-u); x9=w-x1; x7=w-x3=3u;
+x5=.5[x4,x6]; x4-x6=1.2u; lft x5r=hround(.5w-.5(.4[stem,curve]));
+top y3r=0; bot y7r=-d; y9-.5(.4[stem,curve])=vround(top y7l+.25(.4[stem,curve]));
+y3-y1=y9-y7; y5=.5[y3,y7]=.5[y4,y6]; y4-y6=.6(y3-y7);
+transform skewit;
+ z5 transformed skewit=z5; (z5+right) transformed skewit = z5+right;
+ z3 transformed skewit=z3-u*right;
+skewit:=skewit transformed currenttransform;
+save currenttransform; transform currenttransform; currenttransform:=skewit;
+bulb(3,2,1); bulb(7,8,9); % bulbs
+filldraw stroke z3e{left}...z4e{(z5e-z4e)xscaled 1.1}
+ ..tension atleast 1 and atleast .8..{z5-z4}z5e; % upper stem
+filldraw stroke z5'e{z6-z5}..tension atleast.8 and atleast 1
+ ..{(z6e-z5'e)xscaled 1.1}z6e...{left}z7e; % lower stem
+endgroup;
+pickup rule.nib; autorounded;
+draw fullcircle scaled .5w shifted z5; % contour
+penlabels(1,2,3,4,5,6,7,8,9); endchar;
+
+cmchar "\displaystyle contour integral sign";
+beginchar(oct"111",18u#,0,200/54dh#);
+italcorr 2u#; % \TeX\ kerning between upper limit and lower limit
+adjust_fit(-4u#,-4u#-ic#); pickup fine.nib;
+begingroup
+numeric bulb_size, max_size;
+bulb_size=hround(.8[stem,curve]); max_size=hround(bold+.2dw);
+pos1(bulb_size,0); pos2(.75[hair,stem],0); pos3(vair,90); pos4(.4[stem,curve],180);
+pos5(max_size,180); pos5'(max_size,0); z5'=z5; pos6(.4[stem,curve],0);
+pos7(vair,-90); pos8(.75[hair,stem],-180); pos9(bulb_size,-180);
+rt x1r=hround(w-u); x9=w-x1; x7=w-x3=3u;
+x5=.5[x4,x6]; x4-x6=4.8u; lft x5r=hround(.5w-.5max_size);
+top y3r=0; bot y7r=-d;
+y9-.5bulb_size=vround(top y7l+.25bulb_size); y3-y1=y9-y7;
+y5=.5[y3,y7]=.5[y4,y6]; y4-y6=.6(y3-y7);
+transform skewit;
+ z5 transformed skewit=z5; (z5+right) transformed skewit = z5+right;
+ z3 transformed skewit=z3-4u*right;
+skewit:=skewit transformed currenttransform;
+save currenttransform; transform currenttransform; currenttransform:=skewit;
+bulb(3,2,1); bulb(7,8,9); % bulbs
+filldraw stroke z3e{left}...z4e{(z5e-z4e)xscaled 1.1}
+ ..tension atleast 1 and atleast .8..{z5-z4}z5e; % upper stem
+filldraw stroke z5'e{z6-z5}..tension atleast.8 and atleast 1
+ ..{(z6e-z5'e)xscaled 1.1}z6e...{left}z7e; % lower stem
+endgroup;
+pickup rule.nib; autorounded;
+draw fullcircle scaled .5w shifted z5; % contour
+penlabels(1,2,3,4,5,6,7,8,9); endchar;
+
+
+cmchar "\textstyle summation sign";
+beginchar(oct"120",19u#,0,10/6dh#);
+adjust_fit(0,0); pickup tiny.nib;
+numeric top_arm_thickness, bot_arm_thickness;
+top_arm_thickness=Vround(.7[rule_thickness,bold]);
+bot_arm_thickness=Vround(bold+dw);
+lft x1l=hround 2.2u; x1l=x2l; lft x4l=hround u; x3l-x1l=4/11(w-2u)-1.2u;
+top y1=0; bot y2=-top_arm_thickness-eps; bot y4=-d; y3=-.48d;
+numeric alpha[]; alpha1=((x3l-x1l)++(y2-y3))/(y2-y3);
+penpos1(alpha1*(curve-tiny),0); penpos2(alpha1*(curve-tiny),0);
+penpos3(alpha1*(curve-tiny),0);
+alpha2=diag_ratio(1,rule_thickness+dw-tiny,y3-y4,x3r-x4l);
+penpos4(alpha2*(rule_thickness+dw-tiny),0);
+z0=whatever[z2l,z3l]=z4l+whatever*(z3r-z4r);
+filldraw z1l--z2l--z0--z4l--z4r--z3r--z2r--z1r--cycle; % diagonals
+pickup crisp.nib; pos5(top_arm_thickness,90); pos6(hair,0);
+top y5r=0; x5=x1; rt x6r=hround(w-3u); y6=good.y(y5l)-eps;
+{{save serifs; boolean serifs; serifs:=false;
+arm(5,6,a,.45,-.25u); % upper arm and beak
+pos7(bot_arm_thickness,-90); pos8(hair,0);
+bot y7r=-d; z7l=whatever[z4,z3]; x7r:=x4; x8=x6+1.5u; y8=good.y(y7l)+eps;
+arm(7,8,b,.45,.25u);}}; % lower arm and beak
+penlabels(0,1,2,3,4,5,6,7,8); endchar;
+
+cmchar "\displaystyle summation sign";
+beginchar(oct"130",26u#,0,14/6dh#); padded 1/6dh#;
+adjust_fit(0,0); pickup tiny.nib;
+numeric top_arm_thickness, bot_arm_thickness, thick_stem, thin_stem;
+thick_stem=bold+4dw; thin_stem=rule_thickness+2dw;
+top_arm_thickness=Vround(bold-.5dw);
+bot_arm_thickness=Vround(bold+2.5dw);
+lft x1l=hround 3u; x1l=x2l; lft x4l=hround u; x3l-x1l=4/11(w-2u)-2u;
+top y1=0; bot y2=-top_arm_thickness-eps; bot y4=-d; y3=-.48d;
+numeric alpha[]; alpha1=((x3l-x1l)++(y2-y3))/(y2-y3);
+penpos1(alpha1*(thick_stem-tiny),0); penpos2(alpha1*(thick_stem-tiny),0);
+penpos3(alpha1*(thick_stem-tiny),0);
+alpha2=diag_ratio(1,thin_stem-tiny,y3-y4,x3r-x4l);
+penpos4(alpha2*(thin_stem-tiny),0);
+z0=whatever[z2l,z3l]=z4l+whatever*(z3r-z4r);
+filldraw z1l--z2l--z0--z4l--z4r--z3r--z2r--z1r--cycle; % diagonals
+pickup crisp.nib; pos5(top_arm_thickness,90); pos6(hair,0);
+top y5r=0; x5=x1; rt x6r=hround(w-4u); y6=good.y(y5l)-eps;
+{{save serifs; boolean serifs; serifs:=false;
+arm(5,6,a,.45,-.25u); % upper arm and beak
+pos7(bot_arm_thickness,-90); pos8(hair,0);
+bot y7r=-d; z7l=whatever[z4,z3]; x7r:=x4; x8=x6+2.5u; y8=good.y(y7l)+eps;
+arm(7,8,b,.45,.25u);}}; % lower arm and beak
+penlabels(0,1,2,3,4,5,6,7,8); endchar;
+
+cmchar "\textstyle product sign";
+beginchar(oct"121",18.5u#,0,10/6dh#);
+adjust_fit(.25u#,.25u#); pickup crisp.nib;
+numeric heavy_stem,light_stem;
+heavy_stem=hround(bold+dw); light_stem=rule_thickness;
+pos1(heavy_stem,0);pos2(heavy_stem,0); pos1'(heavy_stem,0);pos2'(heavy_stem,0);
+pos11(light_stem,90); pos12(light_stem,90); pos22(light_stem,90);
+lft x11=hround u; x1l-x11=x2l-x12=x22-x2r=hround cap_jut;
+x1=x1'=x2=x2';
+top y1=0; bot y2=-d; y11r=y1; y12l=y22l=y2;
+y11l-y1'=y2'-y12r=1.2bracket;
+filldraw z1r--z11r--serif_arc(11l,1'l)--reverse serif_arc(12r,2'l)
+ --z12l--z22l--serif_arc(22r,2'r)--cycle; % left stem and serifs
+pos3(heavy_stem,0);pos4(heavy_stem,0); pos3'(heavy_stem,0);pos4'(heavy_stem,0);
+pos13(light_stem,90); pos14(light_stem,90); pos24(light_stem,90);
+x3=x3'=x4=x4'=w-x1; x13=x14=w-x11; x24=w-x22;
+y3=y13r=y1; y3'=y1'; y4'=y2'; y4=y14l=y24l=y2;
+filldraw z3l--z13r--serif_arc(13l,3'r)--reverse serif_arc(14r,4'r)
+ --z14l--z24l--serif_arc(24r,4'l)--cycle; % right stem and serifs
+filldraw stroke z11e--z13e; % bar
+penlabels(1,1',2,2',3,3',4,4',11,12,13,14,22,24); endchar;
+
+cmchar "\displaystyle product sign";
+beginchar(oct"131",25.4u#,0,14/6dh#); padded 1/6dh#;
+adjust_fit(0,0); pickup crisp.nib;
+numeric heavy_stem,light_stem;
+heavy_stem=hround(bold+5dw); light_stem=Vround(rule_thickness+dw);
+pos1(heavy_stem,0);pos2(heavy_stem,0); pos1'(heavy_stem,0);pos2'(heavy_stem,0);
+pos11(light_stem,90); pos12(light_stem,90); pos22(light_stem,90);
+lft x11=hround u; x1l-x11=x2l-x12=x22-x2r=hround 1.6cap_jut;
+x1=x1'=x2=x2';
+top y1=0; bot y2=-d; y11r=y1; y12l=y22l=y2;
+y11l-y1'=y2'-y12r=1.8bracket;
+filldraw z1r--z11r--serif_arc(11l,1'l)--reverse serif_arc(12r,2'l)
+ --z12l--z22l--serif_arc(22r,2'r)--cycle; % left stem and serifs
+pos3(heavy_stem,0);pos4(heavy_stem,0); pos3'(heavy_stem,0);pos4'(heavy_stem,0);
+pos13(light_stem,90); pos14(light_stem,90); pos24(light_stem,90);
+x3=x3'=x4=x4'=w-x1; x13=x14=w-x11; x24=w-x22;
+y3=y13r=y1; y3'=y1'; y4'=y2'; y4=y14l=y24l=y2;
+filldraw z3l--z13r--serif_arc(13l,3'r)--reverse serif_arc(14r,4'r)
+ --z14l--z24l--serif_arc(24r,4'l)--cycle; % right stem and serifs
+filldraw stroke z11e--z13e; % bar
+penlabels(1,1',2,2',3,3',4,4',11,12,13,14,22,24); endchar;
+
+cmchar "\textstyle coproduct sign";
+beginchar(oct"140",18.5u#,0,10/6dh#);
+adjust_fit(.25u#,.25u#); pickup crisp.nib;
+numeric heavy_stem,light_stem;
+heavy_stem=hround(bold+dw); light_stem=rule_thickness;
+pos1(heavy_stem,0);pos2(heavy_stem,0); pos1'(heavy_stem,0);pos2'(heavy_stem,0);
+pos11(light_stem,-90); pos12(light_stem,-90); pos22(light_stem,-90);
+lft x11=hround u; x1l-x11=x2l-x12=x22-x2r=hround cap_jut;
+x1=x1'=x2=x2';
+bot y1=-d; top y2=0; y11r=y1; y12l=y22l=y2;
+y1'-y11l=y12r-y2'=1.2bracket;
+filldraw z1r--z11r--serif_arc(11l,1'l)--reverse serif_arc(12r,2'l)
+ --z12l--z22l--serif_arc(22r,2'r)--cycle; % left stem and serifs
+pos3(heavy_stem,0);pos4(heavy_stem,0); pos3'(heavy_stem,0);pos4'(heavy_stem,0);
+pos13(light_stem,-90); pos14(light_stem,-90); pos24(light_stem,-90);
+x3=x3'=x4=x4'=w-x1; x13=x14=w-x11; x24=w-x22;
+y3=y13r=y1; y3'=y1'; y4'=y2'; y4=y14l=y24l=y2;
+filldraw z3l--z13r--serif_arc(13l,3'r)--reverse serif_arc(14r,4'r)
+ --z14l--z24l--serif_arc(24r,4'l)--cycle; % right stem and serifs
+filldraw stroke z11e--z13e; % bar
+penlabels(1,1',2,2',3,3',4,4',11,12,13,14,22,24); endchar;
+
+cmchar "\displaystyle coproduct sign";
+beginchar(oct"141",25.4u#,0,14/6dh#); padded 1/6dh#;
+adjust_fit(0,0); pickup crisp.nib;
+numeric heavy_stem,light_stem;
+heavy_stem=hround(bold+5dw); light_stem=Vround(rule_thickness+dw);
+pos1(heavy_stem,0);pos2(heavy_stem,0); pos1'(heavy_stem,0);pos2'(heavy_stem,0);
+pos11(light_stem,-90); pos12(light_stem,-90); pos22(light_stem,-90);
+lft x11=hround u; x1l-x11=x2l-x12=x22-x2r=hround 1.6cap_jut;
+x1=x1'=x2=x2';
+bot y1=-d; top y2=0; y11r=y1; y12l=y22l=y2;
+y1'-y11l=y12r-y2'=1.8bracket;
+filldraw z1r--z11r--serif_arc(11l,1'l)--reverse serif_arc(12r,2'l)
+ --z12l--z22l--serif_arc(22r,2'r)--cycle; % left stem and serifs
+pos3(heavy_stem,0);pos4(heavy_stem,0); pos3'(heavy_stem,0);pos4'(heavy_stem,0);
+pos13(light_stem,-90); pos14(light_stem,-90); pos24(light_stem,-90);
+x3=x3'=x4=x4'=w-x1; x13=x14=w-x11; x24=w-x22;
+y3=y13r=y1; y3'=y1'; y4'=y2'; y4=y14l=y24l=y2;
+filldraw z3l--z13r--serif_arc(13l,3'r)--reverse serif_arc(14r,4'r)
+ --z14l--z24l--serif_arc(24r,4'l)--cycle; % right stem and serifs
+filldraw stroke z11e--z13e; % bar
+penlabels(1,1',2,2',3,3',4,4',11,12,13,14,22,24); endchar;
+
+cmchar "\big left curly brace";
+beginchar(oct"010",9u#,rule_thickness#,2dh#-rule_thickness#);
+adjust_fit(.75u#,.75u#); left_curly(hair,stem); endchar;
+
+cmchar "\Big left curly brace";
+beginchar(oct"012",10u#,rule_thickness#,3dh#-rule_thickness#);
+adjust_fit(u#,u#); left_curly(rule_thickness,.3[stem,bold]); endchar;
+
+cmchar "\bigg left curly brace";
+beginchar(oct"014",11u#,rule_thickness#,4dh#-rule_thickness#);
+adjust_fit(1.25u#,1.25u#); left_curly(rule_thickness+.2dw,.7[stem,bold]); endchar;
+
+cmchar "\Bigg left curly brace";
+beginchar(oct"016",11.5u#,rule_thickness#,5dh#-rule_thickness#);
+adjust_fit(1.5u#,1.5u#); left_curly(rule_thickness+.4dw,bold); endchar;
+
+cmchar "Extensible left curly brace---top";
+beginchar(oct"070",12u#,0,1.5dh#);
+adjust_fit(2u#,2u#); pickup fine.nib;
+numeric min_breadth; min_breadth=hround(rule_thickness+.6dw);
+pos1(min_breadth,0); pos1'(min_breadth,0);
+pos2(heavy_rule_thickness,0); pos3(heavy_rule_thickness,0);
+rt x1r=rt x1'r=hround(w-1.5u+.5min_breadth);
+lft x2l=lft x3l=hround(.5w-.5heavy_rule_thickness);
+top y1=h-1; y3=-d-eps; y2=.5[y1,y3]; y1-y1'=min_breadth-fine;
+filldraw z1l{3(x2l-x1l),y2-y1}...z2l---z3l
+ --z3r---z2r...{3(x1r-x2r),y1'-y2}z1'r--z1r--cycle; % stroke
+penlabels(1,2,3); endchar;
+
+cmchar "Extensible left curly brace---bottom";
+beginchar(oct"072",12u#,0,1.5dh#);
+adjust_fit(2u#,2u#); pickup fine.nib;
+numeric min_breadth; min_breadth=hround(rule_thickness+.6dw);
+pos7(min_breadth,0); pos7'(min_breadth,0);
+pos5(heavy_rule_thickness,0); pos6(heavy_rule_thickness,0);
+rt x7r=rt x7'r=hround(w-1.5u+.5min_breadth);
+lft x5l=lft x6l=hround(.5w-.5heavy_rule_thickness);
+y5=h+eps; bot y7=1-d; y6=.5[y5,y7]; y7'-y7=min_breadth-fine;
+filldraw z5l---z6l...{3(x7l-x6l),y7-y6}z7l
+ --z7r--z7'r{3(x6r-x7r),y6-y7'}...z6r---z5r--cycle; % stroke
+penlabels(5,6,7); endchar;
+
+cmchar "Extensible left curly brace---middle";
+beginchar(oct"074",12u#,0,3dh#);
+adjust_fit(2u#,2u#); pickup fine.nib;
+numeric min_breadth; min_breadth=hround(rule_thickness+.6dw);
+pos4(min_breadth,0); pos4'(min_breadth,0);
+forsuffixes $=2,3,5,6: pos$(heavy_rule_thickness,0); endfor
+lft x4l=lft x4'l=hround(1.5u-.5min_breadth);
+x2=x3=x5=x6; lft x2l=hround(.5w-.5heavy_rule_thickness);
+y2=h+eps; y6=-d-eps; .5[y4,y4']=.5[y2,y6]=.5[y3,y5];
+y3-y4=(y2-y6)/4; y4-y4'=min_breadth-fine;
+filldraw z2l---z3l...{3(x4l-x3l),y4-y3}z4l
+ --z4'l{3(x5l-x4l),y5-y4'}...z5l---z6l--z6r---z5r
+ ...{3(x4r-x5r),.5[y4,y4']-y5}.5[z4r,z4'r]{3(x3r-x4r),y3-.5[y4,y4']}
+ ...z3r---z2r--cycle; % stroke
+penlabels(2,3,4,5,6); endchar;
+
+cmchar "Extensible curly brace---extension module";
+beginchar(oct"076",12u#,0,.5dh#);
+adjust_fit(2u#,2u#); pickup fine.nib;
+pos2(heavy_rule_thickness,0); pos3(heavy_rule_thickness,0);
+lft x2l=lft x3l=hround(.5w-.5heavy_rule_thickness); y2=h+eps; y3=-d-eps;
+filldraw stroke z2e--z3e; % stem
+penlabels(2,3); endchar;
+
+cmchar "\big right curly brace";
+beginchar(oct"011",9u#,rule_thickness#,2dh#-rule_thickness#);
+adjust_fit(.75u#,.75u#); right_curly(hair,stem); endchar;
+
+cmchar "\Big right curly brace";
+beginchar(oct"013",10u#,rule_thickness#,3dh#-rule_thickness#);
+adjust_fit(u#,u#); right_curly(rule_thickness,.3[stem,bold]); endchar;
+
+cmchar "\bigg right curly brace";
+beginchar(oct"015",11u#,rule_thickness#,4dh#-rule_thickness#);
+adjust_fit(1.25u#,1.25u#); right_curly(rule_thickness+.2dw,.7[stem,bold]); endchar;
+
+cmchar "\Bigg right curly brace";
+beginchar(oct"017",11.5u#,rule_thickness#,5dh#-rule_thickness#);
+adjust_fit(1.5u#,1.5u#); right_curly(rule_thickness+.4dw,bold); endchar;
+
+cmchar "Extensible right curly brace---top";
+beginchar(oct"071",12u#,0,1.5dh#);
+adjust_fit(2u#,2u#); pickup fine.nib;
+numeric min_breadth; min_breadth=hround(rule_thickness+.6dw);
+pos1(min_breadth,0); pos1'(min_breadth,0);
+pos2(heavy_rule_thickness,0); pos3(heavy_rule_thickness,0);
+lft x1l=lft x1'l=hround(1.5u-.5min_breadth);
+lft x2l=lft x3l=hround(.5w-.5heavy_rule_thickness);
+top y1=h-1; y3=-d-eps; y2=.5[y1,y3]; y1-y1'=min_breadth-fine;
+filldraw z1r{3(x2r-x1r),y2-y1}...z2r---z3r
+ --z3l---z2l...{3(x1l-x2l),y1'-y2}z1'l--z1l--cycle; % stroke
+penlabels(1,2,3); endchar;
+
+cmchar "Extensible right curly brace---bottom";
+beginchar(oct"073",12u#,0,1.5dh#);
+adjust_fit(2u#,2u#); pickup fine.nib;
+numeric min_breadth; min_breadth=hround(rule_thickness+.6dw);
+pos7(min_breadth,0); pos7'(min_breadth,0);
+pos5(heavy_rule_thickness,0); pos6(heavy_rule_thickness,0);
+lft x7l=lft x7'l=hround(1.5u-.5min_breadth);
+lft x5l=lft x6l=hround(.5w-.5heavy_rule_thickness);
+y5=h+eps; bot y7=1-d; y6=.5[y5,y7]; y7'-y7=min_breadth-fine;
+filldraw z5r---z6r...{3(x7r-x6r),y7-y6}z7r
+ --z7l--z7'l{3(x6l-x7l),y6-y7'}...z6l---z5l--cycle; % stroke
+penlabels(5,6,7); endchar;
+
+cmchar "Extensible right curly brace---middle";
+beginchar(oct"075",12u#,0,3dh#);
+adjust_fit(2u#,2u#); pickup fine.nib;
+numeric min_breadth; min_breadth=hround(rule_thickness+.6dw);
+pos4(min_breadth,0); pos4'(min_breadth,0);
+forsuffixes $=2,3,5,6: pos$(heavy_rule_thickness,0); endfor
+rt x4r=rt x4'r=hround(w-1.5u+.5min_breadth);
+x2=x3=x5=x6; lft x2l=hround(.5w-.5heavy_rule_thickness);
+y2=h+eps; y6=-d-eps; .5[y4,y4']=.5[y2,y6]=.5[y3,y5];
+y3-y4=(y2-y6)/4; y4-y4'=min_breadth-fine;
+filldraw z2r---z3r...{3(x4r-x3r),y4-y3}z4r
+ --z4'r{3(x5r-x4r),y5-y4'}...z5r---z6r--z6l---z5l
+ ...{3(x4l-x5l),.5[y4,y4']-y5}.5[z4l,z4'l]{3(x3l-x4l),y3-.5[y4,y4']}
+ ...z3l---z2l--cycle; % stroke
+penlabels(2,3,4,5,6); endchar;
+
+cmchar "Horizontal curly brace tip---down and left";
+beginchar(oct"172",.75dh#,heavy_rule_thickness#,0);
+adjust_fit(0,0); pickup fine.nib;
+numeric min_breadth; min_breadth=vround(rule_thickness+.6dw);
+pos1(min_breadth,90); pos1'(min_breadth,90);
+pos2(heavy_rule_thickness,90);
+bot y2l=0; bot y1l=bot y1'l=vround(y2-4.5u-.5min_breadth);
+.5[x1,x1']=-eps; x2=w+shrink_fit+eps; x1'-x1=min_breadth-fine;
+filldraw z1r{x2-x1,3(y2r-y1r)}...{right}z2r
+ --z2l{left}...{x1'-x2,3(y1-y2)}z1'l--z1l--cycle; % stroke
+penlabels(1,2); endchar;
+
+cmchar "Horizontal curly brace tip---down and right";
+beginchar(oct"173",.75dh#,heavy_rule_thickness#,0);
+adjust_fit(0,0); pickup fine.nib;
+numeric min_breadth; min_breadth=vround(rule_thickness+.6dw);
+pos1(min_breadth,90); pos1'(min_breadth,90);
+pos2(heavy_rule_thickness,90);
+bot y2l=0; bot y1l=bot y1'l=vround(y2-4.5u-.5min_breadth);
+.5[x1,x1']=w+shrink_fit+eps; x2=-eps; x1-x1'=min_breadth-fine;
+filldraw z1r{x2-x1,3(y2r-y1r)}...{left}z2r
+ --z2l{right}...{x1'-x2,3(y1-y2)}z1'l--z1l--cycle; % stroke
+penlabels(1,2); endchar;
+
+cmchar "Horizontal curly brace tip---up and left";
+beginchar(oct"174",.75dh#,heavy_rule_thickness#,0);
+adjust_fit(0,0); pickup fine.nib;
+numeric min_breadth; min_breadth=vround(rule_thickness+.6dw);
+pos1(min_breadth,90); pos1'(min_breadth,90);
+pos2(heavy_rule_thickness,90);
+bot y2l=0; top y1r=top y1'r=vround(y2+4.5u+.5min_breadth);
+.5[x1,x1']=-eps; x2=w+shrink_fit+eps; x1'-x1=min_breadth-fine;
+filldraw z1l{x2-x1,3(y2l-y1l)}...{right}z2l
+ --z2r{left}...{x1'-x2,3(y1-y2)}z1'r--z1r--cycle; % stroke
+penlabels(1,2); endchar;
+
+cmchar "Horizontal curly brace tip---up and right";
+beginchar(oct"175",.75dh#,heavy_rule_thickness#,0);
+adjust_fit(0,0); pickup fine.nib;
+numeric min_breadth; min_breadth=vround(rule_thickness+.6dw);
+pos1(min_breadth,90); pos1'(min_breadth,90);
+pos2(heavy_rule_thickness,90);
+bot y2l=0; top y1r=top y1'r=vround(y2+4.5u+.5min_breadth);
+.5[x1,x1']=w+shrink_fit+eps; x2=-eps; x1-x1'=min_breadth-fine;
+filldraw z1l{x2-x1,3(y2l-y1l)}...{left}z2l
+ --z2r{right}...{x1'-x2,3(y1-y2)}z1'r--z1r--cycle; % stroke
+penlabels(1,2); endchar;
+
+cmchar "Leftward arrow";
+compute_spread(.45x_height#,.55x_height#);
+beginchar(oct"040",18u#,v_center(spread#+rule_thickness#));
+adjust_fit(0,0); pickup crisp.nib;
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0+3u+eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+numeric t; path p; p=z4r{z9-z4}..z6r;
+t=xpart(p intersectiontimes((0,y2l)--(w,y2l))); x2=xpart point t of p;
+filldraw z0..{z4-z9}z4l--subpath (0,t) of\\(z4r{z9-z4}..z6r)
+ --z2l---z1l..z1r---z2r--subpath (t,0) of\\(z3r{z9-z3}..z5r)
+ --z3l{z9-z3}..z0 & cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6,9); endchar;
+
+cmchar "Rightward arrow";
+compute_spread(.45x_height#,.55x_height#);
+beginchar(oct"041",18u#,v_center(spread#+rule_thickness#));
+adjust_fit(0,0); pickup crisp.nib;
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0-3u-eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+numeric t; path p; p=z4l{z9-z4}..z6r;
+t=xpart(p intersectiontimes((0,y2l)--(w,y2l))); x2=xpart point t of p;
+filldraw z0..{z4-z9}z4r--subpath (0,t) of\\(z4l{z9-z4}..z6r)
+ --z2l---z1l..z1r---z2r--subpath (t,0) of\\(z3l{z9-z3}..z5r)
+ --z3r{z9-z3}..z0 & cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6,9); endchar;
+
+cmchar "Left-and-right arrow";
+compute_spread(.45x_height#,.55x_height#);
+beginchar(oct"044",18u#,
+ v_center(if monospace:.6asc_height#+fi spread#+rule_thickness#));
+adjust_fit(0,0); pickup crisp.nib;
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis if monospace:+vround.3asc_height fi; lft x0=hround u;
+if monospace: x1+.5rule_thickness=hround(w-u) else: x1=.5w fi;
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0+3u+eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+numeric t; path p; p=z4r{z9-z4}..z6r;
+t=xpart(p intersectiontimes((0,y2l)--(w,y2l))); x2=xpart point t of p;
+filldraw z0..{z4-z9}z4l--subpath (0,t) of\\(z4r{z9-z4}..z6r)
+ --z2l---z1l..z1r---z2r--subpath (t,0) of\\(z3r{z9-z3}..z5r)
+ --z3l{z9-z3}..z0 & cycle; % left arrowhead and stem
+pos11(rule_thickness,90); pos12(rule_thickness,90); pos13(bar,0); pos14(bar,0);
+y10=y11=y12=math_axis if monospace:-vround.3asc_height fi;
+rt x10=hround(w-u);
+if monospace: x11-.5rule_thickness=hround u else: x11=.5w fi;
+y13-y10=y10-y14=.36asc_height+eps; x13=x14=x10-3u-eps;
+pos15(bar,angle(z14-z10)); z15l=z10; pos16(bar,angle(z13-z10)); z16l=z10;
+z19=.381966[.5[z13,z14],z10];
+numeric t; path p; p=z14l{z19-z14}..z16r;
+t=xpart(p intersectiontimes((0,y12l)--(w,y12l))); x12=xpart point t of p;
+filldraw z10..{z14-z19}z14r--subpath (0,t) of\\(z14l{z19-z14}..z16r)
+ --z12l---z11l..z11r---z12r--subpath (t,0) of\\(z13l{z19-z13}..z15r)
+ --z13r{z19-z13}..z10 & cycle; % right arrowhead and stem
+penlabels(0,1,2,3,4,5,6,9,10,11,12,13,14,15,16,19); endchar;
+
+cmchar "Upward arrow";
+beginchar(oct"042",9u#,asc_height#,asc_depth#);
+italcorr .76asc_height#*slant+.5crisp#-u#;
+adjust_fit(0,0); pickup crisp.nib;
+pos1(rule_thickness,0); pos2(rule_thickness,0);
+pos3(bar,90); pos4(bar,90);
+lft x1l=hround(.5w-.5rule_thickness); y1-.5rule_thickness=-d;
+x0=x1=x2; top y0=h; x0-x3=x4-x0=4u+eps;
+y3=y4=y0-.24asc_height-eps;
+pos5(bar,angle(z4-z0)); z5l=z0;
+pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+numeric t; path p; p=z4l{z9-z4}..z6r;
+t=xpart(p intersectiontimes((x2r,-d)--(x2r,h))); y2=ypart point t of p;
+filldraw z0..{z4-z9}z4r--subpath (0,t) of\\(z4l{z9-z4}..z6r)
+ --z2r---z1r..z1l---z2l--subpath (t,0) of\\(z3l{z9-z3}..z5r)
+ --z3r{z9-z3}..z0 & cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6,9); endchar;
+
+cmchar "Downward arrow";
+beginchar(oct"043",9u#,asc_height#,asc_depth#);
+adjust_fit(0,0); pickup crisp.nib;
+pos1(rule_thickness,0); pos2(rule_thickness,0);
+pos3(bar,90); pos4(bar,90);
+lft x1l=hround(.5w-.5rule_thickness); y1+.5rule_thickness=h;
+x0=x1=x2; bot y0=-d; x0-x3=x4-x0=4u+eps;
+y3=y4=y0+.24asc_height+eps;
+pos5(bar,angle(z4-z0)); z5l=z0;
+pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+numeric t; path p; p=z4r{z9-z4}..z6r;
+t=xpart(p intersectiontimes((x2r,-d)--(x2r,h))); y2=ypart point t of p;
+filldraw z0..{z4-z9}z4l--subpath (0,t) of\\(z4r{z9-z4}..z6r)
+ --z2r---z1r..z1l---z2l--subpath (t,0) of\\(z3r{z9-z3}..z5r)
+ --z3l{z9-z3}..z0 & cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6,9); endchar;
+
+cmchar "Up-and-down arrow";
+beginchar(oct"154",9u#,body_height#,paren_depth#);
+adjust_fit(0,0); pickup crisp.nib;
+pos1(rule_thickness,0); pos2(rule_thickness,0);
+pos3(bar,90); pos4(bar,90);
+lft x1l=hround(.5w-.5rule_thickness); y1=.5[-d,h];
+x0=x1=x2; bot y0=-d-o; x0-x3=x4-x0=4u+eps;
+y3=y4=y0+.24asc_height+eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+numeric t; path p; p=z4r{z9-z4}..z6r;
+t=xpart(p intersectiontimes((x2r,-d)--(x2r,h))); y2=ypart point t of p;
+filldraw z0..{z4-z9}z4l--subpath (0,t) of\\(z4r{z9-z4}..z6r)
+ --z2r---z1r..z1l---z2l--subpath (t,0) of\\(z3r{z9-z3}..z5r)
+ --z3l{z9-z3}..z0 & cycle; % lower arrowhead and stem
+pos11(rule_thickness,0); pos12(rule_thickness,0); z11=z1;
+pos13(bar,90); pos14(bar,90);
+x10=x11=x12; top y10=h+o; x10-x13=x14-x10=4u+eps;
+y13=y14=y10-.24asc_height-eps;
+pos15(bar,angle(z14-z10)); z15l=z10; pos16(bar,angle(z13-z10)); z16l=z10;
+z19=.381966[.5[z13,z14],z10];
+numeric t; path p; p=z14l{z19-z14}..z16r;
+t=xpart(p intersectiontimes((x12r,-d)--(x12r,h))); y12=ypart point t of p;
+filldraw z10..{z14-z19}z14r--subpath (0,t) of\\(z14l{z19-z14}..z16r)
+ --z12r---z11r..z11l---z12l--subpath (t,0) of\\(z13l{z19-z13}..z15r)
+ --z13r{z19-z13}..z10 & cycle; % upper arrowhead and stem
+penlabels(0,1,2,3,4,5,6,9,10,11,12,13,14,15,16,19); endchar;
+
+cmchar "Leftward top half arrow";
+compute_spread(.45x_height#,.55x_height#);
+beginchar(oct"030",18u#,v_center(spread#+rule_thickness#));
+adjust_fit(0,0); pickup crisp.nib;
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0+3u+eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+numeric t; path p; p=z4r..{2(x0-x4),y0-y4}z6r;
+t=xpart(p intersectiontimes((0,y2l)--(w,y2l))); x2=xpart point t of p;
+filldraw z0--(x0,y2l)---z1l..z1r---z2r
+ ..subpath (t,0) of\\(z3r..{2(x0-x3),y0-y3}z5r)
+ --z3l..{2(x0-x3),y0-y3}cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6); endchar;
+
+cmchar "Leftward bottom half arrow";
+compute_spread(.45x_height#,.55x_height#);
+beginchar(oct"031",18u#,v_center(spread#+rule_thickness#));
+adjust_fit(0,0); pickup crisp.nib;
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0+3u+eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+numeric t; path p; p=z4r..{2(x0-x4),y0-y4}z6r;
+t=xpart(p intersectiontimes((0,y2l)--(w,y2l))); x2=xpart point t of p;
+filldraw z0{2(x4-x0),y4-y0}..z4l
+ --subpath (0,t) of\\(z4r..{2(x0-x4),y0-y4}z6r)
+ ..z2l---z1l..z1r---(x0,y2r)--cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6); endchar;
+
+cmchar "Rightward top half arrow";
+compute_spread(.45x_height#,.55x_height#);
+beginchar(oct"032",18u#,v_center(spread#+rule_thickness#));
+adjust_fit(0,0); pickup crisp.nib;
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0-3u-eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+numeric t; path p; p=z4l..{2(x0-x4),y0-y4}z6r;
+t=xpart(p intersectiontimes((0,y2l)--(w,y2l))); x2=xpart point t of p;
+filldraw z0--(x0,y2l)---z1l..z1r---z2r
+ ..subpath (t,0) of\\(z3l..{2(x0-x3),y0-y3}z5r)
+ --z3r..{2(x0-x3),y0-y3}cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6); endchar;
+
+cmchar "Rightward bottom half arrow";
+compute_spread(.45x_height#,.55x_height#);
+beginchar(oct"033",18u#,v_center(spread#+rule_thickness#));
+adjust_fit(0,0); pickup crisp.nib;
+pos1(rule_thickness,90); pos2(rule_thickness,90); pos3(bar,0); pos4(bar,0);
+y0=y1=y2=math_axis; x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y3-y0=y0-y4=.36asc_height+eps; x3=x4=x0-3u-eps;
+pos5(bar,angle(z4-z0)); z5l=z0; pos6(bar,angle(z3-z0)); z6l=z0;
+numeric t; path p; p=z4l..{2(x0-x4),y0-y4}z6r;
+t=xpart(p intersectiontimes((0,y2l)--(w,y2l))); x2=xpart point t of p;
+filldraw z0{2(x4-x0),y4-y0}..z4r
+ --subpath (0,t) of\\(z4l..{2(x0-x4),y0-y4}z6r)
+ ..z2l---z1l..z1r---(x0,y2r)--cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6); endchar;
+
+cmchar "Northeast arrow";
+beginchar(oct"045",18u#,asc_height#,asc_depth#);
+adjust_fit(0,0); pickup crisp.nib;
+x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y1-.5rule_thickness=-d; top y0=h;
+numeric theta,delta; theta=angle(z0-z1); delta=3u++.24asc_height;
+if abs(theta-45)<2.5: theta:=45; y1:=y0+x1-x0; fi % near-45$^\circ$ angle
+pos1(rule_thickness,90+theta); pos2(rule_thickness,90+theta);
+pos3(bar,theta); pos4(bar,theta);
+z3-z0=(-3u,.36asc_height) rotated theta;
+z4-z0=(-3u,-.36asc_height) rotated theta;
+pos5(bar,-90); z5l=z0; pos6(bar,-180); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+numeric t; path p; p=z4l{z9-z4}..z6r;
+t=xpart(p intersectiontimes (z1l--(z1l+2(z0-z1)))); z2l=point t of p;
+filldraw z0..{z4-z9}z4r--subpath (0,t) of\\(z4l{z9-z4}..z6r)
+ --z2l---z1l..z1r---z2r--subpath (t,0) of\\(z3l{z9-z3}..z5r)
+ --z3r{z9-z3}..z0 & cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6,9); endchar;
+
+cmchar "Southeast arrow";
+beginchar(oct"046",18u#,asc_height#,asc_depth#);
+adjust_fit(0,0); pickup crisp.nib;
+x1-.5rule_thickness=hround u; rt x0=hround(w-u);
+y1+.5rule_thickness=h; bot y0=-d;
+numeric theta,delta; theta=angle(z0-z1); delta=3u++.24asc_height;
+if abs(theta+45)<2.5: theta:=-45; y1:=y0+x0-x1; fi % near-45$^\circ$ angle
+pos1(rule_thickness,90+theta); pos2(rule_thickness,90+theta);
+pos3(bar,theta); pos4(bar,theta);
+z3-z0=(-3u,.36asc_height) rotated theta;
+z4-z0=(-3u,-.36asc_height) rotated theta;
+pos5(bar,180); z5l=z0; pos6(bar,90); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+numeric t; path p; p=z4l{z9-z4}..z6r;
+t=xpart(p intersectiontimes (z1l--(z1l+2(z0-z1)))); z2l=point t of p;
+filldraw z0..{z4-z9}z4r
+ --subpath (0,t) of\\(z4l{z9-z4}..z6r)
+ --z2l---z1l..z1r---z2r
+ --subpath (t,0) of\\(z3l{z9-z3}..z5r)
+ --z3r{z9-z3}..z0 & cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6,9); endchar;
+
+cmchar "Northwest arrow";
+beginchar(oct"055",18u#,asc_height#,asc_depth#);
+adjust_fit(0,0); pickup crisp.nib;
+x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y1-.5rule_thickness=-d; top y0=h;
+numeric theta,delta; theta=angle(z1-z0); delta=3u++.24asc_height;
+if abs(theta+45)<2.5: theta:=-45; y1:=y0+x0-x1; fi % near-45$^\circ$ angle
+pos1(rule_thickness,-90+theta); pos2(rule_thickness,-90+theta);
+pos3(bar,-180+theta); pos4(bar,-180+theta);
+z4-z0=(3u,.36asc_height) rotated theta;
+z3-z0=(3u,-.36asc_height) rotated theta;
+pos5(bar,0); z5l=z0; pos6(bar,-90); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+numeric t; path p; p=z4l{z9-z4}..z6r;
+t=xpart(p intersectiontimes (z1l--(z1l+2(z0-z1)))); z2l=point t of p;
+filldraw z0..{z4-z9}z4r--subpath (0,t) of\\(z4l{z9-z4}..z6r)
+ --z2l---z1l..z1r---z2r--subpath (t,0) of\\(z3l{z9-z3}..z5r)
+ --z3r{z9-z3}..z0 & cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6,9); endchar;
+
+cmchar "Southwest arrow";
+beginchar(oct"056",18u#,asc_height#,asc_depth#);
+adjust_fit(0,0); pickup crisp.nib;
+x1+.5rule_thickness=hround(w-u); lft x0=hround u;
+y1+.5rule_thickness=h; bot y0=-d;
+numeric theta,delta; theta=angle(z1-z0); delta=3u++.24asc_height;
+if abs(theta-45)<2.5: theta:=45; y1:=y0+x1-x0; fi % near-45$^\circ$ angle
+pos1(rule_thickness,-90+theta); pos2(rule_thickness,-90+theta);
+pos3(bar,-180+theta); pos4(bar,-180+theta);
+z4-z0=(3u,.36asc_height) rotated theta;
+z3-z0=(3u,-.36asc_height) rotated theta;
+pos5(bar,90); z5l=z0; pos6(bar,0); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+numeric t; path p; p=z4l{z9-z4}..z6r;
+t=xpart(p intersectiontimes (z1l--(z1l+2(z0-z1)))); z2l=point t of p;
+filldraw z0..{z4-z9}z4r
+ --subpath (0,t) of\\(z4l{z9-z4}..z6r)
+ --z2l---z1l..z1r---z2r
+ --subpath (t,0) of\\(z3l{z9-z3}..z5r)
+ --z3r{z9-z3}..z0 & cycle; % arrowhead and stem
+penlabels(0,1,2,3,4,5,6,9); endchar;
+
+cmchar "Double leftward arrow";
+compute_spread(.45x_height#,.55x_height#);
+beginchar(oct"050",18u#,v_center(spread#+rule_thickness#));
+adjust_fit(0,0); pickup rule.nib;
+lft x7=hround u-eps; x8=x7; x1=x2=w-x7;
+y1=y7; y2=y8; y1-y2=spread; .5[y1,y2]=math_axis;
+draw z1--z7; draw z2--z8; % bars
+pickup crisp.nib; lft x0=hround u-eps; y0=good.y math_axis;
+pos3(rule_thickness,0); pos4(rule_thickness,0);
+y3-y1=y2-y4=.36asc_height+eps; x3=x4=x0+6u+eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+erase filldraw z0..{z3-z9}z3--(0,y3)
+ --(0,y4)--z4{z9-z4}..z0 & cycle; % erase excess
+numeric t; path p; p=z4r{z9-z4}..z6r;
+t=xpart(p intersectiontimes((0,y0)--(w,y0)));
+filldraw z0..{z4-z9}z4l--subpath (0,t) of\\(z4r{z9-z4}..z6r)
+ --subpath (t,0) of\\(z3r{z9-z3}..z5r)--z3l{z9-z3}..z0 & cycle; % arrowhead
+penlabels(0,1,2,3,4,5,6,7,8,9); endchar;
+
+cmchar "Double rightward arrow";
+compute_spread(.45x_height#,.55x_height#);
+beginchar(oct"051",18u#,v_center(spread#+rule_thickness#));
+adjust_fit(0,0); pickup rule.nib;
+lft x1=hround u-eps; x2=x1; x7=x8=w-x1;
+y1=y7; y2=y8; y1-y2=spread; .5[y1,y2]=math_axis;
+draw z1--z7; draw z2--z8; % bars
+pickup crisp.nib; rt x0=hround(w-u)+eps; y0=good.y math_axis;
+pos3(rule_thickness,0); pos4(rule_thickness,0);
+y3-y1=y2-y4=.36asc_height+eps; x3=x4=x0-6u-eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+erase filldraw z0..{z3-z9}z3--(w,y3)
+ --(w,y4)--z4{z9-z4}..z0 & cycle; % erase excess
+numeric t; path p; p=z4l{z9-z4}..z6r;
+t=xpart(p intersectiontimes((0,y0)--(w,y0)));
+filldraw z0..{z4-z9}z4r--subpath (0,t) of\\(z4l{z9-z4}..z6r)
+ --subpath (t,0) of\\(z3l{z9-z3}..z5r)--z3r{z9-z3}..z0 & cycle; % arrowhead
+penlabels(0,1,2,3,4,5,6,7,8,9); endchar;
+
+cmchar "Double upward arrow";
+beginchar(oct"052",12u#,asc_height#,asc_depth#);
+adjust_fit(0,0); pickup rule.nib;
+compute_spread(.45x_height#,.55x_height#);
+bot y1=bot y2=-d-eps; top y7=h; y8=y7;
+x1=x7; x2=x8; x1-x2=spread; .5[x1,x2]=crisp.lft hround(crisp.rt .5w);
+draw z1--z7; draw z2--z8; % bars
+pickup crisp.nib; top y0=h+eps; x0=good.x .5w;
+pos3(rule_thickness,90); pos4(rule_thickness,90);
+x2-x3=x4-x1=4u+eps; y3=y4=y0-.48asc_height-eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+erase filldraw z0..{z3-z9}z3--(x3,h)
+ --(x4,h)--z4{z9-z4}..z0 & cycle; % erase excess
+numeric t; path p; p=z4l{z9-z4}..z6r;
+t=xpart(p intersectiontimes((x0,-d)--(x0,h)));
+filldraw z0..{z4-z9}z4r--subpath (0,t) of\\(z4l{z9-z4}..z6r)
+ --subpath (t,0) of\\(z3l{z9-z3}..z5r)--z3r{z9-z3}..z0 & cycle; % arrowhead
+penlabels(0,1,2,3,4,5,6,7,8,9); endchar;
+
+cmchar "Double downward arrow";
+beginchar(oct"053",12u#,asc_height#,asc_depth#);
+adjust_fit(0,0); pickup rule.nib;
+compute_spread(.45x_height#,.55x_height#);
+top y1=top y2=h+eps; bot y7=-d; y8=y7;
+x1=x7; x2=x8; x1-x2=spread; .5[x1,x2]=crisp.lft hround(crisp.rt .5w);
+draw z1--z7; draw z2--z8; % bars
+pickup crisp.nib; bot y0=-d-eps; x0=good.x .5w;
+pos3(rule_thickness,90); pos4(rule_thickness,90);
+x2-x3=x4-x1=4u+eps; y3=y4=y0+.48asc_height-eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+erase filldraw z0..{z3-z9}z3--(x3,-d)
+ --(x4,-d)--z4{z9-z4}..z0 & cycle; % erase excess
+numeric t; path p; p=z4r{z9-z4}..z6r;
+t=xpart(p intersectiontimes((x0,-d)--(x0,h)));
+filldraw z0..{z4-z9}z4l--subpath (0,t) of\\(z4r{z9-z4}..z6r)
+ --subpath (t,0) of\\(z3r{z9-z3}..z5r)--z3l{z9-z3}..z0 & cycle; % arrowhead
+penlabels(0,1,2,3,4,5,6,7,8,9); endchar;
+
+cmchar "Double left-and-right arrow";
+compute_spread(.45x_height#,.55x_height#);
+beginchar(oct"054",18u#,v_center(spread#+rule_thickness#));
+adjust_fit(if monospace: -u#,-u# else: 0,0 fi); pickup rule.nib;
+lft x7=hround u-eps; x8=x7; x1=x2=w-x7;
+y1=y7; y2=y8; y1-y2=spread; .5[y1,y2]=math_axis;
+draw z1--z7; draw z2--z8; % bars
+pickup crisp.nib; lft x0=hround u-o; y0=good.y math_axis;
+pos3(rule_thickness,0); pos4(rule_thickness,0);
+y3-y1=y2-y4=.36asc_height+eps; x3=x4=x0+5u+eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+erase filldraw z0..{z3-z9}z3--(0,y3)
+ --(0,y4)--z4{z9-z4}..z0 & cycle; % erase excess at left
+numeric t; path p; p=z4r{z9-z4}..z6r;
+t=xpart(p intersectiontimes((0,y0)--(w,y0)));
+filldraw z0..{z4-z9}z4l--subpath (0,t) of\\(z4r{z9-z4}..z6r)
+ --subpath (t,0) of\\(z3r{z9-z3}..z5r)
+ --z3l{z9-z3}..z0 & cycle; % left arrowhead
+rt x10=hround(w-u)+o; y10=good.y math_axis;
+pos13(rule_thickness,0); pos14(rule_thickness,0);
+y13=y3; y14=y4; x13=x14=x10-5u-eps;
+pos15(rule_thickness,angle(z14-z10)); z15l=z10;
+pos16(rule_thickness,angle(z13-z10)); z16l=z10;
+z19=.381966[.5[z13,z14],z10];
+erase filldraw z10..{z13-z19}z13--(w,y13)
+ --(w,y14)--z14{z19-z14}..z10 & cycle; % erase excess at right
+numeric t; path p; p=z14l{z19-z14}..z16r;
+t=xpart(p intersectiontimes((0,y10)--(w,y10)));
+filldraw z10..{z14-z19}z14r--subpath (0,t) of\\(z14l{z19-z14}..z16r)
+ --subpath (t,0) of\\(z13l{z19-z13}..z15r)
+ --z13r{z19-z13}..z10 & cycle; % right arrowhead
+penlabels(0,1,2,3,4,5,6,7,8,9,10,13,14,15,16,19); endchar;
+
+cmchar "Double up-and-down arrow";
+beginchar(oct"155",12u#,body_height#,paren_depth#);
+adjust_fit(0,0); pickup rule.nib;
+compute_spread(.45x_height#,.55x_height#);
+bot y1=bot y2=-d-eps; top y7=h; y8=y7;
+x1=x7; x2=x8; x1-x2=spread; .5[x1,x2]=crisp.lft hround(crisp.rt .5w);
+draw z1--z7; draw z2--z8; % bars
+pickup crisp.nib; top y0=h+o; x0=good.x .5w;
+pos3(rule_thickness,90); pos4(rule_thickness,90);
+x2-x3=x4-x1=4u+eps; y3=y4=y0-.4asc_height-eps;
+pos5(rule_thickness,angle(z4-z0)); z5l=z0;
+pos6(rule_thickness,angle(z3-z0)); z6l=z0;
+z9=.381966[.5[z3,z4],z0];
+erase filldraw z0..{z3-z9}z3--(x3,h)
+ --(x4,h)--z4{z9-z4}..z0 & cycle; % erase excess at top
+numeric t; path p; p=z4l{z9-z4}..z6r;
+t=xpart(p intersectiontimes((x0,-d)--(x0,h)));
+filldraw z0..{z4-z9}z4r--subpath (0,t) of\\(z4l{z9-z4}..z6r)
+ --subpath (t,0) of\\(z3l{z9-z3}..z5r)
+ --z3r{z9-z3}..z0 & cycle; % top arrowhead
+bot y10=-d-o; x10=good.x .5w;
+pos13(rule_thickness,90); pos14(rule_thickness,90);
+x13=x3; x14=x4; y13=y14=y10+.4asc_height-eps;
+pos15(rule_thickness,angle(z14-z10)); z15l=z10;
+pos16(rule_thickness,angle(z13-z10)); z16l=z10;
+z19=.381966[.5[z13,z14],z10];
+erase filldraw z10..{z13-z19}z13--(x13,-d)
+ --(x14,-d)--z14{z19-z14}..z10 & cycle; % erase excess at bottom
+numeric t; path p; p=z14r{z19-z14}..z16r;
+t=xpart(p intersectiontimes((x10,-d)--(x10,h)));
+filldraw z10..{z14-z19}z14l--subpath (0,t) of\\(z14r{z19-z14}..z16r)
+ --subpath (t,0) of\\(z13r{z19-z13}..z15r)
+ --z13l{z19-z13}..z10 & cycle; % bottom arrowhead
+penlabels(0,1,2,3,4,5,6,7,8,9,13,14,15,16,19); endchar;
+
+cmchar "Infinity";
+beginchar(oct"061",18u#,x_height#,0);
+italcorr .7x_height#*slant-.5u#;
+adjust_fit(0,0); pickup fine.nib;
+numeric theta,phi;
+if monospace: phi=-theta=90;
+else: phi=angle(4u,h); theta=angle(6u,-h); fi
+pos1(.2[vair,curve],180); pos2(.6[vair,curve],90+.5theta); pos3(curve,90+theta);
+pos4(curve,90+theta); pos5(.6[vair,curve],90+.5theta);
+pos6(.2[vair,curve],180); pos7(.3[vair,curve],270+.5phi);
+pos8(.35[vair,curve],270+phi); pos9(.3[vair,curve],270+.5phi);
+lft x1r=w-rt x6l=hround u; x2=x9=.4[x1,x8]; x5=x7=.6[x8,x6];
+if monospace: x2l:=x2r; x5r:=x5l; x7r:=x7l; x9l:=x9r; fi
+y1=y6=.5h; top y2r=top y7l=h+oo; bot y5l=bot y9r=-oo;
+x8=.5[x3,x4]=.5w; y8=.5[y3,y4]=.5h;
+y3-y4=h/3; z3-z4=whatever*dir theta;
+filldraw stroke z1e{up}...z2e{right}...z3e---z4e...z5e{right}
+ ...z6e{up}...z7e{left}...{-dir phi}z8e...{left}z9e...z1e{up}; % bowls
+penlabels(1,2,3,4,5,6,7,8,9); endchar;
+
+font_x_height x_height#;
+font_quad 18u#+4letter_fit#;
+% (The calling file should give the other math extension font parameters.)
+
+bye.
diff --git a/systems/knuth/local/cm/gen.mf b/systems/knuth/local/cm/gen.mf
new file mode 100644
index 0000000000..32a55c77a0
--- /dev/null
+++ b/systems/knuth/local/cm/gen.mf
@@ -0,0 +1,91 @@
+% characters for Jill's genealogy publications
+
+cmchar "Cross (death symbol)";
+beginchar("d",9u#,.65[x_height#,asc_height#],.1(asc_height#-x_height#));
+adjust_fit(0,0); pickup tiny.nib; light_stem=Vround .5[vair,stem];
+pos1(stem,0); pos2(.4[hair,stem],0); pos3(stem,0);
+%pos4(stem,90); pos5(.5[vair,stem],90); pos6(stem,90);
+pos4(light_stem,90); pos5(.5[vair,light_stem],90); pos6(light_stem,90);
+x1=x2=x3=.5w; top y1=h; bot y3=-d; y2=y4r;
+y4=y5=y6; y4l=good.y .6[-d,h]; x4=good.x u; x6=r-x4; x5=x2;
+filldraw z1l...z2l{down}...z3l--z3r...z2r{up}...z1r--cycle; % vertical stroke
+filldraw z4l...z5l...z6l--z6r...z5r...z4r--cycle; % horizontal stroke
+penlabels(1,2,3,4,5,6); endchar;
+
+cmchar "Rings (marriage symbol)";
+beginchar("m",9u#,v_center(x_height#));
+adjust_fit(0,0); pickup tiny.nib;
+numeric light_stem; light_stem=Vround .5[vair,stem];
+pos1(light_stem,90); pos3(light_stem,-90);
+pos2(light_stem,180); pos4(.3[hair,light_stem],0);
+x1=x3=.5[x2,x4]; lft x2r=hround(-u-.5light_stem); rt x4r=hround(.6w+.5stem);
+%top y1r=vround(h+.5light_stem); y2=y4=.5[y1,y3];
+%bot y3r=vround(-d-.5light_stem);
+top y1r=h; y2=y4=.5[y1,y3];
+bot y3r=-d;
+pos5(light_stem,90); pos7(light_stem,-90);
+pos6(.3[hair,light_stem],180); pos8(light_stem,0);
+x5-x1=x6-x2=x7-x3=x8-x4; rt x8r=hround(w+u+.5light_stem);
+y5-y1=y6-y2=y7-y3=y8-y4=0;
+filldraw stroke pulled_arc.e(1,2)&pulled_arc.e(2,3)&pulled_arc.e(3,4)
+ &pulled_arc.e(4,1); % left ring
+filldraw stroke pulled_arc.e(5,6)&pulled_arc.e(6,7)&pulled_arc.e(7,8)
+ &pulled_arc.e(8,5); % right ring
+penlabels(1,2,3,4,5,6,7,8); endchar;
+
+cmchar "Five-pointed star (birth symbol)";
+beginchar("b",9u#,v_center(x_height#));
+adjust_fit(0,0); pickup tiny.nib;
+numeric theta; theta=360/5; % degrees between points
+pos0(curve,0); pos1(rule_thickness,90); x0=x1=good.x .5w;
+top y1r=vround(math_axis+.5x_height+.5rule_thickness);
+ypart(.5[z1,z0+(z1-z2) rotated 2theta])=math_axis;
+pos2(curve,theta); pos3(rule_thickness,90+theta);
+z2=z0; z3-z0=(z1-z0) rotated theta;
+z4=whatever[z0l,z1r]=whatever[z2r,z3r];
+filldraw for n=0 upto 4:
+ z0+(z1r-z0)rotated(n*theta)--z0+(z4-z0)rotated(n*theta)--endfor
+ cycle; % star
+penlabels(0,1,2,3,4); endchar;
+
+cmchar "Divided rings (divorce symbol)";
+beginchar("1",9u#,v_center(x_height#));
+adjust_fit(0,0); pickup tiny.nib;
+numeric light_stem; light_stem=Vround .5[vair,stem];
+pos11(stem,0); pos12(.4[hair,stem],0); pos13(stem,0);
+x11=x12=x13=.5w; top y11=round 1.3h; bot y13=round(-d-.3h); y12=.6[y13,y11];
+filldraw z11l...z12l{down}...z13l
+ --z13r...z12r{up}...z11r--cycle; % vertical stroke
+pos1(light_stem,90); pos3(light_stem,-90);
+pos2(light_stem,180); pos4(.3[hair,light_stem],0);
+x1=x3=.5[x2,x4]; lft x2r=hround(-3u-.5light_stem); rt x4r=hround(.25w+.5stem);
+top y1r=h; y2=y4=.5[y1,y3];
+bot y3r=-d;
+pos5(light_stem,90); pos7(light_stem,-90);
+pos6(.3[hair,light_stem],180); pos8(light_stem,0);
+x5-x1=x6-x2=x7-x3=x8-x4; rt x8r=hround(w+3u+.5light_stem);
+y5-y1=y6-y2=y7-y3=y8-y4=0;
+filldraw stroke pulled_arc.e(1,2)&pulled_arc.e(2,3)&pulled_arc.e(3,4)
+ &pulled_arc.e(4,1); % left ring
+filldraw stroke pulled_arc.e(5,6)&pulled_arc.e(6,7)&pulled_arc.e(7,8)
+ &pulled_arc.e(8,5); % right ring
+penlabels(1,2,3,4,5,6,7,8,11,12,13); endchar;
+
+cmchar "Leaf";
+beginchar("l",18u#,asc_height#,0);
+adjust_fit(0,0); pickup pencircle scaled .6rule_thickness;
+z1=(0,0); z2=(w,h);
+z3=.5[z1,z2]+.25((z2-z1)rotated 90);
+z4=.5[z1,z2]+.25((z2-z1)rotated-90);
+path p[]; p3=z1...z3{z2-z1}...z2; p4=z1...z4{z2-z1}...z2;
+draw p3; % upper edge
+draw p4; % lower edge
+draw z1 -- z2; % stem
+z5=1/4[z1,z2]; z6=.55[z1,z2];
+z35=p3 intersectionpoint (z5--(x5,2h));
+z36=p3 intersectionpoint (z6--(x6,2h));
+z45=p4 intersectionpoint (z5--(2w,y5));
+z46=p4 intersectionpoint (z6--(2w,y6));
+draw z35--z5--z45; % vein
+draw z36--z6--z46; % vein
+labels(1,2,3,4,5,6,35,36,45,46); endchar;
diff --git a/systems/knuth/local/cm/gen10.mf b/systems/knuth/local/cm/gen10.mf
new file mode 100644
index 0000000000..5f2db849bc
--- /dev/null
+++ b/systems/knuth/local/cm/gen10.mf
@@ -0,0 +1,14 @@
+% 10-point genealogy font
+if unknown cmbase: input cmbase fi % omit if CMMF can be assumed
+mode_setup;
+
+def generate suffix t= enddef;
+input cmr10; font_setup;
+
+font_identifier:="GEN";
+font_slant slant; font_x_height x_height#;
+font_normal_space 6u#+2letter_fit#;
+font_quad 18u#+4letter_fit#;
+
+input gen;
+end
diff --git a/systems/knuth/local/cm/gen8.mf b/systems/knuth/local/cm/gen8.mf
new file mode 100644
index 0000000000..1e13f6dbe8
--- /dev/null
+++ b/systems/knuth/local/cm/gen8.mf
@@ -0,0 +1,13 @@
+% 8-point genealogy font
+if unknown cmbase: input cmbase fi % omit if CMMF can be assumed
+mode_setup;
+
+def generate suffix t= enddef;
+input cmr8; font_setup;
+
+font_slant slant; font_x_height x_height#;
+font_normal_space 6u#+2letter_fit#;
+font_quad 18u#+4letter_fit#;
+
+input gen;
+end
diff --git a/systems/knuth/local/cm/gen9.mf b/systems/knuth/local/cm/gen9.mf
new file mode 100644
index 0000000000..eb056926a0
--- /dev/null
+++ b/systems/knuth/local/cm/gen9.mf
@@ -0,0 +1,13 @@
+% 9-point genealogy font
+if unknown cmbase: input cmbase fi % omit if CMMF can be assumed
+mode_setup;
+
+def generate suffix t= enddef;
+input cmr9; font_setup;
+
+font_slant slant; font_x_height x_height#;
+font_normal_space 6u#+2letter_fit#;
+font_quad 18u#+4letter_fit#;
+
+input gen;
+end
diff --git a/systems/knuth/local/cm/odigs.mf b/systems/knuth/local/cm/odigs.mf
new file mode 100644
index 0000000000..2b65df8e30
--- /dev/null
+++ b/systems/knuth/local/cm/odigs.mf
@@ -0,0 +1,11 @@
+% Computer Modern Oldstyle Digits Only
+
+font_coding_scheme:="Numerals only";
+mode_setup; font_setup;
+
+font_slant slant; font_x_height x_height#;
+font_quad 18u#+4letter_fit#;
+
+input olddig; % oldstyle numerals
+
+bye.
diff --git a/systems/knuth/local/etc-sparc/Makefile b/systems/knuth/local/etc-sparc/Makefile
new file mode 100644
index 0000000000..8570427641
--- /dev/null
+++ b/systems/knuth/local/etc-sparc/Makefile
@@ -0,0 +1,41 @@
+# Make executable files for this workstation, using the master TeX sources
+
+PFLAGS= -O -I.. -temp=. -g
+CFLAGS= -O -I.. -g
+PC= time pc
+
+all: vftovp vptovf
+
+%.p: %.ch
+ tangle ../../dist/etc/$*.web $*.ch
+ mv ../../dist/etc/$*.p .
+
+%.ch: ../../dist/etc/%.web
+ touch $*.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+access.o: access.c
+
+dvityext.o: dvityext.c
+
+access.c:
+ cp ../texware/access.c .
+
+dvityext.c:
+ cp ../texware/dvityext.c .
+
+vftovp: vftovp.o access.o dvityext.o
+ ${PC} ${PFLAGS} -o vftovp vftovp.o access.o dvityext.o
+
+vptovf: vptovf.o access.o
+ ${PC} ${PFLAGS} -o vptovf vptovf.o access.o
+
+dvityext.o: dvityext.c ../h00vars.h ../texpaths.h
+
+install: # doit make install "PROG=vftovp"
+ mv $(PROG) /usr/local/bin/$(PROG)
+
+clean:
+ rm -f *.p *.o *~ access.c dvityext.c ../../dist/etc/*.pool
diff --git a/systems/knuth/local/etc-sparc/vftovp.ch b/systems/knuth/local/etc-sparc/vftovp.ch
new file mode 100644
index 0000000000..7e1c67cc0b
--- /dev/null
+++ b/systems/knuth/local/etc-sparc/vftovp.ch
@@ -0,0 +1,330 @@
+% Change file for the VFtoVP processor, for use on Berkeley UNIX systems.
+% This file based on TFtoPL.ch and DVItype.ch by Pavel Curtis, Pavel@Cornell.
+
+% History:
+% 12/11/89 (don) Initial version
+% 3/23/90 (don) Fixed coredump problem when no args given
+% 4/26/90 (don) Up to version 1.1
+% 9/6/90 (don) Up to version 1.2
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{VF\lowercase{to}VP changes for SunOS}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is VFtoVP, Version 1.2' {printed when the program starts}
+@y
+@d banner=='This is VFtoVP, Version 1.2 for SunOS'
+ {printed when the program starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Fix files in program statement; add external access procedures
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program VFtoVP(@!vf_file,@!tfm_file,@!vpl_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@y
+@p program VFtoVP(@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type UNIX_file_name=packed array[1..100] of char;
+ @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+function testreadaccess(var fn:UNIX_file_name):boolean; external;
+procedure setpaths; external;
+function testaccess(accessmode:integer; filepath:integer): boolean; external;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Add a label
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Labels...@>=final_end;
+@y
+@<Labels...@>=final_end-1, final_end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] Increase name_length
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!name_length=50; {a file name shouldn't be longer than this}
+@y
+@!name_length=100; {a file name shouldn't be longer than this}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Fix declaration of vf_file; declare extra VF-file variables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!vf_file:packed file of byte;
+@y
+@!vf_file:packed file of -128..127; {files that contain binary data}
+@!vf_name:UNIX_file_name;
+@!vf_byte:integer;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [10] Fix declaration of tfm_file; declare extra TFM-file variables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!tfm_file:packed file of byte;
+@y
+@!tfm_file:packed file of -128..127; {files that contain binary data}
+@!tfm_name:UNIX_file_name;
+@!tfm_byte:integer;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [11] Open binary files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ On some systems you may have to do something special to read a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+reset(tfm_file); reset(vf_file);
+@y
+@ On some systems you may have to do something special to read a
+packed file of bytes.
+
+@<Set init...@>=
+if argc < 4 then begin
+ print_ln('Usage: vftovp <vf-file> <tfm-file> <vpl-file>');
+ goto final_end-1;
+end;
+argv(1, vf_name); argv(2, tfm_name);
+if testreadaccess(vf_name) then reset(vf_file, vf_name)
+else begin print_ln('I can''t read the VF file!'); goto final_end-1;
+ end;
+if testreadaccess(tfm_name) then reset(tfm_file, tfm_name)
+else begin print_ln('I can''t read the TFM file!'); goto final_end-1;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] Declare vpl_name
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!vpl_file:text;
+@y
+@!vpl_file:text;
+@!vpl_name: array[1..100] of char;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] Open VPL file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ @<Set init...@>=
+rewrite(vpl_file);
+@y
+@ @<Set init...@>=
+setpaths; {read environment, to find TEXFONTS, if there}
+argv(3, vpl_name);
+rewrite(vpl_file, vpl_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] Fix reading of TFM bytes.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Read the whole \.{TFM} file@>=
+read(tfm_file,tfm[0]);
+if tfm[0]>127 then abort('The first byte of the input file exceeds 127!');
+@.The first byte...@>
+if eof(tfm_file) then abort('The input file is only one byte long!');
+@.The input...one byte long@>
+read(tfm_file,tfm[1]); lf:=tfm[0]*@'400+tfm[1];
+if lf=0 then
+ abort('The file claims to have length zero, but that''s impossible!');
+@.The file claims...@>
+if 4*lf-1>tfm_size then abort('The file is bigger than I can handle!');
+@.The file is bigger...@>
+for tfm_ptr:=2 to 4*lf-1 do
+ begin if eof(tfm_file) then
+ abort('The file has fewer bytes than it claims!');
+@.The file has fewer bytes...@>
+ read(tfm_file,tfm[tfm_ptr]);
+ end;
+if not eof(tfm_file) then
+ begin print_ln('There''s some extra junk at the end of the TFM file,');
+@.There's some extra junk...@>
+ print_ln('but I''ll proceed as if it weren''t there.');
+ end
+@y
+@d fget==begin get(tfm_file);
+ tfm_byte:=tfm_file^;
+ if tfm_byte<0 then tfm_byte:=tfm_byte+256;
+ end
+@d fbyte==tfm_byte
+@d read_sixteen(#)==begin #:=fbyte;
+ if #>127 then abort;
+ fget; #:=#*@'400+fbyte;
+ end
+@d store_four_quarters(#)==begin fget; a:=fbyte; qw.b0:=qi(a);
+ fget; b:=fbyte; qw.b1:=qi(b);
+ fget; c:=fbyte; qw.b2:=qi(c);
+ fget; d:=fbyte; qw.b3:=qi(d);
+ #:=qw;
+ end
+
+@<Read the whole \.{TFM} file@>=
+ {Prime the pump}
+tfm_byte:=tfm_file^;
+if tfm_byte<0 then tfm_byte:=tfm_byte+256;
+
+tfm[0]:=fbyte; fget;
+if tfm[0]>127 then abort('The first byte of the input file exceeds 127!');
+@.The first byte...@>
+if eof(tfm_file) then abort('The input file is only one byte long!');
+@.The input...one byte long@>
+tfm[1]:=fbyte; fget; lf:=tfm[0]*@'400+tfm[1];
+if lf=0 then
+ abort('The file claims to have length zero, but that''s impossible!');
+@.The file claims...@>
+if 4*lf-1>tfm_size then abort('The file is bigger than I can handle!');
+@.The file is bigger...@>
+for tfm_ptr:=2 to 4*lf-1 do
+ begin if eof(tfm_file) then
+ abort('The file has fewer bytes than it claims!');
+@.The file has fewer bytes...@>
+ tfm[tfm_ptr]:=fbyte; fget;
+ end;
+if not eof(tfm_file) then
+ begin print_ln('There''s some extra junk at the end of the TFM file,');
+@.There's some extra junk...@>
+ print_ln('but I''ll proceed as if it weren''t there.');
+ end
+
+@^system dependencies@>@^Changes for Berkeley {\mc UNIX}@>
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [31] Fix reading of VF bytes.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d read_vf(#)==read(vf_file,#)
+@y
+@d read_vf(#)==begin vf_byte:=vf_file^;
+ if vf_byte>=0 then #:=vf_byte@+else #:=vf_byte+256;
+ get(vf_file);
+ end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [37] Declare real_name_of_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!cur_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+@y
+@!cur_name,@!real_name_of_file:packed array[1..name_length] of char;
+ {external name}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38] Fix read_tfm_word to read bytes properly
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d read_tfm(#)==if eof(tfm_file) then #:=0@+else read(tfm_file,#)
+@y
+@d read_tfm(#)==if eof(tfm_file) then #:=0
+ else begin tfm_byte:=tfm_file^;
+ if tfm_byte>=0 then #:=tfm_byte@+else #:=tfm_byte+256;
+ get(tfm_file);
+ end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [39] Open a local TFM file using the normal font paths
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reset(tfm_file,cur_name);
+@^system dependencies@>
+if eof(tfm_file) then
+ print_ln('---not loaded, TFM file can''t be opened!')
+@.TFM file can\'t be opened@>
+else begin font_bc:=0; font_ec:=256; {will cause error if not modified soon}
+@y
+if not testaccess(4,3) then
+ print_ln('---not loaded, TFM file can''t be opened!')
+@.TFM file can\'t be opened@>
+else begin reset(tfm_file,real_name_of_file);
+ font_bc:=0; font_ec:=256; {will cause error if not modified soon}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [42] Remove initialization of now-defunct array
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ @<Set init...@>=
+default_directory:=default_directory_name;
+@y
+@ (No initialization to be done. Keep this module to preserve numbering.)
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [43] Fix addition of ".tfm" suffix for portability and keep lowercase
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font. This usually means that we need to
+prepend the name of the default directory, and
+to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
+to upper case, since |cur_name| is a \PASCAL\ string.
+@y
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font. This usually means that we need to,
+at most sites, append the
+suffix ``.tfm''.
+@z
+
+@x
+if a=0 then
+ begin for k:=1 to default_directory_name_length do
+ cur_name[k]:=default_directory[k];
+ r:=default_directory_name_length;
+ end
+else r:=0;
+@y
+r:=0;
+@z
+
+@x
+ if (vf[k]>="a")and(vf[k]<="z") then
+ cur_name[r]:=xchr[vf[k]-@'40]
+ else cur_name[r]:=xchr[vf[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='T'; cur_name[r+3]:='F'; cur_name[r+4]:='M'
+@y
+ cur_name[r]:=xchr[vf[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='t'; cur_name[r+3]:='f'; cur_name[r+4]:='m'
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [133] Add printing of newlines at end of program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+final_end:end.
+@y
+final_end: out_ln; final_end-1: print_ln(' '); end.
+@z
diff --git a/systems/knuth/local/etc-sparc/vptovf.ch b/systems/knuth/local/etc-sparc/vptovf.ch
new file mode 100644
index 0000000000..27d87eb6ad
--- /dev/null
+++ b/systems/knuth/local/etc-sparc/vptovf.ch
@@ -0,0 +1,142 @@
+% Change file for the VPtoVF processor, for use on Berkeley UNIX systems.
+% This file based on PLtoTF.ch by Pavel Curtis, Pavel@Cornell.
+
+% History:
+% 12/14/89 (don) Initial version
+% 4/26/90 (don) Up to version 1.1
+% 9/6/90 (don) Up to version 1.2
+% 3/25/91 (don) Up to version 1.3
+% 3/6/95 (don) Up to version 1.4
+% 8/8/98 (don) Up to version 1.5
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{VP\lowercase{to}VF changes for SunOS}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is VPtoVF, Version 1.5' {printed when the program starts}
+@y
+@d banner=='This is VPtoVF, Version 1.5 for SunOS'
+ {printed when the program starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Fix filenames in program statement; add `final_end' label; add access
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program VPtoVF(@!vpl_file,@!vf_file,@!tfm_file,@!output);
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@y
+@d final_end==9999
+
+@p program VPtoVF(@!output);
+label final_end;
+const @<Constants in the outer block@>@/
+type UNIX_file_name=packed array[1..100] of char;
+ @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+function testreadaccess(var fn:UNIX_file_name):boolean; external;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6] Open VPL file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reset(vpl_file);
+@y
+if argc < 4 then begin
+ print_ln('Usage: vptovf <vpl-file> <vf-file> <tfm-file>');
+ goto final_end;
+end;
+argv(1, vpl_name);
+if testreadaccess(vpl_name) then reset(vpl_file, vpl_name)
+else begin print_ln('I can''t read the VPL file!'); goto final_end;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] Change type of output files and declare extra binary-file variables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!vf_file:packed file of 0..255;
+@!tfm_file:packed file of 0..255;
+@y
+@!vf_file,
+@!tfm_file : packed file of -128..127;
+@!tmp : 0..255;
+@!vf_name, @!tfm_name,
+@!vpl_name : UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [22] Open output files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ On some systems you may have to do something special to write a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(vf_file); rewrite(tfm_file);
+@y
+@ On some systems you may have to do something special to write a
+packed file of bytes.
+@^system dependencies@>
+
+@<Set init...@>=
+argv(2, vf_name); argv(3, tfm_name);
+rewrite(vf_file, vf_name); rewrite(tfm_file, tfm_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [156] Change TFM-byte output to fix ranges
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d out(#)==write(tfm_file,#)
+@y
+@d out(#)==begin
+ tmp := #;
+ if tmp > 127 then
+ write(tfm_file, tmp - 256)
+ else
+ write(tfm_file, tmp)
+ end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [175] Change VF-byte output to fix ranges
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d vout(#)==write(vf_file,#)
+@y
+@d vout(#)==begin
+ tmp := #;
+ if tmp > 127 then
+ write(vf_file, tmp - 256)
+ else
+ write(vf_file, tmp)
+ end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [181] Define label `final_end'; print newline at end of program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+end.
+@y
+final_end: print_ln(' '); end.
+@z
diff --git a/systems/knuth/local/etc/Makefile b/systems/knuth/local/etc/Makefile
new file mode 100644
index 0000000000..7917e58eca
--- /dev/null
+++ b/systems/knuth/local/etc/Makefile
@@ -0,0 +1,46 @@
+# Make executable files for this workstation, using the master TeX sources
+
+PFLAGS= -g -I..
+CFLAGS= -g -I..
+PC= gpc
+
+all: vftovp vptovf
+
+%.p: %.ch
+ tangle ../../dist/etc/$*.web $*.ch
+# the next line formerly necessary; but not with Linux's (Web2C) tangle
+# mv ../../dist/etc/$*.p .
+
+%.ch: ../../dist/etc/%.web
+ touch $*.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+dvityext.c: ../texware/dvityext.c
+ cp ../texware/dvityext.c .
+
+texwarext.h: ../texware/texwarext.h
+ cp ../texware/texwarext.h .
+
+dvityext.h: ../texware/dvityext.h
+ cp ../texware/dvityext.h .
+
+vftovp: vftovp.o dvityext.o
+ ${PC} ${PFLAGS} -o vftovp vftovp.o dvityext.o
+
+vptovf: vptovf.o
+ ${PC} ${PFLAGS} -o vptovf vptovf.o dvityext.o
+
+dvityext.o: dvityext.c ../GPCtypes.h ../texpaths.h
+
+vptovf.o: texwarext.h
+
+vftovp.o: dvityext.h
+
+install: # doit make install "PROG=vftovp"
+ mv $(PROG) /usr/local/bin/$(PROG)
+
+clean:
+# rm -f *.p *.o *~ dvityext.c ../../dist/etc/*.pool
+ rm -f *.p *.o *~ dvityext.c *.pool core texwarext.h
diff --git a/systems/knuth/local/etc/dvityext.h b/systems/knuth/local/etc/dvityext.h
new file mode 100644
index 0000000000..ab9a63da75
--- /dev/null
+++ b/systems/knuth/local/etc/dvityext.h
@@ -0,0 +1,17 @@
+argc:asmname '_p_argc' Integer;
+
+procedure argv(no:Integer;var str:UNIXfilename); external;
+
+function testreadaccess(var a:UNIXfilename):boolean; external;
+
+procedure setpaths; external;
+
+function testaccess(accessmode:integer; filepath:integer): boolean; external;
+
+function flength(var f: bytefile):integer; external;
+
+procedure bseek(var f: bytefile; loc: integer); external;
+
+procedure flushstdout; external;
+
+procedure readbyte(var f: bytefile; var v: ByteCard); external;
diff --git a/systems/knuth/local/etc/texwarext.h b/systems/knuth/local/etc/texwarext.h
new file mode 100644
index 0000000000..a183a12814
--- /dev/null
+++ b/systems/knuth/local/etc/texwarext.h
@@ -0,0 +1,5 @@
+argc:asmname '_p_argc' Integer;
+
+procedure argv(no:Integer;var str:UNIXfilename); external;
+
+function testreadaccess(var a:UNIXfilename):boolean; external;
diff --git a/systems/knuth/local/etc/vftovp.ch b/systems/knuth/local/etc/vftovp.ch
new file mode 100644
index 0000000000..d529a97d10
--- /dev/null
+++ b/systems/knuth/local/etc/vftovp.ch
@@ -0,0 +1,227 @@
+% Change file for the VFtoVP processor, for use with GNU Pascal
+% (by Don Knuth; see ../etc-sparc for its prehistory)
+
+% History:
+% 2000.04.30 Initial version
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{VF\lowercase{to}VP changes for GNU Pascal}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is VFtoVP, Version 1.2' {printed when the program starts}
+@y
+@d banner=='This is VFtoVP, Version 1.2 for Linux'
+ {printed when the program starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Fix files in program statement; add external access procedures
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program VFtoVP(@!vf_file,@!tfm_file,@!vpl_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@y
+@p program VFtoVP(@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type UNIX_file_name=packed array[1..100] of char;
+byte_file=packed file of ByteCard;
+ @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\
+@=#include "dvityext.h"@>
+@\@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Add a label
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Labels...@>=final_end;
+@y
+@<Labels...@>=final_end-1, final_end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] Increase name_length
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!name_length=50; {a file name shouldn't be longer than this}
+@y
+@!name_length=100; {a file name shouldn't be longer than this}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Fix declaration of vf_file; declare extra VF-file variables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!vf_file:packed file of byte;
+@y
+@!vf_file:byte_file; {files that contain binary data}
+@!vf_name:UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [10] Fix declaration of tfm_file; declare extra TFM-file variables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!tfm_file:packed file of byte;
+@y
+@!tfm_file:byte_file; {files that contain binary data}
+@!tfm_name:UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [11] Open binary files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ On some systems you may have to do something special to read a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+reset(tfm_file); reset(vf_file);
+@y
+@ On some systems you may have to do something special to read a
+packed file of bytes.
+
+@<Set init...@>=
+if argc < 4 then begin
+ print_ln('Usage: vftovp <vf-file> <tfm-file> <vpl-file>');
+ goto final_end-1;
+end;
+argv(1, vf_name); argv(2, tfm_name);
+if testreadaccess(vf_name) then reset(vf_file, vf_name)
+else begin print_ln('I can''t read the VF file!'); goto final_end-1;
+ end;
+if testreadaccess(tfm_name) then reset(tfm_file, tfm_name)
+else begin print_ln('I can''t read the TFM file!'); goto final_end-1;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] Declare vpl_name
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!vpl_file:text;
+@y
+@!vpl_file:text;
+@!vpl_name: UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] Open VPL file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ @<Set init...@>=
+rewrite(vpl_file);
+@y
+@ @<Set init...@>=
+setpaths; {read environment, to find TEXFONTS, if there}
+argv(3, vpl_name);
+rewrite(vpl_file, vpl_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [37] Declare real_name_of_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!cur_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+@y
+@!cur_name,@!real_name_of_file:external UNIX_file_name;
+ {external names}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [39] Open a local TFM file using the normal font paths
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reset(tfm_file,cur_name);
+@^system dependencies@>
+if eof(tfm_file) then
+ print_ln('---not loaded, TFM file can''t be opened!')
+@.TFM file can\'t be opened@>
+else begin font_bc:=0; font_ec:=256; {will cause error if not modified soon}
+@y
+if not testaccess(4,3) then
+ print_ln('---not loaded, TFM file can''t be opened!')
+@.TFM file can\'t be opened@>
+else begin reset(tfm_file,real_name_of_file);
+ font_bc:=0; font_ec:=256; {will cause error if not modified soon}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [42] Remove initialization of now-defunct array
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ @<Set init...@>=
+default_directory:=default_directory_name;
+@y
+@ (No initialization to be done. Keep this module to preserve numbering.)
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [43] Fix addition of ".tfm" suffix for portability and keep lowercase
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font. This usually means that we need to
+prepend the name of the default directory, and
+to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
+to upper case, since |cur_name| is a \PASCAL\ string.
+@y
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font. This usually means that we need to,
+at most sites, append the
+suffix ``.tfm''.
+@z
+
+@x
+if a=0 then
+ begin for k:=1 to default_directory_name_length do
+ cur_name[k]:=default_directory[k];
+ r:=default_directory_name_length;
+ end
+else r:=0;
+@y
+r:=0;
+@z
+
+@x
+ if (vf[k]>="a")and(vf[k]<="z") then
+ cur_name[r]:=xchr[vf[k]-@'40]
+ else cur_name[r]:=xchr[vf[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='T'; cur_name[r+3]:='F'; cur_name[r+4]:='M'
+@y
+ cur_name[r]:=xchr[vf[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='t'; cur_name[r+3]:='f'; cur_name[r+4]:='m';
+cur_name[r+5]:=chr(0);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [133] Add printing of newlines at end of program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+final_end:end.
+@y
+final_end: out_ln; final_end-1: print_ln(' '); end.
+@z
diff --git a/systems/knuth/local/etc/vftovp.p b/systems/knuth/local/etc/vftovp.p
new file mode 100644
index 0000000000..df8f5f78b7
--- /dev/null
+++ b/systems/knuth/local/etc/vftovp.p
@@ -0,0 +1,807 @@
+{2:}program VFtoVP(output);label{3:}9998,9999;{:3}const{4:}
+tfmsize=30000;vfsize=10000;maxfonts=300;ligsize=5000;hashsize=5003;
+namelength=100;maxstack=50;{:4}
+type UNIXfilename=packed array[1..100]of char;
+bytefile=packed file of ByteCard;{5:}byte=0..255;{:5}{22:}
+index=0..tfmsize;{:22}var{7:}vffile:bytefile;vfname:UNIXfilename;{:7}
+{10:}tfmfile:bytefile;tfmname:UNIXfilename;{:10}{12:}
+lf,lh,bc,ec,nw,nh,nd,ni,nl,nk,ne,np:0..32767;{:12}{20:}vplfile:text;
+vplname:UNIXfilename;{:20}{23:}tfm:array[-1000..tfmsize]of byte;{:23}
+{26:}
+charbase,widthbase,heightbase,depthbase,italicbase,ligkernbase,kernbase,
+extenbase,parambase:integer;{:26}{29:}fonttype:0..2;{:29}{30:}
+vf:array[0..vfsize]of byte;fontnumber:array[0..maxfonts]of integer;
+fontstart,fontchars:array[0..maxfonts]of 0..vfsize;fontptr:0..maxfonts;
+packetstart,packetend:array[byte]of 0..vfsize;packetfound:boolean;
+tempbyte:byte;count:integer;realdsize:real;pl:integer;vfptr:0..vfsize;
+vfcount:integer;{:30}{37:}a:integer;l:integer;
+curname,realnameoffile:external UNIXfilename;b0,b1,b2,b3:byte;
+fontlh:0..32767;fontbc,fontec:0..32767;{:37}{42:}
+defaultdirectory:packed array[1..9]of char;{:42}{49:}
+ASCII04,ASCII10,ASCII14:packed array[1..32]of char;
+xchr:packed array[0..255]of char;
+MBLstring,RIstring,RCEstring:packed array[1..3]of char;{:49}{51:}
+dig:array[0..11]of 0..9;{:51}{54:}level:0..5;{:54}{67:}charsonline:0..8;
+perfect:boolean;{:67}{69:}i:0..32767;c:0..256;d:0..3;k:index;r:0..65535;
+{:69}{85:}labeltable:array[0..258]of record cc:0..256;rr:0..ligsize;end;
+labelptr:0..257;sortptr:0..257;boundarychar:0..256;bcharlabel:0..32767;
+{:85}{87:}activity:array[0..ligsize]of 0..2;ai,acti:0..ligsize;{:87}
+{111:}hash:array[0..hashsize]of 0..66048;
+class:array[0..hashsize]of 0..4;ligz:array[0..hashsize]of 0..257;
+hashptr:0..hashsize;hashlist:array[0..hashsize]of 0..hashsize;
+h,hh:0..hashsize;xligcycle,yligcycle:0..256;{:111}{123:}top:0..maxstack;
+wstack,xstack,ystack,zstack:array[0..maxstack]of integer;
+vflimit:0..vfsize;o:byte;{:123}
+#include "dvityext.h"
+procedure initialize;var k:integer;
+begin writeln('This is VFtoVP, Version 1.2 for Linux');{11:}
+if argc<4 then begin writeln(
+'Usage: vftovp <vf-file> <tfm-file> <vpl-file>');goto 9998;end;
+argv(1,vfname);argv(2,tfmname);
+if testreadaccess(vfname)then reset(vffile,vfname)else begin writeln(
+'I can''t read the VF file!');goto 9998;end;
+if testreadaccess(tfmname)then reset(tfmfile,tfmname)else begin writeln(
+'I can''t read the TFM file!');goto 9998;end;{:11}{21:}setpaths;
+argv(3,vplname);rewrite(vplfile,vplname);{:21}{50:}
+ASCII04:=' !"#$%&''()*+,-./0123456789:;<=>?';
+ASCII10:='@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_';
+ASCII14:='`abcdefghijklmnopqrstuvwxyz{|}~?';
+for k:=0 to 255 do xchr[k]:='?';
+for k:=0 to 31 do begin xchr[k+32]:=ASCII04[k+1];
+xchr[k+64]:=ASCII10[k+1];xchr[k+96]:=ASCII14[k+1];end;MBLstring:='MBL';
+RIstring:='RI ';RCEstring:='RCE';{:50}{55:}level:=0;{:55}{68:}
+charsonline:=0;perfect:=true;{:68}{86:}boundarychar:=256;
+bcharlabel:=32767;labelptr:=0;labeltable[0].rr:=0;{:86}end;{:2}{38:}
+procedure readtfmword;
+begin if eof(tfmfile)then b0:=0 else read(tfmfile,b0);
+if eof(tfmfile)then b1:=0 else read(tfmfile,b1);
+if eof(tfmfile)then b2:=0 else read(tfmfile,b2);
+if eof(tfmfile)then b3:=0 else read(tfmfile,b3);end;{:38}{45:}
+function vfread(k:integer):integer;var b:byte;a:integer;
+begin vfcount:=vfcount+k;if eof(vffile)then b:=0 else read(vffile,b);
+a:=b;if k=4 then if b>=128 then a:=a-256;
+while k>1 do begin if eof(vffile)then b:=0 else read(vffile,b);
+a:=256*a+b;k:=k-1;end;vfread:=a;end;{:45}{47:}
+function tfmwidth(c:byte):integer;var a:integer;k:index;
+begin k:=4*(widthbase+tfm[4*(charbase+c)]);a:=tfm[k];
+if a>=128 then a:=a-256;
+tfmwidth:=((256*a+tfm[k+1])*256+tfm[k+2])*256+tfm[k+3];end;{:47}{52:}
+procedure outdigs(j:integer);begin repeat j:=j-1;
+write(vplfile,dig[j]:1);until j=0;end;procedure printdigs(j:integer);
+begin repeat j:=j-1;write(dig[j]:1);until j=0;end;{:52}{53:}
+procedure printoctal(c:byte);var j:0..2;begin write('''');
+for j:=0 to 2 do begin dig[j]:=c mod 8;c:=c div 8;end;printdigs(3);end;
+{:53}{56:}procedure outln;var l:0..5;begin writeln(vplfile);
+for l:=1 to level do write(vplfile,' ');end;procedure left;
+begin level:=level+1;write(vplfile,'(');end;procedure right;
+begin level:=level-1;write(vplfile,')');outln;end;{:56}{57:}
+procedure outBCPL(k:index);var l:0..39;begin write(vplfile,' ');
+l:=tfm[k];while l>0 do begin k:=k+1;l:=l-1;write(vplfile,xchr[tfm[k]]);
+end;end;{:57}{58:}procedure outoctal(k,l:index);var a:0..1023;b:0..32;
+j:0..11;begin write(vplfile,' O ');a:=0;b:=0;j:=0;while l>0 do{59:}
+begin l:=l-1;
+if tfm[k+l]<>0 then begin while b>2 do begin dig[j]:=a mod 8;a:=a div 8;
+b:=b-3;j:=j+1;end;case b of 0:a:=tfm[k+l];1:a:=a+2*tfm[k+l];
+2:a:=a+4*tfm[k+l];end;end;b:=b+8;end{:59};
+while(a>0)or(j=0)do begin dig[j]:=a mod 8;a:=a div 8;j:=j+1;end;
+outdigs(j);end;{:58}{60:}procedure outchar(c:byte);
+begin if fonttype>0 then begin tfm[0]:=c;
+outoctal(0,1)end else if((c>=48)and(c<=57))or((c>=65)and(c<=90))or((c>=
+97)and(c<=122))then write(vplfile,' C ',xchr[c])else begin tfm[0]:=c;
+outoctal(0,1);end;end;{:60}{61:}procedure outface(k:index);var s:0..1;
+b:0..8;
+begin if tfm[k]>=18 then outoctal(k,1)else begin write(vplfile,' F ');
+s:=tfm[k]mod 2;b:=tfm[k]div 2;write(vplfile,MBLstring[1+(b mod 3)]);
+write(vplfile,RIstring[1+s]);write(vplfile,RCEstring[1+(b div 3)]);end;
+end;{:61}{62:}procedure outfix(k:index);var a:0..4095;f:integer;j:0..12;
+delta:integer;begin write(vplfile,' R ');
+a:=(tfm[k]*16)+(tfm[k+1]div 16);
+f:=((tfm[k+1]mod 16)*256+tfm[k+2])*256+tfm[k+3];if a>2047 then{65:}
+begin write(vplfile,'-');a:=4096-a;if f>0 then begin f:=1048576-f;
+a:=a-1;end;end{:65};{63:}begin j:=0;repeat dig[j]:=a mod 10;a:=a div 10;
+j:=j+1;until a=0;outdigs(j);end{:63};{64:}begin write(vplfile,'.');
+f:=10*f+5;delta:=10;
+repeat if delta>1048576 then f:=f+524288-(delta div 2);
+write(vplfile,f div 1048576:1);f:=10*(f mod 1048576);delta:=delta*10;
+until f<=delta;end;{:64};end;{:62}{74:}procedure checkBCPL(k,l:index);
+var j:index;c:byte;begin if tfm[k]>=l then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ',
+'String is too long; I''ve shortened it drastically.');end;tfm[k]:=1;
+end;for j:=k+1 to k+tfm[k]do begin c:=tfm[j];
+if(c=40)or(c=41)then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ',
+'Parenthesis in string has been changed to slash.');end;tfm[j]:=47;
+end else if(c<32)or(c>126)then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Nonstandard ASCII code has been blotted out.')
+;end;tfm[j]:=63;end else if(c>=97)and(c<=122)then tfm[j]:=c-32;end;end;
+{:74}{114:}procedure hashinput;label 10;var cc:0..3;zz:0..255;y:0..255;
+key:integer;t:integer;begin if hashptr=hashsize then goto 10;{115:}
+k:=4*(ligkernbase+(i));y:=tfm[k+1];t:=tfm[k+2];cc:=0;zz:=tfm[k+3];
+if t>=128 then zz:=y else begin case t of 0,6:;5,11:zz:=y;1,7:cc:=1;
+2:cc:=2;3:cc:=3;end;end{:115};key:=256*c+y+1;h:=(1009*key)mod hashsize;
+while hash[h]>0 do begin if hash[h]<=key then begin if hash[h]=key then
+goto 10;t:=hash[h];hash[h]:=key;key:=t;t:=class[h];class[h]:=cc;cc:=t;
+t:=ligz[h];ligz[h]:=zz;zz:=t;end;if h>0 then h:=h-1 else h:=hashsize;
+end;hash[h]:=key;class[h]:=cc;ligz[h]:=zz;hashptr:=hashptr+1;
+hashlist[hashptr]:=h;10:end;{:114}{116:}function f(h,x,y:index):index;
+forward;function eval(x,y:index):index;var key:integer;
+begin key:=256*x+y+1;h:=(1009*key)mod hashsize;
+while hash[h]>key do if h>0 then h:=h-1 else h:=hashsize;
+if hash[h]<key then eval:=y else eval:=f(h,x,y);end;{:116}{117:}
+function f;begin case class[h]of 0:;1:begin class[h]:=4;
+ligz[h]:=eval(ligz[h],y);class[h]:=0;end;2:begin class[h]:=4;
+ligz[h]:=eval(x,ligz[h]);class[h]:=0;end;3:begin class[h]:=4;
+ligz[h]:=eval(eval(x,ligz[h]),y);class[h]:=0;end;4:begin xligcycle:=x;
+yligcycle:=y;ligz[h]:=257;class[h]:=0;end;end;f:=ligz[h];end;{:117}
+{118:}function stringbalance(k,l:integer):boolean;label 45,10;
+var j,bal:integer;begin if l>0 then if vf[k]=32 then goto 45;bal:=0;
+for j:=k to k+l-1 do begin if(vf[j]<32)or(vf[j]>=127)then goto 45;
+if vf[j]=40 then bal:=bal+1 else if vf[j]=41 then if bal=0 then goto 45
+else bal:=bal-1;end;if bal>0 then goto 45;stringbalance:=true;goto 10;
+45:stringbalance:=false;10:end;{:118}{120:}
+procedure outasfix(x:integer);var k:1..3;
+begin if abs(x)>=16777216 then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','Oversize dimension has been reset to zero.');
+end;if x>=0 then tfm[28]:=0 else begin tfm[28]:=255;x:=x+16777216;end;
+for k:=3 downto 1 do begin tfm[28+k]:=x mod 256;x:=x div 256;end;
+outfix(28);end;{:120}{125:}function getbytes(k:integer;
+signed:boolean):integer;var a:integer;
+begin if vfptr+k>vflimit then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','Packet ended prematurely');end;
+k:=vflimit-vfptr;end;a:=vf[vfptr];
+if(k=4)or signed then if a>=128 then a:=a-256;vfptr:=vfptr+1;
+while k>1 do begin a:=a*256+vf[vfptr];vfptr:=vfptr+1;k:=k-1;end;
+getbytes:=a;end;{:125}{131:}function vfinput:boolean;label 9999,10;
+var vfptr:0..vfsize;k:integer;c:integer;begin{31:}read(vffile,tempbyte);
+if tempbyte<>247 then begin writeln('The first byte isn''t `pre''!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;{32:}
+if eof(vffile)then begin writeln('The input file is only one byte long!'
+);writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;read(vffile,tempbyte);if tempbyte<>202 then begin writeln(
+'Wrong VF version number in second byte!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;if eof(vffile)then begin writeln(
+'The input file is only two bytes long!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;read(vffile,tempbyte);vfcount:=11;vfptr:=0;
+if vfptr+tempbyte>=vfsize then begin writeln(
+'The file is bigger than I can handle!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;
+for k:=vfptr to vfptr+tempbyte-1 do begin if eof(vffile)then begin
+writeln('The file ended prematurely!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;read(vffile,vf[k]);end;vfcount:=vfcount+tempbyte;
+vfptr:=vfptr+tempbyte;for k:=0 to vfptr-1 do write(xchr[vf[k]]);
+writeln(' ');count:=0;
+for k:=0 to 7 do begin if eof(vffile)then begin writeln(
+'The file ended prematurely!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;read(vffile,tempbyte);
+if tempbyte=tfm[24+k]then count:=count+1;end;
+realdsize:=(((tfm[28]*256+tfm[29])*256+tfm[30])*256+tfm[31])/1048576;
+if count<>8 then begin writeln('Check sum and/or design size mismatch.')
+;writeln('Data from TFM file will be assumed correct.');end{:32};{33:}
+for k:=0 to 255 do packetstart[k]:=vfsize;fontptr:=0;packetfound:=false;
+fontstart[0]:=vfptr;repeat if eof(vffile)then begin writeln(
+'File ended without a postamble!');tempbyte:=248;
+end else begin read(vffile,tempbyte);vfcount:=vfcount+1;
+if tempbyte<>248 then if tempbyte>242 then{35:}
+begin if packetfound or(tempbyte>=247)then begin writeln('Illegal byte '
+,tempbyte:1,' at beginning of character packet!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;fontnumber[fontptr]:=vfread(tempbyte-242);
+if fontptr=maxfonts then begin writeln(
+'I can''t handle that many fonts!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;if vfptr+14>=vfsize then begin writeln(
+'The file is bigger than I can handle!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;
+for k:=vfptr to vfptr+13 do begin if eof(vffile)then begin writeln(
+'The file ended prematurely!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;read(vffile,vf[k]);end;vfcount:=vfcount+14;
+vfptr:=vfptr+14;
+if vf[vfptr-10]>0 then begin writeln('Mapped font size is too big!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;a:=vf[vfptr-2];l:=vf[vfptr-1];
+if vfptr+a+l>=vfsize then begin writeln(
+'The file is bigger than I can handle!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;
+for k:=vfptr to vfptr+a+l-1 do begin if eof(vffile)then begin writeln(
+'The file ended prematurely!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;read(vffile,vf[k]);end;vfcount:=vfcount+a+l;
+vfptr:=vfptr+a+l;{36:}write('MAPFONT ',fontptr:1,': ');
+for k:=fontstart[fontptr]+14 to vfptr-1 do write(xchr[vf[k]]);
+k:=fontstart[fontptr]+5;
+writeln(' at ',(((vf[k]*256+vf[k+1])*256+vf[k+2])/1048576)*realdsize:2:2
+,'pt'){:36};{39:}fontchars[fontptr]:=vfptr;{44:}
+for k:=1 to namelength do curname[k]:=' ';r:=0;
+for k:=fontstart[fontptr]+14 to vfptr-1 do begin r:=r+1;
+if r+4>namelength then begin writeln('Font name too long for me!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;curname[r]:=xchr[vf[k]];end;curname[r+1]:='.';
+curname[r+2]:='t';curname[r+3]:='f';curname[r+4]:='m';
+curname[r+5]:=chr(0);{:44};
+if not testaccess(4,3)then writeln(
+'---not loaded, TFM file can''t be opened!')else begin reset(tfmfile,
+realnameoffile);fontbc:=0;fontec:=256;readtfmword;
+if b2<128 then begin fontlh:=b2*256+b3;readtfmword;
+if(b0<128)and(b2<128)then begin fontbc:=b0*256+b1;fontec:=b2*256+b3;end;
+end;if fontbc<=fontec then if fontec>255 then writeln(
+'---not loaded, bad TFM file!')else begin for k:=0 to 3+fontlh do begin
+readtfmword;if k=4 then{40:}
+if b0+b1+b2+b3>0 then if(b0<>vf[fontstart[fontptr]])or(b1<>vf[fontstart[
+fontptr]+1])or(b2<>vf[fontstart[fontptr]+2])or(b3<>vf[fontstart[fontptr]
++3])then begin writeln(
+'Check sum in VF file being replaced by TFM check sum');
+vf[fontstart[fontptr]]:=b0;vf[fontstart[fontptr]+1]:=b1;
+vf[fontstart[fontptr]+2]:=b2;vf[fontstart[fontptr]+3]:=b3;end{:40};
+if k=5 then{41:}
+if(b0<>vf[fontstart[fontptr]+8])or(b1<>vf[fontstart[fontptr]+9])or(b2<>
+vf[fontstart[fontptr]+10])or(b3<>vf[fontstart[fontptr]+11])then begin
+writeln('Design size in VF file being replaced by TFM design size');
+vf[fontstart[fontptr]+8]:=b0;vf[fontstart[fontptr]+9]:=b1;
+vf[fontstart[fontptr]+10]:=b2;vf[fontstart[fontptr]+11]:=b3;end{:41};
+end;for k:=fontbc to fontec do begin readtfmword;
+if b0>0 then begin vf[vfptr]:=k;vfptr:=vfptr+1;
+if vfptr=vfsize then begin writeln('I''m out of VF memory!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;end;end;end;if eof(tfmfile)then writeln(
+'---trouble is brewing, TFM file ended too soon!');end;
+vfptr:=vfptr+1{:39};fontptr:=fontptr+1;fontstart[fontptr]:=vfptr;
+end{:35}else{46:}begin if tempbyte=242 then begin pl:=vfread(4);
+c:=vfread(4);count:=vfread(4);end else begin pl:=tempbyte;c:=vfread(1);
+count:=vfread(3);end;
+if((c<bc)or(c>ec)or(tfm[4*(charbase+c)]=0))then begin writeln(
+'Character ',c:1,' does not exist!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;if packetstart[c]<vfsize then writeln(
+'Discarding earlier packet for character ',c:1);
+if count<>tfmwidth(c)then writeln('Incorrect TFM width for character ',c
+:1,' in VF file');if pl<0 then begin writeln('Negative packet length!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;packetstart[c]:=vfptr;
+if vfptr+pl>=vfsize then begin writeln(
+'The file is bigger than I can handle!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;
+for k:=vfptr to vfptr+pl-1 do begin if eof(vffile)then begin writeln(
+'The file ended prematurely!');
+writeln('Sorry, but I can''t go on; are you sure this is a VF?');
+goto 9999;end;read(vffile,vf[k]);end;vfcount:=vfcount+pl;
+vfptr:=vfptr+pl;packetend[c]:=vfptr-1;packetfound:=true;end{:46};end;
+until tempbyte=248{:33};{34:}
+while(tempbyte=248)and not eof(vffile)do begin read(vffile,tempbyte);
+vfcount:=vfcount+1;end;if not eof(vffile)then begin writeln(
+'There''s some extra junk at the end of the VF file.');
+writeln('I''ll proceed as if it weren''t there.');end;
+if vfcount mod 4<>0 then writeln('VF data not a multiple of 4 bytes'){:
+34}{:31};vfinput:=true;goto 10;9999:vfinput:=false;10:end;
+function organize:boolean;label 9999,10;var tfmptr:index;begin{24:}
+read(tfmfile,tfm[0]);if tfm[0]>127 then begin writeln(
+'The first byte of the input file exceeds 127!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;if eof(tfmfile)then begin writeln(
+'The input file is only one byte long!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;read(tfmfile,tfm[1]);lf:=tfm[0]*256+tfm[1];
+if lf=0 then begin writeln(
+'The file claims to have length zero, but that''s impossible!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;if 4*lf-1>tfmsize then begin writeln(
+'The file is bigger than I can handle!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;
+for tfmptr:=2 to 4*lf-1 do begin if eof(tfmfile)then begin writeln(
+'The file has fewer bytes than it claims!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;read(tfmfile,tfm[tfmptr]);end;
+if not eof(tfmfile)then begin writeln(
+'There''s some extra junk at the end of the TFM file,');
+writeln('but I''ll proceed as if it weren''t there.');end{:24};{25:}
+begin tfmptr:=2;begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;lh:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;bc:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;ec:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;nw:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;nh:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;nd:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;ni:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;nl:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;nk:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;ne:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;np:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+if lh<2 then begin writeln('The header length is only ',lh:1,'!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;if nl>4*ligsize then begin writeln(
+'The lig/kern program is longer than I can handle!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;
+if(bc>ec+1)or(ec>255)then begin writeln('The character code range ',bc:1
+,'..',ec:1,'is illegal!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;if(nw=0)or(nh=0)or(nd=0)or(ni=0)then begin writeln(
+'Incomplete subfiles for character dimensions!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;
+if ne>256 then begin writeln('There are ',ne:1,' extensible recipes!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;
+if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then begin writeln(
+'Subfile sizes don''t add up to the stated total!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;end{:25};{27:}begin charbase:=6+lh-bc;
+widthbase:=charbase+ec+1;heightbase:=widthbase+nw;
+depthbase:=heightbase+nh;italicbase:=depthbase+nd;
+ligkernbase:=italicbase+ni;kernbase:=ligkernbase+nl;
+extenbase:=kernbase+nk;parambase:=extenbase+ne-1;end{:27};
+organize:=vfinput;goto 10;9999:organize:=false;10:end;{:131}{132:}
+procedure dosimplethings;var i:0..32767;f:0..vfsize;k:integer;
+begin{119:}if stringbalance(0,fontstart[0])then begin left;
+write(vplfile,'VTITLE ');
+for k:=0 to fontstart[0]-1 do write(vplfile,xchr[vf[k]]);right;
+end else begin perfect:=false;if charsonline>0 then writeln(' ');
+charsonline:=0;
+writeln('Bad VF file: ','Title is not a balanced ASCII string');
+end{:119};{70:}begin fonttype:=0;if lh>=12 then begin{75:}
+begin checkBCPL(32,40);
+if(tfm[32]>=11)and(tfm[33]=84)and(tfm[34]=69)and(tfm[35]=88)and(tfm[36]=
+32)and(tfm[37]=77)and(tfm[38]=65)and(tfm[39]=84)and(tfm[40]=72)and(tfm[
+41]=32)then begin if(tfm[42]=83)and(tfm[43]=89)then fonttype:=1 else if(
+tfm[42]=69)and(tfm[43]=88)then fonttype:=2;end;end{:75};
+if lh>=17 then begin{77:}left;write(vplfile,'FAMILY');checkBCPL(72,20);
+outBCPL(72);right{:77};if lh>=18 then{78:}begin left;
+write(vplfile,'FACE');outface(95);right;for i:=18 to lh-1 do begin left;
+write(vplfile,'HEADER D ',i:1);outoctal(24+4*i,4);right;end;end{:78};
+end;{76:}left;write(vplfile,'CODINGSCHEME');outBCPL(32);right{:76};end;
+{73:}left;write(vplfile,'DESIGNSIZE');
+if tfm[28]>127 then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Design size ','negative','!');end;
+writeln('I''ve set it to 10 points.');write(vplfile,' D 10');
+end else if(tfm[28]=0)and(tfm[29]<16)then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Design size ','too small','!');end;
+writeln('I''ve set it to 10 points.');write(vplfile,' D 10');
+end else outfix(28);right;
+write(vplfile,'(COMMENT DESIGNSIZE IS IN POINTS)');outln;
+write(vplfile,'(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)');
+outln{:73};{71:}left;write(vplfile,'CHECKSUM');outoctal(24,4);right{:71}
+;{79:}if(lh>17)and(tfm[92]>127)then begin left;
+write(vplfile,'SEVENBITSAFEFLAG TRUE');right;end{:79};end{:70};{80:}
+if np>0 then begin left;write(vplfile,'FONTDIMEN');outln;
+for i:=1 to np do{82:}begin left;
+if i=1 then write(vplfile,'SLANT')else begin if(tfm[4*(parambase+i)]>0)
+and(tfm[4*(parambase+i)]<255)then begin tfm[4*(parambase+i)]:=0;
+tfm[(4*(parambase+i))+1]:=0;tfm[(4*(parambase+i))+2]:=0;
+tfm[(4*(parambase+i))+3]:=0;begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Parameter',' ',i:1,' is too big;');end;
+writeln('I have set it to zero.');end;{83:}
+if i<=7 then case i of 2:write(vplfile,'SPACE');
+3:write(vplfile,'STRETCH');4:write(vplfile,'SHRINK');
+5:write(vplfile,'XHEIGHT');6:write(vplfile,'QUAD');
+7:write(vplfile,'EXTRASPACE')end else if(i<=22)and(fonttype=1)then case
+i of 8:write(vplfile,'NUM1');9:write(vplfile,'NUM2');
+10:write(vplfile,'NUM3');11:write(vplfile,'DENOM1');
+12:write(vplfile,'DENOM2');13:write(vplfile,'SUP1');
+14:write(vplfile,'SUP2');15:write(vplfile,'SUP3');
+16:write(vplfile,'SUB1');17:write(vplfile,'SUB2');
+18:write(vplfile,'SUPDROP');19:write(vplfile,'SUBDROP');
+20:write(vplfile,'DELIM1');21:write(vplfile,'DELIM2');
+22:write(vplfile,'AXISHEIGHT')end else if(i<=13)and(fonttype=2)then if i
+=8 then write(vplfile,'DEFAULTRULETHICKNESS')else write(vplfile,
+'BIGOPSPACING',i-8:1)else write(vplfile,'PARAMETER D ',i:1){:83};end;
+outfix(4*(parambase+i));right;end{:82};right;end;{81:}
+if(fonttype=1)and(np<>22)then writeln(
+'Unusual number of fontdimen parameters for a math symbols font (',np:1,
+' not 22).')else if(fonttype=2)and(np<>13)then writeln(
+'Unusual number of fontdimen parameters for an extension font (',np:1,
+' not 13).'){:81};{:80};{121:}for f:=0 to fontptr-1 do begin left;
+write(vplfile,'MAPFONT D ',f:1);outln;{122:}a:=vf[fontstart[f]+12];
+l:=vf[fontstart[f]+13];
+if a>0 then if not stringbalance(fontstart[f]+14,a)then begin perfect:=
+false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','Improper font area will be ignored');
+end else begin left;write(vplfile,'FONTAREA ');
+for k:=fontstart[f]+14 to fontstart[f]+a+13 do write(vplfile,xchr[vf[k]]
+);right;end;
+if(l=0)or not stringbalance(fontstart[f]+14+a,l)then begin perfect:=
+false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','Improper font name will be ignored');
+end else begin left;write(vplfile,'FONTNAME ');
+for k:=fontstart[f]+14+a to fontstart[f]+a+l+13 do write(vplfile,xchr[vf
+[k]]);right;end{:122};for k:=0 to 11 do tfm[k]:=vf[fontstart[f]+k];
+if tfm[0]+tfm[1]+tfm[2]+tfm[3]>0 then begin left;
+write(vplfile,'FONTCHECKSUM');outoctal(0,4);right;end;left;
+write(vplfile,'FONTAT');outfix(4);right;left;write(vplfile,'FONTDSIZE');
+outfix(8);right;right;end{:121};{84:}
+if(tfm[4*widthbase]>0)or(tfm[4*widthbase+1]>0)or(tfm[4*widthbase+2]>0)or
+(tfm[4*widthbase+3]>0)then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','width[0] should be zero.');end;
+if(tfm[4*heightbase]>0)or(tfm[4*heightbase+1]>0)or(tfm[4*heightbase+2]>0
+)or(tfm[4*heightbase+3]>0)then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','height[0] should be zero.');end;
+if(tfm[4*depthbase]>0)or(tfm[4*depthbase+1]>0)or(tfm[4*depthbase+2]>0)or
+(tfm[4*depthbase+3]>0)then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','depth[0] should be zero.');end;
+if(tfm[4*italicbase]>0)or(tfm[4*italicbase+1]>0)or(tfm[4*italicbase+2]>0
+)or(tfm[4*italicbase+3]>0)then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','italic[0] should be zero.');end;
+for i:=0 to nw-1 do if(tfm[4*(widthbase+i)]>0)and(tfm[4*(widthbase+i)]<
+255)then begin tfm[4*(widthbase+i)]:=0;tfm[(4*(widthbase+i))+1]:=0;
+tfm[(4*(widthbase+i))+2]:=0;tfm[(4*(widthbase+i))+3]:=0;
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Width',' ',i:1,' is too big;');end;
+writeln('I have set it to zero.');end;
+for i:=0 to nh-1 do if(tfm[4*(heightbase+i)]>0)and(tfm[4*(heightbase+i)]
+<255)then begin tfm[4*(heightbase+i)]:=0;tfm[(4*(heightbase+i))+1]:=0;
+tfm[(4*(heightbase+i))+2]:=0;tfm[(4*(heightbase+i))+3]:=0;
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Height',' ',i:1,' is too big;');end;
+writeln('I have set it to zero.');end;
+for i:=0 to nd-1 do if(tfm[4*(depthbase+i)]>0)and(tfm[4*(depthbase+i)]<
+255)then begin tfm[4*(depthbase+i)]:=0;tfm[(4*(depthbase+i))+1]:=0;
+tfm[(4*(depthbase+i))+2]:=0;tfm[(4*(depthbase+i))+3]:=0;
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Depth',' ',i:1,' is too big;');end;
+writeln('I have set it to zero.');end;
+for i:=0 to ni-1 do if(tfm[4*(italicbase+i)]>0)and(tfm[4*(italicbase+i)]
+<255)then begin tfm[4*(italicbase+i)]:=0;tfm[(4*(italicbase+i))+1]:=0;
+tfm[(4*(italicbase+i))+2]:=0;tfm[(4*(italicbase+i))+3]:=0;
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Italic correction',' ',i:1,' is too big;');
+end;writeln('I have set it to zero.');end;
+if nk>0 then for i:=0 to nk-1 do if(tfm[4*(kernbase+i)]>0)and(tfm[4*(
+kernbase+i)]<255)then begin tfm[4*(kernbase+i)]:=0;
+tfm[(4*(kernbase+i))+1]:=0;tfm[(4*(kernbase+i))+2]:=0;
+tfm[(4*(kernbase+i))+3]:=0;begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Kern',' ',i:1,' is too big;');end;
+writeln('I have set it to zero.');end;{:84};end;{:132}{133:}
+function domap(c:byte):boolean;label 9999,10;var k:integer;f:0..vfsize;
+begin{124:}if packetstart[c]=vfsize then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','Missing packet for character ',c:1);
+end else begin left;write(vplfile,'MAP');outln;top:=0;wstack[0]:=0;
+xstack[0]:=0;ystack[0]:=0;zstack[0]:=0;vfptr:=packetstart[c];
+vflimit:=packetend[c]+1;f:=0;while vfptr<vflimit do begin o:=vf[vfptr];
+vfptr:=vfptr+1;case o of{126:}138:;
+141:begin if top=maxstack then begin writeln('Stack overflow!');
+goto 9999;end;top:=top+1;wstack[top]:=wstack[top-1];
+xstack[top]:=xstack[top-1];ystack[top]:=ystack[top-1];
+zstack[top]:=zstack[top-1];write(vplfile,'(PUSH)');outln;end;
+142:if top=0 then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','More pops than pushes!');
+end else begin top:=top-1;write(vplfile,'(POP)');outln;end;
+132,137:begin if o=137 then write(vplfile,'(PUSH)');left;
+write(vplfile,'SETRULE');outasfix(getbytes(4,true));
+outasfix(getbytes(4,true));if o=137 then write(vplfile,')(POP');right;
+end;{:126}{127:}143,144,145,146:begin write(vplfile,'(MOVERIGHT');
+outasfix(getbytes(o-142,true));write(vplfile,')');outln;end;
+147,148,149,150,151:begin if o<>147 then wstack[top]:=getbytes(o-147,
+true);write(vplfile,'(MOVERIGHT');outasfix(wstack[top]);
+write(vplfile,')');outln;end;
+152,153,154,155,156:begin if o<>152 then xstack[top]:=getbytes(o-152,
+true);write(vplfile,'(MOVERIGHT');outasfix(xstack[top]);
+write(vplfile,')');outln;end;
+157,158,159,160:begin write(vplfile,'(MOVEDOWN');
+outasfix(getbytes(o-156,true));write(vplfile,')');outln;end;
+161,162,163,164,165:begin if o<>161 then ystack[top]:=getbytes(o-161,
+true);write(vplfile,'(MOVEDOWN');outasfix(ystack[top]);
+write(vplfile,')');outln;end;
+166,167,168,169,170:begin if o<>166 then zstack[top]:=getbytes(o-166,
+true);write(vplfile,'(MOVEDOWN');outasfix(zstack[top]);
+write(vplfile,')');outln;end;{:127}{128:}
+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:begin f:=0;
+if o>=235 then fontnumber[fontptr]:=getbytes(o-234,false)else fontnumber
+[fontptr]:=o-171;while fontnumber[f]<>fontnumber[fontptr]do f:=f+1;
+if f=fontptr then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','Undeclared font selected');
+end else begin write(vplfile,'(SELECTFONT D ',f:1,')');outln;end;end;
+{:128}{129:}
+0,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,133,134,135,
+136:begin if o>=128 then if o>=133 then c:=getbytes(o-132,false)else c:=
+getbytes(o-127,false)else c:=o;if f=fontptr then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','Character ',c:1,
+' in undeclared font will be ignored');
+end else begin vf[fontstart[f+1]-1]:=c;k:=fontchars[f];
+while vf[k]<>c do k:=k+1;
+if k=fontstart[f+1]-1 then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','Character ',c:1,' in font ',f:1,
+' will be ignored');
+end else begin if o>=133 then write(vplfile,'(PUSH)');left;
+write(vplfile,'SETCHAR');outchar(c);
+if o>=133 then write(vplfile,')(POP');right;end;end;end;{:129}{130:}
+239,240,241,242:begin k:=getbytes(o-238,false);
+if k<0 then begin perfect:=false;if charsonline>0 then writeln(' ');
+charsonline:=0;writeln('Bad VF file: ','String of negative length!');
+end else begin left;if k+vfptr>vflimit then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','Special command truncated to packet length');
+end;k:=vflimit-vfptr;end;
+if(k>64)or not stringbalance(vfptr,k)then begin write(vplfile,
+'SPECIALHEX ');
+while k>0 do begin if k mod 32=0 then outln else if k mod 4=0 then write
+(vplfile,' ');begin a:=vf[vfptr]div 16;
+if a<10 then write(vplfile,a:1)else write(vplfile,xchr[a+55]);end;
+begin a:=vf[vfptr]mod 16;
+if a<10 then write(vplfile,a:1)else write(vplfile,xchr[a+55]);end;
+vfptr:=vfptr+1;k:=k-1;end;end else begin write(vplfile,'SPECIAL ');
+while k>0 do begin write(vplfile,xchr[vf[vfptr]]);vfptr:=vfptr+1;k:=k-1;
+end;end;right;end;end;{:130}
+139,140,243,244,245,246,247,248,249,250,251,252,253,254,255:begin
+perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','Illegal DVI code ',o:1,' will be ignored');end;
+end;end;if top>0 then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad VF file: ','More pushes than pops!');end;
+repeat write(vplfile,'(POP)');top:=top-1;until top=0;end;right;end{:124}
+;domap:=true;goto 10;9999:domap:=false;10:end;
+function docharacters:boolean;label 9999,10;var c:byte;k:index;
+ai:0..ligsize;begin{100:}sortptr:=0;
+for c:=bc to ec do if tfm[4*(charbase+c)]>0 then begin if charsonline=8
+then begin writeln(' ');charsonline:=1;
+end else begin if charsonline>0 then write(' ');
+charsonline:=charsonline+1;end;printoctal(c);left;
+write(vplfile,'CHARACTER');outchar(c);outln;{101:}begin left;
+write(vplfile,'CHARWD');
+if tfm[4*(charbase+c)]>=nw then begin perfect:=false;writeln(' ');
+write('Width',' index for character ');printoctal(c);
+writeln(' is too large;');writeln('so I reset it to zero.');
+end else outfix(4*(widthbase+tfm[4*(charbase+c)]));right;end{:101};
+if(tfm[4*(charbase+c)+1]div 16)>0 then{102:}
+if(tfm[4*(charbase+c)+1]div 16)>=nh then begin perfect:=false;
+writeln(' ');write('Height',' index for character ');printoctal(c);
+writeln(' is too large;');writeln('so I reset it to zero.');
+end else begin left;write(vplfile,'CHARHT');
+outfix(4*(heightbase+(tfm[4*(charbase+c)+1]div 16)));right;end{:102};
+if(tfm[4*(charbase+c)+1]mod 16)>0 then{103:}
+if(tfm[4*(charbase+c)+1]mod 16)>=nd then begin perfect:=false;
+writeln(' ');write('Depth',' index for character ');printoctal(c);
+writeln(' is too large;');writeln('so I reset it to zero.');
+end else begin left;write(vplfile,'CHARDP');
+outfix(4*(depthbase+(tfm[4*(charbase+c)+1]mod 16)));right;end{:103};
+if(tfm[4*(charbase+c)+2]div 4)>0 then{104:}
+if(tfm[4*(charbase+c)+2]div 4)>=ni then begin perfect:=false;
+writeln(' ');write('Italic correction',' index for character ');
+printoctal(c);writeln(' is too large;');
+writeln('so I reset it to zero.');end else begin left;
+write(vplfile,'CHARIC');
+outfix(4*(italicbase+(tfm[4*(charbase+c)+2]div 4)));right;end{:104};
+case(tfm[4*(charbase+c)+2]mod 4)of 0:;1:{105:}begin left;
+write(vplfile,'COMMENT');outln;i:=tfm[4*(charbase+c)+3];
+r:=4*(ligkernbase+(i));if tfm[r]>128 then i:=256*tfm[r+2]+tfm[r+3];
+repeat{96:}begin k:=4*(ligkernbase+(i));
+if tfm[k]>128 then begin if 256*tfm[k+2]+tfm[k+3]>=nl then begin perfect
+:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ',
+'Ligature unconditional stop command address is too big.');end;
+end else if tfm[k+2]>=128 then{98:}
+begin if((tfm[k+1]<bc)or(tfm[k+1]>ec)or(tfm[4*(charbase+tfm[k+1])]=0))
+then if tfm[k+1]<>boundarychar then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Kern step for',' nonexistent character ');
+printoctal(tfm[k+1]);writeln('.');tfm[k+1]:=bc;end;left;
+write(vplfile,'KRN');outchar(tfm[k+1]);r:=256*(tfm[k+2]-128)+tfm[k+3];
+if r>=nk then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Kern index too large.');end;
+write(vplfile,' R 0.0');end else outfix(4*(kernbase+r));right;end{:98}
+else{99:}
+begin if((tfm[k+1]<bc)or(tfm[k+1]>ec)or(tfm[4*(charbase+tfm[k+1])]=0))
+then if tfm[k+1]<>boundarychar then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Ligature step for',' nonexistent character ');
+printoctal(tfm[k+1]);writeln('.');tfm[k+1]:=bc;end;
+if((tfm[k+3]<bc)or(tfm[k+3]>ec)or(tfm[4*(charbase+tfm[k+3])]=0))then
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Ligature step produces the',
+' nonexistent character ');printoctal(tfm[k+3]);writeln('.');
+tfm[k+3]:=bc;end;left;r:=tfm[k+2];
+if(r=4)or((r>7)and(r<>11))then begin writeln(
+'Ligature step with nonstandard code changed to LIG');r:=0;tfm[k+2]:=0;
+end;if r mod 4>1 then write(vplfile,'/');write(vplfile,'LIG');
+if odd(r)then write(vplfile,'/');while r>3 do begin write(vplfile,'>');
+r:=r-4;end;outchar(tfm[k+1]);outchar(tfm[k+3]);right;end{:99};
+if tfm[k]>0 then if level=1 then{97:}
+begin if tfm[k]>=128 then write(vplfile,'(STOP)')else begin count:=0;
+for ai:=i+1 to i+tfm[k]do if activity[ai]=2 then count:=count+1;
+write(vplfile,'(SKIP D ',count:1,')');end;outln;end{:97};end{:96};
+if tfm[k]>=128 then i:=nl else i:=i+1+tfm[k];until i>=nl;right;end{:105}
+;2:{106:}begin r:=tfm[4*(charbase+c)+3];
+if((r<bc)or(r>ec)or(tfm[4*(charbase+r)]=0))then begin begin perfect:=
+false;if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Character list link to',
+' nonexistent character ');printoctal(r);writeln('.');end;
+tfm[4*(charbase+c)+2]:=4*(tfm[4*(charbase+c)+2]div 4)+0;
+end else begin while(r<c)and((tfm[4*(charbase+r)+2]mod 4)=2)do r:=tfm[4*
+(charbase+r)+3];if r=c then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Cycle in a character list!');end;
+write('Character ');printoctal(c);writeln(' now ends the list.');
+tfm[4*(charbase+c)+2]:=4*(tfm[4*(charbase+c)+2]div 4)+0;
+end else begin left;write(vplfile,'NEXTLARGER');
+outchar(tfm[4*(charbase+c)+3]);right;end;end;end{:106};3:{107:}
+if tfm[4*(charbase+c)+3]>=ne then begin begin perfect:=false;
+writeln(' ');write('Extensible',' index for character ');printoctal(c);
+writeln(' is too large;');writeln('so I reset it to zero.');end;
+tfm[4*(charbase+c)+2]:=4*(tfm[4*(charbase+c)+2]div 4)+0;
+end else begin left;write(vplfile,'VARCHAR');outln;{108:}
+for k:=0 to 3 do if(k=3)or(tfm[4*(extenbase+tfm[4*(charbase+c)+3])+k]>0)
+then begin left;case k of 0:write(vplfile,'TOP');1:write(vplfile,'MID');
+2:write(vplfile,'BOT');3:write(vplfile,'REP')end;
+if((tfm[4*(extenbase+tfm[4*(charbase+c)+3])+k]<bc)or(tfm[4*(extenbase+
+tfm[4*(charbase+c)+3])+k]>ec)or(tfm[4*(charbase+tfm[4*(extenbase+tfm[4*(
+charbase+c)+3])+k])]=0))then outchar(c)else outchar(tfm[4*(extenbase+tfm
+[4*(charbase+c)+3])+k]);right;end{:108};right;end{:107};end;
+if not domap(c)then goto 9999;right;end{:100};docharacters:=true;
+goto 10;9999:docharacters:=false;10:end;{:133}{134:}begin initialize;
+if not organize then goto 9999;dosimplethings;{88:}
+if nl>0 then begin for ai:=0 to nl-1 do activity[ai]:=0;{91:}
+if tfm[4*(ligkernbase+(0))]=255 then begin left;
+write(vplfile,'BOUNDARYCHAR');boundarychar:=tfm[4*(ligkernbase+(0))+1];
+outchar(boundarychar);right;activity[0]:=1;end;
+if tfm[4*(ligkernbase+(nl-1))]=255 then begin r:=256*tfm[4*(ligkernbase+
+(nl-1))+2]+tfm[4*(ligkernbase+(nl-1))+3];
+if r>=nl then begin perfect:=false;writeln(' ');
+write('Ligature/kern starting index for boundarychar is too large;');
+writeln('so I removed it.');end else begin labelptr:=1;
+labeltable[1].cc:=256;labeltable[1].rr:=r;bcharlabel:=r;activity[r]:=2;
+end;activity[nl-1]:=1;end{:91};end;{89:}
+for c:=bc to ec do if(tfm[4*(charbase+c)+2]mod 4)=1 then begin r:=tfm[4*
+(charbase+c)+3];
+if r<nl then begin if tfm[4*(ligkernbase+(r))]>128 then begin r:=256*tfm
+[4*(ligkernbase+(r))+2]+tfm[4*(ligkernbase+(r))+3];
+if r<nl then if activity[tfm[4*(charbase+c)+3]]=0 then activity[tfm[4*(
+charbase+c)+3]]:=1;end;end;if r>=nl then begin perfect:=false;
+writeln(' ');write('Ligature/kern starting index for character ');
+printoctal(c);writeln(' is too large;');writeln('so I removed it.');
+tfm[4*(charbase+c)+2]:=4*(tfm[4*(charbase+c)+2]div 4)+0;end else{90:}
+begin sortptr:=labelptr;
+while labeltable[sortptr].rr>r do begin labeltable[sortptr+1]:=
+labeltable[sortptr];sortptr:=sortptr-1;end;labeltable[sortptr+1].cc:=c;
+labeltable[sortptr+1].rr:=r;labelptr:=labelptr+1;activity[r]:=2;end{:90}
+;end;labeltable[labelptr+1].rr:=ligsize;{:89};if nl>0 then begin left;
+write(vplfile,'LIGTABLE');outln;{92:}
+for ai:=0 to nl-1 do if activity[ai]=2 then begin r:=tfm[4*(ligkernbase+
+(ai))];if r<128 then begin r:=r+ai+1;
+if r>=nl then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Ligature/kern step ',ai:1,' skips too far;');
+end;writeln('I made it stop.');tfm[4*(ligkernbase+(ai))]:=128;
+end else activity[r]:=2;end;end{:92};{93:}sortptr:=1;
+for acti:=0 to nl-1 do if activity[acti]<>1 then begin i:=acti;{95:}
+if activity[i]=0 then begin if level=1 then begin left;
+write(vplfile,'COMMENT THIS PART OF THE PROGRAM IS NEVER USED!');outln;
+end end else if level=2 then right{:95};{94:}
+while i=labeltable[sortptr].rr do begin left;write(vplfile,'LABEL');
+if labeltable[sortptr].cc=256 then write(vplfile,' BOUNDARYCHAR')else
+outchar(labeltable[sortptr].cc);right;sortptr:=sortptr+1;end{:94};{96:}
+begin k:=4*(ligkernbase+(i));
+if tfm[k]>128 then begin if 256*tfm[k+2]+tfm[k+3]>=nl then begin perfect
+:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ',
+'Ligature unconditional stop command address is too big.');end;
+end else if tfm[k+2]>=128 then{98:}
+begin if((tfm[k+1]<bc)or(tfm[k+1]>ec)or(tfm[4*(charbase+tfm[k+1])]=0))
+then if tfm[k+1]<>boundarychar then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Kern step for',' nonexistent character ');
+printoctal(tfm[k+1]);writeln('.');tfm[k+1]:=bc;end;left;
+write(vplfile,'KRN');outchar(tfm[k+1]);r:=256*(tfm[k+2]-128)+tfm[k+3];
+if r>=nk then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Kern index too large.');end;
+write(vplfile,' R 0.0');end else outfix(4*(kernbase+r));right;end{:98}
+else{99:}
+begin if((tfm[k+1]<bc)or(tfm[k+1]>ec)or(tfm[4*(charbase+tfm[k+1])]=0))
+then if tfm[k+1]<>boundarychar then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Ligature step for',' nonexistent character ');
+printoctal(tfm[k+1]);writeln('.');tfm[k+1]:=bc;end;
+if((tfm[k+3]<bc)or(tfm[k+3]>ec)or(tfm[4*(charbase+tfm[k+3])]=0))then
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Ligature step produces the',
+' nonexistent character ');printoctal(tfm[k+3]);writeln('.');
+tfm[k+3]:=bc;end;left;r:=tfm[k+2];
+if(r=4)or((r>7)and(r<>11))then begin writeln(
+'Ligature step with nonstandard code changed to LIG');r:=0;tfm[k+2]:=0;
+end;if r mod 4>1 then write(vplfile,'/');write(vplfile,'LIG');
+if odd(r)then write(vplfile,'/');while r>3 do begin write(vplfile,'>');
+r:=r-4;end;outchar(tfm[k+1]);outchar(tfm[k+3]);right;end{:99};
+if tfm[k]>0 then if level=1 then{97:}
+begin if tfm[k]>=128 then write(vplfile,'(STOP)')else begin count:=0;
+for ai:=i+1 to i+tfm[k]do if activity[ai]=2 then count:=count+1;
+write(vplfile,'(SKIP D ',count:1,')');end;outln;end{:97};end{:96};end;
+if level=2 then right{:93};right;{112:}hashptr:=0;yligcycle:=256;
+for hh:=0 to hashsize do hash[hh]:=0;
+for c:=bc to ec do if(tfm[4*(charbase+c)+2]mod 4)=1 then begin i:=tfm[4*
+(charbase+c)+3];
+if tfm[4*(ligkernbase+(i))]>128 then i:=256*tfm[4*(ligkernbase+(i))+2]+
+tfm[4*(ligkernbase+(i))+3];{113:}repeat hashinput;
+k:=tfm[4*(ligkernbase+(i))];if k>=128 then i:=nl else i:=i+1+k;
+until i>=nl{:113};end;if bcharlabel<nl then begin c:=256;i:=bcharlabel;
+{113:}repeat hashinput;k:=tfm[4*(ligkernbase+(i))];
+if k>=128 then i:=nl else i:=i+1+k;until i>=nl{:113};end;
+if hashptr=hashsize then begin writeln(
+'Sorry, I haven''t room for so many ligature/kern pairs!');goto 9999;
+end;for hh:=1 to hashptr do begin r:=hashlist[hh];
+if class[r]>0 then r:=f(r,(hash[r]-1)div 256,(hash[r]-1)mod 256);end;
+if yligcycle<256 then begin write(
+'Infinite ligature loop starting with ');
+if xligcycle=256 then write('boundary')else printoctal(xligcycle);
+write(' and ');printoctal(yligcycle);writeln('!');
+write(vplfile,'(INFINITE LIGATURE LOOP MUST BE BROKEN!)');goto 9999;
+end{:112};end{:88};{109:}
+if ne>0 then for c:=0 to ne-1 do for d:=0 to 3 do begin k:=4*(extenbase+
+c)+d;if(tfm[k]>0)or(d=3)then begin if((tfm[k]<bc)or(tfm[k]>ec)or(tfm[4*(
+charbase+tfm[k])]=0))then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Extensible recipe involves the',
+' nonexistent character ');printoctal(tfm[k]);writeln('.');end;
+if d<3 then tfm[k]:=0;end;end;end{:109};
+if not docharacters then goto 9999;writeln('.');
+if level<>0 then writeln('This program isn''t working!');
+if not perfect then begin write(vplfile,
+'(COMMENT THE TFM AND/OR VF FILE WAS BAD, ');
+write(vplfile,'SO THE DATA HAS BEEN CHANGED!)');end;9999:outln;
+9998:writeln(' ');end.{:134}
diff --git a/systems/knuth/local/etc/vptovf.ch b/systems/knuth/local/etc/vptovf.ch
new file mode 100644
index 0000000000..b3ae4055fe
--- /dev/null
+++ b/systems/knuth/local/etc/vptovf.ch
@@ -0,0 +1,108 @@
+% Change file for the VPtoVF processor, for use with GNU Pascal
+% (by Don Knuth; see ../etc-sparc for its prehistory)
+
+% History:
+% 2000.04.30 Initial version
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{VP\lowercase{to}VF changes for GNU Pascal}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is VPtoVF, Version 1.5' {printed when the program starts}
+@y
+@d banner=='This is VPtoVF, Version 1.5 for Linux'
+ {printed when the program starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Fix filenames in program statement; add `final_end' label; add access
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program VPtoVF(@!vpl_file,@!vf_file,@!tfm_file,@!output);
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@y
+@d final_end==9999
+
+@p program VPtoVF(@!output);
+label final_end;
+const @<Constants in the outer block@>@/
+type UNIX_file_name=packed array[1..100] of char;
+ @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\
+@=#include "texwarext.h"@>
+@\@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6] Open VPL file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reset(vpl_file);
+@y
+if argc < 4 then begin
+ print_ln('Usage: vptovf <vpl-file> <vf-file> <tfm-file>');
+ goto final_end;
+end;
+argv(1, vpl_name);
+if testreadaccess(vpl_name) then reset(vpl_file, vpl_name)
+else begin print_ln('I can''t read the VPL file!'); goto final_end;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] Change type of output files and declare extra binary-file variables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!vf_file:packed file of 0..255;
+@!tfm_file:packed file of 0..255;
+@y
+@!vf_file,
+@!tfm_file : packed file of ByteCard;
+@!vf_name, @!tfm_name,
+@!vpl_name : UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [22] Open output files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ On some systems you may have to do something special to write a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(vf_file); rewrite(tfm_file);
+@y
+@ On some systems you may have to do something special to write a
+packed file of bytes.
+@^system dependencies@>
+
+@<Set init...@>=
+argv(2, vf_name); argv(3, tfm_name);
+rewrite(vf_file, vf_name); rewrite(tfm_file, tfm_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [181] Define label `final_end'; print newline at end of program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+end.
+@y
+final_end: print_ln(' '); end.
+@z
diff --git a/systems/knuth/local/etc/vptovf.p b/systems/knuth/local/etc/vptovf.p
new file mode 100644
index 0000000000..f2f3fdc0c9
--- /dev/null
+++ b/systems/knuth/local/etc/vptovf.p
@@ -0,0 +1,1112 @@
+{2:}program VPtoVF(output);label 9999;const{3:}bufsize=60;
+maxheaderbytes=100;vfsize=10000;maxstack=100;maxparamwords=30;
+maxligsteps=5000;maxkerns=500;hashsize=5003;{:3}
+type UNIXfilename=packed array[1..100]of char;{23:}byte=0..255;
+ASCIIcode=32..127;{:23}{66:}fourbytes=record b0:byte;b1:byte;b2:byte;
+b3:byte;end;{:66}{71:}fixword=integer;{:71}{78:}
+headerindex=0..maxheaderbytes;indx=0..32767;{:78}{81:}pointer=0..1032;
+{:81}var{5:}vplfile:text;{:5}{21:}
+vffile,tfmfile:packed file of ByteCard;
+vfname,tfmname,vplname:UNIXfilename;{:21}{24:}
+xord:array[char]of ASCIIcode;{:24}{27:}line:integer;goodindent:integer;
+indent:integer;level:integer;{:27}{29:}leftln,rightln:boolean;
+limit:0..bufsize;loc:0..bufsize;buffer:array[1..bufsize]of char;
+inputhasended:boolean;{:29}{31:}charsonline:0..8;{:31}{36:}
+curchar:ASCIIcode;{:36}{44:}start:array[1..100]of 0..666;
+dictionary:array[0..666]of ASCIIcode;startptr:0..100;dictptr:0..666;
+{:44}{46:}curname:array[1..20]of ASCIIcode;namelength:0..20;
+nameptr:0..100;{:46}{47:}nhash:array[0..140]of 0..100;curhash:0..140;
+{:47}{52:}equiv:array[0..100]of byte;curcode:byte;{:52}{67:}
+curbytes:fourbytes;zerobytes:fourbytes;{:67}{75:}
+fractiondigits:array[1..7]of integer;{:75}{77:}
+headerbytes:array[headerindex]of byte;headerptr:headerindex;
+designsize:fixword;designunits:fixword;frozendu:boolean;
+sevenbitsafeflag:boolean;ligkern:array[0..maxligsteps]of fourbytes;
+nl:0..32767;minnl:0..32767;kern:array[0..maxkerns]of fixword;
+nk:0..maxkerns;exten:array[0..255]of fourbytes;ne:0..256;
+param:array[1..maxparamwords]of fixword;np:0..maxparamwords;
+checksumspecified:boolean;bchar:0..256;vf:array[0..vfsize]of byte;
+vfptr:0..vfsize;vtitlestart:0..vfsize;vtitlelength:byte;
+packetstart:array[byte]of 0..vfsize;packetlength:array[byte]of integer;
+fontptr:0..256;curfont:0..256;fnamestart:array[byte]of 0..vfsize;
+fnamelength:array[byte]of byte;fareastart:array[byte]of 0..vfsize;
+farealength:array[byte]of byte;fontchecksum:array[byte]of fourbytes;
+fontnumber:array[0..256]of fourbytes;fontat:array[byte]of fixword;
+fontdsize:array[byte]of fixword;{:77}{82:}
+memory:array[pointer]of fixword;memptr:pointer;
+link:array[pointer]of pointer;charwd:array[byte]of pointer;
+charht:array[byte]of pointer;chardp:array[byte]of pointer;
+charic:array[byte]of pointer;chartag:array[byte]of 0..3;
+charremainder:array[0..256]of 0..65535;{:82}{86:}nextd:fixword;{:86}
+{89:}index:array[pointer]of byte;excess:byte;{:89}{91:}c:byte;x:fixword;
+k:integer;{:91}{113:}lkstepended:boolean;krnptr:0..maxkerns;{:113}{123:}
+hstack:array[0..maxstack]of 0..2;vstack:array[0..maxstack]of 0..2;
+wstack,xstack,ystack,zstack:array[0..maxstack]of fixword;
+stackptr:0..maxstack;{:123}{138:}sevenunsafe:boolean;{:138}{143:}
+delta:fixword;{:143}{147:}ligptr:0..maxligsteps;
+hash:array[0..hashsize]of 0..66048;class:array[0..hashsize]of 0..4;
+ligz:array[0..hashsize]of 0..257;hashptr:0..hashsize;
+hashlist:array[0..hashsize]of 0..hashsize;h,hh:0..hashsize;tt:indx;
+xligcycle,yligcycle:0..256;{:147}{158:}bc:byte;ec:byte;lh:byte;
+lf:0..32767;notfound:boolean;tempwidth:fixword;{:158}{161:}
+j:0..maxheaderbytes;p:pointer;q:1..4;parptr:0..maxparamwords;{:161}
+{167:}labeltable:array[0..256]of record rr:-1..32767;cc:byte;end;
+labelptr:0..256;sortptr:0..256;lkoffset:0..256;t:0..32767;
+extralocneeded:boolean;{:167}{175:}vcount:integer;{:175}
+#include "texwarext.h"
+procedure initialize;var{25:}k:integer;{:25}{48:}h:0..140;{:48}{79:}
+d:headerindex;{:79}{83:}c:byte;{:83}
+begin writeln('This is VPtoVF, Version 1.5 for Linux');{6:}
+if argc<4 then begin writeln(
+'Usage: vptovf <vpl-file> <vf-file> <tfm-file>');goto 9999;end;
+argv(1,vplname);
+if testreadaccess(vplname)then reset(vplfile,vplname)else begin writeln(
+'I can''t read the VPL file!');goto 9999;end;{:6}{22:}argv(2,vfname);
+argv(3,tfmname);rewrite(vffile,vfname);rewrite(tfmfile,tfmname);{:22}
+{26:}for k:=0 to 127 do xord[chr(k)]:=127;xord[' ']:=32;xord['!']:=33;
+xord['"']:=34;xord['#']:=35;xord['$']:=36;xord['%']:=37;xord['&']:=38;
+xord['''']:=39;xord['(']:=40;xord[')']:=41;xord['*']:=42;xord['+']:=43;
+xord[',']:=44;xord['-']:=45;xord['.']:=46;xord['/']:=47;xord['0']:=48;
+xord['1']:=49;xord['2']:=50;xord['3']:=51;xord['4']:=52;xord['5']:=53;
+xord['6']:=54;xord['7']:=55;xord['8']:=56;xord['9']:=57;xord[':']:=58;
+xord[';']:=59;xord['<']:=60;xord['=']:=61;xord['>']:=62;xord['?']:=63;
+xord['@']:=64;xord['A']:=65;xord['B']:=66;xord['C']:=67;xord['D']:=68;
+xord['E']:=69;xord['F']:=70;xord['G']:=71;xord['H']:=72;xord['I']:=73;
+xord['J']:=74;xord['K']:=75;xord['L']:=76;xord['M']:=77;xord['N']:=78;
+xord['O']:=79;xord['P']:=80;xord['Q']:=81;xord['R']:=82;xord['S']:=83;
+xord['T']:=84;xord['U']:=85;xord['V']:=86;xord['W']:=87;xord['X']:=88;
+xord['Y']:=89;xord['Z']:=90;xord['[']:=91;xord['\']:=92;xord[']']:=93;
+xord['^']:=94;xord['_']:=95;xord['`']:=96;xord['a']:=97;xord['b']:=98;
+xord['c']:=99;xord['d']:=100;xord['e']:=101;xord['f']:=102;
+xord['g']:=103;xord['h']:=104;xord['i']:=105;xord['j']:=106;
+xord['k']:=107;xord['l']:=108;xord['m']:=109;xord['n']:=110;
+xord['o']:=111;xord['p']:=112;xord['q']:=113;xord['r']:=114;
+xord['s']:=115;xord['t']:=116;xord['u']:=117;xord['v']:=118;
+xord['w']:=119;xord['x']:=120;xord['y']:=121;xord['z']:=122;
+xord['{']:=123;xord['|']:=124;xord['}']:=125;xord['~']:=126;{:26}{28:}
+line:=0;goodindent:=0;indent:=0;level:=0;{:28}{30:}limit:=0;loc:=0;
+leftln:=true;rightln:=true;inputhasended:=false;{:30}{32:}
+charsonline:=0;{:32}{45:}startptr:=1;start[1]:=0;dictptr:=0;{:45}{49:}
+for h:=0 to 140 do nhash[h]:=0;{:49}{68:}zerobytes.b0:=0;
+zerobytes.b1:=0;zerobytes.b2:=0;zerobytes.b3:=0;{:68}{80:}
+for d:=0 to 18*4-1 do headerbytes[d]:=0;headerbytes[8]:=11;
+headerbytes[9]:=85;headerbytes[10]:=78;headerbytes[11]:=83;
+headerbytes[12]:=80;headerbytes[13]:=69;headerbytes[14]:=67;
+headerbytes[15]:=73;headerbytes[16]:=70;headerbytes[17]:=73;
+headerbytes[18]:=69;headerbytes[19]:=68;
+for d:=48 to 59 do headerbytes[d]:=headerbytes[d-40];
+designsize:=10*1048576;designunits:=1048576;frozendu:=false;
+sevenbitsafeflag:=false;headerptr:=18*4;nl:=0;minnl:=0;nk:=0;ne:=0;
+np:=0;checksumspecified:=false;bchar:=256;vfptr:=0;vtitlestart:=0;
+vtitlelength:=0;fontptr:=0;for k:=0 to 255 do packetstart[k]:=vfsize;
+for k:=0 to 127 do packetlength[k]:=1;
+for k:=128 to 255 do packetlength[k]:=2;{:80}{84:}
+charremainder[256]:=32767;for c:=0 to 255 do begin charwd[c]:=0;
+charht[c]:=0;chardp[c]:=0;charic[c]:=0;chartag[c]:=0;
+charremainder[c]:=0;end;memory[0]:=2147483647;memory[1]:=0;link[1]:=0;
+memory[2]:=0;link[2]:=0;memory[3]:=0;link[3]:=0;memory[4]:=0;link[4]:=0;
+memptr:=4;{:84}{148:}hashptr:=0;yligcycle:=256;
+for k:=0 to hashsize do hash[k]:=0;{:148}end;{:2}{33:}
+procedure showerrorcontext;var k:0..bufsize;
+begin writeln(' (line ',line:1,').');if not leftln then write('...');
+for k:=1 to loc do write(buffer[k]);writeln(' ');
+if not leftln then write(' ');for k:=1 to loc do write(' ');
+for k:=loc+1 to limit do write(buffer[k]);
+if rightln then writeln(' ')else writeln('...');charsonline:=0;end;{:33}
+{34:}procedure fillbuffer;begin leftln:=rightln;limit:=0;loc:=0;
+if leftln then begin if line>0 then readln(vplfile);line:=line+1;end;
+if eof(vplfile)then begin limit:=1;buffer[1]:=')';rightln:=false;
+inputhasended:=true;
+end else begin while(limit<bufsize-1)and(not eoln(vplfile))do begin
+limit:=limit+1;read(vplfile,buffer[limit]);end;buffer[limit+1]:=' ';
+rightln:=eoln(vplfile);if leftln then{35:}
+begin while(loc<limit)and(buffer[loc+1]=' ')do loc:=loc+1;
+if loc<limit then begin if level=0 then if loc=0 then goodindent:=
+goodindent+1 else begin if goodindent>=10 then begin if charsonline>0
+then writeln(' ');
+write('Warning: Indented line occurred at level zero');showerrorcontext;
+end;goodindent:=0;indent:=0;
+end else if indent=0 then if loc mod level=0 then begin indent:=loc div
+level;goodindent:=1;
+end else goodindent:=0 else if indent*level=loc then goodindent:=
+goodindent+1 else begin if goodindent>=10 then begin if charsonline>0
+then writeln(' ');write('Warning: Inconsistent indentation; ',
+'you are at parenthesis level ',level:1);showerrorcontext;end;
+goodindent:=0;indent:=0;end;end;end{:35};end;end;{:34}{37:}
+procedure getkeywordchar;
+begin while(loc=limit)and(not rightln)do fillbuffer;
+if loc=limit then curchar:=32 else begin curchar:=xord[buffer[loc+1]];
+if curchar>=97 then curchar:=curchar-32;
+if((curchar>=48)and(curchar<=57))then loc:=loc+1 else if((curchar>=65)
+and(curchar<=90))then loc:=loc+1 else if curchar=47 then loc:=loc+1 else
+if curchar=62 then loc:=loc+1 else curchar:=32;end;end;{:37}{38:}
+procedure getnext;begin while loc=limit do fillbuffer;loc:=loc+1;
+curchar:=xord[buffer[loc]];
+if curchar>=97 then if curchar<=122 then curchar:=curchar-32 else begin
+if curchar=127 then begin begin if charsonline>0 then writeln(' ');
+write('Illegal character in the file');showerrorcontext;end;curchar:=63;
+end;end else if(curchar<=41)and(curchar>=40)then loc:=loc-1;end;{:38}
+{39:}function gethex:byte;var a:integer;begin repeat getnext;
+until curchar<>32;a:=curchar-41;if a>0 then begin a:=curchar-48;
+if curchar>57 then if curchar<65 then a:=-1 else a:=curchar-55;end;
+if(a<0)or(a>15)then begin begin if charsonline>0 then writeln(' ');
+write('Illegal hexadecimal digit');showerrorcontext;end;gethex:=0;
+end else gethex:=a;end;{:39}{40:}procedure skiptoendofitem;
+var l:integer;begin l:=level;
+while level>=l do begin while loc=limit do fillbuffer;loc:=loc+1;
+if buffer[loc]=')'then level:=level-1 else if buffer[loc]='('then level
+:=level+1;end;
+if inputhasended then begin if charsonline>0 then writeln(' ');
+write('File ended unexpectedly: No closing ")"');showerrorcontext;end;
+curchar:=32;end;{:40}{41:}procedure copytoendofitem;label 30;
+var l:integer;nonblankfound:boolean;begin l:=level;nonblankfound:=false;
+while true do begin while loc=limit do fillbuffer;
+if buffer[loc+1]=')'then if level=l then goto 30 else level:=level-1;
+loc:=loc+1;if buffer[loc]='('then level:=level+1;
+if buffer[loc]<>' 'then nonblankfound:=true;
+if nonblankfound then if xord[buffer[loc]]=127 then begin begin if
+charsonline>0 then writeln(' ');write('Illegal character in the file');
+showerrorcontext;end;begin vf[vfptr]:=63;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;end else begin vf[vfptr]:=xord[buffer[loc]];
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;end;30:end;{:41}{43:}
+procedure finishtheproperty;begin while curchar=32 do getnext;
+if curchar<>41 then begin if charsonline>0 then writeln(' ');
+write('Junk after property value will be ignored');showerrorcontext;end;
+skiptoendofitem;end;{:43}{50:}procedure lookup;var k:0..20;j:0..666;
+notfound:boolean;begin{51:}curhash:=curname[1];
+for k:=2 to namelength do curhash:=(curhash+curhash+curname[k])mod 141{:
+51};notfound:=true;
+while notfound do begin if curhash=0 then curhash:=140 else curhash:=
+curhash-1;
+if nhash[curhash]=0 then notfound:=false else begin j:=start[nhash[
+curhash]];
+if start[nhash[curhash]+1]=j+namelength then begin notfound:=false;
+for k:=1 to namelength do if dictionary[j+k-1]<>curname[k]then notfound
+:=true;end;end;end;nameptr:=nhash[curhash];end;{:50}{53:}
+procedure entername(v:byte);var k:0..20;
+begin for k:=1 to namelength do curname[k]:=curname[k+20-namelength];
+lookup;nhash[curhash]:=startptr;equiv[startptr]:=v;
+for k:=1 to namelength do begin dictionary[dictptr]:=curname[k];
+dictptr:=dictptr+1;end;startptr:=startptr+1;start[startptr]:=dictptr;
+end;{:53}{58:}procedure getname;begin loc:=loc+1;level:=level+1;
+curchar:=32;while curchar=32 do getnext;
+if(curchar>41)or(curchar<40)then loc:=loc-1;namelength:=0;
+getkeywordchar;
+while curchar<>32 do begin if namelength=20 then curname[1]:=88 else
+namelength:=namelength+1;curname[namelength]:=curchar;getkeywordchar;
+end;lookup;if nameptr=0 then begin if charsonline>0 then writeln(' ');
+write('Sorry, I don''t know that property name');showerrorcontext;end;
+curcode:=equiv[nameptr];end;{:58}{60:}function getbyte:byte;
+var acc:integer;t:ASCIIcode;begin repeat getnext;until curchar<>32;
+t:=curchar;acc:=0;repeat getnext;until curchar<>32;if t=67 then{61:}
+if(curchar>=33)and(curchar<=126)and((curchar<40)or(curchar>41))then acc
+:=xord[buffer[loc]]else begin begin if charsonline>0 then writeln(' ');
+write('"C" value must be standard ASCII and not a paren');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end{:61}else if t=68 then{62:}
+begin while(curchar>=48)and(curchar<=57)do begin acc:=acc*10+curchar-48;
+if acc>255 then begin begin begin if charsonline>0 then writeln(' ');
+write('This value shouldn''t exceed 255');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);end;acc:=0;curchar:=32;
+end else getnext;end;begin if(curchar>41)or(curchar<40)then loc:=loc-1;
+end;end{:62}else if t=79 then{63:}
+begin while(curchar>=48)and(curchar<=55)do begin acc:=acc*8+curchar-48;
+if acc>255 then begin begin begin if charsonline>0 then writeln(' ');
+write('This value shouldn''t exceed ''377');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);end;acc:=0;curchar:=32;
+end else getnext;end;begin if(curchar>41)or(curchar<40)then loc:=loc-1;
+end;end{:63}else if t=72 then{64:}
+begin while((curchar>=48)and(curchar<=57))or((curchar>=65)and(curchar<=
+70))do begin if curchar>=65 then curchar:=curchar-7;
+acc:=acc*16+curchar-48;
+if acc>255 then begin begin begin if charsonline>0 then writeln(' ');
+write('This value shouldn''t exceed "FF');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);end;acc:=0;curchar:=32;
+end else getnext;end;begin if(curchar>41)or(curchar<40)then loc:=loc-1;
+end;end{:64}else if t=70 then{65:}
+begin if curchar=66 then acc:=2 else if curchar=76 then acc:=4 else if
+curchar<>77 then acc:=18;getnext;
+if curchar=73 then acc:=acc+1 else if curchar<>82 then acc:=18;getnext;
+if curchar=67 then acc:=acc+6 else if curchar=69 then acc:=acc+12 else
+if curchar<>82 then acc:=18;
+if acc>=18 then begin begin begin if charsonline>0 then writeln(' ');
+write('Illegal face code, I changed it to MRR');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);end;acc:=0;end;end{:65}
+else begin begin if charsonline>0 then writeln(' ');
+write('You need "C" or "D" or "O" or "H" or "F" here');showerrorcontext;
+end;repeat getnext until(curchar=40)or(curchar=41);end;curchar:=32;
+getbyte:=acc;end;{:60}{69:}procedure getfourbytes;var c:integer;
+r:integer;begin repeat getnext;until curchar<>32;r:=0;
+curbytes:=zerobytes;
+if curchar=72 then r:=16 else if curchar=79 then r:=8 else if curchar=68
+then r:=10 else begin begin if charsonline>0 then writeln(' ');
+write('Decimal ("D"), octal ("O"), or hex ("H") value needed here');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);end;
+if r>0 then begin repeat getnext;until curchar<>32;
+while((curchar>=48)and(curchar<=57))or((curchar>=65)and(curchar<=70))do{
+70:}begin if curchar>=65 then curchar:=curchar-7;
+if curchar>=48+r then begin begin if charsonline>0 then writeln(' ');
+write('Illegal digit');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);
+end else begin c:=curbytes.b3*r+curchar-48;curbytes.b3:=c mod 256;
+c:=curbytes.b2*r+c div 256;curbytes.b2:=c mod 256;
+c:=curbytes.b1*r+c div 256;curbytes.b1:=c mod 256;
+c:=curbytes.b0*r+c div 256;
+if c<256 then curbytes.b0:=c else begin curbytes:=zerobytes;
+if r=8 then begin begin if charsonline>0 then writeln(' ');
+write('Sorry, the maximum octal value is O 37777777777');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end else if r=10 then begin begin if charsonline>0 then writeln(' ');
+write('Sorry, the maximum decimal value is D 4294967295');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end else begin begin if charsonline>0 then writeln(' ');
+write('Sorry, the maximum hex value is H FFFFFFFF');showerrorcontext;
+end;repeat getnext until(curchar=40)or(curchar=41);end;end;getnext;end;
+end{:70};end;end;{:69}{72:}function getfix:fixword;var negative:boolean;
+acc:integer;intpart:integer;j:0..7;begin repeat getnext;
+until curchar<>32;negative:=false;acc:=0;
+if(curchar<>82)and(curchar<>68)then begin begin if charsonline>0 then
+writeln(' ');write('An "R" or "D" value is needed here');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end else begin{73:}repeat getnext;if curchar=45 then begin curchar:=32;
+negative:=true;end else if curchar=43 then curchar:=32;
+until curchar<>32{:73};while(curchar>=48)and(curchar<=57)do{74:}
+begin acc:=acc*10+curchar-48;
+if acc>=2048 then begin begin begin if charsonline>0 then writeln(' ');
+write('Real constants must be less than 2048');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);end;acc:=0;curchar:=32;
+end else getnext;end{:74};intpart:=acc;acc:=0;if curchar=46 then{76:}
+begin j:=0;getnext;
+while(curchar>=48)and(curchar<=57)do begin if j<7 then begin j:=j+1;
+fractiondigits[j]:=2097152*(curchar-48);end;getnext;end;acc:=0;
+while j>0 do begin acc:=fractiondigits[j]+(acc div 10);j:=j-1;end;
+acc:=(acc+10)div 20;end{:76};
+if(acc>=1048576)and(intpart=2047)then begin begin if charsonline>0 then
+writeln(' ');write('Real constants must be less than 2048');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end else acc:=intpart*1048576+acc;end;
+if negative then getfix:=-acc else getfix:=acc;end;{:72}{85:}
+function sortin(h:pointer;d:fixword):pointer;var p:pointer;
+begin if(d=0)and(h<>1)then sortin:=0 else begin p:=h;
+while d>=memory[link[p]]do p:=link[p];
+if(d=memory[p])and(p<>h)then sortin:=p else if memptr=1032 then begin
+begin if charsonline>0 then writeln(' ');
+write('Memory overflow: more than 1028 widths, etc');showerrorcontext;
+end;writeln('Congratulations! It''s hard to make this error.');
+sortin:=p;end else begin memptr:=memptr+1;memory[memptr]:=d;
+link[memptr]:=link[p];link[p]:=memptr;memory[h]:=memory[h]+1;
+sortin:=memptr;end;end;end;{:85}{87:}function mincover(h:pointer;
+d:fixword):integer;var p:pointer;l:fixword;m:integer;begin m:=0;
+p:=link[h];nextd:=memory[0];while p<>0 do begin m:=m+1;l:=memory[p];
+while memory[link[p]]<=l+d do p:=link[p];p:=link[p];
+if memory[p]-l<nextd then nextd:=memory[p]-l;end;mincover:=m;end;{:87}
+{88:}function shorten(h:pointer;m:integer):fixword;var d:fixword;
+k:integer;begin if memory[h]>m then begin excess:=memory[h]-m;
+k:=mincover(h,0);d:=nextd;repeat d:=d+d;k:=mincover(h,d);until k<=m;
+d:=d div 2;k:=mincover(h,d);while k>m do begin d:=nextd;
+k:=mincover(h,d);end;shorten:=d;end else shorten:=0;end;{:88}{90:}
+procedure setindices(h:pointer;d:fixword);var p:pointer;q:pointer;
+m:byte;l:fixword;begin q:=h;p:=link[q];m:=0;while p<>0 do begin m:=m+1;
+l:=memory[p];index[p]:=m;while memory[link[p]]<=l+d do begin p:=link[p];
+index[p]:=m;excess:=excess-1;if excess=0 then d:=0;end;link[q]:=p;
+memory[p]:=l+(memory[p]-l)div 2;q:=p;p:=link[p];end;memory[h]:=m;end;
+{:90}{93:}procedure junkerror;
+begin begin if charsonline>0 then writeln(' ');
+write('There''s junk here that is not in parentheses');showerrorcontext;
+end;repeat getnext until(curchar=40)or(curchar=41);end;{:93}{96:}
+procedure readfourbytes(l:headerindex);begin getfourbytes;
+headerbytes[l]:=curbytes.b0;headerbytes[l+1]:=curbytes.b1;
+headerbytes[l+2]:=curbytes.b2;headerbytes[l+3]:=curbytes.b3;end;{:96}
+{97:}procedure readBCPL(l:headerindex;n:byte);var k:headerindex;
+begin k:=l;while curchar=32 do getnext;
+while(curchar<>40)and(curchar<>41)do begin if k<l+n then k:=k+1;
+if k<l+n then headerbytes[k]:=curchar;getnext;end;
+if k=l+n then begin begin if charsonline>0 then writeln(' ');
+write('String is too long; its first ',n-1:1,' characters will be kept')
+;showerrorcontext;end;k:=k-1;end;headerbytes[l]:=k-l;
+while k<l+n-1 do begin k:=k+1;headerbytes[k]:=0;end;end;{:97}{111:}
+procedure checktag(c:byte);begin case chartag[c]of 0:;
+1:begin if charsonline>0 then writeln(' ');
+write('This character already appeared in a LIGTABLE LABEL');
+showerrorcontext;end;2:begin if charsonline>0 then writeln(' ');
+write('This character already has a NEXTLARGER spec');showerrorcontext;
+end;3:begin if charsonline>0 then writeln(' ');
+write('This character already has a VARCHAR spec');showerrorcontext;end;
+end;end;{:111}{124:}{128:}procedure vffix(opcode:byte;x:fixword);
+var negative:boolean;k:0..4;t:integer;begin frozendu:=true;
+if designunits<>1048576 then x:=round((x/designunits)*1048576.0);
+if x>=0 then negative:=false else begin negative:=true;x:=-1-x;end;
+if opcode=0 then begin k:=4;t:=16777216;end else begin t:=127;k:=1;
+while x>t do begin t:=256*t+255;k:=k+1;end;begin vf[vfptr]:=opcode+k-1;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;t:=t div 128+1;end;
+repeat if negative then begin begin vf[vfptr]:=255-(x div t);
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;negative:=false;x:=(x div t)*t+t-1-x;
+end else begin vf[vfptr]:=(x div t)mod 256;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;k:=k-1;t:=t div 256;until k=0;end;{:128}
+procedure readpacket(c:byte);var cc:byte;x:fixword;h,v:0..2;
+specialstart:0..vfsize;k:0..vfsize;begin packetstart[c]:=vfptr;
+stackptr:=0;h:=0;v:=0;curfont:=0;
+while level=2 do begin while curchar=32 do getnext;
+if curchar=40 then{125:}begin getname;
+if curcode=0 then skiptoendofitem else if(curcode<80)or(curcode>90)then
+begin begin if charsonline>0 then writeln(' ');
+write('This property name doesn''t belong in a MAP list');
+showerrorcontext;end;skiptoendofitem;
+end else begin case curcode of 80:{126:}begin getfourbytes;
+fontnumber[fontptr]:=curbytes;curfont:=0;
+while(fontnumber[curfont].b3<>fontnumber[fontptr].b3)or(fontnumber[
+curfont].b2<>fontnumber[fontptr].b2)or(fontnumber[curfont].b1<>
+fontnumber[fontptr].b1)or(fontnumber[curfont].b0<>fontnumber[fontptr].b0
+)do curfont:=curfont+1;
+if curfont=fontptr then begin if charsonline>0 then writeln(' ');
+write('Undefined MAPFONT cannot be selected');showerrorcontext;
+end else if curfont<64 then begin vf[vfptr]:=171+curfont;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end else begin begin vf[vfptr]:=235;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;begin vf[vfptr]:=curfont;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;end;end{:126};81:{127:}
+if curfont=fontptr then begin if charsonline>0 then writeln(' ');
+write('Character cannot be typeset in undefined font');showerrorcontext;
+end else begin cc:=getbyte;if cc>=128 then begin vf[vfptr]:=128;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;begin vf[vfptr]:=cc;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;end{:127};82:{129:}
+begin begin vf[vfptr]:=132;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;vffix(0,getfix);vffix(0,getfix);end{:129};
+83,84:{130:}begin if curcode=83 then x:=getfix else x:=-getfix;
+if h=0 then begin wstack[stackptr]:=x;h:=1;vffix(148,x);
+end else if x=wstack[stackptr]then begin vf[vfptr]:=147;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end else if h=1 then begin xstack[stackptr]:=x;
+h:=2;vffix(153,x);
+end else if x=xstack[stackptr]then begin vf[vfptr]:=152;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end else vffix(143,x);end{:130};85,86:{131:}
+begin if curcode=85 then x:=getfix else x:=-getfix;
+if v=0 then begin ystack[stackptr]:=x;v:=1;vffix(162,x);
+end else if x=ystack[stackptr]then begin vf[vfptr]:=161;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end else if v=1 then begin zstack[stackptr]:=x;
+v:=2;vffix(167,x);
+end else if x=zstack[stackptr]then begin vf[vfptr]:=166;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end else vffix(157,x);end{:131};87:{132:}
+if stackptr=maxstack then begin if charsonline>0 then writeln(' ');
+write('Don''t push so much---stack is full!');showerrorcontext;
+end else begin begin vf[vfptr]:=141;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;hstack[stackptr]:=h;vstack[stackptr]:=v;
+stackptr:=stackptr+1;h:=0;v:=0;end{:132};88:{133:}
+if stackptr=0 then begin if charsonline>0 then writeln(' ');
+write('Empty stack cannot be popped');showerrorcontext;
+end else begin begin vf[vfptr]:=142;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;stackptr:=stackptr-1;h:=hstack[stackptr];
+v:=vstack[stackptr];end{:133};89,90:{134:}begin begin vf[vfptr]:=239;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;begin vf[vfptr]:=0;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;specialstart:=vfptr;
+if curcode=89 then copytoendofitem else begin repeat x:=gethex;
+if curchar>41 then begin vf[vfptr]:=x*16+gethex;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;until curchar<=41;end;
+if vfptr-specialstart>255 then{135:}
+if vfptr+3>vfsize then begin begin if charsonline>0 then writeln(' ');
+write('Special command being clipped---no room left!');showerrorcontext;
+end;vfptr:=specialstart+255;vf[specialstart-1]:=255;
+end else begin for k:=vfptr downto specialstart do vf[k+3]:=vf[k];
+x:=vfptr-specialstart;vfptr:=vfptr+3;vf[specialstart-2]:=242;
+vf[specialstart-1]:=x div 16777216;
+vf[specialstart]:=(x div 65536)mod 256;
+vf[specialstart+1]:=(x div 256)mod 256;vf[specialstart+2]:=x mod 256;
+end{:135}else vf[specialstart-1]:=vfptr-specialstart;end{:134};end;
+finishtheproperty;end;end{:125}
+else if curchar=41 then skiptoendofitem else junkerror;end;
+while stackptr>0 do begin begin if charsonline>0 then writeln(' ');
+write('Missing POP supplied');showerrorcontext;end;begin vf[vfptr]:=142;
+if vfptr=vfsize then begin if charsonline>0 then writeln(' ');
+write('I''m out of memory---increase my vfsize!');showerrorcontext;
+end else vfptr:=vfptr+1;end;stackptr:=stackptr-1;end;
+packetlength[c]:=vfptr-packetstart[c];begin loc:=loc-1;level:=level+1;
+curchar:=41;end;end;{:124}{136:}procedure printoctal(c:byte);
+begin write('''',(c div 64):1,((c div 8)mod 8):1,(c mod 8):1);end;{:136}
+{150:}function hashinput(p,c:indx):boolean;label 30;var cc:0..3;
+zz:0..255;y:0..255;key:integer;t:integer;
+begin if hashptr=hashsize then begin hashinput:=false;goto 30;end;{151:}
+y:=ligkern[p].b1;t:=ligkern[p].b2;cc:=0;zz:=ligkern[p].b3;
+if t>=128 then zz:=y else begin case t of 0,6:;5,11:zz:=y;1,7:cc:=1;
+2:cc:=2;3:cc:=3;end;end{:151};key:=256*c+y+1;h:=(1009*key)mod hashsize;
+while hash[h]>0 do begin if hash[h]<=key then begin if hash[h]=key then
+begin hashinput:=false;goto 30;end;t:=hash[h];hash[h]:=key;key:=t;
+t:=class[h];class[h]:=cc;cc:=t;t:=ligz[h];ligz[h]:=zz;zz:=t;end;
+if h>0 then h:=h-1 else h:=hashsize;end;hash[h]:=key;class[h]:=cc;
+ligz[h]:=zz;hashptr:=hashptr+1;hashlist[hashptr]:=h;hashinput:=true;
+30:end;{:150}{152:}function f(h,x,y:indx):indx;forward;
+function eval(x,y:indx):indx;var key:integer;begin key:=256*x+y+1;
+h:=(1009*key)mod hashsize;
+while hash[h]>key do if h>0 then h:=h-1 else h:=hashsize;
+if hash[h]<key then eval:=y else eval:=f(h,x,y);end;{:152}{153:}
+function f;begin case class[h]of 0:;1:begin class[h]:=4;
+ligz[h]:=eval(ligz[h],y);class[h]:=0;end;2:begin class[h]:=4;
+ligz[h]:=eval(x,ligz[h]);class[h]:=0;end;3:begin class[h]:=4;
+ligz[h]:=eval(eval(x,ligz[h]),y);class[h]:=0;end;4:begin xligcycle:=x;
+yligcycle:=y;ligz[h]:=257;class[h]:=0;end;end;f:=ligz[h];end;{:153}
+{165:}procedure outscaled(x:fixword);var n:byte;m:0..65535;
+begin if abs(x/designunits)>=16.0 then begin writeln(
+'The relative dimension ',x/1048576:1:3,' is too large.');
+write(' (Must be less than 16*designsize');
+if designunits<>1048576 then write(' =',designunits/65536:1:3,
+' designunits');writeln(')');x:=0;end;
+if designunits<>1048576 then x:=round((x/designunits)*1048576.0);
+if x<0 then begin write(tfmfile,255);x:=x+16777216;if x<=0 then x:=1;
+end else begin write(tfmfile,0);if x>=16777216 then x:=16777215;end;
+n:=x div 65536;m:=x mod 65536;write(tfmfile,n);write(tfmfile,m div 256);
+write(tfmfile,m mod 256);end;{:165}{176:}procedure voutint(x:integer);
+begin if x>=0 then write(vffile,x div 16777216)else begin write(vffile,
+255);x:=x+16777216;end;write(vffile,(x div 65536)mod 256);
+write(vffile,(x div 256)mod 256);write(vffile,x mod 256);end;{:176}
+{180:}procedure paramenter;begin{57:}namelength:=5;curname[16]:=83;
+curname[17]:=76;curname[18]:=65;curname[19]:=78;curname[20]:=84;
+entername(31);namelength:=5;curname[16]:=83;curname[17]:=80;
+curname[18]:=65;curname[19]:=67;curname[20]:=69;entername(32);
+namelength:=7;curname[14]:=83;curname[15]:=84;curname[16]:=82;
+curname[17]:=69;curname[18]:=84;curname[19]:=67;curname[20]:=72;
+entername(33);namelength:=6;curname[15]:=83;curname[16]:=72;
+curname[17]:=82;curname[18]:=73;curname[19]:=78;curname[20]:=75;
+entername(34);namelength:=7;curname[14]:=88;curname[15]:=72;
+curname[16]:=69;curname[17]:=73;curname[18]:=71;curname[19]:=72;
+curname[20]:=84;entername(35);namelength:=4;curname[17]:=81;
+curname[18]:=85;curname[19]:=65;curname[20]:=68;entername(36);
+namelength:=10;curname[11]:=69;curname[12]:=88;curname[13]:=84;
+curname[14]:=82;curname[15]:=65;curname[16]:=83;curname[17]:=80;
+curname[18]:=65;curname[19]:=67;curname[20]:=69;entername(37);
+namelength:=4;curname[17]:=78;curname[18]:=85;curname[19]:=77;
+curname[20]:=49;entername(38);namelength:=4;curname[17]:=78;
+curname[18]:=85;curname[19]:=77;curname[20]:=50;entername(39);
+namelength:=4;curname[17]:=78;curname[18]:=85;curname[19]:=77;
+curname[20]:=51;entername(40);namelength:=6;curname[15]:=68;
+curname[16]:=69;curname[17]:=78;curname[18]:=79;curname[19]:=77;
+curname[20]:=49;entername(41);namelength:=6;curname[15]:=68;
+curname[16]:=69;curname[17]:=78;curname[18]:=79;curname[19]:=77;
+curname[20]:=50;entername(42);namelength:=4;curname[17]:=83;
+curname[18]:=85;curname[19]:=80;curname[20]:=49;entername(43);
+namelength:=4;curname[17]:=83;curname[18]:=85;curname[19]:=80;
+curname[20]:=50;entername(44);namelength:=4;curname[17]:=83;
+curname[18]:=85;curname[19]:=80;curname[20]:=51;entername(45);
+namelength:=4;curname[17]:=83;curname[18]:=85;curname[19]:=66;
+curname[20]:=49;entername(46);namelength:=4;curname[17]:=83;
+curname[18]:=85;curname[19]:=66;curname[20]:=50;entername(47);
+namelength:=7;curname[14]:=83;curname[15]:=85;curname[16]:=80;
+curname[17]:=68;curname[18]:=82;curname[19]:=79;curname[20]:=80;
+entername(48);namelength:=7;curname[14]:=83;curname[15]:=85;
+curname[16]:=66;curname[17]:=68;curname[18]:=82;curname[19]:=79;
+curname[20]:=80;entername(49);namelength:=6;curname[15]:=68;
+curname[16]:=69;curname[17]:=76;curname[18]:=73;curname[19]:=77;
+curname[20]:=49;entername(50);namelength:=6;curname[15]:=68;
+curname[16]:=69;curname[17]:=76;curname[18]:=73;curname[19]:=77;
+curname[20]:=50;entername(51);namelength:=10;curname[11]:=65;
+curname[12]:=88;curname[13]:=73;curname[14]:=83;curname[15]:=72;
+curname[16]:=69;curname[17]:=73;curname[18]:=71;curname[19]:=72;
+curname[20]:=84;entername(52);namelength:=20;curname[1]:=68;
+curname[2]:=69;curname[3]:=70;curname[4]:=65;curname[5]:=85;
+curname[6]:=76;curname[7]:=84;curname[8]:=82;curname[9]:=85;
+curname[10]:=76;curname[11]:=69;curname[12]:=84;curname[13]:=72;
+curname[14]:=73;curname[15]:=67;curname[16]:=75;curname[17]:=78;
+curname[18]:=69;curname[19]:=83;curname[20]:=83;entername(38);
+namelength:=13;curname[8]:=66;curname[9]:=73;curname[10]:=71;
+curname[11]:=79;curname[12]:=80;curname[13]:=83;curname[14]:=80;
+curname[15]:=65;curname[16]:=67;curname[17]:=73;curname[18]:=78;
+curname[19]:=71;curname[20]:=49;entername(39);namelength:=13;
+curname[8]:=66;curname[9]:=73;curname[10]:=71;curname[11]:=79;
+curname[12]:=80;curname[13]:=83;curname[14]:=80;curname[15]:=65;
+curname[16]:=67;curname[17]:=73;curname[18]:=78;curname[19]:=71;
+curname[20]:=50;entername(40);namelength:=13;curname[8]:=66;
+curname[9]:=73;curname[10]:=71;curname[11]:=79;curname[12]:=80;
+curname[13]:=83;curname[14]:=80;curname[15]:=65;curname[16]:=67;
+curname[17]:=73;curname[18]:=78;curname[19]:=71;curname[20]:=51;
+entername(41);namelength:=13;curname[8]:=66;curname[9]:=73;
+curname[10]:=71;curname[11]:=79;curname[12]:=80;curname[13]:=83;
+curname[14]:=80;curname[15]:=65;curname[16]:=67;curname[17]:=73;
+curname[18]:=78;curname[19]:=71;curname[20]:=52;entername(42);
+namelength:=13;curname[8]:=66;curname[9]:=73;curname[10]:=71;
+curname[11]:=79;curname[12]:=80;curname[13]:=83;curname[14]:=80;
+curname[15]:=65;curname[16]:=67;curname[17]:=73;curname[18]:=78;
+curname[19]:=71;curname[20]:=53;entername(43);{:57};end;
+procedure vplenter;begin{56:}namelength:=6;curname[15]:=86;
+curname[16]:=84;curname[17]:=73;curname[18]:=84;curname[19]:=76;
+curname[20]:=69;entername(12);namelength:=7;curname[14]:=77;
+curname[15]:=65;curname[16]:=80;curname[17]:=70;curname[18]:=79;
+curname[19]:=78;curname[20]:=84;entername(13);namelength:=3;
+curname[18]:=77;curname[19]:=65;curname[20]:=80;entername(66);
+namelength:=8;curname[13]:=70;curname[14]:=79;curname[15]:=78;
+curname[16]:=84;curname[17]:=78;curname[18]:=65;curname[19]:=77;
+curname[20]:=69;entername(20);namelength:=8;curname[13]:=70;
+curname[14]:=79;curname[15]:=78;curname[16]:=84;curname[17]:=65;
+curname[18]:=82;curname[19]:=69;curname[20]:=65;entername(21);
+namelength:=12;curname[9]:=70;curname[10]:=79;curname[11]:=78;
+curname[12]:=84;curname[13]:=67;curname[14]:=72;curname[15]:=69;
+curname[16]:=67;curname[17]:=75;curname[18]:=83;curname[19]:=85;
+curname[20]:=77;entername(22);namelength:=6;curname[15]:=70;
+curname[16]:=79;curname[17]:=78;curname[18]:=84;curname[19]:=65;
+curname[20]:=84;entername(23);namelength:=9;curname[12]:=70;
+curname[13]:=79;curname[14]:=78;curname[15]:=84;curname[16]:=68;
+curname[17]:=83;curname[18]:=73;curname[19]:=90;curname[20]:=69;
+entername(24);namelength:=10;curname[11]:=83;curname[12]:=69;
+curname[13]:=76;curname[14]:=69;curname[15]:=67;curname[16]:=84;
+curname[17]:=70;curname[18]:=79;curname[19]:=78;curname[20]:=84;
+entername(80);namelength:=7;curname[14]:=83;curname[15]:=69;
+curname[16]:=84;curname[17]:=67;curname[18]:=72;curname[19]:=65;
+curname[20]:=82;entername(81);namelength:=7;curname[14]:=83;
+curname[15]:=69;curname[16]:=84;curname[17]:=82;curname[18]:=85;
+curname[19]:=76;curname[20]:=69;entername(82);namelength:=9;
+curname[12]:=77;curname[13]:=79;curname[14]:=86;curname[15]:=69;
+curname[16]:=82;curname[17]:=73;curname[18]:=71;curname[19]:=72;
+curname[20]:=84;entername(83);namelength:=8;curname[13]:=77;
+curname[14]:=79;curname[15]:=86;curname[16]:=69;curname[17]:=76;
+curname[18]:=69;curname[19]:=70;curname[20]:=84;entername(84);
+namelength:=8;curname[13]:=77;curname[14]:=79;curname[15]:=86;
+curname[16]:=69;curname[17]:=68;curname[18]:=79;curname[19]:=87;
+curname[20]:=78;entername(85);namelength:=6;curname[15]:=77;
+curname[16]:=79;curname[17]:=86;curname[18]:=69;curname[19]:=85;
+curname[20]:=80;entername(86);namelength:=4;curname[17]:=80;
+curname[18]:=85;curname[19]:=83;curname[20]:=72;entername(87);
+namelength:=3;curname[18]:=80;curname[19]:=79;curname[20]:=80;
+entername(88);namelength:=7;curname[14]:=83;curname[15]:=80;
+curname[16]:=69;curname[17]:=67;curname[18]:=73;curname[19]:=65;
+curname[20]:=76;entername(89);namelength:=10;curname[11]:=83;
+curname[12]:=80;curname[13]:=69;curname[14]:=67;curname[15]:=73;
+curname[16]:=65;curname[17]:=76;curname[18]:=72;curname[19]:=69;
+curname[20]:=88;entername(90);{:56};end;procedure nameenter;begin{55:}
+equiv[0]:=0;namelength:=8;curname[13]:=67;curname[14]:=72;
+curname[15]:=69;curname[16]:=67;curname[17]:=75;curname[18]:=83;
+curname[19]:=85;curname[20]:=77;entername(1);namelength:=10;
+curname[11]:=68;curname[12]:=69;curname[13]:=83;curname[14]:=73;
+curname[15]:=71;curname[16]:=78;curname[17]:=83;curname[18]:=73;
+curname[19]:=90;curname[20]:=69;entername(2);namelength:=11;
+curname[10]:=68;curname[11]:=69;curname[12]:=83;curname[13]:=73;
+curname[14]:=71;curname[15]:=78;curname[16]:=85;curname[17]:=78;
+curname[18]:=73;curname[19]:=84;curname[20]:=83;entername(3);
+namelength:=12;curname[9]:=67;curname[10]:=79;curname[11]:=68;
+curname[12]:=73;curname[13]:=78;curname[14]:=71;curname[15]:=83;
+curname[16]:=67;curname[17]:=72;curname[18]:=69;curname[19]:=77;
+curname[20]:=69;entername(4);namelength:=6;curname[15]:=70;
+curname[16]:=65;curname[17]:=77;curname[18]:=73;curname[19]:=76;
+curname[20]:=89;entername(5);namelength:=4;curname[17]:=70;
+curname[18]:=65;curname[19]:=67;curname[20]:=69;entername(6);
+namelength:=16;curname[5]:=83;curname[6]:=69;curname[7]:=86;
+curname[8]:=69;curname[9]:=78;curname[10]:=66;curname[11]:=73;
+curname[12]:=84;curname[13]:=83;curname[14]:=65;curname[15]:=70;
+curname[16]:=69;curname[17]:=70;curname[18]:=76;curname[19]:=65;
+curname[20]:=71;entername(7);namelength:=6;curname[15]:=72;
+curname[16]:=69;curname[17]:=65;curname[18]:=68;curname[19]:=69;
+curname[20]:=82;entername(8);namelength:=9;curname[12]:=70;
+curname[13]:=79;curname[14]:=78;curname[15]:=84;curname[16]:=68;
+curname[17]:=73;curname[18]:=77;curname[19]:=69;curname[20]:=78;
+entername(9);namelength:=8;curname[13]:=76;curname[14]:=73;
+curname[15]:=71;curname[16]:=84;curname[17]:=65;curname[18]:=66;
+curname[19]:=76;curname[20]:=69;entername(10);namelength:=12;
+curname[9]:=66;curname[10]:=79;curname[11]:=85;curname[12]:=78;
+curname[13]:=68;curname[14]:=65;curname[15]:=82;curname[16]:=89;
+curname[17]:=67;curname[18]:=72;curname[19]:=65;curname[20]:=82;
+entername(11);namelength:=9;curname[12]:=67;curname[13]:=72;
+curname[14]:=65;curname[15]:=82;curname[16]:=65;curname[17]:=67;
+curname[18]:=84;curname[19]:=69;curname[20]:=82;entername(14);
+namelength:=9;curname[12]:=80;curname[13]:=65;curname[14]:=82;
+curname[15]:=65;curname[16]:=77;curname[17]:=69;curname[18]:=84;
+curname[19]:=69;curname[20]:=82;entername(30);namelength:=6;
+curname[15]:=67;curname[16]:=72;curname[17]:=65;curname[18]:=82;
+curname[19]:=87;curname[20]:=68;entername(61);namelength:=6;
+curname[15]:=67;curname[16]:=72;curname[17]:=65;curname[18]:=82;
+curname[19]:=72;curname[20]:=84;entername(62);namelength:=6;
+curname[15]:=67;curname[16]:=72;curname[17]:=65;curname[18]:=82;
+curname[19]:=68;curname[20]:=80;entername(63);namelength:=6;
+curname[15]:=67;curname[16]:=72;curname[17]:=65;curname[18]:=82;
+curname[19]:=73;curname[20]:=67;entername(64);namelength:=10;
+curname[11]:=78;curname[12]:=69;curname[13]:=88;curname[14]:=84;
+curname[15]:=76;curname[16]:=65;curname[17]:=82;curname[18]:=71;
+curname[19]:=69;curname[20]:=82;entername(65);namelength:=7;
+curname[14]:=86;curname[15]:=65;curname[16]:=82;curname[17]:=67;
+curname[18]:=72;curname[19]:=65;curname[20]:=82;entername(67);
+namelength:=3;curname[18]:=84;curname[19]:=79;curname[20]:=80;
+entername(68);namelength:=3;curname[18]:=77;curname[19]:=73;
+curname[20]:=68;entername(69);namelength:=3;curname[18]:=66;
+curname[19]:=79;curname[20]:=84;entername(70);namelength:=3;
+curname[18]:=82;curname[19]:=69;curname[20]:=80;entername(71);
+namelength:=3;curname[18]:=69;curname[19]:=88;curname[20]:=84;
+entername(71);namelength:=7;curname[14]:=67;curname[15]:=79;
+curname[16]:=77;curname[17]:=77;curname[18]:=69;curname[19]:=78;
+curname[20]:=84;entername(0);namelength:=5;curname[16]:=76;
+curname[17]:=65;curname[18]:=66;curname[19]:=69;curname[20]:=76;
+entername(100);namelength:=4;curname[17]:=83;curname[18]:=84;
+curname[19]:=79;curname[20]:=80;entername(101);namelength:=4;
+curname[17]:=83;curname[18]:=75;curname[19]:=73;curname[20]:=80;
+entername(102);namelength:=3;curname[18]:=75;curname[19]:=82;
+curname[20]:=78;entername(103);namelength:=3;curname[18]:=76;
+curname[19]:=73;curname[20]:=71;entername(104);namelength:=4;
+curname[17]:=47;curname[18]:=76;curname[19]:=73;curname[20]:=71;
+entername(106);namelength:=5;curname[16]:=47;curname[17]:=76;
+curname[18]:=73;curname[19]:=71;curname[20]:=62;entername(110);
+namelength:=4;curname[17]:=76;curname[18]:=73;curname[19]:=71;
+curname[20]:=47;entername(105);namelength:=5;curname[16]:=76;
+curname[17]:=73;curname[18]:=71;curname[19]:=47;curname[20]:=62;
+entername(109);namelength:=5;curname[16]:=47;curname[17]:=76;
+curname[18]:=73;curname[19]:=71;curname[20]:=47;entername(107);
+namelength:=6;curname[15]:=47;curname[16]:=76;curname[17]:=73;
+curname[18]:=71;curname[19]:=47;curname[20]:=62;entername(111);
+namelength:=7;curname[14]:=47;curname[15]:=76;curname[16]:=73;
+curname[17]:=71;curname[18]:=47;curname[19]:=62;curname[20]:=62;
+entername(115);{:55};vplenter;paramenter;end;procedure readligkern;
+var krnptr:0..maxkerns;c:byte;begin{109:}begin lkstepended:=false;
+while level=1 do begin while curchar=32 do getnext;
+if curchar=40 then{110:}begin getname;
+if curcode=0 then skiptoendofitem else if curcode<100 then begin begin
+if charsonline>0 then writeln(' ');
+write('This property name doesn''t belong in a LIGTABLE list');
+showerrorcontext;end;skiptoendofitem;
+end else begin case curcode of 100:{112:}
+begin while curchar=32 do getnext;
+if curchar=66 then begin charremainder[256]:=nl;
+repeat getnext until(curchar=40)or(curchar=41);
+end else begin begin if(curchar>41)or(curchar<40)then loc:=loc-1;end;
+c:=getbyte;checktag(c);chartag[c]:=1;charremainder[c]:=nl;end;
+if minnl<=nl then minnl:=nl+1;lkstepended:=false;end{:112};101:{114:}
+if not lkstepended then begin if charsonline>0 then writeln(' ');
+write('STOP must follow LIG or KRN');showerrorcontext;
+end else begin ligkern[nl-1].b0:=128;lkstepended:=false;end{:114};
+102:{115:}
+if not lkstepended then begin if charsonline>0 then writeln(' ');
+write('SKIP must follow LIG or KRN');showerrorcontext;
+end else begin c:=getbyte;
+if c>=128 then begin if charsonline>0 then writeln(' ');
+write('Maximum SKIP amount is 127');showerrorcontext;
+end else if nl+c>=maxligsteps then begin if charsonline>0 then writeln(
+' ');write('Sorry, LIGTABLE too long for me to handle');
+showerrorcontext;end else begin ligkern[nl-1].b0:=c;
+if minnl<=nl+c then minnl:=nl+c+1;end;lkstepended:=false;end{:115};
+103:{117:}begin ligkern[nl].b0:=0;ligkern[nl].b1:=getbyte;
+kern[nk]:=getfix;krnptr:=0;
+while kern[krnptr]<>kern[nk]do krnptr:=krnptr+1;
+if krnptr=nk then begin if nk<maxkerns then nk:=nk+1 else begin begin if
+charsonline>0 then writeln(' ');
+write('Sorry, too many different kerns for me to handle');
+showerrorcontext;end;krnptr:=krnptr-1;end;end;
+ligkern[nl].b2:=128+(krnptr div 256);ligkern[nl].b3:=krnptr mod 256;
+if nl>=maxligsteps-1 then begin if charsonline>0 then writeln(' ');
+write('Sorry, LIGTABLE too long for me to handle');showerrorcontext;
+end else nl:=nl+1;lkstepended:=true;end{:117};
+104,105,106,107,109,110,111,115:{116:}begin ligkern[nl].b0:=0;
+ligkern[nl].b2:=curcode-104;ligkern[nl].b1:=getbyte;
+ligkern[nl].b3:=getbyte;
+if nl>=maxligsteps-1 then begin if charsonline>0 then writeln(' ');
+write('Sorry, LIGTABLE too long for me to handle');showerrorcontext;
+end else nl:=nl+1;lkstepended:=true;end{:116};end;finishtheproperty;end;
+end{:110}else if curchar=41 then skiptoendofitem else junkerror;end;
+begin loc:=loc-1;level:=level+1;curchar:=41;end;end{:109};end;
+procedure readcharinfo;var c:byte;begin{118:}begin c:=getbyte;{137:}
+begin if charsonline=8 then begin writeln(' ');charsonline:=1;
+end else begin if charsonline>0 then write(' ');
+charsonline:=charsonline+1;end;printoctal(c);end{:137};
+while level=1 do begin while curchar=32 do getnext;
+if curchar=40 then{119:}begin getname;
+if curcode=0 then skiptoendofitem else if(curcode<61)or(curcode>67)then
+begin begin if charsonline>0 then writeln(' ');
+write('This property name doesn''t belong in a CHARACTER list');
+showerrorcontext;end;skiptoendofitem;
+end else begin case curcode of 61:charwd[c]:=sortin(1,getfix);
+62:charht[c]:=sortin(2,getfix);63:chardp[c]:=sortin(3,getfix);
+64:charic[c]:=sortin(4,getfix);65:begin checktag(c);chartag[c]:=2;
+charremainder[c]:=getbyte;end;66:readpacket(c);67:{120:}
+begin if ne=256 then begin if charsonline>0 then writeln(' ');
+write('At most 256 VARCHAR specs are allowed');showerrorcontext;
+end else begin checktag(c);chartag[c]:=3;charremainder[c]:=ne;
+exten[ne]:=zerobytes;while level=2 do begin while curchar=32 do getnext;
+if curchar=40 then{121:}begin getname;
+if curcode=0 then skiptoendofitem else if(curcode<68)or(curcode>71)then
+begin begin if charsonline>0 then writeln(' ');
+write('This property name doesn''t belong in a VARCHAR list');
+showerrorcontext;end;skiptoendofitem;
+end else begin case curcode-(68)of 0:exten[ne].b0:=getbyte;
+1:exten[ne].b1:=getbyte;2:exten[ne].b2:=getbyte;3:exten[ne].b3:=getbyte;
+end;finishtheproperty;end;end{:121}
+else if curchar=41 then skiptoendofitem else junkerror;end;ne:=ne+1;
+begin loc:=loc-1;level:=level+1;curchar:=41;end;end;end{:120};end;
+finishtheproperty;end;end{:119}
+else if curchar=41 then skiptoendofitem else junkerror;end;
+if charwd[c]=0 then charwd[c]:=sortin(1,0);begin loc:=loc-1;
+level:=level+1;curchar:=41;end;end{:118};end;procedure readinput;
+var c:byte;begin{92:}curchar:=32;repeat while curchar=32 do getnext;
+if curchar=40 then{94:}begin getname;
+if curcode=0 then skiptoendofitem else if curcode>14 then begin begin if
+charsonline>0 then writeln(' ');
+write('This property name doesn''t belong on the outer level');
+showerrorcontext;end;skiptoendofitem;end else begin{95:}
+case curcode of 1:begin checksumspecified:=true;readfourbytes(0);end;
+2:{98:}begin nextd:=getfix;
+if nextd<1048576 then begin if charsonline>0 then writeln(' ');
+write('The design size must be at least 1');showerrorcontext;
+end else designsize:=nextd;end{:98};3:{99:}begin nextd:=getfix;
+if nextd<=0 then begin if charsonline>0 then writeln(' ');
+write('The number of units per design size must be positive');
+showerrorcontext;
+end else if frozendu then begin if charsonline>0 then writeln(' ');
+write('Sorry, it''s too late to change the design units');
+showerrorcontext;end else designunits:=nextd;end{:99};4:readBCPL(8,40);
+5:readBCPL(48,20);6:headerbytes[71]:=getbyte;7:{100:}
+begin while curchar=32 do getnext;
+if curchar=84 then sevenbitsafeflag:=true else if curchar=70 then
+sevenbitsafeflag:=false else begin if charsonline>0 then writeln(' ');
+write('The flag value should be "TRUE" or "FALSE"');showerrorcontext;
+end;repeat getnext until(curchar=40)or(curchar=41);end{:100};8:{101:}
+begin c:=getbyte;
+if c<18 then begin begin if charsonline>0 then writeln(' ');
+write('HEADER indices should be 18 or more');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);
+end else if 4*c+4>maxheaderbytes then begin begin if charsonline>0 then
+writeln(' ');
+write('This HEADER index is too big for my present table size');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end else begin while headerptr<4*c+4 do begin headerbytes[headerptr]:=0;
+headerptr:=headerptr+1;end;readfourbytes(4*c);end;end{:101};9:{102:}
+begin while level=1 do begin while curchar=32 do getnext;
+if curchar=40 then{103:}begin getname;
+if curcode=0 then skiptoendofitem else if(curcode<30)or(curcode>=61)then
+begin begin if charsonline>0 then writeln(' ');
+write('This property name doesn''t belong in a FONTDIMEN list');
+showerrorcontext;end;skiptoendofitem;
+end else begin if curcode=30 then c:=getbyte else c:=curcode-30;
+if c=0 then begin begin if charsonline>0 then writeln(' ');
+write('PARAMETER index must not be zero');showerrorcontext;end;
+skiptoendofitem;
+end else if c>maxparamwords then begin begin if charsonline>0 then
+writeln(' ');
+write('This PARAMETER index is too big for my present table size');
+showerrorcontext;end;skiptoendofitem;
+end else begin while np<c do begin np:=np+1;param[np]:=0;end;
+param[c]:=getfix;finishtheproperty;end;end;end{:103}
+else if curchar=41 then skiptoendofitem else junkerror;end;
+begin loc:=loc-1;level:=level+1;curchar:=41;end;end{:102};
+10:readligkern;11:bchar:=getbyte;12:begin vtitlestart:=vfptr;
+copytoendofitem;
+if vfptr>vtitlestart+255 then begin begin if charsonline>0 then writeln(
+' ');write('VTITLE clipped to 255 characters');showerrorcontext;end;
+vtitlelength:=255;end else vtitlelength:=vfptr-vtitlestart;end;13:{104:}
+begin getfourbytes;fontnumber[fontptr]:=curbytes;curfont:=0;
+while(fontnumber[curfont].b3<>fontnumber[fontptr].b3)or(fontnumber[
+curfont].b2<>fontnumber[fontptr].b2)or(fontnumber[curfont].b1<>
+fontnumber[fontptr].b1)or(fontnumber[curfont].b0<>fontnumber[fontptr].b0
+)do curfont:=curfont+1;if curfont=fontptr then if fontptr<256 then{105:}
+begin fontptr:=fontptr+1;fnamestart[curfont]:=vfsize;
+fnamelength[curfont]:=4;fareastart[curfont]:=vfsize;
+farealength[curfont]:=0;fontchecksum[curfont]:=zerobytes;
+fontat[curfont]:=1048576;fontdsize[curfont]:=10485760;end{:105}
+else begin if charsonline>0 then writeln(' ');
+write('I can handle only 256 different mapfonts');showerrorcontext;end;
+if curfont=fontptr then skiptoendofitem else while level=1 do begin
+while curchar=32 do getnext;if curchar=40 then{106:}begin getname;
+if curcode=0 then skiptoendofitem else if(curcode<20)or(curcode>24)then
+begin begin if charsonline>0 then writeln(' ');
+write('This property name doesn''t belong in a MAPFONT list');
+showerrorcontext;end;skiptoendofitem;
+end else begin case curcode of 20:{107:}
+begin fnamestart[curfont]:=vfptr;copytoendofitem;
+if vfptr>fnamestart[curfont]+255 then begin begin if charsonline>0 then
+writeln(' ');write('FONTNAME clipped to 255 characters');
+showerrorcontext;end;fnamelength[curfont]:=255;
+end else fnamelength[curfont]:=vfptr-fnamestart[curfont];end{:107};
+21:{108:}begin fareastart[curfont]:=vfptr;copytoendofitem;
+if vfptr>fareastart[curfont]+255 then begin begin if charsonline>0 then
+writeln(' ');write('FONTAREA clipped to 255 characters');
+showerrorcontext;end;farealength[curfont]:=255;
+end else farealength[curfont]:=vfptr-fareastart[curfont];end{:108};
+22:begin getfourbytes;fontchecksum[curfont]:=curbytes;end;
+23:begin frozendu:=true;
+if designunits=1048576 then fontat[curfont]:=getfix else fontat[curfont]
+:=round((getfix/designunits)*1048576.0);end;
+24:fontdsize[curfont]:=getfix;end;finishtheproperty;end;end{:106}
+else if curchar=41 then skiptoendofitem else junkerror;end;
+begin loc:=loc-1;level:=level+1;curchar:=41;end;end{:104};
+14:readcharinfo;end{:95};finishtheproperty;end;end{:94}
+else if(curchar=41)and not inputhasended then begin begin if charsonline
+>0 then writeln(' ');write('Extra right parenthesis');showerrorcontext;
+end;loc:=loc+1;curchar:=32;end else if not inputhasended then junkerror;
+until inputhasended{:92};end;procedure corrandcheck;var c:0..256;
+hh:0..hashsize;ligptr:0..maxligsteps;g:byte;begin{139:}
+if nl>0 then{145:}
+begin if charremainder[256]<32767 then begin ligkern[nl].b0:=255;
+ligkern[nl].b1:=0;ligkern[nl].b2:=0;ligkern[nl].b3:=0;nl:=nl+1;end;
+while minnl>nl do begin ligkern[nl].b0:=255;ligkern[nl].b1:=0;
+ligkern[nl].b2:=0;ligkern[nl].b3:=0;nl:=nl+1;end;
+if ligkern[nl-1].b0=0 then ligkern[nl-1].b0:=128;end{:145};
+sevenunsafe:=false;for c:=0 to 255 do if charwd[c]<>0 then{140:}
+case chartag[c]of 0:;1:{149:}begin ligptr:=charremainder[c];
+repeat if hashinput(ligptr,c)then begin if ligkern[ligptr].b2<128 then
+begin if ligkern[ligptr].b1<>bchar then begin g:=ligkern[ligptr].b1;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('LIG character examined by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;begin g:=ligkern[ligptr].b3;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('LIG character generated by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;
+if ligkern[ligptr].b3>=128 then if(c<128)or(c=256)then if(ligkern[ligptr
+].b1<128)or(ligkern[ligptr].b1=bchar)then sevenunsafe:=true;
+end else if ligkern[ligptr].b1<>bchar then begin g:=ligkern[ligptr].b1;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('KRN character examined by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;end;
+if ligkern[ligptr].b0>=128 then ligptr:=nl else ligptr:=ligptr+1+ligkern
+[ligptr].b0;until ligptr>=nl;end{:149};2:begin g:=charremainder[c];
+if(g>=128)and(c<128)then sevenunsafe:=true;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('The character NEXTLARGER than',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;3:{141:}
+begin if exten[charremainder[c]].b0>0 then begin g:=exten[charremainder[
+c]].b0;if(g>=128)and(c<128)then sevenunsafe:=true;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('TOP piece of character',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;
+if exten[charremainder[c]].b1>0 then begin g:=exten[charremainder[c]].b1
+;if(g>=128)and(c<128)then sevenunsafe:=true;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('MID piece of character',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;
+if exten[charremainder[c]].b2>0 then begin g:=exten[charremainder[c]].b2
+;if(g>=128)and(c<128)then sevenunsafe:=true;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('BOT piece of character',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;
+begin g:=exten[charremainder[c]].b3;
+if(g>=128)and(c<128)then sevenunsafe:=true;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('REP piece of character',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;end{:141};end{:140};
+if charremainder[256]<32767 then begin c:=256;{149:}
+begin ligptr:=charremainder[c];
+repeat if hashinput(ligptr,c)then begin if ligkern[ligptr].b2<128 then
+begin if ligkern[ligptr].b1<>bchar then begin g:=ligkern[ligptr].b1;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('LIG character examined by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;begin g:=ligkern[ligptr].b3;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('LIG character generated by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;
+if ligkern[ligptr].b3>=128 then if(c<128)or(c=256)then if(ligkern[ligptr
+].b1<128)or(ligkern[ligptr].b1=bchar)then sevenunsafe:=true;
+end else if ligkern[ligptr].b1<>bchar then begin g:=ligkern[ligptr].b1;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('KRN character examined by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;end;
+if ligkern[ligptr].b0>=128 then ligptr:=nl else ligptr:=ligptr+1+ligkern
+[ligptr].b0;until ligptr>=nl;end{:149};end;
+if sevenbitsafeflag and sevenunsafe then writeln(
+'The font is not really seven-bit-safe!');{154:}
+if hashptr<hashsize then for hh:=1 to hashptr do begin tt:=hashlist[hh];
+if class[tt]>0 then tt:=f(tt,(hash[tt]-1)div 256,(hash[tt]-1)mod 256);
+end;
+if(hashptr=hashsize)or(yligcycle<256)then begin if hashptr<hashsize then
+begin write('Infinite ligature loop starting with ');
+if xligcycle=256 then write('boundary')else printoctal(xligcycle);
+write(' and ');printoctal(yligcycle);writeln('!');
+end else writeln(
+'Sorry, I haven''t room for so many ligature/kern pairs!');
+writeln('All ligatures will be cleared.');
+for c:=0 to 255 do if chartag[c]=1 then begin chartag[c]:=0;
+charremainder[c]:=0;end;nl:=0;bchar:=256;charremainder[256]:=32767;
+end{:154};{155:}
+if nl>0 then for ligptr:=0 to nl-1 do if ligkern[ligptr].b2<128 then
+begin if ligkern[ligptr].b0<255 then begin begin c:=ligkern[ligptr].b1;
+if charwd[c]=0 then if c<>bchar then begin ligkern[ligptr].b1:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','LIG step',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;begin c:=ligkern[ligptr].b3;
+if charwd[c]=0 then if c<>bchar then begin ligkern[ligptr].b3:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','LIG step',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;end;
+end else begin c:=ligkern[ligptr].b1;
+if charwd[c]=0 then if c<>bchar then begin ligkern[ligptr].b1:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','KRN step',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;
+if ne>0 then for g:=0 to ne-1 do begin begin c:=exten[g].b0;
+if c>0 then if charwd[c]=0 then begin exten[g].b0:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','VARCHAR TOP',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;begin c:=exten[g].b1;
+if c>0 then if charwd[c]=0 then begin exten[g].b1:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','VARCHAR MID',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;begin c:=exten[g].b2;
+if c>0 then if charwd[c]=0 then begin exten[g].b2:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','VARCHAR BOT',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;begin c:=exten[g].b3;
+if charwd[c]=0 then begin exten[g].b3:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','VARCHAR REP',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;end{:155};for c:=0 to 255 do{142:}
+if chartag[c]=2 then begin g:=charremainder[c];
+while(g<c)and(chartag[g]=2)do g:=charremainder[g];
+if g=c then begin chartag[c]:=0;
+write('A cycle of NEXTLARGER characters has been broken at ');
+printoctal(c);writeln('.');end;end{:142};{144:}delta:=shorten(1,255);
+setindices(1,delta);
+if delta>0 then writeln('I had to round some ','width','s by ',(((delta
++1)div 2)/1048576):1:7,' units.');delta:=shorten(2,15);
+setindices(2,delta);
+if delta>0 then writeln('I had to round some ','height','s by ',(((delta
++1)div 2)/1048576):1:7,' units.');delta:=shorten(3,15);
+setindices(3,delta);
+if delta>0 then writeln('I had to round some ','depth','s by ',(((delta
++1)div 2)/1048576):1:7,' units.');delta:=shorten(4,63);
+setindices(4,delta);
+if delta>0 then writeln('I had to round some ','italic correction',
+'s by ',(((delta+1)div 2)/1048576):1:7,' units.');{:144}{:139}end;
+procedure vfoutput;var c:byte;curfont:0..256;k:integer;begin{177:}
+write(vffile,247);write(vffile,202);write(vffile,vtitlelength);
+for k:=0 to vtitlelength-1 do write(vffile,vf[vtitlestart+k]);
+for k:=0 to 7 do write(vffile,headerbytes[k]);vcount:=vtitlelength+11;
+for curfont:=0 to fontptr-1 do{178:}begin write(vffile,243);
+write(vffile,curfont);write(vffile,fontchecksum[curfont].b0);
+write(vffile,fontchecksum[curfont].b1);
+write(vffile,fontchecksum[curfont].b2);
+write(vffile,fontchecksum[curfont].b3);voutint(fontat[curfont]);
+voutint(fontdsize[curfont]);write(vffile,farealength[curfont]);
+write(vffile,fnamelength[curfont]);
+for k:=0 to farealength[curfont]-1 do write(vffile,vf[fareastart[curfont
+]+k]);if fnamestart[curfont]=vfsize then begin write(vffile,78);
+write(vffile,85);write(vffile,76);write(vffile,76);
+end else for k:=0 to fnamelength[curfont]-1 do write(vffile,vf[
+fnamestart[curfont]+k]);
+vcount:=vcount+12+farealength[curfont]+fnamelength[curfont];end{:178};
+for c:=bc to ec do if charwd[c]>0 then{179:}begin x:=memory[charwd[c]];
+if designunits<>1048576 then x:=round((x/designunits)*1048576.0);
+if(packetlength[c]>241)or(x<0)or(x>=16777216)then begin write(vffile,242
+);voutint(packetlength[c]);voutint(c);voutint(x);
+vcount:=vcount+13+packetlength[c];
+end else begin write(vffile,packetlength[c]);write(vffile,c);
+write(vffile,x div 65536);write(vffile,(x div 256)mod 256);
+write(vffile,x mod 256);vcount:=vcount+5+packetlength[c];end;
+if packetstart[c]=vfsize then begin if c>=128 then write(vffile,128);
+write(vffile,c);
+end else for k:=0 to packetlength[c]-1 do write(vffile,vf[packetstart[c]
++k]);end{:179};repeat write(vffile,248);vcount:=vcount+1;
+until vcount mod 4=0{:177};end;{:180}{181:}begin initialize;nameenter;
+readinput;writeln('.');corrandcheck;{157:}{159:}lh:=headerptr div 4;
+notfound:=true;bc:=0;
+while notfound do if(charwd[bc]>0)or(bc=255)then notfound:=false else bc
+:=bc+1;notfound:=true;ec:=255;
+while notfound do if(charwd[ec]>0)or(ec=0)then notfound:=false else ec:=
+ec-1;if bc>ec then bc:=1;memory[1]:=memory[1]+1;memory[2]:=memory[2]+1;
+memory[3]:=memory[3]+1;memory[4]:=memory[4]+1;{168:}{169:}labelptr:=0;
+labeltable[0].rr:=-1;
+for c:=bc to ec do if chartag[c]=1 then begin sortptr:=labelptr;
+while labeltable[sortptr].rr>charremainder[c]do begin labeltable[sortptr
++1]:=labeltable[sortptr];sortptr:=sortptr-1;end;
+labeltable[sortptr+1].cc:=c;labeltable[sortptr+1].rr:=charremainder[c];
+labelptr:=labelptr+1;end{:169};
+if bchar<256 then begin extralocneeded:=true;lkoffset:=1;
+end else begin extralocneeded:=false;lkoffset:=0;end;{170:}
+begin sortptr:=labelptr;
+if labeltable[sortptr].rr+lkoffset>255 then begin lkoffset:=0;
+extralocneeded:=false;
+repeat charremainder[labeltable[sortptr].cc]:=lkoffset;
+while labeltable[sortptr-1].rr=labeltable[sortptr].rr do begin sortptr:=
+sortptr-1;charremainder[labeltable[sortptr].cc]:=lkoffset;end;
+lkoffset:=lkoffset+1;sortptr:=sortptr-1;
+until lkoffset+labeltable[sortptr].rr<256;end;
+if lkoffset>0 then while sortptr>0 do begin charremainder[labeltable[
+sortptr].cc]:=charremainder[labeltable[sortptr].cc]+lkoffset;
+sortptr:=sortptr-1;end;end{:170};
+if charremainder[256]<32767 then begin ligkern[nl-1].b2:=(charremainder[
+256]+lkoffset)div 256;
+ligkern[nl-1].b3:=(charremainder[256]+lkoffset)mod 256;end{:168};
+lf:=6+lh+(ec-bc+1)+memory[1]+memory[2]+memory[3]+memory[4]+nl+lkoffset+
+nk+ne+np;{:159};{160:}write(tfmfile,(lf)div 256);
+write(tfmfile,(lf)mod 256);write(tfmfile,(lh)div 256);
+write(tfmfile,(lh)mod 256);write(tfmfile,(bc)div 256);
+write(tfmfile,(bc)mod 256);write(tfmfile,(ec)div 256);
+write(tfmfile,(ec)mod 256);write(tfmfile,(memory[1])div 256);
+write(tfmfile,(memory[1])mod 256);write(tfmfile,(memory[2])div 256);
+write(tfmfile,(memory[2])mod 256);write(tfmfile,(memory[3])div 256);
+write(tfmfile,(memory[3])mod 256);write(tfmfile,(memory[4])div 256);
+write(tfmfile,(memory[4])mod 256);write(tfmfile,(nl+lkoffset)div 256);
+write(tfmfile,(nl+lkoffset)mod 256);write(tfmfile,(nk)div 256);
+write(tfmfile,(nk)mod 256);write(tfmfile,(ne)div 256);
+write(tfmfile,(ne)mod 256);write(tfmfile,(np)div 256);
+write(tfmfile,(np)mod 256);{:160};{162:}
+if not checksumspecified then{163:}begin curbytes.b0:=bc;
+curbytes.b1:=ec;curbytes.b2:=bc;curbytes.b3:=ec;
+for c:=bc to ec do if charwd[c]>0 then begin tempwidth:=memory[charwd[c]
+];if designunits<>1048576 then tempwidth:=round((tempwidth/designunits)
+*1048576.0);tempwidth:=tempwidth+(c+4)*4194304;
+curbytes.b0:=(curbytes.b0+curbytes.b0+tempwidth)mod 255;
+curbytes.b1:=(curbytes.b1+curbytes.b1+tempwidth)mod 253;
+curbytes.b2:=(curbytes.b2+curbytes.b2+tempwidth)mod 251;
+curbytes.b3:=(curbytes.b3+curbytes.b3+tempwidth)mod 247;end;
+headerbytes[0]:=curbytes.b0;headerbytes[1]:=curbytes.b1;
+headerbytes[2]:=curbytes.b2;headerbytes[3]:=curbytes.b3;end{:163};
+headerbytes[4]:=designsize div 16777216;
+headerbytes[5]:=(designsize div 65536)mod 256;
+headerbytes[6]:=(designsize div 256)mod 256;
+headerbytes[7]:=designsize mod 256;
+if not sevenunsafe then headerbytes[68]:=128;
+for j:=0 to headerptr-1 do write(tfmfile,headerbytes[j]);{:162};{164:}
+index[0]:=0;for c:=bc to ec do begin write(tfmfile,index[charwd[c]]);
+write(tfmfile,index[charht[c]]*16+index[chardp[c]]);
+write(tfmfile,index[charic[c]]*4+chartag[c]);
+write(tfmfile,charremainder[c]);end{:164};{166:}
+for q:=1 to 4 do begin write(tfmfile,0);write(tfmfile,0);
+write(tfmfile,0);write(tfmfile,0);p:=link[q];
+while p>0 do begin outscaled(memory[p]);p:=link[p];end;end;{:166};{171:}
+if extralocneeded then begin write(tfmfile,255);write(tfmfile,bchar);
+write(tfmfile,0);write(tfmfile,0);
+end else for sortptr:=1 to lkoffset do begin t:=labeltable[labelptr].rr;
+if bchar<256 then begin write(tfmfile,255);write(tfmfile,bchar);
+end else begin write(tfmfile,254);write(tfmfile,0);end;
+write(tfmfile,(t+lkoffset)div 256);write(tfmfile,(t+lkoffset)mod 256);
+repeat labelptr:=labelptr-1;until labeltable[labelptr].rr<t;end;
+if nl>0 then for ligptr:=0 to nl-1 do begin write(tfmfile,ligkern[ligptr
+].b0);write(tfmfile,ligkern[ligptr].b1);
+write(tfmfile,ligkern[ligptr].b2);write(tfmfile,ligkern[ligptr].b3);end;
+if nk>0 then for krnptr:=0 to nk-1 do outscaled(kern[krnptr]){:171};
+{172:}if ne>0 then for c:=0 to ne-1 do begin write(tfmfile,exten[c].b0);
+write(tfmfile,exten[c].b1);write(tfmfile,exten[c].b2);
+write(tfmfile,exten[c].b3);end;{:172};{173:}
+for parptr:=1 to np do begin if parptr=1 then{174:}
+begin if param[1]<0 then begin param[1]:=param[1]+1073741824;
+write(tfmfile,(param[1]div 16777216)+192);
+end else write(tfmfile,param[1]div 16777216);
+write(tfmfile,(param[1]div 65536)mod 256);
+write(tfmfile,(param[1]div 256)mod 256);write(tfmfile,param[1]mod 256);
+end{:174}else outscaled(param[parptr]);end{:173}{:157};vfoutput;
+9999:writeln(' ');end.{:181}
diff --git a/systems/knuth/local/h00vars.h b/systems/knuth/local/h00vars.h
new file mode 100644
index 0000000000..288205b459
--- /dev/null
+++ b/systems/knuth/local/h00vars.h
@@ -0,0 +1,143 @@
+/* Header file for C programs that supplement the Pascal code */
+/* Copyright (c) 1979 Regents of the University of California */
+
+/* sccsid[] = "@(#)h00vars.h 1.11 5/8/84"; */
+/* THIS VERSION COMES BACK FROM SUN AND ALLEGEDLY MATCHES OUR CURRENT
+ COMPILER 8/4/84 */
+
+#include <stdio.h>
+/* #include "whoami.h" --- no, don't bother; it's empty --- don (8/7/89) */
+
+#define PXPFILE "pmon.out"
+#define BITSPERBYTE 8
+#define BITSPERLONG (BITSPERBYTE * sizeof(long))
+#define LG2BITSBYTE 03
+#define MSKBITSBYTE 07
+#define LG2BITSLONG 05
+#define MSKBITSLONG 037
+#define HZ 60
+#define MAXLVL 20
+#define MAXERRS 75
+#define NAMSIZ 76
+#define MAXFILES 32
+#define PREDEF 2
+#ifdef ADDR32
+#define STDLVL ((struct iorec *)(0x7ffffff1))
+#define GLVL ((struct iorec *)(0x7ffffff0))
+#endif ADDR32
+#ifdef ADDR16
+#define STDLVL ((struct iorec *)(0xfff1))
+#define GLVL ((struct iorec *)(0xfff0))
+#endif ADDR16
+#define FILNIL ((struct iorec *)(0))
+#define INPUT ((struct iorec *)(&input))
+#define OUTPUT ((struct iorec *)(&output))
+#define ERR ((struct iorec *)(&_err))
+typedef enum {FALSE, TRUE} bool;
+#define MAXINT 2147483647
+#define MININT -2147483648
+
+
+/*
+ * runtime display structure
+ */
+struct display {
+ char *ap;
+ char *fp;
+};
+
+/*
+ * formal routine structure
+ */
+struct formalrtn {
+ long (*fentryaddr)(); /* formal entry point */
+ long fbn; /* block number of function */
+ struct display fdisp[ MAXLVL ]; /* saved at first passing */
+};
+
+/*
+ * program variables
+ */
+extern struct display _disply[MAXLVL];/* runtime display */
+extern int _argc; /* number of passed args */
+extern char **_argv; /* values of passed args */
+extern long _stlim; /* statement limit */
+extern long _stcnt; /* statement count */
+extern long _seed; /* random number seed */
+extern char *_maxptr; /* maximum valid pointer */
+extern char *_minptr; /* minimum valid pointer */
+extern long _pcpcount[]; /* pxp buffer */
+
+/*
+ * file structures
+ */
+struct iorechd {
+ char *fileptr; /* ptr to file window */
+ long lcount; /* number of lines printed */
+ long llimit; /* maximum number of text lines */
+ FILE *fbuf; /* FILE ptr */
+ struct iorec *fchain; /* chain to next file */
+ struct iorec *flev; /* ptr to associated file variable */
+ char *pfname; /* ptr to name of file */
+ short funit; /* file status flags */
+ unsigned short fblk; /* index into active file table */
+ long fsize; /* size of elements in the file */
+ char fname[NAMSIZ]; /* name of associated UNIX file */
+};
+
+struct iorec {
+ char *fileptr; /* ptr to file window */
+ long lcount; /* number of lines printed */
+ long llimit; /* maximum number of text lines */
+ FILE *fbuf; /* FILE ptr */
+ struct iorec *fchain; /* chain to next file */
+ struct iorec *flev; /* ptr to associated file variable */
+ char *pfname; /* ptr to name of file */
+ short funit; /* file status flags */
+ unsigned short fblk; /* index into active file table */
+ long fsize; /* size of elements in the file */
+ char fname[NAMSIZ]; /* name of associated UNIX file */
+ char buf[BUFSIZ]; /* I/O buffer */
+ char window[1]; /* file window element */
+};
+
+/*
+ * unit flags
+ */
+#define SPEOLN 0x100 /* 1 => pseudo EOLN char read at EOF */
+#define FDEF 0x080 /* 1 => reserved file name */
+#define FTEXT 0x040 /* 1 => text file, process EOLN */
+#define FWRITE 0x020 /* 1 => open for writing */
+#define FREAD 0x010 /* 1 => open for reading */
+#define TEMP 0x008 /* 1 => temporary file */
+#define SYNC 0x004 /* 1 => window is out of sync */
+#define EOLN 0x002 /* 1 => at end of line */
+#define EOFF 0x001 /* 1 => at end of file */
+
+/*
+ * file routines
+ */
+extern struct iorec *GETNAME();
+extern char *MKTEMP();
+extern char *PALLOC();
+
+/*
+ * file record variables
+ */
+extern struct iorechd _fchain; /* head of active file chain */
+extern struct iorec *_actfile[]; /* table of active files */
+extern long _filefre; /* last used entry in _actfile */
+
+/*
+ * standard files
+ */
+extern struct iorechd input;
+extern struct iorechd output;
+extern struct iorechd _err;
+
+/*
+ * seek pointer struct for TELL, SEEK extensions
+ */
+struct seekptr {
+ long cnt;
+};
diff --git a/systems/knuth/local/lib/3list-small.tex b/systems/knuth/local/lib/3list-small.tex
new file mode 100644
index 0000000000..d5dd720eb1
--- /dev/null
+++ b/systems/knuth/local/lib/3list-small.tex
@@ -0,0 +1,71 @@
+% Makes a 3-column landscape listing of one or more files
+% Typical usage:
+% tex 3list-small *.c \\end
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex8 at 7pt
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\baselineskip=8pt
+\medskipamount=3pt minus 1pt
+\raggedbottom
+\nopagenumbers
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\ifright \headlinefont \hfill Page \folio
+ \else\hbox to2.5in{\filenamefont#1\quad
+ \headlinefont \today\ at \hours\hskip-3in\hss}\hfill \fi}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus .8in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-50\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject
+ \ifright\null\vfill\eject\fi\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+\hsize=3truein
+\vsize=7truein
+\hoffset=-.5truein
+\voffset=-.2truein
+\newif\ifright \newif\ifmid
+\newbox\lefthalf \newbox\midhalf
+\output{\ifright
+ \shipout\hbox{\hbox to3.5in{\box\lefthalf\hss}%
+ \hbox to3.5in{\box\midhalf\hss}%
+ \vbox{\makeheadline\pagebody\makefootline}}
+ \advancepageno\global\rightfalse
+ \else\ifmid\global\setbox\midhalf\vbox{\pagebody\makefootline}
+ \global\midfalse\global\righttrue
+ \else\global\setbox\lefthalf\vbox{\makeheadline\pagebody\makefootline}
+ \global\midtrue\fi\fi}
+
+\special{landscape}
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 63 characters on a line without an overfull box.
+% (But lines with more than 59 characters may overlap with the next column,
+% or be clipped off in the rightmost column.)
diff --git a/systems/knuth/local/lib/3list.tex b/systems/knuth/local/lib/3list.tex
new file mode 100644
index 0000000000..64c21c339c
--- /dev/null
+++ b/systems/knuth/local/lib/3list.tex
@@ -0,0 +1,71 @@
+% Makes a 3-column landscape listing of one or more files
+% Typical usage:
+% tex 3list *.c \\end
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex8
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\baselineskip=9pt
+\medskipamount=4pt minus 1pt
+\raggedbottom
+\nopagenumbers
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\ifright \headlinefont \hfill Page \folio
+ \else\hbox to2.5in{\filenamefont#1\quad
+ \headlinefont \today\ at \hours\hskip-3in\hss}\hfill \fi}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus .8in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-50\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject
+ \ifright\null\vfill\eject\fi\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+\hsize=3truein
+\vsize=7truein
+\hoffset=-.5truein
+\voffset=-.2truein
+\newif\ifright \newif\ifmid
+\newbox\lefthalf \newbox\midhalf
+\output{\ifright
+ \shipout\hbox{\hbox to3.5in{\box\lefthalf\hss}%
+ \hbox to3.5in{\box\midhalf\hss}%
+ \vbox{\makeheadline\pagebody\makefootline}}
+ \advancepageno\global\rightfalse
+ \else\ifmid\global\setbox\midhalf\vbox{\pagebody\makefootline}
+ \global\midfalse\global\righttrue
+ \else\global\setbox\lefthalf\vbox{\makeheadline\pagebody\makefootline}
+ \global\midtrue\fi\fi}
+
+\special{landscape}
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 63 characters on a line without an overfull box.
+% (But lines with more than 59 characters may overlap with the next column,
+% or be clipped off in the rightmost column.)
diff --git a/systems/knuth/local/lib/6list.tex b/systems/knuth/local/lib/6list.tex
new file mode 100644
index 0000000000..5d59194ffe
--- /dev/null
+++ b/systems/knuth/local/lib/6list.tex
@@ -0,0 +1,75 @@
+% Makes a 6-column landscape listing of one or more files with narrow lines
+% Typical usage:
+% tex 6list foo \\end
+% and then use gv to print the result (my dvips has trouble with landscape)
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex8
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\baselineskip=9pt
+\medskipamount=4pt minus 1pt
+\raggedbottom
+\nopagenumbers
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\hbox to1.25in{\filenamefont#1\quad
+ \headlinefont \today\ at \hours\hskip3in Page \folio\hss}\hfill}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus .8in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-50\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject
+ \ifright\null\vfill\eject\fi\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+\hsize=1.65truein
+\vsize=7truein
+\hoffset=-.5truein
+\voffset=-.2truein
+\newif\ifright
+\newcount\boxnum \boxnum=0
+\output{\ifright\else\ifcase\boxnum
+ \global\setbox0=\vbox{\makeheadline\pagebody\makefootline}\global\boxnum=1\or
+ \global\setbox1=\vbox{\pagebody\makefootline}\global\boxnum=2\or
+ \global\setbox2=\vbox{\pagebody\makefootline}\global\boxnum=3\or
+ \global\setbox3=\vbox{\pagebody\makefootline}\global\boxnum=4\or
+ \global\setbox4=\vbox{\pagebody\makefootline}\global\boxnum=5\or
+ \shipout\hbox{\hbox to1.7truein{\box0\hss}%
+ \hbox to1.7truein{\box1\hss}%
+ \hbox to1.7truein{\box2\hss}%
+ \hbox to1.7truein{\box3\hss}%
+ \hbox to1.7truein{\box4\hss}%
+ \vbox{\pagebody\makefootline}}
+ \advancepageno\global\boxnum=0\fi\fi}
+
+\special{landscape}
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 63 characters on a line without an overfull box.
+% (But lines with more than 59 characters may overlap with the next column,
+% or be clipped off in the rightmost column.)
diff --git a/systems/knuth/local/lib/8list.tex b/systems/knuth/local/lib/8list.tex
new file mode 100644
index 0000000000..b25b5b7d0f
--- /dev/null
+++ b/systems/knuth/local/lib/8list.tex
@@ -0,0 +1,79 @@
+% Makes a 8-column landscape listing of one or more files with narrow lines
+% Typical usage:
+% tex 8list foo \\end
+% and then use gv to print the result (my dvips has trouble with landscape)
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex8
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\baselineskip=9pt
+\medskipamount=4pt minus 1pt
+\raggedbottom
+\nopagenumbers
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\hbox to1.25in{\filenamefont#1\quad
+ \headlinefont \today\ at \hours\hskip3in Page \folio\hss}\hfill}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus .8in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-50\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject
+ \ifright\null\vfill\eject\fi\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+\hsize=1.25truein
+\vsize=7truein
+\hoffset=-.5truein
+\voffset=-.2truein
+\newif\ifright
+\newcount\boxnum \boxnum=0
+\output{\ifright\else\ifcase\boxnum
+ \global\setbox0=\vbox{\makeheadline\pagebody\makefootline}\global\boxnum=1\or
+ \global\setbox1=\vbox{\pagebody\makefootline}\global\boxnum=2\or
+ \global\setbox2=\vbox{\pagebody\makefootline}\global\boxnum=3\or
+ \global\setbox3=\vbox{\pagebody\makefootline}\global\boxnum=4\or
+ \global\setbox4=\vbox{\pagebody\makefootline}\global\boxnum=5\or
+ \global\setbox5=\vbox{\pagebody\makefootline}\global\boxnum=6\or
+ \global\setbox6=\vbox{\pagebody\makefootline}\global\boxnum=7\or
+ \shipout\hbox{\hbox to1.3truein{\box0\hss}%
+ \hbox to1.3truein{\box1\hss}%
+ \hbox to1.3truein{\box2\hss}%
+ \hbox to1.3truein{\box3\hss}%
+ \hbox to1.3truein{\box4\hss}%
+ \hbox to1.3truein{\box5\hss}%
+ \hbox to1.3truein{\box6\hss}%
+ \vbox{\pagebody\makefootline}}
+ \advancepageno\global\boxnum=0\fi\fi}
+
+\special{landscape}
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 63 characters on a line without an overfull box.
+% (But lines with more than 59 characters may overlap with the next column,
+% or be clipped off in the rightmost column.)
diff --git a/systems/knuth/local/lib/amsmodes.mf b/systems/knuth/local/lib/amsmodes.mf
new file mode 100644
index 0000000000..5bbd2cd380
--- /dev/null
+++ b/systems/knuth/local/lib/amsmodes.mf
@@ -0,0 +1,286 @@
+% AMSMODES.MF
+% This file can be loaded after PLAIN.MF.
+% It introduces conventions that were commonly used at WAITS and
+% may still be commonly used at AMS.
+% (I combined two files on AMS's public directory --- don.)
+base_version:=base_version&"/AMS";
+
+% We make the following modification of "mode_def" from PLAIN in order
+% to accommodate modes not preloaded in MF. Even though mode had been
+% set dependently on the command line (as with mode=talaris), when
+% mode_def came along to define "talaris" the assignment operator
+% (:=) wiped out that dependency and left mode hanging and undefined
+% when mode_setup was called. We simply add a test to see whether
+% "mode" has been set to the mode being defined.
+
+% Furthermore, if mode was set to a mode which was preloaded and
+% that mode was re-defined at run-time, the value of mode would
+% only pick up the old definition. So, for the other case,
+% that the mode being defined already has a numeric realization,
+% we avoid redefining that mode's (behind-the-scenes) value.
+
+% We also add the following macros to enable conditional "and"
+% and "or". See The METAFONTbook, pp.288--289.
+
+def cand(text q) = startif true q else: false fi enddef;
+def cor(text q) = startif true true else: q fi enddef;
+tertiarydef p startif true = if p: enddef;
+
+def mode_def suffix $ =
+ if known($) cand
+ (numeric $ cand
+ ($<number_of_modes cand
+ (mode_name[$] = (str $ & "_")))):
+ else: if known(mode-$) cand (unknown mode): mode:= fi
+ $:=incr number_of_modes;
+ mode_name[$]:=str$ & "_";
+ fi
+ expandafter quote def scantokens mode_name[$] enddef;
+
+% Say "boise_proof" on the command line, to get proofmode on boise
+def boise_proof = lcode_:=" /";
+ extra_setup:=extra_setup&"grayfont grbois;" enddef;
+
+screen_rows:=400; screen_cols:=480;
+
+% Here are conventions for local output devices:
+
+mode_def dover = % dover mode: for the Xerox Dover
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=384;
+ blacker:=1.2; % make pens somewhat blacker
+ fillin:=0; % no compensation for fillin
+ o_correction:=.6; % but don't overshoot as much
+ enddef;
+
+mode_def imagen = % imagen mode: for the Imagen 8/300 (Canon engine)
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=300;
+ blacker:=0; % Canon engine is black enough
+ fillin:=.2; % and it tends to fill in diagonals
+ o_correction:=.6; %
+ enddef;
+
+mode_def qms = % qms mode: for the QMS (Xerox engine)
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=300;
+ blacker:=.75; % (this value is conjectural)
+ fillin:=0; % (ditto)
+ o_correction:=.5; % (ditto)
+ enddef;
+
+mode_def aps = % aps mode: for the Autologic APS-Micro5
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=1; % yes, show titles online
+ pixels_per_inch:=722.909; % that's roughly 10 per pt
+ blacker:=.2; % make pens a teeny bit blacker
+ fillin:=.2; % but compensate for diagonal fillin
+ o_correction:=1; % and keep the full overshoot
+ enddef;
+
+mode_def linotronic = % for the Linotronic
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=1; % yes, show titles online
+ pixels_per_inch:=1270; % high resolution mode
+ blacker:=.2; % make pens a teeny bit blacker (aps value)
+ fillin:=.2; % but compensate for diagonal fillin (aps value)
+ o_correction:=1; % and keep the full overshoot (aps value)
+ enddef;
+
+mode_def halflinotronic = % for the Linotronic
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=1; % yes, show titles online
+ pixels_per_inch:=635; % high resolution mode
+ blacker:=.2; % make pens a teeny bit blacker (aps value)
+ fillin:=.2; % but compensate for diagonal fillin (aps value)
+ o_correction:=1; % and keep the full overshoot (aps value)
+ enddef;
+
+mode_def crs = % crs mode: for the Alphatype CRS
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=1; % yes, show titles online
+ begingroup interim warningcheck:=0; % (we need to go past 4096)
+ pixels_per_inch:=4000+4000/3 endgroup; % HIGH res
+ blacker:=4; % make pens a lot blacker
+ fillin:=0; % but don't compensate for fillin
+ o_correction:=1; % and keep the full overshoot
+ enddef;
+
+mode_def boise = % boise mode: for the HP 2680A
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=180;
+ blacker:=.55; % make pens a bit blacker
+ fillin:=.1; % and compensate for diagonal fillin
+ o_correction:=.3; % but don't overshoot much
+ enddef;
+
+mode_def DD = % DD mode: for the DataDisc (usually with mag=magstep 2=1.44)
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=70; % really lowres
+ blacker:=0; % don't make the pens any blacker
+ fillin:=0; % and don't compensate for fillin
+ o_correction:=.2; % but suppress most overshoots
+ enddef;
+
+mode_def canon = % canon mode: for the old Imagen (Canon LBP10)
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=240;
+ blacker:=.2; % make pens a wee bit blacker
+ fillin:=.2; % and compensate for fillin
+ o_correction:=.4; % but don't overshoot as much
+ enddef;
+
+mode_def newDD = % newDD mode: for DataDisc with special aspect ratio
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=70; % really lowres
+ blacker:=0; % don't make the pens any blacker
+ fillin:=0; % and don't compensate for fillin
+ o_correction:=.2; % but suppress most overshoots
+ aspect_ratio:=4/3; % this is the new feature...
+ enddef;
+
+mode_def cg = % cg mode: for the Compugraphic 8600
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=1; % yes, show titles online
+ pixels_per_inch:=1301.5; % a bit less than 20 per pt
+ blacker:=.2; % make pens a teeny bit blacker
+ fillin:=.2; % but compensate for diagonal fillin
+ o_correction:=1; % and keep the full overshoot
+ aspect_ratio:=1569/1301.5; % strange, no?
+ enddef;
+
+mode_def epson = % Epson
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=240; % lowres
+ blacker:=0; % don't make the pens any blacker
+ fillin:=0; % and don't compensate for fillin
+ o_correction:=.2; % but suppress most overshoots
+ aspect_ratio:=9/10; % 216 dots/inch vertical
+ enddef;
+
+mode_def pcscreen = % pcscreen mode: for screen fonts (118dpi)
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=118;
+ blacker:=0; % don't make pens any blacker
+ fillin:=0; % and don't compensate for diagonal fillin
+ o_correction:=0; % don't overshoot (corrected -- they had 1 -- don)
+ enddef;
+
+mode_def epsonlq = % epsonlq mode: for the Epson LQ and Toshiba
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=180;
+ blacker:=0; % don't make pens any blacker
+ fillin:=0; % and don't compensate for diagonal fillin
+ o_correction:=.3; % but don't overshoot much
+ enddef;
+
+mode_def epsonx = % Epson 240dpi
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=240; % lowres
+ blacker:=0; % don't make the pens any blacker
+ fillin:=0; % and don't compensate for fillin
+ o_correction:=.2; % but suppress most overshoots
+ aspect_ratio:=9/10; % 216 dots/inch vertical
+ enddef;
+
+mode_def laser = % laser mode: for the Apple LaserWriter or Canon 300dpi
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=300;
+ blacker:=0; % Canon engine is black enough
+ fillin:=.2; % and it tends to fill in diagonals
+ o_correction:=.6; %
+ enddef;
+
+mode_def fourc = % for 400dpi printers
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=400;
+ blacker:=0; % engine is black enough
+ fillin:=.2; % and it tends to fill in diagonals
+ o_correction:=.6; %
+ enddef;
+
+% macintosh mode: for screen fonts (72dpi) & imagewriter (144dpi)
+mode_def macintosh =
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=72;
+ blacker:=0; % Don't blacken
+ fillin:=0; %
+ o_correction:=0; % (corrected -- they had 1 -- don)
+ enddef;
+
+mode_def notimagewriter = % notimagewriter mode: to get 129pk for screen
+ % Largest resolution which guarantees at least one point size less
+ % than imagewriter size.
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=129;
+ blacker:=0; % Don't blacken
+ fillin:=0; %
+ o_correction:=0; % (corrected -- they had 1 -- don)
+ enddef;
+
+localfont:=imagen;
+
+% Finally, here are macros for Xerox-world font info:
+
+def font_family expr s = % string s names the font family, e.g., "CMR"
+ headerbyte 49: BCPL_string(s,20);
+ special "identifier "&s enddef;
+def coding_scheme expr s = % string s names the scheme, e.g. "TEX TEXT"
+ headerbyte 9: BCPL_string(s,40);
+ special "codingscheme "&s enddef;
+def font_face_byte expr x = % integer x gives the family member number,
+ headerbyte 72: x; % which should be between 0 and 255
+ special "fontfacebyte"; numspecial x enddef;
+
+def BCPL_string(expr s,n)= % string s becomes an n-byte BCPL string
+ for l:=if length(s)>=n: n-1 else: length(s) fi: l
+ for k:=1 upto l: , substring (k-1,k) of s endfor
+ for k:=l+2 upto n: , 0 endfor endfor enddef;
+
+Xerox_world:=1; % users can say `if known Xerox_world:...fi'
+
+inner end;
+def bye=
+ if fontmaking>0: font_family font_identifier_;
+ coding_scheme font_coding_scheme_;
+ font_face_byte max(0,254-round 2designsize); fi
+ tracingstats:=1; % at least for now we want this
+ end
+ enddef;
+
+outer bye,end;
diff --git a/systems/knuth/local/lib/amsppt.sty b/systems/knuth/local/lib/amsppt.sty
new file mode 100644
index 0000000000..68e030688b
--- /dev/null
+++ b/systems/knuth/local/lib/amsppt.sty
@@ -0,0 +1,1133 @@
+%% This is file `amsppt.sty' generated from `amsppt.doc'
+%% on 22-JAN-1992 by the docstrip.ams utility (1.2).
+%%
+%% @texfile{
+%% filename="amsppt.sty",
+\def\filename{amsppt.sty}
+%% version="2.1a",
+\def\fileversion{2.1a}
+%% date="22-JAN-1992",
+\def\filedate{22-JAN-1992}
+%% filetype="AMS-TeX: documentstyle",
+%% copyright="Copyright (C) American Mathematical Society,
+%% all rights reserved. Copying of this file is
+%% authorized only if either:
+%% (1) you make absolutely no changes to your copy
+%% including name; OR
+%% (2) if you do make changes, you first rename it to some
+%% other name.",
+%% author="American Mathematical Society",
+%% address="American Mathematical Society,
+%% Technical Support Group,
+%% P. O. Box 6248,
+%% Providence, RI 02940,
+%% USA",
+%% telephone="401-455-4080 or (in the USA) 800-321-4AMS",
+%% email="Internet: Tech-Support@Math.AMS.com",
+%% codetable="ISO/ASCII",
+%% checksumtype="line count",
+%% checksum="1133",
+%% keywords="amstex, ams-tex, tex",
+%% abstract="This file is a documentstyle for use with
+%% AMS-TeX 2.1. It exists in two forms: amsppt.doc, which
+%% is the full form containing all technical documentation,
+%% and amsppt.sty, which is identical except that all lines
+%% beginning with a single percent sign are removed, to
+%% make the file more rapid-loading when processed by
+%% TeX."
+%% }
+\expandafter\ifx\csname amsppt.sty\endcsname\endinput
+ \expandafter\def\csname amsppt.sty\endcsname{2.1 (1-JUL-1991)}\fi
+\xdef\fileversiontest{\fileversion\space(\filedate)}
+\expandafter\ifx\csname\filename\endcsname\fileversiontest
+ \message{[already loaded]}\endinput\fi
+\expandafter\ifx\csname\filename\endcsname\relax % file not yet loaded
+ \else\errmessage{Discrepancy in `\filename' file versions:
+ version \csname\filename\endcsname\space already loaded, trying
+ now to load version \fileversiontest}\fi
+\expandafter\xdef\csname\filename\endcsname{%
+ \catcode`\noexpand\@=\the\catcode`\@
+ \expandafter\gdef\csname\filename\endcsname{%
+ \fileversion\space(\filedate)}}
+\catcode`\@=11
+\message{version \fileversion\space(\filedate):}
+\expandafter\ifx\csname styname\endcsname\relax
+ \def\styname{AMSPPT}\def\styversion{2.1a}
+\fi
+\message{Loading utility definitions,}
+\def\identity@#1{#1}
+\def\nofrills@@#1{%
+ \DN@{#1}%
+ \ifx\next\nofrills \let\frills@\eat@
+ \expandafter\expandafter\expandafter\next@\expandafter\eat@
+ \else \let\frills@\identity@\expandafter\next@\fi}
+\def\nofrillscheck#1{\def\nofrills@{\nofrills@@{#1}}%
+ \futurelet\next\nofrills@}
+\Invalid@\usualspace
+\def\addto#1#2{\csname \expandafter\eat@\string#1@\endcsname
+ \expandafter{\the\csname \expandafter\eat@\string#1@\endcsname#2}}
+\newdimen\bigsize@
+\def\big@#1#2{{\hbox{$\left#2\vcenter to#1\bigsize@{}%
+ \right.\nulldelimiterspace\z@\m@th$}}}
+\def\big{\big@\@ne}
+\def\Big{\big@{1.5}}
+\def\bigg{\big@\tw@}
+\def\Bigg{\big@{2.5}}
+\def\raggedcenter@{\leftskip\z@ plus.4\hsize \rightskip\leftskip
+ \parfillskip\z@ \parindent\z@ \spaceskip.3333em \xspaceskip.5em
+ \pretolerance9999\tolerance9999 \exhyphenpenalty\@M
+ \hyphenpenalty\@M \let\\\linebreak}
+\def\uppercasetext@#1{%
+ {\spaceskip1.3\fontdimen2\the\font plus1.3\fontdimen3\the\font
+ \def\ss{SS}\let\i=I\let\j=J\let\ae\AE\let\oe\OE
+ \let\o\O\let\aa\AA\let\l\L
+ \skipmath@#1$\skipmath@$}}
+\def\skipmath@#1$#2${\uppercase{#1}%
+ \ifx\skipmath@#2\else$#2$\expandafter\skipmath@\fi}
+\def\add@missing#1{\expandafter\ifx\envir@end#1%
+ \Err@{You seem to have a missing or misspelled
+ \expandafter\string\envir@end ...}%
+ \envir@end
+\fi}
+\newtoks\revert@
+\def\envir@stack#1{\toks@\expandafter{\envir@end}%
+ \edef\next@{\def\noexpand\envir@end{\the\toks@}%
+ \revert@{\the\revert@}}%
+ \revert@\expandafter{\next@}%
+ \def\envir@end{#1}}
+\begingroup
+\catcode`\ =11
+\gdef\revert@envir#1{\expandafter\ifx\envir@end#1%
+\the\revert@%
+\else\ifx\envir@end\enddocument \Err@{Extra \string#1}%
+\else\expandafter\add@missing\envir@end\revert@envir#1%
+\fi\fi}
+\xdef\enddocument {\string\enddocument}%
+\global\let\envir@end\enddocument %%%%%% don't remove the final space!
+\endgroup\relax
+\def\first@#1#2\end{#1}
+\def\true@{TT}
+\def\false@{TF}
+\begingroup \catcode`\-=3
+\long\gdef\notempty#1{%
+ \expandafter\ifx\first@#1-\end-\empty \false@\else \true@\fi}
+\endgroup
+\message{more fonts,}
+\font@\tensmc=cmcsc10
+\font@\sevenex=cmex7
+%% non-AMSfonts substitute for previous line:
+%%\font@\sevenex=cmex10 at 7pt
+\font@\sevenit=cmti7
+\font@\eightrm=cmr8 % preloaded in plain.tex
+\font@\sixrm=cmr6 % preloaded in plain.tex
+\font@\eighti=cmmi8 \skewchar\eighti='177 % preloaded
+\font@\sixi=cmmi6 \skewchar\sixi='177 % preloaded
+\font@\eightsy=cmsy8 \skewchar\eightsy='60 % preloaded
+\font@\sixsy=cmsy6 \skewchar\sixsy='60 % preloaded
+\font@\eightex=cmex8
+%% non-AMSfonts substitute for previous line:
+%%\font@\eightex=cmex10 at 8pt
+\font@\eightbf=cmbx8 % preloaded in plain.tex
+\font@\sixbf=cmbx6 % preloaded in plain.tex
+\font@\eightit=cmti8 % preloaded in plain.tex
+\font@\eightsl=cmsl8 % preloaded in plain.tex
+\font@\eightsmc=cmcsc8
+%% non-AMSfonts substitute for previous line:
+%%\font@\eightsmc=cmcsc10 at 8pt
+\font@\eighttt=cmtt8 % preloaded in plain.tex
+%% Nine-point fonts are not needed but are included here, commented
+%% out, to make it easier for a user to add them if they are needed.
+%%\font@\ninerm=cmr9
+%%\font@\ninei=cmmi9 \skewchar\ninei='177
+%%\font@\ninesy=cmsy9 \skewchar\ninesy='60
+%%\font@\nineex=cmex9
+%%%%\font@\nineex=cmex10 at9pt % non-AMSfonts substitute
+%%\font@\ninebf=cmbx9
+%%\font@\nineit=cmti9
+%%\font@\ninesl=cmsl9
+%%\font@\ninesmc=cmcsc9
+%%
+%%\font@\ninemsa=msam9
+%%\font@\ninemsb=msbm9
+%%\font@\nineeufm=eufm9
+%% To use amsppt.sty without AMSFonts, comment out the following
+%% two lines (and refer to the lines above that begin with double
+%% percent signs); to load extra math symbols only on demand (with
+%% \newsymbol) comment out the second line.
+\loadeufm \loadmsam \loadmsbm
+\message{symbol names}\UseAMSsymbols\message{,}
+\newtoks\tenpoint@
+\def\tenpoint{\normalbaselineskip12\p@
+ \abovedisplayskip12\p@ plus3\p@ minus9\p@
+ \belowdisplayskip\abovedisplayskip
+ \abovedisplayshortskip\z@ plus3\p@
+ \belowdisplayshortskip7\p@ plus3\p@ minus4\p@
+ \textonlyfont@\rm\tenrm \textonlyfont@\it\tenit
+ \textonlyfont@\sl\tensl \textonlyfont@\bf\tenbf
+ \textonlyfont@\smc\tensmc \textonlyfont@\tt\tentt
+ \ifsyntax@ \def\big##1{{\hbox{$\left##1\right.$}}}%
+ \let\Big\big \let\bigg\big \let\Bigg\big
+ \else
+ \textfont\z@\tenrm \scriptfont\z@\sevenrm
+ \scriptscriptfont\z@\fiverm
+ \textfont\@ne\teni \scriptfont\@ne\seveni
+ \scriptscriptfont\@ne\fivei
+ \textfont\tw@\tensy \scriptfont\tw@\sevensy
+ \scriptscriptfont\tw@\fivesy
+ \textfont\thr@@\tenex \scriptfont\thr@@\sevenex
+ \scriptscriptfont\thr@@\sevenex
+ \textfont\itfam\tenit \scriptfont\itfam\sevenit
+ \scriptscriptfont\itfam\sevenit
+ \textfont\bffam\tenbf \scriptfont\bffam\sevenbf
+ \scriptscriptfont\bffam\fivebf
+ \setbox\strutbox\hbox{\vrule height8.5\p@ depth3.5\p@ width\z@}%
+ \setbox\strutbox@\hbox{\lower.5\normallineskiplimit\vbox{%
+ \kern-\normallineskiplimit\copy\strutbox}}%
+ \setbox\z@\vbox{\hbox{$($}\kern\z@}\bigsize@1.2\ht\z@
+ \fi
+ \normalbaselines\rm\dotsspace@1.5mu\ex@.2326ex\jot3\ex@
+ \the\tenpoint@}
+\newtoks\eightpoint@
+\def\eightpoint{\normalbaselineskip10\p@
+ \abovedisplayskip10\p@ plus2.4\p@ minus7.2\p@
+ \belowdisplayskip\abovedisplayskip
+ \abovedisplayshortskip\z@ plus2.4\p@
+ \belowdisplayshortskip5.6\p@ plus2.4\p@ minus3.2\p@
+ \textonlyfont@\rm\eightrm \textonlyfont@\it\eightit
+ \textonlyfont@\sl\eightsl \textonlyfont@\bf\eightbf
+ \textonlyfont@\smc\eightsmc \textonlyfont@\tt\eighttt
+ \ifsyntax@\def\big##1{{\hbox{$\left##1\right.$}}}%
+ \let\Big\big \let\bigg\big \let\Bigg\big
+ \else
+ \textfont\z@\eightrm \scriptfont\z@\sixrm
+ \scriptscriptfont\z@\fiverm
+ \textfont\@ne\eighti \scriptfont\@ne\sixi
+ \scriptscriptfont\@ne\fivei
+ \textfont\tw@\eightsy \scriptfont\tw@\sixsy
+ \scriptscriptfont\tw@\fivesy
+ \textfont\thr@@\eightex \scriptfont\thr@@\sevenex
+ \scriptscriptfont\thr@@\sevenex
+ \textfont\itfam\eightit \scriptfont\itfam\sevenit
+ \scriptscriptfont\itfam\sevenit
+ \textfont\bffam\eightbf \scriptfont\bffam\sixbf
+ \scriptscriptfont\bffam\fivebf
+ \setbox\strutbox\hbox{\vrule height7\p@ depth3\p@ width\z@}%
+ \setbox\strutbox@\hbox{\raise.5\normallineskiplimit\vbox{%
+ \kern-\normallineskiplimit\copy\strutbox}}%
+ \setbox\z@\vbox{\hbox{$($}\kern\z@}\bigsize@1.2\ht\z@
+ \fi
+ \normalbaselines\eightrm\dotsspace@1.5mu\ex@.2326ex\jot3\ex@
+ \the\eightpoint@}
+\message{page dimension settings,}
+\parindent1pc
+\newdimen\normalparindent \normalparindent\parindent
+\normallineskiplimit\p@
+\newdimen\indenti \indenti=2pc
+\def\pageheight#1{\vsize#1\relax}
+\def\pagewidth#1{\hsize#1%
+ \captionwidth@\hsize \advance\captionwidth@-2\indenti}
+\pagewidth{30pc} \pageheight{47pc}
+\let\magnification=\mag
+\message{top matter,}
+\def\topmatter{%
+ \ifx\undefined\msafam
+ \else\font@\eightmsa=msam8 \font@\sixmsa=msam6
+ \ifsyntax@\else \addto\tenpoint{\textfont\msafam\tenmsa
+ \scriptfont\msafam\sevenmsa \scriptscriptfont\msafam\fivemsa}%
+ \addto\eightpoint{\textfont\msafam\eightmsa
+ \scriptfont\msafam\sixmsa \scriptscriptfont\msafam\fivemsa}%
+ \fi
+ \fi
+ \ifx\undefined\msbfam
+ \else\font@\eightmsb=msbm8 \font@\sixmsb=msbm6
+ \ifsyntax@\else \addto\tenpoint{\textfont\msbfam\tenmsb
+ \scriptfont\msbfam\sevenmsb \scriptscriptfont\msbfam\fivemsb}%
+ \addto\eightpoint{\textfont\msbfam\eightmsb
+ \scriptfont\msbfam\sixmsb \scriptscriptfont\msbfam\fivemsb}%
+ \fi
+ \fi
+ \ifx\undefined\eufmfam
+ \else \font@\eighteufm=eufm8 \font@\sixeufm=eufm6
+ \ifsyntax@\else \addto\tenpoint{\textfont\eufmfam\teneufm
+ \scriptfont\eufmfam\seveneufm
+ \scriptscriptfont\eufmfam\fiveeufm}%
+ \addto\eightpoint{\textfont\eufmfam\eighteufm
+ \scriptfont\eufmfam\sixeufm
+ \scriptscriptfont\eufmfam\fiveeufm}%
+ \fi
+ \fi
+ \ifx\undefined\eufbfam
+ \else \font@\eighteufb=eufb8 \font@\sixeufb=eufb6
+ \ifsyntax@\else \addto\tenpoint{\textfont\eufbfam\teneufb
+ \scriptfont\eufbfam\seveneufb
+ \scriptscriptfont\eufbfam\fiveeufb}%
+ \addto\eightpoint{\textfont\eufbfam\eighteufb
+ \scriptfont\eufbfam\sixeufb
+ \scriptscriptfont\eufbfam\fiveeufb}%
+ \fi
+ \fi
+ \ifx\undefined\eusmfam
+ \else \font@\eighteusm=eusm8 \font@\sixeusm=eusm6
+ \ifsyntax@\else \addto\tenpoint{\textfont\eusmfam\teneusm
+ \scriptfont\eusmfam\seveneusm
+ \scriptscriptfont\eusmfam\fiveeusm}%
+ \addto\eightpoint{\textfont\eusmfam\eighteusm
+ \scriptfont\eusmfam\sixeusm
+ \scriptscriptfont\eusmfam\fiveeusm}%
+ \fi
+ \fi
+ \ifx\undefined\eusbfam
+ \else \font@\eighteusb=eusb8 \font@\sixeusb=eusb6
+ \ifsyntax@\else \addto\tenpoint{\textfont\eusbfam\teneusb
+ \scriptfont\eusbfam\seveneusb
+ \scriptscriptfont\eusbfam\fiveeusb}%
+ \addto\eightpoint{\textfont\eusbfam\eighteusb
+ \scriptfont\eusbfam\sixeusb
+ \scriptscriptfont\eusbfam\fiveeusb}%
+ \fi
+ \fi
+ \ifx\undefined\eurmfam
+ \else \font@\eighteurm=eurm8 \font@\sixeurm=eurm6
+ \ifsyntax@\else \addto\tenpoint{\textfont\eurmfam\teneurm
+ \scriptfont\eurmfam\seveneurm
+ \scriptscriptfont\eurmfam\fiveeurm}%
+ \addto\eightpoint{\textfont\eurmfam\eighteurm
+ \scriptfont\eurmfam\sixeurm
+ \scriptscriptfont\eurmfam\fiveeurm}%
+ \fi
+ \fi
+ \ifx\undefined\eurbfam
+ \else \font@\eighteurb=eurb8 \font@\sixeurb=eurb6
+ \ifsyntax@\else \addto\tenpoint{\textfont\eurbfam\teneurb
+ \scriptfont\eurbfam\seveneurb
+ \scriptscriptfont\eurbfam\fiveeurb}%
+ \addto\eightpoint{\textfont\eurbfam\eighteurb
+ \scriptfont\eurbfam\sixeurb
+ \scriptscriptfont\eurbfam\fiveeurb}%
+ \fi
+ \fi
+ \ifx\undefined\cmmibfam
+ \else \font@\eightcmmib=cmmib8 \font@\sixcmmib=cmmib6
+ \ifsyntax@\else \addto\tenpoint{\textfont\cmmibfam\tencmmib
+ \scriptfont\cmmibfam\sevencmmib
+ \scriptscriptfont\cmmibfam\fivecmmib}%
+ \addto\eightpoint{\textfont\cmmibfam\eightcmmib
+ \scriptfont\cmmibfam\sixcmmib
+ \scriptscriptfont\cmmibfam\fivecmmib}%
+ \fi
+ \fi
+ \ifx\undefined\cmbsyfam
+ \else \font@\eightcmbsy=cmbsy8 \font@\sixcmbsy=cmbsy6
+ \ifsyntax@\else \addto\tenpoint{\textfont\cmbsyfam\tencmbsy
+ \scriptfont\cmbsyfam\sevencmbsy
+ \scriptscriptfont\cmbsyfam\fivecmbsy}%
+ \addto\eightpoint{\textfont\cmbsyfam\eightcmbsy
+ \scriptfont\cmbsyfam\sixcmbsy
+ \scriptscriptfont\cmbsyfam\fivecmbsy}%
+ \fi
+ \fi
+ \let\topmatter\relax}
+\def\chapterno@{\uppercase\expandafter{\romannumeral\chaptercount@}}
+\newcount\chaptercount@
+\def\chapter{\let\savedef@\chapter
+ \def\chapter##1{\let\chapter\savedef@
+ \leavevmode\hskip-\leftskip
+ \rlap{\vbox to\z@{\vss\centerline{\eightpoint
+ \frills@{CHAPTER\space\afterassignment\chapterno@
+ \global\chaptercount@=}%
+ ##1\unskip}\baselineskip2pc\null}}\hskip\leftskip}%
+ \nofrillscheck\chapter}
+\newbox\titlebox@
+\def\title{\let\savedef@\title
+ \def\title##1\endtitle{\let\title\savedef@
+ \global\setbox\titlebox@\vtop{\tenpoint\bf
+ \raggedcenter@
+ \baselineskip1.3\baselineskip
+ \frills@\uppercasetext@{##1}\endgraf}%
+ \ifmonograph@ \edef\next{\the\leftheadtoks}%
+ \ifx\next\empty
+ \leftheadtext{##1}\fi
+ \fi
+ \edef\next{\the\rightheadtoks}\ifx\next\empty \rightheadtext{##1}\fi
+ }%
+ \nofrillscheck\title}
+\newbox\authorbox@
+\def\author#1\endauthor{\global\setbox\authorbox@
+ \vbox{\tenpoint\smc\raggedcenter@
+ #1\endgraf}\relaxnext@ \edef\next{\the\leftheadtoks}%
+ \ifx\next\empty\leftheadtext{#1}\fi}
+\newbox\affilbox@
+\def\affil#1\endaffil{\global\setbox\affilbox@
+ \vbox{\tenpoint\raggedcenter@#1\endgraf}}
+\newcount\addresscount@
+\addresscount@\z@
+\def\address#1\endaddress{\global\advance\addresscount@\@ne
+ \expandafter\gdef\csname address\number\addresscount@\endcsname
+ {\nobreak\vskip12\p@ minus6\p@\indent\eightpoint\smc#1\par}}
+\def\curraddr{\let\savedef@\curraddr
+ \def\curraddr##1\endcurraddr{\let\curraddr\savedef@
+ \toks@\expandafter\expandafter\expandafter{%
+ \csname address\number\addresscount@\endcsname}%
+ \toks@@{##1}%
+ \expandafter\xdef\csname address\number\addresscount@\endcsname
+ {\the\toks@\endgraf\noexpand\nobreak
+ \indent{\noexpand\rm
+ \frills@{{\noexpand\it Current address\noexpand\/}:\space}%
+ \def\noexpand\usualspace{\space}\the\toks@@\unskip}}}%
+ \nofrillscheck\curraddr}
+\def\email{\let\savedef@\email
+ \def\email##1\endemail{\let\email\savedef@
+ \toks@{\def\usualspace{{\it\enspace}}\endgraf\indent\eightpoint}%
+ \toks@@{##1\par}%
+ \expandafter\xdef\csname email\number\addresscount@\endcsname
+ {\the\toks@\frills@{{\noexpand\it E-mail address\noexpand\/}:%
+ \noexpand\enspace}\the\toks@@}}%
+ \nofrillscheck\email}
+\def\thedate@{}
+\def\date#1\enddate{\gdef\thedate@{\tenpoint#1\unskip}}
+\def\thethanks@{}
+\def\thanks#1\endthanks{%
+ \ifx\thethanks@\empty \gdef\thethanks@{\eightpoint#1}%
+ \else
+ \expandafter\gdef\expandafter\thethanks@\expandafter{%
+ \thethanks@\endgraf#1}%
+ \fi}
+\def\thekeywords@{}
+\def\keywords{\let\savedef@\keywords
+ \def\keywords##1\endkeywords{\let\keywords\savedef@
+ \toks@{\def\usualspace{{\it\enspace}}\eightpoint}%
+ \toks@@{##1\unskip.}%
+ \edef\thekeywords@{\the\toks@\frills@{{\noexpand\it
+ Key words and phrases.\noexpand\enspace}}\the\toks@@}}%
+ \nofrillscheck\keywords}
+\def\thesubjclass@{}
+\def\subjclass{\let\savedef@\subjclass
+ \def\subjclass##1\endsubjclass{\let\subjclass\savedef@
+ \toks@{\def\usualspace{{\rm\enspace}}\eightpoint}%
+ \toks@@{##1\unskip.}%
+ \edef\thesubjclass@{\the\toks@
+ \frills@{{\noexpand\rm1991 {\noexpand\it Mathematics Subject
+ Classification}.\noexpand\enspace}}%
+ \the\toks@@}}%
+ \nofrillscheck\subjclass}
+\newbox\abstractbox@
+\def\abstract{\let\savedef@\abstract
+ \def\abstract{\let\abstract\savedef@
+ \setbox\abstractbox@\vbox\bgroup\noindent$$\vbox\bgroup
+ \def\envir@end{\endabstract}\advance\hsize-2\indenti
+ \def\usualspace{\enspace}\eightpoint \noindent
+ \frills@{{\smc Abstract.\enspace}}}%
+ \nofrillscheck\abstract}
+\def\endabstract{\par\unskip\egroup$$\egroup}
+\def\widestnumber{\begingroup \let\head\relax\let\subhead\relax
+ \let\subsubhead\relax \expandafter\endgroup\setwidest@}
+\def\setwidest@#1#2{%
+ \ifx#1\head\setbox\tocheadbox@\hbox{#2.\enspace}%
+ \else\ifx#1\subhead\setbox\tocsubheadbox@\hbox{#2.\enspace}%
+ \else\ifx#1\subsubhead\setbox\tocsubheadbox@\hbox{#2.\enspace}%
+ \else\ifx#1\key\refstyle A%
+ \setboxz@h{\refsfont@\keyformat{#2}}%
+ \refindentwd\wd\z@
+ \else\ifx#1\no\refstyle C%
+ \setboxz@h{\refsfont@\keyformat{#2}}%
+ \refindentwd\wd\z@
+ \else\ifx#1\page\setbox\z@\hbox{\quad\bf#2}%
+ \pagenumwd\wd\z@
+ \else\ifx#1\item
+ \edef\next@{\the\revert@\rosteritemwd\the\rosteritemwd\relax
+ \revert@{\the\revert@}}%
+ \revert@\expandafter{\next@}%
+ \setboxz@h{(#2)}\rosteritemwd\wdz@
+ \else\message{\string\widestnumber\space not defined for this
+ option (\string#1)}%
+\fi\fi\fi\fi\fi\fi\fi}
+\newif\ifmonograph@
+\def\Monograph{\monograph@true \let\headmark\rightheadtext
+ \let\varindent@\indent \def\headfont@{\bf}\def\proclaimheadfont@{\smc}%
+ \def\remarkheadfont@{\smc}}
+\let\varindent@\noindent
+\newbox\tocheadbox@ \newbox\tocsubheadbox@
+\newbox\tocbox@
+\newdimen\pagenumwd
+\def\toc{\toc@{Contents}}
+\def\newtocdefs{%
+ \def \title##1\endtitle
+ {\penaltyandskip@\z@\smallskipamount
+ \hangindent\wd\tocheadbox@\noindent{\bf##1}}%
+ \def \chapter##1{%
+ Chapter \uppercase\expandafter{%
+ \romannumeral##1.\unskip}\enspace}%
+ \def \specialhead##1\endspecialhead
+ {\par\hangindent\wd\tocheadbox@ \noindent##1\par}%
+ \def \head##1 ##2\endhead
+ {\par\hangindent\wd\tocheadbox@ \noindent
+ \if\notempty{##1}\hbox to\wd\tocheadbox@{\hfil##1\enspace}\fi
+ ##2\par}%
+ \def \subhead##1 ##2\endsubhead
+ {\par\vskip-\parskip {\normalbaselines
+ \advance\leftskip\wd\tocheadbox@
+ \hangindent\wd\tocsubheadbox@ \noindent
+ \if\notempty{##1}%
+ \hbox to\wd\tocsubheadbox@{##1\unskip\hfil}\fi
+ ##2\par}}%
+ \def \subsubhead##1 ##2\endsubsubhead
+ {\par\vskip-\parskip {\normalbaselines
+ \advance\leftskip\wd\tocheadbox@
+ \hangindent\wd\tocsubheadbox@ \noindent
+ \if\notempty{##1}%
+ \hbox to\wd\tocsubheadbox@{##1\unskip\hfil}\fi
+ ##2\par}}}
+\def\toc@#1{\relaxnext@
+ \DN@{\ifx\next\nofrills\DN@\nofrills{\nextii@}%
+ \else\DN@{\nextii@{{#1}}}\fi
+ \next@}%
+ \DNii@##1{%
+\ifmonograph@\bgroup\else\setbox\tocbox@\vbox\bgroup
+ \centerline{\headfont@\ignorespaces##1\unskip}\nobreak
+ \vskip\belowheadskip \fi
+ \def\page####1%
+ {\unskip\penalty\z@\null\hfil
+ \rlap{\hbox to\pagenumwd{\quad\hfil####1}}%
+ \hfilneg\penalty\@M}%
+ \setbox\tocheadbox@\hbox{0.\enspace}%
+ \setbox\tocsubheadbox@\hbox{0.0.\enspace}%
+ \leftskip\indenti \rightskip\leftskip
+ \setboxz@h{\bf\quad000}\pagenumwd\wd\z@
+ \advance\rightskip\pagenumwd
+ \newtocdefs
+ }%
+ \FN@\next@}
+\def\endtoc{\par\egroup}
+\let\pretitle\relax
+\let\preauthor\relax
+\let\preaffil\relax
+\let\predate\relax
+\let\preabstract\relax
+\let\prepaper\relax
+\def\dedicatory #1\enddedicatory{\def\preabstract{{\medskip
+ \eightpoint\it \raggedcenter@#1\endgraf}}}
+\def\thetranslator@{}
+\def\translator{%
+ \let\savedef@\translator
+ \def\translator##1\endtranslator{\let\translator\savedef@
+ \edef\thetranslator@{\noexpand\nobreak\noexpand\medskip
+ \noexpand\line{\noexpand\eightpoint\hfil
+ \frills@{Translated by \uppercase}{##1}\qquad\qquad}%
+ \noexpand\nobreak}}%
+ \nofrillscheck\translator}
+\outer\def\endtopmatter{\add@missing\endabstract
+ \edef\next{\the\leftheadtoks}\ifx\next\empty
+ \expandafter\leftheadtext\expandafter{\the\rightheadtoks}\fi
+ \ifmonograph@\else
+ \ifx\thesubjclass@\empty\else \makefootnote@{}{\thesubjclass@}\fi
+ \ifx\thekeywords@\empty\else \makefootnote@{}{\thekeywords@}\fi
+ \ifx\thethanks@\empty\else \makefootnote@{}{\thethanks@}\fi
+ \fi
+ \pretitle
+ \begingroup % to localize variant topskip
+ \ifmonograph@ \topskip7pc \else \topskip4pc \fi
+ \box\titlebox@
+ \endgroup
+ \preauthor
+ \ifvoid\authorbox@\else \vskip2.5pcplus1pc\unvbox\authorbox@\fi
+ \preaffil
+ \ifvoid\affilbox@\else \vskip1pcplus.5pc\unvbox\affilbox@\fi
+ \predate
+ \ifx\thedate@\empty\else
+ \vskip1pcplus.5pc\line{\hfil\thedate@\hfil}\fi
+ \preabstract
+ \ifvoid\abstractbox@\else
+ \vskip1.5pcplus.5pc\unvbox\abstractbox@ \fi
+ \ifvoid\tocbox@\else\vskip1.5pcplus.5pc\unvbox\tocbox@\fi
+ \prepaper
+ \vskip2pcplus1pc\relax
+}
+\def\document{\let\fontlist@\relax\let\alloclist@\relax
+ \tenpoint}
+\message{section heads,}
+\newskip\aboveheadskip \aboveheadskip\bigskipamount
+\newdimen\belowheadskip \belowheadskip6\p@
+\def\headfont@{\smc}
+\def\penaltyandskip@#1#2{\par\skip@#2\relax
+ \ifdim\lastskip<\skip@\relax\removelastskip
+ \ifnum#1=\z@\else\penalty@#1\relax\fi\vskip\skip@
+ \else\ifnum#1=\z@\else\penalty@#1\relax\fi\fi}
+\def\nobreak{\penalty\@M
+ \ifvmode\gdef\penalty@{\global\let\penalty@\penalty\count@@@}%
+ \everypar{\global\let\penalty@\penalty\everypar{}}\fi}
+\let\penalty@\penalty
+\def\heading#1\endheading{\head#1\endhead}
+\def\subheading{\DN@{\ifx\next\nofrills
+ \expandafter\subheading@
+ \else \expandafter\subheading@\expandafter\empty
+ \fi}%
+ \FN@\next@
+}
+\def\subheading@#1#2{\subhead#1#2\endsubhead}
+\def\specialheadfont@{\bf}
+\outer\def\specialhead{%
+ \add@missing\endroster \add@missing\enddefinition
+ \add@missing\enddemo \add@missing\endexample
+ \add@missing\endproclaim
+ \penaltyandskip@{-200}\aboveheadskip
+ \begingroup\interlinepenalty\@M\rightskip\z@ plus\hsize
+ \let\\\linebreak
+ \specialheadfont@\noindent}
+\def\endspecialhead{\par\endgroup\nobreak\vskip\belowheadskip}
+\outer\def\head#1\endhead{%
+ \add@missing\endroster \add@missing\enddefinition
+ \add@missing\enddemo \add@missing\endexample
+ \add@missing\endproclaim
+ \penaltyandskip@{-200}\aboveheadskip
+ {\headfont@\raggedcenter@\interlinepenalty\@M
+ #1\endgraf}\headmark{#1}%
+ \nobreak
+ \vskip\belowheadskip}
+\let\headmark\eat@
+\def\restoredef@#1{\relax\let#1\savedef@\let\savedef@\relax}
+\newskip\subheadskip \subheadskip\medskipamount
+\def\subheadfont@{\bf}
+\outer\def\subhead{%
+ \add@missing\endroster \add@missing\enddefinition
+ \add@missing\enddemo \add@missing\endexample
+ \add@missing\endproclaim
+ \let\savedef@\subhead \let\subhead\relax
+ \def\subhead##1\endsubhead{\restoredef@\subhead
+ \penaltyandskip@{-100}\subheadskip
+ \varindent@{\def\usualspace{{\subheadfont@\enspace}}%
+ \subheadfont@\ignorespaces##1\unskip\frills@{.\enspace}}%
+ \ignorespaces}%
+ \nofrillscheck\subhead}
+\outer\def\subsubhead{%
+ \add@missing\endroster \add@missing\enddefinition
+ \add@missing\enddemo
+ \add@missing\endexample \add@missing\endproclaim
+ \let\savedef@\subsubhead \let\subsubhead\relax
+ \def\subsubhead##1\endsubsubhead{\restoredef@\subsubhead
+ \penaltyandskip@{-50}\medskipamount
+ {\def\usualspace{\/{\it\enspace}}%
+ \it##1\unskip\frills@{.\enspace}}}%
+ \nofrillscheck\subsubhead}
+\message{theorems/proofs/definitions/remarks,}
+\def\proclaimheadfont@{\bf}
+\outer\def\proclaim{%
+ \let\savedef@\proclaim \let\proclaim\relax
+ \add@missing\endroster \add@missing\enddefinition
+ \add@missing\endproclaim \envir@stack\endproclaim
+ \def\proclaim##1{\restoredef@\proclaim
+ \penaltyandskip@{-100}\medskipamount\varindent@
+ \def\usualspace{{\proclaimheadfont@\enspace}}\proclaimheadfont@
+ \ignorespaces##1\unskip\frills@{.\enspace}%
+ \sl\ignorespaces}%
+ \nofrillscheck\proclaim}
+\def\endproclaim{\revert@envir\endproclaim \par\rm
+ \penaltyandskip@{55}\medskipamount}
+\def\remarkheadfont@{\it}
+\def\remark{\let\savedef@\remark \let\remark\relax
+ \add@missing\endroster \add@missing\endproclaim
+ \envir@stack\endremark
+ \def\remark##1{\restoredef@\remark
+ \penaltyandskip@\z@\medskipamount
+ {\def\usualspace{{\remarkheadfont@\enspace}}%
+ \varindent@\remarkheadfont@\ignorespaces##1\unskip%
+ \frills@{.\enspace}}\rm
+ \ignorespaces}\nofrillscheck\remark}
+\def\endremark{\par\revert@envir\endremark}
+\ifx\undefined\square
+ \def\square{\vrule width.6em height.5em depth.1em\relax}\fi
+\def\qed{\ifhmode\unskip\nobreak\fi\quad
+ \ifmmode\square\else$\m@th\square$\fi}
+\def\demo{\DN@{\ifx\next\nofrills
+ \DN@####1####2{\remark####1{####2}\envir@stack\enddemo
+ \ignorespaces}%
+ \else
+ \DN@####1{\remark{####1}\envir@stack\enddemo\ignorespaces}%
+ \fi
+ \next@}%
+\FN@\next@}
+
+\def\enddemo{\par\revert@envir\enddemo \endremark\medskip}
+\def\definition{\let\savedef@\definition \let\definition\relax
+ \add@missing\endproclaim \add@missing\endroster
+ \add@missing\enddefinition \envir@stack\enddefinition
+ \def\definition##1{\restoredef@\definition
+ \penaltyandskip@{-100}\medskipamount
+ {\def\usualspace{{\proclaimheadfont@\enspace}}%
+ \varindent@\proclaimheadfont@\ignorespaces##1\unskip
+ \frills@{.\proclaimheadfont@\enspace}}%
+ \rm \ignorespaces}%
+ \nofrillscheck\definition}
+\def\enddefinition{\revert@envir\enddefinition
+ \par\medskip}
+\def\example{\DN@{\ifx\next\nofrills
+ \DN@####1####2{\definition####1{####2}\envir@stack\endexample
+ \ignorespaces}%
+ \else
+ \DN@####1{\definition{####1}\envir@stack\endexample\ignorespaces}%
+ \fi
+ \next@}%
+\FN@\next@}
+\def\endexample{\revert@envir\endexample \enddefinition }
+\message{rosters,}
+\newdimen\rosteritemwd
+\rosteritemwd16pt % approximately the width of (iii) in 10 point text
+\newcount\rostercount@
+\newif\iffirstitem@
+\let\plainitem@\item
+\newtoks\everypartoks@
+\def\par@{\everypartoks@\expandafter{\the\everypar}\everypar{}}
+\def\leftskip@{}
+\def\roster{%
+ \envir@stack\endroster
+ \edef\leftskip@{\leftskip\the\leftskip}%
+ \relaxnext@
+ \rostercount@\z@% Initialize \rostercount@ to 0.
+ \def\item{\FN@\rosteritem@}% \item, now redefined, has
+ \DN@{\ifx\next\runinitem\let\next@\nextii@\else
+ \let\next@\nextiii@\fi\next@}%
+ \DNii@\runinitem% If \runinitem occurs, \nextii@ must kill it off.
+ {\unskip% This unskips any space before the original \roster.
+ \DN@{\ifx\next[\let\next@\nextii@\else
+ \ifx\next"\let\next@\nextiii@\else\let\next@\nextiv@\fi\fi\next@}%
+ \DNii@[####1]{\rostercount@####1\relax
+ \enspace\therosteritem{\number\rostercount@}~\ignorespaces}%
+ \def\nextiii@"####1"{\enspace{\rm####1}~\ignorespaces}%
+ \def\nextiv@{\enspace\therosteritem1\rostercount@\@ne~}%
+ \par@\firstitem@false% Before doing any of this we still change
+ \FN@\next@}% End of definition of \nextii@\runinitem.
+ \def\nextiii@{\par\par@% End the present paragraph, change \everypar
+ \penalty\@m\smallskip\vskip-\parskip
+ \firstitem@true}
+ \FN@\next@}
+\def\rosteritem@{\iffirstitem@\firstitem@false
+ \else\par\vskip-\parskip\fi
+ \leftskip\rosteritemwd \advance\leftskip\normalparindent
+ \advance\leftskip.5em \noindent
+ \DNii@[##1]{\rostercount@##1\relax\itembox@}%
+ \def\nextiii@"##1"{\def\therosteritem@{\rm##1}\itembox@}%
+ \def\nextiv@{\advance\rostercount@\@ne\itembox@}%
+ \def\therosteritem@{\therosteritem{\number\rostercount@}}%
+ \ifx\next[\let\next@\nextii@\else\ifx\next"\let\next@\nextiii@\else
+ \let\next@\nextiv@\fi\fi\next@}
+\def\itembox@{\llap{\hbox to\rosteritemwd{\hss
+ \kern\z@ % kern to thwart \unskip in \rom
+ \therosteritem@}\enspace}\ignorespaces}
+\def\therosteritem#1{\rom{(\ignorespaces#1\unskip)}}
+\newif\ifnextRunin@
+\def\endroster{\relaxnext@
+ \revert@envir\endroster % restore \envir@end
+ \par\leftskip@% End the paragraph, and restore the \leftskip.
+ \global\rosteritemwd16\p@ % restore default value
+ \penalty-50 \vskip-\parskip\smallskip% Add a good break and
+ \DN@{\ifx\next\Runinitem\let\next@\relax
+ \else\nextRunin@false\let\item\plainitem@% Otherwise, set
+ \ifx\next\par% moreover, if \endroster is followed by \par,
+ \DN@\par{\everypar\expandafter{\the\everypartoks@}}%
+ \else% but if the \endroster isn't followed by a new paragraph,
+ \DN@{\noindent\everypar\expandafter{\the\everypartoks@}}%
+ \fi\fi\next@}%
+ \FN@\next@}
+\newcount\rosterhangafter@
+\def\Runinitem#1\roster\runinitem{\relaxnext@
+ \envir@stack\endroster
+ \rostercount@\z@
+ \def\item{\FN@\rosteritem@}%
+ \def\runinitem@{#1}%
+ \DN@{\ifx\next[\let\next\nextii@\else\ifx\next"\let\next\nextiii@
+ \else\let\next\nextiv@\fi\fi\next}%
+ \DNii@[##1]{\rostercount@##1\relax
+ \def\item@{\therosteritem{\number\rostercount@}}\nextv@}%
+ \def\nextiii@"##1"{\def\item@{{\rm##1}}\nextv@}%
+ \def\nextiv@{\advance\rostercount@\@ne
+ \def\item@{\therosteritem{\number\rostercount@}}\nextv@}%
+ \def\nextv@{\setbox\z@\vbox
+ {\ifnextRunin@\noindent\fi
+ \runinitem@\unskip\enspace\item@~\par
+ \global\rosterhangafter@\prevgraf}%
+ \firstitem@false% Set \firstitem@false for future \item's.
+ \ifnextRunin@\else\par\fi
+ \hangafter\rosterhangafter@\hangindent3\normalparindent
+ \ifnextRunin@\noindent\fi
+ \runinitem@\unskip\enspace% Put in all the stored stuff
+ \item@~\ifnextRunin@\else\par@\fi% and the \item@, and
+ \nextRunin@true\ignorespaces}%% Here's where we set \nextRunin@true.
+ \FN@\next@}
+\message{footnotes,}
+\def\footmarkform@#1{$\m@th^{#1}$}
+\let\thefootnotemark\footmarkform@
+\def\makefootnote@#1#2{\insert\footins
+ {\interlinepenalty\interfootnotelinepenalty
+ \eightpoint\splittopskip\ht\strutbox\splitmaxdepth\dp\strutbox
+ \floatingpenalty\@MM\leftskip\z@\rightskip\z@
+ \spaceskip\z@\xspaceskip\z@
+ \leavevmode{#1}\footstrut\ignorespaces#2\unskip\lower\dp\strutbox
+ \vbox to\dp\strutbox{}}}
+\newcount\footmarkcount@
+\footmarkcount@\z@
+\def\footnotemark{\let\@sf\empty\relaxnext@
+ \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
+ \DN@{\ifx[\next\let\next@\nextii@\else
+ \ifx"\next\let\next@\nextiii@\else
+ \let\next@\nextiv@\fi\fi\next@}%
+ \DNii@[##1]{\footmarkform@{##1}\@sf}%
+ \def\nextiii@"##1"{{##1}\@sf}%
+ \def\nextiv@{\iffirstchoice@\global\advance\footmarkcount@\@ne\fi
+ \footmarkform@{\number\footmarkcount@}\@sf}%
+ \FN@\next@}
+\def\footnotetext{\relaxnext@
+ \DN@{\ifx[\next\let\next@\nextii@\else
+ \ifx"\next\let\next@\nextiii@\else
+ \let\next@\nextiv@\fi\fi\next@}%
+ \DNii@[##1]##2{\makefootnote@{\footmarkform@{##1}}{##2}}%
+ \def\nextiii@"##1"##2{\makefootnote@{##1}{##2}}%
+ \def\nextiv@##1{\makefootnote@{\footmarkform@%
+ {\number\footmarkcount@}}{##1}}%
+ \FN@\next@}
+\def\footnote{\let\@sf\empty\relaxnext@
+ \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
+ \DN@{\ifx[\next\let\next@\nextii@\else
+ \ifx"\next\let\next@\nextiii@\else
+ \let\next@\nextiv@\fi\fi\next@}%
+ \DNii@[##1]##2{\footnotemark[##1]\footnotetext[##1]{##2}}%
+ \def\nextiii@"##1"##2{\footnotemark"##1"\footnotetext"##1"{##2}}%
+ \def\nextiv@##1{\footnotemark\footnotetext{##1}}%
+ \FN@\next@}
+\def\adjustfootnotemark#1{\advance\footmarkcount@#1\relax}
+\def\footnoterule{\kern-3\p@
+ \hrule width5pc\kern 2.6\p@}% the \hrule is .4pt high
+\message{figures and captions,}
+\def\captionfont@{\smc}
+\def\topcaption#1#2\endcaption{%
+ {\dimen@\hsize \advance\dimen@-\captionwidth@
+ \rm\raggedcenter@ \advance\leftskip.5\dimen@ \rightskip\leftskip
+ {\captionfont@#1}%
+ \if\notempty{#2}.\enspace\ignorespaces#2\fi
+ \endgraf}\nobreak\bigskip}
+\def\botcaption#1#2\endcaption{%
+ \nobreak\bigskip
+ \setboxz@h{\captionfont@#1\if\notempty{#2}.\enspace\rm#2\fi}%
+ {\dimen@\hsize \advance\dimen@-\captionwidth@
+ \leftskip.5\dimen@ \rightskip\leftskip
+ \noindent \ifdim\wdz@>\captionwidth@
+ \else\hfil\fi
+ {\captionfont@#1}%
+ \if\notempty{#2}.\enspace\rm#2\fi\endgraf}}
+\def\@ins{\par\begingroup\def\vspace##1{\vskip##1\relax}%
+ \def\captionwidth##1{\captionwidth@##1\relax}%
+ \setbox\z@\vbox\bgroup} % start a \vbox
+\message{miscellaneous,}
+\def\block{\RIfMIfI@\nondmatherr@\block\fi
+ \else\ifvmode\noindent$$\predisplaysize\hsize
+ \else$$\fi
+ \def\endblock{\par\egroup$$}\fi
+ \vbox\bgroup\advance\hsize-2\indenti\noindent}
+\def\endblock{\par\egroup}
+\def\cite#1{\rom{[{\citefont@\m@th#1}]}}
+\def\citefont@{\rm}
+\def\rom#1{\leavevmode\skip@\lastskip\unskip\/%
+ \ifdim\skip@=\z@\else\hskip\skip@\fi{\rm#1}}
+\message{references,}
+\def\refsfont@{\eightpoint}
+\newdimen\refindentwd
+\setboxz@h{\refsfont@ 00.\enspace}
+\refindentwd\wdz@
+\outer\def\Refs{\add@missing\endroster \add@missing\endproclaim
+ \let\savedef@\Refs \let\Refs\relax % because of \outer-ness
+ \def\Refs##1{\restoredef@\Refs
+ \if\notempty{##1}\penaltyandskip@{-200}\aboveheadskip
+ \begingroup \raggedcenter@\headfont@
+ \ignorespaces##1\endgraf\endgroup
+ \penaltyandskip@\@M\belowheadskip
+ \fi
+ \begingroup\def\envir@end{\endRefs}\refsfont@\sfcode`\.\@m
+ }%
+ \nofrillscheck{\csname Refs\expandafter\endcsname
+ \frills@{{References}}}}
+\def\endRefs{\par % This will check for a missing \endref, also
+ \endgroup}
+\newif\ifbook@ \newif\ifprocpaper@
+\def\nofrills{%
+ \expandafter\ifx\envir@end\endref
+ \let\do\relax
+ \xdef\nofrills@list{\nofrills@list\do\curbox}%
+ \else\errmessage{\Invalid@@ \string\nofrills}%
+ \fi}%
+\def\defaultreftexts{\gdef\edtext{ed.}\gdef\pagestext{pp.}%
+ \gdef\voltext{vol.}\gdef\issuetext{no.}}
+\defaultreftexts
+\def\ref{\par
+ \begingroup \def\envir@end{\endref}%
+ \noindent\hangindent\refindentwd
+ \def\par{\add@missing\endref}%
+ \global\let\nofrills@list\empty
+ \refbreaks
+ \procpaper@false \book@false
+ \def\curbox{\z@}\setbox\z@\vbox\bgroup
+}
+\let\keyhook@\empty
+\def\endref{%
+ \setbox\tw@\box\thr@@
+ \makerefbox?\thr@@{\endgraf\egroup}%
+ \endref@
+ \endgraf
+ \endgroup
+ \keyhook@
+ \global\let\keyhook@\empty % \global to conserve save stack
+}
+\def\key{\gdef\key{\makerefbox\key\keybox@\empty}\key} \newbox\keybox@
+\def\no{\gdef\no{\makerefbox\no\keybox@\empty}%
+ \gdef\keyhook@{\refstyle C}\no}
+\def\by{\makerefbox\by\bybox@\empty} \newbox\bybox@
+\let\manyby\by % for backward compatibility
+\def\bysame{\by\hbox to3em{\hrulefill}\thinspace\kern\z@}
+\def\paper{\makerefbox\paper\paperbox@\it} \newbox\paperbox@
+\def\paperinfo{\makerefbox\paperinfo\paperinfobox@\empty}%
+ \newbox\paperinfobox@
+\def\jour{\makerefbox\jour\jourbox@
+ {\aftergroup\book@false \aftergroup\procpaper@false}} \newbox\jourbox@
+\def\issue{\makerefbox\issue\issuebox@\empty} \newbox\issuebox@
+\def\yr{\makerefbox\yr\yrbox@\empty} \newbox\yrbox@
+\def\pages{\makerefbox\pages\pagesbox@\empty} \newbox\pagesbox@
+\def\page{\gdef\pagestext{p.}\makerefbox\page\pagesbox@\empty}
+\def\ed{\makerefbox\ed\edbox@\empty} \newbox\edbox@
+\def\eds{\gdef\edtext{eds.}\makerefbox\eds\edbox@\empty}
+\def\book{\makerefbox\book\bookbox@
+ {\it\aftergroup\book@true \aftergroup\procpaper@false}}
+ \newbox\bookbox@
+\def\bookinfo{\makerefbox\bookinfo\bookinfobox@\empty}%
+ \newbox\bookinfobox@
+\def\publ{\makerefbox\publ\publbox@\empty} \newbox\publbox@
+\def\publaddr{\makerefbox\publaddr\publaddrbox@\empty}%
+ \newbox\publaddrbox@
+\def\inbook{\makerefbox\inbook\bookbox@
+ {\aftergroup\procpaper@true \aftergroup\book@false}}
+\def\procinfo{\makerefbox\procinfo\procinfobox@\empty}%
+ \newbox\procinfobox@
+\def\finalinfo{\makerefbox\finalinfo\finalinfobox@\empty}%
+ \newbox\finalinfobox@
+\def\miscnote{\makerefbox\miscnote\miscnotebox@\empty}%
+ \newbox\miscnotebox@
+\def\toappear{\miscnote to appear}
+\def\lang{\makerefbox\lang\langbox@\empty} \newbox\langbox@
+\newbox\morerefbox@
+\def\vol{\makerefbox\vol\volbox@{\ifbook@ \else
+ \ifprocpaper@\else\bf\fi\fi}}
+\newbox\volbox@
+\newbox\holdoverbox
+\def\makerefbox#1#2#3{\endgraf
+ \setbox\z@\lastbox
+ \global\setbox\@ne\hbox{\unhbox\holdoverbox
+ \ifvoid\z@\else\unhbox\z@\unskip\unskip\unpenalty\fi}%
+ \egroup
+ \setbox\curbox\box\ifdim\wd\@ne>\z@ \@ne \else\voidb@x\fi
+ \ifvoid#2\else\Err@{Redundant \string#1; duplicate use, or
+ mutually exclusive information already given}\fi
+ \def\curbox{#2}\setbox\curbox\vbox\bgroup \hsize\maxdimen \noindent
+ #3}
+\def\refbreaks{%
+ \def\refconcat##1{\setbox\z@\lastbox \setbox\holdoverbox\hbox{%
+ \unhbox\holdoverbox \unhbox\z@\unskip\unskip\unpenalty##1}}%
+ \def\holdover##1{%
+ \RIfM@
+ \penalty-\@M\null
+ \hfil$\clubpenalty\z@\widowpenalty\z@\interlinepenalty\z@
+ \offinterlineskip\endgraf
+ \setbox\z@\lastbox\unskip \unpenalty
+ \refconcat{##1}%
+ \noindent
+ $\hfil\penalty-\@M
+ \else
+ \endgraf\refconcat{##1}\noindent
+ \fi}%
+ \def\break{\holdover{\penalty-\@M}}%
+ \let\vadjust@\vadjust
+ \def\vadjust##1{\holdover{\vadjust@{##1}}}%
+ \def\newpage{\vadjust{\vfill\break}}%
+}
+\def\refstyle#1{\uppercase{%
+ \if#1A\relax \def\keyformat##1{[##1]\enspace\hfil}%
+ \else\if#1B\relax
+ \def\keyformat##1{\aftergroup\kern
+ \aftergroup-\aftergroup\refindentwd}%
+ \refindentwd\parindent
+ \else\if#1C\relax
+ \def\keyformat##1{\hfil##1.\enspace}%
+ \fi\fi\fi}% end of \uppercase
+}
+\refstyle{A}
+\def\finalpunct{\ifnum\lastkern=\m@ne\unkern\else.\fi
+ \refquotes@\refbreak@}%
+\def\continuepunct#1#2#3#4{}%
+\def\endref@{%
+ \keyhook@
+ \def\nofrillscheck##1{%
+ \def\do####1{\ifx##1####1\let\frills@\eat@\fi}%
+ \let\frills@\identity@ \nofrills@list}%
+ \ifvoid\bybox@
+ \ifvoid\edbox@
+ \else\setbox\bybox@\hbox{\unhbox\edbox@\breakcheck
+ \nofrillscheck\edbox@\frills@{\space(\edtext)}\refbreak@}\fi
+ \fi
+ \ifvoid\keybox@\else\hbox to\refindentwd{%
+ \keyformat{\unhbox\keybox@}}\fi
+ \commaunbox@\morerefbox@
+ \ppunbox@\empty\empty\bybox@\empty
+ \ifbook@ % Case 1: \book etc.
+ \commaunbox@\bookbox@ \commaunbox@\bookinfobox@
+ \ppunbox@\empty{ (}\procinfobox@)%
+ \ppunbox@,{ vol.~}\volbox@\empty
+ \ppunbox@\empty{ (}\edbox@{, \edtext)}%
+ \commaunbox@\publbox@ \commaunbox@\publaddrbox@
+ \commaunbox@\yrbox@
+ \ppunbox@,{ \pagestext~}\pagesbox@\empty
+ \else
+ \commaunbox@\paperbox@ \commaunbox@\paperinfobox@
+ \ifprocpaper@ % Case 2: \paper ... \inbook
+ \commaunbox@\bookbox@
+ \ppunbox@\empty{ (}\procinfobox@)%
+ \ppunbox@\empty{ (}\edbox@{, \edtext)}%
+ \commaunbox@\bookinfobox@
+ \ppunbox@,{ \voltext~}\volbox@\empty
+ \commaunbox@\publbox@ \commaunbox@\publaddrbox@
+ \commaunbox@\yrbox@
+ \ppunbox@,{ \pagestext~}\pagesbox@\empty
+ \else % Case 3: \paper ... \jour
+ \commaunbox@\jourbox@
+ \ppunbox@\empty{ }\volbox@\empty
+ \ppunbox@\empty{ (}\yrbox@)%
+ \ppunbox@,{ \issuetext~}\issuebox@\empty
+ \commaunbox@\publbox@ \commaunbox@\publaddrbox@
+ \commaunbox@\pagesbox@
+ \fi
+ \fi
+ \commaunbox@\finalinfobox@
+ \ppunbox@\empty{ (}\miscnotebox@)%
+ \finalpunct\ppunbox@\empty{ (}\langbox@)%
+ \defaultreftexts
+}
+\def\punct@#1{#1}
+\def\ppunbox@#1#2#3#4{\ifvoid#3\else
+ \ifnum\lastkern=\m@ne\unkern\let\punct@\eat@\fi
+ \nofrillscheck#3%
+ \punct@{#1}\refquotes@\refbreak@
+ \let\punct@\identity@
+ \frills@{#2\eat@}\space
+ \unhbox#3\breakcheck
+ \frills@{#4\eat@}{\kern\m@ne sp}\fi}
+\def\commaunbox@#1{\ppunbox@,\space{#1}\empty}
+\def\breakcheck{\edef\refbreak@{\ifnum\lastpenalty=\z@\else
+ \penalty\the\lastpenalty\relax\fi}\unpenalty}
+\def\endquotes{\def\refquotes@{''\let\refquotes@\empty}}
+\let\refquotes@\empty
+\let\refbreak@\empty
+\def\moreref{%
+ \setbox\tw@\box\thr@@
+ \makerefbox?\thr@@{\endgraf\egroup}%
+ \let\savedef@\finalpunct \let\finalpunct\empty
+ \endref@
+ \def\punct@##1##2{##2;}%
+ \global\let\nofrills@list\empty % global, to conserve save stack
+ \let\finalpunct\savedef@
+ \def\curbox{\morerefbox@}%
+ \setbox\morerefbox@\vbox\bgroup \hsize\maxdimen \noindent
+}
+\def\transl{\moreref}
+\message{end of document,}
+\outer\def\enddocument{\par% \par will do a runaway check for \endref
+ \add@missing\endRefs
+ \add@missing\endroster \add@missing\endproclaim
+ \add@missing\enddefinition
+ \add@missing\enddemo \add@missing\endremark \add@missing\endexample
+ \ifmonograph@ % do nothing
+ \else
+ \nobreak
+ \thetranslator@
+ \count@\z@ \loop\ifnum\count@<\addresscount@\advance\count@\@ne
+ \csname address\number\count@\endcsname
+ \csname email\number\count@\endcsname
+ \repeat
+\fi
+ \vfill\supereject\end}
+\message{output routine,}
+\def\folio{{\foliofont@\ifnum\pageno<\z@ \romannumeral-\pageno
+ \else\number\pageno \fi}}
+\def\foliofont@{\eightrm}
+\def\headlinefont@{\eightpoint}
+\def\leftheadline{\rlap{\folio}\hfill \iftrue\topmark\fi \hfill}
+\def\rightheadline{\hfill \expandafter\iffalse\botmark\fi
+ \hfill \llap{\folio}}
+\newtoks\leftheadtoks
+\newtoks\rightheadtoks
+\def\leftheadtext{\let\savedef@\leftheadtext
+ \def\leftheadtext##1{\let\leftheadtext\savedef@
+ \leftheadtoks\expandafter{\frills@\uppercasetext@{##1}}%
+ \mark{\the\leftheadtoks\noexpand\else\the\rightheadtoks}
+ \ifsyntax@\setboxz@h{\def\\{\unskip\space\ignorespaces}%
+ \headlinefont@##1}\fi}%
+ \nofrillscheck\leftheadtext}
+\def\rightheadtext{\let\savedef@\rightheadtext
+ \def\rightheadtext##1{\let\rightheadtext\savedef@
+ \rightheadtoks\expandafter{\frills@\uppercasetext@{##1}}%
+ \mark{\the\leftheadtoks\noexpand\else\the\rightheadtoks}%
+ \ifsyntax@\setboxz@h{\def\\{\unskip\space\ignorespaces}%
+ \headlinefont@##1}\fi}%
+ \nofrillscheck\rightheadtext}
+\headline={\def\chapter#1{\chapterno@. }%
+ \def\\{\unskip\space\ignorespaces}\headlinefont@
+ \ifodd\pageno \rightheadline \else \leftheadline\fi}
+\def\NoRunningHeads{\global\runheads@false\global\let\headmark\eat@}
+\def\NoPageNumbers{\gdef\folio{}}
+\def\logo@{\baselineskip2pc \hbox to\hsize{\hfil\eightpoint Typeset by
+ \AmSTeX}}
+\newif\iffirstpage@ \firstpage@true
+\newif\ifrunheads@ \runheads@true
+\output={\output@}
+\def\output@{\shipout\vbox{%
+ \iffirstpage@ \global\firstpage@false
+ \pagebody \logo@ \makefootline%
+ \else \ifrunheads@ \makeheadline \pagebody
+ \else \pagebody \makefootline \fi
+ \fi}%
+ \advancepageno \ifnum\outputpenalty>-\@MM\else\dosupereject\fi}
+\message{hyphenation exceptions (U.S. English)}
+\hyphenation{acad-e-my acad-e-mies af-ter-thought anom-aly anom-alies
+an-ti-deriv-a-tive an-tin-o-my an-tin-o-mies apoth-e-o-ses
+apoth-e-o-sis ap-pen-dix ar-che-typ-al as-sign-a-ble as-sist-ant-ship
+as-ymp-tot-ic asyn-chro-nous at-trib-uted at-trib-ut-able bank-rupt
+bank-rupt-cy bi-dif-fer-en-tial blue-print busier busiest
+cat-a-stroph-ic cat-a-stroph-i-cally con-gress cross-hatched data-base
+de-fin-i-tive de-riv-a-tive dis-trib-ute dri-ver dri-vers eco-nom-ics
+econ-o-mist elit-ist equi-vari-ant ex-quis-ite ex-tra-or-di-nary
+flow-chart for-mi-da-ble forth-right friv-o-lous ge-o-des-ic
+ge-o-det-ic geo-met-ric griev-ance griev-ous griev-ous-ly
+hexa-dec-i-mal ho-lo-no-my ho-mo-thetic ideals idio-syn-crasy
+in-fin-ite-ly in-fin-i-tes-i-mal ir-rev-o-ca-ble key-stroke
+lam-en-ta-ble light-weight mal-a-prop-ism man-u-script mar-gin-al
+meta-bol-ic me-tab-o-lism meta-lan-guage me-trop-o-lis
+met-ro-pol-i-tan mi-nut-est mol-e-cule mono-chrome mono-pole
+mo-nop-oly mono-spline mo-not-o-nous mul-ti-fac-eted mul-ti-plic-able
+non-euclid-ean non-iso-mor-phic non-smooth par-a-digm par-a-bol-ic
+pa-rab-o-loid pa-ram-e-trize para-mount pen-ta-gon phe-nom-e-non
+post-script pre-am-ble pro-ce-dur-al pro-hib-i-tive pro-hib-i-tive-ly
+pseu-do-dif-fer-en-tial pseu-do-fi-nite pseu-do-nym qua-drat-ic
+quad-ra-ture qua-si-smooth qua-si-sta-tion-ary qua-si-tri-an-gu-lar
+quin-tes-sence quin-tes-sen-tial re-arrange-ment rec-tan-gle
+ret-ri-bu-tion retro-fit retro-fit-ted right-eous right-eous-ness
+ro-bot ro-bot-ics sched-ul-ing se-mes-ter semi-def-i-nite
+semi-ho-mo-thet-ic set-up se-vere-ly side-step sov-er-eign spe-cious
+spher-oid spher-oid-al star-tling star-tling-ly sta-tis-tics
+sto-chas-tic straight-est strange-ness strat-a-gem strong-hold
+sum-ma-ble symp-to-matic syn-chro-nous topo-graph-i-cal tra-vers-a-ble
+tra-ver-sal tra-ver-sals treach-ery turn-around un-at-tached
+un-err-ing-ly white-space wide-spread wing-spread wretch-ed
+wretch-ed-ly Brown-ian Eng-lish Euler-ian Feb-ru-ary Gauss-ian
+Grothen-dieck Hamil-ton-ian Her-mit-ian Jan-u-ary Japan-ese Kor-te-weg
+Le-gendre Lip-schitz Lip-schitz-ian Mar-kov-ian Noe-ther-ian
+No-vem-ber Rie-mann-ian Schwarz-schild Sep-tem-ber}
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\tenpoint
+\W@{}
+\csname amsppt.sty\endcsname
+\endinput
+%%
+%% End of file `amsppt.sty'.
diff --git a/systems/knuth/local/lib/amssym.def b/systems/knuth/local/lib/amssym.def
new file mode 100644
index 0000000000..563047d0ba
--- /dev/null
+++ b/systems/knuth/local/lib/amssym.def
@@ -0,0 +1,108 @@
+%% @texfile{
+%% filename="amssym.def",
+%% version="2.1",
+%% date="5-APR-1991",
+%% filetype="TeX: option",
+%% copyright="Copyright (C) American Mathematical Society,
+%% all rights reserved. Copying of this file is
+%% authorized only if either:
+%% (1) you make absolutely no changes to your copy
+%% including name; OR
+%% (2) if you do make changes, you first rename it to some
+%% other name.",
+%% author="American Mathematical Society",
+%% address="American Mathematical Society,
+%% Technical Support Group,
+%% P. O. Box 6248,
+%% Providence, RI 02940,
+%% USA",
+%% telephone="401-455-4080 or (in the USA) 800-321-4AMS",
+%% email="Internet: Tech-Support@Math.AMS.com",
+%% codetable="ISO/ASCII",
+%% checksumtype="line count",
+%% checksum="108",
+%% keywords="amsfonts, tex",
+%% abstract="This file contains definitions that perform the same
+%% functions as similar ones in AMS-TeX, so that the file
+%% AMSSYM.TEX can be used outside of AMS-TeX. Instructions
+%% for using this file and the AMS symbol fonts are
+%% included in the AMSFonts 2.0 User's Guide."
+%% }
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+\expandafter\ifx\csname amssym.def\endcsname\relax \else\endinput\fi
+%
+% Store the catcode of the @ in the csname so that it can be restored later.
+\expandafter\edef\csname amssym.def\endcsname{%
+ \catcode`\noexpand\@=\the\catcode`\@\space}
+% Set the catcode to 11 for use in private control sequence names.
+\catcode`\@=11
+%
+% Include all definitions related to the fonts msam, msbm and eufm, so that
+% when this file is used by itself, the results with respect to those fonts
+% are equivalent to what they would have been using AMS-TeX.
+% Most symbols in fonts msam and msbm are defined using \newsymbol;
+% however, a few symbols that replace composites defined in plain must be
+% defined with \mathchardef.
+
+\def\undefine#1{\let#1\undefined}
+\def\newsymbol#1#2#3#4#5{\let\next@\relax
+ \ifnum#2=\@ne\let\next@\msafam@\else
+ \ifnum#2=\tw@\let\next@\msbfam@\fi\fi
+ \mathchardef#1="#3\next@#4#5}
+\def\mathhexbox@#1#2#3{\relax
+ \ifmmode\mathpalette{}{\m@th\mathchar"#1#2#3}%
+ \else\leavevmode\hbox{$\m@th\mathchar"#1#2#3$}\fi}
+\def\hexnumber@#1{\ifcase#1 0\or 1\or 2\or 3\or 4\or 5\or 6\or 7\or 8\or
+ 9\or A\or B\or C\or D\or E\or F\fi}
+
+\font\tenmsa=msam10
+\font\sevenmsa=msam7
+\font\fivemsa=msam5
+\newfam\msafam
+\textfont\msafam=\tenmsa
+\scriptfont\msafam=\sevenmsa
+\scriptscriptfont\msafam=\fivemsa
+\edef\msafam@{\hexnumber@\msafam}
+\mathchardef\dabar@"0\msafam@39
+\def\dashrightarrow{\mathrel{\dabar@\dabar@\mathchar"0\msafam@4B}}
+\def\dashleftarrow{\mathrel{\mathchar"0\msafam@4C\dabar@\dabar@}}
+\let\dasharrow\dashrightarrow
+\def\ulcorner{\delimiter"4\msafam@70\msafam@70 }
+\def\urcorner{\delimiter"5\msafam@71\msafam@71 }
+\def\llcorner{\delimiter"4\msafam@78\msafam@78 }
+\def\lrcorner{\delimiter"5\msafam@79\msafam@79 }
+\def\yen{{\mathhexbox@\msafam@55 }}
+\def\checkmark{{\mathhexbox@\msafam@58 }}
+\def\circledR{{\mathhexbox@\msafam@72 }}
+\def\maltese{{\mathhexbox@\msafam@7A }}
+
+\font\tenmsb=msbm10
+\font\sevenmsb=msbm7
+\font\fivemsb=msbm5
+\newfam\msbfam
+\textfont\msbfam=\tenmsb
+\scriptfont\msbfam=\sevenmsb
+\scriptscriptfont\msbfam=\fivemsb
+\edef\msbfam@{\hexnumber@\msbfam}
+\def\Bbb#1{{\fam\msbfam\relax#1}}
+\def\widehat#1{\setbox\z@\hbox{$\m@th#1$}%
+ \ifdim\wd\z@>\tw@ em\mathaccent"0\msbfam@5B{#1}%
+ \else\mathaccent"0362{#1}\fi}
+\def\widetilde#1{\setbox\z@\hbox{$\m@th#1$}%
+ \ifdim\wd\z@>\tw@ em\mathaccent"0\msbfam@5D{#1}%
+ \else\mathaccent"0365{#1}\fi}
+\font\teneufm=eufm10
+\font\seveneufm=eufm7
+\font\fiveeufm=eufm5
+\newfam\eufmfam
+\textfont\eufmfam=\teneufm
+\scriptfont\eufmfam=\seveneufm
+\scriptscriptfont\eufmfam=\fiveeufm
+\def\frak#1{{\fam\eufmfam\relax#1}}
+\let\goth\frak
+
+% Restore the catcode value for @ that was previously saved.
+\csname amssym.def\endcsname
+
+\endinput
diff --git a/systems/knuth/local/lib/amssym.tex b/systems/knuth/local/lib/amssym.tex
new file mode 100644
index 0000000000..3212baa5cd
--- /dev/null
+++ b/systems/knuth/local/lib/amssym.tex
@@ -0,0 +1,279 @@
+%% @texfile{
+%% filename="amssym.tex",
+%% version="2.1",
+%% date="31-MAY-1991",
+%% filetype="TeX: option",
+%% copyright="Copyright (C) American Mathematical Society,
+%% all rights reserved. Copying of this file is
+%% authorized only if either:
+%% (1) you make absolutely no changes to your copy
+%% including name; OR
+%% (2) if you do make changes, you first rename it to some
+%% other name.",
+%% author="American Mathematical Society",
+%% address="American Mathematical Society,
+%% Technical Support Group,
+%% P. O. Box 6248,
+%% Providence, RI 02940,
+%% USA",
+%% telephone="401-455-4080 or (in the USA) 800-321-4AMS",
+%% email="Internet: Tech-Support@Math.AMS.com",
+%% codetable="ISO/ASCII",
+%% checksumtype="line count",
+%% checksum="277",
+%% keywords="amsfonts, tex, amstex",
+%% abstract="This file defines names for all the math symbols in
+%% the math symbol fonts of the AMSFonts package (MSAM and
+%% MSBM). If this file is not used by way of the AMS-TeX
+%% \UseAMSsymbols command, it must be used in conjunction
+%% with AMSSYM.DEF, which provides a definition of the
+%% \newsymbol and \undefine commands. For more details,
+%% refer to the AMSFonts User's Guide."
+%% }
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+% Save the current value of the @-sign catcode so that it can be restored
+% afterwards. This allows us to call amssym.tex either within an AMS-TeX
+% document style file or by itself, in addition to providing a means
+% of testing whether the file has been previously loaded. We want to
+% avoid inputting this file twice because when AMSTeX is being used
+% \newsymbol will give an error message if used to define a control
+% sequence name that is already defined.
+
+% If the csname is not equal to \relax, we assume this file has already
+% been loaded and \endinput immediately.
+\expandafter\ifx\csname pre amssym.tex at\endcsname\relax \else \endinput\fi
+% Otherwise we store the catcode of the @ in the csname.
+\expandafter\chardef\csname pre amssym.tex at\endcsname=\the\catcode`\@
+% Set the catcode to 11 for use in private control sequence names.
+\catcode`\@=11
+\ifx\undefined\newsymbol \else \begingroup\def\input#1 {\endgroup}\fi
+\input amssym.def \relax
+% Most symbols in fonts msam and msbm are defined using \newsymbol. A few
+% that are delimiters or otherwise require special treatment have already
+% been defined as soon as the fonts were loaded. Finally, a few symbols
+% that replace composites defined in plain must be undefined first.
+\newsymbol\boxdot 1200
+\newsymbol\boxplus 1201
+\newsymbol\boxtimes 1202
+\newsymbol\square 1003
+\newsymbol\blacksquare 1004
+\newsymbol\centerdot 1205
+\newsymbol\lozenge 1006
+\newsymbol\blacklozenge 1007
+\newsymbol\circlearrowright 1308
+\newsymbol\circlearrowleft 1309
+\undefine\rightleftharpoons
+\newsymbol\rightleftharpoons 130A
+\newsymbol\leftrightharpoons 130B
+\newsymbol\boxminus 120C
+\newsymbol\Vdash 130D
+\newsymbol\Vvdash 130E
+\newsymbol\vDash 130F
+\newsymbol\twoheadrightarrow 1310
+\newsymbol\twoheadleftarrow 1311
+\newsymbol\leftleftarrows 1312
+\newsymbol\rightrightarrows 1313
+\newsymbol\upuparrows 1314
+\newsymbol\downdownarrows 1315
+\newsymbol\upharpoonright 1316
+ \let\restriction\upharpoonright
+\newsymbol\downharpoonright 1317
+\newsymbol\upharpoonleft 1318
+\newsymbol\downharpoonleft 1319
+\newsymbol\rightarrowtail 131A
+\newsymbol\leftarrowtail 131B
+\newsymbol\leftrightarrows 131C
+\newsymbol\rightleftarrows 131D
+\newsymbol\Lsh 131E
+\newsymbol\Rsh 131F
+\newsymbol\rightsquigarrow 1320
+\newsymbol\leftrightsquigarrow 1321
+\newsymbol\looparrowleft 1322
+\newsymbol\looparrowright 1323
+\newsymbol\circeq 1324
+\newsymbol\succsim 1325
+\newsymbol\gtrsim 1326
+\newsymbol\gtrapprox 1327
+\newsymbol\multimap 1328
+\newsymbol\therefore 1329
+\newsymbol\because 132A
+\newsymbol\doteqdot 132B
+ \let\Doteq\doteqdot
+\newsymbol\triangleq 132C
+\newsymbol\precsim 132D
+\newsymbol\lesssim 132E
+\newsymbol\lessapprox 132F
+\newsymbol\eqslantless 1330
+\newsymbol\eqslantgtr 1331
+\newsymbol\curlyeqprec 1332
+\newsymbol\curlyeqsucc 1333
+\newsymbol\preccurlyeq 1334
+\newsymbol\leqq 1335
+\newsymbol\leqslant 1336
+\newsymbol\lessgtr 1337
+\newsymbol\backprime 1038
+\newsymbol\risingdotseq 133A
+\newsymbol\fallingdotseq 133B
+\newsymbol\succcurlyeq 133C
+\newsymbol\geqq 133D
+\newsymbol\geqslant 133E
+\newsymbol\gtrless 133F
+\newsymbol\sqsubset 1340
+\newsymbol\sqsupset 1341
+\newsymbol\vartriangleright 1342
+\newsymbol\vartriangleleft 1343
+\newsymbol\trianglerighteq 1344
+\newsymbol\trianglelefteq 1345
+\newsymbol\bigstar 1046
+\newsymbol\between 1347
+\newsymbol\blacktriangledown 1048
+\newsymbol\blacktriangleright 1349
+\newsymbol\blacktriangleleft 134A
+\newsymbol\vartriangle 134D
+\newsymbol\blacktriangle 104E
+\newsymbol\triangledown 104F
+\newsymbol\eqcirc 1350
+\newsymbol\lesseqgtr 1351
+\newsymbol\gtreqless 1352
+\newsymbol\lesseqqgtr 1353
+\newsymbol\gtreqqless 1354
+\newsymbol\Rrightarrow 1356
+\newsymbol\Lleftarrow 1357
+\newsymbol\veebar 1259
+\newsymbol\barwedge 125A
+\newsymbol\doublebarwedge 125B
+\undefine\angle
+\newsymbol\angle 105C
+\newsymbol\measuredangle 105D
+\newsymbol\sphericalangle 105E
+\newsymbol\varpropto 135F
+\newsymbol\smallsmile 1360
+\newsymbol\smallfrown 1361
+\newsymbol\Subset 1362
+\newsymbol\Supset 1363
+\newsymbol\Cup 1264
+ \let\doublecup\Cup
+\newsymbol\Cap 1265
+ \let\doublecap\Cap
+\newsymbol\curlywedge 1266
+\newsymbol\curlyvee 1267
+\newsymbol\leftthreetimes 1268
+\newsymbol\rightthreetimes 1269
+\newsymbol\subseteqq 136A
+\newsymbol\supseteqq 136B
+\newsymbol\bumpeq 136C
+\newsymbol\Bumpeq 136D
+\newsymbol\lll 136E
+ \let\llless\lll
+\newsymbol\ggg 136F
+ \let\gggtr\ggg
+\newsymbol\circledS 1073
+\newsymbol\pitchfork 1374
+\newsymbol\dotplus 1275
+\newsymbol\backsim 1376
+\newsymbol\backsimeq 1377
+\newsymbol\complement 107B
+\newsymbol\intercal 127C
+\newsymbol\circledcirc 127D
+\newsymbol\circledast 127E
+\newsymbol\circleddash 127F
+\newsymbol\lvertneqq 2300
+\newsymbol\gvertneqq 2301
+\newsymbol\nleq 2302
+\newsymbol\ngeq 2303
+\newsymbol\nless 2304
+\newsymbol\ngtr 2305
+\newsymbol\nprec 2306
+\newsymbol\nsucc 2307
+\newsymbol\lneqq 2308
+\newsymbol\gneqq 2309
+\newsymbol\nleqslant 230A
+\newsymbol\ngeqslant 230B
+\newsymbol\lneq 230C
+\newsymbol\gneq 230D
+\newsymbol\npreceq 230E
+\newsymbol\nsucceq 230F
+\newsymbol\precnsim 2310
+\newsymbol\succnsim 2311
+\newsymbol\lnsim 2312
+\newsymbol\gnsim 2313
+\newsymbol\nleqq 2314
+\newsymbol\ngeqq 2315
+\newsymbol\precneqq 2316
+\newsymbol\succneqq 2317
+\newsymbol\precnapprox 2318
+\newsymbol\succnapprox 2319
+\newsymbol\lnapprox 231A
+\newsymbol\gnapprox 231B
+\newsymbol\nsim 231C
+\newsymbol\ncong 231D
+\newsymbol\diagup 231E
+\newsymbol\diagdown 231F
+\newsymbol\varsubsetneq 2320
+\newsymbol\varsupsetneq 2321
+\newsymbol\nsubseteqq 2322
+\newsymbol\nsupseteqq 2323
+\newsymbol\subsetneqq 2324
+\newsymbol\supsetneqq 2325
+\newsymbol\varsubsetneqq 2326
+\newsymbol\varsupsetneqq 2327
+\newsymbol\subsetneq 2328
+\newsymbol\supsetneq 2329
+\newsymbol\nsubseteq 232A
+\newsymbol\nsupseteq 232B
+\newsymbol\nparallel 232C
+\newsymbol\nmid 232D
+\newsymbol\nshortmid 232E
+\newsymbol\nshortparallel 232F
+\newsymbol\nvdash 2330
+\newsymbol\nVdash 2331
+\newsymbol\nvDash 2332
+\newsymbol\nVDash 2333
+\newsymbol\ntrianglerighteq 2334
+\newsymbol\ntrianglelefteq 2335
+\newsymbol\ntriangleleft 2336
+\newsymbol\ntriangleright 2337
+\newsymbol\nleftarrow 2338
+\newsymbol\nrightarrow 2339
+\newsymbol\nLeftarrow 233A
+\newsymbol\nRightarrow 233B
+\newsymbol\nLeftrightarrow 233C
+\newsymbol\nleftrightarrow 233D
+\newsymbol\divideontimes 223E
+\newsymbol\varnothing 203F
+\newsymbol\nexists 2040
+\newsymbol\Finv 2060
+\newsymbol\Game 2061
+\newsymbol\mho 2066
+\newsymbol\eth 2067
+\newsymbol\eqsim 2368
+\newsymbol\beth 2069
+\newsymbol\gimel 206A
+\newsymbol\daleth 206B
+\newsymbol\lessdot 236C
+\newsymbol\gtrdot 236D
+\newsymbol\ltimes 226E
+\newsymbol\rtimes 226F
+\newsymbol\shortmid 2370
+\newsymbol\shortparallel 2371
+\newsymbol\smallsetminus 2272
+\newsymbol\thicksim 2373
+\newsymbol\thickapprox 2374
+\newsymbol\approxeq 2375
+\newsymbol\succapprox 2376
+\newsymbol\precapprox 2377
+\newsymbol\curvearrowleft 2378
+\newsymbol\curvearrowright 2379
+\newsymbol\digamma 207A
+\newsymbol\varkappa 207B
+\newsymbol\Bbbk 207C
+\newsymbol\hslash 207D
+\undefine\hbar
+\newsymbol\hbar 207E
+\newsymbol\backepsilon 237F
+% Restore the catcode value for @ that was previously saved.
+\catcode`\@=\csname pre amssym.tex at\endcsname
+
+\endinput
diff --git a/systems/knuth/local/lib/amstex.tex b/systems/knuth/local/lib/amstex.tex
new file mode 100644
index 0000000000..96502696d6
--- /dev/null
+++ b/systems/knuth/local/lib/amstex.tex
@@ -0,0 +1,1797 @@
+%% @texfile{
+%% filename="amstex.tex",
+%% version="2.1",
+%% date="5-APR-1991",
+%% filetype="AMS-TeX: documentstyle",
+%% copyright="Copyright (C) American Mathematical Society,
+%% all rights reserved. Copying of this file is
+%% authorized only if either:
+%% (1) you make absolutely no changes to your copy
+%% including name; OR
+%% (2) if you do make changes, you first rename it to some
+%% other name.",
+%% author="American Mathematical Society",
+%% address="American Mathematical Society,
+%% Technical Support Group,
+%% P. O. Box 6248,
+%% Providence, RI 02940,
+%% USA",
+%% telephone="401-455-4080 or (in the USA) 800-321-4AMS",
+%% email="Internet: Tech-Support@Math.AMS.com",
+%% codetable="ISO/ASCII",
+%% checksumtype="line count",
+%% checksum="1797",
+%% keywords="amstex, ams-tex, tex",
+%% abstract="This file contains the AMS-TeX Version 2.1 macro
+%% package. It is intended to be used in conjunction with
+%% plain TeX. The macros defined herein are chiefly oriented
+%% to the construction of mathematical formulas, both display
+%% and nondisplay, using AMS conventions for fonts, spacing,
+%% and other details."
+%% }
+\def\fmtname{AmS-TeX}
+\let\plainfmtversion\fmtversion
+\def\fmtversion{2.1}
+\catcode`\@=11
+\ifx\amstexloaded@\relax\catcode`\@=\active
+ \endinput\else\let\amstexloaded@\relax\fi
+\newlinechar=`\^^J
+\def\W@{\immediate\write\sixt@@n}
+\def\CR@{\W@{^^J\fmtname - Version \fmtversion^^J%
+COPYRIGHT 1985, 1990, 1991 - AMERICAN MATHEMATICAL SOCIETY^^J%
+Use of this macro package is not restricted provided^^J%
+each use is acknowledged upon publication.^^J}}
+\CR@ \everyjob{\CR@}
+\message{Loading definitions for}
+\message{misc utility macros,}
+\toksdef\toks@@=2
+\long\def\rightappend@#1\to#2{\toks@{\\{#1}}\toks@@
+ =\expandafter{#2}\xdef#2{\the\toks@@\the\toks@}\toks@{}\toks@@{}}
+\def\alloclist@{}
+\newif\ifalloc@
+\def\showallocations{{\def\\{\immediate\write\m@ne}\alloclist@}\alloc@true}
+\def\alloc@#1#2#3#4#5{\global\advance\count1#1by\@ne
+ \ch@ck#1#4#2\allocationnumber=\count1#1
+ \global#3#5=\allocationnumber
+ \edef\next@{\string#5=\string#2\the\allocationnumber}%
+ \expandafter\rightappend@\next@\to\alloclist@}
+\newcount\count@@
+\newcount\count@@@
+\def\FN@{\futurelet\next}
+\def\DN@{\def\next@}
+\def\DNii@{\def\nextii@}
+\def\RIfM@{\relax\ifmmode}
+\def\RIfMIfI@{\relax\ifmmode\ifinner}
+\def\setboxz@h{\setbox\z@\hbox}
+\def\wdz@{\wd\z@}
+\def\boxz@{\box\z@}
+\def\setbox@ne{\setbox\@ne}
+\def\wd@ne{\wd\@ne}
+\def\iterate{\body\expandafter\iterate\else\fi}
+\def\err@#1{\errmessage{AmS-TeX error: #1}}
+\newhelp\defaulthelp@{Sorry, I already gave what help I could...^^J
+Maybe you should try asking a human?^^J
+An error might have occurred before I noticed any problems.^^J
+``If all else fails, read the instructions.''}
+\def\Err@{\errhelp\defaulthelp@\err@}
+\def\eat@#1{}
+\def\in@#1#2{\def\in@@##1#1##2##3\in@@{\ifx\in@##2\in@false\else\in@true\fi}%
+ \in@@#2#1\in@\in@@}
+\newif\ifin@
+\def\space@.{\futurelet\space@\relax}
+\space@. %
+\newhelp\athelp@
+{Only certain combinations beginning with @ make sense to me.^^J
+Perhaps you wanted \string\@\space for a printed @?^^J
+I've ignored the character or group after @.}
+{\catcode`\~=\active % just in case
+ \lccode`\~=`\@ \lowercase{\gdef~{\FN@\at@}}}
+\def\at@{\let\next@\at@@
+ \ifcat\noexpand\next a\else\ifcat\noexpand\next0\else
+ \ifcat\noexpand\next\relax\else
+ \let\next\at@@@\fi\fi\fi
+ \next@}
+\def\at@@#1{\expandafter
+ \ifx\csname\space @\string#1\endcsname\relax
+ \expandafter\at@@@ \else
+ \csname\space @\string#1\expandafter\endcsname\fi}
+\def\at@@@#1{\errhelp\athelp@ \err@{\Invalid@@ @}}%%
+\def\atdef@#1{\expandafter\def\csname\space @\string#1\endcsname}%%
+\newhelp\defahelp@{If you typed \string\define\space cs instead of
+\string\define\string\cs\space^^J
+I've substituted an inaccessible control sequence so that your^^J
+definition will be completed without mixing me up too badly.^^J
+If you typed \string\define{\string\cs} the inaccessible control sequence^^J
+was defined to be \string\cs, and the rest of your^^J
+definition appears as input.}
+\newhelp\defbhelp@{I've ignored your definition, because it might^^J
+conflict with other uses that are important to me.}
+\def\define{\FN@\define@}
+\def\define@{\ifcat\noexpand\next\relax
+ \expandafter\define@@\else\errhelp\defahelp@ %1
+ \err@{\string\define\space must be followed by a control
+ sequence}\expandafter\def\expandafter\nextii@\fi} %2
+\def\undefined@@@@@@@@@@{}
+\def\preloaded@@@@@@@@@@{}
+\def\next@@@@@@@@@@{}
+\def\define@@#1{\ifx#1\relax\errhelp\defbhelp@ %1
+ \err@{\string#1\space is already defined}\DN@{\DNii@}\else
+ \expandafter\ifx\csname\expandafter\eat@\string %2
+ #1@@@@@@@@@@\endcsname\undefined@@@@@@@@@@\errhelp\defbhelp@
+ \err@{\string#1\space can't be defined}\DN@{\DNii@}\else
+ \expandafter\ifx\csname\expandafter\eat@\string#1\endcsname\relax %3
+ \global\let#1\undefined\DN@{\def#1}\else\errhelp\defbhelp@
+ \err@{\string#1\space is already defined}\DN@{\DNii@}\fi
+ \fi\fi\next@}
+\let\redefine\def
+\def\predefine#1#2{\let#1#2}
+\def\undefine#1{\let#1\undefined}
+\message{page layout,}
+\newdimen\captionwidth@
+\captionwidth@\hsize
+\advance\captionwidth@-1.5in
+\def\pagewidth#1{\hsize#1\relax
+ \captionwidth@\hsize\advance\captionwidth@-1.5in}
+\def\pageheight#1{\vsize#1\relax}
+\def\hcorrection#1{\advance\hoffset#1\relax}
+\def\vcorrection#1{\advance\voffset#1\relax}
+\message{accents/punctuation,}
+\def\tie{\unskip\nobreak\ }
+\let\graveaccent\`
+\let\acuteaccent\'
+\let\tildeaccent\~
+\let\hataccent\^
+\let\underscore\_
+\let\B\=
+\let\D\.
+\let\ic@\/
+\def\/{\unskip\ic@}
+\def\textfonti{\the\textfont\@ne}
+\def\t#1#2{{\edef\next@{\the\font}\textfonti\accent"7F \next@#1#2}}
+\def~{\unskip\nobreak\ \ignorespaces}
+\def\.{.\spacefactor\@m}
+\atdef@;{\leavevmode\null;}
+\atdef@:{\leavevmode\null:}
+\atdef@?{\leavevmode\null?}
+\edef\@{\string @}
+\def\relaxnext@{\let\next\relax}
+\atdef@-{\relaxnext@\leavevmode
+ \DN@{\ifx\next-\DN@-{\FN@\nextii@}\else
+ \DN@{\leavevmode\hbox{-}}\fi\next@}%
+ \DNii@{\ifx\next-\DN@-{\leavevmode\hbox{---}}\else
+ \DN@{\leavevmode\hbox{--}}\fi\next@}%
+ \FN@\next@}
+\def\srdr@{\kern.16667em}
+\def\drsr@{\kern.02778em}
+\def\sldl@{\drsr@}
+\def\dlsl@{\srdr@}
+\atdef@"{\unskip\relaxnext@
+ \DN@{\ifx\next\space@\DN@. {\FN@\nextii@}\else
+ \DN@.{\FN@\nextii@}\fi\next@.}%
+ \DNii@{\ifx\next`\DN@`{\FN@\nextiii@}\else
+ \ifx\next\lq\DN@\lq{\FN@\nextiii@}\else
+ \DN@####1{\FN@\nextiv@}\fi\fi\next@}%
+ \def\nextiii@{\ifx\next`\DN@`{\sldl@``}\else\ifx\next\lq
+ \DN@\lq{\sldl@``}\else\DN@{\dlsl@`}\fi\fi\next@}%
+ \def\nextiv@{\ifx\next'\DN@'{\srdr@''}\else
+ \ifx\next\rq\DN@\rq{\srdr@''}\else\DN@{\drsr@'}\fi\fi\next@}%
+ \FN@\next@}
+\def\flushpar{\par\noindent}
+\def\textfontii{\the\textfont\tw@}
+\def\lbrace@{\delimiter"4266308 }
+\def\rbrace@{\delimiter"5267309 }
+\def\{{\RIfM@\lbrace@\else{\textfontii f}\spacefactor\@m\fi}
+\def\}{\RIfM@\rbrace@\else
+ \let\@sf\empty\ifhmode\edef\@sf{\spacefactor\the\spacefactor}\fi
+ {\textfontii g}\@sf\relax\fi}
+\let\lbrace\{
+\let\rbrace\}
+\def\AmSTeX{{\textfontii A\kern-.1667em%
+ \lower.5ex\hbox{M}\kern-.125emS}-\TeX}
+\message{line and page breaks,}
+\def\vmodeerr@#1{\Err@{\string#1\space not allowed between paragraphs}}
+\def\mathmodeerr@#1{\Err@{\string#1\space not allowed in math mode}}
+\def\linebreak{\RIfM@\mathmodeerr@\linebreak\else
+ \ifhmode\unskip\unkern\break\else\vmodeerr@\linebreak\fi\fi}
+\def\slash{\unskip/\penalty\exhyphenpenalty}
+\newskip\saveskip@
+\def\allowlinebreak{\RIfM@\mathmodeerr@\allowlinebreak\else
+ \ifhmode\saveskip@\lastskip\unskip
+ \allowbreak\ifdim\saveskip@>\z@\hskip\saveskip@\fi
+ \else\vmodeerr@\allowlinebreak\fi\fi}
+\def\nolinebreak{\RIfM@\mathmodeerr@\nolinebreak\else
+ \ifhmode\saveskip@\lastskip\unskip
+ \nobreak\ifdim\saveskip@>\z@\hskip\saveskip@\fi
+ \else\vmodeerr@\nolinebreak\fi\fi}
+\def\newline{\relaxnext@
+ \DN@{\RIfM@\expandafter\mathmodeerr@\expandafter\newline\else
+ \ifhmode\ifx\next\par\else
+ \expandafter\unskip\expandafter\null\expandafter\hfill\expandafter\break\fi
+ \else
+ \expandafter\vmodeerr@\expandafter\newline\fi\fi}%
+ \FN@\next@}
+\def\dmatherr@#1{\Err@{\string#1\space not allowed in display math mode}}
+\def\nondmatherr@#1{\Err@{\string#1\space not allowed in non-display math
+ mode}}
+\def\onlydmatherr@#1{\Err@{\string#1\space allowed only in display math mode}}
+\def\nonmatherr@#1{\Err@{\string#1\space allowed only in math mode}}
+\def\mathbreak{\RIfMIfI@\break\else
+ \dmatherr@\mathbreak\fi\else\nonmatherr@\mathbreak\fi}
+\def\nomathbreak{\RIfMIfI@\nobreak\else
+ \dmatherr@\nomathbreak\fi\else\nonmatherr@\nomathbreak\fi}
+\def\allowmathbreak{\RIfMIfI@\allowbreak\else
+ \dmatherr@\allowmathbreak\fi\else\nonmatherr@\allowmathbreak\fi}
+\def\pagebreak{\RIfM@
+ \ifinner\nondmatherr@\pagebreak\else\postdisplaypenalty-\@M\fi
+ \else\ifvmode\removelastskip\break\else\vadjust{\break}\fi\fi}
+\def\nopagebreak{\RIfM@
+ \ifinner\nondmatherr@\nopagebreak\else\postdisplaypenalty\@M\fi
+ \else\ifvmode\nobreak\else\vadjust{\nobreak}\fi\fi}
+\def\nonvmodeerr@#1{\Err@{\string#1\space not allowed within a paragraph
+ or in math}}
+\def\vnonvmode@#1#2{\relaxnext@\DNii@{\ifx\next\par\DN@{#1}\else
+ \DN@{#2}\fi\next@}%
+ \ifvmode\DN@{#1}\else
+ \DN@{\FN@\nextii@}\fi\next@}
+\def\newpage{\vnonvmode@{\vfill\break}{\nonvmodeerr@\newpage}}
+\def\smallpagebreak{\vnonvmode@\smallbreak{\nonvmodeerr@\smallpagebreak}}
+\def\medpagebreak{\vnonvmode@\medbreak{\nonvmodeerr@\medpagebreak}}
+\def\bigpagebreak{\vnonvmode@\bigbreak{\nonvmodeerr@\bigpagebreak}}
+\def\NoBlackBoxes{\global\overfullrule\z@}
+\def\BlackBoxes{\global\overfullrule5\p@}
+\def\Invalid@#1{\def#1{\Err@{\Invalid@@\string#1}}}
+\def\Invalid@@{Invalid use of }
+\message{figures,}
+\Invalid@\caption
+\Invalid@\captionwidth
+\newdimen\smallcaptionwidth@
+\def\topspace{\mid@false\ins@}
+\def\midspace{\mid@true\ins@}
+\newif\ifmid@
+\def\captionfont@{}
+\def\ins@#1{\relaxnext@\allowbreak
+ \smallcaptionwidth@\captionwidth@\gdef\thespace@{#1}%
+ \DN@{\ifx\next\space@\DN@. {\FN@\nextii@}\else
+ \DN@.{\FN@\nextii@}\fi\next@.}%
+ \DNii@{\ifx\next\caption\DN@\caption{\FN@\nextiii@}%
+ \else\let\next@\nextiv@\fi\next@}%
+ \def\nextiv@{\vnonvmode@
+ {\ifmid@\expandafter\midinsert\else\expandafter\topinsert\fi
+ \vbox to\thespace@{}\endinsert}
+ {\ifmid@\nonvmodeerr@\midspace\else\nonvmodeerr@\topspace\fi}}%
+ \def\nextiii@{\ifx\next\captionwidth\expandafter\nextv@
+ \else\expandafter\nextvi@\fi}%
+ \def\nextv@\captionwidth##1##2{\smallcaptionwidth@##1\relax\nextvi@{##2}}%
+ \def\nextvi@##1{\def\thecaption@{\captionfont@##1}%
+ \DN@{\ifx\next\space@\DN@. {\FN@\nextvii@}\else
+ \DN@.{\FN@\nextvii@}\fi\next@.}%
+ \FN@\next@}%
+ \def\nextvii@{\vnonvmode@
+ {\ifmid@\expandafter\midinsert\else
+ \expandafter\topinsert\fi\vbox to\thespace@{}\nobreak\smallskip
+ \setboxz@h{\noindent\ignorespaces\thecaption@\unskip}%
+ \ifdim\wdz@>\smallcaptionwidth@\centerline{\vbox{\hsize\smallcaptionwidth@
+ \noindent\ignorespaces\thecaption@\unskip}}%
+ \else\centerline{\boxz@}\fi\endinsert}
+ {\ifmid@\nonvmodeerr@\midspace
+ \else\nonvmodeerr@\topspace\fi}}%
+ \FN@\next@}
+\message{comments,}
+\def\newcodes@{\catcode`\\12\catcode`\{12\catcode`\}12\catcode`\#12%
+ \catcode`\%12\relax}
+\def\oldcodes@{\catcode`\\0\catcode`\{1\catcode`\}2\catcode`\#6%
+ \catcode`\%14\relax}
+\def\comment{\newcodes@\endlinechar=10 \comment@}
+{\lccode`\0=`\\
+\lowercase{\gdef\comment@#1^^J{\comment@@#10endcomment\comment@@@}%
+\gdef\comment@@#10endcomment{\FN@\comment@@@}%
+\gdef\comment@@@#1\comment@@@{\ifx\next\comment@@@\let\next\comment@
+ \else\def\next{\oldcodes@\endlinechar=`\^^M\relax}%
+ \fi\next}}}
+\def\pr@m@s{\ifx'\next\DN@##1{\prim@s}\else\let\next@\egroup\fi\next@}
+\def\prime{{\null\prime@\null}}
+\mathchardef\prime@="0230
+\let\dsize\displaystyle
+\let\tsize\textstyle
+\let\ssize\scriptstyle
+\let\sssize\scriptscriptstyle
+\message{math spacing,}
+\def\,{\RIfM@\mskip\thinmuskip\relax\else\kern.16667em\fi}
+\def\!{\RIfM@\mskip-\thinmuskip\relax\else\kern-.16667em\fi}
+\let\thinspace\,
+\let\negthinspace\!
+\def\medspace{\RIfM@\mskip\medmuskip\relax\else\kern.222222em\fi}
+\def\negmedspace{\RIfM@\mskip-\medmuskip\relax\else\kern-.222222em\fi}
+\def\thickspace{\RIfM@\mskip\thickmuskip\relax\else\kern.27777em\fi}
+\let\;\thickspace
+\def\negthickspace{\RIfM@\mskip-\thickmuskip\relax\else
+ \kern-.27777em\fi}
+\atdef@,{\RIfM@\mskip.1\thinmuskip\else\leavevmode\null,\fi}
+\atdef@!{\RIfM@\mskip-.1\thinmuskip\else\leavevmode\null!\fi}
+\atdef@.{\RIfM@&&\else\leavevmode.\spacefactor3000 \fi}
+\def\and{\DOTSB\;\mathchar"3026 \;}
+\def\implies{\DOTSB\;\Longrightarrow\;}
+\def\impliedby{\DOTSB\;\Longleftarrow\;}
+\message{fractions,}
+\def\frac#1#2{{#1\over#2}}
+\def\dfrac#1#2{{\displaystyle{#1\over#2}}}
+\def\tfrac#1#2{{\textstyle{#1\over#2}}}
+\newdimen\ex@
+\ex@.2326ex
+\Invalid@\thickness
+\def\thickfrac{\relaxnext@
+ \DN@{\ifx\next\thickness\let\next@\nextii@\else
+ \DN@{\nextii@\thickness1}\fi\next@}%
+ \DNii@\thickness##1##2##3{{##2\above##1\ex@##3}}%
+ \FN@\next@}
+\def\fracwithdelims#1#2#3#4{{#3\overwithdelims#1#2#4}}
+\def\thickfracwithdelims#1#2{\relaxnext@\def\ldelim@{#1}\def\rdelim@{#2}%
+ \DN@{\ifx\next\thickness\let\next@\nextii@\else
+ \DN@{\nextii@\thickness1}\fi\next@}%
+ \DNii@\thickness##1##2##3{{##2\abovewithdelims
+ \ldelim@\rdelim@##1\ex@##3}}%
+ \FN@\next@}
+\def\binom#1#2{{#1\choose#2}}
+\def\dbinom#1#2{{\displaystyle{#1\choose#2}}}
+\def\tbinom#1#2{{\textstyle{#1\choose#2}}}
+\def\:{\nobreak\hskip.1111em\mathpunct{}\nonscript\mkern-\thinmuskip{:}\hskip
+ .3333emplus.0555em\relax}
+\def\snug{\unskip\kern-\mathsurround}
+\message{smash commands,}
+\def\topsmash{\top@true\bot@false\smash@}
+\def\botsmash{\top@false\bot@true\smash@}
+\newif\iftop@
+\newif\ifbot@
+\def\smash{\top@true\bot@true\smash@}
+\def\smash@{\RIfM@\expandafter\mathpalette\expandafter\mathsm@sh\else
+ \expandafter\makesm@sh\fi}
+\def\finsm@sh{\iftop@\ht\z@\z@\fi\ifbot@\dp\z@\z@\fi\leavevmode\boxz@}
+\message{large operator symbols,}
+\def\LimitsOnSums{\global\let\slimits@\displaylimits}
+\def\NoLimitsOnSums{\global\let\slimits@\nolimits}
+\LimitsOnSums
+\mathchardef\coprod@="1360 \def\coprod{\DOTSB\coprod@\slimits@}
+\mathchardef\bigvee@="1357 \def\bigvee{\DOTSB\bigvee@\slimits@}
+\mathchardef\bigwedge@="1356 \def\bigwedge{\DOTSB\bigwedge@\slimits@}
+\mathchardef\biguplus@="1355 \def\biguplus{\DOTSB\biguplus@\slimits@}
+\mathchardef\bigcap@="1354 \def\bigcap{\DOTSB\bigcap@\slimits@}
+\mathchardef\bigcup@="1353 \def\bigcup{\DOTSB\bigcup@\slimits@}
+\mathchardef\prod@="1351 \def\prod{\DOTSB\prod@\slimits@}
+\mathchardef\sum@="1350 \def\sum{\DOTSB\sum@\slimits@}
+\mathchardef\bigotimes@="134E \def\bigotimes{\DOTSB\bigotimes@\slimits@}
+\mathchardef\bigoplus@="134C \def\bigoplus{\DOTSB\bigoplus@\slimits@}
+\mathchardef\bigodot@="134A \def\bigodot{\DOTSB\bigodot@\slimits@}
+\mathchardef\bigsqcup@="1346 \def\bigsqcup{\DOTSB\bigsqcup@\slimits@}
+\message{integrals,}
+\def\LimitsOnInts{\global\let\ilimits@\displaylimits}
+\def\NoLimitsOnInts{\global\let\ilimits@\nolimits}
+\NoLimitsOnInts
+\def\int{\DOTSI\intop\ilimits@}
+\def\oint{\DOTSI\ointop\ilimits@}
+\def\intic@{\mathchoice{\hskip.5em}{\hskip.4em}{\hskip.4em}{\hskip.4em}}
+\def\negintic@{\mathchoice
+ {\hskip-.5em}{\hskip-.4em}{\hskip-.4em}{\hskip-.4em}}
+\def\intkern@{\mathchoice{\!\!\!}{\!\!}{\!\!}{\!\!}}
+\def\intdots@{\mathchoice{\plaincdots@}
+ {{\cdotp}\mkern1.5mu{\cdotp}\mkern1.5mu{\cdotp}}
+ {{\cdotp}\mkern1mu{\cdotp}\mkern1mu{\cdotp}}
+ {{\cdotp}\mkern1mu{\cdotp}\mkern1mu{\cdotp}}}
+\newcount\intno@
+\def\iint{\DOTSI\intno@\tw@\FN@\ints@}
+\def\iiint{\DOTSI\intno@\thr@@\FN@\ints@}
+\def\iiiint{\DOTSI\intno@4 \FN@\ints@}
+\def\idotsint{\DOTSI\intno@\z@\FN@\ints@}
+\def\ints@{\findlimits@\ints@@}
+\newif\iflimtoken@
+\newif\iflimits@
+\def\findlimits@{\limtoken@true\ifx\next\limits\limits@true
+ \else\ifx\next\nolimits\limits@false\else
+ \limtoken@false\ifx\ilimits@\nolimits\limits@false\else
+ \ifinner\limits@false\else\limits@true\fi\fi\fi\fi}
+\def\multint@{\int\ifnum\intno@=\z@\intdots@ %1
+ \else\intkern@\fi %2
+ \ifnum\intno@>\tw@\int\intkern@\fi %3
+ \ifnum\intno@>\thr@@\int\intkern@\fi %4
+ \int} %5
+\def\multintlimits@{\intop\ifnum\intno@=\z@\intdots@\else\intkern@\fi
+ \ifnum\intno@>\tw@\intop\intkern@\fi
+ \ifnum\intno@>\thr@@\intop\intkern@\fi\intop}
+\def\ints@@{\iflimtoken@ %1
+ \def\ints@@@{\iflimits@\negintic@\mathop{\intic@\multintlimits@}\limits %2
+ \else\multint@\nolimits\fi %3
+ \eat@} %4
+ \else %5
+ \def\ints@@@{\iflimits@\negintic@
+ \mathop{\intic@\multintlimits@}\limits\else
+ \multint@\nolimits\fi}\fi\ints@@@}
+\def\LimitsOnNames{\global\let\nlimits@\displaylimits}
+\def\NoLimitsOnNames{\global\let\nlimits@\nolimits@}
+\LimitsOnNames
+\def\nolimits@{\relaxnext@
+ \DN@{\ifx\next\limits\DN@\limits{\nolimits}\else
+ \let\next@\nolimits\fi\next@}%
+ \FN@\next@}
+\message{operator names,}
+\def\newmcodes@{\mathcode`\'"27\mathcode`\*"2A\mathcode`\."613A%
+ \mathcode`\-"2D\mathcode`\/"2F\mathcode`\:"603A }
+\def\operatorname#1{\mathop{\newmcodes@\kern\z@\fam\z@#1}\nolimits@}
+\def\operatornamewithlimits#1{\mathop{\newmcodes@\kern\z@\fam\z@#1}\nlimits@}
+\def\qopname@#1{\mathop{\fam\z@#1}\nolimits@}
+\def\qopnamewl@#1{\mathop{\fam\z@#1}\nlimits@}
+\def\arccos{\qopname@{arccos}}
+\def\arcsin{\qopname@{arcsin}}
+\def\arctan{\qopname@{arctan}}
+\def\arg{\qopname@{arg}}
+\def\cos{\qopname@{cos}}
+\def\cosh{\qopname@{cosh}}
+\def\cot{\qopname@{cot}}
+\def\coth{\qopname@{coth}}
+\def\csc{\qopname@{csc}}
+\def\deg{\qopname@{deg}}
+\def\det{\qopnamewl@{det}}
+\def\dim{\qopname@{dim}}
+\def\exp{\qopname@{exp}}
+\def\gcd{\qopnamewl@{gcd}}
+\def\hom{\qopname@{hom}}
+\def\inf{\qopnamewl@{inf}}
+\def\injlim{\qopnamewl@{inj\,lim}}
+\def\ker{\qopname@{ker}}
+\def\lg{\qopname@{lg}}
+\def\lim{\qopnamewl@{lim}}
+\def\liminf{\qopnamewl@{lim\,inf}}
+\def\limsup{\qopnamewl@{lim\,sup}}
+\def\ln{\qopname@{ln}}
+\def\log{\qopname@{log}}
+\def\max{\qopnamewl@{max}}
+\def\min{\qopnamewl@{min}}
+\def\Pr{\qopnamewl@{Pr}}
+\def\projlim{\qopnamewl@{proj\,lim}}
+\def\sec{\qopname@{sec}}
+\def\sin{\qopname@{sin}}
+\def\sinh{\qopname@{sinh}}
+\def\sup{\qopnamewl@{sup}}
+\def\tan{\qopname@{tan}}
+\def\tanh{\qopname@{tanh}}
+\def\varinjlim{\mathop{\vtop{\ialign{##\crcr
+ \hfil\rm lim\hfil\crcr\noalign{\nointerlineskip}\rightarrowfill\crcr
+ \noalign{\nointerlineskip\kern-\ex@}\crcr}}}}
+\def\varprojlim{\mathop{\vtop{\ialign{##\crcr
+ \hfil\rm lim\hfil\crcr\noalign{\nointerlineskip}\leftarrowfill\crcr
+ \noalign{\nointerlineskip\kern-\ex@}\crcr}}}}
+\def\varliminf{\mathop{\underline{\vrule height\z@ depth.2exwidth\z@
+ \hbox{\rm lim}}}}
+\def\varlimsup{\mathop{\overline{\hbox{\rm lim}}}}
+\newdimen\buffer@
+\buffer@\fontdimen13 \tenex
+\newdimen\buffer
+\buffer\buffer@
+\def\ChangeBuffer#1{\fontdimen13 \tenex#1\relax
+ \global\buffer\fontdimen13 \tenex}
+\def\ResetBuffer{\fontdimen13 \tenex\buffer@\global\buffer\buffer@}
+\def\shave#1{\mathop{\hbox{$\m@th\fontdimen13 \tenex\z@ %1
+ \displaystyle{#1}$}}\fontdimen13 \tenex\buffer}
+\def\topshave#1{\mathop{\topsmash{\displaystyle{#1}}\vphantom{\shave{#1}}}}
+\def\botshave#1{\mathop{\botsmash{\displaystyle{#1}}\vphantom{\shave{#1}}}}
+\message{multilevel sub/superscripts,}
+\Invalid@\\
+\def\Let@{\relax\iffalse{\fi\let\\=\cr\iffalse}\fi}
+\Invalid@\vspace
+\def\vspace@{\def\vspace##1{\crcr\noalign{\vskip##1\relax}}}
+\def\multilimits@{\bgroup\vspace@\Let@
+ \baselineskip\fontdimen10 \scriptfont\tw@
+ \advance\baselineskip\fontdimen12 \scriptfont\tw@
+ \lineskip\thr@@\fontdimen8 \scriptfont\thr@@
+ \lineskiplimit\lineskip
+ \vbox\bgroup\ialign\bgroup\hfil$\m@th\scriptstyle{##}$\hfil\crcr}
+\def\Sb{_\multilimits@}
+\def\endSb{\crcr\egroup\egroup\egroup}
+\def\Sp{^\multilimits@}
+\let\endSp\endSb
+\def\spreadlines#1{\RIfMIfI@\onlydmatherr@\spreadlines\else
+ \openup#1\relax\fi\else\onlydmatherr@\spreadlines\fi}
+\def\Mathstrut@{\copy\Mathstrutbox@}
+\newbox\Mathstrutbox@
+\setbox\Mathstrutbox@\null
+\setboxz@h{$\m@th($}
+\ht\Mathstrutbox@\ht\z@
+\dp\Mathstrutbox@\dp\z@
+\message{matrices,}
+\newdimen\spreadmlines@
+\def\spreadmatrixlines#1{\RIfMIfI@
+ \onlydmatherr@\spreadmatrixlines\else
+ \spreadmlines@#1\relax\fi\else\onlydmatherr@\spreadmatrixlines\fi}
+\def\matrix{\null\,\vcenter\bgroup\Let@\vspace@
+ \normalbaselines\openup\spreadmlines@\ialign
+ \bgroup\hfil$\m@th##$\hfil&&\quad\hfil$\m@th##$\hfil\crcr
+ \Mathstrut@\crcr\noalign{\kern-\baselineskip}}
+\def\endmatrix{\crcr\Mathstrut@\crcr\noalign{\kern-\baselineskip}\egroup
+ \egroup\,}
+\def\format{\crcr\egroup\iffalse{\fi\ifnum`}=0 \fi\format@}
+\newtoks\hashtoks@
+\hashtoks@{#}
+\def\format@#1\\{\def\preamble@{#1}%
+ \def\l{$\m@th\the\hashtoks@$\hfil}%
+ \def\c{\hfil$\m@th\the\hashtoks@$\hfil}%
+ \def\r{\hfil$\m@th\the\hashtoks@$}%
+ \edef\preamble@@{\preamble@}\ifnum`{=0 \fi\iffalse}\fi
+ \ialign\bgroup\span\preamble@@\crcr}
+\def\smallmatrix{\null\,\vcenter\bgroup\vspace@\Let@
+ \baselineskip9\ex@\lineskip\ex@
+ \ialign\bgroup\hfil$\m@th\scriptstyle{##}$\hfil&&\thickspace\hfil
+ $\m@th\scriptstyle{##}$\hfil\crcr}
+\def\endsmallmatrix{\crcr\egroup\egroup\,}
+\def\pmatrix{\left(\matrix}
+\def\endpmatrix{\endmatrix\right)}
+\def\bmatrix{\left[\matrix}
+\def\endbmatrix{\endmatrix\right]}
+\def\vmatrix{\left|\matrix}
+\def\endvmatrix{\endmatrix\right|}
+\def\Vmatrix{\left\|\matrix}
+\def\endVmatrix{\endmatrix\right\|}
+\def\hdots{\mathinner{\ldotp\ldotp\ldotp}}
+\newmuskip\dotsspace@
+\dotsspace@1.5mu
+\def\strip@#1 {#1}
+\def\spacehdots#1\for#2{\multispan{#2}\xleaders
+ \hbox{$\m@th\mkern\strip@#1 \dotsspace@.\mkern\strip@#1 \dotsspace@$}\hfill}
+\def\hdotsfor#1{\spacehdots\@ne\for{#1}}
+\def\multispan@#1{\omit\mscount#1\unskip\loop\ifnum\mscount>\@ne\sp@n\repeat}
+\def\spaceinnerhdots#1\for#2\after#3{\multispan@{\strip@#2 }#3\xleaders
+ \hbox{$\m@th\mkern\strip@#1 \dotsspace@.\mkern\strip@#1 \dotsspace@$}\hfill}
+\def\innerhdotsfor#1\after#2{\spaceinnerhdots\@ne\for#1\after{#2}}
+\def\cases{\bgroup\spreadmlines@\jot\left\{\,\matrix\format\l&\quad\l\\}
+\def\endcases{\endmatrix\right.\egroup}
+\message{multiline displays,}
+\newif\ifinany@
+\newif\ifinalign@
+\newif\ifingather@
+\def\strut@{\copy\strutbox@}
+\newbox\strutbox@
+\setbox\strutbox@\hbox{\vrule height8\p@ depth3\p@ width\z@}
+\def\topaligned{\null\,\vtop\aligned@}
+\def\botaligned{\null\,\vbox\aligned@}
+\def\aligned{\null\,\vcenter\aligned@}
+\def\aligned@{\bgroup\vspace@\Let@
+ \ifinany@\else\openup\jot\fi\ialign
+ \bgroup\hfil\strut@$\m@th\displaystyle{##}$&
+ $\m@th\displaystyle{{}##}$\hfil\crcr}
+\def\endaligned{\crcr\egroup\egroup}
+\let\endtopaligned\endaligned
+\let\endbotaligned\endaligned
+\def\alignedat#1{\null\,\vcenter\bgroup\doat@{#1}\vspace@\Let@
+ \ifinany@\else\openup\jot\fi\ialign\bgroup\span\preamble@@\crcr}
+\newcount\atcount@
+\def\doat@#1{\toks@{\hfil\strut@$\m@th
+ \displaystyle{\the\hashtoks@}$&$\m@th\displaystyle
+ {{}\the\hashtoks@}$\hfil}% %1
+ \atcount@#1\relax\advance\atcount@\m@ne %2
+ \loop\ifnum\atcount@>\z@\toks@=\expandafter{\the\toks@&\hfil$\m@th
+ \displaystyle{\the\hashtoks@}$&$\m@th
+ \displaystyle{{}\the\hashtoks@}$\hfil}\advance
+ \atcount@\m@ne\repeat %3
+ \xdef\preamble@{\the\toks@}\xdef\preamble@@{\preamble@}}
+\let\endalignedat\endaligned
+\def\gathered{\null\,\vcenter\bgroup\vspace@\Let@
+ \ifinany@\else\openup\jot\fi\ialign
+ \bgroup\hfil\strut@$\m@th\displaystyle{##}$\hfil\crcr}
+\def\endgathered{\crcr\egroup\egroup}
+\newif\iftagsleft@
+\def\TagsOnLeft{\global\tagsleft@true}
+\def\TagsOnRight{\global\tagsleft@false}
+\TagsOnLeft
+\newif\ifmathtags@
+\def\TagsAsMath{\global\mathtags@true}
+\def\TagsAsText{\global\mathtags@false}
+\TagsAsText
+\def\tagform@#1{\hbox{\rm(\ignorespaces#1\unskip)}}
+\def\thetag{\leavevmode\tagform@}
+\def\tag#1$${\iftagsleft@\leqno\else\eqno\fi %1
+ \maketag@#1\maketag@ %2
+ $$} %3
+\def\maketag@{\FN@\maketag@@}
+\def\maketag@@{\ifx\next"\expandafter\maketag@@@\else\expandafter\maketag@@@@
+ \fi}
+\def\maketag@@@"#1"#2\maketag@{\hbox{\rm#1}} %1
+\def\maketag@@@@#1\maketag@{\ifmathtags@\tagform@{$\m@th#1$}\else
+ \tagform@{#1}\fi}
+\interdisplaylinepenalty\@M
+\def\allowdisplaybreaks{\RIfMIfI@
+ \onlydmatherr@\allowdisplaybreaks\else
+ \interdisplaylinepenalty\z@\fi\else\onlydmatherr@\allowdisplaybreaks\fi}
+\Invalid@\allowdisplaybreak
+\Invalid@\displaybreak
+\Invalid@\intertext
+\def\allowdisplaybreak@{\def\allowdisplaybreak{\crcr\noalign{\allowbreak}}}
+\def\displaybreak@{\def\displaybreak{\crcr\noalign{\break}}}
+\def\intertext@{\def\intertext##1{\crcr\noalign{%
+ \penalty\postdisplaypenalty \vskip\belowdisplayskip
+ \vbox{\normalbaselines\noindent##1}%
+ \penalty\predisplaypenalty \vskip\abovedisplayskip}}}
+\newskip\centering@
+\centering@\z@ plus\@m\p@
+\def\align{\relax\ifingather@\DN@{\csname align (in
+ \string\gather)\endcsname}\else
+ \ifmmode\ifinner\DN@{\onlydmatherr@\align}\else
+ \let\next@\align@\fi
+ \else\DN@{\onlydmatherr@\align}\fi\fi\next@}
+\newhelp\andhelp@
+{An extra & here is so disastrous that you should probably exit^^J
+and fix things up.}
+\newif\iftag@
+\newcount\and@
+\def\align@{\inalign@true\inany@true
+ \vspace@\allowdisplaybreak@\displaybreak@\intertext@
+ \def\tag{\global\tag@true\ifnum\and@=\z@\DN@{&&}\else
+ \DN@{&}\fi\next@}%
+ \iftagsleft@\DN@{\csname align \endcsname}\else
+ \DN@{\csname align \space\endcsname}\fi\next@}
+\def\Tag@{\iftag@\else\errhelp\andhelp@\err@{Extra & on this line}\fi}
+\newdimen\lwidth@
+\newdimen\rwidth@
+\newdimen\maxlwidth@
+\newdimen\maxrwidth@
+\newdimen\totwidth@
+\def\measure@#1\endalign{\lwidth@\z@\rwidth@\z@\maxlwidth@\z@\maxrwidth@\z@
+ \global\and@\z@ %1
+ \setbox@ne\vbox %2
+ {\everycr{\noalign{\global\tag@false\global\and@\z@}}\Let@ %3
+ \halign{\setboxz@h{$\m@th\displaystyle{\@lign##}$}% %4
+ \global\lwidth@\wdz@ %5
+ \ifdim\lwidth@>\maxlwidth@\global\maxlwidth@\lwidth@\fi %6
+ \global\advance\and@\@ne %7
+ &\setboxz@h{$\m@th\displaystyle{{}\@lign##}$}\global\rwidth@\wdz@ %8
+ \ifdim\rwidth@>\maxrwidth@\global\maxrwidth@\rwidth@\fi %9
+ \global\advance\and@\@ne %10
+ &\Tag@
+ \eat@{##}\crcr#1\crcr}}% %11
+ \totwidth@\maxlwidth@\advance\totwidth@\maxrwidth@} %12
+\def\displ@y@{\global\dt@ptrue\openup\jot
+ \everycr{\noalign{\global\tag@false\global\and@\z@\ifdt@p\global\dt@pfalse
+ \vskip-\lineskiplimit\vskip\normallineskiplimit\else
+ \penalty\interdisplaylinepenalty\fi}}}
+\def\black@#1{\noalign{\ifdim#1>\displaywidth
+ \dimen@\prevdepth\nointerlineskip %1
+ \vskip-\ht\strutbox@\vskip-\dp\strutbox@ %2
+ \vbox{\noindent\hbox to#1{\strut@\hfill}}% %3
+ \prevdepth\dimen@ %4
+ \fi}}
+\expandafter\def\csname align \space\endcsname#1\endalign
+ {\measure@#1\endalign\global\and@\z@ %1
+ \ifingather@\everycr{\noalign{\global\and@\z@}}\else\displ@y@\fi %2
+ \Let@\tabskip\centering@ %3
+ \halign to\displaywidth
+ {\hfil\strut@\setboxz@h{$\m@th\displaystyle{\@lign##}$}% %4
+ \global\lwidth@\wdz@\boxz@\global\advance\and@\@ne %5
+ \tabskip\z@skip %6
+ &\setboxz@h{$\m@th\displaystyle{{}\@lign##}$}% %7
+ \global\rwidth@\wdz@\boxz@\hfill\global\advance\and@\@ne %8
+ \tabskip\centering@ %9
+ &\setboxz@h{\@lign\strut@\maketag@##\maketag@}% %10
+ \dimen@\displaywidth\advance\dimen@-\totwidth@
+ \divide\dimen@\tw@\advance\dimen@\maxrwidth@\advance\dimen@-\rwidth@ %11
+ \ifdim\dimen@<\tw@\wdz@\llap{\vtop{\normalbaselines\null\boxz@}}% %12
+ \else\llap{\boxz@}\fi %13
+ \tabskip\z@skip %14
+ \crcr#1\crcr %15
+ \black@\totwidth@}} %16
+\newdimen\lineht@
+\expandafter\def\csname align \endcsname#1\endalign{\measure@#1\endalign
+ \global\and@\z@
+ \ifdim\totwidth@>\displaywidth\let\displaywidth@\totwidth@\else
+ \let\displaywidth@\displaywidth\fi %1
+ \ifingather@\everycr{\noalign{\global\and@\z@}}\else\displ@y@\fi
+ \Let@\tabskip\centering@\halign to\displaywidth
+ {\hfil\strut@\setboxz@h{$\m@th\displaystyle{\@lign##}$}%
+ \global\lwidth@\wdz@\global\lineht@\ht\z@ %2
+ \boxz@\global\advance\and@\@ne
+ \tabskip\z@skip&\setboxz@h{$\m@th\displaystyle{{}\@lign##}$}%
+ \global\rwidth@\wdz@\ifdim\ht\z@>\lineht@\global\lineht@\ht\z@\fi %3
+ \boxz@\hfil\global\advance\and@\@ne
+ \tabskip\centering@&\kern-\displaywidth@ %4
+ \setboxz@h{\@lign\strut@\maketag@##\maketag@}%
+ \dimen@\displaywidth\advance\dimen@-\totwidth@
+ \divide\dimen@\tw@\advance\dimen@\maxlwidth@\advance\dimen@-\lwidth@
+ \ifdim\dimen@<\tw@\wdz@
+ \rlap{\vbox{\normalbaselines\boxz@\vbox to\lineht@{}}}\else
+ \rlap{\boxz@}\fi
+ \tabskip\displaywidth@\crcr#1\crcr\black@\totwidth@}}
+\expandafter\def\csname align (in \string\gather)\endcsname
+ #1\endalign{\vcenter{\align@#1\endalign}}
+\Invalid@\endalign
+\newif\ifxat@
+\def\alignat{\RIfMIfI@\DN@{\onlydmatherr@\alignat}\else
+ \DN@{\csname alignat \endcsname}\fi\else
+ \DN@{\onlydmatherr@\alignat}\fi\next@}
+\newif\ifmeasuring@
+\newbox\savealignat@
+\expandafter\def\csname alignat \endcsname#1#2\endalignat %1
+ {\inany@true\xat@false
+ \def\tag{\global\tag@true\count@#1\relax\multiply\count@\tw@
+ \xdef\tag@{}\loop\ifnum\count@>\and@\xdef\tag@{&\tag@}\advance\count@\m@ne
+ \repeat\tag@}%
+ \vspace@\allowdisplaybreak@\displaybreak@\intertext@
+ \displ@y@\measuring@true %2
+ \setbox\savealignat@\hbox{$\m@th\displaystyle\Let@
+ \attag@{#1}% %3
+ \vbox{\halign{\span\preamble@@\crcr#2\crcr}}$}%
+ \measuring@false %4
+ \Let@\attag@{#1}% %5
+ \tabskip\centering@\halign to\displaywidth
+ {\span\preamble@@\crcr#2\crcr %6
+ \black@{\wd\savealignat@}}} %7
+\Invalid@\endalignat
+\def\xalignat{\RIfMIfI@
+ \DN@{\onlydmatherr@\xalignat}\else
+ \DN@{\csname xalignat \endcsname}\fi\else
+ \DN@{\onlydmatherr@\xalignat}\fi\next@}
+\expandafter\def\csname xalignat \endcsname#1#2\endxalignat
+ {\inany@true\xat@true
+ \def\tag{\global\tag@true\def\tag@{}\count@#1\relax\multiply\count@\tw@
+ \loop\ifnum\count@>\and@\xdef\tag@{&\tag@}\advance\count@\m@ne\repeat\tag@}%
+ \vspace@\allowdisplaybreak@\displaybreak@\intertext@
+ \displ@y@\measuring@true\setbox\savealignat@\hbox{$\m@th\displaystyle\Let@
+ \attag@{#1}\vbox{\halign{\span\preamble@@\crcr#2\crcr}}$}%
+ \measuring@false\Let@
+ \attag@{#1}\tabskip\centering@\halign to\displaywidth
+ {\span\preamble@@\crcr#2\crcr\black@{\wd\savealignat@}}}
+\def\attag@#1{\let\Maketag@\maketag@\let\TAG@\Tag@ %1
+ \let\Tag@=0\let\maketag@=0% %2
+ \ifmeasuring@\def\llap@##1{\setboxz@h{##1}\hbox to\tw@\wdz@{}}%
+ \def\rlap@##1{\setboxz@h{##1}\hbox to\tw@\wdz@{}}\else
+ \let\llap@\llap\let\rlap@\rlap\fi %3
+ \toks@{\hfil\strut@$\m@th\displaystyle{\@lign\the\hashtoks@}$\tabskip\z@skip
+ \global\advance\and@\@ne&$\m@th\displaystyle{{}\@lign\the\hashtoks@}$\hfil
+ \ifxat@\tabskip\centering@\fi\global\advance\and@\@ne}% %4
+ \iftagsleft@
+ \toks@@{\tabskip\centering@&\Tag@\kern-\displaywidth
+ \rlap@{\@lign\maketag@\the\hashtoks@\maketag@}%
+ \global\advance\and@\@ne\tabskip\displaywidth}\else
+ \toks@@{\tabskip\centering@&\Tag@\llap@{\@lign\maketag@
+ \the\hashtoks@\maketag@}\global\advance\and@\@ne\tabskip\z@skip}\fi %5
+ \atcount@#1\relax\advance\atcount@\m@ne
+ \loop\ifnum\atcount@>\z@
+ \toks@=\expandafter{\the\toks@&\hfil$\m@th\displaystyle{\@lign
+ \the\hashtoks@}$\global\advance\and@\@ne
+ \tabskip\z@skip&$\m@th\displaystyle{{}\@lign\the\hashtoks@}$\hfil\ifxat@
+ \tabskip\centering@\fi\global\advance\and@\@ne}\advance\atcount@\m@ne
+ \repeat %6
+ \xdef\preamble@{\the\toks@\the\toks@@}% %7
+ \xdef\preamble@@{\preamble@}% %8
+ \let\maketag@\Maketag@\let\Tag@\TAG@} %9
+\Invalid@\endxalignat
+\def\xxalignat{\RIfMIfI@
+ \DN@{\onlydmatherr@\xxalignat}\else\DN@{\csname xxalignat
+ \endcsname}\fi\else
+ \DN@{\onlydmatherr@\xxalignat}\fi\next@}
+\expandafter\def\csname xxalignat \endcsname#1#2\endxxalignat{\inany@true
+ \vspace@\allowdisplaybreak@\displaybreak@\intertext@
+ \displ@y\setbox\savealignat@\hbox{$\m@th\displaystyle\Let@
+ \xxattag@{#1}\vbox{\halign{\span\preamble@@\crcr#2\crcr}}$}%
+ \Let@\xxattag@{#1}\tabskip\z@skip\halign to\displaywidth
+ {\span\preamble@@\crcr#2\crcr\black@{\wd\savealignat@}}}
+\def\xxattag@#1{\toks@{\tabskip\z@skip\hfil\strut@
+ $\m@th\displaystyle{\the\hashtoks@}$&%
+ $\m@th\displaystyle{{}\the\hashtoks@}$\hfil\tabskip\centering@&}%
+ \atcount@#1\relax\advance\atcount@\m@ne\loop\ifnum\atcount@>\z@
+ \toks@=\expandafter{\the\toks@&\hfil$\m@th\displaystyle{\the\hashtoks@}$%
+ \tabskip\z@skip&$\m@th\displaystyle{{}\the\hashtoks@}$\hfil
+ \tabskip\centering@}\advance\atcount@\m@ne\repeat
+ \xdef\preamble@{\the\toks@\tabskip\z@skip}\xdef\preamble@@{\preamble@}}
+\Invalid@\endxxalignat
+\newdimen\gwidth@
+\newdimen\gmaxwidth@
+\def\gmeasure@#1\endgather{\gwidth@\z@\gmaxwidth@\z@\setbox@ne\vbox{\Let@
+ \halign{\setboxz@h{$\m@th\displaystyle{##}$}\global\gwidth@\wdz@
+ \ifdim\gwidth@>\gmaxwidth@\global\gmaxwidth@\gwidth@\fi
+ &\eat@{##}\crcr#1\crcr}}}
+\def\gather{\RIfMIfI@\DN@{\onlydmatherr@\gather}\else
+ \ingather@true\inany@true\def\tag{&}%
+ \vspace@\allowdisplaybreak@\displaybreak@\intertext@
+ \displ@y\Let@
+ \iftagsleft@\DN@{\csname gather \endcsname}\else
+ \DN@{\csname gather \space\endcsname}\fi\fi
+ \else\DN@{\onlydmatherr@\gather}\fi\next@}
+\expandafter\def\csname gather \space\endcsname#1\endgather
+ {\gmeasure@#1\endgather\tabskip\centering@
+ \halign to\displaywidth{\hfil\strut@\setboxz@h{$\m@th\displaystyle{##}$}%
+ \global\gwidth@\wdz@\boxz@\hfil&
+ \setboxz@h{\strut@{\maketag@##\maketag@}}%
+ \dimen@\displaywidth\advance\dimen@-\gwidth@
+ \ifdim\dimen@>\tw@\wdz@\llap{\boxz@}\else
+ \llap{\vtop{\normalbaselines\null\boxz@}}\fi
+ \tabskip\z@skip\crcr#1\crcr\black@\gmaxwidth@}}
+\newdimen\glineht@
+\expandafter\def\csname gather \endcsname#1\endgather{\gmeasure@#1\endgather
+ \ifdim\gmaxwidth@>\displaywidth\let\gdisplaywidth@\gmaxwidth@\else
+ \let\gdisplaywidth@\displaywidth\fi\tabskip\centering@\halign to\displaywidth
+ {\hfil\strut@\setboxz@h{$\m@th\displaystyle{##}$}%
+ \global\gwidth@\wdz@\global\glineht@\ht\z@\boxz@\hfil&\kern-\gdisplaywidth@
+ \setboxz@h{\strut@{\maketag@##\maketag@}}%
+ \dimen@\displaywidth\advance\dimen@-\gwidth@
+ \ifdim\dimen@>\tw@\wdz@\rlap{\boxz@}\else
+ \rlap{\vbox{\normalbaselines\boxz@\vbox to\glineht@{}}}\fi
+ \tabskip\gdisplaywidth@\crcr#1\crcr\black@\gmaxwidth@}}
+\newif\ifctagsplit@
+\def\CenteredTagsOnSplits{\global\ctagsplit@true}
+\def\TopOrBottomTagsOnSplits{\global\ctagsplit@false}
+\TopOrBottomTagsOnSplits
+\def\split{\relax\ifinany@\let\next@\insplit@\else
+ \ifmmode\ifinner\def\next@{\onlydmatherr@\split}\else
+ \let\next@\outsplit@\fi\else
+ \def\next@{\onlydmatherr@\split}\fi\fi\next@}
+\def\insplit@{\global\setbox\z@\vbox\bgroup\vspace@\Let@\ialign\bgroup
+ \hfil\strut@$\m@th\displaystyle{##}$&$\m@th\displaystyle{{}##}$\hfill\crcr}
+\def\endsplit{\crcr\egroup\egroup\iftagsleft@\expandafter\lendsplit@\else
+ \expandafter\rendsplit@\fi}
+\def\rendsplit@{\global\setbox9 \vbox
+ {\unvcopy\z@\global\setbox8 \lastbox\unskip}% %1
+ \setbox@ne\hbox{\unhcopy8 \unskip\global\setbox\tw@\lastbox
+ \unskip\global\setbox\thr@@\lastbox}% %2
+ \global\setbox7 \hbox{\unhbox\tw@\unskip}% %3
+ \ifinalign@\ifctagsplit@ %4
+ \gdef\split@{\hbox to\wd\thr@@{}&
+ \vcenter{\vbox{\moveleft\wd\thr@@\boxz@}}}% %5
+ \else\gdef\split@{&\vbox{\moveleft\wd\thr@@\box9}\crcr
+ \box\thr@@&\box7}\fi %6
+ \else %7
+ \ifctagsplit@\gdef\split@{\vcenter{\boxz@}}\else
+ \gdef\split@{\box9\crcr\hbox{\box\thr@@\box7}}\fi
+ \fi
+ \split@} %8
+\def\lendsplit@{\global\setbox9\vtop{\unvcopy\z@}% %1
+ \setbox@ne\vbox{\unvcopy\z@\global\setbox8\lastbox}% %2
+ \setbox@ne\hbox{\unhcopy8\unskip\setbox\tw@\lastbox
+ \unskip\global\setbox\thr@@\lastbox}% %3
+ \ifinalign@\ifctagsplit@ %4
+ \gdef\split@{\hbox to\wd\thr@@{}&
+ \vcenter{\vbox{\moveleft\wd\thr@@\box9}}}% %5
+ \else %6
+ \gdef\split@{\hbox to\wd\thr@@{}&\vbox{\moveleft\wd\thr@@\box9}}\fi
+ \else
+ \ifctagsplit@\gdef\split@{\vcenter{\box9}}\else
+ \gdef\split@{\box9}\fi
+ \fi\split@}
+\def\outsplit@#1$${\align\insplit@#1\endalign$$}
+\newdimen\multlinegap@
+\multlinegap@1em
+\newdimen\multlinetaggap@
+\multlinetaggap@1em
+\def\MultlineGap#1{\global\multlinegap@#1\relax}
+\def\multlinegap#1{\RIfMIfI@\onlydmatherr@\multlinegap\else
+ \multlinegap@#1\relax\fi\else\onlydmatherr@\multlinegap\fi}
+\def\nomultlinegap{\multlinegap{\z@}}
+\def\multline{\RIfMIfI@
+ \DN@{\onlydmatherr@\multline}\else
+ \DN@{\multline@}\fi\else
+ \DN@{\onlydmatherr@\multline}\fi\next@}
+\newif\iftagin@
+\def\tagin@#1{\tagin@false\in@\tag{#1}\ifin@\tagin@true\fi}
+\def\multline@#1$${\inany@true\vspace@\allowdisplaybreak@\displaybreak@
+ \tagin@{#1}\iftagsleft@\DN@{\multline@l#1$$}\else
+ \DN@{\multline@r#1$$}\fi\next@}
+\newdimen\mwidth@
+\def\rmmeasure@#1\endmultline{%
+ \def\shoveleft##1{##1}\def\shoveright##1{##1}% %1
+ \setbox@ne\vbox{\Let@\halign{\setboxz@h
+ {$\m@th\@lign\displaystyle{}##$}\global\mwidth@\wdz@
+ \crcr#1\crcr}}}
+\newdimen\mlineht@
+\newif\ifzerocr@
+\newif\ifonecr@
+\def\lmmeasure@#1\endmultline{\global\zerocr@true\global\onecr@false
+ \everycr{\noalign{\ifonecr@\global\onecr@false\fi
+ \ifzerocr@\global\zerocr@false\global\onecr@true\fi}}% %1
+ \def\shoveleft##1{##1}\def\shoveright##1{##1}%
+ \setbox@ne\vbox{\Let@\halign{\setboxz@h
+ {$\m@th\@lign\displaystyle{}##$}\ifonecr@\global\mwidth@\wdz@
+ \global\mlineht@\ht\z@\fi\crcr#1\crcr}}}
+\newbox\mtagbox@
+\newdimen\ltwidth@
+\newdimen\rtwidth@
+\def\multline@l#1$${\iftagin@\DN@{\lmultline@@#1$$}\else
+ \DN@{\setbox\mtagbox@\null\ltwidth@\z@\rtwidth@\z@
+ \lmultline@@@#1$$}\fi\next@}
+\def\lmultline@@#1\endmultline\tag#2$${%
+ \setbox\mtagbox@\hbox{\maketag@#2\maketag@}% %1
+ \lmmeasure@#1\endmultline\dimen@\mwidth@\advance\dimen@\wd\mtagbox@
+ \advance\dimen@\multlinetaggap@ %2
+ \ifdim\dimen@>\displaywidth\ltwidth@\z@\else\ltwidth@\wd\mtagbox@\fi %3
+ \lmultline@@@#1\endmultline$$}
+\def\lmultline@@@{\displ@y
+ \def\shoveright##1{##1\hfilneg\hskip\multlinegap@}%
+ \def\shoveleft##1{\setboxz@h{$\m@th\displaystyle{}##1$}%
+ \setbox@ne\hbox{$\m@th\displaystyle##1$}%
+ \hfilneg
+ \iftagin@
+ \ifdim\ltwidth@>\z@\hskip\ltwidth@\hskip\multlinetaggap@\fi
+ \else\hskip\multlinegap@\fi\hskip.5\wd@ne\hskip-.5\wdz@##1}% %1
+ \halign\bgroup\Let@\hbox to\displaywidth
+ {\strut@$\m@th\displaystyle\hfil{}##\hfil$}\crcr
+ \hfilneg %2
+ \iftagin@ %3
+ \ifdim\ltwidth@>\z@ %4
+ \box\mtagbox@\hskip\multlinetaggap@ %5
+ \else
+ \rlap{\vbox{\normalbaselines\hbox{\strut@\box\mtagbox@}%
+ \vbox to\mlineht@{}}}\fi %6
+ \else\hskip\multlinegap@\fi} %7
+\def\multline@r#1$${\iftagin@\DN@{\rmultline@@#1$$}\else
+ \DN@{\setbox\mtagbox@\null\ltwidth@\z@\rtwidth@\z@
+ \rmultline@@@#1$$}\fi\next@}
+\def\rmultline@@#1\endmultline\tag#2$${\ltwidth@\z@
+ \setbox\mtagbox@\hbox{\maketag@#2\maketag@}%
+ \rmmeasure@#1\endmultline\dimen@\mwidth@\advance\dimen@\wd\mtagbox@
+ \advance\dimen@\multlinetaggap@
+ \ifdim\dimen@>\displaywidth\rtwidth@\z@\else\rtwidth@\wd\mtagbox@\fi
+ \rmultline@@@#1\endmultline$$}
+\def\rmultline@@@{\displ@y
+ \def\shoveright##1{##1\hfilneg\iftagin@\ifdim\rtwidth@>\z@
+ \hskip\rtwidth@\hskip\multlinetaggap@\fi\else\hskip\multlinegap@\fi}%
+ \def\shoveleft##1{\setboxz@h{$\m@th\displaystyle{}##1$}%
+ \setbox@ne\hbox{$\m@th\displaystyle##1$}%
+ \hfilneg\hskip\multlinegap@\hskip.5\wd@ne\hskip-.5\wdz@##1}%
+ \halign\bgroup\Let@\hbox to\displaywidth
+ {\strut@$\m@th\displaystyle\hfil{}##\hfil$}\crcr
+ \hfilneg\hskip\multlinegap@}
+\def\endmultline{\iftagsleft@\expandafter\lendmultline@\else
+ \expandafter\rendmultline@\fi}
+\def\lendmultline@{\hfilneg\hskip\multlinegap@\crcr\egroup}
+\def\rendmultline@{\iftagin@ %1
+ \ifdim\rtwidth@>\z@ %2
+ \hskip\multlinetaggap@\box\mtagbox@ %3
+ \else\llap{\vtop{\normalbaselines\null\hbox{\strut@\box\mtagbox@}}}\fi %4
+ \else\hskip\multlinegap@\fi %5
+ \hfilneg\crcr\egroup}
+\def\bmod{\mskip-\medmuskip\mkern5mu\mathbin{\fam\z@ mod}\penalty900
+ \mkern5mu\mskip-\medmuskip}
+\def\pmod#1{\allowbreak\ifinner\mkern8mu\else\mkern18mu\fi
+ ({\fam\z@ mod}\,\,#1)}
+\def\pod#1{\allowbreak\ifinner\mkern8mu\else\mkern18mu\fi(#1)}
+\def\mod#1{\allowbreak\ifinner\mkern12mu\else\mkern18mu\fi{\fam\z@ mod}\,\,#1}
+\message{continued fractions,}
+\newcount\cfraccount@
+\def\cfrac{\bgroup\bgroup\advance\cfraccount@\@ne\strut
+ \iffalse{\fi\def\\{\over\displaystyle}\iffalse}\fi}
+\def\lcfrac{\bgroup\bgroup\advance\cfraccount@\@ne\strut
+ \iffalse{\fi\def\\{\hfill\over\displaystyle}\iffalse}\fi}
+\def\rcfrac{\bgroup\bgroup\advance\cfraccount@\@ne\strut\hfill
+ \iffalse{\fi\def\\{\over\displaystyle}\iffalse}\fi}
+\def\gloop@#1\repeat{\gdef\body{#1}\iterate}
+\def\endcfrac{\gloop@\ifnum\cfraccount@>\z@\global\advance\cfraccount@\m@ne
+ \egroup\hskip-\nulldelimiterspace\egroup\repeat}
+\message{compound symbols,}
+\def\binrel@#1{\setboxz@h{\thinmuskip0mu
+ \medmuskip\m@ne mu\thickmuskip\@ne mu$#1\m@th$}%
+ \setbox@ne\hbox{\thinmuskip0mu\medmuskip\m@ne mu\thickmuskip
+ \@ne mu${}#1{}\m@th$}%
+ \setbox\tw@\hbox{\hskip\wd@ne\hskip-\wdz@}}
+\def\overset#1\to#2{\binrel@{#2}\ifdim\wd\tw@<\z@
+ \mathbin{\mathop{\kern\z@#2}\limits^{#1}}\else\ifdim\wd\tw@>\z@
+ \mathrel{\mathop{\kern\z@#2}\limits^{#1}}\else
+ {\mathop{\kern\z@#2}\limits^{#1}}{}\fi\fi}
+\def\underset#1\to#2{\binrel@{#2}\ifdim\wd\tw@<\z@
+ \mathbin{\mathop{\kern\z@#2}\limits_{#1}}\else\ifdim\wd\tw@>\z@
+ \mathrel{\mathop{\kern\z@#2}\limits_{#1}}\else
+ {\mathop{\kern\z@#2}\limits_{#1}}{}\fi\fi}
+\def\oversetbrace#1\to#2{\overbrace{#2}^{#1}}
+\def\undersetbrace#1\to#2{\underbrace{#2}_{#1}}
+\def\sideset#1\and#2\to#3{%
+ \setbox@ne\hbox{$\dsize{\vphantom{#3}}#1{#3}\m@th$}%
+ \setbox\tw@\hbox{$\dsize{#3}#2\m@th$}%
+ \hskip\wd@ne\hskip-\wd\tw@\mathop{\hskip\wd\tw@\hskip-\wd@ne
+ {\vphantom{#3}}#1{#3}#2}}
+\def\rightarrowfill@#1{\setboxz@h{$#1-\m@th$}\ht\z@\z@
+ $#1\m@th\copy\z@\mkern-6mu\cleaders
+ \hbox{$#1\mkern-2mu\box\z@\mkern-2mu$}\hfill
+ \mkern-6mu\mathord\rightarrow$}
+\def\leftarrowfill@#1{\setboxz@h{$#1-\m@th$}\ht\z@\z@
+ $#1\m@th\mathord\leftarrow\mkern-6mu\cleaders
+ \hbox{$#1\mkern-2mu\copy\z@\mkern-2mu$}\hfill
+ \mkern-6mu\box\z@$}
+\def\leftrightarrowfill@#1{\setboxz@h{$#1-\m@th$}\ht\z@\z@
+ $#1\m@th\mathord\leftarrow\mkern-6mu\cleaders
+ \hbox{$#1\mkern-2mu\box\z@\mkern-2mu$}\hfill
+ \mkern-6mu\mathord\rightarrow$}
+\def\overrightarrow{\mathpalette\overrightarrow@}
+\def\overrightarrow@#1#2{\vbox{\ialign{##\crcr\rightarrowfill@#1\crcr
+ \noalign{\kern-\ex@\nointerlineskip}$\m@th\hfil#1#2\hfil$\crcr}}}
+\let\overarrow\overrightarrow
+\def\overleftarrow{\mathpalette\overleftarrow@}
+\def\overleftarrow@#1#2{\vbox{\ialign{##\crcr\leftarrowfill@#1\crcr
+ \noalign{\kern-\ex@\nointerlineskip}$\m@th\hfil#1#2\hfil$\crcr}}}
+\def\overleftrightarrow{\mathpalette\overleftrightarrow@}
+\def\overleftrightarrow@#1#2{\vbox{\ialign{##\crcr\leftrightarrowfill@#1\crcr
+ \noalign{\kern-\ex@\nointerlineskip}$\m@th\hfil#1#2\hfil$\crcr}}}
+\def\underrightarrow{\mathpalette\underrightarrow@}
+\def\underrightarrow@#1#2{\vtop{\ialign{##\crcr$\m@th\hfil#1#2\hfil$\crcr
+ \noalign{\nointerlineskip}\rightarrowfill@#1\crcr}}}
+\let\underarrow\underrightarrow
+\def\underleftarrow{\mathpalette\underleftarrow@}
+\def\underleftarrow@#1#2{\vtop{\ialign{##\crcr$\m@th\hfil#1#2\hfil$\crcr
+ \noalign{\nointerlineskip}\leftarrowfill@#1\crcr}}}
+\def\underleftrightarrow{\mathpalette\underleftrightarrow@}
+\def\underleftrightarrow@#1#2{\vtop{\ialign{##\crcr$\m@th\hfil#1#2\hfil$\crcr
+ \noalign{\nointerlineskip}\leftrightarrowfill@#1\crcr}}}
+\message{various kinds of dots,}
+\let\DOTSI\relax
+\let\DOTSB\relax
+\let\DOTSX\relax
+\newif\ifmath@
+{\uccode`7=`\\ \uccode`8=`m \uccode`9=`a \uccode`0=`t \uccode`!=`h
+ \uppercase{\gdef\math@#1#2#3#4#5#6\math@{\global\math@false\ifx 7#1\ifx 8#2%
+ \ifx 9#3\ifx 0#4\ifx !#5\xdef\meaning@{#6}\global\math@true\fi\fi\fi\fi\fi}}}
+\newif\ifmathch@
+{\uccode`7=`c \uccode`8=`h \uccode`9=`\"
+ \uppercase{\gdef\mathch@#1#2#3#4#5#6\mathch@{\global\mathch@false
+ \ifx 7#1\ifx 8#2\ifx 9#5\global\mathch@true\xdef\meaning@{9#6}\fi\fi\fi}}}
+\newcount\classnum@
+\def\getmathch@#1.#2\getmathch@{\classnum@#1 \divide\classnum@4096
+ \ifcase\number\classnum@\or\or\gdef\thedots@{\dotsb@}\or
+ \gdef\thedots@{\dotsb@}\fi}
+\newif\ifmathbin@
+{\uccode`4=`b \uccode`5=`i \uccode`6=`n
+ \uppercase{\gdef\mathbin@#1#2#3{\relaxnext@
+ \DNii@##1\mathbin@{\ifx\space@\next\global\mathbin@true\fi}%
+ \global\mathbin@false\DN@##1\mathbin@{}%
+ \ifx 4#1\ifx 5#2\ifx 6#3\DN@{\FN@\nextii@}\fi\fi\fi\next@}}}
+\newif\ifmathrel@
+{\uccode`4=`r \uccode`5=`e \uccode`6=`l
+ \uppercase{\gdef\mathrel@#1#2#3{\relaxnext@
+ \DNii@##1\mathrel@{\ifx\space@\next\global\mathrel@true\fi}%
+ \global\mathrel@false\DN@##1\mathrel@{}%
+ \ifx 4#1\ifx 5#2\ifx 6#3\DN@{\FN@\nextii@}\fi\fi\fi\next@}}}
+\newif\ifmacro@
+{\uccode`5=`m \uccode`6=`a \uccode`7=`c
+ \uppercase{\gdef\macro@#1#2#3#4\macro@{\global\macro@false
+ \ifx 5#1\ifx 6#2\ifx 7#3\global\macro@true
+ \xdef\meaning@{\macro@@#4\macro@@}\fi\fi\fi}}}
+\def\macro@@#1->#2\macro@@{#2}
+\newif\ifDOTS@
+\newcount\DOTSCASE@
+{\uccode`6=`\\ \uccode`7=`D \uccode`8=`O \uccode`9=`T \uccode`0=`S
+ \uppercase{\gdef\DOTS@#1#2#3#4#5{\global\DOTS@false\DN@##1\DOTS@{}%
+ \ifx 6#1\ifx 7#2\ifx 8#3\ifx 9#4\ifx 0#5\let\next@\DOTS@@\fi\fi\fi\fi\fi
+ \next@}}}
+{\uccode`3=`B \uccode`4=`I \uccode`5=`X
+ \uppercase{\gdef\DOTS@@#1{\relaxnext@
+ \DNii@##1\DOTS@{\ifx\space@\next\global\DOTS@true\fi}%
+ \DN@{\FN@\nextii@}%
+ \ifx 3#1\global\DOTSCASE@\z@\else
+ \ifx 4#1\global\DOTSCASE@\@ne\else
+ \ifx 5#1\global\DOTSCASE@\tw@\else\DN@##1\DOTS@{}%
+ \fi\fi\fi\next@}}}
+\newif\ifnot@
+{\uccode`5=`\\ \uccode`6=`n \uccode`7=`o \uccode`8=`t
+ \uppercase{\gdef\not@#1#2#3#4{\relaxnext@
+ \DNii@##1\not@{\ifx\space@\next\global\not@true\fi}%
+ \global\not@false\DN@##1\not@{}%
+ \ifx 5#1\ifx 6#2\ifx 7#3\ifx 8#4\DN@{\FN@\nextii@}\fi\fi\fi
+ \fi\next@}}}
+\newif\ifkeybin@
+\def\keybin@{\keybin@true
+ \ifx\next+\else\ifx\next=\else\ifx\next<\else\ifx\next>\else\ifx\next-\else
+ \ifx\next*\else\ifx\next:\else\keybin@false\fi\fi\fi\fi\fi\fi\fi}
+\def\dots{\RIfM@\expandafter\mdots@\else\expandafter\tdots@\fi}
+\def\tdots@{\unskip\relaxnext@
+ \DN@{$\m@th\mathinner{\ldotp\ldotp\ldotp}\,
+ \ifx\next,\,$\else\ifx\next.\,$\else\ifx\next;\,$\else\ifx\next:\,$\else
+ \ifx\next?\,$\else\ifx\next!\,$\else$ \fi\fi\fi\fi\fi\fi}%
+ \ \FN@\next@}
+\def\mdots@{\FN@\mdots@@}
+\def\mdots@@{\gdef\thedots@{\dotso@}% %1
+ \ifx\next\boldkey\gdef\thedots@\boldkey{\boldkeydots@}\else %2
+ \ifx\next\boldsymbol\gdef\thedots@\boldsymbol{\boldsymboldots@}\else %3
+ \ifx,\next\gdef\thedots@{\dotsc}% %4
+ \else\ifx\not\next\gdef\thedots@{\dotsb@}% %5
+ \else\keybin@
+ \ifkeybin@\gdef\thedots@{\dotsb@}% %6
+ \else\xdef\meaning@{\meaning\next..........}\xdef\meaning@@{\meaning@}% %7
+ \expandafter\math@\meaning@\math@
+ \ifmath@
+ \expandafter\mathch@\meaning@\mathch@
+ \ifmathch@\expandafter\getmathch@\meaning@\getmathch@\fi %8
+ \else\expandafter\macro@\meaning@@\macro@ %9
+ \ifmacro@ %10
+ \expandafter\not@\meaning@\not@\ifnot@\gdef\thedots@{\dotsb@}% %11
+ \else\expandafter\DOTS@\meaning@\DOTS@
+ \ifDOTS@
+ \ifcase\number\DOTSCASE@\gdef\thedots@{\dotsb@}%
+ \or\gdef\thedots@{\dotsi}\else\fi %12
+ \else\expandafter\math@\meaning@\math@ %13
+ \ifmath@\expandafter\mathbin@\meaning@\mathbin@
+ \ifmathbin@\gdef\thedots@{\dotsb@}% %14
+ \else\expandafter\mathrel@\meaning@\mathrel@
+ \ifmathrel@\gdef\thedots@{\dotsb@}% %15
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \thedots@}
+\def\plainldots@{\mathinner{\ldotp\ldotp\ldotp}}
+\def\plaincdots@{\mathinner{\cdotp\cdotp\cdotp}}
+\def\dotsi{\!\plaincdots@}
+\let\dotsb@\plaincdots@
+\newif\ifextra@
+\newif\ifrightdelim@
+\def\rightdelim@{\global\rightdelim@true %1
+ \ifx\next)\else %2
+ \ifx\next]\else
+ \ifx\next\rbrack\else
+ \ifx\next\}\else
+ \ifx\next\rbrace\else
+ \ifx\next\rangle\else
+ \ifx\next\rceil\else
+ \ifx\next\rfloor\else
+ \ifx\next\rgroup\else
+ \ifx\next\rmoustache\else
+ \ifx\next\right\else
+ \ifx\next\bigr\else
+ \ifx\next\biggr\else
+ \ifx\next\Bigr\else %3
+ \ifx\next\Biggr\else\global\rightdelim@false
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
+\def\extra@{%
+ \global\extra@false\rightdelim@\ifrightdelim@\global\extra@true %1
+ \else\ifx\next$\global\extra@true %2
+ \else\xdef\meaning@{\meaning\next..........}% %3
+ \expandafter\macro@\meaning@\macro@\ifmacro@ %4
+ \expandafter\DOTS@\meaning@\DOTS@
+ \ifDOTS@
+ \ifnum\DOTSCASE@=\tw@\global\extra@true %5
+ \fi\fi\fi\fi\fi}
+\newif\ifbold@
+\def\dotso@{\relaxnext@
+ \ifbold@
+ \let\next\delayed@
+ \DNii@{\extra@\plainldots@\ifextra@\,\fi}%
+ \else
+ \DNii@{\DN@{\extra@\plainldots@\ifextra@\,\fi}\FN@\next@}%
+ \fi
+ \nextii@}
+\def\extrap@#1{%
+ \ifx\next,\DN@{#1\,}\else
+ \ifx\next;\DN@{#1\,}\else
+ \ifx\next.\DN@{#1\,}\else\extra@
+ \ifextra@\DN@{#1\,}\else
+ \let\next@#1\fi\fi\fi\fi\next@}
+\def\ldots{\DN@{\extrap@\plainldots@}%
+ \FN@\next@}
+\def\cdots{\DN@{\extrap@\plaincdots@}%
+ \FN@\next@}
+\let\dotso\ldots
+\let\dotsb\cdots
+\let\dotsm\dotsb
+\def\dotsc{\relaxnext@
+ \DN@{\ifx\next;\plainldots@\,\else
+ \ifx\next.\plainldots@\,\else\extra@\plainldots@
+ \ifextra@\,\fi\fi\fi}%
+ \FN@\next@}
+\def\cdot{\mathchar"2201 }
+\def\longrightarrow{\DOTSB\relbar\joinrel\rightarrow}
+\def\Longrightarrow{\DOTSB\Relbar\joinrel\Rightarrow}
+\def\longleftarrow{\DOTSB\leftarrow\joinrel\relbar}
+\def\Longleftarrow{\DOTSB\Leftarrow\joinrel\Relbar}
+\def\longleftrightarrow{\DOTSB\leftarrow\joinrel\rightarrow}
+\def\Longleftrightarrow{\DOTSB\Leftarrow\joinrel\Rightarrow}
+\def\mapsto{\DOTSB\mapstochar\rightarrow}
+\def\longmapsto{\DOTSB\mapstochar\longrightarrow}
+\def\hookrightarrow{\DOTSB\lhook\joinrel\rightarrow}
+\def\hookleftarrow{\DOTSB\leftarrow\joinrel\rhook}
+\def\doteq{\DOTSB\buildrel\textstyle.\over=}
+\def\iff{\DOTSB\;\Longleftrightarrow\;}
+\message{special superscripts,}
+\def\dddot#1{{\mathop{#1}\limits^{\vbox to-1.4\ex@{\kern-\tw@\ex@
+ \hbox{\rm...}\vss}}}}
+\def\ddddot#1{{\mathop{#1}\limits^{\vbox to-1.4\ex@{\kern-\tw@\ex@
+ \hbox{\rm....}\vss}}}}
+\def\sphat{^{\mathchoice{}{}%
+ {\,\,\botsmash{\hbox{\lower4\ex@\hbox{$\m@th\widehat{\null}$}}}}%
+ {\,\botsmash{\hbox{\lower3\ex@\hbox{$\m@th\hat{\null}$}}}}}}
+\def\spcheck{^\vee}
+\def\sptilde{^\sim}
+\def\spacute{^{\!\botsmash{\hbox{\lower\@ne ex\hbox{\'{}}}}}}
+\def\spgrave{^{\mathchoice{}{}{}{\!}%
+ \botsmash{\hbox{\lower\@ne ex\hbox{\`{}}}}}}
+\def\spdot{^{\hbox{\raise\ex@\hbox{\rm.}}}}
+\def\spddot{^{\hbox{\raise\ex@\hbox{\rm..}}}}
+\def\spdddot{^{\hbox{\raise\ex@\hbox{\rm...}}}}
+\def\spddddot{^{\hbox{\raise\ex@\hbox{\rm....}}}}
+\def\spbreve{^{\!\botsmash{\hbox{\lower4\ex@\hbox{\u{}}}}}}
+\def\spbar{^-}
+\def\spvec{^\rightarrow}
+\message{\string\text,}
+\def\textonlyfont@#1#2{\def#1{\RIfM@
+ \Err@{Use \string#1\space only in text}\else#2\fi}}
+\textonlyfont@\rm\tenrm
+\textonlyfont@\it\tenit
+\textonlyfont@\sl\tensl
+\textonlyfont@\bf\tenbf
+\def\oldnos#1{\RIfM@{\mathcode`\,="013B \fam\@ne#1}\else
+ \leavevmode\hbox{$\m@th\mathcode`\,="013B \fam\@ne#1$}\fi}
+\def\text{\RIfM@\expandafter\text@\else\expandafter\text@@\fi}
+\def\text@@#1{\leavevmode\hbox{#1}}
+\def\mathhexbox@#1#2#3{\text{$\m@th\mathchar"#1#2#3$}}
+\def\dag{{\mathhexbox@279}}
+\def\ddag{{\mathhexbox@27A}}
+\def\S{{\mathhexbox@278}}
+\def\P{{\mathhexbox@27B}}
+\newif\iffirstchoice@
+\firstchoice@true
+\def\text@#1{\mathchoice
+ {\hbox{\everymath{\displaystyle}\def\textfonti{\the\textfont\@ne}%
+ \def\textfontii{\the\textfont\tw@}\textdef@@ T#1}}
+ {\hbox{\firstchoice@false
+ \everymath{\textstyle}\def\textfonti{\the\textfont\@ne}%
+ \def\textfontii{\the\textfont\tw@}\textdef@@ T#1}}
+ {\hbox{\firstchoice@false
+ \everymath{\scriptstyle}\def\textfonti{\the\scriptfont\@ne}%
+ \def\textfontii{\the\scriptfont\tw@}\textdef@@ S\rm#1}}
+ {\hbox{\firstchoice@false
+ \everymath{\scriptscriptstyle}\def\textfonti
+ {\the\scriptscriptfont\@ne}%
+ \def\textfontii{\the\scriptscriptfont\tw@}\textdef@@ s\rm#1}}}
+\def\textdef@@#1{\textdef@#1\rm\textdef@#1\bf\textdef@#1\sl\textdef@#1\it}
+\def\rmfam{0}
+\def\textdef@#1#2{%
+ \DN@{\csname\expandafter\eat@\string#2fam\endcsname}%
+ \if S#1\edef#2{\the\scriptfont\next@\relax}%
+ \else\if s#1\edef#2{\the\scriptscriptfont\next@\relax}%
+ \else\edef#2{\the\textfont\next@\relax}\fi\fi}
+\scriptfont\itfam\tenit \scriptscriptfont\itfam\tenit
+\scriptfont\slfam\tensl \scriptscriptfont\slfam\tensl
+\newif\iftopfolded@
+\newif\ifbotfolded@
+\def\topfoldedtext{\topfolded@true\botfolded@false\foldedtext@}
+\def\botfoldedtext{\botfolded@true\topfolded@false\foldedtext@}
+\def\foldedtext{\topfolded@false\botfolded@false\foldedtext@}
+\Invalid@\foldedwidth
+\def\foldedtext@{\relaxnext@
+ \DN@{\ifx\next\foldedwidth\let\next@\nextii@\else
+ \DN@{\nextii@\foldedwidth{.3\hsize}}\fi\next@}%
+ \DNii@\foldedwidth##1##2{\setbox\z@\vbox
+ {\normalbaselines\hsize##1\relax
+ \tolerance1600 \noindent\ignorespaces##2}\ifbotfolded@\boxz@\else
+ \iftopfolded@\vtop{\unvbox\z@}\else\vcenter{\boxz@}\fi\fi}%
+ \FN@\next@}
+\message{math font commands,}
+\def\bold{\RIfM@\expandafter\bold@\else
+ \expandafter\nonmatherr@\expandafter\bold\fi}
+\def\bold@#1{{\bold@@{#1}}}
+\def\bold@@#1{\fam\bffam\relax#1}
+\def\slanted{\RIfM@\expandafter\slanted@\else
+ \expandafter\nonmatherr@\expandafter\slanted\fi}
+\def\slanted@#1{{\slanted@@{#1}}}
+\def\slanted@@#1{\fam\slfam\relax#1}
+\def\roman{\RIfM@\expandafter\roman@\else
+ \expandafter\nonmatherr@\expandafter\roman\fi}
+\def\roman@#1{{\roman@@{#1}}}
+\def\roman@@#1{\fam\rmfam\relax#1}
+\def\italic{\RIfM@\expandafter\italic@\else
+ \expandafter\nonmatherr@\expandafter\italic\fi}
+\def\italic@#1{{\italic@@{#1}}}
+\def\italic@@#1{\fam\itfam\relax#1}
+\def\Cal{\RIfM@\expandafter\Cal@\else
+ \expandafter\nonmatherr@\expandafter\Cal\fi}
+\def\Cal@#1{{\Cal@@{#1}}}
+\def\Cal@@#1{\noaccents@\fam\tw@#1}
+\mathchardef\Gamma="0000
+\mathchardef\Delta="0001
+\mathchardef\Theta="0002
+\mathchardef\Lambda="0003
+\mathchardef\Xi="0004
+\mathchardef\Pi="0005
+\mathchardef\Sigma="0006
+\mathchardef\Upsilon="0007
+\mathchardef\Phi="0008
+\mathchardef\Psi="0009
+\mathchardef\Omega="000A
+\mathchardef\varGamma="0100
+\mathchardef\varDelta="0101
+\mathchardef\varTheta="0102
+\mathchardef\varLambda="0103
+\mathchardef\varXi="0104
+\mathchardef\varPi="0105
+\mathchardef\varSigma="0106
+\mathchardef\varUpsilon="0107
+\mathchardef\varPhi="0108
+\mathchardef\varPsi="0109
+\mathchardef\varOmega="010A
+\let\alloc@@\alloc@
+\def\hexnumber@#1{\ifcase#1 0\or 1\or 2\or 3\or 4\or 5\or 6\or 7\or 8\or
+ 9\or A\or B\or C\or D\or E\or F\fi}
+\def\loadmsam{%
+ \font@\tenmsa=msam10
+ \font@\sevenmsa=msam7
+ \font@\fivemsa=msam5
+ \alloc@@8\fam\chardef\sixt@@n\msafam
+ \textfont\msafam=\tenmsa
+ \scriptfont\msafam=\sevenmsa
+ \scriptscriptfont\msafam=\fivemsa
+ \edef\next{\hexnumber@\msafam}%
+ \mathchardef\dabar@"0\next39
+ \edef\dashrightarrow{\mathrel{\dabar@\dabar@\mathchar"0\next4B}}%
+ \edef\dashleftarrow{\mathrel{\mathchar"0\next4C\dabar@\dabar@}}%
+ \let\dasharrow\dashrightarrow
+ \edef\ulcorner{\delimiter"4\next70\next70 }%
+ \edef\urcorner{\delimiter"5\next71\next71 }%
+ \edef\llcorner{\delimiter"4\next78\next78 }%
+ \edef\lrcorner{\delimiter"5\next79\next79 }%
+ \edef\yen{{\noexpand\mathhexbox@\next55}}%
+ \edef\checkmark{{\noexpand\mathhexbox@\next58}}%
+ \edef\circledR{{\noexpand\mathhexbox@\next72}}%
+ \edef\maltese{{\noexpand\mathhexbox@\next7A}}%
+ \global\let\loadmsam\empty}%
+\def\loadmsbm{%
+ \font@\tenmsb=msbm10 \font@\sevenmsb=msbm7 \font@\fivemsb=msbm5
+ \alloc@@8\fam\chardef\sixt@@n\msbfam
+ \textfont\msbfam=\tenmsb
+ \scriptfont\msbfam=\sevenmsb \scriptscriptfont\msbfam=\fivemsb
+ \global\let\loadmsbm\empty
+ }
+\def\widehat#1{\ifx\undefined\msbfam \DN@{362}%
+ \else \setboxz@h{$\m@th#1$}%
+ \edef\next@{\ifdim\wdz@>\tw@ em%
+ \hexnumber@\msbfam 5B%
+ \else 362\fi}\fi
+ \mathaccent"0\next@{#1}}
+\def\widetilde#1{\ifx\undefined\msbfam \DN@{365}%
+ \else \setboxz@h{$\m@th#1$}%
+ \edef\next@{\ifdim\wdz@>\tw@ em%
+ \hexnumber@\msbfam 5D%
+ \else 365\fi}\fi
+ \mathaccent"0\next@{#1}}
+\message{\string\newsymbol,}
+\def\newsymbol#1#2#3#4#5{\define#1{}%
+ \count@#2\relax \advance\count@\m@ne % to push case 0 to the \else clause
+ \ifcase\count@
+ \ifx\undefined\msafam\loadmsam\fi \let\next@\msafam
+ \or \ifx\undefined\msbfam\loadmsbm\fi \let\next@\msbfam
+ \else \Err@{\Invalid@@\string\newsymbol}\let\next@\tw@\fi
+ \mathchardef#1="#3\hexnumber@\next@#4#5\space}
+\def\UseAMSsymbols{\loadmsam\loadmsbm \input amssym.tex }
+\def\Bbb{\RIfM@\expandafter\Bbb@\else
+ \expandafter\nonmatherr@\expandafter\Bbb\fi}
+\def\Bbb@#1{{\Bbb@@{#1}}}
+\def\Bbb@@#1{\noaccents@\fam\msbfam\relax#1}
+\message{bold Greek and bold symbols,}
+\def\loadbold{%
+ \font@\tencmmib=cmmib10 \font@\sevencmmib=cmmib7 \font@\fivecmmib=cmmib5
+ \skewchar\tencmmib'177 \skewchar\sevencmmib'177 \skewchar\fivecmmib'177
+ \alloc@@8\fam\chardef\sixt@@n\cmmibfam
+ \textfont\cmmibfam\tencmmib
+ \scriptfont\cmmibfam\sevencmmib \scriptscriptfont\cmmibfam\fivecmmib
+ \font@\tencmbsy=cmbsy10 \font@\sevencmbsy=cmbsy7 \font@\fivecmbsy=cmbsy5
+ \skewchar\tencmbsy'60 \skewchar\sevencmbsy'60 \skewchar\fivecmbsy'60
+ \alloc@@8\fam\chardef\sixt@@n\cmbsyfam
+ \textfont\cmbsyfam\tencmbsy
+ \scriptfont\cmbsyfam\sevencmbsy \scriptscriptfont\cmbsyfam\fivecmbsy
+ \let\loadbold\empty
+}
+\def\boldnotloaded#1{\Err@{\ifcase#1\or First\else Second\fi
+ bold symbol font not loaded}}
+\def\mathchari@#1#2#3{\ifx\undefined\cmmibfam
+ \boldnotloaded@\@ne
+ \else\mathchar"#1\hexnumber@\cmmibfam#2#3\space \fi}
+\def\mathcharii@#1#2#3{\ifx\undefined\cmbsyfam
+ \boldnotloaded\tw@
+ \else \mathchar"#1\hexnumber@\cmbsyfam#2#3\space\fi}
+\edef\bffam@{\hexnumber@\bffam}
+\def\boldkey#1{\ifcat\noexpand#1A%
+ \ifx\undefined\cmmibfam \boldnotloaded\@ne
+ \else {\fam\cmmibfam#1}\fi
+ \else
+ \ifx#1!\mathchar"5\bffam@21 \else
+ \ifx#1(\mathchar"4\bffam@28 \else\ifx#1)\mathchar"5\bffam@29 \else
+ \ifx#1+\mathchar"2\bffam@2B \else\ifx#1:\mathchar"3\bffam@3A \else
+ \ifx#1;\mathchar"6\bffam@3B \else\ifx#1=\mathchar"3\bffam@3D \else
+ \ifx#1?\mathchar"5\bffam@3F \else\ifx#1[\mathchar"4\bffam@5B \else
+ \ifx#1]\mathchar"5\bffam@5D \else
+ \ifx#1,\mathchari@63B \else
+ \ifx#1-\mathcharii@200 \else
+ \ifx#1.\mathchari@03A \else
+ \ifx#1/\mathchari@03D \else
+ \ifx#1<\mathchari@33C \else
+ \ifx#1>\mathchari@33E \else
+ \ifx#1*\mathcharii@203 \else
+ \ifx#1|\mathcharii@06A \else
+ \ifx#10\bold0\else\ifx#11\bold1\else\ifx#12\bold2\else\ifx#13\bold3\else
+ \ifx#14\bold4\else\ifx#15\bold5\else\ifx#16\bold6\else\ifx#17\bold7\else
+ \ifx#18\bold8\else\ifx#19\bold9\else
+ \Err@{\string\boldkey\space can't be used with #1}%
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
+\def\boldsymbol#1{%
+ \DN@{\Err@{You can't use \string\boldsymbol\space with \string#1}#1}%
+ \ifcat\noexpand#1A%
+ \let\next@\relax
+ \ifx\undefined\cmmibfam \boldnotloaded\@ne
+ \else {\fam\cmmibfam#1}\fi
+ \else
+ \xdef\meaning@{\meaning#1.........}%
+ \expandafter\math@\meaning@\math@
+ \ifmath@
+ \expandafter\mathch@\meaning@\mathch@
+ \ifmathch@
+ \expandafter\boldsymbol@@\meaning@\boldsymbol@@
+ \fi
+ \else
+ \expandafter\macro@\meaning@\macro@
+ \expandafter\delim@\meaning@\delim@
+ \ifdelim@
+ \expandafter\delim@@\meaning@\delim@@
+ \else
+ \boldsymbol@{#1}%
+ \fi
+ \fi
+ \fi
+ \next@}
+\def\mathhexboxii@#1#2{\ifx\undefined\cmbsyfam
+ \boldnotloaded\tw@
+ \else \mathhexbox@{\hexnumber@\cmbsyfam}{#1}{#2}\fi}
+\def\boldsymbol@#1{\let\next@\relax\let\next#1%
+ \ifx\next\cdot\mathcharii@201 \else
+ \ifx\next\prime{{\null\mathcharii@030 \null}}\else
+ \ifx\next\lbrack\mathchar"4\bffam@5B \else
+ \ifx\next\rbrack\mathchar"5\bffam@5D \else
+ \ifx\next\{\mathcharii@466 \else
+ \ifx\next\lbrace\mathcharii@466 \else
+ \ifx\next\}\mathcharii@567 \else
+ \ifx\next\rbrace\mathcharii@567 \else
+ \ifx\next\surd{{\mathcharii@170}}\else
+ \ifx\next\S{{\mathhexboxii@78}}\else
+ \ifx\next\P{{\mathhexboxii@7B}}\else
+ \ifx\next\dag{{\mathhexboxii@79}}\else
+ \ifx\next\ddag{{\mathhexboxii@7A}}\else
+ \DN@{\Err@{You can't use \string\boldsymbol\space with \string#1}#1}%
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi}
+\def\boldsymbol@@#1.#2\boldsymbol@@{\classnum@#1 \count@@@\classnum@ %1
+ \divide\classnum@4096 \count@\classnum@ %2
+ \multiply\count@4096 \advance\count@@@-\count@ \count@@\count@@@ %3
+ \divide\count@@@\@cclvi \count@\count@@ %4
+ \multiply\count@@@\@cclvi \advance\count@@-\count@@@ %5
+ \divide\count@@@\@cclvi %6
+ \multiply\classnum@4096 \advance\classnum@\count@@ %7
+ \ifnum\count@@@=\z@ %8
+ \count@"\bffam@ \multiply\count@\@cclvi
+ \advance\classnum@\count@
+ \DN@{\mathchar\number\classnum@}%
+ \else
+ \ifnum\count@@@=\@ne %9
+ \ifx\undefined\cmmibfam \DN@{\boldnotloaded\@ne}%
+ \else \count@\cmmibfam \multiply\count@\@cclvi
+ \advance\classnum@\count@
+ \DN@{\mathchar\number\classnum@}\fi
+ \else
+ \ifnum\count@@@=\tw@ %10
+ \ifx\undefined\cmbsyfam
+ \DN@{\boldnotloaded\tw@}%
+ \else
+ \count@\cmbsyfam \multiply\count@\@cclvi
+ \advance\classnum@\count@
+ \DN@{\mathchar\number\classnum@}%
+ \fi
+ \fi
+ \fi
+\fi}
+\newif\ifdelim@
+\newcount\delimcount@
+{\uccode`6=`\\ \uccode`7=`d \uccode`8=`e \uccode`9=`l
+ \uppercase{\gdef\delim@#1#2#3#4#5\delim@
+ {\delim@false\ifx 6#1\ifx 7#2\ifx 8#3\ifx 9#4\delim@true
+ \xdef\meaning@{#5}\fi\fi\fi\fi}}}
+\def\delim@@#1"#2#3#4#5#6\delim@@{\if#32%
+\let\next@\relax
+ \ifx\undefined\cmbsyfam \boldnotloaded\@ne
+ \else \mathcharii@#2#4#5\space \fi\fi}
+\def\vert{\delimiter"026A30C }
+\def\Vert{\delimiter"026B30D }
+\let\|\Vert
+\def\backslash{\delimiter"026E30F }
+\def\boldkeydots@#1{\bold@true\let\next=#1\let\delayed@=#1\mdots@@
+ \boldkey#1\bold@false} % = required!
+\def\boldsymboldots@#1{\bold@true\let\next#1\let\delayed@#1\mdots@@
+ \boldsymbol#1\bold@false}
+\message{Euler fonts,}
+\def\loadeufm{\loadmathfont{eufm}}
+\def\loadeufb{\loadmathfont{eufb}}
+\def\loadeusm{\loadmathfont{eusm}}
+\def\loadeusb{\loadmathfont{eusb}}
+\def\loadeurm{\loadmathfont{eurm}}
+\def\loadeurb{\loadmathfont{eurb}}
+\def\frak{\mathfont@\frak}
+\def\frakfam{\eufmfam}
+\let\goth\frak
+\def\loadmathfont#1{%
+ \expandafter\font@\csname ten#1\endcsname=#110
+ \expandafter\font@\csname seven#1\endcsname=#17
+ \expandafter\font@\csname five#1\endcsname=#15
+ \edef\next{\noexpand\alloc@@8\fam\chardef\sixt@@n
+ \expandafter\noexpand\csname#1fam\endcsname}%
+ \next
+ \textfont\csname#1fam\endcsname \csname ten#1\endcsname
+ \scriptfont\csname#1fam\endcsname \csname seven#1\endcsname
+ \scriptscriptfont\csname#1fam\endcsname \csname five#1\endcsname
+ \expandafter\def\csname #1\expandafter\endcsname\expandafter{%
+ \expandafter\mathfont@\csname#1\endcsname}%
+ \expandafter\gdef\csname load#1\endcsname{}%
+}
+\def\mathfont@#1{\RIfM@\expandafter\mathfont@@\expandafter#1\else
+ \expandafter\nonmatherr@\expandafter#1\fi}
+\def\mathfont@@#1#2{{\mathfont@@@#1{#2}}}
+\def\mathfont@@@#1#2{\noaccents@
+ \fam\csname\expandafter\eat@\string#1fam\endcsname
+ \relax#2}
+\message{math accents,}
+\def\accentclass@{7}
+\def\noaccents@{\def\accentclass@{0}}
+\def\makeacc@#1#2{\def#1{\mathaccent"\accentclass@#2 }}
+\makeacc@\hat{05E}
+\makeacc@\check{014}
+\makeacc@\tilde{07E}
+\makeacc@\acute{013}
+\makeacc@\grave{012}
+\makeacc@\dot{05F}
+\makeacc@\ddot{07F}
+\makeacc@\breve{015}
+\makeacc@\bar{016}
+\def\vec{\mathaccent"017E }
+\newcount\skewcharcount@
+\newcount\familycount@
+\def\theskewchar@{\familycount@\@ne
+ \global\skewcharcount@\the\skewchar\textfont\@ne %1
+ \ifnum\fam>\m@ne\ifnum\fam<16
+ \global\familycount@\the\fam\relax
+ \global\skewcharcount@\the\skewchar\textfont\the\fam\relax\fi\fi %2
+ \ifnum\skewcharcount@>\m@ne
+ \ifnum\skewcharcount@<128
+ \multiply\familycount@256
+ \global\advance\skewcharcount@\familycount@
+ \global\advance\skewcharcount@28672
+ \mathchar\skewcharcount@\else
+ \global\skewcharcount@\m@ne\fi\else
+ \global\skewcharcount@\m@ne\fi} %3
+\newcount\pointcount@
+\def\getpoints@#1.#2\getpoints@{\pointcount@#1 }
+\newdimen\accentdimen@
+\newcount\accentmu@
+\def\dimentomu@{\multiply\accentdimen@ 100
+ \expandafter\getpoints@\the\accentdimen@\getpoints@
+ \multiply\pointcount@18
+ \divide\pointcount@\@m
+ \global\accentmu@\pointcount@}
+\def\Makeacc@#1#2{\def#1{\RIfM@\DN@{\mathaccent@
+ {"\accentclass@#2 }}\else\DN@{\nonmatherr@{#1}}\fi\next@}}
+\def\unbracefonts@{\let\Cal@\Cal@@\let\roman@\roman@@\let\bold@\bold@@
+ \let\slanted@\slanted@@}
+\def\mathaccent@#1#2{\ifnum\fam=\m@ne\xdef\thefam@{1}\else
+ \xdef\thefam@{\the\fam}\fi %1
+ \accentdimen@\z@ %2
+ \setboxz@h{\unbracefonts@$\m@th\fam\thefam@\relax#2$}% %3
+ \ifdim\accentdimen@=\z@\DN@{\mathaccent#1{#2}}% %4
+ \setbox@ne\hbox{\unbracefonts@$\m@th\fam\thefam@\relax#2\theskewchar@$}% %5a
+ \setbox\tw@\hbox{$\m@th\ifnum\skewcharcount@=\m@ne\else
+ \mathchar\skewcharcount@\fi$}% %5b
+ \global\accentdimen@\wd@ne\global\advance\accentdimen@-\wdz@
+ \global\advance\accentdimen@-\wd\tw@ %5c
+ \global\multiply\accentdimen@\tw@
+ \dimentomu@\global\advance\accentmu@\@ne %5d
+ \else\DN@{{\mathaccent#1{#2\mkern\accentmu@ mu}%
+ \mkern-\accentmu@ mu}{}}\fi %6
+ \next@}\Makeacc@\Hat{05E}
+\Makeacc@\Check{014}
+\Makeacc@\Tilde{07E}
+\Makeacc@\Acute{013}
+\Makeacc@\Grave{012}
+\Makeacc@\Dot{05F}
+\Makeacc@\Ddot{07F}
+\Makeacc@\Breve{015}
+\Makeacc@\Bar{016}
+\def\Vec{\RIfM@\DN@{\mathaccent@{"017E }}\else
+ \DN@{\nonmatherr@\Vec}\fi\next@}
+\def\accentedsymbol#1#2{\csname newbox\expandafter\endcsname
+ \csname\expandafter\eat@\string#1@box\endcsname
+ \expandafter\setbox\csname\expandafter\eat@
+ \string#1@box\endcsname\hbox{$\m@th#2$}\define
+ #1{\copy\csname\expandafter\eat@\string#1@box\endcsname{}}}
+\message{roots,}
+\def\sqrt#1{\radical"270370 {#1}}
+\let\underline@\underline
+\let\overline@\overline
+\def\underline#1{\underline@{#1}}
+\def\overline#1{\overline@{#1}}
+\Invalid@\leftroot
+\Invalid@\uproot
+\newcount\uproot@
+\newcount\leftroot@
+\def\root{\relaxnext@
+ \DN@{\ifx\next\uproot\let\next@\nextii@\else
+ \ifx\next\leftroot\let\next@\nextiii@\else
+ \let\next@\plainroot@\fi\fi\next@}%
+ \DNii@\uproot##1{\uproot@##1\relax\FN@\nextiv@}%
+ \def\nextiv@{\ifx\next\space@\DN@. {\FN@\nextv@}\else
+ \DN@.{\FN@\nextv@}\fi\next@.}%
+ \def\nextv@{\ifx\next\leftroot\let\next@\nextvi@\else
+ \let\next@\plainroot@\fi\next@}%
+ \def\nextvi@\leftroot##1{\leftroot@##1\relax\plainroot@}%
+ \def\nextiii@\leftroot##1{\leftroot@##1\relax\FN@\nextvii@}%
+ \def\nextvii@{\ifx\next\space@
+ \DN@. {\FN@\nextviii@}\else
+ \DN@.{\FN@\nextviii@}\fi\next@.}%
+ \def\nextviii@{\ifx\next\uproot\let\next@\nextix@\else
+ \let\next@\plainroot@\fi\next@}%
+ \def\nextix@\uproot##1{\uproot@##1\relax\plainroot@}%
+ \bgroup\uproot@\z@\leftroot@\z@\FN@\next@}
+\def\plainroot@#1\of#2{\setbox\rootbox\hbox{$\m@th\scriptscriptstyle{#1}$}%
+ \mathchoice{\r@@t\displaystyle{#2}}{\r@@t\textstyle{#2}}
+ {\r@@t\scriptstyle{#2}}{\r@@t\scriptscriptstyle{#2}}\egroup}
+\def\r@@t#1#2{\setboxz@h{$\m@th#1\sqrt{#2}$}%
+ \dimen@\ht\z@\advance\dimen@-\dp\z@
+ \setbox@ne\hbox{$\m@th#1\mskip\uproot@ mu$}\advance\dimen@ 1.667\wd@ne
+ \mkern-\leftroot@ mu\mkern5mu\raise.6\dimen@\copy\rootbox
+ \mkern-10mu\mkern\leftroot@ mu\boxz@}
+\def\boxed#1{\setboxz@h{$\m@th\displaystyle{#1}$}\dimen@.4\ex@
+ \advance\dimen@3\ex@\advance\dimen@\dp\z@
+ \hbox{\lower\dimen@\hbox{%
+ \vbox{\hrule height.4\ex@
+ \hbox{\vrule width.4\ex@\hskip3\ex@\vbox{\vskip3\ex@\boxz@\vskip3\ex@}%
+ \hskip3\ex@\vrule width.4\ex@}\hrule height.4\ex@}%
+ }}}
+\message{commutative diagrams,}
+\let\ampersand@\relax
+\newdimen\minaw@
+\minaw@11.11128\ex@
+\newdimen\minCDaw@
+\minCDaw@2.5pc
+\def\minCDarrowwidth#1{\RIfMIfI@\onlydmatherr@\minCDarrowwidth
+ \else\minCDaw@#1\relax\fi\else\onlydmatherr@\minCDarrowwidth\fi}
+\newif\ifCD@
+\def\CD{\bgroup\vspace@\relax\iffalse{\fi\let\ampersand@&\iffalse}\fi
+ \CD@true\vcenter\bgroup\Let@\tabskip\z@skip\baselineskip20\ex@
+ \lineskip3\ex@\lineskiplimit3\ex@\halign\bgroup
+ &\hfill$\m@th##$\hfill\crcr}
+\def\endCD{\crcr\egroup\egroup\egroup}
+\newdimen\bigaw@
+\atdef@>#1>#2>{\ampersand@ %1
+ \setboxz@h{$\m@th\ssize\;{#1}\;\;$}% %2
+ \setbox@ne\hbox{$\m@th\ssize\;{#2}\;\;$}% %3
+ \setbox\tw@\hbox{$\m@th#2$}% %4
+ \ifCD@\global\bigaw@\minCDaw@\else\global\bigaw@\minaw@\fi %5
+ \ifdim\wdz@>\bigaw@\global\bigaw@\wdz@\fi
+ \ifdim\wd@ne>\bigaw@\global\bigaw@\wd@ne\fi %6
+ \ifCD@\enskip\fi %7
+ \ifdim\wd\tw@>\z@
+ \mathrel{\mathop{\hbox to\bigaw@{\rightarrowfill@\displaystyle}}%
+ \limits^{#1}_{#2}}% %8
+ \else\mathrel{\mathop{\hbox to\bigaw@{\rightarrowfill@\displaystyle}}%
+ \limits^{#1}}\fi %9
+ \ifCD@\enskip\fi %10
+ \ampersand@} %11
+\atdef@<#1<#2<{\ampersand@\setboxz@h{$\m@th\ssize\;\;{#1}\;$}%
+ \setbox@ne\hbox{$\m@th\ssize\;\;{#2}\;$}\setbox\tw@\hbox{$\m@th#2$}%
+ \ifCD@\global\bigaw@\minCDaw@\else\global\bigaw@\minaw@\fi
+ \ifdim\wdz@>\bigaw@\global\bigaw@\wdz@\fi
+ \ifdim\wd@ne>\bigaw@\global\bigaw@\wd@ne\fi
+ \ifCD@\enskip\fi
+ \ifdim\wd\tw@>\z@
+ \mathrel{\mathop{\hbox to\bigaw@{\leftarrowfill@\displaystyle}}%
+ \limits^{#1}_{#2}}\else
+ \mathrel{\mathop{\hbox to\bigaw@{\leftarrowfill@\displaystyle}}%
+ \limits^{#1}}\fi
+ \ifCD@\enskip\fi\ampersand@}
+\begingroup
+ \catcode`\~=\active \lccode`\~=`\@
+ \lowercase{%
+ \global\atdef@)#1)#2){~>#1>#2>}
+ \global\atdef@(#1(#2({~<#1<#2<}}
+\endgroup
+\atdef@ A#1A#2A{\llap{$\m@th\vcenter{\hbox
+ {$\ssize#1$}}$}\Big\uparrow\rlap{$\m@th\vcenter{\hbox{$\ssize#2$}}$}&&}
+\atdef@ V#1V#2V{\llap{$\m@th\vcenter{\hbox
+ {$\ssize#1$}}$}\Big\downarrow\rlap{$\m@th\vcenter{\hbox{$\ssize#2$}}$}&&}
+\atdef@={&\enskip\mathrel
+ {\vbox{\hrule width\minCDaw@\vskip3\ex@\hrule width
+ \minCDaw@}}\enskip&}
+\atdef@|{\Big\Vert&&}
+\atdef@\vert{\Big\Vert&&}
+\def\pretend#1\haswidth#2{\setboxz@h{$\m@th\scriptstyle{#2}$}\hbox
+ to\wdz@{\hfill$\m@th\scriptstyle{#1}$\hfill}}
+\message{poor man's bold,}
+\def\pmb{\RIfM@\expandafter\mathpalette\expandafter\pmb@\else
+ \expandafter\pmb@@\fi}
+\def\pmb@@#1{\leavevmode\setboxz@h{#1}%
+ \dimen@-\wdz@
+ \kern-.5\ex@\copy\z@
+ \kern\dimen@\kern.25\ex@\raise.4\ex@\copy\z@
+ \kern\dimen@\kern.25\ex@\box\z@
+}
+\def\binrel@@#1{\ifdim\wd2<\z@\mathbin{#1}\else\ifdim\wd\tw@>\z@
+ \mathrel{#1}\else{#1}\fi\fi}
+\newdimen\pmbraise@
+% Note: because of the use of \mathpalette, if \pmb@ is
+% applied to a single math italic character (or a single
+% character from some other slanted math font), the italic
+% correction will be added. This will cause subscripts
+% to fall too far away from the character in some
+% cases, e.g., $\pmb{T}_1$ or $\pmb{\Cal T}_1$.
+\def\pmb@#1#2{\setbox\thr@@\hbox{$\m@th#1{#2}$}%
+ \setbox4\hbox{$\m@th#1\mkern.5mu$}\pmbraise@\wd4\relax
+ \binrel@{#2}%
+ \dimen@-\wd\thr@@
+ \binrel@@{%
+ \mkern-.8mu\copy\thr@@
+ \kern\dimen@\mkern.4mu\raise\pmbraise@\copy\thr@@
+ \kern\dimen@\mkern.4mu\box\thr@@
+}}
+\def\documentstyle#1{\W@{}\input #1.sty\relax}
+\message{syntax check,}
+\font\dummyft@=dummy
+\fontdimen1 \dummyft@=\z@
+\fontdimen2 \dummyft@=\z@
+\fontdimen3 \dummyft@=\z@
+\fontdimen4 \dummyft@=\z@
+\fontdimen5 \dummyft@=\z@
+\fontdimen6 \dummyft@=\z@
+\fontdimen7 \dummyft@=\z@
+\fontdimen8 \dummyft@=\z@
+\fontdimen9 \dummyft@=\z@
+\fontdimen10 \dummyft@=\z@
+\fontdimen11 \dummyft@=\z@
+\fontdimen12 \dummyft@=\z@
+\fontdimen13 \dummyft@=\z@
+\fontdimen14 \dummyft@=\z@
+\fontdimen15 \dummyft@=\z@
+\fontdimen16 \dummyft@=\z@
+\fontdimen17 \dummyft@=\z@
+\fontdimen18 \dummyft@=\z@
+\fontdimen19 \dummyft@=\z@
+\fontdimen20 \dummyft@=\z@
+\fontdimen21 \dummyft@=\z@
+\fontdimen22 \dummyft@=\z@
+\def\fontlist@{\\{\tenrm}\\{\sevenrm}\\{\fiverm}\\{\teni}\\{\seveni}%
+ \\{\fivei}\\{\tensy}\\{\sevensy}\\{\fivesy}\\{\tenex}\\{\tenbf}\\{\sevenbf}%
+ \\{\fivebf}\\{\tensl}\\{\tenit}}
+\def\font@#1=#2 {\rightappend@#1\to\fontlist@\font#1=#2 }
+\def\dodummy@{{\def\\##1{\global\let##1\dummyft@}\fontlist@}}
+\def\nopages@{\output{\setbox\z@\box\@cclv \deadcycles\z@}%
+ \alloc@5\toks\toksdef\@cclvi\output}
+\let\galleys\nopages@
+\newif\ifsyntax@
+\newcount\countxviii@
+\def\syntax{\syntax@true\dodummy@\countxviii@\count18
+ \loop\ifnum\countxviii@>\m@ne\textfont\countxviii@=\dummyft@
+ \scriptfont\countxviii@=\dummyft@\scriptscriptfont\countxviii@=\dummyft@
+ \advance\countxviii@\m@ne\repeat %1
+ \dummyft@\tracinglostchars\z@\nopages@\frenchspacing\hbadness\@M}
+\def\first@#1#2\end{#1}
+\def\printoptions{\W@{Do you want S(yntax check),
+ G(alleys) or P(ages)?}%
+ \message{Type S, G or P, followed by <return>: }%
+ \begingroup % to localize the following change to \endlinechar:
+ \endlinechar\m@ne % to prevent a space or \par in \ans@ from ^^M
+ \read\m@ne to\ans@
+% Define \ans@ to uppercase itself, and default to P if the user
+% just pressed <return>.
+ \edef\ans@{\uppercase{\def\noexpand\ans@{%
+ \expandafter\first@\ans@ P\end}}}%
+% Cast the new definition of \ans@ outside the group
+ \expandafter\endgroup\ans@
+ \if\ans@ P% fine, no action needs to be taken
+ \else \if\ans@ S\syntax
+ \else \if\ans@ G\galleys
+ \else\message{? Unknown option: \ans@; using the `pages' option.}%
+ \fi\fi\fi}
+\def\alloc@#1#2#3#4#5{\global\advance\count1#1by\@ne
+ \ch@ck#1#4#2\allocationnumber=\count1#1
+ \global#3#5=\allocationnumber
+ \ifalloc@\wlog{\string#5=\string#2\the\allocationnumber}\fi}
+\def\document{\def\alloclist@{}\def\fontlist@{}}
+\let\enddocument\bye
+\let\plainproclaim\proclaim
+\let\plainfootnote\footnote
+\let\proclaim\undefined
+\let\footnote\undefined
+\let\=\undefined
+\let\>\undefined
+\let\cal\undefined
+\let\mit\undefined
+\let\oldstyle\undefined
+\catcode`\@=\active
+\message{... finished}
+\endinput
diff --git a/systems/knuth/local/lib/bigwlist.tex b/systems/knuth/local/lib/bigwlist.tex
new file mode 100644
index 0000000000..8d780a2692
--- /dev/null
+++ b/systems/knuth/local/lib/bigwlist.tex
@@ -0,0 +1,54 @@
+% Makes a landscape listing of one or more files with wide lines
+% Typical usage:
+% tex list *.c \\end
+
+\magnification=\magstep2
+\hsize=9truein
+\vsize=6.5truein
+\special{landscape}
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex8
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\baselineskip=9pt
+\medskipamount=4pt minus 1pt
+\raggedbottom
+\nopagenumbers
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\filenamefont#1\quad\headlinefont \today\ at \hours
+ \hfill Page \folio}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus 1in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-500\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 170 characters on a line without an overfull box.
diff --git a/systems/knuth/local/lib/black.mf b/systems/knuth/local/lib/black.mf
new file mode 100644
index 0000000000..dfbdd1765f
--- /dev/null
+++ b/systems/knuth/local/lib/black.mf
@@ -0,0 +1,2 @@
+%input blackimagen % the `standard' black font is for imagen (300dpi)
+input blacklj % now it's for LaserJet 4 (600dpi)
diff --git a/systems/knuth/local/lib/blackaps.mf b/systems/knuth/local/lib/blackaps.mf
new file mode 100644
index 0000000000..b3ea772032
--- /dev/null
+++ b/systems/knuth/local/lib/blackaps.mf
@@ -0,0 +1,10 @@
+% Black font for APS with proofsheet resolution approx 90 pixels per inch
+% (Used with output of GF2DVI for the METAFONTbook illustrations)
+
+if mode<>aps: errmessage "This file is for aps only"; fi
+
+font_identifier "BLACKAPS";
+picture pix_picture; pix_wd := pix_ht := 8;
+pix_picture := unitpixel scaled 8;
+
+input grayf
diff --git a/systems/knuth/local/lib/blackdvi.tex b/systems/knuth/local/lib/blackdvi.tex
new file mode 100644
index 0000000000..fc1539a33e
--- /dev/null
+++ b/systems/knuth/local/lib/blackdvi.tex
@@ -0,0 +1,96 @@
+% This is BLACKDVI.STY and BLACKDVI.TEX in text format, as of March 5,
+% 1992, written by Jim Hafner, HAFNER@ALMADEN, or hafner@almaden.ibm.com.
+% This file turns all color commands defined by COLORDVI.STY or COLORDVI.TEX
+% into no-ops and so can be used to get B/W printing without having to
+% ferret out all the color macros.
+%
+% For example, just use
+% \documentstyle[blackdvi]{foils}
+% in place of
+% \documentstyle[colordvi]{foils}
+% It should be compatible with any other COLOR*.STY file (e.g.,
+% COLORPS.STY) intended for use with a non-PostScript driver.
+%
+% This file can be used anywhere COLORDVI.STY or COLORDVI.TEX is used. In
+% particular, It can be used in any flavor of LaTeX (e.g. LaTeX,
+% FoilTeX, AMS-LaTeX) by adding the keyword "blackdvi" to the options in
+% the \documentstyle command (e.g., "\documentstyle[blackdvi]{foils}") or
+% \input in any flavor of TeX (e.g. Plain TeX, eplain, AMS-TeX or any of
+% the above LaTeX flavors) with the command "\input blackdvi".
+%
+% Here is the background command
+\def\background#1{}%
+\def\newColor #1 {\expandafter\def\csname text#1\endcsname{}%
+ \expandafter\def\csname #1\endcsname##1{}}%
+%
+\def\textColor#1{}
+\def\Color#1#2{#2}
+
+\newColor GreenYellow
+\newColor Yellow
+\newColor Goldenrod
+\newColor Dandelion
+\newColor Apricot
+\newColor Peach
+\newColor Melon
+\newColor YellowOrange
+\newColor Orange
+\newColor BurntOrange
+\newColor Bittersweet
+\newColor RedOrange
+\newColor Mahogany
+\newColor Maroon
+\newColor BrickRed
+\newColor Red
+\newColor OrangeRed
+\newColor RubineRed
+\newColor WildStrawberry
+\newColor Salmon
+\newColor CarnationPink
+\newColor Magenta
+\newColor VioletRed
+\newColor Rhodamine
+\newColor Mulberry
+\newColor RedViolet
+\newColor Fuchsia
+\newColor Lavender
+\newColor Thistle
+\newColor Orchid
+\newColor DarkOrchid
+\newColor Purple
+\newColor Plum
+\newColor Violet
+\newColor RoyalPurple
+\newColor BlueViolet
+\newColor Periwinkle
+\newColor CadetBlue
+\newColor CornflowerBlue
+\newColor MidnightBlue
+\newColor NavyBlue
+\newColor RoyalBlue
+\newColor Blue
+\newColor Cerulean
+\newColor Cyan
+\newColor ProcessBlue
+\newColor SkyBlue
+\newColor Turquoise
+\newColor TealBlue
+\newColor Aquamarine
+\newColor BlueGreen
+\newColor Emerald
+\newColor JungleGreen
+\newColor SeaGreen
+\newColor Green
+\newColor ForestGreen
+\newColor PineGreen
+\newColor LimeGreen
+\newColor YellowGreen
+\newColor SpringGreen
+\newColor OliveGreen
+\newColor RawSienna
+\newColor Sepia
+\newColor Brown
+\newColor Tan
+\newColor Gray
+\newColor Black
+\newColor White
diff --git a/systems/knuth/local/lib/blackimagen.mf b/systems/knuth/local/lib/blackimagen.mf
new file mode 100644
index 0000000000..6171bdbbaa
--- /dev/null
+++ b/systems/knuth/local/lib/blackimagen.mf
@@ -0,0 +1,9 @@
+% Black font for Imagen with proofsheet resolution 75 pixels per inch
+
+if mode<>imagen: errmessage "This file is for imagen only"; fi
+
+font_identifier "BLACKIMAGEN";
+picture pix_picture; pix_wd := pix_ht := 4;
+pix_picture := unitpixel scaled 4;
+
+input grayf
diff --git a/systems/knuth/local/lib/blacklino.mf b/systems/knuth/local/lib/blacklino.mf
new file mode 100644
index 0000000000..6bc06c4111
--- /dev/null
+++ b/systems/knuth/local/lib/blacklino.mf
@@ -0,0 +1,10 @@
+% Black font for LINO with proofsheet resolution approx 90 pixels per inch
+% (Used with output of GF2DVI for the METAFONTbook illustrations)
+
+if mode<>linohi: errmessage "This file is for linotronic only"; fi
+
+font_identifier "BLACKLINO";
+picture pix_picture; pix_wd := pix_ht := 14;
+pix_picture := unitpixel scaled 14;
+
+input grayf
diff --git a/systems/knuth/local/lib/blacklj.mf b/systems/knuth/local/lib/blacklj.mf
new file mode 100644
index 0000000000..a1808c8a6c
--- /dev/null
+++ b/systems/knuth/local/lib/blacklj.mf
@@ -0,0 +1,9 @@
+% Black font for LJ with proofsheet resolution 75 pixels per inch.
+
+if mode<>ljfour: errmessage "This file is for ljfour only"; fi
+
+font_identifier "BLACKLJ";
+picture pix_picture; pix_wd := pix_ht := 8;
+pix_picture := unitpixel scaled 8;
+
+input grayf
diff --git a/systems/knuth/local/lib/colordvi.tex b/systems/knuth/local/lib/colordvi.tex
new file mode 100644
index 0000000000..5b65e0ccc8
--- /dev/null
+++ b/systems/knuth/local/lib/colordvi.tex
@@ -0,0 +1,139 @@
+% This is COLORDVI.TEX and COLORDVI.STY in text format, as of
+% March 5, 1992, written by Jim Hafner, HAFNER@ALMADEN, or
+% hafner@almaden.ibm.com. Modified by Tomas Rokicki to be a
+% bit smaller and easier to add to.
+%
+% This style file can be used to get color in TeX, LaTeX or more
+% importantly, FoilTeX as an optional style file. It is generic in
+% that the color parameters are determined by the driver's header files.
+%
+% It can be used with any driver which knows how to process the keywords
+% in the \special commands. Currently, only versions of dvips greater
+% than 5.478 (dvips is the DVI->PostScript driver by Tom Rokicki) and
+% TeXview on the NeXT interpret these specials.
+%
+% This file can be used in any flavor of LaTeX (e.g. LaTeX, FoilTeX,
+% AMS-LaTeX) by adding the keyword "colordvi" to the options in the
+% \documentstyle command (e.g., "\documentstyle[colordvi]{foils}") or \input
+% in any flavor of TeX (e.g. Plain TeX, eplain, AMS-TeX or any of the
+% above LaTeX flavors) with the command "\input colordvi".
+%
+% The actual parameters for each color are determined by the color.pro
+% header file prepended to the output file by dvips. They can be
+% overrriden by a user-supplied header, called by either the -h option
+% or the ``h'' flag in a printer configuration file, or in the .dvipsrc
+% (or its analogue on other systems).
+%
+% Most of the color names and their matches were based on the
+% most recent (at time of writing) Crayola Crayon box of 64 colors.
+%
+% There are 68 predefined colors. At the end of this file is a
+% listing of the color names and their approximate PANTONE color
+% match. This is for reference purposes only.
+%
+% The first macro lets the user specify the background color for the
+% document. It sets the background color for the current page and all
+% succeeding pages, unless changed by another command of this type. To
+% change the background color back to the default, issue
+% \background{White}
+%
+\def\background#1{\special{background #1}}
+%
+% There are two types of text color commands. The first is in the form
+% \ColorName (note the uppercase for the color name).
+%
+% It is called a local color command since it takes one argument
+% enclosed in brackets. It writes the contents of its argument in the
+% selected color. This should be used for local or nested color
+% changes, since it restores the original color state when it completes.
+% The second type of color command is in the form
+% \textColorName
+% This uses the same naming convention as before. It is called a global
+% color command since it takes no arguments and simply sets the color at
+% this point. No previous color information is saved. IF YOU USE THIS
+% INTERNAL TO ANY LOCAL COLOR COMMAND, THE NESTING HISTORY IS LOST.
+%
+% Here are the global color changers, with color codes defined;
+% these are used to define the small region colors.
+%
+% This first is for user defined color. The argument #1 is for a "CMYK"
+% quadruple of intensity values between 0 and 1. (CMYK stands for Cyan,
+% Magenta, Yellow and Black.) E.g., \textColor{.2 .3 .4 .1}
+%
+\def\textColor#1{\special{color cmyk #1}}
+%
+\def\newColor #1 {\expandafter\def\csname text#1\endcsname{\special
+ {color #1}}\expandafter\def\csname #1\endcsname##1{\special
+ {color push #1}##1\special{color pop}}}%
+\def\Color#1#2{\special{color push cmyk #1}#2\special{color pop}}
+%
+% Here are the color names and their PANTONE match (approximately)
+%
+\newColor GreenYellow % GreenYellow Approximate PANTONE 388
+\newColor Yellow % Yellow Approximate PANTONE YELLOW
+\newColor Goldenrod % Goldenrod Approximate PANTONE 109
+\newColor Dandelion % Dandelion Approximate PANTONE 123
+\newColor Apricot % Apricot Approximate PANTONE 1565
+\newColor Peach % Peach Approximate PANTONE 164
+\newColor Melon % Melon Approximate PANTONE 177
+\newColor YellowOrange % YellowOrange Approximate PANTONE 130
+\newColor Orange % Orange Approximate PANTONE ORANGE-021
+\newColor BurntOrange % BurntOrange Approximate PANTONE 388
+\newColor Bittersweet % Bittersweet Approximate PANTONE 167
+\newColor RedOrange % RedOrange Approximate PANTONE 179
+\newColor Mahogany % Mahogany Approximate PANTONE 484
+\newColor Maroon % Maroon Approximate PANTONE 201
+\newColor BrickRed % BrickRed Approximate PANTONE 1805
+\newColor Red % Red VERY-Approx PANTONE RED
+\newColor OrangeRed % OrangeRed No PANTONE match
+\newColor RubineRed % RubineRed Approximate PANTONE RUBINE-RED
+\newColor WildStrawberry % WildStrawberry Approximate PANTONE 206
+\newColor Salmon % Salmon Approximate PANTONE 183
+\newColor CarnationPink % CarnationPink Approximate PANTONE 218
+\newColor Magenta % Magenta Approximate PANTONE PROCESS-MAGENTA
+\newColor VioletRed % VioletRed Approximate PANTONE 219
+\newColor Rhodamine % Rhodamine Approximate PANTONE RHODAMINE-RED
+\newColor Mulberry % Mulberry Approximate PANTONE 241
+\newColor RedViolet % RedViolet Approximate PANTONE 234
+\newColor Fuchsia % Fuchsia Approximate PANTONE 248
+\newColor Lavender % Lavender Approximate PANTONE 223
+\newColor Thistle % Thistle Approximate PANTONE 245
+\newColor Orchid % Orchid Approximate PANTONE 252
+\newColor DarkOrchid % DarkOrchid No PANTONE match
+\newColor Purple % Purple Approximate PANTONE PURPLE
+\newColor Plum % Plum VERY-Approx PANTONE 518
+\newColor Violet % Violet Approximate PANTONE VIOLET
+\newColor RoyalPurple % RoyalPurple Approximate PANTONE 267
+\newColor BlueViolet % BlueViolet Approximate PANTONE 2755
+\newColor Periwinkle % Periwinkle Approximate PANTONE 2715
+\newColor CadetBlue % CadetBlue Approximate PANTONE (534+535)/2
+\newColor CornflowerBlue % CornflowerBlue Approximate PANTONE 292
+\newColor MidnightBlue % MidnightBlue Approximate PANTONE 302
+\newColor NavyBlue % NavyBlue Approximate PANTONE 293
+\newColor RoyalBlue % RoyalBlue No PANTONE match
+\newColor Blue % Blue Approximate PANTONE BLUE-072
+\newColor Cerulean % Cerulean Approximate PANTONE 3005
+\newColor Cyan % Cyan Approximate PANTONE PROCESS-CYAN
+\newColor ProcessBlue % ProcessBlue Approximate PANTONE PROCESS-BLUE
+\newColor SkyBlue % SkyBlue Approximate PANTONE 2985
+\newColor Turquoise % Turquoise Approximate PANTONE (312+313)/2
+\newColor TealBlue % TealBlue Approximate PANTONE 3145
+\newColor Aquamarine % Aquamarine Approximate PANTONE 3135
+\newColor BlueGreen % BlueGreen Approximate PANTONE 320
+\newColor Emerald % Emerald No PANTONE match
+\newColor JungleGreen % JungleGreen Approximate PANTONE 328
+\newColor SeaGreen % SeaGreen Approximate PANTONE 3268
+\newColor Green % Green VERY-Approx PANTONE GREEN
+\newColor ForestGreen % ForestGreen Approximate PANTONE 349
+\newColor PineGreen % PineGreen Approximate PANTONE 323
+\newColor LimeGreen % LimeGreen No PANTONE match
+\newColor YellowGreen % YellowGreen Approximate PANTONE 375
+\newColor SpringGreen % SpringGreen Approximate PANTONE 381
+\newColor OliveGreen % OliveGreen Approximate PANTONE 582
+\newColor RawSienna % RawSienna Approximate PANTONE 154
+\newColor Sepia % Sepia Approximate PANTONE 161
+\newColor Brown % Brown Approximate PANTONE 1615
+\newColor Tan % Tan No PANTONE match
+\newColor Gray % Gray Approximate PANTONE COOL-GRAY-8
+\newColor Black % Black Approximate PANTONE PROCESS-BLACK
+\newColor White % White No PANTONE match
diff --git a/systems/knuth/local/lib/ctwimac.tex b/systems/knuth/local/lib/ctwimac.tex
new file mode 100644
index 0000000000..eddd6af352
--- /dev/null
+++ b/systems/knuth/local/lib/ctwimac.tex
@@ -0,0 +1,476 @@
+% standard macros for CWEB listings (in addition to plain.tex)
+% Version 3.6 --- May 2000
+% modified for pages produced by CTWILL
+% further modified for page size of the MMIXware book
+\ifx\documentstyle\undefined\else\endinput\fi % LaTeX will use other macros
+\xdef\fmtversion{\fmtversion+CTWILL3.6+LNCS}
+
+\let\:=\. % preserve a way to get the dot accent
+ % (all other accents will still work as usual)
+
+\newdimen\em \em=10pt % this "em" will not change with font size
+\parskip 0pt plus .1pt % almost no stretch between paragraphs
+\parindent 1\em % for paragraphs and for the first line of C text
+
+\newif\ifsorted
+\newread\sreffile
+\newwrite\reffile
+\ifx\norefs\def\else
+ \openin\sreffile=\jobname.sref
+ \ifeof\sreffile \immediate\openout\reffile=\jobname.ref \sortedfalse
+ \message{This is the first pass!}
+ \else \sortedtrue \message{This is the second pass!} \fi
+\fi
+
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps
+\def\CEE/{{\mc C\spacefactor1000}}
+\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}}
+\def\TEX/{\TeX}
+\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}}
+\def\Cee{\CEE/} % for backward compatibility
+\def\9#1{}
+\font\eightrm=cmr8
+\font\sixrm=cmr6
+\font\ninei=cmmi9
+\font\eighti=cmmi8
+\font\sixi=cmmi6
+\skewchar\ninei='177 \skewchar\eighti='177 \skewchar\sixi='177
+\font\ninesy=cmsy9
+\font\eightsy=cmsy8
+\font\sixsy=cmsy6
+\skewchar\ninesy='60 \skewchar\eightsy='60 \skewchar\sixsy='60
+\font\ninebf=cmbx9
+\font\eightbf=cmbx8
+\font\sixbf=cmbx6
+\font\ninett=cmtt9
+\font\eighttt=cmtt8
+\hyphenchar\ninett=-1 \hyphenchar\eighttt=-1
+\font\ninesl=cmsl9
+\font\eightsl=cmsl8
+\font\nineit=cmti9
+\font\eightit=cmti8
+\font\tentex=cmtex10
+\font\ninetex=cmtex9 % TeX extended character set (used in strings)
+\font\eighttex=cmtex8
+\fontdimen7\tentex=0pt % no double space after sentences
+\fontdimen7\ninetex=0pt
+\fontdimen7\eighttex=0pt
+
+\newif\iftenpoint
+\def\tenpoint{\tenpointtrue
+ \def\rm{\fam0\tenrm}%
+ \textfont0=\tenrm \scriptfont0=\sevenrm \scriptscriptfont0=\fiverm
+ \textfont1=\teni \scriptfont1=\seveni \scriptscriptfont1=\fivei
+ \textfont2=\tensy \scriptfont2=\sevensy \scriptscriptfont2=\fivesy
+ \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+ \def\it{\fam\itfam\tenit}%
+ \textfont\itfam=\tenit
+ \def\sl{\fam\slfam\tensl}%
+ \textfont\slfam=\tensl
+ \def\bf{\fam\bffam\tenbf}%
+ \textfont\bffam=\tenbf \scriptfont\bffam=\sevenbf
+ \scriptscriptfont\bffam=\fivebf
+ \def\tt{\fam\ttfam\tentt}%
+ \textfont\ttfam=\tentt
+ \def\ttx{\tentex}%
+ \normalbaselineskip=12pt
+ \let\cmntfont=\tenrm
+ \let\mc=\ninerm
+ \let\sc=\eightrm
+ \let\big=\tenbig
+ \setbox\strutbox=\hbox{\vrule height8pt depth3pt width 0pt}%
+ \normalbaselines\rm}
+
+\def\ninepoint{\tenpointfalse
+ \def\rm{\fam0\ninerm}%
+ \textfont0=\ninerm \scriptfont0=\sixrm \scriptscriptfont0=\fiverm
+ \textfont1=\ninei \scriptfont1=\sixi \scriptscriptfont1=\fivei
+ \textfont2=\ninesy \scriptfont2=\sixsy \scriptscriptfont2=\fivesy
+ \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+ \def\it{\fam\itfam\nineit}%
+ \textfont\itfam=\nineit
+ \def\sl{\fam\slfam\ninesl}%
+ \textfont\slfam=\ninesl
+ \def\bf{\fam\bffam\ninebf}%
+ \textfont\bffam=\ninebf \scriptfont\bffam=\sixbf
+ \scriptscriptfont\bffam=\fivebf
+ \def\tt{\fam\ttfam\ninett}%
+ \textfont\ttfam=\ninett
+ \def\ttx{\ninetex}%
+ \normalbaselineskip=11pt
+ \let\cmntfont=\ninerm
+ \let\mc=\eightrm
+ \let\sc=\sevenrm
+ \let\big=\ninebig
+ \setbox\strutbox=\hbox{\vrule height8pt depth3pt width 0pt}%
+ \normalbaselines\rm}
+
+\def\eightpoint{%
+ \def\rm{\fam0\eightrm}%
+ \textfont0=\eightrm \scriptfont0=\sixrm \scriptscriptfont0=\fiverm
+ \textfont1=\eighti \scriptfont1=\sixi \scriptscriptfont1=\fivei
+ \textfont2=\eightsy \scriptfont2=\sixsy \scriptscriptfont2=\fivesy
+ \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+ \def\it{\fam\itfam\eightit}%
+ \textfont\itfam=\eightit
+ \def\sl{\fam\slfam\eightsl}%
+ \textfont\slfam=\eightsl
+ \def\bf{\fam\bffam\eightbf}%
+ \textfont\bffam=\eightbf \scriptfont\bffam=\sixbf
+ \scriptscriptfont\bffam=\fivebf
+ \def\tt{\fam\ttfam\eighttt}%
+ \textfont\ttfam=\eighttt
+ \def\ttx{\eighttex}%
+ \normalbaselineskip=9pt
+ \let\cmntfont=\eightrm
+ \let\mc=\sevenrm
+ \let\sc=\sixrm
+ \let\big=\eightbig
+ \setbox\strutbox=\hbox{\vrule height7pt depth2pt width 0pt}%
+ \normalbaselines\rm}
+
+\tenpoint
+\def\tenbig#1{{\hbox{$\left#1\vbox to8.5pt{}\right.\nulldelimiterspace=0pt$}}}
+\def\ninebig#1{{\hbox{$\textfont0=\tenrm\textfont2=\tensy
+ \left#1\vbox to7.25pt{}\right.\nulldelimiterspace=0pt$}}}
+\def\eightbig#1{{\hbox{$\textfont0=\ninerm\textfont2=\ninesy
+ \left#1\vbox to6.5pt{}\right.\nulldelimiterspace=0pt$}}}
+
+%\font\tenss=cmss10 \let\cmntfont\tenss % alternative comment font
+\font\titlefont=cmr7 scaled\magstep4 % title on the contents page
+\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title
+
+\def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way
+\def\&#1{\leavevmode\hbox{\bf
+ \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}%
+ #1\/\kern.05em}} % boldface type for reserved words
+\def\.#1{\leavevmode\hbox{\ttx % typewriter type for strings
+ \let\\=\BS % backslash in a string
+ \let\{=\LB % left brace in a string
+ \let\}=\RB % right brace in a string
+ \let\~=\TL % tilde in a string
+ \let\ =\SP % space in a string
+ \let\_=\UL % underline in a string
+ \let\&=\AM % ampersand in a string
+ \let\^=\CF % circumflex in a string
+ #1\kern.05em}}
+\def\){{\tentex\kern-.05em}\discretionary{\hbox{\tentex\BS}}{}{}}
+\def\AT{@} % at sign for control text (not needed in versions >= 2.9)
+\def\ATL{\par\noindent\bgroup\catcode`\_=12 \postATL} % print @l in limbo
+\def\postATL#1 #2 {\bf letter \\{\uppercase{\char"#1}}
+ tangles as \tentex "#2"\egroup\par}
+\def\noATL#1 #2 {}
+\def\noatl{\let\ATL=\noATL} % suppress output from @l
+\def\ATH{\X\kern-.5em:Preprocessor definitions\X}
+\let\PB=\relax % hook for program brackets |...| in TeX part or section name
+
+\chardef\AM=`\& % ampersand character in a string
+\chardef\BS=`\\ % backslash in a string
+\chardef\LB=`\{ % left brace in a string
+\chardef\RB=`\} % right brace in a string
+\def\SP{{\tt\char`\ }} % (visible) space in a string
+\chardef\TL=`\~ % tilde in a string
+\chardef\UL=`\_ % underline character in a string
+\chardef\CF=`\^ % circumflex character in a string
+
+\newbox\PPbox % symbol for ++
+\setbox\PPbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm+\kern-1pt+}\kern.5pt}
+\def\PP{\copy\PPbox}
+\newbox\MMbox \setbox\MMbox=\hbox{\kern.5pt\raise1pt\hbox{\sevensy\char0
+ \kern-1pt\char0}\kern.5pt}
+\def\MM{\copy\MMbox}
+\newbox\MGbox % symbol for ->
+\setbox\MGbox=\hbox{\kern-2pt\lower3pt\hbox{\teni\char'176}\kern1pt}
+\def\MG{\copy\MGbox}
+\def\MRL#1{\mathrel{\let\K==#1}}
+\let\GG=\gg
+\let\LL=\ll
+\let\NULL=\Lambda
+\mathchardef\AND="2026 % bitwise and; also \& (unary operator)
+\let\OR=\mid % bitwise or
+\let\XOR=\oplus % bitwise exclusive or
+\def\CM{{\sim}} % bitwise complement
+\newbox\MODbox \setbox\MODbox=\hbox{\eightrm\%}
+\def\MOD{\mathbin{\copy\MODbox}}
+\def\DC{\kern.1em{::}\kern.1em} % symbol for ::
+\def\PA{\mathbin{.*}} % symbol for .*
+\def\MGA{\mathbin{\MG*}} % symbol for ->*
+\def\this{\&{this}}
+
+\newbox\bak \setbox\bak=\hbox to -1\em{} % backspace one em
+\newbox\bakk\setbox\bakk=\hbox to -2\em{} % backspace two ems
+
+\newcount\ind % current indentation in ems
+\def\1{\global\advance\ind by1\hangindent\ind\em} % indent one more notch
+\def\2{\global\advance\ind by-1} % indent one less notch
+\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement
+\def\4{\copy\bak} % backspace one notch
+\def\5{\hfil\penalty-1\hfilneg\kern2.5\em\copy\bakk\ignorespaces}% optional break
+\def\6{\ifmmode\else\par % forced break
+ \hangindent\ind\em\noindent\kern\ind\em\copy\bakk\ignorespaces\fi}
+\def\7{\Y\6} % forced break and a little extra space
+\def\8{\hskip-\ind em\hskip 2\em} % no indentation
+
+\newcount\gdepth % depth of current major group, plus one
+\newtoks\gtitle % title of current major group
+\newskip\intersecskip \intersecskip=12pt minus 3pt % space between sections
+\let\yskip=\smallskip
+\def\?{\mathrel?}
+\def\note#1#2.{\par\penalty5000
+ \Y\noindent{\hangindent2\em\baselineskip10pt\eightrm#1~#2.\par}}
+\def\lapstar{\rlap{*}}
+\def\stsec{\tenpoint\rightskip=0pt % get out of C mode (cf. \B)
+ \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50
+ \noindent\strut{\bf\secno.\quad}}
+\def\startsection{\titletrue
+ \line{\smash{\titlefont\title}\quad\hrulefill}\bigskip
+ \let\startsection=\stsec\stsec}
+\def\defin#1{\global\advance\ind by 2 \1\&{#1 } } % begin `define' or `format'
+\def\A{\note{See also section}} % xref for doubly defined section name
+\def\As{\note{See also sections}} % xref for multiply defined section name
+\def\B{\iftenpoint\ninepoint\fi
+ \rightskip=0pt plus 100pt minus 10pt % go into C mode
+ \sfcode`;=3000
+ \pretolerance 10000
+ \hyphenpenalty 9999 % so strings can be broken (discretionary \ is inserted)
+ \exhyphenpenalty 10000
+ \global\ind=2 \1\ \unskip}
+\def\C#1{\5\5\quad$/\ast\,${\cmntfont #1}$\,\ast/$}
+\let\SHC\C % "// short comments" treated like "/* ordinary comments */"
+%\def\C#1{\5\5\quad$\triangleright\,${\cmntfont#1}$\,\triangleleft$}
+%\def\SHC#1{\5\5\quad$\diamond\,${\cmntfont#1}}
+\def\D{\defin{\#define}} % macro definition
+\let\E=\equiv % equivalence sign
+\def\ET{ and~} % conjunction between two section numbers
+\def\ETs{, and~} % conjunction between the last two of several section numbers
+\def\F{\defin{format}} % format definition
+\let\G=\ge % greater than or equal sign
+% \H is long Hungarian umlaut accent
+\let\I=\ne % unequal sign
+\def\J{\.{@\&}} % TANGLE's join operation
+\let\K== % assignment operator
+%\let\K=\leftarrow % "honest" alternative to standard assignment operator
+% \L is Polish letter suppressed-L
+\outer\def\M#1{\def\secno{#1}\startsection\ignorespaces}
+\outer\def\N{\ifvoid\partialpage\lefttrue
+ \else\ifdim\ht\partialpage<\pageht \leftfalse\else\lefttrue\fi\fi\NNN}
+\outer\def\NN{\ifvoid\partialpage\leftfalse
+ \else\ifdim\ht\partialpage<\pageht \lefttrue\else\leftfalse\fi\fi\NNN}
+\outer\def\NNN#1#2#3.{\gdepth=#1\gtitle={#3}\def\secno{#2}% starred section
+ \ifleft \flushout
+ \gdef\rlhead{\let\i=I\uppercase{\ignorespaces#3}} % running left headline
+ \global\let\rrhead=\rlhead % running right headline
+ \else\global\setbox\partialpage=\vbox{
+ \vbox to\pageht{\unvbox\partialpage\vfill}\break}
+ \gdef\rrhead{\let\i=I\uppercase{\ignorespaces#3}}
+ \fi
+ \message{*\secno} % progress report
+ \startsection{\bf\ignorespaces#3.\quad}\ignorespaces}
+% \O is Scandinavian letter O-with-slash
+% \P is paragraph sign
+\def\Q{\note{This code is cited in section}} % xref for mention of a section
+\def\Qs{\note{This code is cited in sections}} % xref for mentions of a section
+\let\R=\lnot % logical not
+% \S is section sign
+\def\T#1{\leavevmode % octal, hex or decimal constant
+ \hbox{$\def\?{\kern.2em}%
+ \def\$##1{\egroup_{\,\rm##1}\bgroup}% suffix to constant
+ \def\_{\cdot 10^{\aftergroup}}% power of ten (via dirty trick)
+ \let\~=\oct \let\^=\hex {#1}$}}
+\def\U{\note{This code is used in section}} % xref for use of a section
+\def\Us{\note{This code is used in sections}} % xref for uses of a section
+\let\V=\lor % logical or
+\let\W=\land % logical and
+\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % section name
+ \XX$\langle\,${\let\I=\ne#2\sevenrm\kern.5em#1}$\,\rangle$\XX}
+\def\Y{\par\yskip}
+\let\Z=\le
+\let\ZZ=\let % now you can \write the control sequence \ZZ
+\let\*=*
+
+%\def\oct{\hbox{\rm\char'23\kern-.2em\it\aftergroup\?\aftergroup}} % WEB style
+%\def\hex{\hbox{\rm\char"7D\tt\aftergroup}} % WEB style
+\def\oct{\hbox{$^\circ$\kern-.1em\it\aftergroup\?\aftergroup}}% CWEB style
+\def\hex{\hbox{$^{\scriptscriptstyle\#}$\tt\aftergroup}} % CWEB style
+\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule
+ \hbox{\strut\kern2pt\.{#1}\kern2pt}}
+ \hrule}\vrule\kern2pt}} % verbatim string
+
+\def\TeX{{\ifmmode\it\fi
+ \leavevmode\hbox{T\kern-.1667em\lower.424ex\hbox{E}\hskip-.125em X}}}
+\def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi}
+
+% now here's the mini-index formatting control
+\newcount\nrefs % total number of references in partial page
+\newcount\baseno % smallest section number in partial page
+\toksdef\prefs=199 % references to previous sections in current program
+\toksdef\frefs=220 % references to future sections in current program
+\toksdef\erefs=221 % references to sections in another program
+% \count and \toks registers 200--219 are also used to keep track of refs
+\catcode`\@=11
+\newcount\@n \newcount\@m \newcount\@p
+\newdimen\pageht \pageht=19cm
+\newdimen\pagewd \pagewd=13cm
+\newdimen\colwd \colwd=\pagewd
+ \advance\colwd by -2pc \divide\colwd by 3 % for three columns
+\newdimen\fullpageht \fullpageht=\pageht \advance\fullpageht by 4pc
+\newdimen\pagethresh \pagethresh=2\pageht
+\newdimen\nsize \newdimen\msize
+\newskip\intersecskip \intersecskip=8pt plus 2pt minus 3pt
+\newbox\partialpage
+\newbox\newsec
+\newtoks\newrefs
+\newtoks\ttoks
+\newdimen\ninept \ninept=9pt
+\newif\iftitle \newif\ifleft
+
+\def\flushout{\ifvoid\partialpage\else
+ \setbox0=\vsplit\partialpage to \pageht
+ \shipout\vbox{
+ \vbox to 3pc{\leftheadline\vfill}
+ \nointerlineskip\box0}
+ \global\advance\pageno 1
+ \global\let\[=\makeinref \global\let\]=\makeoutref
+ \ifsorted \let\readin=\readln \readrefs
+ \else \immediate\write\reffile{!\the\pageno}\let\readin=\readrefs \fi
+ \setbox0=\vbox{\eightpoint \hsize=\colwd
+ \rightskip=0pt plus 100pt minus 10pt
+ \pretolerance 10000
+ \hyphenpenalty 10000 \exhyphenpenalty 10000
+ \noindent\vbox to1pt{}\par % 1pt = \topskip - \ninept
+ \readin}
+ \shipout\vbox to\fullpageht{
+ \vbox to 3pc{\rightheadline\vfill}
+ \unvbox\partialpage
+ \vfill
+ \ifdim\ht0>1pt \kern11pt\hrule \hbox{%
+ \nsize=\ht0 \advance\nsize-\topskip
+ \divide\nsize by 3 \divide\nsize by\ninept
+ \multiply\nsize by\ninept \advance\nsize\topskip
+ \vsplit0 to\nsize \kern1pc
+ \msize=\ht0 \advance\msize-\topskip
+ \divide\msize by 2 \divide\msize by\ninept
+ \multiply\msize by\ninept \advance\msize\topskip
+ \vbox to\nsize{\vsplit0 to\msize\vss}\kern1pc
+ \vbox to\nsize{\box0\vss}} \fi}
+ \global\advance\pageno 1 \global\let\rlhead=\rrhead\fi
+ {\globaldefs=1
+ \@n=199 \loop \ifnum\@n<222 \toks\@n={} \count\@n=0 \advance\@n 1 \repeat
+ \nrefs=0
+ \baseno=\secno}}
+\def\leftheadline{\hbox to\pagewd{\vbox to10pt{}%
+ \iftitle\global\titlefalse\else\ninerm\title:\enspace \eightsl\rlhead\fi
+ \hfil\eightrm\folio}}
+\def\rightheadline{\hbox to\pagewd{\vbox to10pt{}%
+ \eightrm\folio\hfil\ninerm\title:\enspace \eightsl\rrhead\/}}
+
+\def\mini{\begingroup \obeylines \globaldefs=1 \newrefs=\bgroup}
+\def\FI{\@n=\secno \advance\@n-\baseno \advance\@n 200
+ \advance\nrefs-\count\@n \@n=\nrefs
+ \let\[=\countnewref \let\]=\cn \the\newrefs \endgroup
+ \nobreak\null\nobreak\vskip-\baselineskip\penalty-500\vfil\eject}
+\def\twillout{\setbox\newsec=\vbox{\unvbox255}
+ \ifnum\@n=0 \nsize=0pt
+ \else \nsize=3pt \multiply\nsize\@n \advance\nsize 1.25pc \fi
+ \ifvoid\partialpage\else\advance\nsize 5pt \advance\nsize\ht\partialpage\fi
+ \advance\nsize\ht\newsec
+ \message{\the\nsize} % show the page break decision amount
+ \ifbr \global\brfalse \flushout \message{/\secno}\fi
+ \ifdim\nsize>\pagethresh \flushout \message{\secno}\fi
+ \addtopartialpage}
+\def\addtopartialpage{\globaldefs=1
+ \let\[=\addnewref \let\]=\addneweref \the\newrefs
+ \setbox\partialpage=\ifvoid\partialpage \box\newsec
+ \else \vbox{\unvbox\partialpage \vfilneg
+ \vskip\intersecskip \unvbox\newsec}\fi}
+\def\shortpage{\par\vskip-2\baselineskip
+ \null\vfill\penalty-5000\message{(shortpage)}}
+\newif\ifbr
+\def\forcebr{\global\brtrue}
+
+\def\countnewref #1 {\ifnum#1<\baseno \let\next\cn
+ \else\ifnum#1>\secno \let\next\cn \else\let\next\flushref\fi\fi\next#1 }
+\def\cn #1 #2 {\begintest{#1 #2}\cnr\endtest}
+
+% here's a tricky way to present \cnr with a csname unexpanded inside:
+\def\begintest#1{\def\TEST{#1}\expandafter\expandafter\expandafter}
+\def\endtest{\csname\expandafter\gobb\meaning\TEST\endcsname}
+\expandafter\def\expandafter\gobb\meaning\empty{}
+
+{\obeylines\gdef\cnr#1#2
+ {\ifx#1\relax \advance\@n 1 \fi}}
+{\obeylines\gdef\flushref#1
+ {}}
+\def\addnewref #1 {\ifnum#1<\baseno \let\next\an \@n=199
+ \else\ifnum#1>\secno \let\next\an \@n=#1 \advance\@n-\baseno
+ \advance\@n200 \ifnum\@n>219 \@n=220 \fi
+ \else\let\next\flushref\fi\fi\next#1 }
+\def\an#1 #2 {\begintest{#1 #2}\anr\endtest\[#1 #2 }
+\def\addneweref#1 #2 {\@n=221 \begintest{#1 #2}\anr\endtest\]#1 #2 }
+{\obeylines\gdef\anr#1#2
+ {\ifx#1\relax \advance\nrefs1 \advance\count\@n 1 %
+ \def#1{#2
+ }\toks\@n=\expandafter{\the\toks\@n\lmda#1}\fi}}
+\ifsorted \def\lmda#1{\global\let#1\relax}
+\else \def\lmda#1{#1\global\let#1\relax} \fi
+{\obeylines\gdef\makeinref#1 #2 #3
+ {\ifsorted\else\ttoks={\[#1 #2 #3}\immediate\write\reffile{+ \the\ttoks}\fi %
+ \hangindent=1em \noindent #2\miniform#3, \S#1.\par}}
+{\obeylines\gdef\makeoutref#1 #2 #3
+ {\ifsorted\else\ttoks={\]#1 #2 #3}\immediate\write\reffile{+ \the\ttoks}\fi %
+ \hangindent=1em \noindent #2\miniform#3, \extref#1.\par}}
+\def\miniform{\futurelet\next\miniforma}
+\def\miniforma{\ifx\next\zip
+ \else\if=\noexpand\next \let\next\gobbleq
+ \else\let\next\colon\fi\fi\next}
+\def\zip{}
+\def\gobbleq={\,=\,}
+\def\colon{: }
+\def\extref{\futurelet\next\extrefa}
+\def\extrefa{\if"\next\let\next\gobblest\else\let\next\cite\fi\next}
+\def\gobblest"#1"{{\ttx#1}}
+\def\cite#1#2.{{\sc#1}\,\S#2.}
+\def\uninitialized{???}
+
+\def\readrefs{\the\prefs \@m=\secno \advance\@m-\baseno \advance\@m200
+ {\def\lmda##1{\global\let##1\relax}\@p=200
+ \loop\ifnum\@p<\@m \the\toks\@p \advance\@p 1 \repeat}
+ \loop\ifnum\@m<222 \the\toks\@m \advance\@m 1 \repeat}
+\def\readln{\read\sreffile to\next \expandafter\next\newline \readin}
+{\obeylines\gdef\newline{\unskip\noexpand
+ }}
+\def\donewithpage#1 {\let\readin=\relax}
+\catcode`\@=12
+
+\hsize=\pagewd \vsize=\maxdimen \output={\twillout}
+
+\def\inx{\flushout\end}
+\def\fin{\parfillskip 0pt plus 1fil
+ \let\topsecno=\nullsec
+ \message{Section names:}
+ \def\note##1##2.{\quad{\eightrm##1~##2.}}
+ \def\Q{\note{Cited in section}} % crossref for mention of a section
+ \def\Qs{\note{Cited in sections}} % crossref for mentions of a section
+ \def\U{\note{Used in section}} % crossref for use of a section
+ \def\Us{\note{Used in sections}} % crossref for uses of a section
+ \def\I{\par\hangindent 2em}\let\*=*
+ \readsections}
+\def\readsections{\input \jobname.sscn}
+\def\datethis{}
+
+% To produce only a subset of pages, put the page numbers on separate
+% lines in a file called pages.tex
+\let\Shipout=\shipout
+\newread\pages \newcount\nextpage \openin\pages=pages
+\def\getnextpage{\ifeof\pages\else
+ {\endlinechar=-1\read\pages to\next
+ \ifx\next\empty % in this case we should have eof now
+ \else\global\nextpage=\next\fi}\fi}
+\ifeof\pages\else\message{OK, I'll ship only the requested pages!}
+ \getnextpage\fi
+\def\shipout{\ifeof\pages\let\next=\Shipout
+ \else\ifnum\pageno=\nextpage\getnextpage\let\next=\Shipout
+ \else\let\next=\Tosspage\fi\fi \next}
+\newbox\garbage \def\Tosspage{\deadcycles=0\setbox\garbage=}
diff --git a/systems/knuth/local/lib/cwebmac.tex b/systems/knuth/local/lib/cwebmac.tex
new file mode 100644
index 0000000000..8890b63b4d
--- /dev/null
+++ b/systems/knuth/local/lib/cwebmac.tex
@@ -0,0 +1,545 @@
+% standard macros for CWEB listings (in addition to plain.tex)
+% Version 3.67 --- July 2006
+\ifx\renewenvironment\undefined\else\endinput\fi % LaTeX will use other macros
+\xdef\fmtversion{\fmtversion+CWEB3.67}
+\chardef\cwebversion=3 \chardef\cwebrevision=67
+\newif\ifpdf
+\ifx\pdf+\pdftrue\fi
+% Uncomment the following line if you want PDF goodies to be the default
+%\ifx\pdf-\else\pdftrue\fi
+\def\pdflinkcolor{0 0 1} % the RGB values for hyperlink color
+\newif\ifpdftex
+\ifx\pdfoutput\undefined \pdftexfalse \else\ifnum\pdfoutput=0 \pdftexfalse
+\else \pdftextrue \pdfoutput=1 \input pdfcolor \let\setcolor\pdfsetcolor \fi\fi
+\newif\ifacro \ifpdf\acrotrue\fi \ifpdftex\acrotrue\fi
+
+\let\:=\. % preserve a way to get the dot accent
+ % (all other accents will still work as usual)
+
+\parskip 0pt % no stretch between paragraphs
+\parindent 1em % for paragraphs and for the first line of C text
+
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps
+\def\CEE/{{\mc C\spacefactor1000}}
+\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}}
+\def\TEX/{\TeX}
+\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}}
+\def\Cee{\CEE/} % for backward compatibility
+\def\9#1{}
+ % with this definition of \9 you can say @:sort key}{TeX code@>
+ % to alphabetize an index entry by the sort key but format with the TeX code
+\font\eightrm=cmr8
+\let\sc=\eightrm % for smallish caps (NOT a caps-and-small-caps font)
+\let\mainfont=\tenrm
+\let\cmntfont\tenrm
+%\font\tenss=cmss10 \let\cmntfont\tenss % alternative comment font
+\font\titlefont=cmr7 scaled\magstep4 % title on the contents page
+\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title
+\font\tentex=cmtex10 % TeX extended character set (used in strings)
+\fontdimen7\tentex=0pt % no double space after sentences
+
+\def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way
+\def\&#1{\leavevmode\hbox{\bf
+ \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}%
+ #1\/\kern.05em}} % boldface type for reserved words
+\def\.#1{\leavevmode\hbox{\tentex % typewriter type for strings
+ \let\\=\BS % backslash in a string
+ \let\{=\LB % left brace in a string
+ \let\}=\RB % right brace in a string
+ \let\~=\TL % tilde in a string
+ \let\ =\SP % space in a string
+ \let\_=\UL % underline in a string
+ \let\&=\AM % ampersand in a string
+ \let\^=\CF % circumflex in a string
+ #1\kern.05em}}
+\def\){{\tentex\kern-.05em}\discretionary{\hbox{\tentex\BS}}{}{}}
+\def\AT{@} % at sign for control text (not needed in versions >= 2.9)
+\def\ATL{\par\noindent\bgroup\catcode`\_=12 \postATL} % print @l in limbo
+\def\postATL#1 #2 {\bf letter \\{\uppercase{\char"#1}}
+ tangles as \tentex "#2"\egroup\par}
+\def\noATL#1 #2 {}
+\def\noatl{\let\ATL=\noATL} % suppress output from @l
+\def\ATH{{\acrofalse\X\kern-.5em:Preprocessor definitions\X}}
+\let\PB=\relax % hook for program brackets |...| in TeX part or section name
+
+\chardef\AM=`\& % ampersand character in a string
+\chardef\BS=`\\ % backslash in a string
+\chardef\LB=`\{ % left brace in a string
+\chardef\RB=`\} % right brace in a string
+\def\SP{{\tt\char`\ }} % (visible) space in a string
+\chardef\TL=`\~ % tilde in a string
+\chardef\UL=`\_ % underline character in a string
+\chardef\CF=`\^ % circumflex character in a string
+
+\newbox\PPbox % symbol for ++
+\setbox\PPbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm+\kern-1pt+}\kern.5pt}
+\def\PP{\copy\PPbox}
+\newbox\MMbox \setbox\MMbox=\hbox{\kern.5pt\raise1pt\hbox{\sevensy\char0
+ \kern-1pt\char0}\kern.5pt}
+\def\MM{\copy\MMbox}
+\newbox\MGbox % symbol for ->
+\setbox\MGbox=\hbox{\kern-2pt\lower3pt\hbox{\teni\char'176}\kern1pt}
+\def\MG{\copy\MGbox}
+\def\MRL#1{\mathrel{\let\K==#1}}
+%\def\MRL#1{\KK#1}\def\KK#1#2{\buildrel\;#1\over{#2}}
+\let\GG=\gg
+\let\LL=\ll
+\let\NULL=\Lambda
+\mathchardef\AND="2026 % bitwise and; also \& (unary operator)
+\let\OR=\mid % bitwise or
+\let\XOR=\oplus % bitwise exclusive or
+\def\CM{{\sim}} % bitwise complement
+\newbox\MODbox \setbox\MODbox=\hbox{\eightrm\%}
+\def\MOD{\mathbin{\copy\MODbox}}
+\def\DC{\kern.1em{::}\kern.1em} % symbol for ::
+\def\PA{\mathbin{.*}} % symbol for .*
+\def\MGA{\mathbin{\MG*}} % symbol for ->*
+\def\this{\&{this}}
+
+\newbox\bak \setbox\bak=\hbox to -1em{} % backspace one em
+\newbox\bakk\setbox\bakk=\hbox to -2em{} % backspace two ems
+
+\newcount\ind % current indentation in ems
+\def\1{\global\advance\ind by1\hangindent\ind em} % indent one more notch
+\def\2{\global\advance\ind by-1} % indent one less notch
+\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement
+\def\4{\copy\bak} % backspace one notch
+\def\5{\hfil\penalty-1\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break
+\def\6{\ifmmode\else\par % forced break
+ \hangindent\ind em\noindent\kern\ind em\copy\bakk\ignorespaces\fi}
+\def\7{\Y\6} % forced break and a little extra space
+\def\8{\hskip-\ind em\hskip 2em} % no indentation
+
+\newcount\gdepth % depth of current major group, plus one
+\newcount\secpagedepth
+\secpagedepth=3 % page breaks will occur for depths -1, 0, and 1
+\newtoks\gtitle % title of current major group
+\newskip\intersecskip \intersecskip=12pt minus 3pt % space between sections
+\let\yskip=\smallskip
+\def\?{\mathrel?}
+\def\note#1#2.{\Y\noindent{\hangindent2em%
+ \baselineskip10pt\eightrm#1~\ifacro{\pdfnote#2.}\else#2\fi.\par}}
+
+\newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD
+\newtoks\toksE \newtoks\toksF \newtoks\usersanitizer
+\newcount\countA \countA=0 \newcount\countB \countB=0
+\newcount\countC \countC=0
+\newif\iftokprocessed \newif\ifTnum \newif\ifinstr
+{\def\\{\global\let\spacechar= }\\ }
+
+\ifacro % The following are pdf macros
+\def\thewidth{\the\wd0 \space}
+\def\theheight{\the\ht\strutbox\space}
+\def\thedepth{\the\dp\strutbox\space}
+\ifpdftex
+ \ifx\pdfannotlink\undefined\let\pdfannotlink\pdfstartlink\fi% for pdfTeX 0.14
+ \def\pdflink#1#2{\hbox{\pdfannotlink height\ht\strutbox depth\dp\strutbox
+ attr{/Border [0 0 0]} goto num #1 \BlueGreen #1\Black\pdfendlink}}
+\else\def\pdflink#1#2{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}%
+ \special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight
+ depth \thedepth << /Type /Annot /Subtype /Link
+ /Border [0 0 0] /A << /S /GoTo /D (#2) >> >>}\box0\relax}\fi
+\def\pdfnote#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}\maketoks}\the\toksA}
+\def\firstsecno#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}%
+ \def\makenote{\addtokens\toksB{\the\toksC}\def\makenote{\toksD={}
+ \toksC={}\let\space\empty}\makenote}\maketoks}}
+\def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+\def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}%
+ \ifcat\noexpand\first0\countB=`#1\else\countB=0\fi\toksA={#2}}
+\def\maketoks{\expandafter\poptoks\the\toksA|ENDTOKS|%
+ \ifnum\countB>`9 \countB=0 \fi
+ \ifnum\countB<`0
+ \ifnum0=\countC\else\makenote\fi
+ \ifx\first.\let\next=\maketoksdone\else
+ \let\next=\maketoks
+ \addtokens\toksB{\the\toksD}
+ \ifx\first,\addtokens\toksB{\space}\fi
+ \fi
+ \else \addtokens\toksC{\the\toksD}\global\countC=1\let\next=\maketoks
+ \fi
+ \next
+}
+\def\makenote{\addtokens\toksB
+ {\noexpand\pdflink{\the\toksC}{\romannumeral\the\toksC}}\toksC={}\global\countC=0}
+\def\maketoksdone{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+\def\pdfURL#1#2{\ifpdftex\pdfannotlink height\ht\strutbox depth\dp\strutbox
+ attr {/Border [0 0 0]} user { /Type /Action /Subtype /Link /A
+ << /S /URI /URI (#2) >>}\BlueGreen #1\Black \pdfendlink
+ \else \ifpdf{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}%
+ \special{pdf: ec}}\special{pdf: ann width \thewidth\space height \theheight
+ \space depth \thedepth\space << /Border [0 0 0]
+ /Type /Action /Subtype /Link /A << /S /URI /URI (#2) >> >>}\box0\relax}%
+ \else #1 ({\tt#2})\fi\fi}
+{\catcode`\~=12 \gdef\TILDE/{~}} % ~ in a URL
+{\catcode`\_=12 \gdef\UNDER/{_}} % _ in a URL
+\def\sanitizecommand#1#2{\addtokens\usersanitizer
+ {\noexpand\dosanitizecommand\noexpand#1{#2}}}
+\def\dosanitizecommand#1#2{\ifx\nxt#1\addF{#2}\fi}
+
+\catcode`\[=1 \catcode`\]=2 \catcode`\{=12 \catcode`\}=12
+ \def\lbchar[{] \def\rbchar[}]
+\catcode`\[=12 \catcode`\]=12 \catcode`\{=1 \catcode`\}=2
+\catcode`\~=12 \def\tildechar{~} \catcode`\~=13
+\catcode`\|=0 |catcode`|\=12 |def|bschar{\} |catcode`|\=0 \catcode`\|=12
+\def\makeoutlinetoks{\Tnumfalse\afterassignment\makeolproctok\let\nxt= }
+\def\makeolnexttok{\afterassignment\makeolproctok\let\nxt= }
+\def\makeolgobbletok{\afterassignment\makeolnexttok\let\nxt= }
+\def\addF#1{\addtokens\toksF{#1}\tokprocessedtrue}
+% now comes a routine to "sanitize" section names, for pdf outlines
+\def\makeolproctok{\tokprocessedfalse
+ \let\next\makeolnexttok % default
+ \ifx\nxt\outlinedone\let\next\outlinedone
+ \else\ifx{\nxt \else\ifx}\nxt \Tnumfalse \instrfalse % skip braces
+ \else\ifx$\nxt % or a $ sign
+ \else\ifx^\nxt \addF^\else\ifx_\nxt \addF_% sanitize ^ and _
+ \else\ifx\nxt\spacechar \addF\space
+ \else\if\noexpand\nxt\relax % we have a control sequence; is it one we know?
+ \ifx\nxt~\addF\space
+ \else\ifx\nxt\onespace\addF\space
+ \else\the\usersanitizer
+ \iftokprocessed\else\makeolproctokctli
+ \iftokprocessed\else\makeolproctokctlii
+ \iftokprocessed\else\makeolproctokctliii % if not recognised, skip it
+ \fi\fi\fi\fi\fi
+ \else % we don't have a control sequence, it's an ordinary char
+ \ifx/\nxt \addF{\string\/}% quote chars special to PDF with backslash
+ \else\ifx(\nxt \addF{\string\(}\else\ifx)\nxt \addF{\string\)}%
+ \else\ifx[\nxt \addF{\string\[}\else\ifx]\nxt \addF{\string\]}%
+ \else\expandafter\makeolproctokchar\meaning\nxt
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \next
+}
+\def\makeolproctokchar#1 #2 #3{\addF{#3}}
+\def\makeolproctokctli{%
+ \ifx\nxt\CEE\addF{C}\let\next\makeolgobbletok % \CEE/
+ \else\ifx\nxt\UNIX\addF{UNIX}\let\next\makeolgobbletok % \UNIX/
+ \else\ifx\nxt\TEX\addF{TeX}\let\next\makeolgobbletok % \TEX/
+ \else\ifx\nxt\TeX\addF{TeX}\else\ifx\nxt\LaTeX\addF{LaTeX}%
+ \else\ifx\nxt\CPLUSPLUS\addF{C++}\let\next\makeolgobbletok % \CPLUSPLUS/
+ \else\ifx\nxt\Cee\addF{C}%
+ \else\ifx\nxt\PB \let\next\makeolgobbletok \tokprocessedtrue % \PB{...}
+ \else\ifx\nxt\.\tokprocessedtrue\instrtrue % \.{...}
+ % skip \|
+ \else\ifx\nxt\\\ifinstr\addF{\bschar\bschar}\else\tokprocessedtrue\fi
+ \else\ifx\nxt\&\ifinstr\addF&\else\tokprocessedtrue\fi
+ \else\ifx\nxt\~\ifTnum\addF{0}\else\addF\tildechar\fi % 077->\T{\~77}
+ \else\ifx\nxt\_\ifTnum\addF{E}\else\addF_\fi % 0.1E5->\T{0.1\_5}
+ \else\ifx\nxt\^\ifTnum\addF{0x}\else\addF^\fi % 0x77 -> \T{\^77}
+ \else\ifx\nxt\$\ifTnum\tokprocessedtrue\else\addF$\fi % \T{77\$L}
+ \else\ifx\nxt\{\addF\lbchar \else\ifx\nxt\}\addF\rbchar
+ \else\ifx\nxt\ \addF\space \else\ifx\nxt\#\addF{\string\#}%
+ \else\ifx\nxt\PP\addF{++}\else\ifx\nxt\MM\addF{--}%
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+}
+\def\makeolproctokctlii{%
+ \ifx\nxt\MG\addF{->}\else\ifx\nxt\GG\addF{>>}%
+ \else\ifx\nxt\LL\addF{<<}\else\ifx\nxt\NULL\addF{NULL}%
+ \else\ifx\nxt\AND\addF&\else\ifx\nxt\OR\addF|%
+ \else\ifx\nxt\XOR\addF^\else\ifx\nxt\CM\addF\tildechar
+ \else\ifx\nxt\MOD\addF{\string\%}\else\ifx\nxt\DC\addF{::}%
+ \else\ifx\nxt\PA\addF{.*}\else\ifx\nxt\MGA\addF{->*}%
+ \else\ifx\nxt\this\addF{this}\else\ifx\nxt\?\addF?%
+ \else\ifx\nxt\E\addF{==}\else\ifx\nxt\G\addF{>=}%
+ \else\ifx\nxt\I\addF{!=}\else\ifx\nxt\K\addF{=}%
+ \else\ifx\nxt\l\addF{l}\else\ifx\nxt\L\addF{L}%
+ \else\ifx\nxt\o\addF{o}\else\ifx\nxt\O\addF{O}%
+ \else\ifx\nxt\R\addF!%
+ \else\ifx\nxt\T \Tnumtrue \let\next\makeolgobbletok
+ \tokprocessedtrue % \T{number}
+ \else\ifx\nxt\AM\addF&\else\ifx\nxt\%\addF{\string\%}%
+ \fi\fi\fi\fi\fi \fi\fi\fi\fi\fi
+ \fi\fi\fi\fi\fi \fi\fi\fi\fi\fi
+ \fi\fi\fi\fi\fi\fi
+}
+\def\makeolproctokctliii{%
+ \ifx\nxt\V\addF{||}\else\ifx\nxt\W\addF{&&}\else\ifx\nxt\Z\addF{<=}%
+ \else\ifx\nxt\*\addF*\else\ifx\nxt\Xand\addF{\space and\space}%
+ \else\ifx\nxt\Xandxeq\addF{\space and_eq\space}%
+ \else\ifx\nxt\Xbitand\addF{\space bitand\space}%
+ \else\ifx\nxt\Xbitor\addF{\space bitor\space}%
+ \else\ifx\nxt\Xcompl\addF{\space compl\space}%
+ \else\ifx\nxt\Xnot\addF{\space not\space}%
+ \else\ifx\nxt\Xnotxeq\addF{\space not_eq\space}%
+ \else\ifx\nxt\Xor\addF{\space or\space}%
+ \else\ifx\nxt\Xorxeq\addF{\space or_eq\space}%
+ \else\ifx\nxt\Xxor\addF{\space xor\space}%
+ \else\ifx\nxt\Xxorxeq\addF{\space xor_eq\space}%
+ \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi
+ \fi\fi\fi\fi\fi
+}
+\def\outlinedone{\edef\outlinest{\global\noexpand\toksE={\the\toksF}}%
+ \outlinest\let\outlinedone=\relax}
+\fi % End of pdf macros
+
+\def\lapstar{\rlap{*}}
+\def\stsec{\rightskip=0pt % get out of C mode (cf. \B)
+ \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50
+ \noindent{\let\*=\lapstar\bf\secstar.\quad}%
+ \ifpdftex\smash{\raise\baselineskip\hbox to0pt{%
+ \let\*=\empty\pdfdest num \secstar fith}}
+ \else\ifpdf\smash{\raise\baselineskip\hbox to0pt{%
+ \let\*=\empty\special{%
+ pdf: dest (\romannumeral\secstar) [ @thispage /FitH @ypos ]}}}\fi\fi}
+\let\startsection=\stsec
+\def\defin#1{\global\advance\ind by 2 \1\&{#1 } } % begin `define' or `format'
+\def\A{\note{See also section}} % xref for doubly defined section name
+\def\As{\note{See also sections}} % xref for multiply defined section name
+\def\B{\rightskip=0pt plus 100pt minus 10pt % go into C mode
+ \sfcode`;=3000
+ \pretolerance 10000
+ \hyphenpenalty 1000 % so strings can be broken (discretionary \ is inserted)
+ \exhyphenpenalty 10000
+ \global\ind=2 \1\ \unskip}
+\def\C#1{\5\5\quad$/\ast\,${\cmntfont #1}$\,\ast/$}
+\let\SHC\C % "// short comments" treated like "/* ordinary comments */"
+%\def\C#1{\5\5\quad$\triangleright\,${\cmntfont#1}$\,\triangleleft$}
+%\def\SHC#1{\5\5\quad$\diamond\,${\cmntfont#1}}
+\def\D{\defin{\#define}} % macro definition
+\let\E=\equiv % equivalence sign
+\def\ET{ and~} % conjunction between two section numbers
+\def\ETs{, and~} % conjunction between the last two of several section numbers
+\def\F{\defin{format}} % format definition
+\let\G=\ge % greater than or equal sign
+% \H is long Hungarian umlaut accent
+\let\I=\ne % unequal sign
+\def\J{\.{@\&}} % TANGLE's join operation
+\let\K== % assignment operator
+%\let\K=\leftarrow % "honest" alternative to standard assignment operator
+% \L is Polish letter suppressed-L
+\outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section
+ \vskip\intersecskip\startsection\ignorespaces}
+\outer\def\N#1#2#3.{% beginning of starred section
+ \ifacro{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi
+ \gdepth=#1\gtitle={#3}\MN{#2}%
+ \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small
+ \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi
+ \message{*\secno} % progress report
+ \def\stripprefix##1>{}\def\gtitletoks{#3}%
+ \edef\gtitletoks{\expandafter\stripprefix\meaning\gtitletoks}%
+ \edef\next{\write\cont{\ZZ{\gtitletoks}{#1}{\secno}% write to contents file
+ {\noexpand\the\pageno}{\the\toksE}}}\next % \ZZ{title}{depth}{sec}{page}{ss}
+ \ifpdftex\expandafter\xdef\csname curr#1\endcsname{\secno}
+ \ifnum#1>0\countB=#1 \advance\countB by-1
+ \advancenumber{chunk\the\countB.\expnumber{curr\the\countB}}\fi\fi
+ \ifpdf\special{pdf: outline #1 << /Title (\the\toksE) /Dest
+ [ @thispage /FitH @ypos ] >>}\fi
+ \ifon\startsection{\bf#3.\quad}\ignorespaces}
+\def\MN#1{\par % common code for \M, \N
+ {\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% remove \* from section name
+ \ifx\secno\secstar \onmaybe \else\ontrue \fi
+ \mark{{{\tensy x}\secno}{\the\gdepth}{\the\gtitle}}}
+% each \mark is {section reference or null}{depth plus 1}{group title}
+% \O is Scandinavian letter O-with-slash
+% \P is paragraph sign
+\def\Q{\note{This code is cited in section}} % xref for mention of a section
+\def\Qs{\note{This code is cited in sections}} % xref for mentions of a section
+\let\R=\lnot % logical not
+% \S is section sign
+\def\T#1{\leavevmode % octal, hex or decimal constant
+ \hbox{$\def\?{\kern.2em}%
+% \def\$##1{\egroup_{\,\rm##1}\bgroup}% suffix to constant % versions < 3.67
+ \def\$##1{\egroup_{\rm##1}\bgroup}% suffix to constant
+ \def\_{\cdot 10^{\aftergroup}}% power of ten (via dirty trick)
+ \let\~=\oct \let\^=\hex {#1}$}}
+\def\U{\note{This code is used in section}} % xref for use of a section
+\def\Us{\note{This code is used in sections}} % xref for uses of a section
+\let\V=\lor % logical or
+\let\W=\land % logical and
+\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi %$% section name
+ \XX$\langle\,${\let\I=\ne#2\eightrm\kern.5em
+ \ifacro{\pdfnote#1.}\else#1\fi}$\,\rangle$\XX}
+\def\Y{\par\yskip}
+\let\Z=\le
+\let\ZZ=\let % now you can \write the control sequence \ZZ
+\let\*=*
+
+\def\Xand{\W} \def\Xandxeq{\MRL{{\AND}{\K}}} \def\Xbitand{\AND}
+\def\Xbitor{\OR} \def\Xcompl{\CM} \def\Xnot{\R} \def\Xnotxeq{\I} \def\Xor{\V}
+\def\Xorxeq{\MRL{{\OR}{\K}}} \def\Xxor{\XOR} \def\Xxorxeq{\MRL{{\XOR}{\K}}}
+
+%\def\oct{\hbox{\rm\char'23\kern-.2em\it\aftergroup\?\aftergroup}} % WEB style
+%\def\hex{\hbox{\rm\char"7D\tt\aftergroup}} % WEB style
+\def\oct{\hbox{$^\circ$\kern-.1em\it\aftergroup\?\aftergroup}}% CWEB style
+\def\hex{\hbox{$^{\scriptscriptstyle\#}$\tt\aftergroup}} % CWEB style
+\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule
+ \hbox{\strut\kern2pt\.{#1}\kern2pt}}
+ \hrule}\vrule\kern2pt}} % verbatim string
+
+\def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue
+\newif\ifon \newif\iftitle \newif\ifpagesaved
+
+\newif\ifheader
+\def\lheader{\headertrue\mainfont\the\pageno\eightrm\qquad\grouptitle
+ \hfill\title\qquad\mainfont\topsecno} % top line on left-hand pages
+\def\rheader{\headertrue\mainfont\topsecno\eightrm\qquad\title\hfill
+ \grouptitle\qquad\mainfont\the\pageno} % top line on right-hand pages
+\def\grouptitle{\let\i=I\let\j=J\uppercase\expandafter{\expandafter
+ \takethree\topmark}}
+\def\topsecno{\expandafter\takeone\topmark}
+\def\takeone#1#2#3{#1}
+\def\taketwo#1#2#3{#2}
+\def\takethree#1#2#3{#3}
+\def\nullsec{\eightrm\kern-2em} % the \kern-2em cancels \qquad in headers
+
+\let\page=\pagebody \raggedbottom
+% \def\page{\box255 }\normalbottom % faster, but loses plain TeX footnotes
+\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi
+ \shipout\vbox{
+ \vbox to\fullpageheight{
+ \iftitle\global\titlefalse
+ \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi
+ \vfill#1}} % parameter #1 is the page itself
+ \global\advance\pageno by1}
+
+\gtitle={\.{CWEB} output} % this running head is reset by starred sections
+\mark{\noexpand\nullsec0{\the\gtitle}}
+\def\title{\expandafter\uppercase\expandafter{\jobname}}
+\def\topofcontents{\centerline{\titlefont\title}\vskip.7in
+ \vfill} % this material will start the table of contents page
+\def\startpdf{\ifpdftex\pdfcatalog{/PageMode /UseOutlines}\else
+ \ifpdf{\special{pdf: docview << /PageMode /UseOutlines >>}}\fi\fi}
+\def\botofcontents{\vfill
+ \centerline{\covernote}} % this material will end the table of contents page
+\def\covernote{}
+\def\contentspagenumber{0} % default page number for table of contents
+\newdimen\pagewidth \pagewidth=6.5in % the width of each page
+\newdimen\pageheight \pageheight=8.7in % the height of each page
+\newdimen\fullpageheight \fullpageheight=9in % page height including headlines
+\newdimen\pageshift \pageshift=0in % shift righthand pages wrt lefthand ones
+\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein
+ \fullpageheight=9truein\setpage}
+\def\setpage{\hsize\pagewidth\vsize\pageheight} % use after changing page size
+\def\contentsfile{\jobname.toc} % file that gets table of contents info
+\def\readcontents{\input \contentsfile}
+\def\readindex{\input \jobname.idx}
+\def\readsections{\input \jobname.scn}
+
+\newwrite\cont
+\output{\setbox0=\page % the first page is garbage
+ \openout\cont=\contentsfile
+ \write\cont{\catcode `\noexpand\@=11\relax} % \makeatletter
+ \global\output{\normaloutput\page\lheader\rheader}}
+\setpage
+\vbox to \vsize{} % the first \topmark won't be null
+
+\def\ch{\note{The following sections were changed by the change file:}
+ \let\*=\relax}
+\newbox\sbox % saved box preceding the index
+\newbox\lbox % lefthand column in the index
+\def\inx{\par\vskip6pt plus 1fil % we are beginning the index
+ \def\page{\box255 } \normalbottom
+ \write\cont{} % ensure that the contents file isn't empty
+ \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother
+ \closeout\cont % the contents information has been fully gathered
+ \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi
+ \global\setbox\sbox=\page \global\pagesavedtrue}
+ \pagesavedfalse \eject % eject the page-so-far and predecessors
+ \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box
+ \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height
+ \hsize=.5\pagewidth \advance\hsize by -10pt
+ % column width for the index (20pt between cols)
+ \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
+ \def\lr{L} % this tells whether the left or right column is next
+ \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
+ \else\normaloutput{\vbox to\pageheight{\box\sbox\vss
+ \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader
+ \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi}
+ \message{Index:}
+ \parskip 0pt plus .5pt
+ \outer\def\I##1, ##2.{\par\hangindent2em\noindent##1:\kern1em
+ \ifacro\pdfnote##2.\else##2\fi.} % index entry
+ \def\[##1]{$\underline{##1}$} % underlined index item
+ \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar
+ \hyphenpenalty 10000 \parindent0pt
+ \readindex}
+\def\fin{\par\vfill\eject % this is done when we are ending the index
+ \ifpagesaved\null\vfill\eject\fi % output a null index column
+ \if L\lr\else\null\vfill\eject\fi % finish the current page
+ \parfillskip 0pt plus 1fil
+ \def\grouptitle{NAMES OF THE SECTIONS}
+ \let\topsecno=\nullsec
+ \message{Section names:}
+ \output={\normaloutput\page\lheader\rheader}
+ \setpage
+ \def\note##1##2.{\quad{\eightrm##1~\ifacro{\pdfnote##2.}\else{##2}\fi.}}
+ \def\Q{\note{Cited in section}} % crossref for mention of a section
+ \def\Qs{\note{Cited in sections}} % crossref for mentions of a section
+ \def\U{\note{Used in section}} % crossref for use of a section
+ \def\Us{\note{Used in sections}} % crossref for uses of a section
+ \def\I{\par\hangindent 2em}\let\*=*
+ \ifacro \def\outsecname{Names of the sections} \let\Xpdf\X
+ \ifpdftex \makebookmarks \pdfdest name {NOS} fitb
+ \pdfoutline goto name {NOS} count -\secno {\outsecname}
+ \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.%
+ {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}%
+ \pdfoutline goto num \the\toksA \expandafter{\the\toksE}}
+ \else\ifpdf
+ \special{pdf: outline -1 << /Title (\outsecname)
+ /Dest [ @thispage /FitH @ypos ] >>}
+ \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.%
+ {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}%
+ \special{pdf: outline 0 << /Title (\the\toksE)
+ /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}}
+ \fi\fi\fi
+ \readsections}
+\def\makebookmarks{\let\ZZ=\writebookmarkline \readcontents\relax}
+\def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
+ \else \csname#1\endcsname \fi} % Petr Olsak's macros from texinfo.tex
+\def\advancenumber#1{\countA=\expnumber{#1}\relax \advance\countA by1
+ \expandafter\xdef\csname#1\endcsname{\the\countA}}
+\def\writebookmarkline#1#2#3#4#5{{%
+ \let\(=\let \let\)=\let \let\[=\let \let\]=\let \let\/=\let
+ \pdfoutline goto num #3 count -\expnumber{chunk#2.#3} {#5}}}
+\def\con{\par\vfill\eject % finish the section names
+% \ifodd\pageno\else\titletrue\null\vfill\eject\fi % for duplex printers
+ \rightskip 0pt \hyphenpenalty 50 \tolerance 200
+ \setpage \output={\normaloutput\page\lheader\rheader}
+ \titletrue % prepare to output the table of contents
+ \pageno=\contentspagenumber
+ \def\grouptitle{TABLE OF CONTENTS}
+ \message{Table of contents:}
+ \topofcontents \startpdf
+ \line{\hfil Section\hbox to3em{\hss Page}}
+ \let\ZZ=\contentsline
+ \readcontents\relax % read the contents info
+ \botofcontents \end} % print the contents page(s) and terminate
+\def\contentsline#1#2#3#4#5{\ifnum#2=0 \smallbreak\fi
+ \line{\consetup{#2}#1
+ \rm\leaders\hbox to .5em{.\hfil}\hfil
+ \ \ifacro\pdflink{#3}{\romannumeral#3}\else#3\fi\hbox to3em{\hss#4}}}
+\def\consetup#1{\ifcase#1 \bf % depth -1 (@**)
+ \or % depth 0 (@*)
+ \or \hskip2em % depth 1 (@*1)
+ \or \hskip4em \or \hskip6em \or \hskip8em \or \hskip10em % depth 2,3,4,5
+ \else \hskip12em \fi} % depth 6 or more
+\def\noinx{\let\inx=\end} % no indexes or table of contents
+\def\nosecs{\let\FIN=\fin \def\fin{\let\parfillskip=\end \FIN}}
+ % no index of section names or table of contents
+\def\nocon{\let\con=\end} % no table of contents
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\twodigits
+\def\hours{\twodigits=\time \divide\twodigits by 60 \printtwodigits
+ \multiply\twodigits by-60 \advance\twodigits by\time :\printtwodigits}
+\def\gobbleone1{}
+\def\printtwodigits{\advance\twodigits100
+ \expandafter\gobbleone\number\twodigits
+ \advance\twodigits-100 }
+\def\TeX{{\ifmmode\it\fi
+ \leavevmode\hbox{T\kern-.1667em\lower.424ex\hbox{E}\hskip-.125em X}}}
+\def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi}
+\def\datethis{\def\startsection{\leftline{\sc\today\ at \hours}\bigskip
+ \let\startsection=\stsec\stsec}}
+ % say `\datethis' in limbo, to get your listing timestamped before section 1
+%\def\datecontentspage{% versions up to 3.65
+% \def\topofcontents{\leftline{\sc\today\ at \hours}\bigskip
+% \centerline{\titlefont\title}\vfill}} % timestamps the contents page
+\def\datecontentspage{% changed in version 3.66
+ \def\botofcontents{\vfill
+ \centerline{\covernote}
+ \bigskip
+ \leftline{\sc\today\ at \hours}}} % timestamps the contents page
diff --git a/systems/knuth/local/lib/cyracc.def b/systems/knuth/local/lib/cyracc.def
new file mode 100644
index 0000000000..8692858e92
--- /dev/null
+++ b/systems/knuth/local/lib/cyracc.def
@@ -0,0 +1,273 @@
+%% @texfile{
+%% filename="cyracc.def",
+%% version="2.1",
+%% date="31-MAY-1991",
+%% filetype="TeX: option",
+%% copyright="Copyright (C) American Mathematical Society,
+%% all rights reserved. Copying of this file is
+%% authorized only if either:
+%% (1) you make absolutely no changes to your copy
+%% including name; OR
+%% (2) if you do make changes, you first rename it to some
+%% other name.",
+%% author="American Mathematical Society",
+%% address="American Mathematical Society,
+%% Technical Support Group,
+%% P. O. Box 6248,
+%% Providence, RI 02940,
+%% USA",
+%% telephone="401-455-4080 or (in the USA) 800-321-4AMS",
+%% email="Internet: Tech-Support@Math.AMS.com",
+%% codetable="ISO/ASCII",
+%% checksumtype="line count",
+%% checksum="273",
+%% keywords="amsfonts, tex, cyrillic",
+%% abstract="This file contains definitions of accents and
+%% special characters required for use of cyrillic fonts
+%% arranged according to the AMS scheme.
+%% The \font specification(s) should be made elsewhere, and should include
+%% \newfam\cyrfam
+%% \font\...cyr=wncyr... (e.g. \font\tencyr=wncyr10 )
+%% An appropriate definition for cyrillic, usable in math and text, is
+%% \def\cyr{\fam\cyrfam\...cyr\cyracc}
+%% More extensive instructions for using cyrillic fonts are included in
+%% the AMSFonts 2.0 User's Guide."
+%% }
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+\chardef\tempcat=\the\catcode`\@
+\catcode`\@=11
+
+% The first set of definitions is what will be in effect outside of cyrillic,
+% i.e., in transliterated text.
+
+% Definition of \cydot is not ideal; real dot should be in an accent font.
+\def\cydot{{\mathsurround=0pt$\cdot$}}
+
+% \ubar is the definition of \b from PLAIN; it may not work in math mode.
+\def\ubar#1{\oalign{#1\crcr\hidewidth
+ \vbox to.2ex{\hbox{\char22}\vss}\hidewidth}}
+
+% Soft and hard signs are represented in transliteration by prime and
+% double prime respectively.
+\def\cprime{\/{\mathsurround=0pt$'$}}
+\def\Cprime{{\mathsurround=0pt$'$}}
+\def\cdprime{\/{\mathsurround=0pt$''$}}
+\def\Cdprime{{\mathsurround=0pt$\ubar{\hbox{$''$}}$}}
+
+\def\dbar{dj} % make real barred d when accent font available
+\def\Dbar{Dj} % ""
+
+% It would be nice to make these control sequences unnecessary; that will
+% probably require d to become active.
+\def\dz{dz}
+\def\Dz{Dz}
+\def\dzh{dzh\cydot }
+\def\Dzh{Dzh\cydot }
+
+
+% Stress marks are sometimes used in cyrillic text, mainly for pedagogic
+% reasons. These definitions support stress marks within {\cyr ...};
+% in transliteration, they are suppressed. Stress is indicated in
+% input by \! preceding a letter on which a stress mark is to appear;
+% an acute accent will appear in the output. Since \! is also a plain
+% command, for negative thin space in math mode, test for math mode and
+% include the plain definition as appropriate.
+
+\def\@gobble#1{}
+\def\@testgrave{\`}
+\def\@stressit{\futurelet\chartest\@stresschar }
+
+\def\@stresschar#1{%
+ \ifx #1y\def\result{\futurelet\chartest\@yligature}%
+ \else \ifx #1Y\def\result{\futurelet\chartest\@Yligature}%
+ \else \ifx\chartest\@testgrave \def\result{\accent"26 }%
+ \else \def\result{\accent"26 #1}%
+ \fi \fi \fi
+ \result }
+
+\def\@yligature{%
+ \ifx a\chartest \def\result{\accent"26 \char"1F \@gobble}%
+ \else \ifx u\chartest \def\result{\accent"26 \char"18 \@gobble}%
+ \else \def\result{\accent"26 y}%
+ \fi \fi
+ \result }
+
+\def\@Yligature{%
+ \ifx a\chartest \def\result{\accent"26 \char"17 \@gobble}%
+ \else \ifx A\chartest \def\result{\accent"26 \char"17 \@gobble}%
+ \else \ifx u\chartest \def\result{\accent"26 \char"10 \@gobble}%
+ \else \ifx U\chartest \def\result{\accent"26 \char"10 \@gobble}%
+ \else \def\result{\accent"26 Y}%
+ \fi \fi \fi \fi
+ \result }
+
+\def\!{\ifmmode \mskip-\thinmuskip \fi}
+
+
+% These are the definitions that will produce cyrillic letters within
+% {\cyr ...} for letters represented in input by control sequences and
+% accented latin letters.
+
+\def\cyracc{%
+ \def\cydot{{\kern0pt}}%
+ \def\cprime{\char"7E }\def\Cprime{\char"5E }%
+ \def\cdprime{\char"7F }\def\Cdprime{\char"5F }%
+ \def\dbar{dj}\def\Dbar{Dj}%
+ \def\dz{\char"1E }\def\Dz{\char"16 }%
+ \def\dzh{\char"0A }\def\Dzh{\char"02 }%
+ \def\'##1{\if c##1\char"0F %
+ \else \if C##1\char"07 %
+ \else \accent"26 ##1\fi \fi }%
+ \def\`##1{\if e##1\char"0B %
+ \else \if E##1\char"03 %
+ \else \errmessage{accent \string\` not defined in cyrillic}%
+ ##1\fi \fi }%
+ \def\=##1{\if e##1\char"0D %
+ \else \if E##1\char"05 %
+ \else \if \i##1\char"0C %
+ \else \if I##1\char"04 %
+ \else \errmessage{accent \string\= not defined in cyrillic}%
+ ##1\fi \fi \fi \fi }%
+ \def\u##1{\if \i##1\accent"24 i%
+ \else \accent"24 ##1\fi }%
+ \def\"##1{\if \i##1\accent"20 \char"3D %
+ \else \if I##1\accent"20 \char"04 %
+ \else \accent"20 ##1\fi \fi }%
+ \def\!{\ifmmode \def\result{\mskip-\thinmuskip}%
+ \else \def\result{\@stressit}\fi \result}}
+
+
+% If cyrillic strings are to be included in \mark's, the accents need
+% to be kept in unexpanded form until the \mark's are interpreted.
+% See note on \mark below for details.
+
+\def\keep@cyracc{\let\cyr=\relax \let\i=\relax
+ \let\ubar=\relax \let\cydot=\relax
+ \let\cprime=\relax \let\Cprime=\relax
+ \let\cdprime=\relax \let\Cdprime=\relax
+ \let\dbar=\relax \let\Dbar=\relax
+ \let\dz=\relax \let\Dz=\relax
+ \let\dzh=\relax \let\Dzh=\relax
+ \let\'=\relax \let\`=\relax \let\==\relax
+ \let\u=\relax \let\"=\relax \let\!=\relax }
+
+
+\catcode`\@=\tempcat
+\endinput
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+% ***** CYRACC.DEF *****
+%
+% Access to the cyrillic is through the MR 1980 transliteration scheme,
+% using TeX/Metafont ligature capability. (An alternate set of ligatures
+% exists, for accessing letters beyond the 26 located in the font positions
+% of the 26 roman letters. An authoritative list of the ligature rules is
+% given below, under the heading Ligatures.) Successful use of this file
+% requires appropriate cyrillic and accent fonts, arranged according to
+% the AMS/MR scheme.
+%
+% There are some weaknesses in this ligaturing and accent system.
+% The following are known to be less than satisfactory:
+%
+% the intermediate `ligatures' for shch access numerals;
+% primes (transliteration of hard/soft signs) use math mode;
+% underbar on `cap' double prime is too low (hard sign);
+% thorn d's don't yet exist;
+% \cydot uses math mode;
+% this scheme is not compatible with automatic Russian hyphenation.
+%
+%
+% Notes on the macros:
+%
+% The single space following each hex character code is necessary
+% to prevent the next input character from being gobbled.
+%
+%
+% Note on \mark :
+%
+% Control sequences such as these cyrillic accents should remain
+% unexpanded when generating \mark's; otherwise, the definition of
+% the expanded strings in effect when the \mark is used may be
+% inappropriate. By \let\...=\relax , the \... takes on the important
+% characteristic of a primitive that it will not be expanded further.
+% The following code has been shown to generate correct \mark entries
+% that can be used successfully in running heads:
+%
+% \newtoks\marktoks
+% \def\domark{\keep@cyracc \mark{\the\marktoks}}
+%
+% In running heads, the marks should be accessed using \the to expand
+% the tokens strings properly.
+%
+%
+% Ligatures
+%
+% This is a list of all the letter ligatures in the AMS cyrillic fonts,
+% and in other cyrillic fonts (such as those developed at the University
+% of Washington that use the AMS arrangement).
+%
+% LOWER CASE LIGATURES
+%
+% A. All-letter ligatures
+%
+% ch = `q
+% dj = '016
+% kh = `h
+% lj = '011
+% nj = '010
+% sh = `x; xc = `7 (shc); 7h = `w (shch)
+% ts = `c
+% ya = '037
+% yu = '030
+% zh = '031
+%
+% B. Ligatures with keyed numerals
+%
+% c1 = '017
+% d1 = '016 (dj); d2 = '012 (dzh); d3 = '036 (dz)
+% e1 = '013; e2 = '015
+% i1 = '014
+% j1 = '037 (ya); j2 = '030 (yu)
+% l1 = '011 (lj)
+% n1 = '010 (nj)
+% p1 = '176; p2 = '177
+% z1 = '031 (zh)
+%
+% UPPER CASE LIGATURES
+% A. All-letter ligatures
+%
+% CH, Ch = `Q
+% KH, Kh = `H
+% LJ, Lj = '001
+% NJ, Nj = '000
+% SH, Sh = `X; XH, Xh = `6 (SHC, SHc, Shc);
+% 6H, 6h = `W (SHCH, SHCh, SHch, Shch)
+% TS, Ts = `C
+% YA, Ya = '027
+% YU, Yu = '020
+% ZH, Zh = '021
+%
+% B. Ligatures with keyed numerals
+%
+% C1 = '007
+% D1 = '006 (DJ); D2 = '002 (DZH); D3 = '026 (DZ)
+% E1 = '003; E2 = '005
+% I1 = '004
+% J1 = '027 (YA); J2 = '020 (YU)
+% L1 = '001 (LJ)
+% N0 = '175 (Number sign); N1 = '000 (NJ)
+% P1 = '136; P2 = '137
+% Z1 = '021 (ZH)
+%
+% DEACTIVATED LIGATURES
+%
+% The dzh and DZH ligatures were originally built in the following manner,
+% but the results were not compatible with desired keyboarding schemes.
+%
+% dz = '036; '036(roman s)h = '012 (dzh)
+% DZ = '026; '026(roman S)H = '002 (DZH)
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/systems/knuth/local/lib/domino.mf b/systems/knuth/local/lib/domino.mf
new file mode 100644
index 0000000000..6bd9da121b
--- /dev/null
+++ b/systems/knuth/local/lib/domino.mf
@@ -0,0 +1,100 @@
+% a font for double-9 dominoes
+% initially designed to work best on 300dpi printer
+
+% Example: To typeset a 2x3 array containing a vertical "1 over 2"
+% followed by horizontal "3 4" over horizontal "5 6", say
+% \obeylines \catcode`\^=12 \offinterlineskip
+% ^1<3>4
+% v2<5>6
+
+% One can also typeset individual half dominoes without boundaries
+
+% Note: The "real" dominoes (made in Taiwan) have slightly different
+% proportions, more or less as if the centers were at 17,32,47 instead
+% instead of 16,32,48. But the present scheme seems mathematically better.
+mode_setup;
+p#:=in#/300;
+d#:=64p#;
+define_pixels(p);
+
+font_identifier:="DOMINO";
+font_coding_scheme:="dominoes";
+font_size:=10pt#;
+
+pickup pencircle scaled p; tiny_pen=savepen;
+pickup pencircle scaled 13p; huge_pen=savepen;
+% I first tried 15p, and it looked OK, but the Taiwan set has smaller dots
+pickup pencircle scaled 3p; bound_pen=savepen;
+
+def begindomchar(expr code)=
+ beginchar(code,d#,d#,0);
+ pickup tiny_pen;
+ for x=0 step 2p until 60p:
+ drawdot (x+.5p,1.5p); drawdot (x+1.5p,.5p);
+ drawdot (.5p,x+3.5p); drawdot (1.5p,x+2.5p);
+ drawdot (w-1.5p,x+1.5p); drawdot (w-.5p,x+.5p);
+ drawdot (x+2.5p,h-.5p); drawdot (x+3.5p,h-1.5p);
+ endfor
+% fill (2p,2p)--(w-2p,2p)--(w-2p,h-2p)--(2p,h-2p)--cycle;
+ fill (3p,3p)--(w-3p,3p)--(w-3p,h-3p)--(3p,h-3p)--cycle;
+ pickup huge_pen;
+enddef;
+
+def dota = erase drawdot(16p,48p) enddef;
+def dotb = erase drawdot(32p,48p) enddef;
+def dotc = erase drawdot(48p,48p) enddef;
+def dotd = erase drawdot(16p,32p) enddef;
+def dote = erase drawdot(32p,32p) enddef;
+def dotf = erase drawdot(48p,32p) enddef;
+def dotg = erase drawdot(16p,16p) enddef;
+def doth = erase drawdot(32p,16p) enddef;
+def doti = erase drawdot(48p,16p) enddef;
+
+def v(expr code)=128+ASCII code enddef;
+ % change character code for vertical format
+
+begindomchar("0"); endchar;
+begindomchar("1"); dote; endchar;
+%begindomchar("2"); dotc; dotg; endchar;
+%begindomchar(v("2")); dota; doti; endchar; % in vertical position
+%begindomchar("3"); dotc; dote; dotg; endchar;
+%begindomchar(v("3")); dota; dote; doti; endchar; % in vertical position
+begindomchar("2"); dota; doti; endchar;
+begindomchar(v("2")); dotc; dotg; endchar; % in vertical position
+begindomchar("3"); dota; dote; doti; endchar;
+begindomchar(v("3")); dotc; dote; dotg; endchar; % in vertical position
+begindomchar("4"); dota; dotc; dotg; doti; endchar;
+begindomchar("5"); dota; dotc; dote; dotg; doti; endchar;
+begindomchar("6"); dota; dotb; dotc; dotg; doth; doti; endchar;
+begindomchar(v("6")); dota; dotc; dotd; dotf; dotg; doti; endchar; % vert
+begindomchar("7"); dota; dotb; dotc; dote; dotg; doth; doti; endchar;
+begindomchar(v("7")); dota; dotc; dotd; dote; dotf; dotg; doti; endchar; % vert
+begindomchar("8"); dota; dotb; dotc; dotd; dotf; dotg; doth; doti; endchar;
+begindomchar("9"); dota; dotb; dotc; dotd; dote; dotf; dotg; doth; doti;
+ endchar;
+
+beginchar("<",d#,d#,0); % outline of left half
+% fill (w-2.5p,h-2p)--(w+2.5p,h-2p)--(w+2.5p,2p)--(w-2.5p,2p)--cycle;
+ fill (w-3.5p,h-3p)--(w+3.5p,h-3p)--(w+3.5p,3p)--(w-3.5p,3p)--cycle;
+endchar;
+beginchar(">",d#,d#,0); % outline of right half
+% fill (-2.5p,h-2p)--(2.5p,h-2p)--(2.5p,2p)--(-2.5p,2p)--cycle;
+ fill (-3.5p,h-3p)--(3.5p,h-3p)--(3.5p,3p)--(-3.5p,3p)--cycle;
+endchar;
+beginchar("^",d#,d#,0); % outline of top half
+% fill (2p,-2.5p)--(2p,2.5p)--(w-2p,2.5p)--(w-2p,-2.5p)--cycle;
+ fill (3p,-3.5p)--(3p,3.5p)--(w-3p,3.5p)--(w-3p,-3.5p)--cycle;
+endchar;
+beginchar("v",d#,d#,0); % outline of bottom half
+% fill (2p,h-2.5p)--(2p,h+2.5p)--(w-2p,h+2.5p)--(w-2p,h-2.5p)--cycle;
+ fill (3p,h-3.5p)--(3p,h+3.5p)--(w-3p,h+3.5p)--(w-3p,h-3.5p)--cycle;
+endchar;
+
+ligtable "^": "v": "2" |=: v("2"), v("2") kern-d#,
+ "3" |=: v("3"), v("3") kern-d#,
+ "6" |=: v("6"), v("6") kern-d#,
+ "7" |=: v("7"), v("7") kern-d#, skipto 1;
+ligtable "<": ">": "2" kern-d#, "3" kern-d#, "6" kern-d#, "7" kern-d#, 1::
+ "0" kern-d#, "1" kern-d#, "4" kern-d#, "5" kern-d#, "8" kern-d#, "9" kern-d#;
+
+end.
diff --git a/systems/knuth/local/lib/e.mft b/systems/knuth/local/lib/e.mft
new file mode 100644
index 0000000000..729652d263
--- /dev/null
+++ b/systems/knuth/local/lib/e.mft
@@ -0,0 +1,33 @@
+%%%% MFT commands for the Computer Modern book (Volume E)
+%%% } ( ) ]] {{ }} ! ^ %%%% tokens that need no special formatting
+%%% step upto downto %%%% boldface binary operators
+%%% addto fill unfill draw undraw %%%% boldface unary operators
+%%% addto filldraw unfilldraw drawdot undrawdot erase pickup
+%%% addto exitunless stop incr decr proofrulethickness screenrule
+%%% addto define_pixels define_whole_pixels define_whole_vertical_pixels
+%%% addto define_blacker_pixels define_whole_blacker_pixels
+%%% addto define_whole_vertical_blacker_pixels
+%%% addto define_corrected_pixels lowres_fix proofoffset penstroke
+%%% addto beginchar beginarithchar italcorr font_size font_slant
+%%% addto font_normal_space font_normal_stretch font_normal_shrink font_quad
+%%% addto font_x_height font_extra_space font_identifier font_coding_scheme
+%%% addto cmchar iff generate adjust_fit math_fit labels penlabels
+%%% addto stroke circ_stroke padded
+%%% enddef endchar %%%% boldface closing
+%%% true relax mode_setup font_setup %%%% boldface nullary operators
+%%% true clearit shipit cullit openit showit clearxy clearpen
+%%% true nodisplay notransforms screenchars screenstrokes imagerules
+%%% .. ... .... -- --- %%%% path operators made of dots and dashes
+%%% length flex abs dir %%%% unary operators to be in roman type
+%%% length unitvector inverse ceiling hround vround Vround counterclockwise
+%%% length tensepath byte reflectedabout rotatedaround magstep max min
+%%% and mod dotprod intersectionpoint softjoin %%%% binary operators to be roman
+%%% ++ ** %%%% binary operators made of two special characters
+%%% penoffset goodval direction directionpoint %%%% operators that take "of"
+%%% pausing tolerance pixels_per_inch blacker o_correction %%%% internals
+%%% pausing screen_rows screen_cols currentwindow displaying
+%%% pausing pen_top pen_bot pen_lft pen_rt shrink_fit rt lft top bot
+%%% = <= >= <> %%%% conversions for the SAIL character set only
+%%% good crisp fine tiny rule light_rule cal light_cal med_cal heavy_cal
+%%% good term fudged mfudged sloped_serif tilted med_tilted
+%%% pausing slant fudge math_spread superness superpull beak_darkness ligs
diff --git a/systems/knuth/local/lib/epsf.tex b/systems/knuth/local/lib/epsf.tex
new file mode 100644
index 0000000000..81774da682
--- /dev/null
+++ b/systems/knuth/local/lib/epsf.tex
@@ -0,0 +1,215 @@
+% EPSF.TEX macro file:
+% Written by Tomas Rokicki of Radical Eye Software, 29 Mar 1989.
+% Revised by Don Knuth, 3 Jan 1990.
+% Revised by Tomas Rokicki to accept bounding boxes with no
+% space after the colon, 18 Jul 1990.
+% Revised by Knuth to always report if no bounding box found, 27 Nov 2012.
+%
+% TeX macros to include an Encapsulated PostScript graphic.
+% Works by finding the bounding box comment,
+% calculating the correct scale values, and inserting a vbox
+% of the appropriate size at the current position in the TeX document.
+%
+% To use with the center environment of LaTeX, preface the \epsffile
+% call with a \leavevmode. (LaTeX should probably supply this itself
+% for the center environment.)
+%
+% To use, simply say
+% \input epsf % somewhere early on in your TeX file
+% \epsfbox{filename.ps} % where you want to insert a vbox for a figure
+%
+% Alternatively, you can type
+%
+% \epsfbox[0 0 30 50]{filename.ps} % to supply your own BB
+%
+% which will not read in the file, and will instead use the bounding
+% box you specify.
+%
+% The effect will be to typeset the figure as a TeX box, at the
+% point of your \epsfbox command. By default, the graphic will have its
+% `natural' width (namely the width of its bounding box, as described
+% in filename.ps). The TeX box will have depth zero.
+%
+% You can enlarge or reduce the figure by saying
+% \epsfxsize=<dimen> \epsfbox{filename.ps}
+% (or
+% \epsfysize=<dimen> \epsfbox{filename.ps}
+% instead). Then the width of the TeX box will be \epsfxsize and its
+% height will be scaled proportionately (or the height will be
+% \epsfysize and its width will be scaled proportionately). The
+% width (and height) is restored to zero after each use.
+%
+% A more general facility for sizing is available by defining the
+% \epsfsize macro. Normally you can redefine this macro
+% to do almost anything. The first parameter is the natural x size of
+% the PostScript graphic, the second parameter is the natural y size
+% of the PostScript graphic. It must return the xsize to use, or 0 if
+% natural scaling is to be used. Common uses include:
+%
+% \epsfxsize % just leave the old value alone
+% 0pt % use the natural sizes
+% #1 % use the natural sizes
+% \hsize % scale to full width
+% 0.5#1 % scale to 50% of natural size
+% \ifnum#1>\hsize\hsize\else#1\fi % smaller of natural, hsize
+%
+% If you want TeX to report the size of the figure (as a message
+% on your terminal when it processes each figure), say `\epsfverbosetrue'.
+%
+\newread\epsffilein % file to \read
+\newif\ifepsffileok % continue looking for the bounding box?
+\newif\ifepsfbbfound % success?
+\newif\ifepsfverbose % report what you're making?
+\newdimen\epsfxsize % horizontal size after scaling
+\newdimen\epsfysize % vertical size after scaling
+\newdimen\epsftsize % horizontal size before scaling
+\newdimen\epsfrsize % vertical size before scaling
+\newdimen\epsftmp % register for arithmetic manipulation
+\newdimen\pspoints % conversion factor
+%
+\pspoints=1bp % Adobe points are `big'
+\epsfxsize=0pt % Default value, means `use natural size'
+\epsfysize=0pt % ditto
+%
+\def\epsfbox#1{\global\def\epsfllx{72}\global\def\epsflly{72}%
+ \global\def\epsfurx{540}\global\def\epsfury{720}%
+ \def\lbracket{[}\def\testit{#1}\ifx\testit\lbracket
+ \let\next=\epsfgetlitbb\else\let\next=\epsfnormal\fi\next{#1}}%
+%
+\def\epsfgetlitbb#1#2 #3 #4 #5]#6{\epsfgrab #2 #3 #4 #5 .\\%
+ \epsfsetgraph{#6}}%
+%
+\def\epsfnormal#1{\epsfgetbb{#1}\epsfsetgraph{#1}}%
+%
+\def\epsfgetbb#1{%
+%
+% The first thing we need to do is to open the
+% PostScript file, if possible.
+%
+\openin\epsffilein=#1
+\ifeof\epsffilein\errmessage{I couldn't open #1, will ignore it}\else
+%
+% Okay, we got it. Now we'll scan lines until we find one that doesn't
+% start with %. We're looking for the bounding box comment.
+%
+ {\epsffileoktrue \chardef\other=12
+ \epsfbbfoundfalse % added by Knuth, 27 Nov 2012
+ \def\do##1{\catcode`##1=\other}\dospecials \catcode`\ =10
+ \loop
+ \read\epsffilein to \epsffileline
+ \ifeof\epsffilein\epsffileokfalse\else
+%
+% We check to see if the first character is a % sign;
+% if not, we stop reading (unless the line was entirely blank);
+% if so, we look further and stop only if the line begins with
+% `%%BoundingBox:'.
+%
+ \expandafter\epsfaux\epsffileline:. \\%
+ \fi
+ \ifepsffileok\repeat
+ \ifepsfbbfound\else
+% \ifepsfverbose\message{No bounding box comment in #1; using defaults}\fi\fi
+ \errmessage{No bounding box comment in #1; using defaults}\fi
+ }\closein\epsffilein\fi}%
+%
+% Now we have to calculate the scale and offset values to use.
+% First we compute the natural sizes.
+%
+\def\epsfclipstring{}% do we clip or not? If so,
+\def\epsfclipon{\def\epsfclipstring{ clip}}%
+\def\epsfclipoff{\def\epsfclipstring{}}%
+%
+\def\epsfsetgraph#1{%
+ \epsfrsize=\epsfury\pspoints
+ \advance\epsfrsize by-\epsflly\pspoints
+ \epsftsize=\epsfurx\pspoints
+ \advance\epsftsize by-\epsfllx\pspoints
+%
+% If `epsfxsize' is 0, we default to the natural size of the picture.
+% Otherwise we scale the graph to be \epsfxsize wide.
+%
+ \epsfxsize\epsfsize\epsftsize\epsfrsize
+ \ifnum\epsfxsize=0 \ifnum\epsfysize=0
+ \epsfxsize=\epsftsize \epsfysize=\epsfrsize
+ \epsfrsize=0pt
+%
+% We have a sticky problem here: TeX doesn't do floating point arithmetic!
+% Our goal is to compute y = rx/t. The following loop does this reasonably
+% fast, with an error of at most about 16 sp (about 1/4000 pt).
+%
+ \else\epsftmp=\epsftsize \divide\epsftmp\epsfrsize
+ \epsfxsize=\epsfysize \multiply\epsfxsize\epsftmp
+ \multiply\epsftmp\epsfrsize \advance\epsftsize-\epsftmp
+ \epsftmp=\epsfysize
+ \loop \advance\epsftsize\epsftsize \divide\epsftmp 2
+ \ifnum\epsftmp>0
+ \ifnum\epsftsize<\epsfrsize\else
+ \advance\epsftsize-\epsfrsize \advance\epsfxsize\epsftmp \fi
+ \repeat
+ \epsfrsize=0pt
+ \fi
+ \else \ifnum\epsfysize=0
+ \epsftmp=\epsfrsize \divide\epsftmp\epsftsize
+ \epsfysize=\epsfxsize \multiply\epsfysize\epsftmp
+ \multiply\epsftmp\epsftsize \advance\epsfrsize-\epsftmp
+ \epsftmp=\epsfxsize
+ \loop \advance\epsfrsize\epsfrsize \divide\epsftmp 2
+ \ifnum\epsftmp>0
+ \ifnum\epsfrsize<\epsftsize\else
+ \advance\epsfrsize-\epsftsize \advance\epsfysize\epsftmp \fi
+ \repeat
+ \epsfrsize=0pt
+ \else
+ \epsfrsize=\epsfysize
+ \fi
+ \fi
+%
+% Finally, we make the vbox and stick in a \special that dvips can parse.
+%
+ \ifepsfverbose\message{#1: width=\the\epsfxsize, height=\the\epsfysize}\fi
+ \epsftmp=10\epsfxsize \divide\epsftmp\pspoints
+ \vbox to\epsfysize{\vfil\hbox to\epsfxsize{%
+ \ifnum\epsfrsize=0\relax
+ \special{PSfile=#1 llx=\epsfllx\space lly=\epsflly\space
+ urx=\epsfurx\space ury=\epsfury\space rwi=\number\epsftmp
+ \epsfclipstring}%
+ \else
+ \epsfrsize=10\epsfysize \divide\epsfrsize\pspoints
+ \special{PSfile=#1 llx=\epsfllx\space lly=\epsflly\space
+ urx=\epsfurx\space ury=\epsfury\space rwi=\number\epsftmp\space
+ rhi=\number\epsfrsize \epsfclipstring}%
+ \fi
+ \hfil}}%
+\global\epsfxsize=0pt\global\epsfysize=0pt}%
+%
+% We still need to define the tricky \epsfaux macro. This requires
+% a couple of magic constants for comparison purposes.
+%
+{\catcode`\%=12 \global\let\epsfpercent=%\global\def\epsfbblit{%BoundingBox}}%
+%
+% So we're ready to check for `%BoundingBox:' and to grab the
+% values if they are found.
+%
+\long\def\epsfaux#1#2:#3\\{\ifx#1\epsfpercent
+ \def\testit{#2}\ifx\testit\epsfbblit
+ \epsfgrab #3 . . . \\%
+ \epsffileokfalse
+ \global\epsfbbfoundtrue
+ \fi\else\ifx#1\par\else\epsffileokfalse\fi\fi}%
+%
+% Here we grab the values and stuff them in the appropriate definitions.
+%
+\def\epsfempty{}%
+\def\epsfgrab #1 #2 #3 #4 #5\\{%
+\global\def\epsfllx{#1}\ifx\epsfllx\epsfempty
+ \epsfgrab #2 #3 #4 #5 .\\\else
+ \global\def\epsflly{#2}%
+ \global\def\epsfurx{#3}\global\def\epsfury{#4}\fi}%
+%
+% We default the epsfsize macro.
+%
+\def\epsfsize#1#2{\epsfxsize}
+%
+% Finally, another definition for compatibility with older macros.
+%
+\let\epsffile=\epsfbox
diff --git a/systems/knuth/local/lib/fontchart.tex b/systems/knuth/local/lib/fontchart.tex
new file mode 100644
index 0000000000..6acc621155
--- /dev/null
+++ b/systems/knuth/local/lib/fontchart.tex
@@ -0,0 +1,89 @@
+% make a (fairly large) font chart
+
+\newlinechar=`@
+\message{@Name of the font to chart = }
+\read-1 to \fontname
+
+\font\f=\fontname at .75in
+\f
+
+% Here's the best way I know to discover character height and depth
+% when they are possibly negative.
+\fontdimen5\f=-10000pt % a new (rather small) xheight
+\textfont0=\f
+\newdimen\h \newdimen\d % will be set to the character's height, depth
+\def\htdp#1{\setbox0=\hbox{\char#1}\h=-\ht0
+ \setbox0=\hbox{\accent#1\char#1}\advance\h\ht0 \advance\h-10000pt
+ \setbox0=\null \wd0=-10000pt % in case the character has a charlist!
+ \setbox0=\hbox{$\mathaccent#1{\box0}$}%
+ \d=\ht0 \advance\d-10000pt \advance\d-\h}
+
+% (By the way, my previous best was this:
+% \textfont15=\f % use family "F
+% \fontdimen8\tenex=0pt % set defaultrulethickness zero
+% \setbox1=\null \ht1=-10000pt \setbox2=\null \dp3=-10000pt
+% \newdimen\d \newdimen\h \newcount\n
+% \n=#1 \advance\n"F00
+% \setbox0=\hbox{$\radical\n{\copy1}$}
+% \d=\dp0 \advance\d-5000pt \advance\d\d %\showthe\d
+% \setbox0=\hbox{$\radical\n{\copy2}$}
+% \h=\ht0 \advance\h-5000pt \advance\h-.5\d
+% \ifdim\h>0pt \h=.5\h \fi
+% ....very tricky! But it works only when height+depth is positive.)
+
+\tracinglostchars=0
+\nopagenumbers
+
+\def\testrow#1#2{\setbox0=\hbox{\penalty1\def\\{\char'#1#2}%
+ \\0\\1\\2\\3\\4\\5\\6\\7\global\chardef\p=\lastpenalty}} % p=1 if none there
+
+\raggedbottom
+\baselineskip=1in
+\topskip=.75in
+\newdimen\w \w=.75in
+\headline={\vtop{\hbox{\tt\fontname\unskip, page \folio\hfil}
+ \kern-.75in\unitsdigits}\hss}
+\footline={\unitsdigits\hss}
+\def\unitsdigits{\hbox{\kern.3in \tt
+ \hbox to\w{\ \ 0\hss}%
+ \hbox to\w{\ \ 1\hss}%
+ \hbox to\w{\ \ 2\hss}%
+ \hbox to\w{\ \ 3\hss}%
+ \hbox to\w{\ \ 4\hss}%
+ \hbox to\w{\ \ 5\hss}%
+ \hbox to\w{\ \ 6\hss}%
+ \hbox to\w{\ \ 7\hss}}}
+
+\def\row#1#2{\testrow#1#2%
+\ifnum\p=0\hbox{\hbox to.3in{\tt#1#2\hfil}%
+ \mod#1#20\mod#1#21\mod#1#22\mod#1#23\mod#1#24\mod#1#25\mod#1#26\mod#1#27%
+ \hbox to.3in{\tt\hfil#1#2}}\fi}
+
+\newdimen\pixel \pixel=.00333333in
+\def\mod#1#2#3{\chardef\c='#1#2#3 \htdp\c
+ \setbox0=\hbox{\penalty1\c\global\chardef\p=\lastpenalty}%
+ \ifnum\p=1 \hbox to\w{}\else
+ \setbox2=\hbox{\c\/}%
+ \hbox to\w{\kern-\pixel \vrule width\pixel height\h depth\d
+ \copy\leftbox\copy0\copy\rightbox
+ \vrule width\pixel height\h depth\d \kern-\pixel \kern-\wd0
+ \raise\h\vbox{\hrule height\pixel width\wd0}\kern-\wd0
+ \lower\d\vbox{\hrule height0pt depth\pixel width\wd0}\kern-\wd0
+ \ifdim\wd2>\wd0 \kern\wd2\raise\h\copy\icbox \fi
+ \hss}\fi}
+
+\newbox\leftbox % marking the baseline at the left
+\setbox\leftbox=\hbox{\kern-\pixel\vrule width\pixel height4pt
+ \vrule height0pt depth\pixel width4pt \kern-4pt}
+\newbox\rightbox % marking the baseline at the right
+\setbox\rightbox=\hbox{\kern-4pt\vrule height0pt depth\pixel width4pt
+ \vrule width\pixel height4pt \kern-\pixel}
+\newbox\icbox % the mark of an italic correction
+\setbox\icbox=\hbox{\kern-4pt\vrule height\pixel width4pt depth0pt
+ \vrule width\pixel depth4pt}
+
+\row00 \row01 \row02 \row03 \row04 \row05 \row06 \row07
+\row10 \row11 \row12 \row13 \row14 \row15 \row16 \row17
+\row20 \row21 \row22 \row23 \row24 \row25 \row26 \row27
+\row30 \row31 \row32 \row33 \row34 \row35 \row36 \row37
+\bye
diff --git a/systems/knuth/local/lib/gkpmac.tex b/systems/knuth/local/lib/gkpmac.tex
new file mode 100644
index 0000000000..1ae0f1a7a6
--- /dev/null
+++ b/systems/knuth/local/lib/gkpmac.tex
@@ -0,0 +1,890 @@
+\tracingpages=1 % TEMPORARY!
+% Macros for `Concrete Mathematics'
+\ifx\gkpmac\fmtversion\endinput\else\let\gkpmac=\fmtversion\fi
+
+% Fonts for regular text
+
+\font\textrm=ccr10 % roman
+\font\textit=ccti10 % italic (emphasis)
+\font\textsl=ccsl10 % slanted (titles)
+\font\textbf=cmbx10 % bold
+\font\textcsc=cccsc10 % caps and small caps
+\font\oldsty=ccmi10 % equation numbers
+\font\twelvett=cmtt12
+
+% Fonts for regular math
+% I'm setting \fontdimen2 to zero because AMS forgot to do it;
+% they may have fixed the problem in the meantime, but no harm is done
+
+\font\mathtext=ccr10
+ \font\mathsubtext=ccr7
+ \font\mathsubsubtext=ccr5
+\font\mathlet=eurm10 \fontdimen2\mathlet=0pt
+ \font\mathsublet=eurm7 \fontdimen2\mathsublet=0pt
+ \font\mathsubsublet=eurm5 \fontdimen2\mathsubsublet=0pt
+ \skewchar\mathlet='177 \skewchar\mathsublet='177 \skewchar\mathsubsublet='177
+\font\mathsym=cmsy10
+ \font\mathsubsym=cmsy7
+ \font\mathsubsubsym=cmsy5
+ \skewchar\mathsym='60 \skewchar\mathsubsym='60 \skewchar\mathsubsubsym='60
+\font\mathext=cmex10
+ \font\mathsubext=cmex10
+ \font\mathsubsubext=cmex10
+\font\mathscr=euxm10\fontdimen2\mathscr=0pt
+ \font\mathsubscr=euxm7 \fontdimen2\mathsubscr=0pt
+ \font\mathsubsubscr=euxm5 \fontdimen2\mathsubsubscr=0pt
+ \skewchar\mathscr='60 \skewchar\mathsubscr='60 \skewchar\mathsubsubscr='60
+\font\mathfr=eufm10\fontdimen2\mathfr=0pt
+ \font\mathsubfr=eufm7 \fontdimen2\mathsubfr=0pt
+ \font\mathsubsubfr=eufm5 \fontdimen2\mathsubsubfr=0pt
+\font\matheuex=euex10\fontdimen2\matheuex=0pt
+
+\font\eulerbf=eurb10 \fontdimen2\eulerbf=0pt % not hooked in to these macros
+
+% Fonts for graffiti
+
+\font\gtext=ccslc9
+\font\goldstyle=ccmic9
+ \fontdimen3\gtext=0pt \fontdimen4\gtext=0pt % no stretch or shrink
+\font\gmathtext=ccr9
+ \font\gmathsubtext=ccr6
+ \font\gmathsubsubtext=ccr5
+\font\gmathlet=eurm10 scaled 900 \fontdimen2\gmathlet=0pt
+ \font\gmathsublet=eurm7 scaled 857 \fontdimen2\gmathsublet=0pt
+ \font\gmathsubsublet=eurm5 \fontdimen2\gmathsubsublet=0pt
+ \skewchar\gmathlet='177 \skewchar\gmathsublet='177
+ \skewchar\gmathsubsublet='177
+\font\gmathsym=cmsy9
+ \font\gmathsubsym=cmsy6
+ \font\gmathsubsubsym=cmsy5
+ \skewchar\gmathsym='60 \skewchar\gmathsubsym='60 \skewchar\gmathsubsubsym='60
+\font\gmathext=cmex9
+ \font\gmathsubext=cmex9
+ \font\gmathsubsubext=cmex9
+\font\gmathscr=euxm10 scaled 900 \fontdimen2\gmathscr=0pt
+ \font\gmathsubscr=euxm7 scaled 857 \fontdimen2\gmathsubscr=0pt
+ \font\gmathsubsubscr=euxm5 \fontdimen2\gmathsubsubscr=0pt
+ \skewchar\gmathscr='60 \skewchar\gmathsubscr='60 \skewchar\gmathsubsubscr='60
+\font\gmathfr=eufm10 scaled 900 \fontdimen2\gmathfr=0pt
+ \font\gmathsubfr=eufm7 scaled 857 \fontdimen2\gmathsubfr=0pt
+ \font\gmathsubsubfr=eufm5 \fontdimen2\gmathsubsubfr=0pt
+\font\gmatheuex=euex9 \fontdimen2\gmatheuex=0pt
+
+% Fonts for headings, etc.
+
+\font\chapfont=cmbxcd10 scaled 8000
+\font\title=cmbx10 scaled \magstep5
+\font\subtitle=cmbx10 scaled 1315 % that's magstep 1.5
+\font\subsubtitle=cmbxsl10
+\font\runhead=cmbx9
+\font\foliofont=cmr9
+\font\gtfont=cmmi9 % for the \t accent
+\font\ninesl=ccsl9 % used in the index only
+
+% Normal text conventions
+
+\catcode`@=11 % borrow the private macros of PLAIN (with care)
+\def\wlog#1{} % don't put allocation info into the log
+
+\hsize=28pc
+\vsize=45.25pc
+\parindent=20pt
+\baselineskip=13pt
+
+\let\sc=\textcsc
+\let\bf=\textbf
+\def\textindent#1{\noindent\hbox to\parindent{\bf#1\hfil}\ignorespaces}
+\def\exitem{\hangindent2\parindent \textindent}
+
+\textfont0=\mathtext
+ \scriptfont0=\mathsubtext
+ \scriptscriptfont0=\mathsubsubtext
+\textfont1=\mathlet \let\tfont=\teni
+ \scriptfont1=\mathsublet
+ \scriptscriptfont1=\mathsubsublet
+\textfont2=\mathsym
+ \scriptfont2=\mathsubsym
+ \scriptscriptfont2=\mathsubsubsym
+\textfont3=\mathext
+ \scriptfont3=\mathsubext
+ \scriptscriptfont3=\mathsubsubext
+\def\rm{\fam\z@\textrm}
+\def\it{\fam\itfam\textit} % \it is family 4, defined in PLAIN
+\def\sl{\textsl}
+\textfont\itfam=\textit
+\newfam\scrfam \ifnum\scrfam=8\relax\else\error\fi % family 8, script
+\textfont\scrfam=\mathscr
+ \scriptfont\scrfam=\mathsubscr
+ \scriptscriptfont\scrfam=\mathsubsubscr
+\def\scr{\fam8 }
+\mathcode`0="7130
+\mathcode`1="7131
+\mathcode`2="7132
+\mathcode`3="7133
+\mathcode`4="7134
+\mathcode`5="7135
+\mathcode`6="7136
+\mathcode`7="7137
+\mathcode`8="7138
+\mathcode`9="7139
+\newfam\frfam % family 9, fraktur
+\textfont\frfam=\mathfr
+ \scriptfont\frfam=\mathsubfr
+ \scriptscriptfont\frfam=\mathsubsubfr
+\def\frak{\fam9 }
+\newfam\euexfam % family 10, substitions for euler symbols
+\newfam\eqfam % family 11, cmr equal signs for Relbar (!)
+\font\teneq=cmr10 \textfont\eqfam=\teneq
+\font\nineeq=cmr9
+% I'm not using Relbar except in 9&10pt, so this family is very incomplete...
+
+\mathchardef\intop="1A52
+\mathchardef\ointop="1A48
+\mathchardef\coprod="1A60
+\mathchardef\prod="1A51
+\mathchardef\sum="1A50
+\mathchardef\braceld="A7A \mathchardef\bracerd="A7B
+\mathchardef\bracelu="A7C \mathchardef\braceru="A7D
+\mathchardef\infty="0A31
+
+\mathchardef\nearrow="3A25
+\mathchardef\searrow="3A26
+\mathchardef\nwarrow="3A2D
+\mathchardef\swarrow="3A2E
+\mathchardef\Leftrightarrow="3A2C
+\mathchardef\Leftarrow="3A28
+\mathchardef\Rightarrow="3A29
+\mathchardef\leftrightarrow="3A24 \mathcode`\^^W="3A24
+\mathchardef\leftarrow="3A20 \let\gets=\leftarrow \mathcode`\^^X="3A20
+\mathchardef\rightarrow="3A21 \let\to=\rightarrow \mathcode`\^^Y="3A21
+\def\uparrow{\delimiter"3A22378 } \mathcode`\^^K="3A22
+\def\downarrow{\delimiter"3A23379 } \mathcode`\^^A="3A23
+\def\updownarrow{\delimiter"3A6C33F }
+\def\Uparrow{\delimiter"3A2A37E }
+\def\Downarrow{\delimiter"3A2B37F }
+\def\Updownarrow{\delimiter"3A6D377 }
+\mathchardef\leftharpoonup="3A18
+\mathchardef\leftharpoondown="3A19
+\mathchardef\rightharpoonup="3A1A
+\mathchardef\rightharpoondown="3A1B
+
+\textfont\euexfam=\matheuex
+\scriptfont\euexfam=\mathsubsym % this is OK since I don't use all characters
+% of euex in subscripts/superscripts; otherwise I'd have to make euex7 and euex6
+\scriptscriptfont\euexfam=\mathsubsym % only for \mathchoice, not really chosen
+\mathcode`+="292B
+\mathcode`-="292D
+\mathcode`!="0921
+\mathcode`(="4928 \delcode`(="928300
+\mathcode`)="5929 \delcode`)="929301
+\mathcode`[="495B \delcode`[="95B302
+\mathcode`]="595D \delcode`]="95D303
+\mathcode`=="393D
+\mathchardef\Relbar="3B3D % we need the old = to match \Arrows
+\mathchardef\Gamma="7100
+\mathchardef\Delta="7101
+\mathchardef\Theta="7102
+\mathchardef\Lambda="7103
+\mathchardef\Xi="7104
+\mathchardef\Pi="7105
+\mathchardef\Sigma="7106
+\mathchardef\Upsilon="7107
+\mathchardef\Phi="7108
+\mathchardef\Psi="7109
+\mathchardef\Omega="710A
+\let\varsigma=\sigma \let\varrho=\rho % Euler doesn't have these
+\mathchardef\aleph="0840
+\def\rbrace{\delimiter"5867A09 } \let\}=\rbrace
+\def\lbrace{\delimiter"4866A08 } \let\{=\lbrace
+%\mathchardef\equiv="3811 \let\cong=\equiv % lowres bars weren't spaced right
+\mathchardef\leq="3814 \let\le=\leq
+\mathchardef\geq="3815 \let\ge=\geq
+\mathchardef\Re="083C
+\mathchardef\Im="083D
+\def\vert{\delimiter"86A30C }
+\def\backslash{\delimiter"86E30F }
+
+\setbox\strutbox=\hbox{\vrule height9pt depth4pt width\z@}%
+\newbox\bigstrutbox \newbox\biggstrutbox
+\setbox\bigstrutbox=\hbox{\vrule height11pt depth4pt width\z@}%
+\def\bigstrut{\relax\ifmmode\copy\bigstrutbox\else\unhcopy\bigstrutbox\fi}
+\setbox\biggstrutbox=\hbox{\vrule height17pt depth10pt width\z@}%
+\def\biggstrut{\relax\ifmmode\copy\biggstrutbox\else\unhcopy\biggstrutbox\fi}
+\rm
+
+\newmuskip\normalthick \normalthick=5mu plus 5mu
+\newmuskip\normalmedmu \normalmedmu=\medmuskip
+\newmuskip\displaythick \displaythick=10mu minus 3mu
+\everymath{\thickmuskip=\normalthick}
+
+\abovedisplayskip=8pt plus 3pt minus 2pt % there's 2pt more (opened up)
+\belowdisplayskip=10pt plus 3pt minus 2pt
+
+% displays not centered; all have capability of \eqalign
+\outer\def\begindisplay{\obeylines\startdisplay}
+{\obeylines\gdef\startdisplay#1
+ {\catcode`\^^M=5$$%
+ \advance\displayindent\parindent\advance\displaywidth-\parindent%
+ \openup2pt #1\halign\bgroup\span\preamble\cr}}
+\outer\def\enddisplay{\crcr\egroup$$}
+\jot=\z@ % we do our own opening up
+
+\def\displaymath{$\thickmuskip=\displaythick\displaystyle}
+\def\preamble{\hfil\displaymath{##}$&&\displaymath{{}##}$\hfil}
+\def\tablepreamble{\bigstrut\hfil$##$\hfil\ &\vrule##&&\quad\hfil$##$\hfil}
+\def\xbox{\qquad\hbox} % for third column of explanation
+
+\newcount\eqcount
+\def\equ(#1.#2){{\rm({\oldsty#1}.{\oldsty#2})}}
+\def\eq(#1){\equ(\chapno.#1)}
+\def\thiseq{\xdef\next{(\chapno.\number\eqcount)}\expandafter\equ\next}
+\def\eqno{\global\advance\eqcount 1 \global\everycr{\makeeqno\thiseq}}
+\newdimen\backup
+\def\makeeqno#1{\noalign{\global\everycr{}%
+ \advance\displaywidth\displayindent
+ \setbox0=\hbox to\displaywidth{\hfil#1}%
+ \backup=\prevdepth \advance\backup\ht0
+ \setbox0=\vbox{\kern-\backup\box0}\ht0=\z@ \dp0=\z@
+ \setbox0=\vbox{\box0}\unvbox0}} % that puts equation number on prev line!
+
+\newif\iftitle
+\newskip\chaptopspace \chaptopspace=1in minus 5pt
+\def\beginchapter#1 #2 \par{ % we should be at top of a page
+ \titletrue \eqcount=0
+ \ifodd\pageno
+ \rightline{\chapfont#1\kern-.05em}
+ \vskip\chaptopspace
+ \rightline{\title#2}
+ \moveleft7pc % this applies to the \vbox after the \fi
+ \else % left-hand page
+ \leftline{\kern-.05em\chapfont#1}
+ \vskip\chaptopspace
+ \leftline{\title#2} \fi
+ \vbox{\hrule width 35pc}
+ \def\chapno{#1} \edef\chaptitle{#2}
+ \mark{#1\enspace #2}
+ \smallskip\noindent}
+
+\def\beginsection#1 #2 \par{ % should not be first in the chapter
+ \backup=\lastskip % but should come on first or second page of chapter
+ \mark{#1\enspace #2} % because the mark gives running head on right page
+ \nobreak\vskip-\backup\penalty-200
+ {\subtitle\baselineskip=34pt
+ \noindent\hbox to2\parindent{#1\hfil}\uppercase{\kern-.05em#2}\par}
+ \nobreak\vskip5pt\noindent\hbox to2\parindent{}}
+
+\newcount\excount
+\def\beginexercises{\backup=\lastskip \excount=0
+ \mark{\chapno\enspace Exercises}
+ \nobreak\vskip-\backup\penalty-200
+ {\subtitle\baselineskip=34pt\noindent Exercises\par}\nobreak}
+
+{\catcode`\|=\active
+\gdef\table#1\tabref|#2|{\hrule height1pt width\hsize \kern2pt
+ \noindent\strut{\bf Table |#2|\enspace}#1\par
+ \hrule height.5pt width\hsize\tabref|#2|}}
+
+\def\medbr{\ifnum\lastpenalty>9999 \medskip \else\medbreak\fi}
+\def\subhead#1\par{\medbr
+ {\subsubtitle\noindent#1\par}\nobreak}
+\def\ex:{\par{\advance\medskipamount-1pt\medbr}%
+ \advance\excount 1 \item{\number\excount}}
+
+\def\dash---{\thinspace---\hskip.16667em\relax}
+\def\qback{\kern-.15em} % between , or . and ''
+\def\undertext#1{$\underline{\smash{\hbox{#1}}}$}
+\def\newline{\hfil\break}
+\def\Hint:{{\it Hint:\/}}
+\let\macron=\= % we will use \= for congruence relation
+\def\t#1{{\edef\next{\the\font}\tfont\accent"7F\next#1}}
+
+% Here's a definition that was corrected in plain.tex version 3.14159;
+% I'm keeping the old version here, because I had already been compensating
+% for its deficiencies in script and scriptscript styles; without this
+% buggy version, it would be hard for me to match the old pages precisely
+\def\bmod{\mskip-\medmuskip\mkern5mu
+ \mathbin{\rm mod}\penalty900\mkern5mu\mskip-\medmuskip}
+
+% Graffiti macros
+\chardef\other=12
+\newread\grfi \openin\grfi=\jobname.grf
+%\newwrite\grfo \immediate\openout\grfo=\jobname.grf % let's hope no conflict
+%% Hmmm...; that doesn't work on Unix.
+%% Here's my first solution, a kludge where I alternated between .grf and .gr
+%\newif\ifgrf \newwrite\grfo \newwrite\grfempty
+%\ifeof\grfi \grffalse
+%\else\read\grfi to\grfitest \ifeof\grfi \grffalse \else \grftrue \fi\fi
+%\ifgrf\else \openin\grfi=\jobname.gr
+% \ifeof\grfi\else\read\grfi to\grfitest \fi\fi
+%\immediate\openout\grfo=\jobname.gr\ifgrf\else f\fi
+%\immediate\write\grfo{} % an empty line will start a nonempty file (Unix only)
+%% and then at the end I said
+% \immediate\openout\grfempty=\jobname.gr\ifgrf f\fi % clear the input file
+%% That solution worked with the following original macros
+%\def\testnextgrf{{\def\do##1{\catcode`##1=\other}\dospecials
+% \global\read\grfi to\next}\expandafter\testgrf\meaning\next\testgrf}
+%\expandafter\def\expandafter\testgrf\meaning\empty#1 #2\testgrf{%
+% \setup#2!!!!!$\ifx\thisone\thatone\if#1R\Rtrue\else\Rfalse\fi\else\Rguess\fi}
+%% Anyway, here's my second approach to Unix: (order n^2 algorithm)
+\def\\{{\def\do##1{\catcode`##1=\other}\dospecials \endlinechar=-1 \let\eol=0
+ \gdef\grfmem{}
+ \expandafter\def\expandafter\gbbl\meaning\empty{}
+ \def\appgrf{\read\grfi to\g
+ \ifx\g\empty\else\xdef\grfmem{\grfmem\expandafter\gbbl\meaning\g\eol}\fi}
+ \def\next{\ifeof\grfi\let\next\relax\else\appgrf\fi\next}\next}}
+\\ % now \grfmem is "L aaaaa\eol R bbbbb\eol ... R zzzzz\eol".
+\def\testnextgrf#1 #2\eol#3\\{\gdef\grfmem{#3}\setup#2!!!!!$%
+ \ifx\thisone\thatone\if#1R\Rtrue\else\Rfalse\fi\else\Rguess\fi}
+\newwrite\grfo \immediate\openout\grfo=\jobname.grf % no conflict now
+
+\def\graffiti{% set up graffiti style
+ \hsize=6pc
+ \baselineskip=10pt \lineskip=0pt \lineskiplimit=0pt
+ \parindent=0pt
+ \mathsurround=1pt
+ \textfont0=\gmathtext
+ \scriptfont0=\gmathsubtext
+ \scriptscriptfont0=\gmathsubsubtext
+ \textfont1=\gmathlet \let\tfont=\gtfont
+ \scriptfont1=\gmathsublet
+ \scriptscriptfont1=\gmathsubsublet
+ \textfont2=\gmathsym
+ \scriptfont2=\gmathsubsym
+ \scriptscriptfont2=\gmathsubsubsym
+ \textfont3=\gmathext
+ \scriptfont3=\gmathsubext
+ \scriptscriptfont3=\gmathsubsubext
+\textfont\scrfam=\gmathscr
+ \scriptfont\scrfam=\gmathsubscr
+ \scriptscriptfont\scrfam=\gmathsubsubscr
+\textfont\frfam=\gmathfr
+ \scriptfont\frfam=\gmathsubfr
+ \scriptscriptfont\frfam=\gmathsubsubfr
+\textfont\euexfam=\gmatheuex
+\scriptfont\euexfam=\gmathsubsym % OK since I don't use all chars in this size
+\textfont\eqfam=\nineeq
+ \def\rm{\fam\z@\gtext}%
+ \let\oldsty=\goldstyle
+ \let\big=\ninebig
+ \setbox\strutbox=\hbox{\vrule height7.25pt depth2.75pt width\z@}%
+ \gtext
+ \rightskip=\z@ plus2em % ragged right
+ \tolerance=2000
+ \hyphenpenalty=300
+ \exhyphenpenalty=300
+ \doublehyphendemerits=100000
+ \finalhyphendemerits=\doublehyphendemerits
+ }
+\def\ninebig#1{{\hbox{$\textfont0=\tenrm\textfont2=\tensy
+ \left#1\vbox to7.25pt{}\right.\n@space$}}}
+\def\grafctr{\hbox to4.5pc{\hfil##\hfil}}
+
+\newif\ifR % does this entry go on a right-hand page?
+\def\Rguess{\def\ifR{\ifodd\pageno}}
+\long\def\g#1\g{\def\next{#1!!!!!}\expandafter\writegrf\meaning\next$%
+ \ifx\grfmem\empty\Rguess\else\expandafter\testnextgrf\grfmem\\\fi
+% pre-Unix, that line was \ifeof\grfi\Rguess\else\testnextgrf\fi
+ \setbox0=\vtop{\graffiti#1%
+ \write\m@ne\ifR{\Rcheck}\else{\Lcheck}\fi}% log file records successes
+ \ifvmode\kern-\prevdepth\kern-\ht0\dp0=\z@\nointerlineskip\bgroup
+ \else\dp0=\dp\strutbox\strut\vadjust{\kern-\dp\strutbox\kern-\ht0\fi
+ \ifR\moveleft7\else\moveright29\fi pc\box0}}
+\expandafter\def\expandafter\writegrf\meaning\empty#1#2#3#4#5#6${%
+ \write\grfo{\LorR #1#2#3#4#5}\def\thisone{#1#2#3#4#5}}
+\def\setup#1#2#3#4#5#6${\def\thatone{#1#2#3#4#5}}
+\def\LorR{\ifodd\pageno R \else L \fi}
+\def\Lcheck{\ifodd\pageno Bad guess!\fi}
+\def\Rcheck{\ifodd\pageno\else Bad guess!\fi}
+
+% Page layout
+\newif\ifpreprint \preprinttrue % should be false when making the final copy
+\newdimen\pageheight \pageheight=\vsize
+\newdimen\totheight \totheight=49.5pc
+\newdimen\folioht \setbox0=\hbox{\foliofont0} \folioht=\ht0
+\def\leftheadline{\hbox to35pc{\vbox to 10pt{}% strut to position the baseline
+ \llap{\kern-2pc\iftitle\leftcorner\else\foliofont\folio\fi\hfil}%
+ \iftitle\hfil\copyrite\else\runhead\uppercase\expandafter{\chaptitle}\hfil\fi}}
+\def\rightheadline{\hbox to35pc{\iftitle\copyrite\fi\hfil
+ \vbox to 10pt{}% strut to position the baseline
+ \iftitle\else\runhead\uppercase\expandafter{\topmark}\fi
+ \rlap{\hfil\iftitle\rightcorner\else\foliofont\folio\fi\kern-2pc}}}
+\def\leftcorner{\iffinal\else\vrule\vbox to\folioht{\hrule width9pt\vfil}\fi}
+\def\rightcorner{\iffinal\else\vbox to\folioht{\hrule width9pt\vfil}\vrule\fi}
+\def\copyrite{\ifpreprint{\textfont2=\sevensy\sevenrm\copyright\ 1988
+ Addison-Wesley Publishing Company; all rights reserved}\fi}
+
+\newdimen\htrimsize \htrimsize=7.5in
+\newdimen\vtrimsize \vtrimsize=9.1875in
+\newdimen\outermargin \outermargin=23mm
+\newdimen\topmargin \topmargin=10mm % plus height of the headline box
+\newbox\htrim \newbox\vtrim \newbox\trimmarks
+\setbox\htrim=\hbox to\htrimsize{\kern-.65in
+ \vrule height .2pt depth .2pt width .4in\hfil\vrule width.4in\kern-.65in}
+ \wd\htrim=0pt
+\setbox\vtrim=\vbox to\vtrimsize{\kern-.65in
+ \moveleft.2pt\hbox{\vrule height .4in}\vfil
+ \moveleft.2pt\hbox{\vrule height .4in}\kern-.65in}
+ \wd\vtrim=0pt
+\setbox\trimmarks=\hbox to0pt{\raise\vtrimsize\copy\htrim \copy\htrim
+ \copy\vtrim \kern\htrimsize \copy\vtrim\hss}
+ \ht\trimmarks=0pt \dp\trimmarks=0pt
+
+\newif\iffinal % are we making the final copy? (pages.tex says "999")
+\def\onepageout#1{\escapechar=-1 % for writing \tabrefs
+ \shipout % here we define one page of output
+ \iffinal % add the trim marks
+ \hbox to\htrimsize{\copy\trimmarks
+ \ifodd\pageno\hss\else \hskip\outermargin\fi
+ \vbox to\vtrimsize{\kern\topmargin\fi
+ \vbox to\totheight{
+ \offinterlineskip % butt the boxes together
+ \vbox to 2pc{ % this part goes on top of the regular pages
+ \ifodd\pageno \rightheadline\else\leftheadline\fi
+ \vfill} % this completes the \vbox to 2pc
+ \ifodd\pageno\moveright7pc\fi #1
+ \vfill
+ \iftitle \global\titlefalse % reset the titlepage switch
+ \ifodd\pageno \hbox to35pc{\hfil\foliofont\folio}
+ \else\hbox{\foliofont\folio}\fi\fi
+ \ifpreprint\ifinxmode\makeinxfooter\fi\fi
+ \iffinal % finish the trimmed page
+ }\vfill}\ifodd\pageno\hskip\outermargin\else\hss\fi
+ \rlap{\smash{\lower30pt\hbox to.35in{\hss\twelvett\number\pageno}}}\fi
+ }
+ \advancepageno}
+
+\output{\onepageout{\pagebody}}
+
+\newbox\inxfootbanner
+\def\hours{\count0=\time \divide\count0 by60 % find the o'clock
+ \multiply\count0 by40 \advance\count0\time % convert to hhmm
+ \advance\count0 10000 \expandafter\gobbleone\number\count0\relax}
+\def\gobbleone1{}
+\setbox\inxfootbanner=\rlap{\hbox to 6.5in{\hrulefill\sevenrm\quad
+ Author's page proof produced by \TeX\ at
+ \hours\space on \ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day}}
+\def\makeinxfooter{\vbox to0pt{\kern10pt\copy\inxfootbanner\kern4pt
+ \rlap{\vbadness=\maxdimen \inxcolumns}\vss}}
+\def\inxcolumns{\ifvoid\inxbox\let\next\relax\else\let\next\contribcol\fi\next}
+\def\contribcol{\setbox0=\vsplit\inxbox to54pt
+ \vtop{\unvbox0}\kern20pt \inxcolumns}
+\def\inxstyle{\vrule height6pt depth2pt width\z@ \sevenrm}
+\splittopskip=6pt
+
+% Cross references
+
+% \ref{value}|name| gives value to |name|
+% \eqref|name| gives \eqcount to |name|
+% \exref|name| gives \excount to |name|
+% \tabref|name| gives appropriate page number to |name|
+% \refin foo inputs references from job foo (other than this job)
+% \showmissestrue if you want to see missing references
+
+\newif\ifshowmisses
+\def\vertical{|}
+\def\inref#1 #{\expandafter\def\csname\vertical#1\endcsname}
+
+\catcode`\|=\active
+\expandafter\def\expandafter\dospecials\expandafter{\dospecials\do\|}
+\newcount\defcount % number of old definitions not yet repeated
+\newcount\changecount % number of new definitions that are changed
+\newcount\miscount % number of unknown references
+
+\newread\tempin
+\def\refin#1 {\openin\tempin=#1.ref
+ \ifeof\tempin\closein\tempin
+ \else\closein\tempin \let|\inref \input#1.ref \let|\crossref \fi}
+
+{\let\|=\jobname
+ \def\def{\global\advance\defcount1 \gdef}\expandafter\refin\| }
+\newwrite\refo \immediate\openout\refo=\jobname.ref
+
+\def\ref#1|#2|{\xdef\temp{#1}\expandafter\dordef\csname\vertical#2\endcsname}
+\def\dordef#1{\ifx#1\temp \global\advance\defcount-1
+ \else\global\advance\changecount1 \global\let#1\temp\fi
+ {\escapechar=-1\immediate\write\refo{\noexpand#1{\temp}}}}
+\def\eqref{\ref{\number\eqcount}}
+\def\exref{\ref{\number\excount}}
+\def\tabref|#1|{\expandafter\pageref\csname\vertical#1\endcsname}
+\def\pageref#1{\ifx#1\relax\else\global\advance\defcount-1 \fi
+ \write\refo{\noexpand#1{\number\pageno}}%
+ \write\m@ne{\ifx#1\relax New Pageref!\else
+ \ifnum#1=\pageno\else Changed Pageref!\fi\fi}}
+\def\crossref#1|{\expandafter\usedef\csname\vertical#1\endcsname}
+\def\usedef#1{\ifx#1\relax
+ \ifshowmisses\showmiss#1\fi\global\advance\miscount1 ??\else #1\fi}
+\let|=\crossref
+\def\showmiss#1{{\escapechar=-1%
+ \message{***** WARNING: Undefined reference #1\string|! *****}}}
+
+\def\cite#1.{\ifinxmode\write\bnx{[#1] \number\pageno.}\fi#1}
+{\catcode`\@=\active
+\gdef\newcite#1.{\ifinxmode\write\bnx{[#1'] \number\pageno.}\fi#1$'@$}}
+
+\outer\def\bye{
+ \ifnum\miscount>0
+ \message{(\the\miscount\space undefined references were present)}\fi
+ \ifnum\changecount>0
+ \message{(\the\changecount\space new references written on \jobname.ref)}\fi
+ \ifnum\defcount>0
+ \message{(\the\defcount\space old references dropped from \jobname.ref)}\fi
+ \par\vfill\supereject
+ \end}
+
+% Exercises
+
+\newwrite\ans
+\immediate\openout\ans=\jobname.ans
+
+\outer\def\answer{\par
+ \immediate\write\ans{}
+ \immediate\write\ans{\string\ansno\chapno.\the\excount:}
+ \copytoblankline}
+\def\copytoblankline{\begingroup\setupcopy\copyans}
+\def\setupcopy{\def\do##1{\catcode`##1=\other}\dospecials \obeylines}
+{\obeylines \gdef\copyans#1
+ {\def\next{#1}%
+ \ifx\next\empty\let\next=\endgroup %
+ \else\immediate\write\ans{\next} \let\next=\copyans\fi\next}}
+
+\def\ansno#1:{\par\medbreak\def\thisansno{\source#1}%
+ \noindent\hbox to\parindent{\bf #1\hfil}\ignorespaces}
+
+% Pictures (a subset of \LaTeX's conventions)
+\newskip\hsssglue \hsssglue=0pt plus 1fill minus 1fill \def\hsss{\hskip\hsssglue}
+
+\newdimen\unitlength \newdimen\linethickness
+\newdimen\@picheight \newdimen\@xdim \newdimen\@ydim \newdimen\@len \newdimen\@save
+\newcount\@multicount \newcount\@xarg \newcount\@yarg
+\newbox\@picbox \newbox\@mpbox
+
+\font\tenln=line10 \font\tenlnw=linew10
+\font\tencirc=lcircle10 \font\tencircw=lcirclew10
+\font\smallln=linew10 scaled 483 % that's magstep-4
+
+\def\thinlines{\let\linefont=\tenln \let\circlefont=\tencirc
+ \linethickness=\fontdimen8\linefont}
+\def\thicklines{\let\linefont=\tenlnw \let\circlefont=\tencircw
+ \linethickness=\fontdimen8\linefont}
+\thinlines
+
+\def\beginpicture(#1,#2)(#3,#4){\@picheight=#2\unitlength \let\line=\@line
+ \setbox\@picbox=\hbox to#1\unitlength\bgroup
+ \kern-#3\unitlength \lower#4\unitlength\hbox\bgroup\ignorespaces}
+\def\endpicture{\egroup\hss\egroup
+ \ht\@picbox=\@picheight \dp\@picbox=\z@
+ \leavevmode\box\@picbox}
+
+\def\put(#1,#2)#3{\raise#2\unitlength\rlap{\kern#1\unitlength #3}\ignorespaces}
+
+\def\multiput(#1,#2)(#3,#4)#5#6{\@multicount=#5
+ \@xdim=#1\unitlength \@ydim=#2\unitlength \setbox\@mpbox=\hbox{#6}%
+ \loop\ifnum\@multicount>0
+ \raise\@ydim\rlap{\kern\@xdim \unhcopy\@mpbox}%
+ \advance\@xdim#3\unitlength \advance\@ydim#4\unitlength
+ \advance\@multicount\m@ne \repeat\ignorespaces}
+
+\def\makebox(#1,#2)#3{\setbox\@picbox=\hbox to#1\unitlength{\hss#3\hss}%
+ \@ydim=\ht\@picbox \advance\@ydim-\dp\@picbox
+ \ht\@picbox=#2\unitlength \dp\@picbox=\z@
+ \leavevmode\lower.5\@ydim\box\@picbox}
+
+\newif\ifneg
+\def\@line(#1,#2)#3{\@xarg=#1 \@yarg=#2 \@len=#3\unitlength \leavevmode
+ \ifnum\@xarg<0 \reverseline \else \negfalse \@ydim=\z@\fi
+ \ifnum\@xarg=0 \@vline
+ \else\ifnum\@yarg=0 \@hline \else\@sline\fi\fi
+ \ifneg\kern-\@len\else\@save=\@ydim\fi}
+\def\reverseline{\negtrue \kern-\@len \@xarg=-\@xarg
+ \@ydim=\@len \multiply\@ydim\@yarg \divide\@ydim\@xarg \@yarg=-\@yarg}
+
+\def\@hline{\vrule height.5\linethickness depth.5\linethickness width\@len}
+\def\@vline{\kern-.5\linethickness\vrule width\linethickness
+ \ifnum\@yarg<0 height\z@ depth\else depth\z@ height\fi\@len
+ \kern-.5\linethickness}
+
+\def\@sline{\setbox\@picbox=\hbox{\linefont \count@=\@xarg \multiply\count@ 8
+ \ifnum\@yarg>0 \advance\count@\@yarg \advance\count@-9
+ \else \advance\count@-\@yarg \advance\count@ 55 \fi \char\count@}%
+ \ifnum\@yarg<0 \@picheight=-\ht\@picbox \advance\@ydim\@picheight
+ \else \@picheight=\ht\@picbox \fi
+ \@xdim=\wd\@picbox \@save=\@ydim
+ \loop\ifdim\@xdim<\@len \raise\@ydim\copy\@picbox
+ \advance\@xdim\wd\@picbox \advance\@ydim\@picheight \repeat
+ \advance\@xdim-\@len \kern-\@xdim
+ \multiply\@xdim\@yarg \divide\@xdim\@xarg \advance\@ydim-\@xdim
+ \raise\@ydim\box\@picbox}
+
+\def\vector(#1,#2)#3{\@line(#1,#2){#3}%
+ \ifnum\@xarg=0 \@vvector \else\ifnum\@yarg=0 \@hvector \else\@svector\fi\fi}
+\def\@hvector{\ifneg\rlap{\linefont\char27}\else
+ \smash{\llap{\linefont\char45}}\fi} % we have to smash because of font bug
+\def\@vvector{\ifnum\@yarg<0 \raise-\@len\rlap{\linefont\char63}%
+ \else\setbox\@picbox=\rlap{\linefont\char54}\advance\@len-\ht\@picbox
+ \raise\@len\box\@picbox\fi}
+
+\def\@svector{\setbox\@picbox=\hbox to\z@{\linefont
+ \ifnum\@yarg<0 \count@=55 \@yarg=-\@yarg \else\count@=-9 \fi
+ \ifneg\multiply\@xarg16 \multiply\@yarg2
+ \else\hss % \llap
+ \ifnum\@xarg>2 \multiply\@xarg9 \multiply\@yarg2 \advance\count@29
+ \else\ifnum\@yarg>2 \multiply\@xarg16 \multiply\@yarg9 \advance\count@-20
+ \else\multiply\@xarg24 \multiply\@yarg3 \fi\fi\fi
+ \advance\count@\@xarg \advance\count@\@yarg \char\count@
+ \ifneg\hss\fi}% \rlap
+ \raise\@save\box\@picbox}
+
+\def\disk#1{\@len=#1\unitlength \count@='160 \@diskcirc}
+\def\circle#1{\@len=#1\unitlength \count@='140 \@diskcirc}
+\def\@diskcirc{\setbox\@picbox=\hbox{\circlefont\char\count@}\@xdim=\wd\@picbox
+ \leavevmode \ifdim\@len>15.499\@xdim \@bigdc \else \@smalldc\fi}
+\def\@bigdc{\ifnum\count@<'160 \@bigcirc
+ \else \@len=15\@xdim \@diskcirc\fi}
+\def\@smalldc{{\advance\@len-.5\@xdim
+ \loop\ifdim\@xdim<\@len \advance\count@\@ne \advance\@xdim\wd\@picbox\repeat
+ \hbox{\circlefont\char\count@}}}
+\def\@bigcirc{{\circlefont\count@=15
+ \setbox\@picbox=\hbox{\char\count@}\@xdim=\wd\@picbox
+ \ifdim\@len>2.5\@xdim \@len=2.5\@xdim\fi
+ \advance\@len-.125\wd\@picbox
+ \loop\ifdim\@xdim<\@len \advance\count@ 4 \advance\@xdim.25\wd\@picbox\repeat
+ \@ydim=.5\@xdim \advance\@ydim.5\linethickness
+ \setbox\@picbox=\vbox{\hbox{\char\count@\advance\count@-3\char\count@}%
+ \nointerlineskip
+ \hbox{\advance\count@\m@ne\char\count@\advance\count@\m@ne\char\count@}}%
+ \kern-\@ydim\lower\@ydim\box\@picbox}}
+
+\newif\ifovaltl \newif\ifovaltr \newif\ifovalbl \newif\ifovalbr
+\ovaltltrue \ovaltrtrue \ovalbltrue \ovalbrtrue
+\def\oval(#1,#2){\@xdim=#1\unitlength \@ydim=#2\unitlength
+ {\circlefont \setbox\@picbox=\hbox{\char0}
+ \ifdim\@xdim<\wd\@picbox \@xdim=\wd\@picbox\fi
+ \ifdim\@ydim<\wd\@picbox \@ydim=\wd\@picbox\fi
+ \@save=\@xdim \ifdim\@ydim<\@save \@save=\@ydim \fi
+ \count@=39
+ \loop \setbox\@picbox=\hbox{\char\count@}\ifdim\@save<\wd\@picbox
+ \advance\count@-4 \repeat
+ \setbox\strutbox=\hbox{\vrule height\ht\@picbox depth\dp\@picbox width\z@
+ \kern\wd\@picbox}%
+ \@save=.5\wd\@picbox \advance\@save-.5\linethickness
+ \setbox0=\hbox to\@xdim{\ifovaltl\char\count@\else\strut\fi
+ \kern-\@save\leaders\hrule height\ifovaltl\linethickness\else\z@\fi\hfil
+ \leaders\hrule height\ifovaltr\linethickness\else\z@\fi\hfil\kern\@save
+ \ifovaltr\advance\count@-3\char\count@\else\strut\fi\kern-\wd\@picbox}%
+ \advance\count@\m@ne
+ \setbox2=\hbox to\@xdim{\ifovalbl\char\count@\else\strut\fi
+ \kern-\@save\leaders\hrule height\ifovalbl\linethickness\else\z@\fi\hfil
+ \leaders\hrule height\ifovalbr\linethickness\else\z@\fi\hfil\kern\@save
+ \ifovalbr\advance\count@\m@ne\char\count@\else\strut\fi\kern-\wd\@picbox}%
+ \@save=\@ydim \advance\@save-\wd\@picbox \divide\@save 2
+ \setbox\@picbox=\vbox{\box0\nointerlineskip
+ \hbox to\@xdim{\vrule height\@save width\ifovaltl\linethickness\else\z@\fi
+ \hfil\ifovaltr\vrule width\linethickness\kern-\linethickness\fi}%
+ \nointerlineskip
+ \hbox to\@xdim{\vrule height\@save width\ifovalbl\linethickness\else\z@\fi
+ \hfil\ifovalbr\vrule width\linethickness\kern-\linethickness\fi}%
+ \nointerlineskip\box2}%
+ \@save=.5\@ydim \advance\@save.5\linethickness \leavevmode
+ \kern-.5\@xdim \kern-.5\linethickness \lower\@save\box\@picbox}}
+
+\def\cpic#1\endcpic{\vcenter{\hbox{\beginpicture#1\endpicture}}}
+
+% Squines (quadratic splines)
+% example of use: to plot f(x) between x0 and x1, you can say
+% \put(0,0){\squine(x0,xm,x1,y0,ym,y1)}, where y0=f(x0), y1=f(x1)
+% xm=(y0-y1+s1x1-s0x0)/(s1-s0), ym=(s0(s1x1-y1)-s1(s0x0-y0))/(s1-s0),
+% s0=f'(x0), and s1=f'(x1).
+
+\newdimen\@xi \newdimen\@xii \newdimen\@xiii \newdimen\@xiv
+\newdimen\@xpt \newdimen\@xoldpt
+\newdimen\@yi \newdimen\@yii \newdimen\@yiii \newdimen\@yiv
+\newdimen\@ypt \newdimen\@yoldpt
+\def\squine(#1,#2,#3,#4,#5,#6){\setbox\@picbox\hbox{\tencirc q}%
+ \global\@xoldpt=#1\unitlength \global\@yoldpt=#4\unitlength \kern\@xoldpt
+ \@xi=\@xoldpt \@xii=#2\unitlength \@xiii=#3\unitlength
+ \@yi=\@yoldpt \@yii=#5\unitlength \@yiii=#6\unitlength
+ \squinerec
+ \@xpt=#3\unitlength \@ypt=#6\unitlength \@addpoint
+ \raise\@ypt\copy\@picbox}
+\newif\iffar
+\def\squinerec{\farfalse \testnear\@xi\@xiii \testnear\@yi\@yiii
+ \iffar \decast \fi}
+\def\testnear#1#2{\@save=#1\advance\@save-#2%
+ \ifdim\@save<\z@ \@save=-\@save\fi \ifdim\@save>\p@ \fartrue \fi}
+\def\decast{\@xpt=\@xi \advance\@xpt\@xii \divide\@xpt2
+ \advance\@xii\@xiii \divide\@xii2
+ \@xiv=\@xpt \advance\@xiv\@xii \divide\@xiv2
+ \@ypt=\@yi \advance\@ypt\@yii \divide\@ypt2
+ \advance\@yii\@yiii \divide\@yii2
+ \@yiv=\@ypt \advance\@yiv\@yii \divide\@yiv2
+ \begingroup\@xii=\@xpt \@xiii=\@xiv
+ \@yii=\@ypt \@yiii=\@yiv \squinerec\endgroup
+ \@xpt=\@xiv \@ypt=\@yiv \@addpoint
+ \@xi=\@xiv \@yi=\@yiv \squinerec}
+\def\@addpoint{%\message{(\the\@xpt,\the\@ypt)}%
+ \global\advance\@xoldpt-\@xpt \wd\@picbox=-\@xoldpt
+ \raise\@yoldpt\copy\@picbox \global\@xoldpt=\@xpt \global\@yoldpt=\@ypt}
+
+% Math operators
+\def\2{\mskip-.5mu2\mskip.5mu}
+\newmuskip\lessfortimes \lessfortimes=-2mu minus -2mu
+\def\cdt{\mskip\lessfortimes\cdot\mskip\lessfortimes}
+\def\nullnum{\phantom{0}}
+\def\twonullnum{\phantom{00}}
+\def\bex{\mskip-2mu}
+\def\twoconditions#1#2{_{\scriptstyle#1\atop\scriptstyle#2}}
+\def\tworestrictions#1#2{\vcenter{\offinterlineskip
+ \halign{\strut\hfil##\hfil\cr#1\cr#2\cr}}}
+\def\dts{\mathinner{\ldotp\ldotp}}
+\def\[#1]{[\hbox{$\mskip1mu\thickmuskip=\thinmuskip#1\mskip1mu$}]}
+\def\bigi[#1\bigr]{\bigl[\hbox{$\thickmuskip=\thinmuskip#1$}\bigr]}
+\def\Bigi[#1\Bigr]{\Bigl[\hbox{$\thickmuskip=\thinmuskip#1$}\Bigr]}
+\def\prp(#1){(\hbox{$\thickmuskip=\thinmuskip#1$})}
+\def\pbigi(#1\bigr){\bigl(\hbox{$\thickmuskip=\thinmuskip#1$}\bigr)}
+\def\_#1{\def\next{#1}%
+ \ifx\next\risingsign\expandafter\rising\else^{\underline{#1}}\fi}
+\def\risingsign{^}
+\def\rising#1{^{\overline{#1}}}
+\def\dotminus{\mathbin{\buildrel{\hbox{\runhead.}}\over{\smash{-}\vphantom{_2}}}}
+\let\divides=\backslash
+\def\edivides{\divides\mskip-4mu\divides}
+\def\ndivides{\mathpalette\notdiv\relax}
+\def\notdiv#1#2{\setbox0=\hbox{$#1\divides$}%
+ \vcenter{\hbox to\wd0{$\hss\scriptscriptstyle/\hss$}}\kern-\wd0
+ \vcenter{\hbox to\wd0{$\hss\kern.5pt\scriptscriptstyle/\hss$}}\kern-\wd0
+ \box0\relax}
+\def\spec{\mathop{\rm Spec}}
+\def\half{{1\over2}}
+\def\rp{\mathchar"323F } % relatively prime
+\def\lcm{\mathop{\rm lcm}}
+\def\And{\quad{\rm and}\quad}
+\let\==\equiv
+\def\tmod#1{(mod~$#1$)}
+\let\implies=\Longrightarrow
+\def\?{\hbox{!`}} % subfactorial
+\def\hyp{\mathop{F{}}\nolimits\hyper}
+\def\tightplus{\medmuskip=1.5mu\relax}
+\def\hyper#1#2#3{\mathchoice{\tightplus
+ \hbox{$\displaystyle\biggl({#1\atop#2}\Big\vert\,{#3}\!\biggr)$}}%
+ {\bigl({#1\atop#2}\vert\mskip2mu#3\bigr)}%
+ {}{}} % used only in D and T styles
+%\def\hypk_#1{\mathop{F{}}_{#1}\nolimits\hyper} % confl with mFn convention
+\def\hypk_#1#2#3#4{\mathop{F{}}\mathchoice{\tightplus
+ \hbox{$\displaystyle\biggl({#2\atop#3}\Big\vert\,{#4}\!\biggr)$}%
+ \lower\fontdimen11\mathsym\hbox{$\scriptstyle\!#1$}}%
+ {\bigl({#2\atop#3}\vert\mskip2mu#4\bigr)\lower\fontdimen12\mathsym
+ \hbox{$\scriptstyle\!#1$}}%
+ {}{}} % used only in D and T styles
+\def\double(#1\choose#2){\mathchoice{\biggl(\!\!{#1\choose#2}\!\!\biggr)}
+ {\bigl(\!{#1\choose#2}\!\bigr)}{}{}} % only D and T styles
+\def\hypstrut{\vphantom{_1\_^k}} % if there's another denominator with \_^k
+\def\deg{\mathop{\rm deg}}
+\def\Bscr{{\scr B}}
+\def\Escr{{\scr E}}
+\def\Fscr{{\scr F}}
+\def\Pscr{{\scr P}}
+\def\Sscr{{\scr S}}
+\def\adj{\relbar\joinrel\relbar} % adjacent in a graph
+\let\<=\langle \let \>=\rangle
+\def\Pr{\mathop{\rm Pr}\nolimits}
+\def\Mean{\mathop{\rm Mean}\nolimits}
+\def\Var{\mathop{\rm Var}\nolimits}
+\def\between{\big\vert\hbox{\vphantom)}} % \between_a^b
+{\catcode`\'=\active \gdef'{^\bgroup\mskip2mu\prim@s}} % more space before '
+\def\array#1[#2]{\hbox{\tt#1[$#2$]}}
+\def\given{\mskip1mu\vert\mskip1mu}
+\def\euler{\atopwithdelims<>}
+\def\Euler#1#2{\mathchoice{\biggl<\mskip-7mu{#1\euler#2}\mskip-7mu\biggr>}%
+ {\bigl<\!{#1\euler#2}\!\bigr>}{}{}}
+
+\newbox\phihatbox \newbox\scrphihatbox
+\setbox\phihatbox=\hbox{$\phi$} \ht\phihatbox=1ex
+\setbox\scrphihatbox=\hbox{$\scriptstyle\phi$}
+ \ht\scrphihatbox=\fontdimen5\mathsublet
+\setbox\phihatbox=\hbox{$\widehat{\box\phihatbox}$}
+\setbox\scrphihatbox=\hbox{$\hat{\box\scrphihatbox}$}
+\def\phihat{\mathchoice{\copy\phihatbox}{\copy\phihatbox}%
+ {\copy\scrphihatbox}{{\hat\phi}}}
+
+\newbox\mathsizebox
+\def\setmathsize#1{\global\setbox\mathsizebox=\hbox{\displaymath#1$}}
+\def\mathsize#1{\hbox to\wd\mathsizebox{\displaymath#1$\hss}}
+
+\newbox\sqrtstrutbox
+\setbox\sqrtstrutbox=\hbox{\vrule height10.5pt width\z@}
+\def\strutsqrt#1{\copy\sqrtstrutbox\sqrt{{}^{\mathstrut}#1}}
+
+\newbox\Sqbox % for sum of squares
+\setbox\Sqbox=\vbox{\tenrm\hrule height.6pt\kern-.6pt
+ \hbox to1.5ex{\vrule height1.5ex width.6pt\hss\vrule width.6pt}\kern-.6pt
+ \hrule height.3pt depth.3pt}
+\def\Sq{\mskip1.5mu\copy\Sqbox\mskip1.5mu}
+
+% primitive index macros
+% "stuff for index" will go into a file for sorting and into normal text
+% "!stuff for index" will go into the file only
+\expandafter\def\expandafter\dospecials\expandafter{\dospecials\do\"}
+\def\hexcode{"} \catcode`\"=\active
+
+\newif\ifinxmode
+\newwrite\inx \newwrite\bnx
+\newbox\inxbox
+
+\newif\ifsilent
+\def\beginxref{\futurelet\next\beginxrefswitch}
+\def\beginxrefswitch{\ifx\next!\let\next=\silentxref
+ \else\silentfalse\let\next=\xref\fi \next}
+\def\silentxref!{\silenttrue\xref}
+\let"=\beginxref
+
+\def\xref#1"{\ifinxmode\edef\text{#1}\makexref\fi
+ \ifsilent\ignorespaces\else#1\fi}
+\def\makexref{\global\setbox\inxbox=%
+ \vbox{\unvbox\inxbox\allowbreak\hbox{\inxstyle\text}}%
+ \xdef\writeit{\write\inx{\text\space!\space
+ \noexpand\number\pageno.}}\writeit}
+
+% Final considerations
+\catcode`\@=\active \def@{\mskip1mu\relax}
+\expandafter\def\expandafter\dospecials\expandafter{\dospecials\do\@}
+
+\hyphenation{logical Mac-Mahon hyper-geo-metric hyper-geo-met-rics Ber-noulli}
+
+\preprintfalse % WE ARE MAKING THE REAL BOOK!
+\inxmodetrue % WE ARE PREPARING A ROUGH INDEX
+\showmissestrue % THE REFERENCES SHOULD ALL BE READY NOW
+
+\ifinxmode\immediate\openout\inx=\jobname.inx \fi % file for index reminders
+\ifinxmode\immediate\openout\bnx=\jobname.bnx \fi % file for bib reminders
+
+% To make the book:
+% First TeX BIB, to get BIB.REF correct. (Must have \cite entries.)
+% Then TeX CHAP1..CHAP9, PREF, ANS, CRED, FRONT, CONT.
+% Then make BNX file from individual *.BNX files including BIB.BNX.
+% Then reTeX BIB.
+% *.INX files are raw data only. Index and Contents are prepared by hand.
+
+% To produce only a subset of pages, put the page numbers on separate
+% lines in a file called pages.tex, ended by 999
+% WARNING: This will screw up the .grf file! Save it, then restore it.
+% WARNING: This may screw up the .ref file (if there are \tabrefs). Ditto.
+\let\Shipout=\shipout
+\newread\pages \newcount\nxtpg \openin\pages=pages
+\def\getnxtpg{\ifeof\pages\else
+ {\endlinechar=-1\read\pages to\next
+ \ifx\next\empty % in this case we should have eof now
+ \else\global\nxtpg=\next\fi}\fi}
+\ifeof\pages\finalfalse\else\finaltrue
+ \getnxtpg
+\ifnum\nxtpg=999
+ \message{OK, I'm making final copy with trim marks!}
+ \hoffset=-.5in
+ \getnxtpg % this should ensure eof on the \pages file
+\else\message{OK, I'll ship only the requested pages!}\fi\fi
+\def\shipout{\ifeof\pages\let\next=\Shipout
+ \else\ifnum\pageno=\nxtpg\getnxtpg\let\next=\Shipout
+ \else\let\next=\Tosspage\fi\fi \next}
+\newbox\garbage \def\Tosspage{\deadcycles=0\setbox\garbage=}
diff --git a/systems/knuth/local/lib/gray.mf b/systems/knuth/local/lib/gray.mf
new file mode 100644
index 0000000000..0392f30eb7
--- /dev/null
+++ b/systems/knuth/local/lib/gray.mf
@@ -0,0 +1,3 @@
+input graylj % `standard' gray font is for the laserjet
+
+
diff --git a/systems/knuth/local/lib/grayaps.mf b/systems/knuth/local/lib/grayaps.mf
new file mode 100644
index 0000000000..44f1c4eab6
--- /dev/null
+++ b/systems/knuth/local/lib/grayaps.mf
@@ -0,0 +1,9 @@
+% temporary kludge to make the APS font for proofmode with new MF
+magnification 1.0;
+tfmmode;
+fontfacebyte 84;
+ppi=722.909; % pixels per inch, according to DRF
+fontidentifier "FIG";
+codingscheme "GFGRAY";
+input gray;
+end
diff --git a/systems/knuth/local/lib/grayimagen.mf b/systems/knuth/local/lib/grayimagen.mf
new file mode 100644
index 0000000000..63381aa0af
--- /dev/null
+++ b/systems/knuth/local/lib/grayimagen.mf
@@ -0,0 +1,10 @@
+% Gray font for Imagen 8/300; proofsheet resolution is 75 pixels per inch.
+% Each pixel is represented by a 4x4 square, with 4/16 of the dots on.
+
+if mode<>imagen: errmessage "This file is for imagen only"; fi
+
+font_identifier "GRAYIMAGEN";
+
+boolean lightweight;
+
+input grayf
diff --git a/systems/knuth/local/lib/grayimagen3.mf b/systems/knuth/local/lib/grayimagen3.mf
new file mode 100644
index 0000000000..d60fab3f9a
--- /dev/null
+++ b/systems/knuth/local/lib/grayimagen3.mf
@@ -0,0 +1,7 @@
+% Gray font for Imagen with proofsheet resolution 25 pixels per inch
+
+if mode<>imagen: errmessage "This file is for imagen only"; fi
+
+font_identifier "GRAY3IMAGEN";
+rep=3; boolean large_pixels;
+input grayf
diff --git a/systems/knuth/local/lib/grayimagenlight.mf b/systems/knuth/local/lib/grayimagenlight.mf
new file mode 100644
index 0000000000..5625d2554b
--- /dev/null
+++ b/systems/knuth/local/lib/grayimagenlight.mf
@@ -0,0 +1,12 @@
+% Light gray font for Imagen 8/300; proofsheet resolution is 60 pixels per inch.
+% Each pixel is represented by a 5x5 square, with 5/25 of the dots on.
+
+if mode<>imagen: errmessage "This file is for imagen only"; fi
+
+font_identifier "GRAYIMAGEN";
+picture pix_picture; pix_picture := nullpicture;
+for z=(0,1),(1,4),(2,2),(3,0),(4,3):
+ addto pix_picture also unitpixel shifted z; endfor
+pix_wd := pix_ht := 5;
+
+input grayf
diff --git a/systems/knuth/local/lib/graylj.mf b/systems/knuth/local/lib/graylj.mf
new file mode 100644
index 0000000000..5c87efde42
--- /dev/null
+++ b/systems/knuth/local/lib/graylj.mf
@@ -0,0 +1,10 @@
+% Gray font for HP LaserJet; proofsheet resolution is 150 pixels per inch.
+% Each pixel is represented by a 4x4 square, with 4/16 of the dots on.
+
+if mode<>ljfour: errmessage "This file is for laserjet only"; fi
+
+font_identifier "GRAYLJ";
+
+boolean lightweight;
+
+input grayf
diff --git a/systems/knuth/local/lib/lcircle.mf b/systems/knuth/local/lib/lcircle.mf
new file mode 100644
index 0000000000..1d0fa48c07
--- /dev/null
+++ b/systems/knuth/local/lib/lcircle.mf
@@ -0,0 +1,550 @@
+fontdimen 1: 0,0,0,0,0,0,0,thickness#;
+font_coding_scheme:="LaTeX circle";
+
+let cmchar=\; % cmchar should precede each character, for testing purposes
+
+thickness:=ceiling(thickness#*hppp); % the way rules thicknesses are done
+% thickness#:=thickness/hppp; % and let thickness# round to right value
+% NO, I deleted this BAD line! --- DEK, 9 Jul 87
+pickup pencircle scaled thickness; nib:=savepen;
+
+% arcs sets up the points 1, 2, ..., 8 as follows:
+%
+% |1
+% |*
+% * *
+% 8| 2
+% * | *
+% 7* | *3
+% --*-----*--
+% 6| 4
+% * *
+% |5
+%
+% <----w---->
+
+pair upleft,upright,downleft,downright;
+upright=-downleft=(1,1); downright=-upleft=(1,-1);
+
+% a quarter-circle has width, height and depth set as explained on
+% page 389 of the TeXbook, not the real width, height, and depth
+
+def qbeginchar (expr c, diam_sharp) =
+numeric hh, dd; hh:=.5*(diam_sharp+thickness#);
+dd:=.5*(diam_sharp-thickness#);
+beginchar(c,diam_sharp,hh,dd);
+enddef;
+
+def arcs =
+numeric delta;
+delta:=.5 round((1-sqrt.5)*w); % distance from axis to arc's 45 degree points
+bot y3=0; y7=y3=.5[y5,y1]; y1-y5=w;
+lft x1=0; x5=x1=.5[x7,x3]; x3-x7=w;
+x2-x1=delta; x1-x8=delta; x8=x6; x2=x4;
+y8-y7=delta; y7-y6=delta; y8=y2; y6=y4;
+enddef;
+
+cmchar "2pt, top right";
+qbeginchar(0,4pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z7{right}..z6{downright}..z5{down};
+endchar;
+
+cmchar "2pt, bottom right";
+qbeginchar(1,4pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z1{down}..z8{downleft}..z7{left};
+endchar;
+
+cmchar "2pt, bottom left";
+qbeginchar(2,4pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z3{left}..z2{upleft}..z1{up};
+endchar;
+
+cmchar "2pt, top left";
+qbeginchar(3,4pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z5{up}..z4{upright}..z3{right};
+endchar;
+
+cmchar "4pt, top right";
+qbeginchar(4,8pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z7{right}..z6{downright}..z5{down};
+endchar;
+
+cmchar "4pt, bottom right";
+qbeginchar(5,8pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z1{down}..z8{downleft}..z7{left};
+endchar;
+
+cmchar "4pt, bottom left";
+qbeginchar(6,8pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z3{left}..z2{upleft}..z1{up};
+endchar;
+
+cmchar "4pt, top left";
+qbeginchar(7,8pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z5{up}..z4{upright}..z3{right};
+endchar;
+
+cmchar "6pt, top right";
+qbeginchar(8,12pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z7{right}..z6{downright}..z5{down};
+endchar;
+
+cmchar "6pt, bottom right";
+qbeginchar(9,12pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z1{down}..z8{downleft}..z7{left};
+endchar;
+
+cmchar "6pt, bottom left";
+qbeginchar(10,12pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z3{left}..z2{upleft}..z1{up};
+endchar;
+
+cmchar "6pt, top left";
+qbeginchar(11,12pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z5{up}..z4{upright}..z3{right};
+endchar;
+
+cmchar "8pt, top right";
+qbeginchar(12,16pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z7{right}..z6{downright}..z5{down};
+endchar;
+
+cmchar "8pt, bottom right";
+qbeginchar(13,16pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z1{down}..z8{downleft}..z7{left};
+endchar;
+
+cmchar "8pt, bottom left";
+qbeginchar(14,16pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z3{left}..z2{upleft}..z1{up};
+endchar;
+
+cmchar "8pt, top left";
+qbeginchar(15,16pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z5{up}..z4{upright}..z3{right};
+endchar;
+
+cmchar "10pt, top right";
+qbeginchar(16,20pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z7{right}..z6{downright}..z5{down};
+endchar;
+
+cmchar "10pt, bottom right";
+qbeginchar(17,20pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z1{down}..z8{downleft}..z7{left};
+endchar;
+
+cmchar "10pt, bottom left";
+qbeginchar(18,20pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z3{left}..z2{upleft}..z1{up};
+endchar;
+
+cmchar "10pt, top left";
+qbeginchar(19,20pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z5{up}..z4{upright}..z3{right};
+endchar;
+
+cmchar "12pt, top right";
+qbeginchar(20,24pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z7{right}..z6{downright}..z5{down};
+endchar;
+
+cmchar "12pt, bottom right";
+qbeginchar(21,24pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z1{down}..z8{downleft}..z7{left};
+endchar;
+
+cmchar "12pt, bottom left";
+qbeginchar(22,24pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z3{left}..z2{upleft}..z1{up};
+endchar;
+
+cmchar "12pt, top left";
+qbeginchar(23,24pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z5{up}..z4{upright}..z3{right};
+endchar;
+
+cmchar "14pt, top right";
+qbeginchar(24,28pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z7{right}..z6{downright}..z5{down};
+endchar;
+
+cmchar "14pt, bottom right";
+qbeginchar(25,28pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z1{down}..z8{downleft}..z7{left};
+endchar;
+
+cmchar "14pt, bottom left";
+qbeginchar(26,28pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z3{left}..z2{upleft}..z1{up};
+endchar;
+
+cmchar "14pt, top left";
+qbeginchar(27,28pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z5{up}..z4{upright}..z3{right};
+endchar;
+
+cmchar "16pt, top right";
+qbeginchar(28,32pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z7{right}..z6{downright}..z5{down};
+endchar;
+
+cmchar "16pt, bottom right";
+qbeginchar(29,32pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z1{down}..z8{downleft}..z7{left};
+endchar;
+
+cmchar "16pt, bottom left";
+qbeginchar(30,32pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z3{left}..z2{upleft}..z1{up};
+endchar;
+
+cmchar "16pt, top left";
+qbeginchar(31,32pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z5{up}..z4{upright}..z3{right};
+endchar;
+
+cmchar "18pt, top right";
+qbeginchar(32,36pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z7{right}..z6{downright}..z5{down};
+endchar;
+
+cmchar "18pt, bottom right";
+qbeginchar(33,36pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z1{down}..z8{downleft}..z7{left};
+endchar;
+
+cmchar "18pt, bottom left";
+qbeginchar(34,36pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z3{left}..z2{upleft}..z1{up};
+endchar;
+
+cmchar "18pt, top left";
+qbeginchar(35,36pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z5{up}..z4{upright}..z3{right};
+endchar;
+
+cmchar "20pt, top right";
+qbeginchar(36,40pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z7{right}..z6{downright}..z5{down};
+endchar;
+
+cmchar "20pt, bottom right";
+qbeginchar(37,40pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z1{down}..z8{downleft}..z7{left};
+endchar;
+
+cmchar "20pt, bottom left";
+qbeginchar(38,40pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z3{left}..z2{upleft}..z1{up};
+endchar;
+
+cmchar "20pt, top left";
+qbeginchar(39,40pt#);
+numeric delta;
+pickup nib;
+arcs;
+draw z5{up}..z4{upright}..z3{right};
+endchar;
+
+% The full circles have height and depth 0pt because otherwise there
+% are too many heights and depths for the TFM files
+
+cmchar "1pt, circle";
+beginchar(96,1pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "2pt, circle";
+beginchar(97,2pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "3pt, circle";
+beginchar(98,3pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "4pt, circle";
+beginchar(99,4pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "5pt, circle";
+beginchar(100,5pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "6pt, circle";
+beginchar(101,6pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "7pt, circle";
+beginchar(102,7pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "8pt, circle";
+beginchar(103,8pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "9pt, circle";
+beginchar(104,9pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "10pt, circle";
+beginchar(105,10pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "11pt, circle";
+beginchar(106,11pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "12pt, circle";
+beginchar(107,12pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "13pt, circle";
+beginchar(108,13pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "14pt, circle";
+beginchar(109,14pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "15pt, circle";
+beginchar(110,15pt#,0pt#,0pt#);
+pickup nib;
+draw fullcircle scaled w;
+endchar;
+
+cmchar "1pt, blob";
+beginchar(112,1pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "2pt, blob";
+beginchar(113,2pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "3pt, blob";
+beginchar(114,3pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "4pt, blob";
+beginchar(115,4pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "5pt, blob";
+beginchar(116,5pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "6pt, blob";
+beginchar(117,6pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "7pt, blob";
+beginchar(118,7pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "8pt, blob";
+beginchar(119,8pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "9pt, blob";
+beginchar(120,9pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "10pt, blob";
+beginchar(121,10pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "11pt, blob";
+beginchar(122,11pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "12pt, blob";
+beginchar(123,12pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "13pt, blob";
+beginchar(124,13pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "14pt, blob";
+beginchar(125,14pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+cmchar "15pt, blob";
+beginchar(126,15pt#,0pt#,0pt#);
+pickup pencircle scaled w;
+drawdot origin;
+endchar;
+
+bye
diff --git a/systems/knuth/local/lib/lcircle10.mf b/systems/knuth/local/lib/lcircle10.mf
new file mode 100644
index 0000000000..c2b84de489
--- /dev/null
+++ b/systems/knuth/local/lib/lcircle10.mf
@@ -0,0 +1,7 @@
+font_identifier "CIRCLE"; font_size 10pt#;
+font_coding_scheme:="LATEX CIRCLE";
+mode_setup;
+
+thickness#:=.4pt#;
+
+input lcircle
diff --git a/systems/knuth/local/lib/lcirclew10.mf b/systems/knuth/local/lib/lcirclew10.mf
new file mode 100644
index 0000000000..c57edbefbe
--- /dev/null
+++ b/systems/knuth/local/lib/lcirclew10.mf
@@ -0,0 +1,6 @@
+font_identifier "CIRCLEW"; font_size 10pt#;
+font_coding_scheme:="LATEX CIRCLE";
+mode_setup;
+
+thickness#:=.8pt#;
+input lcircle
diff --git a/systems/knuth/local/lib/letter.tex b/systems/knuth/local/lib/letter.tex
new file mode 100644
index 0000000000..efa2bf02fd
--- /dev/null
+++ b/systems/knuth/local/lib/letter.tex
@@ -0,0 +1,119 @@
+% This macro file is for business letters
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\def\narrower{\advance\leftskip by 20pt \advance\rightskip by 20pt }
+
+\raggedbottom
+\interlinepenalty=1000
+\hsize=6.25truein \voffset=24pt \advance\vsize by-\voffset
+\parindent=0pt \parskip=0pt
+\nopagenumbers
+\headline={\ifnum\pageno>1
+ \tenrm To \addressee\hfil\today\hfil Page \folio
+ \else\hfil\fi}
+
+\def\beginlinemode{\endmode
+ \begingroup\obeylines\def\endmode{\par\endgroup}}
+\def\beginparmode{\endmode
+ \begingroup\parskip=\medskipamount \def\endmode{\par\endgroup}}
+\let\endmode=\par
+\def\endletter{\endmode\vfill\supereject \pageno=1 }
+
+\newdimen\longindentation \longindentation=4truein
+\newbox\theaddress
+\def\address{\beginlinemode\getaddress}
+{\obeylines\gdef\getaddress#1
+ #2
+ {\gdef\addressee{#2\unskip}%
+ \global\setbox\theaddress=\vbox\bgroup\raggedright%
+ \hsize=\longindentation \everypar{\hangindent2em}#2
+ \def\endmode{\egroup\endgroup \copy\theaddress \bigskip}}}
+
+\def\body{\beginparmode}
+\def\closing{\beginlinemode\getclosing}
+{\obeylines\gdef\getclosing #1
+ #2
+ {\nobreak\bigskip \leftskip=\longindentation #2
+ \nobreak\bigskip\bigskip\bigskip % space for signature
+ \def
+ {\endgraf\nobreak}}}
+\def\annotations{\beginlinemode\def\par{\endgraf\nobreak}\obeylines\par}
+\def\ps{\beginparmode\nobreak
+ \interlinepenalty5000\def\par{\endgraf\penalty5000}}
+\def\cc{\beginparmode\nobreak
+ \interlinepenalty5000\def\par{\endgraf\penalty5000}}
+
+\def\up#1{\leavevmode \raise.16ex\hbox{#1}}
+\font\smallheadfont=cmr8 at 8truept
+\font\largeheadfont=cmbx10 at 14.4truept
+\font\logofont=stan70 at 70truept
+\def\stanfordseal{\vbox{\kern-3truept\hbox{\logofont A}\kern-3truept}}
+
+\def\csdreturn{Computer Science Department\par \stanfordreturn}
+\def\stanfordreturn{Stanford University\par
+ Stanford, CA 94305\kern.5pt-9045 USA\par}
+\def\letterhead{\pageno=1 \univletterhead
+ {\leftskip=\longindentation
+ {\baselineskip9truept\smallheadfont\sendingaddress}
+ \bigskip\bigskip\rm\today\bigskip}}
+\def\specrule#1{\nointerlineskip
+ \line{\hbox to 60truept{\hrulefill
+ \hphantom{\stanfordseal\kern-#1truept}%
+ \hrulefill}\hrulefill}\nointerlineskip}
+\def\univletterhead{\vglue-34pt
+ \specrule{11}\vskip8truept
+ \hbox{\hbox to\longindentation{\smash{\hbox to60truept{\hfil\kern2.5truept
+ \lower10truept\stanfordseal\hfil}}%
+ \largeheadfont STANFORD UNIVERSITY\hfil}%
+ \smallheadfont STANFORD, CALIFORNIA 94305\kern.5pt-9045}
+ \vskip6truept\specrule{17}\vskip13truept }
+
+\def\deklet{
+ \def\sendingaddress{DONALD E. KNUTH\par
+ Professor Emeritus of The Art of\par
+ \qquad Computer Programming\par
+ Department of Computer Science\par
+ Telephone \up[650\up]\thinspace 723-4367\par}
+ \def\returnaddress{Prof.~D. E. Knuth\par \csdreturn}
+ \letterhead}
+
+\def\memoto{\beginlinemode\parindent=60truept
+ \everypar{\hangindent70truept}\startmemo}
+\def\bigmemoto{\beginlinemode\parindent=60pt % use this for magstep>1!
+ \everypar{\hangindent70pt}\startmemo}
+\def\textin#1{\noindent\hbox to\parindent{#1\hss}\ignorespaces}
+{\obeylines\gdef\startmemo #1
+ {\gdef\addressee{#1}%
+ \global\pageno=1 \vglue-34pt\specrule{11}\vskip8truept
+ \leftline{\smash{\hbox to60truept{\hfil\kern2.5truept
+ \lower10truept\stanfordseal\hfil}}%
+ \largeheadfont Stanford Computer Science Department %
+ \rm\today}%
+ \vskip6truept\specrule{20}\bigskip\bigskip\bigskip%
+ \textin{Memo to:}#1
+ \def\endmode{\par\endgroup\bigskip}}
+ \gdef\from #1
+ {\smallskip\textin{From:}#1
+ }
+ \gdef\subject #1
+ {\smallskip\textin{Subject:}#1
+ }
+}
+
+\def\makelabel{\endletter\hbox{\vrule
+ \vbox{\hrule \kern6truept
+ \hbox{\kern6truept\stanfordseal\kern3pt
+ \vbox{\smallheadfont\baselineskip9truept\hsize=2truein\returnaddress}
+ \kern6truept}\kern6truept\hrule}\vrule}
+ \kern.1truein
+ \hbox{\vrule
+ \vbox{\hrule \kern12truept
+ \hbox{\kern12truept\copy\theaddress
+ \kern12truept}\kern12truept\hrule}\vrule}
+ \pageno=0\vfill\eject}
+
+\def\frac#1/#2{\leavemode\kern.1em\raise.5ex\hbox{\the\scriptfont0
+ #1}\kern-.1em/\kern-.15em\lower.25ex\hbox{\the\scriptfont0 #2}}
diff --git a/systems/knuth/local/lib/line.mf b/systems/knuth/local/lib/line.mf
new file mode 100644
index 0000000000..97cc3105db
--- /dev/null
+++ b/systems/knuth/local/lib/line.mf
@@ -0,0 +1,552 @@
+qqs:=ceiling(qqs#*hppp); % the way rule widths are calculated
+define_pixels(qqh,qqw,arrowlen,arrowht,arrowcurve);
+
+def line (expr delx, dely) =
+numeric qht,qwd,absdely;
+
+if dely > 0:
+ if delx > dely: qwd# := qqw#; qht# := dely*qqw#/delx;
+ else: qht# := qqw#; qwd# := delx*qqw#/dely; fi;
+ beginchar( 8(delx-1)+(dely-1),qwd#,qht#,0pt#);
+ x0=0; y0=0;
+ x1=w; y1=h;
+ else:
+ absdely=-dely;
+ if delx > absdely: qwd# := qqw#; qht# := absdely*qqw#/delx;
+ else: qht# := qqw#; qwd# := delx*qqw#/absdely; fi;
+ beginchar(64+8(delx-1)+(absdely-1),qwd#,qht#,0pt#);
+ x0=0; y0=h;
+ x1=w; y1=0;
+fi;
+pickup pencircle scaled qqs;
+draw z0..z1
+enddef;
+
+def larrow (expr char, delx, dely) =
+numeric rxa,rxb,rxc,rxd,rxe,rya,ryb,ryc,ryd,rye;
+numeric qht,qwd,absdely;
+if dely > 0:
+ if delx > dely: qwd# := qqw#; qht# := dely*qqw#/delx;
+ else: qht# := qqw#; qwd# := delx*qqw#/dely; fi;
+ % fixed by DEK on 24 Jan 89; previous code buggy but caused no harm
+ else:
+ absdely=-dely;
+ if delx > absdely: qwd# := qqw#; qht# := absdely*qqw#/delx;
+ else: qht# := qqw#; qwd# := delx*qqw#/absdely; fi;
+fi;
+
+beginchar(char,qwd#,qht#,0pt#);
+if dely > 0: y0=0; else: y0=h; fi;
+
+x0=0;
+rxa=arrowlen; rya=0;
+rxb=arrowlen; ryb=arrowht;
+rxc=arrowlen; ryc=-arrowht;
+rxd=.5rxa; ryd=.5ryb-arrowcurve;
+rxe=rxd; rye=-ryd;
+
+numeric rlen;
+rlen = sqrt(delx*delx + dely*dely);
+
+x1=(rxa*delx - rya*dely)/rlen;
+y1=(rxa*dely+rya*delx)/rlen + y0;
+
+%x2=rxb; y2=ryb;
+x2=(rxb*delx - ryb*dely)/rlen;
+y2=(rxb*dely+ryb*delx)/rlen + y0;
+
+%x3=rxc; y3=ryc;
+x3=(rxc*delx - ryc*dely)/rlen;
+y3=(rxc*dely+ryc*delx)/rlen + y0;
+
+%x4=rxd; y4=ryd;
+x4=(rxd*delx - ryd*dely)/rlen;
+y4=(rxd*dely+ryd*delx)/rlen + y0;
+
+%x5=rxe; y5=rye;
+x5=(rxe*delx - rye*dely)/rlen;
+y5=(rxe*dely+rye*delx)/rlen + y0;
+
+fill z0..z5..z3--z1--z2..z4..z0--cycle
+enddef;
+
+def rarrow (expr char, delx, dely) =
+
+numeric rxa,rxb,rxc,rxd,rxe,rya,ryb,ryc,ryd,rye;
+numeric qht,qwd,absdely;
+if dely > 0:
+ if delx > dely: qwd# := qqw#; qht# := dely*qqw#/delx;
+ else: qht# := qqw#; qwd# := delx*qqw#/dely; fi;
+ else:
+ absdely=-dely;
+ if delx > absdely: qwd# := qqw#; qht# := absdely*qqw#/delx;
+ else: qht# := qqw#; qwd# := delx*qqw#/absdely; fi;
+fi;
+
+beginchar(char,qwd#,qht#,0pt#);
+
+if dely > 0: y0=h; else: y0=0; fi;
+
+x0=w;
+rxa=-arrowlen; rya=0;
+rxb=rxa; ryb=arrowht;
+rxc=rxa; ryc=-arrowht;
+rxd=.5rxa; ryd=.5ryb-arrowcurve;
+rxe=rxd; rye=-ryd;
+
+numeric rlen;
+rlen = sqrt(delx*delx + dely*dely);
+
+%x1= rxa;y1=rya; %%%% replace the following by rotations
+x1=(rxa*delx - rya*dely)/rlen + x0;
+y1=(rxa*dely+rya*delx)/rlen + y0;
+
+%x2=rxb; y2=ryb;
+x2=(rxb*delx - ryb*dely)/rlen + x0;
+y2=(rxb*dely+ryb*delx)/rlen + y0;
+
+%x3=rxc; y3=ryc;
+x3=(rxc*delx - ryc*dely)/rlen + x0;
+y3=(rxc*dely+ryc*delx)/rlen + y0;
+
+%x4=rxd; y4=ryd;
+x4=(rxd*delx - ryd*dely)/rlen + x0;
+y4=(rxd*dely+ryd*delx)/rlen + y0;
+
+%x5=rxe; y5=rye;
+x5=(rxe*delx - rye*dely)/rlen + x0;
+y5=(rxe*dely+rye*delx)/rlen + y0;
+
+fill z0..z5..z3--z1--z2..z4..z0--cycle
+enddef;
+
+% character code rules, for
+% line(x,y):
+% 0 < x,y <= 6 : 8*(x-1) + (y-1)
+%
+% larrow(x,y):
+% 0 < x,y <= 4 : 8*(2x-1) + (2y-1)
+% y=0 : '33
+% rarrow(x,y):
+% 0 < x,y < 3 : 8*(3x-1) + (3y-1)
+% 0 < x <= 4, y=3 : 8*(2x-1) + 6
+% 0 < x < 4, y = 4 : 8*(2x) + 7
+% x=3, 0 < y <=4 : 8*6 + (2y-1)
+% x = 4, 0 < y < 4 : 8*7 + 2y
+% y=0 : 55
+%
+% for y < 0: value is 64 + value for (x, |y|)
+%
+% up arrow: '66 down arrow: '77
+
+fontdimen 1: 0,0,0,0,0,0,0,qqs#; % line thickness in points
+let cmchar=\; % cmchar should precede each character, for testing purposes
+
+
+font_coding_scheme:= "LaTeX line";
+
+cmchar "Character '00";
+line(1,1);
+endchar;
+
+cmchar "Character '11";
+larrow(9,1,1);
+endchar;
+
+cmchar "Character '22";
+rarrow(18,1,1);
+endchar;
+
+cmchar "Character '01";
+line(1,2);
+endchar;
+
+cmchar "Character '13";
+larrow(11,1,2);
+endchar;
+
+cmchar "Character '25";
+rarrow(21,1,2);
+endchar;
+
+cmchar "Character '02";
+line(1,3);
+endchar;
+
+cmchar "Character '15";
+larrow(13,1,3);
+endchar;
+
+cmchar "Character '16";
+rarrow(14,1,3);
+endchar;
+
+cmchar "Character '03";
+line(1,4);
+endchar;
+
+cmchar "Character '17";
+larrow(15,1,4);
+endchar;
+
+cmchar "Character '27";
+rarrow(23,1,4);
+endchar;
+
+cmchar "Character '04";
+line(1,5);
+endchar;
+
+cmchar "Character '05";
+line(1,6);
+endchar;
+
+cmchar "Character '10";
+line(2,1);
+endchar;
+
+cmchar "Character '31";
+larrow(25,2,1);
+endchar;
+
+cmchar "Character '52";
+rarrow(42,2,1);
+endchar;
+
+cmchar "Character '12";
+line(2,3);
+endchar;
+
+cmchar "Character '35";
+larrow(29,2,3);
+endchar;
+
+cmchar "Character '36";
+rarrow(30,2,3);
+endchar;
+
+cmchar "Character '14";
+line(2,5);
+endchar;
+
+cmchar "Character '20";
+line(3,1);
+endchar;
+
+cmchar "Character '51";
+larrow(41,3,1);
+endchar;
+
+cmchar "Character '61";
+rarrow(49,3,1);
+endchar;
+
+cmchar "Character '21";
+line(3,2);
+endchar;
+
+cmchar "Character '53";
+larrow(43,3,2);
+endchar;
+
+cmchar "Character '63";
+rarrow(51,3,2);
+endchar;
+
+cmchar "Character '23";
+line(3,4);
+endchar;
+
+cmchar "Character '57";
+larrow(47,3,4);
+endchar;
+
+cmchar "Character '67";
+rarrow(55,3,4);
+endchar;
+
+cmchar "Character '24";
+line(3,5);
+endchar;
+
+cmchar "Character '30";
+line(4,1);
+endchar;
+
+cmchar "Character '71";
+larrow(57,4,1);
+endchar;
+
+cmchar "Character '72";
+rarrow(58,4,1);
+endchar;
+
+cmchar "Character '32";
+line(4,3);
+endchar;
+
+cmchar "Character '75";
+larrow(61,4,3);
+endchar;
+
+cmchar "Character '76";
+rarrow(62,4,3);
+endchar;
+
+cmchar "Character '34";
+line(4,5);
+endchar;
+
+cmchar "Character '40";
+line(5,1);
+endchar;
+
+cmchar "Character '41";
+line(5,2);
+endchar;
+
+cmchar "Character '42";
+line(5,3);
+endchar;
+
+cmchar "Character '43";
+line(5,4);
+endchar;
+
+cmchar "Character '45";
+line(5,6);
+endchar;
+
+cmchar "Character '50";
+line(6,1);
+endchar;
+
+cmchar "Character '54";
+line(6,5);
+endchar;
+
+cmchar "Character '100";
+line(1,-1);
+endchar;
+
+cmchar "Character '111";
+larrow(73,1,-1);
+endchar;
+
+cmchar "Character '122";
+rarrow(82,1,-1);
+endchar;
+
+cmchar "Character '101";
+line(1,-2);
+endchar;
+
+cmchar "Character '113";
+larrow(75,1,-2);
+endchar;
+
+cmchar "Character '125";
+rarrow(85,1,-2);
+endchar;
+
+cmchar "Character '102";
+line(1,-3);
+endchar;
+
+cmchar "Character '115";
+larrow(77,1,-3);
+endchar;
+
+cmchar "Character '116";
+rarrow(78,1,-3);
+endchar;
+
+cmchar "Character '103";
+line(1,-4);
+endchar;
+
+cmchar "Character '117";
+larrow(79,1,-4);
+endchar;
+
+cmchar "Character '127";
+rarrow(87,1,-4);
+endchar;
+
+cmchar "Character '104";
+line(1,-5);
+endchar;
+
+cmchar "Character '105";
+line(1,-6);
+endchar;
+
+cmchar "Character '110";
+line(2,-1);
+endchar;
+
+cmchar "Character '131";
+larrow(89,2,-1);
+endchar;
+
+cmchar "Character '152";
+rarrow(106,2,-1);
+endchar;
+
+cmchar "Character '112";
+line(2,-3);
+endchar;
+
+cmchar "Character '135";
+larrow(93,2,-3);
+endchar;
+
+cmchar "Character '136";
+rarrow(94,2,-3);
+endchar;
+
+cmchar "Character '114";
+line(2,-5);
+endchar;
+
+cmchar "Character '120";
+line(3,-1);
+endchar;
+
+cmchar "Character '151";
+larrow(105,3,-1);
+endchar;
+
+cmchar "Character '161";
+rarrow(113,3,-1);
+endchar;
+
+cmchar "Character '121";
+line(3,-2);
+endchar;
+
+cmchar "Character '153";
+larrow(107,3,-2);
+endchar;
+
+cmchar "Character '163";
+rarrow(115,3,-2);
+endchar;
+
+cmchar "Character '123";
+line(3,-4);
+endchar;
+
+cmchar "Character '157";
+larrow(111,3,-4);
+endchar;
+
+cmchar "Character '167";
+rarrow(119,3,-4);
+endchar;
+
+cmchar "Character '124";
+line(3,-5);
+endchar;
+
+cmchar "Character '130";
+line(4,-1);
+endchar;
+
+cmchar "Character '171";
+larrow(121,4,-1);
+endchar;
+
+cmchar "Character '172";
+rarrow(122,4,-1);
+endchar;
+
+cmchar "Character '132";
+line(4,-3);
+endchar;
+
+cmchar "Character '175";
+larrow(125,4,-3);
+endchar;
+
+cmchar "Character '176";
+rarrow(126,4,-3);
+endchar;
+
+cmchar "Character '134";
+line(4,-5);
+endchar;
+
+cmchar "Character '140";
+line(5,-1);
+endchar;
+
+cmchar "Character '141";
+line(5,-2);
+endchar;
+
+cmchar "Character '142";
+line(5,-3);
+endchar;
+
+cmchar "Character '143";
+line(5,-4);
+endchar;
+
+cmchar "Character '145";
+line(5,-6);
+endchar;
+
+cmchar "Character '150";
+line(6,-1);
+endchar;
+
+cmchar "Character '154";
+line(6,-5);
+endchar;
+
+%%%Horizontal arrows:
+
+
+cmchar "Character '33";
+larrow(27,1,0);
+endchar;
+
+cmchar "Character '55"; %right arrow
+beginchar(45,qqw#,qqw#,0pt#);
+x0=w; y0=0;
+x1=x0-arrowlen; y1=0;
+x2=x1; y2=arrowht;
+x3=x1; y3=-y2;
+x4=.5[x0,x1]; y4=.5y2-arrowcurve;
+x5=x4; y5=-y4;
+fill z0..z4..z2--z1--z3..z5..z0--cycle;
+endchar;
+
+%%%vertical arrows
+
+
+cmchar "Character '66"; %up arrow
+beginchar(54,qqw#,qqw#,0pt#);
+x0=0; y0=w;
+y1=y0-arrowlen; x1=0;
+y2=y1; x2=arrowht;
+y3=y1; x3=-x2;
+y4=.5[y0,y1]; x4=.5x2-arrowcurve;
+y5=y4; x5=-x4;
+fill z0..z4..z2--z1--z3..z5..z0--cycle;
+endchar;
+
+
+cmchar "Character '77"; %down arrow
+beginchar(63,qqw#,qqw#,0pt#);
+x0=0; y0=0;
+y1=arrowlen; x1=0;
+y2=y1; x2=arrowht;
+y3=y1; x3=-x2;
+y4=.5y1; x4=.5x2-arrowcurve;
+y5=y4; x5=-x4;
+fill z0..z4..z2--z1--z3..z5..z0--cycle;
+endchar;
+
+bye
diff --git a/systems/knuth/local/lib/line10.mf b/systems/knuth/local/lib/line10.mf
new file mode 100644
index 0000000000..125c4eca93
--- /dev/null
+++ b/systems/knuth/local/lib/line10.mf
@@ -0,0 +1,11 @@
+font_identifier "LINE"; font_size 10pt#;
+font_coding_scheme:="LATEX LINE";
+mode_setup;
+
+qqs#=.4pt#; %width of lines
+qqh#=10pt#; %height of vertical stroke.
+qqw#=10pt#; %length of horizontal stroke.
+
+%arrow dimensions:
+arrowlen#=.4*qqw#; arrowht#=.15*qqw#; arrowcurve#=.1*arrowht#;
+input line;
diff --git a/systems/knuth/local/lib/linew10.mf b/systems/knuth/local/lib/linew10.mf
new file mode 100644
index 0000000000..cab30e9801
--- /dev/null
+++ b/systems/knuth/local/lib/linew10.mf
@@ -0,0 +1,11 @@
+font_identifier "LINEW"; font_size 10pt#;
+font_coding_scheme:="LATEX LINE";
+mode_setup;
+
+qqs#=.8pt#; %width of lines
+qqh#=10pt#; %height of vertical stroke.
+qqw#=10pt#; %length of horizontal stroke.
+
+%arrow dimensions:
+arrowlen#=.6*qqw#; arrowht#=.22*qqw#; arrowcurve#=.1*arrowht#;
+input line;
diff --git a/systems/knuth/local/lib/list-latin.tex b/systems/knuth/local/lib/list-latin.tex
new file mode 100644
index 0000000000..71d5bb6d9a
--- /dev/null
+++ b/systems/knuth/local/lib/list-latin.tex
@@ -0,0 +1,94 @@
+% This program by D. E. Knuth is not copyrighted and can be used freely.
+% Makes a listing of one or more files
+% Extended to allow the Latin-1 accented characters in UTF-8 encoding
+% Typical usage:
+% tex list-latin *.txt \\end
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex10
+\font\ttfont= cmtt10
+\font\syttfont= cmsytt10
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\raggedbottom
+\nopagenumbers
+
+\newcount\n
+\catcode`\^^c3=\active
+\def\missing{{\syttfont\char15}}
+\def^^c3#1{\n=`#1\advance\n by-128
+ {\ttfont\ifcase\n
+ \`A\or\'A\or\^A\or\~A\or\"A\or\AA\or\AE\or\c C\or
+ \`E\or\'E\or\^E\or\"E\or\`I\or\'I\or\^I\or\"I\or
+ \missing\or\~N\or\`O\or\'O\or\^O\or\~O\or\"O\or{\syttfont\char2}\or
+ \O\or\`U\or\'U\or\^U\or\"U\or\'Y\or\missing\or\ss\or
+ \`a\or\'a\or\^a\or\~a\or\"a\or\aa\or\ae\or\c c\or
+ \`e\or\'e\or\^e\or\"e\or\`\i\or\'\i\or\^\i\or\"\i\or
+ \missing\or\~n\or\`o\or\'o\or\^o\or\~o\or\"o\or{\syttfont\char4}\or
+ \o\or\`u\or\'u\or\^u\or\"u\or\'y\or\missing\or\"y\fi}}
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\filenamefont#1\quad\headlinefont \today\ at \hours
+ \hfill Page \folio}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus 1in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-500\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 103 characters on a line without an overfull box.
+
+% Further notes from Don, January 2017:
+%
+% Maybe I'm the only person left who still uses plain TeX in a UTF-8 world.
+%
+% But for what it's worth, I'm appending a little file that I prepared today,
+% since it turned out to be useful in a project of interest to me.
+%
+% Namely, I've got a program that takes an XML encoding of organ-pipe
+% settings, used by performers when they change the stops during a
+% performance, and translates it into a human-readable form.
+% I mean, the output says "when you reach combination nunber 0013,
+% the following ranks of pipes are set to play".
+%
+% The names of those ranks of pipes often contain accented characters,
+% as well as things like the German \ss. In order to use such texts on the Web,
+% I have them in UTF-8 code. But to make a verbatim hardcopy listing using
+% nice typewriter-style fonts, I wanted a program like "list.tex", which
+% I've been using to print text files for more than 25 years.
+%
+% Fortunately, the accented characters all turn out to be two-character
+% sequences in UTF-8, where the first character is always '303. So I
+% can simply make that character active, and it can then change the
+% following character appropriately.
+%
+% Therefore I added a few lines to "list.tex", resulting in "list-latin.tex".
+% And it works! Hence it might be a useful addition to the CTAN archives.
+%
+% Yours truly, Don
diff --git a/systems/knuth/local/lib/list.tex b/systems/knuth/local/lib/list.tex
new file mode 100644
index 0000000000..1daeba2bbe
--- /dev/null
+++ b/systems/knuth/local/lib/list.tex
@@ -0,0 +1,47 @@
+% Makes a listing of one or more files
+% Typical usage:
+% tex list *.c \\end
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex10
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\raggedbottom
+\nopagenumbers
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\filenamefont#1\quad\headlinefont \today\ at \hours
+ \hfill Page \folio}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus 1in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-500\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 103 characters on a line without an overfull box.
diff --git a/systems/knuth/local/lib/llist.tex b/systems/knuth/local/lib/llist.tex
new file mode 100644
index 0000000000..f107169e24
--- /dev/null
+++ b/systems/knuth/local/lib/llist.tex
@@ -0,0 +1,67 @@
+% Makes a landscape listing of one or more files
+% Typical usage:
+% tex llist *.c \\end
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex8
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\baselineskip=9pt
+\medskipamount=4pt minus 1pt
+\raggedbottom
+\nopagenumbers
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\ifright \headlinefont \hfill Page \folio
+ \else\filenamefont#1\quad\headlinefont \today\ at \hours\hfill \fi}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus .8in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-50\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject
+ \ifright\null\vfill\eject\fi\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+\hsize=4.6truein
+\vsize=7truein
+\hoffset=-.5truein
+\voffset=-.2truein
+\newif\ifright
+\newbox\lefthalf
+\output{\ifright
+ \shipout\hbox{\hbox to5.5in{\box\lefthalf\hss}%
+ \vbox{\makeheadline\pagebody\makefootline}}
+ \advancepageno\global\rightfalse
+ \else\global\setbox\lefthalf\vbox{\makeheadline\pagebody\makefootline}
+ \global\righttrue\fi}
+
+\special{landscape}
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 91 characters on a line without an overfull box.
+% (But lines with more than 84 characters will be clipped, if they
+% appear on a righthand halfpage.)
diff --git a/systems/knuth/local/lib/local.mf b/systems/knuth/local/lib/local.mf
new file mode 100644
index 0000000000..1690d48049
--- /dev/null
+++ b/systems/knuth/local/lib/local.mf
@@ -0,0 +1,122 @@
+% This file can be loaded after plain.mf.
+% It introduces conventions that are commonly used on my home computer,
+% while remaining compatible with the plain base.
+% It also adds some macros added by AMS in their amsmodes.mf, version 2.1a
+base_version:=base_version&"/art";
+
+def cand(text q) = startif true q else: false fi enddef;
+def cor(text q) = startif true true else: q fi enddef;
+tertiarydef p startif true = if p: enddef;
+
+def mode_def suffix $ =
+ if known($) cand
+ (numeric $ cand
+ ($<number_of_modes cand
+ (mode_name[$] = (str $ & "_")))):
+ else: if known(mode-$) cand (unknown mode): mode:= fi
+ $:=incr number_of_modes;
+ mode_name[$]:=str$ & "_";
+ fi
+ expandafter quote def scantokens mode_name[$] enddef;
+
+screen_rows:=1024; screen_cols:=1024;
+
+% Here are conventions for local output devices:
+
+mode_def imagen = % imagen mode: for the Imagen 8/300 (Canon engine)
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=300;
+ blacker:=0; % Canon engine is black enough
+ fillin:=.2; % and it tends to fill in diagonals
+ o_correction:=.6; %
+ enddef;
+
+mode_def aps = % aps mode: for the Autologic APS-Micro5
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=1; % yes, show titles online
+ pixels_per_inch:=722.909; % that's roughly 10 per pt
+ blacker:=.2; % make pens a teeny bit blacker
+ fillin:=.2; % but compensate for diagonal fillin
+ o_correction:=1; % and keep the full overshoot
+ enddef;
+
+mode_def linohi = % lino mode: for the Linotronic at 1270dpi
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=1; % yes, show titles online
+ pixels_per_inch:=1270;
+ blacker:=.2; % make pens a teeny bit blacker
+ fillin:=.2; % but compensate for diagonal fillin
+ o_correction:=1; % and keep the full overshoot
+ enddef;
+
+mode_def fourc = % for 400dpi printers
+ proofing:=0; % no, we're not making proofs
+ fontmaking:=1; % yes, we are making a font
+ tracingtitles:=0; % no, don't show titles in the log
+ pixels_per_inch:=400;
+ blacker:=0; % engine is black enough
+ fillin:=.2; % and it tends to fill in diagonals
+ o_correction:=.6; %
+ enddef;
+
+localfont:=imagen;
+
+% Macros to add mode information specials to fonts. (by Pierre MacKay)
+
+def mode_special(suffix $) =
+ string s,d;
+ s:=str$; d:=decimal scantokens s;
+ special s&":="&d&";" enddef;
+
+def font_mode_specials =
+ p_p_i=pixels_per_inch/mag;
+ if fontmaking > 0:
+ begingroup; save d,s,p; save pixels_per_inch;
+ string p;
+ pixels_per_inch:=p_p_i;
+ special jobname;
+ mode_special(mag);
+ if string mode: p:=mode;
+ else: p:=substring(0,length(mode_name[mode])-1) of mode_name[mode]; fi
+ special "mode:="&p&";";
+ mode_special(pixels_per_inch);
+ mode_special(blacker);
+ mode_special(fillin);
+ mode_special(o_correction);
+ endgroup;
+ fi
+enddef;
+
+% Finally, here are macros for Xerox-world font info:
+
+def font_family expr s = % string s names the font family, e.g., "CMR"
+ headerbyte 49: BCPL_string(s,20);
+ special "identifier "&s enddef;
+def coding_scheme expr s = % string s names the scheme, e.g. "TEX TEXT"
+ headerbyte 9: BCPL_string(s,40);
+ special "codingscheme "&s enddef;
+def font_face_byte expr x = % integer x gives the family member number,
+ headerbyte 72: x; % which should be between 0 and 255
+ special "fontfacebyte"; numspecial x enddef;
+
+def BCPL_string(expr s,n)= % string s becomes an n-byte BCPL string
+ for l:=if length(s)>=n: n-1 else: length(s) fi: l
+ for k:=1 upto l: , substring (k-1,k) of s endfor
+ for k:=l+2 upto n: , 0 endfor endfor enddef;
+
+Xerox_world:=1; % users can say `if known Xerox_world:...fi'
+
+inner end;
+def bye=
+ if fontmaking>0: font_family font_identifier_;
+ coding_scheme font_coding_scheme_;
+ font_face_byte max(0,254-round 2designsize);
+ font_mode_specials; fi
+ end
+ enddef;
+
+outer bye,end;
diff --git a/systems/knuth/local/lib/local.tex b/systems/knuth/local/lib/local.tex
new file mode 100644
index 0000000000..6ea2d45cbf
--- /dev/null
+++ b/systems/knuth/local/lib/local.tex
@@ -0,0 +1,8 @@
+% This file can be loaded after plain.tex.
+% It introduces conventions that are commonly used on my home computer,
+% while remaining compatible with the plain format.
+\xdef\fmtversion{\fmtversion/art}
+
+% actually there's nothing needed here, now that \loggingall is public
+
+
diff --git a/systems/knuth/local/lib/logod10.mf b/systems/knuth/local/lib/logod10.mf
new file mode 100644
index 0000000000..fc484a4b18
--- /dev/null
+++ b/systems/knuth/local/lib/logod10.mf
@@ -0,0 +1,10 @@
+% 10-point demibold METAFONT logo
+font_size 10pt#; % the "design size" of this font
+ht#:=6pt#; % height of characters
+xgap#:=21/20*0.6pt#; % horizontal adjustment
+u#:=21/20*4/9pt#; % unit width
+s#:=0; % extra space at the left and the right
+o#:=1/9pt#; % overshoot
+px#:=.5[2/3pt#,44/36pt#]; % horizontal thickness of pen
+input logo % now generate the font
+bye % and stop.
diff --git a/systems/knuth/local/lib/logosl9.mf b/systems/knuth/local/lib/logosl9.mf
new file mode 100644
index 0000000000..03b3d92288
--- /dev/null
+++ b/systems/knuth/local/lib/logosl9.mf
@@ -0,0 +1,3 @@
+% 9-point slanted METAFONT logo
+slant := 1/4;
+input logo9
diff --git a/systems/knuth/local/lib/mfman.mf b/systems/knuth/local/lib/mfman.mf
new file mode 100644
index 0000000000..e272689889
--- /dev/null
+++ b/systems/knuth/local/lib/mfman.mf
@@ -0,0 +1,2481 @@
+% This file generates most of the figures for The METAFONTbook
+
+lcode_:=" /"; % suppress overflow labels
+grayfont black;
+
+def clear_all = clearit; clearxy; pickup standardpen;
+proofrulethickness 0;
+enddef;
+
+def heavy_dot expr z =
+ makelabel.top("",z-(1,0));
+ makelabel.top("",z+(1,0));
+ makelabel.top("",z-(0,1));
+ makelabel.top("",z+(0,1));
+ makelabel.top("",z+(1,1)/sqrt2);
+ makelabel.top("",z+(1,-1)/sqrt2);
+ makelabel.top("",z-(1,1)/sqrt2);
+ makelabel.top("",z-(1,-1)/sqrt2);
+ enddef;
+
+def font_setup=
+define_pixels(u,tiny,axis,hheight,border);
+define_blacker_pixels(hair,thin,thick,rulethickness);
+pickup if tiny<.5: nullpen else: pencircle scaled tiny fi;
+tinypen:=savepen;
+currenttransform:=identity slanted slant yscaled aspect_ratio;
+enddef;
+
+mode=proof; mode_setup;
+standardpen=savepen;
+
+"Figure 2a"; % 6 points and a grid
+clear_all;
+pair offset; offset=(.5,.5);
+z1=(0,100)+offset;
+z2=(100,100)+offset;
+z3=(200,100)+offset;
+z4=(0,0)+offset;
+z5=(100,0)+offset;
+z6=(200,0)+offset;
+proofrulethickness .8;
+proofrule (z1+(0,5),z4-(0,5)); proofrule (z4-(5,0),z6+(5,0));
+makelabel.top("1 ",z1);
+makelabel.top("2 ",z2);
+makelabel.top("3 ",z3);
+makelabel.bot("4 ",z4);
+makelabel.bot("5 ",z5);
+makelabel.bot("6 ",z6);
+for k=1 upto 6: heavy_dot z[k]; endfor;
+pickup pencircle;
+for x=10 step 10 until 200: draw (x,-5)+offset..(x,105)+offset; endfor
+for y=10 step 10 until 100: draw (-5,y)+offset..(205,y)+offset; endfor
+showit;
+shipit;
+
+"Figure A2a"; % 6 points and a grid and two more points
+clear_all;
+proofoffset(30,0);
+pair offset; offset=(.5,.5);
+z1=(0,100)+offset;
+z2=(100,100)+offset;
+z3=(200,100)+offset;
+z4=(0,0)+offset;
+z5=(100,0)+offset;
+z6=(200,0)+offset;
+proofrulethickness .8;
+proofrule (z1+(0,5),z4-(0,5)); proofrule (z4-(5,0),z6+(5,0));
+makelabel.top("1 ",z1);
+makelabel.top("2 ",z2);
+makelabel.top("3 ",z3);
+makelabel.bot("4 ",z4);
+makelabel.bot("5 ",z5);
+makelabel.bot("6 ",z6);
+for k=1 upto 6: heavy_dot z[k]; endfor;
+makelabel.lft("(-5,15)",(-5,15)+offset);
+makelabel.top("(60,30) ",(60,30)+offset);
+%makelabel.top(" ,30) ",(60,30)+offset);
+%makelabel.top("(60 ",(60,30)+offset);
+heavy_dot (-5,15)+offset;
+heavy_dot (60,30)+offset;
+pickup pencircle;
+for x=10 step 10 until 200: draw (x,-5)+offset..(x,105)+offset; endfor
+for y=10 step 10 until 100: draw (-5,y)+offset..(205,y)+offset; endfor
+showit;
+shipit;
+
+"Figure 2b"; % 6 points and a line
+clear_all;
+z1=(0,100);
+z2=(100,100);
+z3=(200,100);
+z4=(0,0);
+z5=(100,0);
+z6=(200,0);
+draw (x1-epsilon,y1)..(x6,y6);
+makelabel.lft("1 ",z1);
+makelabel.lft("2",z2);
+makelabel.rt("3",z3);
+makelabel.lft("4",z4);
+makelabel.lft("5",z5);
+makelabel.rt(" 6",z6);
+showit;
+shipit;
+
+"Figure 2c"; % 6 points and three lines (hex symbol, version 1)
+clear_all;
+z1=(0,100);
+z2=(100,100);
+z3=(200,100);
+z4=(0,0);
+z5=(100,0);
+z6=(200,0);
+draw z1..z6; draw z2..z5; draw z3..z4;
+for k=1 upto 6: drawdot z[k]; endfor
+makelabel.lft("1 ",z1);
+makelabel.lft("2 ",z2);
+makelabel.rt(" 3",z3);
+makelabel.lft("4 ",z4);
+makelabel.lft("5 ",z5);
+makelabel.rt(" 6",z6);
+showit;
+shipit;
+
+"Figure 2d"; % hex symbols with top and bottom adjusted
+clear_all;
+top z1=(0,100);
+top z2=(100,100);
+top z3=(200,100);
+bot z4=(0,0);
+bot z5=(100,0);
+bot z6=(200,0);
+draw z1..z6; draw z2..z5; draw z3..z4;
+for k=1 upto 6: drawdot z[k]; endfor
+makelabel.lft("1 ",z1);
+makelabel.lft("2 ",z2);
+makelabel.rt(" 3",z3);
+makelabel.lft("4 ",z4);
+makelabel.lft("5 ",z5);
+makelabel.rt(" 6",z6);
+showit;
+clearxy; pair offset; offset=(250,0);
+pickup pencircle scaled .6pt;
+top z1=(0,100)+offset;
+top z2=(100,100)+offset;
+top z3=(200,100)+offset;
+bot z4=(0,0)+offset;
+bot z5=(100,0)+offset;
+bot z6=(200,0)+offset;
+draw z1..z6; draw z2..z5; draw z3..z4;
+for k=1 upto 6: drawdot z[k]; endfor
+makelabel.lft("1 ",z1);
+makelabel.lft("2 ",z2);
+makelabel.rt(" 3",z3);
+makelabel.lft("4 ",z4);
+makelabel.lft("5 ",z5);
+makelabel.rt(" 6",z6);
+proofrule ((-5,100),(210,100)+offset);
+proofrule ((-5,0),(210,0)+offset);
+showit;
+shipit;
+
+"Figure 2e"; % stick-letter A
+b#:=250/36pt#; a#:=150/36pt#; s#:=30/36pt#;
+define_pixels(b,a,s);
+def A(expr alpha)=
+beginchar (incr(charcode),s#+a#+s#,b#,0);
+pickup standardpen;
+bot z1=(good.x s,0); z5=z1+(a,0);
+z3=(1/2[x1,x5],good.y b);
+z2=alpha[z1,z3]; z4=alpha[z5,z3];
+draw z1..z3; draw z3..z5; draw z2..z4;
+drawdot z1; drawdot z5; drawdot z3;
+makelabel.lft("1 ",z1);
+makelabel.lft("2 ",z2);
+makelabel.top("3 ",z3);
+makelabel.rt(" 4",z4);
+makelabel.rt(" 5",z5);
+endchar; enddef;
+
+A((3-sqrt5)/2); % (area above bar / area below) = golden ratio
+
+"Figure 3a"; % 4 points and 3 midpoints
+clear_all;
+z1=(.5,.5); z2=(40.5,80.5); z3=(150.5,100.5); z4=(300.5,.5);
+z12=1/2[z1,z2]; z23=1/2[z2,z3]; z34=1/2[z3,z4];
+pickup pencircle;
+draw z1--z2--z3--z4;
+heavy_dot z1; heavy_dot z2; heavy_dot z3; heavy_dot z4;
+makelabel.lft("1",z1);
+makelabel.lft("2",z2);
+makelabel.top(" 3",z3);
+makelabel.rt("4",z4);
+makelabel.rt("12",z12);
+makelabel.bot(" 23",z23);
+makelabel.bot("34 ",z34);
+showit;
+shipit;
+
+"Figure 3b"; % 4 points and 6 midpoints
+clear_all;
+z1=(.5,.5); z2=(40.5,80.5); z3=(150.5,100.5); z4=(300.5,.5);
+z12=1/2[z1,z2]; z23=1/2[z2,z3]; z34=1/2[z3,z4];
+z123=1/2[z12,z23]; z234=1/2[z23,z34]; z1234=1/2[z123,z234];
+heavy_dot z1; heavy_dot z2; heavy_dot z3; heavy_dot z4;
+pickup pencircle;
+draw z1--z2--z3--z4; draw z12--z23--z34; draw z123--z234;
+makelabel.lft("1",z1);
+makelabel.lft("2",z2);
+makelabel.top(" 3",z3);
+makelabel.rt("4",z4);
+makelabel.lft("12",z12);
+makelabel.top("23 ",z23);
+makelabel.rt("34",z34);
+makelabel.bot(" 123",z123);
+makelabel.bot("234 ",z234);
+makelabel.bot("1234",z1234);
+showit;
+shipit;
+
+"Figure 3c"; % 4 points and infinitely many midpoints
+clear_all;
+z1=(.5,.5); z2=(40.5,80.5); z3=(150.5,100.5); z4=(300.5,.5);
+heavy_dot z1; heavy_dot z2; heavy_dot z3; heavy_dot z4;
+pickup pencircle;
+
+def makedot(expr x)= special " 1";
+ numspecial xpart x; numspecial ypart x enddef;
+
+def recurse(expr a,b,c,d)=
+dist:=abs(a-d);
+if dist>4:
+begingroup save e,f,g,h,i,j; pair e,f,g,h,i,j;
+e=(a+b)/2; makedot(e);
+f=(b+c)/2; makedot(f);
+g=(c+d)/2; makedot(g);
+h=(e+f)/2; makedot(h);
+i=(f+g)/2; makedot(i);
+j=(h+i)/2; makedot(j);
+if dist>20: draw a--b--c--d; draw e--f--g; draw h--i; fi
+recurse(a,e,h,j); recurse(j,i,g,d);
+endgroup;
+fi enddef;
+
+makedot(z1); makedot(z2); makedot(z3); makedot(z4); recurse(z1,z2,z3,z4);
+makelabel.bot.nodot("ignore",(160,-10)); % kludge for GFtoEPS!
+shipit;
+
+"Figure 3d"; % 4 points and various cubics
+clear_all;
+z1=(0,0); z2=5/6(40,80); z3=5/6(150,100); z4=5/6(300,0);
+draw z1..controls z2 and z3..z4;
+drawdot z1; drawdot z4;
+makelabel.lft("1 ",z1);
+makelabel.lft("2",z2);
+makelabel.rt("3",z3);
+makelabel.rt(" 4",z4);
+pair offset; offset:=(325,0);
+draw (z1..controls z3 and z2..z4) shifted offset;
+drawdot z1+offset; drawdot z4+offset;
+makelabel.lft("1 ",z1+offset);
+makelabel.lft("3",z2+offset);
+makelabel.rt("2",z3+offset);
+makelabel.rt(" 4",z4+offset);
+offset:=(0,-100);
+draw (z2..controls z1 and z3..z4) shifted offset;
+drawdot z2+offset; drawdot z4+offset;
+makelabel.lft("2",z1+offset);
+makelabel.lft("1 ",z2+offset);
+makelabel.rt("3",z3+offset);
+makelabel.rt(" 4",z4+offset);
+offset:=(325,-100);
+draw (z1..controls z4 and z2..z3) shifted offset;
+drawdot z1+offset; drawdot z3+offset;
+makelabel.lft("1 ",z1+offset);
+makelabel.lft("3",z2+offset);
+makelabel.rt(" 4",z3+offset);
+makelabel.rt("2",z4+offset);
+showit;
+shipit;
+
+"Figure 3e"; % example curves with automatic control points
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z4..z1..z2..z6;
+draw p;
+drawdot z4; drawdot z6;
+makelabel.bot.nodot(" 1",z1);
+makelabel.top.nodot(" 2",z2);
+makelabel.rt.nodot("3",z3+(2,0));
+makelabel.rt.nodot(" 4",z4);
+makelabel.rt.nodot("5",z5+(2,0));
+makelabel.rt.nodot(" 6",z6);
+for k=1 upto 6: heavy_dot z[k]; endfor;
+for k=1 upto length p: heavy_dot postcontrol k-1 of p;
+ heavy_dot precontrol k of p; endfor
+pair offset; offset=(325,0);
+p:=(z5..z4..z1..z3..z6..z5) shifted offset; draw p;
+drawdot z5+offset;
+makelabel.bot.nodot(" 1",z1+offset);
+makelabel.top.nodot("2",z2+offset+(0,2));
+makelabel.bot.nodot("3 ",z3+offset);
+makelabel.top.nodot(" 4",z4+offset);
+makelabel.top.nodot("5",z5+offset+(0,5));
+makelabel.top.nodot("6 ",z6+offset);
+for k=1 upto 6: heavy_dot z[k]+offset; endfor;
+for k=1 upto length p: heavy_dot postcontrol k-1 of p;
+ heavy_dot precontrol k of p; endfor
+showit;
+shipit;
+
+"Figure 3f"; % bean shape with automatic control points
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z5..z4..z1..z3..z6..cycle;
+draw p;
+makelabel.bot.nodot(" 1",z1);
+makelabel.bot.nodot("2",z2-(0,2));
+makelabel.bot.nodot("3 ",z3);
+makelabel.top.nodot(" 4",z4+(-1,1));
+makelabel.top.nodot("5",z5+(0,5));
+makelabel.top.nodot("6 ",z6+(1,1));
+for k=1 upto 6: heavy_dot z[k]; endfor;
+for k=1 upto length p: heavy_dot postcontrol k-1 of p;
+ heavy_dot precontrol k of p; endfor
+showit;
+shipit;
+
+"Figure 3g"; % bean shape with more tension between 1 and 3
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z5..z4..z1..tension1.2..z3..z6..cycle;
+draw p;
+makelabel.bot.nodot(" 1",z1);
+makelabel.bot.nodot("2",z2-(0,2));
+makelabel.bot.nodot("3 ",z3);
+makelabel.top.nodot(" 4",z4+(-1,1));
+makelabel.top.nodot("5",z5+(0,5));
+makelabel.top.nodot("6 ",z6+(1,1));
+for k=1 upto 6: heavy_dot z[k]; endfor;
+for k=1 upto length p: heavy_dot postcontrol k-1 of p;
+ heavy_dot precontrol k of p; endfor
+showit;
+shipit;
+
+"Figure 3h"; % bean shape with more tension before and after 1
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z5..z4..tension 1 and 1.5..z1..tension 1.5 and 1..z3..z6..cycle;
+draw p;
+makelabel.bot.nodot(" 1",z1);
+makelabel.bot.nodot("2",z2-(0,2));
+makelabel.bot.nodot("3 ",z3);
+makelabel.top.nodot(" 4",z4+(-1,1));
+makelabel.top.nodot("5",z5+(0,5));
+makelabel.top.nodot("6 ",z6+(1,1));
+for k=1 upto 6: heavy_dot z[k]; endfor;
+for k=1 upto length p: heavy_dot postcontrol k-1 of p;
+ heavy_dot precontrol k of p; endfor
+showit;
+shipit;
+
+"Figure 3i"; % bean shape with straight bottom
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z5..z4{left}..z1..z3..z6{left}..cycle;
+draw p;
+makelabel.bot.nodot(" 1",z1-(1,1));
+makelabel.bot.nodot("2",z2-(0,2));
+makelabel.bot.nodot("3 ",z3+(1,-1));
+makelabel.top.nodot(" 4",z4+(0,5));
+makelabel.top.nodot("5",z5+(0,5));
+makelabel.top.nodot("6 ",z6+(0,5));
+for k=1 upto 6: heavy_dot z[k]; endfor;
+for k=1 upto length p: heavy_dot postcontrol k-1 of p;
+ heavy_dot precontrol k of p; endfor
+showit;
+shipit;
+
+"Figure 3j"; % example with direction specified
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z4..z2{z3-z4}..z3;
+draw p;
+makelabel.rt.nodot("1",z1+(0,-9));
+makelabel.rt.nodot("2",z2+(1,-11));
+makelabel.rt.nodot("3",z3+(1,-11));
+makelabel.rt.nodot("4",z4+(1,-11));
+makelabel.rt.nodot("5",z5+(0,-9));
+makelabel.rt.nodot("6",z6+(0,-9));
+for k=1 upto 6: heavy_dot z[k]; endfor;
+for k=1 upto length p: heavy_dot postcontrol k-1 of p;
+ heavy_dot precontrol k of p; endfor
+showit;
+shipit;
+
+"Figure 3k"; % wiggly shape for exercise
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z5..z4{z4-z2}..z1..z3..z6{z2-z6}..cycle;
+draw p;
+makelabel.bot.nodot(" 1",z1-(2,2));
+makelabel.bot.nodot("2",z2-(0,2));
+makelabel.bot.nodot("3 ",z3+(2,-2));
+makelabel.bot.nodot(" 4",z4);
+makelabel.top.nodot("5",z5+(0,5));
+makelabel.bot.nodot("6 ",z6);
+for k=1 upto 6: heavy_dot z[k]; endfor;
+showit;
+shipit;
+
+"Figure 3l"; % examples of varying curl
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z4{curl0}..z2{z3-z4}..{curl0}z3;
+draw p;
+drawdot z4; drawdot z3;
+makelabel.rt.nodot("1",z1+(0,-9));
+makelabel.rt.nodot("2",z2+(1,-11));
+makelabel.rt.nodot("3",z3+(1,-11));
+makelabel.rt.nodot("4",z4+(1,-11));
+makelabel.rt.nodot("5",z5+(0,-9));
+makelabel.rt.nodot("6",z6+(0,-9));
+for k=1 upto 6: heavy_dot z[k]; endfor;
+for k=1 upto length p: heavy_dot postcontrol k-1 of p;
+ heavy_dot precontrol k of p; endfor
+pair offset; offset=(325,0);
+p:=(z4{curl2}..z2{z3-z4}..{curl2}z3) shifted offset; draw p;
+drawdot z4+offset; drawdot z3+offset;
+makelabel.rt.nodot("1",z1+(0,-9)+offset);
+makelabel.rt.nodot("2",z2+(1,-11)+offset);
+makelabel.rt.nodot("3",z3+(1,-11)+offset);
+makelabel.rt.nodot("4",z4+(1,-11)+offset);
+makelabel.rt.nodot("5",z5+(0,-9)+offset);
+makelabel.rt.nodot("6",z6+(0,-9)+offset);
+for k=1 upto 6: heavy_dot z[k]+offset; endfor;
+for k=1 upto length p: heavy_dot postcontrol k-1 of p;
+ heavy_dot precontrol k of p; endfor
+showit;
+shipit;
+
+"Figure 3m"; % two directions specified
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z4{z2-z4}..z6{down};
+draw p;
+drawdot z4; drawdot z6;
+makelabel.rt.nodot("1",z1+(2,0));
+makelabel.rt.nodot("2",z2+(2,0));
+makelabel.rt.nodot("3",z3+(2,0));
+makelabel.bot.nodot(" 4",z4);
+makelabel.rt.nodot("5",z5+(2,0));
+makelabel.rt.nodot(" 6",z6);
+for k=1 upto 6: heavy_dot z[k]; endfor;
+for k=1 upto length p: heavy_dot postcontrol k-1 of p;
+ heavy_dot precontrol k of p; endfor
+showit;
+shipit;
+
+"Figure 3n"; % two control points specified
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z4..controls z1 and z2..z6;
+draw p;
+drawdot z4; drawdot z6;
+makelabel.rt.nodot("1",z1+(2,0));
+makelabel.rt.nodot("2",z2+(2,0));
+makelabel.rt.nodot("3",z3+(2,0));
+makelabel.rt.nodot(" 4",z4);
+makelabel.rt.nodot("5",z5+(2,0));
+makelabel.rt.nodot(" 6",z6);
+for k=1 upto 6: heavy_dot z[k]; endfor;
+showit;
+shipit;
+
+"Figure 4a"; % bean shape with elliptical pens
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z5..z4..z1..z3..z6..cycle;
+pickup pencircle xscaled .8pt yscaled .2pt rotated 30;
+draw p;
+makelabel.bot(" 1",z1);
+makelabel.bot("2",z2);
+makelabel.bot(" 3",z3);
+makelabel.top("4 ",z4);
+makelabel.top("",z5); makelabel.top.nodot("5",z5+(0,5));
+makelabel.top("",z6); makelabel.top.nodot("6 ",z6+(1,1));
+pair offset; offset=(325,0);
+pickup pencircle xscaled .8pt rotated 30;
+draw p shifted offset;
+makelabel.bot(" 1",z1+offset);
+makelabel.bot("2",z2+offset);
+makelabel.bot(" 3",z3+offset);
+makelabel.top("4 ",z4+offset);
+makelabel.top("",z5+offset); makelabel.top.nodot("5",z5+(0,5)+offset);
+makelabel.top("",z6+offset); makelabel.top.nodot("6 ",z6+(1,1)+offset);
+showit;
+shipit;
+
+"Figure 4b"; % T of METAFONT logo
+alpha:=.45; % controls bar location and similar things
+beta:=.2; % controls squareness of bowls
+numeric h#, xx#, yy#, o#, s#, u#, ph#, pv#;
+numeric leftstemloc, barheight;
+ h#=6; % height of characters, in pt
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ ph#=2/3; % horizontal thickness of pen, in pt
+yy#=xx#; define_whole_pixels(xx,yy);
+define_pixels(s,u);
+define_corrected_pixels(o);
+pv#=.9ph#; define_blacker_pixels(ph,pv);
+pickup pencircle xscaled ph yscaled pv;
+logo_pen:=savepen;
+leftstemloc:=good.x((2.5u#+s#)*pt);
+barheight:=good.y(alpha*h#*pt);
+def beginlogochar(suffix c)(expr n) = % code c; width is n units plus sidebars
+ beginchar(str c,n*u#+2s#,h#,0); pickup logo_pen enddef;
+
+beginlogochar(T,13);
+if odd(w-ph): w:=w+1; fi % allows a symmetric stem
+x1+x2=2x3=2x4=w; lft.x1=-epsilon; % I tried s-o instead; was not as good
+y1=y2=y3; top.y1=h; bot.y4=-o;
+draw z1..z2; draw z3..z4;
+labels.rt(2,3);
+makelabel.top("1",z1);
+makelabel.top("4",z4);
+endchar;
+
+"Figure 4c"; % M of METAFONT logo
+alpha:=.45; % controls bar location and similar things
+beta:=.2; % controls squareness of bowls
+numeric h#, xx#, yy#, o#, s#, u#, ph#, pv#;
+numeric leftstemloc, barheight;
+ h#=6; % height of characters, in pt
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ ph#=2/3; % horizontal thickness of pen, in pt
+yy#=xx#; define_whole_pixels(xx,yy);
+define_pixels(s,u);
+define_corrected_pixels(o);
+pv#=.9ph#; define_blacker_pixels(ph,pv);
+pickup pencircle xscaled ph yscaled pv;
+logo_pen:=savepen;
+leftstemloc:=good.x((2.5u#+s#)*pt);
+barheight:=good.y(alpha*h#*pt);
+def beginlogochar(suffix c)(expr n) = % code c; width is n units plus sidebars
+ beginchar(str c,n*u#+2s#,h#,0); pickup logo_pen enddef;
+
+beginlogochar(M,18);
+x1+x5=x2+x4=2x3=w; x1=x2=leftstemloc;
+y1=y5; y2=y4; bot.y1=-o; top y2=h+o; y3=y1+yy;
+draw z1--z2--z3--z4--z5;
+labels.rt(3,5);
+makelabel.lft("1",z1);
+makelabel.bot("2",z2);
+makelabel.bot("4",z4);
+endchar;
+
+"Figure 4d"; % solid kidney bean
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z5..z4..z1..z3..z6..cycle;
+fill p;
+makelabel.bot(" 1",z1);
+makelabel.bot("2",z2);
+makelabel.bot("3 ",z3);
+makelabel.top(" 4",z4);
+makelabel.top("5",z5);
+makelabel.top("6 ",z6);
+showit;
+shipit;
+
+"Figure 4e"; % black triangle character for exercises
+beginchar(oct"170", 5pt#, 6.25pt#, 0);
+t:=(2-sqrt3)/2;
+x1=x2=w-x3+.5-t=round .43pt; y3=.5+floor.5h; % MANUAL font has it different!
+%x1=x2=w-x3=round .43pt; y3=.5+floor.5h;
+z1-z2=(z3-z2) rotated 60;
+y2:=t+round(y2-t); y1:=h-y2;
+fill z1--z2--z3--cycle;
+labels.lft(1,2); labels.rt(3); endchar;
+
+"Figure 4f"; % black bean with white holes
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z5..z4..z1..z3..z6..cycle;
+fill p;
+unfill 1/4[z4,z2]..3/4[z4,z2]..cycle;
+unfill 1/4[z6,z2]..3/4[z6,z2]..cycle;
+makelabel.bot(" 1",z1);
+makelabel.bot("2",z2);
+makelabel.bot("3 ",z3);
+makelabel.top(" 4",z4);
+makelabel.top("5",z5);
+makelabel.top("6 ",z6);
+showit;
+shipit;
+
+"Figure 4g"; % black bean with skewed interior cutout
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z5..z4..z1..z3..z6..cycle;
+fill p;
+z0=(.8[x1,x2],.5[y1,y4]);
+for k=1 upto 6: z[k]'=.2[z[k],z0]; endfor
+unfill z5'..z4'..z1'..z3'..z6'..cycle;
+makelabel.bot("0",z0);
+makelabel.top("1 ",z1);
+makelabel.top("2",z2);
+makelabel.top(" 3",z3);
+makelabel.bot("4 ",z4);
+makelabel.bot("5",z5);
+makelabel.bot(" 6",z6);
+makelabel.bot(" 1'",z1');
+makelabel.bot(" 2'",z2');
+makelabel.bot("3' ",z3');
+makelabel.top(" 4'",z4');
+makelabel.top("5'",z5');
+makelabel.top("6' ",z6');
+showit;
+shipit;
+
+"Figure 4h"; % sample penstroke
+clear_all;
+penpos1(1.2pt,30); penpos2(1.0pt,45); penpos3(0.8pt,90);
+z1=(0,2pt); z2=(4pt,0); x3=9pt; y3l=y2r;
+penstroke z1e..z2e{right}..z3e{right};
+labels.top(2r,3r);
+labels.bot(2l,3l);
+labels.rt(3,1r);
+labels.lft(1l,2);
+makelabel.top("1 ",z1);
+showit;
+shipit;
+
+"Figure 4i"; % cyclic penstroke example
+clear_all;
+d:=25;
+penpos1(d,30);
+penpos2(d,60);
+penpos3(d,210);
+penpos4(d,240);
+y1=.51y2r; y2r=175; y3=.49y2r; y4r=0;
+x1r=175; x2=x4=.5x1r; x3r=0;
+penstroke z1e{up}..z2e{left}..z3e{down}..z4e{right}..cycle;
+labels.lft(1l,2,3r,4);
+labels.rt(1r,3l);
+labels.top(2r,3,4l);
+labels.bot(1,2l,4r);
+showit;
+shipit;
+
+"Figure 4j"; % I from IOnian font
+clear_all;
+em#:=10pt#; cap#:=7pt#;
+bar#:=.8pt#;
+stem#:=.9pt#;
+thin#:=.8pt#;
+thick#:=1pt#;
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick,bar,stem);
+beginchar(27,.35em#,cap#,0); % "The letter I"; \IOI=27
+ penpos1(stem,15); penpos2(.9stem,12); penpos3(stem,10);
+ x1=x2=x3=.5w; y1=h; y2=.55h; y3=0;
+ x2l:=1/6[x2l,x2];
+ penstroke z1e..z2e{down}..z3e;
+labels.top(1,2,1r);
+labels.lft(2l);
+labels.rt(2r);
+labels.bot(3,3l);
+makelabel("",z1l); makelabel("",z3r);
+makelabel.lft.nodot("1l",z1l-(0,2));
+makelabel.rt.nodot("3r",z3r+(0,2));
+endchar;
+
+"Figure 4k"; % fixed pens versus penstrokes
+clear_all;
+z1=(0,0); z2=(150,0);
+pickup pencircle xscaled 0.8pt rotated 25;
+draw z1..z2..cycle;
+labels.lft(1,2);
+clearxy;
+pair offset; offset=(250,0);
+penpos1(0.8pt,25); penpos2(0.8pt,25);
+z1=(0,0)+offset; z2=(150,0)+offset;
+penstroke z1e..z2e..cycle;
+labels.lft(1l,2l);
+labels.rt(1r,2r);
+labels.bot(1,2);
+showit;
+shipit;
+
+"Figure 4l"; % hex symbol with pen-pressure adjustments
+%b#:=1.0pt#; define_blacker_pixels(b); % FOR INITIAL TESTS ONLY
+%beginchar(0,500/36pt#,250/36pt#,0); % that's magnified 2.5 as in the type
+b#:=0.4pt#; define_blacker_pixels(b);
+beginchar(0,200/36pt#,100/36pt#,0);
+pickup pencircle scaled b;
+w:=2good.x .5w;
+top z1=(0,h); top z2=(.5w,h); top z3=(w,h);
+bot z4=(0,0); bot z5=(.5w,0); bot z6=(w,0);
+draw z2..z5;
+for k=1 upto 6: drawdot z[k]; endfor
+z1'=.25[z1,z6]; z6'=.75[z1,z6];
+z3'=.25[z3,z4]; z4'=.75[z3,z4];
+ theta1:=angle(z6-z1)+90;
+ theta3:=angle(z4-z3)+90;
+penpos1'(b,theta1); penpos6'(b,theta1); penpos7(.6b,theta1);
+penpos3'(b,theta3); penpos4'(b,theta3); penpos8(.6b,theta3);
+z7=z8=.5[z1,z6];
+draw z1..z1'; draw z6'..z6;
+ penstroke z1'e{z6'-z1'}..z7e..{z6'-z1'}z6'e;
+draw z3..z3'; draw z4'..z4;
+ penstroke z3'e{z4'-z3'}..z8e..{z4'-z3'}z4'e;
+labels.top(1'r,3'l,4'l,6'r);
+labels.bot(1'l,3'r,4'r,6'l);
+forsuffixes $=1',3',4',6': makelabel("",z$); endfor
+makelabel.lft("1 ",z1);
+makelabel.lft("2 ",z2);
+makelabel.rt(" 3",z3);
+makelabel.lft("4 ",z4);
+makelabel.lft("5 ",z5);
+makelabel.rt(" 6",z6);
+makelabel.top("8l ",z8l);
+makelabel.top(" 7r",z7r);
+makelabel.bot("7l ",z7l);
+makelabel.bot(" 8r",z8r);
+endchar;
+
+"Figure 4m"; % same, cut sharp at the terminals, and inside the box
+%b#:=1.0pt#; define_blacker_pixels(b); % FOR INITIAL TESTS ONLY
+%beginchar(0,500/36pt#,250/36pt#,0); % that's magnified 2.5 as in the type
+b#:=0.4pt#; define_blacker_pixels(b);
+beginchar(0,200/36pt#,100/36pt#,0);
+pickup pencircle scaled b;
+w:=2good.x .5w;
+x1l=x4l=0;x2=x5=.5w;x3r=x6r=w;
+y1r=y2=y3l=h; y4r=y5=y6l=0;
+z1'=.25[z1,z6]; z6'=.75[z1,z6]; theta1:=angle(w,-h)+90;
+z3'=.25[z3,z4]; z4'=.75[z3,z4]; theta3:=angle(-w,-h)+90;
+z7=z8=.5[z1,z6];
+penpos1(b,theta1); penpos6(b,theta1);
+penpos1'(b,theta1); penpos6'(b,theta1); penpos7(.6b,theta1);
+penpos3(b,theta3); penpos4(b,theta3);
+penpos3'(b,theta3); penpos4'(b,theta3); penpos8(.6b,theta3);
+penstroke z1e..z1'e{z6'-z1'}..z7e..{z6'-z1'}z6'e..z6e;
+penpos2(b,0); penpos5(b,0); penstroke z2e..z5e;
+penstroke z3e..z3'e{z4'-z3'}..z8e..{z4'-z3'}z4'e..z4e;
+penlabels(1,1',3,3',4,4',6,6');
+labels.top(1r,1'r,2,3l,3'l,4l,4'l,6r,6'r);
+labels.bot(1l,1'l,3r,3'r,4r,4'r,5,6l,6'l);
+labels.lft(2l,5l);
+labels.rt(2r,5r);
+forsuffixes $=1,1',3,3',4,4',6,6': makelabel("",z$); endfor
+makelabel.top("8l ",z8l);
+makelabel.top(" 7r",z7r);
+makelabel.bot("7l ",z7l);
+makelabel.bot(" 8r",z8r);
+endchar;
+
+"Figure A5a"; % S from IOnian font
+clear_all;
+em#:=10pt#; cap#:=7pt#;
+slab#:=.8pt#;
+stem#:=.9pt#;
+thin#:=.8pt#;
+thick#:=5/6pt#;
+ o#:=1/5pt#;
+define_corrected_pixels(o);
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick,slab,stem);
+beginchar(25,5/9em#,cap#,0); % "The letter S"; /IOS=25
+penpos1(slab,70);
+penpos2(.5slab,80);
+penpos3(.5[slab,thick],200);
+penpos5(.5[slab,thick],210);
+penpos6(.7slab,80);
+penpos7(.25[slab,thick],72);
+x1=x5; y1r=.94h+o;
+x2=x4=x6=.5w; y2r=h+o; y4=.54h; y6l=-o;
+x3r=.04em; y3=.5[y4,y2];
+x5l=w-.03em; y5=.5[y4,y6];
+.5[x7l,x7]=.04em; y7l=.12h-o;
+path trial; trial=z3{down}..z4..{down}z5;
+pair dz; dz=direction 1 of trial;
+penpos4(thick,angle dz-90);
+penstroke z1e..z2e{left}..z3e{down}..z4e{dz}..z5e{down}..z6e{left}..z7e;
+labels.lft(2,3r,5r,6,7);
+labels.rt(1,3l,4,5l);
+labels.top(1r,2r,3,4l,5,6r,7r);
+labels.bot(1l,2l,4r,6l,7l);
+endchar;
+
+"Figure 8a"; % twenty points on p2
+clear_all;
+path p[]; p1=(0,0)..(3,3); p2=(0,0)..(3,3)..cycle;
+transform t; t=identity scaled 60 shifted (50,0);
+pair zz;
+z=(1.5,1.5) transformed t;
+pickup pencircle;
+draw p2 transformed t;
+for n=0 upto 19: zz:=point n/10 of p2 transformed t;
+ heavy_dot zz;
+ makelabel.top.nodot(decimal(n/10) if n mod 10=0:&".0" fi,
+.09[zz,z] shifted(.05xpart(z-zz),-6.5)); endfor
+%.1[zz,z] shifted(.1xpart(z-zz),-8)); endfor
+shipit;
+showit;
+
+"Figure 11a"; % E of METAFONT logo
+alpha:=.45; % controls bar location and similar things
+beta:=.2; % controls squareness of bowls
+numeric h#, xx#, yy#, o#, s#, u#, ph#, pv#;
+numeric leftstemloc, barheight;
+ h#=6; % height of characters, in pt
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ ph#=2/3; % horizontal thickness of pen, in pt
+yy#=xx#; define_whole_pixels(xx,yy);
+define_pixels(s,u);
+define_corrected_pixels(o);
+pv#=.9ph#; define_blacker_pixels(ph,pv);
+pickup pencircle xscaled ph yscaled pv;
+logo_pen:=savepen;
+leftstemloc:=good.x((2.5u#+s#)*pt);
+barheight:=good.y(alpha*h#*pt);
+def beginlogochar(suffix c)(expr n) = % code c; width is n units plus sidebars
+ beginchar(str c,n*u#+2s#,h#,0); pickup logo_pen enddef;
+
+beginlogochar(E,14);
+x1=x2=x3=leftstemloc; x4=x6=w-x1+o; x5=x4-xx;
+y1=y6; y2=y5; y3=y4; bot.y1=0; top.y3=h; y2=barheight;
+draw z6--z1--z3--z4; draw z2..z5;
+labels.rt(1,2,3);
+labels.lft(4,5,6);
+endchar;
+
+"Figure 12a"; % cmr10 left parenthesis
+u#:=20/36pt#;
+tiny#:=0;
+axis#:=90/36pt#;
+hheight#:=250/36pt#;
+border#:=20/36pt#;
+hair#:=8/36pt#;
+thin#:=9/36pt#;
+thick#:=25/36pt#;
+slant:=0;
+rulethickness:=.4pt#;
+
+font_setup;
+beginchar("(",7u#, hheight#+border#, hheight#+border#-2axis#);
+italcorr (hheight#+border#)*slant-.5u#;
+pickup tinypen;
+penpos1(hair-tiny,0); penpos3(hair-tiny,0);
+penpos2(.75[thin,thick]-tiny,0);
+rt x1r = rt x3r = w-u; lft x2l = x1-4u;
+top y1=h; y2=.5[y1,y3]=axis;
+filldraw z1l{curl 4}..z2l..{curl 4}z3l--
+ z3r{curl 4}..z2r..{curl 4}z1r--cycle;
+labels.top(3,2);
+labels.bot(1);
+labels.lft(1l,2l,3l);
+labels.rt(1r,2r,3r);
+endchar;
+
+"Figure 12b"; % cmbx10 left parenthesis
+u#:=23/36pt#;
+tiny#:=0;
+axis#:=90/36pt#;
+hheight#:=250/36pt#;
+border#:=20/36pt#;
+hair#:=13/36pt#;
+thin#:=17/36pt#;
+thick#:=41/36pt#;
+slant:=0;
+
+font_setup;
+beginchar("(",7u#, hheight#+border#, hheight#+border#-2axis#);
+italcorr (hheight#+border#)*slant-.5u#;
+pickup tinypen;
+penpos1(hair-tiny,0); penpos3(hair-tiny,0);
+penpos2(.75[thin,thick]-tiny,0);
+rt x1r = rt x3r = w-u; lft x2l = x1-4u;
+top y1=h; y2=.5[y1,y3]=axis;
+filldraw z1l{curl 4}..z2l..{curl 4}z3l--
+ z3r{curl 4}..z2r..{curl 4}z1r--cycle;
+labels.top(3,2);
+labels.bot(1);
+labels.lft(1l,2l,3l);
+labels.rt(1r,2r,3r);
+endchar;
+
+"Figure 12c"; % cmvtt10 left parenthesis
+u#:=21/36pt#;
+tiny#:=22/36pt#;
+axis#:=110/36pt#;
+hheight#:=220/36pt#;
+border#:=30/36pt#;
+hair#:=22/36pt#;
+thin#:=25/36pt#;
+thick#:=25/36pt#;
+slant:=0;
+
+font_setup;
+beginchar("(",7u#, hheight#+border#, hheight#+border#-2axis#);
+italcorr (hheight#+border#)*slant-.5u#;
+pickup tinypen;
+penpos1(hair-tiny,0); penpos3(hair-tiny,0);
+penpos2(.75[thin,thick]-tiny,0);
+rt x1r = rt x3r = w-u; lft x2l = x1-4u;
+top y1=h; y2=.5[y1,y3]=axis;
+filldraw z1l{curl 4}..z2l..{curl 4}z3l--
+ z3r{curl 4}..z2r..{curl 4}z1r--cycle;
+labels.top(3,2);
+labels.bot(1);
+labels.lft(1l,2l,3l);
+labels.rt(1r,2r,3r);
+endchar;
+
+"Figure 12d"; % cmssdc10 left parenthesis
+u#:=19/36pt#;
+tiny#:=8/36pt#;
+axis#:=95/36pt#;
+hheight#:=250/36pt#;
+border#:=20/36pt#;
+hair#:=23/36pt#;
+thin#:=40/36pt#;
+thick#:=40/36pt#;
+slant:=0;
+
+font_setup;
+beginchar("(",7u#, hheight#+border#, hheight#+border#-2axis#);
+italcorr (hheight#+border#)*slant-.5u#;
+pickup tinypen;
+penpos1(hair-tiny,0); penpos3(hair-tiny,0);
+penpos2(.75[thin,thick]-tiny,0);
+rt x1r = rt x3r = w-u; lft x2l = x1-4u;
+top y1=h; y2=.5[y1,y3]=axis;
+filldraw z1l{curl 4}..z2l..{curl 4}z3l--
+ z3r{curl 4}..z2r..{curl 4}z1r--cycle;
+labels.top(3,2);
+labels.bot(1);
+labels.lft(1l,2l,3l);
+labels.rt(1r,2r,3r);
+endchar;
+
+"Figure 12e"; % cmti10 left parenthesis
+u#:=18.4/36pt#;
+tiny#:=7/36;
+axis#:=90/36pt#;
+hheight#:=250/36pt#;
+border#:=20/36pt#;
+hair#:=8/36pt#;
+thin#:=11/36pt#;
+thick#:=23/36pt#;
+slant:=.25;
+
+font_setup;
+beginchar("(",7u#, hheight#+border#, hheight#+border#-2axis#);
+italcorr (hheight#+border#)*slant-.5u#;
+pickup tinypen;
+penpos1(hair-tiny,0); penpos3(hair-tiny,0);
+penpos2(.75[thin,thick]-tiny,0);
+rt x1r = rt x3r = w-u; lft x2l = x1-4u;
+top y1=h; y2=.5[y1,y3]=axis;
+filldraw z1l{curl 4}..z2l..{curl 4}z3l--
+ z3r{curl 4}..z2r..{curl 4}z1r--cycle;
+labels.top(3,2);
+labels.bot(1);
+labels.lft(1l,2l,3l);
+labels.rt(1r,2r,3r);
+endchar;
+currenttransform:=identity yscaled aspect_ratio;
+
+"Figure 12f"; % Dangerous bend symbol
+def font_setup=
+define_pixels(u,tiny,axis,hheight,border);
+define_blacker_pixels(hair,thin,thick,rulethickness);
+pickup if tiny<.5: nullpen else: pencircle scaled tiny fi;
+tinypen:=savepen;
+currenttransform:=identity slanted slant;
+enddef;
+
+% typical cmr10 parameters
+u#:=20/36pt#;
+tiny#:=0;
+axis#:=90/36pt#;
+hheight#:=250/36pt#;
+border#:=20/36pt#;
+hair#:=8/36pt#;
+thin#:=9/36pt#;
+thick#:=25/36pt#;
+fixwidth:=0;
+slant:=0;
+rulethickness#:=.4pt#;
+
+baselinedistance#:=11pt#;
+heavyline#:=50/36pt#;
+font_setup;
+define_whole_pixels(baselinedistance);
+define_blacker_pixels(heavyline);
+
+beginchar(127,25u#,hheight#+border#,0);
+pickup pencircle scaled rulethickness;
+top y1=25/27h; lft x4=0;
+x1+x1=x1a+x1b=x4b+x2a=x4+x2=x4a+x2b=x3b+x3a=x3+x3=w;
+x4a=x4b=x4+u; x3b=x1a=x1-2u;
+y4+y4=y4a+y4b=y3b+y1a=y3+y1=y3a+y1b=y2b+y2a=y2+y2=0;
+y1a=y1b=y1-2/27h; y4b=y2a=y4+4/27h;
+draw z1a..z1..z1b---z2a..z2..z2b---
+ z3a..z3..z3b---z4a..z4..z4b---cycle; % signboard
+x10=x11=x12=x13=good.x(.5w-u); x14=x15=x16=x17=w-x10;
+y10=y14=28/27h+epsilon; bot y13=-baselinedistance;
+z11=(z10..z13) intersectionpoint (z1a{z1a-z4b}..z1{right});
+y15=y11; y16=y12=-y11; y17=y20=y21=y13;
+draw z11--z10--z14--z15; draw z12--z13; draw z16--z17; % signpost
+x20=w-x21; x21-x20=16u; draw z20--z21; % ground level
+x36=w-x31; x36-x31=8u; x32=x33=x36; x31=x34=x35;
+y31=-y36=12/27h; y32=-y35=9/27h; y33=-y34=3/27h;
+pickup pencircle scaled heavyline;
+draw z32{z32-z31}..z33---z34..z35{z36-z35}; % the dangerous bend
+pickup penrazor xscaled heavyline rotated (angle(z32-z31)+90);
+draw z31--z32; draw z35--z36; % upper and lower bars
+labels.top(11,15,2a,4b,4,2,32,34);
+labels.bot(10,14,2b,4a,33,35);
+labels.lft(1a,3b,12,13,21,36);
+labels.rt(1,1b,3a,3,16,20,17,31);
+endchar;
+
+"Figure 13a"; % Possible cube
+clear_all;
+fudge_factor:=1/2;
+s#:=5pt#*fudge_factor; define_pixels(s); % side of the square
+z1=(-epsilon,-epsilon); z2=(s+epsilon,-epsilon);
+ z3=(-epsilon,s+epsilon); z4=(s+epsilon,s+epsilon);
+for k=1 upto 4: z[k+4]=z[k]+(2/3s,1/3s); endfor
+pickup pencircle scaled (.4pt*fudge_factor);
+draw z5--z6--z8--z7--cycle;
+pickup pencircle scaled (1.6pt*fudge_factor);
+erase draw z2--z4--z3;
+pickup pencircle scaled (.4pt*fudge_factor);
+draw z1--z2--z4--z3--cycle;
+for k=1 upto 4: draw z[k]--z[k+4]; endfor
+labels.top(1,2,5,6);
+labels.bot(3,4,7,8);
+showit;
+shipit;
+
+"Figure 14a"; % El Palo Alto
+begingroup save flex; clear_all;
+def flex(text t) = % puts dots into normal flex
+ hide(n_:=0;
+ for z=t: z_[incr n_]:=z; makelabel("",z); endfor
+ dz_:=z_[n_]-z_1)
+ z_1 for k=2 upto n_-1: ...z_[k]{dz_} endfor ...z_[n_] enddef;
+
+def grid= proofoffset (30,0);
+ for y:=0 step 50 until 650: proofrule((-110,y),(110,y));
+ makelabel.lft.nodot(str[y],(100,y+5));
+ endfor;
+ for x:=-100 step 50 until 100: proofrule((x,-10),(x,660));endfor enddef;
+
+path branch[],trunk;
+
+branch1=
+ flex((0,660),(-9,633),(-22,610))
+ &flex((-22,610),(-3,622),(17,617))
+ &flex((17,617),(7,637),(0,660))
+ &cycle;
+
+branch2=
+ flex((30,570),(10,590),(-1,616))
+ &flex((-1,616),(-11,592),(-29,576),(-32,562))
+ &flex((-32,562),(-10,577),(30,570))
+ &cycle;
+
+branch3=
+ flex((-1,570),(-17,550),(-40,535))
+ &flex((-40,535),(-45,510),(-60,477))
+ &flex((-60,477),(-20,505),(40,512))
+ &flex((40,512),(31,532),(8,550),(-1,570))
+ &cycle;
+
+branch4=
+ flex((0,509),(-14,492),(-32,481))
+ &flex((-32,481),(-42,455),(-62,430))
+ &flex((-62,430),(-20,450),(42,448))
+ &flex((42,448),(38,465),(4,493),(0,509))
+ &cycle;
+
+branch5=
+ flex((-22,470),(-23,435),(-44,410))
+ &flex((-44,410),(-10,421),(35,420))
+ &flex((35,420),(15,455),(-22,470))
+ &cycle;
+
+branch6=
+ flex((18,375),(9,396),(5,420))
+ &flex((5,420),(-5,410),(-50,375),(-50,350))
+ &flex((-50,350),(-25,375),(18,375))
+ &cycle;
+
+branch7=
+ flex((0,400),(-13,373),(-30,350))
+ &flex((-30,350),(0,358),(30,350))
+ &flex((30,350),(13,373),(0,400))
+ &cycle;
+
+branch8=
+ flex((50,275),(45,310),(3,360))
+ &flex((3,360),(-20,330),(-70,300),(-100,266))
+ &flex((-100,266),(-75,278),(-60,266))
+ &flex((-60,266),(0,310),(50,275))
+ &cycle;
+
+branch9=
+ flex((10,333),(-15,290),(-43,256))
+ &flex((-43,256),(8,262),(58,245))
+ &flex((58,245),(34,275),(10,333))
+ &cycle;
+
+branch10=
+ flex((8,262),(-21,249),(-55,240))
+ &flex((-55,240),(-51,232),(-53,220))
+ &flex((-53,220),(-28,229),(27,235))
+ &flex((27,235),(16,246),(8,262))
+ &cycle;
+
+branch11=
+ flex((0,250),(-25,220),(-70,195))
+ &flex((-70,195),(-78,180),(-90,170))
+ &flex((-90,170),(-5,188),(74,183))
+ &flex((74,183),(34,214),(0,250))
+ &cycle;
+
+branch12=
+ flex((8,215),(-35,175),(-72,155))
+ &flex((-72,155),(-75,130),(-92,110),(-95,88))
+ &flex((-95,88),(-65,117),(-54,104))
+ &flex((-54,104),(10,151),(35,142))..flex((42,130),(60,123),(76,124))
+ &flex((76,124),(62,146),(26,180),(8,215))&cycle;
+
+trunk=(0,660)---(-12,70)..{curl 5}(-28,-8)
+ &flex((-28,-8),(-16,-4),(-10,-11))
+ &flex((-10,-11),(0,-5),(14,-10))
+ &flex((14,-10),(20,-6),(29,-11))
+ &(29,-11){curl 4}..(10,100)---cycle;
+
+makelabel("",(-12,70));
+makelabel("",(10,100));
+
+pickup pencircle scaled 2;
+draw trunk;
+for n=1 upto 12: draw branch[n]; endfor
+grid;
+showit;shipit;
+endgroup;
+
+"Figure 14b"; % Detail of one branch
+clear_all;
+pair zz[];
+zz1=(0,509);
+zz2=(-14,492);
+zz3=(-32,481);
+zz4=(-42,455);
+zz5=(-62,430);
+zz6=(-20,450);
+zz7=(42,448);
+zz8=(38,465);
+zz9=(4,493);
+for n=1 upto 9: z[n]=(zz[n]+(62,-430)) scaled 2.5; endfor
+pickup pencircle;
+filldraw flex(z1,z2,z3)&flex(z3,z4,z5)
+ &flex(z5,z6,z7)&flex(z7,z8,z9,z1)&cycle;
+labels.lft(2,3,4,5);
+labels.rt(1,7,8,9);
+labels.bot(6);
+showit; shipit;
+
+"Figure 14c"; % superellipses
+clear_all;
+w:=100; h:=150;
+pickup pencircle xscaled 0.7pt yscaled 0.2pt rotated 30;
+forsuffixes ss=65,70,75,80: s:=ss/100;
+draw superellipse((w,.5h),(.5w,h),(0,.5h),(.5w,0),s) shifted (150/.05(s-.65),0);
+makelabel.top.nodot("."&str ss,(.5w,.45h) shifted (150/.05(s-.65),0));
+endfor
+showit;
+shipit;
+
+"Figure 14d"; % demo of joinery
+clear_all;
+pickup standardpen;
+z0=(0,100); z1=(50,0); z2=(180,0);
+for n=3 upto 9: z[n]=z[n-3]+(200,0); endfor
+draw z0..z1---z2...{up}z3
+ & z3..z4--z5...{up}z6
+ & z6...z7---z8..{up}z9;
+makelabel.bot("0 ",z0);
+makelabel.bot("6 ",z6);
+labels.bot(3,9);
+labels.rt(1,4,7);
+labels.lft(2,5,8);
+showit;
+shipit;
+
+"Figure 14e"; % JofA logo
+beginchar("A",29mm#*.22,25mm#*.22,0);
+interim autorounding:=0; interim smoothing:=0;
+if odd(w-h): h:=h-1; fi
+thick#:=2mm#*.22; thin#:=5/4mm#*.22;
+define_whole_blacker_pixels(thick,thin);
+forsuffixes @=a,b,c: transform @;
+ forsuffixes e=l,r: path @e,@'e; numeric t@[]e; endfor endfor
+penpos1(thick,0); penpos2(thick,90); penpos3(thick,180); penpos4(thick,270);
+penpos5(thick,0); penpos6(thick,90); penpos7(thick,180); penpos8(thick,270);
+x2=x4=x6=x8=.5[x5,x7]=.5w; x1r=w; x3r=0; x5-x7=y6-y8;
+y1=y3=y5=y7=.5[y6,y8]=.5h; y2r=h; y4r=0; y6r=round.75h;
+forsuffixes e=l,r: a.e=b'e=c'e=superellipse(z1e,z2e,z3e,z4e,.75);
+ a'e=b.e=c.e=superellipse(z5e,z6e,z7e,z8e,.72); endfor
+penpos.a1(thin,0); penpos.a5(whatever,-90); penpos.a9(thin,180);
+x.a1l-x.a9l=1/3(x5l-x7l); x.a5=.5w; y.a1=y.a9; y.a5r=4/7h;
+x.a3l=x.a1l; x.a3r=x.a1r; x.a4r=1/6[x.a3r,x1l]; x0=.5w; y0=.52h;
+x.a6l+x.a4l=x.a6r+x.a4r=x.a7l+x.a3l=x.a7r+x.a3r=x.a9+x.a1=w;
+y.a3r=y.a4r=y.a6r=y.a7r=.2[y2l,y0]; y.a3l=y.a4l=y.a6l=y.a7l=y.a3r-thin;
+z.a4l=z.a4r+(thin,0)rotated(angle(z.a4r-z.a5r)+90)
+ +whatever*(z.a4r-z.a5r); z.a4l-z.a5l=whatever*(z.a4r-z.a5r);
+z=a.r intersectionpoint (z0--(w,0)); y.a1-y.a5=length(z-z0);
+b=identity shifted(0,y0-y.a1)rotatedaround(z0,90-angle(z0-(w,0)));
+c=b reflectedabout (z2,z4);
+for n=1,3,4,5,6,7,9: forsuffixes e=l,,r: forsuffixes @=b,c:
+ z@[n]e=z.a[n]e transformed @; endfor endfor endfor
+forsuffixes e=l,r: forsuffixes @=a,b,c:
+ z@2e=@r intersectionpoint (z@1e--z@3e);
+ z@8e=@r intersectionpoint (z@9e--z@7e);
+ t@1e=xpart(@e intersectiontimes (z@1l--z@3l));
+ t@9e=xpart(@e intersectiontimes (z@9l--z@7l));
+ t@4e=xpart(@'e intersectiontimes (z@5r--z@4l));
+ t@6e=xpart(@'e intersectiontimes (z@5r--z@6l)); endfor endfor
+penstroke subpath(t.a9e,t.b6e) of a.e;
+penstroke subpath(t.b4e,t.c4e) of b'e;
+penstroke subpath(t.c6e,t.a1e+8) of c'e;
+penstroke subpath(t.a6e,t.b9e) of a'e;
+penstroke subpath(t.b1e,t.c1e) of b.e;
+penstroke subpath(t.c9e,t.a4e+8) of c.e;
+forsuffixes @=a,b,c:
+ penstroke z@2e--z@3e--z@4e--z@5e--z@6e--z@7e--z@8e; endfor
+labels.lft(0,1l,3r,3,5l,7r,7,a1l,a2l,a3l,a6l,
+ a6r,a8r,a9r,b2l,b5,b5r,b9,c1,c2r,c5l);
+labels.rt(1,1r,3l,5,5r,7l,a1r,a2r,a4l,a4r,a7l,
+ a9l,b1,b2r,b5l,c2l,c5,c5r,c9);
+labels.top(2r,2,2l,4l,6r,6,6l,8l,a3r,a7r,a8l,
+ b1l,b3l,b3r,b6r,b6l,b8r,b9r,c1l,c3l,c3r,c6r,c6l,c8r,c9r);
+labels.bot(2r,2,2l,4,4r,8,8r,a1,a5l,a5,a5r,a9,
+ b1r,b4l,b4r,b7l,b7r,b8l,c1r,c4l,c4r,c7l,c7r,c8l);
+makelabel("",z.b9l);
+makelabel("",z.c9l);
+makelabel.lft.nodot("b9l",z.b9l-(0,3));
+makelabel.rt.nodot("c9l",z.c9l-(0,3));
+endchar;
+
+"Figure 15a"; % four u's
+beginchar("4",11pt#,11pt#,0);
+pickup pencircle scaled 3/4pt yscaled 1/3 rotated 30;
+save t; transform t;
+t=identity rotatedaround((.5w,.5h),-90);
+x2=.35w; x3=good.x .6w;
+y2=good.y .1h; top y3=round .4h+eps;
+save p; path p; p=z2{right}...{up}z3;
+top z1=point .5 of p transformed t;
+draw z1...z2{right}...{up}z3;
+addto currentpicture also currentpicture transformed t;
+addto currentpicture also currentpicture transformed(t transformed t);
+labels.bot(1,3); labels.rt(2); endchar;
+
+"Figure 16a"; % circular pen polygon
+clear_all;
+path p;
+p=makepath(pencircle scaled 20) scaled 10 shifted (110,110);
+if length p<>32: error; fi
+fill p;
+for k=1 upto length p: heavy_dot point k of p; endfor
+for x=0 step 10 until 220: proofrule((x,0),(x,220)); endfor
+for y=0 step 10 until 220: proofrule((0,y),(220,y)); endfor
+proofrulethickness .8;
+proofrule ((0,110),(220,110)); proofrule ((110,0),(110,220));
+makelabel.bot.nodot("ignore",(160,-10)); % kludge for GFtoEPS!
+showit;
+shipit;
+
+"Figure 16b"; % elliptical pen polygon
+clear_all;
+path p;
+p=makepath(pencircle xscaled 30 yscaled 20) scaled 10 shifted (160,110);
+if length p<>40: error; fi
+fill p;
+for k=1 upto length p: heavy_dot point k of p; endfor
+for x=0 step 10 until 320: proofrule((x,0),(x,220)); endfor
+for y=0 step 10 until 220: proofrule((0,y),(320,y)); endfor
+proofrulethickness .8;
+proofrule ((0,110),(320,110)); proofrule ((160,0),(160,220));
+makelabel.bot.nodot("ignore",(160,-10)); % kludge for GFtoEPS!
+showit;
+shipit;
+
+"Figure 16c"; % elliptical pen hexagon
+clear_all;
+path p;
+p=makepath((pencircle xscaled 30) yscaled 20) scaled 10 shifted (160,110);
+if length p<>6: error; fi
+fill p;
+for k=1 upto length p: heavy_dot point k of p; endfor
+for x=0 step 10 until 320: proofrule((x,0),(x,220)); endfor
+for y=0 step 10 until 220: proofrule((0,y),(320,y)); endfor
+proofrulethickness .8;
+proofrule ((0,110),(320,110)); proofrule ((160,0),(160,220));
+makelabel.bot.nodot("ignore",(160,-10)); % kludge for GFtoEPS!
+showit;
+shipit;
+
+"Figure 16d"; % cutoff version of Figure 3n
+clear_all;
+z1=(0,100); z2=(100,100); z3=(200,100);
+z4=(0,0); z5=(100,0); z6=(200,0);
+path p; p=z4..controls z1 and z2..z6;
+cutdraw p;
+makelabel.rt.nodot("1",z1+(2,0));
+makelabel.rt.nodot("2",z2+(2,0));
+makelabel.rt.nodot("3",z3+(2,0));
+makelabel.rt.nodot(" 4",z4);
+makelabel.rt.nodot("5",z5+(2,0));
+makelabel.rt.nodot(" 6",z6);
+for k=1 upto 6: heavy_dot z[k]; endfor;
+showit;
+shipit;
+
+"Figure 16e"; % cutoff version of Figure 4b
+alpha:=.45; % controls bar location and similar things
+beta:=.2; % controls squareness of bowls
+numeric h#, xx#, yy#, o#, s#, u#, ph#, pv#;
+numeric leftstemloc, barheight;
+ h#=6; % height of characters, in pt
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ ph#=2/3; % horizontal thickness of pen, in pt
+yy#=xx#; define_whole_pixels(xx,yy);
+define_pixels(s,u);
+define_corrected_pixels(o);
+pv#=.9ph#; define_blacker_pixels(ph,pv);
+pickup pencircle xscaled ph yscaled pv;
+logo_pen:=savepen;
+leftstemloc:=good.x((2.5u#+s#)*pt);
+barheight:=good.y(alpha*h#*pt);
+def beginlogochar(suffix c)(expr n) = % code c; width is n units plus sidebars
+ beginchar(str c,n*u#+2s#,h#,0); pickup logo_pen enddef;
+
+beginlogochar(T,13);
+if odd(w-ph): w:=w+1; fi % allows a symmetric stem
+x1+x2=2x3=2x4=w; lft.x1=0;
+y1=y2=y3; top.y1=h; y4=0;
+draw z1--z2;
+cutoff(z1,170);
+cutoff(z2,-10);
+draw z3--z4;
+cutoff(z4,-80);
+labels.rt(1);
+labels.lft(2);
+labels.bot(3);
+makelabel.top("4",z4);
+endchar;
+
+"Figure 16f"; % tilde with pencircle
+clear_all;
+pickup pencircle xscaled .8pt yscaled .2pt rotated 50;
+z1=(0,0); z4=(200,30);
+z2-z1=z4-z3=whatever*dir 50;
+y2-y1=3(y4-y1);
+draw z1..controls z2 and z3..z4;
+makelabel.lft("1 ",z1);
+makelabel.rt(" 4",z4);
+showit;
+shipit;
+
+"Figure 16g"; % tilde with pensquare
+clear_all;
+pickup pensquare xscaled .8pt yscaled .2pt rotated 50;
+z1=(0,0); z4=(200,30);
+z2-z1=z4-z3=whatever*dir 50;
+y2-y1=3(y4-y1);
+draw z1..controls z2 and z3..z4;
+makelabel.lft("1 ",z1);
+makelabel.rt(" 4",z4);
+showit;
+shipit;
+
+"Figure 16h"; % fancy serif with pencircle
+clear_all;
+pickup pencircle xscaled .8pt yscaled .2pt rotated 20;
+z1=(20,175); bot z3=(0,0);
+z4=(50,0); z6=(25,175);
+x2=x1; z2=z3+whatever*dir 20;
+x5=x6; z5=z4+whatever*dir -20;
+pair dishing; dishing=(8,0) rotated 20;
+filldraw z1..controls z2..z3
+--(flex(z3,.5[z3,z4]+dishing,z4))shifted(0,-epsilon)
+-- z4..controls z5..z6--cycle;
+showit;
+labels.lft(1,2,3);
+labels.rt(4,5,6);
+shipit;
+
+"Figure 16i"; % fancy serif with pensquare
+clear_all;
+pickup pensquare xscaled .8pt yscaled .2pt rotated 20;
+z1=(20,175); bot z3=(0,0);
+z4=(50,0); z6=(25,175);
+x2=x1; z2=z3+whatever*dir 20;
+x5=x6; z5=z4+whatever*dir -20;
+pair dishing; dishing=(8,0) rotated 20;
+filldraw z1..controls z2..z3
+--(flex(z3,.5[z3,z4]+dishing,z4))shifted(0,-epsilon)
+-- z4..controls z5..z6--cycle;
+showit;
+labels.lft(1,2,3);
+labels.rt(4,5,6);
+shipit;
+
+"Figure 18a"; % O of METAFONT logo
+alpha:=.45; % controls bar location and similar things
+beta:=.2; % controls squareness of bowls
+numeric h#, xx#, yy#, o#, s#, u#, ph#, pv#;
+numeric leftstemloc, barheight;
+ h#=6; % height of characters, in pt
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ ph#=2/3; % horizontal thickness of pen, in pt
+yy#=xx#; define_whole_pixels(xx,yy);
+define_pixels(s,u);
+define_corrected_pixels(o);
+pv#=.9ph#; define_blacker_pixels(ph,pv);
+pickup pencircle xscaled ph yscaled pv;
+logo_pen:=savepen;
+leftstemloc:=good.x((2.5u#+s#)*pt);
+barheight:=good.y(alpha*h#*pt);
+def beginlogochar(expr c)(expr n) = % code c; width is n units plus sidebars
+ beginchar(c,n*u#+2s#,h#,0); pickup logo_pen enddef;
+
+def super_half(suffix i,j,k) =
+ draw z.i{0,y.j-y.i}
+ ... (.8[x.j,x.i],.8[y.i,y.j]){z.j-z.i}
+ ... z.j{x.k-x.i,0}
+ ... (.8[x.j,x.k],.8[y.k,y.j]){z.k-z.j}
+ ... z.k{0,y.k-y.j} enddef;
+
+beginlogochar("O",15);
+x1=x4=.5w; top y1=h+o; bot y4=-o;
+x2=w-x3=good.x(1.5u+s);
+y2=y3=barheight;
+super_half(2,1,3); super_half(3,4,2);
+labels.lft(2); labels.rt(1,3,4); endchar;
+
+"Figure A18a"; % A of METAFONT logo
+alpha:=.45; % controls bar location and similar things
+beta:=.2; % controls squareness of bowls
+numeric h#, xx#, yy#, o#, s#, u#, ph#, pv#;
+numeric leftstemloc, barheight;
+ h#=6; % height of characters, in pt
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ ph#=2/3; % horizontal thickness of pen, in pt
+yy#=xx#; define_whole_pixels(xx,yy);
+define_pixels(s,u);
+define_corrected_pixels(o);
+pv#=.9ph#; define_blacker_pixels(ph,pv);
+pickup pencircle xscaled ph yscaled pv;
+logo_pen:=savepen;
+leftstemloc:=good.x((2.5u#+s#)*pt);
+barheight:=good.y(alpha*h#*pt);
+def beginlogochar(expr c)(expr n) = % code c; width is n units plus sidebars
+ beginchar(c,n*u#+2s#,h#,0); pickup logo_pen enddef;
+
+def super_half(suffix i,j,k) =
+ draw z.i{0,y.j-y.i}
+ ... (.8[x.j,x.i],.8[y.i,y.j]){z.j-z.i}
+ ... z.j{x.k-x.i,0}
+ ... (.8[x.j,x.k],.8[y.k,y.j]){z.k-z.j}
+ ... z.k{0,y.k-y.j} enddef;
+
+beginlogochar("A",15);
+x1=.5w;
+x2=x4=leftstemloc;
+x3=x5=w-x2;
+top y1=h+o;
+y2=y3=barheight;
+bot y4=bot y5=-o;
+draw z4--z2--z3--z5;
+super_half(2,1,3);
+labels.top(2,3,4,5);
+labels.rt(1);
+endchar;
+
+"Figure 18b"; % serif points
+clear_all;
+def serif(suffix $)(expr breadth,theta,left_jut,right_jut)=
+ penpos$(breadth/abs sind theta,0);
+z$a-z$l=z$f-z$r=(bracket/abs sind theta)*dir theta;
+y$c=y$d; y$b=y$e=y$; y$b-y$c=if theta<0:-fi slab;
+x$b=x$c=x$l-left_jut; x$d=x$e=x$r+right_jut;
+enddef;
+
+def serif_edge suffix $ =
+ (serif_bracket($a,$l,$b)--z$c
+ --z$d--reverse serif_bracket($f,$r,$e)) enddef;
+
+def serif_bracket(suffix i,j,k) =
+ (z.i{z.j-z.i}...serif_darkness[z.j,.5[z.i,z.k] ]{z.k-z.i}
+ ...z.k{z.k-z.j}) enddef;
+
+serif_darkness:=1/4; slab:=50; bracket:=175;
+z$=(150,slab);
+serif($,100,75,80,90);
+pickup pencircle scaled 2; draw serif_edge $;
+labels.lft($l,$a);
+labels.rt($,$r,$f);
+labels.top($b,$e);
+labels.bot($c,$d);
+showit;
+shipit;
+
+"Figure 18c"; % AI by outlines
+clear_all;
+def serif(suffix $)(expr breadth,theta,left_jut,right_jut)=
+ penpos$(breadth/abs sind theta,0);
+z$a-z$l=z$f-z$r=(bracket/abs sind theta)*dir theta;
+y$c=y$d; y$b=y$e=y$; y$b-y$c=if theta<0:-fi slab;
+x$b=x$c=x$l-left_jut; x$d=x$e=x$r+right_jut;
+enddef;
+
+def serif_edge suffix $ =
+ (serif_bracket($a,$l,$b)--z$c
+ --z$d--reverse serif_bracket($f,$r,$e)) enddef;
+
+def serif_bracket(suffix i,j,k) =
+ (z.i{z.j-z.i}...serif_darkness[z.j,.5[z.i,z.k] ]{z.k-z.i}
+ ...z.k{z.k-z.j}) enddef;
+
+serif_darkness:=1/3;
+slab#:=1/4pt#;
+bracket#:=1pt#;
+jut#:=.9pt#;
+u#:=3/5pt#;
+ht#:=7pt#;
+thin#:=.5pt#;
+thick#:=1.1pt#;
+define_pixels(slab,bracket,jut,u,ht,thin,thick);
+
+beginchar("A",13u#,ht#,0);
+z1=(.5w,round 1.05h); x4l=w-x5r=u; y4l=y5r=slab;
+numeric theta[]; theta4=angle(z1-z4l); theta5=angle(z1-z5r);
+serif(4,thin,theta4,.6jut,jut); serif(5,thick,theta5,jut,.6jut);
+z0=z4r+whatever*dir theta4=z5l+whatever*dir theta5;
+fill z1--serif_edge4--z0 & z0--serif_edge5--z1 & cycle;
+penpos2(whatever,theta4); penpos3(whatever,theta5);
+y2r=y3r=.5[y4,y0]; y2l=y3l=y2r-thin;
+z2=whatever[z1,z4r]; z3=whatever[z1,z5l];
+penstroke z2e--z3e;
+labels.lft(3,5l,5,4a,5a);
+labels.rt(2,4r,4f,5f);
+labels.top(1,2r,3r,4l,5r,4b,4e,5b,5e);
+labels.bot(0,2l,3l,4,4c,4d,5c,5d);
+
+ww:=w; % special hacks to get two on one
+picture tmp; tmp:=currentpicture;
+proofrule((w,-d.o_),(w,h.o_));
+endgroup;
+
+beginchar("I",6u#,ht#,0);
+currentpicture:=tmp;
+currenttransform:=currenttransform shifted(ww,0);
+x1=x2=.5w; y1=h-y2; y2=slab;
+serif(1,thick,-90,1.1jut,1.1jut); serif(2,thick,90,1.1jut,1.1jut);
+fill serif_edge 2 -- reverse serif_edge 1 -- cycle;
+w:=w+ww;
+labels.lft(1a,2a,1l);
+labels.rt(1f,2f,1,1r);
+labels.top(2l,2,2r,2e,1c,1d);
+labels.bot(2d,1b,1e);
+makelabel("",z2b); makelabel("",z2c);
+makelabel.rt.nodot("2b",z2b+(0,3));
+makelabel.rt.nodot("2c",z2c-(0,3));
+endchar;
+
+currenttransform:=identity yscaled aspect_ratio;
+
+"Figure A18b"; % H by outlines
+clear_all;
+def serif(suffix $)(expr breadth,theta,left_jut,right_jut)=
+ penpos$(breadth/abs sind theta,0);
+z$a-z$l=z$f-z$r=(bracket/abs sind theta)*dir theta;
+y$c=y$d; y$b=y$e=y$; y$b-y$c=if theta<0:-fi slab;
+x$b=x$c=x$l-left_jut; x$d=x$e=x$r+right_jut;
+enddef;
+
+def serif_edge suffix $ =
+ (serif_bracket($a,$l,$b)--z$c
+ --z$d--reverse serif_bracket($f,$r,$e)) enddef;
+
+def serif_bracket(suffix i,j,k) =
+ (z.i{z.j-z.i}...serif_darkness[z.j,.5[z.i,z.k] ]{z.k-z.i}
+ ...z.k{z.k-z.j}) enddef;
+
+serif_darkness:=1/3;
+slab#:=1/4pt#;
+bracket#:=1pt#;
+jut#:=.9pt#;
+u#:=3/5pt#;
+ht#:=7pt#;
+thin#:=.5pt#;
+thick#:=1.1pt#;
+define_pixels(slab,bracket,jut,u,ht,thin,thick);
+
+beginchar("H",13u#,ht#,0);
+x1=x2=x5=3u; x3=x4=x6=w-x1; y1c=y3c=h; y2c=y4c=0;
+serif(1,thick,-90,jut,jut);
+serif(2,thick,90,jut,jut);
+serif(3,thick,-90,jut,jut);
+serif(4,thick,90,jut,jut);
+fill serif_edge 2 -- reverse serif_edge 1 -- cycle;
+fill serif_edge 4 -- reverse serif_edge 3 -- cycle;
+penpos5(thin,90); penpos6(thin,90);
+y5=y6=.52h; penstroke z5e--z6e;
+labels.lft(1a,2a,3a,4a,5,1,2,1l,2l,3l,4l);
+labels.rt(1f,2f,3f,4f,6,3,4,1r,2r,3r,4r);
+labels.top(1c,1d,2b,2e,4b,4e,3c,3d,5r,6r);
+labels.bot(1b,1e,2c,2d,4c,4d,3b,3e,5l,6l);
+endchar;
+
+"Figure 18d"; % AI by broadpen
+clear_all;
+def bot_serif(suffix $)(expr xx,theta,left_jut,right_jut)=
+ penpos$(xx,0);
+z$a-z$l=z$f-z$r=(bracket/abs sind theta)*dir theta;
+y$c=top y$l; y$d=y$r; x$c=x$l-left_jut; x$d=x$r+right_jut;
+z$b=z$l+whatever*dir theta=z$c+whatever*dir phi;
+z$e=z$r+whatever*dir theta=z$d+whatever*dir -phi;
+enddef;
+
+def top_serif(suffix $)(expr xx,theta,left_jut,right_jut)=
+ penpos$(xx,0);
+z$a-z$l=z$f-z$r=(bracket/abs sind theta)*dir theta;
+%y$c=top y$l; y$d=y$r; x$c=x$l-left_jut; x$d=x$r+right_jut;
+y$c=y$d=y$; x$c=x$l-left_jut; x$d=x$r+right_jut;
+z$b=z$l+whatever*dir theta=z$c+whatever*dir -phi;
+z$e=z$r+whatever*dir theta=z$d+whatever*dir phi;
+enddef;
+
+def bot_serif_edge suffix $ =
+ (z$a..controls z$b..z$c
+ --(flex(z$c,.5[z$c,z$d]+dishing,z$d)) shifted (0,-epsilon)
+ -- z$d..controls z$e..z$f) enddef;
+
+def top_serif_edge suffix $ =
+ (z$a..controls z$b..z$c
+ --(flex(z$c,.5[z$c,z$d]-dishing,z$d)) shifted (0,+epsilon)
+ -- z$d..controls z$e..z$f) enddef;
+
+phi:=20;
+pair dishing; dishing:=(1/4pt,0) rotated phi;
+bracket#:=1pt#;
+jut#:=.9pt#;
+u#:=3/5pt#;
+ht#:=7pt#;
+px#:=.8pt#;
+py#:=.2pt#;
+xx#:=.3pt#;
+define_pixels(bracket,jut,u,ht,px,py,xx);
+
+pickup pensquare xscaled px yscaled py rotated phi; broad_pen:=savepen;
+
+beginchar("A",13u#,ht#,0); pickup broad_pen;
+z1=(.5w,top h); lft x4l=w-rt x5r=1.2u; y4l=y5r=0;
+numeric theta[]; theta4=angle(z1-z4l); theta5=angle(z1-z5r);
+numeric xxx;
+px*sind(theta5-phi)+xxx*sind theta5 = px*cosd phi+xx;
+bot_serif(4,0,theta4,.8jut,.8jut);
+bot_serif(5,xxx,theta5,.6jut,.8jut);
+z0=z4r+whatever*dir theta4=z5l+whatever*dir theta5;
+filldraw z1--bot_serif_edge4--z0 & z0--bot_serif_edge5--z1 & cycle;
+top y2=top y3=.45 bot y0; z2=whatever[z1,z4r]; z3=whatever[z1,z5l];
+draw z2--z3;
+labels.lft(4a,4b,4c,4,5l,5a,5c);
+labels.rt(4f,4e,5r,5d,5e,5f);
+labels.top(1,2,3,5b);
+labels.bot(0,4d,5);
+
+ww:=w; % special hacks to get two on one
+picture tmp; tmp:=currentpicture;
+proofrule((w,-d.o_),(w,h.o_));
+endgroup;
+
+beginchar("I",6u#,ht#,0); pickup broad_pen;
+currentpicture:=tmp;
+currenttransform:=currenttransform shifted(ww,0);
+x1=x2=.5w; y1=h; y2=0;
+top_serif(1,xx,-90,1.1jut,1.1jut);
+bot_serif(2,xx,90,1.1jut,1.1jut);
+filldraw bot_serif_edge 2 -- reverse top_serif_edge 1 -- cycle;
+labels.lft(1l,2l,1a,1b,1c,2a,2b);
+labels.rt(1r,2r,1d,1e,1f,2d,2e,2f);
+labels.top(1);
+labels.bot(2,2c);
+w:=w+ww;
+endchar;
+
+currenttransform:=identity yscaled aspect_ratio;
+
+"Figure A18c"; % H by broadpen
+clear_all;
+def bot_serif(suffix $)(expr xx,theta,left_jut,right_jut)=
+ penpos$(xx,0);
+z$a-z$l=z$f-z$r=(bracket/abs sind theta)*dir theta;
+y$c=top y$l; y$d=y$r; x$c=x$l-left_jut; x$d=x$r+right_jut;
+z$b=z$l+whatever*dir theta=z$c+whatever*dir phi;
+z$e=z$r+whatever*dir theta=z$d+whatever*dir -phi;
+enddef;
+
+def top_serif(suffix $)(expr xx,theta,left_jut,right_jut)=
+ penpos$(xx,0);
+z$a-z$l=z$f-z$r=(bracket/abs sind theta)*dir theta;
+y$c=y$d=y$; x$c=x$l-left_jut; x$d=x$r+right_jut;
+z$b=z$l+whatever*dir theta=z$c+whatever*dir -phi;
+z$e=z$r+whatever*dir theta=z$d+whatever*dir phi;
+enddef;
+
+def bot_serif_edge suffix $ =
+ (z$a..controls z$b..z$c
+ --(flex(z$c,.5[z$c,z$d]+dishing,z$d))shifted (0,-epsilon)
+ -- z$d..controls z$e..z$f) enddef;
+
+def top_serif_edge suffix $ =
+ (z$a..controls z$b..z$c
+ --(flex(z$c,.5[z$c,z$d]-dishing,z$d))shifted (0,+epsilon)
+ -- z$d..controls z$e..z$f) enddef;
+
+phi:=20;
+pair dishing; dishing:=(1/4pt,0) rotated phi;
+bracket#:=1pt#;
+jut#:=.9pt#;
+u#:=3/5pt#;
+ht#:=7pt#;
+px#:=.8pt#;
+py#:=.2pt#;
+xx#:=.3pt#;
+define_pixels(bracket,jut,u,ht,px,py,xx);
+
+pickup pensquare xscaled px yscaled py rotated phi; broad_pen:=savepen;
+
+beginchar("H",13u#,ht#,0); pickup broad_pen;
+x1=x2=x5=3u; x3=x4=x6=w-x1; y1=y3=h; y2=y4=0;
+top_serif(1,xx,-90,jut,jut);
+bot_serif(2,xx,90,jut,jut);
+top_serif(3,xx,-90,jut,jut);
+bot_serif(4,xx,90,jut,jut);
+filldraw bot_serif_edge 2 -- reverse top_serif_edge 1 -- cycle;
+filldraw bot_serif_edge 4 -- reverse top_serif_edge 3 -- cycle;
+y5=y6=.52h; draw z5--z6;
+labels.lft(l1,2l,3l,4l,1a,1b,1c,2a,2b,2c,3a,3b,3c,4a,4b,4c);
+labels.rt(1r,2r,3r,4r,1d,1e,1f,2d,2e,2f,3d,3e,3f,4d,4e,4f);
+labels.top(1,3);
+labels.bot(2,4,5,6);
+endchar;
+
+currenttransform:=identity yscaled aspect_ratio;
+
+"Figure 20a"; % example of `solve'
+clear_all;
+z1=(0,25); z2=(100,100); z3=(300,100); z4=(320,0);
+vardef upward(expr x)=
+ ypart direction 1 of (z1{z2-z1}..(x,y2)..{z4-z3}z4)>0 enddef;
+z=(solve upward(x2,x3),y2);
+pickup pencircle scaled 4;
+draw z1{z2-z1}..z..{z4-z3}z4;
+pickup pencircle scaled 2;
+draw z1--z2; draw z3--z4;
+makelabel.lft("z1 ",z1);
+makelabel.lft("z2 ",z2);
+makelabel.rt(" z3",z3);
+makelabel.rt(" z4",z4);
+makelabel.top.nodot("z",z+(0,2));
+forsuffixes $=1,2,3,4,: heavy_dot z$; endfor
+showit;
+shipit;
+
+"Figure 21a"; % N of METAFONT logo
+alpha:=.45; % controls bar location and similar things
+beta:=.2; % controls squareness of bowls
+numeric h#, xx#, yy#, o#, s#, u#, ph#, pv#;
+numeric leftstemloc, barheight;
+ h#=6; % height of characters, in pt
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ ph#=2/3; % horizontal thickness of pen, in pt
+yy#=xx#; define_whole_pixels(xx,yy);
+define_pixels(s,u);
+define_corrected_pixels(o);
+pv#=.9ph#; define_blacker_pixels(ph,pv);
+pickup pencircle xscaled ph yscaled pv;
+logo_pen:=savepen;
+leftstemloc:=good.x((2.5u#+s#)*pt);
+barheight:=good.y(alpha*h#*pt);
+def beginlogochar(expr c)(expr n) = % code c; width is n units plus sidebars
+ beginchar(c,n*u#+2s#,h#,0); pickup logo_pen enddef;
+
+beginlogochar("N",15);
+x1+x4=x1+x3=x2+x5=w; x1=x2=leftstemloc;
+y1=y4=y3-yy; bot.y1=-o; y2=y5; top.y5=h+o;
+draw z1--z2--z3; draw z4..z5;
+labels.top(1,3,4);
+labels.bot(2,5);
+endchar;
+
+"Figure 21b"; % random N of METAFONT logo, nine-point size
+randomseed:=1;
+
+vardef noise=normaldeviate*.1pt enddef;
+
+ht#:=.9*6pt#;
+xgap#:=.9*0.6pt#;
+u#:=.91*4/9pt#;
+s#:=.08pt#;
+o#:=1/10pt#;
+px#:=.9*2/3pt#;
+ygap#:=(ht#/13.5u#)*xgap#;
+leftstemloc#:=2.5u#+s#;
+barheight#:=.45ht#;
+define_pixels(s,u,xgap,ygap,leftstemloc,barheight);
+py#:=.9px#; define_blacker_pixels(px,py);
+pickup pencircle xscaled px yscaled py; logo_pen:=savepen;
+define_corrected_pixels(o);
+
+def beginlogochar(expr c,n) =
+ beginchar(c,n*u#+2s#,ht#,0); pickup logo_pen enddef;
+
+beginlogochar("N",15);
+x1=leftstemloc+noise;
+x2=leftstemloc+noise;
+x4=w-leftstemloc+noise;
+x5=w-leftstemloc+noise;
+bot y1=noise-o;
+top y2=h+o+noise;
+y3=y4+ygap+noise;
+bot y4=noise-o;
+top y5=h+o+noise;
+z3=whatever[z4,z5];
+draw z1--z2--z3; draw z4--z5;
+labels.top(1,3,4);
+labels.bot(2,5);
+endchar;
+
+"Figure 21c"; % random N of METAFONT logo, nine-point size
+randomseed:=2;
+
+vardef noise=normaldeviate*.1pt enddef;
+
+ht#:=.9*6pt#;
+xgap#:=.9*0.6pt#;
+u#:=.91*4/9pt#;
+s#:=.08pt#;
+o#:=1/10pt#;
+px#:=.9*2/3pt#;
+ygap#:=(ht#/13.5u#)*xgap#;
+leftstemloc#:=2.5u#+s#;
+barheight#:=.45ht#;
+define_pixels(s,u,xgap,ygap,leftstemloc,barheight);
+py#:=.9px#; define_blacker_pixels(px,py);
+pickup pencircle xscaled px yscaled py; logo_pen:=savepen;
+define_corrected_pixels(o);
+
+def beginlogochar(expr c,n) =
+ beginchar(c,n*u#+2s#,ht#,0); pickup logo_pen enddef;
+
+beginlogochar("N",15);
+x1=leftstemloc+noise;
+x2=leftstemloc+noise;
+x4=w-leftstemloc+noise;
+x5=w-leftstemloc+noise;
+bot y1=noise-o;
+top y2=h+o+noise;
+y3=y4+ygap+noise;
+bot y4=noise-o;
+top y5=h+o+noise;
+z3=whatever[z4,z5];
+draw z1--z2--z3; draw z4--z5;
+labels.top(1,3,4);
+labels.bot(2,5);
+endchar;
+
+"Figure 21d"; % random N of METAFONT logo, nine-point size
+randomseed:=3;
+
+vardef noise=normaldeviate*.1pt enddef;
+
+ht#:=.9*6pt#;
+xgap#:=.9*0.6pt#;
+u#:=.91*4/9pt#;
+s#:=.08pt#;
+o#:=1/10pt#;
+px#:=.9*2/3pt#;
+ygap#:=(ht#/13.5u#)*xgap#;
+leftstemloc#:=2.5u#+s#;
+barheight#:=.45ht#;
+define_pixels(s,u,xgap,ygap,leftstemloc,barheight);
+py#:=.9px#; define_blacker_pixels(px,py);
+pickup pencircle xscaled px yscaled py; logo_pen:=savepen;
+define_corrected_pixels(o);
+
+def beginlogochar(expr c,n) =
+ beginchar(c,n*u#+2s#,ht#,0); pickup logo_pen enddef;
+
+beginlogochar("N",15);
+x1=leftstemloc+noise;
+x2=leftstemloc+noise;
+x4=w-leftstemloc+noise;
+x5=w-leftstemloc+noise;
+bot y1=noise-o;
+top y2=h+o+noise;
+y3=y4+ygap+noise;
+bot y4=noise-o;
+top y5=h+o+noise;
+z3=whatever[z4,z5];
+draw z1--z2--z3; draw z4--z5;
+labels.top(1,3,4);
+labels.bot(2,5);
+endchar;
+
+"Figure 24a"; % Ionian O at 200 pixels/inch
+ss:=10;
+begingroup interim hppp:=200/72.27; % lowres parameters
+interim blacker:=.65; % make pens a bit blacker
+interim o_correction:=.4; % but don't overshoot as much
+ em#:=10pt#; cap#:=7pt#;
+ thin#:=1/3pt#; thick#:=5/6pt#;
+ o#:=1/5pt#;
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick);
+define_corrected_pixels(o);
+ curve_sidebar:=round 1/18em;
+pair u,uu,v;
+beginchar("O",0.8em#,cap#,0);
+ penpos1(thick,10); penpos2(.1[thin,thick],90-10);
+ penpos3(thick,180+10); penpos4(thin,270-10);
+ x1l=w-x3l=curve_sidebar; x2=x4=.5w;
+ y1=.49h; y2l=-o; y3=.51h; y4l=h+o;
+ penstroke (z1e{down}..z2e{right}
+ ..z3e{up}..z4e{left}..cycle) scaled ss;
+for m=0 upto w: proofrule((m*ss,0),(m*ss,h*ss)); endfor
+for n=0 upto h: proofrule((0,n*ss),(w*ss,n*ss)); endfor
+proofrulethickness 1;
+forsuffixes e=l,r:
+ u:=round((point 0 of path_.e)/ss);
+ for t=0 step 1/64 until 4:
+ v:=round((point t of path_.e)/ss);
+ if u<>v: if abs(u-v)=1: proofrule(u*ss,v*ss);
+ else: del:=1/128; tt:=t-del;
+ forever: uu:=round((point tt of path_.e)/ss);
+ exitif abs(u-uu)=1;
+ if del=epsilon:ddt;fi
+ del:=del/2;
+ tt:=tt if uu=u:+ else:-fi del; endfor
+ proofrule(u*ss,uu*ss); proofrule(uu*ss,v*ss); fi
+ u:=v; fi endfor
+ endfor
+showit;
+shipit;
+endgroup;
+endgroup;
+numeric u,uu,v;
+
+"Figure 24b"; % Ionian O at 200 pixels/inch, unrounded position
+ss:=10;
+begingroup interim hppp:=200/72.27; % lowres parameters
+interim blacker:=.65; % make pens a bit blacker
+interim o_correction:=.4; % but don't overshoot as much
+ em#:=10pt#; cap#:=7pt#;
+ thin#:=1/3pt#; thick#:=5/6pt#;
+ o#:=1/5pt#;
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick);
+define_corrected_pixels(o);
+ curve_sidebar:= 1/18em;
+pair u,uu,v;
+beginchar("O",0.8em#,cap#,0);
+ penpos1(thick,10); penpos2(.1[thin,thick],90-10);
+ penpos3(thick,180+10); penpos4(thin,270-10);
+ x1l=w-x3l=curve_sidebar; x2=x4=.5w;
+ y1=.49h; y2l=-o; y3=.51h; y4l=h+o;
+ penstroke (z1e{down}..z2e{right}
+ ..z3e{up}..z4e{left}..cycle) scaled ss;
+for m=0 upto w: proofrule((m*ss,0),(m*ss,h*ss)); endfor
+for n=0 upto h: proofrule((0,n*ss),(w*ss,n*ss)); endfor
+proofrulethickness 1;
+forsuffixes e=l,r:
+ u:=round((point 0 of path_.e)/ss);
+ for t=0 step 1/64 until 4:
+ v:=round((point t of path_.e)/ss);
+ if u<>v: if abs(u-v)=1: proofrule(u*ss,v*ss);
+ else: del:=1/128; tt:=t-del;
+ forever: uu:=round((point tt of path_.e)/ss);
+ exitif abs(u-uu)=1;
+ if del=epsilon:ddt;fi
+ del:=del/2;
+ tt:=tt if uu=u:+ else:-fi del; endfor
+ proofrule(u*ss,uu*ss); proofrule(uu*ss,v*ss); fi
+ u:=v; fi endfor
+ endfor
+showit;
+shipit;
+endgroup;
+endgroup;
+numeric u,uu,v;
+
+"Figure 24c"; % Fig 24b shifted right .05 pixel
+ss:=10;
+begingroup interim hppp:=200/72.27; % lowres parameters
+interim blacker:=.65; % make pens a bit blacker
+interim o_correction:=.4; % but don't overshoot as much
+ em#:=10pt#; cap#:=7pt#;
+ thin#:=1/3pt#; thick#:=5/6pt#;
+ o#:=1/5pt#;
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick);
+define_corrected_pixels(o);
+ curve_sidebar:= 1/18em;
+pair u,uu,v;
+beginchar("O",0.8em#,cap#,0);
+ penpos1(thick,10); penpos2(.1[thin,thick],90-10);
+ penpos3(thick,180+10); penpos4(thin,270-10);
+ x1l=w-x3l=curve_sidebar; x2=x4=.5w;
+ y1=.49h; y2l=-o; y3=.51h; y4l=h+o;
+ penstroke (z1e{down}..z2e{right}
+ ..z3e{up}..z4e{left}..cycle) shifted (.05,0) scaled ss;
+for m=0 upto w: proofrule((m*ss,0),(m*ss,h*ss)); endfor
+for n=0 upto h: proofrule((0,n*ss),(w*ss,n*ss)); endfor
+proofrulethickness 1;
+forsuffixes e=l,r:
+ u:=round((point 0 of path_.e)/ss);
+ for t=0 step 1/64 until 4:
+ v:=round((point t of path_.e)/ss);
+ if u<>v: if abs(u-v)=1: proofrule(u*ss,v*ss);
+ else: del:=1/128; tt:=t-del;
+ forever: uu:=round((point tt of path_.e)/ss);
+ exitif abs(u-uu)=1;
+ if del=epsilon:ddt;fi
+ del:=del/2;
+ tt:=tt if uu=u:+ else:-fi del; endfor
+ proofrule(u*ss,uu*ss); proofrule(uu*ss,v*ss); fi
+ u:=v; fi endfor
+ endfor
+showit;
+shipit;
+endgroup;
+endgroup;
+numeric u,uu,v;
+
+"Figure 24d"; % Fig 24c shifted right .05 pixel
+ss:=10;
+begingroup interim hppp:=200/72.27; % lowres parameters
+interim blacker:=.65; % make pens a bit blacker
+interim o_correction:=.4; % but don't overshoot as much
+ em#:=10pt#; cap#:=7pt#;
+ thin#:=1/3pt#; thick#:=5/6pt#;
+ o#:=1/5pt#;
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick);
+define_corrected_pixels(o);
+ curve_sidebar:= 1/18em;
+pair u,uu,v;
+beginchar("O",0.8em#,cap#,0);
+ penpos1(thick,10); penpos2(.1[thin,thick],90-10);
+ penpos3(thick,180+10); penpos4(thin,270-10);
+ x1l=w-x3l=curve_sidebar; x2=x4=.5w;
+ y1=.49h; y2l=-o; y3=.51h; y4l=h+o;
+ penstroke (z1e{down}..z2e{right}
+ ..z3e{up}..z4e{left}..cycle) shifted (.1,0) scaled ss;
+for m=0 upto w: proofrule((m*ss,0),(m*ss,h*ss)); endfor
+for n=0 upto h: proofrule((0,n*ss),(w*ss,n*ss)); endfor
+proofrulethickness 1;
+forsuffixes e=l,r:
+ u:=round((point 0 of path_.e)/ss);
+ for t=0 step 1/64 until 4:
+ v:=round((point t of path_.e)/ss);
+ if u<>v: if abs(u-v)=1: proofrule(u*ss,v*ss);
+ else: del:=1/128; tt:=t-del;
+ forever: uu:=round((point tt of path_.e)/ss);
+ exitif abs(u-uu)=1;
+ if del=epsilon:ddt;fi
+ del:=del/2;
+ tt:=tt if uu=u:+ else:-fi del; endfor
+ proofrule(u*ss,uu*ss); proofrule(uu*ss,v*ss); fi
+ u:=v; fi endfor
+ endfor
+showit;
+shipit;
+endgroup;
+endgroup;
+numeric u,uu,v;
+
+"Figure 24e"; % hash mark
+numeric u;
+u#:=10/18pt#; define_pixels(u);
+beginchar(0,15u#,250/36pt#,70/36pt#);
+pickup pencircle scaled (.4pt+blacker);
+lft x1=round u-eps; x3=x1; x2=x4=w-x1;
+y1=y2=good.y(.5[-d,h]+1.1pt); y3=y4=h-d-y1;
+draw z1--z2; draw z3--z4;
+lft x6=round 3u; x7=w-x6;
+x8=good.x .5w; x5-x6=x7-x8;
+top y5=top y7=h+eps; bot y6=bot y8=-d-eps;
+draw z5--z6; draw z7--z8;
+labels.top(6,8);
+labels.bot(5,7);
+labels.lft(2,4);
+labels.rt(1,3);
+endchar;
+
+"Figure 24f"; % Ionian O at 200 pixels/inch, autorounding=1
+ss:=10;
+begingroup interim hppp:=200/72.27; % lowres parameters
+interim blacker:=.65; % make pens a bit blacker
+interim o_correction:=.4; % but don't overshoot as much
+ em#:=10pt#; cap#:=7pt#;
+ thin#:=1/3pt#; thick#:=5/6pt#;
+ o#:=1/5pt#;
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick);
+define_corrected_pixels(o);
+ curve_sidebar:= 1/18em;
+interim autorounding:=1;
+interim granularity:=10;
+beginchar("O",0.8em#,cap#,0);
+ penpos1(thick,10); penpos2(.1[thin,thick],90-10);
+ penpos3(thick,180+10); penpos4(thin,270-10);
+ x1l=w-x3l=curve_sidebar; x2=x4=.5w;
+ y1=.49h; y2l=-o; y3=.51h; y4l=h+o;
+ penstroke (z1e{down}..z2e{right}
+ ..z3e{up}..z4e{left}..cycle) scaled ss;
+for m=0 upto w: proofrule((m*ss,0),(m*ss,h*ss)); endfor
+for n=0 upto h: proofrule((0,n*ss),(w*ss,n*ss)); endfor
+proofrulethickness 1;
+pair u;
+for v= % pairs gotten from tracingedges on a separate run!
+(2,9), (2,6), (3,6), (3,4), (4,4), (4,3), (5,3), (5,2), (6,2),
+(6,1), (8,1), (8,0), (14,0), (14,1), (16,1), (16,2), (17,2), (17,3),
+(18,3), (18,4), (19,4), (19,7), (20,7), (20,13), (19,13), (19,15),
+(18,15), (18,16), (17,16), (17,17), (16,17), (16,18), (14,18), (14,19),
+(8,19), (8,18), (6,18), (6,17), (5,17), (5,16), (4,16), (4,15), (3,15),
+(3,12), (2,12), (2,9)
+:if known u: proofrule(u*ss,v*ss) ; fi
+u:=v; endfor
+
+pair u;
+for v=
+(4,9), (4,7), (5,7), (5,5), (6,5),
+(6,4), (7,4), (7,3), (9,3), (9,2), (14,2), (14,3), (15,3), (15,4), (16,4),
+(16,5), (17,5), (17,7), (18,7), (18,12), (17,12), (17,14), (16,14),
+(16,15), (15,15), (15,16), (13,16), (13,17), (8,17), (8,16), (7,16),
+(7,15), (6,15), (6,14), (5,14), (5,12), (4,12), (4,9)
+:if known u: proofrule(u*ss,v*ss) ; fi
+u:=v; endfor
+showit;
+shipit;
+endgroup;
+endgroup;
+numeric u;
+
+"Figure 24g"; % Ionian O at 200 pixels/inch, autorounding=2
+ss:=10;
+begingroup interim hppp:=200/72.27; % lowres parameters
+interim blacker:=.65; % make pens a bit blacker
+interim o_correction:=.4; % but don't overshoot as much
+ em#:=10pt#; cap#:=7pt#;
+ thin#:=1/3pt#; thick#:=5/6pt#;
+ o#:=1/5pt#;
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick);
+define_corrected_pixels(o);
+ curve_sidebar:= 1/18em;
+interim autorounding:=2;
+interim granularity:=10;
+beginchar("O",0.8em#,cap#,0);
+ penpos1(thick,10); penpos2(.1[thin,thick],90-10);
+ penpos3(thick,180+10); penpos4(thin,270-10);
+ x1l=w-x3l=curve_sidebar; x2=x4=.5w;
+ y1=.49h; y2l=-o; y3=.51h; y4l=h+o;
+ penstroke (z1e{down}..z2e{right}
+ ..z3e{up}..z4e{left}..cycle) scaled ss;
+for m=0 upto w: proofrule((m*ss,0),(m*ss,h*ss)); endfor
+for n=0 upto h: proofrule((0,n*ss),(w*ss,n*ss)); endfor
+proofrulethickness 1;
+pair u;
+for v=
+(2,9), (2,6), (3,6),
+(3,4), (4,4), (4,3), (5,3), (5,2), (6,2), (6,1), (8,1), (8,0), (14,0),
+(14,1), (16,1), (16,2), (17,2), (17,3), (18,3), (18,4), (19,4), (19,7),
+(20,7), (20,13), (19,13), (19,15), (18,15), (18,16), (17,16), (17,17),
+(16,17), (16,18), (14,18), (14,19), (8,19), (8,18), (6,18), (6,17),
+(5,17), (5,16), (4,16), (4,15), (3,15), (3,12), (2,12), (2,9)
+:if known u: proofrule(u*ss,v*ss) ; fi
+u:=v; endfor
+
+pair u;
+for v=
+(4,9), (4,7), (5,7), (5,5), (6,5),
+(6,4), (7,4), (7,3), (9,3), (9,2), (13,2), (13,3), (15,3), (15,4), (16,4),
+(16,5), (17,5), (17,7), (18,7), (18,12), (17,12), (17,14), (16,14),
+(16,15), (15,15), (15,16), (13,16), (13,17), (9,17), (9,16), (7,16),
+(7,15), (6,15), (6,14), (5,14), (5,12), (4,12), (4,9)
+:if known u: proofrule(u*ss,v*ss) ; fi
+u:=v; endfor
+showit;
+shipit;
+endgroup;
+endgroup;
+numeric u;
+
+"Figure 24h"; % Ionian O at 200 pixels/inch, autorounding=0, smoothing=1
+ss:=10;
+begingroup interim hppp:=200/72.27; % lowres parameters
+interim blacker:=.65; % make pens a bit blacker
+interim o_correction:=.4; % but don't overshoot as much
+ em#:=10pt#; cap#:=7pt#;
+ thin#:=1/3pt#; thick#:=5/6pt#;
+ o#:=1/5pt#;
+define_pixels(em,cap);
+define_blacker_pixels(thin,thick);
+define_corrected_pixels(o);
+ curve_sidebar:= 1/18em;
+beginchar("O",0.8em#,cap#,0);
+ penpos1(thick,10); penpos2(.1[thin,thick],90-10);
+ penpos3(thick,180+10); penpos4(thin,270-10);
+ x1l=w-x3l=curve_sidebar; x2=x4=.5w;
+ y1=.49h; y2l=-o; y3=.51h; y4l=h+o;
+ penstroke (z1e{down}..z2e{right}
+ ..z3e{up}..z4e{left}..cycle) scaled ss;
+for m=0 upto w: proofrule((m*ss,0),(m*ss,h*ss)); endfor
+for n=0 upto h: proofrule((0,n*ss),(w*ss,n*ss)); endfor
+proofrulethickness 1;
+pair u;
+for v=
+(2,9), (2,5), (3,5),
+(3,3), (4,3), (4,2), (6,2), (6,1), (8,1), (8,0), (14,0), (14,1), (16,1),
+(16,2), (17,2), (17,3), (18,3), (18,4), (19,4), (19,6), (20,6), (20,14),
+(19,14), (19,16), (18,16), (18,17), (16,17), (16,18), (14,18), (14,19),
+(8,19), (8,18), (6,18), (6,17), (5,17), (5,16), (4,16), (4,15), (3,15),
+(3,13), (2,13), (2,9)
+:if known u: proofrule(u*ss,v*ss) ; fi
+u:=v; endfor
+
+pair u;
+for v=
+(4,10), (4,9), (5,9), (5,5), (6,5),
+(6,4), (7,4), (7,3), (8,3), (8,2), (14,2), (14,3), (15,3), (15,4), (16,4),
+(16,6), (17,6), (17,8), (18,8), (18,10), (17,10), (17,13), (16,13),
+(16,15), (15,15), (15,16), (14,16), (14,17), (8,17), (8,16), (7,16),
+(7,15), (6,15), (6,13), (5,13), (5,11), (4,11), (4,10)
+:if known u: proofrule(u*ss,v*ss) ; fi
+u:=v; endfor
+showit;
+shipit;
+endgroup;
+endgroup;
+numeric u;
+
+"Figure 27a"; % Modification of Fig. 14b
+clear_all;
+pair zz[];
+zz1=(0,509);
+zz2=(-14,492);
+zz3=(-32,481);
+zz4=(-42,455);
+zz5=(-62,430);
+zz6=(-20,452);
+zz7=(42,448);
+zz8=(38,465);
+zz9=(4,493);
+for n=1 upto 9: z[n-1]=(zz[n]+(62,-430)) scaled 2.5; heavy_dot z[n-1]; endfor
+pickup pencircle scaled 3;
+draw flex(z0,z1,z2)&flex(z2,z3,z4)
+ &flex(z4,z5,z6)&flex(z6,z7,z8,z0)&cycle;
+labels.lft(1,2,3,4);
+labels.rt(0,6,7,8);
+labels.bot(5);
+showit; shipit;
+
+"Figure Da"; % Neenie's problem
+clear_all;
+z1l=(0,240-y); z1r=(15,288-y);
+z0l-z0r=z1l-z1r; z0r=(144,189-y); y0l=0;
+pickup pencircle scaled 2;
+numeric a,b,c,d;
+a=x1l; b=y1r; (c,d)=z0r;
+draw z0l--z0r--z1r--z1l--cycle;
+proofrule((a,d),(a,b)); proofrule((a,b),(a-d+b,b));
+for z=z0l,z0r,z1l,z1r,(a,b): heavy_dot z; endfor
+makelabel.bot.nodot("<=",z1r+(3,-8));
+makelabel.lft("(a,b)",(a,b));
+makelabel.top("z1r",z1r);
+makelabel.lft("z1l",z1l);
+makelabel.rt("(c,d)",z0r);
+makelabel.lft("z0r ",z0r);
+makelabel.lft("z0l ",z0l);
+showit; shipit;
+
+"Figure Ea"; % T of METAFONT logo, with italic correction
+alpha:=.45; % controls bar location and similar things
+beta:=.2; % controls squareness of bowls
+numeric h#, xx#, yy#, o#, s#, u#, ph#, pv#;
+numeric leftstemloc, barheight;
+ h#=6; % height of characters, in pt
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ ph#=2/3; % horizontal thickness of pen, in pt
+yy#=xx#; define_whole_pixels(xx,yy);
+define_pixels(s,u);
+define_corrected_pixels(o);
+pv#=.9ph#; define_blacker_pixels(ph,pv);
+pickup pencircle xscaled ph yscaled pv;
+logo_pen:=savepen;
+leftstemloc:=good.x((2.5u#+s#)*pt);
+barheight:=good.y(alpha*h#*pt);
+def beginlogochar(suffix c)(expr n) = % code c; width is n units plus sidebars
+ beginchar(str c,n*u#+2s#,h#,0); pickup logo_pen enddef;
+slant:=0;
+
+beginlogochar(T,13);
+italcorr h#*slant+.5u#;
+if odd(w-ph): w:=w+1; fi % allows a symmetric stem
+x1+x2=2x3=2x4=w; lft.x1=-epsilon; % I tried s-o instead; was not as good
+y1=y2=y3; top.y1=h; bot.y4=-o;
+draw z1..z2; draw z3..z4;
+labels.rt(2,3);
+makelabel.top("1",z1);
+makelabel.top("4",z4);
+endchar;
+
+"Figure Eb"; % F of METAFONT logo
+alpha:=.45; % controls bar location and similar things
+beta:=.2; % controls squareness of bowls
+numeric h#, xx#, yy#, o#, s#, u#, ph#, pv#;
+numeric leftstemloc, barheight;
+ h#=6; % height of characters, in pt
+ xx#=0.6; % extra length of certain features, in pt
+ u#=4/9; % unit width, in pt
+ s#=0; % extra sidebar, in pt
+ o#=1/9; % overshoot of curves, in pt
+ ph#=2/3; % horizontal thickness of pen, in pt
+yy#=xx#; define_whole_pixels(xx,yy);
+define_pixels(s,u);
+define_corrected_pixels(o);
+pv#=.9ph#; define_blacker_pixels(ph,pv);
+pickup pencircle xscaled ph yscaled pv;
+logo_pen:=savepen;
+leftstemloc:=good.x((2.5u#+s#)*pt);
+barheight:=good.y(alpha*h#*pt);
+def beginlogochar(suffix c)(expr n) = % code c; width is n units plus sidebars
+ beginchar(str c,n*u#+2s#,h#,0); pickup logo_pen enddef;
+
+beginlogochar(F,14);
+x1=x2=x3=leftstemloc; x4=w-x1+o; x5=x4-xx;
+y2=y5; y3=y4; bot.y1=-o; top.y3=h; y2=barheight;
+draw z1--z3--z4; draw z2--z5;
+labels.rt(2,3);
+labels.lft(4,5);
+labels.top(1);
+endchar;
+
+"Figure Ec"; % cmr10 period
+u#:=20/36pt#;
+dot_diam#:=38/36pt#;
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+x_height#:=155/36pt#; % height of lowercase without ascenders
+bar_height#:=87/36pt#; % height of crossbar in lowercase e
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+body_depth#:=90/36pt#;
+fine#:=7/36pt#;
+
+define_pixels(u);
+define_whole_blacker_pixels(dot_diam,fine);
+define_whole_vertical_pixels(body_height,asc_height,x_height,bar_height,
+ desc_depth,body_depth);
+
+beginchar(".",5u#,dot_diam#,0);
+def makebox(text rule) =
+ for y=0,asc_height,body_height,x_height,bar_height,-desc_depth,-body_depth:
+ rule((0,y),(w,y)); endfor % horizontals
+ for x=0,w: rule((x,-body_depth),(x,body_height)); endfor % verticals
+ for x=u step u until w-1:
+ rule((x,-body_depth),(x,body_height)); endfor % more verticals
+ if charic<>0:
+ rule((w+charic*pt,h.o_),(w+charic*pt,.5h.o_)); fi % italic correction
+ enddef;
+pickup pencircle scaled fine;
+penpos1(dot_diam-fine,0); penpos2(dot_diam-fine,90);
+x1l=good.x(x1l+.5w-x1); bot y2l=0; z1=z2;
+filldraw z1l...z2l...z1r...z2r...cycle; % roundish dot
+labels.lft(1,1l);
+labels.rt(2,1r);
+labels.top(2r);
+labels.bot(2l);
+endchar;
+
+"Figure Ed"; % cmr10 emdash
+u#:=20/36pt#;
+dot_diam#:=38/36pt#;
+body_height#:=270/36pt#; % height of tallest characters
+asc_height#:=250/36pt#; % height of lowercase ascenders
+x_height#:=155/36pt#; % height of lowercase without ascenders
+bar_height#:=87/36pt#; % height of crossbar in lowercase e
+desc_depth#:=70/36pt#; % depth of lowercase descenders
+body_depth#:=90/36pt#;
+vair#:=8/36pt#; % vertical diameter of hairlines
+
+define_pixels(u);
+define_whole_blacker_pixels(dot_diam,vair);
+define_whole_vertical_pixels(body_height,asc_height,x_height,bar_height,
+ desc_depth,body_depth);
+
+beginchar(oct"174",18u#,x_height#,0);
+def makebox(text rule) =
+ for y=0,asc_height,body_height,x_height,bar_height,-desc_depth,-body_depth:
+ rule((0,y),(w,y)); endfor % horizontals
+ for x=0,w: rule((x,-body_depth),(x,body_height)); endfor % verticals
+ for x=u step u until w-1:
+ rule((x,-body_depth),(x,body_height)); endfor % more verticals
+ if charic<>0:
+ rule((w+charic*pt,h.o_),(w+charic*pt,.5h.o_)); fi % italic correction
+ enddef;
+italcorr .61803x_height#*0 + .5u#;
+pickup nullpen;
+penpos1(vair,90); penpos2(vair,90);
+y1r=y2r=good.y(y1r+.61803h-y1);
+lft x1=-eps; rt x2=w+eps;
+penstroke z1e--z2e; % crossbar
+labels.lft(1);
+labels.rt(2);
+labels.top(1r,2r);
+labels.bot(1l,2l);
+endchar;
+
+bye
diff --git a/systems/knuth/local/lib/mproof.tex b/systems/knuth/local/lib/mproof.tex
new file mode 100644
index 0000000000..aac809db3a
--- /dev/null
+++ b/systems/knuth/local/lib/mproof.tex
@@ -0,0 +1,28 @@
+% To produce proof sheets from MetaPost output (includes date and time):
+% tex mproof <MetaPost output file names>
+
+\input epsf
+\font\textt=cmtex10 \textt
+\def\okbreak{\vfil\penalty2000\vfilneg}
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\footline={\sevenrm\today\ at \hours\hss\tenrm\folio\hss}
+
+\def\endit{\\}
+\def\munch#1 {\def\next{#1}\ifx\next\endit\endit
+ \else#1$$\epsfverbosetrue\epsfbox{#1}$$\par\okbreak\fi\munch}
+
+\def\\{\let\munch=\relax\def\\{}}
+
+\everypar{\everypar{}\munchline}
+
+\obeylines\def\munchline#1
+{\catcode`\^^M=5 \munch#1\\ \vfill\end}%
diff --git a/systems/knuth/local/lib/oneone.mf b/systems/knuth/local/lib/oneone.mf
new file mode 100644
index 0000000000..efa30cf1f2
--- /dev/null
+++ b/systems/knuth/local/lib/oneone.mf
@@ -0,0 +1,10 @@
+% Black font for Imagen with proofsheet resolution 300 pixels per inch
+
+if mode<>imagen: errmessage "This file is for imagen only"; fi
+
+font_identifier "BLACKIMAGEN";
+picture pix_picture; pix_wd := pix_ht := 1;
+pix_picture := unitpixel;
+dotsize:=10;
+
+input grayf
diff --git a/systems/knuth/local/lib/picmac.tex b/systems/knuth/local/lib/picmac.tex
new file mode 100644
index 0000000000..0e1b537ab8
--- /dev/null
+++ b/systems/knuth/local/lib/picmac.tex
@@ -0,0 +1,178 @@
+% Pictures (a subset of \LaTeX's conventions, plus squines)
+\catcode`@=11 % enable private control sequences
+\def\wlog#1{} % don't put allocation info into the log
+
+\newskip\hsssglue \hsssglue=0pt plus 1fill minus 1fill \def\hsss{\hskip\hsssglue}
+
+\newdimen\unitlength \newdimen\linethickness
+\newdimen\@picheight \newdimen\@xdim \newdimen\@ydim \newdimen\@len \newdimen\@save
+\newcount\@multicount \newcount\@xarg \newcount\@yarg
+\newbox\@picbox \newbox\@mpbox
+
+\font\tenln=line10 \font\tenlnw=linew10
+\font\tencirc=lcircle10 \font\tencircw=lcirclew10
+
+\def\thinlines{\let\linefont=\tenln \let\circlefont=\tencirc
+ \linethickness=\fontdimen8\linefont}
+\def\thicklines{\let\linefont=\tenlnw \let\circlefont=\tencircw
+ \linethickness=\fontdimen8\linefont}
+\thinlines
+
+\def\beginpicture(#1,#2)(#3,#4){\@picheight=#2\unitlength
+ \setbox\@picbox=\hbox to#1\unitlength\bgroup \let\line=\@line
+ \kern-#3\unitlength \lower#4\unitlength\hbox\bgroup\ignorespaces}
+\def\endpicture{\egroup\hss\egroup
+ \ht\@picbox=\@picheight \dp\@picbox=\z@
+ \leavevmode\box\@picbox}
+
+\def\put(#1,#2)#3{\raise#2\unitlength\rlap{\kern#1\unitlength #3}\ignorespaces}
+
+\def\multiput(#1,#2)(#3,#4)#5#6{\@multicount=#5\relax
+ \@xdim=#1\unitlength \@ydim=#2\unitlength \setbox\@mpbox=\hbox{#6}%
+ \loop\ifnum\@multicount>0
+ \raise\@ydim\rlap{\kern\@xdim \unhcopy\@mpbox}%
+ \advance\@xdim#3\unitlength \advance\@ydim#4\unitlength
+ \advance\@multicount\m@ne \repeat\ignorespaces}
+
+\def\makebox(#1,#2)#3{\setbox\@picbox=\hbox to#1\unitlength{\hss#3\hss}%
+ \@ydim=\ht\@picbox \advance\@ydim-\dp\@picbox
+ \ht\@picbox=#2\unitlength \dp\@picbox=\z@
+ \leavevmode\lower.5\@ydim\box\@picbox}
+
+\newif\ifneg
+\def\@line(#1,#2)#3{\@xarg=#1 \@yarg=#2 \@len=#3\unitlength \leavevmode
+ \ifnum\@xarg<0 \reverseline \else \negfalse \@ydim=\z@\fi
+ \ifnum\@xarg=0 \@vline
+ \else\ifnum\@yarg=0 \@hline \else\@sline\fi\fi
+ \ifneg\kern-\@len\else\@save=\@ydim\fi}
+\def\reverseline{\negtrue \kern-\@len \@xarg=-\@xarg
+ \@ydim=\@len \multiply\@ydim\@yarg \divide\@ydim\@xarg \@yarg=-\@yarg}
+
+\def\@hline{\vrule height.5\linethickness depth.5\linethickness width\@len}
+\def\@vline{\kern-.5\linethickness\vrule width\linethickness
+ \ifnum\@yarg<0 height\z@ depth\else depth\z@ height\fi\@len
+ \kern-.5\linethickness}
+
+\def\@sline{\setbox\@picbox=\hbox{\linefont \count@=\@xarg \multiply\count@ 8
+ \ifnum\@yarg>0 \advance\count@\@yarg \advance\count@-9
+ \else \advance\count@-\@yarg \advance\count@ 55 \fi \char\count@}%
+ \ifnum\@yarg<0 \@picheight=-\ht\@picbox \advance\@ydim\@picheight
+ \else \@picheight=\ht\@picbox \fi
+ \@xdim=\wd\@picbox \@save=\@ydim
+ \loop\ifdim\@xdim<\@len \raise\@ydim\copy\@picbox
+ \advance\@xdim\wd\@picbox \advance\@ydim\@picheight \repeat
+ \advance\@xdim-\@len \kern-\@xdim
+ \multiply\@xdim\@yarg \divide\@xdim\@xarg \advance\@ydim-\@xdim
+ \raise\@ydim\box\@picbox}
+
+\def\vector(#1,#2)#3{\@line(#1,#2){#3}%
+ \ifnum\@xarg=0 \@vvector \else\ifnum\@yarg=0 \@hvector \else\@svector\fi\fi}
+\def\@hvector{\ifneg\rlap{\linefont\char27}\else
+ \smash{\llap{\linefont\char45}}\fi} % we have to smash because of font bug
+\def\@vvector{\ifnum\@yarg<0 \raise-\@len\rlap{\linefont\char63}%
+ \else\setbox\@picbox=\rlap{\linefont\char54}\advance\@len-\ht\@picbox
+ \raise\@len\box\@picbox\fi}
+
+\def\@svector{\setbox\@picbox=\hbox to\z@{\linefont
+ \ifnum\@yarg<0 \count@=55 \@yarg=-\@yarg \else\count@=-9 \fi
+ \ifneg\multiply\@xarg16 \multiply\@yarg2
+ \else\hss % \llap
+ \ifnum\@xarg>2 \multiply\@xarg9 \multiply\@yarg2 \advance\count@29
+ \else\ifnum\@yarg>2 \multiply\@xarg16 \multiply\@yarg9 \advance\count@-20
+ \else\multiply\@xarg24 \multiply\@yarg3 \fi\fi\fi
+ \advance\count@\@xarg \advance\count@\@yarg \char\count@
+ \ifneg\hss\fi}% \rlap
+ \raise\@save\box\@picbox}
+
+\def\disk#1{\@len=#1\unitlength \count@='160 \@diskcirc}
+\def\circle#1{\@len=#1\unitlength \count@='140 \@diskcirc}
+\def\@diskcirc{\setbox\@picbox=\hbox{\circlefont\char\count@}\@xdim=\wd\@picbox
+ \leavevmode \ifdim\@len>15.499\@xdim \@bigdc \else \@smalldc\fi}
+\def\@bigdc{\ifnum\count@<'160 \@bigcirc
+ \else \@len=15\@xdim \@diskcirc\fi}
+\def\@smalldc{{\advance\@len-.5\@xdim
+ \loop\ifdim\@xdim<\@len \advance\count@\@ne \advance\@xdim\wd\@picbox\repeat
+ \hbox{\circlefont\char\count@}}}
+\def\@bigcirc{{\circlefont\count@=15
+ \setbox\@picbox=\hbox{\char\count@}\@xdim=\wd\@picbox
+ \ifdim\@len>2.5\@xdim \@len=2.5\@xdim\fi
+ \advance\@len-.125\wd\@picbox
+ \loop\ifdim\@xdim<\@len \advance\count@ 4 \advance\@xdim.25\wd\@picbox\repeat
+ \@ydim=.5\@xdim \advance\@ydim.5\linethickness
+ \setbox\@picbox=\vbox{\hbox{\char\count@\advance\count@-3\char\count@}%
+ \nointerlineskip
+ \hbox{\advance\count@\m@ne\char\count@\advance\count@\m@ne\char\count@}}%
+ \kern-\@ydim\lower\@ydim\box\@picbox}}
+
+\newif\ifovaltl \newif\ifovaltr \newif\ifovalbl \newif\ifovalbr
+\ovaltltrue \ovaltrtrue \ovalbltrue \ovalbrtrue
+\def\oval(#1,#2){\@xdim=#1\unitlength \@ydim=#2\unitlength
+ {\circlefont \setbox\@picbox=\hbox{\char0}
+ \ifdim\@xdim<\wd\@picbox \@xdim=\wd\@picbox\fi
+ \ifdim\@ydim<\wd\@picbox \@ydim=\wd\@picbox\fi
+ \@save=\@xdim \ifdim\@ydim<\@save \@save=\@ydim \fi
+ \count@=39
+ \loop \setbox\@picbox=\hbox{\char\count@}\ifdim\@save<\wd\@picbox
+ \advance\count@-4 \repeat
+ \setbox\strutbox=\hbox{\vrule height\ht\@picbox depth\dp\@picbox width\z@
+ \kern\wd\@picbox}%
+ \@save=.5\wd\@picbox \advance\@save-.5\linethickness
+ \setbox0=\hbox to\@xdim{\ifovaltl\char\count@\else\strut\fi
+ \kern-\@save\leaders\hrule height\ifovaltl\linethickness\else\z@\fi\hfil
+ \leaders\hrule height\ifovaltr\linethickness\else\z@\fi\hfil\kern\@save
+ \ifovaltr\advance\count@-3\char\count@\else\strut\fi\kern-\wd\@picbox}%
+ \advance\count@\m@ne
+ \setbox2=\hbox to\@xdim{\ifovalbl\char\count@\else\strut\fi
+ \kern-\@save\leaders\hrule height\ifovalbl\linethickness\else\z@\fi\hfil
+ \leaders\hrule height\ifovalbr\linethickness\else\z@\fi\hfil\kern\@save
+ \ifovalbr\advance\count@\m@ne\char\count@\else\strut\fi\kern-\wd\@picbox}%
+ \@save=\@ydim \advance\@save-\wd\@picbox \divide\@save 2
+ \setbox\@picbox=\vbox{\box0\nointerlineskip
+ \hbox to\@xdim{\vrule height\@save width\ifovaltl\linethickness\else\z@\fi
+ \hfil\ifovaltr\vrule width\linethickness\kern-\linethickness\fi}%
+ \nointerlineskip
+ \hbox to\@xdim{\vrule height\@save width\ifovalbl\linethickness\else\z@\fi
+ \hfil\ifovalbr\vrule width\linethickness\kern-\linethickness\fi}%
+ \nointerlineskip\box2}%
+ \@save=.5\@ydim \advance\@save.5\linethickness \leavevmode
+ \kern-.5\@xdim \kern-.5\linethickness \lower\@save\box\@picbox}}
+
+\def\cpic#1\endcpic{\vcenter{\hbox{\beginpicture#1\endpicture}}}
+
+% Squines (quadratic splines)
+% example of use: to plot f(x) between x0 and x1, you can say
+% \put(0,0){\squine(x0,xm,x1,y0,ym,y1)}, where y0=f(x0), y1=f(x1)
+% xm=(y0-y1+s1x1-s0x0)/(s1-s0), ym=(s0(s1x1-y1)-s1(s0x0-y0))/(s1-s0),
+% s0=f'(x0), and s1=f'(x1).
+
+\newdimen\@xi \newdimen\@xii \newdimen\@xiii \newdimen\@xiv
+\newdimen\@xpt \newdimen\@xoldpt
+\newdimen\@yi \newdimen\@yii \newdimen\@yiii \newdimen\@yiv
+\newdimen\@ypt \newdimen\@yoldpt
+\def\squine(#1,#2,#3,#4,#5,#6){\setbox\@picbox\hbox{\tencirc q}%
+ \global\@xoldpt=#1\unitlength \global\@yoldpt=#4\unitlength \kern\@xoldpt
+ \@xi=\@xoldpt \@xii=#2\unitlength \@xiii=#3\unitlength
+ \@yi=\@yoldpt \@yii=#5\unitlength \@yiii=#6\unitlength
+ \squinerec
+ \@xpt=#3\unitlength \@ypt=#6\unitlength \@addpoint
+ \raise\@ypt\copy\@picbox}
+\newif\iffar
+\def\squinerec{\farfalse \testnear\@xi\@xiii \testnear\@yi\@yiii
+ \iffar \decast \fi}
+\def\testnear#1#2{\@save=#1\advance\@save-#2%
+ \ifdim\@save<\z@ \@save=-\@save\fi \ifdim\@save>\p@ \fartrue \fi}
+\def\decast{\@xpt=\@xi \advance\@xpt\@xii \divide\@xpt2
+ \advance\@xii\@xiii \divide\@xii2
+ \@xiv=\@xpt \advance\@xiv\@xii \divide\@xiv2
+ \@ypt=\@yi \advance\@ypt\@yii \divide\@ypt2
+ \advance\@yii\@yiii \divide\@yii2
+ \@yiv=\@ypt \advance\@yiv\@yii \divide\@yiv2
+ \begingroup\@xii=\@xpt \@xiii=\@xiv
+ \@yii=\@ypt \@yiii=\@yiv \squinerec\endgroup
+ \@xpt=\@xiv \@ypt=\@yiv \@addpoint
+ \@xi=\@xiv \@yi=\@yiv \squinerec}
+\def\@addpoint{%\message{(\the\@xpt,\the\@ypt)}%
+ \global\advance\@xoldpt-\@xpt \wd\@picbox=-\@xoldpt
+ \raise\@yoldpt\copy\@picbox \global\@xoldpt=\@xpt \global\@yoldpt=\@ypt}
+
+\catcode`\@=12 % disable private control sequences
diff --git a/systems/knuth/local/lib/proofmac.tex b/systems/knuth/local/lib/proofmac.tex
new file mode 100644
index 0000000000..29533b84b4
--- /dev/null
+++ b/systems/knuth/local/lib/proofmac.tex
@@ -0,0 +1,353 @@
+% standard macros for CWEB listings (in addition to plain.tex)
+% Version 3.6 --- May 2000
+% Modified for proofs in CTWILL
+\ifx\documentstyle\undefined\else\endinput\fi % LaTeX will use other macros
+\xdef\fmtversion{\fmtversion+CWEB3.6+PROOFS}
+
+\let\:=\. % preserve a way to get the dot accent
+ % (all other accents will still work as usual)
+
+\parskip 0pt % no stretch between paragraphs
+\parindent 1em % for paragraphs and for the first line of C text
+
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps
+\def\CEE/{{\mc C\spacefactor1000}}
+\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}}
+\def\TEX/{\TeX}
+\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}}
+\def\Cee{\CEE/} % for backward compatibility
+\def\9#1{}
+\font\eightrm=cmr8
+\let\sc=\eightrm % small caps (NOT a caps-and-small-caps font)
+\let\mainfont=\tenrm
+\let\cmntfont\tenrm
+%\font\tenss=cmss10 \let\cmntfont\tenss % alternative comment font
+\font\titlefont=cmr7 scaled\magstep4 % title on the contents page
+\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title
+\font\tentex=cmtex10 % TeX extended character set (used in strings)
+\fontdimen7\tentex=0pt % no double space after sentences
+
+\def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way
+\def\&#1{\leavevmode\hbox{\bf
+ \def\_{\kern.04em\vbox{\hrule width.3em height .6pt}\kern.08em}%
+ #1\/\kern.05em}} % boldface type for reserved words
+\def\.#1{\leavevmode\hbox{\tentex % typewriter type for strings
+ \let\\=\BS % backslash in a string
+ \let\{=\LB % left brace in a string
+ \let\}=\RB % right brace in a string
+ \let\~=\TL % tilde in a string
+ \let\ =\SP % space in a string
+ \let\_=\UL % underline in a string
+ \let\&=\AM % ampersand in a string
+ \let\^=\CF % circumflex in a string
+ #1\kern.05em}}
+\def\){{\tentex\kern-.05em}\discretionary{\hbox{\tentex\BS}}{}{}}
+\def\AT{@} % at sign for control text (not needed in versions >= 2.9)
+\def\ATL{\par\noindent\bgroup\catcode`\_=12 \postATL} % print @l in limbo
+\def\postATL#1 #2 {\bf letter \\{\uppercase{\char"#1}}
+ tangles as \tentex "#2"\egroup\par}
+\def\noATL#1 #2 {}
+\def\noatl{\let\ATL=\noATL} % suppress output from @l
+\def\ATH{\X\kern-.5em:Preprocessor definitions\X}
+\let\PB=\relax % hook for program brackets |...| in TeX part or section name
+
+\chardef\AM=`\& % ampersand character in a string
+\chardef\BS=`\\ % backslash in a string
+\chardef\LB=`\{ % left brace in a string
+\chardef\RB=`\} % right brace in a string
+\def\SP{{\tt\char`\ }} % (visible) space in a string
+\chardef\TL=`\~ % tilde in a string
+\chardef\UL=`\_ % underline character in a string
+\chardef\CF=`\^ % circumflex character in a string
+
+\newbox\PPbox % symbol for ++
+\setbox\PPbox=\hbox{\kern.5pt\raise1pt\hbox{\sevenrm+\kern-1pt+}\kern.5pt}
+\def\PP{\copy\PPbox}
+\newbox\MMbox \setbox\MMbox=\hbox{\kern.5pt\raise1pt\hbox{\sevensy\char0
+ \kern-1pt\char0}\kern.5pt}
+\def\MM{\copy\MMbox}
+\newbox\MGbox % symbol for ->
+\setbox\MGbox=\hbox{\kern-2pt\lower3pt\hbox{\teni\char'176}\kern1pt}
+\def\MG{\copy\MGbox}
+\def\MRL#1{\mathrel{\let\K==#1}}
+\let\GG=\gg
+\let\LL=\ll
+\let\NULL=\Lambda
+\mathchardef\AND="2026 % bitwise and; also \& (unary operator)
+\let\OR=\mid % bitwise or
+\let\XOR=\oplus % bitwise exclusive or
+\def\CM{{\sim}} % bitwise complement
+\newbox\MODbox \setbox\MODbox=\hbox{\eightrm\%}
+\def\MOD{\mathbin{\copy\MODbox}}
+\def\DC{\kern.1em{::}\kern.1em} % symbol for ::
+\def\PA{\mathbin{.*}} % symbol for .*
+\def\MGA{\mathbin{\MG*}} % symbol for ->*
+\def\this{\&{this}}
+
+\newbox\bak \setbox\bak=\hbox to -1em{} % backspace one em
+\newbox\bakk\setbox\bakk=\hbox to -2em{} % backspace two ems
+
+\newcount\ind % current indentation in ems
+\def\1{\global\advance\ind by1\hangindent\ind em} % indent one more notch
+\def\2{\global\advance\ind by-1} % indent one less notch
+\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement
+\def\4{\copy\bak} % backspace one notch
+\def\5{\hfil\penalty-1\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break
+\def\6{\ifmmode\else\par % forced break
+ \hangindent\ind em\noindent\kern\ind em\copy\bakk\ignorespaces\fi}
+\def\7{\Y\6} % forced break and a little extra space
+\def\8{\hskip-\ind em\hskip 2em} % no indentation
+
+\newcount\gdepth % depth of current major group, plus one
+\newcount\secpagedepth
+\secpagedepth=3 % page breaks will occur for depths -1, 0, and 1
+\newtoks\gtitle % title of current major group
+\newskip\intersecskip \intersecskip=12pt minus 3pt % space between sections
+\let\yskip=\smallskip
+\def\?{\mathrel?}
+\def\note#1#2.{\Y\noindent{\hangindent2em\baselineskip10pt\eightrm#1~#2.\par}}
+\def\lapstar{\rlap{*}}
+\def\stsec{\rightskip=0pt % get out of C mode (cf. \B)
+ \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50
+ \noindent{\let\*=\lapstar\bf\secstar.\quad}}
+\let\startsection=\stsec
+\def\defin#1{\global\advance\ind by 2 \1\&{#1 } } % begin `define' or `format'
+\def\A{\note{See also section}} % xref for doubly defined section name
+\def\As{\note{See also sections}} % xref for multiply defined section name
+\def\B{\rightskip=0pt plus 100pt minus 10pt % go into C mode
+ \sfcode`;=3000
+ \pretolerance 10000
+ \hyphenpenalty 1000 % so strings can be broken (discretionary \ is inserted)
+ \exhyphenpenalty 10000
+ \global\ind=2 \1\ \unskip}
+\def\C#1{\5\5\quad$/\ast\,${\cmntfont #1}$\,\ast/$}
+\let\SHC\C % "// short comments" treated like "/* ordinary comments */"
+%\def\C#1{\5\5\quad$\triangleright\,${\cmntfont#1}$\,\triangleleft$}
+%\def\SHC#1{\5\5\quad$\diamond\,${\cmntfont#1}}
+\def\D{\defin{\#define}} % macro definition
+\let\E=\equiv % equivalence sign
+\def\ET{ and~} % conjunction between two section numbers
+\def\ETs{, and~} % conjunction between the last two of several section numbers
+\def\F{\defin{format}} % format definition
+\let\G=\ge % greater than or equal sign
+% \H is long Hungarian umlaut accent
+\let\I=\ne % unequal sign
+\def\J{\.{@\&}} % TANGLE's join operation
+\let\K== % assignment operator
+%\let\K=\leftarrow % "honest" alternative to standard assignment operator
+% \L is Polish letter suppressed-L
+\outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section
+ \vskip\intersecskip\startsection\ignorespaces}
+\outer\def\N#1#2#3.{\gdepth=#1\gtitle={#3}\MN{#2}% beginning of starred section
+ \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small
+ \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi
+ \message{*\secno} % progress report
+ \edef\next{\write\cont{\ZZ{#3}{#1}{\secno}% write to contents file
+ {\noexpand\the\pageno}}}\next % \ZZ{title}{depth}{sec}{page}
+ \ifon\startsection{\bf#3.\quad}\ignorespaces}
+\def\MN#1{\par % common code for \M, \N
+ {\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% remove \* from section name
+ \ifx\secno\secstar \onmaybe \else\ontrue \fi
+ \mark{{{\tensy x}\secno}{\the\gdepth}{\the\gtitle}}}
+% each \mark is {section reference or null}{depth plus 1}{group title}
+% \O is Scandinavian letter O-with-slash
+% \P is paragraph sign
+\def\Q{\note{This code is cited in section}} % xref for mention of a section
+\def\Qs{\note{This code is cited in sections}} % xref for mentions of a section
+\let\R=\lnot % logical not
+% \S is section sign
+\def\T#1{\leavevmode % octal, hex or decimal constant
+ \hbox{$\def\?{\kern.2em}%
+ \def\$##1{\egroup_{\,\rm##1}\bgroup}% suffix to constant
+ \def\_{\cdot 10^{\aftergroup}}% power of ten (via dirty trick)
+ \let\~=\oct \let\^=\hex {#1}$}}
+\def\U{\note{This code is used in section}} % xref for use of a section
+\def\Us{\note{This code is used in sections}} % xref for uses of a section
+\let\V=\lor % logical or
+\let\W=\land % logical and
+\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % section name
+ \XX$\langle\,${\let\I=\ne#2\eightrm\kern.5em#1}$\,\rangle$\XX}
+\def\Y{\par\yskip}
+\let\Z=\le
+\let\ZZ=\let % now you can \write the control sequence \ZZ
+\let\*=*
+
+%\def\oct{\hbox{\rm\char'23\kern-.2em\it\aftergroup\?\aftergroup}} % WEB style
+%\def\hex{\hbox{\rm\char"7D\tt\aftergroup}} % WEB style
+\def\oct{\hbox{$^\circ$\kern-.1em\it\aftergroup\?\aftergroup}}% CWEB style
+\def\hex{\hbox{$^{\scriptscriptstyle\#}$\tt\aftergroup}} % CWEB style
+\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule
+ \hbox{\strut\kern2pt\.{#1}\kern2pt}}
+ \hrule}\vrule\kern2pt}} % verbatim string
+
+\def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue
+\newif\ifon \newif\iftitle \newif\ifpagesaved
+
+\def\lheader{\mainfont\the\pageno\eightrm\qquad\grouptitle\hfill\title\qquad
+ \mainfont\topsecno} % top line on left-hand pages
+\def\rheader{\mainfont\topsecno\eightrm\qquad\title\hfill\grouptitle
+ \qquad\mainfont\the\pageno} % top line on right-hand pages
+\def\grouptitle{\let\i=I\let\j=J\uppercase\expandafter{\expandafter
+ \takethree\topmark}}
+\def\topsecno{\expandafter\takeone\topmark}
+\def\takeone#1#2#3{#1}
+\def\taketwo#1#2#3{#2}
+\def\takethree#1#2#3{#3}
+\def\nullsec{\eightrm\kern-2em} % the \kern-2em cancels \qquad in headers
+
+\def\page{\box255 }
+\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi
+ \shipout\vbox{
+ \vbox to\fullpageheight{
+ \iftitle\global\titlefalse
+ \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi
+ \vfill#1}} % parameter #1 is the page itself
+ \global\advance\pageno by1}
+
+\gtitle={\.{CWEB} output} % this running head is reset by starred sections
+\mark{\noexpand\nullsec0{\the\gtitle}}
+\def\title{\expandafter\uppercase\expandafter{\jobname}}
+\def\topofcontents{\centerline{\titlefont\title}\vskip.7in
+ \vfill} % this material will start the table of contents page
+\def\botofcontents{\vfill
+ \centerline{\covernote}} % this material will end the table of contents page
+\def\covernote{}
+\def\contentspagenumber{0} % default page number for table of contents
+\newdimen\pagewidth \pagewidth=6.5in % the width of each page
+\newdimen\pageheight \pageheight=8.7in % the height of each page
+\newdimen\fullpageheight \fullpageheight=9in % page height including headlines
+\newdimen\pageshift \pageshift=0in % shift righthand pages wrt lefthand ones
+\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein
+ \fullpageheight=9truein\setpage}
+\def\setpage{\hsize\pagewidth\vsize\pageheight} % use after changing page size
+\def\contentsfile{\jobname.toc} % file that gets table of contents info
+\def\readcontents{\input \contentsfile}
+\def\readindex{\input \jobname.idx}
+\def\readsections{\input \jobname.scn}
+
+\newwrite\cont
+\output{\setbox0=\page % the first page is garbage
+ \openout\cont=\contentsfile
+ \write\cont{\catcode `\noexpand\@=11\relax} % \makeatletter
+ \global\output{\normaloutput\page\lheader\rheader}}
+\setpage
+\vbox to \vsize{} % the first \topmark won't be null
+
+\def\mini{\Y\bgroup\obeylines}
+{\obeylines
+ \gdef\[#1 #2 #3
+{\noindent\kern.5\hsize #2\miniform#3, \S#1.\par}%
+ \gdef\]#1 #2 #3
+{\noindent\kern.5\hsize #2\miniform#3, \extref#1.\par}}
+\def\miniform{\futurelet\next\miniforma}
+\def\miniforma{\ifx\next\zip
+ \else\if=\noexpand\next \let\next\gobbleq
+ \else\let\next\colon\fi\fi\next}
+\def\zip{}
+\def\gobbleq={\,=\,}
+\def\colon{: }
+\def\extref{\futurelet\next\extrefa}
+\def\extrefa{\if"\next\let\next\gobblest\else\let\next\cite\fi\next}
+\def\gobblest"#1"{{\tentex#1}}
+\def\cite#1#2.{{\sc#1}\,\S#2.}
+\let\FI=\fi
+\let\shortpage=\relax
+\let\forcebr=\relax
+\def\uninitialized{???}
+
+\def\ch{\note{The following sections were changed by the change file:}
+ \let\*=\relax}
+\newbox\sbox % saved box preceding the index
+\newbox\lbox % lefthand column in the index
+\def\inx{\par\vskip6pt plus 1fil % we are beginning the index
+ \write\cont{} % ensure that the contents file isn't empty
+ \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother
+ \closeout\cont % the contents information has been fully gathered
+ \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi
+ \global\setbox\sbox=\page \global\pagesavedtrue}
+ \pagesavedfalse \eject % eject the page-so-far and predecessors
+ \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box
+ \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height
+ \hsize=.5\pagewidth \advance\hsize by -10pt
+ % column width for the index (20pt between cols)
+ \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
+ \def\lr{L} % this tells whether the left or right column is next
+ \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
+ \else\normaloutput{\vbox to\pageheight{\box\sbox\vss
+ \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader
+ \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi}
+ \message{Index:}
+ \parskip 0pt plus .5pt
+ \outer\def\I##1, {\par\hangindent2em\noindent##1:\kern1em} % index entry
+ \def\[##1]{$\underline{##1}$} % underlined index item
+ \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar
+ \hyphenpenalty 10000 \parindent0pt
+ \readindex}
+\def\fin{\par\vfill\eject % this is done when we are ending the index
+ \ifpagesaved\null\vfill\eject\fi % output a null index column
+ \if L\lr\else\null\vfill\eject\fi % finish the current page
+ \parfillskip 0pt plus 1fil
+ \def\grouptitle{NAMES OF THE SECTIONS}
+ \let\topsecno=\nullsec
+ \message{Section names:}
+ \output={\normaloutput\page\lheader\rheader}
+ \setpage
+ \def\note##1##2.{\quad{\eightrm##1~##2.}}
+ \def\Q{\note{Cited in section}} % crossref for mention of a section
+ \def\Qs{\note{Cited in sections}} % crossref for mentions of a section
+ \def\U{\note{Used in section}} % crossref for use of a section
+ \def\Us{\note{Used in sections}} % crossref for uses of a section
+ \def\I{\par\hangindent 2em}\let\*=*
+ \readsections}
+\def\con{\par\vfill\eject % finish the section names
+ \rightskip 0pt \hyphenpenalty 50 \tolerance 200
+ \setpage \output={\normaloutput\page\lheader\rheader}
+ \titletrue % prepare to output the table of contents
+ \pageno=\contentspagenumber
+ \def\grouptitle{TABLE OF CONTENTS}
+ \message{Table of contents:}
+ \topofcontents
+ \line{\hfil Section\hbox to3em{\hss Page}}
+ \let\ZZ=\contentsline
+ \readcontents\relax % read the contents info
+ \botofcontents \end} % print the contents page(s) and terminate
+\def\contentsline#1#2#3#4{\ifnum#2=0 \smallbreak\fi
+ \line{\consetup{#2}#1
+ \rm\leaders\hbox to .5em{.\hfil}\hfil\ #3\hbox to3em{\hss#4}}}
+\def\consetup#1{\ifcase#1 \bf % depth -1 (@**)
+ \or % depth 0 (@*)
+ \or \hskip2em % depth 1 (@*1)
+ \or \hskip4em % depth 2 (@*2)
+ \or \hskip6em % depth 3 (@*3)
+ \or \hskip8em % depth 4 (@*4)
+ \or \hskip10em % depth 5 (@*5)
+ \else \hskip12em \fi} % depth 6 or more
+\def\noinx{\let\inx=\end} % no indexes or table of contents
+\def\nosecs{\let\FIN=\fin \def\fin{\let\parfillskip=\end \FIN}}
+ % no index of section names or table of contents
+\def\nocon{\let\con=\end} % no table of contents
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\twodigits
+\def\hours{\twodigits=\time \divide\twodigits by 60 \printtwodigits
+ \multiply\twodigits by-60 \advance\twodigits by\time :\printtwodigits}
+\def\gobbleone1{}
+\def\printtwodigits{\advance\twodigits100
+ \expandafter\gobbleone\number\twodigits
+ \advance\twodigits-100 }
+\def\TeX{{\ifmmode\it\fi
+ \leavevmode\hbox{T\kern-.1667em\lower.424ex\hbox{E}\hskip-.125em X}}}
+\def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi}
+\def\datethis{\def\startsection{\leftline{\sc\today\ at \hours}\bigskip
+ \let\startsection=\stsec\stsec}}
+ % say `\datethis' in limbo, to get your listing timestamped before section 1
+\def\datecontentspage{%
+ \def\topofcontents{\leftline{\sc\today\ at \hours}\bigskip
+ \centerline{\titlefont\title}\vfill}} % timestamps the contents page
+\def\formatoff{\gdef\F##1\par{\setbox0=\lastbox\par}} % omits output of @f
+\def\formaton{\gdef\F{\defin{format}}} % restores output of @f
+ % hack: you can say @t}\par\formaton{@> at end of an @f being suppressed
diff --git a/systems/knuth/local/lib/random.mf b/systems/knuth/local/lib/random.mf
new file mode 100644
index 0000000000..cc433a8d1f
--- /dev/null
+++ b/systems/knuth/local/lib/random.mf
@@ -0,0 +1,198 @@
+% Font for examples in Chapter 21 of The METAFONTbook
+
+font_size 10pt#;
+mode_setup;
+
+font_identifier "RANDOM";
+font_coding_scheme "PI";
+
+% uniforms (characters 0--9)
+randomseed:=3.14159;
+for code=0 upto 9:
+beginchar(code,10pt#,10pt#,0);
+pickup pencircle scaled .3pt; draw unitsquare scaled w;
+pickup pencircle scaled 1pt;
+for k=1 upto 100: drawdot(uniformdeviate w,uniformdeviate w); endfor
+endchar;
+endfor
+
+% normals (characters 10--19)
+randomseed:=3.14159;
+for code=10 upto 19:
+beginchar(code,10pt#,10pt#,0);
+pickup pencircle scaled .3pt; draw unitsquare scaled w;
+pickup pencircle scaled 1pt;
+for k=1 upto 100: drawdot(1/6w*normaldeviate+.5w,1/6w*normaldeviate+.5w); endfor
+endchar;
+endfor
+
+% both (characters 20--29)
+randomseed:=3.14159;
+for code=20 upto 29:
+beginchar(code,10pt#,10pt#,0);
+pickup pencircle scaled .3pt; draw unitsquare scaled w;
+pickup pencircle scaled 1pt;
+for k=1 upto 100: drawdot(uniformdeviate w,1/6w*normaldeviate+.5w); endfor
+endchar;
+endfor
+
+% METAFONT logo (characters 30--109)
+randomseed:=ASCII"M"+epsilon*ASCII"F";
+vardef noise=normaldeviate*craziness enddef;
+
+ht#:=6pt#;
+xgap#:=0.6pt#;
+u#:=4/9pt#;
+s#:=0;
+o#:=1/9pt#;
+px#:=2/3pt#;
+ygap#:=xgap#;
+leftstemloc#:=2.5u#+s#;
+barheight#:=.45ht#;
+define_pixels(s,u,xgap,ygap,leftstemloc,barheight);
+py#:=.9px#; define_blacker_pixels(px,py);
+pickup pencircle xscaled px yscaled py; logo_pen:=savepen;
+define_corrected_pixels(o);
+
+def beginlogochar(expr c,n) =
+ beginchar(incr code,n*u#+2s#,ht#,0); pickup logo_pen enddef;
+
+def NN=
+beginlogochar("N",15);
+x1=leftstemloc+noise;
+x2=leftstemloc+noise;
+x4=w-leftstemloc+noise;
+x5=w-leftstemloc+noise;
+bot y1=noise-o;
+top y2=h+o+noise;
+y3=y4+ygap+noise;
+bot y4=noise-o;
+top y5=h+o+noise;
+z3=whatever[z4,z5];
+draw z1--z2--z3; draw z4--z5; labels(1,2,3,4,5); endchar;
+enddef;
+
+def MM=
+beginlogochar("M",18);
+x1=leftstemloc+noise;
+x2=leftstemloc+noise;
+x3=.5w+noise;
+x4=w-leftstemloc+noise;
+x5=w-leftstemloc+noise;
+bot y1=noise-o;
+top y2=h+o+noise;
+bot y3=ygap+noise;
+top y4=h+o+noise;
+bot y5=noise-o;
+draw z1--z2--z3--z4--z5; labels(1,2,3,4,5); endchar;
+enddef;
+
+def EE=
+beginlogochar("E",14);
+x1=leftstemloc+noise;
+x3=leftstemloc+noise;
+x4=w-leftstemloc+o+noise;
+x5=w-leftstemloc+o-xgap+noise;
+x6=w-leftstemloc+o+noise;
+bot y1=noise;
+y2=barheight+noise;
+top y3=h+noise;
+top y4=h+noise;
+y5=barheight+noise;
+bot y6=noise;
+z2=whatever[z1,z3];
+draw z6--z1--z3--z4; draw z2--z5; labels(1,2,3,4,5,6); endchar;
+enddef;
+
+def FF=
+beginlogochar("F",14);
+x1=leftstemloc+noise;
+x3=leftstemloc+noise;
+x4=w-leftstemloc+o+noise;
+x5=w-leftstemloc+o-xgap+noise;
+bot y1=noise-o;
+y2=barheight+noise;
+top y3=h+noise;
+top y4=h+noise;
+y5=barheight+noise;
+bot y6=noise;
+z2=whatever[z1,z3];
+draw z1--z3--z4; draw z2--z5; labels(1,2,3,4,5); endchar;
+enddef;
+
+def TT=
+beginlogochar("T",13);
+lft x1=noise;
+rt x2=w+noise;
+x3=.5w+noise;
+x4=.5w+noise;
+top y1=h+noise;
+top y2=h+noise;
+bot y4=noise-o;
+z3=whatever[z1,z2];
+draw z1--z2; draw z3--z4; labels(1,2,3,4); endchar;
+enddef;
+
+def AA=
+beginlogochar("A",15);
+x1=.5w+noise;
+x2'=leftstemloc+noise;
+x3'=w-leftstemloc+noise;
+x4=leftstemloc+noise;
+x5=w-leftstemloc+noise;
+top y2'=h+o+noise;
+top y3'=h+o+noise;
+y2=barheight+noise;
+y3=barheight+noise;
+bot y4=noise-o;
+bot y5=noise-o;
+z1=whatever[z2',z3'];
+z2=whatever[z4,z2'];
+z3=whatever[z5,z3'];
+draw z4--z2--z3--z5;
+draw z2{z2'-z2}...z2+.8(z2'-z2)-.2(z2'-z1){z1-z2}
+ ...z1{z3'-z2'}...z3+.8(z3'-z3)-.2(z3'-z1){z3-z1}...z3{z3-z3'};
+labels(1,2,3,4,5,2',3'); endchar;
+enddef;
+
+def OO=
+beginlogochar("O",15);
+x1'=1.5u+s+noise;
+x2'=1.5u+s+noise;
+x3'=w-1.5u-s+noise;
+x4'=w-1.5u-s+noise;
+bot y1'=noise-o;
+top y2'=h+o+noise;
+top y3'=h+o+noise;
+bot y4'=noise-o;
+z1=.5[z1',z2']; z2=.5[z2',z3']; z3=.5[z3',z4']; z4=.5[z4',z1'];
+draw z2{z2'-z3'}...z2+.8(z2'-z2)-.2(z2'-z1){z1-z2}
+ ...z1{z1'-z2'}...z1+.8(z1'-z1)-.2(z1'-z4){z4-z1}
+ ...z4{z4'-z1'}...z4+.8(z4'-z4)-.2(z4'-z3){z3-z4}
+ ...z3{z3'-z4'}...z3+.8(z3'-z3)-.2(z3'-z2){z2-z3}...cycle;
+labels(1,2,3,4,5,2',3'); endchar;
+enddef;
+
+
+code:=29;
+for c=10 downto 1:craziness:=c*.05pt;
+ MM; EE; TT; AA; FF; OO; NN; TT; endfor
+
+% histogram (character 126)
+randomseed:=0;
+beginchar(126,24pt#,10pt#,0);
+%pickup pencircle scaled .3pt; draw unitsquare xscaled w yscaled h;
+ numeric count[]; pickup pencircle scaled 1pt;
+for n:=1 upto 100: x:=normaldeviate*4pt+12pt;
+y:=floor(x/pt); if unknown count[y]: count[y]:=-1; fi
+drawdot(x,pt*incr count[y]); endfor endchar;
+
+% skyline (character 127)
+randomseed:=2.71828;
+beginchar(127,100pt#,10pt#,0);
+for n:=0 upto 99:
+fill unitsquare xscaled 1pt yscaled uniformdeviate h shifted (n*pt,0);
+endfor
+endchar;
+
+bye
diff --git a/systems/knuth/local/lib/rotate.tex b/systems/knuth/local/lib/rotate.tex
new file mode 100644
index 0000000000..27891ee95e
--- /dev/null
+++ b/systems/knuth/local/lib/rotate.tex
@@ -0,0 +1,43 @@
+%
+% These macros allow you to rotate or flip a \TeX\ box. Very useful for
+% sideways tables or upsidedown answers.
+%
+% To use, create a box containing the information you want to rotate.
+% (An hbox or vbox will do.) Now call \rotr\boxnum to rotate the
+% material and create a new box with the appropriate (flipped) dimensions.
+% \rotr rotates right, \rotl rotates left, \rotu turns upside down, and
+% \rotf flips. These boxes may contain other rotated boxes.
+%
+\newdimen\rotdimen
+\def\vspec#1{\special{ps:#1}}% passes #1 verbatim to the output
+\def\rotstart#1{\vspec{gsave currentpoint currentpoint translate
+ #1 neg exch neg exch translate}}% #1 can be any origin-fixing transformation
+\def\rotfinish{\vspec{currentpoint grestore moveto}}% gets back in synch
+%
+% First, the rotation right. The reference point of the rotated box
+% is the lower right corner of the original box.
+%
+\def\rotr#1{\rotdimen=\ht#1\advance\rotdimen by\dp#1%
+ \hbox to\rotdimen{\hskip\ht#1\vbox to\wd#1{\rotstart{90 rotate}%
+ \box#1\vss}\hss}\rotfinish}
+%
+% Next, the rotation left. The reference point of the rotated box
+% is the upper left corner of the original box.
+%
+\def\rotl#1{\rotdimen=\ht#1\advance\rotdimen by\dp#1%
+ \hbox to\rotdimen{\vbox to\wd#1{\vskip\wd#1\rotstart{270 rotate}%
+ \box#1\vss}\hss}\rotfinish}%
+%
+% Upside down is simple. The reference point of the rotated box
+% is the upper right corner of the original box. (The box's height
+% should be the current font's xheight, \fontdimen5\font,
+% if you want that xheight to be at the baseline after rotation.)
+%
+\def\rotu#1{\rotdimen=\ht#1\advance\rotdimen by\dp#1%
+ \hbox to\wd#1{\hskip\wd#1\vbox to\rotdimen{\vskip\rotdimen
+ \rotstart{-1 dup scale}\box#1\vss}\hss}\rotfinish}%
+%
+% And flipped end for end is pretty ysae too. We retain the baseline.
+%
+\def\rotf#1{\hbox to\wd#1{\hskip\wd#1\rotstart{-1 1 scale}%
+ \box#1\hss}\rotfinish}%
diff --git a/systems/knuth/local/lib/slantaps4.mf b/systems/knuth/local/lib/slantaps4.mf
new file mode 100644
index 0000000000..1d7ecbd492
--- /dev/null
+++ b/systems/knuth/local/lib/slantaps4.mf
@@ -0,0 +1,11 @@
+% slant font for slope 1/4 on the APS
+
+if mode<>aps: errmessage "This file is for aps only"; fi
+
+s=1/4;
+n=30;
+r#=.4;
+u=4; % an approximation to r in pixels
+
+font_identifier "SLANTAPS4";
+input slant
diff --git a/systems/knuth/local/lib/slantimagen4.mf b/systems/knuth/local/lib/slantimagen4.mf
new file mode 100644
index 0000000000..ffe19c7b90
--- /dev/null
+++ b/systems/knuth/local/lib/slantimagen4.mf
@@ -0,0 +1,11 @@
+% slant font for slope 1/4 on the Imagen
+
+if mode<>imagen: errmessage "This file is for imagen only"; fi
+
+s=1/4;
+n=30;
+r#=.4;
+u=2; % an approximation to r in pixels
+
+font_identifier "SLANTIMAGEN4";
+input slant
diff --git a/systems/knuth/local/lib/slantimagen6.mf b/systems/knuth/local/lib/slantimagen6.mf
new file mode 100644
index 0000000000..3859b471fd
--- /dev/null
+++ b/systems/knuth/local/lib/slantimagen6.mf
@@ -0,0 +1,11 @@
+% slant font for slope 1/6 on the Imagen
+
+if mode<>imagen: errmessage "This file is for imagen only"; fi
+
+s=1/6;
+n=30;
+r#=.4;
+u=2; % an approximation to r in pixels
+
+font_identifier "SLANTIMAGEN6";
+input slant
diff --git a/systems/knuth/local/lib/slantlino4.mf b/systems/knuth/local/lib/slantlino4.mf
new file mode 100644
index 0000000000..02ef790a03
--- /dev/null
+++ b/systems/knuth/local/lib/slantlino4.mf
@@ -0,0 +1,11 @@
+% slant font for slope 1/4 on the Linotronic
+
+if mode<>linohi: errmessage "This file is for lino only"; fi
+
+s=1/4;
+n=30;
+r#=.4;
+u=7; % an approximation to r in pixels
+
+font_identifier "SLANTLINO4";
+input slant
diff --git a/systems/knuth/local/lib/slantlj4.mf b/systems/knuth/local/lib/slantlj4.mf
new file mode 100644
index 0000000000..53264d0536
--- /dev/null
+++ b/systems/knuth/local/lib/slantlj4.mf
@@ -0,0 +1,11 @@
+% slant font for slope 1/4 on the LaserJet
+
+if mode<>ljfour: errmessage "This file is for LaserJet only"; fi
+
+s=1/4;
+n=30;
+r#=.4;
+u=3; % an approximation to r in pixels
+
+font_identifier "SLANTLJ4";
+input slant
diff --git a/systems/knuth/local/lib/slist.tex b/systems/knuth/local/lib/slist.tex
new file mode 100644
index 0000000000..26ec4d5fbe
--- /dev/null
+++ b/systems/knuth/local/lib/slist.tex
@@ -0,0 +1,49 @@
+% Makes a small-print listing of one or more files
+% Typical usage:
+% tex slist *.c \\end
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex8
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\baselineskip=9pt minus 1pt
+\medskipamount=4pt minus 1pt
+\raggedbottom
+\nopagenumbers
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\filenamefont#1\quad\headlinefont \today\ at \hours
+ \hfill Page \folio}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus 1in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-500\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 103 characters on a line without an overfull box.
diff --git a/systems/knuth/local/lib/snfont.mf b/systems/knuth/local/lib/snfont.mf
new file mode 100644
index 0000000000..dd50508dff
--- /dev/null
+++ b/systems/knuth/local/lib/snfont.mf
@@ -0,0 +1,165 @@
+% special font for Surreal Numbers "Conway Stone"
+
+u#:=1mm#/2.5; % basic unit, corresponds to 1mm after 250% magnification
+mode_setup;
+define_pixels(u);
+
+vardef nd= u*normaldeviate enddef;
+randomseed:=3.14;
+
+def .... = .. tension1.2 .. enddef;
+def ..... = .. tension2 .. enddef;
+
+def zerochar(expr code) =
+ beginchar(code,(10+normaldeviate)*u#,10u#,0);
+ z0=(.5w,5u);
+ for j=1 upto 5:
+ z[j]=z0+((0,4u+.125nd) rotated (j*72+8normaldeviate));
+ endfor
+ fill z1....z2....z3....z4....z5....cycle;
+ labels(0,1,2,3,4,5);
+ endchar;
+enddef;
+
+def colonchar(expr code) =
+ beginchar(code,(5.5+normaldeviate)*u#,10u#,0);
+ z0=(.5w,3u); z10=(.5w,8u);
+ for j=1 upto 3:
+ z[j]=z0+((0,1.75u+.075nd) rotated (j*120+8normaldeviate));
+ z[10+j]=z10+((0,1.75u+.075nd) rotated (60+j*120+8normaldeviate));
+ endfor
+ fill z1....z2....z3....cycle;
+ fill z11....z12....z13....cycle;
+ labels(0,1,2,3,10,11,12,13);
+ endchar;
+enddef;
+
+def horizchar(expr code) =
+ beginchar(code,(13+normaldeviate)*u#,10u#,0);
+ z1=(2u+nd/4,5u+nd/10);
+ z2=(w-2u+nd/4,5u+nd/10);
+ penpos1(1.5u,90);
+ penpos2(1.5u,90);
+ z10r=z1r+(nd/2,nd/8);
+ z10l=z1l+(nd/2,nd/8);
+ z10=(.5+uniformdeviate.3)[z10l,z10r]-(abs nd,0);
+ z20l=z2l+(nd/2,nd/8);
+ z20r=z2r+(nd/2,nd/8);
+ z20=(.5+uniformdeviate.3)[z20l,z20r]+(abs nd,0);
+ pickup pencircle scaled u;
+ filldraw z10r{dir(180+10normaldeviate)}...z10...z10l{dir 10normaldeviate}
+ ....z20l{dir 10normaldeviate}....z20....z20r{dir(180+10normaldeviate)}
+ ....cycle;
+ penlabels(1,2,10,20);
+ endchar;
+enddef;
+
+def equalchar(expr code) =
+ beginchar(code,(15+normaldeviate)*u#,10u#,0);
+ z1=(4.5u+nd/4,6u+nd/10);
+ z2=(w-4.5u+nd/4,6u+nd/10);
+ penpos1(u,90);
+ penpos2(u,90);
+ z10r=z1r+(nd/2,nd/8);
+ z10l=z1l+(nd/2,nd/8);
+ z10=(.5+uniformdeviate.3)[z10l,z10r]-(abs nd,0);
+ z20l=z2l+(nd/2,nd/8);
+ z20r=z2r+(nd/2,nd/8);
+ z20=(.5+uniformdeviate.3)[z20l,z20r]+(abs nd,0);
+ pickup pencircle scaled .5u;
+ filldraw z10r{dir(180+5normaldeviate)}...z10...z10l{dir 5normaldeviate}
+ ....z20l{dir 5normaldeviate}....z20....z20r{dir(180+5normaldeviate)}
+ ....cycle;
+ z3=(4.5u+nd/4,2.5u+nd/10);
+ z4=(w-4.5u+nd/4,2.5u+nd/10);
+ penpos3(u,90);
+ penpos4(u,90);
+ z30r=z3r+(nd/2,nd/8);
+ z30l=z3l+(nd/2,nd/8);
+ z30=(.5+uniformdeviate.3)[z30l,z30r]-(abs nd,0);
+ z40l=z4l+(nd/2,nd/8);
+ z40r=z4r+(nd/2,nd/8);
+ z40=(.5+uniformdeviate.3)[z40l,z40r]+(abs nd,0);
+ filldraw z30r{dir(180+5normaldeviate)}...z30...z30l{dir 5normaldeviate}
+ ....z40l{dir 5normaldeviate}....z40....z40r{dir(180+5normaldeviate)}
+ ....cycle;
+ penlabels(1,2,3,4,10,20,30,40);
+ endchar;
+enddef;
+
+def vertchar(expr code) =
+ beginchar(code,(5.5+normaldeviate)*u#,10u#,0);
+ z1=(.5w+nd/10,9u+nd/4);
+ z2=(.5w+nd/10,u+nd/4);
+ penpos1(1.8u,0);
+ penpos2(1.8u,0);
+ z10r=z1r+(nd/8,nd/2);
+ z10l=z1l+(nd/8,nd/2);
+ z10=(.5+uniformdeviate.3)[z10l,z10r]+(0,abs nd);
+ z20l=z2l+(nd/8,nd/2);
+ z20r=z2r+(nd/8,nd/2);
+ z20=(.5+uniformdeviate.3)[z20l,z20r]-(0,abs nd);
+ pickup pencircle scaled .6u;
+ filldraw z10r{dir(90+10normaldeviate)}....z10....z10l{dir-(90+10normaldeviate)}
+ ....z20l{dir-(90+10normaldeviate)}....z20....z20r{dir(90+10normaldeviate)}
+ ....cycle;
+ penlabels(1,2,10,20);
+ endchar;
+enddef;
+
+def leftchar(expr code) =
+ beginchar(code,(10+normaldeviate)*u#,10u#,0);
+ z1=(w-2u+nd/16,11u+nd/8);
+ z0=z2=(3u+nd/16,5u+nd/16);
+ z3=(w-2u+nd/16,-u+nd/8);
+ penpos1(u,-35+10normaldeviate);
+ penpos0(u,-35);
+ penpos3(u,35+10normaldeviate);
+ penpos2(u,35);
+ z4=whatever[z0l,z1l]=whatever[z2l,z3l];
+ pickup pencircle scaled u;
+ filldraw z1l{(z0l-z1l)rotated 5normaldeviate}....z4--z0r
+ ....z1r{(z1r-z0r)rotated 5normaldeviate}.....cycle;
+ filldraw z3l{(z2l-z3l)rotated 5normaldeviate}....z4--z2r
+ ....z3r{(z3r-z2r)rotated 5normaldeviate}.....cycle;
+ penlabels(0,1,2,3,4);
+ endchar;
+enddef;
+
+def rightchar(expr code) =
+ beginchar(code,(10+normaldeviate)*u#,10u#,0);
+ z1=(2u+nd/16,11u+nd/8);
+ z0=z2=(w-3u+nd/16,5u+nd/16);
+ z3=(2u+nd/16,-u+nd/8);
+ penpos1(u,35+10normaldeviate);
+ penpos0(u,35);
+ penpos3(u,-35+10normaldeviate);
+ penpos2(u,-35);
+ z4=whatever[z0r,z1r]=whatever[z2r,z3r];
+ pickup pencircle scaled u;
+ filldraw z1r{(z0r-z1r)rotated 5normaldeviate}....z4--z0l
+ ....z1l{(z1l-z0l)rotated 5normaldeviate}.....cycle;
+ filldraw z3r{(z2r-z3r)rotated 5normaldeviate}....z4--z2l
+ ....z3l{(z3l-z2l)rotated 5normaldeviate}.....cycle;
+ penlabels(0,1,2,3,4);
+ endchar;
+enddef;
+
+for r=0 upto 9:
+ zerochar(10+r);
+ colonchar(20+r);
+ horizchar(30+r);
+ equalchar(40+r);
+ vertchar(50+r);
+ leftchar(60+r);
+ rightchar(70+r);
+endfor
+
+font_quad:=10u#;
+font_normal_space:=5u#;
+font_normal_stretch:=3u#;
+font_normal_shrink:=2u#;
+font_identifier:="SNFONT";
+font_coding_scheme:="PI";
+
+end.
diff --git a/systems/knuth/local/lib/tinywlist.tex b/systems/knuth/local/lib/tinywlist.tex
new file mode 100644
index 0000000000..cebc29e0a4
--- /dev/null
+++ b/systems/knuth/local/lib/tinywlist.tex
@@ -0,0 +1,53 @@
+% Makes a landscape listing of one or more files with wide lines
+% Typical usage:
+% tex list *.c \\end
+
+\hsize=9in
+\vsize=6.5in
+\special{landscape}
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex8 scaled 800
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\baselineskip=7pt
+\medskipamount=4pt minus 1pt
+\raggedbottom
+\nopagenumbers
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\filenamefont#1\quad\headlinefont \today\ at \hours
+ \hfill Page \folio}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus 1in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-500\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 170 characters on a line without an overfull box.
diff --git a/systems/knuth/local/lib/twimac.tex b/systems/knuth/local/lib/twimac.tex
new file mode 100644
index 0000000000..99905f168c
--- /dev/null
+++ b/systems/knuth/local/lib/twimac.tex
@@ -0,0 +1,400 @@
+% Special formatting for Computers and Typesetting: The Programs
+
+\xdef\fmtversion{\fmtversion+TWIMAC}
+
+\newif\ifsorted
+\newread\sreffile \openin\sreffile=\jobname.sref
+\newwrite\reffile
+\ifeof\sreffile \immediate\openout\reffile=\jobname.ref \sortedfalse
+ \message{This is the first pass!}
+\else \sortedtrue \message{This is the second pass!} \fi
+
+\newdimen\em \em=10pt
+\parskip 0pt % no stretch between paragraphs
+\parindent 1\em % for paragraphs and for the first line of Pascal text
+
+\font\manual=manfnt
+\font\ninerm=cmr9
+\font\eightrm=cmr8
+\font\sixrm=cmr6
+\font\ninei=cmmi9
+\font\eighti=cmmi8
+\font\sixi=cmmi6
+\skewchar\ninei='177 \skewchar\eighti='177 \skewchar\sixi='177
+\font\ninesy=cmsy9
+\font\eightsy=cmsy8
+\font\sixsy=cmsy6
+\skewchar\ninesy='60 \skewchar\eightsy='60 \skewchar\sixsy='60
+\font\ninebf=cmbx9
+\font\eightbf=cmbx8
+\font\sixbf=cmbx6
+\font\ninett=cmtt9
+\font\eighttt=cmtt8
+\hyphenchar\ninett=-1 \hyphenchar\eighttt=-1
+\font\ninesl=cmsl9
+\font\eightsl=cmsl8
+\font\nineit=cmti9
+\font\eightit=cmti8
+
+\newif\iftenpoint
+\def\tenpoint{\tenpointtrue
+ \def\rm{\fam0\tenrm}%
+ \textfont0=\tenrm \scriptfont0=\sevenrm \scriptscriptfont0=\fiverm
+ \textfont1=\teni \scriptfont1=\seveni \scriptscriptfont1=\fivei
+ \textfont2=\tensy \scriptfont2=\sevensy \scriptscriptfont2=\fivesy
+ \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+ \def\it{\fam\itfam\tenit}%
+ \textfont\itfam=\tenit
+ \def\sl{\fam\slfam\tensl}%
+ \textfont\slfam=\tensl
+ \def\bf{\fam\bffam\tenbf}%
+ \textfont\bffam=\tenbf \scriptfont\bffam=\sevenbf
+ \scriptscriptfont\bffam=\fivebf
+ \def\tt{\fam\ttfam\tentt}%
+ \textfont\ttfam=\tentt
+ \def\ttx{\tentex}%
+ \normalbaselineskip=12pt
+ \def\MF{{\manual META}\-{\manual FONT}}%
+ \let\sc=\eightrm
+ \let\big=\tenbig
+ \setbox\strutbox=\hbox{\vrule height8pt depth3pt width 0pt}%
+ \normalbaselines\rm}
+
+\def\ninepoint{\tenpointfalse
+ \def\rm{\fam0\ninerm}%
+ \textfont0=\ninerm \scriptfont0=\sixrm \scriptscriptfont0=\fiverm
+ \textfont1=\ninei \scriptfont1=\sixi \scriptscriptfont1=\fivei
+ \textfont2=\ninesy \scriptfont2=\sixsy \scriptscriptfont2=\fivesy
+ \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+ \def\it{\fam\itfam\nineit}%
+ \textfont\itfam=\nineit
+ \def\sl{\fam\slfam\ninesl}%
+ \textfont\slfam=\ninesl
+ \def\bf{\fam\bffam\ninebf}%
+ \textfont\bffam=\ninebf \scriptfont\bffam=\sixbf
+ \scriptscriptfont\bffam=\fivebf
+ \def\tt{\fam\ttfam\ninett}%
+ \textfont\ttfam=\ninett
+ \def\ttx{\ninetex}%
+ \normalbaselineskip=11pt
+ \def\MF{{\manual hijk}\-{\manual lmnj}}%
+ \let\sc=\sevenrm
+ \let\big=\ninebig
+ \setbox\strutbox=\hbox{\vrule height8pt depth3pt width 0pt}%
+ \normalbaselines\rm}
+
+\def\eightpoint{%
+ \def\rm{\fam0\eightrm}%
+ \textfont0=\eightrm \scriptfont0=\sixrm \scriptscriptfont0=\fiverm
+ \textfont1=\eighti \scriptfont1=\sixi \scriptscriptfont1=\fivei
+ \textfont2=\eightsy \scriptfont2=\sixsy \scriptscriptfont2=\fivesy
+ \textfont3=\tenex \scriptfont3=\tenex \scriptscriptfont3=\tenex
+ \def\it{\fam\itfam\eightit}%
+ \textfont\itfam=\eightit
+ \def\sl{\fam\slfam\eightsl}%
+ \textfont\slfam=\eightsl
+ \def\bf{\fam\bffam\eightbf}%
+ \textfont\bffam=\eightbf \scriptfont\bffam=\sixbf
+ \scriptscriptfont\bffam=\fivebf
+ \def\tt{\fam\ttfam\eighttt}%
+ \textfont\ttfam=\eighttt
+ \def\ttx{\eighttex}%
+ \normalbaselineskip=9pt
+ \def\MF{{\manual opqr}\-{\manual stuq}}%
+ \let\sc=\sixrm
+ \let\big=\eightbig
+ \setbox\strutbox=\hbox{\vrule height7pt depth2pt width 0pt}%
+ \normalbaselines\rm}
+
+\def\tenbig#1{{\hbox{$\left#1\vbox to8.5pt{}\right.\nulldelimiterspace=0pt$}}}
+\def\ninebig#1{{\hbox{$\textfont0=\tenrm\textfont2=\tensy
+ \left#1\vbox to7.25pt{}\right.\nulldelimiterspace=0pt$}}}
+\def\eightbig#1{{\hbox{$\textfont0=\ninerm\textfont2=\ninesy
+ \left#1\vbox to6.5pt{}\right.\nulldelimiterspace=0pt$}}}
+
+\font\tentex=cmtex10
+\font\ninetex=cmtex9 % TeX extended character set (used in strings)
+\font\eighttex=cmtex8
+
+\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers
+\def\|#1{\hbox{$#1$}} % one-letter identifiers look a bit better this way
+\def\&#1{\hbox{\bf#1\/}} % boldface type for reserved words
+\def\.#1{\hbox{\ttx % typewriter type for strings
+ \let\\=\BS % backslash in a string
+ \let\'=\RQ % right quote in a string
+ \let\`=\LQ % left quote in a string
+ \let\{=\LB % left brace in a string
+ \let\}=\RB % right brace in a string
+ \let\~=\TL % tilde in a string
+ \let\ =\SP % space in a string
+ \let\_=\UL % underline in a string
+ \let\&=\AM % ampersand in a string
+ #1}}
+\def\#{\hbox{\tt\char`\#}} % parameter sign
+\def\${\hbox{\tt\char`\$}} % dollar sign
+\def\%{\hbox{\tt\char`\%}} % percent sign
+\def\^{\ifmmode\mathchar"222 \else\char`^ \fi} % pointer or hat
+% circumflex accents can be obtained from \^^D instead of \^
+\def\AT!{@} % at sign for control text
+\def\pct!{{\char`\%}} % percent sign in ordinary text
+
+\chardef\AM=`\& % ampersand character in a string
+\chardef\BS=`\\ % backslash in a string
+\chardef\LB=`\{ % left brace in a string
+\def\LQ{{\tt\char'22}} % left quote in a string
+\chardef\RB=`\} % right brace in a string
+\def\RQ{{\tt\char'23}} % right quote in a string
+\def\SP{{\tt\char`\ }} % (visible) space in a string
+\chardef\TL=`\~ % tilde in a string
+\chardef\UL=`\_ % underline character in a string
+
+\newbox\bak \setbox\bak=\hbox to -\em{} % backspace one em
+\newbox\bakk\setbox\bakk=\hbox to -2\em{} % backspace two ems
+
+\newcount\ind % current indentation in ems
+\def\1{\global\advance\ind by1\hangindent\ind\em} % indent one more notch
+\def\2{\global\advance\ind by-1} % indent one less notch
+\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement
+\def\4{\copy\bak} % backspace one notch
+\def\5{\hfil\penalty-1\hfilneg\kern2.5\em\copy\bakk\ignorespaces}%optional break
+\def\6{\ifmmode\else\par % forced break
+ \hangindent\ind\em\noindent\kern\ind\em\copy\bakk\ignorespaces\fi}
+\def\7{\Y\6} % forced break and a little extra space
+
+\let\yskip=\smallskip
+\def\to{\mathrel{.\,.}} % double dot, used only in math mode
+\def\ellipsis{\kern5\em\smash{\vdots}\qquad\vbox to12pt{}\par}
+\def\note#1#2.{\par\penalty5000
+ \Y\noindent{\hangindent2\em\baselineskip10pt\eightrm#1 #2.\par}}
+\def\startsection{\Q\noindent\strut{\bf\modno.\quad}}
+\def\defin#1{\global\advance\ind by 2 \1\&{#1 }} % begin `define' or `format'
+\def\A{\note{See also}} % cross-reference for multiply defined section names
+\def\B{\mathopen{\.{@\{}}} % begin controlled comment
+\def\C#1{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % Pascal comments
+ \XX\hfil\penalty-1\hfilneg\quad$\{\,$#1$\,\}$\XX}
+\def\D{\defin{define}} % macro definition
+\def\E{\cdot10^} % exponent in floating point constant
+\def\F{\defin{format}} % format definition
+\let\G=\ge % greater than or equal sign
+\def\H#1{\hbox{\rm\char"7D\tt#1}} % hexadecimal constant
+\let\I=\ne % unequal sign
+\def\J{\.{@\&}} % TANGLE's join operation
+\let\K=\gets % left arrow
+\let\L=\le % less than or equal sign
+\outer\def\M#1.{\def\modno{#1}\startsection\ignorespaces}
+\outer\def\N#1. \[#2]#3.{\def\modno{#1}\flushout % begin starred section
+ \gdef\rhead{PART #2:\uppercase{#3}} % define running headline
+ \global\let\rrhead=\rhead
+ \message{*\modno} % progress report
+ \startsection{\bf\ignorespaces#3.\quad}\ignorespaces}
+\outer\def\NN#1. \[#2]#3.{\def\modno{#1} % begin starred section on rh page
+ \global\setbox\partialpage=\vbox{
+ \vbox to 44pc{\box\partialpage\vfill}\break}
+ \gdef\rrhead{PART #2:\uppercase{#3}} % define running headline
+ \message{!\modno} % progress report
+ \startsection{\bf\ignorespaces#3.\quad}\ignorespaces}
+\def\O#1{\hbox{\rm\char'23\kern-.2em\it#1\/\kern.05em}} % octal constant
+\def\P{\iftenpoint \ninepoint\fi
+ \rightskip=0pt plus 100pt minus 10pt % go into Pascal mode
+ \sfcode`;=3000
+ \pretolerance 10000
+ \hyphenpenalty 10000 \exhyphenpenalty 10000
+ \global\ind=2 \1\ \unskip}
+\def\Q{\tenpoint
+ \rightskip=0pt % get out of Pascal mode
+ \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 }
+\let\R=\lnot % logical not
+\let\S=\equiv % equivalence sign
+\def\T{\mathclose{\.{@\}}}} % terminate controlled comment
+\def\U{\note{This code is used in}} % cross-reference for uses of sections
+\let\V=\lor % logical or
+\let\W=\land % logical and
+\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % section name
+ \XX$\langle\,$#2{\sevenrm\kern.5em#1}$\,\rangle$\XX}
+\def\Y{\par\yskip}
+\let\Z=\let % now you can \send the control sequence \Z
+\def\){\hbox{\.{@\$}}} % sign for string pool check sum
+\def\]{\hbox{\.{@\\}}} % sign for forced line break
+\def\=#1{\kern2pt\hbox{\vrule\vtop{\vbox{\hrule
+ \hbox{\strut\kern2pt\.{#1}\kern2pt}}
+ \hrule}\vrule}\kern2pt} % verbatim string
+\let\~=\ignorespaces
+
+\newcount\nrefs % total number of references in partial page
+\newcount\baseno % smallest module number in partial page
+\countdef\nprefs=199 % the number in \prefs
+\countdef\nfrefs=220 % the number in \frefs
+\toksdef\prefs=199 \toksdef\frefs=220
+% \count and \toks registers 200--219 are also used to keep track of refs
+\newcount\nn \newcount\mm \newcount\pp
+\newdimen\nsize \newdimen\msize
+\newskip\intermodskip \intermodskip=8pt plus 2pt minus 3pt
+\newbox\partialpage
+\newbox\newmod
+\newtoks\newrefs
+\newdimen\ninept \ninept=9pt
+
+\def\uline{_}
+\begingroup\catcode`\_=\active \globaldefs=1
+\def\flushout{\ifvoid\partialpage\else
+ \setbox0=\vsplit\partialpage to 44pc
+ \shipout\vbox{
+ \vbox to 3pc{
+ \hbox to 35pc{\vbox to10pt{}\nineit\rhead\hfil\eightrm\folio}\vfill}
+ \nointerlineskip\box0}
+ \global\advance\pageno 1
+ \let\=\makeref
+ \ifsorted \let\readin=\readln \readrefs
+ \else \immediate\write\reffile{!\the\pageno}\let\readin=\readrefs \fi
+ \setbox0=\vbox{\eightpoint \hsize=11pc \catcode`\_=\active \let_=\_
+ \rightskip=0pt plus 100pt minus 10pt
+ \pretolerance 10000
+ \hyphenpenalty 10000 \exhyphenpenalty 10000
+ \noindent\vbox to1pt{}\par % 1pt = \topskip - \ninept
+ \readin}
+ \shipout\vbox to47pc{
+ \vbox to 3pc{
+ \hbox to 35pc{\vbox to10pt{}\eightrm\folio\hfil\nineit\rrhead\/}\vfill}
+ \unvbox\partialpage
+ \vfill
+% \ifdim\ht0>1pt \kern8pt\hrule \hbox{%
+ \ifdim\ht0>1pt \kern11pt\hrule \hbox{% % changed Oct 87 by DEK!
+ \nsize=\ht0 \advance\nsize-\topskip
+ \divide\nsize by 3 \divide\nsize by\ninept
+ \multiply\nsize by\ninept \advance\nsize\topskip
+ \vsplit0 to\nsize \kern1pc
+ \msize=\ht0 \advance\msize-\topskip
+ \divide\msize by 2 \divide\msize by\ninept
+ \multiply\msize by\ninept \advance\msize\topskip
+ \vbox to\nsize{\vsplit0 to\msize\vss}\kern1pc
+ \vbox to\nsize{\box0\vss}} \fi}
+ \global\advance\pageno 1 \global\let\rhead=\rrhead\fi
+ {\globaldefs=1
+ \nn=199 \loop \ifnum\nn<221 \toks\nn={} \count\nn=0 \advance\nn 1 \repeat
+ \nrefs=0
+ \baseno=\modno}}
+
+\def\{\begingroup \catcode`\_=\active \let_=\uline \globaldefs=1
+ \newrefs=\bgroup}
+\def\FI{\nn=\modno \advance\nn-\baseno \advance\nn 200
+ \advance\nrefs-\count\nn \nn=\nrefs
+ \let\=\countnewref \the\newrefs \endgroup
+ \nobreak\null\nobreak\vskip-\baselineskip\penalty-500\vfil\eject}
+\def\twillout{\setbox\newmod=\vbox{\unvbox255}
+ \ifnum\nn=0 \nsize=0pt
+% \else \nsize=3pt \multiply\nsize\nn \advance\nsize 1pc \fi
+ \else \nsize=3pt \multiply\nsize\nn \advance\nsize 1.25pc \fi % changed Oct 87!
+ \ifvoid\partialpage\else\advance\nsize 5pt \advance\nsize\ht\partialpage\fi
+ \advance\nsize\ht\newmod
+ \message{\the\nsize} % show the page break decision amount
+ \ifdim\nsize>88pc \flushout \message{\modno}\fi
+ \addtopartialpage}
+\def\addtopartialpage{\let\=\addnewref \catcode`\_=\active \let_=\uline
+ \globaldefs=1 \the\newrefs
+ \nsize=\dp\partialpage
+ \setbox\partialpage=\ifvoid\partialpage \box\newmod
+ \else \vbox{\unvbox\partialpage \vfilneg
+ \vskip\intermodskip \unvbox\newmod}\fi}
+\endgroup
+\def\shortpage{\par\vskip-2\baselineskip
+ \null\vfill\penalty-5000\message{(shortpage)}}
+
+\def\countnewref #1 #2 {\ifnum#2<\baseno \let\next\cn
+ \else\ifnum#2>\modno \let\next\cn \else\let\next\flushref\fi\fi\next#1 #2 }
+\def\cn #1 #2 {\expandafter\cnr\csname#1 #2\endcsname #2 }
+\def\cnr#1#2 #3 {\ifx#1\relax \advance\nn 1 \fi}
+\def\flushref#1 #2 #3 {}
+\def\addnewref #1 #2 {\ifnum#2<\baseno \let\next\an \nn=199
+ \else\ifnum#2>\modno \let\next\an \nn=#2 \advance\nn-\baseno
+ \advance\nn200 \ifnum\nn>219 \nn=220 \fi
+ \else\let\next\flushref\fi\fi\next#1 #2 }
+\def\an #1 #2 {\expandafter\anr\csname#1 #2\endcsname #1 #2 }
+\def\anr#1#2 #3 #4 {\ifx#1\relax
+ \advance\nrefs1 \advance\count\nn 1
+ \def#1{\#2 #3 #4 }\toks\nn=\expandafter{\the\toks\nn\#1}\fi}
+\ifsorted \def\#1{\global\let#1\relax}
+\else \def\#1{#1\global\let#1\relax} \fi
+\def\makeref #1 #2 #3#4 {\nn=#2 \hangindent=1em
+ \ifsorted\else\toks0={#1 #2 #3#4}\immediate\write\reffile{+ \the\toks0}\fi
+ \noindent\\{#1}\ifx#3\\else
+ \if#3:: \else\unhcopy\eqbox \fi#4\fi, \section\number\nn.\par}
+\newbox\eqbox \setbox\eqbox=\hbox{\kern2pt\eightrm=\kern2pt}
+\def\section{\mathhexbox278}
+\def\{\}
+\def\readrefs{\the\prefs \mm=\modno \advance\mm-\baseno \advance\mm200
+ {\def\##1{\global\let##1\relax}\pp=200 % this patch made AFTER first edition
+ \loop\ifnum\pp<\mm \the\toks\pp \advance\pp 1 \repeat} % of Vol B!!
+ \loop\ifnum\mm<221 \the\toks\mm \advance\mm 1 \repeat}
+\def\readln{\read\sreffile to\next \expandafter\next\space \readin}
+\def\donewithpage#1 {%\ifnum\pageno=#1 \else\message{page #1??}\fi
+ \let\readin=\relax}
+\def\rhead{BLANK PAGE TO THROW AWAY}
+
+\hsize=35pc \vsize=\maxdimen \output={\twillout}
+
+% the remaining stuff applies only to the rear matter
+
+\newif\ifpagesaved
+\def\lheader{\vbox to10pt{}\nineit\rhead\hfil\eightrm\folio}
+\def\rheader{\vbox to10pt{}\eightrm\folio\hfil\nineit\rhead\/}
+\def\page{\box255 }
+\def\normaloutput#1{\shipout\vbox{
+ \vbox to 3pc{
+ \hbox to 35pc{\ifodd\pageno\rheader\else\lheader\fi}\vfill}
+ \nointerlineskip#1}
+ \global\advance\pageno 1 }
+
+\newbox\sbox % saved box preceding the index
+\newbox\lbox % lefthand column in the index
+\def\inx{\box\partialpage
+ \vskip24pt plus 1fil % we are beginning the index
+ \output{\ifpagesaved\normaloutput{\box\sbox}\fi
+ \global\setbox\sbox=\page \global\pagesavedtrue}
+ \pagesavedfalse \eject % eject the page-so-far and predecessors
+ \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box
+ \vsize=44pc \advance\vsize by -\ht\sbox % the remaining height
+ \hsize=17pc % column width for the index (1pc between cols)
+ \eightpoint
+ \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
+ \def\lr{L} % this tells whether the left or right column is next
+ \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
+ \else\normaloutput{\vbox to44pc{\box\sbox\vss
+ \hbox to35pc{\box\lbox\hfil\page}}}
+ \global\vsize=44pc\gdef\lr{L}\global\pagesavedfalse\fi}
+ \message{Index:}
+ \parskip 0pt plus .5pt
+ \gdef\rhead{INDEX}
+ \outer\def\:##1, {\par\hangindent2em\noindent##1:\kern1em} % index entry
+ \let\ttentry=\. \def\.##1{\ttentry{##1\kern.2em}} % give \tt a little room
+ \def\[##1]{$\underline{##1}$} % underlined index item
+ \rm \rightskip0pt plus 2.5em \tolerance 10000
+ \hyphenpenalty 10000 \parindent0pt}
+\def\fin{\par\vfill\eject % this is done when we are ending the index
+ \ifpagesaved\null\vfill\eject\fi % output a null index column
+ \if L\lr\else\null\vfill\eject\fi % finish the current page
+ \parfillskip 0pt plus 1fil
+ \gdef\rhead{NAMES OF THE SECTIONS}
+ \message{Section names:}
+ \output{\normaloutput\page}
+ \ninepoint
+ \hsize=35pc \vsize=44pc
+ \def\note##1##2.{\hfil\penalty-1\hfilneg\quad{\eightrm##1 ##2.}}
+ \linepenalty=10 % try to conserve lines
+ \def\U{\note{Used in}} % cross-reference for uses of sections
+ \def\:{\par\hangindent 2em}\let\*=*\let\.=\ttentry}
+\def\con{\par\vfill\eject\end} % finish the section names
+
+% To produce only a subset of pages, put the page numbers on separate
+% lines in a file called pages.tex
+\let\Shipout=\shipout
+\newread\pages \newcount\nextpage \openin\pages=pages
+\def\getnextpage{\ifeof\pages\else
+ {\endlinechar=-1\read\pages to\next
+ \ifx\next\empty % in this case we should have eof now
+ \else\global\nextpage=\next\fi}\fi}
+\ifeof\pages\else\message{OK, I'll ship only the requested pages!}
+ \getnextpage\fi
+\def\shipout{\ifeof\pages\let\next=\Shipout
+ \else\ifnum\pageno=\nextpage\getnextpage\let\next=\Shipout
+ \else\let\next=\Tosspage\fi\fi \next}
+\newbox\garbage \def\Tosspage{\deadcycles=0\setbox\garbage=}
diff --git a/systems/knuth/local/lib/unicode.tex b/systems/knuth/local/lib/unicode.tex
new file mode 100644
index 0000000000..3453c2c251
--- /dev/null
+++ b/systems/knuth/local/lib/unicode.tex
@@ -0,0 +1,25 @@
+% macros to typeset with unicode in a rudimentary way
+
+% There's a program "unichar" which produces TeX code for typesetting
+% a given Unicode character, provided that one uses these macros.
+% At present, the character is assumed to have width but no height;
+% struts should be used in case extremely tall or deep characters appear.
+
+% Say "\input unicode" at the top of your file, and change \unicodeptsize
+% to whatever point size you are using (if it is not 10pt, the default).
+
+\newdimen\unicodeptsize \unicodeptsize=10pt
+
+\begingroup
+\catcode`@=11 % we will define two private macro names
+
+\gdef\Uni#1:#2:#3:#4:#5<#6>% ems:cols:rows:-hoff:rows+voff<hexbitmap>
+ {\leavevmode \hbox to#1\unicodeptsize
+ {\special{" 0 0 moveto currentpoint translate
+ \unic@deptsize \unic@deptsize scale #2 #3 true
+ [24 0 0 -24 #4 #5] {<#6>} imagemask}\hss}}
+
+{\catcode`p=12\catcode`t=12\gdef\uni@ff#1pt{#1}}
+\gdef\unic@deptsize{\expandafter\uni@ff\the\unicodeptsize\space}
+
+\endgroup
diff --git a/systems/knuth/local/lib/wlist.tex b/systems/knuth/local/lib/wlist.tex
new file mode 100644
index 0000000000..a665a32a62
--- /dev/null
+++ b/systems/knuth/local/lib/wlist.tex
@@ -0,0 +1,53 @@
+% Makes a landscape listing of one or more files with wide lines
+% Typical usage:
+% tex list *.c \\end
+
+\hsize=9in
+\vsize=6.5in
+\special{landscape}
+
+\def\grabfile#1 {\setbox0=\lastbox\endgraf\doit{#1}}
+\everypar{\grabfile}
+
+\font\filenamefont= cmtt8 scaled\magstep3
+\font\headlinefont= cmr8
+\font\listingfont= cmtex8
+\font\eoffont= cmti8
+
+\def\today{\ifcase\month\or
+ January\or February\or March\or April\or May\or June\or
+ July\or August\or September\or October\or November\or December\fi
+ \space\number\day, \number\year}
+\newcount\m \newcount\n
+\n=\time \divide\n 60 \m=-\n \multiply\m 60 \advance\m \time
+\def\hours{\twodigits\n\twodigits\m}
+\def\twodigits#1{\ifnum #1<10 0\fi \number#1}
+
+\newlinechar=`@
+\message{@\today\space at \hours}
+
+\baselineskip=9pt
+\medskipamount=4pt minus 1pt
+\raggedbottom
+\nopagenumbers
+
+\chardef\other=12
+\def\doit#1{\message{@Listing #1@}
+ \begingroup \everypar{} \frenchspacing
+ \headline{\filenamefont#1\quad\headlinefont \today\ at \hours
+ \hfill Page \folio}
+ \def\do##1{\catcode`##1=\other}\dospecials
+ \catcode127=\other \catcode9=\other \catcode12=\other
+ \parindent 0pt \parfillskip=0pt plus 1fil minus 1in
+ \everypar{\hangindent 1in} \rightskip=0pt plus 2in
+ \def\par{\ifvmode\penalty-500\medskip\else\endgraf\fi}
+ \listingfont \obeylines \obeyspaces \global\pageno=1
+ \input #1 {\eoffont(end of\/ file)}\endgraf\vfill\eject\endgroup}
+{\obeyspaces\global\let =\ }
+\catcode`\_=\other % allow _ in file names
+
+% A tab (^^I) prints as lowercase gamma.
+% Character ^^M could be made visible, with a bit of work;
+% at present, it's indistinguishable from newline (^^J).
+
+% You can get up to 170 characters on a line without an overfull box.
diff --git a/systems/knuth/local/lib/xepsf.tex b/systems/knuth/local/lib/xepsf.tex
new file mode 100644
index 0000000000..87d2788d5a
--- /dev/null
+++ b/systems/knuth/local/lib/xepsf.tex
@@ -0,0 +1,219 @@
+% XEPSF.TEX macro file:
+% Same as EPSF.TEX except that it uses the ExactBoundingBox put out by
+% MetaPost under appropriate coding.
+%
+% (I made this unnecessary on 22 Jan 97 by changing MetaPost to
+% output the exact bounding box when prologues<0. But I retain this
+% file for compatibility with older uses of MetaPost, e.g. gbmac.tex.)
+%
+% Written by Tomas Rokicki of Radical Eye Software, 29 Mar 1989.
+% Revised by Don Knuth, 3 Jan 1990.
+% Revised by Tomas Rokicki to accept bounding boxes with no
+% space after the colon, 18 Jul 1990.
+%
+% TeX macros to include an Encapsulated PostScript graphic.
+% Works by finding the bounding box comment,
+% calculating the correct scale values, and inserting a vbox
+% of the appropriate size at the current position in the TeX document.
+%
+% To use with the center environment of LaTeX, preface the \epsffile
+% call with a \leavevmode. (LaTeX should probably supply this itself
+% for the center environment.)
+%
+% To use, simply say
+% \input epsf % somewhere early on in your TeX file
+% \epsfbox{filename.ps} % where you want to insert a vbox for a figure
+%
+% Alternatively, you can type
+%
+% \epsfbox[0 0 30 50]{filename.ps} % to supply your own BB
+%
+% which will not read in the file, and will instead use the bounding
+% box you specify.
+%
+% The effect will be to typeset the figure as a TeX box, at the
+% point of your \epsfbox command. By default, the graphic will have its
+% `natural' width (namely the width of its bounding box, as described
+% in filename.ps). The TeX box will have depth zero.
+%
+% You can enlarge or reduce the figure by saying
+% \epsfxsize=<dimen> \epsfbox{filename.ps}
+% (or
+% \epsfysize=<dimen> \epsfbox{filename.ps}
+% instead). Then the width of the TeX box will be \epsfxsize and its
+% height will be scaled proportionately (or the height will be
+% \epsfysize and its width will be scaled proportionately). The
+% width (and height) is restored to zero after each use.
+%
+% A more general facility for sizing is available by defining the
+% \epsfsize macro. Normally you can redefine this macro
+% to do almost anything. The first parameter is the natural x size of
+% the PostScript graphic, the second parameter is the natural y size
+% of the PostScript graphic. It must return the xsize to use, or 0 if
+% natural scaling is to be used. Common uses include:
+%
+% \epsfxsize % just leave the old value alone
+% 0pt % use the natural sizes
+% #1 % use the natural sizes
+% \hsize % scale to full width
+% 0.5#1 % scale to 50% of natural size
+% \ifnum#1>\hsize\hsize\else#1\fi % smaller of natural, hsize
+%
+% If you want TeX to report the size of the figure (as a message
+% on your terminal when it processes each figure), say `\epsfverbosetrue'.
+%
+\newread\epsffilein % file to \read
+\newif\ifepsffileok % continue looking for the bounding box?
+\newif\ifepsfbbfound % success?
+\newif\ifepsfverbose % report what you're making?
+\newdimen\epsfxsize % horizontal size after scaling
+\newdimen\epsfysize % vertical size after scaling
+\newdimen\epsftsize % horizontal size before scaling
+\newdimen\epsfrsize % vertical size before scaling
+\newdimen\epsftmp % register for arithmetic manipulation
+\newdimen\pspoints % conversion factor
+%
+\pspoints=1bp % Adobe points are `big'
+\epsfxsize=0pt % Default value, means `use natural size'
+\epsfysize=0pt % ditto
+%
+\def\epsfbox#1{\global\def\epsfllx{72}\global\def\epsflly{72}%
+ \global\def\epsfurx{540}\global\def\epsfury{720}%
+ \def\lbracket{[}\def\testit{#1}\ifx\testit\lbracket
+ \let\next=\epsfgetlitbb\else\let\next=\epsfnormal\fi\next{#1}}%
+%
+\def\epsfgetlitbb#1#2 #3 #4 #5]#6{\epsfgrab #2 #3 #4 #5 .\\%
+ \epsfsetgraph{#6}}%
+%
+\def\epsfnormal#1{\epsfgetbb{#1}\epsfsetgraph{#1}}%
+%
+\def\epsfgetbb#1{%
+%
+% The first thing we need to do is to open the
+% PostScript file, if possible.
+%
+\openin\epsffilein=#1
+\ifeof\epsffilein\errmessage{I couldn't open #1, will ignore it}\else
+%
+% Okay, we got it. Now we'll scan lines until we find one that doesn't
+% start with %. We're looking for the bounding box comment.
+%
+ {\epsffileoktrue \chardef\other=12
+ \def\do##1{\catcode`##1=\other}\dospecials \catcode`\ =10
+ \loop
+ \read\epsffilein to \epsffileline
+ \ifeof\epsffilein\epsffileokfalse\else
+%
+% We check to see if the first character is a % sign;
+% if not, we stop reading (unless the line was entirely blank);
+% if so, we look further and stop only if the line begins with
+% `%%%ExactBoundingBox:'.
+%
+ \expandafter\epsfaux\epsffileline:. \\%
+ \fi
+ \ifepsffileok\repeat
+ \ifepsfbbfound\else
+ \ifepsfverbose\message{No bounding box comment in #1; using defaults}\fi\fi
+ }\closein\epsffilein\fi}%
+%
+% Now we have to calculate the scale and offset values to use.
+% First we compute the natural sizes.
+%
+\def\epsfclipstring{}% do we clip or not? If so,
+\def\epsfclipon{\def\epsfclipstring{ clip}}%
+\def\epsfclipoff{\def\epsfclipstring{}}%
+%
+\def\epsfsetgraph#1{%
+ \epsfrsize=\epsfury\pspoints
+ \advance\epsfrsize by-\epsflly\pspoints
+ \epsftsize=\epsfurx\pspoints
+ \advance\epsftsize by-\epsfllx\pspoints
+%
+% If `epsfxsize' is 0, we default to the natural size of the picture.
+% Otherwise we scale the graph to be \epsfxsize wide.
+%
+ \epsfxsize\epsfsize\epsftsize\epsfrsize
+ \ifnum\epsfxsize=0 \ifnum\epsfysize=0
+ \epsfxsize=\epsftsize \epsfysize=\epsfrsize
+ \epsfrsize=0pt
+%
+% We have a sticky problem here: TeX doesn't do floating point arithmetic!
+% Our goal is to compute y = rx/t. The following loop does this reasonably
+% fast, with an error of at most about 16 sp (about 1/4000 pt).
+%
+ \else\epsftmp=\epsftsize \divide\epsftmp\epsfrsize
+ \epsfxsize=\epsfysize \multiply\epsfxsize\epsftmp
+ \multiply\epsftmp\epsfrsize \advance\epsftsize-\epsftmp
+ \epsftmp=\epsfysize
+ \loop \advance\epsftsize\epsftsize \divide\epsftmp 2
+ \ifnum\epsftmp>0
+ \ifnum\epsftsize<\epsfrsize\else
+ \advance\epsftsize-\epsfrsize \advance\epsfxsize\epsftmp \fi
+ \repeat
+ \epsfrsize=0pt
+ \fi
+ \else \ifnum\epsfysize=0
+ \epsftmp=\epsfrsize \divide\epsftmp\epsftsize
+ \epsfysize=\epsfxsize \multiply\epsfysize\epsftmp
+ \multiply\epsftmp\epsftsize \advance\epsfrsize-\epsftmp
+ \epsftmp=\epsfxsize
+ \loop \advance\epsfrsize\epsfrsize \divide\epsftmp 2
+ \ifnum\epsftmp>0
+ \ifnum\epsfrsize<\epsftsize\else
+ \advance\epsfrsize-\epsftsize \advance\epsfysize\epsftmp \fi
+ \repeat
+ \epsfrsize=0pt
+ \else
+ \epsfrsize=\epsfysize
+ \fi
+ \fi
+%
+% Finally, we make the vbox and stick in a \special that dvips can parse.
+%
+ \ifepsfverbose\message{#1: width=\the\epsfxsize, height=\the\epsfysize}\fi
+ \epsftmp=10\epsfxsize \divide\epsftmp\pspoints
+ \vbox to\epsfysize{\vfil\hbox to\epsfxsize{%
+ \ifnum\epsfrsize=0\relax
+ \special{PSfile=#1 llx=\epsfllx\space lly=\epsflly\space
+ urx=\epsfurx\space ury=\epsfury\space rwi=\number\epsftmp
+ \epsfclipstring}%
+ \else
+ \epsfrsize=10\epsfysize \divide\epsfrsize\pspoints
+ \special{PSfile=#1 llx=\epsfllx\space lly=\epsflly\space
+ urx=\epsfurx\space ury=\epsfury\space rwi=\number\epsftmp\space
+ rhi=\number\epsfrsize \epsfclipstring}%
+ \fi
+ \hfil}}%
+\global\epsfxsize=0pt\global\epsfysize=0pt}%
+%
+% We still need to define the tricky \epsfaux macro. This requires
+% a couple of magic constants for comparison purposes.
+%
+{\catcode`\%=12 \global\let\epsfpercent=%\global\def\epsfbblit{%%ExactBoundingBox}}%
+%
+% So we're ready to check for `%%ExactBoundingBox:' and to grab the
+% values if they are found.
+%
+\long\def\epsfaux#1#2:#3\\{\ifx#1\epsfpercent
+ \def\testit{#2}\ifx\testit\epsfbblit
+ \epsfgrab #3 . . . \\%
+ \epsffileokfalse
+ \global\epsfbbfoundtrue
+ \fi\else\ifx#1\par\else\epsffileokfalse\fi\fi}%
+%
+% Here we grab the values and stuff them in the appropriate definitions.
+%
+\def\epsfempty{}%
+\def\epsfgrab #1 #2 #3 #4 #5\\{%
+\global\def\epsfllx{#1}\ifx\epsfllx\epsfempty
+ \epsfgrab #2 #3 #4 #5 .\\\else
+ \global\def\epsflly{#2}%
+ \global\def\epsfurx{#3}\global\def\epsfury{#4}\fi}%
+%
+% We default the epsfsize macro.
+%
+\def\epsfsize#1#2{\epsfxsize}
+%
+% Finally, another definition for compatibility with older macros.
+%
+\let\epsffile=\epsfbox
diff --git a/systems/knuth/local/man1/dvitype.1 b/systems/knuth/local/man1/dvitype.1
new file mode 100644
index 0000000000..6bf82fb7e0
--- /dev/null
+++ b/systems/knuth/local/man1/dvitype.1
@@ -0,0 +1,37 @@
+.TH DVITYPE 1L 10/18/89
+.SH NAME
+dvitype - translate a dvi file for humans to read
+.SH SYNOPSIS
+.B dvitype
+dvi_file_name
+.SH DESCRIPTION
+.I DVItype
+translates a DVI (DeVice Independent) file (output by TeX or GFtoDVI) to a
+file humans can read. It also serves as a DVI file-validating
+program (i.e., if
+.I DVItype
+can read it, it's correct) and as an example of a DVI-reading
+program for future device drivers.
+.PP
+The output file can include all commands, just the important
+ones, or none at all (in which case only errors are reported).
+A subinterval of pages may be selected for transliteration; the
+magnification and resolution of the ``output device'' may be
+changed; and so on. All options are specified with an on-line dialog.
+.PP
+The DVI filename on the command line must be complete. The
+.I .dvi
+extension is not supplied if omitted. The output always goes
+to
+.IR dvitype.out .
+.SH "SEE ALSO"
+gftype(1), tex(1), gftodvi(1)
+.br
+Donald Knuth,
+.I TeXware
+.SH "AUTHORS"
+Donald Knuth wrote the program. It was published as
+part of the
+.I TeXware
+technical report (Stanford CS1097), available from TUG (the TeX Users Group).
+Howard Trickey and Pavel Curtis ported it to Unix.
diff --git a/systems/knuth/local/man1/gftodvi.1 b/systems/knuth/local/man1/gftodvi.1
new file mode 100644
index 0000000000..3c268d519b
--- /dev/null
+++ b/systems/knuth/local/man1/gftodvi.1
@@ -0,0 +1,83 @@
+.TH GFTODVI 1L 10/21/89
+.SH NAME
+gftodvi - make proofsheets from generic font files
+.SH SYNOPSIS
+.B gftodvi
+[gf_file_name]
+.SH DESCRIPTION
+.I GFtoDVI
+converts a generic font file (output by METAFONT) to a
+device independent (DVI) file that can be typeset using the same
+software as for TeX output. The characters in the GF file will
+appear one per page, with labels, titles, and annotations as
+specified in Appendix H (Hardcopy Proofs) of
+.I The
+.IR METAFONTbook .
+.PP
+.I GFtoDVI
+uses four other fonts, in addition to the main gf file
+input. A `gray' font isused to typeset the
+pixels that actually make up the character. (We wouldn't
+want all the pixels to be simply black, since then labels,
+key points, and other information would be lost.) A `title' font
+is used for the information at the top of the page. A `label' font
+is used for the labels on key points of the figure. A `slant'
+font is used to typeset diagonal lines, which would
+otherwise have to be simulated using horizontal and vertical rules.
+The default gray, title, and label fonts are respectively `gray',
+`cmr8', and `cmtt10'; there is no default slant font.
+.PP
+The filename on the command line must be complete. (The program prompts
+you for it if you don't give it.) Because
+the resolution is part of the extension, it would not make
+sense to add on a default extension as is done with TeX or
+DVI-reading software. The output file name defaults to the same
+root as the gf file, with the `dvi' extension added. For
+example, the input file
+.I io.2602gf
+would become
+.IR io.dvi .
+.PP
+The TEXFONTS environment variable is used to find tfm
+files that the program needs. If TEXFONTS is not defined, the directory
+/home/fonts is assumed.
+.PP
+To change the default fonts, you can give `special' commands in your
+GF file, or you can change the fonts online. An online dialog ensues if you
+type `/' after the gf_file_name. For example,
+.br
+.ti +2
+gftodvi io.300gf/
+.br
+.ti +2
+Special font substitution: grayfont black
+.br
+.ti +2
+OK; any more? grayfontarea /home/art/don/
+.br
+.ti +2
+OK; any more? slantfont slantimagen6
+.br
+.ti +2
+OK; any more? <RET>
+.br
+will use /home/art/don/black as the `gray' font and /home/fonts/slantimagen6
+as the slant font (it's a font for lines with slant 1/6 at imagen resolution).
+.SH "FILES"
+/home/fonts/{gray.tfm,...} The default fonts
+.PP
+/home/tex/{dist,local}/lib/{gray.mf,...} The METAFONT sources
+.SH "SEE ALSO"
+tex(1), mf(1)
+.br
+Donald Knuth,
+.I The METAFONTbook
+.br
+Donald Knuth et al.,
+.I METAFONTware
+.SH "AUTHORS"
+Donald Knuth wrote the program. The program was published as
+part of the
+.I METAFONTware
+technical report (Stanford CS1255), available from TUG (the TeX Users Group).
+Paul Richards ported it to Unix.
diff --git a/systems/knuth/local/man1/gftopk.1 b/systems/knuth/local/man1/gftopk.1
new file mode 100644
index 0000000000..5984ec57fc
--- /dev/null
+++ b/systems/knuth/local/man1/gftopk.1
@@ -0,0 +1,37 @@
+.TH GFTOPK 1L 10/21/89
+.SH NAME
+gftopk - convert generic font files to packed font files
+.SH SYNOPSIS
+.B gftopk
+gf_file_name [pk_file_name]
+.SH DESCRIPTION
+.I GFtoPK
+converts a generic font file (output by METAFONT) to a
+packed font file for use by DVI-reading programs. Packed
+font files (PK files) are much smaller than the corresponding GFfiles,
+so they are rapidly becoming the font format of choice.
+.PP
+The gf_file_name on the command line must be complete. Because
+the resolution is part of the extension, it would not make
+sense to add on a default extension as is done with TeX or
+DVI-reading software. The pk_file_name defaults to the same
+stripped name as the gf file, and it is placed in the current
+working directory with the an extension consisting of the resolution
+in dots per inch followed by `pk'. For
+example, the input file
+.I io.300gf
+would become
+.IR io.300pk .
+.PP
+.SH "SEE ALSO"
+mf(1), gftype(1), pktype(1)
+.br
+Donald Knuth et al,
+.I METAFONTware
+.SH "AUTHORS"
+Tomas Rokicki wrote the program. The program was published as
+part of the
+.I METAFONTware
+technical report (Stanford CS1255), available from TUG (the TeX Users Group).
+Paul Richards ported it to Unix.
+
diff --git a/systems/knuth/local/man1/gftype.1 b/systems/knuth/local/man1/gftype.1
new file mode 100644
index 0000000000..14e6c28868
--- /dev/null
+++ b/systems/knuth/local/man1/gftype.1
@@ -0,0 +1,35 @@
+.TH GFTYPE 1L 10/21/89
+.SH NAME
+gftype - translate a generic font file for humans to read
+.SH SYNTAX
+.B gftype
+gf_file_name [output_file_name]
+.SH DESCRIPTION
+.I GFtype
+translates a \fIgeneric font\fR file (output by METAFONT) to a
+file humans can read. It also serves as a GF file-validating
+program (i.e., if
+.I GFtype
+can read it, it's correct) and as an example of a GF-reading
+program for other software that wants to read GF files.
+.PP
+The gf_file_name on the command line must be complete. Because
+the resolution is part of the extension, it would not make
+sense to add on a default extension as is done with TeX or
+DVI-reading software. If no output_file_name is specified, the
+output goes to the standard output.
+.SH "OPTIONS"
+The output file can include a symbolic listing of the \fIgf\fR commands,
+and/or bit-map images of the characters
+(which makes it a lot longer).
+Both options are specified by an online dialog.
+.SH "SEE ALSO"
+dvitype(1), mf(1)
+.br
+Donald Knuth et al.,
+.I METAFONTware
+.SH "AUTHORS"
+David Fuchs and Don Knuth wrote the program. The program was published as
+part of the
+.I METAFONTware
+technical report (Stanford CS1255), available from TUG (the TeX User Group).
diff --git a/systems/knuth/local/man1/mf.1 b/systems/knuth/local/man1/mf.1
new file mode 100644
index 0000000000..40ed164704
--- /dev/null
+++ b/systems/knuth/local/man1/mf.1
@@ -0,0 +1,448 @@
+.TH MF 1L 10/19/89
+.SH NAME
+mf, inimf \- METAFONT, a language for font design
+.SH SYNOPSIS
+.B mf
+[ first line ]
+.PP
+.B inimf
+[ first line ]
+.ie t .ds TX \fRT\v'+0.3m'E\v'-0.3m'X\fP \" for troff
+.el .ds TX TeX \" for nroff
+.\" to use, type \*(TX
+.ie t .ds OX \fIT\v'+0.3m'E\v'-0.3m'X\fP \" for troff
+.el .ds OX TeX \" for nroff
+.\" the same but obliqued
+
+.SH DESCRIPTION
+M\s-2ETAFONT\s0
+reads the program in the specified files
+and outputs font rasters (in gf format) and font metrics (tfm files).
+M\s-2ETAFONT\s0
+capabilities and language are described in
+.I The M\s-2ETAFONT\s0\^book
+by Donald E. Knuth, published by Addison-Wesley.
+.PP
+Like
+\*(TX, M\s-2ETAFONT\s0
+is normally used with a large body of precompiled macros, and font generation
+in particular requires the support of several macro files. The basic
+program as compiled is called
+.I inimf;
+it can be used to precompile macros into a ``*.base'' file, which can be
+preloaded when you invoke inimf under another name. If you call it `mf',
+the file plain.base is quickly loaded; if you call it `foo' and link
+`foo' to `inimf', you get the file foo.base. A different base file
+can be loaded if you say &mybase at the beginning of your first input line.
+As described in
+.I The M\s-2ETAFONT\s0\^book,
+that line should otherwise begin with a file name
+or a \\\|controlsequence.
+The normal usage is to say
+.IP
+\fBmf\ \fR ' \\\|mode=<printengine>\^;\^ [\^\\\|mag=magstep(\fI\^n\fR\^)\^;\^] ' input\ \^\fI font \fR
+.PP
+to start processing
+.I font.mf.
+The single quotes are the best way of keeping the
+shell from misinterpreting the semicolons and
+from removing the \\ characters, which are needed here to
+keep M\s-2ETAFONT\s0 from thinking that you want to produce a font called
+``mode'' or ``mag''. (Or you can just say `mf' and give the other stuff
+on the next line, without quotes.) Other
+\\\|controlsequences, such as \\\|batchmode (for silent operation) can
+also appear.
+The name ``font'' will be the ``jobname'', and is used in forming
+output file names.
+If M\s-2ETAFONT\s0 doesn't get a file name in the first line,
+the jobname is ``mfput''.
+The default `.mf' extension can be overridden by specifying an extension
+explicitly.
+There is no way to read a M\s-2ETAFONT\s0 input file with no filename extension.
+.PP
+A log of error messages goes
+into \fI font\fB\^.\^\fIlog\fR where\fI font\fR is the jobname.
+The output files
+are \fI font\fB\^.\^\fItfm\fR and \fI font\fB\^.\^\fI<\fR\^number\^\fI>gf\fR
+where <number> depends on the resolution and
+magnification of the font. The ``\\\|mode'' in this example is
+shown generically as <printengine>, a symbolic term for which the name
+of an actual printengine or the name ``localfont'' (see below) must be
+substituted.
+If the ``\\\|mode'' is not specified
+or is not valid for your site,
+M\s-2ETAFONT\s0
+will
+default to ``proof'' mode which produces large character images for
+use in font design and refinement. Proof mode can immediately
+be recognized by the appearance of the suffix
+.I .2602gf
+\^ after the jobname. Examples of proof mode output can be found
+in
+.I The Computer Modern Fonts,
+(Volume E of
+.I Computers and Typesetting
+), by Donald Knuth.
+The system of
+.I magsteps
+is identical to the system used by
+\*(TX,
+with values normally in the range 0.5, 1.0, 2.0, 3.0, 4.0 and 5.0.
+A listing of gf numbers for 118-dpi, 240-dpi and 300-dpi fonts
+is shown below.
+.ds f. mf.tbl \" tbl output inserted here
+.TS
+.if \n+(b.=1 .nr d. \n(.c-\n(c.-1
+.de 35
+.ps \n(.s
+.vs \n(.vu
+.in \n(.iu
+.if \n(.u .fi
+.if \n(.j .ad
+.if \n(.j=0 .na
+..
+.nf
+.nr #~ 0
+.if n .nr #~ 0.6n
+.ds #d .d
+.if \(ts\n(.z\(ts\(ts .ds #d nl
+.fc
+.nr 33 \n(.s
+.rm 80 81 82 83
+.nr 80 0
+.nr 38 \w\s-2MAGSTEP\s0
+.if \n(80<\n(38 .nr 80 \n(38
+.nr 38 \wmag=magstep(0)
+.if \n(80<\n(38 .nr 80 \n(38
+.nr 38 \wmag=magstep(0.5)
+.if \n(80<\n(38 .nr 80 \n(38
+.nr 38 \wmag=magstep(1)
+.if \n(80<\n(38 .nr 80 \n(38
+.nr 38 \wmag=magstep(2)
+.if \n(80<\n(38 .nr 80 \n(38
+.nr 38 \wmag=magstep(3)
+.if \n(80<\n(38 .nr 80 \n(38
+.nr 38 \wmag=magstep(4)
+.if \n(80<\n(38 .nr 80 \n(38
+.nr 38 \wmag=magstep(5)
+.if \n(80<\n(38 .nr 80 \n(38
+.80
+.rm 80
+.nr 81 0
+.nr 38 \w118 dpi
+.if \n(81<\n(38 .nr 81 \n(38
+.nr 38 \w118
+.if \n(81<\n(38 .nr 81 \n(38
+.nr 38 \w129
+.if \n(81<\n(38 .nr 81 \n(38
+.nr 38 \w142
+.if \n(81<\n(38 .nr 81 \n(38
+.nr 38 \w170
+.if \n(81<\n(38 .nr 81 \n(38
+.nr 38 \w204
+.if \n(81<\n(38 .nr 81 \n(38
+.nr 38 \w245
+.if \n(81<\n(38 .nr 81 \n(38
+.nr 38 \w294
+.if \n(81<\n(38 .nr 81 \n(38
+.81
+.rm 81
+.nr 82 0
+.nr 38 \w240 dpi
+.if \n(82<\n(38 .nr 82 \n(38
+.nr 38 \w240
+.if \n(82<\n(38 .nr 82 \n(38
+.nr 38 \w263
+.if \n(82<\n(38 .nr 82 \n(38
+.nr 38 \w288
+.if \n(82<\n(38 .nr 82 \n(38
+.nr 38 \w346
+.if \n(82<\n(38 .nr 82 \n(38
+.nr 38 \w415
+.if \n(82<\n(38 .nr 82 \n(38
+.nr 38 \w498
+.if \n(82<\n(38 .nr 82 \n(38
+.nr 38 \w597
+.if \n(82<\n(38 .nr 82 \n(38
+.82
+.rm 82
+.nr 83 0
+.nr 38 \w300 dpi
+.if \n(83<\n(38 .nr 83 \n(38
+.nr 38 \w300
+.if \n(83<\n(38 .nr 83 \n(38
+.nr 38 \w329
+.if \n(83<\n(38 .nr 83 \n(38
+.nr 38 \w360
+.if \n(83<\n(38 .nr 83 \n(38
+.nr 38 \w432
+.if \n(83<\n(38 .nr 83 \n(38
+.nr 38 \w518
+.if \n(83<\n(38 .nr 83 \n(38
+.nr 38 \w622
+.if \n(83<\n(38 .nr 83 \n(38
+.nr 38 \w746
+.if \n(83<\n(38 .nr 83 \n(38
+.83
+.rm 83
+.nr 38 1n
+.nr 79 0
+.nr 40 \n(79+(0*\n(38)
+.nr 80 +\n(40
+.nr 41 \n(80+(3*\n(38)
+.nr 81 +\n(41
+.nr 42 \n(81+(3*\n(38)
+.nr 82 +\n(42
+.nr 43 \n(82+(3*\n(38)
+.nr 83 +\n(43
+.nr TW \n(83
+.if t .if \n(TW>\n(.li .tm Table at line 13 file mf.tbl is too wide - \n(TW units
+.nr #I \n(.i
+.in +(\n(.lu-\n(TWu-\n(.iu)/2u
+.fc  
+.nr #T 0-1
+.nr #a 0-1
+.eo
+.de T#
+.ds #d .d
+.if \(ts\n(.z\(ts\(ts .ds #d nl
+.mk ##
+.nr ## -1v
+.ls 1
+.ls
+..
+.ec
+.ta \n(80u \n(81u \n(82u \n(83u
+.nr 31 \n(.f
+.nr 35 1m
+\&\h'|\n(40u'\s-2MAGSTEP\s0\h'|\n(41u'118 dpi\h'|\n(42u'240 dpi\h'|\n(43u'300 dpi
+.ta \n(80u \n(81u \n(82u \n(83u
+.nr 31 \n(.f
+.nr 35 1m
+\&\h'|\n(40u'mag=magstep(0)\h'|\n(41u'118\h'|\n(42u'240\h'|\n(43u'300
+.ta \n(80u \n(81u \n(82u \n(83u
+.nr 31 \n(.f
+.nr 35 1m
+\&\h'|\n(40u'mag=magstep(0.5)\h'|\n(41u'129\h'|\n(42u'263\h'|\n(43u'329
+.ta \n(80u \n(81u \n(82u \n(83u
+.nr 31 \n(.f
+.nr 35 1m
+\&\h'|\n(40u'mag=magstep(1)\h'|\n(41u'142\h'|\n(42u'288\h'|\n(43u'360
+.ta \n(80u \n(81u \n(82u \n(83u
+.nr 31 \n(.f
+.nr 35 1m
+\&\h'|\n(40u'mag=magstep(2)\h'|\n(41u'170\h'|\n(42u'346\h'|\n(43u'432
+.ta \n(80u \n(81u \n(82u \n(83u
+.nr 31 \n(.f
+.nr 35 1m
+\&\h'|\n(40u'mag=magstep(3)\h'|\n(41u'204\h'|\n(42u'415\h'|\n(43u'518
+.ta \n(80u \n(81u \n(82u \n(83u
+.nr 31 \n(.f
+.nr 35 1m
+\&\h'|\n(40u'mag=magstep(4)\h'|\n(41u'245\h'|\n(42u'498\h'|\n(43u'622
+.ta \n(80u \n(81u \n(82u \n(83u
+.nr 31 \n(.f
+.nr 35 1m
+\&\h'|\n(40u'mag=magstep(5)\h'|\n(41u'294\h'|\n(42u'597\h'|\n(43u'746
+.fc
+.nr T. 1
+.T# 1
+.in \n(#Iu
+.35
+.TE
+.if \n-(b.=0 .nr c. \n(.c-\n(d.-12
+
+.br
+Magnification can also be specified not as a magstep but as an
+arbitrary value, such as 1.315, to create special character sizes.
+.PP
+
+Before font production can begin, it is necessary to set up the
+appropriate `base' files. The minimum set of components for font
+production for a given print-engine is the `plain.mf' macro file
+and the local `mode_def' file. The macros in `plain.mf' can be
+studied in an appendix to the
+.I M\s-2ETAFONT\s0\^book;
+they were developed by Donald Knuth, and this file should never be
+altered except when it is officially upgraded.
+Each mode_def specification helps adapt fonts to a particular print-engine.
+There is a regular discussion of mode_defs in the journal of the
+\*(TX
+Users Group,
+.I TUGboat;
+the local ones in use on this computer (art) are in the
+file /home/tex/local/lib/art.mf.
+With only `plain.mf' and `<\^\s-2SITE\s0\^>\^-modes.mf'
+loaded it is possible to
+create fonts of simple characters, such as those used for the
+M\s-2ETAFONT\s0
+logo, and those used for the LaTeX line and circle fonts,
+but the production of Computer Modern fonts would be facilitated by
+making a cmmf.base file (which includes the macros in cmbase.mf as
+well as those in plain.mf).
+.PP
+Several environment variables can be used to set up directory
+paths to search when M\s-2ETAFONT\s0 opens a file for input.
+For example, the
+.I csh
+command
+.br
+.in +4
+setenv \s-2MFINPUTS\s0\ .\^:\^/usr/me/mylib\^:\^/usr/local/lib/mf/inputs
+.in -4
+or the
+.I sh
+command sequence
+.br
+.in +4
+\s-2MFINPUTS\s0\|=\|.\^:\^/usr/me/mylib\^:\^/usr/local/lib/mf/inputs
+.br
+export \s-2MFINPUTS\s0
+.in -4
+.br
+would cause all invocations of M\s-2ETAFONT\s0 and its derivatives to look for
+\\\|input files first in the current directory, then in a hypothetical
+user's ``mylib'', and finally in the system library.
+Normally, the user will place the command sequence which sets up the
+\s-2MFINPUTS\s0 environment variable in the
+.I .\|cshrc
+or
+.I .\|profile
+file.
+.PP
+The
+.I e
+response to M\s-2ETAFONT\s0\|'s error-recovery mode invokes the
+.I GNU emacs
+editor at the erroneous line of the source file.
+There is an environment variable, \s-2MFEDIT\s0,
+that can be used to specify the editor.
+It should contain a string with "%s" indicating where the
+filename goes and "%d" indicating where the decimal linenumber (if any) goes.
+For example, an \s-2MFEDIT\s0 string for the "vi" editor can be set by:
+.br
+
+.ti +3
+ setenv \s-2MFEDITOR\s0 "/usr/bin/vi +%d %s"
+.br
+.sp .7
+(replacing the path name for the editor
+as appropriate on your system).
+The Environment section below lists the relevant environment variables,
+and their defaults.
+.PP
+A convenient file in the library is null.mf, containing nothing.
+When mf can't find the file it thinks you want to input, it keeps
+asking you for another file name; responding `null' gets you out
+of the loop if you don't want to input anything.
+.PP
+.SH ENVIRONMENT
+The defaults for all environments are set at the
+time of compilation, by reference to a file called /home/tex/local/mfpaths.h.
+The values given below are preset in this file.
+.PP
+.IP \s-2MFINPUTS\s0
+Search path for \\\|input and \\\|openin files. It should be colon-separated,
+and start with ``.''. The entire path must be no longer than 700
+characters long.
+Default: .\^:\^/home/tex/local/lib\^:\^/home/tex/dist/lib
+.IP \s-2MFBASES\s0
+Search path for base files. Default: .\^:\^/home/tex/local/mf
+.IP \s-2MFPOOL\s0
+Search path for M\s-2ETAFONT\s internal
+strings. Default: .\^:\^/home/tex/local/mf
+.IP \s-2MFEDIT\s0
+Command template for switching to editor. Default: /emacs +%d %s
+.br
+
+.SH "FONT UTILITIES"
+.PP
+A number of utility programs are available (see section See Also).
+The following is a partial list of available utilities and their purpose.
+Consult your local M\s-2ETAFONT\s0 guru for details.
+.br
+.TP 1.5i
+.B gftopk
+Takes a GF file and produces a more tightly packed PK font file
+TP
+.B gftodvi
+Produces proof sheets for fonts
+.TP
+.B gftype
+Displays the contents of a GF file in mnemonics and images
+.br
+.TP
+.B pktype
+Mnemonically displays the contents of a PK file
+.br
+.TP
+.B mft
+Formats a source file (font.mf) as shown in
+.I Computer Modern Typefaces
+.PP
+
+.SH "FILES"
+.TP 1.3i
+/home/tex/{dist,local}/lib
+M\s-2ETAFONT\s0's library areas
+.TP
+/home/tex/local/mf/mf.pool
+Encoded text of M\s-2ETAFONT\s0's messages
+.TP
+/home/tex/local/mf/*.base
+M\s-2ETAFONT\s0 base files
+.TP
+/home/tex/dist/lib/plain.mf
+The ``standard'' macro package
+.TP
+/home/tex/local/lib/art.mf
+The file of ``mode_def''s for your site's various printers
+.TP
+/home/tex/dist/cm
+M\s-2ETAFONT\s0 sources for Computer Modern
+.TP
+/home/tex/local/cm
+Local variations of Computer Modern
+.br
+
+.SH "SUGGESTED READING"
+Donald E. Knuth,
+.I The M\s-2ETAFONT\s0\^book
+(Volume C of \fI Computers and Typesetting\fR\|)
+.br
+Donald E. Knuth,
+.I M\s-2ETAFONT\s0\^ the Program
+(Volume D of \fI Computers and Typesetting\fR\|)
+.br
+Donald E. Knuth,
+.I Computer Modern Typefaces
+(Volume E of \fI Computers and Typesetting\fR\|)
+.br
+.I TUGboat
+(the publication of the \*(TX Users Group)
+.br
+.SH "COMMENTS"
+Warning: ``Type design can be hazardous to your other interests.
+Once you get hooked, you will develop intense feelings about letterforms;
+the medium will intrude on the messages that you read.
+And you will perpetually be thinking of improvements to the fonts that
+you see everywhere, especially those of your own design.''
+.br
+.SH "BUGS"
+On January 4, 1986 the ``final''
+bug in M\s-2ETAFONT\s0 was discovered and removed. If an error still
+lurks in the code, D. E. Knuth promises to
+pay a finders fee which doubles every year
+to the first person who finds it. Happy
+hunting.
+.SH "AUTHORS"
+M\s-2ETAFONT\s0 was designed by Donald E. Knuth,
+who implemented it using his W\s-2EB\s0 system for Pascal programs.
+It was ported to Unix by Paul Richards at the University of Illinois
+at Urbana-Champaign.
+This page written by Pierre MacKay (mostly).
+.br
+.SH "SEE ALSO"
+dvitype(1), gftopk(1), gftodvi(1), gftype(1), mft(1),
+pktype(1), pltotf(1), tftopl(1)
+
diff --git a/systems/knuth/local/man1/mft.1 b/systems/knuth/local/man1/mft.1
new file mode 100644
index 0000000000..36f01b17aa
--- /dev/null
+++ b/systems/knuth/local/man1/mft.1
@@ -0,0 +1,54 @@
+.TH MFT 1L 10/21/89
+.SH NAME
+mft \- translate METAFONT code to TeX code
+.SH SYNOPSIS
+.B mft
+mffile[.mf] [-cs] [changefile[.ch]] [stylefile[.mft]]
+
+.SH DESCRIPTION
+The
+.I mft
+program is used to create a TeX file for viewing a METAFONT program.
+It takes appropriate care of typographic details like page
+layout and the use of indentation, italics, boldface, etc., as illustrated
+in the book
+.I Computer Modern Typefaces.
+Special conventions in METAFONT comments allow you to control things that
+would not otherwise come out right; section 1 of the MFT documentation
+in the
+.I METAFONTware
+report explains these rules.
+
+The command line has one required file name and two optional file names.
+The required
+one is a METAFONT source file; there is also an optional change file
+(which works just as the change files to \.{TANGLE} and \.{WEAVE})
+and an optional style file (while is prepended to everything).
+
+A file name
+that doesn't contain a dot is always given an extension, either .mf (METAFONT)
+or .ch (change) or .mft (style). If no style file is specified, the style
+plain.mft is automatically used. The METAFONT and change file names are
+either absolute paths or relative to the current directory; the style file
+is either an absolute path or relative to the MFINPUTS environment variable.
+.PP
+The output TeX file name is formed by using .tex in place of
+the extension of the METAFONT file name.
+.SH FILES
+
+/home/tex/dist/lib/mftmac.tex TeX macros used by mft output.
+.br
+/home/tex/dist/lib/plain.mft Default style file.
+.br
+/home/tex/dist/lib/cmbase.mft Style file for Computer Modern.
+.SH "SEE ALSO"
+D. E. Knuth et al.,
+.I METAFONTware
+.PP
+mf(1), weave(1)
+.SH "AUTHORS"
+MFT was written by Donald E. Knuth, and he ported to Unix with the help of
+Pierre MacKay and the Unix port of WEAVE by Howard Trickey and Pavel Curtis.
+The program is published in the
+.I METAFONTware
+report (Stanford CS1225), available from TUG (the TeX Users Group).
diff --git a/systems/knuth/local/man1/pktype.1 b/systems/knuth/local/man1/pktype.1
new file mode 100644
index 0000000000..d93fca9352
--- /dev/null
+++ b/systems/knuth/local/man1/pktype.1
@@ -0,0 +1,34 @@
+.TH PKTYPE 1L 10/21/89
+.SH NAME
+pktype - translate a packed font file for humans to read
+.SH SYNTAX
+.B pktype
+pk_file_name [output_file_name]
+.SH DESCRIPTION
+.I PKtype
+translates a \fIpacked font\fR file (output by GFtoPK) to a
+file humans can read. It also serves as a PK file-validating
+program (i.e., if
+.I PKtype
+can read it, it's correct) and as an example of a PK-reading
+program for other software that wants to read PK files.
+.PP
+The pk_file_name on the command line must be complete. Because
+the resolution is part of the extension, it would not make
+sense to add on a default extension as is done with TeX or
+DVI-reading software. If no output_file_name is specified, the
+output goes to the standard output.
+.PP
+The output file gives a compact encoding of the packed encoding, using
+conventions described in the
+.I METAFONTware
+report. Run lengths of black pixels alternate with parenthesized run lengths
+of white pixels, and brackets are used to indicate when a row should be
+repeated.
+.SH "SEE ALSO"
+gftopk(1)
+.br
+Donald Knuth et al.,
+.I METAFONTware
+.SH "AUTHORS"
+Tom Rokicki wrote the program, and Don Knuth ported it to Unix.
diff --git a/systems/knuth/local/man1/pltotf.1 b/systems/knuth/local/man1/pltotf.1
new file mode 100644
index 0000000000..6dd5053178
--- /dev/null
+++ b/systems/knuth/local/man1/pltotf.1
@@ -0,0 +1,29 @@
+.TH PLTOTF 1L 10/21/89
+.SH NAME
+pltotf - convert property list files to font metric (tfm)
+.SH SYNOPSIS
+.B pltotf
+pl_file_name tfm_file_name
+.SH DESCRIPTION
+.I PLtoTF
+translates a (human-readable) property list file to a
+(program-readable) font metric file. Thus, the font metric
+file can be edited (although this should not be necessary
+since METAFONT84 has been released), and its exact contents
+can be displayed mnemonically.
+.PP
+Both the
+.I pl_file_name
+and the
+.I tfm_file_name
+must be complete. No adding of default extensions is done.
+.SH "SEE ALSO"
+.br
+Donald Knuth,
+.I TeXware, The METAFONTbook
+.SH "AUTHORS"
+Donald Knuth wrote the program. It was published as
+part of the
+.I TeXware
+technical report (Stanford CS1097), available from TUG (the TeX Users Group).
+Howard Trickey, Pavel Curtis, and Richard Furuta ported it to Unix.
diff --git a/systems/knuth/local/man1/pooltype.1 b/systems/knuth/local/man1/pooltype.1
new file mode 100644
index 0000000000..18c65c8361
--- /dev/null
+++ b/systems/knuth/local/man1/pooltype.1
@@ -0,0 +1,23 @@
+.TH POOLTYPE 1L 10/21/89
+.SH NAME
+pooltype - display a WEB pool file
+.SH SYNOPSIS
+.B pltotf
+pool_file_name
+.SH DESCRIPTION
+.I POOLtype
+translates a (program_readable) string pool file to a
+(human-readable) text file.
+.PP
+The
+.I pool_file_name
+must be complete. No adding of default extensions is done.
+.SH "SEE ALSO"
+.br
+Donald Knuth,
+.I TeXware, The WEB system
+.SH "AUTHORS"
+Donald Knuth wrote the program and ported it to Unix. It was published as
+part of the
+.I TeXware
+technical report (Stanford CS1097), available from TUG (the TeX Users Group).
diff --git a/systems/knuth/local/man1/tangle.1 b/systems/knuth/local/man1/tangle.1
new file mode 100644
index 0000000000..487c354b44
--- /dev/null
+++ b/systems/knuth/local/man1/tangle.1
@@ -0,0 +1,107 @@
+.TH WEB 1L 10/19/89
+.SH NAME
+tangle, weave \- translate WEB to Pascal and/or TeX
+.SH SYNOPSIS
+.B tangle
+webfile[.web] [changefile[.ch]]
+.br
+.B weave
+[
+.B \-x
+] webfile[.web] [changefile[.ch]]
+
+.SH DESCRIPTION
+The
+.I tangle
+program converts a WEB
+source document into a Pascal program that may be compiled in the usual
+way with the on-line Pascal compiler (e.g.,
+.IR pc (1)).
+The output file is all in lower case
+and packed into lines of 72 characters or less, with the only concession
+to readability being the termination of lines at semicolons when this can
+be done conveniently.
+.PP
+WEB is a new language that Don Knuth has created, which
+allows you to prepare a single
+document containing all the information that is needed both to produce
+a compilable Pascal program and to produce a well-formatted document
+describing the program in as much detail as the writer may desire. The
+user of WEB must be familiar with both TeX and Pascal.
+.PP
+WEB provides a relatively simple, although adequate, macro facility that
+permits a Pascal program to be written in small easily-understood modules.
+The
+.I tangle
+program assembles these modules into a usable Pascal program.
+.PP
+The command line should have either one or two names on it.
+The first is taken as the WEB file (and .web is added if there is no
+extension).
+If there is another name, it is a change file (and .ch is added if there is
+no extension). The change file overrides parts of the WEB file,
+as described in the documentation.
+.PP
+The output files are a Pascal file and a string pool file,
+whose names are formed by adding .p and .pool respectively to the
+root of the WEB file name.
+.PP
+The
+.I weave
+program is used to create a TeX file for viewing the WEB program.
+It takes appropriate care of typographic details like page
+layout and the use of indentation, italics, boldface, etc.,and it supplies
+extensive cross-index information that it gathers automatically.
+The command line arguments are the same as for
+.I tangle
+except for the options. The
+.B \-x
+option says to omit the index, module name list, and table of contents pages.
+(A CONTENTS.tex file will still be written, however, unless some default
+macros are redefined.)
+.PP
+The output TeX file name is formed by using .tex as the extension of the
+WEB file name.
+.PP
+There are several macros in webmac.tex that probably should be redefined by
+the user at the beginning of the WEB file.
+It is a good idea to set \\title
+to the name of the program.
+And, to cause output of only changed modules, one can say
+\\let\\maybe=\\iffalse (usually as the first change in the
+change file).
+.PP
+.SH FILES
+
+/home/tex/dist/lib/webmac.tex TeX macros used by weave output.
+.SH "SEE ALSO"
+.I The WEB System of Structured Documentation
+and
+.br
+.I Literate Programming
+by D.E. Knuth.
+.br
+.I Weaving a Program
+by Wayne Sewell
+.br
+tex(1), pc(1)
+.br
+pxp(1) (for formatting tangle output when debugging)
+.br
+pooltype(1) (for symbolic listing of the pool file)
+.br
+.I TeX: The Program
+.br
+.I METAFONT: The Program
+are by far the largest extant examples of WEB programs.
+.br
+.SH "AUTHORS"
+WEB was designed by Donald E. Knuth, based on an earlier system
+called DOC (implemented by Ignacio Zabala).
+The
+.I tangle
+and
+.I weave
+programs are themselves written in WEB. The system
+was ported to Unix at Stanford by Howard Trickey, and at Cornell by Pavel
+Curtis. This version combines their work.
diff --git a/systems/knuth/local/man1/tex.1 b/systems/knuth/local/man1/tex.1
new file mode 100644
index 0000000000..614af047b8
--- /dev/null
+++ b/systems/knuth/local/man1/tex.1
@@ -0,0 +1,219 @@
+.TH TEX 1L 10/19/89
+.SH NAME
+tex, initex, virtex \- text formatting and typesetting
+.SH SYNOPSIS
+.B tex
+[ first line ]
+.PP
+.B initex
+[ first line ]
+.PP
+.B virtex
+[ first line ]
+.ie t .ds TX \fRT\v'+0.3m'E\v'-0.3m'X\fP \" for troff
+.el .ds TX TeX \" for nroff
+.\" to use, type \*(TX
+
+.SH DESCRIPTION
+\*(TX
+formats the interspersed text and commands contained in the named
+files
+and outputs a typesetter independent file (called
+.I DVI
+which is short for
+.IR D e V ice
+.IR I ndependent ).
+\*(TX
+capabilities and language are described in
+.I The \*(TXbook
+by Donald E. Knuth, published by Addison-Wesley.
+.PP
+Any arguments given on the command line to the
+\*(TX
+programs are passed to them as the first input line.
+(But it is easier to type them as the first input line, since you don't have
+to quote any of the symbols.)
+As described in
+.I The \*(TXbook,
+that first line should begin with a file name
+or a \\controlsequence.
+The normal usage is to say
+.RB `` tex
+.IR paper ''
+to start processing
+.I paper.tex.
+The name ``paper'' will be the ``jobname'', and is used in forming
+output file names.
+If \*(TX doesn't get a file name in the first line, the jobname is ``texput''.
+The default `.tex' extension can be overridden by specifying an extension
+explicitly.
+.PP
+If there is no paper.tex in the current directory, \*(TX will look
+look through a search path of directories to try to find it.
+The standard library on the default search path
+has the basic format package, plain.tex, described
+in the \*(TXbook, as well as several others.
+Note that it is hardly ever necessary to \\input plain, since the
+.I tex
+program has preloaded it.
+This means that all of the control sequences discussed in the \*(TXbook
+are known to \*(TX.
+.PP
+The output DVI file is written on
+.I name.dvi
+where
+.I name
+is the jobname.
+A log of error messages goes into
+.I name.log.
+.PP
+.PP
+Several environment variables can be used to set up directory
+paths to search when \*(TX opens a file for input.
+For example, the
+.I csh
+command
+.br
+.in +2
+setenv TEXINPUTS .:/usr/me/mylib:/usr/local/lib/tex/inputs
+.in -2
+or the
+.I sh
+command sequence
+.br
+.in +4
+TEXINPUTS=.:/usr/me/mylib:/usr/local/lib/tex/inputs
+.br
+export TEXINPUTS
+.in -4
+.br
+would cause all invocations of \*(TX and its derivatives to look for
+\\input files first in the current directory, then in a hypothetical
+user's ``mylib'', and finally in the system library.
+Normally, the user will place the command sequence which sets up the
+TEXINPUTS environment variable in the
+.I .cshrc
+or
+.I .profile
+file.
+The Environment section below lists the relevant environment variables,
+and their defaults.
+.PP
+The
+.I e
+response to \*(TX's error prompt causes the
+.I emacs
+editor to start up at the current line of the current file.
+There is an environment variable, TEXEDIT, that can be used to change the
+editor used. It should contain a string with "%s" indicating where the
+filename goes and "%d" indicating where the decimal linenumber (if any) goes.
+For example, a TEXEDIT string for
+.I vi
+can be set by:
+.br
+.ti +2
+setenv TEXEDIT "/usr/bin/vi +%d %s"
+.br
+.PP
+.PP
+A convenient file in the library is null.tex, containing nothing.
+When \*(TX can't find a file it thinks you want to input, it keeps
+asking you for another file name; responding `null' gets you out
+of the loop if you don't want to input anything.
+.PP
+Two other \*(TX programs,
+.I initex
+and
+.IR virtex ,
+can be used to create fast-loading customized versions of \*(TX.
+The
+.I initex
+program is used to create a
+.I format (.fmt)
+file that permits fast loading of fonts and macro packages.
+After processing the fonts and definitions desired, a \\dump command
+will create the format file.
+The format file is used by
+.I virtex.
+It needs to be given a format file name as the first thing it reads.
+A format file name is preceded by an &, which needs to be escaped with \\
+if given on the command line.
+So, for instance, one could create a file myfmt.fmt using initex,
+and then set up a cshell alias with
+.br
+.ti +2
+alias mytex "virtex \\&myfmt"
+.br
+to allow the use of ``mytex paper''. A slightly better way, which also works,
+is to rename myfmt.fmt as mytex.fmt and then make a symbolic link between
+/usr/local/bin/mytex and /usr/local/bin/virtex; virtex looks first to
+see if there is a format whose name matches arg0.
+.SH ENVIRONMENT
+The defaults for all environments are set at the
+time of compilation, by reference to a file called /home/tex/localtexpaths.h.
+.PP
+.IP TEXINPUTS
+Search path for \\input and \\openin files. It should be colon-separated,
+and start with ``.''. The entire path must be no longer than 700
+characters long.
+Default: .:..:/home/tex/local/lib:/home/tex/dist/lib
+.IP TEXFONTS
+Search path for font metric files. The entire path must be no longer than 100
+characters long. The default doesn't include the current area (".") to
+avoid confusing the programs that convert the output for printing on the
+various output devices (most of which don't know about the path stuff yet).
+Default: /home/fonts
+.IP TEXFORMATS
+Search path for format files. Default: .:/home/tex/local/tex
+.IP TEXPOOL
+Search path for INITEX internal strings. Default: .:/home/tex/local/tex
+.IP TEXEDIT
+Command template for switching to editor.
+Default: "emacs +%d %s"
+
+.SH FILES
+
+.TP 1.5i
+/home/tex/{dist,local}/lib
+\*(TX's library areas
+.TP
+/home/tex/local/tex/tex.pool
+Encoded text of \*(TX's messages
+.TP
+/home/fonts/*.tfm
+Width information used by \*(TX (\*(TX Font Metric files)
+.TP
+/home/fonts/*{gf,pk}
+Bit maps for low resolution devices.
+.TP
+/home/tex/local/tex/*.fmt
+\*(TX .fmt files
+.TP
+/home/tex/dist/lib/plain.tex
+The ``default'' macro package
+.br
+.SH "SEE ALSO"
+Donald E. Knuth,
+.I The \*(TXbook
+.br
+Leslie Lamport,
+.I The LaTeX Document Preparation System
+.br
+Michael Spivak,
+.I The Joy of \*(TX
+.br
+.I TUGBOAT
+(the publication of the \*(TX Users Group)
+.SH "TRIVIA"
+\*(TX, pronounced properly, rhymes with ``blecchhh.'' Note that the proper
+spelling in typewriter-like output is ``TeX'' and not ``TEX'' or ``tex.''
+.SH "BUGS"
+Maybe there should be character other than & to specify format files,
+since if you forget the \\ on the command line, it doesn't do what you want!
+Also, there is no way to read a \*(TX input file with no filename extension.
+.SH "AUTHORS"
+\*(TX was designed by Donald E. Knuth,
+who implemented it using his WEB system for Pascal programs.
+It was ported to Unix at Stanford by Howard Trickey, and
+at Cornell by Pavel Curtis.
+This version is a combination of their efforts.
diff --git a/systems/knuth/local/man1/tftopl.1 b/systems/knuth/local/man1/tftopl.1
new file mode 100644
index 0000000000..034cbe6db5
--- /dev/null
+++ b/systems/knuth/local/man1/tftopl.1
@@ -0,0 +1,31 @@
+.TH TFTOPL 1L 10/21/89
+.SH NAME
+tftopl - convert font metric (tfm) files to property list
+.SH SYNOPSIS
+.B tftopl
+tfm_file_name pl_file_name
+.SH DESCRIPTION
+.I TFtoPL
+translates a (program-readable) font metric file to
+a (human-readable) property list file. Thus, the font metric
+file can be edited (although this should not be necessary if it was
+prepared by METAFONT), and its exact contents can be displayed mnemonically.
+.I TFtoPL
+also serves as a TFM-file validating program; if no error messages are given,
+the input file is correct.
+.PP
+Both the
+.I tfm_file_name
+and the
+.I pl_file_name
+must be complete. No adding of default extensions is done.
+.SH "SEE ALSO"
+.br
+Donald Knuth,
+.I TeXware, The METAFONTbook
+.SH "AUTHORS"
+Donald Knuth wrote the program. It was published as
+part of the
+.I TeXware
+technical report (Stanford CS1097), available from TUG (the TeX Users
+Group). Howard Trickey and Pavel Curtis ported it to Unix.
diff --git a/systems/knuth/local/man1/vftovp.1 b/systems/knuth/local/man1/vftovp.1
new file mode 100644
index 0000000000..ce9ee8c1ce
--- /dev/null
+++ b/systems/knuth/local/man1/vftovp.1
@@ -0,0 +1,26 @@
+.TH VFTOVP 1L 12/30/89
+.SH NAME
+vftovp - convert virtual font (vf) files to virtual property list
+.SH SYNOPSIS
+.B vftovp
+vf_file_name tfm_file_name vpl_file_name
+.SH DESCRIPTION
+.I VFtoVP
+translates a (program-readable) virtual font file
+and its accompanying font metric file to
+a (human-readable) property list file. Thus, the virtual font
+can be edited, and its exact contents can be displayed mnemonically.
+.I VFtoVP
+also serves as a VF-file validating program; if no error messages are given,
+the input files are correct.
+.PP
+All three file names,
+.I vf_file_name,
+.I tfm_file_name,
+and
+.I vpl_file_name,
+must be complete. No adding of default extensions is done.
+.SH "AUTHOR"
+Donald Knuth wrote the program, based in part on an idea of David Fuchs,
+starting with the code for TFtoPL.
+
diff --git a/systems/knuth/local/man1/vptovf.1 b/systems/knuth/local/man1/vptovf.1
new file mode 100644
index 0000000000..56c106227f
--- /dev/null
+++ b/systems/knuth/local/man1/vptovf.1
@@ -0,0 +1,26 @@
+.TH VPTOVF 1L 12/30/89
+.SH NAME
+vptovf - convert virtual property list files to virtual fonts
+.SH SYNOPSIS
+.B vptovf
+vpl_file_name vf_file_name tfm_file_name
+.SH DESCRIPTION
+.I VPtoVF
+translates a (human-readable) property list file to a pair of
+(program-readable) files in the VF (virtual font) and TFM (Tex font metric)
+formats. Thus, a virtual font
+file can be edited and its exact contents
+can be displayed mnemonically.
+New virtual fonts, which map characters as seen by TeX into an arbitrary
+sequence of low-level typesetting operations,
+can also be easily created in this way.
+.PP
+All three file names,
+.I vpl_file_name,
+.I vf_file_name,
+and
+.I tfm_file_name,
+must be complete. No adding of default extensions is done.
+.SH "AUTHOR"
+Donald Knuth wrote the program, based in part on an idea of David Fuchs,
+starting with the code for PLtoTF.
diff --git a/systems/knuth/local/man1/weave.1 b/systems/knuth/local/man1/weave.1
new file mode 100644
index 0000000000..487c354b44
--- /dev/null
+++ b/systems/knuth/local/man1/weave.1
@@ -0,0 +1,107 @@
+.TH WEB 1L 10/19/89
+.SH NAME
+tangle, weave \- translate WEB to Pascal and/or TeX
+.SH SYNOPSIS
+.B tangle
+webfile[.web] [changefile[.ch]]
+.br
+.B weave
+[
+.B \-x
+] webfile[.web] [changefile[.ch]]
+
+.SH DESCRIPTION
+The
+.I tangle
+program converts a WEB
+source document into a Pascal program that may be compiled in the usual
+way with the on-line Pascal compiler (e.g.,
+.IR pc (1)).
+The output file is all in lower case
+and packed into lines of 72 characters or less, with the only concession
+to readability being the termination of lines at semicolons when this can
+be done conveniently.
+.PP
+WEB is a new language that Don Knuth has created, which
+allows you to prepare a single
+document containing all the information that is needed both to produce
+a compilable Pascal program and to produce a well-formatted document
+describing the program in as much detail as the writer may desire. The
+user of WEB must be familiar with both TeX and Pascal.
+.PP
+WEB provides a relatively simple, although adequate, macro facility that
+permits a Pascal program to be written in small easily-understood modules.
+The
+.I tangle
+program assembles these modules into a usable Pascal program.
+.PP
+The command line should have either one or two names on it.
+The first is taken as the WEB file (and .web is added if there is no
+extension).
+If there is another name, it is a change file (and .ch is added if there is
+no extension). The change file overrides parts of the WEB file,
+as described in the documentation.
+.PP
+The output files are a Pascal file and a string pool file,
+whose names are formed by adding .p and .pool respectively to the
+root of the WEB file name.
+.PP
+The
+.I weave
+program is used to create a TeX file for viewing the WEB program.
+It takes appropriate care of typographic details like page
+layout and the use of indentation, italics, boldface, etc.,and it supplies
+extensive cross-index information that it gathers automatically.
+The command line arguments are the same as for
+.I tangle
+except for the options. The
+.B \-x
+option says to omit the index, module name list, and table of contents pages.
+(A CONTENTS.tex file will still be written, however, unless some default
+macros are redefined.)
+.PP
+The output TeX file name is formed by using .tex as the extension of the
+WEB file name.
+.PP
+There are several macros in webmac.tex that probably should be redefined by
+the user at the beginning of the WEB file.
+It is a good idea to set \\title
+to the name of the program.
+And, to cause output of only changed modules, one can say
+\\let\\maybe=\\iffalse (usually as the first change in the
+change file).
+.PP
+.SH FILES
+
+/home/tex/dist/lib/webmac.tex TeX macros used by weave output.
+.SH "SEE ALSO"
+.I The WEB System of Structured Documentation
+and
+.br
+.I Literate Programming
+by D.E. Knuth.
+.br
+.I Weaving a Program
+by Wayne Sewell
+.br
+tex(1), pc(1)
+.br
+pxp(1) (for formatting tangle output when debugging)
+.br
+pooltype(1) (for symbolic listing of the pool file)
+.br
+.I TeX: The Program
+.br
+.I METAFONT: The Program
+are by far the largest extant examples of WEB programs.
+.br
+.SH "AUTHORS"
+WEB was designed by Donald E. Knuth, based on an earlier system
+called DOC (implemented by Ignacio Zabala).
+The
+.I tangle
+and
+.I weave
+programs are themselves written in WEB. The system
+was ported to Unix at Stanford by Howard Trickey, and at Cornell by Pavel
+Curtis. This version combines their work.
diff --git a/systems/knuth/local/man1/web.1 b/systems/knuth/local/man1/web.1
new file mode 100644
index 0000000000..487c354b44
--- /dev/null
+++ b/systems/knuth/local/man1/web.1
@@ -0,0 +1,107 @@
+.TH WEB 1L 10/19/89
+.SH NAME
+tangle, weave \- translate WEB to Pascal and/or TeX
+.SH SYNOPSIS
+.B tangle
+webfile[.web] [changefile[.ch]]
+.br
+.B weave
+[
+.B \-x
+] webfile[.web] [changefile[.ch]]
+
+.SH DESCRIPTION
+The
+.I tangle
+program converts a WEB
+source document into a Pascal program that may be compiled in the usual
+way with the on-line Pascal compiler (e.g.,
+.IR pc (1)).
+The output file is all in lower case
+and packed into lines of 72 characters or less, with the only concession
+to readability being the termination of lines at semicolons when this can
+be done conveniently.
+.PP
+WEB is a new language that Don Knuth has created, which
+allows you to prepare a single
+document containing all the information that is needed both to produce
+a compilable Pascal program and to produce a well-formatted document
+describing the program in as much detail as the writer may desire. The
+user of WEB must be familiar with both TeX and Pascal.
+.PP
+WEB provides a relatively simple, although adequate, macro facility that
+permits a Pascal program to be written in small easily-understood modules.
+The
+.I tangle
+program assembles these modules into a usable Pascal program.
+.PP
+The command line should have either one or two names on it.
+The first is taken as the WEB file (and .web is added if there is no
+extension).
+If there is another name, it is a change file (and .ch is added if there is
+no extension). The change file overrides parts of the WEB file,
+as described in the documentation.
+.PP
+The output files are a Pascal file and a string pool file,
+whose names are formed by adding .p and .pool respectively to the
+root of the WEB file name.
+.PP
+The
+.I weave
+program is used to create a TeX file for viewing the WEB program.
+It takes appropriate care of typographic details like page
+layout and the use of indentation, italics, boldface, etc.,and it supplies
+extensive cross-index information that it gathers automatically.
+The command line arguments are the same as for
+.I tangle
+except for the options. The
+.B \-x
+option says to omit the index, module name list, and table of contents pages.
+(A CONTENTS.tex file will still be written, however, unless some default
+macros are redefined.)
+.PP
+The output TeX file name is formed by using .tex as the extension of the
+WEB file name.
+.PP
+There are several macros in webmac.tex that probably should be redefined by
+the user at the beginning of the WEB file.
+It is a good idea to set \\title
+to the name of the program.
+And, to cause output of only changed modules, one can say
+\\let\\maybe=\\iffalse (usually as the first change in the
+change file).
+.PP
+.SH FILES
+
+/home/tex/dist/lib/webmac.tex TeX macros used by weave output.
+.SH "SEE ALSO"
+.I The WEB System of Structured Documentation
+and
+.br
+.I Literate Programming
+by D.E. Knuth.
+.br
+.I Weaving a Program
+by Wayne Sewell
+.br
+tex(1), pc(1)
+.br
+pxp(1) (for formatting tangle output when debugging)
+.br
+pooltype(1) (for symbolic listing of the pool file)
+.br
+.I TeX: The Program
+.br
+.I METAFONT: The Program
+are by far the largest extant examples of WEB programs.
+.br
+.SH "AUTHORS"
+WEB was designed by Donald E. Knuth, based on an earlier system
+called DOC (implemented by Ignacio Zabala).
+The
+.I tangle
+and
+.I weave
+programs are themselves written in WEB. The system
+was ported to Unix at Stanford by Howard Trickey, and at Cornell by Pavel
+Curtis. This version combines their work.
diff --git a/systems/knuth/local/mf-sparc/Makefile b/systems/knuth/local/mf-sparc/Makefile
new file mode 100644
index 0000000000..2bdb6536d4
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/Makefile
@@ -0,0 +1,73 @@
+# Make executable files for this workstation, using the master TeX sources
+
+PFLAGS= -I..
+CFLAGS= -g -I.. -I/usr/openwin/include
+#CFLAGS= -O -I.. -I/usr/openwin/include
+#LIBS= -lsuntool -lsunwindow -lpixrect
+LIBS= -L/usr/openwin/lib -lxview -lolgx -lX11
+
+PC= time pc
+
+all: inimf
+
+traptest: trapmf
+ -trapmf < trap1.in
+ mv trap.log trapin.log
+ -trapmf < trap2.in | tee trap.fot
+ gftype trap.72270gf trap.typ < gftype.in
+ tftopl trap.tfm trap.pl
+ rm -f *.dif
+ -diff trapin.log ../../dist/mf/trapin.log > trapin.log.dif
+ -diff trap.log ../../dist/mf/trap.log > trap.log.dif
+ -diff trap.fot ../../dist/mf/trap.fot > trap.fot.dif
+ -diff trap.typ ../../dist/mf/trap.typ > trap.typ.dif
+ -diff trap.pl ../../dist/mf/trap.pl > trap.pl.dif
+ more trapin.log.dif trap.log.dif trap.typ.dif trap.fot.dif trap.pl.dif
+
+%.p: %.ch
+ tangle ../../dist/mf/mf.web $*.ch
+ mv ../../dist/mf/mf.pool .
+ mv ../../dist/mf/mf.p $*.p
+
+inimf.ch: ../../dist/mf/mf.web
+
+trapmf.ch: inimf.ch ini_to_trap
+ ini_to_trap inimf.ch trapmf.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+trapmf.o: trapmf.p
+ ${PC} -g ${PFLAGS} -c trapmf.p
+
+inimf: inimf.o ext.o mf_arith.o mf_sunwin.o
+ pc ${PFLAGS} -o inimf inimf.o ext.o mf_arith.o mf_sunwin.o ${LIBS}
+
+trapmf: trapmf.o ext.o mf_arith.o
+ pc -g ${PFLAGS} -o trapmf trapmf.o ext.o mf_arith.o
+ cp ../../dist/mf/trap.mf .
+
+ext.o: ext.c ext.h ../h00vars.h ../mfpaths.h
+ cc ${CFLAGS} -c ext.c
+
+mf_arith.o: mf_arith.c
+mf_sunwin.o: mf_sunwin.c
+
+plain.base: ../../dist/lib/plain.mf ../lib/local.mf
+ -inimf plain \\input local \\dump
+
+install: # doit make install
+ mv inimf /usr/local/bin
+ rm /usr/local/bin/mf
+ ln /usr/local/bin/inimf /usr/local/bin/mf
+ make plain.base
+
+installtrap: # when the trap test is satisfactory
+ mv trap.mf trapin.log trap.log trap.fot trap.typ trap.pl ../../dist/mf
+
+clean:
+ rm -f *.p *.o *~ *.mf trapmf* trap.* trapin*
+ @echo "Remaining files should be:"
+ @echo "Makefile gftype.in mf.pool mf_window.h trap1.in"
+ @echo "ext.c ini_to_trap* mf_arith.c plain.base trap2.in"
+ @echo "ext.h inimf.ch mf_sunwin.c plain.log"
diff --git a/systems/knuth/local/mf-sparc/ext.c b/systems/knuth/local/mf-sparc/ext.c
new file mode 100644
index 0000000000..dfbf0b2bbb
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/ext.c
@@ -0,0 +1,457 @@
+/* external procedures called by MF */
+#include <sys/signal.h>
+#include <sys/time.h>
+#include "mfpaths.h" /* defines MAXINPATHCHARS, defaultinputpath, etc. */
+#include "h00vars.h"
+
+/* The following are for closing files */
+
+closea(f)
+ int *f; /* actually, file pointer, but it doesn't matter */
+{
+ close1(f);
+}
+
+closeb(f)
+ int *f; /* actually, file pointer, but it doesn't matter */
+{
+ close1(f);
+}
+
+closew(f)
+ int *f; /* actually, file pointer, but it doesn't matter */
+{
+ close1(f);
+}
+
+extern int interrupt; /* To be made nonzero to tell MF about interrupts */
+
+/*
+** catchint()
+**
+** Gets called when the user hits his interrupt key. Sets the global
+** "interrupt" nonzero, then sets it up so that the next interrupt will
+** also be caught here.
+**
+*/
+
+catchint()
+{
+ interrupt = 1;
+ signal(SIGINT,catchint);
+}
+
+/*
+** dateandtime(time, day, month, year)
+**
+** Stores minutes since midnight, current day, month and year into
+** *time, *day, *month and *year, respectively.
+**
+** Also, set things up so that catchint() will get control on interrupts.
+**
+*/
+
+dateandtime(minutes, day, month, year)
+int *minutes, *day, *month, *year;
+{
+ long clock;
+ struct tm *tmptr, *localtime();
+
+ clock = time(0);
+ tmptr = localtime(&clock);
+
+ *minutes = tmptr->tm_hour * 60 + tmptr->tm_min;
+ *day = tmptr->tm_mday;
+ *month = tmptr->tm_mon + 1;
+ *year = tmptr->tm_year + 1900;
+ signal(SIGINT,catchint);
+}
+
+
+/* fixed arrays are used to hold the paths, to avoid any possible problems
+ involving interaction of malloc and undump */
+
+char inputpath[MAXINPATHCHARS] = defaultinputpath;
+char basepath[MAXOTHPATHCHARS] = defaultbasepath;
+char poolpath[MAXOTHPATHCHARS] = defaultpoolpath;
+
+char *getenv();
+
+/*
+ * setpaths is called to set up the arrays inputpath, basepath, and
+ * poolpath as follows: if the user's environment has a value for the
+ * appropriate value, then use it; otherwise, leave the current value of
+ * the array (which may be the default path, or it may be the result of
+ * a call to setpaths on a previous run that was made the subject of
+ * an undump: this will give the maker of a preloaded MF the option of
+ * providing a new set of "default" paths.
+ *
+ * Note that we have to copy the string from the environment area, since
+ * that will change on the next run (which matters if this is for a
+ * preloaded MF).
+ */
+setpaths()
+{
+ register char *envpath;
+
+ if ((envpath = getenv("MFINPUTS")) != NULL)
+ excopypath(inputpath,envpath,MAXINPATHCHARS);
+ if ((envpath = getenv("MFBASES")) != NULL)
+ excopypath(basepath,envpath,MAXOTHPATHCHARS);
+ if ((envpath = getenv("MFPOOL")) != NULL)
+ excopypath(poolpath,envpath,MAXOTHPATHCHARS);
+}
+
+/*
+ * excopypath(s1,s2,n) copies at most n characters (including the null)
+ * from string s2 to string s1, giving an error message for paths
+ * that are too long.
+ */
+excopypath(s1,s2,n)
+ register char *s1,*s2;
+ register int n;
+{
+ while ((*s1++ = *s2++) != '\0')
+ if (--n == 0) {
+ fprintf(stderr, "! Environment search path is too big\n");
+ *--s1 = '\0';
+ return; /* let user continue with truncated path */
+ }
+}
+
+extern short buffer[],xord[];
+extern short last,first; /* pointers into buffer */
+
+/*
+ * lineread reads from the Pascal text file with iorec pointer filep
+ * into buffer[first], buffer[first+1],..., buffer[last-1] (and
+ * setting "last").
+ * Characters are read until a newline is found (which isn't put in the
+ * buffer) or until the next character would go into buffer[lastlim].
+ * The characters need to be translated, so really xord[c] is put into
+ * the buffer when c is read.
+ * If end-of-file is encountered, the funit field of *filep is set
+ * appropriately.
+ */
+lineread(filep, lastlim)
+struct iorec *filep;
+int lastlim;
+{
+ register c;
+ register short *cs; /* pointer into buffer where next char goes */
+ register FILE *iop; /* stdio-style FILE pointer */
+ int l; /* how many more chars are allowed before buffer overflow */
+ short *bf; /* hold address of buffer[first] */
+
+ iop = filep->fbuf;
+ bf = cs = &(buffer[first]);
+ l = lastlim-first;
+ /* overflow when next char would go into buffer[lastlim] */
+ while (--l>=0 && (c = getc(iop)) != EOF && c!='\n')
+ *cs++ = xord[c];
+ if (c == EOF)
+ filep->funit |= EOFF; /* we hit end-of-file */
+ last = first+cs-bf;
+}
+
+#define filenamesize 1024 /* should agree with inimf.ch */
+extern char nameoffile[],realnameoffile[]; /* these have size filenamesize */
+
+/*
+ * testaccess(amode,filepath)
+ *
+ * Test whether or not the file whose name is in the global nameoffile
+ * can be opened for reading (if mode=READACCESS)
+ * or writing (if mode=WRITEACCESS).
+ *
+ * The filepath argument is one of the ...FILEPATH constants defined below.
+ * If the filename given in nameoffile does not begin with '/', we try
+ * prepending all the ':'-separated areanames in the appropriate path to the
+ * filename until access can be made, if it ever can.
+ *
+ * The realnameoffile global array will contain the name that yielded an
+ * access success.
+ */
+
+#define READACCESS 4
+#define WRITEACCESS 2
+
+#define NOFILEPATH 0
+#define INPUTFILEPATH 1
+#define READFILEPATH 2
+#define BASEFILEPATH 4
+#define POOLFILEPATH 5
+
+bool
+testaccess(amode,filepath)
+ int amode,filepath;
+{
+ register bool ok;
+ register char *p;
+ char *curpathplace;
+ int f;
+
+ switch(filepath) {
+ case NOFILEPATH: curpathplace = NULL; break;
+ case INPUTFILEPATH: case READFILEPATH:
+ curpathplace = inputpath; break;
+ case BASEFILEPATH: curpathplace = basepath; break;
+ case POOLFILEPATH: curpathplace = poolpath; break;
+ }
+ if (nameoffile[0]=='/') /* file name has absolute path */
+ curpathplace = NULL;
+ do {
+ packrealnameoffile(&curpathplace);
+ if (amode==READACCESS)
+ /* use system call "access" to see if we could read it */
+ if (access(realnameoffile,READACCESS)==0) ok = TRUE;
+ else ok = FALSE;
+ else {
+ /* WRITEACCESS: use creat to see if we could create it, but close
+ the file again if we're OK, to let pc open it for real */
+ f = creat(realnameoffile,0666);
+ if (f>=0) ok = TRUE;
+ else ok = FALSE;
+ if (ok)
+ close(f);
+ }
+ } while (!ok && curpathplace != NULL);
+ if (ok) { /* pad realnameoffile with blanks, as Pascal wants */
+ for (p = realnameoffile; *p != '\0'; p++)
+ /* nothing: find end of string */ ;
+ while (p < &(realnameoffile[filenamesize]))
+ *p++ = ' ';
+ }
+ return (ok);
+}
+
+/*
+ * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp,
+ * followed by '/', followed by the characters in nameoffile up until the
+ * first blank there, and finally a '\0'. The cpp pointer is left pointing
+ * at the next directory in the path.
+ * But: if *cpp == NULL, then we are supposed to use nameoffile as is.
+ */
+packrealnameoffile(cpp)
+ char **cpp;
+{
+ register char *p,*realname;
+
+ realname = realnameoffile;
+ if ((p = *cpp)!=NULL) {
+ while ((*p != ':') && (*p != '\0')) {
+ *realname++ = *p++;
+ if (realname == &(realnameoffile[filenamesize-1]))
+ break;
+ }
+ if (*p == '\0') *cpp = NULL; /* at end of path now */
+ else *cpp = p+1; /* else get past ':' */
+ *realname++ = '/'; /* separate the area from the name to follow */
+ }
+ /* now append nameoffile to realname... */
+ p = nameoffile;
+ while (*p != ' ') {
+ if (realname >= &(realnameoffile[filenamesize-1])) {
+ fprintf(stderr,"! Full file name is too long\n");
+ break;
+ }
+ *realname++ = *p++;
+ }
+ *realname = '\0';
+}
+
+/*
+** testeof(filep)
+**
+** Test whether or not the Pascal text file with iorec pointer filep
+** has reached end-of-file (when the only I/O on it is done with
+** lineread, above).
+** We may have to read the next character and unget it to see if perhaps
+** the end-of-file is next.
+*/
+
+bool
+testeof(filep)
+register struct iorec *filep;
+{
+ register char c;
+ register FILE *iop; /* stdio-style FILE pointer */
+ if (filep->funit & EOFF)
+ return(TRUE);
+ else { /* check to see if next is EOF */
+ iop = filep->fbuf;
+ c = getc(iop);
+ if (c == EOF)
+ return(TRUE);
+ else {
+ ungetc(c,iop);
+ return(FALSE);
+ }
+ }
+}
+
+
+/*
+** writegf(a,b)
+**
+** writegf is called to write gfbuf[a..b] to gffile
+** Unfortunately, gfbuf is declared as eight_bits, which in our
+** implementation means that two bytes are taken by each entry.
+** We only want to output the low order (first) byte of each pair.
+*/
+extern struct iorec gffile;
+extern short gfbuf[];
+writegf (a,b)
+ int a,b;
+{
+ register short *ptr,*final;
+ register FILE *iop;
+
+ iop=gffile.fbuf;
+ ptr= &(gfbuf[a]);
+ final= &(gfbuf[b]);
+ while (ptr<=final) {
+ putc((char) (*ptr & 0377), iop);
+ ptr += 1;
+ }
+ /* Note: The above code used to be machine dependent. By changing
+ * the declarations from "char *" to "short *" we get the machine
+ * to do its own byte ordering of the de-reference of "ptr" above.
+ * Then we AND off the low bits (now that it's been pulled from
+ * memory correctly) and cast it into a "char" for putc().
+ * --Peter Kessler's idea; explanation, AND, cast a la clp.
+ */
+}
+
+
+/*
+** close1(filep)
+** close1 does the proper things to pc's runtime system file data structure
+** to close a file
+*/
+close1(filep)
+
+ register struct iorec *filep;
+{
+ register struct iorec *next;
+
+ if (filep->fbuf != 0) {
+ if ((filep->funit & FDEF) == 0) {
+ next = (struct iorec *) &_fchain;
+ while (next->fchain != FILNIL
+ && next->fchain != filep)
+ next = next->fchain;
+ if (next->fchain != FILNIL)
+ next->fchain = next->fchain->fchain;
+
+ if (filep->fblk > PREDEF) {
+ fflush(filep->fbuf);
+ setbuf(filep->fbuf, NULL);
+ }
+ fclose(filep->fbuf);
+ if (ferror(filep->fbuf)) {
+/* ERROR("%s: Close failed\n",
+ filep->pfname); */
+ return;
+ }
+ filep->fbuf=0;
+ }
+ if ((filep->funit & TEMP) != 0 &&
+ unlink(filep->pfname)) {
+/* PERROR("Could not remove ", filep->pfname); */
+ return;
+ }
+ }
+}
+
+/*
+** The following procedure is due to sjc@s1-c.
+**
+** calledit(filename, fnlength, linenumber)
+**
+** MF84 can call this to implement the 'e' feature in error-recovery
+** mode, invoking a text editor on the erroneous source file.
+**
+** You should pass to "filename" the first character of the packed array
+** containing the filename, and to "fnlength" the size of the filename.
+**
+** Ordinarily, this invokes emacs. If you want a different
+** editor, create a shell environment variable MFEDIT containing
+** the string that invokes that editor, with "%s" indicating where
+** the filename goes and "%d" indicating where the decimal
+** linenumber (if any) goes. For example, a MFEDIT string for "vi" might be:
+**
+** setenv MFEDIT "/usr/bin/vi +%d %s"
+**
+*/
+
+ char dvalue[] = "emacs +%d %s";
+ char *mfeditvalue = &dvalue[0];
+
+ calledit(filename, fnlength, linenumber)
+ char *filename;
+ int fnlength, linenumber;
+ {
+ char *temp, *command, c;
+ int sdone, ddone, i;
+
+ sdone = ddone = 0;
+
+ /* Replace default with environment variable if possible. */
+ if (NULL != (temp = (char *) getenv("MFEDIT")))
+ mfeditvalue = temp;
+
+ /* Make command string containing envvalue, filename, and linenumber */
+ if (NULL ==
+ (command = (char *) malloc(strlen(mfeditvalue) + fnlength + 25))) {
+ fprintf(stderr, "! Not enough memory to issue editor command\n");
+ exit(1);
+ }
+ temp = command;
+ while ((c = *mfeditvalue++) != '\0') {
+ if (c == '%') {
+ switch (c = *mfeditvalue++) {
+ case 'd':
+ if (ddone) {
+ fprintf(stderr,
+ "! Line number cannot appear twice in editor command\n");
+ exit(1);
+ }
+ sprintf(temp, "%d", linenumber);
+ while (*temp != '\0')
+ temp++;
+ ddone = 1;
+ break;
+ case 's':
+ if (sdone) {
+ fprintf(stderr,
+ "! Filename cannot appear twice in editor command\n");
+ exit(1);
+ }
+ i = 0;
+ while (i < fnlength)
+ *temp++ = filename[i++] ^ 0x80; /* changed for 8-bit MF */
+ sdone = 1;
+ break;
+ case '\0':
+ *temp++ = '%';
+ mfeditvalue--; /* Back up to \0 to force termination. */
+ break;
+ default:
+ *temp++ = '%';
+ *temp++ = c;
+ break;
+ }
+ }
+ else
+ *temp++ = c;
+ }
+ *temp = '\0';
+
+ /* Execute command. */
+ if (0 != system(command))
+ fprintf(stderr, "! Trouble executing command %s\n", command);
+
+ /* Quit, indicating MF had found an error before you typed "e". */
+ exit(1);
+ }
diff --git a/systems/knuth/local/mf-sparc/ext.h b/systems/knuth/local/mf-sparc/ext.h
new file mode 100644
index 0000000000..7ff0e3bfdc
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/ext.h
@@ -0,0 +1,38 @@
+procedure exit(x : integer);
+ external;
+
+procedure closea(var f:text);
+ external;
+
+procedure closeb(var f:bytefile);
+ external;
+
+procedure closew(var f:wordfile);
+ external;
+
+procedure dateandtime(var minutes, day, month, year : integer);
+ external;
+
+procedure setpaths;
+ external;
+
+function testaccess(accessmode:integer; filepath:integer): boolean;
+ external;
+
+function testeof(var f: text): boolean;
+ external;
+
+procedure lineread(var f: text; lastlim: integer);
+ external;
+
+procedure writegf(a,b:integer);
+ external;
+
+procedure calledit(var filename: packedASCIIcode; fnlength, linenumber: integer);
+ external;
+
+function makefraction(p,q:integer):fraction;
+ external;
+
+function takefraction(q:integer;f:fraction):integer;
+ external;
diff --git a/systems/knuth/local/mf-sparc/gftype.in b/systems/knuth/local/mf-sparc/gftype.in
new file mode 100644
index 0000000000..9b48ee4cf9
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/gftype.in
@@ -0,0 +1,2 @@
+y
+y
diff --git a/systems/knuth/local/mf-sparc/ini_to_trap b/systems/knuth/local/mf-sparc/ini_to_trap
new file mode 100644
index 0000000000..c18984179f
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/ini_to_trap
@@ -0,0 +1,38 @@
+#! /bin/csh
+# A shell script to perform modifications on the changefile for inimf
+# to make a changefile for trapmf (used to test mf).
+#
+# To use it, say
+# ini_to_trap inimf.ch trapmf.ch
+#
+ed - $1 <<edscriptend
+/debug=/
+//
+s/@{//p
+/gubed=/
+s/@t@>@}//p
+/@!mem_max=/
+//
+s/[0-9][0-9]*/3000/p
+/@!error_line=/
+s/[0-9][0-9]*/64/p
+/@!half_error_line=/
+s/[0-9][0-9]*/32/p
+/@!max_print_line=/
+s/[0-9][0-9]*/72/p
+/@!screen_width=/
+s/[0-9][0-9]*/100/p
+/@!screen_depth=/
+s/[0-9][0-9]*/200/p
+/@!gf_buf_size=/
+s/[0-9][0-9]*/8/p
+/@d mem_min=/
+//
+s/-[0-9]*/0/p
+/@d mem_top==/
+s/[0-9][0-9]*/3000/p
+/% BEGIN HOOK FOR THE TRAP TEST/
+.+1,.+5s/%//
+.+1,/% END HOOK FOR THE TRAP TEST/d
+w $2
+edscriptend
diff --git a/systems/knuth/local/mf-sparc/inimf.ch b/systems/knuth/local/mf-sparc/inimf.ch
new file mode 100644
index 0000000000..271532c7b6
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/inimf.ch
@@ -0,0 +1,1505 @@
+% Derived from INITEX.CH for Berkeley Unix TeX 1.1, by Howard Trickey
+% and Pavel Curtis
+
+% Modification History:
+%
+% brought up to version 2.7182 on 8/8/98 --- don
+% brought up to version 2.718 on 3/8/95 --- don
+% UNIXexit convention switched 2/21/93 --- don
+% tfm_out needed to be corrected for new Pascal compiler 7/18/92 --- don
+% brought up to version 2.71 on 9/20/91 --- don
+% brought up to version 2.7 on 9/6/90 --- don
+% fixed max_buf_stack logic (it was one too high) on 1/22/90 --- don
+% brought up to version 1.9 on 12/20/89 --- don
+% brought up to version 1.8 on 10/14/89 --- don
+% 8-bit modifications made 9/11/89 --- don
+%
+% don adapted everything for SunOS, MF Version 1.7, on 8/12/89
+% Module numbers changed to match Volume D
+% Changes made more consistent with initex.ch
+% Window code merged in, with hooks to remove it for the TRAP test
+%
+% Revision 1.3 87/03/07 21:15:21 mackay
+%
+% Minor changes found on archive version on SCORE
+% (max_in_open increased to 10. -- 88/02/07 PAM)
+%
+% Revision 1.2 86/09/29 21:46:43 mackay
+%
+% Made no-debug the default, and changed version number
+% to correspond with improved mf.web file
+% (Got rid of debug code to avoid bug in range check
+% code of VAX4.3 BSD and SUN3 version 3.x Os pc interpreter)
+%
+% Revision 1.0 86/01/31 15:46:08 richards
+% Released for MF 1.0;
+%
+% Incorporates: New binary I/O library, separate optimized
+% arithmetic for takefraction/makefraction, new graphics interface
+%
+% Revision 0.999999.1.2 86/01/28 19:49:38 richards
+% Added:
+% variable "dummy" to routine initterm() because SUN PC compiler
+% loops with only i,j,k defined. Argh!
+%
+% Revision 0.999999.1.1 86/01/21 23:05:16 richards
+% First cut for MF 1.0
+%
+% Revision 0.999999 86/01/13 00:40:25 richards
+% Released for 0.999999:
+% added OTHERCASES sentinel
+% revised WINDOW interface and include files
+%
+% Revision 0.99999 85/12/09 20:23:35 richards
+% Version for 0.99999
+%
+% Revision 0.9999 85/10/21 20:48:24 richards
+% Released for MF version 0.9999
+%
+% Revision 0.999 85/10/18 21:33:44 richards
+% Version that works with 0.999 MF (Never released)
+%
+% Revision 0.91 85/05/16 10:58:13 richards
+% Released for MF version 0.91
+%
+% Revision 0.81 85/05/05 12:24:08 richards
+% Released for MF version 0.81
+%
+% Revision 0.77 85/03/11 20:23:37 richards
+% Released for MF version 0.77
+%
+% Revision 0.6.1.1 85/02/12 20:12:32 richards
+% First edit for 0.76
+%
+% Revision 0.6 85/02/08 00:52:40 richards
+% Distribution version for MF 0.6
+%
+% Revision 0.0.2.1 84/12/06 03:43:33 richards
+% Edited to work with 0.4 -- still needs to have section numbers re-done
+% before release.
+%
+% Revision 0.0 84/11/05 00:50:38 richards
+% Base version (first cut) for MetaFont 0.0
+%
+%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: only print changes
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+@y
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+\let\maybe=\iffalse
+\def\title{{\logo opqrstuq} changes for SunOS}
+\def\glob{13}\def\gglob{20, 25} % these are defined in module 1
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.2] banner line
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is METAFONT, Version 2.7182' {printed when \MF\ starts}
+@y
+@d banner=='This is METAFONT, Version 2.7182 for SunOS'
+ {printed when \MF\ starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.4] program header, include ext.h for C routine linkage
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Actually the heading shown here is not quite normal: The |program| line
+does not mention any |output| file, because \ph\ would ask the \MF\ user
+to specify a file name if |output| were specified here.
+@^system dependencies@>
+
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@y
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@d standard_input==i@&n@&p@&u@&t {and another}
+@z
+@x
+program MF; {all file names are defined dynamically}
+@y
+program MF(standard_input,output); {other file names are defined dynamically}
+@z
+@x
+var @<Global variables@>@/
+@y
+var @<Global variables@>@/
+@#
+@\@=#include "ext.h"@>@\ {declarations for external C procedures}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.7] debug..gubed, stat..tats
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@y
+@d debug==@{
+@d gubed==@t@>@}
+@z
+% (I will use the debug code only in the trap test)
+@x
+@d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering
+ usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering
+ usage statistics}
+@y
+@d stat==
+@d tats==
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.8] init..tini
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
+@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
+@y
+@d init==
+@d tini==
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.9] compiler directives
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+@y
+@=(*$C-*)@> {no range check}
+@!debug @=(*$C+*)@> @+ gubed {but turn everything on when debugging}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.10] othercases, feature of Sun Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.11] compile-time constants
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Constants...@>=
+@!mem_max=30000; {greatest index in \MF's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INIMF}, otherwise |>=mem_top|}
+@!max_internal=100; {maximum number of internal quantities}
+@!buf_size=500; {maximum number of characters simultaneously present in
+ current lines of open files; must not exceed |max_halfword|}
+@!error_line=72; {width of context lines on terminal error messages}
+@!half_error_line=42; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!screen_width=768; {number of pixels in each row of screen display}
+@!screen_depth=1024; {number of pixels in each column of screen display}
+@!stack_size=30; {maximum number of simultaneous input sources}
+@!max_strings=2000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=8000; {the minimum number of characters that should be
+ available for the user's identifier names and strings,
+ after \MF's own error messages are stored}
+@!pool_size=32000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all identifiers;
+ must exceed |string_vacancies| by the total
+ length of \MF's own strings, which is currently about 22000}
+@!move_size=5000; {space for storing moves in a single octant}
+@!max_wiggle=300; {number of autorounded points per cycle}
+@!gf_buf_size=800; {size of the output buffer, must be a multiple of 8}
+@!file_name_size=40; {file names shouldn't be longer than this}
+@!pool_name='MFbases:MF.POOL ';
+ {string of length |file_name_size|; tells where the string pool appears}
+@y
+@<Constants...@>=
+@!mem_max=30000; {greatest index in \MF's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INIMF}, otherwise |>=mem_top|}
+@!max_internal=100; {maximum number of internal quantities}
+@!buf_size=500; {maximum number of characters simultaneously present in
+ current lines of open files; must not exceed |max_halfword|}
+@!error_line=79; {width of context lines on terminal error messages}
+@!half_error_line=50; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!screen_width=1024; {number of pixels in each row of screen display}
+@!screen_depth=800; {number of pixels in each column of screen display}
+@!stack_size=30; {maximum number of simultaneous input sources}
+@!max_strings=3000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=18000; {the minimum number of characters that should be
+ available for the user's identifier names and strings,
+ after \MF's own error messages are stored}
+@!pool_size=42000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all identifiers;
+ must exceed |string_vacancies| by the total
+ length of \MF's own strings, which is currently about 22000}
+@!move_size=5000; {space for storing moves in a single octant}
+@!max_wiggle=300; {number of autorounded points per cycle}
+@!gf_buf_size=800; {size of the output buffer, must be a multiple of 8}
+@!file_name_size=1024; {file names shouldn't be longer than this}
+@!pool_name='mf.pool';
+ {string of length |file_name_size|; tells where the string pool appears}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.12] sensitive compile-time constants
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d mem_min=0 {smallest index in the |mem| array, must not be less
+ than |min_halfword|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INIMF};
+ must be substantially larger than |mem_min|
+ and not greater than |mem_max|}
+@d hash_size=2100 {maximum number of symbolic tokens,
+ must be less than |max_halfword-3*param_size|}
+@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
+@d max_in_open=6 {maximum number of input files and error insertions that
+ can be going on simultaneously}
+@d param_size=150 {maximum number of simultaneous macro parameters}
+@y
+@d mem_min==-30000 {smallest index in the |mem| array, must not be less
+ than |min_halfword|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INIMF};
+ must be substantially larger than |mem_min|
+ and not greater than |mem_max|}
+@d hash_size=2100 {maximum number of symbolic tokens,
+ must be less than |max_halfword-3*param_size|}
+@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
+@d max_in_open=10 {maximum number of input files and error insertions that
+ can be going on simultaneously. Enlarged for Silvio Levy's Greek}
+@d param_size=150 {maximum number of simultaneous macro parameters}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.22] Permissive input
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+@y
+@^character set dependencies@>
+@^system dependencies@>
+
+@d tab = @'11 { ASCII horizontal tab }
+@d form_feed = @'14 { ASCII form-feed }
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:=chr(i);
+for i:=@'177 to @'377 do xchr[i]:=chr(i);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.24] file types
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+The program actually makes use also of a third kind of file, called a
+@y
+The pc compiler on Berkeley {\mc UNIX} won't pack a range into one byte unless
+the lower bound is |-128| or more, and the upper bound is |127| or less.
+This applies to files, too. To make the program work with compiler range
+checking turned on, we'll have to adjust |eight_bits| codes by sometimes
+adding or subtracting |256|.
+Note that |eight_bits| really takes 16 bits here.
+
+The program actually makes use also of a third kind of file, called a
+@z
+@x
+@!alpha_file=packed file of text_char; {files that contain textual data}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+@!alpha_file=t@&e@&x@&t; {files that contain textual data}
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.25] add real_name_of_file array for search path resolution
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \MF\ can open a file whose external name is specified by
+|name_of_file|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file:packed array[1..file_name_size] of char;@;@/
+ {on some systems this may be a \&{record} variable}
+@!name_length:0..file_name_size;@/{this many characters are actually
+ relevant in |name_of_file| (the rest are blank)}
+@y
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \MF\ can open a file whose external name is specified by
+|name_of_file|.
+The Berkeley {\mc UNIX} version uses search paths to look for files to open.
+We use |real_name_of_file| to hold the |name_of_file| with a directory
+name from the path in front of it.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file,@!real_name_of_file:packed array[1..file_name_size] of char;
+@;@/
+@!name_length:0..file_name_size;@/{this many characters are actually
+ relevant in |name_of_file| (the rest are blank)}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.26] file opening
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The \ph\ compiler with which the present version of \MF\ was prepared has
+extended the rules of \PASCAL\ in a very convenient way. To open file~|f|,
+we can write
+$$\vbox{\halign{#\hfil\qquad&#\hfil\cr
+|reset(f,@t\\{name}@>,'/O')|&for input;\cr
+|rewrite(f,@t\\{name}@>,'/O')|&for output.\cr}}$$
+The `\\{name}' parameter, which is of type `\ignorespaces|packed
+array[@t\<\\{any}>@>] of text_char|', stands for the name of
+the external file that is being opened for input or output.
+Blank spaces that might appear in \\{name} are ignored.
+
+The `\.{/O}' parameter tells the operating system not to issue its own
+error messages if something goes wrong. If a file of the specified name
+cannot be found, or if such a file cannot be opened for some other reason
+(e.g., someone may already be trying to write the same file), we will have
+|@!erstat(f)<>0| after an unsuccessful |reset| or |rewrite|. This allows
+\MF\ to undertake appropriate corrective action.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+\MF's file-opening procedures return |false| if no file identified by
+|name_of_file| could be opened.
+
+@d reset_OK(#)==erstat(#)=0
+@d rewrite_OK(#)==erstat(#)=0
+
+@p function a_open_in(var @!f:alpha_file):boolean;
+ {open a text file for input}
+begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f);
+end;
+@#
+function a_open_out(var @!f:alpha_file):boolean;
+ {open a text file for output}
+begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f);
+end;
+@#
+function b_open_out(var @!f:byte_file):boolean;
+ {open a binary file for output}
+begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f);
+end;
+@#
+function w_open_in(var @!f:word_file):boolean;
+ {open a word file for input}
+begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f);
+end;
+@#
+function w_open_out(var @!f:word_file):boolean;
+ {open a word file for output}
+begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f);
+end;
+@y
+@ The \ph\ compiler with which the present version of \MF\ was prepared has
+extended the rules of \PASCAL\ in a very convenient way for file opening.
+Berkeley {\mc UNIX} \PASCAL\ isn't nearly as nice as \ph.
+Normally, it bombs out if a file open fails.
+An external C procedure, |test_access| is used to check whether or not the
+open will work. It is declared in the ``ext.h'' include file, and it returns
+|true| or |false|. The |name_of_file| global holds the file name whose access
+is to be tested.
+The first parameter for |test_access| is the access mode,
+one of |read_access_mode| or |write_access_mode|.
+
+We also implement path searching in |test_access|: its second parameter is
+one of the ``file path'' constants defined below. If |name_of_file|
+doesn't start with |'/'| then |test_access| tries prepending pathnames
+from the appropriate path list until success or the end of path list
+is reached.
+On return, |real_name_of_file| contains the original name with the path
+that succeeded (if any) prepended. It is the name used in the various
+open procedures.
+
+Note that |a_open_in| has been redefined to take an additional argument,
+which should be one of the ``file path'' specifiers.
+Path searching is not done for output files.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {no path searching should be done}
+@d input_file_path=1 {path specifier for \.{\\input} files}
+@d base_file_path=4 {path specifier for base files}
+@d pool_file_path=5 {path specifier for the pool file}
+
+@p function a_open_in(var @!f:alpha_file;@!path_specifier:integer):boolean;
+ {open a text file for input}
+var @!ok:boolean;
+begin
+if test_access(read_access_mode,path_specifier) then
+ begin reset(f,real_name_of_file); ok:=true@+end
+else
+ ok:=false;
+a_open_in:=ok;
+end;
+@#
+function a_open_out(var @!f:alpha_file):boolean;
+ {open a text file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+a_open_out:=ok;
+end;
+@#
+function b_open_out(var f:byte_file):boolean;
+ {open a binary file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+b_open_out:=ok;
+end;
+@#
+function w_open_in(var @!f:word_file):boolean;
+ {open a word file for input}
+var @!ok:boolean;
+begin
+if test_access(read_access_mode,base_file_path) then
+ begin reset(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+w_open_in:=ok;
+end;
+@#
+function w_open_out(var @!f:word_file):boolean;
+ {open a word file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,name_of_file); ok:=true @+end
+else ok:=false;
+w_open_out:=ok;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.27] file closing
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+@p procedure a_close(var @!f:alpha_file); {close a text file}
+begin close(f);
+end;
+@#
+procedure b_close(var @!f:byte_file); {close a binary file}
+begin close(f);
+end;
+@#
+procedure w_close(var @!f:word_file); {close a word file}
+begin close(f);
+end;
+@y
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+With the pc library, files will be automatically closed when the program stops
+and when one reopens them.
+There is, however, the problem of opening a file and writing it, and then
+wanting to read it again, but from a different file variable.
+For this purpose we use external procedures |close_a|, |close_b|, and
+|close_w|.
+
+@p procedure a_close(var f:alpha_file); {close a text file}
+begin close_a(f);
+end;
+@#
+procedure b_close(var f:byte_file); {close a binary file}
+begin close_b(f);
+end;
+@#
+procedure w_close(var f:word_file); {close a word file}
+begin close_w(f);
+end;
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.30] faster version of input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Standard \PASCAL\ says that a file should have |eoln| immediately
+before |eof|, but \MF\ needs only a weaker restriction: If |eof|
+occurs in the middle of a line, the system function |eoln| should return
+a |true| result (even though |f^| will be undefined).
+
+@p function input_ln(var @!f:alpha_file;@!bypass_eoln:boolean):boolean;
+ {inputs the next line or returns |false|}
+var @!last_nonblank:0..buf_size; {|last| with trailing blanks removed}
+begin if bypass_eoln then if not eof(f) then get(f);
+ {input the first character of the line into |f^|}
+last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
+if eof(f) then input_ln:=false
+else begin last_nonblank:=first;
+ while not eoln(f) do
+ begin if last>=max_buf_stack then
+ begin max_buf_stack:=last+1;
+ if max_buf_stack=buf_size then
+ @<Report overflow of the input buffer, and abort@>;
+ end;
+ buffer[last]:=xord[f^]; get(f); incr(last);
+ if buffer[last-1]<>" " then last_nonblank:=last;
+ end;
+ last:=last_nonblank; input_ln:=true;
+ end;
+end;
+@y
+Standard \PASCAL\ says that a file should have |eoln| immediately
+before |eof|, but \MF\ needs only a weaker restriction: If |eof|
+occurs in the middle of a line, the system function |eoln| should return
+a |true| result (even though |f^| will be undefined).
+
+With Berkeley {\mc UNIX} we call an external C procedure, |line_read|.
+That routine fills |buffer| from |first| onwards with the |xord|'ed values
+of the next line, setting |last| appropriately. It will stop if
+|last=buf_size|, and the following will cause an ``overflow'' abort.
+We ignore the |bypass_eoln| argument, since the |line_read| procedure
+will have handled bypassing the end-of-line in its previous call, if
+there was one.
+
+If one uses |input_ln| on a file, the normal |read|
+and |get| routines shouldn't be used, nor the |eof| and |eoln| tests.
+End of file can be tested with the external function |test_eof|.
+
+@p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean;
+ {inputs the next line or returns |false|}
+label done;
+begin
+last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
+if test_eof(f) then input_ln:=false
+else begin last:=first;
+ line_read(f,buf_size);
+ if last>max_buf_stack then begin
+ max_buf_stack:=last;
+ if max_buf_stack>=buf_size then
+ @<Report overflow of the input buffer, and abort@>;
+ end;
+ loop@+ begin if last=first then goto done;
+ if buffer[last-1]<>" " then goto done;
+ decr(last);
+ end;
+done: input_ln:=true;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.31] term_in/out are input,output
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file variable is called |term_in|, and when it
+is considered an output file the file variable is |term_out|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!term_in:alpha_file; {the terminal as an input file}
+@!term_out:alpha_file; {the terminal as an output file}
+@y
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file is called |term_in|, and when it
+is considered an output file the file is |term_out|.
+For Berkeley {\mc UNIX} we use |standard_input| and |output| for these
+files, as were declared in the program header. The former name
+|standard_input| was defined using a web trick so that ``input'' is
+produced in the Pascal file.
+
+@d term_in==standard_input {the terminal as an input file}
+@d term_out==output {the terminal as an output file}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.32] don't need to open terminal files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Here is how to open the terminal files
+in \ph. The `\.{/I}' switch suppresses the first |get|.
+@^system dependencies@>
+
+@d t_open_in==reset(term_in,'TTY:','/O/I') {open the terminal for text input}
+@d t_open_out==rewrite(term_out,'TTY:','/O') {open the terminal for text output}
+@y
+@ Here is how to open the terminal files---do nothing.
+
+@d t_open_in == {input already open for text input}
+@d t_open_out == {output already open for text output}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.33] flushing output
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+these operations can be specified in \ph:
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@d clear_terminal == break_in(term_in,true) {clear the terminal input buffer}
+@d wake_up_terminal == do_nothing {cancel the user's cancellation of output}
+@y
+these operations can be specified with Berkeley {\mc UNIX}:
+@^system dependencies@>
+
+@d update_terminal == flush(output)
+@d clear_terminal == {nothing necessary on UNIX}
+@d wake_up_terminal == {nothing necessary on UNIX}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.36] rescanning the command line
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following program does the required initialization
+without retrieving a possible command line.
+It should be clear how to modify this routine to deal with command lines,
+if the system permits them.
+@^system dependencies@>
+
+@p function init_terminal:boolean; {gets the terminal input started}
+label exit;
+begin t_open_in;
+loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal;
+@.**@>
+ if not input_ln(term_in,true) then {this shouldn't happen}
+ begin write_ln(term_out);
+ write(term_out,'! End of file on the terminal... why?');
+@.End of file on the terminal@>
+ init_terminal:=false; return;
+ end;
+ loc:=first;
+ while (loc<last)and(buffer[loc]=" ") do incr(loc);
+ if loc<last then
+ begin init_terminal:=true;
+ return; {return unless the line was all blank}
+ end;
+ write_ln(term_out,'Please type the name of your input file.');
+ end;
+exit:end;
+@y
+@ The following program does the required initialization
+and also retrieves a possible command line.
+@^system dependencies@>
+
+@p
+function init_terminal:boolean; {gets the terminal input started}
+label exit;
+
+var
+ dummy, i, j, k: integer;
+ arg: packed array[1..100] of char;
+
+begin
+ t_open_in;
+ if argc > 1 then begin
+ last := first;
+ for i := 1 to argc - 1 do begin
+ argv(i, arg);
+ j := 1;
+ k := 100; {find last non-blank char in arg}
+ while (k > 1) and (arg[k] = ' ') do@/
+ decr(k);
+ while (j <= k) do begin
+ buffer[last] := xord[arg[j]];
+ incr(j); incr(last);
+ end;
+ if k > 1 then begin
+ buffer[last] := xord[' '];
+ incr(last);
+ end;
+ end;
+ if last > first then begin
+ loc := first;
+ init_terminal := true;
+ return;
+ end;
+ end;
+ loop@+begin
+ wake_up_terminal; write(term_out, '**'); update_terminal;
+@.**@>
+ if not input_ln(term_in,true) then begin {this shouldn't happen}
+ write_ln(term_out);
+ write_ln(term_out, '! End of file on the terminal... why?');
+@.End of file on the terminal@>
+ init_terminal:=false;
+ return;
+ end;
+
+ loc:=first;
+ while (loc<last)and(buffer[loc]=" ") do
+ incr(loc);
+
+ if loc<last then begin
+ init_terminal:=true;
+ return; {return unless the line was all blank}
+ end;
+ write_ln(term_out, 'Please type the name of your input file.');
+@.Please type the name...@>
+ end;
+exit:
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4.51] a_open_in of pool file needs path specifier
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if a_open_in(pool_file) then
+@y
+if a_open_in(pool_file,pool_file_path) then
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4.51,52,53] make MF.POOL lowercase in messages
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+else bad_pool('! I can''t read MF.POOL.')
+@y
+else bad_pool('! I can''t read mf.pool.')
+@z
+@x
+begin if eof(pool_file) then bad_pool('! MF.POOL has no check sum.');
+@y
+begin if eof(pool_file) then bad_pool('! mf.pool has no check sum.');
+@z
+@x
+ bad_pool('! MF.POOL line doesn''t begin with two digits.');
+@y
+ bad_pool('! mf.pool line doesn''t begin with two digits.');
+@z
+@x
+ bad_pool('! MF.POOL check sum doesn''t have nine digits.');
+@y
+ bad_pool('! mf.pool check sum doesn''t have nine digits.');
+@z
+@x
+done: if a<>@$ then bad_pool('! MF.POOL doesn''t match; TANGLE me again.');
+@y
+done: if a<>@$ then bad_pool('! mf.pool doesn''t match; tangle me again.');
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6.79] switch-to-editor option
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+line ready to be edited. But such an extension requires some system
+wizardry, so the present implementation simply types out the name of the
+file that should be
+edited and the relevant line number.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines haven't
+been commented~out.
+@^debugging@>
+@y
+line ready to be edited.
+We do this by calling the external procedure |calledit| with a pointer to
+the filename, its length, and the line number.
+However, here we just set up the variables that will be used as arguments,
+since we don't want to do the switch-to-editor until after \MF\ has closed
+its files.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines have
+not been commented out.
+@^debugging@>
+@d edit_file==input_stack[file_ptr]
+@z
+@x
+"E": if file_ptr>0 then
+ begin print_nl("You want to edit file ");
+@.You want to edit file x@>
+ slow_print(input_stack[file_ptr].name_field);
+ print(" at line "); print_int(line);@/
+ interaction:=scroll_mode; jump_out;
+@y
+"E": if file_ptr>0 then
+ begin
+ ed_name_start:=str_start[edit_file.name_field];
+ ed_name_length:=str_start[edit_file.name_field+1] -
+ str_start[edit_file.name_field];
+ edit_line:=line;
+ jump_out;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7.107,108] replace make_fraction with external routine
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function make_fraction(@!p,@!q:integer):fraction;
+var @!f:integer; {the fraction bits, with a leading 1 bit}
+@!n:integer; {the integer part of $\vert p/q\vert$}
+@!negative:boolean; {should the result be negated?}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin if p>=0 then negative:=false
+else begin negate(p); negative:=true;
+ end;
+if q<=0 then
+ begin debug if q=0 then confusion("/");@;@+gubed@;@/
+@:this can't happen /}{\quad \./@>
+ negate(q); negative:=not negative;
+ end;
+n:=p div q; p:=p mod q;
+if n>=8 then
+ begin arith_error:=true;
+ if negative then make_fraction:=-el_gordo@+else make_fraction:=el_gordo;
+ end
+else begin n:=(n-1)*fraction_one;
+ @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>;
+ if negative then make_fraction:=-(f+n)@+else make_fraction:=f+n;
+ end;
+end;
+
+@ The |repeat| loop here preserves the following invariant relations
+between |f|, |p|, and~|q|:
+(i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and
+$p_0$ is the original value of~$p$.
+
+Notice that the computation specifies
+|(p-q)+p| instead of |(p+p)-q|, because the latter could overflow.
+Let us hope that optimizing compilers do not miss this point; a
+special variable |be_careful| is used to emphasize the necessary
+order of computation. Optimizing compilers should keep |be_careful|
+in a register, not store it in memory.
+@^inner loop@>
+
+@<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>=
+f:=1;
+repeat be_careful:=p-q; p:=be_careful+p;
+if p>=0 then f:=f+f+1
+else begin double(f); p:=p+q;
+ end;
+until f>=fraction_one;
+be_careful:=p-q;
+if be_careful+p>=0 then incr(f)
+
+@y
+Under {\mc UNIX} \MF, we have replaced the \PASCAL\ version of |make_fraction|
+with either a C or assembly language equivalent for efficiency.
+
+@ This section was deleted when |make_fraction| was removed.
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7.109,110,111] replace take_fraction with external version
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function take_fraction(@!q:integer;@!f:fraction):integer;
+var @!p:integer; {the fraction so far}
+@!negative:boolean; {should the result be negated?}
+@!n:integer; {additional multiple of $q$}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin @<Reduce to the case that |f>=0| and |q>0|@>;
+if f<fraction_one then n:=0
+else begin n:=f div fraction_one; f:=f mod fraction_one;
+ if q<=el_gordo div n then n:=n*q
+ else begin arith_error:=true; n:=el_gordo;
+ end;
+ end;
+f:=f+fraction_one;
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>;
+be_careful:=n-el_gordo;
+if be_careful+p>0 then
+ begin arith_error:=true; n:=el_gordo-p;
+ end;
+if negative then take_fraction:=-(n+p)
+else take_fraction:=n+p;
+end;
+
+@ @<Reduce to the case that |f>=0| and |q>0|@>=
+if f>=0 then negative:=false
+else begin negate(f); negative:=true;
+ end;
+if q<0 then
+ begin negate(q); negative:=not negative;
+ end;
+
+@ The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor
+=\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and
+$f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$.
+@^inner loop@>
+
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>=
+p:=fraction_half; {that's $2^{27}$; the invariants hold now with $k=28$}
+if q<fraction_four then
+ repeat if odd(f) then p:=half(p+q)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+else repeat if odd(f) then p:=p+half(q-p)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+
+@y
+Function |take_fraction| has also been replaced by an external routine.
+
+@ @<Reduce to the case that |f>=0| and |q>0|@>=
+if f>=0 then negative:=false
+else begin negate(f); negative:=true;
+ end;
+if q<0 then
+ begin negate(q); negative:=not negative;
+ end;
+
+@ This section was deleted when |take_fraction| was replaced by an
+external routine. (But the previous section was left in, because
+it is part of |take_scaled|, which follows.)
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [9.153] ranges for quarter,half words
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+can get 256 values into a quarterword only if the subrange is `|-128..127|'.
+
+The present implementation tries to accommodate as many variations as possible,
+so it makes few assumptions. If integers having the subrange
+`|min_quarterword..max_quarterword|' can be packed into a quarterword,
+and if integers having the subrange `|min_halfword..max_halfword|'
+can be packed into a halfword, everything should work satisfactorily.
+
+It is usually most efficient to have |min_quarterword=min_halfword=0|,
+so one should try to achieve this unless it causes a severe problem.
+The values defined here are recommended for most 32-bit computers.
+
+@d min_quarterword=0 {smallest allowable value in a |quarterword|}
+@d max_quarterword=255 {largest allowable value in a |quarterword|}
+@d min_halfword==0 {smallest allowable value in a |halfword|}
+@d max_halfword==65535 {largest allowable value in a |halfword|}
+@y
+can get 256 values into a quarterword only if the subrange is `|-128..127|'.
+
+For Berkeley {\mc UNIX} we need to do the |-128..127| kind of range.
+
+@d min_quarterword=-128 {smallest allowable value in a |quarterword|}
+@d max_quarterword=127 {largest allowable value in a |quarterword|}
+@d min_halfword==-32768 {smallest allowable value in a |halfword|}
+@d max_halfword==32767 {largest allowable value in a |halfword|}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [11.178] fix the word "free" so that it doesn't conflict with a runtime proc
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+@y
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+
+@d free==free_arr
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [12.194] fix_date_and_time
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following procedure, which is called just before \MF\ initializes its
+input and output, establishes the initial values of the date and time.
+@^system dependencies@>
+Since standard \PASCAL\ cannot provide such information, something special
+is needed. The program here simply specifies July 4, 1776, at noon; but
+users probably want a better approximation to the truth.
+
+Note that the values are |scaled| integers. Hence \MF\ can no longer
+be used after the year 32767.
+
+@p procedure fix_date_and_time;
+begin internal[time]:=12*60*unity; {minutes since midnight}
+internal[day]:=4*unity; {fourth day of the month}
+internal[month]:=7*unity; {seventh month of the year}
+internal[year]:=1776*unity; {Anno Domini}
+end;
+@y
+@ The following procedure, which is called just before \MF\ initializes its
+input and output, establishes the initial values of the date and time.
+It is calls an externally defined |date_and_time|, even though it could
+be done from Pascal.
+The external procedure also sets up interrupt catching.
+@^system dependencies@>
+
+Note that the values are |scaled| integers. Hence \MF\ can no longer
+be used after the year 32767.
+
+@p procedure fix_date_and_time;
+begin
+ date_and_time(internal[time],internal[day],internal[month],internal[year]);
+ internal[time] := internal[time] * unity;
+ internal[day] := internal[day] * unity;
+ internal[month] := internal[month] * unity;
+ internal[year] := internal[year] * unity;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [12.199] allow <tab> and <ff> as input
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for k:=0 to " "-1 do char_class[k]:=invalid_class;
+for k:=127 to 255 do char_class[k]:=invalid_class;
+@y
+for k:=0 to " "-1 do char_class[k]:=invalid_class;
+for k:=127 to 255 do char_class[k]:=invalid_class;
+char_class[tab]:=space_class;
+char_class[form_feed]:=space_class;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27.564] init_screen and update_screen are external C procs
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% BEGIN HOOK FOR THE TRAP TEST
+%@x
+%begin init_screen:=false;
+%@y
+%begin init_screen:=true;
+%@z
+@x
+The \PASCAL\ code here is a minimum version of |init_screen| and
+|update_screen|, usable on \MF\ installations that don't
+support screen output. If |init_screen| is changed to return |true|
+instead of |false|, the other routines will simply log the fact
+that they have been called; they won't really display anything.
+The standard test routines for \MF\ use this log information to check
+that \MF\ is working properly, but the |wlog| instructions should be
+removed from production versions of \MF.
+
+@p function init_screen:boolean;
+begin init_screen:=false;
+end;
+@#
+procedure update_screen; {will be called only if |init_screen| returns |true|}
+begin @!init wlog_ln('Calling UPDATESCREEN');@+tini {for testing only}
+end;
+
+@y
+We declare the screen functions
+for the {\mc UNIX} version of \MF\ in the following header file.
+
+@p
+@\@=#include "mf_window.h"@>@\ {declarations for external C procedures}
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27.559] blank_rectangle is an external C proc
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+The commented-out code in the following procedure is for illustrative
+purposes only.
+@^system dependencies@>
+
+@p procedure blank_rectangle(@!left_col,@!right_col:screen_col;
+ @!top_row,@!bot_row:screen_row);
+var @!r:screen_row;
+@!c:screen_col;
+begin @{@+for r:=top_row to bot_row-1 do
+ for c:=left_col to right_col-1 do
+ screen_pixel[r,c]:=white;@+@}@/
+@!init wlog_cr; {this will be done only after |init_screen=true|}
+wlog_ln('Calling BLANKRECTANGLE(',left_col:1,',',
+ right_col:1,',',top_row:1,',',bot_row:1,')');@+tini
+end;
+
+@y
+@^system dependencies@>
+
+The procedure |blank_rectangle| has been declared an external routine
+for the {\mc UNIX} version of \MF\ in the header file above.
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27.560] paint_row is an external C proc
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+The general idea is to paint blocks of pixels in alternate colors;
+the precise details are best conveyed by means of a \PASCAL\
+program (see the commented-out code below).
+@^system dependencies@>
+
+@p procedure paint_row(@!r:screen_row;@!b:pixel_color;var @!a:trans_spec;
+ @!n:screen_col);
+var @!k:screen_col; {an index into |a|}
+@!c:screen_col; {an index into |screen_pixel|}
+begin @{ k:=0; c:=a[0];
+repeat incr(k);
+ repeat screen_pixel[r,c]:=b; incr(c);
+ until c=a[k];
+ b:=black-b; {$|black|\swap|white|$}
+ until k=n;@+@}@/
+@!init wlog('Calling PAINTROW(',r:1,',',b:1,';');
+ {this is done only after |init_screen=true|}
+for k:=0 to n do
+ begin wlog(a[k]:1); if k<>n then wlog(',');
+ end;
+wlog_ln(')');@+tini
+end;
+
+@y
+@^system dependencies@>
+
+|Paint_row| has been declared an external routine in the {\mc UNIX} version
+of \MF\ in the header file above.
+
+@z
+% END HOOK FOR THE TRAP TEST
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.768] area and extension rules
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+@y
+@ The file names we shall deal with for SunOS have the
+following structure: If the name contains `\./', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep
+track of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\./', if any}
+@!ext_delimiter:pool_pointer; {the most recent `\..', if any}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.769] MF area directories
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d MF_area=="MFinputs:"
+@.MFinputs@>
+@y
+Under Berkeley {\mc UNIX}, the default paths are specified in a seprate
+file, `\.{mfpaths.h}'. The file opening procedures do path searching
+based either on those default paths, or on paths given by the user
+in ``environment'' variables.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.771] more_name
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+begin if c=" " then more_name:=false
+else begin if (c=">")or(c=":") then
+@y
+begin if (c=" ")or(c=tab) then more_name:=false
+else begin if (c="/") then
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.775] default base
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d base_default_length=18 {length of the |MF_base_default| string}
+@d base_area_length=8 {length of its area part}
+@d base_ext_length=5 {length of its `\.{.base}' part}
+@y
+Under Berkeley {\mc UNIX} we don't give the area part, instead depending
+on the path searching that will happen during file opening.
+
+@d base_default_length=10 {length of the |MF_base_default| string}
+@d base_area_length=0 {length of its area part}
+@d base_ext_length=5 {length of its `\.{.base}' part}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.776] plain base location
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+MF_base_default:='MFbases:plain.base';
+@y
+MF_base_default:='plain.base';
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.779] w_open_in of base file needs to be called only once
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(base_file) then goto found;
+ pack_buffered_name(base_area_length,loc,j-1);
+ {now try the system base file area}
+ if w_open_in(base_file) then goto found;
+@y
+ pack_buffered_name(0,loc,j-1);
+ if w_open_in(base_file) then goto found;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.780] make_name_string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+which simply makes a \MF\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var @!k:1..file_name_size; {index into |name_of_file|}
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings) then
+ make_name_string:="?"
+else begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+end;
+@y
+which simply makes a \MF\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+With the Berkeley {\mc UNIX} version, we know that |real_name_of_file|
+contains |name_of_file| prepended with the directory name that was found
+by path searching.
+If |real_name_of_file| starts with |'./'|, we don't use that part of the
+name, since {\mc UNIX} users understand that.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var @!k,@!kstart:1..file_name_size; {index into |name_of_file|}
+begin
+k:=1;
+while (k<file_name_size) and (xord[real_name_of_file[k]]<>" ") do
+ incr(k);
+name_length:=k-1; {the real |name_length|}
+if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings) then
+ make_name_string:="?"
+else begin
+ if (xord[real_name_of_file[1]]=".") and (xord[real_name_of_file[2]]="/") then
+ kstart:=3
+ else
+ kstart:=1;
+ for k:=kstart to name_length do append_char(xord[real_name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.781] scan_file_name ignore leading tabs as well as spaces
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure scan_file_name;
+label done;
+begin begin_name;
+while buffer[loc]=" " do incr(loc);
+@y
+@p procedure scan_file_name;
+label done;
+begin begin_name;
+while (buffer[loc]=" ")or(buffer[loc]=tab) do incr(loc);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.787] <Scan_file_name...> needs similar leading tab treatment
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ @<Scan file name in the buffer@>=
+begin begin_name; k:=first;
+while (buffer[k]=" ")and(k<last) do incr(k);
+@y
+@ @<Scan file name in the buffer@>=
+begin begin_name; k:=first;
+while ((buffer[k]=" ")or(buffer[k]=tab))and(k<last) do incr(k);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.793] a_open_in of input file needs path selector
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ if a_open_in(cur_file) then goto done;
+ if cur_area="" then
+ begin pack_file_name(cur_name,MF_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+ end;
+@y
+ if a_open_in(cur_file,input_file_path) then goto done;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.793] get rid of return of name to string pool
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if name=str_ptr-1 then {we can conserve string pool space now}
+ begin flush_string(name); name:=cur_name;
+ end;
+@y
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [45.1133] writing the tfm file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d tfm_out(#)==write(tfm_file,#) {output one byte to |tfm_file|}
+@y
+@d tfm_out(#) ==write(tfm_file,((#+128) mod 256)-128)
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [47.1154] write_gf
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Declare generic font output procedures@>=
+procedure write_gf(@!a,@!b:gf_index);
+var k:gf_index;
+begin for k:=a to b do write(gf_file,gf_buf[k]);
+end;
+@y
+For Berkeley {\mc UNIX}, this is going to be handled by an external procedure,
+|writegf|, which will do the output using |fwrite|.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [49.1204] Delete ready_already; also add call to set_paths;
+% also add call to exit() depending upon value of `history'
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Now this is really it: \MF\ starts and ends here.
+
+The initial test involving |ready_already| should be deleted if the
+\PASCAL\ runtime system is smart enough to detect such a ``mistake.''
+@y
+@ Now this is really it: \MF\ starts and ends here.
+
+Instead of testing |ready_already|, we give up the idea of a preloaded
+core image in favor of silently loading a format file based on the
+name by which this program has been invoked. For example, if the
+program is being called \.{mf}, we will load the \.{plain} base;
+if \.{cmmf}, we will load \.{cmmf.base}. On my machine the
+loading of a format isn't too slow. (I use the new string capabilities
+of Sun Pascal here; |my_name| will be declared {\bf varying} [100]
+{\bf of\/} |char|.)
+
+At the end of the job, we look at |history| to decide the correct
+exit code. We use 1 if |history <= warning_issued| and 0 otherwise.
+
+@d UNIXexit==e@&x@&i@&t
+@z
+
+@x
+t_open_out; {open the terminal for output}
+if ready_already=314159 then goto start_of_MF;
+@y
+t_open_out; {open the terminal for output}
+set_paths;
+@z
+@x
+start_of_MF: @<Initialize the output routines@>;
+@y
+start_of_MF: argv(0,my_name);
+if (my_name<>'inimf')and(my_name<>'trapmf') then
+ begin if my_name = 'mf' then name_of_file:='plain.base'
+ else name_of_file:=my_name+'.base';
+ if w_open_in(base_file) then
+ if not load_base_file then
+ begin w_close(base_file); goto final_end;
+ end
+ else w_close(base_file);
+ end;
+@<Initialize the output routines@>;
+@z
+
+@x
+final_end: ready_already:=0;
+@y
+final_end: ready_already:=0;
+if (history <= warning_issued) then
+ UNIXexit(0)
+else
+ UNIXexit(1);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [49.1204] print new line before termination; switch to editor if nec.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+@y
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+print_ln;
+if (ed_name_start<>0) and (interaction>batch_mode) then
+ calledit(str_pool[ed_name_start],ed_name_length,edit_line);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [51.1214] add editor-switch variable to globals
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* \[51] System-dependent changes.
+This section should be replaced, if necessary, by any special
+modifications of the program
+that are necessary to make \MF\ work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the published program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+@* \[51] System-dependent changes.
+Here are the variables used to hold ``switch-to-editor'' information.
+And the omphaloskeptic variable too.
+@^system dependencies@>
+
+@<Global...@>=
+@!ed_name_start: pool_pointer;
+@!ed_name_length,@!edit_line: integer;
+@!my_name:varying [100] of char;
+
+@ The |ed_name_start| will be set to point into |str_pool| somewhere after
+its beginning if \MF\ is supposed to switch to an editor on exit.
+
+@<Set init...@>=
+ed_name_start:=0;
+@z
+
diff --git a/systems/knuth/local/mf-sparc/mf.pool b/systems/knuth/local/mf-sparc/mf.pool
new file mode 100644
index 0000000000..4f1478cac2
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/mf.pool
@@ -0,0 +1,824 @@
+11buffer size
+09pool size
+17number of strings
+03???
+28End of file on the terminal!
+02!
+42(That makes 100 errors; please try again.)
+02?
+60Type <return> to proceed, S to scroll future error messages,
+44R to run without stopping, Q to run quietly,
+23I to insert something,
+20E to edit your file,
+561 or ... or 9 to ignore the next 1 to 9 tokens of input,
+22H for help, X to quit.
+13OK, entering
+09batchmode
+11nonstopmode
+10scrollmode
+03...
+07insert>
+44I have just deleted some text, as you asked.
+48You can now delete more, or insert, or whatever.
+50Sorry, I don't know how to help in this situation.
+36Maybe you should try asking a human?
+42Sorry, I already gave what help I could...
+59An error might have occurred before I noticed any problems.
+45``If all else fails, read the instructions.''
+00
+14Emergency stop
+35METAFONT capacity exceeded, sorry [
+44If you really absolutely need more capacity,
+35you can ask a wizard to enlarge me.
+19This can't happen (
+59I'm broken. Please show this to someone who can fix can fix
+35I can't go on meeting you like this
+55One of your faux pas seems to have wounded me deeply...
+59in fact, I'm barely conscious. Please fix it and try again.
+12Interruption
+09You rang?
+57Try to insert some instructions for me (e.g.,`I show x'),
+43unless you just want to quit by typing `X'.
+09Missing `
+19' has been inserted
+19Arithmetic overflow
+59Uh, oh. A little while ago one of the quantities that I was
+59computing got too large, so I'm afraid your answers will be
+55somewhat askew. You'll probably have to adopt different
+54tactics next time. But I shall try to carry on anyway.
+15Square root of
+23 has been replaced by 0
+52Since I don't take square roots of negative numbers,
+52I'm zeroing this one. Proceed, with fingers crossed.
+24Pythagorean subtraction
+03+-+
+13Logarithm of
+48Since I don't take logs of non-positive numbers,
+27angle(0,0) is taken as zero
+54The `angle' between two identical points is undefined.
+16main memory size
+24AVAIL list clobbered at
+31Double-AVAIL list clobbered at
+24Doubly free location at
+12Bad flag at
+14New busy locs:
+05LINK(
+05INFO(
+07vacuous
+07boolean
+15unknown boolean
+06string
+14unknown string
+03pen
+11unknown pen
+10future pen
+04path
+12unknown path
+07picture
+15unknown picture
+09transform
+04pair
+13known numeric
+09dependent
+15proto-dependent
+07numeric
+11independent
+10token list
+10structured
+16unsuffixed macro
+14suffixed macro
+09undefined
+04true
+05false
+11nullpicture
+07nullpen
+07jobname
+10readstring
+09pencircle
+13normaldeviate
+03odd
+05known
+07unknown
+03not
+07decimal
+07reverse
+08makepath
+07makepen
+11totalweight
+03oct
+03hex
+05ASCII
+04char
+06length
+13turningnumber
+05xpart
+05ypart
+06xxpart
+06xypart
+06yxpart
+06yypart
+04sqrt
+04mexp
+04mlog
+04sind
+04cosd
+05floor
+14uniformdeviate
+10charexists
+05angle
+05cycle
+02++
+02or
+03and
+02<=
+02>=
+02<>
+07rotated
+07slanted
+06scaled
+07shifted
+11transformed
+07xscaled
+07yscaled
+07zscaled
+17intersectiontimes
+09substring
+07subpath
+13directiontime
+05point
+10precontrol
+11postcontrol
+09penoffset
+02..
+13tracingtitles
+16tracingequations
+15tracingcapsules
+14tracingchoices
+12tracingspecs
+11tracingpens
+15tracingcommands
+15tracingrestores
+13tracingmacros
+12tracingedges
+13tracingoutput
+12tracingstats
+13tracingonline
+04year
+05month
+03day
+04time
+08charcode
+07charext
+06charwd
+06charht
+06chardp
+06charic
+06chardx
+06chardy
+10designsize
+04hppp
+04vppp
+07xoffset
+07yoffset
+07pausing
+12showstopping
+10fontmaking
+08proofing
+09smoothing
+12autorounding
+11granularity
+06fillin
+12turningcheck
+12warningcheck
+12boundarychar
+09 at line
+14a bad variable
+02fi
+08endgroup
+06enddef
+06endfor
+13 INACCESSIBLE
+09hash size
+06EQUIV(
+02::
+03||:
+02:=
+05addto
+02at
+07atleast
+10begingroup
+08controls
+04cull
+04curl
+10delimiters
+07display
+08everyjob
+06exitif
+11expandafter
+04from
+08inwindow
+07interim
+03let
+11newinternal
+02of
+10openwindow
+10randomseed
+04save
+10scantokens
+07shipout
+06skipto
+04step
+03str
+07tension
+02to
+05until
+05token
+05 ETC.
+10 CLOBBERED
+11 IMPOSSIBLE
+12 NONEXISTENT
+04 BAD
+02[]
+05(EXPR
+07(SUFFIX
+05(TEXT
+02->
+03>->
+08<expr>->
+19<expr>of<primary>->
+10<suffix>->
+08<text>->
+07(SAVED)
+03var
+02xx
+02xy
+02yx
+02yy
+08%CAPSULE
+05part
+06struct
+11{restoring
+04Path
+07{open?}
+10..control?
+03 ..
+06{curl
+10..tension
+05 and
+11..controls
+02??
+16, before choices
+15, after choices
+23Some number got too big
+46The path that I just computed is out of range.
+53So it will probably look funny. Proceed, for a laugh.
+09path size
+14Edge structure
+04row
+02 |
+31Scaled picture would be too big
+50I can't yscale the picture as requested---it would
+45make some coordinates too large or too small.
+42Proceed, and I'll omit the transformation.
+50I can't xscale the picture as requested---it would
+15move table size
+13Tracing edges
+09 (weight
+21(No new edges added.)
+10Cycle spec
+24 % beginning in octant `
+19% entering octant `
+08 & cycle
+03ENE
+03NNE
+03NNW
+03WNW
+03WSW
+03SSW
+03SSE
+03ESE
+14 ..controls
+11 % segment
+02 (
+33, before subdivision into octants
+19, after subdivision
+43, after subdivision and double autorounding
+36, after subdivision and autorounding
+18Curve out of range
+56At least one of the coordinates in the path I'm about to
+56digitize was really huge (potentially bigger than 4095).
+40So I've cut it back to the maximum size.
+41The results will probably be pretty wild.
+19rounding table size
+11Pen polygon
+09 .. cycle
+04 ..
+16 (newly created)
+13Pen too large
+59The cycle you specified has a coordinate of 4095.5 or more.
+55So I've replaced it by the trivial path `(0,0)..cycle'.
+24Pen cycle must be convex
+59The cycle you specified either has consecutive equal points
+54or turns right or turns through more than 360 degrees.
+16pen polygon size
+09@ Octant
+07 offset
+08), from
+04 to
+23@ retrograde line from
+18@ transition line
+07, from
+03dep
+02*4
+20Value is too large (
+53The equation I just processed has given some variable
+54a value of 4096 or more. Continue and I'll try to cope
+47with that big value; but it might be dangerous.
+47(Set warningcheck:=0 to suppress this message.)
+05####
+03##
+15Bad PREVDEP at
+16Out of order at
+18Redundant equation
+43I already knew that this equation was true.
+50But perhaps no harm has been done; let's continue.
+23[unknown command code!]
+03<*>
+08<insert>
+12<scantokens>
+02l.
+10<forever>
+11<argument>
+16<recently read>
+19<to be read again>
+16<inserted text>
+05<for(
+03)>
+16input stack size
+17text input levels
+08endinput
+47Incomplete if; all text was ignored after line
+51A forbidden `outer' token occurred in skipped text.
+58This kind of error happens when you say `if...' and forget
+57the matching `fi'. I've inserted a `fi'; this might work.
+53The file ended while I was skipping conditional text.
+10File ended
+21Forbidden token found
+16 while scanning
+41I suspect you have forgotten an `enddef',
+52causing me to read past where you wanted me to stop.
+49I'll try to recover; but if the error is serious,
+51you'd better type `E' or `X' now and fix your file.
+27to the end of the statement
+42A previous error seems to have propagated,
+15a text argument
+45It seems that a right delimiter was left out,
+18the definition of
+14the text of a
+05 loop
+41I suspect you have forgotten an `endfor',
+08Runaway
+05text?
+11definition?
+05loop?
+39Text line contains an invalid character
+53A funny symbol that I can't read has just been input.
+48Continue, and I'll forget that it ever happened.
+40Incomplete string token has been flushed
+53Strings should finish on the same line as they began.
+50I've deleted the partial string; you might want to
+48insert another by typing, e.g., `I"new string"'.
+32Enormous number has been reduced
+52I can't handle numbers bigger than about 4095.99998;
+53so I've changed your constant to that maximum amount.
+36(Please type a command or say `end')
+37*** (job aborted, no legal end found)
+02=>
+03def
+06vardef
+10primarydef
+12secondarydef
+11tertiarydef
+03for
+11forsuffixes
+07forever
+05quote
+02#@
+02@#
+31Missing symbolic token inserted
+52Sorry: You can't redefine a number, string, or expr.
+49I've inserted an inaccessible symbol so that your
+60definition will be completed without mixing me up too badly.
+51Sorry: You can't redefine my error-recovery tokens.
+50The next thing in this `def' should have been `=',
+54because I've already looked at the definition heading.
+49But don't worry; I'll pretend that an equals sign
+45was present. Everything from here to `enddef'
+43will be the replacement text of this macro.
+04expr
+06suffix
+04text
+07primary
+09secondary
+08tertiary
+41This variable already starts with a macro
+44After `vardef a' you can't say `vardef a.b'.
+40So I'll have to discard this definition.
+46Missing parameter type; `expr' will be assumed
+52You should've had `expr' or `suffix' or `text' here.
+20parameter stack size
+14Extra `endfor'
+40I'm not currently working on a for loop,
+40so I had better not try to end anything.
+05input
+09Lost loop
+53I'm confused; after exiting from a loop, I still seem
+53to want to repeat it. I'll try to forget the problem.
+22No loop is in progress
+51Why say `exitif' when there's nothing to exit from?
+57After `exitif <boolean exp>' I expect to see a semicolon.
+35I shall pretend that one was there.
+20*** (loop confusion)
+12Not a string
+41I'm going to flush this expression, since
+48scantokens should be followed by a known string.
+03)<-
+22Too many arguments to
+11 Missing `
+52I'm going to assume that the comma I just read was a
+57right delimiter, and then I'll begin expanding the macro.
+55You might want to delete some tokens before continuing.
+20Missing argument to
+48That macro has more parameters than you thought.
+54I'll continue by pretending that each missing argument
+23is either zero or null.
+54I've finished reading a macro argument and am about to
+56read another; the arguments weren't delimited correctly.
+51I've gotten to the end of the macro parameter list.
+05 for
+57I've got the first argument; will look now for the other.
+02if
+04else
+06elseif
+49There should've been a colon after the condition.
+06{true}
+07{false}
+06Extra
+43I'm ignoring this; it doesn't match any if.
+09Improper
+38When you say `for x=a step b until c',
+43the initial value `a' and the step size `b'
+55and the final value `c' must have known numeric values.
+57The next thing in this loop should have been `=' or `:='.
+47was present, and I'll look for the values next.
+51The next thing in this loop should have been a `:'.
+41So I'll pretend that a colon was present;
+50everything from here to `endfor' will be iterated.
+07 ENDFOR
+12{loop value=
+13initial value
+09step size
+47I assume you meant to say `until' after `step'.
+48So I'll look for the final value and colon next.
+11final value
+05.base
+15input file name
+19I can't find file `
+23I can't write on file `
+02'.
+03.mf
+20Please type another
+45*** (job aborted, file error in nonstop mode)
+02:
+05mfput
+04.log
+02**
+20transcript file name
+02
+20file name for output
+37File names can't appear within macros
+46Sorry...I've converted what follows to tokens,
+37possibly garbaging the name you gave.
+51Please delete the tokens and insert the name again.
+03exp
+26 (see the transcript file)
+13 (future pen)
+10linearform
+03>>
+07recycle
+04###
+03 =
+09A primary
+30 expression can't begin with `
+58I'm afraid I need some sort of value in order to continue,
+49so I've tentatively inserted `0'. You may want to
+43delete this zero and insert something else;
+50see Chapter 27 of The METAFONTbook for an example.
+39Nonnumeric ypart has been replaced by 0
+48I thought you were giving me a pair `(x,y)'; but
+50after finding a nice xpart `x' I found a ypart `y'
+54that isn't of numeric type. So I've changed y to zero.
+59(The y that I didn't like appears above the error message.)
+22A group begun on line
+12 never ended
+56I saw a `begingroup' back there that hasn't been matched
+47by `endgroup'. So I've inserted `endgroup' now.
+16Division by zero
+43I'll pretend that you meant to divide by 1.
+44Improper subscript has been replaced by zero
+54A bracketed subscript must have a known numeric value;
+59unfortunately, what I found was the value that appears just
+55above this error message. So I'll try a zero subscript.
+09Variable
+21 has been obliterated
+54It seems you did a nasty thing---probably by accident,
+46but nevertheless you nearly hornswoggled me...
+50While I was evaluating the right-hand side of this
+51command, something happened, and the left-hand side
+52is no longer a variable! So I won't change anything.
+51While I was evaluating the suffix of this variable,
+55something was redefined, and it's no longer a variable!
+59In order to get back on my feet, I've inserted `0' instead.
+04copy
+47I've scanned an expression of the form `a[b,c',
+41so a right bracket should have come next.
+51I've seen a `[' and a subscript value, in a suffix,
+11A secondary
+10A tertiary
+24Pen path must be a cycle
+39I can't make a pen from the given path.
+02An
+49Undefined coordinates have been replaced by (0,0)
+49I need x and y numbers for this part of the path.
+42The value I found (see above) was no good;
+48so I'll try to keep going by using zero instead.
+45(Chapter 27 of The METAFONTbook explains that
+36you might want to type `I ???' now.)
+45Undefined x coordinate has been replaced by 0
+51I need a `known' x value for this part of the path.
+45Undefined y coordinate has been replaced by 0
+51I need a `known' y value for this part of the path.
+49I've scanned a direction spec for part of a path,
+39so a right brace should have come next.
+36Improper curl has been replaced by 1
+43A curl must be a known, nonnegative number.
+46I've got the x coordinate of a path direction;
+36will look for the y coordinate next.
+45A path join command should end with two dots.
+34Improper tension has been set to 1
+53The expression above should have been a number >=3/4.
+46Paths don't touch; `&' will be changed to `..'
+48When you join paths `p&q', the ending point of p
+49must be exactly equal to the starting point of q.
+53So I'm going to pretend that you said `p..q' instead.
+46Undefined condition will be treated as `false'
+53The expression shown above should have had a definite
+48true-or-false value. I'm changing it to `false'.
+40*** (cannot readstring in nonstop modes)
+12unknown pair
+15unknown numeric
+17Not implemented:
+59I'm afraid I don't know how to apply that operation to that
+53particular type. Continue, and I'll simply return the
+54argument (shown above) as the result of the operation.
+02)}
+30String contains illegal digits
+55I zeroed out characters that weren't in the range 0..7.
+48I zeroed out characters that weren't hex digits.
+18Number too large (
+57I have trouble with numbers greater than 4095; watch out.
+58combination of types. Continue, and I'll return the second
+52argument (see above) as the result of the operation.
+02{(
+49The quantities shown above have not been equated.
+60Oh dear. I can't decide if the expression above is positive,
+59negative, or zero. So this comparison test won't be `true'.
+41Unknown relation will be considered false
+03)*(
+58You're trying to divide the quantity shown above the error
+55message by zero. I'm going to divide it by one instead.
+32Improper transformation argument
+46The expression shown above has the wrong type,
+39so I can't transform anything using it.
+37Transform components aren't all known
+56I'm unable to apply a partially specified transformation
+42except to a fully known pair or transform.
+31That transformation is too hard
+49I can apply complicated transformations to paths,
+49but I can only do integer operations on pictures.
+16Too far to shift
+49I can't shift the picture as requested---it would
+30A statement can't begin with `
+51I was looking for the beginning of a new statement.
+58If you just proceed without changing anything, I'll ignore
+56everything up to the next `;'. Please insert a semicolon
+58now in front of anything that you don't want me to delete.
+52(See Chapter 27 of The METAFONTbook for an example.)
+28Extra tokens will be flushed
+59I've just read as much of that statement as I could fathom,
+59so a semicolon should have been next. It's very puzzling...
+53but I'll try to get myself back together, by ignoring
+19Isolated expression
+40I couldn't find an `=' or `:=' after the
+50expression that is shown above this error message,
+44so I guess I'll just ignore it and carry on.
+36Improper `:=' will be changed to `='
+54I didn't find a variable name at the left of the `:=',
+50so I'm going to pretend that you said `=' instead.
+03)=(
+19Internal quantity `
+28' must receive a known value
+56I can't set an internal quantity to anything but a known
+54numeric value, so I'll have to ignore this assignment.
+30Equation cannot be performed (
+58I'm sorry, but I don't know how to make such things equal.
+55(See the two expressions just above the error message.)
+34Redundant or inconsistent equation
+56An equation between already-known quantities can't help.
+50But don't worry; continue and I'll just ignore it.
+21Inconsistent equation
+58The equation I just read contradicts what was said before.
+09 (off by
+48Declared variable conflicts with previous vardef
+56You can't use, e.g., `numeric foo[]' after `vardef foo'.
+51Proceed, and I'll ignore the illegal redeclaration.
+51Illegal suffix of declared variable will be flushed
+50Variables in declarations must consist entirely of
+46names and collective subscripts, e.g., `x[]a'.
+57Are you trying to use a reserved word in a variable name?
+43I'm going to discard the junk I found here,
+51up to the next comma or the end of the declaration.
+49Explicit subscripts like `x15a' aren't permitted.
+16Extra `endgroup'
+44I'm not currently working on a `begingroup',
+03end
+04dump
+46Always say `randomseed:=<numeric expression>'.
+29Unknown value will be ignored
+48Your expression was too random for me to handle,
+43so I won't change the random seed just now.
+13{randomseed:=
+13errorstopmode
+05inner
+05outer
+55I found no right delimiter to match a left one. So I've
+56put one in, behind the scenes; this may fix the problem.
+11The token `
+32' is no longer a right delimiter
+48Strange: This token has lost its former meaning!
+44I'll read it as a right delimiter this time;
+43but watch out, I'll probably miss it later.
+10(%CAPSULE)
+28' isn't an internal quantity
+55Something like `tracingonline' should follow `interim'.
+46You should have said `let symbol = something'.
+55was present. The next token I read will be `something'.
+19number of internals
+09showtoken
+09showstats
+04show
+12showvariable
+16showdependencies
+02>
+08(outer)
+03lef
+04righ
+25t delimiter that matches
+03tag
+08variable
+06macro:
+09'd macro:
+17[repeat the loop]
+13Memory usage
+17 still untouched)
+13String usage
+07=macro:
+02OK
+56This isn't an error message; I'm just showing something.
+07contour
+10doublepath
+04also
+07withpen
+10withweight
+08dropping
+07keeping
+13Improper type
+54Next time say `withweight <known numeric expression>';
+55I'll ignore the bad `with' clause and look for another.
+47Next time say `withpen <known pen expression>';
+40Weight must be -3, -2, -1, +1, +2, or +3
+20 is the wrong type (
+45I was looking for a "known" picture variable.
+37So I'll not change anything just now.
+23Not a suitable variable
+61At this point I needed to see the name of a picture variable.
+58(Or perhaps you have indeed presented me with one; I might
+59have missed it, if it wasn't followed by the proper token.)
+16Improper `addto'
+54This expression should have specified a known picture.
+46This expression should have been a known path.
+11Not a cycle
+58That contour should have ended with `..cycle' or `&cycle'.
+37Strange path (turning number is zero)
+43Backwards path (turning number is negative)
+53The path doesn't have a counterclockwise orientation,
+41so I'll probably have trouble drawing it.
+51(See Chapter 27 of The METAFONTbook for more help.)
+17Bad window number
+42It should be the number of an open window.
+21Improper `openwindow'
+52Say `openwindow k from (r0,c0) to (r1,c1) at (x,y)',
+57where all quantities are known and k is between 0 and 15.
+19Bad culling amounts
+44Always cull by known amounts that exclude 0.
+07message
+10errmessage
+07errhelp
+46A message should be a known string expression.
+32(That was another `errmessage'.)
+51This error message was generated by an `errmessage'
+43command, so I can't give any explicit help.
+51Pretend that you're Miss Marple: Examine all clues,
+41and deduce the truth by inspired guesses.
+09Enormous
+17 has been reduced
+48Font metric dimensions must be less than 2048pt.
+08charlist
+08ligtable
+10extensible
+10headerbyte
+09fontdimen
+35Invalid code has been replaced by 0
+54I was looking for a number between 0 and 255, or for a
+55string of length 1. Didn't find it; will use 0 instead.
+10Character
+02||
+05code
+12 is already
+13in a ligtable
+13in a charlist
+51It's not legal to label a character more than once.
+17Improper location
+43I was looking for a known, positive number.
+50For safety's sake I'll ignore the present command.
+56A colon should follow a headerbyte or fontinfo location.
+21Illegal ligtable step
+38I was looking for `=:' or `kern' here.
+13ligtable size
+02=:
+03=:|
+04=:|>
+03|=:
+04|=:>
+04|=:|
+05|=:|>
+06|=:|>>
+04kern
+15Too far to skip
+57At most 127 lig/kern steps can separate skipto1 from 1::.
+13Improper kern
+51The amount of kern should be a known numeric value.
+39I'm processing `extensible c: t,m,b,r'.
+23Improper font parameter
+06(some
+41 values had to be adjusted by as much as
+03pt)
+47(illegal design size has been changed to 128pt)
+04.tfm
+26file name for font metrics
+24Font metrics written on
+13(local label
+15:: was missing)
+24(a font metric dimension
+23 font metric dimensions
+21 had to be decreased)
+17 METAFONT output
+03.gf
+02gf
+19 (just shipped out)
+51(There's unbounded black in character shipped out!)
+07special
+10numspecial
+21Unsuitable expression
+59The expression shown above has the wrong type to be output.
+06title
+18Output written on
+10 character
+02,
+08 bytes).
+08 (INIMF)
+25 strings of total length
+43 memory locations dumped; current usage is
+16 symbolic tokens
+17 (preloaded base=
+14base file name
+26Beginning to dump on file
+22Transcript written on
+02 )
+19(end occurred when
+09 on line
+16 was incomplete)
+52(see the transcript file for additional information)
+33(dump is performed only by INIMF)
+21debug # (-1 to exit):
+*415546872
diff --git a/systems/knuth/local/mf-sparc/mf_arith.c b/systems/knuth/local/mf-sparc/mf_arith.c
new file mode 100644
index 0000000000..5347107e81
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/mf_arith.c
@@ -0,0 +1,142 @@
+/*
+ * mf_arith.c -- optimized arithmetic routines for UNIX METAFONT 84
+ *
+ * These functions account for a significant percentage of the processing
+ * time used by METAFONT, and have been recoded in assembly language for
+ * some classes of machines.
+ *
+ * As yet I don't have code for SPARC, so I'm just using a C version
+ * by richards, in hopes that cc does better than pc on the brute-force
+ * algorithm found in mf.web.
+ *
+ * function makefraction(p, q: integer): fraction;
+ * { Calculate the function floor( (p * 2^28) / q + 0.5 ) }
+ * { if both p and q are positive. If not, then the value }
+ * { of makefraction is calculated as though both *were* }
+ * { positive, then the result sign adjusted. }
+ * { (e.g. makefraction ALWAYS rounds away from zero) }
+ * { In case of an overflow, return the largest possible }
+ * { value (2^32-1) with the correct sign, and set global }
+ * { variable "aritherror" to 1. Note that -2^32 is }
+ * { considered to be an illegal product for this type of }
+ * { arithmetic! }
+ *
+ * function takefraction(f: fraction; q: integer): integer;
+ * { Calculate the function floor( (p * q) / 2^28 + 0.5 ) }
+ * { takefraction() rounds in a similar manner as }
+ * { makefraction() above. }
+ *
+ */
+
+#include "h00vars.h"
+
+extern bool aritherror; /* to be set on overflow */
+
+#define EL_GORDO 0x7fffffff /* 2^31-1 */
+#define FRACTION_ONE 0x10000000 /* 2^28 */
+#define FRACTION_HALF 0x08000000 /* 2^27 */
+#define FRACTION_FOUR 0x40000000 /* 2^30 */
+
+int makefraction(p, q)
+ register int p, q;
+{
+ int negative;
+ register int be_careful;
+ register int f;
+ int n;
+
+ if (p < 0) {
+ negative = 1;
+ p = -p;
+ } else negative = 0;
+
+ if (q < 0) {
+ negative = !negative;
+ q = -q;
+ }
+
+ n = p / q;
+ p = p % q;
+
+ if (n >= 8) {
+ aritherror = TRUE;
+ return (negative? -EL_GORDO : EL_GORDO);
+ }
+
+ n = (n-1)*FRACTION_ONE;
+ f = 1;
+ do {
+ be_careful = p - q;
+ p = be_careful + p;
+ if (p >= 0) {
+ f = f + f + 1;
+ } else {
+ f <<= 1;
+ p += q;
+ }
+ } while (f < FRACTION_ONE);
+
+ be_careful = p - q;
+ if ((be_careful + p) >= 0)
+ f += 1;
+ return (negative? -(f+n) : (f+n));
+
+}
+
+int takefraction(q, f)
+ register int q;
+ register int f;
+{
+ int n, negative;
+ register int p, be_careful;
+
+ if (f < 0) {
+ negative = 1;
+ f = -f;
+ } else negative = 0;
+ if (q < 0) {
+ negative = !negative;
+ q = -q;
+ }
+ if (f < FRACTION_ONE)
+ n = 0;
+ else {
+ n = f / FRACTION_ONE;
+ f = f % FRACTION_ONE;
+ if (q < (EL_GORDO / n))
+ n = n * q;
+ else {
+ aritherror = TRUE;
+ n = EL_GORDO;
+ }
+ }
+
+ f += FRACTION_ONE;
+ p = FRACTION_HALF;
+ if (q < FRACTION_FOUR)
+ do {
+ if (f & 0x1)
+ p = (p+q) >> 1;
+ else
+ p >>= 1;
+ f >>= 1;
+ } while (f > 1);
+ else
+ do {
+ if (f & 0x1)
+ p = p + ((q-p) >> 1);
+ else
+ p >>= 1;
+ f >>= 1;
+ } while (f > 1);
+
+ be_careful = n - EL_GORDO;
+ if ((be_careful + p) > 0) {
+ aritherror = TRUE;
+ n = EL_GORDO - p;
+ }
+
+ return(negative? -(n+p) : (n+p));
+}
+
+
diff --git a/systems/knuth/local/mf-sparc/mf_sunwin.c b/systems/knuth/local/mf-sparc/mf_sunwin.c
new file mode 100644
index 0000000000..f1e9fa9b39
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/mf_sunwin.c
@@ -0,0 +1,90 @@
+/*
+ * XView interface to MetaFont
+ * (written hastily and without much understanding...)
+ */
+#define SCREENWIDTH 1024 /* must match the constant in inimf.ch */
+#define SCREENDEPTH 800 /* must match the constant in inimf.ch */
+
+#include <stdio.h>
+#include <xview/xview.h>
+#include <xview/canvas.h>
+#include <X11/Xlib.h>
+
+static Frame frame; /* container */
+static Canvas canvas; /* the viewing and imaging area */
+static Xv_Window canvas_paintwin; /* image component of |canvas| */
+static Display *display; /* handle to X server */
+static Visual *screen; /* where the X server paints */
+static Window xcanvas; /* the X11 ``drawable'' component of our canvas */
+static GC gc; /* X graphic context */
+static XGCValues GCvalues; /* changes to a graphic context */
+
+/*
+ * updatescreen; -- make sure screen display is current
+ */
+
+updatescreen()
+{
+ notify_dispatch();
+ XFlush(display);
+}
+
+/*
+ * init_screen: boolean; return true if window operations legal
+ */
+
+initscreen()
+{
+ if (xv_init(NULL)==NULL) return 0;
+ frame=(Frame)xv_create(NULL,FRAME,XV_WIDTH,700,XV_HEIGHT,600,
+ FRAME_SHOW_HEADER,FALSE,
+ XV_SHOW,TRUE,NULL);
+ canvas=(Canvas)xv_create(frame,CANVAS,CANVAS_REPAINT_PROC,updatescreen,
+ CANVAS_X_PAINT_WINDOW,TRUE,
+ XV_HEIGHT,SCREENDEPTH,XV_WIDTH,SCREENWIDTH,
+ CANVAS_AUTO_EXPAND,FALSE,CANVAS_AUTO_SHRINK,FALSE,
+ NULL);
+ canvas_paintwin=(Xv_window)xv_get(canvas,CANVAS_NTH_PAINT_WINDOW,0);
+ xcanvas=(Window)xv_get(canvas_paintwin,XV_XID);
+ display=(Display*)xv_get(frame,XV_DISPLAY);
+ screen=(Visual*)xv_get(frame,XV_VISUAL);
+ gc=XCreateGC(display,DefaultRootWindow(display),0,GCvalues);
+ notify_do_dispatch(); /* this should allow terminal input */
+ return 1;
+}
+
+/*
+ * blankrectangle: reset rectangle bounded by ([left,right],[top,bottom])
+ * to background color
+ */
+
+blankrectangle(left, right, top, bottom)
+ short left, right;
+ short top, bottom;
+{
+ XSetFunction(display,gc,GXclear);
+ XFillRectangle(display,xcanvas,gc,left,top,right-left,bottom-top);
+}
+
+/*
+ * paintrow -- paint row r starting with color b, up to next
+ * transition specified by vector a, switch colors,
+ * and continue for n transitions.
+ */
+
+paintrow(r, b, a, n)
+ short r;
+ short b;
+ short a[];
+ short n;
+{
+ register short *p,*q=&a[n];
+ XSetFunction(display,gc,b?GXset:GXclear);
+ for (p=a;p<q;p+=2)
+ XFillRectangle(display,xcanvas,gc,*p,r,*(p+1)-*p,1);
+ XSetFunction(display,gc,b?GXclear:GXset);
+ for (p=a+1;p<q;p+=2)
+ XFillRectangle(display,xcanvas,gc,*p,r,*(p+1)-*p,1);
+ notify_dispatch(); /* without this, the Notifier queue overflows */
+}
+
diff --git a/systems/knuth/local/mf-sparc/mf_window.h b/systems/knuth/local/mf-sparc/mf_window.h
new file mode 100644
index 0000000000..761cf006c9
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/mf_window.h
@@ -0,0 +1,15 @@
+{ $Header: mf_window.h,v 0.999999 86/01/13 00:43:56 richards Released $ }
+
+{ External procedures for UNIX MetaFont VIRMF for display graphics }
+
+function initscreen: boolean;
+ external;
+
+procedure updatescreen;
+ external;
+
+procedure blankrectangle(leftcol, rightcol: screencol; toprow, botrow: screenrow);
+ external;
+
+procedure paintrow(r: screenrow; b: pixelcolor; var a: transspec; n: screencol);
+ external;
diff --git a/systems/knuth/local/mf-sparc/plain.base b/systems/knuth/local/mf-sparc/plain.base
new file mode 100644
index 0000000000..8aff956dc2
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/plain.base
Binary files differ
diff --git a/systems/knuth/local/mf-sparc/plain.log b/systems/knuth/local/mf-sparc/plain.log
new file mode 100644
index 0000000000..41d972df4f
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/plain.log
@@ -0,0 +1,16 @@
+This is METAFONT, Version 2.7182 for SunOS (INIMF) 8 AUG 1998 21:30
+**plain \input local \dump
+(/home/tex/dist/lib/plain.mf
+Preloading the plain base, version 2.71: preliminaries,
+ basic constants and mathematical macros,
+ macros for converting from device-independent units to pixels,
+ macros and tables for various modes of operation,
+ macros for drawing and filling,
+ macros for proof labels and rules,
+ macros for character and font administration,
+and a few last-minute items.) (/home/tex/local/lib/local.mf)
+Beginning to dump on file plain.base
+ (preloaded base=plain 98.8.8)
+1407 strings of total length 23104
+5273 memory locations dumped; current usage is 1450&3664
+525 symbolic tokens
diff --git a/systems/knuth/local/mf-sparc/trap1.in b/systems/knuth/local/mf-sparc/trap1.in
new file mode 100644
index 0000000000..1e7b96cab3
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/trap1.in
@@ -0,0 +1,3 @@
+
+\input trap
+
diff --git a/systems/knuth/local/mf-sparc/trap2.in b/systems/knuth/local/mf-sparc/trap2.in
new file mode 100644
index 0000000000..d3dba8ac65
--- /dev/null
+++ b/systems/knuth/local/mf-sparc/trap2.in
@@ -0,0 +1 @@
+ &trap trap
diff --git a/systems/knuth/local/mf/Makefile b/systems/knuth/local/mf/Makefile
new file mode 100644
index 0000000000..445d028a47
--- /dev/null
+++ b/systems/knuth/local/mf/Makefile
@@ -0,0 +1,74 @@
+# Make executable files for this workstation, using the master TeX sources
+
+PFLAGS= -I..
+CFLAGS= -g -I.. -I/usr/openwin/include
+#CFLAGS= -O -I.. -I/usr/openwin/include
+#LIBS= -lsuntool -lsunwindow -lpixrect
+LIBS= -L/usr/openwin/lib -lxview -lolgx -lX11
+
+PC= time pc
+
+all: inimf
+
+traptest: trapmf
+ -trapmf < trap1.in
+ mv trap.log trapin.log
+ -trapmf < trap2.in | tee trap.fot
+ gftype trap.72270gf trap.typ < gftype.in
+ tftopl trap.tfm trap.pl
+ rm -f *.dif
+ -diff trapin.log ../../dist/mf/trapin.log > trapin.log.dif
+ -diff trap.log ../../dist/mf/trap.log > trap.log.dif
+ -diff trap.fot ../../dist/mf/trap.fot > trap.fot.dif
+ -diff trap.typ ../../dist/mf/trap.typ > trap.typ.dif
+ -diff trap.pl ../../dist/mf/trap.pl > trap.pl.dif
+ more trapin.log.dif trap.log.dif trap.typ.dif trap.fot.dif trap.pl.dif
+
+%.p: %.ch
+ tangle ../../dist/mf/mf.web $*.ch
+ mv ../../dist/mf/mf.pool .
+ mv ../../dist/mf/mf.p $*.p
+
+inimf.ch: ../../dist/mf/mf.web
+
+trapmf.ch: inimf.ch ini_to_trap
+ ini_to_trap inimf.ch trapmf.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+trapmf.o: trapmf.p
+ ${PC} -g ${PFLAGS} -c trapmf.p
+
+inimf: inimf.o ext.o mf_arith.o mf_sunwin.o
+ pc ${PFLAGS} -o inimf inimf.o ext.o mf_arith.o mf_sunwin.o ${LIBS}
+
+trapmf: trapmf.o ext.o mf_arith.o
+ pc -g ${PFLAGS} -o trapmf trapmf.o ext.o mf_arith.o
+ cp ../../dist/mf/trap.mf .
+
+ext.o: ext.c ext.h ../h00vars.h ../mfpaths.h
+ cc ${CFLAGS} -c ext.c
+
+mf_arith.o: mf_arith.c
+mf_sunwin.o: mf_sunwin.c
+
+plain.base: ../../dist/lib/plain.mf ../lib/local.mf
+ -inimf plain \\input local \\dump
+
+install: # doit make install
+ mv inimf /usr/local/bin
+ rm /usr/local/bin/mf
+ ln /usr/local/bin/inimf /usr/local/bin/mf
+ make plain.base
+
+installtrap: # when the trap test is satisfactory
+ mv trap.mf trapin.log trap.log trap.fot trap.typ trap.pl
+ trap.72270gf trap.tfm ../../dist/mf
+
+clean:
+ rm -f *.p *.o *~ *.mf trapmf* trap.* trapin*
+ @echo "Remaining files should be:"
+ @echo "Makefile gftype.in mf.pool mf_window.h trap1.in"
+ @echo "ext.c ini_to_trap* mf_arith.c plain.base trap2.in"
+ @echo "ext.h inimf.ch mf_sunwin.c plain.log"
diff --git a/systems/knuth/local/mf/ext.c b/systems/knuth/local/mf/ext.c
new file mode 100644
index 0000000000..dfbf0b2bbb
--- /dev/null
+++ b/systems/knuth/local/mf/ext.c
@@ -0,0 +1,457 @@
+/* external procedures called by MF */
+#include <sys/signal.h>
+#include <sys/time.h>
+#include "mfpaths.h" /* defines MAXINPATHCHARS, defaultinputpath, etc. */
+#include "h00vars.h"
+
+/* The following are for closing files */
+
+closea(f)
+ int *f; /* actually, file pointer, but it doesn't matter */
+{
+ close1(f);
+}
+
+closeb(f)
+ int *f; /* actually, file pointer, but it doesn't matter */
+{
+ close1(f);
+}
+
+closew(f)
+ int *f; /* actually, file pointer, but it doesn't matter */
+{
+ close1(f);
+}
+
+extern int interrupt; /* To be made nonzero to tell MF about interrupts */
+
+/*
+** catchint()
+**
+** Gets called when the user hits his interrupt key. Sets the global
+** "interrupt" nonzero, then sets it up so that the next interrupt will
+** also be caught here.
+**
+*/
+
+catchint()
+{
+ interrupt = 1;
+ signal(SIGINT,catchint);
+}
+
+/*
+** dateandtime(time, day, month, year)
+**
+** Stores minutes since midnight, current day, month and year into
+** *time, *day, *month and *year, respectively.
+**
+** Also, set things up so that catchint() will get control on interrupts.
+**
+*/
+
+dateandtime(minutes, day, month, year)
+int *minutes, *day, *month, *year;
+{
+ long clock;
+ struct tm *tmptr, *localtime();
+
+ clock = time(0);
+ tmptr = localtime(&clock);
+
+ *minutes = tmptr->tm_hour * 60 + tmptr->tm_min;
+ *day = tmptr->tm_mday;
+ *month = tmptr->tm_mon + 1;
+ *year = tmptr->tm_year + 1900;
+ signal(SIGINT,catchint);
+}
+
+
+/* fixed arrays are used to hold the paths, to avoid any possible problems
+ involving interaction of malloc and undump */
+
+char inputpath[MAXINPATHCHARS] = defaultinputpath;
+char basepath[MAXOTHPATHCHARS] = defaultbasepath;
+char poolpath[MAXOTHPATHCHARS] = defaultpoolpath;
+
+char *getenv();
+
+/*
+ * setpaths is called to set up the arrays inputpath, basepath, and
+ * poolpath as follows: if the user's environment has a value for the
+ * appropriate value, then use it; otherwise, leave the current value of
+ * the array (which may be the default path, or it may be the result of
+ * a call to setpaths on a previous run that was made the subject of
+ * an undump: this will give the maker of a preloaded MF the option of
+ * providing a new set of "default" paths.
+ *
+ * Note that we have to copy the string from the environment area, since
+ * that will change on the next run (which matters if this is for a
+ * preloaded MF).
+ */
+setpaths()
+{
+ register char *envpath;
+
+ if ((envpath = getenv("MFINPUTS")) != NULL)
+ excopypath(inputpath,envpath,MAXINPATHCHARS);
+ if ((envpath = getenv("MFBASES")) != NULL)
+ excopypath(basepath,envpath,MAXOTHPATHCHARS);
+ if ((envpath = getenv("MFPOOL")) != NULL)
+ excopypath(poolpath,envpath,MAXOTHPATHCHARS);
+}
+
+/*
+ * excopypath(s1,s2,n) copies at most n characters (including the null)
+ * from string s2 to string s1, giving an error message for paths
+ * that are too long.
+ */
+excopypath(s1,s2,n)
+ register char *s1,*s2;
+ register int n;
+{
+ while ((*s1++ = *s2++) != '\0')
+ if (--n == 0) {
+ fprintf(stderr, "! Environment search path is too big\n");
+ *--s1 = '\0';
+ return; /* let user continue with truncated path */
+ }
+}
+
+extern short buffer[],xord[];
+extern short last,first; /* pointers into buffer */
+
+/*
+ * lineread reads from the Pascal text file with iorec pointer filep
+ * into buffer[first], buffer[first+1],..., buffer[last-1] (and
+ * setting "last").
+ * Characters are read until a newline is found (which isn't put in the
+ * buffer) or until the next character would go into buffer[lastlim].
+ * The characters need to be translated, so really xord[c] is put into
+ * the buffer when c is read.
+ * If end-of-file is encountered, the funit field of *filep is set
+ * appropriately.
+ */
+lineread(filep, lastlim)
+struct iorec *filep;
+int lastlim;
+{
+ register c;
+ register short *cs; /* pointer into buffer where next char goes */
+ register FILE *iop; /* stdio-style FILE pointer */
+ int l; /* how many more chars are allowed before buffer overflow */
+ short *bf; /* hold address of buffer[first] */
+
+ iop = filep->fbuf;
+ bf = cs = &(buffer[first]);
+ l = lastlim-first;
+ /* overflow when next char would go into buffer[lastlim] */
+ while (--l>=0 && (c = getc(iop)) != EOF && c!='\n')
+ *cs++ = xord[c];
+ if (c == EOF)
+ filep->funit |= EOFF; /* we hit end-of-file */
+ last = first+cs-bf;
+}
+
+#define filenamesize 1024 /* should agree with inimf.ch */
+extern char nameoffile[],realnameoffile[]; /* these have size filenamesize */
+
+/*
+ * testaccess(amode,filepath)
+ *
+ * Test whether or not the file whose name is in the global nameoffile
+ * can be opened for reading (if mode=READACCESS)
+ * or writing (if mode=WRITEACCESS).
+ *
+ * The filepath argument is one of the ...FILEPATH constants defined below.
+ * If the filename given in nameoffile does not begin with '/', we try
+ * prepending all the ':'-separated areanames in the appropriate path to the
+ * filename until access can be made, if it ever can.
+ *
+ * The realnameoffile global array will contain the name that yielded an
+ * access success.
+ */
+
+#define READACCESS 4
+#define WRITEACCESS 2
+
+#define NOFILEPATH 0
+#define INPUTFILEPATH 1
+#define READFILEPATH 2
+#define BASEFILEPATH 4
+#define POOLFILEPATH 5
+
+bool
+testaccess(amode,filepath)
+ int amode,filepath;
+{
+ register bool ok;
+ register char *p;
+ char *curpathplace;
+ int f;
+
+ switch(filepath) {
+ case NOFILEPATH: curpathplace = NULL; break;
+ case INPUTFILEPATH: case READFILEPATH:
+ curpathplace = inputpath; break;
+ case BASEFILEPATH: curpathplace = basepath; break;
+ case POOLFILEPATH: curpathplace = poolpath; break;
+ }
+ if (nameoffile[0]=='/') /* file name has absolute path */
+ curpathplace = NULL;
+ do {
+ packrealnameoffile(&curpathplace);
+ if (amode==READACCESS)
+ /* use system call "access" to see if we could read it */
+ if (access(realnameoffile,READACCESS)==0) ok = TRUE;
+ else ok = FALSE;
+ else {
+ /* WRITEACCESS: use creat to see if we could create it, but close
+ the file again if we're OK, to let pc open it for real */
+ f = creat(realnameoffile,0666);
+ if (f>=0) ok = TRUE;
+ else ok = FALSE;
+ if (ok)
+ close(f);
+ }
+ } while (!ok && curpathplace != NULL);
+ if (ok) { /* pad realnameoffile with blanks, as Pascal wants */
+ for (p = realnameoffile; *p != '\0'; p++)
+ /* nothing: find end of string */ ;
+ while (p < &(realnameoffile[filenamesize]))
+ *p++ = ' ';
+ }
+ return (ok);
+}
+
+/*
+ * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp,
+ * followed by '/', followed by the characters in nameoffile up until the
+ * first blank there, and finally a '\0'. The cpp pointer is left pointing
+ * at the next directory in the path.
+ * But: if *cpp == NULL, then we are supposed to use nameoffile as is.
+ */
+packrealnameoffile(cpp)
+ char **cpp;
+{
+ register char *p,*realname;
+
+ realname = realnameoffile;
+ if ((p = *cpp)!=NULL) {
+ while ((*p != ':') && (*p != '\0')) {
+ *realname++ = *p++;
+ if (realname == &(realnameoffile[filenamesize-1]))
+ break;
+ }
+ if (*p == '\0') *cpp = NULL; /* at end of path now */
+ else *cpp = p+1; /* else get past ':' */
+ *realname++ = '/'; /* separate the area from the name to follow */
+ }
+ /* now append nameoffile to realname... */
+ p = nameoffile;
+ while (*p != ' ') {
+ if (realname >= &(realnameoffile[filenamesize-1])) {
+ fprintf(stderr,"! Full file name is too long\n");
+ break;
+ }
+ *realname++ = *p++;
+ }
+ *realname = '\0';
+}
+
+/*
+** testeof(filep)
+**
+** Test whether or not the Pascal text file with iorec pointer filep
+** has reached end-of-file (when the only I/O on it is done with
+** lineread, above).
+** We may have to read the next character and unget it to see if perhaps
+** the end-of-file is next.
+*/
+
+bool
+testeof(filep)
+register struct iorec *filep;
+{
+ register char c;
+ register FILE *iop; /* stdio-style FILE pointer */
+ if (filep->funit & EOFF)
+ return(TRUE);
+ else { /* check to see if next is EOF */
+ iop = filep->fbuf;
+ c = getc(iop);
+ if (c == EOF)
+ return(TRUE);
+ else {
+ ungetc(c,iop);
+ return(FALSE);
+ }
+ }
+}
+
+
+/*
+** writegf(a,b)
+**
+** writegf is called to write gfbuf[a..b] to gffile
+** Unfortunately, gfbuf is declared as eight_bits, which in our
+** implementation means that two bytes are taken by each entry.
+** We only want to output the low order (first) byte of each pair.
+*/
+extern struct iorec gffile;
+extern short gfbuf[];
+writegf (a,b)
+ int a,b;
+{
+ register short *ptr,*final;
+ register FILE *iop;
+
+ iop=gffile.fbuf;
+ ptr= &(gfbuf[a]);
+ final= &(gfbuf[b]);
+ while (ptr<=final) {
+ putc((char) (*ptr & 0377), iop);
+ ptr += 1;
+ }
+ /* Note: The above code used to be machine dependent. By changing
+ * the declarations from "char *" to "short *" we get the machine
+ * to do its own byte ordering of the de-reference of "ptr" above.
+ * Then we AND off the low bits (now that it's been pulled from
+ * memory correctly) and cast it into a "char" for putc().
+ * --Peter Kessler's idea; explanation, AND, cast a la clp.
+ */
+}
+
+
+/*
+** close1(filep)
+** close1 does the proper things to pc's runtime system file data structure
+** to close a file
+*/
+close1(filep)
+
+ register struct iorec *filep;
+{
+ register struct iorec *next;
+
+ if (filep->fbuf != 0) {
+ if ((filep->funit & FDEF) == 0) {
+ next = (struct iorec *) &_fchain;
+ while (next->fchain != FILNIL
+ && next->fchain != filep)
+ next = next->fchain;
+ if (next->fchain != FILNIL)
+ next->fchain = next->fchain->fchain;
+
+ if (filep->fblk > PREDEF) {
+ fflush(filep->fbuf);
+ setbuf(filep->fbuf, NULL);
+ }
+ fclose(filep->fbuf);
+ if (ferror(filep->fbuf)) {
+/* ERROR("%s: Close failed\n",
+ filep->pfname); */
+ return;
+ }
+ filep->fbuf=0;
+ }
+ if ((filep->funit & TEMP) != 0 &&
+ unlink(filep->pfname)) {
+/* PERROR("Could not remove ", filep->pfname); */
+ return;
+ }
+ }
+}
+
+/*
+** The following procedure is due to sjc@s1-c.
+**
+** calledit(filename, fnlength, linenumber)
+**
+** MF84 can call this to implement the 'e' feature in error-recovery
+** mode, invoking a text editor on the erroneous source file.
+**
+** You should pass to "filename" the first character of the packed array
+** containing the filename, and to "fnlength" the size of the filename.
+**
+** Ordinarily, this invokes emacs. If you want a different
+** editor, create a shell environment variable MFEDIT containing
+** the string that invokes that editor, with "%s" indicating where
+** the filename goes and "%d" indicating where the decimal
+** linenumber (if any) goes. For example, a MFEDIT string for "vi" might be:
+**
+** setenv MFEDIT "/usr/bin/vi +%d %s"
+**
+*/
+
+ char dvalue[] = "emacs +%d %s";
+ char *mfeditvalue = &dvalue[0];
+
+ calledit(filename, fnlength, linenumber)
+ char *filename;
+ int fnlength, linenumber;
+ {
+ char *temp, *command, c;
+ int sdone, ddone, i;
+
+ sdone = ddone = 0;
+
+ /* Replace default with environment variable if possible. */
+ if (NULL != (temp = (char *) getenv("MFEDIT")))
+ mfeditvalue = temp;
+
+ /* Make command string containing envvalue, filename, and linenumber */
+ if (NULL ==
+ (command = (char *) malloc(strlen(mfeditvalue) + fnlength + 25))) {
+ fprintf(stderr, "! Not enough memory to issue editor command\n");
+ exit(1);
+ }
+ temp = command;
+ while ((c = *mfeditvalue++) != '\0') {
+ if (c == '%') {
+ switch (c = *mfeditvalue++) {
+ case 'd':
+ if (ddone) {
+ fprintf(stderr,
+ "! Line number cannot appear twice in editor command\n");
+ exit(1);
+ }
+ sprintf(temp, "%d", linenumber);
+ while (*temp != '\0')
+ temp++;
+ ddone = 1;
+ break;
+ case 's':
+ if (sdone) {
+ fprintf(stderr,
+ "! Filename cannot appear twice in editor command\n");
+ exit(1);
+ }
+ i = 0;
+ while (i < fnlength)
+ *temp++ = filename[i++] ^ 0x80; /* changed for 8-bit MF */
+ sdone = 1;
+ break;
+ case '\0':
+ *temp++ = '%';
+ mfeditvalue--; /* Back up to \0 to force termination. */
+ break;
+ default:
+ *temp++ = '%';
+ *temp++ = c;
+ break;
+ }
+ }
+ else
+ *temp++ = c;
+ }
+ *temp = '\0';
+
+ /* Execute command. */
+ if (0 != system(command))
+ fprintf(stderr, "! Trouble executing command %s\n", command);
+
+ /* Quit, indicating MF had found an error before you typed "e". */
+ exit(1);
+ }
diff --git a/systems/knuth/local/mf/ext.h b/systems/knuth/local/mf/ext.h
new file mode 100644
index 0000000000..7ff0e3bfdc
--- /dev/null
+++ b/systems/knuth/local/mf/ext.h
@@ -0,0 +1,38 @@
+procedure exit(x : integer);
+ external;
+
+procedure closea(var f:text);
+ external;
+
+procedure closeb(var f:bytefile);
+ external;
+
+procedure closew(var f:wordfile);
+ external;
+
+procedure dateandtime(var minutes, day, month, year : integer);
+ external;
+
+procedure setpaths;
+ external;
+
+function testaccess(accessmode:integer; filepath:integer): boolean;
+ external;
+
+function testeof(var f: text): boolean;
+ external;
+
+procedure lineread(var f: text; lastlim: integer);
+ external;
+
+procedure writegf(a,b:integer);
+ external;
+
+procedure calledit(var filename: packedASCIIcode; fnlength, linenumber: integer);
+ external;
+
+function makefraction(p,q:integer):fraction;
+ external;
+
+function takefraction(q:integer;f:fraction):integer;
+ external;
diff --git a/systems/knuth/local/mf/gftype.in b/systems/knuth/local/mf/gftype.in
new file mode 100644
index 0000000000..9b48ee4cf9
--- /dev/null
+++ b/systems/knuth/local/mf/gftype.in
@@ -0,0 +1,2 @@
+y
+y
diff --git a/systems/knuth/local/mf/ini_to_trap b/systems/knuth/local/mf/ini_to_trap
new file mode 100644
index 0000000000..c18984179f
--- /dev/null
+++ b/systems/knuth/local/mf/ini_to_trap
@@ -0,0 +1,38 @@
+#! /bin/csh
+# A shell script to perform modifications on the changefile for inimf
+# to make a changefile for trapmf (used to test mf).
+#
+# To use it, say
+# ini_to_trap inimf.ch trapmf.ch
+#
+ed - $1 <<edscriptend
+/debug=/
+//
+s/@{//p
+/gubed=/
+s/@t@>@}//p
+/@!mem_max=/
+//
+s/[0-9][0-9]*/3000/p
+/@!error_line=/
+s/[0-9][0-9]*/64/p
+/@!half_error_line=/
+s/[0-9][0-9]*/32/p
+/@!max_print_line=/
+s/[0-9][0-9]*/72/p
+/@!screen_width=/
+s/[0-9][0-9]*/100/p
+/@!screen_depth=/
+s/[0-9][0-9]*/200/p
+/@!gf_buf_size=/
+s/[0-9][0-9]*/8/p
+/@d mem_min=/
+//
+s/-[0-9]*/0/p
+/@d mem_top==/
+s/[0-9][0-9]*/3000/p
+/% BEGIN HOOK FOR THE TRAP TEST/
+.+1,.+5s/%//
+.+1,/% END HOOK FOR THE TRAP TEST/d
+w $2
+edscriptend
diff --git a/systems/knuth/local/mf/inimf.ch b/systems/knuth/local/mf/inimf.ch
new file mode 100644
index 0000000000..271532c7b6
--- /dev/null
+++ b/systems/knuth/local/mf/inimf.ch
@@ -0,0 +1,1505 @@
+% Derived from INITEX.CH for Berkeley Unix TeX 1.1, by Howard Trickey
+% and Pavel Curtis
+
+% Modification History:
+%
+% brought up to version 2.7182 on 8/8/98 --- don
+% brought up to version 2.718 on 3/8/95 --- don
+% UNIXexit convention switched 2/21/93 --- don
+% tfm_out needed to be corrected for new Pascal compiler 7/18/92 --- don
+% brought up to version 2.71 on 9/20/91 --- don
+% brought up to version 2.7 on 9/6/90 --- don
+% fixed max_buf_stack logic (it was one too high) on 1/22/90 --- don
+% brought up to version 1.9 on 12/20/89 --- don
+% brought up to version 1.8 on 10/14/89 --- don
+% 8-bit modifications made 9/11/89 --- don
+%
+% don adapted everything for SunOS, MF Version 1.7, on 8/12/89
+% Module numbers changed to match Volume D
+% Changes made more consistent with initex.ch
+% Window code merged in, with hooks to remove it for the TRAP test
+%
+% Revision 1.3 87/03/07 21:15:21 mackay
+%
+% Minor changes found on archive version on SCORE
+% (max_in_open increased to 10. -- 88/02/07 PAM)
+%
+% Revision 1.2 86/09/29 21:46:43 mackay
+%
+% Made no-debug the default, and changed version number
+% to correspond with improved mf.web file
+% (Got rid of debug code to avoid bug in range check
+% code of VAX4.3 BSD and SUN3 version 3.x Os pc interpreter)
+%
+% Revision 1.0 86/01/31 15:46:08 richards
+% Released for MF 1.0;
+%
+% Incorporates: New binary I/O library, separate optimized
+% arithmetic for takefraction/makefraction, new graphics interface
+%
+% Revision 0.999999.1.2 86/01/28 19:49:38 richards
+% Added:
+% variable "dummy" to routine initterm() because SUN PC compiler
+% loops with only i,j,k defined. Argh!
+%
+% Revision 0.999999.1.1 86/01/21 23:05:16 richards
+% First cut for MF 1.0
+%
+% Revision 0.999999 86/01/13 00:40:25 richards
+% Released for 0.999999:
+% added OTHERCASES sentinel
+% revised WINDOW interface and include files
+%
+% Revision 0.99999 85/12/09 20:23:35 richards
+% Version for 0.99999
+%
+% Revision 0.9999 85/10/21 20:48:24 richards
+% Released for MF version 0.9999
+%
+% Revision 0.999 85/10/18 21:33:44 richards
+% Version that works with 0.999 MF (Never released)
+%
+% Revision 0.91 85/05/16 10:58:13 richards
+% Released for MF version 0.91
+%
+% Revision 0.81 85/05/05 12:24:08 richards
+% Released for MF version 0.81
+%
+% Revision 0.77 85/03/11 20:23:37 richards
+% Released for MF version 0.77
+%
+% Revision 0.6.1.1 85/02/12 20:12:32 richards
+% First edit for 0.76
+%
+% Revision 0.6 85/02/08 00:52:40 richards
+% Distribution version for MF 0.6
+%
+% Revision 0.0.2.1 84/12/06 03:43:33 richards
+% Edited to work with 0.4 -- still needs to have section numbers re-done
+% before release.
+%
+% Revision 0.0 84/11/05 00:50:38 richards
+% Base version (first cut) for MetaFont 0.0
+%
+%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: only print changes
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+@y
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+\let\maybe=\iffalse
+\def\title{{\logo opqrstuq} changes for SunOS}
+\def\glob{13}\def\gglob{20, 25} % these are defined in module 1
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.2] banner line
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is METAFONT, Version 2.7182' {printed when \MF\ starts}
+@y
+@d banner=='This is METAFONT, Version 2.7182 for SunOS'
+ {printed when \MF\ starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.4] program header, include ext.h for C routine linkage
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Actually the heading shown here is not quite normal: The |program| line
+does not mention any |output| file, because \ph\ would ask the \MF\ user
+to specify a file name if |output| were specified here.
+@^system dependencies@>
+
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@y
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@d standard_input==i@&n@&p@&u@&t {and another}
+@z
+@x
+program MF; {all file names are defined dynamically}
+@y
+program MF(standard_input,output); {other file names are defined dynamically}
+@z
+@x
+var @<Global variables@>@/
+@y
+var @<Global variables@>@/
+@#
+@\@=#include "ext.h"@>@\ {declarations for external C procedures}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.7] debug..gubed, stat..tats
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@y
+@d debug==@{
+@d gubed==@t@>@}
+@z
+% (I will use the debug code only in the trap test)
+@x
+@d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering
+ usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering
+ usage statistics}
+@y
+@d stat==
+@d tats==
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.8] init..tini
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
+@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
+@y
+@d init==
+@d tini==
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.9] compiler directives
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+@y
+@=(*$C-*)@> {no range check}
+@!debug @=(*$C+*)@> @+ gubed {but turn everything on when debugging}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.10] othercases, feature of Sun Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.11] compile-time constants
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Constants...@>=
+@!mem_max=30000; {greatest index in \MF's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INIMF}, otherwise |>=mem_top|}
+@!max_internal=100; {maximum number of internal quantities}
+@!buf_size=500; {maximum number of characters simultaneously present in
+ current lines of open files; must not exceed |max_halfword|}
+@!error_line=72; {width of context lines on terminal error messages}
+@!half_error_line=42; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!screen_width=768; {number of pixels in each row of screen display}
+@!screen_depth=1024; {number of pixels in each column of screen display}
+@!stack_size=30; {maximum number of simultaneous input sources}
+@!max_strings=2000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=8000; {the minimum number of characters that should be
+ available for the user's identifier names and strings,
+ after \MF's own error messages are stored}
+@!pool_size=32000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all identifiers;
+ must exceed |string_vacancies| by the total
+ length of \MF's own strings, which is currently about 22000}
+@!move_size=5000; {space for storing moves in a single octant}
+@!max_wiggle=300; {number of autorounded points per cycle}
+@!gf_buf_size=800; {size of the output buffer, must be a multiple of 8}
+@!file_name_size=40; {file names shouldn't be longer than this}
+@!pool_name='MFbases:MF.POOL ';
+ {string of length |file_name_size|; tells where the string pool appears}
+@y
+@<Constants...@>=
+@!mem_max=30000; {greatest index in \MF's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INIMF}, otherwise |>=mem_top|}
+@!max_internal=100; {maximum number of internal quantities}
+@!buf_size=500; {maximum number of characters simultaneously present in
+ current lines of open files; must not exceed |max_halfword|}
+@!error_line=79; {width of context lines on terminal error messages}
+@!half_error_line=50; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!screen_width=1024; {number of pixels in each row of screen display}
+@!screen_depth=800; {number of pixels in each column of screen display}
+@!stack_size=30; {maximum number of simultaneous input sources}
+@!max_strings=3000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=18000; {the minimum number of characters that should be
+ available for the user's identifier names and strings,
+ after \MF's own error messages are stored}
+@!pool_size=42000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all identifiers;
+ must exceed |string_vacancies| by the total
+ length of \MF's own strings, which is currently about 22000}
+@!move_size=5000; {space for storing moves in a single octant}
+@!max_wiggle=300; {number of autorounded points per cycle}
+@!gf_buf_size=800; {size of the output buffer, must be a multiple of 8}
+@!file_name_size=1024; {file names shouldn't be longer than this}
+@!pool_name='mf.pool';
+ {string of length |file_name_size|; tells where the string pool appears}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.12] sensitive compile-time constants
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d mem_min=0 {smallest index in the |mem| array, must not be less
+ than |min_halfword|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INIMF};
+ must be substantially larger than |mem_min|
+ and not greater than |mem_max|}
+@d hash_size=2100 {maximum number of symbolic tokens,
+ must be less than |max_halfword-3*param_size|}
+@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
+@d max_in_open=6 {maximum number of input files and error insertions that
+ can be going on simultaneously}
+@d param_size=150 {maximum number of simultaneous macro parameters}
+@y
+@d mem_min==-30000 {smallest index in the |mem| array, must not be less
+ than |min_halfword|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INIMF};
+ must be substantially larger than |mem_min|
+ and not greater than |mem_max|}
+@d hash_size=2100 {maximum number of symbolic tokens,
+ must be less than |max_halfword-3*param_size|}
+@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
+@d max_in_open=10 {maximum number of input files and error insertions that
+ can be going on simultaneously. Enlarged for Silvio Levy's Greek}
+@d param_size=150 {maximum number of simultaneous macro parameters}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.22] Permissive input
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+@y
+@^character set dependencies@>
+@^system dependencies@>
+
+@d tab = @'11 { ASCII horizontal tab }
+@d form_feed = @'14 { ASCII form-feed }
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:=chr(i);
+for i:=@'177 to @'377 do xchr[i]:=chr(i);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.24] file types
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+The program actually makes use also of a third kind of file, called a
+@y
+The pc compiler on Berkeley {\mc UNIX} won't pack a range into one byte unless
+the lower bound is |-128| or more, and the upper bound is |127| or less.
+This applies to files, too. To make the program work with compiler range
+checking turned on, we'll have to adjust |eight_bits| codes by sometimes
+adding or subtracting |256|.
+Note that |eight_bits| really takes 16 bits here.
+
+The program actually makes use also of a third kind of file, called a
+@z
+@x
+@!alpha_file=packed file of text_char; {files that contain textual data}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+@!alpha_file=t@&e@&x@&t; {files that contain textual data}
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.25] add real_name_of_file array for search path resolution
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \MF\ can open a file whose external name is specified by
+|name_of_file|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file:packed array[1..file_name_size] of char;@;@/
+ {on some systems this may be a \&{record} variable}
+@!name_length:0..file_name_size;@/{this many characters are actually
+ relevant in |name_of_file| (the rest are blank)}
+@y
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \MF\ can open a file whose external name is specified by
+|name_of_file|.
+The Berkeley {\mc UNIX} version uses search paths to look for files to open.
+We use |real_name_of_file| to hold the |name_of_file| with a directory
+name from the path in front of it.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file,@!real_name_of_file:packed array[1..file_name_size] of char;
+@;@/
+@!name_length:0..file_name_size;@/{this many characters are actually
+ relevant in |name_of_file| (the rest are blank)}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.26] file opening
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The \ph\ compiler with which the present version of \MF\ was prepared has
+extended the rules of \PASCAL\ in a very convenient way. To open file~|f|,
+we can write
+$$\vbox{\halign{#\hfil\qquad&#\hfil\cr
+|reset(f,@t\\{name}@>,'/O')|&for input;\cr
+|rewrite(f,@t\\{name}@>,'/O')|&for output.\cr}}$$
+The `\\{name}' parameter, which is of type `\ignorespaces|packed
+array[@t\<\\{any}>@>] of text_char|', stands for the name of
+the external file that is being opened for input or output.
+Blank spaces that might appear in \\{name} are ignored.
+
+The `\.{/O}' parameter tells the operating system not to issue its own
+error messages if something goes wrong. If a file of the specified name
+cannot be found, or if such a file cannot be opened for some other reason
+(e.g., someone may already be trying to write the same file), we will have
+|@!erstat(f)<>0| after an unsuccessful |reset| or |rewrite|. This allows
+\MF\ to undertake appropriate corrective action.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+\MF's file-opening procedures return |false| if no file identified by
+|name_of_file| could be opened.
+
+@d reset_OK(#)==erstat(#)=0
+@d rewrite_OK(#)==erstat(#)=0
+
+@p function a_open_in(var @!f:alpha_file):boolean;
+ {open a text file for input}
+begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f);
+end;
+@#
+function a_open_out(var @!f:alpha_file):boolean;
+ {open a text file for output}
+begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f);
+end;
+@#
+function b_open_out(var @!f:byte_file):boolean;
+ {open a binary file for output}
+begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f);
+end;
+@#
+function w_open_in(var @!f:word_file):boolean;
+ {open a word file for input}
+begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f);
+end;
+@#
+function w_open_out(var @!f:word_file):boolean;
+ {open a word file for output}
+begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f);
+end;
+@y
+@ The \ph\ compiler with which the present version of \MF\ was prepared has
+extended the rules of \PASCAL\ in a very convenient way for file opening.
+Berkeley {\mc UNIX} \PASCAL\ isn't nearly as nice as \ph.
+Normally, it bombs out if a file open fails.
+An external C procedure, |test_access| is used to check whether or not the
+open will work. It is declared in the ``ext.h'' include file, and it returns
+|true| or |false|. The |name_of_file| global holds the file name whose access
+is to be tested.
+The first parameter for |test_access| is the access mode,
+one of |read_access_mode| or |write_access_mode|.
+
+We also implement path searching in |test_access|: its second parameter is
+one of the ``file path'' constants defined below. If |name_of_file|
+doesn't start with |'/'| then |test_access| tries prepending pathnames
+from the appropriate path list until success or the end of path list
+is reached.
+On return, |real_name_of_file| contains the original name with the path
+that succeeded (if any) prepended. It is the name used in the various
+open procedures.
+
+Note that |a_open_in| has been redefined to take an additional argument,
+which should be one of the ``file path'' specifiers.
+Path searching is not done for output files.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {no path searching should be done}
+@d input_file_path=1 {path specifier for \.{\\input} files}
+@d base_file_path=4 {path specifier for base files}
+@d pool_file_path=5 {path specifier for the pool file}
+
+@p function a_open_in(var @!f:alpha_file;@!path_specifier:integer):boolean;
+ {open a text file for input}
+var @!ok:boolean;
+begin
+if test_access(read_access_mode,path_specifier) then
+ begin reset(f,real_name_of_file); ok:=true@+end
+else
+ ok:=false;
+a_open_in:=ok;
+end;
+@#
+function a_open_out(var @!f:alpha_file):boolean;
+ {open a text file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+a_open_out:=ok;
+end;
+@#
+function b_open_out(var f:byte_file):boolean;
+ {open a binary file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+b_open_out:=ok;
+end;
+@#
+function w_open_in(var @!f:word_file):boolean;
+ {open a word file for input}
+var @!ok:boolean;
+begin
+if test_access(read_access_mode,base_file_path) then
+ begin reset(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+w_open_in:=ok;
+end;
+@#
+function w_open_out(var @!f:word_file):boolean;
+ {open a word file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,name_of_file); ok:=true @+end
+else ok:=false;
+w_open_out:=ok;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.27] file closing
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+@p procedure a_close(var @!f:alpha_file); {close a text file}
+begin close(f);
+end;
+@#
+procedure b_close(var @!f:byte_file); {close a binary file}
+begin close(f);
+end;
+@#
+procedure w_close(var @!f:word_file); {close a word file}
+begin close(f);
+end;
+@y
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+With the pc library, files will be automatically closed when the program stops
+and when one reopens them.
+There is, however, the problem of opening a file and writing it, and then
+wanting to read it again, but from a different file variable.
+For this purpose we use external procedures |close_a|, |close_b|, and
+|close_w|.
+
+@p procedure a_close(var f:alpha_file); {close a text file}
+begin close_a(f);
+end;
+@#
+procedure b_close(var f:byte_file); {close a binary file}
+begin close_b(f);
+end;
+@#
+procedure w_close(var f:word_file); {close a word file}
+begin close_w(f);
+end;
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.30] faster version of input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Standard \PASCAL\ says that a file should have |eoln| immediately
+before |eof|, but \MF\ needs only a weaker restriction: If |eof|
+occurs in the middle of a line, the system function |eoln| should return
+a |true| result (even though |f^| will be undefined).
+
+@p function input_ln(var @!f:alpha_file;@!bypass_eoln:boolean):boolean;
+ {inputs the next line or returns |false|}
+var @!last_nonblank:0..buf_size; {|last| with trailing blanks removed}
+begin if bypass_eoln then if not eof(f) then get(f);
+ {input the first character of the line into |f^|}
+last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
+if eof(f) then input_ln:=false
+else begin last_nonblank:=first;
+ while not eoln(f) do
+ begin if last>=max_buf_stack then
+ begin max_buf_stack:=last+1;
+ if max_buf_stack=buf_size then
+ @<Report overflow of the input buffer, and abort@>;
+ end;
+ buffer[last]:=xord[f^]; get(f); incr(last);
+ if buffer[last-1]<>" " then last_nonblank:=last;
+ end;
+ last:=last_nonblank; input_ln:=true;
+ end;
+end;
+@y
+Standard \PASCAL\ says that a file should have |eoln| immediately
+before |eof|, but \MF\ needs only a weaker restriction: If |eof|
+occurs in the middle of a line, the system function |eoln| should return
+a |true| result (even though |f^| will be undefined).
+
+With Berkeley {\mc UNIX} we call an external C procedure, |line_read|.
+That routine fills |buffer| from |first| onwards with the |xord|'ed values
+of the next line, setting |last| appropriately. It will stop if
+|last=buf_size|, and the following will cause an ``overflow'' abort.
+We ignore the |bypass_eoln| argument, since the |line_read| procedure
+will have handled bypassing the end-of-line in its previous call, if
+there was one.
+
+If one uses |input_ln| on a file, the normal |read|
+and |get| routines shouldn't be used, nor the |eof| and |eoln| tests.
+End of file can be tested with the external function |test_eof|.
+
+@p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean;
+ {inputs the next line or returns |false|}
+label done;
+begin
+last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
+if test_eof(f) then input_ln:=false
+else begin last:=first;
+ line_read(f,buf_size);
+ if last>max_buf_stack then begin
+ max_buf_stack:=last;
+ if max_buf_stack>=buf_size then
+ @<Report overflow of the input buffer, and abort@>;
+ end;
+ loop@+ begin if last=first then goto done;
+ if buffer[last-1]<>" " then goto done;
+ decr(last);
+ end;
+done: input_ln:=true;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.31] term_in/out are input,output
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file variable is called |term_in|, and when it
+is considered an output file the file variable is |term_out|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!term_in:alpha_file; {the terminal as an input file}
+@!term_out:alpha_file; {the terminal as an output file}
+@y
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file is called |term_in|, and when it
+is considered an output file the file is |term_out|.
+For Berkeley {\mc UNIX} we use |standard_input| and |output| for these
+files, as were declared in the program header. The former name
+|standard_input| was defined using a web trick so that ``input'' is
+produced in the Pascal file.
+
+@d term_in==standard_input {the terminal as an input file}
+@d term_out==output {the terminal as an output file}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.32] don't need to open terminal files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Here is how to open the terminal files
+in \ph. The `\.{/I}' switch suppresses the first |get|.
+@^system dependencies@>
+
+@d t_open_in==reset(term_in,'TTY:','/O/I') {open the terminal for text input}
+@d t_open_out==rewrite(term_out,'TTY:','/O') {open the terminal for text output}
+@y
+@ Here is how to open the terminal files---do nothing.
+
+@d t_open_in == {input already open for text input}
+@d t_open_out == {output already open for text output}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.33] flushing output
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+these operations can be specified in \ph:
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@d clear_terminal == break_in(term_in,true) {clear the terminal input buffer}
+@d wake_up_terminal == do_nothing {cancel the user's cancellation of output}
+@y
+these operations can be specified with Berkeley {\mc UNIX}:
+@^system dependencies@>
+
+@d update_terminal == flush(output)
+@d clear_terminal == {nothing necessary on UNIX}
+@d wake_up_terminal == {nothing necessary on UNIX}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.36] rescanning the command line
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following program does the required initialization
+without retrieving a possible command line.
+It should be clear how to modify this routine to deal with command lines,
+if the system permits them.
+@^system dependencies@>
+
+@p function init_terminal:boolean; {gets the terminal input started}
+label exit;
+begin t_open_in;
+loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal;
+@.**@>
+ if not input_ln(term_in,true) then {this shouldn't happen}
+ begin write_ln(term_out);
+ write(term_out,'! End of file on the terminal... why?');
+@.End of file on the terminal@>
+ init_terminal:=false; return;
+ end;
+ loc:=first;
+ while (loc<last)and(buffer[loc]=" ") do incr(loc);
+ if loc<last then
+ begin init_terminal:=true;
+ return; {return unless the line was all blank}
+ end;
+ write_ln(term_out,'Please type the name of your input file.');
+ end;
+exit:end;
+@y
+@ The following program does the required initialization
+and also retrieves a possible command line.
+@^system dependencies@>
+
+@p
+function init_terminal:boolean; {gets the terminal input started}
+label exit;
+
+var
+ dummy, i, j, k: integer;
+ arg: packed array[1..100] of char;
+
+begin
+ t_open_in;
+ if argc > 1 then begin
+ last := first;
+ for i := 1 to argc - 1 do begin
+ argv(i, arg);
+ j := 1;
+ k := 100; {find last non-blank char in arg}
+ while (k > 1) and (arg[k] = ' ') do@/
+ decr(k);
+ while (j <= k) do begin
+ buffer[last] := xord[arg[j]];
+ incr(j); incr(last);
+ end;
+ if k > 1 then begin
+ buffer[last] := xord[' '];
+ incr(last);
+ end;
+ end;
+ if last > first then begin
+ loc := first;
+ init_terminal := true;
+ return;
+ end;
+ end;
+ loop@+begin
+ wake_up_terminal; write(term_out, '**'); update_terminal;
+@.**@>
+ if not input_ln(term_in,true) then begin {this shouldn't happen}
+ write_ln(term_out);
+ write_ln(term_out, '! End of file on the terminal... why?');
+@.End of file on the terminal@>
+ init_terminal:=false;
+ return;
+ end;
+
+ loc:=first;
+ while (loc<last)and(buffer[loc]=" ") do
+ incr(loc);
+
+ if loc<last then begin
+ init_terminal:=true;
+ return; {return unless the line was all blank}
+ end;
+ write_ln(term_out, 'Please type the name of your input file.');
+@.Please type the name...@>
+ end;
+exit:
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4.51] a_open_in of pool file needs path specifier
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if a_open_in(pool_file) then
+@y
+if a_open_in(pool_file,pool_file_path) then
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4.51,52,53] make MF.POOL lowercase in messages
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+else bad_pool('! I can''t read MF.POOL.')
+@y
+else bad_pool('! I can''t read mf.pool.')
+@z
+@x
+begin if eof(pool_file) then bad_pool('! MF.POOL has no check sum.');
+@y
+begin if eof(pool_file) then bad_pool('! mf.pool has no check sum.');
+@z
+@x
+ bad_pool('! MF.POOL line doesn''t begin with two digits.');
+@y
+ bad_pool('! mf.pool line doesn''t begin with two digits.');
+@z
+@x
+ bad_pool('! MF.POOL check sum doesn''t have nine digits.');
+@y
+ bad_pool('! mf.pool check sum doesn''t have nine digits.');
+@z
+@x
+done: if a<>@$ then bad_pool('! MF.POOL doesn''t match; TANGLE me again.');
+@y
+done: if a<>@$ then bad_pool('! mf.pool doesn''t match; tangle me again.');
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6.79] switch-to-editor option
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+line ready to be edited. But such an extension requires some system
+wizardry, so the present implementation simply types out the name of the
+file that should be
+edited and the relevant line number.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines haven't
+been commented~out.
+@^debugging@>
+@y
+line ready to be edited.
+We do this by calling the external procedure |calledit| with a pointer to
+the filename, its length, and the line number.
+However, here we just set up the variables that will be used as arguments,
+since we don't want to do the switch-to-editor until after \MF\ has closed
+its files.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines have
+not been commented out.
+@^debugging@>
+@d edit_file==input_stack[file_ptr]
+@z
+@x
+"E": if file_ptr>0 then
+ begin print_nl("You want to edit file ");
+@.You want to edit file x@>
+ slow_print(input_stack[file_ptr].name_field);
+ print(" at line "); print_int(line);@/
+ interaction:=scroll_mode; jump_out;
+@y
+"E": if file_ptr>0 then
+ begin
+ ed_name_start:=str_start[edit_file.name_field];
+ ed_name_length:=str_start[edit_file.name_field+1] -
+ str_start[edit_file.name_field];
+ edit_line:=line;
+ jump_out;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7.107,108] replace make_fraction with external routine
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function make_fraction(@!p,@!q:integer):fraction;
+var @!f:integer; {the fraction bits, with a leading 1 bit}
+@!n:integer; {the integer part of $\vert p/q\vert$}
+@!negative:boolean; {should the result be negated?}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin if p>=0 then negative:=false
+else begin negate(p); negative:=true;
+ end;
+if q<=0 then
+ begin debug if q=0 then confusion("/");@;@+gubed@;@/
+@:this can't happen /}{\quad \./@>
+ negate(q); negative:=not negative;
+ end;
+n:=p div q; p:=p mod q;
+if n>=8 then
+ begin arith_error:=true;
+ if negative then make_fraction:=-el_gordo@+else make_fraction:=el_gordo;
+ end
+else begin n:=(n-1)*fraction_one;
+ @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>;
+ if negative then make_fraction:=-(f+n)@+else make_fraction:=f+n;
+ end;
+end;
+
+@ The |repeat| loop here preserves the following invariant relations
+between |f|, |p|, and~|q|:
+(i)~|0<=p<q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and
+$p_0$ is the original value of~$p$.
+
+Notice that the computation specifies
+|(p-q)+p| instead of |(p+p)-q|, because the latter could overflow.
+Let us hope that optimizing compilers do not miss this point; a
+special variable |be_careful| is used to emphasize the necessary
+order of computation. Optimizing compilers should keep |be_careful|
+in a register, not store it in memory.
+@^inner loop@>
+
+@<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>=
+f:=1;
+repeat be_careful:=p-q; p:=be_careful+p;
+if p>=0 then f:=f+f+1
+else begin double(f); p:=p+q;
+ end;
+until f>=fraction_one;
+be_careful:=p-q;
+if be_careful+p>=0 then incr(f)
+
+@y
+Under {\mc UNIX} \MF, we have replaced the \PASCAL\ version of |make_fraction|
+with either a C or assembly language equivalent for efficiency.
+
+@ This section was deleted when |make_fraction| was removed.
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7.109,110,111] replace take_fraction with external version
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function take_fraction(@!q:integer;@!f:fraction):integer;
+var @!p:integer; {the fraction so far}
+@!negative:boolean; {should the result be negated?}
+@!n:integer; {additional multiple of $q$}
+@!be_careful:integer; {disables certain compiler optimizations}
+begin @<Reduce to the case that |f>=0| and |q>0|@>;
+if f<fraction_one then n:=0
+else begin n:=f div fraction_one; f:=f mod fraction_one;
+ if q<=el_gordo div n then n:=n*q
+ else begin arith_error:=true; n:=el_gordo;
+ end;
+ end;
+f:=f+fraction_one;
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>;
+be_careful:=n-el_gordo;
+if be_careful+p>0 then
+ begin arith_error:=true; n:=el_gordo-p;
+ end;
+if negative then take_fraction:=-(n+p)
+else take_fraction:=n+p;
+end;
+
+@ @<Reduce to the case that |f>=0| and |q>0|@>=
+if f>=0 then negative:=false
+else begin negate(f); negative:=true;
+ end;
+if q<0 then
+ begin negate(q); negative:=not negative;
+ end;
+
+@ The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor
+=\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and
+$f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$.
+@^inner loop@>
+
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>=
+p:=fraction_half; {that's $2^{27}$; the invariants hold now with $k=28$}
+if q<fraction_four then
+ repeat if odd(f) then p:=half(p+q)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+else repeat if odd(f) then p:=p+half(q-p)@+else p:=half(p);
+ f:=half(f);
+ until f=1
+
+@y
+Function |take_fraction| has also been replaced by an external routine.
+
+@ @<Reduce to the case that |f>=0| and |q>0|@>=
+if f>=0 then negative:=false
+else begin negate(f); negative:=true;
+ end;
+if q<0 then
+ begin negate(q); negative:=not negative;
+ end;
+
+@ This section was deleted when |take_fraction| was replaced by an
+external routine. (But the previous section was left in, because
+it is part of |take_scaled|, which follows.)
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [9.153] ranges for quarter,half words
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+can get 256 values into a quarterword only if the subrange is `|-128..127|'.
+
+The present implementation tries to accommodate as many variations as possible,
+so it makes few assumptions. If integers having the subrange
+`|min_quarterword..max_quarterword|' can be packed into a quarterword,
+and if integers having the subrange `|min_halfword..max_halfword|'
+can be packed into a halfword, everything should work satisfactorily.
+
+It is usually most efficient to have |min_quarterword=min_halfword=0|,
+so one should try to achieve this unless it causes a severe problem.
+The values defined here are recommended for most 32-bit computers.
+
+@d min_quarterword=0 {smallest allowable value in a |quarterword|}
+@d max_quarterword=255 {largest allowable value in a |quarterword|}
+@d min_halfword==0 {smallest allowable value in a |halfword|}
+@d max_halfword==65535 {largest allowable value in a |halfword|}
+@y
+can get 256 values into a quarterword only if the subrange is `|-128..127|'.
+
+For Berkeley {\mc UNIX} we need to do the |-128..127| kind of range.
+
+@d min_quarterword=-128 {smallest allowable value in a |quarterword|}
+@d max_quarterword=127 {largest allowable value in a |quarterword|}
+@d min_halfword==-32768 {smallest allowable value in a |halfword|}
+@d max_halfword==32767 {largest allowable value in a |halfword|}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [11.178] fix the word "free" so that it doesn't conflict with a runtime proc
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+@y
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+
+@d free==free_arr
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [12.194] fix_date_and_time
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following procedure, which is called just before \MF\ initializes its
+input and output, establishes the initial values of the date and time.
+@^system dependencies@>
+Since standard \PASCAL\ cannot provide such information, something special
+is needed. The program here simply specifies July 4, 1776, at noon; but
+users probably want a better approximation to the truth.
+
+Note that the values are |scaled| integers. Hence \MF\ can no longer
+be used after the year 32767.
+
+@p procedure fix_date_and_time;
+begin internal[time]:=12*60*unity; {minutes since midnight}
+internal[day]:=4*unity; {fourth day of the month}
+internal[month]:=7*unity; {seventh month of the year}
+internal[year]:=1776*unity; {Anno Domini}
+end;
+@y
+@ The following procedure, which is called just before \MF\ initializes its
+input and output, establishes the initial values of the date and time.
+It is calls an externally defined |date_and_time|, even though it could
+be done from Pascal.
+The external procedure also sets up interrupt catching.
+@^system dependencies@>
+
+Note that the values are |scaled| integers. Hence \MF\ can no longer
+be used after the year 32767.
+
+@p procedure fix_date_and_time;
+begin
+ date_and_time(internal[time],internal[day],internal[month],internal[year]);
+ internal[time] := internal[time] * unity;
+ internal[day] := internal[day] * unity;
+ internal[month] := internal[month] * unity;
+ internal[year] := internal[year] * unity;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [12.199] allow <tab> and <ff> as input
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for k:=0 to " "-1 do char_class[k]:=invalid_class;
+for k:=127 to 255 do char_class[k]:=invalid_class;
+@y
+for k:=0 to " "-1 do char_class[k]:=invalid_class;
+for k:=127 to 255 do char_class[k]:=invalid_class;
+char_class[tab]:=space_class;
+char_class[form_feed]:=space_class;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27.564] init_screen and update_screen are external C procs
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% BEGIN HOOK FOR THE TRAP TEST
+%@x
+%begin init_screen:=false;
+%@y
+%begin init_screen:=true;
+%@z
+@x
+The \PASCAL\ code here is a minimum version of |init_screen| and
+|update_screen|, usable on \MF\ installations that don't
+support screen output. If |init_screen| is changed to return |true|
+instead of |false|, the other routines will simply log the fact
+that they have been called; they won't really display anything.
+The standard test routines for \MF\ use this log information to check
+that \MF\ is working properly, but the |wlog| instructions should be
+removed from production versions of \MF.
+
+@p function init_screen:boolean;
+begin init_screen:=false;
+end;
+@#
+procedure update_screen; {will be called only if |init_screen| returns |true|}
+begin @!init wlog_ln('Calling UPDATESCREEN');@+tini {for testing only}
+end;
+
+@y
+We declare the screen functions
+for the {\mc UNIX} version of \MF\ in the following header file.
+
+@p
+@\@=#include "mf_window.h"@>@\ {declarations for external C procedures}
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27.559] blank_rectangle is an external C proc
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+The commented-out code in the following procedure is for illustrative
+purposes only.
+@^system dependencies@>
+
+@p procedure blank_rectangle(@!left_col,@!right_col:screen_col;
+ @!top_row,@!bot_row:screen_row);
+var @!r:screen_row;
+@!c:screen_col;
+begin @{@+for r:=top_row to bot_row-1 do
+ for c:=left_col to right_col-1 do
+ screen_pixel[r,c]:=white;@+@}@/
+@!init wlog_cr; {this will be done only after |init_screen=true|}
+wlog_ln('Calling BLANKRECTANGLE(',left_col:1,',',
+ right_col:1,',',top_row:1,',',bot_row:1,')');@+tini
+end;
+
+@y
+@^system dependencies@>
+
+The procedure |blank_rectangle| has been declared an external routine
+for the {\mc UNIX} version of \MF\ in the header file above.
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27.560] paint_row is an external C proc
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+The general idea is to paint blocks of pixels in alternate colors;
+the precise details are best conveyed by means of a \PASCAL\
+program (see the commented-out code below).
+@^system dependencies@>
+
+@p procedure paint_row(@!r:screen_row;@!b:pixel_color;var @!a:trans_spec;
+ @!n:screen_col);
+var @!k:screen_col; {an index into |a|}
+@!c:screen_col; {an index into |screen_pixel|}
+begin @{ k:=0; c:=a[0];
+repeat incr(k);
+ repeat screen_pixel[r,c]:=b; incr(c);
+ until c=a[k];
+ b:=black-b; {$|black|\swap|white|$}
+ until k=n;@+@}@/
+@!init wlog('Calling PAINTROW(',r:1,',',b:1,';');
+ {this is done only after |init_screen=true|}
+for k:=0 to n do
+ begin wlog(a[k]:1); if k<>n then wlog(',');
+ end;
+wlog_ln(')');@+tini
+end;
+
+@y
+@^system dependencies@>
+
+|Paint_row| has been declared an external routine in the {\mc UNIX} version
+of \MF\ in the header file above.
+
+@z
+% END HOOK FOR THE TRAP TEST
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.768] area and extension rules
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+@y
+@ The file names we shall deal with for SunOS have the
+following structure: If the name contains `\./', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep
+track of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\./', if any}
+@!ext_delimiter:pool_pointer; {the most recent `\..', if any}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.769] MF area directories
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d MF_area=="MFinputs:"
+@.MFinputs@>
+@y
+Under Berkeley {\mc UNIX}, the default paths are specified in a seprate
+file, `\.{mfpaths.h}'. The file opening procedures do path searching
+based either on those default paths, or on paths given by the user
+in ``environment'' variables.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.771] more_name
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+begin if c=" " then more_name:=false
+else begin if (c=">")or(c=":") then
+@y
+begin if (c=" ")or(c=tab) then more_name:=false
+else begin if (c="/") then
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.775] default base
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d base_default_length=18 {length of the |MF_base_default| string}
+@d base_area_length=8 {length of its area part}
+@d base_ext_length=5 {length of its `\.{.base}' part}
+@y
+Under Berkeley {\mc UNIX} we don't give the area part, instead depending
+on the path searching that will happen during file opening.
+
+@d base_default_length=10 {length of the |MF_base_default| string}
+@d base_area_length=0 {length of its area part}
+@d base_ext_length=5 {length of its `\.{.base}' part}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.776] plain base location
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+MF_base_default:='MFbases:plain.base';
+@y
+MF_base_default:='plain.base';
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.779] w_open_in of base file needs to be called only once
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(base_file) then goto found;
+ pack_buffered_name(base_area_length,loc,j-1);
+ {now try the system base file area}
+ if w_open_in(base_file) then goto found;
+@y
+ pack_buffered_name(0,loc,j-1);
+ if w_open_in(base_file) then goto found;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.780] make_name_string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+which simply makes a \MF\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var @!k:1..file_name_size; {index into |name_of_file|}
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings) then
+ make_name_string:="?"
+else begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+end;
+@y
+which simply makes a \MF\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+With the Berkeley {\mc UNIX} version, we know that |real_name_of_file|
+contains |name_of_file| prepended with the directory name that was found
+by path searching.
+If |real_name_of_file| starts with |'./'|, we don't use that part of the
+name, since {\mc UNIX} users understand that.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var @!k,@!kstart:1..file_name_size; {index into |name_of_file|}
+begin
+k:=1;
+while (k<file_name_size) and (xord[real_name_of_file[k]]<>" ") do
+ incr(k);
+name_length:=k-1; {the real |name_length|}
+if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings) then
+ make_name_string:="?"
+else begin
+ if (xord[real_name_of_file[1]]=".") and (xord[real_name_of_file[2]]="/") then
+ kstart:=3
+ else
+ kstart:=1;
+ for k:=kstart to name_length do append_char(xord[real_name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.781] scan_file_name ignore leading tabs as well as spaces
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure scan_file_name;
+label done;
+begin begin_name;
+while buffer[loc]=" " do incr(loc);
+@y
+@p procedure scan_file_name;
+label done;
+begin begin_name;
+while (buffer[loc]=" ")or(buffer[loc]=tab) do incr(loc);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.787] <Scan_file_name...> needs similar leading tab treatment
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ @<Scan file name in the buffer@>=
+begin begin_name; k:=first;
+while (buffer[k]=" ")and(k<last) do incr(k);
+@y
+@ @<Scan file name in the buffer@>=
+begin begin_name; k:=first;
+while ((buffer[k]=" ")or(buffer[k]=tab))and(k<last) do incr(k);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.793] a_open_in of input file needs path selector
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ if a_open_in(cur_file) then goto done;
+ if cur_area="" then
+ begin pack_file_name(cur_name,MF_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+ end;
+@y
+ if a_open_in(cur_file,input_file_path) then goto done;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38.793] get rid of return of name to string pool
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if name=str_ptr-1 then {we can conserve string pool space now}
+ begin flush_string(name); name:=cur_name;
+ end;
+@y
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [45.1133] writing the tfm file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d tfm_out(#)==write(tfm_file,#) {output one byte to |tfm_file|}
+@y
+@d tfm_out(#) ==write(tfm_file,((#+128) mod 256)-128)
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [47.1154] write_gf
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Declare generic font output procedures@>=
+procedure write_gf(@!a,@!b:gf_index);
+var k:gf_index;
+begin for k:=a to b do write(gf_file,gf_buf[k]);
+end;
+@y
+For Berkeley {\mc UNIX}, this is going to be handled by an external procedure,
+|writegf|, which will do the output using |fwrite|.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [49.1204] Delete ready_already; also add call to set_paths;
+% also add call to exit() depending upon value of `history'
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Now this is really it: \MF\ starts and ends here.
+
+The initial test involving |ready_already| should be deleted if the
+\PASCAL\ runtime system is smart enough to detect such a ``mistake.''
+@y
+@ Now this is really it: \MF\ starts and ends here.
+
+Instead of testing |ready_already|, we give up the idea of a preloaded
+core image in favor of silently loading a format file based on the
+name by which this program has been invoked. For example, if the
+program is being called \.{mf}, we will load the \.{plain} base;
+if \.{cmmf}, we will load \.{cmmf.base}. On my machine the
+loading of a format isn't too slow. (I use the new string capabilities
+of Sun Pascal here; |my_name| will be declared {\bf varying} [100]
+{\bf of\/} |char|.)
+
+At the end of the job, we look at |history| to decide the correct
+exit code. We use 1 if |history <= warning_issued| and 0 otherwise.
+
+@d UNIXexit==e@&x@&i@&t
+@z
+
+@x
+t_open_out; {open the terminal for output}
+if ready_already=314159 then goto start_of_MF;
+@y
+t_open_out; {open the terminal for output}
+set_paths;
+@z
+@x
+start_of_MF: @<Initialize the output routines@>;
+@y
+start_of_MF: argv(0,my_name);
+if (my_name<>'inimf')and(my_name<>'trapmf') then
+ begin if my_name = 'mf' then name_of_file:='plain.base'
+ else name_of_file:=my_name+'.base';
+ if w_open_in(base_file) then
+ if not load_base_file then
+ begin w_close(base_file); goto final_end;
+ end
+ else w_close(base_file);
+ end;
+@<Initialize the output routines@>;
+@z
+
+@x
+final_end: ready_already:=0;
+@y
+final_end: ready_already:=0;
+if (history <= warning_issued) then
+ UNIXexit(0)
+else
+ UNIXexit(1);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [49.1204] print new line before termination; switch to editor if nec.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+@y
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+print_ln;
+if (ed_name_start<>0) and (interaction>batch_mode) then
+ calledit(str_pool[ed_name_start],ed_name_length,edit_line);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [51.1214] add editor-switch variable to globals
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* \[51] System-dependent changes.
+This section should be replaced, if necessary, by any special
+modifications of the program
+that are necessary to make \MF\ work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the published program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+@* \[51] System-dependent changes.
+Here are the variables used to hold ``switch-to-editor'' information.
+And the omphaloskeptic variable too.
+@^system dependencies@>
+
+@<Global...@>=
+@!ed_name_start: pool_pointer;
+@!ed_name_length,@!edit_line: integer;
+@!my_name:varying [100] of char;
+
+@ The |ed_name_start| will be set to point into |str_pool| somewhere after
+its beginning if \MF\ is supposed to switch to an editor on exit.
+
+@<Set init...@>=
+ed_name_start:=0;
+@z
+
diff --git a/systems/knuth/local/mf/mf.pool b/systems/knuth/local/mf/mf.pool
new file mode 100644
index 0000000000..4f1478cac2
--- /dev/null
+++ b/systems/knuth/local/mf/mf.pool
@@ -0,0 +1,824 @@
+11buffer size
+09pool size
+17number of strings
+03???
+28End of file on the terminal!
+02!
+42(That makes 100 errors; please try again.)
+02?
+60Type <return> to proceed, S to scroll future error messages,
+44R to run without stopping, Q to run quietly,
+23I to insert something,
+20E to edit your file,
+561 or ... or 9 to ignore the next 1 to 9 tokens of input,
+22H for help, X to quit.
+13OK, entering
+09batchmode
+11nonstopmode
+10scrollmode
+03...
+07insert>
+44I have just deleted some text, as you asked.
+48You can now delete more, or insert, or whatever.
+50Sorry, I don't know how to help in this situation.
+36Maybe you should try asking a human?
+42Sorry, I already gave what help I could...
+59An error might have occurred before I noticed any problems.
+45``If all else fails, read the instructions.''
+00
+14Emergency stop
+35METAFONT capacity exceeded, sorry [
+44If you really absolutely need more capacity,
+35you can ask a wizard to enlarge me.
+19This can't happen (
+59I'm broken. Please show this to someone who can fix can fix
+35I can't go on meeting you like this
+55One of your faux pas seems to have wounded me deeply...
+59in fact, I'm barely conscious. Please fix it and try again.
+12Interruption
+09You rang?
+57Try to insert some instructions for me (e.g.,`I show x'),
+43unless you just want to quit by typing `X'.
+09Missing `
+19' has been inserted
+19Arithmetic overflow
+59Uh, oh. A little while ago one of the quantities that I was
+59computing got too large, so I'm afraid your answers will be
+55somewhat askew. You'll probably have to adopt different
+54tactics next time. But I shall try to carry on anyway.
+15Square root of
+23 has been replaced by 0
+52Since I don't take square roots of negative numbers,
+52I'm zeroing this one. Proceed, with fingers crossed.
+24Pythagorean subtraction
+03+-+
+13Logarithm of
+48Since I don't take logs of non-positive numbers,
+27angle(0,0) is taken as zero
+54The `angle' between two identical points is undefined.
+16main memory size
+24AVAIL list clobbered at
+31Double-AVAIL list clobbered at
+24Doubly free location at
+12Bad flag at
+14New busy locs:
+05LINK(
+05INFO(
+07vacuous
+07boolean
+15unknown boolean
+06string
+14unknown string
+03pen
+11unknown pen
+10future pen
+04path
+12unknown path
+07picture
+15unknown picture
+09transform
+04pair
+13known numeric
+09dependent
+15proto-dependent
+07numeric
+11independent
+10token list
+10structured
+16unsuffixed macro
+14suffixed macro
+09undefined
+04true
+05false
+11nullpicture
+07nullpen
+07jobname
+10readstring
+09pencircle
+13normaldeviate
+03odd
+05known
+07unknown
+03not
+07decimal
+07reverse
+08makepath
+07makepen
+11totalweight
+03oct
+03hex
+05ASCII
+04char
+06length
+13turningnumber
+05xpart
+05ypart
+06xxpart
+06xypart
+06yxpart
+06yypart
+04sqrt
+04mexp
+04mlog
+04sind
+04cosd
+05floor
+14uniformdeviate
+10charexists
+05angle
+05cycle
+02++
+02or
+03and
+02<=
+02>=
+02<>
+07rotated
+07slanted
+06scaled
+07shifted
+11transformed
+07xscaled
+07yscaled
+07zscaled
+17intersectiontimes
+09substring
+07subpath
+13directiontime
+05point
+10precontrol
+11postcontrol
+09penoffset
+02..
+13tracingtitles
+16tracingequations
+15tracingcapsules
+14tracingchoices
+12tracingspecs
+11tracingpens
+15tracingcommands
+15tracingrestores
+13tracingmacros
+12tracingedges
+13tracingoutput
+12tracingstats
+13tracingonline
+04year
+05month
+03day
+04time
+08charcode
+07charext
+06charwd
+06charht
+06chardp
+06charic
+06chardx
+06chardy
+10designsize
+04hppp
+04vppp
+07xoffset
+07yoffset
+07pausing
+12showstopping
+10fontmaking
+08proofing
+09smoothing
+12autorounding
+11granularity
+06fillin
+12turningcheck
+12warningcheck
+12boundarychar
+09 at line
+14a bad variable
+02fi
+08endgroup
+06enddef
+06endfor
+13 INACCESSIBLE
+09hash size
+06EQUIV(
+02::
+03||:
+02:=
+05addto
+02at
+07atleast
+10begingroup
+08controls
+04cull
+04curl
+10delimiters
+07display
+08everyjob
+06exitif
+11expandafter
+04from
+08inwindow
+07interim
+03let
+11newinternal
+02of
+10openwindow
+10randomseed
+04save
+10scantokens
+07shipout
+06skipto
+04step
+03str
+07tension
+02to
+05until
+05token
+05 ETC.
+10 CLOBBERED
+11 IMPOSSIBLE
+12 NONEXISTENT
+04 BAD
+02[]
+05(EXPR
+07(SUFFIX
+05(TEXT
+02->
+03>->
+08<expr>->
+19<expr>of<primary>->
+10<suffix>->
+08<text>->
+07(SAVED)
+03var
+02xx
+02xy
+02yx
+02yy
+08%CAPSULE
+05part
+06struct
+11{restoring
+04Path
+07{open?}
+10..control?
+03 ..
+06{curl
+10..tension
+05 and
+11..controls
+02??
+16, before choices
+15, after choices
+23Some number got too big
+46The path that I just computed is out of range.
+53So it will probably look funny. Proceed, for a laugh.
+09path size
+14Edge structure
+04row
+02 |
+31Scaled picture would be too big
+50I can't yscale the picture as requested---it would
+45make some coordinates too large or too small.
+42Proceed, and I'll omit the transformation.
+50I can't xscale the picture as requested---it would
+15move table size
+13Tracing edges
+09 (weight
+21(No new edges added.)
+10Cycle spec
+24 % beginning in octant `
+19% entering octant `
+08 & cycle
+03ENE
+03NNE
+03NNW
+03WNW
+03WSW
+03SSW
+03SSE
+03ESE
+14 ..controls
+11 % segment
+02 (
+33, before subdivision into octants
+19, after subdivision
+43, after subdivision and double autorounding
+36, after subdivision and autorounding
+18Curve out of range
+56At least one of the coordinates in the path I'm about to
+56digitize was really huge (potentially bigger than 4095).
+40So I've cut it back to the maximum size.
+41The results will probably be pretty wild.
+19rounding table size
+11Pen polygon
+09 .. cycle
+04 ..
+16 (newly created)
+13Pen too large
+59The cycle you specified has a coordinate of 4095.5 or more.
+55So I've replaced it by the trivial path `(0,0)..cycle'.
+24Pen cycle must be convex
+59The cycle you specified either has consecutive equal points
+54or turns right or turns through more than 360 degrees.
+16pen polygon size
+09@ Octant
+07 offset
+08), from
+04 to
+23@ retrograde line from
+18@ transition line
+07, from
+03dep
+02*4
+20Value is too large (
+53The equation I just processed has given some variable
+54a value of 4096 or more. Continue and I'll try to cope
+47with that big value; but it might be dangerous.
+47(Set warningcheck:=0 to suppress this message.)
+05####
+03##
+15Bad PREVDEP at
+16Out of order at
+18Redundant equation
+43I already knew that this equation was true.
+50But perhaps no harm has been done; let's continue.
+23[unknown command code!]
+03<*>
+08<insert>
+12<scantokens>
+02l.
+10<forever>
+11<argument>
+16<recently read>
+19<to be read again>
+16<inserted text>
+05<for(
+03)>
+16input stack size
+17text input levels
+08endinput
+47Incomplete if; all text was ignored after line
+51A forbidden `outer' token occurred in skipped text.
+58This kind of error happens when you say `if...' and forget
+57the matching `fi'. I've inserted a `fi'; this might work.
+53The file ended while I was skipping conditional text.
+10File ended
+21Forbidden token found
+16 while scanning
+41I suspect you have forgotten an `enddef',
+52causing me to read past where you wanted me to stop.
+49I'll try to recover; but if the error is serious,
+51you'd better type `E' or `X' now and fix your file.
+27to the end of the statement
+42A previous error seems to have propagated,
+15a text argument
+45It seems that a right delimiter was left out,
+18the definition of
+14the text of a
+05 loop
+41I suspect you have forgotten an `endfor',
+08Runaway
+05text?
+11definition?
+05loop?
+39Text line contains an invalid character
+53A funny symbol that I can't read has just been input.
+48Continue, and I'll forget that it ever happened.
+40Incomplete string token has been flushed
+53Strings should finish on the same line as they began.
+50I've deleted the partial string; you might want to
+48insert another by typing, e.g., `I"new string"'.
+32Enormous number has been reduced
+52I can't handle numbers bigger than about 4095.99998;
+53so I've changed your constant to that maximum amount.
+36(Please type a command or say `end')
+37*** (job aborted, no legal end found)
+02=>
+03def
+06vardef
+10primarydef
+12secondarydef
+11tertiarydef
+03for
+11forsuffixes
+07forever
+05quote
+02#@
+02@#
+31Missing symbolic token inserted
+52Sorry: You can't redefine a number, string, or expr.
+49I've inserted an inaccessible symbol so that your
+60definition will be completed without mixing me up too badly.
+51Sorry: You can't redefine my error-recovery tokens.
+50The next thing in this `def' should have been `=',
+54because I've already looked at the definition heading.
+49But don't worry; I'll pretend that an equals sign
+45was present. Everything from here to `enddef'
+43will be the replacement text of this macro.
+04expr
+06suffix
+04text
+07primary
+09secondary
+08tertiary
+41This variable already starts with a macro
+44After `vardef a' you can't say `vardef a.b'.
+40So I'll have to discard this definition.
+46Missing parameter type; `expr' will be assumed
+52You should've had `expr' or `suffix' or `text' here.
+20parameter stack size
+14Extra `endfor'
+40I'm not currently working on a for loop,
+40so I had better not try to end anything.
+05input
+09Lost loop
+53I'm confused; after exiting from a loop, I still seem
+53to want to repeat it. I'll try to forget the problem.
+22No loop is in progress
+51Why say `exitif' when there's nothing to exit from?
+57After `exitif <boolean exp>' I expect to see a semicolon.
+35I shall pretend that one was there.
+20*** (loop confusion)
+12Not a string
+41I'm going to flush this expression, since
+48scantokens should be followed by a known string.
+03)<-
+22Too many arguments to
+11 Missing `
+52I'm going to assume that the comma I just read was a
+57right delimiter, and then I'll begin expanding the macro.
+55You might want to delete some tokens before continuing.
+20Missing argument to
+48That macro has more parameters than you thought.
+54I'll continue by pretending that each missing argument
+23is either zero or null.
+54I've finished reading a macro argument and am about to
+56read another; the arguments weren't delimited correctly.
+51I've gotten to the end of the macro parameter list.
+05 for
+57I've got the first argument; will look now for the other.
+02if
+04else
+06elseif
+49There should've been a colon after the condition.
+06{true}
+07{false}
+06Extra
+43I'm ignoring this; it doesn't match any if.
+09Improper
+38When you say `for x=a step b until c',
+43the initial value `a' and the step size `b'
+55and the final value `c' must have known numeric values.
+57The next thing in this loop should have been `=' or `:='.
+47was present, and I'll look for the values next.
+51The next thing in this loop should have been a `:'.
+41So I'll pretend that a colon was present;
+50everything from here to `endfor' will be iterated.
+07 ENDFOR
+12{loop value=
+13initial value
+09step size
+47I assume you meant to say `until' after `step'.
+48So I'll look for the final value and colon next.
+11final value
+05.base
+15input file name
+19I can't find file `
+23I can't write on file `
+02'.
+03.mf
+20Please type another
+45*** (job aborted, file error in nonstop mode)
+02:
+05mfput
+04.log
+02**
+20transcript file name
+02
+20file name for output
+37File names can't appear within macros
+46Sorry...I've converted what follows to tokens,
+37possibly garbaging the name you gave.
+51Please delete the tokens and insert the name again.
+03exp
+26 (see the transcript file)
+13 (future pen)
+10linearform
+03>>
+07recycle
+04###
+03 =
+09A primary
+30 expression can't begin with `
+58I'm afraid I need some sort of value in order to continue,
+49so I've tentatively inserted `0'. You may want to
+43delete this zero and insert something else;
+50see Chapter 27 of The METAFONTbook for an example.
+39Nonnumeric ypart has been replaced by 0
+48I thought you were giving me a pair `(x,y)'; but
+50after finding a nice xpart `x' I found a ypart `y'
+54that isn't of numeric type. So I've changed y to zero.
+59(The y that I didn't like appears above the error message.)
+22A group begun on line
+12 never ended
+56I saw a `begingroup' back there that hasn't been matched
+47by `endgroup'. So I've inserted `endgroup' now.
+16Division by zero
+43I'll pretend that you meant to divide by 1.
+44Improper subscript has been replaced by zero
+54A bracketed subscript must have a known numeric value;
+59unfortunately, what I found was the value that appears just
+55above this error message. So I'll try a zero subscript.
+09Variable
+21 has been obliterated
+54It seems you did a nasty thing---probably by accident,
+46but nevertheless you nearly hornswoggled me...
+50While I was evaluating the right-hand side of this
+51command, something happened, and the left-hand side
+52is no longer a variable! So I won't change anything.
+51While I was evaluating the suffix of this variable,
+55something was redefined, and it's no longer a variable!
+59In order to get back on my feet, I've inserted `0' instead.
+04copy
+47I've scanned an expression of the form `a[b,c',
+41so a right bracket should have come next.
+51I've seen a `[' and a subscript value, in a suffix,
+11A secondary
+10A tertiary
+24Pen path must be a cycle
+39I can't make a pen from the given path.
+02An
+49Undefined coordinates have been replaced by (0,0)
+49I need x and y numbers for this part of the path.
+42The value I found (see above) was no good;
+48so I'll try to keep going by using zero instead.
+45(Chapter 27 of The METAFONTbook explains that
+36you might want to type `I ???' now.)
+45Undefined x coordinate has been replaced by 0
+51I need a `known' x value for this part of the path.
+45Undefined y coordinate has been replaced by 0
+51I need a `known' y value for this part of the path.
+49I've scanned a direction spec for part of a path,
+39so a right brace should have come next.
+36Improper curl has been replaced by 1
+43A curl must be a known, nonnegative number.
+46I've got the x coordinate of a path direction;
+36will look for the y coordinate next.
+45A path join command should end with two dots.
+34Improper tension has been set to 1
+53The expression above should have been a number >=3/4.
+46Paths don't touch; `&' will be changed to `..'
+48When you join paths `p&q', the ending point of p
+49must be exactly equal to the starting point of q.
+53So I'm going to pretend that you said `p..q' instead.
+46Undefined condition will be treated as `false'
+53The expression shown above should have had a definite
+48true-or-false value. I'm changing it to `false'.
+40*** (cannot readstring in nonstop modes)
+12unknown pair
+15unknown numeric
+17Not implemented:
+59I'm afraid I don't know how to apply that operation to that
+53particular type. Continue, and I'll simply return the
+54argument (shown above) as the result of the operation.
+02)}
+30String contains illegal digits
+55I zeroed out characters that weren't in the range 0..7.
+48I zeroed out characters that weren't hex digits.
+18Number too large (
+57I have trouble with numbers greater than 4095; watch out.
+58combination of types. Continue, and I'll return the second
+52argument (see above) as the result of the operation.
+02{(
+49The quantities shown above have not been equated.
+60Oh dear. I can't decide if the expression above is positive,
+59negative, or zero. So this comparison test won't be `true'.
+41Unknown relation will be considered false
+03)*(
+58You're trying to divide the quantity shown above the error
+55message by zero. I'm going to divide it by one instead.
+32Improper transformation argument
+46The expression shown above has the wrong type,
+39so I can't transform anything using it.
+37Transform components aren't all known
+56I'm unable to apply a partially specified transformation
+42except to a fully known pair or transform.
+31That transformation is too hard
+49I can apply complicated transformations to paths,
+49but I can only do integer operations on pictures.
+16Too far to shift
+49I can't shift the picture as requested---it would
+30A statement can't begin with `
+51I was looking for the beginning of a new statement.
+58If you just proceed without changing anything, I'll ignore
+56everything up to the next `;'. Please insert a semicolon
+58now in front of anything that you don't want me to delete.
+52(See Chapter 27 of The METAFONTbook for an example.)
+28Extra tokens will be flushed
+59I've just read as much of that statement as I could fathom,
+59so a semicolon should have been next. It's very puzzling...
+53but I'll try to get myself back together, by ignoring
+19Isolated expression
+40I couldn't find an `=' or `:=' after the
+50expression that is shown above this error message,
+44so I guess I'll just ignore it and carry on.
+36Improper `:=' will be changed to `='
+54I didn't find a variable name at the left of the `:=',
+50so I'm going to pretend that you said `=' instead.
+03)=(
+19Internal quantity `
+28' must receive a known value
+56I can't set an internal quantity to anything but a known
+54numeric value, so I'll have to ignore this assignment.
+30Equation cannot be performed (
+58I'm sorry, but I don't know how to make such things equal.
+55(See the two expressions just above the error message.)
+34Redundant or inconsistent equation
+56An equation between already-known quantities can't help.
+50But don't worry; continue and I'll just ignore it.
+21Inconsistent equation
+58The equation I just read contradicts what was said before.
+09 (off by
+48Declared variable conflicts with previous vardef
+56You can't use, e.g., `numeric foo[]' after `vardef foo'.
+51Proceed, and I'll ignore the illegal redeclaration.
+51Illegal suffix of declared variable will be flushed
+50Variables in declarations must consist entirely of
+46names and collective subscripts, e.g., `x[]a'.
+57Are you trying to use a reserved word in a variable name?
+43I'm going to discard the junk I found here,
+51up to the next comma or the end of the declaration.
+49Explicit subscripts like `x15a' aren't permitted.
+16Extra `endgroup'
+44I'm not currently working on a `begingroup',
+03end
+04dump
+46Always say `randomseed:=<numeric expression>'.
+29Unknown value will be ignored
+48Your expression was too random for me to handle,
+43so I won't change the random seed just now.
+13{randomseed:=
+13errorstopmode
+05inner
+05outer
+55I found no right delimiter to match a left one. So I've
+56put one in, behind the scenes; this may fix the problem.
+11The token `
+32' is no longer a right delimiter
+48Strange: This token has lost its former meaning!
+44I'll read it as a right delimiter this time;
+43but watch out, I'll probably miss it later.
+10(%CAPSULE)
+28' isn't an internal quantity
+55Something like `tracingonline' should follow `interim'.
+46You should have said `let symbol = something'.
+55was present. The next token I read will be `something'.
+19number of internals
+09showtoken
+09showstats
+04show
+12showvariable
+16showdependencies
+02>
+08(outer)
+03lef
+04righ
+25t delimiter that matches
+03tag
+08variable
+06macro:
+09'd macro:
+17[repeat the loop]
+13Memory usage
+17 still untouched)
+13String usage
+07=macro:
+02OK
+56This isn't an error message; I'm just showing something.
+07contour
+10doublepath
+04also
+07withpen
+10withweight
+08dropping
+07keeping
+13Improper type
+54Next time say `withweight <known numeric expression>';
+55I'll ignore the bad `with' clause and look for another.
+47Next time say `withpen <known pen expression>';
+40Weight must be -3, -2, -1, +1, +2, or +3
+20 is the wrong type (
+45I was looking for a "known" picture variable.
+37So I'll not change anything just now.
+23Not a suitable variable
+61At this point I needed to see the name of a picture variable.
+58(Or perhaps you have indeed presented me with one; I might
+59have missed it, if it wasn't followed by the proper token.)
+16Improper `addto'
+54This expression should have specified a known picture.
+46This expression should have been a known path.
+11Not a cycle
+58That contour should have ended with `..cycle' or `&cycle'.
+37Strange path (turning number is zero)
+43Backwards path (turning number is negative)
+53The path doesn't have a counterclockwise orientation,
+41so I'll probably have trouble drawing it.
+51(See Chapter 27 of The METAFONTbook for more help.)
+17Bad window number
+42It should be the number of an open window.
+21Improper `openwindow'
+52Say `openwindow k from (r0,c0) to (r1,c1) at (x,y)',
+57where all quantities are known and k is between 0 and 15.
+19Bad culling amounts
+44Always cull by known amounts that exclude 0.
+07message
+10errmessage
+07errhelp
+46A message should be a known string expression.
+32(That was another `errmessage'.)
+51This error message was generated by an `errmessage'
+43command, so I can't give any explicit help.
+51Pretend that you're Miss Marple: Examine all clues,
+41and deduce the truth by inspired guesses.
+09Enormous
+17 has been reduced
+48Font metric dimensions must be less than 2048pt.
+08charlist
+08ligtable
+10extensible
+10headerbyte
+09fontdimen
+35Invalid code has been replaced by 0
+54I was looking for a number between 0 and 255, or for a
+55string of length 1. Didn't find it; will use 0 instead.
+10Character
+02||
+05code
+12 is already
+13in a ligtable
+13in a charlist
+51It's not legal to label a character more than once.
+17Improper location
+43I was looking for a known, positive number.
+50For safety's sake I'll ignore the present command.
+56A colon should follow a headerbyte or fontinfo location.
+21Illegal ligtable step
+38I was looking for `=:' or `kern' here.
+13ligtable size
+02=:
+03=:|
+04=:|>
+03|=:
+04|=:>
+04|=:|
+05|=:|>
+06|=:|>>
+04kern
+15Too far to skip
+57At most 127 lig/kern steps can separate skipto1 from 1::.
+13Improper kern
+51The amount of kern should be a known numeric value.
+39I'm processing `extensible c: t,m,b,r'.
+23Improper font parameter
+06(some
+41 values had to be adjusted by as much as
+03pt)
+47(illegal design size has been changed to 128pt)
+04.tfm
+26file name for font metrics
+24Font metrics written on
+13(local label
+15:: was missing)
+24(a font metric dimension
+23 font metric dimensions
+21 had to be decreased)
+17 METAFONT output
+03.gf
+02gf
+19 (just shipped out)
+51(There's unbounded black in character shipped out!)
+07special
+10numspecial
+21Unsuitable expression
+59The expression shown above has the wrong type to be output.
+06title
+18Output written on
+10 character
+02,
+08 bytes).
+08 (INIMF)
+25 strings of total length
+43 memory locations dumped; current usage is
+16 symbolic tokens
+17 (preloaded base=
+14base file name
+26Beginning to dump on file
+22Transcript written on
+02 )
+19(end occurred when
+09 on line
+16 was incomplete)
+52(see the transcript file for additional information)
+33(dump is performed only by INIMF)
+21debug # (-1 to exit):
+*415546872
diff --git a/systems/knuth/local/mf/mf_arith.c b/systems/knuth/local/mf/mf_arith.c
new file mode 100644
index 0000000000..5347107e81
--- /dev/null
+++ b/systems/knuth/local/mf/mf_arith.c
@@ -0,0 +1,142 @@
+/*
+ * mf_arith.c -- optimized arithmetic routines for UNIX METAFONT 84
+ *
+ * These functions account for a significant percentage of the processing
+ * time used by METAFONT, and have been recoded in assembly language for
+ * some classes of machines.
+ *
+ * As yet I don't have code for SPARC, so I'm just using a C version
+ * by richards, in hopes that cc does better than pc on the brute-force
+ * algorithm found in mf.web.
+ *
+ * function makefraction(p, q: integer): fraction;
+ * { Calculate the function floor( (p * 2^28) / q + 0.5 ) }
+ * { if both p and q are positive. If not, then the value }
+ * { of makefraction is calculated as though both *were* }
+ * { positive, then the result sign adjusted. }
+ * { (e.g. makefraction ALWAYS rounds away from zero) }
+ * { In case of an overflow, return the largest possible }
+ * { value (2^32-1) with the correct sign, and set global }
+ * { variable "aritherror" to 1. Note that -2^32 is }
+ * { considered to be an illegal product for this type of }
+ * { arithmetic! }
+ *
+ * function takefraction(f: fraction; q: integer): integer;
+ * { Calculate the function floor( (p * q) / 2^28 + 0.5 ) }
+ * { takefraction() rounds in a similar manner as }
+ * { makefraction() above. }
+ *
+ */
+
+#include "h00vars.h"
+
+extern bool aritherror; /* to be set on overflow */
+
+#define EL_GORDO 0x7fffffff /* 2^31-1 */
+#define FRACTION_ONE 0x10000000 /* 2^28 */
+#define FRACTION_HALF 0x08000000 /* 2^27 */
+#define FRACTION_FOUR 0x40000000 /* 2^30 */
+
+int makefraction(p, q)
+ register int p, q;
+{
+ int negative;
+ register int be_careful;
+ register int f;
+ int n;
+
+ if (p < 0) {
+ negative = 1;
+ p = -p;
+ } else negative = 0;
+
+ if (q < 0) {
+ negative = !negative;
+ q = -q;
+ }
+
+ n = p / q;
+ p = p % q;
+
+ if (n >= 8) {
+ aritherror = TRUE;
+ return (negative? -EL_GORDO : EL_GORDO);
+ }
+
+ n = (n-1)*FRACTION_ONE;
+ f = 1;
+ do {
+ be_careful = p - q;
+ p = be_careful + p;
+ if (p >= 0) {
+ f = f + f + 1;
+ } else {
+ f <<= 1;
+ p += q;
+ }
+ } while (f < FRACTION_ONE);
+
+ be_careful = p - q;
+ if ((be_careful + p) >= 0)
+ f += 1;
+ return (negative? -(f+n) : (f+n));
+
+}
+
+int takefraction(q, f)
+ register int q;
+ register int f;
+{
+ int n, negative;
+ register int p, be_careful;
+
+ if (f < 0) {
+ negative = 1;
+ f = -f;
+ } else negative = 0;
+ if (q < 0) {
+ negative = !negative;
+ q = -q;
+ }
+ if (f < FRACTION_ONE)
+ n = 0;
+ else {
+ n = f / FRACTION_ONE;
+ f = f % FRACTION_ONE;
+ if (q < (EL_GORDO / n))
+ n = n * q;
+ else {
+ aritherror = TRUE;
+ n = EL_GORDO;
+ }
+ }
+
+ f += FRACTION_ONE;
+ p = FRACTION_HALF;
+ if (q < FRACTION_FOUR)
+ do {
+ if (f & 0x1)
+ p = (p+q) >> 1;
+ else
+ p >>= 1;
+ f >>= 1;
+ } while (f > 1);
+ else
+ do {
+ if (f & 0x1)
+ p = p + ((q-p) >> 1);
+ else
+ p >>= 1;
+ f >>= 1;
+ } while (f > 1);
+
+ be_careful = n - EL_GORDO;
+ if ((be_careful + p) > 0) {
+ aritherror = TRUE;
+ n = EL_GORDO - p;
+ }
+
+ return(negative? -(n+p) : (n+p));
+}
+
+
diff --git a/systems/knuth/local/mf/mf_sunwin.c b/systems/knuth/local/mf/mf_sunwin.c
new file mode 100644
index 0000000000..f1e9fa9b39
--- /dev/null
+++ b/systems/knuth/local/mf/mf_sunwin.c
@@ -0,0 +1,90 @@
+/*
+ * XView interface to MetaFont
+ * (written hastily and without much understanding...)
+ */
+#define SCREENWIDTH 1024 /* must match the constant in inimf.ch */
+#define SCREENDEPTH 800 /* must match the constant in inimf.ch */
+
+#include <stdio.h>
+#include <xview/xview.h>
+#include <xview/canvas.h>
+#include <X11/Xlib.h>
+
+static Frame frame; /* container */
+static Canvas canvas; /* the viewing and imaging area */
+static Xv_Window canvas_paintwin; /* image component of |canvas| */
+static Display *display; /* handle to X server */
+static Visual *screen; /* where the X server paints */
+static Window xcanvas; /* the X11 ``drawable'' component of our canvas */
+static GC gc; /* X graphic context */
+static XGCValues GCvalues; /* changes to a graphic context */
+
+/*
+ * updatescreen; -- make sure screen display is current
+ */
+
+updatescreen()
+{
+ notify_dispatch();
+ XFlush(display);
+}
+
+/*
+ * init_screen: boolean; return true if window operations legal
+ */
+
+initscreen()
+{
+ if (xv_init(NULL)==NULL) return 0;
+ frame=(Frame)xv_create(NULL,FRAME,XV_WIDTH,700,XV_HEIGHT,600,
+ FRAME_SHOW_HEADER,FALSE,
+ XV_SHOW,TRUE,NULL);
+ canvas=(Canvas)xv_create(frame,CANVAS,CANVAS_REPAINT_PROC,updatescreen,
+ CANVAS_X_PAINT_WINDOW,TRUE,
+ XV_HEIGHT,SCREENDEPTH,XV_WIDTH,SCREENWIDTH,
+ CANVAS_AUTO_EXPAND,FALSE,CANVAS_AUTO_SHRINK,FALSE,
+ NULL);
+ canvas_paintwin=(Xv_window)xv_get(canvas,CANVAS_NTH_PAINT_WINDOW,0);
+ xcanvas=(Window)xv_get(canvas_paintwin,XV_XID);
+ display=(Display*)xv_get(frame,XV_DISPLAY);
+ screen=(Visual*)xv_get(frame,XV_VISUAL);
+ gc=XCreateGC(display,DefaultRootWindow(display),0,GCvalues);
+ notify_do_dispatch(); /* this should allow terminal input */
+ return 1;
+}
+
+/*
+ * blankrectangle: reset rectangle bounded by ([left,right],[top,bottom])
+ * to background color
+ */
+
+blankrectangle(left, right, top, bottom)
+ short left, right;
+ short top, bottom;
+{
+ XSetFunction(display,gc,GXclear);
+ XFillRectangle(display,xcanvas,gc,left,top,right-left,bottom-top);
+}
+
+/*
+ * paintrow -- paint row r starting with color b, up to next
+ * transition specified by vector a, switch colors,
+ * and continue for n transitions.
+ */
+
+paintrow(r, b, a, n)
+ short r;
+ short b;
+ short a[];
+ short n;
+{
+ register short *p,*q=&a[n];
+ XSetFunction(display,gc,b?GXset:GXclear);
+ for (p=a;p<q;p+=2)
+ XFillRectangle(display,xcanvas,gc,*p,r,*(p+1)-*p,1);
+ XSetFunction(display,gc,b?GXclear:GXset);
+ for (p=a+1;p<q;p+=2)
+ XFillRectangle(display,xcanvas,gc,*p,r,*(p+1)-*p,1);
+ notify_dispatch(); /* without this, the Notifier queue overflows */
+}
+
diff --git a/systems/knuth/local/mf/mf_window.h b/systems/knuth/local/mf/mf_window.h
new file mode 100644
index 0000000000..761cf006c9
--- /dev/null
+++ b/systems/knuth/local/mf/mf_window.h
@@ -0,0 +1,15 @@
+{ $Header: mf_window.h,v 0.999999 86/01/13 00:43:56 richards Released $ }
+
+{ External procedures for UNIX MetaFont VIRMF for display graphics }
+
+function initscreen: boolean;
+ external;
+
+procedure updatescreen;
+ external;
+
+procedure blankrectangle(leftcol, rightcol: screencol; toprow, botrow: screenrow);
+ external;
+
+procedure paintrow(r: screenrow; b: pixelcolor; var a: transspec; n: screencol);
+ external;
diff --git a/systems/knuth/local/mf/plain.base b/systems/knuth/local/mf/plain.base
new file mode 100644
index 0000000000..8aff956dc2
--- /dev/null
+++ b/systems/knuth/local/mf/plain.base
Binary files differ
diff --git a/systems/knuth/local/mf/plain.log b/systems/knuth/local/mf/plain.log
new file mode 100644
index 0000000000..41d972df4f
--- /dev/null
+++ b/systems/knuth/local/mf/plain.log
@@ -0,0 +1,16 @@
+This is METAFONT, Version 2.7182 for SunOS (INIMF) 8 AUG 1998 21:30
+**plain \input local \dump
+(/home/tex/dist/lib/plain.mf
+Preloading the plain base, version 2.71: preliminaries,
+ basic constants and mathematical macros,
+ macros for converting from device-independent units to pixels,
+ macros and tables for various modes of operation,
+ macros for drawing and filling,
+ macros for proof labels and rules,
+ macros for character and font administration,
+and a few last-minute items.) (/home/tex/local/lib/local.mf)
+Beginning to dump on file plain.base
+ (preloaded base=plain 98.8.8)
+1407 strings of total length 23104
+5273 memory locations dumped; current usage is 1450&3664
+525 symbolic tokens
diff --git a/systems/knuth/local/mf/trap1.in b/systems/knuth/local/mf/trap1.in
new file mode 100644
index 0000000000..1e7b96cab3
--- /dev/null
+++ b/systems/knuth/local/mf/trap1.in
@@ -0,0 +1,3 @@
+
+\input trap
+
diff --git a/systems/knuth/local/mf/trap2.in b/systems/knuth/local/mf/trap2.in
new file mode 100644
index 0000000000..d3dba8ac65
--- /dev/null
+++ b/systems/knuth/local/mf/trap2.in
@@ -0,0 +1 @@
+ &trap trap
diff --git a/systems/knuth/local/mfpaths.h b/systems/knuth/local/mfpaths.h
new file mode 100644
index 0000000000..a582bd4c0d
--- /dev/null
+++ b/systems/knuth/local/mfpaths.h
@@ -0,0 +1,21 @@
+/*
+ * This file defines the default paths that will be used for MF software.
+ * (These paths are used if the user's environment doesn't specify paths
+ * in MFINPUTS, MFBASES, or MFPOOL.)
+ *
+ * Paths should be colon-separated and no longer than MAXINPATHCHARS-1
+ * (for defaultinputpath) or MAXOTHPATHCHARS (for other default paths).
+ */
+
+#define MAXINPATHCHARS 700 /* maximum number of chars in an input path */
+
+#define defaultinputpath ".:/home/tex/local/lib:/home/tex/dist/cm:/home/tex/dist/lib:/home/tex/local/cm:/home/src/ams:/home/src/mf"
+ /* this should always start with "." */
+
+#define MAXOTHPATHCHARS 100 /* other paths should be much shorter */
+
+#define defaultbasepath ".:/home/tex/local/mf"
+
+#define defaultpoolpath ".:/home/tex/local/mf"
+
+
diff --git a/systems/knuth/local/mfware-sparc/Makefile b/systems/knuth/local/mfware-sparc/Makefile
new file mode 100644
index 0000000000..8560035fde
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/Makefile
@@ -0,0 +1,54 @@
+# Make executable files for this workstation, using the master TeX sources
+
+PFLAGS= -O -I.. -temp=.
+CFLAGS= -O -I..
+PC= time pc
+
+all: gftype gftopk gftodvi mft pktype
+
+%.p: %.ch
+ tangle ../../dist/mfware/$*.web $*.ch
+ mv ../../dist/mfware/$*.p .
+
+pktype.p: pktype.ch
+ tangle pktype.web pktype.ch
+
+%.ch: ../../dist/mfware/%.web
+ touch $*.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+gftype: gftype.o gftopk_ext.h gftopk_ext.o
+ pc ${PFLAGS} -o gftype gftype.o gftopk_ext.o
+
+gftopk: gftopk.o gftopk_ext.o gftopk_ext.h
+ pc ${PFLAGS} -o gftopk gftopk.o gftopk_ext.o
+
+pktype: pktype.o gftopk_ext.h gftopk_ext.o
+ pc ${PFLAGS} -o pktype pktype.o gftopk_ext.o
+
+gftodvi: gftodvi.o ext.o ../tex/ext.h
+ pc ${PFLAGS} -o gftodvi gftodvi.o ext.o
+
+gf2dvi.p: gf2dvi.ch
+ cp ../../dist/mfware/gftodvi.web gf2dvi.web
+ tangle gf2dvi.web gf2dvi.ch
+ rm gf2dvi.web
+
+gf2dvi: gf2dvi.o ext.o ../tex/ext.h
+ pc $(PFLAGS) -o gf2dvi gf2dvi.o ext.o
+
+ext.o: ../tex/ext.c
+ cc ${CFLAGS} -c ../tex/ext.c
+
+gftopk_ext.o: gftopk_ext.c
+
+mft: mft.o weavext.o mft_ext.h mft_ext.o
+ pc ${PFLAGS} -o mft mft.o mft_ext.o
+
+install: # doit make install "PROG=gftype"
+ mv $(PROG) /usr/local/bin/$(PROG)
+
+clean:
+ rm -f *.p *.o *~ ../../dist/mfware/*.pool
diff --git a/systems/knuth/local/mfware-sparc/gf2dvi.ch b/systems/knuth/local/mfware-sparc/gf2dvi.ch
new file mode 100644
index 0000000000..364123df5d
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/gf2dvi.ch
@@ -0,0 +1,836 @@
+% Change file for the GF2DVI processor, for use on Berkeley UNIX systems.
+% [A "color separation" variant of gftodvi, for illustrations in Vols C, E]
+
+% History:
+%
+% Created by don on 92/6/8, based on original SAIL version (circa 1965)
+% I started with gftodvi.ch and edited in changes corresponding to
+% the changes previously installed at SAIL ages ago
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{GF\lowercase{to}DVI changes for Berkeley {\mc UNIX}}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is GFtoDVI, Version 3.0' {printed when the program starts}
+@y
+@d banner=='This is GF2DVI [double separation], Version 3.0 for SunOS'
+ {printed when the program starts}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Use default case statement feature of SunOS Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Add inclusion of ext.h and standard input to program header
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program GF_to_DVI(@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var @!i,@!j,@!m,@!n:integer; {loop indices for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@y
+@p program GF_to_DVI(@!input,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\@=#include "../tex/ext.h"@>@\ {declarations for external C procedures}
+procedure initialize; {this procedure gets things started properly}
+ var @!i,@!j,@!m,@!n:integer; {loop indices for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ first_input:=true;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] Distinguish two brands of finality
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d final_end=9999 {label for the end of it all}
+
+@<Labels...@>=final_end;
+@y
+@d final_final_end=9999 {label for the end of it all}
+@d final_end=9998 {lable for the end of a pass}
+
+@<Labels...@>=final_end,final_final_end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [5] Enlarge file_name_size to 1024
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Constants...@>=
+@!max_labels=2000; {maximum number of labels and dots and rules per character}
+@!pool_size=10000; {maximum total length of labels and other strings}
+@!max_strings=1100; {maximum number of labels and other strings}
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!file_name_size=50; {a file name shouldn't be longer than this}
+@!font_mem_size=2000; {space for font metric data}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!widest_row=8192; {maximum number of pixels per row}
+@!lig_lookahead=20; {size of stack used when inserting ligature characters}
+@y
+@<Constants...@>=
+@!max_labels=2000; {maximum number of labels and dots per character}
+@!pool_size=10000; {maximum total length of labels and other strings}
+@!max_strings=1100; {maximum number of labels and other strings}
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!file_name_size=1024; {a file name shouldn't be longer than this}
+@!font_mem_size=2000; {space for font metric data}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!widest_row=8192; {maximum number of pixels per row}
+@!lig_lookahead=20; {size of stack used when inserting ligature characters}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8] Add <nl> to end of abort() message; jump way out
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d abort(#)==@+begin print(' ',#); jump_out;@+end
+@y
+@d abort(#)==@+begin print_ln(' ',#); jump_out;@+end
+@z
+@x
+begin goto final_end;
+@y
+begin goto final_final_end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was15,now16] change update_terminal to flush(), change def'n of term_in
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Since the terminal is being used for both input and output, some systems
+need a special routine to make sure that the user can see a prompt message
+before waiting for input based on that message. (Otherwise the message
+may just be sitting in a hidden buffer somewhere, and the user will have
+no idea what the program is waiting for.) We shall call a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+
+@d update_terminal == break(output) {empty the terminal output buffer}
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of 0..255;
+@!term_in:text_file; {the terminal, considered as an input file}
+@y
+Since the terminal is being used for both input and output, some systems
+need a special routine to make sure that the user can see a prompt message
+before waiting for input based on that message. (Otherwise the message
+may just be sitting in a hidden buffer somewhere, and the user will have
+no idea what the program is waiting for.) We shall call a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+@^system dependencies@>
+
+@d update_terminal == flush(output) {empty the terminal output buffer}
+@d term_in == input {standard input}
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of 0..255;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was16,now17] Remove reset(term_in) from input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ A global variable |line_length| records the first buffer position after
+the line just read.
+@^system dependencies@>
+
+@p procedure input_ln; {inputs a line from the terminal}
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+line_length:=0;
+while (line_length<terminal_line_length)and not eoln(term_in) do
+ begin buffer[line_length]:=xord[term_in^]; incr(line_length); get(term_in);
+ end;
+end;
+
+@y
+@ A global variable |line_length| records the first buffer position after
+the line just read. Another one, |first_input|, tells if such a line exists.
+@^system dependencies@>
+
+@p procedure input_ln; {inputs a line from the terminal}
+begin update_terminal;
+if first_input then first_input:=false
+else if eoln(term_in) then read_ln(term_in);
+line_length:=0;
+while (line_length<terminal_line_length)and not eoln(term_in) do
+ begin buffer[line_length]:=xord[term_in^]; incr(line_length); get(term_in);
+ end;
+end;
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [45] Change type of binary file for binary (byte) files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types ...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+For Berkeley Pascal, we need to use `|packed file of -128..127|' to read and
+write binary bytes.
+
+@<Types ...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@!packed_ASCII_code=-128..127; {declaration needed only because of \.{ext.h}}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [47] Modify file open routines to match binary I/O library
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+variable that specifies the file name.
+@^system dependencies@>
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin reset(gf_file,name_of_file);
+cur_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin reset(tfm_file,name_of_file);
+end;
+@#
+procedure open_dvi_file; {prepares to write packed bytes in |dvi_file|}
+begin rewrite(dvi_file,name_of_file);
+end;
+@y
+variable that specifies the file name.
+
+An external C procedure, |test_access| is used to check whether or not the
+open will work. It is declared in the \.{ext.h} include file, and it returns
+|true| or |false|. The |name_of_file| global holds the file name whose access
+is to be tested.
+The first parameter for |test_access| is the access mode,
+one of |read_access_mode| or |write_access_mode|.
+
+We also implement path searching in |test_access|: its second parameter is
+one of the ``file path'' constants defined below. If |name_of_file|
+doesn't start with |'/'| then |test_access| tries prepending pathnames
+from the appropriate path list until success or the end of path list
+is reached.
+On return, |real_name_of_file| contains the original name with the path
+that succeeded (if any) prepended. It is the name used in the various
+open procedures.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {do no path searching}
+@d font_file_path=3 {path specifier for \.{TFM} files}
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+var@!i: 1..file_name_size;
+begin if test_access(read_access_mode,no_file_path) then
+ begin reset(gf_file,real_name_of_file); cur_loc:=0;
+ end
+else begin i:=1;
+ while (real_name_of_file[i] <> chr(0)) do@+incr(i);
+ abort('Can''t open GF file ',real_name_of_file:i);
+ end;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+var@!i: 1..file_name_size;
+begin if test_access(read_access_mode,font_file_path) then
+ begin reset(tfm_file,real_name_of_file); cur_loc:=0;
+ temp_int:=tfm_file^; {prime the pump}
+ if temp_int<0 then temp_int:=temp_int+256;
+ end
+else begin i:=1;
+ while (real_name_of_file[i] <> chr(0)) do@+incr(i);
+ abort('Can''t open TFM file ',real_name_of_file:i);
+ end;
+end;
+@#
+procedure open_dvi_file; {prepares to write packed bytes in |dvi_file|}
+var@!i: 1..file_name_size;
+begin if test_access(write_access_mode,no_file_path) then
+ rewrite(dvi_file,real_name_of_file)
+else begin i:=1;
+ while (real_name_of_file[i] <> chr(0)) do@+incr(i);
+ abort('Can''t write on DVI file ',real_name_of_file:i);
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [48] Declare real_name_of_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+be read next, and the string |name_of_file| will be set to the current
+file name before the file-opening procedures are called.
+
+@<Glob...@>=
+@!cur_loc:integer; {current byte number in |gf_file|}
+@!name_of_file:packed array[1..file_name_size] of char; {external file name}
+@y
+be read next, and the string |name_of_file| will be set to the current
+file name before the file-opening procedures are called.
+Under UNIX, we also have a |real_name_of_file| string, that gets
+set by the external |test_access| procedure after path searching.
+
+@<Glob...@>=
+@!cur_loc:integer; {current byte number in |gf_file|}
+@!name_of_file,@!real_name_of_file,@!gf_file_name:
+ packed array[1..file_name_size] of char; {external name}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [50] Use modified routines to access tfm_file using b_read_unsigned()
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure read_tfm_word;
+begin read(tfm_file,b0); read(tfm_file,b1);
+read(tfm_file,b2); read(tfm_file,b3);
+end;
+@y
+For Berkeley {\mc UNIX} we had to make the |byte_file| type have data in the
+range |-128..127|.
+If the file byte contains |@'377|, we want to regard this as 255 in decimal.
+However if we do |i:=tfm_file^| on that byte, where i is a signed integer,
+then the result is |i=-1|.
+File bytes with values |<@'200| are read properly.
+The solution is to do the assignment as shown, and then add 256 to values
+less than 0.
+We could also assign to |eight_bits| values, but this causes an error if
+runtime checking is turned on.
+The integer |temp_int| will be added to global variables at the end,
+where it won't disturb module numbering.
+
+@d fget==begin get(tfm_file);
+ temp_int:=tfm_file^;
+ if temp_int<0 then temp_int:=temp_int+256;
+ end
+
+@p procedure read_tfm_word;
+begin b0:=temp_int; fget; b1:=temp_int;
+fget; b2:=temp_int; fget; b3:=temp_int; fget;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [51] Use modified routines to access gf_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ We shall use another set of simple functions to read the next byte or
+bytes from |gf_file|. There are four possibilities, each of which is
+treated as a separate function in order to minimize the overhead for
+subroutine calls.
+@^system dependencies@>
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(gf_file,a); read(gf_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+@y
+@ We shall use another set of simple functions to read the next byte or
+bytes from |gf_file|. There are four possibilities, each of which is
+treated as a separate function in order to minimize the overhead for
+subroutine calls.
+@^system dependencies@>
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:-128..127;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc);
+ if b < 0 then get_byte := b + 256 else get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:-128..127;
+begin read(gf_file,b); incr(cur_loc);
+signed_byte:=b;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,b:integer;
+begin a := get_byte; b := get_byte;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:integer;
+begin a := get_byte; b := get_byte; c := get_byte;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:integer;
+begin a := signed_byte; b := get_byte; c := get_byte; d := get_byte;
+signed_quad:=((a*256+b)*256+c)*256+d
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [52] change definition of min/max_quarterword
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+the more general memory words of \TeX. On some machines it is necessary to
+define |min_quarterword=-128| and |max_quarterword=127| in order to pack
+four quarterwords into a single word.)
+@^system dependencies@>
+
+@d min_quarterword=0 {change this to allow efficient packing, if necessary}
+@d max_quarterword=255 {ditto}
+@y
+the more general memory words of \TeX.
+With Berkeley {\mc UNIX} Pascal, we need to use the |-128..127| range to pack
+an integer subrange into a byte.
+@^system dependencies@>
+
+@d min_quarterword=-128 {change this to allow efficient packing, if necessary}
+@d max_quarterword=127 {ditto}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [78] change default extension to ".2602gf"
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+l:=3; init_str3(".")("g")("f")(gf_ext);@/
+@y
+l:=7; init_str7(".")("2")("6")("0")("2")("g")("f")(gf_ext);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [78] change default gray font to "black"
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+l:=4; init_str4("g")("r")("a")("y")(default_gray_font);@/
+@y
+l:=5; init_str5("b")("l")("a")("c")("k")(default_gray_font);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [87] change ">" and ":" to "/" in file name scanning
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+@y
+@ The file names we shall deal with for SunOS have the
+following structure: If the name contains `\./', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\./', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [88] change home_font_area to null_string (open_tfm provides path)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|home_font_area|. This system area name will, of course, vary from place
+to place. The program here sets it to `\.{TeXfonts:}'.
+@^system dependencies@>
+@.TeXfonts@>
+
+@<Initialize the strings@>=
+l:=9; init_str9("T")("e")("X")("f")("o")("n")("t")("s")(":")(home_font_area);@/
+@y
+@ Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|home_font_area|. This system area name will, of course, vary from place
+to place. Under the Berkeley {\mc UNIX} version, we set |home_font_area|
+to |null_string| because the default areas to search for \.{TFM} files
+are built into the routine |test_access|.
+@^system dependencies@>
+
+@<Initialize the strings@>=
+l:=0; init_str0(home_font_area);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [90] change more_name to understand UNIX file name paths
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin if (c=">")or(c=":") then
+ begin area_delimiter:=pool_ptr; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
+ str_room(1); append_char(c); {contribute |c| to the current string}
+ more_name:=true;
+ end;
+end;
+@y
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin if (c="/") then
+ begin area_delimiter:=pool_ptr; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
+ str_room(1); append_char(c); {contribute |c| to the current string}
+ more_name:=true;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [94] change start_gf to get file name from command line arguments
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The |start_gf| procedure prompts the user for the name of the generic
+font file to be input. It opens the file, making sure that some input is
+present; then it opens the output file.
+
+Although this routine is system-independent, it should probably be
+modified to take the file name from the command line (without an initial
+prompt), on systems that permit such things.
+
+@p procedure start_gf;
+label found,done;
+begin loop@+begin print_nl('GF file name: '); input_ln;
+@.GF file name@>
+ buf_ptr:=0; buffer[line_length]:="?";
+ while buffer[buf_ptr]=" " do incr(buf_ptr);
+ if buf_ptr<line_length then
+ begin @<Scan the file name in the buffer@>;
+ if cur_ext=null_string then cur_ext:=gf_ext;
+ pack_file_name(cur_name,cur_area,cur_ext); open_gf_file;
+ if not eof(gf_file) then goto found;
+ print_nl('Oops... I can''t find file '); print(name_of_file);
+@.Oops...@>
+@.I can't find...@>
+ end;
+ end;
+found:job_name:=cur_name; pack_file_name(job_name,null_string,dvi_ext);
+open_dvi_file;
+end;
+@y
+@ The |start_gf| procedure obtains the name of the generic font file to
+be input from the command line.
+It opens the file, making sure that some input is
+present; then it opens the output file.
+
+@p procedure start_gf;
+label done;
+var
+ arg_buffer: packed array [1..file_name_size] of char;
+ arg_buf_ptr: 1..file_name_size;
+begin if (argc > 2) then abort('Usage: gftodvi [GF-file]');
+if argc = 1 then begin
+ print_nl('GF file name: '); input_ln;
+@.GF file name@>
+ end
+else begin
+ argv(1, arg_buffer);
+ arg_buf_ptr := 1;
+ line_length := 0;
+ while (arg_buf_ptr < file_name_size)
+ and (arg_buffer[arg_buf_ptr] = ' ') do
+ incr(arg_buf_ptr);
+ while (arg_buf_ptr < file_name_size)
+ and (line_length < terminal_line_length)
+ and (arg_buffer[arg_buf_ptr] <> ' ') do
+ begin
+ buffer[line_length] := xord[arg_buffer[arg_buf_ptr]];
+ incr(line_length);
+ incr(arg_buf_ptr);
+ end;
+end;
+ buf_ptr:=0; buffer[line_length]:="?";
+ while buffer[buf_ptr]=" " do incr(buf_ptr);
+ if buf_ptr<line_length then
+ begin @<Scan the file name in the buffer@>;
+ if cur_ext=null_string then cur_ext:=gf_ext;
+ pack_file_name(cur_name,cur_area,cur_ext);
+ gf_file_name:=name_of_file;
+ open_gf_file;
+ end;
+job_name:=cur_name; pack_file_name(job_name,null_string,dvi_ext);
+open_dvi_file;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [98] load_fonts:
+% KLUDGE: SUN Pascal compiler incorrectly aligns four_quarters data
+% item on stack, which causes long reference to odd address.
+% FIX: rearrange four_quarter data to follow aligned long (int) data.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Declare the procedure called |load_fonts|@>=
+procedure load_fonts;
+label done,continue,found,not_found;
+var @!f:internal_font_number;
+@!i:four_quarters; {font information word}
+@!j,@!k,@!v:integer; {registers for initializing font tables}
+@!m:title_font..slant_font+area_code; {keyword found}
+@!n1:0..longest_keyword; {buffered character being checked}
+@y
+@<Declare the procedure called |load_fonts|@>=
+procedure load_fonts;
+label done,continue,found,not_found;
+var @!f:internal_font_number;
+@!j,@!k,@!v:integer; {registers for initializing font tables}
+@!i:four_quarters; {font information word}
+@!m:title_font..slant_font+area_code; {keyword found}
+@!n1:0..longest_keyword; {buffered character being checked}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [107] write_dvi is now an external C routine
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure write_dvi(@!a,@!b:dvi_index);
+var k:dvi_index;
+begin for k:=a to b do write(dvi_file,dvi_buf[k]);
+end;
+@y
+For Berkeley {\mc UNIX}, this is going to be handled by an external procedure,
+which will do the output using |fwrite|.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [115] double the page count in the DVI file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+dvi_out(total_pages div 256); dvi_out(total_pages mod 256);@/
+@y
+total_pages:=total_pages+total_pages;
+dvi_out(total_pages div 256); dvi_out(total_pages mod 256);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [154] avoid spurious error message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ else print_nl('(Tardy font change will be ignored (byte ',
+@y
+ else if pass=1 then print_nl('(Tardy font change will be ignored (byte ',
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [164] do the color separation
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Output all rules for the current character@>;
+@<Output all labels for the current character@>;
+do_pixels;
+@y
+cross(-655360,-655360);
+cross(-655360,page_height+655360);
+cross(page_width+655360,-655360);
+cross(page_width+655360,page_height+655360);
+if pass=1 then
+ begin @<Output all rules for the current character@>;
+ @<Output all labels for the current character@>;
+ skip_pixels;
+ end
+else do_pixels;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [172] supply a secondary page number
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for k:=3 to 9 do dvi_four(0);
+@y
+dvi_four(pass); for k:=4 to 9 do dvi_four(0);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was188,now191] Avoid `oct', a reserved word in SunOS Pascal(!)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d octant==xr {octant code for nearest dot, plus 8 for coincident dots}
+@y
+@d octant==xr {octant code for nearest dot, plus 8 for coincident dots}
+@d oct==octnt {changes variable name since |oct| is reserved word in SunOS}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was216,now219] call set_paths before gf_start to initialize paths
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p begin initialize; {get all variables initialized}
+@<Initialize the strings@>;
+start_gf; {open the input and output files}
+@<Process the preamble@>;
+cur_gf:=get_byte; init_str_ptr:=str_ptr;
+loop@+ begin @<Initialize variables for the next character@>;
+ while (cur_gf>=xxx1)and(cur_gf<=no_op) do @<Process a no-op command@>;
+ if cur_gf=post then @<Finish the \.{DVI} file and |goto final_end|@>;
+@y
+@p @<Last-minute procedures@>@;
+begin initialize; {get all variables initialized}
+@<Initialize the strings@>;
+set_paths; {initialize paths for \.{TFM} files from environment if needed}
+for pass:=1 to 2 do
+ begin if pass=1 then
+ begin start_gf; {open the input and output files}
+ @<Process the preamble@>;
+ init_str_ptr:=str_ptr;
+ end
+ else begin total_pages:=0; name_of_file:=gf_file_name; open_gf_file;
+ q:=get_byte; q:=get_byte; k:=get_byte; for m:=1 to k do q:=get_byte;
+ end;
+cur_gf:=get_byte;
+loop@+ begin @<Initialize variables for the next character@>;
+ while (cur_gf>=xxx1)and(cur_gf<=no_op) do @<Process a no-op command@>;
+ if cur_gf=post then
+ if pass=1 then goto final_end
+ else @<Finish the \.{DVI} file and |goto final_end|@>;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was216,now219] finish normal end with <nl> on terminal; add another `end'
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ cur_gf:=get_byte; str_ptr:=init_str_ptr; pool_ptr:=str_start[str_ptr];
+ end;
+final_end:end.
+@y
+ cur_gf:=get_byte; str_ptr:=init_str_ptr; pool_ptr:=str_start[str_ptr];
+ end;
+final_end:end;
+final_final_end:print_ln(' ');
+end.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was219,now222] declare special globals and procedures
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtoDVI} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtoDVI} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@<Glob...@>=
+@!pass:1..2; {are we doing the first pass (rules) or the second (pixels)?}
+@!temp_int:integer; {used for TFM input}
+@!last,@!first:0..terminal_line_length; {needed in \.{ext.c}}
+@!interrupt:integer; {ditto}
+@!first_input:boolean; {true before the first call to |input_ln|}
+
+@ @<Types in...@>=
+@!word_file=file of memory_word; {needed in \.{ext.c}}
+
+@ Finally, here is a subroutine that draws cross-reference marks.
+@<Last-minute...@>=
+procedure cross(@!x,@!y:scaled);
+begin dvi_goto(x-655360,y+13107);
+dvi_out(put_rule);
+dvi_four(26214); dvi_four(1310720);
+dvi_out(pop);@/
+dvi_goto(x-13107,y+655360);
+dvi_out(put_rule);
+dvi_four(1310720); dvi_four(26214);
+dvi_out(pop);
+end;
+
+@ And still more finally, a subroutine that skips over
+the meaty part of a ``character'' in the \.{GF} file.
+@<Last-min...@>=
+procedure skip_pixels;
+label done;
+var @!k:integer; {garbage}
+begin while cur_gf<>eoc do
+ begin case cur_gf of
+ paint1,skip1:k:=get_byte;
+ paint2,skip2:k:=get_two_bytes;
+ paint3,skip3:k:=get_three_bytes;
+ xxx1,xxx2,xxx3,xxx4,yyy,no_op: begin skip_nop; goto done; end;
+ othercases do_nothing
+ endcases;@/
+ cur_gf:=get_byte;
+done: end;
+end;
+@z
diff --git a/systems/knuth/local/mfware-sparc/gftodvi.ch b/systems/knuth/local/mfware-sparc/gftodvi.ch
new file mode 100644
index 0000000000..29a85a5e12
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/gftodvi.ch
@@ -0,0 +1,757 @@
+% Change file for the GFtoDVI processor, for use on Berkeley UNIX systems.
+
+% History:
+%
+% Revised by don on 91/1/5, default extension is now .2602gf
+%
+% Revised by don on 89/10/16 for version 3.0 (extended ligatures)
+%
+% Revised by don on 89/08/14 for version 2.0 and SunOS
+% with simplications based on initex.ch
+%
+% Revision 1.7.1.5 86/02/01 15:29:58 richards
+% Released again for MF 1.0 package
+%
+% Revision 1.7.1.4 86/02/01 15:06:50 richards
+% Added:
+% <nl> at end of successful run
+%
+% Revision 1.7.1.3 86/01/27 16:39:48 richards
+% Fixed:
+% syntax error in previous edits
+%
+% Revision 1.7.1.2 86/01/27 15:55:58 richards
+% Added:
+% dvi_buf_type declaration and redefined dvi_buf[] in
+% terms of it, so we can use it as a parameter to b_write_buf()
+%
+% Revision 1.7.1.1 86/01/27 15:39:10 richards
+% First edit to use new binary I/O routines
+%
+% Revision 1.7 85/10/21 21:55:50 richards
+% Released for GFtoDVI 1.7
+%
+% Revision 1.3.7.1 85/10/18 22:59:01 richards
+% Updated for GFtoDVI Version 1.7 (Distributed w/ MF84 Version 0.9999)
+%
+% Revision 1.3.5.1 85/10/09 17:02:35 richards
+% First draft to run at 1.5 level
+%
+% Revision 1.3 85/05/27 21:15:30 richards
+% Updated for GFtoDVI Version 1.3 (Distributed w/ MF84 Version 0.91)
+%
+% Revision 1.2 85/04/25 19:33:30 richards
+% Updated to GFtoDVI Version 1.2 (Distributed w/ MF84 Version 0.81)
+%
+% Revision 1.1 85/03/03 21:47:17 richards
+% Updated for GF utilities distributed with MF Version 0.77
+%
+% Revision 1.0 84/12/16 22:38:22 richards
+% Updated for GFtoDVI Version 1.0 (New GF file format)
+%
+% Revision 0.6 84/12/05 13:32:01 richards
+% Updated for GFtoDVI Version 0.6; merged in changes from sdcarl!rusty
+%
+% Note: still has BUGFIX in section 199 to keep GFtoDVI from trying
+% to use non-existant characters in a gray font
+%
+% Revision 0.3 84/11/17 23:51:56 richards
+% Base version for GFtoDVI Version 0.3
+%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{GF\lowercase{to}DVI changes for Berkeley {\mc UNIX}}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is GFtoDVI, Version 3.0' {printed when the program starts}
+@y
+@d banner=='This is GFtoDVI, Version 3.0 for SunOS'
+ {printed when the program starts}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Use default case statement feature of SunOS Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Add inclusion of ext.h and standard input to program header
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program GF_to_DVI(@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var @!i,@!j,@!m,@!n:integer; {loop indices for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@y
+@p program GF_to_DVI(@!input,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\@=#include "../tex/ext.h"@>@\ {declarations for external C procedures}
+procedure initialize; {this procedure gets things started properly}
+ var @!i,@!j,@!m,@!n:integer; {loop indices for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ first_input:=true;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [5] Enlarge file_name_size to 1024
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Constants...@>=
+@!max_labels=2000; {maximum number of labels and dots and rules per character}
+@!pool_size=10000; {maximum total length of labels and other strings}
+@!max_strings=1100; {maximum number of labels and other strings}
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!file_name_size=50; {a file name shouldn't be longer than this}
+@!font_mem_size=2000; {space for font metric data}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!widest_row=8192; {maximum number of pixels per row}
+@!lig_lookahead=20; {size of stack used when inserting ligature characters}
+@y
+@<Constants...@>=
+@!max_labels=2000; {maximum number of labels and dots per character}
+@!pool_size=10000; {maximum total length of labels and other strings}
+@!max_strings=1100; {maximum number of labels and other strings}
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!file_name_size=1024; {a file name shouldn't be longer than this}
+@!font_mem_size=2000; {space for font metric data}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!widest_row=8192; {maximum number of pixels per row}
+@!lig_lookahead=20; {size of stack used when inserting ligature characters}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8] Add <nl> to end of abort() message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d abort(#)==@+begin print(' ',#); jump_out;@+end
+@y
+@d abort(#)==@+begin print_ln(' ',#); jump_out;@+end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was15,now16] change update_terminal to flush(), change def'n of term_in
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Since the terminal is being used for both input and output, some systems
+need a special routine to make sure that the user can see a prompt message
+before waiting for input based on that message. (Otherwise the message
+may just be sitting in a hidden buffer somewhere, and the user will have
+no idea what the program is waiting for.) We shall call a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+
+@d update_terminal == break(output) {empty the terminal output buffer}
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of 0..255;
+@!term_in:text_file; {the terminal, considered as an input file}
+@y
+Since the terminal is being used for both input and output, some systems
+need a special routine to make sure that the user can see a prompt message
+before waiting for input based on that message. (Otherwise the message
+may just be sitting in a hidden buffer somewhere, and the user will have
+no idea what the program is waiting for.) We shall call a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+@^system dependencies@>
+
+@d update_terminal == flush(output) {empty the terminal output buffer}
+@d term_in == input {standard input}
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of 0..255;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was16,now17] Remove reset(term_in) from input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ A global variable |line_length| records the first buffer position after
+the line just read.
+@^system dependencies@>
+
+@p procedure input_ln; {inputs a line from the terminal}
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+line_length:=0;
+while (line_length<terminal_line_length)and not eoln(term_in) do
+ begin buffer[line_length]:=xord[term_in^]; incr(line_length); get(term_in);
+ end;
+end;
+
+@y
+@ A global variable |line_length| records the first buffer position after
+the line just read. Another one, |first_input|, tells if such a line exists.
+@^system dependencies@>
+
+@p procedure input_ln; {inputs a line from the terminal}
+begin update_terminal;
+if first_input then first_input:=false
+else if eoln(term_in) then read_ln(term_in);
+line_length:=0;
+while (line_length<terminal_line_length)and not eoln(term_in) do
+ begin buffer[line_length]:=xord[term_in^]; incr(line_length); get(term_in);
+ end;
+end;
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [45] Change type of binary file for binary (byte) files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types ...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+For Berkeley Pascal, we need to use `|packed file of -128..127|' to read and
+write binary bytes.
+
+@<Types ...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@!packed_ASCII_code=-128..127; {declaration needed only because of \.{ext.h}}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [47] Modify file open routines to match binary I/O library
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+variable that specifies the file name.
+@^system dependencies@>
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin reset(gf_file,name_of_file);
+cur_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin reset(tfm_file,name_of_file);
+end;
+@#
+procedure open_dvi_file; {prepares to write packed bytes in |dvi_file|}
+begin rewrite(dvi_file,name_of_file);
+end;
+@y
+variable that specifies the file name.
+
+An external C procedure, |test_access| is used to check whether or not the
+open will work. It is declared in the \.{ext.h} include file, and it returns
+|true| or |false|. The |name_of_file| global holds the file name whose access
+is to be tested.
+The first parameter for |test_access| is the access mode,
+one of |read_access_mode| or |write_access_mode|.
+
+We also implement path searching in |test_access|: its second parameter is
+one of the ``file path'' constants defined below. If |name_of_file|
+doesn't start with |'/'| then |test_access| tries prepending pathnames
+from the appropriate path list until success or the end of path list
+is reached.
+On return, |real_name_of_file| contains the original name with the path
+that succeeded (if any) prepended. It is the name used in the various
+open procedures.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {do no path searching}
+@d font_file_path=3 {path specifier for \.{TFM} files}
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+var@!i: 1..file_name_size;
+begin if test_access(read_access_mode,no_file_path) then
+ begin reset(gf_file,real_name_of_file); cur_loc:=0;
+ end
+else begin i:=1;
+ while (real_name_of_file[i] <> chr(0)) do@+incr(i);
+ abort('Can''t open GF file ',real_name_of_file:i);
+ end;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+var@!i: 1..file_name_size;
+begin if test_access(read_access_mode,font_file_path) then
+ begin reset(tfm_file,real_name_of_file); cur_loc:=0;
+ temp_int:=tfm_file^; {prime the pump}
+ if temp_int<0 then temp_int:=temp_int+256;
+ end
+else begin i:=1;
+ while (real_name_of_file[i] <> chr(0)) do@+incr(i);
+ abort('Can''t open TFM file ',real_name_of_file:i);
+ end;
+end;
+@#
+procedure open_dvi_file; {prepares to write packed bytes in |dvi_file|}
+var@!i: 1..file_name_size;
+begin if test_access(write_access_mode,no_file_path) then
+ rewrite(dvi_file,real_name_of_file)
+else begin i:=1;
+ while (real_name_of_file[i] <> chr(0)) do@+incr(i);
+ abort('Can''t write on DVI file ',real_name_of_file:i);
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [48] Declare real_name_of_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+be read next, and the string |name_of_file| will be set to the current
+file name before the file-opening procedures are called.
+
+@<Glob...@>=
+@!cur_loc:integer; {current byte number in |gf_file|}
+@!name_of_file:packed array[1..file_name_size] of char; {external file name}
+@y
+be read next, and the string |name_of_file| will be set to the current
+file name before the file-opening procedures are called.
+Under UNIX, we also have a |real_name_of_file| string, that gets
+set by the external |test_access| procedure after path searching.
+
+@<Glob...@>=
+@!cur_loc:integer; {current byte number in |gf_file|}
+@!name_of_file,@!real_name_of_file:packed array[1..file_name_size] of char;
+ {external name}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [50] Use modified routines to access tfm_file using b_read_unsigned()
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure read_tfm_word;
+begin read(tfm_file,b0); read(tfm_file,b1);
+read(tfm_file,b2); read(tfm_file,b3);
+end;
+@y
+For Berkeley {\mc UNIX} we had to make the |byte_file| type have data in the
+range |-128..127|.
+If the file byte contains |@'377|, we want to regard this as 255 in decimal.
+However if we do |i:=tfm_file^| on that byte, where i is a signed integer,
+then the result is |i=-1|.
+File bytes with values |<@'200| are read properly.
+The solution is to do the assignment as shown, and then add 256 to values
+less than 0.
+We could also assign to |eight_bits| values, but this causes an error if
+runtime checking is turned on.
+The integer |temp_int| will be added to global variables at the end,
+where it won't disturb module numbering.
+
+@d fget==begin get(tfm_file);
+ temp_int:=tfm_file^;
+ if temp_int<0 then temp_int:=temp_int+256;
+ end
+
+@p procedure read_tfm_word;
+begin b0:=temp_int; fget; b1:=temp_int;
+fget; b2:=temp_int; fget; b3:=temp_int; fget;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [51] Use modified routines to access gf_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ We shall use another set of simple functions to read the next byte or
+bytes from |gf_file|. There are four possibilities, each of which is
+treated as a separate function in order to minimize the overhead for
+subroutine calls.
+@^system dependencies@>
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(gf_file,a); read(gf_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+@y
+@ We shall use another set of simple functions to read the next byte or
+bytes from |gf_file|. There are four possibilities, each of which is
+treated as a separate function in order to minimize the overhead for
+subroutine calls.
+@^system dependencies@>
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:-128..127;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc);
+ if b < 0 then get_byte := b + 256 else get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:-128..127;
+begin read(gf_file,b); incr(cur_loc);
+signed_byte:=b;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,b:integer;
+begin a := get_byte; b := get_byte;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:integer;
+begin a := get_byte; b := get_byte; c := get_byte;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:integer;
+begin a := signed_byte; b := get_byte; c := get_byte; d := get_byte;
+signed_quad:=((a*256+b)*256+c)*256+d
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [52] change definition of min/max_quarterword
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+the more general memory words of \TeX. On some machines it is necessary to
+define |min_quarterword=-128| and |max_quarterword=127| in order to pack
+four quarterwords into a single word.)
+@^system dependencies@>
+
+@d min_quarterword=0 {change this to allow efficient packing, if necessary}
+@d max_quarterword=255 {ditto}
+@y
+the more general memory words of \TeX.
+With Berkeley {\mc UNIX} Pascal, we need to use the |-128..127| range to pack
+an integer subrange into a byte.
+@^system dependencies@>
+
+@d min_quarterword=-128 {change this to allow efficient packing, if necessary}
+@d max_quarterword=127 {ditto}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [78] change default extension to ".2602gf"
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+l:=3; init_str3(".")("g")("f")(gf_ext);@/
+@y
+l:=7; init_str7(".")("2")("6")("0")("2")("g")("f")(gf_ext);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [87] change ">" and ":" to "/" in file name scanning
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+@y
+@ The file names we shall deal with for SunOS have the
+following structure: If the name contains `\./', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\./', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [88] change home_font_area to null_string (open_tfm provides path)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|home_font_area|. This system area name will, of course, vary from place
+to place. The program here sets it to `\.{TeXfonts:}'.
+@^system dependencies@>
+@.TeXfonts@>
+
+@<Initialize the strings@>=
+l:=9; init_str9("T")("e")("X")("f")("o")("n")("t")("s")(":")(home_font_area);@/
+@y
+@ Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|home_font_area|. This system area name will, of course, vary from place
+to place. Under the Berkeley {\mc UNIX} version, we set |home_font_area|
+to |null_string| because the default areas to search for \.{TFM} files
+are built into the routine |test_access|.
+@^system dependencies@>
+
+@<Initialize the strings@>=
+l:=0; init_str0(home_font_area);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [90] change more_name to understand UNIX file name paths
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin if (c=">")or(c=":") then
+ begin area_delimiter:=pool_ptr; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
+ str_room(1); append_char(c); {contribute |c| to the current string}
+ more_name:=true;
+ end;
+end;
+@y
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin if (c="/") then
+ begin area_delimiter:=pool_ptr; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
+ str_room(1); append_char(c); {contribute |c| to the current string}
+ more_name:=true;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [94] change start_gf to get file name from command line arguments
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The |start_gf| procedure prompts the user for the name of the generic
+font file to be input. It opens the file, making sure that some input is
+present; then it opens the output file.
+
+Although this routine is system-independent, it should probably be
+modified to take the file name from the command line (without an initial
+prompt), on systems that permit such things.
+
+@p procedure start_gf;
+label found,done;
+begin loop@+begin print_nl('GF file name: '); input_ln;
+@.GF file name@>
+ buf_ptr:=0; buffer[line_length]:="?";
+ while buffer[buf_ptr]=" " do incr(buf_ptr);
+ if buf_ptr<line_length then
+ begin @<Scan the file name in the buffer@>;
+ if cur_ext=null_string then cur_ext:=gf_ext;
+ pack_file_name(cur_name,cur_area,cur_ext); open_gf_file;
+ if not eof(gf_file) then goto found;
+ print_nl('Oops... I can''t find file '); print(name_of_file);
+@.Oops...@>
+@.I can't find...@>
+ end;
+ end;
+found:job_name:=cur_name; pack_file_name(job_name,null_string,dvi_ext);
+open_dvi_file;
+end;
+@y
+@ The |start_gf| procedure obtains the name of the generic font file to
+be input from the command line.
+It opens the file, making sure that some input is
+present; then it opens the output file.
+
+@p procedure start_gf;
+label done;
+var
+ arg_buffer: packed array [1..file_name_size] of char;
+ arg_buf_ptr: 1..file_name_size;
+begin if (argc > 2) then abort('Usage: gftodvi [GF-file]');
+if argc = 1 then begin
+ print_nl('GF file name: '); input_ln;
+@.GF file name@>
+ end
+else begin
+ argv(1, arg_buffer);
+ arg_buf_ptr := 1;
+ line_length := 0;
+ while (arg_buf_ptr < file_name_size)
+ and (arg_buffer[arg_buf_ptr] = ' ') do
+ incr(arg_buf_ptr);
+ while (arg_buf_ptr < file_name_size)
+ and (line_length < terminal_line_length)
+ and (arg_buffer[arg_buf_ptr] <> ' ') do
+ begin
+ buffer[line_length] := xord[arg_buffer[arg_buf_ptr]];
+ incr(line_length);
+ incr(arg_buf_ptr);
+ end;
+end;
+ buf_ptr:=0; buffer[line_length]:="?";
+ while buffer[buf_ptr]=" " do incr(buf_ptr);
+ if buf_ptr<line_length then
+ begin @<Scan the file name in the buffer@>;
+ if cur_ext=null_string then cur_ext:=gf_ext;
+ pack_file_name(cur_name,cur_area,cur_ext); open_gf_file;
+ end;
+job_name:=cur_name; pack_file_name(job_name,null_string,dvi_ext);
+open_dvi_file;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [98] load_fonts:
+% KLUDGE: SUN Pascal compiler incorrectly aligns four_quarters data
+% item on stack, which causes long reference to odd address.
+% FIX: rearrange four_quarter data to follow aligned long (int) data.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Declare the procedure called |load_fonts|@>=
+procedure load_fonts;
+label done,continue,found,not_found;
+var @!f:internal_font_number;
+@!i:four_quarters; {font information word}
+@!j,@!k,@!v:integer; {registers for initializing font tables}
+@!m:title_font..slant_font+area_code; {keyword found}
+@!n1:0..longest_keyword; {buffered character being checked}
+@y
+@<Declare the procedure called |load_fonts|@>=
+procedure load_fonts;
+label done,continue,found,not_found;
+var @!f:internal_font_number;
+@!j,@!k,@!v:integer; {registers for initializing font tables}
+@!i:four_quarters; {font information word}
+@!m:title_font..slant_font+area_code; {keyword found}
+@!n1:0..longest_keyword; {buffered character being checked}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [107] write_dvi is now an external C routine
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure write_dvi(@!a,@!b:dvi_index);
+var k:dvi_index;
+begin for k:=a to b do write(dvi_file,dvi_buf[k]);
+end;
+@y
+For Berkeley {\mc UNIX}, this is going to be handled by an external procedure,
+which will do the output using |fwrite|.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was188,now191] Avoid `oct', a reserved word in SunOS Pascal(!)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d octant==xr {octant code for nearest dot, plus 8 for coincident dots}
+@y
+@d octant==xr {octant code for nearest dot, plus 8 for coincident dots}
+@d oct==octnt {changes variable name since |oct| is reserved word in SunOS}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was216,now219] call set_paths before gf_start to initialize paths
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p begin initialize; {get all variables initialized}
+@<Initialize the strings@>;
+start_gf; {open the input and output files}
+@y
+@p begin initialize; {get all variables initialized}
+@<Initialize the strings@>;
+set_paths; {initialize paths for \.{TFM} files from environment if needed}
+start_gf; {open the input and output files}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was216,now219] finish normal end with <nl> on terminal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ cur_gf:=get_byte; str_ptr:=init_str_ptr; pool_ptr:=str_start[str_ptr];
+ end;
+final_end:end.
+@y
+ cur_gf:=get_byte; str_ptr:=init_str_ptr; pool_ptr:=str_start[str_ptr];
+ end;
+final_end:print_ln(' ');
+end.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was219,now222] declare special global
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtoDVI} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtoDVI} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@<Glob...@>=
+@!temp_int:integer; {used for TFM input}
+@!last,@!first:0..terminal_line_length; {needed in \.{ext.c}}
+@!interrupt:integer; {ditto}
+@!first_input:boolean; {true before the first call to |input_ln|}
+
+@ @<Types in...@>=
+@!word_file=file of memory_word; {needed in \.{ext.c}}
+@z
diff --git a/systems/knuth/local/mfware-sparc/gftopk.ch b/systems/knuth/local/mfware-sparc/gftopk.ch
new file mode 100644
index 0000000000..4ed4b64ddf
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/gftopk.ch
@@ -0,0 +1,367 @@
+% $Header: gftopk.ch,v 1.4 87/12/05 10:40:15 mackay Released $
+
+% Change file for the GFtoPK processor, for use on Berkeley UNIX systems.
+
+% History:
+%
+% don upped max_rows on 90/8/30
+% don upgraded to version 2.3 on 90/8/6
+% don upgraded to version 2.2 on 89/11/21
+%
+% don made changes for version 2 and SunOS on 89/8/12
+% (I simplified things by borrowing from texware change files)
+%
+% $Log: gftopk.ch,v $
+% Revision 1.4 87/12/05 10:40:15 mackay
+% Released for GFtoPK 1.4;
+%
+% Revision 1.3 86/12/08 20:20:15 mackay
+% Released for GFtoPK 1.3;
+%
+% Revision 1.2 86/02/01 14:45:21 richards
+% Released for GFtoPK 1.2;
+%
+%
+% Revision 1.1 86/01/30 19:56:05 richards
+% Initial revision
+%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{GF\lowercase{to}PK changes for Berkeley {\mc UNIX}}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is GFtoPK, Version 2.3' {printed when the program starts}
+@y
+@d banner=='This is GFtoPK, Version 2.3 for SunOS'
+ {printed when the program starts}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Fix others:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {SunOS Pascal default cases}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] Redefine program header, eliminate gf_file and pxl_file
+% add <Parse command line> section to initialize
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program GFtoPK(@!gf_file,@!pk_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@y
+@p program GFtoPK(@!input,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\@=#include "gftopk_ext.h"@>@\
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>;@/
+ @<Parse command line arguments@>@/
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6] add file_name_size to constants in the outer block
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!line_length=79; {bracketed lines of output will be at most this long}
+@!max_row=16000; {largest index in the main |row| array}
+@y
+@!line_length=79; {bracketed lines of output will be at most this long}
+@!max_row=60000; {largest index in the main |row| array}
+@!file_name_size=256; {maximum length of a file name}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8] have abort() add <nl> to end of message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d abort(#)==begin print(' ',#); jump_out;
+@y
+@d abort(#)==begin print_ln(' ',#); jump_out;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [37] Add UNIX_file_name type, redefine byte_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!UNIX_file_name=packed array [1..file_name_size] of char;
+@!byte_file=packed file of -128..127;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38] add gf_name and pk_name to global vars
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!gf_file:byte_file; {the stuff we are \.{GFtoPK}ing}
+@!pk_file:byte_file; {the stuff we have \.{GFtoPK}ed}
+@y
+@!gf_file:byte_file; {the stuff we are \.{GFtoPK}ing}
+@!gf_name: UNIX_file_name; {the name of |gf_file|}
+@!pk_file:byte_file; {the stuff we have \.{GFtoPK}ed}
+@!pk_name: UNIX_file_name; {the name of |pk_file|}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [39] open_gf_file with extended reset command (can dump core!)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+begin reset(gf_file);
+@y
+begin if testreadaccess(gf_name) then reset(gf_file,gf_name)
+else abort('Can''t open the GF file');
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [40] and open_pk_file...
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+begin rewrite(pk_file);
+@y
+var j,k,size:integer; startednumber:boolean;
+begin
+if argc < 3 then begin@/
+ @<Generate |pk_name| from |gf_name| and |h_mag|@>@/
+ end;
+rewrite(pk_file,pk_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [43] replace gf_byte and gf_signed_quad
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function gf_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then bad_gf('Unexpected end of file!')
+@.Unexpected end of file@>
+else begin read(gf_file,b); gf_byte:=b;
+ end;
+incr(gf_loc);
+end;
+@#
+function gf_signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+if a<128 then gf_signed_quad:=((a*256+b)*256+c)*256+d
+else gf_signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+gf_loc := gf_loc + 4 ;
+end;
+@y
+@p function gf_byte:integer; {returns the next byte, unsigned}
+var b:-128..127;
+begin if eof(gf_file) then bad_gf('Unexpected end of file!')
+else begin read(gf_file,b);
+ if b < 0 then gf_byte := b + 256 else gf_byte:=b;
+ end;
+incr(gf_loc);
+end;
+@#
+function gf_signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:integer;
+begin read(gf_file,a); b := gf_byte; c := gf_byte; d := gf_byte;
+gf_signed_quad:=((a*256+b)*256+c)*256+d
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [44] redefine pk_byte, pk_halfword, pk_three_bytes, and pk_word
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure pk_byte(a:integer) ;
+begin
+ if pk_open then begin
+ if a < 0 then a := a + 256 ;
+ write(pk_file, a) ;
+ incr(pk_loc) ;
+ end ;
+end ;
+@#
+procedure pk_halfword(a:integer) ;
+begin
+ if a < 0 then a := a + 65536 ;
+ write(pk_file, a div 256) ;
+ write(pk_file, a mod 256) ;
+ pk_loc := pk_loc + 2 ;
+end ;
+@#
+procedure pk_three_bytes(a:integer);
+begin
+ write(pk_file, a div 65536 mod 256) ;
+ write(pk_file, a div 256 mod 256) ;
+ write(pk_file, a mod 256) ;
+ pk_loc := pk_loc + 3 ;
+end ;
+@#
+procedure pk_word(a:integer) ;
+var b : integer ;
+begin
+ if pk_open then begin
+ if a < 0 then begin
+ a := a + @'10000000000 ;
+ a := a + @'10000000000 ;
+ b := 128 + a div 16777216 ;
+ end else b := a div 16777216 ;
+ write(pk_file, b) ;
+ write(pk_file, a div 65536 mod 256) ;
+ write(pk_file, a div 256 mod 256) ;
+ write(pk_file, a mod 256) ;
+ pk_loc := pk_loc + 4 ;
+ end ;
+end ;
+@y
+@p procedure pk_byte(a:integer) ;
+begin
+ if pk_open then begin
+ if a < 0 then a := a + 256 ;
+ if a > 127 then a := a-256;
+ write(pk_file, a) ;
+ incr(pk_loc) ;
+ end ;
+end ;
+@#
+procedure pk_halfword(a:integer) ;
+begin
+ if a < 0 then a := a + 65536 ;
+ pk_byte(a div 256); pk_byte(a mod 256);
+end ;
+@#
+procedure pk_three_bytes(a:integer);
+begin
+ pk_byte(a div 65536 mod 256) ;
+ pk_byte(a div 256 mod 256) ;
+ pk_byte(a mod 256) ;
+ end ;
+@#
+procedure pk_word(a:integer) ;
+var b : integer ;
+begin
+ if a < 0 then begin
+ a := a + @'10000000000 ;
+ a := a + @'10000000000 ;
+ b := 128 + a div 16777216 ;
+ end else b := a div 16777216 ;
+ pk_byte(b) ;
+ pk_byte(a div 65536 mod 256) ;
+ pk_byte(a div 256 mod 256) ;
+ pk_byte(a mod 256) ;
+ end ;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [46] redefine find_gf_length and move_to_byte
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure find_gf_length ;
+begin
+ set_pos(gf_file, -1) ; gf_len := cur_pos(gf_file) ;
+end ;
+@#
+procedure move_to_byte(@!n : integer) ;
+begin
+ set_pos(gf_file, n); gf_loc := n ;
+end ;
+@y
+@p procedure find_gf_length ;
+begin
+ gf_len := flength(gf_file);
+end ;
+@#
+procedure move_to_byte(@!n : integer) ;
+begin
+ b_seek(gf_file, n); gf_loc := n ;
+end ;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [88+] add <Parse command line arguments> section and needed subsections
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtoPK} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+This section contains additions needed to make \.{GFtoPK} work under
+{\mc UNIX}. The additions all deal with obtaining file names from
+the invoking command line and if not all arguments are provided,
+devising defaults for them.
+@^system dependencies@>
+
+@<Parse command line arguments@>=
+if (argc < 2) or (argc > 3) then begin@/
+ write_ln('Usage: gftopk GF-file [PK-file]');@/
+ goto final_end;@/
+ end;
+argv(1, gf_name);@/
+if (argc > 2) then@/
+ argv(2, pk_name);@/
+
+@ Here's a section to construct |pk_name| from |gf_name|, tacking on the
+right extension from the magnification of the file.
+
+@<Generate |pk_name| ...@>=
+j:=file_name_size;@/
+while (j > 1) and (gf_name[j] <> '/') do@/
+ decr(j);
+i:=j;
+while (j > 1) do begin@/
+ decr(j); pk_name[j] := gf_name[j];
+ end;
+while (i < file_name_size) and (gf_name[i] <> '.') and (gf_name[i] <> ' ') do begin@/
+ pk_name[i] := gf_name[i];@/
+ incr(i);@/
+ end;
+pk_name[i]:='.'; incr(i);@/
+size:=h_mag;@/
+j :=10000;@/
+startednumber:=false;@/
+while (j > 0) do begin@/
+ k:=size div j;@/
+ size := size mod j;@/
+ j:=j div 10;
+ if startednumber or (k <> 0) or (j = 0) then begin@/
+ pk_name[i] := chr(ord('0') + k); incr(i);@/
+ startednumber := true;@/
+ end;@/
+ end;@/
+pk_name[i] := 'p'; incr(i);@/
+pk_name[i] := 'k'; incr(i);@/
+pk_name[i] := ' ';@/
+print_ln('[Output file named ''', pk_name:i-1, ''']');@/
+
+@z
+
diff --git a/systems/knuth/local/mfware-sparc/gftopk_ext.c b/systems/knuth/local/mfware-sparc/gftopk_ext.c
new file mode 100644
index 0000000000..5301fc90be
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/gftopk_ext.c
@@ -0,0 +1,43 @@
+#include "h00vars.h" /* defines Pascal I/O structure */
+
+#define namelength 100 /* should agree with gftopk.ch and gftype.ch */
+
+bseek(filep,cnt)
+ register struct iorec *filep;
+ int cnt;
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+
+ iop = filep->fbuf;
+ fseek(iop,(long)cnt,0);
+}
+
+bool testreadaccess(fn)
+ char *fn;
+{
+ register char *p;
+
+ fn[namelength-1] = ' ';
+ p = fn;
+ while (*p++ != ' ');
+ p--;
+ *p = '\0';
+ if (access(fn,4)==0) {
+ *p = ' '; return(TRUE);}
+ else {*p = ' '; return(FALSE);}
+}
+
+int flength(filep)
+ register struct iorec *filep;
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+ register long pos; /* current file position */
+ register int len; /* the file length */
+
+ iop = filep->fbuf;
+ pos = ftell(iop);
+ fseek(iop,0L,2);
+ len = ftell(iop);
+ fseek(iop,pos,0);
+ return(len);
+}
diff --git a/systems/knuth/local/mfware-sparc/gftopk_ext.h b/systems/knuth/local/mfware-sparc/gftopk_ext.h
new file mode 100644
index 0000000000..cd063b1cf1
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/gftopk_ext.h
@@ -0,0 +1,6 @@
+function flength(var f: bytefile):integer;
+ external;
+procedure bseek(var f: bytefile; loc: integer);
+ external;
+function testreadaccess(var fn: UNIXfilename):boolean;
+ external;
diff --git a/systems/knuth/local/mfware-sparc/gftype.ch b/systems/knuth/local/mfware-sparc/gftype.ch
new file mode 100644
index 0000000000..dc63dad049
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/gftype.ch
@@ -0,0 +1,430 @@
+% $Header: gftype.ch,v 2.2.1.4 86/01/27 15:43:41 richards Released $
+
+% Change file for the GFtype processor, for use on Berkeley UNIX systems.
+
+% History:
+%
+% don upgraded to version 3.1 on 3/15/91
+%
+% don added testreadaccess (to avoid core dump on bad GF file name) on 89/10/18
+%
+% don made changes for version 3 and SunOS on 89/08/12
+% (I simplified things by borrowing from dvitype.ch)
+%
+% $Log: gftype.ch,v $
+% Revision 2.2.1.4 86/01/27 15:43:41 richards
+% Changed:
+% text_file type to "text" for Pyramid compiler,
+% who doesn't believe packed array of char == text, and
+% won't allow write('strings') to it!
+%
+% Revision 2.2.1.3 86/01/21 22:04:30 richards
+% Changed:
+% file_name_size and byte_file declaration to agree with binary I/O
+% library updates; b_open_in() call to include path specifier
+%
+% Revision 2.2.1.2 86/01/21 15:54:21 richards
+% Changed:
+% interface to byte I/O again; now cur_loc is back to a variable,
+% and b_set_ptr() saves addr of cur_loc so it can be kept in
+% sync with files. b_file_loc() was called more frequently than
+% I thought...
+%
+% Revision 2.2.1.1 86/01/20 22:06:53 richards
+% Incorporate new Byte-I/O routines in place of fudging Pascal into
+% reading bytes; for Pyramid ....
+%
+% Revision 2.2 85/10/18 23:31:09 richards
+% Updated for GFtype Version 2.2
+%
+% Revision 2.1 85/03/03 21:41:49 richards
+% Updated to GF utilities distributed with MF Version 0.77
+% (New GF file format)
+%
+% Revision 2.0 84/12/16 22:42:52 richards
+% Updated for GFtype Version 2.0 (New GF file format)
+%
+% Revision 1.2 84/12/06 02:35:40 richards
+% Updated to GFtype Version 1.2; merged in changes from sdcarl!rusty
+%
+% Revision 0.0 84/11/10 02:13:19 richards
+% Base version for GFtype 0.0
+%
+%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{GF$\,$\lowercase{type} changes for Berkeley {\mc UNIX}}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is GFtype, Version 3.1' {printed when the program starts}
+@y
+@d banner=='This is GFtype, Version 3.1 for SunOS'
+ {printed when the program starts}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Case statement default feature of SunOS Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Redirect GFtype output to correct output file
+% depending on gfout_active value
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+@d print_nl==write_ln
+@y
+@d print(#)==begin if gfout_active then write(gftyout,#) else write(#) end
+@d print_ln(#)==begin if gfout_active then write_ln(gftyout,#)
+ else write_ln(#) end
+@d print_nl==begin if gfout_active then write_ln(gftyout) else write_ln end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Change program header to standard input/output; include external header
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program GF_type(@!gf_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@y
+@p program GF_type(@!input,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\@=#include "gftopk_ext.h"@>@\
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin @<Set initial values@>@/
+ @<Parse command line arguments@>;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [5] Add file_name_size to constants
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Constants...@>=
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!line_length=79; {\\{xxx} strings will not produce lines longer than this}
+@!max_row=79; {vertical extent of pixel image array}
+@!max_col=79; {horizontal extent of pixel image array}
+@y
+@<Constants...@>=
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!line_length=79; {\\{xxx} strings will not produce lines longer than this}
+@!max_row=79; {vertical extent of pixel image array}
+@!max_col=79; {horizontal extent of pixel image array}
+@!file_name_size=100; {max length of file name on command line}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Have abort append <nl> to end of msg
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d abort(#)==begin print(' ',#); jump_out;
+@y
+@d abort(#)==begin print_ln(' ',#); jump_out;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] Change definition of 'byte_file' to correct subrange
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+On Berkeley {\mc UNIX}, we have to use |-128..127| for byte files, as
+explained in the \TeX\ changes.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@!UNIX_file_name=packed array[1..file_name_size] of char;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] Add gf_name declaration
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The program deals with one binary file variable: |gf_file| is the main
+input file that we are translating into symbolic form.
+
+@<Glob...@>=
+@!gf_file:byte_file; {the stuff we are \.{GF}typing}
+@y
+@ The program deals with one binary file variable: |gf_file| is the main
+input file that we are translating into symbolic form.
+We also declare array |gf_name|, used to hold the name of |gf_file|.
+
+@<Glob...@>=
+@!gf_file:byte_file; {the stuff we are \.{GF}typing}
+@!gf_name: UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [22] Change to file opening
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin reset(gf_file);
+@y
+The local /PASCAL/ runtimes will dump core if the file isn't present.
+We avoid that by using an external procedure.
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin if testreadaccess(gf_name) then reset(gf_file,gf_name)
+else abort('Can''t read the GF file');
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] Use modified routines to access gf_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(gf_file,a); read(gf_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+@y
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:-128..127;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc);
+ if b < 0 then get_byte := b + 256 else get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:-128..127;
+begin read(gf_file,b); incr(cur_loc);
+signed_byte:=b;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,b:integer;
+begin a := get_byte; b := get_byte;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:integer;
+begin a := get_byte; b := get_byte; c := get_byte;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:integer;
+begin a := signed_byte; b := get_byte; c := get_byte; d := get_byte;
+signed_quad:=((a*256+b)*256+c)*256+d
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27] Set term_in and term_out to standard input/output
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The |input_ln| routine waits for the user to type a line at his or her
+terminal; then it puts ASCII-code equivalents for the characters on that line
+into the |buffer| array. The |term_in| file is used for terminal input,
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!term_in:text_file; {the terminal, considered as an input file}
+@!term_out:text_file; {the terminal, considered as an output file}
+@y
+@ The |input_ln| routine waits for the user to type a line at his or her
+terminal; then it puts ASCII-code equivalents for the characters on that line
+into the |buffer| array. The |term_in| file is used for terminal input,
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@d term_in == input {standard input}
+@d term_out == output {standard output}
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!gftyout: text_file; {output from program}
+@!gfout_name: packed array[1..file_name_size] of char; {file name}
+@!gfout_active: boolean; {true if |gftyout| is open and ready to write}
+@!first_input: boolean; {true before the first call of |input_ln|}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [28] update_terminal becomes flush()
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush(term_out) {empty the terminal output buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29] eliminate reset() from input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure input_ln; {inputs a line from the terminal}
+var k:0..terminal_line_length;
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+k:=0;
+while (k<terminal_line_length)and not eoln(term_in) do
+ begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
+ end;
+buffer[k]:=" ";
+end;
+@y
+@p procedure input_ln; {inputs a line from the terminal}
+var k:0..terminal_line_length;
+begin update_terminal;
+if first_input then first_input:=false
+else if eoln(term_in) then read_ln(term_in);
+k:=0;
+while (k<terminal_line_length)and not eoln(term_in) do
+ begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
+ end;
+buffer[k]:=" ";
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [31] eliminate rewrite() from dialog
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure dialog;
+label 1,2;
+begin rewrite(term_out); {prepare the terminal for output}
+write_ln(term_out,banner);@/
+@<Determine whether the user |wants_mnemonics|@>;
+@<Determine whether the user |wants_pixels|@>;
+@<Print all the selected options@>;
+end;
+@y
+@p procedure dialog;
+label 1,2;
+begin
+write_ln(term_out,banner);@/
+@<Determine whether the user |wants_mnemonics|@>;
+@<Determine whether the user |wants_pixels|@>;
+@<Print all the selected options@>;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [65] finish last line written to statistics (stdout)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+print('The file had ',total_chars:1,' character');
+if total_chars<>1 then print('s');
+print(' altogether.');
+@.The file had n characters...@>
+final_end:end.
+@y
+print('The file had ',total_chars:1,' character');
+if total_chars<>1 then print('s');
+print_ln(' altogether.');
+@.The file had n characters...@>
+final_end:end.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [74,75,76] Add new code to parse command line arguments
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtype} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+This section contains additions to \.{GFtype} necessary to integrate it
+into the {\mc UNIX} environment. The primary addition is a section to
+parse command line arguments and put file names into their correct
+variables, if needed.
+@^system dependencies@>
+
+@ The command line issued to start \.{GFtype} should also contain the
+names of the input (GF) file, and optionally, the name of an output
+file to receive the output from \.{GFtype}. This segment decodes
+the command line, and saves the file names in the appropriate variables.
+@^system dependencies@>
+
+@<Parse command...@>=
+if (argc < 2) or (argc > 3) then
+ begin print_ln('Usage: gftype GFfile [outputfile]');@/
+ goto final_end; { we will be unstructured just this once }
+ end;
+argv(1, gf_name);
+if argc > 2 then
+ begin argv(2, gfout_name);@/
+ rewrite(gftyout, gfout_name);@/
+ gfout_active := true;@/
+ print_ln(banner);@/
+ end
+
+@ We also need to initialize the values of the new boolean variables
+we introduced.
+
+@<Set initial ...@>=
+gfout_active:=false;
+first_input:=true;
+@z
+
diff --git a/systems/knuth/local/mfware-sparc/mft.ch b/systems/knuth/local/mfware-sparc/mft.ch
new file mode 100644
index 0000000000..9f4d1e0821
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/mft.ch
@@ -0,0 +1,534 @@
+% Change file for the MFT processor, for use on Berkeley UNIX systems.
+% This file was created by Pierre MacKay, and is based on the
+% change file for WEAVE written by Howard Trickey and Pavel Curtis.
+
+% History:
+% 6/09/86--WEAVE change file copied in entirety and renamed to
+% refer to MFT.
+%
+% 10/16/89 modified by don for SunOS, brought up to version 2.0
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] MFT: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{MFT changes for Berkeley {\mc UNIX}}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Change banner message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is MFT, Version 2.0'
+@y
+@d banner=='This is MFT, Version 2.0 for SunOS'
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] add input and output, remove other files, add ref to scan_args,
+% [3] and #include external definition for exit(), etc.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+program MFT(@!mf_file,@!change_file,@!style_file,@!tex_file);
+label end_of_MFT; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@<Error handling procedures@>@/
+@y
+program MFT(@!input,@!output);
+label end_of_MFT; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\
+@=#include "mft_ext.h"@>
+@\@/
+@<Error handling procedures@>@/
+@<|scan_args| procedure@>@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] compiler options
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@{@&$C+,A+,D-@} {range check, catch arithmetic overflow, no debug overhead}
+@y
+@=(*$C-*)@> {no range check}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Fix others:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {SunOS Pascal default cases}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [13] declare UNIX_file_name type
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!text_file=packed file of text_char;
+@y
+@!text_file=packed file of text_char;
+@!UNIX_file_name = packed array[1..max_file_name_length] of char;
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] terminal output: use standard i/o
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@y
+@d term_out==output
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@z
+
+@x
+@<Globals...@>=
+@!term_out:text_file; {the terminal as an output file}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] init terminal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal. Here is one way to do this
+on the \PASCAL\ system that was used in \.{WEAVE}'s initial development:
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
+@y
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal.
+@^system dependencies@>
+
+@<Set init...@>=
+; {nothing need be done}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [22] flush terminal buffer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush(term_out) {empty the terminal output buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] open input files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens the input files. Since these files were listed
+in the program header, we assume that the \PASCAL\ runtime system has
+already checked that suitable file names have been given; therefore no
+additional error checking needs to be done.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read the inputs}
+begin reset(mf_file); reset(change_file); reset(style_file);
+end;
+@y
+@ The following code opens the input files.
+This is called after |scan_args| has filled the file name variables
+appropriately.
+We use this opportunity, to define some constants related to file opening
+and the use of paths. These constants are repeated in mft_ext.c and
+must match the values given there.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {no path searching should be done}
+@d good_file_path=3 {path specifier for style files}
+
+@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
+
+@p procedure open_input; {prepare to read inputs}
+var i:integer;
+begin if test_read_access(mf_name) then
+ begin reset(mf_file,mf_name);
+ if test_read_access(change_name) then
+ begin reset(change_file,change_name);
+ if test_access(read_access_mode,good_file_path) then
+ reset(style_file,real_name_of_file)
+ else begin i:=1;
+ while real_name_of_file[i]<>chr(0) do incr(i);
+ print_nl('! Can''t open the style file ',real_name_of_file:i);
+ mark_fatal;
+ jump_out;
+ end;
+ end
+ else begin i:=1; change_name[max_file_name_length]:=' ';
+ while change_name[i]<>' ' do incr(i);
+ print_nl('! Can''t open the change file ',change_name:i);
+ mark_fatal; jump_out;
+ end;
+ end
+else begin i:=1; mf_name[max_file_name_length]:=' ';
+ while mf_name[i]<>' ' do incr(i);
+ print_nl('! Can''t open the METAFONT source file ',mf_name:i);
+ mark_fatal; jump_out;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [26] opening \TeX\ file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens |tex_file|.
+Since this file was listed in the program header, we assume that the
+\PASCAL\ runtime system has checked that a suitable external file name has
+been given.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(tex_file);
+@y
+@ The following code opens |tex_file|.
+The |scan_args| procedure is used to set up |tex_name| as required.
+@^system dependencies@>
+
+@<Set init...@>=
+scan_args;
+rewrite(tex_file,tex_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [28] faster input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+var final_limit:0..buf_size; {|limit| without trailing blanks}
+begin limit:=0; final_limit:=0;
+if eof(f) then input_ln:=false
+else begin while not eoln(f) do
+ begin buffer[limit]:=xord[f^]; get(f);
+ incr(limit);
+ if buffer[limit-1]<>" " then final_limit:=limit;
+ if limit=buf_size then
+ begin while not eoln(f) do get(f);
+ decr(limit); {keep |buffer[buf_size]| empty}
+ if final_limit>limit then final_limit:=limit;
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ end;
+ read_ln(f); limit:=final_limit; input_ln:=true;
+ end;
+end;
+@y
+With Berkeley {\mc UNIX} we call an external C procedure, |line_read|.
+That routine fills |buffer| from 0 onwards with the |xord|'ed values
+of the next line, setting |limit| appropriately (ignoring trailing
+blanks).
+It will stop if |limit=buf_size|, and the following will cause an error
+message.
+Note: for bootstrapping purposes it is all right to use the original form
+of |input_ln|; it will just run slower.
+
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+begin limit:=0;
+if test_eof(f) then input_ln:=false
+else begin line_read(f);
+ if limit=buf_size then
+ begin
+ decr(limit); {keep |buffer[buf_size]| empty}
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ input_ln:=true;
+ end;
+end;
+@z
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [87] faster flush_buffer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+done: for k:=1 to j do write(tex_file,xchr[out_buf[k]]);
+@y
+done: linewrite(tex_file,j);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [112] print newline at end of run and exit based upon value of history
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Let's put it all together now: \.{MFT} starts and ends here.
+@^system dependencies@>
+
+@y
+Let's put it all together now: \.{MFT} starts and ends here.
+@^system dependencies@>
+
+@d UNIXexit==e@&x@&i@&t
+
+@z
+
+@x
+end.
+@y
+new_line;
+if (history <> spotless) and (history <> harmless_message) then
+ UNIXexit(1)
+else
+ UNIXexit(0);
+end.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [114] system dependent changes--the scan_args procedure
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This module should be replaced, if necessary, by changes to the program
+that are necessary to make \.{MFT} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous modules preserve the module numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new modules, can be inserted here; then only the index
+itself will get a new module number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+The user calls \.{MFT} with arguments on the command line.
+These are either file names or flags (beginning with '-').
+The following globals are for communicating the user's desires to the rest
+of the program. The various |file_name| variables contain strings with
+the full names of those files, as UNIX knows them.
+
+The only flags that affect \.{MFT} are |'-c'| and |'-s'| whose
+status is kept in |no_change| and |no_style|.
+
+@d max_file_name_length==100
+
+@<Globals...@>=
+@!mf_name,@!change_name,@!style_name,@!tex_name,
+ @!real_name_of_file: UNIX_file_name;
+@!no_change,no_style:boolean;
+
+@ The |scan_args| procedure looks at the command line arguments and sets
+the |file_name| variables accordingly.
+
+At least one file name must be present as the first argument:
+the \.{mf} file. It may have
+an extension, or it may omit it to get |'.mf'| added.
+If there is only one file name, the output file name is formed by replacing
+the \.{mf} file name extension by |'.tex'|.
+Thus, the command line \.{mft foo} implies the use of the \METAFONT\
+input file \.{foo.mf} and the output file \.{foo.tex}.
+If this style of command line, with only one argument is used,
+the default style file, |plain.mft|, will be used to provide
+minimal formatting.
+
+An argument beginning with a minus sign is a flag.
+Any letters following the minus sign may cause global
+flag variables to be set.
+Currently, a |c| means that there is a change file, and an |s| means
+that there is a style file. The auxiliary files must
+of course appear in the same order as the flags. For example,
+the flag -sc must be followed by the name of the |style_file| first,
+and then the name of the |change_file|.
+
+@<|scan_args|...@>=
+procedure scan_args;
+var dot_pos,i,a: integer; {indices}
+@!which_file_next: char;
+@!fname: array[1..max_file_name_length-5] of char; {temporary argument holder}
+@!found_mf,@!found_change,found_style: boolean; {|true| when those
+ file names have been seen}
+begin
+setpaths; {Get path for style file from \.{MFINPUTS}}
+found_mf:=false;
+@<Set up null change file@>; found_change:=true; {Default to no change file}
+@<Set up plain style file@>; found_style:=true; {Default to plain style file}
+for a:=1 to argc-1 do begin
+ argv(a,fname); {put argument number |a| into |fname|}
+ if fname[1]<>'-' then begin
+ if not found_mf then
+ @<Get |mf_name| from |fname|, and set up |tex_name@>
+ else
+ if not found_change then begin
+ if which_file_next <> 's' then begin
+ @<Get |change_name| from |fname|@>;
+ which_file_next := 's'
+ end
+ else @<Get |style_name| from |fname|@>
+ end
+ else if not found_style then begin
+ if which_file_next = 's' then begin
+ @<Get |style_name| from |fname|@>;
+ which_file_next := 'c'
+ end;
+ end
+ else @<Print usage error message and quit@>;
+ end
+ else @<Handle flag argument in |fname|@>;
+ end;
+if not found_mf then @<Print usage error message and quit@>;
+end;
+
+@ Use all of |fname| for the |mf_name| if there is a |'.'| in it,
+otherwise add |'.mf'|.
+The \TeX\ file name comes from adding things after the dot.
+The |argv| procedure will not put more than |max_file_name_length-5|
+characters into |fname|, and this leaves enough room in the |file_name|
+variables to add the extensions.
+
+The end of a file name is marked with a |' '|, the convention assumed by
+the |reset| and |rewrite| procedures.
+
+@<Get |mf_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=max_file_name_length-5) do begin
+ mf_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ mf_name[dot_pos]:='.';
+ mf_name[dot_pos+1]:='m';
+ mf_name[dot_pos+2]:='f';
+ mf_name[dot_pos+3]:=' ';
+ end
+else mf_name[i]:=' ';
+for i:=1 to dot_pos do begin
+ tex_name[i]:=mf_name[i];
+ end;
+tex_name[dot_pos+1]:='t';
+tex_name[dot_pos+2]:='e';
+tex_name[dot_pos+3]:='x';
+tex_name[dot_pos+4]:=' ';
+which_file_next := 'z';
+found_mf:=true;
+end
+
+@ Use all of |fname| for the |change_name| if there is a |'.'| in it,
+otherwise add |'.ch'|.
+
+@<Get |change_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=max_file_name_length-5)
+do begin
+ change_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ change_name[dot_pos]:='.';
+ change_name[dot_pos+1]:='c';
+ change_name[dot_pos+2]:='h';
+ change_name[dot_pos+3]:=' ';
+ end
+else change_name[i]:=' ';
+which_file_next := 'z';
+found_change:=true;
+end
+
+@ Use all of |fname| for the |style_name| if there is a |'.'| in it,
+otherwise add |'.mft'|.
+
+
+@<Get |style_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=max_file_name_length-5)
+do begin
+ style_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ style_name[dot_pos]:='.';
+ style_name[dot_pos+1]:='m';
+ style_name[dot_pos+2]:='f';
+ style_name[dot_pos+3]:='t';
+ style_name[dot_pos+4]:=' ';
+ end
+else style_name[i]:=' ';
+which_file_next := 'z';
+found_style:=true;
+end
+
+@
+
+@<Set up null change file@>=
+begin
+ change_name[1]:='/';
+ change_name[2]:='d';
+ change_name[3]:='e';
+ change_name[4]:='v';
+ change_name[5]:='/';
+ change_name[6]:='n';
+ change_name[7]:='u';
+ change_name[8]:='l';
+ change_name[9]:='l';
+ change_name[10]:=' ';
+end
+
+@
+
+@<Set up plain style file@>=
+begin
+ style_name[1]:='p';
+ style_name[2]:='l';
+ style_name[3]:='a';
+ style_name[4]:='i';
+ style_name[5]:='n';
+ style_name[6]:='.';
+ style_name[7]:='m';
+ style_name[8]:='f';
+ style_name[9]:='t';
+ style_name[10]:=' ';
+end
+
+@
+
+@<Handle flag...@>=
+begin
+i:=2;
+while (fname[i]<>' ') and (i<=max_file_name_length-5) do begin
+ if fname[i]='c' then begin found_change:=false;
+ if which_file_next <> 's' then which_file_next := 'c'
+ end
+ else if fname[i]='s' then begin found_style:=false;
+ if which_file_next <> 'c' then which_file_next := 's'
+ end
+ else print_nl('Invalid flag ',xchr[xord[fname[i]]]);
+ incr(i);
+ end;
+end
+
+@
+
+@<Print usage error message and quit@>=
+begin
+print_nl('! Usage: mft file[.mf] [-cs] [change[.ch]] [style[.mft]] ');
+error; mark_fatal;
+jump_out;
+end
+
+@z
diff --git a/systems/knuth/local/mfware-sparc/mft_ext.c b/systems/knuth/local/mfware-sparc/mft_ext.c
new file mode 100644
index 0000000000..b8fe8d31e6
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/mft_ext.c
@@ -0,0 +1,249 @@
+/* External procedures for MFT*/
+/* (culled from weavext.c and ext.c for WEAVE and TeX) */
+/* Written by: H. Trickey, 11/17/83 */
+/* (modified for 8-bit input by don on 10/16/89) */
+
+/* Note: these procedures aren't necessary; the default input_ln and
+ * flush_buffer routines in tangle/weave work fine on UNIX.
+ * However a very big time improvement is achieved by using these.
+ *
+ * These procedures are the same as for tangle, except for a slight offset
+ * in which characters are output from outbuf in linewrite.
+ */
+
+#define BUF_SIZE 100 /* should agree with mft.web */
+
+#include "mfpaths.h" /* defines default MFINPUTS path */
+#include "h00vars.h" /* defines Pascal I/O structure */
+
+extern short buffer[]; /* 0..BUF_SIZE. Input goes here */
+extern short outbuf[]; /* 0..OUT_BUF_SIZE. Output from here */
+extern short xord[];
+extern char xchr[]; /* character translation arrays */
+extern char limit; /* index into buffer. Note that limit
+ is 0..buf_size (= 0..100) in mft.web; this
+ differs from the definition in weave.web */
+
+/*
+ * lineread reads from the Pascal text file with iorec pointer filep
+ * into buffer[0], buffer[1],..., buffer[limit-1] (and
+ * setting "limit").
+ * Characters are read until a newline is found (which isn't put in the
+ * buffer) or until the next character would go into buffer[BUF_SIZE].
+ * And trailing blanks are to be ignored, so limit is really set to
+ * one past the last non-blank.
+ * The characters need to be translated, so really xord[c] is put into
+ * the buffer when c is read.
+ * If end-of-file is encountered, the funit field of *filep is set
+ * appropriately.
+ */
+lineread(filep)
+struct iorec *filep;
+{
+ register c;
+ register short *cs; /* pointer into buffer where next char goes */
+ register short *cnb; /* last non-blank character input */
+ register FILE *iop; /* stdio-style FILE pointer */
+ register int l; /* how many chars allowed before buffer overflow */
+
+ iop = filep->fbuf;
+ cnb = cs = &(buffer[0]);
+ l = BUF_SIZE;
+ /* overflow when next char would go into buffer[BUF_SIZE] */
+ while (--l>=0 && (c = getc(iop)) != EOF && c!='\n')
+ if((*cs++ = xord[c])!=' ') cnb = cs;
+ if (c == EOF)
+ filep->funit |= EOFF; /* we hit end-of-file */
+ limit = cnb-&(buffer[0]);
+}
+
+/*
+ * linewrite writes to the Pascal text file with iorec pointer filep
+ * from outbuf[1], outbuf[1],..., outbuf[cnt].
+ * (Note the offset of indices vis a vis the tangext version of this.)
+ * Translation is done, so that xchr[c] is output instead of c.
+ */
+ linewrite(filep,cnt)
+ struct iorec *filep;
+ int cnt;
+ {
+ register FILE *iop; /* stdio-style FILE pointer */
+ register short *cs; /* pointer to next character to output */
+ register int l; /* how many characters left to output */
+
+ iop = filep->fbuf;
+ cs = &(outbuf[1]);
+ l = cnt;
+ while (--l>=0) putc(xchr[*cs++],iop);
+}
+
+/*
+** testeof(filep)
+**
+** Test whether or not the Pascal text file with iorec pointer filep
+** has reached end-of-file (when the only I/O on it is done with
+** lineread, above).
+** We may have to read the next character and unget it to see if perhaps
+** the end-of-file is next.
+*/
+
+bool
+testeof(filep)
+register struct iorec *filep;
+{
+ register char c;
+ register FILE *iop; /* stdio-style FILE pointer */
+ if (filep->funit & EOFF)
+ return(TRUE);
+ else { /* check to see if next is EOF */
+ iop = filep->fbuf;
+ c = getc(iop);
+ if (c == EOF)
+ return(TRUE);
+ else {
+ ungetc(c,iop);
+ return(FALSE);
+ }
+ }
+}
+/* and now, some routines to implement paths for file names. These routines
+ are taken from Howard Trickey's port of dvitype (2/19/84). Howard
+ adapted them from his port of TeX, ext.c */
+
+char *inputpath;
+
+char *getenv();
+
+/*
+ * setpaths is called to set up the pointer inputpath
+ * as follows: if the user's environment has a value for TEXINPUTS
+ * then use it; otherwise, use defaultinputpath.
+ */
+setpaths()
+{
+ register char *envpath;
+
+ if ((envpath = getenv("TEXINPUTS")) != NULL)
+ inputpath = envpath;
+ else
+ inputpath = defaultinputpath;
+}
+
+#define maxfilenamelength 100 /* should agree with mft.ch */
+extern char stylename[],realnameoffile[]; /* these have size namelength */
+
+/*
+ * testaccess(amode,filepath)
+ *
+ * Test whether or not the file whose name is in the global curname
+ * can be opened for reading (if mode=READACCESS)
+ * or writing (if mode=WRITEACCESS).
+* [the code here isn't as general as it should be ... it assumes that stylefile
+* is the only file name of interest! Maybe I'll fix this some day -- don]
+ *
+ * The filepath argument is one of the ...FILEPATH constants defined below.
+ * If the filename given in curname does not begin with '/', we try
+ * prepending all the ':'-separated areanames in the appropriate path to the
+ * filename until access can be made, if it ever can.
+ *
+ * The realnameoffile global array will contain the name that yielded an
+ * access success.
+ */
+
+/* note that corresponding constants are defined in mft.ch */
+
+#define READACCESS 4
+#define WRITEACCESS 2
+
+#define NOFILEPATH 0
+#define GOODFILEPATH 3
+
+bool
+testaccess(amode,filepath)
+ int amode,filepath;
+{
+ register bool ok;
+ register char *p;
+ char *curpathplace;
+ int f;
+
+ switch(filepath) {
+ case NOFILEPATH: curpathplace = NULL; break;
+ case GOODFILEPATH: curpathplace = inputpath; break;
+ }
+ if (stylename[0]=='/') /* file name has absolute path */
+ curpathplace = NULL;
+ do {
+ packrealnameoffile(&curpathplace);
+ if (amode==READACCESS)
+ /* use system call "access" to see if we could read it */
+ if (access(realnameoffile,READACCESS)==0) ok = TRUE;
+ else ok = FALSE;
+ else {
+ /* WRITEACCESS: use creat to see if we could create it, but close
+ the file again if we're OK, to let pc open it for real */
+ f = creat(realnameoffile,0666);
+ if (f>=0) ok = TRUE;
+ else ok = FALSE;
+ if (ok)
+ close(f);
+ }
+ } while (!ok && curpathplace != NULL);
+ if (ok) { /* pad realnameoffile with blanks, as Pascal wants */
+ for (p = realnameoffile; *p != '\0'; p++)
+ /* nothing: find end of string */ ;
+ while (p < &(realnameoffile[maxfilenamelength]))
+ *p++ = ' ';
+ }
+ return (ok);
+}
+
+/*
+ * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp,
+ * followed by '/', followed by the characters in stylename up until the
+ * first blank there, and finally a '\0'. The cpp pointer is left pointing
+ * at the next directory in the path.
+ * But: if *cpp == NULL, then we are supposed to use stylename as is.
+ */
+packrealnameoffile(cpp)
+ char **cpp;
+{
+ register char *p,*realname;
+
+ realname = realnameoffile;
+ if ((p = *cpp)!=NULL) {
+ while ((*p != ':') && (*p != '\0')) {
+ *realname++ = *p++;
+ if (realname == &(realnameoffile[maxfilenamelength-1]))
+ break;
+ }
+ if (*p == '\0') *cpp = NULL; /* at end of path now */
+ else *cpp = p+1; /* else get past ':' */
+ *realname++ = '/'; /* separate the area from the name to follow */
+ }
+ /* now append stylename to realname... */
+ p = stylename;
+ while (*p != ' ') {
+ if (realname >= &(realnameoffile[maxfilenamelength-1])) {
+ fprintf(stderr,"! Full file name is too long\n");
+ break;
+ }
+ *realname++ = *p++;
+ }
+ *realname = '\0';
+}
+
+bool testreadaccess(fn)
+ char *fn;
+{
+ register char *p;
+
+ fn[maxfilenamelength-1] = ' ';
+ p = fn;
+ while (*p++ != ' ');
+ p--;
+ *p = '\0';
+ if (access(fn,4)==0) {
+ *p = ' '; return(TRUE);}
+ else {*p = ' '; return(FALSE);}
+}
diff --git a/systems/knuth/local/mfware-sparc/mft_ext.h b/systems/knuth/local/mfware-sparc/mft_ext.h
new file mode 100644
index 0000000000..85090d43e6
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/mft_ext.h
@@ -0,0 +1,16 @@
+procedure exit(x : integer);
+ external;
+procedure lineread(var f:textfile);
+ external;
+procedure linewrite(var f:textfile; cnt:integer);
+ external;
+function testeof(var f:textfile):boolean;
+ external;
+(* now, some routines to implement paths for file names. These are borrowed
+ from Howard Trickey's dvitype *)
+procedure setpaths;
+ external;
+function testaccess(accessmode:integer; filepath:integer): boolean;
+ external;
+function testreadaccess(var f:UNIXfilename):boolean;
+ external;
diff --git a/systems/knuth/local/mfware-sparc/pktype.ch b/systems/knuth/local/mfware-sparc/pktype.ch
new file mode 100644
index 0000000000..0d8e605ca1
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/pktype.ch
@@ -0,0 +1,241 @@
+% Change file for the PKtype processor, for use on Berkeley UNIX systems.
+
+% created by don on 21 Oct 1989 based on GFtype.ch
+% brought up to version 2.3 by don on 18 Nov 89
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{PK$\,$\lowercase{type} changes for Berkeley {\mc UNIX}}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is PKtype, Version 2.3' {printed when the program starts}
+@y
+@d banner=='This is PKtype, Version 2.3 for SunOS'
+ {printed when the program starts}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Case statement default feature of SunOS Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] Redirect PKtype output to correct output file
+% depending on pkout_active value (consistent with GFtype)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print_ln(#)==write_ln(output,#)
+@d print(#)==write(output,#)
+@d t_print_ln(#)==write_ln(typ_file,#)
+@d t_print(#)==write(typ_file,#)
+@y
+@d print_ln(#)==if pkout_active then write_ln(typ_file,#)@+else write_ln(#)
+@d print(#)==if pkout_active then write(typ_file,#)@+else write(#)
+@d t_print_ln==print_ln
+@d t_print==print
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] Include external header and parse the command line
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@y
+var @<Globals in the outer block@>@/
+@\@=#include "gftopk_ext.h"@>@\
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin @<Set initial values@>@/
+ @<Parse command line arguments@>;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6] Make name_size match the external C routine
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!name_length=80; {maximum length of a file name}
+@y
+@!name_length=100; {maximum length of a file name}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Have abort print only once
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d abort(#)==begin print_ln(' ',#); t_print_ln(' ',#); jump_out; end
+@y
+@d abort(#)==begin print_ln(' ',#); jump_out; end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [30] Change definition of 'byte_file' to correct subrange
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!byte_file=packed file of eight_bits ; {for packed file words}
+@y
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@!UNIX_file_name=packed array[1..name_length] of char;
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [32] Change to file opening
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+specifies the file name. If |eof(f)| is true immediately after
+|reset(f,s)| has acted, we assume that no file named |s| is accessible.
+@^system dependencies@>
+
+@p procedure open_pk_file; {prepares the input for reading}
+begin reset(pk_file,pk_name);
+@y
+specifies the file name.
+
+The local /PASCAL/ runtimes will dump core if the file isn't present.
+We avoid that by using an external procedure.
+@^system dependencies@>
+
+@p procedure open_pk_file; {prepares the input for reading}
+begin if testreadaccess(pk_name) then reset(pk_file,pk_name)
+else abort('Can''t read the PK file');
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [33] Slight change needed by fussy Pascal compiler
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!pk_name,@!typ_name:packed array[1..name_length] of char; {name of input
+ and output files}
+@y
+@!pk_name,@!typ_name:UNIX_file_name; {name of input and output files}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [34] Use modified routine to access pk_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function pk_byte : eight_bits ;
+var temp : eight_bits ;
+begin
+ temp := pk_file^ ;
+ get(pk_file) ;
+ incr(pk_loc) ;
+ pk_byte := temp ;
+@y
+@p function pk_byte : eight_bits ;
+var temp : -128..127;
+begin
+ temp := pk_file^ ;
+ get(pk_file) ;
+ incr(pk_loc) ;
+ if temp<0 then pk_byte:=temp+256 @+else pk_byte := temp ;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [35] Don't open the output file twice
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+open_typ_file ;
+t_print_ln(banner) ;
+@y
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [53--54] Use command line instead of dialog
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* Terminal communication.
+We must get the file names and determine whether input is to be in
+hexadecimal or binary. To do this, we use the standard input path
+name. We need a procedure to flush the input buffer. For most systems,
+this will be an empty statement. For other systems, a |print_ln| will
+provide a quick fix. We also need a routine to get a line of input from
+the terminal. On some systems, a simple |read_ln| will do. Finally,
+a macro to print a string to the first blank is required.
+
+@d flush_buffer == begin end
+@d get_line(#) == if eoln(input) then read_ln(input) ;
+ i := 1 ;
+ while not (eoln(input) or eof(input)) do begin
+ #[i] := input^ ;
+ incr(i) ;
+ get(input) ;
+ end ;
+ #[i] := ' '
+
+@ @p procedure dialog ;
+var i : integer ; {index variable}
+buffer : packed array [1..name_length] of char; {input buffer}
+begin
+ for i := 1 to name_length do begin
+ typ_name[i] := ' ' ;
+ pk_name[i] := ' ' ;
+ end;
+ print('Input file name: ') ;
+ flush_buffer ;
+ get_line(pk_name) ;
+ print('Output file name: ') ;
+ flush_buffer ;
+ get_line(typ_name) ;
+end ;
+@y
+@* Command line parsing.
+The command line issued to start \.{PKtype} should also contain the
+names of the input (PK) file, and optionally, the name of an output
+file to receive the output from \.{PKtype}. This segment decodes
+the command line, and saves the file names in the appropriate variables.
+@^system dependencies@>
+
+@d dialog== {we omit the dialog}
+
+@<Parse command...@>=
+if (argc < 2) or (argc > 3) then
+ begin print_ln('Usage: pktype PKfile [outputfile]');@/
+ goto final_end; { we will be unstructured just this once }
+ end;
+argv(1, pk_name);
+if argc > 2 then
+ begin argv(2, typ_name);@/
+ rewrite(typ_file,typ_name);@/
+ pkout_active := true;@/
+ write_ln(banner); {standard output gets only this}
+ end;
+print_ln(banner)
+
+@ @<Glob...@>=
+@!pkout_active: boolean; {has the |typ_file| been opened?}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [56] Final touchup
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{PKtype} work at a particular installation.
+Any additional routines should be inserted here.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{PKtype} work at a particular installation.
+Any additional routines should be inserted here.
+@^system dependencies@>
+
+We need to initialize the value of the new boolean variable that
+we introduced.
+
+@<Set initial ...@>=
+pkout_active:=false;
+@z
+
diff --git a/systems/knuth/local/mfware-sparc/pktype.web b/systems/knuth/local/mfware-sparc/pktype.web
new file mode 100644
index 0000000000..eac85ee36f
--- /dev/null
+++ b/systems/knuth/local/mfware-sparc/pktype.web
@@ -0,0 +1,1158 @@
+% PKtype.web
+%
+% PKtype verifies the correctness of a PK file.
+%
+% Preliminary 0.0 version: May, 1985
+% First release, 0.9 version: 8 May 1985
+% Updated to reflect new pk format, 2.0 version: 25 July 1985
+% Updated again for new pk format, 2.1 version: 15 August 1985
+% Docmentation updated, 2.2 version: 17 November 1987
+%
+% slight editing to correspond with MFware report -- don, 21 Oct 89
+% additional editing suggested by P Breitenlohner -- don, 18 Nov 89
+% Corrected two typos -- 21 Dec 96 (don)
+
+\def\versiondate{18 November 1989}
+%
+\def\hang{\hangindent 3em\noindent\ignorespaces}
+\font\ninerm=cmr9
+\font\logo=logo10 % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\def\PASCAL{Pascal}
+\def\tamu{Texas A\char38 M}
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+\def\title{PKtype}
+\def\contentspagenumber{0}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont PKtype} processor}
+ \vskip 15pt
+ \centerline{(Version 2.3, \versiondate)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926 and MCS-8300984,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+This program reads a \.{PK} file, verifies that it is in the correct
+format, and writes it in textual format.
+
+@ The |banner| string defined here should be changed whenever \.{PKtype}
+gets modified.
+
+@d banner=='This is PKtype, Version 2.3' {printed when the program starts}
+
+@ This program is written in standard \PASCAL, except where it is necessary
+to use extensions; for example, \.{PKtype} must read files whose names
+are dynamically specified, and that would be impossible in pure \PASCAL.
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ Both the input and output come from binary files. On line interaction
+is handled through \PASCAL's standard |input| and |output| files. Two macros
+are used to write to the type file, so this output can easily be redirected.
+
+@d print_ln(#)==write_ln(output,#)
+@d print(#)==write(output,#)
+@d t_print_ln(#)==write_ln(typ_file,#)
+@d t_print(#)==write(typ_file,#)
+
+@p program PKtype(@!input,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ If the program has to stop prematurely, it goes to the
+`|final_end|'.
+
+@d final_end=9999 {label for the end of it all}
+
+@<Labels...@>=final_end;
+
+@ These constants determine the maximum length of a file name and the length
+of the terminal line, as well as the widest character that can be translated.
+@^system dependencies@>
+
+@<Constants...@>=
+@!name_length=80; {maximum length of a file name}
+@!terminal_line_length=132; {maximum length of an input line}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d do_nothing == {empty statement}
+
+@ It is possible that a malformed packed file (heaven forbid!) or some other
+error might be detected by this program. Such errors might occur in a deeply
+nested procedure, so the procedure called |jump_out| has been added to transfer
+to the very end of the program with an error message.
+
+@d abort(#)==begin print_ln(' ',#); t_print_ln(' ',#); jump_out; end
+
+@p procedure jump_out;
+begin goto final_end;
+end;
+
+@* The character set.
+Like all programs written with the \.{WEB} system, \.{PKtype} can be
+used with any character set. But it uses ASCII code internally, because
+the programming for portable input-output is easier when a fixed internal
+code is used.
+
+The next few sections of \.{PKtype} have therefore been copied from the
+analogous ones in the \.{WEB} system routines. They have been considerably
+simplified, since \.{PKtype} need not deal with the controversial
+ASCII codes less than @'40 or greater than @'176.
+
+@<Types...@>=
+@!ASCII_code=" ".."~"; {a subrange of the integers}
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lower case
+letters. Nowadays, of course, we need to deal with both upper and lower case
+alphabets in a convenient way, especially in a program like \.{PKtype}.
+So we shall assume that the \PASCAL\ system being used for \.{PKtype}
+has a character set containing at least the standard visible characters
+of ASCII code (|"!"| through |"~"|).
+
+Some \PASCAL\ compilers use the original name |char| for the data type
+associated with the characters in text files, while other \PASCAL s
+consider |char| to be a 64-element subrange of a larger data type that has
+some other name. In order to accommodate this difference, we shall use
+the name |text_char| to stand for the data type of the characters in the
+output file. We shall also assume that |text_char| consists of
+the elements |chr(first_text_char)| through |chr(last_text_char)|,
+inclusive. The following definitions should be adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=127 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+
+@ The \.{PKtype} processor converts 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...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [0..255] of text_char;
+ {specifies conversion of output characters}
+
+@ Under our assumption that the visible characters of standard ASCII are
+all present, the following assignment statements initialize the
+|xchr| array properly, without needing any system-dependent changes.
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:='?';
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';
+for i:=@'177 to 255 do xchr[i]:='?';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
+for i:=" " to "~" do xord[xchr[i]]:=i;
+
+@* Packed file format.
+The packed file format is a compact representation of the data contained in a
+\.{GF} file. The information content is the same, but packed (\.{PK}) files
+are almost always less than half the size of their \.{GF} counterparts. They
+are also easier to convert into a raster representation because they do not
+have a profusion of \\{paint}, \\{skip}, and \\{new\_row} commands to be
+separately interpreted. In addition, the \.{PK} format expressly forbids
+\&{special} commands within a character. The minimum bounding box for each
+character is explicit in the format, and does not need to be scanned for as in
+the \.{GF} format. Finally, the width and escapement values are combined with
+the raster information into character ``packets'', making it simpler in many
+cases to process a character.
+
+A \.{PK} file is organized as a stream of 8-bit bytes. At times, these bytes
+might be split into 4-bit nybbles or single bits, or combined into multiple
+byte parameters. When bytes are split into smaller pieces, the `first' piece
+is always the most significant of the byte. For instance, the first bit of
+a byte is the bit with value 128; the first nybble can be found by dividing
+a byte by 16. Similarly, when bytes are combined into multiple byte
+parameters, the first byte is the most significant of the parameter. If the
+parameter is signed, it is represented by two's-complement notation.
+
+The set of possible eight-bit values is separated into two sets, those that
+introduce a character definition, and those that do not. The values that
+introduce a character definition range from 0 to 239; byte values
+above 239 are interpreted as commands. Bytes that introduce character
+definitions are called flag bytes, and various fields within the byte indicate
+various things about how the character definition is encoded. Command bytes
+have zero or more parameters, and can never appear within a character
+definition or between parameters of another command, where they would be
+interpeted as data.
+
+A \.{PK} file consists of a preamble, followed by a sequence of one or more
+character definitions, followed by a postamble. The preamble command must
+be the first byte in the file, followed immediately by its parameters.
+Any number of character definitions may follow, and any command but the
+preamble command and the postamble command may occur between character
+definitions. The very last command in the file must be the postamble.
+
+@ The packed file format is intended to be easy to read and interpret by
+device drivers. The small size of the file reduces the input/output overhead
+each time a font is loaded. For those drivers that load and save each font
+file into memory, the small size also helps reduce the memory requirements.
+The length of each character packet is specified, allowing the character raster
+data to be loaded into memory by simply counting bytes, rather than
+interpreting each command; then, each character can be interpreted on a demand
+basis. This also makes it possible for a driver to skip a particular
+character quickly if it knows that the character is unused.
+
+@ First, the command bytes will be presented; then the format of the
+character definitions will be defined. Eight of the possible sixteen
+commands (values 240 through 255) are currently defined; the others are
+reserved for future extensions. The commands are listed below. Each command
+is specified by its symbolic name (e.g., \\{pk\_no\_op}), its opcode byte,
+and any parameters. The parameters are followed by a bracketed number
+telling how many bytes they occupy, with the number preceded by a plus sign if
+it is a signed quantity. (Four byte quantities are always signed, however.)
+
+\yskip\hang|pk_xxx1| 240 |k[1]| |x[k]|. This command is undefined in general;
+it functions as a $(k+2)$-byte \\{no\_op} unless special \.{PK}-reading
+programs are being used. \MF\ generates \\{xxx} commands when encountering
+a \&{special} string. It is recommended that |x| be a string having the form
+of a keyword followed by possible parameters relevant to that keyword.
+
+\yskip\hang\\{pk\_xxx2} 241 |k[2]| |x[k]|. Like |pk_xxx1|, but |0<=k<65536|.
+
+\yskip\hang\\{pk\_xxx3} 242 |k[3]| |x[k]|. Like |pk_xxx1|, but
+|0<=k<@t$2^{24}$@>|. \MF\ uses this when sending a \&{special} string whose
+length exceeds~255.
+
+\yskip\hang\\{pk\_xxx4} 243 |k[4]| |x[k]|. Like |pk_xxx1|, but |k| can be
+ridiculously large; |k| musn't be negative.
+
+\yskip\hang|pk_yyy| 244 |y[4]|. This command is undefined in general; it
+functions as a five-byte \\{no\_op} unless special \.{PK} reading programs
+are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a result of
+\&{numspecial} commands; the intent is to provide numeric parameters to
+\\{xxx} commands that immediately precede.
+
+\yskip\hang|pk_post| 245. Beginning of the postamble. This command is
+followed by enough |pk_no_op| commands to make the file a multiple
+of four bytes long. Zero through three bytes are usual, but any number
+is allowed.
+This should make the file easy to read on machines that pack four bytes to
+a word.
+
+\yskip\hang|pk_no_op| 246. No operation, do nothing. Any number of
+|pk_no_op|'s may appear between \.{PK} commands, but a |pk_no_op| cannot be
+inserted between a command and its parameters, between two parameters, or
+inside a character definition.
+
+\yskip\hang|pk_pre| 247 |i[1]| |k[1]| |x[k]| |ds[4]| |cs[4]| |hppp[4]|
+|vppp[4]|. Preamble command. Here, |i| is the identification byte of the
+file, currently equal to 89. The string |x| is merely a comment, usually
+indicating the source of the \.{PK} file. The parameters |ds| and |cs| are
+the design size of the file in $1/2^{20}$ points, and the checksum of the
+file, respectively. The checksum should match the \.{TFM} file and the
+\.{GF} files for this font. Parameters |hppp| and |vppp| are the ratios
+of pixels per point, horizontally and vertically, multiplied by $2^{16}$; they
+can be used to correlate the font with specific device resolutions,
+magnifications, and ``at sizes''. Usually, the name of the \.{PK} file is
+formed by concatenating the font name (e.g., cmr10) with the resolution at
+which the font is prepared in pixels per inch multiplied by the magnification
+factor, and the letters \.{pk}. For instance, cmr10 at 300 dots per inch
+should be named \.{cmr10.300pk}; at one thousand dots per inch and magstephalf,
+it should be named \.{cmr10.1095pk}.
+
+@ We put a few of the above opcodes into definitions for symbolic use by
+this program.
+
+@d pk_id = 89 {the version of \.{PK} file described}
+@d pk_xxx1 = 240 {\&{special} commands}
+@d pk_yyy = 244 {\&{numspecial} commands}
+@d pk_post = 245 {postamble}
+@d pk_no_op = 246 {no operation}
+@d pk_pre = 247 {preamble}
+@d pk_undefined == 248, 249, 250, 251, 252, 253, 254, 255
+
+@ The \.{PK} format has two conflicting goals: to pack character raster and
+size information as compactly as possible, while retaining ease of translation
+into raster and other forms. A suitable compromise was found in the use of
+run-encoding of the raster information. Instead of packing the individual
+bits of the character, we instead count the number of consecutive `black' or
+`white' pixels in a horizontal raster row, and then encode this number. Run
+counts are found for each row from left to right, traversing rows from the
+top to bottom. This is essentially the way the \.{GF} format works.
+Instead of presenting each row individually, however, we concatenate all
+of the horizontal raster rows into one long string of pixels, and encode this
+row. With knowledge of the width of the bit-map, the original character glyph
+can easily be reconstructed. In addition, we do not need special commands to
+mark the end of one row and the beginning of the next.
+
+Next, we place the burden of finding the minimum bounding box on the part
+of the font generator, since the characters will usually be used much more
+often than they are generated. The minimum bounding box is the smallest
+rectangle that encloses all `black' pixels of a character. We also
+eliminate the need for a special end of character marker, by supplying
+exactly as many bits as are required to fill the minimum bounding box, from
+which the end of the character is implicit.
+
+Let us next consider the distribution of the run counts. Analysis of several
+dozen pixel files at 300 dots per inch yields a distribution peaking at four,
+falling off slowly until ten, then a bit more steeply until twenty, and then
+asymptotically approaching the horizontal. Thus, the great majority of our
+run counts will fit in a four-bit nybble. The eight-bit byte is attractive for
+our run-counts, as it is the standard on many systems; however, the wasted four
+bits in the majority of cases seem a high price to pay. Another possibility
+is to use a Huffman-type encoding scheme with a variable number of bits for
+each run-count; this was rejected because of the overhead in fetching and
+examining individual bits in the file. Thus, the character raster definitions
+in the \.{PK} file format are based on the four-bit nybble.
+
+@ An analysis of typical pixel files yielded another interesting statistic:
+Fully 37\char`\%\
+of the raster rows were duplicates of the previous row. Thus, the \.{PK}
+format allows the specification of repeat counts, which indicate how many times
+a horizontal raster row is to be repeated. These repeated rows are taken out
+of the character glyph before individual rows are concatenated into the long
+string of pixels.
+
+For elegance, we disallow a run count of zero. The case of a null raster
+description should be gleaned from the character width and height being equal
+to zero, and no raster data should be read. No other zero counts are ever
+necessary. Also, in the absence of repeat counts, the repeat value is set to
+be zero (only the original row is sent.) If a repeat count is seen, it takes
+effect on the current row. The current row is defined as the row on which the
+first pixel of the next run count will lie. The repeat count is set back to
+zero when the last pixel in the current row is seen, and the row is sent out.
+
+This poses a problem for entirely black and entirely white rows, however. Let
+us say that the current row ends with four white pixels, and then we have five
+entirely empty rows, followed by a black pixel at the beginning of the next
+row, and the character width is ten pixels. We would like to use a repeat
+count, but there is no legal place to put it. If we put it before the white
+run count, it will apply to the current row. If we put it after, it applies
+to the row with the black pixel at the beginning. Thus, entirely white or
+entirely black repeated rows are always packed as large run counts (in this
+case, a white run count of 54) rather than repeat counts.
+
+@ Now we turn our attention to the actual packing of the run counts and
+repeat counts into nybbles. There are only sixteen possible nybble values.
+We need to indicate run counts and repeat counts. Since the run counts are
+much more common, we will devote the majority of the nybble values to them.
+We therefore indicate a repeat count by a nybble of 14 followed by a packed
+number, where a packed number will be explained later. Since the repeat
+count value of one is so common, we indicate a repeat one command by a single
+nybble of 15. A 14 followed by the packed number 1 is still legal for a
+repeat one count. The run counts are coded directly as packed
+numbers.
+
+For packed numbers, therefore, we have the nybble values 0 through 13. We
+need to represent the positive integers up to, say, $2^{31}-1$. We would
+like the more common smaller numbers to take only one or two nybbles, and
+the infrequent large numbers to take three or more. We could therefore
+allocate one nybble value to indicate a large run count taking three or more
+nybbles. We do this with the value 0.
+
+@ We are left with the values 1 through 13. We can allocate some of these, say
+|dyn_f|, to be one-nybble run counts.
+These will work for the run counts |1..dyn_f|. For subsequent run
+counts, we will use a nybble greater than |dyn_f|, followed by a second nybble,
+whose value can run from 0 through 15. Thus, the two-nybble values will
+run from |dyn_f+1..(13-dyn_f)*16+dyn_f|. We have our definition of large run
+count values now, being all counts greater than |(13-dyn_f)*16+dyn_f|.
+
+We can analyze our several dozen pixel files and determine an optimal value of
+|dyn_f|, and use this value for all of the characters. Unfortunately, values
+of |dyn_f| that pack small characters well tend to pack the large characters
+poorly, and values that pack large characters well are not efficient for the
+smaller characters. Thus, we choose the optimal |dyn_f| on a character basis,
+picking the value that will pack each individual character in the smallest
+number of nybbles. Legal values of |dyn_f| run from 0 (with no one-nybble run
+counts) to 13 (with no two-nybble run counts).
+
+@ Our only remaining task in the coding of packed numbers is the large run
+counts. We use a scheme suggested by D.~E.~Knuth
+@^Knuth, Donald Ervin@>
+that simply and elegantly represents arbitrarily large values. The
+general scheme to represent an integer |i| is to write its hexadecimal
+representation, with leading zeros removed. Then we count the number of
+digits, and prepend one less than that many zeros before the hexadecimal
+representation. Thus, the values from one to fifteen occupy one nybble;
+the values sixteen through 255 occupy three, the values 256 through 4095
+require five, etc.
+
+For our purposes, however, we have already represented the numbers one
+through |(13-dyn_f)*16+dyn_f|. In addition, the one-nybble values have
+already been taken by our other commands, which means that only the values
+from sixteen up are available to us for long run counts. Thus, we simply
+normalize our long run counts, by subtracting |(13-dyn_f)*16+dyn_f+1| and
+adding 16, and then we represent the result according to the scheme above.
+
+@ The final algorithm for decoding the run counts based on the above scheme
+looks like this, assuming that a procedure called \\{get\_nyb} is available
+to get the next nybble from the file, and assuming that the global
+|repeat_count| indicates whether a row needs to be repeated. Note that this
+routine is recursive, but since a repeat count can never directly follow
+another repeat count, it can only be recursive to one level.
+
+@<Packed number procedure@>=
+function pk_packed_num : integer ;
+var i, @!j : integer ;
+begin
+ i := get_nyb ;
+ if i = 0 then begin
+ repeat j := get_nyb ; incr(i) ; until j <> 0 ;
+ while i > 0 do begin j := j * 16 + get_nyb ; decr(i) ; end ;
+ pk_packed_num := j - 15 + (13-dyn_f)*16 + dyn_f ;
+ end else if i <= dyn_f then
+ pk_packed_num := i
+ else if i < 14 then
+ pk_packed_num := (i-dyn_f-1)*16+get_nyb+dyn_f+1
+ else begin
+ if repeat_count <> 0 then abort('Second repeat count for this row!') ;
+@.Second repeat count...@>
+ repeat_count := 1; {prevent recursion more than one level}
+ if i = 14 then repeat_count := pk_packed_num;
+ send_out(true, repeat_count) ;
+ pk_packed_num := pk_packed_num ;
+ end ;
+end ;
+
+@ For low resolution fonts, or characters with `gray' areas, run encoding can
+often make the character many times larger. Therefore, for those characters
+that cannot be encoded efficiently with run counts, the \.{PK} format allows
+bit-mapping of the characters. This is indicated by a |dyn_f| value of
+14. The bits are packed tightly, by concatenating all of the horizontal raster
+rows into one long string, and then packing this string eight bits to a byte.
+The number of bytes required can be calculated by |(width*height+7) div 8|.
+This format should only be used when packing the character by run counts takes
+more bytes than this, although, of course, it is legal for any character.
+Any extra bits in the last byte should be set to zero.
+
+@ At this point, we are ready to introduce the format for a character
+descriptor. It consists of three parts: a flag byte, a character preamble,
+and the raster data. The most significant four bits of the flag byte
+yield the |dyn_f| value for that character. (Notice that only values of
+0 through 14 are legal for |dyn_f|, with 14 indicating a bit mapped character;
+thus, the flag bytes do not conflict with the command bytes, whose upper nybble
+is always 15.) The next bit (with weight 8) indicates whether the first run
+count is a black count or a white count, with a one indicating a black count.
+For bit-mapped characters, this bit should be set to a zero. The next bit
+(with weight 4) indicates whether certain later parameters (referred to as size
+parameters) are given in one-byte or two-byte quantities, with a one indicating
+that they are in two-byte quantities. The last two bits are concatenated on to
+the beginning of the packet-length parameter in the character preamble,
+which will be explained below.
+
+However, if the last three bits of the flag byte are all set (normally
+indicating that the size parameters are two-byte values and that a 3 should be
+prepended to the length parameter), then a long format of the character
+preamble should be used instead of one of the short forms.
+
+Therefore, there are three formats for the character preamble; the one that
+is used depends on the least significant three bits of the flag byte. If the
+least significant three bits are in the range zero through three, the short
+format is used. If they are in the range four through six, the extended short
+format is used. Otherwise, if the least significant bits are all set, then
+the long form of the character preamble is used. The preamble formats are
+explained below.
+
+\yskip\hang Short form: |flag[1]| |pl[1]| |cc[1]| |tfm[3]| |dm[1]| |w[1]|
+|h[1]| |hoff[+1]| |voff[+1]|.
+If this format of the character preamble is used, the above
+parameters must all fit in the indicated number of bytes, signed or unsigned
+as indicated. Almost all of the standard \TeX\ font characters fit; the few
+exceptions are fonts such as \.{cminch}.
+
+\yskip\hang Extended short form: |flag[1]| |pl[2]| |cc[1]| |tfm[3]| |dm[2]|
+|w[2]| |h[2]| |hoff[+2]| |voff[+2]|. Larger characters use this extended
+format.
+
+\yskip\hang Long form: |flag[1]| |pl[4]| |cc[4]| |tfm[4]| |dx[4]| |dy[4]|
+|w[4]| |h[4]| |hoff[4]| |voff[4]|. This is the general format that
+allows all of the
+parameters of the \.{GF} file format, including vertical escapement.
+\vskip\baselineskip
+The |flag| parameter is the flag byte. The parameter |pl| (packet length)
+contains the offset
+of the byte following this character descriptor, with respect to the beginning
+of the |tfm| width parameter. This is given so a \.{PK} reading program can,
+once it has read the flag byte, packet length, and character code (|cc|), skip
+over the character by simply reading this many more bytes. For the two short
+forms of the character preamble, the last two bits of the flag byte should be
+considered the two most-significant bits of the packet length. For the short
+format, the true packet length might be calculated as |(flag mod 4)*256+pl|;
+for the short extended format, it might be calculated as
+|(flag mod 4)*65536+pl|.
+
+The |w| parameter is the width and the |h| parameter is the height in pixels
+of the minimum bounding box. The |dx| and |dy| parameters are the horizontal
+and vertical escapements, respectively. In the short formats, |dy| is assumed
+to be zero and |dm| is |dx| but in pixels;
+in the long format, |dx| and |dy| are both
+in pixels multiplied by $2^{16}$. The |hoff| is the horizontal offset from the
+upper left pixel to the reference pixel; the |voff| is the vertical offset.
+They are both given in pixels, with right and down being positive. The
+reference pixel is the pixel that occupies the unit square in \MF; the
+\MF\ reference point is the lower left hand corner of this pixel. (See the
+example below.)
+
+@ \TeX\ requires all characters that have the same character codes
+modulo 256 to have also the same |tfm| widths and escapement values. The \.{PK}
+format does not itself make this a requirement, but in order for the font to
+work correctly with the \TeX\ software, this constraint should be observed.
+(The standard version of \TeX\ cannot output character codes greater
+than 255, but extended versions do exist.)
+
+Following the character preamble is the raster information for the
+character, packed by run counts or by bits, as indicated by the flag byte.
+If the character is packed by run counts and the required number of nybbles
+is odd, then the last byte of the raster description should have a zero
+for its least significant nybble.
+
+@ As an illustration of the \.{PK} format, the character \char4\ from the font
+amr10 at 300 dots per inch will be encoded. This character was chosen
+because it illustrates some
+of the borderline cases. The raster for the character looks like this (the
+row numbers are chosen for convenience, and are not \MF's row numbers.)
+
+\vskip\baselineskip
+{\def\smbox{\vrule height 7pt width 7pt depth 0pt \hskip 3pt}%
+\catcode`\*=\active \let*=\smbox
+\centerline{\vbox{\baselineskip=10pt
+\halign{\hfil#\quad&&\hfil#\hfil\cr
+0& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+1& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+2& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+3& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+4& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+5& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+6& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+7\cr
+8\cr
+9& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+10& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+11& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+12& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+13& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+14& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+15& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+16& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+17& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+18& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+19\cr
+20\cr
+21\cr
+22& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+23& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+24& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+25& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+26& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+27& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+28&+& &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+&\hphantom{*}&\hphantom{*}\cr
+}}}}
+The width of the minimum bounding box for this character is 20; its height
+is 29. The `+' represents the reference pixel; notice how it lies outside the
+minimum bounding box. The |hoff| value is $-2$, and the |voff| is~28.
+
+The first task is to calculate the run counts and repeat counts. The repeat
+counts are placed at the first transition (black to white or white to black)
+in a row, and are enclosed in brackets. White counts are enclosed in
+parentheses. It is relatively easy to generate the counts list:
+\vskip\baselineskip
+\centerline{82 [2] (16) 2 (42) [2] 2 (12) 2 (4) [3]}
+\centerline{16 (4) [2] 2 (12) 2 (62) [2] 2 (16) 82}
+\vskip\baselineskip
+Note that any duplicated rows that are not all white or all black are removed
+before the run counts are calculated. The rows thus removed are rows 5, 6,
+10, 11, 13, 14, 15, 17, 18, 23, and 24.
+
+@ The next step in the encoding of this character is to calculate the optimal
+value of |dyn_f|. The details of how this calculation is done are not
+important here; suffice it to say that there is a simple algorithm that can
+determine the best value of |dyn_f| in one pass over the count list. For this
+character, the optimal value turns out to be 8 (atypically low). Thus, all
+count values less than or equal to 8 are packed in one nybble; those from
+nine to $(13-8)*16+8$ or 88 are packed in two nybbles. The run encoded values
+now become (in hex, separated according to the above list):
+\vskip\baselineskip
+\centerline{\tt D9 E2 97 2 B1 E2 2 93 2 4 E3}
+\centerline{\tt 97 4 E2 2 93 2 C5 E2 2 97 D9}
+\vskip\baselineskip\noindent
+which comes to 36 nybbles, or 18 bytes. This is shorter than the 73 bytes
+required for the bit map, so we use the run count packing.
+
+@ The short form of the character preamble is used because all of the
+parameters fit in their respective lengths. The packet length is therefore
+18 bytes for the raster, plus
+eight bytes for the character preamble parameters following the character
+code, or 26. The |tfm| width for this character is 640796, or {\tt 9C71C} in
+hexadecimal. The horizontal escapement is 25 pixels. The flag byte is
+88 hex, indicating the short preamble, the black first count, and the
+|dyn_f| value of 8. The final total character packet, in hexadecimal, is:
+\vskip\baselineskip
+$$\vbox{\halign{\hfil #\quad&&{\tt #\ }\cr
+Flag byte&88\cr
+Packet length&1A\cr
+Character code&04\cr
+|tfm| width&09&C7&1C\cr
+Horizontal escapement (pixels)&19\cr
+Width of bit map&14\cr
+Height of bit map&1D\cr
+Horizontal offset (signed)&FE\cr
+Vertical offset&1C\cr
+Raster data&D9&E2&97\cr
+&2B&1E&22\cr
+&93&24&E3\cr
+&97&4E&22\cr
+&93&2C&5E\cr
+&22&97&D9\cr}}$$
+
+@* Input and output.
+There are two types of files that this program must deal with---standard
+text files and files of bytes (packed files.)
+For our purposes, we shall consider an eight-bit byte to consist of the
+values |0..255|. If your system does not pack these values to a byte, it is
+no major difficulty; you must only insure that the input function
+|pk_byte| can read packed bytes.
+
+@<Types...@>=
+@!eight_bits=0..255; {packed file byte}
+@!byte_file=packed file of eight_bits ; {for packed file words}
+@^system dependencies@>
+
+@ @<Glob...@>=
+@!pk_file:byte_file; {where the input comes from}
+@!typ_file:text_file; {where the final output goes}
+@^system dependencies@>
+
+@ To prepare these files for input and output, we |reset| and |rewrite| them.
+An extension of \PASCAL\ is needed, since we want to associate files
+with external names that are specified dynamically (i.e., not
+known at compile time). The following code assumes that `|reset(f,s)|'
+does this, when |f| is a file variable and |s| is a string variable that
+specifies the file name. If |eof(f)| is true immediately after
+|reset(f,s)| has acted, we assume that no file named |s| is accessible.
+@^system dependencies@>
+
+@p procedure open_pk_file; {prepares the input for reading}
+begin reset(pk_file,pk_name);
+pk_loc := 0 ;
+end;
+@#
+procedure open_typ_file; {prepares to write text data to the |typ_file|}
+begin rewrite(typ_file,typ_name);
+end;
+
+@ We need a place to store the names of the input and output file, as well
+as a byte counter for the output file.
+
+@<Glob...@>=
+@!pk_name,@!typ_name:packed array[1..name_length] of char; {name of input
+ and output files}
+@!pk_loc:integer; {how many bytes have we read?}
+
+@ We also need a function that will get a single byte from the \.{pk} file.
+
+@p function pk_byte : eight_bits ;
+var temp : eight_bits ;
+begin
+ temp := pk_file^ ;
+ get(pk_file) ;
+ incr(pk_loc) ;
+ pk_byte := temp ;
+end ;
+
+@ Now we are ready to open the files.
+
+@<Open files@>=
+open_pk_file ;
+open_typ_file ;
+t_print_ln(banner) ;
+t_print('Input file: ') ;
+i := 1 ;
+while pk_name[i] <> ' ' do begin
+ t_print(pk_name[i]) ; incr(i) ;
+end ;
+t_print_ln(' ')
+
+@ As we are reading the packed file, we often need to fetch 16 and 32 bit
+quantities. Here we have two procedures to do this.
+
+@p function get_16 : integer ;
+var a : integer ;
+begin a := pk_byte ; get_16 := a * 256 + pk_byte ; end ;
+@#
+function get_32 : integer ;
+var a : integer ;
+begin a := get_16 ; if a > 32767 then a := a - 65536 ;
+get_32 := a * 65536 + get_16 ; end ;
+
+@ We still need the |term_pos| variable.
+
+@<Glob...@>=
+@!term_pos : integer ; {current terminal position}
+
+@ Now we read and check the preamble of the \.{PK} file. In the preamble, we
+find the |hppp|, |design_size|, |checksum|.
+
+@<Read preamble@>=
+if pk_byte <> pk_pre then abort('Bad PK file: pre command missing!') ;
+@.pre command missing@>
+if pk_byte <> pk_id then abort('Wrong version of PK file!') ;
+@.Wrong version of PK file@>
+j := pk_byte ;
+t_print('''') ;
+for i := 1 to j do t_print(xchr[pk_byte]) ;
+t_print_ln('''') ;
+design_size := get_32 ;
+t_print_ln('Design size = ',design_size:1) ;
+checksum := get_32 ;
+t_print_ln('Checksum = ',checksum:1) ;
+hppp := get_32 ; vppp := get_32 ;
+t_print('Resolution: horizontal = ',hppp:1,' vertical = ',vppp:1) ;
+magnification := round(hppp * 72.27 / 65536) ;
+t_print_ln(' (',magnification:1,' dpi)') ;
+if hppp <> vppp then print_ln('Warning: aspect ratio not 1:1!')
+
+@ Of course, we need to define the above variables.
+
+@<Glob...@>=
+@!magnification : integer ; {resolution at which pixel file is prepared}
+@!design_size : integer ; {design size in FIXes}
+@!checksum : integer ; {checksum of pixel file}
+@!hppp, @!vppp : integer ; {horizontal and vertical points per inch}
+
+@* Character unpacking.
+Here we simply unpack the character, writing the information we glean to the
+|typ_file|.
+
+@<Unpack and write character@>=
+t_print((pk_loc-1):1,': Flag byte = ',flag_byte:1) ;
+dyn_f := flag_byte div 16 ;
+flag_byte := flag_byte mod 16 ;
+turn_on := flag_byte >= 8 ;
+if turn_on then flag_byte := flag_byte - 8 ;
+if flag_byte = 7 then
+ @<Read long character preamble@>
+else if flag_byte > 3 then
+ @<Read extended short character preamble@>
+else
+ @<Read short character preamble@> ;
+t_print_ln(' Character = ',car:1,' Packet length = ',
+ packet_length:1) ;
+t_print_ln(' Dynamic packing variable = ',dyn_f:1) ;
+t_print(' TFM width = ',tfm_width:1,' dx = ',dx:1) ;
+if dy <> 0 then t_print_ln(' dy = ',dy:1) else t_print_ln(' ') ;
+t_print_ln(' Height = ',height:1,' Width = ',width:1,' X-offset = ',
+ x_off:1, ' Y-offset = ',y_off:1) ;
+@<Read and translate raster description@> ;
+if end_of_packet <> pk_loc then abort('Bad PK file: Bad packet length!')
+@.Bad packet length@>
+
+@ We need a whole lot of globals used but not defined up there.
+
+@<Glob...@>=
+@!i, @!j : integer ; {index pointers}
+@!flag_byte : integer ; {the byte that introduces the character definition}
+@!end_of_packet : integer ; {where we expect the end of the packet to be}
+@!width, @!height : integer ; {width and height of character}
+@!x_off, @!y_off : integer ; {x and y offsets of character}
+@!tfm_width : integer ; {character tfm width}
+@!tfms : array [0..255] of integer ; {character tfm widths}
+@!dx, @!dy : integer ; {escapement values}
+@!dxs, @!dys : array [0..255] of integer ; {escapement values}
+@!status : array[0..255] of boolean ; {has the character been seen?}
+@!dyn_f : integer ; {dynamic packing variable}
+@!car : integer ; {the character we are reading}
+@!packet_length : integer ; {the length of the character packet}
+
+@ Now, the preamble reading modules. First, we have the general case: the
+long character preamble format.
+
+@<Read long character preamble@>=
+begin
+ packet_length := get_32 ; car := get_32 ;
+ end_of_packet := packet_length + pk_loc ;
+ packet_length := packet_length + 9 ;
+ tfm_width := get_32 ;
+ dx := get_32 ; dy := get_32 ;
+ width := get_32 ;
+ height := get_32 ;
+ x_off := get_32 ; y_off := get_32 ;
+end
+
+@ This module reads the character preamble with double byte parameters.
+
+@<Read extended short character preamble@>=
+begin
+ packet_length := (flag_byte - 4) * 65536 + get_16 ;
+ car := pk_byte ;
+ end_of_packet := packet_length + pk_loc ;
+ packet_length := packet_length + 4 ;
+ i := pk_byte ;
+ tfm_width := i * 65536 + get_16 ;
+ dx := get_16 * 65536 ;
+ dy := 0 ;
+ width := get_16 ;
+ height := get_16 ;
+ x_off := get_16 ; y_off := get_16 ;
+ if x_off > 32767 then x_off := x_off - 65536 ;
+ if y_off > 32767 then y_off := y_off - 65536 ;
+end
+
+@ Here we read the most common character preamble, that with single byte
+parameters.
+
+@<Read short character preamble@>=
+begin
+ packet_length := flag_byte * 256 + pk_byte ;
+ car := pk_byte ;
+ end_of_packet := packet_length + pk_loc ;
+ packet_length := packet_length + 3 ;
+ i := pk_byte ;
+ tfm_width := i * 65536 + get_16 ;
+ dx := pk_byte * 65536 ;
+ dy := 0 ;
+ width := pk_byte ;
+ height := pk_byte ;
+ x_off := pk_byte ; y_off := pk_byte ;
+ if x_off > 127 then x_off := x_off - 256 ;
+ if y_off > 127 then y_off := y_off - 256 ;
+end
+
+@ Now we have the most important part of the program, where we actually
+interpret the commands in the raster description. First of all, we need
+a procedure to get a single nybble from the file, as well as one to get
+a single bit.
+
+@p function get_nyb : integer ;
+var temp : eight_bits ;
+begin
+ if bit_weight = 0 then begin
+ input_byte := pk_byte ;
+ bit_weight := 16 ;
+ end ;
+ temp := input_byte div bit_weight ;
+ input_byte := input_byte - temp * bit_weight ;
+ bit_weight := bit_weight div 16 ;
+ get_nyb := temp ;
+end ;
+@#
+function get_bit : boolean ;
+var temp : boolean ;
+begin
+ bit_weight := bit_weight div 2 ;
+ if bit_weight = 0 then begin
+ input_byte := pk_byte ;
+ bit_weight := 128 ;
+ end ;
+ temp := input_byte >= bit_weight ;
+ if temp then
+ input_byte := input_byte - bit_weight ;
+ get_bit := temp ;
+end ;
+
+@ We also need a function to write output to the screen. We put as many
+counts on a line as possible, to reduce the volume of output. Each count
+will appear as a number, with white counts enclosed by parentheses and repeat
+counts by brackets.
+
+@p procedure send_out(@!repeat_count: boolean ; @!value : integer ) ;
+var i, len : integer ;
+begin
+ i := 10 ; len := 1 ;
+ while value >= i do begin incr(len) ; i := i * 10 ; end ;
+ if repeat_count or not turn_on then len := len + 2 ;
+ if term_pos + len > 78 then begin
+ term_pos := len + 2 ; t_print_ln(' ') ; t_print(' ') ;
+ end else term_pos := term_pos + len ;
+ if repeat_count then t_print('[', value:1, ']')
+ else if turn_on then t_print(value:1)
+ else t_print('(', value:1, ')') ;
+end ;
+@<Packed number procedure@>
+
+@ Now, the globals to help communication between these procedures.
+
+@<Glob...@>=
+@!input_byte : eight_bits ; {the byte we are currently decimating}
+@!bit_weight : eight_bits ; {weight of the current bit}
+@!nybble : eight_bits ; {the current nybble}
+
+@ And the main procedure.
+
+@<Read and translate raster description@>=
+bit_weight := 0 ;
+if dyn_f = 14 then
+ @<Get raster by bits@>
+else @<Create normally packed raster@>
+
+@ If |dyn_f=14|, then we need to get the raster representation
+one bit at a time.
+
+@<Get raster by bits@>=
+begin
+for i := 1 to height do begin
+ t_print(' ') ;
+ for j := 1 to width do
+ if get_bit then t_print('*')
+ else t_print('.') ;
+ t_print_ln(' ') ;
+end;
+end
+
+@ Otherwise, we translate the bit counts into the raster rows. |count|
+contains the number of bits of the current color, and |turn_on| indicates
+whether or not they should be black. |rows_left| contains the number of
+rows to be sent.
+
+@<Create normally packed raster@>=
+begin
+term_pos := 2 ;
+t_print(' ') ;
+rows_left := height ;
+h_bit := width ;
+repeat_count := 0 ;
+while rows_left > 0 do begin
+ count := pk_packed_num ;
+ send_out(false, count) ;
+ if count >= h_bit then begin
+ rows_left := rows_left - repeat_count - 1 ;
+ repeat_count := 0 ;
+ count := count - h_bit ;
+ h_bit := width ;
+ rows_left := rows_left - count div width ;
+ count := count mod width ;
+ end ;
+ h_bit := h_bit - count ;
+ turn_on := not turn_on ;
+end ;
+t_print_ln(' ') ;
+if (rows_left <> 0) or (h_bit <> width) then
+ abort('Bad PK file: More bits than required!');
+@.More bits than required@>
+end
+
+@ We need to declare the repeat flag, bit counter, and color flag here.
+
+@<Glob...@>=
+@!repeat_count : integer ; {how many times to repeat the next row?}
+@!rows_left : integer ; {how many rows left?}
+@!turn_on : boolean ; {are we black here?}
+@!h_bit : integer ; {what is our horizontal position?}
+@!count : integer ; {how many bits of current color left?}
+
+@ If any specials are found, we write them out here.
+
+@d four_cases(#)==#,#+1,#+2,#+3
+
+@p procedure skip_specials ;
+var @!i, @!j: integer ;
+begin
+ repeat
+ flag_byte := pk_byte ;
+ if flag_byte >= 240 then
+ case flag_byte of
+ four_cases(pk_xxx1):
+begin
+ t_print((pk_loc-1):1,': Special: ''') ;
+ i := 0 ;
+ for j := pk_xxx1 to flag_byte do i := 256 * i + pk_byte ;
+ for j := 1 to i do t_print(xchr[pk_byte]) ;
+ t_print_ln('''') ;
+end ;
+ pk_yyy : t_print_ln((pk_loc-1):1,': Num special: ',get_32:1) ;
+ pk_post : t_print_ln((pk_loc-1):1,': Postamble') ;
+ pk_no_op : t_print_ln((pk_loc-1):1,': No op') ;
+ pk_pre, pk_undefined : abort('Unexpected ', flag_byte:1,'!') ;
+@.Unexpected bbb@>
+ endcases ;
+ until (flag_byte < 240) or (flag_byte = pk_post) ;
+end ;
+
+@* Terminal communication.
+We must get the file names and determine whether input is to be in
+hexadecimal or binary. To do this, we use the standard input path
+name. We need a procedure to flush the input buffer. For most systems,
+this will be an empty statement. For other systems, a |print_ln| will
+provide a quick fix. We also need a routine to get a line of input from
+the terminal. On some systems, a simple |read_ln| will do. Finally,
+a macro to print a string to the first blank is required.
+
+@d flush_buffer == begin end
+@d get_line(#) == if eoln(input) then read_ln(input) ;
+ i := 1 ;
+ while not (eoln(input) or eof(input)) do begin
+ #[i] := input^ ;
+ incr(i) ;
+ get(input) ;
+ end ;
+ #[i] := ' '
+
+@ @p procedure dialog ;
+var i : integer ; {index variable}
+buffer : packed array [1..name_length] of char; {input buffer}
+begin
+ for i := 1 to name_length do begin
+ typ_name[i] := ' ' ;
+ pk_name[i] := ' ' ;
+ end;
+ print('Input file name: ') ;
+ flush_buffer ;
+ get_line(pk_name) ;
+ print('Output file name: ') ;
+ flush_buffer ;
+ get_line(typ_name) ;
+end ;
+
+@* The main program.
+Now that we have all the pieces written, let us put them together.
+
+@p begin
+initialize ;
+dialog ;
+@<Open files@> ;
+@<Read preamble@> ;
+skip_specials ;
+while flag_byte <> pk_post do begin
+ @<Unpack and write character@> ;
+ skip_specials ;
+end ;
+j := 0 ;
+while not eof(pk_file) do begin
+ i := pk_byte ;
+ if i <> pk_no_op then abort('Bad byte at end of file: ',i:1) ;
+@.Bad byte at end of file@>
+ t_print_ln((pk_loc-1):1,': No op') ;
+ incr(j) ;
+end ;
+t_print_ln(pk_loc:1,' bytes read from packed file.');
+final_end :
+end .
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{PKtype} work at a particular installation.
+Any additional routines should be inserted here.
+@^system dependencies@>
+
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/systems/knuth/local/mfware/Makefile b/systems/knuth/local/mfware/Makefile
new file mode 100644
index 0000000000..c4fdfffe97
--- /dev/null
+++ b/systems/knuth/local/mfware/Makefile
@@ -0,0 +1,55 @@
+# Make executable files for this workstation, using the master TeX sources
+
+PFLAGS= -g -I..
+CFLAGS= -g -I..
+PC= gpc
+
+all: gftype gftopk gftodvi mft pktype
+
+%.p: %.ch
+ tangle ../../dist/mfware/$*.web $*.ch
+# mv ../../dist/mfware/$*.p .
+
+pktype.p: pktype.ch
+ tangle pktype.web pktype.ch
+
+%.ch: ../../dist/mfware/%.web
+ touch $*.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+gftype: gftype.o gftopk_ext.h gftopk_ext.o
+ pc ${PFLAGS} -o gftype gftype.o gftopk_ext.o
+
+gftopk: gftopk.o gftopk_ext.o gftopk_ext.h
+ pc ${PFLAGS} -o gftopk gftopk.o gftopk_ext.o
+
+pktype: pktype.o gftopk_ext.h gftopk_ext.o
+ pc ${PFLAGS} -o pktype pktype.o gftopk_ext.o
+
+gftodvi: gftodvi.o ext.o ../tex/ext.h
+ pc ${PFLAGS} -o gftodvi gftodvi.o ext.o
+
+gf2dvi.p: gf2dvi.ch
+ cp ../../dist/mfware/gftodvi.web gf2dvi.web
+ tangle gf2dvi.web gf2dvi.ch
+ rm gf2dvi.web
+
+gf2dvi: gf2dvi.o ext.o ../tex/ext.h
+ pc $(PFLAGS) -o gf2dvi gf2dvi.o ext.o
+
+ext.o: ../tex/ext.c
+ cc ${CFLAGS} -c ../tex/ext.c
+
+gftopk_ext.o: gftopk_ext.c
+
+mft: mft.o weavext.o mft_ext.h mft_ext.o
+ pc ${PFLAGS} -o mft mft.o mft_ext.o
+
+install: # doit make install "PROG=gftype"
+ mv $(PROG) /usr/local/bin/$(PROG)
+
+clean:
+# rm -f *.p *.o *~ ../../dist/mfware/*.pool
+ rm -f *.p *.o *~ *.pool
diff --git a/systems/knuth/local/mfware/gf2dvi.ch b/systems/knuth/local/mfware/gf2dvi.ch
new file mode 100644
index 0000000000..dad33f0384
--- /dev/null
+++ b/systems/knuth/local/mfware/gf2dvi.ch
@@ -0,0 +1,834 @@
+% Change file for the GF2DVI processor, for use with GNU Pascal.
+% [A "color separation" variant of gftodvi, for illustrations in Vols C, E]
+% (by Don Knuth; see ../mfware-sparc for the prehistory)
+
+% History:
+% 2000.04.30 Original version
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{GF\lowercase{to}DVI changes for {\mc GNU} Pascal}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is GFtoDVI, Version 3.0' {printed when the program starts}
+@y
+@d banner=='This is GF2DVI [double separation], Version 3.0 for Linux'
+ {printed when the program starts}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Use default case statement feature of ISO Extended Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Add inclusion of ext.h and standard input to program header
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program GF_to_DVI(@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var @!i,@!j,@!m,@!n:integer; {loop indices for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@y
+@p program GF_to_DVI(@!input,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\@=#include "../tex/ext.h"@>@\ {declarations for external C procedures}
+procedure initialize; {this procedure gets things started properly}
+ var @!i,@!j,@!m,@!n:integer; {loop indices for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ first_input:=true;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] Distinguish two brands of finality
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d final_end=9999 {label for the end of it all}
+
+@<Labels...@>=final_end;
+@y
+@d final_final_end=9999 {label for the end of it all}
+@d final_end=9998 {label for the end of a pass}
+
+@<Labels...@>=final_end,final_final_end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [5] Enlarge file_name_size to 1024
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Constants...@>=
+@!max_labels=2000; {maximum number of labels and dots and rules per character}
+@!pool_size=10000; {maximum total length of labels and other strings}
+@!max_strings=1100; {maximum number of labels and other strings}
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!file_name_size=50; {a file name shouldn't be longer than this}
+@!font_mem_size=2000; {space for font metric data}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!widest_row=8192; {maximum number of pixels per row}
+@!lig_lookahead=20; {size of stack used when inserting ligature characters}
+@y
+@<Constants...@>=
+@!max_labels=2000; {maximum number of labels and dots per character}
+@!pool_size=10000; {maximum total length of labels and other strings}
+@!max_strings=1100; {maximum number of labels and other strings}
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!file_name_size=1024; {a file name shouldn't be longer than this}
+@!font_mem_size=2000; {space for font metric data}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!widest_row=8192; {maximum number of pixels per row}
+@!lig_lookahead=20; {size of stack used when inserting ligature characters}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8] Add <nl> to end of abort() message; jump way out
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d abort(#)==@+begin print(' ',#); jump_out;@+end
+@y
+@d abort(#)==@+begin print_ln(' ',#); jump_out;@+end
+@z
+@x
+begin goto final_end;
+@y
+begin goto final_final_end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was15,now16] change update_terminal to flush(), change def'n of term_in
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Since the terminal is being used for both input and output, some systems
+need a special routine to make sure that the user can see a prompt message
+before waiting for input based on that message. (Otherwise the message
+may just be sitting in a hidden buffer somewhere, and the user will have
+no idea what the program is waiting for.) We shall call a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+
+@d update_terminal == break(output) {empty the terminal output buffer}
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of 0..255;
+@!term_in:text_file; {the terminal, considered as an input file}
+@y
+Since the terminal is being used for both input and output, some systems
+need a special routine to make sure that the user can see a prompt message
+before waiting for input based on that message. (Otherwise the message
+may just be sitting in a hidden buffer somewhere, and the user will have
+no idea what the program is waiting for.) We shall call a system-dependent
+subroutine |update_terminal| in order to avoid this problem.
+@^system dependencies@>
+
+@d update_terminal == flush(output) {empty the terminal output buffer}
+@d term_in == input {standard input}
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of 0..255;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was16,now17] Remove reset(term_in) from input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ A global variable |line_length| records the first buffer position after
+the line just read.
+@^system dependencies@>
+
+@p procedure input_ln; {inputs a line from the terminal}
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+line_length:=0;
+while (line_length<terminal_line_length)and not eoln(term_in) do
+ begin buffer[line_length]:=xord[term_in^]; incr(line_length); get(term_in);
+ end;
+end;
+
+@y
+@ A global variable |line_length| records the first buffer position after
+the line just read. Another one, |first_input|, tells if such a line exists.
+@^system dependencies@>
+
+@p procedure input_ln; {inputs a line from the terminal}
+begin update_terminal;
+if first_input then first_input:=false
+else if eoln(term_in) then read_ln(term_in);
+line_length:=0;
+while (line_length<terminal_line_length)and not eoln(term_in) do
+ begin buffer[line_length]:=xord[term_in^]; incr(line_length); get(term_in);
+ end;
+end;
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [45] Change type of binary file for binary (byte) files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types ...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+For Berkeley Pascal, we need to use `|packed file of -128..127|' to read and
+write binary bytes.
+
+@<Types ...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@!packed_ASCII_code=-128..127; {declaration needed only because of \.{ext.h}}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [47] Modify file open routines to match binary I/O library
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+variable that specifies the file name.
+@^system dependencies@>
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin reset(gf_file,name_of_file);
+cur_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin reset(tfm_file,name_of_file);
+end;
+@#
+procedure open_dvi_file; {prepares to write packed bytes in |dvi_file|}
+begin rewrite(dvi_file,name_of_file);
+end;
+@y
+variable that specifies the file name.
+
+An external C procedure, |test_access| is used to check whether or not the
+open will work. It is declared in the \.{ext.h} include file, and it returns
+|true| or |false|. The |name_of_file| global holds the file name whose access
+is to be tested.
+The first parameter for |test_access| is the access mode,
+one of |read_access_mode| or |write_access_mode|.
+
+We also implement path searching in |test_access|: its second parameter is
+one of the ``file path'' constants defined below. If |name_of_file|
+doesn't start with |'/'| then |test_access| tries prepending pathnames
+from the appropriate path list until success or the end of path list
+is reached.
+On return, |real_name_of_file| contains the original name with the path
+that succeeded (if any) prepended. It is the name used in the various
+open procedures.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {do no path searching}
+@d font_file_path=3 {path specifier for \.{TFM} files}
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+var@!i: 1..file_name_size;
+begin if test_access(read_access_mode,no_file_path) then
+ begin reset(gf_file,real_name_of_file); cur_loc:=0;
+ end
+else begin i:=1;
+ while (real_name_of_file[i] <> chr(0)) do@+incr(i);
+ abort('Can''t open GF file ',real_name_of_file:i);
+ end;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+var@!i: 1..file_name_size;
+begin if test_access(read_access_mode,font_file_path) then
+ begin reset(tfm_file,real_name_of_file); cur_loc:=0;
+ temp_int:=tfm_file^; {prime the pump}
+ if temp_int<0 then temp_int:=temp_int+256;
+ end
+else begin i:=1;
+ while (real_name_of_file[i] <> chr(0)) do@+incr(i);
+ abort('Can''t open TFM file ',real_name_of_file:i);
+ end;
+end;
+@#
+procedure open_dvi_file; {prepares to write packed bytes in |dvi_file|}
+var@!i: 1..file_name_size;
+begin if test_access(write_access_mode,no_file_path) then
+ rewrite(dvi_file,real_name_of_file)
+else begin i:=1;
+ while (real_name_of_file[i] <> chr(0)) do@+incr(i);
+ abort('Can''t write on DVI file ',real_name_of_file:i);
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [48] Declare real_name_of_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+be read next, and the string |name_of_file| will be set to the current
+file name before the file-opening procedures are called.
+
+@<Glob...@>=
+@!cur_loc:integer; {current byte number in |gf_file|}
+@!name_of_file:packed array[1..file_name_size] of char; {external file name}
+@y
+be read next, and the string |name_of_file| will be set to the current
+file name before the file-opening procedures are called.
+Under UNIX, we also have a |real_name_of_file| string, that gets
+set by the external |test_access| procedure after path searching.
+
+@<Glob...@>=
+@!cur_loc:integer; {current byte number in |gf_file|}
+@!name_of_file,@!real_name_of_file,@!gf_file_name:
+ packed array[1..file_name_size] of char; {external name}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [50] Use modified routines to access tfm_file using b_read_unsigned()
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure read_tfm_word;
+begin read(tfm_file,b0); read(tfm_file,b1);
+read(tfm_file,b2); read(tfm_file,b3);
+end;
+@y
+For Berkeley {\mc UNIX} we had to make the |byte_file| type have data in the
+range |-128..127|.
+If the file byte contains |@'377|, we want to regard this as 255 in decimal.
+However if we do |i:=tfm_file^| on that byte, where i is a signed integer,
+then the result is |i=-1|.
+File bytes with values |<@'200| are read properly.
+The solution is to do the assignment as shown, and then add 256 to values
+less than 0.
+We could also assign to |eight_bits| values, but this causes an error if
+runtime checking is turned on.
+The integer |temp_int| will be added to global variables at the end,
+where it won't disturb module numbering.
+
+@d fget==begin get(tfm_file);
+ temp_int:=tfm_file^;
+ if temp_int<0 then temp_int:=temp_int+256;
+ end
+
+@p procedure read_tfm_word;
+begin b0:=temp_int; fget; b1:=temp_int;
+fget; b2:=temp_int; fget; b3:=temp_int; fget;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [51] Use modified routines to access gf_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ We shall use another set of simple functions to read the next byte or
+bytes from |gf_file|. There are four possibilities, each of which is
+treated as a separate function in order to minimize the overhead for
+subroutine calls.
+@^system dependencies@>
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(gf_file,a); read(gf_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+@y
+@ We shall use another set of simple functions to read the next byte or
+bytes from |gf_file|. There are four possibilities, each of which is
+treated as a separate function in order to minimize the overhead for
+subroutine calls.
+@^system dependencies@>
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:-128..127;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc);
+ if b < 0 then get_byte := b + 256 else get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:-128..127;
+begin read(gf_file,b); incr(cur_loc);
+signed_byte:=b;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,b:integer;
+begin a := get_byte; b := get_byte;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:integer;
+begin a := get_byte; b := get_byte; c := get_byte;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:integer;
+begin a := signed_byte; b := get_byte; c := get_byte; d := get_byte;
+signed_quad:=((a*256+b)*256+c)*256+d
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [52] change definition of min/max_quarterword
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+the more general memory words of \TeX. On some machines it is necessary to
+define |min_quarterword=-128| and |max_quarterword=127| in order to pack
+four quarterwords into a single word.)
+@^system dependencies@>
+
+@d min_quarterword=0 {change this to allow efficient packing, if necessary}
+@d max_quarterword=255 {ditto}
+@y
+the more general memory words of \TeX.
+With Berkeley {\mc UNIX} Pascal, we need to use the |-128..127| range to pack
+an integer subrange into a byte.
+@^system dependencies@>
+
+@d min_quarterword=-128 {change this to allow efficient packing, if necessary}
+@d max_quarterword=127 {ditto}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [78] change default extension to ".2602gf"
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+l:=3; init_str3(".")("g")("f")(gf_ext);@/
+@y
+l:=7; init_str7(".")("2")("6")("0")("2")("g")("f")(gf_ext);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [78] change default gray font to "black"
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+l:=4; init_str4("g")("r")("a")("y")(default_gray_font);@/
+@y
+l:=5; init_str5("b")("l")("a")("c")("k")(default_gray_font);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [87] change ">" and ":" to "/" in file name scanning
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+@y
+@ The file names we shall deal with for SunOS have the
+following structure: If the name contains `\./', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\./', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [88] change home_font_area to null_string (open_tfm provides path)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|home_font_area|. This system area name will, of course, vary from place
+to place. The program here sets it to `\.{TeXfonts:}'.
+@^system dependencies@>
+@.TeXfonts@>
+
+@<Initialize the strings@>=
+l:=9; init_str9("T")("e")("X")("f")("o")("n")("t")("s")(":")(home_font_area);@/
+@y
+@ Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|home_font_area|. This system area name will, of course, vary from place
+to place. Under the Berkeley {\mc UNIX} version, we set |home_font_area|
+to |null_string| because the default areas to search for \.{TFM} files
+are built into the routine |test_access|.
+@^system dependencies@>
+
+@<Initialize the strings@>=
+l:=0; init_str0(home_font_area);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [90] change more_name to understand UNIX file name paths
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin if (c=">")or(c=":") then
+ begin area_delimiter:=pool_ptr; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
+ str_room(1); append_char(c); {contribute |c| to the current string}
+ more_name:=true;
+ end;
+end;
+@y
+@p function more_name(@!c:ASCII_code):boolean;
+begin if c=" " then more_name:=false
+else begin if (c="/") then
+ begin area_delimiter:=pool_ptr; ext_delimiter:=0;
+ end
+ else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr;
+ str_room(1); append_char(c); {contribute |c| to the current string}
+ more_name:=true;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [94] change start_gf to get file name from command line arguments
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The |start_gf| procedure prompts the user for the name of the generic
+font file to be input. It opens the file, making sure that some input is
+present; then it opens the output file.
+
+Although this routine is system-independent, it should probably be
+modified to take the file name from the command line (without an initial
+prompt), on systems that permit such things.
+
+@p procedure start_gf;
+label found,done;
+begin loop@+begin print_nl('GF file name: '); input_ln;
+@.GF file name@>
+ buf_ptr:=0; buffer[line_length]:="?";
+ while buffer[buf_ptr]=" " do incr(buf_ptr);
+ if buf_ptr<line_length then
+ begin @<Scan the file name in the buffer@>;
+ if cur_ext=null_string then cur_ext:=gf_ext;
+ pack_file_name(cur_name,cur_area,cur_ext); open_gf_file;
+ if not eof(gf_file) then goto found;
+ print_nl('Oops... I can''t find file '); print(name_of_file);
+@.Oops...@>
+@.I can't find...@>
+ end;
+ end;
+found:job_name:=cur_name; pack_file_name(job_name,null_string,dvi_ext);
+open_dvi_file;
+end;
+@y
+@ The |start_gf| procedure obtains the name of the generic font file to
+be input from the command line.
+It opens the file, making sure that some input is
+present; then it opens the output file.
+
+@p procedure start_gf;
+label done;
+var
+ arg_buffer: packed array [1..file_name_size] of char;
+ arg_buf_ptr: 1..file_name_size;
+begin if (argc > 2) then abort('Usage: gftodvi [GF-file]');
+if argc = 1 then begin
+ print_nl('GF file name: '); input_ln;
+@.GF file name@>
+ end
+else begin
+ argv(1, arg_buffer);
+ arg_buf_ptr := 1;
+ line_length := 0;
+ while (arg_buf_ptr < file_name_size)
+ and (arg_buffer[arg_buf_ptr] = ' ') do
+ incr(arg_buf_ptr);
+ while (arg_buf_ptr < file_name_size)
+ and (line_length < terminal_line_length)
+ and (arg_buffer[arg_buf_ptr] <> ' ') do
+ begin
+ buffer[line_length] := xord[arg_buffer[arg_buf_ptr]];
+ incr(line_length);
+ incr(arg_buf_ptr);
+ end;
+end;
+ buf_ptr:=0; buffer[line_length]:="?";
+ while buffer[buf_ptr]=" " do incr(buf_ptr);
+ if buf_ptr<line_length then
+ begin @<Scan the file name in the buffer@>;
+ if cur_ext=null_string then cur_ext:=gf_ext;
+ pack_file_name(cur_name,cur_area,cur_ext);
+ gf_file_name:=name_of_file;
+ open_gf_file;
+ end;
+job_name:=cur_name; pack_file_name(job_name,null_string,dvi_ext);
+open_dvi_file;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [98] load_fonts:
+% KLUDGE: SUN Pascal compiler incorrectly aligns four_quarters data
+% item on stack, which causes long reference to odd address.
+% FIX: rearrange four_quarter data to follow aligned long (int) data.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Declare the procedure called |load_fonts|@>=
+procedure load_fonts;
+label done,continue,found,not_found;
+var @!f:internal_font_number;
+@!i:four_quarters; {font information word}
+@!j,@!k,@!v:integer; {registers for initializing font tables}
+@!m:title_font..slant_font+area_code; {keyword found}
+@!n1:0..longest_keyword; {buffered character being checked}
+@y
+@<Declare the procedure called |load_fonts|@>=
+procedure load_fonts;
+label done,continue,found,not_found;
+var @!f:internal_font_number;
+@!j,@!k,@!v:integer; {registers for initializing font tables}
+@!i:four_quarters; {font information word}
+@!m:title_font..slant_font+area_code; {keyword found}
+@!n1:0..longest_keyword; {buffered character being checked}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [107] write_dvi is now an external C routine
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure write_dvi(@!a,@!b:dvi_index);
+var k:dvi_index;
+begin for k:=a to b do write(dvi_file,dvi_buf[k]);
+end;
+@y
+For Berkeley {\mc UNIX}, this is going to be handled by an external procedure,
+which will do the output using |fwrite|.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [115] double the page count in the DVI file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+dvi_out(total_pages div 256); dvi_out(total_pages mod 256);@/
+@y
+total_pages:=total_pages+total_pages;
+dvi_out(total_pages div 256); dvi_out(total_pages mod 256);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [154] avoid spurious error message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ else print_nl('(Tardy font change will be ignored (byte ',
+@y
+ else if pass=1 then print_nl('(Tardy font change will be ignored (byte ',
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [164] do the color separation
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Output all rules for the current character@>;
+@<Output all labels for the current character@>;
+do_pixels;
+@y
+cross(-655360,-655360);
+cross(-655360,page_height+655360);
+cross(page_width+655360,-655360);
+cross(page_width+655360,page_height+655360);
+if pass=1 then
+ begin @<Output all rules for the current character@>;
+ @<Output all labels for the current character@>;
+ skip_pixels;
+ end
+else do_pixels;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [172] supply a secondary page number
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for k:=3 to 9 do dvi_four(0);
+@y
+dvi_four(pass); for k:=4 to 9 do dvi_four(0);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was188,now191] Avoid `oct', a reserved word in SunOS Pascal(!)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d octant==xr {octant code for nearest dot, plus 8 for coincident dots}
+@y
+@d octant==xr {octant code for nearest dot, plus 8 for coincident dots}
+@d oct==octnt {changes variable name since |oct| is reserved word in SunOS}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was216,now219] call set_paths before gf_start to initialize paths
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p begin initialize; {get all variables initialized}
+@<Initialize the strings@>;
+start_gf; {open the input and output files}
+@<Process the preamble@>;
+cur_gf:=get_byte; init_str_ptr:=str_ptr;
+loop@+ begin @<Initialize variables for the next character@>;
+ while (cur_gf>=xxx1)and(cur_gf<=no_op) do @<Process a no-op command@>;
+ if cur_gf=post then @<Finish the \.{DVI} file and |goto final_end|@>;
+@y
+@p @<Last-minute procedures@>@;
+begin initialize; {get all variables initialized}
+@<Initialize the strings@>;
+set_paths; {initialize paths for \.{TFM} files from environment if needed}
+for pass:=1 to 2 do
+ begin if pass=1 then
+ begin start_gf; {open the input and output files}
+ @<Process the preamble@>;
+ init_str_ptr:=str_ptr;
+ end
+ else begin total_pages:=0; name_of_file:=gf_file_name; open_gf_file;
+ q:=get_byte; q:=get_byte; k:=get_byte; for m:=1 to k do q:=get_byte;
+ end;
+cur_gf:=get_byte;
+loop@+ begin @<Initialize variables for the next character@>;
+ while (cur_gf>=xxx1)and(cur_gf<=no_op) do @<Process a no-op command@>;
+ if cur_gf=post then
+ if pass=1 then goto final_end
+ else @<Finish the \.{DVI} file and |goto final_end|@>;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was216,now219] finish normal end with <nl> on terminal; add another `end'
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ cur_gf:=get_byte; str_ptr:=init_str_ptr; pool_ptr:=str_start[str_ptr];
+ end;
+final_end:end.
+@y
+ cur_gf:=get_byte; str_ptr:=init_str_ptr; pool_ptr:=str_start[str_ptr];
+ end;
+final_end:end;
+final_final_end:print_ln(' ');
+end.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [was219,now222] declare special globals and procedures
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtoDVI} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtoDVI} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@<Glob...@>=
+@!pass:1..2; {are we doing the first pass (rules) or the second (pixels)?}
+@!temp_int:integer; {used for TFM input}
+@!last,@!first:0..terminal_line_length; {needed in \.{ext.c}}
+@!interrupt:integer; {ditto}
+@!first_input:boolean; {true before the first call to |input_ln|}
+
+@ @<Types in...@>=
+@!word_file=file of memory_word; {needed in \.{ext.c}}
+
+@ Finally, here is a subroutine that draws cross-reference marks.
+@<Last-minute...@>=
+procedure cross(@!x,@!y:scaled);
+begin dvi_goto(x-655360,y+13107);
+dvi_out(put_rule);
+dvi_four(26214); dvi_four(1310720);
+dvi_out(pop);@/
+dvi_goto(x-13107,y+655360);
+dvi_out(put_rule);
+dvi_four(1310720); dvi_four(26214);
+dvi_out(pop);
+end;
+
+@ And still more finally, a subroutine that skips over
+the meaty part of a ``character'' in the \.{GF} file.
+@<Last-min...@>=
+procedure skip_pixels;
+label done;
+var @!k:integer; {garbage}
+begin while cur_gf<>eoc do
+ begin case cur_gf of
+ paint1,skip1:k:=get_byte;
+ paint2,skip2:k:=get_two_bytes;
+ paint3,skip3:k:=get_three_bytes;
+ xxx1,xxx2,xxx3,xxx4,yyy,no_op: begin skip_nop; goto done; end;
+ othercases do_nothing
+ endcases;@/
+ cur_gf:=get_byte;
+done: end;
+end;
+@z
diff --git a/systems/knuth/local/mfware/gftopk.ch b/systems/knuth/local/mfware/gftopk.ch
new file mode 100644
index 0000000000..4ed4b64ddf
--- /dev/null
+++ b/systems/knuth/local/mfware/gftopk.ch
@@ -0,0 +1,367 @@
+% $Header: gftopk.ch,v 1.4 87/12/05 10:40:15 mackay Released $
+
+% Change file for the GFtoPK processor, for use on Berkeley UNIX systems.
+
+% History:
+%
+% don upped max_rows on 90/8/30
+% don upgraded to version 2.3 on 90/8/6
+% don upgraded to version 2.2 on 89/11/21
+%
+% don made changes for version 2 and SunOS on 89/8/12
+% (I simplified things by borrowing from texware change files)
+%
+% $Log: gftopk.ch,v $
+% Revision 1.4 87/12/05 10:40:15 mackay
+% Released for GFtoPK 1.4;
+%
+% Revision 1.3 86/12/08 20:20:15 mackay
+% Released for GFtoPK 1.3;
+%
+% Revision 1.2 86/02/01 14:45:21 richards
+% Released for GFtoPK 1.2;
+%
+%
+% Revision 1.1 86/01/30 19:56:05 richards
+% Initial revision
+%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{GF\lowercase{to}PK changes for Berkeley {\mc UNIX}}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is GFtoPK, Version 2.3' {printed when the program starts}
+@y
+@d banner=='This is GFtoPK, Version 2.3 for SunOS'
+ {printed when the program starts}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Fix others:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {SunOS Pascal default cases}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] Redefine program header, eliminate gf_file and pxl_file
+% add <Parse command line> section to initialize
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program GFtoPK(@!gf_file,@!pk_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@y
+@p program GFtoPK(@!input,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\@=#include "gftopk_ext.h"@>@\
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>;@/
+ @<Parse command line arguments@>@/
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6] add file_name_size to constants in the outer block
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!line_length=79; {bracketed lines of output will be at most this long}
+@!max_row=16000; {largest index in the main |row| array}
+@y
+@!line_length=79; {bracketed lines of output will be at most this long}
+@!max_row=60000; {largest index in the main |row| array}
+@!file_name_size=256; {maximum length of a file name}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8] have abort() add <nl> to end of message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d abort(#)==begin print(' ',#); jump_out;
+@y
+@d abort(#)==begin print_ln(' ',#); jump_out;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [37] Add UNIX_file_name type, redefine byte_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!UNIX_file_name=packed array [1..file_name_size] of char;
+@!byte_file=packed file of -128..127;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38] add gf_name and pk_name to global vars
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!gf_file:byte_file; {the stuff we are \.{GFtoPK}ing}
+@!pk_file:byte_file; {the stuff we have \.{GFtoPK}ed}
+@y
+@!gf_file:byte_file; {the stuff we are \.{GFtoPK}ing}
+@!gf_name: UNIX_file_name; {the name of |gf_file|}
+@!pk_file:byte_file; {the stuff we have \.{GFtoPK}ed}
+@!pk_name: UNIX_file_name; {the name of |pk_file|}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [39] open_gf_file with extended reset command (can dump core!)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+begin reset(gf_file);
+@y
+begin if testreadaccess(gf_name) then reset(gf_file,gf_name)
+else abort('Can''t open the GF file');
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [40] and open_pk_file...
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+begin rewrite(pk_file);
+@y
+var j,k,size:integer; startednumber:boolean;
+begin
+if argc < 3 then begin@/
+ @<Generate |pk_name| from |gf_name| and |h_mag|@>@/
+ end;
+rewrite(pk_file,pk_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [43] replace gf_byte and gf_signed_quad
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function gf_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then bad_gf('Unexpected end of file!')
+@.Unexpected end of file@>
+else begin read(gf_file,b); gf_byte:=b;
+ end;
+incr(gf_loc);
+end;
+@#
+function gf_signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+if a<128 then gf_signed_quad:=((a*256+b)*256+c)*256+d
+else gf_signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+gf_loc := gf_loc + 4 ;
+end;
+@y
+@p function gf_byte:integer; {returns the next byte, unsigned}
+var b:-128..127;
+begin if eof(gf_file) then bad_gf('Unexpected end of file!')
+else begin read(gf_file,b);
+ if b < 0 then gf_byte := b + 256 else gf_byte:=b;
+ end;
+incr(gf_loc);
+end;
+@#
+function gf_signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:integer;
+begin read(gf_file,a); b := gf_byte; c := gf_byte; d := gf_byte;
+gf_signed_quad:=((a*256+b)*256+c)*256+d
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [44] redefine pk_byte, pk_halfword, pk_three_bytes, and pk_word
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure pk_byte(a:integer) ;
+begin
+ if pk_open then begin
+ if a < 0 then a := a + 256 ;
+ write(pk_file, a) ;
+ incr(pk_loc) ;
+ end ;
+end ;
+@#
+procedure pk_halfword(a:integer) ;
+begin
+ if a < 0 then a := a + 65536 ;
+ write(pk_file, a div 256) ;
+ write(pk_file, a mod 256) ;
+ pk_loc := pk_loc + 2 ;
+end ;
+@#
+procedure pk_three_bytes(a:integer);
+begin
+ write(pk_file, a div 65536 mod 256) ;
+ write(pk_file, a div 256 mod 256) ;
+ write(pk_file, a mod 256) ;
+ pk_loc := pk_loc + 3 ;
+end ;
+@#
+procedure pk_word(a:integer) ;
+var b : integer ;
+begin
+ if pk_open then begin
+ if a < 0 then begin
+ a := a + @'10000000000 ;
+ a := a + @'10000000000 ;
+ b := 128 + a div 16777216 ;
+ end else b := a div 16777216 ;
+ write(pk_file, b) ;
+ write(pk_file, a div 65536 mod 256) ;
+ write(pk_file, a div 256 mod 256) ;
+ write(pk_file, a mod 256) ;
+ pk_loc := pk_loc + 4 ;
+ end ;
+end ;
+@y
+@p procedure pk_byte(a:integer) ;
+begin
+ if pk_open then begin
+ if a < 0 then a := a + 256 ;
+ if a > 127 then a := a-256;
+ write(pk_file, a) ;
+ incr(pk_loc) ;
+ end ;
+end ;
+@#
+procedure pk_halfword(a:integer) ;
+begin
+ if a < 0 then a := a + 65536 ;
+ pk_byte(a div 256); pk_byte(a mod 256);
+end ;
+@#
+procedure pk_three_bytes(a:integer);
+begin
+ pk_byte(a div 65536 mod 256) ;
+ pk_byte(a div 256 mod 256) ;
+ pk_byte(a mod 256) ;
+ end ;
+@#
+procedure pk_word(a:integer) ;
+var b : integer ;
+begin
+ if a < 0 then begin
+ a := a + @'10000000000 ;
+ a := a + @'10000000000 ;
+ b := 128 + a div 16777216 ;
+ end else b := a div 16777216 ;
+ pk_byte(b) ;
+ pk_byte(a div 65536 mod 256) ;
+ pk_byte(a div 256 mod 256) ;
+ pk_byte(a mod 256) ;
+ end ;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [46] redefine find_gf_length and move_to_byte
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure find_gf_length ;
+begin
+ set_pos(gf_file, -1) ; gf_len := cur_pos(gf_file) ;
+end ;
+@#
+procedure move_to_byte(@!n : integer) ;
+begin
+ set_pos(gf_file, n); gf_loc := n ;
+end ;
+@y
+@p procedure find_gf_length ;
+begin
+ gf_len := flength(gf_file);
+end ;
+@#
+procedure move_to_byte(@!n : integer) ;
+begin
+ b_seek(gf_file, n); gf_loc := n ;
+end ;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [88+] add <Parse command line arguments> section and needed subsections
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtoPK} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+This section contains additions needed to make \.{GFtoPK} work under
+{\mc UNIX}. The additions all deal with obtaining file names from
+the invoking command line and if not all arguments are provided,
+devising defaults for them.
+@^system dependencies@>
+
+@<Parse command line arguments@>=
+if (argc < 2) or (argc > 3) then begin@/
+ write_ln('Usage: gftopk GF-file [PK-file]');@/
+ goto final_end;@/
+ end;
+argv(1, gf_name);@/
+if (argc > 2) then@/
+ argv(2, pk_name);@/
+
+@ Here's a section to construct |pk_name| from |gf_name|, tacking on the
+right extension from the magnification of the file.
+
+@<Generate |pk_name| ...@>=
+j:=file_name_size;@/
+while (j > 1) and (gf_name[j] <> '/') do@/
+ decr(j);
+i:=j;
+while (j > 1) do begin@/
+ decr(j); pk_name[j] := gf_name[j];
+ end;
+while (i < file_name_size) and (gf_name[i] <> '.') and (gf_name[i] <> ' ') do begin@/
+ pk_name[i] := gf_name[i];@/
+ incr(i);@/
+ end;
+pk_name[i]:='.'; incr(i);@/
+size:=h_mag;@/
+j :=10000;@/
+startednumber:=false;@/
+while (j > 0) do begin@/
+ k:=size div j;@/
+ size := size mod j;@/
+ j:=j div 10;
+ if startednumber or (k <> 0) or (j = 0) then begin@/
+ pk_name[i] := chr(ord('0') + k); incr(i);@/
+ startednumber := true;@/
+ end;@/
+ end;@/
+pk_name[i] := 'p'; incr(i);@/
+pk_name[i] := 'k'; incr(i);@/
+pk_name[i] := ' ';@/
+print_ln('[Output file named ''', pk_name:i-1, ''']');@/
+
+@z
+
diff --git a/systems/knuth/local/mfware/gftopk_ext.c b/systems/knuth/local/mfware/gftopk_ext.c
new file mode 100644
index 0000000000..5301fc90be
--- /dev/null
+++ b/systems/knuth/local/mfware/gftopk_ext.c
@@ -0,0 +1,43 @@
+#include "h00vars.h" /* defines Pascal I/O structure */
+
+#define namelength 100 /* should agree with gftopk.ch and gftype.ch */
+
+bseek(filep,cnt)
+ register struct iorec *filep;
+ int cnt;
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+
+ iop = filep->fbuf;
+ fseek(iop,(long)cnt,0);
+}
+
+bool testreadaccess(fn)
+ char *fn;
+{
+ register char *p;
+
+ fn[namelength-1] = ' ';
+ p = fn;
+ while (*p++ != ' ');
+ p--;
+ *p = '\0';
+ if (access(fn,4)==0) {
+ *p = ' '; return(TRUE);}
+ else {*p = ' '; return(FALSE);}
+}
+
+int flength(filep)
+ register struct iorec *filep;
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+ register long pos; /* current file position */
+ register int len; /* the file length */
+
+ iop = filep->fbuf;
+ pos = ftell(iop);
+ fseek(iop,0L,2);
+ len = ftell(iop);
+ fseek(iop,pos,0);
+ return(len);
+}
diff --git a/systems/knuth/local/mfware/gftopk_ext.h b/systems/knuth/local/mfware/gftopk_ext.h
new file mode 100644
index 0000000000..cd063b1cf1
--- /dev/null
+++ b/systems/knuth/local/mfware/gftopk_ext.h
@@ -0,0 +1,6 @@
+function flength(var f: bytefile):integer;
+ external;
+procedure bseek(var f: bytefile; loc: integer);
+ external;
+function testreadaccess(var fn: UNIXfilename):boolean;
+ external;
diff --git a/systems/knuth/local/mfware/gftype.ch b/systems/knuth/local/mfware/gftype.ch
new file mode 100644
index 0000000000..dc63dad049
--- /dev/null
+++ b/systems/knuth/local/mfware/gftype.ch
@@ -0,0 +1,430 @@
+% $Header: gftype.ch,v 2.2.1.4 86/01/27 15:43:41 richards Released $
+
+% Change file for the GFtype processor, for use on Berkeley UNIX systems.
+
+% History:
+%
+% don upgraded to version 3.1 on 3/15/91
+%
+% don added testreadaccess (to avoid core dump on bad GF file name) on 89/10/18
+%
+% don made changes for version 3 and SunOS on 89/08/12
+% (I simplified things by borrowing from dvitype.ch)
+%
+% $Log: gftype.ch,v $
+% Revision 2.2.1.4 86/01/27 15:43:41 richards
+% Changed:
+% text_file type to "text" for Pyramid compiler,
+% who doesn't believe packed array of char == text, and
+% won't allow write('strings') to it!
+%
+% Revision 2.2.1.3 86/01/21 22:04:30 richards
+% Changed:
+% file_name_size and byte_file declaration to agree with binary I/O
+% library updates; b_open_in() call to include path specifier
+%
+% Revision 2.2.1.2 86/01/21 15:54:21 richards
+% Changed:
+% interface to byte I/O again; now cur_loc is back to a variable,
+% and b_set_ptr() saves addr of cur_loc so it can be kept in
+% sync with files. b_file_loc() was called more frequently than
+% I thought...
+%
+% Revision 2.2.1.1 86/01/20 22:06:53 richards
+% Incorporate new Byte-I/O routines in place of fudging Pascal into
+% reading bytes; for Pyramid ....
+%
+% Revision 2.2 85/10/18 23:31:09 richards
+% Updated for GFtype Version 2.2
+%
+% Revision 2.1 85/03/03 21:41:49 richards
+% Updated to GF utilities distributed with MF Version 0.77
+% (New GF file format)
+%
+% Revision 2.0 84/12/16 22:42:52 richards
+% Updated for GFtype Version 2.0 (New GF file format)
+%
+% Revision 1.2 84/12/06 02:35:40 richards
+% Updated to GFtype Version 1.2; merged in changes from sdcarl!rusty
+%
+% Revision 0.0 84/11/10 02:13:19 richards
+% Base version for GFtype 0.0
+%
+%
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{GF$\,$\lowercase{type} changes for Berkeley {\mc UNIX}}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is GFtype, Version 3.1' {printed when the program starts}
+@y
+@d banner=='This is GFtype, Version 3.1 for SunOS'
+ {printed when the program starts}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Case statement default feature of SunOS Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Redirect GFtype output to correct output file
+% depending on gfout_active value
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+@d print_nl==write_ln
+@y
+@d print(#)==begin if gfout_active then write(gftyout,#) else write(#) end
+@d print_ln(#)==begin if gfout_active then write_ln(gftyout,#)
+ else write_ln(#) end
+@d print_nl==begin if gfout_active then write_ln(gftyout) else write_ln end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Change program header to standard input/output; include external header
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program GF_type(@!gf_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@y
+@p program GF_type(@!input,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\@=#include "gftopk_ext.h"@>@\
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin @<Set initial values@>@/
+ @<Parse command line arguments@>;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [5] Add file_name_size to constants
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Constants...@>=
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!line_length=79; {\\{xxx} strings will not produce lines longer than this}
+@!max_row=79; {vertical extent of pixel image array}
+@!max_col=79; {horizontal extent of pixel image array}
+@y
+@<Constants...@>=
+@!terminal_line_length=150; {maximum number of characters input in a single
+ line of input from the terminal}
+@!line_length=79; {\\{xxx} strings will not produce lines longer than this}
+@!max_row=79; {vertical extent of pixel image array}
+@!max_col=79; {horizontal extent of pixel image array}
+@!file_name_size=100; {max length of file name on command line}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Have abort append <nl> to end of msg
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d abort(#)==begin print(' ',#); jump_out;
+@y
+@d abort(#)==begin print_ln(' ',#); jump_out;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] Change definition of 'byte_file' to correct subrange
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+On Berkeley {\mc UNIX}, we have to use |-128..127| for byte files, as
+explained in the \TeX\ changes.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@!UNIX_file_name=packed array[1..file_name_size] of char;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] Add gf_name declaration
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The program deals with one binary file variable: |gf_file| is the main
+input file that we are translating into symbolic form.
+
+@<Glob...@>=
+@!gf_file:byte_file; {the stuff we are \.{GF}typing}
+@y
+@ The program deals with one binary file variable: |gf_file| is the main
+input file that we are translating into symbolic form.
+We also declare array |gf_name|, used to hold the name of |gf_file|.
+
+@<Glob...@>=
+@!gf_file:byte_file; {the stuff we are \.{GF}typing}
+@!gf_name: UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [22] Change to file opening
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin reset(gf_file);
+@y
+The local /PASCAL/ runtimes will dump core if the file isn't present.
+We avoid that by using an external procedure.
+
+@p procedure open_gf_file; {prepares to read packed bytes in |gf_file|}
+begin if testreadaccess(gf_name) then reset(gf_file,gf_name)
+else abort('Can''t read the GF file');
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] Use modified routines to access gf_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(gf_file,a); read(gf_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(gf_file,a); read(gf_file,b); read(gf_file,c); read(gf_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+@y
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:-128..127;
+begin if eof(gf_file) then get_byte:=0
+else begin read(gf_file,b); incr(cur_loc);
+ if b < 0 then get_byte := b + 256 else get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:-128..127;
+begin read(gf_file,b); incr(cur_loc);
+signed_byte:=b;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,b:integer;
+begin a := get_byte; b := get_byte;
+get_two_bytes:=a*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:integer;
+begin a := get_byte; b := get_byte; c := get_byte;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:integer;
+begin a := signed_byte; b := get_byte; c := get_byte; d := get_byte;
+signed_quad:=((a*256+b)*256+c)*256+d
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27] Set term_in and term_out to standard input/output
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The |input_ln| routine waits for the user to type a line at his or her
+terminal; then it puts ASCII-code equivalents for the characters on that line
+into the |buffer| array. The |term_in| file is used for terminal input,
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!term_in:text_file; {the terminal, considered as an input file}
+@!term_out:text_file; {the terminal, considered as an output file}
+@y
+@ The |input_ln| routine waits for the user to type a line at his or her
+terminal; then it puts ASCII-code equivalents for the characters on that line
+into the |buffer| array. The |term_in| file is used for terminal input,
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@d term_in == input {standard input}
+@d term_out == output {standard output}
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!gftyout: text_file; {output from program}
+@!gfout_name: packed array[1..file_name_size] of char; {file name}
+@!gfout_active: boolean; {true if |gftyout| is open and ready to write}
+@!first_input: boolean; {true before the first call of |input_ln|}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [28] update_terminal becomes flush()
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush(term_out) {empty the terminal output buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29] eliminate reset() from input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure input_ln; {inputs a line from the terminal}
+var k:0..terminal_line_length;
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+k:=0;
+while (k<terminal_line_length)and not eoln(term_in) do
+ begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
+ end;
+buffer[k]:=" ";
+end;
+@y
+@p procedure input_ln; {inputs a line from the terminal}
+var k:0..terminal_line_length;
+begin update_terminal;
+if first_input then first_input:=false
+else if eoln(term_in) then read_ln(term_in);
+k:=0;
+while (k<terminal_line_length)and not eoln(term_in) do
+ begin buffer[k]:=xord[term_in^]; incr(k); get(term_in);
+ end;
+buffer[k]:=" ";
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [31] eliminate rewrite() from dialog
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure dialog;
+label 1,2;
+begin rewrite(term_out); {prepare the terminal for output}
+write_ln(term_out,banner);@/
+@<Determine whether the user |wants_mnemonics|@>;
+@<Determine whether the user |wants_pixels|@>;
+@<Print all the selected options@>;
+end;
+@y
+@p procedure dialog;
+label 1,2;
+begin
+write_ln(term_out,banner);@/
+@<Determine whether the user |wants_mnemonics|@>;
+@<Determine whether the user |wants_pixels|@>;
+@<Print all the selected options@>;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [65] finish last line written to statistics (stdout)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+print('The file had ',total_chars:1,' character');
+if total_chars<>1 then print('s');
+print(' altogether.');
+@.The file had n characters...@>
+final_end:end.
+@y
+print('The file had ',total_chars:1,' character');
+if total_chars<>1 then print('s');
+print_ln(' altogether.');
+@.The file had n characters...@>
+final_end:end.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [74,75,76] Add new code to parse command line arguments
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{GFtype} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+This section contains additions to \.{GFtype} necessary to integrate it
+into the {\mc UNIX} environment. The primary addition is a section to
+parse command line arguments and put file names into their correct
+variables, if needed.
+@^system dependencies@>
+
+@ The command line issued to start \.{GFtype} should also contain the
+names of the input (GF) file, and optionally, the name of an output
+file to receive the output from \.{GFtype}. This segment decodes
+the command line, and saves the file names in the appropriate variables.
+@^system dependencies@>
+
+@<Parse command...@>=
+if (argc < 2) or (argc > 3) then
+ begin print_ln('Usage: gftype GFfile [outputfile]');@/
+ goto final_end; { we will be unstructured just this once }
+ end;
+argv(1, gf_name);
+if argc > 2 then
+ begin argv(2, gfout_name);@/
+ rewrite(gftyout, gfout_name);@/
+ gfout_active := true;@/
+ print_ln(banner);@/
+ end
+
+@ We also need to initialize the values of the new boolean variables
+we introduced.
+
+@<Set initial ...@>=
+gfout_active:=false;
+first_input:=true;
+@z
+
diff --git a/systems/knuth/local/mfware/mft.ch b/systems/knuth/local/mfware/mft.ch
new file mode 100644
index 0000000000..9f4d1e0821
--- /dev/null
+++ b/systems/knuth/local/mfware/mft.ch
@@ -0,0 +1,534 @@
+% Change file for the MFT processor, for use on Berkeley UNIX systems.
+% This file was created by Pierre MacKay, and is based on the
+% change file for WEAVE written by Howard Trickey and Pavel Curtis.
+
+% History:
+% 6/09/86--WEAVE change file copied in entirety and renamed to
+% refer to MFT.
+%
+% 10/16/89 modified by don for SunOS, brought up to version 2.0
+%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] MFT: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{MFT changes for Berkeley {\mc UNIX}}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Change banner message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is MFT, Version 2.0'
+@y
+@d banner=='This is MFT, Version 2.0 for SunOS'
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] add input and output, remove other files, add ref to scan_args,
+% [3] and #include external definition for exit(), etc.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+program MFT(@!mf_file,@!change_file,@!style_file,@!tex_file);
+label end_of_MFT; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@<Error handling procedures@>@/
+@y
+program MFT(@!input,@!output);
+label end_of_MFT; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\
+@=#include "mft_ext.h"@>
+@\@/
+@<Error handling procedures@>@/
+@<|scan_args| procedure@>@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] compiler options
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@{@&$C+,A+,D-@} {range check, catch arithmetic overflow, no debug overhead}
+@y
+@=(*$C-*)@> {no range check}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Fix others:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {SunOS Pascal default cases}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [13] declare UNIX_file_name type
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!text_file=packed file of text_char;
+@y
+@!text_file=packed file of text_char;
+@!UNIX_file_name = packed array[1..max_file_name_length] of char;
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] terminal output: use standard i/o
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@y
+@d term_out==output
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@z
+
+@x
+@<Globals...@>=
+@!term_out:text_file; {the terminal as an output file}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] init terminal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal. Here is one way to do this
+on the \PASCAL\ system that was used in \.{WEAVE}'s initial development:
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
+@y
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal.
+@^system dependencies@>
+
+@<Set init...@>=
+; {nothing need be done}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [22] flush terminal buffer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush(term_out) {empty the terminal output buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] open input files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens the input files. Since these files were listed
+in the program header, we assume that the \PASCAL\ runtime system has
+already checked that suitable file names have been given; therefore no
+additional error checking needs to be done.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read the inputs}
+begin reset(mf_file); reset(change_file); reset(style_file);
+end;
+@y
+@ The following code opens the input files.
+This is called after |scan_args| has filled the file name variables
+appropriately.
+We use this opportunity, to define some constants related to file opening
+and the use of paths. These constants are repeated in mft_ext.c and
+must match the values given there.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {no path searching should be done}
+@d good_file_path=3 {path specifier for style files}
+
+@^system dependencies@>@^changes for Berkeley {\mc UNIX}@>
+
+@p procedure open_input; {prepare to read inputs}
+var i:integer;
+begin if test_read_access(mf_name) then
+ begin reset(mf_file,mf_name);
+ if test_read_access(change_name) then
+ begin reset(change_file,change_name);
+ if test_access(read_access_mode,good_file_path) then
+ reset(style_file,real_name_of_file)
+ else begin i:=1;
+ while real_name_of_file[i]<>chr(0) do incr(i);
+ print_nl('! Can''t open the style file ',real_name_of_file:i);
+ mark_fatal;
+ jump_out;
+ end;
+ end
+ else begin i:=1; change_name[max_file_name_length]:=' ';
+ while change_name[i]<>' ' do incr(i);
+ print_nl('! Can''t open the change file ',change_name:i);
+ mark_fatal; jump_out;
+ end;
+ end
+else begin i:=1; mf_name[max_file_name_length]:=' ';
+ while mf_name[i]<>' ' do incr(i);
+ print_nl('! Can''t open the METAFONT source file ',mf_name:i);
+ mark_fatal; jump_out;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [26] opening \TeX\ file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens |tex_file|.
+Since this file was listed in the program header, we assume that the
+\PASCAL\ runtime system has checked that a suitable external file name has
+been given.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(tex_file);
+@y
+@ The following code opens |tex_file|.
+The |scan_args| procedure is used to set up |tex_name| as required.
+@^system dependencies@>
+
+@<Set init...@>=
+scan_args;
+rewrite(tex_file,tex_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [28] faster input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+var final_limit:0..buf_size; {|limit| without trailing blanks}
+begin limit:=0; final_limit:=0;
+if eof(f) then input_ln:=false
+else begin while not eoln(f) do
+ begin buffer[limit]:=xord[f^]; get(f);
+ incr(limit);
+ if buffer[limit-1]<>" " then final_limit:=limit;
+ if limit=buf_size then
+ begin while not eoln(f) do get(f);
+ decr(limit); {keep |buffer[buf_size]| empty}
+ if final_limit>limit then final_limit:=limit;
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ end;
+ read_ln(f); limit:=final_limit; input_ln:=true;
+ end;
+end;
+@y
+With Berkeley {\mc UNIX} we call an external C procedure, |line_read|.
+That routine fills |buffer| from 0 onwards with the |xord|'ed values
+of the next line, setting |limit| appropriately (ignoring trailing
+blanks).
+It will stop if |limit=buf_size|, and the following will cause an error
+message.
+Note: for bootstrapping purposes it is all right to use the original form
+of |input_ln|; it will just run slower.
+
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+begin limit:=0;
+if test_eof(f) then input_ln:=false
+else begin line_read(f);
+ if limit=buf_size then
+ begin
+ decr(limit); {keep |buffer[buf_size]| empty}
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ input_ln:=true;
+ end;
+end;
+@z
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [87] faster flush_buffer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+done: for k:=1 to j do write(tex_file,xchr[out_buf[k]]);
+@y
+done: linewrite(tex_file,j);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [112] print newline at end of run and exit based upon value of history
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Let's put it all together now: \.{MFT} starts and ends here.
+@^system dependencies@>
+
+@y
+Let's put it all together now: \.{MFT} starts and ends here.
+@^system dependencies@>
+
+@d UNIXexit==e@&x@&i@&t
+
+@z
+
+@x
+end.
+@y
+new_line;
+if (history <> spotless) and (history <> harmless_message) then
+ UNIXexit(1)
+else
+ UNIXexit(0);
+end.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [114] system dependent changes--the scan_args procedure
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This module should be replaced, if necessary, by changes to the program
+that are necessary to make \.{MFT} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous modules preserve the module numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new modules, can be inserted here; then only the index
+itself will get a new module number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+The user calls \.{MFT} with arguments on the command line.
+These are either file names or flags (beginning with '-').
+The following globals are for communicating the user's desires to the rest
+of the program. The various |file_name| variables contain strings with
+the full names of those files, as UNIX knows them.
+
+The only flags that affect \.{MFT} are |'-c'| and |'-s'| whose
+status is kept in |no_change| and |no_style|.
+
+@d max_file_name_length==100
+
+@<Globals...@>=
+@!mf_name,@!change_name,@!style_name,@!tex_name,
+ @!real_name_of_file: UNIX_file_name;
+@!no_change,no_style:boolean;
+
+@ The |scan_args| procedure looks at the command line arguments and sets
+the |file_name| variables accordingly.
+
+At least one file name must be present as the first argument:
+the \.{mf} file. It may have
+an extension, or it may omit it to get |'.mf'| added.
+If there is only one file name, the output file name is formed by replacing
+the \.{mf} file name extension by |'.tex'|.
+Thus, the command line \.{mft foo} implies the use of the \METAFONT\
+input file \.{foo.mf} and the output file \.{foo.tex}.
+If this style of command line, with only one argument is used,
+the default style file, |plain.mft|, will be used to provide
+minimal formatting.
+
+An argument beginning with a minus sign is a flag.
+Any letters following the minus sign may cause global
+flag variables to be set.
+Currently, a |c| means that there is a change file, and an |s| means
+that there is a style file. The auxiliary files must
+of course appear in the same order as the flags. For example,
+the flag -sc must be followed by the name of the |style_file| first,
+and then the name of the |change_file|.
+
+@<|scan_args|...@>=
+procedure scan_args;
+var dot_pos,i,a: integer; {indices}
+@!which_file_next: char;
+@!fname: array[1..max_file_name_length-5] of char; {temporary argument holder}
+@!found_mf,@!found_change,found_style: boolean; {|true| when those
+ file names have been seen}
+begin
+setpaths; {Get path for style file from \.{MFINPUTS}}
+found_mf:=false;
+@<Set up null change file@>; found_change:=true; {Default to no change file}
+@<Set up plain style file@>; found_style:=true; {Default to plain style file}
+for a:=1 to argc-1 do begin
+ argv(a,fname); {put argument number |a| into |fname|}
+ if fname[1]<>'-' then begin
+ if not found_mf then
+ @<Get |mf_name| from |fname|, and set up |tex_name@>
+ else
+ if not found_change then begin
+ if which_file_next <> 's' then begin
+ @<Get |change_name| from |fname|@>;
+ which_file_next := 's'
+ end
+ else @<Get |style_name| from |fname|@>
+ end
+ else if not found_style then begin
+ if which_file_next = 's' then begin
+ @<Get |style_name| from |fname|@>;
+ which_file_next := 'c'
+ end;
+ end
+ else @<Print usage error message and quit@>;
+ end
+ else @<Handle flag argument in |fname|@>;
+ end;
+if not found_mf then @<Print usage error message and quit@>;
+end;
+
+@ Use all of |fname| for the |mf_name| if there is a |'.'| in it,
+otherwise add |'.mf'|.
+The \TeX\ file name comes from adding things after the dot.
+The |argv| procedure will not put more than |max_file_name_length-5|
+characters into |fname|, and this leaves enough room in the |file_name|
+variables to add the extensions.
+
+The end of a file name is marked with a |' '|, the convention assumed by
+the |reset| and |rewrite| procedures.
+
+@<Get |mf_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=max_file_name_length-5) do begin
+ mf_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ mf_name[dot_pos]:='.';
+ mf_name[dot_pos+1]:='m';
+ mf_name[dot_pos+2]:='f';
+ mf_name[dot_pos+3]:=' ';
+ end
+else mf_name[i]:=' ';
+for i:=1 to dot_pos do begin
+ tex_name[i]:=mf_name[i];
+ end;
+tex_name[dot_pos+1]:='t';
+tex_name[dot_pos+2]:='e';
+tex_name[dot_pos+3]:='x';
+tex_name[dot_pos+4]:=' ';
+which_file_next := 'z';
+found_mf:=true;
+end
+
+@ Use all of |fname| for the |change_name| if there is a |'.'| in it,
+otherwise add |'.ch'|.
+
+@<Get |change_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=max_file_name_length-5)
+do begin
+ change_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ change_name[dot_pos]:='.';
+ change_name[dot_pos+1]:='c';
+ change_name[dot_pos+2]:='h';
+ change_name[dot_pos+3]:=' ';
+ end
+else change_name[i]:=' ';
+which_file_next := 'z';
+found_change:=true;
+end
+
+@ Use all of |fname| for the |style_name| if there is a |'.'| in it,
+otherwise add |'.mft'|.
+
+
+@<Get |style_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=max_file_name_length-5)
+do begin
+ style_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ style_name[dot_pos]:='.';
+ style_name[dot_pos+1]:='m';
+ style_name[dot_pos+2]:='f';
+ style_name[dot_pos+3]:='t';
+ style_name[dot_pos+4]:=' ';
+ end
+else style_name[i]:=' ';
+which_file_next := 'z';
+found_style:=true;
+end
+
+@
+
+@<Set up null change file@>=
+begin
+ change_name[1]:='/';
+ change_name[2]:='d';
+ change_name[3]:='e';
+ change_name[4]:='v';
+ change_name[5]:='/';
+ change_name[6]:='n';
+ change_name[7]:='u';
+ change_name[8]:='l';
+ change_name[9]:='l';
+ change_name[10]:=' ';
+end
+
+@
+
+@<Set up plain style file@>=
+begin
+ style_name[1]:='p';
+ style_name[2]:='l';
+ style_name[3]:='a';
+ style_name[4]:='i';
+ style_name[5]:='n';
+ style_name[6]:='.';
+ style_name[7]:='m';
+ style_name[8]:='f';
+ style_name[9]:='t';
+ style_name[10]:=' ';
+end
+
+@
+
+@<Handle flag...@>=
+begin
+i:=2;
+while (fname[i]<>' ') and (i<=max_file_name_length-5) do begin
+ if fname[i]='c' then begin found_change:=false;
+ if which_file_next <> 's' then which_file_next := 'c'
+ end
+ else if fname[i]='s' then begin found_style:=false;
+ if which_file_next <> 'c' then which_file_next := 's'
+ end
+ else print_nl('Invalid flag ',xchr[xord[fname[i]]]);
+ incr(i);
+ end;
+end
+
+@
+
+@<Print usage error message and quit@>=
+begin
+print_nl('! Usage: mft file[.mf] [-cs] [change[.ch]] [style[.mft]] ');
+error; mark_fatal;
+jump_out;
+end
+
+@z
diff --git a/systems/knuth/local/mfware/mft_ext.c b/systems/knuth/local/mfware/mft_ext.c
new file mode 100644
index 0000000000..b8fe8d31e6
--- /dev/null
+++ b/systems/knuth/local/mfware/mft_ext.c
@@ -0,0 +1,249 @@
+/* External procedures for MFT*/
+/* (culled from weavext.c and ext.c for WEAVE and TeX) */
+/* Written by: H. Trickey, 11/17/83 */
+/* (modified for 8-bit input by don on 10/16/89) */
+
+/* Note: these procedures aren't necessary; the default input_ln and
+ * flush_buffer routines in tangle/weave work fine on UNIX.
+ * However a very big time improvement is achieved by using these.
+ *
+ * These procedures are the same as for tangle, except for a slight offset
+ * in which characters are output from outbuf in linewrite.
+ */
+
+#define BUF_SIZE 100 /* should agree with mft.web */
+
+#include "mfpaths.h" /* defines default MFINPUTS path */
+#include "h00vars.h" /* defines Pascal I/O structure */
+
+extern short buffer[]; /* 0..BUF_SIZE. Input goes here */
+extern short outbuf[]; /* 0..OUT_BUF_SIZE. Output from here */
+extern short xord[];
+extern char xchr[]; /* character translation arrays */
+extern char limit; /* index into buffer. Note that limit
+ is 0..buf_size (= 0..100) in mft.web; this
+ differs from the definition in weave.web */
+
+/*
+ * lineread reads from the Pascal text file with iorec pointer filep
+ * into buffer[0], buffer[1],..., buffer[limit-1] (and
+ * setting "limit").
+ * Characters are read until a newline is found (which isn't put in the
+ * buffer) or until the next character would go into buffer[BUF_SIZE].
+ * And trailing blanks are to be ignored, so limit is really set to
+ * one past the last non-blank.
+ * The characters need to be translated, so really xord[c] is put into
+ * the buffer when c is read.
+ * If end-of-file is encountered, the funit field of *filep is set
+ * appropriately.
+ */
+lineread(filep)
+struct iorec *filep;
+{
+ register c;
+ register short *cs; /* pointer into buffer where next char goes */
+ register short *cnb; /* last non-blank character input */
+ register FILE *iop; /* stdio-style FILE pointer */
+ register int l; /* how many chars allowed before buffer overflow */
+
+ iop = filep->fbuf;
+ cnb = cs = &(buffer[0]);
+ l = BUF_SIZE;
+ /* overflow when next char would go into buffer[BUF_SIZE] */
+ while (--l>=0 && (c = getc(iop)) != EOF && c!='\n')
+ if((*cs++ = xord[c])!=' ') cnb = cs;
+ if (c == EOF)
+ filep->funit |= EOFF; /* we hit end-of-file */
+ limit = cnb-&(buffer[0]);
+}
+
+/*
+ * linewrite writes to the Pascal text file with iorec pointer filep
+ * from outbuf[1], outbuf[1],..., outbuf[cnt].
+ * (Note the offset of indices vis a vis the tangext version of this.)
+ * Translation is done, so that xchr[c] is output instead of c.
+ */
+ linewrite(filep,cnt)
+ struct iorec *filep;
+ int cnt;
+ {
+ register FILE *iop; /* stdio-style FILE pointer */
+ register short *cs; /* pointer to next character to output */
+ register int l; /* how many characters left to output */
+
+ iop = filep->fbuf;
+ cs = &(outbuf[1]);
+ l = cnt;
+ while (--l>=0) putc(xchr[*cs++],iop);
+}
+
+/*
+** testeof(filep)
+**
+** Test whether or not the Pascal text file with iorec pointer filep
+** has reached end-of-file (when the only I/O on it is done with
+** lineread, above).
+** We may have to read the next character and unget it to see if perhaps
+** the end-of-file is next.
+*/
+
+bool
+testeof(filep)
+register struct iorec *filep;
+{
+ register char c;
+ register FILE *iop; /* stdio-style FILE pointer */
+ if (filep->funit & EOFF)
+ return(TRUE);
+ else { /* check to see if next is EOF */
+ iop = filep->fbuf;
+ c = getc(iop);
+ if (c == EOF)
+ return(TRUE);
+ else {
+ ungetc(c,iop);
+ return(FALSE);
+ }
+ }
+}
+/* and now, some routines to implement paths for file names. These routines
+ are taken from Howard Trickey's port of dvitype (2/19/84). Howard
+ adapted them from his port of TeX, ext.c */
+
+char *inputpath;
+
+char *getenv();
+
+/*
+ * setpaths is called to set up the pointer inputpath
+ * as follows: if the user's environment has a value for TEXINPUTS
+ * then use it; otherwise, use defaultinputpath.
+ */
+setpaths()
+{
+ register char *envpath;
+
+ if ((envpath = getenv("TEXINPUTS")) != NULL)
+ inputpath = envpath;
+ else
+ inputpath = defaultinputpath;
+}
+
+#define maxfilenamelength 100 /* should agree with mft.ch */
+extern char stylename[],realnameoffile[]; /* these have size namelength */
+
+/*
+ * testaccess(amode,filepath)
+ *
+ * Test whether or not the file whose name is in the global curname
+ * can be opened for reading (if mode=READACCESS)
+ * or writing (if mode=WRITEACCESS).
+* [the code here isn't as general as it should be ... it assumes that stylefile
+* is the only file name of interest! Maybe I'll fix this some day -- don]
+ *
+ * The filepath argument is one of the ...FILEPATH constants defined below.
+ * If the filename given in curname does not begin with '/', we try
+ * prepending all the ':'-separated areanames in the appropriate path to the
+ * filename until access can be made, if it ever can.
+ *
+ * The realnameoffile global array will contain the name that yielded an
+ * access success.
+ */
+
+/* note that corresponding constants are defined in mft.ch */
+
+#define READACCESS 4
+#define WRITEACCESS 2
+
+#define NOFILEPATH 0
+#define GOODFILEPATH 3
+
+bool
+testaccess(amode,filepath)
+ int amode,filepath;
+{
+ register bool ok;
+ register char *p;
+ char *curpathplace;
+ int f;
+
+ switch(filepath) {
+ case NOFILEPATH: curpathplace = NULL; break;
+ case GOODFILEPATH: curpathplace = inputpath; break;
+ }
+ if (stylename[0]=='/') /* file name has absolute path */
+ curpathplace = NULL;
+ do {
+ packrealnameoffile(&curpathplace);
+ if (amode==READACCESS)
+ /* use system call "access" to see if we could read it */
+ if (access(realnameoffile,READACCESS)==0) ok = TRUE;
+ else ok = FALSE;
+ else {
+ /* WRITEACCESS: use creat to see if we could create it, but close
+ the file again if we're OK, to let pc open it for real */
+ f = creat(realnameoffile,0666);
+ if (f>=0) ok = TRUE;
+ else ok = FALSE;
+ if (ok)
+ close(f);
+ }
+ } while (!ok && curpathplace != NULL);
+ if (ok) { /* pad realnameoffile with blanks, as Pascal wants */
+ for (p = realnameoffile; *p != '\0'; p++)
+ /* nothing: find end of string */ ;
+ while (p < &(realnameoffile[maxfilenamelength]))
+ *p++ = ' ';
+ }
+ return (ok);
+}
+
+/*
+ * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp,
+ * followed by '/', followed by the characters in stylename up until the
+ * first blank there, and finally a '\0'. The cpp pointer is left pointing
+ * at the next directory in the path.
+ * But: if *cpp == NULL, then we are supposed to use stylename as is.
+ */
+packrealnameoffile(cpp)
+ char **cpp;
+{
+ register char *p,*realname;
+
+ realname = realnameoffile;
+ if ((p = *cpp)!=NULL) {
+ while ((*p != ':') && (*p != '\0')) {
+ *realname++ = *p++;
+ if (realname == &(realnameoffile[maxfilenamelength-1]))
+ break;
+ }
+ if (*p == '\0') *cpp = NULL; /* at end of path now */
+ else *cpp = p+1; /* else get past ':' */
+ *realname++ = '/'; /* separate the area from the name to follow */
+ }
+ /* now append stylename to realname... */
+ p = stylename;
+ while (*p != ' ') {
+ if (realname >= &(realnameoffile[maxfilenamelength-1])) {
+ fprintf(stderr,"! Full file name is too long\n");
+ break;
+ }
+ *realname++ = *p++;
+ }
+ *realname = '\0';
+}
+
+bool testreadaccess(fn)
+ char *fn;
+{
+ register char *p;
+
+ fn[maxfilenamelength-1] = ' ';
+ p = fn;
+ while (*p++ != ' ');
+ p--;
+ *p = '\0';
+ if (access(fn,4)==0) {
+ *p = ' '; return(TRUE);}
+ else {*p = ' '; return(FALSE);}
+}
diff --git a/systems/knuth/local/mfware/mft_ext.h b/systems/knuth/local/mfware/mft_ext.h
new file mode 100644
index 0000000000..85090d43e6
--- /dev/null
+++ b/systems/knuth/local/mfware/mft_ext.h
@@ -0,0 +1,16 @@
+procedure exit(x : integer);
+ external;
+procedure lineread(var f:textfile);
+ external;
+procedure linewrite(var f:textfile; cnt:integer);
+ external;
+function testeof(var f:textfile):boolean;
+ external;
+(* now, some routines to implement paths for file names. These are borrowed
+ from Howard Trickey's dvitype *)
+procedure setpaths;
+ external;
+function testaccess(accessmode:integer; filepath:integer): boolean;
+ external;
+function testreadaccess(var f:UNIXfilename):boolean;
+ external;
diff --git a/systems/knuth/local/mfware/pktype.ch b/systems/knuth/local/mfware/pktype.ch
new file mode 100644
index 0000000000..0d8e605ca1
--- /dev/null
+++ b/systems/knuth/local/mfware/pktype.ch
@@ -0,0 +1,241 @@
+% Change file for the PKtype processor, for use on Berkeley UNIX systems.
+
+% created by don on 21 Oct 1989 based on GFtype.ch
+% brought up to version 2.3 by don on 18 Nov 89
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{PK$\,$\lowercase{type} changes for Berkeley {\mc UNIX}}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is PKtype, Version 2.3' {printed when the program starts}
+@y
+@d banner=='This is PKtype, Version 2.3 for SunOS'
+ {printed when the program starts}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Case statement default feature of SunOS Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] Redirect PKtype output to correct output file
+% depending on pkout_active value (consistent with GFtype)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print_ln(#)==write_ln(output,#)
+@d print(#)==write(output,#)
+@d t_print_ln(#)==write_ln(typ_file,#)
+@d t_print(#)==write(typ_file,#)
+@y
+@d print_ln(#)==if pkout_active then write_ln(typ_file,#)@+else write_ln(#)
+@d print(#)==if pkout_active then write(typ_file,#)@+else write(#)
+@d t_print_ln==print_ln
+@d t_print==print
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] Include external header and parse the command line
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+@y
+var @<Globals in the outer block@>@/
+@\@=#include "gftopk_ext.h"@>@\
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin @<Set initial values@>@/
+ @<Parse command line arguments@>;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6] Make name_size match the external C routine
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!name_length=80; {maximum length of a file name}
+@y
+@!name_length=100; {maximum length of a file name}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Have abort print only once
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d abort(#)==begin print_ln(' ',#); t_print_ln(' ',#); jump_out; end
+@y
+@d abort(#)==begin print_ln(' ',#); jump_out; end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [30] Change definition of 'byte_file' to correct subrange
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!byte_file=packed file of eight_bits ; {for packed file words}
+@y
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@!UNIX_file_name=packed array[1..name_length] of char;
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [32] Change to file opening
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+specifies the file name. If |eof(f)| is true immediately after
+|reset(f,s)| has acted, we assume that no file named |s| is accessible.
+@^system dependencies@>
+
+@p procedure open_pk_file; {prepares the input for reading}
+begin reset(pk_file,pk_name);
+@y
+specifies the file name.
+
+The local /PASCAL/ runtimes will dump core if the file isn't present.
+We avoid that by using an external procedure.
+@^system dependencies@>
+
+@p procedure open_pk_file; {prepares the input for reading}
+begin if testreadaccess(pk_name) then reset(pk_file,pk_name)
+else abort('Can''t read the PK file');
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [33] Slight change needed by fussy Pascal compiler
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!pk_name,@!typ_name:packed array[1..name_length] of char; {name of input
+ and output files}
+@y
+@!pk_name,@!typ_name:UNIX_file_name; {name of input and output files}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [34] Use modified routine to access pk_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function pk_byte : eight_bits ;
+var temp : eight_bits ;
+begin
+ temp := pk_file^ ;
+ get(pk_file) ;
+ incr(pk_loc) ;
+ pk_byte := temp ;
+@y
+@p function pk_byte : eight_bits ;
+var temp : -128..127;
+begin
+ temp := pk_file^ ;
+ get(pk_file) ;
+ incr(pk_loc) ;
+ if temp<0 then pk_byte:=temp+256 @+else pk_byte := temp ;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [35] Don't open the output file twice
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+open_typ_file ;
+t_print_ln(banner) ;
+@y
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [53--54] Use command line instead of dialog
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* Terminal communication.
+We must get the file names and determine whether input is to be in
+hexadecimal or binary. To do this, we use the standard input path
+name. We need a procedure to flush the input buffer. For most systems,
+this will be an empty statement. For other systems, a |print_ln| will
+provide a quick fix. We also need a routine to get a line of input from
+the terminal. On some systems, a simple |read_ln| will do. Finally,
+a macro to print a string to the first blank is required.
+
+@d flush_buffer == begin end
+@d get_line(#) == if eoln(input) then read_ln(input) ;
+ i := 1 ;
+ while not (eoln(input) or eof(input)) do begin
+ #[i] := input^ ;
+ incr(i) ;
+ get(input) ;
+ end ;
+ #[i] := ' '
+
+@ @p procedure dialog ;
+var i : integer ; {index variable}
+buffer : packed array [1..name_length] of char; {input buffer}
+begin
+ for i := 1 to name_length do begin
+ typ_name[i] := ' ' ;
+ pk_name[i] := ' ' ;
+ end;
+ print('Input file name: ') ;
+ flush_buffer ;
+ get_line(pk_name) ;
+ print('Output file name: ') ;
+ flush_buffer ;
+ get_line(typ_name) ;
+end ;
+@y
+@* Command line parsing.
+The command line issued to start \.{PKtype} should also contain the
+names of the input (PK) file, and optionally, the name of an output
+file to receive the output from \.{PKtype}. This segment decodes
+the command line, and saves the file names in the appropriate variables.
+@^system dependencies@>
+
+@d dialog== {we omit the dialog}
+
+@<Parse command...@>=
+if (argc < 2) or (argc > 3) then
+ begin print_ln('Usage: pktype PKfile [outputfile]');@/
+ goto final_end; { we will be unstructured just this once }
+ end;
+argv(1, pk_name);
+if argc > 2 then
+ begin argv(2, typ_name);@/
+ rewrite(typ_file,typ_name);@/
+ pkout_active := true;@/
+ write_ln(banner); {standard output gets only this}
+ end;
+print_ln(banner)
+
+@ @<Glob...@>=
+@!pkout_active: boolean; {has the |typ_file| been opened?}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [56] Final touchup
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{PKtype} work at a particular installation.
+Any additional routines should be inserted here.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{PKtype} work at a particular installation.
+Any additional routines should be inserted here.
+@^system dependencies@>
+
+We need to initialize the value of the new boolean variable that
+we introduced.
+
+@<Set initial ...@>=
+pkout_active:=false;
+@z
+
diff --git a/systems/knuth/local/mfware/pktype.web b/systems/knuth/local/mfware/pktype.web
new file mode 100644
index 0000000000..eac85ee36f
--- /dev/null
+++ b/systems/knuth/local/mfware/pktype.web
@@ -0,0 +1,1158 @@
+% PKtype.web
+%
+% PKtype verifies the correctness of a PK file.
+%
+% Preliminary 0.0 version: May, 1985
+% First release, 0.9 version: 8 May 1985
+% Updated to reflect new pk format, 2.0 version: 25 July 1985
+% Updated again for new pk format, 2.1 version: 15 August 1985
+% Docmentation updated, 2.2 version: 17 November 1987
+%
+% slight editing to correspond with MFware report -- don, 21 Oct 89
+% additional editing suggested by P Breitenlohner -- don, 18 Nov 89
+% Corrected two typos -- 21 Dec 96 (don)
+
+\def\versiondate{18 November 1989}
+%
+\def\hang{\hangindent 3em\noindent\ignorespaces}
+\font\ninerm=cmr9
+\font\logo=logo10 % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\def\PASCAL{Pascal}
+\def\tamu{Texas A\char38 M}
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index
+\def\title{PKtype}
+\def\contentspagenumber{0}
+\def\topofcontents{\null
+ \def\titlepage{F} % include headline on the contents page
+ \def\rheader{\mainfont\hfil \contentspagenumber}
+ \vfill
+ \centerline{\titlefont The {\ttitlefont PKtype} processor}
+ \vskip 15pt
+ \centerline{(Version 2.3, \versiondate)}
+ \vfill}
+\def\botofcontents{\vfill
+ \centerline{\hsize 5in\baselineskip9pt
+ \vbox{\ninerm\noindent
+ The preparation of this report
+ was supported in part by the National Science
+ Foundation under grants IST-8201926 and MCS-8300984,
+ and by the System Development Foundation. `\TeX' is a
+ trademark of the American Mathematical Society.}}}
+\pageno=\contentspagenumber \advance\pageno by 1
+
+@* Introduction.
+This program reads a \.{PK} file, verifies that it is in the correct
+format, and writes it in textual format.
+
+@ The |banner| string defined here should be changed whenever \.{PKtype}
+gets modified.
+
+@d banner=='This is PKtype, Version 2.3' {printed when the program starts}
+
+@ This program is written in standard \PASCAL, except where it is necessary
+to use extensions; for example, \.{PKtype} must read files whose names
+are dynamically specified, and that would be impossible in pure \PASCAL.
+
+@d othercases == others: {default for cases not listed explicitly}
+@d endcases == @+end {follows the default case in an extended |case| statement}
+@f othercases == else
+@f endcases == end
+
+@ Both the input and output come from binary files. On line interaction
+is handled through \PASCAL's standard |input| and |output| files. Two macros
+are used to write to the type file, so this output can easily be redirected.
+
+@d print_ln(#)==write_ln(output,#)
+@d print(#)==write(output,#)
+@d t_print_ln(#)==write_ln(typ_file,#)
+@d t_print(#)==write(typ_file,#)
+
+@p program PKtype(@!input,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+procedure initialize; {this procedure gets things started properly}
+ var i:integer; {loop index for initializations}
+ begin print_ln(banner);@/
+ @<Set initial values@>@/
+ end;
+
+@ If the program has to stop prematurely, it goes to the
+`|final_end|'.
+
+@d final_end=9999 {label for the end of it all}
+
+@<Labels...@>=final_end;
+
+@ These constants determine the maximum length of a file name and the length
+of the terminal line, as well as the widest character that can be translated.
+@^system dependencies@>
+
+@<Constants...@>=
+@!name_length=80; {maximum length of a file name}
+@!terminal_line_length=132; {maximum length of an input line}
+
+@ Here are some macros for common programming idioms.
+
+@d incr(#) == #:=#+1 {increase a variable by unity}
+@d decr(#) == #:=#-1 {decrease a variable by unity}
+@d do_nothing == {empty statement}
+
+@ It is possible that a malformed packed file (heaven forbid!) or some other
+error might be detected by this program. Such errors might occur in a deeply
+nested procedure, so the procedure called |jump_out| has been added to transfer
+to the very end of the program with an error message.
+
+@d abort(#)==begin print_ln(' ',#); t_print_ln(' ',#); jump_out; end
+
+@p procedure jump_out;
+begin goto final_end;
+end;
+
+@* The character set.
+Like all programs written with the \.{WEB} system, \.{PKtype} can be
+used with any character set. But it uses ASCII code internally, because
+the programming for portable input-output is easier when a fixed internal
+code is used.
+
+The next few sections of \.{PKtype} have therefore been copied from the
+analogous ones in the \.{WEB} system routines. They have been considerably
+simplified, since \.{PKtype} need not deal with the controversial
+ASCII codes less than @'40 or greater than @'176.
+
+@<Types...@>=
+@!ASCII_code=" ".."~"; {a subrange of the integers}
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lower case
+letters. Nowadays, of course, we need to deal with both upper and lower case
+alphabets in a convenient way, especially in a program like \.{PKtype}.
+So we shall assume that the \PASCAL\ system being used for \.{PKtype}
+has a character set containing at least the standard visible characters
+of ASCII code (|"!"| through |"~"|).
+
+Some \PASCAL\ compilers use the original name |char| for the data type
+associated with the characters in text files, while other \PASCAL s
+consider |char| to be a 64-element subrange of a larger data type that has
+some other name. In order to accommodate this difference, we shall use
+the name |text_char| to stand for the data type of the characters in the
+output file. We shall also assume that |text_char| consists of
+the elements |chr(first_text_char)| through |chr(last_text_char)|,
+inclusive. The following definitions should be adjusted if necessary.
+@^system dependencies@>
+
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=127 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+
+@ The \.{PKtype} processor converts 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...@>=
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [0..255] of text_char;
+ {specifies conversion of output characters}
+
+@ Under our assumption that the visible characters of standard ASCII are
+all present, the following assignment statements initialize the
+|xchr| array properly, without needing any system-dependent changes.
+
+@<Set init...@>=
+for i:=0 to @'37 do xchr[i]:='?';
+xchr[@'40]:=' ';
+xchr[@'41]:='!';
+xchr[@'42]:='"';
+xchr[@'43]:='#';
+xchr[@'44]:='$';
+xchr[@'45]:='%';
+xchr[@'46]:='&';
+xchr[@'47]:='''';@/
+xchr[@'50]:='(';
+xchr[@'51]:=')';
+xchr[@'52]:='*';
+xchr[@'53]:='+';
+xchr[@'54]:=',';
+xchr[@'55]:='-';
+xchr[@'56]:='.';
+xchr[@'57]:='/';@/
+xchr[@'60]:='0';
+xchr[@'61]:='1';
+xchr[@'62]:='2';
+xchr[@'63]:='3';
+xchr[@'64]:='4';
+xchr[@'65]:='5';
+xchr[@'66]:='6';
+xchr[@'67]:='7';@/
+xchr[@'70]:='8';
+xchr[@'71]:='9';
+xchr[@'72]:=':';
+xchr[@'73]:=';';
+xchr[@'74]:='<';
+xchr[@'75]:='=';
+xchr[@'76]:='>';
+xchr[@'77]:='?';@/
+xchr[@'100]:='@@';
+xchr[@'101]:='A';
+xchr[@'102]:='B';
+xchr[@'103]:='C';
+xchr[@'104]:='D';
+xchr[@'105]:='E';
+xchr[@'106]:='F';
+xchr[@'107]:='G';@/
+xchr[@'110]:='H';
+xchr[@'111]:='I';
+xchr[@'112]:='J';
+xchr[@'113]:='K';
+xchr[@'114]:='L';
+xchr[@'115]:='M';
+xchr[@'116]:='N';
+xchr[@'117]:='O';@/
+xchr[@'120]:='P';
+xchr[@'121]:='Q';
+xchr[@'122]:='R';
+xchr[@'123]:='S';
+xchr[@'124]:='T';
+xchr[@'125]:='U';
+xchr[@'126]:='V';
+xchr[@'127]:='W';@/
+xchr[@'130]:='X';
+xchr[@'131]:='Y';
+xchr[@'132]:='Z';
+xchr[@'133]:='[';
+xchr[@'134]:='\';
+xchr[@'135]:=']';
+xchr[@'136]:='^';
+xchr[@'137]:='_';@/
+xchr[@'140]:='`';
+xchr[@'141]:='a';
+xchr[@'142]:='b';
+xchr[@'143]:='c';
+xchr[@'144]:='d';
+xchr[@'145]:='e';
+xchr[@'146]:='f';
+xchr[@'147]:='g';@/
+xchr[@'150]:='h';
+xchr[@'151]:='i';
+xchr[@'152]:='j';
+xchr[@'153]:='k';
+xchr[@'154]:='l';
+xchr[@'155]:='m';
+xchr[@'156]:='n';
+xchr[@'157]:='o';@/
+xchr[@'160]:='p';
+xchr[@'161]:='q';
+xchr[@'162]:='r';
+xchr[@'163]:='s';
+xchr[@'164]:='t';
+xchr[@'165]:='u';
+xchr[@'166]:='v';
+xchr[@'167]:='w';@/
+xchr[@'170]:='x';
+xchr[@'171]:='y';
+xchr[@'172]:='z';
+xchr[@'173]:='{';
+xchr[@'174]:='|';
+xchr[@'175]:='}';
+xchr[@'176]:='~';
+for i:=@'177 to 255 do xchr[i]:='?';
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|.
+
+@<Set init...@>=
+for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40;
+for i:=" " to "~" do xord[xchr[i]]:=i;
+
+@* Packed file format.
+The packed file format is a compact representation of the data contained in a
+\.{GF} file. The information content is the same, but packed (\.{PK}) files
+are almost always less than half the size of their \.{GF} counterparts. They
+are also easier to convert into a raster representation because they do not
+have a profusion of \\{paint}, \\{skip}, and \\{new\_row} commands to be
+separately interpreted. In addition, the \.{PK} format expressly forbids
+\&{special} commands within a character. The minimum bounding box for each
+character is explicit in the format, and does not need to be scanned for as in
+the \.{GF} format. Finally, the width and escapement values are combined with
+the raster information into character ``packets'', making it simpler in many
+cases to process a character.
+
+A \.{PK} file is organized as a stream of 8-bit bytes. At times, these bytes
+might be split into 4-bit nybbles or single bits, or combined into multiple
+byte parameters. When bytes are split into smaller pieces, the `first' piece
+is always the most significant of the byte. For instance, the first bit of
+a byte is the bit with value 128; the first nybble can be found by dividing
+a byte by 16. Similarly, when bytes are combined into multiple byte
+parameters, the first byte is the most significant of the parameter. If the
+parameter is signed, it is represented by two's-complement notation.
+
+The set of possible eight-bit values is separated into two sets, those that
+introduce a character definition, and those that do not. The values that
+introduce a character definition range from 0 to 239; byte values
+above 239 are interpreted as commands. Bytes that introduce character
+definitions are called flag bytes, and various fields within the byte indicate
+various things about how the character definition is encoded. Command bytes
+have zero or more parameters, and can never appear within a character
+definition or between parameters of another command, where they would be
+interpeted as data.
+
+A \.{PK} file consists of a preamble, followed by a sequence of one or more
+character definitions, followed by a postamble. The preamble command must
+be the first byte in the file, followed immediately by its parameters.
+Any number of character definitions may follow, and any command but the
+preamble command and the postamble command may occur between character
+definitions. The very last command in the file must be the postamble.
+
+@ The packed file format is intended to be easy to read and interpret by
+device drivers. The small size of the file reduces the input/output overhead
+each time a font is loaded. For those drivers that load and save each font
+file into memory, the small size also helps reduce the memory requirements.
+The length of each character packet is specified, allowing the character raster
+data to be loaded into memory by simply counting bytes, rather than
+interpreting each command; then, each character can be interpreted on a demand
+basis. This also makes it possible for a driver to skip a particular
+character quickly if it knows that the character is unused.
+
+@ First, the command bytes will be presented; then the format of the
+character definitions will be defined. Eight of the possible sixteen
+commands (values 240 through 255) are currently defined; the others are
+reserved for future extensions. The commands are listed below. Each command
+is specified by its symbolic name (e.g., \\{pk\_no\_op}), its opcode byte,
+and any parameters. The parameters are followed by a bracketed number
+telling how many bytes they occupy, with the number preceded by a plus sign if
+it is a signed quantity. (Four byte quantities are always signed, however.)
+
+\yskip\hang|pk_xxx1| 240 |k[1]| |x[k]|. This command is undefined in general;
+it functions as a $(k+2)$-byte \\{no\_op} unless special \.{PK}-reading
+programs are being used. \MF\ generates \\{xxx} commands when encountering
+a \&{special} string. It is recommended that |x| be a string having the form
+of a keyword followed by possible parameters relevant to that keyword.
+
+\yskip\hang\\{pk\_xxx2} 241 |k[2]| |x[k]|. Like |pk_xxx1|, but |0<=k<65536|.
+
+\yskip\hang\\{pk\_xxx3} 242 |k[3]| |x[k]|. Like |pk_xxx1|, but
+|0<=k<@t$2^{24}$@>|. \MF\ uses this when sending a \&{special} string whose
+length exceeds~255.
+
+\yskip\hang\\{pk\_xxx4} 243 |k[4]| |x[k]|. Like |pk_xxx1|, but |k| can be
+ridiculously large; |k| musn't be negative.
+
+\yskip\hang|pk_yyy| 244 |y[4]|. This command is undefined in general; it
+functions as a five-byte \\{no\_op} unless special \.{PK} reading programs
+are being used. \MF\ puts |scaled| numbers into |yyy|'s, as a result of
+\&{numspecial} commands; the intent is to provide numeric parameters to
+\\{xxx} commands that immediately precede.
+
+\yskip\hang|pk_post| 245. Beginning of the postamble. This command is
+followed by enough |pk_no_op| commands to make the file a multiple
+of four bytes long. Zero through three bytes are usual, but any number
+is allowed.
+This should make the file easy to read on machines that pack four bytes to
+a word.
+
+\yskip\hang|pk_no_op| 246. No operation, do nothing. Any number of
+|pk_no_op|'s may appear between \.{PK} commands, but a |pk_no_op| cannot be
+inserted between a command and its parameters, between two parameters, or
+inside a character definition.
+
+\yskip\hang|pk_pre| 247 |i[1]| |k[1]| |x[k]| |ds[4]| |cs[4]| |hppp[4]|
+|vppp[4]|. Preamble command. Here, |i| is the identification byte of the
+file, currently equal to 89. The string |x| is merely a comment, usually
+indicating the source of the \.{PK} file. The parameters |ds| and |cs| are
+the design size of the file in $1/2^{20}$ points, and the checksum of the
+file, respectively. The checksum should match the \.{TFM} file and the
+\.{GF} files for this font. Parameters |hppp| and |vppp| are the ratios
+of pixels per point, horizontally and vertically, multiplied by $2^{16}$; they
+can be used to correlate the font with specific device resolutions,
+magnifications, and ``at sizes''. Usually, the name of the \.{PK} file is
+formed by concatenating the font name (e.g., cmr10) with the resolution at
+which the font is prepared in pixels per inch multiplied by the magnification
+factor, and the letters \.{pk}. For instance, cmr10 at 300 dots per inch
+should be named \.{cmr10.300pk}; at one thousand dots per inch and magstephalf,
+it should be named \.{cmr10.1095pk}.
+
+@ We put a few of the above opcodes into definitions for symbolic use by
+this program.
+
+@d pk_id = 89 {the version of \.{PK} file described}
+@d pk_xxx1 = 240 {\&{special} commands}
+@d pk_yyy = 244 {\&{numspecial} commands}
+@d pk_post = 245 {postamble}
+@d pk_no_op = 246 {no operation}
+@d pk_pre = 247 {preamble}
+@d pk_undefined == 248, 249, 250, 251, 252, 253, 254, 255
+
+@ The \.{PK} format has two conflicting goals: to pack character raster and
+size information as compactly as possible, while retaining ease of translation
+into raster and other forms. A suitable compromise was found in the use of
+run-encoding of the raster information. Instead of packing the individual
+bits of the character, we instead count the number of consecutive `black' or
+`white' pixels in a horizontal raster row, and then encode this number. Run
+counts are found for each row from left to right, traversing rows from the
+top to bottom. This is essentially the way the \.{GF} format works.
+Instead of presenting each row individually, however, we concatenate all
+of the horizontal raster rows into one long string of pixels, and encode this
+row. With knowledge of the width of the bit-map, the original character glyph
+can easily be reconstructed. In addition, we do not need special commands to
+mark the end of one row and the beginning of the next.
+
+Next, we place the burden of finding the minimum bounding box on the part
+of the font generator, since the characters will usually be used much more
+often than they are generated. The minimum bounding box is the smallest
+rectangle that encloses all `black' pixels of a character. We also
+eliminate the need for a special end of character marker, by supplying
+exactly as many bits as are required to fill the minimum bounding box, from
+which the end of the character is implicit.
+
+Let us next consider the distribution of the run counts. Analysis of several
+dozen pixel files at 300 dots per inch yields a distribution peaking at four,
+falling off slowly until ten, then a bit more steeply until twenty, and then
+asymptotically approaching the horizontal. Thus, the great majority of our
+run counts will fit in a four-bit nybble. The eight-bit byte is attractive for
+our run-counts, as it is the standard on many systems; however, the wasted four
+bits in the majority of cases seem a high price to pay. Another possibility
+is to use a Huffman-type encoding scheme with a variable number of bits for
+each run-count; this was rejected because of the overhead in fetching and
+examining individual bits in the file. Thus, the character raster definitions
+in the \.{PK} file format are based on the four-bit nybble.
+
+@ An analysis of typical pixel files yielded another interesting statistic:
+Fully 37\char`\%\
+of the raster rows were duplicates of the previous row. Thus, the \.{PK}
+format allows the specification of repeat counts, which indicate how many times
+a horizontal raster row is to be repeated. These repeated rows are taken out
+of the character glyph before individual rows are concatenated into the long
+string of pixels.
+
+For elegance, we disallow a run count of zero. The case of a null raster
+description should be gleaned from the character width and height being equal
+to zero, and no raster data should be read. No other zero counts are ever
+necessary. Also, in the absence of repeat counts, the repeat value is set to
+be zero (only the original row is sent.) If a repeat count is seen, it takes
+effect on the current row. The current row is defined as the row on which the
+first pixel of the next run count will lie. The repeat count is set back to
+zero when the last pixel in the current row is seen, and the row is sent out.
+
+This poses a problem for entirely black and entirely white rows, however. Let
+us say that the current row ends with four white pixels, and then we have five
+entirely empty rows, followed by a black pixel at the beginning of the next
+row, and the character width is ten pixels. We would like to use a repeat
+count, but there is no legal place to put it. If we put it before the white
+run count, it will apply to the current row. If we put it after, it applies
+to the row with the black pixel at the beginning. Thus, entirely white or
+entirely black repeated rows are always packed as large run counts (in this
+case, a white run count of 54) rather than repeat counts.
+
+@ Now we turn our attention to the actual packing of the run counts and
+repeat counts into nybbles. There are only sixteen possible nybble values.
+We need to indicate run counts and repeat counts. Since the run counts are
+much more common, we will devote the majority of the nybble values to them.
+We therefore indicate a repeat count by a nybble of 14 followed by a packed
+number, where a packed number will be explained later. Since the repeat
+count value of one is so common, we indicate a repeat one command by a single
+nybble of 15. A 14 followed by the packed number 1 is still legal for a
+repeat one count. The run counts are coded directly as packed
+numbers.
+
+For packed numbers, therefore, we have the nybble values 0 through 13. We
+need to represent the positive integers up to, say, $2^{31}-1$. We would
+like the more common smaller numbers to take only one or two nybbles, and
+the infrequent large numbers to take three or more. We could therefore
+allocate one nybble value to indicate a large run count taking three or more
+nybbles. We do this with the value 0.
+
+@ We are left with the values 1 through 13. We can allocate some of these, say
+|dyn_f|, to be one-nybble run counts.
+These will work for the run counts |1..dyn_f|. For subsequent run
+counts, we will use a nybble greater than |dyn_f|, followed by a second nybble,
+whose value can run from 0 through 15. Thus, the two-nybble values will
+run from |dyn_f+1..(13-dyn_f)*16+dyn_f|. We have our definition of large run
+count values now, being all counts greater than |(13-dyn_f)*16+dyn_f|.
+
+We can analyze our several dozen pixel files and determine an optimal value of
+|dyn_f|, and use this value for all of the characters. Unfortunately, values
+of |dyn_f| that pack small characters well tend to pack the large characters
+poorly, and values that pack large characters well are not efficient for the
+smaller characters. Thus, we choose the optimal |dyn_f| on a character basis,
+picking the value that will pack each individual character in the smallest
+number of nybbles. Legal values of |dyn_f| run from 0 (with no one-nybble run
+counts) to 13 (with no two-nybble run counts).
+
+@ Our only remaining task in the coding of packed numbers is the large run
+counts. We use a scheme suggested by D.~E.~Knuth
+@^Knuth, Donald Ervin@>
+that simply and elegantly represents arbitrarily large values. The
+general scheme to represent an integer |i| is to write its hexadecimal
+representation, with leading zeros removed. Then we count the number of
+digits, and prepend one less than that many zeros before the hexadecimal
+representation. Thus, the values from one to fifteen occupy one nybble;
+the values sixteen through 255 occupy three, the values 256 through 4095
+require five, etc.
+
+For our purposes, however, we have already represented the numbers one
+through |(13-dyn_f)*16+dyn_f|. In addition, the one-nybble values have
+already been taken by our other commands, which means that only the values
+from sixteen up are available to us for long run counts. Thus, we simply
+normalize our long run counts, by subtracting |(13-dyn_f)*16+dyn_f+1| and
+adding 16, and then we represent the result according to the scheme above.
+
+@ The final algorithm for decoding the run counts based on the above scheme
+looks like this, assuming that a procedure called \\{get\_nyb} is available
+to get the next nybble from the file, and assuming that the global
+|repeat_count| indicates whether a row needs to be repeated. Note that this
+routine is recursive, but since a repeat count can never directly follow
+another repeat count, it can only be recursive to one level.
+
+@<Packed number procedure@>=
+function pk_packed_num : integer ;
+var i, @!j : integer ;
+begin
+ i := get_nyb ;
+ if i = 0 then begin
+ repeat j := get_nyb ; incr(i) ; until j <> 0 ;
+ while i > 0 do begin j := j * 16 + get_nyb ; decr(i) ; end ;
+ pk_packed_num := j - 15 + (13-dyn_f)*16 + dyn_f ;
+ end else if i <= dyn_f then
+ pk_packed_num := i
+ else if i < 14 then
+ pk_packed_num := (i-dyn_f-1)*16+get_nyb+dyn_f+1
+ else begin
+ if repeat_count <> 0 then abort('Second repeat count for this row!') ;
+@.Second repeat count...@>
+ repeat_count := 1; {prevent recursion more than one level}
+ if i = 14 then repeat_count := pk_packed_num;
+ send_out(true, repeat_count) ;
+ pk_packed_num := pk_packed_num ;
+ end ;
+end ;
+
+@ For low resolution fonts, or characters with `gray' areas, run encoding can
+often make the character many times larger. Therefore, for those characters
+that cannot be encoded efficiently with run counts, the \.{PK} format allows
+bit-mapping of the characters. This is indicated by a |dyn_f| value of
+14. The bits are packed tightly, by concatenating all of the horizontal raster
+rows into one long string, and then packing this string eight bits to a byte.
+The number of bytes required can be calculated by |(width*height+7) div 8|.
+This format should only be used when packing the character by run counts takes
+more bytes than this, although, of course, it is legal for any character.
+Any extra bits in the last byte should be set to zero.
+
+@ At this point, we are ready to introduce the format for a character
+descriptor. It consists of three parts: a flag byte, a character preamble,
+and the raster data. The most significant four bits of the flag byte
+yield the |dyn_f| value for that character. (Notice that only values of
+0 through 14 are legal for |dyn_f|, with 14 indicating a bit mapped character;
+thus, the flag bytes do not conflict with the command bytes, whose upper nybble
+is always 15.) The next bit (with weight 8) indicates whether the first run
+count is a black count or a white count, with a one indicating a black count.
+For bit-mapped characters, this bit should be set to a zero. The next bit
+(with weight 4) indicates whether certain later parameters (referred to as size
+parameters) are given in one-byte or two-byte quantities, with a one indicating
+that they are in two-byte quantities. The last two bits are concatenated on to
+the beginning of the packet-length parameter in the character preamble,
+which will be explained below.
+
+However, if the last three bits of the flag byte are all set (normally
+indicating that the size parameters are two-byte values and that a 3 should be
+prepended to the length parameter), then a long format of the character
+preamble should be used instead of one of the short forms.
+
+Therefore, there are three formats for the character preamble; the one that
+is used depends on the least significant three bits of the flag byte. If the
+least significant three bits are in the range zero through three, the short
+format is used. If they are in the range four through six, the extended short
+format is used. Otherwise, if the least significant bits are all set, then
+the long form of the character preamble is used. The preamble formats are
+explained below.
+
+\yskip\hang Short form: |flag[1]| |pl[1]| |cc[1]| |tfm[3]| |dm[1]| |w[1]|
+|h[1]| |hoff[+1]| |voff[+1]|.
+If this format of the character preamble is used, the above
+parameters must all fit in the indicated number of bytes, signed or unsigned
+as indicated. Almost all of the standard \TeX\ font characters fit; the few
+exceptions are fonts such as \.{cminch}.
+
+\yskip\hang Extended short form: |flag[1]| |pl[2]| |cc[1]| |tfm[3]| |dm[2]|
+|w[2]| |h[2]| |hoff[+2]| |voff[+2]|. Larger characters use this extended
+format.
+
+\yskip\hang Long form: |flag[1]| |pl[4]| |cc[4]| |tfm[4]| |dx[4]| |dy[4]|
+|w[4]| |h[4]| |hoff[4]| |voff[4]|. This is the general format that
+allows all of the
+parameters of the \.{GF} file format, including vertical escapement.
+\vskip\baselineskip
+The |flag| parameter is the flag byte. The parameter |pl| (packet length)
+contains the offset
+of the byte following this character descriptor, with respect to the beginning
+of the |tfm| width parameter. This is given so a \.{PK} reading program can,
+once it has read the flag byte, packet length, and character code (|cc|), skip
+over the character by simply reading this many more bytes. For the two short
+forms of the character preamble, the last two bits of the flag byte should be
+considered the two most-significant bits of the packet length. For the short
+format, the true packet length might be calculated as |(flag mod 4)*256+pl|;
+for the short extended format, it might be calculated as
+|(flag mod 4)*65536+pl|.
+
+The |w| parameter is the width and the |h| parameter is the height in pixels
+of the minimum bounding box. The |dx| and |dy| parameters are the horizontal
+and vertical escapements, respectively. In the short formats, |dy| is assumed
+to be zero and |dm| is |dx| but in pixels;
+in the long format, |dx| and |dy| are both
+in pixels multiplied by $2^{16}$. The |hoff| is the horizontal offset from the
+upper left pixel to the reference pixel; the |voff| is the vertical offset.
+They are both given in pixels, with right and down being positive. The
+reference pixel is the pixel that occupies the unit square in \MF; the
+\MF\ reference point is the lower left hand corner of this pixel. (See the
+example below.)
+
+@ \TeX\ requires all characters that have the same character codes
+modulo 256 to have also the same |tfm| widths and escapement values. The \.{PK}
+format does not itself make this a requirement, but in order for the font to
+work correctly with the \TeX\ software, this constraint should be observed.
+(The standard version of \TeX\ cannot output character codes greater
+than 255, but extended versions do exist.)
+
+Following the character preamble is the raster information for the
+character, packed by run counts or by bits, as indicated by the flag byte.
+If the character is packed by run counts and the required number of nybbles
+is odd, then the last byte of the raster description should have a zero
+for its least significant nybble.
+
+@ As an illustration of the \.{PK} format, the character \char4\ from the font
+amr10 at 300 dots per inch will be encoded. This character was chosen
+because it illustrates some
+of the borderline cases. The raster for the character looks like this (the
+row numbers are chosen for convenience, and are not \MF's row numbers.)
+
+\vskip\baselineskip
+{\def\smbox{\vrule height 7pt width 7pt depth 0pt \hskip 3pt}%
+\catcode`\*=\active \let*=\smbox
+\centerline{\vbox{\baselineskip=10pt
+\halign{\hfil#\quad&&\hfil#\hfil\cr
+0& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+1& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+2& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+3& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+4& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+5& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+6& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+7\cr
+8\cr
+9& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+10& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+11& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+12& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+13& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+14& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+15& & & & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*& & \cr
+16& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+17& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+18& & & & &*&*& & & & & & & & & & & & &*&*& & \cr
+19\cr
+20\cr
+21\cr
+22& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+23& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+24& & &*&*& & & & & & & & & & & & & & & & &*&*\cr
+25& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+26& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+27& & &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+28&+& &*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*&*\cr
+&\hphantom{*}&\hphantom{*}\cr
+}}}}
+The width of the minimum bounding box for this character is 20; its height
+is 29. The `+' represents the reference pixel; notice how it lies outside the
+minimum bounding box. The |hoff| value is $-2$, and the |voff| is~28.
+
+The first task is to calculate the run counts and repeat counts. The repeat
+counts are placed at the first transition (black to white or white to black)
+in a row, and are enclosed in brackets. White counts are enclosed in
+parentheses. It is relatively easy to generate the counts list:
+\vskip\baselineskip
+\centerline{82 [2] (16) 2 (42) [2] 2 (12) 2 (4) [3]}
+\centerline{16 (4) [2] 2 (12) 2 (62) [2] 2 (16) 82}
+\vskip\baselineskip
+Note that any duplicated rows that are not all white or all black are removed
+before the run counts are calculated. The rows thus removed are rows 5, 6,
+10, 11, 13, 14, 15, 17, 18, 23, and 24.
+
+@ The next step in the encoding of this character is to calculate the optimal
+value of |dyn_f|. The details of how this calculation is done are not
+important here; suffice it to say that there is a simple algorithm that can
+determine the best value of |dyn_f| in one pass over the count list. For this
+character, the optimal value turns out to be 8 (atypically low). Thus, all
+count values less than or equal to 8 are packed in one nybble; those from
+nine to $(13-8)*16+8$ or 88 are packed in two nybbles. The run encoded values
+now become (in hex, separated according to the above list):
+\vskip\baselineskip
+\centerline{\tt D9 E2 97 2 B1 E2 2 93 2 4 E3}
+\centerline{\tt 97 4 E2 2 93 2 C5 E2 2 97 D9}
+\vskip\baselineskip\noindent
+which comes to 36 nybbles, or 18 bytes. This is shorter than the 73 bytes
+required for the bit map, so we use the run count packing.
+
+@ The short form of the character preamble is used because all of the
+parameters fit in their respective lengths. The packet length is therefore
+18 bytes for the raster, plus
+eight bytes for the character preamble parameters following the character
+code, or 26. The |tfm| width for this character is 640796, or {\tt 9C71C} in
+hexadecimal. The horizontal escapement is 25 pixels. The flag byte is
+88 hex, indicating the short preamble, the black first count, and the
+|dyn_f| value of 8. The final total character packet, in hexadecimal, is:
+\vskip\baselineskip
+$$\vbox{\halign{\hfil #\quad&&{\tt #\ }\cr
+Flag byte&88\cr
+Packet length&1A\cr
+Character code&04\cr
+|tfm| width&09&C7&1C\cr
+Horizontal escapement (pixels)&19\cr
+Width of bit map&14\cr
+Height of bit map&1D\cr
+Horizontal offset (signed)&FE\cr
+Vertical offset&1C\cr
+Raster data&D9&E2&97\cr
+&2B&1E&22\cr
+&93&24&E3\cr
+&97&4E&22\cr
+&93&2C&5E\cr
+&22&97&D9\cr}}$$
+
+@* Input and output.
+There are two types of files that this program must deal with---standard
+text files and files of bytes (packed files.)
+For our purposes, we shall consider an eight-bit byte to consist of the
+values |0..255|. If your system does not pack these values to a byte, it is
+no major difficulty; you must only insure that the input function
+|pk_byte| can read packed bytes.
+
+@<Types...@>=
+@!eight_bits=0..255; {packed file byte}
+@!byte_file=packed file of eight_bits ; {for packed file words}
+@^system dependencies@>
+
+@ @<Glob...@>=
+@!pk_file:byte_file; {where the input comes from}
+@!typ_file:text_file; {where the final output goes}
+@^system dependencies@>
+
+@ To prepare these files for input and output, we |reset| and |rewrite| them.
+An extension of \PASCAL\ is needed, since we want to associate files
+with external names that are specified dynamically (i.e., not
+known at compile time). The following code assumes that `|reset(f,s)|'
+does this, when |f| is a file variable and |s| is a string variable that
+specifies the file name. If |eof(f)| is true immediately after
+|reset(f,s)| has acted, we assume that no file named |s| is accessible.
+@^system dependencies@>
+
+@p procedure open_pk_file; {prepares the input for reading}
+begin reset(pk_file,pk_name);
+pk_loc := 0 ;
+end;
+@#
+procedure open_typ_file; {prepares to write text data to the |typ_file|}
+begin rewrite(typ_file,typ_name);
+end;
+
+@ We need a place to store the names of the input and output file, as well
+as a byte counter for the output file.
+
+@<Glob...@>=
+@!pk_name,@!typ_name:packed array[1..name_length] of char; {name of input
+ and output files}
+@!pk_loc:integer; {how many bytes have we read?}
+
+@ We also need a function that will get a single byte from the \.{pk} file.
+
+@p function pk_byte : eight_bits ;
+var temp : eight_bits ;
+begin
+ temp := pk_file^ ;
+ get(pk_file) ;
+ incr(pk_loc) ;
+ pk_byte := temp ;
+end ;
+
+@ Now we are ready to open the files.
+
+@<Open files@>=
+open_pk_file ;
+open_typ_file ;
+t_print_ln(banner) ;
+t_print('Input file: ') ;
+i := 1 ;
+while pk_name[i] <> ' ' do begin
+ t_print(pk_name[i]) ; incr(i) ;
+end ;
+t_print_ln(' ')
+
+@ As we are reading the packed file, we often need to fetch 16 and 32 bit
+quantities. Here we have two procedures to do this.
+
+@p function get_16 : integer ;
+var a : integer ;
+begin a := pk_byte ; get_16 := a * 256 + pk_byte ; end ;
+@#
+function get_32 : integer ;
+var a : integer ;
+begin a := get_16 ; if a > 32767 then a := a - 65536 ;
+get_32 := a * 65536 + get_16 ; end ;
+
+@ We still need the |term_pos| variable.
+
+@<Glob...@>=
+@!term_pos : integer ; {current terminal position}
+
+@ Now we read and check the preamble of the \.{PK} file. In the preamble, we
+find the |hppp|, |design_size|, |checksum|.
+
+@<Read preamble@>=
+if pk_byte <> pk_pre then abort('Bad PK file: pre command missing!') ;
+@.pre command missing@>
+if pk_byte <> pk_id then abort('Wrong version of PK file!') ;
+@.Wrong version of PK file@>
+j := pk_byte ;
+t_print('''') ;
+for i := 1 to j do t_print(xchr[pk_byte]) ;
+t_print_ln('''') ;
+design_size := get_32 ;
+t_print_ln('Design size = ',design_size:1) ;
+checksum := get_32 ;
+t_print_ln('Checksum = ',checksum:1) ;
+hppp := get_32 ; vppp := get_32 ;
+t_print('Resolution: horizontal = ',hppp:1,' vertical = ',vppp:1) ;
+magnification := round(hppp * 72.27 / 65536) ;
+t_print_ln(' (',magnification:1,' dpi)') ;
+if hppp <> vppp then print_ln('Warning: aspect ratio not 1:1!')
+
+@ Of course, we need to define the above variables.
+
+@<Glob...@>=
+@!magnification : integer ; {resolution at which pixel file is prepared}
+@!design_size : integer ; {design size in FIXes}
+@!checksum : integer ; {checksum of pixel file}
+@!hppp, @!vppp : integer ; {horizontal and vertical points per inch}
+
+@* Character unpacking.
+Here we simply unpack the character, writing the information we glean to the
+|typ_file|.
+
+@<Unpack and write character@>=
+t_print((pk_loc-1):1,': Flag byte = ',flag_byte:1) ;
+dyn_f := flag_byte div 16 ;
+flag_byte := flag_byte mod 16 ;
+turn_on := flag_byte >= 8 ;
+if turn_on then flag_byte := flag_byte - 8 ;
+if flag_byte = 7 then
+ @<Read long character preamble@>
+else if flag_byte > 3 then
+ @<Read extended short character preamble@>
+else
+ @<Read short character preamble@> ;
+t_print_ln(' Character = ',car:1,' Packet length = ',
+ packet_length:1) ;
+t_print_ln(' Dynamic packing variable = ',dyn_f:1) ;
+t_print(' TFM width = ',tfm_width:1,' dx = ',dx:1) ;
+if dy <> 0 then t_print_ln(' dy = ',dy:1) else t_print_ln(' ') ;
+t_print_ln(' Height = ',height:1,' Width = ',width:1,' X-offset = ',
+ x_off:1, ' Y-offset = ',y_off:1) ;
+@<Read and translate raster description@> ;
+if end_of_packet <> pk_loc then abort('Bad PK file: Bad packet length!')
+@.Bad packet length@>
+
+@ We need a whole lot of globals used but not defined up there.
+
+@<Glob...@>=
+@!i, @!j : integer ; {index pointers}
+@!flag_byte : integer ; {the byte that introduces the character definition}
+@!end_of_packet : integer ; {where we expect the end of the packet to be}
+@!width, @!height : integer ; {width and height of character}
+@!x_off, @!y_off : integer ; {x and y offsets of character}
+@!tfm_width : integer ; {character tfm width}
+@!tfms : array [0..255] of integer ; {character tfm widths}
+@!dx, @!dy : integer ; {escapement values}
+@!dxs, @!dys : array [0..255] of integer ; {escapement values}
+@!status : array[0..255] of boolean ; {has the character been seen?}
+@!dyn_f : integer ; {dynamic packing variable}
+@!car : integer ; {the character we are reading}
+@!packet_length : integer ; {the length of the character packet}
+
+@ Now, the preamble reading modules. First, we have the general case: the
+long character preamble format.
+
+@<Read long character preamble@>=
+begin
+ packet_length := get_32 ; car := get_32 ;
+ end_of_packet := packet_length + pk_loc ;
+ packet_length := packet_length + 9 ;
+ tfm_width := get_32 ;
+ dx := get_32 ; dy := get_32 ;
+ width := get_32 ;
+ height := get_32 ;
+ x_off := get_32 ; y_off := get_32 ;
+end
+
+@ This module reads the character preamble with double byte parameters.
+
+@<Read extended short character preamble@>=
+begin
+ packet_length := (flag_byte - 4) * 65536 + get_16 ;
+ car := pk_byte ;
+ end_of_packet := packet_length + pk_loc ;
+ packet_length := packet_length + 4 ;
+ i := pk_byte ;
+ tfm_width := i * 65536 + get_16 ;
+ dx := get_16 * 65536 ;
+ dy := 0 ;
+ width := get_16 ;
+ height := get_16 ;
+ x_off := get_16 ; y_off := get_16 ;
+ if x_off > 32767 then x_off := x_off - 65536 ;
+ if y_off > 32767 then y_off := y_off - 65536 ;
+end
+
+@ Here we read the most common character preamble, that with single byte
+parameters.
+
+@<Read short character preamble@>=
+begin
+ packet_length := flag_byte * 256 + pk_byte ;
+ car := pk_byte ;
+ end_of_packet := packet_length + pk_loc ;
+ packet_length := packet_length + 3 ;
+ i := pk_byte ;
+ tfm_width := i * 65536 + get_16 ;
+ dx := pk_byte * 65536 ;
+ dy := 0 ;
+ width := pk_byte ;
+ height := pk_byte ;
+ x_off := pk_byte ; y_off := pk_byte ;
+ if x_off > 127 then x_off := x_off - 256 ;
+ if y_off > 127 then y_off := y_off - 256 ;
+end
+
+@ Now we have the most important part of the program, where we actually
+interpret the commands in the raster description. First of all, we need
+a procedure to get a single nybble from the file, as well as one to get
+a single bit.
+
+@p function get_nyb : integer ;
+var temp : eight_bits ;
+begin
+ if bit_weight = 0 then begin
+ input_byte := pk_byte ;
+ bit_weight := 16 ;
+ end ;
+ temp := input_byte div bit_weight ;
+ input_byte := input_byte - temp * bit_weight ;
+ bit_weight := bit_weight div 16 ;
+ get_nyb := temp ;
+end ;
+@#
+function get_bit : boolean ;
+var temp : boolean ;
+begin
+ bit_weight := bit_weight div 2 ;
+ if bit_weight = 0 then begin
+ input_byte := pk_byte ;
+ bit_weight := 128 ;
+ end ;
+ temp := input_byte >= bit_weight ;
+ if temp then
+ input_byte := input_byte - bit_weight ;
+ get_bit := temp ;
+end ;
+
+@ We also need a function to write output to the screen. We put as many
+counts on a line as possible, to reduce the volume of output. Each count
+will appear as a number, with white counts enclosed by parentheses and repeat
+counts by brackets.
+
+@p procedure send_out(@!repeat_count: boolean ; @!value : integer ) ;
+var i, len : integer ;
+begin
+ i := 10 ; len := 1 ;
+ while value >= i do begin incr(len) ; i := i * 10 ; end ;
+ if repeat_count or not turn_on then len := len + 2 ;
+ if term_pos + len > 78 then begin
+ term_pos := len + 2 ; t_print_ln(' ') ; t_print(' ') ;
+ end else term_pos := term_pos + len ;
+ if repeat_count then t_print('[', value:1, ']')
+ else if turn_on then t_print(value:1)
+ else t_print('(', value:1, ')') ;
+end ;
+@<Packed number procedure@>
+
+@ Now, the globals to help communication between these procedures.
+
+@<Glob...@>=
+@!input_byte : eight_bits ; {the byte we are currently decimating}
+@!bit_weight : eight_bits ; {weight of the current bit}
+@!nybble : eight_bits ; {the current nybble}
+
+@ And the main procedure.
+
+@<Read and translate raster description@>=
+bit_weight := 0 ;
+if dyn_f = 14 then
+ @<Get raster by bits@>
+else @<Create normally packed raster@>
+
+@ If |dyn_f=14|, then we need to get the raster representation
+one bit at a time.
+
+@<Get raster by bits@>=
+begin
+for i := 1 to height do begin
+ t_print(' ') ;
+ for j := 1 to width do
+ if get_bit then t_print('*')
+ else t_print('.') ;
+ t_print_ln(' ') ;
+end;
+end
+
+@ Otherwise, we translate the bit counts into the raster rows. |count|
+contains the number of bits of the current color, and |turn_on| indicates
+whether or not they should be black. |rows_left| contains the number of
+rows to be sent.
+
+@<Create normally packed raster@>=
+begin
+term_pos := 2 ;
+t_print(' ') ;
+rows_left := height ;
+h_bit := width ;
+repeat_count := 0 ;
+while rows_left > 0 do begin
+ count := pk_packed_num ;
+ send_out(false, count) ;
+ if count >= h_bit then begin
+ rows_left := rows_left - repeat_count - 1 ;
+ repeat_count := 0 ;
+ count := count - h_bit ;
+ h_bit := width ;
+ rows_left := rows_left - count div width ;
+ count := count mod width ;
+ end ;
+ h_bit := h_bit - count ;
+ turn_on := not turn_on ;
+end ;
+t_print_ln(' ') ;
+if (rows_left <> 0) or (h_bit <> width) then
+ abort('Bad PK file: More bits than required!');
+@.More bits than required@>
+end
+
+@ We need to declare the repeat flag, bit counter, and color flag here.
+
+@<Glob...@>=
+@!repeat_count : integer ; {how many times to repeat the next row?}
+@!rows_left : integer ; {how many rows left?}
+@!turn_on : boolean ; {are we black here?}
+@!h_bit : integer ; {what is our horizontal position?}
+@!count : integer ; {how many bits of current color left?}
+
+@ If any specials are found, we write them out here.
+
+@d four_cases(#)==#,#+1,#+2,#+3
+
+@p procedure skip_specials ;
+var @!i, @!j: integer ;
+begin
+ repeat
+ flag_byte := pk_byte ;
+ if flag_byte >= 240 then
+ case flag_byte of
+ four_cases(pk_xxx1):
+begin
+ t_print((pk_loc-1):1,': Special: ''') ;
+ i := 0 ;
+ for j := pk_xxx1 to flag_byte do i := 256 * i + pk_byte ;
+ for j := 1 to i do t_print(xchr[pk_byte]) ;
+ t_print_ln('''') ;
+end ;
+ pk_yyy : t_print_ln((pk_loc-1):1,': Num special: ',get_32:1) ;
+ pk_post : t_print_ln((pk_loc-1):1,': Postamble') ;
+ pk_no_op : t_print_ln((pk_loc-1):1,': No op') ;
+ pk_pre, pk_undefined : abort('Unexpected ', flag_byte:1,'!') ;
+@.Unexpected bbb@>
+ endcases ;
+ until (flag_byte < 240) or (flag_byte = pk_post) ;
+end ;
+
+@* Terminal communication.
+We must get the file names and determine whether input is to be in
+hexadecimal or binary. To do this, we use the standard input path
+name. We need a procedure to flush the input buffer. For most systems,
+this will be an empty statement. For other systems, a |print_ln| will
+provide a quick fix. We also need a routine to get a line of input from
+the terminal. On some systems, a simple |read_ln| will do. Finally,
+a macro to print a string to the first blank is required.
+
+@d flush_buffer == begin end
+@d get_line(#) == if eoln(input) then read_ln(input) ;
+ i := 1 ;
+ while not (eoln(input) or eof(input)) do begin
+ #[i] := input^ ;
+ incr(i) ;
+ get(input) ;
+ end ;
+ #[i] := ' '
+
+@ @p procedure dialog ;
+var i : integer ; {index variable}
+buffer : packed array [1..name_length] of char; {input buffer}
+begin
+ for i := 1 to name_length do begin
+ typ_name[i] := ' ' ;
+ pk_name[i] := ' ' ;
+ end;
+ print('Input file name: ') ;
+ flush_buffer ;
+ get_line(pk_name) ;
+ print('Output file name: ') ;
+ flush_buffer ;
+ get_line(typ_name) ;
+end ;
+
+@* The main program.
+Now that we have all the pieces written, let us put them together.
+
+@p begin
+initialize ;
+dialog ;
+@<Open files@> ;
+@<Read preamble@> ;
+skip_specials ;
+while flag_byte <> pk_post do begin
+ @<Unpack and write character@> ;
+ skip_specials ;
+end ;
+j := 0 ;
+while not eof(pk_file) do begin
+ i := pk_byte ;
+ if i <> pk_no_op then abort('Bad byte at end of file: ',i:1) ;
+@.Bad byte at end of file@>
+ t_print_ln((pk_loc-1):1,': No op') ;
+ incr(j) ;
+end ;
+t_print_ln(pk_loc:1,' bytes read from packed file.');
+final_end :
+end .
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by changes to the program
+that are necessary to make \.{PKtype} work at a particular installation.
+Any additional routines should be inserted here.
+@^system dependencies@>
+
+@* Index.
+Pointers to error messages appear here together with the section numbers
+where each ident\-i\-fier is used.
diff --git a/systems/knuth/local/tex-sparc/Makefile b/systems/knuth/local/tex-sparc/Makefile
new file mode 100644
index 0000000000..e7880da23c
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/Makefile
@@ -0,0 +1,74 @@
+# Make executable files for this workstation, using the master TeX sources
+
+#PFLAGS= -I.. -temp=.
+PFLAGS= -I.. -g
+#PFLAGS= -I..
+CFLAGS= -O -I..
+PC= time pc
+
+all: initex virtex
+
+triptest: triptex
+ -triptex < trip1.in
+ mv trip.log tripin.log
+ -triptex < trip2.in | tee trip.fot
+ dvitype trip.dvi < dvitype.in > /dev/null
+ mv dvitype.out trip.typ
+ rm -f *.dif
+ -diff tripin.log ../../dist/tex/tripin.log > tripin.log.dif
+ -diff trip.log ../../dist/tex/trip.log > trip.log.dif
+ -diff trip.fot ../../dist/tex/trip.fot > trip.fot.dif
+ -diff trip.typ ../../dist/tex/trip.typ > trip.typ.dif
+ more tripin.log.dif trip.log.dif trip.typ.dif trip.fot.dif
+
+%.p: %.ch
+ tangle ../../dist/tex/tex.web $*.ch
+ mv ../../dist/tex/tex.pool .
+ mv ../../dist/tex/tex.p $*.p
+
+initex.ch: ../../dist/tex/tex.web
+
+virtex.ch: initex.ch ini_to_vir
+ ini_to_vir initex.ch virtex.ch
+
+triptex.ch: initex.ch ini_to_trip
+ ini_to_trip initex.ch triptex.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+triptex.o: triptex.p
+ ${PC} -g ${PFLAGS} -c triptex.p
+
+initex: initex.o ext.o
+ pc ${PFLAGS} -o initex initex.o ext.o
+
+virtex: virtex.o ext.o
+ pc ${PFLAGS} -o virtex virtex.o ext.o
+
+triptex: triptex.o ext.o
+ pc -g ${PFLAGS} -o triptex triptex.o ext.o
+ cp ../../dist/tex/trip.tex .
+
+ext.o: ext.c ext.h ../h00vars.h ../texpaths.h
+ cc ${CFLAGS} -c ext.c
+
+plain.fmt: ../../dist/lib/plain.tex ../lib/local.tex
+ -initex plain \\input local \\dump
+
+install: # doit make install
+ mv initex virtex /usr/local/bin
+ rm /usr/local/bin/tex
+ ln /usr/local/bin/virtex /usr/local/bin/tex
+ make plain.fmt
+
+installtrip: # when the trip test is satisfactory
+ mv trip.tex tripin.log trip.log trip.fot trip.typ ../../dist/tex
+
+clean:
+ rm -f *.p *.o *~ *.tex triptex* *.dif trip.* tripin.log virtex.ch
+ @echo "Remaining files should be:"
+ @echo "Makefile ext.h initex.ch tex.pool"
+ @echo "dvitype.in ini_to_trip* plain.fmt trip1.in"
+ @echo "ext.c ini_to_vir* plain.log trip2.in"
+
diff --git a/systems/knuth/local/tex-sparc/dvitype.in b/systems/knuth/local/tex-sparc/dvitype.in
new file mode 100644
index 0000000000..9aae7baa4b
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/dvitype.in
@@ -0,0 +1,5 @@
+2
+*.*.*.*.*.*.*.*.*.*
+1000000
+7227/100
+0
diff --git a/systems/knuth/local/tex-sparc/ext.c b/systems/knuth/local/tex-sparc/ext.c
new file mode 100644
index 0000000000..5d86e6799e
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/ext.c
@@ -0,0 +1,468 @@
+/* external procedures called by TeX */
+#include <sys/signal.h>
+#include <sys/time.h>
+#include "texpaths.h" /* defines MAXINPATHCHARS, defaultinputpath, etc. */
+#include "h00vars.h"
+
+/* The following are for closing files */
+
+closea(f)
+ int *f; /* actually, file pointer, but it doesn't matter */
+{
+ close1(f);
+}
+
+closeb(f)
+ int *f; /* actually, file pointer, but it doesn't matter */
+{
+ close1(f);
+}
+
+closew(f)
+ int *f; /* actually, file pointer, but it doesn't matter */
+{
+ close1(f);
+}
+
+extern int interrupt; /* To be made nonzero to tell TeX about interrupts */
+
+/*
+** catchint()
+**
+** Gets called when the user hits his interrupt key. Sets the global
+** "interrupt" nonzero, then sets it up so that the next interrupt will
+** also be caught here.
+**
+*/
+
+catchint()
+{
+ interrupt = 1;
+ signal(SIGINT,catchint);
+}
+
+/*
+** dateandtime(time, day, month, year)
+**
+** Stores minutes since midnight, current day, month and year into
+** *time, *day, *month and *year, respectively.
+**
+** Also, set things up so that catchint() will get control on interrupts.
+**
+*/
+
+dateandtime(minutes, day, month, year)
+int *minutes, *day, *month, *year;
+{
+ long clock;
+ struct tm *tmptr, *localtime();
+
+ clock = time(0);
+ tmptr = localtime(&clock);
+
+ *minutes = tmptr->tm_hour * 60 + tmptr->tm_min;
+ *day = tmptr->tm_mday;
+ *month = tmptr->tm_mon + 1;
+ *year = tmptr->tm_year + 1900;
+ signal(SIGINT,catchint);
+}
+
+
+/* fixed arrays are used to hold the paths, to avoid any possible problems
+ involving interaction of malloc and undump */
+
+char inputpath[MAXINPATHCHARS] = defaultinputpath;
+char fontpath[MAXOTHPATHCHARS] = defaultfontpath;
+char formatpath[MAXOTHPATHCHARS] = defaultformatpath;
+char poolpath[MAXOTHPATHCHARS] = defaultpoolpath;
+
+char *getenv();
+
+/*
+ * setpaths is called to set up the arrays inputpath, fontpath, formatpath
+ * and poolpath as follows: if the user's environment has a value for the
+ * appropriate value, then use it; otherwise, leave the current value of
+ * the array (which may be the default path, or it may be the result of
+ * a call to setpaths on a previous run that was made the subject of
+ * an undump: this will give the maker of a preloaded TeX the option of
+ * providing a new set of "default" paths.
+ *
+ * Note that we have to copy the string from the environment area, since
+ * that will change on the next run (which matters if this is for a
+ * preloaded TeX).
+ */
+setpaths()
+{
+ register char *envpath;
+
+ if ((envpath = getenv("TEXINPUTS")) != NULL)
+ copypath(inputpath,envpath,MAXINPATHCHARS);
+ if ((envpath = getenv("TEXFONTS")) != NULL)
+ copypath(fontpath,envpath,MAXOTHPATHCHARS);
+ if ((envpath = getenv("TEXFORMATS")) != NULL)
+ copypath(formatpath,envpath,MAXOTHPATHCHARS);
+ if ((envpath = getenv("TEXPOOL")) != NULL)
+ copypath(poolpath,envpath,MAXOTHPATHCHARS);
+}
+
+/*
+ * copypath(s1,s2,n) copies at most n characters (including the null)
+ * from string s2 to string s1, giving an error message for paths
+ * that are too long.
+ */
+copypath(s1,s2,n)
+ register char *s1,*s2;
+ register int n;
+{
+ while ((*s1++ = *s2++) != '\0')
+ if (--n == 0) {
+ fprintf(stderr, "! Environment search path is too big\n");
+ *--s1 = '\0';
+ return; /* let user continue with truncated path */
+ }
+}
+
+extern short buffer[],xord[]; /* arrays of 0..255 */
+extern short last,first; /* pointers into buffer */
+
+/*
+ * lineread reads from the Pascal text file with iorec pointer filep
+ * into buffer[first], buffer[first+1],..., buffer[last-1] (and
+ * setting "last").
+ * Since 8-bit code is in the buffer, the elements are "short", not char.
+* Characters are read until a newline is found (which isn't put in the
+ * buffer) or until the next character would go into buffer[lastlim].
+ * The characters need to be translated, so really xord[c] is put into
+ * the buffer when c is read.
+ * If end-of-file is encountered, the funit field of *filep is set
+ * appropriately.
+ */
+lineread(filep, lastlim)
+struct iorec *filep;
+int lastlim;
+{
+ register c;
+ register short *cs; /* pointer into buffer where next char goes */
+ register FILE *iop; /* stdio-style FILE pointer */
+ int l; /* how many more chars are allowed before buffer overflow */
+ short *bf; /* hold address of buffer[first] */
+
+ iop = filep->fbuf;
+ bf = cs = &(buffer[first]);
+ l = lastlim-first;
+ /* overflow when next char would go into buffer[lastlim] */
+ while (--l>=0 && (c = getc(iop)) != EOF && c!='\n')
+ *cs++ = xord[c];
+ if (c == EOF)
+ filep->funit |= EOFF; /* we hit end-of-file */
+ last = first+cs-bf;
+}
+
+#define filenamesize 1024 /* should agree with initex.ch */
+extern char nameoffile[],realnameoffile[]; /* these have size filenamesize */
+
+/*
+ * testaccess(amode,filepath)
+ *
+ * Test whether or not the file whose name is in the global nameoffile
+ * can be opened for reading (if mode=READACCESS)
+ * or writing (if mode=WRITEACCESS).
+ *
+ * The filepath argument is one of the ...FILEPATH constants defined below.
+ * If the filename given in nameoffile does not begin with '/', we try
+ * prepending all the ':'-separated areanames in the appropriate path to the
+ * filename until access can be made, if it ever can.
+ *
+ * The realnameoffile global array will contain the name that yielded an
+ * access success.
+ */
+
+#define READACCESS 4
+#define WRITEACCESS 2
+
+#define NOFILEPATH 0
+#define INPUTFILEPATH 1
+#define READFILEPATH 2
+#define FONTFILEPATH 3
+#define FORMATFILEPATH 4
+#define POOLFILEPATH 5
+
+bool
+testaccess(amode,filepath)
+ int amode,filepath;
+{
+ register bool ok;
+ register char *p;
+ char *curpathplace;
+ int f;
+
+ switch(filepath) {
+ case NOFILEPATH: curpathplace = NULL; break;
+ case INPUTFILEPATH: case READFILEPATH:
+ curpathplace = inputpath; break;
+ case FONTFILEPATH: curpathplace = fontpath; break;
+ case FORMATFILEPATH: curpathplace = formatpath; break;
+ case POOLFILEPATH: curpathplace = poolpath; break;
+ }
+ if (nameoffile[0]=='/') /* file name has absolute path */
+ curpathplace = NULL;
+ do {
+ packrealnameoffile(&curpathplace);
+ if (amode==READACCESS)
+ /* use system call "access" to see if we could read it */
+ if (access(realnameoffile,READACCESS)==0) ok = TRUE;
+ else ok = FALSE;
+ else {
+ /* WRITEACCESS: use creat to see if we could create it, but close
+ the file again if we're OK, to let pc open it for real */
+ f = creat(realnameoffile,0666);
+ if (f>=0) ok = TRUE;
+ else ok = FALSE;
+ if (ok)
+ close(f);
+ }
+ } while (!ok && curpathplace != NULL);
+ if (ok) { /* pad realnameoffile with blanks, as Pascal wants */
+ for (p = realnameoffile; *p != '\0'; p++)
+ /* nothing: find end of string */ ;
+ while (p < &(realnameoffile[filenamesize]))
+ *p++ = ' ';
+ }
+ return (ok);
+}
+
+/*
+ * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp,
+ * followed by '/', followed by the characters in nameoffile up until the
+ * first blank there, and finally a '\0'. The cpp pointer is left pointing
+ * at the next directory in the path.
+ * But: if *cpp == NULL, then we are supposed to use nameoffile as is.
+ */
+packrealnameoffile(cpp)
+ char **cpp;
+{
+ register char *p,*realname;
+
+ realname = realnameoffile;
+ if ((p = *cpp)!=NULL) {
+ while ((*p != ':') && (*p != '\0')) {
+ *realname++ = *p++;
+ if (realname == &(realnameoffile[filenamesize-1]))
+ break;
+ }
+ if (*p == '\0') *cpp = NULL; /* at end of path now */
+ else *cpp = p+1; /* else get past ':' */
+ *realname++ = '/'; /* separate the area from the name to follow */
+ }
+ /* now append nameoffile to realname... */
+ p = nameoffile;
+ while (*p != ' ') {
+ if (realname >= &(realnameoffile[filenamesize-1])) {
+ fprintf(stderr,"! Full file name is too long\n");
+ break;
+ }
+ *realname++ = *p++;
+ }
+ *realname = '\0';
+}
+
+/*
+** testeof(filep)
+**
+** Test whether or not the Pascal text file with iorec pointer filep
+** has reached end-of-file (when the only I/O on it is done with
+** lineread, above).
+** We may have to read the next character and unget it to see if perhaps
+** the end-of-file is next.
+*/
+
+bool
+testeof(filep)
+register struct iorec *filep;
+{
+ register char c;
+ register FILE *iop; /* stdio-style FILE pointer */
+ if (filep->funit & EOFF)
+ return(TRUE);
+ else { /* check to see if next is EOF */
+ iop = filep->fbuf;
+ c = getc(iop);
+ if (c == EOF)
+ return(TRUE);
+ else {
+ ungetc(c,iop);
+ return(FALSE);
+ }
+ }
+}
+
+
+/*
+** writedvi(a,b)
+**
+** writedvi is called to write dvibuf[a..b] to dvifile
+** Unfortunately, dvibuf is declared as eight_bits, which in our
+** implementation means that two bytes are taken by each entry.
+** We only want to output the low order (first) byte of each pair.
+*/
+extern struct iorec dvifile;
+extern short dvibuf[];
+writedvi (a,b)
+ int a,b;
+{
+ register short *ptr,*final;
+ register FILE *iop;
+
+ iop=dvifile.fbuf;
+ ptr= &(dvibuf[a]);
+ final= &(dvibuf[b]);
+ while (ptr<=final) {
+ putc((char) (*ptr & 0377), iop);
+ ptr += 1;
+ }
+ /* Note: The above code used to be machine dependent. By changing
+ * the declarations from "char *" to "short *" we get the machine
+ * to do its own byte ordering of the de-reference of "ptr" above.
+ * Then we AND off the low bits (now that it's been pulled from
+ * memory correctly) and cast it into a "char" for putc().
+ * --Peter Kessler's idea; explanation, AND, cast a la clp.
+ */
+}
+
+
+/*
+** close1(filep)
+** close1 does the proper things to pc's runtime system file data structure
+** to close a file
+*/
+close1(filep)
+
+ register struct iorec *filep;
+{
+ register struct iorec *next;
+
+ if (filep->fbuf != 0) {
+ if ((filep->funit & FDEF) == 0) {
+ next = (struct iorec *) &_fchain;
+ while (next->fchain != FILNIL
+ && next->fchain != filep)
+ next = next->fchain;
+ if (next->fchain != FILNIL)
+ next->fchain = next->fchain->fchain;
+
+ if (filep->fblk > PREDEF) {
+ fflush(filep->fbuf);
+ setbuf(filep->fbuf, NULL);
+ }
+ fclose(filep->fbuf);
+ if (ferror(filep->fbuf)) {
+/* ERROR and PERROR no longer exist in Pascal library; TeX doesn't know
+ what to do with them anyway! So I'm commenting this out... don 7/18/92 */
+/* ERROR("%s: Close failed\n",
+ filep->pfname); */
+ return;
+ }
+ filep->fbuf=0;
+ }
+ if ((filep->funit & TEMP) != 0 &&
+ unlink(filep->pfname)) {
+/* PERROR("Could not remove ", filep->pfname); */
+ return;
+ }
+ }
+}
+
+/*
+** The following procedure is due to sjc@s1-c.
+** (with mods for 8-bit changes --- don, Sep 89)
+**
+** calledit(filename, fnlength, linenumber)
+**
+** TeX82 can call this to implement the 'e' feature in error-recovery
+** mode, invoking a text editor on the erroneous source file.
+**
+** You should pass to "filename" the first character of the packed array
+** containing the filename, and to "fnlength" the size of the filename.
+**
+** New problem (8-bit mod): need to add 128 to all characters of filename!
+**
+** Ordinarily, this invokes emacs. If you want a different
+** editor, create a shell environment variable TEXEDIT containing
+** the string that invokes that editor, with "%s" indicating where
+** the filename goes and "%d" indicating where the decimal
+** linenumber (if any) goes. For example, a TEXEDIT string for "vi" might be:
+**
+** setenv TEXEDIT "/usr/bin/vi +%d %s"
+**
+*/
+
+ char dvalue[] = "emacs +%d %s";
+ char *texeditvalue = &dvalue[0];
+
+ calledit(filename, fnlength, linenumber)
+ char *filename;
+ int fnlength, linenumber;
+ {
+ char *temp, *command, c;
+ int sdone, ddone, i;
+
+ sdone = ddone = 0;
+
+ /* Replace default with environment variable if possible. */
+ if (NULL != (temp = (char *) getenv("TEXEDIT")))
+ texeditvalue = temp;
+
+ /* Make command string containing envvalue, filename, and linenumber */
+ if (NULL ==
+ (command = (char *) malloc(strlen(texeditvalue) + fnlength + 25))) {
+ fprintf(stderr, "! Not enough memory to issue editor command\n");
+ exit(1);
+ }
+ temp = command;
+ while ((c = *texeditvalue++) != '\0') {
+ if (c == '%') {
+ switch (c = *texeditvalue++) {
+ case 'd':
+ if (ddone) {
+ fprintf(stderr,
+ "! Line number cannot appear twice in editor command\n");
+ exit(1);
+ }
+ sprintf(temp, "%d", linenumber);
+ while (*temp != '\0')
+ temp++;
+ ddone = 1;
+ break;
+ case 's':
+ if (sdone) {
+ fprintf(stderr,
+ "! Filename cannot appear twice in editor command\n");
+ exit(1);
+ }
+ i = 0;
+ while (i < fnlength)
+ *temp++ = filename[i++] ^ 0x80; /* don's change */
+ sdone = 1;
+ break;
+ case '\0':
+ *temp++ = '%';
+ texeditvalue--; /* Back up to \0 to force termination. */
+ break;
+ default:
+ *temp++ = '%';
+ *temp++ = c;
+ break;
+ }
+ }
+ else
+ *temp++ = c;
+ }
+ *temp = '\0';
+
+ /* Execute command. */
+ if (0 != system(command))
+ fprintf(stderr, "! Trouble executing command %s\n", command);
+
+ /* Quit, indicating TeX had found an error before you typed "e". */
+ exit(1);
+ }
diff --git a/systems/knuth/local/tex-sparc/ext.h b/systems/knuth/local/tex-sparc/ext.h
new file mode 100644
index 0000000000..19dc5595f4
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/ext.h
@@ -0,0 +1,32 @@
+procedure exit(x : integer);
+ external;
+
+procedure closea(var f:text);
+ external;
+
+procedure closeb(var f:bytefile);
+ external;
+
+procedure closew(var f:wordfile);
+ external;
+
+procedure dateandtime(var minutes, day, month, year : integer);
+ external;
+
+procedure setpaths;
+ external;
+
+function testaccess(accessmode:integer; filepath:integer): boolean;
+ external;
+
+function testeof(var f: text): boolean;
+ external;
+
+procedure lineread(var f: text; lastlim: integer);
+ external;
+
+procedure writedvi(a,b:integer);
+ external;
+
+procedure calledit(var filename: packedASCIIcode; fnlength, linenumber: integer);
+ external;
diff --git a/systems/knuth/local/tex-sparc/ini_to_trip b/systems/knuth/local/tex-sparc/ini_to_trip
new file mode 100644
index 0000000000..89ccab0759
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/ini_to_trip
@@ -0,0 +1,41 @@
+#! /bin/csh
+# A shell script to perform modifications on the changefile for initex
+# to make a changefile for triptex (used to test tex).
+#
+# To use it, say
+# ini_to_trip initex.ch triptex.ch
+#
+ed - $1 <<edscriptend
+/debug=/
+//
+s/@{//p
+/gubed=/
+s/@t@>@}//p
+/@!mem_max=/
+//
+s/[0-9][0-9]*/3000/p
+/@!mem_min=/
+s/-[0-9]*/1/p
+/@!error_line=/
+s/[0-9][0-9]*/64/p
+/@!half_error_line=/
+s/[0-9][0-9]*/32/p
+/@!max_print_line=/
+s/[0-9][0-9]*/72/p
+/@!font_max=/
+s/[0-9][0-9]*/75/p
+/@!font_mem_size=/
+s/[0-9][0-9]*/20000/p
+/@!pool_size=/
+s/[0-9][0-9]*/32000/p
+/@d mem_bot=/
+//
+s/-[0-9]*/1/p
+/@d mem_top==/
+s/[0-9][0-9]*/3000/p
+/@d hash_size=/
+s/[0-9][0-9]*/2100/p
+/@d hash_prime=/
+s/[0-9][0-9]*/1777/p
+w $2
+edscriptend
diff --git a/systems/knuth/local/tex-sparc/ini_to_vir b/systems/knuth/local/tex-sparc/ini_to_vir
new file mode 100644
index 0000000000..2120a4beea
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/ini_to_vir
@@ -0,0 +1,23 @@
+#! /bin/csh
+# A shell script to perform modifications on the changefile for initex
+# to make a changefile for virtex. This means changing constants and
+# debug..gubed, init..tini, stat..tats.
+#
+# To use it, say
+# ini_to_vir initex.ch virtex.ch
+#
+ed - $1 <<edscriptend
+/@d init==/
+//
+s//&@{/p
+/@d tini==/
+s//&@t@>@}/p
+/@!mem_max=/
+//
+s/[0-9][0-9]*/32766/p
+/@!max_strings=/
+s/[0-9][0-9]*/6000/p
+/@!trie_size=/
+s/[0-9][0-9]*/6500/p
+w $2
+edscriptend
diff --git a/systems/knuth/local/tex-sparc/initex.ch b/systems/knuth/local/tex-sparc/initex.ch
new file mode 100644
index 0000000000..11b4a9ab38
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/initex.ch
@@ -0,0 +1,1419 @@
+% Change file for Berkeley UNIX, for a hacked version of the pc pascal
+% compiler (with the others: clause in case statements).
+%
+% Distant relation of David Fuchs' Sep 13, 1982 TOPS20 version, greatly
+% modified by Howard Trickey and Pavel Curtis
+
+% Modification History:
+%
+% (10/9) HWT Brought up to version 0.1
+% (10/13) HWT Fixed bug in hpack
+% (10/17) HWT Changed to version 0.3
+% (10/22) HWT Brought up to version 0.4
+% (10/26) HWT Changed to use version 0.5 source
+% (11/19) HWT Changed to use version 0.8 source; fixed glue_ratios to use
+% external C procedures
+% (11/30) HWT Changed to use new tangle, with verbatim Pascal mode, to avoid
+% need for editing in the #include statement
+% (changed modules 4,9)
+% (1/15/83) HWT Changed to use version 0.94 source; added Pavel's changes to
+% allow undumping; added Grim's fix for grtofl, etc.;
+% modified for Knuth's new tangle.
+% (2/1/83) PC Consolidated with Pavel's version, changed the fl<->gr
+% routines to avoid slow and messy union type.
+% (3/18/83) HWT Changed to use version 0.96 source (means changing fltogr and
+% grtofl to unfloat and float, then getting rid of a lot of
+% changes); made TeX print a newline
+% at the end of the run.
+% (4/2/83) PC Added call to exit() at end of run, with exit-value dependent
+% upon the value of `history'.
+% (4/16/83) PC Brought up to version 0.97 of TeX.
+% (5/23/83) HWT Changed mem_max to 30000 for initex (because of latex)
+% (6/2/83) HWT Brought up to version 0.98 of TeX. Changed some of the
+% string-related constants to match the latest tops20
+% change file. Modified input_ln and write_dvi to use external
+% C procedures, for speed increase. Changed file opening
+% procedures to use external C procedure to test for
+% accessibility, so that now none of the pc runtime library
+% programs need to be modified (though close1 is still needed)
+% (6/28/83) HWT Brought up to 0.99, with new change file format.
+% (7/17/83) HWT Brought up to 0.999. Added switch-to-editor stuff.
+% (7/29/83) HWT Brought up to 0.9999.
+% (11/4/83) HWT Brought up to 0.999999.
+% (11/16/83) HWT Brought up to 1.0. Upped max_strings, string_vacancies,
+% pool_size, hash_size, and hash_prime, to agree with
+% TOPS20-changes constants.
+% (1/4/84) HWT Again, upped pool_size to 40000 for LaTeX.
+% (2/18/84) HWT Made changes to use search path stuff in new ext.c. Now all
+% the path specific stuff is localized to texpaths.h
+% (6/6/84) HWT Brought up to 1.1. Upped pool_size and hash_size for LaTeX.
+% (31/7/84) HWT Removed the forgetting of the full file name, so that
+% switch-to-editor works for non-primary file.
+% (11/16/84) RKF Brought up to 1.2
+% (12/26/84) RKF Brought up to 1.3, consulting changes already done by
+% SVB@Purdue, rusty@sdcarl, and a related file from HWT
+% (3/11/85) RKF Brought up to 1.3b
+% (9/2/85) RKF Brought up to 1.5 (yep, missed 1.4)
+% (1/30/86) PAM brought up to 2.0
+% (12/11/86) PAM commented out the patch which turns on debug. This is
+% partly since debug is no longer necessary at this point of development
+% but also because debug turns on range checking in the pc, and in
+% recent months the pc range check has been giving bogus error
+% messages.
+% (2/27/87) PAM No change in working code, but noted places where SUNMakefile
+% makes alterations for more efficient SUN code.
+% (3/7/87) PAM Changed version number for TeX 2.1
+% (9/23/87) PAM Changed version number for TeX 2.3
+% (10/25/87) PAM Changed version number for TeX 2.5
+% (12/5/87) PAM Changed version number for TeX 2.7
+% (1/15/88) PAM Changed version number for TeX 2.9
+% (8/9/89) don SunOS changes for TeX 2.991; decreased some constants
+% (9/10/89) don Major changes for 8-bit TeX beta test (version 2.992-eps)
+% (10/6/89) don Now uptodate with TeX version 2.992 (\approx 3.0)
+% (10/29/89) don Upped fontmax to 127 and fontmemsize to 40000
+% (1/23/90) don Fixed max_buf_stack logic (it was one too high)
+% (9/6/90) don to Version 3.1
+% (1/30/91) don to Version 3.14
+% (9/19/91) don to Version 3.14a
+% (1/25/92) don to Version 3.14b
+% (3/15/92) don to Version 3.141
+% (6/15/92) don reversed the UNIXexit values (0 is success)(cf make)
+% (2/26/93) don to Version 3.1415
+% (3/8/95) don to Version 3.14159
+% (4/2/97) don increased pool_size from 50000 to 60000
+% (and max_strings from 4000 to 6000 in ini_to_vir)
+% (9/16/97) don increased buf_size from 500 to 5000 (without bothering
+% to see why the implementation doesn't overflow buf_size gracefully)
+
+% Use this file as is to make an INITEX. To get production TeX, use the
+% shell script ``ini_to_vir'' and re-TANGLE.
+
+% NOTE: the module numbers in this change file refer to Volume B.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: only print changes
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+@y
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+\let\maybe=\iffalse
+\def\title{\TeX82 changes for SunOS}
+\def\glob{13}\def\gglob{20, 26} % these are defined in module 1
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.2] banner line
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is TeX, Version 3.14159' {printed when \TeX\ starts}
+@y
+@d banner=='This is TeX, Version 3.14159 for SunOS' {printed when \TeX\ starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.4] program header
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Actually the heading shown here is not quite normal: The |program| line
+does not mention any |output| file, because \ph\ would ask the \TeX\ user
+to specify a file name if |output| were specified here.
+@^system dependencies@>
+
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@y
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@d standard_input==i@&n@&p@&u@&t {and another}
+@z
+@x
+program TEX; {all file names are defined dynamically}
+@y
+program TEX(standard_input,output); {other file names are defined dynamically}
+@z
+@x
+var @<Global variables@>@/
+@y
+var @<Global variables@>@/
+@#
+@\@=#include "ext.h"@>@\ {declarations for external C procedures}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.7] debug..gubed, stat..tats
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@y
+@d debug==@{ {the trip test will use debugging}
+@d gubed==@t@>@}
+@z
+@x
+@d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering
+ usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering
+ usage statistics}
+@y
+@d stat==
+@d tats==
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.8] init..tini
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
+@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
+@y
+@d init==
+@d tini==
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.9] compiler directives
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+@y
+@=(*$C-*)@> {no range check}
+@!debug @=(*$C+*)@> @+ gubed {debug with range check (may cause bogus errors)}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.10] othercases, feature of Sun Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.11] compile-time constants
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!mem_max=30000; {greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|}
+@!mem_min=0; {smallest index in \TeX's internal |mem| array;
+ must be |min_halfword| or more;
+ must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|}
+@!buf_size=500; {maximum number of characters simultaneously present in
+ current lines of open files and in control sequences between
+ \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
+@!error_line=72; {width of context lines on terminal error messages}
+@!half_error_line=42; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!stack_size=200; {maximum number of simultaneous input sources}
+@!max_in_open=6; {maximum number of input files and error insertions that
+ can be going on simultaneously}
+@!font_max=75; {maximum internal font number; must not exceed |max_quarterword|
+ and must be at most |font_base+256|}
+@!font_mem_size=20000; {number of words of |font_info| for all fonts}
+@!param_size=60; {maximum number of simultaneous macro parameters}
+@!nest_size=40; {maximum number of semantic levels simultaneously active}
+@!max_strings=3000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=8000; {the minimum number of characters that should be
+ available for the user's control sequences and font names,
+ after \TeX's own error messages are stored}
+@!pool_size=32000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all fonts and
+ control sequences; must exceed |string_vacancies| by the total
+ length of \TeX's own strings, which is currently about 23000}
+@!save_size=600; {space for saving values outside of current group; must be
+ at most |max_halfword|}
+@!trie_size=8000; {space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX}
+@!trie_op_size=500; {space for ``opcodes'' in the hyphenation patterns}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!file_name_size=40; {file names shouldn't be longer than this}
+@!pool_name='TeXformats:TEX.POOL ';
+ {string of length |file_name_size|; tells where the string pool appears}
+@y
+@!mem_max=30000; {greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|}
+@!mem_min=-32767; {smallest index in \TeX's internal |mem| array;
+ must be |min_halfword| or more;
+ must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|}
+@!buf_size=5000; {maximum number of characters simultaneously present in
+ current lines of open files and in control sequences between
+ \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
+@!error_line=79; {width of context lines on terminal error messages}
+@!half_error_line=50; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!stack_size=200; {maximum number of simultaneous input sources}
+@!max_in_open=15; {maximum number of input files and error insertions that
+ can be going on simultaneously}
+@!font_max=127; {maximum internal font number; must not exceed |max_quarterword|
+ and must be at most |font_base+256|}
+@!font_mem_size=40000; {number of words of |font_info| for all fonts}
+@!param_size=60; {maximum number of simultaneous macro parameters}
+@!nest_size=40; {maximum number of semantic levels simultaneously active}
+@!max_strings=3000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=8000; {the minimum number of characters that should be
+ available for the user's control sequences and font names,
+ after \TeX's own error messages are stored}
+@!pool_size=60000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all fonts and
+ control sequences; must exceed |string_vacancies| by the total
+ length of \TeX's own strings, which is currently about 23000}
+@!save_size=600; {space for saving values outside of current group; must be
+ at most |max_halfword|}
+@!trie_size=8000; {space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX}
+@!trie_op_size=500; {space for ``opcodes'' in the hyphenation patterns}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!file_name_size=1024; {file names shouldn't be longer than this}
+@!pool_name='tex.pool';
+ {string of length |file_name_size|; the string pool name}
+@.TeXformats@>
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.12] sensitive compile-time constants
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d mem_bot=0 {smallest index in the |mem| array dumped by \.{INITEX};
+ must not be less than |mem_min|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INITEX};
+ must be substantially larger than |mem_bot|
+ and not greater than |mem_max|}
+@d font_base=0 {smallest internal font number; must not be less
+ than |min_quarterword|}
+@d hash_size=2100 {maximum number of control sequences; it should be at most
+ about |(mem_max-mem_min)/10|}
+@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
+@d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions}
+@y
+@d mem_bot=-32767 {smallest index in the |mem| array dumped by \.{INITEX};
+ must not be less than |mem_min|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INITEX};
+ must be substantially larger than |mem_bot|
+ and not greater than |mem_max|}
+@d font_base=0 {smallest internal font number; must not be less
+ than |min_quarterword|}
+@d hash_size=4000 {maximum number of control sequences; it should be at most
+ about |(mem_max-mem_min)/10|}
+@d hash_prime=3443 {a prime number equal to about 85\pct! of |hash_size|}
+@d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2.23] allow any character that we can input to get in
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+@y
+for i:=0 to @'37 do xchr[i]:=chr(i);
+for i:=@'177 to @'377 do xchr[i]:=chr(i);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.25] file types
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+The program actually makes use also of a third kind of file, called a
+@y
+The pc compiler on Berkeley {\mc UNIX} won't pack a range into one byte unless
+the lower bound is |-128| or more, and the upper bound is |127| or less.
+This applies to files, too. To make the program work with compiler range
+checking turned on, we'll have to adjust |eight_bits| codes by sometimes
+adding or subtracting |256|.
+Note that |eight_bits| really takes 16 bits here.
+
+The program actually makes use also of a third kind of file, called a
+@z
+@x
+@!alpha_file=packed file of text_char; {files that contain textual data}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+@!alpha_file=t@&e@&x@&t; {files that contain textual data}
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.26] add real_name_of_file array
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \TeX\ can open a file whose external name is specified by
+|name_of_file|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file:packed array[1..file_name_size] of char;@;@/
+ {on some systems this may be a \&{record} variable}
+@!name_length:0..file_name_size;@/{this many characters are actually
+ relevant in |name_of_file| (the rest are blank)}
+@y
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \TeX\ can open a file whose external name is specified by
+|name_of_file|.
+The Berkeley {\mc UNIX} version uses search paths to look for files to open.
+We use |real_name_of_file| to hold the |name_of_file| with a directory
+name from the path in front of it.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file,@!real_name_of_file:packed array[1..file_name_size] of char;
+@;@/
+@!name_length:0..file_name_size;@/{this many characters are actually
+ relevant in |name_of_file| (the rest are blank)}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.27] file opening
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The \ph\ compiler with which the present version of \TeX\ was prepared has
+extended the rules of \PASCAL\ in a very convenient way. To open file~|f|,
+we can write
+$$\vbox{\halign{#\hfil\qquad&#\hfil\cr
+|reset(f,@t\\{name}@>,'/O')|&for input;\cr
+|rewrite(f,@t\\{name}@>,'/O')|&for output.\cr}}$$
+The `\\{name}' parameter, which is of type `{\bf packed array
+$[\langle\\{any}\rangle]$ of \\{char}}', stands for the name of
+the external file that is being opened for input or output.
+Blank spaces that might appear in \\{name} are ignored.
+
+The `\.{/O}' parameter tells the operating system not to issue its own
+error messages if something goes wrong. If a file of the specified name
+cannot be found, or if such a file cannot be opened for some other reason
+(e.g., someone may already be trying to write the same file), we will have
+|@!erstat(f)<>0| after an unsuccessful |reset| or |rewrite|. This allows
+\TeX\ to undertake appropriate corrective action.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+\TeX's file-opening procedures return |false| if no file identified by
+|name_of_file| could be opened.
+
+@d reset_OK(#)==erstat(#)=0
+@d rewrite_OK(#)==erstat(#)=0
+
+@p function a_open_in(var f:alpha_file):boolean;
+ {open a text file for input}
+begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f);
+end;
+@#
+function a_open_out(var f:alpha_file):boolean;
+ {open a text file for output}
+begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f);
+end;
+@#
+function b_open_in(var f:byte_file):boolean;
+ {open a binary file for input}
+begin reset(f,name_of_file,'/O'); b_open_in:=reset_OK(f);
+end;
+@#
+function b_open_out(var f:byte_file):boolean;
+ {open a binary file for output}
+begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f);
+end;
+@#
+function w_open_in(var f:word_file):boolean;
+ {open a word file for input}
+begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f);
+end;
+@#
+function w_open_out(var f:word_file):boolean;
+ {open a word file for output}
+begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f);
+end;
+@y
+@ The \ph\ compiler with which the present version of \TeX\ was prepared has
+extended the rules of \PASCAL\ in a very convenient way for file opening.
+Berkeley {\mc UNIX} \PASCAL\ isn't nearly as nice as \ph.
+Normally, it bombs out if a file open fails.
+An external C procedure, |test_access| is used to check whether or not the
+open will work. It is declared in the ``ext.h'' include file, and it returns
+|true| or |false|. The |name_of_file| global holds the file name whose access
+is to be tested.
+The first parameter for |test_access| is the access mode,
+one of |read_access_mode| or |write_access_mode|.
+
+We also implement path searching in |test_access|: its second parameter is
+one of the ``file path'' constants defined below. If |name_of_file|
+doesn't start with |'/'| then |test_access| tries prepending pathnames
+from the appropriate path list until success or the end of path list
+is reached.
+On return, |real_name_of_file| contains the original name with the path
+that succeeded (if any) prepended. It is the name used in the various
+open procedures.
+
+Note that |a_open_in| has been redefined to take an additional argument,
+which should be one of the ``file path'' specifiers.
+Since |b_open_in| is only used for \.{TFM} files, and
+|w_open_in| is only used for format files, we don't need a path specifying
+argument for them.
+Path searching is not done for output files.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {no path searching should be done}
+@d input_file_path=1 {path specifier for \.{\\input} files}
+@d read_file_path=2 {path specifier for \.{\\read} files}
+@d font_file_path=3 {path specifier for \.{TFM} files}
+@d format_file_path=4 {path specifier for format files}
+@d pool_file_path=5 {path specifier for the pool file}
+
+@p function a_open_in(var f:alpha_file;@!path_specifier:integer):boolean;
+ {open a text file for input}
+var @!ok:boolean;
+begin
+if test_access(read_access_mode,path_specifier) then
+ begin reset(f,real_name_of_file); ok:=true@+end
+else
+ ok:=false;
+a_open_in:=ok;
+end;
+@#
+function a_open_out(var f:alpha_file):boolean;
+ {open a text file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+a_open_out:=ok;
+end;
+@#
+function b_open_in(var f:byte_file):boolean;
+ {open a binary file for input}
+var @!ok:boolean;
+begin
+if test_access(read_access_mode,font_file_path) then
+ begin reset(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+b_open_in:=ok;
+end;
+@#
+function b_open_out(var f:byte_file):boolean;
+ {open a binary file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+b_open_out:=ok;
+end;
+@#
+function w_open_in(var f:word_file):boolean;
+ {open a word file for input}
+var @!ok:boolean;
+begin
+if test_access(read_access_mode,format_file_path) then
+ begin reset(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+w_open_in:=ok;
+end;
+@#
+function w_open_out(var f:word_file):boolean;
+ {open a word file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,name_of_file); ok:=true @+end
+else ok:=false;
+w_open_out:=ok;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.28] file closing
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+These procedures should not generate error messages if a file is
+being closed before it has been successfully opened.
+
+@p procedure a_close(var f:alpha_file); {close a text file}
+begin close(f);
+end;
+@#
+procedure b_close(var f:byte_file); {close a binary file}
+begin close(f);
+end;
+@#
+procedure w_close(var f:word_file); {close a word file}
+begin close(f);
+end;
+@y
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+With the pc library, files will be automatically closed when the program stops
+and when one reopens them.
+There is, however, the problem of opening a file and writing it, and then
+wanting to read it again, but from a different file variable.
+For this purpose we use external procedures |close_a|, |close_b|, and
+|close_w|.
+
+@p procedure a_close(var f:alpha_file); {close a text file}
+begin close_a(f);
+end;
+@#
+procedure b_close(var f:byte_file); {close a binary file}
+begin close_b(f);
+end;
+@#
+procedure w_close(var f:word_file); {close a word file}
+begin close_w(f);
+end;
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.31] faster version of input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Since the inner loop of |input_ln| is part of \TeX'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, if such routines are available. The following
+code uses standard \PASCAL\ to illustrate what needs to be done, but
+finer tuning is often possible at well-developed \PASCAL\ sites.
+@^inner loop@>
+
+@p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean;
+ {inputs the next line or returns |false|}
+var last_nonblank:0..buf_size; {|last| with trailing blanks removed}
+begin if bypass_eoln then if not eof(f) then get(f);
+ {input the first character of the line into |f^|}
+last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
+if eof(f) then input_ln:=false
+else begin last_nonblank:=first;
+ while not eoln(f) do
+ begin if last>=max_buf_stack then
+ begin max_buf_stack:=last+1;
+ if max_buf_stack=buf_size then
+ @<Report overflow of the input buffer, and abort@>;
+ end;
+ buffer[last]:=xord[f^]; get(f); incr(last);
+ if buffer[last-1]<>" " then last_nonblank:=last;
+ end;
+ last:=last_nonblank; input_ln:=true;
+ end;
+end;
+@y
+Since the inner loop of |input_ln| is part of \TeX'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.
+
+With Berkeley {\mc UNIX} we call an external C procedure, |line_read|.
+That routine fills |buffer| from |first| onwards with the |xord|'ed values
+of the next line, setting |last| appropriately. It will stop if
+|last=buf_size|, and the following will cause an ``overflow'' abort.
+We ignore the |bypass_eoln| argument, since the |line_read| procedure
+will have handled bypassing the end-of-line in its previous call, if
+there was one.
+
+If one uses |input_ln| on a file, the normal |read|
+and |get| routines shouldn't be used, nor the |eof| and |eoln| tests.
+End of file can be tested with the external function |test_eof|.
+@^inner loop@>
+
+
+@p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean;
+ {inputs the next line or returns |false|}
+label done;
+begin
+last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
+if test_eof(f) then input_ln:=false
+else begin last:=first;
+ line_read(f,buf_size);
+ if last>max_buf_stack then begin
+ max_buf_stack:=last;
+ if max_buf_stack>=buf_size then
+ @<Report overflow of the input buffer, and abort@>;
+ end;
+ loop@+ begin if last=first then goto done;
+ if buffer[last-1]<>" " then goto done;
+ decr(last);
+ end;
+done: input_ln:=true;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.32] term_in/out are input,output
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file variable is called |term_in|, and when it
+is considered an output file the file variable is |term_out|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!term_in:alpha_file; {the terminal as an input file}
+@!term_out:alpha_file; {the terminal as an output file}
+@y
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file is called |term_in|, and when it
+is considered an output file the file is |term_out|.
+For Berkeley {\mc UNIX} we use |standard_input| and |output| for these
+files, as were declared in the program header. The former name
+|standard_input| was defined using a .{WEB} trick so that `input' is
+produced in the Pascal file.
+
+@d term_in==standard_input {the terminal as an input file}
+@d term_out==output {the terminal as an output file}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.33] don't need to open terminal files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Here is how to open the terminal files
+in \ph. The `\.{/I}' switch suppresses the first |get|.
+@^system dependencies@>
+
+@d t_open_in==reset(term_in,'TTY:','/O/I') {open the terminal for text input}
+@d t_open_out==rewrite(term_out,'TTY:','/O') {open the terminal for text output}
+@y
+@ Here is how to open the terminal files---do nothing.
+
+@d t_open_in == {input already open for text input}
+@d t_open_out == {output already open for text output}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.34] flushing output
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+these operations can be specified in \ph:
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@d clear_terminal == break_in(term_in,true) {clear the terminal input buffer}
+@d wake_up_terminal == do_nothing {cancel the user's cancellation of output}
+@y
+these operations can be specified with Berkeley {\mc UNIX}:
+@^system dependencies@>
+
+@d update_terminal == flush(output)
+@d clear_terminal == {nothing necessary on UNIX}
+@d wake_up_terminal == {nothing necessary on UNIX}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.37] rescanning the command line
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following program does the required initialization
+without retrieving a possible command line.
+It should be clear how to modify this routine to deal with command lines,
+if the system permits them.
+@^system dependencies@>
+
+@p function init_terminal:boolean; {gets the terminal input started}
+label exit;
+begin t_open_in;
+loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal;
+@.**@>
+ if not input_ln(term_in,true) then {this shouldn't happen}
+ begin write_ln(term_out);
+ write(term_out,'! End of file on the terminal... why?');
+@.End of file on the terminal@>
+ init_terminal:=false; return;
+ end;
+ loc:=first;
+ while (loc<last)and(buffer[loc]=" ") do incr(loc);
+ if loc<last then
+ begin init_terminal:=true;
+ return; {return unless the line was all blank}
+ end;
+ write_ln(term_out,'Please type the name of your input file.');
+ end;
+exit:end;
+@y
+@ The following program does the required initialization
+and also retrieves a possible command line.
+@^system dependencies@>
+
+@p
+function init_terminal:boolean; {gets the terminal input started}
+label exit;
+
+var
+ dummy, i, j, k: integer;
+ arg: packed array[1..100] of char;
+
+begin
+ t_open_in;
+ if argc > 1 then begin
+ last := first;
+ for i := 1 to argc - 1 do begin
+ argv(i, arg);
+ j := 1;
+ k := 100; {find last non-blank char in arg}
+ while (k > 1) and (arg[k] = ' ') do@/
+ decr(k);
+ while (j <= k) do begin
+ buffer[last] := xord[arg[j]];
+ incr(j); incr(last);
+ end;
+ if k > 1 then begin
+ buffer[last] := xord[' '];
+ incr(last);
+ end;
+ end;
+ if last > first then begin
+ loc := first;
+ init_terminal := true;
+ return;
+ end;
+ end;
+ loop@+begin
+ wake_up_terminal; write(term_out, '**'); update_terminal;
+@.**@>
+ if not input_ln(term_in,true) then begin {this shouldn't happen}
+ write_ln(term_out);
+ write_ln(term_out, '! End of file on the terminal... why?');
+@.End of file on the terminal@>
+ init_terminal:=false;
+ return;
+ end;
+
+ loc:=first;
+ while (loc<last)and(buffer[loc]=" ") do
+ incr(loc);
+
+ if loc<last then begin
+ init_terminal:=true;
+ return; {return unless the line was all blank}
+ end;
+ write_ln(term_out, 'Please type the name of your input file.');
+@.Please type the name...@>
+ end;
+exit:
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4.38] string data to be offset -128 for packing
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d si(#) == # {convert from |ASCII_code| to |packed_ASCII_code|}
+@d so(#) == # {convert from |packed_ASCII_code| to |ASCII_code|}
+
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+@!packed_ASCII_code = 0..255; {elements of |str_pool| array}
+@y
+@d si(#) == #-128 {convert from |ASCII_code| to |packed_ASCII_code|}
+@d so(#) == #+128 {convert from |packed_ASCII_code| to |ASCII_code|}
+
+@<Types...@>=
+@!pool_pointer = 0..pool_size; {for variables that point into |str_pool|}
+@!str_number = 0..max_strings; {for variables that point into |str_start|}
+@!packed_ASCII_code = -128..127; {elements of |str_pool| array}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4.51] a_open_in of pool file needs path specifier
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if a_open_in(pool_file) then
+@y
+if a_open_in(pool_file,pool_file_path) then
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4.51,52,53] make TEX.POOL lowercase in messages
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+else bad_pool('! I can''t read TEX.POOL.')
+@y
+else bad_pool('! I can''t read tex.pool.')
+@z
+@x
+begin if eof(pool_file) then bad_pool('! TEX.POOL has no check sum.');
+@y
+begin if eof(pool_file) then bad_pool('! tex.pool has no check sum.');
+@z
+@x
+ bad_pool('! TEX.POOL line doesn''t begin with two digits.');
+@y
+ bad_pool('! tex.pool line doesn''t begin with two digits.');
+@z
+@x
+ bad_pool('! TEX.POOL check sum doesn''t have nine digits.');
+@y
+ bad_pool('! tex.pool check sum doesn''t have nine digits.');
+@z
+@x
+done: if a<>@$ then bad_pool('! TEX.POOL doesn''t match; TANGLE me again.');
+@y
+done: if a<>@$ then bad_pool('! tex.pool doesn''t match; tangle me again.');
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6.84] switch-to-editor option
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+line ready to be edited. But such an extension requires some system
+wizardry, so the present implementation simply types out the name of the
+file that should be
+edited and the relevant line number.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines haven't
+been commented~out.
+@^debugging@>
+@y
+line ready to be edited.
+We do this by calling the external procedure |calledit| with a pointer to
+the filename, its length, and the line number.
+However, here we just set up the variables that will be used as arguments,
+since we don't want to do the switch-to-editor until after \TeX\ has closed
+its files.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines haven't
+been commented~out.
+@^debugging@>
+@d edit_file==input_stack[base_ptr]
+@z
+@x
+"E": if base_ptr>0 then
+ begin print_nl("You want to edit file ");
+@.You want to edit file x@>
+ slow_print(input_stack[base_ptr].name_field);
+ print(" at line "); print_int(line);
+ interaction:=scroll_mode; jump_out;
+@y
+"E": if base_ptr>0 then
+ begin
+ ed_name_start:=str_start[edit_file.name_field];
+ ed_name_length:=str_start[edit_file.name_field+1] -
+ str_start[edit_file.name_field];
+ edit_line:=line;
+ jump_out;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7.109] glue_ratio
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!glue_ratio=real; {one-word representation of a glue expansion factor}
+@y
+@!glue_ratio=shortreal; {one-word representation of a glue expansion factor}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8.110] ranges for quarter,half words
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if the subrange is `|-128..127|'.
+
+The present implementation tries to accommodate as many variations as possible,
+so it makes few assumptions. If integers having the subrange
+`|min_quarterword..max_quarterword|' can be packed into a quarterword,
+and if integers having the subrange `|min_halfword..max_halfword|'
+can be packed into a halfword, everything should work satisfactorily.
+
+It is usually most efficient to have |min_quarterword=min_halfword=0|,
+so one should try to achieve this unless it causes a severe problem.
+The values defined here are recommended for most 32-bit computers.
+
+@d min_quarterword=0 {smallest allowable value in a |quarterword|}
+@d max_quarterword=255 {largest allowable value in a |quarterword|}
+@d min_halfword==0 {smallest allowable value in a |halfword|}
+@d max_halfword==65535 {largest allowable value in a |halfword|}
+@y
+if the subrange is `|-128..127|'.
+
+For Berkeley {\mc UNIX} (SunOS) we need to do the |-128..127| kind of range.
+
+@d min_quarterword=-128 {smallest allowable value in a |quarterword|}
+@d max_quarterword=127 {largest allowable value in a |quarterword|}
+@d min_halfword==-32768 {smallest allowable value in a |halfword|}
+@d max_halfword==32767 {largest allowable value in a |halfword|}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [11.165] fix the word "free" so that it doesn't conflict with a runtime proc
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+@y
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+
+@d free==free_arr
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [12.186] glue_ratio fix
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+floating point underflow on the author's computer.
+@^system dependencies@>
+@^dirty \PASCAL@>
+
+@<Display the value of |glue_set(p)|@>=
+g:=float(glue_set(p));
+if (g<>float_constant(0))and(glue_sign(p)<>normal) then
+ begin print(", glue set ");
+ if glue_sign(p)=shrinking then print("- ");
+ if abs(mem[p+glue_offset].int)<@'4000000 then print("?.?")
+ else if abs(g)>float_constant(20000) then
+ begin if g>float_constant(0) then print_char(">")
+ else print("< -");
+ print_glue(20000*unity,glue_order(p),0);
+ end
+ else print_glue(round(unity*g),glue_order(p),0);
+@^real multiplication@>
+ end
+@y
+floating point underflow on the author's computer.
+For the {\mc VAX}, the only possible random value that could hurt is
+a reserved value with 1 in the sign bit and 0 for the (excess) exponent.
+Because the sign-plus-exponent is in the middle of the word, the chances
+of this happening are miniscule, and ignored here.
+@^system dependencies@>
+@^dirty \PASCAL@>
+
+@<Display the value of |glue_set(p)|@>=
+g:=float(glue_set(p));
+if (g<>float_constant(0))and(glue_sign(p)<>normal) then
+ begin print(", glue set ");
+ if glue_sign(p)=shrinking then print("- ");
+ if abs(float(glue_set(p)))>20000.0 then
+ begin if float(glue_set(p))>0 then print_char(">")
+ else print("< -");
+ print_glue(20000*unity,glue_order(p),0);
+ end
+ else print_glue(round(float(glue_set(p))*unity),glue_order(p),0);
+@^real multiplication@>
+ end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [17.241] fix_date_and_time
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following procedure, which is called just before \TeX\ initializes its
+input and output, establishes the initial values of the date and time.
+@^system dependencies@>
+Since standard \PASCAL\ cannot provide such information, something special
+is needed. The program here simply specifies July 4, 1776, at noon; but
+users probably want a better approximation to the truth.
+
+@p procedure fix_date_and_time;
+begin time:=12*60; {minutes since midnight}
+day:=4; {fourth day of the month}
+month:=7; {seventh month of the year}
+year:=1776; {Anno Domini}
+end;
+@y
+@ The following procedure, which is called just before \TeX\ initializes its
+input and output, establishes the initial values of the date and time.
+It is calls an externally defined |date_and_time|, even though it could
+be done from Pascal.
+The external procedure also sets up interrupt catching.
+@^system dependencies@>
+
+@p procedure fix_date_and_time;
+begin
+ date_and_time(time,day,month,year);
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.513] area and extension rules
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+@y
+@ The file names we shall deal with for SunOS have the
+following structure: If the name contains `\./', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep
+track of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\./', if any}
+@!ext_delimiter:pool_pointer; {the most recent `\..', if any}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.514] TeX area directories
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d TEX_area=="TeXinputs:"
+@.TeXinputs@>
+@d TEX_font_area=="TeXfonts:"
+@.TeXfonts@>
+@y
+Under Berkeley {\mc UNIX}, the default paths are specified in a separate
+file, `\.{texpaths.h}'. The file opening procedures do path searching
+based either on those default paths, or on paths given by the user
+in ``environment'' variables.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.516] more_name
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ if (c=">")or(c=":") then
+@y
+ if c="/" then
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.520] default format
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d format_default_length=20 {length of the |TEX_format_default| string}
+@d format_area_length=11 {length of its area part}
+@d format_ext_length=4 {length of its `\.{.fmt}' part}
+@y
+Under Berkeley {\mc UNIX} we don't give the area part, instead depending
+on the path searching that will happen during file opening.
+
+@d format_default_length=9 {length of the |TEX_format_default| string}
+@d format_area_length=0 {length of its area part}
+@d format_ext_length=4 {length of its `\.{.fmt}' part}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.521] plain format location
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+TEX_format_default:='TeXformats:plain.fmt';
+@y
+TEX_format_default:='plain.fmt';
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.524] format file opening: only try once, with path search
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(fmt_file) then goto found;
+ pack_buffered_name(format_area_length,loc,j-1);
+ {now try the system format file area}
+ if w_open_in(fmt_file) then goto found;
+@y
+ pack_buffered_name(0,loc,j-1);
+ if w_open_in(fmt_file) then goto found;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.525] make_name_string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+which simply makes a \TeX\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var k:1..file_name_size; {index into |name_of_file|}
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings)or
+ (cur_length>0) then
+ make_name_string:="?"
+else begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+end;
+@y
+which simply makes a \TeX\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+With the Berkeley {\mc UNIX} version, we know that |real_name_of_file|
+contains |name_of_file| prepended with the directory name that was found
+by path searching.
+If |real_name_of_file| starts with |'./'|, we don't use that part of the
+name, since {\mc UNIX} users understand that.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var k,@!kstart:1..file_name_size; {index into |name_of_file|}
+begin
+k:=1;
+while (k<file_name_size) and (xord[real_name_of_file[k]]<>" ") do
+ incr(k);
+name_length:=k-1; {the real |name_length|}
+if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings)or
+ (cur_length>0) then
+ make_name_string:="?"
+else begin
+ if (xord[real_name_of_file[1]]=".") and (xord[real_name_of_file[2]]="/") then
+ kstart:=3
+ else
+ kstart:=1;
+ for k:=kstart to name_length do append_char(xord[real_name_of_file[k]]);
+ make_name_string:=make_string;
+ end
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.537] a_open_in of \input file needs path selector
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ if a_open_in(cur_file) then goto done;
+ if cur_area="" then
+ begin pack_file_name(cur_name,TEX_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+ end;
+@y
+ if a_open_in(cur_file,input_file_path) then goto done;
+ if cur_ext=".tex" then
+ begin pack_file_name(cur_name,cur_area,"");
+ if a_open_in(cur_file,input_file_path) then goto done;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.537] get rid of return of name to string pool
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if name=str_ptr-1 then {we can conserve string pool space now}
+ begin flush_string; name:=cur_name;
+ end;
+@y
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [30.557] optimize kern_base_offset
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d kern_base_offset==256*(128+min_quarterword)
+@y
+@d kern_base_offset==0 {our |kern_flag| is zero}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [30.563] opening tfm file: now path searching is done
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Set temp_int to value of first byte
+@x
+@ @<Open |tfm_file| for input@>=
+file_opened:=false;
+if aire="" then pack_file_name(nom,TEX_font_area,".tfm")
+else pack_file_name(nom,aire,".tfm");
+if not b_open_in(tfm_file) then abort;
+file_opened:=true
+@y
+@ Here we have to ``prime the pump'' for the |temp_int| trick explained below.
+
+ @<Open |tfm_file| for input@>=
+file_opened:=false;
+pack_file_name(nom,aire,".tfm");
+if not b_open_in(tfm_file) then abort;
+begin temp_int:=tfm_file^;
+ if temp_int<0 then temp_int:=temp_int+256;
+end;
+file_opened:=true
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [30.564] reading the tfm file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d fget==get(tfm_file)
+@d fbyte==tfm_file^
+@y
+For Berkeley {\mc UNIX} we had to make the |byte_file| type have data in the
+range |-128..127|.
+If the file byte contains |@'377|, we want to regard this as 255 in decimal.
+However if we do |i:=tfm_file^| on that byte, where i is a signed integer,
+then the result is |i=-1|.
+File bytes with values |<@'200| are read properly.
+The solution is to do the assignment as shown, and then add 256 to values
+less than 0.
+We could also assign to |eight_bits| values, but this causes an error if
+runtime checking is turned on.
+The integer |temp_int| will be added to global variables at the end,
+where it won't disturb module numbering.
+
+@d fget==begin get(tfm_file);
+ temp_int:=tfm_file^;
+ if temp_int<0 then temp_int:=temp_int+256;
+ end
+@d fbyte==temp_int
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [32.597] write_dvi
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure write_dvi(@!a,@!b:dvi_index);
+var k:dvi_index;
+begin for k:=a to b do write(dvi_file,dvi_buf[k]);
+end;
+@y
+For Berkeley {\mc UNIX}, this is going to be handled by an external procedure,
+|writedvi|, which will do the output using |fwrite|.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [49.1275] a_open_in of \read file needs path specifier
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ if a_open_in(read_file[n]) then read_open[n]:=just_open;
+@y
+ if a_open_in(read_file[n],read_file_path) then read_open[n]:=just_open;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [51.1332] Delete ready_already; also add call to set_paths;
+% also add call to exit() depending upon value of `history'
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Now this is really it: \TeX\ starts and ends here.
+
+The initial test involving |ready_already| should be deleted if the
+\PASCAL\ runtime system is smart enough to detect such a ``mistake.''
+@y
+@ Now this is really it: \TeX\ starts and ends here.
+
+Instead of testing |ready_already|, we give up the idea of a preloaded
+core image in favor of silently loading a format file based on the
+name by which this program has been invoked. For example, if the
+program is being called \.{tex}, we will load \.{plain} format;
+if \.{latex}, we will load \.{latex} format. On my machine the
+loading of a format isn't too slow. (I use the new string capabilities
+of Sun Pascal here; |my_name| will be declared {\bf varying} [100]
+{\bf of\/} |char|.)
+
+At the end of the job, we look at |history| to decide the correct
+exit code. We use 1 if |history <= warning_issued| and 0 otherwise.
+
+@d UNIXexit==e@&x@&i@&t
+@z
+
+@x
+t_open_out; {open the terminal for output}
+if ready_already=314159 then goto start_of_TEX;
+@y
+t_open_out; {open the terminal for output}
+set_paths;
+@z
+
+@x
+start_of_TEX: @<Initialize the output routines@>;
+@y
+start_of_TEX: argv(0,my_name);
+if (my_name<>'initex')and(my_name<>'triptex') then
+ begin if my_name = 'tex' then name_of_file:='plain.fmt'
+ else name_of_file:=my_name+'.fmt';
+ if w_open_in(fmt_file) then
+ if not load_fmt_file then
+ begin w_close(fmt_file); goto final_end;
+ end
+ else w_close(fmt_file);
+ end;
+@<Initialize the output routines@>;
+@z
+
+@x
+final_end: ready_already:=0;
+@y
+final_end: ready_already:=0;
+if (history <= warning_issued) then
+ UNIXexit(0)
+else
+ UNIXexit(1);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [51.1333] print new line before termination; switch to editor if nec.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+@y
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+print_ln;
+if (ed_name_start<>0) and (interaction>batch_mode) then
+ calledit(str_pool[ed_name_start],ed_name_length,edit_line);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [54.1376] add temp_int and editor-switch variables to globals
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* \[54] System-dependent changes.
+This section should be replaced, if necessary, by any special
+modifications of the program
+that are necessary to make \TeX\ work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the published program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+@y
+@* \[54] System-dependent changes.
+Here is a temporary integer, used as a holder during reading and writing of
+TFM files.
+Also, the variable used omphaloskeptically.
+Also, the variables that holds ``switch-to-editor'' information.
+@^system dependencies@>
+
+@f varying == array
+
+@<Global...@>=
+@!temp_int:integer;
+@!my_name:varying [100] of char;
+@!ed_name_start: pool_pointer;
+@!ed_name_length,@!edit_line: integer;
+
+@ The |ed_name_start| will be set to point into |str_pool| somewhere after
+its beginning if \TeX\ is supposed to switch to an editor on exit.
+
+@<Set init...@>=
+ed_name_start:=0;
+
+@z
diff --git a/systems/knuth/local/tex-sparc/plain.fmt b/systems/knuth/local/tex-sparc/plain.fmt
new file mode 100644
index 0000000000..f9c7455f65
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/plain.fmt
Binary files differ
diff --git a/systems/knuth/local/tex-sparc/plain.log b/systems/knuth/local/tex-sparc/plain.log
new file mode 100644
index 0000000000..6a7f9f18a3
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/plain.log
@@ -0,0 +1,103 @@
+This is TeX, Version 3.14159 for SunOS (INITEX) 14 AUG 1998 08:06
+**plain \input local \dump
+(/home/tex/dist/lib/plain.tex Preloading the plain format: codes, registers,
+\maxdimen=\dimen10
+\hideskip=\skip10
+\centering=\skip11
+\p@=\dimen11
+\z@=\dimen12
+\z@skip=\skip12
+\voidb@x=\box10
+
+parameters,
+\smallskipamount=\skip13
+\medskipamount=\skip14
+\bigskipamount=\skip15
+\normalbaselineskip=\skip16
+\normallineskip=\skip17
+\normallineskiplimit=\dimen13
+\jot=\dimen14
+\interdisplaylinepenalty=\count23
+\interfootnotelinepenalty=\count24
+ fonts, more fonts,
+\itfam=\fam4
+\slfam=\fam5
+\bffam=\fam6
+\ttfam=\fam7
+ macros,
+\strutbox=\box11
+\mscount=\count25
+\tabs=\box12
+\tabsyet=\box13
+\tabsdone=\box14
+ math definitions,
+\rootbox=\box15
+\p@renwd=\dimen15
+ output routines,
+\headline=\toks10
+\footline=\toks11
+\footins=\insert254
+\topins=\insert253
+
+hyphenation (/home/tex/dist/lib/hyphen.tex)) (/home/tex/local/lib/local.tex)
+Beginning to dump on file plain.fmt
+ (preloaded format=plain 98.8.14)
+1968 strings of total length 27846
+5013 memory locations dumped; current usage is 110&4900
+926 multiletter control sequences
+\font\nullfont=nullfont
+\font\tenrm=cmr10
+\font\preloaded=cmr9
+\font\preloaded=cmr8
+\font\sevenrm=cmr7
+\font\preloaded=cmr6
+\font\fiverm=cmr5
+\font\teni=cmmi10
+\font\preloaded=cmmi9
+\font\preloaded=cmmi8
+\font\seveni=cmmi7
+\font\preloaded=cmmi6
+\font\fivei=cmmi5
+\font\tensy=cmsy10
+\font\preloaded=cmsy9
+\font\preloaded=cmsy8
+\font\sevensy=cmsy7
+\font\preloaded=cmsy6
+\font\fivesy=cmsy5
+\font\tenex=cmex10
+\font\preloaded=cmss10
+\font\preloaded=cmssq8
+\font\preloaded=cmssi10
+\font\preloaded=cmssqi8
+\font\tenbf=cmbx10
+\font\preloaded=cmbx9
+\font\preloaded=cmbx8
+\font\sevenbf=cmbx7
+\font\preloaded=cmbx6
+\font\fivebf=cmbx5
+\font\tentt=cmtt10
+\font\preloaded=cmtt9
+\font\preloaded=cmtt8
+\font\preloaded=cmsltt10
+\font\tensl=cmsl10
+\font\preloaded=cmsl9
+\font\preloaded=cmsl8
+\font\tenit=cmti10
+\font\preloaded=cmti9
+\font\preloaded=cmti8
+\font\preloaded=cmti7
+\font\preloaded=cmu10
+\font\preloaded=cmmib10
+\font\preloaded=cmbsy10
+\font\preloaded=cmcsc10
+\font\preloaded=cmssbx10
+\font\preloaded=cmdunh10
+\font\preloaded=cmr7 at 14.51799pt
+\font\preloaded=cmtt10 at 14.4pt
+\font\preloaded=cmssbx10 at 14.4pt
+\font\preloaded=manfnt
+14787 words of font info for 50 preloaded fonts
+14 hyphenation exceptions
+Hyphenation trie of length 6075 has 181 ops out of 500
+ 181 for language 0
+No pages of output.
diff --git a/systems/knuth/local/tex-sparc/tex.pool b/systems/knuth/local/tex-sparc/tex.pool
new file mode 100644
index 0000000000..c1358a0a41
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/tex.pool
@@ -0,0 +1,1040 @@
+11buffer size
+09pool size
+17number of strings
+03???
+13m2d5c2l5x2v5i
+28End of file on the terminal!
+02!
+42(That makes 100 errors; please try again.)
+02?
+60Type <return> to proceed, S to scroll future error messages,
+44R to run without stopping, Q to run quietly,
+23I to insert something,
+20E to edit your file,
+561 or ... or 9 to ignore the next 1 to 9 tokens of input,
+22H for help, X to quit.
+13OK, entering
+09batchmode
+11nonstopmode
+10scrollmode
+03...
+07insert>
+44I have just deleted some text, as you asked.
+48You can now delete more, or insert, or whatever.
+50Sorry, I don't know how to help in this situation.
+36Maybe you should try asking a human?
+42Sorry, I already gave what help I could...
+59An error might have occurred before I noticed any problems.
+45``If all else fails, read the instructions.''
+02 (
+14Emergency stop
+30TeX capacity exceeded, sorry [
+44If you really absolutely need more capacity,
+35you can ask a wizard to enlarge me.
+19This can't happen (
+59I'm broken. Please show this to someone who can fix can fix
+35I can't go on meeting you like this
+55One of your faux pas seems to have wounded me deeply...
+59in fact, I'm barely conscious. Please fix it and try again.
+12Interruption
+09You rang?
+60Try to insert some instructions for me (e.g.,`I\showlists'),
+43unless you just want to quit by typing `X'.
+16main memory size
+24AVAIL list clobbered at
+31Double-AVAIL list clobbered at
+24Doubly free location at
+12Bad flag at
+14New busy locs:
+05LINK(
+05INFO(
+02[]
+10CLOBBERED.
+04foul
+03fil
+06 plus
+07 minus
+03 []
+26Bad link, display aborted.
+04etc.
+18Unknown node type!
+05unset
+04box(
+02)x
+10, shifted
+09 columns)
+10, stretch
+09, shrink
+11, glue set
+02-
+03< -
+05rule(
+06insert
+15, natural size
+08; split(
+14); float cost
+04glue
+09nonscript
+05mskip
+02mu
+00
+08leaders
+04kern
+13 (for accent)
+05mkern
+04math
+02on
+03off
+13, surrounded
+11 (ligature
+08penalty
+13discretionary
+11 replacing
+04mark
+07vadjust
+08flushing
+07copying
+08vertical
+10horizontal
+12display math
+02no
+17internal vertical
+21restricted horizontal
+05 mode
+18semantic nest size
+04###
+17 entered at line
+10 (language
+10:hyphenmin
+18 (\output routine)
+25### recent contributions:
+10prevdepth
+07ignored
+11, prevgraf
+05 line
+12spacefactor
+19, current language
+28this will be denominator of:
+08lineskip
+12baselineskip
+07parskip
+16abovedisplayskip
+16belowdisplayskip
+21abovedisplayshortskip
+21belowdisplayshortskip
+08leftskip
+09rightskip
+07topskip
+12splittopskip
+07tabskip
+09spaceskip
+10xspaceskip
+11parfillskip
+10thinmuskip
+09medmuskip
+11thickmuskip
+25[unknown glue parameter!]
+04skip
+06muskip
+02pt
+06output
+08everypar
+09everymath
+12everydisplay
+09everyhbox
+09everyvbox
+08everyjob
+07everycr
+07errhelp
+04toks
+08parshape
+03box
+04void
+12current font
+08textfont
+10scriptfont
+16scriptscriptfont
+07catcode
+06lccode
+06uccode
+06sfcode
+08mathcode
+12pretolerance
+09tolerance
+11linepenalty
+13hyphenpenalty
+15exhyphenpenalty
+11clubpenalty
+12widowpenalty
+19displaywidowpenalty
+13brokenpenalty
+12binoppenalty
+10relpenalty
+17predisplaypenalty
+18postdisplaypenalty
+16interlinepenalty
+20doublehyphendemerits
+19finalhyphendemerits
+11adjdemerits
+03mag
+15delimiterfactor
+09looseness
+04time
+03day
+05month
+04year
+14showboxbreadth
+12showboxdepth
+08hbadness
+08vbadness
+07pausing
+13tracingonline
+13tracingmacros
+12tracingstats
+17tracingparagraphs
+12tracingpages
+13tracingoutput
+16tracinglostchars
+15tracingcommands
+15tracingrestores
+06uchyph
+13outputpenalty
+13maxdeadcycles
+09hangafter
+15floatingpenalty
+10globaldefs
+03fam
+10escapechar
+17defaulthyphenchar
+15defaultskewchar
+11endlinechar
+11newlinechar
+08language
+13lefthyphenmin
+14righthyphenmin
+14holdinginserts
+17errorcontextlines
+28[unknown integer parameter!]
+05count
+07delcode
+09parindent
+12mathsurround
+13lineskiplimit
+05hsize
+05vsize
+08maxdepth
+13splitmaxdepth
+11boxmaxdepth
+05hfuzz
+05vfuzz
+18delimitershortfall
+18nulldelimiterspace
+11scriptspace
+14predisplaysize
+12displaywidth
+13displayindent
+12overfullrule
+10hangindent
+07hoffset
+07voffset
+16emergencystretch
+26[unknown dimen parameter!]
+05dimen
+06EQUIV(
+12notexpanded:
+09hash size
+06csname
+09endcsname
+11IMPOSSIBLE.
+12NONEXISTENT.
+06accent
+07advance
+15afterassignment
+10aftergroup
+10begingroup
+04char
+09delimiter
+06divide
+08endgroup
+11expandafter
+04font
+09fontdimen
+06halign
+05hrule
+12ignorespaces
+10mathaccent
+08mathchar
+10mathchoice
+08multiply
+07noalign
+10noboundary
+08noexpand
+04omit
+07penalty
+08prevgraf
+07radical
+04read
+05relax
+06setbox
+03the
+06valign
+07vcenter
+05vrule
+09save size
+15grouping levels
+08curlevel
+09retaining
+09restoring
+05SAVE(
+28Incompatible magnification (
+02);
+36 the previous value will be retained
+58I can handle only one magnification ratio per job. So I've
+59reverted to the magnification you used earlier on this run.
+46Illegal magnification has been changed to 1000
+52The magnification ratio must be between 1 and 32768.
+04ETC.
+04BAD.
+02->
+22begin-group character
+20end-group character
+21math shift character
+26macro parameter character
+22superscript character
+20subscript character
+25end of alignment template
+12blank space
+11the letter
+14the character
+23[unknown command code!]
+02:
+08Runaway
+10definition
+08argument
+08preamble
+04text
+03<*>
+09<insert>
+06<read
+02l.
+11<argument>
+11<template>
+16<recently read>
+19<to be read again>
+16<inserted text>
+09<output>
+11<everypar>
+12<everymath>
+15<everydisplay>
+12<everyhbox>
+12<everyvbox>
+11<everyjob>
+10<everycr>
+07<mark>
+08<write>
+16input stack size
+05write
+48(interwoven alignment preambles are not allowed)
+17text input levels
+03par
+11Incomplete
+34; all text was ignored after line
+54A forbidden control sequence occurred in skipped text.
+59This kind of error happens when you say `\if...' and forget
+59the matching `\fi'. I've inserted a `\fi'; this might work.
+53The file ended while I was skipping conditional text.
+10File ended
+32Forbidden control sequence found
+16 while scanning
+04 of
+46I suspect you have forgotten a `}', causing me
+41to read past where you wanted me to stop.
+49I'll try to recover; but if the error is serious,
+51you'd better type `E' or `X' now and fix your file.
+03use
+39Text line contains an invalid character
+53A funny symbol that I can't read has just been input.
+48Continue, and I'll forget that it ever happened.
+37(Please type a command or say `\end')
+38*** (job aborted, no legal \end found)
+02=>
+26Undefined control sequence
+47The control sequence at the end of the top line
+52of your error message was never \def'ed. If you have
+55misspelled it (e.g., `\hobx'), type `I' and the correct
+51spelling (e.g., `I\hbox'). Otherwise just continue,
+45and I'll forget about whatever was undefined.
+08Missing
+09 inserted
+53The control sequence marked <to be read again> should
+42not appear between \csname and \endcsname.
+05input
+08endinput
+07topmark
+09firstmark
+07botmark
+14splitfirstmark
+12splitbotmark
+20parameter stack size
+12Argument of
+15 has an extra }
+58I've run across a `}' that doesn't seem to match anything.
+52For example, `\def\a#1{...}' and `\a}' would produce
+54this error. If you simply proceed now, the `\par' that
+52I've just inserted will cause me to report a runaway
+54argument that might be the root of the problem. But if
+57your `}' was spurious, just type `2' and it will go away.
+23Paragraph ended before
+13 was complete
+58I suspect you've forgotten a `}', causing me to apply this
+54control sequence to too much text. How can we recover?
+59My plan is to forget the whole thing and hope for the best.
+07Use of
+29 doesn't match its definition
+54If you say, e.g., `\def\a1{...}', then you must always
+52put `1' after `\a', since control sequence names are
+52made up of letters only. The macro here has not been
+51followed by the required stuff, so I'm ignoring it.
+02<-
+18Missing { inserted
+52A left brace was mandatory here, so I've put one in.
+55You might want to delete and/or insert some corrections
+48so that I will find a matching right brace soon.
+54(If you're confused by all this, try typing `I}' now.)
+23Incompatible glue units
+52I'm going to assume that 1mu=1pt when they're mixed.
+31Missing number, treated as zero
+47A number should have been here; I inserted `0'.
+54(If you can't figure out why I needed to see a number,
+51look up `weird error' in the index to The TeXbook.)
+11spacefactor
+09prevdepth
+10deadcycles
+15insertpenalties
+02wd
+02ht
+02dp
+11lastpenalty
+08lastkern
+08lastskip
+11inputlineno
+07badness
+09Improper
+54You can refer to \spacefactor only in horizontal mode;
+54you can refer to \prevdepth only in vertical mode; and
+48neither of these is meaningful inside \write. So
+52I'm forgetting what you said and using zero instead.
+15You can't use `
+08' after
+17Bad register code
+44A register number must be between 0 and 255.
+27I changed this one to zero.
+18Bad character code
+45A character number must be between 0 and 255.
+10Bad number
+51Since I expected to read a number between 0 and 15,
+12Bad mathchar
+46A mathchar number must be between 0 and 32767.
+18Bad delimiter code
+56A numeric delimiter code must be between 0 and 2^{27}-1.
+28Improper alphabetic constant
+56A one-character control sequence belongs after a ` mark.
+37So I'm essentially inserting \0 here.
+14Number too big
+54I can only go up to 2147483647='17777777777="7FFFFFFF,
+42so I'm using that number instead of yours.
+04true
+25Illegal unit of measure (
+18replaced by filll)
+35I dddon't go any higher than filll.
+02em
+02ex
+12mu inserted)
+48The unit of measurement in math glue must be mu.
+51To recover gracefully from this error, it's best to
+52delete the erroneous units; e.g., type `2' to delete
+45two letters. (See Chapter 27 of The TeXbook.)
+02in
+02pc
+02cm
+02mm
+02bp
+02dd
+02cc
+02sp
+12pt inserted)
+49Dimensions can be in units of em, ex, in, pt, pc,
+50cm, mm, dd, cc, bp, or sp; but yours is a new one!
+59I'll assume that you meant to say pt, for printer's points.
+19Dimension too large
+50I can't work with sizes bigger than about 19 feet.
+46Continue and I'll use the largest value I can.
+04plus
+05minus
+05width
+06height
+05depth
+06number
+12romannumeral
+06string
+07meaning
+08fontname
+07jobname
+04 at
+60Where was the left brace? You said something like `\def\a}',
+43which I'm going to interpret as `\def\a{}'.
+32You already have nine parameters
+45I'm going to ignore the # sign you just used.
+41Parameters must be numbered consecutively
+57I've inserted the digit you should have used after the #.
+36Type `1' to delete what you did use.
+42Illegal parameter number in definition of
+41You meant to type ## instead of #, right?
+56Or maybe a } was forgotten somewhere earlier, and things
+58are all screwed up? I'm going to assume that you meant ##.
+49*** (cannot \read from terminal in nonstop modes)
+18File ended within
+33This \read has unbalanced braces.
+02if
+05ifcat
+05ifnum
+05ifdim
+05ifodd
+07ifvmode
+07ifhmode
+07ifmmode
+07ifinner
+06ifvoid
+06ifhbox
+06ifvbox
+03ifx
+05ifeof
+06iftrue
+07iffalse
+06ifcase
+02fi
+02or
+04else
+06Extra
+44I'm ignoring this; it doesn't match any \if.
+06{true}
+07{false}
+23Missing = inserted for
+48I was expecting to see `<', `=', or `>'. Didn't.
+06{case
+04.fmt
+15input file name
+19I can't find file `
+23I can't write on file `
+02'.
+04.tex
+20Please type another
+45*** (job aborted, file error in nonstop mode)
+04.dvi
+20file name for output
+06texput
+04.log
+02**
+20transcript file name
+02
+08nullfont
+05Font
+08 scaled
+36 not loadable: Bad metric (TFM) file
+42 not loadable: Metric (TFM) file not found
+50I wasn't able to read the size data for this font,
+40so I will ignore the font specification.
+48[Wizards can fix TFM files using TFtoPL/PLtoTF.]
+46You might try inserting a different font spec;
+57e.g., type `I\font<same font id>=<substitute font name>'.
+04.tfm
+33 not loaded: Not enough room left
+52I'm afraid I won't be able to make use of this font,
+55because my memory for character-size data is too small.
+51If you're really stuck, ask a wizard to enlarge me.
+58Or maybe try `I\font<same font id>=<name of loaded font>'.
+23Missing font identifier
+42I was looking for a control sequence whose
+42current meaning has been defined by \font.
+10 has only
+21 fontdimen parameters
+51To increase the number of font parameters, you must
+53use \fontdimen immediately after the \font is loaded.
+11font memory
+31Missing character: There is no
+09 in font
+12 TeX output
+08vlistout
+31Completed box being shipped out
+21Memory usage before:
+08 after:
+19; still untouched:
+31Huge page cannot be shipped out
+50The page just created is more than 18 feet tall or
+58more than 18 feet wide, so I suspect something went wrong.
+35The following box has been deleted:
+19No pages of output.
+18Output written on
+05 page
+02,
+08 bytes).
+02to
+06spread
+09Underfull
+05Loose
+16 \hbox (badness
+38) has occurred while \output is active
+24) in paragraph at lines
+24) in alignment at lines
+02--
+19) detected at line
+16Overfull \hbox (
+11pt too wide
+21Tight \hbox (badness
+05vpack
+16 \vbox (badness
+16Overfull \vbox (
+11pt too high
+21Tight \vbox (badness
+02{}
+12displaystyle
+09textstyle
+11scriptstyle
+17scriptscriptstyle
+14Unknown style!
+07mathord
+06mathop
+07mathbin
+07mathrel
+08mathopen
+09mathclose
+09mathpunct
+09mathinner
+08overline
+09underline
+04left
+05right
+06limits
+08nolimits
+20fraction, thickness
+09= default
+17, left-delimiter
+18, right-delimiter
+25 is undefined (character
+54Somewhere in the math formula just ended, you used the
+60stated character from an undefined font family. For example,
+58plain TeX doesn't allow \it or \sl in subscripts. Proceed,
+52and I'll try to forget that I needed that character.
+06mlist1
+06mlist2
+06mlist3
+640234000122*4000133**3**344*0400400*000000234000111*1111112341011
+06mlist4
+12 inside $$'s
+53Displays can use special alignments (like \eqalignno)
+57only if nothing but the alignment itself is between $$'s.
+58So I've deleted the formulas that preceded this alignment.
+04span
+02cr
+04crcr
+11endtemplate
+24alignment tab character
+40Missing # inserted in alignment preamble
+50There should be exactly one # between &'s, when an
+56\halign or \valign is being set up. In this case you had
+47none, so I've put one in; maybe that will work.
+29Only one # is allowed per tab
+49more than one, so I'm ignoring all but the first.
+04endv
+40Extra alignment tab has been changed to
+52You have given more \span or & marks than there were
+58in the preamble to the \halign or \valign now in progress.
+50So I'll assume that you meant to type \cr instead.
+09256 spans
+06align1
+06align0
+44Infinite glue shrinkage found in a paragraph
+52The paragraph just ended includes some glue that has
+54infinite shrinkability, e.g., `\hskip 0pt minus 1fil'.
+54Such glue doesn't belong there---it allows a paragraph
+59of any length to fit on one line. But it's safe to proceed,
+55since the offensive shrinkability has been made finite.
+05disc1
+05disc2
+02@@
+07: line
+03 t=
+06 -> @@
+07 via @@
+03 b=
+03 p=
+03 d=
+10@firstpass
+11@secondpass
+14@emergencypass
+09paragraph
+05disc3
+05disc4
+13line breaking
+05HYPH(
+11hyphenation
+16 will be flushed
+48Hyphenation exceptions must contain only letters
+51and hyphens. But continue; I'll forgive and forget.
+12Not a letter
+50Letters in \hyphenation words must have \lccode>0.
+47Proceed; I'll ignore the character I just read.
+20exception dictionary
+18pattern memory ops
+31pattern memory ops per language
+14pattern memory
+13Too late for
+08patterns
+53All patterns must be given before typesetting begins.
+04Bad
+17(See Appendix H.)
+09Nonletter
+17Duplicate pattern
+07pruning
+09vertbreak
+48Infinite glue shrinkage found in box being split
+52The box you are \vsplitting contains some infinitely
+57shrinkable glue, e.g., `\vss' or `\vskip 0pt minus 1fil'.
+59Such glue doesn't belong there; but you can safely proceed,
+06vsplit
+09 needs a
+04vbox
+44The box you are trying to split is an \hbox.
+49I can't split such a box, so I'll leave it alone.
+08pagegoal
+09pagetotal
+11pagestretch
+14pagefilstretch
+15pagefillstretch
+16pagefilllstretch
+10pageshrink
+09pagedepth
+04fill
+05filll
+17### current page:
+28 (held over for next output)
+13total height
+13 goal height
+06 adds
+03, #
+12 might split
+15%% goal height=
+12, max depth=
+38Insertions can only be added to a vbox
+40Tut tut: You're trying to \insert into a
+41\box register that now contains an \hbox.
+47Proceed, and I'll discard its present contents.
+04page
+45Infinite glue shrinkage found on current page
+52The page about to be output contains some infinitely
+03 g=
+03 c=
+38Infinite glue shrinkage inserted from
+53The correction glue for page breaking with insertions
+52must have finite shrinkability. But you may proceed,
+07% split
+04 to
+15255 is not void
+53You shouldn't use \box255 except in \output routines.
+14Output loop---
+24 consecutive dead cycles
+57I've concluded that your \output is awry; it never does a
+55\shipout, so I'm shipping \box255 out myself. Next time
+58increase \maxdeadcycles if you want me to be more patient!
+25Unbalanced output routine
+58Your sneaky output routine has problematic {'s and/or }'s.
+41I can't handle that very well; good luck.
+33Output routine didn't use all of
+43Your \output commands should empty \box255,
+34e.g., by saying `\shipout\box255'.
+43Proceed; I'll discard its present contents.
+18Missing $ inserted
+56I've inserted a begin-math/end-math symbol since I think
+48you left one out. Proceed, with fingers crossed.
+05' in
+50Sorry, but I'm not programmed to handle this case;
+45I'll just pretend that you didn't ask for it.
+49If you're in the wrong mode, you might be able to
+58return to the right one by typing `I}' or `I$' or `I\par'.
+03end
+04dump
+05hskip
+04hfil
+05hfill
+03hss
+07hfilneg
+05vskip
+04vfil
+05vfill
+03vss
+07vfilneg
+52I've inserted something that you may have forgotten.
+32(See the <inserted text> above.)
+48With luck, this will get me unwedged. But if you
+55really didn't forget anything, try typing `2' now; then
+56my insertion and my current dilemma will both disappear.
+06right.
+58Things are pretty mixed up, but I think the worst is over.
+12Too many }'s
+42You've closed more groups than you opened.
+51Such booboos are generally harmless, so keep going.
+10rightbrace
+22Extra }, or forgotten
+58I've deleted a group-closing symbol because it seems to be
+59spurious, as in `$x}$'. But perhaps the } is legitimate and
+59you forgot something else, as in `\hbox{$x}'. In such cases
+58the way to recover is to insert both the forgotten and the
+40deleted material, e.g., by typing `I$}'.
+08moveleft
+09moveright
+05raise
+05lower
+04copy
+07lastbox
+04vtop
+04hbox
+07shipout
+07leaders
+08cleaders
+08xleaders
+35Leaders not followed by proper glue
+56You should say `\leaders <box or rule><hskip or vskip>'.
+50I found the <box or rule>, but there's no suitable
+48<hskip or vskip>, so I'm ignoring these leaders.
+34Sorry; this \lastbox will be void.
+58Sorry...I usually can't take things from the current page.
+37This \lastbox will therefore be void.
+21Missing `to' inserted
+48I'm working on `\vsplit<box number> to <dimen>';
+31will look for the <dimen> next.
+31A <box> was supposed to be here
+57I was expecting to see \hbox or \vbox or \copy or \box or
+59something like that. So you might find something missing in
+53your output. But keep trying; you can fix this later.
+06indent
+08noindent
+26' here except with leaders
+52To put a horizontal rule in an hbox or an alignment,
+56you should use \leaders or \hrulefill (see The TeXbook).
+10You can't
+45I'm changing to \insert0; box 255 is special.
+32Try `I\vskip-\lastskip' instead.
+31Try `I\kern-\lastkern' instead.
+46Perhaps you can make the output routine do it.
+09unpenalty
+06unkern
+06unskip
+06unhbox
+07unhcopy
+06unvbox
+07unvcopy
+34Incompatible list can't be unboxed
+35Sorry, Pandora. (You sneaky devil.)
+58I refuse to unbox an \hbox in vertical mode or vice versa.
+40And I can't open any boxes in math mode.
+13Illegal math
+54Sorry: The third part of a discretionary break must be
+57empty, in math formulas. I had to delete your third part.
+30Discretionary list is too long
+50Wow---I never thought anybody would tweak me here.
+56You can't seriously need such a huge discretionary list?
+27Improper discretionary list
+54Discretionary lists must contain only boxes and kerns.
+53The following discretionary sublist has been deleted:
+18Missing } inserted
+45I've put in what seems to be necessary to fix
+44the current column of the current alignment.
+43Try to go on, since this might almost work.
+10Misplaced
+55I can't figure out why you would want to use a tab mark
+50here. If you just want an ampersand, the remedy is
+52simple: Just type `I\&' now. But if some right brace
+52up above has ended a previous alignment prematurely,
+52you're probably due for more error messages, and you
+57might try typing `S' now just to see what is salvageable.
+57or \cr or \span just now. If something like a right brace
+46I expect to see \noalign only after the \cr of
+49an alignment. Proceed, and I'll ignore this case.
+56I expect to see \omit only after tab marks or the \cr of
+53I'm guessing that you meant to end an alignment here.
+50I'm ignoring this, since I wasn't doing a \csname.
+04eqno
+05leqno
+13displaylimits
+42Limit controls must follow a math operator
+57I'm ignoring this misplaced \limits or \nolimits command.
+30Missing delimiter (. inserted)
+52I was expecting to see something like `(' or `\{' or
+55`\}' here. If you typed, e.g., `{' instead of `\{', you
+57should probably delete the `{' by typing `1' now, so that
+52braces don't get unbalanced. Otherwise just proceed.
+54Acceptable delimiters are characters whose \delcode is
+58nonnegative, or you can use `\delimiter <delimiter code>'.
+11Please use
+25 for accents in math mode
+55I'm changing \accent to \mathaccent here; wish me luck.
+59(Accents are not the same in formulas as they are in text.)
+18Double superscript
+43I treat `x^1^2' essentially like `x^1{}^2'.
+16Double subscript
+43I treat `x_1_2' essentially like `x_1{}_2'.
+05above
+04over
+04atop
+15abovewithdelims
+14overwithdelims
+14atopwithdelims
+35Ambiguous; you need another { and }
+55I'm ignoring this fraction specification, since I don't
+52know whether a construction like `x \over y \over z'
+53means `{x \over y} \over z' or `x \over {y \over z}'.
+49I'm ignoring a \right that had no matching \left.
+47Math formula deleted: Insufficient symbol fonts
+50Sorry, but I can't typeset math unless \textfont 2
+50and \scriptfont 2 and \scriptscriptfont 2 have all
+50the \fontdimen values needed in math symbol fonts.
+50Math formula deleted: Insufficient extension fonts
+50Sorry, but I can't typeset math unless \textfont 3
+50and \scriptfont 3 and \scriptscriptfont 3 have all
+53the \fontdimen values needed in math extension fonts.
+31Display math should end with $$
+59The `$' that I just saw supposedly matches a previous `$$'.
+49So I shall assume that you typed `$$' both times.
+07display
+19Missing $$ inserted
+04long
+05outer
+06global
+03def
+04gdef
+04edef
+04xdef
+06prefix
+29You can't use a prefix with `
+55I'll pretend you didn't say \long or \outer or \global.
+06' or `
+08' with `
+49I'll pretend you didn't say \long or \outer here.
+33Missing control sequence inserted
+52Please don't say `\def cs{...}', say `\def\cs{...}'.
+59I've inserted an inaccessible control sequence so that your
+60definition will be completed without mixing me up too badly.
+53You can recover graciously from this error, if you're
+42careful; see exercise 27.2 in The TeXbook.
+12inaccessible
+03let
+09futurelet
+07chardef
+11mathchardef
+08countdef
+08dimendef
+07skipdef
+09muskipdef
+07toksdef
+44You should have said `\read<number> to \cs'.
+34I'm going to look for the \cs now.
+14Invalid code (
+29), should be in the range 0..
+21), should be at most
+54I'm going to use 0 instead of that illegal code value.
+02by
+19Arithmetic overflow
+50I can't carry out that multiplication or division,
+33since the result is out of range.
+55I'm forgetting what you said and not changing anything.
+57Sorry, \setbox is not allowed after \halign in a display,
+45or between \accent and an accented character.
+16Bad space factor
+47I allow only values in the range 1..32767 here.
+37I allow only nonnegative values here.
+37Patterns can be loaded only by INITEX
+10hyphenchar
+08skewchar
+04FONT
+02at
+06scaled
+20Improper `at' size (
+21pt), replaced by 10pt
+50I can only handle fonts at positive sizes that are
+56less than 2048pt, so I've changed what you said to 10pt.
+12select font
+13errorstopmode
+06openin
+07closein
+07message
+10errmessage
+31(That was another \errmessage.)
+50This error message was generated by an \errmessage
+43command, so I can't give any explicit help.
+54Pretend that you're Hercule Poirot: Examine all clues,
+41and deduce the truth by order and method.
+09lowercase
+09uppercase
+04show
+07showbox
+07showthe
+09showlists
+57This isn't an error message; I'm just \showing something.
+46Type `I\show...' to show more (e.g., \show\cs,
+43\showthe\count10, \showbox255, \showlists).
+54And type `I\tracingonline=1\show...' to show boxes and
+57lists on your terminal as well as in the transcript file.
+02>
+09undefined
+05macro
+10long macro
+11outer macro
+17outer endtemplate
+06> \box
+02OK
+26 (see the transcript file)
+09 (INITEX)
+29You can't dump inside a group
+24`{...\dump}' is a no-no.
+25 strings of total length
+43 memory locations dumped; current usage is
+30 multiletter control sequences
+24 words of font info for
+15 preloaded font
+05\font
+22 hyphenation exception
+27Hyphenation trie of length
+05 has
+03 op
+08 out of
+14 for language
+19 (preloaded format=
+16format file name
+26Beginning to dump on file
+22Transcript written on
+02 )
+13end occurred
+24inside a group at level
+05when
+09 on line
+16 was incomplete)
+52(see the transcript file for additional information)
+35(\dump is performed only by INITEX)
+21debug # (-1 to exit):
+07openout
+08closeout
+07special
+09immediate
+11setlanguage
+20[unknown extension!]
+04ext1
+12 (hyphenmin
+08whatsit?
+04ext2
+04ext3
+08endwrite
+24Unbalanced write command
+59On this page there's a \write with fewer real {'s than }'s.
+04ext4
+16output file name
+*461251401
diff --git a/systems/knuth/local/tex-sparc/trip1.in b/systems/knuth/local/tex-sparc/trip1.in
new file mode 100644
index 0000000000..a93fd916c0
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/trip1.in
@@ -0,0 +1,2 @@
+
+\input trip
diff --git a/systems/knuth/local/tex-sparc/trip2.in b/systems/knuth/local/tex-sparc/trip2.in
new file mode 100644
index 0000000000..205d5cd650
--- /dev/null
+++ b/systems/knuth/local/tex-sparc/trip2.in
@@ -0,0 +1 @@
+ &trip trip
diff --git a/systems/knuth/local/tex/Makefile b/systems/knuth/local/tex/Makefile
new file mode 100644
index 0000000000..0890d2cc46
--- /dev/null
+++ b/systems/knuth/local/tex/Makefile
@@ -0,0 +1,75 @@
+# Make executable files for this workstation, using the master TeX sources
+
+#PFLAGS= -I.. -temp=.
+PFLAGS= -I.. -g
+#PFLAGS= -I..
+CFLAGS= -O -I..
+PC= time pc
+
+all: initex virtex
+
+triptest: triptex
+ -triptex < trip1.in
+ mv trip.log tripin.log
+ -triptex < trip2.in | tee trip.fot
+ dvitype trip.dvi < dvitype.in > /dev/null
+ mv dvitype.out trip.typ
+ rm -f *.dif
+ -diff tripin.log ../../dist/tex/tripin.log > tripin.log.dif
+ -diff trip.log ../../dist/tex/trip.log > trip.log.dif
+ -diff trip.fot ../../dist/tex/trip.fot > trip.fot.dif
+ -diff trip.typ ../../dist/tex/trip.typ > trip.typ.dif
+ more tripin.log.dif trip.log.dif trip.typ.dif trip.fot.dif
+
+%.p: %.ch
+ tangle ../../dist/tex/tex.web $*.ch
+ mv ../../dist/tex/tex.pool .
+ mv ../../dist/tex/tex.p $*.p
+
+initex.ch: ../../dist/tex/tex.web
+
+virtex.ch: initex.ch ini_to_vir
+ ini_to_vir initex.ch virtex.ch
+
+triptex.ch: initex.ch ini_to_trip
+ ini_to_trip initex.ch triptex.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+triptex.o: triptex.p
+ ${PC} -g ${PFLAGS} -c triptex.p
+
+initex: initex.o ext.o
+ pc ${PFLAGS} -o initex initex.o ext.o
+
+virtex: virtex.o ext.o
+ pc ${PFLAGS} -o virtex virtex.o ext.o
+
+triptex: triptex.o ext.o
+ pc -g ${PFLAGS} -o triptex triptex.o ext.o
+ cp ../../dist/tex/trip.tex .
+
+ext.o: ext.c ext.h ../h00vars.h ../texpaths.h
+ cc ${CFLAGS} -c ext.c
+
+plain.fmt: ../../dist/lib/plain.tex ../lib/local.tex
+ -initex plain \\input local \\dump
+
+install: # doit make install
+ mv initex virtex /usr/local/bin
+ rm /usr/local/bin/tex
+ ln /usr/local/bin/virtex /usr/local/bin/tex
+ make plain.fmt
+
+installtrip: # when the trip test is satisfactory
+ mv trip.tex tripin.log trip.log trip.fot trip.typ \
+ trip.dvi trip.tfm ../../dist/tex
+
+clean:
+ rm -f *.p *.o *~ *.tex triptex* *.dif trip.* tripin.log virtex.ch
+ @echo "Remaining files should be:"
+ @echo "Makefile ext.h initex.ch tex.pool"
+ @echo "dvitype.in ini_to_trip* plain.fmt trip1.in"
+ @echo "ext.c ini_to_vir* plain.log trip2.in"
+
diff --git a/systems/knuth/local/tex/dvitype.in b/systems/knuth/local/tex/dvitype.in
new file mode 100644
index 0000000000..9aae7baa4b
--- /dev/null
+++ b/systems/knuth/local/tex/dvitype.in
@@ -0,0 +1,5 @@
+2
+*.*.*.*.*.*.*.*.*.*
+1000000
+7227/100
+0
diff --git a/systems/knuth/local/tex/ext.c b/systems/knuth/local/tex/ext.c
new file mode 100644
index 0000000000..c0238a9b86
--- /dev/null
+++ b/systems/knuth/local/tex/ext.c
@@ -0,0 +1,228 @@
+/* external procedures called by TeX */
+#include <sys/signal.h>
+#include <sys/time.h>
+#include "texpaths.h" /* defines MAXINPATHCHARS, defaultinputpath, etc. */
+#include "GPCtypes.h"
+#include <string.h>
+
+#define max_file_name_length 1024
+
+void Argv(int n, char* str)
+{
+ register char *s,*t;
+ if (n<0 || n>=_p_argc) return;
+ s=_p_argv[n];
+ if (strlen(s) >= max_file_name_length-5) {
+ str[0]='\0'; return; /* TANGLE doesn't want it too long */
+ }
+ for (t=str; *s; s++,t++) *t=*s;
+ *t='\0';
+}
+
+void Flushstdout()
+{
+ fflush(stdout);
+}
+
+/* The following are for closing files */
+
+extern _p_close(FDR f); /* in GPC rts/file.c */
+
+Closea(FDR f)
+{
+ _p_close(f);
+}
+
+Closeb(FDR f)
+{
+ _p_close(f);
+}
+
+Closew(FDR f)
+{
+ _p_close(f);
+}
+
+/*
+** dateandtime(time, day, month, year)
+**
+** Stores minutes since midnight, current day, month and year into
+** *time, *day, *month and *year, respectively.
+**
+**
+*/
+
+Dateandtime(minutes, day, month, year)
+int *minutes, *day, *month, *year;
+{
+ long clock;
+ struct tm *tmptr, *localtime();
+
+ clock = time(0);
+ tmptr = localtime(&clock);
+
+ *minutes = tmptr->tm_hour * 60 + tmptr->tm_min;
+ *day = tmptr->tm_mday;
+ *month = tmptr->tm_mon + 1;
+ *year = tmptr->tm_year + 1900;
+}
+
+
+/* fixed arrays are used to hold the paths, to avoid any possible problems
+ involving interaction of malloc and undump */
+
+char inputpath[MAXINPATHCHARS] = defaultinputpath;
+char fontpath[MAXOTHPATHCHARS] = defaultfontpath;
+char formatpath[MAXOTHPATHCHARS] = defaultformatpath;
+char poolpath[MAXOTHPATHCHARS] = defaultpoolpath;
+
+extern char *getenv();
+
+/*
+ * setpaths is called to set up the arrays inputpath, fontpath, formatpath
+ * and poolpath as follows: if the user's environment has a value for the
+ * appropriate value, then use it; otherwise, leave the current value of
+ * the array (which may be the default path, or it may be the result of
+ * a call to setpaths on a previous run that was made the subject of
+ * an undump: this will give the maker of a preloaded TeX the option of
+ * providing a new set of "default" paths.
+ *
+ * Note that we have to copy the string from the environment area, since
+ * that will change on the next run (which matters if this is for a
+ * preloaded TeX).
+ */
+Setpaths()
+{
+ register char *envpath;
+
+ if ((envpath = getenv("TEXINPUTS")) != NULL)
+ copypath(inputpath,envpath,MAXINPATHCHARS);
+ if ((envpath = getenv("TEXFONTS")) != NULL)
+ copypath(fontpath,envpath,MAXOTHPATHCHARS);
+ if ((envpath = getenv("TEXFORMATS")) != NULL)
+ copypath(formatpath,envpath,MAXOTHPATHCHARS);
+ if ((envpath = getenv("TEXPOOL")) != NULL)
+ copypath(poolpath,envpath,MAXOTHPATHCHARS);
+}
+
+/*
+ * copypath(s1,s2,n) copies at most n characters (including the null)
+ * from string s2 to string s1, giving an error message for paths
+ * that are too long.
+ */
+copypath(s1,s2,n)
+ register char *s1,*s2;
+ register int n;
+{
+ while ((*s1++ = *s2++) != '\0')
+ if (--n == 0) {
+ fprintf(stderr, "! Environment search path is too big\n");
+ *--s1 = '\0';
+ return; /* let user continue with truncated path */
+ }
+}
+
+#define filenamesize 1024 /* should agree with initex.ch */
+#define nameoffile Nameoffile
+#define realnameoffile Realnameoffile
+char nameoffile[filenamesize],realnameoffile[filenamesize];
+
+/*
+ * testaccess(amode,filepath)
+ *
+ * Test whether or not the file whose name is in the global nameoffile
+ * can be opened for reading (if mode=READACCESS)
+ * or writing (if mode=WRITEACCESS).
+ *
+ * The filepath argument is one of the ...FILEPATH constants defined below.
+ * If the filename given in nameoffile does not begin with '/', we try
+ * prepending all the ':'-separated areanames in the appropriate path to the
+ * filename until access can be made, if it ever can.
+ *
+ * The realnameoffile global array will contain the name that yielded an
+ * access success.
+ */
+
+#define READACCESS 4
+#define WRITEACCESS 2
+
+#define NOFILEPATH 0
+#define INPUTFILEPATH 1
+#define READFILEPATH 2
+#define FONTFILEPATH 3
+#define FORMATFILEPATH 4
+#define POOLFILEPATH 5
+
+bool
+Testaccess(amode,filepath)
+ int amode,filepath;
+{
+ register bool ok;
+ register char *p;
+ char *curpathplace;
+ int f;
+
+ switch(filepath) {
+ case NOFILEPATH: curpathplace = NULL; break;
+ case INPUTFILEPATH: case READFILEPATH:
+ curpathplace = inputpath; break;
+ case FONTFILEPATH: curpathplace = fontpath; break;
+ case FORMATFILEPATH: curpathplace = formatpath; break;
+ case POOLFILEPATH: curpathplace = poolpath; break;
+ }
+ if (nameoffile[0]=='/') /* file name has absolute path */
+ curpathplace = NULL;
+ do {
+ packrealnameoffile(&curpathplace);
+ if (amode==READACCESS)
+ /* use system call "access" to see if we could read it */
+ if (access(realnameoffile,READACCESS)==0) ok = TRUE;
+ else ok = FALSE;
+ else {
+ /* WRITEACCESS: use creat to see if we could create it, but close
+ the file again if we're OK, to let pc open it for real */
+ f = creat(realnameoffile,0666);
+ if (f>=0) ok = TRUE;
+ else ok = FALSE;
+ if (ok)
+ close(f);
+ }
+ } while (!ok && curpathplace != NULL);
+ return (ok);
+}
+
+/*
+ * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp,
+ * followed by '/', followed by the characters in nameoffile up until the
+ * first blank there, and finally a '\0'. The cpp pointer is left pointing
+ * at the next directory in the path.
+ * But: if *cpp == NULL, then we are supposed to use nameoffile as is.
+ */
+packrealnameoffile(cpp)
+ char **cpp;
+{
+ register char *p,*realname;
+
+ realname = realnameoffile;
+ if ((p = *cpp)!=NULL) {
+ while ((*p != ':') && (*p != '\0')) {
+ *realname++ = *p++;
+ if (realname == &(realnameoffile[filenamesize-1]))
+ break;
+ }
+ if (*p == '\0') *cpp = NULL; /* at end of path now */
+ else *cpp = p+1; /* else get past ':' */
+ *realname++ = '/'; /* separate the area from the name to follow */
+ }
+ /* now append nameoffile to realname... */
+ p = nameoffile;
+ while (*p) {
+ if (realname >= &(realnameoffile[filenamesize-1])) {
+ fprintf(stderr,"! Full file name is too long\n");
+ break;
+ }
+ *realname++ = *p++;
+ }
+ *realname = '\0';
+}
+
diff --git a/systems/knuth/local/tex/ext.h b/systems/knuth/local/tex/ext.h
new file mode 100644
index 0000000000..d6914c4c67
--- /dev/null
+++ b/systems/knuth/local/tex/ext.h
@@ -0,0 +1,27 @@
+argc:asmname '_p_argc' Integer;
+
+procedure argv(no:Integer;var str:UNIXfilename); external;
+
+procedure flushstdout; external;
+
+procedure exit(x : integer);
+ external;
+
+procedure closea(var f:text);
+ external;
+
+procedure closeb(var f:bytefile);
+ external;
+
+procedure closew(var f:wordfile);
+ external;
+
+procedure dateandtime(var minutes, day, month, year : integer);
+ external;
+
+procedure setpaths;
+ external;
+
+function testaccess(accessmode:integer; filepath:integer): boolean;
+ external;
+
diff --git a/systems/knuth/local/tex/ini_to_trip b/systems/knuth/local/tex/ini_to_trip
new file mode 100644
index 0000000000..89ccab0759
--- /dev/null
+++ b/systems/knuth/local/tex/ini_to_trip
@@ -0,0 +1,41 @@
+#! /bin/csh
+# A shell script to perform modifications on the changefile for initex
+# to make a changefile for triptex (used to test tex).
+#
+# To use it, say
+# ini_to_trip initex.ch triptex.ch
+#
+ed - $1 <<edscriptend
+/debug=/
+//
+s/@{//p
+/gubed=/
+s/@t@>@}//p
+/@!mem_max=/
+//
+s/[0-9][0-9]*/3000/p
+/@!mem_min=/
+s/-[0-9]*/1/p
+/@!error_line=/
+s/[0-9][0-9]*/64/p
+/@!half_error_line=/
+s/[0-9][0-9]*/32/p
+/@!max_print_line=/
+s/[0-9][0-9]*/72/p
+/@!font_max=/
+s/[0-9][0-9]*/75/p
+/@!font_mem_size=/
+s/[0-9][0-9]*/20000/p
+/@!pool_size=/
+s/[0-9][0-9]*/32000/p
+/@d mem_bot=/
+//
+s/-[0-9]*/1/p
+/@d mem_top==/
+s/[0-9][0-9]*/3000/p
+/@d hash_size=/
+s/[0-9][0-9]*/2100/p
+/@d hash_prime=/
+s/[0-9][0-9]*/1777/p
+w $2
+edscriptend
diff --git a/systems/knuth/local/tex/ini_to_vir b/systems/knuth/local/tex/ini_to_vir
new file mode 100644
index 0000000000..2120a4beea
--- /dev/null
+++ b/systems/knuth/local/tex/ini_to_vir
@@ -0,0 +1,23 @@
+#! /bin/csh
+# A shell script to perform modifications on the changefile for initex
+# to make a changefile for virtex. This means changing constants and
+# debug..gubed, init..tini, stat..tats.
+#
+# To use it, say
+# ini_to_vir initex.ch virtex.ch
+#
+ed - $1 <<edscriptend
+/@d init==/
+//
+s//&@{/p
+/@d tini==/
+s//&@t@>@}/p
+/@!mem_max=/
+//
+s/[0-9][0-9]*/32766/p
+/@!max_strings=/
+s/[0-9][0-9]*/6000/p
+/@!trie_size=/
+s/[0-9][0-9]*/6500/p
+w $2
+edscriptend
diff --git a/systems/knuth/local/tex/initex.ch b/systems/knuth/local/tex/initex.ch
new file mode 100644
index 0000000000..b4024f6f2f
--- /dev/null
+++ b/systems/knuth/local/tex/initex.ch
@@ -0,0 +1,982 @@
+% Change file for GNU Pascal and Linux
+% (by Don Knuth, 2000; see ../tex-sparc for the prehistory)
+% (I'm no longer keeping the "efficiency" changes; just enuf for TRIP test)
+%
+% Use this file as is to make an INITEX. To get triptex, use the
+% shell script ``ini_to_trip'' and re-TANGLE.
+
+% History:
+% 2000.04.30 first sketch --- untested --- see FIXTHIS for parts not done yet
+
+% NOTE: the module numbers in this change file refer to Volume B.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: only print changes
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+@y
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+\let\maybe=\iffalse
+\def\title{\TeX82 changes for GNU Pascal}
+\def\glob{13}\def\gglob{20, 26} % these are defined in module 1
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.2] banner line
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is TeX, Version 3.14159' {printed when \TeX\ starts}
+@y
+@d banner=='This is TeX, Version 3.14159 for Linux' {printed when \TeX\ starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.4] program header
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+Actually the heading shown here is not quite normal: The |program| line
+does not mention any |output| file, because \ph\ would ask the \TeX\ user
+to specify a file name if |output| were specified here.
+@^system dependencies@>
+
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@y
+@d mtype==t@&y@&p@&e {this is a \.{WEB} coding trick:}
+@d standard_input==i@&n@&p@&u@&t {and another}
+@z
+@x
+program TEX; {all file names are defined dynamically}
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+mtype @<Types in the outer block@>@/
+var @<Global variables@>@/
+@y
+program TEX(standard_input,output); {other file names are defined dynamically}
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+mtype @!UNIX_filename = packed array[1..file_name_size] of char;@/
+@<Types in the outer block@>@/
+var @<Global variables@>@/
+@#
+@\@=#include "ext.h"@>@\ {declarations for external C procedures}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.7] debug..gubed, stat..tats
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
+@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
+@y
+@d debug==@{ {the trip test will use debugging}
+@d gubed==@t@>@}
+@z
+@x
+@d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering
+ usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering
+ usage statistics}
+@y
+@d stat==
+@d tats==
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.8] init..tini
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
+@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
+@y
+@d init==
+@d tini==
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.10] othercases, feature of ISO Extended Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.11] compile-time constants
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!mem_max=30000; {greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|}
+@!mem_min=0; {smallest index in \TeX's internal |mem| array;
+ must be |min_halfword| or more;
+ must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|}
+@!buf_size=500; {maximum number of characters simultaneously present in
+ current lines of open files and in control sequences between
+ \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
+@!error_line=72; {width of context lines on terminal error messages}
+@!half_error_line=42; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!stack_size=200; {maximum number of simultaneous input sources}
+@!max_in_open=6; {maximum number of input files and error insertions that
+ can be going on simultaneously}
+@!font_max=75; {maximum internal font number; must not exceed |max_quarterword|
+ and must be at most |font_base+256|}
+@!font_mem_size=20000; {number of words of |font_info| for all fonts}
+@!param_size=60; {maximum number of simultaneous macro parameters}
+@!nest_size=40; {maximum number of semantic levels simultaneously active}
+@!max_strings=3000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=8000; {the minimum number of characters that should be
+ available for the user's control sequences and font names,
+ after \TeX's own error messages are stored}
+@!pool_size=32000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all fonts and
+ control sequences; must exceed |string_vacancies| by the total
+ length of \TeX's own strings, which is currently about 23000}
+@!save_size=600; {space for saving values outside of current group; must be
+ at most |max_halfword|}
+@!trie_size=8000; {space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX}
+@!trie_op_size=500; {space for ``opcodes'' in the hyphenation patterns}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!file_name_size=40; {file names shouldn't be longer than this}
+@!pool_name='TeXformats:TEX.POOL ';
+ {string of length |file_name_size|; tells where the string pool appears}
+@y
+@!mem_max=30000; {greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|}
+@!mem_min=-32767; {smallest index in \TeX's internal |mem| array;
+ must be |min_halfword| or more;
+ must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|}
+@!buf_size=5000; {maximum number of characters simultaneously present in
+ current lines of open files and in control sequences between
+ \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
+@!error_line=79; {width of context lines on terminal error messages}
+@!half_error_line=50; {width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|}
+@!max_print_line=79; {width of longest text lines output; should be at least 60}
+@!stack_size=200; {maximum number of simultaneous input sources}
+@!max_in_open=15; {maximum number of input files and error insertions that
+ can be going on simultaneously}
+@!font_max=127; {maximum internal font number; must not exceed |max_quarterword|
+ and must be at most |font_base+256|}
+@!font_mem_size=40000; {number of words of |font_info| for all fonts}
+@!param_size=60; {maximum number of simultaneous macro parameters}
+@!nest_size=40; {maximum number of semantic levels simultaneously active}
+@!max_strings=3000; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=8000; {the minimum number of characters that should be
+ available for the user's control sequences and font names,
+ after \TeX's own error messages are stored}
+@!pool_size=60000; {maximum number of characters in strings, including all
+ error messages and help texts, and the names of all fonts and
+ control sequences; must exceed |string_vacancies| by the total
+ length of \TeX's own strings, which is currently about 23000}
+@!save_size=600; {space for saving values outside of current group; must be
+ at most |max_halfword|}
+@!trie_size=8000; {space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX}
+@!trie_op_size=500; {space for ``opcodes'' in the hyphenation patterns}
+@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
+@!file_name_size=1024; {file names shouldn't be longer than this}
+@!pool_name='tex.pool';
+ {string of length |file_name_size|; the string pool name}
+@.TeXformats@>
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1.12] sensitive compile-time constants
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d mem_bot=0 {smallest index in the |mem| array dumped by \.{INITEX};
+ must not be less than |mem_min|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INITEX};
+ must be substantially larger than |mem_bot|
+ and not greater than |mem_max|}
+@d font_base=0 {smallest internal font number; must not be less
+ than |min_quarterword|}
+@d hash_size=2100 {maximum number of control sequences; it should be at most
+ about |(mem_max-mem_min)/10|}
+@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
+@d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions}
+@y
+@d mem_bot=-32767 {smallest index in the |mem| array dumped by \.{INITEX};
+ must not be less than |mem_min|}
+@d mem_top==30000 {largest index in the |mem| array dumped by \.{INITEX};
+ must be substantially larger than |mem_bot|
+ and not greater than |mem_max|}
+@d font_base=0 {smallest internal font number; must not be less
+ than |min_quarterword|}
+@d hash_size=4000 {maximum number of control sequences; it should be at most
+ about |(mem_max-mem_min)/10|}
+@d hash_prime=3443 {a prime number equal to about 85\pct! of |hash_size|}
+@d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2.23] allow any character that we can input to get in
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+@y
+for i:=0 to @'37 do xchr[i]:=chr(i);
+for i:=@'177 to @'377 do xchr[i]:=chr(i);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.25] file types
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!alpha_file=packed file of text_char; {files that contain textual data}
+@y
+@!eight_bits=ByteCard; {unsigned one-byte quantity}
+@!alpha_file=t@&e@&x@&t; {files that contain textual data}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.26] add real_name_of_file array
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \TeX\ can open a file whose external name is specified by
+|name_of_file|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file:packed array[1..file_name_size] of char;@;@/
+ {on some systems this may be a \&{record} variable}
+@y
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \TeX\ can open a file whose external name is specified by
+|name_of_file|.
+The Linux version uses search paths to look for files to open.
+We use |real_name_of_file| to hold the |name_of_file| with a directory
+name from the path in front of it.
+@^system dependencies@>
+
+@<Glob...@>=
+@!name_of_file,@!real_name_of_file:external UNIX_filename;
+@;@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.27] file opening
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The \ph\ compiler with which the present version of \TeX\ was prepared has
+extended the rules of \PASCAL\ in a very convenient way. To open file~|f|,
+we can write
+$$\vbox{\halign{#\hfil\qquad&#\hfil\cr
+|reset(f,@t\\{name}@>,'/O')|&for input;\cr
+|rewrite(f,@t\\{name}@>,'/O')|&for output.\cr}}$$
+The `\\{name}' parameter, which is of type `{\bf packed array
+$[\langle\\{any}\rangle]$ of \\{char}}', stands for the name of
+the external file that is being opened for input or output.
+Blank spaces that might appear in \\{name} are ignored.
+
+The `\.{/O}' parameter tells the operating system not to issue its own
+error messages if something goes wrong. If a file of the specified name
+cannot be found, or if such a file cannot be opened for some other reason
+(e.g., someone may already be trying to write the same file), we will have
+|@!erstat(f)<>0| after an unsuccessful |reset| or |rewrite|. This allows
+\TeX\ to undertake appropriate corrective action.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+\TeX's file-opening procedures return |false| if no file identified by
+|name_of_file| could be opened.
+
+@d reset_OK(#)==erstat(#)=0
+@d rewrite_OK(#)==erstat(#)=0
+
+@p function a_open_in(var f:alpha_file):boolean;
+ {open a text file for input}
+begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f);
+end;
+@#
+function a_open_out(var f:alpha_file):boolean;
+ {open a text file for output}
+begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f);
+end;
+@#
+function b_open_in(var f:byte_file):boolean;
+ {open a binary file for input}
+begin reset(f,name_of_file,'/O'); b_open_in:=reset_OK(f);
+end;
+@#
+function b_open_out(var f:byte_file):boolean;
+ {open a binary file for output}
+begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f);
+end;
+@#
+function w_open_in(var f:word_file):boolean;
+ {open a word file for input}
+begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f);
+end;
+@#
+function w_open_out(var f:word_file):boolean;
+ {open a word file for output}
+begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f);
+end;
+@y
+@ An external C procedure, |test_access| is used to check whether or not the
+open will work. It is declared in the ``ext.h'' include file, and it returns
+|true| or |false|. The |name_of_file| global holds the file name whose access
+is to be tested.
+The first parameter for |test_access| is the access mode,
+one of |read_access_mode| or |write_access_mode|.
+
+We also implement path searching in |test_access|: its second parameter is
+one of the ``file path'' constants defined below. If |name_of_file|
+doesn't start with |'/'| then |test_access| tries prepending pathnames
+from the appropriate path list until success or the end of path list
+is reached.
+On return, |real_name_of_file| contains the original name with the path
+that succeeded (if any) prepended. It is the name used in the various
+open procedures.
+
+Note that |a_open_in| has been redefined to take an additional argument,
+which should be one of the ``file path'' specifiers.
+Since |b_open_in| is only used for \.{TFM} files, and
+|w_open_in| is only used for format files, we don't need a path specifying
+argument for them.
+Path searching is not done for output files.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {no path searching should be done}
+@d input_file_path=1 {path specifier for \.{\\input} files}
+@d read_file_path=2 {path specifier for \.{\\read} files}
+@d font_file_path=3 {path specifier for \.{TFM} files}
+@d format_file_path=4 {path specifier for format files}
+@d pool_file_path=5 {path specifier for the pool file}
+
+@p function a_open_in(var f:alpha_file;@!path_specifier:integer):boolean;
+ {open a text file for input}
+var @!ok:boolean;
+begin
+if test_access(read_access_mode,path_specifier) then
+ begin reset(f,real_name_of_file); ok:=true@+end
+else
+ ok:=false;
+a_open_in:=ok;
+end;
+@#
+function a_open_out(var f:alpha_file):boolean;
+ {open a text file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+a_open_out:=ok;
+end;
+@#
+function b_open_in(var f:byte_file):boolean;
+ {open a binary file for input}
+var @!ok:boolean;
+begin
+if test_access(read_access_mode,font_file_path) then
+ begin reset(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+b_open_in:=ok;
+end;
+@#
+function b_open_out(var f:byte_file):boolean;
+ {open a binary file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+b_open_out:=ok;
+end;
+@#
+function w_open_in(var f:word_file):boolean;
+ {open a word file for input}
+var @!ok:boolean;
+begin
+if test_access(read_access_mode,format_file_path) then
+ begin reset(f,real_name_of_file); ok:=true @+end
+else ok:=false;
+w_open_in:=ok;
+end;
+@#
+function w_open_out(var f:word_file):boolean;
+ {open a word file for output}
+var @!ok:boolean;
+begin
+if test_access(write_access_mode,no_file_path) then
+ begin rewrite(f,name_of_file); ok:=true @+end
+else ok:=false;
+w_open_out:=ok;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.28] file closing
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+These procedures should not generate error messages if a file is
+being closed before it has been successfully opened.
+
+@p procedure a_close(var f:alpha_file); {close a text file}
+begin close(f);
+end;
+@#
+procedure b_close(var f:byte_file); {close a binary file}
+begin close(f);
+end;
+@#
+procedure w_close(var f:word_file); {close a word file}
+begin close(f);
+end;
+@y
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+With the pc library, files will be automatically closed when the program stops
+and when one reopens them.
+There is, however, the problem of opening a file and writing it, and then
+wanting to read it again, but from a different file variable.
+For this purpose we use external procedures |close_a|, |close_b|, and
+|close_w|.
+
+@p procedure a_close(var f:alpha_file); {close a text file}
+begin close_a(f);
+end;
+@#
+procedure b_close(var f:byte_file); {close a binary file}
+begin close_b(f);
+end;
+@#
+procedure w_close(var f:word_file); {close a word file}
+begin close_w(f);
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.32] term_in/out are input,output
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file variable is called |term_in|, and when it
+is considered an output file the file variable is |term_out|.
+@^system dependencies@>
+
+@<Glob...@>=
+@!term_in:alpha_file; {the terminal as an input file}
+@!term_out:alpha_file; {the terminal as an output file}
+@y
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file is called |term_in|, and when it
+is considered an output file the file is |term_out|.
+For Berkeley {\mc UNIX} we use |standard_input| and |output| for these
+files, as were declared in the program header. The former name
+|standard_input| was defined using a .{WEB} trick so that `input' is
+produced in the Pascal file.
+
+@d term_in==standard_input {the terminal as an input file}
+@d term_out==output {the terminal as an output file}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.33] don't need to open terminal files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Here is how to open the terminal files
+in \ph. The `\.{/I}' switch suppresses the first |get|.
+@^system dependencies@>
+
+@d t_open_in==reset(term_in,'TTY:','/O/I') {open the terminal for text input}
+@d t_open_out==rewrite(term_out,'TTY:','/O') {open the terminal for text output}
+@y
+@ Here is how to open the terminal files---do nothing.
+
+@d t_open_in == {input already open for text input}
+@d t_open_out == {output already open for text output}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3.34] flushing output
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+these operations can be specified in \ph:
+@^system dependencies@>
+
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@d clear_terminal == break_in(term_in,true) {clear the terminal input buffer}
+@d wake_up_terminal == do_nothing {cancel the user's cancellation of output}
+@y
+these operations can be specified with Berkeley {\mc UNIX}:
+@^system dependencies@>
+
+@d update_terminal == flush_stdout
+@d clear_terminal == {nothing necessary on UNIX}
+@d wake_up_terminal == {nothing necessary on UNIX}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4.38] string data
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!packed_ASCII_code = 0..255; {elements of |str_pool| array}
+@y
+@!packed_ASCII_code = ByteCard; {elements of |str_pool| array}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4.51] a_open_in of pool file needs path specifier
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if a_open_in(pool_file) then
+@y
+if a_open_in(pool_file,pool_file_path) then
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4.51,52,53] make TEX.POOL lowercase in messages
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+else bad_pool('! I can''t read TEX.POOL.')
+@y
+else bad_pool('! I can''t read tex.pool.')
+@z
+@x
+begin if eof(pool_file) then bad_pool('! TEX.POOL has no check sum.');
+@y
+begin if eof(pool_file) then bad_pool('! tex.pool has no check sum.');
+@z
+@x
+ bad_pool('! TEX.POOL line doesn''t begin with two digits.');
+@y
+ bad_pool('! tex.pool line doesn''t begin with two digits.');
+@z
+@x
+ bad_pool('! TEX.POOL check sum doesn''t have nine digits.');
+@y
+ bad_pool('! tex.pool check sum doesn''t have nine digits.');
+@z
+@x
+done: if a<>@$ then bad_pool('! TEX.POOL doesn''t match; TANGLE me again.');
+@y
+done: if a<>@$ then bad_pool('! tex.pool doesn''t match; tangle me again.');
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7.109] glue_ratio
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!glue_ratio=real; {one-word representation of a glue expansion factor}
+@y
+@!glue_ratio=ShortReal; {one-word representation of a glue expansion factor}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8.110] ranges for quarter,half words FIXTHIS
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if the subrange is `|-128..127|'.
+
+The present implementation tries to accommodate as many variations as possible,
+so it makes few assumptions. If integers having the subrange
+`|min_quarterword..max_quarterword|' can be packed into a quarterword,
+and if integers having the subrange `|min_halfword..max_halfword|'
+can be packed into a halfword, everything should work satisfactorily.
+
+It is usually most efficient to have |min_quarterword=min_halfword=0|,
+so one should try to achieve this unless it causes a severe problem.
+The values defined here are recommended for most 32-bit computers.
+
+@d min_quarterword=0 {smallest allowable value in a |quarterword|}
+@d max_quarterword=255 {largest allowable value in a |quarterword|}
+@d min_halfword==0 {smallest allowable value in a |halfword|}
+@d max_halfword==65535 {largest allowable value in a |halfword|}
+@y
+if the subrange is `|-128..127|'.
+
+For Berkeley {\mc UNIX} (SunOS) we need to do the |-128..127| kind of range.
+
+@d min_quarterword=-128 {smallest allowable value in a |quarterword|}
+@d max_quarterword=127 {largest allowable value in a |quarterword|}
+@d min_halfword==-32768 {smallest allowable value in a |halfword|}
+@d max_halfword==32767 {largest allowable value in a |halfword|}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [11.165] fix the word "free" so that it doesn't conflict with a runtime proc
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+@y
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+
+@d free==free_arr
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [12.186] glue_ratio fix FIXTHIS
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+floating point underflow on the author's computer.
+@^system dependencies@>
+@^dirty \PASCAL@>
+
+@<Display the value of |glue_set(p)|@>=
+g:=float(glue_set(p));
+if (g<>float_constant(0))and(glue_sign(p)<>normal) then
+ begin print(", glue set ");
+ if glue_sign(p)=shrinking then print("- ");
+ if abs(mem[p+glue_offset].int)<@'4000000 then print("?.?")
+ else if abs(g)>float_constant(20000) then
+ begin if g>float_constant(0) then print_char(">")
+ else print("< -");
+ print_glue(20000*unity,glue_order(p),0);
+ end
+ else print_glue(round(unity*g),glue_order(p),0);
+@^real multiplication@>
+ end
+@y
+floating point underflow on the author's computer.
+For the {\mc VAX}, the only possible random value that could hurt is
+a reserved value with 1 in the sign bit and 0 for the (excess) exponent.
+Because the sign-plus-exponent is in the middle of the word, the chances
+of this happening are miniscule, and ignored here.
+@^system dependencies@>
+@^dirty \PASCAL@>
+
+@<Display the value of |glue_set(p)|@>=
+g:=float(glue_set(p));
+if (g<>float_constant(0))and(glue_sign(p)<>normal) then
+ begin print(", glue set ");
+ if glue_sign(p)=shrinking then print("- ");
+ if abs(float(glue_set(p)))>20000.0 then
+ begin if float(glue_set(p))>0 then print_char(">")
+ else print("< -");
+ print_glue(20000*unity,glue_order(p),0);
+ end
+ else print_glue(round(float(glue_set(p))*unity),glue_order(p),0);
+@^real multiplication@>
+ end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [17.241] fix_date_and_time
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following procedure, which is called just before \TeX\ initializes its
+input and output, establishes the initial values of the date and time.
+@^system dependencies@>
+Since standard \PASCAL\ cannot provide such information, something special
+is needed. The program here simply specifies July 4, 1776, at noon; but
+users probably want a better approximation to the truth.
+
+@p procedure fix_date_and_time;
+begin time:=12*60; {minutes since midnight}
+day:=4; {fourth day of the month}
+month:=7; {seventh month of the year}
+year:=1776; {Anno Domini}
+end;
+@y
+@ The following procedure, which is called just before \TeX\ initializes its
+input and output, establishes the initial values of the date and time.
+It is calls an externally defined |date_and_time|, even though it could
+be done from Pascal.
+The external procedure also sets up interrupt catching.
+@^system dependencies@>
+
+@p procedure fix_date_and_time;
+begin
+ date_and_time(time,day,month,year);
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.513] area and extension rules
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
+@!ext_delimiter:pool_pointer; {the relevant `\..', if any}
+@y
+@ The file names we shall deal with for SunOS have the
+following structure: If the name contains `\./', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep
+track of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+@!area_delimiter:pool_pointer; {the most recent `\./', if any}
+@!ext_delimiter:pool_pointer; {the most recent `\..', if any}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.514] TeX area directories
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d TEX_area=="TeXinputs:"
+@.TeXinputs@>
+@d TEX_font_area=="TeXfonts:"
+@.TeXfonts@>
+@y
+Under Linux, the default paths are specified in a separate
+file, `\.{texpaths.h}'. The file opening procedures do path searching
+based either on those default paths, or on paths given by the user
+in ``environment'' variables.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.516] more_name
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ if (c=">")or(c=":") then
+@y
+ if c="/" then
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.520] default format
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d format_default_length=20 {length of the |TEX_format_default| string}
+@d format_area_length=11 {length of its area part}
+@d format_ext_length=4 {length of its `\.{.fmt}' part}
+@y
+Under Linux we don't give the area part, instead depending
+on the path searching that will happen during file opening.
+
+@d format_default_length=9 {length of the |TEX_format_default| string}
+@d format_area_length=0 {length of its area part}
+@d format_ext_length=4 {length of its `\.{.fmt}' part}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.521] plain format location
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+TEX_format_default:='TeXformats:plain.fmt';
+@y
+TEX_format_default:='plain.fmt';
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.524] format file opening: only try once, with path search
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(fmt_file) then goto found;
+ pack_buffered_name(format_area_length,loc,j-1);
+ {now try the system format file area}
+ if w_open_in(fmt_file) then goto found;
+@y
+ pack_buffered_name(0,loc,j-1);
+ if w_open_in(fmt_file) then goto found;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.525] make_name_string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+which simply makes a \TeX\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var k:1..file_name_size; {index into |name_of_file|}
+begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings)or
+ (cur_length>0) then
+ make_name_string:="?"
+else begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
+ make_name_string:=make_string;
+ end;
+end;
+@y
+which simply makes a \TeX\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+With the Berkeley {\mc UNIX} version, we know that |real_name_of_file|
+contains |name_of_file| prepended with the directory name that was found
+by path searching.
+If |real_name_of_file| starts with |'./'|, we don't use that part of the
+name, since {\mc UNIX} users understand that.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p function make_name_string:str_number;
+var k,@!kstart:1..file_name_size; {index into |name_of_file|}
+begin
+k:=1;
+while (k<file_name_size) and (xord[real_name_of_file[k]]<>" ") do
+ incr(k);
+name_length:=k-1; {the real |name_length|}
+if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings)or
+ (cur_length>0) then
+ make_name_string:="?"
+else begin
+ if (xord[real_name_of_file[1]]=".") and (xord[real_name_of_file[2]]="/") then
+ kstart:=3
+ else
+ kstart:=1;
+ for k:=kstart to name_length do append_char(xord[real_name_of_file[k]]);
+ make_name_string:=make_string;
+ end
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [29.537] a_open_in of \input file needs path selector
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ if a_open_in(cur_file) then goto done;
+ if cur_area="" then
+ begin pack_file_name(cur_name,TEX_area,cur_ext);
+ if a_open_in(cur_file) then goto done;
+ end;
+@y
+ if a_open_in(cur_file,input_file_path) then goto done;
+ if cur_ext=".tex" then
+ begin pack_file_name(cur_name,cur_area,"");
+ if a_open_in(cur_file,input_file_path) then goto done;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [30.563] opening tfm file: now path searching is done
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% Set temp_int to value of first byte
+@x
+@ @<Open |tfm_file| for input@>=
+file_opened:=false;
+if aire="" then pack_file_name(nom,TEX_font_area,".tfm")
+else pack_file_name(nom,aire,".tfm");
+if not b_open_in(tfm_file) then abort;
+file_opened:=true
+@y
+@ Here we have to ``prime the pump'' for the |temp_int| trick explained below.
+
+ @<Open |tfm_file| for input@>=
+file_opened:=false;
+pack_file_name(nom,aire,".tfm");
+if not b_open_in(tfm_file) then abort;
+begin temp_int:=tfm_file^;
+ if temp_int<0 then temp_int:=temp_int+256;
+end;
+file_opened:=true
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [49.1275] a_open_in of \read file needs path specifier
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ if a_open_in(read_file[n]) then read_open[n]:=just_open;
+@y
+ if a_open_in(read_file[n],read_file_path) then read_open[n]:=just_open;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [51.1332] Delete ready_already; also add call to set_paths;
+% also add call to exit() depending upon value of `history'
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Now this is really it: \TeX\ starts and ends here.
+
+The initial test involving |ready_already| should be deleted if the
+\PASCAL\ runtime system is smart enough to detect such a ``mistake.''
+@y
+@ Now this is really it: \TeX\ starts and ends here.
+
+At the end of the job, we look at |history| to decide the correct
+exit code. We use 1 if |history <= warning_issued| and 0 otherwise.
+
+@d UNIXexit==e@&x@&i@&t
+@z
+
+@x
+t_open_out; {open the terminal for output}
+if ready_already=314159 then goto start_of_TEX;
+@y
+t_open_out; {open the terminal for output}
+set_paths;
+@z
+
+@x
+start_of_TEX: @<Initialize the output routines@>;
+@y
+start_of_TEX: argv(0,my_name); FIXTHIS: DELETE MY_NAME!
+if (my_name<>'initex')and(my_name<>'triptex') then
+ begin if my_name = 'tex' then name_of_file:='plain.fmt'
+ else name_of_file:=my_name+'.fmt';
+ if w_open_in(fmt_file) then
+ if not load_fmt_file then
+ begin w_close(fmt_file); goto final_end;
+ end
+ else w_close(fmt_file);
+ end;
+@<Initialize the output routines@>;
+@z
+
+@x
+final_end: ready_already:=0;
+@y
+final_end: ready_already:=0;
+if (history <= warning_issued) then
+ UNIXexit(0)
+else
+ UNIXexit(1);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [51.1333] print new line before termination; switch to editor if nec.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+@y
+ slow_print(log_name); print_char(".");
+ end;
+ end;
+print_ln;
+if (ed_name_start<>0) and (interaction>batch_mode) then
+ calledit(str_pool[ed_name_start],ed_name_length,edit_line);
+@z
+
diff --git a/systems/knuth/local/tex/plain.fmt b/systems/knuth/local/tex/plain.fmt
new file mode 100644
index 0000000000..f9c7455f65
--- /dev/null
+++ b/systems/knuth/local/tex/plain.fmt
Binary files differ
diff --git a/systems/knuth/local/tex/plain.log b/systems/knuth/local/tex/plain.log
new file mode 100644
index 0000000000..6a7f9f18a3
--- /dev/null
+++ b/systems/knuth/local/tex/plain.log
@@ -0,0 +1,103 @@
+This is TeX, Version 3.14159 for SunOS (INITEX) 14 AUG 1998 08:06
+**plain \input local \dump
+(/home/tex/dist/lib/plain.tex Preloading the plain format: codes, registers,
+\maxdimen=\dimen10
+\hideskip=\skip10
+\centering=\skip11
+\p@=\dimen11
+\z@=\dimen12
+\z@skip=\skip12
+\voidb@x=\box10
+
+parameters,
+\smallskipamount=\skip13
+\medskipamount=\skip14
+\bigskipamount=\skip15
+\normalbaselineskip=\skip16
+\normallineskip=\skip17
+\normallineskiplimit=\dimen13
+\jot=\dimen14
+\interdisplaylinepenalty=\count23
+\interfootnotelinepenalty=\count24
+ fonts, more fonts,
+\itfam=\fam4
+\slfam=\fam5
+\bffam=\fam6
+\ttfam=\fam7
+ macros,
+\strutbox=\box11
+\mscount=\count25
+\tabs=\box12
+\tabsyet=\box13
+\tabsdone=\box14
+ math definitions,
+\rootbox=\box15
+\p@renwd=\dimen15
+ output routines,
+\headline=\toks10
+\footline=\toks11
+\footins=\insert254
+\topins=\insert253
+
+hyphenation (/home/tex/dist/lib/hyphen.tex)) (/home/tex/local/lib/local.tex)
+Beginning to dump on file plain.fmt
+ (preloaded format=plain 98.8.14)
+1968 strings of total length 27846
+5013 memory locations dumped; current usage is 110&4900
+926 multiletter control sequences
+\font\nullfont=nullfont
+\font\tenrm=cmr10
+\font\preloaded=cmr9
+\font\preloaded=cmr8
+\font\sevenrm=cmr7
+\font\preloaded=cmr6
+\font\fiverm=cmr5
+\font\teni=cmmi10
+\font\preloaded=cmmi9
+\font\preloaded=cmmi8
+\font\seveni=cmmi7
+\font\preloaded=cmmi6
+\font\fivei=cmmi5
+\font\tensy=cmsy10
+\font\preloaded=cmsy9
+\font\preloaded=cmsy8
+\font\sevensy=cmsy7
+\font\preloaded=cmsy6
+\font\fivesy=cmsy5
+\font\tenex=cmex10
+\font\preloaded=cmss10
+\font\preloaded=cmssq8
+\font\preloaded=cmssi10
+\font\preloaded=cmssqi8
+\font\tenbf=cmbx10
+\font\preloaded=cmbx9
+\font\preloaded=cmbx8
+\font\sevenbf=cmbx7
+\font\preloaded=cmbx6
+\font\fivebf=cmbx5
+\font\tentt=cmtt10
+\font\preloaded=cmtt9
+\font\preloaded=cmtt8
+\font\preloaded=cmsltt10
+\font\tensl=cmsl10
+\font\preloaded=cmsl9
+\font\preloaded=cmsl8
+\font\tenit=cmti10
+\font\preloaded=cmti9
+\font\preloaded=cmti8
+\font\preloaded=cmti7
+\font\preloaded=cmu10
+\font\preloaded=cmmib10
+\font\preloaded=cmbsy10
+\font\preloaded=cmcsc10
+\font\preloaded=cmssbx10
+\font\preloaded=cmdunh10
+\font\preloaded=cmr7 at 14.51799pt
+\font\preloaded=cmtt10 at 14.4pt
+\font\preloaded=cmssbx10 at 14.4pt
+\font\preloaded=manfnt
+14787 words of font info for 50 preloaded fonts
+14 hyphenation exceptions
+Hyphenation trie of length 6075 has 181 ops out of 500
+ 181 for language 0
+No pages of output.
diff --git a/systems/knuth/local/tex/tex.pool b/systems/knuth/local/tex/tex.pool
new file mode 100644
index 0000000000..c1358a0a41
--- /dev/null
+++ b/systems/knuth/local/tex/tex.pool
@@ -0,0 +1,1040 @@
+11buffer size
+09pool size
+17number of strings
+03???
+13m2d5c2l5x2v5i
+28End of file on the terminal!
+02!
+42(That makes 100 errors; please try again.)
+02?
+60Type <return> to proceed, S to scroll future error messages,
+44R to run without stopping, Q to run quietly,
+23I to insert something,
+20E to edit your file,
+561 or ... or 9 to ignore the next 1 to 9 tokens of input,
+22H for help, X to quit.
+13OK, entering
+09batchmode
+11nonstopmode
+10scrollmode
+03...
+07insert>
+44I have just deleted some text, as you asked.
+48You can now delete more, or insert, or whatever.
+50Sorry, I don't know how to help in this situation.
+36Maybe you should try asking a human?
+42Sorry, I already gave what help I could...
+59An error might have occurred before I noticed any problems.
+45``If all else fails, read the instructions.''
+02 (
+14Emergency stop
+30TeX capacity exceeded, sorry [
+44If you really absolutely need more capacity,
+35you can ask a wizard to enlarge me.
+19This can't happen (
+59I'm broken. Please show this to someone who can fix can fix
+35I can't go on meeting you like this
+55One of your faux pas seems to have wounded me deeply...
+59in fact, I'm barely conscious. Please fix it and try again.
+12Interruption
+09You rang?
+60Try to insert some instructions for me (e.g.,`I\showlists'),
+43unless you just want to quit by typing `X'.
+16main memory size
+24AVAIL list clobbered at
+31Double-AVAIL list clobbered at
+24Doubly free location at
+12Bad flag at
+14New busy locs:
+05LINK(
+05INFO(
+02[]
+10CLOBBERED.
+04foul
+03fil
+06 plus
+07 minus
+03 []
+26Bad link, display aborted.
+04etc.
+18Unknown node type!
+05unset
+04box(
+02)x
+10, shifted
+09 columns)
+10, stretch
+09, shrink
+11, glue set
+02-
+03< -
+05rule(
+06insert
+15, natural size
+08; split(
+14); float cost
+04glue
+09nonscript
+05mskip
+02mu
+00
+08leaders
+04kern
+13 (for accent)
+05mkern
+04math
+02on
+03off
+13, surrounded
+11 (ligature
+08penalty
+13discretionary
+11 replacing
+04mark
+07vadjust
+08flushing
+07copying
+08vertical
+10horizontal
+12display math
+02no
+17internal vertical
+21restricted horizontal
+05 mode
+18semantic nest size
+04###
+17 entered at line
+10 (language
+10:hyphenmin
+18 (\output routine)
+25### recent contributions:
+10prevdepth
+07ignored
+11, prevgraf
+05 line
+12spacefactor
+19, current language
+28this will be denominator of:
+08lineskip
+12baselineskip
+07parskip
+16abovedisplayskip
+16belowdisplayskip
+21abovedisplayshortskip
+21belowdisplayshortskip
+08leftskip
+09rightskip
+07topskip
+12splittopskip
+07tabskip
+09spaceskip
+10xspaceskip
+11parfillskip
+10thinmuskip
+09medmuskip
+11thickmuskip
+25[unknown glue parameter!]
+04skip
+06muskip
+02pt
+06output
+08everypar
+09everymath
+12everydisplay
+09everyhbox
+09everyvbox
+08everyjob
+07everycr
+07errhelp
+04toks
+08parshape
+03box
+04void
+12current font
+08textfont
+10scriptfont
+16scriptscriptfont
+07catcode
+06lccode
+06uccode
+06sfcode
+08mathcode
+12pretolerance
+09tolerance
+11linepenalty
+13hyphenpenalty
+15exhyphenpenalty
+11clubpenalty
+12widowpenalty
+19displaywidowpenalty
+13brokenpenalty
+12binoppenalty
+10relpenalty
+17predisplaypenalty
+18postdisplaypenalty
+16interlinepenalty
+20doublehyphendemerits
+19finalhyphendemerits
+11adjdemerits
+03mag
+15delimiterfactor
+09looseness
+04time
+03day
+05month
+04year
+14showboxbreadth
+12showboxdepth
+08hbadness
+08vbadness
+07pausing
+13tracingonline
+13tracingmacros
+12tracingstats
+17tracingparagraphs
+12tracingpages
+13tracingoutput
+16tracinglostchars
+15tracingcommands
+15tracingrestores
+06uchyph
+13outputpenalty
+13maxdeadcycles
+09hangafter
+15floatingpenalty
+10globaldefs
+03fam
+10escapechar
+17defaulthyphenchar
+15defaultskewchar
+11endlinechar
+11newlinechar
+08language
+13lefthyphenmin
+14righthyphenmin
+14holdinginserts
+17errorcontextlines
+28[unknown integer parameter!]
+05count
+07delcode
+09parindent
+12mathsurround
+13lineskiplimit
+05hsize
+05vsize
+08maxdepth
+13splitmaxdepth
+11boxmaxdepth
+05hfuzz
+05vfuzz
+18delimitershortfall
+18nulldelimiterspace
+11scriptspace
+14predisplaysize
+12displaywidth
+13displayindent
+12overfullrule
+10hangindent
+07hoffset
+07voffset
+16emergencystretch
+26[unknown dimen parameter!]
+05dimen
+06EQUIV(
+12notexpanded:
+09hash size
+06csname
+09endcsname
+11IMPOSSIBLE.
+12NONEXISTENT.
+06accent
+07advance
+15afterassignment
+10aftergroup
+10begingroup
+04char
+09delimiter
+06divide
+08endgroup
+11expandafter
+04font
+09fontdimen
+06halign
+05hrule
+12ignorespaces
+10mathaccent
+08mathchar
+10mathchoice
+08multiply
+07noalign
+10noboundary
+08noexpand
+04omit
+07penalty
+08prevgraf
+07radical
+04read
+05relax
+06setbox
+03the
+06valign
+07vcenter
+05vrule
+09save size
+15grouping levels
+08curlevel
+09retaining
+09restoring
+05SAVE(
+28Incompatible magnification (
+02);
+36 the previous value will be retained
+58I can handle only one magnification ratio per job. So I've
+59reverted to the magnification you used earlier on this run.
+46Illegal magnification has been changed to 1000
+52The magnification ratio must be between 1 and 32768.
+04ETC.
+04BAD.
+02->
+22begin-group character
+20end-group character
+21math shift character
+26macro parameter character
+22superscript character
+20subscript character
+25end of alignment template
+12blank space
+11the letter
+14the character
+23[unknown command code!]
+02:
+08Runaway
+10definition
+08argument
+08preamble
+04text
+03<*>
+09<insert>
+06<read
+02l.
+11<argument>
+11<template>
+16<recently read>
+19<to be read again>
+16<inserted text>
+09<output>
+11<everypar>
+12<everymath>
+15<everydisplay>
+12<everyhbox>
+12<everyvbox>
+11<everyjob>
+10<everycr>
+07<mark>
+08<write>
+16input stack size
+05write
+48(interwoven alignment preambles are not allowed)
+17text input levels
+03par
+11Incomplete
+34; all text was ignored after line
+54A forbidden control sequence occurred in skipped text.
+59This kind of error happens when you say `\if...' and forget
+59the matching `\fi'. I've inserted a `\fi'; this might work.
+53The file ended while I was skipping conditional text.
+10File ended
+32Forbidden control sequence found
+16 while scanning
+04 of
+46I suspect you have forgotten a `}', causing me
+41to read past where you wanted me to stop.
+49I'll try to recover; but if the error is serious,
+51you'd better type `E' or `X' now and fix your file.
+03use
+39Text line contains an invalid character
+53A funny symbol that I can't read has just been input.
+48Continue, and I'll forget that it ever happened.
+37(Please type a command or say `\end')
+38*** (job aborted, no legal \end found)
+02=>
+26Undefined control sequence
+47The control sequence at the end of the top line
+52of your error message was never \def'ed. If you have
+55misspelled it (e.g., `\hobx'), type `I' and the correct
+51spelling (e.g., `I\hbox'). Otherwise just continue,
+45and I'll forget about whatever was undefined.
+08Missing
+09 inserted
+53The control sequence marked <to be read again> should
+42not appear between \csname and \endcsname.
+05input
+08endinput
+07topmark
+09firstmark
+07botmark
+14splitfirstmark
+12splitbotmark
+20parameter stack size
+12Argument of
+15 has an extra }
+58I've run across a `}' that doesn't seem to match anything.
+52For example, `\def\a#1{...}' and `\a}' would produce
+54this error. If you simply proceed now, the `\par' that
+52I've just inserted will cause me to report a runaway
+54argument that might be the root of the problem. But if
+57your `}' was spurious, just type `2' and it will go away.
+23Paragraph ended before
+13 was complete
+58I suspect you've forgotten a `}', causing me to apply this
+54control sequence to too much text. How can we recover?
+59My plan is to forget the whole thing and hope for the best.
+07Use of
+29 doesn't match its definition
+54If you say, e.g., `\def\a1{...}', then you must always
+52put `1' after `\a', since control sequence names are
+52made up of letters only. The macro here has not been
+51followed by the required stuff, so I'm ignoring it.
+02<-
+18Missing { inserted
+52A left brace was mandatory here, so I've put one in.
+55You might want to delete and/or insert some corrections
+48so that I will find a matching right brace soon.
+54(If you're confused by all this, try typing `I}' now.)
+23Incompatible glue units
+52I'm going to assume that 1mu=1pt when they're mixed.
+31Missing number, treated as zero
+47A number should have been here; I inserted `0'.
+54(If you can't figure out why I needed to see a number,
+51look up `weird error' in the index to The TeXbook.)
+11spacefactor
+09prevdepth
+10deadcycles
+15insertpenalties
+02wd
+02ht
+02dp
+11lastpenalty
+08lastkern
+08lastskip
+11inputlineno
+07badness
+09Improper
+54You can refer to \spacefactor only in horizontal mode;
+54you can refer to \prevdepth only in vertical mode; and
+48neither of these is meaningful inside \write. So
+52I'm forgetting what you said and using zero instead.
+15You can't use `
+08' after
+17Bad register code
+44A register number must be between 0 and 255.
+27I changed this one to zero.
+18Bad character code
+45A character number must be between 0 and 255.
+10Bad number
+51Since I expected to read a number between 0 and 15,
+12Bad mathchar
+46A mathchar number must be between 0 and 32767.
+18Bad delimiter code
+56A numeric delimiter code must be between 0 and 2^{27}-1.
+28Improper alphabetic constant
+56A one-character control sequence belongs after a ` mark.
+37So I'm essentially inserting \0 here.
+14Number too big
+54I can only go up to 2147483647='17777777777="7FFFFFFF,
+42so I'm using that number instead of yours.
+04true
+25Illegal unit of measure (
+18replaced by filll)
+35I dddon't go any higher than filll.
+02em
+02ex
+12mu inserted)
+48The unit of measurement in math glue must be mu.
+51To recover gracefully from this error, it's best to
+52delete the erroneous units; e.g., type `2' to delete
+45two letters. (See Chapter 27 of The TeXbook.)
+02in
+02pc
+02cm
+02mm
+02bp
+02dd
+02cc
+02sp
+12pt inserted)
+49Dimensions can be in units of em, ex, in, pt, pc,
+50cm, mm, dd, cc, bp, or sp; but yours is a new one!
+59I'll assume that you meant to say pt, for printer's points.
+19Dimension too large
+50I can't work with sizes bigger than about 19 feet.
+46Continue and I'll use the largest value I can.
+04plus
+05minus
+05width
+06height
+05depth
+06number
+12romannumeral
+06string
+07meaning
+08fontname
+07jobname
+04 at
+60Where was the left brace? You said something like `\def\a}',
+43which I'm going to interpret as `\def\a{}'.
+32You already have nine parameters
+45I'm going to ignore the # sign you just used.
+41Parameters must be numbered consecutively
+57I've inserted the digit you should have used after the #.
+36Type `1' to delete what you did use.
+42Illegal parameter number in definition of
+41You meant to type ## instead of #, right?
+56Or maybe a } was forgotten somewhere earlier, and things
+58are all screwed up? I'm going to assume that you meant ##.
+49*** (cannot \read from terminal in nonstop modes)
+18File ended within
+33This \read has unbalanced braces.
+02if
+05ifcat
+05ifnum
+05ifdim
+05ifodd
+07ifvmode
+07ifhmode
+07ifmmode
+07ifinner
+06ifvoid
+06ifhbox
+06ifvbox
+03ifx
+05ifeof
+06iftrue
+07iffalse
+06ifcase
+02fi
+02or
+04else
+06Extra
+44I'm ignoring this; it doesn't match any \if.
+06{true}
+07{false}
+23Missing = inserted for
+48I was expecting to see `<', `=', or `>'. Didn't.
+06{case
+04.fmt
+15input file name
+19I can't find file `
+23I can't write on file `
+02'.
+04.tex
+20Please type another
+45*** (job aborted, file error in nonstop mode)
+04.dvi
+20file name for output
+06texput
+04.log
+02**
+20transcript file name
+02
+08nullfont
+05Font
+08 scaled
+36 not loadable: Bad metric (TFM) file
+42 not loadable: Metric (TFM) file not found
+50I wasn't able to read the size data for this font,
+40so I will ignore the font specification.
+48[Wizards can fix TFM files using TFtoPL/PLtoTF.]
+46You might try inserting a different font spec;
+57e.g., type `I\font<same font id>=<substitute font name>'.
+04.tfm
+33 not loaded: Not enough room left
+52I'm afraid I won't be able to make use of this font,
+55because my memory for character-size data is too small.
+51If you're really stuck, ask a wizard to enlarge me.
+58Or maybe try `I\font<same font id>=<name of loaded font>'.
+23Missing font identifier
+42I was looking for a control sequence whose
+42current meaning has been defined by \font.
+10 has only
+21 fontdimen parameters
+51To increase the number of font parameters, you must
+53use \fontdimen immediately after the \font is loaded.
+11font memory
+31Missing character: There is no
+09 in font
+12 TeX output
+08vlistout
+31Completed box being shipped out
+21Memory usage before:
+08 after:
+19; still untouched:
+31Huge page cannot be shipped out
+50The page just created is more than 18 feet tall or
+58more than 18 feet wide, so I suspect something went wrong.
+35The following box has been deleted:
+19No pages of output.
+18Output written on
+05 page
+02,
+08 bytes).
+02to
+06spread
+09Underfull
+05Loose
+16 \hbox (badness
+38) has occurred while \output is active
+24) in paragraph at lines
+24) in alignment at lines
+02--
+19) detected at line
+16Overfull \hbox (
+11pt too wide
+21Tight \hbox (badness
+05vpack
+16 \vbox (badness
+16Overfull \vbox (
+11pt too high
+21Tight \vbox (badness
+02{}
+12displaystyle
+09textstyle
+11scriptstyle
+17scriptscriptstyle
+14Unknown style!
+07mathord
+06mathop
+07mathbin
+07mathrel
+08mathopen
+09mathclose
+09mathpunct
+09mathinner
+08overline
+09underline
+04left
+05right
+06limits
+08nolimits
+20fraction, thickness
+09= default
+17, left-delimiter
+18, right-delimiter
+25 is undefined (character
+54Somewhere in the math formula just ended, you used the
+60stated character from an undefined font family. For example,
+58plain TeX doesn't allow \it or \sl in subscripts. Proceed,
+52and I'll try to forget that I needed that character.
+06mlist1
+06mlist2
+06mlist3
+640234000122*4000133**3**344*0400400*000000234000111*1111112341011
+06mlist4
+12 inside $$'s
+53Displays can use special alignments (like \eqalignno)
+57only if nothing but the alignment itself is between $$'s.
+58So I've deleted the formulas that preceded this alignment.
+04span
+02cr
+04crcr
+11endtemplate
+24alignment tab character
+40Missing # inserted in alignment preamble
+50There should be exactly one # between &'s, when an
+56\halign or \valign is being set up. In this case you had
+47none, so I've put one in; maybe that will work.
+29Only one # is allowed per tab
+49more than one, so I'm ignoring all but the first.
+04endv
+40Extra alignment tab has been changed to
+52You have given more \span or & marks than there were
+58in the preamble to the \halign or \valign now in progress.
+50So I'll assume that you meant to type \cr instead.
+09256 spans
+06align1
+06align0
+44Infinite glue shrinkage found in a paragraph
+52The paragraph just ended includes some glue that has
+54infinite shrinkability, e.g., `\hskip 0pt minus 1fil'.
+54Such glue doesn't belong there---it allows a paragraph
+59of any length to fit on one line. But it's safe to proceed,
+55since the offensive shrinkability has been made finite.
+05disc1
+05disc2
+02@@
+07: line
+03 t=
+06 -> @@
+07 via @@
+03 b=
+03 p=
+03 d=
+10@firstpass
+11@secondpass
+14@emergencypass
+09paragraph
+05disc3
+05disc4
+13line breaking
+05HYPH(
+11hyphenation
+16 will be flushed
+48Hyphenation exceptions must contain only letters
+51and hyphens. But continue; I'll forgive and forget.
+12Not a letter
+50Letters in \hyphenation words must have \lccode>0.
+47Proceed; I'll ignore the character I just read.
+20exception dictionary
+18pattern memory ops
+31pattern memory ops per language
+14pattern memory
+13Too late for
+08patterns
+53All patterns must be given before typesetting begins.
+04Bad
+17(See Appendix H.)
+09Nonletter
+17Duplicate pattern
+07pruning
+09vertbreak
+48Infinite glue shrinkage found in box being split
+52The box you are \vsplitting contains some infinitely
+57shrinkable glue, e.g., `\vss' or `\vskip 0pt minus 1fil'.
+59Such glue doesn't belong there; but you can safely proceed,
+06vsplit
+09 needs a
+04vbox
+44The box you are trying to split is an \hbox.
+49I can't split such a box, so I'll leave it alone.
+08pagegoal
+09pagetotal
+11pagestretch
+14pagefilstretch
+15pagefillstretch
+16pagefilllstretch
+10pageshrink
+09pagedepth
+04fill
+05filll
+17### current page:
+28 (held over for next output)
+13total height
+13 goal height
+06 adds
+03, #
+12 might split
+15%% goal height=
+12, max depth=
+38Insertions can only be added to a vbox
+40Tut tut: You're trying to \insert into a
+41\box register that now contains an \hbox.
+47Proceed, and I'll discard its present contents.
+04page
+45Infinite glue shrinkage found on current page
+52The page about to be output contains some infinitely
+03 g=
+03 c=
+38Infinite glue shrinkage inserted from
+53The correction glue for page breaking with insertions
+52must have finite shrinkability. But you may proceed,
+07% split
+04 to
+15255 is not void
+53You shouldn't use \box255 except in \output routines.
+14Output loop---
+24 consecutive dead cycles
+57I've concluded that your \output is awry; it never does a
+55\shipout, so I'm shipping \box255 out myself. Next time
+58increase \maxdeadcycles if you want me to be more patient!
+25Unbalanced output routine
+58Your sneaky output routine has problematic {'s and/or }'s.
+41I can't handle that very well; good luck.
+33Output routine didn't use all of
+43Your \output commands should empty \box255,
+34e.g., by saying `\shipout\box255'.
+43Proceed; I'll discard its present contents.
+18Missing $ inserted
+56I've inserted a begin-math/end-math symbol since I think
+48you left one out. Proceed, with fingers crossed.
+05' in
+50Sorry, but I'm not programmed to handle this case;
+45I'll just pretend that you didn't ask for it.
+49If you're in the wrong mode, you might be able to
+58return to the right one by typing `I}' or `I$' or `I\par'.
+03end
+04dump
+05hskip
+04hfil
+05hfill
+03hss
+07hfilneg
+05vskip
+04vfil
+05vfill
+03vss
+07vfilneg
+52I've inserted something that you may have forgotten.
+32(See the <inserted text> above.)
+48With luck, this will get me unwedged. But if you
+55really didn't forget anything, try typing `2' now; then
+56my insertion and my current dilemma will both disappear.
+06right.
+58Things are pretty mixed up, but I think the worst is over.
+12Too many }'s
+42You've closed more groups than you opened.
+51Such booboos are generally harmless, so keep going.
+10rightbrace
+22Extra }, or forgotten
+58I've deleted a group-closing symbol because it seems to be
+59spurious, as in `$x}$'. But perhaps the } is legitimate and
+59you forgot something else, as in `\hbox{$x}'. In such cases
+58the way to recover is to insert both the forgotten and the
+40deleted material, e.g., by typing `I$}'.
+08moveleft
+09moveright
+05raise
+05lower
+04copy
+07lastbox
+04vtop
+04hbox
+07shipout
+07leaders
+08cleaders
+08xleaders
+35Leaders not followed by proper glue
+56You should say `\leaders <box or rule><hskip or vskip>'.
+50I found the <box or rule>, but there's no suitable
+48<hskip or vskip>, so I'm ignoring these leaders.
+34Sorry; this \lastbox will be void.
+58Sorry...I usually can't take things from the current page.
+37This \lastbox will therefore be void.
+21Missing `to' inserted
+48I'm working on `\vsplit<box number> to <dimen>';
+31will look for the <dimen> next.
+31A <box> was supposed to be here
+57I was expecting to see \hbox or \vbox or \copy or \box or
+59something like that. So you might find something missing in
+53your output. But keep trying; you can fix this later.
+06indent
+08noindent
+26' here except with leaders
+52To put a horizontal rule in an hbox or an alignment,
+56you should use \leaders or \hrulefill (see The TeXbook).
+10You can't
+45I'm changing to \insert0; box 255 is special.
+32Try `I\vskip-\lastskip' instead.
+31Try `I\kern-\lastkern' instead.
+46Perhaps you can make the output routine do it.
+09unpenalty
+06unkern
+06unskip
+06unhbox
+07unhcopy
+06unvbox
+07unvcopy
+34Incompatible list can't be unboxed
+35Sorry, Pandora. (You sneaky devil.)
+58I refuse to unbox an \hbox in vertical mode or vice versa.
+40And I can't open any boxes in math mode.
+13Illegal math
+54Sorry: The third part of a discretionary break must be
+57empty, in math formulas. I had to delete your third part.
+30Discretionary list is too long
+50Wow---I never thought anybody would tweak me here.
+56You can't seriously need such a huge discretionary list?
+27Improper discretionary list
+54Discretionary lists must contain only boxes and kerns.
+53The following discretionary sublist has been deleted:
+18Missing } inserted
+45I've put in what seems to be necessary to fix
+44the current column of the current alignment.
+43Try to go on, since this might almost work.
+10Misplaced
+55I can't figure out why you would want to use a tab mark
+50here. If you just want an ampersand, the remedy is
+52simple: Just type `I\&' now. But if some right brace
+52up above has ended a previous alignment prematurely,
+52you're probably due for more error messages, and you
+57might try typing `S' now just to see what is salvageable.
+57or \cr or \span just now. If something like a right brace
+46I expect to see \noalign only after the \cr of
+49an alignment. Proceed, and I'll ignore this case.
+56I expect to see \omit only after tab marks or the \cr of
+53I'm guessing that you meant to end an alignment here.
+50I'm ignoring this, since I wasn't doing a \csname.
+04eqno
+05leqno
+13displaylimits
+42Limit controls must follow a math operator
+57I'm ignoring this misplaced \limits or \nolimits command.
+30Missing delimiter (. inserted)
+52I was expecting to see something like `(' or `\{' or
+55`\}' here. If you typed, e.g., `{' instead of `\{', you
+57should probably delete the `{' by typing `1' now, so that
+52braces don't get unbalanced. Otherwise just proceed.
+54Acceptable delimiters are characters whose \delcode is
+58nonnegative, or you can use `\delimiter <delimiter code>'.
+11Please use
+25 for accents in math mode
+55I'm changing \accent to \mathaccent here; wish me luck.
+59(Accents are not the same in formulas as they are in text.)
+18Double superscript
+43I treat `x^1^2' essentially like `x^1{}^2'.
+16Double subscript
+43I treat `x_1_2' essentially like `x_1{}_2'.
+05above
+04over
+04atop
+15abovewithdelims
+14overwithdelims
+14atopwithdelims
+35Ambiguous; you need another { and }
+55I'm ignoring this fraction specification, since I don't
+52know whether a construction like `x \over y \over z'
+53means `{x \over y} \over z' or `x \over {y \over z}'.
+49I'm ignoring a \right that had no matching \left.
+47Math formula deleted: Insufficient symbol fonts
+50Sorry, but I can't typeset math unless \textfont 2
+50and \scriptfont 2 and \scriptscriptfont 2 have all
+50the \fontdimen values needed in math symbol fonts.
+50Math formula deleted: Insufficient extension fonts
+50Sorry, but I can't typeset math unless \textfont 3
+50and \scriptfont 3 and \scriptscriptfont 3 have all
+53the \fontdimen values needed in math extension fonts.
+31Display math should end with $$
+59The `$' that I just saw supposedly matches a previous `$$'.
+49So I shall assume that you typed `$$' both times.
+07display
+19Missing $$ inserted
+04long
+05outer
+06global
+03def
+04gdef
+04edef
+04xdef
+06prefix
+29You can't use a prefix with `
+55I'll pretend you didn't say \long or \outer or \global.
+06' or `
+08' with `
+49I'll pretend you didn't say \long or \outer here.
+33Missing control sequence inserted
+52Please don't say `\def cs{...}', say `\def\cs{...}'.
+59I've inserted an inaccessible control sequence so that your
+60definition will be completed without mixing me up too badly.
+53You can recover graciously from this error, if you're
+42careful; see exercise 27.2 in The TeXbook.
+12inaccessible
+03let
+09futurelet
+07chardef
+11mathchardef
+08countdef
+08dimendef
+07skipdef
+09muskipdef
+07toksdef
+44You should have said `\read<number> to \cs'.
+34I'm going to look for the \cs now.
+14Invalid code (
+29), should be in the range 0..
+21), should be at most
+54I'm going to use 0 instead of that illegal code value.
+02by
+19Arithmetic overflow
+50I can't carry out that multiplication or division,
+33since the result is out of range.
+55I'm forgetting what you said and not changing anything.
+57Sorry, \setbox is not allowed after \halign in a display,
+45or between \accent and an accented character.
+16Bad space factor
+47I allow only values in the range 1..32767 here.
+37I allow only nonnegative values here.
+37Patterns can be loaded only by INITEX
+10hyphenchar
+08skewchar
+04FONT
+02at
+06scaled
+20Improper `at' size (
+21pt), replaced by 10pt
+50I can only handle fonts at positive sizes that are
+56less than 2048pt, so I've changed what you said to 10pt.
+12select font
+13errorstopmode
+06openin
+07closein
+07message
+10errmessage
+31(That was another \errmessage.)
+50This error message was generated by an \errmessage
+43command, so I can't give any explicit help.
+54Pretend that you're Hercule Poirot: Examine all clues,
+41and deduce the truth by order and method.
+09lowercase
+09uppercase
+04show
+07showbox
+07showthe
+09showlists
+57This isn't an error message; I'm just \showing something.
+46Type `I\show...' to show more (e.g., \show\cs,
+43\showthe\count10, \showbox255, \showlists).
+54And type `I\tracingonline=1\show...' to show boxes and
+57lists on your terminal as well as in the transcript file.
+02>
+09undefined
+05macro
+10long macro
+11outer macro
+17outer endtemplate
+06> \box
+02OK
+26 (see the transcript file)
+09 (INITEX)
+29You can't dump inside a group
+24`{...\dump}' is a no-no.
+25 strings of total length
+43 memory locations dumped; current usage is
+30 multiletter control sequences
+24 words of font info for
+15 preloaded font
+05\font
+22 hyphenation exception
+27Hyphenation trie of length
+05 has
+03 op
+08 out of
+14 for language
+19 (preloaded format=
+16format file name
+26Beginning to dump on file
+22Transcript written on
+02 )
+13end occurred
+24inside a group at level
+05when
+09 on line
+16 was incomplete)
+52(see the transcript file for additional information)
+35(\dump is performed only by INITEX)
+21debug # (-1 to exit):
+07openout
+08closeout
+07special
+09immediate
+11setlanguage
+20[unknown extension!]
+04ext1
+12 (hyphenmin
+08whatsit?
+04ext2
+04ext3
+08endwrite
+24Unbalanced write command
+59On this page there's a \write with fewer real {'s than }'s.
+04ext4
+16output file name
+*461251401
diff --git a/systems/knuth/local/tex/trip1.in b/systems/knuth/local/tex/trip1.in
new file mode 100644
index 0000000000..a93fd916c0
--- /dev/null
+++ b/systems/knuth/local/tex/trip1.in
@@ -0,0 +1,2 @@
+
+\input trip
diff --git a/systems/knuth/local/tex/trip2.in b/systems/knuth/local/tex/trip2.in
new file mode 100644
index 0000000000..205d5cd650
--- /dev/null
+++ b/systems/knuth/local/tex/trip2.in
@@ -0,0 +1 @@
+ &trip trip
diff --git a/systems/knuth/local/texpaths.h b/systems/knuth/local/texpaths.h
new file mode 100644
index 0000000000..1e3c02230f
--- /dev/null
+++ b/systems/knuth/local/texpaths.h
@@ -0,0 +1,26 @@
+/*
+ * This file defines the default paths that will be used for TeX software.
+ * (These paths are used if the user's environment doesn't specify paths
+ * in TEXINPUTS, TEXFONTS, TEXBASES, or TEXPOOL.)
+ *
+ * Paths should be colon-separated and no longer than MAXINPATHCHARS-1
+ * (for defaultinputpath) or MAXOTHPATHCHARS (for other default paths).
+ */
+
+#define MAXINPATHCHARS 700 /* maximum number of chars in an input path */
+
+#define defaultinputpath ".:..:/home/tex/local/lib:/home/tex/dist/lib"
+ /* this should always start with "." */
+
+#define MAXOTHPATHCHARS 200 /* other paths should be much shorter */
+
+#define defaultfontpath "/home/fonts"
+ /* it is probably best not to include "." here to prevent confusion
+ by spooled device drivers that think they know where the fonts
+ really are */
+
+#define defaultformatpath ".:/home/tex/local/tex"
+
+#define defaultpoolpath ".:/home/tex/local/tex"
+
+
diff --git a/systems/knuth/local/texware-sparc/Makefile b/systems/knuth/local/texware-sparc/Makefile
new file mode 100644
index 0000000000..3478169052
--- /dev/null
+++ b/systems/knuth/local/texware-sparc/Makefile
@@ -0,0 +1,39 @@
+# Make executable files for this workstation, using the master TeX sources
+
+PFLAGS= -O -I.. -temp=.
+CFLAGS= -O -I..
+PC= time pc
+
+all: pooltype tftopl pltotf dvitype
+
+%.p: %.ch
+ tangle ../../dist/texware/$*.web $*.ch
+ mv ../../dist/texware/$*.p .
+
+%.ch: ../../dist/texware/%.web
+ touch $*.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+pooltype: pooltype.o
+ ${PC} ${PFLAGS} -o pooltype pooltype.o
+
+tftopl: tftopl.o access.o
+ ${PC} ${PFLAGS} -o tftopl tftopl.o access.o
+
+pltotf: pltotf.o access.o
+ ${PC} ${PFLAGS} -o pltotf pltotf.o access.o
+
+dvitype: dvitype.o dvityext.o dvityext.h
+ ${PC} ${PFLAGS} -o dvitype dvitype.o dvityext.o
+
+dvityext.o: dvityext.c ../h00vars.h ../texpaths.h
+
+access.o: access.c
+
+install: # doit make install "PROG=dvitype"
+ mv $(PROG) /usr/local/bin/$(PROG)
+
+clean:
+ rm -f *.p *.o *~ ../../dist/texware/*.pool
diff --git a/systems/knuth/local/texware-sparc/access.c b/systems/knuth/local/texware-sparc/access.c
new file mode 100644
index 0000000000..af3e5de5e2
--- /dev/null
+++ b/systems/knuth/local/texware-sparc/access.c
@@ -0,0 +1,19 @@
+#include "h00vars.h" /* defines Pascal I/O structure */
+
+#define namelength 100 /* must agree with tftopl.ch and pltotf.ch */
+
+bool testreadaccess(fn)
+ char *fn;
+{
+ register char *p;
+
+ fn[namelength-1] = ' ';
+ p = fn;
+ while (*p++ != ' ');
+ p--;
+ *p = '\0';
+ if (access(fn,4)==0) {
+ *p = ' '; return(TRUE);}
+ else {*p = ' '; return(FALSE);}
+}
+
diff --git a/systems/knuth/local/texware-sparc/dvityext.c b/systems/knuth/local/texware-sparc/dvityext.c
new file mode 100644
index 0000000000..38c1d45159
--- /dev/null
+++ b/systems/knuth/local/texware-sparc/dvityext.c
@@ -0,0 +1,150 @@
+/* External procedures for dvitype */
+/* Written by: H. Trickey, 2/19/83 (adapted from TeX's ext.c) */
+
+#include "texpaths.h" /* defines default TEXFONTS path */
+#include "h00vars.h" /* defines Pascal I/O structure */
+
+char *fontpath;
+
+char *getenv();
+
+/*
+ * setpaths is called to set up the pointer fontpath
+ * as follows: if the user's environment has a value for TEXFONTS
+ * then use it; otherwise, use defaultfontpath.
+ */
+setpaths()
+{
+ register char *envpath;
+
+ if ((envpath = getenv("TEXFONTS")) != NULL)
+ fontpath = envpath;
+ else
+ fontpath = defaultfontpath;
+}
+
+#define namelength 100 /* should agree with dvitype.ch */
+extern char curname[],realnameoffile[]; /* these have size namelength */
+
+/*
+ * testaccess(amode,filepath)
+ *
+ * Test whether or not the file whose name is in the global curname
+ * can be opened for reading (if mode=READACCESS)
+ * or writing (if mode=WRITEACCESS).
+ *
+ * The filepath argument is one of the ...FILEPATH constants defined below.
+ * If the filename given in curname does not begin with '/', we try
+ * prepending all the ':'-separated areanames in the appropriate path to the
+ * filename until access can be made, if it ever can.
+ *
+ * The realnameoffile global array will contain the name that yielded an
+ * access success.
+ */
+
+#define READACCESS 4
+#define WRITEACCESS 2
+
+#define NOFILEPATH 0
+#define FONTFILEPATH 3
+
+bool
+testaccess(amode,filepath)
+ int amode,filepath;
+{
+ register bool ok;
+ register char *p;
+ char *curpathplace;
+ int f;
+
+ switch(filepath) {
+ case NOFILEPATH: curpathplace = NULL; break;
+ case FONTFILEPATH: curpathplace = fontpath; break;
+ }
+ if (curname[0]=='/') /* file name has absolute path */
+ curpathplace = NULL;
+ do {
+ packrealnameoffile(&curpathplace);
+ if (amode==READACCESS)
+ /* use system call "access" to see if we could read it */
+ if (access(realnameoffile,READACCESS)==0) ok = TRUE;
+ else ok = FALSE;
+ else {
+ /* WRITEACCESS: use creat to see if we could create it, but close
+ the file again if we're OK, to let pc open it for real */
+ f = creat(realnameoffile,0666);
+ if (f>=0) ok = TRUE;
+ else ok = FALSE;
+ if (ok)
+ close(f);
+ }
+ } while (!ok && curpathplace != NULL);
+ if (ok) { /* pad realnameoffile with blanks, as Pascal wants */
+ for (p = realnameoffile; *p != '\0'; p++)
+ /* nothing: find end of string */ ;
+ while (p < &(realnameoffile[namelength]))
+ *p++ = ' ';
+ }
+ return (ok);
+}
+
+/*
+ * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp,
+ * followed by '/', followed by the characters in curname up until the
+ * first blank there, and finally a '\0'. The cpp pointer is left pointing
+ * at the next directory in the path.
+ * But: if *cpp == NULL, then we are supposed to use curname as is.
+ */
+packrealnameoffile(cpp)
+ char **cpp;
+{
+ register char *p,*realname;
+
+ realname = realnameoffile;
+ if ((p = *cpp)!=NULL) {
+ while ((*p != ':') && (*p != '\0')) {
+ *realname++ = *p++;
+ if (realname == &(realnameoffile[namelength-1]))
+ break;
+ }
+ if (*p == '\0') *cpp = NULL; /* at end of path now */
+ else *cpp = p+1; /* else get past ':' */
+ *realname++ = '/'; /* separate the area from the name to follow */
+ }
+ /* now append curname to realname... */
+ p = curname;
+ while (*p != ' ') {
+ if (realname >= &(realnameoffile[namelength-1])) {
+ fprintf(stderr,"! Full file name is too long\n");
+ break;
+ }
+ *realname++ = *p++;
+ }
+ *realname = '\0';
+}
+
+int flength(filep)
+ register struct iorec *filep;
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+ register long pos; /* current file position */
+ register int len; /* the file length */
+
+ iop = filep->fbuf;
+ pos = ftell(iop);
+ fseek(iop,0L,2);
+ len = ftell(iop);
+ fseek(iop,pos,0);
+ return(len);
+}
+
+bseek(filep,cnt)
+ register struct iorec *filep;
+ int cnt;
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+
+ iop = filep->fbuf;
+ fseek(iop,(long)cnt,0);
+}
+
diff --git a/systems/knuth/local/texware-sparc/dvityext.h b/systems/knuth/local/texware-sparc/dvityext.h
new file mode 100644
index 0000000000..d177f6fe7e
--- /dev/null
+++ b/systems/knuth/local/texware-sparc/dvityext.h
@@ -0,0 +1,11 @@
+procedure setpaths;
+ external;
+
+function testaccess(accessmode:integer; filepath:integer): boolean;
+ external;
+
+function flength(var f: bytefile):integer;
+ external;
+
+procedure bseek(var f: bytefile; loc: integer);
+ external;
diff --git a/systems/knuth/local/texware-sparc/dvitype.ch b/systems/knuth/local/texware-sparc/dvitype.ch
new file mode 100644
index 0000000000..a2ce230dd7
--- /dev/null
+++ b/systems/knuth/local/texware-sparc/dvitype.ch
@@ -0,0 +1,497 @@
+% Change file for the DVItype processor, for use on Berkeley UNIX systems.
+% This file was created by Howard Trickey, CSD.Trickey@SCORE, and modified
+% by Pavel Curtis, Pavel@Cornell.
+
+% History:
+% ?? (HT) Original version.
+% 4/4/83 (PC) Merged with Pavel's change file and made to work with the
+% version 1.0 of DVItype released with version 0.95 of TeX in
+% February, 1983.
+% 4/18 (PC) Added changes to module 47 so that it would work the same
+% when input was a file (or pipe) as with a terminal.
+% 6/29 (HWT) Brought up to version 1.1 as released with version 0.99 of
+% TeX, with new change file format
+% 7/28 (HWT) Brought up to version 2 as released with version 0.999.
+% Only the banner changes.
+% 11/21 (HWT) Brought up to version 2.2 as released with version 1.0.
+% 2/19/84 (HWT) Made it use TEXFONTS environment.
+% 3/23/84 (HWT) Brought up to version 2.3.
+% 7/11/84 (HWT) Brought up to version 2.6 as released with version 1.1.
+% 8/4/84 (RKF) To version 2.7
+% 9/3/85 (RKF) To version 2.8
+% 12/5/87 (PAM) To version 2.9
+% 8/10/89 (don) Cosmetic change for SunOS; moved usage test
+% 10/15/89 (don) Fixed input_ln so that initial newline isn't lost
+% 10/18/89 (don) Enabled random reading
+% 10/30/89 (don) To version 3
+% 11/18/89 (don) To version 3.1
+% 1/2/90 (don) To version 3.2
+% 5/17/90 (don) To version 3.3
+% 9/6/90 (don) To version 3.4
+% 3/7/95 (don) To version 3.5
+% 12/28/95 (don) To version 3.6
+% 12/23/96 (don) Identified the TFM file that was missing
+
+% The section numbers used in this file refer to those in the standard
+% TeXware report (CS1097).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{DVI$\,$\lowercase{type} changes for SunOS}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is DVItype, Version 3.6' {printed when the program starts}
+@y
+@d banner=='This is DVItype, Version 3.6 for SunOS'
+ {printed when the program starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Use default case statement feature of SunOS Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Change filenames in program statement; add #include statement
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+
+@p program DVI_type(@!dvi_file,@!output);
+@y
+For Berkeley {\mc UNIX}, we need the name |output| for the dialog, so make
+the output come out on a file called |'dvitype.out'|.
+
+@d print(#)==write(dvityout,#)
+@d print_ln(#)==write_ln(dvityout,#)
+
+@p program DVI_type(@!input,@!output);
+@z
+
+@x
+var @<Globals in the outer block@>@/
+@y
+var @<Globals in the outer block@>@/
+@\@=#include "dvityext.h"@>@\ {declarations for external C procedures}
+@z
+
+@x
+ begin print_ln(banner);@/
+@y
+ begin
+ setpaths; {read environment, to find TEXFONTS, if there}
+ rewrite(dvityout,'dvitype.out'); {prepare typescript for output}
+ print_ln(banner);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [5] Increase name_length
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!name_length=50; {a file name shouldn't be longer than this}
+@y
+@!name_length=100; {a file name shouldn't be longer than this}
+@z
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] Change definition of 'byte_file' to correct subrange
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+We shall stick to simple \PASCAL\ in this program, for reasons of clarity,
+even if such simplicity is sometimes unrealistic.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of eight_bits; {files that contain binary data}
+@y
+On Berkeley {\mc UNIX}, we have to use |-128..127| for byte files, as
+explained in the \TeX\ changes.
+
+@<Types...@>=
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@!byte_file=packed file of -128..127; {files that contain binary data}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [23] Fix up opening the binary files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+begin reset(dvi_file);
+cur_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin reset(tfm_file,cur_name);
+end;
+@y
+On Berkeley {\mc UNIX}, the |eof| testing won't work.
+We use the external |test_access| procedure, which also does path searching
+based on the user's environment or the default path.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {no path searching should be done}
+@d font_file_path=3 {path specifier for \.{TFM} files}
+
+@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+begin argv(1, cur_name);
+ if test_access(read_access_mode,no_file_path) then
+ begin dvi_name:=real_name_of_file; reset(dvi_file,dvi_name)
+ end
+ else begin
+ write_ln('DVI file not found');
+ jump_out;
+ end;
+ cur_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin
+if test_access(read_access_mode,font_file_path) then
+ reset(tfm_file,real_name_of_file)
+else begin
+ write_ln('TFM file not found: ',cur_name);
+ jump_out
+end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] Declare real_name_of_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+|dvi_file|, and |cur_name| is a string variable that will be set to the
+current font metric file name before |open_tfm_file| is called.
+
+@<Glob...@>=
+@!cur_loc:integer; {where we are about to look, in |dvi_file|}
+@!cur_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+@y
+|dvi_file|, and |cur_name| is a string variable that will be set to the
+current font metric file name before |open_tfm_file| is called.
+Under UNIX, we also have a |real_name_of_file| string, that gets
+set by the external |test_access| procedure after path searching.
+
+@<Glob...@>=
+@!cur_loc:integer; {where we are about to look, in |dvi_file|}
+@!cur_name,@!real_name_of_file,@!dvi_name:packed array[1..name_length] of char;
+ {external name}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [26] Fix read_tfm_word to read bytes properly
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure read_tfm_word;
+begin read(tfm_file,b0); read(tfm_file,b1);
+read(tfm_file,b2); read(tfm_file,b3);
+end;
+@y
+@d get_tfm_byte(#) ==
+ read(tfm_file,byte); if byte < 0 then # := byte + 256 else # := byte;
+
+@p procedure read_tfm_word;
+var byte : -128..127;
+begin
+get_tfm_byte(b0);
+get_tfm_byte(b1);
+get_tfm_byte(b2);
+get_tfm_byte(b3);
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27] Fix up functions to read DVI bytes
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(dvi_file) then get_byte:=0
+else begin read(dvi_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:eight_bits;
+begin read(dvi_file,b); incr(cur_loc);
+if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function signed_pair:integer; {returns the next two bytes, signed}
+var a,@!b:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b);
+cur_loc:=cur_loc+2;
+if a<128 then signed_pair:=a*256+b
+else signed_pair:=(a-256)*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_trio:integer; {returns the next three bytes, signed}
+var a,@!b,@!c:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
+cur_loc:=cur_loc+3;
+if a<128 then signed_trio:=(a*256+b)*256+c
+else signed_trio:=((a-256)*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+@y
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:-128..127;
+begin if eof(dvi_file) then get_byte:=0
+else begin read(dvi_file,b); incr(cur_loc);
+ if b < 0 then get_byte := b + 256 else get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:-128..127;
+begin read(dvi_file,b); incr(cur_loc);
+signed_byte:=b;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,b:integer;
+begin a := get_byte; b := get_byte;
+get_two_bytes:=a*256+b;
+end;
+@#
+function signed_pair:integer; {returns the next two bytes, signed}
+var a,@!b:integer;
+begin a := signed_byte; b := get_byte;
+signed_pair:=a*256+b
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:integer;
+begin a := get_byte; b := get_byte; c := get_byte;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_trio:integer; {returns the next three bytes, signed}
+var a,@!b,@!c:integer;
+begin a := signed_byte; b := get_byte; c := get_byte;
+signed_trio:=(a*256+b)*256+c
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:integer;
+begin a := signed_byte; b := get_byte; c := get_byte; d := get_byte;
+signed_quad:=((a*256+b)*256+c)*256+d
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [28] Redefine dvi_length() and move_to_byte().
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function dvi_length:integer;
+begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file);
+end;
+@#
+procedure move_to_byte(n:integer);
+begin set_pos(dvi_file,n); cur_loc:=n;
+end;
+@y
+@p function dvi_length:integer;
+begin dvi_length:=flength(dvi_file);
+end;
+@#
+procedure move_to_byte(n:integer);
+begin bseek(dvi_file,n); cur_loc:=n;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [45] Define term_in and term_out and declare dvityout, first_input
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!term_in:text_file; {the terminal, considered as an input file}
+@!term_out:text_file; {the terminal, considered as an output file}
+@y
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@d term_in==input
+@d term_out==output
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!dvityout:text;
+@!first_input:boolean; {true until |input_ln| has been invoked}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [46] Define update_terminal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush(term_out) {empty the terminal output buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [47] Remove call to reset(term_in) and initial eoln test
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+@y
+begin update_terminal;
+if first_input then first_input:=false
+else if eoln(term_in) then read_ln(term_in);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [50] Remove call to rewrite(term_out)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+begin rewrite(term_out); {prepare the terminal for output}
+@y
+begin
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [64] Set default_directory_name
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d default_directory_name=='TeXfonts:' {change this to the correct name}
+@d default_directory_name_length=9 {change this to the correct length}
+
+@<Glob...@>=
+@!default_directory:packed array[1..default_directory_name_length] of char;
+@y
+Actually, under UNIX the standard area is defined in an external
+``texpaths.h'' file. And the users have a path searched for fonts,
+by setting the TEXFONTS environment variable.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [65] Remove initialization of now-defunct array
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ @<Set init...@>=
+default_directory:=default_directory_name;
+@y
+@ (No initialization to be done. Keep this module to preserve numbering.)
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [66] Fix addition of ".tfm" suffix for portability and keep lowercase
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font. This usually means that we need to
+prepend the name of the default directory, and
+to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
+to upper case, since |cur_name| is a \PASCAL\ string.
+@y
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font. This usually means that we need to,
+at most sites, append the
+suffix ``.tfm''.
+@z
+
+@x
+if p=0 then
+ begin for k:=1 to default_directory_name_length do
+ cur_name[k]:=default_directory[k];
+ r:=default_directory_name_length;
+ end
+else r:=0;
+@y
+r:=0;
+@z
+
+@x
+ if (names[k]>="a")and(names[k]<="z") then
+ cur_name[r]:=xchr[names[k]-@'40]
+ else cur_name[r]:=xchr[names[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='T'; cur_name[r+3]:='F'; cur_name[r+4]:='M'
+@y
+ cur_name[r]:=xchr[names[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='t'; cur_name[r+3]:='f'; cur_name[r+4]:='m'
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [105] Reopen DVI file after postamble has been checked
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if not eof(dvi_file) then bad_dvi('signature in byte ',cur_loc-1:1,
+@.signature...should be...@>
+ ' should be 223')
+else if cur_loc<k+4 then
+ print_ln('not enough signature bytes at end of file (',
+@.not enough signature bytes...@>
+ cur_loc-k:1,')');
+@y
+if not eof(dvi_file) then bad_dvi('signature in byte ',cur_loc-1:1,
+@.signature...should be...@>
+ ' should be 223');
+if cur_loc<k+4 then
+ print_ln('not enough signature bytes at end of file (',
+@.not enough signature bytes...@>
+ cur_loc-k:1,')');
+reset(dvi_file,dvi_name) {the Berkeley UNIX routines have closed the file}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [107] Check usage; also print newline at end of program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+dialog; {set up all the options}
+@y
+if argc <> 2 then
+ begin write_ln('Usage: dvitype <dvi-file>'); goto final_end;
+ end;
+first_input:=true; dialog; {set up all the options}
+@z
+@x
+final_end:end.
+@y
+final_end: print_ln(' '); end.
+@z
diff --git a/systems/knuth/local/texware-sparc/pltotf.ch b/systems/knuth/local/texware-sparc/pltotf.ch
new file mode 100644
index 0000000000..8134f8dc46
--- /dev/null
+++ b/systems/knuth/local/texware-sparc/pltotf.ch
@@ -0,0 +1,135 @@
+% Change file for the PLtoTF processor, for use on Berkeley UNIX systems.
+% This file was created by Pavel Curtis, Pavel@Cornell.
+
+% History:
+% 4/4/83 (PC) Original version, made to work with version 1.2 of PLtoTF.
+% 4/16 (PC) Brought up to version 1.3 of PLtoTF.
+% 6/30 (HWT) Revised changefile format for version 1.7 Tangle
+% 7/28 (HWT) Brought up to version 2
+% 11/21 (HWT) Brought up to version 2.1
+% 9/3/85 (RKF) Brought up to version 2.3
+% 8/7/89 (don) Cosmetic change for SunOS
+% 9/16/89 (don) Cosmetic change for version 3
+% 10/16/89 (don) Avoided core dump when PL file can't be opened
+% 11/18/89 (don) Brought up to version 3.1
+% 9/6/90 (don) Version 3.3
+% 3/25/91 (don) Version 3.4
+% 3/6/95 (don) Version 3.5
+
+% The section numbers used in this file refer to the current nunmbers,
+% and, where different, also to those in the standard TeXware report (CS1097).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{PL\lowercase{to}TF changes for SunOS}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is PLtoTF, Version 3.5' {printed when the program starts}
+@y
+@d banner=='This is PLtoTF, Version 3.5 for SunOS'
+ {printed when the program starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Fix filenames in program statement; add `final_end' label; add access
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program PLtoTF(@!pl_file,@!tfm_file,@!output);
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@y
+@d final_end==9999
+
+@p program PLtoTF(@!output);
+label final_end;
+const @<Constants in the outer block@>@/
+type UNIX_file_name=packed array[1..100] of char;
+ @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+function testreadaccess(var fn:UNIX_file_name):boolean; external;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6] Open PL file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reset(pl_file);
+@y
+if argc < 3 then begin
+ print_ln('Usage: pltotf <pl-file> <tfm-file>');
+ goto final_end;
+end;
+argv(1, pl_name);
+if testreadaccess(pl_name) then reset(pl_file, pl_name)
+else begin print_ln('I can''t read the PL file!'); goto final_end;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [15] Change type of tfm_file and declare extra TFM-file variables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!tfm_file:packed file of 0..255;
+@y
+@!tfm_file : packed file of -128..127;
+@!tmp : 0..255;
+@!tfm_name,
+@!pl_name : UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [16] Open TFM file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ On some systems you may have to do something special to write a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(tfm_file);
+@y
+@ On some systems you may have to do something special to write a
+packed file of bytes.
+@^system dependencies@>
+
+@<Set init...@>=
+argv(2, tfm_name);
+rewrite(tfm_file, tfm_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [127, was 119] Change TFM-byte output to fix ranges
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d out(#)==write(tfm_file,#)
+@y
+@d out(#)==begin
+ tmp := #;
+ if tmp > 127 then
+ write(tfm_file, tmp - 256)
+ else
+ write(tfm_file, tmp)
+ end
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [147, was 135] Define label `final_end'; print newline at end of program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+end.
+@y
+final_end: print_ln(' '); end.
+@z
diff --git a/systems/knuth/local/texware-sparc/pooltype.ch b/systems/knuth/local/texware-sparc/pooltype.ch
new file mode 100644
index 0000000000..94241bd9c6
--- /dev/null
+++ b/systems/knuth/local/texware-sparc/pooltype.ch
@@ -0,0 +1,70 @@
+% Change file for the Pooltype processor
+% Hacked together by don on 8/7/89 since the old pooltype.ch seems lost
+% 8-bit modifications made 9/11/89
+
+% Module numbers are from the standard TeXware report (CS1097)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{POOLTYPE changes for SunOS}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Program header
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+@x
+@p program POOLtype(@!pool_file,@!output);
+@y
+@p program POOLtype(@!output);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [12] Permissiveness
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+@y
+for i:=0 to @'37 do xchr[i]:=chr(i);
+for i:=@'177 to @'377 do xchr[i]:=chr(i);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [15] Beginning of main program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+@x
+@p begin initialize;@/
+@y
+@p begin if argc < 2 then
+ abort('Usage: pooltype <pool-file>');
+initialize;@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [18] Input file declaration needs also a string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+@x
+@!pool_file:packed file of text_char;
+@y
+@!pool_file:packed file of text_char;
+@!pool_name: array[1..100] of char; {not 100 percent robust}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [19] Opening the input file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+@x
+reset(pool_file); xsum:=false;
+@y
+argv(1,pool_name); reset(pool_file,pool_name); xsum:=false;
+@z
diff --git a/systems/knuth/local/texware-sparc/tftopl.ch b/systems/knuth/local/texware-sparc/tftopl.ch
new file mode 100644
index 0000000000..cb18739546
--- /dev/null
+++ b/systems/knuth/local/texware-sparc/tftopl.ch
@@ -0,0 +1,206 @@
+% Change file for the TFtoPL processor, for use on Berkeley UNIX systems.
+% This file was created by Pavel Curtis, Pavel@Cornell.
+
+% History:
+% 4/4/83 (PC) Original version, made to work with version 1.0 of TFtoPL,
+% released with version 0.96 of TeX in February, 1983.
+% 4/16 (PC) Brought up to version 1.0 released with version 0.97 of TeX
+% in April, 1983.
+% 6/30 (HWT) Revised changefile format, for use with version 1.7 Tangle.
+% 7/28 (HWT) Brought up to version 2
+% 11/21 (HWT) Brought up to version 2.1
+% 3/24/84 (HWT) Brought up to version 2.2
+% 7/12/84 (HWT) Brought up to version 2.3
+% 9/3/85 (RKF) Brought up to version 2.4
+% 3/1/86 (PAM) Cosmetic upgrade to Version 2.5
+% 8/7/89 (don) Cosmetic change for SunOS
+% 9/15/89 (don) Cosmetic upgrade to Version 3
+% 10/18/89 (don) Avoid core dump when the file isn't present
+% 11/18/89 (don) Brought up to version 3.1
+
+% The section numbers used in this file refer to the current numbers,
+% and, where different, also to those in the standard TeXware report (CS1097).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{TF\lowercase{to}PL changes for SunOS}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is TFtoPL, Version 3.1' {printed when the program starts}
+@y
+@d banner=='This is TFtoPL, Version 3.1 for SunOS'
+ {printed when the program starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Fix files in program statement; add external access procedure
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program TFtoPL(@!tfm_file,@!pl_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@y
+@p program TFtoPL(@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type UNIX_file_name=packed array[1..100] of char;
+ @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+function testreadaccess(var fn:UNIX_file_name):boolean; external;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6] Fix declaration of tfm_file; declare extra TFM-file variables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!tfm_file:packed file of 0..255;
+@y
+@!tfm_file:packed file of -128..127; {files that contain binary data}
+@!tfm_name:UNIX_file_name;
+@!tfm_byte:integer;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Open TFM file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ On some systems you may have to do something special to read a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+reset(tfm_file);
+@y
+@ On some systems you may have to do something special to read a
+packed file of bytes.
+
+@<Set init...@>=
+if argc < 3 then begin
+ print_ln('Usage: tftopl <tfm-file> <pl-file>');
+ goto final_end;
+end;
+argv(1, tfm_name);
+if testreadaccess(tfm_name) then reset(tfm_file, tfm_name)
+else begin print_ln('I can''t read the TFM file!'); goto final_end;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [16] Declare pl_name
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!pl_file:text;
+@y
+@!pl_file:text;
+@!pl_name: array[1..100] of char;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [17] Open PL file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ @<Set init...@>=
+rewrite(pl_file);
+@y
+@ @<Set init...@>=
+argv(2, pl_name);
+rewrite(pl_file, pl_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] Fix reading of TFM bytes.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Read the whole input file@>=
+read(tfm_file,tfm[0]);
+if tfm[0]>127 then abort('The first byte of the input file exceeds 127!');
+@.The first byte...@>
+if eof(tfm_file) then abort('The input file is only one byte long!');
+@.The input...one byte long@>
+read(tfm_file,tfm[1]); lf:=tfm[0]*@'400+tfm[1];
+if lf=0 then
+ abort('The file claims to have length zero, but that''s impossible!');
+@.The file claims...@>
+if 4*lf-1>tfm_size then abort('The file is bigger than I can handle!');
+@.The file is bigger...@>
+for tfm_ptr:=2 to 4*lf-1 do
+ begin if eof(tfm_file) then
+ abort('The file has fewer bytes than it claims!');
+@.The file has fewer bytes...@>
+ read(tfm_file,tfm[tfm_ptr]);
+ end;
+if not eof(tfm_file) then
+ begin print_ln('There''s some extra junk at the end of the TFM file,');
+@.There's some extra junk...@>
+ print_ln('but I''ll proceed as if it weren''t there.');
+ end
+@y
+@d fget==begin get(tfm_file);
+ tfm_byte:=tfm_file^;
+ if tfm_byte<0 then tfm_byte:=tfm_byte+256;
+ end
+@d fbyte==tfm_byte
+@d read_sixteen(#)==begin #:=fbyte;
+ if #>127 then abort;
+ fget; #:=#*@'400+fbyte;
+ end
+@d store_four_quarters(#)==begin fget; a:=fbyte; qw.b0:=qi(a);
+ fget; b:=fbyte; qw.b1:=qi(b);
+ fget; c:=fbyte; qw.b2:=qi(c);
+ fget; d:=fbyte; qw.b3:=qi(d);
+ #:=qw;
+ end
+
+@<Read the whole input file@>=
+ {Prime the pump}
+tfm_byte:=tfm_file^;
+if tfm_byte<0 then tfm_byte:=tfm_byte+256;
+
+tfm[0]:=fbyte; fget;
+if tfm[0]>127 then abort('The first byte of the input file exceeds 127!');
+@.The first byte...@>
+if eof(tfm_file) then abort('The input file is only one byte long!');
+@.The input...one byte long@>
+tfm[1]:=fbyte; fget; lf:=tfm[0]*@'400+tfm[1];
+if lf=0 then
+ abort('The file claims to have length zero, but that''s impossible!');
+@.The file claims...@>
+if 4*lf-1>tfm_size then abort('The file is bigger than I can handle!');
+@.The file is bigger...@>
+for tfm_ptr:=2 to 4*lf-1 do
+ begin if eof(tfm_file) then
+ abort('The file has fewer bytes than it claims!');
+@.The file has fewer bytes...@>
+ tfm[tfm_ptr]:=fbyte; fget;
+ end;
+if not eof(tfm_file) then
+ begin print_ln('There''s some extra junk at the end of the TFM file,');
+@.There's some extra junk...@>
+ print_ln('but I''ll proceed as if it weren''t there.');
+ end
+
+@^system dependencies@>@^Changes for Berkeley {\mc UNIX}@>
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [99, was 88] Add printing of newline at end of program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+final_end:end.
+@y
+final_end: print_ln(' '); end.
+@z
diff --git a/systems/knuth/local/texware/Makefile b/systems/knuth/local/texware/Makefile
new file mode 100644
index 0000000000..db7ad06a2c
--- /dev/null
+++ b/systems/knuth/local/texware/Makefile
@@ -0,0 +1,40 @@
+# Make executable files for this workstation, using the master TeX sources
+
+PFLAGS= -g -I..
+CFLAGS= -g -I..
+PC= gpc
+
+all: pooltype tftopl pltotf dvitype
+
+%.p: %.ch
+ tangle ../../dist/texware/$*.web $*.ch
+# the next line formerly necessary; but not with Linux's (Web2C) tangle
+# mv ../../dist/texware/$*.p .
+
+%.ch: ../../dist/texware/%.web
+ touch $*.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+pooltype: pooltype.o dvityext.o dvityext.h
+ pc ${PFLAGS} -o pooltype pooltype.o dvityext.o
+
+tftopl: tftopl.o dvityext.o dvityext.h
+ pc ${PFLAGS} -o tftopl tftopl.o dvityext.o
+
+pltotf: pltotf.o dvityext.o dvityext.h
+ ${PC} ${PFLAGS} -o pltotf pltotf.o dvityext.o
+
+dvitype: dvitype.o dvityext.o dvityext.h
+ ${PC} ${PFLAGS} -o dvitype dvitype.o dvityext.o
+
+dvityext.o: dvityext.c ../GPCtypes.h ../texpaths.h
+
+install: # doit make install "PROG=dvitype"
+ mv $(PROG) /usr/local/bin/$(PROG)
+
+clean:
+# rm -f *.p *.o *~ ../../dist/texware/*.pool
+ rm -f *.p *.o *~ *.pool core
+
diff --git a/systems/knuth/local/texware/access.c b/systems/knuth/local/texware/access.c
new file mode 100644
index 0000000000..6fde385791
--- /dev/null
+++ b/systems/knuth/local/texware/access.c
@@ -0,0 +1,17 @@
+#define namelength 100 /* must agree with tftopl.ch and pltotf.ch */
+
+bool testreadaccess(fn)
+ char *fn;
+{
+ register char *p;
+
+ fn[namelength-1] = ' ';
+ p = fn;
+ while (*p++ != ' ');
+ p--;
+ *p = '\0';
+ if (access(fn,4)==0) {
+ *p = ' '; return(TRUE);}
+ else {*p = ' '; return(FALSE);}
+}
+
diff --git a/systems/knuth/local/texware/dvityext.c b/systems/knuth/local/texware/dvityext.c
new file mode 100644
index 0000000000..61f20538a5
--- /dev/null
+++ b/systems/knuth/local/texware/dvityext.c
@@ -0,0 +1,176 @@
+/* External procedures for texware */
+/* Written by Don Knuth, 2000.04.29, based on Trickey's vintage 1983 code */
+
+#include "../texpaths.h" /* defines default TEXFONTS path */
+#include "../GPCtypes.h" /* defines Pascal I/O structure */
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#define namelength 100 /* should agree with .ch files */
+
+void Argv(int n, char* str)
+{
+ register char *s,*t;
+ if (n<0 || n>=_p_argc) return;
+ s=_p_argv[n];
+ if (strlen(s) >= namelength-5) {
+ str[0]='\0'; return; /* we don't want it too long */
+ }
+ for (t=str; *s; s++,t++) *t=*s;
+ *t='\0';
+}
+
+bool Testreadaccess(char *fn)
+{
+ return access(fn,R_OK)? FALSE: TRUE;
+}
+
+char *fontpath;
+
+char *getenv();
+
+/*
+ * setpaths is called to set up the pointer fontpath
+ * as follows: if the user's environment has a value for TEXFONTS
+ * then use it; otherwise, use defaultfontpath.
+ */
+Setpaths()
+{
+ register char *envpath;
+
+ if ((envpath = getenv("TEXFONTS")) != NULL)
+ fontpath = envpath;
+ else
+ fontpath = defaultfontpath;
+}
+
+#define namelength 100 /* should agree with dvitype.ch */
+char Curname[namelength],Realnameoffile[namelength];
+#define curname Curname
+#define realnameoffile Realnameoffile
+
+/*
+ * testaccess(amode,filepath)
+ *
+ * Test whether or not the file whose name is in the global curname
+ * can be opened for reading (if mode=READACCESS)
+ * or writing (if mode=WRITEACCESS).
+ *
+ * The filepath argument is one of the ...FILEPATH constants defined below.
+ * If the filename given in curname does not begin with '/', we try
+ * prepending all the ':'-separated areanames in the appropriate path to the
+ * filename until access can be made, if it ever can.
+ *
+ * The realnameoffile global array will contain the name that yielded an
+ * access success.
+ */
+
+#define READACCESS 4
+#define WRITEACCESS 2
+
+#define NOFILEPATH 0
+#define FONTFILEPATH 3
+
+bool
+Testaccess(int amode,int filepath)
+{
+ register bool ok;
+ register char *p;
+ char *curpathplace;
+ int f;
+
+ switch(filepath) {
+ case NOFILEPATH: curpathplace = NULL; break;
+ case FONTFILEPATH: curpathplace = fontpath; break;
+ }
+ if (curname[0]=='/') /* file name has absolute path */
+ curpathplace = NULL;
+ do {
+ packrealnameoffile(&curpathplace);
+ if (amode==READACCESS)
+ /* use system call "access" to see if we could read it */
+ if (access(realnameoffile,R_OK)==0) ok = TRUE;
+ else ok = FALSE;
+ else {
+ /* WRITEACCESS: use creat to see if we could create it, but close
+ the file again if we're OK, to let pc open it for real */
+ f = creat(realnameoffile,0666);
+ if (f>=0) { ok = TRUE; close(f);}
+ else ok = FALSE;
+ }
+ } while (!ok && curpathplace != NULL);
+ return (ok);
+}
+
+/*
+ * packrealnameoffile(cpp) makes realnameoffile contain the directory at *cpp,
+ * followed by '/', followed by the characters in curname up until the
+ * first blank there, and finally a '\0'. The cpp pointer is left pointing
+ * at the next directory in the path.
+ * But: if *cpp == NULL, then we are supposed to use curname as is.
+ */
+packrealnameoffile(cpp)
+ char **cpp;
+{
+ register char *p,*realname;
+
+ realname = realnameoffile;
+ if ((p = *cpp)!=NULL) {
+ while ((*p != ':') && (*p != '\0')) {
+ *realname++ = *p++;
+ if (realname == &(realnameoffile[namelength-1]))
+ break;
+ }
+ if (*p == '\0') *cpp = NULL; /* at end of path now */
+ else *cpp = p+1; /* else get past ':' */
+ *realname++ = '/'; /* separate the area from the name to follow */
+ }
+ /* now append curname to realname... */
+ p = curname;
+ while (*p) {
+ if (realname >= &(realnameoffile[namelength-1])) {
+ fprintf(stderr,"! Full file name is too long\n");
+ break;
+ }
+ *realname++ = *p++;
+ }
+ *realname = '\0';
+}
+
+int Flength(FDR filep)
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+ register long pos; /* current file position */
+ register int len; /* the file length */
+
+ iop = filep->FilJfn;
+ pos = ftell(iop);
+ fseek(iop,0L,SEEK_END);
+ len = ftell(iop);
+ fseek(iop,pos,0);
+ return len;
+}
+
+void Bseek(FDR filep,int cnt)
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+
+ iop = filep->FilJfn;
+ fseek(iop,(long)cnt,SEEK_SET);
+}
+
+void Flushstdout()
+{
+ fflush(stdout);
+}
+
+void Readbyte(FDR filep, unsigned char *x)
+{
+ /* reads a character WITHOUT using the Pascal runtime buffers etc */
+ register int c;
+ c=getc(filep->FilJfn);
+ if (c==EOF) c=0;
+ *x=c;
+}
+
diff --git a/systems/knuth/local/texware/dvityext.h b/systems/knuth/local/texware/dvityext.h
new file mode 100644
index 0000000000..ab9a63da75
--- /dev/null
+++ b/systems/knuth/local/texware/dvityext.h
@@ -0,0 +1,17 @@
+argc:asmname '_p_argc' Integer;
+
+procedure argv(no:Integer;var str:UNIXfilename); external;
+
+function testreadaccess(var a:UNIXfilename):boolean; external;
+
+procedure setpaths; external;
+
+function testaccess(accessmode:integer; filepath:integer): boolean; external;
+
+function flength(var f: bytefile):integer; external;
+
+procedure bseek(var f: bytefile; loc: integer); external;
+
+procedure flushstdout; external;
+
+procedure readbyte(var f: bytefile; var v: ByteCard); external;
diff --git a/systems/knuth/local/texware/dvitype.ch b/systems/knuth/local/texware/dvitype.ch
new file mode 100644
index 0000000000..4857a60d62
--- /dev/null
+++ b/systems/knuth/local/texware/dvitype.ch
@@ -0,0 +1,468 @@
+% Change file for the DVItype processor, for use with GNU Pascal.
+% (by Don Knuth, based on prior work by Trickey and Curtis)
+% (see ../texware-sparc for complete details of their changes)
+
+% History:
+% 2000.04.29 Original version.
+
+% The section numbers used in this file refer to those in the standard
+% TeXware report (CS1097).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{DVI$\,$\lowercase{type} changes for GNU Pascal}
+@z
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is DVItype, Version 3.6' {printed when the program starts}
+@y
+@d banner=='This is DVItype, Version 3.6 for Linux'
+ {printed when the program starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Use default case statement feature of ISO extended Pascal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {default for cases not listed explicitly}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Change filenames in program statement; add #include statement
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print(#)==write(#)
+@d print_ln(#)==write_ln(#)
+
+@p program DVI_type(@!dvi_file,@!output);
+@y
+For {\mc UNIX}, we need the name |output| for the dialog, so make
+the output come out on a file called |'dvitype.out'|.
+
+@d print(#)==write(dvityout,#)
+@d print_ln(#)==write_ln(dvityout,#)
+
+@p program DVI_type(@!input,@!output);
+@z
+
+@x
+var @<Globals in the outer block@>@/
+@y
+var @<Globals in the outer block@>@/
+@\@=#include "dvityext.h"@>@\ {declarations for external C procedures}
+@z
+
+@x
+ begin print_ln(banner);@/
+@y
+ begin
+ setpaths; {read environment, to find TEXFONTS, if there}
+ rewrite(dvityout,'dvitype.out'); {prepare typescript for output}
+ print_ln(banner);@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [5] Increase name_length
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!name_length=50; {a file name shouldn't be longer than this}
+@y
+@!name_length=100; {a file name shouldn't be longer than this}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [9] make the compiler recognize a text file as text
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=127 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+@y
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+@d text_file==text
+
+@<Types...@>=
+@!UNIX_filename=packed array[1..name_length] of char;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] Change definition of 'eight_bits'
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!eight_bits=0..255; {unsigned one-byte quantity}
+@y
+@!eight_bits=ByteCard; {unsigned one-byte quantity}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [23] Fix up opening the binary files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+begin reset(dvi_file);
+cur_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin reset(tfm_file,cur_name);
+end;
+@y
+We use the external |test_access| procedure, which also does path searching
+based on the user's environment or the default path.
+
+@d read_access_mode=4 {``read'' mode for |test_access|}
+@d write_access_mode=2 {``write'' mode for |test_access|}
+
+@d no_file_path=0 {no path searching should be done}
+@d font_file_path=3 {path specifier for \.{TFM} files}
+
+@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
+begin argv(1, cur_name);
+ if test_access(read_access_mode,no_file_path) then
+ begin dvi_name:=real_name_of_file; reset(dvi_file,dvi_name)
+ end
+ else begin
+ write_ln('DVI file not found');
+ jump_out;
+ end;
+ cur_loc:=0;
+end;
+@#
+procedure open_tfm_file; {prepares to read packed bytes in |tfm_file|}
+begin
+if test_access(read_access_mode,font_file_path) then
+ reset(tfm_file,real_name_of_file)
+else begin
+ write_ln('TFM file not found: ',cur_name);
+ jump_out
+end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] Declare real_name_of_file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+|dvi_file|, and |cur_name| is a string variable that will be set to the
+current font metric file name before |open_tfm_file| is called.
+
+@<Glob...@>=
+@!cur_loc:integer; {where we are about to look, in |dvi_file|}
+@!cur_name:packed array[1..name_length] of char; {external name,
+ with no lower case letters}
+@y
+|dvi_file|, and |cur_name| is a string variable that will be set to the
+current font metric file name before |open_tfm_file| is called.
+Under UNIX, we also have a |real_name_of_file| string, that gets
+set by the external |test_access| procedure after path searching.
+
+@<Glob...@>=
+@!cur_loc:integer; {where we are about to look, in |dvi_file|}
+@!cur_name,@!real_name_of_file:external UNIX_filename; {external names}
+@!dvi_name:UNIX_filename;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27] Use C library to read bytes
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin if eof(dvi_file) then get_byte:=0
+else begin read(dvi_file,b); incr(cur_loc); get_byte:=b;
+ end;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:eight_bits;
+begin read(dvi_file,b); incr(cur_loc);
+if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function signed_pair:integer; {returns the next two bytes, signed}
+var a,@!b:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b);
+cur_loc:=cur_loc+2;
+if a<128 then signed_pair:=a*256+b
+else signed_pair:=(a-256)*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_trio:integer; {returns the next three bytes, signed}
+var a,@!b,@!c:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
+cur_loc:=cur_loc+3;
+if a<128 then signed_trio:=(a*256+b)*256+c
+else signed_trio:=((a-256)*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+@y
+The GNU Pascal runtime programs for |read| keeps its own
+buffers, and this conflicts with the |move_to_byte| routine.
+So we substitute our own function, |read_byte|. (Actually
+the Pascal runtimes work fine if |random_reading| is false.)
+
+@p function get_byte:integer; {returns the next byte, unsigned}
+var b:eight_bits;
+begin read_byte(dvi_file,b);
+incr(cur_loc); get_byte:=b;
+end;
+@#
+function signed_byte:integer; {returns the next byte, signed}
+var b:eight_bits;
+begin read_byte(dvi_file,b); incr(cur_loc);
+if b<128 then signed_byte:=b @+ else signed_byte:=b-256;
+end;
+@#
+function get_two_bytes:integer; {returns the next two bytes, unsigned}
+var a,@!b:eight_bits;
+begin read_byte(dvi_file,a); read_byte(dvi_file,b);
+cur_loc:=cur_loc+2;
+get_two_bytes:=a*256+b;
+end;
+@#
+function signed_pair:integer; {returns the next two bytes, signed}
+var a,@!b:eight_bits;
+begin read_byte(dvi_file,a); read_byte(dvi_file,b);
+cur_loc:=cur_loc+2;
+if a<128 then signed_pair:=a*256+b
+else signed_pair:=(a-256)*256+b;
+end;
+@#
+function get_three_bytes:integer; {returns the next three bytes, unsigned}
+var a,@!b,@!c:eight_bits;
+begin read_byte(dvi_file,a); read_byte(dvi_file,b); read_byte(dvi_file,c);
+cur_loc:=cur_loc+3;
+get_three_bytes:=(a*256+b)*256+c;
+end;
+@#
+function signed_trio:integer; {returns the next three bytes, signed}
+var a,@!b,@!c:eight_bits;
+begin read_byte(dvi_file,a); read_byte(dvi_file,b); read_byte(dvi_file,c);
+cur_loc:=cur_loc+3;
+if a<128 then signed_trio:=(a*256+b)*256+c
+else signed_trio:=((a-256)*256+b)*256+c;
+end;
+@#
+function signed_quad:integer; {returns the next four bytes, signed}
+var a,@!b,@!c,@!d:eight_bits;
+begin read_byte(dvi_file,a); read_byte(dvi_file,b); read_byte(dvi_file,c); read_byte(dvi_file,d);
+cur_loc:=cur_loc+4;
+if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
+else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [28] Redefine dvi_length() and move_to_byte().
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function dvi_length:integer;
+begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file);
+end;
+@#
+procedure move_to_byte(n:integer);
+begin set_pos(dvi_file,n); cur_loc:=n;
+end;
+@y
+@p function dvi_length:integer;
+begin dvi_length:=flength(dvi_file);
+end;
+@#
+procedure move_to_byte(n:integer);
+begin bseek(dvi_file,n); cur_loc:=n;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [45] Define term_in and term_out and declare dvityout, first_input
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!term_in:text_file; {the terminal, considered as an input file}
+@!term_out:text_file; {the terminal, considered as an output file}
+@y
+and |term_out| for terminal output.
+@^system dependencies@>
+
+@d term_in==input
+@d term_out==output
+
+@<Glob...@>=
+@!buffer:array[0..terminal_line_length] of ASCII_code;
+@!dvityout:text;
+@!first_input:boolean; {true until |input_ln| has been invoked}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [46] Define update_terminal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush_stdout {empty the terminal output buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [47] Remove call to reset(term_in) and initial eoln test
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+begin update_terminal; reset(term_in);
+if eoln(term_in) then read_ln(term_in);
+@y
+begin update_terminal;
+if first_input then first_input:=false
+else if eoln(term_in) then read_ln(term_in);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [50] Remove call to rewrite(term_out)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+begin rewrite(term_out); {prepare the terminal for output}
+@y
+begin
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [64] Set default_directory_name
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d default_directory_name=='TeXfonts:' {change this to the correct name}
+@d default_directory_name_length=9 {change this to the correct length}
+
+@<Glob...@>=
+@!default_directory:packed array[1..default_directory_name_length] of char;
+@y
+Actually, under UNIX the standard area is defined in an external
+``texpaths.h'' file. And the users have a path searched for fonts,
+by setting the TEXFONTS environment variable.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [65] Remove initialization of now-defunct array
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ @<Set init...@>=
+default_directory:=default_directory_name;
+@y
+@ (No initialization to be done. Keep this module to preserve numbering.)
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [66] Fix addition of ".tfm" suffix for portability and keep lowercase
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font. This usually means that we need to
+prepend the name of the default directory, and
+to append the suffix `\.{.TFM}'. Furthermore, we change lower case letters
+to upper case, since |cur_name| is a \PASCAL\ string.
+@y
+@ The string |cur_name| is supposed to be set to the external name of the
+\.{TFM} file for the current font. This usually means that we need to,
+at most sites, append the
+suffix ``.tfm''.
+@z
+
+@x
+if p=0 then
+ begin for k:=1 to default_directory_name_length do
+ cur_name[k]:=default_directory[k];
+ r:=default_directory_name_length;
+ end
+else r:=0;
+@y
+r:=0;
+@z
+
+@x
+ if (names[k]>="a")and(names[k]<="z") then
+ cur_name[r]:=xchr[names[k]-@'40]
+ else cur_name[r]:=xchr[names[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='T'; cur_name[r+3]:='F'; cur_name[r+4]:='M'
+@y
+ cur_name[r]:=xchr[names[k]];
+ end;
+cur_name[r+1]:='.'; cur_name[r+2]:='t'; cur_name[r+3]:='f'; cur_name[r+4]:='m';
+cur_name[r+5]:=chr(0)
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [105] Reopen DVI file after postamble has been checked
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+if not eof(dvi_file) then bad_dvi('signature in byte ',cur_loc-1:1,
+@.signature...should be...@>
+ ' should be 223')
+else if cur_loc<k+4 then
+ print_ln('not enough signature bytes at end of file (',
+@.not enough signature bytes...@>
+ cur_loc-k:1,')');
+@y
+if not eof(dvi_file) then bad_dvi('signature in byte ',cur_loc-1:1,
+@.signature...should be...@>
+ ' should be 223');
+if cur_loc<k+4 then
+ print_ln('not enough signature bytes at end of file (',
+@.not enough signature bytes...@>
+ cur_loc-k:1,')');
+reset(dvi_file,dvi_name) {the UNIX routines may have closed the file}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [107] Check usage; also print newline at end of program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+dialog; {set up all the options}
+@y
+if argc <> 2 then
+ begin write_ln('Usage: dvitype <dvi-file>'); goto final_end;
+ end;
+first_input:=true; dialog; {set up all the options}
+@z
+@x
+final_end:end.
+@y
+final_end: print_ln(' '); end.
+@z
diff --git a/systems/knuth/local/texware/dvitype.p b/systems/knuth/local/texware/dvitype.p
new file mode 100644
index 0000000000..71262aa3d8
--- /dev/null
+++ b/systems/knuth/local/texware/dvitype.p
@@ -0,0 +1,604 @@
+{3:}program DVItype(input,output);label{4:}9999,30;{:4}const{5:}
+maxfonts=100;maxwidths=10000;linelength=79;terminallinelength=150;
+stacksize=100;namesize=1000;namelength=100;{:5}type{8:}
+ASCIIcode=32..126;{:8}{9:}
+UNIXfilename=packed array[1..namelength]of char;{:9}{21:}
+eightbits=ByteCard;bytefile=packed file of eightbits;{:21}var{10:}
+xord:array[char]of ASCIIcode;xchr:array[0..255]of char;{:10}{22:}
+dvifile:bytefile;tfmfile:bytefile;{:22}{24:}curloc:integer;
+curname,realnameoffile:external UNIXfilename;dviname:UNIXfilename;{:24}
+{25:}b0,b1,b2,b3:eightbits;{:25}{30:}
+fontnum:array[0..maxfonts]of integer;
+fontname:array[0..maxfonts]of 1..namesize;
+names:array[1..namesize]of ASCIIcode;
+fontchecksum:array[0..maxfonts]of integer;
+fontscaledsize:array[0..maxfonts]of integer;
+fontdesignsize:array[0..maxfonts]of integer;
+fontspace:array[0..maxfonts]of integer;
+fontbc:array[0..maxfonts]of integer;fontec:array[0..maxfonts]of integer;
+widthbase:array[0..maxfonts]of integer;
+width:array[0..maxwidths]of integer;nf:0..maxfonts;
+widthptr:0..maxwidths;{:30}{33:}inwidth:array[0..255]of integer;
+tfmchecksum:integer;tfmdesignsize:integer;tfmconv:real;{:33}{39:}
+pixelwidth:array[0..maxwidths]of integer;conv:real;trueconv:real;
+numerator,denominator:integer;mag:integer;{:39}{41:}outmode:0..4;
+maxpages:integer;resolution:real;newmag:integer;{:41}{42:}
+startcount:array[0..9]of integer;startthere:array[0..9]of boolean;
+startvals:0..9;count:array[0..9]of integer;{:42}{45:}
+buffer:array[0..terminallinelength]of ASCIIcode;dvityout:text;
+firstinput:boolean;{:45}{48:}bufptr:0..terminallinelength;{:48}{57:}
+inpostamble:boolean;{:57}{67:}textptr:0..linelength;
+textbuf:array[1..linelength]of ASCIIcode;{:67}{72:}
+h,v,w,x,y,z,hh,vv:integer;
+hstack,vstack,wstack,xstack,ystack,zstack:array[0..stacksize]of integer;
+hhstack,vvstack:array[0..stacksize]of integer;{:72}{73:}maxv:integer;
+maxh:integer;maxs:integer;maxvsofar,maxhsofar,maxssofar:integer;
+totalpages:integer;pagecount:integer;{:73}{78:}s:integer;ss:integer;
+curfont:integer;showing:boolean;{:78}{97:}oldbackpointer:integer;
+newbackpointer:integer;started:boolean;{:97}{101:}postloc:integer;
+firstbackpointer:integer;startloc:integer;afterpre:integer;{:101}{108:}
+k,m,n,p,q:integer;{:108}
+#include "dvityext.h"
+procedure initialize;var i:integer;begin setpaths;
+rewrite(dvityout,'dvitype.out');
+writeln(dvityout,'This is DVItype, Version 3.6 for Linux');{11:}
+for i:=0 to 31 do xchr[i]:='?';xchr[32]:=' ';xchr[33]:='!';
+xchr[34]:='"';xchr[35]:='#';xchr[36]:='$';xchr[37]:='%';xchr[38]:='&';
+xchr[39]:='''';xchr[40]:='(';xchr[41]:=')';xchr[42]:='*';xchr[43]:='+';
+xchr[44]:=',';xchr[45]:='-';xchr[46]:='.';xchr[47]:='/';xchr[48]:='0';
+xchr[49]:='1';xchr[50]:='2';xchr[51]:='3';xchr[52]:='4';xchr[53]:='5';
+xchr[54]:='6';xchr[55]:='7';xchr[56]:='8';xchr[57]:='9';xchr[58]:=':';
+xchr[59]:=';';xchr[60]:='<';xchr[61]:='=';xchr[62]:='>';xchr[63]:='?';
+xchr[64]:='@';xchr[65]:='A';xchr[66]:='B';xchr[67]:='C';xchr[68]:='D';
+xchr[69]:='E';xchr[70]:='F';xchr[71]:='G';xchr[72]:='H';xchr[73]:='I';
+xchr[74]:='J';xchr[75]:='K';xchr[76]:='L';xchr[77]:='M';xchr[78]:='N';
+xchr[79]:='O';xchr[80]:='P';xchr[81]:='Q';xchr[82]:='R';xchr[83]:='S';
+xchr[84]:='T';xchr[85]:='U';xchr[86]:='V';xchr[87]:='W';xchr[88]:='X';
+xchr[89]:='Y';xchr[90]:='Z';xchr[91]:='[';xchr[92]:='\';xchr[93]:=']';
+xchr[94]:='^';xchr[95]:='_';xchr[96]:='`';xchr[97]:='a';xchr[98]:='b';
+xchr[99]:='c';xchr[100]:='d';xchr[101]:='e';xchr[102]:='f';
+xchr[103]:='g';xchr[104]:='h';xchr[105]:='i';xchr[106]:='j';
+xchr[107]:='k';xchr[108]:='l';xchr[109]:='m';xchr[110]:='n';
+xchr[111]:='o';xchr[112]:='p';xchr[113]:='q';xchr[114]:='r';
+xchr[115]:='s';xchr[116]:='t';xchr[117]:='u';xchr[118]:='v';
+xchr[119]:='w';xchr[120]:='x';xchr[121]:='y';xchr[122]:='z';
+xchr[123]:='{';xchr[124]:='|';xchr[125]:='}';xchr[126]:='~';
+for i:=127 to 255 do xchr[i]:='?';{:11}{12:}
+for i:=0 to 255 do xord[chr(i)]:=32;
+for i:=32 to 126 do xord[xchr[i]]:=i;{:12}{31:}nf:=0;widthptr:=0;
+fontname[0]:=1;fontspace[maxfonts]:=0;fontbc[maxfonts]:=1;
+fontec[maxfonts]:=0;{:31}{43:}outmode:=4;maxpages:=1000000;startvals:=0;
+startthere[0]:=false;{:43}{58:}inpostamble:=false;{:58}{68:}textptr:=0;
+{:68}{74:}maxv:=2147483548;maxh:=2147483548;maxs:=stacksize+1;
+maxvsofar:=0;maxhsofar:=0;maxssofar:=0;pagecount:=0;{:74}{98:}
+oldbackpointer:=-1;started:=false;{:98}end;{:3}{7:}procedure jumpout;
+begin goto 9999;end;{:7}{23:}procedure opendvifile;
+begin argv(1,curname);
+if testaccess(4,0)then begin dviname:=realnameoffile;
+reset(dvifile,dviname)end else begin writeln('DVI file not found');
+jumpout;end;curloc:=0;end;procedure opentfmfile;
+begin if testaccess(4,3)then reset(tfmfile,realnameoffile)else begin
+writeln('TFM file not found: ',curname);jumpout end;end;{:23}{26:}
+procedure readtfmword;begin read(tfmfile,b0);read(tfmfile,b1);
+read(tfmfile,b2);read(tfmfile,b3);end;{:26}{27:}
+function getbyte:integer;var b:eightbits;begin readbyte(dvifile,b);
+curloc:=curloc+1;getbyte:=b;end;function signedbyte:integer;
+var b:eightbits;begin readbyte(dvifile,b);curloc:=curloc+1;
+if b<128 then signedbyte:=b else signedbyte:=b-256;end;
+function gettwobytes:integer;var a,b:eightbits;
+begin readbyte(dvifile,a);readbyte(dvifile,b);curloc:=curloc+2;
+gettwobytes:=a*256+b;end;function signedpair:integer;var a,b:eightbits;
+begin readbyte(dvifile,a);readbyte(dvifile,b);curloc:=curloc+2;
+if a<128 then signedpair:=a*256+b else signedpair:=(a-256)*256+b;end;
+function getthreebytes:integer;var a,b,c:eightbits;
+begin readbyte(dvifile,a);readbyte(dvifile,b);readbyte(dvifile,c);
+curloc:=curloc+3;getthreebytes:=(a*256+b)*256+c;end;
+function signedtrio:integer;var a,b,c:eightbits;
+begin readbyte(dvifile,a);readbyte(dvifile,b);readbyte(dvifile,c);
+curloc:=curloc+3;
+if a<128 then signedtrio:=(a*256+b)*256+c else signedtrio:=((a-256)*256+
+b)*256+c;end;function signedquad:integer;var a,b,c,d:eightbits;
+begin readbyte(dvifile,a);readbyte(dvifile,b);readbyte(dvifile,c);
+readbyte(dvifile,d);curloc:=curloc+4;
+if a<128 then signedquad:=((a*256+b)*256+c)*256+d else signedquad:=(((a
+-256)*256+b)*256+c)*256+d;end;{:27}{28:}function dvilength:integer;
+begin dvilength:=flength(dvifile);end;procedure movetobyte(n:integer);
+begin bseek(dvifile,n);curloc:=n;end;{:28}{32:}
+procedure printfont(f:integer);var k:0..namesize;
+begin if f=maxfonts then write(dvityout,'UNDEFINED!')else begin for k:=
+fontname[f]to fontname[f+1]-1 do write(dvityout,xchr[names[k]]);end;end;
+{:32}{34:}function inTFM(z:integer):boolean;label 9997,9998,9999;
+var k:integer;lh:integer;nw:integer;wp:0..maxwidths;alpha,beta:integer;
+begin{35:}readtfmword;lh:=b2*256+b3;readtfmword;fontbc[nf]:=b0*256+b1;
+fontec[nf]:=b2*256+b3;
+if fontec[nf]<fontbc[nf]then fontbc[nf]:=fontec[nf]+1;
+if widthptr+fontec[nf]-fontbc[nf]+1>maxwidths then begin writeln(
+dvityout,'---not loaded, DVItype needs larger width table');goto 9998;
+end;wp:=widthptr+fontec[nf]-fontbc[nf]+1;readtfmword;nw:=b0*256+b1;
+if(nw=0)or(nw>256)then goto 9997;
+for k:=1 to 3+lh do begin if eof(tfmfile)then goto 9997;readtfmword;
+if k=4 then if b0<128 then tfmchecksum:=((b0*256+b1)*256+b2)*256+b3 else
+tfmchecksum:=(((b0-256)*256+b1)*256+b2)*256+b3 else if k=5 then if b0<
+128 then tfmdesignsize:=round(tfmconv*(((b0*256+b1)*256+b2)*256+b3))else
+goto 9997;end;{:35};{36:}
+if wp>0 then for k:=widthptr to wp-1 do begin readtfmword;
+if b0>nw then goto 9997;width[k]:=b0;end;{:36};{37:}{38:}
+begin alpha:=16;while z>=8388608 do begin z:=z div 2;alpha:=alpha+alpha;
+end;beta:=256 div alpha;alpha:=alpha*z;end{:38};
+for k:=0 to nw-1 do begin readtfmword;
+inwidth[k]:=(((((b3*z)div 256)+(b2*z))div 256)+(b1*z))div beta;
+if b0>0 then if b0<255 then goto 9997 else inwidth[k]:=inwidth[k]-alpha;
+end{:37};{40:}if inwidth[0]<>0 then goto 9997;
+widthbase[nf]:=widthptr-fontbc[nf];
+if wp>0 then for k:=widthptr to wp-1 do if width[k]=0 then begin width[k
+]:=2147483647;pixelwidth[k]:=0;
+end else begin width[k]:=inwidth[width[k]];
+pixelwidth[k]:=round(conv*(width[k]));end{:40};widthptr:=wp;inTFM:=true;
+goto 9999;9997:writeln(dvityout,'---not loaded, TFM file is bad');
+9998:inTFM:=false;9999:end;{:34}{44:}function startmatch:boolean;
+var k:0..9;match:boolean;begin match:=true;
+for k:=0 to startvals do if startthere[k]and(startcount[k]<>count[k])
+then match:=false;startmatch:=match;end;{:44}{47:}procedure inputln;
+var k:0..terminallinelength;begin flushstdout;
+if firstinput then firstinput:=false else if eoln(input)then readln(
+input);k:=0;
+while(k<terminallinelength)and not eoln(input)do begin buffer[k]:=xord[
+input^];k:=k+1;get(input);end;buffer[k]:=32;end;{:47}{49:}
+function getinteger:integer;var x:integer;negative:boolean;
+begin if buffer[bufptr]=45 then begin negative:=true;bufptr:=bufptr+1;
+end else negative:=false;x:=0;
+while(buffer[bufptr]>=48)and(buffer[bufptr]<=57)do begin x:=10*x+buffer[
+bufptr]-48;bufptr:=bufptr+1;end;
+if negative then getinteger:=-x else getinteger:=x;end;{:49}{50:}
+procedure dialog;label 1,2,3,4,5;var k:integer;
+begin writeln(output,'This is DVItype, Version 3.6 for Linux');{51:}
+1:write(output,'Output level (default=4, ? for help): ');outmode:=4;
+inputln;
+if buffer[0]<>32 then if(buffer[0]>=48)and(buffer[0]<=52)then outmode:=
+buffer[0]-48 else begin write(output,'Type 4 for complete listing,');
+write(output,' 0 for errors and fonts only,');
+writeln(output,' 1 or 2 or 3 for something in between.');goto 1;end{:51}
+;{52:}2:write(output,'Starting page (default=*): ');startvals:=0;
+startthere[0]:=false;inputln;bufptr:=0;k:=0;
+if buffer[0]<>32 then repeat if buffer[bufptr]=42 then begin startthere[
+k]:=false;bufptr:=bufptr+1;end else begin startthere[k]:=true;
+startcount[k]:=getinteger;end;
+if(k<9)and(buffer[bufptr]=46)then begin k:=k+1;bufptr:=bufptr+1;
+end else if buffer[bufptr]=32 then startvals:=k else begin write(output,
+'Type, e.g., 1.*.-5 to specify the ');
+writeln(output,'first page with \count0=1, \count2=-5.');goto 2;end;
+until startvals=k{:52};{53:}
+3:write(output,'Maximum number of pages (default=1000000): ');
+maxpages:=1000000;inputln;bufptr:=0;
+if buffer[0]<>32 then begin maxpages:=getinteger;
+if maxpages<=0 then begin writeln(output,
+'Please type a positive number.');goto 3;end;end{:53};{54:}
+4:write(output,'Assumed device resolution');
+write(output,' in pixels per inch (default=300/1): ');resolution:=300.0;
+inputln;bufptr:=0;if buffer[0]<>32 then begin k:=getinteger;
+if(k>0)and(buffer[bufptr]=47)and(buffer[bufptr+1]>48)and(buffer[bufptr+1
+]<=57)then begin bufptr:=bufptr+1;resolution:=k/getinteger;
+end else begin write(output,'Type a ratio of positive integers;');
+writeln(output,' (1 pixel per mm would be 254/10).');goto 4;end;end{:54}
+;{55:}
+5:write(output,'New magnification (default=0 to keep the old one): ');
+newmag:=0;inputln;bufptr:=0;
+if buffer[0]<>32 then if(buffer[0]>=48)and(buffer[0]<=57)then newmag:=
+getinteger else begin write(output,
+'Type a positive integer to override ');
+writeln(output,'the magnification in the DVI file.');goto 5;end{:55};
+{56:}writeln(dvityout,'Options selected:');
+write(dvityout,' Starting page = ');
+for k:=0 to startvals do begin if startthere[k]then write(dvityout,
+startcount[k]:1)else write(dvityout,'*');
+if k<startvals then write(dvityout,'.')else writeln(dvityout,' ');end;
+writeln(dvityout,' Maximum number of pages = ',maxpages:1);
+write(dvityout,' Output level = ',outmode:1);
+case outmode of 0:writeln(dvityout,
+' (showing bops, fonts, and error messages only)');
+1:writeln(dvityout,' (terse)');2:writeln(dvityout,' (mnemonics)');
+3:writeln(dvityout,' (verbose)');
+4:if true then writeln(dvityout,' (the works)')else begin outmode:=3;
+writeln(dvityout,' (the works: same as level 3 in this DVItype)');end;
+end;
+writeln(dvityout,' Resolution = ',resolution:12:8,' pixels per inch');
+if newmag>0 then writeln(dvityout,' New magnification factor = ',newmag
+/1000:8:3){:56};end;{:50}{59:}procedure definefont(e:integer);
+var f:0..maxfonts;p:integer;n:integer;c,q,d,m:integer;r:0..namelength;
+j,k:0..namesize;mismatch:boolean;
+begin if nf=maxfonts then begin write(dvityout,' ',
+'DVItype capacity exceeded (max fonts=',maxfonts:1,')!');jumpout;end;
+fontnum[nf]:=e;f:=0;while fontnum[f]<>e do f:=f+1;{61:}c:=signedquad;
+fontchecksum[nf]:=c;q:=signedquad;fontscaledsize[nf]:=q;d:=signedquad;
+fontdesignsize[nf]:=d;
+if(q<=0)or(d<=0)then m:=1000 else m:=round((1000.0*conv*q)/(trueconv*d))
+;p:=getbyte;n:=getbyte;
+if fontname[nf]+n+p>namesize then begin write(dvityout,' ',
+'DVItype capacity exceeded (name size=',namesize:1,')!');jumpout;end;
+fontname[nf+1]:=fontname[nf]+n+p;
+if showing then write(dvityout,': ')else write(dvityout,'Font ',e:1,': '
+);
+if n+p=0 then write(dvityout,'null font name!')else for k:=fontname[nf]
+to fontname[nf+1]-1 do names[k]:=getbyte;printfont(nf);
+if not showing then if m<>1000 then write(dvityout,' scaled ',m:1){:61};
+if((outmode=4)and inpostamble)or((outmode<4)and not inpostamble)then
+begin if f<nf then writeln(dvityout,'---this font was already defined!')
+;end else begin if f=nf then writeln(dvityout,
+'---this font wasn''t loaded before!');end;if f=nf then{62:}begin{66:}
+for k:=1 to namelength do curname[k]:=' ';r:=0;
+for k:=fontname[nf]to fontname[nf+1]-1 do begin r:=r+1;
+if r+4>namelength then begin write(dvityout,' ',
+'DVItype capacity exceeded (max font name length=',namelength:1,')!');
+jumpout;end;curname[r]:=xchr[names[k]];end;curname[r+1]:='.';
+curname[r+2]:='t';curname[r+3]:='f';curname[r+4]:='m';
+curname[r+5]:=chr(0){:66};opentfmfile;
+if eof(tfmfile)then write(dvityout,
+'---not loaded, TFM file can''t be opened!')else begin if(q<=0)or(q>=
+134217728)then write(dvityout,'---not loaded, bad scale (',q:1,')!')else
+if(d<=0)or(d>=134217728)then write(dvityout,
+'---not loaded, bad design size (',d:1,')!')else if inTFM(q)then{63:}
+begin fontspace[nf]:=q div 6;
+if(c<>0)and(tfmchecksum<>0)and(c<>tfmchecksum)then begin writeln(
+dvityout,'---beware: check sums do not agree!');
+writeln(dvityout,' (',c:1,' vs. ',tfmchecksum:1,')');
+write(dvityout,' ');end;
+if abs(tfmdesignsize-d)>2 then begin writeln(dvityout,
+'---beware: design sizes do not agree!');
+writeln(dvityout,' (',d:1,' vs. ',tfmdesignsize:1,')');
+write(dvityout,' ');end;
+write(dvityout,'---loaded at size ',q:1,' DVI units');
+d:=round((100.0*conv*q)/(trueconv*d));
+if d<>100 then begin writeln(dvityout,' ');
+write(dvityout,' (this font is magnified ',d:1,'%)');end;nf:=nf+1;
+end{:63};end;if outmode=0 then writeln(dvityout,' ');end{:62}else{60:}
+begin if fontchecksum[f]<>c then writeln(dvityout,
+'---check sum doesn''t match previous definition!');
+if fontscaledsize[f]<>q then writeln(dvityout,
+'---scaled size doesn''t match previous definition!');
+if fontdesignsize[f]<>d then writeln(dvityout,
+'---design size doesn''t match previous definition!');j:=fontname[f];
+k:=fontname[nf];
+if fontname[f+1]-j<>fontname[nf+1]-k then mismatch:=true else begin
+mismatch:=false;
+while j<fontname[f+1]do begin if names[j]<>names[k]then mismatch:=true;
+j:=j+1;k:=k+1;end;end;
+if mismatch then writeln(dvityout,
+'---font name doesn''t match previous definition!');end{:60};end;{:59}
+{69:}procedure flushtext;var k:0..linelength;
+begin if textptr>0 then begin if outmode>0 then begin write(dvityout,'['
+);for k:=1 to textptr do write(dvityout,xchr[textbuf[k]]);
+writeln(dvityout,']');end;textptr:=0;end;end;{:69}{70:}
+procedure outtext(c:ASCIIcode);
+begin if textptr=linelength-2 then flushtext;textptr:=textptr+1;
+textbuf[textptr]:=c;end;{:70}{75:}
+function firstpar(o:eightbits):integer;
+begin case o of 0,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:firstpar:=o
+-0;128,133,235,239,243:firstpar:=getbyte;
+129,134,236,240,244:firstpar:=gettwobytes;
+130,135,237,241,245:firstpar:=getthreebytes;
+143,148,153,157,162,167:firstpar:=signedbyte;
+144,149,154,158,163,168:firstpar:=signedpair;
+145,150,155,159,164,169:firstpar:=signedtrio;
+131,132,136,137,146,151,156,160,165,170,238,242,246:firstpar:=signedquad
+;138,139,140,141,142,247,248,249,250,251,252,253,254,255:firstpar:=0;
+147:firstpar:=w;152:firstpar:=x;161:firstpar:=y;166:firstpar:=z;
+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:firstpar:=o-171;end;end;{:75}
+{76:}function rulepixels(x:integer):integer;var n:integer;
+begin n:=trunc(conv*x);
+if n<conv*x then rulepixels:=n+1 else rulepixels:=n;end;{:76}{79:}{82:}
+function specialcases(o:eightbits;p,a:integer):boolean;
+label 46,44,30,9998;var q:integer;k:integer;badchar:boolean;
+pure:boolean;vvv:integer;begin pure:=true;case o of{85:}
+157,158,159,160:begin if abs(p)>=5*fontspace[curfont]then vv:=round(conv
+*(v+p))else vv:=vv+round(conv*(p));if outmode>0 then begin flushtext;
+showing:=true;write(dvityout,a:1,': ','down',o-156:1,' ',p:1);end;
+goto 44;end;161,162,163,164,165:begin y:=p;
+if abs(p)>=5*fontspace[curfont]then vv:=round(conv*(v+p))else vv:=vv+
+round(conv*(p));if outmode>0 then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','y',o-161:1,' ',p:1);end;goto 44;end;
+166,167,168,169,170:begin z:=p;
+if abs(p)>=5*fontspace[curfont]then vv:=round(conv*(v+p))else vv:=vv+
+round(conv*(p));if outmode>0 then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','z',o-166:1,' ',p:1);end;goto 44;end;{:85}{86:}
+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:begin if outmode>0 then begin
+flushtext;showing:=true;write(dvityout,a:1,': ','fntnum',p:1);end;
+goto 46;end;235,236,237,238:begin if outmode>0 then begin flushtext;
+showing:=true;write(dvityout,a:1,': ','fnt',o-234:1,' ',p:1);end;
+goto 46;end;243,244,245,246:begin if outmode>0 then begin flushtext;
+showing:=true;write(dvityout,a:1,': ','fntdef',o-242:1,' ',p:1);end;
+definefont(p);goto 30;end;{:86}239,240,241,242:{87:}
+begin if outmode>0 then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','xxx ''');end;badchar:=false;
+if p<0 then if not showing then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','string of negative length!');
+end else write(dvityout,' ','string of negative length!');
+for k:=1 to p do begin q:=getbyte;if(q<32)or(q>126)then badchar:=true;
+if showing then write(dvityout,xchr[q]);end;
+if showing then write(dvityout,'''');
+if badchar then if not showing then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','non-ASCII character in xxx command!');
+end else write(dvityout,' ','non-ASCII character in xxx command!');
+goto 30;end{:87};247:begin if not showing then begin flushtext;
+showing:=true;
+write(dvityout,a:1,': ','preamble command within a page!');
+end else write(dvityout,' ','preamble command within a page!');
+goto 9998;end;248,249:begin if not showing then begin flushtext;
+showing:=true;
+write(dvityout,a:1,': ','postamble command within a page!');
+end else write(dvityout,' ','postamble command within a page!');
+goto 9998;end;otherwise begin if not showing then begin flushtext;
+showing:=true;write(dvityout,a:1,': ','undefined command ',o:1,'!');
+end else write(dvityout,' ','undefined command ',o:1,'!');goto 30;
+end end;44:{92:}
+if(v>0)and(p>0)then if v>2147483647-p then begin if not showing then
+begin flushtext;showing:=true;
+write(dvityout,a:1,': ','arithmetic overflow! parameter changed from ',p
+:1,' to ',2147483647-v:1);end else write(dvityout,' ',
+'arithmetic overflow! parameter changed from ',p:1,' to ',2147483647-v:1
+);p:=2147483647-v;end;
+if(v<0)and(p<0)then if-v>p+2147483647 then begin if not showing then
+begin flushtext;showing:=true;
+write(dvityout,a:1,': ','arithmetic overflow! parameter changed from ',p
+:1,' to ',(-v)-2147483647:1);end else write(dvityout,' ',
+'arithmetic overflow! parameter changed from ',p:1,' to ',(-v)
+-2147483647:1);p:=(-v)-2147483647;end;vvv:=round(conv*(v+p));
+if abs(vvv-vv)>2 then if vvv>vv then vv:=vvv-2 else vv:=vvv+2;
+if showing then if outmode>2 then begin write(dvityout,' v:=',v:1);
+if p>=0 then write(dvityout,'+');
+write(dvityout,p:1,'=',v+p:1,', vv:=',vv:1);end;v:=v+p;
+if abs(v)>maxvsofar then begin if abs(v)>maxv+99 then begin if not
+showing then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','warning: |v|>',maxv:1,'!');
+end else write(dvityout,' ','warning: |v|>',maxv:1,'!');maxv:=abs(v);
+end;maxvsofar:=abs(v);end;goto 30{:92};46:{94:}fontnum[nf]:=p;
+curfont:=0;while fontnum[curfont]<>p do curfont:=curfont+1;
+if curfont=nf then begin curfont:=maxfonts;
+if not showing then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','invalid font selection: font ',p:1,
+' was never defined!');
+end else write(dvityout,' ','invalid font selection: font ',p:1,
+' was never defined!');end;
+if showing then if outmode>2 then begin write(dvityout,
+' current font is ');printfont(curfont);end;goto 30{:94};
+9998:pure:=false;30:specialcases:=pure;end;{:82}function dopage:boolean;
+label 41,42,43,45,30,9998,9999;var o:eightbits;p,q:integer;a:integer;
+hhh:integer;begin curfont:=maxfonts;s:=0;h:=0;v:=0;w:=0;x:=0;y:=0;z:=0;
+hh:=0;vv:=0;while true do{80:}begin a:=curloc;showing:=false;o:=getbyte;
+p:=firstpar(o);
+if eof(dvifile)then begin write(dvityout,' ','Bad DVI file: ',
+'the file ended prematurely','!');jumpout;end;{81:}if o<128 then{88:}
+begin if(o>32)and(o<=126)then begin outtext(p);
+if outmode>1 then begin showing:=true;
+write(dvityout,a:1,': ','setchar',p:1);end;
+end else if outmode>0 then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','setchar',p:1);end;goto 41;end{:88}
+else case o of 128,129,130,131:begin if outmode>0 then begin flushtext;
+showing:=true;write(dvityout,a:1,': ','set',o-127:1,' ',p:1);end;
+goto 41;end;133,134,135,136:begin if outmode>0 then begin flushtext;
+showing:=true;write(dvityout,a:1,': ','put',o-132:1,' ',p:1);end;
+goto 41;end;132:begin if outmode>0 then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','setrule');end;goto 42;end;
+137:begin if outmode>0 then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','putrule');end;goto 42;end;{83:}
+138:begin if outmode>1 then begin showing:=true;
+write(dvityout,a:1,': ','nop');end;goto 30;end;
+139:begin if not showing then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','bop occurred before eop!');
+end else write(dvityout,' ','bop occurred before eop!');goto 9998;end;
+140:begin if outmode>0 then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','eop');end;
+if s<>0 then if not showing then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','stack not empty at end of page (level ',s:1,
+')!');
+end else write(dvityout,' ','stack not empty at end of page (level ',s:1
+,')!');dopage:=true;writeln(dvityout,' ');goto 9999;end;
+141:begin if outmode>0 then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','push');end;
+if s=maxssofar then begin maxssofar:=s+1;
+if s=maxs then if not showing then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','deeper than claimed in postamble!');
+end else write(dvityout,' ','deeper than claimed in postamble!');
+if s=stacksize then begin if not showing then begin flushtext;
+showing:=true;
+write(dvityout,a:1,': ','DVItype capacity exceeded (stack size=',
+stacksize:1,')');
+end else write(dvityout,' ','DVItype capacity exceeded (stack size=',
+stacksize:1,')');goto 9998;end;end;hstack[s]:=h;vstack[s]:=v;
+wstack[s]:=w;xstack[s]:=x;ystack[s]:=y;zstack[s]:=z;hhstack[s]:=hh;
+vvstack[s]:=vv;s:=s+1;ss:=s-1;goto 45;end;
+142:begin if outmode>0 then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','pop');end;
+if s=0 then if not showing then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','(illegal at level zero)!');
+end else write(dvityout,' ','(illegal at level zero)!')else begin s:=s-1
+;hh:=hhstack[s];vv:=vvstack[s];h:=hstack[s];v:=vstack[s];w:=wstack[s];
+x:=xstack[s];y:=ystack[s];z:=zstack[s];end;ss:=s;goto 45;end;{:83}{84:}
+143,144,145,146:begin if(p>=fontspace[curfont])or(p<=-4*fontspace[
+curfont])then begin outtext(32);hh:=round(conv*(h+p));
+end else hh:=hh+round(conv*(p));if outmode>1 then begin showing:=true;
+write(dvityout,a:1,': ','right',o-142:1,' ',p:1);end;q:=p;goto 43;end;
+147,148,149,150,151:begin w:=p;
+if(p>=fontspace[curfont])or(p<=-4*fontspace[curfont])then begin outtext(
+32);hh:=round(conv*(h+p));end else hh:=hh+round(conv*(p));
+if outmode>1 then begin showing:=true;
+write(dvityout,a:1,': ','w',o-147:1,' ',p:1);end;q:=p;goto 43;end;
+152,153,154,155,156:begin x:=p;
+if(p>=fontspace[curfont])or(p<=-4*fontspace[curfont])then begin outtext(
+32);hh:=round(conv*(h+p));end else hh:=hh+round(conv*(p));
+if outmode>1 then begin showing:=true;
+write(dvityout,a:1,': ','x',o-152:1,' ',p:1);end;q:=p;goto 43;end;{:84}
+otherwise if specialcases(o,p,a)then goto 30 else goto 9998 end{:81};
+41:{89:}
+if p<0 then p:=255-((-1-p)mod 256)else if p>=256 then p:=p mod 256;
+if(p<fontbc[curfont])or(p>fontec[curfont])then q:=2147483647 else q:=
+width[widthbase[curfont]+p];
+if q=2147483647 then begin if not showing then begin flushtext;
+showing:=true;
+write(dvityout,a:1,': ','character ',p:1,' invalid in font ');
+end else write(dvityout,' ','character ',p:1,' invalid in font ');
+printfont(curfont);if curfont<>maxfonts then write(dvityout,'!');end;
+if o>=133 then goto 30;
+if q=2147483647 then q:=0 else hh:=hh+pixelwidth[widthbase[curfont]+p];
+goto 43{:89};42:{90:}q:=signedquad;
+if showing then begin write(dvityout,' height ',p:1,', width ',q:1);
+if outmode>2 then if(p<=0)or(q<=0)then write(dvityout,' (invisible)')
+else write(dvityout,' (',rulepixels(p):1,'x',rulepixels(q):1,' pixels)')
+;end;if o=137 then goto 30;
+if showing then if outmode>2 then writeln(dvityout,' ');
+hh:=hh+rulepixels(q);goto 43{:90};43:{91:}
+if(h>0)and(q>0)then if h>2147483647-q then begin if not showing then
+begin flushtext;showing:=true;
+write(dvityout,a:1,': ','arithmetic overflow! parameter changed from ',q
+:1,' to ',2147483647-h:1);end else write(dvityout,' ',
+'arithmetic overflow! parameter changed from ',q:1,' to ',2147483647-h:1
+);q:=2147483647-h;end;
+if(h<0)and(q<0)then if-h>q+2147483647 then begin if not showing then
+begin flushtext;showing:=true;
+write(dvityout,a:1,': ','arithmetic overflow! parameter changed from ',q
+:1,' to ',(-h)-2147483647:1);end else write(dvityout,' ',
+'arithmetic overflow! parameter changed from ',q:1,' to ',(-h)
+-2147483647:1);q:=(-h)-2147483647;end;hhh:=round(conv*(h+q));
+if abs(hhh-hh)>2 then if hhh>hh then hh:=hhh-2 else hh:=hhh+2;
+if showing then if outmode>2 then begin write(dvityout,' h:=',h:1);
+if q>=0 then write(dvityout,'+');
+write(dvityout,q:1,'=',h+q:1,', hh:=',hh:1);end;h:=h+q;
+if abs(h)>maxhsofar then begin if abs(h)>maxh+99 then begin if not
+showing then begin flushtext;showing:=true;
+write(dvityout,a:1,': ','warning: |h|>',maxh:1,'!');
+end else write(dvityout,' ','warning: |h|>',maxh:1,'!');maxh:=abs(h);
+end;maxhsofar:=abs(h);end;goto 30{:91};45:{93:}
+if showing then if outmode>2 then begin writeln(dvityout,' ');
+write(dvityout,'level ',ss:1,':(h=',h:1,',v=',v:1,',w=',w:1,',x=',x:1,
+',y=',y:1,',z=',z:1,',hh=',hh:1,',vv=',vv:1,')');end;goto 30{:93};
+30:if showing then writeln(dvityout,' ');end{:80};
+9998:writeln(dvityout,'!');dopage:=false;9999:end;{:79}{95:}{99:}
+procedure scanbop;var k:0..255;
+begin repeat if eof(dvifile)then begin write(dvityout,' ',
+'Bad DVI file: ','the file ended prematurely','!');jumpout;end;
+k:=getbyte;if(k>=243)and(k<247)then begin definefont(firstpar(k));
+k:=138;end;until k<>138;
+if k=248 then inpostamble:=true else begin if k<>139 then begin write(
+dvityout,' ','Bad DVI file: ','byte ',curloc-1:1,' is not bop','!');
+jumpout;end;newbackpointer:=curloc-1;pagecount:=pagecount+1;
+for k:=0 to 9 do count[k]:=signedquad;
+if signedquad<>oldbackpointer then writeln(dvityout,
+'backpointer in byte ',curloc-4:1,' should be ',oldbackpointer:1,'!');
+oldbackpointer:=newbackpointer;end;end;{:99}
+procedure skippages(bopseen:boolean);label 9999;var p:integer;k:0..255;
+downthedrain:integer;begin showing:=false;
+while true do begin if not bopseen then begin scanbop;
+if inpostamble then goto 9999;
+if not started then if startmatch then begin started:=true;goto 9999;
+end;end;{96:}
+repeat if eof(dvifile)then begin write(dvityout,' ','Bad DVI file: ',
+'the file ended prematurely','!');jumpout;end;k:=getbyte;p:=firstpar(k);
+case k of 132,137:downthedrain:=signedquad;
+243,244,245,246:begin definefont(p);writeln(dvityout,' ');end;
+239,240,241,242:while p>0 do begin downthedrain:=getbyte;p:=p-1;end;
+139,247,248,249,250,251,252,253,254,255:begin write(dvityout,' ',
+'Bad DVI file: ','illegal command at byte ',curloc-1:1,'!');jumpout;end;
+otherwise end;until k=140;{:96};bopseen:=false;end;9999:end;{:95}{103:}
+procedure readpostamble;var k:integer;p,q,m:integer;
+begin showing:=false;postloc:=curloc-5;
+writeln(dvityout,'Postamble starts at byte ',postloc:1,'.');
+if signedquad<>numerator then writeln(dvityout,
+'numerator doesn''t match the preamble!');
+if signedquad<>denominator then writeln(dvityout,
+'denominator doesn''t match the preamble!');
+if signedquad<>mag then if newmag=0 then writeln(dvityout,
+'magnification doesn''t match the preamble!');maxv:=signedquad;
+maxh:=signedquad;write(dvityout,'maxv=',maxv:1,', maxh=',maxh:1);
+maxs:=gettwobytes;totalpages:=gettwobytes;
+writeln(dvityout,', maxstackdepth=',maxs:1,', totalpages=',totalpages:1)
+;if outmode<4 then{104:}
+begin if maxv+99<maxvsofar then writeln(dvityout,
+'warning: observed maxv was ',maxvsofar:1);
+if maxh+99<maxhsofar then writeln(dvityout,'warning: observed maxh was '
+,maxhsofar:1);if maxs<maxssofar then writeln(dvityout,
+'warning: observed maxstackdepth was ',maxssofar:1);
+if pagecount<>totalpages then writeln(dvityout,'there are really ',
+pagecount:1,' pages, not ',totalpages:1,'!');end{:104};{106:}
+repeat k:=getbyte;if(k>=243)and(k<247)then begin p:=firstpar(k);
+definefont(p);writeln(dvityout,' ');k:=138;end;until k<>138;
+if k<>249 then writeln(dvityout,'byte ',curloc-1:1,' is not postpost!'){
+:106};{105:}q:=signedquad;
+if q<>postloc then writeln(dvityout,'bad postamble pointer in byte ',
+curloc-4:1,'!');m:=getbyte;
+if m<>2 then writeln(dvityout,'identification in byte ',curloc-1:1,
+' should be ',2:1,'!');k:=curloc;m:=223;
+while(m=223)and not eof(dvifile)do m:=getbyte;
+if not eof(dvifile)then begin write(dvityout,' ','Bad DVI file: ',
+'signature in byte ',curloc-1:1,' should be 223','!');jumpout;end;
+if curloc<k+4 then writeln(dvityout,
+'not enough signature bytes at end of file (',curloc-k:1,')');
+reset(dvifile,dviname){:105};end;{:103}{107:}begin initialize;
+if argc<>2 then begin writeln('Usage: dvitype <dvi-file>');goto 9999;
+end;firstinput:=true;dialog;{109:}opendvifile;p:=getbyte;
+if p<>247 then begin write(dvityout,' ','Bad DVI file: ',
+'First byte isn''t start of preamble!','!');jumpout;end;p:=getbyte;
+if p<>2 then writeln(dvityout,'identification in byte 1 should be ',2:1,
+'!');{110:}numerator:=signedquad;denominator:=signedquad;
+if numerator<=0 then begin write(dvityout,' ','Bad DVI file: ',
+'numerator is ',numerator:1,'!');jumpout;end;
+if denominator<=0 then begin write(dvityout,' ','Bad DVI file: ',
+'denominator is ',denominator:1,'!');jumpout;end;
+writeln(dvityout,'numerator/denominator=',numerator:1,'/',denominator:1)
+;tfmconv:=(25400000.0/numerator)*(denominator/473628672)/16.0;
+conv:=(numerator/254000.0)*(resolution/denominator);mag:=signedquad;
+if newmag>0 then mag:=newmag else if mag<=0 then begin write(dvityout,
+' ','Bad DVI file: ','magnification is ',mag:1,'!');jumpout;end;
+trueconv:=conv;conv:=trueconv*(mag/1000.0);
+writeln(dvityout,'magnification=',mag:1,'; ',conv:16:8,
+' pixels per DVI unit'){:110};p:=getbyte;write(dvityout,'''');
+while p>0 do begin p:=p-1;write(dvityout,xchr[getbyte]);end;
+writeln(dvityout,'''');afterpre:=curloc{:109};
+if outmode=4 then begin{100:}n:=dvilength;
+if n<53 then begin write(dvityout,' ','Bad DVI file: ','only ',n:1,
+' bytes long','!');jumpout;end;m:=n-4;
+repeat if m=0 then begin write(dvityout,' ','Bad DVI file: ','all 223s',
+'!');jumpout;end;movetobyte(m);k:=getbyte;m:=m-1;until k<>223;
+if k<>2 then begin write(dvityout,' ','Bad DVI file: ','ID byte is ',k:1
+,'!');jumpout;end;movetobyte(m-3);q:=signedquad;
+if(q<0)or(q>m-33)then begin write(dvityout,' ','Bad DVI file: ',
+'post pointer ',q:1,' at byte ',m-3:1,'!');jumpout;end;movetobyte(q);
+k:=getbyte;
+if k<>248 then begin write(dvityout,' ','Bad DVI file: ','byte ',q:1,
+' is not post','!');jumpout;end;postloc:=q;
+firstbackpointer:=signedquad{:100};inpostamble:=true;readpostamble;
+inpostamble:=false;{102:}q:=postloc;p:=firstbackpointer;startloc:=-1;
+if p<0 then inpostamble:=true else begin repeat if p>q-46 then begin
+write(dvityout,' ','Bad DVI file: ','page link ',p:1,' after byte ',q:1,
+'!');jumpout;end;q:=p;movetobyte(q);k:=getbyte;
+if k=139 then pagecount:=pagecount+1 else begin write(dvityout,' ',
+'Bad DVI file: ','byte ',q:1,' is not bop','!');jumpout;end;
+for k:=0 to 9 do count[k]:=signedquad;p:=signedquad;
+if startmatch then begin startloc:=q;oldbackpointer:=p;end;until p<0;
+if startloc<0 then begin write(dvityout,' ',
+'starting page number could not be found!');jumpout;end;
+if oldbackpointer<0 then startloc:=afterpre;movetobyte(startloc);end;
+if pagecount<>totalpages then writeln(dvityout,'there are really ',
+pagecount:1,' pages, not ',totalpages:1,'!'){:102};end;skippages(false);
+if not inpostamble then{111:}
+begin while maxpages>0 do begin maxpages:=maxpages-1;
+writeln(dvityout,' ');
+write(dvityout,curloc-45:1,': beginning of page ');
+for k:=0 to startvals do begin write(dvityout,count[k]:1);
+if k<startvals then write(dvityout,'.')else writeln(dvityout,' ');end;
+if not dopage then begin write(dvityout,' ','Bad DVI file: ',
+'page ended unexpectedly','!');jumpout;end;scanbop;
+if inpostamble then goto 30;end;30:end{:111};
+if outmode<4 then begin if not inpostamble then skippages(true);
+if signedquad<>oldbackpointer then writeln(dvityout,
+'backpointer in byte ',curloc-4:1,' should be ',oldbackpointer:1,'!');
+readpostamble;end;9999:writeln(dvityout,' ');end.{:107}
diff --git a/systems/knuth/local/texware/pltotf.ch b/systems/knuth/local/texware/pltotf.ch
new file mode 100644
index 0000000000..72afbe0f3a
--- /dev/null
+++ b/systems/knuth/local/texware/pltotf.ch
@@ -0,0 +1,118 @@
+% Change file for the PLtoTF processor, for use with GNU Pascal.
+% (by Don Knuth, based on the work of Pavel Curtis in ../texware-sparc)
+
+% History:
+% 2000.04.30 Original version
+
+% The section numbers used in this file refer to the current nunmbers,
+% and, where different, also to those in the standard TeXware report (CS1097).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{PL\lowercase{to}TF changes for GNU Pascal}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is PLtoTF, Version 3.5' {printed when the program starts}
+@y
+@d banner=='This is PLtoTF, Version 3.5 for Linux'
+ {printed when the program starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Fix filenames in program statement; add `final_end' label; add access
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program PLtoTF(@!pl_file,@!tfm_file,@!output);
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@y
+@d final_end==9999
+
+@p program PLtoTF(@!output);
+label final_end;
+const @<Constants in the outer block@>@/
+type UNIX_file_name=packed array[1..100] of char;
+ @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\
+@=#include "texwarext.h"@>
+@\@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6] Open PL file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reset(pl_file);
+@y
+if argc < 3 then begin
+ print_ln('Usage: pltotf <pl-file> <tfm-file>');
+ goto final_end;
+end;
+argv(1, pl_name);
+if testreadaccess(pl_name) then reset(pl_file, pl_name)
+else begin print_ln('I can''t read the PL file!'); goto final_end;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [15] Change type of tfm_file and declare extra TFM-file variables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!tfm_file:packed file of 0..255;
+@y
+@!tfm_file : packed file of ByteCard;
+@!tfm_name,
+@!pl_name : UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [16] Open TFM file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ On some systems you may have to do something special to write a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(tfm_file);
+@y
+@ On some systems you may have to do something special to write a
+packed file of bytes.
+@^system dependencies@>
+
+@<Set init...@>=
+argv(2, tfm_name);
+rewrite(tfm_file, tfm_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [17] Make byte a byte
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!byte=0..255; {unsigned eight-bit quantity}
+@y
+@!byte=ByteCard; {unsigned eight-bit quantity}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [147, was 135] Define label `final_end'; print newline at end of program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+end.
+@y
+final_end: print_ln(' '); end.
+@z
diff --git a/systems/knuth/local/texware/pltotf.p b/systems/knuth/local/texware/pltotf.p
new file mode 100644
index 0000000000..6001876228
--- /dev/null
+++ b/systems/knuth/local/texware/pltotf.p
@@ -0,0 +1,830 @@
+{2:}program PLtoTF(output);label 9999;const{3:}bufsize=60;
+maxheaderbytes=100;maxparamwords=30;maxligsteps=5000;maxkerns=500;
+hashsize=5003;{:3}type UNIXfilename=packed array[1..100]of char;{17:}
+byte=ByteCard;ASCIIcode=32..127;{:17}{57:}fourbytes=record b0:byte;
+b1:byte;b2:byte;b3:byte;end;{:57}{61:}fixword=integer;{:61}{68:}
+headerindex=0..maxheaderbytes;indx=0..32767;{:68}{71:}pointer=0..1032;
+{:71}var{5:}plfile:text;{:5}{15:}tfmfile:packed file of ByteCard;
+tfmname,plname:UNIXfilename;{:15}{18:}xord:array[char]of ASCIIcode;{:18}
+{21:}line:integer;goodindent:integer;indent:integer;level:integer;{:21}
+{23:}leftln,rightln:boolean;limit:0..bufsize;loc:0..bufsize;
+buffer:array[1..bufsize]of char;inputhasended:boolean;{:23}{25:}
+charsonline:0..8;{:25}{30:}curchar:ASCIIcode;{:30}{36:}
+start:array[1..88]of 0..600;dictionary:array[0..600]of ASCIIcode;
+startptr:0..88;dictptr:0..600;{:36}{38:}
+curname:array[1..20]of ASCIIcode;namelength:0..20;nameptr:0..88;{:38}
+{39:}nhash:array[0..100]of 0..88;curhash:0..100;{:39}{44:}
+equiv:array[0..88]of byte;curcode:byte;{:44}{58:}curbytes:fourbytes;
+{:58}{65:}fractiondigits:array[1..7]of integer;{:65}{67:}
+headerbytes:array[headerindex]of byte;headerptr:headerindex;
+designsize:fixword;designunits:fixword;sevenbitsafeflag:boolean;
+ligkern:array[0..maxligsteps]of fourbytes;nl:0..32767;minnl:0..32767;
+kern:array[0..maxkerns]of fixword;nk:0..maxkerns;
+exten:array[0..255]of fourbytes;ne:0..256;
+param:array[1..maxparamwords]of fixword;np:0..maxparamwords;
+checksumspecified:boolean;bchar:0..256;{:67}{72:}
+memory:array[pointer]of fixword;memptr:pointer;
+link:array[pointer]of pointer;charwd:array[byte]of pointer;
+charht:array[byte]of pointer;chardp:array[byte]of pointer;
+charic:array[byte]of pointer;chartag:array[byte]of 0..3;
+charremainder:array[0..256]of 0..65535;{:72}{76:}nextd:fixword;{:76}
+{79:}index:array[pointer]of byte;excess:byte;{:79}{81:}c:byte;{:81}{98:}
+lkstepended:boolean;krnptr:0..maxkerns;{:98}{109:}sevenunsafe:boolean;
+{:109}{114:}delta:fixword;{:114}{118:}ligptr:0..maxligsteps;
+hash:array[0..hashsize]of 0..66048;class:array[0..hashsize]of 0..4;
+ligz:array[0..hashsize]of 0..257;hashptr:0..hashsize;
+hashlist:array[0..hashsize]of 0..hashsize;h,hh:0..hashsize;tt:indx;
+xligcycle,yligcycle:0..256;{:118}{129:}bc:byte;ec:byte;lh:byte;
+lf:0..32767;notfound:boolean;tempwidth:fixword;{:129}{132:}
+j:0..maxheaderbytes;p:pointer;q:1..4;parptr:0..maxparamwords;{:132}
+{138:}labeltable:array[0..256]of record rr:-1..32767;cc:byte;end;
+labelptr:0..256;sortptr:0..256;lkoffset:0..256;t:0..32767;
+extralocneeded:boolean;{:138}
+#include "texwarext.h"
+procedure initialize;var{19:}k:integer;{:19}{40:}h:0..100;{:40}{69:}
+d:headerindex;{:69}{73:}c:byte;{:73}
+begin writeln('This is PLtoTF, Version 3.5 for Linux');{6:}
+if argc<3 then begin writeln('Usage: pltotf <pl-file> <tfm-file>');
+goto 9999;end;argv(1,plname);
+if testreadaccess(plname)then reset(plfile,plname)else begin writeln(
+'I can''t read the PL file!');goto 9999;end;{:6}{16:}argv(2,tfmname);
+rewrite(tfmfile,tfmname);{:16}{20:}for k:=0 to 127 do xord[chr(k)]:=127;
+xord[' ']:=32;xord['!']:=33;xord['"']:=34;xord['#']:=35;xord['$']:=36;
+xord['%']:=37;xord['&']:=38;xord['''']:=39;xord['(']:=40;xord[')']:=41;
+xord['*']:=42;xord['+']:=43;xord[',']:=44;xord['-']:=45;xord['.']:=46;
+xord['/']:=47;xord['0']:=48;xord['1']:=49;xord['2']:=50;xord['3']:=51;
+xord['4']:=52;xord['5']:=53;xord['6']:=54;xord['7']:=55;xord['8']:=56;
+xord['9']:=57;xord[':']:=58;xord[';']:=59;xord['<']:=60;xord['=']:=61;
+xord['>']:=62;xord['?']:=63;xord['@']:=64;xord['A']:=65;xord['B']:=66;
+xord['C']:=67;xord['D']:=68;xord['E']:=69;xord['F']:=70;xord['G']:=71;
+xord['H']:=72;xord['I']:=73;xord['J']:=74;xord['K']:=75;xord['L']:=76;
+xord['M']:=77;xord['N']:=78;xord['O']:=79;xord['P']:=80;xord['Q']:=81;
+xord['R']:=82;xord['S']:=83;xord['T']:=84;xord['U']:=85;xord['V']:=86;
+xord['W']:=87;xord['X']:=88;xord['Y']:=89;xord['Z']:=90;xord['[']:=91;
+xord['\']:=92;xord[']']:=93;xord['^']:=94;xord['_']:=95;xord['`']:=96;
+xord['a']:=97;xord['b']:=98;xord['c']:=99;xord['d']:=100;xord['e']:=101;
+xord['f']:=102;xord['g']:=103;xord['h']:=104;xord['i']:=105;
+xord['j']:=106;xord['k']:=107;xord['l']:=108;xord['m']:=109;
+xord['n']:=110;xord['o']:=111;xord['p']:=112;xord['q']:=113;
+xord['r']:=114;xord['s']:=115;xord['t']:=116;xord['u']:=117;
+xord['v']:=118;xord['w']:=119;xord['x']:=120;xord['y']:=121;
+xord['z']:=122;xord['{']:=123;xord['|']:=124;xord['}']:=125;
+xord['~']:=126;{:20}{22:}line:=0;goodindent:=0;indent:=0;level:=0;{:22}
+{24:}limit:=0;loc:=0;leftln:=true;rightln:=true;inputhasended:=false;
+{:24}{26:}charsonline:=0;{:26}{37:}startptr:=1;start[1]:=0;dictptr:=0;
+{:37}{41:}for h:=0 to 100 do nhash[h]:=0;{:41}{70:}
+for d:=0 to 18*4-1 do headerbytes[d]:=0;headerbytes[8]:=11;
+headerbytes[9]:=85;headerbytes[10]:=78;headerbytes[11]:=83;
+headerbytes[12]:=80;headerbytes[13]:=69;headerbytes[14]:=67;
+headerbytes[15]:=73;headerbytes[16]:=70;headerbytes[17]:=73;
+headerbytes[18]:=69;headerbytes[19]:=68;
+for d:=48 to 59 do headerbytes[d]:=headerbytes[d-40];
+designsize:=10*1048576;designunits:=1048576;sevenbitsafeflag:=false;
+headerptr:=18*4;nl:=0;minnl:=0;nk:=0;ne:=0;np:=0;
+checksumspecified:=false;bchar:=256;{:70}{74:}charremainder[256]:=32767;
+for c:=0 to 255 do begin charwd[c]:=0;charht[c]:=0;chardp[c]:=0;
+charic[c]:=0;chartag[c]:=0;charremainder[c]:=0;end;
+memory[0]:=2147483647;memory[1]:=0;link[1]:=0;memory[2]:=0;link[2]:=0;
+memory[3]:=0;link[3]:=0;memory[4]:=0;link[4]:=0;memptr:=4;{:74}{119:}
+hashptr:=0;yligcycle:=256;for k:=0 to hashsize do hash[k]:=0;{:119}end;
+{:2}{27:}procedure showerrorcontext;var k:0..bufsize;
+begin writeln(' (line ',line:1,').');if not leftln then write('...');
+for k:=1 to loc do write(buffer[k]);writeln(' ');
+if not leftln then write(' ');for k:=1 to loc do write(' ');
+for k:=loc+1 to limit do write(buffer[k]);
+if rightln then writeln(' ')else writeln('...');charsonline:=0;end;{:27}
+{28:}procedure fillbuffer;begin leftln:=rightln;limit:=0;loc:=0;
+if leftln then begin if line>0 then readln(plfile);line:=line+1;end;
+if eof(plfile)then begin limit:=1;buffer[1]:=')';rightln:=false;
+inputhasended:=true;
+end else begin while(limit<bufsize-1)and(not eoln(plfile))do begin limit
+:=limit+1;read(plfile,buffer[limit]);end;buffer[limit+1]:=' ';
+rightln:=eoln(plfile);if leftln then{29:}
+begin while(loc<limit)and(buffer[loc+1]=' ')do loc:=loc+1;
+if loc<limit then begin if level=0 then if loc=0 then goodindent:=
+goodindent+1 else begin if goodindent>=10 then begin if charsonline>0
+then writeln(' ');
+write('Warning: Indented line occurred at level zero');showerrorcontext;
+end;goodindent:=0;indent:=0;
+end else if indent=0 then if loc mod level=0 then begin indent:=loc div
+level;goodindent:=1;
+end else goodindent:=0 else if indent*level=loc then goodindent:=
+goodindent+1 else begin if goodindent>=10 then begin if charsonline>0
+then writeln(' ');write('Warning: Inconsistent indentation; ',
+'you are at parenthesis level ',level:1);showerrorcontext;end;
+goodindent:=0;indent:=0;end;end;end{:29};end;end;{:28}{31:}
+procedure getkeywordchar;
+begin while(loc=limit)and(not rightln)do fillbuffer;
+if loc=limit then curchar:=32 else begin curchar:=xord[buffer[loc+1]];
+if curchar>=97 then curchar:=curchar-32;
+if((curchar>=48)and(curchar<=57))then loc:=loc+1 else if((curchar>=65)
+and(curchar<=90))then loc:=loc+1 else if curchar=47 then loc:=loc+1 else
+if curchar=62 then loc:=loc+1 else curchar:=32;end;end;{:31}{32:}
+procedure getnext;begin while loc=limit do fillbuffer;loc:=loc+1;
+curchar:=xord[buffer[loc]];
+if curchar>=97 then if curchar<=122 then curchar:=curchar-32 else begin
+if curchar=127 then begin begin if charsonline>0 then writeln(' ');
+write('Illegal character in the file');showerrorcontext;end;curchar:=63;
+end;end else if(curchar<=41)and(curchar>=40)then loc:=loc-1;end;{:32}
+{33:}procedure skiptoendofitem;var l:integer;begin l:=level;
+while level>=l do begin while loc=limit do fillbuffer;loc:=loc+1;
+if buffer[loc]=')'then level:=level-1 else if buffer[loc]='('then level
+:=level+1;end;
+if inputhasended then begin if charsonline>0 then writeln(' ');
+write('File ended unexpectedly: No closing ")"');showerrorcontext;end;
+curchar:=32;end;{:33}{35:}procedure finishtheproperty;
+begin while curchar=32 do getnext;
+if curchar<>41 then begin if charsonline>0 then writeln(' ');
+write('Junk after property value will be ignored');showerrorcontext;end;
+skiptoendofitem;end;{:35}{42:}procedure lookup;var k:0..20;j:0..600;
+notfound:boolean;begin{43:}curhash:=curname[1];
+for k:=2 to namelength do curhash:=(curhash+curhash+curname[k])mod 101{:
+43};notfound:=true;
+while notfound do begin if curhash=0 then curhash:=100 else curhash:=
+curhash-1;
+if nhash[curhash]=0 then notfound:=false else begin j:=start[nhash[
+curhash]];
+if start[nhash[curhash]+1]=j+namelength then begin notfound:=false;
+for k:=1 to namelength do if dictionary[j+k-1]<>curname[k]then notfound
+:=true;end;end;end;nameptr:=nhash[curhash];end;{:42}{45:}
+procedure entername(v:byte);var k:0..20;
+begin for k:=1 to namelength do curname[k]:=curname[k+20-namelength];
+lookup;nhash[curhash]:=startptr;equiv[startptr]:=v;
+for k:=1 to namelength do begin dictionary[dictptr]:=curname[k];
+dictptr:=dictptr+1;end;startptr:=startptr+1;start[startptr]:=dictptr;
+end;{:45}{49:}procedure getname;begin loc:=loc+1;level:=level+1;
+curchar:=32;while curchar=32 do getnext;
+if(curchar>41)or(curchar<40)then loc:=loc-1;namelength:=0;
+getkeywordchar;
+while curchar<>32 do begin if namelength=20 then curname[1]:=88 else
+namelength:=namelength+1;curname[namelength]:=curchar;getkeywordchar;
+end;lookup;if nameptr=0 then begin if charsonline>0 then writeln(' ');
+write('Sorry, I don''t know that property name');showerrorcontext;end;
+curcode:=equiv[nameptr];end;{:49}{51:}function getbyte:byte;
+var acc:integer;t:ASCIIcode;begin repeat getnext;until curchar<>32;
+t:=curchar;acc:=0;repeat getnext;until curchar<>32;if t=67 then{52:}
+if(curchar>=33)and(curchar<=126)and((curchar<40)or(curchar>41))then acc
+:=xord[buffer[loc]]else begin begin if charsonline>0 then writeln(' ');
+write('"C" value must be standard ASCII and not a paren');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end{:52}else if t=68 then{53:}
+begin while(curchar>=48)and(curchar<=57)do begin acc:=acc*10+curchar-48;
+if acc>255 then begin begin begin if charsonline>0 then writeln(' ');
+write('This value shouldn''t exceed 255');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);end;acc:=0;curchar:=32;
+end else getnext;end;begin if(curchar>41)or(curchar<40)then loc:=loc-1;
+end;end{:53}else if t=79 then{54:}
+begin while(curchar>=48)and(curchar<=55)do begin acc:=acc*8+curchar-48;
+if acc>255 then begin begin begin if charsonline>0 then writeln(' ');
+write('This value shouldn''t exceed ''377');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);end;acc:=0;curchar:=32;
+end else getnext;end;begin if(curchar>41)or(curchar<40)then loc:=loc-1;
+end;end{:54}else if t=72 then{55:}
+begin while((curchar>=48)and(curchar<=57))or((curchar>=65)and(curchar<=
+70))do begin if curchar>=65 then curchar:=curchar-7;
+acc:=acc*16+curchar-48;
+if acc>255 then begin begin begin if charsonline>0 then writeln(' ');
+write('This value shouldn''t exceed "FF');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);end;acc:=0;curchar:=32;
+end else getnext;end;begin if(curchar>41)or(curchar<40)then loc:=loc-1;
+end;end{:55}else if t=70 then{56:}
+begin if curchar=66 then acc:=2 else if curchar=76 then acc:=4 else if
+curchar<>77 then acc:=18;getnext;
+if curchar=73 then acc:=acc+1 else if curchar<>82 then acc:=18;getnext;
+if curchar=67 then acc:=acc+6 else if curchar=69 then acc:=acc+12 else
+if curchar<>82 then acc:=18;
+if acc>=18 then begin begin begin if charsonline>0 then writeln(' ');
+write('Illegal face code, I changed it to MRR');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);end;acc:=0;end;end{:56}
+else begin begin if charsonline>0 then writeln(' ');
+write('You need "C" or "D" or "O" or "H" or "F" here');showerrorcontext;
+end;repeat getnext until(curchar=40)or(curchar=41);end;curchar:=32;
+getbyte:=acc;end;{:51}{59:}procedure getfourbytes;var c:integer;
+r:integer;q:integer;begin repeat getnext;until curchar<>32;r:=0;
+curbytes.b0:=0;curbytes.b1:=0;curbytes.b2:=0;curbytes.b3:=0;
+if curchar=72 then r:=16 else if curchar=79 then r:=8 else begin begin
+if charsonline>0 then writeln(' ');
+write('An octal ("O") or hex ("H") value is needed here');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);end;
+if r>0 then begin q:=256 div r;repeat getnext;until curchar<>32;
+while((curchar>=48)and(curchar<=57))or((curchar>=65)and(curchar<=70))do{
+60:}begin if curchar>=65 then curchar:=curchar-7;
+c:=(r*curbytes.b0)+(curbytes.b1 div q);
+if c>255 then begin curbytes.b0:=0;curbytes.b1:=0;curbytes.b2:=0;
+curbytes.b3:=0;
+if r=8 then begin begin if charsonline>0 then writeln(' ');
+write('Sorry, the maximum octal value is O 37777777777');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end else begin begin if charsonline>0 then writeln(' ');
+write('Sorry, the maximum hex value is H FFFFFFFF');showerrorcontext;
+end;repeat getnext until(curchar=40)or(curchar=41);end;
+end else if curchar>=48+r then begin begin if charsonline>0 then writeln
+(' ');write('Illegal digit');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);
+end else begin curbytes.b0:=c;
+curbytes.b1:=(r*(curbytes.b1 mod q))+(curbytes.b2 div q);
+curbytes.b2:=(r*(curbytes.b2 mod q))+(curbytes.b3 div q);
+curbytes.b3:=(r*(curbytes.b3 mod q))+curchar-48;getnext;end;end{:60};
+end;end;{:59}{62:}function getfix:fixword;var negative:boolean;
+acc:integer;intpart:integer;j:0..7;begin repeat getnext;
+until curchar<>32;negative:=false;acc:=0;
+if(curchar<>82)and(curchar<>68)then begin begin if charsonline>0 then
+writeln(' ');write('An "R" or "D" value is needed here');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end else begin{63:}repeat getnext;if curchar=45 then begin curchar:=32;
+negative:=true;end else if curchar=43 then curchar:=32;
+until curchar<>32{:63};while(curchar>=48)and(curchar<=57)do{64:}
+begin acc:=acc*10+curchar-48;
+if acc>=2048 then begin begin begin if charsonline>0 then writeln(' ');
+write('Real constants must be less than 2048');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);end;acc:=0;curchar:=32;
+end else getnext;end{:64};intpart:=acc;acc:=0;if curchar=46 then{66:}
+begin j:=0;getnext;
+while(curchar>=48)and(curchar<=57)do begin if j<7 then begin j:=j+1;
+fractiondigits[j]:=2097152*(curchar-48);end;getnext;end;acc:=0;
+while j>0 do begin acc:=fractiondigits[j]+(acc div 10);j:=j-1;end;
+acc:=(acc+10)div 20;end{:66};
+if(acc>=1048576)and(intpart=2047)then begin begin if charsonline>0 then
+writeln(' ');write('Real constants must be less than 2048');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end else acc:=intpart*1048576+acc;end;
+if negative then getfix:=-acc else getfix:=acc;end;{:62}{75:}
+function sortin(h:pointer;d:fixword):pointer;var p:pointer;
+begin if(d=0)and(h<>1)then sortin:=0 else begin p:=h;
+while d>=memory[link[p]]do p:=link[p];
+if(d=memory[p])and(p<>h)then sortin:=p else if memptr=1032 then begin
+begin if charsonline>0 then writeln(' ');
+write('Memory overflow: more than 1028 widths, etc');showerrorcontext;
+end;writeln('Congratulations! It''s hard to make this error.');
+sortin:=p;end else begin memptr:=memptr+1;memory[memptr]:=d;
+link[memptr]:=link[p];link[p]:=memptr;memory[h]:=memory[h]+1;
+sortin:=memptr;end;end;end;{:75}{77:}function mincover(h:pointer;
+d:fixword):integer;var p:pointer;l:fixword;m:integer;begin m:=0;
+p:=link[h];nextd:=memory[0];while p<>0 do begin m:=m+1;l:=memory[p];
+while memory[link[p]]<=l+d do p:=link[p];p:=link[p];
+if memory[p]-l<nextd then nextd:=memory[p]-l;end;mincover:=m;end;{:77}
+{78:}function shorten(h:pointer;m:integer):fixword;var d:fixword;
+k:integer;begin if memory[h]>m then begin excess:=memory[h]-m;
+k:=mincover(h,0);d:=nextd;repeat d:=d+d;k:=mincover(h,d);until k<=m;
+d:=d div 2;k:=mincover(h,d);while k>m do begin d:=nextd;
+k:=mincover(h,d);end;shorten:=d;end else shorten:=0;end;{:78}{80:}
+procedure setindices(h:pointer;d:fixword);var p:pointer;q:pointer;
+m:byte;l:fixword;begin q:=h;p:=link[q];m:=0;while p<>0 do begin m:=m+1;
+l:=memory[p];index[p]:=m;while memory[link[p]]<=l+d do begin p:=link[p];
+index[p]:=m;excess:=excess-1;if excess=0 then d:=0;end;link[q]:=p;
+memory[p]:=l+(memory[p]-l)div 2;q:=p;p:=link[p];end;memory[h]:=m;end;
+{:80}{83:}procedure junkerror;
+begin begin if charsonline>0 then writeln(' ');
+write('There''s junk here that is not in parentheses');showerrorcontext;
+end;repeat getnext until(curchar=40)or(curchar=41);end;{:83}{86:}
+procedure readfourbytes(l:headerindex);begin getfourbytes;
+headerbytes[l]:=curbytes.b0;headerbytes[l+1]:=curbytes.b1;
+headerbytes[l+2]:=curbytes.b2;headerbytes[l+3]:=curbytes.b3;end;{:86}
+{87:}procedure readBCPL(l:headerindex;n:byte);var k:headerindex;
+begin k:=l;while curchar=32 do getnext;
+while(curchar<>40)and(curchar<>41)do begin if k<l+n then k:=k+1;
+if k<l+n then headerbytes[k]:=curchar;getnext;end;
+if k=l+n then begin begin if charsonline>0 then writeln(' ');
+write('String is too long; its first ',n-1:1,' characters will be kept')
+;showerrorcontext;end;k:=k-1;end;headerbytes[l]:=k-l;
+while k<l+n-1 do begin k:=k+1;headerbytes[k]:=0;end;end;{:87}{96:}
+procedure checktag(c:byte);begin case chartag[c]of 0:;
+1:begin if charsonline>0 then writeln(' ');
+write('This character already appeared in a LIGTABLE LABEL');
+showerrorcontext;end;2:begin if charsonline>0 then writeln(' ');
+write('This character already has a NEXTLARGER spec');showerrorcontext;
+end;3:begin if charsonline>0 then writeln(' ');
+write('This character already has a VARCHAR spec');showerrorcontext;end;
+end;end;{:96}{107:}procedure printoctal(c:byte);
+begin write('''',(c div 64):1,((c div 8)mod 8):1,(c mod 8):1);end;{:107}
+{121:}function hashinput(p,c:indx):boolean;label 30;var cc:0..3;
+zz:0..255;y:0..255;key:integer;t:integer;
+begin if hashptr=hashsize then begin hashinput:=false;goto 30;end;{122:}
+y:=ligkern[p].b1;t:=ligkern[p].b2;cc:=0;zz:=ligkern[p].b3;
+if t>=128 then zz:=y else begin case t of 0,6:;5,11:zz:=y;1,7:cc:=1;
+2:cc:=2;3:cc:=3;end;end{:122};key:=256*c+y+1;h:=(1009*key)mod hashsize;
+while hash[h]>0 do begin if hash[h]<=key then begin if hash[h]=key then
+begin hashinput:=false;goto 30;end;t:=hash[h];hash[h]:=key;key:=t;
+t:=class[h];class[h]:=cc;cc:=t;t:=ligz[h];ligz[h]:=zz;zz:=t;end;
+if h>0 then h:=h-1 else h:=hashsize;end;hash[h]:=key;class[h]:=cc;
+ligz[h]:=zz;hashptr:=hashptr+1;hashlist[hashptr]:=h;hashinput:=true;
+30:end;{:121}{123:}function f(h,x,y:indx):indx;forward;
+function eval(x,y:indx):indx;var key:integer;begin key:=256*x+y+1;
+h:=(1009*key)mod hashsize;
+while hash[h]>key do if h>0 then h:=h-1 else h:=hashsize;
+if hash[h]<key then eval:=y else eval:=f(h,x,y);end;{:123}{124:}
+function f;begin case class[h]of 0:;1:begin class[h]:=4;
+ligz[h]:=eval(ligz[h],y);class[h]:=0;end;2:begin class[h]:=4;
+ligz[h]:=eval(x,ligz[h]);class[h]:=0;end;3:begin class[h]:=4;
+ligz[h]:=eval(eval(x,ligz[h]),y);class[h]:=0;end;4:begin xligcycle:=x;
+yligcycle:=y;ligz[h]:=257;class[h]:=0;end;end;f:=ligz[h];end;{:124}
+{136:}procedure outscaled(x:fixword);var n:byte;m:0..65535;
+begin if abs(x/designunits)>=16.0 then begin writeln(
+'The relative dimension ',x/1048576:1:3,' is too large.');
+write(' (Must be less than 16*designsize');
+if designunits<>1048576 then write(' =',designunits/65536:1:3,
+' designunits');writeln(')');x:=0;end;
+if designunits<>1048576 then x:=round((x/designunits)*1048576.0);
+if x<0 then begin write(tfmfile,255);x:=x+16777216;if x<=0 then x:=1;
+end else begin write(tfmfile,0);if x>=16777216 then x:=16777215;end;
+n:=x div 65536;m:=x mod 65536;write(tfmfile,n);write(tfmfile,m div 256);
+write(tfmfile,m mod 256);end;{:136}{146:}procedure paramenter;begin{48:}
+namelength:=5;curname[16]:=83;curname[17]:=76;curname[18]:=65;
+curname[19]:=78;curname[20]:=84;entername(21);namelength:=5;
+curname[16]:=83;curname[17]:=80;curname[18]:=65;curname[19]:=67;
+curname[20]:=69;entername(22);namelength:=7;curname[14]:=83;
+curname[15]:=84;curname[16]:=82;curname[17]:=69;curname[18]:=84;
+curname[19]:=67;curname[20]:=72;entername(23);namelength:=6;
+curname[15]:=83;curname[16]:=72;curname[17]:=82;curname[18]:=73;
+curname[19]:=78;curname[20]:=75;entername(24);namelength:=7;
+curname[14]:=88;curname[15]:=72;curname[16]:=69;curname[17]:=73;
+curname[18]:=71;curname[19]:=72;curname[20]:=84;entername(25);
+namelength:=4;curname[17]:=81;curname[18]:=85;curname[19]:=65;
+curname[20]:=68;entername(26);namelength:=10;curname[11]:=69;
+curname[12]:=88;curname[13]:=84;curname[14]:=82;curname[15]:=65;
+curname[16]:=83;curname[17]:=80;curname[18]:=65;curname[19]:=67;
+curname[20]:=69;entername(27);namelength:=4;curname[17]:=78;
+curname[18]:=85;curname[19]:=77;curname[20]:=49;entername(28);
+namelength:=4;curname[17]:=78;curname[18]:=85;curname[19]:=77;
+curname[20]:=50;entername(29);namelength:=4;curname[17]:=78;
+curname[18]:=85;curname[19]:=77;curname[20]:=51;entername(30);
+namelength:=6;curname[15]:=68;curname[16]:=69;curname[17]:=78;
+curname[18]:=79;curname[19]:=77;curname[20]:=49;entername(31);
+namelength:=6;curname[15]:=68;curname[16]:=69;curname[17]:=78;
+curname[18]:=79;curname[19]:=77;curname[20]:=50;entername(32);
+namelength:=4;curname[17]:=83;curname[18]:=85;curname[19]:=80;
+curname[20]:=49;entername(33);namelength:=4;curname[17]:=83;
+curname[18]:=85;curname[19]:=80;curname[20]:=50;entername(34);
+namelength:=4;curname[17]:=83;curname[18]:=85;curname[19]:=80;
+curname[20]:=51;entername(35);namelength:=4;curname[17]:=83;
+curname[18]:=85;curname[19]:=66;curname[20]:=49;entername(36);
+namelength:=4;curname[17]:=83;curname[18]:=85;curname[19]:=66;
+curname[20]:=50;entername(37);namelength:=7;curname[14]:=83;
+curname[15]:=85;curname[16]:=80;curname[17]:=68;curname[18]:=82;
+curname[19]:=79;curname[20]:=80;entername(38);namelength:=7;
+curname[14]:=83;curname[15]:=85;curname[16]:=66;curname[17]:=68;
+curname[18]:=82;curname[19]:=79;curname[20]:=80;entername(39);
+namelength:=6;curname[15]:=68;curname[16]:=69;curname[17]:=76;
+curname[18]:=73;curname[19]:=77;curname[20]:=49;entername(40);
+namelength:=6;curname[15]:=68;curname[16]:=69;curname[17]:=76;
+curname[18]:=73;curname[19]:=77;curname[20]:=50;entername(41);
+namelength:=10;curname[11]:=65;curname[12]:=88;curname[13]:=73;
+curname[14]:=83;curname[15]:=72;curname[16]:=69;curname[17]:=73;
+curname[18]:=71;curname[19]:=72;curname[20]:=84;entername(42);
+namelength:=20;curname[1]:=68;curname[2]:=69;curname[3]:=70;
+curname[4]:=65;curname[5]:=85;curname[6]:=76;curname[7]:=84;
+curname[8]:=82;curname[9]:=85;curname[10]:=76;curname[11]:=69;
+curname[12]:=84;curname[13]:=72;curname[14]:=73;curname[15]:=67;
+curname[16]:=75;curname[17]:=78;curname[18]:=69;curname[19]:=83;
+curname[20]:=83;entername(28);namelength:=13;curname[8]:=66;
+curname[9]:=73;curname[10]:=71;curname[11]:=79;curname[12]:=80;
+curname[13]:=83;curname[14]:=80;curname[15]:=65;curname[16]:=67;
+curname[17]:=73;curname[18]:=78;curname[19]:=71;curname[20]:=49;
+entername(29);namelength:=13;curname[8]:=66;curname[9]:=73;
+curname[10]:=71;curname[11]:=79;curname[12]:=80;curname[13]:=83;
+curname[14]:=80;curname[15]:=65;curname[16]:=67;curname[17]:=73;
+curname[18]:=78;curname[19]:=71;curname[20]:=50;entername(30);
+namelength:=13;curname[8]:=66;curname[9]:=73;curname[10]:=71;
+curname[11]:=79;curname[12]:=80;curname[13]:=83;curname[14]:=80;
+curname[15]:=65;curname[16]:=67;curname[17]:=73;curname[18]:=78;
+curname[19]:=71;curname[20]:=51;entername(31);namelength:=13;
+curname[8]:=66;curname[9]:=73;curname[10]:=71;curname[11]:=79;
+curname[12]:=80;curname[13]:=83;curname[14]:=80;curname[15]:=65;
+curname[16]:=67;curname[17]:=73;curname[18]:=78;curname[19]:=71;
+curname[20]:=52;entername(32);namelength:=13;curname[8]:=66;
+curname[9]:=73;curname[10]:=71;curname[11]:=79;curname[12]:=80;
+curname[13]:=83;curname[14]:=80;curname[15]:=65;curname[16]:=67;
+curname[17]:=73;curname[18]:=78;curname[19]:=71;curname[20]:=53;
+entername(33);{:48};end;procedure nameenter;begin{47:}equiv[0]:=0;
+namelength:=8;curname[13]:=67;curname[14]:=72;curname[15]:=69;
+curname[16]:=67;curname[17]:=75;curname[18]:=83;curname[19]:=85;
+curname[20]:=77;entername(1);namelength:=10;curname[11]:=68;
+curname[12]:=69;curname[13]:=83;curname[14]:=73;curname[15]:=71;
+curname[16]:=78;curname[17]:=83;curname[18]:=73;curname[19]:=90;
+curname[20]:=69;entername(2);namelength:=11;curname[10]:=68;
+curname[11]:=69;curname[12]:=83;curname[13]:=73;curname[14]:=71;
+curname[15]:=78;curname[16]:=85;curname[17]:=78;curname[18]:=73;
+curname[19]:=84;curname[20]:=83;entername(3);namelength:=12;
+curname[9]:=67;curname[10]:=79;curname[11]:=68;curname[12]:=73;
+curname[13]:=78;curname[14]:=71;curname[15]:=83;curname[16]:=67;
+curname[17]:=72;curname[18]:=69;curname[19]:=77;curname[20]:=69;
+entername(4);namelength:=6;curname[15]:=70;curname[16]:=65;
+curname[17]:=77;curname[18]:=73;curname[19]:=76;curname[20]:=89;
+entername(5);namelength:=4;curname[17]:=70;curname[18]:=65;
+curname[19]:=67;curname[20]:=69;entername(6);namelength:=16;
+curname[5]:=83;curname[6]:=69;curname[7]:=86;curname[8]:=69;
+curname[9]:=78;curname[10]:=66;curname[11]:=73;curname[12]:=84;
+curname[13]:=83;curname[14]:=65;curname[15]:=70;curname[16]:=69;
+curname[17]:=70;curname[18]:=76;curname[19]:=65;curname[20]:=71;
+entername(7);namelength:=6;curname[15]:=72;curname[16]:=69;
+curname[17]:=65;curname[18]:=68;curname[19]:=69;curname[20]:=82;
+entername(8);namelength:=9;curname[12]:=70;curname[13]:=79;
+curname[14]:=78;curname[15]:=84;curname[16]:=68;curname[17]:=73;
+curname[18]:=77;curname[19]:=69;curname[20]:=78;entername(9);
+namelength:=8;curname[13]:=76;curname[14]:=73;curname[15]:=71;
+curname[16]:=84;curname[17]:=65;curname[18]:=66;curname[19]:=76;
+curname[20]:=69;entername(10);namelength:=12;curname[9]:=66;
+curname[10]:=79;curname[11]:=85;curname[12]:=78;curname[13]:=68;
+curname[14]:=65;curname[15]:=82;curname[16]:=89;curname[17]:=67;
+curname[18]:=72;curname[19]:=65;curname[20]:=82;entername(11);
+namelength:=9;curname[12]:=67;curname[13]:=72;curname[14]:=65;
+curname[15]:=82;curname[16]:=65;curname[17]:=67;curname[18]:=84;
+curname[19]:=69;curname[20]:=82;entername(12);namelength:=9;
+curname[12]:=80;curname[13]:=65;curname[14]:=82;curname[15]:=65;
+curname[16]:=77;curname[17]:=69;curname[18]:=84;curname[19]:=69;
+curname[20]:=82;entername(20);namelength:=6;curname[15]:=67;
+curname[16]:=72;curname[17]:=65;curname[18]:=82;curname[19]:=87;
+curname[20]:=68;entername(51);namelength:=6;curname[15]:=67;
+curname[16]:=72;curname[17]:=65;curname[18]:=82;curname[19]:=72;
+curname[20]:=84;entername(52);namelength:=6;curname[15]:=67;
+curname[16]:=72;curname[17]:=65;curname[18]:=82;curname[19]:=68;
+curname[20]:=80;entername(53);namelength:=6;curname[15]:=67;
+curname[16]:=72;curname[17]:=65;curname[18]:=82;curname[19]:=73;
+curname[20]:=67;entername(54);namelength:=10;curname[11]:=78;
+curname[12]:=69;curname[13]:=88;curname[14]:=84;curname[15]:=76;
+curname[16]:=65;curname[17]:=82;curname[18]:=71;curname[19]:=69;
+curname[20]:=82;entername(55);namelength:=7;curname[14]:=86;
+curname[15]:=65;curname[16]:=82;curname[17]:=67;curname[18]:=72;
+curname[19]:=65;curname[20]:=82;entername(56);namelength:=3;
+curname[18]:=84;curname[19]:=79;curname[20]:=80;entername(57);
+namelength:=3;curname[18]:=77;curname[19]:=73;curname[20]:=68;
+entername(58);namelength:=3;curname[18]:=66;curname[19]:=79;
+curname[20]:=84;entername(59);namelength:=3;curname[18]:=82;
+curname[19]:=69;curname[20]:=80;entername(60);namelength:=3;
+curname[18]:=69;curname[19]:=88;curname[20]:=84;entername(60);
+namelength:=7;curname[14]:=67;curname[15]:=79;curname[16]:=77;
+curname[17]:=77;curname[18]:=69;curname[19]:=78;curname[20]:=84;
+entername(0);namelength:=5;curname[16]:=76;curname[17]:=65;
+curname[18]:=66;curname[19]:=69;curname[20]:=76;entername(70);
+namelength:=4;curname[17]:=83;curname[18]:=84;curname[19]:=79;
+curname[20]:=80;entername(71);namelength:=4;curname[17]:=83;
+curname[18]:=75;curname[19]:=73;curname[20]:=80;entername(72);
+namelength:=3;curname[18]:=75;curname[19]:=82;curname[20]:=78;
+entername(73);namelength:=3;curname[18]:=76;curname[19]:=73;
+curname[20]:=71;entername(74);namelength:=4;curname[17]:=47;
+curname[18]:=76;curname[19]:=73;curname[20]:=71;entername(76);
+namelength:=5;curname[16]:=47;curname[17]:=76;curname[18]:=73;
+curname[19]:=71;curname[20]:=62;entername(80);namelength:=4;
+curname[17]:=76;curname[18]:=73;curname[19]:=71;curname[20]:=47;
+entername(75);namelength:=5;curname[16]:=76;curname[17]:=73;
+curname[18]:=71;curname[19]:=47;curname[20]:=62;entername(79);
+namelength:=5;curname[16]:=47;curname[17]:=76;curname[18]:=73;
+curname[19]:=71;curname[20]:=47;entername(77);namelength:=6;
+curname[15]:=47;curname[16]:=76;curname[17]:=73;curname[18]:=71;
+curname[19]:=47;curname[20]:=62;entername(81);namelength:=7;
+curname[14]:=47;curname[15]:=76;curname[16]:=73;curname[17]:=71;
+curname[18]:=47;curname[19]:=62;curname[20]:=62;entername(85);{:47};
+paramenter;end;procedure readligkern;var krnptr:0..maxkerns;c:byte;
+begin{94:}begin lkstepended:=false;
+while level=1 do begin while curchar=32 do getnext;
+if curchar=40 then{95:}begin getname;
+if curcode=0 then skiptoendofitem else if curcode<70 then begin begin if
+charsonline>0 then writeln(' ');
+write('This property name doesn''t belong in a LIGTABLE list');
+showerrorcontext;end;skiptoendofitem;
+end else begin case curcode of 70:{97:}
+begin while curchar=32 do getnext;
+if curchar=66 then begin charremainder[256]:=nl;
+repeat getnext until(curchar=40)or(curchar=41);
+end else begin begin if(curchar>41)or(curchar<40)then loc:=loc-1;end;
+c:=getbyte;checktag(c);chartag[c]:=1;charremainder[c]:=nl;end;
+if minnl<=nl then minnl:=nl+1;lkstepended:=false;end{:97};71:{99:}
+if not lkstepended then begin if charsonline>0 then writeln(' ');
+write('STOP must follow LIG or KRN');showerrorcontext;
+end else begin ligkern[nl-1].b0:=128;lkstepended:=false;end{:99};
+72:{100:}
+if not lkstepended then begin if charsonline>0 then writeln(' ');
+write('SKIP must follow LIG or KRN');showerrorcontext;
+end else begin c:=getbyte;
+if c>=128 then begin if charsonline>0 then writeln(' ');
+write('Maximum SKIP amount is 127');showerrorcontext;
+end else if nl+c>=maxligsteps then begin if charsonline>0 then writeln(
+' ');write('Sorry, LIGTABLE too long for me to handle');
+showerrorcontext;end else begin ligkern[nl-1].b0:=c;
+if minnl<=nl+c then minnl:=nl+c+1;end;lkstepended:=false;end{:100};
+73:{102:}begin ligkern[nl].b0:=0;ligkern[nl].b1:=getbyte;
+kern[nk]:=getfix;krnptr:=0;
+while kern[krnptr]<>kern[nk]do krnptr:=krnptr+1;
+if krnptr=nk then begin if nk<maxkerns then nk:=nk+1 else begin begin if
+charsonline>0 then writeln(' ');
+write('Sorry, too many different kerns for me to handle');
+showerrorcontext;end;krnptr:=krnptr-1;end;end;
+ligkern[nl].b2:=128+(krnptr div 256);ligkern[nl].b3:=krnptr mod 256;
+if nl>=maxligsteps-1 then begin if charsonline>0 then writeln(' ');
+write('Sorry, LIGTABLE too long for me to handle');showerrorcontext;
+end else nl:=nl+1;lkstepended:=true;end{:102};
+74,75,76,77,79,80,81,85:{101:}begin ligkern[nl].b0:=0;
+ligkern[nl].b2:=curcode-74;ligkern[nl].b1:=getbyte;
+ligkern[nl].b3:=getbyte;
+if nl>=maxligsteps-1 then begin if charsonline>0 then writeln(' ');
+write('Sorry, LIGTABLE too long for me to handle');showerrorcontext;
+end else nl:=nl+1;lkstepended:=true;end{:101};end;finishtheproperty;end;
+end{:95}else if curchar=41 then skiptoendofitem else junkerror;end;
+begin loc:=loc-1;level:=level+1;curchar:=41;end;end{:94};end;
+procedure readcharinfo;var c:byte;begin{103:}begin c:=getbyte;{108:}
+begin if charsonline=8 then begin writeln(' ');charsonline:=1;
+end else begin if charsonline>0 then write(' ');
+charsonline:=charsonline+1;end;printoctal(c);end{:108};
+while level=1 do begin while curchar=32 do getnext;
+if curchar=40 then{104:}begin getname;
+if curcode=0 then skiptoendofitem else if(curcode<51)or(curcode>56)then
+begin begin if charsonline>0 then writeln(' ');
+write('This property name doesn''t belong in a CHARACTER list');
+showerrorcontext;end;skiptoendofitem;
+end else begin case curcode of 51:charwd[c]:=sortin(1,getfix);
+52:charht[c]:=sortin(2,getfix);53:chardp[c]:=sortin(3,getfix);
+54:charic[c]:=sortin(4,getfix);55:begin checktag(c);chartag[c]:=2;
+charremainder[c]:=getbyte;end;56:{105:}
+begin if ne=256 then begin if charsonline>0 then writeln(' ');
+write('At most 256 VARCHAR specs are allowed');showerrorcontext;
+end else begin checktag(c);chartag[c]:=3;charremainder[c]:=ne;
+exten[ne].b0:=0;exten[ne].b1:=0;exten[ne].b2:=0;exten[ne].b3:=0;
+while level=2 do begin while curchar=32 do getnext;
+if curchar=40 then{106:}begin getname;
+if curcode=0 then skiptoendofitem else if(curcode<57)or(curcode>60)then
+begin begin if charsonline>0 then writeln(' ');
+write('This property name doesn''t belong in a VARCHAR list');
+showerrorcontext;end;skiptoendofitem;
+end else begin case curcode-(57)of 0:exten[ne].b0:=getbyte;
+1:exten[ne].b1:=getbyte;2:exten[ne].b2:=getbyte;3:exten[ne].b3:=getbyte;
+end;finishtheproperty;end;end{:106}
+else if curchar=41 then skiptoendofitem else junkerror;end;ne:=ne+1;
+begin loc:=loc-1;level:=level+1;curchar:=41;end;end;end{:105};end;
+finishtheproperty;end;end{:104}
+else if curchar=41 then skiptoendofitem else junkerror;end;
+if charwd[c]=0 then charwd[c]:=sortin(1,0);begin loc:=loc-1;
+level:=level+1;curchar:=41;end;end{:103};end;procedure readinput;
+var c:byte;begin{82:}curchar:=32;repeat while curchar=32 do getnext;
+if curchar=40 then{84:}begin getname;
+if curcode=0 then skiptoendofitem else if curcode>12 then begin begin if
+charsonline>0 then writeln(' ');
+write('This property name doesn''t belong on the outer level');
+showerrorcontext;end;skiptoendofitem;end else begin{85:}
+case curcode of 1:begin checksumspecified:=true;readfourbytes(0);end;
+2:{88:}begin nextd:=getfix;
+if nextd<1048576 then begin if charsonline>0 then writeln(' ');
+write('The design size must be at least 1');showerrorcontext;
+end else designsize:=nextd;end{:88};3:{89:}begin nextd:=getfix;
+if nextd<=0 then begin if charsonline>0 then writeln(' ');
+write('The number of units per design size must be positive');
+showerrorcontext;end else designunits:=nextd;end{:89};4:readBCPL(8,40);
+5:readBCPL(48,20);6:headerbytes[71]:=getbyte;7:{90:}
+begin while curchar=32 do getnext;
+if curchar=84 then sevenbitsafeflag:=true else if curchar=70 then
+sevenbitsafeflag:=false else begin if charsonline>0 then writeln(' ');
+write('The flag value should be "TRUE" or "FALSE"');showerrorcontext;
+end;repeat getnext until(curchar=40)or(curchar=41);end{:90};8:{91:}
+begin c:=getbyte;
+if c<18 then begin begin if charsonline>0 then writeln(' ');
+write('HEADER indices should be 18 or more');showerrorcontext;end;
+repeat getnext until(curchar=40)or(curchar=41);
+end else if 4*c+4>maxheaderbytes then begin begin if charsonline>0 then
+writeln(' ');
+write('This HEADER index is too big for my present table size');
+showerrorcontext;end;repeat getnext until(curchar=40)or(curchar=41);
+end else begin while headerptr<4*c+4 do begin headerbytes[headerptr]:=0;
+headerptr:=headerptr+1;end;readfourbytes(4*c);end;end{:91};9:{92:}
+begin while level=1 do begin while curchar=32 do getnext;
+if curchar=40 then{93:}begin getname;
+if curcode=0 then skiptoendofitem else if(curcode<20)or(curcode>=51)then
+begin begin if charsonline>0 then writeln(' ');
+write('This property name doesn''t belong in a FONTDIMEN list');
+showerrorcontext;end;skiptoendofitem;
+end else begin if curcode=20 then c:=getbyte else c:=curcode-20;
+if c=0 then begin begin if charsonline>0 then writeln(' ');
+write('PARAMETER index must not be zero');showerrorcontext;end;
+skiptoendofitem;
+end else if c>maxparamwords then begin begin if charsonline>0 then
+writeln(' ');
+write('This PARAMETER index is too big for my present table size');
+showerrorcontext;end;skiptoendofitem;
+end else begin while np<c do begin np:=np+1;param[np]:=0;end;
+param[c]:=getfix;finishtheproperty;end;end;end{:93}
+else if curchar=41 then skiptoendofitem else junkerror;end;
+begin loc:=loc-1;level:=level+1;curchar:=41;end;end{:92};10:readligkern;
+11:bchar:=getbyte;12:readcharinfo;end{:85};finishtheproperty;end;
+end{:84}
+else if(curchar=41)and not inputhasended then begin begin if charsonline
+>0 then writeln(' ');write('Extra right parenthesis');showerrorcontext;
+end;loc:=loc+1;curchar:=32;end else if not inputhasended then junkerror;
+until inputhasended{:82};end;procedure corrandcheck;var c:0..256;
+hh:0..hashsize;ligptr:0..maxligsteps;g:byte;begin{110:}
+if nl>0 then{116:}
+begin if charremainder[256]<32767 then begin ligkern[nl].b0:=255;
+ligkern[nl].b1:=0;ligkern[nl].b2:=0;ligkern[nl].b3:=0;nl:=nl+1;end;
+while minnl>nl do begin ligkern[nl].b0:=255;ligkern[nl].b1:=0;
+ligkern[nl].b2:=0;ligkern[nl].b3:=0;nl:=nl+1;end;
+if ligkern[nl-1].b0=0 then ligkern[nl-1].b0:=128;end{:116};
+sevenunsafe:=false;for c:=0 to 255 do if charwd[c]<>0 then{111:}
+case chartag[c]of 0:;1:{120:}begin ligptr:=charremainder[c];
+repeat if hashinput(ligptr,c)then begin if ligkern[ligptr].b2<128 then
+begin if ligkern[ligptr].b1<>bchar then begin g:=ligkern[ligptr].b1;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('LIG character examined by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;begin g:=ligkern[ligptr].b3;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('LIG character generated by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;
+if ligkern[ligptr].b3>=128 then if(c<128)or(c=256)then if(ligkern[ligptr
+].b1<128)or(ligkern[ligptr].b1=bchar)then sevenunsafe:=true;
+end else if ligkern[ligptr].b1<>bchar then begin g:=ligkern[ligptr].b1;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('KRN character examined by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;end;
+if ligkern[ligptr].b0>=128 then ligptr:=nl else ligptr:=ligptr+1+ligkern
+[ligptr].b0;until ligptr>=nl;end{:120};2:begin g:=charremainder[c];
+if(g>=128)and(c<128)then sevenunsafe:=true;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('The character NEXTLARGER than',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;3:{112:}
+begin if exten[charremainder[c]].b0>0 then begin g:=exten[charremainder[
+c]].b0;if(g>=128)and(c<128)then sevenunsafe:=true;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('TOP piece of character',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;
+if exten[charremainder[c]].b1>0 then begin g:=exten[charremainder[c]].b1
+;if(g>=128)and(c<128)then sevenunsafe:=true;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('MID piece of character',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;
+if exten[charremainder[c]].b2>0 then begin g:=exten[charremainder[c]].b2
+;if(g>=128)and(c<128)then sevenunsafe:=true;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('BOT piece of character',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;
+begin g:=exten[charremainder[c]].b3;
+if(g>=128)and(c<128)then sevenunsafe:=true;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('REP piece of character',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;end{:112};end{:111};
+if charremainder[256]<32767 then begin c:=256;{120:}
+begin ligptr:=charremainder[c];
+repeat if hashinput(ligptr,c)then begin if ligkern[ligptr].b2<128 then
+begin if ligkern[ligptr].b1<>bchar then begin g:=ligkern[ligptr].b1;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('LIG character examined by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;begin g:=ligkern[ligptr].b3;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('LIG character generated by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;
+if ligkern[ligptr].b3>=128 then if(c<128)or(c=256)then if(ligkern[ligptr
+].b1<128)or(ligkern[ligptr].b1=bchar)then sevenunsafe:=true;
+end else if ligkern[ligptr].b1<>bchar then begin g:=ligkern[ligptr].b1;
+if charwd[g]=0 then begin charwd[g]:=sortin(1,0);
+write('KRN character examined by',' ');printoctal(c);
+writeln(' had no CHARACTER spec.');end;end;end;
+if ligkern[ligptr].b0>=128 then ligptr:=nl else ligptr:=ligptr+1+ligkern
+[ligptr].b0;until ligptr>=nl;end{:120};end;
+if sevenbitsafeflag and sevenunsafe then writeln(
+'The font is not really seven-bit-safe!');{125:}
+if hashptr<hashsize then for hh:=1 to hashptr do begin tt:=hashlist[hh];
+if class[tt]>0 then tt:=f(tt,(hash[tt]-1)div 256,(hash[tt]-1)mod 256);
+end;
+if(hashptr=hashsize)or(yligcycle<256)then begin if hashptr<hashsize then
+begin write('Infinite ligature loop starting with ');
+if xligcycle=256 then write('boundary')else printoctal(xligcycle);
+write(' and ');printoctal(yligcycle);writeln('!');
+end else writeln(
+'Sorry, I haven''t room for so many ligature/kern pairs!');
+writeln('All ligatures will be cleared.');
+for c:=0 to 255 do if chartag[c]=1 then begin chartag[c]:=0;
+charremainder[c]:=0;end;nl:=0;bchar:=256;charremainder[256]:=32767;
+end{:125};{126:}
+if nl>0 then for ligptr:=0 to nl-1 do if ligkern[ligptr].b2<128 then
+begin if ligkern[ligptr].b0<255 then begin begin c:=ligkern[ligptr].b1;
+if charwd[c]=0 then if c<>bchar then begin ligkern[ligptr].b1:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','LIG step',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;begin c:=ligkern[ligptr].b3;
+if charwd[c]=0 then if c<>bchar then begin ligkern[ligptr].b3:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','LIG step',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;end;
+end else begin c:=ligkern[ligptr].b1;
+if charwd[c]=0 then if c<>bchar then begin ligkern[ligptr].b1:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','KRN step',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;
+if ne>0 then for g:=0 to ne-1 do begin begin c:=exten[g].b0;
+if c>0 then if charwd[c]=0 then begin exten[g].b0:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','VARCHAR TOP',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;begin c:=exten[g].b1;
+if c>0 then if charwd[c]=0 then begin exten[g].b1:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','VARCHAR MID',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;begin c:=exten[g].b2;
+if c>0 then if charwd[c]=0 then begin exten[g].b2:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','VARCHAR BOT',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;begin c:=exten[g].b3;
+if charwd[c]=0 then begin exten[g].b3:=0;
+if charwd[0]=0 then charwd[0]:=sortin(1,0);
+write('Unused ','VARCHAR REP',' refers to nonexistent character ');
+printoctal(c);writeln('!');end;end;end{:126};for c:=0 to 255 do{113:}
+if chartag[c]=2 then begin g:=charremainder[c];
+while(g<c)and(chartag[g]=2)do g:=charremainder[g];
+if g=c then begin chartag[c]:=0;
+write('A cycle of NEXTLARGER characters has been broken at ');
+printoctal(c);writeln('.');end;end{:113};{115:}delta:=shorten(1,255);
+setindices(1,delta);
+if delta>0 then writeln('I had to round some ','width','s by ',(((delta
++1)div 2)/1048576):1:7,' units.');delta:=shorten(2,15);
+setindices(2,delta);
+if delta>0 then writeln('I had to round some ','height','s by ',(((delta
++1)div 2)/1048576):1:7,' units.');delta:=shorten(3,15);
+setindices(3,delta);
+if delta>0 then writeln('I had to round some ','depth','s by ',(((delta
++1)div 2)/1048576):1:7,' units.');delta:=shorten(4,63);
+setindices(4,delta);
+if delta>0 then writeln('I had to round some ','italic correction',
+'s by ',(((delta+1)div 2)/1048576):1:7,' units.');{:115}{:110}end;{:146}
+{147:}begin initialize;nameenter;readinput;writeln('.');corrandcheck;
+{128:}{130:}lh:=headerptr div 4;notfound:=true;bc:=0;
+while notfound do if(charwd[bc]>0)or(bc=255)then notfound:=false else bc
+:=bc+1;notfound:=true;ec:=255;
+while notfound do if(charwd[ec]>0)or(ec=0)then notfound:=false else ec:=
+ec-1;if bc>ec then bc:=1;memory[1]:=memory[1]+1;memory[2]:=memory[2]+1;
+memory[3]:=memory[3]+1;memory[4]:=memory[4]+1;{139:}{140:}labelptr:=0;
+labeltable[0].rr:=-1;
+for c:=bc to ec do if chartag[c]=1 then begin sortptr:=labelptr;
+while labeltable[sortptr].rr>charremainder[c]do begin labeltable[sortptr
++1]:=labeltable[sortptr];sortptr:=sortptr-1;end;
+labeltable[sortptr+1].cc:=c;labeltable[sortptr+1].rr:=charremainder[c];
+labelptr:=labelptr+1;end{:140};
+if bchar<256 then begin extralocneeded:=true;lkoffset:=1;
+end else begin extralocneeded:=false;lkoffset:=0;end;{141:}
+begin sortptr:=labelptr;
+if labeltable[sortptr].rr+lkoffset>255 then begin lkoffset:=0;
+extralocneeded:=false;
+repeat charremainder[labeltable[sortptr].cc]:=lkoffset;
+while labeltable[sortptr-1].rr=labeltable[sortptr].rr do begin sortptr:=
+sortptr-1;charremainder[labeltable[sortptr].cc]:=lkoffset;end;
+lkoffset:=lkoffset+1;sortptr:=sortptr-1;
+until lkoffset+labeltable[sortptr].rr<256;end;
+if lkoffset>0 then while sortptr>0 do begin charremainder[labeltable[
+sortptr].cc]:=charremainder[labeltable[sortptr].cc]+lkoffset;
+sortptr:=sortptr-1;end;end{:141};
+if charremainder[256]<32767 then begin ligkern[nl-1].b2:=(charremainder[
+256]+lkoffset)div 256;
+ligkern[nl-1].b3:=(charremainder[256]+lkoffset)mod 256;end{:139};
+lf:=6+lh+(ec-bc+1)+memory[1]+memory[2]+memory[3]+memory[4]+nl+lkoffset+
+nk+ne+np;{:130};{131:}write(tfmfile,(lf)div 256);
+write(tfmfile,(lf)mod 256);write(tfmfile,(lh)div 256);
+write(tfmfile,(lh)mod 256);write(tfmfile,(bc)div 256);
+write(tfmfile,(bc)mod 256);write(tfmfile,(ec)div 256);
+write(tfmfile,(ec)mod 256);write(tfmfile,(memory[1])div 256);
+write(tfmfile,(memory[1])mod 256);write(tfmfile,(memory[2])div 256);
+write(tfmfile,(memory[2])mod 256);write(tfmfile,(memory[3])div 256);
+write(tfmfile,(memory[3])mod 256);write(tfmfile,(memory[4])div 256);
+write(tfmfile,(memory[4])mod 256);write(tfmfile,(nl+lkoffset)div 256);
+write(tfmfile,(nl+lkoffset)mod 256);write(tfmfile,(nk)div 256);
+write(tfmfile,(nk)mod 256);write(tfmfile,(ne)div 256);
+write(tfmfile,(ne)mod 256);write(tfmfile,(np)div 256);
+write(tfmfile,(np)mod 256);{:131};{133:}
+if not checksumspecified then{134:}begin curbytes.b0:=bc;
+curbytes.b1:=ec;curbytes.b2:=bc;curbytes.b3:=ec;
+for c:=bc to ec do if charwd[c]>0 then begin tempwidth:=memory[charwd[c]
+];if designunits<>1048576 then tempwidth:=round((tempwidth/designunits)
+*1048576.0);tempwidth:=tempwidth+(c+4)*4194304;
+curbytes.b0:=(curbytes.b0+curbytes.b0+tempwidth)mod 255;
+curbytes.b1:=(curbytes.b1+curbytes.b1+tempwidth)mod 253;
+curbytes.b2:=(curbytes.b2+curbytes.b2+tempwidth)mod 251;
+curbytes.b3:=(curbytes.b3+curbytes.b3+tempwidth)mod 247;end;
+headerbytes[0]:=curbytes.b0;headerbytes[1]:=curbytes.b1;
+headerbytes[2]:=curbytes.b2;headerbytes[3]:=curbytes.b3;end{:134};
+headerbytes[4]:=designsize div 16777216;
+headerbytes[5]:=(designsize div 65536)mod 256;
+headerbytes[6]:=(designsize div 256)mod 256;
+headerbytes[7]:=designsize mod 256;
+if not sevenunsafe then headerbytes[68]:=128;
+for j:=0 to headerptr-1 do write(tfmfile,headerbytes[j]);{:133};{135:}
+index[0]:=0;for c:=bc to ec do begin write(tfmfile,index[charwd[c]]);
+write(tfmfile,index[charht[c]]*16+index[chardp[c]]);
+write(tfmfile,index[charic[c]]*4+chartag[c]);
+write(tfmfile,charremainder[c]);end{:135};{137:}
+for q:=1 to 4 do begin write(tfmfile,0);write(tfmfile,0);
+write(tfmfile,0);write(tfmfile,0);p:=link[q];
+while p>0 do begin outscaled(memory[p]);p:=link[p];end;end;{:137};{142:}
+if extralocneeded then begin write(tfmfile,255);write(tfmfile,bchar);
+write(tfmfile,0);write(tfmfile,0);
+end else for sortptr:=1 to lkoffset do begin t:=labeltable[labelptr].rr;
+if bchar<256 then begin write(tfmfile,255);write(tfmfile,bchar);
+end else begin write(tfmfile,254);write(tfmfile,0);end;
+write(tfmfile,(t+lkoffset)div 256);write(tfmfile,(t+lkoffset)mod 256);
+repeat labelptr:=labelptr-1;until labeltable[labelptr].rr<t;end;
+if nl>0 then for ligptr:=0 to nl-1 do begin write(tfmfile,ligkern[ligptr
+].b0);write(tfmfile,ligkern[ligptr].b1);
+write(tfmfile,ligkern[ligptr].b2);write(tfmfile,ligkern[ligptr].b3);end;
+if nk>0 then for krnptr:=0 to nk-1 do outscaled(kern[krnptr]){:142};
+{143:}if ne>0 then for c:=0 to ne-1 do begin write(tfmfile,exten[c].b0);
+write(tfmfile,exten[c].b1);write(tfmfile,exten[c].b2);
+write(tfmfile,exten[c].b3);end;{:143};{144:}
+for parptr:=1 to np do begin if parptr=1 then{145:}
+begin if param[1]<0 then begin param[1]:=param[1]+1073741824;
+write(tfmfile,(param[1]div 16777216)+192);
+end else write(tfmfile,param[1]div 16777216);
+write(tfmfile,(param[1]div 65536)mod 256);
+write(tfmfile,(param[1]div 256)mod 256);write(tfmfile,param[1]mod 256);
+end{:145}else outscaled(param[parptr]);end{:144}{:128};
+9999:writeln(' ');end.{:147}
diff --git a/systems/knuth/local/texware/pooltype.ch b/systems/knuth/local/texware/pooltype.ch
new file mode 100644
index 0000000000..c96ec0c8cb
--- /dev/null
+++ b/systems/knuth/local/texware/pooltype.ch
@@ -0,0 +1,92 @@
+% Change file for the Pooltype processor
+% Hacked together by don on 8/7/89 since the old pooltype.ch seems lost
+% 8-bit modifications made 9/11/89
+
+% Module numbers are from the standard TeXware report (CS1097)
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{POOLTYPE changes for GNU Pascal}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Program header
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+@x
+@p program POOLtype(@!pool_file,@!output);
+label 9999; {this labels the end of the program}
+type @<Types in the outer block@>@/
+@y
+@p program POOLtype(@!output);
+label 9999; {this labels the end of the program}
+type UNIX_file_name=packed array[1..100] of char;
+@<Types in the outer block@>@/
+@z
+@x
+procedure initialize; {this procedure gets things started properly}
+@y
+@\
+@=#include "texwarext.h"@>
+@\@/
+procedure initialize; {this procedure gets things started properly}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [5] Bytes
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!ASCII_code=0..255; {eight-bit numbers}
+@y
+@!ASCII_code=ByteCard; {eight-bit numbers}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [12] Permissiveness
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for i:=0 to @'37 do xchr[i]:=' ';
+for i:=@'177 to @'377 do xchr[i]:=' ';
+@y
+for i:=0 to @'37 do xchr[i]:=chr(i);
+for i:=@'177 to @'377 do xchr[i]:=chr(i);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [15] Beginning of main program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+@x
+@p begin initialize;@/
+@y
+@p begin if argc < 2 then
+ abort('Usage: pooltype <pool-file>');
+initialize;@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [18] Input file declaration needs also a string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+@x
+@!pool_file:packed file of text_char;
+@y
+@!pool_file:text; {GNU Pascal needs this simplication}
+@!pool_name:UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [19] Opening the input file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+@x
+reset(pool_file); xsum:=false;
+@y
+argv(1,pool_name); reset(pool_file,pool_name); xsum:=false;
+@z
diff --git a/systems/knuth/local/texware/pooltype.p b/systems/knuth/local/texware/pooltype.p
new file mode 100644
index 0000000000..c6526499f3
--- /dev/null
+++ b/systems/knuth/local/texware/pooltype.p
@@ -0,0 +1,57 @@
+{2:}program POOLtype(output);label 9999;
+type UNIXfilename=packed array[1..100]of char;{5:}ASCIIcode=ByteCard;
+{:5}var{7:}xord:array[char]of ASCIIcode;xchr:array[ASCIIcode]of char;
+{:7}{12:}k,l:0..255;m,n:char;s:integer;{:12}{13:}count:integer;{:13}
+{18:}poolfile:text;poolname:UNIXfilename;xsum:boolean;{:18}
+#include "dvityext.h"
+procedure initialize;var{6:}i:integer;{:6}begin{8:}xchr[32]:=' ';
+xchr[33]:='!';xchr[34]:='"';xchr[35]:='#';xchr[36]:='$';xchr[37]:='%';
+xchr[38]:='&';xchr[39]:='''';xchr[40]:='(';xchr[41]:=')';xchr[42]:='*';
+xchr[43]:='+';xchr[44]:=',';xchr[45]:='-';xchr[46]:='.';xchr[47]:='/';
+xchr[48]:='0';xchr[49]:='1';xchr[50]:='2';xchr[51]:='3';xchr[52]:='4';
+xchr[53]:='5';xchr[54]:='6';xchr[55]:='7';xchr[56]:='8';xchr[57]:='9';
+xchr[58]:=':';xchr[59]:=';';xchr[60]:='<';xchr[61]:='=';xchr[62]:='>';
+xchr[63]:='?';xchr[64]:='@';xchr[65]:='A';xchr[66]:='B';xchr[67]:='C';
+xchr[68]:='D';xchr[69]:='E';xchr[70]:='F';xchr[71]:='G';xchr[72]:='H';
+xchr[73]:='I';xchr[74]:='J';xchr[75]:='K';xchr[76]:='L';xchr[77]:='M';
+xchr[78]:='N';xchr[79]:='O';xchr[80]:='P';xchr[81]:='Q';xchr[82]:='R';
+xchr[83]:='S';xchr[84]:='T';xchr[85]:='U';xchr[86]:='V';xchr[87]:='W';
+xchr[88]:='X';xchr[89]:='Y';xchr[90]:='Z';xchr[91]:='[';xchr[92]:='\';
+xchr[93]:=']';xchr[94]:='^';xchr[95]:='_';xchr[96]:='`';xchr[97]:='a';
+xchr[98]:='b';xchr[99]:='c';xchr[100]:='d';xchr[101]:='e';
+xchr[102]:='f';xchr[103]:='g';xchr[104]:='h';xchr[105]:='i';
+xchr[106]:='j';xchr[107]:='k';xchr[108]:='l';xchr[109]:='m';
+xchr[110]:='n';xchr[111]:='o';xchr[112]:='p';xchr[113]:='q';
+xchr[114]:='r';xchr[115]:='s';xchr[116]:='t';xchr[117]:='u';
+xchr[118]:='v';xchr[119]:='w';xchr[120]:='x';xchr[121]:='y';
+xchr[122]:='z';xchr[123]:='{';xchr[124]:='|';xchr[125]:='}';
+xchr[126]:='~';{:8}{10:}for i:=0 to 31 do xchr[i]:=chr(i);
+for i:=127 to 255 do xchr[i]:=chr(i);{:10}{11:}
+for i:=0 to 255 do xord[chr(i)]:=127;
+for i:=128 to 255 do xord[xchr[i]]:=i;
+for i:=0 to 126 do xord[xchr[i]]:=i;{:11}{14:}count:=0;{:14}end;{:2}
+{15:}begin if argc<2 then begin writeln('Usage: pooltype <pool-file>');
+goto 9999;end;initialize;{16:}for k:=0 to 255 do begin write(k:3,': "');
+l:=k;if({17:}(k<32)or(k>126){:17})then begin write(xchr[94],xchr[94]);
+if k<64 then l:=k+64 else if k<128 then l:=k-64 else begin l:=k div 16;
+if l<10 then l:=l+48 else l:=l+87;write(xchr[l]);l:=k mod 16;
+if l<10 then l:=l+48 else l:=l+87;count:=count+1;end;count:=count+2;end;
+if l=34 then write(xchr[l],xchr[l])else write(xchr[l]);count:=count+1;
+writeln('"');end{:16};s:=256;{19:}argv(1,poolname);
+reset(poolfile,poolname);xsum:=false;
+if eof(poolfile)then begin writeln('! I can''t read the POOL file.');
+goto 9999;end;repeat{20:}
+if eof(poolfile)then begin writeln('! POOL file contained no check sum')
+;goto 9999;end;read(poolfile,m,n);
+if m<>'*'then begin if(xord[m]<48)or(xord[m]>57)or(xord[n]<48)or(xord[n]
+>57)then begin writeln('! POOL line doesn''t begin with two digits');
+goto 9999;end;l:=xord[m]*10+xord[n]-48*11;write(s:3,': "');
+count:=count+l;
+for k:=1 to l do begin if eoln(poolfile)then begin writeln('"');
+begin writeln('! That POOL line was too short');goto 9999;end;end;
+read(poolfile,m);write(xchr[xord[m]]);
+if xord[m]=34 then write(xchr[34]);end;writeln('"');s:=s+1;
+end else xsum:=true;readln(poolfile){:20};until xsum;
+if not eof(poolfile)then begin writeln(
+'! There''s junk after the check sum');goto 9999;end{:19};
+writeln('(',count:1,' characters in all.)');9999:end.{:15}
diff --git a/systems/knuth/local/texware/texwarext.h b/systems/knuth/local/texware/texwarext.h
new file mode 100644
index 0000000000..a183a12814
--- /dev/null
+++ b/systems/knuth/local/texware/texwarext.h
@@ -0,0 +1,5 @@
+argc:asmname '_p_argc' Integer;
+
+procedure argv(no:Integer;var str:UNIXfilename); external;
+
+function testreadaccess(var a:UNIXfilename):boolean; external;
diff --git a/systems/knuth/local/texware/tftopl.ch b/systems/knuth/local/texware/tftopl.ch
new file mode 100644
index 0000000000..2c1304d3b5
--- /dev/null
+++ b/systems/knuth/local/texware/tftopl.ch
@@ -0,0 +1,128 @@
+% Change file for the TFtoPL processor, for use with GNU Pascal.
+% (by Don Knuth, based on the work of Pavel Curtis in ../texware-sparc)
+
+% History:
+% 2000.04.30 Original version
+
+% The section numbers used in this file refer to the current numbers,
+% and, where different, also to those in the standard TeXware report (CS1097).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{TF\lowercase{to}PL changes for GNU Pascal}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner string
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is TFtoPL, Version 3.1' {printed when the program starts}
+@y
+@d banner=='This is TFtoPL, Version 3.1 for Linux'
+ {printed when the program starts}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] Fix files in program statement; add external access procedure
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p program TFtoPL(@!tfm_file,@!pl_file,@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@y
+@p program TFtoPL(@!output);
+label @<Labels in the outer block@>@/
+const @<Constants in the outer block@>@/
+type UNIX_file_name=packed array[1..100] of char;
+ @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\
+@=#include "texwarext.h"@>
+@\@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [6] Fix declaration of tfm_file; declare extra TFM-file variables
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!tfm_file:packed file of 0..255;
+@y
+@!tfm_file:packed file of ByteCard; {files that contain binary data}
+@!tfm_name:UNIX_file_name;
+@!tfm_byte:integer;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Open TFM file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ On some systems you may have to do something special to read a
+packed file of bytes. For example, the following code didn't work
+when it was first tried at Stanford, because packed files have to be
+opened with a special switch setting on the \PASCAL\ that was used.
+@^system dependencies@>
+
+@<Set init...@>=
+reset(tfm_file);
+@y
+@ On some systems you may have to do something special to read a
+packed file of bytes.
+
+@<Set init...@>=
+if argc < 3 then begin
+ print_ln('Usage: tftopl <tfm-file> <pl-file>');
+ goto final_end;
+end;
+argv(1, tfm_name);
+if testreadaccess(tfm_name) then reset(tfm_file, tfm_name)
+else begin print_ln('I can''t read the TFM file!'); goto final_end;
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [16] Declare pl_name
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!pl_file:text;
+@y
+@!pl_file:text;
+@!pl_name: UNIX_file_name;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [17] Open PL file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ @<Set init...@>=
+rewrite(pl_file);
+@y
+@ @<Set init...@>=
+argv(2, pl_name);
+rewrite(pl_file, pl_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [18] Fix definition of byte
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!byte=0..255; {unsigned eight-bit quantity}
+@y
+@!byte=ByteCard; {unsigned eight-bit quantity}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [99, was 88] Add printing of newline at end of program
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+final_end:end.
+@y
+final_end: print_ln(' '); end.
+@z
diff --git a/systems/knuth/local/texware/tftopl.p b/systems/knuth/local/texware/tftopl.p
new file mode 100644
index 0000000000..4aaa676b47
--- /dev/null
+++ b/systems/knuth/local/texware/tftopl.p
@@ -0,0 +1,504 @@
+{2:}program TFtoPL(output);label{3:}9999;{:3}const{4:}tfmsize=30000;
+ligsize=5000;hashsize=5003;{:4}
+type UNIXfilename=packed array[1..100]of char;{18:}byte=ByteCard;
+index=0..tfmsize;{:18}var{6:}tfmfile:packed file of ByteCard;
+tfmname:UNIXfilename;tfmbyte:integer;{:6}{8:}
+lf,lh,bc,ec,nw,nh,nd,ni,nl,nk,ne,np:0..32767;{:8}{16:}plfile:text;
+plname:UNIXfilename;{:16}{19:}tfm:array[-1000..tfmsize]of byte;{:19}
+{22:}
+charbase,widthbase,heightbase,depthbase,italicbase,ligkernbase,kernbase,
+extenbase,parambase:integer;{:22}{25:}fonttype:0..2;{:25}{27:}
+ASCII04,ASCII10,ASCII14:packed array[1..32]of char;
+MBLstring,RIstring,RCEstring:packed array[1..3]of char;{:27}{29:}
+dig:array[0..11]of 0..9;{:29}{32:}level:0..5;{:32}{45:}charsonline:0..8;
+perfect:boolean;{:45}{47:}i:0..32767;c:0..256;d:0..3;k:index;r:0..65535;
+count:0..127;{:47}{63:}labeltable:array[0..258]of record cc:0..256;
+rr:0..ligsize;end;labelptr:0..257;sortptr:0..257;boundarychar:0..256;
+bcharlabel:0..32767;{:63}{65:}activity:array[0..ligsize]of 0..2;
+ai,acti:0..ligsize;{:65}{89:}hash:array[0..hashsize]of 0..66048;
+class:array[0..hashsize]of 0..4;ligz:array[0..hashsize]of 0..257;
+hashptr:0..hashsize;hashlist:array[0..hashsize]of 0..hashsize;
+h,hh:0..hashsize;xligcycle,yligcycle:0..256;{:89}
+#include "texwarext.h"
+procedure initialize;
+begin writeln('This is TFtoPL, Version 3.1 for Linux');{7:}
+if argc<3 then begin writeln('Usage: tftopl <tfm-file> <pl-file>');
+goto 9999;end;argv(1,tfmname);
+if testreadaccess(tfmname)then reset(tfmfile,tfmname)else begin writeln(
+'I can''t read the TFM file!');goto 9999;end;{:7}{17:}argv(2,plname);
+rewrite(plfile,plname);{:17}{28:}
+ASCII04:=' !"#$%&''()*+,-./0123456789:;<=>?';
+ASCII10:='@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_';
+ASCII14:='`abcdefghijklmnopqrstuvwxyz{|}~ ';MBLstring:='MBL';
+RIstring:='RI ';RCEstring:='RCE';{:28}{33:}level:=0;{:33}{46:}
+charsonline:=0;perfect:=true;{:46}{64:}boundarychar:=256;
+bcharlabel:=32767;labelptr:=0;labeltable[0].rr:=0;{:64}end;{:2}{30:}
+procedure outdigs(j:integer);begin repeat j:=j-1;write(plfile,dig[j]:1);
+until j=0;end;procedure printdigs(j:integer);begin repeat j:=j-1;
+write(dig[j]:1);until j=0;end;{:30}{31:}procedure printoctal(c:byte);
+var j:0..2;begin write('''');for j:=0 to 2 do begin dig[j]:=c mod 8;
+c:=c div 8;end;printdigs(3);end;{:31}{34:}procedure outln;var l:0..5;
+begin writeln(plfile);for l:=1 to level do write(plfile,' ');end;
+procedure left;begin level:=level+1;write(plfile,'(');end;
+procedure right;begin level:=level-1;write(plfile,')');outln;end;{:34}
+{35:}procedure outBCPL(k:index);var l:0..39;begin write(plfile,' ');
+l:=tfm[k];while l>0 do begin k:=k+1;l:=l-1;
+case tfm[k]div 32 of 1:write(plfile,ASCII04[1+(tfm[k]mod 32)]);
+2:write(plfile,ASCII10[1+(tfm[k]mod 32)]);
+3:write(plfile,ASCII14[1+(tfm[k]mod 32)]);end;end;end;{:35}{36:}
+procedure outoctal(k,l:index);var a:0..1023;b:0..32;j:0..11;
+begin write(plfile,' O ');a:=0;b:=0;j:=0;while l>0 do{37:}begin l:=l-1;
+if tfm[k+l]<>0 then begin while b>2 do begin dig[j]:=a mod 8;a:=a div 8;
+b:=b-3;j:=j+1;end;case b of 0:a:=tfm[k+l];1:a:=a+2*tfm[k+l];
+2:a:=a+4*tfm[k+l];end;end;b:=b+8;end{:37};
+while(a>0)or(j=0)do begin dig[j]:=a mod 8;a:=a div 8;j:=j+1;end;
+outdigs(j);end;{:36}{38:}procedure outchar(c:byte);
+begin if fonttype>0 then begin tfm[0]:=c;
+outoctal(0,1)end else if(c>=48)and(c<=57)then write(plfile,' C ',c-48:1)
+else if(c>=65)and(c<=90)then write(plfile,' C ',ASCII10[c-63])else if(c
+>=97)and(c<=122)then write(plfile,' C ',ASCII14[c-95])else begin tfm[0]
+:=c;outoctal(0,1);end;end;{:38}{39:}procedure outface(k:index);
+var s:0..1;b:0..8;
+begin if tfm[k]>=18 then outoctal(k,1)else begin write(plfile,' F ');
+s:=tfm[k]mod 2;b:=tfm[k]div 2;write(plfile,MBLstring[1+(b mod 3)]);
+write(plfile,RIstring[1+s]);write(plfile,RCEstring[1+(b div 3)]);end;
+end;{:39}{40:}procedure outfix(k:index);var a:0..4095;f:integer;j:0..12;
+delta:integer;begin write(plfile,' R ');a:=(tfm[k]*16)+(tfm[k+1]div 16);
+f:=((tfm[k+1]mod 16)*256+tfm[k+2])*256+tfm[k+3];if a>2047 then{43:}
+begin write(plfile,'-');a:=4096-a;if f>0 then begin f:=1048576-f;a:=a-1;
+end;end{:43};{41:}begin j:=0;repeat dig[j]:=a mod 10;a:=a div 10;j:=j+1;
+until a=0;outdigs(j);end{:41};{42:}begin write(plfile,'.');f:=10*f+5;
+delta:=10;repeat if delta>1048576 then f:=f+524288-(delta div 2);
+write(plfile,f div 1048576:1);f:=10*(f mod 1048576);delta:=delta*10;
+until f<=delta;end;{:42};end;{:40}{52:}procedure checkBCPL(k,l:index);
+var j:index;c:byte;begin if tfm[k]>=l then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ',
+'String is too long; I''ve shortened it drastically.');end;tfm[k]:=1;
+end;for j:=k+1 to k+tfm[k]do begin c:=tfm[j];
+if(c=40)or(c=41)then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ',
+'Parenthesis in string has been changed to slash.');end;tfm[j]:=47;
+end else if(c<32)or(c>126)then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Nonstandard ASCII code has been blotted out.')
+;end;tfm[j]:=63;end else if(c>=97)and(c<=122)then tfm[j]:=c-32;end;end;
+{:52}{92:}procedure hashinput;label 30;var cc:0..3;zz:0..255;y:0..255;
+key:integer;t:integer;begin if hashptr=hashsize then goto 30;{93:}
+k:=4*(ligkernbase+(i));y:=tfm[k+1];t:=tfm[k+2];cc:=0;zz:=tfm[k+3];
+if t>=128 then zz:=y else begin case t of 0,6:;5,11:zz:=y;1,7:cc:=1;
+2:cc:=2;3:cc:=3;end;end{:93};key:=256*c+y+1;h:=(1009*key)mod hashsize;
+while hash[h]>0 do begin if hash[h]<=key then begin if hash[h]=key then
+goto 30;t:=hash[h];hash[h]:=key;key:=t;t:=class[h];class[h]:=cc;cc:=t;
+t:=ligz[h];ligz[h]:=zz;zz:=t;end;if h>0 then h:=h-1 else h:=hashsize;
+end;hash[h]:=key;class[h]:=cc;ligz[h]:=zz;hashptr:=hashptr+1;
+hashlist[hashptr]:=h;30:end;{:92}{94:}function f(h,x,y:index):index;
+forward;function eval(x,y:index):index;var key:integer;
+begin key:=256*x+y+1;h:=(1009*key)mod hashsize;
+while hash[h]>key do if h>0 then h:=h-1 else h:=hashsize;
+if hash[h]<key then eval:=y else eval:=f(h,x,y);end;{:94}{95:}
+function f;begin case class[h]of 0:;1:begin class[h]:=4;
+ligz[h]:=eval(ligz[h],y);class[h]:=0;end;2:begin class[h]:=4;
+ligz[h]:=eval(x,ligz[h]);class[h]:=0;end;3:begin class[h]:=4;
+ligz[h]:=eval(eval(x,ligz[h]),y);class[h]:=0;end;4:begin xligcycle:=x;
+yligcycle:=y;ligz[h]:=257;class[h]:=0;end;end;f:=ligz[h];end;{:95}{96:}
+function organize:boolean;label 9999,30;var tfmptr:index;begin{20:}
+read(tfmfile,tfm[0]);if tfm[0]>127 then begin writeln(
+'The first byte of the input file exceeds 127!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;if eof(tfmfile)then begin writeln(
+'The input file is only one byte long!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;read(tfmfile,tfm[1]);lf:=tfm[0]*256+tfm[1];
+if lf=0 then begin writeln(
+'The file claims to have length zero, but that''s impossible!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;if 4*lf-1>tfmsize then begin writeln(
+'The file is bigger than I can handle!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;
+for tfmptr:=2 to 4*lf-1 do begin if eof(tfmfile)then begin writeln(
+'The file has fewer bytes than it claims!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;read(tfmfile,tfm[tfmptr]);end;
+if not eof(tfmfile)then begin writeln(
+'There''s some extra junk at the end of the TFM file,');
+writeln('but I''ll proceed as if it weren''t there.');end{:20};{21:}
+begin tfmptr:=2;begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;lh:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;bc:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;ec:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;nw:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;nh:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;nd:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;ni:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;nl:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;nk:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;ne:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+begin if tfm[tfmptr]>127 then begin writeln(
+'One of the subfile sizes is negative!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;np:=tfm[tfmptr]*256+tfm[tfmptr+1];tfmptr:=tfmptr+2;end;
+if lh<2 then begin writeln('The header length is only ',lh:1,'!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;if nl>4*ligsize then begin writeln(
+'The lig/kern program is longer than I can handle!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;
+if(bc>ec+1)or(ec>255)then begin writeln('The character code range ',bc:1
+,'..',ec:1,'is illegal!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;if(nw=0)or(nh=0)or(nd=0)or(ni=0)then begin writeln(
+'Incomplete subfiles for character dimensions!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;
+if ne>256 then begin writeln('There are ',ne:1,' extensible recipes!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;
+if lf<>6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np then begin writeln(
+'Subfile sizes don''t add up to the stated total!');
+writeln('Sorry, but I can''t go on; are you sure this is a TFM?');
+goto 9999;end;end{:21};{23:}begin charbase:=6+lh-bc;
+widthbase:=charbase+ec+1;heightbase:=widthbase+nw;
+depthbase:=heightbase+nh;italicbase:=depthbase+nd;
+ligkernbase:=italicbase+ni;kernbase:=ligkernbase+nl;
+extenbase:=kernbase+nk;parambase:=extenbase+ne-1;end{:23};
+organize:=true;goto 30;9999:organize:=false;30:end;{:96}{97:}
+procedure dosimplethings;var i:0..32767;begin{48:}begin fonttype:=0;
+if lh>=12 then begin{53:}begin checkBCPL(32,40);
+if(tfm[32]>=11)and(tfm[33]=84)and(tfm[34]=69)and(tfm[35]=88)and(tfm[36]=
+32)and(tfm[37]=77)and(tfm[38]=65)and(tfm[39]=84)and(tfm[40]=72)and(tfm[
+41]=32)then begin if(tfm[42]=83)and(tfm[43]=89)then fonttype:=1 else if(
+tfm[42]=69)and(tfm[43]=88)then fonttype:=2;end;end{:53};
+if lh>=17 then begin{55:}left;write(plfile,'FAMILY');checkBCPL(72,20);
+outBCPL(72);right{:55};if lh>=18 then{56:}begin left;
+write(plfile,'FACE');outface(95);right;for i:=18 to lh-1 do begin left;
+write(plfile,'HEADER D ',i:1);outoctal(24+4*i,4);right;end;end{:56};end;
+{54:}left;write(plfile,'CODINGSCHEME');outBCPL(32);right{:54};end;{51:}
+left;write(plfile,'DESIGNSIZE');
+if tfm[28]>127 then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Design size ','negative','!');end;
+writeln('I''ve set it to 10 points.');write(plfile,' D 10');
+end else if(tfm[28]=0)and(tfm[29]<16)then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Design size ','too small','!');end;
+writeln('I''ve set it to 10 points.');write(plfile,' D 10');
+end else outfix(28);right;
+write(plfile,'(COMMENT DESIGNSIZE IS IN POINTS)');outln;
+write(plfile,'(COMMENT OTHER SIZES ARE MULTIPLES OF DESIGNSIZE)');
+outln{:51};{49:}left;write(plfile,'CHECKSUM');outoctal(24,4);right{:49};
+{57:}if(lh>17)and(tfm[92]>127)then begin left;
+write(plfile,'SEVENBITSAFEFLAG TRUE');right;end{:57};end{:48};{58:}
+if np>0 then begin left;write(plfile,'FONTDIMEN');outln;
+for i:=1 to np do{60:}begin left;
+if i=1 then write(plfile,'SLANT')else begin if(tfm[4*(parambase+i)]>0)
+and(tfm[4*(parambase+i)]<255)then begin tfm[4*(parambase+i)]:=0;
+tfm[(4*(parambase+i))+1]:=0;tfm[(4*(parambase+i))+2]:=0;
+tfm[(4*(parambase+i))+3]:=0;begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Parameter',' ',i:1,' is too big;');end;
+writeln('I have set it to zero.');end;{61:}
+if i<=7 then case i of 2:write(plfile,'SPACE');
+3:write(plfile,'STRETCH');4:write(plfile,'SHRINK');
+5:write(plfile,'XHEIGHT');6:write(plfile,'QUAD');
+7:write(plfile,'EXTRASPACE')end else if(i<=22)and(fonttype=1)then case i
+of 8:write(plfile,'NUM1');9:write(plfile,'NUM2');
+10:write(plfile,'NUM3');11:write(plfile,'DENOM1');
+12:write(plfile,'DENOM2');13:write(plfile,'SUP1');
+14:write(plfile,'SUP2');15:write(plfile,'SUP3');16:write(plfile,'SUB1');
+17:write(plfile,'SUB2');18:write(plfile,'SUPDROP');
+19:write(plfile,'SUBDROP');20:write(plfile,'DELIM1');
+21:write(plfile,'DELIM2');
+22:write(plfile,'AXISHEIGHT')end else if(i<=13)and(fonttype=2)then if i=
+8 then write(plfile,'DEFAULTRULETHICKNESS')else write(plfile,
+'BIGOPSPACING',i-8:1)else write(plfile,'PARAMETER D ',i:1){:61};end;
+outfix(4*(parambase+i));right;end{:60};right;end;{59:}
+if(fonttype=1)and(np<>22)then writeln(
+'Unusual number of fontdimen parameters for a math symbols font (',np:1,
+' not 22).')else if(fonttype=2)and(np<>13)then writeln(
+'Unusual number of fontdimen parameters for an extension font (',np:1,
+' not 13).'){:59};{:58};{62:}
+if(tfm[4*widthbase]>0)or(tfm[4*widthbase+1]>0)or(tfm[4*widthbase+2]>0)or
+(tfm[4*widthbase+3]>0)then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','width[0] should be zero.');end;
+if(tfm[4*heightbase]>0)or(tfm[4*heightbase+1]>0)or(tfm[4*heightbase+2]>0
+)or(tfm[4*heightbase+3]>0)then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','height[0] should be zero.');end;
+if(tfm[4*depthbase]>0)or(tfm[4*depthbase+1]>0)or(tfm[4*depthbase+2]>0)or
+(tfm[4*depthbase+3]>0)then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','depth[0] should be zero.');end;
+if(tfm[4*italicbase]>0)or(tfm[4*italicbase+1]>0)or(tfm[4*italicbase+2]>0
+)or(tfm[4*italicbase+3]>0)then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','italic[0] should be zero.');end;
+for i:=0 to nw-1 do if(tfm[4*(widthbase+i)]>0)and(tfm[4*(widthbase+i)]<
+255)then begin tfm[4*(widthbase+i)]:=0;tfm[(4*(widthbase+i))+1]:=0;
+tfm[(4*(widthbase+i))+2]:=0;tfm[(4*(widthbase+i))+3]:=0;
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Width',' ',i:1,' is too big;');end;
+writeln('I have set it to zero.');end;
+for i:=0 to nh-1 do if(tfm[4*(heightbase+i)]>0)and(tfm[4*(heightbase+i)]
+<255)then begin tfm[4*(heightbase+i)]:=0;tfm[(4*(heightbase+i))+1]:=0;
+tfm[(4*(heightbase+i))+2]:=0;tfm[(4*(heightbase+i))+3]:=0;
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Height',' ',i:1,' is too big;');end;
+writeln('I have set it to zero.');end;
+for i:=0 to nd-1 do if(tfm[4*(depthbase+i)]>0)and(tfm[4*(depthbase+i)]<
+255)then begin tfm[4*(depthbase+i)]:=0;tfm[(4*(depthbase+i))+1]:=0;
+tfm[(4*(depthbase+i))+2]:=0;tfm[(4*(depthbase+i))+3]:=0;
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Depth',' ',i:1,' is too big;');end;
+writeln('I have set it to zero.');end;
+for i:=0 to ni-1 do if(tfm[4*(italicbase+i)]>0)and(tfm[4*(italicbase+i)]
+<255)then begin tfm[4*(italicbase+i)]:=0;tfm[(4*(italicbase+i))+1]:=0;
+tfm[(4*(italicbase+i))+2]:=0;tfm[(4*(italicbase+i))+3]:=0;
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Italic correction',' ',i:1,' is too big;');
+end;writeln('I have set it to zero.');end;
+if nk>0 then for i:=0 to nk-1 do if(tfm[4*(kernbase+i)]>0)and(tfm[4*(
+kernbase+i)]<255)then begin tfm[4*(kernbase+i)]:=0;
+tfm[(4*(kernbase+i))+1]:=0;tfm[(4*(kernbase+i))+2]:=0;
+tfm[(4*(kernbase+i))+3]:=0;begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Kern',' ',i:1,' is too big;');end;
+writeln('I have set it to zero.');end;{:62}end;{:97}{98:}
+procedure docharacters;var c:byte;k:index;ai:0..ligsize;begin{78:}
+sortptr:=0;
+for c:=bc to ec do if tfm[4*(charbase+c)]>0 then begin if charsonline=8
+then begin writeln(' ');charsonline:=1;
+end else begin if charsonline>0 then write(' ');
+charsonline:=charsonline+1;end;printoctal(c);left;
+write(plfile,'CHARACTER');outchar(c);outln;{79:}begin left;
+write(plfile,'CHARWD');
+if tfm[4*(charbase+c)]>=nw then begin perfect:=false;writeln(' ');
+write('Width',' index for character ');printoctal(c);
+writeln(' is too large;');writeln('so I reset it to zero.');
+end else outfix(4*(widthbase+tfm[4*(charbase+c)]));right;end{:79};
+if(tfm[4*(charbase+c)+1]div 16)>0 then{80:}
+if(tfm[4*(charbase+c)+1]div 16)>=nh then begin perfect:=false;
+writeln(' ');write('Height',' index for character ');printoctal(c);
+writeln(' is too large;');writeln('so I reset it to zero.');
+end else begin left;write(plfile,'CHARHT');
+outfix(4*(heightbase+(tfm[4*(charbase+c)+1]div 16)));right;end{:80};
+if(tfm[4*(charbase+c)+1]mod 16)>0 then{81:}
+if(tfm[4*(charbase+c)+1]mod 16)>=nd then begin perfect:=false;
+writeln(' ');write('Depth',' index for character ');printoctal(c);
+writeln(' is too large;');writeln('so I reset it to zero.');
+end else begin left;write(plfile,'CHARDP');
+outfix(4*(depthbase+(tfm[4*(charbase+c)+1]mod 16)));right;end{:81};
+if(tfm[4*(charbase+c)+2]div 4)>0 then{82:}
+if(tfm[4*(charbase+c)+2]div 4)>=ni then begin perfect:=false;
+writeln(' ');write('Italic correction',' index for character ');
+printoctal(c);writeln(' is too large;');
+writeln('so I reset it to zero.');end else begin left;
+write(plfile,'CHARIC');
+outfix(4*(italicbase+(tfm[4*(charbase+c)+2]div 4)));right;end{:82};
+case(tfm[4*(charbase+c)+2]mod 4)of 0:;1:{83:}begin left;
+write(plfile,'COMMENT');outln;i:=tfm[4*(charbase+c)+3];
+r:=4*(ligkernbase+(i));if tfm[r]>128 then i:=256*tfm[r+2]+tfm[r+3];
+repeat{74:}begin k:=4*(ligkernbase+(i));
+if tfm[k]>128 then begin if 256*tfm[k+2]+tfm[k+3]>=nl then begin perfect
+:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ',
+'Ligature unconditional stop command address is too big.');end;
+end else if tfm[k+2]>=128 then{76:}
+begin if((tfm[k+1]<bc)or(tfm[k+1]>ec)or(tfm[4*(charbase+tfm[k+1])]=0))
+then if tfm[k+1]<>boundarychar then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Kern step for',' nonexistent character ');
+printoctal(tfm[k+1]);writeln('.');tfm[k+1]:=bc;end;left;
+write(plfile,'KRN');outchar(tfm[k+1]);r:=256*(tfm[k+2]-128)+tfm[k+3];
+if r>=nk then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Kern index too large.');end;
+write(plfile,' R 0.0');end else outfix(4*(kernbase+r));right;end{:76}
+else{77:}
+begin if((tfm[k+1]<bc)or(tfm[k+1]>ec)or(tfm[4*(charbase+tfm[k+1])]=0))
+then if tfm[k+1]<>boundarychar then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Ligature step for',' nonexistent character ');
+printoctal(tfm[k+1]);writeln('.');tfm[k+1]:=bc;end;
+if((tfm[k+3]<bc)or(tfm[k+3]>ec)or(tfm[4*(charbase+tfm[k+3])]=0))then
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Ligature step produces the',
+' nonexistent character ');printoctal(tfm[k+3]);writeln('.');
+tfm[k+3]:=bc;end;left;r:=tfm[k+2];
+if(r=4)or((r>7)and(r<>11))then begin writeln(
+'Ligature step with nonstandard code changed to LIG');r:=0;tfm[k+2]:=0;
+end;if r mod 4>1 then write(plfile,'/');write(plfile,'LIG');
+if odd(r)then write(plfile,'/');while r>3 do begin write(plfile,'>');
+r:=r-4;end;outchar(tfm[k+1]);outchar(tfm[k+3]);right;end{:77};
+if tfm[k]>0 then if level=1 then{75:}
+begin if tfm[k]>=128 then write(plfile,'(STOP)')else begin count:=0;
+for ai:=i+1 to i+tfm[k]do if activity[ai]=2 then count:=count+1;
+write(plfile,'(SKIP D ',count:1,')');end;outln;end{:75};end{:74};
+if tfm[k]>=128 then i:=nl else i:=i+1+tfm[k];until i>=nl;right;end{:83};
+2:{84:}begin r:=tfm[4*(charbase+c)+3];
+if((r<bc)or(r>ec)or(tfm[4*(charbase+r)]=0))then begin begin perfect:=
+false;if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Character list link to',
+' nonexistent character ');printoctal(r);writeln('.');end;
+tfm[4*(charbase+c)+2]:=4*(tfm[4*(charbase+c)+2]div 4)+0;
+end else begin while(r<c)and((tfm[4*(charbase+r)+2]mod 4)=2)do r:=tfm[4*
+(charbase+r)+3];if r=c then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Cycle in a character list!');end;
+write('Character ');printoctal(c);writeln(' now ends the list.');
+tfm[4*(charbase+c)+2]:=4*(tfm[4*(charbase+c)+2]div 4)+0;
+end else begin left;write(plfile,'NEXTLARGER');
+outchar(tfm[4*(charbase+c)+3]);right;end;end;end{:84};3:{85:}
+if tfm[4*(charbase+c)+3]>=ne then begin begin perfect:=false;
+writeln(' ');write('Extensible',' index for character ');printoctal(c);
+writeln(' is too large;');writeln('so I reset it to zero.');end;
+tfm[4*(charbase+c)+2]:=4*(tfm[4*(charbase+c)+2]div 4)+0;
+end else begin left;write(plfile,'VARCHAR');outln;{86:}
+for k:=0 to 3 do if(k=3)or(tfm[4*(extenbase+tfm[4*(charbase+c)+3])+k]>0)
+then begin left;case k of 0:write(plfile,'TOP');1:write(plfile,'MID');
+2:write(plfile,'BOT');3:write(plfile,'REP')end;
+if((tfm[4*(extenbase+tfm[4*(charbase+c)+3])+k]<bc)or(tfm[4*(extenbase+
+tfm[4*(charbase+c)+3])+k]>ec)or(tfm[4*(charbase+tfm[4*(extenbase+tfm[4*(
+charbase+c)+3])+k])]=0))then outchar(c)else outchar(tfm[4*(extenbase+tfm
+[4*(charbase+c)+3])+k]);right;end{:86};right;end{:85};end;right;end{:78}
+;end;{:98}{99:}begin initialize;if not organize then goto 9999;
+dosimplethings;{66:}
+if nl>0 then begin for ai:=0 to nl-1 do activity[ai]:=0;{69:}
+if tfm[4*(ligkernbase+(0))]=255 then begin left;
+write(plfile,'BOUNDARYCHAR');boundarychar:=tfm[4*(ligkernbase+(0))+1];
+outchar(boundarychar);right;activity[0]:=1;end;
+if tfm[4*(ligkernbase+(nl-1))]=255 then begin r:=256*tfm[4*(ligkernbase+
+(nl-1))+2]+tfm[4*(ligkernbase+(nl-1))+3];
+if r>=nl then begin perfect:=false;writeln(' ');
+write('Ligature/kern starting index for boundarychar is too large;');
+writeln('so I removed it.');end else begin labelptr:=1;
+labeltable[1].cc:=256;labeltable[1].rr:=r;bcharlabel:=r;activity[r]:=2;
+end;activity[nl-1]:=1;end{:69};end;{67:}
+for c:=bc to ec do if(tfm[4*(charbase+c)+2]mod 4)=1 then begin r:=tfm[4*
+(charbase+c)+3];
+if r<nl then begin if tfm[4*(ligkernbase+(r))]>128 then begin r:=256*tfm
+[4*(ligkernbase+(r))+2]+tfm[4*(ligkernbase+(r))+3];
+if r<nl then if activity[tfm[4*(charbase+c)+3]]=0 then activity[tfm[4*(
+charbase+c)+3]]:=1;end;end;if r>=nl then begin perfect:=false;
+writeln(' ');write('Ligature/kern starting index for character ');
+printoctal(c);writeln(' is too large;');writeln('so I removed it.');
+tfm[4*(charbase+c)+2]:=4*(tfm[4*(charbase+c)+2]div 4)+0;end else{68:}
+begin sortptr:=labelptr;
+while labeltable[sortptr].rr>r do begin labeltable[sortptr+1]:=
+labeltable[sortptr];sortptr:=sortptr-1;end;labeltable[sortptr+1].cc:=c;
+labeltable[sortptr+1].rr:=r;labelptr:=labelptr+1;activity[r]:=2;end{:68}
+;end;labeltable[labelptr+1].rr:=ligsize;{:67};if nl>0 then begin left;
+write(plfile,'LIGTABLE');outln;{70:}
+for ai:=0 to nl-1 do if activity[ai]=2 then begin r:=tfm[4*(ligkernbase+
+(ai))];if r<128 then begin r:=r+ai+1;
+if r>=nl then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Ligature/kern step ',ai:1,' skips too far;');
+end;writeln('I made it stop.');tfm[4*(ligkernbase+(ai))]:=128;
+end else activity[r]:=2;end;end{:70};{71:}sortptr:=1;
+for acti:=0 to nl-1 do if activity[acti]<>1 then begin i:=acti;{73:}
+if activity[i]=0 then begin if level=1 then begin left;
+write(plfile,'COMMENT THIS PART OF THE PROGRAM IS NEVER USED!');outln;
+end end else if level=2 then right{:73};{72:}
+while i=labeltable[sortptr].rr do begin left;write(plfile,'LABEL');
+if labeltable[sortptr].cc=256 then write(plfile,' BOUNDARYCHAR')else
+outchar(labeltable[sortptr].cc);right;sortptr:=sortptr+1;end{:72};{74:}
+begin k:=4*(ligkernbase+(i));
+if tfm[k]>128 then begin if 256*tfm[k+2]+tfm[k+3]>=nl then begin perfect
+:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ',
+'Ligature unconditional stop command address is too big.');end;
+end else if tfm[k+2]>=128 then{76:}
+begin if((tfm[k+1]<bc)or(tfm[k+1]>ec)or(tfm[4*(charbase+tfm[k+1])]=0))
+then if tfm[k+1]<>boundarychar then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Kern step for',' nonexistent character ');
+printoctal(tfm[k+1]);writeln('.');tfm[k+1]:=bc;end;left;
+write(plfile,'KRN');outchar(tfm[k+1]);r:=256*(tfm[k+2]-128)+tfm[k+3];
+if r>=nk then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+writeln('Bad TFM file: ','Kern index too large.');end;
+write(plfile,' R 0.0');end else outfix(4*(kernbase+r));right;end{:76}
+else{77:}
+begin if((tfm[k+1]<bc)or(tfm[k+1]>ec)or(tfm[4*(charbase+tfm[k+1])]=0))
+then if tfm[k+1]<>boundarychar then begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Ligature step for',' nonexistent character ');
+printoctal(tfm[k+1]);writeln('.');tfm[k+1]:=bc;end;
+if((tfm[k+3]<bc)or(tfm[k+3]>ec)or(tfm[4*(charbase+tfm[k+3])]=0))then
+begin perfect:=false;if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Ligature step produces the',
+' nonexistent character ');printoctal(tfm[k+3]);writeln('.');
+tfm[k+3]:=bc;end;left;r:=tfm[k+2];
+if(r=4)or((r>7)and(r<>11))then begin writeln(
+'Ligature step with nonstandard code changed to LIG');r:=0;tfm[k+2]:=0;
+end;if r mod 4>1 then write(plfile,'/');write(plfile,'LIG');
+if odd(r)then write(plfile,'/');while r>3 do begin write(plfile,'>');
+r:=r-4;end;outchar(tfm[k+1]);outchar(tfm[k+3]);right;end{:77};
+if tfm[k]>0 then if level=1 then{75:}
+begin if tfm[k]>=128 then write(plfile,'(STOP)')else begin count:=0;
+for ai:=i+1 to i+tfm[k]do if activity[ai]=2 then count:=count+1;
+write(plfile,'(SKIP D ',count:1,')');end;outln;end{:75};end{:74};end;
+if level=2 then right{:71};right;{90:}hashptr:=0;yligcycle:=256;
+for hh:=0 to hashsize do hash[hh]:=0;
+for c:=bc to ec do if(tfm[4*(charbase+c)+2]mod 4)=1 then begin i:=tfm[4*
+(charbase+c)+3];
+if tfm[4*(ligkernbase+(i))]>128 then i:=256*tfm[4*(ligkernbase+(i))+2]+
+tfm[4*(ligkernbase+(i))+3];{91:}repeat hashinput;
+k:=tfm[4*(ligkernbase+(i))];if k>=128 then i:=nl else i:=i+1+k;
+until i>=nl{:91};end;if bcharlabel<nl then begin c:=256;i:=bcharlabel;
+{91:}repeat hashinput;k:=tfm[4*(ligkernbase+(i))];
+if k>=128 then i:=nl else i:=i+1+k;until i>=nl{:91};end;
+if hashptr=hashsize then begin writeln(
+'Sorry, I haven''t room for so many ligature/kern pairs!');goto 9999;
+end;for hh:=1 to hashptr do begin r:=hashlist[hh];
+if class[r]>0 then r:=f(r,(hash[r]-1)div 256,(hash[r]-1)mod 256);end;
+if yligcycle<256 then begin write(
+'Infinite ligature loop starting with ');
+if xligcycle=256 then write('boundary')else printoctal(xligcycle);
+write(' and ');printoctal(yligcycle);writeln('!');
+write(plfile,'(INFINITE LIGATURE LOOP MUST BE BROKEN!)');goto 9999;
+end{:90};end{:66};{87:}
+if ne>0 then for c:=0 to ne-1 do for d:=0 to 3 do begin k:=4*(extenbase+
+c)+d;if(tfm[k]>0)or(d=3)then begin if((tfm[k]<bc)or(tfm[k]>ec)or(tfm[4*(
+charbase+tfm[k])]=0))then begin begin perfect:=false;
+if charsonline>0 then writeln(' ');charsonline:=0;
+write('Bad TFM file: ','Extensible recipe involves the',
+' nonexistent character ');printoctal(tfm[k]);writeln('.');end;
+if d<3 then tfm[k]:=0;end;end;end{:87};docharacters;writeln('.');
+if level<>0 then writeln('This program isn''t working!');
+if not perfect then write(plfile,
+'(COMMENT THE TFM FILE WAS BAD, SO THE DATA HAS BEEN CHANGED!)');
+9999:writeln(' ');end.{:99}
diff --git a/systems/knuth/local/texware/tftopl.pool b/systems/knuth/local/texware/tftopl.pool
new file mode 100644
index 0000000000..f6df1aa211
--- /dev/null
+++ b/systems/knuth/local/texware/tftopl.pool
@@ -0,0 +1,2 @@
+11texwarext.h
+*039917878
diff --git a/systems/knuth/local/web-sparc/Makefile b/systems/knuth/local/web-sparc/Makefile
new file mode 100644
index 0000000000..74811857a3
--- /dev/null
+++ b/systems/knuth/local/web-sparc/Makefile
@@ -0,0 +1,33 @@
+# Make executable files for this workstation, using the master TeX sources
+
+PFLAGS= -O -I.. -temp=.
+CFLAGS= -O -I..
+PC= time pc
+
+all: tangle weave
+
+%.p: %.ch
+ tangle ../../dist/web/$*.web $*.ch
+ mv ../../dist/web/$*.p .
+
+%.ch: ../../dist/web/%.web
+ touch $*.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+tangle: tangle.o tangext.o tangext.h
+ pc ${PFLAGS} -o tangle tangle.o tangext.o
+
+tangext.o: tangext.c ../h00vars.h
+
+weave: weave.o weavext.o tangext.h
+ pc ${PFLAGS} -o weave weave.o weavext.o
+
+weavext.o: weavext.c ../h00vars.h
+
+install: # doit make install PROG=tangle
+ mv ${PROG} /usr/local/bin/${PROG}
+
+clean: # we save tangle.p for emergency rebooting
+ rm -f weave.p *.o *~ ../../dist/web/*.pool
diff --git a/systems/knuth/local/web-sparc/tangext.c b/systems/knuth/local/web-sparc/tangext.c
new file mode 100644
index 0000000000..bcbe6e1ffc
--- /dev/null
+++ b/systems/knuth/local/web-sparc/tangext.c
@@ -0,0 +1,109 @@
+/* External procedures for tangle/weave */
+/* Written by: H. Trickey, 11/17/83 */
+/* Modified for 8-bit input by don, 8/31/89 */
+
+/* Note: these procedures aren't necessary; the default input_ln and
+ * flush_buffer routines in tangle/weave work fine on UNIX.
+ * However a very big time improvement is achieved by using these.
+ */
+
+#define BUF_SIZE 100 /* should agree with tangle.web */
+
+#include "h00vars.h" /* defines Pascal I/O structure */
+
+extern short buffer[]; /* 0..BUF_SIZE. Input goes here */
+extern short outbuf[]; /* 0..OUT_BUF_SIZE. Output from here */
+extern short xord[];
+extern char xchr[]; /* character translation arrays */
+extern char limit; /* index into buffer, defined in the range
+ 0..BUF_SIZE (and note that this is a
+ char rather than a short because BUF_SIZE
+ is 100) */
+
+/*
+ * lineread reads from the Pascal text file with iorec pointer filep
+ * into buffer[0], buffer[1],..., buffer[limit-1] (and
+ * setting "limit").
+ * Characters are read until a newline is found (which isn't put in the
+ * buffer) or until the next character would go into buffer[BUF_SIZE].
+ * And trailing blanks are to be ignored, so limit is really set to
+ * one past the last non-blank.
+ * The characters need to be translated, so really xord[c] is put into
+ * the buffer when c is read.
+ * If end-of-file is encountered, the funit field of *filep is set
+ * appropriately.
+ */
+lineread(filep)
+ struct iorec *filep;
+{
+ register int c;
+ register short *cs; /* pointer into buffer where next char goes */
+ register short *cnb; /* last non-blank character input */
+ register FILE *iop; /* stdio-style FILE pointer */
+ register int l; /* how many chars before buffer overflow */
+
+ iop = filep->fbuf;
+ cnb = cs = &buffer[0];
+ l = BUF_SIZE;
+
+ /* overflow when next char would go into buffer[BUF_SIZE] */
+ while ((--l >= 0) && ((c = getc(iop)) != EOF) && (c != '\n')) {
+ if ((*cs++ = xord[c]) != ' ')
+ cnb = cs;
+ }
+
+ if (c == EOF)
+ filep->funit |= EOFF; /* we hit end-of-file */
+
+ limit = (cnb - &buffer[0]);
+}
+
+/*
+ * linewrite writes to the Pascal text file with iorec pointer filep
+ * from outbuf[0], outbuf[1],..., outbuf[cnt-1].
+ * Translation is done, so that xchr[c] is output instead of c.
+ */
+linewrite(filep, cnt)
+ struct iorec *filep;
+ int cnt;
+{
+ register FILE *iop; /* stdio-style FILE pointer */
+ register short *cs; /* pointer to next character to output */
+ register int l; /* how many characters left to output */
+
+ iop = filep->fbuf;
+ cs = &outbuf[0];
+ l = cnt;
+
+ while (--l >= 0)
+ putc(xchr[*cs++], iop);
+}
+
+/*
+ * testeof(filep)
+ *
+ * Test whether or not the Pascal text file with iorec pointer filep
+ * has reached end-of-file (when the only I/O on it is done with
+ * lineread, above).
+ * We may have to read the next character and unget it to see if perhaps
+ * the end-of-file is next.
+ */
+bool
+testeof(filep)
+ register struct iorec *filep;
+{
+ register char c;
+ register FILE *iop; /* stdio-style FILE pointer */
+ if (filep->funit & EOFF)
+ return(TRUE);
+ else { /* check to see if next is EOF */
+ iop = filep->fbuf;
+ c = getc(iop);
+ if (c == EOF)
+ return(TRUE);
+ else {
+ ungetc(c,iop);
+ return(FALSE);
+ }
+ }
+}
diff --git a/systems/knuth/local/web-sparc/tangext.h b/systems/knuth/local/web-sparc/tangext.h
new file mode 100644
index 0000000000..e4a5fed066
--- /dev/null
+++ b/systems/knuth/local/web-sparc/tangext.h
@@ -0,0 +1,8 @@
+procedure exit(x : integer);
+ external;
+procedure lineread(var f:textfile);
+ external;
+procedure linewrite(var f:textfile; cnt:integer);
+ external;
+function testeof(var f:textfile):boolean;
+ external;
diff --git a/systems/knuth/local/web-sparc/tangle.ch b/systems/knuth/local/web-sparc/tangle.ch
new file mode 100644
index 0000000000..99cd9c0de1
--- /dev/null
+++ b/systems/knuth/local/web-sparc/tangle.ch
@@ -0,0 +1,655 @@
+% Change file for the TANGLE processor, for use on Berkeley UNIX systems.
+% This file was created by Howard Trickey and Pavel Curtis.
+% Subsequent small updates (to match Stanford releases) by
+% Richard Furuta and Pierre MacKay
+
+% History:
+% 10/9/82 (HT) Original version
+% 11/29 (HT) New version, with conversion to lowercase handled properly
+% Also, new control sequence:
+% @=...text...@> Put ...text... verbatim on a line
+% by itself in the Pascal output.
+% (argument must fit on one line)
+% This control sequence facilitates putting #include "gcons.h"
+% (for example) in files meant for the pc compiler.
+% Also, changed command line usage, so that the absence of a
+% change file implies no change file, rather than one with the
+% same name as the web file, with .ch at the end.
+% 1/15/83 (HT) Changed to work with version 1.2, which incorporates the
+% above change (though unbundling the output line breaking),
+% so mainly had to remove stuff.
+% 2/17 (HT) Fixed bug that caused 0-9 in identifiers to be converted to
+% Q-Y on output.
+% 3/18 (HT) Brought up to work with Version 1.5. Added -r command line
+% flag to cause a .rpl file to be written with all the lines
+% of the .web file that were replaced because of the .ch file
+% (useful for comparing with previous .rpl files, to see if a
+% change file will still work with a new version of a .web file)
+% Also, made it write a newline just before exit.
+% 4/12 (PC) Merged with Pavel's version, including adding a call to exit()
+% at the end depending upon the value of history.
+% 4/16 (PC) Brought up to date with version 1.5 released April, 1983.
+% 6/28 (HWT) Brought up to date with version 1.7 released June, 1983.
+% With new change file format, the -r option is now unnecessary.
+% 7/17 (HWT) Brought up to date with version 2.0 released July, 1983.
+% 11/17 (HWT) Made some I/O use external C procedures, for speedup.
+% 8/4/84 (RKF) To version 2.6
+% 9/3/85 (RKF) To version 2.7
+% 12/28/85(PAM) To version 2.8, and increase ww and zz so that METAFONT fits
+% 8/10/89 (don) To version 3, with `otherwise' fixed too
+% 8/31/89 (don) To version 4, with all possible characters enabled
+% 10/6/89 (don) Removed changes for "unambig_length"; I must meet higher goals.
+% Also fixed bugs related to uppercase
+% 10/16/89 don fixed bug when ".web" and ".ch" are explicitly stated
+% 9/6/90 (don) To version 4.1
+% 3/25/91 (don) To version 4.2
+% 9/19/91 (don) To version 4.3
+% 2/27/93 (don) To version 4.4
+
+% NOTE: The module numbers refer to the standard WEB manual (CS 980).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{TANGLE changes for Berkeley {\mc UNIX}}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is TANGLE, Version 4.4'
+@y
+@d banner=='This is TANGLE, Version 4.4 for SunOS'
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] add input and output, remove other files, add ref to scan_args,
+% [2] and #include external definition for exit(), etc.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+program TANGLE(@!web_file,@!change_file,@!Pascal_file,@!pool);
+label end_of_TANGLE; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@<Error handling procedures@>@/
+@y
+program TANGLE(@!input,@!output);
+label end_of_TANGLE; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\
+@=#include "tangext.h"@>
+@\@/
+@<Error handling procedures@>@/
+@<Declaration of |scan_args|@>@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] compiler options
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+@y
+@=(*$C-*)@> {no range check}
+@!debug @=(*$C+*)@>@+ gubed {but turn everything on when debugging}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Fix others:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {SunOS Pascal default cases}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8] keep long identifier names
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!max_id_length=12; {long identifiers are chopped to this length, which must
+ not exceed |line_length|}
+@y
+@!max_id_length=50; {long identifiers are chopped to this length, which must
+ not exceed |line_length|}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [17] enable maximum character set
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for i:=1 to @'37 do xchr[i]:=' ';
+for i:=@'200 to @'377 do xchr[i]:=' ';
+@y
+for i:=1 to @'37 do xchr[i]:=chr(i);
+for i:=@'200 to @'377 do xchr[i]:=chr(i);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] terminal output: use standard i/o
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@y
+@d term_out==output
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@z
+
+@x
+@<Globals...@>=
+@!term_out:text_file; {the terminal as an output file}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] init terminal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal. Here is one way to do this
+on the \PASCAL\ system that was used in \.{TANGLE}'s initial development:
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
+@y
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal.
+@^system dependencies@>
+
+@<Set init...@>=
+ {Nothing need be done on Berkeley UNIX}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [22] flush terminal buffer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush(term_out) {empty the terminal output buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] open input files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens the input files. Since these files were listed
+in the program header, we assume that the \PASCAL\ runtime system has
+already checked that suitable file names have been given; therefore no
+additional error checking needs to be done.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read |web_file| and |change_file|}
+begin reset(web_file); reset(change_file);
+end;
+@y
+@ The following code opens the input files.
+This happens after the initialize procedure has executed.
+That will have called the |scan_args| procedure to set up the global
+variables |web_name| and |change_name| to the appropriate file
+names.
+These globals, and the |scan_args| procedure will be defined at the end
+where they won't disturb the module numbering.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read |web_file| and |change_file|}
+begin
+reset(web_file,web_name); reset(change_file,change_name);
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [26] open output files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens |Pascal_file| and |pool|.
+Since these files were listed in the program header, we assume that the
+\PASCAL\ runtime system has checked that suitable external file names have
+been given.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(Pascal_file); rewrite(pool);
+@y
+@ The following code opens |Pascal_file| and |pool|.
+Use the |scan_args| procedure to fill the global file names,
+according to the names given on the command line.
+@^system dependencies@>
+
+@<Set init...@>=
+scan_args;
+rewrite(Pascal_file,Pascal_name); rewrite(pool,pool_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [28] faster input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+var final_limit:0..buf_size; {|limit| without trailing blanks}
+begin limit:=0; final_limit:=0;
+if eof(f) then input_ln:=false
+else begin while not eoln(f) do
+ begin buffer[limit]:=xord[f^]; get(f);
+ incr(limit);
+ if buffer[limit-1]<>" " then final_limit:=limit;
+ if limit=buf_size then
+ begin while not eoln(f) do get(f);
+ decr(limit); {keep |buffer[buf_size]| empty}
+ if final_limit>limit then final_limit:=limit;
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ end;
+ read_ln(f); limit:=final_limit; input_ln:=true;
+ end;
+end;
+@y
+With Berkeley {\mc UNIX} we call an external C procedure, |line_read|.
+That routine fills |buffer| from 0 onwards with the |xord|'ed values
+of the next line, setting |limit| appropriately (ignoring trailing
+blanks).
+It will stop if |limit=buf_size|, and the following will cause an error
+message.
+Note: for bootstrapping purposes it is all right to use the original form
+of |input_ln|; it will just run slower.
+
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+begin limit:=0;
+if test_eof(f) then input_ln:=false
+else begin line_read(f);
+ if limit=buf_size then
+ begin
+ decr(limit); {keep |buffer[buf_size]| empty}
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ input_ln:=true;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38] Data Structures: provide for larger |byte_mem| and |tok_mem|
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x Extra capacity:
+@d ww=2 {we multiply the byte capacity by approximately this amount}
+@d zz=3 {we multiply the token capacity by approximately this amount}
+@y
+@d ww=3 {we multiply the byte capacity by approximately this amount}
+@d zz=4 {we multiply the token capacity by approximately this amount}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [96] faster flush_buffer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d check_break==if out_ptr>line_length then flush_buffer
+
+@p procedure flush_buffer; {writes one line to output file}
+var k:0..out_buf_size; {index into |out_buf|}
+@!b:0..out_buf_size; {value of |break_ptr| upon entry}
+begin b:=break_ptr;
+if (semi_ptr<>0)and(out_ptr-semi_ptr<=line_length) then break_ptr:=semi_ptr;
+for k:=1 to break_ptr do write(Pascal_file,xchr[out_buf[k-1]]);
+@y
+For Berkeley {\mc UNIX} we use an external C procedure, |line_write|,
+to write the |xchr|'d version of characters to a file.
+This improves the speed significantly.
+
+@d check_break==if out_ptr>line_length then flush_buffer
+
+@p procedure flush_buffer; {writes one line to output file}
+var k:0..out_buf_size; {index into |out_buf|}
+@!b:0..out_buf_size; {value of |break_ptr| upon entry}
+begin b:=break_ptr;
+if (semi_ptr<>0)and(out_ptr-semi_ptr<=line_length) then break_ptr:=semi_ptr;
+line_write(Pascal_file,break_ptr);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [105] Accept DIV, div, MOD, and mod
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ (((out_contrib[1]="D")and(out_contrib[2]="I")and(out_contrib[3]="V")) or@|
+ ((out_contrib[1]="M")and(out_contrib[2]="O")and(out_contrib[3]="D")) ))or@|
+@^uppercase@>
+@y
+ (((out_contrib[1]="D")and(out_contrib[2]="I")and(out_contrib[3]="V")) or@|
+ ((out_contrib[1]="d")and(out_contrib[2]="i")and(out_contrib[3]="v")) or@|
+ ((out_contrib[1]="M")and(out_contrib[2]="O")and(out_contrib[3]="D")) or@|
+ ((out_contrib[1]="m")and(out_contrib[2]="o")and(out_contrib[3]="d")) ))or@|
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [110] lowercase ids
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@^uppercase@>
+ if ((out_buf[out_ptr-3]="D")and(out_buf[out_ptr-2]="I")and
+ (out_buf[out_ptr-1]="V"))or @/
+ ((out_buf[out_ptr-3]="M")and(out_buf[out_ptr-2]="O")and
+ (out_buf[out_ptr-1]="D")) then@/ goto bad_case
+@y
+ if ((out_buf[out_ptr-3]="D")and(out_buf[out_ptr-2]="I")and
+ (out_buf[out_ptr-1]="V"))or @/
+ ((out_buf[out_ptr-3]="d")and(out_buf[out_ptr-2]="i")and
+ (out_buf[out_ptr-1]="v"))or @/
+ ((out_buf[out_ptr-3]="M")and(out_buf[out_ptr-2]="O")and
+ (out_buf[out_ptr-1]="D"))or @/
+ ((out_buf[out_ptr-3]="m")and(out_buf[out_ptr-2]="o")and
+ (out_buf[out_ptr-1]="d")) then@/ goto bad_case
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [114] lowercase operators (`and', `or', etc.)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+and_sign: begin out_contrib[1]:="A"; out_contrib[2]:="N"; out_contrib[3]:="D";
+@^uppercase@>
+ send_out(ident,3);
+ end;
+not_sign: begin out_contrib[1]:="N"; out_contrib[2]:="O"; out_contrib[3]:="T";
+ send_out(ident,3);
+ end;
+set_element_sign: begin out_contrib[1]:="I"; out_contrib[2]:="N";
+ send_out(ident,2);
+ end;
+or_sign: begin out_contrib[1]:="O"; out_contrib[2]:="R"; send_out(ident,2);
+@y
+and_sign: begin out_contrib[1]:="a"; out_contrib[2]:="n"; out_contrib[3]:="d";
+ send_out(ident,3);
+ end;
+not_sign: begin out_contrib[1]:="n"; out_contrib[2]:="o"; out_contrib[3]:="t";
+ send_out(ident,3);
+ end;
+set_element_sign: begin out_contrib[1]:="i"; out_contrib[2]:="n";
+ send_out(ident,2);
+ end;
+or_sign: begin out_contrib[1]:="o"; out_contrib[2]:="r"; send_out(ident,2);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [116] Remove conversion to uppercase
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Single-character identifiers represent themselves, while longer ones
+appear in |byte_mem|. All must be converted to uppercase,
+with underlines removed. Extremely long identifiers must be chopped.
+
+(Some \PASCAL\ compilers work with lowercase letters instead of
+uppercase. If this module of \.{TANGLE} is changed, it's also necessary
+to change from uppercase to lowercase in the modules that are
+listed in the index under ``uppercase''.)
+@^system dependencies@>
+@^uppercase@>
+
+@d up_to(#)==#-24,#-23,#-22,#-21,#-20,#-19,#-18,#-17,#-16,#-15,#-14,
+ #-13,#-12,#-11,#-10,#-9,#-8,#-7,#-6,#-5,#-4,#-3,#-2,#-1,#
+
+@<Cases related to identifiers@>=
+"A",up_to("Z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
+ end;
+"a",up_to("z"): begin out_contrib[1]:=cur_char-@'40; send_out(ident,1);
+ end;
+identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
+ while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
+ begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
+ if out_contrib[k]>="a" then out_contrib[k]:=out_contrib[k]-@'40
+ else if out_contrib[k]="_" then decr(k);
+ end;
+ send_out(ident,k);
+ end;
+@y
+@ Single-character identifiers represent themselves, while longer ones
+appear in |byte_mem|. All must be converted to lowercase,
+with underlines removed. Extremely long identifiers must be chopped.
+@^system dependencies@>
+
+@d up_to(#)==#-24,#-23,#-22,#-21,#-20,#-19,#-18,#-17,#-16,#-15,#-14,
+ #-13,#-12,#-11,#-10,#-9,#-8,#-7,#-6,#-5,#-4,#-3,#-2,#-1,#
+
+@<Cases related to identifiers@>=
+"A",up_to("Z"),
+"a",up_to("z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
+ end;
+identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
+ while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
+ begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
+ if out_contrib[k]="_" then decr(k);
+ end;
+ send_out(ident,k);
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [179] make term_in = input
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+any error stop will set |debug_cycle| to zero.
+@y
+any error stop will set |debug_cycle| to zero.
+
+@d term_in==input
+@z
+
+@x
+@!term_in:text_file; {the user's terminal as an input file}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [180] remove term_in reset
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reset(term_in,'TTY:','/I'); {open |term_in| as the terminal, don't do a |get|}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [182] write newline just before exit; use value of |history|
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+We have defined plenty of procedures, and it is time to put the last
+pieces of the puzzle in place. Here is where \.{TANGLE} starts, and where
+it ends.
+@^system dependencies@>
+@y
+We have defined plenty of procedures, and it is time to put the last
+pieces of the puzzle in place. Here is where \.{TANGLE} starts, and where
+it ends.
+@^system dependencies@>
+
+@d UNIXexit==e@&x@&i@&t
+@z
+
+@x
+@<Print the job |history|@>;
+@y
+@<Print the job |history|@>;
+new_line;
+if (history <> spotless) and (history <> harmless_message) then
+ UNIXexit(1)
+else
+ UNIXexit(0);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [188] system dependent changes--the |scan_args| procedure.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This module should be replaced, if necessary, by changes to the program
+that are necessary to make \.{TANGLE} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous modules preserve the module numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new modules, can be inserted here; then only the index
+itself will get a new module number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+@^system dependencies@>
+
+@ The user calls \.{TANGLE} with arguments on the command line.
+These are either file names or flags (beginning with '-').
+The following globals are for communicating the user's desires to the rest
+of the program. The various |file_name| variables contain strings with
+the full names of those files, as UNIX knows them.
+
+There are no flags that affect \.{TANGLE} at the moment.
+
+@d max_file_name_length==60
+
+@<Globals...@>=
+@!web_name,@!change_name,@!Pascal_name,@!pool_name:
+ array[1..max_file_name_length] of char;
+
+@ The |scan_args| procedure looks at the command line arguments and sets
+the |file_name| variables accordingly.
+At least one file name must be present: the \.{WEB} file. It may have
+an extension, or it may omit it to get |'.web'| added.
+The \PASCAL\ output file name is formed by replacing the \.{WEB} file
+name extension by |'.p'|.
+Similarly, the pool file name is formed using a |'.pool'| extension.
+
+If there is another file name present among the arguments, it is the
+change file, again either with an extension or without one to get |'.ch'|
+An omitted change file argument means that |'/dev/null'| should be used,
+when no changes are desired.
+
+@<Declaration of |scan_args|@>=
+procedure scan_args;
+var dot_pos,i,a: integer; {indices}
+c: char;
+@!fname: array[1..max_file_name_length-5] of char; {temporary argument holder}
+@!found_web,@!found_change: boolean; {|true| when those file names have
+ been seen}
+begin
+found_web:=false;
+found_change:=false;
+for a:=1 to argc-1 do begin
+ argv(a,fname); {put argument number |a| into |fname|}
+ if fname[1]<>'-' then begin
+ if not found_web then
+ @<Get |web_name|, |Pascal_name|, and
+ | pool_name| variables from |fname|@>
+ else if not found_change then
+ @<Get |change_name| from |fname|@>
+ else @<Print usage error message and quit@>;
+ end
+ else @<Handle flag argument in |fname|@>;
+ end;
+if not found_web then @<Print usage error message and quit@>;
+if not found_change then @<Set up null change file@>;
+end;
+
+@ Use all of |fname| for the |web_name| if there is a |'.'| in it,
+otherwise add |'.web'|.
+The other file names come from adding things after the dot.
+The |argv| procedure will not put more than |max_file_name_length-5|
+characters into |fname|, and this leaves enough room in the |file_name|
+variables to add the extensions.
+
+The end of a file name is marked with a |' '|, the convention assumed by
+the |reset| and |rewrite| procedures.
+
+@<Get |web_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=max_file_name_length-5) do begin
+ web_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ web_name[dot_pos]:='.';
+ web_name[dot_pos+1]:='w';
+ web_name[dot_pos+2]:='e';
+ web_name[dot_pos+3]:='b';
+ web_name[dot_pos+4]:=' ';
+ end
+else web_name[i]:=' ';
+for i:=1 to dot_pos do begin
+ c:=web_name[i];
+ Pascal_name[i]:=c;
+ pool_name[i]:=c;
+ end;
+Pascal_name[dot_pos+1]:='p';
+Pascal_name[dot_pos+2]:=' ';
+pool_name[dot_pos+1]:='p';
+pool_name[dot_pos+2]:='o';
+pool_name[dot_pos+3]:='o';
+pool_name[dot_pos+4]:='l';
+pool_name[dot_pos+5]:=' ';
+found_web:=true;
+end
+
+@ @<Get |change_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=max_file_name_length-5)
+do begin
+ change_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ change_name[dot_pos]:='.';
+ change_name[dot_pos+1]:='c';
+ change_name[dot_pos+2]:='h';
+ change_name[dot_pos+3]:=' ';
+ end
+else change_name[i]:=' ';
+found_change:=true;
+end
+
+@ @<Set up null...@>=
+begin
+ change_name[1]:='/';
+ change_name[2]:='d';
+ change_name[3]:='e';
+ change_name[4]:='v';
+ change_name[5]:='/';
+ change_name[6]:='n';
+ change_name[7]:='u';
+ change_name[8]:='l';
+ change_name[9]:='l';
+ change_name[10]:=' ';
+end
+
+@ There are no flags currently used by \.{TANGLE}, but this module can be
+used as a hook to introduce flags.
+
+@<Handle flag...@>=
+begin
+ @<Print usage error message and quit@>;
+end
+
+@ @<Print usage error message and quit@>=
+begin
+print_nl('! Usage: webfile[.web] [changefile[.ch]]');
+error;
+jump_out;
+end
+
+@z
diff --git a/systems/knuth/local/web-sparc/tangle.p b/systems/knuth/local/web-sparc/tangle.p
new file mode 100644
index 0000000000..30fe333069
--- /dev/null
+++ b/systems/knuth/local/web-sparc/tangle.p
@@ -0,0 +1,814 @@
+{2:}{4:}(*$C-*){(*$C+*)}{:4}program TANGLE(input,output);label 9999;
+const{8:}bufsize=100;maxbytes=45000;maxtoks=50000;maxnames=4000;
+maxtexts=2000;hashsize=353;longestname=400;linelength=72;outbufsize=144;
+stacksize=50;maxidlength=50;unambiglength=7;{:8}type{11:}
+ASCIIcode=0..255;{:11}{12:}textfile=packed file of char;{:12}{37:}
+eightbits=0..255;sixteenbits=0..65535;{:37}{39:}namepointer=0..maxnames;
+{:39}{43:}textpointer=0..maxtexts;{:43}{78:}
+outputstate=record endfield:sixteenbits;bytefield:sixteenbits;
+namefield:namepointer;replfield:textpointer;modfield:0..12287;end;{:78}
+var{9:}history:0..3;{:9}{13:}xord:array[char]of ASCIIcode;
+xchr:array[ASCIIcode]of char;{:13}{23:}webfile:textfile;
+changefile:textfile;{:23}{25:}Pascalfile:textfile;pool:textfile;{:25}
+{27:}buffer:array[0..bufsize]of ASCIIcode;{:27}{29:}phaseone:boolean;
+{:29}{38:}bytemem:packed array[0..2,0..maxbytes]of ASCIIcode;
+tokmem:packed array[0..3,0..maxtoks]of eightbits;
+bytestart:array[0..maxnames]of sixteenbits;
+tokstart:array[0..maxtexts]of sixteenbits;
+link:array[0..maxnames]of sixteenbits;
+ilk:array[0..maxnames]of sixteenbits;
+equiv:array[0..maxnames]of sixteenbits;
+textlink:array[0..maxtexts]of sixteenbits;{:38}{40:}nameptr:namepointer;
+stringptr:namepointer;byteptr:array[0..2]of 0..maxbytes;
+poolchecksum:integer;{:40}{44:}textptr:textpointer;
+tokptr:array[0..3]of 0..maxtoks;z:0..3;
+{maxtokptr:array[0..3]of 0..maxtoks;}{:44}{50:}idfirst:0..bufsize;
+idloc:0..bufsize;doublechars:0..bufsize;
+hash,chophash:array[0..hashsize]of sixteenbits;
+choppedid:array[0..unambiglength]of ASCIIcode;{:50}{65:}
+modtext:array[0..longestname]of ASCIIcode;{:65}{70:}
+lastunnamed:textpointer;{:70}{79:}curstate:outputstate;
+stack:array[1..stacksize]of outputstate;stackptr:0..stacksize;{:79}{80:}
+zo:0..3;{:80}{82:}bracelevel:eightbits;{:82}{86:}curval:integer;{:86}
+{94:}outbuf:array[0..outbufsize]of ASCIIcode;outptr:0..outbufsize;
+breakptr:0..outbufsize;semiptr:0..outbufsize;{:94}{95:}
+outstate:eightbits;outval,outapp:integer;outsign:ASCIIcode;
+lastsign:-1..+1;{:95}{100:}outcontrib:array[1..linelength]of ASCIIcode;
+{:100}{124:}ii:integer;line:integer;otherline:integer;templine:integer;
+limit:0..bufsize;loc:0..bufsize;inputhasended:boolean;changing:boolean;
+{:124}{126:}changebuffer:array[0..bufsize]of ASCIIcode;
+changelimit:0..bufsize;{:126}{143:}curmodule:namepointer;
+scanninghex:boolean;{:143}{156:}nextcontrol:eightbits;{:156}{164:}
+currepltext:textpointer;{:164}{171:}modulecount:0..12287;{:171}{179:}
+{troubleshooting:boolean;ddt:integer;dd:integer;debugcycle:integer;
+debugskipped:integer;}{:179}{185:}{wo:0..2;}{:185}{189:}
+webname,changename,Pascalname,poolname:array[1..60]of char;{:189}
+#include "tangext.h"
+{30:}{procedure debughelp;forward;}{:30}{31:}procedure error;
+var j:0..outbufsize;k,l:0..bufsize;begin if phaseone then{32:}
+begin if changing then write(output,'. (change file ')else write(output,
+'. (');writeln(output,'l.',line:1,')');
+if loc>=limit then l:=limit else l:=loc;
+for k:=1 to l do if buffer[k-1]=9 then write(output,' ')else write(
+output,xchr[buffer[k-1]]);writeln(output);
+for k:=1 to l do write(output,' ');
+for k:=l+1 to limit do write(output,xchr[buffer[k-1]]);
+write(output,' ');end{:32}else{33:}
+begin writeln(output,'. (l.',line:1,')');
+for j:=1 to outptr do write(output,xchr[outbuf[j-1]]);
+write(output,'... ');end{:33};flush(output);history:=2;
+{debugskipped:=debugcycle;debughelp;}end;{:31}{34:}procedure jumpout;
+begin goto 9999;end;{:34}{190:}procedure scanargs;
+var dotpos,i,a:integer;c:char;fname:array[1..55]of char;
+foundweb,foundchange:boolean;begin foundweb:=false;foundchange:=false;
+for a:=1 to argc-1 do begin argv(a,fname);
+if fname[1]<>'-'then begin if not foundweb then{191:}begin dotpos:=-1;
+i:=1;while(fname[i]<>' ')and(i<=55)do begin webname[i]:=fname[i];
+if fname[i]='.'then dotpos:=i;i:=i+1;end;
+if dotpos=-1 then begin dotpos:=i;webname[dotpos]:='.';
+webname[dotpos+1]:='w';webname[dotpos+2]:='e';webname[dotpos+3]:='b';
+webname[dotpos+4]:=' ';end else webname[i]:=' ';
+for i:=1 to dotpos do begin c:=webname[i];Pascalname[i]:=c;
+poolname[i]:=c;end;Pascalname[dotpos+1]:='p';Pascalname[dotpos+2]:=' ';
+poolname[dotpos+1]:='p';poolname[dotpos+2]:='o';poolname[dotpos+3]:='o';
+poolname[dotpos+4]:='l';poolname[dotpos+5]:=' ';foundweb:=true;end{:191}
+else if not foundchange then{192:}begin dotpos:=-1;i:=1;
+while(fname[i]<>' ')and(i<=55)do begin changename[i]:=fname[i];
+if fname[i]='.'then dotpos:=i;i:=i+1;end;
+if dotpos=-1 then begin dotpos:=i;changename[dotpos]:='.';
+changename[dotpos+1]:='c';changename[dotpos+2]:='h';
+changename[dotpos+3]:=' ';end else changename[i]:=' ';foundchange:=true;
+end{:192}else{195:}begin begin writeln(output);
+write(output,'! Usage: webfile[.web] [changefile[.ch]]');end;error;
+jumpout;end{:195};end else{194:}begin{195:}begin begin writeln(output);
+write(output,'! Usage: webfile[.web] [changefile[.ch]]');end;error;
+jumpout;end{:195};end{:194};end;if not foundweb then{195:}
+begin begin writeln(output);
+write(output,'! Usage: webfile[.web] [changefile[.ch]]');end;error;
+jumpout;end{:195};if not foundchange then{193:}begin changename[1]:='/';
+changename[2]:='d';changename[3]:='e';changename[4]:='v';
+changename[5]:='/';changename[6]:='n';changename[7]:='u';
+changename[8]:='l';changename[9]:='l';changename[10]:=' ';end{:193};end;
+{:190}procedure initialize;var{16:}i:0..255;{:16}{41:}wi:0..2;{:41}{45:}
+zi:0..3;{:45}{51:}h:0..hashsize;{:51}begin{10:}history:=0;{:10}{14:}
+xchr[32]:=' ';xchr[33]:='!';xchr[34]:='"';xchr[35]:='#';xchr[36]:='$';
+xchr[37]:='%';xchr[38]:='&';xchr[39]:='''';xchr[40]:='(';xchr[41]:=')';
+xchr[42]:='*';xchr[43]:='+';xchr[44]:=',';xchr[45]:='-';xchr[46]:='.';
+xchr[47]:='/';xchr[48]:='0';xchr[49]:='1';xchr[50]:='2';xchr[51]:='3';
+xchr[52]:='4';xchr[53]:='5';xchr[54]:='6';xchr[55]:='7';xchr[56]:='8';
+xchr[57]:='9';xchr[58]:=':';xchr[59]:=';';xchr[60]:='<';xchr[61]:='=';
+xchr[62]:='>';xchr[63]:='?';xchr[64]:='@';xchr[65]:='A';xchr[66]:='B';
+xchr[67]:='C';xchr[68]:='D';xchr[69]:='E';xchr[70]:='F';xchr[71]:='G';
+xchr[72]:='H';xchr[73]:='I';xchr[74]:='J';xchr[75]:='K';xchr[76]:='L';
+xchr[77]:='M';xchr[78]:='N';xchr[79]:='O';xchr[80]:='P';xchr[81]:='Q';
+xchr[82]:='R';xchr[83]:='S';xchr[84]:='T';xchr[85]:='U';xchr[86]:='V';
+xchr[87]:='W';xchr[88]:='X';xchr[89]:='Y';xchr[90]:='Z';xchr[91]:='[';
+xchr[92]:='\';xchr[93]:=']';xchr[94]:='^';xchr[95]:='_';xchr[96]:='`';
+xchr[97]:='a';xchr[98]:='b';xchr[99]:='c';xchr[100]:='d';xchr[101]:='e';
+xchr[102]:='f';xchr[103]:='g';xchr[104]:='h';xchr[105]:='i';
+xchr[106]:='j';xchr[107]:='k';xchr[108]:='l';xchr[109]:='m';
+xchr[110]:='n';xchr[111]:='o';xchr[112]:='p';xchr[113]:='q';
+xchr[114]:='r';xchr[115]:='s';xchr[116]:='t';xchr[117]:='u';
+xchr[118]:='v';xchr[119]:='w';xchr[120]:='x';xchr[121]:='y';
+xchr[122]:='z';xchr[123]:='{';xchr[124]:='|';xchr[125]:='}';
+xchr[126]:='~';xchr[0]:=' ';xchr[127]:=' ';{:14}{17:}
+for i:=1 to 31 do xchr[i]:=chr(i);for i:=128 to 255 do xchr[i]:=chr(i);
+{:17}{18:}for i:=0 to 255 do xord[chr(i)]:=32;
+for i:=1 to 255 do xord[xchr[i]]:=i;xord[' ']:=32;{:18}{21:}{:21}{26:}
+scanargs;rewrite(Pascalfile,Pascalname);rewrite(pool,poolname);{:26}
+{42:}for wi:=0 to 2 do begin bytestart[wi]:=0;byteptr[wi]:=0;end;
+bytestart[3]:=0;nameptr:=1;stringptr:=256;poolchecksum:=271828;{:42}
+{46:}for zi:=0 to 3 do begin tokstart[zi]:=0;tokptr[zi]:=0;end;
+tokstart[4]:=0;textptr:=1;z:=1 mod 4;{:46}{48:}ilk[0]:=0;equiv[0]:=0;
+{:48}{52:}for h:=0 to hashsize-1 do begin hash[h]:=0;chophash[h]:=0;end;
+{:52}{71:}lastunnamed:=0;textlink[0]:=0;{:71}{144:}scanninghex:=false;
+{:144}{152:}modtext[0]:=32;{:152}{180:}{troubleshooting:=true;
+debugcycle:=1;debugskipped:=0;troubleshooting:=false;debugcycle:=99999;}
+{:180}end;{:2}{24:}procedure openinput;begin reset(webfile,webname);
+reset(changefile,changename);end;{:24}{28:}
+function inputln(var f:textfile):boolean;begin limit:=0;
+if testeof(f)then inputln:=false else begin lineread(f);
+if limit=bufsize then begin limit:=limit-1;begin writeln(output);
+write(output,'! Input line too long');end;loc:=0;error;end;
+inputln:=true;end;end;{:28}{49:}procedure printid(p:namepointer);
+var k:0..maxbytes;w:0..2;
+begin if p>=nameptr then write(output,'IMPOSSIBLE')else begin w:=p mod 3
+;
+for k:=bytestart[p]to bytestart[p+3]-1 do write(output,xchr[bytemem[w,k]
+]);end;end;{:49}{53:}function idlookup(t:eightbits):namepointer;
+label 31,32;var c:eightbits;i:0..bufsize;h:0..hashsize;k:0..maxbytes;
+w:0..2;l:0..bufsize;p,q:namepointer;s:0..unambiglength;
+begin l:=idloc-idfirst;{54:}h:=buffer[idfirst];i:=idfirst+1;
+while i<idloc do begin h:=(h+h+buffer[i])mod hashsize;i:=i+1;end{:54};
+{55:}p:=hash[h];
+while p<>0 do begin if bytestart[p+3]-bytestart[p]=l then{56:}
+begin i:=idfirst;k:=bytestart[p];w:=p mod 3;
+while(i<idloc)and(buffer[i]=bytemem[w,k])do begin i:=i+1;k:=k+1;end;
+if i=idloc then goto 31;end{:56};p:=link[p];end;p:=nameptr;
+link[p]:=hash[h];hash[h]:=p;31:{:55};if(p=nameptr)or(t<>0)then{57:}
+begin if((p<>nameptr)and(t<>0)and(ilk[p]=0))or((p=nameptr)and(t=0)and(
+buffer[idfirst]<>34))then{58:}begin i:=idfirst;s:=0;h:=0;
+while(i<idloc)and(s<unambiglength)do begin if buffer[i]<>95 then begin
+if buffer[i]>=97 then choppedid[s]:=buffer[i]-32 else choppedid[s]:=
+buffer[i];h:=(h+h+choppedid[s])mod hashsize;s:=s+1;end;i:=i+1;end;
+choppedid[s]:=0;end{:58};if p<>nameptr then{59:}
+begin if ilk[p]=0 then begin if t=1 then begin writeln(output);
+write(output,'! This identifier has already appeared');error;end;{60:}
+q:=chophash[h];
+if q=p then chophash[h]:=equiv[p]else begin while equiv[q]<>p do q:=
+equiv[q];equiv[q]:=equiv[p];end{:60};end else begin writeln(output);
+write(output,'! This identifier was defined before');error;end;
+ilk[p]:=t;end{:59}else{61:}
+begin if(t=0)and(buffer[idfirst]<>34)then{62:}begin q:=chophash[h];
+while q<>0 do begin{63:}begin k:=bytestart[q];s:=0;w:=q mod 3;
+while(k<bytestart[q+3])and(s<unambiglength)do begin c:=bytemem[w,k];
+if c<>95 then begin if c>=97 then c:=c-32;
+if choppedid[s]<>c then goto 32;s:=s+1;end;k:=k+1;end;
+if(k=bytestart[q+3])and(choppedid[s]<>0)then goto 32;
+begin writeln(output);write(output,'! Identifier conflict with ');end;
+for k:=bytestart[q]to bytestart[q+3]-1 do write(output,xchr[bytemem[w,k]
+]);error;q:=0;32:end{:63};q:=equiv[q];end;equiv[p]:=chophash[h];
+chophash[h]:=p;end{:62};w:=nameptr mod 3;k:=byteptr[w];
+if k+l>maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;i:=idfirst;while i<idloc do begin bytemem[w,k]:=buffer[i];
+k:=k+1;i:=i+1;end;byteptr[w]:=k;bytestart[nameptr+3]:=k;
+nameptr:=nameptr+1;if buffer[idfirst]<>34 then ilk[p]:=t else{64:}
+begin ilk[p]:=1;
+if l-doublechars=2 then equiv[p]:=buffer[idfirst+1]+32768 else begin
+equiv[p]:=stringptr+32768;l:=l-doublechars-1;
+if l>99 then begin writeln(output);
+write(output,'! Preprocessed string is too long');error;end;
+stringptr:=stringptr+1;write(pool,xchr[48+l div 10],xchr[48+l mod 10]);
+poolchecksum:=poolchecksum+poolchecksum+l;
+while poolchecksum>536870839 do poolchecksum:=poolchecksum-536870839;
+i:=idfirst+1;while i<idloc do begin write(pool,xchr[buffer[i]]);
+poolchecksum:=poolchecksum+poolchecksum+buffer[i];
+while poolchecksum>536870839 do poolchecksum:=poolchecksum-536870839;
+if(buffer[i]=34)or(buffer[i]=64)then i:=i+2 else i:=i+1;end;
+writeln(pool);end;end{:64};end{:61};end{:57};idlookup:=p;end;{:53}{66:}
+function modlookup(l:sixteenbits):namepointer;label 31;var c:0..4;
+j:0..longestname;k:0..maxbytes;w:0..2;p:namepointer;q:namepointer;
+begin c:=2;q:=0;p:=ilk[0];while p<>0 do begin{68:}begin k:=bytestart[p];
+w:=p mod 3;c:=1;j:=1;
+while(k<bytestart[p+3])and(j<=l)and(modtext[j]=bytemem[w,k])do begin k:=
+k+1;j:=j+1;end;
+if k=bytestart[p+3]then if j>l then c:=1 else c:=4 else if j>l then c:=3
+else if modtext[j]<bytemem[w,k]then c:=0 else c:=2;end{:68};q:=p;
+if c=0 then p:=link[q]else if c=2 then p:=ilk[q]else goto 31;end;{67:}
+w:=nameptr mod 3;k:=byteptr[w];
+if k+l>maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;p:=nameptr;if c=0 then link[q]:=p else ilk[q]:=p;link[p]:=0;
+ilk[p]:=0;c:=1;equiv[p]:=0;
+for j:=1 to l do bytemem[w,k+j-1]:=modtext[j];byteptr[w]:=k+l;
+bytestart[nameptr+3]:=k+l;nameptr:=nameptr+1;{:67};
+31:if c<>1 then begin begin writeln(output);
+write(output,'! Incompatible section names');error;end;p:=0;end;
+modlookup:=p;end;{:66}{69:}
+function prefixlookup(l:sixteenbits):namepointer;var c:0..4;
+count:0..maxnames;j:0..longestname;k:0..maxbytes;w:0..2;p:namepointer;
+q:namepointer;r:namepointer;begin q:=0;p:=ilk[0];count:=0;r:=0;
+while p<>0 do begin{68:}begin k:=bytestart[p];w:=p mod 3;c:=1;j:=1;
+while(k<bytestart[p+3])and(j<=l)and(modtext[j]=bytemem[w,k])do begin k:=
+k+1;j:=j+1;end;
+if k=bytestart[p+3]then if j>l then c:=1 else c:=4 else if j>l then c:=3
+else if modtext[j]<bytemem[w,k]then c:=0 else c:=2;end{:68};
+if c=0 then p:=link[p]else if c=2 then p:=ilk[p]else begin r:=p;
+count:=count+1;q:=ilk[p];p:=link[p];end;if p=0 then begin p:=q;q:=0;end;
+end;if count<>1 then if count=0 then begin writeln(output);
+write(output,'! Name does not match');error;
+end else begin writeln(output);write(output,'! Ambiguous prefix');error;
+end;prefixlookup:=r;end;{:69}{73:}
+procedure storetwobytes(x:sixteenbits);
+begin if tokptr[z]+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=x div 256;
+tokmem[z,tokptr[z]+1]:=x mod 256;tokptr[z]:=tokptr[z]+2;end;{:73}{74:}
+{procedure printrepl(p:textpointer);var k:0..maxtoks;a:sixteenbits;
+zp:0..3;
+begin if p>=textptr then write(output,'BAD')else begin k:=tokstart[p];
+zp:=p mod 4;while k<tokstart[p+4]do begin a:=tokmem[zp,k];
+if a>=128 then[75:]begin k:=k+1;
+if a<168 then begin a:=(a-128)*256+tokmem[zp,k];printid(a);
+if bytemem[a mod 3,bytestart[a]]=34 then write(output,'"')else write(
+output,' ');end else if a<208 then begin write(output,'@<');
+printid((a-168)*256+tokmem[zp,k]);write(output,'@>');
+end else begin a:=(a-208)*256+tokmem[zp,k];
+write(output,'@',xchr[123],a:1,'@',xchr[125]);end;
+end[:75]else[76:]case a of 9:write(output,'@',xchr[123]);
+10:write(output,'@',xchr[125]);12:write(output,'@''');
+13:write(output,'@"');125:write(output,'@$');0:write(output,'#');
+64:write(output,'@@');2:write(output,'@=');3:write(output,'@\');
+otherwise write(output,xchr[a])end[:76];k:=k+1;end;end;end;}{:74}{84:}
+procedure pushlevel(p:namepointer);
+begin if stackptr=stacksize then begin writeln(output);
+write(output,'! Sorry, ','stack',' capacity exceeded');error;history:=3;
+jumpout;end else begin stack[stackptr]:=curstate;stackptr:=stackptr+1;
+curstate.namefield:=p;curstate.replfield:=equiv[p];
+zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];curstate.modfield:=0;
+end;end;{:84}{85:}procedure poplevel;label 10;
+begin if textlink[curstate.replfield]=0 then begin if ilk[curstate.
+namefield]=3 then{91:}begin nameptr:=nameptr-1;textptr:=textptr-1;
+z:=textptr mod 4;{if tokptr[z]>maxtokptr[z]then maxtokptr[z]:=tokptr[z];
+}tokptr[z]:=tokstart[textptr];
+{byteptr[nameptr mod 3]:=byteptr[nameptr mod 3]-1;}end{:91};
+end else if textlink[curstate.replfield]<maxtexts then begin curstate.
+replfield:=textlink[curstate.replfield];zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];goto 10;end;
+stackptr:=stackptr-1;if stackptr>0 then begin curstate:=stack[stackptr];
+zo:=curstate.replfield mod 4;end;10:end;{:85}{87:}
+function getoutput:sixteenbits;label 20,30,31;var a:sixteenbits;
+b:eightbits;bal:sixteenbits;k:0..maxbytes;w:0..2;
+begin 20:if stackptr=0 then begin a:=0;goto 31;end;
+if curstate.bytefield=curstate.endfield then begin curval:=-curstate.
+modfield;poplevel;if curval=0 then goto 20;a:=129;goto 31;end;
+a:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;if a<128 then if a=0 then{92:}
+begin pushlevel(nameptr-1);goto 20;end{:92}else goto 31;
+a:=(a-128)*256+tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;if a<10240 then{89:}
+begin case ilk[a]of 0:begin curval:=a;a:=130;end;
+1:begin curval:=equiv[a]-32768;a:=128;end;2:begin pushlevel(a);goto 20;
+end;3:begin{90:}
+while(curstate.bytefield=curstate.endfield)and(stackptr>0)do poplevel;
+if(stackptr=0)or(tokmem[zo,curstate.bytefield]<>40)then begin begin
+writeln(output);write(output,'! No parameter given for ');end;
+printid(a);error;goto 20;end;{93:}bal:=1;
+curstate.bytefield:=curstate.bytefield+1;
+while true do begin b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;
+if b=0 then storetwobytes(nameptr+32767)else begin if b>=128 then begin
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;
+b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;
+end else case b of 40:bal:=bal+1;41:begin bal:=bal-1;
+if bal=0 then goto 30;end;
+39:repeat begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;
+b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;until b=39;otherwise end;
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;end;end;
+30:{:93};equiv[nameptr]:=textptr;ilk[nameptr]:=2;w:=nameptr mod 3;
+k:=byteptr[w];{if k=maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;bytemem[w,k]:=35;k:=k+1;byteptr[w]:=k;}
+if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;bytestart[nameptr+3]:=k;nameptr:=nameptr+1;
+if textptr>maxtexts-4 then begin writeln(output);
+write(output,'! Sorry, ','text',' capacity exceeded');error;history:=3;
+jumpout;end;textlink[textptr]:=0;tokstart[textptr+4]:=tokptr[z];
+textptr:=textptr+1;z:=textptr mod 4{:90};pushlevel(a);goto 20;end;
+otherwise begin writeln(output);
+write(output,'! This can''t happen (','output',')');error;history:=3;
+jumpout;end end;goto 31;end{:89};if a<20480 then{88:}begin a:=a-10240;
+if equiv[a]<>0 then pushlevel(a)else if a<>0 then begin begin writeln(
+output);write(output,'! Not present: <');end;printid(a);
+write(output,'>');error;end;goto 20;end{:88};curval:=a-20480;a:=129;
+curstate.modfield:=curval;31:{if troubleshooting then debughelp;}
+getoutput:=a;end;{:87}{97:}procedure flushbuffer;var k:0..outbufsize;
+b:0..outbufsize;begin b:=breakptr;
+if(semiptr<>0)and(outptr-semiptr<=linelength)then breakptr:=semiptr;
+linewrite(Pascalfile,breakptr);writeln(Pascalfile);line:=line+1;
+if line mod 100=0 then begin write(output,'.');
+if line mod 500=0 then write(output,line:1);flush(output);end;
+if breakptr<outptr then begin if outbuf[breakptr]=32 then begin breakptr
+:=breakptr+1;if breakptr>b then b:=breakptr;end;
+for k:=breakptr to outptr-1 do outbuf[k-breakptr]:=outbuf[k];end;
+outptr:=outptr-breakptr;breakptr:=b-breakptr;semiptr:=0;
+if outptr>linelength then begin begin writeln(output);
+write(output,'! Long line must be truncated');error;end;
+outptr:=linelength;end;end;{:97}{99:}procedure appval(v:integer);
+var k:0..outbufsize;begin k:=outbufsize;repeat outbuf[k]:=v mod 10;
+v:=v div 10;k:=k-1;until v=0;repeat k:=k+1;
+begin outbuf[outptr]:=outbuf[k]+48;outptr:=outptr+1;end;
+until k=outbufsize;end;{:99}{101:}procedure sendout(t:eightbits;
+v:sixteenbits);label 20;var k:0..linelength;begin{102:}
+20:case outstate of 1:if t<>3 then begin breakptr:=outptr;
+if t=2 then begin outbuf[outptr]:=32;outptr:=outptr+1;end;end;
+2:begin begin outbuf[outptr]:=44-outapp;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;breakptr:=outptr;end;
+3,4:begin{103:}
+if(outval<0)or((outval=0)and(lastsign<0))then begin outbuf[outptr]:=45;
+outptr:=outptr+1;
+end else if outsign>0 then begin outbuf[outptr]:=outsign;
+outptr:=outptr+1;end;appval(abs(outval));
+if outptr>linelength then flushbuffer;{:103};outstate:=outstate-2;
+goto 20;end;5:{104:}begin if(t=3)or({105:}
+((t=2)and(v=3)and(((outcontrib[1]=68)and(outcontrib[2]=73)and(outcontrib
+[3]=86))or((outcontrib[1]=100)and(outcontrib[2]=105)and(outcontrib[3]=
+118))or((outcontrib[1]=77)and(outcontrib[2]=79)and(outcontrib[3]=68))or(
+(outcontrib[1]=109)and(outcontrib[2]=111)and(outcontrib[3]=100))))or((t=
+0)and((v=42)or(v=47))){:105})then begin{103:}
+if(outval<0)or((outval=0)and(lastsign<0))then begin outbuf[outptr]:=45;
+outptr:=outptr+1;
+end else if outsign>0 then begin outbuf[outptr]:=outsign;
+outptr:=outptr+1;end;appval(abs(outval));
+if outptr>linelength then flushbuffer;{:103};outsign:=43;outval:=outapp;
+end else outval:=outval+outapp;outstate:=3;goto 20;end{:104};
+0:if t<>3 then breakptr:=outptr;otherwise end{:102};
+if t<>0 then for k:=1 to v do begin outbuf[outptr]:=outcontrib[k];
+outptr:=outptr+1;end else begin outbuf[outptr]:=v;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;
+if(t=0)and((v=59)or(v=125))then begin semiptr:=outptr;breakptr:=outptr;
+end;if t>=2 then outstate:=1 else outstate:=0 end;{:101}{106:}
+procedure sendsign(v:integer);
+begin case outstate of 2,4:outapp:=outapp*v;3:begin outapp:=v;
+outstate:=4;end;5:begin outval:=outval+outapp;outapp:=v;outstate:=4;end;
+otherwise begin breakptr:=outptr;outapp:=v;outstate:=2;end end;
+lastsign:=outapp;end;{:106}{107:}procedure sendval(v:integer);
+label 666,10;begin case outstate of 1:begin{110:}
+if(outptr=breakptr+3)or((outptr=breakptr+4)and(outbuf[breakptr]=32))then
+if((outbuf[outptr-3]=68)and(outbuf[outptr-2]=73)and(outbuf[outptr-1]=86)
+)or((outbuf[outptr-3]=100)and(outbuf[outptr-2]=105)and(outbuf[outptr-1]=
+118))or((outbuf[outptr-3]=77)and(outbuf[outptr-2]=79)and(outbuf[outptr-1
+]=68))or((outbuf[outptr-3]=109)and(outbuf[outptr-2]=111)and(outbuf[
+outptr-1]=100))then goto 666{:110};outsign:=32;outstate:=3;outval:=v;
+breakptr:=outptr;lastsign:=+1;end;0:begin{109:}
+if(outptr=breakptr+1)and((outbuf[breakptr]=42)or(outbuf[breakptr]=47))
+then goto 666{:109};outsign:=0;outstate:=3;outval:=v;breakptr:=outptr;
+lastsign:=+1;end;{108:}2:begin outsign:=43;outstate:=3;outval:=outapp*v;
+end;3:begin outstate:=5;outapp:=v;begin writeln(output);
+write(output,'! Two numbers occurred without a sign between them');
+error;end;end;4:begin outstate:=5;outapp:=outapp*v;end;
+5:begin outval:=outval+outapp;outapp:=v;begin writeln(output);
+write(output,'! Two numbers occurred without a sign between them');
+error;end;end;{:108}otherwise goto 666 end;goto 10;666:{111:}
+if v>=0 then begin if outstate=1 then begin breakptr:=outptr;
+begin outbuf[outptr]:=32;outptr:=outptr+1;end;end;appval(v);
+if outptr>linelength then flushbuffer;outstate:=1;
+end else begin begin outbuf[outptr]:=40;outptr:=outptr+1;end;
+begin outbuf[outptr]:=45;outptr:=outptr+1;end;appval(-v);
+begin outbuf[outptr]:=41;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;outstate:=0;end{:111};10:end;
+{:107}{113:}procedure sendtheoutput;label 2,21,22;var curchar:eightbits;
+k:0..linelength;j:0..maxbytes;w:0..2;n:integer;
+begin while stackptr>0 do begin curchar:=getoutput;
+21:case curchar of 0:;{116:}
+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,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:begin outcontrib[1]:=curchar;
+sendout(2,1);end;130:begin k:=0;j:=bytestart[curval];w:=curval mod 3;
+while(k<maxidlength)and(j<bytestart[curval+3])do begin k:=k+1;
+outcontrib[k]:=bytemem[w,j];j:=j+1;if outcontrib[k]=95 then k:=k-1;end;
+sendout(2,k);end;{:116}{119:}48,49,50,51,52,53,54,55,56,57:begin n:=0;
+repeat curchar:=curchar-48;if n>=214748364 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=10*n+curchar;
+curchar:=getoutput;until(curchar>57)or(curchar<48);sendval(n);k:=0;
+if curchar=101 then curchar:=69;if curchar=69 then goto 2 else goto 21;
+end;125:sendval(poolchecksum);12:begin n:=0;curchar:=48;
+repeat curchar:=curchar-48;if n>=268435456 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=8*n+curchar;
+curchar:=getoutput;until(curchar>55)or(curchar<48);sendval(n);goto 21;
+end;13:begin n:=0;curchar:=48;
+repeat if curchar>=65 then curchar:=curchar-55 else curchar:=curchar-48;
+if n>=134217728 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=16*n+curchar;
+curchar:=getoutput;
+until(curchar>70)or(curchar<48)or((curchar>57)and(curchar<65));
+sendval(n);goto 21;end;128:sendval(curval);46:begin k:=1;
+outcontrib[1]:=46;curchar:=getoutput;
+if curchar=46 then begin outcontrib[2]:=46;sendout(1,2);
+end else if(curchar>=48)and(curchar<=57)then goto 2 else begin sendout(0
+,46);goto 21;end;end;{:119}43,45:sendsign(44-curchar);{114:}
+4:begin outcontrib[1]:=97;outcontrib[2]:=110;outcontrib[3]:=100;
+sendout(2,3);end;5:begin outcontrib[1]:=110;outcontrib[2]:=111;
+outcontrib[3]:=116;sendout(2,3);end;6:begin outcontrib[1]:=105;
+outcontrib[2]:=110;sendout(2,2);end;31:begin outcontrib[1]:=111;
+outcontrib[2]:=114;sendout(2,2);end;24:begin outcontrib[1]:=58;
+outcontrib[2]:=61;sendout(1,2);end;26:begin outcontrib[1]:=60;
+outcontrib[2]:=62;sendout(1,2);end;28:begin outcontrib[1]:=60;
+outcontrib[2]:=61;sendout(1,2);end;29:begin outcontrib[1]:=62;
+outcontrib[2]:=61;sendout(1,2);end;30:begin outcontrib[1]:=61;
+outcontrib[2]:=61;sendout(1,2);end;32:begin outcontrib[1]:=46;
+outcontrib[2]:=46;sendout(1,2);end;{:114}39:{117:}begin k:=1;
+outcontrib[1]:=39;repeat if k<linelength then k:=k+1;
+outcontrib[k]:=getoutput;until(outcontrib[k]=39)or(stackptr=0);
+if k=linelength then begin writeln(output);
+write(output,'! String too long');error;end;sendout(1,k);
+curchar:=getoutput;if curchar=39 then outstate:=6;goto 21;end{:117};
+{115:}
+33,34,35,36,37,38,40,41,42,44,47,58,59,60,61,62,63,64,91,92,93,94,95,96,
+123,124{:115}:sendout(0,curchar);{121:}
+9:begin if bracelevel=0 then sendout(0,123)else sendout(0,91);
+bracelevel:=bracelevel+1;end;
+10:if bracelevel>0 then begin bracelevel:=bracelevel-1;
+if bracelevel=0 then sendout(0,125)else sendout(0,93);
+end else begin writeln(output);write(output,'! Extra @}');error;end;
+129:begin if bracelevel=0 then sendout(0,123)else sendout(0,91);
+if curval<0 then begin sendout(0,58);sendval(-curval);
+end else begin sendval(curval);sendout(0,58);end;
+if bracelevel=0 then sendout(0,125)else sendout(0,93);end;{:121}
+127:begin sendout(3,0);outstate:=6;end;2:{118:}begin k:=0;
+repeat if k<linelength then k:=k+1;outcontrib[k]:=getoutput;
+until(outcontrib[k]=2)or(stackptr=0);
+if k=linelength then begin writeln(output);
+write(output,'! Verbatim string too long');error;end;sendout(1,k-1);
+end{:118};3:{122:}begin sendout(1,0);
+while outptr>0 do begin if outptr<=linelength then breakptr:=outptr;
+flushbuffer;end;outstate:=0;end{:122};otherwise begin writeln(output);
+write(output,'! Can''t output ASCII code ',curchar:1);error;end end;
+goto 22;2:{120:}repeat if k<linelength then k:=k+1;
+outcontrib[k]:=curchar;curchar:=getoutput;
+if(outcontrib[k]=69)and((curchar=43)or(curchar=45))then begin if k<
+linelength then k:=k+1;outcontrib[k]:=curchar;curchar:=getoutput;
+end else if curchar=101 then curchar:=69;
+until(curchar<>69)and((curchar<48)or(curchar>57));
+if k=linelength then begin writeln(output);
+write(output,'! Fraction too long');error;end;sendout(3,k);goto 21{:120}
+;22:end;end;{:113}{127:}function linesdontmatch:boolean;label 10;
+var k:0..bufsize;begin linesdontmatch:=true;
+if changelimit<>limit then goto 10;
+if limit>0 then for k:=0 to limit-1 do if changebuffer[k]<>buffer[k]then
+goto 10;linesdontmatch:=false;10:end;{:127}{128:}
+procedure primethechangebuffer;label 22,30,10;var k:0..bufsize;
+begin changelimit:=0;{129:}while true do begin line:=line+1;
+if not inputln(changefile)then goto 10;if limit<2 then goto 22;
+if buffer[0]<>64 then goto 22;
+if(buffer[1]>=88)and(buffer[1]<=90)then buffer[1]:=buffer[1]+32;
+if buffer[1]=120 then goto 30;
+if(buffer[1]=121)or(buffer[1]=122)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @x?');error;
+end;end;22:end;30:{:129};{130:}repeat line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended after @x');error;end;goto 10;end;
+until limit>0;{:130};{131:}begin changelimit:=limit;
+if limit>0 then for k:=0 to limit-1 do changebuffer[k]:=buffer[k];
+end{:131};10:end;{:128}{132:}procedure checkchange;label 10;
+var n:integer;k:0..bufsize;begin if linesdontmatch then goto 10;n:=0;
+while true do begin changing:=not changing;templine:=otherline;
+otherline:=line;line:=templine;line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended before @y');error;end;changelimit:=0;
+changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;goto 10;end;{133:}
+if limit>1 then if buffer[0]=64 then begin if(buffer[1]>=88)and(buffer[1
+]<=90)then buffer[1]:=buffer[1]+32;
+if(buffer[1]=120)or(buffer[1]=122)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @y?');error;
+end;end else if buffer[1]=121 then begin if n>0 then begin loc:=2;
+begin writeln(output);
+write(output,'! Hmm... ',n:1,' of the preceding lines failed to match');
+error;end;end;goto 10;end;end{:133};{131:}begin changelimit:=limit;
+if limit>0 then for k:=0 to limit-1 do changebuffer[k]:=buffer[k];
+end{:131};changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;line:=line+1;
+if not inputln(webfile)then begin begin writeln(output);
+write(output,'! WEB file ended during a change');error;end;
+inputhasended:=true;goto 10;end;if linesdontmatch then n:=n+1;end;
+10:end;{:132}{135:}procedure getline;label 20;
+begin 20:if changing then{137:}begin line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended without @z');error;end;buffer[0]:=64;
+buffer[1]:=122;limit:=2;end;
+if limit>1 then if buffer[0]=64 then begin if(buffer[1]>=88)and(buffer[1
+]<=90)then buffer[1]:=buffer[1]+32;
+if(buffer[1]=120)or(buffer[1]=121)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @z?');error;
+end;end else if buffer[1]=122 then begin primethechangebuffer;
+changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;end;end;end{:137};if not changing then begin{136:}
+begin line:=line+1;
+if not inputln(webfile)then inputhasended:=true else if limit=
+changelimit then if buffer[0]=changebuffer[0]then if changelimit>0 then
+checkchange;end{:136};if changing then goto 20;end;loc:=0;
+buffer[limit]:=32;end;{:135}{139:}
+function controlcode(c:ASCIIcode):eightbits;
+begin case c of 64:controlcode:=64;39:controlcode:=12;
+34:controlcode:=13;36:controlcode:=125;32,9:controlcode:=136;
+42:begin write(output,'*',modulecount+1:1);flush(output);
+controlcode:=136;end;68,100:controlcode:=133;70,102:controlcode:=132;
+123:controlcode:=9;125:controlcode:=10;80,112:controlcode:=134;
+84,116,94,46,58:controlcode:=131;38:controlcode:=127;
+60:controlcode:=135;61:controlcode:=2;92:controlcode:=3;
+otherwise controlcode:=0 end;end;{:139}{140:}
+function skipahead:eightbits;label 30;var c:eightbits;
+begin while true do begin if loc>limit then begin getline;
+if inputhasended then begin c:=136;goto 30;end;end;buffer[limit+1]:=64;
+while buffer[loc]<>64 do loc:=loc+1;if loc<=limit then begin loc:=loc+2;
+c:=controlcode(buffer[loc-1]);if(c<>0)or(buffer[loc-1]=62)then goto 30;
+end;end;30:skipahead:=c;end;{:140}{141:}procedure skipcomment;label 10;
+var bal:eightbits;c:ASCIIcode;begin bal:=0;
+while true do begin if loc>limit then begin getline;
+if inputhasended then begin begin writeln(output);
+write(output,'! Input ended in mid-comment');error;end;goto 10;end;end;
+c:=buffer[loc];loc:=loc+1;{142:}if c=64 then begin c:=buffer[loc];
+if(c<>32)and(c<>9)and(c<>42)and(c<>122)and(c<>90)then loc:=loc+1 else
+begin begin writeln(output);
+write(output,'! Section ended in mid-comment');error;end;loc:=loc-1;
+goto 10;
+end end else if(c=92)and(buffer[loc]<>64)then loc:=loc+1 else if c=123
+then bal:=bal+1 else if c=125 then begin if bal=0 then goto 10;
+bal:=bal-1;end{:142};end;10:end;{:141}{145:}function getnext:eightbits;
+label 20,30,31;var c:eightbits;d:eightbits;j,k:0..longestname;
+begin 20:if loc>limit then begin getline;
+if inputhasended then begin c:=136;goto 31;end;end;c:=buffer[loc];
+loc:=loc+1;if scanninghex then{146:}
+if((c>=48)and(c<=57))or((c>=65)and(c<=70))then goto 31 else scanninghex
+:=false{:146};
+case c of 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,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:{148:}
+begin if((c=101)or(c=69))and(loc>1)then if(buffer[loc-2]<=57)and(buffer[
+loc-2]>=48)then c:=0;if c<>0 then begin loc:=loc-1;idfirst:=loc;
+repeat loc:=loc+1;d:=buffer[loc];
+until((d<48)or((d>57)and(d<65))or((d>90)and(d<97))or(d>122))and(d<>95);
+if loc>idfirst+1 then begin c:=130;idloc:=loc;end;end else c:=69;
+end{:148};34:{149:}begin doublechars:=0;idfirst:=loc-1;
+repeat d:=buffer[loc];loc:=loc+1;
+if(d=34)or(d=64)then if buffer[loc]=d then begin loc:=loc+1;d:=0;
+doublechars:=doublechars+1;
+end else begin if d=64 then begin writeln(output);
+write(output,'! Double @ sign missing');error;
+end end else if loc>limit then begin begin writeln(output);
+write(output,'! String constant didn''t end');error;end;d:=34;end;
+until d=34;idloc:=loc-1;c:=130;end{:149};64:{150:}
+begin c:=controlcode(buffer[loc]);loc:=loc+1;
+if c=0 then goto 20 else if c=13 then scanninghex:=true else if c=135
+then{151:}begin{153:}k:=0;
+while true do begin if loc>limit then begin getline;
+if inputhasended then begin begin writeln(output);
+write(output,'! Input ended in section name');error;end;goto 30;end;end;
+d:=buffer[loc];{154:}if d=64 then begin d:=buffer[loc+1];
+if d=62 then begin loc:=loc+2;goto 30;end;
+if(d=32)or(d=9)or(d=42)then begin begin writeln(output);
+write(output,'! Section name didn''t end');error;end;goto 30;end;k:=k+1;
+modtext[k]:=64;loc:=loc+1;end{:154};loc:=loc+1;
+if k<longestname-1 then k:=k+1;if(d=32)or(d=9)then begin d:=32;
+if modtext[k-1]=32 then k:=k-1;end;modtext[k]:=d;end;30:{155:}
+if k>=longestname-2 then begin begin writeln(output);
+write(output,'! Section name too long: ');end;
+for j:=1 to 25 do write(output,xchr[modtext[j]]);write(output,'...');
+if history=0 then history:=1;end{:155};
+if(modtext[k]=32)and(k>0)then k:=k-1;{:153};
+if k>3 then begin if(modtext[k]=46)and(modtext[k-1]=46)and(modtext[k-2]=
+46)then curmodule:=prefixlookup(k-3)else curmodule:=modlookup(k);
+end else curmodule:=modlookup(k);end{:151}
+else if c=131 then begin repeat c:=skipahead;until c<>64;
+if buffer[loc-1]<>62 then begin writeln(output);
+write(output,'! Improper @ within control text');error;end;goto 20;end;
+end{:150};{147:}
+46:if buffer[loc]=46 then begin if loc<=limit then begin c:=32;
+loc:=loc+1;end;
+end else if buffer[loc]=41 then begin if loc<=limit then begin c:=93;
+loc:=loc+1;end;end;
+58:if buffer[loc]=61 then begin if loc<=limit then begin c:=24;
+loc:=loc+1;end;end;
+61:if buffer[loc]=61 then begin if loc<=limit then begin c:=30;
+loc:=loc+1;end;end;
+62:if buffer[loc]=61 then begin if loc<=limit then begin c:=29;
+loc:=loc+1;end;end;
+60:if buffer[loc]=61 then begin if loc<=limit then begin c:=28;
+loc:=loc+1;end;
+end else if buffer[loc]=62 then begin if loc<=limit then begin c:=26;
+loc:=loc+1;end;end;
+40:if buffer[loc]=42 then begin if loc<=limit then begin c:=9;
+loc:=loc+1;end;
+end else if buffer[loc]=46 then begin if loc<=limit then begin c:=91;
+loc:=loc+1;end;end;
+42:if buffer[loc]=41 then begin if loc<=limit then begin c:=10;
+loc:=loc+1;end;end;{:147}32,9:goto 20;123:begin skipcomment;goto 20;end;
+125:begin begin writeln(output);write(output,'! Extra }');error;end;
+goto 20;end;otherwise if c>=128 then goto 20 else end;
+31:{if troubleshooting then debughelp;}getnext:=c;end;{:145}{157:}
+procedure scannumeric(p:namepointer);label 21,30;
+var accumulator:integer;nextsign:-1..+1;q:namepointer;val:integer;
+begin{158:}accumulator:=0;nextsign:=+1;
+while true do begin nextcontrol:=getnext;
+21:case nextcontrol of 48,49,50,51,52,53,54,55,56,57:begin{160:}val:=0;
+repeat val:=10*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>57)or(nextcontrol<48){:160};
+begin accumulator:=accumulator+nextsign*(val);nextsign:=+1;end;goto 21;
+end;12:begin{161:}val:=0;nextcontrol:=48;
+repeat val:=8*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>55)or(nextcontrol<48){:161};
+begin accumulator:=accumulator+nextsign*(val);nextsign:=+1;end;goto 21;
+end;13:begin{162:}val:=0;nextcontrol:=48;
+repeat if nextcontrol>=65 then nextcontrol:=nextcontrol-7;
+val:=16*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>70)or(nextcontrol<48)or((nextcontrol>57)and(
+nextcontrol<65)){:162};begin accumulator:=accumulator+nextsign*(val);
+nextsign:=+1;end;goto 21;end;130:begin q:=idlookup(0);
+if ilk[q]<>1 then begin nextcontrol:=42;goto 21;end;
+begin accumulator:=accumulator+nextsign*(equiv[q]-32768);nextsign:=+1;
+end;end;43:;45:nextsign:=-nextsign;132,133,135,134,136:goto 30;
+59:begin writeln(output);
+write(output,'! Omit semicolon in numeric definition');error;end;
+otherwise{159:}begin begin writeln(output);
+write(output,'! Improper numeric definition will be flushed');error;end;
+repeat nextcontrol:=skipahead until(nextcontrol>=132);
+if nextcontrol=135 then begin loc:=loc-2;nextcontrol:=getnext;end;
+accumulator:=0;goto 30;end{:159}end;end;30:{:158};
+if abs(accumulator)>=32768 then begin begin writeln(output);
+write(output,'! Value too big: ',accumulator:1);error;end;
+accumulator:=0;end;equiv[p]:=accumulator+32768;end;{:157}{165:}
+procedure scanrepl(t:eightbits);label 22,30,31,21;var a:sixteenbits;
+b:ASCIIcode;bal:eightbits;begin bal:=0;
+while true do begin 22:a:=getnext;case a of 40:bal:=bal+1;
+41:if bal=0 then begin writeln(output);write(output,'! Extra )');error;
+end else bal:=bal-1;39:{168:}begin b:=39;
+while true do begin begin if tokptr[z]=maxtoks then begin writeln(output
+);write(output,'! Sorry, ','token',' capacity exceeded');error;
+history:=3;jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;
+end;
+if b=64 then if buffer[loc]=64 then loc:=loc+1 else begin writeln(output
+);write(output,'! You should double @ signs in strings');error;end;
+if loc=limit then begin begin writeln(output);
+write(output,'! String didn''t end');error;end;buffer[loc]:=39;
+buffer[loc+1]:=0;end;b:=buffer[loc];loc:=loc+1;
+if b=39 then begin if buffer[loc]<>39 then goto 31 else begin loc:=loc+1
+;begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=39;tokptr[z]:=tokptr[z]+1;end;end;end;
+end;31:end{:168};35:if t=3 then a:=0;{167:}130:begin a:=idlookup(0);
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=(a div 256)+128;tokptr[z]:=tokptr[z]+1;
+end;a:=a mod 256;end;
+135:if t<>135 then goto 30 else begin begin if tokptr[z]=maxtoks then
+begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=(curmodule div 256)+168;
+tokptr[z]:=tokptr[z]+1;end;a:=curmodule mod 256;end;2:{169:}
+begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=2;tokptr[z]:=tokptr[z]+1;end;
+buffer[limit+1]:=64;
+21:if buffer[loc]=64 then begin if loc<limit then if buffer[loc+1]=64
+then begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=64;tokptr[z]:=tokptr[z]+1;end;
+loc:=loc+2;goto 21;end;
+end else begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=buffer[loc];tokptr[z]:=tokptr[z]+1;end;
+loc:=loc+1;goto 21;end;if loc>=limit then begin writeln(output);
+write(output,'! Verbatim string didn''t end');error;
+end else if buffer[loc+1]<>62 then begin writeln(output);
+write(output,'! You should double @ signs in verbatim strings');error;
+end;loc:=loc+2;end{:169};
+133,132,134:if t<>135 then goto 30 else begin begin writeln(output);
+write(output,'! @',xchr[buffer[loc-1]],' is ignored in Pascal text');
+error;end;goto 22;end;136:goto 30;{:167}otherwise end;
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=a;tokptr[z]:=tokptr[z]+1;end;end;
+30:nextcontrol:=a;{166:}
+if bal>0 then begin if bal=1 then begin writeln(output);
+write(output,'! Missing )');error;end else begin writeln(output);
+write(output,'! Missing ',bal:1,' )''s');error;end;
+while bal>0 do begin begin if tokptr[z]=maxtoks then begin writeln(
+output);write(output,'! Sorry, ','token',' capacity exceeded');error;
+history:=3;jumpout;end;tokmem[z,tokptr[z]]:=41;tokptr[z]:=tokptr[z]+1;
+end;bal:=bal-1;end;end{:166};
+if textptr>maxtexts-4 then begin writeln(output);
+write(output,'! Sorry, ','text',' capacity exceeded');error;history:=3;
+jumpout;end;currepltext:=textptr;tokstart[textptr+4]:=tokptr[z];
+textptr:=textptr+1;if z=3 then z:=0 else z:=z+1;end;{:165}{170:}
+procedure definemacro(t:eightbits);var p:namepointer;
+begin p:=idlookup(t);scanrepl(t);equiv[p]:=currepltext;
+textlink[currepltext]:=0;end;{:170}{172:}procedure scanmodule;
+label 22,30,10;var p:namepointer;begin modulecount:=modulecount+1;{173:}
+nextcontrol:=0;
+while true do begin 22:while nextcontrol<=132 do begin nextcontrol:=
+skipahead;if nextcontrol=135 then begin loc:=loc-2;nextcontrol:=getnext;
+end;end;if nextcontrol<>133 then goto 30;nextcontrol:=getnext;
+if nextcontrol<>130 then begin begin writeln(output);
+write(output,'! Definition flushed, must start with ',
+'identifier of length > 1');error;end;goto 22;end;nextcontrol:=getnext;
+if nextcontrol=61 then begin scannumeric(idlookup(1));goto 22;
+end else if nextcontrol=30 then begin definemacro(2);goto 22;
+end else{174:}if nextcontrol=40 then begin nextcontrol:=getnext;
+if nextcontrol=35 then begin nextcontrol:=getnext;
+if nextcontrol=41 then begin nextcontrol:=getnext;
+if nextcontrol=61 then begin begin writeln(output);
+write(output,'! Use == for macros');error;end;nextcontrol:=30;end;
+if nextcontrol=30 then begin definemacro(3);goto 22;end;end;end;end;
+{:174};begin writeln(output);
+write(output,'! Definition flushed since it starts badly');error;end;
+end;30:{:173};{175:}case nextcontrol of 134:p:=0;135:begin p:=curmodule;
+{176:}repeat nextcontrol:=getnext;until nextcontrol<>43;
+if(nextcontrol<>61)and(nextcontrol<>30)then begin begin writeln(output);
+write(output,'! Pascal text flushed, = sign is missing');error;end;
+repeat nextcontrol:=skipahead;until nextcontrol=136;goto 10;end{:176};
+end;otherwise goto 10 end;{177:}storetwobytes(53248+modulecount);{:177};
+scanrepl(135);{178:}
+if p=0 then begin textlink[lastunnamed]:=currepltext;
+lastunnamed:=currepltext;
+end else if equiv[p]=0 then equiv[p]:=currepltext else begin p:=equiv[p]
+;while textlink[p]<maxtexts do p:=textlink[p];textlink[p]:=currepltext;
+end;textlink[currepltext]:=maxtexts;{:178};{:175};10:end;{:172}{181:}
+{procedure debughelp;label 888,10;var k:integer;
+begin debugskipped:=debugskipped+1;
+if debugskipped<debugcycle then goto 10;debugskipped:=0;
+while true do begin write(output,'#');flush(output);read(input,ddt);
+if ddt<0 then goto 10 else if ddt=0 then begin goto 888;
+888:ddt:=0;
+end else begin read(input,dd);case ddt of 1:printid(dd);2:printrepl(dd);
+3:for k:=1 to dd do write(output,xchr[buffer[k]]);
+4:for k:=1 to dd do write(output,xchr[modtext[k]]);
+5:for k:=1 to outptr do write(output,xchr[outbuf[k]]);
+6:for k:=1 to dd do write(output,xchr[outcontrib[k]]);
+otherwise write(output,'?')end;end;end;10:end;}{:181}{182:}
+begin initialize;{134:}openinput;line:=0;otherline:=0;changing:=true;
+primethechangebuffer;changing:=not changing;templine:=otherline;
+otherline:=line;line:=templine;limit:=0;loc:=1;buffer[0]:=32;
+inputhasended:=false;{:134};
+writeln(output,'This is TANGLE, Version 4.4 for SunOS');{183:}
+phaseone:=true;modulecount:=0;repeat nextcontrol:=skipahead;
+until nextcontrol=136;while not inputhasended do scanmodule;{138:}
+if changelimit<>0 then begin for ii:=0 to changelimit do buffer[ii]:=
+changebuffer[ii];limit:=changelimit;changing:=true;line:=otherline;
+loc:=changelimit;begin writeln(output);
+write(output,'! Change file entry did not match');error;end;end{:138};
+phaseone:=false;{:183};{for ii:=0 to 3 do maxtokptr[ii]:=tokptr[ii];}
+{112:}if textlink[0]=0 then begin begin writeln(output);
+write(output,'! No output was specified.');end;
+if history=0 then history:=1;end else begin begin writeln(output);
+write(output,'Writing the output file');end;flush(output);{83:}
+stackptr:=1;bracelevel:=0;curstate.namefield:=0;
+curstate.replfield:=textlink[0];zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];curstate.modfield:=0;
+{:83};{96:}outstate:=0;outptr:=0;breakptr:=0;semiptr:=0;outbuf[0]:=0;
+line:=1;{:96};sendtheoutput;{98:}breakptr:=outptr;semiptr:=0;
+flushbuffer;if bracelevel<>0 then begin writeln(output);
+write(output,'! Program ended at brace level ',bracelevel:1);error;end;
+{:98};begin writeln(output);write(output,'Done.');end;end{:112};
+9999:if stringptr>256 then{184:}begin begin writeln(output);
+write(output,stringptr-256:1,' strings written to string pool file.');
+end;write(pool,'*');
+for ii:=1 to 9 do begin outbuf[ii]:=poolchecksum mod 10;
+poolchecksum:=poolchecksum div 10;end;
+for ii:=9 downto 1 do write(pool,xchr[48+outbuf[ii]]);writeln(pool);
+end{:184};{[186:]begin writeln(output);
+write(output,'Memory usage statistics:');end;begin writeln(output);
+write(output,nameptr:1,' names, ',textptr:1,' replacement texts;');end;
+begin writeln(output);write(output,byteptr[0]:1);end;
+for wo:=1 to 2 do write(output,'+',byteptr[wo]:1);
+if phaseone then for ii:=0 to 3 do maxtokptr[ii]:=tokptr[ii];
+write(output,' bytes, ',maxtokptr[0]:1);
+for ii:=1 to 3 do write(output,'+',maxtokptr[ii]:1);
+write(output,' tokens.');[:186];}{187:}
+case history of 0:begin writeln(output);
+write(output,'(No errors were found.)');end;1:begin writeln(output);
+write(output,'(Did you see the warning message above?)');end;
+2:begin writeln(output);
+write(output,'(Pardon me, but I think I spotted something wrong.)');end;
+3:begin writeln(output);
+write(output,'(That was a fatal error, my friend.)');end;end{:187};
+writeln(output);if(history<>0)and(history<>1)then exit(1)else exit(0);
+end.{:182}
diff --git a/systems/knuth/local/web-sparc/weave.ch b/systems/knuth/local/web-sparc/weave.ch
new file mode 100644
index 0000000000..87c0e0e674
--- /dev/null
+++ b/systems/knuth/local/web-sparc/weave.ch
@@ -0,0 +1,591 @@
+% Change file for the WEAVE processor, for use on Berkeley UNIX systems.
+% This file was created by Howard Trickey and Pavel Curtis.
+
+% History:
+% 11/29/82 HWT Original version. This modifies weave to allow a new
+% control sequence:
+% @=...text...@> Put ...text... verbatim on a line
+% by itself in the Pascal output.
+% (argument must fit on one line)
+% This control sequence facilitates putting #include "gcons.h"
+% (for example) in files meant for the pc compiler.
+% Also, there is a command line option, -c, which means that
+% only the modules affected by the change file are to generate
+% TeX output. (All the limbo stuff still goes to the output
+% file, as does the index and table of contents.)
+%
+% 2/12/83 HWT Brought up for use with version 1.3. Uses Knuth's new
+% control sequences for verbatim Pascal (as above, without
+% the "on one line" part), and the force_line (@\) primitive.
+% Also, he added stuff to keep track of changed modules, and
+% output enough information that macros can choose only to
+% print changed modules. This isn't as efficient as my
+% implementation because I avoided outputting the text for
+% non-changed modules altogether, but this feature won't be
+% used too often (just for TeX and TeXware), so Knuth's
+% solution is accepted.
+% The change file changes that used
+% to implement these features have been removed.
+% There is a -x flag to cause WEAVE to omit cross referencing
+% of identifiers, and index and T.of.C. processing.
+% This, too, is unnecessary, for one could simply redefine some
+% WEB macros to avoid the printing, but there are only a couple
+% of easy changes, so they have been made.
+%
+% 2/18 HWT Increased stack size to 400 (not for TeX-related programs).
+%
+% 3/18 HWT Brought up for Version 1.5. Made it print newline at end of
+% run.
+%
+% 4/13 PC Merged with Pavel's version, including adding a call to
+% exit() at the end of the program, based upon the value of
+% `history'.
+% 4/16 PC Brought up to version 1.5 released with TeX 0.97 in April 1983
+% 6/29 HWT Brought up to version 1.7 released with TeX 0.99 in June 1983,
+% introducing a new change file format
+% 7/17 HWT Brought up to version 2.0 released with TeX 0.999 in July 1983
+% 7/29 HWT Brought up to version 2.1
+% 11/17 HWT Brought up to version 2.4 released with TeX 1.0. Made
+% changes to use C routines for I/O, for speedup.
+% 1/31 HWT Brought up to version 2.6
+% 12/28/85 PAM Brought up to version 2.8 (like 2.7, version number only)
+% 3/6/87 PAM Brought up to version 2.9 (like 2.8, version number only)
+% 8/10/89 don Brought up to version 3.1, with `otherwise' fixed too
+% 8/31/89 don Brought up to version 4, with all possible characters enabled
+% 10/16/89 don fixed bug when ".web" and ".ch" are explicitly stated
+% 3/25/90 don changed version number to 4.1
+% 9/6/90 don changed version number to 4.2
+% 9/19/91 don changed version number to 4.3
+% 1/12/92 don changed version number to 4.4
+
+% NOTE: The module numbers refer to the standard WEB manual (CS 980).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{WEAVE changes for Berkeley {\mc UNIX}}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is WEAVE, Version 4.4'
+@y
+@d banner=='This is WEAVE, Version 4.4 for SunOS'
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] add input and output, remove other files, add ref to scan_args,
+% [2] and #include external definition for exit(), etc.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+program WEAVE(@!web_file,@!change_file,@!tex_file);
+label end_of_WEAVE; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@<Error handling procedures@>@/
+@y
+program WEAVE(@!input,@!output);
+label end_of_WEAVE; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\
+@=#include "tangext.h"@>
+@\@/
+@<Error handling procedures@>@/
+@<|scan_args| procedure@>@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Show statistics
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d stat==@{ {change this to `$\\{stat}\equiv\null$'
+ when gathering usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$'
+ when gathering usage statistics}
+@y
+@d stat== {I'm gathering usage statistics}
+@d tats== {because I have to know how close \TeX\ comes to the limits}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] compiler options
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+@y
+@=(*$C-*)@> {no range check}
+@!debug @=(*$C+*)@>@+ gubed {but turn everything on when debugging}
+@\
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Fix others:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {SunOS Pascal default cases}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8] Constants: Make stack_size=400 instead of 200
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!stack_size=200; {number of simultaneous output levels}
+@y
+@!stack_size=400; {number of simultaneous output levels}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [17] enable maximum character set
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for i:=1 to @'37 do xchr[i]:=' ';
+for i:=@'200 to @'377 do xchr[i]:=' ';
+@y
+for i:=1 to @'37 do xchr[i]:=chr(i);
+for i:=@'200 to @'377 do xchr[i]:=chr(i);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] terminal output: use standard i/o
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@y
+@d term_out==output
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@z
+
+@x
+@<Globals...@>=
+@!term_out:text_file; {the terminal as an output file}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] init terminal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal. Here is one way to do this
+on the \PASCAL\ system that was used in \.{TANGLE}'s initial development:
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
+@y
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal.
+@^system dependencies@>
+
+@<Set init...@>=
+; {nothing need be done}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [22] flush terminal buffer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush(term_out) {empty the terminal output buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] open input files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens the input 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. We will see below that
+\.{WEAVE} reads through the entire input twice.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read |web_file| and |change_file|}
+begin reset(web_file); reset(change_file);
+end;
+@y
+@ The following code opens the input files.
+This is called after |scan_args| has filled the file name variables
+appropriately.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read |web_file| and |change_file|}
+begin reset(web_file,web_name);
+ reset(change_file,change_name);
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [26] opening tex file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens |tex_file|.
+Since this file was listed in the program header, we assume that the
+\PASCAL\ runtime system has checked that a suitable external file name has
+been given.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(tex_file);
+@y
+@ The following code opens |tex_file|.
+The |scan_args| procedure is used to set up |tex_name| as required.
+@^system dependencies@>
+
+@<Set init...@>=
+scan_args;
+rewrite(tex_file,tex_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [28] faster input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+var final_limit:0..buf_size; {|limit| without trailing blanks}
+begin limit:=0; final_limit:=0;
+if eof(f) then input_ln:=false
+else begin while not eoln(f) do
+ begin buffer[limit]:=xord[f^]; get(f);
+ incr(limit);
+ if buffer[limit-1]<>" " then final_limit:=limit;
+ if limit=buf_size then
+ begin while not eoln(f) do get(f);
+ decr(limit); {keep |buffer[buf_size]| empty}
+ if final_limit>limit then final_limit:=limit;
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ end;
+ read_ln(f); limit:=final_limit; input_ln:=true;
+ end;
+end;
+@y
+With Berkeley {\mc UNIX} we call an external C procedure, |line_read|.
+That routine fills |buffer| from 0 onwards with the |xord|'ed values
+of the next line, setting |limit| appropriately (ignoring trailing
+blanks).
+It will stop if |limit=buf_size|, and the following will cause an error
+message.
+Note: for bootstrapping purposes it is all right to use the original form
+of |input_ln|; it will just run slower.
+
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+begin limit:=0;
+if test_eof(f) then input_ln:=false
+else begin line_read(f);
+ if limit=buf_size then
+ begin
+ decr(limit); {keep |buffer[buf_size]| empty}
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ input_ln:=true;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [50] don't enter xrefs if no_xref set
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d append_xref(#)==if xref_ptr=max_refs then overflow('cross reference')
+ else begin incr(xref_ptr); num(xref_ptr):=#;
+ end
+
+@p procedure new_xref(@!p:name_pointer);
+label exit;
+var q:xref_number; {pointer to previous cross reference}
+@!m,@!n: sixteen_bits; {new and previous cross-reference value}
+begin if (reserved(p)or(byte_start[p]+1=byte_start[p+ww]))and
+@y
+If the user has sent the |no_xref| flag (the -x option of the command line),
+then it is unnecessary to keep track of cross references for identifers.
+If one were careful, one could probably make more changes around module
+100 to avoid a lot of identifier looking up.
+
+@d append_xref(#)==if xref_ptr=max_refs then overflow('cross reference')
+ else begin incr(xref_ptr); num(xref_ptr):=#;
+ end
+
+@p procedure new_xref(@!p:name_pointer);
+label exit;
+var q:xref_number; {pointer to previous cross-reference}
+@!m,@!n: sixteen_bits; {new and previous cross-reference value}
+begin if no_xref then return;
+if (reserved(p)or(byte_start[p]+1=byte_start[p+ww]))and
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [122] faster flush_buffer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+done: for k:=1 to j do write(tex_file,xchr[out_buf[k]]);
+@y
+done: linewrite(tex_file,j);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [239] omit index and module names if no_xref set
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Phase III: Output the cross-reference index@>=
+@y
+If the user has set the |no_xref| flag (the -x option on the command line),
+just finish off the page, omitting the index, module name list, and table of
+contents.
+
+@<Phase III: Output the cross-reference index@>=
+if no_xref then begin
+ finish_line;
+ out("\"); out5("v")("f")("i")("l")("l");
+ out4("\")("e")("n")("d");
+ finish_line;
+ end
+else begin
+@z
+
+@x
+print('Done.');
+@y
+end;
+print('Done.');
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [258] term_in == input, when debugging
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+any error stop will set |debug_cycle| to zero.
+@y
+any error stop will set |debug_cycle| to zero.
+
+@d term_in==input
+@z
+
+@x
+@!term_in:text_file; {the user's terminal as an input file}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [259] Take out reset(term_in)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reset(term_in,'TTY:','/I'); {open |term_in| as the terminal, don't do a |get|}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [261] print newline at end of run and exit based upon value of history
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure Phase_I;
+@y
+@d UNIXexit==e@&x@&i@&t
+
+@p procedure Phase_I;
+@z
+
+@x
+end.
+@y
+new_line;
+if (history <> spotless) and (history <> harmless_message) then
+ UNIXexit(1)
+else
+ UNIXexit(0);
+end.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [264] system dependent changes--the scan_args procedure
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This module should be replaced, if necessary, by changes to the program
+that are necessary to make \.{WEAVE} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous modules preserve the module numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new modules, can be inserted here; then only the index
+itself will get a new module number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+The user calls \.{WEAVE} with arguments on the command line.
+These are either file names or flags (beginning with '-').
+The following globals are for communicating the user's desires to the rest
+of the program. The various |file_name| variables contain strings with
+the full names of those files, as UNIX knows them.
+
+The only flag that affects weave is |'-x'| whose status is kept in |no_xref|.
+
+@d max_file_name_length==60
+
+@<Globals...@>=
+@!web_name,@!change_name,@!tex_name:
+ array[1..max_file_name_length] of char;
+@!no_xref:boolean;
+
+@ The |scan_args| procedure looks at the command line arguments and sets
+the |file_name| variables accordingly.
+At least one file name must be present: the \.{WEB} file. It may have
+an extension, or it may omit it to get |'.web'| added.
+The \TeX\ output file name is formed by replacing the \.{WEB} file
+name extension by |'.tex'|.
+
+If there is another file name present among the arguments, it is the
+change file, again either with an extension or without one to get |'.ch'|
+An omitted change file argument means that |'/dev/null'| should be used,
+when no changes are desired.
+
+An argument beginning with a minus sign is a flag.
+Any letters following the minus sign may cause global flag variables to be
+set.
+Currently, an |x| means that the cross referencing information---the index,
+the module name list, and the table of contents---is to be suppressed.
+
+@<|scan_args|...@>=
+procedure scan_args;
+var dot_pos,i,a: integer; {indices}
+@!fname: array[1..max_file_name_length-5] of char; {temporary argument holder}
+@!found_web,@!found_change: boolean; {|true| when those file names have
+ been seen}
+begin
+found_web:=false;
+found_change:=false;
+no_xref:=false;
+for a:=1 to argc-1 do begin
+ argv(a,fname); {put argument number |a| into |fname|}
+ if fname[1]<>'-' then begin
+ if not found_web then
+ @<Get |web_name|, |pascal_name|, and
+ | pool_name| variables from |fname|@>
+ else if not found_change then
+ @<Get |change_name| from |fname|@>
+ else @<Print usage error message and quit@>;
+ end
+ else @<Handle flag argument in |fname|@>;
+ end;
+if not found_web then @<Print usage error message and quit@>;
+if not found_change then @<Set up null change file@>;
+end;
+
+@ Use all of |fname| for the |web_name| if there is a |'.'| in it,
+otherwise add |'.web'|.
+The other file names come from adding things after the dot.
+The |argv| procedure will not put more than |max_file_name_length-5|
+characters into |fname|, and this leaves enough room in the |file_name|
+variables to add the extensions.
+
+The end of a file name is marked with a |' '|, the convention assumed by
+the |reset| and |rewrite| procedures.
+
+@<Get |web_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=max_file_name_length-5) do begin
+ web_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ web_name[dot_pos]:='.';
+ web_name[dot_pos+1]:='w';
+ web_name[dot_pos+2]:='e';
+ web_name[dot_pos+3]:='b';
+ web_name[dot_pos+4]:=' ';
+ end
+else web_name[i]:=' ';
+for i:=1 to dot_pos do begin
+ tex_name[i]:=web_name[i];
+ end;
+tex_name[dot_pos+1]:='t';
+tex_name[dot_pos+2]:='e';
+tex_name[dot_pos+3]:='x';
+tex_name[dot_pos+4]:=' ';
+found_web:=true;
+end
+
+@
+
+@<Get |change_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>' ') and (i<=max_file_name_length-5)
+do begin
+ change_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ change_name[dot_pos]:='.';
+ change_name[dot_pos+1]:='c';
+ change_name[dot_pos+2]:='h';
+ change_name[dot_pos+3]:=' ';
+ end
+else change_name[i]:=' ';
+found_change:=true;
+end
+
+@
+
+@<Set up null...@>=
+begin
+ change_name[1]:='/';
+ change_name[2]:='d';
+ change_name[3]:='e';
+ change_name[4]:='v';
+ change_name[5]:='/';
+ change_name[6]:='n';
+ change_name[7]:='u';
+ change_name[8]:='l';
+ change_name[9]:='l';
+ change_name[10]:=' ';
+end
+
+@
+
+@<Handle flag...@>=
+begin
+i:=2;
+while (fname[i]<>' ') and (i<=max_file_name_length-5) do begin
+ if fname[i]='x' then no_xref:=true;
+ incr(i);
+ end;
+end
+
+@
+
+@<Print usage error message and quit@>=
+begin
+print_nl('! Usage: weave webfile[.web] [changefile[.ch]] [-x]');
+error;
+jump_out;
+end
+
+@z
diff --git a/systems/knuth/local/web-sparc/weavext.c b/systems/knuth/local/web-sparc/weavext.c
new file mode 100644
index 0000000000..87f1c99b22
--- /dev/null
+++ b/systems/knuth/local/web-sparc/weavext.c
@@ -0,0 +1,107 @@
+/* External procedures for weave */
+/* Written by: H. Trickey, 11/17/83 */
+
+/* Modified for 8-bit input --- don 8/31/89 */
+
+/* Note: these procedures aren't necessary; the default input_ln and
+ * flush_buffer routines in tangle/weave work fine on UNIX.
+ * However a very big time improvement is achieved by using these.
+ *
+ * These procedures are the same as for tangle, except for a slight offset
+ * in which characters are output from outbuf in linewrite.
+ */
+
+#define BUF_SIZE 100 /* should agree with tangle.web */
+
+#include "h00vars.h" /* defines Pascal I/O structure */
+
+extern short buffer[]; /* 0..BUF_SIZE. Input goes here */
+extern short outbuf[]; /* 0..OUT_BUF_SIZE. Output from here */
+extern short xord[];
+extern char xchr[]; /* character translation arrays */
+extern short limit; /* index into buffer. Note that limit
+ is 0..long_buf_size in weave.web; this
+ differs from the definition in tangle.web */
+
+/*
+ * lineread reads from the Pascal text file with iorec pointer filep
+ * into buffer[0], buffer[1],..., buffer[limit-1] (and
+ * setting "limit").
+ * Characters are read until a newline is found (which isn't put in the
+ * buffer) or until the next character would go into buffer[BUF_SIZE].
+ * And trailing blanks are to be ignored, so limit is really set to
+ * one past the last non-blank.
+ * The characters need to be translated, so really xord[c] is put into
+ * the buffer when c is read.
+ * If end-of-file is encountered, the funit field of *filep is set
+ * appropriately.
+ */
+lineread(filep)
+struct iorec *filep;
+{
+ register c;
+ register short *cs; /* pointer into buffer where next char goes */
+ register short *cnb; /* last non-blank character input */
+ register FILE *iop; /* stdio-style FILE pointer */
+ register int l; /* how many chars allowed before buffer overflow */
+
+ iop = filep->fbuf;
+ cnb = cs = &(buffer[0]);
+ l = BUF_SIZE;
+ /* overflow when next char would go into buffer[BUF_SIZE] */
+ while (--l>=0 && (c = getc(iop)) != EOF && c!='\n')
+ if((*cs++ = xord[c])!=' ') cnb = cs;
+ if (c == EOF)
+ filep->funit |= EOFF; /* we hit end-of-file */
+ limit = cnb-&(buffer[0]);
+}
+
+/*
+ * linewrite writes to the Pascal text file with iorec pointer filep
+ * from outbuf[1], outbuf[1],..., outbuf[cnt].
+ * (Note the offset of indices vis a vis the tangext version of this.)
+ * Translation is done, so that xchr[c] is output instead of c.
+ */
+ linewrite(filep,cnt)
+ struct iorec *filep;
+ int cnt;
+ {
+ register FILE *iop; /* stdio-style FILE pointer */
+ register short *cs; /* pointer to next character to output */
+ register int l; /* how many characters left to output */
+
+ iop = filep->fbuf;
+ cs = &(outbuf[1]);
+ l = cnt;
+ while (--l>=0) putc(xchr[*cs++],iop);
+}
+
+/*
+** testeof(filep)
+**
+** Test whether or not the Pascal text file with iorec pointer filep
+** has reached end-of-file (when the only I/O on it is done with
+** lineread, above).
+** We may have to read the next character and unget it to see if perhaps
+** the end-of-file is next.
+*/
+
+bool
+testeof(filep)
+register struct iorec *filep;
+{
+ register char c;
+ register FILE *iop; /* stdio-style FILE pointer */
+ if (filep->funit & EOFF)
+ return(TRUE);
+ else { /* check to see if next is EOF */
+ iop = filep->fbuf;
+ c = getc(iop);
+ if (c == EOF)
+ return(TRUE);
+ else {
+ ungetc(c,iop);
+ return(FALSE);
+ }
+ }
+}
diff --git a/systems/knuth/local/web/Makefile b/systems/knuth/local/web/Makefile
new file mode 100644
index 0000000000..5a71f3c71a
--- /dev/null
+++ b/systems/knuth/local/web/Makefile
@@ -0,0 +1,34 @@
+# Make executable files for this workstation, using the master TeX sources
+
+PFLAGS= -g -I..
+CFLAGS= -g -I..
+PC= gpc
+
+all: tangle weave
+
+%.p: %.ch
+ tangle ../../dist/web/$*.web $*.ch
+# the next line formerly necessary; but not with Linux's (Web2C) tangle
+# mv ../../dist/web/$*.p .
+
+%.ch: ../../dist/web/%.web
+ touch $*.ch
+
+%.o: %.p
+ ${PC} ${PFLAGS} -c $*.p
+
+tangle: tangle.o tangext.o tangext.h
+ pc ${PFLAGS} -o tangle tangle.o tangext.o
+
+tangext.o: tangext.c ../GPCtypes.h
+
+weave: weave.o tangext.o tangext.h
+ pc ${PFLAGS} -o weave weave.o tangext.o
+
+install: # doit make install PROG=tangle
+ mv ${PROG} /usr/local/bin/${PROG}
+
+clean: # we save tangle.p for emergency rebooting
+# rm -f weave.p *.o *~ ../../dist/web/*.pool
+ rm -f weave.p *.o *~ *.pool
+
diff --git a/systems/knuth/local/web/tangext.c b/systems/knuth/local/web/tangext.c
new file mode 100644
index 0000000000..2a8a88d292
--- /dev/null
+++ b/systems/knuth/local/web/tangext.c
@@ -0,0 +1,106 @@
+/* External procedures for tangle/weave */
+/* Written by Don Knuth, 29 April 2000, based on Howard Trickey's code of 83 */
+
+/* Note: The input files are read directly with C routines, instead
+ of using the Pascal runtime system. So we do our own EOF testing.
+ The GNU Pascal routines are used only to open ("reset") the files. */
+
+#define BUF_SIZE 500 /* should agree with long_buf_size in weave.web */
+#define max_file_name_length 60 /* should agree with tangle.ch */
+
+#include "GPCtypes.h" /* defines GNU Pascal I/O structure */
+#include <string.h>
+
+void Argv(int n, char* str)
+{
+ register char *s,*t;
+ if (n<0 || n>=_p_argc) return;
+ s=_p_argv[n];
+ if (strlen(s) >= max_file_name_length-5) {
+ str[0]='\0'; return; /* TANGLE doesn't want it too long */
+ }
+ for (t=str; *s; s++,t++) *t=*s;
+ *t='\0';
+}
+
+void Flushstdout()
+{
+ fflush(stdout);
+}
+
+#define buffer Buffer /* GNU Pascal likes to capitalize */
+#define xord Xord
+#define xchr Xchr
+#define limit Limit
+
+unsigned char buffer[BUF_SIZE+1]; /* 0..BUF_SIZE. Input goes here */
+unsigned char xord[256];
+unsigned char xchr[256]; /* character translation arrays */
+int limit; /* index into buffer */
+
+/*
+ * lineread reads from the Pascal text file with iorec pointer filep
+ * into buffer[0], buffer[1],..., buffer[limit-1] (and
+ * setting "limit").
+ * Characters are read until a newline is found (which isn't put in the
+ * buffer) or until the next character would go into buffer[BUF_SIZE].
+ * And trailing blanks are to be ignored, so limit is really set to
+ * one past the last non-blank.
+ * The characters need to be translated, so really xord[c] is put into
+ * the buffer when c is read.
+ * If end-of-file is encountered, the FilSta field of *filep is set
+ * appropriately.
+ */
+void Lineread(FDR filep)
+{
+ register int c;
+ register unsigned char *cs; /* points to where next char goes */
+ register unsigned char *cnb; /* last non-blank character input */
+ register FILE *iop; /* stdio-style FILE pointer */
+ register int l; /* how many chars before buffer overflow */
+
+ iop = filep->FilJfn;
+ cnb = cs = &buffer[0];
+ l = BUF_SIZE;
+
+ /* overflow when next char would go into buffer[BUF_SIZE] */
+ while ((--l >= 0) && ((c = getc(iop)) != EOF) && (c != '\n')) {
+ if ((*cs++ = xord[c]) != ' ')
+ cnb = cs;
+ }
+
+ if (c == EOF)
+ filep->FilSta |= FiEof; /* we hit end-of-file */
+ /* actually that wasn't necessary, since testeof uses feof */
+
+ limit = (cnb - &buffer[0]);
+}
+
+/*
+ * testeof(filep)
+ *
+ * Test whether or not the Pascal text file with iorec pointer filep
+ * has reached end-of-file (when the only I/O on it is done with
+ * lineread, above).
+ * We may have to read the next character and unget it to see if perhaps
+ * the end-of-file is next.
+ */
+bool Testeof(FDR filep)
+{
+ register int c;
+ register FILE *iop; /* stdio-style FILE pointer */
+
+ iop = filep->FilJfn;
+ if (feof(iop)) return TRUE;
+ c = getc(iop);
+ if (c == EOF) return TRUE;
+ else {
+ ungetc(c,iop);
+ return FALSE;
+ }
+}
+
+void Exit(int c)
+{
+ exit(c);
+}
diff --git a/systems/knuth/local/web/tangext.h b/systems/knuth/local/web/tangext.h
new file mode 100644
index 0000000000..c460a973c5
--- /dev/null
+++ b/systems/knuth/local/web/tangext.h
@@ -0,0 +1,13 @@
+argc:asmname '_p_argc' Integer;
+
+procedure argv(no:Integer;var str:UNIXfilename); external;
+
+procedure flushstdout; external;
+
+procedure exit(x : integer); external;
+
+procedure lineread(var f:text); external;
+
+procedure linewrite(var f:text; cnt:integer); external;
+
+function testeof(var f:text):boolean; external;
diff --git a/systems/knuth/local/web/tangle.ch b/systems/knuth/local/web/tangle.ch
new file mode 100644
index 0000000000..83351de0a2
--- /dev/null
+++ b/systems/knuth/local/web/tangle.ch
@@ -0,0 +1,654 @@
+% Change file for the TANGLE processor, for use with GNU Pascal.
+% This file was created by Don Knuth, based on the original
+% UNIX port by Howard Trickey, Pavel Curtis, et al.
+% (See ../web-sparc for details of that work.)
+
+% History:
+% 2000.04.29 Original version
+
+% NOTE: The module numbers refer to the standard WEB manual (CS 980).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{TANGLE changes for {\mc GNU} Pascal}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is TANGLE, Version 4.5'
+@y
+@d banner=='This is TANGLE, Version 4.5 for Linux'
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] add input and output, remove other files, add ref to scan_args,
+% [2] and #include external definition for exit(), etc.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+program TANGLE(@!web_file,@!change_file,@!Pascal_file,@!pool);
+label end_of_TANGLE; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@<Error handling procedures@>@/
+@y
+program TANGLE(@!input,@!output);
+label end_of_TANGLE; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\
+@=#include "tangext.h"@>
+@\@/
+@<Error handling procedures@>@/
+@<Declaration of |scan_args|@>@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] compiler options
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+@y
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Fix others:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {ISO extended Pascal default cases}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8] keep long identifier names
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!max_id_length=12; {long identifiers are chopped to this length, which must
+ not exceed |line_length|}
+@y
+@!max_id_length=50; {long identifiers are chopped to this length, which must
+ not exceed |line_length|}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [11] make ASCII codes take 1 byte (not 4 or more!)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!ASCII_code=0..255; {eight-bit numbers, a subrange of the integers}
+@y
+@!ASCII_code=ByteCard; {eight-bit numbers, a subrange of the integers}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [12] make the compiler recognize a text file as text
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+@y
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+@d text_file==text
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [13] xchr and xord now external
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+@y
+@!xord: external array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: external array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [17] enable maximum character set
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for i:=1 to @'37 do xchr[i]:=' ';
+for i:=@'200 to @'377 do xchr[i]:=' ';
+@y
+for i:=1 to @'37 do xchr[i]:=chr(i);
+for i:=@'200 to @'377 do xchr[i]:=chr(i);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] terminal output: use standard i/o
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@y
+@d term_out==output
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@z
+
+@x
+@<Globals...@>=
+@!term_out:text_file; {the terminal as an output file}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] init terminal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal. Here is one way to do this
+on the \PASCAL\ system that was used in \.{TANGLE}'s initial development:
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
+@y
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal.
+@^system dependencies@>
+
+@<Set init...@>=
+ {Nothing need be done on Linux}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [22] flush terminal buffer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush_stdout {empty the terminal output buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] open input files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens the input files. Since these files were listed
+in the program header, we assume that the \PASCAL\ runtime system has
+already checked that suitable file names have been given; therefore no
+additional error checking needs to be done.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read |web_file| and |change_file|}
+begin reset(web_file); reset(change_file);
+end;
+@y
+@ The following code opens the input files.
+This happens after the initialize procedure has executed.
+That will have called the |scan_args| procedure to set up the global
+variables |web_name| and |change_name| to the appropriate file
+names.
+These globals, and the |scan_args| procedure will be defined at the end
+where they won't disturb the module numbering.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read |web_file| and |change_file|}
+begin
+reset(web_file,web_name); reset(change_file,change_name);
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [26] open output files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens |Pascal_file| and |pool|.
+Since these files were listed in the program header, we assume that the
+\PASCAL\ runtime system has checked that suitable external file names have
+been given.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(Pascal_file); rewrite(pool);
+@y
+@ The following code opens |Pascal_file| and |pool|.
+Use the |scan_args| procedure to fill the global file names,
+according to the names given on the command line.
+@^system dependencies@>
+
+@<Set init...@>=
+scan_args;
+rewrite(Pascal_file,Pascal_name); rewrite(pool,pool_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27] buffer now external
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Globals...@>=@!buffer: array[0..buf_size] of ASCII_code;
+@y
+@<Globals...@>=@!buffer: external array[0..buf_size] of ASCII_code;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [28] faster input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+var final_limit:0..buf_size; {|limit| without trailing blanks}
+begin limit:=0; final_limit:=0;
+if eof(f) then input_ln:=false
+else begin while not eoln(f) do
+ begin buffer[limit]:=xord[f^]; get(f);
+ incr(limit);
+ if buffer[limit-1]<>" " then final_limit:=limit;
+ if limit=buf_size then
+ begin while not eoln(f) do get(f);
+ decr(limit); {keep |buffer[buf_size]| empty}
+ if final_limit>limit then final_limit:=limit;
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ end;
+ read_ln(f); limit:=final_limit; input_ln:=true;
+ end;
+end;
+@y
+With Linux we call an external C procedure, |line_read|.
+That routine fills |buffer| from 0 onwards with the |xord|'ed values
+of the next line, setting |limit| appropriately (ignoring trailing
+blanks).
+It will stop if |limit=buf_size|, and the following will cause an error
+message.
+
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+begin limit:=0;
+if test_eof(f) then input_ln:=false
+else begin line_read(f);
+ if limit=buf_size then
+ begin
+ decr(limit); {keep |buffer[buf_size]| empty}
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ input_ln:=true;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [38] Data Structures: provide for larger |byte_mem| and |tok_mem|
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x Extra capacity:
+@d ww=2 {we multiply the byte capacity by approximately this amount}
+@d zz=3 {we multiply the token capacity by approximately this amount}
+@y
+@d ww=3 {we multiply the byte capacity by approximately this amount}
+@d zz=4 {we multiply the token capacity by approximately this amount}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [89] Work around a bug in GPC version 2.8.1
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+othercases confusion('output')
+@y ('otherwise' syntax not recognized when there's a case inside a case!)
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [105] Accept DIV, div, MOD, and mod
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+ (((out_contrib[1]="D")and(out_contrib[2]="I")and(out_contrib[3]="V")) or@|
+ ((out_contrib[1]="M")and(out_contrib[2]="O")and(out_contrib[3]="D")) ))or@|
+@^uppercase@>
+@y
+ (((out_contrib[1]="D")and(out_contrib[2]="I")and(out_contrib[3]="V")) or@|
+ ((out_contrib[1]="d")and(out_contrib[2]="i")and(out_contrib[3]="v")) or@|
+ ((out_contrib[1]="M")and(out_contrib[2]="O")and(out_contrib[3]="D")) or@|
+ ((out_contrib[1]="m")and(out_contrib[2]="o")and(out_contrib[3]="d")) ))or@|
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [110] lowercase ids
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@^uppercase@>
+ if ((out_buf[out_ptr-3]="D")and(out_buf[out_ptr-2]="I")and
+ (out_buf[out_ptr-1]="V"))or @/
+ ((out_buf[out_ptr-3]="M")and(out_buf[out_ptr-2]="O")and
+ (out_buf[out_ptr-1]="D")) then@/ goto bad_case
+@y
+ if ((out_buf[out_ptr-3]="D")and(out_buf[out_ptr-2]="I")and
+ (out_buf[out_ptr-1]="V"))or @/
+ ((out_buf[out_ptr-3]="d")and(out_buf[out_ptr-2]="i")and
+ (out_buf[out_ptr-1]="v"))or @/
+ ((out_buf[out_ptr-3]="M")and(out_buf[out_ptr-2]="O")and
+ (out_buf[out_ptr-1]="D"))or @/
+ ((out_buf[out_ptr-3]="m")and(out_buf[out_ptr-2]="o")and
+ (out_buf[out_ptr-1]="d")) then@/ goto bad_case
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [114] lowercase operators (`and', `or', etc.)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+and_sign: begin out_contrib[1]:="A"; out_contrib[2]:="N"; out_contrib[3]:="D";
+@^uppercase@>
+ send_out(ident,3);
+ end;
+not_sign: begin out_contrib[1]:="N"; out_contrib[2]:="O"; out_contrib[3]:="T";
+ send_out(ident,3);
+ end;
+set_element_sign: begin out_contrib[1]:="I"; out_contrib[2]:="N";
+ send_out(ident,2);
+ end;
+or_sign: begin out_contrib[1]:="O"; out_contrib[2]:="R"; send_out(ident,2);
+@y
+and_sign: begin out_contrib[1]:="a"; out_contrib[2]:="n"; out_contrib[3]:="d";
+ send_out(ident,3);
+ end;
+not_sign: begin out_contrib[1]:="n"; out_contrib[2]:="o"; out_contrib[3]:="t";
+ send_out(ident,3);
+ end;
+set_element_sign: begin out_contrib[1]:="i"; out_contrib[2]:="n";
+ send_out(ident,2);
+ end;
+or_sign: begin out_contrib[1]:="o"; out_contrib[2]:="r"; send_out(ident,2);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [116] Remove conversion to uppercase
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Single-character identifiers represent themselves, while longer ones
+appear in |byte_mem|. All must be converted to uppercase,
+with underlines removed. Extremely long identifiers must be chopped.
+
+(Some \PASCAL\ compilers work with lowercase letters instead of
+uppercase. If this module of \.{TANGLE} is changed, it's also necessary
+to change from uppercase to lowercase in the modules that are
+listed in the index under ``uppercase''.)
+@^system dependencies@>
+@^uppercase@>
+
+@d up_to(#)==#-24,#-23,#-22,#-21,#-20,#-19,#-18,#-17,#-16,#-15,#-14,
+ #-13,#-12,#-11,#-10,#-9,#-8,#-7,#-6,#-5,#-4,#-3,#-2,#-1,#
+
+@<Cases related to identifiers@>=
+"A",up_to("Z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
+ end;
+"a",up_to("z"): begin out_contrib[1]:=cur_char-@'40; send_out(ident,1);
+ end;
+identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
+ while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
+ begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
+ if out_contrib[k]>="a" then out_contrib[k]:=out_contrib[k]-@'40
+ else if out_contrib[k]="_" then decr(k);
+ end;
+ send_out(ident,k);
+ end;
+@y
+@ Single-character identifiers represent themselves, while longer ones
+appear in |byte_mem|. All must be converted to lowercase,
+with underlines removed. Extremely long identifiers must be chopped.
+@^system dependencies@>
+
+@d up_to(#)==#-24,#-23,#-22,#-21,#-20,#-19,#-18,#-17,#-16,#-15,#-14,
+ #-13,#-12,#-11,#-10,#-9,#-8,#-7,#-6,#-5,#-4,#-3,#-2,#-1,#
+
+@<Cases related to identifiers@>=
+"A",up_to("Z"),
+"a",up_to("z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
+ end;
+identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
+ while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
+ begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
+ if out_contrib[k]="_" then decr(k);
+ end;
+ send_out(ident,k);
+ end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [124] limit is now set by lineread procedure
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!limit:0..buf_size; {the last character position occupied in the buffer}
+@y
+@!limit:external integer; {the last character position occupied in the buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [179] make term_in = input
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+any error stop will set |debug_cycle| to zero.
+@y
+any error stop will set |debug_cycle| to zero.
+
+@d term_in==input
+@z
+
+@x
+@!term_in:text_file; {the user's terminal as an input file}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [180] remove term_in reset
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reset(term_in,'TTY:','/I'); {open |term_in| as the terminal, don't do a |get|}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [182] write newline just before exit; use value of |history|
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+We have defined plenty of procedures, and it is time to put the last
+pieces of the puzzle in place. Here is where \.{TANGLE} starts, and where
+it ends.
+@^system dependencies@>
+@y
+We have defined plenty of procedures, and it is time to put the last
+pieces of the puzzle in place. Here is where \.{TANGLE} starts, and where
+it ends.
+@^system dependencies@>
+
+@d UNIXexit==e@&x@&i@&t
+@z
+
+@x
+@<Print the job |history|@>;
+@y
+@<Print the job |history|@>;
+new_line;
+if (history <> spotless) and (history <> harmless_message) then
+ UNIXexit(1)
+else
+ UNIXexit(0);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [188] system dependent changes--the |scan_args| procedure.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This module should be replaced, if necessary, by changes to the program
+that are necessary to make \.{TANGLE} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous modules preserve the module numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new modules, can be inserted here; then only the index
+itself will get a new module number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+@^system dependencies@>
+
+@ The user calls \.{TANGLE} with arguments on the command line.
+These are either file names or flags (beginning with '-').
+The following globals are for communicating the user's desires to the rest
+of the program. The various |file_name| variables contain strings with
+the full names of those files, as UNIX knows them.
+
+There are no flags that affect \.{TANGLE} at the moment.
+
+@d max_file_name_length==60
+
+@<Globals...@>=
+@!web_name,@!change_name,@!Pascal_name,@!pool_name:
+ UNIX_filename;
+
+@ @<Types...@>=
+UNIX_filename=packed array[1..max_file_name_length] of char;
+
+@ The |scan_args| procedure looks at the command line arguments and sets
+the |file_name| variables accordingly.
+At least one file name must be present: the \.{WEB} file. It may have
+an extension, or it may omit it to get |'.web'| added.
+The \PASCAL\ output file name is formed by replacing the \.{WEB} file
+name extension by |'.p'|.
+Similarly, the pool file name is formed using a |'.pool'| extension.
+
+If there is another file name present among the arguments, it is the
+change file, again either with an extension or without one to get |'.ch'|
+An omitted change file argument means that |'/dev/null'| should be used,
+when no changes are desired.
+
+@<Declaration of |scan_args|@>=
+procedure scan_args;
+var dot_pos,i,a: integer; {indices}
+c: char;
+@!fname: UNIX_filename; {temporary argument holder}
+@!found_web,@!found_change: boolean; {|true| when those file names have
+ been seen}
+begin
+found_web:=false;
+found_change:=false;
+for a:=1 to argc-1 do begin
+ argv(a,fname); {put argument number |a| into |fname|}
+ if fname[1]<>'-' then begin
+ if not found_web then
+ @<Get |web_name|, |Pascal_name|, and
+ | pool_name| variables from |fname|@>
+ else if not found_change then
+ @<Get |change_name| from |fname|@>
+ else @<Print usage error message and quit@>;
+ end
+ else @<Handle flag argument in |fname|@>;
+ end;
+if not found_web then @<Print usage error message and quit@>;
+if not found_change then @<Set up null change file@>;
+end;
+
+@ Use all of |fname| for the |web_name| if there is a |'.'| in it,
+otherwise add |'.web'|.
+The other file names come from adding things after the dot.
+The |argv| procedure will not put more than |max_file_name_length-5|
+characters into |fname|, and this leaves enough room in the |file_name|
+variables to add the extensions.
+
+The end of a file name is marked with a |chr(0)|, the convention assumed by
+the |argv| and |reset| and |rewrite| procedures.
+
+@<Get |web_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>chr(0)) and (i<=max_file_name_length-5) do begin
+ web_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ web_name[dot_pos]:='.';
+ web_name[dot_pos+1]:='w';
+ web_name[dot_pos+2]:='e';
+ web_name[dot_pos+3]:='b';
+ web_name[dot_pos+4]:=chr(0);
+ end
+else web_name[i]:=chr(0);
+for i:=1 to dot_pos do begin
+ c:=web_name[i];
+ Pascal_name[i]:=c;
+ pool_name[i]:=c;
+ end;
+Pascal_name[dot_pos+1]:='p';
+Pascal_name[dot_pos+2]:=chr(0);
+pool_name[dot_pos+1]:='p';
+pool_name[dot_pos+2]:='o';
+pool_name[dot_pos+3]:='o';
+pool_name[dot_pos+4]:='l';
+pool_name[dot_pos+5]:=chr(0);
+found_web:=true;
+end
+
+@ @<Get |change_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>chr(0)) and (i<=max_file_name_length-5)
+do begin
+ change_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ change_name[dot_pos]:='.';
+ change_name[dot_pos+1]:='c';
+ change_name[dot_pos+2]:='h';
+ change_name[dot_pos+3]:=chr(0);
+ end
+else change_name[i]:=chr(0);
+found_change:=true;
+end
+
+@ @<Set up null...@>=
+begin
+ change_name[1]:='/';
+ change_name[2]:='d';
+ change_name[3]:='e';
+ change_name[4]:='v';
+ change_name[5]:='/';
+ change_name[6]:='n';
+ change_name[7]:='u';
+ change_name[8]:='l';
+ change_name[9]:='l';
+ change_name[10]:=chr(0);
+end
+
+@ There are no flags currently used by \.{TANGLE}, but this module can be
+used as a hook to introduce flags.
+
+@<Handle flag...@>=
+begin
+ @<Print usage error message and quit@>;
+end
+
+@ @<Print usage error message and quit@>=
+begin
+print_nl('! Usage: tangle webfile[.web] [changefile[.ch]]');
+error;
+jump_out;
+end
+
+@z
diff --git a/systems/knuth/local/web/tangle.p b/systems/knuth/local/web/tangle.p
new file mode 100644
index 0000000000..48232b026f
--- /dev/null
+++ b/systems/knuth/local/web/tangle.p
@@ -0,0 +1,821 @@
+{2:}{4:}{:4}program TANGLE(input,output);label 9999;const{8:}
+bufsize=100;maxbytes=45000;maxtoks=50000;maxnames=4000;maxtexts=2000;
+hashsize=353;longestname=400;linelength=72;outbufsize=144;stacksize=50;
+maxidlength=50;unambiglength=7;{:8}type{11:}ASCIIcode=ByteCard;{:11}
+{37:}eightbits=0..255;sixteenbits=0..65535;{:37}{39:}
+namepointer=0..maxnames;{:39}{43:}textpointer=0..maxtexts;{:43}{78:}
+outputstate=record endfield:sixteenbits;bytefield:sixteenbits;
+namefield:namepointer;replfield:textpointer;modfield:0..12287;end;{:78}
+{190:}UNIXfilename=packed array[1..60]of char;{:190}var{9:}history:0..3;
+{:9}{13:}xord:external array[char]of ASCIIcode;
+xchr:external array[ASCIIcode]of char;{:13}{23:}webfile:text;
+changefile:text;{:23}{25:}Pascalfile:text;pool:text;{:25}{27:}
+buffer:external array[0..bufsize]of ASCIIcode;{:27}{29:}
+phaseone:boolean;{:29}{38:}
+bytemem:packed array[0..2,0..maxbytes]of ASCIIcode;
+tokmem:packed array[0..3,0..maxtoks]of eightbits;
+bytestart:array[0..maxnames]of sixteenbits;
+tokstart:array[0..maxtexts]of sixteenbits;
+link:array[0..maxnames]of sixteenbits;
+ilk:array[0..maxnames]of sixteenbits;
+equiv:array[0..maxnames]of sixteenbits;
+textlink:array[0..maxtexts]of sixteenbits;{:38}{40:}nameptr:namepointer;
+stringptr:namepointer;byteptr:array[0..2]of 0..maxbytes;
+poolchecksum:integer;{:40}{44:}textptr:textpointer;
+tokptr:array[0..3]of 0..maxtoks;z:0..3;
+{maxtokptr:array[0..3]of 0..maxtoks;}{:44}{50:}idfirst:0..bufsize;
+idloc:0..bufsize;doublechars:0..bufsize;
+hash,chophash:array[0..hashsize]of sixteenbits;
+choppedid:array[0..unambiglength]of ASCIIcode;{:50}{65:}
+modtext:array[0..longestname]of ASCIIcode;{:65}{70:}
+lastunnamed:textpointer;{:70}{79:}curstate:outputstate;
+stack:array[1..stacksize]of outputstate;stackptr:0..stacksize;{:79}{80:}
+zo:0..3;{:80}{82:}bracelevel:eightbits;{:82}{86:}curval:integer;{:86}
+{94:}outbuf:array[0..outbufsize]of ASCIIcode;outptr:0..outbufsize;
+breakptr:0..outbufsize;semiptr:0..outbufsize;{:94}{95:}
+outstate:eightbits;outval,outapp:integer;outsign:ASCIIcode;
+lastsign:-1..+1;{:95}{100:}outcontrib:array[1..linelength]of ASCIIcode;
+{:100}{124:}ii:integer;line:integer;otherline:integer;templine:integer;
+limit:external integer;loc:0..bufsize;inputhasended:boolean;
+changing:boolean;{:124}{126:}changebuffer:array[0..bufsize]of ASCIIcode;
+changelimit:0..bufsize;{:126}{143:}curmodule:namepointer;
+scanninghex:boolean;{:143}{156:}nextcontrol:eightbits;{:156}{164:}
+currepltext:textpointer;{:164}{171:}modulecount:0..12287;{:171}{179:}
+{troubleshooting:boolean;ddt:integer;dd:integer;debugcycle:integer;
+debugskipped:integer;}{:179}{185:}{wo:0..2;}{:185}{189:}
+webname,changename,Pascalname,poolname:UNIXfilename;{:189}
+#include "tangext.h"
+{30:}{procedure debughelp;forward;}{:30}{31:}procedure error;
+var j:0..outbufsize;k,l:0..bufsize;begin if phaseone then{32:}
+begin if changing then write(output,'. (change file ')else write(output,
+'. (');writeln(output,'l.',line:1,')');
+if loc>=limit then l:=limit else l:=loc;
+for k:=1 to l do if buffer[k-1]=9 then write(output,' ')else write(
+output,xchr[buffer[k-1]]);writeln(output);
+for k:=1 to l do write(output,' ');
+for k:=l+1 to limit do write(output,xchr[buffer[k-1]]);
+write(output,' ');end{:32}else{33:}
+begin writeln(output,'. (l.',line:1,')');
+for j:=1 to outptr do write(output,xchr[outbuf[j-1]]);
+write(output,'... ');end{:33};flushstdout;history:=2;
+{debugskipped:=debugcycle;debughelp;}end;{:31}{34:}procedure jumpout;
+begin goto 9999;end;{:34}{191:}procedure scanargs;
+var dotpos,i,a:integer;c:char;fname:UNIXfilename;
+foundweb,foundchange:boolean;begin foundweb:=false;foundchange:=false;
+for a:=1 to argc-1 do begin argv(a,fname);
+if fname[1]<>'-'then begin if not foundweb then{192:}begin dotpos:=-1;
+i:=1;while(fname[i]<>chr(0))and(i<=55)do begin webname[i]:=fname[i];
+if fname[i]='.'then dotpos:=i;i:=i+1;end;
+if dotpos=-1 then begin dotpos:=i;webname[dotpos]:='.';
+webname[dotpos+1]:='w';webname[dotpos+2]:='e';webname[dotpos+3]:='b';
+webname[dotpos+4]:=chr(0);end else webname[i]:=chr(0);
+for i:=1 to dotpos do begin c:=webname[i];Pascalname[i]:=c;
+poolname[i]:=c;end;Pascalname[dotpos+1]:='p';
+Pascalname[dotpos+2]:=chr(0);poolname[dotpos+1]:='p';
+poolname[dotpos+2]:='o';poolname[dotpos+3]:='o';poolname[dotpos+4]:='l';
+poolname[dotpos+5]:=chr(0);foundweb:=true;end{:192}
+else if not foundchange then{193:}begin dotpos:=-1;i:=1;
+while(fname[i]<>chr(0))and(i<=55)do begin changename[i]:=fname[i];
+if fname[i]='.'then dotpos:=i;i:=i+1;end;
+if dotpos=-1 then begin dotpos:=i;changename[dotpos]:='.';
+changename[dotpos+1]:='c';changename[dotpos+2]:='h';
+changename[dotpos+3]:=chr(0);end else changename[i]:=chr(0);
+foundchange:=true;end{:193}else{196:}begin begin writeln(output);
+write(output,'! Usage: tangle webfile[.web] [changefile[.ch]]');end;
+error;jumpout;end{:196};end else{195:}begin{196:}
+begin begin writeln(output);
+write(output,'! Usage: tangle webfile[.web] [changefile[.ch]]');end;
+error;jumpout;end{:196};end{:195};end;if not foundweb then{196:}
+begin begin writeln(output);
+write(output,'! Usage: tangle webfile[.web] [changefile[.ch]]');end;
+error;jumpout;end{:196};if not foundchange then{194:}
+begin changename[1]:='/';changename[2]:='d';changename[3]:='e';
+changename[4]:='v';changename[5]:='/';changename[6]:='n';
+changename[7]:='u';changename[8]:='l';changename[9]:='l';
+changename[10]:=chr(0);end{:194};end;{:191}procedure initialize;var{16:}
+i:0..255;{:16}{41:}wi:0..2;{:41}{45:}zi:0..3;{:45}{51:}h:0..hashsize;
+{:51}begin{10:}history:=0;{:10}{14:}xchr[32]:=' ';xchr[33]:='!';
+xchr[34]:='"';xchr[35]:='#';xchr[36]:='$';xchr[37]:='%';xchr[38]:='&';
+xchr[39]:='''';xchr[40]:='(';xchr[41]:=')';xchr[42]:='*';xchr[43]:='+';
+xchr[44]:=',';xchr[45]:='-';xchr[46]:='.';xchr[47]:='/';xchr[48]:='0';
+xchr[49]:='1';xchr[50]:='2';xchr[51]:='3';xchr[52]:='4';xchr[53]:='5';
+xchr[54]:='6';xchr[55]:='7';xchr[56]:='8';xchr[57]:='9';xchr[58]:=':';
+xchr[59]:=';';xchr[60]:='<';xchr[61]:='=';xchr[62]:='>';xchr[63]:='?';
+xchr[64]:='@';xchr[65]:='A';xchr[66]:='B';xchr[67]:='C';xchr[68]:='D';
+xchr[69]:='E';xchr[70]:='F';xchr[71]:='G';xchr[72]:='H';xchr[73]:='I';
+xchr[74]:='J';xchr[75]:='K';xchr[76]:='L';xchr[77]:='M';xchr[78]:='N';
+xchr[79]:='O';xchr[80]:='P';xchr[81]:='Q';xchr[82]:='R';xchr[83]:='S';
+xchr[84]:='T';xchr[85]:='U';xchr[86]:='V';xchr[87]:='W';xchr[88]:='X';
+xchr[89]:='Y';xchr[90]:='Z';xchr[91]:='[';xchr[92]:='\';xchr[93]:=']';
+xchr[94]:='^';xchr[95]:='_';xchr[96]:='`';xchr[97]:='a';xchr[98]:='b';
+xchr[99]:='c';xchr[100]:='d';xchr[101]:='e';xchr[102]:='f';
+xchr[103]:='g';xchr[104]:='h';xchr[105]:='i';xchr[106]:='j';
+xchr[107]:='k';xchr[108]:='l';xchr[109]:='m';xchr[110]:='n';
+xchr[111]:='o';xchr[112]:='p';xchr[113]:='q';xchr[114]:='r';
+xchr[115]:='s';xchr[116]:='t';xchr[117]:='u';xchr[118]:='v';
+xchr[119]:='w';xchr[120]:='x';xchr[121]:='y';xchr[122]:='z';
+xchr[123]:='{';xchr[124]:='|';xchr[125]:='}';xchr[126]:='~';
+xchr[0]:=' ';xchr[127]:=' ';{:14}{17:}for i:=1 to 31 do xchr[i]:=chr(i);
+for i:=128 to 255 do xchr[i]:=chr(i);{:17}{18:}
+for i:=0 to 255 do xord[chr(i)]:=32;for i:=1 to 255 do xord[xchr[i]]:=i;
+xord[' ']:=32;{:18}{21:}{:21}{26:}scanargs;
+rewrite(Pascalfile,Pascalname);rewrite(pool,poolname);{:26}{42:}
+for wi:=0 to 2 do begin bytestart[wi]:=0;byteptr[wi]:=0;end;
+bytestart[3]:=0;nameptr:=1;stringptr:=256;poolchecksum:=271828;{:42}
+{46:}for zi:=0 to 3 do begin tokstart[zi]:=0;tokptr[zi]:=0;end;
+tokstart[4]:=0;textptr:=1;z:=1 mod 4;{:46}{48:}ilk[0]:=0;equiv[0]:=0;
+{:48}{52:}for h:=0 to hashsize-1 do begin hash[h]:=0;chophash[h]:=0;end;
+{:52}{71:}lastunnamed:=0;textlink[0]:=0;{:71}{144:}scanninghex:=false;
+{:144}{152:}modtext[0]:=32;{:152}{180:}{troubleshooting:=true;
+debugcycle:=1;debugskipped:=0;troubleshooting:=false;debugcycle:=99999;}
+{:180}end;{:2}{24:}procedure openinput;begin reset(webfile,webname);
+reset(changefile,changename);end;{:24}{28:}
+function inputln(var f:text):boolean;begin limit:=0;
+if testeof(f)then inputln:=false else begin lineread(f);
+if limit=bufsize then begin limit:=limit-1;begin writeln(output);
+write(output,'! Input line too long');end;loc:=0;error;end;
+inputln:=true;end;end;{:28}{49:}procedure printid(p:namepointer);
+var k:0..maxbytes;w:0..2;
+begin if p>=nameptr then write(output,'IMPOSSIBLE')else begin w:=p mod 3
+;
+for k:=bytestart[p]to bytestart[p+3]-1 do write(output,xchr[bytemem[w,k]
+]);end;end;{:49}{53:}function idlookup(t:eightbits):namepointer;
+label 31,32;var c:eightbits;i:0..bufsize;h:0..hashsize;k:0..maxbytes;
+w:0..2;l:0..bufsize;p,q:namepointer;s:0..unambiglength;
+begin l:=idloc-idfirst;{54:}h:=buffer[idfirst];i:=idfirst+1;
+while i<idloc do begin h:=(h+h+buffer[i])mod hashsize;i:=i+1;end{:54};
+{55:}p:=hash[h];
+while p<>0 do begin if bytestart[p+3]-bytestart[p]=l then{56:}
+begin i:=idfirst;k:=bytestart[p];w:=p mod 3;
+while(i<idloc)and(buffer[i]=bytemem[w,k])do begin i:=i+1;k:=k+1;end;
+if i=idloc then goto 31;end{:56};p:=link[p];end;p:=nameptr;
+link[p]:=hash[h];hash[h]:=p;31:{:55};if(p=nameptr)or(t<>0)then{57:}
+begin if((p<>nameptr)and(t<>0)and(ilk[p]=0))or((p=nameptr)and(t=0)and(
+buffer[idfirst]<>34))then{58:}begin i:=idfirst;s:=0;h:=0;
+while(i<idloc)and(s<unambiglength)do begin if buffer[i]<>95 then begin
+if buffer[i]>=97 then choppedid[s]:=buffer[i]-32 else choppedid[s]:=
+buffer[i];h:=(h+h+choppedid[s])mod hashsize;s:=s+1;end;i:=i+1;end;
+choppedid[s]:=0;end{:58};if p<>nameptr then{59:}
+begin if ilk[p]=0 then begin if t=1 then begin writeln(output);
+write(output,'! This identifier has already appeared');error;end;{60:}
+q:=chophash[h];
+if q=p then chophash[h]:=equiv[p]else begin while equiv[q]<>p do q:=
+equiv[q];equiv[q]:=equiv[p];end{:60};end else begin writeln(output);
+write(output,'! This identifier was defined before');error;end;
+ilk[p]:=t;end{:59}else{61:}
+begin if(t=0)and(buffer[idfirst]<>34)then{62:}begin q:=chophash[h];
+while q<>0 do begin{63:}begin k:=bytestart[q];s:=0;w:=q mod 3;
+while(k<bytestart[q+3])and(s<unambiglength)do begin c:=bytemem[w,k];
+if c<>95 then begin if c>=97 then c:=c-32;
+if choppedid[s]<>c then goto 32;s:=s+1;end;k:=k+1;end;
+if(k=bytestart[q+3])and(choppedid[s]<>0)then goto 32;
+begin writeln(output);write(output,'! Identifier conflict with ');end;
+for k:=bytestart[q]to bytestart[q+3]-1 do write(output,xchr[bytemem[w,k]
+]);error;q:=0;32:end{:63};q:=equiv[q];end;equiv[p]:=chophash[h];
+chophash[h]:=p;end{:62};w:=nameptr mod 3;k:=byteptr[w];
+if k+l>maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;i:=idfirst;while i<idloc do begin bytemem[w,k]:=buffer[i];
+k:=k+1;i:=i+1;end;byteptr[w]:=k;bytestart[nameptr+3]:=k;
+nameptr:=nameptr+1;if buffer[idfirst]<>34 then ilk[p]:=t else{64:}
+begin ilk[p]:=1;
+if l-doublechars=2 then equiv[p]:=buffer[idfirst+1]+32768 else begin
+equiv[p]:=stringptr+32768;l:=l-doublechars-1;
+if l>99 then begin writeln(output);
+write(output,'! Preprocessed string is too long');error;end;
+stringptr:=stringptr+1;write(pool,xchr[48+l div 10],xchr[48+l mod 10]);
+poolchecksum:=poolchecksum+poolchecksum+l;
+while poolchecksum>536870839 do poolchecksum:=poolchecksum-536870839;
+i:=idfirst+1;while i<idloc do begin write(pool,xchr[buffer[i]]);
+poolchecksum:=poolchecksum+poolchecksum+buffer[i];
+while poolchecksum>536870839 do poolchecksum:=poolchecksum-536870839;
+if(buffer[i]=34)or(buffer[i]=64)then i:=i+2 else i:=i+1;end;
+writeln(pool);end;end{:64};end{:61};end{:57};idlookup:=p;end;{:53}{66:}
+function modlookup(l:sixteenbits):namepointer;label 31;var c:0..4;
+j:0..longestname;k:0..maxbytes;w:0..2;p:namepointer;q:namepointer;
+begin c:=2;q:=0;p:=ilk[0];while p<>0 do begin{68:}begin k:=bytestart[p];
+w:=p mod 3;c:=1;j:=1;
+while(k<bytestart[p+3])and(j<=l)and(modtext[j]=bytemem[w,k])do begin k:=
+k+1;j:=j+1;end;
+if k=bytestart[p+3]then if j>l then c:=1 else c:=4 else if j>l then c:=3
+else if modtext[j]<bytemem[w,k]then c:=0 else c:=2;end{:68};q:=p;
+if c=0 then p:=link[q]else if c=2 then p:=ilk[q]else goto 31;end;{67:}
+w:=nameptr mod 3;k:=byteptr[w];
+if k+l>maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;p:=nameptr;if c=0 then link[q]:=p else ilk[q]:=p;link[p]:=0;
+ilk[p]:=0;c:=1;equiv[p]:=0;
+for j:=1 to l do bytemem[w,k+j-1]:=modtext[j];byteptr[w]:=k+l;
+bytestart[nameptr+3]:=k+l;nameptr:=nameptr+1;{:67};
+31:if c<>1 then begin begin writeln(output);
+write(output,'! Incompatible section names');error;end;p:=0;end;
+modlookup:=p;end;{:66}{69:}
+function prefixlookup(l:sixteenbits):namepointer;var c:0..4;
+count:0..maxnames;j:0..longestname;k:0..maxbytes;w:0..2;p:namepointer;
+q:namepointer;r:namepointer;begin q:=0;p:=ilk[0];count:=0;r:=0;
+while p<>0 do begin{68:}begin k:=bytestart[p];w:=p mod 3;c:=1;j:=1;
+while(k<bytestart[p+3])and(j<=l)and(modtext[j]=bytemem[w,k])do begin k:=
+k+1;j:=j+1;end;
+if k=bytestart[p+3]then if j>l then c:=1 else c:=4 else if j>l then c:=3
+else if modtext[j]<bytemem[w,k]then c:=0 else c:=2;end{:68};
+if c=0 then p:=link[p]else if c=2 then p:=ilk[p]else begin r:=p;
+count:=count+1;q:=ilk[p];p:=link[p];end;if p=0 then begin p:=q;q:=0;end;
+end;if count<>1 then if count=0 then begin writeln(output);
+write(output,'! Name does not match');error;
+end else begin writeln(output);write(output,'! Ambiguous prefix');error;
+end;prefixlookup:=r;end;{:69}{73:}
+procedure storetwobytes(x:sixteenbits);
+begin if tokptr[z]+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=x div 256;
+tokmem[z,tokptr[z]+1]:=x mod 256;tokptr[z]:=tokptr[z]+2;end;{:73}{74:}
+{procedure printrepl(p:textpointer);var k:0..maxtoks;a:sixteenbits;
+zp:0..3;
+begin if p>=textptr then write(output,'BAD')else begin k:=tokstart[p];
+zp:=p mod 4;while k<tokstart[p+4]do begin a:=tokmem[zp,k];
+if a>=128 then[75:]begin k:=k+1;
+if a<168 then begin a:=(a-128)*256+tokmem[zp,k];printid(a);
+if bytemem[a mod 3,bytestart[a]]=34 then write(output,'"')else write(
+output,' ');end else if a<208 then begin write(output,'@<');
+printid((a-168)*256+tokmem[zp,k]);write(output,'@>');
+end else begin a:=(a-208)*256+tokmem[zp,k];
+write(output,'@',xchr[123],a:1,'@',xchr[125]);end;
+end[:75]else[76:]case a of 9:write(output,'@',xchr[123]);
+10:write(output,'@',xchr[125]);12:write(output,'@''');
+13:write(output,'@"');125:write(output,'@$');0:write(output,'#');
+64:write(output,'@@');2:write(output,'@=');3:write(output,'@\');
+otherwise write(output,xchr[a])end[:76];k:=k+1;end;end;end;}{:74}{84:}
+procedure pushlevel(p:namepointer);
+begin if stackptr=stacksize then begin writeln(output);
+write(output,'! Sorry, ','stack',' capacity exceeded');error;history:=3;
+jumpout;end else begin stack[stackptr]:=curstate;stackptr:=stackptr+1;
+curstate.namefield:=p;curstate.replfield:=equiv[p];
+zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];curstate.modfield:=0;
+end;end;{:84}{85:}procedure poplevel;label 10;
+begin if textlink[curstate.replfield]=0 then begin if ilk[curstate.
+namefield]=3 then{91:}begin nameptr:=nameptr-1;textptr:=textptr-1;
+z:=textptr mod 4;{if tokptr[z]>maxtokptr[z]then maxtokptr[z]:=tokptr[z];
+}tokptr[z]:=tokstart[textptr];
+{byteptr[nameptr mod 3]:=byteptr[nameptr mod 3]-1;}end{:91};
+end else if textlink[curstate.replfield]<maxtexts then begin curstate.
+replfield:=textlink[curstate.replfield];zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];goto 10;end;
+stackptr:=stackptr-1;if stackptr>0 then begin curstate:=stack[stackptr];
+zo:=curstate.replfield mod 4;end;10:end;{:85}{87:}
+function getoutput:sixteenbits;label 20,30,31;var a:sixteenbits;
+b:eightbits;bal:sixteenbits;k:0..maxbytes;w:0..2;
+begin 20:if stackptr=0 then begin a:=0;goto 31;end;
+if curstate.bytefield=curstate.endfield then begin curval:=-curstate.
+modfield;poplevel;if curval=0 then goto 20;a:=129;goto 31;end;
+a:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;if a<128 then if a=0 then{92:}
+begin pushlevel(nameptr-1);goto 20;end{:92}else goto 31;
+a:=(a-128)*256+tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;if a<10240 then{89:}
+begin case ilk[a]of 0:begin curval:=a;a:=130;end;
+1:begin curval:=equiv[a]-32768;a:=128;end;2:begin pushlevel(a);goto 20;
+end;3:begin{90:}
+while(curstate.bytefield=curstate.endfield)and(stackptr>0)do poplevel;
+if(stackptr=0)or(tokmem[zo,curstate.bytefield]<>40)then begin begin
+writeln(output);write(output,'! No parameter given for ');end;
+printid(a);error;goto 20;end;{93:}bal:=1;
+curstate.bytefield:=curstate.bytefield+1;
+while true do begin b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;
+if b=0 then storetwobytes(nameptr+32767)else begin if b>=128 then begin
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;
+b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;
+end else case b of 40:bal:=bal+1;41:begin bal:=bal-1;
+if bal=0 then goto 30;end;
+39:repeat begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;
+b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;until b=39;otherwise end;
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;end;end;
+30:{:93};equiv[nameptr]:=textptr;ilk[nameptr]:=2;w:=nameptr mod 3;
+k:=byteptr[w];{if k=maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;bytemem[w,k]:=35;k:=k+1;byteptr[w]:=k;}
+if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;bytestart[nameptr+3]:=k;nameptr:=nameptr+1;
+if textptr>maxtexts-4 then begin writeln(output);
+write(output,'! Sorry, ','text',' capacity exceeded');error;history:=3;
+jumpout;end;textlink[textptr]:=0;tokstart[textptr+4]:=tokptr[z];
+textptr:=textptr+1;z:=textptr mod 4{:90};pushlevel(a);goto 20;end;end;
+goto 31;end{:89};if a<20480 then{88:}begin a:=a-10240;
+if equiv[a]<>0 then pushlevel(a)else if a<>0 then begin begin writeln(
+output);write(output,'! Not present: <');end;printid(a);
+write(output,'>');error;end;goto 20;end{:88};curval:=a-20480;a:=129;
+curstate.modfield:=curval;31:{if troubleshooting then debughelp;}
+getoutput:=a;end;{:87}{97:}procedure flushbuffer;var k:0..outbufsize;
+b:0..outbufsize;begin b:=breakptr;
+if(semiptr<>0)and(outptr-semiptr<=linelength)then breakptr:=semiptr;
+for k:=1 to breakptr do write(Pascalfile,xchr[outbuf[k-1]]);
+writeln(Pascalfile);line:=line+1;
+if line mod 100=0 then begin write(output,'.');
+if line mod 500=0 then write(output,line:1);flushstdout;end;
+if breakptr<outptr then begin if outbuf[breakptr]=32 then begin breakptr
+:=breakptr+1;if breakptr>b then b:=breakptr;end;
+for k:=breakptr to outptr-1 do outbuf[k-breakptr]:=outbuf[k];end;
+outptr:=outptr-breakptr;breakptr:=b-breakptr;semiptr:=0;
+if outptr>linelength then begin begin writeln(output);
+write(output,'! Long line must be truncated');error;end;
+outptr:=linelength;end;end;{:97}{99:}procedure appval(v:integer);
+var k:0..outbufsize;begin k:=outbufsize;repeat outbuf[k]:=v mod 10;
+v:=v div 10;k:=k-1;until v=0;repeat k:=k+1;
+begin outbuf[outptr]:=outbuf[k]+48;outptr:=outptr+1;end;
+until k=outbufsize;end;{:99}{101:}procedure sendout(t:eightbits;
+v:sixteenbits);label 20;var k:0..linelength;begin{102:}
+20:case outstate of 1:if t<>3 then begin breakptr:=outptr;
+if t=2 then begin outbuf[outptr]:=32;outptr:=outptr+1;end;end;
+2:begin begin outbuf[outptr]:=44-outapp;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;breakptr:=outptr;end;
+3,4:begin{103:}
+if(outval<0)or((outval=0)and(lastsign<0))then begin outbuf[outptr]:=45;
+outptr:=outptr+1;
+end else if outsign>0 then begin outbuf[outptr]:=outsign;
+outptr:=outptr+1;end;appval(abs(outval));
+if outptr>linelength then flushbuffer;{:103};outstate:=outstate-2;
+goto 20;end;5:{104:}begin if(t=3)or({105:}
+((t=2)and(v=3)and(((outcontrib[1]=68)and(outcontrib[2]=73)and(outcontrib
+[3]=86))or((outcontrib[1]=100)and(outcontrib[2]=105)and(outcontrib[3]=
+118))or((outcontrib[1]=77)and(outcontrib[2]=79)and(outcontrib[3]=68))or(
+(outcontrib[1]=109)and(outcontrib[2]=111)and(outcontrib[3]=100))))or((t=
+0)and((v=42)or(v=47))){:105})then begin{103:}
+if(outval<0)or((outval=0)and(lastsign<0))then begin outbuf[outptr]:=45;
+outptr:=outptr+1;
+end else if outsign>0 then begin outbuf[outptr]:=outsign;
+outptr:=outptr+1;end;appval(abs(outval));
+if outptr>linelength then flushbuffer;{:103};outsign:=43;outval:=outapp;
+end else outval:=outval+outapp;outstate:=3;goto 20;end{:104};
+0:if t<>3 then breakptr:=outptr;otherwise end{:102};
+if t<>0 then for k:=1 to v do begin outbuf[outptr]:=outcontrib[k];
+outptr:=outptr+1;end else begin outbuf[outptr]:=v;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;
+if(t=0)and((v=59)or(v=125))then begin semiptr:=outptr;breakptr:=outptr;
+end;if t>=2 then outstate:=1 else outstate:=0 end;{:101}{106:}
+procedure sendsign(v:integer);
+begin case outstate of 2,4:outapp:=outapp*v;3:begin outapp:=v;
+outstate:=4;end;5:begin outval:=outval+outapp;outapp:=v;outstate:=4;end;
+otherwise begin breakptr:=outptr;outapp:=v;outstate:=2;end end;
+lastsign:=outapp;end;{:106}{107:}procedure sendval(v:integer);
+label 666,10;begin case outstate of 1:begin{110:}
+if(outptr=breakptr+3)or((outptr=breakptr+4)and(outbuf[breakptr]=32))then
+if((outbuf[outptr-3]=68)and(outbuf[outptr-2]=73)and(outbuf[outptr-1]=86)
+)or((outbuf[outptr-3]=100)and(outbuf[outptr-2]=105)and(outbuf[outptr-1]=
+118))or((outbuf[outptr-3]=77)and(outbuf[outptr-2]=79)and(outbuf[outptr-1
+]=68))or((outbuf[outptr-3]=109)and(outbuf[outptr-2]=111)and(outbuf[
+outptr-1]=100))then goto 666{:110};outsign:=32;outstate:=3;outval:=v;
+breakptr:=outptr;lastsign:=+1;end;0:begin{109:}
+if(outptr=breakptr+1)and((outbuf[breakptr]=42)or(outbuf[breakptr]=47))
+then goto 666{:109};outsign:=0;outstate:=3;outval:=v;breakptr:=outptr;
+lastsign:=+1;end;{108:}2:begin outsign:=43;outstate:=3;outval:=outapp*v;
+end;3:begin outstate:=5;outapp:=v;begin writeln(output);
+write(output,'! Two numbers occurred without a sign between them');
+error;end;end;4:begin outstate:=5;outapp:=outapp*v;end;
+5:begin outval:=outval+outapp;outapp:=v;begin writeln(output);
+write(output,'! Two numbers occurred without a sign between them');
+error;end;end;{:108}otherwise goto 666 end;goto 10;666:{111:}
+if v>=0 then begin if outstate=1 then begin breakptr:=outptr;
+begin outbuf[outptr]:=32;outptr:=outptr+1;end;end;appval(v);
+if outptr>linelength then flushbuffer;outstate:=1;
+end else begin begin outbuf[outptr]:=40;outptr:=outptr+1;end;
+begin outbuf[outptr]:=45;outptr:=outptr+1;end;appval(-v);
+begin outbuf[outptr]:=41;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;outstate:=0;end{:111};10:end;
+{:107}{113:}procedure sendtheoutput;label 2,21,22;var curchar:eightbits;
+k:0..linelength;j:0..maxbytes;w:0..2;n:integer;
+begin while stackptr>0 do begin curchar:=getoutput;
+21:case curchar of 0:;{116:}
+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,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:begin outcontrib[1]:=curchar;
+sendout(2,1);end;130:begin k:=0;j:=bytestart[curval];w:=curval mod 3;
+while(k<maxidlength)and(j<bytestart[curval+3])do begin k:=k+1;
+outcontrib[k]:=bytemem[w,j];j:=j+1;if outcontrib[k]=95 then k:=k-1;end;
+sendout(2,k);end;{:116}{119:}48,49,50,51,52,53,54,55,56,57:begin n:=0;
+repeat curchar:=curchar-48;if n>=214748364 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=10*n+curchar;
+curchar:=getoutput;until(curchar>57)or(curchar<48);sendval(n);k:=0;
+if curchar=101 then curchar:=69;if curchar=69 then goto 2 else goto 21;
+end;125:sendval(poolchecksum);12:begin n:=0;curchar:=48;
+repeat curchar:=curchar-48;if n>=268435456 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=8*n+curchar;
+curchar:=getoutput;until(curchar>55)or(curchar<48);sendval(n);goto 21;
+end;13:begin n:=0;curchar:=48;
+repeat if curchar>=65 then curchar:=curchar-55 else curchar:=curchar-48;
+if n>=134217728 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=16*n+curchar;
+curchar:=getoutput;
+until(curchar>70)or(curchar<48)or((curchar>57)and(curchar<65));
+sendval(n);goto 21;end;128:sendval(curval);46:begin k:=1;
+outcontrib[1]:=46;curchar:=getoutput;
+if curchar=46 then begin outcontrib[2]:=46;sendout(1,2);
+end else if(curchar>=48)and(curchar<=57)then goto 2 else begin sendout(0
+,46);goto 21;end;end;{:119}43,45:sendsign(44-curchar);{114:}
+4:begin outcontrib[1]:=97;outcontrib[2]:=110;outcontrib[3]:=100;
+sendout(2,3);end;5:begin outcontrib[1]:=110;outcontrib[2]:=111;
+outcontrib[3]:=116;sendout(2,3);end;6:begin outcontrib[1]:=105;
+outcontrib[2]:=110;sendout(2,2);end;31:begin outcontrib[1]:=111;
+outcontrib[2]:=114;sendout(2,2);end;24:begin outcontrib[1]:=58;
+outcontrib[2]:=61;sendout(1,2);end;26:begin outcontrib[1]:=60;
+outcontrib[2]:=62;sendout(1,2);end;28:begin outcontrib[1]:=60;
+outcontrib[2]:=61;sendout(1,2);end;29:begin outcontrib[1]:=62;
+outcontrib[2]:=61;sendout(1,2);end;30:begin outcontrib[1]:=61;
+outcontrib[2]:=61;sendout(1,2);end;32:begin outcontrib[1]:=46;
+outcontrib[2]:=46;sendout(1,2);end;{:114}39:{117:}begin k:=1;
+outcontrib[1]:=39;repeat if k<linelength then k:=k+1;
+outcontrib[k]:=getoutput;until(outcontrib[k]=39)or(stackptr=0);
+if k=linelength then begin writeln(output);
+write(output,'! String too long');error;end;sendout(1,k);
+curchar:=getoutput;if curchar=39 then outstate:=6;goto 21;end{:117};
+{115:}
+33,34,35,36,37,38,40,41,42,44,47,58,59,60,61,62,63,64,91,92,93,94,95,96,
+123,124{:115}:sendout(0,curchar);{121:}
+9:begin if bracelevel=0 then sendout(0,123)else sendout(0,91);
+bracelevel:=bracelevel+1;end;
+10:if bracelevel>0 then begin bracelevel:=bracelevel-1;
+if bracelevel=0 then sendout(0,125)else sendout(0,93);
+end else begin writeln(output);write(output,'! Extra @}');error;end;
+129:begin k:=2;
+if bracelevel=0 then outcontrib[1]:=123 else outcontrib[1]:=91;
+if curval<0 then begin outcontrib[k]:=58;curval:=-curval;k:=k+1;end;
+n:=10;while curval>=n do n:=10*n;repeat n:=n div 10;
+outcontrib[k]:=48+(curval div n);curval:=curval mod n;k:=k+1;until n=1;
+if outcontrib[2]<>58 then begin outcontrib[k]:=58;k:=k+1;end;
+if bracelevel=0 then outcontrib[k]:=125 else outcontrib[k]:=93;
+sendout(1,k);end;{:121}127:begin sendout(3,0);outstate:=6;end;2:{118:}
+begin k:=0;repeat if k<linelength then k:=k+1;outcontrib[k]:=getoutput;
+until(outcontrib[k]=2)or(stackptr=0);
+if k=linelength then begin writeln(output);
+write(output,'! Verbatim string too long');error;end;sendout(1,k-1);
+end{:118};3:{122:}begin sendout(1,0);
+while outptr>0 do begin if outptr<=linelength then breakptr:=outptr;
+flushbuffer;end;outstate:=0;end{:122};otherwise begin writeln(output);
+write(output,'! Can''t output ASCII code ',curchar:1);error;end end;
+goto 22;2:{120:}repeat if k<linelength then k:=k+1;
+outcontrib[k]:=curchar;curchar:=getoutput;
+if(outcontrib[k]=69)and((curchar=43)or(curchar=45))then begin if k<
+linelength then k:=k+1;outcontrib[k]:=curchar;curchar:=getoutput;
+end else if curchar=101 then curchar:=69;
+until(curchar<>69)and((curchar<48)or(curchar>57));
+if k=linelength then begin writeln(output);
+write(output,'! Fraction too long');error;end;sendout(3,k);goto 21{:120}
+;22:end;end;{:113}{127:}function linesdontmatch:boolean;label 10;
+var k:0..bufsize;begin linesdontmatch:=true;
+if changelimit<>limit then goto 10;
+if limit>0 then for k:=0 to limit-1 do if changebuffer[k]<>buffer[k]then
+goto 10;linesdontmatch:=false;10:end;{:127}{128:}
+procedure primethechangebuffer;label 22,30,10;var k:0..bufsize;
+begin changelimit:=0;{129:}while true do begin line:=line+1;
+if not inputln(changefile)then goto 10;if limit<2 then goto 22;
+if buffer[0]<>64 then goto 22;
+if(buffer[1]>=88)and(buffer[1]<=90)then buffer[1]:=buffer[1]+32;
+if buffer[1]=120 then goto 30;
+if(buffer[1]=121)or(buffer[1]=122)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @x?');error;
+end;end;22:end;30:{:129};{130:}repeat line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended after @x');error;end;goto 10;end;
+until limit>0;{:130};{131:}begin changelimit:=limit;
+if limit>0 then for k:=0 to limit-1 do changebuffer[k]:=buffer[k];
+end{:131};10:end;{:128}{132:}procedure checkchange;label 10;
+var n:integer;k:0..bufsize;begin if linesdontmatch then goto 10;n:=0;
+while true do begin changing:=not changing;templine:=otherline;
+otherline:=line;line:=templine;line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended before @y');error;end;changelimit:=0;
+changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;goto 10;end;{133:}
+if limit>1 then if buffer[0]=64 then begin if(buffer[1]>=88)and(buffer[1
+]<=90)then buffer[1]:=buffer[1]+32;
+if(buffer[1]=120)or(buffer[1]=122)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @y?');error;
+end;end else if buffer[1]=121 then begin if n>0 then begin loc:=2;
+begin writeln(output);
+write(output,'! Hmm... ',n:1,' of the preceding lines failed to match');
+error;end;end;goto 10;end;end{:133};{131:}begin changelimit:=limit;
+if limit>0 then for k:=0 to limit-1 do changebuffer[k]:=buffer[k];
+end{:131};changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;line:=line+1;
+if not inputln(webfile)then begin begin writeln(output);
+write(output,'! WEB file ended during a change');error;end;
+inputhasended:=true;goto 10;end;if linesdontmatch then n:=n+1;end;
+10:end;{:132}{135:}procedure getline;label 20;
+begin 20:if changing then{137:}begin line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended without @z');error;end;buffer[0]:=64;
+buffer[1]:=122;limit:=2;end;
+if limit>1 then if buffer[0]=64 then begin if(buffer[1]>=88)and(buffer[1
+]<=90)then buffer[1]:=buffer[1]+32;
+if(buffer[1]=120)or(buffer[1]=121)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @z?');error;
+end;end else if buffer[1]=122 then begin primethechangebuffer;
+changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;end;end;end{:137};if not changing then begin{136:}
+begin line:=line+1;
+if not inputln(webfile)then inputhasended:=true else if limit=
+changelimit then if buffer[0]=changebuffer[0]then if changelimit>0 then
+checkchange;end{:136};if changing then goto 20;end;loc:=0;
+buffer[limit]:=32;end;{:135}{139:}
+function controlcode(c:ASCIIcode):eightbits;
+begin case c of 64:controlcode:=64;39:controlcode:=12;
+34:controlcode:=13;36:controlcode:=125;32,9:controlcode:=136;
+42:begin write(output,'*',modulecount+1:1);flushstdout;controlcode:=136;
+end;68,100:controlcode:=133;70,102:controlcode:=132;123:controlcode:=9;
+125:controlcode:=10;80,112:controlcode:=134;
+84,116,94,46,58:controlcode:=131;38:controlcode:=127;
+60:controlcode:=135;61:controlcode:=2;92:controlcode:=3;
+otherwise controlcode:=0 end;end;{:139}{140:}
+function skipahead:eightbits;label 30;var c:eightbits;
+begin while true do begin if loc>limit then begin getline;
+if inputhasended then begin c:=136;goto 30;end;end;buffer[limit+1]:=64;
+while buffer[loc]<>64 do loc:=loc+1;if loc<=limit then begin loc:=loc+2;
+c:=controlcode(buffer[loc-1]);if(c<>0)or(buffer[loc-1]=62)then goto 30;
+end;end;30:skipahead:=c;end;{:140}{141:}procedure skipcomment;label 10;
+var bal:eightbits;c:ASCIIcode;begin bal:=0;
+while true do begin if loc>limit then begin getline;
+if inputhasended then begin begin writeln(output);
+write(output,'! Input ended in mid-comment');error;end;goto 10;end;end;
+c:=buffer[loc];loc:=loc+1;{142:}if c=64 then begin c:=buffer[loc];
+if(c<>32)and(c<>9)and(c<>42)and(c<>122)and(c<>90)then loc:=loc+1 else
+begin begin writeln(output);
+write(output,'! Section ended in mid-comment');error;end;loc:=loc-1;
+goto 10;
+end end else if(c=92)and(buffer[loc]<>64)then loc:=loc+1 else if c=123
+then bal:=bal+1 else if c=125 then begin if bal=0 then goto 10;
+bal:=bal-1;end{:142};end;10:end;{:141}{145:}function getnext:eightbits;
+label 20,30,31;var c:eightbits;d:eightbits;j,k:0..longestname;
+begin 20:if loc>limit then begin getline;
+if inputhasended then begin c:=136;goto 31;end;end;c:=buffer[loc];
+loc:=loc+1;if scanninghex then{146:}
+if((c>=48)and(c<=57))or((c>=65)and(c<=70))then goto 31 else scanninghex
+:=false{:146};
+case c of 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,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:{148:}
+begin if((c=101)or(c=69))and(loc>1)then if(buffer[loc-2]<=57)and(buffer[
+loc-2]>=48)then c:=0;if c<>0 then begin loc:=loc-1;idfirst:=loc;
+repeat loc:=loc+1;d:=buffer[loc];
+until((d<48)or((d>57)and(d<65))or((d>90)and(d<97))or(d>122))and(d<>95);
+if loc>idfirst+1 then begin c:=130;idloc:=loc;end;end else c:=69;
+end{:148};34:{149:}begin doublechars:=0;idfirst:=loc-1;
+repeat d:=buffer[loc];loc:=loc+1;
+if(d=34)or(d=64)then if buffer[loc]=d then begin loc:=loc+1;d:=0;
+doublechars:=doublechars+1;
+end else begin if d=64 then begin writeln(output);
+write(output,'! Double @ sign missing');error;
+end end else if loc>limit then begin begin writeln(output);
+write(output,'! String constant didn''t end');error;end;d:=34;end;
+until d=34;idloc:=loc-1;c:=130;end{:149};64:{150:}
+begin c:=controlcode(buffer[loc]);loc:=loc+1;
+if c=0 then goto 20 else if c=13 then scanninghex:=true else if c=135
+then{151:}begin{153:}k:=0;
+while true do begin if loc>limit then begin getline;
+if inputhasended then begin begin writeln(output);
+write(output,'! Input ended in section name');error;end;goto 30;end;end;
+d:=buffer[loc];{154:}if d=64 then begin d:=buffer[loc+1];
+if d=62 then begin loc:=loc+2;goto 30;end;
+if(d=32)or(d=9)or(d=42)then begin begin writeln(output);
+write(output,'! Section name didn''t end');error;end;goto 30;end;k:=k+1;
+modtext[k]:=64;loc:=loc+1;end{:154};loc:=loc+1;
+if k<longestname-1 then k:=k+1;if(d=32)or(d=9)then begin d:=32;
+if modtext[k-1]=32 then k:=k-1;end;modtext[k]:=d;end;30:{155:}
+if k>=longestname-2 then begin begin writeln(output);
+write(output,'! Section name too long: ');end;
+for j:=1 to 25 do write(output,xchr[modtext[j]]);write(output,'...');
+if history=0 then history:=1;end{:155};
+if(modtext[k]=32)and(k>0)then k:=k-1;{:153};
+if k>3 then begin if(modtext[k]=46)and(modtext[k-1]=46)and(modtext[k-2]=
+46)then curmodule:=prefixlookup(k-3)else curmodule:=modlookup(k);
+end else curmodule:=modlookup(k);end{:151}
+else if c=131 then begin repeat c:=skipahead;until c<>64;
+if buffer[loc-1]<>62 then begin writeln(output);
+write(output,'! Improper @ within control text');error;end;goto 20;end;
+end{:150};{147:}
+46:if buffer[loc]=46 then begin if loc<=limit then begin c:=32;
+loc:=loc+1;end;
+end else if buffer[loc]=41 then begin if loc<=limit then begin c:=93;
+loc:=loc+1;end;end;
+58:if buffer[loc]=61 then begin if loc<=limit then begin c:=24;
+loc:=loc+1;end;end;
+61:if buffer[loc]=61 then begin if loc<=limit then begin c:=30;
+loc:=loc+1;end;end;
+62:if buffer[loc]=61 then begin if loc<=limit then begin c:=29;
+loc:=loc+1;end;end;
+60:if buffer[loc]=61 then begin if loc<=limit then begin c:=28;
+loc:=loc+1;end;
+end else if buffer[loc]=62 then begin if loc<=limit then begin c:=26;
+loc:=loc+1;end;end;
+40:if buffer[loc]=42 then begin if loc<=limit then begin c:=9;
+loc:=loc+1;end;
+end else if buffer[loc]=46 then begin if loc<=limit then begin c:=91;
+loc:=loc+1;end;end;
+42:if buffer[loc]=41 then begin if loc<=limit then begin c:=10;
+loc:=loc+1;end;end;{:147}32,9:goto 20;123:begin skipcomment;goto 20;end;
+125:begin begin writeln(output);write(output,'! Extra }');error;end;
+goto 20;end;otherwise if c>=128 then goto 20 else end;
+31:{if troubleshooting then debughelp;}getnext:=c;end;{:145}{157:}
+procedure scannumeric(p:namepointer);label 21,30;
+var accumulator:integer;nextsign:-1..+1;q:namepointer;val:integer;
+begin{158:}accumulator:=0;nextsign:=+1;
+while true do begin nextcontrol:=getnext;
+21:case nextcontrol of 48,49,50,51,52,53,54,55,56,57:begin{160:}val:=0;
+repeat val:=10*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>57)or(nextcontrol<48){:160};
+begin accumulator:=accumulator+nextsign*(val);nextsign:=+1;end;goto 21;
+end;12:begin{161:}val:=0;nextcontrol:=48;
+repeat val:=8*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>55)or(nextcontrol<48){:161};
+begin accumulator:=accumulator+nextsign*(val);nextsign:=+1;end;goto 21;
+end;13:begin{162:}val:=0;nextcontrol:=48;
+repeat if nextcontrol>=65 then nextcontrol:=nextcontrol-7;
+val:=16*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>70)or(nextcontrol<48)or((nextcontrol>57)and(
+nextcontrol<65)){:162};begin accumulator:=accumulator+nextsign*(val);
+nextsign:=+1;end;goto 21;end;130:begin q:=idlookup(0);
+if ilk[q]<>1 then begin nextcontrol:=42;goto 21;end;
+begin accumulator:=accumulator+nextsign*(equiv[q]-32768);nextsign:=+1;
+end;end;43:;45:nextsign:=-nextsign;132,133,135,134,136:goto 30;
+59:begin writeln(output);
+write(output,'! Omit semicolon in numeric definition');error;end;
+otherwise{159:}begin begin writeln(output);
+write(output,'! Improper numeric definition will be flushed');error;end;
+repeat nextcontrol:=skipahead until(nextcontrol>=132);
+if nextcontrol=135 then begin loc:=loc-2;nextcontrol:=getnext;end;
+accumulator:=0;goto 30;end{:159}end;end;30:{:158};
+if abs(accumulator)>=32768 then begin begin writeln(output);
+write(output,'! Value too big: ',accumulator:1);error;end;
+accumulator:=0;end;equiv[p]:=accumulator+32768;end;{:157}{165:}
+procedure scanrepl(t:eightbits);label 22,30,31,21;var a:sixteenbits;
+b:ASCIIcode;bal:eightbits;begin bal:=0;
+while true do begin 22:a:=getnext;case a of 40:bal:=bal+1;
+41:if bal=0 then begin writeln(output);write(output,'! Extra )');error;
+end else bal:=bal-1;39:{168:}begin b:=39;
+while true do begin begin if tokptr[z]=maxtoks then begin writeln(output
+);write(output,'! Sorry, ','token',' capacity exceeded');error;
+history:=3;jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;
+end;
+if b=64 then if buffer[loc]=64 then loc:=loc+1 else begin writeln(output
+);write(output,'! You should double @ signs in strings');error;end;
+if loc=limit then begin begin writeln(output);
+write(output,'! String didn''t end');error;end;buffer[loc]:=39;
+buffer[loc+1]:=0;end;b:=buffer[loc];loc:=loc+1;
+if b=39 then begin if buffer[loc]<>39 then goto 31 else begin loc:=loc+1
+;begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=39;tokptr[z]:=tokptr[z]+1;end;end;end;
+end;31:end{:168};35:if t=3 then a:=0;{167:}130:begin a:=idlookup(0);
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=(a div 256)+128;tokptr[z]:=tokptr[z]+1;
+end;a:=a mod 256;end;
+135:if t<>135 then goto 30 else begin begin if tokptr[z]=maxtoks then
+begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=(curmodule div 256)+168;
+tokptr[z]:=tokptr[z]+1;end;a:=curmodule mod 256;end;2:{169:}
+begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=2;tokptr[z]:=tokptr[z]+1;end;
+buffer[limit+1]:=64;
+21:if buffer[loc]=64 then begin if loc<limit then if buffer[loc+1]=64
+then begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=64;tokptr[z]:=tokptr[z]+1;end;
+loc:=loc+2;goto 21;end;
+end else begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=buffer[loc];tokptr[z]:=tokptr[z]+1;end;
+loc:=loc+1;goto 21;end;if loc>=limit then begin writeln(output);
+write(output,'! Verbatim string didn''t end');error;
+end else if buffer[loc+1]<>62 then begin writeln(output);
+write(output,'! You should double @ signs in verbatim strings');error;
+end;loc:=loc+2;end{:169};
+133,132,134:if t<>135 then goto 30 else begin begin writeln(output);
+write(output,'! @',xchr[buffer[loc-1]],' is ignored in Pascal text');
+error;end;goto 22;end;136:goto 30;{:167}otherwise end;
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=a;tokptr[z]:=tokptr[z]+1;end;end;
+30:nextcontrol:=a;{166:}
+if bal>0 then begin if bal=1 then begin writeln(output);
+write(output,'! Missing )');error;end else begin writeln(output);
+write(output,'! Missing ',bal:1,' )''s');error;end;
+while bal>0 do begin begin if tokptr[z]=maxtoks then begin writeln(
+output);write(output,'! Sorry, ','token',' capacity exceeded');error;
+history:=3;jumpout;end;tokmem[z,tokptr[z]]:=41;tokptr[z]:=tokptr[z]+1;
+end;bal:=bal-1;end;end{:166};
+if textptr>maxtexts-4 then begin writeln(output);
+write(output,'! Sorry, ','text',' capacity exceeded');error;history:=3;
+jumpout;end;currepltext:=textptr;tokstart[textptr+4]:=tokptr[z];
+textptr:=textptr+1;if z=3 then z:=0 else z:=z+1;end;{:165}{170:}
+procedure definemacro(t:eightbits);var p:namepointer;
+begin p:=idlookup(t);scanrepl(t);equiv[p]:=currepltext;
+textlink[currepltext]:=0;end;{:170}{172:}procedure scanmodule;
+label 22,30,10;var p:namepointer;begin modulecount:=modulecount+1;{173:}
+nextcontrol:=0;
+while true do begin 22:while nextcontrol<=132 do begin nextcontrol:=
+skipahead;if nextcontrol=135 then begin loc:=loc-2;nextcontrol:=getnext;
+end;end;if nextcontrol<>133 then goto 30;nextcontrol:=getnext;
+if nextcontrol<>130 then begin begin writeln(output);
+write(output,'! Definition flushed, must start with ',
+'identifier of length > 1');error;end;goto 22;end;nextcontrol:=getnext;
+if nextcontrol=61 then begin scannumeric(idlookup(1));goto 22;
+end else if nextcontrol=30 then begin definemacro(2);goto 22;
+end else{174:}if nextcontrol=40 then begin nextcontrol:=getnext;
+if nextcontrol=35 then begin nextcontrol:=getnext;
+if nextcontrol=41 then begin nextcontrol:=getnext;
+if nextcontrol=61 then begin begin writeln(output);
+write(output,'! Use == for macros');error;end;nextcontrol:=30;end;
+if nextcontrol=30 then begin definemacro(3);goto 22;end;end;end;end;
+{:174};begin writeln(output);
+write(output,'! Definition flushed since it starts badly');error;end;
+end;30:{:173};{175:}case nextcontrol of 134:p:=0;135:begin p:=curmodule;
+{176:}repeat nextcontrol:=getnext;until nextcontrol<>43;
+if(nextcontrol<>61)and(nextcontrol<>30)then begin begin writeln(output);
+write(output,'! Pascal text flushed, = sign is missing');error;end;
+repeat nextcontrol:=skipahead;until nextcontrol=136;goto 10;end{:176};
+end;otherwise goto 10 end;{177:}storetwobytes(53248+modulecount);{:177};
+scanrepl(135);{178:}
+if p=0 then begin textlink[lastunnamed]:=currepltext;
+lastunnamed:=currepltext;
+end else if equiv[p]=0 then equiv[p]:=currepltext else begin p:=equiv[p]
+;while textlink[p]<maxtexts do p:=textlink[p];textlink[p]:=currepltext;
+end;textlink[currepltext]:=maxtexts;{:178};{:175};10:end;{:172}{181:}
+{procedure debughelp;label 888,10;var k:integer;
+begin debugskipped:=debugskipped+1;
+if debugskipped<debugcycle then goto 10;debugskipped:=0;
+while true do begin begin writeln(output);write(output,'#');end;
+flushstdout;read(input,ddt);
+if ddt<0 then goto 10 else if ddt=0 then begin goto 888;
+888:ddt:=0;
+end else begin read(input,dd);case ddt of 1:printid(dd);2:printrepl(dd);
+3:for k:=1 to dd do write(output,xchr[buffer[k]]);
+4:for k:=1 to dd do write(output,xchr[modtext[k]]);
+5:for k:=1 to outptr do write(output,xchr[outbuf[k]]);
+6:for k:=1 to dd do write(output,xchr[outcontrib[k]]);
+otherwise write(output,'?')end;end;end;10:end;}{:181}{182:}
+begin initialize;{134:}openinput;line:=0;otherline:=0;changing:=true;
+primethechangebuffer;changing:=not changing;templine:=otherline;
+otherline:=line;line:=templine;limit:=0;loc:=1;buffer[0]:=32;
+inputhasended:=false;{:134};
+writeln(output,'This is TANGLE, Version 4.5 for Linux');{183:}
+phaseone:=true;modulecount:=0;repeat nextcontrol:=skipahead;
+until nextcontrol=136;while not inputhasended do scanmodule;{138:}
+if changelimit<>0 then begin for ii:=0 to changelimit do buffer[ii]:=
+changebuffer[ii];limit:=changelimit;changing:=true;line:=otherline;
+loc:=changelimit;begin writeln(output);
+write(output,'! Change file entry did not match');error;end;end{:138};
+phaseone:=false;{:183};{for ii:=0 to 3 do maxtokptr[ii]:=tokptr[ii];}
+{112:}if textlink[0]=0 then begin begin writeln(output);
+write(output,'! No output was specified.');end;
+if history=0 then history:=1;end else begin begin writeln(output);
+write(output,'Writing the output file');end;flushstdout;{83:}
+stackptr:=1;bracelevel:=0;curstate.namefield:=0;
+curstate.replfield:=textlink[0];zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];curstate.modfield:=0;
+{:83};{96:}outstate:=0;outptr:=0;breakptr:=0;semiptr:=0;outbuf[0]:=0;
+line:=1;{:96};sendtheoutput;{98:}breakptr:=outptr;semiptr:=0;
+flushbuffer;if bracelevel<>0 then begin writeln(output);
+write(output,'! Program ended at brace level ',bracelevel:1);error;end;
+{:98};begin writeln(output);write(output,'Done.');end;end{:112};
+9999:if stringptr>256 then{184:}begin begin writeln(output);
+write(output,stringptr-256:1,' strings written to string pool file.');
+end;write(pool,'*');
+for ii:=1 to 9 do begin outbuf[ii]:=poolchecksum mod 10;
+poolchecksum:=poolchecksum div 10;end;
+for ii:=9 downto 1 do write(pool,xchr[48+outbuf[ii]]);writeln(pool);
+end{:184};{[186:]begin writeln(output);
+write(output,'Memory usage statistics:');end;begin writeln(output);
+write(output,nameptr:1,' names, ',textptr:1,' replacement texts;');end;
+begin writeln(output);write(output,byteptr[0]:1);end;
+for wo:=1 to 2 do write(output,'+',byteptr[wo]:1);
+if phaseone then for ii:=0 to 3 do maxtokptr[ii]:=tokptr[ii];
+write(output,' bytes, ',maxtokptr[0]:1);
+for ii:=1 to 3 do write(output,'+',maxtokptr[ii]:1);
+write(output,' tokens.');[:186];}{187:}
+case history of 0:begin writeln(output);
+write(output,'(No errors were found.)');end;1:begin writeln(output);
+write(output,'(Did you see the warning message above?)');end;
+2:begin writeln(output);
+write(output,'(Pardon me, but I think I spotted something wrong.)');end;
+3:begin writeln(output);
+write(output,'(That was a fatal error, my friend.)');end;end{:187};
+writeln(output);if(history<>0)and(history<>1)then exit(1)else exit(0);
+end.{:182}
diff --git a/systems/knuth/local/web/tangle.p-edited b/systems/knuth/local/web/tangle.p-edited
new file mode 100644
index 0000000000..2588255865
--- /dev/null
+++ b/systems/knuth/local/web/tangle.p-edited
@@ -0,0 +1,816 @@
+{2:}{4:}{:4}program TANGLE(input,output);label 9999;
+const{8:}bufsize=100;maxbytes=45000;maxtoks=50000;maxnames=4000;
+maxtexts=2000;hashsize=353;longestname=400;linelength=72;outbufsize=144;
+stacksize=50;maxidlength=50;unambiglength=7;{:8}type{11:}
+ASCIIcode=integer(8);{:11}{12:}textfile=packed file of char;{:12}{37:}
+eightbits=0..255;sixteenbits=0..65535;{:37}{39:}namepointer=0..maxnames;
+UNIXfilename=packed array[1..60] of char;
+{:39}{43:}textpointer=0..maxtexts;{:43}{78:}
+outputstate=record endfield:sixteenbits;bytefield:sixteenbits;
+namefield:namepointer;replfield:textpointer;modfield:0..12287;end;{:78}
+var{9:}history:0..3;{:9}{13:}xord:array[char]of ASCIIcode;
+xchr:array[ASCIIcode]of char;{:13}{23:}webfile:textfile;
+changefile:textfile;{:23}{25:}Pascalfile:textfile;pool:textfile;{:25}
+{27:}buffer:array[0..bufsize]of ASCIIcode;{:27}{29:}phaseone:boolean;
+{:29}{38:}bytemem:packed array[0..2,0..maxbytes]of ASCIIcode;
+tokmem:packed array[0..3,0..maxtoks]of eightbits;
+bytestart:array[0..maxnames]of sixteenbits;
+tokstart:array[0..maxtexts]of sixteenbits;
+link:array[0..maxnames]of sixteenbits;
+ilk:array[0..maxnames]of sixteenbits;
+equiv:array[0..maxnames]of sixteenbits;
+textlink:array[0..maxtexts]of sixteenbits;{:38}{40:}nameptr:namepointer;
+stringptr:namepointer;byteptr:array[0..2]of 0..maxbytes;
+poolchecksum:integer;{:40}{44:}textptr:textpointer;
+tokptr:array[0..3]of 0..maxtoks;z:0..3;
+{maxtokptr:array[0..3]of 0..maxtoks;}{:44}{50:}idfirst:0..bufsize;
+idloc:0..bufsize;doublechars:0..bufsize;
+hash,chophash:array[0..hashsize]of sixteenbits;
+choppedid:array[0..unambiglength]of ASCIIcode;{:50}{65:}
+modtext:array[0..longestname]of ASCIIcode;{:65}{70:}
+lastunnamed:textpointer;{:70}{79:}curstate:outputstate;
+stack:array[1..stacksize]of outputstate;stackptr:0..stacksize;{:79}{80:}
+zo:0..3;{:80}{82:}bracelevel:eightbits;{:82}{86:}curval:integer;{:86}
+{94:}outbuf:array[0..outbufsize]of ASCIIcode;outptr:0..outbufsize;
+breakptr:0..outbufsize;semiptr:0..outbufsize;{:94}{95:}
+outstate:eightbits;outval,outapp:integer;outsign:ASCIIcode;
+lastsign:-1..+1;{:95}{100:}outcontrib:array[1..linelength]of ASCIIcode;
+{:100}{124:}ii:integer;line:integer;otherline:integer;templine:integer;
+limit:0..bufsize;loc:0..bufsize;inputhasended:boolean;changing:boolean;
+{:124}{126:}changebuffer:array[0..bufsize]of ASCIIcode;
+changelimit:0..bufsize;{:126}{143:}curmodule:namepointer;
+scanninghex:boolean;{:143}{156:}nextcontrol:eightbits;{:156}{164:}
+currepltext:textpointer;{:164}{171:}modulecount:0..12287;{:171}{179:}
+{troubleshooting:boolean;ddt:integer;dd:integer;debugcycle:integer;
+debugskipped:integer;}{:179}{185:}{wo:0..2;}{:185}{189:}
+webname,changename,Pascalname,poolname:UNIXfilename;{:189}
+#include "tangext.h"
+{30:}{procedure debughelp;forward;}{:30}{31:}procedure error;
+var j:0..outbufsize;k,l:0..bufsize;begin if phaseone then{32:}
+begin if changing then write(output,'. (change file ')else write(output,
+'. (');writeln(output,'l.',line:1,')');
+if loc>=limit then l:=limit else l:=loc;
+for k:=1 to l do if buffer[k-1]=9 then write(output,' ')else write(
+output,xchr[buffer[k-1]]);writeln(output);
+for k:=1 to l do write(output,' ');
+for k:=l+1 to limit do write(output,xchr[buffer[k-1]]);
+write(output,' ');end{:32}else{33:}
+begin writeln(output,'. (l.',line:1,')');
+for j:=1 to outptr do write(output,xchr[outbuf[j-1]]);
+write(output,'... ');end{:33};flush(output);history:=2;
+{debugskipped:=debugcycle;debughelp;}end;{:31}{34:}procedure jumpout;
+begin goto 9999;end;{:34}{190:}procedure scanargs;
+var dotpos,i,a:integer;c:char;fname:array[1..55]of char;
+foundweb,foundchange:boolean;begin foundweb:=false;foundchange:=false;
+for a:=1 to argc-1 do begin argv(a,fname);
+if fname[1]<>'-'then begin if not foundweb then{191:}begin dotpos:=-1;
+i:=1;while(fname[i]<>' ')and(i<=55)do begin webname[i]:=fname[i];
+if fname[i]='.'then dotpos:=i;i:=i+1;end;
+if dotpos=-1 then begin dotpos:=i;webname[dotpos]:='.';
+webname[dotpos+1]:='w';webname[dotpos+2]:='e';webname[dotpos+3]:='b';
+webname[dotpos+4]:=' ';end else webname[i]:=' ';
+for i:=1 to dotpos do begin c:=webname[i];Pascalname[i]:=c;
+poolname[i]:=c;end;Pascalname[dotpos+1]:='p';Pascalname[dotpos+2]:=' ';
+poolname[dotpos+1]:='p';poolname[dotpos+2]:='o';poolname[dotpos+3]:='o';
+poolname[dotpos+4]:='l';poolname[dotpos+5]:=' ';foundweb:=true;end{:191}
+else if not foundchange then{192:}begin dotpos:=-1;i:=1;
+while(fname[i]<>' ')and(i<=55)do begin changename[i]:=fname[i];
+if fname[i]='.'then dotpos:=i;i:=i+1;end;
+if dotpos=-1 then begin dotpos:=i;changename[dotpos]:='.';
+changename[dotpos+1]:='c';changename[dotpos+2]:='h';
+changename[dotpos+3]:=' ';end else changename[i]:=' ';foundchange:=true;
+end{:192}else{195:}begin begin writeln(output);
+write(output,'! Usage: webfile[.web] [changefile[.ch]]');end;error;
+jumpout;end{:195};end else{194:}begin{195:}begin begin writeln(output);
+write(output,'! Usage: webfile[.web] [changefile[.ch]]');end;error;
+jumpout;end{:195};end{:194};end;if not foundweb then{195:}
+begin begin writeln(output);
+write(output,'! Usage: webfile[.web] [changefile[.ch]]');end;error;
+jumpout;end{:195};if not foundchange then{193:}begin changename[1]:='/';
+changename[2]:='d';changename[3]:='e';changename[4]:='v';
+changename[5]:='/';changename[6]:='n';changename[7]:='u';
+changename[8]:='l';changename[9]:='l';changename[10]:=' ';end{:193};end;
+{:190}procedure initialize;var{16:}i:0..255;{:16}{41:}wi:0..2;{:41}{45:}
+zi:0..3;{:45}{51:}h:0..hashsize;{:51}begin{10:}history:=0;{:10}{14:}
+xchr[32]:=' ';xchr[33]:='!';xchr[34]:='"';xchr[35]:='#';xchr[36]:='$';
+xchr[37]:='%';xchr[38]:='&';xchr[39]:='''';xchr[40]:='(';xchr[41]:=')';
+xchr[42]:='*';xchr[43]:='+';xchr[44]:=',';xchr[45]:='-';xchr[46]:='.';
+xchr[47]:='/';xchr[48]:='0';xchr[49]:='1';xchr[50]:='2';xchr[51]:='3';
+xchr[52]:='4';xchr[53]:='5';xchr[54]:='6';xchr[55]:='7';xchr[56]:='8';
+xchr[57]:='9';xchr[58]:=':';xchr[59]:=';';xchr[60]:='<';xchr[61]:='=';
+xchr[62]:='>';xchr[63]:='?';xchr[64]:='@';xchr[65]:='A';xchr[66]:='B';
+xchr[67]:='C';xchr[68]:='D';xchr[69]:='E';xchr[70]:='F';xchr[71]:='G';
+xchr[72]:='H';xchr[73]:='I';xchr[74]:='J';xchr[75]:='K';xchr[76]:='L';
+xchr[77]:='M';xchr[78]:='N';xchr[79]:='O';xchr[80]:='P';xchr[81]:='Q';
+xchr[82]:='R';xchr[83]:='S';xchr[84]:='T';xchr[85]:='U';xchr[86]:='V';
+xchr[87]:='W';xchr[88]:='X';xchr[89]:='Y';xchr[90]:='Z';xchr[91]:='[';
+xchr[92]:='\';xchr[93]:=']';xchr[94]:='^';xchr[95]:='_';xchr[96]:='`';
+xchr[97]:='a';xchr[98]:='b';xchr[99]:='c';xchr[100]:='d';xchr[101]:='e';
+xchr[102]:='f';xchr[103]:='g';xchr[104]:='h';xchr[105]:='i';
+xchr[106]:='j';xchr[107]:='k';xchr[108]:='l';xchr[109]:='m';
+xchr[110]:='n';xchr[111]:='o';xchr[112]:='p';xchr[113]:='q';
+xchr[114]:='r';xchr[115]:='s';xchr[116]:='t';xchr[117]:='u';
+xchr[118]:='v';xchr[119]:='w';xchr[120]:='x';xchr[121]:='y';
+xchr[122]:='z';xchr[123]:='{';xchr[124]:='|';xchr[125]:='}';
+xchr[126]:='~';xchr[0]:=' ';xchr[127]:=' ';{:14}{17:}
+for i:=1 to 31 do xchr[i]:=chr(i);for i:=128 to 255 do xchr[i]:=chr(i);
+{:17}{18:}for i:=0 to 255 do xord[chr(i)]:=32;
+for i:=1 to 255 do xord[xchr[i]]:=i;xord[' ']:=32;{:18}{21:}{:21}{26:}
+scanargs;rewrite(Pascalfile,Pascalname);rewrite(pool,poolname);{:26}
+{42:}for wi:=0 to 2 do begin bytestart[wi]:=0;byteptr[wi]:=0;end;
+bytestart[3]:=0;nameptr:=1;stringptr:=256;poolchecksum:=271828;{:42}
+{46:}for zi:=0 to 3 do begin tokstart[zi]:=0;tokptr[zi]:=0;end;
+tokstart[4]:=0;textptr:=1;z:=1 mod 4;{:46}{48:}ilk[0]:=0;equiv[0]:=0;
+{:48}{52:}for h:=0 to hashsize-1 do begin hash[h]:=0;chophash[h]:=0;end;
+{:52}{71:}lastunnamed:=0;textlink[0]:=0;{:71}{144:}scanninghex:=false;
+{:144}{152:}modtext[0]:=32;{:152}{180:}{troubleshooting:=true;
+debugcycle:=1;debugskipped:=0;troubleshooting:=false;debugcycle:=99999;}
+{:180}end;{:2}{24:}procedure openinput;begin reset(webfile,webname);
+reset(changefile,changename);end;{:24}{28:}
+function inputln(var f:textfile):boolean;begin limit:=0;
+if testeof(f)then inputln:=false else begin lineread(f);
+if limit=bufsize then begin limit:=limit-1;begin writeln(output);
+write(output,'! Input line too long');end;loc:=0;error;end;
+inputln:=true;end;end;{:28}{49:}procedure printid(p:namepointer);
+var k:0..maxbytes;w:0..2;
+begin if p>=nameptr then write(output,'IMPOSSIBLE')else begin w:=p mod 3
+;
+for k:=bytestart[p]to bytestart[p+3]-1 do write(output,xchr[bytemem[w,k]
+]);end;end;{:49}{53:}function idlookup(t:eightbits):namepointer;
+label 31,32;var c:eightbits;i:0..bufsize;h:0..hashsize;k:0..maxbytes;
+w:0..2;l:0..bufsize;p,q:namepointer;s:0..unambiglength;
+begin l:=idloc-idfirst;{54:}h:=buffer[idfirst];i:=idfirst+1;
+while i<idloc do begin h:=(h+h+buffer[i])mod hashsize;i:=i+1;end{:54};
+{55:}p:=hash[h];
+while p<>0 do begin if bytestart[p+3]-bytestart[p]=l then{56:}
+begin i:=idfirst;k:=bytestart[p];w:=p mod 3;
+while(i<idloc)and(buffer[i]=bytemem[w,k])do begin i:=i+1;k:=k+1;end;
+if i=idloc then goto 31;end{:56};p:=link[p];end;p:=nameptr;
+link[p]:=hash[h];hash[h]:=p;31:{:55};if(p=nameptr)or(t<>0)then{57:}
+begin if((p<>nameptr)and(t<>0)and(ilk[p]=0))or((p=nameptr)and(t=0)and(
+buffer[idfirst]<>34))then{58:}begin i:=idfirst;s:=0;h:=0;
+while(i<idloc)and(s<unambiglength)do begin if buffer[i]<>95 then begin
+if buffer[i]>=97 then choppedid[s]:=buffer[i]-32 else choppedid[s]:=
+buffer[i];h:=(h+h+choppedid[s])mod hashsize;s:=s+1;end;i:=i+1;end;
+choppedid[s]:=0;end{:58};if p<>nameptr then{59:}
+begin if ilk[p]=0 then begin if t=1 then begin writeln(output);
+write(output,'! This identifier has already appeared');error;end;{60:}
+q:=chophash[h];
+if q=p then chophash[h]:=equiv[p]else begin while equiv[q]<>p do q:=
+equiv[q];equiv[q]:=equiv[p];end{:60};end else begin writeln(output);
+write(output,'! This identifier was defined before');error;end;
+ilk[p]:=t;end{:59}else{61:}
+begin if(t=0)and(buffer[idfirst]<>34)then{62:}begin q:=chophash[h];
+while q<>0 do begin{63:}begin k:=bytestart[q];s:=0;w:=q mod 3;
+while(k<bytestart[q+3])and(s<unambiglength)do begin c:=bytemem[w,k];
+if c<>95 then begin if c>=97 then c:=c-32;
+if choppedid[s]<>c then goto 32;s:=s+1;end;k:=k+1;end;
+if(k=bytestart[q+3])and(choppedid[s]<>0)then goto 32;
+begin writeln(output);write(output,'! Identifier conflict with ');end;
+for k:=bytestart[q]to bytestart[q+3]-1 do write(output,xchr[bytemem[w,k]
+]);error;q:=0;32:end{:63};q:=equiv[q];end;equiv[p]:=chophash[h];
+chophash[h]:=p;end{:62};w:=nameptr mod 3;k:=byteptr[w];
+if k+l>maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;i:=idfirst;while i<idloc do begin bytemem[w,k]:=buffer[i];
+k:=k+1;i:=i+1;end;byteptr[w]:=k;bytestart[nameptr+3]:=k;
+nameptr:=nameptr+1;if buffer[idfirst]<>34 then ilk[p]:=t else{64:}
+begin ilk[p]:=1;
+if l-doublechars=2 then equiv[p]:=buffer[idfirst+1]+32768 else begin
+equiv[p]:=stringptr+32768;l:=l-doublechars-1;
+if l>99 then begin writeln(output);
+write(output,'! Preprocessed string is too long');error;end;
+stringptr:=stringptr+1;write(pool,xchr[48+l div 10],xchr[48+l mod 10]);
+poolchecksum:=poolchecksum+poolchecksum+l;
+while poolchecksum>536870839 do poolchecksum:=poolchecksum-536870839;
+i:=idfirst+1;while i<idloc do begin write(pool,xchr[buffer[i]]);
+poolchecksum:=poolchecksum+poolchecksum+buffer[i];
+while poolchecksum>536870839 do poolchecksum:=poolchecksum-536870839;
+if(buffer[i]=34)or(buffer[i]=64)then i:=i+2 else i:=i+1;end;
+writeln(pool);end;end{:64};end{:61};end{:57};idlookup:=p;end;{:53}{66:}
+function modlookup(l:sixteenbits):namepointer;label 31;var c:0..4;
+j:0..longestname;k:0..maxbytes;w:0..2;p:namepointer;q:namepointer;
+begin c:=2;q:=0;p:=ilk[0];while p<>0 do begin{68:}begin k:=bytestart[p];
+w:=p mod 3;c:=1;j:=1;
+while(k<bytestart[p+3])and(j<=l)and(modtext[j]=bytemem[w,k])do begin k:=
+k+1;j:=j+1;end;
+if k=bytestart[p+3]then if j>l then c:=1 else c:=4 else if j>l then c:=3
+else if modtext[j]<bytemem[w,k]then c:=0 else c:=2;end{:68};q:=p;
+if c=0 then p:=link[q]else if c=2 then p:=ilk[q]else goto 31;end;{67:}
+w:=nameptr mod 3;k:=byteptr[w];
+if k+l>maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;p:=nameptr;if c=0 then link[q]:=p else ilk[q]:=p;link[p]:=0;
+ilk[p]:=0;c:=1;equiv[p]:=0;
+for j:=1 to l do bytemem[w,k+j-1]:=modtext[j];byteptr[w]:=k+l;
+bytestart[nameptr+3]:=k+l;nameptr:=nameptr+1;{:67};
+31:if c<>1 then begin begin writeln(output);
+write(output,'! Incompatible section names');error;end;p:=0;end;
+modlookup:=p;end;{:66}{69:}
+function prefixlookup(l:sixteenbits):namepointer;var c:0..4;
+count:0..maxnames;j:0..longestname;k:0..maxbytes;w:0..2;p:namepointer;
+q:namepointer;r:namepointer;begin q:=0;p:=ilk[0];count:=0;r:=0;
+while p<>0 do begin{68:}begin k:=bytestart[p];w:=p mod 3;c:=1;j:=1;
+while(k<bytestart[p+3])and(j<=l)and(modtext[j]=bytemem[w,k])do begin k:=
+k+1;j:=j+1;end;
+if k=bytestart[p+3]then if j>l then c:=1 else c:=4 else if j>l then c:=3
+else if modtext[j]<bytemem[w,k]then c:=0 else c:=2;end{:68};
+if c=0 then p:=link[p]else if c=2 then p:=ilk[p]else begin r:=p;
+count:=count+1;q:=ilk[p];p:=link[p];end;if p=0 then begin p:=q;q:=0;end;
+end;if count<>1 then if count=0 then begin writeln(output);
+write(output,'! Name does not match');error;
+end else begin writeln(output);write(output,'! Ambiguous prefix');error;
+end;prefixlookup:=r;end;{:69}{73:}
+procedure storetwobytes(x:sixteenbits);
+begin if tokptr[z]+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=x div 256;
+tokmem[z,tokptr[z]+1]:=x mod 256;tokptr[z]:=tokptr[z]+2;end;{:73}{74:}
+{procedure printrepl(p:textpointer);var k:0..maxtoks;a:sixteenbits;
+zp:0..3;
+begin if p>=textptr then write(output,'BAD')else begin k:=tokstart[p];
+zp:=p mod 4;while k<tokstart[p+4]do begin a:=tokmem[zp,k];
+if a>=128 then[75:]begin k:=k+1;
+if a<168 then begin a:=(a-128)*256+tokmem[zp,k];printid(a);
+if bytemem[a mod 3,bytestart[a]]=34 then write(output,'"')else write(
+output,' ');end else if a<208 then begin write(output,'@<');
+printid((a-168)*256+tokmem[zp,k]);write(output,'@>');
+end else begin a:=(a-208)*256+tokmem[zp,k];
+write(output,'@',xchr[123],a:1,'@',xchr[125]);end;
+end[:75]else[76:]case a of 9:write(output,'@',xchr[123]);
+10:write(output,'@',xchr[125]);12:write(output,'@''');
+13:write(output,'@"');125:write(output,'@$');0:write(output,'#');
+64:write(output,'@@');2:write(output,'@=');3:write(output,'@\');
+otherwise write(output,xchr[a])end[:76];k:=k+1;end;end;end;}{:74}{84:}
+procedure pushlevel(p:namepointer);
+begin if stackptr=stacksize then begin writeln(output);
+write(output,'! Sorry, ','stack',' capacity exceeded');error;history:=3;
+jumpout;end else begin stack[stackptr]:=curstate;stackptr:=stackptr+1;
+curstate.namefield:=p;curstate.replfield:=equiv[p];
+zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];curstate.modfield:=0;
+end;end;{:84}{85:}procedure poplevel;label 10;
+begin if textlink[curstate.replfield]=0 then begin if ilk[curstate.
+namefield]=3 then{91:}begin nameptr:=nameptr-1;textptr:=textptr-1;
+z:=textptr mod 4;{if tokptr[z]>maxtokptr[z]then maxtokptr[z]:=tokptr[z];
+}tokptr[z]:=tokstart[textptr];
+{byteptr[nameptr mod 3]:=byteptr[nameptr mod 3]-1;}end{:91};
+end else if textlink[curstate.replfield]<maxtexts then begin curstate.
+replfield:=textlink[curstate.replfield];zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];goto 10;end;
+stackptr:=stackptr-1;if stackptr>0 then begin curstate:=stack[stackptr];
+zo:=curstate.replfield mod 4;end;10:end;{:85}{87:}
+function getoutput:sixteenbits;label 20,30,31;var a:sixteenbits;
+b:eightbits;bal:sixteenbits;k:0..maxbytes;w:0..2;
+begin 20:if stackptr=0 then begin a:=0;goto 31;end;
+if curstate.bytefield=curstate.endfield then begin curval:=-curstate.
+modfield;poplevel;if curval=0 then goto 20;a:=129;goto 31;end;
+a:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;if a<128 then if a=0 then{92:}
+begin pushlevel(nameptr-1);goto 20;end{:92}else goto 31;
+a:=(a-128)*256+tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;if a<10240 then{89:}
+begin case ilk[a]of 0:begin curval:=a;a:=130;end;
+1:begin curval:=equiv[a]-32768;a:=128;end;2:begin pushlevel(a);goto 20;
+end;3:begin{90:}
+while(curstate.bytefield=curstate.endfield)and(stackptr>0)do poplevel;
+if(stackptr=0)or(tokmem[zo,curstate.bytefield]<>40)then begin begin
+writeln(output);write(output,'! No parameter given for ');end;
+printid(a);error;goto 20;end;{93:}bal:=1;
+curstate.bytefield:=curstate.bytefield+1;
+while true do begin b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;
+if b=0 then storetwobytes(nameptr+32767)else begin if b>=128 then begin
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;
+b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;
+end else case b of 40:bal:=bal+1;41:begin bal:=bal-1;
+if bal=0 then goto 30;end;
+39:repeat begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;
+b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;until b=39;otherwise end;
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;end;end;
+30:{:93};equiv[nameptr]:=textptr;ilk[nameptr]:=2;w:=nameptr mod 3;
+k:=byteptr[w];{if k=maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;bytemem[w,k]:=35;k:=k+1;byteptr[w]:=k;}
+if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;bytestart[nameptr+3]:=k;nameptr:=nameptr+1;
+if textptr>maxtexts-4 then begin writeln(output);
+write(output,'! Sorry, ','text',' capacity exceeded');error;history:=3;
+jumpout;end;textlink[textptr]:=0;tokstart[textptr+4]:=tokptr[z];
+textptr:=textptr+1;z:=textptr mod 4{:90};pushlevel(a);goto 20;end;
+otherwise begin writeln(output);
+write(output,'! This can''t happen (','output',')');error;history:=3;
+jumpout;end end;goto 31;end{:89};if a<20480 then{88:}begin a:=a-10240;
+if equiv[a]<>0 then pushlevel(a)else if a<>0 then begin begin writeln(
+output);write(output,'! Not present: <');end;printid(a);
+write(output,'>');error;end;goto 20;end{:88};curval:=a-20480;a:=129;
+curstate.modfield:=curval;31:{if troubleshooting then debughelp;}
+getoutput:=a;end;{:87}{97:}procedure flushbuffer;var k:0..outbufsize;
+b:0..outbufsize;begin b:=breakptr;
+if(semiptr<>0)and(outptr-semiptr<=linelength)then breakptr:=semiptr;
+for k:=1 to break_ptr do write(Pascal_file,xchr[out_buf[k-1]]);
+writeln(Pascalfile);line:=line+1;
+if line mod 100=0 then begin write(output,'.');
+if line mod 500=0 then write(output,line:1);flush(output);end;
+if breakptr<outptr then begin if outbuf[breakptr]=32 then begin breakptr
+:=breakptr+1;if breakptr>b then b:=breakptr;end;
+for k:=breakptr to outptr-1 do outbuf[k-breakptr]:=outbuf[k];end;
+outptr:=outptr-breakptr;breakptr:=b-breakptr;semiptr:=0;
+if outptr>linelength then begin begin writeln(output);
+write(output,'! Long line must be truncated');error;end;
+outptr:=linelength;end;end;{:97}{99:}procedure appval(v:integer);
+var k:0..outbufsize;begin k:=outbufsize;repeat outbuf[k]:=v mod 10;
+v:=v div 10;k:=k-1;until v=0;repeat k:=k+1;
+begin outbuf[outptr]:=outbuf[k]+48;outptr:=outptr+1;end;
+until k=outbufsize;end;{:99}{101:}procedure sendout(t:eightbits;
+v:sixteenbits);label 20;var k:0..linelength;begin{102:}
+20:case outstate of 1:if t<>3 then begin breakptr:=outptr;
+if t=2 then begin outbuf[outptr]:=32;outptr:=outptr+1;end;end;
+2:begin begin outbuf[outptr]:=44-outapp;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;breakptr:=outptr;end;
+3,4:begin{103:}
+if(outval<0)or((outval=0)and(lastsign<0))then begin outbuf[outptr]:=45;
+outptr:=outptr+1;
+end else if outsign>0 then begin outbuf[outptr]:=outsign;
+outptr:=outptr+1;end;appval(abs(outval));
+if outptr>linelength then flushbuffer;{:103};outstate:=outstate-2;
+goto 20;end;5:{104:}begin if(t=3)or({105:}
+((t=2)and(v=3)and(((outcontrib[1]=68)and(outcontrib[2]=73)and(outcontrib
+[3]=86))or((outcontrib[1]=100)and(outcontrib[2]=105)and(outcontrib[3]=
+118))or((outcontrib[1]=77)and(outcontrib[2]=79)and(outcontrib[3]=68))or(
+(outcontrib[1]=109)and(outcontrib[2]=111)and(outcontrib[3]=100))))or((t=
+0)and((v=42)or(v=47))){:105})then begin{103:}
+if(outval<0)or((outval=0)and(lastsign<0))then begin outbuf[outptr]:=45;
+outptr:=outptr+1;
+end else if outsign>0 then begin outbuf[outptr]:=outsign;
+outptr:=outptr+1;end;appval(abs(outval));
+if outptr>linelength then flushbuffer;{:103};outsign:=43;outval:=outapp;
+end else outval:=outval+outapp;outstate:=3;goto 20;end{:104};
+0:if t<>3 then breakptr:=outptr;otherwise end{:102};
+if t<>0 then for k:=1 to v do begin outbuf[outptr]:=outcontrib[k];
+outptr:=outptr+1;end else begin outbuf[outptr]:=v;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;
+if(t=0)and((v=59)or(v=125))then begin semiptr:=outptr;breakptr:=outptr;
+end;if t>=2 then outstate:=1 else outstate:=0 end;{:101}{106:}
+procedure sendsign(v:integer);
+begin case outstate of 2,4:outapp:=outapp*v;3:begin outapp:=v;
+outstate:=4;end;5:begin outval:=outval+outapp;outapp:=v;outstate:=4;end;
+otherwise begin breakptr:=outptr;outapp:=v;outstate:=2;end end;
+lastsign:=outapp;end;{:106}{107:}procedure sendval(v:integer);
+label 666,10;begin case outstate of 1:begin{110:}
+if(outptr=breakptr+3)or((outptr=breakptr+4)and(outbuf[breakptr]=32))then
+if((outbuf[outptr-3]=68)and(outbuf[outptr-2]=73)and(outbuf[outptr-1]=86)
+)or((outbuf[outptr-3]=100)and(outbuf[outptr-2]=105)and(outbuf[outptr-1]=
+118))or((outbuf[outptr-3]=77)and(outbuf[outptr-2]=79)and(outbuf[outptr-1
+]=68))or((outbuf[outptr-3]=109)and(outbuf[outptr-2]=111)and(outbuf[
+outptr-1]=100))then goto 666{:110};outsign:=32;outstate:=3;outval:=v;
+breakptr:=outptr;lastsign:=+1;end;0:begin{109:}
+if(outptr=breakptr+1)and((outbuf[breakptr]=42)or(outbuf[breakptr]=47))
+then goto 666{:109};outsign:=0;outstate:=3;outval:=v;breakptr:=outptr;
+lastsign:=+1;end;{108:}2:begin outsign:=43;outstate:=3;outval:=outapp*v;
+end;3:begin outstate:=5;outapp:=v;begin writeln(output);
+write(output,'! Two numbers occurred without a sign between them');
+error;end;end;4:begin outstate:=5;outapp:=outapp*v;end;
+5:begin outval:=outval+outapp;outapp:=v;begin writeln(output);
+write(output,'! Two numbers occurred without a sign between them');
+error;end;end;{:108}otherwise goto 666 end;goto 10;666:{111:}
+if v>=0 then begin if outstate=1 then begin breakptr:=outptr;
+begin outbuf[outptr]:=32;outptr:=outptr+1;end;end;appval(v);
+if outptr>linelength then flushbuffer;outstate:=1;
+end else begin begin outbuf[outptr]:=40;outptr:=outptr+1;end;
+begin outbuf[outptr]:=45;outptr:=outptr+1;end;appval(-v);
+begin outbuf[outptr]:=41;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;outstate:=0;end{:111};10:end;
+{:107}{113:}procedure sendtheoutput;label 2,21,22;var curchar:eightbits;
+k:0..linelength;j:0..maxbytes;w:0..2;n:integer;
+begin while stackptr>0 do begin curchar:=getoutput;
+21:case curchar of 0:;{116:}
+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,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:begin outcontrib[1]:=curchar;
+sendout(2,1);end;130:begin k:=0;j:=bytestart[curval];w:=curval mod 3;
+while(k<maxidlength)and(j<bytestart[curval+3])do begin k:=k+1;
+outcontrib[k]:=bytemem[w,j];j:=j+1;if outcontrib[k]=95 then k:=k-1;end;
+sendout(2,k);end;{:116}{119:}48,49,50,51,52,53,54,55,56,57:begin n:=0;
+repeat curchar:=curchar-48;if n>=214748364 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=10*n+curchar;
+curchar:=getoutput;until(curchar>57)or(curchar<48);sendval(n);k:=0;
+if curchar=101 then curchar:=69;if curchar=69 then goto 2 else goto 21;
+end;125:sendval(poolchecksum);12:begin n:=0;curchar:=48;
+repeat curchar:=curchar-48;if n>=268435456 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=8*n+curchar;
+curchar:=getoutput;until(curchar>55)or(curchar<48);sendval(n);goto 21;
+end;13:begin n:=0;curchar:=48;
+repeat if curchar>=65 then curchar:=curchar-55 else curchar:=curchar-48;
+if n>=134217728 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=16*n+curchar;
+curchar:=getoutput;
+until(curchar>70)or(curchar<48)or((curchar>57)and(curchar<65));
+sendval(n);goto 21;end;128:sendval(curval);46:begin k:=1;
+outcontrib[1]:=46;curchar:=getoutput;
+if curchar=46 then begin outcontrib[2]:=46;sendout(1,2);
+end else if(curchar>=48)and(curchar<=57)then goto 2 else begin sendout(0
+,46);goto 21;end;end;{:119}43,45:sendsign(44-curchar);{114:}
+4:begin outcontrib[1]:=97;outcontrib[2]:=110;outcontrib[3]:=100;
+sendout(2,3);end;5:begin outcontrib[1]:=110;outcontrib[2]:=111;
+outcontrib[3]:=116;sendout(2,3);end;6:begin outcontrib[1]:=105;
+outcontrib[2]:=110;sendout(2,2);end;31:begin outcontrib[1]:=111;
+outcontrib[2]:=114;sendout(2,2);end;24:begin outcontrib[1]:=58;
+outcontrib[2]:=61;sendout(1,2);end;26:begin outcontrib[1]:=60;
+outcontrib[2]:=62;sendout(1,2);end;28:begin outcontrib[1]:=60;
+outcontrib[2]:=61;sendout(1,2);end;29:begin outcontrib[1]:=62;
+outcontrib[2]:=61;sendout(1,2);end;30:begin outcontrib[1]:=61;
+outcontrib[2]:=61;sendout(1,2);end;32:begin outcontrib[1]:=46;
+outcontrib[2]:=46;sendout(1,2);end;{:114}39:{117:}begin k:=1;
+outcontrib[1]:=39;repeat if k<linelength then k:=k+1;
+outcontrib[k]:=getoutput;until(outcontrib[k]=39)or(stackptr=0);
+if k=linelength then begin writeln(output);
+write(output,'! String too long');error;end;sendout(1,k);
+curchar:=getoutput;if curchar=39 then outstate:=6;goto 21;end{:117};
+{115:}
+33,34,35,36,37,38,40,41,42,44,47,58,59,60,61,62,63,64,91,92,93,94,95,96,
+123,124{:115}:sendout(0,curchar);{121:}
+9:begin if bracelevel=0 then sendout(0,123)else sendout(0,91);
+bracelevel:=bracelevel+1;end;
+10:if bracelevel>0 then begin bracelevel:=bracelevel-1;
+if bracelevel=0 then sendout(0,125)else sendout(0,93);
+end else begin writeln(output);write(output,'! Extra @}');error;end;
+129:begin if bracelevel=0 then sendout(0,123)else sendout(0,91);
+if curval<0 then begin sendout(0,58);sendval(-curval);
+end else begin sendval(curval);sendout(0,58);end;
+if bracelevel=0 then sendout(0,125)else sendout(0,93);end;{:121}
+127:begin sendout(3,0);outstate:=6;end;2:{118:}begin k:=0;
+repeat if k<linelength then k:=k+1;outcontrib[k]:=getoutput;
+until(outcontrib[k]=2)or(stackptr=0);
+if k=linelength then begin writeln(output);
+write(output,'! Verbatim string too long');error;end;sendout(1,k-1);
+end{:118};3:{122:}begin sendout(1,0);
+while outptr>0 do begin if outptr<=linelength then breakptr:=outptr;
+flushbuffer;end;outstate:=0;end{:122};otherwise begin writeln(output);
+write(output,'! Can''t output ASCII code ',curchar:1);error;end end;
+goto 22;2:{120:}repeat if k<linelength then k:=k+1;
+outcontrib[k]:=curchar;curchar:=getoutput;
+if(outcontrib[k]=69)and((curchar=43)or(curchar=45))then begin if k<
+linelength then k:=k+1;outcontrib[k]:=curchar;curchar:=getoutput;
+end else if curchar=101 then curchar:=69;
+until(curchar<>69)and((curchar<48)or(curchar>57));
+if k=linelength then begin writeln(output);
+write(output,'! Fraction too long');error;end;sendout(3,k);goto 21{:120}
+;22:end;end;{:113}{127:}function linesdontmatch:boolean;label 10;
+var k:0..bufsize;begin linesdontmatch:=true;
+if changelimit<>limit then goto 10;
+if limit>0 then for k:=0 to limit-1 do if changebuffer[k]<>buffer[k]then
+goto 10;linesdontmatch:=false;10:end;{:127}{128:}
+procedure primethechangebuffer;label 22,30,10;var k:0..bufsize;
+begin changelimit:=0;{129:}while true do begin line:=line+1;
+if not inputln(changefile)then goto 10;if limit<2 then goto 22;
+if buffer[0]<>64 then goto 22;
+if(buffer[1]>=88)and(buffer[1]<=90)then buffer[1]:=buffer[1]+32;
+if buffer[1]=120 then goto 30;
+if(buffer[1]=121)or(buffer[1]=122)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @x?');error;
+end;end;22:end;30:{:129};{130:}repeat line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended after @x');error;end;goto 10;end;
+until limit>0;{:130};{131:}begin changelimit:=limit;
+if limit>0 then for k:=0 to limit-1 do changebuffer[k]:=buffer[k];
+end{:131};10:end;{:128}{132:}procedure checkchange;label 10;
+var n:integer;k:0..bufsize;begin if linesdontmatch then goto 10;n:=0;
+while true do begin changing:=not changing;templine:=otherline;
+otherline:=line;line:=templine;line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended before @y');error;end;changelimit:=0;
+changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;goto 10;end;{133:}
+if limit>1 then if buffer[0]=64 then begin if(buffer[1]>=88)and(buffer[1
+]<=90)then buffer[1]:=buffer[1]+32;
+if(buffer[1]=120)or(buffer[1]=122)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @y?');error;
+end;end else if buffer[1]=121 then begin if n>0 then begin loc:=2;
+begin writeln(output);
+write(output,'! Hmm... ',n:1,' of the preceding lines failed to match');
+error;end;end;goto 10;end;end{:133};{131:}begin changelimit:=limit;
+if limit>0 then for k:=0 to limit-1 do changebuffer[k]:=buffer[k];
+end{:131};changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;line:=line+1;
+if not inputln(webfile)then begin begin writeln(output);
+write(output,'! WEB file ended during a change');error;end;
+inputhasended:=true;goto 10;end;if linesdontmatch then n:=n+1;end;
+10:end;{:132}{135:}procedure getline;label 20;
+begin 20:if changing then{137:}begin line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended without @z');error;end;buffer[0]:=64;
+buffer[1]:=122;limit:=2;end;
+if limit>1 then if buffer[0]=64 then begin if(buffer[1]>=88)and(buffer[1
+]<=90)then buffer[1]:=buffer[1]+32;
+if(buffer[1]=120)or(buffer[1]=121)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @z?');error;
+end;end else if buffer[1]=122 then begin primethechangebuffer;
+changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;end;end;end{:137};if not changing then begin{136:}
+begin line:=line+1;
+if not inputln(webfile)then inputhasended:=true else if limit=
+changelimit then if buffer[0]=changebuffer[0]then if changelimit>0 then
+checkchange;end{:136};if changing then goto 20;end;loc:=0;
+buffer[limit]:=32;end;{:135}{139:}
+function controlcode(c:ASCIIcode):eightbits;
+begin case c of 64:controlcode:=64;39:controlcode:=12;
+34:controlcode:=13;36:controlcode:=125;32,9:controlcode:=136;
+42:begin write(output,'*',modulecount+1:1);flush(output);
+controlcode:=136;end;68,100:controlcode:=133;70,102:controlcode:=132;
+123:controlcode:=9;125:controlcode:=10;80,112:controlcode:=134;
+84,116,94,46,58:controlcode:=131;38:controlcode:=127;
+60:controlcode:=135;61:controlcode:=2;92:controlcode:=3;
+otherwise controlcode:=0 end;end;{:139}{140:}
+function skipahead:eightbits;label 30;var c:eightbits;
+begin while true do begin if loc>limit then begin getline;
+if inputhasended then begin c:=136;goto 30;end;end;buffer[limit+1]:=64;
+while buffer[loc]<>64 do loc:=loc+1;if loc<=limit then begin loc:=loc+2;
+c:=controlcode(buffer[loc-1]);if(c<>0)or(buffer[loc-1]=62)then goto 30;
+end;end;30:skipahead:=c;end;{:140}{141:}procedure skipcomment;label 10;
+var bal:eightbits;c:ASCIIcode;begin bal:=0;
+while true do begin if loc>limit then begin getline;
+if inputhasended then begin begin writeln(output);
+write(output,'! Input ended in mid-comment');error;end;goto 10;end;end;
+c:=buffer[loc];loc:=loc+1;{142:}if c=64 then begin c:=buffer[loc];
+if(c<>32)and(c<>9)and(c<>42)and(c<>122)and(c<>90)then loc:=loc+1 else
+begin begin writeln(output);
+write(output,'! Section ended in mid-comment');error;end;loc:=loc-1;
+goto 10;
+end end else if(c=92)and(buffer[loc]<>64)then loc:=loc+1 else if c=123
+then bal:=bal+1 else if c=125 then begin if bal=0 then goto 10;
+bal:=bal-1;end{:142};end;10:end;{:141}{145:}function getnext:eightbits;
+label 20,30,31;var c:eightbits;d:eightbits;j,k:0..longestname;
+begin 20:if loc>limit then begin getline;
+if inputhasended then begin c:=136;goto 31;end;end;c:=buffer[loc];
+loc:=loc+1;if scanninghex then{146:}
+if((c>=48)and(c<=57))or((c>=65)and(c<=70))then goto 31 else scanninghex
+:=false{:146};
+case c of 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,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:{148:}
+begin if((c=101)or(c=69))and(loc>1)then if(buffer[loc-2]<=57)and(buffer[
+loc-2]>=48)then c:=0;if c<>0 then begin loc:=loc-1;idfirst:=loc;
+repeat loc:=loc+1;d:=buffer[loc];
+until((d<48)or((d>57)and(d<65))or((d>90)and(d<97))or(d>122))and(d<>95);
+if loc>idfirst+1 then begin c:=130;idloc:=loc;end;end else c:=69;
+end{:148};34:{149:}begin doublechars:=0;idfirst:=loc-1;
+repeat d:=buffer[loc];loc:=loc+1;
+if(d=34)or(d=64)then if buffer[loc]=d then begin loc:=loc+1;d:=0;
+doublechars:=doublechars+1;
+end else begin if d=64 then begin writeln(output);
+write(output,'! Double @ sign missing');error;
+end end else if loc>limit then begin begin writeln(output);
+write(output,'! String constant didn''t end');error;end;d:=34;end;
+until d=34;idloc:=loc-1;c:=130;end{:149};64:{150:}
+begin c:=controlcode(buffer[loc]);loc:=loc+1;
+if c=0 then goto 20 else if c=13 then scanninghex:=true else if c=135
+then{151:}begin{153:}k:=0;
+while true do begin if loc>limit then begin getline;
+if inputhasended then begin begin writeln(output);
+write(output,'! Input ended in section name');error;end;goto 30;end;end;
+d:=buffer[loc];{154:}if d=64 then begin d:=buffer[loc+1];
+if d=62 then begin loc:=loc+2;goto 30;end;
+if(d=32)or(d=9)or(d=42)then begin begin writeln(output);
+write(output,'! Section name didn''t end');error;end;goto 30;end;k:=k+1;
+modtext[k]:=64;loc:=loc+1;end{:154};loc:=loc+1;
+if k<longestname-1 then k:=k+1;if(d=32)or(d=9)then begin d:=32;
+if modtext[k-1]=32 then k:=k-1;end;modtext[k]:=d;end;30:{155:}
+if k>=longestname-2 then begin begin writeln(output);
+write(output,'! Section name too long: ');end;
+for j:=1 to 25 do write(output,xchr[modtext[j]]);write(output,'...');
+if history=0 then history:=1;end{:155};
+if(modtext[k]=32)and(k>0)then k:=k-1;{:153};
+if k>3 then begin if(modtext[k]=46)and(modtext[k-1]=46)and(modtext[k-2]=
+46)then curmodule:=prefixlookup(k-3)else curmodule:=modlookup(k);
+end else curmodule:=modlookup(k);end{:151}
+else if c=131 then begin repeat c:=skipahead;until c<>64;
+if buffer[loc-1]<>62 then begin writeln(output);
+write(output,'! Improper @ within control text');error;end;goto 20;end;
+end{:150};{147:}
+46:if buffer[loc]=46 then begin if loc<=limit then begin c:=32;
+loc:=loc+1;end;
+end else if buffer[loc]=41 then begin if loc<=limit then begin c:=93;
+loc:=loc+1;end;end;
+58:if buffer[loc]=61 then begin if loc<=limit then begin c:=24;
+loc:=loc+1;end;end;
+61:if buffer[loc]=61 then begin if loc<=limit then begin c:=30;
+loc:=loc+1;end;end;
+62:if buffer[loc]=61 then begin if loc<=limit then begin c:=29;
+loc:=loc+1;end;end;
+60:if buffer[loc]=61 then begin if loc<=limit then begin c:=28;
+loc:=loc+1;end;
+end else if buffer[loc]=62 then begin if loc<=limit then begin c:=26;
+loc:=loc+1;end;end;
+40:if buffer[loc]=42 then begin if loc<=limit then begin c:=9;
+loc:=loc+1;end;
+end else if buffer[loc]=46 then begin if loc<=limit then begin c:=91;
+loc:=loc+1;end;end;
+42:if buffer[loc]=41 then begin if loc<=limit then begin c:=10;
+loc:=loc+1;end;end;{:147}32,9:goto 20;123:begin skipcomment;goto 20;end;
+125:begin begin writeln(output);write(output,'! Extra }');error;end;
+goto 20;end;otherwise if c>=128 then goto 20 else end;
+31:{if troubleshooting then debughelp;}getnext:=c;end;{:145}{157:}
+procedure scannumeric(p:namepointer);label 21,30;
+var accumulator:integer;nextsign:-1..+1;q:namepointer;val:integer;
+begin{158:}accumulator:=0;nextsign:=+1;
+while true do begin nextcontrol:=getnext;
+21:case nextcontrol of 48,49,50,51,52,53,54,55,56,57:begin{160:}val:=0;
+repeat val:=10*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>57)or(nextcontrol<48){:160};
+begin accumulator:=accumulator+nextsign*(val);nextsign:=+1;end;goto 21;
+end;12:begin{161:}val:=0;nextcontrol:=48;
+repeat val:=8*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>55)or(nextcontrol<48){:161};
+begin accumulator:=accumulator+nextsign*(val);nextsign:=+1;end;goto 21;
+end;13:begin{162:}val:=0;nextcontrol:=48;
+repeat if nextcontrol>=65 then nextcontrol:=nextcontrol-7;
+val:=16*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>70)or(nextcontrol<48)or((nextcontrol>57)and(
+nextcontrol<65)){:162};begin accumulator:=accumulator+nextsign*(val);
+nextsign:=+1;end;goto 21;end;130:begin q:=idlookup(0);
+if ilk[q]<>1 then begin nextcontrol:=42;goto 21;end;
+begin accumulator:=accumulator+nextsign*(equiv[q]-32768);nextsign:=+1;
+end;end;43:;45:nextsign:=-nextsign;132,133,135,134,136:goto 30;
+59:begin writeln(output);
+write(output,'! Omit semicolon in numeric definition');error;end;
+otherwise{159:}begin begin writeln(output);
+write(output,'! Improper numeric definition will be flushed');error;end;
+repeat nextcontrol:=skipahead until(nextcontrol>=132);
+if nextcontrol=135 then begin loc:=loc-2;nextcontrol:=getnext;end;
+accumulator:=0;goto 30;end{:159}end;end;30:{:158};
+if abs(accumulator)>=32768 then begin begin writeln(output);
+write(output,'! Value too big: ',accumulator:1);error;end;
+accumulator:=0;end;equiv[p]:=accumulator+32768;end;{:157}{165:}
+procedure scanrepl(t:eightbits);label 22,30,31,21;var a:sixteenbits;
+b:ASCIIcode;bal:eightbits;begin bal:=0;
+while true do begin 22:a:=getnext;case a of 40:bal:=bal+1;
+41:if bal=0 then begin writeln(output);write(output,'! Extra )');error;
+end else bal:=bal-1;39:{168:}begin b:=39;
+while true do begin begin if tokptr[z]=maxtoks then begin writeln(output
+);write(output,'! Sorry, ','token',' capacity exceeded');error;
+history:=3;jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;
+end;
+if b=64 then if buffer[loc]=64 then loc:=loc+1 else begin writeln(output
+);write(output,'! You should double @ signs in strings');error;end;
+if loc=limit then begin begin writeln(output);
+write(output,'! String didn''t end');error;end;buffer[loc]:=39;
+buffer[loc+1]:=0;end;b:=buffer[loc];loc:=loc+1;
+if b=39 then begin if buffer[loc]<>39 then goto 31 else begin loc:=loc+1
+;begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=39;tokptr[z]:=tokptr[z]+1;end;end;end;
+end;31:end{:168};35:if t=3 then a:=0;{167:}130:begin a:=idlookup(0);
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=(a div 256)+128;tokptr[z]:=tokptr[z]+1;
+end;a:=a mod 256;end;
+135:if t<>135 then goto 30 else begin begin if tokptr[z]=maxtoks then
+begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=(curmodule div 256)+168;
+tokptr[z]:=tokptr[z]+1;end;a:=curmodule mod 256;end;2:{169:}
+begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=2;tokptr[z]:=tokptr[z]+1;end;
+buffer[limit+1]:=64;
+21:if buffer[loc]=64 then begin if loc<limit then if buffer[loc+1]=64
+then begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=64;tokptr[z]:=tokptr[z]+1;end;
+loc:=loc+2;goto 21;end;
+end else begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=buffer[loc];tokptr[z]:=tokptr[z]+1;end;
+loc:=loc+1;goto 21;end;if loc>=limit then begin writeln(output);
+write(output,'! Verbatim string didn''t end');error;
+end else if buffer[loc+1]<>62 then begin writeln(output);
+write(output,'! You should double @ signs in verbatim strings');error;
+end;loc:=loc+2;end{:169};
+133,132,134:if t<>135 then goto 30 else begin begin writeln(output);
+write(output,'! @',xchr[buffer[loc-1]],' is ignored in Pascal text');
+error;end;goto 22;end;136:goto 30;{:167}otherwise end;
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=a;tokptr[z]:=tokptr[z]+1;end;end;
+30:nextcontrol:=a;{166:}
+if bal>0 then begin if bal=1 then begin writeln(output);
+write(output,'! Missing )');error;end else begin writeln(output);
+write(output,'! Missing ',bal:1,' )''s');error;end;
+while bal>0 do begin begin if tokptr[z]=maxtoks then begin writeln(
+output);write(output,'! Sorry, ','token',' capacity exceeded');error;
+history:=3;jumpout;end;tokmem[z,tokptr[z]]:=41;tokptr[z]:=tokptr[z]+1;
+end;bal:=bal-1;end;end{:166};
+if textptr>maxtexts-4 then begin writeln(output);
+write(output,'! Sorry, ','text',' capacity exceeded');error;history:=3;
+jumpout;end;currepltext:=textptr;tokstart[textptr+4]:=tokptr[z];
+textptr:=textptr+1;if z=3 then z:=0 else z:=z+1;end;{:165}{170:}
+procedure definemacro(t:eightbits);var p:namepointer;
+begin p:=idlookup(t);scanrepl(t);equiv[p]:=currepltext;
+textlink[currepltext]:=0;end;{:170}{172:}procedure scanmodule;
+label 22,30,10;var p:namepointer;begin modulecount:=modulecount+1;{173:}
+nextcontrol:=0;
+while true do begin 22:while nextcontrol<=132 do begin nextcontrol:=
+skipahead;if nextcontrol=135 then begin loc:=loc-2;nextcontrol:=getnext;
+end;end;if nextcontrol<>133 then goto 30;nextcontrol:=getnext;
+if nextcontrol<>130 then begin begin writeln(output);
+write(output,'! Definition flushed, must start with ',
+'identifier of length > 1');error;end;goto 22;end;nextcontrol:=getnext;
+if nextcontrol=61 then begin scannumeric(idlookup(1));goto 22;
+end else if nextcontrol=30 then begin definemacro(2);goto 22;
+end else{174:}if nextcontrol=40 then begin nextcontrol:=getnext;
+if nextcontrol=35 then begin nextcontrol:=getnext;
+if nextcontrol=41 then begin nextcontrol:=getnext;
+if nextcontrol=61 then begin begin writeln(output);
+write(output,'! Use == for macros');error;end;nextcontrol:=30;end;
+if nextcontrol=30 then begin definemacro(3);goto 22;end;end;end;end;
+{:174};begin writeln(output);
+write(output,'! Definition flushed since it starts badly');error;end;
+end;30:{:173};{175:}case nextcontrol of 134:p:=0;135:begin p:=curmodule;
+{176:}repeat nextcontrol:=getnext;until nextcontrol<>43;
+if(nextcontrol<>61)and(nextcontrol<>30)then begin begin writeln(output);
+write(output,'! Pascal text flushed, = sign is missing');error;end;
+repeat nextcontrol:=skipahead;until nextcontrol=136;goto 10;end{:176};
+end;otherwise goto 10 end;{177:}storetwobytes(53248+modulecount);{:177};
+scanrepl(135);{178:}
+if p=0 then begin textlink[lastunnamed]:=currepltext;
+lastunnamed:=currepltext;
+end else if equiv[p]=0 then equiv[p]:=currepltext else begin p:=equiv[p]
+;while textlink[p]<maxtexts do p:=textlink[p];textlink[p]:=currepltext;
+end;textlink[currepltext]:=maxtexts;{:178};{:175};10:end;{:172}{181:}
+{procedure debughelp;label 888,10;var k:integer;
+begin debugskipped:=debugskipped+1;
+if debugskipped<debugcycle then goto 10;debugskipped:=0;
+while true do begin write(output,'#');flush(output);read(input,ddt);
+if ddt<0 then goto 10 else if ddt=0 then begin goto 888;
+888:ddt:=0;
+end else begin read(input,dd);case ddt of 1:printid(dd);2:printrepl(dd);
+3:for k:=1 to dd do write(output,xchr[buffer[k]]);
+4:for k:=1 to dd do write(output,xchr[modtext[k]]);
+5:for k:=1 to outptr do write(output,xchr[outbuf[k]]);
+6:for k:=1 to dd do write(output,xchr[outcontrib[k]]);
+otherwise write(output,'?')end;end;end;10:end;}{:181}{182:}
+begin initialize;{134:}openinput;line:=0;otherline:=0;changing:=true;
+primethechangebuffer;changing:=not changing;templine:=otherline;
+otherline:=line;line:=templine;limit:=0;loc:=1;buffer[0]:=32;
+inputhasended:=false;{:134};
+writeln(output,'This is TANGLE, Version 4.4 for SunOS');{183:}
+phaseone:=true;modulecount:=0;repeat nextcontrol:=skipahead;
+until nextcontrol=136;while not inputhasended do scanmodule;{138:}
+if changelimit<>0 then begin for ii:=0 to changelimit do buffer[ii]:=
+changebuffer[ii];limit:=changelimit;changing:=true;line:=otherline;
+loc:=changelimit;begin writeln(output);
+write(output,'! Change file entry did not match');error;end;end{:138};
+phaseone:=false;{:183};{for ii:=0 to 3 do maxtokptr[ii]:=tokptr[ii];}
+{112:}if textlink[0]=0 then begin begin writeln(output);
+write(output,'! No output was specified.');end;
+if history=0 then history:=1;end else begin begin writeln(output);
+write(output,'Writing the output file');end;flush(output);{83:}
+stackptr:=1;bracelevel:=0;curstate.namefield:=0;
+curstate.replfield:=textlink[0];zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];curstate.modfield:=0;
+{:83};{96:}outstate:=0;outptr:=0;breakptr:=0;semiptr:=0;outbuf[0]:=0;
+line:=1;{:96};sendtheoutput;{98:}breakptr:=outptr;semiptr:=0;
+flushbuffer;if bracelevel<>0 then begin writeln(output);
+write(output,'! Program ended at brace level ',bracelevel:1);error;end;
+{:98};begin writeln(output);write(output,'Done.');end;end{:112};
+9999:if stringptr>256 then{184:}begin begin writeln(output);
+write(output,stringptr-256:1,' strings written to string pool file.');
+end;write(pool,'*');
+for ii:=1 to 9 do begin outbuf[ii]:=poolchecksum mod 10;
+poolchecksum:=poolchecksum div 10;end;
+for ii:=9 downto 1 do write(pool,xchr[48+outbuf[ii]]);writeln(pool);
+end{:184};{[186:]begin writeln(output);
+write(output,'Memory usage statistics:');end;begin writeln(output);
+write(output,nameptr:1,' names, ',textptr:1,' replacement texts;');end;
+begin writeln(output);write(output,byteptr[0]:1);end;
+for wo:=1 to 2 do write(output,'+',byteptr[wo]:1);
+if phaseone then for ii:=0 to 3 do maxtokptr[ii]:=tokptr[ii];
+write(output,' bytes, ',maxtokptr[0]:1);
+for ii:=1 to 3 do write(output,'+',maxtokptr[ii]:1);
+write(output,' tokens.');[:186];}{187:}
+case history of 0:begin writeln(output);
+write(output,'(No errors were found.)');end;1:begin writeln(output);
+write(output,'(Did you see the warning message above?)');end;
+2:begin writeln(output);
+write(output,'(Pardon me, but I think I spotted something wrong.)');end;
+3:begin writeln(output);
+write(output,'(That was a fatal error, my friend.)');end;end{:187};
+writeln(output);if(history<>0)and(history<>1)then exit(1)else exit(0);
+end.{:182}
diff --git a/systems/knuth/local/web/tangle.p1 b/systems/knuth/local/web/tangle.p1
new file mode 100644
index 0000000000..0eeee9ce50
--- /dev/null
+++ b/systems/knuth/local/web/tangle.p1
@@ -0,0 +1,814 @@
+{2:}{4:}{:4}program TANGLE(input,output);label 9999;const{8:}
+bufsize=100;maxbytes=45000;maxtoks=50000;maxnames=4000;maxtexts=2000;
+hashsize=353;longestname=400;linelength=72;outbufsize=144;stacksize=50;
+maxidlength=50;unambiglength=7;{:8}type{11:}ASCIIcode=integer(8);{:11}
+{37:}eightbits=0..255;sixteenbits=0..65535;{:37}{39:}
+namepointer=0..maxnames;{:39}{43:}textpointer=0..maxtexts;{:43}{78:}
+outputstate=record endfield:sixteenbits;bytefield:sixteenbits;
+namefield:namepointer;replfield:textpointer;modfield:0..12287;end;{:78}
+{190:}UNIXfilename=packed array[1..60]of char;{:190}var{9:}history:0..3;
+{:9}{13:}xord:array[char]of ASCIIcode;xchr:array[ASCIIcode]of char;{:13}
+{23:}webfile:text;changefile:text;{:23}{25:}Pascalfile:text;pool:text;
+{:25}{27:}buffer:array[0..bufsize]of ASCIIcode;{:27}{29:}
+phaseone:boolean;{:29}{38:}
+bytemem:packed array[0..2,0..maxbytes]of ASCIIcode;
+tokmem:packed array[0..3,0..maxtoks]of eightbits;
+bytestart:array[0..maxnames]of sixteenbits;
+tokstart:array[0..maxtexts]of sixteenbits;
+link:array[0..maxnames]of sixteenbits;
+ilk:array[0..maxnames]of sixteenbits;
+equiv:array[0..maxnames]of sixteenbits;
+textlink:array[0..maxtexts]of sixteenbits;{:38}{40:}nameptr:namepointer;
+stringptr:namepointer;byteptr:array[0..2]of 0..maxbytes;
+poolchecksum:integer;{:40}{44:}textptr:textpointer;
+tokptr:array[0..3]of 0..maxtoks;z:0..3;
+{maxtokptr:array[0..3]of 0..maxtoks;}{:44}{50:}idfirst:0..bufsize;
+idloc:0..bufsize;doublechars:0..bufsize;
+hash,chophash:array[0..hashsize]of sixteenbits;
+choppedid:array[0..unambiglength]of ASCIIcode;{:50}{65:}
+modtext:array[0..longestname]of ASCIIcode;{:65}{70:}
+lastunnamed:textpointer;{:70}{79:}curstate:outputstate;
+stack:array[1..stacksize]of outputstate;stackptr:0..stacksize;{:79}{80:}
+zo:0..3;{:80}{82:}bracelevel:eightbits;{:82}{86:}curval:integer;{:86}
+{94:}outbuf:array[0..outbufsize]of ASCIIcode;outptr:0..outbufsize;
+breakptr:0..outbufsize;semiptr:0..outbufsize;{:94}{95:}
+outstate:eightbits;outval,outapp:integer;outsign:ASCIIcode;
+lastsign:-1..+1;{:95}{100:}outcontrib:array[1..linelength]of ASCIIcode;
+{:100}{124:}ii:integer;line:integer;otherline:integer;templine:integer;
+limit:0..bufsize;loc:0..bufsize;inputhasended:boolean;changing:boolean;
+{:124}{126:}changebuffer:array[0..bufsize]of ASCIIcode;
+changelimit:0..bufsize;{:126}{143:}curmodule:namepointer;
+scanninghex:boolean;{:143}{156:}nextcontrol:eightbits;{:156}{164:}
+currepltext:textpointer;{:164}{171:}modulecount:0..12287;{:171}{179:}
+{troubleshooting:boolean;ddt:integer;dd:integer;debugcycle:integer;
+debugskipped:integer;}{:179}{185:}{wo:0..2;}{:185}{189:}
+webname,changename,Pascalname,poolname:UNIXfilename;{:189}
+#include "tangext.h"
+{30:}{procedure debughelp;forward;}{:30}{31:}procedure error;
+var j:0..outbufsize;k,l:0..bufsize;begin if phaseone then{32:}
+begin if changing then write(output,'. (change file ')else write(output,
+'. (');writeln(output,'l.',line:1,')');
+if loc>=limit then l:=limit else l:=loc;
+for k:=1 to l do if buffer[k-1]=9 then write(output,' ')else write(
+output,xchr[buffer[k-1]]);writeln(output);
+for k:=1 to l do write(output,' ');
+for k:=l+1 to limit do write(output,xchr[buffer[k-1]]);
+write(output,' ');end{:32}else{33:}
+begin writeln(output,'. (l.',line:1,')');
+for j:=1 to outptr do write(output,xchr[outbuf[j-1]]);
+write(output,'... ');end{:33};flush(output);history:=2;
+{debugskipped:=debugcycle;debughelp;}end;{:31}{34:}procedure jumpout;
+begin goto 9999;end;{:34}{191:}procedure scanargs;
+var dotpos,i,a:integer;c:char;fname:UNIXfilename;
+foundweb,foundchange:boolean;begin foundweb:=false;foundchange:=false;
+for a:=1 to argc-1 do begin argv(a,fname);
+if fname[1]<>'-'then begin if not foundweb then{192:}begin dotpos:=-1;
+i:=1;while(fname[i]<>' ')and(i<=55)do begin webname[i]:=fname[i];
+if fname[i]='.'then dotpos:=i;i:=i+1;end;
+if dotpos=-1 then begin dotpos:=i;webname[dotpos]:='.';
+webname[dotpos+1]:='w';webname[dotpos+2]:='e';webname[dotpos+3]:='b';
+webname[dotpos+4]:=' ';end else webname[i]:=' ';
+for i:=1 to dotpos do begin c:=webname[i];Pascalname[i]:=c;
+poolname[i]:=c;end;Pascalname[dotpos+1]:='p';Pascalname[dotpos+2]:=' ';
+poolname[dotpos+1]:='p';poolname[dotpos+2]:='o';poolname[dotpos+3]:='o';
+poolname[dotpos+4]:='l';poolname[dotpos+5]:=' ';foundweb:=true;end{:192}
+else if not foundchange then{193:}begin dotpos:=-1;i:=1;
+while(fname[i]<>' ')and(i<=55)do begin changename[i]:=fname[i];
+if fname[i]='.'then dotpos:=i;i:=i+1;end;
+if dotpos=-1 then begin dotpos:=i;changename[dotpos]:='.';
+changename[dotpos+1]:='c';changename[dotpos+2]:='h';
+changename[dotpos+3]:=' ';end else changename[i]:=' ';foundchange:=true;
+end{:193}else{196:}begin begin writeln(output);
+write(output,'! Usage: webfile[.web] [changefile[.ch]]');end;error;
+jumpout;end{:196};end else{195:}begin{196:}begin begin writeln(output);
+write(output,'! Usage: webfile[.web] [changefile[.ch]]');end;error;
+jumpout;end{:196};end{:195};end;if not foundweb then{196:}
+begin begin writeln(output);
+write(output,'! Usage: webfile[.web] [changefile[.ch]]');end;error;
+jumpout;end{:196};if not foundchange then{194:}begin changename[1]:='/';
+changename[2]:='d';changename[3]:='e';changename[4]:='v';
+changename[5]:='/';changename[6]:='n';changename[7]:='u';
+changename[8]:='l';changename[9]:='l';changename[10]:=' ';end{:194};end;
+{:191}procedure initialize;var{16:}i:0..255;{:16}{41:}wi:0..2;{:41}{45:}
+zi:0..3;{:45}{51:}h:0..hashsize;{:51}begin{10:}history:=0;{:10}{14:}
+xchr[32]:=' ';xchr[33]:='!';xchr[34]:='"';xchr[35]:='#';xchr[36]:='$';
+xchr[37]:='%';xchr[38]:='&';xchr[39]:='''';xchr[40]:='(';xchr[41]:=')';
+xchr[42]:='*';xchr[43]:='+';xchr[44]:=',';xchr[45]:='-';xchr[46]:='.';
+xchr[47]:='/';xchr[48]:='0';xchr[49]:='1';xchr[50]:='2';xchr[51]:='3';
+xchr[52]:='4';xchr[53]:='5';xchr[54]:='6';xchr[55]:='7';xchr[56]:='8';
+xchr[57]:='9';xchr[58]:=':';xchr[59]:=';';xchr[60]:='<';xchr[61]:='=';
+xchr[62]:='>';xchr[63]:='?';xchr[64]:='@';xchr[65]:='A';xchr[66]:='B';
+xchr[67]:='C';xchr[68]:='D';xchr[69]:='E';xchr[70]:='F';xchr[71]:='G';
+xchr[72]:='H';xchr[73]:='I';xchr[74]:='J';xchr[75]:='K';xchr[76]:='L';
+xchr[77]:='M';xchr[78]:='N';xchr[79]:='O';xchr[80]:='P';xchr[81]:='Q';
+xchr[82]:='R';xchr[83]:='S';xchr[84]:='T';xchr[85]:='U';xchr[86]:='V';
+xchr[87]:='W';xchr[88]:='X';xchr[89]:='Y';xchr[90]:='Z';xchr[91]:='[';
+xchr[92]:='\';xchr[93]:=']';xchr[94]:='^';xchr[95]:='_';xchr[96]:='`';
+xchr[97]:='a';xchr[98]:='b';xchr[99]:='c';xchr[100]:='d';xchr[101]:='e';
+xchr[102]:='f';xchr[103]:='g';xchr[104]:='h';xchr[105]:='i';
+xchr[106]:='j';xchr[107]:='k';xchr[108]:='l';xchr[109]:='m';
+xchr[110]:='n';xchr[111]:='o';xchr[112]:='p';xchr[113]:='q';
+xchr[114]:='r';xchr[115]:='s';xchr[116]:='t';xchr[117]:='u';
+xchr[118]:='v';xchr[119]:='w';xchr[120]:='x';xchr[121]:='y';
+xchr[122]:='z';xchr[123]:='{';xchr[124]:='|';xchr[125]:='}';
+xchr[126]:='~';xchr[0]:=' ';xchr[127]:=' ';{:14}{17:}
+for i:=1 to 31 do xchr[i]:=chr(i);for i:=128 to 255 do xchr[i]:=chr(i);
+{:17}{18:}for i:=0 to 255 do xord[chr(i)]:=32;
+for i:=1 to 255 do xord[xchr[i]]:=i;xord[' ']:=32;{:18}{21:}{:21}{26:}
+scanargs;rewrite(Pascalfile,Pascalname);rewrite(pool,poolname);{:26}
+{42:}for wi:=0 to 2 do begin bytestart[wi]:=0;byteptr[wi]:=0;end;
+bytestart[3]:=0;nameptr:=1;stringptr:=256;poolchecksum:=271828;{:42}
+{46:}for zi:=0 to 3 do begin tokstart[zi]:=0;tokptr[zi]:=0;end;
+tokstart[4]:=0;textptr:=1;z:=1 mod 4;{:46}{48:}ilk[0]:=0;equiv[0]:=0;
+{:48}{52:}for h:=0 to hashsize-1 do begin hash[h]:=0;chophash[h]:=0;end;
+{:52}{71:}lastunnamed:=0;textlink[0]:=0;{:71}{144:}scanninghex:=false;
+{:144}{152:}modtext[0]:=32;{:152}{180:}{troubleshooting:=true;
+debugcycle:=1;debugskipped:=0;troubleshooting:=false;debugcycle:=99999;}
+{:180}end;{:2}{24:}procedure openinput;begin reset(webfile,webname);
+reset(changefile,changename);end;{:24}{28:}
+function inputln(var f:text):boolean;begin limit:=0;
+if testeof(f)then inputln:=false else begin lineread(f);
+if limit=bufsize then begin limit:=limit-1;begin writeln(output);
+write(output,'! Input line too long');end;loc:=0;error;end;
+inputln:=true;end;end;{:28}{49:}procedure printid(p:namepointer);
+var k:0..maxbytes;w:0..2;
+begin if p>=nameptr then write(output,'IMPOSSIBLE')else begin w:=p mod 3
+;
+for k:=bytestart[p]to bytestart[p+3]-1 do write(output,xchr[bytemem[w,k]
+]);end;end;{:49}{53:}function idlookup(t:eightbits):namepointer;
+label 31,32;var c:eightbits;i:0..bufsize;h:0..hashsize;k:0..maxbytes;
+w:0..2;l:0..bufsize;p,q:namepointer;s:0..unambiglength;
+begin l:=idloc-idfirst;{54:}h:=buffer[idfirst];i:=idfirst+1;
+while i<idloc do begin h:=(h+h+buffer[i])mod hashsize;i:=i+1;end{:54};
+{55:}p:=hash[h];
+while p<>0 do begin if bytestart[p+3]-bytestart[p]=l then{56:}
+begin i:=idfirst;k:=bytestart[p];w:=p mod 3;
+while(i<idloc)and(buffer[i]=bytemem[w,k])do begin i:=i+1;k:=k+1;end;
+if i=idloc then goto 31;end{:56};p:=link[p];end;p:=nameptr;
+link[p]:=hash[h];hash[h]:=p;31:{:55};if(p=nameptr)or(t<>0)then{57:}
+begin if((p<>nameptr)and(t<>0)and(ilk[p]=0))or((p=nameptr)and(t=0)and(
+buffer[idfirst]<>34))then{58:}begin i:=idfirst;s:=0;h:=0;
+while(i<idloc)and(s<unambiglength)do begin if buffer[i]<>95 then begin
+if buffer[i]>=97 then choppedid[s]:=buffer[i]-32 else choppedid[s]:=
+buffer[i];h:=(h+h+choppedid[s])mod hashsize;s:=s+1;end;i:=i+1;end;
+choppedid[s]:=0;end{:58};if p<>nameptr then{59:}
+begin if ilk[p]=0 then begin if t=1 then begin writeln(output);
+write(output,'! This identifier has already appeared');error;end;{60:}
+q:=chophash[h];
+if q=p then chophash[h]:=equiv[p]else begin while equiv[q]<>p do q:=
+equiv[q];equiv[q]:=equiv[p];end{:60};end else begin writeln(output);
+write(output,'! This identifier was defined before');error;end;
+ilk[p]:=t;end{:59}else{61:}
+begin if(t=0)and(buffer[idfirst]<>34)then{62:}begin q:=chophash[h];
+while q<>0 do begin{63:}begin k:=bytestart[q];s:=0;w:=q mod 3;
+while(k<bytestart[q+3])and(s<unambiglength)do begin c:=bytemem[w,k];
+if c<>95 then begin if c>=97 then c:=c-32;
+if choppedid[s]<>c then goto 32;s:=s+1;end;k:=k+1;end;
+if(k=bytestart[q+3])and(choppedid[s]<>0)then goto 32;
+begin writeln(output);write(output,'! Identifier conflict with ');end;
+for k:=bytestart[q]to bytestart[q+3]-1 do write(output,xchr[bytemem[w,k]
+]);error;q:=0;32:end{:63};q:=equiv[q];end;equiv[p]:=chophash[h];
+chophash[h]:=p;end{:62};w:=nameptr mod 3;k:=byteptr[w];
+if k+l>maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;i:=idfirst;while i<idloc do begin bytemem[w,k]:=buffer[i];
+k:=k+1;i:=i+1;end;byteptr[w]:=k;bytestart[nameptr+3]:=k;
+nameptr:=nameptr+1;if buffer[idfirst]<>34 then ilk[p]:=t else{64:}
+begin ilk[p]:=1;
+if l-doublechars=2 then equiv[p]:=buffer[idfirst+1]+32768 else begin
+equiv[p]:=stringptr+32768;l:=l-doublechars-1;
+if l>99 then begin writeln(output);
+write(output,'! Preprocessed string is too long');error;end;
+stringptr:=stringptr+1;write(pool,xchr[48+l div 10],xchr[48+l mod 10]);
+poolchecksum:=poolchecksum+poolchecksum+l;
+while poolchecksum>536870839 do poolchecksum:=poolchecksum-536870839;
+i:=idfirst+1;while i<idloc do begin write(pool,xchr[buffer[i]]);
+poolchecksum:=poolchecksum+poolchecksum+buffer[i];
+while poolchecksum>536870839 do poolchecksum:=poolchecksum-536870839;
+if(buffer[i]=34)or(buffer[i]=64)then i:=i+2 else i:=i+1;end;
+writeln(pool);end;end{:64};end{:61};end{:57};idlookup:=p;end;{:53}{66:}
+function modlookup(l:sixteenbits):namepointer;label 31;var c:0..4;
+j:0..longestname;k:0..maxbytes;w:0..2;p:namepointer;q:namepointer;
+begin c:=2;q:=0;p:=ilk[0];while p<>0 do begin{68:}begin k:=bytestart[p];
+w:=p mod 3;c:=1;j:=1;
+while(k<bytestart[p+3])and(j<=l)and(modtext[j]=bytemem[w,k])do begin k:=
+k+1;j:=j+1;end;
+if k=bytestart[p+3]then if j>l then c:=1 else c:=4 else if j>l then c:=3
+else if modtext[j]<bytemem[w,k]then c:=0 else c:=2;end{:68};q:=p;
+if c=0 then p:=link[q]else if c=2 then p:=ilk[q]else goto 31;end;{67:}
+w:=nameptr mod 3;k:=byteptr[w];
+if k+l>maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;p:=nameptr;if c=0 then link[q]:=p else ilk[q]:=p;link[p]:=0;
+ilk[p]:=0;c:=1;equiv[p]:=0;
+for j:=1 to l do bytemem[w,k+j-1]:=modtext[j];byteptr[w]:=k+l;
+bytestart[nameptr+3]:=k+l;nameptr:=nameptr+1;{:67};
+31:if c<>1 then begin begin writeln(output);
+write(output,'! Incompatible section names');error;end;p:=0;end;
+modlookup:=p;end;{:66}{69:}
+function prefixlookup(l:sixteenbits):namepointer;var c:0..4;
+count:0..maxnames;j:0..longestname;k:0..maxbytes;w:0..2;p:namepointer;
+q:namepointer;r:namepointer;begin q:=0;p:=ilk[0];count:=0;r:=0;
+while p<>0 do begin{68:}begin k:=bytestart[p];w:=p mod 3;c:=1;j:=1;
+while(k<bytestart[p+3])and(j<=l)and(modtext[j]=bytemem[w,k])do begin k:=
+k+1;j:=j+1;end;
+if k=bytestart[p+3]then if j>l then c:=1 else c:=4 else if j>l then c:=3
+else if modtext[j]<bytemem[w,k]then c:=0 else c:=2;end{:68};
+if c=0 then p:=link[p]else if c=2 then p:=ilk[p]else begin r:=p;
+count:=count+1;q:=ilk[p];p:=link[p];end;if p=0 then begin p:=q;q:=0;end;
+end;if count<>1 then if count=0 then begin writeln(output);
+write(output,'! Name does not match');error;
+end else begin writeln(output);write(output,'! Ambiguous prefix');error;
+end;prefixlookup:=r;end;{:69}{73:}
+procedure storetwobytes(x:sixteenbits);
+begin if tokptr[z]+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=x div 256;
+tokmem[z,tokptr[z]+1]:=x mod 256;tokptr[z]:=tokptr[z]+2;end;{:73}{74:}
+{procedure printrepl(p:textpointer);var k:0..maxtoks;a:sixteenbits;
+zp:0..3;
+begin if p>=textptr then write(output,'BAD')else begin k:=tokstart[p];
+zp:=p mod 4;while k<tokstart[p+4]do begin a:=tokmem[zp,k];
+if a>=128 then[75:]begin k:=k+1;
+if a<168 then begin a:=(a-128)*256+tokmem[zp,k];printid(a);
+if bytemem[a mod 3,bytestart[a]]=34 then write(output,'"')else write(
+output,' ');end else if a<208 then begin write(output,'@<');
+printid((a-168)*256+tokmem[zp,k]);write(output,'@>');
+end else begin a:=(a-208)*256+tokmem[zp,k];
+write(output,'@',xchr[123],a:1,'@',xchr[125]);end;
+end[:75]else[76:]case a of 9:write(output,'@',xchr[123]);
+10:write(output,'@',xchr[125]);12:write(output,'@''');
+13:write(output,'@"');125:write(output,'@$');0:write(output,'#');
+64:write(output,'@@');2:write(output,'@=');3:write(output,'@\');
+otherwise write(output,xchr[a])end[:76];k:=k+1;end;end;end;}{:74}{84:}
+procedure pushlevel(p:namepointer);
+begin if stackptr=stacksize then begin writeln(output);
+write(output,'! Sorry, ','stack',' capacity exceeded');error;history:=3;
+jumpout;end else begin stack[stackptr]:=curstate;stackptr:=stackptr+1;
+curstate.namefield:=p;curstate.replfield:=equiv[p];
+zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];curstate.modfield:=0;
+end;end;{:84}{85:}procedure poplevel;label 10;
+begin if textlink[curstate.replfield]=0 then begin if ilk[curstate.
+namefield]=3 then{91:}begin nameptr:=nameptr-1;textptr:=textptr-1;
+z:=textptr mod 4;{if tokptr[z]>maxtokptr[z]then maxtokptr[z]:=tokptr[z];
+}tokptr[z]:=tokstart[textptr];
+{byteptr[nameptr mod 3]:=byteptr[nameptr mod 3]-1;}end{:91};
+end else if textlink[curstate.replfield]<maxtexts then begin curstate.
+replfield:=textlink[curstate.replfield];zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];goto 10;end;
+stackptr:=stackptr-1;if stackptr>0 then begin curstate:=stack[stackptr];
+zo:=curstate.replfield mod 4;end;10:end;{:85}{87:}
+function getoutput:sixteenbits;label 20,30,31;var a:sixteenbits;
+b:eightbits;bal:sixteenbits;k:0..maxbytes;w:0..2;
+begin 20:if stackptr=0 then begin a:=0;goto 31;end;
+if curstate.bytefield=curstate.endfield then begin curval:=-curstate.
+modfield;poplevel;if curval=0 then goto 20;a:=129;goto 31;end;
+a:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;if a<128 then if a=0 then{92:}
+begin pushlevel(nameptr-1);goto 20;end{:92}else goto 31;
+a:=(a-128)*256+tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;if a<10240 then{89:}
+begin case ilk[a]of 0:begin curval:=a;a:=130;end;
+1:begin curval:=equiv[a]-32768;a:=128;end;2:begin pushlevel(a);goto 20;
+end;3:begin{90:}
+while(curstate.bytefield=curstate.endfield)and(stackptr>0)do poplevel;
+if(stackptr=0)or(tokmem[zo,curstate.bytefield]<>40)then begin begin
+writeln(output);write(output,'! No parameter given for ');end;
+printid(a);error;goto 20;end;{93:}bal:=1;
+curstate.bytefield:=curstate.bytefield+1;
+while true do begin b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;
+if b=0 then storetwobytes(nameptr+32767)else begin if b>=128 then begin
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;
+b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;
+end else case b of 40:bal:=bal+1;41:begin bal:=bal-1;
+if bal=0 then goto 30;end;
+39:repeat begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;
+b:=tokmem[zo,curstate.bytefield];
+curstate.bytefield:=curstate.bytefield+1;until b=39;otherwise end;
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;end;end;end;
+30:{:93};equiv[nameptr]:=textptr;ilk[nameptr]:=2;w:=nameptr mod 3;
+k:=byteptr[w];
+if nameptr>maxnames-3 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;bytestart[nameptr+3]:=k;nameptr:=nameptr+1;
+if textptr>maxtexts-4 then begin writeln(output);
+write(output,'! Sorry, ','text',' capacity exceeded');error;history:=3;
+jumpout;end;textlink[textptr]:=0;tokstart[textptr+4]:=tokptr[z];
+textptr:=textptr+1;z:=textptr mod 4{:90};
+pushlevel(a);goto 20;end;
+otherwise begin writeln(output);
+write(output,'! This can''t happen (','output',')');error;history:=3;
+jumpout;end end;goto 31;end{:89};if a<20480 then{88:}begin a:=a-10240;
+if equiv[a]<>0 then pushlevel(a)else if a<>0 then begin begin writeln(
+output);write(output,'! Not present: <');end;printid(a);
+write(output,'>');error;end;goto 20;end{:88};curval:=a-20480;a:=129;
+curstate.modfield:=curval;31:{if troubleshooting then debughelp;}
+getoutput:=a;end;{:87}{97:}procedure flushbuffer;var k:0..outbufsize;
+b:0..outbufsize;begin b:=breakptr;
+if(semiptr<>0)and(outptr-semiptr<=linelength)then breakptr:=semiptr;
+for k:=1 to breakptr do write(Pascalfile,xchr[outbuf[k-1]]);
+writeln(Pascalfile);line:=line+1;
+if line mod 100=0 then begin write(output,'.');
+if line mod 500=0 then write(output,line:1);flush(output);end;
+if breakptr<outptr then begin if outbuf[breakptr]=32 then begin breakptr
+:=breakptr+1;if breakptr>b then b:=breakptr;end;
+for k:=breakptr to outptr-1 do outbuf[k-breakptr]:=outbuf[k];end;
+outptr:=outptr-breakptr;breakptr:=b-breakptr;semiptr:=0;
+if outptr>linelength then begin begin writeln(output);
+write(output,'! Long line must be truncated');error;end;
+outptr:=linelength;end;end;{:97}{99:}procedure appval(v:integer);
+var k:0..outbufsize;begin k:=outbufsize;repeat outbuf[k]:=v mod 10;
+v:=v div 10;k:=k-1;until v=0;repeat k:=k+1;
+begin outbuf[outptr]:=outbuf[k]+48;outptr:=outptr+1;end;
+until k=outbufsize;end;{:99}{101:}procedure sendout(t:eightbits;
+v:sixteenbits);label 20;var k:0..linelength;begin{102:}
+20:case outstate of 1:if t<>3 then begin breakptr:=outptr;
+if t=2 then begin outbuf[outptr]:=32;outptr:=outptr+1;end;end;
+2:begin begin outbuf[outptr]:=44-outapp;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;breakptr:=outptr;end;
+3,4:begin{103:}
+if(outval<0)or((outval=0)and(lastsign<0))then begin outbuf[outptr]:=45;
+outptr:=outptr+1;
+end else if outsign>0 then begin outbuf[outptr]:=outsign;
+outptr:=outptr+1;end;appval(abs(outval));
+if outptr>linelength then flushbuffer;{:103};outstate:=outstate-2;
+goto 20;end;5:{104:}begin if(t=3)or({105:}
+((t=2)and(v=3)and(((outcontrib[1]=68)and(outcontrib[2]=73)and(outcontrib
+[3]=86))or((outcontrib[1]=100)and(outcontrib[2]=105)and(outcontrib[3]=
+118))or((outcontrib[1]=77)and(outcontrib[2]=79)and(outcontrib[3]=68))or(
+(outcontrib[1]=109)and(outcontrib[2]=111)and(outcontrib[3]=100))))or((t=
+0)and((v=42)or(v=47))){:105})then begin{103:}
+if(outval<0)or((outval=0)and(lastsign<0))then begin outbuf[outptr]:=45;
+outptr:=outptr+1;
+end else if outsign>0 then begin outbuf[outptr]:=outsign;
+outptr:=outptr+1;end;appval(abs(outval));
+if outptr>linelength then flushbuffer;{:103};outsign:=43;outval:=outapp;
+end else outval:=outval+outapp;outstate:=3;goto 20;end{:104};
+0:if t<>3 then breakptr:=outptr;otherwise end{:102};
+if t<>0 then for k:=1 to v do begin outbuf[outptr]:=outcontrib[k];
+outptr:=outptr+1;end else begin outbuf[outptr]:=v;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;
+if(t=0)and((v=59)or(v=125))then begin semiptr:=outptr;breakptr:=outptr;
+end;if t>=2 then outstate:=1 else outstate:=0 end;{:101}{106:}
+procedure sendsign(v:integer);
+begin case outstate of 2,4:outapp:=outapp*v;3:begin outapp:=v;
+outstate:=4;end;5:begin outval:=outval+outapp;outapp:=v;outstate:=4;end;
+otherwise begin breakptr:=outptr;outapp:=v;outstate:=2;end end;
+lastsign:=outapp;end;{:106}{107:}procedure sendval(v:integer);
+label 666,10;begin case outstate of 1:begin{110:}
+if(outptr=breakptr+3)or((outptr=breakptr+4)and(outbuf[breakptr]=32))then
+if((outbuf[outptr-3]=68)and(outbuf[outptr-2]=73)and(outbuf[outptr-1]=86)
+)or((outbuf[outptr-3]=100)and(outbuf[outptr-2]=105)and(outbuf[outptr-1]=
+118))or((outbuf[outptr-3]=77)and(outbuf[outptr-2]=79)and(outbuf[outptr-1
+]=68))or((outbuf[outptr-3]=109)and(outbuf[outptr-2]=111)and(outbuf[
+outptr-1]=100))then goto 666{:110};outsign:=32;outstate:=3;outval:=v;
+breakptr:=outptr;lastsign:=+1;end;0:begin{109:}
+if(outptr=breakptr+1)and((outbuf[breakptr]=42)or(outbuf[breakptr]=47))
+then goto 666{:109};outsign:=0;outstate:=3;outval:=v;breakptr:=outptr;
+lastsign:=+1;end;{108:}2:begin outsign:=43;outstate:=3;outval:=outapp*v;
+end;3:begin outstate:=5;outapp:=v;begin writeln(output);
+write(output,'! Two numbers occurred without a sign between them');
+error;end;end;4:begin outstate:=5;outapp:=outapp*v;end;
+5:begin outval:=outval+outapp;outapp:=v;begin writeln(output);
+write(output,'! Two numbers occurred without a sign between them');
+error;end;end;{:108}otherwise goto 666 end;goto 10;666:{111:}
+if v>=0 then begin if outstate=1 then begin breakptr:=outptr;
+begin outbuf[outptr]:=32;outptr:=outptr+1;end;end;appval(v);
+if outptr>linelength then flushbuffer;outstate:=1;
+end else begin begin outbuf[outptr]:=40;outptr:=outptr+1;end;
+begin outbuf[outptr]:=45;outptr:=outptr+1;end;appval(-v);
+begin outbuf[outptr]:=41;outptr:=outptr+1;end;
+if outptr>linelength then flushbuffer;outstate:=0;end{:111};10:end;
+{:107}{113:}procedure sendtheoutput;label 2,21,22;var curchar:eightbits;
+k:0..linelength;j:0..maxbytes;w:0..2;n:integer;
+begin while stackptr>0 do begin curchar:=getoutput;
+21:case curchar of 0:;{116:}
+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,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:begin outcontrib[1]:=curchar;
+sendout(2,1);end;130:begin k:=0;j:=bytestart[curval];w:=curval mod 3;
+while(k<maxidlength)and(j<bytestart[curval+3])do begin k:=k+1;
+outcontrib[k]:=bytemem[w,j];j:=j+1;if outcontrib[k]=95 then k:=k-1;end;
+sendout(2,k);end;{:116}{119:}48,49,50,51,52,53,54,55,56,57:begin n:=0;
+repeat curchar:=curchar-48;if n>=214748364 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=10*n+curchar;
+curchar:=getoutput;until(curchar>57)or(curchar<48);sendval(n);k:=0;
+if curchar=101 then curchar:=69;if curchar=69 then goto 2 else goto 21;
+end;125:sendval(poolchecksum);12:begin n:=0;curchar:=48;
+repeat curchar:=curchar-48;if n>=268435456 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=8*n+curchar;
+curchar:=getoutput;until(curchar>55)or(curchar<48);sendval(n);goto 21;
+end;13:begin n:=0;curchar:=48;
+repeat if curchar>=65 then curchar:=curchar-55 else curchar:=curchar-48;
+if n>=134217728 then begin writeln(output);
+write(output,'! Constant too big');error;end else n:=16*n+curchar;
+curchar:=getoutput;
+until(curchar>70)or(curchar<48)or((curchar>57)and(curchar<65));
+sendval(n);goto 21;end;128:sendval(curval);46:begin k:=1;
+outcontrib[1]:=46;curchar:=getoutput;
+if curchar=46 then begin outcontrib[2]:=46;sendout(1,2);
+end else if(curchar>=48)and(curchar<=57)then goto 2 else begin sendout(0
+,46);goto 21;end;end;{:119}43,45:sendsign(44-curchar);{114:}
+4:begin outcontrib[1]:=97;outcontrib[2]:=110;outcontrib[3]:=100;
+sendout(2,3);end;5:begin outcontrib[1]:=110;outcontrib[2]:=111;
+outcontrib[3]:=116;sendout(2,3);end;6:begin outcontrib[1]:=105;
+outcontrib[2]:=110;sendout(2,2);end;31:begin outcontrib[1]:=111;
+outcontrib[2]:=114;sendout(2,2);end;24:begin outcontrib[1]:=58;
+outcontrib[2]:=61;sendout(1,2);end;26:begin outcontrib[1]:=60;
+outcontrib[2]:=62;sendout(1,2);end;28:begin outcontrib[1]:=60;
+outcontrib[2]:=61;sendout(1,2);end;29:begin outcontrib[1]:=62;
+outcontrib[2]:=61;sendout(1,2);end;30:begin outcontrib[1]:=61;
+outcontrib[2]:=61;sendout(1,2);end;32:begin outcontrib[1]:=46;
+outcontrib[2]:=46;sendout(1,2);end;{:114}39:{117:}begin k:=1;
+outcontrib[1]:=39;repeat if k<linelength then k:=k+1;
+outcontrib[k]:=getoutput;until(outcontrib[k]=39)or(stackptr=0);
+if k=linelength then begin writeln(output);
+write(output,'! String too long');error;end;sendout(1,k);
+curchar:=getoutput;if curchar=39 then outstate:=6;goto 21;end{:117};
+{115:}
+33,34,35,36,37,38,40,41,42,44,47,58,59,60,61,62,63,64,91,92,93,94,95,96,
+123,124{:115}:sendout(0,curchar);{121:}
+9:begin if bracelevel=0 then sendout(0,123)else sendout(0,91);
+bracelevel:=bracelevel+1;end;
+10:if bracelevel>0 then begin bracelevel:=bracelevel-1;
+if bracelevel=0 then sendout(0,125)else sendout(0,93);
+end else begin writeln(output);write(output,'! Extra @}');error;end;
+129:begin if bracelevel=0 then sendout(0,123)else sendout(0,91);
+if curval<0 then begin sendout(0,58);sendval(-curval);
+end else begin sendval(curval);sendout(0,58);end;
+if bracelevel=0 then sendout(0,125)else sendout(0,93);end;{:121}
+127:begin sendout(3,0);outstate:=6;end;2:{118:}begin k:=0;
+repeat if k<linelength then k:=k+1;outcontrib[k]:=getoutput;
+until(outcontrib[k]=2)or(stackptr=0);
+if k=linelength then begin writeln(output);
+write(output,'! Verbatim string too long');error;end;sendout(1,k-1);
+end{:118};3:{122:}begin sendout(1,0);
+while outptr>0 do begin if outptr<=linelength then breakptr:=outptr;
+flushbuffer;end;outstate:=0;end{:122};otherwise begin writeln(output);
+write(output,'! Can''t output ASCII code ',curchar:1);error;end end;
+goto 22;2:{120:}repeat if k<linelength then k:=k+1;
+outcontrib[k]:=curchar;curchar:=getoutput;
+if(outcontrib[k]=69)and((curchar=43)or(curchar=45))then begin if k<
+linelength then k:=k+1;outcontrib[k]:=curchar;curchar:=getoutput;
+end else if curchar=101 then curchar:=69;
+until(curchar<>69)and((curchar<48)or(curchar>57));
+if k=linelength then begin writeln(output);
+write(output,'! Fraction too long');error;end;sendout(3,k);goto 21{:120}
+;22:end;end;{:113}{127:}function linesdontmatch:boolean;label 10;
+var k:0..bufsize;begin linesdontmatch:=true;
+if changelimit<>limit then goto 10;
+if limit>0 then for k:=0 to limit-1 do if changebuffer[k]<>buffer[k]then
+goto 10;linesdontmatch:=false;10:end;{:127}{128:}
+procedure primethechangebuffer;label 22,30,10;var k:0..bufsize;
+begin changelimit:=0;{129:}while true do begin line:=line+1;
+if not inputln(changefile)then goto 10;if limit<2 then goto 22;
+if buffer[0]<>64 then goto 22;
+if(buffer[1]>=88)and(buffer[1]<=90)then buffer[1]:=buffer[1]+32;
+if buffer[1]=120 then goto 30;
+if(buffer[1]=121)or(buffer[1]=122)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @x?');error;
+end;end;22:end;30:{:129};{130:}repeat line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended after @x');error;end;goto 10;end;
+until limit>0;{:130};{131:}begin changelimit:=limit;
+if limit>0 then for k:=0 to limit-1 do changebuffer[k]:=buffer[k];
+end{:131};10:end;{:128}{132:}procedure checkchange;label 10;
+var n:integer;k:0..bufsize;begin if linesdontmatch then goto 10;n:=0;
+while true do begin changing:=not changing;templine:=otherline;
+otherline:=line;line:=templine;line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended before @y');error;end;changelimit:=0;
+changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;goto 10;end;{133:}
+if limit>1 then if buffer[0]=64 then begin if(buffer[1]>=88)and(buffer[1
+]<=90)then buffer[1]:=buffer[1]+32;
+if(buffer[1]=120)or(buffer[1]=122)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @y?');error;
+end;end else if buffer[1]=121 then begin if n>0 then begin loc:=2;
+begin writeln(output);
+write(output,'! Hmm... ',n:1,' of the preceding lines failed to match');
+error;end;end;goto 10;end;end{:133};{131:}begin changelimit:=limit;
+if limit>0 then for k:=0 to limit-1 do changebuffer[k]:=buffer[k];
+end{:131};changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;line:=line+1;
+if not inputln(webfile)then begin begin writeln(output);
+write(output,'! WEB file ended during a change');error;end;
+inputhasended:=true;goto 10;end;if linesdontmatch then n:=n+1;end;
+10:end;{:132}{135:}procedure getline;label 20;
+begin 20:if changing then{137:}begin line:=line+1;
+if not inputln(changefile)then begin begin writeln(output);
+write(output,'! Change file ended without @z');error;end;buffer[0]:=64;
+buffer[1]:=122;limit:=2;end;
+if limit>1 then if buffer[0]=64 then begin if(buffer[1]>=88)and(buffer[1
+]<=90)then buffer[1]:=buffer[1]+32;
+if(buffer[1]=120)or(buffer[1]=121)then begin loc:=2;
+begin writeln(output);write(output,'! Where is the matching @z?');error;
+end;end else if buffer[1]=122 then begin primethechangebuffer;
+changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;end;end;end{:137};if not changing then begin{136:}
+begin line:=line+1;
+if not inputln(webfile)then inputhasended:=true else if limit=
+changelimit then if buffer[0]=changebuffer[0]then if changelimit>0 then
+checkchange;end{:136};if changing then goto 20;end;loc:=0;
+buffer[limit]:=32;end;{:135}{139:}
+function controlcode(c:ASCIIcode):eightbits;
+begin case c of 64:controlcode:=64;39:controlcode:=12;
+34:controlcode:=13;36:controlcode:=125;32,9:controlcode:=136;
+42:begin write(output,'*',modulecount+1:1);flush(output);
+controlcode:=136;end;68,100:controlcode:=133;70,102:controlcode:=132;
+123:controlcode:=9;125:controlcode:=10;80,112:controlcode:=134;
+84,116,94,46,58:controlcode:=131;38:controlcode:=127;
+60:controlcode:=135;61:controlcode:=2;92:controlcode:=3;
+otherwise controlcode:=0 end;end;{:139}{140:}
+function skipahead:eightbits;label 30;var c:eightbits;
+begin while true do begin if loc>limit then begin getline;
+if inputhasended then begin c:=136;goto 30;end;end;buffer[limit+1]:=64;
+while buffer[loc]<>64 do loc:=loc+1;if loc<=limit then begin loc:=loc+2;
+c:=controlcode(buffer[loc-1]);if(c<>0)or(buffer[loc-1]=62)then goto 30;
+end;end;30:skipahead:=c;end;{:140}{141:}procedure skipcomment;label 10;
+var bal:eightbits;c:ASCIIcode;begin bal:=0;
+while true do begin if loc>limit then begin getline;
+if inputhasended then begin begin writeln(output);
+write(output,'! Input ended in mid-comment');error;end;goto 10;end;end;
+c:=buffer[loc];loc:=loc+1;{142:}if c=64 then begin c:=buffer[loc];
+if(c<>32)and(c<>9)and(c<>42)and(c<>122)and(c<>90)then loc:=loc+1 else
+begin begin writeln(output);
+write(output,'! Section ended in mid-comment');error;end;loc:=loc-1;
+goto 10;
+end end else if(c=92)and(buffer[loc]<>64)then loc:=loc+1 else if c=123
+then bal:=bal+1 else if c=125 then begin if bal=0 then goto 10;
+bal:=bal-1;end{:142};end;10:end;{:141}{145:}function getnext:eightbits;
+label 20,30,31;var c:eightbits;d:eightbits;j,k:0..longestname;
+begin 20:if loc>limit then begin getline;
+if inputhasended then begin c:=136;goto 31;end;end;c:=buffer[loc];
+loc:=loc+1;if scanninghex then{146:}
+if((c>=48)and(c<=57))or((c>=65)and(c<=70))then goto 31 else scanninghex
+:=false{:146};
+case c of 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,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:{148:}
+begin if((c=101)or(c=69))and(loc>1)then if(buffer[loc-2]<=57)and(buffer[
+loc-2]>=48)then c:=0;if c<>0 then begin loc:=loc-1;idfirst:=loc;
+repeat loc:=loc+1;d:=buffer[loc];
+until((d<48)or((d>57)and(d<65))or((d>90)and(d<97))or(d>122))and(d<>95);
+if loc>idfirst+1 then begin c:=130;idloc:=loc;end;end else c:=69;
+end{:148};34:{149:}begin doublechars:=0;idfirst:=loc-1;
+repeat d:=buffer[loc];loc:=loc+1;
+if(d=34)or(d=64)then if buffer[loc]=d then begin loc:=loc+1;d:=0;
+doublechars:=doublechars+1;
+end else begin if d=64 then begin writeln(output);
+write(output,'! Double @ sign missing');error;
+end end else if loc>limit then begin begin writeln(output);
+write(output,'! String constant didn''t end');error;end;d:=34;end;
+until d=34;idloc:=loc-1;c:=130;end{:149};64:{150:}
+begin c:=controlcode(buffer[loc]);loc:=loc+1;
+if c=0 then goto 20 else if c=13 then scanninghex:=true else if c=135
+then{151:}begin{153:}k:=0;
+while true do begin if loc>limit then begin getline;
+if inputhasended then begin begin writeln(output);
+write(output,'! Input ended in section name');error;end;goto 30;end;end;
+d:=buffer[loc];{154:}if d=64 then begin d:=buffer[loc+1];
+if d=62 then begin loc:=loc+2;goto 30;end;
+if(d=32)or(d=9)or(d=42)then begin begin writeln(output);
+write(output,'! Section name didn''t end');error;end;goto 30;end;k:=k+1;
+modtext[k]:=64;loc:=loc+1;end{:154};loc:=loc+1;
+if k<longestname-1 then k:=k+1;if(d=32)or(d=9)then begin d:=32;
+if modtext[k-1]=32 then k:=k-1;end;modtext[k]:=d;end;30:{155:}
+if k>=longestname-2 then begin begin writeln(output);
+write(output,'! Section name too long: ');end;
+for j:=1 to 25 do write(output,xchr[modtext[j]]);write(output,'...');
+if history=0 then history:=1;end{:155};
+if(modtext[k]=32)and(k>0)then k:=k-1;{:153};
+if k>3 then begin if(modtext[k]=46)and(modtext[k-1]=46)and(modtext[k-2]=
+46)then curmodule:=prefixlookup(k-3)else curmodule:=modlookup(k);
+end else curmodule:=modlookup(k);end{:151}
+else if c=131 then begin repeat c:=skipahead;until c<>64;
+if buffer[loc-1]<>62 then begin writeln(output);
+write(output,'! Improper @ within control text');error;end;goto 20;end;
+end{:150};{147:}
+46:if buffer[loc]=46 then begin if loc<=limit then begin c:=32;
+loc:=loc+1;end;
+end else if buffer[loc]=41 then begin if loc<=limit then begin c:=93;
+loc:=loc+1;end;end;
+58:if buffer[loc]=61 then begin if loc<=limit then begin c:=24;
+loc:=loc+1;end;end;
+61:if buffer[loc]=61 then begin if loc<=limit then begin c:=30;
+loc:=loc+1;end;end;
+62:if buffer[loc]=61 then begin if loc<=limit then begin c:=29;
+loc:=loc+1;end;end;
+60:if buffer[loc]=61 then begin if loc<=limit then begin c:=28;
+loc:=loc+1;end;
+end else if buffer[loc]=62 then begin if loc<=limit then begin c:=26;
+loc:=loc+1;end;end;
+40:if buffer[loc]=42 then begin if loc<=limit then begin c:=9;
+loc:=loc+1;end;
+end else if buffer[loc]=46 then begin if loc<=limit then begin c:=91;
+loc:=loc+1;end;end;
+42:if buffer[loc]=41 then begin if loc<=limit then begin c:=10;
+loc:=loc+1;end;end;{:147}32,9:goto 20;123:begin skipcomment;goto 20;end;
+125:begin begin writeln(output);write(output,'! Extra }');error;end;
+goto 20;end;otherwise if c>=128 then goto 20 else end;
+31:{if troubleshooting then debughelp;}getnext:=c;end;{:145}{157:}
+procedure scannumeric(p:namepointer);label 21,30;
+var accumulator:integer;nextsign:-1..+1;q:namepointer;val:integer;
+begin{158:}accumulator:=0;nextsign:=+1;
+while true do begin nextcontrol:=getnext;
+21:case nextcontrol of 48,49,50,51,52,53,54,55,56,57:begin{160:}val:=0;
+repeat val:=10*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>57)or(nextcontrol<48){:160};
+begin accumulator:=accumulator+nextsign*(val);nextsign:=+1;end;goto 21;
+end;12:begin{161:}val:=0;nextcontrol:=48;
+repeat val:=8*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>55)or(nextcontrol<48){:161};
+begin accumulator:=accumulator+nextsign*(val);nextsign:=+1;end;goto 21;
+end;13:begin{162:}val:=0;nextcontrol:=48;
+repeat if nextcontrol>=65 then nextcontrol:=nextcontrol-7;
+val:=16*val+nextcontrol-48;nextcontrol:=getnext;
+until(nextcontrol>70)or(nextcontrol<48)or((nextcontrol>57)and(
+nextcontrol<65)){:162};begin accumulator:=accumulator+nextsign*(val);
+nextsign:=+1;end;goto 21;end;130:begin q:=idlookup(0);
+if ilk[q]<>1 then begin nextcontrol:=42;goto 21;end;
+begin accumulator:=accumulator+nextsign*(equiv[q]-32768);nextsign:=+1;
+end;end;43:;45:nextsign:=-nextsign;132,133,135,134,136:goto 30;
+59:begin writeln(output);
+write(output,'! Omit semicolon in numeric definition');error;end;
+otherwise{159:}begin begin writeln(output);
+write(output,'! Improper numeric definition will be flushed');error;end;
+repeat nextcontrol:=skipahead until(nextcontrol>=132);
+if nextcontrol=135 then begin loc:=loc-2;nextcontrol:=getnext;end;
+accumulator:=0;goto 30;end{:159}end;end;30:{:158};
+if abs(accumulator)>=32768 then begin begin writeln(output);
+write(output,'! Value too big: ',accumulator:1);error;end;
+accumulator:=0;end;equiv[p]:=accumulator+32768;end;{:157}{165:}
+procedure scanrepl(t:eightbits);label 22,30,31,21;var a:sixteenbits;
+b:ASCIIcode;bal:eightbits;begin bal:=0;
+while true do begin 22:a:=getnext;case a of 40:bal:=bal+1;
+41:if bal=0 then begin writeln(output);write(output,'! Extra )');error;
+end else bal:=bal-1;39:{168:}begin b:=39;
+while true do begin begin if tokptr[z]=maxtoks then begin writeln(output
+);write(output,'! Sorry, ','token',' capacity exceeded');error;
+history:=3;jumpout;end;tokmem[z,tokptr[z]]:=b;tokptr[z]:=tokptr[z]+1;
+end;
+if b=64 then if buffer[loc]=64 then loc:=loc+1 else begin writeln(output
+);write(output,'! You should double @ signs in strings');error;end;
+if loc=limit then begin begin writeln(output);
+write(output,'! String didn''t end');error;end;buffer[loc]:=39;
+buffer[loc+1]:=0;end;b:=buffer[loc];loc:=loc+1;
+if b=39 then begin if buffer[loc]<>39 then goto 31 else begin loc:=loc+1
+;begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=39;tokptr[z]:=tokptr[z]+1;end;end;end;
+end;31:end{:168};35:if t=3 then a:=0;{167:}130:begin a:=idlookup(0);
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=(a div 256)+128;tokptr[z]:=tokptr[z]+1;
+end;a:=a mod 256;end;
+135:if t<>135 then goto 30 else begin begin if tokptr[z]=maxtoks then
+begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=(curmodule div 256)+168;
+tokptr[z]:=tokptr[z]+1;end;a:=curmodule mod 256;end;2:{169:}
+begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=2;tokptr[z]:=tokptr[z]+1;end;
+buffer[limit+1]:=64;
+21:if buffer[loc]=64 then begin if loc<limit then if buffer[loc+1]=64
+then begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=64;tokptr[z]:=tokptr[z]+1;end;
+loc:=loc+2;goto 21;end;
+end else begin begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=buffer[loc];tokptr[z]:=tokptr[z]+1;end;
+loc:=loc+1;goto 21;end;if loc>=limit then begin writeln(output);
+write(output,'! Verbatim string didn''t end');error;
+end else if buffer[loc+1]<>62 then begin writeln(output);
+write(output,'! You should double @ signs in verbatim strings');error;
+end;loc:=loc+2;end{:169};
+133,132,134:if t<>135 then goto 30 else begin begin writeln(output);
+write(output,'! @',xchr[buffer[loc-1]],' is ignored in Pascal text');
+error;end;goto 22;end;136:goto 30;{:167}otherwise end;
+begin if tokptr[z]=maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[z,tokptr[z]]:=a;tokptr[z]:=tokptr[z]+1;end;end;
+30:nextcontrol:=a;{166:}
+if bal>0 then begin if bal=1 then begin writeln(output);
+write(output,'! Missing )');error;end else begin writeln(output);
+write(output,'! Missing ',bal:1,' )''s');error;end;
+while bal>0 do begin begin if tokptr[z]=maxtoks then begin writeln(
+output);write(output,'! Sorry, ','token',' capacity exceeded');error;
+history:=3;jumpout;end;tokmem[z,tokptr[z]]:=41;tokptr[z]:=tokptr[z]+1;
+end;bal:=bal-1;end;end{:166};
+if textptr>maxtexts-4 then begin writeln(output);
+write(output,'! Sorry, ','text',' capacity exceeded');error;history:=3;
+jumpout;end;currepltext:=textptr;tokstart[textptr+4]:=tokptr[z];
+textptr:=textptr+1;if z=3 then z:=0 else z:=z+1;end;{:165}{170:}
+procedure definemacro(t:eightbits);var p:namepointer;
+begin p:=idlookup(t);scanrepl(t);equiv[p]:=currepltext;
+textlink[currepltext]:=0;end;{:170}{172:}procedure scanmodule;
+label 22,30,10;var p:namepointer;begin modulecount:=modulecount+1;{173:}
+nextcontrol:=0;
+while true do begin 22:while nextcontrol<=132 do begin nextcontrol:=
+skipahead;if nextcontrol=135 then begin loc:=loc-2;nextcontrol:=getnext;
+end;end;if nextcontrol<>133 then goto 30;nextcontrol:=getnext;
+if nextcontrol<>130 then begin begin writeln(output);
+write(output,'! Definition flushed, must start with ',
+'identifier of length > 1');error;end;goto 22;end;nextcontrol:=getnext;
+if nextcontrol=61 then begin scannumeric(idlookup(1));goto 22;
+end else if nextcontrol=30 then begin definemacro(2);goto 22;
+end else{174:}if nextcontrol=40 then begin nextcontrol:=getnext;
+if nextcontrol=35 then begin nextcontrol:=getnext;
+if nextcontrol=41 then begin nextcontrol:=getnext;
+if nextcontrol=61 then begin begin writeln(output);
+write(output,'! Use == for macros');error;end;nextcontrol:=30;end;
+if nextcontrol=30 then begin definemacro(3);goto 22;end;end;end;end;
+{:174};begin writeln(output);
+write(output,'! Definition flushed since it starts badly');error;end;
+end;30:{:173};{175:}case nextcontrol of 134:p:=0;135:begin p:=curmodule;
+{176:}repeat nextcontrol:=getnext;until nextcontrol<>43;
+if(nextcontrol<>61)and(nextcontrol<>30)then begin begin writeln(output);
+write(output,'! Pascal text flushed, = sign is missing');error;end;
+repeat nextcontrol:=skipahead;until nextcontrol=136;goto 10;end{:176};
+end;otherwise goto 10 end;{177:}storetwobytes(53248+modulecount);{:177};
+scanrepl(135);{178:}
+if p=0 then begin textlink[lastunnamed]:=currepltext;
+lastunnamed:=currepltext;
+end else if equiv[p]=0 then equiv[p]:=currepltext else begin p:=equiv[p]
+;while textlink[p]<maxtexts do p:=textlink[p];textlink[p]:=currepltext;
+end;textlink[currepltext]:=maxtexts;{:178};{:175};10:end;{:172}{181:}
+{procedure debughelp;label 888,10;var k:integer;
+begin debugskipped:=debugskipped+1;
+if debugskipped<debugcycle then goto 10;debugskipped:=0;
+while true do begin write(output,'#');flush(output);read(input,ddt);
+if ddt<0 then goto 10 else if ddt=0 then begin goto 888;
+888:ddt:=0;
+end else begin read(input,dd);case ddt of 1:printid(dd);2:printrepl(dd);
+3:for k:=1 to dd do write(output,xchr[buffer[k]]);
+4:for k:=1 to dd do write(output,xchr[modtext[k]]);
+5:for k:=1 to outptr do write(output,xchr[outbuf[k]]);
+6:for k:=1 to dd do write(output,xchr[outcontrib[k]]);
+otherwise write(output,'?')end;end;end;10:end;}{:181}{182:}
+begin initialize;{134:}openinput;line:=0;otherline:=0;changing:=true;
+primethechangebuffer;changing:=not changing;templine:=otherline;
+otherline:=line;line:=templine;limit:=0;loc:=1;buffer[0]:=32;
+inputhasended:=false;{:134};
+writeln(output,'This is TANGLE, Version 4.4 for Linux');{183:}
+phaseone:=true;modulecount:=0;repeat nextcontrol:=skipahead;
+until nextcontrol=136;while not inputhasended do scanmodule;{138:}
+if changelimit<>0 then begin for ii:=0 to changelimit do buffer[ii]:=
+changebuffer[ii];limit:=changelimit;changing:=true;line:=otherline;
+loc:=changelimit;begin writeln(output);
+write(output,'! Change file entry did not match');error;end;end{:138};
+phaseone:=false;{:183};{for ii:=0 to 3 do maxtokptr[ii]:=tokptr[ii];}
+{112:}if textlink[0]=0 then begin begin writeln(output);
+write(output,'! No output was specified.');end;
+if history=0 then history:=1;end else begin begin writeln(output);
+write(output,'Writing the output file');end;flush(output);{83:}
+stackptr:=1;bracelevel:=0;curstate.namefield:=0;
+curstate.replfield:=textlink[0];zo:=curstate.replfield mod 4;
+curstate.bytefield:=tokstart[curstate.replfield];
+curstate.endfield:=tokstart[curstate.replfield+4];curstate.modfield:=0;
+{:83};{96:}outstate:=0;outptr:=0;breakptr:=0;semiptr:=0;outbuf[0]:=0;
+line:=1;{:96};sendtheoutput;{98:}breakptr:=outptr;semiptr:=0;
+flushbuffer;if bracelevel<>0 then begin writeln(output);
+write(output,'! Program ended at brace level ',bracelevel:1);error;end;
+{:98};begin writeln(output);write(output,'Done.');end;end{:112};
+9999:if stringptr>256 then{184:}begin begin writeln(output);
+write(output,stringptr-256:1,' strings written to string pool file.');
+end;write(pool,'*');
+for ii:=1 to 9 do begin outbuf[ii]:=poolchecksum mod 10;
+poolchecksum:=poolchecksum div 10;end;
+for ii:=9 downto 1 do write(pool,xchr[48+outbuf[ii]]);writeln(pool);
+end{:184};{[186:]begin writeln(output);
+write(output,'Memory usage statistics:');end;begin writeln(output);
+write(output,nameptr:1,' names, ',textptr:1,' replacement texts;');end;
+begin writeln(output);write(output,byteptr[0]:1);end;
+for wo:=1 to 2 do write(output,'+',byteptr[wo]:1);
+if phaseone then for ii:=0 to 3 do maxtokptr[ii]:=tokptr[ii];
+write(output,' bytes, ',maxtokptr[0]:1);
+for ii:=1 to 3 do write(output,'+',maxtokptr[ii]:1);
+write(output,' tokens.');[:186];}{187:}
+case history of 0:begin writeln(output);
+write(output,'(No errors were found.)');end;1:begin writeln(output);
+write(output,'(Did you see the warning message above?)');end;
+2:begin writeln(output);
+write(output,'(Pardon me, but I think I spotted something wrong.)');end;
+3:begin writeln(output);
+write(output,'(That was a fatal error, my friend.)');end;end{:187};
+writeln(output);if(history<>0)and(history<>1)then exit(1)else exit(0);
+end.{:182}
diff --git a/systems/knuth/local/web/weave.ch b/systems/knuth/local/web/weave.ch
new file mode 100644
index 0000000000..9f36a11f90
--- /dev/null
+++ b/systems/knuth/local/web/weave.ch
@@ -0,0 +1,598 @@
+% Change file for the WEAVE processor, for use with GNU Pascal.
+% This file was created by Don Knuth, based on the original
+% UNIX port by Howard Trickey, Pavel Curtis, et al.
+% (See ../web-sparc for details of that work.)
+
+% History:
+% 2000.04.29 Original version
+
+% NOTE: The module numbers refer to the standard WEB manual (CS 980).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [0] WEAVE: print changes only
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+\pageno=\contentspagenumber \advance\pageno by 1
+@y
+\pageno=\contentspagenumber \advance\pageno by 1
+\let\maybe=\iffalse
+\def\title{WEAVE changes for{\mc GNU} Pascal}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [1] Change banner message
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d banner=='This is WEAVE, Version 4.4'
+@y
+@d banner=='This is WEAVE, Version 4.4 for Linux'
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [2] add input and output, remove other files, add ref to scan_args,
+% [2] and #include external definition for exit(), etc.
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+program WEAVE(@!web_file,@!change_file,@!tex_file);
+label end_of_WEAVE; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@<Error handling procedures@>@/
+@y
+program WEAVE(@!input,@!output);
+label end_of_WEAVE; {go here to finish}
+const @<Constants in the outer block@>@/
+type @<Types in the outer block@>@/
+var @<Globals in the outer block@>@/
+@\
+@=#include "tangext.h"@>
+@\@/
+@<Error handling procedures@>@/
+@<Declaration of |scan_args|@>@/
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [3] Show statistics
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d stat==@{ {change this to `$\\{stat}\equiv\null$'
+ when gathering usage statistics}
+@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$'
+ when gathering usage statistics}
+@y
+@d stat== {I'm gathering usage statistics}
+@d tats== {because I have to know how close \TeX\ comes to the limits}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [4] compiler options
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
+@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
+@y
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [7] Fix others:
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d othercases == others: {default for cases not listed explicitly}
+@y
+@d othercases == otherwise {ISO extended Pascal default cases}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [8] Constants: Make stack_size=400 instead of 200
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!stack_size=200; {number of simultaneous output levels}
+@y
+@!stack_size=400; {number of simultaneous output levels}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [11] make ASCII codes take 1 byte (not 4 or more!)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!ASCII_code=0..255; {eight-bit numbers, a subrange of the integers}
+@y
+@!ASCII_code=ByteCard; {eight-bit numbers, a subrange of the integers}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [12] make the compiler recognize a text file as text
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+
+@<Types...@>=
+@!text_file=packed file of text_char;
+@y
+@d text_char == char {the data type of characters in text files}
+@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
+@d last_text_char=255 {ordinal number of the largest element of |text_char|}
+@d text_file==text
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [13] xchr and xord now external
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@!xord: array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+@y
+@!xord: external array [text_char] of ASCII_code;
+ {specifies conversion of input characters}
+@!xchr: external array [ASCII_code] of text_char;
+ {specifies conversion of output characters}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [17] enable maximum character set
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+for i:=1 to @'37 do xchr[i]:=' ';
+for i:=@'200 to @'377 do xchr[i]:=' ';
+@y
+for i:=1 to @'37 do xchr[i]:=chr(i);
+for i:=@'200 to @'377 do xchr[i]:=chr(i);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [20] terminal output: use standard i/o
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@y
+@d term_out==output
+@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
+@z
+
+@x
+@<Globals...@>=
+@!term_out:text_file; {the terminal as an output file}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [21] init terminal
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal. Here is one way to do this
+on the \PASCAL\ system that was used in \.{TANGLE}'s initial development:
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
+@y
+@ Different systems have different ways of specifying that the output on a
+certain file will appear on the user's terminal.
+@^system dependencies@>
+
+@<Set init...@>=
+ {Nothing need be done on Linux}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [22] flush terminal buffer
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d update_terminal == break(term_out) {empty the terminal output buffer}
+@y
+@d update_terminal == flush_stdout {empty the terminal output buffer}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [24] open input files
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens the input 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. We will see below that
+\.{WEAVE} reads through the entire input twice.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read |web_file| and |change_file|}
+begin reset(web_file); reset(change_file);
+end;
+@y
+@ The following code opens the input files.
+This is called after |scan_args| has filled the file name variables
+appropriately.
+@^system dependencies@>
+
+@p procedure open_input; {prepare to read |web_file| and |change_file|}
+begin
+reset(web_file,web_name); reset(change_file,change_name);
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [26] opening tex file
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@ The following code opens |tex_file|.
+Since this file was listed in the program header, we assume that the
+\PASCAL\ runtime system has checked that a suitable external file name has
+been given.
+@^system dependencies@>
+
+@<Set init...@>=
+rewrite(tex_file);
+@y
+@ The following code opens |tex_file|.
+The |scan_args| procedure is used to set up |tex_name| as required.
+@^system dependencies@>
+
+@<Set init...@>=
+scan_args;
+rewrite(tex_file,tex_name);
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [27] buffer now external
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Globals...@>=@!buffer: array[0..long_buf_size] of ASCII_code;
+@y
+@<Globals...@>=@!buffer: external array[0..long_buf_size] of ASCII_code;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [28] faster input_ln
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+var final_limit:0..buf_size; {|limit| without trailing blanks}
+begin limit:=0; final_limit:=0;
+if eof(f) then input_ln:=false
+else begin while not eoln(f) do
+ begin buffer[limit]:=xord[f^]; get(f);
+ incr(limit);
+ if buffer[limit-1]<>" " then final_limit:=limit;
+ if limit=buf_size then
+ begin while not eoln(f) do get(f);
+ decr(limit); {keep |buffer[buf_size]| empty}
+ if final_limit>limit then final_limit:=limit;
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ end;
+ read_ln(f); limit:=final_limit; input_ln:=true;
+ end;
+end;
+@y
+With Linux we call an external C procedure, |line_read|.
+That routine fills |buffer| from 0 onwards with the |xord|'ed values
+of the next line, setting |limit| appropriately (ignoring trailing
+blanks).
+It will stop if |limit=buf_size|, and the following will cause an error
+message.
+
+@p function input_ln(var f:text_file):boolean;
+ {inputs a line or returns |false|}
+begin limit:=0;
+if test_eof(f) then input_ln:=false
+else begin line_read(f);
+ if limit=buf_size then
+ begin
+ decr(limit); {keep |buffer[buf_size]| empty}
+ print_nl('! Input line too long'); loc:=0; error;
+@.Input line too long@>
+ end;
+ input_ln:=true;
+ end;
+end;
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [50] don't enter xrefs if no_xref set
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@d append_xref(#)==if xref_ptr=max_refs then overflow('cross reference')
+ else begin incr(xref_ptr); num(xref_ptr):=#;
+ end
+
+@p procedure new_xref(@!p:name_pointer);
+label exit;
+var q:xref_number; {pointer to previous cross reference}
+@!m,@!n: sixteen_bits; {new and previous cross-reference value}
+begin if (reserved(p)or(byte_start[p]+1=byte_start[p+ww]))and
+@y
+If the user has sent the |no_xref| flag (the -x option of the command line),
+then it is unnecessary to keep track of cross references for identifers.
+If one were careful, one could probably make more changes around module
+100 to avoid a lot of identifier looking up.
+
+@d append_xref(#)==if xref_ptr=max_refs then overflow('cross reference')
+ else begin incr(xref_ptr); num(xref_ptr):=#;
+ end
+
+@p procedure new_xref(@!p:name_pointer);
+label exit;
+var q:xref_number; {pointer to previous cross-reference}
+@!m,@!n: sixteen_bits; {new and previous cross-reference value}
+begin if no_xref then return;
+if (reserved(p)or(byte_start[p]+1=byte_start[p+ww]))and
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [53] Work around a bug in GPC version 2.8.1
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@t\hskip1em@>@!tok_mem: packed array [0..max_toks] of sixteen_bits; {tokens}
+@y
+@t\hskip1em@>@!tok_mem: array [0..max_toks] of sixteen_bits; {tokens}
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [185] Work around a bug in GPC version 2.8.1
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reswitch: case next_control of
+string,verbatim: @<Append a \(string scrap@>;
+identifier: @<Append an identifier scrap@>;
+TeX_string: @<Append a \TeX\ string scrap@>;
+othercases easy_cases
+endcases
+@y
+reswitch: if (next_control=string)or(next_control=verbatim)
+ then @<Append a \(string scrap@>
+else if (next_control=identifier) then @<Append an identifier scrap@>
+else if (next_control=TeX_string) then @<Append a \TeX\ string scrap@>
+else easy_cases
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [239] omit index and module names if no_xref set
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@<Phase III: Output the cross-reference index@>=
+@y
+If the user has set the |no_xref| flag (the -x option on the command line),
+just finish off the page, omitting the index, module name list, and table of
+contents.
+
+@<Phase III: Output the cross-reference index@>=
+if no_xref then begin
+ finish_line;
+ out("\"); out5("v")("f")("i")("l")("l");
+ out4("\")("e")("n")("d");
+ finish_line;
+ end
+else begin
+@z
+
+@x
+print('Done.');
+@y
+end;
+print('Done.');
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [258] term_in == input, when debugging
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+any error stop will set |debug_cycle| to zero.
+@y
+any error stop will set |debug_cycle| to zero.
+
+@d term_in==input
+@z
+
+@x
+@!term_in:text_file; {the user's terminal as an input file}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [259] Take out reset(term_in)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+reset(term_in,'TTY:','/I'); {open |term_in| as the terminal, don't do a |get|}
+@y
+
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [261] print newline at end of run and exit based upon value of history
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@p procedure Phase_I;
+@y
+@d UNIXexit==e@&x@&i@&t
+
+@p procedure Phase_I;
+@z
+
+@x
+end.
+@y
+new_line;
+if (history <> spotless) and (history <> harmless_message) then
+ UNIXexit(1)
+else
+ UNIXexit(0);
+end.
+@z
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+% [264] system dependent changes--the scan_args procedure
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+@x
+@* System-dependent changes.
+This module should be replaced, if necessary, by changes to the program
+that are necessary to make \.{WEAVE} work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous modules preserve the module numbering; then everybody's version
+will be consistent with the printed program. More extensive changes,
+which introduce new modules, can be inserted here; then only the index
+itself will get a new module number.
+@^system dependencies@>
+@y
+@* System-dependent changes.
+@^system dependencies@>
+
+@ The user calls \.{WEAVE} with arguments on the command line.
+These are either file names or flags (beginning with '-').
+The following globals are for communicating the user's desires to the rest
+of the program. The various |file_name| variables contain strings with
+the full names of those files, as UNIX knows them.
+
+The only flag that affects weave is |'-x'| whose status is kept in |no_xref|.
+
+@d max_file_name_length==60
+
+@<Globals...@>=
+@!web_name,@!change_name,@!tex_name:
+ array[1..max_file_name_length] of char;
+@!no_xref:boolean;
+
+@ @<Types...@>=
+UNIX_filename=packed array[1..max_file_name_length] of char;
+
+@ The |scan_args| procedure looks at the command line arguments and sets
+the |file_name| variables accordingly.
+At least one file name must be present: the \.{WEB} file. It may have
+an extension, or it may omit it to get |'.web'| added.
+The \TeX\ output file name is formed by replacing the \.{WEB} file
+name extension by |'.tex'|.
+
+If there is another file name present among the arguments, it is the
+change file, again either with an extension or without one to get |'.ch'|
+An omitted change file argument means that |'/dev/null'| should be used,
+when no changes are desired.
+
+An argument beginning with a minus sign is a flag.
+Any letters following the minus sign may cause global flag variables to be
+set.
+Currently, an |x| means that the cross referencing information---the index,
+the module name list, and the table of contents---is to be suppressed.
+
+@<Declaration of |scan_args|@>=
+procedure scan_args;
+var dot_pos,i,a: integer; {indices}
+@!fname: UNIX_filename; {temporary argument holder}
+@!found_web,@!found_change: boolean; {|true| when those file names have
+ been seen}
+begin
+found_web:=false;
+found_change:=false;
+no_xref:=false;
+for a:=1 to argc-1 do begin
+ argv(a,fname); {put argument number |a| into |fname|}
+ if fname[1]<>'-' then begin
+ if not found_web then
+ @<Get |web_name| and |tex_name| from |fname|@>
+ else if not found_change then
+ @<Get |change_name| from |fname|@>
+ else @<Print usage error message and quit@>;
+ end
+ else @<Handle flag argument in |fname|@>;
+ end;
+if not found_web then @<Print usage error message and quit@>;
+if not found_change then @<Set up null change file@>;
+end;
+
+@ Use all of |fname| for the |web_name| if there is a |'.'| in it,
+otherwise add |'.web'|.
+The other file names come from adding things after the dot.
+The |argv| procedure will not put more than |max_file_name_length-5|
+characters into |fname|, and this leaves enough room in the |file_name|
+variables to add the extensions.
+
+The end of a file name is marked with a |chr(0)|, the convention assumed by
+the |argv| and |reset| and |rewrite| procedures.
+
+@<Get |web_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>chr(0)) and (i<=max_file_name_length-5) do begin
+ web_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ web_name[dot_pos]:='.';
+ web_name[dot_pos+1]:='w';
+ web_name[dot_pos+2]:='e';
+ web_name[dot_pos+3]:='b';
+ web_name[dot_pos+4]:=chr(0);
+ end
+else web_name[i]:=chr(0);
+for i:=1 to dot_pos do begin
+ tex_name[i]:=web_name[i];
+ end;
+tex_name[dot_pos+1]:='t';
+tex_name[dot_pos+2]:='e';
+tex_name[dot_pos+3]:='x';
+tex_name[dot_pos+4]:=chr(0);
+found_web:=true;
+end
+
+@ @<Get |change_name|...@>=
+begin
+dot_pos:=-1;
+i:=1;
+while (fname[i]<>chr(0)) and (i<=max_file_name_length-5)
+do begin
+ change_name[i]:=fname[i];
+ if fname[i]='.' then dot_pos:=i;
+ incr(i);
+ end;
+if dot_pos=-1 then begin
+ dot_pos:=i;
+ change_name[dot_pos]:='.';
+ change_name[dot_pos+1]:='c';
+ change_name[dot_pos+2]:='h';
+ change_name[dot_pos+3]:=chr(0);
+ end
+else change_name[i]:=chr(0);
+found_change:=true;
+end
+
+@ @<Set up null...@>=
+begin
+ change_name[1]:='/';
+ change_name[2]:='d';
+ change_name[3]:='e';
+ change_name[4]:='v';
+ change_name[5]:='/';
+ change_name[6]:='n';
+ change_name[7]:='u';
+ change_name[8]:='l';
+ change_name[9]:='l';
+ change_name[10]:=chr(0);
+end
+
+@ @<Handle flag...@>=
+begin
+i:=2;
+while (fname[i]<>chr(0)) and (i<=max_file_name_length-5) do begin
+ if fname[i]='x' then no_xref:=true;
+ incr(i);
+ end;
+end
+
+@ @<Print usage error message and quit@>=
+begin
+print_nl('! Usage: weave [-x] webfile[.web] [changefile[.ch]]');
+error;
+jump_out;
+end
+
+@z
diff --git a/systems/knuth/local/web/weave.p b/systems/knuth/local/web/weave.p
new file mode 100644
index 0000000000..27ee89cd87
--- /dev/null
+++ b/systems/knuth/local/web/weave.p
@@ -0,0 +1,1603 @@
+{2:}{4:}{:4}program WEAVE(input,output);label 9999;const{8:}
+maxbytes=45000;maxnames=5000;maxmodules=2000;hashsize=353;bufsize=100;
+longestname=400;longbufsize=500;linelength=80;maxrefs=30000;
+maxtoks=30000;maxtexts=2000;maxscraps=1000;stacksize=400;{:8}type{11:}
+ASCIIcode=ByteCard;{:11}{36:}eightbits=0..255;sixteenbits=0..65535;{:36}
+{38:}namepointer=0..maxnames;{:38}{47:}xrefnumber=0..maxrefs;{:47}{52:}
+textpointer=0..maxtexts;{:52}{201:}mode=0..1;
+outputstate=record endfield:sixteenbits;tokfield:sixteenbits;
+modefield:mode;end;{:201}{266:}UNIXfilename=packed array[1..60]of char;
+{:266}var{9:}history:0..3;{:9}{13:}
+xord:external array[char]of ASCIIcode;
+xchr:external array[ASCIIcode]of char;{:13}{23:}webfile:text;
+changefile:text;{:23}{25:}texfile:text;{:25}{27:}
+buffer:external array[0..longbufsize]of ASCIIcode;{:27}{29:}
+phaseone:boolean;phasethree:boolean;{:29}{37:}
+bytemem:packed array[0..1,0..maxbytes]of ASCIIcode;
+bytestart:array[0..maxnames]of sixteenbits;
+link:array[0..maxnames]of sixteenbits;
+ilk:array[0..maxnames]of sixteenbits;
+xref:array[0..maxnames]of sixteenbits;{:37}{39:}nameptr:namepointer;
+byteptr:array[0..1]of 0..maxbytes;{:39}{45:}modulecount:0..maxmodules;
+changedmodule:packed array[0..maxmodules]of boolean;
+changeexists:boolean;{:45}{48:}
+xmem:array[xrefnumber]of packed record numfield:sixteenbits;
+xlinkfield:sixteenbits;end;xrefptr:xrefnumber;
+xrefswitch,modxrefswitch:0..10240;{:48}{53:}
+tokmem:array[0..maxtoks]of sixteenbits;
+tokstart:array[textpointer]of sixteenbits;textptr:textpointer;
+tokptr:0..maxtoks;maxtokptr,maxtxtptr:0..maxtoks;{:53}{55:}
+idfirst:0..longbufsize;idloc:0..longbufsize;
+hash:array[0..hashsize]of sixteenbits;{:55}{63:}curname:namepointer;
+{:63}{65:}modtext:array[0..longestname]of ASCIIcode;{:65}{71:}
+ii:integer;line:integer;otherline:integer;templine:integer;
+limit:0..longbufsize;loc:0..longbufsize;inputhasended:boolean;
+changing:boolean;changepending:boolean;{:71}{73:}
+changebuffer:array[0..bufsize]of ASCIIcode;changelimit:0..bufsize;{:73}
+{93:}curmodule:namepointer;scanninghex:boolean;{:93}{108:}
+nextcontrol:eightbits;{:108}{114:}lhs,rhs:namepointer;{:114}{118:}
+curxref:xrefnumber;{:118}{121:}outbuf:array[0..linelength]of ASCIIcode;
+outptr:0..linelength;outline:integer;{:121}{129:}dig:array[0..4]of 0..9;
+{:129}{144:}cat:array[0..maxscraps]of eightbits;
+trans:array[0..maxscraps]of 0..10239;pp:0..maxscraps;
+scrapbase:0..maxscraps;scrapptr:0..maxscraps;loptr:0..maxscraps;
+hiptr:0..maxscraps;maxscrptr:0..maxscraps;{:144}{177:}{tracing:0..2;}
+{:177}{202:}curstate:outputstate;
+stack:array[1..stacksize]of outputstate;stackptr:0..stacksize;
+maxstackptr:0..stacksize;{:202}{219:}saveline:integer;
+saveplace:sixteenbits;{:219}{229:}thismodule:namepointer;{:229}{234:}
+nextxref,thisxref,firstxref,midxref:xrefnumber;{:234}{240:}
+kmodule:0..maxmodules;{:240}{242:}bucket:array[ASCIIcode]of namepointer;
+nextname:namepointer;c:ASCIIcode;h:0..hashsize;
+blink:array[0..maxnames]of sixteenbits;{:242}{244:}curdepth:eightbits;
+curbyte:0..maxbytes;curbank:0..1;curval:sixteenbits;
+maxsortptr:0..maxscraps;{:244}{246:}collate:array[0..229]of ASCIIcode;
+{:246}{258:}{troubleshooting:boolean;ddt:integer;dd:integer;
+debugcycle:integer;debugskipped:integer;}{:258}{265:}
+webname,changename,texname:array[1..60]of char;noxref:boolean;{:265}
+#include "tangext.h"
+{30:}{procedure debughelp;forward;}{:30}{31:}procedure error;
+var k,l:0..longbufsize;begin{32:}
+begin if changing then write(output,'. (change file ')else write(output,
+'. (');writeln(output,'l.',line:1,')');
+if loc>=limit then l:=limit else l:=loc;
+for k:=1 to l do if buffer[k-1]=9 then write(output,' ')else write(
+output,xchr[buffer[k-1]]);writeln(output);
+for k:=1 to l do write(output,' ');
+for k:=l+1 to limit do write(output,xchr[buffer[k-1]]);
+if buffer[limit]=124 then write(output,xchr[124]);write(output,' ');
+end{:32};flushstdout;history:=2;{debugskipped:=debugcycle;debughelp;}
+end;{:31}{33:}procedure jumpout;begin goto 9999;end;{:33}{267:}
+procedure scanargs;var dotpos,i,a:integer;fname:UNIXfilename;
+foundweb,foundchange:boolean;begin foundweb:=false;foundchange:=false;
+noxref:=false;for a:=1 to argc-1 do begin argv(a,fname);
+if fname[1]<>'-'then begin if not foundweb then{268:}begin dotpos:=-1;
+i:=1;while(fname[i]<>chr(0))and(i<=55)do begin webname[i]:=fname[i];
+if fname[i]='.'then dotpos:=i;i:=i+1;end;
+if dotpos=-1 then begin dotpos:=i;webname[dotpos]:='.';
+webname[dotpos+1]:='w';webname[dotpos+2]:='e';webname[dotpos+3]:='b';
+webname[dotpos+4]:=chr(0);end else webname[i]:=chr(0);
+for i:=1 to dotpos do begin texname[i]:=webname[i];end;
+texname[dotpos+1]:='t';texname[dotpos+2]:='e';texname[dotpos+3]:='x';
+texname[dotpos+4]:=chr(0);foundweb:=true;end{:268}
+else if not foundchange then{269:}begin dotpos:=-1;i:=1;
+while(fname[i]<>chr(0))and(i<=55)do begin changename[i]:=fname[i];
+if fname[i]='.'then dotpos:=i;i:=i+1;end;
+if dotpos=-1 then begin dotpos:=i;changename[dotpos]:='.';
+changename[dotpos+1]:='c';changename[dotpos+2]:='h';
+changename[dotpos+3]:=chr(0);end else changename[i]:=chr(0);
+foundchange:=true;end{:269}else{272:}begin begin writeln(output);
+write(output,'! Usage: weave [-x] webfile[.web] [changefile[.ch]]');end;
+error;jumpout;end{:272};end else{271:}begin i:=2;
+while(fname[i]<>chr(0))and(i<=55)do begin if fname[i]='x'then noxref:=
+true;i:=i+1;end;end{:271};end;if not foundweb then{272:}
+begin begin writeln(output);
+write(output,'! Usage: weave [-x] webfile[.web] [changefile[.ch]]');end;
+error;jumpout;end{:272};if not foundchange then{270:}
+begin changename[1]:='/';changename[2]:='d';changename[3]:='e';
+changename[4]:='v';changename[5]:='/';changename[6]:='n';
+changename[7]:='u';changename[8]:='l';changename[9]:='l';
+changename[10]:=chr(0);end{:270};end;{:267}procedure initialize;var{16:}
+i:0..255;{:16}{40:}wi:0..1;{:40}{56:}h:0..hashsize;{:56}{247:}
+c:ASCIIcode;{:247}begin{10:}history:=0;{:10}{14:}xchr[32]:=' ';
+xchr[33]:='!';xchr[34]:='"';xchr[35]:='#';xchr[36]:='$';xchr[37]:='%';
+xchr[38]:='&';xchr[39]:='''';xchr[40]:='(';xchr[41]:=')';xchr[42]:='*';
+xchr[43]:='+';xchr[44]:=',';xchr[45]:='-';xchr[46]:='.';xchr[47]:='/';
+xchr[48]:='0';xchr[49]:='1';xchr[50]:='2';xchr[51]:='3';xchr[52]:='4';
+xchr[53]:='5';xchr[54]:='6';xchr[55]:='7';xchr[56]:='8';xchr[57]:='9';
+xchr[58]:=':';xchr[59]:=';';xchr[60]:='<';xchr[61]:='=';xchr[62]:='>';
+xchr[63]:='?';xchr[64]:='@';xchr[65]:='A';xchr[66]:='B';xchr[67]:='C';
+xchr[68]:='D';xchr[69]:='E';xchr[70]:='F';xchr[71]:='G';xchr[72]:='H';
+xchr[73]:='I';xchr[74]:='J';xchr[75]:='K';xchr[76]:='L';xchr[77]:='M';
+xchr[78]:='N';xchr[79]:='O';xchr[80]:='P';xchr[81]:='Q';xchr[82]:='R';
+xchr[83]:='S';xchr[84]:='T';xchr[85]:='U';xchr[86]:='V';xchr[87]:='W';
+xchr[88]:='X';xchr[89]:='Y';xchr[90]:='Z';xchr[91]:='[';xchr[92]:='\';
+xchr[93]:=']';xchr[94]:='^';xchr[95]:='_';xchr[96]:='`';xchr[97]:='a';
+xchr[98]:='b';xchr[99]:='c';xchr[100]:='d';xchr[101]:='e';
+xchr[102]:='f';xchr[103]:='g';xchr[104]:='h';xchr[105]:='i';
+xchr[106]:='j';xchr[107]:='k';xchr[108]:='l';xchr[109]:='m';
+xchr[110]:='n';xchr[111]:='o';xchr[112]:='p';xchr[113]:='q';
+xchr[114]:='r';xchr[115]:='s';xchr[116]:='t';xchr[117]:='u';
+xchr[118]:='v';xchr[119]:='w';xchr[120]:='x';xchr[121]:='y';
+xchr[122]:='z';xchr[123]:='{';xchr[124]:='|';xchr[125]:='}';
+xchr[126]:='~';xchr[0]:=' ';xchr[127]:=' ';{:14}{17:}
+for i:=1 to 31 do xchr[i]:=chr(i);for i:=128 to 255 do xchr[i]:=chr(i);
+{:17}{18:}for i:=0 to 255 do xord[chr(i)]:=32;
+for i:=1 to 255 do xord[xchr[i]]:=i;xord[' ']:=32;{:18}{21:}{:21}{26:}
+scanargs;rewrite(texfile,texname);{:26}{41:}
+for wi:=0 to 1 do begin bytestart[wi]:=0;byteptr[wi]:=0;end;
+bytestart[2]:=0;nameptr:=1;{:41}{43:}ilk[0]:=0;{:43}{49:}xrefptr:=0;
+xrefswitch:=0;modxrefswitch:=0;xmem[0].numfield:=0;xref[0]:=0;{:49}{54:}
+tokptr:=1;textptr:=1;tokstart[0]:=1;tokstart[1]:=1;maxtokptr:=1;
+maxtxtptr:=1;{:54}{57:}for h:=0 to hashsize-1 do hash[h]:=0;{:57}{94:}
+scanninghex:=false;{:94}{102:}modtext[0]:=32;{:102}{124:}outptr:=1;
+outline:=1;outbuf[1]:=99;write(texfile,'\input webma');{:124}{126:}
+outbuf[0]:=92;{:126}{145:}scrapbase:=1;scrapptr:=0;maxscrptr:=0;{:145}
+{203:}maxstackptr:=0;{:203}{245:}maxsortptr:=0;{:245}{248:}
+collate[0]:=0;collate[1]:=32;for c:=1 to 31 do collate[c+1]:=c;
+for c:=33 to 47 do collate[c]:=c;for c:=58 to 64 do collate[c-10]:=c;
+for c:=91 to 94 do collate[c-36]:=c;collate[59]:=96;
+for c:=123 to 255 do collate[c-63]:=c;collate[193]:=95;
+for c:=97 to 122 do collate[c+97]:=c;
+for c:=48 to 57 do collate[c+172]:=c;{:248}{259:}{troubleshooting:=true;
+debugcycle:=1;debugskipped:=0;tracing:=0;troubleshooting:=false;
+debugcycle:=99999;}{:259}end;{:2}{24:}procedure openinput;
+begin reset(webfile,webname);reset(changefile,changename);end;{:24}{28:}
+function inputln(var f:text):boolean;begin limit:=0;
+if testeof(f)then inputln:=false else begin lineread(f);
+if limit=bufsize then begin limit:=limit-1;begin writeln(output);
+write(output,'! Input line too long');end;loc:=0;error;end;
+inputln:=true;end;end;{:28}{44:}procedure printid(p:namepointer);
+var k:0..maxbytes;w:0..1;
+begin if p>=nameptr then write(output,'IMPOSSIBLE')else begin w:=p mod 2
+;
+for k:=bytestart[p]to bytestart[p+2]-1 do write(output,xchr[bytemem[w,k]
+]);end;end;{:44}{50:}procedure newxref(p:namepointer);label 10;
+var q:xrefnumber;m,n:sixteenbits;begin if noxref then goto 10;
+if((ilk[p]>3)or(bytestart[p]+1=bytestart[p+2]))and(xrefswitch=0)then
+goto 10;m:=modulecount+xrefswitch;xrefswitch:=0;q:=xref[p];
+if q>0 then begin n:=xmem[q].numfield;
+if(n=m)or(n=m+10240)then goto 10 else if m=n+10240 then begin xmem[q].
+numfield:=m;goto 10;end;end;
+if xrefptr=maxrefs then begin writeln(output);
+write(output,'! Sorry, ','cross reference',' capacity exceeded');error;
+history:=3;jumpout;end else begin xrefptr:=xrefptr+1;
+xmem[xrefptr].numfield:=m;end;xmem[xrefptr].xlinkfield:=q;
+xref[p]:=xrefptr;10:end;{:50}{51:}procedure newmodxref(p:namepointer);
+var q,r:xrefnumber;begin q:=xref[p];r:=0;
+if q>0 then begin if modxrefswitch=0 then while xmem[q].numfield>=10240
+do begin r:=q;q:=xmem[q].xlinkfield;
+end else if xmem[q].numfield>=10240 then begin r:=q;
+q:=xmem[q].xlinkfield;end;end;
+if xrefptr=maxrefs then begin writeln(output);
+write(output,'! Sorry, ','cross reference',' capacity exceeded');error;
+history:=3;jumpout;end else begin xrefptr:=xrefptr+1;
+xmem[xrefptr].numfield:=modulecount+modxrefswitch;end;
+xmem[xrefptr].xlinkfield:=q;modxrefswitch:=0;
+if r=0 then xref[p]:=xrefptr else xmem[r].xlinkfield:=xrefptr;end;{:51}
+{58:}function idlookup(t:eightbits):namepointer;label 31;
+var i:0..longbufsize;h:0..hashsize;k:0..maxbytes;w:0..1;
+l:0..longbufsize;p:namepointer;begin l:=idloc-idfirst;{59:}
+h:=buffer[idfirst];i:=idfirst+1;
+while i<idloc do begin h:=(h+h+buffer[i])mod hashsize;i:=i+1;end{:59};
+{60:}p:=hash[h];
+while p<>0 do begin if(bytestart[p+2]-bytestart[p]=l)and((ilk[p]=t)or((t
+=0)and(ilk[p]>3)))then{61:}begin i:=idfirst;k:=bytestart[p];w:=p mod 2;
+while(i<idloc)and(buffer[i]=bytemem[w,k])do begin i:=i+1;k:=k+1;end;
+if i=idloc then goto 31;end{:61};p:=link[p];end;p:=nameptr;
+link[p]:=hash[h];hash[h]:=p;31:{:60};if p=nameptr then{62:}
+begin w:=nameptr mod 2;
+if byteptr[w]+l>maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;if nameptr+2>maxnames then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;i:=idfirst;k:=byteptr[w];
+while i<idloc do begin bytemem[w,k]:=buffer[i];k:=k+1;i:=i+1;end;
+byteptr[w]:=k;bytestart[nameptr+2]:=k;nameptr:=nameptr+1;ilk[p]:=t;
+xref[p]:=0;end{:62};idlookup:=p;end;{:58}{66:}
+function modlookup(l:sixteenbits):namepointer;label 31;var c:0..4;
+j:0..longestname;k:0..maxbytes;w:0..1;p:namepointer;q:namepointer;
+begin c:=2;q:=0;p:=ilk[0];while p<>0 do begin{68:}begin k:=bytestart[p];
+w:=p mod 2;c:=1;j:=1;
+while(k<bytestart[p+2])and(j<=l)and(modtext[j]=bytemem[w,k])do begin k:=
+k+1;j:=j+1;end;
+if k=bytestart[p+2]then if j>l then c:=1 else c:=4 else if j>l then c:=3
+else if modtext[j]<bytemem[w,k]then c:=0 else c:=2;end{:68};q:=p;
+if c=0 then p:=link[q]else if c=2 then p:=ilk[q]else goto 31;end;{67:}
+w:=nameptr mod 2;k:=byteptr[w];
+if k+l>maxbytes then begin writeln(output);
+write(output,'! Sorry, ','byte memory',' capacity exceeded');error;
+history:=3;jumpout;end;if nameptr>maxnames-2 then begin writeln(output);
+write(output,'! Sorry, ','name',' capacity exceeded');error;history:=3;
+jumpout;end;p:=nameptr;if c=0 then link[q]:=p else ilk[q]:=p;link[p]:=0;
+ilk[p]:=0;xref[p]:=0;c:=1;for j:=1 to l do bytemem[w,k+j-1]:=modtext[j];
+byteptr[w]:=k+l;bytestart[nameptr+2]:=k+l;nameptr:=nameptr+1;{:67};
+31:if c<>1 then begin begin if not phaseone then begin writeln(output);
+write(output,'! Incompatible section names');error;end;end;p:=0;end;
+modlookup:=p;end;{:66}{69:}
+function prefixlookup(l:sixteenbits):namepointer;var c:0..4;
+count:0..maxnames;j:0..longestname;k:0..maxbytes;w:0..1;p:namepointer;
+q:namepointer;r:namepointer;begin q:=0;p:=ilk[0];count:=0;r:=0;
+while p<>0 do begin{68:}begin k:=bytestart[p];w:=p mod 2;c:=1;j:=1;
+while(k<bytestart[p+2])and(j<=l)and(modtext[j]=bytemem[w,k])do begin k:=
+k+1;j:=j+1;end;
+if k=bytestart[p+2]then if j>l then c:=1 else c:=4 else if j>l then c:=3
+else if modtext[j]<bytemem[w,k]then c:=0 else c:=2;end{:68};
+if c=0 then p:=link[p]else if c=2 then p:=ilk[p]else begin r:=p;
+count:=count+1;q:=ilk[p];p:=link[p];end;if p=0 then begin p:=q;q:=0;end;
+end;if count<>1 then if count=0 then begin if not phaseone then begin
+writeln(output);write(output,'! Name does not match');error;end;
+end else begin if not phaseone then begin writeln(output);
+write(output,'! Ambiguous prefix');error;end;end;prefixlookup:=r;end;
+{:69}{74:}function linesdontmatch:boolean;label 10;var k:0..bufsize;
+begin linesdontmatch:=true;if changelimit<>limit then goto 10;
+if limit>0 then for k:=0 to limit-1 do if changebuffer[k]<>buffer[k]then
+goto 10;linesdontmatch:=false;10:end;{:74}{75:}
+procedure primethechangebuffer;label 22,30,10;var k:0..bufsize;
+begin changelimit:=0;{76:}while true do begin line:=line+1;
+if not inputln(changefile)then goto 10;if limit<2 then goto 22;
+if buffer[0]<>64 then goto 22;
+if(buffer[1]>=88)and(buffer[1]<=90)then buffer[1]:=buffer[1]+32;
+if buffer[1]=120 then goto 30;
+if(buffer[1]=121)or(buffer[1]=122)then begin loc:=2;
+begin if not phaseone then begin writeln(output);
+write(output,'! Where is the matching @x?');error;end;end;end;22:end;
+30:{:76};{77:}repeat line:=line+1;
+if not inputln(changefile)then begin begin if not phaseone then begin
+writeln(output);write(output,'! Change file ended after @x');error;end;
+end;goto 10;end;until limit>0;{:77};{78:}begin changelimit:=limit;
+if limit>0 then for k:=0 to limit-1 do changebuffer[k]:=buffer[k];
+end{:78};10:end;{:75}{79:}procedure checkchange;label 10;var n:integer;
+k:0..bufsize;begin if linesdontmatch then goto 10;changepending:=false;
+if not changedmodule[modulecount]then begin loc:=0;buffer[limit]:=33;
+while(buffer[loc]=32)or(buffer[loc]=9)do loc:=loc+1;buffer[limit]:=32;
+if buffer[loc]=64 then if(buffer[loc+1]=42)or(buffer[loc+1]=32)or(buffer
+[loc+1]=9)then changepending:=true;
+if not changepending then changedmodule[modulecount]:=true;end;n:=0;
+while true do begin changing:=not changing;templine:=otherline;
+otherline:=line;line:=templine;line:=line+1;
+if not inputln(changefile)then begin begin if not phaseone then begin
+writeln(output);write(output,'! Change file ended before @y');error;end;
+end;changelimit:=0;changing:=not changing;templine:=otherline;
+otherline:=line;line:=templine;goto 10;end;{80:}
+if limit>1 then if buffer[0]=64 then begin if(buffer[1]>=88)and(buffer[1
+]<=90)then buffer[1]:=buffer[1]+32;
+if(buffer[1]=120)or(buffer[1]=122)then begin loc:=2;
+begin if not phaseone then begin writeln(output);
+write(output,'! Where is the matching @y?');error;end;end;
+end else if buffer[1]=121 then begin if n>0 then begin loc:=2;
+begin if not phaseone then begin writeln(output);
+write(output,'! Hmm... ',n:1,' of the preceding lines failed to match');
+error;end;end;end;goto 10;end;end{:80};{78:}begin changelimit:=limit;
+if limit>0 then for k:=0 to limit-1 do changebuffer[k]:=buffer[k];
+end{:78};changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;line:=line+1;
+if not inputln(webfile)then begin begin if not phaseone then begin
+writeln(output);write(output,'! WEB file ended during a change');error;
+end;end;inputhasended:=true;goto 10;end;if linesdontmatch then n:=n+1;
+end;10:end;{:79}{81:}procedure resetinput;begin openinput;line:=0;
+otherline:=0;changing:=true;primethechangebuffer;changing:=not changing;
+templine:=otherline;otherline:=line;line:=templine;limit:=0;loc:=1;
+buffer[0]:=32;inputhasended:=false;end;{:81}{82:}procedure getline;
+label 20;begin 20:if changing then{84:}begin line:=line+1;
+if not inputln(changefile)then begin begin if not phaseone then begin
+writeln(output);write(output,'! Change file ended without @z');error;
+end;end;buffer[0]:=64;buffer[1]:=122;limit:=2;end;
+if limit>0 then begin if changepending then begin loc:=0;
+buffer[limit]:=33;while(buffer[loc]=32)or(buffer[loc]=9)do loc:=loc+1;
+buffer[limit]:=32;
+if buffer[loc]=64 then if(buffer[loc+1]=42)or(buffer[loc+1]=32)or(buffer
+[loc+1]=9)then changepending:=false;
+if changepending then begin changedmodule[modulecount]:=true;
+changepending:=false;end;end;buffer[limit]:=32;
+if buffer[0]=64 then begin if(buffer[1]>=88)and(buffer[1]<=90)then
+buffer[1]:=buffer[1]+32;
+if(buffer[1]=120)or(buffer[1]=121)then begin loc:=2;
+begin if not phaseone then begin writeln(output);
+write(output,'! Where is the matching @z?');error;end;end;
+end else if buffer[1]=122 then begin primethechangebuffer;
+changing:=not changing;templine:=otherline;otherline:=line;
+line:=templine;end;end;end;end{:84};if not changing then begin{83:}
+begin line:=line+1;
+if not inputln(webfile)then inputhasended:=true else if limit=
+changelimit then if buffer[0]=changebuffer[0]then if changelimit>0 then
+checkchange;end{:83};if changing then goto 20;end;loc:=0;
+buffer[limit]:=32;end;{:82}{87:}
+function controlcode(c:ASCIIcode):eightbits;
+begin case c of 64:controlcode:=64;39:controlcode:=12;
+34:controlcode:=13;36:controlcode:=135;32,9,42:controlcode:=147;
+61:controlcode:=2;92:controlcode:=3;68,100:controlcode:=144;
+70,102:controlcode:=143;123:controlcode:=9;125:controlcode:=10;
+80,112:controlcode:=145;38:controlcode:=136;60:controlcode:=146;
+62:begin begin if not phaseone then begin writeln(output);
+write(output,'! Extra @>');error;end;end;controlcode:=0;end;
+84,116:controlcode:=134;33:controlcode:=126;63:controlcode:=125;
+94:controlcode:=131;58:controlcode:=132;46:controlcode:=133;
+44:controlcode:=137;124:controlcode:=138;47:controlcode:=139;
+35:controlcode:=140;43:controlcode:=141;59:controlcode:=142;{88:}
+{48,49,50:begin tracing:=c-48;controlcode:=0;end;}{:88}
+otherwise begin begin if not phaseone then begin writeln(output);
+write(output,'! Unknown control code');error;end;end;controlcode:=0;
+end end;end;{:87}{89:}procedure skiplimbo;label 10;var c:ASCIIcode;
+begin while true do if loc>limit then begin getline;
+if inputhasended then goto 10;end else begin buffer[limit+1]:=64;
+while buffer[loc]<>64 do loc:=loc+1;if loc<=limit then begin loc:=loc+2;
+c:=buffer[loc-1];if(c=32)or(c=9)or(c=42)then goto 10;end;end;10:end;
+{:89}{90:}function skipTeX:eightbits;label 30;var c:eightbits;
+begin while true do begin if loc>limit then begin getline;
+if inputhasended then begin c:=147;goto 30;end;end;buffer[limit+1]:=64;
+repeat c:=buffer[loc];loc:=loc+1;if c=124 then goto 30;until c=64;
+if loc<=limit then begin c:=controlcode(buffer[loc]);loc:=loc+1;goto 30;
+end;end;30:skipTeX:=c;end;{:90}{91:}
+function skipcomment(bal:eightbits):eightbits;label 30;var c:ASCIIcode;
+begin while true do begin if loc>limit then begin getline;
+if inputhasended then begin bal:=0;goto 30;end;end;c:=buffer[loc];
+loc:=loc+1;if c=124 then goto 30;{92:}if c=64 then begin c:=buffer[loc];
+if(c<>32)and(c<>9)and(c<>42)then loc:=loc+1 else begin loc:=loc-1;
+bal:=0;goto 30;
+end end else if(c=92)and(buffer[loc]<>64)then loc:=loc+1 else if c=123
+then bal:=bal+1 else if c=125 then begin bal:=bal-1;
+if bal=0 then goto 30;end{:92};end;30:skipcomment:=bal;end;{:91}{95:}
+function getnext:eightbits;label 20,30,31;var c:eightbits;d:eightbits;
+j,k:0..longestname;begin 20:if loc>limit then begin getline;
+if inputhasended then begin c:=147;goto 31;end;end;c:=buffer[loc];
+loc:=loc+1;if scanninghex then{96:}
+if((c>=48)and(c<=57))or((c>=65)and(c<=70))then goto 31 else scanninghex
+:=false{:96};
+case c of 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,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:{98:}
+begin if((c=69)or(c=101))and(loc>1)then if(buffer[loc-2]<=57)and(buffer[
+loc-2]>=48)then c:=128;if c<>128 then begin loc:=loc-1;idfirst:=loc;
+repeat loc:=loc+1;d:=buffer[loc];
+until((d<48)or((d>57)and(d<65))or((d>90)and(d<97))or(d>122))and(d<>95);
+c:=130;idloc:=loc;end;end{:98};39,34:{99:}begin idfirst:=loc-1;
+repeat d:=buffer[loc];loc:=loc+1;
+if loc>limit then begin begin if not phaseone then begin writeln(output)
+;write(output,'! String constant didn''t end');error;end;end;loc:=limit;
+d:=c;end;until d=c;idloc:=loc;c:=129;end{:99};64:{100:}
+begin c:=controlcode(buffer[loc]);loc:=loc+1;
+if c=126 then begin xrefswitch:=10240;goto 20;
+end else if c=125 then begin xrefswitch:=0;goto 20;
+end else if(c<=134)and(c>=131)then{106:}begin idfirst:=loc;
+buffer[limit+1]:=64;while buffer[loc]<>64 do loc:=loc+1;idloc:=loc;
+if loc>limit then begin begin if not phaseone then begin writeln(output)
+;write(output,'! Control text didn''t end');error;end;end;loc:=limit;
+end else begin loc:=loc+2;
+if buffer[loc-1]<>62 then begin if not phaseone then begin writeln(
+output);write(output,'! Control codes are forbidden in control text');
+error;end;end;end;end{:106}
+else if c=13 then scanninghex:=true else if c=146 then{101:}begin{103:}
+k:=0;while true do begin if loc>limit then begin getline;
+if inputhasended then begin begin if not phaseone then begin writeln(
+output);write(output,'! Input ended in section name');error;end;end;
+loc:=1;goto 30;end;end;d:=buffer[loc];{104:}
+if d=64 then begin d:=buffer[loc+1];if d=62 then begin loc:=loc+2;
+goto 30;end;
+if(d=32)or(d=9)or(d=42)then begin begin if not phaseone then begin
+writeln(output);write(output,'! Section name didn''t end');error;end;
+end;goto 30;end;k:=k+1;modtext[k]:=64;loc:=loc+1;end{:104};loc:=loc+1;
+if k<longestname-1 then k:=k+1;if(d=32)or(d=9)then begin d:=32;
+if modtext[k-1]=32 then k:=k-1;end;modtext[k]:=d;end;30:{105:}
+if k>=longestname-2 then begin begin writeln(output);
+write(output,'! Section name too long: ');end;
+for j:=1 to 25 do write(output,xchr[modtext[j]]);write(output,'...');
+if history=0 then history:=1;end{:105};
+if(modtext[k]=32)and(k>0)then k:=k-1{:103};
+if k>3 then begin if(modtext[k]=46)and(modtext[k-1]=46)and(modtext[k-2]=
+46)then curmodule:=prefixlookup(k-3)else curmodule:=modlookup(k);
+end else curmodule:=modlookup(k);xrefswitch:=0;end{:101}
+else if c=2 then{107:}begin idfirst:=loc;loc:=loc+1;buffer[limit+1]:=64;
+buffer[limit+2]:=62;
+while(buffer[loc]<>64)or(buffer[loc+1]<>62)do loc:=loc+1;
+if loc>=limit then begin if not phaseone then begin writeln(output);
+write(output,'! Verbatim string didn''t end');error;end;end;idloc:=loc;
+loc:=loc+2;end{:107};end{:100};{97:}
+46:if buffer[loc]=46 then begin if loc<=limit then begin c:=32;
+loc:=loc+1;end;
+end else if buffer[loc]=41 then begin if loc<=limit then begin c:=93;
+loc:=loc+1;end;end;
+58:if buffer[loc]=61 then begin if loc<=limit then begin c:=24;
+loc:=loc+1;end;end;
+61:if buffer[loc]=61 then begin if loc<=limit then begin c:=30;
+loc:=loc+1;end;end;
+62:if buffer[loc]=61 then begin if loc<=limit then begin c:=29;
+loc:=loc+1;end;end;
+60:if buffer[loc]=61 then begin if loc<=limit then begin c:=28;
+loc:=loc+1;end;
+end else if buffer[loc]=62 then begin if loc<=limit then begin c:=26;
+loc:=loc+1;end;end;
+40:if buffer[loc]=42 then begin if loc<=limit then begin c:=9;
+loc:=loc+1;end;
+end else if buffer[loc]=46 then begin if loc<=limit then begin c:=91;
+loc:=loc+1;end;end;
+42:if buffer[loc]=41 then begin if loc<=limit then begin c:=10;
+loc:=loc+1;end;end;{:97}32,9:goto 20;
+125:begin begin if not phaseone then begin writeln(output);
+write(output,'! Extra }');error;end;end;goto 20;end;
+otherwise if c>=128 then goto 20 else end;
+31:{if troubleshooting then debughelp;}getnext:=c;end;{:95}{111:}
+procedure Pascalxref;label 10;var p:namepointer;
+begin while nextcontrol<143 do begin if(nextcontrol>=130)and(nextcontrol
+<=133)then begin p:=idlookup(nextcontrol-130);newxref(p);
+if(ilk[p]=17)or(ilk[p]=22)then xrefswitch:=10240;end;
+nextcontrol:=getnext;if(nextcontrol=124)or(nextcontrol=123)then goto 10;
+end;10:end;{:111}{112:}procedure outerxref;var bal:eightbits;
+begin while nextcontrol<143 do if nextcontrol<>123 then Pascalxref else
+begin bal:=skipcomment(1);nextcontrol:=124;
+while bal>0 do begin Pascalxref;
+if nextcontrol=124 then bal:=skipcomment(bal)else bal:=0;end;end;end;
+{:112}{119:}procedure modcheck(p:namepointer);
+begin if p>0 then begin modcheck(link[p]);curxref:=xref[p];
+if xmem[curxref].numfield<10240 then begin begin writeln(output);
+write(output,'! Never defined: <');end;printid(p);write(output,'>');
+if history=0 then history:=1;end;
+while xmem[curxref].numfield>=10240 do curxref:=xmem[curxref].xlinkfield
+;if curxref=0 then begin begin writeln(output);
+write(output,'! Never used: <');end;printid(p);write(output,'>');
+if history=0 then history:=1;end;modcheck(ilk[p]);end;end;{:119}{122:}
+procedure flushbuffer(b:eightbits;percent,carryover:boolean);
+label 30,31;var j,k:0..linelength;begin j:=b;
+if not percent then while true do begin if j=0 then goto 30;
+if outbuf[j]<>32 then goto 30;j:=j-1;end;
+30:for k:=1 to j do write(texfile,xchr[outbuf[k]]);
+if percent then write(texfile,xchr[37]);writeln(texfile);
+outline:=outline+1;
+if carryover then for k:=1 to j do if outbuf[k]=37 then if(k=1)or(outbuf
+[k-1]<>92)then begin outbuf[b]:=37;b:=b-1;goto 31;end;
+31:if(b<outptr)then for k:=b+1 to outptr do outbuf[k-b]:=outbuf[k];
+outptr:=outptr-b;end;{:122}{123:}procedure finishline;label 10;
+var k:0..bufsize;
+begin if outptr>0 then flushbuffer(outptr,false,false)else begin for k:=
+0 to limit do if(buffer[k]<>32)and(buffer[k]<>9)then goto 10;
+flushbuffer(0,false,false);end;10:end;{:123}{127:}procedure breakout;
+label 10;var k:0..linelength;d:ASCIIcode;begin k:=outptr;
+while true do begin if k=0 then{128:}begin begin writeln(output);
+write(output,'! Line had to be broken (output l.',outline:1);end;
+writeln(output,'):');
+for k:=1 to outptr-1 do write(output,xchr[outbuf[k]]);writeln(output);
+if history=0 then history:=1;flushbuffer(outptr-1,true,true);goto 10;
+end{:128};d:=outbuf[k];if d=32 then begin flushbuffer(k,false,true);
+goto 10;end;
+if(d=92)and(outbuf[k-1]<>92)then begin flushbuffer(k-1,true,true);
+goto 10;end;k:=k-1;end;10:end;{:127}{130:}procedure outmod(m:integer);
+var k:0..5;a:integer;begin k:=0;a:=m;repeat dig[k]:=a mod 10;
+a:=a div 10;k:=k+1;until a=0;repeat k:=k-1;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=dig[k]+48;end;until k=0;
+if changedmodule[m]then begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=92;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=42;end;end;{:130}{131:}
+procedure outname(p:namepointer);var k:0..maxbytes;w:0..1;
+begin begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=123;end;w:=p mod 2;
+for k:=bytestart[p]to bytestart[p+2]-1 do begin if bytemem[w,k]=95 then
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;end;begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=bytemem[w,k];end;end;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=125;end;end;{:131}{132:}procedure copylimbo;label 10;
+var c:ASCIIcode;begin while true do if loc>limit then begin finishline;
+getline;if inputhasended then goto 10;
+end else begin buffer[limit+1]:=64;{133:}
+while buffer[loc]<>64 do begin begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=buffer[loc];end;loc:=loc+1;end;
+if loc<=limit then begin loc:=loc+2;c:=buffer[loc-1];
+if(c=32)or(c=9)or(c=42)then goto 10;
+if(c<>122)and(c<>90)then begin begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=64;end;
+if c<>64 then begin if not phaseone then begin writeln(output);
+write(output,'! Double @ required outside of sections');error;end;end;
+end;end{:133};end;10:end;{:132}{134:}function copyTeX:eightbits;
+label 30;var c:eightbits;
+begin while true do begin if loc>limit then begin finishline;getline;
+if inputhasended then begin c:=147;goto 30;end;end;buffer[limit+1]:=64;
+{135:}repeat c:=buffer[loc];loc:=loc+1;if c=124 then goto 30;
+if c<>64 then begin begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=c;end;
+if(outptr=1)and((c=32)or(c=9))then outptr:=outptr-1;end;until c=64;
+if loc<=limit then begin c:=controlcode(buffer[loc]);loc:=loc+1;goto 30;
+end{:135};end;30:copyTeX:=c;end;{:134}{136:}
+function copycomment(bal:eightbits):eightbits;label 30;var c:ASCIIcode;
+begin while true do begin if loc>limit then begin getline;
+if inputhasended then begin begin if not phaseone then begin writeln(
+output);write(output,'! Input ended in mid-comment');error;end;end;
+loc:=1;{138:}begin if tokptr+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[tokptr]:=32;tokptr:=tokptr+1;end;
+repeat begin if tokptr+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[tokptr]:=125;tokptr:=tokptr+1;end;bal:=bal-1;
+until bal=0;goto 30;{:138};end;end;c:=buffer[loc];loc:=loc+1;
+if c=124 then goto 30;
+begin if tokptr+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[tokptr]:=c;tokptr:=tokptr+1;end;{137:}
+if c=64 then begin loc:=loc+1;
+if buffer[loc-1]<>64 then begin begin if not phaseone then begin writeln
+(output);write(output,'! Illegal use of @ in comment');error;end;end;
+loc:=loc-2;tokptr:=tokptr-1;{138:}
+begin if tokptr+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[tokptr]:=32;tokptr:=tokptr+1;end;
+repeat begin if tokptr+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[tokptr]:=125;tokptr:=tokptr+1;end;bal:=bal-1;
+until bal=0;goto 30;{:138};end;
+end else if(c=92)and(buffer[loc]<>64)then begin begin if tokptr+2>
+maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[tokptr]:=buffer[loc];tokptr:=tokptr+1;end;loc:=loc+1;
+end else if c=123 then bal:=bal+1 else if c=125 then begin bal:=bal-1;
+if bal=0 then goto 30;end{:137};end;30:copycomment:=bal;end;{:136}{140:}
+{procedure printcat(c:eightbits);begin case c of 1:write(output,'simp');
+2:write(output,'math');3:write(output,'intro');4:write(output,'open');
+5:write(output,'beginning');6:write(output,'close');
+7:write(output,'alpha');8:write(output,'omega');9:write(output,'semi');
+10:write(output,'terminator');11:write(output,'stmt');
+12:write(output,'cond');13:write(output,'clause');
+14:write(output,'colon');15:write(output,'exp');16:write(output,'proc');
+17:write(output,'casehead');18:write(output,'recordhead');
+19:write(output,'varhead');20:write(output,'elsie');
+21:write(output,'casey');22:write(output,'module');
+otherwise write(output,'UNKNOWN')end;end;}{:140}{146:}
+{procedure printtext(p:textpointer);var j:0..maxtoks;r:0..10239;
+begin if p>=textptr then write(output,'BAD')else for j:=tokstart[p]to
+tokstart[p+1]-1 do begin r:=tokmem[j]mod 10240;
+case tokmem[j]div 10240 of 1:begin write(output,'\\',xchr[123]);
+printid(r);write(output,xchr[125]);end;
+2:begin write(output,'\&',xchr[123]);printid(r);write(output,xchr[125]);
+end;3:begin write(output,'<');printid(r);write(output,'>');end;
+4:write(output,'[[',r:1,']]');5:write(output,'|[[',r:1,']]|');
+otherwise[147:]case r of 131:write(output,'\mathbin',xchr[123]);
+132:write(output,'\mathrel',xchr[123]);
+133:write(output,'\mathop',xchr[123]);134:write(output,'[ccancel]');
+135:write(output,'[cancel]');136:write(output,'[indent]');
+137:write(output,'[outdent]');139:write(output,'[backup]');
+138:write(output,'[opt]');140:write(output,'[break]');
+141:write(output,'[force]');142:write(output,'[fforce]');
+143:write(output,'[quit]');otherwise write(output,xchr[r])end[:147]end;
+end;end;}{:146}{172:}procedure red(j:sixteenbits;k:eightbits;
+c:eightbits;d:integer);var i:0..maxscraps;begin cat[j]:=c;
+trans[j]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+if k>1 then begin for i:=j+k to loptr do begin cat[i-k+1]:=cat[i];
+trans[i-k+1]:=trans[i];end;loptr:=loptr-k+1;end;{173:}
+if pp+d>=scrapbase then pp:=pp+d else pp:=scrapbase{:173};end;{:172}
+{174:}procedure sq(j:sixteenbits;k:eightbits;c:eightbits;d:integer);
+var i:0..maxscraps;begin if k=1 then begin cat[j]:=c;{173:}
+if pp+d>=scrapbase then pp:=pp+d else pp:=scrapbase{:173};
+end else begin for i:=j to j+k-1 do begin tokmem[tokptr]:=40960+trans[i]
+;tokptr:=tokptr+1;end;red(j,k,c,d);end;end;{:174}{178:}
+{procedure prod(n:eightbits);var k:1..maxscraps;
+begin if tracing=2 then begin begin writeln(output);
+write(output,n:1,':');end;
+for k:=scrapbase to loptr do begin if k=pp then write(output,'*')else
+write(output,' ');printcat(cat[k]);end;
+if hiptr<=scrapptr then write(output,'...');end;end;}{:178}{179:}{150:}
+procedure fivecases;label 31;begin case cat[pp]of 5:{152:}
+if cat[pp+1]=6 then begin if(cat[pp+2]=10)or(cat[pp+2]=11)then begin sq(
+pp,3,11,-2);{prod(5)};goto 31;end;
+end else if cat[pp+1]=11 then begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=140;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;red(pp,2,5,-1);
+{prod(6)};goto 31;end{:152};3:{159:}
+if cat[pp+1]=11 then begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=32;tokptr:=tokptr+1;
+tokmem[tokptr]:=138;tokptr:=tokptr+1;tokmem[tokptr]:=55;
+tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;red(pp,2,11,-2);
+{prod(17)};goto 31;end{:159};2:{160:}
+if cat[pp+1]=6 then begin tokmem[tokptr]:=36;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp];tokptr:=tokptr+1;tokmem[tokptr]:=36;
+tokptr:=tokptr+1;red(pp,1,11,-2);{prod(18)};goto 31;
+end else if cat[pp+1]=14 then begin tokmem[tokptr]:=141;
+tokptr:=tokptr+1;tokmem[tokptr]:=139;tokptr:=tokptr+1;
+tokmem[tokptr]:=36;tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=36;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;red(pp,2,3,-3);
+{prod(19)};goto 31;end else if cat[pp+1]=2 then begin sq(pp,2,2,-1);
+{prod(20)};goto 31;end else if cat[pp+1]=1 then begin sq(pp,2,2,-1);
+{prod(21)};goto 31;
+end else if cat[pp+1]=11 then begin tokmem[tokptr]:=36;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp];tokptr:=tokptr+1;tokmem[tokptr]:=36;
+tokptr:=tokptr+1;tokmem[tokptr]:=136;tokptr:=tokptr+1;
+tokmem[tokptr]:=140;tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];
+tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+tokmem[tokptr]:=137;tokptr:=tokptr+1;tokmem[tokptr]:=141;
+tokptr:=tokptr+1;red(pp,2,11,-2);{prod(22)};goto 31;
+end else if cat[pp+1]=10 then begin tokmem[tokptr]:=36;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp];tokptr:=tokptr+1;tokmem[tokptr]:=36;
+tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;
+red(pp,2,11,-2);{prod(23)};goto 31;end{:160};4:{162:}
+if(cat[pp+1]=17)and(cat[pp+2]=6)then begin tokmem[tokptr]:=40960+trans[
+pp];tokptr:=tokptr+1;tokmem[tokptr]:=36;tokptr:=tokptr+1;
+tokmem[tokptr]:=135;tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];
+tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+tokmem[tokptr]:=137;tokptr:=tokptr+1;tokmem[tokptr]:=36;
+tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;
+red(pp,3,2,-1);{prod(26)};goto 31;
+end else if cat[pp+1]=6 then begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=44;
+tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;
+red(pp,2,2,-1);{prod(27)};goto 31;end else if cat[pp+1]=2 then{163:}
+begin if(cat[pp+2]=17)and(cat[pp+3]=6)then begin tokmem[tokptr]:=40960+
+trans[pp];tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];
+tokptr:=tokptr+1;tokmem[tokptr]:=36;tokptr:=tokptr+1;
+tokmem[tokptr]:=135;tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+2];
+tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+tokmem[tokptr]:=137;tokptr:=tokptr+1;tokmem[tokptr]:=36;
+tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+3];tokptr:=tokptr+1;
+red(pp,4,2,-1);{prod(28)};goto 31;
+end else if cat[pp+2]=6 then begin sq(pp,3,2,-1);{prod(29)};goto 31;
+end else if cat[pp+2]=14 then begin sq(pp+1,2,2,0);{prod(30)};goto 31;
+end else if cat[pp+2]=16 then begin if cat[pp+3]=3 then begin tokmem[
+tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;tokmem[tokptr]:=133;
+tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;tokmem[tokptr]:=125;
+tokptr:=tokptr+1;red(pp+1,3,2,0);{prod(31)};goto 31;end;
+end else if cat[pp+2]=9 then begin tokmem[tokptr]:=40960+trans[pp+1];
+tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;
+tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=44;tokptr:=tokptr+1;
+tokmem[tokptr]:=138;tokptr:=tokptr+1;tokmem[tokptr]:=53;
+tokptr:=tokptr+1;red(pp+1,2,2,0);{prod(32)};goto 31;
+end else if cat[pp+2]=19 then begin if cat[pp+3]=3 then begin tokmem[
+tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;tokmem[tokptr]:=133;
+tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;tokmem[tokptr]:=125;
+tokptr:=tokptr+1;red(pp+1,3,2,0);{prod(31)};goto 31;end;end;end{:163}
+else if cat[pp+1]=16 then begin if cat[pp+2]=3 then begin tokmem[tokptr]
+:=133;tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;tokmem[tokptr]:=125;
+tokptr:=tokptr+1;red(pp+1,2,2,0);{prod(34)};goto 31;end;
+end else if cat[pp+1]=1 then begin sq(pp+1,1,2,0);{prod(35)};goto 31;
+end else if(cat[pp+1]=11)and(cat[pp+2]=6)then begin tokmem[tokptr]:=
+40960+trans[pp];tokptr:=tokptr+1;tokmem[tokptr]:=36;tokptr:=tokptr+1;
+tokmem[tokptr]:=135;tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];
+tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+tokmem[tokptr]:=36;tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+2];
+tokptr:=tokptr+1;red(pp,3,2,-1);{prod(36)};goto 31;
+end else if cat[pp+1]=19 then begin if cat[pp+2]=3 then begin tokmem[
+tokptr]:=133;tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;tokmem[tokptr]:=125;
+tokptr:=tokptr+1;red(pp+1,2,2,0);{prod(37)};goto 31;end;end{:162};
+1:{167:}if cat[pp+1]=6 then begin sq(pp,1,11,-2);{prod(43)};goto 31;
+end else if cat[pp+1]=14 then begin tokmem[tokptr]:=141;
+tokptr:=tokptr+1;tokmem[tokptr]:=139;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp];tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;red(pp,2,3,-3);
+{prod(44)};goto 31;end else if cat[pp+1]=2 then begin sq(pp,2,2,-1);
+{prod(45)};goto 31;end else if cat[pp+1]=22 then begin sq(pp,2,22,0);
+{prod(46)};goto 31;end else if cat[pp+1]=1 then begin sq(pp,2,1,-2);
+{prod(47)};goto 31;end else if cat[pp+1]=10 then begin sq(pp,2,11,-2);
+{prod(48)};goto 31;end{:167};otherwise end;pp:=pp+1;31:end;
+procedure alphacases;label 31;begin{151:}
+if cat[pp+1]=2 then begin if cat[pp+2]=14 then begin sq(pp+1,2,2,0);
+{prod(1)};goto 31;
+end else if cat[pp+2]=8 then begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=32;tokptr:=tokptr+1;tokmem[tokptr]:=36;
+tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;
+tokmem[tokptr]:=36;tokptr:=tokptr+1;tokmem[tokptr]:=32;tokptr:=tokptr+1;
+tokmem[tokptr]:=136;tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+2];
+tokptr:=tokptr+1;red(pp,3,13,-2);{prod(2)};goto 31;end;
+end else if cat[pp+1]=8 then begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=32;tokptr:=tokptr+1;
+tokmem[tokptr]:=136;tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];
+tokptr:=tokptr+1;red(pp,2,13,-2);{prod(3)};goto 31;
+end else if cat[pp+1]=1 then begin sq(pp+1,1,2,0);{prod(4)};goto 31;
+end{:151};pp:=pp+1;31:end;{:150}function translate:textpointer;
+label 30,31;var i:1..maxscraps;j:0..maxscraps;k:0..longbufsize;
+begin pp:=scrapbase;loptr:=pp-1;hiptr:=pp;{182:}
+{if tracing=2 then begin begin writeln(output);
+write(output,'Tracing after l.',line:1,':');end;
+if history=0 then history:=1;if loc>50 then begin write(output,'...');
+for k:=loc-50 to loc do write(output,xchr[buffer[k-1]]);
+end else for k:=1 to loc do write(output,xchr[buffer[k-1]]);end}{:182};
+{175:}while true do begin{176:}
+if loptr<pp+3 then begin repeat if hiptr<=scrapptr then begin loptr:=
+loptr+1;cat[loptr]:=cat[hiptr];trans[loptr]:=trans[hiptr];
+hiptr:=hiptr+1;end;until(hiptr>scrapptr)or(loptr=pp+3);
+for i:=loptr+1 to pp+3 do cat[i]:=0;end{:176};
+if(tokptr+8>maxtoks)or(textptr+4>maxtexts)then begin if tokptr>maxtokptr
+then maxtokptr:=tokptr;if textptr>maxtxtptr then maxtxtptr:=textptr;
+begin writeln(output);
+write(output,'! Sorry, ','token/text',' capacity exceeded');error;
+history:=3;jumpout;end;end;if pp>loptr then goto 30;{149:}
+if cat[pp]<=7 then if cat[pp]<7 then fivecases else alphacases else
+begin case cat[pp]of 17:{153:}
+if cat[pp+1]=21 then begin if cat[pp+2]=13 then begin tokmem[tokptr]:=
+40960+trans[pp];tokptr:=tokptr+1;tokmem[tokptr]:=137;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;red(pp,3,17,0);
+{prod(7)};goto 31;end;
+end else if cat[pp+1]=6 then begin if cat[pp+2]=10 then begin tokmem[
+tokptr]:=40960+trans[pp];tokptr:=tokptr+1;tokmem[tokptr]:=135;
+tokptr:=tokptr+1;tokmem[tokptr]:=137;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;red(pp,3,11,-2);
+{prod(8)};goto 31;end;
+end else if cat[pp+1]=11 then begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=141;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;red(pp,2,17,0);
+{prod(9)};goto 31;end{:153};21:{154:}
+if cat[pp+1]=13 then begin sq(pp,2,17,0);{prod(10)};goto 31;end{:154};
+13:{155:}if cat[pp+1]=11 then begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=140;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;tokmem[tokptr]:=135;
+tokptr:=tokptr+1;tokmem[tokptr]:=137;tokptr:=tokptr+1;
+tokmem[tokptr]:=141;tokptr:=tokptr+1;red(pp,2,11,-2);{prod(11)};goto 31;
+end{:155};12:{156:}
+if(cat[pp+1]=13)and(cat[pp+2]=11)then if cat[pp+3]=20 then begin tokmem[
+tokptr]:=40960+trans[pp];tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;tokmem[tokptr]:=140;
+tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+3];tokptr:=tokptr+1;tokmem[tokptr]:=32;
+tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;red(pp,4,13,-2);
+{prod(12)};goto 31;end else begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;
+tokmem[tokptr]:=140;tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+2];
+tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+tokmem[tokptr]:=137;tokptr:=tokptr+1;tokmem[tokptr]:=141;
+tokptr:=tokptr+1;red(pp,3,11,-2);{prod(13)};goto 31;end{:156};20:{157:}
+begin sq(pp,1,3,-3);{prod(14)};goto 31;end{:157};15:{158:}
+if cat[pp+1]=2 then begin if cat[pp+2]=1 then if cat[pp+3]<>1 then begin
+tokmem[tokptr]:=40960+trans[pp];tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;tokmem[tokptr]:=125;
+tokptr:=tokptr+1;red(pp,3,2,-1);{prod(15)};goto 31;end;
+end else if cat[pp+1]=1 then if cat[pp+2]<>1 then begin tokmem[tokptr]:=
+40960+trans[pp];tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];
+tokptr:=tokptr+1;tokmem[tokptr]:=125;tokptr:=tokptr+1;red(pp,2,2,-1);
+{prod(16)};goto 31;end{:158};22:{161:}
+if(cat[pp+1]=10)or(cat[pp+1]=9)then begin tokmem[tokptr]:=40960+trans[pp
+];tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;
+tokmem[tokptr]:=141;tokptr:=tokptr+1;red(pp,2,11,-2);{prod(24)};goto 31;
+end else begin sq(pp,1,1,-2);{prod(25)};goto 31;end{:161};16:{164:}
+if cat[pp+1]=5 then begin if(cat[pp+2]=6)and(cat[pp+3]=10)then begin
+tokmem[tokptr]:=40960+trans[pp];tokptr:=tokptr+1;tokmem[tokptr]:=135;
+tokptr:=tokptr+1;tokmem[tokptr]:=137;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+3];tokptr:=tokptr+1;red(pp,4,11,-2);
+{prod(38)};goto 31;end;
+end else if cat[pp+1]=11 then begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=140;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;red(pp,2,16,-2);
+{prod(39)};goto 31;end{:164};18:{165:}
+if(cat[pp+1]=3)and(cat[pp+2]=21)then begin tokmem[tokptr]:=40960+trans[
+pp];tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;
+tokmem[tokptr]:=32;tokptr:=tokptr+1;tokmem[tokptr]:=135;
+tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;
+red(pp,3,21,-2);{prod(40)};goto 31;end else begin tokmem[tokptr]:=136;
+tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp];tokptr:=tokptr+1;
+tokmem[tokptr]:=135;tokptr:=tokptr+1;red(pp,1,17,0);{prod(41)};goto 31;
+end{:165};9:{166:}begin sq(pp,1,10,-3);{prod(42)};goto 31;end{:166};
+11:{168:}if cat[pp+1]=11 then begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=140;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;red(pp,2,11,-2);
+{prod(49)};goto 31;end{:168};10:{169:}begin sq(pp,1,11,-2);{prod(50)};
+goto 31;end{:169};19:{170:}if cat[pp+1]=5 then begin sq(pp,1,11,-2);
+{prod(51)};goto 31;
+end else if cat[pp+1]=2 then begin if cat[pp+2]=14 then begin tokmem[
+tokptr]:=36;tokptr:=tokptr+1;tokmem[tokptr]:=40960+trans[pp+1];
+tokptr:=tokptr+1;tokmem[tokptr]:=36;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+2];tokptr:=tokptr+1;red(pp+1,2,3,+1);
+{prod(52)};goto 31;end;
+end else if cat[pp+1]=1 then begin if cat[pp+2]=14 then begin sq(pp+1,2,
+3,+1);{prod(53)};goto 31;end;
+end else if cat[pp+1]=11 then begin tokmem[tokptr]:=40960+trans[pp];
+tokptr:=tokptr+1;tokmem[tokptr]:=140;tokptr:=tokptr+1;
+tokmem[tokptr]:=40960+trans[pp+1];tokptr:=tokptr+1;red(pp,2,19,-2);
+{prod(54)};goto 31;end{:170};otherwise end;pp:=pp+1;31:end{:149};end;
+30:{:175};
+if(loptr=scrapbase)and(cat[loptr]<>2)then translate:=trans[loptr]else{
+180:}begin{181:}
+{if(loptr>scrapbase)and(tracing=1)then begin begin writeln(output);
+write(output,'Irreducible scrap sequence in section ',modulecount:1);
+end;writeln(output,':');if history=0 then history:=1;
+for j:=scrapbase to loptr do begin write(output,' ');printcat(cat[j]);
+end;end;}{:181};
+for j:=scrapbase to loptr do begin if j<>scrapbase then begin tokmem[
+tokptr]:=32;tokptr:=tokptr+1;end;
+if cat[j]=2 then begin tokmem[tokptr]:=36;tokptr:=tokptr+1;end;
+tokmem[tokptr]:=40960+trans[j];tokptr:=tokptr+1;
+if cat[j]=2 then begin tokmem[tokptr]:=36;tokptr:=tokptr+1;end;
+if tokptr+6>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;end;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+translate:=textptr-1;end{:180};end;{:179}{183:}{195:}
+procedure appcomment;begin textptr:=textptr+1;tokstart[textptr]:=tokptr;
+if(scrapptr<scrapbase)or(cat[scrapptr]<8)or(cat[scrapptr]>10)then begin
+scrapptr:=scrapptr+1;cat[scrapptr]:=10;trans[scrapptr]:=0;
+end else begin tokmem[tokptr]:=40960+trans[scrapptr];tokptr:=tokptr+1;
+end;tokmem[tokptr]:=textptr+40959;tokptr:=tokptr+1;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;{:195}{196:}procedure appoctal;begin tokmem[tokptr]:=92;
+tokptr:=tokptr+1;tokmem[tokptr]:=79;tokptr:=tokptr+1;
+tokmem[tokptr]:=123;tokptr:=tokptr+1;
+while(buffer[loc]>=48)and(buffer[loc]<=55)do begin begin if tokptr+2>
+maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[tokptr]:=buffer[loc];tokptr:=tokptr+1;end;loc:=loc+1;
+end;begin tokmem[tokptr]:=125;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=1;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;end;procedure apphex;
+begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=72;
+tokptr:=tokptr+1;tokmem[tokptr]:=123;tokptr:=tokptr+1;
+while((buffer[loc]>=48)and(buffer[loc]<=57))or((buffer[loc]>=65)and(
+buffer[loc]<=70))do begin begin if tokptr+2>maxtoks then begin writeln(
+output);write(output,'! Sorry, ','token',' capacity exceeded');error;
+history:=3;jumpout;end;tokmem[tokptr]:=buffer[loc];tokptr:=tokptr+1;end;
+loc:=loc+1;end;begin tokmem[tokptr]:=125;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=1;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;end;{:196}{186:}
+procedure easycases;
+begin case nextcontrol of 6:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;
+tokmem[tokptr]:=105;tokptr:=tokptr+1;tokmem[tokptr]:=110;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;32:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=116;
+tokptr:=tokptr+1;tokmem[tokptr]:=111;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=2;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+35,36,37,94,95:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;
+tokmem[tokptr]:=nextcontrol;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=2;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;0,124,131,132,133:;
+40,91:begin tokmem[tokptr]:=nextcontrol;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=4;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+41,93:begin tokmem[tokptr]:=nextcontrol;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=6;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+42:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=97;
+tokptr:=tokptr+1;tokmem[tokptr]:=115;tokptr:=tokptr+1;
+tokmem[tokptr]:=116;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=2;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;44:begin tokmem[tokptr]:=44;
+tokptr:=tokptr+1;tokmem[tokptr]:=138;tokptr:=tokptr+1;
+tokmem[tokptr]:=57;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=2;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;
+46,48,49,50,51,52,53,54,55,56,57:begin tokmem[tokptr]:=nextcontrol;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=1;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;59:begin tokmem[tokptr]:=59;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=9;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;58:begin tokmem[tokptr]:=58;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=14;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;{188:}26:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;
+tokmem[tokptr]:=73;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=2;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;28:begin tokmem[tokptr]:=92;
+tokptr:=tokptr+1;tokmem[tokptr]:=76;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=2;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+29:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=71;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;30:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=83;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;4:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=87;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;31:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=86;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;5:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=82;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;24:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=75;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;{:188}128:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;
+tokmem[tokptr]:=69;tokptr:=tokptr+1;tokmem[tokptr]:=123;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=15;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;9:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=66;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;10:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=84;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;12:appoctal;13:apphex;135:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;
+tokmem[tokptr]:=41;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=1;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;3:begin tokmem[tokptr]:=92;
+tokptr:=tokptr+1;tokmem[tokptr]:=93;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=1;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+137:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=44;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;138:begin tokmem[tokptr]:=138;tokptr:=tokptr+1;tokmem[tokptr]:=48;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=1;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;139:begin tokmem[tokptr]:=141;tokptr:=tokptr+1;appcomment;end;
+140:begin tokmem[tokptr]:=142;tokptr:=tokptr+1;appcomment;end;
+141:begin tokmem[tokptr]:=134;tokptr:=tokptr+1;tokmem[tokptr]:=92;
+tokptr:=tokptr+1;tokmem[tokptr]:=32;tokptr:=tokptr+1;
+begin tokmem[tokptr]:=134;tokptr:=tokptr+1;appcomment;end;end;
+142:begin scrapptr:=scrapptr+1;cat[scrapptr]:=9;trans[scrapptr]:=0;end;
+136:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=74;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;otherwise begin tokmem[tokptr]:=nextcontrol;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=2;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end end;end;{:186}{192:}
+procedure subcases(p:namepointer);
+begin case ilk[p]of 0:begin tokmem[tokptr]:=10240+p;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=1;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+4:begin tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=7;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;7:begin tokmem[tokptr]:=141;
+tokptr:=tokptr+1;tokmem[tokptr]:=139;tokptr:=tokptr+1;
+tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=3;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;8:begin tokmem[tokptr]:=131;
+tokptr:=tokptr+1;tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;
+tokmem[tokptr]:=125;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=2;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;9:begin tokmem[tokptr]:=20480+p;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=8;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;12:begin tokmem[tokptr]:=141;tokptr:=tokptr+1;
+tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=7;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;13:begin tokmem[tokptr]:=20480+p;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=3;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;16:begin tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=1;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+20:begin tokmem[tokptr]:=132;tokptr:=tokptr+1;tokmem[tokptr]:=20480+p;
+tokptr:=tokptr+1;tokmem[tokptr]:=125;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=2;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;end;end;{:192}
+procedure Pascalparse;label 21,10;var j:0..longbufsize;p:namepointer;
+begin while nextcontrol<143 do begin{185:}{187:}
+if(scrapptr+4>maxscraps)or(tokptr+6>maxtoks)or(textptr+4>maxtexts)then
+begin if scrapptr>maxscrptr then maxscrptr:=scrapptr;
+if tokptr>maxtokptr then maxtokptr:=tokptr;
+if textptr>maxtxtptr then maxtxtptr:=textptr;begin writeln(output);
+write(output,'! Sorry, ','scrap/token/text',' capacity exceeded');error;
+history:=3;jumpout;end;end{:187};
+21:if(nextcontrol=129)or(nextcontrol=2)then{189:}
+begin tokmem[tokptr]:=92;tokptr:=tokptr+1;
+if nextcontrol=2 then begin tokmem[tokptr]:=61;tokptr:=tokptr+1;
+end else begin tokmem[tokptr]:=46;tokptr:=tokptr+1;end;
+tokmem[tokptr]:=123;tokptr:=tokptr+1;j:=idfirst;
+while j<idloc do begin case buffer[j]of 32,92,35,37,36,94,39,96,123,125,
+126,38,95:begin tokmem[tokptr]:=92;tokptr:=tokptr+1;end;
+64:if buffer[j+1]=64 then j:=j+1 else begin if not phaseone then begin
+writeln(output);write(output,'! Double @ should be used in strings');
+error;end;end;otherwise end;
+begin if tokptr+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[tokptr]:=buffer[j];tokptr:=tokptr+1;end;j:=j+1;end;
+begin tokmem[tokptr]:=125;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=1;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;end{:189}
+else if(nextcontrol=130)then{191:}begin p:=idlookup(0);
+case ilk[p]of 0,4,7,8,9,12,13,16,20:subcases(p);{193:}
+5:begin begin tokmem[tokptr]:=141;tokptr:=tokptr+1;
+tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;tokmem[tokptr]:=135;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=5;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;begin scrapptr:=scrapptr+1;cat[scrapptr]:=3;trans[scrapptr]:=0;end;
+end;6:begin begin scrapptr:=scrapptr+1;cat[scrapptr]:=21;
+trans[scrapptr]:=0;end;begin tokmem[tokptr]:=141;tokptr:=tokptr+1;
+tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=7;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;end;10:begin{194:}
+if(scrapptr<scrapbase)or((cat[scrapptr]<>10)and(cat[scrapptr]<>9))then
+begin scrapptr:=scrapptr+1;cat[scrapptr]:=10;trans[scrapptr]:=0;
+end{:194};begin tokmem[tokptr]:=141;tokptr:=tokptr+1;
+tokmem[tokptr]:=139;tokptr:=tokptr+1;tokmem[tokptr]:=20480+p;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=20;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;end;11:begin{194:}
+if(scrapptr<scrapbase)or((cat[scrapptr]<>10)and(cat[scrapptr]<>9))then
+begin scrapptr:=scrapptr+1;cat[scrapptr]:=10;trans[scrapptr]:=0;
+end{:194};begin tokmem[tokptr]:=141;tokptr:=tokptr+1;
+tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=6;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;end;14:begin begin scrapptr:=scrapptr+1;
+cat[scrapptr]:=12;trans[scrapptr]:=0;end;begin tokmem[tokptr]:=141;
+tokptr:=tokptr+1;tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=7;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;end;
+23:begin begin tokmem[tokptr]:=141;tokptr:=tokptr+1;tokmem[tokptr]:=92;
+tokptr:=tokptr+1;tokmem[tokptr]:=126;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=7;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+begin tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=8;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;end;17:begin begin tokmem[tokptr]:=141;
+tokptr:=tokptr+1;tokmem[tokptr]:=139;tokptr:=tokptr+1;
+tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;tokmem[tokptr]:=135;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=16;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;begin tokmem[tokptr]:=136;tokptr:=tokptr+1;tokmem[tokptr]:=92;
+tokptr:=tokptr+1;tokmem[tokptr]:=32;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=3;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;end;
+18:begin begin tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=18;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+begin scrapptr:=scrapptr+1;cat[scrapptr]:=3;trans[scrapptr]:=0;end;end;
+19:begin begin tokmem[tokptr]:=141;tokptr:=tokptr+1;tokmem[tokptr]:=136;
+tokptr:=tokptr+1;tokmem[tokptr]:=20480+p;tokptr:=tokptr+1;
+tokmem[tokptr]:=135;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=5;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;begin scrapptr:=scrapptr+1;
+cat[scrapptr]:=3;trans[scrapptr]:=0;end;end;21:begin{194:}
+if(scrapptr<scrapbase)or((cat[scrapptr]<>10)and(cat[scrapptr]<>9))then
+begin scrapptr:=scrapptr+1;cat[scrapptr]:=10;trans[scrapptr]:=0;
+end{:194};begin tokmem[tokptr]:=141;tokptr:=tokptr+1;
+tokmem[tokptr]:=139;tokptr:=tokptr+1;tokmem[tokptr]:=20480+p;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=6;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;begin scrapptr:=scrapptr+1;cat[scrapptr]:=13;trans[scrapptr]:=0;end;
+end;22:begin begin tokmem[tokptr]:=141;tokptr:=tokptr+1;
+tokmem[tokptr]:=139;tokptr:=tokptr+1;tokmem[tokptr]:=20480+p;
+tokptr:=tokptr+1;tokmem[tokptr]:=135;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=19;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+begin scrapptr:=scrapptr+1;cat[scrapptr]:=3;trans[scrapptr]:=0;end;end;
+{:193}otherwise begin nextcontrol:=ilk[p]-24;goto 21;end end;end{:191}
+else if(nextcontrol=134)then{190:}begin tokmem[tokptr]:=92;
+tokptr:=tokptr+1;tokmem[tokptr]:=104;tokptr:=tokptr+1;
+tokmem[tokptr]:=98;tokptr:=tokptr+1;tokmem[tokptr]:=111;
+tokptr:=tokptr+1;tokmem[tokptr]:=120;tokptr:=tokptr+1;
+tokmem[tokptr]:=123;tokptr:=tokptr+1;
+for j:=idfirst to idloc-1 do begin if tokptr+2>maxtoks then begin
+writeln(output);write(output,'! Sorry, ','token',' capacity exceeded');
+error;history:=3;jumpout;end;tokmem[tokptr]:=buffer[j];tokptr:=tokptr+1;
+end;begin tokmem[tokptr]:=125;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=1;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;end{:190}else easycases{:185};
+nextcontrol:=getnext;if(nextcontrol=124)or(nextcontrol=123)then goto 10;
+end;10:end;{:183}{197:}function Pascaltranslate:textpointer;
+var p:textpointer;savebase:0..maxscraps;begin savebase:=scrapbase;
+scrapbase:=scrapptr+1;Pascalparse;
+if nextcontrol<>124 then begin if not phaseone then begin writeln(output
+);write(output,'! Missing "|" after Pascal text');error;end;end;
+begin if tokptr+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[tokptr]:=135;tokptr:=tokptr+1;end;appcomment;
+p:=translate;if scrapptr>maxscrptr then maxscrptr:=scrapptr;
+scrapptr:=scrapbase-1;scrapbase:=savebase;Pascaltranslate:=p;end;{:197}
+{198:}procedure outerparse;var bal:eightbits;p,q:textpointer;
+begin while nextcontrol<143 do if nextcontrol<>123 then Pascalparse else
+begin{199:}
+if(tokptr+7>maxtoks)or(textptr+3>maxtexts)or(scrapptr>=maxscraps)then
+begin if scrapptr>maxscrptr then maxscrptr:=scrapptr;
+if tokptr>maxtokptr then maxtokptr:=tokptr;
+if textptr>maxtxtptr then maxtxtptr:=textptr;begin writeln(output);
+write(output,'! Sorry, ','token/text/scrap',' capacity exceeded');error;
+history:=3;jumpout;end;end{:199};tokmem[tokptr]:=92;tokptr:=tokptr+1;
+tokmem[tokptr]:=67;tokptr:=tokptr+1;tokmem[tokptr]:=123;
+tokptr:=tokptr+1;bal:=copycomment(1);nextcontrol:=124;
+while bal>0 do begin p:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;q:=Pascaltranslate;tokmem[tokptr]:=40960+p;
+tokptr:=tokptr+1;tokmem[tokptr]:=51200+q;tokptr:=tokptr+1;
+if nextcontrol=124 then bal:=copycomment(bal)else bal:=0;end;
+tokmem[tokptr]:=141;tokptr:=tokptr+1;appcomment;end;end;{:198}{204:}
+procedure pushlevel(p:textpointer);
+begin if stackptr=stacksize then begin writeln(output);
+write(output,'! Sorry, ','stack',' capacity exceeded');error;history:=3;
+jumpout;end else begin if stackptr>0 then stack[stackptr]:=curstate;
+stackptr:=stackptr+1;if stackptr>maxstackptr then maxstackptr:=stackptr;
+curstate.tokfield:=tokstart[p];curstate.endfield:=tokstart[p+1];end;end;
+{:204}{206:}function getoutput:eightbits;label 20;var a:sixteenbits;
+begin 20:while curstate.tokfield=curstate.endfield do begin stackptr:=
+stackptr-1;curstate:=stack[stackptr];end;a:=tokmem[curstate.tokfield];
+curstate.tokfield:=curstate.tokfield+1;
+if a>=256 then begin curname:=a mod 10240;case a div 10240 of 2:a:=129;
+3:a:=128;4:begin pushlevel(curname);goto 20;end;
+5:begin pushlevel(curname);curstate.modefield:=0;goto 20;end;
+otherwise a:=130 end;end;{if troubleshooting then debughelp;}
+getoutput:=a;end;{:206}{207:}procedure makeoutput;forward;
+procedure outputPascal;
+var savetokptr,savetextptr,savenextcontrol:sixteenbits;p:textpointer;
+begin savetokptr:=tokptr;savetextptr:=textptr;
+savenextcontrol:=nextcontrol;nextcontrol:=124;p:=Pascaltranslate;
+tokmem[tokptr]:=p+51200;tokptr:=tokptr+1;makeoutput;
+if textptr>maxtxtptr then maxtxtptr:=textptr;
+if tokptr>maxtokptr then maxtokptr:=tokptr;textptr:=savetextptr;
+tokptr:=savetokptr;nextcontrol:=savenextcontrol;end;{:207}{208:}
+procedure makeoutput;label 21,10,31;var a:eightbits;b:eightbits;
+k,klimit:0..maxbytes;w:0..1;j:0..longbufsize;stringdelimiter:ASCIIcode;
+saveloc,savelimit:0..longbufsize;curmodname:namepointer;savemode:mode;
+begin tokmem[tokptr]:=143;tokptr:=tokptr+1;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;pushlevel(textptr-1);
+while true do begin a:=getoutput;21:case a of 143:goto 10;130,129:{209:}
+begin begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;end;
+if a=130 then if bytestart[curname+2]-bytestart[curname]=1 then begin if
+outptr=linelength then breakout;outptr:=outptr+1;outbuf[outptr]:=124;
+end else begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;end else begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=38;end;
+if bytestart[curname+2]-bytestart[curname]=1 then begin if outptr=
+linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=bytemem[curname mod 2,bytestart[curname]];
+end else outname(curname);end{:209};128:{213:}
+begin begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=88;end;curxref:=xref[curname];
+if xmem[curxref].numfield>=10240 then begin outmod(xmem[curxref].
+numfield-10240);
+if phasethree then begin curxref:=xmem[curxref].xlinkfield;
+while xmem[curxref].numfield>=10240 do begin begin if outptr=linelength
+then breakout;outptr:=outptr+1;outbuf[outptr]:=44;
+if outptr=linelength then breakout;outptr:=outptr+1;outbuf[outptr]:=32;
+end;outmod(xmem[curxref].numfield-10240);
+curxref:=xmem[curxref].xlinkfield;end;end;
+end else begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=48;end;begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=58;end;{214:}k:=bytestart[curname];
+w:=curname mod 2;klimit:=bytestart[curname+2];curmodname:=curname;
+while k<klimit do begin b:=bytemem[w,k];k:=k+1;if b=64 then{215:}
+begin if bytemem[w,k]<>64 then begin begin writeln(output);
+write(output,'! Illegal control code in section name:');end;
+begin writeln(output);write(output,'<');end;printid(curmodname);
+write(output,'> ');history:=2;end;k:=k+1;end{:215};
+if b<>124 then begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=b;end else begin{216:}j:=limit+1;
+buffer[j]:=124;stringdelimiter:=0;
+while true do begin if k>=klimit then begin begin writeln(output);
+write(output,'! Pascal text in section name didn''t end:');end;
+begin writeln(output);write(output,'<');end;printid(curmodname);
+write(output,'> ');history:=2;goto 31;end;b:=bytemem[w,k];k:=k+1;
+if b=64 then{217:}begin if j>longbufsize-4 then begin writeln(output);
+write(output,'! Sorry, ','buffer',' capacity exceeded');error;
+history:=3;jumpout;end;buffer[j+1]:=64;buffer[j+2]:=bytemem[w,k];j:=j+2;
+k:=k+1;end{:217}
+else begin if(b=34)or(b=39)then if stringdelimiter=0 then
+stringdelimiter:=b else if stringdelimiter=b then stringdelimiter:=0;
+if(b<>124)or(stringdelimiter<>0)then begin if j>longbufsize-3 then begin
+writeln(output);write(output,'! Sorry, ','buffer',' capacity exceeded');
+error;history:=3;jumpout;end;j:=j+1;buffer[j]:=b;end else goto 31;end;
+end;31:{:216};saveloc:=loc;savelimit:=limit;loc:=limit+2;limit:=j+1;
+buffer[limit]:=124;outputPascal;loc:=saveloc;limit:=savelimit;end;
+end{:214};begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=88;end;end{:213};131,133,132:{210:}
+begin begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=109;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=97;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=116;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=104;end;
+if a=131 then begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=98;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=105;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=110;
+end else if a=132 then begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=114;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=101;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=108;
+end else begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=111;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=112;end;begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=123;end;end{:210};
+135:begin repeat a:=getoutput;until(a<139)or(a>142);goto 21;end;
+134:begin repeat a:=getoutput;until((a<139)and(a<>32))or(a>142);goto 21;
+end;136,137,138,139,140,141,142:{211:}
+if a<140 then begin if curstate.modefield=1 then begin begin if outptr=
+linelength then breakout;outptr:=outptr+1;outbuf[outptr]:=92;
+if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=a-87;end;
+if a=138 then begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=getoutput;
+end end else if a=138 then b:=getoutput end else{212:}begin b:=a;
+savemode:=curstate.modefield;while true do begin a:=getoutput;
+if(a=135)or(a=134)then goto 21;
+if((a<>32)and(a<140))or(a>142)then begin if savemode=1 then begin if
+outptr>3 then if(outbuf[outptr]=80)and(outbuf[outptr-1]=92)and(outbuf[
+outptr-2]=89)and(outbuf[outptr-3]=92)then goto 21;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=b-87;end;if a<>143 then finishline;
+end else if(a<>143)and(curstate.modefield=0)then begin if outptr=
+linelength then breakout;outptr:=outptr+1;outbuf[outptr]:=32;end;
+goto 21;end;if a>b then b:=a;end;end{:212}{:211};
+otherwise begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=a;end end;end;10:end;{:208}{226:}procedure finishPascal;
+var p:textpointer;begin begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=92;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=80;end;
+begin if tokptr+2>maxtoks then begin writeln(output);
+write(output,'! Sorry, ','token',' capacity exceeded');error;history:=3;
+jumpout;end;tokmem[tokptr]:=141;tokptr:=tokptr+1;end;appcomment;
+p:=translate;tokmem[tokptr]:=p+40960;tokptr:=tokptr+1;makeoutput;
+if outptr>1 then if outbuf[outptr-1]=92 then if outbuf[outptr]=54 then
+outptr:=outptr-2 else if outbuf[outptr]=55 then outbuf[outptr]:=89;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=112;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=97;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=114;end;finishline;
+if textptr>maxtxtptr then maxtxtptr:=textptr;
+if tokptr>maxtokptr then maxtokptr:=tokptr;
+if scrapptr>maxscrptr then maxscrptr:=scrapptr;tokptr:=1;textptr:=1;
+scrapptr:=0;end;{:226}{236:}procedure footnote(flag:sixteenbits);
+label 30,10;var q:xrefnumber;
+begin if xmem[curxref].numfield<=flag then goto 10;finishline;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;end;
+if flag=0 then begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=85;
+end else begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=65;end;{237:}q:=curxref;
+if xmem[xmem[q].xlinkfield].numfield>flag then begin if outptr=
+linelength then breakout;outptr:=outptr+1;outbuf[outptr]:=115;end;
+while true do begin outmod(xmem[curxref].numfield-flag);
+curxref:=xmem[curxref].xlinkfield;
+if xmem[curxref].numfield<=flag then goto 30;
+if xmem[xmem[curxref].xlinkfield].numfield>flag then begin if outptr=
+linelength then breakout;outptr:=outptr+1;outbuf[outptr]:=44;
+if outptr=linelength then breakout;outptr:=outptr+1;outbuf[outptr]:=32;
+end else begin begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=92;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=69;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=84;end;
+if curxref<>xmem[q].xlinkfield then begin if outptr=linelength then
+breakout;outptr:=outptr+1;outbuf[outptr]:=115;end;end;end;30:{:237};
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=46;end;10:end;{:236}{249:}
+procedure unbucket(d:eightbits);var c:ASCIIcode;
+begin for c:=229 downto 0 do if bucket[collate[c]]>0 then begin if
+scrapptr>maxscraps then begin writeln(output);
+write(output,'! Sorry, ','sorting',' capacity exceeded');error;
+history:=3;jumpout;end;scrapptr:=scrapptr+1;
+if scrapptr>maxsortptr then maxsortptr:=scrapptr;
+if c=0 then cat[scrapptr]:=255 else cat[scrapptr]:=d;
+trans[scrapptr]:=bucket[collate[c]];bucket[collate[c]]:=0;end;end;{:249}
+{256:}procedure modprint(p:namepointer);
+begin if p>0 then begin modprint(link[p]);
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=58;end;tokptr:=1;textptr:=1;scrapptr:=0;stackptr:=0;
+curstate.modefield:=1;tokmem[tokptr]:=p+30720;tokptr:=tokptr+1;
+makeoutput;footnote(0);finishline;modprint(ilk[p]);end;end;{:256}{260:}
+{procedure debughelp;label 888,10;var k:integer;
+begin debugskipped:=debugskipped+1;
+if debugskipped<debugcycle then goto 10;debugskipped:=0;
+while true do begin write(output,'#');flushstdout;read(input,ddt);
+if ddt<0 then goto 10 else if ddt=0 then begin goto 888;
+888:ddt:=0;
+end else begin read(input,dd);case ddt of 1:printid(dd);2:printtext(dd);
+3:for k:=1 to dd do write(output,xchr[buffer[k]]);
+4:for k:=1 to dd do write(output,xchr[modtext[k]]);
+5:for k:=1 to outptr do write(output,xchr[outbuf[k]]);
+6:for k:=1 to dd do begin printcat(cat[k]);write(output,' ');end;
+otherwise write(output,'?')end;end;end;10:end;}{:260}{261:}
+procedure PhaseI;begin{109:}phaseone:=true;phasethree:=false;resetinput;
+modulecount:=0;skiplimbo;changeexists:=false;
+while not inputhasended do{110:}begin modulecount:=modulecount+1;
+if modulecount=maxmodules then begin writeln(output);
+write(output,'! Sorry, ','section number',' capacity exceeded');error;
+history:=3;jumpout;end;changedmodule[modulecount]:=changing;
+if buffer[loc-1]=42 then begin write(output,'*',modulecount:1);
+flushstdout;end;{113:}repeat nextcontrol:=skipTeX;
+case nextcontrol of 126:xrefswitch:=10240;125:xrefswitch:=0;
+124:Pascalxref;131,132,133,146:begin loc:=loc-2;nextcontrol:=getnext;
+if nextcontrol<>146 then newxref(idlookup(nextcontrol-130));end;
+otherwise end;until nextcontrol>=143{:113};{115:}
+while nextcontrol<=144 do begin xrefswitch:=10240;
+if nextcontrol=144 then nextcontrol:=getnext else{116:}
+begin nextcontrol:=getnext;
+if nextcontrol=130 then begin lhs:=idlookup(0);ilk[lhs]:=0;newxref(lhs);
+nextcontrol:=getnext;if nextcontrol=30 then begin nextcontrol:=getnext;
+if nextcontrol=130 then begin rhs:=idlookup(0);ilk[lhs]:=ilk[rhs];
+ilk[rhs]:=0;newxref(rhs);ilk[rhs]:=ilk[lhs];nextcontrol:=getnext;end;
+end;end;end{:116};outerxref;end{:115};{117:}
+if nextcontrol<=146 then begin if nextcontrol=145 then modxrefswitch:=0
+else modxrefswitch:=10240;
+repeat if nextcontrol=146 then newmodxref(curmodule);
+nextcontrol:=getnext;outerxref;until nextcontrol>146;end{:117};
+if changedmodule[modulecount]then changeexists:=true;end{:110};
+changedmodule[modulecount]:=changeexists;phaseone:=false;{120:}
+modcheck(ilk[0]){:120};{:109};end;procedure PhaseII;begin{218:}
+resetinput;begin writeln(output);
+write(output,'Writing the output file...');end;modulecount:=0;copylimbo;
+finishline;flushbuffer(0,false,false);while not inputhasended do{220:}
+begin modulecount:=modulecount+1;{221:}
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;end;
+if buffer[loc-1]<>42 then begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=77;
+end else begin begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=78;end;write(output,'*',modulecount:1);
+flushstdout;end;outmod(modulecount);
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=46;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=32;end{:221};saveline:=outline;saveplace:=outptr;{222:}
+repeat nextcontrol:=copyTeX;case nextcontrol of 124:begin stackptr:=0;
+curstate.modefield:=1;outputPascal;end;
+64:begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=64;end;12:{223:}
+begin begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=79;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=123;end;
+while(buffer[loc]>=48)and(buffer[loc]<=55)do begin begin if outptr=
+linelength then breakout;outptr:=outptr+1;outbuf[outptr]:=buffer[loc];
+end;loc:=loc+1;end;begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=125;end;end{:223};13:{224:}
+begin begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=72;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=123;end;
+while((buffer[loc]>=48)and(buffer[loc]<=57))or((buffer[loc]>=65)and(
+buffer[loc]<=70))do begin begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=buffer[loc];end;loc:=loc+1;end;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=125;end;end{:224};134,131,132,133,146:begin loc:=loc-2;
+nextcontrol:=getnext;
+if nextcontrol=134 then begin if not phaseone then begin writeln(output)
+;write(output,'! TeX string should be in Pascal text only');error;end;
+end;end;
+9,10,135,137,138,139,140,141,136,142:begin if not phaseone then begin
+writeln(output);write(output,'! You can''t do that in TeX text');error;
+end;end;otherwise end;until nextcontrol>=143{:222};{225:}
+if nextcontrol<=144 then begin if(saveline<>outline)or(saveplace<>outptr
+)then begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=89;end;saveline:=outline;saveplace:=outptr;end;
+while nextcontrol<=144 do begin stackptr:=0;curstate.modefield:=1;
+if nextcontrol=144 then{227:}begin begin tokmem[tokptr]:=92;
+tokptr:=tokptr+1;tokmem[tokptr]:=68;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=3;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;nextcontrol:=getnext;
+if nextcontrol<>130 then begin if not phaseone then begin writeln(output
+);write(output,'! Improper macro definition');error;end;
+end else begin tokmem[tokptr]:=10240+idlookup(0);tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=2;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;nextcontrol:=getnext;
+end{:227}else{228:}begin begin tokmem[tokptr]:=92;tokptr:=tokptr+1;
+tokmem[tokptr]:=70;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=3;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;nextcontrol:=getnext;
+if nextcontrol=130 then begin begin tokmem[tokptr]:=10240+idlookup(0);
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;nextcontrol:=getnext;
+if nextcontrol=30 then begin begin tokmem[tokptr]:=92;tokptr:=tokptr+1;
+tokmem[tokptr]:=83;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=2;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;nextcontrol:=getnext;
+if nextcontrol=130 then begin begin tokmem[tokptr]:=10240+idlookup(0);
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;begin scrapptr:=scrapptr+1;cat[scrapptr]:=9;trans[scrapptr]:=0;end;
+nextcontrol:=getnext;end;end;end;
+if scrapptr<>5 then begin if not phaseone then begin writeln(output);
+write(output,'! Improper format definition');error;end;end;end{:228};
+outerparse;finishPascal;end{:225};{230:}thismodule:=0;
+if nextcontrol<=146 then begin if(saveline<>outline)or(saveplace<>outptr
+)then begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=89;end;stackptr:=0;curstate.modefield:=1;
+if nextcontrol=145 then nextcontrol:=getnext else begin thismodule:=
+curmodule;{231:}repeat nextcontrol:=getnext;until nextcontrol<>43;
+if(nextcontrol<>61)and(nextcontrol<>30)then begin if not phaseone then
+begin writeln(output);
+write(output,'! You need an = sign after the section name');error;end;
+end else nextcontrol:=getnext;
+if outptr>1 then if(outbuf[outptr]=89)and(outbuf[outptr-1]=92)then begin
+tokmem[tokptr]:=139;tokptr:=tokptr+1;end;
+begin tokmem[tokptr]:=30720+thismodule;tokptr:=tokptr+1;
+scrapptr:=scrapptr+1;cat[scrapptr]:=22;trans[scrapptr]:=textptr;
+textptr:=textptr+1;tokstart[textptr]:=tokptr;end;
+curxref:=xref[thismodule];
+if xmem[curxref].numfield<>modulecount+10240 then begin begin tokmem[
+tokptr]:=132;tokptr:=tokptr+1;tokmem[tokptr]:=43;tokptr:=tokptr+1;
+tokmem[tokptr]:=125;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=2;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;thismodule:=0;end;
+begin tokmem[tokptr]:=92;tokptr:=tokptr+1;tokmem[tokptr]:=83;
+tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=2;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;begin tokmem[tokptr]:=141;tokptr:=tokptr+1;scrapptr:=scrapptr+1;
+cat[scrapptr]:=9;trans[scrapptr]:=textptr;textptr:=textptr+1;
+tokstart[textptr]:=tokptr;end;{:231};end;
+while nextcontrol<=146 do begin outerparse;{232:}
+if nextcontrol<146 then begin begin if not phaseone then begin writeln(
+output);write(output,'! You can''t do that in Pascal text');error;end;
+end;nextcontrol:=getnext;
+end else if nextcontrol=146 then begin begin tokmem[tokptr]:=30720+
+curmodule;tokptr:=tokptr+1;scrapptr:=scrapptr+1;cat[scrapptr]:=22;
+trans[scrapptr]:=textptr;textptr:=textptr+1;tokstart[textptr]:=tokptr;
+end;nextcontrol:=getnext;end{:232};end;finishPascal;end{:230};{233:}
+if thismodule>0 then begin{235:}firstxref:=xref[thismodule];
+thisxref:=xmem[firstxref].xlinkfield;
+if xmem[thisxref].numfield>10240 then begin midxref:=thisxref;
+curxref:=0;repeat nextxref:=xmem[thisxref].xlinkfield;
+xmem[thisxref].xlinkfield:=curxref;curxref:=thisxref;thisxref:=nextxref;
+until xmem[thisxref].numfield<=10240;
+xmem[firstxref].xlinkfield:=curxref;end else midxref:=0;curxref:=0;
+while thisxref<>0 do begin nextxref:=xmem[thisxref].xlinkfield;
+xmem[thisxref].xlinkfield:=curxref;curxref:=thisxref;thisxref:=nextxref;
+end;
+if midxref>0 then xmem[midxref].xlinkfield:=curxref else xmem[firstxref]
+.xlinkfield:=curxref;curxref:=xmem[firstxref].xlinkfield{:235};
+footnote(10240);footnote(0);end{:233};{238:}
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=102;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=105;end;finishline;flushbuffer(0,false,false);{:238};
+end{:220}{:218};end;begin initialize;
+writeln(output,'This is WEAVE, Version 4.4 for Linux');{64:}idloc:=10;
+idfirst:=7;buffer[7]:=97;buffer[8]:=110;buffer[9]:=100;
+curname:=idlookup(28);idfirst:=5;buffer[5]:=97;buffer[6]:=114;
+buffer[7]:=114;buffer[8]:=97;buffer[9]:=121;curname:=idlookup(4);
+idfirst:=5;buffer[5]:=98;buffer[6]:=101;buffer[7]:=103;buffer[8]:=105;
+buffer[9]:=110;curname:=idlookup(5);idfirst:=6;buffer[6]:=99;
+buffer[7]:=97;buffer[8]:=115;buffer[9]:=101;curname:=idlookup(6);
+idfirst:=5;buffer[5]:=99;buffer[6]:=111;buffer[7]:=110;buffer[8]:=115;
+buffer[9]:=116;curname:=idlookup(7);idfirst:=7;buffer[7]:=100;
+buffer[8]:=105;buffer[9]:=118;curname:=idlookup(8);idfirst:=8;
+buffer[8]:=100;buffer[9]:=111;curname:=idlookup(9);idfirst:=4;
+buffer[4]:=100;buffer[5]:=111;buffer[6]:=119;buffer[7]:=110;
+buffer[8]:=116;buffer[9]:=111;curname:=idlookup(20);idfirst:=6;
+buffer[6]:=101;buffer[7]:=108;buffer[8]:=115;buffer[9]:=101;
+curname:=idlookup(10);idfirst:=7;buffer[7]:=101;buffer[8]:=110;
+buffer[9]:=100;curname:=idlookup(11);idfirst:=6;buffer[6]:=102;
+buffer[7]:=105;buffer[8]:=108;buffer[9]:=101;curname:=idlookup(4);
+idfirst:=7;buffer[7]:=102;buffer[8]:=111;buffer[9]:=114;
+curname:=idlookup(12);idfirst:=2;buffer[2]:=102;buffer[3]:=117;
+buffer[4]:=110;buffer[5]:=99;buffer[6]:=116;buffer[7]:=105;
+buffer[8]:=111;buffer[9]:=110;curname:=idlookup(17);idfirst:=6;
+buffer[6]:=103;buffer[7]:=111;buffer[8]:=116;buffer[9]:=111;
+curname:=idlookup(13);idfirst:=8;buffer[8]:=105;buffer[9]:=102;
+curname:=idlookup(14);idfirst:=8;buffer[8]:=105;buffer[9]:=110;
+curname:=idlookup(30);idfirst:=5;buffer[5]:=108;buffer[6]:=97;
+buffer[7]:=98;buffer[8]:=101;buffer[9]:=108;curname:=idlookup(7);
+idfirst:=7;buffer[7]:=109;buffer[8]:=111;buffer[9]:=100;
+curname:=idlookup(8);idfirst:=7;buffer[7]:=110;buffer[8]:=105;
+buffer[9]:=108;curname:=idlookup(16);idfirst:=7;buffer[7]:=110;
+buffer[8]:=111;buffer[9]:=116;curname:=idlookup(29);idfirst:=8;
+buffer[8]:=111;buffer[9]:=102;curname:=idlookup(9);idfirst:=8;
+buffer[8]:=111;buffer[9]:=114;curname:=idlookup(55);idfirst:=4;
+buffer[4]:=112;buffer[5]:=97;buffer[6]:=99;buffer[7]:=107;
+buffer[8]:=101;buffer[9]:=100;curname:=idlookup(13);idfirst:=1;
+buffer[1]:=112;buffer[2]:=114;buffer[3]:=111;buffer[4]:=99;
+buffer[5]:=101;buffer[6]:=100;buffer[7]:=117;buffer[8]:=114;
+buffer[9]:=101;curname:=idlookup(17);idfirst:=3;buffer[3]:=112;
+buffer[4]:=114;buffer[5]:=111;buffer[6]:=103;buffer[7]:=114;
+buffer[8]:=97;buffer[9]:=109;curname:=idlookup(17);idfirst:=4;
+buffer[4]:=114;buffer[5]:=101;buffer[6]:=99;buffer[7]:=111;
+buffer[8]:=114;buffer[9]:=100;curname:=idlookup(18);idfirst:=4;
+buffer[4]:=114;buffer[5]:=101;buffer[6]:=112;buffer[7]:=101;
+buffer[8]:=97;buffer[9]:=116;curname:=idlookup(19);idfirst:=7;
+buffer[7]:=115;buffer[8]:=101;buffer[9]:=116;curname:=idlookup(4);
+idfirst:=6;buffer[6]:=116;buffer[7]:=104;buffer[8]:=101;buffer[9]:=110;
+curname:=idlookup(9);idfirst:=8;buffer[8]:=116;buffer[9]:=111;
+curname:=idlookup(20);idfirst:=6;buffer[6]:=116;buffer[7]:=121;
+buffer[8]:=112;buffer[9]:=101;curname:=idlookup(7);idfirst:=5;
+buffer[5]:=117;buffer[6]:=110;buffer[7]:=116;buffer[8]:=105;
+buffer[9]:=108;curname:=idlookup(21);idfirst:=7;buffer[7]:=118;
+buffer[8]:=97;buffer[9]:=114;curname:=idlookup(22);idfirst:=5;
+buffer[5]:=119;buffer[6]:=104;buffer[7]:=105;buffer[8]:=108;
+buffer[9]:=101;curname:=idlookup(12);idfirst:=6;buffer[6]:=119;
+buffer[7]:=105;buffer[8]:=116;buffer[9]:=104;curname:=idlookup(12);
+idfirst:=3;buffer[3]:=120;buffer[4]:=99;buffer[5]:=108;buffer[6]:=97;
+buffer[7]:=117;buffer[8]:=115;buffer[9]:=101;curname:=idlookup(23);{:64}
+;PhaseI;PhaseII;{239:}if noxref then begin finishline;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;end;begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=118;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=102;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=105;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=108;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=108;end;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=101;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=110;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=100;end;finishline;end else begin phasethree:=true;
+begin writeln(output);write(output,'Writing the index...');end;
+if changeexists then begin finishline;{241:}begin kmodule:=1;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=99;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=104;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=32;end;
+while kmodule<modulecount do begin if changedmodule[kmodule]then begin
+outmod(kmodule);begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=44;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=32;end;end;kmodule:=kmodule+1;end;
+outmod(kmodule);begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=46;end;end{:241};end;finishline;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=105;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=110;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=120;end;finishline;{243:}
+for c:=0 to 255 do bucket[c]:=0;
+for h:=0 to hashsize-1 do begin nextname:=hash[h];
+while nextname<>0 do begin curname:=nextname;nextname:=link[curname];
+if xref[curname]<>0 then begin c:=bytemem[curname mod 2,bytestart[
+curname]];if(c<=90)and(c>=65)then c:=c+32;blink[curname]:=bucket[c];
+bucket[c]:=curname;end;end;end{:243};{250:}scrapptr:=0;unbucket(1);
+while scrapptr>0 do begin curdepth:=cat[scrapptr];
+if(blink[trans[scrapptr]]=0)or(curdepth=255)then{252:}
+begin curname:=trans[scrapptr];{if troubleshooting then debughelp;}
+repeat begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=58;end;{253:}
+case ilk[curname]of 0:if bytestart[curname+2]-bytestart[curname]=1 then
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=124;end else begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=92;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=92;end;1:;
+2:begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=57;end;3:begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=92;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=46;end;
+otherwise begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=38;end end;outname(curname){:253};{254:}{255:}
+thisxref:=xref[curname];curxref:=0;
+repeat nextxref:=xmem[thisxref].xlinkfield;
+xmem[thisxref].xlinkfield:=curxref;curxref:=thisxref;thisxref:=nextxref;
+until thisxref=0{:255};repeat begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=44;if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=32;end;curval:=xmem[curxref].numfield;
+if curval<10240 then outmod(curval)else begin begin if outptr=linelength
+then breakout;outptr:=outptr+1;outbuf[outptr]:=92;
+if outptr=linelength then breakout;outptr:=outptr+1;outbuf[outptr]:=91;
+end;outmod(curval-10240);begin if outptr=linelength then breakout;
+outptr:=outptr+1;outbuf[outptr]:=93;end;end;
+curxref:=xmem[curxref].xlinkfield;until curxref=0;
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=46;end;finishline{:254};curname:=blink[curname];
+until curname=0;scrapptr:=scrapptr-1;end{:252}else{251:}
+begin nextname:=trans[scrapptr];repeat curname:=nextname;
+nextname:=blink[curname];curbyte:=bytestart[curname]+curdepth;
+curbank:=curname mod 2;
+if curbyte=bytestart[curname+2]then c:=0 else begin c:=bytemem[curbank,
+curbyte];if(c<=90)and(c>=65)then c:=c+32;end;blink[curname]:=bucket[c];
+bucket[c]:=curname;until nextname=0;scrapptr:=scrapptr-1;
+unbucket(curdepth+1);end{:251};end{:250};
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=102;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=105;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=110;end;finishline;{257:}modprint(ilk[0]){:257};
+begin if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=92;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=99;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=111;if outptr=linelength then breakout;outptr:=outptr+1;
+outbuf[outptr]:=110;end;finishline;end;write(output,'Done.');{:239};
+{85:}
+if changelimit<>0 then begin for ii:=0 to changelimit do buffer[ii]:=
+changebuffer[ii];limit:=changelimit;changing:=true;line:=otherline;
+loc:=changelimit;begin if not phaseone then begin writeln(output);
+write(output,'! Change file entry did not match');error;end;end;end{:85}
+;9999:{262:}begin writeln(output);
+write(output,'Memory usage statistics: ',nameptr:1,' names, ',xrefptr:1,
+' cross references, ',byteptr[0]:1);end;
+for curbank:=1 to 1 do write(output,'+',byteptr[curbank]:1);
+write(output,' bytes;');begin writeln(output);
+write(output,'parsing required ',maxscrptr:1,' scraps, ',maxtxtptr:1,
+' texts, ',maxtokptr:1,' tokens, ',maxstackptr:1,' levels;');end;
+begin writeln(output);
+write(output,'sorting required ',maxsortptr:1,' levels.');end{:262};
+{263:}case history of 0:begin writeln(output);
+write(output,'(No errors were found.)');end;1:begin writeln(output);
+write(output,'(Did you see the warning message above?)');end;
+2:begin writeln(output);
+write(output,'(Pardon me, but I think I spotted something wrong.)');end;
+3:begin writeln(output);
+write(output,'(That was a fatal error, my friend.)');end;end{:263};
+writeln(output);if(history<>0)and(history<>1)then exit(1)else exit(0);
+end.{:261}
diff --git a/systems/knuth/local/web/weavext.c b/systems/knuth/local/web/weavext.c
new file mode 100644
index 0000000000..87f1c99b22
--- /dev/null
+++ b/systems/knuth/local/web/weavext.c
@@ -0,0 +1,107 @@
+/* External procedures for weave */
+/* Written by: H. Trickey, 11/17/83 */
+
+/* Modified for 8-bit input --- don 8/31/89 */
+
+/* Note: these procedures aren't necessary; the default input_ln and
+ * flush_buffer routines in tangle/weave work fine on UNIX.
+ * However a very big time improvement is achieved by using these.
+ *
+ * These procedures are the same as for tangle, except for a slight offset
+ * in which characters are output from outbuf in linewrite.
+ */
+
+#define BUF_SIZE 100 /* should agree with tangle.web */
+
+#include "h00vars.h" /* defines Pascal I/O structure */
+
+extern short buffer[]; /* 0..BUF_SIZE. Input goes here */
+extern short outbuf[]; /* 0..OUT_BUF_SIZE. Output from here */
+extern short xord[];
+extern char xchr[]; /* character translation arrays */
+extern short limit; /* index into buffer. Note that limit
+ is 0..long_buf_size in weave.web; this
+ differs from the definition in tangle.web */
+
+/*
+ * lineread reads from the Pascal text file with iorec pointer filep
+ * into buffer[0], buffer[1],..., buffer[limit-1] (and
+ * setting "limit").
+ * Characters are read until a newline is found (which isn't put in the
+ * buffer) or until the next character would go into buffer[BUF_SIZE].
+ * And trailing blanks are to be ignored, so limit is really set to
+ * one past the last non-blank.
+ * The characters need to be translated, so really xord[c] is put into
+ * the buffer when c is read.
+ * If end-of-file is encountered, the funit field of *filep is set
+ * appropriately.
+ */
+lineread(filep)
+struct iorec *filep;
+{
+ register c;
+ register short *cs; /* pointer into buffer where next char goes */
+ register short *cnb; /* last non-blank character input */
+ register FILE *iop; /* stdio-style FILE pointer */
+ register int l; /* how many chars allowed before buffer overflow */
+
+ iop = filep->fbuf;
+ cnb = cs = &(buffer[0]);
+ l = BUF_SIZE;
+ /* overflow when next char would go into buffer[BUF_SIZE] */
+ while (--l>=0 && (c = getc(iop)) != EOF && c!='\n')
+ if((*cs++ = xord[c])!=' ') cnb = cs;
+ if (c == EOF)
+ filep->funit |= EOFF; /* we hit end-of-file */
+ limit = cnb-&(buffer[0]);
+}
+
+/*
+ * linewrite writes to the Pascal text file with iorec pointer filep
+ * from outbuf[1], outbuf[1],..., outbuf[cnt].
+ * (Note the offset of indices vis a vis the tangext version of this.)
+ * Translation is done, so that xchr[c] is output instead of c.
+ */
+ linewrite(filep,cnt)
+ struct iorec *filep;
+ int cnt;
+ {
+ register FILE *iop; /* stdio-style FILE pointer */
+ register short *cs; /* pointer to next character to output */
+ register int l; /* how many characters left to output */
+
+ iop = filep->fbuf;
+ cs = &(outbuf[1]);
+ l = cnt;
+ while (--l>=0) putc(xchr[*cs++],iop);
+}
+
+/*
+** testeof(filep)
+**
+** Test whether or not the Pascal text file with iorec pointer filep
+** has reached end-of-file (when the only I/O on it is done with
+** lineread, above).
+** We may have to read the next character and unget it to see if perhaps
+** the end-of-file is next.
+*/
+
+bool
+testeof(filep)
+register struct iorec *filep;
+{
+ register char c;
+ register FILE *iop; /* stdio-style FILE pointer */
+ if (filep->funit & EOFF)
+ return(TRUE);
+ else { /* check to see if next is EOF */
+ iop = filep->fbuf;
+ c = getc(iop);
+ if (c == EOF)
+ return(TRUE);
+ else {
+ ungetc(c,iop);
+ return(FALSE);
+ }
+ }
+}